Blame view
fs/utimes.c
7.15 KB
b24413180 License cleanup: ... |
1 |
// SPDX-License-Identifier: GPL-2.0 |
1c710c896 utimensat impleme... |
2 |
#include <linux/file.h> |
74f9fdfa1 [PATCH] r/o bind ... |
3 |
#include <linux/mount.h> |
82b0547cf [PATCH] Create fs... |
4 5 |
#include <linux/namei.h> #include <linux/utime.h> |
12c2ab5e8 fs/utimes.c shoul... |
6 |
#include <linux/syscalls.h> |
7c0f6ba68 Replace <asm/uacc... |
7 |
#include <linux/uaccess.h> |
1a060ba3c utimes: move comp... |
8 |
#include <linux/compat.h> |
82b0547cf [PATCH] Create fs... |
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <asm/unistd.h> #ifdef __ARCH_WANT_SYS_UTIME /* * sys_utime() can be implemented in user-level using sys_utimes(). * Is this for backwards compatibility? If so, why not move it * into the appropriate arch directory (for those architectures that * need it). */ /* If times==NULL, set access and modification to current time, * must be owner or have write permission. * Else, update from *times, must be owner or super user. */ |
003d7ab47 [CVE-2009-0029] S... |
24 |
SYSCALL_DEFINE2(utime, char __user *, filename, struct utimbuf __user *, times) |
82b0547cf [PATCH] Create fs... |
25 |
{ |
aaed2dd8a utimes: Make utim... |
26 |
struct timespec64 tv[2]; |
82b0547cf [PATCH] Create fs... |
27 |
|
82b0547cf [PATCH] Create fs... |
28 |
if (times) { |
1c710c896 utimensat impleme... |
29 30 31 32 33 |
if (get_user(tv[0].tv_sec, ×->actime) || get_user(tv[1].tv_sec, ×->modtime)) return -EFAULT; tv[0].tv_nsec = 0; tv[1].tv_nsec = 0; |
82b0547cf [PATCH] Create fs... |
34 |
} |
1c710c896 utimensat impleme... |
35 |
return do_utimes(AT_FDCWD, filename, times ? tv : NULL, 0); |
82b0547cf [PATCH] Create fs... |
36 37 38 |
} #endif |
043f46f61 VFS: check nanose... |
39 40 |
static bool nsec_valid(long nsec) { |
4cca92264 [patch for 2.6.26... |
41 |
if (nsec == UTIME_OMIT || nsec == UTIME_NOW) |
043f46f61 VFS: check nanose... |
42 43 44 45 |
return true; return nsec >= 0 && nsec <= 999999999; } |
aaed2dd8a utimes: Make utim... |
46 |
static int utimes_common(const struct path *path, struct timespec64 *times) |
82b0547cf [PATCH] Create fs... |
47 48 |
{ int error; |
82b0547cf [PATCH] Create fs... |
49 |
struct iattr newattrs; |
e9b76fedc [patch 2/4] vfs: ... |
50 |
struct inode *inode = path->dentry->d_inode; |
27ac0ffea locks: break dele... |
51 |
struct inode *delegated_inode = NULL; |
82b0547cf [PATCH] Create fs... |
52 |
|
e9b76fedc [patch 2/4] vfs: ... |
53 |
error = mnt_want_write(path->mnt); |
74f9fdfa1 [PATCH] r/o bind ... |
54 |
if (error) |
e9b76fedc [patch 2/4] vfs: ... |
55 |
goto out; |
82b0547cf [PATCH] Create fs... |
56 |
|
12fd0d308 [patch for 2.6.26... |
57 58 59 |
if (times && times[0].tv_nsec == UTIME_NOW && times[1].tv_nsec == UTIME_NOW) times = NULL; |
82b0547cf [PATCH] Create fs... |
60 61 |
newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { |
1c710c896 utimensat impleme... |
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
if (times[0].tv_nsec == UTIME_OMIT) newattrs.ia_valid &= ~ATTR_ATIME; else if (times[0].tv_nsec != UTIME_NOW) { newattrs.ia_atime.tv_sec = times[0].tv_sec; newattrs.ia_atime.tv_nsec = times[0].tv_nsec; newattrs.ia_valid |= ATTR_ATIME_SET; } if (times[1].tv_nsec == UTIME_OMIT) newattrs.ia_valid &= ~ATTR_MTIME; else if (times[1].tv_nsec != UTIME_NOW) { newattrs.ia_mtime.tv_sec = times[1].tv_sec; newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; newattrs.ia_valid |= ATTR_MTIME_SET; } |
4cca92264 [patch for 2.6.26... |
77 |
/* |
31051c85b fs: Give dentry t... |
78 |
* Tell setattr_prepare(), that this is an explicit time |
9767d7495 [patch 1/4] vfs: ... |
79 80 |
* update, even if neither ATTR_ATIME_SET nor ATTR_MTIME_SET * were used. |
4cca92264 [patch for 2.6.26... |
81 |
*/ |
9767d7495 [patch 1/4] vfs: ... |
82 |
newattrs.ia_valid |= ATTR_TIMES_SET; |
4cca92264 [patch for 2.6.26... |
83 |
} else { |
f2b20f6ee vfs: move permiss... |
84 |
newattrs.ia_valid |= ATTR_TOUCH; |
82b0547cf [PATCH] Create fs... |
85 |
} |
27ac0ffea locks: break dele... |
86 |
retry_deleg: |
5955102c9 wrappers for ->i_... |
87 |
inode_lock(inode); |
27ac0ffea locks: break dele... |
88 |
error = notify_change(path->dentry, &newattrs, &delegated_inode); |
5955102c9 wrappers for ->i_... |
89 |
inode_unlock(inode); |
27ac0ffea locks: break dele... |
90 91 92 93 94 |
if (delegated_inode) { error = break_deleg_wait(&delegated_inode); if (!error) goto retry_deleg; } |
e9b76fedc [patch 2/4] vfs: ... |
95 |
|
e9b76fedc [patch 2/4] vfs: ... |
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
mnt_drop_write(path->mnt); out: return error; } /* * do_utimes - change times on filename or file descriptor * @dfd: open file descriptor, -1 or AT_FDCWD * @filename: path name or NULL * @times: new times or NULL * @flags: zero or more flags (only AT_SYMLINK_NOFOLLOW for the moment) * * If filename is NULL and dfd refers to an open file, then operate on * the file. Otherwise look up filename, possibly using dfd as a * starting point. * * If times==NULL, set access and modification to current time, * must be owner or have write permission. * Else, update from *times, must be owner or super user. */ |
aaed2dd8a utimes: Make utim... |
116 |
long do_utimes(int dfd, const char __user *filename, struct timespec64 *times, |
c78873252 Mark arguments to... |
117 |
int flags) |
e9b76fedc [patch 2/4] vfs: ... |
118 119 120 121 122 123 124 125 126 127 128 129 |
{ int error = -EINVAL; if (times && (!nsec_valid(times[0].tv_nsec) || !nsec_valid(times[1].tv_nsec))) { goto out; } if (flags & ~AT_SYMLINK_NOFOLLOW) goto out; if (filename == NULL && dfd != AT_FDCWD) { |
2903ff019 switch simple cas... |
130 |
struct fd f; |
e9b76fedc [patch 2/4] vfs: ... |
131 132 133 |
if (flags & AT_SYMLINK_NOFOLLOW) goto out; |
2903ff019 switch simple cas... |
134 |
f = fdget(dfd); |
e9b76fedc [patch 2/4] vfs: ... |
135 |
error = -EBADF; |
2903ff019 switch simple cas... |
136 |
if (!f.file) |
e9b76fedc [patch 2/4] vfs: ... |
137 |
goto out; |
2903ff019 switch simple cas... |
138 139 |
error = utimes_common(&f.file->f_path, times); fdput(f); |
e9b76fedc [patch 2/4] vfs: ... |
140 |
} else { |
2d8f30380 [PATCH] sanitize ... |
141 |
struct path path; |
e9b76fedc [patch 2/4] vfs: ... |
142 143 144 145 |
int lookup_flags = 0; if (!(flags & AT_SYMLINK_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; |
a69201d6f vfs: allow utimen... |
146 |
retry: |
2d8f30380 [PATCH] sanitize ... |
147 |
error = user_path_at(dfd, filename, lookup_flags, &path); |
e9b76fedc [patch 2/4] vfs: ... |
148 149 |
if (error) goto out; |
2d8f30380 [PATCH] sanitize ... |
150 151 |
error = utimes_common(&path, times); path_put(&path); |
a69201d6f vfs: allow utimen... |
152 153 154 155 |
if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } |
e9b76fedc [patch 2/4] vfs: ... |
156 |
} |
82b0547cf [PATCH] Create fs... |
157 158 159 |
out: return error; } |
c78873252 Mark arguments to... |
160 |
SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename, |
6559eed8c [CVE-2009-0029] S... |
161 |
struct timespec __user *, utimes, int, flags) |
1c710c896 utimensat impleme... |
162 |
{ |
aaed2dd8a utimes: Make utim... |
163 |
struct timespec64 tstimes[2]; |
1c710c896 utimensat impleme... |
164 165 |
if (utimes) { |
aaed2dd8a utimes: Make utim... |
166 167 |
if ((get_timespec64(&tstimes[0], &utimes[0]) || get_timespec64(&tstimes[1], &utimes[1]))) |
1c710c896 utimensat impleme... |
168 |
return -EFAULT; |
1c710c896 utimensat impleme... |
169 170 171 172 173 174 175 176 177 |
/* Nothing to do, we must not even check the path. */ if (tstimes[0].tv_nsec == UTIME_OMIT && tstimes[1].tv_nsec == UTIME_OMIT) return 0; } return do_utimes(dfd, filename, utimes ? tstimes : NULL, flags); } |
c78873252 Mark arguments to... |
178 |
SYSCALL_DEFINE3(futimesat, int, dfd, const char __user *, filename, |
6559eed8c [CVE-2009-0029] S... |
179 |
struct timeval __user *, utimes) |
82b0547cf [PATCH] Create fs... |
180 181 |
{ struct timeval times[2]; |
aaed2dd8a utimes: Make utim... |
182 |
struct timespec64 tstimes[2]; |
1c710c896 utimensat impleme... |
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
if (utimes) { if (copy_from_user(×, utimes, sizeof(times))) return -EFAULT; /* This test is needed to catch all invalid values. If we would test only in do_utimes we would miss those invalid values truncated by the multiplication with 1000. Note that we also catch UTIME_{NOW,OMIT} here which are only valid for utimensat. */ if (times[0].tv_usec >= 1000000 || times[0].tv_usec < 0 || times[1].tv_usec >= 1000000 || times[1].tv_usec < 0) return -EINVAL; tstimes[0].tv_sec = times[0].tv_sec; tstimes[0].tv_nsec = 1000 * times[0].tv_usec; tstimes[1].tv_sec = times[1].tv_sec; tstimes[1].tv_nsec = 1000 * times[1].tv_usec; } |
82b0547cf [PATCH] Create fs... |
202 |
|
1c710c896 utimensat impleme... |
203 |
return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0); |
82b0547cf [PATCH] Create fs... |
204 |
} |
003d7ab47 [CVE-2009-0029] S... |
205 206 |
SYSCALL_DEFINE2(utimes, char __user *, filename, struct timeval __user *, utimes) |
82b0547cf [PATCH] Create fs... |
207 208 209 |
{ return sys_futimesat(AT_FDCWD, filename, utimes); } |
1a060ba3c utimes: move comp... |
210 211 212 213 214 215 216 217 218 |
#ifdef CONFIG_COMPAT /* * Not all architectures have sys_utime, so implement this in terms * of sys_utimes. */ COMPAT_SYSCALL_DEFINE2(utime, const char __user *, filename, struct compat_utimbuf __user *, t) { |
aaed2dd8a utimes: Make utim... |
219 |
struct timespec64 tv[2]; |
1a060ba3c utimes: move comp... |
220 221 222 223 224 225 226 227 228 229 230 231 232 |
if (t) { if (get_user(tv[0].tv_sec, &t->actime) || get_user(tv[1].tv_sec, &t->modtime)) return -EFAULT; tv[0].tv_nsec = 0; tv[1].tv_nsec = 0; } return do_utimes(AT_FDCWD, filename, t ? tv : NULL, 0); } COMPAT_SYSCALL_DEFINE4(utimensat, unsigned int, dfd, const char __user *, filename, struct compat_timespec __user *, t, int, flags) { |
aaed2dd8a utimes: Make utim... |
233 |
struct timespec64 tv[2]; |
1a060ba3c utimes: move comp... |
234 235 |
if (t) { |
aaed2dd8a utimes: Make utim... |
236 237 |
if (compat_get_timespec64(&tv[0], &t[0]) || compat_get_timespec64(&tv[1], &t[1])) |
1a060ba3c utimes: move comp... |
238 239 240 241 242 243 244 245 246 247 |
return -EFAULT; if (tv[0].tv_nsec == UTIME_OMIT && tv[1].tv_nsec == UTIME_OMIT) return 0; } return do_utimes(dfd, filename, t ? tv : NULL, flags); } COMPAT_SYSCALL_DEFINE3(futimesat, unsigned int, dfd, const char __user *, filename, struct compat_timeval __user *, t) { |
aaed2dd8a utimes: Make utim... |
248 |
struct timespec64 tv[2]; |
1a060ba3c utimes: move comp... |
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
if (t) { if (get_user(tv[0].tv_sec, &t[0].tv_sec) || get_user(tv[0].tv_nsec, &t[0].tv_usec) || get_user(tv[1].tv_sec, &t[1].tv_sec) || get_user(tv[1].tv_nsec, &t[1].tv_usec)) return -EFAULT; if (tv[0].tv_nsec >= 1000000 || tv[0].tv_nsec < 0 || tv[1].tv_nsec >= 1000000 || tv[1].tv_nsec < 0) return -EINVAL; tv[0].tv_nsec *= 1000; tv[1].tv_nsec *= 1000; } return do_utimes(dfd, filename, t ? tv : NULL, 0); } COMPAT_SYSCALL_DEFINE2(utimes, const char __user *, filename, struct compat_timeval __user *, t) { return compat_sys_futimesat(AT_FDCWD, filename, t); } #endif |