]> git.apps.os.sepia.ceph.com Git - xfsprogs-dev.git/commitdiff
xfs_repair: don't let metadata and regular files mix
authorDarrick J. Wong <djwong@kernel.org>
Thu, 21 Nov 2024 00:24:20 +0000 (16:24 -0800)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 24 Dec 2024 02:01:27 +0000 (18:01 -0800)
Track whether or not inodes thought they were metadata inodes.  We
cannot allow metadata inodes to appear in the regular directory tree,
and we cannot allow regular inodes to appear in the metadata directory
tree.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
repair/dinode.c
repair/incore.h
repair/incore_ino.c
repair/phase2.c
repair/phase6.c

index 5dd7edfa36aed010fbea7f7be3de43af70c79e9b..60a853d152ccecfdb3c9fefd616603d042b331fa 100644 (file)
@@ -2392,6 +2392,7 @@ process_dinode_int(
        struct xfs_dinode       *dino = *dinop;
        xfs_agino_t             unlinked_ino;
        struct xfs_perag        *pag;
+       bool                    is_meta = false;
 
        *dirty = *isa_dir = 0;
        *used = is_used;
@@ -2971,6 +2972,18 @@ _("Bad CoW extent size %u on inode %" PRIu64 ", "),
        if (collect_rmaps)
                record_inode_reflink_flag(mp, dino, agno, ino, lino);
 
+       /* Does this inode think it was metadata? */
+       if (dino->di_version >= 3 &&
+           (dino->di_flags2 & cpu_to_be64(XFS_DIFLAG2_METADATA))) {
+               struct ino_tree_node    *irec;
+               int                     off;
+
+               irec = find_inode_rec(mp, agno, ino);
+               off = get_inode_offset(mp, lino, irec);
+               set_inode_is_meta(irec, off);
+               is_meta = true;
+       }
+
        /*
         * check data fork -- if it's bad, clear the inode
         */
@@ -3057,6 +3070,14 @@ bad_out:
        *used = is_free;
        *isa_dir = 0;
        blkmap_free(dblkmap);
+       if (is_meta) {
+               struct ino_tree_node    *irec;
+               int                     off;
+
+               irec = find_inode_rec(mp, agno, ino);
+               off = get_inode_offset(mp, lino, irec);
+               clear_inode_is_meta(irec, off);
+       }
        return 1;
 }
 
index 4f32ad3377faedc97d4adda329ba56a44fb75cab..568a8c7cb75b7c3885fe25364f0bdc12bdb49f38 100644 (file)
@@ -271,6 +271,7 @@ typedef struct ino_tree_node  {
        uint64_t                ino_isa_dir;    /* bit == 1 if a directory */
        uint64_t                ino_was_rl;     /* bit == 1 if reflink flag set */
        uint64_t                ino_is_rl;      /* bit == 1 if reflink flag should be set */
+       uint64_t                ino_is_meta;    /* bit == 1 if metadata */
        uint8_t                 nlink_size;
        union ino_nlink         disk_nlinks;    /* on-disk nlinks, set in P3 */
        union  {
@@ -538,6 +539,24 @@ static inline int inode_is_rl(struct ino_tree_node *irec, int offset)
        return (irec->ino_is_rl & IREC_MASK(offset)) != 0;
 }
 
+/*
+ * set/clear/test was inode marked as metadata
+ */
+static inline void set_inode_is_meta(struct ino_tree_node *irec, int offset)
+{
+       irec->ino_is_meta |= IREC_MASK(offset);
+}
+
+static inline void clear_inode_is_meta(struct ino_tree_node *irec, int offset)
+{
+       irec->ino_is_meta &= ~IREC_MASK(offset);
+}
+
+static inline int inode_is_meta(struct ino_tree_node *irec, int offset)
+{
+       return (irec->ino_is_meta & IREC_MASK(offset)) != 0;
+}
+
 /*
  * add_inode_reached() is set on inode I only if I has been reached
  * by an inode P claiming to be the parent and if I is a directory,
index 158e9b4980d9846f103a02c2c263ca22ba14001e..3189e019faa23de0d6fc6b2c55b51439dbd05555 100644 (file)
@@ -257,6 +257,7 @@ alloc_ino_node(
        irec->ino_isa_dir = 0;
        irec->ino_was_rl = 0;
        irec->ino_is_rl = 0;
+       irec->ino_is_meta = 0;
        irec->ir_free = (xfs_inofree_t) - 1;
        irec->ir_sparse = 0;
        irec->ino_un.ex_data = NULL;
index 17c16e94a600c2c2297f97d1a8722510f434a1d5..476a1c74db8c8d38b5e5c10511172af13d2f26c0 100644 (file)
@@ -557,8 +557,10 @@ phase2(
                 */
                ino_rec = set_inode_used_alloc(mp, 0,
                                XFS_INO_TO_AGINO(mp, sb->sb_rootino));
-               for (j = 1; j < inuse; j++)
+               for (j = 1; j < inuse; j++) {
                        set_inode_used(ino_rec, j);
+                       set_inode_is_meta(ino_rec, j);
+               }
 
                for (j = inuse; j < XFS_INODES_PER_CHUNK; j++)
                        set_inode_free(ino_rec, j);
@@ -594,6 +596,7 @@ phase2(
                                else
                                        do_warn(_("would correct\n"));
                        }
+                       set_inode_is_meta(ino_rec, j);
                        j++;
                }
 
