LCOV - code coverage report
Current view: top level - fs - utimes.c (source / functions) Hit Total Coverage
Test: landlock.info Lines: 75 102 73.5 %
Date: 2021-04-22 12:43:58 Functions: 8 15 53.3 %

          Line data    Source code
       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             : 
      11         176 : static bool nsec_valid(long nsec)
      12             : {
      13         176 :         if (nsec == UTIME_OMIT || nsec == UTIME_NOW)
      14             :                 return true;
      15             : 
      16         164 :         return nsec >= 0 && nsec <= 999999999;
      17             : }
      18             : 
      19         267 : int vfs_utimes(const struct path *path, struct timespec64 *times)
      20             : {
      21         267 :         int error;
      22         267 :         struct iattr newattrs;
      23         267 :         struct inode *inode = path->dentry->d_inode;
      24         267 :         struct inode *delegated_inode = NULL;
      25             : 
      26         267 :         if (times) {
      27          88 :                 if (!nsec_valid(times[0].tv_nsec) ||
      28          88 :                     !nsec_valid(times[1].tv_nsec))
      29             :                         return -EINVAL;
      30          88 :                 if (times[0].tv_nsec == UTIME_NOW &&
      31             :                     times[1].tv_nsec == UTIME_NOW)
      32           0 :                         times = NULL;
      33             :         }
      34             : 
      35         267 :         error = mnt_want_write(path->mnt);
      36         267 :         if (error)
      37           1 :                 goto out;
      38             : 
      39         266 :         newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
      40         266 :         if (times) {
      41          87 :                 if (times[0].tv_nsec == UTIME_OMIT)
      42          12 :                         newattrs.ia_valid &= ~ATTR_ATIME;
      43          75 :                 else if (times[0].tv_nsec != UTIME_NOW) {
      44          75 :                         newattrs.ia_atime = times[0];
      45          75 :                         newattrs.ia_valid |= ATTR_ATIME_SET;
      46             :                 }
      47             : 
      48          87 :                 if (times[1].tv_nsec == UTIME_OMIT)
      49           0 :                         newattrs.ia_valid &= ~ATTR_MTIME;
      50          87 :                 else if (times[1].tv_nsec != UTIME_NOW) {
      51          87 :                         newattrs.ia_mtime = times[1];
      52          87 :                         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          87 :                 newattrs.ia_valid |= ATTR_TIMES_SET;
      60             :         } else {
      61         179 :                 newattrs.ia_valid |= ATTR_TOUCH;
      62             :         }
      63             : retry_deleg:
      64         266 :         inode_lock(inode);
      65         266 :         error = notify_change(mnt_user_ns(path->mnt), path->dentry, &newattrs,
      66             :                               &delegated_inode);
      67         266 :         inode_unlock(inode);
      68         266 :         if (delegated_inode) {
      69           0 :                 error = break_deleg_wait(&delegated_inode);
      70           0 :                 if (!error)
      71           0 :                         goto retry_deleg;
      72             :         }
      73             : 
      74         266 :         mnt_drop_write(path->mnt);
      75             : out:
      76             :         return error;
      77             : }
      78             : 
      79         225 : static int do_utimes_path(int dfd, const char __user *filename,
      80             :                 struct timespec64 *times, int flags)
      81             : {
      82         225 :         struct path path;
      83         225 :         int lookup_flags = 0, error;
      84             : 
      85         225 :         if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH))
      86             :                 return -EINVAL;
      87             : 
      88         225 :         if (!(flags & AT_SYMLINK_NOFOLLOW))
      89         208 :                 lookup_flags |= LOOKUP_FOLLOW;
      90         225 :         if (flags & AT_EMPTY_PATH)
      91           0 :                 lookup_flags |= LOOKUP_EMPTY;
      92             : 
      93         225 : retry:
      94         225 :         error = user_path_at(dfd, filename, lookup_flags, &path);
      95         225 :         if (error)
      96           0 :                 return error;
      97             : 
      98         225 :         error = vfs_utimes(&path, times);
      99         225 :         path_put(&path);
     100         450 :         if (retry_estale(error, lookup_flags)) {
     101           0 :                 lookup_flags |= LOOKUP_REVAL;
     102           0 :                 goto retry;
     103             :         }
     104             : 
     105             :         return error;
     106             : }
     107             : 
     108          39 : static int do_utimes_fd(int fd, struct timespec64 *times, int flags)
     109             : {
     110          39 :         struct fd f;
     111          39 :         int error;
     112             : 
     113          39 :         if (flags)
     114             :                 return -EINVAL;
     115             : 
     116          39 :         f = fdget(fd);
     117          39 :         if (!f.file)
     118             :                 return -EBADF;
     119          39 :         error = vfs_utimes(&f.file->f_path, times);
     120          39 :         fdput(f);
     121          39 :         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             :  */
     139         264 : long do_utimes(int dfd, const char __user *filename, struct timespec64 *times,
     140             :                int flags)
     141             : {
     142         264 :         if (filename == NULL && dfd != AT_FDCWD)
     143          39 :                 return do_utimes_fd(dfd, times, flags);
     144         225 :         return do_utimes_path(dfd, filename, times, flags);
     145             : }
     146             : 
     147         506 : SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename,
     148             :                 struct __kernel_timespec __user *, utimes, int, flags)
     149             : {
     150         253 :         struct timespec64 tstimes[2];
     151             : 
     152         253 :         if (utimes) {
     153         148 :                 if ((get_timespec64(&tstimes[0], &utimes[0]) ||
     154          74 :                         get_timespec64(&tstimes[1], &utimes[1])))
     155           0 :                         return -EFAULT;
     156             : 
     157             :                 /* Nothing to do, we must not even check the path.  */
     158          74 :                 if (tstimes[0].tv_nsec == UTIME_OMIT &&
     159          12 :                     tstimes[1].tv_nsec == UTIME_OMIT)
     160             :                         return 0;
     161             :         }
     162             : 
     163         432 :         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             :  */
     173           0 : static long do_futimesat(int dfd, const char __user *filename,
     174             :                          struct __kernel_old_timeval __user *utimes)
     175             : {
     176           0 :         struct __kernel_old_timeval times[2];
     177           0 :         struct timespec64 tstimes[2];
     178             : 
     179           0 :         if (utimes) {
     180           0 :                 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           0 :                 if (times[0].tv_usec >= 1000000 || times[0].tv_usec < 0 ||
     189           0 :                     times[1].tv_usec >= 1000000 || times[1].tv_usec < 0)
     190             :                         return -EINVAL;
     191             : 
     192           0 :                 tstimes[0].tv_sec = times[0].tv_sec;
     193           0 :                 tstimes[0].tv_nsec = 1000 * times[0].tv_usec;
     194           0 :                 tstimes[1].tv_sec = times[1].tv_sec;
     195           0 :                 tstimes[1].tv_nsec = 1000 * times[1].tv_usec;
     196             :         }
     197             : 
     198           0 :         return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0);
     199             : }
     200             : 
     201             : 
     202           0 : SYSCALL_DEFINE3(futimesat, int, dfd, const char __user *, filename,
     203             :                 struct __kernel_old_timeval __user *, utimes)
     204             : {
     205           0 :         return do_futimesat(dfd, filename, utimes);
     206             : }
     207             : 
     208           0 : SYSCALL_DEFINE2(utimes, char __user *, filename,
     209             :                 struct __kernel_old_timeval __user *, utimes)
     210             : {
     211           0 :         return do_futimesat(AT_FDCWD, filename, utimes);
     212             : }
     213             : 
     214          22 : SYSCALL_DEFINE2(utime, char __user *, filename, struct utimbuf __user *, times)
     215             : {
     216          11 :         struct timespec64 tv[2];
     217             : 
     218          11 :         if (times) {
     219          11 :                 if (get_user(tv[0].tv_sec, &times->actime) ||
     220          11 :                     get_user(tv[1].tv_sec, &times->modtime))
     221           0 :                         return -EFAULT;
     222          11 :                 tv[0].tv_nsec = 0;
     223          11 :                 tv[1].tv_nsec = 0;
     224             :         }
     225          11 :         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
     235             : SYSCALL_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             : 
     251             : SYSCALL_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
     267             : static 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             : 
     287             : SYSCALL_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             : 
     294             : SYSCALL_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

Generated by: LCOV version 1.14