Skip to content

Commit 6b69e48

Browse files
author
Darrick J. Wong
committed
xfs: standardize extent size hint validation
While chasing a bug involving invalid extent size hints being propagated into newly created realtime files, I noticed that the xfs_ioctl_setattr checks for the extent size hints weren't the same as the ones now encoded in libxfs and used for validation in repair and mkfs. Because the checks in libxfs are more stringent than the ones in the ioctl, it's possible for a live system to set inode flags that immediately result in corruption warnings. Specifically, it's possible to set an extent size hint on an rtinherit directory without checking if the hint is aligned to the realtime extent size, which makes no sense since that combination is used only to seed new realtime files. Replace the open-coded and inadequate checks with the libxfs verifier versions and update the code comments a bit. Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Brian Foster <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]>
1 parent 0f93425 commit 6b69e48

File tree

2 files changed

+41
-73
lines changed

2 files changed

+41
-73
lines changed

fs/xfs/libxfs/xfs_inode_buf.c

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -559,8 +559,17 @@ xfs_dinode_calc_crc(
559559
/*
560560
* Validate di_extsize hint.
561561
*
562-
* The rules are documented at xfs_ioctl_setattr_check_extsize().
563-
* These functions must be kept in sync with each other.
562+
* 1. Extent size hint is only valid for directories and regular files.
563+
* 2. FS_XFLAG_EXTSIZE is only valid for regular files.
564+
* 3. FS_XFLAG_EXTSZINHERIT is only valid for directories.
565+
* 4. Hint cannot be larger than MAXTEXTLEN.
566+
* 5. Can be changed on directories at any time.
567+
* 6. Hint value of 0 turns off hints, clears inode flags.
568+
* 7. Extent size must be a multiple of the appropriate block size.
569+
* For realtime files, this is the rt extent size.
570+
* 8. For non-realtime files, the extent size hint must be limited
571+
* to half the AG size to avoid alignment extending the extent beyond the
572+
* limits of the AG.
564573
*/
565574
xfs_failaddr_t
566575
xfs_inode_validate_extsize(
@@ -616,8 +625,15 @@ xfs_inode_validate_extsize(
616625
/*
617626
* Validate di_cowextsize hint.
618627
*
619-
* The rules are documented at xfs_ioctl_setattr_check_cowextsize().
620-
* These functions must be kept in sync with each other.
628+
* 1. CoW extent size hint can only be set if reflink is enabled on the fs.
629+
* The inode does not have to have any shared blocks, but it must be a v3.
630+
* 2. FS_XFLAG_COWEXTSIZE is only valid for directories and regular files;
631+
* for a directory, the hint is propagated to new files.
632+
* 3. Can be changed on files & directories at any time.
633+
* 4. Hint value of 0 turns off hints, clears inode flags.
634+
* 5. Extent size must be a multiple of the appropriate block size.
635+
* 6. The extent size hint must be limited to half the AG size to avoid
636+
* alignment extending the extent beyond the limits of the AG.
621637
*/
622638
xfs_failaddr_t
623639
xfs_inode_validate_cowextsize(

fs/xfs/xfs_ioctl.c

Lines changed: 21 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,107 +1267,59 @@ xfs_ioctl_setattr_get_trans(
12671267
}
12681268

12691269
/*
1270-
* extent size hint validation is somewhat cumbersome. Rules are:
1271-
*
1272-
* 1. extent size hint is only valid for directories and regular files
1273-
* 2. FS_XFLAG_EXTSIZE is only valid for regular files
1274-
* 3. FS_XFLAG_EXTSZINHERIT is only valid for directories.
1275-
* 4. can only be changed on regular files if no extents are allocated
1276-
* 5. can be changed on directories at any time
1277-
* 6. extsize hint of 0 turns off hints, clears inode flags.
1278-
* 7. Extent size must be a multiple of the appropriate block size.
1279-
* 8. for non-realtime files, the extent size hint must be limited
1280-
* to half the AG size to avoid alignment extending the extent beyond the
1281-
* limits of the AG.
1282-
*
1283-
* Please keep this function in sync with xfs_scrub_inode_extsize.
1270+
* Validate a proposed extent size hint. For regular files, the hint can only
1271+
* be changed if no extents are allocated.
12841272
*/
12851273
static int
12861274
xfs_ioctl_setattr_check_extsize(
12871275
struct xfs_inode *ip,
12881276
struct fileattr *fa)
12891277
{
12901278
struct xfs_mount *mp = ip->i_mount;
1291-
xfs_extlen_t size;
1292-
xfs_fsblock_t extsize_fsb;
1279+
xfs_failaddr_t failaddr;
1280+
uint16_t new_diflags;
12931281

12941282
if (!fa->fsx_valid)
12951283
return 0;
12961284

12971285
if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_df.if_nextents &&
1298-
((ip->i_extsize << mp->m_sb.sb_blocklog) != fa->fsx_extsize))
1286+
XFS_FSB_TO_B(mp, ip->i_extsize) != fa->fsx_extsize)
12991287
return -EINVAL;
13001288

1301-
if (fa->fsx_extsize == 0)
1302-
return 0;
1303-
1304-
extsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_extsize);
1305-
if (extsize_fsb > MAXEXTLEN)
1289+
if (fa->fsx_extsize & mp->m_blockmask)
13061290
return -EINVAL;
13071291

1308-
if (XFS_IS_REALTIME_INODE(ip) ||
1309-
(fa->fsx_xflags & FS_XFLAG_REALTIME)) {
1310-
size = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog;
1311-
} else {
1312-
size = mp->m_sb.sb_blocksize;
1313-
if (extsize_fsb > mp->m_sb.sb_agblocks / 2)
1314-
return -EINVAL;
1315-
}
1316-
1317-
if (fa->fsx_extsize % size)
1318-
return -EINVAL;
1292+
new_diflags = xfs_flags2diflags(ip, fa->fsx_xflags);
13191293

1320-
return 0;
1294+
failaddr = xfs_inode_validate_extsize(ip->i_mount,
1295+
XFS_B_TO_FSB(mp, fa->fsx_extsize),
1296+
VFS_I(ip)->i_mode, new_diflags);
1297+
return failaddr != NULL ? -EINVAL : 0;
13211298
}
13221299

1323-
/*
1324-
* CoW extent size hint validation rules are:
1325-
*
1326-
* 1. CoW extent size hint can only be set if reflink is enabled on the fs.
1327-
* The inode does not have to have any shared blocks, but it must be a v3.
1328-
* 2. FS_XFLAG_COWEXTSIZE is only valid for directories and regular files;
1329-
* for a directory, the hint is propagated to new files.
1330-
* 3. Can be changed on files & directories at any time.
1331-
* 4. CoW extsize hint of 0 turns off hints, clears inode flags.
1332-
* 5. Extent size must be a multiple of the appropriate block size.
1333-
* 6. The extent size hint must be limited to half the AG size to avoid
1334-
* alignment extending the extent beyond the limits of the AG.
1335-
*
1336-
* Please keep this function in sync with xfs_scrub_inode_cowextsize.
1337-
*/
13381300
static int
13391301
xfs_ioctl_setattr_check_cowextsize(
13401302
struct xfs_inode *ip,
13411303
struct fileattr *fa)
13421304
{
13431305
struct xfs_mount *mp = ip->i_mount;
1344-
xfs_extlen_t size;
1345-
xfs_fsblock_t cowextsize_fsb;
1306+
xfs_failaddr_t failaddr;
1307+
uint64_t new_diflags2;
1308+
uint16_t new_diflags;
13461309

13471310
if (!fa->fsx_valid)
13481311
return 0;
13491312

1350-
if (!(fa->fsx_xflags & FS_XFLAG_COWEXTSIZE))
1351-
return 0;
1352-
1353-
if (!xfs_sb_version_hasreflink(&ip->i_mount->m_sb))
1354-
return -EINVAL;
1355-
1356-
if (fa->fsx_cowextsize == 0)
1357-
return 0;
1358-
1359-
cowextsize_fsb = XFS_B_TO_FSB(mp, fa->fsx_cowextsize);
1360-
if (cowextsize_fsb > MAXEXTLEN)
1361-
return -EINVAL;
1362-
1363-
size = mp->m_sb.sb_blocksize;
1364-
if (cowextsize_fsb > mp->m_sb.sb_agblocks / 2)
1313+
if (fa->fsx_cowextsize & mp->m_blockmask)
13651314
return -EINVAL;
13661315

1367-
if (fa->fsx_cowextsize % size)
1368-
return -EINVAL;
1316+
new_diflags = xfs_flags2diflags(ip, fa->fsx_xflags);
1317+
new_diflags2 = xfs_flags2diflags2(ip, fa->fsx_xflags);
13691318

1370-
return 0;
1319+
failaddr = xfs_inode_validate_cowextsize(ip->i_mount,
1320+
XFS_B_TO_FSB(mp, fa->fsx_cowextsize),
1321+
VFS_I(ip)->i_mode, new_diflags, new_diflags2);
1322+
return failaddr != NULL ? -EINVAL : 0;
13711323
}
13721324

13731325
static int

0 commit comments

Comments
 (0)