* [PATCHSET DELUGE v11] xfs: Parent Pointers
@ 2023-04-06 18:10 Darrick J. Wong
2023-04-06 19:07 ` Darrick J. Wong
` (11 more replies)
0 siblings, 12 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 18:10 UTC (permalink / raw)
To: Allison Henderson; +Cc: xfs, fstests
Hi everyone,
This submission contains all of the changes to the parent pointers
patchset that I've been working since last month's deluge. The kernel
and xfsprogs patchsets are based on Allison's v10 tag from some time
ago. To recap Allison's cover letter:
"The goal of this patch set is to add a parent pointer attribute to each
inode. The attribute name containing the parent inode, generation, and
directory offset, while the attribute value contains the file name.
This feature will enable future optimizations for online scrub, shrink,
nfs handles, verity, or any other feature that could make use of quickly
deriving an inodes path from the mount point."
v11 rebases everything against 6.3-rc5, and weaves all the changes that
I had made against v10 into Allison's original series. The new xattr
NVLOOKUP mode that I introduced for v10 is critical for handling parent
pointer attr name collisions with grace, so that has been retained from
v10. With that in place, I've replaced the diroffset in the ondisk
parent pointer with the dirent hash of the name.
Parent pointers now look like this:
(parent_ino, parent_gen, namehash) -> (name[])
I experimented with replacing the dahash with crc32c for this patchset
but left it out, having concluded that checksum operation has higher
overhead (thanks, crypto api!), would require someone to figure out crc
spoofing sufficiently well to keep metadump name obfuscation working,
and doesn't seem to improve collision resistance sufficiently to be
worth the added engineering cost.
As of this submission, I think this feature is ready to go once we've
merged the online repair code and rebased the online repair code to
actually commit the repaired directories.
If you want to pull the whole thing, use these links:
https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=pptrs
https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=pptrs-drop-unnecessary
https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfstests-dev.git/log/?h=pptrs
--D
^ permalink raw reply [flat|nested] 121+ messages in thread
* Re: [PATCHSET DELUGE v11] xfs: Parent Pointers
2023-04-06 18:10 [PATCHSET DELUGE v11] xfs: Parent Pointers Darrick J. Wong
@ 2023-04-06 19:07 ` Darrick J. Wong
2023-04-06 19:13 ` [PATCHSET v11 00/12] xfs: name-value xattr lookups Darrick J. Wong
` (10 subsequent siblings)
11 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:07 UTC (permalink / raw)
To: Allison Henderson; +Cc: xfs, fstests
On Thu, Apr 06, 2023 at 11:10:38AM -0700, Darrick J. Wong wrote:
> Hi everyone,
>
> This submission contains all of the changes to the parent pointers
> patchset that I've been working since last month's deluge. The kernel
> and xfsprogs patchsets are based on Allison's v10 tag from some time
> ago. To recap Allison's cover letter:
>
> "The goal of this patch set is to add a parent pointer attribute to each
> inode. The attribute name containing the parent inode, generation, and
> directory offset, while the attribute value contains the file name.
> This feature will enable future optimizations for online scrub, shrink,
> nfs handles, verity, or any other feature that could make use of quickly
> deriving an inodes path from the mount point."
>
> v11 rebases everything against 6.3-rc5, and weaves all the changes that
> I had made against v10 into Allison's original series. The new xattr
> NVLOOKUP mode that I introduced for v10 is critical for handling parent
> pointer attr name collisions with grace, so that has been retained from
> v10. With that in place, I've replaced the diroffset in the ondisk
> parent pointer with the dirent hash of the name.
>
> Parent pointers now look like this:
>
> (parent_ino, parent_gen, namehash) -> (name[])
>
> I experimented with replacing the dahash with crc32c for this patchset
> but left it out, having concluded that checksum operation has higher
> overhead (thanks, crypto api!), would require someone to figure out crc
> spoofing sufficiently well to keep metadump name obfuscation working,
> and doesn't seem to improve collision resistance sufficiently to be
> worth the added engineering cost.
>
> As of this submission, I think this feature is ready to go once we've
> merged the online repair code and rebased the online repair code to
> actually commit the repaired directories.
>
> If you want to pull the whole thing, use these links:
> https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=pptrs
> https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=pptrs-drop-unnecessary
<sigh> Apparently vim undid my edit here; the correct URL is:
https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=pptrs
--D
> https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfstests-dev.git/log/?h=pptrs
>
> --D
^ permalink raw reply [flat|nested] 121+ messages in thread
* [PATCHSET v11 00/12] xfs: name-value xattr lookups
2023-04-06 18:10 [PATCHSET DELUGE v11] xfs: Parent Pointers Darrick J. Wong
2023-04-06 19:07 ` Darrick J. Wong
@ 2023-04-06 19:13 ` Darrick J. Wong
2023-04-06 19:16 ` [PATCH 01/12] xfs: check opcode and iovec count match in xlog_recover_attri_commit_pass2 Darrick J. Wong
` (11 more replies)
2023-04-06 19:14 ` [PATCHSET v11 0/3] xfs: hold ILOCK during deferred dir ops Darrick J. Wong
` (9 subsequent siblings)
11 siblings, 12 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:13 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
Hi all,
Directory parent pointers are stored as namespaced extended attributes
of a file. Because parent pointers can consume up to 267 bytes of
space and xattr names are 255 bytes at most, we cannot use the usual
attr name lookup functions to find a parent pointer. This is solvable
by introducing a new lookup mode that checks both the name and the
value of the xattr.
Therefore, introduce this new lookup mode. Because all parent pointer
updates are logged, we must extend the xattr logging code to capture the
VLOOKUP variants, and restore them when recovering logged operations.
These new log formats are protected by the sb_incompat PARENT flag, so
they do not need a separate log_incompat feature flag.
If you're going to start using this mess, you probably ought to just
pull from my git trees, which are linked below.
This is an extraordinary way to destroy everything. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=pptrs-attr-nvlookups
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=pptrs-attr-nvlookups
---
fs/xfs/libxfs/xfs_attr.c | 57 +++++--
fs/xfs/libxfs/xfs_attr.h | 9 +
fs/xfs/libxfs/xfs_attr_leaf.c | 45 ++++-
fs/xfs/libxfs/xfs_da_btree.h | 10 +
fs/xfs/libxfs/xfs_log_format.h | 30 +++
fs/xfs/xfs_attr_item.c | 347 ++++++++++++++++++++++++++++++++++------
fs/xfs/xfs_attr_item.h | 2
fs/xfs/xfs_xattr.c | 5 +
8 files changed, 425 insertions(+), 80 deletions(-)
^ permalink raw reply [flat|nested] 121+ messages in thread
* [PATCHSET v11 0/3] xfs: hold ILOCK during deferred dir ops
2023-04-06 18:10 [PATCHSET DELUGE v11] xfs: Parent Pointers Darrick J. Wong
2023-04-06 19:07 ` Darrick J. Wong
2023-04-06 19:13 ` [PATCHSET v11 00/12] xfs: name-value xattr lookups Darrick J. Wong
@ 2023-04-06 19:14 ` Darrick J. Wong
2023-04-06 19:19 ` [PATCH 1/3] xfs: Hold inode locks in xfs_ialloc Darrick J. Wong
` (2 more replies)
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (8 subsequent siblings)
11 siblings, 3 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:14 UTC (permalink / raw)
To: djwong; +Cc: Catherine Hoang, Allison Henderson, allison.henderson, linux-xfs
Hi all,
Currently, directory updates (link, unlink, rename) only hold the ILOCK
for the first transaction in the chain. The first transaction is where
we perform all the directory updates, so this has sufficed to coordinate
access to the directory itself.
With parent pointers, we need to hold both directories and children
ILOCKed across the entire directory update transaction chain so that
other threads never see an inconsistent edge state (parent -> child and
parent <- child). Prepare for this by making the directory update code
hold all the ILOCKs.
There's a subtle issue with online rmapbt repair that gets fixed here.
Space allocations performed as part of a directory update result in
deferred rmap updates later in the chain. With the current code, the
directory ILOCK (but not the IOLOCK) is dropped before the rmapbt
updates are performed. As a result, the online rmapbt repair scanner
has to hold each directory's IOLOCK and ILOCK to coordinate with writer
threads correctly. This change makes it so that online repair no longer
has to hold the directory IOLOCK, which makes the locking model here
consistent with the other repair scanners.
If you're going to start using this mess, you probably ought to just
pull from my git trees, which are linked below.
This is an extraordinary way to destroy everything. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=pptrs-hold-ilocks
---
fs/xfs/xfs_inode.c | 65 ++++++++++++++++++++++++++++++++++++++------------
fs/xfs/xfs_qm.c | 4 ++-
fs/xfs/xfs_symlink.c | 2 ++
fs/xfs/xfs_trans.c | 9 +++++--
4 files changed, 61 insertions(+), 19 deletions(-)
^ permalink raw reply [flat|nested] 121+ messages in thread
* [PATCHSET v11 00/23] xfs: Parent Pointers
2023-04-06 18:10 [PATCHSET DELUGE v11] xfs: Parent Pointers Darrick J. Wong
` (2 preceding siblings ...)
2023-04-06 19:14 ` [PATCHSET v11 0/3] xfs: hold ILOCK during deferred dir ops Darrick J. Wong
@ 2023-04-06 19:14 ` Darrick J. Wong
2023-04-06 19:20 ` [PATCH 01/23] xfs: Expose init_xattrs in xfs_create_tmpfile Darrick J. Wong
` (22 more replies)
2023-04-06 19:14 ` [PATCHSET v11 0/3] xfs: online repair of directories Darrick J. Wong
` (7 subsequent siblings)
11 siblings, 23 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:14 UTC (permalink / raw)
To: djwong
Cc: Darrick J. Wong, Dave Chinner, Catherine Hoang, Mark Tinguely,
Allison Henderson, allison.henderson, linux-xfs
Hi all,
This is the latest parent pointer attributes for xfs.
The goal of this patch set is to add a parent pointer attribute to each inode.
The attribute name containing the parent inode, generation, and directory
offset, while the attribute value contains the file name. This feature will
enable future optimizations for online scrub, shrink, nfs handles, verity, or
any other feature that could make use of quickly deriving an inodes path from
the mount point.
Updates since v10 [djwong]:
Merge in the ondisk format changes to get rid of the diroffset conflicts
with the parent pointer repair code, rebase the entire series with the
attr vlookup changes first, and merge all the other random fixes.
Updates since v9:
Reordered patches 2 and 3 to be 6 and 7
xfs: Add xfs_verify_pptr
moved parent pointer validators to xfs_parent
xfs: Add parent pointer ioctl
Extra validation checks for fs id
added missing release for the inode
use GFP_KERNEL flags for malloc/realloc
reworked ioctl to use pptr listenty and flex array
NEW
xfs: don't remove the attr fork when parent pointers are enabled
NEW
directory lookups should return diroffsets too
NEW
xfs: move/add parent pointer validators to xfs_parent
Updates since v8:
xfs: parent pointer attribute creation
Fix xfs_parent_init to release log assist on alloc fail
Add slab cache for xfs_parent_defer
Fix xfs_create to release after unlock
Add xfs_parent_start and xfs_parent_finish wrappers
removed unused xfs_parent_name_irec and xfs_init_parent_name_irec
xfs: add parent attributes to link
Start/finish wrapper updates
Fix xfs_link to disallow reservationless quotas
xfs: add parent attributes to symlink
Fix xfs_symlink to release after unlock
Start/finish wrapper updates
xfs: remove parent pointers in unlink
Start/finish wrapper updates
Add missing parent free
xfs: Add parent pointers to rename
Start/finish wrapper updates
Fix rename to only grab logged xattr once
Fix xfs_rename to disallow reservationless quotas
Fix double unlock on dqattach fail
Move parent frees to out_release_wip
xfs: Add parent pointers to xfs_cross_rename
Hoist parent pointers into rename
Questions comments and feedback appreciated!
Thanks all!
Allison
If you're going to start using this mess, you probably ought to just
pull from my git trees, which are linked below.
This is an extraordinary way to destroy everything. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=pptrs
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=pptrs
fstests git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfstests-dev.git/log/?h=pptrs
---
fs/xfs/Makefile | 2
fs/xfs/libxfs/xfs_attr.c | 21 ++
fs/xfs/libxfs/xfs_attr.h | 14 +-
fs/xfs/libxfs/xfs_attr_leaf.c | 6 -
fs/xfs/libxfs/xfs_attr_sf.h | 1
fs/xfs/libxfs/xfs_da_format.h | 34 ++++
fs/xfs/libxfs/xfs_defer.c | 28 +++
fs/xfs/libxfs/xfs_defer.h | 8 +
fs/xfs/libxfs/xfs_format.h | 4
fs/xfs/libxfs/xfs_fs.h | 2
fs/xfs/libxfs/xfs_fs_staging.h | 84 ++++++++++
fs/xfs/libxfs/xfs_log_format.h | 1
fs/xfs/libxfs/xfs_log_rlimit.c | 53 ++++++
fs/xfs/libxfs/xfs_parent.c | 317 ++++++++++++++++++++++++++++++++++++
fs/xfs/libxfs/xfs_parent.h | 104 ++++++++++++
fs/xfs/libxfs/xfs_sb.c | 4
fs/xfs/libxfs/xfs_trans_resv.c | 326 +++++++++++++++++++++++++++++++------
fs/xfs/libxfs/xfs_trans_space.h | 8 -
fs/xfs/scrub/attr.c | 12 +
fs/xfs/xfs_attr_item.c | 24 ++-
fs/xfs/xfs_attr_list.c | 25 ++-
fs/xfs/xfs_dquot.c | 38 ++++
fs/xfs/xfs_dquot.h | 1
fs/xfs/xfs_file.c | 1
fs/xfs/xfs_inode.c | 343 ++++++++++++++++++++++++++++++++-------
fs/xfs/xfs_inode.h | 9 +
fs/xfs/xfs_ioctl.c | 189 ++++++++++++++++++++-
fs/xfs/xfs_ioctl.h | 2
fs/xfs/xfs_iops.c | 2
fs/xfs/xfs_linux.h | 1
fs/xfs/xfs_ondisk.h | 4
fs/xfs/xfs_parent_utils.c | 157 ++++++++++++++++++
fs/xfs/xfs_parent_utils.h | 20 ++
fs/xfs/xfs_qm.h | 2
fs/xfs/xfs_super.c | 14 ++
fs/xfs/xfs_symlink.c | 49 +++++-
fs/xfs/xfs_trace.c | 1
fs/xfs/xfs_trace.h | 76 +++++++++
fs/xfs/xfs_trans_dquot.c | 15 +-
fs/xfs/xfs_xattr.c | 8 +
fs/xfs/xfs_xattr.h | 2
41 files changed, 1829 insertions(+), 183 deletions(-)
create mode 100644 fs/xfs/libxfs/xfs_fs_staging.h
create mode 100644 fs/xfs/libxfs/xfs_parent.c
create mode 100644 fs/xfs/libxfs/xfs_parent.h
create mode 100644 fs/xfs/xfs_parent_utils.c
create mode 100644 fs/xfs/xfs_parent_utils.h
^ permalink raw reply [flat|nested] 121+ messages in thread
* [PATCHSET v11 0/3] xfs: online repair of directories
2023-04-06 18:10 [PATCHSET DELUGE v11] xfs: Parent Pointers Darrick J. Wong
` (3 preceding siblings ...)
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
@ 2023-04-06 19:14 ` Darrick J. Wong
2023-04-06 19:26 ` [PATCH 1/3] xfs: reconstruct directories from parent pointers Darrick J. Wong
` (2 more replies)
2023-04-06 19:14 ` [PATCHSET v11 0/2] xfs: online checking of parent pointers Darrick J. Wong
` (6 subsequent siblings)
11 siblings, 3 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:14 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
Hi all,
With this patchset, we implement online reconstruction of directories by
scanning the entire filesystem looking for parent pointer data. This
mostly works, except for the part where we need to resync the diroffset
field of the parent pointers to match the new directory structure.
Fixing that is left as an open research question, with a few possible
solutions:
1. As part of committing the new directory, queue a bunch of parent
pointer updates to make those changes.
2. Leave them inconsistent and let the parent pointer repair fix it.
3. Change the ondisk format of parent pointers (and xattrs) so that we
can encode the full dirent name in the xattr name.
4. Change the ondisk format of parent pointers to encode a sha256 hash
of the dirent name in the xattr name. This will work as long as nobody
breaks sha256.
Thoughts? Note that the atomic swapext and block reaping code is NOT
ported for this PoC, so we do not commit any repairs.
If you're going to start using this mess, you probably ought to just
pull from my git trees, which are linked below.
This is an extraordinary way to destroy everything. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=pptrs-online-dir-repair
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=pptrs-online-dir-repair
---
fs/xfs/Makefile | 1
fs/xfs/libxfs/xfs_dir2.c | 2
fs/xfs/libxfs/xfs_dir2.h | 2
fs/xfs/scrub/common.c | 15 +
fs/xfs/scrub/common.h | 28 +
fs/xfs/scrub/dir.c | 9
fs/xfs/scrub/dir_repair.c | 1125 +++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/scrub/repair.h | 16 +
fs/xfs/scrub/scrub.c | 2
fs/xfs/scrub/tempfile.c | 42 ++
fs/xfs/scrub/tempfile.h | 2
fs/xfs/scrub/trace.c | 1
fs/xfs/scrub/trace.h | 65 +++
13 files changed, 1307 insertions(+), 3 deletions(-)
create mode 100644 fs/xfs/scrub/dir_repair.c
^ permalink raw reply [flat|nested] 121+ messages in thread
* [PATCHSET v11 0/2] xfs: online checking of parent pointers
2023-04-06 18:10 [PATCHSET DELUGE v11] xfs: Parent Pointers Darrick J. Wong
` (4 preceding siblings ...)
2023-04-06 19:14 ` [PATCHSET v11 0/3] xfs: online repair of directories Darrick J. Wong
@ 2023-04-06 19:14 ` Darrick J. Wong
2023-04-06 19:27 ` [PATCH 1/2] xfs: scrub " Darrick J. Wong
2023-04-06 19:27 ` [PATCH 2/2] xfs: deferred scrub of " Darrick J. Wong
2023-04-06 19:15 ` [PATCHSET v11 0/3] xfs: online checking " Darrick J. Wong
` (5 subsequent siblings)
11 siblings, 2 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:14 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
Hi all,
Update the existing online parent pointer checker to confirm the
directory entries that should also exist.
If you're going to start using this mess, you probably ought to just
pull from my git trees, which are linked below.
This is an extraordinary way to destroy everything. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=pptrs-online-parent-check
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=pptrs-online-parent-check
---
fs/xfs/Makefile | 2
fs/xfs/libxfs/xfs_parent.c | 42 ++++
fs/xfs/libxfs/xfs_parent.h | 10 +
fs/xfs/scrub/parent.c | 501 ++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/scrub/trace.h | 33 +++
5 files changed, 587 insertions(+), 1 deletion(-)
^ permalink raw reply [flat|nested] 121+ messages in thread
* [PATCHSET v11 0/3] xfs: online checking of parent pointers
2023-04-06 18:10 [PATCHSET DELUGE v11] xfs: Parent Pointers Darrick J. Wong
` (5 preceding siblings ...)
2023-04-06 19:14 ` [PATCHSET v11 0/2] xfs: online checking of parent pointers Darrick J. Wong
@ 2023-04-06 19:15 ` Darrick J. Wong
2023-04-06 19:27 ` [PATCH 1/3] xfs: repair parent pointers by scanning directories Darrick J. Wong
` (2 more replies)
2023-04-06 19:15 ` [PATCHSET v11 0/2] xfs: online checking of directories Darrick J. Wong
` (4 subsequent siblings)
11 siblings, 3 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:15 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
Hi all,
With this patchset, we implement online repairs for parent pointers.
This is structured similarly to the directory repair code in that we
scan the entire filesystem looking for dirents and use them to
reconstruct the parent pointer information.
Note that the atomic swapext and block reaping code is NOT ported for
this PoC, so we do not commit any repairs.
If you're going to start using this mess, you probably ought to just
pull from my git trees, which are linked below.
This is an extraordinary way to destroy everything. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=pptrs-online-parent-repair
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=pptrs-online-parent-repair
---
fs/xfs/Makefile | 1
fs/xfs/libxfs/xfs_parent.c | 37 ++
fs/xfs/libxfs/xfs_parent.h | 8
fs/xfs/scrub/parent.c | 10 +
fs/xfs/scrub/parent_repair.c | 739 ++++++++++++++++++++++++++++++++++++++++++
fs/xfs/scrub/repair.h | 4
fs/xfs/scrub/scrub.c | 2
fs/xfs/scrub/trace.c | 2
fs/xfs/scrub/trace.h | 74 ++++
9 files changed, 876 insertions(+), 1 deletion(-)
create mode 100644 fs/xfs/scrub/parent_repair.c
^ permalink raw reply [flat|nested] 121+ messages in thread
* [PATCHSET v11 0/2] xfs: online checking of directories
2023-04-06 18:10 [PATCHSET DELUGE v11] xfs: Parent Pointers Darrick J. Wong
` (6 preceding siblings ...)
2023-04-06 19:15 ` [PATCHSET v11 0/3] xfs: online checking " Darrick J. Wong
@ 2023-04-06 19:15 ` Darrick J. Wong
2023-04-06 19:28 ` [PATCH 1/2] xfs: check dirents have parent pointers Darrick J. Wong
2023-04-06 19:28 ` [PATCH 2/2] xfs: deferred scrub of dirents Darrick J. Wong
2023-04-06 19:15 ` [PATCHSET v11 00/10] xfs: name-value xattr lookups Darrick J. Wong
` (3 subsequent siblings)
11 siblings, 2 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:15 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
Hi all,
Update the existing online directory checker to confirm the parent
pointers that should also exist.
If you're going to start using this mess, you probably ought to just
pull from my git trees, which are linked below.
This is an extraordinary way to destroy everything. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=pptrs-online-dir-check
---
fs/xfs/scrub/dir.c | 338 ++++++++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/scrub/trace.h | 2
2 files changed, 339 insertions(+), 1 deletion(-)
^ permalink raw reply [flat|nested] 121+ messages in thread
* [PATCHSET v11 00/10] xfs: name-value xattr lookups
2023-04-06 18:10 [PATCHSET DELUGE v11] xfs: Parent Pointers Darrick J. Wong
` (7 preceding siblings ...)
2023-04-06 19:15 ` [PATCHSET v11 0/2] xfs: online checking of directories Darrick J. Wong
@ 2023-04-06 19:15 ` Darrick J. Wong
2023-04-06 19:29 ` [PATCH 01/10] xfs: make xfs_attr_set require XFS_DA_OP_REMOVE Darrick J. Wong
` (9 more replies)
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (2 subsequent siblings)
11 siblings, 10 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:15 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
Hi all,
Directory parent pointers are stored as namespaced extended attributes
of a file. Because parent pointers can consume up to 267 bytes of
space and xattr names are 255 bytes at most, we cannot use the usual
attr name lookup functions to find a parent pointer. This is solvable
by introducing a new lookup mode that checks both the name and the
value of the xattr.
Therefore, introduce this new lookup mode. Because all parent pointer
updates are logged, we must extend the xattr logging code to capture the
VLOOKUP variants, and restore them when recovering logged operations.
These new log formats are protected by the sb_incompat PARENT flag, so
they do not need a separate log_incompat feature flag.
If you're going to start using this mess, you probably ought to just
pull from my git trees, which are linked below.
This is an extraordinary way to destroy everything. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=pptrs-attr-nvlookups
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=pptrs-attr-nvlookups
---
db/attrset.c | 4 +
libxfs/xfs_attr.c | 57 ++++++++++++++-----
libxfs/xfs_attr.h | 9 ++-
libxfs/xfs_attr_leaf.c | 45 +++++++++++++--
libxfs/xfs_da_btree.h | 10 +++
libxfs/xfs_log_format.h | 30 +++++++++-
logprint/log_redo.c | 138 ++++++++++++++++++++++++++++++++++++++---------
logprint/logprint.h | 6 +-
8 files changed, 239 insertions(+), 60 deletions(-)
^ permalink raw reply [flat|nested] 121+ messages in thread
* [PATCHSET v11 00/32] xfsprogs: Parent Pointers
2023-04-06 18:10 [PATCHSET DELUGE v11] xfs: Parent Pointers Darrick J. Wong
` (8 preceding siblings ...)
2023-04-06 19:15 ` [PATCHSET v11 00/10] xfs: name-value xattr lookups Darrick J. Wong
@ 2023-04-06 19:15 ` Darrick J. Wong
2023-04-06 19:31 ` [PATCH 01/32] xfsprogs: Increase XFS_DEFER_OPS_NR_INODES to 5 Darrick J. Wong
` (31 more replies)
2023-04-06 19:16 ` [PATCHSET v11 0/7] xfs_repair: support " Darrick J. Wong
2023-04-06 19:16 ` [PATCHSET v11 00/11] fstests: adjust tests for xfs parent pointers Darrick J. Wong
11 siblings, 32 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:15 UTC (permalink / raw)
To: djwong
Cc: Allison Collins, Darrick J. Wong, Dave Chinner, Allison Henderson,
Mark Tinguely, Catherine Hoang, allison.henderson, linux-xfs
Hi all,
NOTE: Darrick has tweaked some of these patches to match the kernel
code.
This is the latest parent pointer attributes for xfs.
The goal of this patch set is to add a parent pointer attribute to each inode.
The attribute name containing the parent inode, generation, and directory
offset, while the attribute value contains the file name. This feature will
enable future optimizations for online scrub, shrink, nfs handles, verity, or
any other feature that could make use of quickly deriving an inodes path from
the mount point.
This set can be viewed on github here
https://github.com/allisonhenderson/xfs/tree/xfs_new_pptrsv10
And the corresponding xfsprogs code is here
https://github.com/allisonhenderson/xfsprogs/tree/xfsprogs_new_pptrs_v10
This set has been tested with the below parent pointers tests
https://lore.kernel.org/fstests/20221012013812.82161-1-catherine.hoang@oracle.com/T/#t
Updates since v9:
Reordered patches 2 and 3 to be 6 and 7
xfs: Add xfs_verify_pptr
moved parent pointer validators to xfs_parent
xfs: Add parent pointer ioctl
Extra validation checks for fs id
added missing release for the inode
use GFP_KERNEL flags for malloc/realloc
reworked ioctl to use pptr listenty and flex array
NEW
xfs: don't remove the attr fork when parent pointers are enabled
NEW
directory lookups should return diroffsets too
NEW
xfs: move/add parent pointer validators to xfs_parent
Updates since v8:
xfs: parent pointer attribute creation
Fix xfs_parent_init to release log assist on alloc fail
Add slab cache for xfs_parent_defer
Fix xfs_create to release after unlock
Add xfs_parent_start and xfs_parent_finish wrappers
removed unused xfs_parent_name_irec and xfs_init_parent_name_irec
xfs: add parent attributes to link
Start/finish wrapper updates
Fix xfs_link to disallow reservationless quotas
xfs: add parent attributes to symlink
Fix xfs_symlink to release after unlock
Start/finish wrapper updates
xfs: remove parent pointers in unlink
Start/finish wrapper updates
Add missing parent free
xfs: Add parent pointers to rename
Start/finish wrapper updates
Fix rename to only grab logged xattr once
Fix xfs_rename to disallow reservationless quotas
Fix double unlock on dqattach fail
Move parent frees to out_release_wip
xfs: Add parent pointers to xfs_cross_rename
Hoist parent pointers into rename
Questions comments and feedback appreciated!
Thanks all!
Allison
If you're going to start using this mess, you probably ought to just
pull from my git trees, which are linked below.
This is an extraordinary way to destroy everything. Enjoy!
Comments and questions are, as always, welcome.
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=pptrs
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=pptrs
fstests git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfstests-dev.git/log/?h=pptrs
---
db/Makefile | 2
db/attr.c | 65 +++++
db/attrshort.c | 51 ++++
db/hash.c | 374 +++++++++++++++++++++++++++
db/metadump.c | 646 +++++++++++++++++++++-------------------------
db/namei.c | 335 ++++++++++++++++++++++++
db/obfuscate.c | 348 +++++++++++++++++++++++++
db/obfuscate.h | 16 +
include/handle.h | 1
include/libxfs.h | 2
include/xfs.h | 1
include/xfs_inode.h | 6
io/parent.c | 527 +++++++++++++-------------------------
libfrog/Makefile | 2
libfrog/fsgeom.c | 4
libfrog/getparents.c | 338 ++++++++++++++++++++++++
libfrog/getparents.h | 36 +++
libfrog/paths.c | 167 ++++++++++++
libfrog/paths.h | 25 ++
libhandle/handle.c | 7
libxfs/Makefile | 3
libxfs/init.c | 7
libxfs/libxfs_api_defs.h | 6
libxfs/libxfs_priv.h | 6
libxfs/util.c | 14 +
libxfs/xfs_attr.c | 21 +
libxfs/xfs_attr.h | 14 +
libxfs/xfs_attr_leaf.c | 6
libxfs/xfs_attr_sf.h | 1
libxfs/xfs_da_format.h | 34 ++
libxfs/xfs_defer.c | 28 ++
libxfs/xfs_defer.h | 8 -
libxfs/xfs_format.h | 4
libxfs/xfs_fs.h | 2
libxfs/xfs_fs_staging.h | 84 ++++++
libxfs/xfs_log_format.h | 1
libxfs/xfs_log_rlimit.c | 53 ++++
libxfs/xfs_parent.c | 318 +++++++++++++++++++++++
libxfs/xfs_parent.h | 104 +++++++
libxfs/xfs_sb.c | 4
libxfs/xfs_trans_resv.c | 324 +++++++++++++++++++----
libxfs/xfs_trans_space.h | 8 -
logprint/log_redo.c | 81 ++++++
man/man3/xfsctl.3 | 63 ++++
man/man8/xfs_db.8 | 40 +++
man/man8/xfs_io.8 | 30 +-
mkfs/proto.c | 48 +++
mkfs/xfs_mkfs.c | 31 ++
repair/attr_repair.c | 19 +
repair/phase6.c | 6
scrub/common.c | 41 +++
51 files changed, 3562 insertions(+), 800 deletions(-)
create mode 100644 db/obfuscate.c
create mode 100644 db/obfuscate.h
create mode 100644 libfrog/getparents.c
create mode 100644 libfrog/getparents.h
create mode 100644 libxfs/xfs_fs_staging.h
create mode 100644 libxfs/xfs_parent.c
create mode 100644 libxfs/xfs_parent.h
^ permalink raw reply [flat|nested] 121+ messages in thread
* [PATCHSET v11 0/7] xfs_repair: support parent pointers
2023-04-06 18:10 [PATCHSET DELUGE v11] xfs: Parent Pointers Darrick J. Wong
` (9 preceding siblings ...)
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
@ 2023-04-06 19:16 ` Darrick J. Wong
2023-04-06 19:40 ` [PATCH 1/7] xfs_repair: build a parent pointer index Darrick J. Wong
` (6 more replies)
2023-04-06 19:16 ` [PATCHSET v11 00/11] fstests: adjust tests for xfs parent pointers Darrick J. Wong
11 siblings, 7 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:16 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
Hi all,
This patchset implements offline checking and repair for parent
pointers. We do this rather expensively by constructing a (per-AG)
master list of parent pointers for inodes rooted in that AG. Next, we
walk each inode of that AG, construct an index of that file's parent
pointers, and then compare the file index against the relevant part of
the master index. From there we can sync the parent pointers as needed.
If you're going to start using this mess, you probably ought to just
pull from my git trees, which are linked below.
This is an extraordinary way to destroy everything. Enjoy!
Comments and questions are, as always, welcome.
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=pptrs-offline-repair
---
libxfs/libxfs_api_defs.h | 7
libxfs/xfblob.c | 9
libxfs/xfblob.h | 2
repair/Makefile | 6
repair/listxattr.c | 271 ++++++++++
repair/listxattr.h | 15 +
repair/phase6.c | 41 +
repair/pptr.c | 1271 ++++++++++++++++++++++++++++++++++++++++++++++
repair/pptr.h | 17 +
repair/strblobs.c | 212 ++++++++
repair/strblobs.h | 24 +
11 files changed, 1873 insertions(+), 2 deletions(-)
create mode 100644 repair/listxattr.c
create mode 100644 repair/listxattr.h
create mode 100644 repair/pptr.c
create mode 100644 repair/pptr.h
create mode 100644 repair/strblobs.c
create mode 100644 repair/strblobs.h
^ permalink raw reply [flat|nested] 121+ messages in thread
* [PATCHSET v11 00/11] fstests: adjust tests for xfs parent pointers
2023-04-06 18:10 [PATCHSET DELUGE v11] xfs: Parent Pointers Darrick J. Wong
` (10 preceding siblings ...)
2023-04-06 19:16 ` [PATCHSET v11 0/7] xfs_repair: support " Darrick J. Wong
@ 2023-04-06 19:16 ` Darrick J. Wong
2023-04-06 19:41 ` [PATCH 01/11] xfs/206: filter out the parent= status from mkfs Darrick J. Wong
` (10 more replies)
11 siblings, 11 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:16 UTC (permalink / raw)
To: zlang, djwong
Cc: Allison Henderson, Catherine Hoang, linux-xfs, fstests, guan
Hi all,
These are the test adjustments that are required for parent pointers.
There's also a few new tests to ensure that the GETPARENTS ioctl (and
hence the ondisk parent pointers) work the way they're supposed to.
If you're going to start using this mess, you probably ought to just
pull from my git trees, which are linked below.
This is an extraordinary way to destroy everything. Enjoy!
Comments and questions are, as always, welcome.
--D
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=pptrs
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=pptrs
fstests git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfstests-dev.git/log/?h=pptrs
---
common/parent | 209 +++++++
common/populate | 38 +
common/rc | 7
common/xfs | 12
doc/group-names.txt | 1
src/popdir.pl | 11
tests/generic/050 | 10
tests/generic/050.cfg | 1
tests/generic/050.out.xfsquotaparent | 23 +
tests/xfs/018 | 7
tests/xfs/021 | 15 -
tests/xfs/021.cfg | 1
tests/xfs/021.out.default | 0
tests/xfs/021.out.parent | 62 ++
tests/xfs/122.out | 3
tests/xfs/191 | 7
tests/xfs/206 | 3
tests/xfs/288 | 7
tests/xfs/306 | 9
tests/xfs/851 | 116 ++++
tests/xfs/851.out | 69 ++
tests/xfs/852 | 69 ++
tests/xfs/852.out | 1002 ++++++++++++++++++++++++++++++++++
tests/xfs/853 | 85 +++
tests/xfs/853.out | 14
25 files changed, 1770 insertions(+), 11 deletions(-)
create mode 100644 common/parent
create mode 100644 tests/generic/050.out.xfsquotaparent
create mode 100644 tests/xfs/021.cfg
rename tests/xfs/{021.out => 021.out.default} (100%)
create mode 100644 tests/xfs/021.out.parent
create mode 100755 tests/xfs/851
create mode 100644 tests/xfs/851.out
create mode 100755 tests/xfs/852
create mode 100644 tests/xfs/852.out
create mode 100755 tests/xfs/853
create mode 100644 tests/xfs/853.out
^ permalink raw reply [flat|nested] 121+ messages in thread
* [PATCH 01/12] xfs: check opcode and iovec count match in xlog_recover_attri_commit_pass2
2023-04-06 19:13 ` [PATCHSET v11 00/12] xfs: name-value xattr lookups Darrick J. Wong
@ 2023-04-06 19:16 ` Darrick J. Wong
2023-04-06 19:17 ` [PATCH 02/12] xfs: make xfs_attr_set require XFS_DA_OP_REMOVE Darrick J. Wong
` (10 subsequent siblings)
11 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:16 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Check that the number of recovered log iovecs is what is expected for
the xattri opcode is expecting.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/xfs_attr_item.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
index 2788a6f2edcd..61ed139af8b1 100644
--- a/fs/xfs/xfs_attr_item.c
+++ b/fs/xfs/xfs_attr_item.c
@@ -710,6 +710,7 @@ xlog_recover_attri_commit_pass2(
const void *attr_value = NULL;
const void *attr_name;
size_t len;
+ unsigned int op;
attri_formatp = item->ri_buf[0].i_addr;
attr_name = item->ri_buf[1].i_addr;
@@ -728,6 +729,32 @@ xlog_recover_attri_commit_pass2(
return -EFSCORRUPTED;
}
+ /* Check the number of log iovecs makes sense for the op code. */
+ op = attri_formatp->alfi_op_flags & XFS_ATTRI_OP_FLAGS_TYPE_MASK;
+ switch (op) {
+ case XFS_ATTRI_OP_FLAGS_SET:
+ case XFS_ATTRI_OP_FLAGS_REPLACE:
+ /* Log item, attr name, attr value */
+ if (item->ri_total != 3) {
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ attri_formatp, len);
+ return -EFSCORRUPTED;
+ }
+ break;
+ case XFS_ATTRI_OP_FLAGS_REMOVE:
+ /* Log item, attr name */
+ if (item->ri_total != 2) {
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ attri_formatp, len);
+ return -EFSCORRUPTED;
+ }
+ break;
+ default:
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ attri_formatp, len);
+ return -EFSCORRUPTED;
+ }
+
/* Validate the attr name */
if (item->ri_buf[1].i_len !=
xlog_calc_iovec_len(attri_formatp->alfi_name_len)) {
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 02/12] xfs: make xfs_attr_set require XFS_DA_OP_REMOVE
2023-04-06 19:13 ` [PATCHSET v11 00/12] xfs: name-value xattr lookups Darrick J. Wong
2023-04-06 19:16 ` [PATCH 01/12] xfs: check opcode and iovec count match in xlog_recover_attri_commit_pass2 Darrick J. Wong
@ 2023-04-06 19:17 ` Darrick J. Wong
2023-04-06 19:17 ` [PATCH 03/12] xfs: allow xattr matching on name and value for local/sf attrs Darrick J. Wong
` (9 subsequent siblings)
11 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:17 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Quite a few patches from now, we're going to change the parent pointer
xattr format to encode as much of the dirent name in the xattr name as
fits, and spill the rest of it to the xattr value. To make this work
correctly, we'll be adding the ability to look up xattrs based on name
/and/ value.
Internally, the xattr data structure supports attributes with a zero
length value, which is how we're going to store parent pointers for
short dirent names. The parent pointer repair code uses xfs_attr_set to
add missing and remove dangling parent pointers, so that interface must
be capable of setting an xattr with args->value == NULL.
The userspace API doesn't support this, so xfs_attr_set currently treats
a NULL args->value as a request to remove an attr. However, that's a
quirk of the existing callers and the interface. Make the callers of
xfs_attr_set to declare explicitly that they want to remove an xattr.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_attr.c | 9 +++++----
fs/xfs/xfs_xattr.c | 5 +++++
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index e28d93d232de..1518b786cdaa 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -967,6 +967,7 @@ xfs_attr_set(
struct xfs_mount *mp = dp->i_mount;
struct xfs_trans_res tres;
bool rsvd = (args->attr_filter & XFS_ATTR_ROOT);
+ bool is_remove = args->op_flags & XFS_DA_OP_REMOVE;
int error, local;
int rmt_blks = 0;
unsigned int total;
@@ -991,7 +992,7 @@ xfs_attr_set(
args->op_flags = XFS_DA_OP_OKNOENT |
(args->op_flags & XFS_DA_OP_LOGGED);
- if (args->value) {
+ if (!is_remove) {
XFS_STATS_INC(mp, xs_attr_set);
args->total = xfs_attr_calc_size(args, &local);
@@ -1025,7 +1026,7 @@ xfs_attr_set(
if (error)
return error;
- if (args->value || xfs_inode_hasattr(dp)) {
+ if (!is_remove || xfs_inode_hasattr(dp)) {
error = xfs_iext_count_may_overflow(dp, XFS_ATTR_FORK,
XFS_IEXT_ATTR_MANIP_CNT(rmt_blks));
if (error == -EFBIG)
@@ -1039,7 +1040,7 @@ xfs_attr_set(
switch (error) {
case -EEXIST:
/* if no value, we are performing a remove operation */
- if (!args->value) {
+ if (is_remove) {
error = xfs_attr_defer_remove(args);
break;
}
@@ -1051,7 +1052,7 @@ xfs_attr_set(
break;
case -ENOATTR:
/* Can't remove what isn't there. */
- if (!args->value)
+ if (is_remove)
goto out_trans_cancel;
/* Pure replace fails if no existing attr to replace. */
diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
index 7b9a0ed1b11f..61779d92295f 100644
--- a/fs/xfs/xfs_xattr.c
+++ b/fs/xfs/xfs_xattr.c
@@ -103,6 +103,11 @@ xfs_attr_change(
use_logging = true;
}
+ if (args->value)
+ args->op_flags &= ~XFS_DA_OP_REMOVE;
+ else
+ args->op_flags |= XFS_DA_OP_REMOVE;
+
error = xfs_attr_set(args);
if (use_logging)
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 03/12] xfs: allow xattr matching on name and value for local/sf attrs
2023-04-06 19:13 ` [PATCHSET v11 00/12] xfs: name-value xattr lookups Darrick J. Wong
2023-04-06 19:16 ` [PATCH 01/12] xfs: check opcode and iovec count match in xlog_recover_attri_commit_pass2 Darrick J. Wong
2023-04-06 19:17 ` [PATCH 02/12] xfs: make xfs_attr_set require XFS_DA_OP_REMOVE Darrick J. Wong
@ 2023-04-06 19:17 ` Darrick J. Wong
2023-04-06 19:17 ` [PATCH 04/12] xfs: preserve NVLOOKUP in xfs_attr_set Darrick J. Wong
` (8 subsequent siblings)
11 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:17 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add a new NVLOOKUP flag to signal that the caller wants to look up an
extended attribute by name and value. This only works with shortform
and local attributes. Only parent pointers need this functionality
and parent pointers cannot be remote xattrs, so this limitation is ok
for now.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_attr_leaf.c | 45 ++++++++++++++++++++++++++++++++++-------
fs/xfs/libxfs/xfs_da_btree.h | 4 +++-
2 files changed, 40 insertions(+), 9 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index beee51ad75ce..c38caa8cba3a 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -473,10 +473,12 @@ xfs_attr3_leaf_read(
*/
static bool
xfs_attr_match(
- struct xfs_da_args *args,
- uint8_t namelen,
- unsigned char *name,
- int flags)
+ const struct xfs_da_args *args,
+ uint8_t namelen,
+ const unsigned char *name,
+ unsigned int valuelen,
+ const void *value,
+ int flags)
{
if (args->namelen != namelen)
@@ -484,6 +486,23 @@ xfs_attr_match(
if (memcmp(args->name, name, namelen) != 0)
return false;
+ if (args->op_flags & XFS_DA_OP_NVLOOKUP) {
+ if (args->valuelen != valuelen)
+ return false;
+ if (args->valuelen && !value) {
+ /* not implemented for remote values */
+ ASSERT(0);
+ return false;
+ }
+ if (valuelen && !args->value) {
+ /* caller gave us valuelen > 0 but no value?? */
+ ASSERT(0);
+ return false;
+ }
+ if (valuelen > 0 && memcmp(args->value, value, valuelen) != 0)
+ return false;
+ }
+
/* Recovery ignores the INCOMPLETE flag. */
if ((args->op_flags & XFS_DA_OP_RECOVERY) &&
args->attr_filter == (flags & XFS_ATTR_NSP_ONDISK_MASK))
@@ -502,6 +521,10 @@ xfs_attr_copy_value(
unsigned char *value,
int valuelen)
{
+ /* vlookups already supplied the attr value; don't copy anything */
+ if (args->op_flags & XFS_DA_OP_NVLOOKUP)
+ return 0;
+
/*
* No copy if all we have to do is get the length
*/
@@ -726,6 +749,7 @@ xfs_attr_sf_findname(
base += size, i++) {
size = xfs_attr_sf_entsize(sfe);
if (!xfs_attr_match(args, sfe->namelen, sfe->nameval,
+ sfe->valuelen, &sfe->nameval[sfe->namelen],
sfe->flags))
continue;
break;
@@ -894,6 +918,7 @@ xfs_attr_shortform_lookup(xfs_da_args_t *args)
for (i = 0; i < sf->hdr.count;
sfe = xfs_attr_sf_nextentry(sfe), i++) {
if (xfs_attr_match(args, sfe->namelen, sfe->nameval,
+ sfe->valuelen, &sfe->nameval[sfe->namelen],
sfe->flags))
return -EEXIST;
}
@@ -921,6 +946,7 @@ xfs_attr_shortform_getvalue(
for (i = 0; i < sf->hdr.count;
sfe = xfs_attr_sf_nextentry(sfe), i++) {
if (xfs_attr_match(args, sfe->namelen, sfe->nameval,
+ sfe->valuelen, &sfe->nameval[sfe->namelen],
sfe->flags))
return xfs_attr_copy_value(args,
&sfe->nameval[args->namelen], sfe->valuelen);
@@ -973,7 +999,7 @@ xfs_attr_shortform_to_leaf(
nargs.total = args->total;
nargs.whichfork = XFS_ATTR_FORK;
nargs.trans = args->trans;
- nargs.op_flags = XFS_DA_OP_OKNOENT;
+ nargs.op_flags = XFS_DA_OP_OKNOENT | XFS_DA_OP_NVLOOKUP;
sfe = &sf->list[0];
for (i = 0; i < sf->hdr.count; i++) {
@@ -1183,7 +1209,7 @@ xfs_attr3_leaf_to_shortform(
nargs.total = args->total;
nargs.whichfork = XFS_ATTR_FORK;
nargs.trans = args->trans;
- nargs.op_flags = XFS_DA_OP_OKNOENT;
+ nargs.op_flags = XFS_DA_OP_OKNOENT | XFS_DA_OP_NVLOOKUP;
for (i = 0; i < ichdr.count; entry++, i++) {
if (entry->flags & XFS_ATTR_INCOMPLETE)
@@ -2482,14 +2508,17 @@ xfs_attr3_leaf_lookup_int(
if (entry->flags & XFS_ATTR_LOCAL) {
name_loc = xfs_attr3_leaf_name_local(leaf, probe);
if (!xfs_attr_match(args, name_loc->namelen,
- name_loc->nameval, entry->flags))
+ name_loc->nameval,
+ be16_to_cpu(name_loc->valuelen),
+ &name_loc->nameval[name_loc->namelen],
+ entry->flags))
continue;
args->index = probe;
return -EEXIST;
} else {
name_rmt = xfs_attr3_leaf_name_remote(leaf, probe);
if (!xfs_attr_match(args, name_rmt->namelen,
- name_rmt->name, entry->flags))
+ name_rmt->name, 0, NULL, entry->flags))
continue;
args->index = probe;
args->rmtvaluelen = be32_to_cpu(name_rmt->valuelen);
diff --git a/fs/xfs/libxfs/xfs_da_btree.h b/fs/xfs/libxfs/xfs_da_btree.h
index ffa3df5b2893..94a544fc8a9c 100644
--- a/fs/xfs/libxfs/xfs_da_btree.h
+++ b/fs/xfs/libxfs/xfs_da_btree.h
@@ -93,6 +93,7 @@ typedef struct xfs_da_args {
#define XFS_DA_OP_REMOVE (1u << 6) /* this is a remove operation */
#define XFS_DA_OP_RECOVERY (1u << 7) /* Log recovery operation */
#define XFS_DA_OP_LOGGED (1u << 8) /* Use intent items to track op */
+#define XFS_DA_OP_NVLOOKUP (1u << 9) /* Match local attr on name+value */
#define XFS_DA_OP_FLAGS \
{ XFS_DA_OP_JUSTCHECK, "JUSTCHECK" }, \
@@ -103,7 +104,8 @@ typedef struct xfs_da_args {
{ XFS_DA_OP_NOTIME, "NOTIME" }, \
{ XFS_DA_OP_REMOVE, "REMOVE" }, \
{ XFS_DA_OP_RECOVERY, "RECOVERY" }, \
- { XFS_DA_OP_LOGGED, "LOGGED" }
+ { XFS_DA_OP_LOGGED, "LOGGED" }, \
+ { XFS_DA_OP_NVLOOKUP, "NVLOOKUP" }
/*
* Storage for holding state during Btree searches and split/join ops.
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 04/12] xfs: preserve NVLOOKUP in xfs_attr_set
2023-04-06 19:13 ` [PATCHSET v11 00/12] xfs: name-value xattr lookups Darrick J. Wong
` (2 preceding siblings ...)
2023-04-06 19:17 ` [PATCH 03/12] xfs: allow xattr matching on name and value for local/sf attrs Darrick J. Wong
@ 2023-04-06 19:17 ` Darrick J. Wong
2023-04-06 19:17 ` [PATCH 05/12] xfs: restructure xfs_attr_complete_op a bit Darrick J. Wong
` (7 subsequent siblings)
11 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:17 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Preserve the attr-value lookup flag when calling xfs_attr_set. Normal
xattr users will never use this, but parent pointer fsck will.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_attr.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 1518b786cdaa..2897fdc8372e 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -986,11 +986,11 @@ xfs_attr_set(
/*
* We have no control over the attribute names that userspace passes us
* to remove, so we have to allow the name lookup prior to attribute
- * removal to fail as well. Preserve the logged flag, since we need
- * to pass that through to the logging code.
+ * removal to fail as well. Preserve the logged and vlookup flags,
+ * since we need to pass them through to the lower levels.
*/
- args->op_flags = XFS_DA_OP_OKNOENT |
- (args->op_flags & XFS_DA_OP_LOGGED);
+ args->op_flags &= (XFS_DA_OP_LOGGED | XFS_DA_OP_NVLOOKUP);
+ args->op_flags |= XFS_DA_OP_OKNOENT;
if (!is_remove) {
XFS_STATS_INC(mp, xs_attr_set);
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 05/12] xfs: restructure xfs_attr_complete_op a bit
2023-04-06 19:13 ` [PATCHSET v11 00/12] xfs: name-value xattr lookups Darrick J. Wong
` (3 preceding siblings ...)
2023-04-06 19:17 ` [PATCH 04/12] xfs: preserve NVLOOKUP in xfs_attr_set Darrick J. Wong
@ 2023-04-06 19:17 ` Darrick J. Wong
2023-04-06 19:18 ` [PATCH 06/12] xfs: use helpers to extract xattr op from opflags Darrick J. Wong
` (6 subsequent siblings)
11 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:17 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Reduce the indentation in this function by flattening the nested if
statements. We're going to add more code later to this function later,
hence the early cleanup. No functional changes.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_attr.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 2897fdc8372e..b0da2e862ef9 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -421,11 +421,11 @@ xfs_attr_complete_op(
bool do_replace = args->op_flags & XFS_DA_OP_REPLACE;
args->op_flags &= ~XFS_DA_OP_REPLACE;
- if (do_replace) {
- args->attr_filter &= ~XFS_ATTR_INCOMPLETE;
- return replace_state;
- }
- return XFS_DAS_DONE;
+ if (!do_replace)
+ return XFS_DAS_DONE;
+
+ args->attr_filter &= ~XFS_ATTR_INCOMPLETE;
+ return replace_state;
}
static int
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 06/12] xfs: use helpers to extract xattr op from opflags
2023-04-06 19:13 ` [PATCHSET v11 00/12] xfs: name-value xattr lookups Darrick J. Wong
` (4 preceding siblings ...)
2023-04-06 19:17 ` [PATCH 05/12] xfs: restructure xfs_attr_complete_op a bit Darrick J. Wong
@ 2023-04-06 19:18 ` Darrick J. Wong
2023-04-06 19:18 ` [PATCH 07/12] xfs: validate recovered name buffers when recovering xattr items Darrick J. Wong
` (5 subsequent siblings)
11 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:18 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Create helper functions to extract the xattr op from the ondisk xattri
log item and the incore attr intent item. These will get more use in
the patches that follow.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_attr.h | 5 +++++
fs/xfs/xfs_attr_item.c | 16 ++++++++++------
2 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 81be9b3e4004..f0aa372ecba1 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -529,6 +529,11 @@ struct xfs_attr_intent {
struct xfs_bmbt_irec xattri_map;
};
+static inline unsigned int
+xfs_attr_intent_op(const struct xfs_attr_intent *attr)
+{
+ return attr->xattri_op_flags & XFS_ATTRI_OP_FLAGS_TYPE_MASK;
+}
/*========================================================================
* Function prototypes for the kernel.
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
index 61ed139af8b1..23fee7c0938f 100644
--- a/fs/xfs/xfs_attr_item.c
+++ b/fs/xfs/xfs_attr_item.c
@@ -351,6 +351,12 @@ xfs_xattri_finish_update(
return error;
}
+static inline unsigned int
+xfs_attr_log_item_op(const struct xfs_attri_log_format *attrp)
+{
+ return attrp->alfi_op_flags & XFS_ATTRI_OP_FLAGS_TYPE_MASK;
+}
+
/* Log an attr to the intent item. */
STATIC void
xfs_attr_log_item(
@@ -500,8 +506,7 @@ xfs_attri_validate(
struct xfs_mount *mp,
struct xfs_attri_log_format *attrp)
{
- unsigned int op = attrp->alfi_op_flags &
- XFS_ATTRI_OP_FLAGS_TYPE_MASK;
+ unsigned int op = xfs_attr_log_item_op(attrp);
if (attrp->__pad != 0)
return false;
@@ -573,8 +578,7 @@ xfs_attri_item_recover(
args = (struct xfs_da_args *)(attr + 1);
attr->xattri_da_args = args;
- attr->xattri_op_flags = attrp->alfi_op_flags &
- XFS_ATTRI_OP_FLAGS_TYPE_MASK;
+ attr->xattri_op_flags = xfs_attr_log_item_op(attrp);
/*
* We're reconstructing the deferred work state structure from the
@@ -596,7 +600,7 @@ xfs_attri_item_recover(
ASSERT(xfs_sb_version_haslogxattrs(&mp->m_sb));
- switch (attr->xattri_op_flags) {
+ switch (xfs_attr_intent_op(attr)) {
case XFS_ATTRI_OP_FLAGS_SET:
case XFS_ATTRI_OP_FLAGS_REPLACE:
args->value = nv->value.i_addr;
@@ -730,7 +734,7 @@ xlog_recover_attri_commit_pass2(
}
/* Check the number of log iovecs makes sense for the op code. */
- op = attri_formatp->alfi_op_flags & XFS_ATTRI_OP_FLAGS_TYPE_MASK;
+ op = xfs_attr_log_item_op(attri_formatp);
switch (op) {
case XFS_ATTRI_OP_FLAGS_SET:
case XFS_ATTRI_OP_FLAGS_REPLACE:
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 07/12] xfs: validate recovered name buffers when recovering xattr items
2023-04-06 19:13 ` [PATCHSET v11 00/12] xfs: name-value xattr lookups Darrick J. Wong
` (5 preceding siblings ...)
2023-04-06 19:18 ` [PATCH 06/12] xfs: use helpers to extract xattr op from opflags Darrick J. Wong
@ 2023-04-06 19:18 ` Darrick J. Wong
2023-04-06 19:18 ` [PATCH 08/12] xfs: always set args->value in xfs_attri_item_recover Darrick J. Wong
` (4 subsequent siblings)
11 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:18 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Strengthen the xattri log item recovery code by checking that we
actually have the required name and newname buffers for whatever
operation we're replaying.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/xfs_attr_item.c | 58 +++++++++++++++++++++++++++++++++++++++---------
1 file changed, 47 insertions(+), 11 deletions(-)
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
index 23fee7c0938f..082d7790fc2c 100644
--- a/fs/xfs/xfs_attr_item.c
+++ b/fs/xfs/xfs_attr_item.c
@@ -714,22 +714,20 @@ xlog_recover_attri_commit_pass2(
const void *attr_value = NULL;
const void *attr_name;
size_t len;
- unsigned int op;
-
- attri_formatp = item->ri_buf[0].i_addr;
- attr_name = item->ri_buf[1].i_addr;
+ unsigned int op, i = 0;
/* Validate xfs_attri_log_format before the large memory allocation */
len = sizeof(struct xfs_attri_log_format);
- if (item->ri_buf[0].i_len != len) {
+ if (item->ri_buf[i].i_len != len) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
return -EFSCORRUPTED;
}
+ attri_formatp = item->ri_buf[i].i_addr;
if (!xfs_attri_validate(mp, attri_formatp)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
- item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
+ attri_formatp, len);
return -EFSCORRUPTED;
}
@@ -758,31 +756,69 @@ xlog_recover_attri_commit_pass2(
attri_formatp, len);
return -EFSCORRUPTED;
}
+ i++;
/* Validate the attr name */
- if (item->ri_buf[1].i_len !=
+ if (item->ri_buf[i].i_len !=
xlog_calc_iovec_len(attri_formatp->alfi_name_len)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
- item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
+ attri_formatp, len);
return -EFSCORRUPTED;
}
+ attr_name = item->ri_buf[i].i_addr;
if (!xfs_attr_namecheck(attr_name, attri_formatp->alfi_name_len)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
- item->ri_buf[1].i_addr, item->ri_buf[1].i_len);
+ attri_formatp, len);
return -EFSCORRUPTED;
}
+ i++;
/* Validate the attr value, if present */
if (attri_formatp->alfi_value_len != 0) {
- if (item->ri_buf[2].i_len != xlog_calc_iovec_len(attri_formatp->alfi_value_len)) {
+ if (item->ri_buf[i].i_len != xlog_calc_iovec_len(attri_formatp->alfi_value_len)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
item->ri_buf[0].i_addr,
item->ri_buf[0].i_len);
return -EFSCORRUPTED;
}
- attr_value = item->ri_buf[2].i_addr;
+ attr_value = item->ri_buf[i].i_addr;
+ i++;
+ }
+
+ /*
+ * Make sure we got the correct number of buffers for the operation
+ * that we just loaded.
+ */
+ if (i != item->ri_total) {
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ attri_formatp, len);
+ return -EFSCORRUPTED;
+ }
+
+ switch (op) {
+ case XFS_ATTRI_OP_FLAGS_REMOVE:
+ /* Regular remove operations operate only on names. */
+ if (attr_value != NULL || attri_formatp->alfi_value_len != 0) {
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ attri_formatp, len);
+ return -EFSCORRUPTED;
+ }
+ fallthrough;
+ case XFS_ATTRI_OP_FLAGS_SET:
+ case XFS_ATTRI_OP_FLAGS_REPLACE:
+ /*
+ * Regular xattr set/remove/replace operations require a name
+ * and do not take a newname. Values are optional for set and
+ * replace.
+ */
+ if (attr_name == NULL || attri_formatp->alfi_name_len == 0) {
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ attri_formatp, len);
+ return -EFSCORRUPTED;
+ }
+ break;
}
/*
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 08/12] xfs: always set args->value in xfs_attri_item_recover
2023-04-06 19:13 ` [PATCHSET v11 00/12] xfs: name-value xattr lookups Darrick J. Wong
` (6 preceding siblings ...)
2023-04-06 19:18 ` [PATCH 07/12] xfs: validate recovered name buffers when recovering xattr items Darrick J. Wong
@ 2023-04-06 19:18 ` Darrick J. Wong
2023-04-06 19:18 ` [PATCH 09/12] xfs: use local variables for name and value length in _attri_commit_pass2 Darrick J. Wong
` (3 subsequent siblings)
11 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:18 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Always set args->value to the recovered value buffer. This reduces the
amount of code in the switch statement, and hence the amount of thinking
that I have to do. We validated the recovered buffers, supposedly.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/xfs_attr_item.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
index 082d7790fc2c..07a6cec3dfb9 100644
--- a/fs/xfs/xfs_attr_item.c
+++ b/fs/xfs/xfs_attr_item.c
@@ -594,6 +594,8 @@ xfs_attri_item_recover(
args->name = nv->name.i_addr;
args->namelen = nv->name.i_len;
args->hashval = xfs_da_hashname(args->name, args->namelen);
+ args->value = nv->value.i_addr;
+ args->valuelen = nv->value.i_len;
args->attr_filter = attrp->alfi_attr_filter & XFS_ATTRI_FILTER_MASK;
args->op_flags = XFS_DA_OP_RECOVERY | XFS_DA_OP_OKNOENT |
XFS_DA_OP_LOGGED;
@@ -603,8 +605,6 @@ xfs_attri_item_recover(
switch (xfs_attr_intent_op(attr)) {
case XFS_ATTRI_OP_FLAGS_SET:
case XFS_ATTRI_OP_FLAGS_REPLACE:
- args->value = nv->value.i_addr;
- args->valuelen = nv->value.i_len;
args->total = xfs_attr_calc_size(args, &local);
if (xfs_inode_hasattr(args->dp))
attr->xattri_dela_state = xfs_attr_init_replace_state(args);
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 09/12] xfs: use local variables for name and value length in _attri_commit_pass2
2023-04-06 19:13 ` [PATCHSET v11 00/12] xfs: name-value xattr lookups Darrick J. Wong
` (7 preceding siblings ...)
2023-04-06 19:18 ` [PATCH 08/12] xfs: always set args->value in xfs_attri_item_recover Darrick J. Wong
@ 2023-04-06 19:18 ` Darrick J. Wong
2023-04-06 19:19 ` [PATCH 10/12] xfs: log NVLOOKUP xattr removal operations Darrick J. Wong
` (2 subsequent siblings)
11 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:18 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
We're about to start using tagged unions in the xattr log format, so
create a bunch of local variables in the recovery function so we only
have to decode the log item fields once.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/xfs_attr_item.c | 25 ++++++++++++++-----------
1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
index 07a6cec3dfb9..f756bee9fb73 100644
--- a/fs/xfs/xfs_attr_item.c
+++ b/fs/xfs/xfs_attr_item.c
@@ -711,9 +711,11 @@ xlog_recover_attri_commit_pass2(
struct xfs_attri_log_item *attrip;
struct xfs_attri_log_format *attri_formatp;
struct xfs_attri_log_nameval *nv;
- const void *attr_value = NULL;
const void *attr_name;
+ const void *attr_value = NULL;
size_t len;
+ unsigned int name_len = 0;
+ unsigned int value_len = 0;
unsigned int op, i = 0;
/* Validate xfs_attri_log_format before the large memory allocation */
@@ -742,6 +744,8 @@ xlog_recover_attri_commit_pass2(
attri_formatp, len);
return -EFSCORRUPTED;
}
+ name_len = attri_formatp->alfi_name_len;
+ value_len = attri_formatp->alfi_value_len;
break;
case XFS_ATTRI_OP_FLAGS_REMOVE:
/* Log item, attr name */
@@ -750,6 +754,7 @@ xlog_recover_attri_commit_pass2(
attri_formatp, len);
return -EFSCORRUPTED;
}
+ name_len = attri_formatp->alfi_name_len;
break;
default:
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
@@ -759,15 +764,14 @@ xlog_recover_attri_commit_pass2(
i++;
/* Validate the attr name */
- if (item->ri_buf[i].i_len !=
- xlog_calc_iovec_len(attri_formatp->alfi_name_len)) {
+ if (item->ri_buf[i].i_len != xlog_calc_iovec_len(name_len)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
attri_formatp, len);
return -EFSCORRUPTED;
}
attr_name = item->ri_buf[i].i_addr;
- if (!xfs_attr_namecheck(attr_name, attri_formatp->alfi_name_len)) {
+ if (!xfs_attr_namecheck(attr_name, name_len)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
attri_formatp, len);
return -EFSCORRUPTED;
@@ -775,8 +779,8 @@ xlog_recover_attri_commit_pass2(
i++;
/* Validate the attr value, if present */
- if (attri_formatp->alfi_value_len != 0) {
- if (item->ri_buf[i].i_len != xlog_calc_iovec_len(attri_formatp->alfi_value_len)) {
+ if (value_len != 0) {
+ if (item->ri_buf[i].i_len != xlog_calc_iovec_len(value_len)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
item->ri_buf[0].i_addr,
item->ri_buf[0].i_len);
@@ -800,7 +804,7 @@ xlog_recover_attri_commit_pass2(
switch (op) {
case XFS_ATTRI_OP_FLAGS_REMOVE:
/* Regular remove operations operate only on names. */
- if (attr_value != NULL || attri_formatp->alfi_value_len != 0) {
+ if (attr_value != NULL || value_len != 0) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
attri_formatp, len);
return -EFSCORRUPTED;
@@ -813,7 +817,7 @@ xlog_recover_attri_commit_pass2(
* and do not take a newname. Values are optional for set and
* replace.
*/
- if (attr_name == NULL || attri_formatp->alfi_name_len == 0) {
+ if (attr_name == NULL || name_len == 0) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
attri_formatp, len);
return -EFSCORRUPTED;
@@ -826,9 +830,8 @@ xlog_recover_attri_commit_pass2(
* name/value buffer to the recovered incore log item and drop our
* reference.
*/
- nv = xfs_attri_log_nameval_alloc(attr_name,
- attri_formatp->alfi_name_len, attr_value,
- attri_formatp->alfi_value_len);
+ nv = xfs_attri_log_nameval_alloc(attr_name, name_len,
+ attr_value, value_len);
attrip = xfs_attri_init(mp, nv);
memcpy(&attrip->attri_format, attri_formatp, len);
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 10/12] xfs: log NVLOOKUP xattr removal operations
2023-04-06 19:13 ` [PATCHSET v11 00/12] xfs: name-value xattr lookups Darrick J. Wong
` (8 preceding siblings ...)
2023-04-06 19:18 ` [PATCH 09/12] xfs: use local variables for name and value length in _attri_commit_pass2 Darrick J. Wong
@ 2023-04-06 19:19 ` Darrick J. Wong
2023-04-06 19:19 ` [PATCH 11/12] xfs: log NVLOOKUP xattr setting operations Darrick J. Wong
2023-04-06 19:19 ` [PATCH 12/12] xfs: log NVLOOKUP xattr nvreplace operations Darrick J. Wong
11 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:19 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If high level code wants to do a deferred xattr remove operation with
the NVLOOKUP flag set, we need to push this through the log.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_attr.c | 6 +++++-
fs/xfs/libxfs/xfs_log_format.h | 1 +
fs/xfs/xfs_attr_item.c | 18 ++++++++++++++++++
3 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index b0da2e862ef9..1f4dc6e01e68 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -942,9 +942,13 @@ xfs_attr_defer_remove(
{
struct xfs_attr_intent *new;
+ int op_flag = XFS_ATTRI_OP_FLAGS_REMOVE;
int error;
- error = xfs_attr_intent_init(args, XFS_ATTRI_OP_FLAGS_REMOVE, &new);
+ if (args->op_flags & XFS_DA_OP_NVLOOKUP)
+ op_flag = XFS_ATTRI_OP_FLAGS_NVREMOVE;
+
+ error = xfs_attr_intent_init(args, op_flag, &new);
if (error)
return error;
diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h
index f13e0809dc63..ecf0ac32d752 100644
--- a/fs/xfs/libxfs/xfs_log_format.h
+++ b/fs/xfs/libxfs/xfs_log_format.h
@@ -957,6 +957,7 @@ struct xfs_icreate_log {
#define XFS_ATTRI_OP_FLAGS_SET 1 /* Set the attribute */
#define XFS_ATTRI_OP_FLAGS_REMOVE 2 /* Remove the attribute */
#define XFS_ATTRI_OP_FLAGS_REPLACE 3 /* Replace the attribute */
+#define XFS_ATTRI_OP_FLAGS_NVREMOVE 4 /* Remove attr w/ vlookup */
#define XFS_ATTRI_OP_FLAGS_TYPE_MASK 0xFF /* Flags type mask */
/*
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
index f756bee9fb73..dd73d30f9e8d 100644
--- a/fs/xfs/xfs_attr_item.c
+++ b/fs/xfs/xfs_attr_item.c
@@ -522,6 +522,7 @@ xfs_attri_validate(
case XFS_ATTRI_OP_FLAGS_SET:
case XFS_ATTRI_OP_FLAGS_REPLACE:
case XFS_ATTRI_OP_FLAGS_REMOVE:
+ case XFS_ATTRI_OP_FLAGS_NVREMOVE:
break;
default:
return false;
@@ -611,6 +612,9 @@ xfs_attri_item_recover(
else
attr->xattri_dela_state = xfs_attr_init_add_state(args);
break;
+ case XFS_ATTRI_OP_FLAGS_NVREMOVE:
+ args->op_flags |= XFS_DA_OP_NVLOOKUP;
+ fallthrough;
case XFS_ATTRI_OP_FLAGS_REMOVE:
if (!xfs_inode_hasattr(args->dp))
goto out;
@@ -736,6 +740,16 @@ xlog_recover_attri_commit_pass2(
/* Check the number of log iovecs makes sense for the op code. */
op = xfs_attr_log_item_op(attri_formatp);
switch (op) {
+ case XFS_ATTRI_OP_FLAGS_NVREMOVE:
+ /* Log item, attr name, optional attr value */
+ if (item->ri_total != 3 && item->ri_total != 2) {
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ attri_formatp, len);
+ return -EFSCORRUPTED;
+ }
+ name_len = attri_formatp->alfi_name_len;
+ value_len = attri_formatp->alfi_value_len;
+ break;
case XFS_ATTRI_OP_FLAGS_SET:
case XFS_ATTRI_OP_FLAGS_REPLACE:
/* Log item, attr name, attr value */
@@ -810,12 +824,16 @@ xlog_recover_attri_commit_pass2(
return -EFSCORRUPTED;
}
fallthrough;
+ case XFS_ATTRI_OP_FLAGS_NVREMOVE:
case XFS_ATTRI_OP_FLAGS_SET:
case XFS_ATTRI_OP_FLAGS_REPLACE:
/*
* Regular xattr set/remove/replace operations require a name
* and do not take a newname. Values are optional for set and
* replace.
+ *
+ * Name-value remove operations must have a name, do not
+ * take a newname, and can take a value.
*/
if (attr_name == NULL || name_len == 0) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 11/12] xfs: log NVLOOKUP xattr setting operations
2023-04-06 19:13 ` [PATCHSET v11 00/12] xfs: name-value xattr lookups Darrick J. Wong
` (9 preceding siblings ...)
2023-04-06 19:19 ` [PATCH 10/12] xfs: log NVLOOKUP xattr removal operations Darrick J. Wong
@ 2023-04-06 19:19 ` Darrick J. Wong
2023-04-06 19:19 ` [PATCH 12/12] xfs: log NVLOOKUP xattr nvreplace operations Darrick J. Wong
11 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:19 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If high level code wants to do a deferred xattr set operation with the
NVLOOKUP flag set, we need to push this through the log.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_attr.c | 6 +++++-
fs/xfs/libxfs/xfs_log_format.h | 1 +
fs/xfs/xfs_attr_item.c | 8 +++++++-
3 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 1f4dc6e01e68..287990a9b29d 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -903,9 +903,13 @@ xfs_attr_defer_add(
struct xfs_da_args *args)
{
struct xfs_attr_intent *new;
+ int op_flag = XFS_ATTRI_OP_FLAGS_SET;
int error = 0;
- error = xfs_attr_intent_init(args, XFS_ATTRI_OP_FLAGS_SET, &new);
+ if (args->op_flags & XFS_DA_OP_NVLOOKUP)
+ op_flag = XFS_ATTRI_OP_FLAGS_NVSET;
+
+ error = xfs_attr_intent_init(args, op_flag, &new);
if (error)
return error;
diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h
index ecf0ac32d752..7a4226e20dae 100644
--- a/fs/xfs/libxfs/xfs_log_format.h
+++ b/fs/xfs/libxfs/xfs_log_format.h
@@ -958,6 +958,7 @@ struct xfs_icreate_log {
#define XFS_ATTRI_OP_FLAGS_REMOVE 2 /* Remove the attribute */
#define XFS_ATTRI_OP_FLAGS_REPLACE 3 /* Replace the attribute */
#define XFS_ATTRI_OP_FLAGS_NVREMOVE 4 /* Remove attr w/ vlookup */
+#define XFS_ATTRI_OP_FLAGS_NVSET 5 /* Set attr with w/ vlookup */
#define XFS_ATTRI_OP_FLAGS_TYPE_MASK 0xFF /* Flags type mask */
/*
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
index dd73d30f9e8d..f8def28090f8 100644
--- a/fs/xfs/xfs_attr_item.c
+++ b/fs/xfs/xfs_attr_item.c
@@ -523,6 +523,7 @@ xfs_attri_validate(
case XFS_ATTRI_OP_FLAGS_REPLACE:
case XFS_ATTRI_OP_FLAGS_REMOVE:
case XFS_ATTRI_OP_FLAGS_NVREMOVE:
+ case XFS_ATTRI_OP_FLAGS_NVSET:
break;
default:
return false;
@@ -604,6 +605,9 @@ xfs_attri_item_recover(
ASSERT(xfs_sb_version_haslogxattrs(&mp->m_sb));
switch (xfs_attr_intent_op(attr)) {
+ case XFS_ATTRI_OP_FLAGS_NVSET:
+ args->op_flags |= XFS_DA_OP_NVLOOKUP;
+ fallthrough;
case XFS_ATTRI_OP_FLAGS_SET:
case XFS_ATTRI_OP_FLAGS_REPLACE:
args->total = xfs_attr_calc_size(args, &local);
@@ -740,6 +744,7 @@ xlog_recover_attri_commit_pass2(
/* Check the number of log iovecs makes sense for the op code. */
op = xfs_attr_log_item_op(attri_formatp);
switch (op) {
+ case XFS_ATTRI_OP_FLAGS_NVSET:
case XFS_ATTRI_OP_FLAGS_NVREMOVE:
/* Log item, attr name, optional attr value */
if (item->ri_total != 3 && item->ri_total != 2) {
@@ -824,6 +829,7 @@ xlog_recover_attri_commit_pass2(
return -EFSCORRUPTED;
}
fallthrough;
+ case XFS_ATTRI_OP_FLAGS_NVSET:
case XFS_ATTRI_OP_FLAGS_NVREMOVE:
case XFS_ATTRI_OP_FLAGS_SET:
case XFS_ATTRI_OP_FLAGS_REPLACE:
@@ -832,7 +838,7 @@ xlog_recover_attri_commit_pass2(
* and do not take a newname. Values are optional for set and
* replace.
*
- * Name-value remove operations must have a name, do not
+ * Name-value set/remove operations must have a name, do not
* take a newname, and can take a value.
*/
if (attr_name == NULL || name_len == 0) {
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 12/12] xfs: log NVLOOKUP xattr nvreplace operations
2023-04-06 19:13 ` [PATCHSET v11 00/12] xfs: name-value xattr lookups Darrick J. Wong
` (10 preceding siblings ...)
2023-04-06 19:19 ` [PATCH 11/12] xfs: log NVLOOKUP xattr setting operations Darrick J. Wong
@ 2023-04-06 19:19 ` Darrick J. Wong
11 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:19 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
(Formerly titled "xfs: Add new name to attri/d" and described as
follows:
This patch adds two new fields to the atti/d. They are nname and
nnamelen. This will be used for parent pointer updates since a
rename operation may cause the parent pointer to update both the
name and value. So we need to carry both the new name as well as
the target name in the attri/d.)
If high level code wants to do a deferred xattr nvreplace operation with
the NVLOOKUP flag set, we need to push this through the log. This log
item records the old name/value pair and the new name/value pair, and
completely replaces one with the other. Parent pointers will need this
ability to handle rename moving a child file between parents.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: reworked to handle new disk format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_attr.c | 20 ++++
fs/xfs/libxfs/xfs_attr.h | 4 -
fs/xfs/libxfs/xfs_da_btree.h | 6 +
fs/xfs/libxfs/xfs_log_format.h | 28 ++++-
fs/xfs/xfs_attr_item.c | 207 +++++++++++++++++++++++++++++++++++-----
fs/xfs/xfs_attr_item.h | 2
6 files changed, 233 insertions(+), 34 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 287990a9b29d..d03966796e97 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -425,6 +425,20 @@ xfs_attr_complete_op(
return XFS_DAS_DONE;
args->attr_filter &= ~XFS_ATTR_INCOMPLETE;
+ if (xfs_attr_intent_op(attr) != XFS_ATTRI_OP_FLAGS_NVREPLACE)
+ return replace_state;
+
+ /*
+ * NVREPLACE operations require the caller to set the old and new names
+ * and values explicitly.
+ */
+ ASSERT(args->new_namelen > 0);
+
+ args->name = args->new_name;
+ args->namelen = args->new_namelen;
+ args->hashval = xfs_da_hashname(args->name, args->namelen);
+ args->value = args->new_value;
+ args->valuelen = args->new_valuelen;
return replace_state;
}
@@ -926,9 +940,13 @@ xfs_attr_defer_replace(
struct xfs_da_args *args)
{
struct xfs_attr_intent *new;
+ int op_flag = XFS_ATTRI_OP_FLAGS_REPLACE;
int error = 0;
- error = xfs_attr_intent_init(args, XFS_ATTRI_OP_FLAGS_REPLACE, &new);
+ if (args->op_flags & XFS_DA_OP_NVLOOKUP)
+ op_flag = XFS_ATTRI_OP_FLAGS_NVREPLACE;
+
+ error = xfs_attr_intent_init(args, op_flag, &new);
if (error)
return error;
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index f0aa372ecba1..d543a6a01f08 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -510,8 +510,8 @@ struct xfs_attr_intent {
struct xfs_da_args *xattri_da_args;
/*
- * Shared buffer containing the attr name and value so that the logging
- * code can share large memory buffers between log items.
+ * Shared buffer containing the attr name, new name, and value so that
+ * the logging code can share large memory buffers between log items.
*/
struct xfs_attri_log_nameval *xattri_nameval;
diff --git a/fs/xfs/libxfs/xfs_da_btree.h b/fs/xfs/libxfs/xfs_da_btree.h
index 94a544fc8a9c..fc4dc3e87837 100644
--- a/fs/xfs/libxfs/xfs_da_btree.h
+++ b/fs/xfs/libxfs/xfs_da_btree.h
@@ -54,11 +54,15 @@ enum xfs_dacmp {
*/
typedef struct xfs_da_args {
struct xfs_da_geometry *geo; /* da block geometry */
- const uint8_t *name; /* string (maybe not NULL terminated) */
+ const uint8_t *name; /* string (maybe not NULL terminated) */
+ const uint8_t *new_name; /* new attr name */
int namelen; /* length of string (maybe no NULL) */
+ int new_namelen; /* new attr name len */
uint8_t filetype; /* filetype of inode for directories */
void *value; /* set of bytes (maybe contain NULLs) */
+ void *new_value; /* new xattr value (may contain NULLs) */
int valuelen; /* length of value */
+ int new_valuelen; /* length of new attr value */
unsigned int attr_filter; /* XFS_ATTR_{ROOT,SECURE,INCOMPLETE} */
unsigned int attr_flags; /* XATTR_{CREATE,REPLACE} */
xfs_dahash_t hashval; /* hash value of name */
diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h
index 7a4226e20dae..d666bfa5d08c 100644
--- a/fs/xfs/libxfs/xfs_log_format.h
+++ b/fs/xfs/libxfs/xfs_log_format.h
@@ -115,10 +115,11 @@ struct xfs_unmount_log_format {
#define XLOG_REG_TYPE_BUD_FORMAT 26
#define XLOG_REG_TYPE_ATTRI_FORMAT 27
#define XLOG_REG_TYPE_ATTRD_FORMAT 28
-#define XLOG_REG_TYPE_ATTR_NAME 29
+#define XLOG_REG_TYPE_ATTR_NAME 29
#define XLOG_REG_TYPE_ATTR_VALUE 30
-#define XLOG_REG_TYPE_MAX 30
-
+#define XLOG_REG_TYPE_ATTR_NEWNAME 31
+#define XLOG_REG_TYPE_ATTR_NEWVALUE 32
+#define XLOG_REG_TYPE_MAX 32
/*
* Flags to log operation header
@@ -959,6 +960,7 @@ struct xfs_icreate_log {
#define XFS_ATTRI_OP_FLAGS_REPLACE 3 /* Replace the attribute */
#define XFS_ATTRI_OP_FLAGS_NVREMOVE 4 /* Remove attr w/ vlookup */
#define XFS_ATTRI_OP_FLAGS_NVSET 5 /* Set attr with w/ vlookup */
+#define XFS_ATTRI_OP_FLAGS_NVREPLACE 6 /* Replace attr name and val */
#define XFS_ATTRI_OP_FLAGS_TYPE_MASK 0xFF /* Flags type mask */
/*
@@ -976,11 +978,27 @@ struct xfs_icreate_log {
struct xfs_attri_log_format {
uint16_t alfi_type; /* attri log item type */
uint16_t alfi_size; /* size of this item */
- uint32_t __pad; /* pad to 64 bit aligned */
+
+ /*
+ * For NVREPLACE, this is the length of the new xattr value.
+ * alfi_value_len contains the length of the old xattr value.
+ */
+ uint32_t alfi_new_value_len;
+
uint64_t alfi_id; /* attri identifier */
uint64_t alfi_ino; /* the inode for this attr operation */
uint32_t alfi_op_flags; /* marks the op as a set or remove */
- uint32_t alfi_name_len; /* attr name length */
+ union {
+ uint32_t alfi_name_len; /* attr name length */
+ struct {
+ /*
+ * For NVREPLACE, these are the lengths of the old and
+ * new attr name.
+ */
+ uint16_t alfi_old_name_len;
+ uint16_t alfi_new_name_len;
+ };
+ };
uint32_t alfi_value_len; /* attr value length */
uint32_t alfi_attr_filter;/* attr filter flags */
};
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
index f8def28090f8..cff8ff66cddc 100644
--- a/fs/xfs/xfs_attr_item.c
+++ b/fs/xfs/xfs_attr_item.c
@@ -75,8 +75,12 @@ static inline struct xfs_attri_log_nameval *
xfs_attri_log_nameval_alloc(
const void *name,
unsigned int name_len,
+ const void *new_name,
+ unsigned int new_name_len,
const void *value,
- unsigned int value_len)
+ unsigned int value_len,
+ const void *new_value,
+ unsigned int new_value_len)
{
struct xfs_attri_log_nameval *nv;
@@ -85,15 +89,26 @@ xfs_attri_log_nameval_alloc(
* this. But kvmalloc() utterly sucks, so we use our own version.
*/
nv = xlog_kvmalloc(sizeof(struct xfs_attri_log_nameval) +
- name_len + value_len);
+ name_len + new_name_len + value_len +
+ new_value_len);
nv->name.i_addr = nv + 1;
nv->name.i_len = name_len;
nv->name.i_type = XLOG_REG_TYPE_ATTR_NAME;
memcpy(nv->name.i_addr, name, name_len);
+ if (new_name_len) {
+ nv->new_name.i_addr = nv->name.i_addr + name_len;
+ nv->new_name.i_len = new_name_len;
+ memcpy(nv->new_name.i_addr, new_name, new_name_len);
+ } else {
+ nv->new_name.i_addr = NULL;
+ nv->new_name.i_len = 0;
+ }
+ nv->new_name.i_type = XLOG_REG_TYPE_ATTR_NEWNAME;
+
if (value_len) {
- nv->value.i_addr = nv->name.i_addr + name_len;
+ nv->value.i_addr = nv->name.i_addr + name_len + new_name_len;
nv->value.i_len = value_len;
memcpy(nv->value.i_addr, value, value_len);
} else {
@@ -102,6 +117,17 @@ xfs_attri_log_nameval_alloc(
}
nv->value.i_type = XLOG_REG_TYPE_ATTR_VALUE;
+ if (new_value_len) {
+ nv->new_value.i_addr = nv->name.i_addr + name_len +
+ new_name_len + value_len;
+ nv->new_value.i_len = new_value_len;
+ memcpy(nv->new_value.i_addr, new_value, new_value_len);
+ } else {
+ nv->new_value.i_addr = NULL;
+ nv->new_value.i_len = 0;
+ }
+ nv->new_value.i_type = XLOG_REG_TYPE_ATTR_NEWVALUE;
+
refcount_set(&nv->refcount, 1);
return nv;
}
@@ -147,11 +173,20 @@ xfs_attri_item_size(
*nbytes += sizeof(struct xfs_attri_log_format) +
xlog_calc_iovec_len(nv->name.i_len);
- if (!nv->value.i_len)
- return;
+ if (nv->new_name.i_len) {
+ *nvecs += 1;
+ *nbytes += xlog_calc_iovec_len(nv->new_name.i_len);
+ }
- *nvecs += 1;
- *nbytes += xlog_calc_iovec_len(nv->value.i_len);
+ if (nv->value.i_len) {
+ *nvecs += 1;
+ *nbytes += xlog_calc_iovec_len(nv->value.i_len);
+ }
+
+ if (nv->new_value.i_len) {
+ *nvecs += 1;
+ *nbytes += xlog_calc_iovec_len(nv->new_value.i_len);
+ }
}
/*
@@ -181,15 +216,28 @@ xfs_attri_item_format(
ASSERT(nv->name.i_len > 0);
attrip->attri_format.alfi_size++;
+ if (nv->new_name.i_len > 0)
+ attrip->attri_format.alfi_size++;
+
if (nv->value.i_len > 0)
attrip->attri_format.alfi_size++;
+ if (nv->new_value.i_len > 0)
+ attrip->attri_format.alfi_size++;
+
xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_ATTRI_FORMAT,
&attrip->attri_format,
sizeof(struct xfs_attri_log_format));
xlog_copy_from_iovec(lv, &vecp, &nv->name);
+
+ if (nv->new_name.i_len > 0)
+ xlog_copy_from_iovec(lv, &vecp, &nv->new_name);
+
if (nv->value.i_len > 0)
xlog_copy_from_iovec(lv, &vecp, &nv->value);
+
+ if (nv->new_value.i_len > 0)
+ xlog_copy_from_iovec(lv, &vecp, &nv->new_value);
}
/*
@@ -379,7 +427,15 @@ xfs_attr_log_item(
ASSERT(!(attr->xattri_op_flags & ~XFS_ATTRI_OP_FLAGS_TYPE_MASK));
attrp->alfi_op_flags = attr->xattri_op_flags;
attrp->alfi_value_len = attr->xattri_nameval->value.i_len;
- attrp->alfi_name_len = attr->xattri_nameval->name.i_len;
+
+ if (xfs_attr_log_item_op(attrp) == XFS_ATTRI_OP_FLAGS_NVREPLACE) {
+ attrp->alfi_old_name_len = attr->xattri_nameval->name.i_len;
+ attrp->alfi_new_name_len = attr->xattri_nameval->new_name.i_len;
+ attrp->alfi_new_value_len = attr->xattri_nameval->new_value.i_len;
+ } else {
+ attrp->alfi_name_len = attr->xattri_nameval->name.i_len;
+ }
+
ASSERT(!(attr->xattri_da_args->attr_filter & ~XFS_ATTRI_FILTER_MASK));
attrp->alfi_attr_filter = attr->xattri_da_args->attr_filter;
}
@@ -420,8 +476,11 @@ xfs_attr_create_intent(
* Transfer our reference to the name/value buffer to the
* deferred work state structure.
*/
- attr->xattri_nameval = xfs_attri_log_nameval_alloc(args->name,
- args->namelen, args->value, args->valuelen);
+ attr->xattri_nameval = xfs_attri_log_nameval_alloc(
+ args->name, args->namelen,
+ args->new_name, args->new_namelen,
+ args->value, args->valuelen,
+ args->new_value, args->new_valuelen);
}
attrip = xfs_attri_init(mp, attr->xattri_nameval);
@@ -508,9 +567,6 @@ xfs_attri_validate(
{
unsigned int op = xfs_attr_log_item_op(attrp);
- if (attrp->__pad != 0)
- return false;
-
if (attrp->alfi_op_flags & ~XFS_ATTRI_OP_FLAGS_TYPE_MASK)
return false;
@@ -519,23 +575,43 @@ xfs_attri_validate(
/* alfi_op_flags should be either a set or remove */
switch (op) {
+ case XFS_ATTRI_OP_FLAGS_REMOVE:
+ if (attrp->alfi_value_len != 0)
+ return false;
+ if (attrp->alfi_name_len == 0 ||
+ attrp->alfi_name_len > XATTR_NAME_MAX)
+ return false;
+ if (attrp->alfi_new_value_len != 0)
+ return false;
+ break;
case XFS_ATTRI_OP_FLAGS_SET:
case XFS_ATTRI_OP_FLAGS_REPLACE:
- case XFS_ATTRI_OP_FLAGS_REMOVE:
case XFS_ATTRI_OP_FLAGS_NVREMOVE:
case XFS_ATTRI_OP_FLAGS_NVSET:
+ if (attrp->alfi_name_len == 0 ||
+ attrp->alfi_name_len > XATTR_NAME_MAX)
+ return false;
+ if (attrp->alfi_value_len > XATTR_SIZE_MAX)
+ return false;
+ if (attrp->alfi_new_value_len != 0)
+ return false;
+ break;
+ case XFS_ATTRI_OP_FLAGS_NVREPLACE:
+ if (attrp->alfi_old_name_len == 0 ||
+ attrp->alfi_old_name_len > XATTR_NAME_MAX)
+ return false;
+ if (attrp->alfi_new_name_len == 0 ||
+ attrp->alfi_new_name_len > XATTR_NAME_MAX)
+ return false;
+ if (attrp->alfi_value_len > XATTR_SIZE_MAX)
+ return false;
+ if (attrp->alfi_new_value_len > XATTR_SIZE_MAX)
+ return false;
break;
default:
return false;
}
- if (attrp->alfi_value_len > XATTR_SIZE_MAX)
- return false;
-
- if ((attrp->alfi_name_len > XATTR_NAME_MAX) ||
- (attrp->alfi_name_len == 0))
- return false;
-
return xfs_verify_ino(mp, attrp->alfi_ino);
}
@@ -595,9 +671,13 @@ xfs_attri_item_recover(
args->whichfork = XFS_ATTR_FORK;
args->name = nv->name.i_addr;
args->namelen = nv->name.i_len;
+ args->new_name = nv->new_name.i_addr;
+ args->new_namelen = nv->new_name.i_len;
args->hashval = xfs_da_hashname(args->name, args->namelen);
args->value = nv->value.i_addr;
args->valuelen = nv->value.i_len;
+ args->new_value = nv->new_value.i_addr;
+ args->new_valuelen = nv->new_value.i_len;
args->attr_filter = attrp->alfi_attr_filter & XFS_ATTRI_FILTER_MASK;
args->op_flags = XFS_DA_OP_RECOVERY | XFS_DA_OP_OKNOENT |
XFS_DA_OP_LOGGED;
@@ -605,6 +685,7 @@ xfs_attri_item_recover(
ASSERT(xfs_sb_version_haslogxattrs(&mp->m_sb));
switch (xfs_attr_intent_op(attr)) {
+ case XFS_ATTRI_OP_FLAGS_NVREPLACE:
case XFS_ATTRI_OP_FLAGS_NVSET:
args->op_flags |= XFS_DA_OP_NVLOOKUP;
fallthrough;
@@ -699,7 +780,15 @@ xfs_attri_item_relog(
new_attrp->alfi_ino = old_attrp->alfi_ino;
new_attrp->alfi_op_flags = old_attrp->alfi_op_flags;
new_attrp->alfi_value_len = old_attrp->alfi_value_len;
- new_attrp->alfi_name_len = old_attrp->alfi_name_len;
+
+ if (xfs_attr_log_item_op(old_attrp) == XFS_ATTRI_OP_FLAGS_NVREPLACE) {
+ new_attrp->alfi_new_name_len = old_attrp->alfi_new_name_len;
+ new_attrp->alfi_old_name_len = old_attrp->alfi_old_name_len;
+ new_attrp->alfi_new_value_len = old_attrp->alfi_new_value_len;
+ } else {
+ new_attrp->alfi_name_len = old_attrp->alfi_name_len;
+ }
+
new_attrp->alfi_attr_filter = old_attrp->alfi_attr_filter;
xfs_trans_add_item(tp, &new_attrip->attri_item);
@@ -721,9 +810,13 @@ xlog_recover_attri_commit_pass2(
struct xfs_attri_log_nameval *nv;
const void *attr_name;
const void *attr_value = NULL;
+ const void *attr_new_name = NULL;
+ const void *attr_new_value = NULL;
size_t len;
unsigned int name_len = 0;
unsigned int value_len = 0;
+ unsigned int new_name_len = 0;
+ unsigned int new_value_len = 0;
unsigned int op, i = 0;
/* Validate xfs_attri_log_format before the large memory allocation */
@@ -775,6 +868,21 @@ xlog_recover_attri_commit_pass2(
}
name_len = attri_formatp->alfi_name_len;
break;
+ case XFS_ATTRI_OP_FLAGS_NVREPLACE:
+ /*
+ * Log item, attr name, new attr name, optional attr value,
+ * optional new attr value
+ */
+ if (item->ri_total < 3 || item->ri_total > 5) {
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ attri_formatp, len);
+ return -EFSCORRUPTED;
+ }
+ name_len = attri_formatp->alfi_old_name_len;
+ new_name_len = attri_formatp->alfi_new_name_len;
+ value_len = attri_formatp->alfi_value_len;
+ new_value_len = attri_formatp->alfi_new_value_len;
+ break;
default:
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
attri_formatp, len);
@@ -797,12 +905,30 @@ xlog_recover_attri_commit_pass2(
}
i++;
+ /* Validate the new attr name */
+ if (new_name_len > 0) {
+ if (item->ri_buf[i].i_len != xlog_calc_iovec_len(new_name_len)) {
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ item->ri_buf[i].i_addr,
+ item->ri_buf[i].i_len);
+ return -EFSCORRUPTED;
+ }
+
+ attr_new_name = item->ri_buf[i].i_addr;
+ if (!xfs_attr_namecheck(attr_new_name, new_name_len)) {
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ item->ri_buf[i].i_addr,
+ item->ri_buf[i].i_len);
+ return -EFSCORRUPTED;
+ }
+ i++;
+ }
+
/* Validate the attr value, if present */
if (value_len != 0) {
if (item->ri_buf[i].i_len != xlog_calc_iovec_len(value_len)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
- item->ri_buf[0].i_addr,
- item->ri_buf[0].i_len);
+ attri_formatp, len);
return -EFSCORRUPTED;
}
@@ -810,6 +936,18 @@ xlog_recover_attri_commit_pass2(
i++;
}
+ /* Validate the new attr value, if present */
+ if (new_value_len != 0) {
+ if (item->ri_buf[i].i_len != xlog_calc_iovec_len(new_value_len)) {
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ attri_formatp, len);
+ return -EFSCORRUPTED;
+ }
+
+ attr_new_value = item->ri_buf[i].i_addr;
+ i++;
+ }
+
/*
* Make sure we got the correct number of buffers for the operation
* that we just loaded.
@@ -847,6 +985,23 @@ xlog_recover_attri_commit_pass2(
return -EFSCORRUPTED;
}
break;
+ case XFS_ATTRI_OP_FLAGS_NVREPLACE:
+ /*
+ * Name-value replace operations require the caller to
+ * specify the old and new names and values explicitly.
+ * Values are optional.
+ */
+ if (attr_name == NULL || name_len == 0) {
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ attri_formatp, len);
+ return -EFSCORRUPTED;
+ }
+ if (attr_new_name == NULL || new_name_len == 0) {
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ attri_formatp, len);
+ return -EFSCORRUPTED;
+ }
+ break;
}
/*
@@ -855,7 +1010,9 @@ xlog_recover_attri_commit_pass2(
* reference.
*/
nv = xfs_attri_log_nameval_alloc(attr_name, name_len,
- attr_value, value_len);
+ attr_new_name, new_name_len,
+ attr_value, value_len,
+ attr_new_value, new_value_len);
attrip = xfs_attri_init(mp, nv);
memcpy(&attrip->attri_format, attri_formatp, len);
diff --git a/fs/xfs/xfs_attr_item.h b/fs/xfs/xfs_attr_item.h
index 3280a7930287..9ae0b3696847 100644
--- a/fs/xfs/xfs_attr_item.h
+++ b/fs/xfs/xfs_attr_item.h
@@ -13,7 +13,9 @@ struct kmem_zone;
struct xfs_attri_log_nameval {
struct xfs_log_iovec name;
+ struct xfs_log_iovec new_name; /* NVREPLACE only */
struct xfs_log_iovec value;
+ struct xfs_log_iovec new_value; /* NVREPLACE only */
refcount_t refcount;
/* name and value follow the end of this struct */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 1/3] xfs: Hold inode locks in xfs_ialloc
2023-04-06 19:14 ` [PATCHSET v11 0/3] xfs: hold ILOCK during deferred dir ops Darrick J. Wong
@ 2023-04-06 19:19 ` Darrick J. Wong
2023-04-06 19:20 ` [PATCH 2/3] xfs: Hold inode locks in xfs_trans_alloc_dir Darrick J. Wong
2023-04-06 19:20 ` [PATCH 3/3] xfs: Hold inode locks in xfs_rename Darrick J. Wong
2 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:19 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, Catherine Hoang, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Modify xfs_ialloc to hold locks after return. Caller will be
responsible for manual unlock. We will need this later to hold locks
across parent pointer operations
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Catherine Hoang <catherine.hoang@oracle.com>
---
fs/xfs/xfs_inode.c | 8 +++++++-
fs/xfs/xfs_qm.c | 4 +++-
fs/xfs/xfs_symlink.c | 2 ++
3 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 5808abab786c..07b9aae3150e 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -774,6 +774,8 @@ xfs_inode_inherit_flags2(
/*
* Initialise a newly allocated inode and return the in-core inode to the
* caller locked exclusively.
+ *
+ * Caller is responsible for unlocking the inode manually upon return
*/
int
xfs_init_new_inode(
@@ -899,7 +901,7 @@ xfs_init_new_inode(
/*
* Log the new values stuffed into the inode.
*/
- xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, ip, 0);
xfs_trans_log_inode(tp, ip, flags);
/* now that we have an i_mode we can setup the inode structure */
@@ -1076,6 +1078,7 @@ xfs_create(
xfs_qm_dqrele(pdqp);
*ipp = ip;
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
return 0;
out_trans_cancel:
@@ -1087,6 +1090,7 @@ xfs_create(
* transactions and deadlocks from xfs_inactive.
*/
if (ip) {
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
xfs_finish_inode_setup(ip);
xfs_irele(ip);
}
@@ -1172,6 +1176,7 @@ xfs_create_tmpfile(
xfs_qm_dqrele(pdqp);
*ipp = ip;
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
return 0;
out_trans_cancel:
@@ -1183,6 +1188,7 @@ xfs_create_tmpfile(
* transactions and deadlocks from xfs_inactive.
*/
if (ip) {
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
xfs_finish_inode_setup(ip);
xfs_irele(ip);
}
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index 6abcc34fafd8..0e19ad8719af 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -826,8 +826,10 @@ xfs_qm_qino_alloc(
ASSERT(xfs_is_shutdown(mp));
xfs_alert(mp, "%s failed (error %d)!", __func__, error);
}
- if (need_alloc)
+ if (need_alloc) {
xfs_finish_inode_setup(*ipp);
+ xfs_iunlock(*ipp, XFS_ILOCK_EXCL);
+ }
return error;
}
diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c
index 85e433df6a3f..315a709e53bd 100644
--- a/fs/xfs/xfs_symlink.c
+++ b/fs/xfs/xfs_symlink.c
@@ -337,6 +337,7 @@ xfs_symlink(
xfs_qm_dqrele(pdqp);
*ipp = ip;
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
return 0;
out_trans_cancel:
@@ -348,6 +349,7 @@ xfs_symlink(
* transactions and deadlocks from xfs_inactive.
*/
if (ip) {
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
xfs_finish_inode_setup(ip);
xfs_irele(ip);
}
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 2/3] xfs: Hold inode locks in xfs_trans_alloc_dir
2023-04-06 19:14 ` [PATCHSET v11 0/3] xfs: hold ILOCK during deferred dir ops Darrick J. Wong
2023-04-06 19:19 ` [PATCH 1/3] xfs: Hold inode locks in xfs_ialloc Darrick J. Wong
@ 2023-04-06 19:20 ` Darrick J. Wong
2023-04-06 19:20 ` [PATCH 3/3] xfs: Hold inode locks in xfs_rename Darrick J. Wong
2 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:20 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, Catherine Hoang, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Modify xfs_trans_alloc_dir to hold locks after return. Caller will be
responsible for manual unlock. We will need this later to hold locks
across parent pointer operations
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Catherine Hoang <catherine.hoang@oracle.com>
---
fs/xfs/xfs_inode.c | 14 ++++++++++++--
fs/xfs/xfs_trans.c | 9 +++++++--
2 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 07b9aae3150e..cb68e6661c96 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1279,10 +1279,15 @@ xfs_link(
if (xfs_has_wsync(mp) || xfs_has_dirsync(mp))
xfs_trans_set_sync(tp);
- return xfs_trans_commit(tp);
+ error = xfs_trans_commit(tp);
+ xfs_iunlock(tdp, XFS_ILOCK_EXCL);
+ xfs_iunlock(sip, XFS_ILOCK_EXCL);
+ return error;
error_return:
xfs_trans_cancel(tp);
+ xfs_iunlock(tdp, XFS_ILOCK_EXCL);
+ xfs_iunlock(sip, XFS_ILOCK_EXCL);
std_return:
if (error == -ENOSPC && nospace_error)
error = nospace_error;
@@ -2518,15 +2523,20 @@ xfs_remove(
error = xfs_trans_commit(tp);
if (error)
- goto std_return;
+ goto out_unlock;
if (is_dir && xfs_inode_is_filestream(ip))
xfs_filestream_deassociate(ip);
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
return 0;
out_trans_cancel:
xfs_trans_cancel(tp);
+ out_unlock:
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
std_return:
return error;
}
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
index 8afc0c080861..7e656dd42362 100644
--- a/fs/xfs/xfs_trans.c
+++ b/fs/xfs/xfs_trans.c
@@ -1356,6 +1356,8 @@ xfs_trans_alloc_ichange(
* The caller must ensure that the on-disk dquots attached to this inode have
* already been allocated and initialized. The ILOCKs will be dropped when the
* transaction is committed or cancelled.
+ *
+ * Caller is responsible for unlocking the inodes manually upon return
*/
int
xfs_trans_alloc_dir(
@@ -1386,8 +1388,8 @@ xfs_trans_alloc_dir(
xfs_lock_two_inodes(dp, XFS_ILOCK_EXCL, ip, XFS_ILOCK_EXCL);
- xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
- xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, dp, 0);
+ xfs_trans_ijoin(tp, ip, 0);
error = xfs_qm_dqattach_locked(dp, false);
if (error) {
@@ -1410,6 +1412,9 @@ xfs_trans_alloc_dir(
if (error == -EDQUOT || error == -ENOSPC) {
if (!retried) {
xfs_trans_cancel(tp);
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ if (dp != ip)
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
xfs_blockgc_free_quota(dp, 0);
retried = true;
goto retry;
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 3/3] xfs: Hold inode locks in xfs_rename
2023-04-06 19:14 ` [PATCHSET v11 0/3] xfs: hold ILOCK during deferred dir ops Darrick J. Wong
2023-04-06 19:19 ` [PATCH 1/3] xfs: Hold inode locks in xfs_ialloc Darrick J. Wong
2023-04-06 19:20 ` [PATCH 2/3] xfs: Hold inode locks in xfs_trans_alloc_dir Darrick J. Wong
@ 2023-04-06 19:20 ` Darrick J. Wong
2 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:20 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, Catherine Hoang, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Modify xfs_rename to hold all inode locks across a rename operation
We will need this later when we add parent pointers
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Catherine Hoang <catherine.hoang@oracle.com>
---
fs/xfs/xfs_inode.c | 43 ++++++++++++++++++++++++++++++-------------
1 file changed, 30 insertions(+), 13 deletions(-)
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index cb68e6661c96..a62e07ae9c80 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2541,6 +2541,21 @@ xfs_remove(
return error;
}
+static inline void
+xfs_iunlock_rename(
+ struct xfs_inode **i_tab,
+ int num_inodes)
+{
+ int i;
+
+ for (i = num_inodes - 1; i >= 0; i--) {
+ /* Skip duplicate inodes if src and target dps are the same */
+ if (!i_tab[i] || (i > 0 && i_tab[i] == i_tab[i - 1]))
+ continue;
+ xfs_iunlock(i_tab[i], XFS_ILOCK_EXCL);
+ }
+}
+
/*
* Enter all inodes for a rename transaction into a sorted array.
*/
@@ -2839,18 +2854,16 @@ xfs_rename(
xfs_lock_inodes(inodes, num_inodes, XFS_ILOCK_EXCL);
/*
- * Join all the inodes to the transaction. From this point on,
- * we can rely on either trans_commit or trans_cancel to unlock
- * them.
+ * Join all the inodes to the transaction.
*/
- xfs_trans_ijoin(tp, src_dp, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, src_dp, 0);
if (new_parent)
- xfs_trans_ijoin(tp, target_dp, XFS_ILOCK_EXCL);
- xfs_trans_ijoin(tp, src_ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, target_dp, 0);
+ xfs_trans_ijoin(tp, src_ip, 0);
if (target_ip)
- xfs_trans_ijoin(tp, target_ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, target_ip, 0);
if (wip)
- xfs_trans_ijoin(tp, wip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, wip, 0);
/*
* If we are using project inheritance, we only allow renames
@@ -2864,10 +2877,12 @@ xfs_rename(
}
/* RENAME_EXCHANGE is unique from here on. */
- if (flags & RENAME_EXCHANGE)
- return xfs_cross_rename(tp, src_dp, src_name, src_ip,
+ if (flags & RENAME_EXCHANGE) {
+ error = xfs_cross_rename(tp, src_dp, src_name, src_ip,
target_dp, target_name, target_ip,
spaceres);
+ goto out_unlock;
+ }
/*
* Try to reserve quota to handle an expansion of the target directory.
@@ -2881,6 +2896,7 @@ xfs_rename(
if (error == -EDQUOT || error == -ENOSPC) {
if (!retried) {
xfs_trans_cancel(tp);
+ xfs_iunlock_rename(inodes, num_inodes);
xfs_blockgc_free_quota(target_dp, 0);
retried = true;
goto retry;
@@ -3092,12 +3108,13 @@ xfs_rename(
xfs_trans_log_inode(tp, target_dp, XFS_ILOG_CORE);
error = xfs_finish_rename(tp);
- if (wip)
- xfs_irele(wip);
- return error;
+
+ goto out_unlock;
out_trans_cancel:
xfs_trans_cancel(tp);
+out_unlock:
+ xfs_iunlock_rename(inodes, num_inodes);
out_release_wip:
if (wip)
xfs_irele(wip);
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 01/23] xfs: Expose init_xattrs in xfs_create_tmpfile
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
@ 2023-04-06 19:20 ` Darrick J. Wong
2023-04-06 19:20 ` [PATCH 02/23] xfs: Increase XFS_DEFER_OPS_NR_INODES to 5 Darrick J. Wong
` (21 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:20 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Tmp files are used as part of rename operations and will need attr forks
initialized for parent pointers. Expose the init_xattrs parameter to
the calling function to initialize the fork.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/xfs_inode.c | 5 +++--
fs/xfs/xfs_inode.h | 2 +-
fs/xfs/xfs_iops.c | 2 +-
3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index a62e07ae9c80..3e08a056bafe 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1109,6 +1109,7 @@ xfs_create_tmpfile(
struct mnt_idmap *idmap,
struct xfs_inode *dp,
umode_t mode,
+ bool init_xattrs,
struct xfs_inode **ipp)
{
struct xfs_mount *mp = dp->i_mount;
@@ -1149,7 +1150,7 @@ xfs_create_tmpfile(
error = xfs_dialloc(&tp, dp->i_ino, mode, &ino);
if (!error)
error = xfs_init_new_inode(idmap, tp, dp, ino, mode,
- 0, 0, prid, false, &ip);
+ 0, 0, prid, init_xattrs, &ip);
if (error)
goto out_trans_cancel;
@@ -2750,7 +2751,7 @@ xfs_rename_alloc_whiteout(
int error;
error = xfs_create_tmpfile(idmap, dp, S_IFCHR | WHITEOUT_MODE,
- &tmpfile);
+ false, &tmpfile);
if (error)
return error;
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index 69d21e42c10a..112fb5767233 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -478,7 +478,7 @@ int xfs_create(struct mnt_idmap *idmap,
umode_t mode, dev_t rdev, bool need_xattr,
struct xfs_inode **ipp);
int xfs_create_tmpfile(struct mnt_idmap *idmap,
- struct xfs_inode *dp, umode_t mode,
+ struct xfs_inode *dp, umode_t mode, bool init_xattrs,
struct xfs_inode **ipp);
int xfs_remove(struct xfs_inode *dp, struct xfs_name *name,
struct xfs_inode *ip);
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index 24718adb3c16..afc3cff11cf9 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -200,7 +200,7 @@ xfs_generic_create(
xfs_create_need_xattr(dir, default_acl, acl),
&ip);
} else {
- error = xfs_create_tmpfile(idmap, XFS_I(dir), mode, &ip);
+ error = xfs_create_tmpfile(idmap, XFS_I(dir), mode, true, &ip);
}
if (unlikely(error))
goto out_free_acl;
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 02/23] xfs: Increase XFS_DEFER_OPS_NR_INODES to 5
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
2023-04-06 19:20 ` [PATCH 01/23] xfs: Expose init_xattrs in xfs_create_tmpfile Darrick J. Wong
@ 2023-04-06 19:20 ` Darrick J. Wong
2023-04-06 19:21 ` [PATCH 03/23] xfs: Increase XFS_QM_TRANS_MAXDQS " Darrick J. Wong
` (20 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:20 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, Catherine Hoang, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Renames that generate parent pointer updates can join up to 5
inodes locked in sorted order. So we need to increase the
number of defer ops inodes and relock them in the same way.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Catherine Hoang <catherine.hoang@oracle.com>
---
fs/xfs/libxfs/xfs_defer.c | 28 ++++++++++++++++++++++++++--
fs/xfs/libxfs/xfs_defer.h | 8 +++++++-
fs/xfs/xfs_inode.c | 2 +-
fs/xfs/xfs_inode.h | 1 +
4 files changed, 35 insertions(+), 4 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
index 5a321b783398..c0279b57e51d 100644
--- a/fs/xfs/libxfs/xfs_defer.c
+++ b/fs/xfs/libxfs/xfs_defer.c
@@ -820,13 +820,37 @@ xfs_defer_ops_continue(
struct xfs_trans *tp,
struct xfs_defer_resources *dres)
{
- unsigned int i;
+ unsigned int i, j;
+ struct xfs_inode *sips[XFS_DEFER_OPS_NR_INODES];
+ struct xfs_inode *temp;
ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
ASSERT(!(tp->t_flags & XFS_TRANS_DIRTY));
/* Lock the captured resources to the new transaction. */
- if (dfc->dfc_held.dr_inos == 2)
+ if (dfc->dfc_held.dr_inos > 2) {
+ /*
+ * Renames with parent pointer updates can lock up to 5 inodes,
+ * sorted by their inode number. So we need to make sure they
+ * are relocked in the same way.
+ */
+ memset(sips, 0, sizeof(sips));
+ for (i = 0; i < dfc->dfc_held.dr_inos; i++)
+ sips[i] = dfc->dfc_held.dr_ip[i];
+
+ /* Bubble sort of at most 5 inodes */
+ for (i = 0; i < dfc->dfc_held.dr_inos; i++) {
+ for (j = 1; j < dfc->dfc_held.dr_inos; j++) {
+ if (sips[j]->i_ino < sips[j-1]->i_ino) {
+ temp = sips[j];
+ sips[j] = sips[j-1];
+ sips[j-1] = temp;
+ }
+ }
+ }
+
+ xfs_lock_inodes(sips, dfc->dfc_held.dr_inos, XFS_ILOCK_EXCL);
+ } else if (dfc->dfc_held.dr_inos == 2)
xfs_lock_two_inodes(dfc->dfc_held.dr_ip[0], XFS_ILOCK_EXCL,
dfc->dfc_held.dr_ip[1], XFS_ILOCK_EXCL);
else if (dfc->dfc_held.dr_inos == 1)
diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h
index 114a3a4930a3..fdf6941f8f4d 100644
--- a/fs/xfs/libxfs/xfs_defer.h
+++ b/fs/xfs/libxfs/xfs_defer.h
@@ -70,7 +70,13 @@ extern const struct xfs_defer_op_type xfs_attr_defer_type;
/*
* Deferred operation item relogging limits.
*/
-#define XFS_DEFER_OPS_NR_INODES 2 /* join up to two inodes */
+
+/*
+ * Rename w/ parent pointers can require up to 5 inodes with deferred ops to
+ * be joined to the transaction: src_dp, target_dp, src_ip, target_ip, and wip.
+ * These inodes are locked in sorted order by their inode numbers
+ */
+#define XFS_DEFER_OPS_NR_INODES 5
#define XFS_DEFER_OPS_NR_BUFS 2 /* join up to two buffers */
/* Resources that must be held across a transaction roll. */
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 3e08a056bafe..c8be00f20a6d 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -447,7 +447,7 @@ xfs_lock_inumorder(
* lock more than one at a time, lockdep will report false positives saying we
* have violated locking orders.
*/
-static void
+void
xfs_lock_inodes(
struct xfs_inode **ips,
int inodes,
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index 112fb5767233..ee09aefa6088 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -574,5 +574,6 @@ void xfs_end_io(struct work_struct *work);
int xfs_ilock2_io_mmap(struct xfs_inode *ip1, struct xfs_inode *ip2);
void xfs_iunlock2_io_mmap(struct xfs_inode *ip1, struct xfs_inode *ip2);
+void xfs_lock_inodes(struct xfs_inode **ips, int inodes, uint lock_mode);
#endif /* __XFS_INODE_H__ */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 03/23] xfs: Increase XFS_QM_TRANS_MAXDQS to 5
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
2023-04-06 19:20 ` [PATCH 01/23] xfs: Expose init_xattrs in xfs_create_tmpfile Darrick J. Wong
2023-04-06 19:20 ` [PATCH 02/23] xfs: Increase XFS_DEFER_OPS_NR_INODES to 5 Darrick J. Wong
@ 2023-04-06 19:21 ` Darrick J. Wong
2023-04-06 19:21 ` [PATCH 04/23] xfs: add parent pointer support to attribute code Darrick J. Wong
` (19 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:21 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
With parent pointers enabled, a rename operation can update up to 5
inodes: src_dp, target_dp, src_ip, target_ip and wip. This causes
their dquots to a be attached to the transaction chain, so we need
to increase XFS_QM_TRANS_MAXDQS. This patch also add a helper
function xfs_dqlockn to lock an arbitrary number of dquots.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/xfs_dquot.c | 38 ++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_dquot.h | 1 +
fs/xfs/xfs_qm.h | 2 +-
fs/xfs/xfs_trans_dquot.c | 15 ++++++++++-----
4 files changed, 50 insertions(+), 6 deletions(-)
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c
index 8fb90da89787..9f311729c4c8 100644
--- a/fs/xfs/xfs_dquot.c
+++ b/fs/xfs/xfs_dquot.c
@@ -1333,6 +1333,44 @@ xfs_dqlock2(
}
}
+static int
+xfs_dqtrx_cmp(
+ const void *a,
+ const void *b)
+{
+ const struct xfs_dqtrx *qa = a;
+ const struct xfs_dqtrx *qb = b;
+
+ if (qa->qt_dquot->q_id > qb->qt_dquot->q_id)
+ return 1;
+ if (qa->qt_dquot->q_id < qb->qt_dquot->q_id)
+ return -1;
+ return 0;
+}
+
+void
+xfs_dqlockn(
+ struct xfs_dqtrx *q)
+{
+ unsigned int i;
+
+ /* Sort in order of dquot id, do not allow duplicates */
+ for (i = 0; i < XFS_QM_TRANS_MAXDQS && q[i].qt_dquot != NULL; i++) {
+ unsigned int j;
+
+ for (j = 0; j < i; j++)
+ ASSERT(q[i].qt_dquot != q[j].qt_dquot);
+ }
+ if (i == 0)
+ return;
+
+ sort(q, i, sizeof(struct xfs_dqtrx), xfs_dqtrx_cmp, NULL);
+
+ mutex_lock(&q[0].qt_dquot->q_qlock);
+ for (i = 1; i < XFS_QM_TRANS_MAXDQS && q[i].qt_dquot != NULL; i++)
+ mutex_lock_nested(&q[i].qt_dquot->q_qlock, XFS_QLOCK_NESTED);
+}
+
int __init
xfs_qm_init(void)
{
diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h
index 80c8f851a2f3..dc7d0226242b 100644
--- a/fs/xfs/xfs_dquot.h
+++ b/fs/xfs/xfs_dquot.h
@@ -223,6 +223,7 @@ int xfs_qm_dqget_uncached(struct xfs_mount *mp,
void xfs_qm_dqput(struct xfs_dquot *dqp);
void xfs_dqlock2(struct xfs_dquot *, struct xfs_dquot *);
+void xfs_dqlockn(struct xfs_dqtrx *q);
void xfs_dquot_set_prealloc_limits(struct xfs_dquot *);
diff --git a/fs/xfs/xfs_qm.h b/fs/xfs/xfs_qm.h
index 9683f0457d19..c6ec88779356 100644
--- a/fs/xfs/xfs_qm.h
+++ b/fs/xfs/xfs_qm.h
@@ -120,7 +120,7 @@ enum {
XFS_QM_TRANS_PRJ,
XFS_QM_TRANS_DQTYPES
};
-#define XFS_QM_TRANS_MAXDQS 2
+#define XFS_QM_TRANS_MAXDQS 5
struct xfs_dquot_acct {
struct xfs_dqtrx dqs[XFS_QM_TRANS_DQTYPES][XFS_QM_TRANS_MAXDQS];
};
diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
index aa00cf67ad72..8a48175ea3a7 100644
--- a/fs/xfs/xfs_trans_dquot.c
+++ b/fs/xfs/xfs_trans_dquot.c
@@ -268,24 +268,29 @@ xfs_trans_mod_dquot(
/*
* Given an array of dqtrx structures, lock all the dquots associated and join
- * them to the transaction, provided they have been modified. We know that the
- * highest number of dquots of one type - usr, grp and prj - involved in a
- * transaction is 3 so we don't need to make this very generic.
+ * them to the transaction, provided they have been modified.
*/
STATIC void
xfs_trans_dqlockedjoin(
struct xfs_trans *tp,
struct xfs_dqtrx *q)
{
+ unsigned int i;
ASSERT(q[0].qt_dquot != NULL);
if (q[1].qt_dquot == NULL) {
xfs_dqlock(q[0].qt_dquot);
xfs_trans_dqjoin(tp, q[0].qt_dquot);
- } else {
- ASSERT(XFS_QM_TRANS_MAXDQS == 2);
+ } else if (q[2].qt_dquot == NULL) {
xfs_dqlock2(q[0].qt_dquot, q[1].qt_dquot);
xfs_trans_dqjoin(tp, q[0].qt_dquot);
xfs_trans_dqjoin(tp, q[1].qt_dquot);
+ } else {
+ xfs_dqlockn(q);
+ for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
+ if (q[i].qt_dquot == NULL)
+ break;
+ xfs_trans_dqjoin(tp, q[i].qt_dquot);
+ }
}
}
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 04/23] xfs: add parent pointer support to attribute code
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (2 preceding siblings ...)
2023-04-06 19:21 ` [PATCH 03/23] xfs: Increase XFS_QM_TRANS_MAXDQS " Darrick J. Wong
@ 2023-04-06 19:21 ` Darrick J. Wong
2023-04-06 19:21 ` [PATCH 05/23] xfs: define parent pointer ondisk extended attribute format Darrick J. Wong
` (18 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:21 UTC (permalink / raw)
To: djwong
Cc: Mark Tinguely, Dave Chinner, Allison Henderson, allison.henderson,
linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Add the new parent attribute type. XFS_ATTR_PARENT is used only for parent pointer
entries; it uses reserved blocks like XFS_ATTR_ROOT.
Signed-off-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_attr.c | 3 ++-
fs/xfs/libxfs/xfs_da_format.h | 5 ++++-
fs/xfs/libxfs/xfs_log_format.h | 1 +
fs/xfs/scrub/attr.c | 2 +-
fs/xfs/xfs_trace.h | 3 ++-
5 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index d03966796e97..55188551c674 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -992,7 +992,8 @@ xfs_attr_set(
struct xfs_inode *dp = args->dp;
struct xfs_mount *mp = dp->i_mount;
struct xfs_trans_res tres;
- bool rsvd = (args->attr_filter & XFS_ATTR_ROOT);
+ bool rsvd = (args->attr_filter & (XFS_ATTR_ROOT |
+ XFS_ATTR_PARENT));
bool is_remove = args->op_flags & XFS_DA_OP_REMOVE;
int error, local;
int rmt_blks = 0;
diff --git a/fs/xfs/libxfs/xfs_da_format.h b/fs/xfs/libxfs/xfs_da_format.h
index 25e2841084e1..3dc03968bba6 100644
--- a/fs/xfs/libxfs/xfs_da_format.h
+++ b/fs/xfs/libxfs/xfs_da_format.h
@@ -688,12 +688,15 @@ struct xfs_attr3_leafblock {
#define XFS_ATTR_LOCAL_BIT 0 /* attr is stored locally */
#define XFS_ATTR_ROOT_BIT 1 /* limit access to trusted attrs */
#define XFS_ATTR_SECURE_BIT 2 /* limit access to secure attrs */
+#define XFS_ATTR_PARENT_BIT 3 /* parent pointer attrs */
#define XFS_ATTR_INCOMPLETE_BIT 7 /* attr in middle of create/delete */
#define XFS_ATTR_LOCAL (1u << XFS_ATTR_LOCAL_BIT)
#define XFS_ATTR_ROOT (1u << XFS_ATTR_ROOT_BIT)
#define XFS_ATTR_SECURE (1u << XFS_ATTR_SECURE_BIT)
+#define XFS_ATTR_PARENT (1u << XFS_ATTR_PARENT_BIT)
#define XFS_ATTR_INCOMPLETE (1u << XFS_ATTR_INCOMPLETE_BIT)
-#define XFS_ATTR_NSP_ONDISK_MASK (XFS_ATTR_ROOT | XFS_ATTR_SECURE)
+#define XFS_ATTR_NSP_ONDISK_MASK \
+ (XFS_ATTR_ROOT | XFS_ATTR_SECURE | XFS_ATTR_PARENT)
/*
* Alignment for namelist and valuelist entries (since they are mixed
diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h
index d666bfa5d08c..5728805a3b8c 100644
--- a/fs/xfs/libxfs/xfs_log_format.h
+++ b/fs/xfs/libxfs/xfs_log_format.h
@@ -969,6 +969,7 @@ struct xfs_icreate_log {
*/
#define XFS_ATTRI_FILTER_MASK (XFS_ATTR_ROOT | \
XFS_ATTR_SECURE | \
+ XFS_ATTR_PARENT | \
XFS_ATTR_INCOMPLETE)
/*
diff --git a/fs/xfs/scrub/attr.c b/fs/xfs/scrub/attr.c
index 31529b9bf389..9d2e33743ecd 100644
--- a/fs/xfs/scrub/attr.c
+++ b/fs/xfs/scrub/attr.c
@@ -441,7 +441,7 @@ xchk_xattr_rec(
/* Retrieve the entry and check it. */
hash = be32_to_cpu(ent->hashval);
badflags = ~(XFS_ATTR_LOCAL | XFS_ATTR_ROOT | XFS_ATTR_SECURE |
- XFS_ATTR_INCOMPLETE);
+ XFS_ATTR_INCOMPLETE | XFS_ATTR_PARENT);
if ((ent->flags & badflags) != 0)
xchk_da_set_corrupt(ds, level);
if (ent->flags & XFS_ATTR_LOCAL) {
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 9c0006c55fec..64df29414440 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -79,7 +79,8 @@ struct xfs_perag;
#define XFS_ATTR_FILTER_FLAGS \
{ XFS_ATTR_ROOT, "ROOT" }, \
{ XFS_ATTR_SECURE, "SECURE" }, \
- { XFS_ATTR_INCOMPLETE, "INCOMPLETE" }
+ { XFS_ATTR_INCOMPLETE, "INCOMPLETE" }, \
+ { XFS_ATTR_PARENT, "PARENT" }
DECLARE_EVENT_CLASS(xfs_attr_list_class,
TP_PROTO(struct xfs_attr_list_context *ctx),
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 05/23] xfs: define parent pointer ondisk extended attribute format
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (3 preceding siblings ...)
2023-04-06 19:21 ` [PATCH 04/23] xfs: add parent pointer support to attribute code Darrick J. Wong
@ 2023-04-06 19:21 ` Darrick J. Wong
2023-04-06 19:21 ` [PATCH 06/23] xfs: add parent pointer validator functions Darrick J. Wong
` (17 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:21 UTC (permalink / raw)
To: djwong; +Cc: Dave Chinner, Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
We need to define the parent pointer attribute format before we start
adding support for it into all the code that needs to use it. The EA
format we will use encodes the following information:
name={parent inode #, parent inode generation, dirent namehash}
value={dirent name}
The inode/gen gives all the information we need to reliably identify the
parent without requiring child->parent lock ordering, and allows
userspace to do pathname component level reconstruction without the
kernel ever needing to verify the parent itself as part of ioctl calls.
Storing the dirent name hash in the key reduces hash collisions if a
file is hardlinked multiple times in the same directory.
By using the NVLOOKUP mode in the extended attribute code to match
parent pointers using both the xattr name and value, we can identify the
exact parent pointer EA we need to modify/remove in rename/unlink
operations without searching the entire EA space.
By storing the dirent name, we have enough information to be able to
validate and reconstruct damaged directory trees. Earlier iterations of
this patchset encoded the directory offset in the parent pointer key,
but this format required repair to keep that in sync across directory
rebuilds, which is unnecessary complexity.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: replace diroffset with the namehash in the pptr key]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_da_format.h | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/fs/xfs/libxfs/xfs_da_format.h b/fs/xfs/libxfs/xfs_da_format.h
index 3dc03968bba6..40016eb0878e 100644
--- a/fs/xfs/libxfs/xfs_da_format.h
+++ b/fs/xfs/libxfs/xfs_da_format.h
@@ -805,4 +805,25 @@ static inline unsigned int xfs_dir2_dirblock_bytes(struct xfs_sb *sbp)
xfs_failaddr_t xfs_da3_blkinfo_verify(struct xfs_buf *bp,
struct xfs_da3_blkinfo *hdr3);
+/*
+ * Parent pointer attribute format definition
+ *
+ * The xattr name encodes the parent inode number, generation and the crc32c
+ * hash of the dirent name.
+ *
+ * The xattr value contains the dirent name.
+ */
+struct xfs_parent_name_rec {
+ __be64 p_ino;
+ __be32 p_gen;
+ __be32 p_namehash;
+};
+
+/*
+ * Maximum size of the dirent name that can be stored in a parent pointer.
+ * This matches the maximum dirent name length.
+ */
+#define XFS_PARENT_DIRENT_NAME_MAX_SIZE \
+ (MAXNAMELEN - 1)
+
#endif /* __XFS_DA_FORMAT_H__ */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 06/23] xfs: add parent pointer validator functions
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (4 preceding siblings ...)
2023-04-06 19:21 ` [PATCH 05/23] xfs: define parent pointer ondisk extended attribute format Darrick J. Wong
@ 2023-04-06 19:21 ` Darrick J. Wong
2023-04-06 19:22 ` [PATCH 07/23] xfs: extend transaction reservations for parent attributes Darrick J. Wong
` (16 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:21 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Attribute names of parent pointers are not strings. So we need to
modify attr_namecheck to verify parent pointer records when the
XFS_ATTR_PARENT flag is set. At the same time, we need to validate attr
values during log recovery if the xattr is really a parent pointer.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: move functions to xfs_parent.c, adjust for new disk format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/Makefile | 1
fs/xfs/libxfs/xfs_attr.c | 10 ++++
fs/xfs/libxfs/xfs_attr.h | 3 +
fs/xfs/libxfs/xfs_da_format.h | 8 ++++
fs/xfs/libxfs/xfs_parent.c | 92 +++++++++++++++++++++++++++++++++++++++++
fs/xfs/libxfs/xfs_parent.h | 16 +++++++
fs/xfs/scrub/attr.c | 2 -
fs/xfs/xfs_attr_item.c | 24 +++++++++--
fs/xfs/xfs_attr_list.c | 17 +++++---
9 files changed, 161 insertions(+), 12 deletions(-)
create mode 100644 fs/xfs/libxfs/xfs_parent.c
create mode 100644 fs/xfs/libxfs/xfs_parent.h
diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index 92d88dc3c9f7..afa9fd61924d 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -41,6 +41,7 @@ xfs-y += $(addprefix libxfs/, \
xfs_inode_buf.o \
xfs_log_rlimit.o \
xfs_ag_resv.o \
+ xfs_parent.o \
xfs_rmap.o \
xfs_rmap_btree.o \
xfs_refcount.o \
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 55188551c674..c9c6c5d7e144 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -26,6 +26,7 @@
#include "xfs_trace.h"
#include "xfs_attr_item.h"
#include "xfs_xattr.h"
+#include "xfs_parent.h"
struct kmem_cache *xfs_attr_intent_cache;
@@ -1596,9 +1597,14 @@ xfs_attr_node_get(
/* Returns true if the attribute entry name is valid. */
bool
xfs_attr_namecheck(
- const void *name,
- size_t length)
+ struct xfs_mount *mp,
+ const void *name,
+ size_t length,
+ unsigned int flags)
{
+ if (flags & XFS_ATTR_PARENT)
+ return xfs_parent_namecheck(mp, name, length, flags);
+
/*
* MAXNAMELEN includes the trailing null, but (name/length) leave it
* out, so use >= for the length check.
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index d543a6a01f08..13b65aaf3116 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -552,7 +552,8 @@ int xfs_attr_get(struct xfs_da_args *args);
int xfs_attr_set(struct xfs_da_args *args);
int xfs_attr_set_iter(struct xfs_attr_intent *attr);
int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
-bool xfs_attr_namecheck(const void *name, size_t length);
+bool xfs_attr_namecheck(struct xfs_mount *mp, const void *name, size_t length,
+ unsigned int flags);
int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
void xfs_init_attr_trans(struct xfs_da_args *args, struct xfs_trans_res *tres,
unsigned int *total);
diff --git a/fs/xfs/libxfs/xfs_da_format.h b/fs/xfs/libxfs/xfs_da_format.h
index 40016eb0878e..7426f905277e 100644
--- a/fs/xfs/libxfs/xfs_da_format.h
+++ b/fs/xfs/libxfs/xfs_da_format.h
@@ -731,6 +731,14 @@ xfs_attr3_leaf_name(xfs_attr_leafblock_t *leafp, int idx)
return &((char *)leafp)[be16_to_cpu(entries[idx].nameidx)];
}
+static inline int
+xfs_attr3_leaf_flags(xfs_attr_leafblock_t *leafp, int idx)
+{
+ struct xfs_attr_leaf_entry *entries = xfs_attr3_leaf_entryp(leafp);
+
+ return entries[idx].flags;
+}
+
static inline xfs_attr_leaf_name_remote_t *
xfs_attr3_leaf_name_remote(xfs_attr_leafblock_t *leafp, int idx)
{
diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c
new file mode 100644
index 000000000000..3d9f5051f87b
--- /dev/null
+++ b/fs/xfs/libxfs/xfs_parent.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2023 Oracle, Inc.
+ * All rights reserved.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_format.h"
+#include "xfs_da_format.h"
+#include "xfs_log_format.h"
+#include "xfs_shared.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_inode.h"
+#include "xfs_error.h"
+#include "xfs_trace.h"
+#include "xfs_trans.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr.h"
+#include "xfs_dir2.h"
+#include "xfs_dir2_priv.h"
+#include "xfs_attr_sf.h"
+#include "xfs_bmap.h"
+#include "xfs_defer.h"
+#include "xfs_log.h"
+#include "xfs_xattr.h"
+#include "xfs_parent.h"
+#include "xfs_trans_space.h"
+
+/*
+ * Parent pointer attribute handling.
+ *
+ * Because the attribute value is a filename component, it will never be longer
+ * than 255 bytes. This means the attribute will always be a local format
+ * attribute as it is xfs_attr_leaf_entsize_local_max() for v5 filesystems will
+ * always be larger than this (max is 75% of block size).
+ *
+ * Creating a new parent attribute will always create a new attribute - there
+ * should never, ever be an existing attribute in the tree for a new inode.
+ * ENOSPC behavior is problematic - creating the inode without the parent
+ * pointer is effectively a corruption, so we allow parent attribute creation
+ * to dip into the reserve block pool to avoid unexpected ENOSPC errors from
+ * occurring.
+ */
+
+/* Return true if parent pointer EA name is valid. */
+bool
+xfs_parent_namecheck(
+ struct xfs_mount *mp,
+ const struct xfs_parent_name_rec *rec,
+ size_t reclen,
+ unsigned int attr_flags)
+{
+ xfs_ino_t p_ino;
+
+ if (!(attr_flags & XFS_ATTR_PARENT))
+ return false;
+
+ if (reclen != sizeof(struct xfs_parent_name_rec))
+ return false;
+
+ /* Only one namespace bit allowed. */
+ if (hweight32(attr_flags & XFS_ATTR_NSP_ONDISK_MASK) > 1)
+ return false;
+
+ p_ino = be64_to_cpu(rec->p_ino);
+ if (!xfs_verify_ino(mp, p_ino))
+ return false;
+
+ return true;
+}
+
+/* Return true if parent pointer EA value is valid. */
+bool
+xfs_parent_valuecheck(
+ struct xfs_mount *mp,
+ const void *value,
+ size_t valuelen)
+{
+ if (valuelen == 0 || valuelen > XFS_PARENT_DIRENT_NAME_MAX_SIZE)
+ return false;
+
+ if (value == NULL)
+ return false;
+
+ /* Valid dirent name? */
+ if (!xfs_dir2_namecheck(value, valuelen))
+ return false;
+
+ return true;
+}
diff --git a/fs/xfs/libxfs/xfs_parent.h b/fs/xfs/libxfs/xfs_parent.h
new file mode 100644
index 000000000000..6e2a2528d2d8
--- /dev/null
+++ b/fs/xfs/libxfs/xfs_parent.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2023 Oracle, Inc.
+ * All Rights Reserved.
+ */
+#ifndef __XFS_PARENT_H__
+#define __XFS_PARENT_H__
+
+/* Metadata validators */
+bool xfs_parent_namecheck(struct xfs_mount *mp,
+ const struct xfs_parent_name_rec *rec, size_t reclen,
+ unsigned int attr_flags);
+bool xfs_parent_valuecheck(struct xfs_mount *mp, const void *value,
+ size_t valuelen);
+
+#endif /* __XFS_PARENT_H__ */
diff --git a/fs/xfs/scrub/attr.c b/fs/xfs/scrub/attr.c
index 9d2e33743ecd..2a79a13cb600 100644
--- a/fs/xfs/scrub/attr.c
+++ b/fs/xfs/scrub/attr.c
@@ -129,7 +129,7 @@ xchk_xattr_listent(
}
/* Does this name make sense? */
- if (!xfs_attr_namecheck(name, namelen)) {
+ if (!xfs_attr_namecheck(sx->sc->mp, name, namelen, flags)) {
xchk_fblock_set_corrupt(sx->sc, XFS_ATTR_FORK, args.blkno);
return;
}
diff --git a/fs/xfs/xfs_attr_item.c b/fs/xfs/xfs_attr_item.c
index cff8ff66cddc..86ed6bfa2da8 100644
--- a/fs/xfs/xfs_attr_item.c
+++ b/fs/xfs/xfs_attr_item.c
@@ -27,6 +27,7 @@
#include "xfs_error.h"
#include "xfs_log_priv.h"
#include "xfs_log_recover.h"
+#include "xfs_parent.h"
struct kmem_cache *xfs_attri_cache;
struct kmem_cache *xfs_attrd_cache;
@@ -644,7 +645,8 @@ xfs_attri_item_recover(
*/
attrp = &attrip->attri_format;
if (!xfs_attri_validate(mp, attrp) ||
- !xfs_attr_namecheck(nv->name.i_addr, nv->name.i_len))
+ !xfs_attr_namecheck(mp, nv->name.i_addr, nv->name.i_len,
+ attrp->alfi_attr_filter))
return -EFSCORRUPTED;
error = xlog_recover_iget(mp, attrp->alfi_ino, &ip);
@@ -898,7 +900,8 @@ xlog_recover_attri_commit_pass2(
}
attr_name = item->ri_buf[i].i_addr;
- if (!xfs_attr_namecheck(attr_name, name_len)) {
+ if (!xfs_attr_namecheck(mp, attr_name, name_len,
+ attri_formatp->alfi_attr_filter)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
attri_formatp, len);
return -EFSCORRUPTED;
@@ -915,7 +918,8 @@ xlog_recover_attri_commit_pass2(
}
attr_new_name = item->ri_buf[i].i_addr;
- if (!xfs_attr_namecheck(attr_new_name, new_name_len)) {
+ if (!xfs_attr_namecheck(mp, attr_new_name, new_name_len,
+ attri_formatp->alfi_attr_filter)) {
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
item->ri_buf[i].i_addr,
item->ri_buf[i].i_len);
@@ -933,6 +937,13 @@ xlog_recover_attri_commit_pass2(
}
attr_value = item->ri_buf[i].i_addr;
+ if ((attri_formatp->alfi_attr_filter & XFS_ATTR_PARENT) &&
+ !xfs_parent_valuecheck(mp, attr_value, value_len)) {
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ item->ri_buf[i].i_addr,
+ item->ri_buf[i].i_len);
+ return -EFSCORRUPTED;
+ }
i++;
}
@@ -945,6 +956,13 @@ xlog_recover_attri_commit_pass2(
}
attr_new_value = item->ri_buf[i].i_addr;
+ if ((attri_formatp->alfi_attr_filter & XFS_ATTR_PARENT) &&
+ !xfs_parent_valuecheck(mp, attr_new_value, new_value_len)) {
+ XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
+ item->ri_buf[i].i_addr,
+ item->ri_buf[i].i_len);
+ return -EFSCORRUPTED;
+ }
i++;
}
diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c
index 99bbbe1a0e44..a51f7f13a352 100644
--- a/fs/xfs/xfs_attr_list.c
+++ b/fs/xfs/xfs_attr_list.c
@@ -58,9 +58,13 @@ xfs_attr_shortform_list(
struct xfs_attr_sf_sort *sbuf, *sbp;
struct xfs_attr_shortform *sf;
struct xfs_attr_sf_entry *sfe;
+ struct xfs_mount *mp;
int sbsize, nsbuf, count, i;
int error = 0;
+ ASSERT(context != NULL);
+ ASSERT(dp != NULL);
+ mp = dp->i_mount;
sf = (struct xfs_attr_shortform *)dp->i_af.if_u1.if_data;
ASSERT(sf != NULL);
if (!sf->hdr.count)
@@ -82,8 +86,9 @@ xfs_attr_shortform_list(
(dp->i_af.if_bytes + sf->hdr.count * 16) < context->bufsize)) {
for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) {
if (XFS_IS_CORRUPT(context->dp->i_mount,
- !xfs_attr_namecheck(sfe->nameval,
- sfe->namelen)))
+ !xfs_attr_namecheck(mp, sfe->nameval,
+ sfe->namelen,
+ sfe->flags)))
return -EFSCORRUPTED;
context->put_listent(context,
sfe->flags,
@@ -174,8 +179,9 @@ xfs_attr_shortform_list(
cursor->offset = 0;
}
if (XFS_IS_CORRUPT(context->dp->i_mount,
- !xfs_attr_namecheck(sbp->name,
- sbp->namelen))) {
+ !xfs_attr_namecheck(mp, sbp->name,
+ sbp->namelen,
+ sbp->flags))) {
error = -EFSCORRUPTED;
goto out;
}
@@ -465,7 +471,8 @@ xfs_attr3_leaf_list_int(
}
if (XFS_IS_CORRUPT(context->dp->i_mount,
- !xfs_attr_namecheck(name, namelen)))
+ !xfs_attr_namecheck(mp, name, namelen,
+ entry->flags)))
return -EFSCORRUPTED;
context->put_listent(context, entry->flags,
name, namelen, valuelen);
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 07/23] xfs: extend transaction reservations for parent attributes
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (5 preceding siblings ...)
2023-04-06 19:21 ` [PATCH 06/23] xfs: add parent pointer validator functions Darrick J. Wong
@ 2023-04-06 19:22 ` Darrick J. Wong
2023-04-06 19:22 ` [PATCH 08/23] xfs: parent pointer attribute creation Darrick J. Wong
` (15 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:22 UTC (permalink / raw)
To: djwong; +Cc: Dave Chinner, Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
We need to add, remove or modify parent pointer attributes during
create/link/unlink/rename operations atomically with the dirents in the
parent directories being modified. This means they need to be modified
in the same transaction as the parent directories, and so we need to add
the required space for the attribute modifications to the transaction
reservations.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: fix indenting errors, adjust for new log format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_trans_resv.c | 326 ++++++++++++++++++++++++++++++++++------
1 file changed, 274 insertions(+), 52 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_trans_resv.c b/fs/xfs/libxfs/xfs_trans_resv.c
index 5b2f27cbdb80..803153e20937 100644
--- a/fs/xfs/libxfs/xfs_trans_resv.c
+++ b/fs/xfs/libxfs/xfs_trans_resv.c
@@ -19,6 +19,9 @@
#include "xfs_trans.h"
#include "xfs_qm.h"
#include "xfs_trans_space.h"
+#include "xfs_attr_item.h"
+#include "xfs_log.h"
+#include "xfs_da_format.h"
#define _ALLOC true
#define _FREE false
@@ -420,29 +423,110 @@ xfs_calc_itruncate_reservation_minlogsize(
return xfs_calc_itruncate_reservation(mp, true);
}
+static inline unsigned int xfs_calc_pptr_link_overhead(void)
+{
+ return sizeof(struct xfs_attri_log_format) +
+ xlog_calc_iovec_len(sizeof(struct xfs_parent_name_rec)) +
+ xlog_calc_iovec_len(XFS_PARENT_DIRENT_NAME_MAX_SIZE);
+}
+static inline unsigned int xfs_calc_pptr_unlink_overhead(void)
+{
+ return sizeof(struct xfs_attri_log_format) +
+ xlog_calc_iovec_len(sizeof(struct xfs_parent_name_rec)) +
+ xlog_calc_iovec_len(XFS_PARENT_DIRENT_NAME_MAX_SIZE);
+}
+static inline unsigned int xfs_calc_pptr_replace_overhead(void)
+{
+ return sizeof(struct xfs_attri_log_format) +
+ xlog_calc_iovec_len(sizeof(struct xfs_parent_name_rec)) +
+ xlog_calc_iovec_len(XFS_PARENT_DIRENT_NAME_MAX_SIZE) +
+ xlog_calc_iovec_len(sizeof(struct xfs_parent_name_rec)) +
+ xlog_calc_iovec_len(XFS_PARENT_DIRENT_NAME_MAX_SIZE);
+}
+
/*
* In renaming a files we can modify:
* the five inodes involved: 5 * inode size
* the two directory btrees: 2 * (max depth + v2) * dir block size
* the two directory bmap btrees: 2 * max depth * block size
* And the bmap_finish transaction can free dir and bmap blocks (two sets
- * of bmap blocks) giving:
+ * of bmap blocks) giving (t2):
* the agf for the ags in which the blocks live: 3 * sector size
* the agfl for the ags in which the blocks live: 3 * sector size
* the superblock for the free block count: sector size
* the allocation btrees: 3 exts * 2 trees * (2 * max depth - 1) * block size
+ * If parent pointers are enabled (t3), then each transaction in the chain
+ * must be capable of setting or removing the extended attribute
+ * containing the parent information. It must also be able to handle
+ * the three xattr intent items that track the progress of the parent
+ * pointer update.
*/
STATIC uint
xfs_calc_rename_reservation(
struct xfs_mount *mp)
{
- return XFS_DQUOT_LOGRES(mp) +
- max((xfs_calc_inode_res(mp, 5) +
- xfs_calc_buf_res(2 * XFS_DIROP_LOG_COUNT(mp),
- XFS_FSB_TO_B(mp, 1))),
- (xfs_calc_buf_res(7, mp->m_sb.sb_sectsize) +
- xfs_calc_buf_res(xfs_allocfree_block_count(mp, 3),
- XFS_FSB_TO_B(mp, 1))));
+ unsigned int overhead = XFS_DQUOT_LOGRES(mp);
+ struct xfs_trans_resv *resp = M_RES(mp);
+ unsigned int t1, t2, t3 = 0;
+
+ t1 = xfs_calc_inode_res(mp, 5) +
+ xfs_calc_buf_res(2 * XFS_DIROP_LOG_COUNT(mp),
+ XFS_FSB_TO_B(mp, 1));
+
+ t2 = xfs_calc_buf_res(7, mp->m_sb.sb_sectsize) +
+ xfs_calc_buf_res(xfs_allocfree_block_count(mp, 3),
+ XFS_FSB_TO_B(mp, 1));
+
+ if (xfs_has_parent(mp)) {
+ unsigned int rename_overhead, exchange_overhead;
+
+ t3 = max(resp->tr_attrsetm.tr_logres,
+ resp->tr_attrrm.tr_logres);
+
+ /*
+ * For a standard rename, the three xattr intent log items
+ * are (1) replacing the pptr for the source file; (2)
+ * removing the pptr on the dest file; and (3) adding a
+ * pptr for the whiteout file in the src dir.
+ *
+ * For an RENAME_EXCHANGE, there are two xattr intent
+ * items to replace the pptr for both src and dest
+ * files. Link counts don't change and there is no
+ * whiteout.
+ *
+ * In the worst case we can end up relogging all log
+ * intent items to allow the log tail to move ahead, so
+ * they become overhead added to each transaction in a
+ * processing chain.
+ */
+ rename_overhead = xfs_calc_pptr_replace_overhead() +
+ xfs_calc_pptr_unlink_overhead() +
+ xfs_calc_pptr_link_overhead();
+ exchange_overhead = 2 * xfs_calc_pptr_replace_overhead();
+
+ overhead += max(rename_overhead, exchange_overhead);
+ }
+
+ return overhead + max3(t1, t2, t3);
+}
+
+static inline unsigned int
+xfs_rename_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ /* One for the rename, one more for freeing blocks */
+ unsigned int ret = XFS_RENAME_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to remove or add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += max(resp->tr_attrsetm.tr_logcount,
+ resp->tr_attrrm.tr_logcount);
+
+ return ret;
}
/*
@@ -459,6 +543,23 @@ xfs_calc_iunlink_remove_reservation(
2 * M_IGEO(mp)->inode_cluster_size;
}
+static inline unsigned int
+xfs_link_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ unsigned int ret = XFS_LINK_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += resp->tr_attrsetm.tr_logcount;
+
+ return ret;
+}
+
/*
* For creating a link to an inode:
* the parent directory inode: inode size
@@ -475,14 +576,23 @@ STATIC uint
xfs_calc_link_reservation(
struct xfs_mount *mp)
{
- return XFS_DQUOT_LOGRES(mp) +
- xfs_calc_iunlink_remove_reservation(mp) +
- max((xfs_calc_inode_res(mp, 2) +
- xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),
- XFS_FSB_TO_B(mp, 1))),
- (xfs_calc_buf_res(3, mp->m_sb.sb_sectsize) +
- xfs_calc_buf_res(xfs_allocfree_block_count(mp, 1),
- XFS_FSB_TO_B(mp, 1))));
+ unsigned int overhead = XFS_DQUOT_LOGRES(mp);
+ struct xfs_trans_resv *resp = M_RES(mp);
+ unsigned int t1, t2, t3 = 0;
+
+ overhead += xfs_calc_iunlink_remove_reservation(mp);
+ t1 = xfs_calc_inode_res(mp, 2) +
+ xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1));
+ t2 = xfs_calc_buf_res(3, mp->m_sb.sb_sectsize) +
+ xfs_calc_buf_res(xfs_allocfree_block_count(mp, 1),
+ XFS_FSB_TO_B(mp, 1));
+
+ if (xfs_has_parent(mp)) {
+ t3 = resp->tr_attrsetm.tr_logres;
+ overhead += xfs_calc_pptr_link_overhead();
+ }
+
+ return overhead + max3(t1, t2, t3);
}
/*
@@ -497,6 +607,23 @@ xfs_calc_iunlink_add_reservation(xfs_mount_t *mp)
M_IGEO(mp)->inode_cluster_size;
}
+static inline unsigned int
+xfs_remove_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ unsigned int ret = XFS_REMOVE_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += resp->tr_attrrm.tr_logcount;
+
+ return ret;
+}
+
/*
* For removing a directory entry we can modify:
* the parent directory inode: inode size
@@ -513,14 +640,24 @@ STATIC uint
xfs_calc_remove_reservation(
struct xfs_mount *mp)
{
- return XFS_DQUOT_LOGRES(mp) +
- xfs_calc_iunlink_add_reservation(mp) +
- max((xfs_calc_inode_res(mp, 2) +
- xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),
- XFS_FSB_TO_B(mp, 1))),
- (xfs_calc_buf_res(4, mp->m_sb.sb_sectsize) +
- xfs_calc_buf_res(xfs_allocfree_block_count(mp, 2),
- XFS_FSB_TO_B(mp, 1))));
+ unsigned int overhead = XFS_DQUOT_LOGRES(mp);
+ struct xfs_trans_resv *resp = M_RES(mp);
+ unsigned int t1, t2, t3 = 0;
+
+ overhead += xfs_calc_iunlink_add_reservation(mp);
+
+ t1 = xfs_calc_inode_res(mp, 2) +
+ xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1));
+ t2 = xfs_calc_buf_res(4, mp->m_sb.sb_sectsize) +
+ xfs_calc_buf_res(xfs_allocfree_block_count(mp, 2),
+ XFS_FSB_TO_B(mp, 1));
+
+ if (xfs_has_parent(mp)) {
+ t3 = resp->tr_attrrm.tr_logres;
+ overhead += xfs_calc_pptr_unlink_overhead();
+ }
+
+ return overhead + max3(t1, t2, t3);
}
/*
@@ -569,12 +706,40 @@ xfs_calc_icreate_resv_alloc(
xfs_calc_finobt_res(mp);
}
+static inline unsigned int
+xfs_icreate_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ unsigned int ret = XFS_CREATE_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += resp->tr_attrsetm.tr_logcount;
+
+ return ret;
+}
+
STATIC uint
-xfs_calc_icreate_reservation(xfs_mount_t *mp)
+xfs_calc_icreate_reservation(
+ struct xfs_mount *mp)
{
- return XFS_DQUOT_LOGRES(mp) +
- max(xfs_calc_icreate_resv_alloc(mp),
- xfs_calc_create_resv_modify(mp));
+ struct xfs_trans_resv *resp = M_RES(mp);
+ unsigned int overhead = XFS_DQUOT_LOGRES(mp);
+ unsigned int t1, t2, t3 = 0;
+
+ t1 = xfs_calc_icreate_resv_alloc(mp);
+ t2 = xfs_calc_create_resv_modify(mp);
+
+ if (xfs_has_parent(mp)) {
+ t3 = resp->tr_attrsetm.tr_logres;
+ overhead += xfs_calc_pptr_link_overhead();
+ }
+
+ return overhead + max3(t1, t2, t3);
}
STATIC uint
@@ -587,6 +752,23 @@ xfs_calc_create_tmpfile_reservation(
return res + xfs_calc_iunlink_add_reservation(mp);
}
+static inline unsigned int
+xfs_mkdir_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ unsigned int ret = XFS_MKDIR_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += resp->tr_attrsetm.tr_logcount;
+
+ return ret;
+}
+
/*
* Making a new directory is the same as creating a new file.
*/
@@ -597,6 +779,22 @@ xfs_calc_mkdir_reservation(
return xfs_calc_icreate_reservation(mp);
}
+static inline unsigned int
+xfs_symlink_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ unsigned int ret = XFS_SYMLINK_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += resp->tr_attrsetm.tr_logcount;
+
+ return ret;
+}
/*
* Making a new symplink is the same as creating a new file, but
@@ -909,6 +1107,52 @@ xfs_calc_sb_reservation(
return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
}
+/*
+ * Namespace reservations.
+ *
+ * These get tricky when parent pointers are enabled as we have attribute
+ * modifications occurring from within these transactions. Rather than confuse
+ * each of these reservation calculations with the conditional attribute
+ * reservations, add them here in a clear and concise manner. This requires that
+ * the attribute reservations have already been calculated.
+ *
+ * Note that we only include the static attribute reservation here; the runtime
+ * reservation will have to be modified by the size of the attributes being
+ * added/removed/modified. See the comments on the attribute reservation
+ * calculations for more details.
+ */
+STATIC void
+xfs_calc_namespace_reservations(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ ASSERT(resp->tr_attrsetm.tr_logres > 0);
+
+ resp->tr_rename.tr_logres = xfs_calc_rename_reservation(mp);
+ resp->tr_rename.tr_logcount = xfs_rename_log_count(mp, resp);
+ resp->tr_rename.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+ resp->tr_link.tr_logres = xfs_calc_link_reservation(mp);
+ resp->tr_link.tr_logcount = xfs_link_log_count(mp, resp);
+ resp->tr_link.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+ resp->tr_remove.tr_logres = xfs_calc_remove_reservation(mp);
+ resp->tr_remove.tr_logcount = xfs_remove_log_count(mp, resp);
+ resp->tr_remove.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+ resp->tr_symlink.tr_logres = xfs_calc_symlink_reservation(mp);
+ resp->tr_symlink.tr_logcount = xfs_symlink_log_count(mp, resp);
+ resp->tr_symlink.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+ resp->tr_create.tr_logres = xfs_calc_icreate_reservation(mp);
+ resp->tr_create.tr_logcount = xfs_icreate_log_count(mp, resp);
+ resp->tr_create.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+ resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);
+ resp->tr_mkdir.tr_logcount = xfs_mkdir_log_count(mp, resp);
+ resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+}
+
void
xfs_trans_resv_calc(
struct xfs_mount *mp,
@@ -928,35 +1172,11 @@ xfs_trans_resv_calc(
resp->tr_itruncate.tr_logcount = XFS_ITRUNCATE_LOG_COUNT;
resp->tr_itruncate.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
- resp->tr_rename.tr_logres = xfs_calc_rename_reservation(mp);
- resp->tr_rename.tr_logcount = XFS_RENAME_LOG_COUNT;
- resp->tr_rename.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
- resp->tr_link.tr_logres = xfs_calc_link_reservation(mp);
- resp->tr_link.tr_logcount = XFS_LINK_LOG_COUNT;
- resp->tr_link.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
- resp->tr_remove.tr_logres = xfs_calc_remove_reservation(mp);
- resp->tr_remove.tr_logcount = XFS_REMOVE_LOG_COUNT;
- resp->tr_remove.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
- resp->tr_symlink.tr_logres = xfs_calc_symlink_reservation(mp);
- resp->tr_symlink.tr_logcount = XFS_SYMLINK_LOG_COUNT;
- resp->tr_symlink.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
- resp->tr_create.tr_logres = xfs_calc_icreate_reservation(mp);
- resp->tr_create.tr_logcount = XFS_CREATE_LOG_COUNT;
- resp->tr_create.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
resp->tr_create_tmpfile.tr_logres =
xfs_calc_create_tmpfile_reservation(mp);
resp->tr_create_tmpfile.tr_logcount = XFS_CREATE_TMPFILE_LOG_COUNT;
resp->tr_create_tmpfile.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
- resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);
- resp->tr_mkdir.tr_logcount = XFS_MKDIR_LOG_COUNT;
- resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
resp->tr_ifree.tr_logres = xfs_calc_ifree_reservation(mp);
resp->tr_ifree.tr_logcount = XFS_INACTIVE_LOG_COUNT;
resp->tr_ifree.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
@@ -986,6 +1206,8 @@ xfs_trans_resv_calc(
resp->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT;
resp->tr_qm_dqalloc.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+ xfs_calc_namespace_reservations(mp, resp);
+
/*
* The following transactions are logged in logical format with
* a default log count.
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 08/23] xfs: parent pointer attribute creation
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (6 preceding siblings ...)
2023-04-06 19:22 ` [PATCH 07/23] xfs: extend transaction reservations for parent attributes Darrick J. Wong
@ 2023-04-06 19:22 ` Darrick J. Wong
2023-04-06 19:22 ` [PATCH 09/23] xfs: add parent attributes to link Darrick J. Wong
` (14 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:22 UTC (permalink / raw)
To: djwong; +Cc: Dave Chinner, Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Add parent pointer attribute during xfs_create, and subroutines to
initialize attributes
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: shorten names, adjust to new format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_attr.c | 4 +-
fs/xfs/libxfs/xfs_attr.h | 4 +-
fs/xfs/libxfs/xfs_parent.c | 115 ++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/libxfs/xfs_parent.h | 44 ++++++++++++++++-
fs/xfs/xfs_inode.c | 57 +++++++++++++++++++---
fs/xfs/xfs_inode.h | 6 ++
fs/xfs/xfs_super.c | 10 ++++
fs/xfs/xfs_xattr.c | 4 +-
fs/xfs/xfs_xattr.h | 2 +
9 files changed, 233 insertions(+), 13 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index c9c6c5d7e144..e2acd7ecdfcc 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -895,7 +895,7 @@ xfs_attr_lookup(
return error;
}
-static int
+int
xfs_attr_intent_init(
struct xfs_da_args *args,
unsigned int op_flags, /* op flag (set or remove) */
@@ -913,7 +913,7 @@ xfs_attr_intent_init(
}
/* Sets an attribute for an inode as a deferred operation */
-static int
+int
xfs_attr_defer_add(
struct xfs_da_args *args)
{
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 13b65aaf3116..1002e431b728 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -549,6 +549,7 @@ int xfs_inode_hasattr(struct xfs_inode *ip);
bool xfs_attr_is_leaf(struct xfs_inode *ip);
int xfs_attr_get_ilocked(struct xfs_da_args *args);
int xfs_attr_get(struct xfs_da_args *args);
+int xfs_attr_defer_add(struct xfs_da_args *args);
int xfs_attr_set(struct xfs_da_args *args);
int xfs_attr_set_iter(struct xfs_attr_intent *attr);
int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
@@ -557,7 +558,8 @@ bool xfs_attr_namecheck(struct xfs_mount *mp, const void *name, size_t length,
int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
void xfs_init_attr_trans(struct xfs_da_args *args, struct xfs_trans_res *tres,
unsigned int *total);
-
+int xfs_attr_intent_init(struct xfs_da_args *args, unsigned int op_flags,
+ struct xfs_attr_intent **attr);
/*
* Check to see if the attr should be upgraded from non-existent or shortform to
* single-leaf-block attribute list.
diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c
index 3d9f5051f87b..f57e1a201f17 100644
--- a/fs/xfs/libxfs/xfs_parent.c
+++ b/fs/xfs/libxfs/xfs_parent.c
@@ -28,6 +28,8 @@
#include "xfs_parent.h"
#include "xfs_trans_space.h"
+struct kmem_cache *xfs_parent_intent_cache;
+
/*
* Parent pointer attribute handling.
*
@@ -90,3 +92,116 @@ xfs_parent_valuecheck(
return true;
}
+
+/* Initializes a xfs_parent_name_rec to be stored as an attribute name. */
+static inline void
+xfs_init_parent_name_rec(
+ struct xfs_parent_name_rec *rec,
+ const struct xfs_inode *dp,
+ const struct xfs_name *name,
+ struct xfs_inode *ip)
+{
+ rec->p_ino = cpu_to_be64(dp->i_ino);
+ rec->p_gen = cpu_to_be32(VFS_IC(dp)->i_generation);
+ rec->p_namehash = cpu_to_be32(xfs_dir2_hashname(dp->i_mount, name));
+}
+
+/* Point the da args value fields at the non-key parts of a parent pointer. */
+static inline void
+xfs_init_parent_davalue(
+ struct xfs_da_args *args,
+ const struct xfs_name *name)
+{
+ args->valuelen = name->len;
+ args->value = (void *)name->name;
+}
+
+/*
+ * Allocate memory to control a logged parent pointer update as part of a
+ * dirent operation.
+ */
+int
+__xfs_parent_init(
+ struct xfs_mount *mp,
+ struct xfs_parent_defer **parentp)
+{
+ struct xfs_parent_defer *parent;
+ int error;
+
+ error = xfs_attr_grab_log_assist(mp);
+ if (error)
+ return error;
+
+ parent = kmem_cache_zalloc(xfs_parent_intent_cache, GFP_KERNEL);
+ if (!parent) {
+ xfs_attr_rele_log_assist(mp);
+ return -ENOMEM;
+ }
+
+ /* init parent da_args */
+ parent->args.geo = mp->m_attr_geo;
+ parent->args.whichfork = XFS_ATTR_FORK;
+ parent->args.attr_filter = XFS_ATTR_PARENT;
+ parent->args.op_flags = XFS_DA_OP_OKNOENT | XFS_DA_OP_LOGGED |
+ XFS_DA_OP_NVLOOKUP;
+ parent->args.name = (const uint8_t *)&parent->rec;
+ parent->args.namelen = sizeof(struct xfs_parent_name_rec);
+
+ *parentp = parent;
+ return 0;
+}
+
+static inline xfs_dahash_t
+xfs_parent_hashname(
+ struct xfs_inode *ip,
+ const struct xfs_parent_defer *parent)
+{
+ return xfs_da_hashname((const void *)&parent->rec,
+ sizeof(struct xfs_parent_name_rec));
+}
+
+/* Add a parent pointer to reflect a dirent addition. */
+int
+xfs_parent_add(
+ struct xfs_trans *tp,
+ struct xfs_parent_defer *parent,
+ struct xfs_inode *dp,
+ const struct xfs_name *parent_name,
+ struct xfs_inode *child)
+{
+ struct xfs_da_args *args = &parent->args;
+
+ xfs_init_parent_name_rec(&parent->rec, dp, parent_name, child);
+ args->hashval = xfs_parent_hashname(dp, parent);
+
+ args->trans = tp;
+ args->dp = child;
+
+ xfs_init_parent_davalue(&parent->args, parent_name);
+
+ return xfs_attr_defer_add(args);
+}
+
+/* Cancel a parent pointer operation. */
+void
+__xfs_parent_cancel(
+ struct xfs_mount *mp,
+ struct xfs_parent_defer *parent)
+{
+ xlog_drop_incompat_feat(mp->m_log);
+ kmem_cache_free(xfs_parent_intent_cache, parent);
+}
+
+/* Calculate the disk space required to add a parent pointer. */
+unsigned int
+xfs_parent_calc_space_res(
+ struct xfs_mount *mp,
+ unsigned int namelen)
+{
+ /*
+ * Parent pointers are always the first attr in an attr tree, and never
+ * larger than a block
+ */
+ return XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK) +
+ XFS_NEXTENTADD_SPACE_RES(mp, namelen, XFS_ATTR_FORK);
+}
diff --git a/fs/xfs/libxfs/xfs_parent.h b/fs/xfs/libxfs/xfs_parent.h
index 6e2a2528d2d8..8e7dbe22e1f7 100644
--- a/fs/xfs/libxfs/xfs_parent.h
+++ b/fs/xfs/libxfs/xfs_parent.h
@@ -13,4 +13,46 @@ bool xfs_parent_namecheck(struct xfs_mount *mp,
bool xfs_parent_valuecheck(struct xfs_mount *mp, const void *value,
size_t valuelen);
-#endif /* __XFS_PARENT_H__ */
+extern struct kmem_cache *xfs_parent_intent_cache;
+
+/*
+ * Dynamically allocd structure used to wrap the needed data to pass around
+ * the defer ops machinery
+ */
+struct xfs_parent_defer {
+ struct xfs_parent_name_rec rec;
+ struct xfs_da_args args;
+};
+
+int __xfs_parent_init(struct xfs_mount *mp, struct xfs_parent_defer **parentp);
+
+static inline int
+xfs_parent_start(
+ struct xfs_mount *mp,
+ struct xfs_parent_defer **pp)
+{
+ *pp = NULL;
+
+ if (xfs_has_parent(mp))
+ return __xfs_parent_init(mp, pp);
+ return 0;
+}
+
+int xfs_parent_add(struct xfs_trans *tp, struct xfs_parent_defer *parent,
+ struct xfs_inode *dp, const struct xfs_name *parent_name,
+ struct xfs_inode *child);
+void __xfs_parent_cancel(struct xfs_mount *mp, struct xfs_parent_defer *parent);
+
+static inline void
+xfs_parent_finish(
+ struct xfs_mount *mp,
+ struct xfs_parent_defer *p)
+{
+ if (p)
+ __xfs_parent_cancel(mp, p);
+}
+
+unsigned int xfs_parent_calc_space_res(struct xfs_mount *mp,
+ unsigned int namelen);
+
+#endif /* __XFS_PARENT_H__ */
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index c8be00f20a6d..e079fa95ad2e 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -37,6 +37,8 @@
#include "xfs_reflink.h"
#include "xfs_ag.h"
#include "xfs_log_priv.h"
+#include "xfs_parent.h"
+#include "xfs_xattr.h"
struct kmem_cache *xfs_inode_cache;
@@ -946,10 +948,32 @@ xfs_bumplink(
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
}
+static unsigned int
+xfs_create_space_res(
+ struct xfs_mount *mp,
+ unsigned int namelen)
+{
+ unsigned int ret;
+
+ ret = XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp, namelen);
+ if (xfs_has_parent(mp))
+ ret += xfs_parent_calc_space_res(mp, namelen);
+
+ return ret;
+}
+
+static unsigned int
+xfs_mkdir_space_res(
+ struct xfs_mount *mp,
+ unsigned int namelen)
+{
+ return xfs_create_space_res(mp, namelen);
+}
+
int
xfs_create(
struct mnt_idmap *idmap,
- xfs_inode_t *dp,
+ struct xfs_inode *dp,
struct xfs_name *name,
umode_t mode,
dev_t rdev,
@@ -961,7 +985,7 @@ xfs_create(
struct xfs_inode *ip = NULL;
struct xfs_trans *tp = NULL;
int error;
- bool unlock_dp_on_error = false;
+ bool unlock_dp_on_error = false;
prid_t prid;
struct xfs_dquot *udqp = NULL;
struct xfs_dquot *gdqp = NULL;
@@ -969,6 +993,7 @@ xfs_create(
struct xfs_trans_res *tres;
uint resblks;
xfs_ino_t ino;
+ struct xfs_parent_defer *parent;
trace_xfs_create(dp, name);
@@ -988,13 +1013,17 @@ xfs_create(
return error;
if (is_dir) {
- resblks = XFS_MKDIR_SPACE_RES(mp, name->len);
+ resblks = xfs_mkdir_space_res(mp, name->len);
tres = &M_RES(mp)->tr_mkdir;
} else {
- resblks = XFS_CREATE_SPACE_RES(mp, name->len);
+ resblks = xfs_create_space_res(mp, name->len);
tres = &M_RES(mp)->tr_create;
}
+ error = xfs_parent_start(mp, &parent);
+ if (error)
+ goto out_release_dquots;
+
/*
* Initially assume that the file does not exist and
* reserve the resources for that case. If that is not
@@ -1010,7 +1039,7 @@ xfs_create(
resblks, &tp);
}
if (error)
- goto out_release_dquots;
+ goto out_parent;
xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
unlock_dp_on_error = true;
@@ -1020,6 +1049,7 @@ xfs_create(
* entry pointing to them, but a directory also the "." entry
* pointing to itself.
*/
+ init_xattrs = init_xattrs || xfs_has_parent(mp);
error = xfs_dialloc(&tp, dp->i_ino, mode, &ino);
if (!error)
error = xfs_init_new_inode(idmap, tp, dp, ino, mode,
@@ -1034,8 +1064,7 @@ xfs_create(
* the transaction cancel unlocking dp so don't do it explicitly in the
* error path.
*/
- xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
- unlock_dp_on_error = false;
+ xfs_trans_ijoin(tp, dp, 0);
error = xfs_dir_createname(tp, dp, name, ip->i_ino,
resblks - XFS_IALLOC_SPACE_RES(mp));
@@ -1054,6 +1083,16 @@ xfs_create(
xfs_bumplink(tp, dp);
}
+ /*
+ * If we have parent pointers, we need to add the attribute containing
+ * the parent information now.
+ */
+ if (parent) {
+ error = xfs_parent_add(tp, parent, dp, name, ip);
+ if (error)
+ goto out_trans_cancel;
+ }
+
/*
* If this is a synchronous mount, make sure that the
* create transaction goes to disk before returning to
@@ -1079,6 +1118,8 @@ xfs_create(
*ipp = ip;
xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ xfs_parent_finish(mp, parent);
return 0;
out_trans_cancel:
@@ -1094,6 +1135,8 @@ xfs_create(
xfs_finish_inode_setup(ip);
xfs_irele(ip);
}
+ out_parent:
+ xfs_parent_finish(mp, parent);
out_release_dquots:
xfs_qm_dqrele(udqp);
xfs_qm_dqrele(gdqp);
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index ee09aefa6088..a695bb2687ff 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -153,6 +153,12 @@ static inline struct inode *VFS_I(struct xfs_inode *ip)
return &ip->i_vnode;
}
+/* convert from const xfs inode to vfs inode */
+static inline const struct inode *VFS_IC(const struct xfs_inode *ip)
+{
+ return &ip->i_vnode;
+}
+
/*
* For regular files we only update the on-disk filesize when actually
* writing data back to disk. Until then only the copy in the VFS inode
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 4f814f9e12ab..95b906a14698 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -42,6 +42,7 @@
#include "xfs_xattr.h"
#include "xfs_iunlink_item.h"
#include "xfs_dahash_test.h"
+#include "xfs_parent.h"
#include <linux/magic.h>
#include <linux/fs_context.h>
@@ -2125,8 +2126,16 @@ xfs_init_caches(void)
if (!xfs_iunlink_cache)
goto out_destroy_attri_cache;
+ xfs_parent_intent_cache = kmem_cache_create("xfs_parent_intent",
+ sizeof(struct xfs_parent_defer),
+ 0, 0, NULL);
+ if (!xfs_parent_intent_cache)
+ goto out_destroy_iul_cache;
+
return 0;
+ out_destroy_iul_cache:
+ kmem_cache_destroy(xfs_iunlink_cache);
out_destroy_attri_cache:
kmem_cache_destroy(xfs_attri_cache);
out_destroy_attrd_cache:
@@ -2181,6 +2190,7 @@ xfs_destroy_caches(void)
* destroy caches.
*/
rcu_barrier();
+ kmem_cache_destroy(xfs_parent_intent_cache);
kmem_cache_destroy(xfs_iunlink_cache);
kmem_cache_destroy(xfs_attri_cache);
kmem_cache_destroy(xfs_attrd_cache);
diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
index 61779d92295f..ef5a635238e5 100644
--- a/fs/xfs/xfs_xattr.c
+++ b/fs/xfs/xfs_xattr.c
@@ -27,7 +27,7 @@
* they must release the permission by calling xlog_drop_incompat_feat
* when they're done.
*/
-static inline int
+int
xfs_attr_grab_log_assist(
struct xfs_mount *mp)
{
@@ -61,7 +61,7 @@ xfs_attr_grab_log_assist(
return error;
}
-static inline void
+void
xfs_attr_rele_log_assist(
struct xfs_mount *mp)
{
diff --git a/fs/xfs/xfs_xattr.h b/fs/xfs/xfs_xattr.h
index 2b09133b1b9b..7e0a2f3bb7f8 100644
--- a/fs/xfs/xfs_xattr.h
+++ b/fs/xfs/xfs_xattr.h
@@ -7,6 +7,8 @@
#define __XFS_XATTR_H__
int xfs_attr_change(struct xfs_da_args *args);
+int xfs_attr_grab_log_assist(struct xfs_mount *mp);
+void xfs_attr_rele_log_assist(struct xfs_mount *mp);
extern const struct xattr_handler *xfs_xattr_handlers[];
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 09/23] xfs: add parent attributes to link
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (7 preceding siblings ...)
2023-04-06 19:22 ` [PATCH 08/23] xfs: parent pointer attribute creation Darrick J. Wong
@ 2023-04-06 19:22 ` Darrick J. Wong
2023-04-06 19:23 ` [PATCH 10/23] xfs: add parent attributes to symlink Darrick J. Wong
` (13 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:22 UTC (permalink / raw)
To: djwong; +Cc: Dave Chinner, Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
This patch modifies xfs_link to add a parent pointer to the inode.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: minor rebase fixes]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_trans_space.h | 2 -
fs/xfs/xfs_inode.c | 56 +++++++++++++++++++++++++++++++++++----
2 files changed, 50 insertions(+), 8 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_trans_space.h b/fs/xfs/libxfs/xfs_trans_space.h
index 87b31c69a773..f72207923ec2 100644
--- a/fs/xfs/libxfs/xfs_trans_space.h
+++ b/fs/xfs/libxfs/xfs_trans_space.h
@@ -84,8 +84,6 @@
(2 * (mp)->m_alloc_maxlevels)
#define XFS_GROWFSRT_SPACE_RES(mp,b) \
((b) + XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK))
-#define XFS_LINK_SPACE_RES(mp,nl) \
- XFS_DIRENTER_SPACE_RES(mp,nl)
#define XFS_MKDIR_SPACE_RES(mp,nl) \
(XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
#define XFS_QM_DQALLOC_SPACE_RES(mp) \
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index e079fa95ad2e..c07b9b5b8d34 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1244,16 +1244,31 @@ xfs_create_tmpfile(
return error;
}
+static unsigned int
+xfs_link_space_res(
+ struct xfs_mount *mp,
+ unsigned int namelen)
+{
+ unsigned int ret;
+
+ ret = XFS_DIRENTER_SPACE_RES(mp, namelen);
+ if (xfs_has_parent(mp))
+ ret += xfs_parent_calc_space_res(mp, namelen);
+
+ return ret;
+}
+
int
xfs_link(
- xfs_inode_t *tdp,
- xfs_inode_t *sip,
+ struct xfs_inode *tdp,
+ struct xfs_inode *sip,
struct xfs_name *target_name)
{
- xfs_mount_t *mp = tdp->i_mount;
- xfs_trans_t *tp;
+ struct xfs_mount *mp = tdp->i_mount;
+ struct xfs_trans *tp;
int error, nospace_error = 0;
int resblks;
+ struct xfs_parent_defer *parent = NULL;
trace_xfs_link(tdp, target_name);
@@ -1270,11 +1285,25 @@ xfs_link(
if (error)
goto std_return;
- resblks = XFS_LINK_SPACE_RES(mp, target_name->len);
+ error = xfs_parent_start(mp, &parent);
+ if (error)
+ goto std_return;
+
+ resblks = xfs_link_space_res(mp, target_name->len);
error = xfs_trans_alloc_dir(tdp, &M_RES(mp)->tr_link, sip, &resblks,
&tp, &nospace_error);
if (error)
- goto std_return;
+ goto out_parent;
+
+ /*
+ * We don't allow reservationless or quotaless hardlinking when parent
+ * pointers are enabled because we can't back out if the xattrs must
+ * grow.
+ */
+ if (parent && nospace_error) {
+ error = nospace_error;
+ goto error_return;
+ }
/*
* If we are using project inheritance, we only allow hard link
@@ -1315,6 +1344,18 @@ xfs_link(
xfs_bumplink(tp, sip);
+ /*
+ * If we have parent pointers, we now need to add the parent record to
+ * the attribute fork of the inode. If this is the initial parent
+ * attribute, we need to create it correctly, otherwise we can just add
+ * the parent to the inode.
+ */
+ if (parent) {
+ error = xfs_parent_add(tp, parent, tdp, target_name, sip);
+ if (error)
+ goto error_return;
+ }
+
/*
* If this is a synchronous mount, make sure that the
* link transaction goes to disk before returning to
@@ -1326,12 +1367,15 @@ xfs_link(
error = xfs_trans_commit(tp);
xfs_iunlock(tdp, XFS_ILOCK_EXCL);
xfs_iunlock(sip, XFS_ILOCK_EXCL);
+ xfs_parent_finish(mp, parent);
return error;
error_return:
xfs_trans_cancel(tp);
xfs_iunlock(tdp, XFS_ILOCK_EXCL);
xfs_iunlock(sip, XFS_ILOCK_EXCL);
+ out_parent:
+ xfs_parent_finish(mp, parent);
std_return:
if (error == -ENOSPC && nospace_error)
error = nospace_error;
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 10/23] xfs: add parent attributes to symlink
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (8 preceding siblings ...)
2023-04-06 19:22 ` [PATCH 09/23] xfs: add parent attributes to link Darrick J. Wong
@ 2023-04-06 19:23 ` Darrick J. Wong
2023-04-06 19:23 ` [PATCH 11/23] xfs: remove parent pointers in unlink Darrick J. Wong
` (12 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:23 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
This patch modifies xfs_symlink to add a parent pointer to the inode.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: minor rebase fixups]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_trans_space.h | 2 --
fs/xfs/xfs_symlink.c | 49 +++++++++++++++++++++++++++++++++------
2 files changed, 42 insertions(+), 9 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_trans_space.h b/fs/xfs/libxfs/xfs_trans_space.h
index f72207923ec2..25a55650baf4 100644
--- a/fs/xfs/libxfs/xfs_trans_space.h
+++ b/fs/xfs/libxfs/xfs_trans_space.h
@@ -95,8 +95,6 @@
XFS_DIRREMOVE_SPACE_RES(mp)
#define XFS_RENAME_SPACE_RES(mp,nl) \
(XFS_DIRREMOVE_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
-#define XFS_SYMLINK_SPACE_RES(mp,nl,b) \
- (XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl) + (b))
#define XFS_IFREE_SPACE_RES(mp) \
(xfs_has_finobt(mp) ? M_IGEO(mp)->inobt_maxlevels : 0)
diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c
index 315a709e53bd..198527c1fb22 100644
--- a/fs/xfs/xfs_symlink.c
+++ b/fs/xfs/xfs_symlink.c
@@ -23,6 +23,8 @@
#include "xfs_trans.h"
#include "xfs_ialloc.h"
#include "xfs_error.h"
+#include "xfs_parent.h"
+#include "xfs_defer.h"
/* ----- Kernel only functions below ----- */
int
@@ -142,6 +144,23 @@ xfs_readlink(
return error;
}
+static unsigned int
+xfs_symlink_space_res(
+ struct xfs_mount *mp,
+ unsigned int namelen,
+ unsigned int fsblocks)
+{
+ unsigned int ret;
+
+ ret = XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp, namelen) +
+ fsblocks;
+
+ if (xfs_has_parent(mp))
+ ret += xfs_parent_calc_space_res(mp, namelen);
+
+ return ret;
+}
+
int
xfs_symlink(
struct mnt_idmap *idmap,
@@ -172,6 +191,7 @@ xfs_symlink(
struct xfs_dquot *pdqp = NULL;
uint resblks;
xfs_ino_t ino;
+ struct xfs_parent_defer *parent;
*ipp = NULL;
@@ -202,18 +222,24 @@ xfs_symlink(
/*
* The symlink will fit into the inode data fork?
- * There can't be any attributes so we get the whole variable part.
+ * If there are no parent pointers, then there wont't be any attributes.
+ * So we get the whole variable part, and do not need to reserve extra
+ * blocks. Otherwise, we need to reserve the blocks.
*/
- if (pathlen <= XFS_LITINO(mp))
+ if (pathlen <= XFS_LITINO(mp) && !xfs_has_parent(mp))
fs_blocks = 0;
else
fs_blocks = xfs_symlink_blocks(mp, pathlen);
- resblks = XFS_SYMLINK_SPACE_RES(mp, link_name->len, fs_blocks);
+ resblks = xfs_symlink_space_res(mp, link_name->len, fs_blocks);
+
+ error = xfs_parent_start(mp, &parent);
+ if (error)
+ goto out_release_dquots;
error = xfs_trans_alloc_icreate(mp, &M_RES(mp)->tr_symlink, udqp, gdqp,
pdqp, resblks, &tp);
if (error)
- goto out_release_dquots;
+ goto out_parent;
xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
unlock_dp_on_error = true;
@@ -233,7 +259,7 @@ xfs_symlink(
if (!error)
error = xfs_init_new_inode(idmap, tp, dp, ino,
S_IFLNK | (mode & ~S_IFMT), 1, 0, prid,
- false, &ip);
+ xfs_has_parent(mp), &ip);
if (error)
goto out_trans_cancel;
@@ -244,8 +270,7 @@ xfs_symlink(
* the transaction cancel unlocking dp so don't do it explicitly in the
* error path.
*/
- xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
- unlock_dp_on_error = false;
+ xfs_trans_ijoin(tp, dp, 0);
/*
* Also attach the dquot(s) to it, if applicable.
@@ -320,6 +345,12 @@ xfs_symlink(
xfs_trans_ichgtime(tp, dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE);
+ if (parent) {
+ error = xfs_parent_add(tp, parent, dp, link_name, ip);
+ if (error)
+ goto out_trans_cancel;
+ }
+
/*
* If this is a synchronous mount, make sure that the
* symlink transaction goes to disk before returning to
@@ -338,6 +369,8 @@ xfs_symlink(
*ipp = ip;
xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ xfs_parent_finish(mp, parent);
return 0;
out_trans_cancel:
@@ -353,6 +386,8 @@ xfs_symlink(
xfs_finish_inode_setup(ip);
xfs_irele(ip);
}
+out_parent:
+ xfs_parent_finish(mp, parent);
out_release_dquots:
xfs_qm_dqrele(udqp);
xfs_qm_dqrele(gdqp);
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 11/23] xfs: remove parent pointers in unlink
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (9 preceding siblings ...)
2023-04-06 19:23 ` [PATCH 10/23] xfs: add parent attributes to symlink Darrick J. Wong
@ 2023-04-06 19:23 ` Darrick J. Wong
2023-04-06 19:23 ` [PATCH 12/23] xfs: Indent xfs_rename Darrick J. Wong
` (11 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:23 UTC (permalink / raw)
To: djwong; +Cc: Dave Chinner, Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
This patch removes the parent pointer attribute during unlink
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: adjust to new ondisk format, minor rebase fixes]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_attr.c | 2 +-
fs/xfs/libxfs/xfs_attr.h | 1 +
fs/xfs/libxfs/xfs_parent.c | 22 ++++++++++++++++++++++
fs/xfs/libxfs/xfs_parent.h | 4 ++++
fs/xfs/libxfs/xfs_trans_space.h | 2 --
fs/xfs/xfs_inode.c | 39 +++++++++++++++++++++++++++++++++------
6 files changed, 61 insertions(+), 9 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index e2acd7ecdfcc..37c86274b568 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -959,7 +959,7 @@ xfs_attr_defer_replace(
}
/* Removes an attribute for an inode as a deferred operation */
-static int
+int
xfs_attr_defer_remove(
struct xfs_da_args *args)
{
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 1002e431b728..07c61bd70ef8 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -550,6 +550,7 @@ bool xfs_attr_is_leaf(struct xfs_inode *ip);
int xfs_attr_get_ilocked(struct xfs_da_args *args);
int xfs_attr_get(struct xfs_da_args *args);
int xfs_attr_defer_add(struct xfs_da_args *args);
+int xfs_attr_defer_remove(struct xfs_da_args *args);
int xfs_attr_set(struct xfs_da_args *args);
int xfs_attr_set_iter(struct xfs_attr_intent *attr);
int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c
index f57e1a201f17..393409502770 100644
--- a/fs/xfs/libxfs/xfs_parent.c
+++ b/fs/xfs/libxfs/xfs_parent.c
@@ -182,6 +182,28 @@ xfs_parent_add(
return xfs_attr_defer_add(args);
}
+/* Remove a parent pointer to reflect a dirent removal. */
+int
+xfs_parent_remove(
+ struct xfs_trans *tp,
+ struct xfs_parent_defer *parent,
+ struct xfs_inode *dp,
+ const struct xfs_name *parent_name,
+ struct xfs_inode *child)
+{
+ struct xfs_da_args *args = &parent->args;
+
+ xfs_init_parent_name_rec(&parent->rec, dp, parent_name, child);
+ args->hashval = xfs_parent_hashname(dp, parent);
+
+ args->trans = tp;
+ args->dp = child;
+
+ xfs_init_parent_davalue(&parent->args, parent_name);
+
+ return xfs_attr_defer_remove(args);
+}
+
/* Cancel a parent pointer operation. */
void
__xfs_parent_cancel(
diff --git a/fs/xfs/libxfs/xfs_parent.h b/fs/xfs/libxfs/xfs_parent.h
index 8e7dbe22e1f7..0f8194cd8a9f 100644
--- a/fs/xfs/libxfs/xfs_parent.h
+++ b/fs/xfs/libxfs/xfs_parent.h
@@ -41,6 +41,10 @@ xfs_parent_start(
int xfs_parent_add(struct xfs_trans *tp, struct xfs_parent_defer *parent,
struct xfs_inode *dp, const struct xfs_name *parent_name,
struct xfs_inode *child);
+int xfs_parent_remove(struct xfs_trans *tp, struct xfs_parent_defer *parent,
+ struct xfs_inode *dp, const struct xfs_name *parent_name,
+ struct xfs_inode *child);
+
void __xfs_parent_cancel(struct xfs_mount *mp, struct xfs_parent_defer *parent);
static inline void
diff --git a/fs/xfs/libxfs/xfs_trans_space.h b/fs/xfs/libxfs/xfs_trans_space.h
index 25a55650baf4..b5ab6701e7fb 100644
--- a/fs/xfs/libxfs/xfs_trans_space.h
+++ b/fs/xfs/libxfs/xfs_trans_space.h
@@ -91,8 +91,6 @@
XFS_DQUOT_CLUSTER_SIZE_FSB)
#define XFS_QM_QINOCREATE_SPACE_RES(mp) \
XFS_IALLOC_SPACE_RES(mp)
-#define XFS_REMOVE_SPACE_RES(mp) \
- XFS_DIRREMOVE_SPACE_RES(mp)
#define XFS_RENAME_SPACE_RES(mp,nl) \
(XFS_DIRREMOVE_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
#define XFS_IFREE_SPACE_RES(mp) \
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index c07b9b5b8d34..aea1f781c84c 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2472,6 +2472,19 @@ xfs_iunpin_wait(
__xfs_iunpin_wait(ip);
}
+static unsigned int
+xfs_remove_space_res(
+ struct xfs_mount *mp,
+ unsigned int namelen)
+{
+ unsigned int ret = XFS_DIRREMOVE_SPACE_RES(mp);
+
+ if (xfs_has_parent(mp))
+ ret += xfs_parent_calc_space_res(mp, namelen);
+
+ return ret;
+}
+
/*
* Removing an inode from the namespace involves removing the directory entry
* and dropping the link count on the inode. Removing the directory entry can
@@ -2501,16 +2514,17 @@ xfs_iunpin_wait(
*/
int
xfs_remove(
- xfs_inode_t *dp,
+ struct xfs_inode *dp,
struct xfs_name *name,
- xfs_inode_t *ip)
+ struct xfs_inode *ip)
{
- xfs_mount_t *mp = dp->i_mount;
- xfs_trans_t *tp = NULL;
+ struct xfs_mount *mp = dp->i_mount;
+ struct xfs_trans *tp = NULL;
int is_dir = S_ISDIR(VFS_I(ip)->i_mode);
int dontcare;
int error = 0;
uint resblks;
+ struct xfs_parent_defer *parent = NULL;
trace_xfs_remove(dp, name);
@@ -2525,6 +2539,10 @@ xfs_remove(
if (error)
goto std_return;
+ error = xfs_parent_start(mp, &parent);
+ if (error)
+ goto std_return;
+
/*
* We try to get the real space reservation first, allowing for
* directory btree deletion(s) implying possible bmap insert(s). If we
@@ -2536,12 +2554,12 @@ xfs_remove(
* the directory code can handle a reservationless update and we don't
* want to prevent a user from trying to free space by deleting things.
*/
- resblks = XFS_REMOVE_SPACE_RES(mp);
+ resblks = xfs_remove_space_res(mp, name->len);
error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, ip, &resblks,
&tp, &dontcare);
if (error) {
ASSERT(error != -ENOSPC);
- goto std_return;
+ goto out_parent;
}
/*
@@ -2601,6 +2619,12 @@ xfs_remove(
goto out_trans_cancel;
}
+ if (parent) {
+ error = xfs_parent_remove(tp, parent, dp, name, ip);
+ if (error)
+ goto out_trans_cancel;
+ }
+
/*
* If this is a synchronous mount, make sure that the
* remove transaction goes to disk before returning to
@@ -2618,6 +2642,7 @@ xfs_remove(
xfs_iunlock(ip, XFS_ILOCK_EXCL);
xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ xfs_parent_finish(mp, parent);
return 0;
out_trans_cancel:
@@ -2625,6 +2650,8 @@ xfs_remove(
out_unlock:
xfs_iunlock(ip, XFS_ILOCK_EXCL);
xfs_iunlock(dp, XFS_ILOCK_EXCL);
+ out_parent:
+ xfs_parent_finish(mp, parent);
std_return:
return error;
}
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 12/23] xfs: Indent xfs_rename
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (10 preceding siblings ...)
2023-04-06 19:23 ` [PATCH 11/23] xfs: remove parent pointers in unlink Darrick J. Wong
@ 2023-04-06 19:23 ` Darrick J. Wong
2023-04-06 19:23 ` [PATCH 13/23] xfs: Add parent pointers to rename Darrick J. Wong
` (10 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:23 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Indent variables and parameters in xfs_rename in preparation for
parent pointer modifications. White space only, no functional
changes. This will make reviewing new code easier on reviewers.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/xfs_inode.c | 39 ++++++++++++++++++++-------------------
1 file changed, 20 insertions(+), 19 deletions(-)
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index aea1f781c84c..1c09ae7d4ed5 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2896,26 +2896,27 @@ xfs_rename_alloc_whiteout(
*/
int
xfs_rename(
- struct mnt_idmap *idmap,
- struct xfs_inode *src_dp,
- struct xfs_name *src_name,
- struct xfs_inode *src_ip,
- struct xfs_inode *target_dp,
- struct xfs_name *target_name,
- struct xfs_inode *target_ip,
- unsigned int flags)
+ struct mnt_idmap *idmap,
+ struct xfs_inode *src_dp,
+ struct xfs_name *src_name,
+ struct xfs_inode *src_ip,
+ struct xfs_inode *target_dp,
+ struct xfs_name *target_name,
+ struct xfs_inode *target_ip,
+ unsigned int flags)
{
- struct xfs_mount *mp = src_dp->i_mount;
- struct xfs_trans *tp;
- struct xfs_inode *wip = NULL; /* whiteout inode */
- struct xfs_inode *inodes[__XFS_SORT_INODES];
- int i;
- int num_inodes = __XFS_SORT_INODES;
- bool new_parent = (src_dp != target_dp);
- bool src_is_directory = S_ISDIR(VFS_I(src_ip)->i_mode);
- int spaceres;
- bool retried = false;
- int error, nospace_error = 0;
+ struct xfs_mount *mp = src_dp->i_mount;
+ struct xfs_trans *tp;
+ struct xfs_inode *wip = NULL; /* whiteout inode */
+ struct xfs_inode *inodes[__XFS_SORT_INODES];
+ int i;
+ int num_inodes = __XFS_SORT_INODES;
+ bool new_parent = (src_dp != target_dp);
+ bool src_is_directory =
+ S_ISDIR(VFS_I(src_ip)->i_mode);
+ int spaceres;
+ bool retried = false;
+ int error, nospace_error = 0;
trace_xfs_rename(src_dp, target_dp, src_name, target_name);
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 13/23] xfs: Add parent pointers to rename
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (11 preceding siblings ...)
2023-04-06 19:23 ` [PATCH 12/23] xfs: Indent xfs_rename Darrick J. Wong
@ 2023-04-06 19:23 ` Darrick J. Wong
2023-04-06 19:24 ` [PATCH 14/23] xfs: Add parent pointers to xfs_cross_rename Darrick J. Wong
` (9 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:23 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
This patch removes the old parent pointer attribute during the rename
operation, and re-adds the updated parent pointer.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: adjust to new ondisk format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_attr.c | 2 -
fs/xfs/libxfs/xfs_attr.h | 1
fs/xfs/libxfs/xfs_parent.c | 58 ++++++++++++++++++++--
fs/xfs/libxfs/xfs_parent.h | 23 ++++++++-
fs/xfs/libxfs/xfs_trans_space.h | 2 -
fs/xfs/xfs_inode.c | 103 +++++++++++++++++++++++++++++++++++++--
6 files changed, 174 insertions(+), 15 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c
index 37c86274b568..e1e0a5fd3418 100644
--- a/fs/xfs/libxfs/xfs_attr.c
+++ b/fs/xfs/libxfs/xfs_attr.c
@@ -936,7 +936,7 @@ xfs_attr_defer_add(
}
/* Sets an attribute for an inode as a deferred operation */
-static int
+int
xfs_attr_defer_replace(
struct xfs_da_args *args)
{
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 07c61bd70ef8..3ad1f12a511a 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -551,6 +551,7 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args);
int xfs_attr_get(struct xfs_da_args *args);
int xfs_attr_defer_add(struct xfs_da_args *args);
int xfs_attr_defer_remove(struct xfs_da_args *args);
+int xfs_attr_defer_replace(struct xfs_da_args *args);
int xfs_attr_set(struct xfs_da_args *args);
int xfs_attr_set_iter(struct xfs_attr_intent *attr);
int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c
index 393409502770..977412b8f9f3 100644
--- a/fs/xfs/libxfs/xfs_parent.c
+++ b/fs/xfs/libxfs/xfs_parent.c
@@ -116,6 +116,19 @@ xfs_init_parent_davalue(
args->value = (void *)name->name;
}
+/*
+ * Point the da args new value fields at the non-key parts of a replacement
+ * parent pointer.
+ */
+static inline void
+xfs_init_parent_danewvalue(
+ struct xfs_da_args *args,
+ const struct xfs_name *name)
+{
+ args->new_valuelen = name->len;
+ args->new_value = (void *)name->name;
+}
+
/*
* Allocate memory to control a logged parent pointer update as part of a
* dirent operation.
@@ -123,22 +136,27 @@ xfs_init_parent_davalue(
int
__xfs_parent_init(
struct xfs_mount *mp,
+ bool grab_log,
struct xfs_parent_defer **parentp)
{
struct xfs_parent_defer *parent;
int error;
- error = xfs_attr_grab_log_assist(mp);
- if (error)
- return error;
+ if (grab_log) {
+ error = xfs_attr_grab_log_assist(mp);
+ if (error)
+ return error;
+ }
parent = kmem_cache_zalloc(xfs_parent_intent_cache, GFP_KERNEL);
if (!parent) {
- xfs_attr_rele_log_assist(mp);
+ if (grab_log)
+ xfs_attr_rele_log_assist(mp);
return -ENOMEM;
}
/* init parent da_args */
+ parent->have_log = grab_log;
parent->args.geo = mp->m_attr_geo;
parent->args.whichfork = XFS_ATTR_FORK;
parent->args.attr_filter = XFS_ATTR_PARENT;
@@ -204,13 +222,43 @@ xfs_parent_remove(
return xfs_attr_defer_remove(args);
}
+/* Replace one parent pointer with another to reflect a rename. */
+int
+xfs_parent_replace(
+ struct xfs_trans *tp,
+ struct xfs_parent_defer *parent,
+ struct xfs_inode *old_dp,
+ const struct xfs_name *old_name,
+ struct xfs_inode *new_dp,
+ const struct xfs_name *new_name,
+ struct xfs_inode *child)
+{
+ struct xfs_da_args *args = &parent->args;
+
+ xfs_init_parent_name_rec(&parent->rec, old_dp, old_name, child);
+ args->hashval = xfs_parent_hashname(old_dp, parent);
+
+ xfs_init_parent_name_rec(&parent->new_rec, new_dp, new_name, child);
+ args->new_name = (const uint8_t *)&parent->new_rec;
+ args->new_namelen = sizeof(struct xfs_parent_name_rec);
+
+ args->trans = tp;
+ args->dp = child;
+
+ xfs_init_parent_davalue(&parent->args, old_name);
+ xfs_init_parent_danewvalue(&parent->args, new_name);
+
+ return xfs_attr_defer_replace(args);
+}
+
/* Cancel a parent pointer operation. */
void
__xfs_parent_cancel(
struct xfs_mount *mp,
struct xfs_parent_defer *parent)
{
- xlog_drop_incompat_feat(mp->m_log);
+ if (parent->have_log)
+ xlog_drop_incompat_feat(mp->m_log);
kmem_cache_free(xfs_parent_intent_cache, parent);
}
diff --git a/fs/xfs/libxfs/xfs_parent.h b/fs/xfs/libxfs/xfs_parent.h
index 0f8194cd8a9f..f4a0793bc4fc 100644
--- a/fs/xfs/libxfs/xfs_parent.h
+++ b/fs/xfs/libxfs/xfs_parent.h
@@ -21,10 +21,13 @@ extern struct kmem_cache *xfs_parent_intent_cache;
*/
struct xfs_parent_defer {
struct xfs_parent_name_rec rec;
+ struct xfs_parent_name_rec new_rec;
struct xfs_da_args args;
+ bool have_log;
};
-int __xfs_parent_init(struct xfs_mount *mp, struct xfs_parent_defer **parentp);
+int __xfs_parent_init(struct xfs_mount *mp, bool grab_log,
+ struct xfs_parent_defer **parentp);
static inline int
xfs_parent_start(
@@ -34,7 +37,19 @@ xfs_parent_start(
*pp = NULL;
if (xfs_has_parent(mp))
- return __xfs_parent_init(mp, pp);
+ return __xfs_parent_init(mp, true, pp);
+ return 0;
+}
+
+static inline int
+xfs_parent_start_locked(
+ struct xfs_mount *mp,
+ struct xfs_parent_defer **pp)
+{
+ *pp = NULL;
+
+ if (xfs_has_parent(mp))
+ return __xfs_parent_init(mp, false, pp);
return 0;
}
@@ -44,6 +59,10 @@ int xfs_parent_add(struct xfs_trans *tp, struct xfs_parent_defer *parent,
int xfs_parent_remove(struct xfs_trans *tp, struct xfs_parent_defer *parent,
struct xfs_inode *dp, const struct xfs_name *parent_name,
struct xfs_inode *child);
+int xfs_parent_replace(struct xfs_trans *tp, struct xfs_parent_defer *parent,
+ struct xfs_inode *old_dp, const struct xfs_name *old_name,
+ struct xfs_inode *new_dp, const struct xfs_name *new_name,
+ struct xfs_inode *child);
void __xfs_parent_cancel(struct xfs_mount *mp, struct xfs_parent_defer *parent);
diff --git a/fs/xfs/libxfs/xfs_trans_space.h b/fs/xfs/libxfs/xfs_trans_space.h
index b5ab6701e7fb..810610a14c4d 100644
--- a/fs/xfs/libxfs/xfs_trans_space.h
+++ b/fs/xfs/libxfs/xfs_trans_space.h
@@ -91,8 +91,6 @@
XFS_DQUOT_CLUSTER_SIZE_FSB)
#define XFS_QM_QINOCREATE_SPACE_RES(mp) \
XFS_IALLOC_SPACE_RES(mp)
-#define XFS_RENAME_SPACE_RES(mp,nl) \
- (XFS_DIRREMOVE_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
#define XFS_IFREE_SPACE_RES(mp) \
(xfs_has_finobt(mp) ? M_IGEO(mp)->inobt_maxlevels : 0)
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 1c09ae7d4ed5..5ad934358791 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2865,7 +2865,7 @@ xfs_rename_alloc_whiteout(
int error;
error = xfs_create_tmpfile(idmap, dp, S_IFCHR | WHITEOUT_MODE,
- false, &tmpfile);
+ xfs_has_parent(dp->i_mount), &tmpfile);
if (error)
return error;
@@ -2891,6 +2891,31 @@ xfs_rename_alloc_whiteout(
return 0;
}
+static unsigned int
+xfs_rename_space_res(
+ struct xfs_mount *mp,
+ struct xfs_name *src_name,
+ struct xfs_parent_defer *target_parent_ptr,
+ struct xfs_name *target_name,
+ struct xfs_parent_defer *new_parent_ptr,
+ struct xfs_inode *wip)
+{
+ unsigned int ret;
+
+ ret = XFS_DIRREMOVE_SPACE_RES(mp) +
+ XFS_DIRENTER_SPACE_RES(mp, target_name->len);
+
+ if (new_parent_ptr) {
+ if (wip)
+ ret += xfs_parent_calc_space_res(mp, src_name->len);
+ ret += 2 * xfs_parent_calc_space_res(mp, target_name->len);
+ }
+ if (target_parent_ptr)
+ ret += xfs_parent_calc_space_res(mp, target_name->len);
+
+ return ret;
+}
+
/*
* xfs_rename
*/
@@ -2917,6 +2942,9 @@ xfs_rename(
int spaceres;
bool retried = false;
int error, nospace_error = 0;
+ struct xfs_parent_defer *src_ip_pptr = NULL;
+ struct xfs_parent_defer *tgt_ip_pptr = NULL;
+ struct xfs_parent_defer *wip_pptr = NULL;
trace_xfs_rename(src_dp, target_dp, src_name, target_name);
@@ -2941,9 +2969,26 @@ xfs_rename(
xfs_sort_for_rename(src_dp, target_dp, src_ip, target_ip, wip,
inodes, &num_inodes);
+ error = xfs_parent_start(mp, &src_ip_pptr);
+ if (error)
+ goto out_release_wip;
+
+ if (wip) {
+ error = xfs_parent_start_locked(mp, &wip_pptr);
+ if (error)
+ goto out_src_ip_pptr;
+ }
+
+ if (target_ip) {
+ error = xfs_parent_start_locked(mp, &tgt_ip_pptr);
+ if (error)
+ goto out_wip_pptr;
+ }
+
retry:
nospace_error = 0;
- spaceres = XFS_RENAME_SPACE_RES(mp, target_name->len);
+ spaceres = xfs_rename_space_res(mp, src_name, tgt_ip_pptr,
+ target_name, src_ip_pptr, wip);
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_rename, spaceres, 0, 0, &tp);
if (error == -ENOSPC) {
nospace_error = error;
@@ -2952,14 +2997,26 @@ xfs_rename(
&tp);
}
if (error)
- goto out_release_wip;
+ goto out_tgt_ip_pptr;
+
+ /*
+ * We don't allow reservationless renaming when parent pointers are
+ * enabled because we can't back out if the xattrs must grow.
+ */
+ if (src_ip_pptr && nospace_error) {
+ error = nospace_error;
+ xfs_trans_cancel(tp);
+ goto out_tgt_ip_pptr;
+ }
/*
* Attach the dquots to the inodes
*/
error = xfs_qm_vop_rename_dqattach(inodes);
- if (error)
- goto out_trans_cancel;
+ if (error) {
+ xfs_trans_cancel(tp);
+ goto out_tgt_ip_pptr;
+ }
/*
* Lock all the participating inodes. Depending upon whether
@@ -3026,6 +3083,15 @@ xfs_rename(
goto out_trans_cancel;
}
+ /*
+ * We don't allow quotaless renaming when parent pointers are enabled
+ * because we can't back out if the xattrs must grow.
+ */
+ if (src_ip_pptr && nospace_error) {
+ error = nospace_error;
+ goto out_trans_cancel;
+ }
+
/*
* Check for expected errors before we dirty the transaction
* so we can return an error without a transaction abort.
@@ -3218,6 +3284,26 @@ xfs_rename(
if (error)
goto out_trans_cancel;
+ if (wip_pptr) {
+ error = xfs_parent_add(tp, wip_pptr, src_dp, src_name, wip);
+ if (error)
+ goto out_trans_cancel;
+ }
+
+ if (src_ip_pptr) {
+ error = xfs_parent_replace(tp, src_ip_pptr, src_dp, src_name,
+ target_dp, target_name, src_ip);
+ if (error)
+ goto out_trans_cancel;
+ }
+
+ if (tgt_ip_pptr) {
+ error = xfs_parent_remove(tp, tgt_ip_pptr, target_dp,
+ target_name, target_ip);
+ if (error)
+ goto out_trans_cancel;
+ }
+
xfs_trans_ichgtime(tp, src_dp, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
xfs_trans_log_inode(tp, src_dp, XFS_ILOG_CORE);
if (new_parent)
@@ -3231,6 +3317,13 @@ xfs_rename(
xfs_trans_cancel(tp);
out_unlock:
xfs_iunlock_rename(inodes, num_inodes);
+out_tgt_ip_pptr:
+ xfs_parent_finish(mp, tgt_ip_pptr);
+out_wip_pptr:
+ xfs_parent_finish(mp, wip_pptr);
+out_src_ip_pptr:
+ xfs_parent_finish(mp, src_ip_pptr);
+
out_release_wip:
if (wip)
xfs_irele(wip);
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 14/23] xfs: Add parent pointers to xfs_cross_rename
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (12 preceding siblings ...)
2023-04-06 19:23 ` [PATCH 13/23] xfs: Add parent pointers to rename Darrick J. Wong
@ 2023-04-06 19:24 ` Darrick J. Wong
2023-04-06 19:24 ` [PATCH 15/23] xfs: Add helper function xfs_attr_list_context_init Darrick J. Wong
` (8 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:24 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Cross renames are handled separately from standard renames, and
need different handling to update the parent attributes correctly.
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
fs/xfs/xfs_inode.c | 44 ++++++++++++++++++++++++++++++--------------
1 file changed, 30 insertions(+), 14 deletions(-)
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 5ad934358791..6d364a48e3cc 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -2743,19 +2743,22 @@ xfs_finish_rename(
*/
STATIC int
xfs_cross_rename(
- struct xfs_trans *tp,
- struct xfs_inode *dp1,
- struct xfs_name *name1,
- struct xfs_inode *ip1,
- struct xfs_inode *dp2,
- struct xfs_name *name2,
- struct xfs_inode *ip2,
- int spaceres)
+ struct xfs_trans *tp,
+ struct xfs_inode *dp1,
+ struct xfs_name *name1,
+ struct xfs_inode *ip1,
+ struct xfs_parent_defer *ip1_pptr,
+ struct xfs_inode *dp2,
+ struct xfs_name *name2,
+ struct xfs_inode *ip2,
+ struct xfs_parent_defer *ip2_pptr,
+ int spaceres)
{
- int error = 0;
- int ip1_flags = 0;
- int ip2_flags = 0;
- int dp2_flags = 0;
+ struct xfs_mount *mp = dp1->i_mount;
+ int error = 0;
+ int ip1_flags = 0;
+ int ip2_flags = 0;
+ int dp2_flags = 0;
/* Swap inode number for dirent in first parent */
error = xfs_dir_replace(tp, dp1, name1, ip2->i_ino, spaceres);
@@ -2824,6 +2827,18 @@ xfs_cross_rename(
}
}
+ if (xfs_has_parent(mp)) {
+ error = xfs_parent_replace(tp, ip1_pptr, dp1, name1, dp2,
+ name2, ip1);
+ if (error)
+ goto out_trans_abort;
+
+ error = xfs_parent_replace(tp, ip2_pptr, dp2, name2, dp1,
+ name1, ip2);
+ if (error)
+ goto out_trans_abort;
+ }
+
if (ip1_flags) {
xfs_trans_ichgtime(tp, ip1, ip1_flags);
xfs_trans_log_inode(tp, ip1, XFS_ILOG_CORE);
@@ -2838,6 +2853,7 @@ xfs_cross_rename(
}
xfs_trans_ichgtime(tp, dp1, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
xfs_trans_log_inode(tp, dp1, XFS_ILOG_CORE);
+
return xfs_finish_rename(tp);
out_trans_abort:
@@ -3052,8 +3068,8 @@ xfs_rename(
/* RENAME_EXCHANGE is unique from here on. */
if (flags & RENAME_EXCHANGE) {
error = xfs_cross_rename(tp, src_dp, src_name, src_ip,
- target_dp, target_name, target_ip,
- spaceres);
+ src_ip_pptr, target_dp, target_name, target_ip,
+ tgt_ip_pptr, spaceres);
goto out_unlock;
}
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 15/23] xfs: Add helper function xfs_attr_list_context_init
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (13 preceding siblings ...)
2023-04-06 19:24 ` [PATCH 14/23] xfs: Add parent pointers to xfs_cross_rename Darrick J. Wong
@ 2023-04-06 19:24 ` Darrick J. Wong
2023-04-06 19:24 ` [PATCH 16/23] xfs: Filter XFS_ATTR_PARENT for getfattr Darrick J. Wong
` (7 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:24 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
This patch adds a helper function xfs_attr_list_context_init used by
xfs_attr_list. This function initializes the xfs_attr_list_context
structure passed to xfs_attr_list_int. We will need this later to call
xfs_attr_list_int_ilocked when the node is already locked.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/xfs_file.c | 1 +
fs/xfs/xfs_ioctl.c | 54 +++++++++++++++++++++++++++++++++++++---------------
fs/xfs/xfs_ioctl.h | 2 ++
3 files changed, 41 insertions(+), 16 deletions(-)
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 705250f9f90a..2ffa4488e246 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -17,6 +17,7 @@
#include "xfs_bmap_util.h"
#include "xfs_dir2.h"
#include "xfs_dir2_priv.h"
+#include "xfs_attr.h"
#include "xfs_ioctl.h"
#include "xfs_trace.h"
#include "xfs_log.h"
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 55bb01173cde..59987b95201c 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -369,6 +369,40 @@ xfs_attr_flags(
return 0;
}
+/*
+ * Initializes an xfs_attr_list_context suitable for
+ * use by xfs_attr_list
+ */
+int
+xfs_ioc_attr_list_context_init(
+ struct xfs_inode *dp,
+ char *buffer,
+ int bufsize,
+ int flags,
+ struct xfs_attr_list_context *context)
+{
+ struct xfs_attrlist *alist;
+
+ /*
+ * Initialize the output buffer.
+ */
+ context->dp = dp;
+ context->resynch = 1;
+ context->attr_filter = xfs_attr_filter(flags);
+ context->buffer = buffer;
+ context->bufsize = round_down(bufsize, sizeof(uint32_t));
+ context->firstu = context->bufsize;
+ context->put_listent = xfs_ioc_attr_put_listent;
+
+ alist = context->buffer;
+ alist->al_count = 0;
+ alist->al_more = 0;
+ alist->al_offset[0] = context->bufsize;
+
+ return 0;
+}
+
+
int
xfs_ioc_attr_list(
struct xfs_inode *dp,
@@ -378,7 +412,6 @@ xfs_ioc_attr_list(
struct xfs_attrlist_cursor __user *ucursor)
{
struct xfs_attr_list_context context = { };
- struct xfs_attrlist *alist;
void *buffer;
int error;
@@ -410,21 +443,10 @@ xfs_ioc_attr_list(
if (!buffer)
return -ENOMEM;
- /*
- * Initialize the output buffer.
- */
- context.dp = dp;
- context.resynch = 1;
- context.attr_filter = xfs_attr_filter(flags);
- context.buffer = buffer;
- context.bufsize = round_down(bufsize, sizeof(uint32_t));
- context.firstu = context.bufsize;
- context.put_listent = xfs_ioc_attr_put_listent;
-
- alist = context.buffer;
- alist->al_count = 0;
- alist->al_more = 0;
- alist->al_offset[0] = context.bufsize;
+ error = xfs_ioc_attr_list_context_init(dp, buffer, bufsize, flags,
+ &context);
+ if (error)
+ return error;
error = xfs_attr_list(&context);
if (error)
diff --git a/fs/xfs/xfs_ioctl.h b/fs/xfs/xfs_ioctl.h
index 38be600b5e1e..955a67261dc5 100644
--- a/fs/xfs/xfs_ioctl.h
+++ b/fs/xfs/xfs_ioctl.h
@@ -35,6 +35,8 @@ int xfs_ioc_attrmulti_one(struct file *parfilp, struct inode *inode,
int xfs_ioc_attr_list(struct xfs_inode *dp, void __user *ubuf,
size_t bufsize, int flags,
struct xfs_attrlist_cursor __user *ucursor);
+int xfs_ioc_attr_list_context_init(struct xfs_inode *dp, char *buffer,
+ int bufsize, int flags, struct xfs_attr_list_context *context);
extern struct dentry *
xfs_handle_to_dentry(
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 16/23] xfs: Filter XFS_ATTR_PARENT for getfattr
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (14 preceding siblings ...)
2023-04-06 19:24 ` [PATCH 15/23] xfs: Add helper function xfs_attr_list_context_init Darrick J. Wong
@ 2023-04-06 19:24 ` Darrick J. Wong
2023-04-06 19:24 ` [PATCH 17/23] xfs: pass the attr value to put_listent when possible Darrick J. Wong
` (6 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:24 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Parent pointers returned to the get_fattr tool cause errors since
the tool cannot parse parent pointers. Fix this by filtering parent
parent pointers from xfs_xattr_put_listent.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/xfs_xattr.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
index ef5a635238e5..c0c27c0edb33 100644
--- a/fs/xfs/xfs_xattr.c
+++ b/fs/xfs/xfs_xattr.c
@@ -239,6 +239,9 @@ xfs_xattr_put_listent(
ASSERT(context->count >= 0);
+ if (flags & XFS_ATTR_PARENT)
+ return;
+
if (flags & XFS_ATTR_ROOT) {
#ifdef CONFIG_XFS_POSIX_ACL
if (namelen == SGI_ACL_FILE_SIZE &&
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 17/23] xfs: pass the attr value to put_listent when possible
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (15 preceding siblings ...)
2023-04-06 19:24 ` [PATCH 16/23] xfs: Filter XFS_ATTR_PARENT for getfattr Darrick J. Wong
@ 2023-04-06 19:24 ` Darrick J. Wong
2023-04-06 19:25 ` [PATCH 18/23] xfs: add a libxfs header file for staging new ioctls Darrick J. Wong
` (5 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:24 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Pass the attr value to put_listent when we have local xattrs or
shortform xattrs.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
fs/xfs/libxfs/xfs_attr.h | 5 +++--
fs/xfs/libxfs/xfs_attr_sf.h | 1 +
fs/xfs/scrub/attr.c | 8 ++++++++
fs/xfs/xfs_attr_list.c | 8 +++++++-
fs/xfs/xfs_ioctl.c | 1 +
fs/xfs/xfs_xattr.c | 1 +
6 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h
index 3ad1f12a511a..0185d29d5109 100644
--- a/fs/xfs/libxfs/xfs_attr.h
+++ b/fs/xfs/libxfs/xfs_attr.h
@@ -47,8 +47,9 @@ struct xfs_attrlist_cursor_kern {
/* void; state communicated via *context */
-typedef void (*put_listent_func_t)(struct xfs_attr_list_context *, int,
- unsigned char *, int, int);
+typedef void (*put_listent_func_t)(struct xfs_attr_list_context *context,
+ int flags, unsigned char *name, int namelen, void *value,
+ int valuelen);
struct xfs_attr_list_context {
struct xfs_trans *tp;
diff --git a/fs/xfs/libxfs/xfs_attr_sf.h b/fs/xfs/libxfs/xfs_attr_sf.h
index 37578b369d9b..c6e259791bc3 100644
--- a/fs/xfs/libxfs/xfs_attr_sf.h
+++ b/fs/xfs/libxfs/xfs_attr_sf.h
@@ -24,6 +24,7 @@ typedef struct xfs_attr_sf_sort {
uint8_t flags; /* flags bits (see xfs_attr_leaf.h) */
xfs_dahash_t hash; /* this entry's hash value */
unsigned char *name; /* name value, pointer into buffer */
+ void *value;
} xfs_attr_sf_sort_t;
#define XFS_ATTR_SF_ENTSIZE_MAX /* max space for name&value */ \
diff --git a/fs/xfs/scrub/attr.c b/fs/xfs/scrub/attr.c
index 2a79a13cb600..00682006d0d3 100644
--- a/fs/xfs/scrub/attr.c
+++ b/fs/xfs/scrub/attr.c
@@ -109,6 +109,7 @@ xchk_xattr_listent(
int flags,
unsigned char *name,
int namelen,
+ void *value,
int valuelen)
{
struct xchk_xattr *sx;
@@ -134,6 +135,13 @@ xchk_xattr_listent(
return;
}
+ /*
+ * Shortform and local attrs don't require external lookups to retrieve
+ * the value, so there's nothing else to check here.
+ */
+ if (value)
+ return;
+
/*
* Try to allocate enough memory to extrat the attr value. If that
* doesn't work, we overload the seen_enough variable to convey
diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c
index a51f7f13a352..8e3891b96736 100644
--- a/fs/xfs/xfs_attr_list.c
+++ b/fs/xfs/xfs_attr_list.c
@@ -94,6 +94,7 @@ xfs_attr_shortform_list(
sfe->flags,
sfe->nameval,
(int)sfe->namelen,
+ &sfe->nameval[sfe->namelen],
(int)sfe->valuelen);
/*
* Either search callback finished early or
@@ -139,6 +140,7 @@ xfs_attr_shortform_list(
sbp->name = sfe->nameval;
sbp->namelen = sfe->namelen;
/* These are bytes, and both on-disk, don't endian-flip */
+ sbp->value = &sfe->nameval[sfe->namelen],
sbp->valuelen = sfe->valuelen;
sbp->flags = sfe->flags;
sfe = xfs_attr_sf_nextentry(sfe);
@@ -189,6 +191,7 @@ xfs_attr_shortform_list(
sbp->flags,
sbp->name,
sbp->namelen,
+ sbp->value,
sbp->valuelen);
if (context->seen_enough)
break;
@@ -443,6 +446,7 @@ xfs_attr3_leaf_list_int(
*/
for (; i < ichdr.count; entry++, i++) {
char *name;
+ void *value;
int namelen, valuelen;
if (be32_to_cpu(entry->hashval) != cursor->hashval) {
@@ -460,6 +464,7 @@ xfs_attr3_leaf_list_int(
name_loc = xfs_attr3_leaf_name_local(leaf, i);
name = name_loc->nameval;
namelen = name_loc->namelen;
+ value = &name_loc->nameval[name_loc->namelen];
valuelen = be16_to_cpu(name_loc->valuelen);
} else {
xfs_attr_leaf_name_remote_t *name_rmt;
@@ -467,6 +472,7 @@ xfs_attr3_leaf_list_int(
name_rmt = xfs_attr3_leaf_name_remote(leaf, i);
name = name_rmt->name;
namelen = name_rmt->namelen;
+ value = NULL;
valuelen = be32_to_cpu(name_rmt->valuelen);
}
@@ -475,7 +481,7 @@ xfs_attr3_leaf_list_int(
entry->flags)))
return -EFSCORRUPTED;
context->put_listent(context, entry->flags,
- name, namelen, valuelen);
+ name, namelen, value, valuelen);
if (context->seen_enough)
break;
cursor->offset++;
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 59987b95201c..9abf47efd076 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -307,6 +307,7 @@ xfs_ioc_attr_put_listent(
int flags,
unsigned char *name,
int namelen,
+ void *value,
int valuelen)
{
struct xfs_attrlist *alist = context->buffer;
diff --git a/fs/xfs/xfs_xattr.c b/fs/xfs/xfs_xattr.c
index c0c27c0edb33..46de0e2bfc46 100644
--- a/fs/xfs/xfs_xattr.c
+++ b/fs/xfs/xfs_xattr.c
@@ -232,6 +232,7 @@ xfs_xattr_put_listent(
int flags,
unsigned char *name,
int namelen,
+ void *value,
int valuelen)
{
char *prefix;
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 18/23] xfs: add a libxfs header file for staging new ioctls
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (16 preceding siblings ...)
2023-04-06 19:24 ` [PATCH 17/23] xfs: pass the attr value to put_listent when possible Darrick J. Wong
@ 2023-04-06 19:25 ` Darrick J. Wong
2023-04-06 19:25 ` [PATCH 19/23] xfs: Add parent pointer ioctl Darrick J. Wong
` (4 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:25 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Create a new xfs_fs_staging.h header where we can land experimental
ioctls without committing them to any stable interfaces anywhere.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_fs_staging.h | 18 ++++++++++++++++++
fs/xfs/xfs_linux.h | 1 +
2 files changed, 19 insertions(+)
create mode 100644 fs/xfs/libxfs/xfs_fs_staging.h
diff --git a/fs/xfs/libxfs/xfs_fs_staging.h b/fs/xfs/libxfs/xfs_fs_staging.h
new file mode 100644
index 000000000000..bc97193dde9d
--- /dev/null
+++ b/fs/xfs/libxfs/xfs_fs_staging.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+/*
+ * Copyright (C) 2020-2023 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __XFS_FS_STAGING_H__
+#define __XFS_FS_STAGING_H__
+
+/*
+ * Experimental system calls, ioctls and data structures supporting them.
+ * Nothing in here should be considered part of a stable interface of any kind.
+ *
+ * If you add an ioctl here, please leave a comment in xfs_fs.h marking it
+ * reserved. If you promote anything out of this file, please leave a comment
+ * explaining where it went.
+ */
+
+#endif /* __XFS_FS_STAGING_H__ */
diff --git a/fs/xfs/xfs_linux.h b/fs/xfs/xfs_linux.h
index e88f18f85e4b..4cd21a0b3043 100644
--- a/fs/xfs/xfs_linux.h
+++ b/fs/xfs/xfs_linux.h
@@ -72,6 +72,7 @@ typedef __u32 xfs_nlink_t;
#include <asm/unaligned.h>
#include "xfs_fs.h"
+#include "xfs_fs_staging.h"
#include "xfs_stats.h"
#include "xfs_sysctl.h"
#include "xfs_iops.h"
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 19/23] xfs: Add parent pointer ioctl
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (17 preceding siblings ...)
2023-04-06 19:25 ` [PATCH 18/23] xfs: add a libxfs header file for staging new ioctls Darrick J. Wong
@ 2023-04-06 19:25 ` Darrick J. Wong
2023-04-06 19:25 ` [PATCH 20/23] xfs: fix unit conversion error in xfs_log_calc_max_attrsetm_res Darrick J. Wong
` (3 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:25 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
This patch adds a new file ioctl to retrieve the parent pointer of a
given inode
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: move new ioctl to xfs_fs_staging.h, adjust to new ondisk format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/Makefile | 1
fs/xfs/libxfs/xfs_fs.h | 1
fs/xfs/libxfs/xfs_fs_staging.h | 66 +++++++++++++++++
fs/xfs/libxfs/xfs_parent.c | 40 ++++++++++
fs/xfs/libxfs/xfs_parent.h | 23 ++++++
fs/xfs/xfs_ioctl.c | 134 ++++++++++++++++++++++++++++++++++
fs/xfs/xfs_ondisk.h | 4 +
fs/xfs/xfs_parent_utils.c | 157 ++++++++++++++++++++++++++++++++++++++++
fs/xfs/xfs_parent_utils.h | 20 +++++
fs/xfs/xfs_trace.c | 1
fs/xfs/xfs_trace.h | 73 +++++++++++++++++++
11 files changed, 519 insertions(+), 1 deletion(-)
create mode 100644 fs/xfs/xfs_parent_utils.c
create mode 100644 fs/xfs/xfs_parent_utils.h
diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index afa9fd61924d..e0cdd9884a0d 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -87,6 +87,7 @@ xfs-y += xfs_aops.o \
xfs_mount.o \
xfs_mru_cache.o \
xfs_pwork.o \
+ xfs_parent_utils.o \
xfs_reflink.o \
xfs_stats.o \
xfs_super.o \
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 1cfd5bc6520a..390b8cbe2760 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -796,6 +796,7 @@ struct xfs_scrub_metadata {
/* XFS_IOC_GETFSMAP ------ hoisted 59 */
#define XFS_IOC_SCRUB_METADATA _IOWR('X', 60, struct xfs_scrub_metadata)
#define XFS_IOC_AG_GEOMETRY _IOWR('X', 61, struct xfs_ag_geometry)
+/* XFS_IOC_GETPARENTS ---- staging 62 */
/*
* ioctl commands that replace IRIX syssgi()'s
diff --git a/fs/xfs/libxfs/xfs_fs_staging.h b/fs/xfs/libxfs/xfs_fs_staging.h
index bc97193dde9d..fe11ad6f25d0 100644
--- a/fs/xfs/libxfs/xfs_fs_staging.h
+++ b/fs/xfs/libxfs/xfs_fs_staging.h
@@ -15,4 +15,70 @@
* explaining where it went.
*/
+/* Iterating parent pointers of files. */
+
+/* return parents of the handle, not the open fd */
+#define XFS_GETPARENTS_IFLAG_HANDLE (1U << 0)
+
+/* target was the root directory */
+#define XFS_GETPARENTS_OFLAG_ROOT (1U << 1)
+
+/* Cursor is done iterating pptrs */
+#define XFS_GETPARENTS_OFLAG_DONE (1U << 2)
+
+#define XFS_GETPARENTS_FLAG_ALL (XFS_GETPARENTS_IFLAG_HANDLE | \
+ XFS_GETPARENTS_OFLAG_ROOT | \
+ XFS_GETPARENTS_OFLAG_DONE)
+
+/* Get an inode parent pointer through ioctl */
+struct xfs_getparents_rec {
+ __u64 gpr_ino; /* Inode number */
+ __u32 gpr_gen; /* Inode generation */
+ __u32 gpr_pad; /* Reserved */
+ __u64 gpr_rsvd; /* Reserved */
+ __u8 gpr_name[]; /* File name and null terminator */
+};
+
+/* Iterate through an inodes parent pointers */
+struct xfs_getparents {
+ /* File handle, if XFS_GETPARENTS_IFLAG_HANDLE is set */
+ struct xfs_handle gp_handle;
+
+ /*
+ * Structure to track progress in iterating the parent pointers.
+ * Must be initialized to zeroes before the first ioctl call, and
+ * not touched by callers after that.
+ */
+ struct xfs_attrlist_cursor gp_cursor;
+
+ /* Operational flags: XFS_GETPARENTS_*FLAG* */
+ __u32 gp_flags;
+
+ /* Must be set to zero */
+ __u32 gp_reserved;
+
+ /* Size of the buffer in bytes, including this header */
+ __u32 gp_bufsize;
+
+ /* # of entries filled in (output) */
+ __u32 gp_count;
+
+ /* Must be set to zero */
+ __u64 gp_reserved2[5];
+
+ /* Byte offset of each record within the buffer */
+ __u32 gp_offsets[];
+};
+
+static inline struct xfs_getparents_rec*
+xfs_getparents_rec(
+ struct xfs_getparents *info,
+ unsigned int idx)
+{
+ return (struct xfs_getparents_rec *)((char *)info +
+ info->gp_offsets[idx]);
+}
+
+#define XFS_IOC_GETPARENTS _IOWR('X', 62, struct xfs_getparents)
+
#endif /* __XFS_FS_STAGING_H__ */
diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c
index 977412b8f9f3..37a3e73049d9 100644
--- a/fs/xfs/libxfs/xfs_parent.c
+++ b/fs/xfs/libxfs/xfs_parent.c
@@ -275,3 +275,43 @@ xfs_parent_calc_space_res(
return XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK) +
XFS_NEXTENTADD_SPACE_RES(mp, namelen, XFS_ATTR_FORK);
}
+
+/* Convert an ondisk parent pointer to the incore format. */
+void
+xfs_parent_irec_from_disk(
+ struct xfs_parent_name_irec *irec,
+ const struct xfs_parent_name_rec *rec,
+ const void *value,
+ unsigned int valuelen)
+{
+ irec->p_ino = be64_to_cpu(rec->p_ino);
+ irec->p_gen = be32_to_cpu(rec->p_gen);
+ irec->p_namehash = be32_to_cpu(rec->p_namehash);
+ irec->p_namelen = valuelen;
+ memcpy(irec->p_name, value, valuelen);
+}
+
+/* Convert an incore parent pointer to the ondisk attr name format. */
+void
+xfs_parent_irec_to_disk(
+ struct xfs_parent_name_rec *rec,
+ const struct xfs_parent_name_irec *irec)
+{
+ rec->p_ino = cpu_to_be64(irec->p_ino);
+ rec->p_gen = cpu_to_be32(irec->p_gen);
+ rec->p_namehash = cpu_to_be32(irec->p_namehash);
+}
+
+/* Compute p_namehash for the this parent pointer. */
+void
+xfs_parent_irec_hashname(
+ struct xfs_mount *mp,
+ struct xfs_parent_name_irec *irec)
+{
+ struct xfs_name dname = {
+ .name = irec->p_name,
+ .len = irec->p_namelen,
+ };
+
+ irec->p_namehash = xfs_dir2_hashname(mp, &dname);
+}
diff --git a/fs/xfs/libxfs/xfs_parent.h b/fs/xfs/libxfs/xfs_parent.h
index f4a0793bc4fc..1cc4778968df 100644
--- a/fs/xfs/libxfs/xfs_parent.h
+++ b/fs/xfs/libxfs/xfs_parent.h
@@ -78,4 +78,27 @@ xfs_parent_finish(
unsigned int xfs_parent_calc_space_res(struct xfs_mount *mp,
unsigned int namelen);
+/*
+ * Incore version of a parent pointer, also contains dirent name so callers
+ * can pass/obtain all the parent pointer information in a single structure
+ */
+struct xfs_parent_name_irec {
+ /* Parent pointer attribute name fields */
+ xfs_ino_t p_ino;
+ uint32_t p_gen;
+ xfs_dahash_t p_namehash;
+
+ /* Parent pointer attribute value fields */
+ uint8_t p_namelen;
+ unsigned char p_name[MAXNAMELEN];
+};
+
+void xfs_parent_irec_from_disk(struct xfs_parent_name_irec *irec,
+ const struct xfs_parent_name_rec *rec, const void *value,
+ unsigned int valuelen);
+void xfs_parent_irec_to_disk(struct xfs_parent_name_rec *rec,
+ const struct xfs_parent_name_irec *irec);
+void xfs_parent_irec_hashname(struct xfs_mount *mp,
+ struct xfs_parent_name_irec *irec);
+
#endif /* __XFS_PARENT_H__ */
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 9abf47efd076..f265e28d0611 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -37,6 +37,7 @@
#include "xfs_health.h"
#include "xfs_reflink.h"
#include "xfs_ioctl.h"
+#include "xfs_parent_utils.h"
#include "xfs_xattr.h"
#include <linux/mount.h>
@@ -1676,6 +1677,136 @@ xfs_ioc_scrub_metadata(
return 0;
}
+/*
+ * IOCTL routine to get the parent pointers of an inode and return it to user
+ * space. Caller must pass a buffer space containing a struct xfs_getparents,
+ * followed by a region large enough to contain an array of struct
+ * xfs_getparents_rec of a size specified in gp_bufsize. If the inode contains
+ * more parent pointers than can fit in the buffer space, caller may re-call
+ * the function using the returned gp_cursor to resume iteration. The
+ * number of xfs_getparents_rec returned will be stored in gp_count.
+ *
+ * Returns 0 on success or non-zero on failure
+ */
+STATIC int
+xfs_ioc_get_parent_pointer(
+ struct file *filp,
+ void __user *arg)
+{
+ struct xfs_getparents *ppi = NULL;
+ int error = 0;
+ struct xfs_inode *file_ip = XFS_I(file_inode(filp));
+ struct xfs_inode *call_ip = file_ip;
+ struct xfs_mount *mp = file_ip->i_mount;
+ void __user *o_pptr;
+ struct xfs_getparents_rec *i_pptr;
+ unsigned int bytes;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ /* Allocate an xfs_getparents to put the user data */
+ ppi = kvmalloc(sizeof(struct xfs_getparents), GFP_KERNEL);
+ if (!ppi)
+ return -ENOMEM;
+
+ /* Copy the data from the user */
+ error = copy_from_user(ppi, arg, sizeof(struct xfs_getparents));
+ if (error) {
+ error = -EFAULT;
+ goto out;
+ }
+
+ /* Check size of buffer requested by user */
+ if (ppi->gp_bufsize > XFS_XATTR_LIST_MAX) {
+ error = -ENOMEM;
+ goto out;
+ }
+ if (ppi->gp_bufsize < sizeof(struct xfs_getparents)) {
+ error = -EINVAL;
+ goto out;
+ }
+
+ if (ppi->gp_flags & ~XFS_GETPARENTS_FLAG_ALL) {
+ error = -EINVAL;
+ goto out;
+ }
+ ppi->gp_flags &= ~(XFS_GETPARENTS_OFLAG_ROOT | XFS_GETPARENTS_OFLAG_DONE);
+
+ /*
+ * Now that we know how big the trailing buffer is, expand
+ * our kernel xfs_getparents to be the same size
+ */
+ ppi = kvrealloc(ppi, sizeof(struct xfs_getparents), ppi->gp_bufsize,
+ GFP_KERNEL | __GFP_ZERO);
+ if (!ppi)
+ return -ENOMEM;
+
+ if (ppi->gp_flags & XFS_GETPARENTS_IFLAG_HANDLE) {
+ struct xfs_handle *hanp = &ppi->gp_handle;
+
+ if (memcmp(&hanp->ha_fsid, mp->m_fixedfsid,
+ sizeof(xfs_fsid_t))) {
+ error = -EINVAL;
+ goto out;
+ }
+
+ if (hanp->ha_fid.fid_ino != file_ip->i_ino) {
+ error = xfs_iget(mp, NULL, hanp->ha_fid.fid_ino,
+ XFS_IGET_UNTRUSTED, 0, &call_ip);
+ if (error)
+ goto out;
+ }
+
+ if (VFS_I(call_ip)->i_generation != hanp->ha_fid.fid_gen) {
+ error = -EINVAL;
+ goto out;
+ }
+ }
+
+ /* Get the parent pointers */
+ error = xfs_getparent_pointers(call_ip, ppi);
+ if (error)
+ goto out;
+
+ /*
+ * If we ran out of buffer space before copying any parent pointers at
+ * all, the caller's buffer was too short. Tell userspace that, erm,
+ * the message is too long.
+ */
+ if (ppi->gp_count == 0 && !(ppi->gp_flags & XFS_GETPARENTS_OFLAG_DONE)) {
+ error = -EMSGSIZE;
+ goto out;
+ }
+
+ /* Copy the parent pointer head back to the user */
+ bytes = xfs_getparents_arraytop(ppi, ppi->gp_count);
+ error = copy_to_user(arg, ppi, bytes);
+ if (error) {
+ error = -EFAULT;
+ goto out;
+ }
+
+ if (ppi->gp_count == 0)
+ goto out;
+
+ /* Copy the parent pointer records back to the user. */
+ o_pptr = (__user char*)arg + ppi->gp_offsets[ppi->gp_count - 1];
+ i_pptr = xfs_getparents_rec(ppi, ppi->gp_count - 1);
+ bytes = ((char *)ppi + ppi->gp_bufsize) - (char *)i_pptr;
+ error = copy_to_user(o_pptr, i_pptr, bytes);
+ if (error) {
+ error = -EFAULT;
+ goto out;
+ }
+
+out:
+ if (call_ip != file_ip)
+ xfs_irele(call_ip);
+ kvfree(ppi);
+ return error;
+}
+
int
xfs_ioc_swapext(
xfs_swapext_t *sxp)
@@ -1965,7 +2096,8 @@ xfs_file_ioctl(
case XFS_IOC_FSGETXATTRA:
return xfs_ioc_fsgetxattra(ip, arg);
-
+ case XFS_IOC_GETPARENTS:
+ return xfs_ioc_get_parent_pointer(filp, arg);
case XFS_IOC_GETBMAP:
case XFS_IOC_GETBMAPA:
case XFS_IOC_GETBMAPX:
diff --git a/fs/xfs/xfs_ondisk.h b/fs/xfs/xfs_ondisk.h
index 9737b5a9f405..88f9ec393c3d 100644
--- a/fs/xfs/xfs_ondisk.h
+++ b/fs/xfs/xfs_ondisk.h
@@ -150,6 +150,10 @@ xfs_check_ondisk_structs(void)
XFS_CHECK_OFFSET(struct xfs_efi_log_format_32, efi_extents, 16);
XFS_CHECK_OFFSET(struct xfs_efi_log_format_64, efi_extents, 16);
+ /* parent pointer ioctls */
+ XFS_CHECK_STRUCT_SIZE(struct xfs_getparents_rec, 24);
+ XFS_CHECK_STRUCT_SIZE(struct xfs_getparents, 96);
+
/*
* The v5 superblock format extended several v4 header structures with
* additional data. While new fields are only accessible on v5
diff --git a/fs/xfs/xfs_parent_utils.c b/fs/xfs/xfs_parent_utils.c
new file mode 100644
index 000000000000..3617b81624bd
--- /dev/null
+++ b/fs/xfs/xfs_parent_utils.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2023 Oracle, Inc.
+ * All rights reserved.
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_shared.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_inode.h"
+#include "xfs_error.h"
+#include "xfs_trace.h"
+#include "xfs_trans.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr.h"
+#include "xfs_ioctl.h"
+#include "xfs_parent.h"
+#include "xfs_da_btree.h"
+#include "xfs_parent_utils.h"
+
+struct xfs_getparent_ctx {
+ struct xfs_attr_list_context context;
+ struct xfs_parent_name_irec pptr_irec;
+ struct xfs_getparents *ppi;
+};
+
+static inline unsigned int
+xfs_getparents_rec_sizeof(
+ const struct xfs_parent_name_irec *irec)
+{
+ return round_up(sizeof(struct xfs_getparents_rec) + irec->p_namelen + 1,
+ sizeof(uint32_t));
+}
+
+static void
+xfs_getparent_listent(
+ struct xfs_attr_list_context *context,
+ int flags,
+ unsigned char *name,
+ int namelen,
+ void *value,
+ int valuelen)
+{
+ struct xfs_getparent_ctx *gp;
+ struct xfs_getparents *ppi;
+ struct xfs_getparents_rec *pptr;
+ struct xfs_parent_name_rec *rec = (void *)name;
+ struct xfs_parent_name_irec *irec;
+ struct xfs_mount *mp = context->dp->i_mount;
+ int arraytop;
+
+ gp = container_of(context, struct xfs_getparent_ctx, context);
+ ppi = gp->ppi;
+ irec = &gp->pptr_irec;
+
+ /* Ignore non-parent xattrs */
+ if (!(flags & XFS_ATTR_PARENT))
+ return;
+
+ /*
+ * Report corruption for anything that doesn't look like a parent
+ * pointer. The attr list functions filtered out INCOMPLETE attrs.
+ */
+ if (XFS_IS_CORRUPT(mp,
+ !xfs_parent_namecheck(mp, rec, namelen, flags)) ||
+ XFS_IS_CORRUPT(mp,
+ !xfs_parent_valuecheck(mp, value, valuelen))) {
+ context->seen_enough = -EFSCORRUPTED;
+ return;
+ }
+
+ xfs_parent_irec_from_disk(&gp->pptr_irec, rec, value, valuelen);
+
+ /*
+ * We found a parent pointer, but we've filled up the buffer. Signal
+ * to the caller that we did /not/ reach the end of the parent pointer
+ * recordset.
+ */
+ arraytop = xfs_getparents_arraytop(ppi, ppi->gp_count + 1);
+ context->firstu -= xfs_getparents_rec_sizeof(irec);
+ if (context->firstu < arraytop) {
+ context->seen_enough = 1;
+ return;
+ }
+
+ trace_xfs_getparent_listent(context->dp, ppi, irec);
+
+ /* Format the parent pointer directly into the caller buffer. */
+ ppi->gp_offsets[ppi->gp_count] = context->firstu;
+ pptr = xfs_getparents_rec(ppi, ppi->gp_count);
+ pptr->gpr_ino = irec->p_ino;
+ pptr->gpr_gen = irec->p_gen;
+ pptr->gpr_pad = 0;
+ pptr->gpr_rsvd = 0;
+
+ memcpy(pptr->gpr_name, irec->p_name, irec->p_namelen);
+ pptr->gpr_name[irec->p_namelen] = 0;
+ ppi->gp_count++;
+}
+
+/* Retrieve the parent pointers for a given inode. */
+int
+xfs_getparent_pointers(
+ struct xfs_inode *ip,
+ struct xfs_getparents *ppi)
+{
+ struct xfs_getparent_ctx *gp;
+ int error;
+
+ gp = kzalloc(sizeof(struct xfs_getparent_ctx), GFP_KERNEL);
+ if (!gp)
+ return -ENOMEM;
+ gp->ppi = ppi;
+ gp->context.dp = ip;
+ gp->context.resynch = 1;
+ gp->context.put_listent = xfs_getparent_listent;
+ gp->context.bufsize = round_down(ppi->gp_bufsize, sizeof(uint32_t));
+ gp->context.firstu = gp->context.bufsize;
+
+ /* Copy the cursor provided by caller */
+ memcpy(&gp->context.cursor, &ppi->gp_cursor,
+ sizeof(struct xfs_attrlist_cursor));
+ ppi->gp_count = 0;
+
+ trace_xfs_getparent_pointers(ip, ppi, &gp->context.cursor);
+
+ error = xfs_attr_list(&gp->context);
+ if (error)
+ goto out_free;
+ if (gp->context.seen_enough < 0) {
+ error = gp->context.seen_enough;
+ goto out_free;
+ }
+
+ /* Is this the root directory? */
+ if (ip->i_ino == ip->i_mount->m_sb.sb_rootino)
+ ppi->gp_flags |= XFS_GETPARENTS_OFLAG_ROOT;
+
+ /*
+ * If we did not run out of buffer space, then we reached the end of
+ * the pptr recordset, so set the DONE flag.
+ */
+ if (gp->context.seen_enough == 0)
+ ppi->gp_flags |= XFS_GETPARENTS_OFLAG_DONE;
+
+ /* Update the caller with the current cursor position */
+ memcpy(&ppi->gp_cursor, &gp->context.cursor,
+ sizeof(struct xfs_attrlist_cursor));
+out_free:
+ kfree(gp);
+ return error;
+}
diff --git a/fs/xfs/xfs_parent_utils.h b/fs/xfs/xfs_parent_utils.h
new file mode 100644
index 000000000000..bd578761bc2f
--- /dev/null
+++ b/fs/xfs/xfs_parent_utils.h
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2023 Oracle, Inc.
+ * All rights reserved.
+ */
+#ifndef __XFS_PARENT_UTILS_H__
+#define __XFS_PARENT_UTILS_H__
+
+static inline unsigned int
+xfs_getparents_arraytop(
+ const struct xfs_getparents *ppi,
+ unsigned int nr)
+{
+ return sizeof(struct xfs_getparents) +
+ (nr * sizeof(ppi->gp_offsets[0]));
+}
+
+int xfs_getparent_pointers(struct xfs_inode *ip, struct xfs_getparents *ppi);
+
+#endif /* __XFS_PARENT_UTILS_H__ */
diff --git a/fs/xfs/xfs_trace.c b/fs/xfs/xfs_trace.c
index 8a5dc1538aa8..c1f339481697 100644
--- a/fs/xfs/xfs_trace.c
+++ b/fs/xfs/xfs_trace.c
@@ -36,6 +36,7 @@
#include "xfs_error.h"
#include <linux/iomap.h>
#include "xfs_iomap.h"
+#include "xfs_parent.h"
/*
* We include this last to have the helpers above available for the trace
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 64df29414440..31f7871908dd 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -75,6 +75,9 @@ union xfs_btree_ptr;
struct xfs_dqtrx;
struct xfs_icwalk;
struct xfs_perag;
+struct xfs_getparents;
+struct xfs_parent_name_irec;
+struct xfs_attrlist_cursor_kern;
#define XFS_ATTR_FILTER_FLAGS \
{ XFS_ATTR_ROOT, "ROOT" }, \
@@ -4326,6 +4329,76 @@ TRACE_EVENT(xfs_force_shutdown,
__entry->line_num)
);
+TRACE_EVENT(xfs_getparent_listent,
+ TP_PROTO(struct xfs_inode *ip, const struct xfs_getparents *ppi,
+ const struct xfs_parent_name_irec *irec),
+ TP_ARGS(ip, ppi, irec),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(xfs_ino_t, ino)
+ __field(unsigned int, count)
+ __field(unsigned int, bufsize)
+ __field(xfs_ino_t, parent_ino)
+ __field(unsigned int, parent_gen)
+ __field(unsigned int, namelen)
+ __dynamic_array(char, name, irec->p_namelen)
+ ),
+ TP_fast_assign(
+ __entry->dev = ip->i_mount->m_super->s_dev;
+ __entry->ino = ip->i_ino;
+ __entry->count = ppi->gp_count;
+ __entry->bufsize = ppi->gp_bufsize;
+ __entry->parent_ino = irec->p_ino;
+ __entry->parent_gen = irec->p_gen;
+ __entry->namelen = irec->p_namelen;
+ memcpy(__get_str(name), irec->p_name, irec->p_namelen);
+ ),
+ TP_printk("dev %d:%d ino 0x%llx bufsize %u count %u: parent_ino 0x%llx parent_gen 0x%x name '%.*s'",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->ino,
+ __entry->bufsize,
+ __entry->count,
+ __entry->parent_ino,
+ __entry->parent_gen,
+ __entry->namelen,
+ __get_str(name))
+);
+
+TRACE_EVENT(xfs_getparent_pointers,
+ TP_PROTO(struct xfs_inode *ip, const struct xfs_getparents *ppi,
+ const struct xfs_attrlist_cursor_kern *cur),
+ TP_ARGS(ip, ppi, cur),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(xfs_ino_t, ino)
+ __field(unsigned int, flags)
+ __field(unsigned int, bufsize)
+ __field(unsigned int, hashval)
+ __field(unsigned int, blkno)
+ __field(unsigned int, offset)
+ __field(int, initted)
+ ),
+ TP_fast_assign(
+ __entry->dev = ip->i_mount->m_super->s_dev;
+ __entry->ino = ip->i_ino;
+ __entry->flags = ppi->gp_flags;
+ __entry->bufsize = ppi->gp_bufsize;
+ __entry->hashval = cur->hashval;
+ __entry->blkno = cur->blkno;
+ __entry->offset = cur->offset;
+ __entry->initted = cur->initted;
+ ),
+ TP_printk("dev %d:%d ino 0x%llx flags 0x%x bufsize %u cur_init? %d hashval 0x%x blkno %u offset %u",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->ino,
+ __entry->flags,
+ __entry->bufsize,
+ __entry->initted,
+ __entry->hashval,
+ __entry->blkno,
+ __entry->offset)
+);
+
#endif /* _TRACE_XFS_H */
#undef TRACE_INCLUDE_PATH
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 20/23] xfs: fix unit conversion error in xfs_log_calc_max_attrsetm_res
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (18 preceding siblings ...)
2023-04-06 19:25 ` [PATCH 19/23] xfs: Add parent pointer ioctl Darrick J. Wong
@ 2023-04-06 19:25 ` Darrick J. Wong
2023-04-06 19:25 ` [PATCH 21/23] xfs: drop compatibility minimum log size computations for reflink Darrick J. Wong
` (2 subsequent siblings)
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:25 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Dave and I were discussing some recent test regressions as a result of
me turning on nrext64=1 on realtime filesystems, when we noticed that
the minimum log size of a 32M filesystem jumped from 954 blocks to 4287
blocks.
Digging through xfs_log_calc_max_attrsetm_res, Dave noticed that @size
contains the maximum estimated amount of space needed for a local format
xattr, in bytes, but we feed this quantity to XFS_NEXTENTADD_SPACE_RES,
which requires units of blocks. This has resulted in an overestimation
of the minimum log size over the years.
We should nominally correct this, but there's a backwards compatibility
problem -- if we enable it now, the minimum log size will decrease. If
a corrected mkfs formats a filesystem with this new smaller log size, a
user will encounter mount failures on an uncorrected kernel due to the
larger minimum log size computations there.
However, the large extent counters feature is still EXPERIMENTAL, so we
can gate the correction on that feature (or any features that get added
after that) being enabled. Any filesystem with nrext64 or any of the
as-yet-undefined feature bits turned on will be rejected by old
uncorrected kernels, so this should be safe even in the upgrade case.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
---
fs/xfs/libxfs/xfs_log_rlimit.c | 43 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/fs/xfs/libxfs/xfs_log_rlimit.c b/fs/xfs/libxfs/xfs_log_rlimit.c
index 9975b93a7412..e5c606fb7a6a 100644
--- a/fs/xfs/libxfs/xfs_log_rlimit.c
+++ b/fs/xfs/libxfs/xfs_log_rlimit.c
@@ -16,6 +16,39 @@
#include "xfs_bmap_btree.h"
#include "xfs_trace.h"
+/*
+ * Decide if the filesystem has the parent pointer feature or any feature
+ * added after that.
+ */
+static inline bool
+xfs_has_parent_or_newer_feature(
+ struct xfs_mount *mp)
+{
+ if (!xfs_sb_is_v5(&mp->m_sb))
+ return false;
+
+ if (xfs_sb_has_compat_feature(&mp->m_sb, ~0))
+ return true;
+
+ if (xfs_sb_has_ro_compat_feature(&mp->m_sb,
+ ~(XFS_SB_FEAT_RO_COMPAT_FINOBT |
+ XFS_SB_FEAT_RO_COMPAT_RMAPBT |
+ XFS_SB_FEAT_RO_COMPAT_REFLINK |
+ XFS_SB_FEAT_RO_COMPAT_INOBTCNT)))
+ return true;
+
+ if (xfs_sb_has_incompat_feature(&mp->m_sb,
+ ~(XFS_SB_FEAT_INCOMPAT_FTYPE |
+ XFS_SB_FEAT_INCOMPAT_SPINODES |
+ XFS_SB_FEAT_INCOMPAT_META_UUID |
+ XFS_SB_FEAT_INCOMPAT_BIGTIME |
+ XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR |
+ XFS_SB_FEAT_INCOMPAT_NREXT64)))
+ return true;
+
+ return false;
+}
+
/*
* Calculate the maximum length in bytes that would be required for a local
* attribute value as large attributes out of line are not logged.
@@ -31,6 +64,16 @@ xfs_log_calc_max_attrsetm_res(
MAXNAMELEN - 1;
nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK);
nblks += XFS_B_TO_FSB(mp, size);
+
+ /*
+ * Starting with the parent pointer feature, every new fs feature
+ * corrects a unit conversion error in the xattr transaction
+ * reservation code that resulted in oversized minimum log size
+ * computations.
+ */
+ if (xfs_has_parent_or_newer_feature(mp))
+ size = XFS_B_TO_FSB(mp, size);
+
nblks += XFS_NEXTENTADD_SPACE_RES(mp, size, XFS_ATTR_FORK);
return M_RES(mp)->tr_attrsetm.tr_logres +
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 21/23] xfs: drop compatibility minimum log size computations for reflink
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (19 preceding siblings ...)
2023-04-06 19:25 ` [PATCH 20/23] xfs: fix unit conversion error in xfs_log_calc_max_attrsetm_res Darrick J. Wong
@ 2023-04-06 19:25 ` Darrick J. Wong
2023-04-06 19:26 ` [PATCH 22/23] xfs: don't remove the attr fork when parent pointers are enabled Darrick J. Wong
2023-04-06 19:26 ` [PATCH 23/23] xfs: Add the parent pointer support to the superblock version 5 Darrick J. Wong
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:25 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Having established that we can reduce the minimum log size computation
for filesystems with parent pointers or any newer feature, we should
also drop the compat minlogsize code that we added when we reduced the
transaction reservation size for rmap and reflink.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
---
fs/xfs/libxfs/xfs_log_rlimit.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/fs/xfs/libxfs/xfs_log_rlimit.c b/fs/xfs/libxfs/xfs_log_rlimit.c
index e5c606fb7a6a..74821c7fd0cc 100644
--- a/fs/xfs/libxfs/xfs_log_rlimit.c
+++ b/fs/xfs/libxfs/xfs_log_rlimit.c
@@ -91,6 +91,16 @@ xfs_log_calc_trans_resv_for_minlogblocks(
{
unsigned int rmap_maxlevels = mp->m_rmap_maxlevels;
+ /*
+ * Starting with the parent pointer feature, every new fs feature
+ * drops the oversized minimum log size computation introduced by the
+ * original reflink code.
+ */
+ if (xfs_has_parent_or_newer_feature(mp)) {
+ xfs_trans_resv_calc(mp, resv);
+ return;
+ }
+
/*
* In the early days of rmap+reflink, we always set the rmap maxlevels
* to 9 even if the AG was small enough that it would never grow to
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 22/23] xfs: don't remove the attr fork when parent pointers are enabled
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (20 preceding siblings ...)
2023-04-06 19:25 ` [PATCH 21/23] xfs: drop compatibility minimum log size computations for reflink Darrick J. Wong
@ 2023-04-06 19:26 ` Darrick J. Wong
2023-04-06 19:26 ` [PATCH 23/23] xfs: Add the parent pointer support to the superblock version 5 Darrick J. Wong
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:26 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
When an inode is removed, it may also cause the attribute fork to be
removed if it is the last attribute. This transaction gets flushed to
the log, but if the system goes down before we could inactivate the symlink,
the log recovery tries to inactivate this inode (since it is on the unlinked
list) but the verifier trips over the remote value and leaks it.
Hence we ended up with a file in this odd state on a "clean" mount. The
"obvious" fix is to prohibit erasure of the attr fork to avoid tripping
over the verifiers when pptrs are enabled.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
fs/xfs/libxfs/xfs_attr_leaf.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index c38caa8cba3a..37d76e855edf 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -878,7 +878,8 @@ xfs_attr_sf_removename(
totsize -= size;
if (totsize == sizeof(xfs_attr_sf_hdr_t) && xfs_has_attr2(mp) &&
(dp->i_df.if_format != XFS_DINODE_FMT_BTREE) &&
- !(args->op_flags & (XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE))) {
+ !(args->op_flags & (XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE)) &&
+ !xfs_has_parent(mp)) {
xfs_attr_fork_remove(dp, args->trans);
} else {
xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
@@ -887,7 +888,8 @@ xfs_attr_sf_removename(
ASSERT(totsize > sizeof(xfs_attr_sf_hdr_t) ||
(args->op_flags & XFS_DA_OP_ADDNAME) ||
!xfs_has_attr2(mp) ||
- dp->i_df.if_format == XFS_DINODE_FMT_BTREE);
+ dp->i_df.if_format == XFS_DINODE_FMT_BTREE ||
+ xfs_has_parent(mp));
xfs_trans_log_inode(args->trans, dp,
XFS_ILOG_CORE | XFS_ILOG_ADATA);
}
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 23/23] xfs: Add the parent pointer support to the superblock version 5.
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
` (21 preceding siblings ...)
2023-04-06 19:26 ` [PATCH 22/23] xfs: don't remove the attr fork when parent pointers are enabled Darrick J. Wong
@ 2023-04-06 19:26 ` Darrick J. Wong
22 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:26 UTC (permalink / raw)
To: djwong
Cc: Mark Tinguely, Dave Chinner, Allison Henderson, Darrick J. Wong,
allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Add the parent pointer superblock flag so that we can actually mount
filesystems with this feature enabled.
Signed-off-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
---
fs/xfs/libxfs/xfs_format.h | 4 +++-
fs/xfs/libxfs/xfs_fs.h | 1 +
fs/xfs/libxfs/xfs_sb.c | 4 ++++
fs/xfs/xfs_super.c | 4 ++++
4 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h
index 371dc07233e0..f413819b2a8a 100644
--- a/fs/xfs/libxfs/xfs_format.h
+++ b/fs/xfs/libxfs/xfs_format.h
@@ -373,13 +373,15 @@ xfs_sb_has_ro_compat_feature(
#define XFS_SB_FEAT_INCOMPAT_BIGTIME (1 << 3) /* large timestamps */
#define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4) /* needs xfs_repair */
#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */
+#define XFS_SB_FEAT_INCOMPAT_PARENT (1 << 6) /* parent pointers */
#define XFS_SB_FEAT_INCOMPAT_ALL \
(XFS_SB_FEAT_INCOMPAT_FTYPE| \
XFS_SB_FEAT_INCOMPAT_SPINODES| \
XFS_SB_FEAT_INCOMPAT_META_UUID| \
XFS_SB_FEAT_INCOMPAT_BIGTIME| \
XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR| \
- XFS_SB_FEAT_INCOMPAT_NREXT64)
+ XFS_SB_FEAT_INCOMPAT_NREXT64| \
+ XFS_SB_FEAT_INCOMPAT_PARENT)
#define XFS_SB_FEAT_INCOMPAT_UNKNOWN ~XFS_SB_FEAT_INCOMPAT_ALL
static inline bool
diff --git a/fs/xfs/libxfs/xfs_fs.h b/fs/xfs/libxfs/xfs_fs.h
index 390b8cbe2760..fc45b0e258a1 100644
--- a/fs/xfs/libxfs/xfs_fs.h
+++ b/fs/xfs/libxfs/xfs_fs.h
@@ -237,6 +237,7 @@ typedef struct xfs_fsop_resblks {
#define XFS_FSOP_GEOM_FLAGS_BIGTIME (1 << 21) /* 64-bit nsec timestamps */
#define XFS_FSOP_GEOM_FLAGS_INOBTCNT (1 << 22) /* inobt btree counter */
#define XFS_FSOP_GEOM_FLAGS_NREXT64 (1 << 23) /* large extent counters */
+#define XFS_FSOP_GEOM_FLAGS_PARENT (1 << 24) /* parent pointers */
/*
* Minimum and maximum sizes need for growth checks.
diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c
index 99cc03a298e2..c47748b95987 100644
--- a/fs/xfs/libxfs/xfs_sb.c
+++ b/fs/xfs/libxfs/xfs_sb.c
@@ -173,6 +173,8 @@ xfs_sb_version_to_features(
features |= XFS_FEAT_NEEDSREPAIR;
if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_NREXT64)
features |= XFS_FEAT_NREXT64;
+ if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_PARENT)
+ features |= XFS_FEAT_PARENT;
return features;
}
@@ -1190,6 +1192,8 @@ xfs_fs_geometry(
geo->flags |= XFS_FSOP_GEOM_FLAGS_BIGTIME;
if (xfs_has_inobtcounts(mp))
geo->flags |= XFS_FSOP_GEOM_FLAGS_INOBTCNT;
+ if (xfs_has_parent(mp))
+ geo->flags |= XFS_FSOP_GEOM_FLAGS_PARENT;
if (xfs_has_sector(mp)) {
geo->flags |= XFS_FSOP_GEOM_FLAGS_SECTOR;
geo->logsectsize = sbp->sb_logsectsize;
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 95b906a14698..62d87194d632 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -1675,6 +1675,10 @@ xfs_fs_fill_super(
xfs_warn(mp,
"EXPERIMENTAL Large extent counts feature in use. Use at your own risk!");
+ if (xfs_has_parent(mp))
+ xfs_alert(mp,
+ "EXPERIMENTAL parent pointer feature enabled. Use at your own risk!");
+
error = xfs_mountfs(mp);
if (error)
goto out_filestream_unmount;
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 1/3] xfs: reconstruct directories from parent pointers
2023-04-06 19:14 ` [PATCHSET v11 0/3] xfs: online repair of directories Darrick J. Wong
@ 2023-04-06 19:26 ` Darrick J. Wong
2023-04-06 19:26 ` [PATCH 2/3] xfs: add hooks to do directory updates Darrick J. Wong
2023-04-06 19:27 ` [PATCH 3/3] xfs: compare generated and existing dirents Darrick J. Wong
2 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:26 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Use the filesystem scanning infrastructure to walk the filesystem
looking for parent pointers and child dirents that reference the
directory that we're rebuilding.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/Makefile | 1
fs/xfs/scrub/common.c | 15 +
fs/xfs/scrub/common.h | 28 +
fs/xfs/scrub/dir.c | 9
fs/xfs/scrub/dir_repair.c | 964 +++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/scrub/repair.h | 16 +
fs/xfs/scrub/scrub.c | 2
fs/xfs/scrub/tempfile.c | 42 ++
fs/xfs/scrub/tempfile.h | 2
fs/xfs/scrub/trace.c | 1
fs/xfs/scrub/trace.h | 64 +++
11 files changed, 1143 insertions(+), 1 deletion(-)
create mode 100644 fs/xfs/scrub/dir_repair.c
diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index e03dd935c8e8..0a83cd9585d1 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -179,6 +179,7 @@ xfs-$(CONFIG_XFS_QUOTA) += scrub/quota.o
ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y)
xfs-y += $(addprefix scrub/, \
agheader_repair.o \
+ dir_repair.o \
repair.o \
tempfile.o \
xfblob.o \
diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c
index 729d8f66909e..757b741fdf21 100644
--- a/fs/xfs/scrub/common.c
+++ b/fs/xfs/scrub/common.c
@@ -551,6 +551,21 @@ xchk_ag_init(
/* Per-scrubber setup functions */
+void
+xchk_trans_cancel(
+ struct xfs_scrub *sc)
+{
+ xfs_trans_cancel(sc->tp);
+ sc->tp = NULL;
+}
+
+int
+xchk_trans_alloc_empty(
+ struct xfs_scrub *sc)
+{
+ return xfs_trans_alloc_empty(sc->mp, &sc->tp);
+}
+
/*
* Grab an empty transaction so that we can re-grab locked buffers if
* one of our btrees turns out to be cyclic.
diff --git a/fs/xfs/scrub/common.h b/fs/xfs/scrub/common.h
index 423a98c39fb6..7720982adfc6 100644
--- a/fs/xfs/scrub/common.h
+++ b/fs/xfs/scrub/common.h
@@ -31,6 +31,9 @@ xchk_should_terminate(
return false;
}
+void xchk_trans_cancel(struct xfs_scrub *sc);
+int xchk_trans_alloc_empty(struct xfs_scrub *sc);
+
int xchk_trans_alloc(struct xfs_scrub *sc, uint resblks);
bool xchk_process_error(struct xfs_scrub *sc, xfs_agnumber_t agno,
xfs_agblock_t bno, int *error);
@@ -159,4 +162,29 @@ void xchk_start_reaping(struct xfs_scrub *sc);
void xchk_fshooks_enable(struct xfs_scrub *sc, unsigned int scrub_fshooks);
+#ifdef CONFIG_XFS_ONLINE_REPAIR
+/* Decide if a repair is required. */
+static inline bool xchk_needs_repair(const struct xfs_scrub_metadata *sm)
+{
+ return sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT |
+ XFS_SCRUB_OFLAG_XCORRUPT |
+ XFS_SCRUB_OFLAG_PREEN);
+}
+
+/*
+ * "Should we prepare for a repair?"
+ *
+ * Return true if the caller permits us to repair metadata and we're not
+ * setting up for a post-repair evaluation.
+ */
+static inline bool xchk_could_repair(const struct xfs_scrub *sc)
+{
+ return (sc->sm->sm_flags & XFS_SCRUB_IFLAG_REPAIR) &&
+ !(sc->flags & XREP_ALREADY_FIXED);
+}
+#else
+# define xchk_needs_repair(sc) (false)
+# define xchk_could_repair(sc) (false)
+#endif /* CONFIG_XFS_ONLINE_REPAIR */
+
#endif /* __XFS_SCRUB_COMMON_H__ */
diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c
index 46080134b408..e30624dc35b3 100644
--- a/fs/xfs/scrub/dir.c
+++ b/fs/xfs/scrub/dir.c
@@ -19,12 +19,21 @@
#include "scrub/common.h"
#include "scrub/dabtree.h"
#include "scrub/readdir.h"
+#include "scrub/repair.h"
/* Set us up to scrub directories. */
int
xchk_setup_directory(
struct xfs_scrub *sc)
{
+ int error;
+
+ if (xchk_could_repair(sc)) {
+ error = xrep_setup_directory(sc);
+ if (error)
+ return error;
+ }
+
return xchk_setup_inode_contents(sc, 0);
}
diff --git a/fs/xfs/scrub/dir_repair.c b/fs/xfs/scrub/dir_repair.c
new file mode 100644
index 000000000000..a1f2bca53655
--- /dev/null
+++ b/fs/xfs/scrub/dir_repair.c
@@ -0,0 +1,964 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2023 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_bit.h"
+#include "xfs_log_format.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_inode.h"
+#include "xfs_icache.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
+#include "xfs_dir2.h"
+#include "xfs_dir2_priv.h"
+#include "xfs_bmap.h"
+#include "xfs_quota.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_trans_space.h"
+#include "xfs_bmap_util.h"
+#include "xfs_attr.h"
+#include "xfs_parent.h"
+#include "scrub/xfs_scrub.h"
+#include "scrub/scrub.h"
+#include "scrub/common.h"
+#include "scrub/trace.h"
+#include "scrub/repair.h"
+#include "scrub/tempfile.h"
+#include "scrub/iscan.h"
+#include "scrub/readdir.h"
+#include "scrub/listxattr.h"
+#include "scrub/xfile.h"
+#include "scrub/xfarray.h"
+#include "scrub/xfblob.h"
+
+/*
+ * Directory Repairs
+ * =================
+ *
+ * Reconstruct a directory by visiting each parent pointer of each file in the
+ * filesystem and translating the relevant pptrs into dirents. Translation
+ * occurs by adding new dirents to a temporary directory, which formats the
+ * ondisk directory blocks. In the final version of this code, we'll use the
+ * atomic extent swap code to exchange the entire directory structure of the
+ * file being repaired and the temporary, but for this PoC we omit the commit
+ * to reduce the amount of code that has to be ported.
+ *
+ * Because we have to scan the entire filesystem, the next patch introduces the
+ * inode scan and live update hooks so that the rebuilder can be kept aware of
+ * filesystem updates being made to this directory by other threads. Directory
+ * entry translation therefore requires two steps to avoid problems with lock
+ * contention and to keep ondisk tempdir updates out of the hook path.
+ *
+ * Every time the filesystem scanner or the live update hook code encounter a
+ * directory operation relevant to this rebuilder, they will write a record of
+ * the createname/removename operation to an xfarray. Dirent names are stored
+ * in an xfblob structure. At opportune times, these stashed updates will be
+ * read from the xfarray and committed (individually) to the temporary
+ * directory.
+ *
+ * When the filesystem scan is complete, we relock both the directory and the
+ * tempdir, and finish any stashed operations. At that point, we are
+ * theoretically ready to exchange the directory data fork mappings. This
+ * cannot happen until two patchsets get merged: the first allows callers to
+ * specify the owning inode number explicitly; and the second is the atomic
+ * extent swap series.
+ *
+ * For now we'll simply compare the two directories and complain about
+ * discrepancies.
+ */
+
+/* Maximum memory usage for the tempdir log, in bytes. */
+#define MAX_DIRENT_STASH_SIZE (32ULL << 10)
+
+/* Create a dirent in the tempdir. */
+#define XREP_DIRENT_ADD (1)
+
+/* Remove a dirent from the tempdir. */
+#define XREP_DIRENT_REMOVE (2)
+
+/* A stashed dirent update. */
+struct xrep_dirent {
+ /* Cookie for retrieval of the dirent name. */
+ xfblob_cookie name_cookie;
+
+ /* Child inode number. */
+ xfs_ino_t ino;
+
+ /* Length of the dirent name. */
+ uint8_t namelen;
+
+ /* File type of the dirent. */
+ uint8_t ftype;
+
+ /* XREP_DIRENT_{ADD,REMOVE} */
+ uint8_t action;
+};
+
+struct xrep_dir {
+ struct xfs_scrub *sc;
+
+ /* Inode scan cursor. */
+ struct xchk_iscan iscan;
+
+ /* Preallocated args struct for performing dir operations */
+ struct xfs_da_args args;
+
+ /* Stashed directory entry updates. */
+ struct xfarray *dir_entries;
+
+ /* Directory entry names. */
+ struct xfblob *dir_names;
+
+ /* Mutex protecting dir_entries, dir_names, and parent_ino. */
+ struct mutex lock;
+
+ /*
+ * This is the dotdot inumber that we're going to set on the
+ * reconstructed directory.
+ */
+ xfs_ino_t parent_ino;
+
+ /* Scratch buffer for scanning pptr xattrs */
+ struct xfs_parent_name_irec pptr;
+};
+
+/* Tear down all the incore stuff we created. */
+static void
+xrep_dir_teardown(
+ struct xfs_scrub *sc)
+{
+ struct xrep_dir *rd = sc->buf;
+
+ xchk_iscan_teardown(&rd->iscan);
+ mutex_destroy(&rd->lock);
+ xfblob_destroy(rd->dir_names);
+ xfarray_destroy(rd->dir_entries);
+}
+
+/* Set up for a directory repair. */
+int
+xrep_setup_directory(
+ struct xfs_scrub *sc)
+{
+ struct xrep_dir *rd;
+ int error;
+
+ error = xrep_tempfile_create(sc, S_IFDIR);
+ if (error)
+ return error;
+
+ rd = kvzalloc(sizeof(struct xrep_dir), XCHK_GFP_FLAGS);
+ if (!rd)
+ return -ENOMEM;
+
+ sc->buf = rd;
+ rd->sc = sc;
+ rd->parent_ino = NULLFSINO;
+ return 0;
+}
+
+/* Are these two directory names the same? */
+static inline bool
+xrep_dir_samename(
+ const struct xfs_name *n1,
+ const struct xfs_name *n2)
+{
+ return n1->len == n2->len && !memcmp(n1->name, n2->name, n1->len);
+}
+
+/*
+ * Look up the inode number for an exact name in a directory.
+ *
+ * Callers must hold the ILOCK. File types are XFS_DIR3_FT_*. Names are not
+ * checked for correctness. This initializes rd->args.
+ */
+STATIC int
+xrep_dir_lookup(
+ struct xrep_dir *rd,
+ struct xfs_inode *dp,
+ const struct xfs_name *name,
+ xfs_ino_t *ino)
+{
+ struct xfs_scrub *sc = rd->sc;
+ bool isblock, isleaf;
+ int error;
+
+ if (xfs_is_shutdown(dp->i_mount))
+ return -EIO;
+
+ ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
+ ASSERT(xfs_isilocked(dp, XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
+
+ memset(&rd->args, 0, sizeof(struct xfs_da_args));
+ rd->args.dp = dp;
+ rd->args.geo = sc->mp->m_dir_geo;
+ rd->args.hashval = xfs_dir2_hashname(dp->i_mount, name);
+ rd->args.namelen = name->len;
+ rd->args.name = name->name;
+ rd->args.op_flags = XFS_DA_OP_OKNOENT;
+ rd->args.trans = sc->tp;
+ rd->args.whichfork = XFS_DATA_FORK;
+
+ if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL) {
+ error = xfs_dir2_sf_lookup(&rd->args);
+ goto out_check_rval;
+ }
+
+ /* dir2 functions require that the data fork is loaded */
+ error = xfs_iread_extents(sc->tp, dp, XFS_DATA_FORK);
+ if (error)
+ return error;
+
+ error = xfs_dir2_isblock(&rd->args, &isblock);
+ if (error)
+ return error;
+
+ if (isblock) {
+ error = xfs_dir2_block_lookup(&rd->args);
+ goto out_check_rval;
+ }
+
+ error = xfs_dir2_isleaf(&rd->args, &isleaf);
+ if (error)
+ return error;
+
+ if (isleaf) {
+ error = xfs_dir2_leaf_lookup(&rd->args);
+ goto out_check_rval;
+ }
+
+ error = xfs_dir2_node_lookup(&rd->args);
+
+out_check_rval:
+ if (error == -EEXIST)
+ error = 0;
+ if (!error)
+ *ino = rd->args.inumber;
+ return error;
+}
+
+/* Create a directory entry, having filled out most of rd->args via lookup. */
+STATIC int
+xrep_dir_createname(
+ struct xrep_dir *rd,
+ const struct xfs_name *name,
+ xfs_ino_t inum,
+ xfs_extlen_t total)
+{
+ struct xfs_scrub *sc = rd->sc;
+ struct xfs_inode *dp = rd->args.dp;
+ bool is_block, is_leaf;
+ int error;
+
+ ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
+
+ error = xfs_dir_ino_validate(sc->mp, inum);
+ if (error)
+ return error;
+
+ trace_xrep_dir_createname(dp, name, inum);
+
+ /* reset cmpresult as if we haven't done a lookup */
+ rd->args.cmpresult = XFS_CMP_DIFFERENT;
+ rd->args.filetype = name->type;
+ rd->args.inumber = inum;
+ rd->args.op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT;
+ rd->args.total = total;
+
+ if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
+ return xfs_dir2_sf_addname(&rd->args);
+
+ error = xfs_dir2_isblock(&rd->args, &is_block);
+ if (error)
+ return error;
+ if (is_block)
+ return xfs_dir2_block_addname(&rd->args);
+
+ error = xfs_dir2_isleaf(&rd->args, &is_leaf);
+ if (error)
+ return error;
+ if (is_leaf)
+ return xfs_dir2_leaf_addname(&rd->args);
+
+ return xfs_dir2_node_addname(&rd->args);
+}
+
+/* Remove a directory entry, having filled out rd->args via lookup. */
+STATIC int
+xrep_dir_removename(
+ struct xrep_dir *rd,
+ const struct xfs_name *name,
+ xfs_extlen_t total)
+{
+ struct xfs_inode *dp = rd->args.dp;
+ bool is_block, is_leaf;
+ int error;
+
+ ASSERT(S_ISDIR(VFS_I(dp)->i_mode));
+
+ /* reset cmpresult as if we haven't done a lookup */
+ rd->args.cmpresult = XFS_CMP_DIFFERENT;
+ rd->args.op_flags = 0;
+ rd->args.total = total;
+
+ trace_xrep_dir_removename(dp, name, rd->args.inumber);
+
+ if (dp->i_df.if_format == XFS_DINODE_FMT_LOCAL)
+ return xfs_dir2_sf_removename(&rd->args);
+
+ error = xfs_dir2_isblock(&rd->args, &is_block);
+ if (error)
+ return error;
+ if (is_block)
+ return xfs_dir2_block_removename(&rd->args);
+
+ error = xfs_dir2_isleaf(&rd->args, &is_leaf);
+ if (error)
+ return error;
+ if (is_leaf)
+ return xfs_dir2_leaf_removename(&rd->args);
+
+ return xfs_dir2_node_removename(&rd->args);
+}
+
+/* Update the temporary directory with a stashed update. */
+STATIC int
+xrep_dir_replay_update(
+ struct xrep_dir *rd,
+ const struct xrep_dirent *dirent)
+{
+ struct xfs_name xname = {
+ .len = dirent->namelen,
+ .type = dirent->ftype,
+ .name = rd->pptr.p_name,
+ };
+ struct xfs_scrub *sc = rd->sc;
+ struct xfs_mount *mp = sc->mp;
+ xfs_ino_t child_ino;
+ uint resblks;
+ int error;
+
+ if (dirent->action == XREP_DIRENT_REMOVE)
+ resblks = XFS_DIRREMOVE_SPACE_RES(mp);
+ else
+ resblks = XFS_DIRENTER_SPACE_RES(mp, dirent->namelen);
+
+ error = xchk_trans_alloc(sc, resblks);
+ if (error)
+ return error;
+
+ error = xrep_tempfile_ilock_polled(sc);
+ if (error) {
+ xchk_trans_cancel(rd->sc);
+ return error;
+ }
+
+ xfs_trans_ijoin(sc->tp, sc->tempip, 0);
+
+ error = xrep_dir_lookup(rd, sc->tempip, &xname, &child_ino);
+ if (dirent->action == XREP_DIRENT_REMOVE) {
+ /* Remove this dirent. The lookup must succeed. */
+ if (error)
+ goto out_cancel;
+ if (child_ino != dirent->ino) {
+ error = -ENOENT;
+ goto out_cancel;
+ }
+
+ error = xrep_dir_removename(rd, &xname, resblks);
+ } else {
+ /* Add this dirent. The lookup must not succeed. */
+ if (error == 0)
+ error = -EEXIST;
+ if (error != -ENOENT)
+ goto out_cancel;
+
+ error = xrep_dir_createname(rd, &xname, dirent->ino, resblks);
+ }
+ if (error)
+ goto out_cancel;
+
+ error = xrep_trans_commit(sc);
+ goto out_ilock;
+
+out_cancel:
+ xchk_trans_cancel(rd->sc);
+out_ilock:
+ xrep_tempfile_iunlock(rd->sc);
+ return error;
+}
+
+/*
+ * Flush stashed dirent updates that have been recorded by the scanner. This
+ * is done to reduce the memory requirements of the directory rebuild, since
+ * directories can contain up to 32GB of directory data.
+ *
+ * Caller must not hold transactions or ILOCKs. Caller must hold the tempdir
+ * IOLOCK.
+ */
+STATIC int
+xrep_dir_replay_updates(
+ struct xrep_dir *rd)
+{
+ xfarray_idx_t array_cur;
+ int error;
+
+ mutex_lock(&rd->lock);
+ foreach_xfarray_idx(rd->dir_entries, array_cur) {
+ struct xrep_dirent dirent;
+
+ error = xfarray_load(rd->dir_entries, array_cur, &dirent);
+ if (error)
+ goto out_unlock;
+
+ error = xfblob_load(rd->dir_names, dirent.name_cookie,
+ rd->pptr.p_name, dirent.namelen);
+ if (error)
+ goto out_unlock;
+ rd->pptr.p_name[MAXNAMELEN - 1] = 0;
+ mutex_unlock(&rd->lock);
+
+ error = xrep_dir_replay_update(rd, &dirent);
+ if (error)
+ return error;
+
+ mutex_lock(&rd->lock);
+ }
+
+ /* Empty out both arrays now that we've added the entries. */
+ xfarray_truncate(rd->dir_entries);
+ xfblob_truncate(rd->dir_names);
+ mutex_unlock(&rd->lock);
+ return 0;
+out_unlock:
+ mutex_unlock(&rd->lock);
+ return error;
+}
+
+/*
+ * Remember that we want to create a dirent in the tempdir. These stashed
+ * actions will be replayed later.
+ */
+STATIC int
+xrep_dir_add_dirent(
+ struct xrep_dir *rd,
+ const struct xfs_name *name,
+ xfs_ino_t ino)
+{
+ struct xrep_dirent dirent = {
+ .action = XREP_DIRENT_ADD,
+ .ino = ino,
+ .namelen = name->len,
+ .ftype = name->type,
+ };
+ int error;
+
+ trace_xrep_dir_add_dirent(rd->sc->tempip, name, ino);
+
+ error = xfblob_store(rd->dir_names, &dirent.name_cookie, name->name,
+ name->len);
+ if (error)
+ return error;
+
+ return xfarray_append(rd->dir_entries, &dirent);
+}
+
+/*
+ * Remember that we want to remove a dirent from the tempdir. These stashed
+ * actions will be replayed later.
+ */
+STATIC int
+xrep_dir_remove_dirent(
+ struct xrep_dir *rd,
+ const struct xfs_name *name,
+ xfs_ino_t ino)
+{
+ struct xrep_dirent dirent = {
+ .action = XREP_DIRENT_REMOVE,
+ .ino = ino,
+ .namelen = name->len,
+ .ftype = name->type,
+ };
+ int error;
+
+ trace_xrep_dir_remove_dirent(rd->sc->tempip, name, ino);
+
+ error = xfblob_store(rd->dir_names, &dirent.name_cookie, name->name,
+ name->len);
+ if (error)
+ return error;
+
+ return xfarray_append(rd->dir_entries, &dirent);
+}
+
+/*
+ * Examine an xattr of a file. If this xattr is a parent pointer that leads us
+ * back to the directory that we're rebuilding, add a dirent to the temporary
+ * directory.
+ */
+STATIC int
+xrep_dir_scan_parent_pointer(
+ struct xfs_scrub *sc,
+ struct xfs_inode *ip,
+ unsigned int attr_flags,
+ const unsigned char *name,
+ unsigned int namelen,
+ const void *value,
+ unsigned int valuelen,
+ void *priv)
+{
+ struct xfs_name xname;
+ struct xrep_dir *rd = priv;
+ const struct xfs_parent_name_rec *rec = (const void *)name;
+ int error;
+
+ /* Ignore incomplete xattrs */
+ if (attr_flags & XFS_ATTR_INCOMPLETE)
+ return 0;
+
+ /* Ignore anything that isn't a parent pointer. */
+ if (!(attr_flags & XFS_ATTR_PARENT))
+ return 0;
+
+ /* Does the ondisk parent pointer structure make sense? */
+ if (!xfs_parent_namecheck(sc->mp, rec, namelen, attr_flags) ||
+ !xfs_parent_valuecheck(sc->mp, value, valuelen))
+ return -EFSCORRUPTED;
+
+ xfs_parent_irec_from_disk(&rd->pptr, rec, value, valuelen);
+
+ /* Ignore parent pointers that point back to a different dir. */
+ if (rd->pptr.p_ino != sc->ip->i_ino ||
+ rd->pptr.p_gen != VFS_I(sc->ip)->i_generation)
+ return 0;
+
+ /*
+ * Transform this parent pointer into a dirent and queue it for later
+ * addition to the temporary directory.
+ */
+ xname.name = rd->pptr.p_name;
+ xname.len = rd->pptr.p_namelen;
+ xname.type = xfs_mode_to_ftype(VFS_I(ip)->i_mode);
+
+ mutex_lock(&rd->lock);
+ error = xrep_dir_add_dirent(rd, &xname, ip->i_ino);
+ mutex_unlock(&rd->lock);
+ return error;
+}
+
+/*
+ * If this child dirent points to the directory being repaired, remember that
+ * fact so that we can reset the dotdot entry if necessary.
+ */
+STATIC int
+xrep_dir_scan_dirent(
+ struct xfs_scrub *sc,
+ struct xfs_inode *dp,
+ xfs_dir2_dataptr_t dapos,
+ const struct xfs_name *name,
+ xfs_ino_t ino,
+ void *priv)
+{
+ struct xrep_dir *rd = priv;
+
+ /* Dirent doesn't point to this directory. */
+ if (ino != rd->sc->ip->i_ino)
+ return 0;
+
+ /* Ignore garbage inum. */
+ if (!xfs_verify_dir_ino(rd->sc->mp, ino))
+ return 0;
+
+ /* No weird looking names. */
+ if (name->len >= MAXNAMELEN || name->len <= 0)
+ return 0;
+
+ /* Don't pick up dot or dotdot entries; we only want child dirents. */
+ if (xrep_dir_samename(name, &xfs_name_dotdot) ||
+ xrep_dir_samename(name, &xfs_name_dot))
+ return 0;
+
+ trace_xrep_dir_replacename(sc->tempip, &xfs_name_dotdot, dp->i_ino);
+
+ mutex_lock(&rd->lock);
+ rd->parent_ino = dp->i_ino;
+ mutex_unlock(&rd->lock);
+ return 0;
+}
+
+/*
+ * Decide if we want to look for child dirents or parent pointers in this file.
+ * Skip the dir being repaired and any files being used to stage repairs.
+ */
+static inline bool
+xrep_dir_want_scan(
+ struct xrep_dir *rd,
+ const struct xfs_inode *ip)
+{
+ return ip != rd->sc->ip && !xrep_is_tempfile(ip);
+}
+
+/*
+ * Take ILOCK on a file that we want to scan.
+ *
+ * Select ILOCK_EXCL if the file is a directory with an unloaded data bmbt or
+ * has an unloaded attr bmbt. Otherwise, take ILOCK_SHARED.
+ */
+static inline unsigned int
+xrep_dir_scan_ilock(
+ struct xrep_dir *rd,
+ struct xfs_inode *ip)
+{
+ uint lock_mode = XFS_ILOCK_SHARED;
+
+ /* Need to take the shared ILOCK to advance the iscan cursor. */
+ if (!xrep_dir_want_scan(rd, ip))
+ goto lock;
+
+ if (S_ISDIR(VFS_I(ip)->i_mode) && xfs_need_iread_extents(&ip->i_df)) {
+ lock_mode = XFS_ILOCK_EXCL;
+ goto lock;
+ }
+
+ if (xfs_inode_has_attr_fork(ip) && xfs_need_iread_extents(&ip->i_af))
+ lock_mode = XFS_ILOCK_EXCL;
+
+lock:
+ xfs_ilock(ip, lock_mode);
+ return lock_mode;
+}
+
+/*
+ * Scan this file for relevant child dirents or parent pointers that point to
+ * the directory we're rebuilding.
+ */
+STATIC int
+xrep_dir_scan_file(
+ struct xrep_dir *rd,
+ struct xfs_inode *ip)
+{
+ unsigned int lock_mode;
+ int error = 0;
+
+ lock_mode = xrep_dir_scan_ilock(rd, ip);
+
+ if (!xrep_dir_want_scan(rd, ip))
+ goto scan_done;
+
+ error = xchk_xattr_walk(rd->sc, ip, xrep_dir_scan_parent_pointer, rd);
+ if (error)
+ goto scan_done;
+
+ if (S_ISDIR(VFS_I(ip)->i_mode)) {
+ error = xchk_dir_walk(rd->sc, ip, xrep_dir_scan_dirent, rd);
+ if (error)
+ goto scan_done;
+ }
+
+scan_done:
+ xchk_iscan_mark_visited(&rd->iscan, ip);
+ xfs_iunlock(ip, lock_mode);
+ return error;
+}
+
+/* Scan all files in the filesystem for dirents. */
+STATIC int
+xrep_dir_scan_dirtree(
+ struct xrep_dir *rd)
+{
+ struct xfs_scrub *sc = rd->sc;
+ struct xfs_inode *ip;
+ int error;
+
+ /*
+ * Filesystem scans are time consuming. Drop the directory ILOCK and
+ * all other resources for the duration of the scan and hope for the
+ * best.
+ */
+ xchk_trans_cancel(sc);
+ if (sc->ilock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL))
+ xchk_iunlock(sc, sc->ilock_flags & (XFS_ILOCK_SHARED |
+ XFS_ILOCK_EXCL));
+ error = xchk_trans_alloc_empty(sc);
+ if (error)
+ return error;
+
+ while ((error = xchk_iscan_iter(&rd->iscan, &ip)) == 1) {
+ uint64_t mem_usage;
+
+ error = xrep_dir_scan_file(rd, ip);
+ xchk_irele(sc, ip);
+ if (error)
+ break;
+
+ /* Flush stashed dirent updates to constrain memory usage. */
+ mutex_lock(&rd->lock);
+ mem_usage = xfarray_bytes(rd->dir_entries) +
+ xfblob_bytes(rd->dir_names);
+ mutex_unlock(&rd->lock);
+ if (mem_usage >= MAX_DIRENT_STASH_SIZE) {
+ xchk_trans_cancel(sc);
+
+ error = xrep_tempfile_iolock_polled(sc);
+ if (error)
+ break;
+
+ error = xrep_dir_replay_updates(rd);
+ xrep_tempfile_iounlock(sc);
+ if (error)
+ break;
+
+ error = xchk_trans_alloc_empty(sc);
+ if (error)
+ break;
+ }
+
+ if (xchk_should_terminate(sc, &error))
+ break;
+ }
+ xchk_iscan_iter_finish(&rd->iscan);
+ if (error) {
+ /*
+ * If we couldn't grab an inode that was busy with a state
+ * change, change the error code so that we exit to userspace
+ * as quickly as possible.
+ */
+ if (error == -EBUSY)
+ return -ECANCELED;
+ return error;
+ }
+
+ return 0;
+}
+
+/* Dump a dirent from the temporary dir. */
+STATIC int
+xrep_dir_dump_tempdir(
+ struct xfs_scrub *sc,
+ struct xfs_inode *dp,
+ xfs_dir2_dataptr_t dapos,
+ const struct xfs_name *name,
+ xfs_ino_t ino,
+ void *priv)
+{
+ struct xrep_dir *rd = priv;
+ bool child_dirent = true;
+ int error = 0;
+
+ /*
+ * The tempdir was created with a dotdot entry pointing to the root
+ * directory. Substitute whatever inode number we found during the
+ * filesystem scan.
+ *
+ * The tempdir was also created with a dot entry pointing to itself.
+ * Substitute the inode number of the directory being repaired. A
+ * prerequisite for the real repair code is a patchset to allow dir
+ * callers to set the owner (and dot entry in the case of sf -> block
+ * conversion) explicitly.
+ *
+ * I've chosen not to port the owner setting patchset or the swapext
+ * patchset for this PoC, which is why we build the tempdir, compare
+ * the contents, and drop the tempdir.
+ */
+ if (xrep_dir_samename(name, &xfs_name_dotdot)) {
+ child_dirent = false;
+ ino = rd->parent_ino;
+ }
+ if (xrep_dir_samename(name, &xfs_name_dot)) {
+ child_dirent = false;
+ ino = sc->ip->i_ino;
+ }
+
+ trace_xrep_dir_dumpname(sc->tempip, name, ino);
+
+ /*
+ * Set ourselves up to free every dirent in the tempdir because
+ * directory inactivation won't do it for us. The rest of the online
+ * fsck patchset provides us a means to swap the directory structure
+ * and reap it responsibly, but I didn't feel like porting all that.
+ */
+ if (child_dirent) {
+ mutex_lock(&rd->lock);
+ error = xrep_dir_remove_dirent(rd, name, ino);
+ mutex_unlock(&rd->lock);
+ }
+
+ return error;
+}
+
+/*
+ * "Commit" the new directory structure to the file that we're repairing.
+ *
+ * In the final version, we'd swap the new directory contents (which we created
+ * in the tempfile) into the directory being repaired. For now we just lock
+ * the temporary dir and dump what we found.
+ */
+STATIC int
+xrep_dir_rebuild_tree(
+ struct xrep_dir *rd)
+{
+ struct xfs_scrub *sc = rd->sc;
+ int error = 0;
+
+ /*
+ * Replay the last of the stashed dirent updates. We still hold the
+ * IOLOCK_EXCL of the directory that we're repairing and the temporary
+ * directory.
+ */
+ xchk_trans_cancel(sc);
+
+ ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL);
+ error = xrep_tempfile_iolock_polled(sc);
+ if (error)
+ return error;
+
+ /*
+ * Replay stashed updates and take the ILOCKs of both directories
+ * before we simulate committing the new directory structure.
+ *
+ * As of Linux 6.3, if /a, /a/b, and /c are all directories, the VFS
+ * does not take i_rwsem on /a/b for a "mv /a/b /c/" operation. This
+ * means that only b's ILOCK protects b's dotdot update. b's IOLOCK
+ * is not held, unlike every other dotdot update. To stabilize sc->ip
+ * to simulate the repair commit, we must hold the ILOCK of the
+ * directory being repaired /and/ there must not be any pending live
+ * updates.
+ */
+ do {
+ error = xrep_dir_replay_updates(rd);
+ if (error)
+ return error;
+
+ error = xchk_trans_alloc_empty(sc);
+ if (error)
+ return error;
+
+ xchk_ilock(sc, XFS_ILOCK_EXCL);
+ if (xfarray_length(rd->dir_entries) == 0)
+ break;
+
+ xchk_iunlock(sc, XFS_ILOCK_EXCL);
+ xchk_trans_cancel(sc);
+ } while (!xchk_should_terminate(sc, &error));
+ if (error)
+ return error;
+
+ if (sc->ip == sc->mp->m_rootip) {
+ /* Should not have found any parent of the root directory. */
+ ASSERT(rd->parent_ino == NULLFSINO);
+ rd->parent_ino = sc->mp->m_rootip->i_ino;
+ } else if (rd->parent_ino == NULLFSINO) {
+ /*
+ * Should have found a parent somewhere unless this is an
+ * unlinked directory.
+ */
+ ASSERT(VFS_I(sc->ip)->i_nlink == 0);
+ rd->parent_ino = rd->sc->mp->m_sb.sb_rootino;
+ }
+
+ trace_xrep_dir_rebuild_tree(sc->ip, rd->parent_ino);
+
+ /*
+ * At this point, we've quiesced both directories and should be ready
+ * to commit the new contents.
+ *
+ * We don't have atomic swapext here, so all we do is dump the dirents
+ * that we found to the ftrace buffer and {ab,re}use the dirent update
+ * stashing mechanism to schedule deletion of every dirent in the
+ * temporary directory to avoid leaking directory blocks.
+ */
+ error = xrep_tempfile_ilock_polled(sc);
+ if (error)
+ return error;
+
+ error = xchk_dir_walk(sc, sc->tempip, xrep_dir_dump_tempdir, rd);
+ if (error)
+ return error;
+
+ /*
+ * Inactivation will not free any of the tempdir's directory blocks,
+ * so we have to do that ourselves.
+ *
+ * Abort the iscan so that live updates will be ignored. Cancel the
+ * transaction, unlock the inodes, and "replay" all the dirent
+ * deletions that the walk queued against the temp dir.
+ */
+ xchk_iscan_abort(&rd->iscan);
+ xchk_trans_cancel(sc);
+ xrep_tempfile_iunlock(sc);
+ xchk_iunlock(sc, XFS_ILOCK_EXCL);
+ return xrep_dir_replay_updates(rd);
+}
+
+/* Set up the filesystem scan so we can regenerate directory entries. */
+STATIC int
+xrep_dir_setup_scan(
+ struct xrep_dir *rd)
+{
+ struct xfs_scrub *sc = rd->sc;
+ int error;
+
+ error = xfarray_create(sc->mp, "directory entries", 0,
+ sizeof(struct xrep_dirent), &rd->dir_entries);
+ if (error)
+ return error;
+
+ error = xfblob_create(sc->mp, "dirent names", &rd->dir_names);
+ if (error)
+ goto out_entries;
+
+ mutex_init(&rd->lock);
+
+ /* Retry iget every tenth of a second for up to 30 seconds. */
+ xchk_iscan_start(sc, 30000, 100, &rd->iscan);
+
+ return 0;
+
+out_entries:
+ xfarray_destroy(rd->dir_entries);
+ return error;
+}
+
+/*
+ * Repair the directory metadata.
+ *
+ * XXX: Is it necessary to check the dcache for this directory to make sure
+ * that we always recreate every cached entry?
+ */
+int
+xrep_directory(
+ struct xfs_scrub *sc)
+{
+ struct xrep_dir *rd = sc->buf;
+ int error = 0;
+
+ /* We require directory parent pointers to rebuild anything. */
+ if (!xfs_has_parent(sc->mp))
+ return -EOPNOTSUPP;
+
+ error = xrep_dir_setup_scan(rd);
+ if (error)
+ goto out;
+
+ error = xrep_dir_scan_dirtree(rd);
+ if (error)
+ goto out_finish_scan;
+
+ error = xrep_dir_rebuild_tree(rd);
+ if (error)
+ goto out_finish_scan;
+
+out_finish_scan:
+ xrep_dir_teardown(sc);
+out:
+ return error;
+}
diff --git a/fs/xfs/scrub/repair.h b/fs/xfs/scrub/repair.h
index 840f74ec431c..ff254ff9b86d 100644
--- a/fs/xfs/scrub/repair.h
+++ b/fs/xfs/scrub/repair.h
@@ -30,6 +30,16 @@ int xrep_init_btblock(struct xfs_scrub *sc, xfs_fsblock_t fsb,
struct xfs_buf **bpp, xfs_btnum_t btnum,
const struct xfs_buf_ops *ops);
+static inline int
+xrep_trans_commit(
+ struct xfs_scrub *sc)
+{
+ int error = xfs_trans_commit(sc->tp);
+
+ sc->tp = NULL;
+ return error;
+}
+
struct xbitmap;
int xrep_fix_freelist(struct xfs_scrub *sc, bool can_shrink);
@@ -57,6 +67,8 @@ int xrep_find_ag_btree_roots(struct xfs_scrub *sc, struct xfs_buf *agf_bp,
void xrep_force_quotacheck(struct xfs_scrub *sc, xfs_dqtype_t type);
int xrep_ino_dqattach(struct xfs_scrub *sc);
+int xrep_setup_directory(struct xfs_scrub *sc);
+
/* Metadata repairers */
int xrep_probe(struct xfs_scrub *sc);
@@ -64,6 +76,7 @@ int xrep_superblock(struct xfs_scrub *sc);
int xrep_agf(struct xfs_scrub *sc);
int xrep_agfl(struct xfs_scrub *sc);
int xrep_agi(struct xfs_scrub *sc);
+int xrep_directory(struct xfs_scrub *sc);
#else
@@ -83,11 +96,14 @@ xrep_calc_ag_resblks(
return 0;
}
+#define xrep_setup_directory(sc) (0)
+
#define xrep_probe xrep_notsupported
#define xrep_superblock xrep_notsupported
#define xrep_agf xrep_notsupported
#define xrep_agfl xrep_notsupported
#define xrep_agi xrep_notsupported
+#define xrep_directory xrep_notsupported
#endif /* CONFIG_XFS_ONLINE_REPAIR */
diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c
index a19ea7fdd510..b2a8de449d11 100644
--- a/fs/xfs/scrub/scrub.c
+++ b/fs/xfs/scrub/scrub.c
@@ -299,7 +299,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
.type = ST_INODE,
.setup = xchk_setup_directory,
.scrub = xchk_directory,
- .repair = xrep_notsupported,
+ .repair = xrep_directory,
},
[XFS_SCRUB_TYPE_XATTR] = { /* extended attributes */
.type = ST_INODE,
diff --git a/fs/xfs/scrub/tempfile.c b/fs/xfs/scrub/tempfile.c
index 15b3f2c42011..ab012aa30882 100644
--- a/fs/xfs/scrub/tempfile.c
+++ b/fs/xfs/scrub/tempfile.c
@@ -136,6 +136,7 @@ xrep_tempfile_create(
xfs_setup_iops(sc->tempip);
xfs_finish_inode_setup(sc->tempip);
+ xfs_iunlock(sc->tempip, XFS_ILOCK_EXCL);
sc->temp_ilock_flags = 0;
return error;
@@ -149,6 +150,7 @@ xrep_tempfile_create(
*/
if (sc->tempip) {
xfs_finish_inode_setup(sc->tempip);
+ xfs_iunlock(sc->tempip, XFS_ILOCK_EXCL);
xchk_irele(sc, sc->tempip);
}
out_release_dquots:
@@ -172,6 +174,26 @@ xrep_tempfile_iolock_nowait(
return false;
}
+/*
+ * Take the temporary file's IOLOCK while holding a different inode's IOLOCK.
+ * In theory nobody else should hold the tempfile's IOLOCK, but we use trylock
+ * to avoid deadlocks and lockdep.
+ */
+int
+xrep_tempfile_iolock_polled(
+ struct xfs_scrub *sc)
+{
+ int error = 0;
+
+ while (!xrep_tempfile_iolock_nowait(sc)) {
+ if (xchk_should_terminate(sc, &error))
+ return error;
+ delay(1);
+ }
+
+ return 0;
+}
+
/* Release IOLOCK_EXCL on the temporary file. */
void
xrep_tempfile_iounlock(
@@ -203,6 +225,26 @@ xrep_tempfile_ilock_nowait(
return false;
}
+/*
+ * Take the temporary file's ILOCK while holding a different inode's ILOCK. In
+ * theory nobody else should hold the tempfile's ILOCK, but we use trylock to
+ * avoid deadlocks and lockdep.
+ */
+int
+xrep_tempfile_ilock_polled(
+ struct xfs_scrub *sc)
+{
+ int error = 0;
+
+ while (!xrep_tempfile_ilock_nowait(sc)) {
+ if (xchk_should_terminate(sc, &error))
+ return error;
+ delay(1);
+ }
+
+ return 0;
+}
+
/* Unlock ILOCK_EXCL on the temporary file after an update. */
void
xrep_tempfile_iunlock(
diff --git a/fs/xfs/scrub/tempfile.h b/fs/xfs/scrub/tempfile.h
index e2f493b5d3d9..1e61d8e1ddce 100644
--- a/fs/xfs/scrub/tempfile.h
+++ b/fs/xfs/scrub/tempfile.h
@@ -11,10 +11,12 @@ int xrep_tempfile_create(struct xfs_scrub *sc, uint16_t mode);
void xrep_tempfile_rele(struct xfs_scrub *sc);
bool xrep_tempfile_iolock_nowait(struct xfs_scrub *sc);
+int xrep_tempfile_iolock_polled(struct xfs_scrub *sc);
void xrep_tempfile_iounlock(struct xfs_scrub *sc);
void xrep_tempfile_ilock(struct xfs_scrub *sc);
bool xrep_tempfile_ilock_nowait(struct xfs_scrub *sc);
+int xrep_tempfile_ilock_polled(struct xfs_scrub *sc);
void xrep_tempfile_iunlock(struct xfs_scrub *sc);
bool xrep_is_tempfile(const struct xfs_inode *ip);
#else
diff --git a/fs/xfs/scrub/trace.c b/fs/xfs/scrub/trace.c
index 83e8a64c95d4..61b51617fbb4 100644
--- a/fs/xfs/scrub/trace.c
+++ b/fs/xfs/scrub/trace.c
@@ -17,6 +17,7 @@
#include "scrub/xfile.h"
#include "scrub/xfarray.h"
#include "scrub/iscan.h"
+#include "xfs_da_format.h"
/* Figure out which block the btree cursor was pointing to. */
static inline xfs_fsblock_t
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index 49e4a27526d2..af5b5cd6d55b 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -1240,6 +1240,70 @@ TRACE_EVENT(xrep_tempfile_create,
__entry->temp_inum)
);
+DECLARE_EVENT_CLASS(xrep_dirent_class,
+ TP_PROTO(struct xfs_inode *dp, const struct xfs_name *name,
+ xfs_ino_t ino),
+ TP_ARGS(dp, name, ino),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(xfs_ino_t, dir_ino)
+ __field(unsigned int, namelen)
+ __dynamic_array(char, name, name->len)
+ __field(xfs_ino_t, ino)
+ __field(uint8_t, ftype)
+ ),
+ TP_fast_assign(
+ __entry->dev = dp->i_mount->m_super->s_dev;
+ __entry->dir_ino = dp->i_ino;
+ __entry->namelen = name->len;
+ memcpy(__get_str(name), name->name, name->len);
+ __entry->ino = ino;
+ __entry->ftype = name->type;
+ ),
+ TP_printk("dev %d:%d dir 0x%llx ftype %s name '%.*s' ino 0x%llx",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->dir_ino,
+ __print_symbolic(__entry->ftype, XFS_DIR3_FTYPE_STR),
+ __entry->namelen,
+ __get_str(name),
+ __entry->ino)
+)
+#define DEFINE_XREP_DIRENT_CLASS(name) \
+DEFINE_EVENT(xrep_dirent_class, name, \
+ TP_PROTO(struct xfs_inode *dp, const struct xfs_name *name, \
+ xfs_ino_t ino), \
+ TP_ARGS(dp, name, ino))
+DEFINE_XREP_DIRENT_CLASS(xrep_dir_add_dirent);
+DEFINE_XREP_DIRENT_CLASS(xrep_dir_remove_dirent);
+DEFINE_XREP_DIRENT_CLASS(xrep_dir_createname);
+DEFINE_XREP_DIRENT_CLASS(xrep_dir_removename);
+DEFINE_XREP_DIRENT_CLASS(xrep_dir_replacename);
+DEFINE_XREP_DIRENT_CLASS(xrep_dir_dumpname);
+
+DECLARE_EVENT_CLASS(xrep_dir_class,
+ TP_PROTO(struct xfs_inode *dp, xfs_ino_t parent_ino),
+ TP_ARGS(dp, parent_ino),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(xfs_ino_t, dir_ino)
+ __field(xfs_ino_t, parent_ino)
+ ),
+ TP_fast_assign(
+ __entry->dev = dp->i_mount->m_super->s_dev;
+ __entry->dir_ino = dp->i_ino;
+ __entry->parent_ino = parent_ino;
+ ),
+ TP_printk("dev %d:%d dir 0x%llx parent 0x%llx",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->dir_ino,
+ __entry->parent_ino)
+)
+#define DEFINE_XREP_DIR_CLASS(name) \
+DEFINE_EVENT(xrep_dir_class, name, \
+ TP_PROTO(struct xfs_inode *dp, xfs_ino_t parent_ino), \
+ TP_ARGS(dp, parent_ino))
+DEFINE_XREP_DIR_CLASS(xrep_dir_rebuild_tree);
+
#endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */
#endif /* _TRACE_XFS_SCRUB_TRACE_H */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 2/3] xfs: add hooks to do directory updates
2023-04-06 19:14 ` [PATCHSET v11 0/3] xfs: online repair of directories Darrick J. Wong
2023-04-06 19:26 ` [PATCH 1/3] xfs: reconstruct directories from parent pointers Darrick J. Wong
@ 2023-04-06 19:26 ` Darrick J. Wong
2023-04-06 19:27 ` [PATCH 3/3] xfs: compare generated and existing dirents Darrick J. Wong
2 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:26 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
While we're scanning the filesystem, we still need to keep the tempdir
up to date with whatever changes get made to the you know what.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_dir2.c | 2 +
fs/xfs/libxfs/xfs_dir2.h | 2 +
fs/xfs/scrub/dir_repair.c | 90 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 92 insertions(+), 2 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c
index a73573d47b13..825a8cd9ee57 100644
--- a/fs/xfs/libxfs/xfs_dir2.c
+++ b/fs/xfs/libxfs/xfs_dir2.c
@@ -435,7 +435,7 @@ int
xfs_dir_removename(
struct xfs_trans *tp,
struct xfs_inode *dp,
- struct xfs_name *name,
+ const struct xfs_name *name,
xfs_ino_t ino,
xfs_extlen_t total) /* bmap's total block count */
{
diff --git a/fs/xfs/libxfs/xfs_dir2.h b/fs/xfs/libxfs/xfs_dir2.h
index 15a36cf7ae87..d394bcd5e82c 100644
--- a/fs/xfs/libxfs/xfs_dir2.h
+++ b/fs/xfs/libxfs/xfs_dir2.h
@@ -46,7 +46,7 @@ extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp,
const struct xfs_name *name, xfs_ino_t *inum,
struct xfs_name *ci_name);
extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
- struct xfs_name *name, xfs_ino_t ino,
+ const struct xfs_name *name, xfs_ino_t ino,
xfs_extlen_t tot);
extern int xfs_dir_replace(struct xfs_trans *tp, struct xfs_inode *dp,
const struct xfs_name *name, xfs_ino_t inum,
diff --git a/fs/xfs/scrub/dir_repair.c b/fs/xfs/scrub/dir_repair.c
index a1f2bca53655..c484e6f36ca0 100644
--- a/fs/xfs/scrub/dir_repair.c
+++ b/fs/xfs/scrub/dir_repair.c
@@ -121,6 +121,9 @@ struct xrep_dir {
/* Mutex protecting dir_entries, dir_names, and parent_ino. */
struct mutex lock;
+ /* Hook to capture directory entry updates. */
+ struct xfs_dirent_hook hooks;
+
/*
* This is the dotdot inumber that we're going to set on the
* reconstructed directory.
@@ -138,6 +141,7 @@ xrep_dir_teardown(
{
struct xrep_dir *rd = sc->buf;
+ xfs_dirent_hook_del(sc->mp, &rd->hooks);
xchk_iscan_teardown(&rd->iscan);
mutex_destroy(&rd->lock);
xfblob_destroy(rd->dir_names);
@@ -152,6 +156,8 @@ xrep_setup_directory(
struct xrep_dir *rd;
int error;
+ xchk_fshooks_enable(sc, XCHK_FSHOOKS_DIRENTS);
+
error = xrep_tempfile_create(sc, S_IFDIR);
if (error)
return error;
@@ -899,6 +905,74 @@ xrep_dir_rebuild_tree(
return xrep_dir_replay_updates(rd);
}
+/*
+ * Capture dirent updates being made by other threads which are relevant to the
+ * directory being repaired.
+ */
+STATIC int
+xrep_dir_live_update(
+ struct notifier_block *nb,
+ unsigned long action,
+ void *data)
+{
+ struct xfs_dirent_update_params *p = data;
+ struct xrep_dir *rd;
+ struct xfs_scrub *sc;
+ int error = 0;
+
+ rd = container_of(nb, struct xrep_dir, hooks.delta_hook.nb);
+ sc = rd->sc;
+
+ /*
+ * This thread updated a child dirent in the directory that we're
+ * rebuilding. Stash the update for replay against the temporary
+ * directory.
+ */
+ if (action == XFS_DIRENT_CHILD_DELTA &&
+ p->dp->i_ino == sc->ip->i_ino &&
+ xchk_iscan_want_live_update(&rd->iscan, p->ip->i_ino)) {
+ mutex_lock(&rd->lock);
+ if (p->delta > 0)
+ error = xrep_dir_add_dirent(rd, p->name, p->ip->i_ino);
+ else
+ error = xrep_dir_remove_dirent(rd, p->name,
+ p->ip->i_ino);
+ mutex_unlock(&rd->lock);
+ if (error)
+ goto out_abort;
+ }
+
+ /*
+ * This thread updated another directory's child dirent that points to
+ * the directory that we're rebuilding, so remember the new dotdot
+ * target.
+ */
+ if (action == XFS_DIRENT_BACKREF_DELTA &&
+ p->ip->i_ino == sc->ip->i_ino &&
+ xchk_iscan_want_live_update(&rd->iscan, p->dp->i_ino)) {
+ if (p->delta > 0) {
+ trace_xrep_dir_add_dirent(sc->tempip, &xfs_name_dotdot,
+ p->dp->i_ino);
+
+ mutex_lock(&rd->lock);
+ rd->parent_ino = p->dp->i_ino;
+ mutex_unlock(&rd->lock);
+ } else {
+ trace_xrep_dir_remove_dirent(sc->tempip,
+ &xfs_name_dotdot, NULLFSINO);
+
+ mutex_lock(&rd->lock);
+ rd->parent_ino = NULLFSINO;
+ mutex_unlock(&rd->lock);
+ }
+ }
+
+ return NOTIFY_DONE;
+out_abort:
+ xchk_iscan_abort(&rd->iscan);
+ return NOTIFY_DONE;
+}
+
/* Set up the filesystem scan so we can regenerate directory entries. */
STATIC int
xrep_dir_setup_scan(
@@ -921,8 +995,24 @@ xrep_dir_setup_scan(
/* Retry iget every tenth of a second for up to 30 seconds. */
xchk_iscan_start(sc, 30000, 100, &rd->iscan);
+ /*
+ * Hook into the dirent update code. The hook only operates on inodes
+ * that were already scanned, and the scanner thread takes each inode's
+ * ILOCK, which means that any in-progress inode updates will finish
+ * before we can scan the inode.
+ */
+ ASSERT(sc->flags & XCHK_FSHOOKS_DIRENTS);
+ xfs_hook_setup(&rd->hooks.delta_hook, xrep_dir_live_update);
+ error = xfs_dirent_hook_add(sc->mp, &rd->hooks);
+ if (error)
+ goto out_scan;
+
return 0;
+out_scan:
+ xchk_iscan_teardown(&rd->iscan);
+ mutex_destroy(&rd->lock);
+ xfblob_destroy(rd->dir_names);
out_entries:
xfarray_destroy(rd->dir_entries);
return error;
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 3/3] xfs: compare generated and existing dirents
2023-04-06 19:14 ` [PATCHSET v11 0/3] xfs: online repair of directories Darrick J. Wong
2023-04-06 19:26 ` [PATCH 1/3] xfs: reconstruct directories from parent pointers Darrick J. Wong
2023-04-06 19:26 ` [PATCH 2/3] xfs: add hooks to do directory updates Darrick J. Wong
@ 2023-04-06 19:27 ` Darrick J. Wong
2 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:27 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Check our work to make sure we found all the dirents that the original
directory had.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/scrub/dir_repair.c | 75 ++++++++++++++++++++++++++++++++++++++++++++-
fs/xfs/scrub/trace.h | 1 +
2 files changed, 74 insertions(+), 2 deletions(-)
diff --git a/fs/xfs/scrub/dir_repair.c b/fs/xfs/scrub/dir_repair.c
index c484e6f36ca0..1e253feaa15d 100644
--- a/fs/xfs/scrub/dir_repair.c
+++ b/fs/xfs/scrub/dir_repair.c
@@ -746,7 +746,10 @@ xrep_dir_scan_dirtree(
return 0;
}
-/* Dump a dirent from the temporary dir. */
+/*
+ * Dump a dirent from the temporary dir and check it against the dir we're
+ * rebuilding. We are not committing any of this.
+ */
STATIC int
xrep_dir_dump_tempdir(
struct xfs_scrub *sc,
@@ -757,8 +760,10 @@ xrep_dir_dump_tempdir(
void *priv)
{
struct xrep_dir *rd = priv;
+ xfs_ino_t child_ino;
bool child_dirent = true;
- int error = 0;
+ bool compare_dirent = true;
+ int error;
/*
* The tempdir was created with a dotdot entry pointing to the root
@@ -781,11 +786,30 @@ xrep_dir_dump_tempdir(
}
if (xrep_dir_samename(name, &xfs_name_dot)) {
child_dirent = false;
+ compare_dirent = false;
ino = sc->ip->i_ino;
}
trace_xrep_dir_dumpname(sc->tempip, name, ino);
+ /* Check that the dir being repaired has the same entry. */
+ if (compare_dirent) {
+ error = xchk_dir_lookup(sc, sc->ip, name, &child_ino);
+ if (error == -ENOENT) {
+ trace_xrep_dir_checkname(sc->ip, name, NULLFSINO);
+ ASSERT(error != -ENOENT);
+ return -EFSCORRUPTED;
+ }
+ if (error)
+ return error;
+
+ if (ino != child_ino) {
+ trace_xrep_dir_checkname(sc->ip, name, child_ino);
+ ASSERT(ino == child_ino);
+ return -EFSCORRUPTED;
+ }
+ }
+
/*
* Set ourselves up to free every dirent in the tempdir because
* directory inactivation won't do it for us. The rest of the online
@@ -801,6 +825,49 @@ xrep_dir_dump_tempdir(
return error;
}
+/*
+ * Dump a dirent from the dir we're rebuilding and check it against the
+ * temporary dir. This assumes that the directory wasn't really corrupt to
+ * begin with.
+ */
+STATIC int
+xrep_dir_dump_baddir(
+ struct xfs_scrub *sc,
+ struct xfs_inode *dp,
+ xfs_dir2_dataptr_t dapos,
+ const struct xfs_name *name,
+ xfs_ino_t ino,
+ void *priv)
+{
+ xfs_ino_t child_ino;
+ int error;
+
+ /* Ignore the directory's dot and dotdot entries. */
+ if (xrep_dir_samename(name, &xfs_name_dotdot) ||
+ xrep_dir_samename(name, &xfs_name_dot))
+ return 0;
+
+ trace_xrep_dir_dumpname(sc->ip, name, ino);
+
+ /* Check that the tempdir has the same entry. */
+ error = xchk_dir_lookup(sc, sc->tempip, name, &child_ino);
+ if (error == -ENOENT) {
+ trace_xrep_dir_checkname(sc->tempip, name, NULLFSINO);
+ ASSERT(error != -ENOENT);
+ return -EFSCORRUPTED;
+ }
+ if (error)
+ return error;
+
+ if (ino != child_ino) {
+ trace_xrep_dir_checkname(sc->tempip, name, child_ino);
+ ASSERT(ino == child_ino);
+ return -EFSCORRUPTED;
+ }
+
+ return 0;
+}
+
/*
* "Commit" the new directory structure to the file that we're repairing.
*
@@ -886,6 +953,10 @@ xrep_dir_rebuild_tree(
if (error)
return error;
+ error = xchk_dir_walk(sc, sc->ip, xrep_dir_dump_baddir, rd);
+ if (error)
+ return error;
+
error = xchk_dir_walk(sc, sc->tempip, xrep_dir_dump_tempdir, rd);
if (error)
return error;
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index af5b5cd6d55b..d97a6bab9a4a 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -1279,6 +1279,7 @@ DEFINE_XREP_DIRENT_CLASS(xrep_dir_createname);
DEFINE_XREP_DIRENT_CLASS(xrep_dir_removename);
DEFINE_XREP_DIRENT_CLASS(xrep_dir_replacename);
DEFINE_XREP_DIRENT_CLASS(xrep_dir_dumpname);
+DEFINE_XREP_DIRENT_CLASS(xrep_dir_checkname);
DECLARE_EVENT_CLASS(xrep_dir_class,
TP_PROTO(struct xfs_inode *dp, xfs_ino_t parent_ino),
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 1/2] xfs: scrub parent pointers
2023-04-06 19:14 ` [PATCHSET v11 0/2] xfs: online checking of parent pointers Darrick J. Wong
@ 2023-04-06 19:27 ` Darrick J. Wong
2023-04-06 19:27 ` [PATCH 2/2] xfs: deferred scrub of " Darrick J. Wong
1 sibling, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:27 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Actually check parent pointers now.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/scrub/parent.c | 280 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 280 insertions(+)
diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c
index d59184a59671..02938a1fcd47 100644
--- a/fs/xfs/scrub/parent.c
+++ b/fs/xfs/scrub/parent.c
@@ -14,9 +14,13 @@
#include "xfs_icache.h"
#include "xfs_dir2.h"
#include "xfs_dir2_priv.h"
+#include "xfs_attr.h"
+#include "xfs_parent.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/readdir.h"
+#include "scrub/listxattr.h"
+#include "scrub/trace.h"
/* Set us up to scrub parents. */
int
@@ -231,6 +235,8 @@ xchk_parent_validate(
return error;
}
+STATIC int xchk_parent_pptr(struct xfs_scrub *sc);
+
/* Scrub a parent pointer. */
int
xchk_parent(
@@ -240,6 +246,9 @@ xchk_parent(
xfs_ino_t parent_ino;
int error;
+ if (xfs_has_parent(mp))
+ return xchk_parent_pptr(sc);
+
/*
* If we're a directory, check that the '..' link points up to
* a directory that has one entry pointing to us.
@@ -282,3 +291,274 @@ xchk_parent(
return xchk_parent_validate(sc, parent_ino);
}
+
+/*
+ * Checking of Parent Pointers
+ * ===========================
+ *
+ * On filesystems with directory parent pointers, we check the referential
+ * integrity by visiting each parent pointer of a child file and checking that
+ * the directory referenced by the pointer actually has a dirent pointing back
+ * to the child file.
+ */
+
+struct xchk_pptrs {
+ struct xfs_scrub *sc;
+
+ /* Scratch buffer for scanning pptr xattrs */
+ struct xfs_parent_name_irec pptr;
+
+ /* Parent of this directory. */
+ xfs_ino_t parent_ino;
+};
+
+/* Look up the dotdot entry so that we can check it as we walk the pptrs. */
+STATIC int
+xchk_parent_dotdot(
+ struct xchk_pptrs *pp)
+{
+ struct xfs_scrub *sc = pp->sc;
+ int error;
+
+ if (!S_ISDIR(VFS_I(sc->ip)->i_mode)) {
+ pp->parent_ino = NULLFSINO;
+ return 0;
+ }
+
+ /* Look up '..' */
+ error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &pp->parent_ino);
+ if (!xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
+ return error;
+ if (!xfs_verify_dir_ino(sc->mp, pp->parent_ino)) {
+ xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
+ return 0;
+ }
+
+ /* Is this the root dir? Then '..' must point to itself. */
+ if (sc->ip == sc->mp->m_rootip && sc->ip->i_ino != pp->parent_ino)
+ xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0);
+
+ return 0;
+}
+
+/*
+ * Try to lock a parent directory for checking dirents. Returns the inode
+ * flags for the locks we now hold, or zero if we failed.
+ */
+STATIC unsigned int
+xchk_parent_lock_dir(
+ struct xfs_scrub *sc,
+ struct xfs_inode *dp)
+{
+ if (!xfs_ilock_nowait(dp, XFS_IOLOCK_SHARED))
+ return 0;
+
+ if (!xfs_ilock_nowait(dp, XFS_ILOCK_SHARED)) {
+ xfs_iunlock(dp, XFS_IOLOCK_SHARED);
+ return 0;
+ }
+
+ if (!xfs_need_iread_extents(&dp->i_df))
+ return XFS_IOLOCK_SHARED | XFS_ILOCK_SHARED;
+
+ xfs_iunlock(dp, XFS_ILOCK_SHARED);
+
+ if (!xfs_ilock_nowait(dp, XFS_ILOCK_EXCL)) {
+ xfs_iunlock(dp, XFS_IOLOCK_SHARED);
+ return 0;
+ }
+
+ return XFS_IOLOCK_SHARED | XFS_ILOCK_EXCL;
+}
+
+/* Check the forward link (dirent) associated with this parent pointer. */
+STATIC int
+xchk_parent_dirent(
+ struct xchk_pptrs *pp,
+ struct xfs_inode *dp)
+{
+ struct xfs_name xname = {
+ .name = pp->pptr.p_name,
+ .len = pp->pptr.p_namelen,
+ };
+ struct xfs_scrub *sc = pp->sc;
+ xfs_ino_t child_ino;
+ int error;
+
+ /*
+ * Use the name attached to this parent pointer to look up the
+ * directory entry in the alleged parent.
+ */
+ error = xchk_dir_lookup(sc, dp, &xname, &child_ino);
+ if (error == -ENOENT) {
+ xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);
+ return 0;
+ }
+ if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))
+ return error;
+
+ /* Does the inode number match? */
+ if (child_ino != sc->ip->i_ino) {
+ xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);
+ return 0;
+ }
+
+ /*
+ * If we're scanning a directory, we should only ever encounter a
+ * single parent pointer, and it should match the dotdot entry. We set
+ * the parent_ino from the dotdot entry before the scan, so compare it
+ * now.
+ */
+ if (!S_ISDIR(VFS_I(sc->ip)->i_mode))
+ return 0;
+
+ if (pp->parent_ino != dp->i_ino) {
+ xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
+ return 0;
+ }
+
+ pp->parent_ino = NULLFSINO;
+ return 0;
+}
+
+/* Try to grab a parent directory. */
+STATIC int
+xchk_parent_iget(
+ struct xchk_pptrs *pp,
+ struct xfs_inode **dpp)
+{
+ struct xfs_scrub *sc = pp->sc;
+ struct xfs_inode *ip;
+ int error;
+
+ /* Validate inode number. */
+ error = xfs_dir_ino_validate(sc->mp, pp->pptr.p_ino);
+ if (error) {
+ xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
+ return -ECANCELED;
+ }
+
+ error = xchk_iget(sc, pp->pptr.p_ino, &ip);
+ if (error == -EINVAL || error == -ENOENT) {
+ xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
+ return -ECANCELED;
+ }
+ if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))
+ return error;
+
+ /* The parent must be a directory. */
+ if (!S_ISDIR(VFS_I(ip)->i_mode)) {
+ xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);
+ goto out_rele;
+ }
+
+ /* Validate generation number. */
+ if (VFS_I(ip)->i_generation != pp->pptr.p_gen) {
+ xchk_fblock_xref_set_corrupt(sc, XFS_ATTR_FORK, 0);
+ goto out_rele;
+ }
+
+ *dpp = ip;
+ return 0;
+out_rele:
+ xchk_irele(sc, ip);
+ return 0;
+}
+
+/*
+ * Walk an xattr of a file. If this xattr is a parent pointer, follow it up
+ * to a parent directory and check that the parent has a dirent pointing back
+ * to us.
+ */
+STATIC int
+xchk_parent_scan_attr(
+ struct xfs_scrub *sc,
+ struct xfs_inode *ip,
+ unsigned int attr_flags,
+ const unsigned char *name,
+ unsigned int namelen,
+ const void *value,
+ unsigned int valuelen,
+ void *priv)
+{
+ struct xchk_pptrs *pp = priv;
+ struct xfs_inode *dp = NULL;
+ const struct xfs_parent_name_rec *rec = (const void *)name;
+ unsigned int lockmode;
+ int error;
+
+ /* Ignore incomplete xattrs */
+ if (attr_flags & XFS_ATTR_INCOMPLETE)
+ return 0;
+
+ /* Ignore anything that isn't a parent pointer. */
+ if (!(attr_flags & XFS_ATTR_PARENT))
+ return 0;
+
+ /* Does the ondisk parent pointer structure make sense? */
+ if (!xfs_parent_namecheck(sc->mp, rec, namelen, attr_flags)) {
+ xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
+ return -ECANCELED;
+ }
+
+ if (!xfs_parent_valuecheck(sc->mp, value, valuelen)) {
+ xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
+ return -ECANCELED;
+ }
+
+ xfs_parent_irec_from_disk(&pp->pptr, rec, value, valuelen);
+
+ error = xchk_parent_iget(pp, &dp);
+ if (error)
+ return error;
+ if (!dp)
+ return 0;
+
+ /* Try to lock the inode. */
+ lockmode = xchk_parent_lock_dir(sc, dp);
+ if (!lockmode) {
+ xchk_set_incomplete(sc);
+ error = -ECANCELED;
+ goto out_rele;
+ }
+
+ error = xchk_parent_dirent(pp, dp);
+ if (error)
+ goto out_unlock;
+
+out_unlock:
+ xfs_iunlock(dp, lockmode);
+out_rele:
+ xchk_irele(sc, dp);
+ return error;
+}
+
+/* Check parent pointers of a file. */
+STATIC int
+xchk_parent_pptr(
+ struct xfs_scrub *sc)
+{
+ struct xchk_pptrs *pp;
+ int error;
+
+ pp = kvzalloc(sizeof(struct xchk_pptrs), XCHK_GFP_FLAGS);
+ if (!pp)
+ return -ENOMEM;
+ pp->sc = sc;
+
+ error = xchk_parent_dotdot(pp);
+ if (error)
+ goto out_pp;
+
+ error = xchk_xattr_walk(sc, sc->ip, xchk_parent_scan_attr, pp);
+ if (error == -ECANCELED) {
+ error = 0;
+ goto out_pp;
+ }
+ if (error)
+ goto out_pp;
+
+out_pp:
+ kvfree(pp);
+ return error;
+}
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 2/2] xfs: deferred scrub of parent pointers
2023-04-06 19:14 ` [PATCHSET v11 0/2] xfs: online checking of parent pointers Darrick J. Wong
2023-04-06 19:27 ` [PATCH 1/2] xfs: scrub " Darrick J. Wong
@ 2023-04-06 19:27 ` Darrick J. Wong
1 sibling, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:27 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If the trylock-based dirent check fails, retain those parent pointers
and check them at the end. This may involve dropping the locks on the
file being scanned, so yay.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/Makefile | 2
fs/xfs/libxfs/xfs_parent.c | 42 ++++++++
fs/xfs/libxfs/xfs_parent.h | 10 ++
fs/xfs/scrub/parent.c | 229 +++++++++++++++++++++++++++++++++++++++++++-
fs/xfs/scrub/trace.h | 33 ++++++
5 files changed, 311 insertions(+), 5 deletions(-)
diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index 0a83cd9585d1..bd9b65dcc802 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -169,6 +169,7 @@ xfs-y += $(addprefix scrub/, \
scrub.o \
symlink.o \
xfarray.o \
+ xfblob.o \
xfile.o \
)
@@ -182,7 +183,6 @@ xfs-y += $(addprefix scrub/, \
dir_repair.o \
repair.o \
tempfile.o \
- xfblob.o \
)
endif
endif
diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c
index 37a3e73049d9..1f3d50fb424d 100644
--- a/fs/xfs/libxfs/xfs_parent.c
+++ b/fs/xfs/libxfs/xfs_parent.c
@@ -315,3 +315,45 @@ xfs_parent_irec_hashname(
irec->p_namehash = xfs_dir2_hashname(mp, &dname);
}
+
+static inline void
+xfs_parent_scratch_init(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip,
+ const struct xfs_parent_name_irec *pptr,
+ struct xfs_parent_scratch *scr)
+{
+ memset(&scr->args, 0, sizeof(struct xfs_da_args));
+ scr->args.attr_filter = XFS_ATTR_PARENT;
+ scr->args.dp = ip;
+ scr->args.geo = ip->i_mount->m_attr_geo;
+ scr->args.name = (const unsigned char *)&scr->rec;
+ scr->args.namelen = sizeof(struct xfs_parent_name_rec);
+ scr->args.op_flags = XFS_DA_OP_NVLOOKUP;
+ scr->args.trans = tp;
+ scr->args.value = (void *)pptr->p_name;
+ scr->args.valuelen = pptr->p_namelen;
+ scr->args.whichfork = XFS_ATTR_FORK;
+ scr->args.hashval = xfs_da_hashname((const void *)&scr->rec,
+ sizeof(struct xfs_parent_name_rec));
+}
+
+/*
+ * Look up the @name associated with the parent pointer (@pptr) of @ip.
+ * Caller must hold at least ILOCK_SHARED. Returns 0 if the pointer is found,
+ * -ENOATTR if there is no match, or a negative errno. The scratchpad need not
+ * be initialized.
+ */
+int
+xfs_parent_lookup(
+ struct xfs_trans *tp,
+ struct xfs_inode *ip,
+ const struct xfs_parent_name_irec *pptr,
+ struct xfs_parent_scratch *scr)
+{
+ xfs_parent_irec_to_disk(&scr->rec, pptr);
+ xfs_parent_scratch_init(tp, ip, pptr, scr);
+ scr->args.op_flags |= XFS_DA_OP_OKNOENT;
+
+ return xfs_attr_get_ilocked(&scr->args);
+}
diff --git a/fs/xfs/libxfs/xfs_parent.h b/fs/xfs/libxfs/xfs_parent.h
index 1cc4778968df..a7eea91960cd 100644
--- a/fs/xfs/libxfs/xfs_parent.h
+++ b/fs/xfs/libxfs/xfs_parent.h
@@ -101,4 +101,14 @@ void xfs_parent_irec_to_disk(struct xfs_parent_name_rec *rec,
void xfs_parent_irec_hashname(struct xfs_mount *mp,
struct xfs_parent_name_irec *irec);
+/* Scratchpad memory so that raw parent operations don't burn stack space. */
+struct xfs_parent_scratch {
+ struct xfs_parent_name_rec rec;
+ struct xfs_da_args args;
+};
+
+int xfs_parent_lookup(struct xfs_trans *tp, struct xfs_inode *ip,
+ const struct xfs_parent_name_irec *pptr,
+ struct xfs_parent_scratch *scratch);
+
#endif /* __XFS_PARENT_H__ */
diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c
index 02938a1fcd47..efdd4cac89e6 100644
--- a/fs/xfs/scrub/parent.c
+++ b/fs/xfs/scrub/parent.c
@@ -20,6 +20,9 @@
#include "scrub/common.h"
#include "scrub/readdir.h"
#include "scrub/listxattr.h"
+#include "scrub/xfile.h"
+#include "scrub/xfarray.h"
+#include "scrub/xfblob.h"
#include "scrub/trace.h"
/* Set us up to scrub parents. */
@@ -302,14 +305,39 @@ xchk_parent(
* to the child file.
*/
+/* Deferred parent pointer entry that we saved for later. */
+struct xchk_pptr {
+ /* Cookie for retrieval of the pptr name. */
+ xfblob_cookie name_cookie;
+
+ /* Parent pointer attr key. */
+ xfs_ino_t p_ino;
+ uint32_t p_gen;
+
+ /* Length of the pptr name. */
+ uint8_t namelen;
+};
+
struct xchk_pptrs {
struct xfs_scrub *sc;
/* Scratch buffer for scanning pptr xattrs */
struct xfs_parent_name_irec pptr;
+ /* Fixed-size array of xchk_pptr structures. */
+ struct xfarray *pptr_entries;
+
+ /* Blobs containing parent pointer names. */
+ struct xfblob *pptr_names;
+
/* Parent of this directory. */
xfs_ino_t parent_ino;
+
+ /* If we've cycled the ILOCK, we must revalidate all deferred pptrs. */
+ bool need_revalidate;
+
+ /* xattr key and da args for parent pointer revalidation. */
+ struct xfs_parent_scratch pptr_scratch;
};
/* Look up the dotdot entry so that we can check it as we walk the pptrs. */
@@ -517,8 +545,25 @@ xchk_parent_scan_attr(
/* Try to lock the inode. */
lockmode = xchk_parent_lock_dir(sc, dp);
if (!lockmode) {
- xchk_set_incomplete(sc);
- error = -ECANCELED;
+ struct xchk_pptr save_pp = {
+ .p_ino = pp->pptr.p_ino,
+ .p_gen = pp->pptr.p_gen,
+ .namelen = pp->pptr.p_namelen,
+ };
+
+ /* Couldn't lock the inode, so save the pptr for later. */
+ trace_xchk_parent_defer(sc->ip, pp->pptr.p_name,
+ pp->pptr.p_namelen, dp->i_ino);
+
+ error = xfblob_store(pp->pptr_names, &save_pp.name_cookie,
+ pp->pptr.p_name, pp->pptr.p_namelen);
+ if (xchk_fblock_process_error(sc, XFS_ATTR_FORK, 0, &error))
+ goto out_rele;
+
+ error = xfarray_append(pp->pptr_entries, &save_pp);
+ if (xchk_fblock_process_error(sc, XFS_ATTR_FORK, 0, &error))
+ goto out_rele;
+
goto out_rele;
}
@@ -533,6 +578,161 @@ xchk_parent_scan_attr(
return error;
}
+/*
+ * Revalidate a parent pointer that we collected in the past but couldn't check
+ * because of lock contention. Returns 0 if the parent pointer is still valid,
+ * -ENOENT if it has gone away on us, or a negative errno.
+ */
+STATIC int
+xchk_parent_revalidate_pptr(
+ struct xchk_pptrs *pp)
+{
+ struct xfs_scrub *sc = pp->sc;
+ int error;
+
+ error = xfs_parent_lookup(sc->tp, sc->ip, &pp->pptr,
+ &pp->pptr_scratch);
+ if (error == -ENOATTR) {
+ /* Parent pointer went away, nothing to revalidate. */
+ return -ENOENT;
+ }
+
+ return error;
+}
+
+/*
+ * Check a parent pointer the slow way, which means we cycle locks a bunch
+ * and put up with revalidation until we get it done.
+ */
+STATIC int
+xchk_parent_slow_pptr(
+ struct xchk_pptrs *pp,
+ struct xchk_pptr *pptr)
+{
+ struct xfs_scrub *sc = pp->sc;
+ struct xfs_inode *dp = NULL;
+ unsigned int lockmode;
+ int error;
+
+ /* Restore the saved parent pointer into the irec. */
+ pp->pptr.p_ino = pptr->p_ino;
+ pp->pptr.p_gen = pptr->p_gen;
+
+ error = xfblob_load(pp->pptr_names, pptr->name_cookie, pp->pptr.p_name,
+ pptr->namelen);
+ if (error)
+ return error;
+ pp->pptr.p_name[MAXNAMELEN - 1] = 0;
+ pp->pptr.p_namelen = pptr->namelen;
+ xfs_parent_irec_hashname(sc->mp, &pp->pptr);
+
+ /* Check that the deferred parent pointer still exists. */
+ if (pp->need_revalidate) {
+ error = xchk_parent_revalidate_pptr(pp);
+ if (error == -ENOENT)
+ return 0;
+ if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0,
+ &error))
+ return error;
+ }
+
+ error = xchk_parent_iget(pp, &dp);
+ if (error)
+ return error;
+ if (!dp)
+ return 0;
+
+ /*
+ * If we can grab both IOLOCK and ILOCK of the alleged parent, we
+ * can proceed with the validation.
+ */
+ lockmode = xchk_parent_lock_dir(sc, dp);
+ if (lockmode)
+ goto check_dirent;
+
+ /*
+ * We couldn't lock the parent dir. Drop all the locks and try to
+ * get them again, one at a time.
+ */
+ xchk_iunlock(sc, sc->ilock_flags);
+ pp->need_revalidate = true;
+
+ trace_xchk_parent_slowpath(sc->ip, pp->pptr.p_name, pptr->namelen,
+ dp->i_ino);
+
+ while (true) {
+ xchk_ilock(sc, XFS_IOLOCK_EXCL);
+ if (xfs_ilock_nowait(dp, XFS_IOLOCK_SHARED)) {
+ xchk_ilock(sc, XFS_ILOCK_EXCL);
+ if (xfs_ilock_nowait(dp, XFS_ILOCK_EXCL)) {
+ break;
+ }
+ xchk_iunlock(sc, XFS_ILOCK_EXCL);
+ }
+ xchk_iunlock(sc, XFS_IOLOCK_EXCL);
+
+ if (xchk_should_terminate(sc, &error))
+ goto out_rele;
+
+ delay(1);
+ }
+ lockmode = XFS_IOLOCK_SHARED | XFS_ILOCK_EXCL;
+
+ /*
+ * If we didn't already find a parent pointer matching the dotdot
+ * entry, re-query the dotdot entry so that we can validate it.
+ */
+ if (pp->parent_ino != NULLFSINO) {
+ error = xchk_parent_dotdot(pp);
+ if (error)
+ goto out_unlock;
+ }
+
+ /* Revalidate the parent pointer now that we cycled locks. */
+ error = xchk_parent_revalidate_pptr(pp);
+ if (error == -ENOENT)
+ goto out_unlock;
+ if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))
+ goto out_unlock;
+
+check_dirent:
+ error = xchk_parent_dirent(pp, dp);
+out_unlock:
+ xfs_iunlock(dp, lockmode);
+out_rele:
+ xchk_irele(sc, dp);
+ return error;
+}
+
+/* Check all the parent pointers that we deferred the first time around. */
+STATIC int
+xchk_parent_finish_slow_pptrs(
+ struct xchk_pptrs *pp)
+{
+ xfarray_idx_t array_cur;
+ int error;
+
+ foreach_xfarray_idx(pp->pptr_entries, array_cur) {
+ struct xchk_pptr pptr;
+
+ if (pp->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ return 0;
+
+ error = xfarray_load(pp->pptr_entries, array_cur, &pptr);
+ if (error)
+ return error;
+
+ error = xchk_parent_slow_pptr(pp, &pptr);
+ if (error)
+ return error;
+ }
+
+ /* Empty out both xfiles now that we've checked everything. */
+ xfarray_truncate(pp->pptr_entries);
+ xfblob_truncate(pp->pptr_names);
+ return 0;
+}
+
/* Check parent pointers of a file. */
STATIC int
xchk_parent_pptr(
@@ -550,14 +750,35 @@ xchk_parent_pptr(
if (error)
goto out_pp;
+ /*
+ * Set up some staging memory for parent pointers that we can't check
+ * due to locking contention.
+ */
+ error = xfarray_create(sc->mp, "pptr entries", 0,
+ sizeof(struct xchk_pptr), &pp->pptr_entries);
+ if (error)
+ goto out_pp;
+
+ error = xfblob_create(sc->mp, "pptr names", &pp->pptr_names);
+ if (error)
+ goto out_entries;
+
error = xchk_xattr_walk(sc, sc->ip, xchk_parent_scan_attr, pp);
if (error == -ECANCELED) {
error = 0;
- goto out_pp;
+ goto out_names;
}
if (error)
- goto out_pp;
+ goto out_names;
+ error = xchk_parent_finish_slow_pptrs(pp);
+ if (error)
+ goto out_names;
+
+out_names:
+ xfblob_destroy(pp->pptr_names);
+out_entries:
+ xfarray_destroy(pp->pptr_entries);
out_pp:
kvfree(pp);
return error;
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index d97a6bab9a4a..7eb6dcb3aa49 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -954,6 +954,39 @@ TRACE_EVENT(xchk_nlinks_live_update,
__get_str(name))
);
+DECLARE_EVENT_CLASS(xchk_pptr_class,
+ TP_PROTO(struct xfs_inode *ip, const unsigned char *name,
+ unsigned int namelen, xfs_ino_t parent_ino),
+ TP_ARGS(ip, name, namelen, parent_ino),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(xfs_ino_t, ino)
+ __field(unsigned int, namelen)
+ __dynamic_array(char, name, namelen)
+ __field(xfs_ino_t, parent_ino)
+ ),
+ TP_fast_assign(
+ __entry->dev = ip->i_mount->m_super->s_dev;
+ __entry->ino = ip->i_ino;
+ __entry->namelen = namelen;
+ memcpy(__get_str(name), name, namelen);
+ __entry->parent_ino = parent_ino;
+ ),
+ TP_printk("dev %d:%d ino 0x%llx name '%.*s' parent_ino 0x%llx",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->ino,
+ __entry->namelen,
+ __get_str(name),
+ __entry->parent_ino)
+)
+#define DEFINE_XCHK_PPTR_CLASS(name) \
+DEFINE_EVENT(xchk_pptr_class, name, \
+ TP_PROTO(struct xfs_inode *ip, const unsigned char *name, \
+ unsigned int namelen, xfs_ino_t parent_ino), \
+ TP_ARGS(ip, name, namelen, parent_ino))
+DEFINE_XCHK_PPTR_CLASS(xchk_parent_defer);
+DEFINE_XCHK_PPTR_CLASS(xchk_parent_slowpath);
+
/* repair tracepoints */
#if IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR)
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 1/3] xfs: repair parent pointers by scanning directories
2023-04-06 19:15 ` [PATCHSET v11 0/3] xfs: online checking " Darrick J. Wong
@ 2023-04-06 19:27 ` Darrick J. Wong
2023-04-06 19:28 ` [PATCH 2/3] xfs: repair parent pointers with live scan hooks Darrick J. Wong
2023-04-06 19:28 ` [PATCH 3/3] xfs: compare generated and existing parent pointers Darrick J. Wong
2 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:27 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Walk the filesystem to rebuild parent pointer information.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/Makefile | 1
fs/xfs/libxfs/xfs_parent.c | 18 +
fs/xfs/libxfs/xfs_parent.h | 4
fs/xfs/scrub/parent.c | 10 +
fs/xfs/scrub/parent_repair.c | 614 ++++++++++++++++++++++++++++++++++++++++++
fs/xfs/scrub/repair.h | 4
fs/xfs/scrub/scrub.c | 2
fs/xfs/scrub/trace.c | 2
fs/xfs/scrub/trace.h | 71 +++++
9 files changed, 725 insertions(+), 1 deletion(-)
create mode 100644 fs/xfs/scrub/parent_repair.c
diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile
index bd9b65dcc802..46b6aa577373 100644
--- a/fs/xfs/Makefile
+++ b/fs/xfs/Makefile
@@ -181,6 +181,7 @@ ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y)
xfs-y += $(addprefix scrub/, \
agheader_repair.o \
dir_repair.o \
+ parent_repair.o \
repair.o \
tempfile.o \
)
diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c
index 1f3d50fb424d..59fe4181bedd 100644
--- a/fs/xfs/libxfs/xfs_parent.c
+++ b/fs/xfs/libxfs/xfs_parent.c
@@ -357,3 +357,21 @@ xfs_parent_lookup(
return xfs_attr_get_ilocked(&scr->args);
}
+
+/*
+ * Attach the parent pointer (@pptr -> @name) to @ip immediately. Caller must
+ * not have a transaction or hold the ILOCK. The update will not use logged
+ * xattrs. This is for specialized repair functions only. The scratchpad need
+ * not be initialized.
+ */
+int
+xfs_parent_set(
+ struct xfs_inode *ip,
+ const struct xfs_parent_name_irec *pptr,
+ struct xfs_parent_scratch *scr)
+{
+ xfs_parent_irec_to_disk(&scr->rec, pptr);
+ xfs_parent_scratch_init(NULL, ip, pptr, scr);
+
+ return xfs_attr_set(&scr->args);
+}
diff --git a/fs/xfs/libxfs/xfs_parent.h b/fs/xfs/libxfs/xfs_parent.h
index a7eea91960cd..da8dc689221c 100644
--- a/fs/xfs/libxfs/xfs_parent.h
+++ b/fs/xfs/libxfs/xfs_parent.h
@@ -111,4 +111,8 @@ int xfs_parent_lookup(struct xfs_trans *tp, struct xfs_inode *ip,
const struct xfs_parent_name_irec *pptr,
struct xfs_parent_scratch *scratch);
+int xfs_parent_set(struct xfs_inode *ip,
+ const struct xfs_parent_name_irec *pptr,
+ struct xfs_parent_scratch *scratch);
+
#endif /* __XFS_PARENT_H__ */
diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c
index efdd4cac89e6..d5bcec430115 100644
--- a/fs/xfs/scrub/parent.c
+++ b/fs/xfs/scrub/parent.c
@@ -10,6 +10,7 @@
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_log_format.h"
+#include "xfs_trans.h"
#include "xfs_inode.h"
#include "xfs_icache.h"
#include "xfs_dir2.h"
@@ -24,12 +25,21 @@
#include "scrub/xfarray.h"
#include "scrub/xfblob.h"
#include "scrub/trace.h"
+#include "scrub/repair.h"
/* Set us up to scrub parents. */
int
xchk_setup_parent(
struct xfs_scrub *sc)
{
+ int error;
+
+ if (xchk_could_repair(sc)) {
+ error = xrep_setup_parent(sc);
+ if (error)
+ return error;
+ }
+
return xchk_setup_inode_contents(sc, 0);
}
diff --git a/fs/xfs/scrub/parent_repair.c b/fs/xfs/scrub/parent_repair.c
new file mode 100644
index 000000000000..30d2a81e4df2
--- /dev/null
+++ b/fs/xfs/scrub/parent_repair.c
@@ -0,0 +1,614 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2023 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_defer.h"
+#include "xfs_bit.h"
+#include "xfs_log_format.h"
+#include "xfs_trans.h"
+#include "xfs_sb.h"
+#include "xfs_inode.h"
+#include "xfs_icache.h"
+#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
+#include "xfs_dir2.h"
+#include "xfs_dir2_priv.h"
+#include "xfs_bmap.h"
+#include "xfs_quota.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_trans_space.h"
+#include "xfs_bmap_util.h"
+#include "xfs_attr.h"
+#include "xfs_parent.h"
+#include "scrub/xfs_scrub.h"
+#include "scrub/scrub.h"
+#include "scrub/common.h"
+#include "scrub/trace.h"
+#include "scrub/repair.h"
+#include "scrub/tempfile.h"
+#include "scrub/iscan.h"
+#include "scrub/readdir.h"
+#include "scrub/listxattr.h"
+#include "scrub/xfile.h"
+#include "scrub/xfarray.h"
+#include "scrub/xfblob.h"
+
+/*
+ * Parent Pointer Repairs
+ * ======================
+ *
+ * Reconstruct a file's parent pointers by visiting each dirent of each
+ * directory in the filesystem and translating the relevant dirents into parent
+ * pointers. Translation occurs by adding new parent pointers to a temporary
+ * file, which formats the ondisk extended attribute blocks. In the final
+ * version of this code, we'll use the atomic extent swap code to exchange the
+ * entire xattr structure of the file being repaired and the temporary file,
+ * but for this PoC we omit the commit to reduce the amount of code that has to
+ * be ported.
+ *
+ * Because we have to scan the entire filesystem, the next patch introduces the
+ * inode scan and live update hooks so that the rebuilder can be kept aware of
+ * filesystem updates being made to this file's parents by other threads.
+ * Parent pointer translation therefore requires two steps to avoid problems
+ * with lock contention and to keep ondisk tempdir updates out of the hook
+ * path.
+ *
+ * Every time the filesystem scanner or the live update hook code encounter a
+ * directory operation relevant to this rebuilder, they will write a record of
+ * the createpptr/removepptr operation to an xfarray. Parent pointer names are
+ * stored in an xfblob structure. At opportune times, these stashed updates
+ * will be read from the xfarray and committed (individually) to the temporary
+ * file's parent pointers.
+ *
+ * When the filesystem scan is complete, we relock both the file and the
+ * tempfile, and finish any stashed operations. At that point, had we copied
+ * the extended attributes, we would be ready to exchange the attribute data
+ * fork mappings. This cannot happen until two patchsets get merged: the first
+ * allows callers to specify the owning inode number explicitly; and the second
+ * is the atomic extent swap series.
+ *
+ * For now we'll simply compare the two files parent pointers and complain
+ * about discrepancies.
+ */
+
+/* Maximum memory usage for the tempfile log, in bytes. */
+#define MAX_PPTR_STASH_SIZE (32ULL << 10)
+
+/* Create a parent pointer in the tempfile. */
+#define XREP_PPTR_ADD (1)
+
+/* Remove a parent pointer from the tempfile. */
+#define XREP_PPTR_REMOVE (2)
+
+/* A stashed parent pointer update. */
+struct xrep_pptr {
+ /* Cookie for retrieval of the pptr name. */
+ xfblob_cookie name_cookie;
+
+ /* Parent pointer attr key. */
+ xfs_ino_t p_ino;
+ uint32_t p_gen;
+
+ /* Length of the pptr name. */
+ uint8_t namelen;
+
+ /* XREP_PPTR_{ADD,REMOVE} */
+ uint8_t action;
+};
+
+struct xrep_pptrs {
+ struct xfs_scrub *sc;
+
+ /* Inode scan cursor. */
+ struct xchk_iscan iscan;
+
+ /* Scratch buffer for scanning dirents to create pptr xattrs */
+ struct xfs_parent_name_irec pptr;
+
+ /* xattr key and da args for parent pointer replay. */
+ struct xfs_parent_scratch pptr_scratch;
+
+ /* Mutex protecting parent_ptrs, pptr_names. */
+ struct mutex lock;
+
+ /* Stashed parent pointer updates. */
+ struct xfarray *parent_ptrs;
+
+ /* Parent pointer names. */
+ struct xfblob *pptr_names;
+};
+
+/* Tear down all the incore stuff we created. */
+static void
+xrep_pptr_teardown(
+ struct xrep_pptrs *rp)
+{
+ xchk_iscan_teardown(&rp->iscan);
+ mutex_destroy(&rp->lock);
+ xfblob_destroy(rp->pptr_names);
+ xfarray_destroy(rp->parent_ptrs);
+}
+
+/* Set up for a parent pointer repair. */
+int
+xrep_setup_parent(
+ struct xfs_scrub *sc)
+{
+ struct xrep_pptrs *rp;
+ int error;
+
+ error = xrep_tempfile_create(sc, S_IFREG);
+ if (error)
+ return error;
+
+ rp = kvzalloc(sizeof(struct xrep_pptrs), XCHK_GFP_FLAGS);
+ if (!rp)
+ return -ENOMEM;
+
+ sc->buf = rp;
+ rp->sc = sc;
+ return 0;
+}
+
+/* Are these two parent pointer names the same? */
+static inline bool
+xrep_pptr_samename(
+ const struct xfs_name *n1,
+ const struct xfs_name *n2)
+{
+ return n1->len == n2->len && !memcmp(n1->name, n2->name, n1->len);
+}
+
+/* Update the temporary file's parent pointers with a stashed update. */
+STATIC int
+xrep_pptr_replay_update(
+ struct xrep_pptrs *rp,
+ const struct xrep_pptr *pptr)
+{
+ struct xfs_scrub *sc = rp->sc;
+
+ rp->pptr.p_ino = pptr->p_ino;
+ rp->pptr.p_gen = pptr->p_gen;
+ rp->pptr.p_namelen = pptr->namelen;
+ xfs_parent_irec_hashname(sc->mp, &rp->pptr);
+
+ if (pptr->action == XREP_PPTR_ADD) {
+ /* Create parent pointer. */
+ trace_xrep_pptr_createname(sc->tempip, &rp->pptr);
+
+ return xfs_parent_set(sc->tempip, &rp->pptr, &rp->pptr_scratch);
+ }
+
+ ASSERT(0);
+ return -EOPNOTSUPP;
+}
+
+/*
+ * Flush stashed parent pointer updates that have been recorded by the scanner.
+ * This is done to reduce the memory requirements of the parent pointer
+ * rebuild, since files can have a lot of hardlinks and the fs can be busy.
+ *
+ * Caller must not hold transactions or ILOCKs. Caller must hold the tempfile
+ * IOLOCK.
+ */
+STATIC int
+xrep_pptr_replay_updates(
+ struct xrep_pptrs *rp)
+{
+ xfarray_idx_t array_cur;
+ int error;
+
+ mutex_lock(&rp->lock);
+ foreach_xfarray_idx(rp->parent_ptrs, array_cur) {
+ struct xrep_pptr pptr;
+
+ error = xfarray_load(rp->parent_ptrs, array_cur, &pptr);
+ if (error)
+ goto out_unlock;
+
+ error = xfblob_load(rp->pptr_names, pptr.name_cookie,
+ rp->pptr.p_name, pptr.namelen);
+ if (error)
+ goto out_unlock;
+ rp->pptr.p_name[MAXNAMELEN - 1] = 0;
+ mutex_unlock(&rp->lock);
+
+ error = xrep_pptr_replay_update(rp, &pptr);
+ if (error)
+ return error;
+
+ mutex_lock(&rp->lock);
+ }
+
+ /* Empty out both arrays now that we've added the entries. */
+ xfarray_truncate(rp->parent_ptrs);
+ xfblob_truncate(rp->pptr_names);
+ mutex_unlock(&rp->lock);
+ return 0;
+out_unlock:
+ mutex_unlock(&rp->lock);
+ return error;
+}
+
+/*
+ * Remember that we want to create a parent pointer in the tempfile. These
+ * stashed actions will be replayed later.
+ */
+STATIC int
+xrep_pptr_add_pointer(
+ struct xrep_pptrs *rp,
+ const struct xfs_name *name,
+ const struct xfs_inode *dp)
+{
+ struct xrep_pptr pptr = {
+ .action = XREP_PPTR_ADD,
+ .namelen = name->len,
+ .p_ino = dp->i_ino,
+ .p_gen = VFS_IC(dp)->i_generation,
+ };
+ int error;
+
+ trace_xrep_pptr_add_pointer(rp->sc->tempip, dp, name);
+
+ error = xfblob_store(rp->pptr_names, &pptr.name_cookie, name->name,
+ name->len);
+ if (error)
+ return error;
+
+ return xfarray_append(rp->parent_ptrs, &pptr);
+}
+
+/*
+ * Examine an entry of a directory. If this dirent leads us back to the file
+ * whose parent pointers we're rebuilding, add a pptr to the temporary
+ * directory.
+ */
+STATIC int
+xrep_pptr_scan_dirent(
+ struct xfs_scrub *sc,
+ struct xfs_inode *dp,
+ xfs_dir2_dataptr_t dapos,
+ const struct xfs_name *name,
+ xfs_ino_t ino,
+ void *priv)
+{
+ struct xrep_pptrs *rp = priv;
+ int error;
+
+ /* Dirent doesn't point to this directory. */
+ if (ino != rp->sc->ip->i_ino)
+ return 0;
+
+ /* No weird looking names. */
+ if (!xfs_dir2_namecheck(name->name, name->len))
+ return -EFSCORRUPTED;
+
+ /* No mismatching ftypes. */
+ if (name->type != xfs_mode_to_ftype(VFS_I(sc->ip)->i_mode))
+ return -EFSCORRUPTED;
+
+ /* Don't pick up dot or dotdot entries; we only want child dirents. */
+ if (xrep_pptr_samename(name, &xfs_name_dotdot) ||
+ xrep_pptr_samename(name, &xfs_name_dot))
+ return 0;
+
+ /*
+ * Transform this dirent into a parent pointer and queue it for later
+ * addition to the temporary file.
+ */
+ mutex_lock(&rp->lock);
+ error = xrep_pptr_add_pointer(rp, name, dp);
+ mutex_unlock(&rp->lock);
+ return error;
+}
+
+/*
+ * Decide if we want to look for dirents in this directory. Skip the file
+ * being repaired and any files being used to stage repairs.
+ */
+static inline bool
+xrep_pptr_want_scan(
+ struct xrep_pptrs *rp,
+ const struct xfs_inode *ip)
+{
+ return ip != rp->sc->ip && !xrep_is_tempfile(ip);
+}
+
+/*
+ * Take ILOCK on a file that we want to scan.
+ *
+ * Select ILOCK_EXCL if the file is a directory with an unloaded data bmbt.
+ * Otherwise, take ILOCK_SHARED.
+ */
+static inline unsigned int
+xrep_pptr_scan_ilock(
+ struct xrep_pptrs *rp,
+ struct xfs_inode *ip)
+{
+ uint lock_mode = XFS_ILOCK_SHARED;
+
+ /* Still need to take the shared ILOCK to advance the iscan cursor. */
+ if (!xrep_pptr_want_scan(rp, ip))
+ goto lock;
+
+ if (S_ISDIR(VFS_I(ip)->i_mode) && xfs_need_iread_extents(&ip->i_df)) {
+ lock_mode = XFS_ILOCK_EXCL;
+ goto lock;
+ }
+
+lock:
+ xfs_ilock(ip, lock_mode);
+ return lock_mode;
+}
+
+/*
+ * Scan this file for relevant child dirents that point to the file whose
+ * parent pointers we're rebuilding.
+ */
+STATIC int
+xrep_pptr_scan_file(
+ struct xrep_pptrs *rp,
+ struct xfs_inode *ip)
+{
+ unsigned int lock_mode;
+ int error = 0;
+
+ lock_mode = xrep_pptr_scan_ilock(rp, ip);
+
+ if (!xrep_pptr_want_scan(rp, ip))
+ goto scan_done;
+
+ if (S_ISDIR(VFS_I(ip)->i_mode)) {
+ error = xchk_dir_walk(rp->sc, ip, xrep_pptr_scan_dirent, rp);
+ if (error)
+ goto scan_done;
+ }
+
+scan_done:
+ xchk_iscan_mark_visited(&rp->iscan, ip);
+ xfs_iunlock(ip, lock_mode);
+ return error;
+}
+
+/* Scan all files in the filesystem for parent pointers. */
+STATIC int
+xrep_pptr_scan_dirtree(
+ struct xrep_pptrs *rp)
+{
+ struct xfs_scrub *sc = rp->sc;
+ struct xfs_inode *ip;
+ int error;
+
+ /*
+ * Filesystem scans are time consuming. Drop the file ILOCK and all
+ * other resources for the duration of the scan and hope for the best.
+ */
+ xchk_trans_cancel(sc);
+ if (sc->ilock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL))
+ xchk_iunlock(sc, sc->ilock_flags & (XFS_ILOCK_SHARED |
+ XFS_ILOCK_EXCL));
+ error = xchk_trans_alloc_empty(sc);
+ if (error)
+ return error;
+
+ while ((error = xchk_iscan_iter(&rp->iscan, &ip)) == 1) {
+ uint64_t mem_usage;
+
+ error = xrep_pptr_scan_file(rp, ip);
+ xchk_irele(sc, ip);
+ if (error)
+ break;
+
+ /* Flush stashed pptr updates to constrain memory usage. */
+ mutex_lock(&rp->lock);
+ mem_usage = xfarray_bytes(rp->parent_ptrs) +
+ xfblob_bytes(rp->pptr_names);
+ mutex_unlock(&rp->lock);
+ if (mem_usage >= MAX_PPTR_STASH_SIZE) {
+ xchk_trans_cancel(sc);
+
+ error = xrep_tempfile_iolock_polled(sc);
+ if (error)
+ break;
+
+ error = xrep_pptr_replay_updates(rp);
+ xrep_tempfile_iounlock(sc);
+ if (error)
+ break;
+
+ error = xchk_trans_alloc_empty(sc);
+ if (error)
+ break;
+ }
+
+ if (xchk_should_terminate(sc, &error))
+ break;
+ }
+ xchk_iscan_iter_finish(&rp->iscan);
+ if (error) {
+ /*
+ * If we couldn't grab an inode that was busy with a state
+ * change, change the error code so that we exit to userspace
+ * as quickly as possible.
+ */
+ if (error == -EBUSY)
+ return -ECANCELED;
+ return error;
+ }
+
+ return 0;
+}
+
+/* Dump a parent pointer from the temporary file. */
+STATIC int
+xrep_pptr_dump_tempptr(
+ struct xfs_scrub *sc,
+ struct xfs_inode *ip,
+ unsigned int attr_flags,
+ const unsigned char *name,
+ unsigned int namelen,
+ const void *value,
+ unsigned int valuelen,
+ void *priv)
+{
+ struct xrep_pptrs *rp = priv;
+ const struct xfs_parent_name_rec *rec = (const void *)name;
+
+ if (!(attr_flags & XFS_ATTR_PARENT))
+ return 0;
+
+ if (!xfs_parent_namecheck(sc->mp, rec, namelen, attr_flags) ||
+ !xfs_parent_valuecheck(sc->mp, value, valuelen))
+ return -EFSCORRUPTED;
+
+ xfs_parent_irec_from_disk(&rp->pptr, rec, value, valuelen);
+
+ trace_xrep_pptr_dumpname(sc->tempip, &rp->pptr);
+ return 0;
+}
+
+/*
+ * "Commit" the new parent pointer (aka extended attribute) structure to the
+ * file that we're repairing.
+ *
+ * In the final version, we'd copy the existing xattrs from the file being
+ * repaired to the temporary file and swap the new xattr contents (which we
+ * created in the tempfile) into the file being repaired. For now we just lock
+ * the temporary file and dump what we found.
+ */
+STATIC int
+xrep_pptr_rebuild_tree(
+ struct xrep_pptrs *rp)
+{
+ struct xfs_scrub *sc = rp->sc;
+ int error = 0;
+
+ /*
+ * Replay the last of the stashed dirent updates after retaking
+ * IOLOCK_EXCL of the directory that we're repairing and the temporary
+ * directory.
+ */
+ xchk_trans_cancel(sc);
+
+ ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL);
+ error = xrep_tempfile_iolock_polled(sc);
+ if (error)
+ return error;
+
+ /*
+ * Replay stashed updates and take the ILOCKs of both files before we
+ * simulate committing the new parent pointer structure.
+ *
+ * As of Linux 6.3, if /a, /a/b, and /c are all directories, the VFS
+ * does not take i_rwsem on /a/b for a "mv /a/b /c/" operation. This
+ * means that only b's ILOCK protects b's dotdot update. b's IOLOCK
+ * is not held, unlike every other dotdot update. To stabilize sc->ip
+ * to simulate the repair commit, we must hold the ILOCK of the
+ * directory being repaired /and/ there must not be any pending live
+ * updates.
+ *
+ * For non-directories it /does/ take i_rwsem on the children, so we
+ * should only traverse the loop once.
+ */
+ do {
+ error = xrep_pptr_replay_updates(rp);
+ if (error)
+ return error;
+
+ error = xchk_trans_alloc(sc, 0);
+ if (error)
+ return error;
+
+ xchk_ilock(sc, XFS_ILOCK_EXCL);
+ if (xfarray_length(rp->parent_ptrs) == 0)
+ break;
+
+ xchk_iunlock(sc, XFS_ILOCK_EXCL);
+ xchk_trans_cancel(sc);
+ } while (!xchk_should_terminate(sc, &error));
+ if (error)
+ return error;
+
+ trace_xrep_pptr_rebuild_tree(sc->ip, 0);
+
+ /*
+ * At this point, we've quiesced both files and should be ready
+ * to commit the new contents.
+ *
+ * We don't have atomic swapext here, so all we do is dump the pptrs
+ * that we found to the ftrace buffer. Inactivation of the tempfile
+ * will erase the attr fork for us.
+ */
+ error = xrep_tempfile_ilock_polled(sc);
+ if (error)
+ return error;
+
+ return xchk_xattr_walk(sc, sc->tempip, xrep_pptr_dump_tempptr, rp);
+}
+
+/* Set up the filesystem scan so we can look for pptrs. */
+STATIC int
+xrep_pptr_setup_scan(
+ struct xrep_pptrs *rp)
+{
+ struct xfs_scrub *sc = rp->sc;
+ int error;
+
+ /* Set up some staging memory for logging parent pointers. */
+ error = xfarray_create(sc->mp, "parent pointers", 0,
+ sizeof(struct xrep_pptr), &rp->parent_ptrs);
+ if (error)
+ return error;
+
+ error = xfblob_create(sc->mp, "pptr names", &rp->pptr_names);
+ if (error)
+ goto out_entries;
+
+ mutex_init(&rp->lock);
+
+ /* Retry iget every tenth of a second for up to 30 seconds. */
+ xchk_iscan_start(sc, 30000, 100, &rp->iscan);
+
+ return 0;
+
+out_entries:
+ xfarray_destroy(rp->parent_ptrs);
+ return error;
+}
+
+/* Repair the parent pointers. */
+int
+xrep_parent(
+ struct xfs_scrub *sc)
+{
+ struct xrep_pptrs *rp = sc->buf;
+ int error = 0;
+
+ /* We require directory parent pointers to rebuild anything. */
+ if (!xfs_has_parent(sc->mp))
+ return -EOPNOTSUPP;
+
+ error = xrep_pptr_setup_scan(rp);
+ if (error)
+ goto out;
+
+ error = xrep_pptr_scan_dirtree(rp);
+ if (error)
+ goto out_finish_scan;
+
+ error = xrep_pptr_rebuild_tree(rp);
+ if (error)
+ goto out_finish_scan;
+
+out_finish_scan:
+ xrep_pptr_teardown(rp);
+out:
+ return error;
+}
diff --git a/fs/xfs/scrub/repair.h b/fs/xfs/scrub/repair.h
index ff254ff9b86d..cc42cf65ac92 100644
--- a/fs/xfs/scrub/repair.h
+++ b/fs/xfs/scrub/repair.h
@@ -68,6 +68,7 @@ void xrep_force_quotacheck(struct xfs_scrub *sc, xfs_dqtype_t type);
int xrep_ino_dqattach(struct xfs_scrub *sc);
int xrep_setup_directory(struct xfs_scrub *sc);
+int xrep_setup_parent(struct xfs_scrub *sc);
/* Metadata repairers */
@@ -77,6 +78,7 @@ int xrep_agf(struct xfs_scrub *sc);
int xrep_agfl(struct xfs_scrub *sc);
int xrep_agi(struct xfs_scrub *sc);
int xrep_directory(struct xfs_scrub *sc);
+int xrep_parent(struct xfs_scrub *sc);
#else
@@ -97,6 +99,7 @@ xrep_calc_ag_resblks(
}
#define xrep_setup_directory(sc) (0)
+#define xrep_setup_parent(sc) (0)
#define xrep_probe xrep_notsupported
#define xrep_superblock xrep_notsupported
@@ -104,6 +107,7 @@ xrep_calc_ag_resblks(
#define xrep_agfl xrep_notsupported
#define xrep_agi xrep_notsupported
#define xrep_directory xrep_notsupported
+#define xrep_parent xrep_notsupported
#endif /* CONFIG_XFS_ONLINE_REPAIR */
diff --git a/fs/xfs/scrub/scrub.c b/fs/xfs/scrub/scrub.c
index b2a8de449d11..5ddb4dcff978 100644
--- a/fs/xfs/scrub/scrub.c
+++ b/fs/xfs/scrub/scrub.c
@@ -317,7 +317,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
.type = ST_INODE,
.setup = xchk_setup_parent,
.scrub = xchk_parent,
- .repair = xrep_notsupported,
+ .repair = xrep_parent,
},
[XFS_SCRUB_TYPE_RTBITMAP] = { /* realtime bitmap */
.type = ST_FS,
diff --git a/fs/xfs/scrub/trace.c b/fs/xfs/scrub/trace.c
index 61b51617fbb4..30946b6a16dd 100644
--- a/fs/xfs/scrub/trace.c
+++ b/fs/xfs/scrub/trace.c
@@ -18,6 +18,8 @@
#include "scrub/xfarray.h"
#include "scrub/iscan.h"
#include "xfs_da_format.h"
+#include "xfs_da_btree.h"
+#include "xfs_parent.h"
/* Figure out which block the btree cursor was pointing to. */
static inline xfs_fsblock_t
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index 7eb6dcb3aa49..caf8d343926e 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -19,6 +19,7 @@
struct xfile;
struct xfarray;
struct xchk_iscan;
+struct xfs_parent_name_irec;
/*
* ftrace's __print_symbolic requires that all enum values be wrapped in the
@@ -1337,6 +1338,76 @@ DEFINE_EVENT(xrep_dir_class, name, \
TP_PROTO(struct xfs_inode *dp, xfs_ino_t parent_ino), \
TP_ARGS(dp, parent_ino))
DEFINE_XREP_DIR_CLASS(xrep_dir_rebuild_tree);
+DEFINE_XREP_DIR_CLASS(xrep_pptr_rebuild_tree);
+
+DECLARE_EVENT_CLASS(xrep_pptr_class,
+ TP_PROTO(struct xfs_inode *ip, const struct xfs_parent_name_irec *pptr),
+ TP_ARGS(ip, pptr),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(xfs_ino_t, ino)
+ __field(xfs_ino_t, parent_ino)
+ __field(unsigned int, parent_gen)
+ __field(unsigned int, namelen)
+ __dynamic_array(char, name, pptr->p_namelen)
+ ),
+ TP_fast_assign(
+ __entry->dev = ip->i_mount->m_super->s_dev;
+ __entry->ino = ip->i_ino;
+ __entry->parent_ino = pptr->p_ino;
+ __entry->parent_gen = pptr->p_gen;
+ __entry->namelen = pptr->p_namelen;
+ memcpy(__get_str(name), pptr->p_name, pptr->p_namelen);
+ ),
+ TP_printk("dev %d:%d ino 0x%llx parent_ino 0x%llx parent_gen 0x%x name '%.*s'",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->ino,
+ __entry->parent_ino,
+ __entry->parent_gen,
+ __entry->namelen,
+ __get_str(name))
+)
+#define DEFINE_XREP_PPTR_CLASS(name) \
+DEFINE_EVENT(xrep_pptr_class, name, \
+ TP_PROTO(struct xfs_inode *ip, const struct xfs_parent_name_irec *pptr), \
+ TP_ARGS(ip, pptr))
+DEFINE_XREP_PPTR_CLASS(xrep_pptr_createname);
+DEFINE_XREP_PPTR_CLASS(xrep_pptr_dumpname);
+
+DECLARE_EVENT_CLASS(xrep_pptr_scan_class,
+ TP_PROTO(struct xfs_inode *ip, const struct xfs_inode *dp,
+ const struct xfs_name *name),
+ TP_ARGS(ip, dp, name),
+ TP_STRUCT__entry(
+ __field(dev_t, dev)
+ __field(xfs_ino_t, ino)
+ __field(xfs_ino_t, parent_ino)
+ __field(unsigned int, parent_gen)
+ __field(unsigned int, namelen)
+ __dynamic_array(char, name, name->len)
+ ),
+ TP_fast_assign(
+ __entry->dev = ip->i_mount->m_super->s_dev;
+ __entry->ino = ip->i_ino;
+ __entry->parent_ino = dp->i_ino;
+ __entry->parent_gen = VFS_IC(dp)->i_generation;
+ __entry->namelen = name->len;
+ memcpy(__get_str(name), name->name, name->len);
+ ),
+ TP_printk("dev %d:%d ino 0x%llx parent_ino 0x%llx parent_gen 0x%x name '%.*s'",
+ MAJOR(__entry->dev), MINOR(__entry->dev),
+ __entry->ino,
+ __entry->parent_ino,
+ __entry->parent_gen,
+ __entry->namelen,
+ __get_str(name))
+)
+#define DEFINE_XREP_PPTR_SCAN_CLASS(name) \
+DEFINE_EVENT(xrep_pptr_scan_class, name, \
+ TP_PROTO(struct xfs_inode *ip, const struct xfs_inode *dp, \
+ const struct xfs_name *name), \
+ TP_ARGS(ip, dp, name))
+DEFINE_XREP_PPTR_SCAN_CLASS(xrep_pptr_add_pointer);
#endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 2/3] xfs: repair parent pointers with live scan hooks
2023-04-06 19:15 ` [PATCHSET v11 0/3] xfs: online checking " Darrick J. Wong
2023-04-06 19:27 ` [PATCH 1/3] xfs: repair parent pointers by scanning directories Darrick J. Wong
@ 2023-04-06 19:28 ` Darrick J. Wong
2023-04-06 19:28 ` [PATCH 3/3] xfs: compare generated and existing parent pointers Darrick J. Wong
2 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:28 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Use the nlink hooks to keep our tempfile's parent pointers up to date.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_parent.c | 19 ++++++++
fs/xfs/libxfs/xfs_parent.h | 4 ++
fs/xfs/scrub/parent_repair.c | 100 ++++++++++++++++++++++++++++++++++++++++++
fs/xfs/scrub/trace.h | 2 +
4 files changed, 125 insertions(+)
diff --git a/fs/xfs/libxfs/xfs_parent.c b/fs/xfs/libxfs/xfs_parent.c
index 59fe4181bedd..bf3989f519cd 100644
--- a/fs/xfs/libxfs/xfs_parent.c
+++ b/fs/xfs/libxfs/xfs_parent.c
@@ -375,3 +375,22 @@ xfs_parent_set(
return xfs_attr_set(&scr->args);
}
+
+/*
+ * Remove the parent pointer (@rec -> @name) from @ip immediately. Caller must
+ * not have a transaction or hold the ILOCK. The update will not use logged
+ * xattrs. This is for specialized repair functions only. The scratchpad need
+ * not be initialized.
+ */
+int
+xfs_parent_unset(
+ struct xfs_inode *ip,
+ const struct xfs_parent_name_irec *pptr,
+ struct xfs_parent_scratch *scr)
+{
+ xfs_parent_irec_to_disk(&scr->rec, pptr);
+ xfs_parent_scratch_init(NULL, ip, pptr, scr);
+ scr->args.op_flags |= XFS_DA_OP_REMOVE;
+
+ return xfs_attr_set(&scr->args);
+}
diff --git a/fs/xfs/libxfs/xfs_parent.h b/fs/xfs/libxfs/xfs_parent.h
index da8dc689221c..b4f45803ebf5 100644
--- a/fs/xfs/libxfs/xfs_parent.h
+++ b/fs/xfs/libxfs/xfs_parent.h
@@ -115,4 +115,8 @@ int xfs_parent_set(struct xfs_inode *ip,
const struct xfs_parent_name_irec *pptr,
struct xfs_parent_scratch *scratch);
+int xfs_parent_unset(struct xfs_inode *ip,
+ const struct xfs_parent_name_irec *rec,
+ struct xfs_parent_scratch *scratch);
+
#endif /* __XFS_PARENT_H__ */
diff --git a/fs/xfs/scrub/parent_repair.c b/fs/xfs/scrub/parent_repair.c
index 30d2a81e4df2..293d9e931018 100644
--- a/fs/xfs/scrub/parent_repair.c
+++ b/fs/xfs/scrub/parent_repair.c
@@ -118,6 +118,9 @@ struct xrep_pptrs {
/* Mutex protecting parent_ptrs, pptr_names. */
struct mutex lock;
+ /* Hook to capture directory entry updates. */
+ struct xfs_dirent_hook hooks;
+
/* Stashed parent pointer updates. */
struct xfarray *parent_ptrs;
@@ -130,6 +133,7 @@ static void
xrep_pptr_teardown(
struct xrep_pptrs *rp)
{
+ xfs_dirent_hook_del(rp->sc->mp, &rp->hooks);
xchk_iscan_teardown(&rp->iscan);
mutex_destroy(&rp->lock);
xfblob_destroy(rp->pptr_names);
@@ -144,6 +148,8 @@ xrep_setup_parent(
struct xrep_pptrs *rp;
int error;
+ xchk_fshooks_enable(sc, XCHK_FSHOOKS_DIRENTS);
+
error = xrep_tempfile_create(sc, S_IFREG);
if (error)
return error;
@@ -184,6 +190,12 @@ xrep_pptr_replay_update(
trace_xrep_pptr_createname(sc->tempip, &rp->pptr);
return xfs_parent_set(sc->tempip, &rp->pptr, &rp->pptr_scratch);
+ } else if (pptr->action == XREP_PPTR_REMOVE) {
+ /* Remove parent pointer. */
+ trace_xrep_pptr_removename(sc->tempip, &rp->pptr);
+
+ return xfs_parent_unset(sc->tempip, &rp->pptr,
+ &rp->pptr_scratch);
}
ASSERT(0);
@@ -265,6 +277,34 @@ xrep_pptr_add_pointer(
return xfarray_append(rp->parent_ptrs, &pptr);
}
+/*
+ * Remember that we want to remove a parent pointer from the tempfile. These
+ * stashed actions will be replayed later.
+ */
+STATIC int
+xrep_pptr_remove_pointer(
+ struct xrep_pptrs *rp,
+ const struct xfs_name *name,
+ const struct xfs_inode *dp)
+{
+ struct xrep_pptr pptr = {
+ .action = XREP_PPTR_REMOVE,
+ .namelen = name->len,
+ .p_ino = dp->i_ino,
+ .p_gen = VFS_IC(dp)->i_generation,
+ };
+ int error;
+
+ trace_xrep_pptr_remove_pointer(rp->sc->tempip, dp, name);
+
+ error = xfblob_store(rp->pptr_names, &pptr.name_cookie, name->name,
+ name->len);
+ if (error)
+ return error;
+
+ return xfarray_append(rp->parent_ptrs, &pptr);
+}
+
/*
* Examine an entry of a directory. If this dirent leads us back to the file
* whose parent pointers we're rebuilding, add a pptr to the temporary
@@ -553,6 +593,50 @@ xrep_pptr_rebuild_tree(
return xchk_xattr_walk(sc, sc->tempip, xrep_pptr_dump_tempptr, rp);
}
+/*
+ * Capture dirent updates being made by other threads which are relevant to the
+ * file being repaired.
+ */
+STATIC int
+xrep_pptr_live_update(
+ struct notifier_block *nb,
+ unsigned long action,
+ void *data)
+{
+ struct xfs_dirent_update_params *p = data;
+ struct xrep_pptrs *rp;
+ struct xfs_scrub *sc;
+ int error;
+
+ rp = container_of(nb, struct xrep_pptrs, hooks.delta_hook.nb);
+ sc = rp->sc;
+
+ if (action != XFS_DIRENT_CHILD_DELTA)
+ return NOTIFY_DONE;
+
+ /*
+ * This thread updated a dirent that points to the file that we're
+ * repairing, so stash the update for replay against the temporary
+ * file.
+ */
+ if (p->ip->i_ino == sc->ip->i_ino &&
+ xchk_iscan_want_live_update(&rp->iscan, p->dp->i_ino)) {
+ mutex_lock(&rp->lock);
+ if (p->delta > 0)
+ error = xrep_pptr_add_pointer(rp, p->name, p->dp);
+ else
+ error = xrep_pptr_remove_pointer(rp, p->name, p->dp);
+ mutex_unlock(&rp->lock);
+ if (error)
+ goto out_abort;
+ }
+
+ return NOTIFY_DONE;
+out_abort:
+ xchk_iscan_abort(&rp->iscan);
+ return NOTIFY_DONE;
+}
+
/* Set up the filesystem scan so we can look for pptrs. */
STATIC int
xrep_pptr_setup_scan(
@@ -576,8 +660,24 @@ xrep_pptr_setup_scan(
/* Retry iget every tenth of a second for up to 30 seconds. */
xchk_iscan_start(sc, 30000, 100, &rp->iscan);
+ /*
+ * Hook into the dirent update code. The hook only operates on inodes
+ * that were already scanned, and the scanner thread takes each inode's
+ * ILOCK, which means that any in-progress inode updates will finish
+ * before we can scan the inode.
+ */
+ ASSERT(sc->flags & XCHK_FSHOOKS_DIRENTS);
+ xfs_hook_setup(&rp->hooks.delta_hook, xrep_pptr_live_update);
+ error = xfs_dirent_hook_add(sc->mp, &rp->hooks);
+ if (error)
+ goto out_scan;
+
return 0;
+out_scan:
+ xchk_iscan_teardown(&rp->iscan);
+ mutex_destroy(&rp->lock);
+ xfblob_destroy(rp->pptr_names);
out_entries:
xfarray_destroy(rp->parent_ptrs);
return error;
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index caf8d343926e..5b070c177d48 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -1372,6 +1372,7 @@ DEFINE_EVENT(xrep_pptr_class, name, \
TP_PROTO(struct xfs_inode *ip, const struct xfs_parent_name_irec *pptr), \
TP_ARGS(ip, pptr))
DEFINE_XREP_PPTR_CLASS(xrep_pptr_createname);
+DEFINE_XREP_PPTR_CLASS(xrep_pptr_removename);
DEFINE_XREP_PPTR_CLASS(xrep_pptr_dumpname);
DECLARE_EVENT_CLASS(xrep_pptr_scan_class,
@@ -1408,6 +1409,7 @@ DEFINE_EVENT(xrep_pptr_scan_class, name, \
const struct xfs_name *name), \
TP_ARGS(ip, dp, name))
DEFINE_XREP_PPTR_SCAN_CLASS(xrep_pptr_add_pointer);
+DEFINE_XREP_PPTR_SCAN_CLASS(xrep_pptr_remove_pointer);
#endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 3/3] xfs: compare generated and existing parent pointers
2023-04-06 19:15 ` [PATCHSET v11 0/3] xfs: online checking " Darrick J. Wong
2023-04-06 19:27 ` [PATCH 1/3] xfs: repair parent pointers by scanning directories Darrick J. Wong
2023-04-06 19:28 ` [PATCH 2/3] xfs: repair parent pointers with live scan hooks Darrick J. Wong
@ 2023-04-06 19:28 ` Darrick J. Wong
2 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:28 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Check our work to make sure we found all the parent pointers that the
original file had.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/scrub/parent_repair.c | 29 +++++++++++++++++++++++++++--
fs/xfs/scrub/trace.h | 1 +
2 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/fs/xfs/scrub/parent_repair.c b/fs/xfs/scrub/parent_repair.c
index 293d9e931018..1f83e0f85dc0 100644
--- a/fs/xfs/scrub/parent_repair.c
+++ b/fs/xfs/scrub/parent_repair.c
@@ -486,7 +486,10 @@ xrep_pptr_scan_dirtree(
return 0;
}
-/* Dump a parent pointer from the temporary file. */
+/*
+ * Dump a parent pointer from the temporary file and check it against the file
+ * we're rebuilding. We are not committing any of this.
+ */
STATIC int
xrep_pptr_dump_tempptr(
struct xfs_scrub *sc,
@@ -500,6 +503,8 @@ xrep_pptr_dump_tempptr(
{
struct xrep_pptrs *rp = priv;
const struct xfs_parent_name_rec *rec = (const void *)name;
+ struct xfs_inode *other_ip;
+ int error;
if (!(attr_flags & XFS_ATTR_PARENT))
return 0;
@@ -508,10 +513,26 @@ xrep_pptr_dump_tempptr(
!xfs_parent_valuecheck(sc->mp, value, valuelen))
return -EFSCORRUPTED;
+ if (ip == sc->ip)
+ other_ip = sc->tempip;
+ else if (ip == sc->tempip)
+ other_ip = sc->ip;
+ else
+ return -EFSCORRUPTED;
+
xfs_parent_irec_from_disk(&rp->pptr, rec, value, valuelen);
trace_xrep_pptr_dumpname(sc->tempip, &rp->pptr);
- return 0;
+
+ error = xfs_parent_lookup(sc->tp, other_ip, &rp->pptr,
+ &rp->pptr_scratch);
+ if (error == -ENOATTR) {
+ trace_xrep_pptr_checkname(other_ip, &rp->pptr);
+ ASSERT(error != -ENOATTR);
+ return -EFSCORRUPTED;
+ }
+
+ return error;
}
/*
@@ -590,6 +611,10 @@ xrep_pptr_rebuild_tree(
if (error)
return error;
+ error = xchk_xattr_walk(sc, sc->ip, xrep_pptr_dump_tempptr, rp);
+ if (error)
+ return error;
+
return xchk_xattr_walk(sc, sc->tempip, xrep_pptr_dump_tempptr, rp);
}
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index 5b070c177d48..87e400096245 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -1374,6 +1374,7 @@ DEFINE_EVENT(xrep_pptr_class, name, \
DEFINE_XREP_PPTR_CLASS(xrep_pptr_createname);
DEFINE_XREP_PPTR_CLASS(xrep_pptr_removename);
DEFINE_XREP_PPTR_CLASS(xrep_pptr_dumpname);
+DEFINE_XREP_PPTR_CLASS(xrep_pptr_checkname);
DECLARE_EVENT_CLASS(xrep_pptr_scan_class,
TP_PROTO(struct xfs_inode *ip, const struct xfs_inode *dp,
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 1/2] xfs: check dirents have parent pointers
2023-04-06 19:15 ` [PATCHSET v11 0/2] xfs: online checking of directories Darrick J. Wong
@ 2023-04-06 19:28 ` Darrick J. Wong
2023-04-06 19:28 ` [PATCH 2/2] xfs: deferred scrub of dirents Darrick J. Wong
1 sibling, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:28 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If the fs has parent pointers, we need to check that each child dirent
points to a file that has a parent pointer pointing back at us.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/scrub/dir.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 116 insertions(+), 1 deletion(-)
diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c
index e30624dc35b3..9ae3afc4661a 100644
--- a/fs/xfs/scrub/dir.c
+++ b/fs/xfs/scrub/dir.c
@@ -15,6 +15,8 @@
#include "xfs_icache.h"
#include "xfs_dir2.h"
#include "xfs_dir2_priv.h"
+#include "xfs_attr.h"
+#include "xfs_parent.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/dabtree.h"
@@ -39,6 +41,20 @@ xchk_setup_directory(
/* Directories */
+struct xchk_dir {
+ struct xfs_scrub *sc;
+
+ /* Scratch buffer for scanning pptr xattrs */
+ struct xfs_parent_name_irec pptr;
+
+ /* xattr key and da args for parent pointer validation. */
+ struct xfs_parent_scratch pptr_scratch;
+
+ /* Name buffer for dirent revalidation. */
+ uint8_t namebuf[MAXNAMELEN];
+
+};
+
/* Scrub a directory entry. */
/* Check that an inode's mode matches a given XFS_DIR3_FT_* type. */
@@ -61,6 +77,88 @@ xchk_dir_check_ftype(
xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, offset);
}
+/*
+ * Try to lock a child file for checking parent pointers. Returns the inode
+ * flags for the locks we now hold, or zero if we failed.
+ */
+STATIC unsigned int
+xchk_dir_lock_child(
+ struct xfs_scrub *sc,
+ struct xfs_inode *ip)
+{
+ if (!xfs_ilock_nowait(ip, XFS_IOLOCK_SHARED))
+ return 0;
+
+ if (!xfs_ilock_nowait(ip, XFS_ILOCK_SHARED)) {
+ xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+ return 0;
+ }
+
+ if (!xfs_inode_has_attr_fork(ip) || !xfs_need_iread_extents(&ip->i_af))
+ return XFS_IOLOCK_SHARED | XFS_ILOCK_SHARED;
+
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+ if (!xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) {
+ xfs_iunlock(ip, XFS_IOLOCK_SHARED);
+ return 0;
+ }
+
+ return XFS_IOLOCK_SHARED | XFS_ILOCK_EXCL;
+}
+
+/* Check the backwards link (parent pointer) associated with this dirent. */
+STATIC int
+xchk_dir_parent_pointer(
+ struct xchk_dir *sd,
+ const struct xfs_name *name,
+ struct xfs_inode *ip)
+{
+ struct xfs_scrub *sc = sd->sc;
+ int error;
+
+ sd->pptr.p_ino = sc->ip->i_ino;
+ sd->pptr.p_gen = VFS_I(sc->ip)->i_generation;
+ sd->pptr.p_namelen = name->len;
+ memcpy(sd->pptr.p_name, name->name, name->len);
+ xfs_parent_irec_hashname(sc->mp, &sd->pptr);
+
+ error = xfs_parent_lookup(sc->tp, ip, &sd->pptr, &sd->pptr_scratch);
+ if (error == -ENOATTR)
+ xchk_fblock_xref_set_corrupt(sc, XFS_DATA_FORK, 0);
+
+ return 0;
+}
+
+/* Look for a parent pointer matching this dirent, if the child isn't busy. */
+STATIC int
+xchk_dir_check_pptr_fast(
+ struct xchk_dir *sd,
+ xfs_dir2_dataptr_t dapos,
+ const struct xfs_name *name,
+ struct xfs_inode *ip)
+{
+ struct xfs_scrub *sc = sd->sc;
+ unsigned int lockmode;
+ int error;
+
+ /* dot and dotdot entries do not have parent pointers */
+ if (!strncmp(".", name->name, name->len) ||
+ !strncmp("..", name->name, name->len))
+ return 0;
+
+ /* Try to lock the inode. */
+ lockmode = xchk_dir_lock_child(sc, ip);
+ if (!lockmode) {
+ xchk_set_incomplete(sc);
+ return -ECANCELED;
+ }
+
+ error = xchk_dir_parent_pointer(sd, name, ip);
+ xfs_iunlock(ip, lockmode);
+ return error;
+}
+
/*
* Scrub a single directory entry.
*
@@ -78,6 +176,7 @@ xchk_dir_actor(
{
struct xfs_mount *mp = dp->i_mount;
struct xfs_inode *ip;
+ struct xchk_dir *sd = priv;
xfs_ino_t lookup_ino;
xfs_dablk_t offset;
int error = 0;
@@ -144,6 +243,14 @@ xchk_dir_actor(
goto out;
xchk_dir_check_ftype(sc, offset, ip, name->type);
+
+ if (xfs_has_parent(mp)) {
+ error = xchk_dir_check_pptr_fast(sd, dapos, name, ip);
+ if (error)
+ goto out_rele;
+ }
+
+out_rele:
xchk_irele(sc, ip);
out:
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
@@ -759,6 +866,7 @@ int
xchk_directory(
struct xfs_scrub *sc)
{
+ struct xchk_dir *sd;
int error;
if (!S_ISDIR(VFS_I(sc->ip)->i_mode))
@@ -786,9 +894,16 @@ xchk_directory(
if (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
return 0;
+ sd = kvzalloc(sizeof(struct xchk_dir), XCHK_GFP_FLAGS);
+ if (!sd)
+ return -ENOMEM;
+ sd->sc = sc;
+
/* Look up every name in this directory by hash. */
- error = xchk_dir_walk(sc, sc->ip, xchk_dir_actor, NULL);
+ error = xchk_dir_walk(sc, sc->ip, xchk_dir_actor, sd);
if (error == -ECANCELED)
error = 0;
+
+ kvfree(sd);
return error;
}
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 2/2] xfs: deferred scrub of dirents
2023-04-06 19:15 ` [PATCHSET v11 0/2] xfs: online checking of directories Darrick J. Wong
2023-04-06 19:28 ` [PATCH 1/2] xfs: check dirents have parent pointers Darrick J. Wong
@ 2023-04-06 19:28 ` Darrick J. Wong
1 sibling, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:28 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If the trylock-based parent pointer check fails, retain those dirents
and check them at the end. This may involve dropping the locks on the
file being scanned, so yay.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
fs/xfs/scrub/dir.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++++++
fs/xfs/scrub/trace.h | 2
2 files changed, 225 insertions(+), 2 deletions(-)
diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c
index 9ae3afc4661a..5c28adca97f1 100644
--- a/fs/xfs/scrub/dir.c
+++ b/fs/xfs/scrub/dir.c
@@ -22,6 +22,10 @@
#include "scrub/dabtree.h"
#include "scrub/readdir.h"
#include "scrub/repair.h"
+#include "scrub/trace.h"
+#include "scrub/xfile.h"
+#include "scrub/xfarray.h"
+#include "scrub/xfblob.h"
/* Set us up to scrub directories. */
int
@@ -41,6 +45,18 @@ xchk_setup_directory(
/* Directories */
+/* Deferred directory entry that we saved for later. */
+struct xchk_dirent {
+ /* Cookie for retrieval of the dirent name. */
+ xfblob_cookie name_cookie;
+
+ /* Child inode number. */
+ xfs_ino_t ino;
+
+ /* Length of the pptr name. */
+ uint8_t namelen;
+};
+
struct xchk_dir {
struct xfs_scrub *sc;
@@ -50,6 +66,15 @@ struct xchk_dir {
/* xattr key and da args for parent pointer validation. */
struct xfs_parent_scratch pptr_scratch;
+ /* Fixed-size array of xchk_dirent structures. */
+ struct xfarray *dir_entries;
+
+ /* Blobs containing dirent names. */
+ struct xfblob *dir_names;
+
+ /* If we've cycled the ILOCK, we must revalidate deferred dirents. */
+ bool need_revalidate;
+
/* Name buffer for dirent revalidation. */
uint8_t namebuf[MAXNAMELEN];
@@ -150,8 +175,24 @@ xchk_dir_check_pptr_fast(
/* Try to lock the inode. */
lockmode = xchk_dir_lock_child(sc, ip);
if (!lockmode) {
- xchk_set_incomplete(sc);
- return -ECANCELED;
+ struct xchk_dirent save_de = {
+ .namelen = name->len,
+ .ino = ip->i_ino,
+ };
+
+ /* Couldn't lock the inode, so save the dirent for later. */
+ trace_xchk_dir_defer(sc->ip, name->name, name->len, ip->i_ino);
+
+ error = xfblob_store(sd->dir_names, &save_de.name_cookie,
+ name->name, name->len);
+ if (xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
+ return error;
+
+ error = xfarray_append(sd->dir_entries, &save_de);
+ if (xchk_fblock_process_error(sc, XFS_DATA_FORK, 0, &error))
+ return error;
+
+ return 0;
}
error = xchk_dir_parent_pointer(sd, name, ip);
@@ -861,6 +902,156 @@ xchk_directory_blocks(
return error;
}
+/*
+ * Revalidate a dirent that we collected in the past but couldn't check because
+ * of lock contention. Returns 0 if the dirent is still valid, -ENOENT if it
+ * has gone away on us, or a negative errno.
+ */
+STATIC int
+xchk_dir_revalidate_dirent(
+ struct xchk_dir *sd,
+ const struct xfs_name *xname,
+ xfs_ino_t ino)
+{
+ struct xfs_scrub *sc = sd->sc;
+ xfs_ino_t child_ino;
+ int error;
+
+ /*
+ * Look up the directory entry. If we get -ENOENT, the directory entry
+ * went away and there's nothing to revalidate. Return any other
+ * error.
+ */
+ error = xchk_dir_lookup(sc, sc->ip, xname, &child_ino);
+ if (error)
+ return error;
+
+ /* The inode number changed, nothing to revalidate. */
+ if (ino != child_ino)
+ return -ENOENT;
+
+ return 0;
+}
+
+/*
+ * Check a directory entry's parent pointers the slow way, which means we cycle
+ * locks a bunch and put up with revalidation until we get it done.
+ */
+STATIC int
+xchk_dir_slow_dirent(
+ struct xchk_dir *sd,
+ struct xchk_dirent *dirent)
+{
+ struct xfs_name xname = {
+ .name = sd->namebuf,
+ .len = dirent->namelen,
+ };
+ struct xfs_scrub *sc = sd->sc;
+ struct xfs_inode *ip;
+ unsigned int lockmode;
+ int error;
+
+ /* Check that the deferred dirent still exists. */
+ if (sd->need_revalidate) {
+ error = xchk_dir_revalidate_dirent(sd, &xname, dirent->ino);
+ if (error == -ENOENT)
+ return 0;
+ if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0,
+ &error))
+ return error;
+ }
+
+ error = xchk_iget(sc, dirent->ino, &ip);
+ if (error == -EINVAL || error == -ENOENT) {
+ xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0);
+ return 0;
+ }
+ if (!xchk_fblock_xref_process_error(sc, XFS_ATTR_FORK, 0, &error))
+ return error;
+
+ /*
+ * If we can grab both IOLOCK and ILOCK of the alleged child, we can
+ * proceed with the validation.
+ */
+ lockmode = xchk_dir_lock_child(sc, ip);
+ if (lockmode)
+ goto check_pptr;
+
+ /*
+ * We couldn't lock the child file. Drop all the locks and try to
+ * get them again, one at a time.
+ */
+ xchk_iunlock(sc, sc->ilock_flags);
+ sd->need_revalidate = true;
+
+ trace_xchk_dir_slowpath(sc->ip, xname.name, xname.len, ip->i_ino);
+
+ while (true) {
+ xchk_ilock(sc, XFS_IOLOCK_EXCL);
+ if (xfs_ilock_nowait(ip, XFS_IOLOCK_SHARED)) {
+ xchk_ilock(sc, XFS_ILOCK_EXCL);
+ if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) {
+ break;
+ }
+ xchk_iunlock(sc, XFS_ILOCK_EXCL);
+ }
+ xchk_iunlock(sc, XFS_IOLOCK_EXCL);
+
+ if (xchk_should_terminate(sc, &error))
+ goto out_rele;
+
+ delay(1);
+ }
+ lockmode = XFS_IOLOCK_SHARED | XFS_ILOCK_EXCL;
+
+ /* Revalidate, since we just cycled the locks. */
+ error = xchk_dir_revalidate_dirent(sd, &xname, dirent->ino);
+ if (error == -ENOENT)
+ goto out_unlock;
+ if (!xchk_fblock_xref_process_error(sc, XFS_DATA_FORK, 0, &error))
+ goto out_unlock;
+
+check_pptr:
+ error = xchk_dir_parent_pointer(sd, &xname, ip);
+out_unlock:
+ xfs_iunlock(ip, lockmode);
+out_rele:
+ xchk_irele(sc, ip);
+ return error;
+}
+
+/* Check all the dirents that we deferred the first time around. */
+STATIC int
+xchk_dir_finish_slow_dirents(
+ struct xchk_dir *sd)
+{
+ xfarray_idx_t array_cur;
+ int error;
+
+ foreach_xfarray_idx(sd->dir_entries, array_cur) {
+ struct xchk_dirent dirent;
+
+ if (sd->sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)
+ return 0;
+
+ error = xfarray_load(sd->dir_entries, array_cur, &dirent);
+ if (error)
+ return error;
+
+ error = xfblob_load(sd->dir_names, dirent.name_cookie,
+ sd->namebuf, dirent.namelen);
+ if (error)
+ return error;
+ sd->namebuf[MAXNAMELEN - 1] = 0;
+
+ error = xchk_dir_slow_dirent(sd, &dirent);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
/* Scrub a whole directory. */
int
xchk_directory(
@@ -899,11 +1090,41 @@ xchk_directory(
return -ENOMEM;
sd->sc = sc;
+ if (xfs_has_parent(sc->mp)) {
+ /*
+ * Set up some staging memory for dirents that we can't check
+ * due to locking contention.
+ */
+ error = xfarray_create(sc->mp, "directory entries", 0,
+ sizeof(struct xchk_dirent), &sd->dir_entries);
+ if (error)
+ goto out_sd;
+
+ error = xfblob_create(sc->mp, "dirent names", &sd->dir_names);
+ if (error)
+ goto out_entries;
+ }
+
/* Look up every name in this directory by hash. */
error = xchk_dir_walk(sc, sc->ip, xchk_dir_actor, sd);
if (error == -ECANCELED)
error = 0;
+ if (error)
+ goto out_names;
+ if (xfs_has_parent(sc->mp)) {
+ error = xchk_dir_finish_slow_dirents(sd);
+ if (error)
+ goto out_names;
+ }
+
+out_names:
+ if (sd->dir_names)
+ xfblob_destroy(sd->dir_names);
+out_entries:
+ if (sd->dir_entries)
+ xfarray_destroy(sd->dir_entries);
+out_sd:
kvfree(sd);
return error;
}
diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h
index 87e400096245..1b73fa20b6a1 100644
--- a/fs/xfs/scrub/trace.h
+++ b/fs/xfs/scrub/trace.h
@@ -987,6 +987,8 @@ DEFINE_EVENT(xchk_pptr_class, name, \
TP_ARGS(ip, name, namelen, parent_ino))
DEFINE_XCHK_PPTR_CLASS(xchk_parent_defer);
DEFINE_XCHK_PPTR_CLASS(xchk_parent_slowpath);
+DEFINE_XCHK_PPTR_CLASS(xchk_dir_defer);
+DEFINE_XCHK_PPTR_CLASS(xchk_dir_slowpath);
/* repair tracepoints */
#if IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR)
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 01/10] xfs: make xfs_attr_set require XFS_DA_OP_REMOVE
2023-04-06 19:15 ` [PATCHSET v11 00/10] xfs: name-value xattr lookups Darrick J. Wong
@ 2023-04-06 19:29 ` Darrick J. Wong
2023-04-06 19:29 ` [PATCH 02/10] xfs: allow xattr matching on name and value for local/sf attrs Darrick J. Wong
` (8 subsequent siblings)
9 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:29 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Quite a few patches from now, we're going to change the parent pointer
xattr format to encode as much of the dirent name in the xattr name as
fits, and spill the rest of it to the xattr value. To make this work
correctly, we'll be adding the ability to look up xattrs based on name
/and/ value.
Internally, the xattr data structure supports attributes with a zero
length value, which is how we're going to store parent pointers for
short dirent names. The parent pointer repair code uses xfs_attr_set to
add missing and remove dangling parent pointers, so that interface must
be capable of setting an xattr with args->value == NULL.
The userspace API doesn't support this, so xfs_attr_set currently treats
a NULL args->value as a request to remove an attr. However, that's a
quirk of the existing callers and the interface. Make the callers of
xfs_attr_set to declare explicitly that they want to remove an xattr.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
db/attrset.c | 4 +++-
libxfs/xfs_attr.c | 9 +++++----
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/db/attrset.c b/db/attrset.c
index 0d8d70a84..d493a1329 100644
--- a/db/attrset.c
+++ b/db/attrset.c
@@ -184,7 +184,9 @@ attr_remove_f(
int argc,
char **argv)
{
- struct xfs_da_args args = { };
+ struct xfs_da_args args = {
+ .op_flags = XFS_DA_OP_REMOVE,
+ };
int c;
if (cur_typ == NULL) {
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 2103a06b9..177962dec 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -965,6 +965,7 @@ xfs_attr_set(
struct xfs_mount *mp = dp->i_mount;
struct xfs_trans_res tres;
bool rsvd = (args->attr_filter & XFS_ATTR_ROOT);
+ bool is_remove = args->op_flags & XFS_DA_OP_REMOVE;
int error, local;
int rmt_blks = 0;
unsigned int total;
@@ -989,7 +990,7 @@ xfs_attr_set(
args->op_flags = XFS_DA_OP_OKNOENT |
(args->op_flags & XFS_DA_OP_LOGGED);
- if (args->value) {
+ if (!is_remove) {
XFS_STATS_INC(mp, xs_attr_set);
args->total = xfs_attr_calc_size(args, &local);
@@ -1023,7 +1024,7 @@ xfs_attr_set(
if (error)
return error;
- if (args->value || xfs_inode_hasattr(dp)) {
+ if (!is_remove || xfs_inode_hasattr(dp)) {
error = xfs_iext_count_may_overflow(dp, XFS_ATTR_FORK,
XFS_IEXT_ATTR_MANIP_CNT(rmt_blks));
if (error == -EFBIG)
@@ -1037,7 +1038,7 @@ xfs_attr_set(
switch (error) {
case -EEXIST:
/* if no value, we are performing a remove operation */
- if (!args->value) {
+ if (is_remove) {
error = xfs_attr_defer_remove(args);
break;
}
@@ -1049,7 +1050,7 @@ xfs_attr_set(
break;
case -ENOATTR:
/* Can't remove what isn't there. */
- if (!args->value)
+ if (is_remove)
goto out_trans_cancel;
/* Pure replace fails if no existing attr to replace. */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 02/10] xfs: allow xattr matching on name and value for local/sf attrs
2023-04-06 19:15 ` [PATCHSET v11 00/10] xfs: name-value xattr lookups Darrick J. Wong
2023-04-06 19:29 ` [PATCH 01/10] xfs: make xfs_attr_set require XFS_DA_OP_REMOVE Darrick J. Wong
@ 2023-04-06 19:29 ` Darrick J. Wong
2023-04-06 19:29 ` [PATCH 03/10] xfs: preserve NVLOOKUP in xfs_attr_set Darrick J. Wong
` (7 subsequent siblings)
9 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:29 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add a new NVLOOKUP flag to signal that the caller wants to look up an
extended attribute by name and value. This only works with shortform
and local attributes. Only parent pointers need this functionality
and parent pointers cannot be remote xattrs, so this limitation is ok
for now.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_attr_leaf.c | 45 +++++++++++++++++++++++++++++++++++++--------
libxfs/xfs_da_btree.h | 4 +++-
2 files changed, 40 insertions(+), 9 deletions(-)
diff --git a/libxfs/xfs_attr_leaf.c b/libxfs/xfs_attr_leaf.c
index 6cac25312..b095b25a6 100644
--- a/libxfs/xfs_attr_leaf.c
+++ b/libxfs/xfs_attr_leaf.c
@@ -470,10 +470,12 @@ xfs_attr3_leaf_read(
*/
static bool
xfs_attr_match(
- struct xfs_da_args *args,
- uint8_t namelen,
- unsigned char *name,
- int flags)
+ const struct xfs_da_args *args,
+ uint8_t namelen,
+ const unsigned char *name,
+ unsigned int valuelen,
+ const void *value,
+ int flags)
{
if (args->namelen != namelen)
@@ -481,6 +483,23 @@ xfs_attr_match(
if (memcmp(args->name, name, namelen) != 0)
return false;
+ if (args->op_flags & XFS_DA_OP_NVLOOKUP) {
+ if (args->valuelen != valuelen)
+ return false;
+ if (args->valuelen && !value) {
+ /* not implemented for remote values */
+ ASSERT(0);
+ return false;
+ }
+ if (valuelen && !args->value) {
+ /* caller gave us valuelen > 0 but no value?? */
+ ASSERT(0);
+ return false;
+ }
+ if (valuelen > 0 && memcmp(args->value, value, valuelen) != 0)
+ return false;
+ }
+
/* Recovery ignores the INCOMPLETE flag. */
if ((args->op_flags & XFS_DA_OP_RECOVERY) &&
args->attr_filter == (flags & XFS_ATTR_NSP_ONDISK_MASK))
@@ -499,6 +518,10 @@ xfs_attr_copy_value(
unsigned char *value,
int valuelen)
{
+ /* vlookups already supplied the attr value; don't copy anything */
+ if (args->op_flags & XFS_DA_OP_NVLOOKUP)
+ return 0;
+
/*
* No copy if all we have to do is get the length
*/
@@ -723,6 +746,7 @@ xfs_attr_sf_findname(
base += size, i++) {
size = xfs_attr_sf_entsize(sfe);
if (!xfs_attr_match(args, sfe->namelen, sfe->nameval,
+ sfe->valuelen, &sfe->nameval[sfe->namelen],
sfe->flags))
continue;
break;
@@ -891,6 +915,7 @@ xfs_attr_shortform_lookup(xfs_da_args_t *args)
for (i = 0; i < sf->hdr.count;
sfe = xfs_attr_sf_nextentry(sfe), i++) {
if (xfs_attr_match(args, sfe->namelen, sfe->nameval,
+ sfe->valuelen, &sfe->nameval[sfe->namelen],
sfe->flags))
return -EEXIST;
}
@@ -918,6 +943,7 @@ xfs_attr_shortform_getvalue(
for (i = 0; i < sf->hdr.count;
sfe = xfs_attr_sf_nextentry(sfe), i++) {
if (xfs_attr_match(args, sfe->namelen, sfe->nameval,
+ sfe->valuelen, &sfe->nameval[sfe->namelen],
sfe->flags))
return xfs_attr_copy_value(args,
&sfe->nameval[args->namelen], sfe->valuelen);
@@ -970,7 +996,7 @@ xfs_attr_shortform_to_leaf(
nargs.total = args->total;
nargs.whichfork = XFS_ATTR_FORK;
nargs.trans = args->trans;
- nargs.op_flags = XFS_DA_OP_OKNOENT;
+ nargs.op_flags = XFS_DA_OP_OKNOENT | XFS_DA_OP_NVLOOKUP;
sfe = &sf->list[0];
for (i = 0; i < sf->hdr.count; i++) {
@@ -1180,7 +1206,7 @@ xfs_attr3_leaf_to_shortform(
nargs.total = args->total;
nargs.whichfork = XFS_ATTR_FORK;
nargs.trans = args->trans;
- nargs.op_flags = XFS_DA_OP_OKNOENT;
+ nargs.op_flags = XFS_DA_OP_OKNOENT | XFS_DA_OP_NVLOOKUP;
for (i = 0; i < ichdr.count; entry++, i++) {
if (entry->flags & XFS_ATTR_INCOMPLETE)
@@ -2479,14 +2505,17 @@ xfs_attr3_leaf_lookup_int(
if (entry->flags & XFS_ATTR_LOCAL) {
name_loc = xfs_attr3_leaf_name_local(leaf, probe);
if (!xfs_attr_match(args, name_loc->namelen,
- name_loc->nameval, entry->flags))
+ name_loc->nameval,
+ be16_to_cpu(name_loc->valuelen),
+ &name_loc->nameval[name_loc->namelen],
+ entry->flags))
continue;
args->index = probe;
return -EEXIST;
} else {
name_rmt = xfs_attr3_leaf_name_remote(leaf, probe);
if (!xfs_attr_match(args, name_rmt->namelen,
- name_rmt->name, entry->flags))
+ name_rmt->name, 0, NULL, entry->flags))
continue;
args->index = probe;
args->rmtvaluelen = be32_to_cpu(name_rmt->valuelen);
diff --git a/libxfs/xfs_da_btree.h b/libxfs/xfs_da_btree.h
index ffa3df5b2..94a544fc8 100644
--- a/libxfs/xfs_da_btree.h
+++ b/libxfs/xfs_da_btree.h
@@ -93,6 +93,7 @@ typedef struct xfs_da_args {
#define XFS_DA_OP_REMOVE (1u << 6) /* this is a remove operation */
#define XFS_DA_OP_RECOVERY (1u << 7) /* Log recovery operation */
#define XFS_DA_OP_LOGGED (1u << 8) /* Use intent items to track op */
+#define XFS_DA_OP_NVLOOKUP (1u << 9) /* Match local attr on name+value */
#define XFS_DA_OP_FLAGS \
{ XFS_DA_OP_JUSTCHECK, "JUSTCHECK" }, \
@@ -103,7 +104,8 @@ typedef struct xfs_da_args {
{ XFS_DA_OP_NOTIME, "NOTIME" }, \
{ XFS_DA_OP_REMOVE, "REMOVE" }, \
{ XFS_DA_OP_RECOVERY, "RECOVERY" }, \
- { XFS_DA_OP_LOGGED, "LOGGED" }
+ { XFS_DA_OP_LOGGED, "LOGGED" }, \
+ { XFS_DA_OP_NVLOOKUP, "NVLOOKUP" }
/*
* Storage for holding state during Btree searches and split/join ops.
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 03/10] xfs: preserve NVLOOKUP in xfs_attr_set
2023-04-06 19:15 ` [PATCHSET v11 00/10] xfs: name-value xattr lookups Darrick J. Wong
2023-04-06 19:29 ` [PATCH 01/10] xfs: make xfs_attr_set require XFS_DA_OP_REMOVE Darrick J. Wong
2023-04-06 19:29 ` [PATCH 02/10] xfs: allow xattr matching on name and value for local/sf attrs Darrick J. Wong
@ 2023-04-06 19:29 ` Darrick J. Wong
2023-04-06 19:30 ` [PATCH 04/10] xfs: restructure xfs_attr_complete_op a bit Darrick J. Wong
` (6 subsequent siblings)
9 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:29 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Preserve the attr-value lookup flag when calling xfs_attr_set. Normal
xattr users will never use this, but parent pointer fsck will.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_attr.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 177962dec..7782f00c1 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -984,11 +984,11 @@ xfs_attr_set(
/*
* We have no control over the attribute names that userspace passes us
* to remove, so we have to allow the name lookup prior to attribute
- * removal to fail as well. Preserve the logged flag, since we need
- * to pass that through to the logging code.
+ * removal to fail as well. Preserve the logged and vlookup flags,
+ * since we need to pass them through to the lower levels.
*/
- args->op_flags = XFS_DA_OP_OKNOENT |
- (args->op_flags & XFS_DA_OP_LOGGED);
+ args->op_flags &= (XFS_DA_OP_LOGGED | XFS_DA_OP_NVLOOKUP);
+ args->op_flags |= XFS_DA_OP_OKNOENT;
if (!is_remove) {
XFS_STATS_INC(mp, xs_attr_set);
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 04/10] xfs: restructure xfs_attr_complete_op a bit
2023-04-06 19:15 ` [PATCHSET v11 00/10] xfs: name-value xattr lookups Darrick J. Wong
` (2 preceding siblings ...)
2023-04-06 19:29 ` [PATCH 03/10] xfs: preserve NVLOOKUP in xfs_attr_set Darrick J. Wong
@ 2023-04-06 19:30 ` Darrick J. Wong
2023-04-06 19:30 ` [PATCH 05/10] xfs: use helpers to extract xattr op from opflags Darrick J. Wong
` (5 subsequent siblings)
9 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:30 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Reduce the indentation in this function by flattening the nested if
statements. We're going to add more code later to this function later,
hence the early cleanup. No functional changes.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_attr.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 7782f00c1..137ba5e50 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -419,11 +419,11 @@ xfs_attr_complete_op(
bool do_replace = args->op_flags & XFS_DA_OP_REPLACE;
args->op_flags &= ~XFS_DA_OP_REPLACE;
- if (do_replace) {
- args->attr_filter &= ~XFS_ATTR_INCOMPLETE;
- return replace_state;
- }
- return XFS_DAS_DONE;
+ if (!do_replace)
+ return XFS_DAS_DONE;
+
+ args->attr_filter &= ~XFS_ATTR_INCOMPLETE;
+ return replace_state;
}
static int
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 05/10] xfs: use helpers to extract xattr op from opflags
2023-04-06 19:15 ` [PATCHSET v11 00/10] xfs: name-value xattr lookups Darrick J. Wong
` (3 preceding siblings ...)
2023-04-06 19:30 ` [PATCH 04/10] xfs: restructure xfs_attr_complete_op a bit Darrick J. Wong
@ 2023-04-06 19:30 ` Darrick J. Wong
2023-04-06 19:30 ` [PATCH 06/10] xfs: log NVLOOKUP xattr removal operations Darrick J. Wong
` (4 subsequent siblings)
9 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:30 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Create helper functions to extract the xattr op from the ondisk xattri
log item and the incore attr intent item. These will get more use in
the patches that follow.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_attr.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index 81be9b3e4..f0aa372ec 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -529,6 +529,11 @@ struct xfs_attr_intent {
struct xfs_bmbt_irec xattri_map;
};
+static inline unsigned int
+xfs_attr_intent_op(const struct xfs_attr_intent *attr)
+{
+ return attr->xattri_op_flags & XFS_ATTRI_OP_FLAGS_TYPE_MASK;
+}
/*========================================================================
* Function prototypes for the kernel.
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 06/10] xfs: log NVLOOKUP xattr removal operations
2023-04-06 19:15 ` [PATCHSET v11 00/10] xfs: name-value xattr lookups Darrick J. Wong
` (4 preceding siblings ...)
2023-04-06 19:30 ` [PATCH 05/10] xfs: use helpers to extract xattr op from opflags Darrick J. Wong
@ 2023-04-06 19:30 ` Darrick J. Wong
2023-04-06 19:30 ` [PATCH 07/10] xfs: log NVLOOKUP xattr setting operations Darrick J. Wong
` (3 subsequent siblings)
9 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:30 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If high level code wants to do a deferred xattr remove operation with
the NVLOOKUP flag set, we need to push this through the log.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_attr.c | 6 +++++-
libxfs/xfs_log_format.h | 1 +
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 137ba5e50..9621f8715 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -940,9 +940,13 @@ xfs_attr_defer_remove(
{
struct xfs_attr_intent *new;
+ int op_flag = XFS_ATTRI_OP_FLAGS_REMOVE;
int error;
- error = xfs_attr_intent_init(args, XFS_ATTRI_OP_FLAGS_REMOVE, &new);
+ if (args->op_flags & XFS_DA_OP_NVLOOKUP)
+ op_flag = XFS_ATTRI_OP_FLAGS_NVREMOVE;
+
+ error = xfs_attr_intent_init(args, op_flag, &new);
if (error)
return error;
diff --git a/libxfs/xfs_log_format.h b/libxfs/xfs_log_format.h
index f13e0809d..ecf0ac32d 100644
--- a/libxfs/xfs_log_format.h
+++ b/libxfs/xfs_log_format.h
@@ -957,6 +957,7 @@ struct xfs_icreate_log {
#define XFS_ATTRI_OP_FLAGS_SET 1 /* Set the attribute */
#define XFS_ATTRI_OP_FLAGS_REMOVE 2 /* Remove the attribute */
#define XFS_ATTRI_OP_FLAGS_REPLACE 3 /* Replace the attribute */
+#define XFS_ATTRI_OP_FLAGS_NVREMOVE 4 /* Remove attr w/ vlookup */
#define XFS_ATTRI_OP_FLAGS_TYPE_MASK 0xFF /* Flags type mask */
/*
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 07/10] xfs: log NVLOOKUP xattr setting operations
2023-04-06 19:15 ` [PATCHSET v11 00/10] xfs: name-value xattr lookups Darrick J. Wong
` (5 preceding siblings ...)
2023-04-06 19:30 ` [PATCH 06/10] xfs: log NVLOOKUP xattr removal operations Darrick J. Wong
@ 2023-04-06 19:30 ` Darrick J. Wong
2023-04-06 19:31 ` [PATCH 08/10] xfs: log NVLOOKUP xattr nvreplace operations Darrick J. Wong
` (2 subsequent siblings)
9 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:30 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If high level code wants to do a deferred xattr set operation with the
NVLOOKUP flag set, we need to push this through the log.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_attr.c | 6 +++++-
libxfs/xfs_log_format.h | 1 +
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 9621f8715..a8c778bbd 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -901,9 +901,13 @@ xfs_attr_defer_add(
struct xfs_da_args *args)
{
struct xfs_attr_intent *new;
+ int op_flag = XFS_ATTRI_OP_FLAGS_SET;
int error = 0;
- error = xfs_attr_intent_init(args, XFS_ATTRI_OP_FLAGS_SET, &new);
+ if (args->op_flags & XFS_DA_OP_NVLOOKUP)
+ op_flag = XFS_ATTRI_OP_FLAGS_NVSET;
+
+ error = xfs_attr_intent_init(args, op_flag, &new);
if (error)
return error;
diff --git a/libxfs/xfs_log_format.h b/libxfs/xfs_log_format.h
index ecf0ac32d..7a4226e20 100644
--- a/libxfs/xfs_log_format.h
+++ b/libxfs/xfs_log_format.h
@@ -958,6 +958,7 @@ struct xfs_icreate_log {
#define XFS_ATTRI_OP_FLAGS_REMOVE 2 /* Remove the attribute */
#define XFS_ATTRI_OP_FLAGS_REPLACE 3 /* Replace the attribute */
#define XFS_ATTRI_OP_FLAGS_NVREMOVE 4 /* Remove attr w/ vlookup */
+#define XFS_ATTRI_OP_FLAGS_NVSET 5 /* Set attr with w/ vlookup */
#define XFS_ATTRI_OP_FLAGS_TYPE_MASK 0xFF /* Flags type mask */
/*
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 08/10] xfs: log NVLOOKUP xattr nvreplace operations
2023-04-06 19:15 ` [PATCHSET v11 00/10] xfs: name-value xattr lookups Darrick J. Wong
` (6 preceding siblings ...)
2023-04-06 19:30 ` [PATCH 07/10] xfs: log NVLOOKUP xattr setting operations Darrick J. Wong
@ 2023-04-06 19:31 ` Darrick J. Wong
2023-04-06 19:31 ` [PATCH 09/10] xfs_logprint: dump new attr log item fields Darrick J. Wong
2023-04-06 19:31 ` [PATCH 10/10] xfs_logprint: print missing attri header fields Darrick J. Wong
9 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:31 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
(Formerly titled "xfs: Add new name to attri/d" and described as
follows:
This patch adds two new fields to the atti/d. They are nname and
nnamelen. This will be used for parent pointer updates since a
rename operation may cause the parent pointer to update both the
name and value. So we need to carry both the new name as well as
the target name in the attri/d.)
If high level code wants to do a deferred xattr nvreplace operation with
the NVLOOKUP flag set, we need to push this through the log. This log
item records the old name/value pair and the new name/value pair, and
completely replaces one with the other. Parent pointers will need this
ability to handle rename moving a child file between parents.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: reworked to handle new disk format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_attr.c | 20 +++++++++++++++++++-
libxfs/xfs_attr.h | 4 ++--
libxfs/xfs_da_btree.h | 6 +++++-
libxfs/xfs_log_format.h | 28 +++++++++++++++++++++++-----
4 files changed, 49 insertions(+), 9 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index a8c778bbd..41d7a56c1 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -423,6 +423,20 @@ xfs_attr_complete_op(
return XFS_DAS_DONE;
args->attr_filter &= ~XFS_ATTR_INCOMPLETE;
+ if (xfs_attr_intent_op(attr) != XFS_ATTRI_OP_FLAGS_NVREPLACE)
+ return replace_state;
+
+ /*
+ * NVREPLACE operations require the caller to set the old and new names
+ * and values explicitly.
+ */
+ ASSERT(args->new_namelen > 0);
+
+ args->name = args->new_name;
+ args->namelen = args->new_namelen;
+ args->hashval = xfs_da_hashname(args->name, args->namelen);
+ args->value = args->new_value;
+ args->valuelen = args->new_valuelen;
return replace_state;
}
@@ -924,9 +938,13 @@ xfs_attr_defer_replace(
struct xfs_da_args *args)
{
struct xfs_attr_intent *new;
+ int op_flag = XFS_ATTRI_OP_FLAGS_REPLACE;
int error = 0;
- error = xfs_attr_intent_init(args, XFS_ATTRI_OP_FLAGS_REPLACE, &new);
+ if (args->op_flags & XFS_DA_OP_NVLOOKUP)
+ op_flag = XFS_ATTRI_OP_FLAGS_NVREPLACE;
+
+ error = xfs_attr_intent_init(args, op_flag, &new);
if (error)
return error;
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index f0aa372ec..d543a6a01 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -510,8 +510,8 @@ struct xfs_attr_intent {
struct xfs_da_args *xattri_da_args;
/*
- * Shared buffer containing the attr name and value so that the logging
- * code can share large memory buffers between log items.
+ * Shared buffer containing the attr name, new name, and value so that
+ * the logging code can share large memory buffers between log items.
*/
struct xfs_attri_log_nameval *xattri_nameval;
diff --git a/libxfs/xfs_da_btree.h b/libxfs/xfs_da_btree.h
index 94a544fc8..fc4dc3e87 100644
--- a/libxfs/xfs_da_btree.h
+++ b/libxfs/xfs_da_btree.h
@@ -54,11 +54,15 @@ enum xfs_dacmp {
*/
typedef struct xfs_da_args {
struct xfs_da_geometry *geo; /* da block geometry */
- const uint8_t *name; /* string (maybe not NULL terminated) */
+ const uint8_t *name; /* string (maybe not NULL terminated) */
+ const uint8_t *new_name; /* new attr name */
int namelen; /* length of string (maybe no NULL) */
+ int new_namelen; /* new attr name len */
uint8_t filetype; /* filetype of inode for directories */
void *value; /* set of bytes (maybe contain NULLs) */
+ void *new_value; /* new xattr value (may contain NULLs) */
int valuelen; /* length of value */
+ int new_valuelen; /* length of new attr value */
unsigned int attr_filter; /* XFS_ATTR_{ROOT,SECURE,INCOMPLETE} */
unsigned int attr_flags; /* XATTR_{CREATE,REPLACE} */
xfs_dahash_t hashval; /* hash value of name */
diff --git a/libxfs/xfs_log_format.h b/libxfs/xfs_log_format.h
index 7a4226e20..d666bfa5d 100644
--- a/libxfs/xfs_log_format.h
+++ b/libxfs/xfs_log_format.h
@@ -115,10 +115,11 @@ struct xfs_unmount_log_format {
#define XLOG_REG_TYPE_BUD_FORMAT 26
#define XLOG_REG_TYPE_ATTRI_FORMAT 27
#define XLOG_REG_TYPE_ATTRD_FORMAT 28
-#define XLOG_REG_TYPE_ATTR_NAME 29
+#define XLOG_REG_TYPE_ATTR_NAME 29
#define XLOG_REG_TYPE_ATTR_VALUE 30
-#define XLOG_REG_TYPE_MAX 30
-
+#define XLOG_REG_TYPE_ATTR_NEWNAME 31
+#define XLOG_REG_TYPE_ATTR_NEWVALUE 32
+#define XLOG_REG_TYPE_MAX 32
/*
* Flags to log operation header
@@ -959,6 +960,7 @@ struct xfs_icreate_log {
#define XFS_ATTRI_OP_FLAGS_REPLACE 3 /* Replace the attribute */
#define XFS_ATTRI_OP_FLAGS_NVREMOVE 4 /* Remove attr w/ vlookup */
#define XFS_ATTRI_OP_FLAGS_NVSET 5 /* Set attr with w/ vlookup */
+#define XFS_ATTRI_OP_FLAGS_NVREPLACE 6 /* Replace attr name and val */
#define XFS_ATTRI_OP_FLAGS_TYPE_MASK 0xFF /* Flags type mask */
/*
@@ -976,11 +978,27 @@ struct xfs_icreate_log {
struct xfs_attri_log_format {
uint16_t alfi_type; /* attri log item type */
uint16_t alfi_size; /* size of this item */
- uint32_t __pad; /* pad to 64 bit aligned */
+
+ /*
+ * For NVREPLACE, this is the length of the new xattr value.
+ * alfi_value_len contains the length of the old xattr value.
+ */
+ uint32_t alfi_new_value_len;
+
uint64_t alfi_id; /* attri identifier */
uint64_t alfi_ino; /* the inode for this attr operation */
uint32_t alfi_op_flags; /* marks the op as a set or remove */
- uint32_t alfi_name_len; /* attr name length */
+ union {
+ uint32_t alfi_name_len; /* attr name length */
+ struct {
+ /*
+ * For NVREPLACE, these are the lengths of the old and
+ * new attr name.
+ */
+ uint16_t alfi_old_name_len;
+ uint16_t alfi_new_name_len;
+ };
+ };
uint32_t alfi_value_len; /* attr value length */
uint32_t alfi_attr_filter;/* attr filter flags */
};
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 09/10] xfs_logprint: dump new attr log item fields
2023-04-06 19:15 ` [PATCHSET v11 00/10] xfs: name-value xattr lookups Darrick J. Wong
` (7 preceding siblings ...)
2023-04-06 19:31 ` [PATCH 08/10] xfs: log NVLOOKUP xattr nvreplace operations Darrick J. Wong
@ 2023-04-06 19:31 ` Darrick J. Wong
2023-04-06 19:31 ` [PATCH 10/10] xfs_logprint: print missing attri header fields Darrick J. Wong
9 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:31 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Dump the new extended attribute log item fields. This was split out
from the previous patch to make libxfs resyncing easier. This code
needs more cleaning, which we'll do in the next few patches before
moving on to the parent pointer code.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
logprint/log_redo.c | 132 +++++++++++++++++++++++++++++++++++++++++----------
logprint/logprint.h | 6 ++
2 files changed, 111 insertions(+), 27 deletions(-)
diff --git a/logprint/log_redo.c b/logprint/log_redo.c
index edf7e0fbf..2eaf58918 100644
--- a/logprint/log_redo.c
+++ b/logprint/log_redo.c
@@ -674,6 +674,12 @@ xfs_attri_copy_log_format(
return 1;
}
+static inline unsigned int
+xfs_attr_log_item_op(const struct xfs_attri_log_format *attrp)
+{
+ return attrp->alfi_op_flags & XFS_ATTRI_OP_FLAGS_TYPE_MASK;
+}
+
int
xlog_print_trans_attri(
char **ptr,
@@ -683,6 +689,10 @@ xlog_print_trans_attri(
struct xfs_attri_log_format *src_f = NULL;
xlog_op_header_t *head = NULL;
uint dst_len;
+ unsigned int name_len = 0;
+ unsigned int new_name_len = 0;
+ unsigned int value_len = 0;
+ unsigned int new_value_len = 0;
int error = 0;
dst_len = sizeof(struct xfs_attri_log_format);
@@ -705,27 +715,67 @@ xlog_print_trans_attri(
memmove((char*)src_f, *ptr, src_len);
*ptr += src_len;
- printf(_("ATTRI: #regs: %d name_len: %d, value_len: %d id: 0x%llx\n"),
- src_f->alfi_size, src_f->alfi_name_len, src_f->alfi_value_len,
- (unsigned long long)src_f->alfi_id);
+ if (xfs_attr_log_item_op(src_f) == XFS_ATTRI_OP_FLAGS_NVREPLACE) {
+ name_len = src_f->alfi_old_name_len;
+ new_name_len = src_f->alfi_new_name_len;
+ value_len = src_f->alfi_value_len;
+ new_value_len = src_f->alfi_new_value_len;
+ } else {
+ name_len = src_f->alfi_name_len;
+ value_len = src_f->alfi_value_len;
+ }
+
+ printf(_("ATTRI: #regs: %d name_len: %u, new_name_len: %u, value_len: %u, new_value_len: %u id: 0x%llx\n"),
+ src_f->alfi_size,
+ name_len,
+ new_name_len,
+ value_len,
+ new_value_len,
+ (unsigned long long)src_f->alfi_id);
+
+ if (name_len > 0) {
+ printf(_("\n"));
+ (*i)++;
+ head = (xlog_op_header_t *)*ptr;
+ xlog_print_op_header(head, *i, ptr);
+ error = xlog_print_trans_attri_name(ptr,
+ be32_to_cpu(head->oh_len), "name");
+ if (error)
+ goto error;
+ }
- if (src_f->alfi_name_len > 0) {
+ if (new_name_len > 0) {
printf(_("\n"));
(*i)++;
head = (xlog_op_header_t *)*ptr;
xlog_print_op_header(head, *i, ptr);
- error = xlog_print_trans_attri_name(ptr, be32_to_cpu(head->oh_len));
+ error = xlog_print_trans_attri_name(ptr,
+ be32_to_cpu(head->oh_len), "newname");
if (error)
goto error;
}
- if (src_f->alfi_value_len > 0) {
+ if (value_len > 0) {
printf(_("\n"));
(*i)++;
head = (xlog_op_header_t *)*ptr;
xlog_print_op_header(head, *i, ptr);
- error = xlog_print_trans_attri_value(ptr, be32_to_cpu(head->oh_len),
- src_f->alfi_value_len);
+ error = xlog_print_trans_attri_value(ptr,
+ be32_to_cpu(head->oh_len), value_len, "value");
+ if (error)
+ goto error;
+ }
+
+ if (new_value_len > 0) {
+ printf(_("\n"));
+ (*i)++;
+ head = (xlog_op_header_t *)*ptr;
+ xlog_print_op_header(head, *i, ptr);
+ error = xlog_print_trans_attri_value(ptr,
+ be32_to_cpu(head->oh_len), new_value_len,
+ "newvalue");
+ if (error)
+ goto error;
}
error:
free(src_f);
@@ -736,31 +786,33 @@ xlog_print_trans_attri(
int
xlog_print_trans_attri_name(
char **ptr,
- uint src_len)
+ uint src_len,
+ const char *tag)
{
- printf(_("ATTRI: name len:%u\n"), src_len);
+ printf(_("ATTRI: %s len:%u\n"), tag, src_len);
print_or_dump(*ptr, src_len);
*ptr += src_len;
return 0;
-} /* xlog_print_trans_attri */
+}
int
xlog_print_trans_attri_value(
char **ptr,
uint src_len,
- int value_len)
+ int value_len,
+ const char *tag)
{
int len = min(value_len, src_len);
- printf(_("ATTRI: value len:%u\n"), value_len);
+ printf(_("ATTRI: %s len:%u\n"), tag, value_len);
print_or_dump(*ptr, len);
*ptr += src_len;
return 0;
-} /* xlog_print_trans_attri_value */
+}
void
xlog_recover_print_attri(
@@ -768,7 +820,10 @@ xlog_recover_print_attri(
{
struct xfs_attri_log_format *f, *src_f = NULL;
uint src_len, dst_len;
-
+ unsigned int name_len = 0;
+ unsigned int new_name_len = 0;
+ unsigned int value_len = 0;
+ unsigned int new_value_len = 0;
int region = 0;
src_f = (struct xfs_attri_log_format *)item->ri_buf[0].i_addr;
@@ -788,24 +843,51 @@ xlog_recover_print_attri(
if (xfs_attri_copy_log_format((char*)src_f, src_len, f))
goto out;
- printf(_("ATTRI: #regs: %d name_len: %d, value_len: %d id: 0x%llx\n"),
- f->alfi_size, f->alfi_name_len, f->alfi_value_len, (unsigned long long)f->alfi_id);
+ if (xfs_attr_log_item_op(f) == XFS_ATTRI_OP_FLAGS_NVREPLACE) {
+ name_len = f->alfi_old_name_len;
+ new_name_len = f->alfi_new_name_len;
+ value_len = f->alfi_value_len;
+ new_value_len = f->alfi_new_value_len;
+ } else {
+ name_len = f->alfi_name_len;
+ value_len = f->alfi_value_len;
+ }
+
+ printf(_("ATTRI: #regs: %d name_len: %u, new_name_len: %u, value_len: %d, new_value_len: %u id: 0x%llx\n"),
+ f->alfi_size,
+ name_len,
+ new_name_len,
+ value_len,
+ new_value_len,
+ (unsigned long long)f->alfi_id);
- if (f->alfi_name_len > 0) {
+ if (name_len > 0) {
region++;
- printf(_("ATTRI: name len:%u\n"), f->alfi_name_len);
+ printf(_("ATTRI: name len:%u\n"), name_len);
print_or_dump((char *)item->ri_buf[region].i_addr,
- f->alfi_name_len);
+ name_len);
}
- if (f->alfi_value_len > 0) {
- int len = f->alfi_value_len;
+ if (new_name_len > 0) {
+ region++;
+ printf(_("ATTRI: newname len:%u\n"), new_name_len);
+ print_or_dump((char *)item->ri_buf[region].i_addr,
+ new_name_len);
+ }
+
+ if (value_len > 0) {
+ int len = min(MAX_ATTR_VAL_PRINT, value_len);
+
+ region++;
+ printf(_("ATTRI: value len:%u\n"), value_len);
+ print_or_dump((char *)item->ri_buf[region].i_addr, len);
+ }
- if (len > MAX_ATTR_VAL_PRINT)
- len = MAX_ATTR_VAL_PRINT;
+ if (new_value_len > 0) {
+ int len = min(MAX_ATTR_VAL_PRINT, new_value_len);
region++;
- printf(_("ATTRI: value len:%u\n"), f->alfi_value_len);
+ printf(_("ATTRI: newvalue len:%u\n"), new_value_len);
print_or_dump((char *)item->ri_buf[region].i_addr, len);
}
diff --git a/logprint/logprint.h b/logprint/logprint.h
index b4479c240..27f1cd9a7 100644
--- a/logprint/logprint.h
+++ b/logprint/logprint.h
@@ -59,8 +59,10 @@ extern void xlog_recover_print_bud(struct xlog_recover_item *item);
#define MAX_ATTR_VAL_PRINT 128
extern int xlog_print_trans_attri(char **ptr, uint src_len, int *i);
-extern int xlog_print_trans_attri_name(char **ptr, uint src_len);
-extern int xlog_print_trans_attri_value(char **ptr, uint src_len, int value_len);
+extern int xlog_print_trans_attri_name(char **ptr, uint src_len,
+ const char *tag);
+extern int xlog_print_trans_attri_value(char **ptr, uint src_len, int value_len,
+ const char *tag);
extern void xlog_recover_print_attri(struct xlog_recover_item *item);
extern int xlog_print_trans_attrd(char **ptr, uint len);
extern void xlog_recover_print_attrd(struct xlog_recover_item *item);
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 10/10] xfs_logprint: print missing attri header fields
2023-04-06 19:15 ` [PATCHSET v11 00/10] xfs: name-value xattr lookups Darrick J. Wong
` (8 preceding siblings ...)
2023-04-06 19:31 ` [PATCH 09/10] xfs_logprint: dump new attr log item fields Darrick J. Wong
@ 2023-04-06 19:31 ` Darrick J. Wong
9 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:31 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Not sure why logprint doesn't print the op flags, inode, or attr filter
fields. Make it do that.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
logprint/log_redo.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/logprint/log_redo.c b/logprint/log_redo.c
index 2eaf58918..8b6aa9279 100644
--- a/logprint/log_redo.c
+++ b/logprint/log_redo.c
@@ -725,8 +725,11 @@ xlog_print_trans_attri(
value_len = src_f->alfi_value_len;
}
- printf(_("ATTRI: #regs: %d name_len: %u, new_name_len: %u, value_len: %u, new_value_len: %u id: 0x%llx\n"),
+ printf(_("ATTRI: #regs: %d f: 0x%x, ino: 0x%llx, attr_filter: 0x%x, name_len: %u, new_name_len: %u, value_len: %u, new_value_len: %u id: 0x%llx\n"),
src_f->alfi_size,
+ src_f->alfi_op_flags,
+ (unsigned long long)src_f->alfi_ino,
+ src_f->alfi_attr_filter,
name_len,
new_name_len,
value_len,
@@ -853,8 +856,11 @@ xlog_recover_print_attri(
value_len = f->alfi_value_len;
}
- printf(_("ATTRI: #regs: %d name_len: %u, new_name_len: %u, value_len: %d, new_value_len: %u id: 0x%llx\n"),
+ printf(_("ATTRI: #regs: %d f: 0x%x, ino: 0x%llx, attr_filter: 0x%x, name_len: %u, new_name_len: %u, value_len: %u, new_value_len: %u id: 0x%llx\n"),
f->alfi_size,
+ f->alfi_op_flags,
+ (unsigned long long)f->alfi_ino,
+ f->alfi_attr_filter,
name_len,
new_name_len,
value_len,
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 01/32] xfsprogs: Increase XFS_DEFER_OPS_NR_INODES to 5
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
@ 2023-04-06 19:31 ` Darrick J. Wong
2023-04-06 19:32 ` [PATCH 02/32] xfs: add parent pointer support to attribute code Darrick J. Wong
` (30 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:31 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, Catherine Hoang, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: e9dc6a1e293b7e3843cd3868603801a1af2704c3
Renames that generate parent pointer updates can join up to 5
inodes locked in sorted order. So we need to increase the
number of defer ops inodes and relock them in the same way.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Catherine Hoang <catherine.hoang@oracle.com>
---
libxfs/libxfs_priv.h | 1 +
libxfs/xfs_defer.c | 28 ++++++++++++++++++++++++++--
libxfs/xfs_defer.h | 8 +++++++-
3 files changed, 34 insertions(+), 3 deletions(-)
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index e5f37df28..98966f584 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -478,6 +478,7 @@ void __xfs_buf_mark_corrupt(struct xfs_buf *bp, xfs_failaddr_t fa);
__mode = __mode; /* no set-but-unused warning */ \
})
#define xfs_lock_two_inodes(ip0,mode0,ip1,mode1) ((void) 0)
+#define xfs_lock_inodes(ips,num_ips,mode) ((void) 0)
/* space allocation */
#define XFS_EXTENT_BUSY_DISCARDED 0x01 /* undergoing a discard op. */
diff --git a/libxfs/xfs_defer.c b/libxfs/xfs_defer.c
index c4f0269d6..415fcaf56 100644
--- a/libxfs/xfs_defer.c
+++ b/libxfs/xfs_defer.c
@@ -815,13 +815,37 @@ xfs_defer_ops_continue(
struct xfs_trans *tp,
struct xfs_defer_resources *dres)
{
- unsigned int i;
+ unsigned int i, j;
+ struct xfs_inode *sips[XFS_DEFER_OPS_NR_INODES];
+ struct xfs_inode *temp;
ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
ASSERT(!(tp->t_flags & XFS_TRANS_DIRTY));
/* Lock the captured resources to the new transaction. */
- if (dfc->dfc_held.dr_inos == 2)
+ if (dfc->dfc_held.dr_inos > 2) {
+ /*
+ * Renames with parent pointer updates can lock up to 5 inodes,
+ * sorted by their inode number. So we need to make sure they
+ * are relocked in the same way.
+ */
+ memset(sips, 0, sizeof(sips));
+ for (i = 0; i < dfc->dfc_held.dr_inos; i++)
+ sips[i] = dfc->dfc_held.dr_ip[i];
+
+ /* Bubble sort of at most 5 inodes */
+ for (i = 0; i < dfc->dfc_held.dr_inos; i++) {
+ for (j = 1; j < dfc->dfc_held.dr_inos; j++) {
+ if (sips[j]->i_ino < sips[j-1]->i_ino) {
+ temp = sips[j];
+ sips[j] = sips[j-1];
+ sips[j-1] = temp;
+ }
+ }
+ }
+
+ xfs_lock_inodes(sips, dfc->dfc_held.dr_inos, XFS_ILOCK_EXCL);
+ } else if (dfc->dfc_held.dr_inos == 2)
xfs_lock_two_inodes(dfc->dfc_held.dr_ip[0], XFS_ILOCK_EXCL,
dfc->dfc_held.dr_ip[1], XFS_ILOCK_EXCL);
else if (dfc->dfc_held.dr_inos == 1)
diff --git a/libxfs/xfs_defer.h b/libxfs/xfs_defer.h
index 114a3a493..fdf6941f8 100644
--- a/libxfs/xfs_defer.h
+++ b/libxfs/xfs_defer.h
@@ -70,7 +70,13 @@ extern const struct xfs_defer_op_type xfs_attr_defer_type;
/*
* Deferred operation item relogging limits.
*/
-#define XFS_DEFER_OPS_NR_INODES 2 /* join up to two inodes */
+
+/*
+ * Rename w/ parent pointers can require up to 5 inodes with deferred ops to
+ * be joined to the transaction: src_dp, target_dp, src_ip, target_ip, and wip.
+ * These inodes are locked in sorted order by their inode numbers
+ */
+#define XFS_DEFER_OPS_NR_INODES 5
#define XFS_DEFER_OPS_NR_BUFS 2 /* join up to two buffers */
/* Resources that must be held across a transaction roll. */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 02/32] xfs: add parent pointer support to attribute code
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
2023-04-06 19:31 ` [PATCH 01/32] xfsprogs: Increase XFS_DEFER_OPS_NR_INODES to 5 Darrick J. Wong
@ 2023-04-06 19:32 ` Darrick J. Wong
2023-04-06 19:32 ` [PATCH 03/32] xfs: define parent pointer ondisk extended attribute format Darrick J. Wong
` (29 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:32 UTC (permalink / raw)
To: djwong
Cc: Mark Tinguely, Dave Chinner, Allison Henderson, allison.henderson,
linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Add the new parent attribute type. XFS_ATTR_PARENT is used only for parent pointer
entries; it uses reserved blocks like XFS_ATTR_ROOT.
Signed-off-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_attr.c | 3 ++-
libxfs/xfs_da_format.h | 5 ++++-
libxfs/xfs_log_format.h | 1 +
3 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 41d7a56c1..13bc77f7c 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -990,7 +990,8 @@ xfs_attr_set(
struct xfs_inode *dp = args->dp;
struct xfs_mount *mp = dp->i_mount;
struct xfs_trans_res tres;
- bool rsvd = (args->attr_filter & XFS_ATTR_ROOT);
+ bool rsvd = (args->attr_filter & (XFS_ATTR_ROOT |
+ XFS_ATTR_PARENT));
bool is_remove = args->op_flags & XFS_DA_OP_REMOVE;
int error, local;
int rmt_blks = 0;
diff --git a/libxfs/xfs_da_format.h b/libxfs/xfs_da_format.h
index 25e284108..3dc03968b 100644
--- a/libxfs/xfs_da_format.h
+++ b/libxfs/xfs_da_format.h
@@ -688,12 +688,15 @@ struct xfs_attr3_leafblock {
#define XFS_ATTR_LOCAL_BIT 0 /* attr is stored locally */
#define XFS_ATTR_ROOT_BIT 1 /* limit access to trusted attrs */
#define XFS_ATTR_SECURE_BIT 2 /* limit access to secure attrs */
+#define XFS_ATTR_PARENT_BIT 3 /* parent pointer attrs */
#define XFS_ATTR_INCOMPLETE_BIT 7 /* attr in middle of create/delete */
#define XFS_ATTR_LOCAL (1u << XFS_ATTR_LOCAL_BIT)
#define XFS_ATTR_ROOT (1u << XFS_ATTR_ROOT_BIT)
#define XFS_ATTR_SECURE (1u << XFS_ATTR_SECURE_BIT)
+#define XFS_ATTR_PARENT (1u << XFS_ATTR_PARENT_BIT)
#define XFS_ATTR_INCOMPLETE (1u << XFS_ATTR_INCOMPLETE_BIT)
-#define XFS_ATTR_NSP_ONDISK_MASK (XFS_ATTR_ROOT | XFS_ATTR_SECURE)
+#define XFS_ATTR_NSP_ONDISK_MASK \
+ (XFS_ATTR_ROOT | XFS_ATTR_SECURE | XFS_ATTR_PARENT)
/*
* Alignment for namelist and valuelist entries (since they are mixed
diff --git a/libxfs/xfs_log_format.h b/libxfs/xfs_log_format.h
index d666bfa5d..5728805a3 100644
--- a/libxfs/xfs_log_format.h
+++ b/libxfs/xfs_log_format.h
@@ -969,6 +969,7 @@ struct xfs_icreate_log {
*/
#define XFS_ATTRI_FILTER_MASK (XFS_ATTR_ROOT | \
XFS_ATTR_SECURE | \
+ XFS_ATTR_PARENT | \
XFS_ATTR_INCOMPLETE)
/*
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 03/32] xfs: define parent pointer ondisk extended attribute format
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
2023-04-06 19:31 ` [PATCH 01/32] xfsprogs: Increase XFS_DEFER_OPS_NR_INODES to 5 Darrick J. Wong
2023-04-06 19:32 ` [PATCH 02/32] xfs: add parent pointer support to attribute code Darrick J. Wong
@ 2023-04-06 19:32 ` Darrick J. Wong
2023-04-06 19:32 ` [PATCH 04/32] xfs: add parent pointer validator functions Darrick J. Wong
` (28 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:32 UTC (permalink / raw)
To: djwong; +Cc: Dave Chinner, Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
We need to define the parent pointer attribute format before we start
adding support for it into all the code that needs to use it. The EA
format we will use encodes the following information:
name={parent inode #, parent inode generation, dirent namehash}
value={dirent name}
The inode/gen gives all the information we need to reliably identify the
parent without requiring child->parent lock ordering, and allows
userspace to do pathname component level reconstruction without the
kernel ever needing to verify the parent itself as part of ioctl calls.
Storing the dirent name hash in the key reduces hash collisions if a
file is hardlinked multiple times in the same directory.
By using the NVLOOKUP mode in the extended attribute code to match
parent pointers using both the xattr name and value, we can identify the
exact parent pointer EA we need to modify/remove in rename/unlink
operations without searching the entire EA space.
By storing the dirent name, we have enough information to be able to
validate and reconstruct damaged directory trees. Earlier iterations of
this patchset encoded the directory offset in the parent pointer key,
but this format required repair to keep that in sync across directory
rebuilds, which is unnecessary complexity.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: replace diroffset with the namehash in the pptr key]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_da_format.h | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/libxfs/xfs_da_format.h b/libxfs/xfs_da_format.h
index 3dc03968b..40016eb08 100644
--- a/libxfs/xfs_da_format.h
+++ b/libxfs/xfs_da_format.h
@@ -805,4 +805,25 @@ static inline unsigned int xfs_dir2_dirblock_bytes(struct xfs_sb *sbp)
xfs_failaddr_t xfs_da3_blkinfo_verify(struct xfs_buf *bp,
struct xfs_da3_blkinfo *hdr3);
+/*
+ * Parent pointer attribute format definition
+ *
+ * The xattr name encodes the parent inode number, generation and the crc32c
+ * hash of the dirent name.
+ *
+ * The xattr value contains the dirent name.
+ */
+struct xfs_parent_name_rec {
+ __be64 p_ino;
+ __be32 p_gen;
+ __be32 p_namehash;
+};
+
+/*
+ * Maximum size of the dirent name that can be stored in a parent pointer.
+ * This matches the maximum dirent name length.
+ */
+#define XFS_PARENT_DIRENT_NAME_MAX_SIZE \
+ (MAXNAMELEN - 1)
+
#endif /* __XFS_DA_FORMAT_H__ */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 04/32] xfs: add parent pointer validator functions
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (2 preceding siblings ...)
2023-04-06 19:32 ` [PATCH 03/32] xfs: define parent pointer ondisk extended attribute format Darrick J. Wong
@ 2023-04-06 19:32 ` Darrick J. Wong
2023-04-06 19:32 ` [PATCH 05/32] xfs: extend transaction reservations for parent attributes Darrick J. Wong
` (27 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:32 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Attribute names of parent pointers are not strings. So we need to
modify attr_namecheck to verify parent pointer records when the
XFS_ATTR_PARENT flag is set. At the same time, we need to validate attr
values during log recovery if the xattr is really a parent pointer.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: move functions to xfs_parent.c, adjust for new disk format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/Makefile | 2 +
libxfs/xfs_attr.c | 10 ++++-
libxfs/xfs_attr.h | 3 +-
libxfs/xfs_da_format.h | 8 ++++
libxfs/xfs_parent.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++
libxfs/xfs_parent.h | 16 ++++++++
repair/attr_repair.c | 19 ++++++----
7 files changed, 140 insertions(+), 10 deletions(-)
create mode 100644 libxfs/xfs_parent.c
create mode 100644 libxfs/xfs_parent.h
diff --git a/libxfs/Makefile b/libxfs/Makefile
index 010ee68e2..89d29dc97 100644
--- a/libxfs/Makefile
+++ b/libxfs/Makefile
@@ -45,6 +45,7 @@ HFILES = \
xfs_ialloc_btree.h \
xfs_inode_buf.h \
xfs_inode_fork.h \
+ xfs_parent.h \
xfs_quota_defs.h \
xfs_refcount.h \
xfs_refcount_btree.h \
@@ -92,6 +93,7 @@ CFILES = cache.c \
xfs_inode_fork.c \
xfs_ialloc_btree.c \
xfs_log_rlimit.c \
+ xfs_parent.c \
xfs_refcount.c \
xfs_refcount_btree.c \
xfs_rmap.c \
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 13bc77f7c..809c285d4 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -24,6 +24,7 @@
#include "xfs_quota_defs.h"
#include "xfs_trans_space.h"
#include "xfs_trace.h"
+#include "xfs_parent.h"
struct kmem_cache *xfs_attr_intent_cache;
@@ -1594,9 +1595,14 @@ xfs_attr_node_get(
/* Returns true if the attribute entry name is valid. */
bool
xfs_attr_namecheck(
- const void *name,
- size_t length)
+ struct xfs_mount *mp,
+ const void *name,
+ size_t length,
+ unsigned int flags)
{
+ if (flags & XFS_ATTR_PARENT)
+ return xfs_parent_namecheck(mp, name, length, flags);
+
/*
* MAXNAMELEN includes the trailing null, but (name/length) leave it
* out, so use >= for the length check.
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index d543a6a01..13b65aaf3 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -552,7 +552,8 @@ int xfs_attr_get(struct xfs_da_args *args);
int xfs_attr_set(struct xfs_da_args *args);
int xfs_attr_set_iter(struct xfs_attr_intent *attr);
int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
-bool xfs_attr_namecheck(const void *name, size_t length);
+bool xfs_attr_namecheck(struct xfs_mount *mp, const void *name, size_t length,
+ unsigned int flags);
int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
void xfs_init_attr_trans(struct xfs_da_args *args, struct xfs_trans_res *tres,
unsigned int *total);
diff --git a/libxfs/xfs_da_format.h b/libxfs/xfs_da_format.h
index 40016eb08..7426f9052 100644
--- a/libxfs/xfs_da_format.h
+++ b/libxfs/xfs_da_format.h
@@ -731,6 +731,14 @@ xfs_attr3_leaf_name(xfs_attr_leafblock_t *leafp, int idx)
return &((char *)leafp)[be16_to_cpu(entries[idx].nameidx)];
}
+static inline int
+xfs_attr3_leaf_flags(xfs_attr_leafblock_t *leafp, int idx)
+{
+ struct xfs_attr_leaf_entry *entries = xfs_attr3_leaf_entryp(leafp);
+
+ return entries[idx].flags;
+}
+
static inline xfs_attr_leaf_name_remote_t *
xfs_attr3_leaf_name_remote(xfs_attr_leafblock_t *leafp, int idx)
{
diff --git a/libxfs/xfs_parent.c b/libxfs/xfs_parent.c
new file mode 100644
index 000000000..583607c13
--- /dev/null
+++ b/libxfs/xfs_parent.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2023 Oracle, Inc.
+ * All rights reserved.
+ */
+#include "libxfs_priv.h"
+#include "xfs_shared.h"
+#include "xfs_format.h"
+#include "xfs_log_format.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_inode.h"
+#include "xfs_trans_resv.h"
+#include "xfs_mount.h"
+#include "xfs_trace.h"
+#include "xfs.h"
+#include "xfs_fs.h"
+#include "xfs_da_format.h"
+#include "xfs_bmap_btree.h"
+#include "xfs_trans.h"
+#include "xfs_da_btree.h"
+#include "xfs_attr.h"
+#include "xfs_dir2.h"
+#include "xfs_attr_sf.h"
+#include "xfs_bmap.h"
+#include "xfs_parent.h"
+#include "xfs_da_format.h"
+#include "xfs_format.h"
+#include "xfs_trans_space.h"
+
+/*
+ * Parent pointer attribute handling.
+ *
+ * Because the attribute value is a filename component, it will never be longer
+ * than 255 bytes. This means the attribute will always be a local format
+ * attribute as it is xfs_attr_leaf_entsize_local_max() for v5 filesystems will
+ * always be larger than this (max is 75% of block size).
+ *
+ * Creating a new parent attribute will always create a new attribute - there
+ * should never, ever be an existing attribute in the tree for a new inode.
+ * ENOSPC behavior is problematic - creating the inode without the parent
+ * pointer is effectively a corruption, so we allow parent attribute creation
+ * to dip into the reserve block pool to avoid unexpected ENOSPC errors from
+ * occurring.
+ */
+
+/* Return true if parent pointer EA name is valid. */
+bool
+xfs_parent_namecheck(
+ struct xfs_mount *mp,
+ const struct xfs_parent_name_rec *rec,
+ size_t reclen,
+ unsigned int attr_flags)
+{
+ xfs_ino_t p_ino;
+
+ if (!(attr_flags & XFS_ATTR_PARENT))
+ return false;
+
+ if (reclen != sizeof(struct xfs_parent_name_rec))
+ return false;
+
+ /* Only one namespace bit allowed. */
+ if (hweight32(attr_flags & XFS_ATTR_NSP_ONDISK_MASK) > 1)
+ return false;
+
+ p_ino = be64_to_cpu(rec->p_ino);
+ if (!xfs_verify_ino(mp, p_ino))
+ return false;
+
+ return true;
+}
+
+/* Return true if parent pointer EA value is valid. */
+bool
+xfs_parent_valuecheck(
+ struct xfs_mount *mp,
+ const void *value,
+ size_t valuelen)
+{
+ if (valuelen == 0 || valuelen > XFS_PARENT_DIRENT_NAME_MAX_SIZE)
+ return false;
+
+ if (value == NULL)
+ return false;
+
+ /* Valid dirent name? */
+ if (!xfs_dir2_namecheck(value, valuelen))
+ return false;
+
+ return true;
+}
diff --git a/libxfs/xfs_parent.h b/libxfs/xfs_parent.h
new file mode 100644
index 000000000..6e2a2528d
--- /dev/null
+++ b/libxfs/xfs_parent.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2023 Oracle, Inc.
+ * All Rights Reserved.
+ */
+#ifndef __XFS_PARENT_H__
+#define __XFS_PARENT_H__
+
+/* Metadata validators */
+bool xfs_parent_namecheck(struct xfs_mount *mp,
+ const struct xfs_parent_name_rec *rec, size_t reclen,
+ unsigned int attr_flags);
+bool xfs_parent_valuecheck(struct xfs_mount *mp, const void *value,
+ size_t valuelen);
+
+#endif /* __XFS_PARENT_H__ */
diff --git a/repair/attr_repair.c b/repair/attr_repair.c
index c3a6d5026..afe8073ca 100644
--- a/repair/attr_repair.c
+++ b/repair/attr_repair.c
@@ -293,8 +293,9 @@ process_shortform_attr(
}
/* namecheck checks for null chars in attr names. */
- if (!libxfs_attr_namecheck(currententry->nameval,
- currententry->namelen)) {
+ if (!libxfs_attr_namecheck(mp, currententry->nameval,
+ currententry->namelen,
+ currententry->flags)) {
do_warn(
_("entry contains illegal character in shortform attribute name\n"));
junkit = 1;
@@ -454,12 +455,14 @@ process_leaf_attr_local(
xfs_dablk_t da_bno,
xfs_ino_t ino)
{
- xfs_attr_leaf_name_local_t *local;
+ xfs_attr_leaf_name_local_t *local;
+ int flags;
local = xfs_attr3_leaf_name_local(leaf, i);
+ flags = xfs_attr3_leaf_flags(leaf, i);
if (local->namelen == 0 ||
- !libxfs_attr_namecheck(local->nameval,
- local->namelen)) {
+ !libxfs_attr_namecheck(mp, local->nameval,
+ local->namelen, flags)) {
do_warn(
_("attribute entry %d in attr block %u, inode %" PRIu64 " has bad name (namelen = %d)\n"),
i, da_bno, ino, local->namelen);
@@ -510,12 +513,14 @@ process_leaf_attr_remote(
{
xfs_attr_leaf_name_remote_t *remotep;
char* value;
+ int flags;
remotep = xfs_attr3_leaf_name_remote(leaf, i);
+ flags = xfs_attr3_leaf_flags(leaf, i);
if (remotep->namelen == 0 ||
- !libxfs_attr_namecheck(remotep->name,
- remotep->namelen) ||
+ !libxfs_attr_namecheck(mp, remotep->name,
+ remotep->namelen, flags) ||
be32_to_cpu(entry->hashval) !=
libxfs_da_hashname((unsigned char *)&remotep->name[0],
remotep->namelen) ||
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 05/32] xfs: extend transaction reservations for parent attributes
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (3 preceding siblings ...)
2023-04-06 19:32 ` [PATCH 04/32] xfs: add parent pointer validator functions Darrick J. Wong
@ 2023-04-06 19:32 ` Darrick J. Wong
2023-04-06 19:33 ` [PATCH 06/32] xfs: parent pointer attribute creation Darrick J. Wong
` (26 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:32 UTC (permalink / raw)
To: djwong; +Cc: Dave Chinner, Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
We need to add, remove or modify parent pointer attributes during
create/link/unlink/rename operations atomically with the dirents in the
parent directories being modified. This means they need to be modified
in the same transaction as the parent directories, and so we need to add
the required space for the attribute modifications to the transaction
reservations.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: fix indenting errors, adjust for new log format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/libxfs_priv.h | 1
libxfs/xfs_trans_resv.c | 324 +++++++++++++++++++++++++++++++++++++++--------
2 files changed, 273 insertions(+), 52 deletions(-)
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index 98966f584..db6c40131 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -523,6 +523,7 @@ static inline int retzero(void) { return 0; }
#define xfs_icreate_log(tp, agno, agbno, cnt, isize, len, gen) ((void) 0)
#define xfs_sb_validate_fsb_count(sbp, nblks) (0)
+#define xlog_calc_iovec_len(len) roundup(len, sizeof(uint32_t))
/*
* Prototypes for kernel static functions that are aren't in their
diff --git a/libxfs/xfs_trans_resv.c b/libxfs/xfs_trans_resv.c
index 04c444806..b555d455f 100644
--- a/libxfs/xfs_trans_resv.c
+++ b/libxfs/xfs_trans_resv.c
@@ -18,6 +18,7 @@
#include "xfs_trans.h"
#include "xfs_trans_space.h"
#include "xfs_quota_defs.h"
+#include "xfs_da_format.h"
#define _ALLOC true
#define _FREE false
@@ -419,29 +420,110 @@ xfs_calc_itruncate_reservation_minlogsize(
return xfs_calc_itruncate_reservation(mp, true);
}
+static inline unsigned int xfs_calc_pptr_link_overhead(void)
+{
+ return sizeof(struct xfs_attri_log_format) +
+ xlog_calc_iovec_len(sizeof(struct xfs_parent_name_rec)) +
+ xlog_calc_iovec_len(XFS_PARENT_DIRENT_NAME_MAX_SIZE);
+}
+static inline unsigned int xfs_calc_pptr_unlink_overhead(void)
+{
+ return sizeof(struct xfs_attri_log_format) +
+ xlog_calc_iovec_len(sizeof(struct xfs_parent_name_rec)) +
+ xlog_calc_iovec_len(XFS_PARENT_DIRENT_NAME_MAX_SIZE);
+}
+static inline unsigned int xfs_calc_pptr_replace_overhead(void)
+{
+ return sizeof(struct xfs_attri_log_format) +
+ xlog_calc_iovec_len(sizeof(struct xfs_parent_name_rec)) +
+ xlog_calc_iovec_len(XFS_PARENT_DIRENT_NAME_MAX_SIZE) +
+ xlog_calc_iovec_len(sizeof(struct xfs_parent_name_rec)) +
+ xlog_calc_iovec_len(XFS_PARENT_DIRENT_NAME_MAX_SIZE);
+}
+
/*
* In renaming a files we can modify:
* the five inodes involved: 5 * inode size
* the two directory btrees: 2 * (max depth + v2) * dir block size
* the two directory bmap btrees: 2 * max depth * block size
* And the bmap_finish transaction can free dir and bmap blocks (two sets
- * of bmap blocks) giving:
+ * of bmap blocks) giving (t2):
* the agf for the ags in which the blocks live: 3 * sector size
* the agfl for the ags in which the blocks live: 3 * sector size
* the superblock for the free block count: sector size
* the allocation btrees: 3 exts * 2 trees * (2 * max depth - 1) * block size
+ * If parent pointers are enabled (t3), then each transaction in the chain
+ * must be capable of setting or removing the extended attribute
+ * containing the parent information. It must also be able to handle
+ * the three xattr intent items that track the progress of the parent
+ * pointer update.
*/
STATIC uint
xfs_calc_rename_reservation(
struct xfs_mount *mp)
{
- return XFS_DQUOT_LOGRES(mp) +
- max((xfs_calc_inode_res(mp, 5) +
- xfs_calc_buf_res(2 * XFS_DIROP_LOG_COUNT(mp),
- XFS_FSB_TO_B(mp, 1))),
- (xfs_calc_buf_res(7, mp->m_sb.sb_sectsize) +
- xfs_calc_buf_res(xfs_allocfree_block_count(mp, 3),
- XFS_FSB_TO_B(mp, 1))));
+ unsigned int overhead = XFS_DQUOT_LOGRES(mp);
+ struct xfs_trans_resv *resp = M_RES(mp);
+ unsigned int t1, t2, t3 = 0;
+
+ t1 = xfs_calc_inode_res(mp, 5) +
+ xfs_calc_buf_res(2 * XFS_DIROP_LOG_COUNT(mp),
+ XFS_FSB_TO_B(mp, 1));
+
+ t2 = xfs_calc_buf_res(7, mp->m_sb.sb_sectsize) +
+ xfs_calc_buf_res(xfs_allocfree_block_count(mp, 3),
+ XFS_FSB_TO_B(mp, 1));
+
+ if (xfs_has_parent(mp)) {
+ unsigned int rename_overhead, exchange_overhead;
+
+ t3 = max(resp->tr_attrsetm.tr_logres,
+ resp->tr_attrrm.tr_logres);
+
+ /*
+ * For a standard rename, the three xattr intent log items
+ * are (1) replacing the pptr for the source file; (2)
+ * removing the pptr on the dest file; and (3) adding a
+ * pptr for the whiteout file in the src dir.
+ *
+ * For an RENAME_EXCHANGE, there are two xattr intent
+ * items to replace the pptr for both src and dest
+ * files. Link counts don't change and there is no
+ * whiteout.
+ *
+ * In the worst case we can end up relogging all log
+ * intent items to allow the log tail to move ahead, so
+ * they become overhead added to each transaction in a
+ * processing chain.
+ */
+ rename_overhead = xfs_calc_pptr_replace_overhead() +
+ xfs_calc_pptr_unlink_overhead() +
+ xfs_calc_pptr_link_overhead();
+ exchange_overhead = 2 * xfs_calc_pptr_replace_overhead();
+
+ overhead += max(rename_overhead, exchange_overhead);
+ }
+
+ return overhead + max3(t1, t2, t3);
+}
+
+static inline unsigned int
+xfs_rename_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ /* One for the rename, one more for freeing blocks */
+ unsigned int ret = XFS_RENAME_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to remove or add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += max(resp->tr_attrsetm.tr_logcount,
+ resp->tr_attrrm.tr_logcount);
+
+ return ret;
}
/*
@@ -458,6 +540,23 @@ xfs_calc_iunlink_remove_reservation(
2 * M_IGEO(mp)->inode_cluster_size;
}
+static inline unsigned int
+xfs_link_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ unsigned int ret = XFS_LINK_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += resp->tr_attrsetm.tr_logcount;
+
+ return ret;
+}
+
/*
* For creating a link to an inode:
* the parent directory inode: inode size
@@ -474,14 +573,23 @@ STATIC uint
xfs_calc_link_reservation(
struct xfs_mount *mp)
{
- return XFS_DQUOT_LOGRES(mp) +
- xfs_calc_iunlink_remove_reservation(mp) +
- max((xfs_calc_inode_res(mp, 2) +
- xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),
- XFS_FSB_TO_B(mp, 1))),
- (xfs_calc_buf_res(3, mp->m_sb.sb_sectsize) +
- xfs_calc_buf_res(xfs_allocfree_block_count(mp, 1),
- XFS_FSB_TO_B(mp, 1))));
+ unsigned int overhead = XFS_DQUOT_LOGRES(mp);
+ struct xfs_trans_resv *resp = M_RES(mp);
+ unsigned int t1, t2, t3 = 0;
+
+ overhead += xfs_calc_iunlink_remove_reservation(mp);
+ t1 = xfs_calc_inode_res(mp, 2) +
+ xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1));
+ t2 = xfs_calc_buf_res(3, mp->m_sb.sb_sectsize) +
+ xfs_calc_buf_res(xfs_allocfree_block_count(mp, 1),
+ XFS_FSB_TO_B(mp, 1));
+
+ if (xfs_has_parent(mp)) {
+ t3 = resp->tr_attrsetm.tr_logres;
+ overhead += xfs_calc_pptr_link_overhead();
+ }
+
+ return overhead + max3(t1, t2, t3);
}
/*
@@ -496,6 +604,23 @@ xfs_calc_iunlink_add_reservation(xfs_mount_t *mp)
M_IGEO(mp)->inode_cluster_size;
}
+static inline unsigned int
+xfs_remove_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ unsigned int ret = XFS_REMOVE_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += resp->tr_attrrm.tr_logcount;
+
+ return ret;
+}
+
/*
* For removing a directory entry we can modify:
* the parent directory inode: inode size
@@ -512,14 +637,24 @@ STATIC uint
xfs_calc_remove_reservation(
struct xfs_mount *mp)
{
- return XFS_DQUOT_LOGRES(mp) +
- xfs_calc_iunlink_add_reservation(mp) +
- max((xfs_calc_inode_res(mp, 2) +
- xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp),
- XFS_FSB_TO_B(mp, 1))),
- (xfs_calc_buf_res(4, mp->m_sb.sb_sectsize) +
- xfs_calc_buf_res(xfs_allocfree_block_count(mp, 2),
- XFS_FSB_TO_B(mp, 1))));
+ unsigned int overhead = XFS_DQUOT_LOGRES(mp);
+ struct xfs_trans_resv *resp = M_RES(mp);
+ unsigned int t1, t2, t3 = 0;
+
+ overhead += xfs_calc_iunlink_add_reservation(mp);
+
+ t1 = xfs_calc_inode_res(mp, 2) +
+ xfs_calc_buf_res(XFS_DIROP_LOG_COUNT(mp), XFS_FSB_TO_B(mp, 1));
+ t2 = xfs_calc_buf_res(4, mp->m_sb.sb_sectsize) +
+ xfs_calc_buf_res(xfs_allocfree_block_count(mp, 2),
+ XFS_FSB_TO_B(mp, 1));
+
+ if (xfs_has_parent(mp)) {
+ t3 = resp->tr_attrrm.tr_logres;
+ overhead += xfs_calc_pptr_unlink_overhead();
+ }
+
+ return overhead + max3(t1, t2, t3);
}
/*
@@ -568,12 +703,40 @@ xfs_calc_icreate_resv_alloc(
xfs_calc_finobt_res(mp);
}
+static inline unsigned int
+xfs_icreate_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ unsigned int ret = XFS_CREATE_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += resp->tr_attrsetm.tr_logcount;
+
+ return ret;
+}
+
STATIC uint
-xfs_calc_icreate_reservation(xfs_mount_t *mp)
+xfs_calc_icreate_reservation(
+ struct xfs_mount *mp)
{
- return XFS_DQUOT_LOGRES(mp) +
- max(xfs_calc_icreate_resv_alloc(mp),
- xfs_calc_create_resv_modify(mp));
+ struct xfs_trans_resv *resp = M_RES(mp);
+ unsigned int overhead = XFS_DQUOT_LOGRES(mp);
+ unsigned int t1, t2, t3 = 0;
+
+ t1 = xfs_calc_icreate_resv_alloc(mp);
+ t2 = xfs_calc_create_resv_modify(mp);
+
+ if (xfs_has_parent(mp)) {
+ t3 = resp->tr_attrsetm.tr_logres;
+ overhead += xfs_calc_pptr_link_overhead();
+ }
+
+ return overhead + max3(t1, t2, t3);
}
STATIC uint
@@ -586,6 +749,23 @@ xfs_calc_create_tmpfile_reservation(
return res + xfs_calc_iunlink_add_reservation(mp);
}
+static inline unsigned int
+xfs_mkdir_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ unsigned int ret = XFS_MKDIR_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += resp->tr_attrsetm.tr_logcount;
+
+ return ret;
+}
+
/*
* Making a new directory is the same as creating a new file.
*/
@@ -596,6 +776,22 @@ xfs_calc_mkdir_reservation(
return xfs_calc_icreate_reservation(mp);
}
+static inline unsigned int
+xfs_symlink_log_count(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ unsigned int ret = XFS_SYMLINK_LOG_COUNT;
+
+ /*
+ * Pre-reserve enough log reservation to handle the transaction
+ * rolling needed to add one parent pointer.
+ */
+ if (xfs_has_parent(mp))
+ ret += resp->tr_attrsetm.tr_logcount;
+
+ return ret;
+}
/*
* Making a new symplink is the same as creating a new file, but
@@ -908,6 +1104,52 @@ xfs_calc_sb_reservation(
return xfs_calc_buf_res(1, mp->m_sb.sb_sectsize);
}
+/*
+ * Namespace reservations.
+ *
+ * These get tricky when parent pointers are enabled as we have attribute
+ * modifications occurring from within these transactions. Rather than confuse
+ * each of these reservation calculations with the conditional attribute
+ * reservations, add them here in a clear and concise manner. This requires that
+ * the attribute reservations have already been calculated.
+ *
+ * Note that we only include the static attribute reservation here; the runtime
+ * reservation will have to be modified by the size of the attributes being
+ * added/removed/modified. See the comments on the attribute reservation
+ * calculations for more details.
+ */
+STATIC void
+xfs_calc_namespace_reservations(
+ struct xfs_mount *mp,
+ struct xfs_trans_resv *resp)
+{
+ ASSERT(resp->tr_attrsetm.tr_logres > 0);
+
+ resp->tr_rename.tr_logres = xfs_calc_rename_reservation(mp);
+ resp->tr_rename.tr_logcount = xfs_rename_log_count(mp, resp);
+ resp->tr_rename.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+ resp->tr_link.tr_logres = xfs_calc_link_reservation(mp);
+ resp->tr_link.tr_logcount = xfs_link_log_count(mp, resp);
+ resp->tr_link.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+ resp->tr_remove.tr_logres = xfs_calc_remove_reservation(mp);
+ resp->tr_remove.tr_logcount = xfs_remove_log_count(mp, resp);
+ resp->tr_remove.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+ resp->tr_symlink.tr_logres = xfs_calc_symlink_reservation(mp);
+ resp->tr_symlink.tr_logcount = xfs_symlink_log_count(mp, resp);
+ resp->tr_symlink.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+ resp->tr_create.tr_logres = xfs_calc_icreate_reservation(mp);
+ resp->tr_create.tr_logcount = xfs_icreate_log_count(mp, resp);
+ resp->tr_create.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+
+ resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);
+ resp->tr_mkdir.tr_logcount = xfs_mkdir_log_count(mp, resp);
+ resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+}
+
void
xfs_trans_resv_calc(
struct xfs_mount *mp,
@@ -927,35 +1169,11 @@ xfs_trans_resv_calc(
resp->tr_itruncate.tr_logcount = XFS_ITRUNCATE_LOG_COUNT;
resp->tr_itruncate.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
- resp->tr_rename.tr_logres = xfs_calc_rename_reservation(mp);
- resp->tr_rename.tr_logcount = XFS_RENAME_LOG_COUNT;
- resp->tr_rename.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
- resp->tr_link.tr_logres = xfs_calc_link_reservation(mp);
- resp->tr_link.tr_logcount = XFS_LINK_LOG_COUNT;
- resp->tr_link.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
- resp->tr_remove.tr_logres = xfs_calc_remove_reservation(mp);
- resp->tr_remove.tr_logcount = XFS_REMOVE_LOG_COUNT;
- resp->tr_remove.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
- resp->tr_symlink.tr_logres = xfs_calc_symlink_reservation(mp);
- resp->tr_symlink.tr_logcount = XFS_SYMLINK_LOG_COUNT;
- resp->tr_symlink.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
- resp->tr_create.tr_logres = xfs_calc_icreate_reservation(mp);
- resp->tr_create.tr_logcount = XFS_CREATE_LOG_COUNT;
- resp->tr_create.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
resp->tr_create_tmpfile.tr_logres =
xfs_calc_create_tmpfile_reservation(mp);
resp->tr_create_tmpfile.tr_logcount = XFS_CREATE_TMPFILE_LOG_COUNT;
resp->tr_create_tmpfile.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
- resp->tr_mkdir.tr_logres = xfs_calc_mkdir_reservation(mp);
- resp->tr_mkdir.tr_logcount = XFS_MKDIR_LOG_COUNT;
- resp->tr_mkdir.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
-
resp->tr_ifree.tr_logres = xfs_calc_ifree_reservation(mp);
resp->tr_ifree.tr_logcount = XFS_INACTIVE_LOG_COUNT;
resp->tr_ifree.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
@@ -985,6 +1203,8 @@ xfs_trans_resv_calc(
resp->tr_qm_dqalloc.tr_logcount = XFS_WRITE_LOG_COUNT;
resp->tr_qm_dqalloc.tr_logflags |= XFS_TRANS_PERM_LOG_RES;
+ xfs_calc_namespace_reservations(mp, resp);
+
/*
* The following transactions are logged in logical format with
* a default log count.
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 06/32] xfs: parent pointer attribute creation
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (4 preceding siblings ...)
2023-04-06 19:32 ` [PATCH 05/32] xfs: extend transaction reservations for parent attributes Darrick J. Wong
@ 2023-04-06 19:33 ` Darrick J. Wong
2023-04-06 19:33 ` [PATCH 07/32] xfs: add parent attributes to link Darrick J. Wong
` (25 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:33 UTC (permalink / raw)
To: djwong; +Cc: Dave Chinner, Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Add parent pointer attribute during xfs_create, and subroutines to
initialize attributes
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: shorten names, adjust to new format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
include/libxfs.h | 1
include/xfs_inode.h | 6 +++
libxfs/init.c | 3 +
libxfs/libxfs_priv.h | 3 +
libxfs/xfs_attr.c | 4 +-
libxfs/xfs_attr.h | 4 +-
libxfs/xfs_parent.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++
libxfs/xfs_parent.h | 44 +++++++++++++++++++
8 files changed, 176 insertions(+), 5 deletions(-)
diff --git a/include/libxfs.h b/include/libxfs.h
index b28781d19..cc57e8887 100644
--- a/include/libxfs.h
+++ b/include/libxfs.h
@@ -78,6 +78,7 @@ struct iomap;
#include "xfs_refcount_btree.h"
#include "xfs_refcount.h"
#include "xfs_btree_staging.h"
+#include "xfs_parent.h"
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
diff --git a/include/xfs_inode.h b/include/xfs_inode.h
index b0bba1094..74de05191 100644
--- a/include/xfs_inode.h
+++ b/include/xfs_inode.h
@@ -175,6 +175,12 @@ static inline struct inode *VFS_I(struct xfs_inode *ip)
return &ip->i_vnode;
}
+/* convert from const xfs inode to const vfs inode */
+static inline const struct inode *VFS_IC(const struct xfs_inode *ip)
+{
+ return &ip->i_vnode;
+}
+
/* We only have i_size in the xfs inode in userspace */
static inline loff_t i_size_read(struct inode *inode)
{
diff --git a/libxfs/init.c b/libxfs/init.c
index fda36ba0f..59cd547d6 100644
--- a/libxfs/init.c
+++ b/libxfs/init.c
@@ -258,6 +258,8 @@ init_caches(void)
"xfs_extfree_item");
xfs_trans_cache = kmem_cache_init(
sizeof(struct xfs_trans), "xfs_trans");
+ xfs_parent_intent_cache = kmem_cache_init(
+ sizeof(struct xfs_parent_defer), "xfs_parent_defer");
}
static int
@@ -275,6 +277,7 @@ destroy_caches(void)
xfs_btree_destroy_cur_caches();
leaked += kmem_cache_destroy(xfs_extfree_item_cache);
leaked += kmem_cache_destroy(xfs_trans_cache);
+ leaked += kmem_cache_destroy(xfs_parent_intent_cache);
return leaked;
}
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index db6c40131..ff027dbe3 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -616,7 +616,8 @@ int libxfs_zero_extent(struct xfs_inode *ip, xfs_fsblock_t start_fsb,
/* xfs_log.c */
bool xfs_log_check_lsn(struct xfs_mount *, xfs_lsn_t);
void xfs_log_item_init(struct xfs_mount *, struct xfs_log_item *, int);
-#define xfs_attr_use_log_assist(mp) (0)
+#define xfs_attr_grab_log_assist(mp) (0)
+#define xfs_attr_rele_log_assist(mp) ((void) 0)
#define xlog_drop_incompat_feat(log) do { } while (0)
#define xfs_log_in_recovery(mp) (false)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 809c285d4..4e1d9551a 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -893,7 +893,7 @@ xfs_attr_lookup(
return error;
}
-static int
+int
xfs_attr_intent_init(
struct xfs_da_args *args,
unsigned int op_flags, /* op flag (set or remove) */
@@ -911,7 +911,7 @@ xfs_attr_intent_init(
}
/* Sets an attribute for an inode as a deferred operation */
-static int
+int
xfs_attr_defer_add(
struct xfs_da_args *args)
{
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index 13b65aaf3..1002e431b 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -549,6 +549,7 @@ int xfs_inode_hasattr(struct xfs_inode *ip);
bool xfs_attr_is_leaf(struct xfs_inode *ip);
int xfs_attr_get_ilocked(struct xfs_da_args *args);
int xfs_attr_get(struct xfs_da_args *args);
+int xfs_attr_defer_add(struct xfs_da_args *args);
int xfs_attr_set(struct xfs_da_args *args);
int xfs_attr_set_iter(struct xfs_attr_intent *attr);
int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
@@ -557,7 +558,8 @@ bool xfs_attr_namecheck(struct xfs_mount *mp, const void *name, size_t length,
int xfs_attr_calc_size(struct xfs_da_args *args, int *local);
void xfs_init_attr_trans(struct xfs_da_args *args, struct xfs_trans_res *tres,
unsigned int *total);
-
+int xfs_attr_intent_init(struct xfs_da_args *args, unsigned int op_flags,
+ struct xfs_attr_intent **attr);
/*
* Check to see if the attr should be upgraded from non-existent or shortform to
* single-leaf-block attribute list.
diff --git a/libxfs/xfs_parent.c b/libxfs/xfs_parent.c
index 583607c13..ca1c6eeaf 100644
--- a/libxfs/xfs_parent.c
+++ b/libxfs/xfs_parent.c
@@ -21,6 +21,7 @@
#include "xfs_da_btree.h"
#include "xfs_attr.h"
#include "xfs_dir2.h"
+#include "xfs_dir2_priv.h"
#include "xfs_attr_sf.h"
#include "xfs_bmap.h"
#include "xfs_parent.h"
@@ -28,6 +29,8 @@
#include "xfs_format.h"
#include "xfs_trans_space.h"
+struct kmem_cache *xfs_parent_intent_cache;
+
/*
* Parent pointer attribute handling.
*
@@ -90,3 +93,116 @@ xfs_parent_valuecheck(
return true;
}
+
+/* Initializes a xfs_parent_name_rec to be stored as an attribute name. */
+static inline void
+xfs_init_parent_name_rec(
+ struct xfs_parent_name_rec *rec,
+ const struct xfs_inode *dp,
+ const struct xfs_name *name,
+ struct xfs_inode *ip)
+{
+ rec->p_ino = cpu_to_be64(dp->i_ino);
+ rec->p_gen = cpu_to_be32(VFS_IC(dp)->i_generation);
+ rec->p_namehash = cpu_to_be32(xfs_dir2_hashname(dp->i_mount, name));
+}
+
+/* Point the da args value fields at the non-key parts of a parent pointer. */
+static inline void
+xfs_init_parent_davalue(
+ struct xfs_da_args *args,
+ const struct xfs_name *name)
+{
+ args->valuelen = name->len;
+ args->value = (void *)name->name;
+}
+
+/*
+ * Allocate memory to control a logged parent pointer update as part of a
+ * dirent operation.
+ */
+int
+__xfs_parent_init(
+ struct xfs_mount *mp,
+ struct xfs_parent_defer **parentp)
+{
+ struct xfs_parent_defer *parent;
+ int error;
+
+ error = xfs_attr_grab_log_assist(mp);
+ if (error)
+ return error;
+
+ parent = kmem_cache_zalloc(xfs_parent_intent_cache, GFP_KERNEL);
+ if (!parent) {
+ xfs_attr_rele_log_assist(mp);
+ return -ENOMEM;
+ }
+
+ /* init parent da_args */
+ parent->args.geo = mp->m_attr_geo;
+ parent->args.whichfork = XFS_ATTR_FORK;
+ parent->args.attr_filter = XFS_ATTR_PARENT;
+ parent->args.op_flags = XFS_DA_OP_OKNOENT | XFS_DA_OP_LOGGED |
+ XFS_DA_OP_NVLOOKUP;
+ parent->args.name = (const uint8_t *)&parent->rec;
+ parent->args.namelen = sizeof(struct xfs_parent_name_rec);
+
+ *parentp = parent;
+ return 0;
+}
+
+static inline xfs_dahash_t
+xfs_parent_hashname(
+ struct xfs_inode *ip,
+ const struct xfs_parent_defer *parent)
+{
+ return xfs_da_hashname((const void *)&parent->rec,
+ sizeof(struct xfs_parent_name_rec));
+}
+
+/* Add a parent pointer to reflect a dirent addition. */
+int
+xfs_parent_add(
+ struct xfs_trans *tp,
+ struct xfs_parent_defer *parent,
+ struct xfs_inode *dp,
+ const struct xfs_name *parent_name,
+ struct xfs_inode *child)
+{
+ struct xfs_da_args *args = &parent->args;
+
+ xfs_init_parent_name_rec(&parent->rec, dp, parent_name, child);
+ args->hashval = xfs_parent_hashname(dp, parent);
+
+ args->trans = tp;
+ args->dp = child;
+
+ xfs_init_parent_davalue(&parent->args, parent_name);
+
+ return xfs_attr_defer_add(args);
+}
+
+/* Cancel a parent pointer operation. */
+void
+__xfs_parent_cancel(
+ struct xfs_mount *mp,
+ struct xfs_parent_defer *parent)
+{
+ xlog_drop_incompat_feat(mp->m_log);
+ kmem_cache_free(xfs_parent_intent_cache, parent);
+}
+
+/* Calculate the disk space required to add a parent pointer. */
+unsigned int
+xfs_parent_calc_space_res(
+ struct xfs_mount *mp,
+ unsigned int namelen)
+{
+ /*
+ * Parent pointers are always the first attr in an attr tree, and never
+ * larger than a block
+ */
+ return XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK) +
+ XFS_NEXTENTADD_SPACE_RES(mp, namelen, XFS_ATTR_FORK);
+}
diff --git a/libxfs/xfs_parent.h b/libxfs/xfs_parent.h
index 6e2a2528d..8e7dbe22e 100644
--- a/libxfs/xfs_parent.h
+++ b/libxfs/xfs_parent.h
@@ -13,4 +13,46 @@ bool xfs_parent_namecheck(struct xfs_mount *mp,
bool xfs_parent_valuecheck(struct xfs_mount *mp, const void *value,
size_t valuelen);
-#endif /* __XFS_PARENT_H__ */
+extern struct kmem_cache *xfs_parent_intent_cache;
+
+/*
+ * Dynamically allocd structure used to wrap the needed data to pass around
+ * the defer ops machinery
+ */
+struct xfs_parent_defer {
+ struct xfs_parent_name_rec rec;
+ struct xfs_da_args args;
+};
+
+int __xfs_parent_init(struct xfs_mount *mp, struct xfs_parent_defer **parentp);
+
+static inline int
+xfs_parent_start(
+ struct xfs_mount *mp,
+ struct xfs_parent_defer **pp)
+{
+ *pp = NULL;
+
+ if (xfs_has_parent(mp))
+ return __xfs_parent_init(mp, pp);
+ return 0;
+}
+
+int xfs_parent_add(struct xfs_trans *tp, struct xfs_parent_defer *parent,
+ struct xfs_inode *dp, const struct xfs_name *parent_name,
+ struct xfs_inode *child);
+void __xfs_parent_cancel(struct xfs_mount *mp, struct xfs_parent_defer *parent);
+
+static inline void
+xfs_parent_finish(
+ struct xfs_mount *mp,
+ struct xfs_parent_defer *p)
+{
+ if (p)
+ __xfs_parent_cancel(mp, p);
+}
+
+unsigned int xfs_parent_calc_space_res(struct xfs_mount *mp,
+ unsigned int namelen);
+
+#endif /* __XFS_PARENT_H__ */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 07/32] xfs: add parent attributes to link
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (5 preceding siblings ...)
2023-04-06 19:33 ` [PATCH 06/32] xfs: parent pointer attribute creation Darrick J. Wong
@ 2023-04-06 19:33 ` Darrick J. Wong
2023-04-06 19:33 ` [PATCH 08/32] xfs: add parent attributes to symlink Darrick J. Wong
` (24 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:33 UTC (permalink / raw)
To: djwong; +Cc: Dave Chinner, Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
This patch modifies xfs_link to add a parent pointer to the inode.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: minor rebase fixes]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_trans_space.h | 2 --
1 file changed, 2 deletions(-)
diff --git a/libxfs/xfs_trans_space.h b/libxfs/xfs_trans_space.h
index 87b31c69a..f72207923 100644
--- a/libxfs/xfs_trans_space.h
+++ b/libxfs/xfs_trans_space.h
@@ -84,8 +84,6 @@
(2 * (mp)->m_alloc_maxlevels)
#define XFS_GROWFSRT_SPACE_RES(mp,b) \
((b) + XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK))
-#define XFS_LINK_SPACE_RES(mp,nl) \
- XFS_DIRENTER_SPACE_RES(mp,nl)
#define XFS_MKDIR_SPACE_RES(mp,nl) \
(XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
#define XFS_QM_DQALLOC_SPACE_RES(mp) \
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 08/32] xfs: add parent attributes to symlink
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (6 preceding siblings ...)
2023-04-06 19:33 ` [PATCH 07/32] xfs: add parent attributes to link Darrick J. Wong
@ 2023-04-06 19:33 ` Darrick J. Wong
2023-04-06 19:33 ` [PATCH 09/32] xfs: remove parent pointers in unlink Darrick J. Wong
` (23 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:33 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
This patch modifies xfs_symlink to add a parent pointer to the inode.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: minor rebase fixups]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_trans_space.h | 2 --
1 file changed, 2 deletions(-)
diff --git a/libxfs/xfs_trans_space.h b/libxfs/xfs_trans_space.h
index f72207923..25a55650b 100644
--- a/libxfs/xfs_trans_space.h
+++ b/libxfs/xfs_trans_space.h
@@ -95,8 +95,6 @@
XFS_DIRREMOVE_SPACE_RES(mp)
#define XFS_RENAME_SPACE_RES(mp,nl) \
(XFS_DIRREMOVE_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
-#define XFS_SYMLINK_SPACE_RES(mp,nl,b) \
- (XFS_IALLOC_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl) + (b))
#define XFS_IFREE_SPACE_RES(mp) \
(xfs_has_finobt(mp) ? M_IGEO(mp)->inobt_maxlevels : 0)
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 09/32] xfs: remove parent pointers in unlink
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (7 preceding siblings ...)
2023-04-06 19:33 ` [PATCH 08/32] xfs: add parent attributes to symlink Darrick J. Wong
@ 2023-04-06 19:33 ` Darrick J. Wong
2023-04-06 19:34 ` [PATCH 10/32] xfs: Add parent pointers to rename Darrick J. Wong
` (22 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:33 UTC (permalink / raw)
To: djwong; +Cc: Dave Chinner, Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
This patch removes the parent pointer attribute during unlink
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: adjust to new ondisk format, minor rebase fixes]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_attr.c | 2 +-
libxfs/xfs_attr.h | 1 +
libxfs/xfs_parent.c | 22 ++++++++++++++++++++++
libxfs/xfs_parent.h | 4 ++++
libxfs/xfs_trans_space.h | 2 --
repair/phase6.c | 6 +++---
6 files changed, 31 insertions(+), 6 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 4e1d9551a..30d1a2992 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -957,7 +957,7 @@ xfs_attr_defer_replace(
}
/* Removes an attribute for an inode as a deferred operation */
-static int
+int
xfs_attr_defer_remove(
struct xfs_da_args *args)
{
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index 1002e431b..07c61bd70 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -550,6 +550,7 @@ bool xfs_attr_is_leaf(struct xfs_inode *ip);
int xfs_attr_get_ilocked(struct xfs_da_args *args);
int xfs_attr_get(struct xfs_da_args *args);
int xfs_attr_defer_add(struct xfs_da_args *args);
+int xfs_attr_defer_remove(struct xfs_da_args *args);
int xfs_attr_set(struct xfs_da_args *args);
int xfs_attr_set_iter(struct xfs_attr_intent *attr);
int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
diff --git a/libxfs/xfs_parent.c b/libxfs/xfs_parent.c
index ca1c6eeaf..b06033243 100644
--- a/libxfs/xfs_parent.c
+++ b/libxfs/xfs_parent.c
@@ -183,6 +183,28 @@ xfs_parent_add(
return xfs_attr_defer_add(args);
}
+/* Remove a parent pointer to reflect a dirent removal. */
+int
+xfs_parent_remove(
+ struct xfs_trans *tp,
+ struct xfs_parent_defer *parent,
+ struct xfs_inode *dp,
+ const struct xfs_name *parent_name,
+ struct xfs_inode *child)
+{
+ struct xfs_da_args *args = &parent->args;
+
+ xfs_init_parent_name_rec(&parent->rec, dp, parent_name, child);
+ args->hashval = xfs_parent_hashname(dp, parent);
+
+ args->trans = tp;
+ args->dp = child;
+
+ xfs_init_parent_davalue(&parent->args, parent_name);
+
+ return xfs_attr_defer_remove(args);
+}
+
/* Cancel a parent pointer operation. */
void
__xfs_parent_cancel(
diff --git a/libxfs/xfs_parent.h b/libxfs/xfs_parent.h
index 8e7dbe22e..0f8194cd8 100644
--- a/libxfs/xfs_parent.h
+++ b/libxfs/xfs_parent.h
@@ -41,6 +41,10 @@ xfs_parent_start(
int xfs_parent_add(struct xfs_trans *tp, struct xfs_parent_defer *parent,
struct xfs_inode *dp, const struct xfs_name *parent_name,
struct xfs_inode *child);
+int xfs_parent_remove(struct xfs_trans *tp, struct xfs_parent_defer *parent,
+ struct xfs_inode *dp, const struct xfs_name *parent_name,
+ struct xfs_inode *child);
+
void __xfs_parent_cancel(struct xfs_mount *mp, struct xfs_parent_defer *parent);
static inline void
diff --git a/libxfs/xfs_trans_space.h b/libxfs/xfs_trans_space.h
index 25a55650b..b5ab6701e 100644
--- a/libxfs/xfs_trans_space.h
+++ b/libxfs/xfs_trans_space.h
@@ -91,8 +91,6 @@
XFS_DQUOT_CLUSTER_SIZE_FSB)
#define XFS_QM_QINOCREATE_SPACE_RES(mp) \
XFS_IALLOC_SPACE_RES(mp)
-#define XFS_REMOVE_SPACE_RES(mp) \
- XFS_DIRREMOVE_SPACE_RES(mp)
#define XFS_RENAME_SPACE_RES(mp,nl) \
(XFS_DIRREMOVE_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
#define XFS_IFREE_SPACE_RES(mp) \
diff --git a/repair/phase6.c b/repair/phase6.c
index 0be2c9c97..40d7e17b1 100644
--- a/repair/phase6.c
+++ b/repair/phase6.c
@@ -1266,7 +1266,7 @@ longform_dir2_rebuild(
libxfs_dir_ino_validate(mp, pip.i_ino))
pip.i_ino = mp->m_sb.sb_rootino;
- nres = XFS_REMOVE_SPACE_RES(mp);
+ nres = XFS_DIRREMOVE_SPACE_RES(mp);
error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_remove, nres, 0, 0, &tp);
if (error)
res_failed(error);
@@ -1371,7 +1371,7 @@ dir2_kill_block(
int nres;
xfs_trans_t *tp;
- nres = XFS_REMOVE_SPACE_RES(mp);
+ nres = XFS_DIRREMOVE_SPACE_RES(mp);
error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_remove, nres, 0, 0, &tp);
if (error)
res_failed(error);
@@ -2887,7 +2887,7 @@ process_dir_inode(
* inode but it's easier than wedging a
* new define in ourselves.
*/
- nres = no_modify ? 0 : XFS_REMOVE_SPACE_RES(mp);
+ nres = no_modify ? 0 : XFS_DIRREMOVE_SPACE_RES(mp);
error = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_remove,
nres, 0, 0, &tp);
if (error)
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 10/32] xfs: Add parent pointers to rename
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (8 preceding siblings ...)
2023-04-06 19:33 ` [PATCH 09/32] xfs: remove parent pointers in unlink Darrick J. Wong
@ 2023-04-06 19:34 ` Darrick J. Wong
2023-04-06 19:34 ` [PATCH 11/32] xfsprogs: pass the attr value to put_listent when possible Darrick J. Wong
` (21 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:34 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
This patch removes the old parent pointer attribute during the rename
operation, and re-adds the updated parent pointer.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: adjust to new ondisk format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_attr.c | 2 +-
libxfs/xfs_attr.h | 1 +
libxfs/xfs_parent.c | 58 ++++++++++++++++++++++++++++++++++++++++++----
libxfs/xfs_parent.h | 23 +++++++++++++++++-
libxfs/xfs_trans_space.h | 2 --
5 files changed, 76 insertions(+), 10 deletions(-)
diff --git a/libxfs/xfs_attr.c b/libxfs/xfs_attr.c
index 30d1a2992..5a713f612 100644
--- a/libxfs/xfs_attr.c
+++ b/libxfs/xfs_attr.c
@@ -934,7 +934,7 @@ xfs_attr_defer_add(
}
/* Sets an attribute for an inode as a deferred operation */
-static int
+int
xfs_attr_defer_replace(
struct xfs_da_args *args)
{
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index 07c61bd70..3ad1f12a5 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -551,6 +551,7 @@ int xfs_attr_get_ilocked(struct xfs_da_args *args);
int xfs_attr_get(struct xfs_da_args *args);
int xfs_attr_defer_add(struct xfs_da_args *args);
int xfs_attr_defer_remove(struct xfs_da_args *args);
+int xfs_attr_defer_replace(struct xfs_da_args *args);
int xfs_attr_set(struct xfs_da_args *args);
int xfs_attr_set_iter(struct xfs_attr_intent *attr);
int xfs_attr_remove_iter(struct xfs_attr_intent *attr);
diff --git a/libxfs/xfs_parent.c b/libxfs/xfs_parent.c
index b06033243..9f61b9fc6 100644
--- a/libxfs/xfs_parent.c
+++ b/libxfs/xfs_parent.c
@@ -117,6 +117,19 @@ xfs_init_parent_davalue(
args->value = (void *)name->name;
}
+/*
+ * Point the da args new value fields at the non-key parts of a replacement
+ * parent pointer.
+ */
+static inline void
+xfs_init_parent_danewvalue(
+ struct xfs_da_args *args,
+ const struct xfs_name *name)
+{
+ args->new_valuelen = name->len;
+ args->new_value = (void *)name->name;
+}
+
/*
* Allocate memory to control a logged parent pointer update as part of a
* dirent operation.
@@ -124,22 +137,27 @@ xfs_init_parent_davalue(
int
__xfs_parent_init(
struct xfs_mount *mp,
+ bool grab_log,
struct xfs_parent_defer **parentp)
{
struct xfs_parent_defer *parent;
int error;
- error = xfs_attr_grab_log_assist(mp);
- if (error)
- return error;
+ if (grab_log) {
+ error = xfs_attr_grab_log_assist(mp);
+ if (error)
+ return error;
+ }
parent = kmem_cache_zalloc(xfs_parent_intent_cache, GFP_KERNEL);
if (!parent) {
- xfs_attr_rele_log_assist(mp);
+ if (grab_log)
+ xfs_attr_rele_log_assist(mp);
return -ENOMEM;
}
/* init parent da_args */
+ parent->have_log = grab_log;
parent->args.geo = mp->m_attr_geo;
parent->args.whichfork = XFS_ATTR_FORK;
parent->args.attr_filter = XFS_ATTR_PARENT;
@@ -205,13 +223,43 @@ xfs_parent_remove(
return xfs_attr_defer_remove(args);
}
+/* Replace one parent pointer with another to reflect a rename. */
+int
+xfs_parent_replace(
+ struct xfs_trans *tp,
+ struct xfs_parent_defer *parent,
+ struct xfs_inode *old_dp,
+ const struct xfs_name *old_name,
+ struct xfs_inode *new_dp,
+ const struct xfs_name *new_name,
+ struct xfs_inode *child)
+{
+ struct xfs_da_args *args = &parent->args;
+
+ xfs_init_parent_name_rec(&parent->rec, old_dp, old_name, child);
+ args->hashval = xfs_parent_hashname(old_dp, parent);
+
+ xfs_init_parent_name_rec(&parent->new_rec, new_dp, new_name, child);
+ args->new_name = (const uint8_t *)&parent->new_rec;
+ args->new_namelen = sizeof(struct xfs_parent_name_rec);
+
+ args->trans = tp;
+ args->dp = child;
+
+ xfs_init_parent_davalue(&parent->args, old_name);
+ xfs_init_parent_danewvalue(&parent->args, new_name);
+
+ return xfs_attr_defer_replace(args);
+}
+
/* Cancel a parent pointer operation. */
void
__xfs_parent_cancel(
struct xfs_mount *mp,
struct xfs_parent_defer *parent)
{
- xlog_drop_incompat_feat(mp->m_log);
+ if (parent->have_log)
+ xlog_drop_incompat_feat(mp->m_log);
kmem_cache_free(xfs_parent_intent_cache, parent);
}
diff --git a/libxfs/xfs_parent.h b/libxfs/xfs_parent.h
index 0f8194cd8..f4a0793bc 100644
--- a/libxfs/xfs_parent.h
+++ b/libxfs/xfs_parent.h
@@ -21,10 +21,13 @@ extern struct kmem_cache *xfs_parent_intent_cache;
*/
struct xfs_parent_defer {
struct xfs_parent_name_rec rec;
+ struct xfs_parent_name_rec new_rec;
struct xfs_da_args args;
+ bool have_log;
};
-int __xfs_parent_init(struct xfs_mount *mp, struct xfs_parent_defer **parentp);
+int __xfs_parent_init(struct xfs_mount *mp, bool grab_log,
+ struct xfs_parent_defer **parentp);
static inline int
xfs_parent_start(
@@ -34,7 +37,19 @@ xfs_parent_start(
*pp = NULL;
if (xfs_has_parent(mp))
- return __xfs_parent_init(mp, pp);
+ return __xfs_parent_init(mp, true, pp);
+ return 0;
+}
+
+static inline int
+xfs_parent_start_locked(
+ struct xfs_mount *mp,
+ struct xfs_parent_defer **pp)
+{
+ *pp = NULL;
+
+ if (xfs_has_parent(mp))
+ return __xfs_parent_init(mp, false, pp);
return 0;
}
@@ -44,6 +59,10 @@ int xfs_parent_add(struct xfs_trans *tp, struct xfs_parent_defer *parent,
int xfs_parent_remove(struct xfs_trans *tp, struct xfs_parent_defer *parent,
struct xfs_inode *dp, const struct xfs_name *parent_name,
struct xfs_inode *child);
+int xfs_parent_replace(struct xfs_trans *tp, struct xfs_parent_defer *parent,
+ struct xfs_inode *old_dp, const struct xfs_name *old_name,
+ struct xfs_inode *new_dp, const struct xfs_name *new_name,
+ struct xfs_inode *child);
void __xfs_parent_cancel(struct xfs_mount *mp, struct xfs_parent_defer *parent);
diff --git a/libxfs/xfs_trans_space.h b/libxfs/xfs_trans_space.h
index b5ab6701e..810610a14 100644
--- a/libxfs/xfs_trans_space.h
+++ b/libxfs/xfs_trans_space.h
@@ -91,8 +91,6 @@
XFS_DQUOT_CLUSTER_SIZE_FSB)
#define XFS_QM_QINOCREATE_SPACE_RES(mp) \
XFS_IALLOC_SPACE_RES(mp)
-#define XFS_RENAME_SPACE_RES(mp,nl) \
- (XFS_DIRREMOVE_SPACE_RES(mp) + XFS_DIRENTER_SPACE_RES(mp,nl))
#define XFS_IFREE_SPACE_RES(mp) \
(xfs_has_finobt(mp) ? M_IGEO(mp)->inobt_maxlevels : 0)
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 11/32] xfsprogs: pass the attr value to put_listent when possible
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (9 preceding siblings ...)
2023-04-06 19:34 ` [PATCH 10/32] xfs: Add parent pointers to rename Darrick J. Wong
@ 2023-04-06 19:34 ` Darrick J. Wong
2023-04-06 19:34 ` [PATCH 12/32] xfs: add a libxfs header file for staging new ioctls Darrick J. Wong
` (20 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:34 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Source kernel commit: 45a4366e940b853beb4d65efec08e204aee0113d
Pass the attr value to put_listent when we have local xattrs or
shortform xattrs.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
libxfs/xfs_attr.h | 5 +++--
libxfs/xfs_attr_sf.h | 1 +
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/libxfs/xfs_attr.h b/libxfs/xfs_attr.h
index 3ad1f12a5..0185d29d5 100644
--- a/libxfs/xfs_attr.h
+++ b/libxfs/xfs_attr.h
@@ -47,8 +47,9 @@ struct xfs_attrlist_cursor_kern {
/* void; state communicated via *context */
-typedef void (*put_listent_func_t)(struct xfs_attr_list_context *, int,
- unsigned char *, int, int);
+typedef void (*put_listent_func_t)(struct xfs_attr_list_context *context,
+ int flags, unsigned char *name, int namelen, void *value,
+ int valuelen);
struct xfs_attr_list_context {
struct xfs_trans *tp;
diff --git a/libxfs/xfs_attr_sf.h b/libxfs/xfs_attr_sf.h
index 37578b369..c6e259791 100644
--- a/libxfs/xfs_attr_sf.h
+++ b/libxfs/xfs_attr_sf.h
@@ -24,6 +24,7 @@ typedef struct xfs_attr_sf_sort {
uint8_t flags; /* flags bits (see xfs_attr_leaf.h) */
xfs_dahash_t hash; /* this entry's hash value */
unsigned char *name; /* name value, pointer into buffer */
+ void *value;
} xfs_attr_sf_sort_t;
#define XFS_ATTR_SF_ENTSIZE_MAX /* max space for name&value */ \
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 12/32] xfs: add a libxfs header file for staging new ioctls
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (10 preceding siblings ...)
2023-04-06 19:34 ` [PATCH 11/32] xfsprogs: pass the attr value to put_listent when possible Darrick J. Wong
@ 2023-04-06 19:34 ` Darrick J. Wong
2023-04-06 19:34 ` [PATCH 13/32] xfs: Add parent pointer ioctl Darrick J. Wong
` (19 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:34 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Create a new xfs_fs_staging.h header where we can land experimental
ioctls without committing them to any stable interfaces anywhere.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
include/libxfs.h | 1 +
include/xfs.h | 1 +
libxfs/Makefile | 1 +
libxfs/libxfs_priv.h | 1 +
libxfs/xfs_fs_staging.h | 18 ++++++++++++++++++
5 files changed, 22 insertions(+)
create mode 100644 libxfs/xfs_fs_staging.h
diff --git a/include/libxfs.h b/include/libxfs.h
index cc57e8887..3340e2af3 100644
--- a/include/libxfs.h
+++ b/include/libxfs.h
@@ -23,6 +23,7 @@
#include "xfs_types.h"
#include "xfs_fs.h"
+#include "xfs_fs_staging.h"
#include "xfs_arch.h"
#include "xfs_shared.h"
diff --git a/include/xfs.h b/include/xfs.h
index e97158c8d..c4a95bec9 100644
--- a/include/xfs.h
+++ b/include/xfs.h
@@ -44,5 +44,6 @@ extern int xfs_assert_largefile[sizeof(off_t)-8];
/* Include deprecated/compat pre-vfs xfs-specific symbols */
#include <xfs/xfs_fs_compat.h>
#include <xfs/xfs_fs.h>
+#include <xfs/xfs_fs_staging.h>
#endif /* __XFS_H__ */
diff --git a/libxfs/Makefile b/libxfs/Makefile
index 89d29dc97..951df6231 100644
--- a/libxfs/Makefile
+++ b/libxfs/Makefile
@@ -14,6 +14,7 @@ LTLDFLAGS += -static
# headers to install in include/xfs
PKGHFILES = xfs_fs.h \
+ xfs_fs_staging.h \
xfs_types.h \
xfs_da_format.h \
xfs_format.h \
diff --git a/libxfs/libxfs_priv.h b/libxfs/libxfs_priv.h
index ff027dbe3..411cafb26 100644
--- a/libxfs/libxfs_priv.h
+++ b/libxfs/libxfs_priv.h
@@ -56,6 +56,7 @@
#include "xfs_arch.h"
#include "xfs_fs.h"
+#include "xfs_fs_staging.h"
#include "libfrog/crc32c.h"
#include <sys/xattr.h>
diff --git a/libxfs/xfs_fs_staging.h b/libxfs/xfs_fs_staging.h
new file mode 100644
index 000000000..bc97193dd
--- /dev/null
+++ b/libxfs/xfs_fs_staging.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+/*
+ * Copyright (C) 2020-2023 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __XFS_FS_STAGING_H__
+#define __XFS_FS_STAGING_H__
+
+/*
+ * Experimental system calls, ioctls and data structures supporting them.
+ * Nothing in here should be considered part of a stable interface of any kind.
+ *
+ * If you add an ioctl here, please leave a comment in xfs_fs.h marking it
+ * reserved. If you promote anything out of this file, please leave a comment
+ * explaining where it went.
+ */
+
+#endif /* __XFS_FS_STAGING_H__ */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 13/32] xfs: Add parent pointer ioctl
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (11 preceding siblings ...)
2023-04-06 19:34 ` [PATCH 12/32] xfs: add a libxfs header file for staging new ioctls Darrick J. Wong
@ 2023-04-06 19:34 ` Darrick J. Wong
2023-04-06 19:35 ` [PATCH 14/32] xfs: fix unit conversion error in xfs_log_calc_max_attrsetm_res Darrick J. Wong
` (18 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:34 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
This patch adds a new file ioctl to retrieve the parent pointer of a
given inode
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: move new ioctl to xfs_fs_staging.h, adjust to new ondisk format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/xfs_fs.h | 1 +
libxfs/xfs_fs_staging.h | 66 +++++++++++++++++++++++++++++++++++++++++++++++
libxfs/xfs_parent.c | 40 ++++++++++++++++++++++++++++
libxfs/xfs_parent.h | 23 ++++++++++++++++
man/man3/xfsctl.3 | 63 +++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 193 insertions(+)
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index 1cfd5bc65..390b8cbe2 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -796,6 +796,7 @@ struct xfs_scrub_metadata {
/* XFS_IOC_GETFSMAP ------ hoisted 59 */
#define XFS_IOC_SCRUB_METADATA _IOWR('X', 60, struct xfs_scrub_metadata)
#define XFS_IOC_AG_GEOMETRY _IOWR('X', 61, struct xfs_ag_geometry)
+/* XFS_IOC_GETPARENTS ---- staging 62 */
/*
* ioctl commands that replace IRIX syssgi()'s
diff --git a/libxfs/xfs_fs_staging.h b/libxfs/xfs_fs_staging.h
index bc97193dd..fe11ad6f2 100644
--- a/libxfs/xfs_fs_staging.h
+++ b/libxfs/xfs_fs_staging.h
@@ -15,4 +15,70 @@
* explaining where it went.
*/
+/* Iterating parent pointers of files. */
+
+/* return parents of the handle, not the open fd */
+#define XFS_GETPARENTS_IFLAG_HANDLE (1U << 0)
+
+/* target was the root directory */
+#define XFS_GETPARENTS_OFLAG_ROOT (1U << 1)
+
+/* Cursor is done iterating pptrs */
+#define XFS_GETPARENTS_OFLAG_DONE (1U << 2)
+
+#define XFS_GETPARENTS_FLAG_ALL (XFS_GETPARENTS_IFLAG_HANDLE | \
+ XFS_GETPARENTS_OFLAG_ROOT | \
+ XFS_GETPARENTS_OFLAG_DONE)
+
+/* Get an inode parent pointer through ioctl */
+struct xfs_getparents_rec {
+ __u64 gpr_ino; /* Inode number */
+ __u32 gpr_gen; /* Inode generation */
+ __u32 gpr_pad; /* Reserved */
+ __u64 gpr_rsvd; /* Reserved */
+ __u8 gpr_name[]; /* File name and null terminator */
+};
+
+/* Iterate through an inodes parent pointers */
+struct xfs_getparents {
+ /* File handle, if XFS_GETPARENTS_IFLAG_HANDLE is set */
+ struct xfs_handle gp_handle;
+
+ /*
+ * Structure to track progress in iterating the parent pointers.
+ * Must be initialized to zeroes before the first ioctl call, and
+ * not touched by callers after that.
+ */
+ struct xfs_attrlist_cursor gp_cursor;
+
+ /* Operational flags: XFS_GETPARENTS_*FLAG* */
+ __u32 gp_flags;
+
+ /* Must be set to zero */
+ __u32 gp_reserved;
+
+ /* Size of the buffer in bytes, including this header */
+ __u32 gp_bufsize;
+
+ /* # of entries filled in (output) */
+ __u32 gp_count;
+
+ /* Must be set to zero */
+ __u64 gp_reserved2[5];
+
+ /* Byte offset of each record within the buffer */
+ __u32 gp_offsets[];
+};
+
+static inline struct xfs_getparents_rec*
+xfs_getparents_rec(
+ struct xfs_getparents *info,
+ unsigned int idx)
+{
+ return (struct xfs_getparents_rec *)((char *)info +
+ info->gp_offsets[idx]);
+}
+
+#define XFS_IOC_GETPARENTS _IOWR('X', 62, struct xfs_getparents)
+
#endif /* __XFS_FS_STAGING_H__ */
diff --git a/libxfs/xfs_parent.c b/libxfs/xfs_parent.c
index 9f61b9fc6..e276a1d91 100644
--- a/libxfs/xfs_parent.c
+++ b/libxfs/xfs_parent.c
@@ -276,3 +276,43 @@ xfs_parent_calc_space_res(
return XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK) +
XFS_NEXTENTADD_SPACE_RES(mp, namelen, XFS_ATTR_FORK);
}
+
+/* Convert an ondisk parent pointer to the incore format. */
+void
+xfs_parent_irec_from_disk(
+ struct xfs_parent_name_irec *irec,
+ const struct xfs_parent_name_rec *rec,
+ const void *value,
+ unsigned int valuelen)
+{
+ irec->p_ino = be64_to_cpu(rec->p_ino);
+ irec->p_gen = be32_to_cpu(rec->p_gen);
+ irec->p_namehash = be32_to_cpu(rec->p_namehash);
+ irec->p_namelen = valuelen;
+ memcpy(irec->p_name, value, valuelen);
+}
+
+/* Convert an incore parent pointer to the ondisk attr name format. */
+void
+xfs_parent_irec_to_disk(
+ struct xfs_parent_name_rec *rec,
+ const struct xfs_parent_name_irec *irec)
+{
+ rec->p_ino = cpu_to_be64(irec->p_ino);
+ rec->p_gen = cpu_to_be32(irec->p_gen);
+ rec->p_namehash = cpu_to_be32(irec->p_namehash);
+}
+
+/* Compute p_namehash for the this parent pointer. */
+void
+xfs_parent_irec_hashname(
+ struct xfs_mount *mp,
+ struct xfs_parent_name_irec *irec)
+{
+ struct xfs_name dname = {
+ .name = irec->p_name,
+ .len = irec->p_namelen,
+ };
+
+ irec->p_namehash = xfs_dir2_hashname(mp, &dname);
+}
diff --git a/libxfs/xfs_parent.h b/libxfs/xfs_parent.h
index f4a0793bc..1cc477896 100644
--- a/libxfs/xfs_parent.h
+++ b/libxfs/xfs_parent.h
@@ -78,4 +78,27 @@ xfs_parent_finish(
unsigned int xfs_parent_calc_space_res(struct xfs_mount *mp,
unsigned int namelen);
+/*
+ * Incore version of a parent pointer, also contains dirent name so callers
+ * can pass/obtain all the parent pointer information in a single structure
+ */
+struct xfs_parent_name_irec {
+ /* Parent pointer attribute name fields */
+ xfs_ino_t p_ino;
+ uint32_t p_gen;
+ xfs_dahash_t p_namehash;
+
+ /* Parent pointer attribute value fields */
+ uint8_t p_namelen;
+ unsigned char p_name[MAXNAMELEN];
+};
+
+void xfs_parent_irec_from_disk(struct xfs_parent_name_irec *irec,
+ const struct xfs_parent_name_rec *rec, const void *value,
+ unsigned int valuelen);
+void xfs_parent_irec_to_disk(struct xfs_parent_name_rec *rec,
+ const struct xfs_parent_name_irec *irec);
+void xfs_parent_irec_hashname(struct xfs_mount *mp,
+ struct xfs_parent_name_irec *irec);
+
#endif /* __XFS_PARENT_H__ */
diff --git a/man/man3/xfsctl.3 b/man/man3/xfsctl.3
index 4a0d4d08d..e663b64a8 100644
--- a/man/man3/xfsctl.3
+++ b/man/man3/xfsctl.3
@@ -321,6 +321,69 @@ They are all subject to change and should not be called directly
by applications.
XFS_IOC_FSSETDM_BY_HANDLE is not supported as of Linux 5.5.
+.PP
+.TP
+.B XFS_IOC_GETPARENTS
+This command is used to get a file's parent pointers.
+Parent pointers point upwards in the directory tree towards directories that
+have entries pointing downwards.
+
+Calling programs should allocate a large memory buffer, initialize the head
+structure to zeroes, set gp_bufsize to the size of the buffer, and call the
+ioctl.
+The kernel will fill out the gp_offsets array with integer offsets to
+struct xfs_getparents_rec objects that are written within the provided memory
+buffer.
+The size of the gp_offsets array is given by gp_count.
+The XFS_GETPARENTS_OFLAG_DONE flag will be set in gp_flags when there are no
+more parent pointers to be read.
+The below code is an example of XFS_IOC_GETPARENTS usage:
+
+.nf
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <xfs/linux.h>
+#include <xfs/xfs.h>
+#include <xfs/xfs_types.h>
+#include <xfs/xfs_fs.h>
+#include <xfs/xfs_fs_staging.h>
+
+int main() {
+ struct xfs_getparents *pi;
+ struct xfs_getparents_rec *p;
+ int i, error, fd, nr_ptrs = 4;
+
+ error = malloc(65536);
+ if (!error) {
+ perror("malloc");
+ return 1;
+ }
+
+ memset(pi, 0, sizeof(*pi));
+ pi->gp_bufsize = 65536;
+
+ fd = open("/mnt/test/foo.txt", O_RDONLY | O_CREAT);
+ if (fd == -1)
+ return errno;
+
+ do {
+ error = ioctl(fd, XFS_IOC_GETPARENTS, pi);
+ if (error)
+ return error;
+
+ for (i = 0; i < pi->gp_count; i++) {
+ p = xfs_getparents_rec(pi, i);
+ printf("inode = %llu\\n", (unsigned long long)p->gpr_ino);
+ printf("generation = %u\\n", (unsigned int)p->gpr_gen);
+ printf("name = \\"%s\\"\\n\\n", (char *)p->gpr_name);
+ }
+ } while (!(pi->gp_flags & XFS_GETPARENTS_OFLAG_DONE));
+
+ return 0;
+}
+.fi
+
.SS Filesystem Operations
In order to effect one of the following operations, the pathname
and descriptor arguments passed to
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 14/32] xfs: fix unit conversion error in xfs_log_calc_max_attrsetm_res
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (12 preceding siblings ...)
2023-04-06 19:34 ` [PATCH 13/32] xfs: Add parent pointer ioctl Darrick J. Wong
@ 2023-04-06 19:35 ` Darrick J. Wong
2023-04-06 19:35 ` [PATCH 15/32] xfs: drop compatibility minimum log size computations for reflink Darrick J. Wong
` (17 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:35 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Dave and I were discussing some recent test regressions as a result of
me turning on nrext64=1 on realtime filesystems, when we noticed that
the minimum log size of a 32M filesystem jumped from 954 blocks to 4287
blocks.
Digging through xfs_log_calc_max_attrsetm_res, Dave noticed that @size
contains the maximum estimated amount of space needed for a local format
xattr, in bytes, but we feed this quantity to XFS_NEXTENTADD_SPACE_RES,
which requires units of blocks. This has resulted in an overestimation
of the minimum log size over the years.
We should nominally correct this, but there's a backwards compatibility
problem -- if we enable it now, the minimum log size will decrease. If
a corrected mkfs formats a filesystem with this new smaller log size, a
user will encounter mount failures on an uncorrected kernel due to the
larger minimum log size computations there.
However, the large extent counters feature is still EXPERIMENTAL, so we
can gate the correction on that feature (or any features that get added
after that) being enabled. Any filesystem with nrext64 or any of the
as-yet-undefined feature bits turned on will be rejected by old
uncorrected kernels, so this should be safe even in the upgrade case.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
---
libxfs/xfs_log_rlimit.c | 43 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/libxfs/xfs_log_rlimit.c b/libxfs/xfs_log_rlimit.c
index cba24493f..6ecb9ad51 100644
--- a/libxfs/xfs_log_rlimit.c
+++ b/libxfs/xfs_log_rlimit.c
@@ -16,6 +16,39 @@
#include "xfs_bmap_btree.h"
#include "xfs_trace.h"
+/*
+ * Decide if the filesystem has the parent pointer feature or any feature
+ * added after that.
+ */
+static inline bool
+xfs_has_parent_or_newer_feature(
+ struct xfs_mount *mp)
+{
+ if (!xfs_sb_is_v5(&mp->m_sb))
+ return false;
+
+ if (xfs_sb_has_compat_feature(&mp->m_sb, ~0))
+ return true;
+
+ if (xfs_sb_has_ro_compat_feature(&mp->m_sb,
+ ~(XFS_SB_FEAT_RO_COMPAT_FINOBT |
+ XFS_SB_FEAT_RO_COMPAT_RMAPBT |
+ XFS_SB_FEAT_RO_COMPAT_REFLINK |
+ XFS_SB_FEAT_RO_COMPAT_INOBTCNT)))
+ return true;
+
+ if (xfs_sb_has_incompat_feature(&mp->m_sb,
+ ~(XFS_SB_FEAT_INCOMPAT_FTYPE |
+ XFS_SB_FEAT_INCOMPAT_SPINODES |
+ XFS_SB_FEAT_INCOMPAT_META_UUID |
+ XFS_SB_FEAT_INCOMPAT_BIGTIME |
+ XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR |
+ XFS_SB_FEAT_INCOMPAT_NREXT64)))
+ return true;
+
+ return false;
+}
+
/*
* Calculate the maximum length in bytes that would be required for a local
* attribute value as large attributes out of line are not logged.
@@ -31,6 +64,16 @@ xfs_log_calc_max_attrsetm_res(
MAXNAMELEN - 1;
nblks = XFS_DAENTER_SPACE_RES(mp, XFS_ATTR_FORK);
nblks += XFS_B_TO_FSB(mp, size);
+
+ /*
+ * Starting with the parent pointer feature, every new fs feature
+ * corrects a unit conversion error in the xattr transaction
+ * reservation code that resulted in oversized minimum log size
+ * computations.
+ */
+ if (xfs_has_parent_or_newer_feature(mp))
+ size = XFS_B_TO_FSB(mp, size);
+
nblks += XFS_NEXTENTADD_SPACE_RES(mp, size, XFS_ATTR_FORK);
return M_RES(mp)->tr_attrsetm.tr_logres +
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 15/32] xfs: drop compatibility minimum log size computations for reflink
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (13 preceding siblings ...)
2023-04-06 19:35 ` [PATCH 14/32] xfs: fix unit conversion error in xfs_log_calc_max_attrsetm_res Darrick J. Wong
@ 2023-04-06 19:35 ` Darrick J. Wong
2023-04-06 19:35 ` [PATCH 16/32] xfs: don't remove the attr fork when parent pointers are enabled Darrick J. Wong
` (16 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:35 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Having established that we can reduce the minimum log size computation
for filesystems with parent pointers or any newer feature, we should
also drop the compat minlogsize code that we added when we reduced the
transaction reservation size for rmap and reflink.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
---
libxfs/xfs_log_rlimit.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/libxfs/xfs_log_rlimit.c b/libxfs/xfs_log_rlimit.c
index 6ecb9ad51..59605f0dc 100644
--- a/libxfs/xfs_log_rlimit.c
+++ b/libxfs/xfs_log_rlimit.c
@@ -91,6 +91,16 @@ xfs_log_calc_trans_resv_for_minlogblocks(
{
unsigned int rmap_maxlevels = mp->m_rmap_maxlevels;
+ /*
+ * Starting with the parent pointer feature, every new fs feature
+ * drops the oversized minimum log size computation introduced by the
+ * original reflink code.
+ */
+ if (xfs_has_parent_or_newer_feature(mp)) {
+ xfs_trans_resv_calc(mp, resv);
+ return;
+ }
+
/*
* In the early days of rmap+reflink, we always set the rmap maxlevels
* to 9 even if the AG was small enough that it would never grow to
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 16/32] xfs: don't remove the attr fork when parent pointers are enabled
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (14 preceding siblings ...)
2023-04-06 19:35 ` [PATCH 15/32] xfs: drop compatibility minimum log size computations for reflink Darrick J. Wong
@ 2023-04-06 19:35 ` Darrick J. Wong
2023-04-06 19:35 ` [PATCH 17/32] xfs: Add the parent pointer support to the superblock version 5 Darrick J. Wong
` (15 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:35 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
When an inode is removed, it may also cause the attribute fork to be
removed if it is the last attribute. This transaction gets flushed to
the log, but if the system goes down before we could inactivate the symlink,
the log recovery tries to inactivate this inode (since it is on the unlinked
list) but the verifier trips over the remote value and leaks it.
Hence we ended up with a file in this odd state on a "clean" mount. The
"obvious" fix is to prohibit erasure of the attr fork to avoid tripping
over the verifiers when pptrs are enabled.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
---
libxfs/xfs_attr_leaf.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/libxfs/xfs_attr_leaf.c b/libxfs/xfs_attr_leaf.c
index b095b25a6..63d96bed4 100644
--- a/libxfs/xfs_attr_leaf.c
+++ b/libxfs/xfs_attr_leaf.c
@@ -875,7 +875,8 @@ xfs_attr_sf_removename(
totsize -= size;
if (totsize == sizeof(xfs_attr_sf_hdr_t) && xfs_has_attr2(mp) &&
(dp->i_df.if_format != XFS_DINODE_FMT_BTREE) &&
- !(args->op_flags & (XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE))) {
+ !(args->op_flags & (XFS_DA_OP_ADDNAME | XFS_DA_OP_REPLACE)) &&
+ !xfs_has_parent(mp)) {
xfs_attr_fork_remove(dp, args->trans);
} else {
xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
@@ -884,7 +885,8 @@ xfs_attr_sf_removename(
ASSERT(totsize > sizeof(xfs_attr_sf_hdr_t) ||
(args->op_flags & XFS_DA_OP_ADDNAME) ||
!xfs_has_attr2(mp) ||
- dp->i_df.if_format == XFS_DINODE_FMT_BTREE);
+ dp->i_df.if_format == XFS_DINODE_FMT_BTREE ||
+ xfs_has_parent(mp));
xfs_trans_log_inode(args->trans, dp,
XFS_ILOG_CORE | XFS_ILOG_ADATA);
}
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 17/32] xfs: Add the parent pointer support to the superblock version 5.
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (15 preceding siblings ...)
2023-04-06 19:35 ` [PATCH 16/32] xfs: don't remove the attr fork when parent pointers are enabled Darrick J. Wong
@ 2023-04-06 19:35 ` Darrick J. Wong
2023-04-06 19:36 ` [PATCH 18/32] libfrog: add parent pointer support code Darrick J. Wong
` (14 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:35 UTC (permalink / raw)
To: djwong
Cc: Mark Tinguely, Dave Chinner, Allison Henderson, Darrick J. Wong,
allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Add the parent pointer superblock flag so that we can actually mount
filesystems with this feature enabled.
Signed-off-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
---
libfrog/fsgeom.c | 4 ++++
libxfs/xfs_format.h | 4 +++-
libxfs/xfs_fs.h | 1 +
libxfs/xfs_sb.c | 4 ++++
4 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/libfrog/fsgeom.c b/libfrog/fsgeom.c
index 3e7f0797d..3bb753ac9 100644
--- a/libfrog/fsgeom.c
+++ b/libfrog/fsgeom.c
@@ -31,6 +31,7 @@ xfs_report_geom(
int bigtime_enabled;
int inobtcount;
int nrext64;
+ int parent;
isint = geo->logstart > 0;
lazycount = geo->flags & XFS_FSOP_GEOM_FLAGS_LAZYSB ? 1 : 0;
@@ -49,12 +50,14 @@ xfs_report_geom(
bigtime_enabled = geo->flags & XFS_FSOP_GEOM_FLAGS_BIGTIME ? 1 : 0;
inobtcount = geo->flags & XFS_FSOP_GEOM_FLAGS_INOBTCNT ? 1 : 0;
nrext64 = geo->flags & XFS_FSOP_GEOM_FLAGS_NREXT64 ? 1 : 0;
+ parent = geo->flags & XFS_FSOP_GEOM_FLAGS_PARENT ? 1 : 0;
printf(_(
"meta-data=%-22s isize=%-6d agcount=%u, agsize=%u blks\n"
" =%-22s sectsz=%-5u attr=%u, projid32bit=%u\n"
" =%-22s crc=%-8u finobt=%u, sparse=%u, rmapbt=%u\n"
" =%-22s reflink=%-4u bigtime=%u inobtcount=%u nrext64=%u\n"
+" =%-22s parent=%d\n"
"data =%-22s bsize=%-6u blocks=%llu, imaxpct=%u\n"
" =%-22s sunit=%-6u swidth=%u blks\n"
"naming =version %-14u bsize=%-6u ascii-ci=%d, ftype=%d\n"
@@ -65,6 +68,7 @@ xfs_report_geom(
"", geo->sectsize, attrversion, projid32bit,
"", crcs_enabled, finobt_enabled, spinodes, rmapbt_enabled,
"", reflink_enabled, bigtime_enabled, inobtcount, nrext64,
+ "", parent,
"", geo->blocksize, (unsigned long long)geo->datablocks,
geo->imaxpct,
"", geo->sunit, geo->swidth,
diff --git a/libxfs/xfs_format.h b/libxfs/xfs_format.h
index 371dc0723..f413819b2 100644
--- a/libxfs/xfs_format.h
+++ b/libxfs/xfs_format.h
@@ -373,13 +373,15 @@ xfs_sb_has_ro_compat_feature(
#define XFS_SB_FEAT_INCOMPAT_BIGTIME (1 << 3) /* large timestamps */
#define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4) /* needs xfs_repair */
#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */
+#define XFS_SB_FEAT_INCOMPAT_PARENT (1 << 6) /* parent pointers */
#define XFS_SB_FEAT_INCOMPAT_ALL \
(XFS_SB_FEAT_INCOMPAT_FTYPE| \
XFS_SB_FEAT_INCOMPAT_SPINODES| \
XFS_SB_FEAT_INCOMPAT_META_UUID| \
XFS_SB_FEAT_INCOMPAT_BIGTIME| \
XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR| \
- XFS_SB_FEAT_INCOMPAT_NREXT64)
+ XFS_SB_FEAT_INCOMPAT_NREXT64| \
+ XFS_SB_FEAT_INCOMPAT_PARENT)
#define XFS_SB_FEAT_INCOMPAT_UNKNOWN ~XFS_SB_FEAT_INCOMPAT_ALL
static inline bool
diff --git a/libxfs/xfs_fs.h b/libxfs/xfs_fs.h
index 390b8cbe2..fc45b0e25 100644
--- a/libxfs/xfs_fs.h
+++ b/libxfs/xfs_fs.h
@@ -237,6 +237,7 @@ typedef struct xfs_fsop_resblks {
#define XFS_FSOP_GEOM_FLAGS_BIGTIME (1 << 21) /* 64-bit nsec timestamps */
#define XFS_FSOP_GEOM_FLAGS_INOBTCNT (1 << 22) /* inobt btree counter */
#define XFS_FSOP_GEOM_FLAGS_NREXT64 (1 << 23) /* large extent counters */
+#define XFS_FSOP_GEOM_FLAGS_PARENT (1 << 24) /* parent pointers */
/*
* Minimum and maximum sizes need for growth checks.
diff --git a/libxfs/xfs_sb.c b/libxfs/xfs_sb.c
index 64b4e7bea..61f86777e 100644
--- a/libxfs/xfs_sb.c
+++ b/libxfs/xfs_sb.c
@@ -171,6 +171,8 @@ xfs_sb_version_to_features(
features |= XFS_FEAT_NEEDSREPAIR;
if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_NREXT64)
features |= XFS_FEAT_NREXT64;
+ if (sbp->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_PARENT)
+ features |= XFS_FEAT_PARENT;
return features;
}
@@ -1188,6 +1190,8 @@ xfs_fs_geometry(
geo->flags |= XFS_FSOP_GEOM_FLAGS_BIGTIME;
if (xfs_has_inobtcounts(mp))
geo->flags |= XFS_FSOP_GEOM_FLAGS_INOBTCNT;
+ if (xfs_has_parent(mp))
+ geo->flags |= XFS_FSOP_GEOM_FLAGS_PARENT;
if (xfs_has_sector(mp)) {
geo->flags |= XFS_FSOP_GEOM_FLAGS_SECTOR;
geo->logsectsize = sbp->sb_logsectsize;
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 18/32] libfrog: add parent pointer support code
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (16 preceding siblings ...)
2023-04-06 19:35 ` [PATCH 17/32] xfs: Add the parent pointer support to the superblock version 5 Darrick J. Wong
@ 2023-04-06 19:36 ` Darrick J. Wong
2023-04-06 19:36 ` [PATCH 19/32] xfs_io: adapt parent command to new parent pointer ioctls Darrick J. Wong
` (13 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:36 UTC (permalink / raw)
To: djwong; +Cc: Allison Collins, allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Add some support code to libfrog so that client programs can walk file
descriptors and handles upwards through the directory tree; and obtain a
reasonable file path from a file descriptor/handle. This code will be
used in xfsprogs utilities.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Allison Collins <allison.henderson@oracle.com>
---
include/handle.h | 1
libfrog/Makefile | 2
libfrog/getparents.c | 338 ++++++++++++++++++++++++++++++++++++++++++++++++++
libfrog/getparents.h | 36 +++++
libfrog/paths.c | 167 +++++++++++++++++++++++++
libfrog/paths.h | 25 ++++
libhandle/handle.c | 7 +
7 files changed, 573 insertions(+), 3 deletions(-)
create mode 100644 libfrog/getparents.c
create mode 100644 libfrog/getparents.h
diff --git a/include/handle.h b/include/handle.h
index 34246f385..ba0650051 100644
--- a/include/handle.h
+++ b/include/handle.h
@@ -17,6 +17,7 @@ struct parent;
extern int path_to_handle (char *__path, void **__hanp, size_t *__hlen);
extern int path_to_fshandle (char *__path, void **__fshanp, size_t *__fshlen);
extern int fd_to_handle (int fd, void **hanp, size_t *hlen);
+extern int handle_to_fsfd(void *, char **);
extern int handle_to_fshandle (void *__hanp, size_t __hlen, void **__fshanp,
size_t *__fshlen);
extern void free_handle (void *__hanp, size_t __hlen);
diff --git a/libfrog/Makefile b/libfrog/Makefile
index 011070823..b327c1fdf 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -19,6 +19,7 @@ bulkstat.c \
convert.c \
crc32.c \
fsgeom.c \
+getparents.c \
list_sort.c \
linux.c \
logging.c \
@@ -40,6 +41,7 @@ crc32cselftest.h \
crc32defs.h \
crc32table.h \
fsgeom.h \
+getparents.h \
logging.h \
paths.h \
projects.h \
diff --git a/libfrog/getparents.c b/libfrog/getparents.c
new file mode 100644
index 000000000..3b4fc011a
--- /dev/null
+++ b/libfrog/getparents.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017-2023 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "platform_defs.h"
+#include "xfs.h"
+#include "xfs_arch.h"
+#include "list.h"
+#include "libfrog/paths.h"
+#include "handle.h"
+#include "libfrog/getparents.h"
+
+/* Allocate a buffer large enough for some parent pointer records. */
+static inline struct xfs_getparents *
+alloc_pptr_buf(
+ size_t bufsize)
+{
+ struct xfs_getparents *pi;
+
+ pi = calloc(bufsize, 1);
+ if (!pi)
+ return NULL;
+ pi->gp_bufsize = bufsize;
+ return pi;
+}
+
+/*
+ * Walk all parents of the given file handle. Returns 0 on success or positive
+ * errno.
+ */
+static int
+call_getparents(
+ int fd,
+ struct xfs_handle *handle,
+ walk_parent_fn fn,
+ void *arg)
+{
+ struct xfs_getparents *pi;
+ struct xfs_getparents_rec *p;
+ unsigned int i;
+ ssize_t ret = -1;
+
+ pi = alloc_pptr_buf(XFS_XATTR_LIST_MAX);
+ if (!pi)
+ return errno;
+
+ if (handle) {
+ memcpy(&pi->gp_handle, handle, sizeof(struct xfs_handle));
+ pi->gp_flags = XFS_GETPARENTS_IFLAG_HANDLE;
+ }
+
+ ret = ioctl(fd, XFS_IOC_GETPARENTS, pi);
+ while (!ret) {
+ if (pi->gp_flags & XFS_GETPARENTS_OFLAG_ROOT) {
+ struct parent_rec rec = {
+ .p_flags = PARENT_IS_ROOT,
+ };
+
+ ret = fn(&rec, arg);
+ goto out_pi;
+ }
+
+ for (i = 0; i < pi->gp_count; i++) {
+ struct parent_rec rec = { 0 };
+
+ p = xfs_getparents_rec(pi, i);
+ rec.p_ino = p->gpr_ino;
+ rec.p_gen = p->gpr_gen;
+ strncpy((char *)rec.p_name, (char *)p->gpr_name,
+ MAXNAMELEN - 1);
+
+ ret = fn(&rec, arg);
+ if (ret)
+ goto out_pi;
+ }
+
+ if (pi->gp_flags & XFS_GETPARENTS_OFLAG_DONE)
+ break;
+
+ ret = ioctl(fd, XFS_IOC_GETPARENTS, pi);
+ }
+ if (ret)
+ ret = errno;
+
+out_pi:
+ free(pi);
+ return ret;
+}
+
+/* Walk all parent pointers of this handle. Returns 0 or positive errno. */
+int
+handle_walk_parents(
+ void *hanp,
+ size_t hlen,
+ walk_parent_fn fn,
+ void *arg)
+{
+ char *mntpt;
+ int fd;
+
+ if (hlen != sizeof(struct xfs_handle))
+ return EINVAL;
+
+ fd = handle_to_fsfd(hanp, &mntpt);
+ if (fd < 0)
+ return errno;
+
+ return call_getparents(fd, hanp, fn, arg);
+}
+
+/* Walk all parent pointers of this fd. Returns 0 or positive errno. */
+int
+fd_walk_parents(
+ int fd,
+ walk_parent_fn fn,
+ void *arg)
+{
+ return call_getparents(fd, NULL, fn, arg);
+}
+
+struct walk_ppaths_info {
+ walk_path_fn fn;
+ void *arg;
+ char *mntpt;
+ struct path_list *path;
+ int fd;
+};
+
+struct walk_ppath_level_info {
+ struct xfs_handle newhandle;
+ struct path_component *pc;
+ struct walk_ppaths_info *wpi;
+};
+
+static int handle_walk_ppath(struct walk_ppaths_info *wpi,
+ struct xfs_handle *handle);
+
+static int
+handle_walk_ppath_rec(
+ const struct parent_rec *rec,
+ void *arg)
+{
+ struct walk_ppath_level_info *wpli = arg;
+ struct walk_ppaths_info *wpi = wpli->wpi;
+ int ret = 0;
+
+ if (rec->p_flags & PARENT_IS_ROOT)
+ return wpi->fn(wpi->mntpt, wpi->path, wpi->arg);
+
+ ret = path_component_change(wpli->pc, rec->p_name,
+ strlen((char *)rec->p_name), rec->p_ino);
+ if (ret)
+ return ret;
+
+ wpli->newhandle.ha_fid.fid_ino = rec->p_ino;
+ wpli->newhandle.ha_fid.fid_gen = rec->p_gen;
+
+ path_list_add_parent_component(wpi->path, wpli->pc);
+ ret = handle_walk_ppath(wpi, &wpli->newhandle);
+ path_list_del_component(wpi->path, wpli->pc);
+
+ return ret;
+}
+
+/*
+ * Recursively walk all parents of the given file handle; if we hit the
+ * fs root then we call the associated function with the constructed path.
+ * Returns 0 for success or positive errno.
+ */
+static int
+handle_walk_ppath(
+ struct walk_ppaths_info *wpi,
+ struct xfs_handle *handle)
+{
+ struct walk_ppath_level_info *wpli;
+ int ret;
+
+ wpli = malloc(sizeof(struct walk_ppath_level_info));
+ if (!wpli)
+ return errno;
+ wpli->pc = path_component_init("", 0);
+ if (!wpli->pc) {
+ ret = errno;
+ free(wpli);
+ return ret;
+ }
+ wpli->wpi = wpi;
+ memcpy(&wpli->newhandle, handle, sizeof(struct xfs_handle));
+
+ ret = call_getparents(wpi->fd, handle, handle_walk_ppath_rec, wpli);
+
+ path_component_free(wpli->pc);
+ free(wpli);
+ return ret;
+}
+
+/*
+ * Call the given function on all known paths from the vfs root to the inode
+ * described in the handle. Returns 0 for success or positive errno.
+ */
+int
+handle_walk_parent_paths(
+ void *hanp,
+ size_t hlen,
+ walk_path_fn fn,
+ void *arg)
+{
+ struct walk_ppaths_info wpi;
+ ssize_t ret;
+
+ if (hlen != sizeof(struct xfs_handle))
+ return EINVAL;
+
+ wpi.fd = handle_to_fsfd(hanp, &wpi.mntpt);
+ if (wpi.fd < 0)
+ return errno;
+ wpi.path = path_list_init();
+ if (!wpi.path)
+ return errno;
+ wpi.fn = fn;
+ wpi.arg = arg;
+
+ ret = handle_walk_ppath(&wpi, hanp);
+ path_list_free(wpi.path);
+
+ return ret;
+}
+
+/*
+ * Call the given function on all known paths from the vfs root to the inode
+ * referred to by the file description. Returns 0 or positive errno.
+ */
+int
+fd_walk_parent_paths(
+ int fd,
+ walk_path_fn fn,
+ void *arg)
+{
+ struct walk_ppaths_info wpi;
+ void *hanp;
+ size_t hlen;
+ int fsfd;
+ int ret;
+
+ ret = fd_to_handle(fd, &hanp, &hlen);
+ if (ret)
+ return errno;
+
+ fsfd = handle_to_fsfd(hanp, &wpi.mntpt);
+ if (fsfd < 0)
+ return errno;
+ wpi.fd = fd;
+ wpi.path = path_list_init();
+ if (!wpi.path)
+ return errno;
+ wpi.fn = fn;
+ wpi.arg = arg;
+
+ ret = handle_walk_ppath(&wpi, hanp);
+ path_list_free(wpi.path);
+
+ return ret;
+}
+
+struct path_walk_info {
+ char *buf;
+ size_t len;
+};
+
+/* Helper that stringifies the first full path that we find. */
+static int
+handle_to_path_walk(
+ const char *mntpt,
+ const struct path_list *path,
+ void *arg)
+{
+ struct path_walk_info *pwi = arg;
+ int mntpt_len = strlen(mntpt);
+ int ret;
+
+ /* Trim trailing slashes from the mountpoint */
+ while (mntpt_len > 0 && mntpt[mntpt_len - 1] == '/')
+ mntpt_len--;
+
+ ret = snprintf(pwi->buf, pwi->len, "%.*s", mntpt_len, mntpt);
+ if (ret != mntpt_len)
+ return ENAMETOOLONG;
+
+ ret = path_list_to_string(path, pwi->buf + ret, pwi->len - ret);
+ if (ret < 0)
+ return ENAMETOOLONG;
+
+ return ECANCELED;
+}
+
+/*
+ * Return any eligible path to this file handle. Returns 0 for success or
+ * positive errno.
+ */
+int
+handle_to_path(
+ void *hanp,
+ size_t hlen,
+ char *path,
+ size_t pathlen)
+{
+ struct path_walk_info pwi;
+ int ret;
+
+ pwi.buf = path;
+ pwi.len = pathlen;
+ ret = handle_walk_parent_paths(hanp, hlen, handle_to_path_walk, &pwi);
+ if (ret == ECANCELED)
+ return 0;
+ return ret;
+}
+
+/*
+ * Return any eligible path to this file description. Returns 0 for success
+ * or positive errno.
+ */
+int
+fd_to_path(
+ int fd,
+ char *path,
+ size_t pathlen)
+{
+ struct path_walk_info pwi;
+ int ret;
+
+ pwi.buf = path;
+ pwi.len = pathlen;
+ ret = fd_walk_parent_paths(fd, handle_to_path_walk, &pwi);
+ if (ret == ECANCELED)
+ return 0;
+ return ret;
+}
diff --git a/libfrog/getparents.h b/libfrog/getparents.h
new file mode 100644
index 000000000..c95efed24
--- /dev/null
+++ b/libfrog/getparents.h
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2023 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __LIBFROG_GETPARENTS_H_
+#define __LIBFROG_GETPARENTS_H_
+
+struct path_list;
+
+struct parent_rec {
+ uint64_t p_ino;
+ uint32_t p_gen;
+ uint32_t p_flags;
+ unsigned char p_name[MAXNAMELEN];
+};
+
+/* This is the root directory. */
+#define PARENT_IS_ROOT (1U << 0)
+
+typedef int (*walk_parent_fn)(const struct parent_rec *rec, void *arg);
+typedef int (*walk_path_fn)(const char *mntpt, const struct path_list *path,
+ void *arg);
+
+int fd_walk_parents(int fd, walk_parent_fn fn, void *arg);
+int handle_walk_parents(void *hanp, size_t hanlen, walk_parent_fn fn,
+ void *arg);
+
+int fd_walk_parent_paths(int fd, walk_path_fn fn, void *arg);
+int handle_walk_parent_paths(void *hanp, size_t hanlen, walk_path_fn fn,
+ void *arg);
+
+int fd_to_path(int fd, char *path, size_t pathlen);
+int handle_to_path(void *hanp, size_t hlen, char *path, size_t pathlen);
+
+#endif /* __LIBFROG_GETPARENTS_H_ */
diff --git a/libfrog/paths.c b/libfrog/paths.c
index abb29a237..4b9c8c644 100644
--- a/libfrog/paths.c
+++ b/libfrog/paths.c
@@ -15,6 +15,7 @@
#include "paths.h"
#include "input.h"
#include "projects.h"
+#include "list.h"
#include <limits.h>
extern char *progname;
@@ -563,3 +564,169 @@ fs_table_insert_project_path(
return error;
}
+
+/* Structured path components. */
+
+struct path_list {
+ struct list_head p_head;
+};
+
+struct path_component {
+ struct list_head pc_list;
+ uint64_t pc_ino;
+ char *pc_fname;
+};
+
+/* Initialize a path component with a given name. */
+struct path_component *
+path_component_init(
+ const char *name,
+ uint64_t ino)
+{
+ struct path_component *pc;
+
+ pc = malloc(sizeof(struct path_component));
+ if (!pc)
+ return NULL;
+ INIT_LIST_HEAD(&pc->pc_list);
+ pc->pc_fname = strdup(name);
+ if (!pc->pc_fname) {
+ free(pc);
+ return NULL;
+ }
+ pc->pc_ino = ino;
+ return pc;
+}
+
+/* Free a path component. */
+void
+path_component_free(
+ struct path_component *pc)
+{
+ free(pc->pc_fname);
+ free(pc);
+}
+
+/* Change a path component's filename or returns positive errno. */
+int
+path_component_change(
+ struct path_component *pc,
+ const void *name,
+ size_t namelen,
+ uint64_t ino)
+{
+ void *p;
+
+ p = realloc(pc->pc_fname, namelen + 1);
+ if (!p)
+ return errno;
+ pc->pc_fname = p;
+ memcpy(pc->pc_fname, name, namelen);
+ pc->pc_fname[namelen] = 0;
+ pc->pc_ino = ino;
+ return 0;
+}
+
+/* Initialize a pathname or returns positive errno. */
+struct path_list *
+path_list_init(void)
+{
+ struct path_list *path;
+
+ path = malloc(sizeof(struct path_list));
+ if (!path)
+ return NULL;
+ INIT_LIST_HEAD(&path->p_head);
+ return path;
+}
+
+/* Empty out a pathname. */
+void
+path_list_free(
+ struct path_list *path)
+{
+ struct path_component *pos;
+ struct path_component *n;
+
+ list_for_each_entry_safe(pos, n, &path->p_head, pc_list) {
+ path_list_del_component(path, pos);
+ path_component_free(pos);
+ }
+ free(path);
+}
+
+/* Add a parent component to a pathname. */
+void
+path_list_add_parent_component(
+ struct path_list *path,
+ struct path_component *pc)
+{
+ list_add(&pc->pc_list, &path->p_head);
+}
+
+/* Add a component to a pathname. */
+void
+path_list_add_component(
+ struct path_list *path,
+ struct path_component *pc)
+{
+ list_add_tail(&pc->pc_list, &path->p_head);
+}
+
+/* Remove a component from a pathname. */
+void
+path_list_del_component(
+ struct path_list *path,
+ struct path_component *pc)
+{
+ list_del_init(&pc->pc_list);
+}
+
+/*
+ * Convert a pathname into a string or returns -1 if the buffer isn't long
+ * enough.
+ */
+ssize_t
+path_list_to_string(
+ const struct path_list *path,
+ char *buf,
+ size_t buflen)
+{
+ struct path_component *pos;
+ char *buf_end = buf + buflen;
+ ssize_t bytes = 0;
+ int ret;
+
+ list_for_each_entry(pos, &path->p_head, pc_list) {
+ if (buf >= buf_end)
+ return -1;
+
+ ret = snprintf(buf, buflen, "/%s", pos->pc_fname);
+ if (ret < 0 || ret >= buflen)
+ return -1;
+
+ bytes += ret;
+ buf += ret;
+ buflen -= ret;
+ }
+ return bytes;
+}
+
+/* Walk each component of a path. */
+int
+path_walk_components(
+ const struct path_list *path,
+ path_walk_fn_t fn,
+ void *arg)
+{
+ struct path_component *pos;
+ int ret;
+
+ list_for_each_entry(pos, &path->p_head, pc_list) {
+ ret = fn(pos->pc_fname, pos->pc_ino, arg);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/libfrog/paths.h b/libfrog/paths.h
index f20a2c3ef..6be74c42b 100644
--- a/libfrog/paths.h
+++ b/libfrog/paths.h
@@ -58,4 +58,29 @@ typedef struct fs_cursor {
extern void fs_cursor_initialise(char *__dir, uint __flags, fs_cursor_t *__cp);
extern fs_path_t *fs_cursor_next_entry(fs_cursor_t *__cp);
+/* Path information. */
+
+struct path_list;
+struct path_component;
+
+struct path_component *path_component_init(const char *name, uint64_t ino);
+void path_component_free(struct path_component *pc);
+int path_component_change(struct path_component *pc, const void *name,
+ size_t namelen, uint64_t ino);
+
+struct path_list *path_list_init(void);
+void path_list_free(struct path_list *path);
+void path_list_add_parent_component(struct path_list *path,
+ struct path_component *pc);
+void path_list_add_component(struct path_list *path, struct path_component *pc);
+void path_list_del_component(struct path_list *path, struct path_component *pc);
+
+ssize_t path_list_to_string(const struct path_list *path, char *buf,
+ size_t buflen);
+
+typedef int (*path_walk_fn_t)(const char *name, uint64_t ino, void *arg);
+
+int path_walk_components(const struct path_list *path, path_walk_fn_t fn,
+ void *arg);
+
#endif /* __LIBFROG_PATH_H__ */
diff --git a/libhandle/handle.c b/libhandle/handle.c
index 333c21909..1e8fe9ac5 100644
--- a/libhandle/handle.c
+++ b/libhandle/handle.c
@@ -29,7 +29,6 @@ typedef union {
} comarg_t;
static int obj_to_handle(char *, int, unsigned int, comarg_t, void**, size_t*);
-static int handle_to_fsfd(void *, char **);
static char *path_to_fspath(char *path);
@@ -203,8 +202,10 @@ handle_to_fshandle(
return 0;
}
-static int
-handle_to_fsfd(void *hanp, char **path)
+int
+handle_to_fsfd(
+ void *hanp,
+ char **path)
{
struct fdhash *fdhp;
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 19/32] xfs_io: adapt parent command to new parent pointer ioctls
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (17 preceding siblings ...)
2023-04-06 19:36 ` [PATCH 18/32] libfrog: add parent pointer support code Darrick J. Wong
@ 2023-04-06 19:36 ` Darrick J. Wong
2023-04-06 19:36 ` [PATCH 20/32] xfs_io: Add i, n and f flags to parent command Darrick J. Wong
` (12 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:36 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
For ages, xfs_io has had a totally useless 'parent' command that enabled
callers to walk the parents or print the directory tree path of an open
file. This code used the ioctl interface presented by SGI's version of
parent pointers that was never merged. Rework the code in here to use
the new ioctl interfaces that we've settled upon. Get rid of the old
parent pointer checking code since xfs_repair/xfs_scrub will take care
of that.
(This originally was in the "xfsprogs: implement the upper half of
parent pointers" megapatch.)
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
io/parent.c | 470 ++++++++++++++---------------------------------------
man/man8/xfs_io.8 | 21 +-
2 files changed, 133 insertions(+), 358 deletions(-)
diff --git a/io/parent.c b/io/parent.c
index 8f63607ff..65fd892bf 100644
--- a/io/parent.c
+++ b/io/parent.c
@@ -7,365 +7,112 @@
#include "command.h"
#include "input.h"
#include "libfrog/paths.h"
-#include "parent.h"
+#include "libfrog/getparents.h"
#include "handle.h"
-#include "jdm.h"
#include "init.h"
#include "io.h"
-#define PARENTBUF_SZ 16384
-#define BSTATBUF_SZ 16384
-
static cmdinfo_t parent_cmd;
-static int verbose_flag;
-static int err_status;
-static __u64 inodes_checked;
static char *mntpt;
-/*
- * check out a parent entry to see if the values seem valid
- */
-static void
-check_parent_entry(struct xfs_bstat *bstatp, parent_t *parent)
+static int
+pptr_print(
+ const struct parent_rec *rec,
+ void *arg)
{
- int sts;
- char fullpath[PATH_MAX];
- struct stat statbuf;
- char *str;
-
- sprintf(fullpath, _("%s%s"), mntpt, parent->p_name);
-
- sts = lstat(fullpath, &statbuf);
- if (sts != 0) {
- fprintf(stderr,
- _("inode-path for inode: %llu is incorrect - path \"%s\" non-existent\n"),
- (unsigned long long) bstatp->bs_ino, fullpath);
- if (verbose_flag) {
- fprintf(stderr,
- _("path \"%s\" does not stat for inode: %llu; err = %s\n"),
- fullpath,
- (unsigned long long) bstatp->bs_ino,
- strerror(errno));
- }
- err_status++;
- return;
- } else {
- if (verbose_flag > 1) {
- printf(_("path \"%s\" found\n"), fullpath);
- }
- }
-
- if (statbuf.st_ino != bstatp->bs_ino) {
- fprintf(stderr,
- _("inode-path for inode: %llu is incorrect - wrong inode#\n"),
- (unsigned long long) bstatp->bs_ino);
- if (verbose_flag) {
- fprintf(stderr,
- _("ino mismatch for path \"%s\" %llu vs %llu\n"),
- fullpath,
- (unsigned long long)statbuf.st_ino,
- (unsigned long long)bstatp->bs_ino);
- }
- err_status++;
- return;
- } else if (verbose_flag > 1) {
- printf(_("inode number match: %llu\n"),
- (unsigned long long)statbuf.st_ino);
- }
+ const char *name = (char *)rec->p_name;
+ unsigned int namelen;
- /* get parent path */
- str = strrchr(fullpath, '/');
- *str = '\0';
- sts = stat(fullpath, &statbuf);
- if (sts != 0) {
- fprintf(stderr,
- _("parent path \"%s\" does not stat: %s\n"),
- fullpath,
- strerror(errno));
- err_status++;
- return;
- } else {
- if (parent->p_ino != statbuf.st_ino) {
- fprintf(stderr,
- _("inode-path for inode: %llu is incorrect - wrong parent inode#\n"),
- (unsigned long long) bstatp->bs_ino);
- if (verbose_flag) {
- fprintf(stderr,
- _("ino mismatch for path \"%s\" %llu vs %llu\n"),
- fullpath,
- (unsigned long long)parent->p_ino,
- (unsigned long long)statbuf.st_ino);
- }
- err_status++;
- return;
- } else {
- if (verbose_flag > 1) {
- printf(_("parent ino match for %llu\n"),
- (unsigned long long) parent->p_ino);
- }
- }
+ if (rec->p_flags & PARENT_IS_ROOT) {
+ printf(_("Root directory.\n"));
+ return 0;
}
-}
-
-static void
-check_parents(parent_t *parentbuf, size_t *parentbuf_size,
- jdm_fshandle_t *fshandlep, struct xfs_bstat *statp)
-{
- int error, i;
- __u32 count;
- parent_t *entryp;
-
- do {
- error = jdm_parentpaths(fshandlep, statp, parentbuf, *parentbuf_size, &count);
-
- if (error == ERANGE) {
- *parentbuf_size *= 2;
- parentbuf = (parent_t *)realloc(parentbuf, *parentbuf_size);
- } else if (error) {
- fprintf(stderr, _("parentpaths failed for ino %llu: %s\n"),
- (unsigned long long) statp->bs_ino,
- strerror(errno));
- err_status++;
- break;
- }
- } while (error == ERANGE);
-
- if (count == 0) {
- /* no links for inode - something wrong here */
- fprintf(stderr, _("inode-path for inode: %llu is missing\n"),
- (unsigned long long) statp->bs_ino);
- err_status++;
- }
+ namelen = strlen(name);
+ printf(_("p_ino = %llu\n"), (unsigned long long)rec->p_ino);
+ printf(_("p_gen = %u\n"), (unsigned int)rec->p_gen);
+ printf(_("p_namelen = %u\n"), namelen);
+ printf(_("p_name = \"%s\"\n\n"), rec->p_name);
- entryp = parentbuf;
- for (i = 0; i < count; i++) {
- check_parent_entry(statp, entryp);
- entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
- }
+ return 0;
}
static int
-do_bulkstat(parent_t *parentbuf, size_t *parentbuf_size,
- struct xfs_bstat *bstatbuf, int fsfd, jdm_fshandle_t *fshandlep)
+print_parents(
+ struct xfs_handle *handle)
{
- __s32 buflenout;
- __u64 lastino = 0;
- struct xfs_bstat *p;
- struct xfs_bstat *endp;
- struct xfs_fsop_bulkreq bulkreq;
- struct stat mntstat;
-
- if (stat(mntpt, &mntstat)) {
- fprintf(stderr, _("can't stat mount point \"%s\": %s\n"),
- mntpt, strerror(errno));
- return 1;
- }
-
- bulkreq.lastip = &lastino;
- bulkreq.icount = BSTATBUF_SZ;
- bulkreq.ubuffer = (void *)bstatbuf;
- bulkreq.ocount = &buflenout;
-
- while (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT, &bulkreq) == 0) {
- if (*(bulkreq.ocount) == 0) {
- return 0;
- }
- for (p = bstatbuf, endp = bstatbuf + *bulkreq.ocount; p < endp; p++) {
-
- /* inode being modified, get synced data with iget */
- if ( (!p->bs_nlink || !p->bs_mode) && p->bs_ino != 0 ) {
-
- if (xfsctl(mntpt, fsfd, XFS_IOC_FSBULKSTAT_SINGLE, &bulkreq) < 0) {
- fprintf(stderr,
- _("failed to get bulkstat information for inode %llu\n"),
- (unsigned long long) p->bs_ino);
- continue;
- }
- if (!p->bs_nlink || !p->bs_mode || !p->bs_ino) {
- fprintf(stderr,
- _("failed to get valid bulkstat information for inode %llu\n"),
- (unsigned long long) p->bs_ino);
- continue;
- }
- }
-
- /* skip root */
- if (p->bs_ino == mntstat.st_ino) {
- continue;
- }
-
- if (verbose_flag > 1) {
- printf(_("checking inode %llu\n"),
- (unsigned long long) p->bs_ino);
- }
-
- /* print dotted progress */
- if ((inodes_checked % 100) == 0 && verbose_flag == 1) {
- printf("."); fflush(stdout);
- }
- inodes_checked++;
-
- check_parents(parentbuf, parentbuf_size, fshandlep, p);
- }
+ int ret;
- }/*while*/
+ if (handle)
+ ret = handle_walk_parents(handle, sizeof(*handle), pptr_print,
+ NULL);
+ else
+ ret = fd_walk_parents(file->fd, pptr_print, NULL);
+ if (ret)
+ fprintf(stderr, "%s: %s\n", file->name, strerror(ret));
- fprintf(stderr, _("syssgi bulkstat failed: %s\n"), strerror(errno));
- return 1;
+ return 0;
}
static int
-parent_check(void)
+path_print(
+ const char *mntpt,
+ const struct path_list *path,
+ void *arg)
{
- int fsfd;
- jdm_fshandle_t *fshandlep;
- parent_t *parentbuf;
- size_t parentbuf_size = PARENTBUF_SZ;
- struct xfs_bstat *bstatbuf;
-
- err_status = 0;
- inodes_checked = 0;
-
- sync();
-
- fsfd = file->fd;
-
- fshandlep = jdm_getfshandle(mntpt);
- if (fshandlep == NULL) {
- fprintf(stderr, _("unable to open \"%s\" for jdm: %s\n"),
- mntpt,
- strerror(errno));
- return 1;
- }
+ char buf[PATH_MAX];
+ size_t len = PATH_MAX;
+ int mntpt_len = strlen(mntpt);
+ int ret;
- /* allocate buffers */
- bstatbuf = (struct xfs_bstat *)calloc(BSTATBUF_SZ, sizeof(struct xfs_bstat));
- parentbuf = (parent_t *)malloc(parentbuf_size);
- if (!bstatbuf || !parentbuf) {
- fprintf(stderr, _("unable to allocate buffers: %s\n"),
- strerror(errno));
- err_status = 1;
- goto out;
- }
-
- if (do_bulkstat(parentbuf, &parentbuf_size, bstatbuf, fsfd, fshandlep) != 0)
- err_status++;
+ /* Trim trailing slashes from the mountpoint */
+ while (mntpt_len > 0 && mntpt[mntpt_len - 1] == '/')
+ mntpt_len--;
- if (err_status > 0)
- fprintf(stderr, _("num errors: %d\n"), err_status);
- else
- printf(_("succeeded checking %llu inodes\n"),
- (unsigned long long) inodes_checked);
+ ret = snprintf(buf, len, "%.*s", mntpt_len, mntpt);
+ if (ret != mntpt_len)
+ return ENAMETOOLONG;
-out:
- free(bstatbuf);
- free(parentbuf);
- free(fshandlep);
- return err_status;
-}
+ ret = path_list_to_string(path, buf + ret, len - ret);
+ if (ret < 0)
+ return ENAMETOOLONG;
-static void
-print_parent_entry(parent_t *parent, int fullpath)
-{
- printf(_("p_ino = %llu\n"), (unsigned long long) parent->p_ino);
- printf(_("p_gen = %u\n"), parent->p_gen);
- printf(_("p_reclen = %u\n"), parent->p_reclen);
- if (fullpath)
- printf(_("p_name = \"%s%s\"\n"), mntpt, parent->p_name);
- else
- printf(_("p_name = \"%s\"\n"), parent->p_name);
+ printf("%s\n", buf);
+ return 0;
}
static int
-parent_list(int fullpath)
+print_paths(
+ struct xfs_handle *handle)
{
- void *handlep = NULL;
- size_t handlen;
- int error, i;
- int retval = 1;
- __u32 count;
- parent_t *entryp;
- parent_t *parentbuf = NULL;
- char *path = file->name;
- int pb_size = PARENTBUF_SZ;
+ int ret;
- /* XXXX for linux libhandle version - to set libhandle fsfd cache */
- {
- void *fshandle;
- size_t fshlen;
-
- if (path_to_fshandle(mntpt, &fshandle, &fshlen) != 0) {
- fprintf(stderr, _("%s: failed path_to_fshandle \"%s\": %s\n"),
- progname, path, strerror(errno));
- goto error;
- }
- free_handle(fshandle, fshlen);
- }
-
- if (path_to_handle(path, &handlep, &handlen) != 0) {
- fprintf(stderr, _("%s: path_to_handle failed for \"%s\"\n"), progname, path);
- goto error;
- }
-
- do {
- parentbuf = (parent_t *)realloc(parentbuf, pb_size);
- if (!parentbuf) {
- fprintf(stderr, _("%s: unable to allocate parent buffer: %s\n"),
- progname, strerror(errno));
- goto error;
- }
-
- if (fullpath) {
- error = parentpaths_by_handle(handlep,
- handlen,
- parentbuf,
- pb_size,
- &count);
- } else {
- error = parents_by_handle(handlep,
- handlen,
- parentbuf,
- pb_size,
- &count);
- }
- if (error == ERANGE) {
- pb_size *= 2;
- } else if (error) {
- fprintf(stderr, _("%s: %s call failed for \"%s\": %s\n"),
- progname, fullpath ? "parentpaths" : "parents",
- path, strerror(errno));
- goto error;
- }
- } while (error == ERANGE);
-
- if (count == 0) {
- /* no links for inode - something wrong here */
- fprintf(stderr, _("%s: inode-path is missing\n"), progname);
- goto error;
- }
-
- entryp = parentbuf;
- for (i = 0; i < count; i++) {
- print_parent_entry(entryp, fullpath);
- entryp = (parent_t*) (((char*)entryp) + entryp->p_reclen);
- }
-
- retval = 0;
-error:
- free(handlep);
- free(parentbuf);
- return retval;
+ if (handle)
+ ret = handle_walk_parent_paths(handle, sizeof(*handle),
+ path_print, NULL);
+ else
+ ret = fd_walk_parent_paths(file->fd, path_print, NULL);
+ if (ret)
+ fprintf(stderr, "%s: %s\n", file->name, strerror(ret));
+ return 0;
}
static int
-parent_f(int argc, char **argv)
+parent_f(
+ int argc,
+ char **argv)
{
- int c;
- int listpath_flag = 0;
- int check_flag = 0;
- fs_path_t *fs;
- static int tab_init;
+ struct xfs_handle handle;
+ void *hanp = NULL;
+ size_t hlen;
+ struct fs_path *fs;
+ char *p;
+ uint64_t ino = 0;
+ uint32_t gen = 0;
+ int c;
+ int listpath_flag = 0;
+ int ret;
+ static int tab_init;
if (!tab_init) {
tab_init = 1;
@@ -380,46 +127,74 @@ parent_f(int argc, char **argv)
}
mntpt = fs->fs_dir;
- verbose_flag = 0;
-
- while ((c = getopt(argc, argv, "cpv")) != EOF) {
+ while ((c = getopt(argc, argv, "p")) != EOF) {
switch (c) {
- case 'c':
- check_flag = 1;
- break;
case 'p':
listpath_flag = 1;
break;
- case 'v':
- verbose_flag++;
- break;
default:
return command_usage(&parent_cmd);
}
}
- if (!check_flag && !listpath_flag) /* default case */
- exitcode = parent_list(listpath_flag);
- else {
- if (listpath_flag)
- exitcode = parent_list(listpath_flag);
- if (check_flag)
- exitcode = parent_check();
+ /*
+ * Always initialize the fshandle table because we need it for
+ * the ppaths functions to work.
+ */
+ ret = path_to_fshandle((char *)mntpt, &hanp, &hlen);
+ if (ret) {
+ perror(mntpt);
+ return 0;
}
+ if (optind + 2 == argc) {
+ ino = strtoull(argv[optind], &p, 0);
+ if (*p != '\0' || ino == 0) {
+ fprintf(stderr,
+ _("Bad inode number '%s'.\n"),
+ argv[optind]);
+ return 0;
+ }
+ gen = strtoul(argv[optind + 1], &p, 0);
+ if (*p != '\0') {
+ fprintf(stderr,
+ _("Bad generation number '%s'.\n"),
+ argv[optind + 1]);
+ return 0;
+ }
+
+ memcpy(&handle, hanp, sizeof(handle));
+ handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
+ sizeof(handle.ha_fid.fid_len);
+ handle.ha_fid.fid_pad = 0;
+ handle.ha_fid.fid_ino = ino;
+ handle.ha_fid.fid_gen = gen;
+
+ } else if (optind != argc) {
+ return command_usage(&parent_cmd);
+ }
+
+ if (listpath_flag)
+ exitcode = print_paths(ino ? &handle : NULL);
+ else
+ exitcode = print_parents(ino ? &handle : NULL);
+
+ if (hanp)
+ free_handle(hanp, hlen);
+
return 0;
}
static void
parent_help(void)
{
- printf(_(
+printf(_(
"\n"
" list the current file's parents and their filenames\n"
"\n"
-" -c -- check the current file's file system for parent consistency\n"
-" -p -- list the current file's parents and their full paths\n"
-" -v -- verbose mode\n"
+" -p -- list the current file's paths up to the root\n"
+"\n"
+"If ino and gen are supplied, use them instead.\n"
"\n"));
}
@@ -430,11 +205,10 @@ parent_init(void)
parent_cmd.cfunc = parent_f;
parent_cmd.argmin = 0;
parent_cmd.argmax = -1;
- parent_cmd.args = _("[-cpv]");
+ parent_cmd.args = _("[-p] [ino gen]");
parent_cmd.flags = CMD_NOMAP_OK;
- parent_cmd.oneline = _("print or check parent inodes");
+ parent_cmd.oneline = _("print parent inodes");
parent_cmd.help = parent_help;
- if (expert)
- add_command(&parent_cmd);
+ add_command(&parent_cmd);
}
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index ef7087b3d..57ebe3a52 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -961,25 +961,26 @@ and
options behave as described above, in
.B chproj.
.TP
-.BR parent " [ " \-cpv " ]"
+.BR parent " [ " \-p " ] [" " ino gen " "]"
By default this command prints out the parent inode numbers,
inode generation numbers and basenames of all the hardlinks which
point to the inode of the current file.
+
+If the optional
+.B ino
+and
+.B gen
+parameters are provided, they will be used to create a file handle on the same
+filesystem as the open file.
+The parents of the file represented by the handle will be reported instead of
+the open file.
+
.RS 1.0i
.PD 0
.TP 0.4i
.B \-p
the output is similar to the default output except pathnames up to
the mount-point are printed out instead of the component name.
-.TP
-.B \-c
-the file's filesystem will check all the parent attributes for consistency.
-.TP
-.B \-v
-verbose output will be printed.
-.RE
-.IP
-.B [NOTE: Not currently operational on Linux.]
.RE
.PD
.TP
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 20/32] xfs_io: Add i, n and f flags to parent command
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (18 preceding siblings ...)
2023-04-06 19:36 ` [PATCH 19/32] xfs_io: adapt parent command to new parent pointer ioctls Darrick J. Wong
@ 2023-04-06 19:36 ` Darrick J. Wong
2023-04-06 19:37 ` [PATCH 21/32] xfs_logprint: decode parent pointers in ATTRI items fully Darrick J. Wong
` (11 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:36 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
This patch adds the flags i, n, and f to the parent command. These flags add
filtering options that are used by the new parent pointer tests in xfstests, and
help to improve the test run time. The flags are:
-i: Only show parent pointer records containing the given inode
-n: Only show parent pointer records containing the given filename
-f: Print records in short format: ino/gen/namelen/name
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
[djwong: adapt to new getparents ioctl]
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
io/parent.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++------
man/man8/xfs_io.8 | 11 ++++++-
2 files changed, 89 insertions(+), 11 deletions(-)
diff --git a/io/parent.c b/io/parent.c
index 65fd892bf..6bb7571e1 100644
--- a/io/parent.c
+++ b/io/parent.c
@@ -15,11 +15,18 @@
static cmdinfo_t parent_cmd;
static char *mntpt;
+struct pptr_args {
+ uint64_t filter_ino;
+ char *filter_name;
+ bool shortformat;
+};
+
static int
pptr_print(
const struct parent_rec *rec,
void *arg)
{
+ struct pptr_args *args = arg;
const char *name = (char *)rec->p_name;
unsigned int namelen;
@@ -28,7 +35,22 @@ pptr_print(
return 0;
}
+ if (args->filter_ino && rec->p_ino != args->filter_ino)
+ return 0;
+ if (args->filter_name && strcmp(args->filter_name, name))
+ return 0;
+
namelen = strlen(name);
+
+ if (args->shortformat) {
+ printf("%llu/%u/%u/%s\n",
+ (unsigned long long)rec->p_ino,
+ (unsigned int)rec->p_gen,
+ namelen,
+ rec->p_name);
+ return 0;
+ }
+
printf(_("p_ino = %llu\n"), (unsigned long long)rec->p_ino);
printf(_("p_gen = %u\n"), (unsigned int)rec->p_gen);
printf(_("p_namelen = %u\n"), namelen);
@@ -39,32 +61,55 @@ pptr_print(
static int
print_parents(
- struct xfs_handle *handle)
+ struct xfs_handle *handle,
+ struct pptr_args *args)
{
int ret;
if (handle)
ret = handle_walk_parents(handle, sizeof(*handle), pptr_print,
- NULL);
+ args);
else
- ret = fd_walk_parents(file->fd, pptr_print, NULL);
+ ret = fd_walk_parents(file->fd, pptr_print, args);
if (ret)
fprintf(stderr, "%s: %s\n", file->name, strerror(ret));
return 0;
}
+static int
+filter_path_components(
+ const char *name,
+ uint64_t ino,
+ void *arg)
+{
+ struct pptr_args *args = arg;
+
+ if (args->filter_ino && ino == args->filter_ino)
+ return ECANCELED;
+ if (args->filter_name && !strcmp(args->filter_name, name))
+ return ECANCELED;
+ return 0;
+}
+
static int
path_print(
const char *mntpt,
const struct path_list *path,
void *arg)
{
+ struct pptr_args *args = arg;
char buf[PATH_MAX];
size_t len = PATH_MAX;
int mntpt_len = strlen(mntpt);
int ret;
+ if (args->filter_ino || args->filter_name) {
+ ret = path_walk_components(path, filter_path_components, args);
+ if (ret != ECANCELED)
+ return 0;
+ }
+
/* Trim trailing slashes from the mountpoint */
while (mntpt_len > 0 && mntpt[mntpt_len - 1] == '/')
mntpt_len--;
@@ -83,15 +128,16 @@ path_print(
static int
print_paths(
- struct xfs_handle *handle)
+ struct xfs_handle *handle,
+ struct pptr_args *args)
{
int ret;
if (handle)
ret = handle_walk_parent_paths(handle, sizeof(*handle),
- path_print, NULL);
+ path_print, args);
else
- ret = fd_walk_parent_paths(file->fd, path_print, NULL);
+ ret = fd_walk_parent_paths(file->fd, path_print, args);
if (ret)
fprintf(stderr, "%s: %s\n", file->name, strerror(ret));
return 0;
@@ -103,6 +149,7 @@ parent_f(
char **argv)
{
struct xfs_handle handle;
+ struct pptr_args args = { 0 };
void *hanp = NULL;
size_t hlen;
struct fs_path *fs;
@@ -127,11 +174,27 @@ parent_f(
}
mntpt = fs->fs_dir;
- while ((c = getopt(argc, argv, "p")) != EOF) {
+ while ((c = getopt(argc, argv, "pfi:n:")) != EOF) {
switch (c) {
case 'p':
listpath_flag = 1;
break;
+ case 'i':
+ args.filter_ino = strtoull(optarg, &p, 0);
+ if (*p != '\0' || args.filter_ino == 0) {
+ fprintf(stderr,
+ _("Bad inode number '%s'.\n"),
+ optarg);
+ return 0;
+ }
+
+ break;
+ case 'n':
+ args.filter_name = optarg;
+ break;
+ case 'f':
+ args.shortformat = true;
+ break;
default:
return command_usage(&parent_cmd);
}
@@ -175,9 +238,9 @@ parent_f(
}
if (listpath_flag)
- exitcode = print_paths(ino ? &handle : NULL);
+ exitcode = print_paths(ino ? &handle : NULL, &args);
else
- exitcode = print_parents(ino ? &handle : NULL);
+ exitcode = print_parents(ino ? &handle : NULL, &args);
if (hanp)
free_handle(hanp, hlen);
@@ -195,6 +258,12 @@ printf(_(
" -p -- list the current file's paths up to the root\n"
"\n"
"If ino and gen are supplied, use them instead.\n"
+"\n"
+" -i -- Only show parent pointer records containing the given inode\n"
+"\n"
+" -n -- Only show parent pointer records containing the given filename\n"
+"\n"
+" -f -- Print records in short format: ino/gen/namelen/filename\n"
"\n"));
}
@@ -205,7 +274,7 @@ parent_init(void)
parent_cmd.cfunc = parent_f;
parent_cmd.argmin = 0;
parent_cmd.argmax = -1;
- parent_cmd.args = _("[-p] [ino gen]");
+ parent_cmd.args = _("[-p] [ino gen] [-i ino] [-n name] [-f]");
parent_cmd.flags = CMD_NOMAP_OK;
parent_cmd.oneline = _("print parent inodes");
parent_cmd.help = parent_help;
diff --git a/man/man8/xfs_io.8 b/man/man8/xfs_io.8
index 57ebe3a52..7c88784a1 100644
--- a/man/man8/xfs_io.8
+++ b/man/man8/xfs_io.8
@@ -961,7 +961,7 @@ and
options behave as described above, in
.B chproj.
.TP
-.BR parent " [ " \-p " ] [" " ino gen " "]"
+.BR parent " [ " \-fp " ] [-i " ino "] [-n " name "] [" " ino gen " "]"
By default this command prints out the parent inode numbers,
inode generation numbers and basenames of all the hardlinks which
point to the inode of the current file.
@@ -978,6 +978,15 @@ the open file.
.RS 1.0i
.PD 0
.TP 0.4i
+.B \-f
+Print records in short format: ino/gen/namelen/name
+.TP 0.4i
+.B \-i
+Only show parent pointer records containing this inode number.
+.TP 0.4i
+.B \-n
+Only show parent pointer records containing this directory entry name.
+.TP 0.4i
.B \-p
the output is similar to the default output except pathnames up to
the mount-point are printed out instead of the component name.
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 21/32] xfs_logprint: decode parent pointers in ATTRI items fully
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (19 preceding siblings ...)
2023-04-06 19:36 ` [PATCH 20/32] xfs_io: Add i, n and f flags to parent command Darrick J. Wong
@ 2023-04-06 19:37 ` Darrick J. Wong
2023-04-06 19:37 ` [PATCH 22/32] xfs_scrub: use parent pointers when possible to report file operations Darrick J. Wong
` (10 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:37 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
This patch modifies the ATTRI print routines to look for the parent
pointer flag, and decode logged parent pointers fully when dumping log
contents. Between the existing ATTRI: printouts and the new ones
introduced here, we can figure out what was stored in each log iovec,
as well as the higher level parent pointer that was logged.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: adjust to new ondisk format]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/libxfs_api_defs.h | 4 ++
logprint/log_redo.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 85 insertions(+)
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index f8efcce77..092934935 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -139,6 +139,10 @@
#define xfs_log_get_max_trans_res libxfs_log_get_max_trans_res
#define xfs_log_sb libxfs_log_sb
#define xfs_mode_to_ftype libxfs_mode_to_ftype
+#define xfs_parent_add libxfs_parent_add
+#define xfs_parent_finish libxfs_parent_finish
+#define xfs_parent_irec_from_disk libxfs_parent_irec_from_disk
+#define xfs_parent_start libxfs_parent_start
#define xfs_perag_get libxfs_perag_get
#define xfs_perag_put libxfs_perag_put
#define xfs_prealloc_blocks libxfs_prealloc_blocks
diff --git a/logprint/log_redo.c b/logprint/log_redo.c
index 8b6aa9279..a8a367bb0 100644
--- a/logprint/log_redo.c
+++ b/logprint/log_redo.c
@@ -674,6 +674,59 @@ xfs_attri_copy_log_format(
return 1;
}
+static void
+dump_pptr(
+ const char *tag,
+ const void *name_ptr,
+ unsigned int name_len,
+ const void *value_ptr,
+ unsigned int value_len)
+{
+ struct xfs_parent_name_irec irec;
+
+ if (name_len < sizeof(struct xfs_parent_name_rec)) {
+ printf("PPTR: %s CORRUPT\n", tag);
+ return;
+ }
+
+ libxfs_parent_irec_from_disk(&irec, name_ptr, value_ptr, value_len);
+
+ printf("PPTR: %s attr_namelen %u attr_valuelen %u\n", tag, name_len, value_len);
+ printf("PPTR: %s parent_ino %llu parent_gen %u namehash 0x%x namelen %u name '%.*s'\n",
+ tag,
+ (unsigned long long)irec.p_ino,
+ irec.p_gen,
+ irec.p_namehash,
+ irec.p_namelen,
+ irec.p_namelen,
+ irec.p_name);
+}
+
+static void
+dump_pptr_update(
+ const void *name_ptr,
+ unsigned int name_len,
+ const void *new_name_ptr,
+ unsigned int new_name_len,
+ const void *value_ptr,
+ unsigned int value_len,
+ const void *new_value_ptr,
+ unsigned int new_value_len)
+{
+ if (new_name_ptr && name_ptr) {
+ dump_pptr("OLDNAME", name_ptr, name_len, value_ptr, value_len);
+ dump_pptr("NEWNAME", new_name_ptr, new_name_len, new_value_ptr,
+ new_value_len);
+ return;
+ }
+
+ if (name_ptr)
+ dump_pptr("NAME", name_ptr, name_len, value_ptr, value_len);
+ if (new_name_ptr)
+ dump_pptr("NEWNAME", new_name_ptr, new_name_len, new_value_ptr,
+ new_value_len);
+}
+
static inline unsigned int
xfs_attr_log_item_op(const struct xfs_attri_log_format *attrp)
{
@@ -688,6 +741,10 @@ xlog_print_trans_attri(
{
struct xfs_attri_log_format *src_f = NULL;
xlog_op_header_t *head = NULL;
+ void *name_ptr = NULL;
+ void *new_name_ptr = NULL;
+ void *value_ptr = NULL;
+ void *new_value_ptr = NULL;
uint dst_len;
unsigned int name_len = 0;
unsigned int new_name_len = 0;
@@ -741,6 +798,7 @@ xlog_print_trans_attri(
(*i)++;
head = (xlog_op_header_t *)*ptr;
xlog_print_op_header(head, *i, ptr);
+ name_ptr = *ptr;
error = xlog_print_trans_attri_name(ptr,
be32_to_cpu(head->oh_len), "name");
if (error)
@@ -752,6 +810,7 @@ xlog_print_trans_attri(
(*i)++;
head = (xlog_op_header_t *)*ptr;
xlog_print_op_header(head, *i, ptr);
+ new_name_ptr = *ptr;
error = xlog_print_trans_attri_name(ptr,
be32_to_cpu(head->oh_len), "newname");
if (error)
@@ -763,6 +822,7 @@ xlog_print_trans_attri(
(*i)++;
head = (xlog_op_header_t *)*ptr;
xlog_print_op_header(head, *i, ptr);
+ value_ptr = *ptr;
error = xlog_print_trans_attri_value(ptr,
be32_to_cpu(head->oh_len), value_len, "value");
if (error)
@@ -774,12 +834,19 @@ xlog_print_trans_attri(
(*i)++;
head = (xlog_op_header_t *)*ptr;
xlog_print_op_header(head, *i, ptr);
+ new_value_ptr = *ptr;
error = xlog_print_trans_attri_value(ptr,
be32_to_cpu(head->oh_len), new_value_len,
"newvalue");
if (error)
goto error;
}
+
+ if (src_f->alfi_attr_filter & XFS_ATTR_PARENT)
+ dump_pptr_update(name_ptr, name_len,
+ new_name_ptr, new_name_len,
+ value_ptr, value_len,
+ new_value_ptr, new_value_len);
error:
free(src_f);
@@ -822,6 +889,10 @@ xlog_recover_print_attri(
struct xlog_recover_item *item)
{
struct xfs_attri_log_format *f, *src_f = NULL;
+ void *name_ptr = NULL;
+ void *new_name_ptr = NULL;
+ void *value_ptr = NULL;
+ void *new_value_ptr = NULL;
uint src_len, dst_len;
unsigned int name_len = 0;
unsigned int new_name_len = 0;
@@ -872,6 +943,7 @@ xlog_recover_print_attri(
printf(_("ATTRI: name len:%u\n"), name_len);
print_or_dump((char *)item->ri_buf[region].i_addr,
name_len);
+ name_ptr = item->ri_buf[region].i_addr;
}
if (new_name_len > 0) {
@@ -879,6 +951,7 @@ xlog_recover_print_attri(
printf(_("ATTRI: newname len:%u\n"), new_name_len);
print_or_dump((char *)item->ri_buf[region].i_addr,
new_name_len);
+ new_name_ptr = item->ri_buf[region].i_addr;
}
if (value_len > 0) {
@@ -887,6 +960,7 @@ xlog_recover_print_attri(
region++;
printf(_("ATTRI: value len:%u\n"), value_len);
print_or_dump((char *)item->ri_buf[region].i_addr, len);
+ value_ptr = item->ri_buf[region].i_addr;
}
if (new_value_len > 0) {
@@ -895,8 +969,15 @@ xlog_recover_print_attri(
region++;
printf(_("ATTRI: newvalue len:%u\n"), new_value_len);
print_or_dump((char *)item->ri_buf[region].i_addr, len);
+ new_value_ptr = item->ri_buf[region].i_addr;
}
+ if (src_f->alfi_attr_filter & XFS_ATTR_PARENT)
+ dump_pptr_update(name_ptr, name_len,
+ new_name_ptr, new_name_len,
+ value_ptr, value_len,
+ new_value_ptr, new_value_len);
+
out:
free(f);
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 22/32] xfs_scrub: use parent pointers when possible to report file operations
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (20 preceding siblings ...)
2023-04-06 19:37 ` [PATCH 21/32] xfs_logprint: decode parent pointers in ATTRI items fully Darrick J. Wong
@ 2023-04-06 19:37 ` Darrick J. Wong
2023-04-06 19:37 ` [PATCH 23/32] xfs_db: report parent bit on xattrs Darrick J. Wong
` (9 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:37 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
If parent pointers are available, use them to supply file paths when
doing things to files, instead of merely printing the inode number.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
scrub/common.c | 41 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 39 insertions(+), 2 deletions(-)
diff --git a/scrub/common.c b/scrub/common.c
index 49a87f412..172a32ba6 100644
--- a/scrub/common.c
+++ b/scrub/common.c
@@ -9,6 +9,7 @@
#include <syslog.h>
#include "platform_defs.h"
#include "libfrog/paths.h"
+#include "libfrog/getparents.h"
#include "xfs_scrub.h"
#include "common.h"
#include "progress.h"
@@ -403,19 +404,55 @@ scrub_render_ino_descr(
...)
{
va_list args;
+ size_t pathlen = 0;
uint32_t agno;
uint32_t agino;
int ret;
+ if (ctx->mnt.fsgeom.flags & XFS_FSOP_GEOM_FLAGS_PARENT) {
+ struct xfs_handle handle;
+
+ memcpy(&handle.ha_fsid, ctx->fshandle, sizeof(handle.ha_fsid));
+ handle.ha_fid.fid_len = sizeof(xfs_fid_t) -
+ sizeof(handle.ha_fid.fid_len);
+ handle.ha_fid.fid_pad = 0;
+ handle.ha_fid.fid_ino = ino;
+ handle.ha_fid.fid_gen = gen;
+
+ ret = handle_to_path(&handle, sizeof(struct xfs_handle), buf,
+ buflen);
+ if (ret)
+ goto report_inum;
+
+ /*
+ * Leave at least 16 bytes for the description of what went
+ * wrong. If we can't do that, we'll use the inode number.
+ */
+ pathlen = strlen(buf);
+ if (pathlen >= buflen - 16)
+ goto report_inum;
+
+ if (format) {
+ buf[pathlen] = ' ';
+ buf[pathlen + 1] = 0;
+ pathlen++;
+ }
+
+ goto report_format;
+ }
+
+report_inum:
agno = cvt_ino_to_agno(&ctx->mnt, ino);
agino = cvt_ino_to_agino(&ctx->mnt, ino);
ret = snprintf(buf, buflen, _("inode %"PRIu64" (%"PRIu32"/%"PRIu32")%s"),
ino, agno, agino, format ? " " : "");
if (ret < 0 || ret >= buflen || format == NULL)
return ret;
+ pathlen = ret;
+report_format:
va_start(args, format);
- ret += vsnprintf(buf + ret, buflen - ret, format, args);
+ pathlen += vsnprintf(buf + pathlen, buflen - pathlen, format, args);
va_end(args);
- return ret;
+ return pathlen;
}
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 23/32] xfs_db: report parent bit on xattrs
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (21 preceding siblings ...)
2023-04-06 19:37 ` [PATCH 22/32] xfs_scrub: use parent pointers when possible to report file operations Darrick J. Wong
@ 2023-04-06 19:37 ` Darrick J. Wong
2023-04-06 19:37 ` [PATCH 24/32] xfs_db: report parent pointers embedded in xattrs Darrick J. Wong
` (8 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:37 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Display the parent bit on xattr keys
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
---
db/attr.c | 3 +++
db/attrshort.c | 3 +++
2 files changed, 6 insertions(+)
diff --git a/db/attr.c b/db/attr.c
index ba722e146..f29e4a544 100644
--- a/db/attr.c
+++ b/db/attr.c
@@ -82,6 +82,9 @@ const field_t attr_leaf_entry_flds[] = {
{ "local", FLDT_UINT1,
OI(LEOFF(flags) + bitsz(uint8_t) - XFS_ATTR_LOCAL_BIT - 1), C1, 0,
TYP_NONE },
+ { "parent", FLDT_UINT1,
+ OI(LEOFF(flags) + bitsz(uint8_t) - XFS_ATTR_PARENT_BIT - 1), C1, 0,
+ TYP_NONE },
{ "pad2", FLDT_UINT8X, OI(LEOFF(pad2)), C1, FLD_SKIPALL, TYP_NONE },
{ NULL }
};
diff --git a/db/attrshort.c b/db/attrshort.c
index e234fbd83..872d771d5 100644
--- a/db/attrshort.c
+++ b/db/attrshort.c
@@ -44,6 +44,9 @@ const field_t attr_sf_entry_flds[] = {
{ "secure", FLDT_UINT1,
OI(EOFF(flags) + bitsz(uint8_t) - XFS_ATTR_SECURE_BIT - 1), C1, 0,
TYP_NONE },
+ { "parent", FLDT_UINT1,
+ OI(EOFF(flags) + bitsz(uint8_t) - XFS_ATTR_PARENT_BIT - 1), C1, 0,
+ TYP_NONE },
{ "name", FLDT_CHARNS, OI(EOFF(nameval)), attr_sf_entry_name_count,
FLD_COUNT, TYP_NONE },
{ "value", FLDT_CHARNS, attr_sf_entry_value_offset,
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 24/32] xfs_db: report parent pointers embedded in xattrs
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (22 preceding siblings ...)
2023-04-06 19:37 ` [PATCH 23/32] xfs_db: report parent bit on xattrs Darrick J. Wong
@ 2023-04-06 19:37 ` Darrick J. Wong
2023-04-06 19:38 ` [PATCH 25/32] xfs_db: obfuscate dirent and parent pointer names consistently Darrick J. Wong
` (7 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:37 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Decode the parent pointer inode, generation, namehash, and name fields
if the parent pointer passes basic validation checks.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
db/attr.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
db/attrshort.c | 48 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 110 insertions(+)
diff --git a/db/attr.c b/db/attr.c
index f29e4a544..9e7bbd164 100644
--- a/db/attr.c
+++ b/db/attr.c
@@ -19,6 +19,8 @@ static int attr_leaf_entries_count(void *obj, int startoff);
static int attr_leaf_hdr_count(void *obj, int startoff);
static int attr_leaf_name_local_count(void *obj, int startoff);
static int attr_leaf_name_local_name_count(void *obj, int startoff);
+static int attr_leaf_name_pptr_count(void *obj, int startoff);
+static int attr_leaf_name_pptr_namelen(void *obj, int startoff);
static int attr_leaf_name_local_value_count(void *obj, int startoff);
static int attr_leaf_name_local_value_offset(void *obj, int startoff,
int idx);
@@ -111,6 +113,8 @@ const field_t attr_leaf_map_flds[] = {
#define LNOFF(f) bitize(offsetof(xfs_attr_leaf_name_local_t, f))
#define LVOFF(f) bitize(offsetof(xfs_attr_leaf_name_remote_t, f))
+#define PPOFF(f) bitize(offsetof(xfs_attr_leaf_name_local_t, nameval) + \
+ offsetof(struct xfs_parent_name_rec, f))
const field_t attr_leaf_name_flds[] = {
{ "valuelen", FLDT_UINT16D, OI(LNOFF(valuelen)),
attr_leaf_name_local_count, FLD_COUNT, TYP_NONE },
@@ -118,6 +122,14 @@ const field_t attr_leaf_name_flds[] = {
attr_leaf_name_local_count, FLD_COUNT, TYP_NONE },
{ "name", FLDT_CHARNS, OI(LNOFF(nameval)),
attr_leaf_name_local_name_count, FLD_COUNT, TYP_NONE },
+ { "parent_ino", FLDT_INO, OI(PPOFF(p_ino)),
+ attr_leaf_name_pptr_count, FLD_COUNT, TYP_INODE },
+ { "parent_gen", FLDT_UINT32D, OI(PPOFF(p_gen)),
+ attr_leaf_name_pptr_count, FLD_COUNT, TYP_NONE },
+ { "parent_namehash", FLDT_UINT32X, OI(PPOFF(p_namehash)),
+ attr_leaf_name_pptr_count, FLD_COUNT, TYP_NONE },
+ { "parent_name", FLDT_CHARNS, attr_leaf_name_local_value_offset,
+ attr_leaf_name_pptr_namelen, FLD_COUNT|FLD_OFFSET, TYP_NONE },
{ "value", FLDT_CHARNS, attr_leaf_name_local_value_offset,
attr_leaf_name_local_value_count, FLD_COUNT|FLD_OFFSET, TYP_NONE },
{ "valueblk", FLDT_UINT32X, OI(LVOFF(valueblk)),
@@ -273,6 +285,26 @@ attr_leaf_name_local_count(
__attr_leaf_name_local_count);
}
+static int
+__attr_leaf_name_pptr_count(
+ struct xfs_attr_leafblock *leaf,
+ struct xfs_attr_leaf_entry *e,
+ int i)
+{
+ if ((e->flags & XFS_ATTR_NSP_ONDISK_MASK) == XFS_ATTR_PARENT)
+ return 1;
+ return 0;
+}
+
+static int
+attr_leaf_name_pptr_count(
+ void *obj,
+ int startoff)
+{
+ return attr_leaf_entry_walk(obj, startoff,
+ __attr_leaf_name_pptr_count);
+}
+
static int
__attr_leaf_name_local_name_count(
struct xfs_attr_leafblock *leaf,
@@ -283,6 +315,8 @@ __attr_leaf_name_local_name_count(
if (!(e->flags & XFS_ATTR_LOCAL))
return 0;
+ if ((e->flags & XFS_ATTR_NSP_ONDISK_MASK) == XFS_ATTR_PARENT)
+ return 0;
l = xfs_attr3_leaf_name_local(leaf, i);
return l->namelen;
@@ -297,6 +331,32 @@ attr_leaf_name_local_name_count(
__attr_leaf_name_local_name_count);
}
+static int
+__attr_leaf_name_pptr_namelen(
+ struct xfs_attr_leafblock *leaf,
+ struct xfs_attr_leaf_entry *e,
+ int i)
+{
+ struct xfs_attr_leaf_name_local *l;
+
+ if (!(e->flags & XFS_ATTR_LOCAL))
+ return 0;
+ if ((e->flags & XFS_ATTR_NSP_ONDISK_MASK) != XFS_ATTR_PARENT)
+ return 0;
+
+ l = xfs_attr3_leaf_name_local(leaf, i);
+ return be16_to_cpu(l->valuelen);
+}
+
+static int
+attr_leaf_name_pptr_namelen(
+ void *obj,
+ int startoff)
+{
+ return attr_leaf_entry_walk(obj, startoff,
+ __attr_leaf_name_pptr_namelen);
+}
+
static int
__attr_leaf_name_local_value_count(
struct xfs_attr_leafblock *leaf,
@@ -307,6 +367,8 @@ __attr_leaf_name_local_value_count(
if (!(e->flags & XFS_ATTR_LOCAL))
return 0;
+ if ((e->flags & XFS_ATTR_NSP_ONDISK_MASK) == XFS_ATTR_PARENT)
+ return 0;
l = xfs_attr3_leaf_name_local(leaf, i);
return be16_to_cpu(l->valuelen);
diff --git a/db/attrshort.c b/db/attrshort.c
index 872d771d5..9cd3411be 100644
--- a/db/attrshort.c
+++ b/db/attrshort.c
@@ -13,6 +13,8 @@
#include "attrshort.h"
static int attr_sf_entry_name_count(void *obj, int startoff);
+static int attr_sf_entry_pptr_count(void *obj, int startoff);
+static int attr_sf_entry_pptr_namelen(void *obj, int startoff);
static int attr_sf_entry_value_count(void *obj, int startoff);
static int attr_sf_entry_value_offset(void *obj, int startoff, int idx);
static int attr_shortform_list_count(void *obj, int startoff);
@@ -34,6 +36,8 @@ const field_t attr_sf_hdr_flds[] = {
};
#define EOFF(f) bitize(offsetof(struct xfs_attr_sf_entry, f))
+#define PPOFF(f) bitize(offsetof(struct xfs_attr_sf_entry, nameval) + \
+ offsetof(struct xfs_parent_name_rec, f))
const field_t attr_sf_entry_flds[] = {
{ "namelen", FLDT_UINT8D, OI(EOFF(namelen)), C1, 0, TYP_NONE },
{ "valuelen", FLDT_UINT8D, OI(EOFF(valuelen)), C1, 0, TYP_NONE },
@@ -49,11 +53,33 @@ const field_t attr_sf_entry_flds[] = {
TYP_NONE },
{ "name", FLDT_CHARNS, OI(EOFF(nameval)), attr_sf_entry_name_count,
FLD_COUNT, TYP_NONE },
+ { "parent_ino", FLDT_INO, OI(PPOFF(p_ino)), attr_sf_entry_pptr_count,
+ FLD_COUNT, TYP_INODE },
+ { "parent_gen", FLDT_UINT32D, OI(PPOFF(p_gen)), attr_sf_entry_pptr_count,
+ FLD_COUNT, TYP_NONE },
+ { "parent_namehash", FLDT_UINT32X, OI(PPOFF(p_namehash)),
+ attr_sf_entry_pptr_count, FLD_COUNT, TYP_NONE },
+ { "parent_name", FLDT_CHARNS, attr_sf_entry_value_offset,
+ attr_sf_entry_pptr_namelen, FLD_COUNT|FLD_OFFSET, TYP_NONE },
{ "value", FLDT_CHARNS, attr_sf_entry_value_offset,
attr_sf_entry_value_count, FLD_COUNT|FLD_OFFSET, TYP_NONE },
{ NULL }
};
+static int
+attr_sf_entry_pptr_count(
+ void *obj,
+ int startoff)
+{
+ struct xfs_attr_sf_entry *e;
+
+ ASSERT(bitoffs(startoff) == 0);
+ e = (struct xfs_attr_sf_entry *)((char *)obj + byteize(startoff));
+ if ((e->flags & XFS_ATTR_NSP_ONDISK_MASK) == XFS_ATTR_PARENT)
+ return 1;
+ return 0;
+}
+
static int
attr_sf_entry_name_count(
void *obj,
@@ -63,6 +89,8 @@ attr_sf_entry_name_count(
ASSERT(bitoffs(startoff) == 0);
e = (struct xfs_attr_sf_entry *)((char *)obj + byteize(startoff));
+ if ((e->flags & XFS_ATTR_NSP_ONDISK_MASK) == XFS_ATTR_PARENT)
+ return 0;
return e->namelen;
}
@@ -84,6 +112,22 @@ attr_sf_entry_size(
return bitize((int)xfs_attr_sf_entsize(e));
}
+static int
+attr_sf_entry_pptr_namelen(
+ void *obj,
+ int startoff)
+{
+ struct xfs_attr_sf_entry *e;
+
+ ASSERT(bitoffs(startoff) == 0);
+ e = (struct xfs_attr_sf_entry *)((char *)obj + byteize(startoff));
+
+ if ((e->flags & XFS_ATTR_NSP_ONDISK_MASK) != XFS_ATTR_PARENT)
+ return 0;
+
+ return e->valuelen;
+}
+
static int
attr_sf_entry_value_count(
void *obj,
@@ -93,6 +137,10 @@ attr_sf_entry_value_count(
ASSERT(bitoffs(startoff) == 0);
e = (struct xfs_attr_sf_entry *)((char *)obj + byteize(startoff));
+
+ if ((e->flags & XFS_ATTR_NSP_ONDISK_MASK) == XFS_ATTR_PARENT)
+ return 0;
+
return e->valuelen;
}
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 25/32] xfs_db: obfuscate dirent and parent pointer names consistently
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (23 preceding siblings ...)
2023-04-06 19:37 ` [PATCH 24/32] xfs_db: report parent pointers embedded in xattrs Darrick J. Wong
@ 2023-04-06 19:38 ` Darrick J. Wong
2023-04-06 19:38 ` [PATCH 26/32] xfs_db: hoist name obfuscation code out of metadump.c Darrick J. Wong
` (6 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:38 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
When someone wants to perform an obfuscated metadump of a filesystem
where parent pointers are enabled, we have to use the *exact* same
obfuscated name for both the directory entry and the parent pointer.
Create a name remapping table so that when we obfuscate a dirent name or
a parent pointer name, we can apply the same obfuscation when we find
the corresponding parent pointer or dirent.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
db/metadump.c | 313 ++++++++++++++++++++++++++++++++++++++++++++--
libxfs/libxfs_api_defs.h | 2
2 files changed, 304 insertions(+), 11 deletions(-)
diff --git a/db/metadump.c b/db/metadump.c
index 27d1df432..b413ef5b3 100644
--- a/db/metadump.c
+++ b/db/metadump.c
@@ -20,6 +20,14 @@
#include "field.h"
#include "dir2.h"
+#undef REMAP_DEBUG
+
+#ifdef REMAP_DEBUG
+# define remap_debug printf
+#else
+# define remap_debug(...) ((void)0)
+#endif
+
#define DEFAULT_MAX_EXT_SIZE XFS_MAX_BMBT_EXTLEN
/* copy all metadata structures to/from a file */
@@ -736,6 +744,111 @@ nametable_add(xfs_dahash_t hash, int namelen, unsigned char *name)
return ent;
}
+/*
+ * Obfuscated name remapping table for parent pointer-enabled filesystems.
+ * When this feature is enabled, we have to maintain consistency between the
+ * names that appears in the dirent and the corresponding parent pointer.
+ */
+
+struct remap_ent {
+ struct remap_ent *next;
+ xfs_ino_t dir_ino;
+ xfs_dahash_t namehash;
+ uint8_t namelen;
+
+ uint8_t names[];
+};
+
+static inline uint8_t *remap_ent_before(struct remap_ent *ent)
+{
+ return &ent->names[0];
+}
+
+static inline uint8_t *remap_ent_after(struct remap_ent *ent)
+{
+ return &ent->names[ent->namelen];
+}
+
+#define REMAP_TABLE_SIZE 4096
+
+static struct remap_ent *remaptable[REMAP_TABLE_SIZE];
+
+static void
+remaptable_clear(void)
+{
+ int i;
+ struct remap_ent *ent, *next;
+
+ for (i = 0; i < REMAP_TABLE_SIZE; i++) {
+ ent = remaptable[i];
+
+ while (ent) {
+ next = ent->next;
+ free(ent);
+ ent = next;
+ }
+ }
+}
+
+/* Try to find a remapping table entry. */
+static struct remap_ent *
+remaptable_find(
+ xfs_ino_t dir_ino,
+ xfs_dahash_t namehash,
+ const unsigned char *name,
+ unsigned int namelen)
+{
+ struct remap_ent *ent = remaptable[namehash % REMAP_TABLE_SIZE];
+
+ remap_debug("REMAP FIND: 0x%lx hash 0x%x '%.*s'\n",
+ dir_ino, namehash, namelen, name);
+
+ while (ent) {
+ remap_debug("REMAP ENT: 0x%lx hash 0x%x '%.*s'\n",
+ ent->dir_ino, ent->namehash, ent->namelen,
+ remap_ent_before(ent));
+
+ if (ent->dir_ino == dir_ino &&
+ ent->namehash == namehash &&
+ ent->namelen == namelen &&
+ !memcmp(remap_ent_before(ent), name, namelen))
+ return ent;
+ ent = ent->next;
+ }
+
+ return NULL;
+}
+
+/* Remember the remapping for a particular dirent that we obfuscated. */
+static struct remap_ent *
+remaptable_add(
+ xfs_ino_t dir_ino,
+ xfs_dahash_t namehash,
+ const unsigned char *old_name,
+ unsigned int namelen,
+ const unsigned char *new_name)
+{
+ struct remap_ent *ent;
+
+ ent = malloc(sizeof(struct remap_ent) + (namelen * 2));
+ if (!ent)
+ return NULL;
+
+ ent->dir_ino = dir_ino;
+ ent->namehash = namehash;
+ ent->namelen = namelen;
+ memcpy(remap_ent_before(ent), old_name, namelen);
+ memcpy(remap_ent_after(ent), new_name, namelen);
+ ent->next = remaptable[namehash % REMAP_TABLE_SIZE];
+
+ remaptable[namehash % REMAP_TABLE_SIZE] = ent;
+
+ remap_debug("REMAP ADD: 0x%lx hash 0x%x '%.*s' -> '%.*s'\n",
+ dir_ino, namehash, namelen, old_name, namelen,
+ new_name);
+ return ent;
+}
+
#define is_invalid_char(c) ((c) == '/' || (c) == '\0')
#define rol32(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
@@ -1184,6 +1297,7 @@ generate_obfuscated_name(
int namelen,
unsigned char *name)
{
+ unsigned char *old_name = NULL;
xfs_dahash_t hash;
/*
@@ -1205,8 +1319,32 @@ generate_obfuscated_name(
name++;
/* Obfuscate the name (if possible) */
-
hash = libxfs_da_hashname(name, namelen);
+
+ /*
+ * If we're obfuscating a dirent name on a pptrs filesystem, see if we
+ * already processed the parent pointer and use the same name.
+ */
+ if (xfs_has_parent(mp) && ino) {
+ struct remap_ent *remap;
+
+ remap = remaptable_find(cur_ino, hash, name, namelen);
+ if (remap) {
+ remap_debug("found obfuscated dir 0x%lx '%.*s' -> 0x%lx -> '%.*s' \n",
+ cur_ino, namelen,
+ remap_ent_before(remap), ino, namelen,
+ remap_ent_after(remap));
+ memcpy(name, remap_ent_after(remap), namelen);
+ return;
+ }
+
+ /*
+ * If we haven't procesed this dirent name before, save the
+ * old name for a remap table entry. Obfuscate the name.
+ */
+ old_name = alloca(namelen);
+ memcpy(old_name, name, namelen);
+ }
obfuscate_name(hash, namelen, name);
/*
@@ -1230,6 +1368,23 @@ generate_obfuscated_name(
"in dir inode %llu\n",
(unsigned long long) ino,
(unsigned long long) cur_ino);
+
+ /*
+ * We've obfuscated a name in the directory entry. Remember this
+ * remapping for when we come across the parent pointer later.
+ */
+ if (!old_name)
+ return;
+
+ remap_debug("obfuscating dir 0x%lx '%.*s' -> 0x%lx -> '%.*s' \n",
+ cur_ino, namelen, old_name, ino, namelen,
+ name);
+
+ if (!remaptable_add(cur_ino, hash, old_name, namelen, name))
+ print_warning("unable to record remapped dirent name for inode %llu "
+ "in dir inode %llu\n",
+ (unsigned long long) ino,
+ (unsigned long long) cur_ino);
}
static void
@@ -1361,6 +1516,123 @@ process_sf_symlink(
memset(&buf[len], 0, XFS_DFORK_DSIZE(dip, mp) - len);
}
+static inline bool
+want_obfuscate_pptr(
+ unsigned int nsp_flags,
+ const void *name,
+ unsigned int namelen,
+ const void *value,
+ unsigned int valuelen)
+{
+ if (!obfuscate)
+ return false;
+
+ /* Ignore if parent pointers aren't enabled. */
+ if (!xfs_has_parent(mp))
+ return false;
+
+ /* Ignore anything not claiming to be a parent pointer. */
+ if (!(nsp_flags & XFS_ATTR_PARENT))
+ return false;
+
+ /* Obfuscate this parent pointer if it passes basic checks. */
+ if (libxfs_parent_namecheck(mp, name, namelen, nsp_flags) &&
+ libxfs_parent_valuecheck(mp, value, valuelen))
+ return true;
+
+ /* Ignore otherwise. */
+ return false;
+}
+
+static void
+obfuscate_parent_pointer(
+ const struct xfs_parent_name_rec *rec,
+ unsigned char *value,
+ unsigned int valuelen)
+{
+ struct xfs_parent_name_irec irec;
+ struct remap_ent *remap;
+ char *old_name = irec.p_name;
+ xfs_dahash_t hash;
+ xfs_ino_t child_ino = cur_ino;
+
+ libxfs_parent_irec_from_disk(&irec, rec, value, valuelen);
+
+ /*
+ * We don't obfuscate "lost+found" or any orphan files
+ * therein. If When the name table is used for extended
+ * attributes, the inode number provided is 0, in which
+ * case we don't need to make this check.
+ */
+ cur_ino = irec.p_ino;
+ if (in_lost_found(child_ino, valuelen, value)) {
+ cur_ino = child_ino;
+ return;
+ }
+ cur_ino = child_ino;
+
+ /*
+ * If the name starts with a slash, just skip over it. It isn't
+ * included in the hash and we don't record it in the name table.
+ */
+ if (*value == '/') {
+ old_name++;
+ value++;
+ valuelen--;
+ }
+
+ hash = libxfs_da_hashname(value, valuelen);
+
+ /*
+ * If we already processed the dirent, use the same name for the parent
+ * pointer.
+ */
+ remap = remaptable_find(irec.p_ino, hash, value, valuelen);
+ if (remap) {
+ remap_debug("found obfuscated pptr 0x%lx '%.*s' -> 0x%lx -> '%.*s' \n",
+ irec.p_ino, valuelen, remap_ent_before(remap),
+ cur_ino, valuelen, remap_ent_after(remap));
+ memcpy(value, remap_ent_after(remap), valuelen);
+ return;
+ }
+
+ /*
+ * Obfuscate the parent pointer name and remember this for later
+ * in case we encounter the dirent and need to reuse the name there.
+ */
+ obfuscate_name(hash, valuelen, value);
+
+ remap_debug("obfuscated pptr 0x%lx '%.*s' -> 0x%lx -> '%.*s'\n",
+ irec.p_ino, valuelen, old_name, cur_ino, valuelen,
+ value);
+ if (!remaptable_add(irec.p_ino, hash, old_name, valuelen, value))
+ print_warning("unable to record remapped pptr name for inode %llu "
+ "in dir inode %llu\n",
+ (unsigned long long) cur_ino,
+ (unsigned long long) irec.p_ino);
+}
+
+static inline bool
+want_obfuscate_attr(
+ unsigned int nsp_flags,
+ const void *name,
+ unsigned int namelen,
+ const void *value,
+ unsigned int valuelen)
+{
+ if (!obfuscate)
+ return false;
+
+ /*
+ * If we didn't already obfuscate the parent pointer, it's probably
+ * corrupt. Leave it intact for analysis.
+ */
+ if (nsp_flags & XFS_ATTR_PARENT)
+ return false;
+
+ return true;
+}
+
static void
process_sf_attr(
struct xfs_dinode *dip)
@@ -1390,7 +1662,7 @@ process_sf_attr(
asfep = &asfp->list[0];
for (i = 0; (i < asfp->hdr.count) &&
((char *)asfep - (char *)asfp < ino_attr_size); i++) {
-
+ void *name, *value;
int namelen = asfep->namelen;
if (namelen == 0) {
@@ -1406,11 +1678,16 @@ process_sf_attr(
break;
}
- if (obfuscate) {
- generate_obfuscated_name(0, asfep->namelen,
- &asfep->nameval[0]);
- memset(&asfep->nameval[asfep->namelen], 'v',
- asfep->valuelen);
+ name = &asfep->nameval[0];
+ value = &asfep->nameval[asfep->namelen];
+
+ if (want_obfuscate_pptr(asfep->flags, name, namelen, value,
+ asfep->valuelen)) {
+ obfuscate_parent_pointer(name, value, asfep->valuelen);
+ } else if (want_obfuscate_attr(asfep->flags, name, namelen,
+ value, asfep->valuelen)) {
+ generate_obfuscated_name(0, asfep->namelen, name);
+ memset(value, 'v', asfep->valuelen);
}
asfep = (struct xfs_attr_sf_entry *)((char *)asfep +
@@ -1777,6 +2054,9 @@ process_attr_block(
break;
}
if (entry->flags & XFS_ATTR_LOCAL) {
+ void *name, *value;
+ unsigned int valuelen;
+
local = xfs_attr3_leaf_name_local(leaf, i);
if (local->namelen == 0) {
if (show_warnings)
@@ -1785,11 +2065,21 @@ process_attr_block(
(long long)cur_ino);
break;
}
- if (obfuscate) {
+
+ name = &local->nameval[0];
+ value = &local->nameval[local->namelen];
+ valuelen = be16_to_cpu(local->valuelen);
+
+ if (want_obfuscate_pptr(entry->flags, name,
+ local->namelen, value,
+ valuelen)) {
+ obfuscate_parent_pointer(name, value, valuelen);
+ } else if (want_obfuscate_attr(entry->flags, name,
+ local->namelen, value,
+ valuelen)) {
generate_obfuscated_name(0, local->namelen,
- &local->nameval[0]);
- memset(&local->nameval[local->namelen], 'v',
- be16_to_cpu(local->valuelen));
+ name);
+ memset(value, 'v', valuelen);
}
/* zero from end of nameval[] to next name start */
nlen = local->namelen;
@@ -3166,6 +3456,7 @@ metadump_f(
pop_cur();
out:
free(metablock);
+ remaptable_clear();
return 0;
}
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index 092934935..11bde3073 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -143,6 +143,8 @@
#define xfs_parent_finish libxfs_parent_finish
#define xfs_parent_irec_from_disk libxfs_parent_irec_from_disk
#define xfs_parent_start libxfs_parent_start
+#define xfs_parent_namecheck libxfs_parent_namecheck
+#define xfs_parent_valuecheck libxfs_parent_valuecheck
#define xfs_perag_get libxfs_perag_get
#define xfs_perag_put libxfs_perag_put
#define xfs_prealloc_blocks libxfs_prealloc_blocks
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 26/32] xfs_db: hoist name obfuscation code out of metadump.c
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (24 preceding siblings ...)
2023-04-06 19:38 ` [PATCH 25/32] xfs_db: obfuscate dirent and parent pointer names consistently Darrick J. Wong
@ 2023-04-06 19:38 ` Darrick J. Wong
2023-04-06 19:38 ` [PATCH 27/32] xfs_db: create dirents and xattrs with colliding names Darrick J. Wong
` (5 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:38 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
We want to create a debugger command that will create obfuscated names
for directory and xattr names, so hoist the name obfuscation code into a
separate file.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
db/Makefile | 2
db/metadump.c | 343 -------------------------------------------------------
db/obfuscate.c | 348 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
db/obfuscate.h | 16 +++
4 files changed, 366 insertions(+), 343 deletions(-)
create mode 100644 db/obfuscate.c
create mode 100644 db/obfuscate.h
diff --git a/db/Makefile b/db/Makefile
index b2e011745..2f95f6707 100644
--- a/db/Makefile
+++ b/db/Makefile
@@ -13,7 +13,7 @@ HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \
flist.h fprint.h frag.h freesp.h hash.h help.h init.h inode.h input.h \
io.h logformat.h malloc.h metadump.h output.h print.h quit.h sb.h \
sig.h strvec.h text.h type.h write.h attrset.h symlink.h fsmap.h \
- fuzz.h
+ fuzz.h obfuscate.h
CFILES = $(HFILES:.h=.c) btdump.c btheight.c convert.c info.c namei.c \
timelimit.c
LSRCFILES = xfs_admin.sh xfs_ncheck.sh xfs_metadump.sh
diff --git a/db/metadump.c b/db/metadump.c
index b413ef5b3..e1b707898 100644
--- a/db/metadump.c
+++ b/db/metadump.c
@@ -19,6 +19,7 @@
#include "faddr.h"
#include "field.h"
#include "dir2.h"
+#include "obfuscate.h"
#undef REMAP_DEBUG
@@ -849,19 +850,6 @@ remaptable_add(
return ent;
}
-#define is_invalid_char(c) ((c) == '/' || (c) == '\0')
-#define rol32(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
-
-static inline unsigned char
-random_filename_char(void)
-{
- static unsigned char filename_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789-_";
-
- return filename_alphabet[random() % (sizeof filename_alphabet - 1)];
-}
-
#define ORPHANAGE "lost+found"
#define ORPHANAGE_LEN (sizeof (ORPHANAGE) - 1)
@@ -921,335 +909,6 @@ in_lost_found(
return slen == namelen && !memcmp(name, s, namelen);
}
-/*
- * Given a name and its hash value, massage the name in such a way
- * that the result is another name of equal length which shares the
- * same hash value.
- */
-static void
-obfuscate_name(
- xfs_dahash_t hash,
- size_t name_len,
- unsigned char *name)
-{
- unsigned char *newp = name;
- int i;
- xfs_dahash_t new_hash = 0;
- unsigned char *first;
- unsigned char high_bit;
- int shift;
-
- /*
- * Our obfuscation algorithm requires at least 5-character
- * names, so don't bother if the name is too short. We
- * work backward from a hash value to determine the last
- * five bytes in a name required to produce a new name
- * with the same hash.
- */
- if (name_len < 5)
- return;
-
- /*
- * The beginning of the obfuscated name can be pretty much
- * anything, so fill it in with random characters.
- * Accumulate its new hash value as we go.
- */
- for (i = 0; i < name_len - 5; i++) {
- *newp = random_filename_char();
- new_hash = *newp ^ rol32(new_hash, 7);
- newp++;
- }
-
- /*
- * Compute which five bytes need to be used at the end of
- * the name so the hash of the obfuscated name is the same
- * as the hash of the original. If any result in an invalid
- * character, flip a bit and arrange for a corresponding bit
- * in a neighboring byte to be flipped as well. For the
- * last byte, the "neighbor" to change is the first byte
- * we're computing here.
- */
- new_hash = rol32(new_hash, 3) ^ hash;
-
- first = newp;
- high_bit = 0;
- for (shift = 28; shift >= 0; shift -= 7) {
- *newp = (new_hash >> shift & 0x7f) ^ high_bit;
- if (is_invalid_char(*newp)) {
- *newp ^= 1;
- high_bit = 0x80;
- } else
- high_bit = 0;
- ASSERT(!is_invalid_char(*newp));
- newp++;
- }
-
- /*
- * If we flipped a bit on the last byte, we need to fix up
- * the matching bit in the first byte. The result will
- * be a valid character, because we know that first byte
- * has 0's in its upper four bits (it was produced by a
- * 28-bit right-shift of a 32-bit unsigned value).
- */
- if (high_bit) {
- *first ^= 0x10;
- ASSERT(!is_invalid_char(*first));
- }
- ASSERT(libxfs_da_hashname(name, name_len) == hash);
-}
-
-/*
- * Flip a bit in each of two bytes at the end of the given name.
- * This is used in generating a series of alternate names to be used
- * in the event a duplicate is found.
- *
- * The bits flipped are selected such that they both affect the same
- * bit in the name's computed hash value, so flipping them both will
- * preserve the hash.
- *
- * The following diagram aims to show the portion of a computed
- * hash that a given byte of a name affects.
- *
- * 31 28 24 21 14 8 7 3 0
- * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
- * hash: | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
- * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
- * last-4 ->| |<-- last-2 --->| |<--- last ---->|
- * |<-- last-3 --->| |<-- last-1 --->| |<- last-4
- * |<-- last-7 --->| |<-- last-5 --->|
- * |<-- last-8 --->| |<-- last-6 --->|
- * . . . and so on
- *
- * The last byte of the name directly affects the low-order byte of
- * the hash. The next-to-last affects bits 7-14, the next one back
- * affects bits 14-21, and so on. The effect wraps around when it
- * goes beyond the top of the hash (as happens for byte last-4).
- *
- * Bits that are flipped together "overlap" on the hash value. As
- * an example of overlap, the last two bytes both affect bit 7 in
- * the hash. That pair of bytes (and their overlapping bits) can be
- * used for this "flip bit" operation (it's the first pair tried,
- * actually).
- *
- * A table defines overlapping pairs--the bytes involved and bits
- * within them--that can be used this way. The byte offset is
- * relative to a starting point within the name, which will be set
- * to affect the bytes at the end of the name. The function is
- * called with a "bitseq" value which indicates which bit flip is
- * desired, and this translates directly into selecting which entry
- * in the bit_to_flip[] table to apply.
- *
- * The function returns 1 if the operation was successful. It
- * returns 0 if the result produced a character that's not valid in
- * a name (either '/' or a '\0'). Finally, it returns -1 if the bit
- * sequence number is beyond what is supported for a name of this
- * length.
- *
- * Discussion
- * ----------
- * (Also see the discussion above find_alternate(), below.)
- *
- * In order to make this function work for any length name, the
- * table is ordered by increasing byte offset, so that the earliest
- * entries can apply to the shortest strings. This way all names
- * are done consistently.
- *
- * When bit flips occur, they can convert printable characters
- * into non-printable ones. In an effort to reduce the impact of
- * this, the first bit flips are chosen to affect bytes the end of
- * the name (and furthermore, toward the low bits of a byte). Those
- * bytes are often non-printable anyway because of the way they are
- * initially selected by obfuscate_name()). This is accomplished,
- * using later table entries first.
- *
- * Each row in the table doubles the number of alternates that
- * can be generated. A two-byte name is limited to using only
- * the first row, so it's possible to generate two alternates
- * (the original name, plus the alternate produced by flipping
- * the one pair of bits). In a 5-byte name, the effect of the
- * first byte overlaps the last by 4 its, and there are 8 bits
- * to flip, allowing for 256 possible alternates.
- *
- * Short names (less than 5 bytes) are never even obfuscated, so for
- * such names the relatively small number of alternates should never
- * really be a problem.
- *
- * Long names (more than 6 bytes, say) are not likely to exhaust
- * the number of available alternates. In fact, the table could
- * probably have stopped at 8 entries, on the assumption that 256
- * alternates should be enough for most any situation. The entries
- * beyond those are present mostly for demonstration of how it could
- * be populated with more entries, should it ever be necessary to do
- * so.
- */
-static int
-flip_bit(
- size_t name_len,
- unsigned char *name,
- uint32_t bitseq)
-{
- int index;
- size_t offset;
- unsigned char *p0, *p1;
- unsigned char m0, m1;
- struct {
- int byte; /* Offset from start within name */
- unsigned char bit; /* Bit within that byte */
- } bit_to_flip[][2] = { /* Sorted by second entry's byte */
- { { 0, 0 }, { 1, 7 } }, /* Each row defines a pair */
- { { 1, 0 }, { 2, 7 } }, /* of bytes and a bit within */
- { { 2, 0 }, { 3, 7 } }, /* each byte. Each bit in */
- { { 0, 4 }, { 4, 0 } }, /* a pair affects the same */
- { { 0, 5 }, { 4, 1 } }, /* bit in the hash, so flipping */
- { { 0, 6 }, { 4, 2 } }, /* both will change the name */
- { { 0, 7 }, { 4, 3 } }, /* while preserving the hash. */
- { { 3, 0 }, { 4, 7 } },
- { { 0, 0 }, { 5, 3 } }, /* The first entry's byte offset */
- { { 0, 1 }, { 5, 4 } }, /* must be less than the second. */
- { { 0, 2 }, { 5, 5 } },
- { { 0, 3 }, { 5, 6 } }, /* The table can be extended to */
- { { 0, 4 }, { 5, 7 } }, /* an arbitrary number of entries */
- { { 4, 0 }, { 5, 7 } }, /* but there's not much point. */
- /* . . . */
- };
-
- /* Find the first entry *not* usable for name of this length */
-
- for (index = 0; index < ARRAY_SIZE(bit_to_flip); index++)
- if (bit_to_flip[index][1].byte >= name_len)
- break;
-
- /*
- * Back up to the last usable entry. If that number is
- * smaller than the bit sequence number, inform the caller
- * that nothing this large (or larger) will work.
- */
- if (bitseq > --index)
- return -1;
-
- /*
- * We will be switching bits at the end of name, with a
- * preference for affecting the last bytes first. Compute
- * where in the name we'll start applying the changes.
- */
- offset = name_len - (bit_to_flip[index][1].byte + 1);
- index -= bitseq; /* Use later table entries first */
-
- p0 = name + offset + bit_to_flip[index][0].byte;
- p1 = name + offset + bit_to_flip[index][1].byte;
- m0 = 1 << bit_to_flip[index][0].bit;
- m1 = 1 << bit_to_flip[index][1].bit;
-
- /* Only change the bytes if it produces valid characters */
-
- if (is_invalid_char(*p0 ^ m0) || is_invalid_char(*p1 ^ m1))
- return 0;
-
- *p0 ^= m0;
- *p1 ^= m1;
-
- return 1;
-}
-
-/*
- * This function generates a well-defined sequence of "alternate"
- * names for a given name. An alternate is a name having the same
- * length and same hash value as the original name. This is needed
- * because the algorithm produces only one obfuscated name to use
- * for a given original name, and it's possible that result matches
- * a name already seen. This function checks for this, and if it
- * occurs, finds another suitable obfuscated name to use.
- *
- * Each bit in the binary representation of the sequence number is
- * used to select one possible "bit flip" operation to perform on
- * the name. So for example:
- * seq = 0: selects no bits to flip
- * seq = 1: selects the 0th bit to flip
- * seq = 2: selects the 1st bit to flip
- * seq = 3: selects the 0th and 1st bit to flip
- * ... and so on.
- *
- * The flip_bit() function takes care of the details of the bit
- * flipping within the name. Note that the "1st bit" in this
- * context is a bit sequence number; i.e. it doesn't necessarily
- * mean bit 0x02 will be changed.
- *
- * If a valid name (one that contains no '/' or '\0' characters) is
- * produced by this process for the given sequence number, this
- * function returns 1. If the result is not valid, it returns 0.
- * Returns -1 if the sequence number is beyond the the maximum for
- * names of the given length.
- *
- *
- * Discussion
- * ----------
- * The number of alternates available for a given name is dependent
- * on its length. A "bit flip" involves inverting two bits in
- * a name--the two bits being selected such that their values
- * affect the name's hash value in the same way. Alternates are
- * thus generated by inverting the value of pairs of such
- * "overlapping" bits in the original name. Each byte after the
- * first in a name adds at least one bit of overlap to work with.
- * (See comments above flip_bit() for more discussion on this.)
- *
- * So the number of alternates is dependent on the number of such
- * overlapping bits in a name. If there are N bit overlaps, there
- * 2^N alternates for that hash value.
- *
- * Here are the number of overlapping bits available for generating
- * alternates for names of specific lengths:
- * 1 0 (must have 2 bytes to have any overlap)
- * 2 1 One bit overlaps--so 2 possible alternates
- * 3 2 Two bits overlap--so 4 possible alternates
- * 4 4 Three bits overlap, so 2^3 alternates
- * 5 8 8 bits overlap (due to wrapping), 256 alternates
- * 6 18 2^18 alternates
- * 7 28 2^28 alternates
- * ...
- * It's clear that the number of alternates grows very quickly with
- * the length of the name. But note that the set of alternates
- * includes invalid names. And for certain (contrived) names, the
- * number of valid names is a fairly small fraction of the total
- * number of alternates.
- *
- * The main driver for this infrastructure for coming up with
- * alternate names is really related to names 5 (or possibly 6)
- * bytes in length. 5-byte obfuscated names contain no randomly-
- * generated bytes in them, and the chance of an obfuscated name
- * matching an already-seen name is too high to just ignore. This
- * methodical selection of alternates ensures we don't produce
- * duplicate names unless we have exhausted our options.
- */
-static int
-find_alternate(
- size_t name_len,
- unsigned char *name,
- uint32_t seq)
-{
- uint32_t bitseq = 0;
- uint32_t bits = seq;
-
- if (!seq)
- return 1; /* alternate 0 is the original name */
- if (name_len < 2) /* Must have 2 bytes to flip */
- return -1;
-
- for (bitseq = 0; bits; bitseq++) {
- uint32_t mask = 1 << bitseq;
- int fb;
-
- if (!(bits & mask))
- continue;
-
- fb = flip_bit(name_len, name, bitseq);
- if (fb < 1)
- return fb ? -1 : 0;
- bits ^= mask;
- }
-
- return 1;
-}
-
/*
* Look up the given name in the name table. If it is already
* present, iterate through a well-defined sequence of alternate
diff --git a/db/obfuscate.c b/db/obfuscate.c
new file mode 100644
index 000000000..ae38cb9d0
--- /dev/null
+++ b/db/obfuscate.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2007, 2011 SGI
+ * All Rights Reserved.
+ */
+#include "libxfs.h"
+#include "obfuscate.h"
+
+static inline unsigned char
+random_filename_char(void)
+{
+ static unsigned char filename_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789-_";
+
+ return filename_alphabet[random() % (sizeof filename_alphabet - 1)];
+}
+
+#define rol32(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
+
+/*
+ * Given a name and its hash value, massage the name in such a way
+ * that the result is another name of equal length which shares the
+ * same hash value.
+ */
+void
+obfuscate_name(
+ xfs_dahash_t hash,
+ size_t name_len,
+ unsigned char *name)
+{
+ unsigned char *newp = name;
+ int i;
+ xfs_dahash_t new_hash = 0;
+ unsigned char *first;
+ unsigned char high_bit;
+ int shift;
+
+ /*
+ * Our obfuscation algorithm requires at least 5-character
+ * names, so don't bother if the name is too short. We
+ * work backward from a hash value to determine the last
+ * five bytes in a name required to produce a new name
+ * with the same hash.
+ */
+ if (name_len < 5)
+ return;
+
+ /*
+ * The beginning of the obfuscated name can be pretty much
+ * anything, so fill it in with random characters.
+ * Accumulate its new hash value as we go.
+ */
+ for (i = 0; i < name_len - 5; i++) {
+ *newp = random_filename_char();
+ new_hash = *newp ^ rol32(new_hash, 7);
+ newp++;
+ }
+
+ /*
+ * Compute which five bytes need to be used at the end of
+ * the name so the hash of the obfuscated name is the same
+ * as the hash of the original. If any result in an invalid
+ * character, flip a bit and arrange for a corresponding bit
+ * in a neighboring byte to be flipped as well. For the
+ * last byte, the "neighbor" to change is the first byte
+ * we're computing here.
+ */
+ new_hash = rol32(new_hash, 3) ^ hash;
+
+ first = newp;
+ high_bit = 0;
+ for (shift = 28; shift >= 0; shift -= 7) {
+ *newp = (new_hash >> shift & 0x7f) ^ high_bit;
+ if (is_invalid_char(*newp)) {
+ *newp ^= 1;
+ high_bit = 0x80;
+ } else
+ high_bit = 0;
+ ASSERT(!is_invalid_char(*newp));
+ newp++;
+ }
+
+ /*
+ * If we flipped a bit on the last byte, we need to fix up
+ * the matching bit in the first byte. The result will
+ * be a valid character, because we know that first byte
+ * has 0's in its upper four bits (it was produced by a
+ * 28-bit right-shift of a 32-bit unsigned value).
+ */
+ if (high_bit) {
+ *first ^= 0x10;
+ ASSERT(!is_invalid_char(*first));
+ }
+ ASSERT(libxfs_da_hashname(name, name_len) == hash);
+}
+
+/*
+ * Flip a bit in each of two bytes at the end of the given name.
+ * This is used in generating a series of alternate names to be used
+ * in the event a duplicate is found.
+ *
+ * The bits flipped are selected such that they both affect the same
+ * bit in the name's computed hash value, so flipping them both will
+ * preserve the hash.
+ *
+ * The following diagram aims to show the portion of a computed
+ * hash that a given byte of a name affects.
+ *
+ * 31 28 24 21 14 8 7 3 0
+ * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ * hash: | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+ * +-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-|-+-+-+-+-+-+-+-+
+ * last-4 ->| |<-- last-2 --->| |<--- last ---->|
+ * |<-- last-3 --->| |<-- last-1 --->| |<- last-4
+ * |<-- last-7 --->| |<-- last-5 --->|
+ * |<-- last-8 --->| |<-- last-6 --->|
+ * . . . and so on
+ *
+ * The last byte of the name directly affects the low-order byte of
+ * the hash. The next-to-last affects bits 7-14, the next one back
+ * affects bits 14-21, and so on. The effect wraps around when it
+ * goes beyond the top of the hash (as happens for byte last-4).
+ *
+ * Bits that are flipped together "overlap" on the hash value. As
+ * an example of overlap, the last two bytes both affect bit 7 in
+ * the hash. That pair of bytes (and their overlapping bits) can be
+ * used for this "flip bit" operation (it's the first pair tried,
+ * actually).
+ *
+ * A table defines overlapping pairs--the bytes involved and bits
+ * within them--that can be used this way. The byte offset is
+ * relative to a starting point within the name, which will be set
+ * to affect the bytes at the end of the name. The function is
+ * called with a "bitseq" value which indicates which bit flip is
+ * desired, and this translates directly into selecting which entry
+ * in the bit_to_flip[] table to apply.
+ *
+ * The function returns 1 if the operation was successful. It
+ * returns 0 if the result produced a character that's not valid in
+ * a name (either '/' or a '\0'). Finally, it returns -1 if the bit
+ * sequence number is beyond what is supported for a name of this
+ * length.
+ *
+ * Discussion
+ * ----------
+ * (Also see the discussion above find_alternate(), below.)
+ *
+ * In order to make this function work for any length name, the
+ * table is ordered by increasing byte offset, so that the earliest
+ * entries can apply to the shortest strings. This way all names
+ * are done consistently.
+ *
+ * When bit flips occur, they can convert printable characters
+ * into non-printable ones. In an effort to reduce the impact of
+ * this, the first bit flips are chosen to affect bytes the end of
+ * the name (and furthermore, toward the low bits of a byte). Those
+ * bytes are often non-printable anyway because of the way they are
+ * initially selected by obfuscate_name()). This is accomplished,
+ * using later table entries first.
+ *
+ * Each row in the table doubles the number of alternates that
+ * can be generated. A two-byte name is limited to using only
+ * the first row, so it's possible to generate two alternates
+ * (the original name, plus the alternate produced by flipping
+ * the one pair of bits). In a 5-byte name, the effect of the
+ * first byte overlaps the last by 4 its, and there are 8 bits
+ * to flip, allowing for 256 possible alternates.
+ *
+ * Short names (less than 5 bytes) are never even obfuscated, so for
+ * such names the relatively small number of alternates should never
+ * really be a problem.
+ *
+ * Long names (more than 6 bytes, say) are not likely to exhaust
+ * the number of available alternates. In fact, the table could
+ * probably have stopped at 8 entries, on the assumption that 256
+ * alternates should be enough for most any situation. The entries
+ * beyond those are present mostly for demonstration of how it could
+ * be populated with more entries, should it ever be necessary to do
+ * so.
+ */
+static int
+flip_bit(
+ size_t name_len,
+ unsigned char *name,
+ uint32_t bitseq)
+{
+ int index;
+ size_t offset;
+ unsigned char *p0, *p1;
+ unsigned char m0, m1;
+ struct {
+ int byte; /* Offset from start within name */
+ unsigned char bit; /* Bit within that byte */
+ } bit_to_flip[][2] = { /* Sorted by second entry's byte */
+ { { 0, 0 }, { 1, 7 } }, /* Each row defines a pair */
+ { { 1, 0 }, { 2, 7 } }, /* of bytes and a bit within */
+ { { 2, 0 }, { 3, 7 } }, /* each byte. Each bit in */
+ { { 0, 4 }, { 4, 0 } }, /* a pair affects the same */
+ { { 0, 5 }, { 4, 1 } }, /* bit in the hash, so flipping */
+ { { 0, 6 }, { 4, 2 } }, /* both will change the name */
+ { { 0, 7 }, { 4, 3 } }, /* while preserving the hash. */
+ { { 3, 0 }, { 4, 7 } },
+ { { 0, 0 }, { 5, 3 } }, /* The first entry's byte offset */
+ { { 0, 1 }, { 5, 4 } }, /* must be less than the second. */
+ { { 0, 2 }, { 5, 5 } },
+ { { 0, 3 }, { 5, 6 } }, /* The table can be extended to */
+ { { 0, 4 }, { 5, 7 } }, /* an arbitrary number of entries */
+ { { 4, 0 }, { 5, 7 } }, /* but there's not much point. */
+ /* . . . */
+ };
+
+ /* Find the first entry *not* usable for name of this length */
+
+ for (index = 0; index < ARRAY_SIZE(bit_to_flip); index++)
+ if (bit_to_flip[index][1].byte >= name_len)
+ break;
+
+ /*
+ * Back up to the last usable entry. If that number is
+ * smaller than the bit sequence number, inform the caller
+ * that nothing this large (or larger) will work.
+ */
+ if (bitseq > --index)
+ return -1;
+
+ /*
+ * We will be switching bits at the end of name, with a
+ * preference for affecting the last bytes first. Compute
+ * where in the name we'll start applying the changes.
+ */
+ offset = name_len - (bit_to_flip[index][1].byte + 1);
+ index -= bitseq; /* Use later table entries first */
+
+ p0 = name + offset + bit_to_flip[index][0].byte;
+ p1 = name + offset + bit_to_flip[index][1].byte;
+ m0 = 1 << bit_to_flip[index][0].bit;
+ m1 = 1 << bit_to_flip[index][1].bit;
+
+ /* Only change the bytes if it produces valid characters */
+
+ if (is_invalid_char(*p0 ^ m0) || is_invalid_char(*p1 ^ m1))
+ return 0;
+
+ *p0 ^= m0;
+ *p1 ^= m1;
+
+ return 1;
+}
+
+/*
+ * This function generates a well-defined sequence of "alternate"
+ * names for a given name. An alternate is a name having the same
+ * length and same hash value as the original name. This is needed
+ * because the algorithm produces only one obfuscated name to use
+ * for a given original name, and it's possible that result matches
+ * a name already seen. This function checks for this, and if it
+ * occurs, finds another suitable obfuscated name to use.
+ *
+ * Each bit in the binary representation of the sequence number is
+ * used to select one possible "bit flip" operation to perform on
+ * the name. So for example:
+ * seq = 0: selects no bits to flip
+ * seq = 1: selects the 0th bit to flip
+ * seq = 2: selects the 1st bit to flip
+ * seq = 3: selects the 0th and 1st bit to flip
+ * ... and so on.
+ *
+ * The flip_bit() function takes care of the details of the bit
+ * flipping within the name. Note that the "1st bit" in this
+ * context is a bit sequence number; i.e. it doesn't necessarily
+ * mean bit 0x02 will be changed.
+ *
+ * If a valid name (one that contains no '/' or '\0' characters) is
+ * produced by this process for the given sequence number, this
+ * function returns 1. If the result is not valid, it returns 0.
+ * Returns -1 if the sequence number is beyond the the maximum for
+ * names of the given length.
+ *
+ *
+ * Discussion
+ * ----------
+ * The number of alternates available for a given name is dependent
+ * on its length. A "bit flip" involves inverting two bits in
+ * a name--the two bits being selected such that their values
+ * affect the name's hash value in the same way. Alternates are
+ * thus generated by inverting the value of pairs of such
+ * "overlapping" bits in the original name. Each byte after the
+ * first in a name adds at least one bit of overlap to work with.
+ * (See comments above flip_bit() for more discussion on this.)
+ *
+ * So the number of alternates is dependent on the number of such
+ * overlapping bits in a name. If there are N bit overlaps, there
+ * 2^N alternates for that hash value.
+ *
+ * Here are the number of overlapping bits available for generating
+ * alternates for names of specific lengths:
+ * 1 0 (must have 2 bytes to have any overlap)
+ * 2 1 One bit overlaps--so 2 possible alternates
+ * 3 2 Two bits overlap--so 4 possible alternates
+ * 4 4 Three bits overlap, so 2^3 alternates
+ * 5 8 8 bits overlap (due to wrapping), 256 alternates
+ * 6 18 2^18 alternates
+ * 7 28 2^28 alternates
+ * ...
+ * It's clear that the number of alternates grows very quickly with
+ * the length of the name. But note that the set of alternates
+ * includes invalid names. And for certain (contrived) names, the
+ * number of valid names is a fairly small fraction of the total
+ * number of alternates.
+ *
+ * The main driver for this infrastructure for coming up with
+ * alternate names is really related to names 5 (or possibly 6)
+ * bytes in length. 5-byte obfuscated names contain no randomly-
+ * generated bytes in them, and the chance of an obfuscated name
+ * matching an already-seen name is too high to just ignore. This
+ * methodical selection of alternates ensures we don't produce
+ * duplicate names unless we have exhausted our options.
+ */
+int
+find_alternate(
+ size_t name_len,
+ unsigned char *name,
+ uint32_t seq)
+{
+ uint32_t bitseq = 0;
+ uint32_t bits = seq;
+
+ if (!seq)
+ return 1; /* alternate 0 is the original name */
+ if (name_len < 2) /* Must have 2 bytes to flip */
+ return -1;
+
+ for (bitseq = 0; bits; bitseq++) {
+ uint32_t mask = 1 << bitseq;
+ int fb;
+
+ if (!(bits & mask))
+ continue;
+
+ fb = flip_bit(name_len, name, bitseq);
+ if (fb < 1)
+ return fb ? -1 : 0;
+ bits ^= mask;
+ }
+
+ return 1;
+}
diff --git a/db/obfuscate.h b/db/obfuscate.h
new file mode 100644
index 000000000..bba3c286b
--- /dev/null
+++ b/db/obfuscate.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2007, 2011 SGI
+ * All Rights Reserved.
+ */
+#ifndef __DB_OBFUSCATE_H__
+#define __DB_OBFUSCATE_H__
+
+/* Routines to obfuscate directory filenames and xattr names. */
+
+#define is_invalid_char(c) ((c) == '/' || (c) == '\0')
+
+void obfuscate_name(xfs_dahash_t hash, size_t name_len, unsigned char *name);
+int find_alternate(size_t name_len, unsigned char *name, uint32_t seq);
+
+#endif /* __DB_OBFUSCATE_H__ */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 27/32] xfs_db: create dirents and xattrs with colliding names
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (25 preceding siblings ...)
2023-04-06 19:38 ` [PATCH 26/32] xfs_db: hoist name obfuscation code out of metadump.c Darrick J. Wong
@ 2023-04-06 19:38 ` Darrick J. Wong
2023-04-06 19:38 ` [PATCH 28/32] xfs_db: add a parents command to list the parents of a file Darrick J. Wong
` (4 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:38 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Create a new debugger command that will create dirent and xattr names
that induce dahash collisions.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
db/hash.c | 374 +++++++++++++++++++++++++++++++++++++++++++++++++++++
man/man8/xfs_db.8 | 31 ++++
2 files changed, 405 insertions(+)
diff --git a/db/hash.c b/db/hash.c
index 68c53e7f9..9fa5f054e 100644
--- a/db/hash.c
+++ b/db/hash.c
@@ -5,12 +5,15 @@
*/
#include "libxfs.h"
+#include "init.h"
#include "addr.h"
#include "command.h"
#include "type.h"
#include "io.h"
#include "output.h"
#include "hash.h"
+#include "obfuscate.h"
+#include <sys/xattr.h>
static int hash_f(int argc, char **argv);
static void hash_help(void);
@@ -46,8 +49,379 @@ hash_f(
return 0;
}
+static void
+hashcoll_help(void)
+{
+ printf(_(
+"\n"
+" Generate obfuscated variants of the provided name. Each variant will have\n"
+" the same dahash value. Names are written to stdout with a NULL separating\n"
+" each name.\n"
+"\n"
+" -a -- create extended attributes.\n"
+" -i -- read standard input for the name, up to %d bytes.\n"
+" -n -- create this many names.\n"
+" -p -- create directory entries or extended attributes in this file.\n"
+" -s -- seed the rng with this value.\n"
+"\n"),
+ MAXNAMELEN - 1);
+}
+
+struct name_dup {
+ struct name_dup *next;
+ uint32_t crc;
+ uint8_t namelen;
+ uint8_t name[];
+};
+
+static inline size_t
+name_dup_sizeof(
+ unsigned int namelen)
+{
+ return sizeof(struct name_dup) + namelen;
+}
+
+#define MAX_DUP_TABLE_BUCKETS (1048575)
+
+struct dup_table {
+ unsigned int nr_buckets;
+ struct name_dup *buckets[];
+};
+
+static inline size_t
+dup_table_sizeof(
+ unsigned int nr_buckets)
+{
+ return sizeof(struct dup_table) +
+ (nr_buckets * sizeof(struct name_dup *));
+}
+
+static int
+dup_table_alloc(
+ unsigned long nr_names,
+ struct dup_table **tabp)
+{
+ struct dup_table *t;
+
+ *tabp = NULL;
+
+ if (nr_names == 1)
+ return 0;
+
+ nr_names = min(MAX_DUP_TABLE_BUCKETS, nr_names);
+ t = calloc(1, dup_table_sizeof(nr_names));
+ if (!t)
+ return ENOMEM;
+
+ t->nr_buckets = nr_names;
+ *tabp = t;
+ return 0;
+}
+
+static void
+dup_table_free(
+ struct dup_table *tab)
+{
+ struct name_dup *ent, *next;
+ unsigned int i;
+
+ if (!tab)
+ return;
+
+ for (i = 0; i < tab->nr_buckets; i++) {
+ ent = tab->buckets[i];
+
+ while (ent) {
+ next = ent->next;
+ free(ent);
+ ent = next;
+ }
+ }
+ free(tab);
+}
+
+static struct name_dup *
+dup_table_find(
+ struct dup_table *tab,
+ unsigned char *name,
+ size_t namelen)
+{
+ struct name_dup *ent;
+ uint32_t crc = crc32c(~0, name, namelen);
+
+ ent = tab->buckets[crc % tab->nr_buckets];
+ while (ent) {
+ if (ent->crc == crc &&
+ ent->namelen == namelen &&
+ !memcmp(ent->name, name, namelen))
+ return ent;
+
+ ent = ent->next;
+ }
+
+ return NULL;
+}
+
+static int
+dup_table_store(
+ struct dup_table *tab,
+ unsigned char *name,
+ size_t namelen)
+{
+ struct name_dup *dup;
+ uint32_t seq = 1;
+
+ ASSERT(namelen < MAXNAMELEN);
+
+ while ((dup = dup_table_find(tab, name, namelen)) != NULL) {
+ int ret;
+
+ do {
+ ret = find_alternate(namelen, name, seq++);
+ } while (ret == 0);
+ if (ret < 0)
+ return EEXIST;
+ }
+
+ dup = malloc(name_dup_sizeof(namelen));
+ if (!dup)
+ return ENOMEM;
+
+ dup->crc = crc32c(~0, name, namelen);
+ dup->namelen = namelen;
+ memcpy(dup->name, name, namelen);
+ dup->next = tab->buckets[dup->crc % tab->nr_buckets];
+
+ tab->buckets[dup->crc % tab->nr_buckets] = dup;
+ return 0;
+}
+
+static int
+collide_dirents(
+ unsigned long nr,
+ const unsigned char *name,
+ size_t namelen,
+ int fd)
+{
+ struct xfs_name dname = {
+ .name = name,
+ .len = namelen,
+ };
+ unsigned char direntname[MAXNAMELEN + 1];
+ struct dup_table *tab = NULL;
+ xfs_dahash_t old_hash;
+ unsigned long i;
+ int error = 0;
+
+ old_hash = libxfs_dir2_hashname(mp, &dname);
+
+ if (fd >= 0) {
+ int newfd;
+
+ /*
+ * User passed in a fd, so we'll use the directory to detect
+ * duplicate names. First create the name that we are passed
+ * in; the new names will be hardlinks to the first file.
+ */
+ newfd = openat(fd, name, O_CREAT, 0600);
+ if (newfd < 0)
+ return errno;
+ close(newfd);
+ } else if (nr > 1) {
+ /*
+ * Track every name we create so that we don't emit duplicates.
+ */
+ error = dup_table_alloc(nr, &tab);
+ if (error)
+ return error;
+ }
+
+ for (i = 0; i < nr; i++) {
+ strncpy(direntname, name, MAXNAMELEN);
+ obfuscate_name(old_hash, namelen, direntname);
+
+ if (fd >= 0) {
+ error = linkat(fd, name, fd, direntname, 0);
+ if (error && errno != EEXIST)
+ return errno;
+
+ /* don't print names to stdout */
+ continue;
+ } else if (tab) {
+ error = dup_table_store(tab, direntname, namelen);
+ if (error)
+ break;
+ }
+
+ printf("%s%c", direntname, 0);
+ }
+
+ dup_table_free(tab);
+ return error;
+}
+
+static int
+collide_xattrs(
+ unsigned long nr,
+ const unsigned char *name,
+ size_t namelen,
+ int fd)
+{
+ unsigned char xattrname[MAXNAMELEN + 5];
+ struct dup_table *tab = NULL;
+ xfs_dahash_t old_hash;
+ unsigned long i;
+ int error;
+
+ old_hash = libxfs_da_hashname(name, namelen);
+
+ if (fd >= 0) {
+ /*
+ * User passed in a fd, so we'll use the xattr structure to
+ * detect duplicate names. First create the attribute that we
+ * are passed in.
+ */
+ snprintf(xattrname, MAXNAMELEN + 5, "user.%s", name);
+ error = fsetxattr(fd, xattrname, "1", 1, 0);
+ if (error)
+ return errno;
+ } else if (nr > 1) {
+ /*
+ * Track every name we create so that we don't emit duplicates.
+ */
+ error = dup_table_alloc(nr, &tab);
+ if (error)
+ return error;
+ }
+
+ for (i = 0; i < nr; i++) {
+ snprintf(xattrname, MAXNAMELEN + 5, "user.%s", name);
+ obfuscate_name(old_hash, namelen, xattrname + 5);
+
+ if (fd >= 0) {
+ error = fsetxattr(fd, xattrname, "1", 1, 0);
+ if (error)
+ return errno;
+
+ /* don't print names to stdout */
+ continue;
+ } else if (tab) {
+ error = dup_table_store(tab, xattrname, namelen + 5);
+ if (error)
+ break;
+ }
+
+ printf("%s%c", xattrname, 0);
+ }
+
+ dup_table_free(tab);
+ return error;
+}
+
+static int
+hashcoll_f(
+ int argc,
+ char **argv)
+{
+ const char *path = NULL;
+ bool read_stdin = false;
+ bool create_xattr = false;
+ unsigned long nr = 1, seed = 0;
+ int fd = -1;
+ int c;
+ int error;
+
+ while ((c = getopt(argc, argv, "ain:p:s:")) != EOF) {
+ switch (c) {
+ case 'a':
+ create_xattr = true;
+ break;
+ case 'i':
+ read_stdin = true;
+ break;
+ case 'n':
+ nr = strtoul(optarg, NULL, 10);
+ break;
+ case 'p':
+ path = optarg;
+ break;
+ case 's':
+ seed = strtoul(optarg, NULL, 10);
+ break;
+ default:
+ exitcode = 1;
+ hashcoll_help();
+ return 0;
+ }
+ }
+
+ if (path) {
+ int oflags = O_RDWR;
+
+ if (!create_xattr)
+ oflags = O_RDONLY | O_DIRECTORY;
+
+ fd = open(path, oflags);
+ if (fd < 0) {
+ perror(path);
+ exitcode = 1;
+ return 0;
+ }
+ }
+
+ if (seed)
+ srandom(seed);
+
+ if (read_stdin) {
+ char buf[MAXNAMELEN];
+ size_t len;
+
+ len = fread(buf, 1, MAXNAMELEN - 1, stdin);
+
+ if (create_xattr)
+ error = collide_xattrs(nr, buf, len, fd);
+ else
+ error = collide_dirents(nr, buf, len, fd);
+ if (error) {
+ printf(_("hashcoll: %s\n"), strerror(error));
+ exitcode = 1;
+ }
+ goto done;
+ }
+
+ for (c = optind; c < argc; c++) {
+ size_t len = strlen(argv[c]);
+
+ if (create_xattr)
+ error = collide_xattrs(nr, argv[c], len, fd);
+ else
+ error = collide_dirents(nr, argv[c], len, fd);
+ if (error) {
+ printf(_("hashcoll: %s\n"), strerror(error));
+ exitcode = 1;
+ }
+ }
+
+done:
+ if (fd >= 0)
+ close(fd);
+ return 0;
+}
+
+static cmdinfo_t hashcoll_cmd = {
+ .name = "hashcoll",
+ .cfunc = hashcoll_f,
+ .argmin = 0,
+ .argmax = -1,
+ .args = N_("[-a] [-s seed] [-n nr] [-p path] -i|names..."),
+ .oneline = N_("create names that produce dahash collisions"),
+ .help = hashcoll_help,
+};
+
void
hash_init(void)
{
add_command(&hash_cmd);
+ add_command(&hashcoll_cmd);
}
+
diff --git a/man/man8/xfs_db.8 b/man/man8/xfs_db.8
index 1a2bb7e98..fde1c5c6c 100644
--- a/man/man8/xfs_db.8
+++ b/man/man8/xfs_db.8
@@ -768,6 +768,37 @@ Prints the hash value of
.I string
using the hash function of the XFS directory and attribute implementation.
.TP
+.BI "hashcoll [-a] [-s seed] [-n " nr "] [-p " path "] -i | " names...
+Create directory entries or extended attributes names that all have the same
+hash value.
+The metadump name obfuscation algorithm is used here.
+Names are written to standard output, with a NULL between each name for use
+with xargs -0.
+.RS 1.0i
+.PD 0
+.TP 0.4i
+.TP 0.4i
+.B \-a
+Create extended attribute names.
+.TP 0.4i
+.B \-i
+Read the first name to create from standard input.
+Up to 255 bytes are read.
+If this option is not specified, first names are taken from the command line.
+.TP 0.4i
+.BI \-n " nr"
+Create this many duplicated names.
+The default is to create one name.
+.TP 0.4i
+.BI \-p " path"
+Create directory entries or extended attributes in this file instead of
+writing the names to standard output.
+.TP 0.4i
+.BI \-s " seed"
+Seed the random number generator with this value.
+.PD
+.RE
+.TP
.BI "help [" command ]
Print help for one or all commands.
.TP
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 28/32] xfs_db: add a parents command to list the parents of a file
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (26 preceding siblings ...)
2023-04-06 19:38 ` [PATCH 27/32] xfs_db: create dirents and xattrs with colliding names Darrick J. Wong
@ 2023-04-06 19:38 ` Darrick J. Wong
2023-04-06 19:39 ` [PATCH 29/32] libxfs: create new files with attr forks if necessary Darrick J. Wong
` (3 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:38 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Create a command to dump the parents of a file.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
db/namei.c | 335 +++++++++++++++++++++++++++++++++++++++++++++++++++++
man/man8/xfs_db.8 | 9 +
2 files changed, 344 insertions(+)
diff --git a/db/namei.c b/db/namei.c
index 063721ca9..98a019d8f 100644
--- a/db/namei.c
+++ b/db/namei.c
@@ -596,6 +596,338 @@ static struct cmdinfo ls_cmd = {
.help = ls_help,
};
+static void
+pptr_emit(
+ struct xfs_mount *mp,
+ const struct xfs_parent_name_irec *irec)
+{
+ struct xfs_name xname = {
+ .name = irec->p_name,
+ .len = irec->p_namelen,
+ };
+ xfs_dahash_t hash;
+ bool good;
+
+ hash = libxfs_dir2_hashname(mp, &xname);
+ good = hash == irec->p_namehash &&
+ libxfs_dir2_namecheck(irec->p_name, irec->p_namelen);
+
+ dbprintf("%18llu:0x%08x 0x%08x:0x%08x %3d %.*s %s\n",
+ irec->p_ino, irec->p_gen, irec->p_namehash, hash,
+ xname.len, xname.len, xname.name,
+ good ? _("(good)") : _("(corrupt)"));
+}
+
+static int
+list_sf_pptrs(
+ struct xfs_inode *ip)
+{
+ struct xfs_parent_name_irec irec;
+ struct xfs_attr_shortform *sf;
+ struct xfs_attr_sf_entry *sfe;
+ unsigned int i;
+
+ sf = (struct xfs_attr_shortform *)ip->i_af.if_u1.if_data;
+ for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) {
+ void *name = sfe->nameval;
+ void *value = &sfe->nameval[sfe->namelen];
+
+ if ((sfe->flags & XFS_ATTR_PARENT) &&
+ libxfs_parent_namecheck(mp, name, sfe->namelen, sfe->flags) &&
+ libxfs_parent_valuecheck(mp, value, sfe->valuelen)) {
+ libxfs_parent_irec_from_disk(&irec, name, value,
+ sfe->valuelen);
+ pptr_emit(mp, &irec);
+ }
+
+ sfe = xfs_attr_sf_nextentry(sfe);
+ }
+
+ return 0;
+}
+
+static void
+list_leaf_pptr_entries(
+ struct xfs_inode *ip,
+ struct xfs_buf *bp)
+{
+ struct xfs_parent_name_irec irec;
+ struct xfs_attr3_icleaf_hdr ichdr;
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_attr_leafblock *leaf = bp->b_addr;
+ struct xfs_attr_leaf_entry *entry;
+ unsigned int i;
+
+ libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, leaf);
+ entry = xfs_attr3_leaf_entryp(leaf);
+
+ for (i = 0; i < ichdr.count; entry++, i++) {
+ struct xfs_attr_leaf_name_local *name_loc;
+ void *value;
+ void *name;
+ unsigned int namelen, valuelen;
+
+ if (!(entry->flags & XFS_ATTR_LOCAL) ||
+ !(entry->flags & XFS_ATTR_PARENT))
+ continue;
+
+ name_loc = xfs_attr3_leaf_name_local(leaf, i);
+ name = name_loc->nameval;
+ namelen = name_loc->namelen;
+ value = &name_loc->nameval[name_loc->namelen];
+ valuelen = be16_to_cpu(name_loc->valuelen);
+
+ if (libxfs_parent_namecheck(mp, name, namelen, entry->flags) &&
+ libxfs_parent_valuecheck(mp, value, valuelen)) {
+ libxfs_parent_irec_from_disk(&irec, name, value,
+ valuelen);
+ pptr_emit(mp, &irec);
+ }
+ }
+}
+
+static int
+list_leaf_pptrs(
+ struct xfs_inode *ip)
+{
+ struct xfs_buf *leaf_bp;
+ int error;
+
+ error = -libxfs_attr3_leaf_read(NULL, ip, 0, &leaf_bp);
+ if (error)
+ return error;
+
+ list_leaf_pptr_entries(ip, leaf_bp);
+ libxfs_trans_brelse(NULL, leaf_bp);
+ return 0;
+}
+
+static int
+find_leftmost_attr_leaf(
+ struct xfs_inode *ip,
+ struct xfs_buf **leaf_bpp)
+{
+ struct xfs_da3_icnode_hdr nodehdr;
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_da_intnode *node;
+ struct xfs_da_node_entry *btree;
+ struct xfs_buf *bp;
+ xfs_dablk_t blkno = 0;
+ unsigned int expected_level = 0;
+ int error;
+
+ for (;;) {
+ uint16_t magic;
+
+ error = -libxfs_da3_node_read(NULL, ip, blkno, &bp,
+ XFS_ATTR_FORK);
+ if (error)
+ return error;
+
+ node = bp->b_addr;
+ magic = be16_to_cpu(node->hdr.info.magic);
+ if (magic == XFS_ATTR_LEAF_MAGIC ||
+ magic == XFS_ATTR3_LEAF_MAGIC)
+ break;
+
+ error = EFSCORRUPTED;
+ if (magic != XFS_DA_NODE_MAGIC &&
+ magic != XFS_DA3_NODE_MAGIC)
+ goto out_buf;
+
+ libxfs_da3_node_hdr_from_disk(mp, &nodehdr, node);
+
+ if (nodehdr.count == 0 || nodehdr.level >= XFS_DA_NODE_MAXDEPTH)
+ goto out_buf;
+
+ /* Check the level from the root node. */
+ if (blkno == 0)
+ expected_level = nodehdr.level - 1;
+ else if (expected_level != nodehdr.level)
+ goto out_buf;
+ else
+ expected_level--;
+
+ /* Find the next level towards the leaves of the dabtree. */
+ btree = nodehdr.btree;
+ blkno = be32_to_cpu(btree->before);
+ libxfs_trans_brelse(NULL, bp);
+ }
+
+ error = EFSCORRUPTED;
+ if (expected_level != 0)
+ goto out_buf;
+
+ *leaf_bpp = bp;
+ return 0;
+
+out_buf:
+ libxfs_trans_brelse(NULL, bp);
+ return error;
+}
+
+static int
+list_node_pptrs(
+ struct xfs_inode *ip)
+{
+ struct xfs_attr3_icleaf_hdr leafhdr;
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_attr_leafblock *leaf;
+ struct xfs_buf *leaf_bp;
+ int error;
+
+ error = find_leftmost_attr_leaf(ip, &leaf_bp);
+ if (error)
+ return error;
+
+ for (;;) {
+ list_leaf_pptr_entries(ip, leaf_bp);
+
+ /* Find the right sibling of this leaf block. */
+ leaf = leaf_bp->b_addr;
+ libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
+ if (leafhdr.forw == 0)
+ goto out_leaf;
+
+ libxfs_trans_brelse(NULL, leaf_bp);
+
+ error = -libxfs_attr3_leaf_read(NULL, ip, leafhdr.forw,
+ &leaf_bp);
+ if (error)
+ return error;
+ }
+
+out_leaf:
+ libxfs_trans_brelse(NULL, leaf_bp);
+ return error;
+}
+
+static int
+list_pptrs(
+ struct xfs_inode *ip)
+{
+ int error;
+
+ if (!libxfs_inode_hasattr(ip))
+ return 0;
+
+ if (ip->i_af.if_format == XFS_DINODE_FMT_LOCAL)
+ return list_sf_pptrs(ip);
+
+ /* attr functions require that the attr fork is loaded */
+ error = -libxfs_iread_extents(NULL, ip, XFS_ATTR_FORK);
+ if (error)
+ return error;
+
+ if (libxfs_attr_is_leaf(ip))
+ return list_leaf_pptrs(ip);
+
+ return list_node_pptrs(ip);
+}
+
+/* If the io cursor points to a file, list its parents. */
+static int
+parent_cur(
+ char *tag)
+{
+ struct xfs_inode *ip;
+ int error = 0;
+
+ if (!xfs_has_parent(mp))
+ return 0;
+
+ if (iocur_top->typ != &typtab[TYP_INODE])
+ return ENOTDIR;
+
+ error = -libxfs_iget(mp, NULL, iocur_top->ino, 0, &ip);
+ if (error)
+ return error;
+
+ /* List the parents of a file. */
+ if (tag)
+ dbprintf(_("%s:\n"), tag);
+
+ error = list_pptrs(ip);
+ if (error)
+ goto rele;
+
+rele:
+ libxfs_irele(ip);
+ return error;
+}
+
+static void
+parent_help(void)
+{
+ dbprintf(_(
+"\n"
+" List the parents of the currently selected file.\n"
+"\n"
+" Parent pointers will be listed in the format:\n"
+" inode_number:inode_gen ondisk_namehash:namehash name_length name\n"
+ ));
+}
+
+static int
+parent_f(
+ int argc,
+ char **argv)
+{
+ int c;
+ int error = 0;
+
+ while ((c = getopt(argc, argv, "")) != -1) {
+ switch (c) {
+ default:
+ ls_help();
+ return 0;
+ }
+ }
+
+ if (optind == argc) {
+ error = parent_cur(NULL);
+ if (error) {
+ dbprintf("%s\n", strerror(error));
+ exitcode = 1;
+ }
+
+ return 0;
+ }
+
+ for (c = optind; c < argc; c++) {
+ push_cur();
+
+ error = path_walk(argv[c]);
+ if (error)
+ goto err_cur;
+
+ error = parent_cur(argv[c]);
+ if (error)
+ goto err_cur;
+
+ pop_cur();
+ }
+
+ return 0;
+err_cur:
+ pop_cur();
+ if (error) {
+ dbprintf("%s: %s\n", argv[c], strerror(error));
+ exitcode = 1;
+ }
+ return 0;
+}
+
+static struct cmdinfo parent_cmd = {
+ .name = "parent",
+ .altname = "pptr",
+ .cfunc = parent_f,
+ .argmin = 0,
+ .argmax = -1,
+ .canpush = 0,
+ .args = "[paths...]",
+ .help = parent_help,
+};
void
namei_init(void)
{
@@ -604,4 +936,7 @@ namei_init(void)
ls_cmd.oneline = _("list directory contents");
add_command(&ls_cmd);
+
+ parent_cmd.oneline = _("list parent pointers");
+ add_command(&parent_cmd);
}
diff --git a/man/man8/xfs_db.8 b/man/man8/xfs_db.8
index fde1c5c6c..02351d1ec 100644
--- a/man/man8/xfs_db.8
+++ b/man/man8/xfs_db.8
@@ -884,6 +884,15 @@ See the
.B print
command.
.TP
+.BI "parent [" paths "]..."
+List the parents of a file.
+If a path resolves to a file, the parents of that file will be listed.
+If no paths are supplied and the IO cursor points at an inode, the parents of
+that file will be listed.
+
+The output format is:
+inode number, inode generation, ondisk namehash, namehash, name length, name.
+.TP
.BI "path " dir_path
Walk the directory tree to an inode using the supplied path.
Absolute and relative paths are supported.
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 29/32] libxfs: create new files with attr forks if necessary
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (27 preceding siblings ...)
2023-04-06 19:38 ` [PATCH 28/32] xfs_db: add a parents command to list the parents of a file Darrick J. Wong
@ 2023-04-06 19:39 ` Darrick J. Wong
2023-04-06 19:39 ` [PATCH 30/32] xfsprogs: Fix default superblock attr bits Darrick J. Wong
` (2 subsequent siblings)
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:39 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Create new files with attr forks if they're going to have parent
pointers. In the next patch we'll fix mkfs to use the same parent
creation functions as the kernel, so we're going to need this.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/init.c | 4 ++++
libxfs/util.c | 14 ++++++++++++++
2 files changed, 18 insertions(+)
diff --git a/libxfs/init.c b/libxfs/init.c
index 59cd547d6..101b77602 100644
--- a/libxfs/init.c
+++ b/libxfs/init.c
@@ -746,14 +746,18 @@ void
libxfs_compute_all_maxlevels(
struct xfs_mount *mp)
{
+ struct xfs_ino_geometry *igeo = M_IGEO(mp);
+
xfs_alloc_compute_maxlevels(mp);
xfs_bmap_compute_maxlevels(mp, XFS_DATA_FORK);
xfs_bmap_compute_maxlevels(mp, XFS_ATTR_FORK);
+ igeo->attr_fork_offset = xfs_bmap_compute_attr_offset(mp);
xfs_ialloc_setup_geometry(mp);
xfs_rmapbt_compute_maxlevels(mp);
xfs_refcountbt_compute_maxlevels(mp);
xfs_agbtree_compute_maxlevels(mp);
+
}
/*
diff --git a/libxfs/util.c b/libxfs/util.c
index 6525f63de..bea5f1c71 100644
--- a/libxfs/util.c
+++ b/libxfs/util.c
@@ -322,6 +322,20 @@ libxfs_init_new_inode(
ASSERT(0);
}
+ /*
+ * If we need to create attributes immediately after allocating the
+ * inode, initialise an empty attribute fork right now. We use the
+ * default fork offset for attributes here as we don't know exactly what
+ * size or how many attributes we might be adding. We can do this
+ * safely here because we know the data fork is completely empty and
+ * this saves us from needing to run a separate transaction to set the
+ * fork offset in the immediate future.
+ */
+ if (xfs_has_parent(tp->t_mountp) && xfs_has_attr(tp->t_mountp)) {
+ ip->i_forkoff = xfs_default_attroffset(ip) >> 3;
+ xfs_ifork_init_attr(ip, XFS_DINODE_FMT_EXTENTS, 0);
+ }
+
/*
* Log the new values stuffed into the inode.
*/
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 30/32] xfsprogs: Fix default superblock attr bits
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (28 preceding siblings ...)
2023-04-06 19:39 ` [PATCH 29/32] libxfs: create new files with attr forks if necessary Darrick J. Wong
@ 2023-04-06 19:39 ` Darrick J. Wong
2023-04-06 19:39 ` [PATCH 31/32] mkfs: Add parent pointers during protofile creation Darrick J. Wong
2023-04-06 19:39 ` [PATCH 32/32] mkfs: enable formatting with parent pointers Darrick J. Wong
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:39 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Recent parent pointer testing discovered that the default attr
configuration has XFS_SB_VERSION2_ATTR2BIT enabled but
XFS_SB_VERSION_ATTRBIT disabled. This is incorrect since
XFS_SB_VERSION2_ATTR2BIT describes the format of the attr where
as XFS_SB_VERSION_ATTRBIT enables or disables attrs. Fix this
by enableing XFS_SB_VERSION_ATTRBIT for either attr version 1 or 2
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
mkfs/xfs_mkfs.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index 4399bf379..f2e683117 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -3266,7 +3266,7 @@ sb_set_features(
sbp->sb_versionnum |= XFS_SB_VERSION_DALIGNBIT;
if (fp->log_version == 2)
sbp->sb_versionnum |= XFS_SB_VERSION_LOGV2BIT;
- if (fp->attr_version == 1)
+ if (fp->attr_version >= 1)
sbp->sb_versionnum |= XFS_SB_VERSION_ATTRBIT;
if (fp->nci)
sbp->sb_versionnum |= XFS_SB_VERSION_BORGBIT;
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 31/32] mkfs: Add parent pointers during protofile creation
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (29 preceding siblings ...)
2023-04-06 19:39 ` [PATCH 30/32] xfsprogs: Fix default superblock attr bits Darrick J. Wong
@ 2023-04-06 19:39 ` Darrick J. Wong
2023-04-06 19:39 ` [PATCH 32/32] mkfs: enable formatting with parent pointers Darrick J. Wong
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:39 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Inodes created from protofile parsing will also need to add the
appropriate parent pointers.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: use xfs_parent_add from libxfs instead of open-coding xfs_attr_set]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
mkfs/proto.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 47 insertions(+), 1 deletion(-)
diff --git a/mkfs/proto.c b/mkfs/proto.c
index ea31cfe5c..d6999b850 100644
--- a/mkfs/proto.c
+++ b/mkfs/proto.c
@@ -378,6 +378,20 @@ newdirectory(
fail(_("directory create error"), error);
}
+static struct xfs_parent_defer *
+newpptr(
+ struct xfs_mount *mp)
+{
+ struct xfs_parent_defer *ret;
+ int error;
+
+ error = -libxfs_parent_start(mp, &ret);
+ if (error)
+ fail(_("initializing parent pointer"), error);
+
+ return ret;
+}
+
static void
parseproto(
xfs_mount_t *mp,
@@ -412,6 +426,7 @@ parseproto(
struct cred creds;
char *value;
struct xfs_name xname;
+ struct xfs_parent_defer *parent = NULL;
memset(&creds, 0, sizeof(creds));
mstr = getstr(pp);
@@ -486,6 +501,7 @@ parseproto(
case IF_REGULAR:
buf = newregfile(pp, &len);
tp = getres(mp, XFS_B_TO_FSB(mp, len));
+ parent = newpptr(mp);
error = -libxfs_dir_ialloc(&tp, pip, mode|S_IFREG, 1, 0,
&creds, fsxp, &ip);
if (error)
@@ -509,7 +525,7 @@ parseproto(
exit(1);
}
tp = getres(mp, XFS_B_TO_FSB(mp, llen));
-
+ parent = newpptr(mp);
error = -libxfs_dir_ialloc(&tp, pip, mode|S_IFREG, 1, 0,
&creds, fsxp, &ip);
if (error)
@@ -520,15 +536,24 @@ parseproto(
xname.type = XFS_DIR3_FT_REG_FILE;
newdirent(mp, tp, pip, &xname, ip->i_ino);
libxfs_trans_log_inode(tp, ip, flags);
+ if (parent) {
+ error = -libxfs_parent_add(tp, parent, pip, &xname,
+ ip);
+ if (error)
+ fail(_("committing parent pointers failed."),
+ error);
+ }
error = -libxfs_trans_commit(tp);
if (error)
fail(_("Space preallocation failed."), error);
+ libxfs_parent_finish(mp, parent);
rsvfile(mp, ip, llen);
libxfs_irele(ip);
return;
case IF_BLOCK:
tp = getres(mp, 0);
+ parent = newpptr(mp);
majdev = getnum(getstr(pp), 0, 0, false);
mindev = getnum(getstr(pp), 0, 0, false);
error = -libxfs_dir_ialloc(&tp, pip, mode|S_IFBLK, 1,
@@ -544,6 +569,7 @@ parseproto(
case IF_CHAR:
tp = getres(mp, 0);
+ parent = newpptr(mp);
majdev = getnum(getstr(pp), 0, 0, false);
mindev = getnum(getstr(pp), 0, 0, false);
error = -libxfs_dir_ialloc(&tp, pip, mode|S_IFCHR, 1,
@@ -558,6 +584,7 @@ parseproto(
case IF_FIFO:
tp = getres(mp, 0);
+ parent = newpptr(mp);
error = -libxfs_dir_ialloc(&tp, pip, mode|S_IFIFO, 1, 0,
&creds, fsxp, &ip);
if (error)
@@ -570,6 +597,7 @@ parseproto(
buf = getstr(pp);
len = (int)strlen(buf);
tp = getres(mp, XFS_B_TO_FSB(mp, len));
+ parent = newpptr(mp);
error = -libxfs_dir_ialloc(&tp, pip, mode|S_IFLNK, 1, 0,
&creds, fsxp, &ip);
if (error)
@@ -592,6 +620,7 @@ parseproto(
libxfs_log_sb(tp);
isroot = 1;
} else {
+ parent = newpptr(mp);
libxfs_trans_ijoin(tp, pip, 0);
xname.type = XFS_DIR3_FT_DIR;
newdirent(mp, tp, pip, &xname, ip->i_ino);
@@ -600,9 +629,19 @@ parseproto(
}
newdirectory(mp, tp, ip, pip);
libxfs_trans_log_inode(tp, ip, flags);
+ if (parent) {
+ error = -libxfs_parent_add(tp, parent, pip, &xname,
+ ip);
+ if (error)
+ fail(_("committing parent pointers failed."),
+ error);
+ }
error = -libxfs_trans_commit(tp);
if (error)
fail(_("Directory inode allocation failed."), error);
+
+ libxfs_parent_finish(mp, parent);
+
/*
* RT initialization. Do this here to ensure that
* the RT inodes get placed after the root inode.
@@ -625,11 +664,18 @@ parseproto(
fail(_("Unknown format"), EINVAL);
}
libxfs_trans_log_inode(tp, ip, flags);
+ if (parent) {
+ error = -libxfs_parent_add(tp, parent, pip, &xname, ip);
+ if (error)
+ fail(_("committing parent pointers failed."), error);
+ }
error = -libxfs_trans_commit(tp);
if (error) {
fail(_("Error encountered creating file from prototype file"),
error);
}
+
+ libxfs_parent_finish(mp, parent);
libxfs_irele(ip);
}
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 32/32] mkfs: enable formatting with parent pointers
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
` (30 preceding siblings ...)
2023-04-06 19:39 ` [PATCH 31/32] mkfs: Add parent pointers during protofile creation Darrick J. Wong
@ 2023-04-06 19:39 ` Darrick J. Wong
31 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:39 UTC (permalink / raw)
To: djwong; +Cc: Allison Henderson, allison.henderson, linux-xfs
From: Allison Henderson <allison.henderson@oracle.com>
Enable parent pointer support in mkfs via the '-n parent' parameter.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
---
mkfs/xfs_mkfs.c | 29 ++++++++++++++++++++++++++---
1 file changed, 26 insertions(+), 3 deletions(-)
diff --git a/mkfs/xfs_mkfs.c b/mkfs/xfs_mkfs.c
index f2e683117..c86f2bedf 100644
--- a/mkfs/xfs_mkfs.c
+++ b/mkfs/xfs_mkfs.c
@@ -110,6 +110,7 @@ enum {
N_SIZE = 0,
N_VERSION,
N_FTYPE,
+ N_PARENT,
N_MAX_OPTS,
};
@@ -621,6 +622,7 @@ static struct opt_params nopts = {
[N_SIZE] = "size",
[N_VERSION] = "version",
[N_FTYPE] = "ftype",
+ [N_PARENT] = "parent",
[N_MAX_OPTS] = NULL,
},
.subopt_params = {
@@ -644,6 +646,14 @@ static struct opt_params nopts = {
.maxval = 1,
.defaultval = 1,
},
+ { .index = N_PARENT,
+ .conflicts = { { NULL, LAST_CONFLICT } },
+ .minval = 0,
+ .maxval = 1,
+ .defaultval = 1,
+ },
+
+
},
};
@@ -1000,7 +1010,7 @@ usage( void )
/* log subvol */ [-l agnum=n,internal,size=num,logdev=xxx,version=n\n\
sunit=value|su=num,sectsize=num,lazy-count=0|1]\n\
/* label */ [-L label (maximum 12 characters)]\n\
-/* naming */ [-n size=num,version=2|ci,ftype=0|1]\n\
+/* naming */ [-n size=num,version=2|ci,ftype=0|1,parent=0|1]]\n\
/* no-op info only */ [-N]\n\
/* prototype file */ [-p fname]\n\
/* quiet */ [-q]\n\
@@ -1774,6 +1784,9 @@ naming_opts_parser(
case N_FTYPE:
cli->sb_feat.dirftype = getnum(value, opts, subopt);
break;
+ case N_PARENT:
+ cli->sb_feat.parent_pointers = getnum(value, &nopts, N_PARENT);
+ break;
default:
return -EINVAL;
}
@@ -2286,6 +2299,14 @@ _("inode btree counters not supported without finobt support\n"));
cli->sb_feat.inobtcnt = false;
}
+ if ((cli->sb_feat.parent_pointers) &&
+ cli->sb_feat.dir_version == 4) {
+ fprintf(stderr,
+_("parent pointers not supported on v4 filesystems\n"));
+ usage();
+ cli->sb_feat.parent_pointers = false;
+ }
+
if (cli->xi->rtname) {
if (cli->sb_feat.reflink && cli_opt_set(&mopts, M_REFLINK)) {
fprintf(stderr,
@@ -3285,8 +3306,6 @@ sb_set_features(
sbp->sb_features2 |= XFS_SB_VERSION2_LAZYSBCOUNTBIT;
if (fp->projid32bit)
sbp->sb_features2 |= XFS_SB_VERSION2_PROJID32BIT;
- if (fp->parent_pointers)
- sbp->sb_features2 |= XFS_SB_VERSION2_PARENTBIT;
if (fp->crcs_enabled)
sbp->sb_features2 |= XFS_SB_VERSION2_CRCBIT;
if (fp->attr_version == 2)
@@ -3327,6 +3346,10 @@ sb_set_features(
sbp->sb_features_ro_compat |= XFS_SB_FEAT_RO_COMPAT_INOBTCNT;
if (fp->bigtime)
sbp->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_BIGTIME;
+ if (fp->parent_pointers) {
+ sbp->sb_features_incompat |= XFS_SB_FEAT_INCOMPAT_PARENT;
+ sbp->sb_versionnum |= XFS_SB_VERSION_ATTRBIT;
+ }
/*
* Sparse inode chunk support has two main inode alignment requirements.
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 1/7] xfs_repair: build a parent pointer index
2023-04-06 19:16 ` [PATCHSET v11 0/7] xfs_repair: support " Darrick J. Wong
@ 2023-04-06 19:40 ` Darrick J. Wong
2023-04-06 19:40 ` [PATCH 2/7] xfs_repair: move the global dirent name store to a separate object Darrick J. Wong
` (5 subsequent siblings)
6 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:40 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
When we're walking directories during phase 6, build an index of parent
pointers that we expect to find.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
repair/Makefile | 2 +
repair/phase6.c | 39 ++++++++++-
repair/pptr.c | 198 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
repair/pptr.h | 15 ++++
4 files changed, 252 insertions(+), 2 deletions(-)
create mode 100644 repair/pptr.c
create mode 100644 repair/pptr.h
diff --git a/repair/Makefile b/repair/Makefile
index 2c40e59a3..18731613b 100644
--- a/repair/Makefile
+++ b/repair/Makefile
@@ -23,6 +23,7 @@ HFILES = \
err_protos.h \
globals.h \
incore.h \
+ pptr.h \
prefetch.h \
progress.h \
protos.h \
@@ -59,6 +60,7 @@ CFILES = \
phase5.c \
phase6.c \
phase7.c \
+ pptr.c \
prefetch.c \
progress.c \
quotacheck.c \
diff --git a/repair/phase6.c b/repair/phase6.c
index 24ee6c27a..4df3b2402 100644
--- a/repair/phase6.c
+++ b/repair/phase6.c
@@ -18,6 +18,7 @@
#include "dinode.h"
#include "progress.h"
#include "versions.h"
+#include "repair/pptr.h"
static struct cred zerocr;
static struct fsxattr zerofsx;
@@ -974,6 +975,7 @@ mk_orphanage(xfs_mount_t *mp)
do_error(
_("can't make %s, createname error %d\n"),
ORPHANAGE, error);
+ add_parent_ptr(ip->i_ino, ORPHANAGE, pip);
/*
* bump up the link count in the root directory to account
@@ -1160,6 +1162,10 @@ mv_orphanage(
do_error(
_("orphanage name create failed (%d)\n"), err);
}
+
+ if (xfs_has_parent(mp))
+ add_parent_ptr(ino_p->i_ino, xname.name, orphanage_ip);
+
libxfs_irele(ino_p);
libxfs_irele(orphanage_ip);
}
@@ -2459,6 +2465,7 @@ shortform_dir2_entry_check(
struct xfs_dir2_sf_entry *next_sfep;
struct xfs_ifork *ifp;
struct ino_tree_node *irec;
+ xfs_dir2_dataptr_t diroffset;
int max_size;
int ino_offset;
int i;
@@ -2637,8 +2644,9 @@ shortform_dir2_entry_check(
/*
* check for duplicate names in directory.
*/
- if (!dir_hash_add(mp, hashtab, (xfs_dir2_dataptr_t)
- (sfep - xfs_dir2_sf_firstentry(sfp)),
+ diroffset = xfs_dir2_byte_to_dataptr(
+ xfs_dir2_sf_get_offset(sfep));
+ if (!dir_hash_add(mp, hashtab, diroffset,
lino, sfep->namelen, sfep->name,
libxfs_dir2_sf_get_ftype(mp, sfep))) {
do_warn(
@@ -2672,6 +2680,7 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
next_sfep = shortform_dir2_junk(mp, sfp, sfep,
lino, &max_size, &i,
&bytes_deleted, ino_dirty);
+ dir_hash_junkit(hashtab, diroffset);
continue;
} else if (parent == ino) {
add_inode_reached(irec, ino_offset);
@@ -2696,6 +2705,7 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
next_sfep = shortform_dir2_junk(mp, sfp, sfep,
lino, &max_size, &i,
&bytes_deleted, ino_dirty);
+ dir_hash_junkit(hashtab, diroffset);
continue;
}
}
@@ -2787,6 +2797,26 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
}
}
+static void
+dir_hash_add_parent_ptrs(
+ struct xfs_inode *dp,
+ struct dir_hash_tab *hashtab)
+{
+ struct dir_hash_ent *p;
+
+ if (!xfs_has_parent(dp->i_mount))
+ return;
+
+ for (p = hashtab->first; p; p = p->nextbyorder) {
+ if (p->name.name[0] == '/' || (p->name.name[0] == '.' &&
+ (p->name.len == 1 || (p->name.len == 2 &&
+ p->name.name[1] == '.'))))
+ continue;
+
+ add_parent_ptr(p->inum, p->name.name, dp);
+ }
+}
+
/*
* processes all reachable inodes in directories
*/
@@ -2913,6 +2943,7 @@ _("error %d fixing shortform directory %llu\n"),
default:
break;
}
+ dir_hash_add_parent_ptrs(ip, hashtab);
dir_hash_done(hashtab);
/*
@@ -3204,6 +3235,8 @@ phase6(xfs_mount_t *mp)
ino_tree_node_t *irec;
int i;
+ parent_ptr_init(mp);
+
memset(&zerocr, 0, sizeof(struct cred));
memset(&zerofsx, 0, sizeof(struct fsxattr));
orphanage_ino = 0;
@@ -3304,4 +3337,6 @@ _(" - resetting contents of realtime bitmap and summary inodes\n"));
irec = next_ino_rec(irec);
}
}
+
+ parent_ptr_free(mp);
}
diff --git a/repair/pptr.c b/repair/pptr.c
new file mode 100644
index 000000000..f1b3332fc
--- /dev/null
+++ b/repair/pptr.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2023 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "libxfs.h"
+#include "libxfs/xfile.h"
+#include "libxfs/xfblob.h"
+#include "repair/err_protos.h"
+#include "repair/slab.h"
+#include "repair/pptr.h"
+
+#undef PPTR_DEBUG
+
+#ifdef PPTR_DEBUG
+# define dbg_printf(f, a...) do {printf(f, ## a); fflush(stdout); } while (0)
+#else
+# define dbg_printf(f, a...)
+#endif
+
+/*
+ * Parent Pointer Validation
+ * =========================
+ *
+ * Phase 6 validates the connectivity of the directory tree after validating
+ * that all the space metadata are correct, and confirming all the inodes that
+ * we intend to keep. The first part of phase 6 walks the directories of the
+ * filesystem to ensure that every file that isn't the root directory has a
+ * parent. Unconnected files are attached to the orphanage. Filesystems with
+ * the directory parent pointer feature enabled must also ensure that for every
+ * directory entry that points to a child file, that child has a matching
+ * parent pointer.
+ *
+ * There are many ways that we could check the parent pointers, but the means
+ * that we have chosen is to build a per-AG master index of all parent pointers
+ * of all inodes stored in that AG, and use that as the basis for comparison.
+ * This consumes a lot of memory, but performing both a forward scan to check
+ * dirent -> parent pointer and a backwards scan of parent pointer -> dirent
+ * takes longer than the simple method presented here. Userspace adds the
+ * additional twist that inodes are not cached (and there are no ILOCKs), which
+ * makes that approach even less attractive.
+ *
+ * During the directory walk at the start of phase 6, we transform each child
+ * directory entry found into its parent pointer equivalent. In other words,
+ * the forward information:
+ *
+ * (dir_ino, name, child_ino)
+ *
+ * becomes this backwards information:
+ *
+ * (child_agino*, dir_ino*, dir_gen, name*)
+ *
+ * Key fields are starred.
+ *
+ * This tuple is recorded in the per-AG master parent pointer index. Note
+ * that names are stored separately in an xfblob data structure so that the
+ * rest of the information can be sorted and processed as fixed-size records;
+ * the incore parent pointer record contains a pointer to the xfblob data.
+ */
+
+struct ag_pptr {
+ /* parent directory handle */
+ xfs_ino_t parent_ino;
+ unsigned int parent_gen;
+
+ /* dirent name length */
+ unsigned int namelen;
+
+ /* cookie for the actual dirent name */
+ xfblob_cookie name_cookie;
+
+ /* agino of the child file */
+ xfs_agino_t child_agino;
+
+ /* hash of the dirent name */
+ xfs_dahash_t namehash;
+};
+
+struct ag_pptrs {
+ /* Lock to protect pptr_recs during the dirent scan. */
+ pthread_mutex_t lock;
+
+ /* Parent pointer records for files in this AG. */
+ struct xfs_slab *pptr_recs;
+};
+
+/* Global names storage file. */
+static struct xfblob *names;
+static pthread_mutex_t names_mutex = PTHREAD_MUTEX_INITIALIZER;
+static struct ag_pptrs *fs_pptrs;
+
+void
+parent_ptr_free(
+ struct xfs_mount *mp)
+{
+ xfs_agnumber_t agno;
+
+ if (!xfs_has_parent(mp))
+ return;
+
+ for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+ free_slab(&fs_pptrs[agno].pptr_recs);
+ pthread_mutex_destroy(&fs_pptrs[agno].lock);
+ }
+ free(fs_pptrs);
+ fs_pptrs = NULL;
+
+ xfblob_destroy(names);
+}
+
+void
+parent_ptr_init(
+ struct xfs_mount *mp)
+{
+ xfs_agnumber_t agno;
+ int error;
+
+ if (!xfs_has_parent(mp))
+ return;
+
+ error = -xfblob_create(mp, "parent pointer names", &names);
+ if (error)
+ do_error(_("init parent pointer names failed: %s\n"),
+ strerror(error));
+
+ fs_pptrs = calloc(mp->m_sb.sb_agcount, sizeof(struct ag_pptrs));
+ if (!fs_pptrs)
+ do_error(
+ _("init parent pointer per-AG record array failed: %s\n"),
+ strerror(errno));
+
+ for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
+ error = pthread_mutex_init(&fs_pptrs[agno].lock, NULL);
+ if (error)
+ do_error(
+ _("init agno %u parent pointer lock failed: %s\n"),
+ agno, strerror(error));
+
+ error = -init_slab(&fs_pptrs[agno].pptr_recs,
+ sizeof(struct ag_pptr));
+ if (error)
+ do_error(
+ _("init agno %u parent pointer recs failed: %s\n"),
+ agno, strerror(error));
+ }
+}
+
+/* Remember that @dp has a dirent (@fname, @ino). */
+void
+add_parent_ptr(
+ xfs_ino_t ino,
+ const unsigned char *fname,
+ struct xfs_inode *dp)
+{
+ struct xfs_mount *mp = dp->i_mount;
+ struct xfs_name dname = {
+ .name = fname,
+ .len = strlen(fname),
+ };
+ struct ag_pptr ag_pptr = {
+ .child_agino = XFS_INO_TO_AGINO(mp, ino),
+ .parent_ino = dp->i_ino,
+ .parent_gen = VFS_I(dp)->i_generation,
+ .namelen = dname.len,
+ };
+ struct ag_pptrs *ag_pptrs;
+ xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, ino);
+ int error;
+
+ if (!xfs_has_parent(mp))
+ return;
+
+ ag_pptr.namehash = libxfs_dir2_hashname(mp, &dname);
+
+ pthread_mutex_lock(&names_mutex);
+ error = -xfblob_store(names, &ag_pptr.name_cookie, fname,
+ ag_pptr.namelen);
+ pthread_mutex_unlock(&names_mutex);
+ if (error)
+ do_error(_("storing name '%s' failed: %s\n"),
+ fname, strerror(error));
+
+ ag_pptrs = &fs_pptrs[agno];
+ pthread_mutex_lock(&ag_pptrs->lock);
+ error = -slab_add(ag_pptrs->pptr_recs, &ag_pptr);
+ pthread_mutex_unlock(&ag_pptrs->lock);
+ if (error)
+ do_error(_("storing name '%s' key failed: %s\n"),
+ fname, strerror(error));
+
+ dbg_printf(
+ _("%s: dp %llu fname '%s' ino %llu namecookie 0x%llx\n"),
+ __func__,
+ (unsigned long long)dp->i_ino,
+ fname,
+ (unsigned long long)ino,
+ (unsigned long long)ag_pptr.name_cookie);
+}
diff --git a/repair/pptr.h b/repair/pptr.h
new file mode 100644
index 000000000..0ca2e1c6f
--- /dev/null
+++ b/repair/pptr.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2023 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __REPAIR_PPTR_H__
+#define __REPAIR_PPTR_H__
+
+void parent_ptr_free(struct xfs_mount *mp);
+void parent_ptr_init(struct xfs_mount *mp);
+
+void add_parent_ptr(xfs_ino_t ino, const unsigned char *fname,
+ struct xfs_inode *dp);
+
+#endif /* __REPAIR_PPTR_H__ */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 2/7] xfs_repair: move the global dirent name store to a separate object
2023-04-06 19:16 ` [PATCHSET v11 0/7] xfs_repair: support " Darrick J. Wong
2023-04-06 19:40 ` [PATCH 1/7] xfs_repair: build a parent pointer index Darrick J. Wong
@ 2023-04-06 19:40 ` Darrick J. Wong
2023-04-06 19:40 ` [PATCH 3/7] xfs_repair: deduplicate strings stored in string blob Darrick J. Wong
` (4 subsequent siblings)
6 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:40 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Abstract the main parent pointer dirent names xfblob object into a
separate data structure to hide implementation details.
The goals here are (a) reduce memory usage when we can by deduplicating
dirent names that exist in multiple directories; and (b) provide a
unique id for each name in the system so that sorting incore parent
pointer records can be done in a stable manner. Fast stable sorting of
records is required for the dirent <-> pptr matching algorithm.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
repair/Makefile | 2 +
repair/pptr.c | 11 ++++---
repair/strblobs.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++
repair/strblobs.h | 20 +++++++++++++
4 files changed, 108 insertions(+), 5 deletions(-)
create mode 100644 repair/strblobs.c
create mode 100644 repair/strblobs.h
diff --git a/repair/Makefile b/repair/Makefile
index 18731613b..395466026 100644
--- a/repair/Makefile
+++ b/repair/Makefile
@@ -32,6 +32,7 @@ HFILES = \
rt.h \
scan.h \
slab.h \
+ strblobs.h \
threads.h \
versions.h
@@ -69,6 +70,7 @@ CFILES = \
sb.c \
scan.c \
slab.c \
+ strblobs.c \
threads.c \
versions.c \
xfs_repair.c
diff --git a/repair/pptr.c b/repair/pptr.c
index f1b3332fc..d18fa0493 100644
--- a/repair/pptr.c
+++ b/repair/pptr.c
@@ -9,6 +9,7 @@
#include "repair/err_protos.h"
#include "repair/slab.h"
#include "repair/pptr.h"
+#include "repair/strblobs.h"
#undef PPTR_DEBUG
@@ -55,7 +56,7 @@
* This tuple is recorded in the per-AG master parent pointer index. Note
* that names are stored separately in an xfblob data structure so that the
* rest of the information can be sorted and processed as fixed-size records;
- * the incore parent pointer record contains a pointer to the xfblob data.
+ * the incore parent pointer record contains a pointer to the strblob data.
*/
struct ag_pptr {
@@ -85,7 +86,7 @@ struct ag_pptrs {
};
/* Global names storage file. */
-static struct xfblob *names;
+static struct strblobs *nameblobs;
static pthread_mutex_t names_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct ag_pptrs *fs_pptrs;
@@ -105,7 +106,7 @@ parent_ptr_free(
free(fs_pptrs);
fs_pptrs = NULL;
- xfblob_destroy(names);
+ strblobs_destroy(&nameblobs);
}
void
@@ -118,7 +119,7 @@ parent_ptr_init(
if (!xfs_has_parent(mp))
return;
- error = -xfblob_create(mp, "parent pointer names", &names);
+ error = strblobs_init(mp, "parent pointer names", &nameblobs);
if (error)
do_error(_("init parent pointer names failed: %s\n"),
strerror(error));
@@ -173,7 +174,7 @@ add_parent_ptr(
ag_pptr.namehash = libxfs_dir2_hashname(mp, &dname);
pthread_mutex_lock(&names_mutex);
- error = -xfblob_store(names, &ag_pptr.name_cookie, fname,
+ error = strblobs_store(nameblobs, &ag_pptr.name_cookie, fname,
ag_pptr.namelen);
pthread_mutex_unlock(&names_mutex);
if (error)
diff --git a/repair/strblobs.c b/repair/strblobs.c
new file mode 100644
index 000000000..2b7a7a5e0
--- /dev/null
+++ b/repair/strblobs.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2023 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "libxfs.h"
+#include "libxfs/xfile.h"
+#include "libxfs/xfblob.h"
+#include "repair/strblobs.h"
+
+/*
+ * String Blob Structure
+ * =====================
+ *
+ * This data structure wraps the storage of strings with explicit length in an
+ * xfblob structure.
+ */
+struct strblobs {
+ struct xfblob *strings;
+};
+
+/* Initialize a string blob structure. */
+int
+strblobs_init(
+ struct xfs_mount *mp,
+ const char *descr,
+ struct strblobs **sblobs)
+{
+ struct strblobs *sb;
+ int error;
+
+ sb = malloc(sizeof(struct strblobs));
+ if (!sb)
+ return ENOMEM;
+
+ error = -xfblob_create(mp, descr, &sb->strings);
+ if (error)
+ goto out_free;
+
+ *sblobs = sb;
+ return 0;
+
+out_free:
+ free(sb);
+ return error;
+}
+
+/* Deconstruct a string blob structure. */
+void
+strblobs_destroy(
+ struct strblobs **sblobs)
+{
+ struct strblobs *sb = *sblobs;
+
+ xfblob_destroy(sb->strings);
+ free(sb);
+ *sblobs = NULL;
+}
+
+/* Store a string and return a cookie for its retrieval. */
+int
+strblobs_store(
+ struct strblobs *sblobs,
+ xfblob_cookie *str_cookie,
+ const unsigned char *str,
+ unsigned int str_len)
+{
+ return -xfblob_store(sblobs->strings, str_cookie, str, str_len);
+}
+
+/* Retrieve a previously stored string. */
+int
+strblobs_load(
+ struct strblobs *sblobs,
+ xfblob_cookie str_cookie,
+ unsigned char *str,
+ unsigned int str_len)
+{
+ return -xfblob_load(sblobs->strings, str_cookie, str, str_len);
+}
diff --git a/repair/strblobs.h b/repair/strblobs.h
new file mode 100644
index 000000000..f56801754
--- /dev/null
+++ b/repair/strblobs.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2023 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __REPAIR_STRBLOBS_H__
+#define __REPAIR_STRBLOBS_H__
+
+struct strblobs;
+
+int strblobs_init(struct xfs_mount *mp, const char *descr,
+ struct strblobs **sblobs);
+void strblobs_destroy(struct strblobs **sblobs);
+
+int strblobs_store(struct strblobs *sblobs, xfblob_cookie *str_cookie,
+ const unsigned char *str, unsigned int str_len);
+int strblobs_load(struct strblobs *sblobs, xfblob_cookie str_cookie,
+ unsigned char *str, unsigned int str_len);
+
+#endif /* __REPAIR_STRBLOBS_H__ */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 3/7] xfs_repair: deduplicate strings stored in string blob
2023-04-06 19:16 ` [PATCHSET v11 0/7] xfs_repair: support " Darrick J. Wong
2023-04-06 19:40 ` [PATCH 1/7] xfs_repair: build a parent pointer index Darrick J. Wong
2023-04-06 19:40 ` [PATCH 2/7] xfs_repair: move the global dirent name store to a separate object Darrick J. Wong
@ 2023-04-06 19:40 ` Darrick J. Wong
2023-04-06 19:40 ` [PATCH 4/7] xfs_repair: check parent pointers Darrick J. Wong
` (3 subsequent siblings)
6 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:40 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Reduce the memory requirements of the string blob structure by
deduplicating the strings stored within.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
repair/pptr.c | 13 ++++-
repair/strblobs.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++--
repair/strblobs.h | 8 ++-
3 files changed, 152 insertions(+), 9 deletions(-)
diff --git a/repair/pptr.c b/repair/pptr.c
index d18fa0493..2523f4a53 100644
--- a/repair/pptr.c
+++ b/repair/pptr.c
@@ -49,7 +49,7 @@
*
* becomes this backwards information:
*
- * (child_agino*, dir_ino*, dir_gen, name*)
+ * (child_agino*, dir_ino*, dir_gen, name_cookie*)
*
* Key fields are starred.
*
@@ -57,6 +57,10 @@
* that names are stored separately in an xfblob data structure so that the
* rest of the information can be sorted and processed as fixed-size records;
* the incore parent pointer record contains a pointer to the strblob data.
+ * Because string blobs are deduplicated, there's a 1:1 mapping of name cookies
+ * to strings, which means that we can use the name cookie as a comparison key
+ * instead of loading the full dentry name every time we want to perform a
+ * comparison.
*/
struct ag_pptr {
@@ -113,13 +117,16 @@ void
parent_ptr_init(
struct xfs_mount *mp)
{
+ uint64_t iused;
xfs_agnumber_t agno;
int error;
if (!xfs_has_parent(mp))
return;
- error = strblobs_init(mp, "parent pointer names", &nameblobs);
+ /* One hash bucket per inode, up to about 8M of memory on 64-bit. */
+ iused = min(mp->m_sb.sb_icount - mp->m_sb.sb_ifree, 1048573);
+ error = strblobs_init(mp, "parent pointer names", iused, &nameblobs);
if (error)
do_error(_("init parent pointer names failed: %s\n"),
strerror(error));
@@ -175,7 +182,7 @@ add_parent_ptr(
pthread_mutex_lock(&names_mutex);
error = strblobs_store(nameblobs, &ag_pptr.name_cookie, fname,
- ag_pptr.namelen);
+ ag_pptr.namelen, ag_pptr.namehash);
pthread_mutex_unlock(&names_mutex);
if (error)
do_error(_("storing name '%s' failed: %s\n"),
diff --git a/repair/strblobs.c b/repair/strblobs.c
index 2b7a7a5e0..0478c5ac8 100644
--- a/repair/strblobs.c
+++ b/repair/strblobs.c
@@ -13,23 +13,43 @@
* =====================
*
* This data structure wraps the storage of strings with explicit length in an
- * xfblob structure.
+ * xfblob structure. It stores a hashtable of string checksums to provide
+ * fast(ish) lookups of existing strings to enable deduplication of the strings
+ * contained within.
*/
+struct strblob_hashent {
+ struct strblob_hashent *next;
+
+ xfblob_cookie str_cookie;
+ unsigned int str_len;
+ xfs_dahash_t str_hash;
+};
+
struct strblobs {
struct xfblob *strings;
+ unsigned int nr_buckets;
+
+ struct strblob_hashent *buckets[];
};
+static inline size_t strblobs_sizeof(unsigned int nr_buckets)
+{
+ return sizeof(struct strblobs) +
+ (nr_buckets * sizeof(struct strblobs_hashent *));
+}
+
/* Initialize a string blob structure. */
int
strblobs_init(
struct xfs_mount *mp,
const char *descr,
+ unsigned int hash_buckets,
struct strblobs **sblobs)
{
struct strblobs *sb;
int error;
- sb = malloc(sizeof(struct strblobs));
+ sb = calloc(strblobs_sizeof(hash_buckets), 1);
if (!sb)
return ENOMEM;
@@ -37,6 +57,7 @@ strblobs_init(
if (error)
goto out_free;
+ sb->nr_buckets = hash_buckets;
*sblobs = sb;
return 0;
@@ -51,21 +72,132 @@ strblobs_destroy(
struct strblobs **sblobs)
{
struct strblobs *sb = *sblobs;
+ struct strblob_hashent *ent, *ent_next;
+ unsigned int bucket;
+
+ for (bucket = 0; bucket < sb->nr_buckets; bucket++) {
+ ent = sb->buckets[bucket];
+ while (ent != NULL) {
+ ent_next = ent->next;
+ free(ent);
+ ent = ent_next;
+ }
+ }
xfblob_destroy(sb->strings);
free(sb);
*sblobs = NULL;
}
+/*
+ * Search the string hashtable for a matching entry. Sets sets the cookie and
+ * returns 0 if one is found; ENOENT if there is no match; or a positive errno.
+ */
+static int
+__strblobs_lookup(
+ struct strblobs *sblobs,
+ xfblob_cookie *str_cookie,
+ const unsigned char *str,
+ unsigned int str_len,
+ xfs_dahash_t str_hash)
+{
+ struct strblob_hashent *ent;
+ char *buf = NULL;
+ unsigned int bucket;
+ int error;
+
+ bucket = str_hash % sblobs->nr_buckets;
+ ent = sblobs->buckets[bucket];
+
+ for (ent = sblobs->buckets[bucket]; ent != NULL; ent = ent->next) {
+ if (ent->str_len != str_len || ent->str_hash != str_hash)
+ continue;
+
+ if (!buf) {
+ buf = malloc(str_len);
+ if (!buf)
+ return ENOMEM;
+ }
+
+ error = strblobs_load(sblobs, ent->str_cookie, buf, str_len);
+ if (error)
+ goto out;
+
+ if (memcmp(str, buf, str_len))
+ continue;
+
+ *str_cookie = ent->str_cookie;
+ goto out;
+ }
+ error = ENOENT;
+
+out:
+ free(buf);
+ return error;
+}
+
+/*
+ * Search the string hashtable for a matching entry. Sets sets the cookie and
+ * returns 0 if one is found; ENOENT if there is no match; or a positive errno.
+ */
+int
+strblobs_lookup(
+ struct strblobs *sblobs,
+ xfblob_cookie *str_cookie,
+ const unsigned char *str,
+ unsigned int str_len,
+ xfs_dahash_t str_hash)
+{
+ return __strblobs_lookup(sblobs, str_cookie, str, str_len, str_hash);
+}
+
+/* Remember a string in the hashtable. */
+static int
+strblobs_hash(
+ struct strblobs *sblobs,
+ xfblob_cookie str_cookie,
+ const unsigned char *str,
+ unsigned int str_len,
+ xfs_dahash_t str_hash)
+{
+ struct strblob_hashent *ent;
+ unsigned int bucket;
+
+ bucket = str_hash % sblobs->nr_buckets;
+
+ ent = malloc(sizeof(struct strblob_hashent));
+ if (!ent)
+ return ENOMEM;
+
+ ent->str_cookie = str_cookie;
+ ent->str_len = str_len;
+ ent->str_hash = str_hash;
+ ent->next = sblobs->buckets[bucket];
+
+ sblobs->buckets[bucket] = ent;
+ return 0;
+}
+
/* Store a string and return a cookie for its retrieval. */
int
strblobs_store(
struct strblobs *sblobs,
xfblob_cookie *str_cookie,
const unsigned char *str,
- unsigned int str_len)
+ unsigned int str_len,
+ xfs_dahash_t str_hash)
{
- return -xfblob_store(sblobs->strings, str_cookie, str, str_len);
+ int error;
+
+ error = __strblobs_lookup(sblobs, str_cookie, str, str_len, str_hash);
+ if (error != ENOENT)
+ return error;
+
+ error = -xfblob_store(sblobs->strings, str_cookie, str, str_len);
+ if (error)
+ return error;
+
+ return strblobs_hash(sblobs, *str_cookie, str, str_len, str_hash);
}
/* Retrieve a previously stored string. */
diff --git a/repair/strblobs.h b/repair/strblobs.h
index f56801754..6de623e66 100644
--- a/repair/strblobs.h
+++ b/repair/strblobs.h
@@ -9,12 +9,16 @@
struct strblobs;
int strblobs_init(struct xfs_mount *mp, const char *descr,
- struct strblobs **sblobs);
+ unsigned int hash_buckets, struct strblobs **sblobs);
void strblobs_destroy(struct strblobs **sblobs);
int strblobs_store(struct strblobs *sblobs, xfblob_cookie *str_cookie,
- const unsigned char *str, unsigned int str_len);
+ const unsigned char *str, unsigned int str_len,
+ xfs_dahash_t hash);
int strblobs_load(struct strblobs *sblobs, xfblob_cookie str_cookie,
unsigned char *str, unsigned int str_len);
+int strblobs_lookup(struct strblobs *sblobs, xfblob_cookie *str_cookie,
+ const unsigned char *str, unsigned int str_len,
+ xfs_dahash_t hash);
#endif /* __REPAIR_STRBLOBS_H__ */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 4/7] xfs_repair: check parent pointers
2023-04-06 19:16 ` [PATCHSET v11 0/7] xfs_repair: support " Darrick J. Wong
` (2 preceding siblings ...)
2023-04-06 19:40 ` [PATCH 3/7] xfs_repair: deduplicate strings stored in string blob Darrick J. Wong
@ 2023-04-06 19:40 ` Darrick J. Wong
2023-04-06 19:41 ` [PATCH 5/7] xfs_repair: dump garbage parent pointer attributes Darrick J. Wong
` (2 subsequent siblings)
6 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:40 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Use the parent pointer index that we constructed in the previous patch
to check that each file's parent pointer records exactly match the
directory entries that we recorded while walking directory entries.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/libxfs_api_defs.h | 4
libxfs/xfblob.c | 9 +
libxfs/xfblob.h | 2
repair/Makefile | 2
repair/listxattr.c | 271 +++++++++++++++
repair/listxattr.h | 15 +
repair/phase6.c | 2
repair/pptr.c | 813 ++++++++++++++++++++++++++++++++++++++++++++++
repair/pptr.h | 2
9 files changed, 1120 insertions(+)
create mode 100644 repair/listxattr.c
create mode 100644 repair/listxattr.h
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index 13242b12a..fb1865483 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -34,7 +34,9 @@
#define xfs_alloc_vextent libxfs_alloc_vextent
#define xfs_attr3_leaf_hdr_from_disk libxfs_attr3_leaf_hdr_from_disk
+#define xfs_attr3_leaf_read libxfs_attr3_leaf_read
#define xfs_attr_get libxfs_attr_get
+#define xfs_attr_is_leaf libxfs_attr_is_leaf
#define xfs_attr_leaf_newentsize libxfs_attr_leaf_newentsize
#define xfs_attr_namecheck libxfs_attr_namecheck
#define xfs_attr_set libxfs_attr_set
@@ -63,6 +65,7 @@
#define xfs_bwrite libxfs_bwrite
#define xfs_calc_dquots_per_chunk libxfs_calc_dquots_per_chunk
#define xfs_da3_node_hdr_from_disk libxfs_da3_node_hdr_from_disk
+#define xfs_da3_node_read libxfs_da3_node_read
#define xfs_da_get_buf libxfs_da_get_buf
#define xfs_da_hashname libxfs_da_hashname
#define xfs_da_read_buf libxfs_da_read_buf
@@ -130,6 +133,7 @@
#define xfs_inobt_stage_cursor libxfs_inobt_stage_cursor
#define xfs_inode_from_disk libxfs_inode_from_disk
#define xfs_inode_from_disk_ts libxfs_inode_from_disk_ts
+#define xfs_inode_hasattr libxfs_inode_hasattr
#define xfs_inode_to_disk libxfs_inode_to_disk
#define xfs_inode_validate_cowextsize libxfs_inode_validate_cowextsize
#define xfs_inode_validate_extsize libxfs_inode_validate_extsize
diff --git a/libxfs/xfblob.c b/libxfs/xfblob.c
index 6c1c8e6f9..2c6e69a21 100644
--- a/libxfs/xfblob.c
+++ b/libxfs/xfblob.c
@@ -146,3 +146,12 @@ xfblob_free(
xfile_discard(blob->xfile, cookie, sizeof(key) + key.xb_size);
return 0;
}
+
+/* Drop all the blobs. */
+void
+xfblob_truncate(
+ struct xfblob *blob)
+{
+ xfile_discard(blob->xfile, PAGE_SIZE, blob->last_offset - PAGE_SIZE);
+ blob->last_offset = PAGE_SIZE;
+}
diff --git a/libxfs/xfblob.h b/libxfs/xfblob.h
index d1282810b..0d6de8ce0 100644
--- a/libxfs/xfblob.h
+++ b/libxfs/xfblob.h
@@ -22,4 +22,6 @@ int xfblob_store(struct xfblob *blob, xfblob_cookie *cookie, const void *ptr,
uint32_t size);
int xfblob_free(struct xfblob *blob, xfblob_cookie cookie);
+void xfblob_truncate(struct xfblob *blob);
+
#endif /* __XFS_SCRUB_XFBLOB_H__ */
diff --git a/repair/Makefile b/repair/Makefile
index 395466026..48ddcdd10 100644
--- a/repair/Makefile
+++ b/repair/Makefile
@@ -23,6 +23,7 @@ HFILES = \
err_protos.h \
globals.h \
incore.h \
+ listxattr.h \
pptr.h \
prefetch.h \
progress.h \
@@ -54,6 +55,7 @@ CFILES = \
incore_ext.c \
incore_ino.c \
init.c \
+ listxattr.c \
phase1.c \
phase2.c \
phase3.c \
diff --git a/repair/listxattr.c b/repair/listxattr.c
new file mode 100644
index 000000000..41fbc9255
--- /dev/null
+++ b/repair/listxattr.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2022-2023 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include "libxfs.h"
+#include "libxlog.h"
+#include "libfrog/bitmap.h"
+#include "repair/listxattr.h"
+
+/* Call a function for every entry in a shortform xattr structure. */
+STATIC int
+xattr_walk_sf(
+ struct xfs_inode *ip,
+ xattr_walk_fn attr_fn,
+ void *priv)
+{
+ struct xfs_attr_shortform *sf;
+ struct xfs_attr_sf_entry *sfe;
+ unsigned int i;
+ int error;
+
+ sf = (struct xfs_attr_shortform *)ip->i_af.if_u1.if_data;
+ for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) {
+ error = attr_fn(ip, sfe->flags, sfe->nameval, sfe->namelen,
+ &sfe->nameval[sfe->namelen], sfe->valuelen,
+ priv);
+ if (error)
+ return error;
+
+ sfe = xfs_attr_sf_nextentry(sfe);
+ }
+
+ return 0;
+}
+
+/* Call a function for every entry in this xattr leaf block. */
+STATIC int
+xattr_walk_leaf_entries(
+ struct xfs_inode *ip,
+ xattr_walk_fn attr_fn,
+ struct xfs_buf *bp,
+ void *priv)
+{
+ struct xfs_attr3_icleaf_hdr ichdr;
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_attr_leafblock *leaf = bp->b_addr;
+ struct xfs_attr_leaf_entry *entry;
+ unsigned int i;
+ int error;
+
+ libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &ichdr, leaf);
+ entry = xfs_attr3_leaf_entryp(leaf);
+
+ for (i = 0; i < ichdr.count; entry++, i++) {
+ void *value;
+ char *name;
+ unsigned int namelen, valuelen;
+
+ if (entry->flags & XFS_ATTR_LOCAL) {
+ struct xfs_attr_leaf_name_local *name_loc;
+
+ name_loc = xfs_attr3_leaf_name_local(leaf, i);
+ name = name_loc->nameval;
+ namelen = name_loc->namelen;
+ value = &name_loc->nameval[name_loc->namelen];
+ valuelen = be16_to_cpu(name_loc->valuelen);
+ } else {
+ struct xfs_attr_leaf_name_remote *name_rmt;
+
+ name_rmt = xfs_attr3_leaf_name_remote(leaf, i);
+ name = name_rmt->name;
+ namelen = name_rmt->namelen;
+ value = NULL;
+ valuelen = be32_to_cpu(name_rmt->valuelen);
+ }
+
+ error = attr_fn(ip, entry->flags, name, namelen, value,
+ valuelen, priv);
+ if (error)
+ return error;
+
+ }
+
+ return 0;
+}
+
+/*
+ * Call a function for every entry in a leaf-format xattr structure. Avoid
+ * memory allocations for the loop detector since there's only one block.
+ */
+STATIC int
+xattr_walk_leaf(
+ struct xfs_inode *ip,
+ xattr_walk_fn attr_fn,
+ void *priv)
+{
+ struct xfs_buf *leaf_bp;
+ int error;
+
+ error = -libxfs_attr3_leaf_read(NULL, ip, 0, &leaf_bp);
+ if (error)
+ return error;
+
+ error = xattr_walk_leaf_entries(ip, attr_fn, leaf_bp, priv);
+ libxfs_trans_brelse(NULL, leaf_bp);
+ return error;
+}
+
+/* Find the leftmost leaf in the xattr dabtree. */
+STATIC int
+xattr_walk_find_leftmost_leaf(
+ struct xfs_inode *ip,
+ struct bitmap *seen_blocks,
+ struct xfs_buf **leaf_bpp)
+{
+ struct xfs_da3_icnode_hdr nodehdr;
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_da_intnode *node;
+ struct xfs_da_node_entry *btree;
+ struct xfs_buf *bp;
+ //xfs_failaddr_t fa;
+ xfs_dablk_t blkno = 0;
+ unsigned int expected_level = 0;
+ int error;
+
+ for (;;) {
+ uint16_t magic;
+
+ error = -libxfs_da3_node_read(NULL, ip, blkno, &bp,
+ XFS_ATTR_FORK);
+ if (error)
+ return error;
+
+ node = bp->b_addr;
+ magic = be16_to_cpu(node->hdr.info.magic);
+ if (magic == XFS_ATTR_LEAF_MAGIC ||
+ magic == XFS_ATTR3_LEAF_MAGIC)
+ break;
+
+ error = EFSCORRUPTED;
+ if (magic != XFS_DA_NODE_MAGIC &&
+ magic != XFS_DA3_NODE_MAGIC)
+ goto out_buf;
+
+ libxfs_da3_node_hdr_from_disk(mp, &nodehdr, node);
+
+ if (nodehdr.count == 0 || nodehdr.level >= XFS_DA_NODE_MAXDEPTH)
+ goto out_buf;
+
+ /* Check the level from the root node. */
+ if (blkno == 0)
+ expected_level = nodehdr.level - 1;
+ else if (expected_level != nodehdr.level)
+ goto out_buf;
+ else
+ expected_level--;
+
+ /* Remember that we've seen this node. */
+ error = -bitmap_set(seen_blocks, blkno, 1);
+ if (error)
+ goto out_buf;
+
+ /* Find the next level towards the leaves of the dabtree. */
+ btree = nodehdr.btree;
+ blkno = be32_to_cpu(btree->before);
+ libxfs_trans_brelse(NULL, bp);
+
+ /* Make sure we haven't seen this new block already. */
+ if (bitmap_test(seen_blocks, blkno, 1))
+ return EFSCORRUPTED;
+ }
+
+ error = EFSCORRUPTED;
+ if (expected_level != 0)
+ goto out_buf;
+
+ /* Remember that we've seen this leaf. */
+ error = -bitmap_set(seen_blocks, blkno, 1);
+ if (error)
+ goto out_buf;
+
+ *leaf_bpp = bp;
+ return 0;
+
+out_buf:
+ libxfs_trans_brelse(NULL, bp);
+ return error;
+}
+
+/* Call a function for every entry in a node-format xattr structure. */
+STATIC int
+xattr_walk_node(
+ struct xfs_inode *ip,
+ xattr_walk_fn attr_fn,
+ void *priv)
+{
+ struct xfs_attr3_icleaf_hdr leafhdr;
+ struct bitmap *seen_blocks;
+ struct xfs_mount *mp = ip->i_mount;
+ struct xfs_attr_leafblock *leaf;
+ struct xfs_buf *leaf_bp;
+ int error;
+
+ bitmap_alloc(&seen_blocks);
+
+ error = xattr_walk_find_leftmost_leaf(ip, seen_blocks, &leaf_bp);
+ if (error)
+ goto out_bitmap;
+
+ for (;;) {
+ error = xattr_walk_leaf_entries(ip, attr_fn, leaf_bp,
+ priv);
+ if (error)
+ goto out_leaf;
+
+ /* Find the right sibling of this leaf block. */
+ leaf = leaf_bp->b_addr;
+ libxfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &leafhdr, leaf);
+ if (leafhdr.forw == 0)
+ goto out_leaf;
+
+ libxfs_trans_brelse(NULL, leaf_bp);
+
+ /* Make sure we haven't seen this new leaf already. */
+ if (bitmap_test(seen_blocks, leafhdr.forw, 1))
+ goto out_bitmap;
+
+ error = -libxfs_attr3_leaf_read(NULL, ip, leafhdr.forw,
+ &leaf_bp);
+ if (error)
+ goto out_bitmap;
+
+ /* Remember that we've seen this new leaf. */
+ error = -bitmap_set(seen_blocks, leafhdr.forw, 1);
+ if (error)
+ goto out_leaf;
+ }
+
+out_leaf:
+ libxfs_trans_brelse(NULL, leaf_bp);
+out_bitmap:
+ bitmap_free(&seen_blocks);
+ return error;
+}
+
+/* Call a function for every extended attribute in a file. */
+int
+xattr_walk(
+ struct xfs_inode *ip,
+ xattr_walk_fn attr_fn,
+ void *priv)
+{
+ int error;
+
+ if (!libxfs_inode_hasattr(ip))
+ return 0;
+
+ if (ip->i_af.if_format == XFS_DINODE_FMT_LOCAL)
+ return xattr_walk_sf(ip, attr_fn, priv);
+
+ /* attr functions require that the attr fork is loaded */
+ error = -libxfs_iread_extents(NULL, ip, XFS_ATTR_FORK);
+ if (error)
+ return error;
+
+ if (libxfs_attr_is_leaf(ip))
+ return xattr_walk_leaf(ip, attr_fn, priv);
+
+ return xattr_walk_node(ip, attr_fn, priv);
+}
diff --git a/repair/listxattr.h b/repair/listxattr.h
new file mode 100644
index 000000000..8ddae2297
--- /dev/null
+++ b/repair/listxattr.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2022-2023 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef __REPAIR_LISTXATTR_H__
+#define __REPAIR_LISTXATTR_H__
+
+typedef int (*xattr_walk_fn)(struct xfs_inode *ip, unsigned int attr_flags,
+ const unsigned char *name, unsigned int namelen,
+ const void *value, unsigned int valuelen, void *priv);
+
+int xattr_walk(struct xfs_inode *ip, xattr_walk_fn attr_fn, void *priv);
+
+#endif /* __REPAIR_LISTXATTR_H__ */
diff --git a/repair/phase6.c b/repair/phase6.c
index 4df3b2402..c8fa88d0b 100644
--- a/repair/phase6.c
+++ b/repair/phase6.c
@@ -3338,5 +3338,7 @@ _(" - resetting contents of realtime bitmap and summary inodes\n"));
}
}
+ /* Check and repair directory parent pointers, if enabled. */
+ check_parent_ptrs(mp);
parent_ptr_free(mp);
}
diff --git a/repair/pptr.c b/repair/pptr.c
index 2523f4a53..8aab94d74 100644
--- a/repair/pptr.c
+++ b/repair/pptr.c
@@ -6,8 +6,13 @@
#include "libxfs.h"
#include "libxfs/xfile.h"
#include "libxfs/xfblob.h"
+#include "libfrog/workqueue.h"
+#include "repair/globals.h"
#include "repair/err_protos.h"
#include "repair/slab.h"
+#include "repair/listxattr.h"
+#include "repair/threads.h"
+#include "repair/incore.h"
#include "repair/pptr.h"
#include "repair/strblobs.h"
@@ -61,6 +66,65 @@
* to strings, which means that we can use the name cookie as a comparison key
* instead of loading the full dentry name every time we want to perform a
* comparison.
+ *
+ * Once we've finished with the forward scan, we get to work on the backwards
+ * scan. Each AG is processed independently. First, we sort the per-AG master
+ * records in order of child_agino, dir_ino, and name_cookie. Each inode in
+ * the AG is then processed in numerical order.
+ *
+ * The first thing that happens to the file is that we read all the extended
+ * attributes to look for parent pointers. Attributes that claim to be parent
+ * pointers but are obviously garbage are thrown away. The rest of the ondisk
+ * parent pointers for that file are stored in memory like this:
+ *
+ * (dir_ino*, dir_gen, name_cookie*)
+ *
+ * After loading the ondisk parent pointer name, we search the strblobs
+ * structure to see if it has already recorded the name. If so, this value is
+ * used as the name cookie. If the name has not yet been recorded, we flag the
+ * incore record for later deletion.
+ *
+ * When we've concluded the xattr scan, the per-file records are sorted in
+ * order of dir_ino and name_cookie.
+ *
+ * There are three possibilities here:
+ *
+ * A. The first record in the per-AG master index is an exact match for the
+ * first record in the per-file index. Everything is consistent, and we can
+ * proceed with the lockstep scan detailed below.
+ *
+ * B. The per-AG master index cursor points to a higher inode number than the
+ * first inode we are scanning. Delete the ondisk parent pointers
+ * corresponding to the per-file records until condition (B) is no longer true.
+ *
+ * C. The per-AG master index cursor instead points to a lower inode number
+ * than the one we are scanning. This means that there exists a directory
+ * entry pointing at an inode that is free. We supposedly already settled
+ * which inodes are free and which aren't, which means in-memory information is
+ * inconsistent. Abort.
+ *
+ * Otherwise, we are ready to check the file parent pointers against the
+ * master. If the ondisk directory metadata are all consistent, this recordset
+ * should correspond exactly to the subset of the master records with a
+ * child_agino matching the file that we're scanning. We should be able to
+ * walk both sets in lockstep, and find one of the following outcomes:
+ *
+ * 1) The master index cursor is ahead of the ondisk index cursor. This means
+ * that the inode has parent pointers that were not found during the dirent
+ * scan. These should be deleted.
+ *
+ * 2) The ondisk index gets ahead of the master index. This means that the
+ * dirent scan found parent pointers that are not attached to the inode.
+ * These should be added.
+ *
+ * 3) The parent_gen or (dirent) name are not consistent. Update the parent
+ * pointer to the values that we found during the dirent scan.
+ *
+ * 4) Everything matches. Move on to the next parent pointer.
+ *
+ * The current implementation does not try to rebuild directories from parent
+ * pointer information, as this requires a lengthy scan of the filesystem for
+ * each broken directory.
*/
struct ag_pptr {
@@ -81,6 +145,24 @@ struct ag_pptr {
xfs_dahash_t namehash;
};
+struct file_pptr {
+ /* parent directory handle */
+ unsigned long long parent_ino;
+ unsigned int parent_gen;
+
+ /* Is the name stored in the global nameblobs structure? */
+ unsigned int name_in_nameblobs;
+
+ /* hash of the dirent name */
+ xfs_dahash_t namehash;
+
+ /* parent pointer name length */
+ unsigned int namelen;
+
+ /* cookie for the file dirent name */
+ xfblob_cookie name_cookie;
+};
+
struct ag_pptrs {
/* Lock to protect pptr_recs during the dirent scan. */
pthread_mutex_t lock;
@@ -89,11 +171,99 @@ struct ag_pptrs {
struct xfs_slab *pptr_recs;
};
+struct file_scan {
+ struct ag_pptrs *ag_pptrs;
+
+ /* cursor for comparing ag_pptrs.pptr_recs against file_pptrs_recs */
+ struct xfs_slab_cursor *ag_pptr_recs_cur;
+
+ /* xfs_parent_name_rec records for a file that we're checking */
+ struct xfs_slab *file_pptr_recs;
+
+ /* cursor for comparing file_pptr_recs against pptrs_recs */
+ struct xfs_slab_cursor *file_pptr_recs_cur;
+
+ /* names associated with file_pptr_recs */
+ struct xfblob *file_pptr_names;
+
+ /* Number of parent pointers recorded for this file. */
+ unsigned int nr_file_pptrs;
+
+ /* Does this file have garbage xattrs with ATTR_PARENT set? */
+ bool have_garbage;
+};
+
/* Global names storage file. */
static struct strblobs *nameblobs;
static pthread_mutex_t names_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct ag_pptrs *fs_pptrs;
+static int
+cmp_ag_pptr(
+ const void *a,
+ const void *b)
+{
+ const struct ag_pptr *pa = a;
+ const struct ag_pptr *pb = b;
+
+ if (pa->child_agino < pb->child_agino)
+ return -1;
+ if (pa->child_agino > pb->child_agino)
+ return 1;
+
+ if (pa->parent_ino < pb->parent_ino)
+ return -1;
+ if (pa->parent_ino > pb->parent_ino)
+ return 1;
+
+ if (pa->namehash < pb->namehash)
+ return -1;
+ if (pa->namehash > pb->namehash)
+ return 1;
+
+ if (pa->name_cookie < pb->name_cookie)
+ return -1;
+ if (pa->name_cookie > pb->name_cookie)
+ return 1;
+
+ return 0;
+}
+
+static int
+cmp_file_pptr(
+ const void *a,
+ const void *b)
+{
+ const struct file_pptr *pa = a;
+ const struct file_pptr *pb = b;
+
+ if (pa->parent_ino < pb->parent_ino)
+ return -1;
+ if (pa->parent_ino > pb->parent_ino)
+ return 1;
+
+ /*
+ * Push the parent pointer names that we didn't find in the dirent scan
+ * towards the front of the list so that we delete them first.
+ */
+ if (!pa->name_in_nameblobs && pb->name_in_nameblobs)
+ return -1;
+ if (pa->name_in_nameblobs && !pb->name_in_nameblobs)
+ return 1;
+
+ if (pa->namehash < pb->namehash)
+ return -1;
+ if (pa->namehash > pb->namehash)
+ return 1;
+
+ if (pa->name_cookie < pb->name_cookie)
+ return -1;
+ if (pa->name_cookie > pb->name_cookie)
+ return 1;
+
+ return 0;
+}
+
void
parent_ptr_free(
struct xfs_mount *mp)
@@ -204,3 +374,646 @@ add_parent_ptr(
(unsigned long long)ino,
(unsigned long long)ag_pptr.name_cookie);
}
+
+/* Schedule this ATTR_PARENT extended attribute for deletion. */
+static void
+record_garbage_xattr(
+ struct xfs_inode *ip,
+ struct file_scan *fscan,
+ unsigned int attr_filter,
+ const char *name,
+ unsigned int namelen,
+ const void *value,
+ unsigned int valuelen)
+{
+ if (no_modify) {
+ if (!fscan->have_garbage)
+ do_warn(
+ _("would delete garbage parent pointer extended attributes in ino %llu\n"),
+ (unsigned long long)ip->i_ino);
+ fscan->have_garbage = true;
+ return;
+ }
+
+ if (fscan->have_garbage)
+ return;
+ fscan->have_garbage = true;
+
+ do_warn(
+ _("deleting garbage parent pointer extended attributes in ino %llu\n"),
+ (unsigned long long)ip->i_ino);
+ /* XXX do the work */
+}
+
+/*
+ * Store this file parent pointer's name in the file scan namelist unless it's
+ * already in the global list.
+ */
+static int
+store_file_pptr_name(
+ struct file_scan *fscan,
+ struct file_pptr *file_pptr,
+ const struct xfs_parent_name_irec *irec)
+{
+ int error;
+
+ error = strblobs_lookup(nameblobs, &file_pptr->name_cookie,
+ irec->p_name, irec->p_namelen, file_pptr->namehash);
+ if (!error) {
+ file_pptr->name_in_nameblobs = true;
+ return 0;
+ }
+ if (error != ENOENT)
+ return error;
+
+ file_pptr->name_in_nameblobs = false;
+ return -xfblob_store(fscan->file_pptr_names, &file_pptr->name_cookie,
+ irec->p_name, irec->p_namelen);
+}
+
+/* Decide if this is a directory parent pointer and stash it if so. */
+static int
+examine_xattr(
+ struct xfs_inode *ip,
+ unsigned int attr_flags,
+ const unsigned char *name,
+ unsigned int namelen,
+ const void *value,
+ unsigned int valuelen,
+ void *priv)
+{
+ struct file_pptr file_pptr = { };
+ struct xfs_name dname = {
+ .name = value,
+ .len = valuelen,
+ };
+ struct xfs_parent_name_irec irec;
+ struct xfs_mount *mp = ip->i_mount;
+ struct file_scan *fscan = priv;
+ const struct xfs_parent_name_rec *rec = (const void *)name;
+ xfs_dahash_t computed_hash;
+ int error;
+
+ /* Ignore anything that isn't a parent pointer. */
+ if (!(attr_flags & XFS_ATTR_PARENT))
+ return 0;
+
+ /* No incomplete parent pointers. */
+ if (attr_flags & XFS_ATTR_INCOMPLETE)
+ goto corrupt;
+
+ /* Does the ondisk parent pointer structure make sense? */
+ if (!xfs_parent_namecheck(mp, rec, namelen, attr_flags) ||
+ !xfs_parent_valuecheck(mp, value, valuelen))
+ goto corrupt;
+
+ libxfs_parent_irec_from_disk(&irec, rec, value, valuelen);
+
+ file_pptr.parent_ino = irec.p_ino;
+ file_pptr.parent_gen = irec.p_gen;
+ file_pptr.namelen = irec.p_namelen;
+ file_pptr.namehash = irec.p_namehash;
+
+ /*
+ * If the namehash of the dirent name encoded in the parent pointer
+ * attr value doesn't match the namehash in the parent pointer key,
+ * delete this attribute.
+ */
+ computed_hash = libxfs_dir2_hashname(ip->i_mount, &dname);
+ if (file_pptr.namehash != computed_hash) {
+ do_warn(
+ _("bad hash 0x%x for ino %llu parent pointer '%.*s', expected 0x%x\n"),
+ irec.p_namehash,
+ (unsigned long long)ip->i_ino,
+ irec.p_namelen,
+ (const char *)irec.p_name,
+ computed_hash);
+ goto corrupt;
+ }
+
+ error = store_file_pptr_name(fscan, &file_pptr, &irec);
+ if (error)
+ do_error(
+ _("storing ino %llu parent pointer '%.*s' failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ irec.p_namelen,
+ (const char *)irec.p_name,
+ strerror(error));
+
+ error = -slab_add(fscan->file_pptr_recs, &file_pptr);
+ if (error)
+ do_error(_("storing ino %llu parent pointer rec failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
+
+ dbg_printf(
+ _("%s: dp %llu fname '%.*s' namelen %u ino %llu namecookie 0x%llx\n"),
+ __func__,
+ (unsigned long long)irec.p_ino,
+ irec.p_namelen,
+ (const char *)irec.p_name,
+ irec.p_namelen,
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr.name_cookie);
+
+ fscan->nr_file_pptrs++;
+ return 0;
+corrupt:
+ record_garbage_xattr(ip, fscan, attr_flags, name, namelen, value,
+ valuelen);
+ return 0;
+}
+
+/* Load a file parent pointer name from wherever we stored it. */
+static int
+load_file_pptr_name(
+ struct file_scan *fscan,
+ const struct file_pptr *file_pptr,
+ unsigned char *name)
+{
+ if (file_pptr->name_in_nameblobs)
+ return strblobs_load(nameblobs, file_pptr->name_cookie,
+ name, file_pptr->namelen);
+
+ return -xfblob_load(fscan->file_pptr_names, file_pptr->name_cookie,
+ name, file_pptr->namelen);
+}
+
+/* Remove all pptrs from @ip. */
+static void
+clear_all_pptrs(
+ struct xfs_inode *ip)
+{
+ if (no_modify) {
+ do_warn(_("would delete unlinked ino %llu parent pointers\n"),
+ (unsigned long long)ip->i_ino);
+ return;
+ }
+
+ do_warn(_("deleting unlinked ino %llu parent pointers\n"),
+ (unsigned long long)ip->i_ino);
+ /* XXX actually do the work */
+}
+
+/* Add @ag_pptr to @ip. */
+static void
+add_missing_parent_ptr(
+ struct xfs_inode *ip,
+ struct file_scan *fscan,
+ const struct ag_pptr *ag_pptr)
+{
+ unsigned char name[MAXNAMELEN];
+ int error;
+
+ error = strblobs_load(nameblobs, ag_pptr->name_cookie, name,
+ ag_pptr->namelen);
+ if (error)
+ do_error(
+ _("loading missing name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx) failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ (unsigned long long)ag_pptr->name_cookie,
+ strerror(error));
+
+ if (no_modify) {
+ do_warn(
+ _("would add missing ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ ag_pptr->namelen,
+ name);
+ return;
+ }
+
+ do_warn(
+ _("adding missing ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ ag_pptr->namelen,
+ name);
+
+ /* XXX actually do the work */
+}
+
+/* Remove @file_pptr from @ip. */
+static void
+remove_incorrect_parent_ptr(
+ struct xfs_inode *ip,
+ struct file_scan *fscan,
+ const struct file_pptr *file_pptr)
+{
+ unsigned char name[MAXNAMELEN] = { };
+ int error;
+
+ error = load_file_pptr_name(fscan, file_pptr, name);
+ if (error)
+ do_error(
+ _("loading incorrect name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx) failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ (unsigned long long)file_pptr->name_cookie,
+ strerror(error));
+
+ if (no_modify) {
+ do_warn(
+ _("would remove bad ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ file_pptr->namelen,
+ name);
+ return;
+ }
+
+ do_warn(
+ _("removing bad ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ file_pptr->namelen,
+ name);
+
+ /* XXX actually do the work */
+}
+
+/*
+ * We found parent pointers that point to the same inode and directory offset.
+ * Make sure they have the same generation number and dirent name.
+ */
+static void
+compare_parent_ptrs(
+ struct xfs_inode *ip,
+ struct file_scan *fscan,
+ const struct ag_pptr *ag_pptr,
+ const struct file_pptr *file_pptr)
+{
+ unsigned char name1[MAXNAMELEN] = { };
+ unsigned char name2[MAXNAMELEN] = { };
+ int error;
+
+ error = strblobs_load(nameblobs, ag_pptr->name_cookie, name1,
+ ag_pptr->namelen);
+ if (error)
+ do_error(
+ _("loading master-list name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx namelen %u) failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ (unsigned long long)ag_pptr->name_cookie,
+ ag_pptr->namelen,
+ strerror(error));
+
+ error = load_file_pptr_name(fscan, file_pptr, name2);
+ if (error)
+ do_error(
+ _("loading file-list name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx namelen %u) failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ (unsigned long long)file_pptr->name_cookie,
+ ag_pptr->namelen,
+ strerror(error));
+
+ if (ag_pptr->parent_gen != file_pptr->parent_gen)
+ goto reset;
+ if (ag_pptr->namelen != file_pptr->namelen)
+ goto reset;
+ if (ag_pptr->namehash != file_pptr->namehash)
+ goto reset;
+ if (memcmp(name1, name2, ag_pptr->namelen))
+ goto reset;
+
+ return;
+
+reset:
+ if (no_modify) {
+ do_warn(
+ _("would update ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ ag_pptr->namelen,
+ name1);
+ return;
+ }
+
+ do_warn(
+ _("updating ino %llu parent pointer (ino %llu gen 0x%x name '%.*s')\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ ag_pptr->namelen,
+ name1);
+
+ /* XXX do the work */
+}
+
+static int
+cmp_file_to_ag_pptr(
+ const struct file_pptr *fp,
+ const struct ag_pptr *ap)
+{
+ /*
+ * We finished iterating all the pptrs attached to the file before we
+ * ran out of pptrs that we found in the directory scan. Return 1 so
+ * the caller adds the pptr from the dir scan.
+ */
+ if (!fp)
+ return 1;
+
+ if (fp->parent_ino > ap->parent_ino)
+ return 1;
+ if (fp->parent_ino < ap->parent_ino)
+ return -1;
+
+ if (fp->namehash < ap->namehash)
+ return -1;
+ if (fp->namehash > ap->namehash)
+ return 1;
+
+ /*
+ * If this parent pointer wasn't found in the dirent scan, we know it
+ * should be removed.
+ */
+ if (!fp->name_in_nameblobs)
+ return -1;
+
+ if (fp->name_cookie < ap->name_cookie)
+ return -1;
+ if (fp->name_cookie > ap->name_cookie)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Make sure that the parent pointers we observed match the ones ondisk.
+ *
+ * Earlier, we generated a master list of parent pointers for files in this AG
+ * based on what we saw during the directory walk at the start of phase 6.
+ * Now that we've read in all of this file's parent pointers, make sure the
+ * lists match.
+ */
+static void
+crosscheck_file_parent_ptrs(
+ struct xfs_inode *ip,
+ struct file_scan *fscan)
+{
+ struct ag_pptr *ag_pptr;
+ struct file_pptr *file_pptr;
+ struct xfs_mount *mp = ip->i_mount;
+ xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, ip->i_ino);
+ xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ip->i_ino);
+ int error;
+
+ ag_pptr = peek_slab_cursor(fscan->ag_pptr_recs_cur);
+
+ if (!ag_pptr || ag_pptr->child_agino > agino) {
+ /*
+ * The cursor for the master pptr list has gone beyond this
+ * file that we're scanning. Evidently it has no parents at
+ * all, so we better not have found any pptrs attached to the
+ * file.
+ */
+ if (fscan->nr_file_pptrs > 0)
+ clear_all_pptrs(ip);
+
+ return;
+ }
+
+ if (ag_pptr->child_agino < agino) {
+ /*
+ * The cursor for the master pptr list is behind the file that
+ * we're scanning. This suggests that the incore inode tree
+ * doesn't know about a file that is mentioned by a dirent.
+ * At this point the inode liveness is supposed to be settled,
+ * which means our incore information is inconsistent.
+ */
+ do_error(
+ _("found dirent referring to ino %llu even though inobt scan moved on to ino %llu?!\n"),
+ (unsigned long long)XFS_AGINO_TO_INO(mp, agno,
+ ag_pptr->child_agino),
+ (unsigned long long)ip->i_ino);
+ /* does not return */
+ }
+
+ /*
+ * The master pptr list cursor is pointing to the inode that we want
+ * to check. Sort the pptr records that we recorded from the ondisk
+ * pptrs for this file, then set up for the comparison.
+ */
+ qsort_slab(fscan->file_pptr_recs, cmp_file_pptr);
+
+ error = -init_slab_cursor(fscan->file_pptr_recs, cmp_file_pptr,
+ &fscan->file_pptr_recs_cur);
+ if (error)
+ do_error(_("init ino %llu parent pointer cursor failed: %s\n"),
+ (unsigned long long)ip->i_ino, strerror(error));
+
+ do {
+ int cmp_result;
+
+ file_pptr = peek_slab_cursor(fscan->file_pptr_recs_cur);
+
+ dbg_printf(
+ _("%s: dp %llu dp_gen 0x%x namelen %u ino %llu namecookie 0x%llx (master)\n"),
+ __func__,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ ag_pptr->namelen,
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->name_cookie);
+
+ if (file_pptr) {
+ dbg_printf(
+ _("%s: dp %llu dp_gen 0x%x namelen %u ino %llu namecookie 0x%llx (file)\n"),
+ __func__,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ file_pptr->namelen,
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->name_cookie);
+ } else {
+ dbg_printf(
+ _("%s: ran out of parent pointers for ino %llu (file)\n"),
+ __func__,
+ (unsigned long long)ip->i_ino);
+ }
+
+ cmp_result = cmp_file_to_ag_pptr(file_pptr, ag_pptr);
+ if (cmp_result > 0) {
+ /*
+ * The master pptr list knows about pptrs that are not
+ * in the ondisk metadata. Add the missing pptr and
+ * advance only the master pptr cursor.
+ */
+ add_missing_parent_ptr(ip, fscan, ag_pptr);
+ advance_slab_cursor(fscan->ag_pptr_recs_cur);
+ } else if (cmp_result < 0) {
+ /*
+ * The ondisk pptrs mention a link that is not in the
+ * master list. Delete the extra pptr and advance only
+ * the file pptr cursor.
+ */
+ remove_incorrect_parent_ptr(ip, fscan, file_pptr);
+ advance_slab_cursor(fscan->file_pptr_recs_cur);
+ } else {
+ /*
+ * Exact match, make sure the parent_gen and dirent
+ * name parts of the parent pointer match. Move both
+ * cursors forward.
+ */
+ compare_parent_ptrs(ip, fscan, ag_pptr, file_pptr);
+ advance_slab_cursor(fscan->ag_pptr_recs_cur);
+ advance_slab_cursor(fscan->file_pptr_recs_cur);
+ }
+
+ ag_pptr = peek_slab_cursor(fscan->ag_pptr_recs_cur);
+ } while (ag_pptr && ag_pptr->child_agino == agino);
+
+ while ((file_pptr = pop_slab_cursor(fscan->file_pptr_recs_cur))) {
+ dbg_printf(
+ _("%s: dp %llu dp_gen 0x%x namelen %u ino %llu namecookie 0x%llx (excess)\n"),
+ __func__,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ file_pptr->namelen,
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->name_cookie);
+
+ /*
+ * The master pptr list does not have any more pptrs for this
+ * file, but we still have unprocessed ondisk pptrs. Delete
+ * all these ondisk pptrs.
+ */
+ remove_incorrect_parent_ptr(ip, fscan, file_pptr);
+ }
+}
+
+/* Ensure this file's parent pointers match what we found in the dirent scan. */
+static void
+check_file_parent_ptrs(
+ struct xfs_inode *ip,
+ struct file_scan *fscan)
+{
+ int error;
+
+ error = -init_slab(&fscan->file_pptr_recs, sizeof(struct file_pptr));
+ if (error)
+ do_error(_("init file parent pointer recs failed: %s\n"),
+ strerror(error));
+
+ fscan->have_garbage = false;
+ fscan->nr_file_pptrs = 0;
+
+ error = xattr_walk(ip, examine_xattr, fscan);
+ if (error && !no_modify)
+ do_error(_("ino %llu parent pointer scan failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
+ if (error) {
+ do_warn(_("ino %llu parent pointer scan failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
+ goto out_free;
+ }
+
+ crosscheck_file_parent_ptrs(ip, fscan);
+
+out_free:
+ free_slab(&fscan->file_pptr_recs);
+ xfblob_truncate(fscan->file_pptr_names);
+}
+
+/* Check all the parent pointers of files in this AG. */
+static void
+check_ag_parent_ptrs(
+ struct workqueue *wq,
+ uint32_t agno,
+ void *arg)
+{
+ struct xfs_mount *mp = wq->wq_ctx;
+ struct file_scan fscan = {
+ .ag_pptrs = &fs_pptrs[agno],
+ };
+ struct ag_pptrs *ag_pptrs = &fs_pptrs[agno];
+ struct ino_tree_node *irec;
+ int error;
+
+ qsort_slab(ag_pptrs->pptr_recs, cmp_ag_pptr);
+
+ error = -init_slab_cursor(ag_pptrs->pptr_recs, cmp_ag_pptr,
+ &fscan.ag_pptr_recs_cur);
+ if (error)
+ do_error(
+ _("init agno %u parent pointer slab cursor failed: %s\n"),
+ agno, strerror(error));
+
+ error = -xfblob_create(mp, "file parent pointer names",
+ &fscan.file_pptr_names);
+ if (error)
+ do_error(
+ _("init agno %u file parent pointer names failed: %s\n"),
+ agno, strerror(error));
+
+ for (irec = findfirst_inode_rec(agno);
+ irec != NULL;
+ irec = next_ino_rec(irec)) {
+ unsigned int ino_offset;
+
+ for (ino_offset = 0;
+ ino_offset < XFS_INODES_PER_CHUNK;
+ ino_offset++) {
+ struct xfs_inode *ip;
+ xfs_ino_t ino;
+
+ if (is_inode_free(irec, ino_offset))
+ continue;
+
+ ino = XFS_AGINO_TO_INO(mp, agno,
+ irec->ino_startnum + ino_offset);
+ error = -libxfs_iget(mp, NULL, ino, 0, &ip);
+ if (error && !no_modify)
+ do_error(
+ _("loading ino %llu for parent pointer check failed: %s\n"),
+ (unsigned long long)ino,
+ strerror(error));
+ if (error) {
+ do_warn(
+ _("loading ino %llu for parent pointer check failed: %s\n"),
+ (unsigned long long)ino,
+ strerror(error));
+ continue;
+ }
+
+ check_file_parent_ptrs(ip, &fscan);
+ libxfs_irele(ip);
+ }
+ }
+
+ xfblob_destroy(fscan.file_pptr_names);
+ free_slab_cursor(&fscan.ag_pptr_recs_cur);
+}
+
+/* Check all the parent pointers of all files in this filesystem. */
+void
+check_parent_ptrs(
+ struct xfs_mount *mp)
+{
+ struct workqueue wq;
+ xfs_agnumber_t agno;
+
+ if (!xfs_has_parent(mp))
+ return;
+
+ create_work_queue(&wq, mp, ag_stride);
+
+ for (agno = 0; agno < mp->m_sb.sb_agcount; agno++)
+ queue_work(&wq, check_ag_parent_ptrs, agno, NULL);
+
+ destroy_work_queue(&wq);
+}
diff --git a/repair/pptr.h b/repair/pptr.h
index 0ca2e1c6f..1cf3444c8 100644
--- a/repair/pptr.h
+++ b/repair/pptr.h
@@ -12,4 +12,6 @@ void parent_ptr_init(struct xfs_mount *mp);
void add_parent_ptr(xfs_ino_t ino, const unsigned char *fname,
struct xfs_inode *dp);
+void check_parent_ptrs(struct xfs_mount *mp);
+
#endif /* __REPAIR_PPTR_H__ */
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 5/7] xfs_repair: dump garbage parent pointer attributes
2023-04-06 19:16 ` [PATCHSET v11 0/7] xfs_repair: support " Darrick J. Wong
` (3 preceding siblings ...)
2023-04-06 19:40 ` [PATCH 4/7] xfs_repair: check parent pointers Darrick J. Wong
@ 2023-04-06 19:41 ` Darrick J. Wong
2023-04-06 19:41 ` [PATCH 6/7] xfs_repair: update ondisk parent pointer records Darrick J. Wong
2023-04-06 19:41 ` [PATCH 7/7] xfs_repair: wipe ondisk parent pointers when there are none Darrick J. Wong
6 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:41 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Delete xattrs that have ATTR_PARENT set but are so garbage that they
clearly aren't parent pointers.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
repair/pptr.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 140 insertions(+), 2 deletions(-)
diff --git a/repair/pptr.c b/repair/pptr.c
index 8aab94d74..8506405a1 100644
--- a/repair/pptr.c
+++ b/repair/pptr.c
@@ -191,6 +191,29 @@ struct file_scan {
/* Does this file have garbage xattrs with ATTR_PARENT set? */
bool have_garbage;
+
+ /* xattrs that we have to remove from this file */
+ struct xfs_slab *garbage_xattr_recs;
+
+ /* attr names associated with garbage_xattr_recs */
+ struct xfblob *garbage_xattr_names;
+};
+
+struct garbage_xattr {
+ /* xfs_da_args.attr_filter for the attribute being removed */
+ unsigned int attr_filter;
+
+ /* attribute name length */
+ unsigned int attrnamelen;
+
+ /* attribute value length */
+ unsigned int attrvaluelen;
+
+ /* cookie for the attribute name */
+ xfblob_cookie attrname_cookie;
+
+ /* cookie for the attribute value */
+ xfblob_cookie attrvalue_cookie;
};
/* Global names storage file. */
@@ -375,6 +398,78 @@ add_parent_ptr(
(unsigned long long)ag_pptr.name_cookie);
}
+/* Remove garbage extended attributes that have ATTR_PARENT set. */
+static void
+remove_garbage_xattrs(
+ struct xfs_inode *ip,
+ struct file_scan *fscan)
+{
+ struct xfs_slab_cursor *cur;
+ struct garbage_xattr *ga;
+ void *buf = NULL;
+ size_t bufsize = 0;
+ int error;
+
+ error = -init_slab_cursor(fscan->garbage_xattr_recs, NULL, &cur);
+ if (error)
+ do_error(_("init garbage xattr cursor failed: %s\n"),
+ strerror(error));
+
+ while ((ga = pop_slab_cursor(cur)) != NULL) {
+ struct xfs_da_args args = {
+ .dp = ip,
+ .attr_filter = ga->attr_filter,
+ .namelen = ga->attrnamelen,
+ .valuelen = ga->attrvaluelen,
+ .op_flags = XFS_DA_OP_REMOVE | XFS_DA_OP_NVLOOKUP,
+ };
+ size_t desired = ga->attrnamelen + ga->attrvaluelen;
+
+ if (desired > bufsize) {
+ free(buf);
+ buf = malloc(desired);
+ if (!buf)
+ do_error(
+ _("allocating %zu bytes to remove ino %llu garbage xattr failed: %s\n"),
+ desired,
+ (unsigned long long)ip->i_ino,
+ strerror(errno));
+ bufsize = desired;
+ }
+
+ args.name = buf;
+ args.value = buf + ga->attrnamelen;
+
+ error = -xfblob_load(fscan->garbage_xattr_names,
+ ga->attrname_cookie, buf, ga->attrnamelen);
+ if (error)
+ do_error(
+ _("loading garbage xattr name failed: %s\n"),
+ strerror(error));
+
+ error = -xfblob_load(fscan->garbage_xattr_names,
+ ga->attrvalue_cookie, args.value,
+ ga->attrvaluelen);
+ if (error)
+ do_error(
+ _("loading garbage xattr value failed: %s\n"),
+ strerror(error));
+
+ error = -libxfs_attr_set(&args);
+ if (error)
+ do_error(
+ _("removing ino %llu garbage xattr failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
+ }
+
+ free(buf);
+ free_slab_cursor(&cur);
+ free_slab(&fscan->garbage_xattr_recs);
+ xfblob_destroy(fscan->garbage_xattr_names);
+ fscan->garbage_xattr_names = NULL;
+}
+
/* Schedule this ATTR_PARENT extended attribute for deletion. */
static void
record_garbage_xattr(
@@ -386,6 +481,14 @@ record_garbage_xattr(
const void *value,
unsigned int valuelen)
{
+ struct garbage_xattr garbage_xattr = {
+ .attr_filter = attr_filter,
+ .attrnamelen = namelen,
+ .attrvaluelen = valuelen,
+ };
+ struct xfs_mount *mp = ip->i_mount;
+ int error;
+
if (no_modify) {
if (!fscan->have_garbage)
do_warn(
@@ -396,13 +499,45 @@ record_garbage_xattr(
}
if (fscan->have_garbage)
- return;
+ goto stuffit;
fscan->have_garbage = true;
do_warn(
_("deleting garbage parent pointer extended attributes in ino %llu\n"),
(unsigned long long)ip->i_ino);
- /* XXX do the work */
+
+ error = -init_slab(&fscan->garbage_xattr_recs,
+ sizeof(struct garbage_xattr));
+ if (error)
+ do_error(_("init garbage xattr recs failed: %s\n"),
+ strerror(error));
+
+ error = -xfblob_create(mp, "garbage xattr names",
+ &fscan->garbage_xattr_names);
+ if (error)
+ do_error("init garbage xattr names failed: %s\n",
+ strerror(error));
+
+stuffit:
+ error = -xfblob_store(fscan->garbage_xattr_names,
+ &garbage_xattr.attrname_cookie, name, namelen);
+ if (error)
+ do_error(_("storing ino %llu garbage xattr failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
+
+ error = -xfblob_store(fscan->garbage_xattr_names,
+ &garbage_xattr.attrvalue_cookie, value, valuelen);
+ if (error)
+ do_error(_("storing ino %llu garbage xattr failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
+
+ error = -slab_add(fscan->garbage_xattr_recs, &garbage_xattr);
+ if (error)
+ do_error(_("storing ino %llu garbage xattr rec failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
}
/*
@@ -922,6 +1057,9 @@ check_file_parent_ptrs(
goto out_free;
}
+ if (!no_modify && fscan->have_garbage)
+ remove_garbage_xattrs(ip, fscan);
+
crosscheck_file_parent_ptrs(ip, fscan);
out_free:
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 6/7] xfs_repair: update ondisk parent pointer records
2023-04-06 19:16 ` [PATCHSET v11 0/7] xfs_repair: support " Darrick J. Wong
` (4 preceding siblings ...)
2023-04-06 19:41 ` [PATCH 5/7] xfs_repair: dump garbage parent pointer attributes Darrick J. Wong
@ 2023-04-06 19:41 ` Darrick J. Wong
2023-04-06 19:41 ` [PATCH 7/7] xfs_repair: wipe ondisk parent pointers when there are none Darrick J. Wong
6 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:41 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Update the ondisk parent pointer records as necessary.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
libxfs/libxfs_api_defs.h | 3 ++
repair/pptr.c | 85 ++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 85 insertions(+), 3 deletions(-)
diff --git a/libxfs/libxfs_api_defs.h b/libxfs/libxfs_api_defs.h
index fb1865483..621768965 100644
--- a/libxfs/libxfs_api_defs.h
+++ b/libxfs/libxfs_api_defs.h
@@ -147,9 +147,12 @@
#define xfs_parent_add libxfs_parent_add
#define xfs_parent_finish libxfs_parent_finish
#define xfs_parent_irec_from_disk libxfs_parent_irec_from_disk
+#define xfs_parent_irec_hashname libxfs_parent_irec_hashname
+#define xfs_parent_set libxfs_parent_set
#define xfs_parent_start libxfs_parent_start
#define xfs_parent_namecheck libxfs_parent_namecheck
#define xfs_parent_valuecheck libxfs_parent_valuecheck
+#define xfs_parent_unset libxfs_parent_unset
#define xfs_perag_get libxfs_perag_get
#define xfs_perag_put libxfs_perag_put
#define xfs_prealloc_blocks libxfs_prealloc_blocks
diff --git a/repair/pptr.c b/repair/pptr.c
index 8506405a1..1a0836ed4 100644
--- a/repair/pptr.c
+++ b/repair/pptr.c
@@ -674,6 +674,44 @@ load_file_pptr_name(
name, file_pptr->namelen);
}
+/* Add an on disk parent pointer to a file. */
+static int
+add_file_pptr(
+ struct xfs_inode *ip,
+ const struct ag_pptr *ag_pptr,
+ const unsigned char *name)
+{
+ struct xfs_parent_name_irec pptr_rec = {
+ .p_ino = ag_pptr->parent_ino,
+ .p_gen = ag_pptr->parent_gen,
+ .p_namelen = ag_pptr->namelen,
+ };
+ struct xfs_parent_scratch scratch;
+
+ memcpy(pptr_rec.p_name, name, ag_pptr->namelen);
+ libxfs_parent_irec_hashname(ip->i_mount, &pptr_rec);
+ return -libxfs_parent_set(ip, &pptr_rec, &scratch);
+}
+
+/* Remove an on disk parent pointer from a file. */
+static int
+remove_file_pptr(
+ struct xfs_inode *ip,
+ const struct file_pptr *file_pptr,
+ const unsigned char *name)
+{
+ struct xfs_parent_name_irec pptr_rec = {
+ .p_ino = file_pptr->parent_ino,
+ .p_gen = file_pptr->parent_gen,
+ .p_namelen = file_pptr->namelen,
+ };
+ struct xfs_parent_scratch scratch;
+
+ memcpy(pptr_rec.p_name, name, file_pptr->namelen);
+ libxfs_parent_irec_hashname(ip->i_mount, &pptr_rec);
+ return -libxfs_parent_unset(ip, &pptr_rec, &scratch);
+}
+
/* Remove all pptrs from @ip. */
static void
clear_all_pptrs(
@@ -730,7 +768,16 @@ add_missing_parent_ptr(
ag_pptr->namelen,
name);
- /* XXX actually do the work */
+ error = add_file_pptr(ip, ag_pptr, name);
+ if (error)
+ do_error(
+ _("adding ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ ag_pptr->namelen,
+ name,
+ strerror(error));
}
/* Remove @file_pptr from @ip. */
@@ -772,7 +819,16 @@ remove_incorrect_parent_ptr(
file_pptr->namelen,
name);
- /* XXX actually do the work */
+ error = remove_file_pptr(ip, file_pptr, name);
+ if (error)
+ do_error(
+ _("removing ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ file_pptr->namelen,
+ name,
+ strerror(error));
}
/*
@@ -844,7 +900,30 @@ compare_parent_ptrs(
ag_pptr->namelen,
name1);
- /* XXX do the work */
+ if (ag_pptr->parent_gen != file_pptr->parent_gen ||
+ ag_pptr->namehash != file_pptr->namehash) {
+ error = remove_file_pptr(ip, file_pptr, name2);
+ if (error)
+ do_error(
+ _("erasing ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ file_pptr->namelen,
+ name2,
+ strerror(error));
+ }
+
+ error = add_file_pptr(ip, ag_pptr, name1);
+ if (error)
+ do_error(
+ _("updating ino %llu pptr (ino %llu gen 0x%x name '%.*s') failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)ag_pptr->parent_ino,
+ ag_pptr->parent_gen,
+ ag_pptr->namelen,
+ name1,
+ strerror(error));
}
static int
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 7/7] xfs_repair: wipe ondisk parent pointers when there are none
2023-04-06 19:16 ` [PATCHSET v11 0/7] xfs_repair: support " Darrick J. Wong
` (5 preceding siblings ...)
2023-04-06 19:41 ` [PATCH 6/7] xfs_repair: update ondisk parent pointer records Darrick J. Wong
@ 2023-04-06 19:41 ` Darrick J. Wong
6 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:41 UTC (permalink / raw)
To: djwong; +Cc: allison.henderson, linux-xfs
From: Darrick J. Wong <djwong@kernel.org>
Erase all the parent pointers when there aren't any found by the
directory entry scan.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
repair/pptr.c | 41 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 38 insertions(+), 3 deletions(-)
diff --git a/repair/pptr.c b/repair/pptr.c
index 1a0836ed4..980d367ac 100644
--- a/repair/pptr.c
+++ b/repair/pptr.c
@@ -715,8 +715,13 @@ remove_file_pptr(
/* Remove all pptrs from @ip. */
static void
clear_all_pptrs(
- struct xfs_inode *ip)
+ struct xfs_inode *ip,
+ struct file_scan *fscan)
{
+ struct xfs_slab_cursor *cur;
+ struct file_pptr *file_pptr;
+ int error;
+
if (no_modify) {
do_warn(_("would delete unlinked ino %llu parent pointers\n"),
(unsigned long long)ip->i_ino);
@@ -725,7 +730,37 @@ clear_all_pptrs(
do_warn(_("deleting unlinked ino %llu parent pointers\n"),
(unsigned long long)ip->i_ino);
- /* XXX actually do the work */
+
+ error = -init_slab_cursor(fscan->file_pptr_recs, NULL, &cur);
+ if (error)
+ do_error(_("init ino %llu pptr cursor failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ strerror(error));
+
+ while ((file_pptr = pop_slab_cursor(cur)) != NULL) {
+ unsigned char name[MAXNAMELEN];
+
+ error = load_file_pptr_name(fscan, file_pptr, name);
+ if (error)
+ do_error(
+ _("loading incorrect name for ino %llu parent pointer (ino %llu gen 0x%x namecookie 0x%llx) failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ (unsigned long long)file_pptr->name_cookie,
+ strerror(error));
+
+ error = remove_file_pptr(ip, file_pptr, name);
+ if (error)
+ do_error(
+ _("wiping ino %llu pptr (ino %llu gen 0x%x) failed: %s\n"),
+ (unsigned long long)ip->i_ino,
+ (unsigned long long)file_pptr->parent_ino,
+ file_pptr->parent_gen,
+ strerror(error));
+ }
+
+ free_slab_cursor(&cur);
}
/* Add @ag_pptr to @ip. */
@@ -994,7 +1029,7 @@ crosscheck_file_parent_ptrs(
* file.
*/
if (fscan->nr_file_pptrs > 0)
- clear_all_pptrs(ip);
+ clear_all_pptrs(ip, fscan);
return;
}
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 01/11] xfs/206: filter out the parent= status from mkfs
2023-04-06 19:16 ` [PATCHSET v11 00/11] fstests: adjust tests for xfs parent pointers Darrick J. Wong
@ 2023-04-06 19:41 ` Darrick J. Wong
2023-04-06 19:42 ` [PATCH 02/11] xfs/122: update for parent pointers Darrick J. Wong
` (9 subsequent siblings)
10 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:41 UTC (permalink / raw)
To: zlang, djwong; +Cc: linux-xfs, fstests, guan
From: Darrick J. Wong <djwong@kernel.org>
Filter out the parent pointer bits from the mkfs output so that we don't
cause a regression in this test.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
tests/xfs/206 | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tests/xfs/206 b/tests/xfs/206
index cb346b6dc9..af86570a81 100755
--- a/tests/xfs/206
+++ b/tests/xfs/206
@@ -64,7 +64,8 @@ mkfs_filter()
-e "s/\(sunit=\)\([0-9]* blks,\)/\10 blks,/" \
-e "s/, lazy-count=[0-9]//" \
-e "/.*crc=/d" \
- -e "/^Default configuration/d"
+ -e "/^Default configuration/d" \
+ -e '/parent=/d'
}
# mkfs slightly smaller than that, small log for speed.
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 02/11] xfs/122: update for parent pointers
2023-04-06 19:16 ` [PATCHSET v11 00/11] fstests: adjust tests for xfs parent pointers Darrick J. Wong
2023-04-06 19:41 ` [PATCH 01/11] xfs/206: filter out the parent= status from mkfs Darrick J. Wong
@ 2023-04-06 19:42 ` Darrick J. Wong
2023-04-06 19:42 ` [PATCH 03/11] populate: create hardlinks " Darrick J. Wong
` (8 subsequent siblings)
10 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:42 UTC (permalink / raw)
To: zlang, djwong; +Cc: linux-xfs, fstests, guan
From: Darrick J. Wong <djwong@kernel.org>
Update test for parent pointers.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
tests/xfs/122.out | 3 +++
1 file changed, 3 insertions(+)
diff --git a/tests/xfs/122.out b/tests/xfs/122.out
index 89f7b735b0..55138218dd 100644
--- a/tests/xfs/122.out
+++ b/tests/xfs/122.out
@@ -98,6 +98,8 @@ sizeof(struct xfs_fsop_ag_resblks) = 64
sizeof(struct xfs_fsop_geom) = 256
sizeof(struct xfs_fsop_geom_v1) = 112
sizeof(struct xfs_fsop_geom_v4) = 112
+sizeof(struct xfs_getparents) = 96
+sizeof(struct xfs_getparents_rec) = 24
sizeof(struct xfs_icreate_log) = 28
sizeof(struct xfs_inode_log_format) = 56
sizeof(struct xfs_inode_log_format_32) = 52
@@ -107,6 +109,7 @@ sizeof(struct xfs_legacy_timestamp) = 8
sizeof(struct xfs_log_dinode) = 176
sizeof(struct xfs_log_legacy_timestamp) = 8
sizeof(struct xfs_map_extent) = 32
+sizeof(struct xfs_parent_name_rec) = 16
sizeof(struct xfs_phys_extent) = 16
sizeof(struct xfs_refcount_key) = 4
sizeof(struct xfs_refcount_rec) = 12
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 03/11] populate: create hardlinks for parent pointers
2023-04-06 19:16 ` [PATCHSET v11 00/11] fstests: adjust tests for xfs parent pointers Darrick J. Wong
2023-04-06 19:41 ` [PATCH 01/11] xfs/206: filter out the parent= status from mkfs Darrick J. Wong
2023-04-06 19:42 ` [PATCH 02/11] xfs/122: update for parent pointers Darrick J. Wong
@ 2023-04-06 19:42 ` Darrick J. Wong
2023-04-06 19:42 ` [PATCH 04/11] xfs/021: adapt golden output files " Darrick J. Wong
` (7 subsequent siblings)
10 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:42 UTC (permalink / raw)
To: zlang, djwong; +Cc: linux-xfs, fstests, guan
From: Darrick J. Wong <djwong@kernel.org>
Create some hardlinked files so that we can exercise parent pointers.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
common/populate | 38 ++++++++++++++++++++++++++++++++++++++
src/popdir.pl | 11 +++++++++++
2 files changed, 49 insertions(+)
diff --git a/common/populate b/common/populate
index 3d233073c9..c75253ff14 100644
--- a/common/populate
+++ b/common/populate
@@ -450,6 +450,44 @@ _scratch_xfs_populate() {
cp --reflink=always "${SCRATCH_MNT}/REFCOUNTBT" "${SCRATCH_MNT}/REFCOUNTBT2"
fi
+ # Parent pointers
+ is_pptr="$(_xfs_has_feature "$SCRATCH_MNT" parent -v)"
+ if [ $is_pptr -gt 0 ]; then
+ echo "+ parent pointers"
+
+ # Create a couple of parent pointers
+ __populate_create_dir "${SCRATCH_MNT}/PPTRS" 1 '' --hardlink --format "two_%d"
+
+ # Create one xattr leaf block of parent pointers
+ nr="$((blksz * 2 / 16))"
+ __populate_create_dir "${SCRATCH_MNT}/PPTRS" ${nr} '' --hardlink --format "many%04d"
+
+ # Create multiple xattr leaf blocks of large parent pointers
+ nr="$((blksz * 16 / 16))"
+ __populate_create_dir "${SCRATCH_MNT}/PPTRS" ${nr} '' --hardlink --format "y%0254d"
+
+ # Create multiple paths to a file
+ local moof="${SCRATCH_MNT}/PPTRS/moofile"
+ touch "${moof}"
+ for ((i = 0; i < 4; i++)); do
+ mkdir -p "${SCRATCH_MNT}/PPTRS/SUB${i}"
+ ln "${moof}" "${SCRATCH_MNT}/PPTRS/SUB${i}/moofile"
+ done
+
+ # Create parent pointers of various lengths
+ touch "${SCRATCH_MNT}/PPTRS/vlength"
+ local len_len
+ local tst
+ local fname
+ ln "${SCRATCH_MNT}/PPTRS/vlength" "${SCRATCH_MNT}/PPTRS/b"
+ for len in 32 64 96 128 160 192 224 250 255; do
+ len_len="${#len}"
+ tst="$(perl -e "print \"b\" x (${len} - (${len_len} + 1))")"
+ fname="v${tst}${len}"
+ ln "${SCRATCH_MNT}/PPTRS/vlength" "${SCRATCH_MNT}/PPTRS/${fname}"
+ done
+ fi
+
# Copy some real files (xfs tests, I guess...)
echo "+ real files"
test $fill -ne 0 && __populate_fill_fs "${SCRATCH_MNT}" 5
diff --git a/src/popdir.pl b/src/popdir.pl
index e89095aafe..0104957a3c 100755
--- a/src/popdir.pl
+++ b/src/popdir.pl
@@ -17,6 +17,7 @@ GetOptions("start=i" => \$start,
"dir=s" => \$dir,
"remove!" => \$remove,
"help!" => \$help,
+ "hardlink!" => \$hardlink,
"verbose!" => \$verbose);
@@ -35,6 +36,7 @@ Options:
--format=str printf formatting string for file name ("%08d")
--verbose verbose output
--help this help screen
+ --hardlink hardlink subsequent files to the first one created
EOF
exit(1) unless defined $help;
# otherwise...
@@ -56,12 +58,21 @@ if ($file_pct < 0) {
$file_pct = 100;
}
+if ($hardlink) {
+ $file_pct = 100;
+ $link_fname = sprintf($format, $start);
+}
+
for ($i = $start; $i <= $end; $i += $incr) {
$fname = sprintf($format, $i);
if ($remove) {
$verbose && print "rm $fname\n";
unlink($fname) or rmdir($fname) or die("unlink $fname");
+ } elsif ($hardlink && $i > $start) {
+ # hardlink everything after the first file
+ $verbose && print "ln $link_fname $fname\n";
+ link $link_fname, $fname;
} elsif (($i % 100) < $file_pct) {
# create a file
$verbose && print "touch $fname\n";
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 04/11] xfs/021: adapt golden output files for parent pointers
2023-04-06 19:16 ` [PATCHSET v11 00/11] fstests: adjust tests for xfs parent pointers Darrick J. Wong
` (2 preceding siblings ...)
2023-04-06 19:42 ` [PATCH 03/11] populate: create hardlinks " Darrick J. Wong
@ 2023-04-06 19:42 ` Darrick J. Wong
2023-04-06 19:43 ` [PATCH 05/11] generic/050: adapt " Darrick J. Wong
` (6 subsequent siblings)
10 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:42 UTC (permalink / raw)
To: zlang, djwong; +Cc: linux-xfs, fstests, guan
From: Darrick J. Wong <djwong@kernel.org>
Parent pointers change the xattr structure dramatically, so fix this
test to handle them. For the most part we can get away with filtering
out the parent pointer fields (which xfs_db decodes for us), but the
namelen/valuelen/attr_filter fields still show through.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
common/rc | 4 +++
tests/xfs/021 | 15 +++++++++--
tests/xfs/021.cfg | 1 +
tests/xfs/021.out.default | 0
tests/xfs/021.out.parent | 62 +++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 80 insertions(+), 2 deletions(-)
create mode 100644 tests/xfs/021.cfg
rename tests/xfs/{021.out => 021.out.default} (100%)
create mode 100644 tests/xfs/021.out.parent
diff --git a/common/rc b/common/rc
index 58fa580a55..a7e7d1f25b 100644
--- a/common/rc
+++ b/common/rc
@@ -3307,6 +3307,8 @@ _get_os_name()
_link_out_file_named()
{
+ test -n "$seqfull" || _fail "need to set seqfull"
+
local features=$2
local suffix=$(FEATURES="$features" perl -e '
my %feathash;
@@ -3342,6 +3344,8 @@ _link_out_file()
{
local features
+ test -n "$seqfull" || _fail "need to set seqfull"
+
if [ $# -eq 0 ]; then
features="$(_get_os_name),$FSTYP"
if [ -n "$MOUNT_OPTIONS" ]; then
diff --git a/tests/xfs/021 b/tests/xfs/021
index 9432e2acb0..ef307fc064 100755
--- a/tests/xfs/021
+++ b/tests/xfs/021
@@ -67,6 +67,13 @@ _scratch_mkfs_xfs >/dev/null \
echo "*** mount FS"
_scratch_mount
+seqfull=$0
+if _xfs_has_feature $SCRATCH_MNT parent; then
+ _link_out_file "parent"
+else
+ _link_out_file ""
+fi
+
testfile=$SCRATCH_MNT/testfile
echo "*** make test file 1"
@@ -108,7 +115,10 @@ _scratch_unmount >>$seqres.full 2>&1 \
echo "*** dump attributes (1)"
_scratch_xfs_db -r -c "inode $inum_1" -c "print a.sfattr" | \
- sed -e '/secure = /d' | sed -e '/parent = /d'
+ perl -ne '
+/\.secure/ && next;
+/\.parent/ && next;
+ print unless /^\d+:\[.*/;'
echo "*** dump attributes (2)"
@@ -124,10 +134,11 @@ s/info.hdr/info/;
/hdr.info.uuid/ && next;
/hdr.info.lsn/ && next;
/hdr.info.owner/ && next;
+/\.parent/ && next;
s/^(hdr.info.magic =) 0x3bee/\1 0xfbee/;
s/^(hdr.firstused =) (\d+)/\1 FIRSTUSED/;
s/^(hdr.freemap\[0-2] = \[base,size]).*/\1 [FREEMAP..]/;
-s/^(entries\[0-2] = \[hashval,nameidx,incomplete,root,local]).*/\1 [ENTRIES..]/;
+s/^(entries\[0-[23]] = \[hashval,nameidx,incomplete,root,local]).*/\1 [ENTRIES..]/;
print unless /^\d+:\[.*/;'
echo "*** done"
diff --git a/tests/xfs/021.cfg b/tests/xfs/021.cfg
new file mode 100644
index 0000000000..73b127260c
--- /dev/null
+++ b/tests/xfs/021.cfg
@@ -0,0 +1 @@
+parent: parent
diff --git a/tests/xfs/021.out b/tests/xfs/021.out.default
similarity index 100%
rename from tests/xfs/021.out
rename to tests/xfs/021.out.default
diff --git a/tests/xfs/021.out.parent b/tests/xfs/021.out.parent
new file mode 100644
index 0000000000..1af1301061
--- /dev/null
+++ b/tests/xfs/021.out.parent
@@ -0,0 +1,62 @@
+QA output created by 021
+*** mkfs
+*** mount FS
+*** make test file 1
+# file: <TESTFILE>.1
+user.a1
+user.a2--
+
+*** make test file 2
+1+0 records in
+1+0 records out
+# file: <TESTFILE>.2
+user.a1
+user.a2-----
+user.a3
+
+Attribute "a3" had a 65535 byte value for <TESTFILE>.2:
+size of attr value = 65536
+
+*** unmount FS
+*** dump attributes (1)
+a.sfattr.hdr.totsize = 53
+a.sfattr.hdr.count = 3
+a.sfattr.list[0].namelen = 16
+a.sfattr.list[0].valuelen = 10
+a.sfattr.list[0].root = 0
+a.sfattr.list[1].namelen = 2
+a.sfattr.list[1].valuelen = 3
+a.sfattr.list[1].root = 0
+a.sfattr.list[1].name = "a1"
+a.sfattr.list[1].value = "v1\d"
+a.sfattr.list[2].namelen = 4
+a.sfattr.list[2].valuelen = 5
+a.sfattr.list[2].root = 0
+a.sfattr.list[2].name = "a2--"
+a.sfattr.list[2].value = "v2--\d"
+*** dump attributes (2)
+hdr.info.forw = 0
+hdr.info.back = 0
+hdr.info.magic = 0xfbee
+hdr.count = 4
+hdr.usedbytes = 84
+hdr.firstused = FIRSTUSED
+hdr.holes = 0
+hdr.freemap[0-2] = [base,size] [FREEMAP..]
+entries[0-3] = [hashval,nameidx,incomplete,root,local] [ENTRIES..]
+nvlist[0].valuelen = 8
+nvlist[0].namelen = 2
+nvlist[0].name = "a1"
+nvlist[0].value = "value_1\d"
+nvlist[1].valueblk = 0x1
+nvlist[1].valuelen = 65535
+nvlist[1].namelen = 2
+nvlist[1].name = "a3"
+nvlist[2].valuelen = 10
+nvlist[2].namelen = 16
+nvlist[3].valuelen = 8
+nvlist[3].namelen = 7
+nvlist[3].name = "a2-----"
+nvlist[3].value = "value_2\d"
+*** done
+*** unmount
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 05/11] generic/050: adapt for parent pointers
2023-04-06 19:16 ` [PATCHSET v11 00/11] fstests: adjust tests for xfs parent pointers Darrick J. Wong
` (3 preceding siblings ...)
2023-04-06 19:42 ` [PATCH 04/11] xfs/021: adapt golden output files " Darrick J. Wong
@ 2023-04-06 19:43 ` Darrick J. Wong
2023-04-06 19:43 ` [PATCH 06/11] xfs/018: disable parent pointers for this test Darrick J. Wong
` (5 subsequent siblings)
10 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:43 UTC (permalink / raw)
To: zlang, djwong; +Cc: linux-xfs, fstests, guan
From: Darrick J. Wong <djwong@kernel.org>
Fix this test when quotas and parent pointers are enabled.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
tests/generic/050 | 10 ++++++++++
tests/generic/050.cfg | 1 +
tests/generic/050.out.xfsquotaparent | 23 +++++++++++++++++++++++
3 files changed, 34 insertions(+)
create mode 100644 tests/generic/050.out.xfsquotaparent
diff --git a/tests/generic/050 b/tests/generic/050
index 0664f8c0e4..b38401b9fc 100755
--- a/tests/generic/050
+++ b/tests/generic/050
@@ -36,6 +36,16 @@ elif [ "$FSTYP" = "xfs" ] && echo "$MOUNT_OPTIONS" | grep -q quota ; then
# Mounting with quota on XFS requires a writable fs, which means
# we expect to fail the ro blockdev test with with EPERM.
features="xfsquota"
+
+ if _xfs_has_feature $SCRATCH_DEV parent && \
+ ! _xfs_has_feature $SCRATCH_DEV realtime; then
+ # If we have quotas and parent pointers enabled, the primary
+ # superblock will be written out with the quota flags set when
+ # the logged xattrs log_incompat feature is set. Hence the
+ # norecovery mount won't fail due to quota rejecting the
+ # mismatch between the mount qflags and the ondisk ones.
+ features="xfsquotaparent"
+ fi
fi
_link_out_file "$features"
diff --git a/tests/generic/050.cfg b/tests/generic/050.cfg
index 1d9d60bc69..85924d117d 100644
--- a/tests/generic/050.cfg
+++ b/tests/generic/050.cfg
@@ -1,2 +1,3 @@
nojournal: nojournal
xfsquota: xfsquota
+xfsquotaparent: xfsquotaparent
diff --git a/tests/generic/050.out.xfsquotaparent b/tests/generic/050.out.xfsquotaparent
new file mode 100644
index 0000000000..b341aca5be
--- /dev/null
+++ b/tests/generic/050.out.xfsquotaparent
@@ -0,0 +1,23 @@
+QA output created by 050
+setting device read-only
+mounting read-only block device:
+mount: SCRATCH_MNT: permission denied
+unmounting read-only filesystem
+umount: SCRATCH_DEV: not mounted
+setting device read-write
+mounting read-write block device:
+touch files
+going down:
+unmounting shutdown filesystem:
+setting device read-only
+mounting filesystem that needs recovery on a read-only device:
+mount: device write-protected, mounting read-only
+mount: cannot mount device read-only
+unmounting read-only filesystem
+umount: SCRATCH_DEV: not mounted
+mounting filesystem with -o norecovery on a read-only device:
+mount: device write-protected, mounting read-only
+unmounting read-only filesystem
+setting device read-write
+mounting filesystem that needs recovery with -o ro:
+*** done
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 06/11] xfs/018: disable parent pointers for this test
2023-04-06 19:16 ` [PATCHSET v11 00/11] fstests: adjust tests for xfs parent pointers Darrick J. Wong
` (4 preceding siblings ...)
2023-04-06 19:43 ` [PATCH 05/11] generic/050: adapt " Darrick J. Wong
@ 2023-04-06 19:43 ` Darrick J. Wong
2023-04-06 19:43 ` [PATCH 07/11] xfs/306: fix formatting failures with parent pointers Darrick J. Wong
` (4 subsequent siblings)
10 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:43 UTC (permalink / raw)
To: zlang, djwong; +Cc: linux-xfs, fstests, guan
From: Darrick J. Wong <djwong@kernel.org>
This test depends heavily on the xattr formats created for new files.
Parent pointers break those assumptions, so force parent pointers off.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
tests/xfs/018 | 7 ++++++-
tests/xfs/191 | 7 ++++++-
tests/xfs/288 | 7 ++++++-
3 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/tests/xfs/018 b/tests/xfs/018
index 1ef51a2e61..34b6e91579 100755
--- a/tests/xfs/018
+++ b/tests/xfs/018
@@ -100,7 +100,12 @@ attr32l="X$attr32k"
attr64k="$attr32k$attr32k"
echo "*** mkfs"
-_scratch_mkfs >/dev/null
+
+# Parent pointers change the xattr formats sufficiently to break this test.
+# Disable parent pointers if mkfs supports it.
+mkfs_args=()
+$MKFS_XFS_PROG 2>&1 | grep -q parent=0 && mkfs_args+=(-n parent=0)
+_scratch_mkfs "${mkfs_args[@]}" >/dev/null
blk_sz=$(_scratch_xfs_get_sb_field blocksize)
err_inj_attr_sz=$(( blk_sz / 3 - 50 ))
diff --git a/tests/xfs/191 b/tests/xfs/191
index 7a02f1be21..0a6c20dad7 100755
--- a/tests/xfs/191
+++ b/tests/xfs/191
@@ -33,7 +33,12 @@ _fixed_by_kernel_commit 7be3bd8856fb "xfs: empty xattr leaf header blocks are no
_fixed_by_kernel_commit e87021a2bc10 "xfs: use larger in-core attr firstused field and detect overflow"
_fixed_by_git_commit xfsprogs f50d3462c654 "xfs_repair: ignore empty xattr leaf blocks"
-_scratch_mkfs_xfs | _filter_mkfs >$seqres.full 2>$tmp.mkfs
+# Parent pointers change the xattr formats sufficiently to break this test.
+# Disable parent pointers if mkfs supports it.
+mkfs_args=()
+$MKFS_XFS_PROG 2>&1 | grep -q parent=0 && mkfs_args+=(-n parent=0)
+
+_scratch_mkfs_xfs "${mkfs_args[@]}" | _filter_mkfs >$seqres.full 2>$tmp.mkfs
cat $tmp.mkfs >> $seqres.full
source $tmp.mkfs
_scratch_mount
diff --git a/tests/xfs/288 b/tests/xfs/288
index aa664a266e..6bfc9ac0c8 100755
--- a/tests/xfs/288
+++ b/tests/xfs/288
@@ -19,8 +19,13 @@ _supported_fs xfs
_require_scratch
_require_attrs
+# Parent pointers change the xattr formats sufficiently to break this test.
+# Disable parent pointers if mkfs supports it.
+mkfs_args=()
+$MKFS_XFS_PROG 2>&1 | grep -q parent=0 && mkfs_args+=(-n parent=0)
+
# get block size ($dbsize) from the mkfs output
-_scratch_mkfs_xfs 2>/dev/null | _filter_mkfs 2>$tmp.mkfs >/dev/null
+_scratch_mkfs_xfs "${mkfs_args[@]}" 2>/dev/null | _filter_mkfs 2>$tmp.mkfs >/dev/null
. $tmp.mkfs
_scratch_mount
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 07/11] xfs/306: fix formatting failures with parent pointers
2023-04-06 19:16 ` [PATCHSET v11 00/11] fstests: adjust tests for xfs parent pointers Darrick J. Wong
` (5 preceding siblings ...)
2023-04-06 19:43 ` [PATCH 06/11] xfs/018: disable parent pointers for this test Darrick J. Wong
@ 2023-04-06 19:43 ` Darrick J. Wong
2023-04-06 19:43 ` [PATCH 08/11] common: add helpers for parent pointer tests Darrick J. Wong
` (3 subsequent siblings)
10 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:43 UTC (permalink / raw)
To: zlang, djwong; +Cc: linux-xfs, fstests, guan
From: Darrick J. Wong <djwong@kernel.org>
The parent pointers feature isn't supported on tiny 20MB filesystems
because the larger directory transactions result in larger minimum log
sizes, particularly with nrext64 enabled:
** mkfs failed with extra mkfs options added to " -m rmapbt=0, -i nrext64=1, -n parent=1," by test 306 **
** attempting to mkfs using only test 306 options: -d size=20m -n size=64k **
max log size 5108 smaller than min log size 5310, filesystem is too small
We don't support 20M filesystems anymore, so bump the filesystem size up
to 100M and skip this test if we can't actually format the filesystem.
Convert the open-coded punch-alternating logic into a call to that
program to reduce execve overhead, which more than makes up having to
write 5x as much data to fragment the free space.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
tests/xfs/306 | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/tests/xfs/306 b/tests/xfs/306
index b57bf4c0a9..152971cfc3 100755
--- a/tests/xfs/306
+++ b/tests/xfs/306
@@ -23,6 +23,7 @@ _supported_fs xfs
_require_scratch_nocheck # check complains about single AG fs
_require_xfs_io_command "fpunch"
_require_command $UUIDGEN_PROG uuidgen
+_require_test_program "punch-alternating"
# Disable the scratch rt device to avoid test failures relating to the rt
# bitmap consuming all the free space in our small data device.
@@ -30,7 +31,8 @@ unset SCRATCH_RTDEV
# Create a small fs with a large directory block size. We want to fill up the fs
# quickly and then create multi-fsb dirblocks over fragmented free space.
-_scratch_mkfs_xfs -d size=20m -n size=64k >> $seqres.full 2>&1
+_scratch_mkfs_xfs -d size=100m -n size=64k >> $seqres.full 2>&1 || \
+ _notrun 'could not format tiny scratch fs'
_scratch_mount
# Fill a source directory with many largish-named files. 1k uuid-named entries
@@ -49,10 +51,7 @@ done
$XFS_IO_PROG -xc "resblks 16" $SCRATCH_MNT >> $seqres.full 2>&1
dd if=/dev/zero of=$SCRATCH_MNT/file bs=4k >> $seqres.full 2>&1
$XFS_IO_PROG -c "fsync" $SCRATCH_MNT/file >> $seqres.full 2>&1
-size=`_get_filesize $SCRATCH_MNT/file`
-for i in $(seq 0 8192 $size); do
- $XFS_IO_PROG -c "fpunch $i 4k" $SCRATCH_MNT/file >> $seqres.full 2>&1
-done
+$here/src/punch-alternating $SCRATCH_MNT/file
# Replicate the src dir several times into fragmented free space. After one or
# two dirs, we should have nothing but non-contiguous directory blocks.
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 08/11] common: add helpers for parent pointer tests
2023-04-06 19:16 ` [PATCHSET v11 00/11] fstests: adjust tests for xfs parent pointers Darrick J. Wong
` (6 preceding siblings ...)
2023-04-06 19:43 ` [PATCH 07/11] xfs/306: fix formatting failures with parent pointers Darrick J. Wong
@ 2023-04-06 19:43 ` Darrick J. Wong
2023-04-06 19:44 ` [PATCH 09/11] xfs: add parent pointer test Darrick J. Wong
` (2 subsequent siblings)
10 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:43 UTC (permalink / raw)
To: zlang, djwong
Cc: Allison Henderson, Catherine Hoang, linux-xfs, fstests, guan
From: Allison Henderson <allison.henderson@oracle.com>
Add helper functions in common/parent to parse and verify parent
pointers. Also add functions to check that mkfs, kernel, and xfs_io
support parent pointers.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: add license and copyright, dont _fail tests immediately, make
sure the pptr-generated paths match the dir-generated paths]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
common/parent | 209 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
common/rc | 3 +
common/xfs | 12 +++
3 files changed, 224 insertions(+)
create mode 100644 common/parent
diff --git a/common/parent b/common/parent
new file mode 100644
index 0000000000..f849e4b27c
--- /dev/null
+++ b/common/parent
@@ -0,0 +1,209 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022-2023, Oracle and/or its affiliates. All Rights Reserved.
+#
+# Parent pointer common functions
+#
+
+#
+# parse_parent_pointer parents parent_inode parent_pointer_name
+#
+# Given a list of parent pointers, find the record that matches
+# the given inode and filename
+#
+# inputs:
+# parents : A list of parent pointers in the format of:
+# inode/generation/name_length/name
+# parent_inode : The parent inode to search for
+# parent_name : The parent name to search for
+#
+# outputs:
+# PPINO : Parent pointer inode
+# PPGEN : Parent pointer generation
+# PPNAME : Parent pointer name
+# PPNAME_LEN : Parent pointer name length
+#
+_parse_parent_pointer()
+{
+ local parents=$1
+ local pino=$2
+ local parent_pointer_name=$3
+
+ local found=0
+
+ # Find the entry that has the same inode as the parent
+ # and parse out the entry info
+ while IFS=\/ read PPINO PPGEN PPNAME_LEN PPNAME; do
+ if [ "$PPINO" != "$pino" ]; then
+ continue
+ fi
+
+ if [ "$PPNAME" != "$parent_pointer_name" ]; then
+ continue
+ fi
+
+ found=1
+ break
+ done <<< $(echo "$parents")
+
+ # Check to see if we found anything
+ # We do not fail the test because we also use this
+ # routine to verify when parent pointers should
+ # be removed or updated (ie a rename or a move
+ # operation changes your parent pointer)
+ if [ $found -eq "0" ]; then
+ return 1
+ fi
+
+ # Verify the parent pointer name length is correct
+ if [ "$PPNAME_LEN" -ne "${#parent_pointer_name}" ]
+ then
+ echo "*** Bad parent pointer:"\
+ "name:$PPNAME, namelen:$PPNAME_LEN"
+ fi
+
+ #return sucess
+ return 0
+}
+
+#
+# _verify_parent parent_path parent_pointer_name child_path
+#
+# Verify that the given child path lists the given parent as a parent pointer
+# and that the parent pointer name matches the given name
+#
+# Examples:
+#
+# #simple example
+# mkdir testfolder1
+# touch testfolder1/file1
+# verify_parent testfolder1 file1 testfolder1/file1
+#
+# # In this above example, we want to verify that "testfolder1"
+# # appears as a parent pointer of "testfolder1/file1". Additionally
+# # we verify that the name record of the parent pointer is "file1"
+#
+#
+# #hardlink example
+# mkdir testfolder1
+# mkdir testfolder2
+# touch testfolder1/file1
+# ln testfolder1/file1 testfolder2/file1_ln
+# verify_parent testfolder2 file1_ln testfolder1/file1
+#
+# # In this above example, we want to verify that "testfolder2"
+# # appears as a parent pointer of "testfolder1/file1". Additionally
+# # we verify that the name record of the parent pointer is "file1_ln"
+#
+_verify_parent()
+{
+ local parent_path=$1
+ local parent_pointer_name=$2
+ local child_path=$3
+
+ local parent_ppath="$parent_path/$parent_pointer_name"
+
+ # Verify parent exists
+ if [ ! -d $SCRATCH_MNT/$parent_path ]; then
+ echo "$SCRATCH_MNT/$parent_path not found"
+ else
+ echo "*** $parent_path OK"
+ fi
+
+ # Verify child exists
+ if [ ! -f $SCRATCH_MNT/$child_path ]; then
+ echo "$SCRATCH_MNT/$child_path not found"
+ else
+ echo "*** $child_path OK"
+ fi
+
+ # Verify the parent pointer name exists as a child of the parent
+ if [ ! -f $SCRATCH_MNT/$parent_ppath ]; then
+ echo "$SCRATCH_MNT/$parent_ppath not found"
+ else
+ echo "*** $parent_ppath OK"
+ fi
+
+ # Get the inodes of both parent and child
+ pino="$(stat -c '%i' $SCRATCH_MNT/$parent_path)"
+ cino="$(stat -c '%i' $SCRATCH_MNT/$child_path)"
+
+ # Get all the parent pointers of the child
+ parents=($($XFS_IO_PROG -x -c \
+ "parent -f -i $pino -n $parent_pointer_name" $SCRATCH_MNT/$child_path))
+ if [[ $? != 0 ]]; then
+ echo "No parent pointers found for $child_path"
+ fi
+
+ # Parse parent pointer output.
+ # This sets PPINO PPGEN PPNAME PPNAME_LEN
+ _parse_parent_pointer $parents $pino $parent_pointer_name
+
+ # If we didnt find one, bail out
+ if [ $? -ne 0 ]; then
+ echo "No parent pointer record found for $parent_path"\
+ "in $child_path"
+ fi
+
+ # Verify the inode generated by the parent pointer name is
+ # the same as the child inode
+ pppino="$(stat -c '%i' $SCRATCH_MNT/$parent_ppath)"
+ if [ $cino -ne $pppino ]
+ then
+ echo "Bad parent pointer name value for $child_path."\
+ "$SCRATCH_MNT/$parent_ppath belongs to inode $PPPINO,"\
+ "but should be $cino"
+ fi
+
+ # Make sure path printing works by checking that the paths returned
+ # all point to the same inode.
+ local tgt="$SCRATCH_MNT/$child_path"
+ $XFS_IO_PROG -x -c 'parent -p' "$tgt" | while read pptr_path; do
+ test "$tgt" -ef "$pptr_path" || \
+ echo "$tgt parent pointer $pptr_path should be the same file"
+ done
+
+ echo "*** Verified parent pointer:"\
+ "name:$PPNAME, namelen:$PPNAME_LEN"
+ echo "*** Parent pointer OK for child $child_path"
+}
+
+#
+# _verify_parent parent_pointer_name pino child_path
+#
+# Verify that the given child path contains no parent pointer entry
+# for the given inode and file name
+#
+_verify_no_parent()
+{
+ local parent_pname=$1
+ local pino=$2
+ local child_path=$3
+
+ # Verify child exists
+ if [ ! -f $SCRATCH_MNT/$child_path ]; then
+ echo "$SCRATCH_MNT/$child_path not found"
+ else
+ echo "*** $child_path OK"
+ fi
+
+ # Get all the parent pointers of the child
+ local parents=($($XFS_IO_PROG -x -c \
+ "parent -f -i $pino -n $parent_pname" $SCRATCH_MNT/$child_path))
+ if [[ $? != 0 ]]; then
+ return 0
+ fi
+
+ # Parse parent pointer output.
+ # This sets PPINO PPGEN PPNAME PPNAME_LEN
+ _parse_parent_pointer $parents $pino $parent_pname
+
+ # If we didnt find one, return sucess
+ if [ $? -ne 0 ]; then
+ return 0
+ fi
+
+ echo "Parent pointer entry found where none should:"\
+ "inode:$PPINO, gen:$PPGEN,"
+ "name:$PPNAME, namelen:$PPNAME_LEN"
+}
diff --git a/common/rc b/common/rc
index a7e7d1f25b..bf53d62aac 100644
--- a/common/rc
+++ b/common/rc
@@ -2565,6 +2565,9 @@ _require_xfs_io_command()
echo $testio | grep -q "invalid option" && \
_notrun "xfs_io $command support is missing"
;;
+ "parent")
+ testio=`$XFS_IO_PROG -x -c "parent" $TEST_DIR 2>&1`
+ ;;
"pwrite")
# -N (RWF_NOWAIT) only works with direct vectored I/O writes
local pwrite_opts=" "
diff --git a/common/xfs b/common/xfs
index b211c7a6d8..81ac143374 100644
--- a/common/xfs
+++ b/common/xfs
@@ -1791,3 +1791,15 @@ _xfs_discard_max_offset_kb()
$XFS_IO_PROG -c 'statfs' "$1" | \
awk '{g[$1] = $3} END {print (g["geom.bsize"] * g["geom.datablocks"] / 1024)}'
}
+
+# this test requires the xfs parent pointers feature
+#
+_require_xfs_parent()
+{
+ _scratch_mkfs_xfs_supported -n parent > /dev/null 2>&1 \
+ || _notrun "mkfs.xfs does not support parent pointers"
+ _scratch_mkfs_xfs -n parent > /dev/null 2>&1
+ _try_scratch_mount >/dev/null 2>&1 \
+ || _notrun "kernel does not support parent pointers"
+ _scratch_unmount
+}
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 09/11] xfs: add parent pointer test
2023-04-06 19:16 ` [PATCHSET v11 00/11] fstests: adjust tests for xfs parent pointers Darrick J. Wong
` (7 preceding siblings ...)
2023-04-06 19:43 ` [PATCH 08/11] common: add helpers for parent pointer tests Darrick J. Wong
@ 2023-04-06 19:44 ` Darrick J. Wong
2023-04-06 19:44 ` [PATCH 10/11] xfs: add multi link " Darrick J. Wong
2023-04-06 19:44 ` [PATCH 11/11] xfs: add parent pointer inject test Darrick J. Wong
10 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:44 UTC (permalink / raw)
To: zlang, djwong
Cc: Allison Henderson, Catherine Hoang, linux-xfs, fstests, guan
From: Allison Henderson <allison.henderson@oracle.com>
Add a test to verify basic parent pointers operations (create, move, link,
unlink, rename, overwrite).
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
[djwong: test the xfs_io parent -p argument too]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
doc/group-names.txt | 1
tests/xfs/851 | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++
tests/xfs/851.out | 69 ++++++++++++++++++++++++++++++
3 files changed, 186 insertions(+)
create mode 100755 tests/xfs/851
create mode 100644 tests/xfs/851.out
diff --git a/doc/group-names.txt b/doc/group-names.txt
index 88f9b8a0e7..81c7057bf0 100644
--- a/doc/group-names.txt
+++ b/doc/group-names.txt
@@ -82,6 +82,7 @@ nfs4_acl NFSv4 access control lists
nonsamefs overlayfs layers on different filesystems
online_repair online repair functionality tests
other dumping ground, do not add more tests to this group
+parent Parent pointer tests
pattern specific IO pattern tests
perms access control and permission checking
pipe pipe functionality
diff --git a/tests/xfs/851 b/tests/xfs/851
new file mode 100755
index 0000000000..8233c1563c
--- /dev/null
+++ b/tests/xfs/851
@@ -0,0 +1,116 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022, Oracle and/or its affiliates. All Rights Reserved.
+#
+# FS QA Test 851
+#
+# simple parent pointer test
+#
+
+. ./common/preamble
+_begin_fstest auto quick parent
+
+# get standard environment, filters and checks
+. ./common/parent
+. ./common/filter
+
+# Modify as appropriate
+_supported_fs xfs
+_require_scratch
+_require_xfs_sysfs debug/larp
+_require_xfs_parent
+_require_xfs_io_command "parent"
+
+# real QA test starts here
+
+# Create a directory tree using a protofile and
+# make sure all inodes created have parent pointers
+
+protofile=$tmp.proto
+
+cat >$protofile <<EOF
+DUMMY1
+0 0
+: root directory
+d--777 3 1
+: a directory
+testfolder1 d--755 3 1
+file1 ---755 3 1 /dev/null
+$
+: back in the root
+testfolder2 d--755 3 1
+file2 ---755 3 1 /dev/null
+: done
+$
+EOF
+
+_scratch_mkfs -f -n parent=1 -p $protofile >>$seqres.full 2>&1 \
+ || _fail "mkfs failed"
+_check_scratch_fs
+
+_scratch_mount >>$seqres.full 2>&1 \
+ || _fail "mount failed"
+
+testfolder1="testfolder1"
+testfolder2="testfolder2"
+file1="file1"
+file2="file2"
+file3="file3"
+file1_ln="file1_link"
+
+echo ""
+# Create parent pointer test
+_verify_parent "$testfolder1" "$file1" "$testfolder1/$file1"
+
+echo ""
+# Move parent pointer test
+mv $SCRATCH_MNT/$testfolder1/$file1 $SCRATCH_MNT/$testfolder2/$file1
+_verify_parent "$testfolder2" "$file1" "$testfolder2/$file1"
+
+echo ""
+# Hard link parent pointer test
+ln $SCRATCH_MNT/$testfolder2/$file1 $SCRATCH_MNT/$testfolder1/$file1_ln
+_verify_parent "$testfolder1" "$file1_ln" "$testfolder1/$file1_ln"
+_verify_parent "$testfolder1" "$file1_ln" "$testfolder2/$file1"
+_verify_parent "$testfolder2" "$file1" "$testfolder1/$file1_ln"
+_verify_parent "$testfolder2" "$file1" "$testfolder2/$file1"
+
+echo ""
+# Remove hard link parent pointer test
+ino="$(stat -c '%i' $SCRATCH_MNT/$testfolder2/$file1)"
+rm $SCRATCH_MNT/$testfolder2/$file1
+_verify_parent "$testfolder1" "$file1_ln" "$testfolder1/$file1_ln"
+_verify_no_parent "$file1" "$ino" "$testfolder1/$file1_ln"
+
+echo ""
+# Rename parent pointer test
+ino="$(stat -c '%i' $SCRATCH_MNT/$testfolder1/$file1_ln)"
+mv $SCRATCH_MNT/$testfolder1/$file1_ln $SCRATCH_MNT/$testfolder1/$file2
+_verify_parent "$testfolder1" "$file2" "$testfolder1/$file2"
+_verify_no_parent "$file1_ln" "$ino" "$testfolder1/$file2"
+
+echo ""
+# Over write parent pointer test
+touch $SCRATCH_MNT/$testfolder2/$file3
+_verify_parent "$testfolder2" "$file3" "$testfolder2/$file3"
+ino="$(stat -c '%i' $SCRATCH_MNT/$testfolder2/$file3)"
+mv -f $SCRATCH_MNT/$testfolder2/$file3 $SCRATCH_MNT/$testfolder1/$file2
+_verify_parent "$testfolder1" "$file2" "$testfolder1/$file2"
+
+# Make sure that parent -p filtering works
+mkdir -p $SCRATCH_MNT/dira/ $SCRATCH_MNT/dirb/
+dira_inum=$(stat -c '%i' $SCRATCH_MNT/dira)
+dirb_inum=$(stat -c '%i' $SCRATCH_MNT/dirb)
+touch $SCRATCH_MNT/gorn
+ln $SCRATCH_MNT/gorn $SCRATCH_MNT/dira/file1
+ln $SCRATCH_MNT/gorn $SCRATCH_MNT/dirb/file1
+echo look for both
+$XFS_IO_PROG -c 'parent -p' $SCRATCH_MNT/gorn | _filter_scratch
+echo look for dira
+$XFS_IO_PROG -c 'parent -p -n dira' -c "parent -p -i $dira_inum" $SCRATCH_MNT/gorn | _filter_scratch
+echo look for dirb
+$XFS_IO_PROG -c 'parent -p -n dirb' -c "parent -p -i $dirb_inum" $SCRATCH_MNT/gorn | _filter_scratch
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/851.out b/tests/xfs/851.out
new file mode 100644
index 0000000000..f44d3e5d4f
--- /dev/null
+++ b/tests/xfs/851.out
@@ -0,0 +1,69 @@
+QA output created by 851
+
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1
+
+*** testfolder2 OK
+*** testfolder2/file1 OK
+*** testfolder2/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder2/file1
+
+*** testfolder1 OK
+*** testfolder1/file1_link OK
+*** testfolder1/file1_link OK
+*** Verified parent pointer: name:file1_link, namelen:10
+*** Parent pointer OK for child testfolder1/file1_link
+*** testfolder1 OK
+*** testfolder2/file1 OK
+*** testfolder1/file1_link OK
+*** Verified parent pointer: name:file1_link, namelen:10
+*** Parent pointer OK for child testfolder2/file1
+*** testfolder2 OK
+*** testfolder1/file1_link OK
+*** testfolder2/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link
+*** testfolder2 OK
+*** testfolder2/file1 OK
+*** testfolder2/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder2/file1
+
+*** testfolder1 OK
+*** testfolder1/file1_link OK
+*** testfolder1/file1_link OK
+*** Verified parent pointer: name:file1_link, namelen:10
+*** Parent pointer OK for child testfolder1/file1_link
+*** testfolder1/file1_link OK
+
+*** testfolder1 OK
+*** testfolder1/file2 OK
+*** testfolder1/file2 OK
+*** Verified parent pointer: name:file2, namelen:5
+*** Parent pointer OK for child testfolder1/file2
+*** testfolder1/file2 OK
+
+*** testfolder2 OK
+*** testfolder2/file3 OK
+*** testfolder2/file3 OK
+*** Verified parent pointer: name:file3, namelen:5
+*** Parent pointer OK for child testfolder2/file3
+*** testfolder1 OK
+*** testfolder1/file2 OK
+*** testfolder1/file2 OK
+*** Verified parent pointer: name:file2, namelen:5
+*** Parent pointer OK for child testfolder1/file2
+look for both
+SCRATCH_MNT/gorn
+SCRATCH_MNT/dira/file1
+SCRATCH_MNT/dirb/file1
+look for dira
+SCRATCH_MNT/dira/file1
+SCRATCH_MNT/dira/file1
+look for dirb
+SCRATCH_MNT/dirb/file1
+SCRATCH_MNT/dirb/file1
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 10/11] xfs: add multi link parent pointer test
2023-04-06 19:16 ` [PATCHSET v11 00/11] fstests: adjust tests for xfs parent pointers Darrick J. Wong
` (8 preceding siblings ...)
2023-04-06 19:44 ` [PATCH 09/11] xfs: add parent pointer test Darrick J. Wong
@ 2023-04-06 19:44 ` Darrick J. Wong
2023-04-06 19:44 ` [PATCH 11/11] xfs: add parent pointer inject test Darrick J. Wong
10 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:44 UTC (permalink / raw)
To: zlang, djwong
Cc: Allison Henderson, Catherine Hoang, linux-xfs, fstests, guan
From: Allison Henderson <allison.henderson@oracle.com>
Add a test to verify parent pointers while multiple links to a file are
created and removed.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
tests/xfs/852 | 69 ++++
tests/xfs/852.out | 1002 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1071 insertions(+)
create mode 100755 tests/xfs/852
create mode 100644 tests/xfs/852.out
diff --git a/tests/xfs/852 b/tests/xfs/852
new file mode 100755
index 0000000000..4d1be0e945
--- /dev/null
+++ b/tests/xfs/852
@@ -0,0 +1,69 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022, Oracle and/or its affiliates. All Rights Reserved.
+#
+# FS QA Test 852
+#
+# multi link parent pointer test
+#
+. ./common/preamble
+_begin_fstest auto quick parent
+
+# get standard environment, filters and checks
+. ./common/parent
+
+# Modify as appropriate
+_supported_fs xfs
+_require_scratch
+_require_xfs_sysfs debug/larp
+_require_xfs_parent
+_require_xfs_io_command "parent"
+
+# real QA test starts here
+
+# Create a directory tree using a protofile and
+# make sure all inodes created have parent pointers
+
+protofile=$tmp.proto
+
+cat >$protofile <<EOF
+DUMMY1
+0 0
+: root directory
+d--777 3 1
+: a directory
+testfolder1 d--755 3 1
+file1 ---755 3 1 /dev/null
+: done
+$
+EOF
+
+_scratch_mkfs -f -n parent=1 -p $protofile >>$seqresres.full 2>&1 \
+ || _fail "mkfs failed"
+_check_scratch_fs
+
+_scratch_mount >>$seqres.full 2>&1 \
+ || _fail "mount failed"
+
+testfolder1="testfolder1"
+file1="file1"
+file1_ln="file1_link"
+
+echo ""
+# Multi link parent pointer test
+NLINKS=100
+for (( j=0; j<$NLINKS; j++ )); do
+ ln $SCRATCH_MNT/$testfolder1/$file1 $SCRATCH_MNT/$testfolder1/$file1_ln.$j
+ _verify_parent "$testfolder1" "$file1_ln.$j" "$testfolder1/$file1"
+ _verify_parent "$testfolder1" "$file1" "$testfolder1/$file1_ln.$j"
+done
+# Multi unlink parent pointer test
+for (( j=$NLINKS-1; j<=0; j-- )); do
+ ino="$(stat -c '%i' $SCRATCH_MNT/$testfolder1/$file1_ln.$j)"
+ rm $SCRATCH_MNT/$testfolder1/$file1_ln.$j
+ _verify_no_parent "$file1_ln.$j" "$ino" "$testfolder1/$file1"
+done
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/852.out b/tests/xfs/852.out
new file mode 100644
index 0000000000..9cc4b354ad
--- /dev/null
+++ b/tests/xfs/852.out
@@ -0,0 +1,1002 @@
+QA output created by 852
+
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.0 OK
+*** Verified parent pointer: name:file1_link.0, namelen:12
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.0 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.0
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.1 OK
+*** Verified parent pointer: name:file1_link.1, namelen:12
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.1 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.1
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.2 OK
+*** Verified parent pointer: name:file1_link.2, namelen:12
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.2 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.2
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.3 OK
+*** Verified parent pointer: name:file1_link.3, namelen:12
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.3 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.3
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.4 OK
+*** Verified parent pointer: name:file1_link.4, namelen:12
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.4 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.4
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.5 OK
+*** Verified parent pointer: name:file1_link.5, namelen:12
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.5 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.5
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.6 OK
+*** Verified parent pointer: name:file1_link.6, namelen:12
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.6 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.6
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.7 OK
+*** Verified parent pointer: name:file1_link.7, namelen:12
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.7 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.7
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.8 OK
+*** Verified parent pointer: name:file1_link.8, namelen:12
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.8 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.8
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.9 OK
+*** Verified parent pointer: name:file1_link.9, namelen:12
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.9 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.9
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.10 OK
+*** Verified parent pointer: name:file1_link.10, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.10 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.10
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.11 OK
+*** Verified parent pointer: name:file1_link.11, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.11 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.11
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.12 OK
+*** Verified parent pointer: name:file1_link.12, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.12 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.12
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.13 OK
+*** Verified parent pointer: name:file1_link.13, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.13 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.13
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.14 OK
+*** Verified parent pointer: name:file1_link.14, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.14 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.14
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.15 OK
+*** Verified parent pointer: name:file1_link.15, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.15 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.15
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.16 OK
+*** Verified parent pointer: name:file1_link.16, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.16 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.16
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.17 OK
+*** Verified parent pointer: name:file1_link.17, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.17 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.17
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.18 OK
+*** Verified parent pointer: name:file1_link.18, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.18 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.18
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.19 OK
+*** Verified parent pointer: name:file1_link.19, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.19 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.19
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.20 OK
+*** Verified parent pointer: name:file1_link.20, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.20 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.20
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.21 OK
+*** Verified parent pointer: name:file1_link.21, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.21 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.21
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.22 OK
+*** Verified parent pointer: name:file1_link.22, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.22 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.22
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.23 OK
+*** Verified parent pointer: name:file1_link.23, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.23 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.23
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.24 OK
+*** Verified parent pointer: name:file1_link.24, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.24 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.24
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.25 OK
+*** Verified parent pointer: name:file1_link.25, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.25 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.25
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.26 OK
+*** Verified parent pointer: name:file1_link.26, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.26 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.26
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.27 OK
+*** Verified parent pointer: name:file1_link.27, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.27 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.27
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.28 OK
+*** Verified parent pointer: name:file1_link.28, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.28 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.28
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.29 OK
+*** Verified parent pointer: name:file1_link.29, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.29 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.29
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.30 OK
+*** Verified parent pointer: name:file1_link.30, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.30 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.30
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.31 OK
+*** Verified parent pointer: name:file1_link.31, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.31 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.31
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.32 OK
+*** Verified parent pointer: name:file1_link.32, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.32 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.32
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.33 OK
+*** Verified parent pointer: name:file1_link.33, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.33 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.33
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.34 OK
+*** Verified parent pointer: name:file1_link.34, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.34 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.34
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.35 OK
+*** Verified parent pointer: name:file1_link.35, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.35 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.35
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.36 OK
+*** Verified parent pointer: name:file1_link.36, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.36 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.36
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.37 OK
+*** Verified parent pointer: name:file1_link.37, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.37 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.37
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.38 OK
+*** Verified parent pointer: name:file1_link.38, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.38 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.38
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.39 OK
+*** Verified parent pointer: name:file1_link.39, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.39 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.39
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.40 OK
+*** Verified parent pointer: name:file1_link.40, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.40 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.40
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.41 OK
+*** Verified parent pointer: name:file1_link.41, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.41 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.41
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.42 OK
+*** Verified parent pointer: name:file1_link.42, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.42 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.42
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.43 OK
+*** Verified parent pointer: name:file1_link.43, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.43 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.43
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.44 OK
+*** Verified parent pointer: name:file1_link.44, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.44 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.44
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.45 OK
+*** Verified parent pointer: name:file1_link.45, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.45 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.45
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.46 OK
+*** Verified parent pointer: name:file1_link.46, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.46 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.46
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.47 OK
+*** Verified parent pointer: name:file1_link.47, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.47 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.47
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.48 OK
+*** Verified parent pointer: name:file1_link.48, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.48 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.48
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.49 OK
+*** Verified parent pointer: name:file1_link.49, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.49 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.49
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.50 OK
+*** Verified parent pointer: name:file1_link.50, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.50 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.50
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.51 OK
+*** Verified parent pointer: name:file1_link.51, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.51 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.51
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.52 OK
+*** Verified parent pointer: name:file1_link.52, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.52 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.52
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.53 OK
+*** Verified parent pointer: name:file1_link.53, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.53 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.53
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.54 OK
+*** Verified parent pointer: name:file1_link.54, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.54 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.54
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.55 OK
+*** Verified parent pointer: name:file1_link.55, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.55 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.55
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.56 OK
+*** Verified parent pointer: name:file1_link.56, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.56 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.56
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.57 OK
+*** Verified parent pointer: name:file1_link.57, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.57 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.57
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.58 OK
+*** Verified parent pointer: name:file1_link.58, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.58 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.58
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.59 OK
+*** Verified parent pointer: name:file1_link.59, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.59 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.59
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.60 OK
+*** Verified parent pointer: name:file1_link.60, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.60 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.60
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.61 OK
+*** Verified parent pointer: name:file1_link.61, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.61 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.61
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.62 OK
+*** Verified parent pointer: name:file1_link.62, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.62 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.62
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.63 OK
+*** Verified parent pointer: name:file1_link.63, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.63 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.63
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.64 OK
+*** Verified parent pointer: name:file1_link.64, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.64 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.64
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.65 OK
+*** Verified parent pointer: name:file1_link.65, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.65 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.65
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.66 OK
+*** Verified parent pointer: name:file1_link.66, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.66 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.66
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.67 OK
+*** Verified parent pointer: name:file1_link.67, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.67 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.67
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.68 OK
+*** Verified parent pointer: name:file1_link.68, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.68 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.68
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.69 OK
+*** Verified parent pointer: name:file1_link.69, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.69 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.69
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.70 OK
+*** Verified parent pointer: name:file1_link.70, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.70 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.70
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.71 OK
+*** Verified parent pointer: name:file1_link.71, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.71 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.71
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.72 OK
+*** Verified parent pointer: name:file1_link.72, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.72 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.72
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.73 OK
+*** Verified parent pointer: name:file1_link.73, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.73 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.73
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.74 OK
+*** Verified parent pointer: name:file1_link.74, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.74 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.74
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.75 OK
+*** Verified parent pointer: name:file1_link.75, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.75 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.75
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.76 OK
+*** Verified parent pointer: name:file1_link.76, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.76 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.76
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.77 OK
+*** Verified parent pointer: name:file1_link.77, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.77 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.77
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.78 OK
+*** Verified parent pointer: name:file1_link.78, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.78 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.78
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.79 OK
+*** Verified parent pointer: name:file1_link.79, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.79 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.79
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.80 OK
+*** Verified parent pointer: name:file1_link.80, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.80 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.80
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.81 OK
+*** Verified parent pointer: name:file1_link.81, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.81 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.81
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.82 OK
+*** Verified parent pointer: name:file1_link.82, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.82 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.82
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.83 OK
+*** Verified parent pointer: name:file1_link.83, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.83 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.83
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.84 OK
+*** Verified parent pointer: name:file1_link.84, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.84 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.84
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.85 OK
+*** Verified parent pointer: name:file1_link.85, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.85 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.85
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.86 OK
+*** Verified parent pointer: name:file1_link.86, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.86 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.86
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.87 OK
+*** Verified parent pointer: name:file1_link.87, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.87 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.87
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.88 OK
+*** Verified parent pointer: name:file1_link.88, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.88 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.88
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.89 OK
+*** Verified parent pointer: name:file1_link.89, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.89 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.89
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.90 OK
+*** Verified parent pointer: name:file1_link.90, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.90 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.90
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.91 OK
+*** Verified parent pointer: name:file1_link.91, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.91 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.91
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.92 OK
+*** Verified parent pointer: name:file1_link.92, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.92 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.92
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.93 OK
+*** Verified parent pointer: name:file1_link.93, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.93 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.93
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.94 OK
+*** Verified parent pointer: name:file1_link.94, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.94 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.94
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.95 OK
+*** Verified parent pointer: name:file1_link.95, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.95 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.95
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.96 OK
+*** Verified parent pointer: name:file1_link.96, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.96 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.96
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.97 OK
+*** Verified parent pointer: name:file1_link.97, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.97 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.97
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.98 OK
+*** Verified parent pointer: name:file1_link.98, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.98 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.98
+*** testfolder1 OK
+*** testfolder1/file1 OK
+*** testfolder1/file1_link.99 OK
+*** Verified parent pointer: name:file1_link.99, namelen:13
+*** Parent pointer OK for child testfolder1/file1
+*** testfolder1 OK
+*** testfolder1/file1_link.99 OK
+*** testfolder1/file1 OK
+*** Verified parent pointer: name:file1, namelen:5
+*** Parent pointer OK for child testfolder1/file1_link.99
^ permalink raw reply related [flat|nested] 121+ messages in thread
* [PATCH 11/11] xfs: add parent pointer inject test
2023-04-06 19:16 ` [PATCHSET v11 00/11] fstests: adjust tests for xfs parent pointers Darrick J. Wong
` (9 preceding siblings ...)
2023-04-06 19:44 ` [PATCH 10/11] xfs: add multi link " Darrick J. Wong
@ 2023-04-06 19:44 ` Darrick J. Wong
10 siblings, 0 replies; 121+ messages in thread
From: Darrick J. Wong @ 2023-04-06 19:44 UTC (permalink / raw)
To: zlang, djwong
Cc: Allison Henderson, Catherine Hoang, linux-xfs, fstests, guan
From: Allison Henderson <allison.henderson@oracle.com>
Add a test to verify parent pointers after an error injection and log
replay.
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Catherine Hoang <catherine.hoang@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
---
tests/xfs/853 | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++
tests/xfs/853.out | 14 +++++++++
2 files changed, 99 insertions(+)
create mode 100755 tests/xfs/853
create mode 100644 tests/xfs/853.out
diff --git a/tests/xfs/853 b/tests/xfs/853
new file mode 100755
index 0000000000..f17f4b7e9e
--- /dev/null
+++ b/tests/xfs/853
@@ -0,0 +1,85 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2022, Oracle and/or its affiliates. All Rights Reserved.
+#
+# FS QA Test 853
+#
+# parent pointer inject test
+#
+. ./common/preamble
+_begin_fstest auto quick parent
+
+# get standard environment, filters and checks
+. ./common/filter
+. ./common/inject
+. ./common/parent
+
+# Modify as appropriate
+_supported_fs xfs
+_require_scratch
+_require_xfs_sysfs debug/larp
+_require_xfs_io_error_injection "larp"
+_require_xfs_parent
+_require_xfs_io_command "parent"
+
+# real QA test starts here
+
+# Create a directory tree using a protofile and
+# make sure all inodes created have parent pointers
+
+protofile=$tmp.proto
+
+cat >$protofile <<EOF
+DUMMY1
+0 0
+: root directory
+d--777 3 1
+: a directory
+testfolder1 d--755 3 1
+$
+: back in the root
+testfolder2 d--755 3 1
+: done
+$
+EOF
+
+_scratch_mkfs -f -n parent=1 -p $protofile >>$seqres.full 2>&1 \
+ || _fail "mkfs failed"
+_check_scratch_fs
+
+_scratch_mount >>$seqres.full 2>&1 \
+ || _fail "mount failed"
+
+testfolder1="testfolder1"
+testfolder2="testfolder2"
+file4="file4"
+file5="file5"
+
+echo ""
+
+# Create files
+touch $SCRATCH_MNT/$testfolder1/$file4
+_verify_parent "$testfolder1" "$file4" "$testfolder1/$file4"
+
+# Inject error
+_scratch_inject_error "larp"
+
+# Move files
+mv $SCRATCH_MNT/$testfolder1/$file4 $SCRATCH_MNT/$testfolder2/$file5 2>&1 \
+ | _filter_scratch
+
+# FS should be shut down, touch will fail
+touch $SCRATCH_MNT/$testfolder2/$file5 2>&1 | _filter_scratch
+
+# Remount to replay log
+_scratch_remount_dump_log >> $seqres.full
+
+# FS should be online, touch should succeed
+touch $SCRATCH_MNT/$testfolder2/$file5
+
+# Check files again
+_verify_parent "$testfolder2" "$file5" "$testfolder2/$file5"
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/853.out b/tests/xfs/853.out
new file mode 100644
index 0000000000..56247c1434
--- /dev/null
+++ b/tests/xfs/853.out
@@ -0,0 +1,14 @@
+QA output created by 853
+
+*** testfolder1 OK
+*** testfolder1/file4 OK
+*** testfolder1/file4 OK
+*** Verified parent pointer: name:file4, namelen:5
+*** Parent pointer OK for child testfolder1/file4
+mv: cannot stat 'SCRATCH_MNT/testfolder1/file4': Input/output error
+touch: cannot touch 'SCRATCH_MNT/testfolder2/file5': Input/output error
+*** testfolder2 OK
+*** testfolder2/file5 OK
+*** testfolder2/file5 OK
+*** Verified parent pointer: name:file5, namelen:5
+*** Parent pointer OK for child testfolder2/file5
^ permalink raw reply related [flat|nested] 121+ messages in thread
end of thread, other threads:[~2023-04-06 19:45 UTC | newest]
Thread overview: 121+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-04-06 18:10 [PATCHSET DELUGE v11] xfs: Parent Pointers Darrick J. Wong
2023-04-06 19:07 ` Darrick J. Wong
2023-04-06 19:13 ` [PATCHSET v11 00/12] xfs: name-value xattr lookups Darrick J. Wong
2023-04-06 19:16 ` [PATCH 01/12] xfs: check opcode and iovec count match in xlog_recover_attri_commit_pass2 Darrick J. Wong
2023-04-06 19:17 ` [PATCH 02/12] xfs: make xfs_attr_set require XFS_DA_OP_REMOVE Darrick J. Wong
2023-04-06 19:17 ` [PATCH 03/12] xfs: allow xattr matching on name and value for local/sf attrs Darrick J. Wong
2023-04-06 19:17 ` [PATCH 04/12] xfs: preserve NVLOOKUP in xfs_attr_set Darrick J. Wong
2023-04-06 19:17 ` [PATCH 05/12] xfs: restructure xfs_attr_complete_op a bit Darrick J. Wong
2023-04-06 19:18 ` [PATCH 06/12] xfs: use helpers to extract xattr op from opflags Darrick J. Wong
2023-04-06 19:18 ` [PATCH 07/12] xfs: validate recovered name buffers when recovering xattr items Darrick J. Wong
2023-04-06 19:18 ` [PATCH 08/12] xfs: always set args->value in xfs_attri_item_recover Darrick J. Wong
2023-04-06 19:18 ` [PATCH 09/12] xfs: use local variables for name and value length in _attri_commit_pass2 Darrick J. Wong
2023-04-06 19:19 ` [PATCH 10/12] xfs: log NVLOOKUP xattr removal operations Darrick J. Wong
2023-04-06 19:19 ` [PATCH 11/12] xfs: log NVLOOKUP xattr setting operations Darrick J. Wong
2023-04-06 19:19 ` [PATCH 12/12] xfs: log NVLOOKUP xattr nvreplace operations Darrick J. Wong
2023-04-06 19:14 ` [PATCHSET v11 0/3] xfs: hold ILOCK during deferred dir ops Darrick J. Wong
2023-04-06 19:19 ` [PATCH 1/3] xfs: Hold inode locks in xfs_ialloc Darrick J. Wong
2023-04-06 19:20 ` [PATCH 2/3] xfs: Hold inode locks in xfs_trans_alloc_dir Darrick J. Wong
2023-04-06 19:20 ` [PATCH 3/3] xfs: Hold inode locks in xfs_rename Darrick J. Wong
2023-04-06 19:14 ` [PATCHSET v11 00/23] xfs: Parent Pointers Darrick J. Wong
2023-04-06 19:20 ` [PATCH 01/23] xfs: Expose init_xattrs in xfs_create_tmpfile Darrick J. Wong
2023-04-06 19:20 ` [PATCH 02/23] xfs: Increase XFS_DEFER_OPS_NR_INODES to 5 Darrick J. Wong
2023-04-06 19:21 ` [PATCH 03/23] xfs: Increase XFS_QM_TRANS_MAXDQS " Darrick J. Wong
2023-04-06 19:21 ` [PATCH 04/23] xfs: add parent pointer support to attribute code Darrick J. Wong
2023-04-06 19:21 ` [PATCH 05/23] xfs: define parent pointer ondisk extended attribute format Darrick J. Wong
2023-04-06 19:21 ` [PATCH 06/23] xfs: add parent pointer validator functions Darrick J. Wong
2023-04-06 19:22 ` [PATCH 07/23] xfs: extend transaction reservations for parent attributes Darrick J. Wong
2023-04-06 19:22 ` [PATCH 08/23] xfs: parent pointer attribute creation Darrick J. Wong
2023-04-06 19:22 ` [PATCH 09/23] xfs: add parent attributes to link Darrick J. Wong
2023-04-06 19:23 ` [PATCH 10/23] xfs: add parent attributes to symlink Darrick J. Wong
2023-04-06 19:23 ` [PATCH 11/23] xfs: remove parent pointers in unlink Darrick J. Wong
2023-04-06 19:23 ` [PATCH 12/23] xfs: Indent xfs_rename Darrick J. Wong
2023-04-06 19:23 ` [PATCH 13/23] xfs: Add parent pointers to rename Darrick J. Wong
2023-04-06 19:24 ` [PATCH 14/23] xfs: Add parent pointers to xfs_cross_rename Darrick J. Wong
2023-04-06 19:24 ` [PATCH 15/23] xfs: Add helper function xfs_attr_list_context_init Darrick J. Wong
2023-04-06 19:24 ` [PATCH 16/23] xfs: Filter XFS_ATTR_PARENT for getfattr Darrick J. Wong
2023-04-06 19:24 ` [PATCH 17/23] xfs: pass the attr value to put_listent when possible Darrick J. Wong
2023-04-06 19:25 ` [PATCH 18/23] xfs: add a libxfs header file for staging new ioctls Darrick J. Wong
2023-04-06 19:25 ` [PATCH 19/23] xfs: Add parent pointer ioctl Darrick J. Wong
2023-04-06 19:25 ` [PATCH 20/23] xfs: fix unit conversion error in xfs_log_calc_max_attrsetm_res Darrick J. Wong
2023-04-06 19:25 ` [PATCH 21/23] xfs: drop compatibility minimum log size computations for reflink Darrick J. Wong
2023-04-06 19:26 ` [PATCH 22/23] xfs: don't remove the attr fork when parent pointers are enabled Darrick J. Wong
2023-04-06 19:26 ` [PATCH 23/23] xfs: Add the parent pointer support to the superblock version 5 Darrick J. Wong
2023-04-06 19:14 ` [PATCHSET v11 0/3] xfs: online repair of directories Darrick J. Wong
2023-04-06 19:26 ` [PATCH 1/3] xfs: reconstruct directories from parent pointers Darrick J. Wong
2023-04-06 19:26 ` [PATCH 2/3] xfs: add hooks to do directory updates Darrick J. Wong
2023-04-06 19:27 ` [PATCH 3/3] xfs: compare generated and existing dirents Darrick J. Wong
2023-04-06 19:14 ` [PATCHSET v11 0/2] xfs: online checking of parent pointers Darrick J. Wong
2023-04-06 19:27 ` [PATCH 1/2] xfs: scrub " Darrick J. Wong
2023-04-06 19:27 ` [PATCH 2/2] xfs: deferred scrub of " Darrick J. Wong
2023-04-06 19:15 ` [PATCHSET v11 0/3] xfs: online checking " Darrick J. Wong
2023-04-06 19:27 ` [PATCH 1/3] xfs: repair parent pointers by scanning directories Darrick J. Wong
2023-04-06 19:28 ` [PATCH 2/3] xfs: repair parent pointers with live scan hooks Darrick J. Wong
2023-04-06 19:28 ` [PATCH 3/3] xfs: compare generated and existing parent pointers Darrick J. Wong
2023-04-06 19:15 ` [PATCHSET v11 0/2] xfs: online checking of directories Darrick J. Wong
2023-04-06 19:28 ` [PATCH 1/2] xfs: check dirents have parent pointers Darrick J. Wong
2023-04-06 19:28 ` [PATCH 2/2] xfs: deferred scrub of dirents Darrick J. Wong
2023-04-06 19:15 ` [PATCHSET v11 00/10] xfs: name-value xattr lookups Darrick J. Wong
2023-04-06 19:29 ` [PATCH 01/10] xfs: make xfs_attr_set require XFS_DA_OP_REMOVE Darrick J. Wong
2023-04-06 19:29 ` [PATCH 02/10] xfs: allow xattr matching on name and value for local/sf attrs Darrick J. Wong
2023-04-06 19:29 ` [PATCH 03/10] xfs: preserve NVLOOKUP in xfs_attr_set Darrick J. Wong
2023-04-06 19:30 ` [PATCH 04/10] xfs: restructure xfs_attr_complete_op a bit Darrick J. Wong
2023-04-06 19:30 ` [PATCH 05/10] xfs: use helpers to extract xattr op from opflags Darrick J. Wong
2023-04-06 19:30 ` [PATCH 06/10] xfs: log NVLOOKUP xattr removal operations Darrick J. Wong
2023-04-06 19:30 ` [PATCH 07/10] xfs: log NVLOOKUP xattr setting operations Darrick J. Wong
2023-04-06 19:31 ` [PATCH 08/10] xfs: log NVLOOKUP xattr nvreplace operations Darrick J. Wong
2023-04-06 19:31 ` [PATCH 09/10] xfs_logprint: dump new attr log item fields Darrick J. Wong
2023-04-06 19:31 ` [PATCH 10/10] xfs_logprint: print missing attri header fields Darrick J. Wong
2023-04-06 19:15 ` [PATCHSET v11 00/32] xfsprogs: Parent Pointers Darrick J. Wong
2023-04-06 19:31 ` [PATCH 01/32] xfsprogs: Increase XFS_DEFER_OPS_NR_INODES to 5 Darrick J. Wong
2023-04-06 19:32 ` [PATCH 02/32] xfs: add parent pointer support to attribute code Darrick J. Wong
2023-04-06 19:32 ` [PATCH 03/32] xfs: define parent pointer ondisk extended attribute format Darrick J. Wong
2023-04-06 19:32 ` [PATCH 04/32] xfs: add parent pointer validator functions Darrick J. Wong
2023-04-06 19:32 ` [PATCH 05/32] xfs: extend transaction reservations for parent attributes Darrick J. Wong
2023-04-06 19:33 ` [PATCH 06/32] xfs: parent pointer attribute creation Darrick J. Wong
2023-04-06 19:33 ` [PATCH 07/32] xfs: add parent attributes to link Darrick J. Wong
2023-04-06 19:33 ` [PATCH 08/32] xfs: add parent attributes to symlink Darrick J. Wong
2023-04-06 19:33 ` [PATCH 09/32] xfs: remove parent pointers in unlink Darrick J. Wong
2023-04-06 19:34 ` [PATCH 10/32] xfs: Add parent pointers to rename Darrick J. Wong
2023-04-06 19:34 ` [PATCH 11/32] xfsprogs: pass the attr value to put_listent when possible Darrick J. Wong
2023-04-06 19:34 ` [PATCH 12/32] xfs: add a libxfs header file for staging new ioctls Darrick J. Wong
2023-04-06 19:34 ` [PATCH 13/32] xfs: Add parent pointer ioctl Darrick J. Wong
2023-04-06 19:35 ` [PATCH 14/32] xfs: fix unit conversion error in xfs_log_calc_max_attrsetm_res Darrick J. Wong
2023-04-06 19:35 ` [PATCH 15/32] xfs: drop compatibility minimum log size computations for reflink Darrick J. Wong
2023-04-06 19:35 ` [PATCH 16/32] xfs: don't remove the attr fork when parent pointers are enabled Darrick J. Wong
2023-04-06 19:35 ` [PATCH 17/32] xfs: Add the parent pointer support to the superblock version 5 Darrick J. Wong
2023-04-06 19:36 ` [PATCH 18/32] libfrog: add parent pointer support code Darrick J. Wong
2023-04-06 19:36 ` [PATCH 19/32] xfs_io: adapt parent command to new parent pointer ioctls Darrick J. Wong
2023-04-06 19:36 ` [PATCH 20/32] xfs_io: Add i, n and f flags to parent command Darrick J. Wong
2023-04-06 19:37 ` [PATCH 21/32] xfs_logprint: decode parent pointers in ATTRI items fully Darrick J. Wong
2023-04-06 19:37 ` [PATCH 22/32] xfs_scrub: use parent pointers when possible to report file operations Darrick J. Wong
2023-04-06 19:37 ` [PATCH 23/32] xfs_db: report parent bit on xattrs Darrick J. Wong
2023-04-06 19:37 ` [PATCH 24/32] xfs_db: report parent pointers embedded in xattrs Darrick J. Wong
2023-04-06 19:38 ` [PATCH 25/32] xfs_db: obfuscate dirent and parent pointer names consistently Darrick J. Wong
2023-04-06 19:38 ` [PATCH 26/32] xfs_db: hoist name obfuscation code out of metadump.c Darrick J. Wong
2023-04-06 19:38 ` [PATCH 27/32] xfs_db: create dirents and xattrs with colliding names Darrick J. Wong
2023-04-06 19:38 ` [PATCH 28/32] xfs_db: add a parents command to list the parents of a file Darrick J. Wong
2023-04-06 19:39 ` [PATCH 29/32] libxfs: create new files with attr forks if necessary Darrick J. Wong
2023-04-06 19:39 ` [PATCH 30/32] xfsprogs: Fix default superblock attr bits Darrick J. Wong
2023-04-06 19:39 ` [PATCH 31/32] mkfs: Add parent pointers during protofile creation Darrick J. Wong
2023-04-06 19:39 ` [PATCH 32/32] mkfs: enable formatting with parent pointers Darrick J. Wong
2023-04-06 19:16 ` [PATCHSET v11 0/7] xfs_repair: support " Darrick J. Wong
2023-04-06 19:40 ` [PATCH 1/7] xfs_repair: build a parent pointer index Darrick J. Wong
2023-04-06 19:40 ` [PATCH 2/7] xfs_repair: move the global dirent name store to a separate object Darrick J. Wong
2023-04-06 19:40 ` [PATCH 3/7] xfs_repair: deduplicate strings stored in string blob Darrick J. Wong
2023-04-06 19:40 ` [PATCH 4/7] xfs_repair: check parent pointers Darrick J. Wong
2023-04-06 19:41 ` [PATCH 5/7] xfs_repair: dump garbage parent pointer attributes Darrick J. Wong
2023-04-06 19:41 ` [PATCH 6/7] xfs_repair: update ondisk parent pointer records Darrick J. Wong
2023-04-06 19:41 ` [PATCH 7/7] xfs_repair: wipe ondisk parent pointers when there are none Darrick J. Wong
2023-04-06 19:16 ` [PATCHSET v11 00/11] fstests: adjust tests for xfs parent pointers Darrick J. Wong
2023-04-06 19:41 ` [PATCH 01/11] xfs/206: filter out the parent= status from mkfs Darrick J. Wong
2023-04-06 19:42 ` [PATCH 02/11] xfs/122: update for parent pointers Darrick J. Wong
2023-04-06 19:42 ` [PATCH 03/11] populate: create hardlinks " Darrick J. Wong
2023-04-06 19:42 ` [PATCH 04/11] xfs/021: adapt golden output files " Darrick J. Wong
2023-04-06 19:43 ` [PATCH 05/11] generic/050: adapt " Darrick J. Wong
2023-04-06 19:43 ` [PATCH 06/11] xfs/018: disable parent pointers for this test Darrick J. Wong
2023-04-06 19:43 ` [PATCH 07/11] xfs/306: fix formatting failures with parent pointers Darrick J. Wong
2023-04-06 19:43 ` [PATCH 08/11] common: add helpers for parent pointer tests Darrick J. Wong
2023-04-06 19:44 ` [PATCH 09/11] xfs: add parent pointer test Darrick J. Wong
2023-04-06 19:44 ` [PATCH 10/11] xfs: add multi link " Darrick J. Wong
2023-04-06 19:44 ` [PATCH 11/11] xfs: add parent pointer inject test Darrick J. Wong
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox