From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Anthony Iliopoulos Date: Thu, 26 Oct 2023 11:53:39 +0200 Subject: [PATCH] fs/xfs: Add large extent counters incompat feature support XFS introduced 64-bit extent counters for inodes via a series of upstream commits and the feature was marked as stable in v6.5 via commit 61d7e8274cd8 (xfs: drop EXPERIMENTAL tag for large extent counts). Further, xfsprogs release v6.5.0 switched this feature on by default in mkfs.xfs via commit e5b18d7d1d96 (mkfs: enable large extent counts by default). Filesystems formatted with large extent count support, nrext64=1, are thus currently not recognizable by GRUB, since this is an incompat feature. Add the required support so that those filesystems and inodes with large extent counters can be read by GRUB. Signed-off-by: Anthony Iliopoulos Reviewed-by: Andrey Albershteyn Reviewed-by: Daniel Kiper Tested-by: Marta Lewandowska Tested-by: Sebastian Andrzej Siewior --- grub-core/fs/xfs.c | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index 18edfcff486c..bc2224dbb463 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -79,6 +79,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); /* Inode flags2 flags */ #define XFS_DIFLAG2_BIGTIME_BIT 3 #define XFS_DIFLAG2_BIGTIME (1 << XFS_DIFLAG2_BIGTIME_BIT) +#define XFS_DIFLAG2_NREXT64_BIT 4 +#define XFS_DIFLAG2_NREXT64 (1 << XFS_DIFLAG2_NREXT64_BIT) /* incompat feature flags */ #define XFS_SB_FEAT_INCOMPAT_FTYPE (1 << 0) /* filetype in dirent */ @@ -86,6 +88,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */ #define XFS_SB_FEAT_INCOMPAT_BIGTIME (1 << 3) /* large timestamps */ #define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4) /* needs xfs_repair */ +#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */ /* * Directory entries with ftype are explicitly handled by GRUB code. @@ -101,7 +104,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); XFS_SB_FEAT_INCOMPAT_SPINODES | \ XFS_SB_FEAT_INCOMPAT_META_UUID | \ XFS_SB_FEAT_INCOMPAT_BIGTIME | \ - XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR) + XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR | \ + XFS_SB_FEAT_INCOMPAT_NREXT64) struct grub_xfs_sblock { @@ -203,7 +207,8 @@ struct grub_xfs_inode grub_uint16_t mode; grub_uint8_t version; grub_uint8_t format; - grub_uint8_t unused2[26]; + grub_uint8_t unused2[18]; + grub_uint64_t nextents_big; grub_uint64_t atime; grub_uint64_t mtime; grub_uint64_t ctime; @@ -545,11 +550,26 @@ get_fsb (const void *keys, int idx) return grub_be_to_cpu64 (grub_get_unaligned64 (p)); } +static int +grub_xfs_inode_has_large_extent_counts (const struct grub_xfs_inode *inode) +{ + return inode->version >= 3 && + (inode->flags2 & grub_cpu_to_be64_compile_time (XFS_DIFLAG2_NREXT64)); +} + +static grub_uint64_t +grub_xfs_get_inode_nextents (struct grub_xfs_inode *inode) +{ + return (grub_xfs_inode_has_large_extent_counts (inode)) ? + grub_be_to_cpu64 (inode->nextents_big) : + grub_be_to_cpu32 (inode->nextents); +} + static grub_disk_addr_t grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { struct grub_xfs_btree_node *leaf = 0; - int ex, nrec; + grub_uint64_t ex, nrec; struct grub_xfs_extent *exts; grub_uint64_t ret = 0; @@ -574,7 +594,7 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) / (2 * sizeof (grub_uint64_t)); do { - int i; + grub_uint64_t i; for (i = 0; i < nrec; i++) { @@ -621,7 +641,7 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) grub_addr_t exts_end = 0; grub_addr_t data_end = 0; - nrec = grub_be_to_cpu32 (node->inode.nextents); + nrec = grub_xfs_get_inode_nextents (&node->inode); exts = (struct grub_xfs_extent *) grub_xfs_inode_data(&node->inode); if (grub_mul (sizeof (struct grub_xfs_extent), nrec, &exts_end) ||