Commit 9fefd5db authored by Miklos Szeredi's avatar Miklos Szeredi
Browse files

xfs: convert to fileattr



Use the fileattr API to let the VFS handle locking, permission checking and
conversion.

Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
Cc: Darrick J. Wong <djwong@kernel.org>
parent 1f26b062
Loading
Loading
Loading
Loading
+0 −4
Original line number Diff line number Diff line
@@ -770,8 +770,6 @@ struct xfs_scrub_metadata {
/*
 * ioctl commands that are used by Linux filesystems
 */
#define XFS_IOC_GETXFLAGS	FS_IOC_GETFLAGS
#define XFS_IOC_SETXFLAGS	FS_IOC_SETFLAGS
#define XFS_IOC_GETVERSION	FS_IOC_GETVERSION

/*
@@ -782,8 +780,6 @@ struct xfs_scrub_metadata {
#define XFS_IOC_ALLOCSP		_IOW ('X', 10, struct xfs_flock64)
#define XFS_IOC_FREESP		_IOW ('X', 11, struct xfs_flock64)
#define XFS_IOC_DIOINFO		_IOR ('X', 30, struct dioattr)
#define XFS_IOC_FSGETXATTR	FS_IOC_FSGETXATTR
#define XFS_IOC_FSSETXATTR	FS_IOC_FSSETXATTR
#define XFS_IOC_ALLOCSP64	_IOW ('X', 36, struct xfs_flock64)
#define XFS_IOC_FREESP64	_IOW ('X', 37, struct xfs_flock64)
#define XFS_IOC_GETBMAP		_IOWR('X', 38, struct getbmap)
+62 −196
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@

#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/fileattr.h>

/*
 * xfs_find_handle maps from userspace xfs_fsop_handlereq structure to
@@ -1053,73 +1054,15 @@ xfs_ioc_ag_geometry(
 * Linux extended inode flags interface.
 */

STATIC unsigned int
xfs_merge_ioc_xflags(
	unsigned int	flags,
	unsigned int	start)
{
	unsigned int	xflags = start;

	if (flags & FS_IMMUTABLE_FL)
		xflags |= FS_XFLAG_IMMUTABLE;
	else
		xflags &= ~FS_XFLAG_IMMUTABLE;
	if (flags & FS_APPEND_FL)
		xflags |= FS_XFLAG_APPEND;
	else
		xflags &= ~FS_XFLAG_APPEND;
	if (flags & FS_SYNC_FL)
		xflags |= FS_XFLAG_SYNC;
	else
		xflags &= ~FS_XFLAG_SYNC;
	if (flags & FS_NOATIME_FL)
		xflags |= FS_XFLAG_NOATIME;
	else
		xflags &= ~FS_XFLAG_NOATIME;
	if (flags & FS_NODUMP_FL)
		xflags |= FS_XFLAG_NODUMP;
	else
		xflags &= ~FS_XFLAG_NODUMP;
	if (flags & FS_DAX_FL)
		xflags |= FS_XFLAG_DAX;
	else
		xflags &= ~FS_XFLAG_DAX;

	return xflags;
}

STATIC unsigned int
xfs_di2lxflags(
	uint16_t	di_flags,
	uint64_t	di_flags2)
{
	unsigned int	flags = 0;

	if (di_flags & XFS_DIFLAG_IMMUTABLE)
		flags |= FS_IMMUTABLE_FL;
	if (di_flags & XFS_DIFLAG_APPEND)
		flags |= FS_APPEND_FL;
	if (di_flags & XFS_DIFLAG_SYNC)
		flags |= FS_SYNC_FL;
	if (di_flags & XFS_DIFLAG_NOATIME)
		flags |= FS_NOATIME_FL;
	if (di_flags & XFS_DIFLAG_NODUMP)
		flags |= FS_NODUMP_FL;
	if (di_flags2 & XFS_DIFLAG2_DAX) {
		flags |= FS_DAX_FL;
	}
	return flags;
}

static void
xfs_fill_fsxattr(
	struct xfs_inode	*ip,
	bool			attr,
	struct fsxattr		*fa)
	int			whichfork,
	struct fileattr		*fa)
{
	struct xfs_ifork	*ifp = attr ? ip->i_afp : &ip->i_df;
	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);

	simple_fill_fsxattr(fa, xfs_ip2xflags(ip));
	fileattr_fill_xflags(fa, xfs_ip2xflags(ip));
	fa->fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog;
	fa->fsx_cowextsize = ip->i_d.di_cowextsize <<
			ip->i_mount->m_sb.sb_blocklog;
