Back to home page

LXR

 
 

    


0001 #include <linux/compiler.h>
0002 #include <linux/file.h>
0003 #include <linux/fs.h>
0004 #include <linux/linkage.h>
0005 #include <linux/mount.h>
0006 #include <linux/namei.h>
0007 #include <linux/sched.h>
0008 #include <linux/stat.h>
0009 #include <linux/utime.h>
0010 #include <linux/syscalls.h>
0011 #include <linux/uaccess.h>
0012 #include <asm/unistd.h>
0013 
0014 #ifdef __ARCH_WANT_SYS_UTIME
0015 
0016 /*
0017  * sys_utime() can be implemented in user-level using sys_utimes().
0018  * Is this for backwards compatibility?  If so, why not move it
0019  * into the appropriate arch directory (for those architectures that
0020  * need it).
0021  */
0022 
0023 /* If times==NULL, set access and modification to current time,
0024  * must be owner or have write permission.
0025  * Else, update from *times, must be owner or super user.
0026  */
0027 SYSCALL_DEFINE2(utime, char __user *, filename, struct utimbuf __user *, times)
0028 {
0029     struct timespec tv[2];
0030 
0031     if (times) {
0032         if (get_user(tv[0].tv_sec, &times->actime) ||
0033             get_user(tv[1].tv_sec, &times->modtime))
0034             return -EFAULT;
0035         tv[0].tv_nsec = 0;
0036         tv[1].tv_nsec = 0;
0037     }
0038     return do_utimes(AT_FDCWD, filename, times ? tv : NULL, 0);
0039 }
0040 
0041 #endif
0042 
0043 static bool nsec_valid(long nsec)
0044 {
0045     if (nsec == UTIME_OMIT || nsec == UTIME_NOW)
0046         return true;
0047 
0048     return nsec >= 0 && nsec <= 999999999;
0049 }
0050 
0051 static int utimes_common(const struct path *path, struct timespec *times)
0052 {
0053     int error;
0054     struct iattr newattrs;
0055     struct inode *inode = path->dentry->d_inode;
0056     struct inode *delegated_inode = NULL;
0057 
0058     error = mnt_want_write(path->mnt);
0059     if (error)
0060         goto out;
0061 
0062     if (times && times[0].tv_nsec == UTIME_NOW &&
0063              times[1].tv_nsec == UTIME_NOW)
0064         times = NULL;
0065 
0066     newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME;
0067     if (times) {
0068         if (times[0].tv_nsec == UTIME_OMIT)
0069             newattrs.ia_valid &= ~ATTR_ATIME;
0070         else if (times[0].tv_nsec != UTIME_NOW) {
0071             newattrs.ia_atime.tv_sec = times[0].tv_sec;
0072             newattrs.ia_atime.tv_nsec = times[0].tv_nsec;
0073             newattrs.ia_valid |= ATTR_ATIME_SET;
0074         }
0075 
0076         if (times[1].tv_nsec == UTIME_OMIT)
0077             newattrs.ia_valid &= ~ATTR_MTIME;
0078         else if (times[1].tv_nsec != UTIME_NOW) {
0079             newattrs.ia_mtime.tv_sec = times[1].tv_sec;
0080             newattrs.ia_mtime.tv_nsec = times[1].tv_nsec;
0081             newattrs.ia_valid |= ATTR_MTIME_SET;
0082         }
0083         /*
0084          * Tell setattr_prepare(), that this is an explicit time
0085          * update, even if neither ATTR_ATIME_SET nor ATTR_MTIME_SET
0086          * were used.
0087          */
0088         newattrs.ia_valid |= ATTR_TIMES_SET;
0089     } else {
0090         newattrs.ia_valid |= ATTR_TOUCH;
0091     }
0092 retry_deleg:
0093     inode_lock(inode);
0094     error = notify_change(path->dentry, &newattrs, &delegated_inode);
0095     inode_unlock(inode);
0096     if (delegated_inode) {
0097         error = break_deleg_wait(&delegated_inode);
0098         if (!error)
0099             goto retry_deleg;
0100     }
0101 
0102     mnt_drop_write(path->mnt);
0103 out:
0104     return error;
0105 }
0106 
0107 /*
0108  * do_utimes - change times on filename or file descriptor
0109  * @dfd: open file descriptor, -1 or AT_FDCWD
0110  * @filename: path name or NULL
0111  * @times: new times or NULL
0112  * @flags: zero or more flags (only AT_SYMLINK_NOFOLLOW for the moment)
0113  *
0114  * If filename is NULL and dfd refers to an open file, then operate on
0115  * the file.  Otherwise look up filename, possibly using dfd as a
0116  * starting point.
0117  *
0118  * If times==NULL, set access and modification to current time,
0119  * must be owner or have write permission.
0120  * Else, update from *times, must be owner or super user.
0121  */
0122 long do_utimes(int dfd, const char __user *filename, struct timespec *times,
0123            int flags)
0124 {
0125     int error = -EINVAL;
0126 
0127     if (times && (!nsec_valid(times[0].tv_nsec) ||
0128               !nsec_valid(times[1].tv_nsec))) {
0129         goto out;
0130     }
0131 
0132     if (flags & ~AT_SYMLINK_NOFOLLOW)
0133         goto out;
0134 
0135     if (filename == NULL && dfd != AT_FDCWD) {
0136         struct fd f;
0137 
0138         if (flags & AT_SYMLINK_NOFOLLOW)
0139             goto out;
0140 
0141         f = fdget(dfd);
0142         error = -EBADF;
0143         if (!f.file)
0144             goto out;
0145 
0146         error = utimes_common(&f.file->f_path, times);
0147         fdput(f);
0148     } else {
0149         struct path path;
0150         int lookup_flags = 0;
0151 
0152         if (!(flags & AT_SYMLINK_NOFOLLOW))
0153             lookup_flags |= LOOKUP_FOLLOW;
0154 retry:
0155         error = user_path_at(dfd, filename, lookup_flags, &path);
0156         if (error)
0157             goto out;
0158 
0159         error = utimes_common(&path, times);
0160         path_put(&path);
0161         if (retry_estale(error, lookup_flags)) {
0162             lookup_flags |= LOOKUP_REVAL;
0163             goto retry;
0164         }
0165     }
0166 
0167 out:
0168     return error;
0169 }
0170 
0171 SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename,
0172         struct timespec __user *, utimes, int, flags)
0173 {
0174     struct timespec tstimes[2];
0175 
0176     if (utimes) {
0177         if (copy_from_user(&tstimes, utimes, sizeof(tstimes)))
0178             return -EFAULT;
0179 
0180         /* Nothing to do, we must not even check the path.  */
0181         if (tstimes[0].tv_nsec == UTIME_OMIT &&
0182             tstimes[1].tv_nsec == UTIME_OMIT)
0183             return 0;
0184     }
0185 
0186     return do_utimes(dfd, filename, utimes ? tstimes : NULL, flags);
0187 }
0188 
0189 SYSCALL_DEFINE3(futimesat, int, dfd, const char __user *, filename,
0190         struct timeval __user *, utimes)
0191 {
0192     struct timeval times[2];
0193     struct timespec tstimes[2];
0194 
0195     if (utimes) {
0196         if (copy_from_user(&times, utimes, sizeof(times)))
0197             return -EFAULT;
0198 
0199         /* This test is needed to catch all invalid values.  If we
0200            would test only in do_utimes we would miss those invalid
0201            values truncated by the multiplication with 1000.  Note
0202            that we also catch UTIME_{NOW,OMIT} here which are only
0203            valid for utimensat.  */
0204         if (times[0].tv_usec >= 1000000 || times[0].tv_usec < 0 ||
0205             times[1].tv_usec >= 1000000 || times[1].tv_usec < 0)
0206             return -EINVAL;
0207 
0208         tstimes[0].tv_sec = times[0].tv_sec;
0209         tstimes[0].tv_nsec = 1000 * times[0].tv_usec;
0210         tstimes[1].tv_sec = times[1].tv_sec;
0211         tstimes[1].tv_nsec = 1000 * times[1].tv_usec;
0212     }
0213 
0214     return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0);
0215 }
0216 
0217 SYSCALL_DEFINE2(utimes, char __user *, filename,
0218         struct timeval __user *, utimes)
0219 {
0220     return sys_futimesat(AT_FDCWD, filename, utimes);
0221 }