1#include <linux/compiler.h> 2#include <linux/file.h> 3#include <linux/fs.h> 4#include <linux/linkage.h> 5#include <linux/mount.h> 6#include <linux/namei.h> 7#include <linux/sched.h> 8#include <linux/stat.h> 9#include <linux/utime.h> 10#include <linux/syscalls.h> 11#include <asm/uaccess.h> 12#include <asm/unistd.h> 13 14#ifdef __ARCH_WANT_SYS_UTIME 15 16/* 17 * sys_utime() can be implemented in user-level using sys_utimes(). 18 * Is this for backwards compatibility? If so, why not move it 19 * into the appropriate arch directory (for those architectures that 20 * need it). 21 */ 22 23/* If times==NULL, set access and modification to current time, 24 * must be owner or have write permission. 25 * Else, update from *times, must be owner or super user. 26 */ 27SYSCALL_DEFINE2(utime, char __user *, filename, struct utimbuf __user *, times) 28{ 29 struct timespec tv[2]; 30 31 if (times) { 32 if (get_user(tv[0].tv_sec, ×->actime) || 33 get_user(tv[1].tv_sec, ×->modtime)) 34 return -EFAULT; 35 tv[0].tv_nsec = 0; 36 tv[1].tv_nsec = 0; 37 } 38 return do_utimes(AT_FDCWD, filename, times ? tv : NULL, 0); 39} 40 41#endif 42 43static bool nsec_valid(long nsec) 44{ 45 if (nsec == UTIME_OMIT || nsec == UTIME_NOW) 46 return true; 47 48 return nsec >= 0 && nsec <= 999999999; 49} 50 51static int utimes_common(struct path *path, struct timespec *times) 52{ 53 int error; 54 struct iattr newattrs; 55 struct inode *inode = path->dentry->d_inode; 56 struct inode *delegated_inode = NULL; 57 58 error = mnt_want_write(path->mnt); 59 if (error) 60 goto out; 61 62 if (times && times[0].tv_nsec == UTIME_NOW && 63 times[1].tv_nsec == UTIME_NOW) 64 times = NULL; 65 66 newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; 67 if (times) { 68 if (times[0].tv_nsec == UTIME_OMIT) 69 newattrs.ia_valid &= ~ATTR_ATIME; 70 else if (times[0].tv_nsec != UTIME_NOW) { 71 newattrs.ia_atime.tv_sec = times[0].tv_sec; 72 newattrs.ia_atime.tv_nsec = times[0].tv_nsec; 73 newattrs.ia_valid |= ATTR_ATIME_SET; 74 } 75 76 if (times[1].tv_nsec == UTIME_OMIT) 77 newattrs.ia_valid &= ~ATTR_MTIME; 78 else if (times[1].tv_nsec != UTIME_NOW) { 79 newattrs.ia_mtime.tv_sec = times[1].tv_sec; 80 newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; 81 newattrs.ia_valid |= ATTR_MTIME_SET; 82 } 83 /* 84 * Tell inode_change_ok(), that this is an explicit time 85 * update, even if neither ATTR_ATIME_SET nor ATTR_MTIME_SET 86 * were used. 87 */ 88 newattrs.ia_valid |= ATTR_TIMES_SET; 89 } else { 90 /* 91 * If times is NULL (or both times are UTIME_NOW), 92 * then we need to check permissions, because 93 * inode_change_ok() won't do it. 94 */ 95 error = -EACCES; 96 if (IS_IMMUTABLE(inode)) 97 goto mnt_drop_write_and_out; 98 99 if (!inode_owner_or_capable(inode)) { 100 error = inode_permission(inode, MAY_WRITE); 101 if (error) 102 goto mnt_drop_write_and_out; 103 } 104 } 105retry_deleg: 106 mutex_lock(&inode->i_mutex); 107 error = notify_change(path->dentry, &newattrs, &delegated_inode); 108 mutex_unlock(&inode->i_mutex); 109 if (delegated_inode) { 110 error = break_deleg_wait(&delegated_inode); 111 if (!error) 112 goto retry_deleg; 113 } 114 115mnt_drop_write_and_out: 116 mnt_drop_write(path->mnt); 117out: 118 return error; 119} 120 121/* 122 * do_utimes - change times on filename or file descriptor 123 * @dfd: open file descriptor, -1 or AT_FDCWD 124 * @filename: path name or NULL 125 * @times: new times or NULL 126 * @flags: zero or more flags (only AT_SYMLINK_NOFOLLOW for the moment) 127 * 128 * If filename is NULL and dfd refers to an open file, then operate on 129 * the file. Otherwise look up filename, possibly using dfd as a 130 * starting point. 131 * 132 * If times==NULL, set access and modification to current time, 133 * must be owner or have write permission. 134 * Else, update from *times, must be owner or super user. 135 */ 136long do_utimes(int dfd, const char __user *filename, struct timespec *times, 137 int flags) 138{ 139 int error = -EINVAL; 140 141 if (times && (!nsec_valid(times[0].tv_nsec) || 142 !nsec_valid(times[1].tv_nsec))) { 143 goto out; 144 } 145 146 if (flags & ~AT_SYMLINK_NOFOLLOW) 147 goto out; 148 149 if (filename == NULL && dfd != AT_FDCWD) { 150 struct fd f; 151 152 if (flags & AT_SYMLINK_NOFOLLOW) 153 goto out; 154 155 f = fdget(dfd); 156 error = -EBADF; 157 if (!f.file) 158 goto out; 159 160 error = utimes_common(&f.file->f_path, times); 161 fdput(f); 162 } else { 163 struct path path; 164 int lookup_flags = 0; 165 166 if (!(flags & AT_SYMLINK_NOFOLLOW)) 167 lookup_flags |= LOOKUP_FOLLOW; 168retry: 169 error = user_path_at(dfd, filename, lookup_flags, &path); 170 if (error) 171 goto out; 172 173 error = utimes_common(&path, times); 174 path_put(&path); 175 if (retry_estale(error, lookup_flags)) { 176 lookup_flags |= LOOKUP_REVAL; 177 goto retry; 178 } 179 } 180 181out: 182 return error; 183} 184 185SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename, 186 struct timespec __user *, utimes, int, flags) 187{ 188 struct timespec tstimes[2]; 189 190 if (utimes) { 191 if (copy_from_user(&tstimes, utimes, sizeof(tstimes))) 192 return -EFAULT; 193 194 /* Nothing to do, we must not even check the path. */ 195 if (tstimes[0].tv_nsec == UTIME_OMIT && 196 tstimes[1].tv_nsec == UTIME_OMIT) 197 return 0; 198 } 199 200 return do_utimes(dfd, filename, utimes ? tstimes : NULL, flags); 201} 202 203SYSCALL_DEFINE3(futimesat, int, dfd, const char __user *, filename, 204 struct timeval __user *, utimes) 205{ 206 struct timeval times[2]; 207 struct timespec tstimes[2]; 208 209 if (utimes) { 210 if (copy_from_user(×, utimes, sizeof(times))) 211 return -EFAULT; 212 213 /* This test is needed to catch all invalid values. If we 214 would test only in do_utimes we would miss those invalid 215 values truncated by the multiplication with 1000. Note 216 that we also catch UTIME_{NOW,OMIT} here which are only 217 valid for utimensat. */ 218 if (times[0].tv_usec >= 1000000 || times[0].tv_usec < 0 || 219 times[1].tv_usec >= 1000000 || times[1].tv_usec < 0) 220 return -EINVAL; 221 222 tstimes[0].tv_sec = times[0].tv_sec; 223 tstimes[0].tv_nsec = 1000 * times[0].tv_usec; 224 tstimes[1].tv_sec = times[1].tv_sec; 225 tstimes[1].tv_nsec = 1000 * times[1].tv_usec; 226 } 227 228 return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0); 229} 230 231SYSCALL_DEFINE2(utimes, char __user *, filename, 232 struct timeval __user *, utimes) 233{ 234 return sys_futimesat(AT_FDCWD, filename, utimes); 235} 236