From a506a578cae45116724a0d0fc8dc7882d70d2f3a Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 1 Jul 2019 13:26:28 -0700 Subject: [PATCH] f2fs: use generic checking and prep function for FS_IOC_SETFLAGS Make the f2fs implementation of FS_IOC_SETFLAGS use the new VFS helper function vfs_ioc_setflags_prepare(). This is based on a patch from Darrick Wong, but reworked to apply after commit 360985573b55 ("f2fs: separate f2fs i_flags from fs_flags and ext4 i_flags"). Originally-from: Darrick J. Wong Signed-off-by: Eric Biggers Reviewed-by: Chao Yu Reviewed-by: Darrick J. Wong Signed-off-by: Jaegeuk Kim --- fs/f2fs/file.c | 9 ++++++++- fs/inode.c | 24 ++++++++++++++++++++++++ include/linux/fs.h | 3 +++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 92c69264ba14..4d2665ec5be3 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1770,7 +1770,8 @@ static int f2fs_ioc_getflags(struct file *filp, unsigned long arg) static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) { struct inode *inode = file_inode(filp); - u32 fsflags; + struct f2fs_inode_info *fi = F2FS_I(inode); + u32 fsflags, old_fsflags; u32 iflags; int ret; @@ -1794,8 +1795,14 @@ static int f2fs_ioc_setflags(struct file *filp, unsigned long arg) inode_lock(inode); + old_fsflags = f2fs_iflags_to_fsflags(fi->i_flags); + ret = vfs_ioc_setflags_prepare(inode, old_fsflags, fsflags); + if (ret) + goto out; + ret = f2fs_setflags_common(inode, iflags, f2fs_fsflags_to_iflags(F2FS_SETTABLE_FS_FL)); +out: inode_unlock(inode); mnt_drop_write_file(filp); return ret; diff --git a/fs/inode.c b/fs/inode.c index d1e35b53bb23..beda394100ca 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -2125,3 +2125,27 @@ struct timespec current_time(struct inode *inode) return timespec_trunc(now, inode->i_sb->s_time_gran); } EXPORT_SYMBOL(current_time); + +/* + * Generic function to check FS_IOC_SETFLAGS values and reject any invalid + * configurations. + * + * Note: the caller should be holding i_mutex, or else be sure that they have + * exclusive access to the inode structure. + */ +int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags, + unsigned int flags) +{ + /* + * The IMMUTABLE and APPEND_ONLY flags can only be changed by + * the relevant capability. + * + * This test looks nicer. Thanks to Pauline Middelink + */ + if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) && + !capable(CAP_LINUX_IMMUTABLE)) + return -EPERM; + + return 0; +} +EXPORT_SYMBOL(vfs_ioc_setflags_prepare); diff --git a/include/linux/fs.h b/include/linux/fs.h index 142b0a799fc0..825a942d0e33 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3394,4 +3394,7 @@ static inline bool dir_relax_shared(struct inode *inode) extern bool path_noexec(const struct path *path); extern void inode_nohighmem(struct inode *inode); +int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags, + unsigned int flags); + #endif /* _LINUX_FS_H */