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 */ 27 SYSCALL_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 43 static 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 51 static 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 57 error = mnt_want_write(path->mnt); 58 if (error) 59 goto out; 60 61 if (times && times[0].tv_nsec == UTIME_NOW && 62 times[1].tv_nsec == UTIME_NOW) 63 times = NULL; 64 65 newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; 66 if (times) { 67 if (times[0].tv_nsec == UTIME_OMIT) 68 newattrs.ia_valid &= ~ATTR_ATIME; 69 else if (times[0].tv_nsec != UTIME_NOW) { 70 newattrs.ia_atime.tv_sec = times[0].tv_sec; 71 newattrs.ia_atime.tv_nsec = times[0].tv_nsec; 72 newattrs.ia_valid |= ATTR_ATIME_SET; 73 } 74 75 if (times[1].tv_nsec == UTIME_OMIT) 76 newattrs.ia_valid &= ~ATTR_MTIME; 77 else if (times[1].tv_nsec != UTIME_NOW) { 78 newattrs.ia_mtime.tv_sec = times[1].tv_sec; 79 newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; 80 newattrs.ia_valid |= ATTR_MTIME_SET; 81 } 82 /* 83 * Tell inode_change_ok(), that this is an explicit time 84 * update, even if neither ATTR_ATIME_SET nor ATTR_MTIME_SET 85 * were used. 86 */ 87 newattrs.ia_valid |= ATTR_TIMES_SET; 88 } else { 89 /* 90 * If times is NULL (or both times are UTIME_NOW), 91 * then we need to check permissions, because 92 * inode_change_ok() won't do it. 93 */ 94 error = -EACCES; 95 if (IS_IMMUTABLE(inode)) 96 goto mnt_drop_write_and_out; 97 98 if (!inode_owner_or_capable(inode)) { 99 error = inode_permission(inode, MAY_WRITE); 100 if (error) 101 goto mnt_drop_write_and_out; 102 } 103 } 104 mutex_lock(&inode->i_mutex); 105 error = notify_change(path->dentry, &newattrs); 106 mutex_unlock(&inode->i_mutex); 107 108 mnt_drop_write_and_out: 109 mnt_drop_write(path->mnt); 110 out: 111 return error; 112 } 113 114 /* 115 * do_utimes - change times on filename or file descriptor 116 * @dfd: open file descriptor, -1 or AT_FDCWD 117 * @filename: path name or NULL 118 * @times: new times or NULL 119 * @flags: zero or more flags (only AT_SYMLINK_NOFOLLOW for the moment) 120 * 121 * If filename is NULL and dfd refers to an open file, then operate on 122 * the file. Otherwise look up filename, possibly using dfd as a 123 * starting point. 124 * 125 * If times==NULL, set access and modification to current time, 126 * must be owner or have write permission. 127 * Else, update from *times, must be owner or super user. 128 */ 129 long do_utimes(int dfd, const char __user *filename, struct timespec *times, 130 int flags) 131 { 132 int error = -EINVAL; 133 134 if (times && (!nsec_valid(times[0].tv_nsec) || 135 !nsec_valid(times[1].tv_nsec))) { 136 goto out; 137 } 138 139 if (flags & ~AT_SYMLINK_NOFOLLOW) 140 goto out; 141 142 if (filename == NULL && dfd != AT_FDCWD) { 143 struct fd f; 144 145 if (flags & AT_SYMLINK_NOFOLLOW) 146 goto out; 147 148 f = fdget(dfd); 149 error = -EBADF; 150 if (!f.file) 151 goto out; 152 153 error = utimes_common(&f.file->f_path, times); 154 fdput(f); 155 } else { 156 struct path path; 157 int lookup_flags = 0; 158 159 if (!(flags & AT_SYMLINK_NOFOLLOW)) 160 lookup_flags |= LOOKUP_FOLLOW; 161 162 error = user_path_at(dfd, filename, lookup_flags, &path); 163 if (error) 164 goto out; 165 166 error = utimes_common(&path, times); 167 path_put(&path); 168 } 169 170 out: 171 return error; 172 } 173 174 SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename, 175 struct timespec __user *, utimes, int, flags) 176 { 177 struct timespec tstimes[2]; 178 179 if (utimes) { 180 if (copy_from_user(&tstimes, utimes, sizeof(tstimes))) 181 return -EFAULT; 182 183 /* Nothing to do, we must not even check the path. */ 184 if (tstimes[0].tv_nsec == UTIME_OMIT && 185 tstimes[1].tv_nsec == UTIME_OMIT) 186 return 0; 187 } 188 189 return do_utimes(dfd, filename, utimes ? tstimes : NULL, flags); 190 } 191 192 SYSCALL_DEFINE3(futimesat, int, dfd, const char __user *, filename, 193 struct timeval __user *, utimes) 194 { 195 struct timeval times[2]; 196 struct timespec tstimes[2]; 197 198 if (utimes) { 199 if (copy_from_user(×, utimes, sizeof(times))) 200 return -EFAULT; 201 202 /* This test is needed to catch all invalid values. If we 203 would test only in do_utimes we would miss those invalid 204 values truncated by the multiplication with 1000. Note 205 that we also catch UTIME_{NOW,OMIT} here which are only 206 valid for utimensat. */ 207 if (times[0].tv_usec >= 1000000 || times[0].tv_usec < 0 || 208 times[1].tv_usec >= 1000000 || times[1].tv_usec < 0) 209 return -EINVAL; 210 211 tstimes[0].tv_sec = times[0].tv_sec; 212 tstimes[0].tv_nsec = 1000 * times[0].tv_usec; 213 tstimes[1].tv_sec = times[1].tv_sec; 214 tstimes[1].tv_nsec = 1000 * times[1].tv_usec; 215 } 216 217 return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0); 218 } 219 220 SYSCALL_DEFINE2(utimes, char __user *, filename, 221 struct timeval __user *, utimes) 222 { 223 return sys_futimesat(AT_FDCWD, filename, utimes); 224 } 225