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 /* If times==NULL, set access and modification to current time, 52 * must be owner or have write permission. 53 * Else, update from *times, must be owner or super user. 54 */ 55 long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags) 56 { 57 int error; 58 struct nameidata nd; 59 struct dentry *dentry; 60 struct inode *inode; 61 struct iattr newattrs; 62 struct file *f = NULL; 63 struct vfsmount *mnt; 64 65 error = -EINVAL; 66 if (times && (!nsec_valid(times[0].tv_nsec) || 67 !nsec_valid(times[1].tv_nsec))) { 68 goto out; 69 } 70 71 if (flags & ~AT_SYMLINK_NOFOLLOW) 72 goto out; 73 74 if (filename == NULL && dfd != AT_FDCWD) { 75 error = -EINVAL; 76 if (flags & AT_SYMLINK_NOFOLLOW) 77 goto out; 78 79 error = -EBADF; 80 f = fget(dfd); 81 if (!f) 82 goto out; 83 dentry = f->f_path.dentry; 84 mnt = f->f_path.mnt; 85 } else { 86 error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd); 87 if (error) 88 goto out; 89 90 dentry = nd.path.dentry; 91 mnt = nd.path.mnt; 92 } 93 94 inode = dentry->d_inode; 95 96 error = mnt_want_write(mnt); 97 if (error) 98 goto dput_and_out; 99 100 /* Don't worry, the checks are done in inode_change_ok() */ 101 newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; 102 if (times) { 103 error = -EPERM; 104 if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) 105 goto mnt_drop_write_and_out; 106 107 if (times[0].tv_nsec == UTIME_OMIT) 108 newattrs.ia_valid &= ~ATTR_ATIME; 109 else if (times[0].tv_nsec != UTIME_NOW) { 110 newattrs.ia_atime.tv_sec = times[0].tv_sec; 111 newattrs.ia_atime.tv_nsec = times[0].tv_nsec; 112 newattrs.ia_valid |= ATTR_ATIME_SET; 113 } 114 115 if (times[1].tv_nsec == UTIME_OMIT) 116 newattrs.ia_valid &= ~ATTR_MTIME; 117 else if (times[1].tv_nsec != UTIME_NOW) { 118 newattrs.ia_mtime.tv_sec = times[1].tv_sec; 119 newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; 120 newattrs.ia_valid |= ATTR_MTIME_SET; 121 } 122 } else { 123 error = -EACCES; 124 if (IS_IMMUTABLE(inode)) 125 goto mnt_drop_write_and_out; 126 127 if (!is_owner_or_cap(inode)) { 128 if (f) { 129 if (!(f->f_mode & FMODE_WRITE)) 130 goto mnt_drop_write_and_out; 131 } else { 132 error = vfs_permission(&nd, MAY_WRITE); 133 if (error) 134 goto mnt_drop_write_and_out; 135 } 136 } 137 } 138 mutex_lock(&inode->i_mutex); 139 error = notify_change(dentry, &newattrs); 140 mutex_unlock(&inode->i_mutex); 141 mnt_drop_write_and_out: 142 mnt_drop_write(mnt); 143 dput_and_out: 144 if (f) 145 fput(f); 146 else 147 path_put(&nd.path); 148 out: 149 return error; 150 } 151 152 asmlinkage long sys_utimensat(int dfd, char __user *filename, struct timespec __user *utimes, int flags) 153 { 154 struct timespec tstimes[2]; 155 156 if (utimes) { 157 if (copy_from_user(&tstimes, utimes, sizeof(tstimes))) 158 return -EFAULT; 159 if ((tstimes[0].tv_nsec == UTIME_OMIT || 160 tstimes[0].tv_nsec == UTIME_NOW) && 161 tstimes[0].tv_sec != 0) 162 return -EINVAL; 163 if ((tstimes[1].tv_nsec == UTIME_OMIT || 164 tstimes[1].tv_nsec == UTIME_NOW) && 165 tstimes[1].tv_sec != 0) 166 return -EINVAL; 167 168 /* Nothing to do, we must not even check the path. */ 169 if (tstimes[0].tv_nsec == UTIME_OMIT && 170 tstimes[1].tv_nsec == UTIME_OMIT) 171 return 0; 172 } 173 174 return do_utimes(dfd, filename, utimes ? tstimes : NULL, flags); 175 } 176 177 asmlinkage long sys_futimesat(int dfd, char __user *filename, struct timeval __user *utimes) 178 { 179 struct timeval times[2]; 180 struct timespec tstimes[2]; 181 182 if (utimes) { 183 if (copy_from_user(×, utimes, sizeof(times))) 184 return -EFAULT; 185 186 /* This test is needed to catch all invalid values. If we 187 would test only in do_utimes we would miss those invalid 188 values truncated by the multiplication with 1000. Note 189 that we also catch UTIME_{NOW,OMIT} here which are only 190 valid for utimensat. */ 191 if (times[0].tv_usec >= 1000000 || times[0].tv_usec < 0 || 192 times[1].tv_usec >= 1000000 || times[1].tv_usec < 0) 193 return -EINVAL; 194 195 tstimes[0].tv_sec = times[0].tv_sec; 196 tstimes[0].tv_nsec = 1000 * times[0].tv_usec; 197 tstimes[1].tv_sec = times[1].tv_sec; 198 tstimes[1].tv_nsec = 1000 * times[1].tv_usec; 199 } 200 201 return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0); 202 } 203 204 asmlinkage long sys_utimes(char __user *filename, struct timeval __user *utimes) 205 { 206 return sys_futimesat(AT_FDCWD, filename, utimes); 207 } 208