]>
Commit | Line | Data |
---|---|---|
8f69975d BS |
1 | Subject: VFS: new fsetattr() file operation |
2 | ||
3 | From: Miklos Szeredi <mszeredi@suse.cz> | |
4 | ||
5 | Add a new file operation: f_op->fsetattr(), that is invoked by | |
6 | ftruncate, fchmod, fchown and utimensat. Fall back to i_op->setattr() | |
7 | if it is not defined. | |
8 | ||
9 | For the reasons why we need this, see patch adding fgetattr(). | |
10 | ||
11 | ftruncate() already passed the open file to the filesystem via the | |
12 | ia_file member of struct iattr. However it is cleaner to have a | |
13 | separate file operation for this, so remove ia_file, ATTR_FILE and | |
14 | convert existing users: fuse and AFS. | |
15 | ||
16 | Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> --- | |
17 | Signed-off-by: John Johansen <jjohansen@suse.de> --- | |
18 | ||
19 | --- | |
20 | fs/afs/dir.c | 1 + | |
21 | fs/afs/file.c | 1 + | |
22 | fs/afs/inode.c | 19 +++++++++++++++---- | |
23 | fs/afs/internal.h | 1 + | |
24 | fs/attr.c | 19 +++++++++++++++---- | |
25 | fs/fuse/dir.c | 20 +++++++++----------- | |
26 | fs/fuse/file.c | 7 +++++++ | |
27 | fs/fuse/fuse_i.h | 4 ++++ | |
28 | fs/open.c | 20 ++++++++------------ | |
29 | fs/utimes.c | 9 +++++---- | |
30 | include/linux/fs.h | 9 ++------- | |
31 | 11 files changed, 68 insertions(+), 42 deletions(-) | |
32 | ||
33 | --- a/fs/afs/dir.c | |
34 | +++ b/fs/afs/dir.c | |
35 | @@ -45,6 +45,7 @@ const struct file_operations afs_dir_fil | |
36 | .release = afs_release, | |
37 | .readdir = afs_readdir, | |
38 | .lock = afs_lock, | |
39 | + .fsetattr = afs_fsetattr, | |
40 | }; | |
41 | ||
42 | const struct inode_operations afs_dir_inode_operations = { | |
43 | --- a/fs/afs/file.c | |
44 | +++ b/fs/afs/file.c | |
45 | @@ -36,6 +36,7 @@ const struct file_operations afs_file_op | |
46 | .fsync = afs_fsync, | |
47 | .lock = afs_lock, | |
48 | .flock = afs_flock, | |
49 | + .fsetattr = afs_fsetattr, | |
50 | }; | |
51 | ||
52 | const struct inode_operations afs_file_inode_operations = { | |
53 | --- a/fs/afs/inode.c | |
54 | +++ b/fs/afs/inode.c | |
55 | @@ -358,7 +358,8 @@ void afs_clear_inode(struct inode *inode | |
56 | /* | |
57 | * set the attributes of an inode | |
58 | */ | |
59 | -int afs_setattr(struct dentry *dentry, struct iattr *attr) | |
60 | +static int afs_do_setattr(struct dentry *dentry, struct iattr *attr, | |
61 | + struct file *file) | |
62 | { | |
63 | struct afs_vnode *vnode = AFS_FS_I(dentry->d_inode); | |
64 | struct key *key; | |
65 | @@ -380,8 +381,8 @@ int afs_setattr(struct dentry *dentry, s | |
66 | afs_writeback_all(vnode); | |
67 | } | |
68 | ||
69 | - if (attr->ia_valid & ATTR_FILE) { | |
70 | - key = attr->ia_file->private_data; | |
71 | + if (file) { | |
72 | + key = file->private_data; | |
73 | } else { | |
74 | key = afs_request_key(vnode->volume->cell); | |
75 | if (IS_ERR(key)) { | |
76 | @@ -391,10 +392,20 @@ int afs_setattr(struct dentry *dentry, s | |
77 | } | |
78 | ||
79 | ret = afs_vnode_setattr(vnode, key, attr); | |
80 | - if (!(attr->ia_valid & ATTR_FILE)) | |
81 | + if (!file) | |
82 | key_put(key); | |
83 | ||
84 | error: | |
85 | _leave(" = %d", ret); | |
86 | return ret; | |
87 | } | |
88 | + | |
89 | +int afs_setattr(struct dentry *dentry, struct iattr *attr) | |
90 | +{ | |
91 | + return afs_do_setattr(dentry, attr, NULL); | |
92 | +} | |
93 | + | |
94 | +int afs_fsetattr(struct file *file, struct iattr *attr) | |
95 | +{ | |
96 | + return afs_do_setattr(file->f_path.dentry, attr, file); | |
97 | +} | |
98 | --- a/fs/afs/internal.h | |
99 | +++ b/fs/afs/internal.h | |
100 | @@ -548,6 +548,7 @@ extern void afs_zap_data(struct afs_vnod | |
101 | extern int afs_validate(struct afs_vnode *, struct key *); | |
102 | extern int afs_getattr(struct vfsmount *, struct dentry *, struct kstat *); | |
103 | extern int afs_setattr(struct dentry *, struct iattr *); | |
104 | +extern int afs_fsetattr(struct file *, struct iattr *); | |
105 | extern void afs_clear_inode(struct inode *); | |
106 | ||
107 | /* | |
108 | --- a/fs/attr.c | |
109 | +++ b/fs/attr.c | |
110 | @@ -100,8 +100,8 @@ int inode_setattr(struct inode * inode, | |
111 | } | |
112 | EXPORT_SYMBOL(inode_setattr); | |
113 | ||
114 | -int notify_change(struct dentry *dentry, struct vfsmount *mnt, | |
115 | - struct iattr *attr) | |
116 | +int fnotify_change(struct dentry *dentry, struct vfsmount *mnt, | |
117 | + struct iattr *attr, struct file *file) | |
118 | { | |
119 | struct inode *inode = dentry->d_inode; | |
120 | mode_t mode = inode->i_mode; | |
121 | @@ -165,8 +165,12 @@ int notify_change(struct dentry *dentry, | |
122 | ||
123 | if (inode->i_op && inode->i_op->setattr) { | |
124 | error = security_inode_setattr(dentry, mnt, attr); | |
125 | - if (!error) | |
126 | - error = inode->i_op->setattr(dentry, attr); | |
127 | + if (!error) { | |
128 | + if (file && file->f_op && file->f_op->fsetattr) | |
129 | + error = file->f_op->fsetattr(file, attr); | |
130 | + else | |
131 | + error = inode->i_op->setattr(dentry, attr); | |
132 | + } | |
133 | } else { | |
134 | error = inode_change_ok(inode, attr); | |
135 | if (!error) | |
136 | @@ -188,5 +192,12 @@ int notify_change(struct dentry *dentry, | |
137 | ||
138 | return error; | |
139 | } | |
140 | +EXPORT_SYMBOL_GPL(fnotify_change); | |
141 | + | |
142 | +int notify_change(struct dentry *dentry, struct vfsmount *mnt, | |
143 | + struct iattr *attr) | |
144 | +{ | |
145 | + return fnotify_change(dentry, mnt, attr, NULL); | |
146 | +} | |
147 | ||
148 | EXPORT_SYMBOL(notify_change); | |
149 | --- a/fs/fuse/dir.c | |
150 | +++ b/fs/fuse/dir.c | |
151 | @@ -1105,21 +1105,22 @@ static int fuse_dir_fsync(struct file *f | |
152 | return file ? fuse_fsync_common(file, de, datasync, 1) : 0; | |
153 | } | |
154 | ||
155 | -static bool update_mtime(unsigned ivalid) | |
156 | +static bool update_mtime(unsigned ivalid, bool have_file) | |
157 | { | |
158 | /* Always update if mtime is explicitly set */ | |
159 | if (ivalid & ATTR_MTIME_SET) | |
160 | return true; | |
161 | ||
162 | /* If it's an open(O_TRUNC) or an ftruncate(), don't update */ | |
163 | - if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE))) | |
164 | + if ((ivalid & ATTR_SIZE) && ((ivalid & ATTR_OPEN) || have_file)) | |
165 | return false; | |
166 | ||
167 | /* In all other cases update */ | |
168 | return true; | |
169 | } | |
170 | ||
171 | -static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) | |
172 | +static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg, | |
173 | + bool have_file) | |
174 | { | |
175 | unsigned ivalid = iattr->ia_valid; | |
176 | ||
177 | @@ -1138,7 +1139,7 @@ static void iattr_to_fattr(struct iattr | |
178 | if (!(ivalid & ATTR_ATIME_SET)) | |
179 | arg->valid |= FATTR_ATIME_NOW; | |
180 | } | |
181 | - if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) { | |
182 | + if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, have_file)) { | |
183 | arg->valid |= FATTR_MTIME; | |
184 | arg->mtime = iattr->ia_mtime.tv_sec; | |
185 | arg->mtimensec = iattr->ia_mtime.tv_nsec; | |
186 | @@ -1199,8 +1200,8 @@ void fuse_release_nowrite(struct inode * | |
187 | * vmtruncate() doesn't allow for this case, so do the rlimit checking | |
188 | * and the actual truncation by hand. | |
189 | */ | |
190 | -static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, | |
191 | - struct file *file) | |
192 | +int fuse_do_setattr(struct dentry *entry, struct iattr *attr, | |
193 | + struct file *file) | |
194 | { | |
195 | struct inode *inode = entry->d_inode; | |
196 | struct fuse_conn *fc = get_fuse_conn(inode); | |
197 | @@ -1244,7 +1245,7 @@ static int fuse_do_setattr(struct dentry | |
198 | ||
199 | memset(&inarg, 0, sizeof(inarg)); | |
200 | memset(&outarg, 0, sizeof(outarg)); | |
201 | - iattr_to_fattr(attr, &inarg); | |
202 | + iattr_to_fattr(attr, &inarg, file != NULL); | |
203 | if (file) { | |
204 | struct fuse_file *ff = file->private_data; | |
205 | inarg.valid |= FATTR_FH; | |
206 | @@ -1314,10 +1315,7 @@ error: | |
207 | ||
208 | static int fuse_setattr(struct dentry *entry, struct iattr *attr) | |
209 | { | |
210 | - if (attr->ia_valid & ATTR_FILE) | |
211 | - return fuse_do_setattr(entry, attr, attr->ia_file); | |
212 | - else | |
213 | - return fuse_do_setattr(entry, attr, NULL); | |
214 | + return fuse_do_setattr(entry, attr, NULL); | |
215 | } | |
216 | ||
217 | static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, | |
218 | --- a/fs/fuse/file.c | |
219 | +++ b/fs/fuse/file.c | |
220 | @@ -1466,6 +1466,11 @@ static loff_t fuse_file_llseek(struct fi | |
221 | return retval; | |
222 | } | |
223 | ||
224 | +static int fuse_fsetattr(struct file *file, struct iattr *attr) | |
225 | +{ | |
226 | + return fuse_do_setattr(file->f_path.dentry, attr, file); | |
227 | +} | |
228 | + | |
229 | static const struct file_operations fuse_file_operations = { | |
230 | .llseek = fuse_file_llseek, | |
231 | .read = do_sync_read, | |
232 | @@ -1479,6 +1484,7 @@ static const struct file_operations fuse | |
233 | .fsync = fuse_fsync, | |
234 | .lock = fuse_file_lock, | |
235 | .flock = fuse_file_flock, | |
236 | + .fsetattr = fuse_fsetattr, | |
237 | .splice_read = generic_file_splice_read, | |
238 | }; | |
239 | ||
240 | @@ -1492,6 +1498,7 @@ static const struct file_operations fuse | |
241 | .fsync = fuse_fsync, | |
242 | .lock = fuse_file_lock, | |
243 | .flock = fuse_file_flock, | |
244 | + .fsetattr = fuse_fsetattr, | |
245 | /* no mmap and splice_read */ | |
246 | }; | |
247 | ||
248 | --- a/fs/fuse/fuse_i.h | |
249 | +++ b/fs/fuse/fuse_i.h | |
250 | @@ -551,6 +551,10 @@ void fuse_truncate(struct address_space | |
251 | */ | |
252 | int fuse_dev_init(void); | |
253 | ||
254 | + | |
255 | +int fuse_do_setattr(struct dentry *entry, struct iattr *attr, | |
256 | + struct file *file); | |
257 | + | |
258 | /** | |
259 | * Cleanup the client device | |
260 | */ | |
261 | --- a/fs/open.c | |
262 | +++ b/fs/open.c | |
263 | @@ -207,16 +207,12 @@ int do_truncate(struct dentry *dentry, s | |
264 | ||
265 | newattrs.ia_size = length; | |
266 | newattrs.ia_valid = ATTR_SIZE | time_attrs; | |
267 | - if (filp) { | |
268 | - newattrs.ia_file = filp; | |
269 | - newattrs.ia_valid |= ATTR_FILE; | |
270 | - } | |
271 | ||
272 | /* Remove suid/sgid on truncate too */ | |
273 | newattrs.ia_valid |= should_remove_suid(dentry); | |
274 | ||
275 | mutex_lock(&dentry->d_inode->i_mutex); | |
276 | - err = notify_change(dentry, mnt, &newattrs); | |
277 | + err = fnotify_change(dentry, mnt, &newattrs, filp); | |
278 | mutex_unlock(&dentry->d_inode->i_mutex); | |
279 | return err; | |
280 | } | |
281 | @@ -625,7 +621,7 @@ SYSCALL_DEFINE2(fchmod, unsigned int, fd | |
282 | mode = inode->i_mode; | |
283 | newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); | |
284 | newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; | |
285 | - err = notify_change(dentry, file->f_path.mnt, &newattrs); | |
286 | + err = fnotify_change(dentry, file->f_path.mnt, &newattrs, file); | |
287 | mutex_unlock(&inode->i_mutex); | |
288 | mnt_drop_write(file->f_path.mnt); | |
289 | out_putf: | |
290 | @@ -669,7 +665,7 @@ SYSCALL_DEFINE2(chmod, const char __user | |
291 | } | |
292 | ||
293 | static int chown_common(struct dentry * dentry, struct vfsmount *mnt, | |
294 | - uid_t user, gid_t group) | |
295 | + uid_t user, gid_t group, struct file *file) | |
296 | { | |
297 | struct inode *inode = dentry->d_inode; | |
298 | int error; | |
299 | @@ -688,7 +684,7 @@ static int chown_common(struct dentry * | |
300 | newattrs.ia_valid |= | |
301 | ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; | |
302 | mutex_lock(&inode->i_mutex); | |
303 | - error = notify_change(dentry, mnt, &newattrs); | |
304 | + error = fnotify_change(dentry, mnt, &newattrs, file); | |
305 | mutex_unlock(&inode->i_mutex); | |
306 | ||
307 | return error; | |
308 | @@ -705,7 +701,7 @@ SYSCALL_DEFINE3(chown, const char __user | |
309 | error = mnt_want_write(path.mnt); | |
310 | if (error) | |
311 | goto out_release; | |
312 | - error = chown_common(path.dentry, path.mnt, user, group); | |
313 | + error = chown_common(path.dentry, path.mnt, user, group, NULL); | |
314 | mnt_drop_write(path.mnt); | |
315 | out_release: | |
316 | path_put(&path); | |
317 | @@ -730,7 +726,7 @@ SYSCALL_DEFINE5(fchownat, int, dfd, cons | |
318 | error = mnt_want_write(path.mnt); | |
319 | if (error) | |
320 | goto out_release; | |
321 | - error = chown_common(path.dentry, path.mnt, user, group); | |
322 | + error = chown_common(path.dentry, path.mnt, user, group, NULL); | |
323 | mnt_drop_write(path.mnt); | |
324 | out_release: | |
325 | path_put(&path); | |
326 | @@ -749,7 +745,7 @@ SYSCALL_DEFINE3(lchown, const char __use | |
327 | error = mnt_want_write(path.mnt); | |
328 | if (error) | |
329 | goto out_release; | |
330 | - error = chown_common(path.dentry, path.mnt, user, group); | |
331 | + error = chown_common(path.dentry, path.mnt, user, group, NULL); | |
332 | mnt_drop_write(path.mnt); | |
333 | out_release: | |
334 | path_put(&path); | |
335 | @@ -772,7 +768,7 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd | |
336 | goto out_fput; | |
337 | dentry = file->f_path.dentry; | |
338 | audit_inode(NULL, dentry); | |
339 | - error = chown_common(dentry, file->f_path.mnt, user, group); | |
340 | + error = chown_common(dentry, file->f_path.mnt, user, group, file); | |
341 | mnt_drop_write(file->f_path.mnt); | |
342 | out_fput: | |
343 | fput(file); | |
344 | --- a/fs/utimes.c | |
345 | +++ b/fs/utimes.c | |
346 | @@ -48,7 +48,8 @@ static bool nsec_valid(long nsec) | |
347 | return nsec >= 0 && nsec <= 999999999; | |
348 | } | |
349 | ||
350 | -static int utimes_common(struct path *path, struct timespec *times) | |
351 | +static int utimes_common(struct path *path, struct timespec *times, | |
352 | + struct file *f) | |
353 | { | |
354 | int error; | |
355 | struct iattr newattrs; | |
356 | @@ -102,7 +103,7 @@ static int utimes_common(struct path *pa | |
357 | } | |
358 | } | |
359 | mutex_lock(&inode->i_mutex); | |
360 | - error = notify_change(path->dentry, path->mnt, &newattrs); | |
361 | + error = fnotify_change(path->dentry, path->mnt, &newattrs, f); | |
362 | mutex_unlock(&inode->i_mutex); | |
363 | ||
364 | mnt_drop_write_and_out: | |
365 | @@ -149,7 +150,7 @@ long do_utimes(int dfd, char __user *fil | |
366 | if (!file) | |
367 | goto out; | |
368 | ||
369 | - error = utimes_common(&file->f_path, times); | |
370 | + error = utimes_common(&file->f_path, times, file); | |
371 | fput(file); | |
372 | } else { | |
373 | struct path path; | |
374 | @@ -162,7 +163,7 @@ long do_utimes(int dfd, char __user *fil | |
375 | if (error) | |
376 | goto out; | |
377 | ||
378 | - error = utimes_common(&path, times); | |
379 | + error = utimes_common(&path, times, NULL); | |
380 | path_put(&path); | |
381 | } | |
382 | ||
383 | --- a/include/linux/fs.h | |
384 | +++ b/include/linux/fs.h | |
385 | @@ -367,13 +367,6 @@ struct iattr { | |
386 | struct timespec ia_atime; | |
387 | struct timespec ia_mtime; | |
388 | struct timespec ia_ctime; | |
389 | - | |
390 | - /* | |
391 | - * Not an attribute, but an auxilary info for filesystems wanting to | |
392 | - * implement an ftruncate() like method. NOTE: filesystem should | |
393 | - * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL). | |
394 | - */ | |
395 | - struct file *ia_file; | |
396 | }; | |
397 | ||
398 | /* | |
399 | @@ -1280,6 +1273,7 @@ struct file_operations { | |
400 | #define HAVE_FOP_OPEN_EXEC | |
401 | int (*open_exec) (struct inode *); | |
402 | int (*setlease)(struct file *, long, struct file_lock **); | |
403 | + int (*fsetattr)(struct file *, struct iattr *); | |
404 | }; | |
405 | ||
406 | struct inode_operations { | |
407 | @@ -1799,6 +1793,7 @@ extern int do_remount_sb(struct super_bl | |
408 | extern sector_t bmap(struct inode *, sector_t); | |
409 | #endif | |
410 | extern int notify_change(struct dentry *, struct vfsmount *, struct iattr *); | |
411 | +extern int fnotify_change(struct dentry *, struct vfsmount *, struct iattr *, struct file *); | |
412 | extern int inode_permission(struct inode *, int); | |
413 | extern int generic_permission(struct inode *, int, | |
414 | int (*check_acl)(struct inode *, int)); |