]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_repair: check filesystem geometry before allowing upgrades
authorDarrick J. Wong <djwong@kernel.org>
Fri, 5 Aug 2022 02:54:25 +0000 (21:54 -0500)
committerEric Sandeen <sandeen@sandeen.net>
Fri, 5 Aug 2022 02:54:25 +0000 (21:54 -0500)
Currently, the two V5 feature upgrades permitted by xfs_repair do not
affect filesystem space usage, so we haven't needed to verify the
geometry.

However, this will change once we start to allow the sysadmin to add the
large extent count feature to existing filesystems.  Add all the
infrastructure we need to ensure that the log will still be large
enough, and the root inode will still be where we expect it to be after
the upgrade.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Chandan Babu R <chandan.babu@oracle.com>
[david: Recompute transaction reservation values; Exit with error if upgrade fails]
Signed-off-by: Dave Chinner <david@fromorbit.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
include/xfs_mount.h
libxfs/init.c
libxfs/libxfs_api_defs.h
repair/phase2.c

index ba80aa79d8b908ad694503c485b53e18b7a33e5e..24b1d87359286381323bf8ecc8d160e673a9999b 100644 (file)
@@ -259,6 +259,7 @@ __XFS_UNSUPP_OPSTATE(shutdown)
 
 #define LIBXFS_BHASHSIZE(sbp)          (1<<10)
 
+void libxfs_compute_all_maxlevels(struct xfs_mount *mp);
 struct xfs_mount *libxfs_mount(struct xfs_mount *mp, struct xfs_sb *sb,
                dev_t dev, dev_t logdev, dev_t rtdev, unsigned int flags);
 int libxfs_flush_mount(struct xfs_mount *mp);
index a01a41b2f4b7ed5d64ba2a5b1a0c45b8b0a20268..15052696c537395cf0498a632c2e4175b12568b6 100644 (file)
@@ -728,6 +728,21 @@ xfs_agbtree_compute_maxlevels(
        mp->m_agbtree_maxlevels = max(levels, mp->m_refc_maxlevels);
 }
 
