Blame view
fs/utimes.c
5.66 KB
82b0547cf [PATCH] Create fs... |
1 |
#include <linux/compiler.h> |
1c710c896 utimensat impleme... |
2 |
#include <linux/file.h> |
82b0547cf [PATCH] Create fs... |
3 4 |
#include <linux/fs.h> #include <linux/linkage.h> |
74f9fdfa1 [PATCH] r/o bind ... |
5 |
#include <linux/mount.h> |
82b0547cf [PATCH] Create fs... |
6 |
#include <linux/namei.h> |
914e26379 [PATCH] severing ... |
7 |
#include <linux/sched.h> |
1c710c896 utimensat impleme... |
8 |
#include <linux/stat.h> |
82b0547cf [PATCH] Create fs... |
9 |
#include <linux/utime.h> |
12c2ab5e8 fs/utimes.c shoul... |
10 |
#include <linux/syscalls.h> |
82b0547cf [PATCH] Create fs... |
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include <asm/uaccess.h> #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... |
27 |
SYSCALL_DEFINE2(utime, char __user *, filename, struct utimbuf __user *, times) |
82b0547cf [PATCH] Create fs... |
28 |
{ |
1c710c896 utimensat impleme... |
29 |
struct timespec tv[2]; |
82b0547cf [PATCH] Create fs... |
30 |
|
82b0547cf [PATCH] Create fs... |
31 |
if (times) { |
1c710c896 utimensat impleme... |
32 33 34 35 36 |
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... |
37 |
} |
1c710c896 utimensat impleme... |
38 |
return do_utimes(AT_FDCWD, filename, times ? tv : NULL, 0); |
82b0547cf [PATCH] Create fs... |
39 40 41 |
} #endif |
043f46f61 VFS: check nanose... |
42 43 |
static bool nsec_valid(long nsec) { |
4cca92264 [patch for 2.6.26... |
44 |
if (nsec == UTIME_OMIT || nsec == UTIME_NOW) |
043f46f61 VFS: check nanose... |
45 46 47 48 |
return true; return nsec >= 0 && nsec <= 999999999; } |
e9b76fedc [patch 2/4] vfs: ... |
49 |
static int utimes_common(struct path *path, struct timespec *times) |
82b0547cf [PATCH] Create fs... |
50 51 |
{ int error; |
82b0547cf [PATCH] Create fs... |
52 |
struct iattr newattrs; |
e9b76fedc [patch 2/4] vfs: ... |
53 |
struct inode *inode = path->dentry->d_inode; |
82b0547cf [PATCH] Create fs... |
54 |
|
e9b76fedc [patch 2/4] vfs: ... |
55 |
error = mnt_want_write(path->mnt); |
74f9fdfa1 [PATCH] r/o bind ... |
56 |
if (error) |
e9b76fedc [patch 2/4] vfs: ... |
57 |
goto out; |
82b0547cf [PATCH] Create fs... |
58 |
|
12fd0d308 [patch for 2.6.26... |
59 60 61 |
if (times && times[0].tv_nsec == UTIME_NOW && times[1].tv_nsec == UTIME_NOW) times = NULL; |
82b0547cf [PATCH] Create fs... |
62 63 |
newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { |
1c710c896 utimensat impleme... |
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
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... |
79 |
/* |
9767d7495 [patch 1/4] vfs: ... |
80 81 82 |
* Tell inode_change_ok(), that this is an explicit time * update, even if neither ATTR_ATIME_SET nor ATTR_MTIME_SET * were used. |
4cca92264 [patch for 2.6.26... |
83 |
*/ |
9767d7495 [patch 1/4] vfs: ... |
84 |
newattrs.ia_valid |= ATTR_TIMES_SET; |
4cca92264 [patch for 2.6.26... |
85 |
} else { |
4cca92264 [patch for 2.6.26... |
86 87 88 89 90 |
/* * If times is NULL (or both times are UTIME_NOW), * then we need to check permissions, because * inode_change_ok() won't do it. */ |
82b0547cf [PATCH] Create fs... |
91 92 |
error = -EACCES; if (IS_IMMUTABLE(inode)) |
74f9fdfa1 [PATCH] r/o bind ... |
93 |
goto mnt_drop_write_and_out; |
82b0547cf [PATCH] Create fs... |
94 |
|
2e1496707 userns: rename is... |
95 |
if (!inode_owner_or_capable(inode)) { |
f419a2e3b [PATCH] kill name... |
96 |
error = inode_permission(inode, MAY_WRITE); |
c70f84417 [patch for 2.6.26... |
97 98 |
if (error) goto mnt_drop_write_and_out; |
1e5de2837 Fix permission ch... |
99 |
} |
82b0547cf [PATCH] Create fs... |
100 101 |
} mutex_lock(&inode->i_mutex); |
e9b76fedc [patch 2/4] vfs: ... |
102 |
error = notify_change(path->dentry, &newattrs); |
82b0547cf [PATCH] Create fs... |
103 |
mutex_unlock(&inode->i_mutex); |
e9b76fedc [patch 2/4] vfs: ... |
104 |
|
74f9fdfa1 [PATCH] r/o bind ... |
105 |
mnt_drop_write_and_out: |
e9b76fedc [patch 2/4] vfs: ... |
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
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. */ |
c78873252 Mark arguments to... |
126 127 |
long do_utimes(int dfd, const char __user *filename, struct timespec *times, int flags) |
e9b76fedc [patch 2/4] vfs: ... |
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
{ 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) { struct file *file; if (flags & AT_SYMLINK_NOFOLLOW) goto out; file = fget(dfd); error = -EBADF; if (!file) goto out; error = utimes_common(&file->f_path, times); fput(file); } else { |
2d8f30380 [PATCH] sanitize ... |
153 |
struct path path; |
e9b76fedc [patch 2/4] vfs: ... |
154 155 156 157 |
int lookup_flags = 0; if (!(flags & AT_SYMLINK_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; |
2d8f30380 [PATCH] sanitize ... |
158 |
error = user_path_at(dfd, filename, lookup_flags, &path); |
e9b76fedc [patch 2/4] vfs: ... |
159 160 |
if (error) goto out; |
2d8f30380 [PATCH] sanitize ... |
161 162 |
error = utimes_common(&path, times); path_put(&path); |
e9b76fedc [patch 2/4] vfs: ... |
163 |
} |
82b0547cf [PATCH] Create fs... |
164 165 166 |
out: return error; } |
c78873252 Mark arguments to... |
167 |
SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename, |
6559eed8c [CVE-2009-0029] S... |
168 |
struct timespec __user *, utimes, int, flags) |
1c710c896 utimensat impleme... |
169 170 171 172 173 174 |
{ struct timespec tstimes[2]; if (utimes) { if (copy_from_user(&tstimes, utimes, sizeof(tstimes))) return -EFAULT; |
1c710c896 utimensat impleme... |
175 176 177 178 179 180 181 182 183 |
/* 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... |
184 |
SYSCALL_DEFINE3(futimesat, int, dfd, const char __user *, filename, |
6559eed8c [CVE-2009-0029] S... |
185 |
struct timeval __user *, utimes) |
82b0547cf [PATCH] Create fs... |
186 187 |
{ struct timeval times[2]; |
1c710c896 utimensat impleme... |
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
struct timespec tstimes[2]; 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... |
208 |
|
1c710c896 utimensat impleme... |
209 |
return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0); |
82b0547cf [PATCH] Create fs... |
210 |
} |
003d7ab47 [CVE-2009-0029] S... |
211 212 |
SYSCALL_DEFINE2(utimes, char __user *, filename, struct timeval __user *, utimes) |
82b0547cf [PATCH] Create fs... |
213 214 215 |
{ return sys_futimesat(AT_FDCWD, filename, utimes); } |