Blame view
fs/utimes.c
7.6 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 |
#include <asm/unistd.h> |
043f46f61 VFS: check nanose... |
10 11 |
static bool nsec_valid(long nsec) { |
4cca92264 [patch for 2.6.26... |
12 |
if (nsec == UTIME_OMIT || nsec == UTIME_NOW) |
043f46f61 VFS: check nanose... |
13 14 15 16 |
return true; return nsec >= 0 && nsec <= 999999999; } |
fd5ad30c7 fs: expose utimes... |
17 |
int vfs_utimes(const struct path *path, struct timespec64 *times) |
82b0547cf [PATCH] Create fs... |
18 19 |
{ int error; |
82b0547cf [PATCH] Create fs... |
20 |
struct iattr newattrs; |
e9b76fedc [patch 2/4] vfs: ... |
21 |
struct inode *inode = path->dentry->d_inode; |
27ac0ffea locks: break dele... |
22 |
struct inode *delegated_inode = NULL; |
82b0547cf [PATCH] Create fs... |
23 |
|
27eb11c96 fs: move timespec... |
24 25 26 27 28 29 30 31 |
if (times) { if (!nsec_valid(times[0].tv_nsec) || !nsec_valid(times[1].tv_nsec)) return -EINVAL; if (times[0].tv_nsec == UTIME_NOW && times[1].tv_nsec == UTIME_NOW) times = NULL; } |
e9b76fedc [patch 2/4] vfs: ... |
32 |
error = mnt_want_write(path->mnt); |
74f9fdfa1 [PATCH] r/o bind ... |
33 |
if (error) |
e9b76fedc [patch 2/4] vfs: ... |
34 |
goto out; |
82b0547cf [PATCH] Create fs... |
35 |
|
82b0547cf [PATCH] Create fs... |
36 37 |
newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { |
1c710c896 utimensat impleme... |
38 39 40 |
if (times[0].tv_nsec == UTIME_OMIT) newattrs.ia_valid &= ~ATTR_ATIME; else if (times[0].tv_nsec != UTIME_NOW) { |
eb31e2f63 utimes: Clamp the... |
41 |
newattrs.ia_atime = times[0]; |
1c710c896 utimensat impleme... |
42 43 44 45 46 47 |
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) { |
eb31e2f63 utimes: Clamp the... |
48 |
newattrs.ia_mtime = times[1]; |
1c710c896 utimensat impleme... |
49 50 |
newattrs.ia_valid |= ATTR_MTIME_SET; } |
4cca92264 [patch for 2.6.26... |
51 |
/* |
31051c85b fs: Give dentry t... |
52 |
* Tell setattr_prepare(), that this is an explicit time |
9767d7495 [patch 1/4] vfs: ... |
53 54 |
* update, even if neither ATTR_ATIME_SET nor ATTR_MTIME_SET * were used. |
4cca92264 [patch for 2.6.26... |
55 |
*/ |
9767d7495 [patch 1/4] vfs: ... |
56 |
newattrs.ia_valid |= ATTR_TIMES_SET; |
4cca92264 [patch for 2.6.26... |
57 |
} else { |
f2b20f6ee vfs: move permiss... |
58 |
newattrs.ia_valid |= ATTR_TOUCH; |
82b0547cf [PATCH] Create fs... |
59 |
} |
27ac0ffea locks: break dele... |
60 |
retry_deleg: |
5955102c9 wrappers for ->i_... |
61 |
inode_lock(inode); |
27ac0ffea locks: break dele... |
62 |
error = notify_change(path->dentry, &newattrs, &delegated_inode); |
5955102c9 wrappers for ->i_... |
63 |
inode_unlock(inode); |
27ac0ffea locks: break dele... |
64 65 66 67 68 |
if (delegated_inode) { error = break_deleg_wait(&delegated_inode); if (!error) goto retry_deleg; } |
e9b76fedc [patch 2/4] vfs: ... |
69 |
|
e9b76fedc [patch 2/4] vfs: ... |
70 71 72 73 |
mnt_drop_write(path->mnt); out: return error; } |
9d4b74aee fs: refactor do_u... |
74 75 76 77 78 |
static int do_utimes_path(int dfd, const char __user *filename, struct timespec64 *times, int flags) { struct path path; int lookup_flags = 0, error; |
9d4b74aee fs: refactor do_u... |
79 80 81 82 83 84 85 86 87 88 89 90 |
if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) return -EINVAL; if (!(flags & AT_SYMLINK_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; if (flags & AT_EMPTY_PATH) lookup_flags |= LOOKUP_EMPTY; retry: error = user_path_at(dfd, filename, lookup_flags, &path); if (error) return error; |
fd5ad30c7 fs: expose utimes... |
91 |
error = vfs_utimes(&path, times); |
9d4b74aee fs: refactor do_u... |
92 93 94 95 96 97 98 99 100 101 102 103 104 |
path_put(&path); if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; } return error; } static int do_utimes_fd(int fd, struct timespec64 *times, int flags) { struct fd f; int error; |
9d4b74aee fs: refactor do_u... |
105 106 107 108 109 110 |
if (flags) return -EINVAL; f = fdget(fd); if (!f.file) return -EBADF; |
fd5ad30c7 fs: expose utimes... |
111 |
error = vfs_utimes(&f.file->f_path, times); |
9d4b74aee fs: refactor do_u... |
112 113 114 |
fdput(f); return error; } |
e9b76fedc [patch 2/4] vfs: ... |
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
/* * 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... |
130 |
long do_utimes(int dfd, const char __user *filename, struct timespec64 *times, |
c78873252 Mark arguments to... |
131 |
int flags) |
e9b76fedc [patch 2/4] vfs: ... |
132 |
{ |
9d4b74aee fs: refactor do_u... |
133 134 135 |
if (filename == NULL && dfd != AT_FDCWD) return do_utimes_fd(dfd, times, flags); return do_utimes_path(dfd, filename, times, flags); |
82b0547cf [PATCH] Create fs... |
136 |
} |
c78873252 Mark arguments to... |
137 |
SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename, |
a4f7a3004 y2038: Change sys... |
138 |
struct __kernel_timespec __user *, utimes, int, flags) |
1c710c896 utimensat impleme... |
139 |
{ |
aaed2dd8a utimes: Make utim... |
140 |
struct timespec64 tstimes[2]; |
1c710c896 utimensat impleme... |
141 142 |
if (utimes) { |
aaed2dd8a utimes: Make utim... |
143 144 |
if ((get_timespec64(&tstimes[0], &utimes[0]) || get_timespec64(&tstimes[1], &utimes[1]))) |
1c710c896 utimensat impleme... |
145 |
return -EFAULT; |
1c710c896 utimensat impleme... |
146 147 148 149 150 151 152 153 154 |
/* 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); } |
185cfaf76 y2038: Compile ut... |
155 156 157 158 159 160 161 |
#ifdef __ARCH_WANT_SYS_UTIME /* * futimesat(), utimes() and utime() are older versions of utimensat() * that are provided for compatibility with traditional C libraries. * On modern architectures, we always use libc wrappers around * utimensat() instead. */ |
f13903587 fs: add do_futime... |
162 |
static long do_futimesat(int dfd, const char __user *filename, |
75d319c06 y2038: syscalls: ... |
163 |
struct __kernel_old_timeval __user *utimes) |
82b0547cf [PATCH] Create fs... |
164 |
{ |
75d319c06 y2038: syscalls: ... |
165 |
struct __kernel_old_timeval times[2]; |
aaed2dd8a utimes: Make utim... |
166 |
struct timespec64 tstimes[2]; |
1c710c896 utimensat impleme... |
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
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... |
186 |
|
1c710c896 utimensat impleme... |
187 |
return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0); |
82b0547cf [PATCH] Create fs... |
188 |
} |
f13903587 fs: add do_futime... |
189 190 |
SYSCALL_DEFINE3(futimesat, int, dfd, const char __user *, filename, |
75d319c06 y2038: syscalls: ... |
191 |
struct __kernel_old_timeval __user *, utimes) |
f13903587 fs: add do_futime... |
192 193 194 |
{ return do_futimesat(dfd, filename, utimes); } |
003d7ab47 [CVE-2009-0029] S... |
195 |
SYSCALL_DEFINE2(utimes, char __user *, filename, |
75d319c06 y2038: syscalls: ... |
196 |
struct __kernel_old_timeval __user *, utimes) |
82b0547cf [PATCH] Create fs... |
197 |
{ |
f13903587 fs: add do_futime... |
198 |
return do_futimesat(AT_FDCWD, filename, utimes); |
82b0547cf [PATCH] Create fs... |
199 |
} |
1a060ba3c utimes: move comp... |
200 |
|
185cfaf76 y2038: Compile ut... |
201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
SYSCALL_DEFINE2(utime, char __user *, filename, struct utimbuf __user *, times) { struct timespec64 tv[2]; if (times) { 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; } return do_utimes(AT_FDCWD, filename, times ? tv : NULL, 0); } #endif |
4faea239e y2038: utimes: Re... |
215 |
#ifdef CONFIG_COMPAT_32BIT_TIME |
1a060ba3c utimes: move comp... |
216 217 218 219 |
/* * Not all architectures have sys_utime, so implement this in terms * of sys_utimes. */ |
4faea239e y2038: utimes: Re... |
220 |
#ifdef __ARCH_WANT_SYS_UTIME32 |
8dabe7245 y2038: syscalls: ... |
221 222 |
SYSCALL_DEFINE2(utime32, const char __user *, filename, struct old_utimbuf32 __user *, t) |
1a060ba3c utimes: move comp... |
223 |
{ |
aaed2dd8a utimes: Make utim... |
224 |
struct timespec64 tv[2]; |
1a060ba3c utimes: move comp... |
225 226 227 228 229 230 231 232 233 234 |
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); } |
4faea239e y2038: utimes: Re... |
235 |
#endif |
1a060ba3c utimes: move comp... |
236 |
|
8dabe7245 y2038: syscalls: ... |
237 |
SYSCALL_DEFINE4(utimensat_time32, unsigned int, dfd, const char __user *, filename, struct old_timespec32 __user *, t, int, flags) |
1a060ba3c utimes: move comp... |
238 |
{ |
aaed2dd8a utimes: Make utim... |
239 |
struct timespec64 tv[2]; |
1a060ba3c utimes: move comp... |
240 241 |
if (t) { |
9afc5eee6 y2038: globally r... |
242 243 |
if (get_old_timespec32(&tv[0], &t[0]) || get_old_timespec32(&tv[1], &t[1])) |
1a060ba3c utimes: move comp... |
244 245 246 247 248 249 250 |
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); } |
4faea239e y2038: utimes: Re... |
251 |
#ifdef __ARCH_WANT_SYS_UTIME32 |
ab641afa7 fs: add do_compat... |
252 |
static long do_compat_futimesat(unsigned int dfd, const char __user *filename, |
9afc5eee6 y2038: globally r... |
253 |
struct old_timeval32 __user *t) |
1a060ba3c utimes: move comp... |
254 |
{ |
aaed2dd8a utimes: Make utim... |
255 |
struct timespec64 tv[2]; |
1a060ba3c utimes: move comp... |
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
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); } |
8dabe7245 y2038: syscalls: ... |
271 |
SYSCALL_DEFINE3(futimesat_time32, unsigned int, dfd, |
ab641afa7 fs: add do_compat... |
272 |
const char __user *, filename, |
9afc5eee6 y2038: globally r... |
273 |
struct old_timeval32 __user *, t) |
ab641afa7 fs: add do_compat... |
274 275 276 |
{ return do_compat_futimesat(dfd, filename, t); } |
8dabe7245 y2038: syscalls: ... |
277 |
SYSCALL_DEFINE2(utimes_time32, const char __user *, filename, struct old_timeval32 __user *, t) |
1a060ba3c utimes: move comp... |
278 |
{ |
ab641afa7 fs: add do_compat... |
279 |
return do_compat_futimesat(AT_FDCWD, filename, t); |
1a060ba3c utimes: move comp... |
280 281 |
} #endif |
4faea239e y2038: utimes: Re... |
282 |
#endif |