]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: Directory's data fork extent counter can never overflow
authorChandan Babu R <chandan.babu@oracle.com>
Wed, 22 Jun 2022 19:28:52 +0000 (14:28 -0500)
committerEric Sandeen <sandeen@sandeen.net>
Wed, 22 Jun 2022 19:28:52 +0000 (14:28 -0500)
Source kernel commit: 83a21c18441f75aec64548692b52d34582b98a6a

The maximum file size that can be represented by the data fork extent counter
in the worst case occurs when all extents are 1 block in length and each block
is 1KB in size.

With XFS_MAX_EXTCNT_DATA_FORK_SMALL representing maximum extent count and with
1KB sized blocks, a file can reach upto,
(2^31) * 1KB = 2TB

This is much larger than the theoretical maximum size of a directory
i.e. XFS_DIR2_SPACE_SIZE * 3 = ~96GB.

Since a directory's inode can never overflow its data fork extent counter,
this commit removes all the overflow checks associated with
it. xfs_dinode_verify() now performs a rough check to verify if a diretory's
data fork is larger than 96GB.

Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandan.babu@oracle.com>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
libxfs/xfs_bmap.c
libxfs/xfs_da_btree.h
libxfs/xfs_da_format.h
libxfs/xfs_dir2.c
libxfs/xfs_format.h
libxfs/xfs_inode_buf.c
libxfs/xfs_inode_fork.h

