Subject: VFS: new fsetattr() file operation 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, ATTR_FILE and convert existing users: fuse and AFS. 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 | 20 ++++++++------------ fs/utimes.c | 2 +- include/linux/fs.h | 10 ++-------- 11 files changed, 63 insertions(+), 40 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 @@ -360,7 +360,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; @@ -382,8 +383,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)) { @@ -393,10 +394,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 @@ -1032,21 +1032,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; @@ -1065,7 +1066,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; @@ -1082,8 +1083,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); @@ -1121,7 +1122,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; @@ -1163,10 +1164,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 @@ -894,6 +894,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, @@ -908,6 +913,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, }; @@ -922,6 +928,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,12 @@ int do_truncate(struct dentry *dentry, s newattrs.ia_size = length; newattrs.ia_valid = ATTR_SIZE | time_attrs; - if (filp) { - newattrs.ia_file = 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; } @@ -583,7 +579,7 @@ asmlinkage long sys_fchmod(unsigned int 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); + err = fnotify_change(dentry, file->f_path.mnt, &newattrs, file); mutex_unlock(&inode->i_mutex); out_putf: @@ -633,7 +629,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; @@ -663,7 +659,7 @@ static int chown_common(struct dentry * newattrs.ia_valid |= ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_KILL_PRIV; 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; @@ -677,7 +673,7 @@ asmlinkage long sys_chown(const char __u error = user_path_walk(filename, &nd); if (error) goto out; - error = chown_common(nd.dentry, nd.mnt, user, group); + error = chown_common(nd.dentry, nd.mnt, user, group, NULL); path_release(&nd); out: return error; @@ -697,7 +693,7 @@ asmlinkage long sys_fchownat(int dfd, co error = __user_walk_fd(dfd, filename, follow, &nd); if (error) goto out; - error = chown_common(nd.dentry, nd.mnt, user, group); + error = chown_common(nd.dentry, nd.mnt, user, group, NULL); path_release(&nd); out: return error; @@ -711,7 +707,7 @@ asmlinkage long sys_lchown(const char __ error = user_path_walk_link(filename, &nd); if (error) goto out; - error = chown_common(nd.dentry, nd.mnt, user, group); + error = chown_common(nd.dentry, nd.mnt, user, group, NULL); path_release(&nd); out: return error; @@ -730,7 +726,7 @@ asmlinkage long sys_fchown(unsigned int 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); fput(file); out: return error; --- a/fs/utimes.c +++ b/fs/utimes.c @@ -132,7 +132,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); dput_and_out: if (f) --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -329,7 +329,6 @@ typedef void (dio_iodone_t)(struct kiocb #define ATTR_ATTR_FLAG 1024 #define ATTR_KILL_SUID 2048 #define ATTR_KILL_SGID 4096 -#define ATTR_FILE 8192 #define ATTR_KILL_PRIV 16384 #define ATTR_OPEN 32768 /* Truncating from open(O_TRUNC) */ @@ -351,13 +350,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; }; /* @@ -1189,6 +1181,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 { @@ -1695,6 +1688,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));