Blame view
fs/utimes.c
4.85 KB
82b0547cf
|
1 |
#include <linux/compiler.h> |
1c710c896
|
2 |
#include <linux/file.h> |
82b0547cf
|
3 4 5 |
#include <linux/fs.h> #include <linux/linkage.h> #include <linux/namei.h> |
914e26379
|
6 |
#include <linux/sched.h> |
1c710c896
|
7 |
#include <linux/stat.h> |
82b0547cf
|
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <linux/utime.h> #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. */ |
1c710c896
|
25 |
asmlinkage long sys_utime(char __user *filename, struct utimbuf __user *times) |
82b0547cf
|
26 |
{ |
1c710c896
|
27 |
struct timespec tv[2]; |
82b0547cf
|
28 |
|
82b0547cf
|
29 |
if (times) { |
1c710c896
|
30 31 32 33 34 |
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
|
35 |
} |
1c710c896
|
36 |
return do_utimes(AT_FDCWD, filename, times ? tv : NULL, 0); |
82b0547cf
|
37 38 39 40 41 42 43 44 |
} #endif /* 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. */ |
1c710c896
|
45 |
long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags) |
82b0547cf
|
46 47 48 |
{ int error; struct nameidata nd; |
1c710c896
|
49 50 |
struct dentry *dentry; struct inode *inode; |
82b0547cf
|
51 |
struct iattr newattrs; |
1c710c896
|
52 |
struct file *f = NULL; |
82b0547cf
|
53 |
|
1c710c896
|
54 55 |
error = -EINVAL; if (flags & ~AT_SYMLINK_NOFOLLOW) |
82b0547cf
|
56 |
goto out; |
1c710c896
|
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
if (filename == NULL && dfd != AT_FDCWD) { error = -EINVAL; if (flags & AT_SYMLINK_NOFOLLOW) goto out; error = -EBADF; f = fget(dfd); if (!f) goto out; dentry = f->f_path.dentry; } else { error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd); if (error) goto out; dentry = nd.dentry; } inode = dentry->d_inode; |
82b0547cf
|
77 78 79 80 81 82 83 84 85 86 87 |
error = -EROFS; if (IS_RDONLY(inode)) goto dput_and_out; /* Don't worry, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { error = -EPERM; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) goto dput_and_out; |
1c710c896
|
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
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; } |
82b0547cf
|
103 104 105 106 |
} else { error = -EACCES; if (IS_IMMUTABLE(inode)) goto dput_and_out; |
3bd858ab1
|
107 |
if (!is_owner_or_cap(inode)) { |
1e5de2837
|
108 109 110 111 112 113 114 115 116 |
if (f) { if (!(f->f_mode & FMODE_WRITE)) goto dput_and_out; } else { error = vfs_permission(&nd, MAY_WRITE); if (error) goto dput_and_out; } } |
82b0547cf
|
117 118 |
} mutex_lock(&inode->i_mutex); |
1c710c896
|
119 |
error = notify_change(dentry, &newattrs); |
82b0547cf
|
120 121 |
mutex_unlock(&inode->i_mutex); dput_and_out: |
1c710c896
|
122 123 124 125 |
if (f) fput(f); else path_release(&nd); |
82b0547cf
|
126 127 128 |
out: return error; } |
1c710c896
|
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
asmlinkage long sys_utimensat(int dfd, char __user *filename, struct timespec __user *utimes, int flags) { struct timespec tstimes[2]; if (utimes) { if (copy_from_user(&tstimes, utimes, sizeof(tstimes))) return -EFAULT; if ((tstimes[0].tv_nsec == UTIME_OMIT || tstimes[0].tv_nsec == UTIME_NOW) && tstimes[0].tv_sec != 0) return -EINVAL; if ((tstimes[1].tv_nsec == UTIME_OMIT || tstimes[1].tv_nsec == UTIME_NOW) && tstimes[1].tv_sec != 0) return -EINVAL; /* 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); } |
82b0547cf
|
153 154 155 |
asmlinkage long sys_futimesat(int dfd, char __user *filename, struct timeval __user *utimes) { struct timeval times[2]; |
1c710c896
|
156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
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
|
176 |
|
1c710c896
|
177 |
return do_utimes(dfd, filename, utimes ? tstimes : NULL, 0); |
82b0547cf
|
178 179 180 181 182 183 |
} asmlinkage long sys_utimes(char __user *filename, struct timeval __user *utimes) { return sys_futimesat(AT_FDCWD, filename, utimes); } |