@@ -605,6 +608,7 @@ phase2(
                        else
                                do_warn(_("would correct\n"));
                }
+               set_inode_is_meta(ino_rec, j);
                j++;
 
                if (is_inode_free(ino_rec, j))  {
@@ -615,6 +619,7 @@ phase2(
                        else
                                do_warn(_("would correct\n"));
                }
+               set_inode_is_meta(ino_rec, j);
                j++;
        }
 
index 7a8edbad2ebfc29a0b9775e2c76111e2bc98609e..688eee20bb3e8ead4df36ac4d67f7dbdffd96b66 100644 (file)
@@ -1610,6 +1610,38 @@ longform_dir2_entry_check_data(
                        continue;
                }
 
+               /*
+                * Regular directories cannot point to metadata files.  If
+                * we find such a thing, blow out the entry.
+                */
+               if (!xfs_is_metadir_inode(ip) &&
+                   inode_is_meta(irec, ino_offset)) {
+                       nbad++;
+                       if (entry_junked(
+       _("entry \"%s\" in regular dir %" PRIu64" points to a metadata inode %" PRIu64 ", "),
+                                       fname, ip->i_ino, inum, NULLFSINO)) {
+                               dep->name[0] = '/';
+                               libxfs_dir2_data_log_entry(&da, bp, dep);
+                       }
+                       continue;
+               }
+
+               /*
+                * Metadata directories cannot point to regular files.  If
+                * we find such a thing, blow out the entry.
+                */
+               if (xfs_is_metadir_inode(ip) &&
+                   !inode_is_meta(irec, ino_offset)) {
+                       nbad++;
+                       if (entry_junked(
+       _("entry \"%s\" in metadata dir %" PRIu64" points to a regular inode %" PRIu64 ", "),
+                                       fname, ip->i_ino, inum, NULLFSINO)) {
+                               dep->name[0] = '/';
+                               libxfs_dir2_data_log_entry(&da, bp, dep);
+                       }
+                       continue;
+               }
+
                /*
                 * check if this inode is lost+found dir in the root
                 */
@@ -2521,6 +2553,37 @@ shortform_dir2_entry_check(
                                                ino_dirty);
                        continue;
                }
+
+               /*
+                * Regular directories cannot point to metadata files.  If
+                * we find such a thing, blow out the entry.
+                */
+               if (!xfs_is_metadir_inode(ip) &&
+                   inode_is_meta(irec, ino_offset)) {
+                       do_warn(
+       _("entry \"%s\" in regular dir %" PRIu64" points to a metadata inode %" PRIu64 ", "),
+                                       fname, ip->i_ino, lino);
+                       next_sfep = shortform_dir2_junk(mp, sfp, sfep, lino,
+                                               &max_size, &i, &bytes_deleted,
+                                               ino_dirty);
+                       continue;
+               }
+
+               /*
+                * Metadata directories cannot point to regular files.  If
+                * we find such a thing, blow out the entry.
+                */
+               if (xfs_is_metadir_inode(ip) &&
+                   !inode_is_meta(irec, ino_offset)) {
+                       do_warn(
+       _("entry \"%s\" in metadata dir %" PRIu64" points to a regular inode %" PRIu64 ", "),
+                                       fname, ip->i_ino, lino);
+                       next_sfep = shortform_dir2_junk(mp, sfp, sfep, lino,
+                                               &max_size, &i, &bytes_deleted,
+                                               ino_dirty);
+                       continue;
+               }
+
                /*
                 * check if this inode is lost+found dir in the root
                 */