@@ -231,7 +231,9 @@ libxfs_imeta_iget(
if (error)
return error;
- if (ftype == XFS_DIR3_FT_UNKNOWN ||
+ if ((xfs_has_metadir(mp) &&
+ !xfs_is_metadata_inode(ip)) ||
+ ftype == XFS_DIR3_FT_UNKNOWN ||
xfs_mode_to_ftype(VFS_I(ip)->i_mode) != ftype) {
libxfs_irele(ip);
return -EFSCORRUPTED;
@@ -278,6 +280,9 @@ void
libxfs_imeta_irele(
struct xfs_inode *ip)
{
+ ASSERT(!xfs_has_metadir(ip->i_mount) ||
+ xfs_is_metadata_inode(ip));
+
libxfs_irele(ip);
}
@@ -454,6 +454,73 @@ xfs_dinode_verify_nrext64(
return NULL;
}
+/*
+ * Validate all the picky requirements we have for a file that claims to be
+ * filesystem metadata.
+ */
+xfs_failaddr_t
+xfs_dinode_verify_metaflag(
+ struct xfs_mount *mp,
+ struct xfs_dinode *dip,
+ uint16_t mode,
+ uint16_t flags,
+ uint64_t flags2)
+{
+ if (!xfs_has_metadir(mp))
+ return __this_address;
+
+ /* V5 filesystem only */
+ if (dip->di_version < 3)
+ return __this_address;
+
+ /* V3 inode fields that are always zero */
+ if (dip->di_onlink)
+ return __this_address;
+ if ((flags2 & XFS_DIFLAG2_NREXT64) && dip->di_nrext64_pad)
+ return __this_address;
+ if (!(flags2 & XFS_DIFLAG2_NREXT64) && dip->di_flushiter)
+ return __this_address;
+
+ /* Metadata files can only be directories or regular files */
+ if (!S_ISDIR(mode) && !S_ISREG(mode))
+ return __this_address;
+
+ /* They must have zero access permissions */
+ if (mode & 0777)
+ return __this_address;
+
+ /* DMAPI event and state masks are zero */
+ if (dip->di_dmevmask || dip->di_dmstate)
+ return __this_address;
+
+ /* User, group, and project IDs must be zero */
+ if (dip->di_uid || dip->di_gid ||
+ dip->di_projid_lo || dip->di_projid_hi)
+ return __this_address;
+
+ /* Immutable, sync, noatime, nodump, and nodefrag flags must be set */
+ if (!(flags & XFS_DIFLAG_IMMUTABLE))
+ return __this_address;
+ if (!(flags & XFS_DIFLAG_SYNC))
+ return __this_address;
+ if (!(flags & XFS_DIFLAG_NOATIME))
+ return __this_address;
+ if (!(flags & XFS_DIFLAG_NODUMP))
+ return __this_address;
+ if (!(flags & XFS_DIFLAG_NODEFRAG))
+ return __this_address;
+
+ /* Directories must have nosymlinks flags set */
+ if (S_ISDIR(mode) && !(flags & XFS_DIFLAG_NOSYMLINKS))
+ return __this_address;
+
+ /* dax flags2 must not be set */
+ if (flags2 & XFS_DIFLAG2_DAX)
+ return __this_address;
+
+ return NULL;
+}
+
xfs_failaddr_t
xfs_dinode_verify(
struct xfs_mount *mp,
@@ -607,6 +674,12 @@ xfs_dinode_verify(
!xfs_has_bigtime(mp))
return __this_address;
+ if (flags2 & XFS_DIFLAG2_METADATA) {
+ fa = xfs_dinode_verify_metaflag(mp, dip, mode, flags, flags2);
+ if (fa)
+ return fa;
+ }
+
return NULL;
}
@@ -28,6 +28,9 @@ int xfs_inode_from_disk(struct xfs_inode *ip, struct xfs_dinode *from);
xfs_failaddr_t xfs_dinode_verify(struct xfs_mount *mp, xfs_ino_t ino,
struct xfs_dinode *dip);
+xfs_failaddr_t xfs_dinode_verify_metaflag(struct xfs_mount *mp,
+ struct xfs_dinode *dip, uint16_t mode, uint16_t flags,
+ uint64_t flags2);
xfs_failaddr_t xfs_inode_validate_extsize(struct xfs_mount *mp,
uint32_t extsize, uint16_t mode, uint16_t flags);
xfs_failaddr_t xfs_inode_validate_cowextsize(struct xfs_mount *mp,