]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_repair: rebuild the metadata directory
authorDarrick J. Wong <djwong@kernel.org>
Thu, 21 Nov 2024 00:24:19 +0000 (16:24 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 24 Dec 2024 02:01:27 +0000 (18:01 -0800)
Check the dirents in metadata directories for problems and repair them
if necessary.  Also make sure that the sb-rooted inodes (root, metadir
root, rt bitmap, rt summary) are always allocated in that order.

Note that xfs_repair will always rebuild the metadata directory tree
itself, so we only need to report problems, not fix them.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
14 files changed:
libxfs/libxfs_api_defs.h
repair/dino_chunks.c
repair/dir2.c
repair/globals.c
repair/globals.h
repair/incore.h
repair/phase1.c
repair/phase2.c
repair/phase4.c
repair/phase6.c
repair/pptr.c
repair/pptr.h
repair/sb.c
repair/xfs_repair.c

index d2611d7a76425941c9bb789b5c5aaeeed3fdffa1..e79aa0e06e4f90682379760ab05e1dec57ce03e1 100644 (file)
 
 #define xfs_metafile_iget              libxfs_metafile_iget
 #define xfs_trans_metafile_iget                libxfs_trans_metafile_iget
+#define xfs_metafile_set_iflag         libxfs_metafile_set_iflag
 #define xfs_metadir_link               libxfs_metadir_link
 #define xfs_metadir_lookup             libxfs_metadir_lookup
 #define xfs_metadir_start_create       libxfs_metadir_start_create
index 49d57948c7eca86eaabf36bd3d61bd2a6ef4faa6..0d9b3a01bc298d7556e1cb2253933aaf0ce52460 100644 (file)
@@ -932,6 +932,18 @@ next_readbuf:
        _("would clear root inode %" PRIu64 "\n"),
                                                ino);
                                }
