@@ -2361,6 +2361,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;
@@ -2933,6 +2934,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_METADIR))) {
+ 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
*/
@@ -3019,6 +3032,14 @@ _("Bad CoW extent size %u on inode %" PRIu64 ", "),
*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;
}
@@ -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_was_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_was_meta |= IREC_MASK(offset);
+}
+
+static inline void clear_inode_is_meta(struct ino_tree_node *irec, int offset)
+{
+ irec->ino_was_meta &= ~IREC_MASK(offset);
+}
+
+static inline int inode_is_meta(struct ino_tree_node *irec, int offset)
+{
+ return (irec->ino_was_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,
@@ -254,6 +254,7 @@ alloc_ino_node(
irec->ino_isa_dir = 0;
irec->ino_was_rl = 0;
irec->ino_is_rl = 0;
+ irec->ino_was_meta = 0;
irec->ir_free = (xfs_inofree_t) - 1;
irec->ir_sparse = 0;
irec->ino_un.ex_data = NULL;
@@ -2020,6 +2020,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)) {
+ 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)) {
+ dep->name[0] = '/';
+ libxfs_dir2_data_log_entry(&da, bp, dep);
+ }
+ continue;
+ }
+
/*
* check if this inode is lost+found dir in the root
*/
@@ -2931,6 +2963,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
*/