[PATCH] xfs: symlinks can be zero length during log recovery

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

 



From: Dave Chinner <dchinner@xxxxxxxxxx>

A log recovery failure has been reproduced where a symlink inode has
a zero length in extent form. It was caused by a shutdown during a
combined fstress+fsmark workload.

To fix it, we have to allow zero length symlink inodes through
xfs_dinode_verify() during log recovery. We already specifically
check and allow this case in the shortform symlink fork verifier,
but in this case we don't get that far, and the inode is not in
shortform format.

Update the dinode verifier to handle this case, and change the
symlink fork verifier to only allow this case to exist during log
recovery.

Signed-off-by: Dave Chinner <dchinner@xxxxxxxxxx>
---
 fs/xfs/libxfs/xfs_inode_buf.c      | 16 +++++++++++++---
 fs/xfs/libxfs/xfs_symlink_remote.c | 12 +++++++++---
 fs/xfs/xfs_log.c                   | 11 +++++++++++
 fs/xfs/xfs_log.h                   |  3 ++-
 4 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index d38d724534c4..bec9178377e3 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -19,6 +19,7 @@
 #include "xfs_trans.h"
 #include "xfs_ialloc.h"
 #include "xfs_dir2.h"
+#include "xfs_log.h"
 
 #include <linux/iversion.h>
 
@@ -411,9 +412,18 @@ xfs_dinode_verify(
 	if (mode && xfs_mode_to_ftype(mode) == XFS_DIR3_FT_UNKNOWN)
 		return __this_address;
 
-	/* No zero-length symlinks/dirs. */
-	if ((S_ISLNK(mode) || S_ISDIR(mode)) && di_size == 0)
-		return __this_address;
+	/*
+	 * Normally both symlinks and dirs cannot be zero length. However, if a
+	 * symlink is in the process of being torn down and there's a
+	 * shutdown/crash, the symlink on disk may have a zero size. Hence we
+	 * only allow zero length symlinks during log recovery.
+	 */
+	if (di_size == 0) {
+		if (S_ISDIR(mode))
+			return __this_address;
+		if (S_ISLNK(mode) && !xfs_log_in_recovery(mp))
+			return __this_address;
+	}
 
 	/* Fork checks carried over from xfs_iformat_fork */
 	if (mode &&
diff --git a/fs/xfs/libxfs/xfs_symlink_remote.c b/fs/xfs/libxfs/xfs_symlink_remote.c
index 95374ab2dee7..b1f0dd14f805 100644
--- a/fs/xfs/libxfs/xfs_symlink_remote.c
+++ b/fs/xfs/libxfs/xfs_symlink_remote.c
@@ -215,9 +215,15 @@ xfs_symlink_shortform_verify(
 	size = ifp->if_bytes;
 	endp = sfp + size;
 
-	/* Zero length symlinks can exist while we're deleting a remote one. */
-	if (size == 0)
-		return NULL;
+	/*
+	 * Zero length symlinks can only pass through here during log recovery
+	 * while recovering deletion of a remote symlink.
+	 */
+	if (size == 0) {
+		if (xfs_log_in_recovery(ip->i_mount))
+			return NULL;
+		return __this_address;
+	}
 
 	/* No negative sizes or overly long symlink targets. */
 	if (size < 0 || size > XFS_SYMLINK_MAXLEN)
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index 5e56f3b93d4b..012397f6ec5a 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -4083,3 +4083,14 @@ xfs_log_check_lsn(
 
 	return valid;
 }
+
+bool
+xfs_log_in_recovery(
+	struct xfs_mount	*mp)
+{
+	if (!mp->m_log)
+		return false;
+	if (mp->m_log->l_flags & XLOG_ACTIVE_RECOVERY)
+		return true;
+	return false;
+}
diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h
index 3c1f6a8b4b70..410d4b3a20d3 100644
--- a/fs/xfs/xfs_log.h
+++ b/fs/xfs/xfs_log.h
@@ -152,6 +152,7 @@ bool	xfs_log_item_in_current_chkpt(struct xfs_log_item *lip);
 
 void	xfs_log_work_queue(struct xfs_mount *mp);
 void	xfs_log_quiesce(struct xfs_mount *mp);
-bool	xfs_log_check_lsn(struct xfs_mount *, xfs_lsn_t);
+bool	xfs_log_check_lsn(struct xfs_mount *mp, xfs_lsn_t lsn);
+bool	xfs_log_in_recovery(struct xfs_mount *mp);
 
 #endif	/* __XFS_LOG_H__ */
-- 
2.17.0

--
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