[PATCH] xfs: allow zero-length symlinks (and directories), sort of

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

 



In commit ef388e2054 ("don't allow di_size with high bit set") and
commit 3c6f46eacd87 ("sanity check directory inode di_size") we
erroneously fail the inode verification checks for symlinks or
directories with di_size == 0.

This is proven incorrect by generic/388 because the unlink process uses
multiple unconnected transactions to truncate the remote symlink /
directory and to unlink and free the inode.  If the fs goes down after
the truncate commit but before the unlink happens, we are left with a
zero-length inode.  Worse yet, if the unlink /does/ commit, log recovery
will now break after the first transaction because we've zeroed di_size.

In order to prevent the crashes and ASSERTs that the old patches
covered, re-allow the zero-length inodes and change the functions that
interface with the vfs to return the appropriate error codes to
userspace.

Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx>
---
 fs/xfs/libxfs/xfs_inode_buf.c |    4 ----
 fs/xfs/xfs_dir2_readdir.c     |    6 ++----
 fs/xfs/xfs_iops.c             |    9 ++++++++-
 3 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index 3752bac..6e62999 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -402,10 +402,6 @@ xfs_dinode_verify(
 	if (mode && xfs_mode_to_ftype(mode) == XFS_DIR3_FT_UNKNOWN)
 		return false;
 
-	/* No zero-length symlinks/dirs. */
-	if ((S_ISLNK(mode) || S_ISDIR(mode)) && dip->di_size == 0)
-		return false;
-
 	/* only version 3 or greater inodes are extensively verified here */
 	if (dip->di_version < 3)
 		return true;
diff --git a/fs/xfs/xfs_dir2_readdir.c b/fs/xfs/xfs_dir2_readdir.c
index 0b3b636..be1367a 100644
--- a/fs/xfs/xfs_dir2_readdir.c
+++ b/fs/xfs/xfs_dir2_readdir.c
@@ -74,10 +74,8 @@ xfs_dir2_sf_getdents(
 	/*
 	 * Give up if the directory is way too short.
 	 */
-	if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent)) {
-		ASSERT(XFS_FORCED_SHUTDOWN(dp->i_mount));
-		return -EIO;
-	}
+	if (dp->i_d.di_size < offsetof(xfs_dir2_sf_hdr_t, parent))
+		return -EFSCORRUPTED;
 
 	ASSERT(dp->i_df.if_bytes == dp->i_d.di_size);
 	ASSERT(dp->i_df.if_u1.if_data != NULL);
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 22c1615..b7b6807 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -457,6 +457,9 @@ xfs_vn_get_link(
 	char			*link;
 	int			error = -ENOMEM;
 
+	if (i_size_read(inode) == 0)
+		return ERR_PTR(-EFSCORRUPTED);
+
 	if (!dentry)
 		return ERR_PTR(-ECHILD);
 
@@ -483,8 +486,12 @@ xfs_vn_get_link_inline(
 	struct inode		*inode,
 	struct delayed_call	*done)
 {
+	char			*p;
 	ASSERT(XFS_I(inode)->i_df.if_flags & XFS_IFINLINE);
-	return XFS_I(inode)->i_df.if_u1.if_data;
+	p = XFS_I(inode)->i_df.if_u1.if_data;
+	if (p)
+		return p;
+	return ERR_PTR(-EFSCORRUPTED);
 }
 
 STATIC int
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[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