Skip to content

Commit

Permalink
Merge tag 'vfs-fix-ioctl-checking-3' of git://git.kernel.org/pub/scm/…
Browse files Browse the repository at this point in the history
…fs/xfs/xfs-linux

Pull common SETFLAGS/FSSETXATTR parameter checking from Darrick Wong:
 "Here's a patch series that sets up common parameter checking functions
  for the FS_IOC_SETFLAGS and FS_IOC_FSSETXATTR ioctl implementations.

  The goal here is to reduce the amount of behaviorial variance between
  the filesystems where those ioctls originated (ext2 and XFS,
  respectively) and everybody else.

   - Standardize parameter checking for the SETFLAGS and FSSETXATTR
     ioctls (which were the file attribute setters for ext4 and xfs and
     have now been hoisted to the vfs)

   - Only allow the DAX flag to be set on files and directories"

* tag 'vfs-fix-ioctl-checking-3' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  vfs: only allow FSSETXATTR to set DAX flag on files and dirs
  vfs: teach vfs_ioc_fssetxattr_check to check extent size hints
  vfs: teach vfs_ioc_fssetxattr_check to check project id info
  vfs: create a generic checking function for FS_IOC_FSSETXATTR
  vfs: create a generic checking and prep function for FS_IOC_SETFLAGS
  • Loading branch information
torvalds committed Jul 12, 2019
2 parents 8487d82 + dbc77f3 commit 5010fe9
Show file tree
Hide file tree
Showing 15 changed files with 300 additions and 242 deletions.
30 changes: 11 additions & 19 deletions fs/btrfs/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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_prepare(inode, old_fsflags, fsflags);
if (ret)
goto out_unlock;

if (fsflags & FS_SYNC_FL)
binode_flags |= BTRFS_INODE_SYNC;
Expand Down Expand Up @@ -376,9 +373,7 @@ static int btrfs_ioctl_fsgetxattr(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(file_inode(file));
struct fsxattr fa;

memset(&fa, 0, sizeof(fa));
fa.fsx_xflags = btrfs_inode_flags_to_xflags(binode->flags);

simple_fill_fsxattr(&fa, btrfs_inode_flags_to_xflags(binode->flags));
if (copy_to_user(arg, &fa, sizeof(fa)))
return -EFAULT;

Expand All @@ -391,7 +386,7 @@ static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg)
struct btrfs_inode *binode = BTRFS_I(inode);
struct btrfs_root *root = binode->root;
struct btrfs_trans_handle *trans;
struct fsxattr fa;
struct fsxattr fa, old_fa;
unsigned old_flags;
unsigned old_i_flags;
int ret = 0;
Expand All @@ -402,7 +397,6 @@ static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg)
if (btrfs_root_readonly(root))
return -EROFS;

memset(&fa, 0, sizeof(fa));
if (copy_from_user(&fa, arg, sizeof(fa)))
return -EFAULT;

Expand All @@ -422,13 +416,11 @@ static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg)
old_flags = binode->flags;
old_i_flags = inode->i_flags;

/* We need the capabilities to change append-only or immutable inode */
if (((old_flags & (BTRFS_INODE_APPEND | BTRFS_INODE_IMMUTABLE)) ||
(fa.fsx_xflags & (FS_XFLAG_APPEND | FS_XFLAG_IMMUTABLE))) &&
!capable(CAP_LINUX_IMMUTABLE)) {
ret = -EPERM;
simple_fill_fsxattr(&old_fa,
btrfs_inode_flags_to_xflags(binode->flags));
ret = vfs_ioc_fssetxattr_check(inode, &old_fa, &fa);
if (ret)
goto out_unlock;
}

if (fa.fsx_xflags & FS_XFLAG_SYNC)
binode->flags |= BTRFS_INODE_SYNC;
Expand Down
26 changes: 17 additions & 9 deletions fs/efivarfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,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;
Expand All @@ -129,6 +135,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))
Expand All @@ -140,9 +147,6 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
if (flags & ~FS_IMMUTABLE_FL)
return -EOPNOTSUPP;

if (!capable(CAP_LINUX_IMMUTABLE))
return -EPERM;

if (flags & FS_IMMUTABLE_FL)
i_flags |= S_IMMUTABLE;

