linux/fs/utimes.c
<<
>>
Prefs
   1#include <linux/compiler.h>
   2#include <linux/file.h>
   3#include <linux/fs.h>
   4#include <linux/linkage.h>
   5#include <linux/namei.h>
   6#include <linux/sched.h>
   7#include <linux/stat.h>
   8#include <linux/utime.h>
   9#include <asm/uaccess.h>
  10#include <asm/unistd.h>
  11
  12#ifdef __ARCH_WANT_SYS_UTIME
  13
  14/*
  15 * sys_utime() can be implemented in user-level using sys_utimes().
  16 * Is this for backwards compatibility?  If so, why not move it
  17 * into the appropriate arch directory (for those architectures that
  18 * need it).
  19 */
  20
  21/* If times==NULL, set access and modification to current time,
  22 * must be owner or have write permission.
  23 * Else, update from *times, must be owner or super user.
  24 */
  25asmlinkage long sys_utime(char __user *filename, struct utimbuf __user *times)
  26{
  27        struct timespec tv[2];
  28
  29        if (times) {
  30                if (get_user(tv[0].tv_sec, &times->actime) ||
  31                    get_user(tv[1].tv_sec, &times->modtime))
  32                        return -EFAULT;
  33                tv[0].tv_nsec = 0;
  34                tv[1].tv_nsec = 0;
  35        }
  36        return do_utimes(AT_FDCWD, filename, times ? tv : NULL, 0);
  37}
  38
  39#endif
  40
  41static bool nsec_valid(long nsec)
  42{
  43        if (nsec == UTIME_OMIT || nsec == UTIME_NOW)
  44                return true;
  45
  46        return nsec >= 0 && nsec <= 999999999;
  47}
  48
  49/* If times==NULL, set access and modification to current time,
  50 * must be owner or have write permission.
  51 * Else, update from *times, must be owner or super user.
  52 */
  53long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags)
  54{
  55        int error;
  56        struct nameidata nd;
  57        struct dentry *dentry;
  58        struct inode *inode;
  59        struct iattr newattrs;
  60        struct file *f = NULL;
  61
  62        error = -EINVAL;
  63        if (times && (!nsec_valid(times[0].tv_nsec) ||
  64                      !nsec_valid(times[1].tv_nsec))) {
  65                goto out;
  66        }
  67
  68        if (flags & ~AT_SYMLINK_NOFOLLOW)
  69                goto out;
  70
  71        if (filename == NULL && dfd != AT_FDCWD) {
  72                error = -EINVAL;
  73                if (flags & AT_SYMLINK_NOFOLLOW)
  74                        goto out;
  75
  76                error = -EBADF;
  77                f = fget(dfd);
  78                if (!f)
  79                        goto out;
  80                dentry = f->f_path.dentry;
  81        } else {
  82                error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd);
  83                if (error)
  84                        goto out;
  85
  86                dentry = nd.dentry;
  87        }
  88
  89        inode = dentry->d_inode;
  90
  91        error = -EROFS;
  92        if (IS_RDONLY(inode))
  93                goto dput_and_out;
  94
  95        /* Don't worry, the checks are done in inode_change_ok() */
  96        newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
  97        if (times) {
  98                error = -EPERM;
  99                if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
 100                        goto dput_and_out;
 101
 102                if (times[0].tv_nsec == UTIME_OMIT)
 103                        newattrs.ia_valid &= ~ATTR_ATIME;
 104                else if (times[0].tv_nsec != UTIME_NOW) {
 105                        newattrs.ia_atime.tv_sec = times[0].tv_sec;
 106                        newattrs.ia_atime.tv_nsec = times[0].tv_nsec;
 107                        newattrs.ia_valid |= ATTR_ATIME_SET;
 108                }
 109
 110                if (times[1].tv_nsec == UTIME_OMIT)
 111                        newattrs.ia_valid &= ~ATTR_MTIME;
 112                else if (times[1].tv_nsec != UTIME_NOW) {
 113                        newattrs.ia_mtime.tv_sec = times[1].tv_sec;
 114                        newattrs.ia_mtime.tv_nsec = times[1].tv_nsec;
 115                        newattrs.ia_valid |= ATTR_MTIME_SET;
 116                }
 117        } else {
 118                error = -EACCES;
 119                if (IS_IMMUTABLE(inode))
 120                        goto dput_and_out;
 121
 122                if (!is_owner_or_cap(inode)) {
 123                        if (f) {
 124                                if (!(f->f_mode & FMODE_WRITE))
 125                                        goto dput_and_out;
 126                        } else {
 127                                error = vfs_permission(&nd, MAY_WRITE);
 128                                if (error)
 129                                        goto dput_and_out;
 130                        }
 131                }
 132        }
 133        mutex_lock(&inode->i_mutex);
 134        error = notify_change(dentry, &newattrs);
 135        mutex_unlock(&inode->i_mutex);
 136dput_and_out:
 137        if (f)
 138                fput(f);
 139        else
 140                path_release(&nd);
 141out:
 142        return error;
 143}
 144
 145asmlinkage long sys_utimensat(int dfd, char __user *filename, struct timespec __user *utimes, int flags)
 146{
 147        struct timespec tstimes[2];
 148
 149        if (utimes) {
 150                if (copy_from_user(&tstimes, utimes, sizeof(tstimes)))
 151                        return -EFAULT;
 152                if ((tstimes[0].tv_nsec == UTIME_OMIT ||
 153                     tstimes[0].tv_nsec == UTIME_NOW) &&
 154                    tstimes[0].tv_sec != 0)
 155                        return -EINVAL;
 156                if ((tstimes[1].tv_nsec == UTIME_OMIT ||
 157                     tstimes[1].tv_nsec == UTIME_NOW) &&
 158                    tstimes[1].tv_sec != 0)
 159                        return -EINVAL;
 160
 161                /* Nothing to do, we must not even check the path.  */
 162                if (tstimes[0].tv_nsec == UTIME_OMIT &&
 163                    tstimes[1].tv_nsec == UTIME_OMIT)
 164                        return 0;
 165        }
 166
 167        return do_utimes(dfd, filename, utimes ? tstimes : NULL, flags);
 168}
 169
 170asmlinkage long sys_futimesat(int dfd, char __user *filename, struct timeval __user *utimes)
 171{
 172        struct timeval times[2];
 173        struct timespec tstimes[2];
 174
 175        if (utimes) {
 176                if (copy_from_user(&times, utimes, sizeof(times)))
 177                        return -EFAULT;
 178
 179                /* This test is needed to catch all invalid values.  If we
 180                   would test only in do_utimes we would miss those invalid
 181                   values truncated by the multiplication with 1000.  Note
 182                   that we also catch UTIME_{NOW,OMIT} here which are only
 183                   valid for utimensat.  */
 184                if (times[0].tv_usec >= 1000000 || times[0].tv_usec < 0 ||
 185                    times[1].tv_usec >= 1000000 || times[1].tv_usec < 0)
 186                        return -EINVAL;
 187
 188                tstimes[0].tv_sec = times[0].tv_sec;
 189                tstimes[0].tv_nsec = 1000 * times[0].tv_usec;
 190                tstimes[1].tv_sec = times[1].tv_sec;
 191                tstimes[1].tv_nsec = 1000 * times[1].tv_usec;
 192        }
 193
 194        return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0);
 195}
 196
 197asmlinkage long sys_utimes(char __user *filename, struct timeval __user *utimes)
 198{
 199        return sys_futimesat(AT_FDCWD, filename, utimes);
 200}
 201