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