|
|
|
@ -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 @@ out_error:
|
|
|
|
|
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 @@ error_free_dquots:
|
|
|
|
|
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:
|
|
|
|
|