summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2019-07-01 11:25:35 -0400
committerDarrick J. Wong <darrick.wong@oracle.com>2019-07-01 11:25:35 -0400
commit7b0e492e6b80d51db4156996b248522c7b50d467 (patch)
tree7b7e23b0a4bc9c2a97b1af4cb977cf0a5033ca59
parent5aca284210ce827f780ea2f4f9c6ab8d6e2d6648 (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.c17
-rw-r--r--fs/ext4/ioctl.c25
-rw-r--r--fs/inode.c23
-rw-r--r--fs/xfs/xfs_ioctl.c69
-rw-r--r--include/linux/fs.h9
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
724static 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
724long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 735long 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}
2196EXPORT_SYMBOL(vfs_ioc_setflags_prepare); 2196EXPORT_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 */
2205int 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}
2219EXPORT_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
882STATIC int 882static void
883xfs_ioc_fsgetxattr( 883xfs_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
910STATIC int
911xfs_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)
3549int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags, 3549int vfs_ioc_setflags_prepare(struct inode *inode, unsigned int oldflags,
3550 unsigned int flags); 3550 unsigned int flags);
3551 3551
3552int vfs_ioc_fssetxattr_check(struct inode *inode, const struct fsxattr *old_fa,
3553 struct fsxattr *fa);
3554
3555static 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 */