1 #include <linux/compiler.h> 2 #include <linux/file.h> 3 #include <linux/fs.h> 4 #include <linux/linkage.h> 5 #include <linux/namei.h> 6 #include <linux/sched.h> 7 #include <linux/stat.h> 8 #include <linux/utime.h> 9 #include <linux/syscalls.h> 10 #include <asm/uaccess.h> 11 #include <asm/unistd.h> 12 13 #ifdef __ARCH_WANT_SYS_UTIME 14 15 /* 16 * sys_utime() can be implemented in user-level using sys_utimes(). 17 * Is this for backwards compatibility? If so, why not move it 18 * into the appropriate arch directory (for those architectures that 19 * need it). 20 */ 21 22 /* If times==NULL, set access and modification to current time, 23 * must be owner or have write permission. 24 * Else, update from *times, must be owner or super user. 25 */ 26 asmlinkage long sys_utime(char __user *filename, struct utimbuf __user *times) 27 { 28 struct timespec tv[2]; 29 30 if (times) { 31 if (get_user(tv[0].tv_sec, ×->actime) || 32 get_user(tv[1].tv_sec, ×->modtime)) 33 return -EFAULT; 34 tv[0].tv_nsec = 0; 35 tv[1].tv_nsec = 0; 36 } 37 return do_utimes(AT_FDCWD, filename, times ? tv : NULL, 0); 38 } 39 40 #endif 41 42 static bool nsec_valid(long nsec) 43 { 44 if (nsec == UTIME_OMIT || nsec == UTIME_NOW) 45 return true; 46 47 return nsec >= 0 && nsec <= 999999999; 48 } 49 50 /* If times==NULL, set access and modification to current time, 51 * must be owner or have write permission. 52 * Else, update from *times, must be owner or super user. 53 */ 54 long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags) 55 { 56 int error; 57 struct nameidata nd; 58 struct dentry *dentry; 59 struct inode *inode; 60 struct iattr newattrs; 61 struct file *f = NULL; 62 63 error = -EINVAL; 64 if (times && (!nsec_valid(times[0].tv_nsec) || 65 !nsec_valid(times[1].tv_nsec))) { 66 goto out; 67 } 68 69 if (flags & ~AT_SYMLINK_NOFOLLOW) 70 goto out; 71 72 if (filename == NULL && dfd != AT_FDCWD) { 73 error = -EINVAL; 74 if (flags & AT_SYMLINK_NOFOLLOW) 75 goto out; 76 77 error = -EBADF; 78 f = fget(dfd); 79 if (!f) 80 goto out; 81 dentry = f->f_path.dentry; 82 } else { 83 error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd); 84 if (error) 85 goto out; 86 87 dentry = nd.dentry; 88 } 89 90 inode = dentry->d_inode; 91 92 error = -EROFS; 93 if (IS_RDONLY(inode)) 94 goto dput_and_out; 95 96 /* Don't worry, the checks are done in inode_change_ok() */ 97 newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; 98 if (times) { 99 error = -EPERM; 100 if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) 101 goto dput_and_out; 102 103 if (times[0].tv_nsec == UTIME_OMIT) 104 newattrs.ia_valid &= ~ATTR_ATIME; 105 else if (times[0].tv_nsec != UTIME_NOW) { 106 newattrs.ia_atime.tv_sec = times[0].tv_sec; 107 newattrs.ia_atime.tv_nsec = times[0].tv_nsec; 108 newattrs.ia_valid |= ATTR_ATIME_SET; 109 } 110 111 if (times[1].tv_nsec == UTIME_OMIT) 112 newattrs.ia_valid &= ~ATTR_MTIME; 113 else if (times[1].tv_nsec != UTIME_NOW) { 114 newattrs.ia_mtime.tv_sec = times[1].tv_sec; 115 newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; 116 newattrs.ia_valid |= ATTR_MTIME_SET; 117 } 118 } else { 119 error = -EACCES; 120 if (IS_IMMUTABLE(inode)) 121 goto dput_and_out; 122 123 if (!is_owner_or_cap(inode)) { 124 if (f) { 125 if (!(f->f_mode & FMODE_WRITE)) 126 goto dput_and_out; 127 } else { 128 error = vfs_permission(&nd, MAY_WRITE); 129 if (error) 130 goto dput_and_out; 131 } 132 } 133 } 134 mutex_lock(&inode->i_mutex); 135 error = notify_change(dentry, &newattrs); 136 mutex_unlock(&inode->i_mutex); 137 dput_and_out: 138 if (f) 139 fput(f); 140 else 141 path_release(&nd); 142 out: 143 return error; 144 } 145 146 asmlinkage long sys_utimensat(int dfd, char __user *filename, struct timespec __user *utimes, int flags) 147 { 148 struct timespec tstimes[2]; 149 150 if (utimes) { 151 if (copy_from_user(&tstimes, utimes, sizeof(tstimes))) 152 return -EFAULT; 153 if ((tstimes[0].tv_nsec == UTIME_OMIT || 154 tstimes[0].tv_nsec == UTIME_NOW) && 155 tstimes[0].tv_sec != 0) 156 return -EINVAL; 157 if ((tstimes[1].tv_nsec == UTIME_OMIT || 158 tstimes[1].tv_nsec == UTIME_NOW) && 159 tstimes[1].tv_sec != 0) 160 return -EINVAL; 161 162 /* Nothing to do, we must not even check the path. */ 163 if (tstimes[0].tv_nsec == UTIME_OMIT && 164 tstimes[1].tv_nsec == UTIME_OMIT) 165 return 0; 166 } 167 168 return do_utimes(dfd, filename, utimes ? tstimes : NULL, flags); 169 } 170 171 asmlinkage long sys_futimesat(int dfd, char __user *filename, struct timeval __user *utimes) 172 { 173 struct timeval times[2]; 174 struct timespec tstimes[2]; 175 176 if (utimes) { 177 if (copy_from_user(×, utimes, sizeof(times))) 178 return -EFAULT; 179 180 /* This test is needed to catch all invalid values. If we 181 would test only in do_utimes we would miss those invalid 182 values truncated by the multiplication with 1000. Note 183 that we also catch UTIME_{NOW,OMIT} here which are only 184 valid for utimensat. */ 185 if (times[0].tv_usec >= 1000000 || times[0].tv_usec < 0 || 186 times[1].tv_usec >= 1000000 || times[1].tv_usec < 0) 187 return -EINVAL; 188 189 tstimes[0].tv_sec = times[0].tv_sec; 190 tstimes[0].tv_nsec = 1000 * times[0].tv_usec; 191 tstimes[1].tv_sec = times[1].tv_sec; 192 tstimes[1].tv_nsec = 1000 * times[1].tv_usec; 193 } 194 195 return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0); 196 } 197 198 asmlinkage long sys_utimes(char __user *filename, struct timeval __user *utimes) 199 { 200 return sys_futimesat(AT_FDCWD, filename, utimes); 201 } 202