]> www.infradead.org Git - users/hch/xfsprogs.git/commitdiff
xfs_repair: don't let metadata and regular files mix
authorDarrick J. Wong <djwong@kernel.org>
Wed, 3 Jul 2024 21:21:54 +0000 (14:21 -0700)
committerDarrick J. Wong <djwong@kernel.org>
Tue, 30 Jul 2024 00:13:18 +0000 (17:13 -0700)
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>
repair/dinode.c
repair/incore.h
repair/incore_ino.c
repair/phase2.c
repair/phase6.c

index 8f74dc09144585570964ec61f65eceeacac34116..928392fb914a04b70d6e71eaf2c694de69c937ac 100644 (file)
@@ -2389,6 +2389,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;
@@ -2968,6 +2969,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
         */
@@ -3054,6 +3067,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 9ad5f1972d3deef5ff4b9711ba9d32635ca1737f..910e4ad5a2f7a4c0c38c3f815050a1728835768f 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 6618e534ab9f8ad44912563280dc4c76ad4fde77..5269145cdfae3bbffb07cc2f54c126a7a768b383 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 1d8ad61bfb5d976dc12c73152fa931edaad99148..f26cc6d25f7860ef0f64dba19c90d16c19ec08c8 100644 (file)
@@ -695,8 +695,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);
@@ -732,6 +734,7 @@ phase2(
                                else
                                        do_warn(_("would correct\n"));
                        }
+                       set_inode_is_meta(ino_rec, j);
                        j++;
                }
 
@@ -743,6 +746,7 @@ phase2(
                        else
                                do_warn(_("would correct\n"));
                }
+               set_inode_is_meta(ino_rec, j);
                j++;
 
                if (is_inode_free(ino_rec, j))  {
@@ -753,6 +757,7 @@ phase2(
                        else
                                do_warn(_("would correct\n"));
                }
+               set_inode_is_meta(ino_rec, j);
                j++;
        }
 
index 6d0bcc955a8cd2e3ecd6166df4c1a843d73267d8..c9e816da8cab532ebc679053bee777f9b71164e8 100644 (file)
@@ -1790,6 +1790,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
                 */
@@ -2701,6 +2733,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
                 */