diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2019-07-01 11:25:35 -0400 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2019-07-01 11:25:35 -0400 |
commit | 7b0e492e6b80d51db4156996b248522c7b50d467 (patch) | |
tree | 7b7e23b0a4bc9c2a97b1af4cb977cf0a5033ca59 | |
parent | 5aca284210ce827f780ea2f4f9c6ab8d6e2d6648 (diff) |
vfs: create a generic checking function for FS_IOC_FSSETXATTR
Create a generic checking function for the incoming FS_IOC_FSSETXATTR
fsxattr values so that we can standardize some of the implementation
behaviors.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Jan Kara <jack@suse.cz>
-rw-r--r-- | fs/btrfs/ioctl.c | 17 | ||||
-rw-r--r-- | fs/ext4/ioctl.c | 25 | ||||
-rw-r--r-- | fs/inode.c | 23 | ||||
-rw-r--r-- | fs/xfs/xfs_ioctl.c | 69 | ||||
-rw-r--r-- | include/linux/fs.h | 9 |
5 files changed, 95 insertions, 48 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index d3d9b4abb09b..3cd66efdb99d 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -375,9 +375,7 @@ static int btrfs_ioctl_fsgetxattr(struct file *file, void __user *arg) | |||
375 | struct btrfs_inode *binode = BTRFS_I(file_inode(file)); | 375 | struct btrfs_inode *binode = BTRFS_I(file_inode(file)); |
376 | struct fsxattr fa; | 376 | struct fsxattr fa; |
377 | 377 | ||
378 | memset(&fa, 0, sizeof(fa)); | 378 | simple_fill_fsxattr(&fa, btrfs_inode_flags_to_xflags(binode->flags)); |
379 | fa.fsx_xflags = btrfs_inode_flags_to_xflags(binode->flags); | ||
380 | |||
381 | if (copy_to_user(arg, &fa, sizeof(fa))) | 379 | if (copy_to_user(arg, &fa, sizeof(fa))) |
382 | return -EFAULT; | 380 | return -EFAULT; |
383 | 381 | ||
@@ -390,7 +388,7 @@ static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg) | |||
390 | struct btrfs_inode *binode = BTRFS_I(inode); | 388 | struct btrfs_inode *binode = BTRFS_I(inode); |
391 | struct btrfs_root *root = binode->root; | 389 | struct btrfs_root *root = binode->root; |
392 | struct btrfs_trans_handle *trans; | 390 | struct btrfs_trans_handle *trans; |
393 | struct fsxattr fa; | 391 | struct fsxattr fa, old_fa; |
394 | unsigned old_flags; | 392 | unsigned old_flags; |
395 | unsigned old_i_flags; | 393 | unsigned old_i_flags; |
396 | int ret = 0; | 394 | int ret = 0; |
@@ -401,7 +399,6 @@ static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg) | |||
401 | if (btrfs_root_readonly(root)) | 399 | if (btrfs_root_readonly(root)) |
402 | return -EROFS; | 400 | return -EROFS; |
403 | 401 | ||
404 | memset(&fa, 0, sizeof(fa)); | ||
405 | if (copy_from_user(&fa, arg, sizeof(fa))) | 402 | if (copy_from_user(&fa, arg, sizeof(fa))) |
406 | return -EFAULT; | 403 | return -EFAULT; |
407 | 404 | ||
@@ -421,13 +418,11 @@ static int btrfs_ioctl_fssetxattr(struct file *file, void __user *arg) | |||
421 | old_flags = binode->flags; | 418 | old_flags = binode->flags; |
422 | old_i_flags = inode->i_flags; | 419 | old_i_flags = inode->i_flags; |
423 | 420 | ||
424 | /* We need the capabilities to change append-only or immutable inode */ | 421 | simple_fill_fsxattr(&old_fa, |
425 | if (((old_flags & (BTRFS_INODE_APPEND | BTRFS_INODE_IMMUTABLE)) || | 422 | btrfs_inode_flags_to_xflags(binode->flags)); |
426 | (fa.fsx_xflags & (FS_XFLAG_APPEND | FS_XFLAG_IMMUTABLE))) && | 423 | ret = vfs_ioc_fssetxattr_check(inode, &old_fa, &fa); |
427 | !capable(CAP_LINUX_IMMUTABLE)) { | 424 | if (ret) |
428 | ret = -EPERM; | ||
429 | goto out_unlock; | 425 | goto out_unlock; |
430 | } | ||
431 | 426 | ||
432 | if (fa.fsx_xflags & FS_XFLAG_SYNC) | 427 | if (fa.fsx_xflags & FS_XFLAG_SYNC) |
433 | binode->flags |= BTRFS_INODE_SYNC; | 428 | binode->flags |= BTRFS_INODE_SYNC; |
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 272b6e44191b..1974cb755d09 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c | |||
@@ -721,6 +721,17 @@ static int ext4_ioctl_check_project(struct inode *inode, struct fsxattr *fa) | |||
721 | return 0; | 721 | return 0; |
722 | } | 722 | } |
723 | 723 | ||
724 | static void ext4_fill_fsxattr(struct inode *inode, struct fsxattr *fa) | ||
725 | { | ||
726 | struct ext4_inode_info *ei = EXT4_I(inode); | ||
727 | |||
728 | simple_fill_fsxattr(fa, ext4_iflags_to_xflags(ei->i_flags & | ||
729 | EXT4_FL_USER_VISIBLE)); | ||
730 | |||
731 | if (ext4_has_feature_project(inode->i_sb)) | ||
732 | fa->fsx_projid = from_kprojid(&init_user_ns, ei->i_projid); | ||
733 | } | ||
734 | |||
724 | long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | 735 | long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
725 | { | 736 | { |
726 | struct inode *inode = file_inode(filp); | 737 | struct inode *inode = file_inode(filp); |
@@ -1089,13 +1100,7 @@ resizefs_out: | |||
1089 | { | 1100 | { |
1090 | struct fsxattr fa; | 1101 | struct fsxattr fa; |
1091 | 1102 | ||
1092 | memset(&fa, 0, sizeof(struct fsxattr)); | 1103 | ext4_fill_fsxattr(inode, &fa); |
1093 | fa.fsx_xflags = ext4_iflags_to_xflags(ei->i_flags & EXT4_FL_USER_VISIBLE); | ||
1094 | |||
1095 | if (ext4_has_feature_project(inode->i_sb)) { | ||
1096 | fa.fsx_projid = (__u32)from_kprojid(&init_user_ns, | ||
1097 | EXT4_I(inode)->i_projid); | ||
1098 | } | ||
1099 | 1104 | ||
1100 | if (copy_to_user((struct fsxattr __user *)arg, | 1105 | if (copy_to_user((struct fsxattr __user *)arg, |
1101 | &fa, sizeof(fa))) | 1106 | &fa, sizeof(fa))) |
@@ -1104,7 +1109,7 @@ resizefs_out: | |||
1104 | } | 1109 | } |
1105 | case EXT4_IOC_FSSETXATTR: | 1110 | case EXT4_IOC_FSSETXATTR: |
1106 | { | 1111 | { |
1107 | struct fsxattr fa; | 1112 | struct fsxattr fa, old_fa; |
1108 | int err; | 1113 | int err; |
1109 | 1114 | ||
1110 | if (copy_from_user(&fa, (struct fsxattr __user *)arg, | 1115 | if (copy_from_user(&fa, (struct fsxattr __user *)arg, |
@@ -1127,9 +1132,13 @@ resizefs_out: | |||
1127 | return err; | 1132 | return err; |
1128 | 1133 | ||
1129 | inode_lock(inode); | 1134 | inode_lock(inode); |
1135 | ext4_fill_fsxattr(inode, &old_fa); | ||
1130 | err = ext4_ioctl_check_project(inode, &fa); | 1136 | err = ext4_ioctl_check_project(inode, &fa); |
1131 | if (err) | 1137 | if (err) |
1132 | goto out; | 1138 | goto out; |
1139 | err = vfs_ioc_fssetxattr_check(inode, &old_fa, &fa); | ||
1140 | if (err) | ||
1141 | goto out; | ||
1133 | flags = (ei->i_flags & ~EXT4_FL_XFLAG_VISIBLE) | | 1142 | flags = (ei->i_flags & ~EXT4_FL_XFLAG_VISIBLE) | |
1134 | (flags & EXT4_FL_XFLAG_VISIBLE); | 1143 | (flags & EXT4_FL_XFLAG_VISIBLE); |
1135 | err = ext4_ioctl_setflags(inode, flags); | 1144 | err = ext4_ioctl_setflags(inode, flags); |
diff --git a/fs/inode.c b/fs/inode.c index 8072a09fd0b9..ba2bafa22885 100644 --- a/fs/inode.c +++ b/fs/inode.c | |||
@@ -2194,3 +2194,26 @@ int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags, | |||
2194 | return 0; | 2194 | return 0; |
2195 | } | 2195 | } |
2196 | EXPORT_SYMBOL(vfs_ioc_setflags_prepare); | 2196 | EXPORT_SYMBOL(vfs_ioc_setflags_prepare); |
2197 | |||
2198 | /* | ||
2199 | * Generic function to check FS_IOC_FSSETXATTR values and reject any invalid | ||
2200 | * configurations. | ||
2201 | * | ||
2202 | * Note: the caller should be holding i_mutex, or else be sure that they have | ||
2203 | * exclusive access to the inode structure. | ||
2204 | */ | ||
2205 | int vfs_ioc_fssetxattr_check(struct inode *inode, const struct fsxattr *old_fa, | ||
2206 | struct fsxattr *fa) | ||
2207 | { | ||
2208 | /* | ||
2209 | * Can't modify an immutable/append-only file unless we have | ||
2210 | * appropriate permission. | ||
2211 | */ | ||
2212 | if ((old_fa->fsx_xflags ^ fa->fsx_xflags) & | ||
2213 | (FS_XFLAG_IMMUTABLE | FS_XFLAG_APPEND) && | ||
2214 | !capable(CAP_LINUX_IMMUTABLE)) | ||
2215 | return -EPERM; | ||
2216 | |||
2217 | return 0; | ||
2218 | } | ||
2219 | EXPORT_SYMBOL(vfs_ioc_fssetxattr_check); | ||
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index d7dfc13f30f5..458a7043b4d2 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c | |||
@@ -879,37 +879,44 @@ xfs_di2lxflags( | |||
879 | return flags; | 879 | return flags; |
880 | } | 880 | } |
881 | 881 | ||
882 | STATIC int | 882 | static void |
883 | xfs_ioc_fsgetxattr( | 883 | xfs_fill_fsxattr( |
884 | xfs_inode_t *ip, | 884 | struct xfs_inode *ip, |
885 | int attr, | 885 | bool attr, |
886 | void __user *arg) | 886 | struct fsxattr *fa) |
887 | { | 887 | { |
888 | struct fsxattr fa; | 888 | simple_fill_fsxattr(fa, xfs_ip2xflags(ip)); |
889 | 889 | fa->fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog; | |
890 | memset(&fa, 0, sizeof(struct fsxattr)); | 890 | fa->fsx_cowextsize = ip->i_d.di_cowextsize << |
891 | |||
892 | xfs_ilock(ip, XFS_ILOCK_SHARED); | ||
893 | fa.fsx_xflags = xfs_ip2xflags(ip); | ||
894 | fa.fsx_extsize = ip->i_d.di_extsize << ip->i_mount->m_sb.sb_blocklog; | ||
895 | fa.fsx_cowextsize = ip->i_d.di_cowextsize << | ||
896 | ip->i_mount->m_sb.sb_blocklog; | 891 | ip->i_mount->m_sb.sb_blocklog; |
897 | fa.fsx_projid = xfs_get_projid(ip); | 892 | fa->fsx_projid = xfs_get_projid(ip); |
898 | 893 | ||
899 | if (attr) { | 894 | if (attr) { |
900 | if (ip->i_afp) { | 895 | if (ip->i_afp) { |
901 | if (ip->i_afp->if_flags & XFS_IFEXTENTS) | 896 | if (ip->i_afp->if_flags & XFS_IFEXTENTS) |
902 | fa.fsx_nextents = xfs_iext_count(ip->i_afp); | 897 | fa->fsx_nextents = xfs_iext_count(ip->i_afp); |
903 | else | 898 | else |
904 | fa.fsx_nextents = ip->i_d.di_anextents; | 899 | fa->fsx_nextents = ip->i_d.di_anextents; |
905 | } else | 900 | } else |
906 | fa.fsx_nextents = 0; | 901 | fa->fsx_nextents = 0; |
907 | } else { | 902 | } else { |
908 | if (ip->i_df.if_flags & XFS_IFEXTENTS) | 903 | if (ip->i_df.if_flags & XFS_IFEXTENTS) |
909 | fa.fsx_nextents = xfs_iext_count(&ip->i_df); | 904 | fa->fsx_nextents = xfs_iext_count(&ip->i_df); |
910 | else | 905 | else |
911 | fa.fsx_nextents = ip->i_d.di_nextents; | 906 | fa->fsx_nextents = ip->i_d.di_nextents; |
912 | } | 907 | } |
908 | } | ||
909 | |||
910 | STATIC int | ||
911 | xfs_ioc_fsgetxattr( | ||
912 | xfs_inode_t *ip, | ||
913 | int attr, | ||
914 | void __user *arg) | ||
915 | { | ||
916 | struct fsxattr fa; | ||
917 | |||
918 | xfs_ilock(ip, XFS_ILOCK_SHARED); | ||
919 | xfs_fill_fsxattr(ip, attr, &fa); | ||
913 | xfs_iunlock(ip, XFS_ILOCK_SHARED); | 920 | xfs_iunlock(ip, XFS_ILOCK_SHARED); |
914 | 921 | ||
915 | if (copy_to_user(arg, &fa, sizeof(fa))) | 922 | if (copy_to_user(arg, &fa, sizeof(fa))) |
@@ -1035,15 +1042,6 @@ xfs_ioctl_setattr_xflags( | |||
1035 | if ((fa->fsx_xflags & FS_XFLAG_DAX) && xfs_is_reflink_inode(ip)) | 1042 | if ((fa->fsx_xflags & FS_XFLAG_DAX) && xfs_is_reflink_inode(ip)) |
1036 | return -EINVAL; | 1043 | return -EINVAL; |
1037 | 1044 | ||
1038 | /* | ||
1039 | * Can't modify an immutable/append-only file unless | ||
1040 | * we have appropriate permission. | ||
1041 | */ | ||
1042 | if (((ip->i_d.di_flags & (XFS_DIFLAG_IMMUTABLE | XFS_DIFLAG_APPEND)) || | ||
1043 | (fa->fsx_xflags & (FS_XFLAG_IMMUTABLE | FS_XFLAG_APPEND))) && | ||
1044 | !capable(CAP_LINUX_IMMUTABLE)) | ||
1045 | return -EPERM; | ||
1046 | |||
1047 | /* diflags2 only valid for v3 inodes. */ | 1045 | /* diflags2 only valid for v3 inodes. */ |
1048 | di_flags2 = xfs_flags2diflags2(ip, fa->fsx_xflags); | 1046 | di_flags2 = xfs_flags2diflags2(ip, fa->fsx_xflags); |
1049 | if (di_flags2 && ip->i_d.di_version < 3) | 1047 | if (di_flags2 && ip->i_d.di_version < 3) |
@@ -1323,6 +1321,7 @@ xfs_ioctl_setattr( | |||
1323 | xfs_inode_t *ip, | 1321 | xfs_inode_t *ip, |
1324 | struct fsxattr *fa) | 1322 | struct fsxattr *fa) |
1325 | { | 1323 | { |
1324 | struct fsxattr old_fa; | ||
1326 | struct xfs_mount *mp = ip->i_mount; | 1325 | struct xfs_mount *mp = ip->i_mount; |
1327 | struct xfs_trans *tp; | 1326 | struct xfs_trans *tp; |
1328 | struct xfs_dquot *udqp = NULL; | 1327 | struct xfs_dquot *udqp = NULL; |
@@ -1370,7 +1369,6 @@ xfs_ioctl_setattr( | |||
1370 | goto error_free_dquots; | 1369 | goto error_free_dquots; |
1371 | } | 1370 | } |
1372 | 1371 | ||
1373 | |||
1374 | if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) && | 1372 | if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) && |
1375 | xfs_get_projid(ip) != fa->fsx_projid) { | 1373 | xfs_get_projid(ip) != fa->fsx_projid) { |
1376 | code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL, pdqp, | 1374 | code = xfs_qm_vop_chown_reserve(tp, ip, udqp, NULL, pdqp, |
@@ -1379,6 +1377,11 @@ xfs_ioctl_setattr( | |||
1379 | goto error_trans_cancel; | 1377 | goto error_trans_cancel; |
1380 | } | 1378 | } |
1381 | 1379 | ||
1380 | xfs_fill_fsxattr(ip, false, &old_fa); | ||
1381 | code = vfs_ioc_fssetxattr_check(VFS_I(ip), &old_fa, fa); | ||
1382 | if (code) | ||
1383 | goto error_trans_cancel; | ||
1384 | |||
1382 | code = xfs_ioctl_setattr_check_extsize(ip, fa); | 1385 | code = xfs_ioctl_setattr_check_extsize(ip, fa); |
1383 | if (code) | 1386 | if (code) |
1384 | goto error_trans_cancel; | 1387 | goto error_trans_cancel; |
@@ -1489,6 +1492,7 @@ xfs_ioc_setxflags( | |||
1489 | { | 1492 | { |
1490 | struct xfs_trans *tp; | 1493 | struct xfs_trans *tp; |
1491 | struct fsxattr fa; | 1494 | struct fsxattr fa; |
1495 | struct fsxattr old_fa; | ||
1492 | unsigned int flags; | 1496 | unsigned int flags; |
1493 | int join_flags = 0; | 1497 | int join_flags = 0; |
1494 | int error; | 1498 | int error; |
@@ -1524,6 +1528,13 @@ xfs_ioc_setxflags( | |||
1524 | goto out_drop_write; | 1528 | goto out_drop_write; |
1525 | } | 1529 | } |
1526 | 1530 | ||
1531 | xfs_fill_fsxattr(ip, false, &old_fa); | ||
1532 | error = vfs_ioc_fssetxattr_check(VFS_I(ip), &old_fa, &fa); | ||
1533 | if (error) { | ||
1534 | xfs_trans_cancel(tp); | ||
1535 | goto out_drop_write; | ||
1536 | } | ||
1537 | |||
1527 | error = xfs_ioctl_setattr_xflags(tp, ip, &fa); | 1538 | error = xfs_ioctl_setattr_xflags(tp, ip, &fa); |
1528 | if (error) { | 1539 | if (error) { |
1529 | xfs_trans_cancel(tp); | 1540 | xfs_trans_cancel(tp); |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 41d5175ffdd7..36f9691d7046 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -3549,4 +3549,13 @@ static inline struct sock *io_uring_get_socket(struct file *file) | |||
3549 | int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags, | 3549 | int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags, |
3550 | unsigned int flags); | 3550 | unsigned int flags); |
3551 | 3551 | ||
3552 | int vfs_ioc_fssetxattr_check(struct inode *inode, const struct fsxattr *old_fa, | ||
3553 | struct fsxattr *fa); | ||
3554 | |||
3555 | static inline void simple_fill_fsxattr(struct fsxattr *fa, __u32 xflags) | ||
3556 | { | ||
3557 | memset(fa, 0, sizeof(*fa)); | ||
3558 | fa->fsx_xflags = xflags; | ||
3559 | } | ||
3560 | |||
3552 | #endif /* _LINUX_FS_H */ | 3561 | #endif /* _LINUX_FS_H */ |