Expand All @@ -152,12 +156,16 @@ efivarfs_ioc_setxflags(struct file *file, void __user *arg)
return error;

inode_lock(inode);

error = vfs_ioc_setflags_prepare(inode, oldflags, flags);
if (error)
goto out;

inode_set_flags(inode, i_flags, S_IMMUTABLE);
out:
inode_unlock(inode);

mnt_drop_write_file(file);

return 0;
return error;
}

static long
Expand Down
16 changes: 4 additions & 12 deletions fs/ext2/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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_prepare(inode, oldflags, flags);
if (ret) {
inode_unlock(inode);
goto setflags_out;
}

flags = flags & EXT2_FL_USER_MODIFIABLE;
Expand Down
51 changes: 13 additions & 38 deletions fs/ext4/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -312,16 +312,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_prepare(inode, oldflags, flags);
if (err)
goto flags_out;

/*
* The JOURNAL_DATA flag can only be changed by
Expand Down Expand Up @@ -741,28 +734,15 @@ static long ext4_ioctl_group_add(struct file *file,
return err;
}

static int ext4_ioctl_check_project(struct inode *inode, struct fsxattr *fa)
static void ext4_fill_fsxattr(struct inode *inode, struct fsxattr *fa)
{
/*
* Project Quota ID state is only allowed to change from within the init
* namespace. Enforce that restriction only if we are trying to change
* the quota ID state. Everything else is allowed in user namespaces.
*/
if (current_user_ns() == &init_user_ns)
return 0;

if (__kprojid_val(EXT4_I(inode)->i_projid) != fa->fsx_projid)
return -EINVAL;
struct ext4_inode_info *ei = EXT4_I(inode);

if (ext4_test_inode_flag(inode, EXT4_INODE_PROJINHERIT)) {
if (!(fa->fsx_xflags & FS_XFLAG_PROJINHERIT))
return -EINVAL;
} else {
if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT)
return -EINVAL;
}
simple_fill_fsxattr(fa, ext4_iflags_to_xflags(ei->i_flags &
EXT4_FL_USER_VISIBLE));

return 0;
if (ext4_has_feature_project(inode->i_sb))
fa->fsx_projid = from_kprojid(&init_user_ns, ei->i_projid);
}

long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
Expand Down Expand Up @@ -1139,13 +1119,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct fsxattr fa;

memset(&fa, 0, sizeof(struct fsxattr));
fa.fsx_xflags = ext4_iflags_to_xflags(ei->i_flags & EXT4_FL_USER_VISIBLE);

if (ext4_has_feature_project(inode->i_sb)) {
fa.fsx_projid = (__u32)from_kprojid(&init_user_ns,
EXT4_I(inode)->i_projid);
}
ext4_fill_fsxattr(inode, &fa);

if (copy_to_user((struct fsxattr __user *)arg,
&fa, sizeof(fa)))
Expand All @@ -1154,7 +1128,7 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
}
case EXT4_IOC_FSSETXATTR:
{
struct fsxattr fa;
struct fsxattr fa, old_fa;
int err;

if (copy_from_user(&fa, (struct fsxattr __user *)arg,
Expand All @@ -1177,7 +1151,8 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
return err;

inode_lock(inode);
err = ext4_ioctl_check_project(inode, &fa);
ext4_fill_fsxattr(inode, &old_fa);
err = vfs_ioc_fssetxattr_check(inode, &old_fa, &fa);
if (err)
goto out;
flags = (ei->i_flags & ~EXT4_FL_XFLAG_VISIBLE) |
Expand Down
42 changes: 29 additions & 13 deletions fs/gfs2/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -200,17 +209,19 @@ 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);
struct gfs2_sbd *sdp = GFS2_SB(inode);
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)
Expand All @@ -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_prepare(inode, oldflags, fsflags);
if (error)
goto out;

error = -EACCES;
if (!inode_owner_or_capable(inode))
goto out;
Expand Down Expand Up @@ -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)
Expand Down
21 changes: 12 additions & 9 deletions fs/hfsplus/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
}
Expand All @@ -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);
Expand All @@ -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_prepare(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)) {
Expand Down
Loading

0 comments on commit 5010fe9

Please sign in to comment.