+                       } else if (mp->m_sb.sb_metadirino == ino) {
+                               need_metadir_inode = true;
+
+                               if (!no_modify)  {
+                                       do_warn(
+       _("cleared metadata directory %" PRIu64 "\n"),
+                                               ino);
+                               } else  {
+                                       do_warn(
+       _("would clear metadata directory %" PRIu64 "\n"),
+                                               ino);
+                               }
                        } else if (mp->m_sb.sb_rbmino == ino) {
                                need_rbmino = 1;
 
index dab6523f676a34ce5a6ff289b4953dd32a5ec00a..d233c724488182f2612556ba60a429ecf3f2f072 100644 (file)
@@ -271,6 +271,9 @@ process_sf_dir2(
                } else if (lino == mp->m_sb.sb_pquotino)  {
                        junkit = 1;
                        junkreason = _("project quota");
+               } else if (lino == mp->m_sb.sb_metadirino)  {
+                       junkit = 1;
+                       junkreason = _("metadata directory root");
                } else if ((irec_p = find_inode_rec(mp,
                                        XFS_INO_TO_AGNO(mp, lino),
                                        XFS_INO_TO_AGINO(mp, lino))) != NULL) {
@@ -564,7 +567,8 @@ _("corrected root directory %" PRIu64 " .. entry, was %" PRIu64 ", now %" PRIu64
 _("would have corrected root directory %" PRIu64 " .. entry from %" PRIu64" to %" PRIu64 "\n"),
                                ino, *parent, ino);
                }
-       } else if (ino == *parent && ino != mp->m_sb.sb_rootino)  {
+       } else if (ino == *parent && ino != mp->m_sb.sb_rootino &&
+                  ino != mp->m_sb.sb_metadirino)  {
                /*
                 * likewise, non-root directories can't have .. pointing
                 * to .
@@ -743,6 +747,8 @@ process_dir2_data(
                        clearreason = _("group quota");
                } else if (ent_ino == mp->m_sb.sb_pquotino) {
                        clearreason = _("project quota");
+               } else if (ent_ino == mp->m_sb.sb_metadirino)  {
+                       clearreason = _("metadata directory root");
                } else {
                        irec_p = find_inode_rec(mp,
                                                XFS_INO_TO_AGNO(mp, ent_ino),
@@ -864,7 +870,8 @@ _("entry at block %u offset %" PRIdPTR " in directory inode %" PRIu64 " has ille
                                 * NULLFSINO otherwise.
                                 */
                                if (ino == ent_ino &&
-                                               ino != mp->m_sb.sb_rootino) {
+                                   ino != mp->m_sb.sb_rootino &&
+                                   ino != mp->m_sb.sb_metadirino) {
                                        *parent = NULLFSINO;
                                        do_warn(
 _("bad .. entry in directory inode %" PRIu64 ", points to self: "),
@@ -1519,9 +1526,14 @@ process_dir2(
        } else if (dotdot == 0 && ino == mp->m_sb.sb_rootino) {
                do_warn(_("no .. entry for root directory %" PRIu64 "\n"), ino);
                need_root_dotdot = 1;
+       } else if (dotdot == 0 && ino == mp->m_sb.sb_metadirino) {
+               do_warn(_("no .. entry for metaino directory %" PRIu64 "\n"), ino);
+               need_metadir_dotdot = 1;
        }
 
        ASSERT((ino != mp->m_sb.sb_rootino && ino != *parent) ||
+               (ino == mp->m_sb.sb_metadirino &&
+                       (ino == *parent || need_metadir_dotdot == 1)) ||
                (ino == mp->m_sb.sb_rootino &&
                        (ino == *parent || need_root_dotdot == 1)));
 
index 7388090a7d39f3ba8c355bfacf8c14860efe68cb..b63931be9fdb7083ae50d64867d313034f8660f7 100644 (file)
@@ -66,6 +66,9 @@ int   fs_is_dirty;
 int    need_root_inode;
 int    need_root_dotdot;
 
+bool   need_metadir_inode;
+int    need_metadir_dotdot;
+
 int    need_rbmino;
 int    need_rsumino;
 
index fa53502f98bbcd43035be2dab507eddd11ce5aa1..1dc85ce7f8114cf4166ac2529ae16945668d5999 100644 (file)
@@ -107,6 +107,9 @@ extern int          fs_is_dirty;
 extern int             need_root_inode;
 extern int             need_root_dotdot;
 
+extern bool            need_metadir_inode;
+extern int             need_metadir_dotdot;
+
 extern int             need_rbmino;
 extern int             need_rsumino;
 
index 9ad5f1972d3deef5ff4b9711ba9d32635ca1737f..4f32ad3377faedc97d4adda329ba56a44fb75cab 100644 (file)
@@ -661,4 +661,17 @@ inorec_set_freecount(
                rp->ir_u.f.ir_freecount = cpu_to_be32(freecount);
 }
 
+/*
+ * Number of inodes assumed to be always allocated because they are created
+ * by mkfs.
+ */
+static inline unsigned int
+xfs_rootrec_inodes_inuse(
+       struct xfs_mount        *mp)
+{
+       if (xfs_has_metadir(mp))
+               return 4; /* sb_rootino, sb_rbmino, sb_rsumino, sb_metadirino */
+       return 3; /* sb_rootino, sb_rbmino, sb_rsumino */
+}
+
 #endif /* XFS_REPAIR_INCORE_H */
index 00b98584eed4291e7ab3eebcd786d4439cc0bc69..40e7f164c55158c793fd323aa436715528c5f06f 100644 (file)
@@ -48,6 +48,8 @@ phase1(xfs_mount_t *mp)
        primary_sb_modified = 0;
        need_root_inode = 0;
        need_root_dotdot = 0;
+       need_metadir_inode = false;
+       need_metadir_dotdot = 0;
        need_rbmino = 0;
        need_rsumino = 0;
        lost_quotas = 0;
index 17966bb54db09d639a1ea841a2db42ae0acee232..17c16e94a600c2c2297f97d1a8722510f434a1d5 100644 (file)
@@ -496,8 +496,8 @@ phase2(
        struct xfs_mount        *mp,
        int                     scan_threads)
 {
-       int                     j;
        ino_tree_node_t         *ino_rec;
+       unsigned int            inuse = xfs_rootrec_inodes_inuse(mp), j;
 
        /* now we can start using the buffer cache routines */
        set_mp(mp);
@@ -541,58 +541,81 @@ phase2(
         * make sure we know about the root inode chunk
         */
        if ((ino_rec = find_inode_rec(mp, 0, mp->m_sb.sb_rootino)) == NULL)  {
-               ASSERT(mp->m_sb.sb_rbmino == mp->m_sb.sb_rootino + 1 &&
-                       mp->m_sb.sb_rsumino == mp->m_sb.sb_rootino + 2);
+               struct xfs_sb   *sb = &mp->m_sb;
+
+               if (xfs_has_metadir(mp))
+                       ASSERT(sb->sb_metadirino == sb->sb_rootino + 1 &&
+                              sb->sb_rbmino  == sb->sb_rootino + 2 &&
+                              sb->sb_rsumino == sb->sb_rootino + 3);
+               else
+                       ASSERT(sb->sb_rbmino  == sb->sb_rootino + 1 &&
+                              sb->sb_rsumino == sb->sb_rootino + 2);
                do_warn(_("root inode chunk not found\n"));
 
                /*
-                * mark the first 3 used, the rest are free
+                * mark the first 3-4 inodes used, the rest are free
                 */
                ino_rec = set_inode_used_alloc(mp, 0,
-                               (xfs_agino_t) mp->m_sb.sb_rootino);
-               set_inode_used(ino_rec, 1);
-               set_inode_used(ino_rec, 2);
+                               XFS_INO_TO_AGINO(mp, sb->sb_rootino));
+               for (j = 1; j < inuse; j++)
+                       set_inode_used(ino_rec, j);
 
-               for (j = 3; j < XFS_INODES_PER_CHUNK; j++)
+               for (j = inuse; j < XFS_INODES_PER_CHUNK; j++)
                        set_inode_free(ino_rec, j);
 
                /*
                 * also mark blocks
                 */
-               set_bmap_ext(0, XFS_INO_TO_AGBNO(mp, mp->m_sb.sb_rootino),
+               set_bmap_ext(0, XFS_INO_TO_AGBNO(mp, sb->sb_rootino),
                             M_IGEO(mp)->ialloc_blks, XR_E_INO);
        } else  {
                do_log(_("        - found root inode chunk\n"));
+               j = 0;
 
                /*
                 * blocks are marked, just make sure they're in use
                 */
-               if (is_inode_free(ino_rec, 0))  {
+               if (is_inode_free(ino_rec, j)) {
                        do_warn(_("root inode marked free, "));
-                       set_inode_used(ino_rec, 0);
+                       set_inode_used(ino_rec, j);
                        if (!no_modify)
                                do_warn(_("correcting\n"));
                        else
                                do_warn(_("would correct\n"));
                }
+               j++;
+
+               if (xfs_has_metadir(mp)) {
+                       if (is_inode_free(ino_rec, j))  {
+                               do_warn(_("metadata root inode marked free, "));
+                               set_inode_used(ino_rec, j);
+                               if (!no_modify)
+                                       do_warn(_("correcting\n"));
+                               else
+                                       do_warn(_("would correct\n"));
+                       }
+                       j++;
+               }
 
-               if (is_inode_free(ino_rec, 1))  {
+               if (is_inode_free(ino_rec, j))  {
                        do_warn(_("realtime bitmap inode marked free, "));
-                       set_inode_used(ino_rec, 1);
+                       set_inode_used(ino_rec, j);
                        if (!no_modify)
                                do_warn(_("correcting\n"));
                        else
                                do_warn(_("would correct\n"));
                }
+               j++;
 
-               if (is_inode_free(ino_rec, 2))  {
+               if (is_inode_free(ino_rec, j))  {
                        do_warn(_("realtime summary inode marked free, "));
-                       set_inode_used(ino_rec, 2);
+                       set_inode_used(ino_rec, j);
                        if (!no_modify)
                                do_warn(_("correcting\n"));
                        else
                                do_warn(_("would correct\n"));
                }
+               j++;
        }
 
        /*
index 071f20ed736e4b323a96a182c28bab3fdcbeb299..7efef86245fbe7a6899168d17d71829150d89e4c 100644 (file)
@@ -264,6 +264,22 @@ phase4(xfs_mount_t *mp)
                        do_warn(_("root inode lost\n"));
        }
 
+       /*
+        * If metadata directory trees are enabled, the metadata root directory
+        * always comes immediately after the regular root directory, even if
+        * it's free.
+        */
+       if (xfs_has_metadir(mp) &&
+           (is_inode_free(irec, 1) || !inode_isadir(irec, 1))) {
+               need_metadir_inode = true;
+               if (no_modify)
+                       do_warn(
+       _("metadata directory root inode would be lost\n"));
+               else
+                       do_warn(
+       _("metadata directory root inode lost\n"));
+       }
+
        for (i = 0; i < mp->m_sb.sb_agcount; i++)  {
                ag_end = (i < mp->m_sb.sb_agcount - 1) ? mp->m_sb.sb_agblocks :
                        mp->m_sb.sb_dblocks -
index 82e1687f9b278d6223824306980938cf551d52ce..7a8edbad2ebfc29a0b9775e2c76111e2bc98609e 100644 (file)
@@ -478,12 +478,25 @@ reset_sbroot_ino(
 static int
 ensure_rtino(
        struct xfs_trans                *tp,
-       xfs_ino_t                       ino,
+       enum xfs_metafile_type          metafile_type,
        struct xfs_inode                **ipp)
 {
        struct xfs_mount                *mp = tp->t_mountp;
+       xfs_ino_t                       ino;
        int                             error;
 
+       switch (metafile_type) {
+       case XFS_METAFILE_RTBITMAP:
+               ino = mp->m_sb.sb_rbmino;
+               break;
+       case XFS_METAFILE_RTSUMMARY:
+               ino = mp->m_sb.sb_rsumino;
+               break;
+       default:
+               ASSERT(0);
+               return -EFSCORRUPTED;
+       }
+
        /*
         * Don't use metafile iget here because we're resetting sb-rooted
         * inodes that live at fixed inumbers, but these inodes could be in
@@ -494,6 +507,8 @@ ensure_rtino(
                return error;
 
        reset_sbroot_ino(tp, S_IFREG, *ipp);
+       if (xfs_has_metadir(mp))
+               libxfs_metafile_set_iflag(tp, *ipp, metafile_type);
        return 0;
 }
 
@@ -510,7 +525,7 @@ mk_rbmino(
                res_failed(error);
 
        /* Reset the realtime bitmap inode. */
-       error = ensure_rtino(tp, mp->m_sb.sb_rbmino, &ip);
+       error = ensure_rtino(tp, XFS_METAFILE_RTBITMAP, &ip);
        if (error) {
                do_error(
                _("couldn't iget realtime bitmap inode -- error - %d\n"),
@@ -584,7 +599,7 @@ mk_rsumino(
                res_failed(error);
 
        /* Reset the rt summary inode. */
-       error = ensure_rtino(tp, mp->m_sb.sb_rsumino, &ip);
+       error = ensure_rtino(tp, XFS_METAFILE_RTSUMMARY, &ip);
        if (error) {
                do_error(
                _("couldn't iget realtime summary inode -- error - %d\n"),
@@ -655,6 +670,36 @@ mk_root_dir(xfs_mount_t *mp)
        libxfs_irele(ip);
 }
 
+/* Create a new metadata directory root. */
+static void
+mk_metadir(
+       struct xfs_mount        *mp)
+{
+       struct xfs_trans        *tp;
+       int                     error;
+
+       error = init_fs_root_dir(mp, mp->m_sb.sb_metadirino, 0,
+                       &mp->m_metadirip);
+       if (error)
+               do_error(
+       _("Initialization of the metadata root directory failed, error %d\n"),
+                       error);
+
+       /* Mark the new metadata root dir as metadata. */
+       error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
+       if (error)
+               do_error(
+       _("Marking metadata root directory failed"));
+
+       libxfs_trans_ijoin(tp, mp->m_metadirip, 0);
+       libxfs_metafile_set_iflag(tp, mp->m_metadirip, XFS_METAFILE_DIR);
+
+       error = -libxfs_trans_commit(tp);
+       if (error)
+               do_error(
+       _("Marking metadata root directory failed, error %d\n"), error);
+}
+
 /*
  * orphanage name == lost+found
  */
@@ -1168,6 +1213,8 @@ longform_dir2_rebuild(
 
        if (ino == mp->m_sb.sb_rootino)
                need_root_dotdot = 0;
+       else if (ino == mp->m_sb.sb_metadirino)
+               need_metadir_dotdot = 0;
 
        /* go through the hash list and re-add the inodes */
 
@@ -2789,7 +2836,7 @@ process_dir_inode(
 
        need_dot = dirty = num_illegal = 0;
 
-       if (mp->m_sb.sb_rootino == ino {
+       if (mp->m_sb.sb_rootino == ino || mp->m_sb.sb_metadirino == ino) {
                /*
                 * mark root inode reached and bump up
                 * link count for root inode to account
@@ -2864,6 +2911,9 @@ _("error %d fixing shortform directory %llu\n"),
        dir_hash_done(hashtab);
 
        fix_dotdot(mp, ino, ip, mp->m_sb.sb_rootino, "root", &need_root_dotdot);
+       if (xfs_has_metadir(mp))
+               fix_dotdot(mp, ino, ip, mp->m_sb.sb_metadirino, "metadata",
+                               &need_metadir_dotdot);
 
        /*
         * if we need to create the '.' entry, do so only if
@@ -3117,6 +3167,21 @@ phase6(xfs_mount_t *mp)
                }
        }
 
+       if (!no_modify && xfs_has_metadir(mp)) {
+               /*
+                * In write mode, we always rebuild the metadata directory
+                * tree, even if the old one was correct.  However, we still
+                * want to log something if we couldn't find the old root.
+                */
+               if (need_metadir_inode)
+                       do_warn(_("reinitializing metadata root directory\n"));
+               mk_metadir(mp);
+               need_metadir_inode = false;
+               need_metadir_dotdot = 0;
+       } else if (need_metadir_inode) {
+               do_warn(_("would reinitialize metadata root directory\n"));
+       }
+
        if (need_rbmino)  {
                if (!no_modify)  {
                        do_warn(_("reinitializing realtime bitmap inode\n"));
index ee29e47a87bd0730f76e9117e2aadd8c0c08109d..ac0a9c618bc87d29f54b808f8c84aace75d1eddf 100644 (file)
@@ -1334,3 +1334,97 @@ check_parent_ptrs(
 
        destroy_work_queue(&wq);
 }
+
+static int
+erase_pptrs(
+       struct xfs_trans        *tp,
+       struct xfs_inode        *ip,
+       unsigned int            attr_flags,
+       const unsigned char     *name,
+       unsigned int            namelen,
+       const void              *value,
+       unsigned int            valuelen,
+       void                    *priv)
+{
+       struct garbage_xattr    garbage_xattr = {
+               .attr_filter    = attr_flags,
+               .attrnamelen    = namelen,
+               .attrvaluelen   = valuelen,
+       };
+       struct file_scan        *fscan = priv;
+       int                     error;
+
+       if (!(attr_flags & XFS_ATTR_PARENT))
+               return 0;
+
+       error = -xfblob_store(fscan->garbage_xattr_names,
+                       &garbage_xattr.attrname_cookie, name, namelen);
+       if (error)
+               do_error(_("storing ino %llu garbage pptr failed: %s\n"),
+                               (unsigned long long)ip->i_ino,
+                               strerror(error));
+
+       error = -xfblob_store(fscan->garbage_xattr_names,
+                       &garbage_xattr.attrvalue_cookie, value, valuelen);
+       if (error)
+               do_error(_("storing ino %llu garbage pptr failed: %s\n"),
+                               (unsigned long long)ip->i_ino,
+                               strerror(error));
+
+       error = -slab_add(fscan->garbage_xattr_recs, &garbage_xattr);
+       if (error)
+               do_error(_("storing ino %llu garbage pptr rec failed: %s\n"),
+                               (unsigned long long)ip->i_ino,
+                               strerror(error));
+
+       return 0;
+}
+
+/* Delete all of this file's parent pointers if we can. */
+void
+try_erase_parent_ptrs(
+       struct xfs_inode        *ip)
+{
+       struct file_scan        fscan = {
+               .have_garbage   = true,
+       };
+       struct xfs_mount        *mp = ip->i_mount;
+       struct xfs_trans        *tp = NULL;
+       char                    *descr;
+       int                     error;
+
+       if (!xfs_has_parent(ip->i_mount))
+               return;
+
+       if (no_modify) {
+               do_warn(
+ _("would delete garbage parent pointers in metadata ino %llu\n"),
+                               (unsigned long long)ip->i_ino);
+               return;
+       }
+
+       error = -init_slab(&fscan.garbage_xattr_recs,
+                       sizeof(struct garbage_xattr));
+       if (error)
+               do_error(_("init garbage pptr recs failed: %s\n"),
+                               strerror(error));
+
+       descr = kasprintf(GFP_KERNEL, "xfs_repair (%s): garbage pptr names",
+                       mp->m_fsname);
+       error = -xfblob_create(descr, &fscan.garbage_xattr_names);
+       kfree(descr);
+       if (error)
+               do_error("init garbage pptr names failed: %s\n",
+                               strerror(error));
+
+       libxfs_trans_alloc_empty(ip->i_mount, &tp);
+       error = xattr_walk(tp, ip, erase_pptrs, &fscan);
+       if (tp)
+               libxfs_trans_cancel(tp);
+       if (error)
+               do_warn(_("ino %llu garbage pptr collection failed: %s\n"),
+                               (unsigned long long)ip->i_ino,
+                               strerror(error));
+
+       remove_garbage_xattrs(ip, &fscan);
+}
index 65acff963a3fe9f10a641db802cf3d1a769b828e..38d5c4052ea86c4d01b66f4a52b0067a68c64927 100644 (file)
@@ -14,4 +14,6 @@ void add_parent_ptr(xfs_ino_t ino, const unsigned char *fname,
 
 void check_parent_ptrs(struct xfs_mount *mp);
 
+void try_erase_parent_ptrs(struct xfs_inode *ip);
+
 #endif /* __REPAIR_PPTR_H__ */
index 1320929caee5905d52583ee2d990b4f0e43df38f..7f27833d697ea9046f61898d1f24f3a3b2993c56 100644 (file)
@@ -28,6 +28,7 @@ copy_sb(xfs_sb_t *source, xfs_sb_t *dest)
        xfs_ino_t       uquotino;
        xfs_ino_t       gquotino;
        xfs_ino_t       pquotino;
+       xfs_ino_t       metadirino;
        uint16_t        versionnum;
 
        rootino = dest->sb_rootino;
@@ -36,6 +37,7 @@ copy_sb(xfs_sb_t *source, xfs_sb_t *dest)
        uquotino = dest->sb_uquotino;
        gquotino = dest->sb_gquotino;
        pquotino = dest->sb_pquotino;
+       metadirino = dest->sb_metadirino;
 
        versionnum = dest->sb_versionnum;
 
@@ -47,6 +49,7 @@ copy_sb(xfs_sb_t *source, xfs_sb_t *dest)
        dest->sb_uquotino = uquotino;
        dest->sb_gquotino = gquotino;
        dest->sb_pquotino = pquotino;
+       dest->sb_metadirino = metadirino;
 
        dest->sb_versionnum = versionnum;
 
index 3ade85bbcbb7fd771b2b807d1dd49f7675431eb9..70cab1ad852a211dfd8acfc650b29760199790ff 100644 (file)
@@ -644,6 +644,46 @@ fix:
                do_warn(_("Would reset sb_width to %u\n"), new_sunit);
 }
 
+/*
+ * Check that the metadata directory inode comes immediately after the root
+ * directory inode and that it seems to look like a metadata directory.
+ */
+STATIC void
+check_metadir_inode(
+       struct xfs_mount        *mp,
+       xfs_ino_t               rootino)
+{
+       int                     error;
+
+       validate_sb_ino(&mp->m_sb.sb_metadirino, rootino + 1,
+                       _("metadata root directory"));
+
+       /* If we changed the metadir inode, try reloading it. */
+       if (!mp->m_metadirip ||
+           mp->m_metadirip->i_ino != mp->m_sb.sb_metadirino) {
+               if (mp->m_metadirip)
+                       libxfs_irele(mp->m_metadirip);
+
+               error = -libxfs_metafile_iget(mp, mp->m_sb.sb_metadirino,
+                               XFS_METAFILE_DIR, &mp->m_metadirip);
+               if (error) {
+                       need_metadir_inode = true;
+                       goto done;
+               }
+       }
+
+done:
+       if (need_metadir_inode) {
+               if (!no_modify)
+                       do_warn(_("will reset metadata root directory\n"));
+               else
+                       do_warn(_("would reset metadata root directory\n"));
+               if (mp->m_metadirip)
+                       libxfs_irele(mp->m_metadirip);
+               mp->m_metadirip = NULL;
+       }
+}
+
 /*
  * Make sure that the first 3 inodes in the filesystem are the root directory,
  * the realtime bitmap, and the realtime summary, in that order.
@@ -673,6 +713,16 @@ _("sb root inode value %" PRIu64 " valid but in unaligned location (expected %"P
 
        validate_sb_ino(&mp->m_sb.sb_rootino, rootino,
                        _("root"));
+
+       if (xfs_has_metadir(mp)) {
+               /*
+                * The metadata root directory comes after the regular root
+                * directory.
+                */
+               check_metadir_inode(mp, rootino);
+               rootino++;
+       }
+
        validate_sb_ino(&mp->m_sb.sb_rbmino, rootino + 1,
                        _("realtime bitmap"));
        validate_sb_ino(&mp->m_sb.sb_rsumino, rootino + 2,