* [PATCH] xfs: allow symlinks with short remote targets
@ 2024-05-21 1:04 Darrick J. Wong
2024-05-21 1:06 ` [PATCH] generic: test creating and removing symlink xattrs Darrick J. Wong
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Darrick J. Wong @ 2024-05-21 1:04 UTC (permalink / raw)
To: Chandan Babu R, Christoph Hellwig; +Cc: xfs
From: Darrick J. Wong <djwong@kernel.org>
An internal user complained about log recovery failing on a symlink
("Bad dinode after recovery") with the following (excerpted) format:
core.magic = 0x494e
core.mode = 0120777
core.version = 3
core.format = 2 (extents)
core.nlinkv2 = 1
core.nextents = 1
core.size = 297
core.nblocks = 1
core.naextents = 0
core.forkoff = 0
core.aformat = 2 (extents)
u3.bmx[0] = [startoff,startblock,blockcount,extentflag]
0:[0,12,1,0]
This is a symbolic link with a 297-byte target stored in a disk block,
which is to say this is a symlink with a remote target. The forkoff is
0, which is to say that there's 512 - 176 == 336 bytes in the inode core
to store the data fork.
Eventually, testing of generic/388 failed with the same inode corruption
message during inode recovery. In writing a debugging patch to call
xfs_dinode_verify on dirty inode log items when we're committing
transactions, I observed that xfs/298 can reproduce the problem quite
quickly.
xfs/298 creates a symbolic link, adds some extended attributes, then
deletes them all. The test failure occurs when the final removexattr
also deletes the attr fork because that does not convert the remote
symlink back into a shortform symlink. That is how we trip this test.
The only reason why xfs/298 only triggers with the debug patch added is
that it deletes the symlink, so the final iflush shows the inode as
free.
I wrote a quick fstest to emulate the behavior of xfs/298, except that
it leaves the symlinks on the filesystem after inducing the "corrupt"
state. Kernels going back at least as far as 4.18 have written out
symlink inodes in this manner and prior to 1eb70f54c445f they did not
object to reading them back in.
Because we've been writing out inodes this way for quite some time, the
only way to fix this is to relax the check for symbolic links.
Directories don't have this problem because di_size is bumped to
blocksize during the sf->data conversion.
Fixes: 1eb70f54c445f ("xfs: validate inode fork size against fork format")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_inode_buf.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index 2305e64a4d5a9..88f4f2a1855ae 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -375,16 +375,27 @@ xfs_dinode_verify_fork(
* For fork types that can contain local data, check that the fork
* format matches the size of local data contained within the fork.
*
+ * A symlink with a small target can have a data fork can be in extents
+ * format if xattrs were added (thus converting the data fork from
+ * shortform to remote format) and then removed.
+ *
* For all types, check that when the size says the should be in extent
* or btree format, the inode isn't claiming it is in local format.
*/
if (whichfork == XFS_DATA_FORK) {
- if (S_ISDIR(mode) || S_ISLNK(mode)) {
+ if (S_ISDIR(mode)) {
if (be64_to_cpu(dip->di_size) <= fork_size &&
fork_format != XFS_DINODE_FMT_LOCAL)
return __this_address;
}
+ if (S_ISLNK(mode)) {
+ if (be64_to_cpu(dip->di_size) <= fork_size &&
+ fork_format != XFS_DINODE_FMT_EXTENTS &&
+ fork_format != XFS_DINODE_FMT_LOCAL)
+ return __this_address;
+ }
+
if (be64_to_cpu(dip->di_size) > fork_size &&
fork_format == XFS_DINODE_FMT_LOCAL)
return __this_address;
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH] generic: test creating and removing symlink xattrs
2024-05-21 1:04 [PATCH] xfs: allow symlinks with short remote targets Darrick J. Wong
@ 2024-05-21 1:06 ` Darrick J. Wong
2024-05-21 13:59 ` Christoph Hellwig
2024-05-21 13:59 ` [PATCH] xfs: allow symlinks with short remote targets Christoph Hellwig
2024-05-21 16:06 ` [PATCH v2] " Darrick J. Wong
2 siblings, 1 reply; 6+ messages in thread
From: Darrick J. Wong @ 2024-05-21 1:06 UTC (permalink / raw)
To: Chandan Babu R, Christoph Hellwig; +Cc: xfs
From: Darrick J. Wong <djwong@kernel.org>
This began as a regression test for the issues identified in "xfs: allow
symlinks with short remote targets". To summarize, the kernel XFS code
does not convert a remote symlink back to a shortform symlink after
deleting the attr fork. Recent attempts to tighten validation have
flagged this incorrectly, so we need a regression test to focus on this
dusty corner of the codebase.
However, there's nothing in here that's xfs-specific so it's a generic
test.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
tests/generic/1836 | 53 ++++++++++++++++++++++++++++++++++++++++++++++++
tests/generic/1836.out | 2 ++
2 files changed, 55 insertions(+)
create mode 100755 tests/generic/1836
create mode 100644 tests/generic/1836.out
diff --git a/tests/generic/1836 b/tests/generic/1836
new file mode 100755
index 0000000000..d5e45bb47a
--- /dev/null
+++ b/tests/generic/1836
@@ -0,0 +1,53 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2024 Oracle. All Rights Reserved.
+#
+# FS QA Test 1836
+#
+# Test that we can add xattrs to a symbolic link, remove all the xattrs, and
+# that the symbolic link doesn't get corrupted.
+#
+. ./common/preamble
+_begin_fstest auto
+
+_supported_fs generic
+_require_scratch
+
+_scratch_mkfs >> $seqres.full
+_scratch_mount >> $seqres.full
+
+SYMLINK_ADD="0123456789ABCDEF01234567890ABCDEF"
+
+# test from 32 to MAXPATHLEN sized symlink. This should make sure that
+# 256-1024 byte version 2 and 3 inodes are covered.
+SYMLINK=""
+for ((SIZE = 32; SIZE < 1024; SIZE += 32)); do
+ SYMLINK_FILE="$SCRATCH_MNT/symlink.$SIZE"
+ SYMLINK="${SYMLINK}${SYMLINK_ADD}"
+ ln -s $SYMLINK $SYMLINK_FILE > /dev/null 2>&1
+
+# add the extended attributes
+ attr -Rs 1234567890ab $SYMLINK_FILE < /dev/null > /dev/null 2>&1
+ attr -Rs 1234567890ac $SYMLINK_FILE < /dev/null > /dev/null 2>&1
+ attr -Rs 1234567890ad $SYMLINK_FILE < /dev/null > /dev/null 2>&1
+# remove the extended attributes
+ attr -Rr 1234567890ab $SYMLINK_FILE > /dev/null 2>&1
+ attr -Rr 1234567890ac $SYMLINK_FILE > /dev/null 2>&1
+ attr -Rr 1234567890ad $SYMLINK_FILE > /dev/null 2>&1
+done
+
+_scratch_cycle_mount
+
+# Now check the symlink target contents
+SYMLINK=""
+for ((SIZE = 32; SIZE < 1024; SIZE += 32)); do
+ SYMLINK_FILE="$SCRATCH_MNT/symlink.$SIZE"
+ SYMLINK="${SYMLINK}${SYMLINK_ADD}"
+
+ target="$(readlink $SYMLINK_FILE)"
+ test "$target" = "$SYMLINK" || echo "$SYMLINK_FILE: target is corrupt"
+done
+
+echo Silence is golden
+status=0
+exit
diff --git a/tests/generic/1836.out b/tests/generic/1836.out
new file mode 100644
index 0000000000..cf78922dea
--- /dev/null
+++ b/tests/generic/1836.out
@@ -0,0 +1,2 @@
+QA output created by 1836
+Silence is golden
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] xfs: allow symlinks with short remote targets
2024-05-21 1:04 [PATCH] xfs: allow symlinks with short remote targets Darrick J. Wong
2024-05-21 1:06 ` [PATCH] generic: test creating and removing symlink xattrs Darrick J. Wong
@ 2024-05-21 13:59 ` Christoph Hellwig
2024-05-21 16:06 ` [PATCH v2] " Darrick J. Wong
2 siblings, 0 replies; 6+ messages in thread
From: Christoph Hellwig @ 2024-05-21 13:59 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: Chandan Babu R, Christoph Hellwig, xfs
On Mon, May 20, 2024 at 06:04:47PM -0700, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
>
> An internal user complained about log recovery failing on a symlink
> ("Bad dinode after recovery") with the following (excerpted) format:
>
> core.magic = 0x494e
> core.mode = 0120777
> core.version = 3
> core.format = 2 (extents)
> core.nlinkv2 = 1
> core.nextents = 1
> core.size = 297
> core.nblocks = 1
> core.naextents = 0
> core.forkoff = 0
> core.aformat = 2 (extents)
> u3.bmx[0] = [startoff,startblock,blockcount,extentflag]
> 0:[0,12,1,0]
>
> This is a symbolic link with a 297-byte target stored in a disk block,
> which is to say this is a symlink with a remote target. The forkoff is
> 0, which is to say that there's 512 - 176 == 336 bytes in the inode core
> to store the data fork.
>
> Eventually, testing of generic/388 failed with the same inode corruption
> message during inode recovery. In writing a debugging patch to call
> xfs_dinode_verify on dirty inode log items when we're committing
> transactions, I observed that xfs/298 can reproduce the problem quite
> quickly.
>
> xfs/298 creates a symbolic link, adds some extended attributes, then
> deletes them all. The test failure occurs when the final removexattr
> also deletes the attr fork because that does not convert the remote
> symlink back into a shortform symlink. That is how we trip this test.
> The only reason why xfs/298 only triggers with the debug patch added is
> that it deletes the symlink, so the final iflush shows the inode as
> free.
>
> I wrote a quick fstest to emulate the behavior of xfs/298, except that
> it leaves the symlinks on the filesystem after inducing the "corrupt"
> state. Kernels going back at least as far as 4.18 have written out
> symlink inodes in this manner and prior to 1eb70f54c445f they did not
> object to reading them back in.
>
> Because we've been writing out inodes this way for quite some time, the
> only way to fix this is to relax the check for symbolic links.
> Directories don't have this problem because di_size is bumped to
> blocksize during the sf->data conversion.
>
> Fixes: 1eb70f54c445f ("xfs: validate inode fork size against fork format")
> Signed-off-by: Darrick J. Wong <djwong@kernel.org>
> ---
> fs/xfs/libxfs/xfs_inode_buf.c | 13 ++++++++++++-
> 1 file changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
> index 2305e64a4d5a9..88f4f2a1855ae 100644
> --- a/fs/xfs/libxfs/xfs_inode_buf.c
> +++ b/fs/xfs/libxfs/xfs_inode_buf.c
> @@ -375,16 +375,27 @@ xfs_dinode_verify_fork(
> * For fork types that can contain local data, check that the fork
> * format matches the size of local data contained within the fork.
> *
> + * A symlink with a small target can have a data fork can be in extents
This doesn't parse. Do you mean something like:
* Even a symlink with a target small enough to fit into the inode can
* be stored in extent format if ...
?
The existing parts of the comment could also use a bit of an overhaul
and be moved closer to the code they are documenting while you are at
it.
Otherwise looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] generic: test creating and removing symlink xattrs
2024-05-21 1:06 ` [PATCH] generic: test creating and removing symlink xattrs Darrick J. Wong
@ 2024-05-21 13:59 ` Christoph Hellwig
0 siblings, 0 replies; 6+ messages in thread
From: Christoph Hellwig @ 2024-05-21 13:59 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: Chandan Babu R, Christoph Hellwig, xfs
Looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2] xfs: allow symlinks with short remote targets
2024-05-21 1:04 [PATCH] xfs: allow symlinks with short remote targets Darrick J. Wong
2024-05-21 1:06 ` [PATCH] generic: test creating and removing symlink xattrs Darrick J. Wong
2024-05-21 13:59 ` [PATCH] xfs: allow symlinks with short remote targets Christoph Hellwig
@ 2024-05-21 16:06 ` Darrick J. Wong
2024-05-21 16:25 ` Christoph Hellwig
2 siblings, 1 reply; 6+ messages in thread
From: Darrick J. Wong @ 2024-05-21 16:06 UTC (permalink / raw)
To: Chandan Babu R, Christoph Hellwig; +Cc: xfs
From: Darrick J. Wong <djwong@kernel.org>
An internal user complained about log recovery failing on a symlink
("Bad dinode after recovery") with the following (excerpted) format:
core.magic = 0x494e
core.mode = 0120777
core.version = 3
core.format = 2 (extents)
core.nlinkv2 = 1
core.nextents = 1
core.size = 297
core.nblocks = 1
core.naextents = 0
core.forkoff = 0
core.aformat = 2 (extents)
u3.bmx[0] = [startoff,startblock,blockcount,extentflag]
0:[0,12,1,0]
This is a symbolic link with a 297-byte target stored in a disk block,
which is to say this is a symlink with a remote target. The forkoff is
0, which is to say that there's 512 - 176 == 336 bytes in the inode core
to store the data fork.
Eventually, testing of generic/388 failed with the same inode corruption
message during inode recovery. In writing a debugging patch to call
xfs_dinode_verify on dirty inode log items when we're committing
transactions, I observed that xfs/298 can reproduce the problem quite
quickly.
xfs/298 creates a symbolic link, adds some extended attributes, then
deletes them all. The test failure occurs when the final removexattr
also deletes the attr fork because that does not convert the remote
symlink back into a shortform symlink. That is how we trip this test.
The only reason why xfs/298 only triggers with the debug patch added is
that it deletes the symlink, so the final iflush shows the inode as
free.
I wrote a quick fstest to emulate the behavior of xfs/298, except that
it leaves the symlinks on the filesystem after inducing the "corrupt"
state. Kernels going back at least as far as 4.18 have written out
symlink inodes in this manner and prior to 1eb70f54c445f they did not
object to reading them back in.
Because we've been writing out inodes this way for quite some time, the
only way to fix this is to relax the check for symbolic links.
Directories don't have this problem because di_size is bumped to
blocksize during the sf->data conversion.
Fixes: 1eb70f54c445f ("xfs: validate inode fork size against fork format")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
---
v2: relocate comments, fix borken sentence
---
fs/xfs/libxfs/xfs_inode_buf.c | 28 ++++++++++++++++++++++++----
1 file changed, 24 insertions(+), 4 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index 2305e64a4d5a9..9caf9aa2221d3 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -374,17 +374,37 @@ xfs_dinode_verify_fork(
/*
* For fork types that can contain local data, check that the fork
* format matches the size of local data contained within the fork.
- *
- * For all types, check that when the size says the should be in extent
- * or btree format, the inode isn't claiming it is in local format.
*/
if (whichfork == XFS_DATA_FORK) {
- if (S_ISDIR(mode) || S_ISLNK(mode)) {
+ /*
+ * A directory small enough to fit in the inode must be stored
+ * in local format. The directory sf <-> extents conversion
+ * code updates the directory size accordingly.
+ */
+ if (S_ISDIR(mode)) {
if (be64_to_cpu(dip->di_size) <= fork_size &&
fork_format != XFS_DINODE_FMT_LOCAL)
return __this_address;
}
+ /*
+ * A symlink with a target small enough to fit in the inode can
+ * be stored in extents format if xattrs were added (thus
+ * converting the data fork from shortform to remote format)
+ * and then removed.
+ */
+ if (S_ISLNK(mode)) {
+ if (be64_to_cpu(dip->di_size) <= fork_size &&
+ fork_format != XFS_DINODE_FMT_EXTENTS &&
+ fork_format != XFS_DINODE_FMT_LOCAL)
+ return __this_address;
+ }
+
+ /*
+ * For all types, check that when the size says the fork should
+ * be in extent or btree format, the inode isn't claiming to be
+ * in local format.
+ */
if (be64_to_cpu(dip->di_size) > fork_size &&
fork_format == XFS_DINODE_FMT_LOCAL)
return __this_address;
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v2] xfs: allow symlinks with short remote targets
2024-05-21 16:06 ` [PATCH v2] " Darrick J. Wong
@ 2024-05-21 16:25 ` Christoph Hellwig
0 siblings, 0 replies; 6+ messages in thread
From: Christoph Hellwig @ 2024-05-21 16:25 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: Chandan Babu R, Christoph Hellwig, xfs
Looks good:
Reviewed-by: Christoph Hellwig <hch@lst.de>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2024-05-21 16:25 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-05-21 1:04 [PATCH] xfs: allow symlinks with short remote targets Darrick J. Wong
2024-05-21 1:06 ` [PATCH] generic: test creating and removing symlink xattrs Darrick J. Wong
2024-05-21 13:59 ` Christoph Hellwig
2024-05-21 13:59 ` [PATCH] xfs: allow symlinks with short remote targets Christoph Hellwig
2024-05-21 16:06 ` [PATCH v2] " Darrick J. Wong
2024-05-21 16:25 ` Christoph Hellwig
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox