From: Miklos Szeredi Add a new file operation: f_op->fsetattr(), that is invoked by ftruncate, fchmod, fchown and utimensat. Fall back to i_op->setattr() if it is not defined. For the reasons why we need this, see patch adding fgetattr(). ftruncate() already passed the open file to the filesystem via the ia_file member of struct iattr. However it is cleaner to have a separate file operation for this, so remove ia_file and convert existing users: fuse, AFS, and reiserfs4. Use ATTR_FILE to allow LSMs to distinguish file descriptor based operations. Signed-off-by: Miklos Szeredi Signed-off-by: John Johansen --- fs/afs/dir.c | 1 + fs/afs/file.c | 1 + fs/afs/inode.c | 19 +++++++++++++++---- fs/afs/internal.h | 1 + fs/attr.c | 18 ++++++++++++++---- fs/fuse/dir.c | 20 +++++++++----------- fs/fuse/file.c | 7 +++++++ fs/fuse/fuse_i.h | 4 ++++ fs/open.c | 25 +++++++++++++------------ fs/reiser4/plugin/file/cryptcompress.c | 12 +++++++++++- fs/reiser4/plugin/file/file.c | 27 ++++++++++++++++++++------- fs/reiser4/plugin/file/file.h | 3 +++ fs/reiser4/plugin/file/file_conversion.c | 7 +++++++ fs/reiser4/plugin/object.c | 5 ++++- fs/reiser4/plugin/plugin.h | 1 + fs/utimes.c | 2 +- include/linux/fs.h | 9 ++------- 17 files changed, 114 insertions(+), 48 deletions(-) --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -45,6 +45,7 @@ const struct file_operations afs_dir_fil .release = afs_release, .readdir = afs_readdir, .lock = afs_lock, + .fsetattr = afs_fsetattr, }; const struct inode_operations afs_dir_inode_operations = { --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -36,6 +36,7 @@ const struct file_operations afs_file_op .fsync = afs_fsync, .lock = afs_lock, .flock = afs_flock, + .fsetattr = afs_fsetattr, }; const struct inode_operations afs_file_inode_operations = { --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -358,7 +358,8 @@ void afs_clear_inode(struct inode *inode /* * set the attributes of an inode */ -int afs_setattr(struct dentry *dentry, struct iattr *attr) +static int afs_do_setattr(struct dentry *dentry, struct iattr *attr, + struct file *file) { struct afs_vnode *vnode = AFS_FS_I(dentry->d_inode); struct key *key; @@ -380,8 +381,8 @@ int afs_setattr(struct dentry *dentry, s afs_writeback_all(vnode); } - if (attr->ia_valid & ATTR_FILE) { - key = attr->ia_file->private_data; + if (file) { + key = file->private_data; } else { key = afs_request_key(vnode->volume->cell); if (IS_ERR(key)) { @@ -391,10 +392,20 @@ int afs_setattr(struct dentry *dentry, s } ret = afs_vnode_setattr(vnode, key, attr); - if (!(attr->ia_valid & ATTR_FILE)) + if (!file) key_put(key); error: _leave(" = %d", ret); return ret; } + +int afs_setattr(struct dentry *dentry, struct iattr *attr) +{ + return afs_do_setattr(dentry, attr, NULL); +} + +int afs_fsetattr(struct file *file, struct iattr *attr) +{ + return afs_do_setattr(file->f_path.dentry, attr, file); +} --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -550,6 +550,7 @@ extern void afs_zap_data(struct afs_vnod extern int afs_validate(struct afs_vnode *, struct key *); extern int afs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int afs_setattr(struct dentry *, struct iattr *); +extern int afs_fsetattr(struct file *, struct iattr *); extern void afs_clear_inode(struct inode *); /* --- a/fs/attr.c +++ b/fs/attr.c @@ -100,8 +100,8 @@ int inode_setattr(struct inode * inode, } EXPORT_SYMBOL(inode_setattr); -int notify_change(struct dentry *dentry, struct vfsmount *mnt, - struct iattr *attr) +int fnotify_change(struct dentry *dentry, struct vfsmount *mnt, + struct iattr *attr, struct file *file) { struct inode *inode = dentry->d_inode; mode_t mode = inode->i_mode; @@ -160,8 +160,12 @@ int notify_change(struct dentry *dentry, if (inode->i_op && inode->i_op->setattr) { error = security_inode_setattr(dentry, mnt, attr); - if (!error) - error = inode->i_op->setattr(dentry, attr); + if (!error) { + if (file && file->f_op && file->f_op->fsetattr) + error = file->f_op->fsetattr(file, attr); + else + error = inode->i_op->setattr(dentry, attr); + } } else { error = inode_change_ok(inode, attr); if (!error) @@ -184,4 +188,10 @@ int notify_change(struct dentry *dentry, return error; } +int notify_change(struct dentry *dentry, struct vfsmount *mnt, + struct iattr *attr) +{ + return fnotify_change(dentry, mnt, attr, NULL); +} + EXPORT_SYMBOL(notify_change); --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1063,21 +1063,22 @@ static int fuse_dir_fsync(struct file *f return file ? fuse_fsync_common(file, de, datasync, 1) : 0; } -static bool update_mtime(unsigned ivalid) +static bool update_mtime(unsigned ivalid, bool have_file) { /* Always update if mtime is explicitly set */ if (ivalid & ATTR_MTIME_SET) return true; /* If it's an open(O_TRUNC) or an ftruncate(), don't update */ - if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE))) + if ((ivalid & ATTR_SIZE) && ((ivalid & ATTR_OPEN) || have_file)) return false; /* In all other cases update */ return true; } -static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) +static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg, + bool have_file) { unsigned ivalid = iattr->ia_valid; @@ -1096,7 +1097,7 @@ static void iattr_to_fattr(struct iattr if (!(ivalid & ATTR_ATIME_SET)) arg->valid |= FATTR_ATIME_NOW; } - if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) { + if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, have_file)) { arg->valid |= FATTR_MTIME; arg->mtime = iattr->ia_mtime.tv_sec; arg->mtimensec = iattr->ia_mtime.tv_nsec; @@ -1113,8 +1114,8 @@ static void iattr_to_fattr(struct iattr * vmtruncate() doesn't allow for this case, so do the rlimit checking * and the actual truncation by hand. */ -static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, - struct file *file) +int fuse_do_setattr(struct dentry *entry, struct iattr *attr, + struct file *file) { struct inode *inode = entry->d_inode; struct fuse_conn *fc = get_fuse_conn(inode); @@ -1152,7 +1153,7 @@ static int fuse_do_setattr(struct dentry memset(&inarg, 0, sizeof(inarg)); memset(&outarg, 0, sizeof(outarg)); - iattr_to_fattr(attr, &inarg); + iattr_to_fattr(attr, &inarg, file != NULL); if (file) { struct fuse_file *ff = file->private_data; inarg.valid |= FATTR_FH; @@ -1194,10 +1195,7 @@ static int fuse_do_setattr(struct dentry static int fuse_setattr(struct dentry *entry, struct iattr *attr) { - if (attr->ia_valid & ATTR_FILE) - return fuse_do_setattr(entry, attr, attr->ia_file); - else - return fuse_do_setattr(entry, attr, NULL); + return fuse_do_setattr(entry, attr, NULL); } static int fuse_getattr(struct vfsmount *mnt, struct dentry *entry, --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -918,6 +918,11 @@ static sector_t fuse_bmap(struct address return err ? 0 : outarg.block; } +static int fuse_fsetattr(struct file *file, struct iattr *attr) +{ + return fuse_do_setattr(file->f_path.dentry, attr, file); +} + static const struct file_operations fuse_file_operations = { .llseek = generic_file_llseek, .read = do_sync_read, @@ -932,6 +937,7 @@ static const struct file_operations fuse .lock = fuse_file_lock, .flock = fuse_file_flock, .fgetattr = fuse_file_fgetattr, + .fsetattr = fuse_fsetattr, .splice_read = generic_file_splice_read, }; @@ -946,6 +952,7 @@ static const struct file_operations fuse .lock = fuse_file_lock, .flock = fuse_file_flock, .fgetattr = fuse_file_fgetattr, + .fsetattr = fuse_fsetattr, /* no mmap and splice_read */ }; --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -505,6 +505,10 @@ void fuse_change_attributes(struct inode */ int fuse_dev_init(void); + +int fuse_do_setattr(struct dentry *entry, struct iattr *attr, + struct file *file); + /** * Cleanup the client device */ --- a/fs/open.c +++ b/fs/open.c @@ -206,16 +206,15 @@ int do_truncate(struct dentry *dentry, s newattrs.ia_size = length; newattrs.ia_valid = ATTR_SIZE | time_attrs; - if (filp) { - newattrs.ia_file = filp; + + if (filp) newattrs.ia_valid |= ATTR_FILE; - } /* Remove suid/sgid on truncate too */ newattrs.ia_valid |= should_remove_suid(dentry); mutex_lock(&dentry->d_inode->i_mutex); - err = notify_change(dentry, mnt, &newattrs); + err = fnotify_change(dentry, mnt, &newattrs, filp); mutex_unlock(&dentry->d_inode->i_mutex); return err; } @@ -590,8 +589,8 @@ asmlinkage long sys_fchmod(unsigned int if (mode == (mode_t) -1) mode = inode->i_mode; newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); - newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - err = notify_change(dentry, file->f_path.mnt, &newattrs); + newattrs.ia_valid = ATTR_MODE | ATTR_CTIME | ATTR_FILE; + err = fnotify_change(dentry, file->f_path.mnt, &newattrs, file); mutex_unlock(&inode->i_mutex); out_drop_write: @@ -645,7 +644,7 @@ asmlinkage long sys_chmod(const char __u } static int chown_common(struct dentry * dentry, struct vfsmount *mnt, - uid_t user, gid_t group) + uid_t user, gid_t group, struct file *file) { struct inode * inode; int error; @@ -671,8 +670,10 @@ static int chown_common(struct dentry * if (!S_ISDIR(inode->i_mode)) newattrs.ia_valid |= ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; + if (file) + newattrs.ia_valid |= ATTR_FILE; mutex_lock(&inode->i_mutex); - error = notify_change(dentry, mnt, &newattrs); + error = fnotify_change(dentry, mnt, &newattrs, file); mutex_unlock(&inode->i_mutex); out: return error; @@ -689,7 +690,7 @@ asmlinkage long sys_chown(const char __u error = mnt_want_write(nd.path.mnt); if (error) goto out_release; - error = chown_common(nd.path.dentry, nd.path.mnt, user, group); + error = chown_common(nd.path.dentry, nd.path.mnt, user, group, NULL); mnt_drop_write(nd.path.mnt); out_release: path_put(&nd.path); @@ -714,7 +715,7 @@ asmlinkage long sys_fchownat(int dfd, co error = mnt_want_write(nd.path.mnt); if (error) goto out_release; - error = chown_common(nd.path.dentry, nd.path.mnt, user, group); + error = chown_common(nd.path.dentry, nd.path.mnt, user, group, NULL); mnt_drop_write(nd.path.mnt); out_release: path_put(&nd.path); @@ -733,7 +734,7 @@ asmlinkage long sys_lchown(const char __ error = mnt_want_write(nd.path.mnt); if (error) goto out_release; - error = chown_common(nd.path.dentry, nd.path.mnt, user, group); + error = chown_common(nd.path.dentry, nd.path.mnt, user, group, NULL); mnt_drop_write(nd.path.mnt); out_release: path_put(&nd.path); @@ -757,7 +758,7 @@ asmlinkage long sys_fchown(unsigned int goto out_fput; dentry = file->f_path.dentry; audit_inode(NULL, dentry); - error = chown_common(dentry, file->f_path.mnt, user, group); + error = chown_common(dentry, file->f_path.mnt, user, group, file); mnt_drop_write(file->f_vfsmnt); out_fput: fput(file); --- a/fs/reiser4/plugin/file/cryptcompress.c +++ b/fs/reiser4/plugin/file/cryptcompress.c @@ -3688,7 +3688,7 @@ int delete_object_cryptcompress(struct i * plugin->setattr * This implements actual truncate (see comments in reiser4/page_cache.c) */ -int setattr_cryptcompress(struct dentry *dentry, struct iattr *attr) +static int do_setattr_cryptcompress(struct dentry *dentry, struct iattr *attr) { int result; struct inode *inode; @@ -3731,6 +3731,16 @@ int setattr_cryptcompress(struct dentry return result; } +int setattr_cryptcompress(struct dentry *dentry, struct iattr *attr) +{ + return do_setattr_cryptcompress(dentry, attr); +} + +int fsetattr_cryptcompress(struct file *file, struct iattr *attr) +{ + return do_setattr_cryptcompress(file->f_path.dentry, attr); +} + /* plugin->release */ int release_cryptcompress(struct inode *inode, struct file *file) { --- a/fs/reiser4/plugin/file/file.c +++ b/fs/reiser4/plugin/file/file.c @@ -561,19 +561,20 @@ static int should_have_notail(const stru * truncate_file_body - change length of file * @inode: inode of file * @new_size: new file length + * @file: open file being truncated through foperation * * Adjusts items file @inode is built of to match @new_size. It may either cut * items or add them to represent a hole at the end of file. The caller has to * obtain exclusive access to the file. */ -static int truncate_file_body(struct inode *inode, struct iattr *attr) +static int truncate_file_body(struct inode *inode, struct iattr *attr, + struct file *file) { int result; loff_t new_size = attr->ia_size; if (inode->i_size < new_size) { /* expanding truncate */ - struct file * file = attr->ia_file; struct unix_file_info *uf_info = unix_file_inode_data(inode); assert("edward-1532", attr->ia_valid & ATTR_FILE); @@ -2581,7 +2582,8 @@ owns_item_unix_file(const struct inode * return 1; } -static int setattr_truncate(struct inode *inode, struct iattr *attr) +static int setattr_truncate(struct inode *inode, struct iattr *attr, + struct file *file) { int result; int s_result; @@ -2597,7 +2599,7 @@ static int setattr_truncate(struct inode if (result == 0) result = safe_link_add(inode, SAFE_TRUNCATE); if (result == 0) - result = truncate_file_body(inode, attr); + result = truncate_file_body(inode, attr, file); if (result) warning("vs-1588", "truncate_file failed: oid %lli, " "old size %lld, new size %lld, retval %d", @@ -2619,8 +2621,9 @@ static int setattr_truncate(struct inode /* plugin->u.file.setattr method */ /* This calls inode_setattr and if truncate is in effect it also takes exclusive inode access to avoid races */ -int setattr_unix_file(struct dentry *dentry, /* Object to change attributes */ - struct iattr *attr /* change description */ ) +static int do_setattr_unix_file(struct dentry *dentry, /* Object to change attributes */ + struct iattr *attr, /* change description */ + struct file *file) { int result; @@ -2636,7 +2639,7 @@ int setattr_unix_file(struct dentry *den uf_info = unix_file_inode_data(dentry->d_inode); get_exclusive_access_careful(uf_info, dentry->d_inode); - result = setattr_truncate(dentry->d_inode, attr); + result = setattr_truncate(dentry->d_inode, attr, file); drop_exclusive_access(uf_info); context_set_commit_async(ctx); reiser4_exit_context(ctx); @@ -2646,6 +2649,16 @@ int setattr_unix_file(struct dentry *den return result; } +int setattr_unix_file(struct dentry *dentry, struct iattr *attr) +{ + return do_setattr_unix_file(dentry, attr, NULL); +} + +int fsetattr_unix_file(struct file *file, struct iattr *attr) +{ + return do_setattr_unix_file(file->f_path.dentry, attr, file); +} + /* plugin->u.file.init_inode_data */ void init_inode_data_unix_file(struct inode *inode, --- a/fs/reiser4/plugin/file/file.h +++ b/fs/reiser4/plugin/file/file.h @@ -39,6 +39,7 @@ int reiser4_mmap_careful(struct file *, int reiser4_open_careful(struct inode *inode, struct file *file); int reiser4_release_careful(struct inode *, struct file *); int reiser4_sync_file_common(struct file *, struct dentry *, int datasync); +int reiser4_fsetattr_careful(struct file *file, struct iattr *attr); /* address space operations */ int reiser4_readpage(struct file *, struct page *); @@ -70,6 +71,7 @@ int ioctl_unix_file(struct inode *, stru int mmap_unix_file(struct file *, struct vm_area_struct *); int open_unix_file(struct inode *, struct file *); int release_unix_file(struct inode *, struct file *); +int fsetattr_unix_file(struct file *, struct iattr *); /* private address space operations */ int readpage_unix_file(struct file *, struct page *); @@ -107,6 +109,7 @@ int ioctl_cryptcompress(struct inode *, int mmap_cryptcompress(struct file *, struct vm_area_struct *); int open_cryptcompress(struct inode *, struct file *); int release_cryptcompress(struct inode *, struct file *); +int fsetattr_cryptcompress(struct file *file, struct iattr *attr); /* private address space operations */ int readpage_cryptcompress(struct file *, struct page *); --- a/fs/reiser4/plugin/file/file_conversion.c +++ b/fs/reiser4/plugin/file/file_conversion.c @@ -602,6 +602,13 @@ int reiser4_setattr_careful(struct dentr setattr_conversion_hook(inode, attr)); } +int reiser4_fsetattr_careful(struct file *file, struct iattr *attr) +{ + struct inode * inode = file->f_path.dentry->d_inode; + return PROT_ACTIVE(int, fsetattr, (file, attr), + setattr_conversion_hook(inode, attr)); +} + /* Wrappers with passive protection for: * * ->open(); --- a/fs/reiser4/plugin/object.c +++ b/fs/reiser4/plugin/object.c @@ -105,7 +105,8 @@ static struct file_operations regular_fi .release = reiser4_release_careful, .fsync = reiser4_sync_file_common, .splice_read = generic_file_splice_read, - .splice_write = generic_file_splice_write + .splice_write = generic_file_splice_write, + .fsetattr = reiser4_fsetattr_careful }; static struct address_space_operations regular_file_a_ops = { .writepage = reiser4_writepage, @@ -211,6 +212,7 @@ file_plugin file_plugins[LAST_FILE_PLUGI .writepages = writepages_unix_file, .prepare_write = prepare_write_unix_file, .commit_write = commit_write_unix_file, + .fsetattr = fsetattr_unix_file, /* * private a_ops */ @@ -405,6 +407,7 @@ file_plugin file_plugins[LAST_FILE_PLUGI .writepages = writepages_cryptcompress, .prepare_write = prepare_write_cryptcompress, .commit_write = commit_write_cryptcompress, + .fsetattr = fsetattr_cryptcompress, .bmap = bmap_cryptcompress, --- a/fs/reiser4/plugin/plugin.h +++ b/fs/reiser4/plugin/plugin.h @@ -237,6 +237,7 @@ typedef struct file_plugin { unsigned int cmd, unsigned long arg); int (*mmap) (struct file *, struct vm_area_struct *); int (*release) (struct inode *, struct file *); + int (*fsetattr)(struct file *, struct iattr *); /* * private a_ops */ --- a/fs/utimes.c +++ b/fs/utimes.c @@ -135,7 +135,7 @@ long do_utimes(int dfd, char __user *fil } } mutex_lock(&inode->i_mutex); - error = notify_change(path.dentry, path.mnt, &newattrs); + error = fnotify_change(path.dentry, path.mnt, &newattrs, f); mutex_unlock(&inode->i_mutex); mnt_drop_write_and_out: mnt_drop_write(path.mnt); --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -351,13 +351,6 @@ struct iattr { struct timespec ia_atime; struct timespec ia_mtime; struct timespec ia_ctime; - - /* - * Not an attribute, but an auxilary info for filesystems wanting to - * implement an ftruncate() like method. NOTE: filesystem should - * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL). - */ - struct file *ia_file; }; /* @@ -1191,6 +1184,7 @@ struct file_operations { ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **); int (*fgetattr)(struct file *, struct kstat *); + int (*fsetattr)(struct file *, struct iattr *); }; struct inode_operations { @@ -1697,6 +1691,7 @@ extern int do_remount_sb(struct super_bl extern sector_t bmap(struct inode *, sector_t); #endif extern int notify_change(struct dentry *, struct vfsmount *, struct iattr *); +extern int fnotify_change(struct dentry *, struct vfsmount *, struct iattr *, struct file *); extern int permission(struct inode *, int, struct nameidata *); extern int generic_permission(struct inode *, int, int (*check_acl)(struct inode *, int)); -- -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/