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 asmlinkage long sys_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 (!is_owner_or_cap(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, char __user *filename, struct timespec *times, int flags) 130 { 131 int error = -EINVAL; 132 133 if (times && (!nsec_valid(times[0].tv_nsec) || 134 !nsec_valid(times[1].tv_nsec))) { 135 goto out; 136 } 137 138 if (flags & ~AT_SYMLINK_NOFOLLOW) 139 goto out; 140 141 if (filename == NULL && dfd != AT_FDCWD) { 142 struct file *file; 143 144 if (flags & AT_SYMLINK_NOFOLLOW) 145 goto out; 146 147 file = fget(dfd); 148 error = -EBADF; 149 if (!file) 150 goto out; 151 152 error = utimes_common(&file->f_path, times); 153 fput(file); 154 } else { 155 struct path path; 156 int lookup_flags = 0; 157 158 if (!(flags & AT_SYMLINK_NOFOLLOW)) 159 lookup_flags |= LOOKUP_FOLLOW; 160 161 error = user_path_at(dfd, filename, lookup_flags, &path); 162 if (error) 163 goto out; 164 165 error = utimes_common(&path, times); 166 path_put(&path); 167 } 168 169 out: 170 return error; 171 } 172 173 asmlinkage long sys_utimensat(int dfd, char __user *filename, struct timespec __user *utimes, int flags) 174 { 175 struct timespec tstimes[2]; 176 177 if (utimes) { 178 if (copy_from_user(&tstimes, utimes, sizeof(tstimes))) 179 return -EFAULT; 180 181 /* Nothing to do, we must not even check the path. */ 182 if (tstimes[0].tv_nsec == UTIME_OMIT && 183 tstimes[1].tv_nsec == UTIME_OMIT) 184 return 0; 185 } 186 187 return do_utimes(dfd, filename, utimes ? tstimes : NULL, flags); 188 } 189 190 asmlinkage long sys_futimesat(int dfd, char __user *filename, struct timeval __user *utimes) 191 { 192 struct timeval times[2]; 193 struct timespec tstimes[2]; 194 195 if (utimes) { 196 if (copy_from_user(×, utimes, sizeof(times))) 197 return -EFAULT; 198 199 /* This test is needed to catch all invalid values. If we 200 would test only in do_utimes we would miss those invalid 201 values truncated by the multiplication with 1000. Note 202 that we also catch UTIME_{NOW,OMIT} here which are only 203 valid for utimensat. */ 204 if (times[0].tv_usec >= 1000000 || times[0].tv_usec < 0 || 205 times[1].tv_usec >= 1000000 || times[1].tv_usec < 0) 206 return -EINVAL; 207 208 tstimes[0].tv_sec = times[0].tv_sec; 209 tstimes[0].tv_nsec = 1000 * times[0].tv_usec; 210 tstimes[1].tv_sec = times[1].tv_sec; 211 tstimes[1].tv_nsec = 1000 * times[1].tv_usec; 212 } 213 214 return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0); 215 } 216 217 asmlinkage long sys_utimes(char __user *filename, struct timeval __user *utimes) 218 { 219 return sys_futimesat(AT_FDCWD, filename, utimes); 220 } 221