index 105717f4ceeeb58e99e58cd23944d1e2b630f905..591d79c4033b4ceebf3d204f7a45ccf330207aba 100644 (file)
@@ -5140,26 +5140,6 @@ xfs_bmap_del_extent_real(
                 * Deleting the middle of the extent.
                 */
 
-               /*
-                * For directories, -ENOSPC is returned since a directory entry
-                * remove operation must not fail due to low extent count
-                * availability. -ENOSPC will be handled by higher layers of XFS
-                * by letting the corresponding empty Data/Free blocks to linger
-                * until a future remove operation. Dabtree blocks would be
-                * swapped with the last block in the leaf space and then the
-                * new last block will be unmapped.
-                *
-                * The above logic also applies to the source directory entry of
-                * a rename operation.
-                */
-               error = xfs_iext_count_may_overflow(ip, whichfork, 1);
-               if (error) {
-                       ASSERT(S_ISDIR(VFS_I(ip)->i_mode) &&
-                               whichfork == XFS_DATA_FORK);
-                       error = -ENOSPC;
-                       goto done;
-               }
-
                old = got;
 
                got.br_blockcount = del->br_startoff - got.br_startoff;
index 0faf7d9ac241fbda34bdccb8a2772c0bc347415f..7f08f6de48bf45ab686f0389ad8820e2892206a8 100644 (file)
@@ -30,6 +30,7 @@ struct xfs_da_geometry {
        unsigned int    free_hdr_size;  /* dir2 free header size */
        unsigned int    free_max_bests; /* # of bests entries in dir2 free */
        xfs_dablk_t     freeblk;        /* blockno of free data v2 */
+       xfs_extnum_t    max_extents;    /* Max. extents in corresponding fork */
 
        xfs_dir2_data_aoff_t data_first_offset;
        size_t          data_entry_offset;
index 5a49caa5c9dfa9d935552d1325300523f677190a..95354b7ab7f51e2f4c69ea5cb3b94708a124d541 100644 (file)
@@ -277,6 +277,7 @@ xfs_dir2_sf_firstentry(struct xfs_dir2_sf_hdr *hdr)
  * Directory address space divided into sections,
  * spaces separated by 32GB.
  */
+#define        XFS_DIR2_MAX_SPACES     3
 #define        XFS_DIR2_SPACE_SIZE     (1ULL << (32 + XFS_DIR2_DATA_ALIGN_LOG))
 #define        XFS_DIR2_DATA_SPACE     0
 #define        XFS_DIR2_DATA_OFFSET    (XFS_DIR2_DATA_SPACE * XFS_DIR2_SPACE_SIZE)
index 9018cc09c7c853770462f7dfc230412f2ffd4986..9f4adb09683cd372f61be3adff03bdf4fa80590f 100644 (file)
@@ -149,6 +149,8 @@ xfs_da_mount(
        dageo->freeblk = xfs_dir2_byte_to_da(dageo, XFS_DIR2_FREE_OFFSET);
        dageo->node_ents = (dageo->blksize - dageo->node_hdr_size) /
                                (uint)sizeof(xfs_da_node_entry_t);
+       dageo->max_extents = (XFS_DIR2_MAX_SPACES * XFS_DIR2_SPACE_SIZE) >>
+                                       mp->m_sb.sb_blocklog;
        dageo->magicpct = (dageo->blksize * 37) / 100;
 
        /* set up attribute geometry - single fsb only */
@@ -160,6 +162,12 @@ xfs_da_mount(
        dageo->node_hdr_size = mp->m_dir_geo->node_hdr_size;
        dageo->node_ents = (dageo->blksize - dageo->node_hdr_size) /
                                (uint)sizeof(xfs_da_node_entry_t);
+
+       if (xfs_has_large_extent_counts(mp))
+               dageo->max_extents = XFS_MAX_EXTCNT_ATTR_FORK_LARGE;
+       else
+               dageo->max_extents = XFS_MAX_EXTCNT_ATTR_FORK_SMALL;
+
        dageo->magicpct = (dageo->blksize * 37) / 100;
        return 0;
 }
index 82b404c99b800d5d396492b97afeddf03a8e95ce..43de892d030579cd92457575011b1a99b9e33345 100644 (file)
@@ -915,6 +915,19 @@ enum xfs_dinode_fmt {
  *
  * Rounding up 47 to the nearest multiple of bits-per-byte results in 48. Hence
  * 2^48 was chosen as the maximum data fork extent count.
+ *
+ * The maximum file size that can be represented by the data fork extent counter
+ * in the worst case occurs when all extents are 1 block in length and each
+ * block is 1KB in size.
+ *
+ * With XFS_MAX_EXTCNT_DATA_FORK_SMALL representing maximum extent count and
+ * with 1KB sized blocks, a file can reach upto,
+ * 1KB * (2^31) = 2TB
+ *
+ * This is much larger than the theoretical maximum size of a directory
+ * i.e. XFS_DIR2_SPACE_SIZE * XFS_DIR2_MAX_SPACES = ~96GB.
+ *
+ * Hence, a directory inode can never overflow its data fork extent counter.
  */
 #define XFS_MAX_EXTCNT_DATA_FORK_LARGE ((xfs_extnum_t)((1ULL << 48) - 1))
 #define XFS_MAX_EXTCNT_ATTR_FORK_LARGE ((xfs_extnum_t)((1ULL << 32) - 1))
index bb0f8a7dd290c79fe5f2e96467ffbd4b894ba57d..0f9d81e618f82dec127312a0eb38e5342e80ea98 100644 (file)
@@ -488,6 +488,9 @@ xfs_dinode_verify(
        if (mode && nextents + naextents > nblocks)
                return __this_address;
 
+       if (S_ISDIR(mode) && nextents > mp->m_dir_geo->max_extents)
+               return __this_address;
+
        if (mode && XFS_DFORK_BOFF(dip) > mp->m_sb.sb_inodesize)
                return __this_address;
 
index fd5c3c2d77e0dcabe9ca40fe41c02f4da760a55f..6f9d69f8896e05ad6a719339b774b709348c9ad1 100644 (file)
@@ -39,19 +39,6 @@ struct xfs_ifork {
  */
 #define XFS_IEXT_PUNCH_HOLE_CNT                (1)
 
-/*
- * Directory entry addition can cause the following,
- * 1. Data block can be added/removed.
- *    A new extent can cause extent count to increase by 1.
- * 2. Free disk block can be added/removed.
- *    Same behaviour as described above for Data block.
- * 3. Dabtree blocks.
- *    XFS_DA_NODE_MAXDEPTH blocks can be added. Each of these can be new
- *    extents. Hence extent count can increase by XFS_DA_NODE_MAXDEPTH.
- */
-#define XFS_IEXT_DIR_MANIP_CNT(mp) \
-       ((XFS_DA_NODE_MAXDEPTH + 1 + 1) * (mp)->m_dir_geo->fsbcount)
-
 /*
  * Adding/removing an xattr can cause XFS_DA_NODE_MAXDEPTH extents to
  * be added. One extra extent for dabtree in case a local attr is