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