+/* Compute maximum possible height of all btrees. */
+void
+libxfs_compute_all_maxlevels(
+       struct xfs_mount        *mp)
+{
+       xfs_alloc_compute_maxlevels(mp);
+       xfs_bmap_compute_maxlevels(mp, XFS_DATA_FORK);
+       xfs_bmap_compute_maxlevels(mp, XFS_ATTR_FORK);
+       xfs_ialloc_setup_geometry(mp);
+       xfs_rmapbt_compute_maxlevels(mp);
+       xfs_refcountbt_compute_maxlevels(mp);
+
+       xfs_agbtree_compute_maxlevels(mp);
+}
+
 /*
  * Mount structure initialization, provides a filled-in xfs_mount_t
  * such that the numerous XFS_* macros can be used.  If dev is zero,
@@ -772,14 +787,7 @@ libxfs_mount(
                mp->m_swidth = sbp->sb_width;
        }
 
-       xfs_alloc_compute_maxlevels(mp);
-       xfs_bmap_compute_maxlevels(mp, XFS_DATA_FORK);
-       xfs_bmap_compute_maxlevels(mp, XFS_ATTR_FORK);
-       xfs_ialloc_setup_geometry(mp);
-       xfs_rmapbt_compute_maxlevels(mp);
-       xfs_refcountbt_compute_maxlevels(mp);
-
-       xfs_agbtree_compute_maxlevels(mp);
+       libxfs_compute_all_maxlevels(mp);
 
        /*
         * Check that the data (and log if separate) are an ok size.
index 370ad8b3822037058156ad1c1aeeeffa52e54df0..824f2c4d30a92c50ac13e9b6ca6fa09aef0a8587 100644 (file)
@@ -21,6 +21,8 @@
 
 #define xfs_ag_init_headers            libxfs_ag_init_headers
 #define xfs_ag_block_count             libxfs_ag_block_count
+#define xfs_ag_resv_init               libxfs_ag_resv_init
+#define xfs_ag_resv_free               libxfs_ag_resv_free
 
 #define xfs_alloc_ag_max_usable                libxfs_alloc_ag_max_usable
 #define xfs_allocbt_maxlevels_ondisk   libxfs_allocbt_maxlevels_ondisk
 #define xfs_highbit64                  libxfs_highbit64
 #define xfs_ialloc_calc_rootino                libxfs_ialloc_calc_rootino
 #define xfs_iallocbt_maxlevels_ondisk  libxfs_iallocbt_maxlevels_ondisk
+#define xfs_ialloc_read_agi            libxfs_ialloc_read_agi
 #define xfs_idata_realloc              libxfs_idata_realloc
 #define xfs_idestroy_fork              libxfs_idestroy_fork
 #define xfs_iext_lookup_extent         libxfs_iext_lookup_extent
index 138327016520787dce5ee737ba1515faa0d00829..703656203ecf9222f84a8af75d3ae1c3a626d2a0 100644 (file)
@@ -133,7 +133,8 @@ zero_log(
 
 static bool
 set_inobtcount(
-       struct xfs_mount        *mp)
+       struct xfs_mount        *mp,
+       struct xfs_sb           *new_sb)
 {
        if (!xfs_has_crc(mp)) {
                printf(
@@ -153,14 +154,15 @@ set_inobtcount(
        }
 
        printf(_("Adding inode btree counts to filesystem.\n"));
-       mp->m_sb.sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_INOBTCNT;
-       mp->m_sb.sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR;
+       new_sb->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_INOBTCNT;
+       new_sb->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR;
        return true;
 }
 
 static bool
 set_bigtime(
-       struct xfs_mount        *mp)
+       struct xfs_mount        *mp,
+       struct xfs_sb           *new_sb)
 {
        if (!xfs_has_crc(mp)) {
                printf(
@@ -174,28 +176,121 @@ set_bigtime(
        }
 
        printf(_("Adding large timestamp support to filesystem.\n"));
-       mp->m_sb.sb_features_incompat |= (XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR |
-                                         XFS_SB_FEAT_INCOMPAT_BIGTIME);
+       new_sb->sb_features_incompat |= (XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR |
+                                        XFS_SB_FEAT_INCOMPAT_BIGTIME);
        return true;
 }
 
+struct check_state {
+       struct xfs_sb           sb;
+       uint64_t                features;
+       bool                    finobt_nores;
+};
+
+static inline void
+capture_old_state(
+       struct check_state      *old_state,
+       const struct xfs_mount  *mp)
+{
+       memcpy(&old_state->sb, &mp->m_sb, sizeof(struct xfs_sb));
+       old_state->finobt_nores = mp->m_finobt_nores;
+       old_state->features = mp->m_features;
+}
+
+static inline void
+restore_old_state(
+       struct xfs_mount                *mp,
+       const struct check_state        *old_state)
+{
+       memcpy(&mp->m_sb, &old_state->sb, sizeof(struct xfs_sb));
+       mp->m_finobt_nores = old_state->finobt_nores;
+       mp->m_features = old_state->features;
+       libxfs_compute_all_maxlevels(mp);
+       libxfs_trans_init(mp);
+}
+
+static inline void
+install_new_state(
+       struct xfs_mount        *mp,
+       struct xfs_sb           *new_sb)
+{
+       memcpy(&mp->m_sb, new_sb, sizeof(struct xfs_sb));
+       mp->m_features |= libxfs_sb_version_to_features(new_sb);
+       libxfs_compute_all_maxlevels(mp);
+       libxfs_trans_init(mp);
+}
+
+/*
+ * Make sure we can actually upgrade this (v5) filesystem without running afoul
+ * of root inode or log size requirements that would prevent us from mounting
+ * the filesystem.  If everything checks out, commit the new geometry.
+ */
+static void
+install_new_geometry(
+       struct xfs_mount        *mp,
+       struct xfs_sb           *new_sb)
+{
+       struct check_state      old;
+       xfs_ino_t               rootino;
+       int                     min_logblocks;
+
+       capture_old_state(&old, mp);
+       install_new_state(mp, new_sb);
+
+       /*
+        * The existing log must be large enough to satisfy the new minimum log
+        * size requirements.
+        */
+       min_logblocks = libxfs_log_calc_minimum_size(mp);
+       if (old.sb.sb_logblocks < min_logblocks) {
+               printf(
+       _("Filesystem log too small to upgrade filesystem; need %u blocks, have %u.\n"),
+                               min_logblocks, old.sb.sb_logblocks);
+               exit(1);
+       }
+
+       /*
+        * The root inode must be where xfs_repair will expect it to be with
+        * the new geometry.
+        */
+       rootino = libxfs_ialloc_calc_rootino(mp, new_sb->sb_unit);
+       if (old.sb.sb_rootino != rootino) {
+               printf(
+       _("Cannot upgrade filesystem, root inode (%llu) cannot be moved to %llu.\n"),
+                               (unsigned long long)old.sb.sb_rootino,
+                               (unsigned long long)rootino);
+               exit(1);
+       }
+
+       /*
+        * Restore the old state to get everything back to a clean state,
+        * upgrade the featureset one more time, and recompute the btree max
+        * levels for this filesystem.
+        */
+       restore_old_state(mp, &old);
+       install_new_state(mp, new_sb);
+}
+
 /* Perform the user's requested upgrades on filesystem. */
 static void
 upgrade_filesystem(
        struct xfs_mount        *mp)
 {
+       struct xfs_sb           new_sb;
        struct xfs_buf          *bp;
        bool                    dirty = false;
        int                     error;
 
+       memcpy(&new_sb, &mp->m_sb, sizeof(struct xfs_sb));
+
        if (add_inobtcount)
-               dirty |= set_inobtcount(mp);
+               dirty |= set_inobtcount(mp, &new_sb);
        if (add_bigtime)
-               dirty |= set_bigtime(mp);
+               dirty |= set_bigtime(mp, &new_sb);
        if (!dirty)
                return;
 
-       mp->m_features |= libxfs_sb_version_to_features(&mp->m_sb);
+       install_new_geometry(mp, &new_sb);
        if (no_modify)
                return;