[PATCH 09/13] xfs: enforce metadata inode flag

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Darrick J. Wong <darrick.wong@xxxxxxxxxx>

Add checks for the metadata inode flag so that we don't ever leak
metadata inodes out to userspace, and we don't ever try to read a
regular inode as metadata.

Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx>
---
 fs/xfs/libxfs/xfs_inode_buf.c |    5 +++++
 fs/xfs/scrub/common.c         |    3 ++-
 fs/xfs/scrub/inode_repair.c   |    3 +++
 fs/xfs/scrub/scrub.c          |    1 +
 fs/xfs/xfs_icache.c           |    4 +++-
 fs/xfs/xfs_inode.c            |   10 ++++++++++
 fs/xfs/xfs_itable.c           |   11 +++++++++++
 7 files changed, 35 insertions(+), 2 deletions(-)


diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index d0084f47f246..6823e6eeec2c 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -548,6 +548,11 @@ xfs_dinode_verify(
 
 	flags2 = be64_to_cpu(dip->di_flags2);
 
+	/* don't allow the metadata iflag if we don't have metadir */
+	if ((flags2 & XFS_DIFLAG2_METADATA) &&
+	    !xfs_sb_version_hasmetadir(&mp->m_sb))
+		return __this_address;
+
 	/* don't allow reflink/cowextsize if we don't have reflink */
 	if ((flags2 & (XFS_DIFLAG2_REFLINK | XFS_DIFLAG2_COWEXTSIZE)) &&
 	     !xfs_sb_version_hasreflink(&mp->m_sb))
diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
index 71f49f2478d7..68fa5ab8c52b 100644
--- a/fs/xfs/scrub/common.c
+++ b/fs/xfs/scrub/common.c
@@ -732,7 +732,8 @@ xchk_get_inode(
 				error, __return_address);
 		return error;
 	}
-	if (VFS_I(ip)->i_generation != sc->sm->sm_gen) {
+	if (VFS_I(ip)->i_generation != sc->sm->sm_gen ||
+	    xfs_is_metadata_inode(ip)) {
 		xfs_irele(ip);
 		return -ENOENT;
 	}
diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c
index bd4374f49035..0cd2e1e7616d 100644
--- a/fs/xfs/scrub/inode_repair.c
+++ b/fs/xfs/scrub/inode_repair.c
@@ -171,6 +171,9 @@ xrep_dinode_flags(
 		flags2 &= ~XFS_DIFLAG2_DAX;
 	if (!xfs_sb_version_hasbigtime(&mp->m_sb))
 		flags2 &= ~XFS_DIFLAG2_BIGTIME;
+	if (!xfs_sb_version_hasmetadir(&mp->m_sb) &&
+	    (flags2 & XFS_DIFLAG2_METADATA))
+		flags2 &= ~XFS_DIFLAG2_METADATA;
 	dip->di_flags = cpu_to_be16(flags);
 	dip->di_flags2 = cpu_to_be64(flags2);
 }
diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c
index 9a7a040ab2c0..54a524d19948 100644
--- a/fs/xfs/scrub/scrub.c
+++ b/fs/xfs/scrub/scrub.c
@@ -166,6 +166,7 @@ xchk_teardown(
 		if (sc->ilock_flags)
 			xfs_iunlock(sc->ip, sc->ilock_flags);
 		if (sc->ip != ip_in &&
+		    !xfs_is_metadata_inode(sc->ip) &&
 		    !xfs_internal_inum(sc->mp, sc->ip->i_ino))
 			xfs_irele(sc->ip);
 		sc->ip = NULL;
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index adf5a63129c6..57f2f46afb13 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -891,7 +891,9 @@ xfs_imeta_iget(
 	if (error)
 		return error;
 
-	if (ftype == XFS_DIR3_FT_UNKNOWN ||
+	if ((xfs_sb_version_hasmetadir(&mp->m_sb) &&
+	     !xfs_is_metadata_inode(ip)) ||
+	    ftype == XFS_DIR3_FT_UNKNOWN ||
 	    xfs_mode_to_ftype(VFS_I(ip)->i_mode) != ftype) {
 		xfs_irele(ip);
 		return -EFSCORRUPTED;
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 00c633ce1013..bac26f793746 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -605,8 +605,15 @@ xfs_lookup(
 	if (error)
 		goto out_free_name;
 
+	if (xfs_is_metadata_inode(*ipp)) {
+		error = -EINVAL;
+		goto out_irele;
+	}
+
 	return 0;
 
+out_irele:
+	xfs_irele(*ipp);
 out_free_name:
 	if (ci_name)
 		kmem_free(ci_name->name);
@@ -2912,6 +2919,9 @@ void
 xfs_imeta_irele(
 	struct xfs_inode	*ip)
 {
+	ASSERT(!xfs_sb_version_hasmetadir(&ip->i_mount->m_sb) ||
+	       xfs_is_metadata_inode(ip));
+
 	xfs_irele(ip);
 }
 
diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c
index 4b31c29b7e6b..5d0612e35d18 100644
--- a/fs/xfs/xfs_itable.c
+++ b/fs/xfs/xfs_itable.c
@@ -75,6 +75,17 @@ xfs_bulkstat_one_int(
 	if (error)
 		goto out;
 
+	/*
+	 * Inodes marked as being metadata are treated the same as "internal"
+	 * metadata inodes (which are rooted in the superblock).
+	 */
+	if (xfs_is_metadata_inode(ip)) {
+		xfs_iunlock(ip, XFS_ILOCK_SHARED);
+		xfs_irele(ip);
+		error = -EINVAL;
+		goto out_advance;
+	}
+
 	ASSERT(ip != NULL);
 	ASSERT(ip->i_imap.im_blkno != 0);
 	inode = VFS_I(ip);




[Index of Archives]     [XFS Filesystem Development (older mail)]     [Linux Filesystem Development]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux RAID]     [Linux SCSI]


  Powered by Linux