From e561e709be666424cdd0c6b82f131221d2c8510f Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 3 Jul 2024 14:21:54 -0700 Subject: [PATCH] xfs_repair: don't let metadata and regular files mix 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 --- repair/dinode.c | 21 +++++++++++++++ repair/incore.h | 19 ++++++++++++++ repair/incore_ino.c | 1 + repair/phase2.c | 7 ++++- repair/phase6.c | 63 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 110 insertions(+), 1 deletion(-) diff --git a/repair/dinode.c b/repair/dinode.c index 8f74dc091..928392fb9 100644 --- a/repair/dinode.c +++ b/repair/dinode.c @@ -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; } diff --git a/repair/incore.h b/repair/incore.h index 9ad5f1972..910e4ad5a 100644 --- a/repair/incore.h +++ b/repair/incore.h @@ -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, diff --git a/repair/incore_ino.c b/repair/incore_ino.c index 6618e534a..5269145cd 100644 --- a/repair/incore_ino.c +++ b/repair/incore_ino.c @@ -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; diff --git a/repair/phase2.c b/repair/phase2.c index 1d8ad61bf..f26cc6d25 100644 --- a/repair/phase2.c +++ b/repair/phase2.c @@ -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++; } diff --git a/repair/phase6.c b/repair/phase6.c index 6d0bcc955..c9e816da8 100644 --- a/repair/phase6.c +++ b/repair/phase6.c @@ -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 */ -- 2.50.1