* [Ocfs2-devel] [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-11 4:45 ` Darrick J. Wong
0 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-11 4:45 UTC (permalink / raw)
To: matthew.garrett, yuchao0, tytso, darrick.wong, shaggy,
ard.biesheuvel, josef, clm, adilger.kernel, jk, jack, dsterba,
jaegeuk, viro
Cc: cluster-devel, jfs-discussion, linux-efi, reiserfs-devel,
linux-kernel, linux-f2fs-devel, linux-xfs, linux-nilfs, linux-mtd,
ocfs2-devel, linux-fsdevel, linux-ext4, linux-btrfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
values so that we can standardize the implementations that follow ext4's
flag values.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/btrfs/ioctl.c | 13 +++++--------
fs/efivarfs/file.c | 18 +++++++++++++-----
fs/ext2/ioctl.c | 16 ++++------------
fs/ext4/ioctl.c | 13 +++----------
fs/f2fs/file.c | 7 ++++---
fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
fs/hfsplus/ioctl.c | 21 ++++++++++++---------
fs/inode.c | 17 +++++++++++++++++
fs/jfs/ioctl.c | 6 ++++++
fs/nilfs2/ioctl.c | 9 ++-------
fs/ocfs2/ioctl.c | 13 +++----------
fs/reiserfs/ioctl.c | 10 ++++------
fs/ubifs/ioctl.c | 13 +++----------
include/linux/fs.h | 2 ++
14 files changed, 107 insertions(+), 93 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6dafa857bbb9..f408aa93b0cf 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(inode);
struct btrfs_root *root = binode->root;
struct btrfs_trans_handle *trans;
- unsigned int fsflags;
+ unsigned int fsflags, old_fsflags;
int ret;
const char *comp = NULL;
u32 binode_flags = binode->flags;
@@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
inode_lock(inode);
fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
- if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
- (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- ret = -EPERM;
- goto out_unlock;
- }
- }
+ old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
+ ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
+ if (ret)
+ goto out_unlock;
if (fsflags & FS_SYNC_FL)
binode_flags |= BTRFS_INODE_SYNC;
diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
index 8e568428c88b..f4f6c1bec132 100644
--- a/fs/efivarfs/file.c
+++ b/fs/efivarfs/file.c
@@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
return size;
}
-static int
-efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+static inline unsigned int efivarfs_getflags(struct inode *inode)
{
- struct inode *inode = file->f_mapping->host;
unsigned int i_flags;
unsigned int flags = 0;
i_flags = inode->i_flags;
if (i_flags & S_IMMUTABLE)
flags |= FS_IMMUTABLE_FL;
+ return flags;
+}
+
+static int
+efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+{
+ struct inode *inode = file->f_mapping->host;
+ unsigned int flags = efivarfs_getflags(inode);
if (copy_to_user(arg, &flags, sizeof(flags)))
return -EFAULT;
@@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
struct inode *inode = file->f_mapping->host;
unsigned int flags;
unsigned int i_flags = 0;
+ unsigned int oldflags = efivarfs_getflags(inode);
int error;
if (!inode_owner_or_capable(inode))
@@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
if (flags & ~FS_IMMUTABLE_FL)
return -EOPNOTSUPP;
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ error = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (error)
+ return error;
if (flags & FS_IMMUTABLE_FL)
i_flags |= S_IMMUTABLE;
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 0367c0039e68..88b3b9720023 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- ret = -EPERM;
- goto setflags_out;
- }
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & EXT2_FL_USER_MODIFIABLE;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index e486e49b31ed..5126ee351a84 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
/* The JOURNAL_DATA flag is modifiable only by root */
jflag = flags & EXT4_JOURNAL_DATA_FL;
- /*
- * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto flags_out;
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto flags_out;
/*
* The JOURNAL_DATA flag can only be changed by
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 45b45f37d347..a969d5497e03 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
unsigned int oldflags;
+ int err;
/* Is it quota file? Do not allow user to mess with it */
if (IS_NOQUOTA(inode))
@@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
oldflags = fi->i_flags;
- if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ return err;
flags = flags & F2FS_FL_USER_MODIFIABLE;
flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index d174b1f8fd08..99f53cf699c6 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -136,27 +136,36 @@ static struct {
{FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
};
+static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
+{
+ int i;
+ u32 fsflags = 0;
+
+ if (S_ISDIR(inode->i_mode))
+ gfsflags &= ~GFS2_DIF_JDATA;
+ else
+ gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
+
+ for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
+ if (gfsflags & fsflag_gfs2flag[i].gfsflag)
+ fsflags |= fsflag_gfs2flag[i].fsflag;
+ return fsflags;
+}
+
static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
- int i, error;
- u32 gfsflags, fsflags = 0;
+ int error;
+ u32 fsflags;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
error = gfs2_glock_nq(&gh);
if (error)
goto out_uninit;
- gfsflags = ip->i_diskflags;
- if (S_ISDIR(inode->i_mode))
- gfsflags &= ~GFS2_DIF_JDATA;
- else
- gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
- for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
- if (gfsflags & fsflag_gfs2flag[i].gfsflag)
- fsflags |= fsflag_gfs2flag[i].fsflag;
+ fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
if (put_user(fsflags, ptr))
error = -EFAULT;
@@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
* @filp: file pointer
* @reqflags: The flags to set
* @mask: Indicates which flags are valid
+ * @fsflags: The FS_* inode flags passed in
*
*/
-static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
+static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
+ const u32 fsflags)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
@@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
struct buffer_head *bh;
struct gfs2_holder gh;
int error;
- u32 new_flags, flags;
+ u32 new_flags, flags, oldflags;
error = mnt_want_write_file(filp);
if (error)
@@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
if (error)
goto out_drop_write;
+ oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
+ error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
+ if (error)
+ goto out;
+
error = -EACCES;
if (!inode_owner_or_capable(inode))
goto out;
@@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
}
- return do_gfs2_set_flags(filp, gfsflags, mask);
+ return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
}
static int gfs2_getlabel(struct file *filp, char __user *label)
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
index 5e6502ef7415..862a3c9481d7 100644
--- a/fs/hfsplus/ioctl.c
+++ b/fs/hfsplus/ioctl.c
@@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
return 0;
}
-static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+static inline unsigned int hfsplus_getflags(struct inode *inode)
{
- struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags = 0;
@@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
flags |= FS_APPEND_FL;
if (hip->userflags & HFSPLUS_FLG_NODUMP)
flags |= FS_NODUMP_FL;
+ return flags;
+}
+
+static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+{
+ struct inode *inode = file_inode(file);
+ unsigned int flags = hfsplus_getflags(inode);
return put_user(flags, user_flags);
}
@@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags, new_fl = 0;
+ unsigned int oldflags = hfsplus_getflags(inode);
int err = 0;
err = mnt_want_write_file(file);
@@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
inode_lock(inode);
- if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
- inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock_inode;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock_inode;
/* don't silently ignore unsupported ext2 flags */
if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
diff --git a/fs/inode.c b/fs/inode.c
index df6542ec3b88..0ce60b720608 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
return timespec64_trunc(now, inode->i_sb->s_time_gran);
}
EXPORT_SYMBOL(current_time);
+
+/* Generic function to check FS_IOC_SETFLAGS values. */
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
index ba34dae8bd9f..c8446d2cd0c7 100644
--- a/fs/jfs/ioctl.c
+++ b/fs/jfs/ioctl.c
@@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
/* Lock against other parallel changes of flags */
inode_lock(inode);
+ oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
+ 0);
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto setflags_out;
+
oldflags = jfs_inode->mode2;
/*
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 9b96d79eea6c..0632336d2515 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
oldflags = NILFS_I(inode)->i_flags;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
- * relevant capability.
- */
- ret = -EPERM;
- if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
- !capable(CAP_LINUX_IMMUTABLE))
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret)
goto out;
ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 994726ada857..467a2faf0305 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
flags = flags & mask;
flags |= oldflags & ~mask;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- status = -EPERM;
- if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
- (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto bail_unlock;
- }
+ status = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (status)
+ goto bail_unlock;
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
if (IS_ERR(handle)) {
diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
index acbbaf7a0bb2..92bcb1ecd994 100644
--- a/fs/reiserfs/ioctl.c
+++ b/fs/reiserfs/ioctl.c
@@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
err = -EPERM;
goto setflags_out;
}
- if (((flags ^ REISERFS_I(inode)->
- i_attrs) & (REISERFS_IMMUTABLE_FL |
- REISERFS_APPEND_FL))
- && !capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
+ err = vfs_ioc_setflags_check(inode,
+ REISERFS_I(inode)->i_attrs,
+ flags);
+ if (err)
goto setflags_out;
- }
if ((flags & REISERFS_NOTAIL_FL) &&
S_ISREG(inode->i_mode)) {
int result;
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 4f1a397fda69..bdea836fc38b 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
if (err)
return err;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
mutex_lock(&ui->ui_mutex);
oldflags = ubifs2ioctl(ui->flags);
- if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock;
ui->flags = ioctl2ubifs(flags);
ubifs_set_inode_flags(inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7fdfe93e25d..1825d055808c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
}
#endif
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
+
#endif /* _LINUX_FS_H */
^ permalink raw reply related [flat|nested] 66+ messages in thread* [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-11 4:45 ` Darrick J. Wong
0 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-11 4:45 UTC (permalink / raw)
To: matthew.garrett, yuchao0, tytso, darrick.wong, shaggy,
ard.biesheuvel, josef, clm, adilger.kernel, jk, jack, dsterba,
jaegeuk, viro
Cc: linux-xfs, jfs-discussion, linux-efi, linux-kernel,
reiserfs-devel, linux-f2fs-devel, cluster-devel, linux-nilfs,
linux-mtd, linux-btrfs, linux-fsdevel, linux-ext4, ocfs2-devel
From: Darrick J. Wong <darrick.wong@oracle.com>
Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
values so that we can standardize the implementations that follow ext4's
flag values.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/btrfs/ioctl.c | 13 +++++--------
fs/efivarfs/file.c | 18 +++++++++++++-----
fs/ext2/ioctl.c | 16 ++++------------
fs/ext4/ioctl.c | 13 +++----------
fs/f2fs/file.c | 7 ++++---
fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
fs/hfsplus/ioctl.c | 21 ++++++++++++---------
fs/inode.c | 17 +++++++++++++++++
fs/jfs/ioctl.c | 6 ++++++
fs/nilfs2/ioctl.c | 9 ++-------
fs/ocfs2/ioctl.c | 13 +++----------
fs/reiserfs/ioctl.c | 10 ++++------
fs/ubifs/ioctl.c | 13 +++----------
include/linux/fs.h | 2 ++
14 files changed, 107 insertions(+), 93 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6dafa857bbb9..f408aa93b0cf 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(inode);
struct btrfs_root *root = binode->root;
struct btrfs_trans_handle *trans;
- unsigned int fsflags;
+ unsigned int fsflags, old_fsflags;
int ret;
const char *comp = NULL;
u32 binode_flags = binode->flags;
@@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
inode_lock(inode);
fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
- if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
- (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- ret = -EPERM;
- goto out_unlock;
- }
- }
+ old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
+ ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
+ if (ret)
+ goto out_unlock;
if (fsflags & FS_SYNC_FL)
binode_flags |= BTRFS_INODE_SYNC;
diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
index 8e568428c88b..f4f6c1bec132 100644
--- a/fs/efivarfs/file.c
+++ b/fs/efivarfs/file.c
@@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
return size;
}
-static int
-efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+static inline unsigned int efivarfs_getflags(struct inode *inode)
{
- struct inode *inode = file->f_mapping->host;
unsigned int i_flags;
unsigned int flags = 0;
i_flags = inode->i_flags;
if (i_flags & S_IMMUTABLE)
flags |= FS_IMMUTABLE_FL;
+ return flags;
+}
+
+static int
+efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+{
+ struct inode *inode = file->f_mapping->host;
+ unsigned int flags = efivarfs_getflags(inode);
if (copy_to_user(arg, &flags, sizeof(flags)))
return -EFAULT;
@@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
struct inode *inode = file->f_mapping->host;
unsigned int flags;
unsigned int i_flags = 0;
+ unsigned int oldflags = efivarfs_getflags(inode);
int error;
if (!inode_owner_or_capable(inode))
@@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
if (flags & ~FS_IMMUTABLE_FL)
return -EOPNOTSUPP;
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ error = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (error)
+ return error;
if (flags & FS_IMMUTABLE_FL)
i_flags |= S_IMMUTABLE;
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 0367c0039e68..88b3b9720023 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- ret = -EPERM;
- goto setflags_out;
- }
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & EXT2_FL_USER_MODIFIABLE;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index e486e49b31ed..5126ee351a84 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
/* The JOURNAL_DATA flag is modifiable only by root */
jflag = flags & EXT4_JOURNAL_DATA_FL;
- /*
- * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto flags_out;
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto flags_out;
/*
* The JOURNAL_DATA flag can only be changed by
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 45b45f37d347..a969d5497e03 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
unsigned int oldflags;
+ int err;
/* Is it quota file? Do not allow user to mess with it */
if (IS_NOQUOTA(inode))
@@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
oldflags = fi->i_flags;
- if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ return err;
flags = flags & F2FS_FL_USER_MODIFIABLE;
flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index d174b1f8fd08..99f53cf699c6 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -136,27 +136,36 @@ static struct {
{FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
};
+static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
+{
+ int i;
+ u32 fsflags = 0;
+
+ if (S_ISDIR(inode->i_mode))
+ gfsflags &= ~GFS2_DIF_JDATA;
+ else
+ gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
+
+ for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
+ if (gfsflags & fsflag_gfs2flag[i].gfsflag)
+ fsflags |= fsflag_gfs2flag[i].fsflag;
+ return fsflags;
+}
+
static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
- int i, error;
- u32 gfsflags, fsflags = 0;
+ int error;
+ u32 fsflags;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
error = gfs2_glock_nq(&gh);
if (error)
goto out_uninit;
- gfsflags = ip->i_diskflags;
- if (S_ISDIR(inode->i_mode))
- gfsflags &= ~GFS2_DIF_JDATA;
- else
- gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
- for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
- if (gfsflags & fsflag_gfs2flag[i].gfsflag)
- fsflags |= fsflag_gfs2flag[i].fsflag;
+ fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
if (put_user(fsflags, ptr))
error = -EFAULT;
@@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
* @filp: file pointer
* @reqflags: The flags to set
* @mask: Indicates which flags are valid
+ * @fsflags: The FS_* inode flags passed in
*
*/
-static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
+static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
+ const u32 fsflags)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
@@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
struct buffer_head *bh;
struct gfs2_holder gh;
int error;
- u32 new_flags, flags;
+ u32 new_flags, flags, oldflags;
error = mnt_want_write_file(filp);
if (error)
@@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
if (error)
goto out_drop_write;
+ oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
+ error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
+ if (error)
+ goto out;
+
error = -EACCES;
if (!inode_owner_or_capable(inode))
goto out;
@@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
}
- return do_gfs2_set_flags(filp, gfsflags, mask);
+ return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
}
static int gfs2_getlabel(struct file *filp, char __user *label)
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
index 5e6502ef7415..862a3c9481d7 100644
--- a/fs/hfsplus/ioctl.c
+++ b/fs/hfsplus/ioctl.c
@@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
return 0;
}
-static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+static inline unsigned int hfsplus_getflags(struct inode *inode)
{
- struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags = 0;
@@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
flags |= FS_APPEND_FL;
if (hip->userflags & HFSPLUS_FLG_NODUMP)
flags |= FS_NODUMP_FL;
+ return flags;
+}
+
+static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+{
+ struct inode *inode = file_inode(file);
+ unsigned int flags = hfsplus_getflags(inode);
return put_user(flags, user_flags);
}
@@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags, new_fl = 0;
+ unsigned int oldflags = hfsplus_getflags(inode);
int err = 0;
err = mnt_want_write_file(file);
@@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
inode_lock(inode);
- if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
- inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock_inode;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock_inode;
/* don't silently ignore unsupported ext2 flags */
if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
diff --git a/fs/inode.c b/fs/inode.c
index df6542ec3b88..0ce60b720608 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
return timespec64_trunc(now, inode->i_sb->s_time_gran);
}
EXPORT_SYMBOL(current_time);
+
+/* Generic function to check FS_IOC_SETFLAGS values. */
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
index ba34dae8bd9f..c8446d2cd0c7 100644
--- a/fs/jfs/ioctl.c
+++ b/fs/jfs/ioctl.c
@@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
/* Lock against other parallel changes of flags */
inode_lock(inode);
+ oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
+ 0);
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto setflags_out;
+
oldflags = jfs_inode->mode2;
/*
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 9b96d79eea6c..0632336d2515 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
oldflags = NILFS_I(inode)->i_flags;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
- * relevant capability.
- */
- ret = -EPERM;
- if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
- !capable(CAP_LINUX_IMMUTABLE))
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret)
goto out;
ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 994726ada857..467a2faf0305 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
flags = flags & mask;
flags |= oldflags & ~mask;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- status = -EPERM;
- if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
- (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto bail_unlock;
- }
+ status = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (status)
+ goto bail_unlock;
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
if (IS_ERR(handle)) {
diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
index acbbaf7a0bb2..92bcb1ecd994 100644
--- a/fs/reiserfs/ioctl.c
+++ b/fs/reiserfs/ioctl.c
@@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
err = -EPERM;
goto setflags_out;
}
- if (((flags ^ REISERFS_I(inode)->
- i_attrs) & (REISERFS_IMMUTABLE_FL |
- REISERFS_APPEND_FL))
- && !capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
+ err = vfs_ioc_setflags_check(inode,
+ REISERFS_I(inode)->i_attrs,
+ flags);
+ if (err)
goto setflags_out;
- }
if ((flags & REISERFS_NOTAIL_FL) &&
S_ISREG(inode->i_mode)) {
int result;
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 4f1a397fda69..bdea836fc38b 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
if (err)
return err;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
mutex_lock(&ui->ui_mutex);
oldflags = ubifs2ioctl(ui->flags);
- if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock;
ui->flags = ioctl2ubifs(flags);
ubifs_set_inode_flags(inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7fdfe93e25d..1825d055808c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
}
#endif
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
+
#endif /* _LINUX_FS_H */
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 66+ messages in thread* [f2fs-dev] [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-11 4:45 ` Darrick J. Wong
0 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-11 4:45 UTC (permalink / raw)
To: matthew.garrett, yuchao0, tytso, darrick.wong, shaggy,
ard.biesheuvel, josef, clm, adilger.kernel, jk, jack, dsterba,
jaegeuk, viro
Cc: linux-xfs, jfs-discussion, linux-efi, linux-kernel,
reiserfs-devel, linux-f2fs-devel, cluster-devel, linux-nilfs,
linux-mtd, linux-btrfs, linux-fsdevel, linux-ext4, ocfs2-devel
From: Darrick J. Wong <darrick.wong@oracle.com>
Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
values so that we can standardize the implementations that follow ext4's
flag values.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/btrfs/ioctl.c | 13 +++++--------
fs/efivarfs/file.c | 18 +++++++++++++-----
fs/ext2/ioctl.c | 16 ++++------------
fs/ext4/ioctl.c | 13 +++----------
fs/f2fs/file.c | 7 ++++---
fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
fs/hfsplus/ioctl.c | 21 ++++++++++++---------
fs/inode.c | 17 +++++++++++++++++
fs/jfs/ioctl.c | 6 ++++++
fs/nilfs2/ioctl.c | 9 ++-------
fs/ocfs2/ioctl.c | 13 +++----------
fs/reiserfs/ioctl.c | 10 ++++------
fs/ubifs/ioctl.c | 13 +++----------
include/linux/fs.h | 2 ++
14 files changed, 107 insertions(+), 93 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6dafa857bbb9..f408aa93b0cf 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(inode);
struct btrfs_root *root = binode->root;
struct btrfs_trans_handle *trans;
- unsigned int fsflags;
+ unsigned int fsflags, old_fsflags;
int ret;
const char *comp = NULL;
u32 binode_flags = binode->flags;
@@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
inode_lock(inode);
fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
- if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
- (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- ret = -EPERM;
- goto out_unlock;
- }
- }
+ old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
+ ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
+ if (ret)
+ goto out_unlock;
if (fsflags & FS_SYNC_FL)
binode_flags |= BTRFS_INODE_SYNC;
diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
index 8e568428c88b..f4f6c1bec132 100644
--- a/fs/efivarfs/file.c
+++ b/fs/efivarfs/file.c
@@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
return size;
}
-static int
-efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+static inline unsigned int efivarfs_getflags(struct inode *inode)
{
- struct inode *inode = file->f_mapping->host;
unsigned int i_flags;
unsigned int flags = 0;
i_flags = inode->i_flags;
if (i_flags & S_IMMUTABLE)
flags |= FS_IMMUTABLE_FL;
+ return flags;
+}
+
+static int
+efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+{
+ struct inode *inode = file->f_mapping->host;
+ unsigned int flags = efivarfs_getflags(inode);
if (copy_to_user(arg, &flags, sizeof(flags)))
return -EFAULT;
@@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
struct inode *inode = file->f_mapping->host;
unsigned int flags;
unsigned int i_flags = 0;
+ unsigned int oldflags = efivarfs_getflags(inode);
int error;
if (!inode_owner_or_capable(inode))
@@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
if (flags & ~FS_IMMUTABLE_FL)
return -EOPNOTSUPP;
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ error = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (error)
+ return error;
if (flags & FS_IMMUTABLE_FL)
i_flags |= S_IMMUTABLE;
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 0367c0039e68..88b3b9720023 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- ret = -EPERM;
- goto setflags_out;
- }
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & EXT2_FL_USER_MODIFIABLE;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index e486e49b31ed..5126ee351a84 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
/* The JOURNAL_DATA flag is modifiable only by root */
jflag = flags & EXT4_JOURNAL_DATA_FL;
- /*
- * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto flags_out;
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto flags_out;
/*
* The JOURNAL_DATA flag can only be changed by
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 45b45f37d347..a969d5497e03 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
unsigned int oldflags;
+ int err;
/* Is it quota file? Do not allow user to mess with it */
if (IS_NOQUOTA(inode))
@@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
oldflags = fi->i_flags;
- if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ return err;
flags = flags & F2FS_FL_USER_MODIFIABLE;
flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index d174b1f8fd08..99f53cf699c6 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -136,27 +136,36 @@ static struct {
{FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
};
+static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
+{
+ int i;
+ u32 fsflags = 0;
+
+ if (S_ISDIR(inode->i_mode))
+ gfsflags &= ~GFS2_DIF_JDATA;
+ else
+ gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
+
+ for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
+ if (gfsflags & fsflag_gfs2flag[i].gfsflag)
+ fsflags |= fsflag_gfs2flag[i].fsflag;
+ return fsflags;
+}
+
static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
- int i, error;
- u32 gfsflags, fsflags = 0;
+ int error;
+ u32 fsflags;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
error = gfs2_glock_nq(&gh);
if (error)
goto out_uninit;
- gfsflags = ip->i_diskflags;
- if (S_ISDIR(inode->i_mode))
- gfsflags &= ~GFS2_DIF_JDATA;
- else
- gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
- for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
- if (gfsflags & fsflag_gfs2flag[i].gfsflag)
- fsflags |= fsflag_gfs2flag[i].fsflag;
+ fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
if (put_user(fsflags, ptr))
error = -EFAULT;
@@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
* @filp: file pointer
* @reqflags: The flags to set
* @mask: Indicates which flags are valid
+ * @fsflags: The FS_* inode flags passed in
*
*/
-static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
+static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
+ const u32 fsflags)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
@@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
struct buffer_head *bh;
struct gfs2_holder gh;
int error;
- u32 new_flags, flags;
+ u32 new_flags, flags, oldflags;
error = mnt_want_write_file(filp);
if (error)
@@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
if (error)
goto out_drop_write;
+ oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
+ error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
+ if (error)
+ goto out;
+
error = -EACCES;
if (!inode_owner_or_capable(inode))
goto out;
@@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
}
- return do_gfs2_set_flags(filp, gfsflags, mask);
+ return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
}
static int gfs2_getlabel(struct file *filp, char __user *label)
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
index 5e6502ef7415..862a3c9481d7 100644
--- a/fs/hfsplus/ioctl.c
+++ b/fs/hfsplus/ioctl.c
@@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
return 0;
}
-static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+static inline unsigned int hfsplus_getflags(struct inode *inode)
{
- struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags = 0;
@@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
flags |= FS_APPEND_FL;
if (hip->userflags & HFSPLUS_FLG_NODUMP)
flags |= FS_NODUMP_FL;
+ return flags;
+}
+
+static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+{
+ struct inode *inode = file_inode(file);
+ unsigned int flags = hfsplus_getflags(inode);
return put_user(flags, user_flags);
}
@@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags, new_fl = 0;
+ unsigned int oldflags = hfsplus_getflags(inode);
int err = 0;
err = mnt_want_write_file(file);
@@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
inode_lock(inode);
- if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
- inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock_inode;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock_inode;
/* don't silently ignore unsupported ext2 flags */
if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
diff --git a/fs/inode.c b/fs/inode.c
index df6542ec3b88..0ce60b720608 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
return timespec64_trunc(now, inode->i_sb->s_time_gran);
}
EXPORT_SYMBOL(current_time);
+
+/* Generic function to check FS_IOC_SETFLAGS values. */
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
index ba34dae8bd9f..c8446d2cd0c7 100644
--- a/fs/jfs/ioctl.c
+++ b/fs/jfs/ioctl.c
@@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
/* Lock against other parallel changes of flags */
inode_lock(inode);
+ oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
+ 0);
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto setflags_out;
+
oldflags = jfs_inode->mode2;
/*
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 9b96d79eea6c..0632336d2515 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
oldflags = NILFS_I(inode)->i_flags;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
- * relevant capability.
- */
- ret = -EPERM;
- if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
- !capable(CAP_LINUX_IMMUTABLE))
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret)
goto out;
ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 994726ada857..467a2faf0305 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
flags = flags & mask;
flags |= oldflags & ~mask;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- status = -EPERM;
- if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
- (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto bail_unlock;
- }
+ status = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (status)
+ goto bail_unlock;
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
if (IS_ERR(handle)) {
diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
index acbbaf7a0bb2..92bcb1ecd994 100644
--- a/fs/reiserfs/ioctl.c
+++ b/fs/reiserfs/ioctl.c
@@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
err = -EPERM;
goto setflags_out;
}
- if (((flags ^ REISERFS_I(inode)->
- i_attrs) & (REISERFS_IMMUTABLE_FL |
- REISERFS_APPEND_FL))
- && !capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
+ err = vfs_ioc_setflags_check(inode,
+ REISERFS_I(inode)->i_attrs,
+ flags);
+ if (err)
goto setflags_out;
- }
if ((flags & REISERFS_NOTAIL_FL) &&
S_ISREG(inode->i_mode)) {
int result;
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 4f1a397fda69..bdea836fc38b 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
if (err)
return err;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
mutex_lock(&ui->ui_mutex);
oldflags = ubifs2ioctl(ui->flags);
- if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock;
ui->flags = ioctl2ubifs(flags);
ubifs_set_inode_flags(inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7fdfe93e25d..1825d055808c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
}
#endif
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
+
#endif /* _LINUX_FS_H */
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply related [flat|nested] 66+ messages in thread* [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-11 4:45 ` Darrick J. Wong
0 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-11 4:45 UTC (permalink / raw)
To: matthew.garrett, yuchao0, tytso, darrick.wong, shaggy,
ard.biesheuvel, josef, clm, adilger.kernel, jk, jack, dsterba,
jaegeuk, viro
Cc: linux-xfs, jfs-discussion, linux-efi, linux-kernel,
reiserfs-devel, linux-f2fs-devel, cluster-devel, linux-nilfs,
linux-mtd, linux-btrfs, linux-fsdevel, linux-ext4, ocfs2-devel
From: Darrick J. Wong <darrick.wong@oracle.com>
Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
values so that we can standardize the implementations that follow ext4's
flag values.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/btrfs/ioctl.c | 13 +++++--------
fs/efivarfs/file.c | 18 +++++++++++++-----
fs/ext2/ioctl.c | 16 ++++------------
fs/ext4/ioctl.c | 13 +++----------
fs/f2fs/file.c | 7 ++++---
fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
fs/hfsplus/ioctl.c | 21 ++++++++++++---------
fs/inode.c | 17 +++++++++++++++++
fs/jfs/ioctl.c | 6 ++++++
fs/nilfs2/ioctl.c | 9 ++-------
fs/ocfs2/ioctl.c | 13 +++----------
fs/reiserfs/ioctl.c | 10 ++++------
fs/ubifs/ioctl.c | 13 +++----------
include/linux/fs.h | 2 ++
14 files changed, 107 insertions(+), 93 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6dafa857bbb9..f408aa93b0cf 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(inode);
struct btrfs_root *root = binode->root;
struct btrfs_trans_handle *trans;
- unsigned int fsflags;
+ unsigned int fsflags, old_fsflags;
int ret;
const char *comp = NULL;
u32 binode_flags = binode->flags;
@@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
inode_lock(inode);
fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
- if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
- (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- ret = -EPERM;
- goto out_unlock;
- }
- }
+ old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
+ ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
+ if (ret)
+ goto out_unlock;
if (fsflags & FS_SYNC_FL)
binode_flags |= BTRFS_INODE_SYNC;
diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
index 8e568428c88b..f4f6c1bec132 100644
--- a/fs/efivarfs/file.c
+++ b/fs/efivarfs/file.c
@@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
return size;
}
-static int
-efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+static inline unsigned int efivarfs_getflags(struct inode *inode)
{
- struct inode *inode = file->f_mapping->host;
unsigned int i_flags;
unsigned int flags = 0;
i_flags = inode->i_flags;
if (i_flags & S_IMMUTABLE)
flags |= FS_IMMUTABLE_FL;
+ return flags;
+}
+
+static int
+efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+{
+ struct inode *inode = file->f_mapping->host;
+ unsigned int flags = efivarfs_getflags(inode);
if (copy_to_user(arg, &flags, sizeof(flags)))
return -EFAULT;
@@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
struct inode *inode = file->f_mapping->host;
unsigned int flags;
unsigned int i_flags = 0;
+ unsigned int oldflags = efivarfs_getflags(inode);
int error;
if (!inode_owner_or_capable(inode))
@@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
if (flags & ~FS_IMMUTABLE_FL)
return -EOPNOTSUPP;
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ error = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (error)
+ return error;
if (flags & FS_IMMUTABLE_FL)
i_flags |= S_IMMUTABLE;
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 0367c0039e68..88b3b9720023 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- ret = -EPERM;
- goto setflags_out;
- }
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & EXT2_FL_USER_MODIFIABLE;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index e486e49b31ed..5126ee351a84 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
/* The JOURNAL_DATA flag is modifiable only by root */
jflag = flags & EXT4_JOURNAL_DATA_FL;
- /*
- * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto flags_out;
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto flags_out;
/*
* The JOURNAL_DATA flag can only be changed by
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 45b45f37d347..a969d5497e03 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
unsigned int oldflags;
+ int err;
/* Is it quota file? Do not allow user to mess with it */
if (IS_NOQUOTA(inode))
@@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
oldflags = fi->i_flags;
- if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ return err;
flags = flags & F2FS_FL_USER_MODIFIABLE;
flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index d174b1f8fd08..99f53cf699c6 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -136,27 +136,36 @@ static struct {
{FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
};
+static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
+{
+ int i;
+ u32 fsflags = 0;
+
+ if (S_ISDIR(inode->i_mode))
+ gfsflags &= ~GFS2_DIF_JDATA;
+ else
+ gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
+
+ for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
+ if (gfsflags & fsflag_gfs2flag[i].gfsflag)
+ fsflags |= fsflag_gfs2flag[i].fsflag;
+ return fsflags;
+}
+
static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
- int i, error;
- u32 gfsflags, fsflags = 0;
+ int error;
+ u32 fsflags;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
error = gfs2_glock_nq(&gh);
if (error)
goto out_uninit;
- gfsflags = ip->i_diskflags;
- if (S_ISDIR(inode->i_mode))
- gfsflags &= ~GFS2_DIF_JDATA;
- else
- gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
- for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
- if (gfsflags & fsflag_gfs2flag[i].gfsflag)
- fsflags |= fsflag_gfs2flag[i].fsflag;
+ fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
if (put_user(fsflags, ptr))
error = -EFAULT;
@@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
* @filp: file pointer
* @reqflags: The flags to set
* @mask: Indicates which flags are valid
+ * @fsflags: The FS_* inode flags passed in
*
*/
-static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
+static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
+ const u32 fsflags)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
@@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
struct buffer_head *bh;
struct gfs2_holder gh;
int error;
- u32 new_flags, flags;
+ u32 new_flags, flags, oldflags;
error = mnt_want_write_file(filp);
if (error)
@@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
if (error)
goto out_drop_write;
+ oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
+ error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
+ if (error)
+ goto out;
+
error = -EACCES;
if (!inode_owner_or_capable(inode))
goto out;
@@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
}
- return do_gfs2_set_flags(filp, gfsflags, mask);
+ return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
}
static int gfs2_getlabel(struct file *filp, char __user *label)
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
index 5e6502ef7415..862a3c9481d7 100644
--- a/fs/hfsplus/ioctl.c
+++ b/fs/hfsplus/ioctl.c
@@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
return 0;
}
-static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+static inline unsigned int hfsplus_getflags(struct inode *inode)
{
- struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags = 0;
@@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
flags |= FS_APPEND_FL;
if (hip->userflags & HFSPLUS_FLG_NODUMP)
flags |= FS_NODUMP_FL;
+ return flags;
+}
+
+static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+{
+ struct inode *inode = file_inode(file);
+ unsigned int flags = hfsplus_getflags(inode);
return put_user(flags, user_flags);
}
@@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags, new_fl = 0;
+ unsigned int oldflags = hfsplus_getflags(inode);
int err = 0;
err = mnt_want_write_file(file);
@@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
inode_lock(inode);
- if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
- inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock_inode;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock_inode;
/* don't silently ignore unsupported ext2 flags */
if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
diff --git a/fs/inode.c b/fs/inode.c
index df6542ec3b88..0ce60b720608 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
return timespec64_trunc(now, inode->i_sb->s_time_gran);
}
EXPORT_SYMBOL(current_time);
+
+/* Generic function to check FS_IOC_SETFLAGS values. */
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
index ba34dae8bd9f..c8446d2cd0c7 100644
--- a/fs/jfs/ioctl.c
+++ b/fs/jfs/ioctl.c
@@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
/* Lock against other parallel changes of flags */
inode_lock(inode);
+ oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
+ 0);
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto setflags_out;
+
oldflags = jfs_inode->mode2;
/*
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 9b96d79eea6c..0632336d2515 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
oldflags = NILFS_I(inode)->i_flags;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
- * relevant capability.
- */
- ret = -EPERM;
- if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
- !capable(CAP_LINUX_IMMUTABLE))
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret)
goto out;
ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 994726ada857..467a2faf0305 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
flags = flags & mask;
flags |= oldflags & ~mask;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- status = -EPERM;
- if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
- (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto bail_unlock;
- }
+ status = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (status)
+ goto bail_unlock;
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
if (IS_ERR(handle)) {
diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
index acbbaf7a0bb2..92bcb1ecd994 100644
--- a/fs/reiserfs/ioctl.c
+++ b/fs/reiserfs/ioctl.c
@@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
err = -EPERM;
goto setflags_out;
}
- if (((flags ^ REISERFS_I(inode)->
- i_attrs) & (REISERFS_IMMUTABLE_FL |
- REISERFS_APPEND_FL))
- && !capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
+ err = vfs_ioc_setflags_check(inode,
+ REISERFS_I(inode)->i_attrs,
+ flags);
+ if (err)
goto setflags_out;
- }
if ((flags & REISERFS_NOTAIL_FL) &&
S_ISREG(inode->i_mode)) {
int result;
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 4f1a397fda69..bdea836fc38b 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
if (err)
return err;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
mutex_lock(&ui->ui_mutex);
oldflags = ubifs2ioctl(ui->flags);
- if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock;
ui->flags = ioctl2ubifs(flags);
ubifs_set_inode_flags(inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7fdfe93e25d..1825d055808c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
}
#endif
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
+
#endif /* _LINUX_FS_H */
^ permalink raw reply related [flat|nested] 66+ messages in thread* [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-11 4:45 ` Darrick J. Wong
0 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-11 4:45 UTC (permalink / raw)
To: matthew.garrett, yuchao0, tytso, darrick.wong, shaggy,
ard.biesheuvel, josef, clm, adilger.kernel, jk, jack, dsterba,
jaegeuk, viro
Cc: cluster-devel, jfs-discussion, linux-efi, reiserfs-devel,
linux-kernel, linux-f2fs-devel, linux-xfs, linux-nilfs, linux-mtd,
ocfs2-devel, linux-fsdevel, linux-ext4, linux-btrfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
values so that we can standardize the implementations that follow ext4's
flag values.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/btrfs/ioctl.c | 13 +++++--------
fs/efivarfs/file.c | 18 +++++++++++++-----
fs/ext2/ioctl.c | 16 ++++------------
fs/ext4/ioctl.c | 13 +++----------
fs/f2fs/file.c | 7 ++++---
fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
fs/hfsplus/ioctl.c | 21 ++++++++++++---------
fs/inode.c | 17 +++++++++++++++++
fs/jfs/ioctl.c | 6 ++++++
fs/nilfs2/ioctl.c | 9 ++-------
fs/ocfs2/ioctl.c | 13 +++----------
fs/reiserfs/ioctl.c | 10 ++++------
fs/ubifs/ioctl.c | 13 +++----------
include/linux/fs.h | 2 ++
14 files changed, 107 insertions(+), 93 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6dafa857bbb9..f408aa93b0cf 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(inode);
struct btrfs_root *root = binode->root;
struct btrfs_trans_handle *trans;
- unsigned int fsflags;
+ unsigned int fsflags, old_fsflags;
int ret;
const char *comp = NULL;
u32 binode_flags = binode->flags;
@@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
inode_lock(inode);
fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
- if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
- (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- ret = -EPERM;
- goto out_unlock;
- }
- }
+ old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
+ ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
+ if (ret)
+ goto out_unlock;
if (fsflags & FS_SYNC_FL)
binode_flags |= BTRFS_INODE_SYNC;
diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
index 8e568428c88b..f4f6c1bec132 100644
--- a/fs/efivarfs/file.c
+++ b/fs/efivarfs/file.c
@@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
return size;
}
-static int
-efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+static inline unsigned int efivarfs_getflags(struct inode *inode)
{
- struct inode *inode = file->f_mapping->host;
unsigned int i_flags;
unsigned int flags = 0;
i_flags = inode->i_flags;
if (i_flags & S_IMMUTABLE)
flags |= FS_IMMUTABLE_FL;
+ return flags;
+}
+
+static int
+efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+{
+ struct inode *inode = file->f_mapping->host;
+ unsigned int flags = efivarfs_getflags(inode);
if (copy_to_user(arg, &flags, sizeof(flags)))
return -EFAULT;
@@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
struct inode *inode = file->f_mapping->host;
unsigned int flags;
unsigned int i_flags = 0;
+ unsigned int oldflags = efivarfs_getflags(inode);
int error;
if (!inode_owner_or_capable(inode))
@@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
if (flags & ~FS_IMMUTABLE_FL)
return -EOPNOTSUPP;
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ error = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (error)
+ return error;
if (flags & FS_IMMUTABLE_FL)
i_flags |= S_IMMUTABLE;
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 0367c0039e68..88b3b9720023 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- ret = -EPERM;
- goto setflags_out;
- }
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & EXT2_FL_USER_MODIFIABLE;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index e486e49b31ed..5126ee351a84 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
/* The JOURNAL_DATA flag is modifiable only by root */
jflag = flags & EXT4_JOURNAL_DATA_FL;
- /*
- * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto flags_out;
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto flags_out;
/*
* The JOURNAL_DATA flag can only be changed by
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 45b45f37d347..a969d5497e03 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
unsigned int oldflags;
+ int err;
/* Is it quota file? Do not allow user to mess with it */
if (IS_NOQUOTA(inode))
@@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
oldflags = fi->i_flags;
- if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ return err;
flags = flags & F2FS_FL_USER_MODIFIABLE;
flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index d174b1f8fd08..99f53cf699c6 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -136,27 +136,36 @@ static struct {
{FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
};
+static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
+{
+ int i;
+ u32 fsflags = 0;
+
+ if (S_ISDIR(inode->i_mode))
+ gfsflags &= ~GFS2_DIF_JDATA;
+ else
+ gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
+
+ for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
+ if (gfsflags & fsflag_gfs2flag[i].gfsflag)
+ fsflags |= fsflag_gfs2flag[i].fsflag;
+ return fsflags;
+}
+
static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
- int i, error;
- u32 gfsflags, fsflags = 0;
+ int error;
+ u32 fsflags;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
error = gfs2_glock_nq(&gh);
if (error)
goto out_uninit;
- gfsflags = ip->i_diskflags;
- if (S_ISDIR(inode->i_mode))
- gfsflags &= ~GFS2_DIF_JDATA;
- else
- gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
- for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
- if (gfsflags & fsflag_gfs2flag[i].gfsflag)
- fsflags |= fsflag_gfs2flag[i].fsflag;
+ fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
if (put_user(fsflags, ptr))
error = -EFAULT;
@@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
* @filp: file pointer
* @reqflags: The flags to set
* @mask: Indicates which flags are valid
+ * @fsflags: The FS_* inode flags passed in
*
*/
-static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
+static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
+ const u32 fsflags)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
@@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
struct buffer_head *bh;
struct gfs2_holder gh;
int error;
- u32 new_flags, flags;
+ u32 new_flags, flags, oldflags;
error = mnt_want_write_file(filp);
if (error)
@@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
if (error)
goto out_drop_write;
+ oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
+ error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
+ if (error)
+ goto out;
+
error = -EACCES;
if (!inode_owner_or_capable(inode))
goto out;
@@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
}
- return do_gfs2_set_flags(filp, gfsflags, mask);
+ return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
}
static int gfs2_getlabel(struct file *filp, char __user *label)
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
index 5e6502ef7415..862a3c9481d7 100644
--- a/fs/hfsplus/ioctl.c
+++ b/fs/hfsplus/ioctl.c
@@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
return 0;
}
-static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+static inline unsigned int hfsplus_getflags(struct inode *inode)
{
- struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags = 0;
@@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
flags |= FS_APPEND_FL;
if (hip->userflags & HFSPLUS_FLG_NODUMP)
flags |= FS_NODUMP_FL;
+ return flags;
+}
+
+static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+{
+ struct inode *inode = file_inode(file);
+ unsigned int flags = hfsplus_getflags(inode);
return put_user(flags, user_flags);
}
@@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags, new_fl = 0;
+ unsigned int oldflags = hfsplus_getflags(inode);
int err = 0;
err = mnt_want_write_file(file);
@@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
inode_lock(inode);
- if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
- inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock_inode;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock_inode;
/* don't silently ignore unsupported ext2 flags */
if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
diff --git a/fs/inode.c b/fs/inode.c
index df6542ec3b88..0ce60b720608 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
return timespec64_trunc(now, inode->i_sb->s_time_gran);
}
EXPORT_SYMBOL(current_time);
+
+/* Generic function to check FS_IOC_SETFLAGS values. */
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
index ba34dae8bd9f..c8446d2cd0c7 100644
--- a/fs/jfs/ioctl.c
+++ b/fs/jfs/ioctl.c
@@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
/* Lock against other parallel changes of flags */
inode_lock(inode);
+ oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
+ 0);
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto setflags_out;
+
oldflags = jfs_inode->mode2;
/*
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 9b96d79eea6c..0632336d2515 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
oldflags = NILFS_I(inode)->i_flags;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
- * relevant capability.
- */
- ret = -EPERM;
- if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
- !capable(CAP_LINUX_IMMUTABLE))
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret)
goto out;
ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 994726ada857..467a2faf0305 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
flags = flags & mask;
flags |= oldflags & ~mask;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- status = -EPERM;
- if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
- (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto bail_unlock;
- }
+ status = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (status)
+ goto bail_unlock;
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
if (IS_ERR(handle)) {
diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
index acbbaf7a0bb2..92bcb1ecd994 100644
--- a/fs/reiserfs/ioctl.c
+++ b/fs/reiserfs/ioctl.c
@@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
err = -EPERM;
goto setflags_out;
}
- if (((flags ^ REISERFS_I(inode)->
- i_attrs) & (REISERFS_IMMUTABLE_FL |
- REISERFS_APPEND_FL))
- && !capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
+ err = vfs_ioc_setflags_check(inode,
+ REISERFS_I(inode)->i_attrs,
+ flags);
+ if (err)
goto setflags_out;
- }
if ((flags & REISERFS_NOTAIL_FL) &&
S_ISREG(inode->i_mode)) {
int result;
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 4f1a397fda69..bdea836fc38b 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
if (err)
return err;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
mutex_lock(&ui->ui_mutex);
oldflags = ubifs2ioctl(ui->flags);
- if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock;
ui->flags = ioctl2ubifs(flags);
ubifs_set_inode_flags(inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7fdfe93e25d..1825d055808c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
}
#endif
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
+
#endif /* _LINUX_FS_H */
^ permalink raw reply related [flat|nested] 66+ messages in thread* [Cluster-devel] [Jfs-discussion] [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
2019-06-11 4:45 ` Darrick J. Wong
` (2 preceding siblings ...)
(?)
@ 2019-06-11 13:41 ` Dave Kleikamp
-1 siblings, 0 replies; 66+ messages in thread
From: Dave Kleikamp @ 2019-06-11 13:41 UTC (permalink / raw)
To: cluster-devel.redhat.com
On 6/10/19 11:45 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> values so that we can standardize the implementations that follow ext4's
> flag values.
>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-- clip --
> diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> index ba34dae8bd9f..c8446d2cd0c7 100644
> --- a/fs/jfs/ioctl.c
> +++ b/fs/jfs/ioctl.c
> @@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> /* Lock against other parallel changes of flags */
> inode_lock(inode);
>
> + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> + 0);
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto setflags_out;
inode_unlock(inode) is not called on the error path.
> +
> oldflags = jfs_inode->mode2;
>
> /*
This patch leaves jfs's open-coded version of the same check.
Thanks,
Shaggy
^ permalink raw reply [flat|nested] 66+ messages in thread* [Ocfs2-devel] [Jfs-discussion] [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-11 13:41 ` Dave Kleikamp
0 siblings, 0 replies; 66+ messages in thread
From: Dave Kleikamp @ 2019-06-11 13:41 UTC (permalink / raw)
To: Darrick J. Wong, matthew.garrett, yuchao0, tytso, shaggy,
ard.biesheuvel, josef, clm, adilger.kernel, jk, jack, dsterba,
jaegeuk, viro
Cc: linux-xfs, jfs-discussion, linux-efi, linux-kernel,
reiserfs-devel, linux-f2fs-devel, cluster-devel, linux-nilfs,
linux-mtd, linux-btrfs, linux-fsdevel, linux-ext4, ocfs2-devel
On 6/10/19 11:45 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> values so that we can standardize the implementations that follow ext4's
> flag values.
>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-- clip --
> diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> index ba34dae8bd9f..c8446d2cd0c7 100644
> --- a/fs/jfs/ioctl.c
> +++ b/fs/jfs/ioctl.c
> @@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> /* Lock against other parallel changes of flags */
> inode_lock(inode);
>
> + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> + 0);
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto setflags_out;
inode_unlock(inode) is not called on the error path.
> +
> oldflags = jfs_inode->mode2;
>
> /*
This patch leaves jfs's open-coded version of the same check.
Thanks,
Shaggy
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [f2fs-dev] [Jfs-discussion] [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-11 13:41 ` Dave Kleikamp
0 siblings, 0 replies; 66+ messages in thread
From: Dave Kleikamp @ 2019-06-11 13:41 UTC (permalink / raw)
To: Darrick J. Wong, matthew.garrett, yuchao0, tytso, shaggy,
ard.biesheuvel, josef, clm, adilger.kernel, jk, jack, dsterba,
jaegeuk, viro
Cc: cluster-devel, jfs-discussion, linux-efi, reiserfs-devel,
linux-kernel, linux-f2fs-devel, linux-xfs, linux-nilfs, linux-mtd,
ocfs2-devel, linux-fsdevel, linux-ext4, linux-btrfs
On 6/10/19 11:45 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> values so that we can standardize the implementations that follow ext4's
> flag values.
>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-- clip --
> diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> index ba34dae8bd9f..c8446d2cd0c7 100644
> --- a/fs/jfs/ioctl.c
> +++ b/fs/jfs/ioctl.c
> @@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> /* Lock against other parallel changes of flags */
> inode_lock(inode);
>
> + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> + 0);
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto setflags_out;
inode_unlock(inode) is not called on the error path.
> +
> oldflags = jfs_inode->mode2;
>
> /*
This patch leaves jfs's open-coded version of the same check.
Thanks,
Shaggy
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [Jfs-discussion] [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-11 13:41 ` Dave Kleikamp
0 siblings, 0 replies; 66+ messages in thread
From: Dave Kleikamp @ 2019-06-11 13:41 UTC (permalink / raw)
To: Darrick J. Wong, matthew.garrett, yuchao0, tytso, shaggy,
ard.biesheuvel, josef, clm, adilger.kernel, jk, jack, dsterba,
jaegeuk, viro
Cc: cluster-devel, jfs-discussion, linux-efi, reiserfs-devel,
linux-kernel, linux-f2fs-devel, linux-xfs, linux-nilfs, linux-mtd,
ocfs2-devel, linux-fsdevel, linux-ext4, linux-btrfs
On 6/10/19 11:45 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> values so that we can standardize the implementations that follow ext4's
> flag values.
>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-- clip --
> diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> index ba34dae8bd9f..c8446d2cd0c7 100644
> --- a/fs/jfs/ioctl.c
> +++ b/fs/jfs/ioctl.c
> @@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> /* Lock against other parallel changes of flags */
> inode_lock(inode);
>
> + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> + 0);
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto setflags_out;
inode_unlock(inode) is not called on the error path.
> +
> oldflags = jfs_inode->mode2;
>
> /*
This patch leaves jfs's open-coded version of the same check.
Thanks,
Shaggy
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [Jfs-discussion] [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-11 13:41 ` Dave Kleikamp
0 siblings, 0 replies; 66+ messages in thread
From: Dave Kleikamp @ 2019-06-11 13:41 UTC (permalink / raw)
To: Darrick J. Wong, matthew.garrett, yuchao0, tytso, shaggy,
ard.biesheuvel, josef, clm, adilger.kernel, jk, jack, dsterba,
jaegeuk, viro
Cc: linux-xfs, jfs-discussion, linux-efi, linux-kernel,
reiserfs-devel, linux-f2fs-devel, cluster-devel, linux-nilfs,
linux-mtd, linux-btrfs, linux-fsdevel, linux-ext4, ocfs2-devel
On 6/10/19 11:45 PM, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> values so that we can standardize the implementations that follow ext4's
> flag values.
>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-- clip --
> diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> index ba34dae8bd9f..c8446d2cd0c7 100644
> --- a/fs/jfs/ioctl.c
> +++ b/fs/jfs/ioctl.c
> @@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> /* Lock against other parallel changes of flags */
> inode_lock(inode);
>
> + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> + 0);
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto setflags_out;
inode_unlock(inode) is not called on the error path.
> +
> oldflags = jfs_inode->mode2;
>
> /*
This patch leaves jfs's open-coded version of the same check.
Thanks,
Shaggy
^ permalink raw reply [flat|nested] 66+ messages in thread
* [Cluster-devel] [Jfs-discussion] [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
2019-06-11 13:41 ` Dave Kleikamp
` (3 preceding siblings ...)
(?)
@ 2019-06-12 0:35 ` Darrick J. Wong
-1 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-12 0:35 UTC (permalink / raw)
To: cluster-devel.redhat.com
On Tue, Jun 11, 2019 at 08:41:06AM -0500, Dave Kleikamp wrote:
> On 6/10/19 11:45 PM, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> >
> > Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> > values so that we can standardize the implementations that follow ext4's
> > flag values.
> >
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
>
> -- clip --
>
> > diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> > index ba34dae8bd9f..c8446d2cd0c7 100644
> > --- a/fs/jfs/ioctl.c
> > +++ b/fs/jfs/ioctl.c
> > @@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> > /* Lock against other parallel changes of flags */
> > inode_lock(inode);
> >
> > + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> > + 0);
> > + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> > + if (err)
> > + goto setflags_out;
>
> inode_unlock(inode) is not called on the error path.
>
> > +
> > oldflags = jfs_inode->mode2;
> >
> > /*
>
> This patch leaves jfs's open-coded version of the same check.
Heh, thanks for pointing that out. I'll fix both of those things.
--D
> Thanks,
> Shaggy
^ permalink raw reply [flat|nested] 66+ messages in thread* [Ocfs2-devel] [Jfs-discussion] [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-12 0:35 ` Darrick J. Wong
0 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-12 0:35 UTC (permalink / raw)
To: Dave Kleikamp
Cc: matthew.garrett, yuchao0, tytso, shaggy, ard.biesheuvel, josef,
clm, adilger.kernel, jk, jack, dsterba, jaegeuk, viro, linux-xfs,
jfs-discussion, linux-efi, linux-kernel, reiserfs-devel,
linux-f2fs-devel, cluster-devel, linux-nilfs, linux-mtd,
linux-btrfs, linux-fsdevel, linux-ext4, ocfs2-devel
On Tue, Jun 11, 2019 at 08:41:06AM -0500, Dave Kleikamp wrote:
> On 6/10/19 11:45 PM, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> >
> > Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> > values so that we can standardize the implementations that follow ext4's
> > flag values.
> >
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
>
> -- clip --
>
> > diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> > index ba34dae8bd9f..c8446d2cd0c7 100644
> > --- a/fs/jfs/ioctl.c
> > +++ b/fs/jfs/ioctl.c
> > @@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> > /* Lock against other parallel changes of flags */
> > inode_lock(inode);
> >
> > + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> > + 0);
> > + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> > + if (err)
> > + goto setflags_out;
>
> inode_unlock(inode) is not called on the error path.
>
> > +
> > oldflags = jfs_inode->mode2;
> >
> > /*
>
> This patch leaves jfs's open-coded version of the same check.
Heh, thanks for pointing that out. I'll fix both of those things.
--D
> Thanks,
> Shaggy
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [Jfs-discussion] [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-12 0:35 ` Darrick J. Wong
0 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-12 0:35 UTC (permalink / raw)
To: Dave Kleikamp
Cc: shaggy, jfs-discussion, linux-efi, yuchao0, clm, adilger.kernel,
ocfs2-devel, matthew.garrett, linux-nilfs, cluster-devel,
linux-ext4, reiserfs-devel, josef, viro, dsterba, jaegeuk, tytso,
ard.biesheuvel, linux-kernel, linux-f2fs-devel, linux-xfs, jk,
jack, linux-fsdevel, linux-mtd, linux-btrfs
On Tue, Jun 11, 2019 at 08:41:06AM -0500, Dave Kleikamp wrote:
> On 6/10/19 11:45 PM, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> >
> > Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> > values so that we can standardize the implementations that follow ext4's
> > flag values.
> >
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
>
> -- clip --
>
> > diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> > index ba34dae8bd9f..c8446d2cd0c7 100644
> > --- a/fs/jfs/ioctl.c
> > +++ b/fs/jfs/ioctl.c
> > @@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> > /* Lock against other parallel changes of flags */
> > inode_lock(inode);
> >
> > + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> > + 0);
> > + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> > + if (err)
> > + goto setflags_out;
>
> inode_unlock(inode) is not called on the error path.
>
> > +
> > oldflags = jfs_inode->mode2;
> >
> > /*
>
> This patch leaves jfs's open-coded version of the same check.
Heh, thanks for pointing that out. I'll fix both of those things.
--D
> Thanks,
> Shaggy
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [f2fs-dev] [Jfs-discussion] [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-12 0:35 ` Darrick J. Wong
0 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-12 0:35 UTC (permalink / raw)
To: Dave Kleikamp
Cc: shaggy, jfs-discussion, linux-efi, clm, adilger.kernel,
ocfs2-devel, matthew.garrett, linux-nilfs, cluster-devel,
linux-ext4, reiserfs-devel, josef, viro, dsterba, jaegeuk, tytso,
ard.biesheuvel, linux-kernel, linux-f2fs-devel, linux-xfs, jk,
jack, linux-fsdevel, linux-mtd, linux-btrfs
On Tue, Jun 11, 2019 at 08:41:06AM -0500, Dave Kleikamp wrote:
> On 6/10/19 11:45 PM, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> >
> > Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> > values so that we can standardize the implementations that follow ext4's
> > flag values.
> >
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
>
> -- clip --
>
> > diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> > index ba34dae8bd9f..c8446d2cd0c7 100644
> > --- a/fs/jfs/ioctl.c
> > +++ b/fs/jfs/ioctl.c
> > @@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> > /* Lock against other parallel changes of flags */
> > inode_lock(inode);
> >
> > + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> > + 0);
> > + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> > + if (err)
> > + goto setflags_out;
>
> inode_unlock(inode) is not called on the error path.
>
> > +
> > oldflags = jfs_inode->mode2;
> >
> > /*
>
> This patch leaves jfs's open-coded version of the same check.
Heh, thanks for pointing that out. I'll fix both of those things.
--D
> Thanks,
> Shaggy
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-12 0:35 ` Darrick J. Wong
0 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-12 0:35 UTC (permalink / raw)
To: Dave Kleikamp
Cc: shaggy, jfs-discussion, linux-efi, yuchao0, clm, adilger.kernel,
ocfs2-devel, matthew.garrett, linux-nilfs, cluster-devel,
linux-ext4, reiserfs-devel, josef, viro, dsterba, jaegeuk, tytso,
ard.biesheuvel, linux-kernel, linux-f2fs-devel, linux-xfs, jk,
jack, linux-fsdevel, linux-mtd, linux-btrfs
On Tue, Jun 11, 2019 at 08:41:06AM -0500, Dave Kleikamp wrote:
> On 6/10/19 11:45 PM, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> >
> > Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> > values so that we can standardize the implementations that follow ext4's
> > flag values.
> >
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
>
> -- clip --
>
> > diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> > index ba34dae8bd9f..c8446d2cd0c7 100644
> > --- a/fs/jfs/ioctl.c
> > +++ b/fs/jfs/ioctl.c
> > @@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> > /* Lock against other parallel changes of flags */
> > inode_lock(inode);
> >
> > + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> > + 0);
> > + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> > + if (err)
> > + goto setflags_out;
>
> inode_unlock(inode) is not called on the error path.
>
> > +
> > oldflags = jfs_inode->mode2;
> >
> > /*
>
> This patch leaves jfs's open-coded version of the same check.
Heh, thanks for pointing that out. I'll fix both of those things.
--D
> Thanks,
> Shaggy
^ permalink raw reply [flat|nested] 66+ messages in thread
* Re: [Jfs-discussion] [PATCH 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-12 0:35 ` Darrick J. Wong
0 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-12 0:35 UTC (permalink / raw)
To: Dave Kleikamp
Cc: matthew.garrett, yuchao0, tytso, shaggy, ard.biesheuvel, josef,
clm, adilger.kernel, jk, jack, dsterba, jaegeuk, viro, linux-xfs,
jfs-discussion, linux-efi, linux-kernel, reiserfs-devel,
linux-f2fs-devel, cluster-devel, linux-nilfs, linux-mtd,
linux-btrfs, linux-fsdevel, linux-ext4, ocfs2-devel
On Tue, Jun 11, 2019 at 08:41:06AM -0500, Dave Kleikamp wrote:
> On 6/10/19 11:45 PM, Darrick J. Wong wrote:
> > From: Darrick J. Wong <darrick.wong@oracle.com>
> >
> > Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> > values so that we can standardize the implementations that follow ext4's
> > flag values.
> >
> > Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
>
> -- clip --
>
> > diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> > index ba34dae8bd9f..c8446d2cd0c7 100644
> > --- a/fs/jfs/ioctl.c
> > +++ b/fs/jfs/ioctl.c
> > @@ -98,6 +98,12 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> > /* Lock against other parallel changes of flags */
> > inode_lock(inode);
> >
> > + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> > + 0);
> > + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> > + if (err)
> > + goto setflags_out;
>
> inode_unlock(inode) is not called on the error path.
>
> > +
> > oldflags = jfs_inode->mode2;
> >
> > /*
>
> This patch leaves jfs's open-coded version of the same check.
Heh, thanks for pointing that out. I'll fix both of those things.
--D
> Thanks,
> Shaggy
^ permalink raw reply [flat|nested] 66+ messages in thread
* [Cluster-devel] [PATCH v2 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
2019-06-11 4:45 ` Darrick J. Wong
` (3 preceding siblings ...)
(?)
@ 2019-06-12 0:42 ` Darrick J. Wong
-1 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-12 0:42 UTC (permalink / raw)
To: cluster-devel.redhat.com
From: Darrick J. Wong <darrick.wong@oracle.com>
Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
values so that we can standardize the implementations that follow ext4's
flag values.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: fix jfs locking and remove its opencoded flags check
---
fs/btrfs/ioctl.c | 13 +++++--------
fs/efivarfs/file.c | 18 +++++++++++++-----
fs/ext2/ioctl.c | 16 ++++------------
fs/ext4/ioctl.c | 13 +++----------
fs/f2fs/file.c | 7 ++++---
fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
fs/hfsplus/ioctl.c | 21 ++++++++++++---------
fs/inode.c | 17 +++++++++++++++++
fs/jfs/ioctl.c | 22 +++++++---------------
fs/nilfs2/ioctl.c | 9 ++-------
fs/ocfs2/ioctl.c | 13 +++----------
fs/reiserfs/ioctl.c | 10 ++++------
fs/ubifs/ioctl.c | 13 +++----------
include/linux/fs.h | 2 ++
14 files changed, 108 insertions(+), 108 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6dafa857bbb9..f408aa93b0cf 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(inode);
struct btrfs_root *root = binode->root;
struct btrfs_trans_handle *trans;
- unsigned int fsflags;
+ unsigned int fsflags, old_fsflags;
int ret;
const char *comp = NULL;
u32 binode_flags = binode->flags;
@@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
inode_lock(inode);
fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
- if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
- (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- ret = -EPERM;
- goto out_unlock;
- }
- }
+ old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
+ ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
+ if (ret)
+ goto out_unlock;
if (fsflags & FS_SYNC_FL)
binode_flags |= BTRFS_INODE_SYNC;
diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
index 8e568428c88b..f4f6c1bec132 100644
--- a/fs/efivarfs/file.c
+++ b/fs/efivarfs/file.c
@@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
return size;
}
-static int
-efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+static inline unsigned int efivarfs_getflags(struct inode *inode)
{
- struct inode *inode = file->f_mapping->host;
unsigned int i_flags;
unsigned int flags = 0;
i_flags = inode->i_flags;
if (i_flags & S_IMMUTABLE)
flags |= FS_IMMUTABLE_FL;
+ return flags;
+}
+
+static int
+efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+{
+ struct inode *inode = file->f_mapping->host;
+ unsigned int flags = efivarfs_getflags(inode);
if (copy_to_user(arg, &flags, sizeof(flags)))
return -EFAULT;
@@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
struct inode *inode = file->f_mapping->host;
unsigned int flags;
unsigned int i_flags = 0;
+ unsigned int oldflags = efivarfs_getflags(inode);
int error;
if (!inode_owner_or_capable(inode))
@@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
if (flags & ~FS_IMMUTABLE_FL)
return -EOPNOTSUPP;
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ error = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (error)
+ return error;
if (flags & FS_IMMUTABLE_FL)
i_flags |= S_IMMUTABLE;
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 0367c0039e68..88b3b9720023 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- ret = -EPERM;
- goto setflags_out;
- }
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & EXT2_FL_USER_MODIFIABLE;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index e486e49b31ed..5126ee351a84 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
/* The JOURNAL_DATA flag is modifiable only by root */
jflag = flags & EXT4_JOURNAL_DATA_FL;
- /*
- * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto flags_out;
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto flags_out;
/*
* The JOURNAL_DATA flag can only be changed by
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 45b45f37d347..a969d5497e03 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
unsigned int oldflags;
+ int err;
/* Is it quota file? Do not allow user to mess with it */
if (IS_NOQUOTA(inode))
@@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
oldflags = fi->i_flags;
- if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ return err;
flags = flags & F2FS_FL_USER_MODIFIABLE;
flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index d174b1f8fd08..99f53cf699c6 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -136,27 +136,36 @@ static struct {
{FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
};
+static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
+{
+ int i;
+ u32 fsflags = 0;
+
+ if (S_ISDIR(inode->i_mode))
+ gfsflags &= ~GFS2_DIF_JDATA;
+ else
+ gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
+
+ for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
+ if (gfsflags & fsflag_gfs2flag[i].gfsflag)
+ fsflags |= fsflag_gfs2flag[i].fsflag;
+ return fsflags;
+}
+
static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
- int i, error;
- u32 gfsflags, fsflags = 0;
+ int error;
+ u32 fsflags;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
error = gfs2_glock_nq(&gh);
if (error)
goto out_uninit;
- gfsflags = ip->i_diskflags;
- if (S_ISDIR(inode->i_mode))
- gfsflags &= ~GFS2_DIF_JDATA;
- else
- gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
- for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
- if (gfsflags & fsflag_gfs2flag[i].gfsflag)
- fsflags |= fsflag_gfs2flag[i].fsflag;
+ fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
if (put_user(fsflags, ptr))
error = -EFAULT;
@@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
* @filp: file pointer
* @reqflags: The flags to set
* @mask: Indicates which flags are valid
+ * @fsflags: The FS_* inode flags passed in
*
*/
-static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
+static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
+ const u32 fsflags)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
@@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
struct buffer_head *bh;
struct gfs2_holder gh;
int error;
- u32 new_flags, flags;
+ u32 new_flags, flags, oldflags;
error = mnt_want_write_file(filp);
if (error)
@@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
if (error)
goto out_drop_write;
+ oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
+ error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
+ if (error)
+ goto out;
+
error = -EACCES;
if (!inode_owner_or_capable(inode))
goto out;
@@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
}
- return do_gfs2_set_flags(filp, gfsflags, mask);
+ return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
}
static int gfs2_getlabel(struct file *filp, char __user *label)
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
index 5e6502ef7415..862a3c9481d7 100644
--- a/fs/hfsplus/ioctl.c
+++ b/fs/hfsplus/ioctl.c
@@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
return 0;
}
-static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+static inline unsigned int hfsplus_getflags(struct inode *inode)
{
- struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags = 0;
@@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
flags |= FS_APPEND_FL;
if (hip->userflags & HFSPLUS_FLG_NODUMP)
flags |= FS_NODUMP_FL;
+ return flags;
+}
+
+static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+{
+ struct inode *inode = file_inode(file);
+ unsigned int flags = hfsplus_getflags(inode);
return put_user(flags, user_flags);
}
@@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags, new_fl = 0;
+ unsigned int oldflags = hfsplus_getflags(inode);
int err = 0;
err = mnt_want_write_file(file);
@@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
inode_lock(inode);
- if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
- inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock_inode;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock_inode;
/* don't silently ignore unsupported ext2 flags */
if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
diff --git a/fs/inode.c b/fs/inode.c
index df6542ec3b88..0ce60b720608 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
return timespec64_trunc(now, inode->i_sb->s_time_gran);
}
EXPORT_SYMBOL(current_time);
+
+/* Generic function to check FS_IOC_SETFLAGS values. */
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
index ba34dae8bd9f..b485c2d7620f 100644
--- a/fs/jfs/ioctl.c
+++ b/fs/jfs/ioctl.c
@@ -98,24 +98,16 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
/* Lock against other parallel changes of flags */
inode_lock(inode);
- oldflags = jfs_inode->mode2;
-
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- if ((oldflags & JFS_IMMUTABLE_FL) ||
- ((flags ^ oldflags) &
- (JFS_APPEND_FL | JFS_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- err = -EPERM;
- goto setflags_out;
- }
+ oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
+ 0);
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & JFS_FL_USER_MODIFIABLE;
- flags |= oldflags & ~JFS_FL_USER_MODIFIABLE;
+ flags |= jfs_inode->mode2 & ~JFS_FL_USER_MODIFIABLE;
jfs_inode->mode2 = flags;
jfs_set_inode_flags(inode);
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 9b96d79eea6c..0632336d2515 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
oldflags = NILFS_I(inode)->i_flags;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
- * relevant capability.
- */
- ret = -EPERM;
- if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
- !capable(CAP_LINUX_IMMUTABLE))
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret)
goto out;
ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 994726ada857..467a2faf0305 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
flags = flags & mask;
flags |= oldflags & ~mask;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- status = -EPERM;
- if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
- (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto bail_unlock;
- }
+ status = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (status)
+ goto bail_unlock;
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
if (IS_ERR(handle)) {
diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
index acbbaf7a0bb2..92bcb1ecd994 100644
--- a/fs/reiserfs/ioctl.c
+++ b/fs/reiserfs/ioctl.c
@@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
err = -EPERM;
goto setflags_out;
}
- if (((flags ^ REISERFS_I(inode)->
- i_attrs) & (REISERFS_IMMUTABLE_FL |
- REISERFS_APPEND_FL))
- && !capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
+ err = vfs_ioc_setflags_check(inode,
+ REISERFS_I(inode)->i_attrs,
+ flags);
+ if (err)
goto setflags_out;
- }
if ((flags & REISERFS_NOTAIL_FL) &&
S_ISREG(inode->i_mode)) {
int result;
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 4f1a397fda69..bdea836fc38b 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
if (err)
return err;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
mutex_lock(&ui->ui_mutex);
oldflags = ubifs2ioctl(ui->flags);
- if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock;
ui->flags = ioctl2ubifs(flags);
ubifs_set_inode_flags(inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7fdfe93e25d..1825d055808c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
}
#endif
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
+
#endif /* _LINUX_FS_H */
^ permalink raw reply related [flat|nested] 66+ messages in thread* [Ocfs2-devel] [PATCH v2 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-12 0:42 ` Darrick J. Wong
0 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-12 0:42 UTC (permalink / raw)
To: matthew.garrett, yuchao0, tytso, shaggy, ard.biesheuvel, josef,
clm, adilger.kernel, jk, jack, dsterba, jaegeuk, viro
Cc: cluster-devel, jfs-discussion, linux-efi, reiserfs-devel,
linux-kernel, linux-f2fs-devel, linux-xfs, linux-nilfs, linux-mtd,
ocfs2-devel, linux-fsdevel, linux-ext4, linux-btrfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
values so that we can standardize the implementations that follow ext4's
flag values.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: fix jfs locking and remove its opencoded flags check
---
fs/btrfs/ioctl.c | 13 +++++--------
fs/efivarfs/file.c | 18 +++++++++++++-----
fs/ext2/ioctl.c | 16 ++++------------
fs/ext4/ioctl.c | 13 +++----------
fs/f2fs/file.c | 7 ++++---
fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
fs/hfsplus/ioctl.c | 21 ++++++++++++---------
fs/inode.c | 17 +++++++++++++++++
fs/jfs/ioctl.c | 22 +++++++---------------
fs/nilfs2/ioctl.c | 9 ++-------
fs/ocfs2/ioctl.c | 13 +++----------
fs/reiserfs/ioctl.c | 10 ++++------
fs/ubifs/ioctl.c | 13 +++----------
include/linux/fs.h | 2 ++
14 files changed, 108 insertions(+), 108 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6dafa857bbb9..f408aa93b0cf 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(inode);
struct btrfs_root *root = binode->root;
struct btrfs_trans_handle *trans;
- unsigned int fsflags;
+ unsigned int fsflags, old_fsflags;
int ret;
const char *comp = NULL;
u32 binode_flags = binode->flags;
@@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
inode_lock(inode);
fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
- if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
- (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- ret = -EPERM;
- goto out_unlock;
- }
- }
+ old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
+ ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
+ if (ret)
+ goto out_unlock;
if (fsflags & FS_SYNC_FL)
binode_flags |= BTRFS_INODE_SYNC;
diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
index 8e568428c88b..f4f6c1bec132 100644
--- a/fs/efivarfs/file.c
+++ b/fs/efivarfs/file.c
@@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
return size;
}
-static int
-efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+static inline unsigned int efivarfs_getflags(struct inode *inode)
{
- struct inode *inode = file->f_mapping->host;
unsigned int i_flags;
unsigned int flags = 0;
i_flags = inode->i_flags;
if (i_flags & S_IMMUTABLE)
flags |= FS_IMMUTABLE_FL;
+ return flags;
+}
+
+static int
+efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+{
+ struct inode *inode = file->f_mapping->host;
+ unsigned int flags = efivarfs_getflags(inode);
if (copy_to_user(arg, &flags, sizeof(flags)))
return -EFAULT;
@@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
struct inode *inode = file->f_mapping->host;
unsigned int flags;
unsigned int i_flags = 0;
+ unsigned int oldflags = efivarfs_getflags(inode);
int error;
if (!inode_owner_or_capable(inode))
@@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
if (flags & ~FS_IMMUTABLE_FL)
return -EOPNOTSUPP;
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ error = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (error)
+ return error;
if (flags & FS_IMMUTABLE_FL)
i_flags |= S_IMMUTABLE;
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 0367c0039e68..88b3b9720023 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- ret = -EPERM;
- goto setflags_out;
- }
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & EXT2_FL_USER_MODIFIABLE;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index e486e49b31ed..5126ee351a84 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
/* The JOURNAL_DATA flag is modifiable only by root */
jflag = flags & EXT4_JOURNAL_DATA_FL;
- /*
- * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto flags_out;
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto flags_out;
/*
* The JOURNAL_DATA flag can only be changed by
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 45b45f37d347..a969d5497e03 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
unsigned int oldflags;
+ int err;
/* Is it quota file? Do not allow user to mess with it */
if (IS_NOQUOTA(inode))
@@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
oldflags = fi->i_flags;
- if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ return err;
flags = flags & F2FS_FL_USER_MODIFIABLE;
flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index d174b1f8fd08..99f53cf699c6 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -136,27 +136,36 @@ static struct {
{FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
};
+static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
+{
+ int i;
+ u32 fsflags = 0;
+
+ if (S_ISDIR(inode->i_mode))
+ gfsflags &= ~GFS2_DIF_JDATA;
+ else
+ gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
+
+ for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
+ if (gfsflags & fsflag_gfs2flag[i].gfsflag)
+ fsflags |= fsflag_gfs2flag[i].fsflag;
+ return fsflags;
+}
+
static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
- int i, error;
- u32 gfsflags, fsflags = 0;
+ int error;
+ u32 fsflags;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
error = gfs2_glock_nq(&gh);
if (error)
goto out_uninit;
- gfsflags = ip->i_diskflags;
- if (S_ISDIR(inode->i_mode))
- gfsflags &= ~GFS2_DIF_JDATA;
- else
- gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
- for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
- if (gfsflags & fsflag_gfs2flag[i].gfsflag)
- fsflags |= fsflag_gfs2flag[i].fsflag;
+ fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
if (put_user(fsflags, ptr))
error = -EFAULT;
@@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
* @filp: file pointer
* @reqflags: The flags to set
* @mask: Indicates which flags are valid
+ * @fsflags: The FS_* inode flags passed in
*
*/
-static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
+static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
+ const u32 fsflags)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
@@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
struct buffer_head *bh;
struct gfs2_holder gh;
int error;
- u32 new_flags, flags;
+ u32 new_flags, flags, oldflags;
error = mnt_want_write_file(filp);
if (error)
@@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
if (error)
goto out_drop_write;
+ oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
+ error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
+ if (error)
+ goto out;
+
error = -EACCES;
if (!inode_owner_or_capable(inode))
goto out;
@@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
}
- return do_gfs2_set_flags(filp, gfsflags, mask);
+ return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
}
static int gfs2_getlabel(struct file *filp, char __user *label)
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
index 5e6502ef7415..862a3c9481d7 100644
--- a/fs/hfsplus/ioctl.c
+++ b/fs/hfsplus/ioctl.c
@@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
return 0;
}
-static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+static inline unsigned int hfsplus_getflags(struct inode *inode)
{
- struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags = 0;
@@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
flags |= FS_APPEND_FL;
if (hip->userflags & HFSPLUS_FLG_NODUMP)
flags |= FS_NODUMP_FL;
+ return flags;
+}
+
+static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+{
+ struct inode *inode = file_inode(file);
+ unsigned int flags = hfsplus_getflags(inode);
return put_user(flags, user_flags);
}
@@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags, new_fl = 0;
+ unsigned int oldflags = hfsplus_getflags(inode);
int err = 0;
err = mnt_want_write_file(file);
@@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
inode_lock(inode);
- if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
- inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock_inode;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock_inode;
/* don't silently ignore unsupported ext2 flags */
if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
diff --git a/fs/inode.c b/fs/inode.c
index df6542ec3b88..0ce60b720608 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
return timespec64_trunc(now, inode->i_sb->s_time_gran);
}
EXPORT_SYMBOL(current_time);
+
+/* Generic function to check FS_IOC_SETFLAGS values. */
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
index ba34dae8bd9f..b485c2d7620f 100644
--- a/fs/jfs/ioctl.c
+++ b/fs/jfs/ioctl.c
@@ -98,24 +98,16 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
/* Lock against other parallel changes of flags */
inode_lock(inode);
- oldflags = jfs_inode->mode2;
-
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- if ((oldflags & JFS_IMMUTABLE_FL) ||
- ((flags ^ oldflags) &
- (JFS_APPEND_FL | JFS_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- err = -EPERM;
- goto setflags_out;
- }
+ oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
+ 0);
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & JFS_FL_USER_MODIFIABLE;
- flags |= oldflags & ~JFS_FL_USER_MODIFIABLE;
+ flags |= jfs_inode->mode2 & ~JFS_FL_USER_MODIFIABLE;
jfs_inode->mode2 = flags;
jfs_set_inode_flags(inode);
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 9b96d79eea6c..0632336d2515 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
oldflags = NILFS_I(inode)->i_flags;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
- * relevant capability.
- */
- ret = -EPERM;
- if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
- !capable(CAP_LINUX_IMMUTABLE))
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret)
goto out;
ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 994726ada857..467a2faf0305 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
flags = flags & mask;
flags |= oldflags & ~mask;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- status = -EPERM;
- if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
- (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto bail_unlock;
- }
+ status = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (status)
+ goto bail_unlock;
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
if (IS_ERR(handle)) {
diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
index acbbaf7a0bb2..92bcb1ecd994 100644
--- a/fs/reiserfs/ioctl.c
+++ b/fs/reiserfs/ioctl.c
@@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
err = -EPERM;
goto setflags_out;
}
- if (((flags ^ REISERFS_I(inode)->
- i_attrs) & (REISERFS_IMMUTABLE_FL |
- REISERFS_APPEND_FL))
- && !capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
+ err = vfs_ioc_setflags_check(inode,
+ REISERFS_I(inode)->i_attrs,
+ flags);
+ if (err)
goto setflags_out;
- }
if ((flags & REISERFS_NOTAIL_FL) &&
S_ISREG(inode->i_mode)) {
int result;
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 4f1a397fda69..bdea836fc38b 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
if (err)
return err;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
mutex_lock(&ui->ui_mutex);
oldflags = ubifs2ioctl(ui->flags);
- if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock;
ui->flags = ioctl2ubifs(flags);
ubifs_set_inode_flags(inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7fdfe93e25d..1825d055808c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
}
#endif
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
+
#endif /* _LINUX_FS_H */
^ permalink raw reply related [flat|nested] 66+ messages in thread* [PATCH v2 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-12 0:42 ` Darrick J. Wong
0 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-12 0:42 UTC (permalink / raw)
To: matthew.garrett, yuchao0, tytso, shaggy, ard.biesheuvel, josef,
clm, adilger.kernel, jk, jack, dsterba, jaegeuk, viro
Cc: linux-xfs, jfs-discussion, linux-efi, linux-kernel,
reiserfs-devel, linux-f2fs-devel, cluster-devel, linux-nilfs,
linux-mtd, linux-btrfs, linux-fsdevel, linux-ext4, ocfs2-devel
From: Darrick J. Wong <darrick.wong@oracle.com>
Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
values so that we can standardize the implementations that follow ext4's
flag values.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: fix jfs locking and remove its opencoded flags check
---
fs/btrfs/ioctl.c | 13 +++++--------
fs/efivarfs/file.c | 18 +++++++++++++-----
fs/ext2/ioctl.c | 16 ++++------------
fs/ext4/ioctl.c | 13 +++----------
fs/f2fs/file.c | 7 ++++---
fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
fs/hfsplus/ioctl.c | 21 ++++++++++++---------
fs/inode.c | 17 +++++++++++++++++
fs/jfs/ioctl.c | 22 +++++++---------------
fs/nilfs2/ioctl.c | 9 ++-------
fs/ocfs2/ioctl.c | 13 +++----------
fs/reiserfs/ioctl.c | 10 ++++------
fs/ubifs/ioctl.c | 13 +++----------
include/linux/fs.h | 2 ++
14 files changed, 108 insertions(+), 108 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6dafa857bbb9..f408aa93b0cf 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(inode);
struct btrfs_root *root = binode->root;
struct btrfs_trans_handle *trans;
- unsigned int fsflags;
+ unsigned int fsflags, old_fsflags;
int ret;
const char *comp = NULL;
u32 binode_flags = binode->flags;
@@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
inode_lock(inode);
fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
- if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
- (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- ret = -EPERM;
- goto out_unlock;
- }
- }
+ old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
+ ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
+ if (ret)
+ goto out_unlock;
if (fsflags & FS_SYNC_FL)
binode_flags |= BTRFS_INODE_SYNC;
diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
index 8e568428c88b..f4f6c1bec132 100644
--- a/fs/efivarfs/file.c
+++ b/fs/efivarfs/file.c
@@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
return size;
}
-static int
-efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+static inline unsigned int efivarfs_getflags(struct inode *inode)
{
- struct inode *inode = file->f_mapping->host;
unsigned int i_flags;
unsigned int flags = 0;
i_flags = inode->i_flags;
if (i_flags & S_IMMUTABLE)
flags |= FS_IMMUTABLE_FL;
+ return flags;
+}
+
+static int
+efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+{
+ struct inode *inode = file->f_mapping->host;
+ unsigned int flags = efivarfs_getflags(inode);
if (copy_to_user(arg, &flags, sizeof(flags)))
return -EFAULT;
@@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
struct inode *inode = file->f_mapping->host;
unsigned int flags;
unsigned int i_flags = 0;
+ unsigned int oldflags = efivarfs_getflags(inode);
int error;
if (!inode_owner_or_capable(inode))
@@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
if (flags & ~FS_IMMUTABLE_FL)
return -EOPNOTSUPP;
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ error = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (error)
+ return error;
if (flags & FS_IMMUTABLE_FL)
i_flags |= S_IMMUTABLE;
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 0367c0039e68..88b3b9720023 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- ret = -EPERM;
- goto setflags_out;
- }
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & EXT2_FL_USER_MODIFIABLE;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index e486e49b31ed..5126ee351a84 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
/* The JOURNAL_DATA flag is modifiable only by root */
jflag = flags & EXT4_JOURNAL_DATA_FL;
- /*
- * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto flags_out;
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto flags_out;
/*
* The JOURNAL_DATA flag can only be changed by
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 45b45f37d347..a969d5497e03 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
unsigned int oldflags;
+ int err;
/* Is it quota file? Do not allow user to mess with it */
if (IS_NOQUOTA(inode))
@@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
oldflags = fi->i_flags;
- if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ return err;
flags = flags & F2FS_FL_USER_MODIFIABLE;
flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index d174b1f8fd08..99f53cf699c6 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -136,27 +136,36 @@ static struct {
{FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
};
+static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
+{
+ int i;
+ u32 fsflags = 0;
+
+ if (S_ISDIR(inode->i_mode))
+ gfsflags &= ~GFS2_DIF_JDATA;
+ else
+ gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
+
+ for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
+ if (gfsflags & fsflag_gfs2flag[i].gfsflag)
+ fsflags |= fsflag_gfs2flag[i].fsflag;
+ return fsflags;
+}
+
static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
- int i, error;
- u32 gfsflags, fsflags = 0;
+ int error;
+ u32 fsflags;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
error = gfs2_glock_nq(&gh);
if (error)
goto out_uninit;
- gfsflags = ip->i_diskflags;
- if (S_ISDIR(inode->i_mode))
- gfsflags &= ~GFS2_DIF_JDATA;
- else
- gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
- for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
- if (gfsflags & fsflag_gfs2flag[i].gfsflag)
- fsflags |= fsflag_gfs2flag[i].fsflag;
+ fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
if (put_user(fsflags, ptr))
error = -EFAULT;
@@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
* @filp: file pointer
* @reqflags: The flags to set
* @mask: Indicates which flags are valid
+ * @fsflags: The FS_* inode flags passed in
*
*/
-static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
+static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
+ const u32 fsflags)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
@@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
struct buffer_head *bh;
struct gfs2_holder gh;
int error;
- u32 new_flags, flags;
+ u32 new_flags, flags, oldflags;
error = mnt_want_write_file(filp);
if (error)
@@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
if (error)
goto out_drop_write;
+ oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
+ error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
+ if (error)
+ goto out;
+
error = -EACCES;
if (!inode_owner_or_capable(inode))
goto out;
@@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
}
- return do_gfs2_set_flags(filp, gfsflags, mask);
+ return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
}
static int gfs2_getlabel(struct file *filp, char __user *label)
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
index 5e6502ef7415..862a3c9481d7 100644
--- a/fs/hfsplus/ioctl.c
+++ b/fs/hfsplus/ioctl.c
@@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
return 0;
}
-static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+static inline unsigned int hfsplus_getflags(struct inode *inode)
{
- struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags = 0;
@@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
flags |= FS_APPEND_FL;
if (hip->userflags & HFSPLUS_FLG_NODUMP)
flags |= FS_NODUMP_FL;
+ return flags;
+}
+
+static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+{
+ struct inode *inode = file_inode(file);
+ unsigned int flags = hfsplus_getflags(inode);
return put_user(flags, user_flags);
}
@@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags, new_fl = 0;
+ unsigned int oldflags = hfsplus_getflags(inode);
int err = 0;
err = mnt_want_write_file(file);
@@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
inode_lock(inode);
- if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
- inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock_inode;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock_inode;
/* don't silently ignore unsupported ext2 flags */
if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
diff --git a/fs/inode.c b/fs/inode.c
index df6542ec3b88..0ce60b720608 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
return timespec64_trunc(now, inode->i_sb->s_time_gran);
}
EXPORT_SYMBOL(current_time);
+
+/* Generic function to check FS_IOC_SETFLAGS values. */
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
index ba34dae8bd9f..b485c2d7620f 100644
--- a/fs/jfs/ioctl.c
+++ b/fs/jfs/ioctl.c
@@ -98,24 +98,16 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
/* Lock against other parallel changes of flags */
inode_lock(inode);
- oldflags = jfs_inode->mode2;
-
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- if ((oldflags & JFS_IMMUTABLE_FL) ||
- ((flags ^ oldflags) &
- (JFS_APPEND_FL | JFS_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- err = -EPERM;
- goto setflags_out;
- }
+ oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
+ 0);
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & JFS_FL_USER_MODIFIABLE;
- flags |= oldflags & ~JFS_FL_USER_MODIFIABLE;
+ flags |= jfs_inode->mode2 & ~JFS_FL_USER_MODIFIABLE;
jfs_inode->mode2 = flags;
jfs_set_inode_flags(inode);
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 9b96d79eea6c..0632336d2515 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
oldflags = NILFS_I(inode)->i_flags;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
- * relevant capability.
- */
- ret = -EPERM;
- if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
- !capable(CAP_LINUX_IMMUTABLE))
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret)
goto out;
ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 994726ada857..467a2faf0305 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
flags = flags & mask;
flags |= oldflags & ~mask;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- status = -EPERM;
- if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
- (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto bail_unlock;
- }
+ status = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (status)
+ goto bail_unlock;
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
if (IS_ERR(handle)) {
diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
index acbbaf7a0bb2..92bcb1ecd994 100644
--- a/fs/reiserfs/ioctl.c
+++ b/fs/reiserfs/ioctl.c
@@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
err = -EPERM;
goto setflags_out;
}
- if (((flags ^ REISERFS_I(inode)->
- i_attrs) & (REISERFS_IMMUTABLE_FL |
- REISERFS_APPEND_FL))
- && !capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
+ err = vfs_ioc_setflags_check(inode,
+ REISERFS_I(inode)->i_attrs,
+ flags);
+ if (err)
goto setflags_out;
- }
if ((flags & REISERFS_NOTAIL_FL) &&
S_ISREG(inode->i_mode)) {
int result;
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 4f1a397fda69..bdea836fc38b 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
if (err)
return err;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
mutex_lock(&ui->ui_mutex);
oldflags = ubifs2ioctl(ui->flags);
- if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock;
ui->flags = ioctl2ubifs(flags);
ubifs_set_inode_flags(inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7fdfe93e25d..1825d055808c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
}
#endif
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
+
#endif /* _LINUX_FS_H */
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 66+ messages in thread* [f2fs-dev] [PATCH v2 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-12 0:42 ` Darrick J. Wong
0 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-12 0:42 UTC (permalink / raw)
To: matthew.garrett, yuchao0, tytso, shaggy, ard.biesheuvel, josef,
clm, adilger.kernel, jk, jack, dsterba, jaegeuk, viro
Cc: linux-xfs, jfs-discussion, linux-efi, linux-kernel,
reiserfs-devel, linux-f2fs-devel, cluster-devel, linux-nilfs,
linux-mtd, linux-btrfs, linux-fsdevel, linux-ext4, ocfs2-devel
From: Darrick J. Wong <darrick.wong@oracle.com>
Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
values so that we can standardize the implementations that follow ext4's
flag values.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: fix jfs locking and remove its opencoded flags check
---
fs/btrfs/ioctl.c | 13 +++++--------
fs/efivarfs/file.c | 18 +++++++++++++-----
fs/ext2/ioctl.c | 16 ++++------------
fs/ext4/ioctl.c | 13 +++----------
fs/f2fs/file.c | 7 ++++---
fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
fs/hfsplus/ioctl.c | 21 ++++++++++++---------
fs/inode.c | 17 +++++++++++++++++
fs/jfs/ioctl.c | 22 +++++++---------------
fs/nilfs2/ioctl.c | 9 ++-------
fs/ocfs2/ioctl.c | 13 +++----------
fs/reiserfs/ioctl.c | 10 ++++------
fs/ubifs/ioctl.c | 13 +++----------
include/linux/fs.h | 2 ++
14 files changed, 108 insertions(+), 108 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6dafa857bbb9..f408aa93b0cf 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(inode);
struct btrfs_root *root = binode->root;
struct btrfs_trans_handle *trans;
- unsigned int fsflags;
+ unsigned int fsflags, old_fsflags;
int ret;
const char *comp = NULL;
u32 binode_flags = binode->flags;
@@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
inode_lock(inode);
fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
- if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
- (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- ret = -EPERM;
- goto out_unlock;
- }
- }
+ old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
+ ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
+ if (ret)
+ goto out_unlock;
if (fsflags & FS_SYNC_FL)
binode_flags |= BTRFS_INODE_SYNC;
diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
index 8e568428c88b..f4f6c1bec132 100644
--- a/fs/efivarfs/file.c
+++ b/fs/efivarfs/file.c
@@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
return size;
}
-static int
-efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+static inline unsigned int efivarfs_getflags(struct inode *inode)
{
- struct inode *inode = file->f_mapping->host;
unsigned int i_flags;
unsigned int flags = 0;
i_flags = inode->i_flags;
if (i_flags & S_IMMUTABLE)
flags |= FS_IMMUTABLE_FL;
+ return flags;
+}
+
+static int
+efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+{
+ struct inode *inode = file->f_mapping->host;
+ unsigned int flags = efivarfs_getflags(inode);
if (copy_to_user(arg, &flags, sizeof(flags)))
return -EFAULT;
@@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
struct inode *inode = file->f_mapping->host;
unsigned int flags;
unsigned int i_flags = 0;
+ unsigned int oldflags = efivarfs_getflags(inode);
int error;
if (!inode_owner_or_capable(inode))
@@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
if (flags & ~FS_IMMUTABLE_FL)
return -EOPNOTSUPP;
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ error = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (error)
+ return error;
if (flags & FS_IMMUTABLE_FL)
i_flags |= S_IMMUTABLE;
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 0367c0039e68..88b3b9720023 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- ret = -EPERM;
- goto setflags_out;
- }
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & EXT2_FL_USER_MODIFIABLE;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index e486e49b31ed..5126ee351a84 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
/* The JOURNAL_DATA flag is modifiable only by root */
jflag = flags & EXT4_JOURNAL_DATA_FL;
- /*
- * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto flags_out;
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto flags_out;
/*
* The JOURNAL_DATA flag can only be changed by
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 45b45f37d347..a969d5497e03 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
unsigned int oldflags;
+ int err;
/* Is it quota file? Do not allow user to mess with it */
if (IS_NOQUOTA(inode))
@@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
oldflags = fi->i_flags;
- if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ return err;
flags = flags & F2FS_FL_USER_MODIFIABLE;
flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index d174b1f8fd08..99f53cf699c6 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -136,27 +136,36 @@ static struct {
{FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
};
+static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
+{
+ int i;
+ u32 fsflags = 0;
+
+ if (S_ISDIR(inode->i_mode))
+ gfsflags &= ~GFS2_DIF_JDATA;
+ else
+ gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
+
+ for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
+ if (gfsflags & fsflag_gfs2flag[i].gfsflag)
+ fsflags |= fsflag_gfs2flag[i].fsflag;
+ return fsflags;
+}
+
static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
- int i, error;
- u32 gfsflags, fsflags = 0;
+ int error;
+ u32 fsflags;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
error = gfs2_glock_nq(&gh);
if (error)
goto out_uninit;
- gfsflags = ip->i_diskflags;
- if (S_ISDIR(inode->i_mode))
- gfsflags &= ~GFS2_DIF_JDATA;
- else
- gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
- for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
- if (gfsflags & fsflag_gfs2flag[i].gfsflag)
- fsflags |= fsflag_gfs2flag[i].fsflag;
+ fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
if (put_user(fsflags, ptr))
error = -EFAULT;
@@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
* @filp: file pointer
* @reqflags: The flags to set
* @mask: Indicates which flags are valid
+ * @fsflags: The FS_* inode flags passed in
*
*/
-static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
+static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
+ const u32 fsflags)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
@@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
struct buffer_head *bh;
struct gfs2_holder gh;
int error;
- u32 new_flags, flags;
+ u32 new_flags, flags, oldflags;
error = mnt_want_write_file(filp);
if (error)
@@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
if (error)
goto out_drop_write;
+ oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
+ error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
+ if (error)
+ goto out;
+
error = -EACCES;
if (!inode_owner_or_capable(inode))
goto out;
@@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
}
- return do_gfs2_set_flags(filp, gfsflags, mask);
+ return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
}
static int gfs2_getlabel(struct file *filp, char __user *label)
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
index 5e6502ef7415..862a3c9481d7 100644
--- a/fs/hfsplus/ioctl.c
+++ b/fs/hfsplus/ioctl.c
@@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
return 0;
}
-static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+static inline unsigned int hfsplus_getflags(struct inode *inode)
{
- struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags = 0;
@@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
flags |= FS_APPEND_FL;
if (hip->userflags & HFSPLUS_FLG_NODUMP)
flags |= FS_NODUMP_FL;
+ return flags;
+}
+
+static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+{
+ struct inode *inode = file_inode(file);
+ unsigned int flags = hfsplus_getflags(inode);
return put_user(flags, user_flags);
}
@@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags, new_fl = 0;
+ unsigned int oldflags = hfsplus_getflags(inode);
int err = 0;
err = mnt_want_write_file(file);
@@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
inode_lock(inode);
- if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
- inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock_inode;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock_inode;
/* don't silently ignore unsupported ext2 flags */
if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
diff --git a/fs/inode.c b/fs/inode.c
index df6542ec3b88..0ce60b720608 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
return timespec64_trunc(now, inode->i_sb->s_time_gran);
}
EXPORT_SYMBOL(current_time);
+
+/* Generic function to check FS_IOC_SETFLAGS values. */
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
index ba34dae8bd9f..b485c2d7620f 100644
--- a/fs/jfs/ioctl.c
+++ b/fs/jfs/ioctl.c
@@ -98,24 +98,16 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
/* Lock against other parallel changes of flags */
inode_lock(inode);
- oldflags = jfs_inode->mode2;
-
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- if ((oldflags & JFS_IMMUTABLE_FL) ||
- ((flags ^ oldflags) &
- (JFS_APPEND_FL | JFS_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- err = -EPERM;
- goto setflags_out;
- }
+ oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
+ 0);
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & JFS_FL_USER_MODIFIABLE;
- flags |= oldflags & ~JFS_FL_USER_MODIFIABLE;
+ flags |= jfs_inode->mode2 & ~JFS_FL_USER_MODIFIABLE;
jfs_inode->mode2 = flags;
jfs_set_inode_flags(inode);
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 9b96d79eea6c..0632336d2515 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
oldflags = NILFS_I(inode)->i_flags;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
- * relevant capability.
- */
- ret = -EPERM;
- if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
- !capable(CAP_LINUX_IMMUTABLE))
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret)
goto out;
ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 994726ada857..467a2faf0305 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
flags = flags & mask;
flags |= oldflags & ~mask;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- status = -EPERM;
- if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
- (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto bail_unlock;
- }
+ status = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (status)
+ goto bail_unlock;
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
if (IS_ERR(handle)) {
diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
index acbbaf7a0bb2..92bcb1ecd994 100644
--- a/fs/reiserfs/ioctl.c
+++ b/fs/reiserfs/ioctl.c
@@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
err = -EPERM;
goto setflags_out;
}
- if (((flags ^ REISERFS_I(inode)->
- i_attrs) & (REISERFS_IMMUTABLE_FL |
- REISERFS_APPEND_FL))
- && !capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
+ err = vfs_ioc_setflags_check(inode,
+ REISERFS_I(inode)->i_attrs,
+ flags);
+ if (err)
goto setflags_out;
- }
if ((flags & REISERFS_NOTAIL_FL) &&
S_ISREG(inode->i_mode)) {
int result;
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 4f1a397fda69..bdea836fc38b 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
if (err)
return err;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
mutex_lock(&ui->ui_mutex);
oldflags = ubifs2ioctl(ui->flags);
- if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock;
ui->flags = ioctl2ubifs(flags);
ubifs_set_inode_flags(inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7fdfe93e25d..1825d055808c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
}
#endif
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
+
#endif /* _LINUX_FS_H */
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply related [flat|nested] 66+ messages in thread* [PATCH v2 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-12 0:42 ` Darrick J. Wong
0 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-12 0:42 UTC (permalink / raw)
To: matthew.garrett, yuchao0, tytso, shaggy, ard.biesheuvel, josef,
clm, adilger.kernel, jk, jack, dsterba, jaegeuk, viro
Cc: linux-xfs, jfs-discussion, linux-efi, linux-kernel,
reiserfs-devel, linux-f2fs-devel, cluster-devel, linux-nilfs,
linux-mtd, linux-btrfs, linux-fsdevel, linux-ext4, ocfs2-devel
From: Darrick J. Wong <darrick.wong@oracle.com>
Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
values so that we can standardize the implementations that follow ext4's
flag values.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: fix jfs locking and remove its opencoded flags check
---
fs/btrfs/ioctl.c | 13 +++++--------
fs/efivarfs/file.c | 18 +++++++++++++-----
fs/ext2/ioctl.c | 16 ++++------------
fs/ext4/ioctl.c | 13 +++----------
fs/f2fs/file.c | 7 ++++---
fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
fs/hfsplus/ioctl.c | 21 ++++++++++++---------
fs/inode.c | 17 +++++++++++++++++
fs/jfs/ioctl.c | 22 +++++++---------------
fs/nilfs2/ioctl.c | 9 ++-------
fs/ocfs2/ioctl.c | 13 +++----------
fs/reiserfs/ioctl.c | 10 ++++------
fs/ubifs/ioctl.c | 13 +++----------
include/linux/fs.h | 2 ++
14 files changed, 108 insertions(+), 108 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6dafa857bbb9..f408aa93b0cf 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(inode);
struct btrfs_root *root = binode->root;
struct btrfs_trans_handle *trans;
- unsigned int fsflags;
+ unsigned int fsflags, old_fsflags;
int ret;
const char *comp = NULL;
u32 binode_flags = binode->flags;
@@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
inode_lock(inode);
fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
- if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
- (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- ret = -EPERM;
- goto out_unlock;
- }
- }
+ old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
+ ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
+ if (ret)
+ goto out_unlock;
if (fsflags & FS_SYNC_FL)
binode_flags |= BTRFS_INODE_SYNC;
diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
index 8e568428c88b..f4f6c1bec132 100644
--- a/fs/efivarfs/file.c
+++ b/fs/efivarfs/file.c
@@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
return size;
}
-static int
-efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+static inline unsigned int efivarfs_getflags(struct inode *inode)
{
- struct inode *inode = file->f_mapping->host;
unsigned int i_flags;
unsigned int flags = 0;
i_flags = inode->i_flags;
if (i_flags & S_IMMUTABLE)
flags |= FS_IMMUTABLE_FL;
+ return flags;
+}
+
+static int
+efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+{
+ struct inode *inode = file->f_mapping->host;
+ unsigned int flags = efivarfs_getflags(inode);
if (copy_to_user(arg, &flags, sizeof(flags)))
return -EFAULT;
@@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
struct inode *inode = file->f_mapping->host;
unsigned int flags;
unsigned int i_flags = 0;
+ unsigned int oldflags = efivarfs_getflags(inode);
int error;
if (!inode_owner_or_capable(inode))
@@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
if (flags & ~FS_IMMUTABLE_FL)
return -EOPNOTSUPP;
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ error = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (error)
+ return error;
if (flags & FS_IMMUTABLE_FL)
i_flags |= S_IMMUTABLE;
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 0367c0039e68..88b3b9720023 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- ret = -EPERM;
- goto setflags_out;
- }
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & EXT2_FL_USER_MODIFIABLE;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index e486e49b31ed..5126ee351a84 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
/* The JOURNAL_DATA flag is modifiable only by root */
jflag = flags & EXT4_JOURNAL_DATA_FL;
- /*
- * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto flags_out;
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto flags_out;
/*
* The JOURNAL_DATA flag can only be changed by
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 45b45f37d347..a969d5497e03 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
unsigned int oldflags;
+ int err;
/* Is it quota file? Do not allow user to mess with it */
if (IS_NOQUOTA(inode))
@@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
oldflags = fi->i_flags;
- if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ return err;
flags = flags & F2FS_FL_USER_MODIFIABLE;
flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index d174b1f8fd08..99f53cf699c6 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -136,27 +136,36 @@ static struct {
{FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
};
+static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
+{
+ int i;
+ u32 fsflags = 0;
+
+ if (S_ISDIR(inode->i_mode))
+ gfsflags &= ~GFS2_DIF_JDATA;
+ else
+ gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
+
+ for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
+ if (gfsflags & fsflag_gfs2flag[i].gfsflag)
+ fsflags |= fsflag_gfs2flag[i].fsflag;
+ return fsflags;
+}
+
static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
- int i, error;
- u32 gfsflags, fsflags = 0;
+ int error;
+ u32 fsflags;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
error = gfs2_glock_nq(&gh);
if (error)
goto out_uninit;
- gfsflags = ip->i_diskflags;
- if (S_ISDIR(inode->i_mode))
- gfsflags &= ~GFS2_DIF_JDATA;
- else
- gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
- for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
- if (gfsflags & fsflag_gfs2flag[i].gfsflag)
- fsflags |= fsflag_gfs2flag[i].fsflag;
+ fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
if (put_user(fsflags, ptr))
error = -EFAULT;
@@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
* @filp: file pointer
* @reqflags: The flags to set
* @mask: Indicates which flags are valid
+ * @fsflags: The FS_* inode flags passed in
*
*/
-static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
+static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
+ const u32 fsflags)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
@@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
struct buffer_head *bh;
struct gfs2_holder gh;
int error;
- u32 new_flags, flags;
+ u32 new_flags, flags, oldflags;
error = mnt_want_write_file(filp);
if (error)
@@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
if (error)
goto out_drop_write;
+ oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
+ error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
+ if (error)
+ goto out;
+
error = -EACCES;
if (!inode_owner_or_capable(inode))
goto out;
@@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
}
- return do_gfs2_set_flags(filp, gfsflags, mask);
+ return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
}
static int gfs2_getlabel(struct file *filp, char __user *label)
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
index 5e6502ef7415..862a3c9481d7 100644
--- a/fs/hfsplus/ioctl.c
+++ b/fs/hfsplus/ioctl.c
@@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
return 0;
}
-static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+static inline unsigned int hfsplus_getflags(struct inode *inode)
{
- struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags = 0;
@@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
flags |= FS_APPEND_FL;
if (hip->userflags & HFSPLUS_FLG_NODUMP)
flags |= FS_NODUMP_FL;
+ return flags;
+}
+
+static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+{
+ struct inode *inode = file_inode(file);
+ unsigned int flags = hfsplus_getflags(inode);
return put_user(flags, user_flags);
}
@@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags, new_fl = 0;
+ unsigned int oldflags = hfsplus_getflags(inode);
int err = 0;
err = mnt_want_write_file(file);
@@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
inode_lock(inode);
- if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
- inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock_inode;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock_inode;
/* don't silently ignore unsupported ext2 flags */
if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
diff --git a/fs/inode.c b/fs/inode.c
index df6542ec3b88..0ce60b720608 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
return timespec64_trunc(now, inode->i_sb->s_time_gran);
}
EXPORT_SYMBOL(current_time);
+
+/* Generic function to check FS_IOC_SETFLAGS values. */
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
index ba34dae8bd9f..b485c2d7620f 100644
--- a/fs/jfs/ioctl.c
+++ b/fs/jfs/ioctl.c
@@ -98,24 +98,16 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
/* Lock against other parallel changes of flags */
inode_lock(inode);
- oldflags = jfs_inode->mode2;
-
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- if ((oldflags & JFS_IMMUTABLE_FL) ||
- ((flags ^ oldflags) &
- (JFS_APPEND_FL | JFS_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- err = -EPERM;
- goto setflags_out;
- }
+ oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
+ 0);
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & JFS_FL_USER_MODIFIABLE;
- flags |= oldflags & ~JFS_FL_USER_MODIFIABLE;
+ flags |= jfs_inode->mode2 & ~JFS_FL_USER_MODIFIABLE;
jfs_inode->mode2 = flags;
jfs_set_inode_flags(inode);
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 9b96d79eea6c..0632336d2515 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
oldflags = NILFS_I(inode)->i_flags;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
- * relevant capability.
- */
- ret = -EPERM;
- if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
- !capable(CAP_LINUX_IMMUTABLE))
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret)
goto out;
ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 994726ada857..467a2faf0305 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
flags = flags & mask;
flags |= oldflags & ~mask;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- status = -EPERM;
- if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
- (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto bail_unlock;
- }
+ status = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (status)
+ goto bail_unlock;
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
if (IS_ERR(handle)) {
diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
index acbbaf7a0bb2..92bcb1ecd994 100644
--- a/fs/reiserfs/ioctl.c
+++ b/fs/reiserfs/ioctl.c
@@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
err = -EPERM;
goto setflags_out;
}
- if (((flags ^ REISERFS_I(inode)->
- i_attrs) & (REISERFS_IMMUTABLE_FL |
- REISERFS_APPEND_FL))
- && !capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
+ err = vfs_ioc_setflags_check(inode,
+ REISERFS_I(inode)->i_attrs,
+ flags);
+ if (err)
goto setflags_out;
- }
if ((flags & REISERFS_NOTAIL_FL) &&
S_ISREG(inode->i_mode)) {
int result;
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 4f1a397fda69..bdea836fc38b 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
if (err)
return err;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
mutex_lock(&ui->ui_mutex);
oldflags = ubifs2ioctl(ui->flags);
- if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock;
ui->flags = ioctl2ubifs(flags);
ubifs_set_inode_flags(inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7fdfe93e25d..1825d055808c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
}
#endif
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
+
#endif /* _LINUX_FS_H */
^ permalink raw reply related [flat|nested] 66+ messages in thread* [PATCH v2 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-12 0:42 ` Darrick J. Wong
0 siblings, 0 replies; 66+ messages in thread
From: Darrick J. Wong @ 2019-06-12 0:42 UTC (permalink / raw)
To: matthew.garrett, yuchao0, tytso, shaggy, ard.biesheuvel, josef,
clm, adilger.kernel, jk, jack, dsterba, jaegeuk, viro
Cc: cluster-devel, jfs-discussion, linux-efi, reiserfs-devel,
linux-kernel, linux-f2fs-devel, linux-xfs, linux-nilfs, linux-mtd,
ocfs2-devel, linux-fsdevel, linux-ext4, linux-btrfs
From: Darrick J. Wong <darrick.wong@oracle.com>
Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
values so that we can standardize the implementations that follow ext4's
flag values.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
---
v2: fix jfs locking and remove its opencoded flags check
---
fs/btrfs/ioctl.c | 13 +++++--------
fs/efivarfs/file.c | 18 +++++++++++++-----
fs/ext2/ioctl.c | 16 ++++------------
fs/ext4/ioctl.c | 13 +++----------
fs/f2fs/file.c | 7 ++++---
fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
fs/hfsplus/ioctl.c | 21 ++++++++++++---------
fs/inode.c | 17 +++++++++++++++++
fs/jfs/ioctl.c | 22 +++++++---------------
fs/nilfs2/ioctl.c | 9 ++-------
fs/ocfs2/ioctl.c | 13 +++----------
fs/reiserfs/ioctl.c | 10 ++++------
fs/ubifs/ioctl.c | 13 +++----------
include/linux/fs.h | 2 ++
14 files changed, 108 insertions(+), 108 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 6dafa857bbb9..f408aa93b0cf 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(inode);
struct btrfs_root *root = binode->root;
struct btrfs_trans_handle *trans;
- unsigned int fsflags;
+ unsigned int fsflags, old_fsflags;
int ret;
const char *comp = NULL;
u32 binode_flags = binode->flags;
@@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
inode_lock(inode);
fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
- if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
- (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- ret = -EPERM;
- goto out_unlock;
- }
- }
+ old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
+ ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
+ if (ret)
+ goto out_unlock;
if (fsflags & FS_SYNC_FL)
binode_flags |= BTRFS_INODE_SYNC;
diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
index 8e568428c88b..f4f6c1bec132 100644
--- a/fs/efivarfs/file.c
+++ b/fs/efivarfs/file.c
@@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
return size;
}
-static int
-efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+static inline unsigned int efivarfs_getflags(struct inode *inode)
{
- struct inode *inode = file->f_mapping->host;
unsigned int i_flags;
unsigned int flags = 0;
i_flags = inode->i_flags;
if (i_flags & S_IMMUTABLE)
flags |= FS_IMMUTABLE_FL;
+ return flags;
+}
+
+static int
+efivarfs_ioc_getxflags(struct file *file, void __user *arg)
+{
+ struct inode *inode = file->f_mapping->host;
+ unsigned int flags = efivarfs_getflags(inode);
if (copy_to_user(arg, &flags, sizeof(flags)))
return -EFAULT;
@@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
struct inode *inode = file->f_mapping->host;
unsigned int flags;
unsigned int i_flags = 0;
+ unsigned int oldflags = efivarfs_getflags(inode);
int error;
if (!inode_owner_or_capable(inode))
@@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
if (flags & ~FS_IMMUTABLE_FL)
return -EOPNOTSUPP;
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ error = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (error)
+ return error;
if (flags & FS_IMMUTABLE_FL)
i_flags |= S_IMMUTABLE;
diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
index 0367c0039e68..88b3b9720023 100644
--- a/fs/ext2/ioctl.c
+++ b/fs/ext2/ioctl.c
@@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- ret = -EPERM;
- goto setflags_out;
- }
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & EXT2_FL_USER_MODIFIABLE;
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index e486e49b31ed..5126ee351a84 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
/* The JOURNAL_DATA flag is modifiable only by root */
jflag = flags & EXT4_JOURNAL_DATA_FL;
- /*
- * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto flags_out;
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto flags_out;
/*
* The JOURNAL_DATA flag can only be changed by
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 45b45f37d347..a969d5497e03 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
{
struct f2fs_inode_info *fi = F2FS_I(inode);
unsigned int oldflags;
+ int err;
/* Is it quota file? Do not allow user to mess with it */
if (IS_NOQUOTA(inode))
@@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
oldflags = fi->i_flags;
- if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
- if (!capable(CAP_LINUX_IMMUTABLE))
- return -EPERM;
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ return err;
flags = flags & F2FS_FL_USER_MODIFIABLE;
flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index d174b1f8fd08..99f53cf699c6 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -136,27 +136,36 @@ static struct {
{FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
};
+static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
+{
+ int i;
+ u32 fsflags = 0;
+
+ if (S_ISDIR(inode->i_mode))
+ gfsflags &= ~GFS2_DIF_JDATA;
+ else
+ gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
+
+ for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
+ if (gfsflags & fsflag_gfs2flag[i].gfsflag)
+ fsflags |= fsflag_gfs2flag[i].fsflag;
+ return fsflags;
+}
+
static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh;
- int i, error;
- u32 gfsflags, fsflags = 0;
+ int error;
+ u32 fsflags;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
error = gfs2_glock_nq(&gh);
if (error)
goto out_uninit;
- gfsflags = ip->i_diskflags;
- if (S_ISDIR(inode->i_mode))
- gfsflags &= ~GFS2_DIF_JDATA;
- else
- gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
- for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
- if (gfsflags & fsflag_gfs2flag[i].gfsflag)
- fsflags |= fsflag_gfs2flag[i].fsflag;
+ fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
if (put_user(fsflags, ptr))
error = -EFAULT;
@@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
* @filp: file pointer
* @reqflags: The flags to set
* @mask: Indicates which flags are valid
+ * @fsflags: The FS_* inode flags passed in
*
*/
-static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
+static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
+ const u32 fsflags)
{
struct inode *inode = file_inode(filp);
struct gfs2_inode *ip = GFS2_I(inode);
@@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
struct buffer_head *bh;
struct gfs2_holder gh;
int error;
- u32 new_flags, flags;
+ u32 new_flags, flags, oldflags;
error = mnt_want_write_file(filp);
if (error)
@@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
if (error)
goto out_drop_write;
+ oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
+ error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
+ if (error)
+ goto out;
+
error = -EACCES;
if (!inode_owner_or_capable(inode))
goto out;
@@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
}
- return do_gfs2_set_flags(filp, gfsflags, mask);
+ return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
}
static int gfs2_getlabel(struct file *filp, char __user *label)
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
index 5e6502ef7415..862a3c9481d7 100644
--- a/fs/hfsplus/ioctl.c
+++ b/fs/hfsplus/ioctl.c
@@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
return 0;
}
-static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+static inline unsigned int hfsplus_getflags(struct inode *inode)
{
- struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags = 0;
@@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
flags |= FS_APPEND_FL;
if (hip->userflags & HFSPLUS_FLG_NODUMP)
flags |= FS_NODUMP_FL;
+ return flags;
+}
+
+static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
+{
+ struct inode *inode = file_inode(file);
+ unsigned int flags = hfsplus_getflags(inode);
return put_user(flags, user_flags);
}
@@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
struct inode *inode = file_inode(file);
struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
unsigned int flags, new_fl = 0;
+ unsigned int oldflags = hfsplus_getflags(inode);
int err = 0;
err = mnt_want_write_file(file);
@@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
inode_lock(inode);
- if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
- inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock_inode;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock_inode;
/* don't silently ignore unsupported ext2 flags */
if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
diff --git a/fs/inode.c b/fs/inode.c
index df6542ec3b88..0ce60b720608 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
return timespec64_trunc(now, inode->i_sb->s_time_gran);
}
EXPORT_SYMBOL(current_time);
+
+/* Generic function to check FS_IOC_SETFLAGS values. */
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
index ba34dae8bd9f..b485c2d7620f 100644
--- a/fs/jfs/ioctl.c
+++ b/fs/jfs/ioctl.c
@@ -98,24 +98,16 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
/* Lock against other parallel changes of flags */
inode_lock(inode);
- oldflags = jfs_inode->mode2;
-
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- if ((oldflags & JFS_IMMUTABLE_FL) ||
- ((flags ^ oldflags) &
- (JFS_APPEND_FL | JFS_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- inode_unlock(inode);
- err = -EPERM;
- goto setflags_out;
- }
+ oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
+ 0);
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err) {
+ inode_unlock(inode);
+ goto setflags_out;
}
flags = flags & JFS_FL_USER_MODIFIABLE;
- flags |= oldflags & ~JFS_FL_USER_MODIFIABLE;
+ flags |= jfs_inode->mode2 & ~JFS_FL_USER_MODIFIABLE;
jfs_inode->mode2 = flags;
jfs_set_inode_flags(inode);
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 9b96d79eea6c..0632336d2515 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
oldflags = NILFS_I(inode)->i_flags;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
- * relevant capability.
- */
- ret = -EPERM;
- if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
- !capable(CAP_LINUX_IMMUTABLE))
+ ret = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (ret)
goto out;
ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
index 994726ada857..467a2faf0305 100644
--- a/fs/ocfs2/ioctl.c
+++ b/fs/ocfs2/ioctl.c
@@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
flags = flags & mask;
flags |= oldflags & ~mask;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
- status = -EPERM;
- if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
- (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
- if (!capable(CAP_LINUX_IMMUTABLE))
- goto bail_unlock;
- }
+ status = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (status)
+ goto bail_unlock;
handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
if (IS_ERR(handle)) {
diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
index acbbaf7a0bb2..92bcb1ecd994 100644
--- a/fs/reiserfs/ioctl.c
+++ b/fs/reiserfs/ioctl.c
@@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
err = -EPERM;
goto setflags_out;
}
- if (((flags ^ REISERFS_I(inode)->
- i_attrs) & (REISERFS_IMMUTABLE_FL |
- REISERFS_APPEND_FL))
- && !capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
+ err = vfs_ioc_setflags_check(inode,
+ REISERFS_I(inode)->i_attrs,
+ flags);
+ if (err)
goto setflags_out;
- }
if ((flags & REISERFS_NOTAIL_FL) &&
S_ISREG(inode->i_mode)) {
int result;
diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
index 4f1a397fda69..bdea836fc38b 100644
--- a/fs/ubifs/ioctl.c
+++ b/fs/ubifs/ioctl.c
@@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
if (err)
return err;
- /*
- * The IMMUTABLE and APPEND_ONLY flags can only be changed by
- * the relevant capability.
- */
mutex_lock(&ui->ui_mutex);
oldflags = ubifs2ioctl(ui->flags);
- if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
- if (!capable(CAP_LINUX_IMMUTABLE)) {
- err = -EPERM;
- goto out_unlock;
- }
- }
+ err = vfs_ioc_setflags_check(inode, oldflags, flags);
+ if (err)
+ goto out_unlock;
ui->flags = ioctl2ubifs(flags);
ubifs_set_inode_flags(inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f7fdfe93e25d..1825d055808c 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
}
#endif
+int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
+
#endif /* _LINUX_FS_H */
^ permalink raw reply related [flat|nested] 66+ messages in thread* [Cluster-devel] [PATCH v2 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
2019-06-12 0:42 ` Darrick J. Wong
` (2 preceding siblings ...)
(?)
@ 2019-06-20 13:34 ` Jan Kara
-1 siblings, 0 replies; 66+ messages in thread
From: Jan Kara @ 2019-06-20 13:34 UTC (permalink / raw)
To: cluster-devel.redhat.com
On Tue 11-06-19 17:42:58, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> values so that we can standardize the implementations that follow ext4's
> flag values.
>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
The patch looks good to me. You can add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> v2: fix jfs locking and remove its opencoded flags check
> ---
> fs/btrfs/ioctl.c | 13 +++++--------
> fs/efivarfs/file.c | 18 +++++++++++++-----
> fs/ext2/ioctl.c | 16 ++++------------
> fs/ext4/ioctl.c | 13 +++----------
> fs/f2fs/file.c | 7 ++++---
> fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
> fs/hfsplus/ioctl.c | 21 ++++++++++++---------
> fs/inode.c | 17 +++++++++++++++++
> fs/jfs/ioctl.c | 22 +++++++---------------
> fs/nilfs2/ioctl.c | 9 ++-------
> fs/ocfs2/ioctl.c | 13 +++----------
> fs/reiserfs/ioctl.c | 10 ++++------
> fs/ubifs/ioctl.c | 13 +++----------
> include/linux/fs.h | 2 ++
> 14 files changed, 108 insertions(+), 108 deletions(-)
>
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index 6dafa857bbb9..f408aa93b0cf 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
> struct btrfs_inode *binode = BTRFS_I(inode);
> struct btrfs_root *root = binode->root;
> struct btrfs_trans_handle *trans;
> - unsigned int fsflags;
> + unsigned int fsflags, old_fsflags;
> int ret;
> const char *comp = NULL;
> u32 binode_flags = binode->flags;
> @@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
> inode_lock(inode);
>
> fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
> - if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
> - (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - ret = -EPERM;
> - goto out_unlock;
> - }
> - }
> + old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
> + ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
> + if (ret)
> + goto out_unlock;
>
> if (fsflags & FS_SYNC_FL)
> binode_flags |= BTRFS_INODE_SYNC;
> diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
> index 8e568428c88b..f4f6c1bec132 100644
> --- a/fs/efivarfs/file.c
> +++ b/fs/efivarfs/file.c
> @@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
> return size;
> }
>
> -static int
> -efivarfs_ioc_getxflags(struct file *file, void __user *arg)
> +static inline unsigned int efivarfs_getflags(struct inode *inode)
> {
> - struct inode *inode = file->f_mapping->host;
> unsigned int i_flags;
> unsigned int flags = 0;
>
> i_flags = inode->i_flags;
> if (i_flags & S_IMMUTABLE)
> flags |= FS_IMMUTABLE_FL;
> + return flags;
> +}
> +
> +static int
> +efivarfs_ioc_getxflags(struct file *file, void __user *arg)
> +{
> + struct inode *inode = file->f_mapping->host;
> + unsigned int flags = efivarfs_getflags(inode);
>
> if (copy_to_user(arg, &flags, sizeof(flags)))
> return -EFAULT;
> @@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
> struct inode *inode = file->f_mapping->host;
> unsigned int flags;
> unsigned int i_flags = 0;
> + unsigned int oldflags = efivarfs_getflags(inode);
> int error;
>
> if (!inode_owner_or_capable(inode))
> @@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
> if (flags & ~FS_IMMUTABLE_FL)
> return -EOPNOTSUPP;
>
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - return -EPERM;
> + error = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (error)
> + return error;
>
> if (flags & FS_IMMUTABLE_FL)
> i_flags |= S_IMMUTABLE;
> diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
> index 0367c0039e68..88b3b9720023 100644
> --- a/fs/ext2/ioctl.c
> +++ b/fs/ext2/ioctl.c
> @@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> }
> oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - inode_unlock(inode);
> - ret = -EPERM;
> - goto setflags_out;
> - }
> + ret = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (ret) {
> + inode_unlock(inode);
> + goto setflags_out;
> }
>
> flags = flags & EXT2_FL_USER_MODIFIABLE;
> diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
> index e486e49b31ed..5126ee351a84 100644
> --- a/fs/ext4/ioctl.c
> +++ b/fs/ext4/ioctl.c
> @@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
> /* The JOURNAL_DATA flag is modifiable only by root */
> jflag = flags & EXT4_JOURNAL_DATA_FL;
>
> - /*
> - * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - goto flags_out;
> - }
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto flags_out;
>
> /*
> * The JOURNAL_DATA flag can only be changed by
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 45b45f37d347..a969d5497e03 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
> {
> struct f2fs_inode_info *fi = F2FS_I(inode);
> unsigned int oldflags;
> + int err;
>
> /* Is it quota file? Do not allow user to mess with it */
> if (IS_NOQUOTA(inode))
> @@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
>
> oldflags = fi->i_flags;
>
> - if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - return -EPERM;
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + return err;
>
> flags = flags & F2FS_FL_USER_MODIFIABLE;
> flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
> diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
> index d174b1f8fd08..99f53cf699c6 100644
> --- a/fs/gfs2/file.c
> +++ b/fs/gfs2/file.c
> @@ -136,27 +136,36 @@ static struct {
> {FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
> };
>
> +static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
> +{
> + int i;
> + u32 fsflags = 0;
> +
> + if (S_ISDIR(inode->i_mode))
> + gfsflags &= ~GFS2_DIF_JDATA;
> + else
> + gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
> +
> + for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
> + if (gfsflags & fsflag_gfs2flag[i].gfsflag)
> + fsflags |= fsflag_gfs2flag[i].fsflag;
> + return fsflags;
> +}
> +
> static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
> {
> struct inode *inode = file_inode(filp);
> struct gfs2_inode *ip = GFS2_I(inode);
> struct gfs2_holder gh;
> - int i, error;
> - u32 gfsflags, fsflags = 0;
> + int error;
> + u32 fsflags;
>
> gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
> error = gfs2_glock_nq(&gh);
> if (error)
> goto out_uninit;
>
> - gfsflags = ip->i_diskflags;
> - if (S_ISDIR(inode->i_mode))
> - gfsflags &= ~GFS2_DIF_JDATA;
> - else
> - gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
> - for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
> - if (gfsflags & fsflag_gfs2flag[i].gfsflag)
> - fsflags |= fsflag_gfs2flag[i].fsflag;
> + fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
>
> if (put_user(fsflags, ptr))
> error = -EFAULT;
> @@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
> * @filp: file pointer
> * @reqflags: The flags to set
> * @mask: Indicates which flags are valid
> + * @fsflags: The FS_* inode flags passed in
> *
> */
> -static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
> +static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
> + const u32 fsflags)
> {
> struct inode *inode = file_inode(filp);
> struct gfs2_inode *ip = GFS2_I(inode);
> @@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
> struct buffer_head *bh;
> struct gfs2_holder gh;
> int error;
> - u32 new_flags, flags;
> + u32 new_flags, flags, oldflags;
>
> error = mnt_want_write_file(filp);
> if (error)
> @@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
> if (error)
> goto out_drop_write;
>
> + oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
> + error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
> + if (error)
> + goto out;
> +
> error = -EACCES;
> if (!inode_owner_or_capable(inode))
> goto out;
> @@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
> mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
> }
>
> - return do_gfs2_set_flags(filp, gfsflags, mask);
> + return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
> }
>
> static int gfs2_getlabel(struct file *filp, char __user *label)
> diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
> index 5e6502ef7415..862a3c9481d7 100644
> --- a/fs/hfsplus/ioctl.c
> +++ b/fs/hfsplus/ioctl.c
> @@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
> return 0;
> }
>
> -static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
> +static inline unsigned int hfsplus_getflags(struct inode *inode)
> {
> - struct inode *inode = file_inode(file);
> struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
> unsigned int flags = 0;
>
> @@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
> flags |= FS_APPEND_FL;
> if (hip->userflags & HFSPLUS_FLG_NODUMP)
> flags |= FS_NODUMP_FL;
> + return flags;
> +}
> +
> +static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
> +{
> + struct inode *inode = file_inode(file);
> + unsigned int flags = hfsplus_getflags(inode);
>
> return put_user(flags, user_flags);
> }
> @@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
> struct inode *inode = file_inode(file);
> struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
> unsigned int flags, new_fl = 0;
> + unsigned int oldflags = hfsplus_getflags(inode);
> int err = 0;
>
> err = mnt_want_write_file(file);
> @@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
>
> inode_lock(inode);
>
> - if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
> - inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - err = -EPERM;
> - goto out_unlock_inode;
> - }
> - }
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto out_unlock_inode;
>
> /* don't silently ignore unsupported ext2 flags */
> if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
> diff --git a/fs/inode.c b/fs/inode.c
> index df6542ec3b88..0ce60b720608 100644
> --- a/fs/inode.c
> +++ b/fs/inode.c
> @@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
> return timespec64_trunc(now, inode->i_sb->s_time_gran);
> }
> EXPORT_SYMBOL(current_time);
> +
> +/* Generic function to check FS_IOC_SETFLAGS values. */
> +int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
> diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> index ba34dae8bd9f..b485c2d7620f 100644
> --- a/fs/jfs/ioctl.c
> +++ b/fs/jfs/ioctl.c
> @@ -98,24 +98,16 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> /* Lock against other parallel changes of flags */
> inode_lock(inode);
>
> - oldflags = jfs_inode->mode2;
> -
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> - if ((oldflags & JFS_IMMUTABLE_FL) ||
> - ((flags ^ oldflags) &
> - (JFS_APPEND_FL | JFS_IMMUTABLE_FL))) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - inode_unlock(inode);
> - err = -EPERM;
> - goto setflags_out;
> - }
> + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> + 0);
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err) {
> + inode_unlock(inode);
> + goto setflags_out;
> }
>
> flags = flags & JFS_FL_USER_MODIFIABLE;
> - flags |= oldflags & ~JFS_FL_USER_MODIFIABLE;
> + flags |= jfs_inode->mode2 & ~JFS_FL_USER_MODIFIABLE;
> jfs_inode->mode2 = flags;
>
> jfs_set_inode_flags(inode);
> diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
> index 9b96d79eea6c..0632336d2515 100644
> --- a/fs/nilfs2/ioctl.c
> +++ b/fs/nilfs2/ioctl.c
> @@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
>
> oldflags = NILFS_I(inode)->i_flags;
>
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
> - * relevant capability.
> - */
> - ret = -EPERM;
> - if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
> - !capable(CAP_LINUX_IMMUTABLE))
> + ret = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (ret)
> goto out;
>
> ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
> diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
> index 994726ada857..467a2faf0305 100644
> --- a/fs/ocfs2/ioctl.c
> +++ b/fs/ocfs2/ioctl.c
> @@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
> flags = flags & mask;
> flags |= oldflags & ~mask;
>
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> - status = -EPERM;
> - if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
> - (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - goto bail_unlock;
> - }
> + status = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (status)
> + goto bail_unlock;
>
> handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
> if (IS_ERR(handle)) {
> diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
> index acbbaf7a0bb2..92bcb1ecd994 100644
> --- a/fs/reiserfs/ioctl.c
> +++ b/fs/reiserfs/ioctl.c
> @@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> err = -EPERM;
> goto setflags_out;
> }
> - if (((flags ^ REISERFS_I(inode)->
> - i_attrs) & (REISERFS_IMMUTABLE_FL |
> - REISERFS_APPEND_FL))
> - && !capable(CAP_LINUX_IMMUTABLE)) {
> - err = -EPERM;
> + err = vfs_ioc_setflags_check(inode,
> + REISERFS_I(inode)->i_attrs,
> + flags);
> + if (err)
> goto setflags_out;
> - }
> if ((flags & REISERFS_NOTAIL_FL) &&
> S_ISREG(inode->i_mode)) {
> int result;
> diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
> index 4f1a397fda69..bdea836fc38b 100644
> --- a/fs/ubifs/ioctl.c
> +++ b/fs/ubifs/ioctl.c
> @@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
> if (err)
> return err;
>
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> mutex_lock(&ui->ui_mutex);
> oldflags = ubifs2ioctl(ui->flags);
> - if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - err = -EPERM;
> - goto out_unlock;
> - }
> - }
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto out_unlock;
>
> ui->flags = ioctl2ubifs(flags);
> ubifs_set_inode_flags(inode);
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index f7fdfe93e25d..1825d055808c 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
> }
> #endif
>
> +int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
> +
> #endif /* _LINUX_FS_H */
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 66+ messages in thread* [Ocfs2-devel] [PATCH v2 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-20 13:34 ` Jan Kara
0 siblings, 0 replies; 66+ messages in thread
From: Jan Kara @ 2019-06-20 13:34 UTC (permalink / raw)
To: Darrick J. Wong
Cc: matthew.garrett, yuchao0, tytso, shaggy, ard.biesheuvel, josef,
clm, adilger.kernel, jk, jack, dsterba, jaegeuk, viro,
cluster-devel, jfs-discussion, linux-efi, reiserfs-devel,
linux-kernel, linux-f2fs-devel, linux-xfs, linux-nilfs, linux-mtd,
ocfs2-devel, linux-fsdevel, linux-ext4, linux-btrfs
On Tue 11-06-19 17:42:58, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> values so that we can standardize the implementations that follow ext4's
> flag values.
>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
The patch looks good to me. You can add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> v2: fix jfs locking and remove its opencoded flags check
> ---
> fs/btrfs/ioctl.c | 13 +++++--------
> fs/efivarfs/file.c | 18 +++++++++++++-----
> fs/ext2/ioctl.c | 16 ++++------------
> fs/ext4/ioctl.c | 13 +++----------
> fs/f2fs/file.c | 7 ++++---
> fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
> fs/hfsplus/ioctl.c | 21 ++++++++++++---------
> fs/inode.c | 17 +++++++++++++++++
> fs/jfs/ioctl.c | 22 +++++++---------------
> fs/nilfs2/ioctl.c | 9 ++-------
> fs/ocfs2/ioctl.c | 13 +++----------
> fs/reiserfs/ioctl.c | 10 ++++------
> fs/ubifs/ioctl.c | 13 +++----------
> include/linux/fs.h | 2 ++
> 14 files changed, 108 insertions(+), 108 deletions(-)
>
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index 6dafa857bbb9..f408aa93b0cf 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
> struct btrfs_inode *binode = BTRFS_I(inode);
> struct btrfs_root *root = binode->root;
> struct btrfs_trans_handle *trans;
> - unsigned int fsflags;
> + unsigned int fsflags, old_fsflags;
> int ret;
> const char *comp = NULL;
> u32 binode_flags = binode->flags;
> @@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
> inode_lock(inode);
>
> fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
> - if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
> - (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - ret = -EPERM;
> - goto out_unlock;
> - }
> - }
> + old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
> + ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
> + if (ret)
> + goto out_unlock;
>
> if (fsflags & FS_SYNC_FL)
> binode_flags |= BTRFS_INODE_SYNC;
> diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
> index 8e568428c88b..f4f6c1bec132 100644
> --- a/fs/efivarfs/file.c
> +++ b/fs/efivarfs/file.c
> @@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
> return size;
> }
>
> -static int
> -efivarfs_ioc_getxflags(struct file *file, void __user *arg)
> +static inline unsigned int efivarfs_getflags(struct inode *inode)
> {
> - struct inode *inode = file->f_mapping->host;
> unsigned int i_flags;
> unsigned int flags = 0;
>
> i_flags = inode->i_flags;
> if (i_flags & S_IMMUTABLE)
> flags |= FS_IMMUTABLE_FL;
> + return flags;
> +}
> +
> +static int
> +efivarfs_ioc_getxflags(struct file *file, void __user *arg)
> +{
> + struct inode *inode = file->f_mapping->host;
> + unsigned int flags = efivarfs_getflags(inode);
>
> if (copy_to_user(arg, &flags, sizeof(flags)))
> return -EFAULT;
> @@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
> struct inode *inode = file->f_mapping->host;
> unsigned int flags;
> unsigned int i_flags = 0;
> + unsigned int oldflags = efivarfs_getflags(inode);
> int error;
>
> if (!inode_owner_or_capable(inode))
> @@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
> if (flags & ~FS_IMMUTABLE_FL)
> return -EOPNOTSUPP;
>
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - return -EPERM;
> + error = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (error)
> + return error;
>
> if (flags & FS_IMMUTABLE_FL)
> i_flags |= S_IMMUTABLE;
> diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
> index 0367c0039e68..88b3b9720023 100644
> --- a/fs/ext2/ioctl.c
> +++ b/fs/ext2/ioctl.c
> @@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> }
> oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - inode_unlock(inode);
> - ret = -EPERM;
> - goto setflags_out;
> - }
> + ret = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (ret) {
> + inode_unlock(inode);
> + goto setflags_out;
> }
>
> flags = flags & EXT2_FL_USER_MODIFIABLE;
> diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
> index e486e49b31ed..5126ee351a84 100644
> --- a/fs/ext4/ioctl.c
> +++ b/fs/ext4/ioctl.c
> @@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
> /* The JOURNAL_DATA flag is modifiable only by root */
> jflag = flags & EXT4_JOURNAL_DATA_FL;
>
> - /*
> - * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - goto flags_out;
> - }
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto flags_out;
>
> /*
> * The JOURNAL_DATA flag can only be changed by
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 45b45f37d347..a969d5497e03 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
> {
> struct f2fs_inode_info *fi = F2FS_I(inode);
> unsigned int oldflags;
> + int err;
>
> /* Is it quota file? Do not allow user to mess with it */
> if (IS_NOQUOTA(inode))
> @@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
>
> oldflags = fi->i_flags;
>
> - if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - return -EPERM;
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + return err;
>
> flags = flags & F2FS_FL_USER_MODIFIABLE;
> flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
> diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
> index d174b1f8fd08..99f53cf699c6 100644
> --- a/fs/gfs2/file.c
> +++ b/fs/gfs2/file.c
> @@ -136,27 +136,36 @@ static struct {
> {FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
> };
>
> +static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
> +{
> + int i;
> + u32 fsflags = 0;
> +
> + if (S_ISDIR(inode->i_mode))
> + gfsflags &= ~GFS2_DIF_JDATA;
> + else
> + gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
> +
> + for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
> + if (gfsflags & fsflag_gfs2flag[i].gfsflag)
> + fsflags |= fsflag_gfs2flag[i].fsflag;
> + return fsflags;
> +}
> +
> static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
> {
> struct inode *inode = file_inode(filp);
> struct gfs2_inode *ip = GFS2_I(inode);
> struct gfs2_holder gh;
> - int i, error;
> - u32 gfsflags, fsflags = 0;
> + int error;
> + u32 fsflags;
>
> gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
> error = gfs2_glock_nq(&gh);
> if (error)
> goto out_uninit;
>
> - gfsflags = ip->i_diskflags;
> - if (S_ISDIR(inode->i_mode))
> - gfsflags &= ~GFS2_DIF_JDATA;
> - else
> - gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
> - for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
> - if (gfsflags & fsflag_gfs2flag[i].gfsflag)
> - fsflags |= fsflag_gfs2flag[i].fsflag;
> + fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
>
> if (put_user(fsflags, ptr))
> error = -EFAULT;
> @@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
> * @filp: file pointer
> * @reqflags: The flags to set
> * @mask: Indicates which flags are valid
> + * @fsflags: The FS_* inode flags passed in
> *
> */
> -static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
> +static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
> + const u32 fsflags)
> {
> struct inode *inode = file_inode(filp);
> struct gfs2_inode *ip = GFS2_I(inode);
> @@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
> struct buffer_head *bh;
> struct gfs2_holder gh;
> int error;
> - u32 new_flags, flags;
> + u32 new_flags, flags, oldflags;
>
> error = mnt_want_write_file(filp);
> if (error)
> @@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
> if (error)
> goto out_drop_write;
>
> + oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
> + error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
> + if (error)
> + goto out;
> +
> error = -EACCES;
> if (!inode_owner_or_capable(inode))
> goto out;
> @@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
> mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
> }
>
> - return do_gfs2_set_flags(filp, gfsflags, mask);
> + return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
> }
>
> static int gfs2_getlabel(struct file *filp, char __user *label)
> diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
> index 5e6502ef7415..862a3c9481d7 100644
> --- a/fs/hfsplus/ioctl.c
> +++ b/fs/hfsplus/ioctl.c
> @@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
> return 0;
> }
>
> -static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
> +static inline unsigned int hfsplus_getflags(struct inode *inode)
> {
> - struct inode *inode = file_inode(file);
> struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
> unsigned int flags = 0;
>
> @@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
> flags |= FS_APPEND_FL;
> if (hip->userflags & HFSPLUS_FLG_NODUMP)
> flags |= FS_NODUMP_FL;
> + return flags;
> +}
> +
> +static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
> +{
> + struct inode *inode = file_inode(file);
> + unsigned int flags = hfsplus_getflags(inode);
>
> return put_user(flags, user_flags);
> }
> @@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
> struct inode *inode = file_inode(file);
> struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
> unsigned int flags, new_fl = 0;
> + unsigned int oldflags = hfsplus_getflags(inode);
> int err = 0;
>
> err = mnt_want_write_file(file);
> @@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
>
> inode_lock(inode);
>
> - if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
> - inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - err = -EPERM;
> - goto out_unlock_inode;
> - }
> - }
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto out_unlock_inode;
>
> /* don't silently ignore unsupported ext2 flags */
> if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
> diff --git a/fs/inode.c b/fs/inode.c
> index df6542ec3b88..0ce60b720608 100644
> --- a/fs/inode.c
> +++ b/fs/inode.c
> @@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
> return timespec64_trunc(now, inode->i_sb->s_time_gran);
> }
> EXPORT_SYMBOL(current_time);
> +
> +/* Generic function to check FS_IOC_SETFLAGS values. */
> +int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
> diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> index ba34dae8bd9f..b485c2d7620f 100644
> --- a/fs/jfs/ioctl.c
> +++ b/fs/jfs/ioctl.c
> @@ -98,24 +98,16 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> /* Lock against other parallel changes of flags */
> inode_lock(inode);
>
> - oldflags = jfs_inode->mode2;
> -
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> - if ((oldflags & JFS_IMMUTABLE_FL) ||
> - ((flags ^ oldflags) &
> - (JFS_APPEND_FL | JFS_IMMUTABLE_FL))) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - inode_unlock(inode);
> - err = -EPERM;
> - goto setflags_out;
> - }
> + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> + 0);
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err) {
> + inode_unlock(inode);
> + goto setflags_out;
> }
>
> flags = flags & JFS_FL_USER_MODIFIABLE;
> - flags |= oldflags & ~JFS_FL_USER_MODIFIABLE;
> + flags |= jfs_inode->mode2 & ~JFS_FL_USER_MODIFIABLE;
> jfs_inode->mode2 = flags;
>
> jfs_set_inode_flags(inode);
> diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
> index 9b96d79eea6c..0632336d2515 100644
> --- a/fs/nilfs2/ioctl.c
> +++ b/fs/nilfs2/ioctl.c
> @@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
>
> oldflags = NILFS_I(inode)->i_flags;
>
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
> - * relevant capability.
> - */
> - ret = -EPERM;
> - if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
> - !capable(CAP_LINUX_IMMUTABLE))
> + ret = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (ret)
> goto out;
>
> ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
> diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
> index 994726ada857..467a2faf0305 100644
> --- a/fs/ocfs2/ioctl.c
> +++ b/fs/ocfs2/ioctl.c
> @@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
> flags = flags & mask;
> flags |= oldflags & ~mask;
>
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> - status = -EPERM;
> - if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
> - (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - goto bail_unlock;
> - }
> + status = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (status)
> + goto bail_unlock;
>
> handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
> if (IS_ERR(handle)) {
> diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
> index acbbaf7a0bb2..92bcb1ecd994 100644
> --- a/fs/reiserfs/ioctl.c
> +++ b/fs/reiserfs/ioctl.c
> @@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> err = -EPERM;
> goto setflags_out;
> }
> - if (((flags ^ REISERFS_I(inode)->
> - i_attrs) & (REISERFS_IMMUTABLE_FL |
> - REISERFS_APPEND_FL))
> - && !capable(CAP_LINUX_IMMUTABLE)) {
> - err = -EPERM;
> + err = vfs_ioc_setflags_check(inode,
> + REISERFS_I(inode)->i_attrs,
> + flags);
> + if (err)
> goto setflags_out;
> - }
> if ((flags & REISERFS_NOTAIL_FL) &&
> S_ISREG(inode->i_mode)) {
> int result;
> diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
> index 4f1a397fda69..bdea836fc38b 100644
> --- a/fs/ubifs/ioctl.c
> +++ b/fs/ubifs/ioctl.c
> @@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
> if (err)
> return err;
>
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> mutex_lock(&ui->ui_mutex);
> oldflags = ubifs2ioctl(ui->flags);
> - if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - err = -EPERM;
> - goto out_unlock;
> - }
> - }
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto out_unlock;
>
> ui->flags = ioctl2ubifs(flags);
> ubifs_set_inode_flags(inode);
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index f7fdfe93e25d..1825d055808c 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
> }
> #endif
>
> +int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
> +
> #endif /* _LINUX_FS_H */
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 66+ messages in thread* Re: [PATCH v2 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-20 13:34 ` Jan Kara
0 siblings, 0 replies; 66+ messages in thread
From: Jan Kara @ 2019-06-20 13:34 UTC (permalink / raw)
To: Darrick J. Wong
Cc: shaggy, jfs-discussion, linux-efi, linux-btrfs, yuchao0, clm,
adilger.kernel, matthew.garrett, linux-nilfs, cluster-devel,
linux-ext4, josef, reiserfs-devel, viro, dsterba, jaegeuk, tytso,
ard.biesheuvel, linux-kernel, linux-f2fs-devel, linux-xfs, jk,
jack, linux-fsdevel, linux-mtd, ocfs2-devel
On Tue 11-06-19 17:42:58, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> values so that we can standardize the implementations that follow ext4's
> flag values.
>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
The patch looks good to me. You can add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> v2: fix jfs locking and remove its opencoded flags check
> ---
> fs/btrfs/ioctl.c | 13 +++++--------
> fs/efivarfs/file.c | 18 +++++++++++++-----
> fs/ext2/ioctl.c | 16 ++++------------
> fs/ext4/ioctl.c | 13 +++----------
> fs/f2fs/file.c | 7 ++++---
> fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
> fs/hfsplus/ioctl.c | 21 ++++++++++++---------
> fs/inode.c | 17 +++++++++++++++++
> fs/jfs/ioctl.c | 22 +++++++---------------
> fs/nilfs2/ioctl.c | 9 ++-------
> fs/ocfs2/ioctl.c | 13 +++----------
> fs/reiserfs/ioctl.c | 10 ++++------
> fs/ubifs/ioctl.c | 13 +++----------
> include/linux/fs.h | 2 ++
> 14 files changed, 108 insertions(+), 108 deletions(-)
>
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index 6dafa857bbb9..f408aa93b0cf 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
> struct btrfs_inode *binode = BTRFS_I(inode);
> struct btrfs_root *root = binode->root;
> struct btrfs_trans_handle *trans;
> - unsigned int fsflags;
> + unsigned int fsflags, old_fsflags;
> int ret;
> const char *comp = NULL;
> u32 binode_flags = binode->flags;
> @@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
> inode_lock(inode);
>
> fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
> - if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
> - (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - ret = -EPERM;
> - goto out_unlock;
> - }
> - }
> + old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
> + ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
> + if (ret)
> + goto out_unlock;
>
> if (fsflags & FS_SYNC_FL)
> binode_flags |= BTRFS_INODE_SYNC;
> diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
> index 8e568428c88b..f4f6c1bec132 100644
> --- a/fs/efivarfs/file.c
> +++ b/fs/efivarfs/file.c
> @@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
> return size;
> }
>
> -static int
> -efivarfs_ioc_getxflags(struct file *file, void __user *arg)
> +static inline unsigned int efivarfs_getflags(struct inode *inode)
> {
> - struct inode *inode = file->f_mapping->host;
> unsigned int i_flags;
> unsigned int flags = 0;
>
> i_flags = inode->i_flags;
> if (i_flags & S_IMMUTABLE)
> flags |= FS_IMMUTABLE_FL;
> + return flags;
> +}
> +
> +static int
> +efivarfs_ioc_getxflags(struct file *file, void __user *arg)
> +{
> + struct inode *inode = file->f_mapping->host;
> + unsigned int flags = efivarfs_getflags(inode);
>
> if (copy_to_user(arg, &flags, sizeof(flags)))
> return -EFAULT;
> @@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
> struct inode *inode = file->f_mapping->host;
> unsigned int flags;
> unsigned int i_flags = 0;
> + unsigned int oldflags = efivarfs_getflags(inode);
> int error;
>
> if (!inode_owner_or_capable(inode))
> @@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
> if (flags & ~FS_IMMUTABLE_FL)
> return -EOPNOTSUPP;
>
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - return -EPERM;
> + error = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (error)
> + return error;
>
> if (flags & FS_IMMUTABLE_FL)
> i_flags |= S_IMMUTABLE;
> diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
> index 0367c0039e68..88b3b9720023 100644
> --- a/fs/ext2/ioctl.c
> +++ b/fs/ext2/ioctl.c
> @@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> }
> oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - inode_unlock(inode);
> - ret = -EPERM;
> - goto setflags_out;
> - }
> + ret = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (ret) {
> + inode_unlock(inode);
> + goto setflags_out;
> }
>
> flags = flags & EXT2_FL_USER_MODIFIABLE;
> diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
> index e486e49b31ed..5126ee351a84 100644
> --- a/fs/ext4/ioctl.c
> +++ b/fs/ext4/ioctl.c
> @@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
> /* The JOURNAL_DATA flag is modifiable only by root */
> jflag = flags & EXT4_JOURNAL_DATA_FL;
>
> - /*
> - * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - goto flags_out;
> - }
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto flags_out;
>
> /*
> * The JOURNAL_DATA flag can only be changed by
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 45b45f37d347..a969d5497e03 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
> {
> struct f2fs_inode_info *fi = F2FS_I(inode);
> unsigned int oldflags;
> + int err;
>
> /* Is it quota file? Do not allow user to mess with it */
> if (IS_NOQUOTA(inode))
> @@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
>
> oldflags = fi->i_flags;
>
> - if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - return -EPERM;
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + return err;
>
> flags = flags & F2FS_FL_USER_MODIFIABLE;
> flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
> diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
> index d174b1f8fd08..99f53cf699c6 100644
> --- a/fs/gfs2/file.c
> +++ b/fs/gfs2/file.c
> @@ -136,27 +136,36 @@ static struct {
> {FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
> };
>
> +static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
> +{
> + int i;
> + u32 fsflags = 0;
> +
> + if (S_ISDIR(inode->i_mode))
> + gfsflags &= ~GFS2_DIF_JDATA;
> + else
> + gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
> +
> + for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
> + if (gfsflags & fsflag_gfs2flag[i].gfsflag)
> + fsflags |= fsflag_gfs2flag[i].fsflag;
> + return fsflags;
> +}
> +
> static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
> {
> struct inode *inode = file_inode(filp);
> struct gfs2_inode *ip = GFS2_I(inode);
> struct gfs2_holder gh;
> - int i, error;
> - u32 gfsflags, fsflags = 0;
> + int error;
> + u32 fsflags;
>
> gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
> error = gfs2_glock_nq(&gh);
> if (error)
> goto out_uninit;
>
> - gfsflags = ip->i_diskflags;
> - if (S_ISDIR(inode->i_mode))
> - gfsflags &= ~GFS2_DIF_JDATA;
> - else
> - gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
> - for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
> - if (gfsflags & fsflag_gfs2flag[i].gfsflag)
> - fsflags |= fsflag_gfs2flag[i].fsflag;
> + fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
>
> if (put_user(fsflags, ptr))
> error = -EFAULT;
> @@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
> * @filp: file pointer
> * @reqflags: The flags to set
> * @mask: Indicates which flags are valid
> + * @fsflags: The FS_* inode flags passed in
> *
> */
> -static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
> +static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
> + const u32 fsflags)
> {
> struct inode *inode = file_inode(filp);
> struct gfs2_inode *ip = GFS2_I(inode);
> @@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
> struct buffer_head *bh;
> struct gfs2_holder gh;
> int error;
> - u32 new_flags, flags;
> + u32 new_flags, flags, oldflags;
>
> error = mnt_want_write_file(filp);
> if (error)
> @@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
> if (error)
> goto out_drop_write;
>
> + oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
> + error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
> + if (error)
> + goto out;
> +
> error = -EACCES;
> if (!inode_owner_or_capable(inode))
> goto out;
> @@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
> mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
> }
>
> - return do_gfs2_set_flags(filp, gfsflags, mask);
> + return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
> }
>
> static int gfs2_getlabel(struct file *filp, char __user *label)
> diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
> index 5e6502ef7415..862a3c9481d7 100644
> --- a/fs/hfsplus/ioctl.c
> +++ b/fs/hfsplus/ioctl.c
> @@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
> return 0;
> }
>
> -static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
> +static inline unsigned int hfsplus_getflags(struct inode *inode)
> {
> - struct inode *inode = file_inode(file);
> struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
> unsigned int flags = 0;
>
> @@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
> flags |= FS_APPEND_FL;
> if (hip->userflags & HFSPLUS_FLG_NODUMP)
> flags |= FS_NODUMP_FL;
> + return flags;
> +}
> +
> +static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
> +{
> + struct inode *inode = file_inode(file);
> + unsigned int flags = hfsplus_getflags(inode);
>
> return put_user(flags, user_flags);
> }
> @@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
> struct inode *inode = file_inode(file);
> struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
> unsigned int flags, new_fl = 0;
> + unsigned int oldflags = hfsplus_getflags(inode);
> int err = 0;
>
> err = mnt_want_write_file(file);
> @@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
>
> inode_lock(inode);
>
> - if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
> - inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - err = -EPERM;
> - goto out_unlock_inode;
> - }
> - }
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto out_unlock_inode;
>
> /* don't silently ignore unsupported ext2 flags */
> if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
> diff --git a/fs/inode.c b/fs/inode.c
> index df6542ec3b88..0ce60b720608 100644
> --- a/fs/inode.c
> +++ b/fs/inode.c
> @@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
> return timespec64_trunc(now, inode->i_sb->s_time_gran);
> }
> EXPORT_SYMBOL(current_time);
> +
> +/* Generic function to check FS_IOC_SETFLAGS values. */
> +int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
> diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> index ba34dae8bd9f..b485c2d7620f 100644
> --- a/fs/jfs/ioctl.c
> +++ b/fs/jfs/ioctl.c
> @@ -98,24 +98,16 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> /* Lock against other parallel changes of flags */
> inode_lock(inode);
>
> - oldflags = jfs_inode->mode2;
> -
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> - if ((oldflags & JFS_IMMUTABLE_FL) ||
> - ((flags ^ oldflags) &
> - (JFS_APPEND_FL | JFS_IMMUTABLE_FL))) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - inode_unlock(inode);
> - err = -EPERM;
> - goto setflags_out;
> - }
> + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> + 0);
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err) {
> + inode_unlock(inode);
> + goto setflags_out;
> }
>
> flags = flags & JFS_FL_USER_MODIFIABLE;
> - flags |= oldflags & ~JFS_FL_USER_MODIFIABLE;
> + flags |= jfs_inode->mode2 & ~JFS_FL_USER_MODIFIABLE;
> jfs_inode->mode2 = flags;
>
> jfs_set_inode_flags(inode);
> diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
> index 9b96d79eea6c..0632336d2515 100644
> --- a/fs/nilfs2/ioctl.c
> +++ b/fs/nilfs2/ioctl.c
> @@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
>
> oldflags = NILFS_I(inode)->i_flags;
>
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
> - * relevant capability.
> - */
> - ret = -EPERM;
> - if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
> - !capable(CAP_LINUX_IMMUTABLE))
> + ret = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (ret)
> goto out;
>
> ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
> diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
> index 994726ada857..467a2faf0305 100644
> --- a/fs/ocfs2/ioctl.c
> +++ b/fs/ocfs2/ioctl.c
> @@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
> flags = flags & mask;
> flags |= oldflags & ~mask;
>
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> - status = -EPERM;
> - if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
> - (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - goto bail_unlock;
> - }
> + status = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (status)
> + goto bail_unlock;
>
> handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
> if (IS_ERR(handle)) {
> diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
> index acbbaf7a0bb2..92bcb1ecd994 100644
> --- a/fs/reiserfs/ioctl.c
> +++ b/fs/reiserfs/ioctl.c
> @@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> err = -EPERM;
> goto setflags_out;
> }
> - if (((flags ^ REISERFS_I(inode)->
> - i_attrs) & (REISERFS_IMMUTABLE_FL |
> - REISERFS_APPEND_FL))
> - && !capable(CAP_LINUX_IMMUTABLE)) {
> - err = -EPERM;
> + err = vfs_ioc_setflags_check(inode,
> + REISERFS_I(inode)->i_attrs,
> + flags);
> + if (err)
> goto setflags_out;
> - }
> if ((flags & REISERFS_NOTAIL_FL) &&
> S_ISREG(inode->i_mode)) {
> int result;
> diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
> index 4f1a397fda69..bdea836fc38b 100644
> --- a/fs/ubifs/ioctl.c
> +++ b/fs/ubifs/ioctl.c
> @@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
> if (err)
> return err;
>
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> mutex_lock(&ui->ui_mutex);
> oldflags = ubifs2ioctl(ui->flags);
> - if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - err = -EPERM;
> - goto out_unlock;
> - }
> - }
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto out_unlock;
>
> ui->flags = ioctl2ubifs(flags);
> ubifs_set_inode_flags(inode);
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index f7fdfe93e25d..1825d055808c 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
> }
> #endif
>
> +int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
> +
> #endif /* _LINUX_FS_H */
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 66+ messages in thread* Re: [f2fs-dev] [PATCH v2 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-20 13:34 ` Jan Kara
0 siblings, 0 replies; 66+ messages in thread
From: Jan Kara @ 2019-06-20 13:34 UTC (permalink / raw)
To: Darrick J. Wong
Cc: shaggy, jfs-discussion, linux-efi, linux-btrfs, clm,
adilger.kernel, matthew.garrett, linux-nilfs, cluster-devel,
linux-ext4, josef, reiserfs-devel, viro, dsterba, jaegeuk, tytso,
ard.biesheuvel, linux-kernel, linux-f2fs-devel, linux-xfs, jk,
jack, linux-fsdevel, linux-mtd, ocfs2-devel
On Tue 11-06-19 17:42:58, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> values so that we can standardize the implementations that follow ext4's
> flag values.
>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
The patch looks good to me. You can add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> v2: fix jfs locking and remove its opencoded flags check
> ---
> fs/btrfs/ioctl.c | 13 +++++--------
> fs/efivarfs/file.c | 18 +++++++++++++-----
> fs/ext2/ioctl.c | 16 ++++------------
> fs/ext4/ioctl.c | 13 +++----------
> fs/f2fs/file.c | 7 ++++---
> fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
> fs/hfsplus/ioctl.c | 21 ++++++++++++---------
> fs/inode.c | 17 +++++++++++++++++
> fs/jfs/ioctl.c | 22 +++++++---------------
> fs/nilfs2/ioctl.c | 9 ++-------
> fs/ocfs2/ioctl.c | 13 +++----------
> fs/reiserfs/ioctl.c | 10 ++++------
> fs/ubifs/ioctl.c | 13 +++----------
> include/linux/fs.h | 2 ++
> 14 files changed, 108 insertions(+), 108 deletions(-)
>
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index 6dafa857bbb9..f408aa93b0cf 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
> struct btrfs_inode *binode = BTRFS_I(inode);
> struct btrfs_root *root = binode->root;
> struct btrfs_trans_handle *trans;
> - unsigned int fsflags;
> + unsigned int fsflags, old_fsflags;
> int ret;
> const char *comp = NULL;
> u32 binode_flags = binode->flags;
> @@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
> inode_lock(inode);
>
> fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
> - if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
> - (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - ret = -EPERM;
> - goto out_unlock;
> - }
> - }
> + old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
> + ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
> + if (ret)
> + goto out_unlock;
>
> if (fsflags & FS_SYNC_FL)
> binode_flags |= BTRFS_INODE_SYNC;
> diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
> index 8e568428c88b..f4f6c1bec132 100644
> --- a/fs/efivarfs/file.c
> +++ b/fs/efivarfs/file.c
> @@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
> return size;
> }
>
> -static int
> -efivarfs_ioc_getxflags(struct file *file, void __user *arg)
> +static inline unsigned int efivarfs_getflags(struct inode *inode)
> {
> - struct inode *inode = file->f_mapping->host;
> unsigned int i_flags;
> unsigned int flags = 0;
>
> i_flags = inode->i_flags;
> if (i_flags & S_IMMUTABLE)
> flags |= FS_IMMUTABLE_FL;
> + return flags;
> +}
> +
> +static int
> +efivarfs_ioc_getxflags(struct file *file, void __user *arg)
> +{
> + struct inode *inode = file->f_mapping->host;
> + unsigned int flags = efivarfs_getflags(inode);
>
> if (copy_to_user(arg, &flags, sizeof(flags)))
> return -EFAULT;
> @@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
> struct inode *inode = file->f_mapping->host;
> unsigned int flags;
> unsigned int i_flags = 0;
> + unsigned int oldflags = efivarfs_getflags(inode);
> int error;
>
> if (!inode_owner_or_capable(inode))
> @@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
> if (flags & ~FS_IMMUTABLE_FL)
> return -EOPNOTSUPP;
>
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - return -EPERM;
> + error = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (error)
> + return error;
>
> if (flags & FS_IMMUTABLE_FL)
> i_flags |= S_IMMUTABLE;
> diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
> index 0367c0039e68..88b3b9720023 100644
> --- a/fs/ext2/ioctl.c
> +++ b/fs/ext2/ioctl.c
> @@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> }
> oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - inode_unlock(inode);
> - ret = -EPERM;
> - goto setflags_out;
> - }
> + ret = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (ret) {
> + inode_unlock(inode);
> + goto setflags_out;
> }
>
> flags = flags & EXT2_FL_USER_MODIFIABLE;
> diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
> index e486e49b31ed..5126ee351a84 100644
> --- a/fs/ext4/ioctl.c
> +++ b/fs/ext4/ioctl.c
> @@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
> /* The JOURNAL_DATA flag is modifiable only by root */
> jflag = flags & EXT4_JOURNAL_DATA_FL;
>
> - /*
> - * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - goto flags_out;
> - }
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto flags_out;
>
> /*
> * The JOURNAL_DATA flag can only be changed by
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 45b45f37d347..a969d5497e03 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
> {
> struct f2fs_inode_info *fi = F2FS_I(inode);
> unsigned int oldflags;
> + int err;
>
> /* Is it quota file? Do not allow user to mess with it */
> if (IS_NOQUOTA(inode))
> @@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
>
> oldflags = fi->i_flags;
>
> - if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - return -EPERM;
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + return err;
>
> flags = flags & F2FS_FL_USER_MODIFIABLE;
> flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
> diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
> index d174b1f8fd08..99f53cf699c6 100644
> --- a/fs/gfs2/file.c
> +++ b/fs/gfs2/file.c
> @@ -136,27 +136,36 @@ static struct {
> {FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
> };
>
> +static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
> +{
> + int i;
> + u32 fsflags = 0;
> +
> + if (S_ISDIR(inode->i_mode))
> + gfsflags &= ~GFS2_DIF_JDATA;
> + else
> + gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
> +
> + for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
> + if (gfsflags & fsflag_gfs2flag[i].gfsflag)
> + fsflags |= fsflag_gfs2flag[i].fsflag;
> + return fsflags;
> +}
> +
> static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
> {
> struct inode *inode = file_inode(filp);
> struct gfs2_inode *ip = GFS2_I(inode);
> struct gfs2_holder gh;
> - int i, error;
> - u32 gfsflags, fsflags = 0;
> + int error;
> + u32 fsflags;
>
> gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
> error = gfs2_glock_nq(&gh);
> if (error)
> goto out_uninit;
>
> - gfsflags = ip->i_diskflags;
> - if (S_ISDIR(inode->i_mode))
> - gfsflags &= ~GFS2_DIF_JDATA;
> - else
> - gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
> - for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
> - if (gfsflags & fsflag_gfs2flag[i].gfsflag)
> - fsflags |= fsflag_gfs2flag[i].fsflag;
> + fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
>
> if (put_user(fsflags, ptr))
> error = -EFAULT;
> @@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
> * @filp: file pointer
> * @reqflags: The flags to set
> * @mask: Indicates which flags are valid
> + * @fsflags: The FS_* inode flags passed in
> *
> */
> -static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
> +static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
> + const u32 fsflags)
> {
> struct inode *inode = file_inode(filp);
> struct gfs2_inode *ip = GFS2_I(inode);
> @@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
> struct buffer_head *bh;
> struct gfs2_holder gh;
> int error;
> - u32 new_flags, flags;
> + u32 new_flags, flags, oldflags;
>
> error = mnt_want_write_file(filp);
> if (error)
> @@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
> if (error)
> goto out_drop_write;
>
> + oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
> + error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
> + if (error)
> + goto out;
> +
> error = -EACCES;
> if (!inode_owner_or_capable(inode))
> goto out;
> @@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
> mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
> }
>
> - return do_gfs2_set_flags(filp, gfsflags, mask);
> + return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
> }
>
> static int gfs2_getlabel(struct file *filp, char __user *label)
> diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
> index 5e6502ef7415..862a3c9481d7 100644
> --- a/fs/hfsplus/ioctl.c
> +++ b/fs/hfsplus/ioctl.c
> @@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
> return 0;
> }
>
> -static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
> +static inline unsigned int hfsplus_getflags(struct inode *inode)
> {
> - struct inode *inode = file_inode(file);
> struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
> unsigned int flags = 0;
>
> @@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
> flags |= FS_APPEND_FL;
> if (hip->userflags & HFSPLUS_FLG_NODUMP)
> flags |= FS_NODUMP_FL;
> + return flags;
> +}
> +
> +static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
> +{
> + struct inode *inode = file_inode(file);
> + unsigned int flags = hfsplus_getflags(inode);
>
> return put_user(flags, user_flags);
> }
> @@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
> struct inode *inode = file_inode(file);
> struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
> unsigned int flags, new_fl = 0;
> + unsigned int oldflags = hfsplus_getflags(inode);
> int err = 0;
>
> err = mnt_want_write_file(file);
> @@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
>
> inode_lock(inode);
>
> - if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
> - inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - err = -EPERM;
> - goto out_unlock_inode;
> - }
> - }
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto out_unlock_inode;
>
> /* don't silently ignore unsupported ext2 flags */
> if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
> diff --git a/fs/inode.c b/fs/inode.c
> index df6542ec3b88..0ce60b720608 100644
> --- a/fs/inode.c
> +++ b/fs/inode.c
> @@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
> return timespec64_trunc(now, inode->i_sb->s_time_gran);
> }
> EXPORT_SYMBOL(current_time);
> +
> +/* Generic function to check FS_IOC_SETFLAGS values. */
> +int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
> diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> index ba34dae8bd9f..b485c2d7620f 100644
> --- a/fs/jfs/ioctl.c
> +++ b/fs/jfs/ioctl.c
> @@ -98,24 +98,16 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> /* Lock against other parallel changes of flags */
> inode_lock(inode);
>
> - oldflags = jfs_inode->mode2;
> -
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> - if ((oldflags & JFS_IMMUTABLE_FL) ||
> - ((flags ^ oldflags) &
> - (JFS_APPEND_FL | JFS_IMMUTABLE_FL))) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - inode_unlock(inode);
> - err = -EPERM;
> - goto setflags_out;
> - }
> + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> + 0);
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err) {
> + inode_unlock(inode);
> + goto setflags_out;
> }
>
> flags = flags & JFS_FL_USER_MODIFIABLE;
> - flags |= oldflags & ~JFS_FL_USER_MODIFIABLE;
> + flags |= jfs_inode->mode2 & ~JFS_FL_USER_MODIFIABLE;
> jfs_inode->mode2 = flags;
>
> jfs_set_inode_flags(inode);
> diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
> index 9b96d79eea6c..0632336d2515 100644
> --- a/fs/nilfs2/ioctl.c
> +++ b/fs/nilfs2/ioctl.c
> @@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
>
> oldflags = NILFS_I(inode)->i_flags;
>
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
> - * relevant capability.
> - */
> - ret = -EPERM;
> - if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
> - !capable(CAP_LINUX_IMMUTABLE))
> + ret = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (ret)
> goto out;
>
> ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
> diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
> index 994726ada857..467a2faf0305 100644
> --- a/fs/ocfs2/ioctl.c
> +++ b/fs/ocfs2/ioctl.c
> @@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
> flags = flags & mask;
> flags |= oldflags & ~mask;
>
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> - status = -EPERM;
> - if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
> - (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - goto bail_unlock;
> - }
> + status = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (status)
> + goto bail_unlock;
>
> handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
> if (IS_ERR(handle)) {
> diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
> index acbbaf7a0bb2..92bcb1ecd994 100644
> --- a/fs/reiserfs/ioctl.c
> +++ b/fs/reiserfs/ioctl.c
> @@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> err = -EPERM;
> goto setflags_out;
> }
> - if (((flags ^ REISERFS_I(inode)->
> - i_attrs) & (REISERFS_IMMUTABLE_FL |
> - REISERFS_APPEND_FL))
> - && !capable(CAP_LINUX_IMMUTABLE)) {
> - err = -EPERM;
> + err = vfs_ioc_setflags_check(inode,
> + REISERFS_I(inode)->i_attrs,
> + flags);
> + if (err)
> goto setflags_out;
> - }
> if ((flags & REISERFS_NOTAIL_FL) &&
> S_ISREG(inode->i_mode)) {
> int result;
> diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
> index 4f1a397fda69..bdea836fc38b 100644
> --- a/fs/ubifs/ioctl.c
> +++ b/fs/ubifs/ioctl.c
> @@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
> if (err)
> return err;
>
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> mutex_lock(&ui->ui_mutex);
> oldflags = ubifs2ioctl(ui->flags);
> - if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - err = -EPERM;
> - goto out_unlock;
> - }
> - }
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto out_unlock;
>
> ui->flags = ioctl2ubifs(flags);
> ubifs_set_inode_flags(inode);
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index f7fdfe93e25d..1825d055808c 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
> }
> #endif
>
> +int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
> +
> #endif /* _LINUX_FS_H */
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel
^ permalink raw reply [flat|nested] 66+ messages in thread* Re: [PATCH v2 1/4] vfs: create a generic checking function for FS_IOC_SETFLAGS
@ 2019-06-20 13:34 ` Jan Kara
0 siblings, 0 replies; 66+ messages in thread
From: Jan Kara @ 2019-06-20 13:34 UTC (permalink / raw)
To: Darrick J. Wong
Cc: matthew.garrett, yuchao0, tytso, shaggy, ard.biesheuvel, josef,
clm, adilger.kernel, jk, jack, dsterba, jaegeuk, viro,
cluster-devel, jfs-discussion, linux-efi, reiserfs-devel,
linux-kernel, linux-f2fs-devel, linux-xfs, linux-nilfs, linux-mtd,
ocfs2-devel, linux-fsdevel, linux-ext4, linux-btrfs
On Tue 11-06-19 17:42:58, Darrick J. Wong wrote:
> From: Darrick J. Wong <darrick.wong@oracle.com>
>
> Create a generic checking function for the incoming FS_IOC_SETFLAGS flag
> values so that we can standardize the implementations that follow ext4's
> flag values.
>
> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
The patch looks good to me. You can add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> v2: fix jfs locking and remove its opencoded flags check
> ---
> fs/btrfs/ioctl.c | 13 +++++--------
> fs/efivarfs/file.c | 18 +++++++++++++-----
> fs/ext2/ioctl.c | 16 ++++------------
> fs/ext4/ioctl.c | 13 +++----------
> fs/f2fs/file.c | 7 ++++---
> fs/gfs2/file.c | 42 +++++++++++++++++++++++++++++-------------
> fs/hfsplus/ioctl.c | 21 ++++++++++++---------
> fs/inode.c | 17 +++++++++++++++++
> fs/jfs/ioctl.c | 22 +++++++---------------
> fs/nilfs2/ioctl.c | 9 ++-------
> fs/ocfs2/ioctl.c | 13 +++----------
> fs/reiserfs/ioctl.c | 10 ++++------
> fs/ubifs/ioctl.c | 13 +++----------
> include/linux/fs.h | 2 ++
> 14 files changed, 108 insertions(+), 108 deletions(-)
>
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index 6dafa857bbb9..f408aa93b0cf 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -187,7 +187,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
> struct btrfs_inode *binode = BTRFS_I(inode);
> struct btrfs_root *root = binode->root;
> struct btrfs_trans_handle *trans;
> - unsigned int fsflags;
> + unsigned int fsflags, old_fsflags;
> int ret;
> const char *comp = NULL;
> u32 binode_flags = binode->flags;
> @@ -212,13 +212,10 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)
> inode_lock(inode);
>
> fsflags = btrfs_mask_fsflags_for_type(inode, fsflags);
> - if ((fsflags ^ btrfs_inode_flags_to_fsflags(binode->flags)) &
> - (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - ret = -EPERM;
> - goto out_unlock;
> - }
> - }
> + old_fsflags = btrfs_inode_flags_to_fsflags(binode->flags);
> + ret = vfs_ioc_setflags_check(inode, old_fsflags, fsflags);
> + if (ret)
> + goto out_unlock;
>
> if (fsflags & FS_SYNC_FL)
> binode_flags |= BTRFS_INODE_SYNC;
> diff --git a/fs/efivarfs/file.c b/fs/efivarfs/file.c
> index 8e568428c88b..f4f6c1bec132 100644
> --- a/fs/efivarfs/file.c
> +++ b/fs/efivarfs/file.c
> @@ -110,16 +110,22 @@ static ssize_t efivarfs_file_read(struct file *file, char __user *userbuf,
> return size;
> }
>
> -static int
> -efivarfs_ioc_getxflags(struct file *file, void __user *arg)
> +static inline unsigned int efivarfs_getflags(struct inode *inode)
> {
> - struct inode *inode = file->f_mapping->host;
> unsigned int i_flags;
> unsigned int flags = 0;
>
> i_flags = inode->i_flags;
> if (i_flags & S_IMMUTABLE)
> flags |= FS_IMMUTABLE_FL;
> + return flags;
> +}
> +
> +static int
> +efivarfs_ioc_getxflags(struct file *file, void __user *arg)
> +{
> + struct inode *inode = file->f_mapping->host;
> + unsigned int flags = efivarfs_getflags(inode);
>
> if (copy_to_user(arg, &flags, sizeof(flags)))
> return -EFAULT;
> @@ -132,6 +138,7 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
> struct inode *inode = file->f_mapping->host;
> unsigned int flags;
> unsigned int i_flags = 0;
> + unsigned int oldflags = efivarfs_getflags(inode);
> int error;
>
> if (!inode_owner_or_capable(inode))
> @@ -143,8 +150,9 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
> if (flags & ~FS_IMMUTABLE_FL)
> return -EOPNOTSUPP;
>
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - return -EPERM;
> + error = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (error)
> + return error;
>
> if (flags & FS_IMMUTABLE_FL)
> i_flags |= S_IMMUTABLE;
> diff --git a/fs/ext2/ioctl.c b/fs/ext2/ioctl.c
> index 0367c0039e68..88b3b9720023 100644
> --- a/fs/ext2/ioctl.c
> +++ b/fs/ext2/ioctl.c
> @@ -60,18 +60,10 @@ long ext2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> }
> oldflags = ei->i_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) & (EXT2_APPEND_FL | EXT2_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - inode_unlock(inode);
> - ret = -EPERM;
> - goto setflags_out;
> - }
> + ret = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (ret) {
> + inode_unlock(inode);
> + goto setflags_out;
> }
>
> flags = flags & EXT2_FL_USER_MODIFIABLE;
> diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
> index e486e49b31ed..5126ee351a84 100644
> --- a/fs/ext4/ioctl.c
> +++ b/fs/ext4/ioctl.c
> @@ -289,16 +289,9 @@ static int ext4_ioctl_setflags(struct inode *inode,
> /* The JOURNAL_DATA flag is modifiable only by root */
> jflag = flags & EXT4_JOURNAL_DATA_FL;
>
> - /*
> - * 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) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - goto flags_out;
> - }
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto flags_out;
>
> /*
> * The JOURNAL_DATA flag can only be changed by
> diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
> index 45b45f37d347..a969d5497e03 100644
> --- a/fs/f2fs/file.c
> +++ b/fs/f2fs/file.c
> @@ -1670,6 +1670,7 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
> {
> struct f2fs_inode_info *fi = F2FS_I(inode);
> unsigned int oldflags;
> + int err;
>
> /* Is it quota file? Do not allow user to mess with it */
> if (IS_NOQUOTA(inode))
> @@ -1679,9 +1680,9 @@ static int __f2fs_ioc_setflags(struct inode *inode, unsigned int flags)
>
> oldflags = fi->i_flags;
>
> - if ((flags ^ oldflags) & (F2FS_APPEND_FL | F2FS_IMMUTABLE_FL))
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - return -EPERM;
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + return err;
>
> flags = flags & F2FS_FL_USER_MODIFIABLE;
> flags |= oldflags & ~F2FS_FL_USER_MODIFIABLE;
> diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
> index d174b1f8fd08..99f53cf699c6 100644
> --- a/fs/gfs2/file.c
> +++ b/fs/gfs2/file.c
> @@ -136,27 +136,36 @@ static struct {
> {FS_JOURNAL_DATA_FL, GFS2_DIF_JDATA | GFS2_DIF_INHERIT_JDATA},
> };
>
> +static inline u32 gfs2_gfsflags_to_fsflags(struct inode *inode, u32 gfsflags)
> +{
> + int i;
> + u32 fsflags = 0;
> +
> + if (S_ISDIR(inode->i_mode))
> + gfsflags &= ~GFS2_DIF_JDATA;
> + else
> + gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
> +
> + for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
> + if (gfsflags & fsflag_gfs2flag[i].gfsflag)
> + fsflags |= fsflag_gfs2flag[i].fsflag;
> + return fsflags;
> +}
> +
> static int gfs2_get_flags(struct file *filp, u32 __user *ptr)
> {
> struct inode *inode = file_inode(filp);
> struct gfs2_inode *ip = GFS2_I(inode);
> struct gfs2_holder gh;
> - int i, error;
> - u32 gfsflags, fsflags = 0;
> + int error;
> + u32 fsflags;
>
> gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh);
> error = gfs2_glock_nq(&gh);
> if (error)
> goto out_uninit;
>
> - gfsflags = ip->i_diskflags;
> - if (S_ISDIR(inode->i_mode))
> - gfsflags &= ~GFS2_DIF_JDATA;
> - else
> - gfsflags &= ~GFS2_DIF_INHERIT_JDATA;
> - for (i = 0; i < ARRAY_SIZE(fsflag_gfs2flag); i++)
> - if (gfsflags & fsflag_gfs2flag[i].gfsflag)
> - fsflags |= fsflag_gfs2flag[i].fsflag;
> + fsflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
>
> if (put_user(fsflags, ptr))
> error = -EFAULT;
> @@ -200,9 +209,11 @@ void gfs2_set_inode_flags(struct inode *inode)
> * @filp: file pointer
> * @reqflags: The flags to set
> * @mask: Indicates which flags are valid
> + * @fsflags: The FS_* inode flags passed in
> *
> */
> -static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
> +static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask,
> + const u32 fsflags)
> {
> struct inode *inode = file_inode(filp);
> struct gfs2_inode *ip = GFS2_I(inode);
> @@ -210,7 +221,7 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
> struct buffer_head *bh;
> struct gfs2_holder gh;
> int error;
> - u32 new_flags, flags;
> + u32 new_flags, flags, oldflags;
>
> error = mnt_want_write_file(filp);
> if (error)
> @@ -220,6 +231,11 @@ static int do_gfs2_set_flags(struct file *filp, u32 reqflags, u32 mask)
> if (error)
> goto out_drop_write;
>
> + oldflags = gfs2_gfsflags_to_fsflags(inode, ip->i_diskflags);
> + error = vfs_ioc_setflags_check(inode, oldflags, fsflags);
> + if (error)
> + goto out;
> +
> error = -EACCES;
> if (!inode_owner_or_capable(inode))
> goto out;
> @@ -308,7 +324,7 @@ static int gfs2_set_flags(struct file *filp, u32 __user *ptr)
> mask &= ~(GFS2_DIF_TOPDIR | GFS2_DIF_INHERIT_JDATA);
> }
>
> - return do_gfs2_set_flags(filp, gfsflags, mask);
> + return do_gfs2_set_flags(filp, gfsflags, mask, fsflags);
> }
>
> static int gfs2_getlabel(struct file *filp, char __user *label)
> diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
> index 5e6502ef7415..862a3c9481d7 100644
> --- a/fs/hfsplus/ioctl.c
> +++ b/fs/hfsplus/ioctl.c
> @@ -57,9 +57,8 @@ static int hfsplus_ioctl_bless(struct file *file, int __user *user_flags)
> return 0;
> }
>
> -static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
> +static inline unsigned int hfsplus_getflags(struct inode *inode)
> {
> - struct inode *inode = file_inode(file);
> struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
> unsigned int flags = 0;
>
> @@ -69,6 +68,13 @@ static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
> flags |= FS_APPEND_FL;
> if (hip->userflags & HFSPLUS_FLG_NODUMP)
> flags |= FS_NODUMP_FL;
> + return flags;
> +}
> +
> +static int hfsplus_ioctl_getflags(struct file *file, int __user *user_flags)
> +{
> + struct inode *inode = file_inode(file);
> + unsigned int flags = hfsplus_getflags(inode);
>
> return put_user(flags, user_flags);
> }
> @@ -78,6 +84,7 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
> struct inode *inode = file_inode(file);
> struct hfsplus_inode_info *hip = HFSPLUS_I(inode);
> unsigned int flags, new_fl = 0;
> + unsigned int oldflags = hfsplus_getflags(inode);
> int err = 0;
>
> err = mnt_want_write_file(file);
> @@ -96,13 +103,9 @@ static int hfsplus_ioctl_setflags(struct file *file, int __user *user_flags)
>
> inode_lock(inode);
>
> - if ((flags & (FS_IMMUTABLE_FL|FS_APPEND_FL)) ||
> - inode->i_flags & (S_IMMUTABLE|S_APPEND)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - err = -EPERM;
> - goto out_unlock_inode;
> - }
> - }
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto out_unlock_inode;
>
> /* don't silently ignore unsupported ext2 flags */
> if (flags & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL)) {
> diff --git a/fs/inode.c b/fs/inode.c
> index df6542ec3b88..0ce60b720608 100644
> --- a/fs/inode.c
> +++ b/fs/inode.c
> @@ -2170,3 +2170,20 @@ struct timespec64 current_time(struct inode *inode)
> return timespec64_trunc(now, inode->i_sb->s_time_gran);
> }
> EXPORT_SYMBOL(current_time);
> +
> +/* Generic function to check FS_IOC_SETFLAGS values. */
> +int vfs_ioc_setflags_check(struct inode *inode, int oldflags, 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_check);
> diff --git a/fs/jfs/ioctl.c b/fs/jfs/ioctl.c
> index ba34dae8bd9f..b485c2d7620f 100644
> --- a/fs/jfs/ioctl.c
> +++ b/fs/jfs/ioctl.c
> @@ -98,24 +98,16 @@ long jfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> /* Lock against other parallel changes of flags */
> inode_lock(inode);
>
> - oldflags = jfs_inode->mode2;
> -
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> - if ((oldflags & JFS_IMMUTABLE_FL) ||
> - ((flags ^ oldflags) &
> - (JFS_APPEND_FL | JFS_IMMUTABLE_FL))) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - inode_unlock(inode);
> - err = -EPERM;
> - goto setflags_out;
> - }
> + oldflags = jfs_map_ext2(jfs_inode->mode2 & JFS_FL_USER_VISIBLE,
> + 0);
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err) {
> + inode_unlock(inode);
> + goto setflags_out;
> }
>
> flags = flags & JFS_FL_USER_MODIFIABLE;
> - flags |= oldflags & ~JFS_FL_USER_MODIFIABLE;
> + flags |= jfs_inode->mode2 & ~JFS_FL_USER_MODIFIABLE;
> jfs_inode->mode2 = flags;
>
> jfs_set_inode_flags(inode);
> diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
> index 9b96d79eea6c..0632336d2515 100644
> --- a/fs/nilfs2/ioctl.c
> +++ b/fs/nilfs2/ioctl.c
> @@ -148,13 +148,8 @@ static int nilfs_ioctl_setflags(struct inode *inode, struct file *filp,
>
> oldflags = NILFS_I(inode)->i_flags;
>
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by the
> - * relevant capability.
> - */
> - ret = -EPERM;
> - if (((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) &&
> - !capable(CAP_LINUX_IMMUTABLE))
> + ret = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (ret)
> goto out;
>
> ret = nilfs_transaction_begin(inode->i_sb, &ti, 0);
> diff --git a/fs/ocfs2/ioctl.c b/fs/ocfs2/ioctl.c
> index 994726ada857..467a2faf0305 100644
> --- a/fs/ocfs2/ioctl.c
> +++ b/fs/ocfs2/ioctl.c
> @@ -106,16 +106,9 @@ static int ocfs2_set_inode_attr(struct inode *inode, unsigned flags,
> flags = flags & mask;
> flags |= oldflags & ~mask;
>
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> - status = -EPERM;
> - if ((oldflags & OCFS2_IMMUTABLE_FL) || ((flags ^ oldflags) &
> - (OCFS2_APPEND_FL | OCFS2_IMMUTABLE_FL))) {
> - if (!capable(CAP_LINUX_IMMUTABLE))
> - goto bail_unlock;
> - }
> + status = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (status)
> + goto bail_unlock;
>
> handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS);
> if (IS_ERR(handle)) {
> diff --git a/fs/reiserfs/ioctl.c b/fs/reiserfs/ioctl.c
> index acbbaf7a0bb2..92bcb1ecd994 100644
> --- a/fs/reiserfs/ioctl.c
> +++ b/fs/reiserfs/ioctl.c
> @@ -74,13 +74,11 @@ long reiserfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> err = -EPERM;
> goto setflags_out;
> }
> - if (((flags ^ REISERFS_I(inode)->
> - i_attrs) & (REISERFS_IMMUTABLE_FL |
> - REISERFS_APPEND_FL))
> - && !capable(CAP_LINUX_IMMUTABLE)) {
> - err = -EPERM;
> + err = vfs_ioc_setflags_check(inode,
> + REISERFS_I(inode)->i_attrs,
> + flags);
> + if (err)
> goto setflags_out;
> - }
> if ((flags & REISERFS_NOTAIL_FL) &&
> S_ISREG(inode->i_mode)) {
> int result;
> diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c
> index 4f1a397fda69..bdea836fc38b 100644
> --- a/fs/ubifs/ioctl.c
> +++ b/fs/ubifs/ioctl.c
> @@ -107,18 +107,11 @@ static int setflags(struct inode *inode, int flags)
> if (err)
> return err;
>
> - /*
> - * The IMMUTABLE and APPEND_ONLY flags can only be changed by
> - * the relevant capability.
> - */
> mutex_lock(&ui->ui_mutex);
> oldflags = ubifs2ioctl(ui->flags);
> - if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) {
> - if (!capable(CAP_LINUX_IMMUTABLE)) {
> - err = -EPERM;
> - goto out_unlock;
> - }
> - }
> + err = vfs_ioc_setflags_check(inode, oldflags, flags);
> + if (err)
> + goto out_unlock;
>
> ui->flags = ioctl2ubifs(flags);
> ubifs_set_inode_flags(inode);
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index f7fdfe93e25d..1825d055808c 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -3546,4 +3546,6 @@ static inline struct sock *io_uring_get_socket(struct file *file)
> }
> #endif
>
> +int vfs_ioc_setflags_check(struct inode *inode, int oldflags, int flags);
> +
> #endif /* _LINUX_FS_H */
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 66+ messages in thread