Loading fs/xfs/xfs_ioctl.c +231 −220 Original line number Diff line number Diff line Loading @@ -987,210 +987,245 @@ xfs_diflags_to_linux( inode->i_flags &= ~S_NOATIME; } #define FSX_PROJID 1 #define FSX_EXTSIZE 2 #define FSX_XFLAGS 4 #define FSX_NONBLOCK 8 STATIC int xfs_ioctl_setattr( xfs_inode_t *ip, struct fsxattr *fa, int mask) static int xfs_ioctl_setattr_xflags( struct xfs_trans *tp, struct xfs_inode *ip, struct fsxattr *fa) { struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; unsigned int lock_flags = 0; struct xfs_dquot *udqp = NULL; struct xfs_dquot *pdqp = NULL; struct xfs_dquot *olddquot = NULL; int code; trace_xfs_ioctl_setattr(ip); if (mp->m_flags & XFS_MOUNT_RDONLY) return -EROFS; if (XFS_FORCED_SHUTDOWN(mp)) return -EIO; /* Can't change realtime flag if any extents are allocated. */ if ((ip->i_d.di_nextents || ip->i_delayed_blks) && XFS_IS_REALTIME_INODE(ip) != (fa->fsx_xflags & XFS_XFLAG_REALTIME)) return -EINVAL; /* * Disallow 32bit project ids when projid32bit feature is not enabled. */ if ((mask & FSX_PROJID) && (fa->fsx_projid > (__uint16_t)-1) && !xfs_sb_version_hasprojid32bit(&ip->i_mount->m_sb)) /* If realtime flag is set then must have realtime device */ if (fa->fsx_xflags & XFS_XFLAG_REALTIME) { if (mp->m_sb.sb_rblocks == 0 || mp->m_sb.sb_rextsize == 0 || (ip->i_d.di_extsize % mp->m_sb.sb_rextsize)) return -EINVAL; } /* * If disk quotas is on, we make sure that the dquots do exist on disk, * before we start any other transactions. Trying to do this later * is messy. We don't care to take a readlock to look at the ids * in inode here, because we can't hold it across the trans_reserve. * If the IDs do change before we take the ilock, we're covered * because the i_*dquot fields will get updated anyway. * Can't modify an immutable/append-only file unless * we have appropriate permission. */ if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) { code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid, ip->i_d.di_gid, fa->fsx_projid, XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp); if (code) return code; if (((ip->i_d.di_flags & (XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_APPEND)) || (fa->fsx_xflags & (XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) && !capable(CAP_LINUX_IMMUTABLE)) return -EPERM; xfs_set_diflags(ip, fa->fsx_xflags); xfs_diflags_to_linux(ip); xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); XFS_STATS_INC(xs_ig_attrchg); return 0; } /* * For the other attributes, we acquire the inode lock and * first do an error checking pass. * Set up the transaction structure for the setattr operation, checking that we * have permission to do so. On success, return a clean transaction and the * inode locked exclusively ready for further operation specific checks. On * failure, return an error without modifying or locking the inode. */ static struct xfs_trans * xfs_ioctl_setattr_get_trans( struct xfs_inode *ip) { struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; int error; if (mp->m_flags & XFS_MOUNT_RDONLY) return ERR_PTR(-EROFS); if (XFS_FORCED_SHUTDOWN(mp)) return ERR_PTR(-EIO); tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE); code = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0); if (code) goto error_return; error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0); if (error) goto out_cancel; lock_flags = XFS_ILOCK_EXCL; xfs_ilock(ip, lock_flags); xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); /* * 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. * 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(VFS_I(ip))) { code = -EPERM; goto error_return; error = -EPERM; goto out_cancel; } /* * Do a quota reservation only if projid is actually going to change. * Only allow changing of projid from init_user_ns since it is a * non user namespace aware identifier. */ if (mask & FSX_PROJID) { if (current_user_ns() != &init_user_ns) { code = -EINVAL; goto error_return; } if (mp->m_flags & XFS_MOUNT_WSYNC) xfs_trans_set_sync(tp); if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) && xfs_get_projid(ip) != fa->fsx_projid) { ASSERT(tp); code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL, pdqp, capable(CAP_FOWNER) ? XFS_QMOPT_FORCE_RES : 0); if (code) /* out of quota */ goto error_return; } } return tp; if (mask & FSX_EXTSIZE) { /* * Can't change extent size if any extents are allocated. */ if (ip->i_d.di_nextents && ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize)) { code = -EINVAL; /* EFBIG? */ goto error_return; out_cancel: xfs_trans_cancel(tp, 0); return ERR_PTR(error); } /* * Extent size must be a multiple of the appropriate block * size, if set at all. It must also be smaller than the * maximum extent size supported by the filesystem. * extent size hint validation is somewhat cumbersome. Rules are: * * Also, for non-realtime files, limit the extent size hint to * half the size of the AGs in the filesystem so alignment * doesn't result in extents larger than an AG. * 1. extent size hint is only valid for directories and regular files * 2. XFS_XFLAG_EXTSIZE is only valid for regular files * 3. XFS_XFLAG_EXTSZINHERIT is only valid for directories. * 4. can only be changed on regular files if no extents are allocated * 5. can be changed on directories at any time * 6. extsize hint of 0 turns off hints, clears inode flags. * 7. Extent size must be a multiple of the appropriate block size. * 8. for non-realtime files, the extent size hint must be limited * to half the AG size to avoid alignment extending the extent beyond the * limits of the AG. */ int xfs_ioctl_setattr_check_extsize( struct xfs_inode *ip, struct fsxattr *fa) { struct xfs_mount *mp = ip->i_mount; if ((fa->fsx_xflags & XFS_XFLAG_EXTSIZE) && !S_ISREG(ip->i_d.di_mode)) return -EINVAL; if ((fa->fsx_xflags & XFS_XFLAG_EXTSZINHERIT) && !S_ISDIR(ip->i_d.di_mode)) return -EINVAL; if (S_ISREG(ip->i_d.di_mode) && ip->i_d.di_nextents && ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize)) return -EINVAL; if (fa->fsx_extsize != 0) { xfs_extlen_t size; xfs_fsblock_t extsize_fsb; extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize); if (extsize_fsb > MAXEXTLEN) { code = -EINVAL; goto error_return; } if (extsize_fsb > MAXEXTLEN) return -EINVAL; if (XFS_IS_REALTIME_INODE(ip) || ((mask & FSX_XFLAGS) && (fa->fsx_xflags & XFS_XFLAG_REALTIME))) { size = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog; (fa->fsx_xflags & XFS_XFLAG_REALTIME)) { size = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog; } else { size = mp->m_sb.sb_blocksize; if (extsize_fsb > mp->m_sb.sb_agblocks / 2) { code = -EINVAL; goto error_return; } if (extsize_fsb > mp->m_sb.sb_agblocks / 2) return -EINVAL; } if (fa->fsx_extsize % size) { code = -EINVAL; goto error_return; } } if (fa->fsx_extsize % size) return -EINVAL; } else fa->fsx_xflags &= ~(XFS_XFLAG_EXTSIZE | XFS_XFLAG_EXTSZINHERIT); return 0; } int xfs_ioctl_setattr_check_projid( struct xfs_inode *ip, struct fsxattr *fa) { /* 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)) return -EINVAL; if (mask & FSX_XFLAGS) { /* * Can't change realtime flag if any extents are allocated. * 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 ((ip->i_d.di_nextents || ip->i_delayed_blks) && (XFS_IS_REALTIME_INODE(ip)) != (fa->fsx_xflags & XFS_XFLAG_REALTIME)) { code = -EINVAL; /* EFBIG? */ goto error_return; if (current_user_ns() == &init_user_ns) return 0; if (xfs_get_projid(ip) != fa->fsx_projid) return -EINVAL; if ((fa->fsx_xflags & XFS_XFLAG_PROJINHERIT) != (ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT)) return -EINVAL; return 0; } STATIC int xfs_ioctl_setattr( xfs_inode_t *ip, struct fsxattr *fa) { struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; struct xfs_dquot *udqp = NULL; struct xfs_dquot *pdqp = NULL; struct xfs_dquot *olddquot = NULL; int code; trace_xfs_ioctl_setattr(ip); code = xfs_ioctl_setattr_check_projid(ip, fa); if (code) return code; /* * If realtime flag is set then must have realtime data. * If disk quotas is on, we make sure that the dquots do exist on disk, * before we start any other transactions. Trying to do this later * is messy. We don't care to take a readlock to look at the ids * in inode here, because we can't hold it across the trans_reserve. * If the IDs do change before we take the ilock, we're covered * because the i_*dquot fields will get updated anyway. */ if ((fa->fsx_xflags & XFS_XFLAG_REALTIME)) { if ((mp->m_sb.sb_rblocks == 0) || (mp->m_sb.sb_rextsize == 0) || (ip->i_d.di_extsize % mp->m_sb.sb_rextsize)) { code = -EINVAL; goto error_return; } if (XFS_IS_QUOTA_ON(mp)) { code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid, ip->i_d.di_gid, fa->fsx_projid, XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp); if (code) return code; } /* * Can't modify an immutable/append-only file unless * we have appropriate permission. */ if ((ip->i_d.di_flags & (XFS_DIFLAG_IMMUTABLE|XFS_DIFLAG_APPEND) || (fa->fsx_xflags & (XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) && !capable(CAP_LINUX_IMMUTABLE)) { code = -EPERM; goto error_return; tp = xfs_ioctl_setattr_get_trans(ip); if (IS_ERR(tp)) { code = PTR_ERR(tp); goto error_free_dquots; } if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) && xfs_get_projid(ip) != fa->fsx_projid) { code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL, pdqp, capable(CAP_FOWNER) ? XFS_QMOPT_FORCE_RES : 0); if (code) /* out of quota */ goto error_trans_cancel; } xfs_trans_ijoin(tp, ip, 0); code = xfs_ioctl_setattr_check_extsize(ip, fa); if (code) goto error_trans_cancel; code = xfs_ioctl_setattr_xflags(tp, ip, fa); if (code) goto error_trans_cancel; /* * Change file ownership. Must be the owner or privileged. */ if (mask & FSX_PROJID) { /* * CAP_FSETID overrides the following restrictions: * Change file ownership. Must be the owner or privileged. CAP_FSETID * overrides the following restrictions: * * The set-user-ID and set-group-ID bits of a file will be * cleared upon successful return from chown() * The set-user-ID and set-group-ID bits of a file will be cleared upon * successful return from chown() */ if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) && !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID)) ip->i_d.di_mode &= ~(S_ISUID|S_ISGID); /* * Change the ownerships and register quota modifications * in the transaction. */ /* Change the ownerships and register project quota modifications */ if (xfs_get_projid(ip) != fa->fsx_projid) { if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) { olddquot = xfs_qm_vop_chown(tp, ip, Loading @@ -1200,46 +1235,17 @@ xfs_ioctl_setattr( xfs_set_projid(ip, fa->fsx_projid); } } if (mask & FSX_XFLAGS) { xfs_set_diflags(ip, fa->fsx_xflags); xfs_diflags_to_linux(ip); } /* * Only set the extent size hint if we've already determined that the * extent size hint should be set on the inode. If no extent size flags * are set on the inode then unconditionally clear the extent size hint. */ if (mask & FSX_EXTSIZE) { int extsize = 0; if (ip->i_d.di_flags & (XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT)) extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog; ip->i_d.di_extsize = extsize; } xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); XFS_STATS_INC(xs_ig_attrchg); if (ip->i_d.di_flags & (XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT)) ip->i_d.di_extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog; else ip->i_d.di_extsize = 0; /* * If this is a synchronous mount, make sure that the * transaction goes to disk before returning to the user. * This is slightly sub-optimal in that truncates require * two sync transactions instead of one for wsync filesystems. * One for the truncate and one for the timestamps since we * don't want to change the timestamps unless we're sure the * truncate worked. Truncates are less than 1% of the laddis * mix so this probably isn't worth the trouble to optimize. */ if (mp->m_flags & XFS_MOUNT_WSYNC) xfs_trans_set_sync(tp); code = xfs_trans_commit(tp, 0); xfs_iunlock(ip, lock_flags); /* * Release any dquot(s) the inode had kept before chown. Loading @@ -1250,12 +1256,11 @@ xfs_ioctl_setattr( return code; error_return: error_trans_cancel: xfs_trans_cancel(tp, 0); error_free_dquots: xfs_qm_dqrele(udqp); xfs_qm_dqrele(pdqp); xfs_trans_cancel(tp, 0); if (lock_flags) xfs_iunlock(ip, lock_flags); return code; } Loading @@ -1266,20 +1271,15 @@ xfs_ioc_fssetxattr( void __user *arg) { struct fsxattr fa; unsigned int mask; int error; if (copy_from_user(&fa, arg, sizeof(fa))) return -EFAULT; mask = FSX_XFLAGS | FSX_EXTSIZE | FSX_PROJID; if (filp->f_flags & (O_NDELAY|O_NONBLOCK)) mask |= FSX_NONBLOCK; error = mnt_want_write_file(filp); if (error) return error; error = xfs_ioctl_setattr(ip, &fa, mask); error = xfs_ioctl_setattr(ip, &fa); mnt_drop_write_file(filp); return error; } Loading @@ -1299,13 +1299,13 @@ xfs_ioc_getxflags( STATIC int xfs_ioc_setxflags( xfs_inode_t *ip, struct xfs_inode *ip, struct file *filp, void __user *arg) { struct xfs_trans *tp; struct fsxattr fa; unsigned int flags; unsigned int mask; int error; if (copy_from_user(&flags, arg, sizeof(flags))) Loading @@ -1316,15 +1316,26 @@ xfs_ioc_setxflags( FS_SYNC_FL)) return -EOPNOTSUPP; mask = FSX_XFLAGS; if (filp->f_flags & (O_NDELAY|O_NONBLOCK)) mask |= FSX_NONBLOCK; fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip)); error = mnt_want_write_file(filp); if (error) return error; error = xfs_ioctl_setattr(ip, &fa, mask); tp = xfs_ioctl_setattr_get_trans(ip); if (IS_ERR(tp)) { error = PTR_ERR(tp); goto out_drop_write; } error = xfs_ioctl_setattr_xflags(tp, ip, &fa); if (error) { xfs_trans_cancel(tp, 0); goto out_drop_write; } error = xfs_trans_commit(tp, 0); out_drop_write: mnt_drop_write_file(filp); return error; } Loading Loading
fs/xfs/xfs_ioctl.c +231 −220 Original line number Diff line number Diff line Loading @@ -987,210 +987,245 @@ xfs_diflags_to_linux( inode->i_flags &= ~S_NOATIME; } #define FSX_PROJID 1 #define FSX_EXTSIZE 2 #define FSX_XFLAGS 4 #define FSX_NONBLOCK 8 STATIC int xfs_ioctl_setattr( xfs_inode_t *ip, struct fsxattr *fa, int mask) static int xfs_ioctl_setattr_xflags( struct xfs_trans *tp, struct xfs_inode *ip, struct fsxattr *fa) { struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; unsigned int lock_flags = 0; struct xfs_dquot *udqp = NULL; struct xfs_dquot *pdqp = NULL; struct xfs_dquot *olddquot = NULL; int code; trace_xfs_ioctl_setattr(ip); if (mp->m_flags & XFS_MOUNT_RDONLY) return -EROFS; if (XFS_FORCED_SHUTDOWN(mp)) return -EIO; /* Can't change realtime flag if any extents are allocated. */ if ((ip->i_d.di_nextents || ip->i_delayed_blks) && XFS_IS_REALTIME_INODE(ip) != (fa->fsx_xflags & XFS_XFLAG_REALTIME)) return -EINVAL; /* * Disallow 32bit project ids when projid32bit feature is not enabled. */ if ((mask & FSX_PROJID) && (fa->fsx_projid > (__uint16_t)-1) && !xfs_sb_version_hasprojid32bit(&ip->i_mount->m_sb)) /* If realtime flag is set then must have realtime device */ if (fa->fsx_xflags & XFS_XFLAG_REALTIME) { if (mp->m_sb.sb_rblocks == 0 || mp->m_sb.sb_rextsize == 0 || (ip->i_d.di_extsize % mp->m_sb.sb_rextsize)) return -EINVAL; } /* * If disk quotas is on, we make sure that the dquots do exist on disk, * before we start any other transactions. Trying to do this later * is messy. We don't care to take a readlock to look at the ids * in inode here, because we can't hold it across the trans_reserve. * If the IDs do change before we take the ilock, we're covered * because the i_*dquot fields will get updated anyway. * Can't modify an immutable/append-only file unless * we have appropriate permission. */ if (XFS_IS_QUOTA_ON(mp) && (mask & FSX_PROJID)) { code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid, ip->i_d.di_gid, fa->fsx_projid, XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp); if (code) return code; if (((ip->i_d.di_flags & (XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_APPEND)) || (fa->fsx_xflags & (XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) && !capable(CAP_LINUX_IMMUTABLE)) return -EPERM; xfs_set_diflags(ip, fa->fsx_xflags); xfs_diflags_to_linux(ip); xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); XFS_STATS_INC(xs_ig_attrchg); return 0; } /* * For the other attributes, we acquire the inode lock and * first do an error checking pass. * Set up the transaction structure for the setattr operation, checking that we * have permission to do so. On success, return a clean transaction and the * inode locked exclusively ready for further operation specific checks. On * failure, return an error without modifying or locking the inode. */ static struct xfs_trans * xfs_ioctl_setattr_get_trans( struct xfs_inode *ip) { struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; int error; if (mp->m_flags & XFS_MOUNT_RDONLY) return ERR_PTR(-EROFS); if (XFS_FORCED_SHUTDOWN(mp)) return ERR_PTR(-EIO); tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_NOT_SIZE); code = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0); if (code) goto error_return; error = xfs_trans_reserve(tp, &M_RES(mp)->tr_ichange, 0, 0); if (error) goto out_cancel; lock_flags = XFS_ILOCK_EXCL; xfs_ilock(ip, lock_flags); xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); /* * 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. * 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(VFS_I(ip))) { code = -EPERM; goto error_return; error = -EPERM; goto out_cancel; } /* * Do a quota reservation only if projid is actually going to change. * Only allow changing of projid from init_user_ns since it is a * non user namespace aware identifier. */ if (mask & FSX_PROJID) { if (current_user_ns() != &init_user_ns) { code = -EINVAL; goto error_return; } if (mp->m_flags & XFS_MOUNT_WSYNC) xfs_trans_set_sync(tp); if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) && xfs_get_projid(ip) != fa->fsx_projid) { ASSERT(tp); code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL, pdqp, capable(CAP_FOWNER) ? XFS_QMOPT_FORCE_RES : 0); if (code) /* out of quota */ goto error_return; } } return tp; if (mask & FSX_EXTSIZE) { /* * Can't change extent size if any extents are allocated. */ if (ip->i_d.di_nextents && ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize)) { code = -EINVAL; /* EFBIG? */ goto error_return; out_cancel: xfs_trans_cancel(tp, 0); return ERR_PTR(error); } /* * Extent size must be a multiple of the appropriate block * size, if set at all. It must also be smaller than the * maximum extent size supported by the filesystem. * extent size hint validation is somewhat cumbersome. Rules are: * * Also, for non-realtime files, limit the extent size hint to * half the size of the AGs in the filesystem so alignment * doesn't result in extents larger than an AG. * 1. extent size hint is only valid for directories and regular files * 2. XFS_XFLAG_EXTSIZE is only valid for regular files * 3. XFS_XFLAG_EXTSZINHERIT is only valid for directories. * 4. can only be changed on regular files if no extents are allocated * 5. can be changed on directories at any time * 6. extsize hint of 0 turns off hints, clears inode flags. * 7. Extent size must be a multiple of the appropriate block size. * 8. for non-realtime files, the extent size hint must be limited * to half the AG size to avoid alignment extending the extent beyond the * limits of the AG. */ int xfs_ioctl_setattr_check_extsize( struct xfs_inode *ip, struct fsxattr *fa) { struct xfs_mount *mp = ip->i_mount; if ((fa->fsx_xflags & XFS_XFLAG_EXTSIZE) && !S_ISREG(ip->i_d.di_mode)) return -EINVAL; if ((fa->fsx_xflags & XFS_XFLAG_EXTSZINHERIT) && !S_ISDIR(ip->i_d.di_mode)) return -EINVAL; if (S_ISREG(ip->i_d.di_mode) && ip->i_d.di_nextents && ((ip->i_d.di_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize)) return -EINVAL; if (fa->fsx_extsize != 0) { xfs_extlen_t size; xfs_fsblock_t extsize_fsb; extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize); if (extsize_fsb > MAXEXTLEN) { code = -EINVAL; goto error_return; } if (extsize_fsb > MAXEXTLEN) return -EINVAL; if (XFS_IS_REALTIME_INODE(ip) || ((mask & FSX_XFLAGS) && (fa->fsx_xflags & XFS_XFLAG_REALTIME))) { size = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog; (fa->fsx_xflags & XFS_XFLAG_REALTIME)) { size = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog; } else { size = mp->m_sb.sb_blocksize; if (extsize_fsb > mp->m_sb.sb_agblocks / 2) { code = -EINVAL; goto error_return; } if (extsize_fsb > mp->m_sb.sb_agblocks / 2) return -EINVAL; } if (fa->fsx_extsize % size) { code = -EINVAL; goto error_return; } } if (fa->fsx_extsize % size) return -EINVAL; } else fa->fsx_xflags &= ~(XFS_XFLAG_EXTSIZE | XFS_XFLAG_EXTSZINHERIT); return 0; } int xfs_ioctl_setattr_check_projid( struct xfs_inode *ip, struct fsxattr *fa) { /* 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)) return -EINVAL; if (mask & FSX_XFLAGS) { /* * Can't change realtime flag if any extents are allocated. * 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 ((ip->i_d.di_nextents || ip->i_delayed_blks) && (XFS_IS_REALTIME_INODE(ip)) != (fa->fsx_xflags & XFS_XFLAG_REALTIME)) { code = -EINVAL; /* EFBIG? */ goto error_return; if (current_user_ns() == &init_user_ns) return 0; if (xfs_get_projid(ip) != fa->fsx_projid) return -EINVAL; if ((fa->fsx_xflags & XFS_XFLAG_PROJINHERIT) != (ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT)) return -EINVAL; return 0; } STATIC int xfs_ioctl_setattr( xfs_inode_t *ip, struct fsxattr *fa) { struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; struct xfs_dquot *udqp = NULL; struct xfs_dquot *pdqp = NULL; struct xfs_dquot *olddquot = NULL; int code; trace_xfs_ioctl_setattr(ip); code = xfs_ioctl_setattr_check_projid(ip, fa); if (code) return code; /* * If realtime flag is set then must have realtime data. * If disk quotas is on, we make sure that the dquots do exist on disk, * before we start any other transactions. Trying to do this later * is messy. We don't care to take a readlock to look at the ids * in inode here, because we can't hold it across the trans_reserve. * If the IDs do change before we take the ilock, we're covered * because the i_*dquot fields will get updated anyway. */ if ((fa->fsx_xflags & XFS_XFLAG_REALTIME)) { if ((mp->m_sb.sb_rblocks == 0) || (mp->m_sb.sb_rextsize == 0) || (ip->i_d.di_extsize % mp->m_sb.sb_rextsize)) { code = -EINVAL; goto error_return; } if (XFS_IS_QUOTA_ON(mp)) { code = xfs_qm_vop_dqalloc(ip, ip->i_d.di_uid, ip->i_d.di_gid, fa->fsx_projid, XFS_QMOPT_PQUOTA, &udqp, NULL, &pdqp); if (code) return code; } /* * Can't modify an immutable/append-only file unless * we have appropriate permission. */ if ((ip->i_d.di_flags & (XFS_DIFLAG_IMMUTABLE|XFS_DIFLAG_APPEND) || (fa->fsx_xflags & (XFS_XFLAG_IMMUTABLE | XFS_XFLAG_APPEND))) && !capable(CAP_LINUX_IMMUTABLE)) { code = -EPERM; goto error_return; tp = xfs_ioctl_setattr_get_trans(ip); if (IS_ERR(tp)) { code = PTR_ERR(tp); goto error_free_dquots; } if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) && xfs_get_projid(ip) != fa->fsx_projid) { code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL, pdqp, capable(CAP_FOWNER) ? XFS_QMOPT_FORCE_RES : 0); if (code) /* out of quota */ goto error_trans_cancel; } xfs_trans_ijoin(tp, ip, 0); code = xfs_ioctl_setattr_check_extsize(ip, fa); if (code) goto error_trans_cancel; code = xfs_ioctl_setattr_xflags(tp, ip, fa); if (code) goto error_trans_cancel; /* * Change file ownership. Must be the owner or privileged. */ if (mask & FSX_PROJID) { /* * CAP_FSETID overrides the following restrictions: * Change file ownership. Must be the owner or privileged. CAP_FSETID * overrides the following restrictions: * * The set-user-ID and set-group-ID bits of a file will be * cleared upon successful return from chown() * The set-user-ID and set-group-ID bits of a file will be cleared upon * successful return from chown() */ if ((ip->i_d.di_mode & (S_ISUID|S_ISGID)) && !capable_wrt_inode_uidgid(VFS_I(ip), CAP_FSETID)) ip->i_d.di_mode &= ~(S_ISUID|S_ISGID); /* * Change the ownerships and register quota modifications * in the transaction. */ /* Change the ownerships and register project quota modifications */ if (xfs_get_projid(ip) != fa->fsx_projid) { if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp)) { olddquot = xfs_qm_vop_chown(tp, ip, Loading @@ -1200,46 +1235,17 @@ xfs_ioctl_setattr( xfs_set_projid(ip, fa->fsx_projid); } } if (mask & FSX_XFLAGS) { xfs_set_diflags(ip, fa->fsx_xflags); xfs_diflags_to_linux(ip); } /* * Only set the extent size hint if we've already determined that the * extent size hint should be set on the inode. If no extent size flags * are set on the inode then unconditionally clear the extent size hint. */ if (mask & FSX_EXTSIZE) { int extsize = 0; if (ip->i_d.di_flags & (XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT)) extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog; ip->i_d.di_extsize = extsize; } xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); XFS_STATS_INC(xs_ig_attrchg); if (ip->i_d.di_flags & (XFS_DIFLAG_EXTSIZE | XFS_DIFLAG_EXTSZINHERIT)) ip->i_d.di_extsize = fa->fsx_extsize >> mp->m_sb.sb_blocklog; else ip->i_d.di_extsize = 0; /* * If this is a synchronous mount, make sure that the * transaction goes to disk before returning to the user. * This is slightly sub-optimal in that truncates require * two sync transactions instead of one for wsync filesystems. * One for the truncate and one for the timestamps since we * don't want to change the timestamps unless we're sure the * truncate worked. Truncates are less than 1% of the laddis * mix so this probably isn't worth the trouble to optimize. */ if (mp->m_flags & XFS_MOUNT_WSYNC) xfs_trans_set_sync(tp); code = xfs_trans_commit(tp, 0); xfs_iunlock(ip, lock_flags); /* * Release any dquot(s) the inode had kept before chown. Loading @@ -1250,12 +1256,11 @@ xfs_ioctl_setattr( return code; error_return: error_trans_cancel: xfs_trans_cancel(tp, 0); error_free_dquots: xfs_qm_dqrele(udqp); xfs_qm_dqrele(pdqp); xfs_trans_cancel(tp, 0); if (lock_flags) xfs_iunlock(ip, lock_flags); return code; } Loading @@ -1266,20 +1271,15 @@ xfs_ioc_fssetxattr( void __user *arg) { struct fsxattr fa; unsigned int mask; int error; if (copy_from_user(&fa, arg, sizeof(fa))) return -EFAULT; mask = FSX_XFLAGS | FSX_EXTSIZE | FSX_PROJID; if (filp->f_flags & (O_NDELAY|O_NONBLOCK)) mask |= FSX_NONBLOCK; error = mnt_want_write_file(filp); if (error) return error; error = xfs_ioctl_setattr(ip, &fa, mask); error = xfs_ioctl_setattr(ip, &fa); mnt_drop_write_file(filp); return error; } Loading @@ -1299,13 +1299,13 @@ xfs_ioc_getxflags( STATIC int xfs_ioc_setxflags( xfs_inode_t *ip, struct xfs_inode *ip, struct file *filp, void __user *arg) { struct xfs_trans *tp; struct fsxattr fa; unsigned int flags; unsigned int mask; int error; if (copy_from_user(&flags, arg, sizeof(flags))) Loading @@ -1316,15 +1316,26 @@ xfs_ioc_setxflags( FS_SYNC_FL)) return -EOPNOTSUPP; mask = FSX_XFLAGS; if (filp->f_flags & (O_NDELAY|O_NONBLOCK)) mask |= FSX_NONBLOCK; fa.fsx_xflags = xfs_merge_ioc_xflags(flags, xfs_ip2xflags(ip)); error = mnt_want_write_file(filp); if (error) return error; error = xfs_ioctl_setattr(ip, &fa, mask); tp = xfs_ioctl_setattr_get_trans(ip); if (IS_ERR(tp)) { error = PTR_ERR(tp); goto out_drop_write; } error = xfs_ioctl_setattr_xflags(tp, ip, &fa); if (error) { xfs_trans_cancel(tp, 0); goto out_drop_write; } error = xfs_trans_commit(tp, 0); out_drop_write: mnt_drop_write_file(filp); return error; } Loading