@@ -1131,19 +1074,33 @@ xfs_fill_fsxattr(
}

STATIC int
xfs_ioc_fsgetxattr(
xfs_ioc_fsgetxattra(
	xfs_inode_t		*ip,
	int			attr,
	void			__user *arg)
{
	struct fsxattr		fa;
	struct fileattr		fa;

	xfs_ilock(ip, XFS_ILOCK_SHARED);
	xfs_fill_fsxattr(ip, attr, &fa);
	xfs_fill_fsxattr(ip, XFS_ATTR_FORK, &fa);
	xfs_iunlock(ip, XFS_ILOCK_SHARED);

	return copy_fsxattr_to_user(&fa, arg);
}

int
xfs_fileattr_get(
	struct dentry		*dentry,
	struct fileattr		*fa)
{
	struct xfs_inode	*ip = XFS_I(d_inode(dentry));

	if (d_is_special(dentry))
		return -ENOTTY;

	xfs_ilock(ip, XFS_ILOCK_SHARED);
	xfs_fill_fsxattr(ip, XFS_DATA_FORK, fa);
	xfs_iunlock(ip, XFS_ILOCK_SHARED);

	if (copy_to_user(arg, &fa, sizeof(fa)))
		return -EFAULT;
	return 0;
}

@@ -1210,7 +1167,7 @@ static int
xfs_ioctl_setattr_xflags(
	struct xfs_trans	*tp,
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
	struct fileattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;
	uint64_t		di_flags2;
@@ -1253,7 +1210,7 @@ xfs_ioctl_setattr_xflags(
static void
xfs_ioctl_setattr_prepare_dax(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
	struct fileattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;
	struct inode            *inode = VFS_I(ip);
@@ -1280,10 +1237,9 @@ xfs_ioctl_setattr_prepare_dax(
 */
static struct xfs_trans *
xfs_ioctl_setattr_get_trans(
	struct file		*file,
	struct xfs_inode	*ip,
	struct xfs_dquot	*pdqp)
{
	struct xfs_inode	*ip = XFS_I(file_inode(file));
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
	int			error = -EROFS;
@@ -1299,24 +1255,11 @@ xfs_ioctl_setattr_get_trans(
	if (error)
		goto out_error;

	/*
	 * CAP_FOWNER overrides the following restrictions:
	 *
	 * The user ID of the calling process must be equal to the file owner
	 * ID, except in cases where the CAP_FSETID capability is applicable.
	 */
	if (!inode_owner_or_capable(file_mnt_user_ns(file), VFS_I(ip))) {
		error = -EPERM;
		goto out_cancel;
	}

	if (mp->m_flags & XFS_MOUNT_WSYNC)
		xfs_trans_set_sync(tp);

	return tp;

out_cancel:
	xfs_trans_cancel(tp);
out_error:
	return ERR_PTR(error);
}
@@ -1340,12 +1283,15 @@ xfs_ioctl_setattr_get_trans(
static int
xfs_ioctl_setattr_check_extsize(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
	struct fileattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;
	xfs_extlen_t		size;
	xfs_fsblock_t		extsize_fsb;

	if (!fa->fsx_valid)
		return 0;

	if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_df.if_nextents &&
	    ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize))
		return -EINVAL;
@@ -1390,12 +1336,15 @@ xfs_ioctl_setattr_check_extsize(
static int
xfs_ioctl_setattr_check_cowextsize(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
	struct fileattr		*fa)
{
	struct xfs_mount	*mp = ip->i_mount;
	xfs_extlen_t		size;
	xfs_fsblock_t		cowextsize_fsb;

	if (!fa->fsx_valid)
		return 0;

	if (!(fa->fsx_xflags & FS_XFLAG_COWEXTSIZE))
		return 0;

@@ -1422,8 +1371,11 @@ xfs_ioctl_setattr_check_cowextsize(
static int
xfs_ioctl_setattr_check_projid(
	struct xfs_inode	*ip,
	struct fsxattr		*fa)
	struct fileattr		*fa)
{
	if (!fa->fsx_valid)
		return 0;

	/* Disallow 32bit project ids if projid32bit feature is not enabled. */
	if (fa->fsx_projid > (uint16_t)-1 &&
	    !xfs_sb_version_hasprojid32bit(&ip->i_mount->m_sb))
@@ -1431,14 +1383,13 @@ xfs_ioctl_setattr_check_projid(
	return 0;
}

STATIC int
xfs_ioctl_setattr(
	struct file		*file,
	struct fsxattr		*fa)
int
xfs_fileattr_set(
	struct user_namespace	*mnt_userns,
	struct dentry		*dentry,
	struct fileattr		*fa)
{
	struct user_namespace	*mnt_userns = file_mnt_user_ns(file);
	struct xfs_inode	*ip = XFS_I(file_inode(file));
	struct fsxattr		old_fa;
	struct xfs_inode	*ip = XFS_I(d_inode(dentry));
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_trans	*tp;
	struct xfs_dquot	*pdqp = NULL;
@@ -1447,6 +1398,16 @@ xfs_ioctl_setattr(

	trace_xfs_ioctl_setattr(ip);

	if (d_is_special(dentry))
		return -ENOTTY;

	if (!fa->fsx_valid) {
		if (fa->flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL |
				  FS_NOATIME_FL | FS_NODUMP_FL |
				  FS_SYNC_FL | FS_DAX_FL | FS_PROJINHERIT_FL))
			return -EOPNOTSUPP;
	}

	error = xfs_ioctl_setattr_check_projid(ip, fa);
	if (error)
		return error;
@@ -1459,7 +1420,7 @@ xfs_ioctl_setattr(
	 * If the IDs do change before we take the ilock, we're covered
	 * because the i_*dquot fields will get updated anyway.
	 */
	if (XFS_IS_QUOTA_ON(mp)) {
	if (fa->fsx_valid && XFS_IS_QUOTA_ON(mp)) {
		error = xfs_qm_vop_dqalloc(ip, VFS_I(ip)->i_uid,
				VFS_I(ip)->i_gid, fa->fsx_projid,
				XFS_QMOPT_PQUOTA, NULL, NULL, &pdqp);
@@ -1469,17 +1430,12 @@ xfs_ioctl_setattr(

	xfs_ioctl_setattr_prepare_dax(ip, fa);

	tp = xfs_ioctl_setattr_get_trans(file, pdqp);
	tp = xfs_ioctl_setattr_get_trans(ip, pdqp);
	if (IS_ERR(tp)) {
		error = PTR_ERR(tp);
		goto error_free_dquots;
	}

	xfs_fill_fsxattr(ip, false, &old_fa);
	error = vfs_ioc_fssetxattr_check(VFS_I(ip), &old_fa, fa);
	if (error)
		goto error_trans_cancel;

	error = xfs_ioctl_setattr_check_extsize(ip, fa);
	if (error)
		goto error_trans_cancel;
@@ -1492,6 +1448,8 @@ xfs_ioctl_setattr(
	if (error)
		goto error_trans_cancel;

	if (!fa->fsx_valid)
		goto skip_xattr;
	/*
	 * Change file ownership.  Must be the owner or privileged.  CAP_FSETID
	 * overrides the following restrictions:
@@ -1529,6 +1487,7 @@ xfs_ioctl_setattr(
	else
		ip->i_d.di_cowextsize = 0;

skip_xattr:
	error = xfs_trans_commit(tp);

	/*
@@ -1546,91 +1505,6 @@ xfs_ioctl_setattr(
	return error;
}

STATIC int
xfs_ioc_fssetxattr(
	struct file		*filp,
	void			__user *arg)
{
	struct fsxattr		fa;
	int error;

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

	error = mnt_want_write_file(filp);
	if (error)
		return error;
	error = xfs_ioctl_setattr(filp, &fa);
	mnt_drop_write_file(filp);
	return error;
}

STATIC int
xfs_ioc_getxflags(
	xfs_inode_t		*ip,
	void			__user *arg)
{
	unsigned int		flags;

	flags = xfs_di2lxflags(ip->i_d.di_flags, ip->i_d.di_flags2);
	if (copy_to_user(arg, &flags, sizeof(flags)))
		return -EFAULT;
	return 0;
}

STATIC int
xfs_ioc_setxflags(
	struct xfs_inode	*ip,
	struct file		*filp,
	void			__user *arg)
{
	struct xfs_trans	*tp;
	struct fsxattr		fa;
	struct fsxattr		old_fa;
	unsigned int		flags;
	int			error;

	if (copy_from_user(&flags, arg, sizeof(flags)))
		return -EFAULT;

	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \
		      FS_NOATIME_FL | FS_NODUMP_FL | \
		      FS_SYNC_FL | FS_DAX_FL))
		return -EOPNOTSUPP;

	fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip));

	error = mnt_want_write_file(filp);
	if (error)
		return error;

	xfs_ioctl_setattr_prepare_dax(ip, &fa);

	tp = xfs_ioctl_setattr_get_trans(filp, NULL);
	if (IS_ERR(tp)) {
		error = PTR_ERR(tp);
		goto out_drop_write;
	}

	xfs_fill_fsxattr(ip, false, &old_fa);
	error = vfs_ioc_fssetxattr_check(VFS_I(ip), &old_fa, &fa);
	if (error) {
		xfs_trans_cancel(tp);
		goto out_drop_write;
	}

	error = xfs_ioctl_setattr_xflags(tp, ip, &fa);
	if (error) {
		xfs_trans_cancel(tp);
		goto out_drop_write;
	}

	error = xfs_trans_commit(tp);
out_drop_write:
	mnt_drop_write_file(filp);
	return error;
}

static bool
xfs_getbmap_format(
	struct kgetbmap		*p,
@@ -2137,16 +2011,8 @@ xfs_file_ioctl(
	case XFS_IOC_GETVERSION:
		return put_user(inode->i_generation, (int __user *)arg);

	case XFS_IOC_FSGETXATTR:
		return xfs_ioc_fsgetxattr(ip, 0, arg);
	case XFS_IOC_FSGETXATTRA:
		return xfs_ioc_fsgetxattr(ip, 1, arg);
	case XFS_IOC_FSSETXATTR:
		return xfs_ioc_fssetxattr(filp, arg);
	case XFS_IOC_GETXFLAGS:
		return xfs_ioc_getxflags(ip, arg);
	case XFS_IOC_SETXFLAGS:
		return xfs_ioc_setxflags(ip, filp, arg);
		return xfs_ioc_fsgetxattra(ip, arg);

	case XFS_IOC_GETBMAP:
	case XFS_IOC_GETBMAPA:
+11 −0
Original line number Diff line number Diff line
@@ -47,6 +47,17 @@ xfs_handle_to_dentry(
	void __user		*uhandle,
	u32			hlen);

extern int
xfs_fileattr_get(
	struct dentry		*dentry,
	struct fileattr		*fa);

extern int
xfs_fileattr_set(
	struct user_namespace	*mnt_userns,
	struct dentry		*dentry,
	struct fileattr		*fa);

extern long
xfs_file_ioctl(
	struct file		*filp,
+0 −2
Original line number Diff line number Diff line
@@ -484,8 +484,6 @@ xfs_file_compat_ioctl(
	}
#endif
	/* long changes size, but xfs only copiese out 32 bits */
	case XFS_IOC_GETXFLAGS_32:
	case XFS_IOC_SETXFLAGS_32:
	case XFS_IOC_GETVERSION_32:
		cmd = _NATIVE_IOC(cmd, long);
		return xfs_file_ioctl(filp, cmd, p);
+0 −2
Original line number Diff line number Diff line
@@ -17,8 +17,6 @@
 */

/* stock kernel-level ioctls we support */
#define XFS_IOC_GETXFLAGS_32	FS_IOC32_GETFLAGS
#define XFS_IOC_SETXFLAGS_32	FS_IOC32_SETFLAGS
#define XFS_IOC_GETVERSION_32	FS_IOC32_GETVERSION

/*
Loading