linux/fs/utimes.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/file.h>
   3#include <linux/mount.h>
   4#include <linux/namei.h>
   5#include <linux/utime.h>
   6#include <linux/syscalls.h>
   7#include <linux/uaccess.h>
   8#include <linux/compat.h>
   9#include <asm/unistd.h>
  10
  11static bool nsec_valid(long nsec)
  12{
  13        if (nsec == UTIME_OMIT || nsec == UTIME_NOW)
  14                return true;
  15
  16        return nsec >= 0 && nsec <= 999999999;
  17}
  18
  19int vfs_utimes(const struct path *path, struct timespec64 *times)
  20{
  21        int error;
  22        struct iattr newattrs;
  23        struct inode *inode = path->dentry->d_inode;
  24        struct inode *delegated_inode = NULL;
  25
  26        if (times) {
  27                if (!nsec_valid(times[0].tv_nsec) ||
  28                    !nsec_valid(times[1].tv_nsec))
  29                        return -EINVAL;
  30                if (times[0].tv_nsec == UTIME_NOW &&
  31                    times[1].tv_nsec == UTIME_NOW)
  32                        times = NULL;
  33        }
  34
  35        error = mnt_want_write(path->mnt);
  36        if (error)
  37                goto out;
  38
  39        newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
  40        if (times) {
  41                if (times[0].tv_nsec == UTIME_OMIT)
  42                        newattrs.ia_valid &= ~ATTR_ATIME;
  43                else if (times[0].tv_nsec != UTIME_NOW) {
  44                        newattrs.ia_atime = times[0];
  45                        newattrs.ia_valid |= ATTR_ATIME_SET;
  46                }
  47
  48                if (times[1].tv_nsec == UTIME_OMIT)
  49                        newattrs.ia_valid &= ~ATTR_MTIME;
  50                else if (times[1].tv_nsec != UTIME_NOW) {
  51                        newattrs.ia_mtime = times[1];
  52                        newattrs.ia_valid |= ATTR_MTIME_SET;
  53                }
  54                /*
  55                 * Tell setattr_prepare(), that this is an explicit time
  56                 * update, even if neither ATTR_ATIME_SET nor ATTR_MTIME_SET
  57                 * were used.
  58                 */
  59                newattrs.ia_valid |= ATTR_TIMES_SET;
  60        } else {
  61                newattrs.ia_valid |= ATTR_TOUCH;
  62        }
  63retry_deleg:
  64        inode_lock(inode);
  65        error = notify_change(mnt_user_ns(path->mnt), path->dentry, &newattrs,
  66                              &delegated_inode);
  67        inode_unlock(inode);
  68        if (delegated_inode) {
  69                error = break_deleg_wait(&delegated_inode);
  70                if (!error)
  71                        goto retry_deleg;
  72        }
  73
  74        mnt_drop_write(path->mnt);
  75out:
  76        return error;
  77}
  78
  79static int do_utimes_path(int dfd, const char __user *filename,
  80                struct timespec64 *times, int flags)
  81{
  82        struct path path;
  83        int lookup_flags = 0, error;
  84
  85        if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH))
  86                return -EINVAL;
  87
  88        if (!(flags & AT_SYMLINK_NOFOLLOW))
  89                lookup_flags |= LOOKUP_FOLLOW;
  90        if (flags & AT_EMPTY_PATH)
  91                lookup_flags |= LOOKUP_EMPTY;
  92
  93retry:
  94        error = user_path_at(dfd, filename, lookup_flags, &path);
  95        if (error)
  96                return error;
  97
  98        error = vfs_utimes(&path, times);
  99        path_put(&path);
 100        if (retry_estale(error, lookup_flags)) {
 101                lookup_flags |= LOOKUP_REVAL;
 102                goto retry;
 103        }
 104
 105        return error;
 106}
 107
 108static int do_utimes_fd(int fd, struct timespec64 *times, int flags)
 109{
 110        struct fd f;
 111        int error;
 112
 113        if (flags)
 114                return -EINVAL;
 115
 116        f = fdget(fd);
 117        if (!f.file)
 118                return -EBADF;
 119        error = vfs_utimes(&f.file->f_path, times);
 120        fdput(f);
 121        return error;
 122}
 123
 124/*
 125 * do_utimes - change times on filename or file descriptor
 126 * @dfd: open file descriptor, -1 or AT_FDCWD
 127 * @filename: path name or NULL
 128 * @times: new times or NULL
 129 * @flags: zero or more flags (only AT_SYMLINK_NOFOLLOW for the moment)
 130 *
 131 * If filename is NULL and dfd refers to an open file, then operate on
 132 * the file.  Otherwise look up filename, possibly using dfd as a
 133 * starting point.
 134 *
 135 * If times==NULL, set access and modification to current time,
 136 * must be owner or have write permission.
 137 * Else, update from *times, must be owner or super user.
 138 */
 139long do_utimes(int dfd, const char __user *filename, struct timespec64 *times,
 140               int flags)
 141{
 142        if (filename == NULL && dfd != AT_FDCWD)
 143                return do_utimes_fd(dfd, times, flags);
 144        return do_utimes_path(dfd, filename, times, flags);
 145}
 146
 147SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename,
 148                struct __kernel_timespec __user *, utimes, int, flags)
 149{
 150        struct timespec64 tstimes[2];
 151
 152        if (utimes) {
 153                if ((get_timespec64(&tstimes[0], &utimes[0]) ||
 154                        get_timespec64(&tstimes[1], &utimes[1])))
 155                        return -EFAULT;
 156
 157                /* Nothing to do, we must not even check the path.  */
 158                if (tstimes[0].tv_nsec == UTIME_OMIT &&
 159                    tstimes[1].tv_nsec == UTIME_OMIT)
 160                        return 0;
 161        }
 162
 163        return do_utimes(dfd, filename, utimes ? tstimes : NULL, flags);
 164}
 165
 166#ifdef __ARCH_WANT_SYS_UTIME
 167/*
 168 * futimesat(), utimes() and utime() are older versions of utimensat()
 169 * that are provided for compatibility with traditional C libraries.
 170 * On modern architectures, we always use libc wrappers around
 171 * utimensat() instead.
 172 */
 173static long do_futimesat(int dfd, const char __user *filename,
 174                         struct __kernel_old_timeval __user *utimes)
 175{
 176        struct __kernel_old_timeval times[2];
 177        struct timespec64 tstimes[2];
 178
 179        if (utimes) {
 180                if (copy_from_user(&times, utimes, sizeof(times)))
 181                        return -EFAULT;
 182
 183                /* This test is needed to catch all invalid values.  If we
 184                   would test only in do_utimes we would miss those invalid
 185                   values truncated by the multiplication with 1000.  Note
 186                   that we also catch UTIME_{NOW,OMIT} here which are only
 187                   valid for utimensat.  */
 188                if (times[0].tv_usec >= 1000000 || times[0].tv_usec < 0 ||
 189                    times[1].tv_usec >= 1000000 || times[1].tv_usec < 0)
 190                        return -EINVAL;
 191
 192                tstimes[0].tv_sec = times[0].tv_sec;
 193                tstimes[0].tv_nsec = 1000 * times[0].tv_usec;
 194                tstimes[1].tv_sec = times[1].tv_sec;
 195                tstimes[1].tv_nsec = 1000 * times[1].tv_usec;
 196        }
 197
 198        return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0);
 199}
 200
 201
 202SYSCALL_DEFINE3(futimesat, int, dfd, const char __user *, filename,
 203                struct __kernel_old_timeval __user *, utimes)
 204{
 205        return do_futimesat(dfd, filename, utimes);
 206}
 207
 208SYSCALL_DEFINE2(utimes, char __user *, filename,
 209                struct __kernel_old_timeval __user *, utimes)
 210{
 211        return do_futimesat(AT_FDCWD, filename, utimes);
 212}
 213
 214SYSCALL_DEFINE2(utime, char __user *, filename, struct utimbuf __user *, times)
 215{
 216        struct timespec64 tv[2];
 217
 218        if (times) {
 219                if (get_user(tv[0].tv_sec, &times->actime) ||
 220                    get_user(tv[1].tv_sec, &times->modtime))
 221                        return -EFAULT;
 222                tv[0].tv_nsec = 0;
 223                tv[1].tv_nsec = 0;
 224        }
 225        return do_utimes(AT_FDCWD, filename, times ? tv : NULL, 0);
 226}
 227#endif
 228
 229#ifdef CONFIG_COMPAT_32BIT_TIME
 230/*
 231 * Not all architectures have sys_utime, so implement this in terms
 232 * of sys_utimes.
 233 */
 234#ifdef __ARCH_WANT_SYS_UTIME32
 235SYSCALL_DEFINE2(utime32, const char __user *, filename,
 236                struct old_utimbuf32 __user *, t)
 237{
 238        struct timespec64 tv[2];
 239
 240        if (t) {
 241                if (get_user(tv[0].tv_sec, &t->actime) ||
 242                    get_user(tv[1].tv_sec, &t->modtime))
 243                        return -EFAULT;
 244                tv[0].tv_nsec = 0;
 245                tv[1].tv_nsec = 0;
 246        }
 247        return do_utimes(AT_FDCWD, filename, t ? tv : NULL, 0);
 248}
 249#endif
 250
 251SYSCALL_DEFINE4(utimensat_time32, unsigned int, dfd, const char __user *, filename, struct old_timespec32 __user *, t, int, flags)
 252{
 253        struct timespec64 tv[2];
 254
 255        if  (t) {
 256                if (get_old_timespec32(&tv[0], &t[0]) ||
 257                    get_old_timespec32(&tv[1], &t[1]))
 258                        return -EFAULT;
 259
 260                if (tv[0].tv_nsec == UTIME_OMIT && tv[1].tv_nsec == UTIME_OMIT)
 261                        return 0;
 262        }
 263        return do_utimes(dfd, filename, t ? tv : NULL, flags);
 264}
 265
 266#ifdef __ARCH_WANT_SYS_UTIME32
 267static long do_compat_futimesat(unsigned int dfd, const char __user *filename,
 268                                struct old_timeval32 __user *t)
 269{
 270        struct timespec64 tv[2];
 271
 272        if (t) {
 273                if (get_user(tv[0].tv_sec, &t[0].tv_sec) ||
 274                    get_user(tv[0].tv_nsec, &t[0].tv_usec) ||
 275                    get_user(tv[1].tv_sec, &t[1].tv_sec) ||
 276                    get_user(tv[1].tv_nsec, &t[1].tv_usec))
 277                        return -EFAULT;
 278                if (tv[0].tv_nsec >= 1000000 || tv[0].tv_nsec < 0 ||
 279                    tv[1].tv_nsec >= 1000000 || tv[1].tv_nsec < 0)
 280                        return -EINVAL;
 281                tv[0].tv_nsec *= 1000;
 282                tv[1].tv_nsec *= 1000;
 283        }
 284        return do_utimes(dfd, filename, t ? tv : NULL, 0);
 285}
 286
 287SYSCALL_DEFINE3(futimesat_time32, unsigned int, dfd,
 288                       const char __user *, filename,
 289                       struct old_timeval32 __user *, t)
 290{
 291        return do_compat_futimesat(dfd, filename, t);
 292}
 293
 294SYSCALL_DEFINE2(utimes_time32, const char __user *, filename, struct old_timeval32 __user *, t)
 295{
 296        return do_compat_futimesat(AT_FDCWD, filename, t);
 297}
 298#endif
 299#endif
 300