Commit 5010fe9f authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'vfs-fix-ioctl-checking-3' of git://git.kernel.org/pub/scm/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
parents 8487d822 dbc77f31
Loading
Loading
Loading
Loading
+11 −19
Original line number Diff line number Diff line
@@ -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;
	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;
@@ -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;

@@ -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;
@@ -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;

@@ -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;
+17 −9
Original line number Diff line number Diff line
@@ -107,16 +107,22 @@ out_free:
	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;
@@ -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))
@@ -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;

@@ -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
+4 −12
Original line number Diff line number Diff line
@@ -60,19 +60,11 @@ 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)) {
		ret = vfs_ioc_setflags_prepare(inode, oldflags, flags);
		if (ret) {
			inode_unlock(inode);
				ret = -EPERM;
			goto setflags_out;
		}
		}

		flags = flags & EXT2_FL_USER_MODIFIABLE;
		flags |= oldflags & ~EXT2_FL_USER_MODIFIABLE;
+13 −38
Original line number Diff line number Diff line
@@ -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))
	err = vfs_ioc_setflags_prepare(inode, oldflags, flags);
	if (err)
		goto flags_out;
	}

	/*
	 * The JOURNAL_DATA flag can only be changed by
@@ -741,28 +734,15 @@ group_add_out:
	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)
@@ -1139,13 +1119,7 @@ resizefs_out:
	{
		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)))
@@ -1154,7 +1128,7 @@ resizefs_out:
	}
	case EXT4_IOC_FSSETXATTR:
	{
		struct fsxattr fa;
		struct fsxattr fa, old_fa;
		int err;

		if (copy_from_user(&fa, (struct fsxattr __user *)arg,
@@ -1177,7 +1151,8 @@ resizefs_out:
			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) |
+29 −13
Original line number Diff line number Diff line
@@ -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_prepare(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)
Loading