All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/13] rename & split tests
@ 2022-05-12 16:52 Christian Brauner
  2022-05-12 16:52 ` [PATCH v2 01/13] idmapped-mounts: make all tests set their required feature flags Christian Brauner
                   ` (12 more replies)
  0 siblings, 13 replies; 24+ messages in thread
From: Christian Brauner @ 2022-05-12 16:52 UTC (permalink / raw)
  To: Zorro Lang, fstests
  Cc: Christian Brauner (Microsoft),
	Dave Chinner, Eryu Guan, Amir Goldstein, Christoph Hellwig,
	Darrick J. Wong

From: "Christian Brauner (Microsoft)" <brauner@kernel.org>

Hey everyone,

Please note that this patch series contains patches that will be
rejected by the fstests mailing list because of the amount of changes
they contain. So tools like b4 will not be able to find the whole patch
series on a mailing list. In case it's helpful I've added the
"fstests.vfstest.for-next" tag which can be pulled. Otherwise it's
possible to simply use the patch series as it appears in your inbox.

All vfstests pass:

#### btrfs ####
ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
FSTYP         -- btrfs
PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
MKFS_OPTIONS  -- /dev/sda4
MOUNT_OPTIONS -- /dev/sda4 /mnt/scratch

btrfs/245 52s ...  54s
generic/633 58s ...  51s
generic/644 60s ...  49s
generic/645 161s ...  143s
generic/656 63s ...  55s
Ran: btrfs/245 generic/633 generic/644 generic/645 generic/656
Passed all 5 tests

#### ext4 ####
ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
FSTYP         -- ext4
PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
MKFS_OPTIONS  -- /dev/sda4
MOUNT_OPTIONS -- -o acl,user_xattr /dev/sda4 /mnt/scratch

generic/633 47s ...  50s
generic/644 46s ...  49s
generic/645 135s ...  139s
generic/656 53s ...  54s
Ran: generic/633 generic/644 generic/645 generic/656
Passed all 4 tests

#### xfs ####
ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
FSTYP         -- xfs (debug)
PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
MKFS_OPTIONS  -- -f /dev/sda4
MOUNT_OPTIONS -- /dev/sda4 /mnt/scratch

generic/633 58s ...  58s
generic/644 62s ...  60s
generic/645 161s ...  161s
generic/656 62s ...  63s
xfs/152 133s ...  133s
xfs/153 94s ...  92s
Ran: generic/633 generic/644 generic/645 generic/656 xfs/152 xfs/153
Passed all 6 tests

/* v2 */
* This rebases the patchset on top the for-next branch.
* Last week we merged 858a19c5e9b0 ("idmapped_mounts: Prepare for
  support for more features"). The patch switched feature checking from
  a boolean to a flag. It failed convert all tests. This adds a patch to
  fix this in patch 01/13.
* A patch has been added to remove an invalid test. The semantics for a
  specific corner-case where we allowed a mount's idmapping to change
  while there were active writers will be altered.

/* v1 */
As announced multiple times already we need to rename and split the
idmapped mount testsuite into separate source files and also give it a
better name to reflect the fact that it covers a lot more than just
idmapped mounts.

I have decided against compiling different binaries for now. Instead we
compile a single vfstest binary that can be called with various command
line switches to run the various test suites. This is not different than
what we did for the idmapped-mounts binary. Of course, nothing prevents
us from using multiple binaries in the future.

Thanks!
Christian

Christian Brauner (13):
  idmapped-mounts: make all tests set their required feature flags
  src: rename idmapped-mounts folder
  src/vfs: rename idmapped-mounts.c file
  vfstest: rename struct t_idmapped_mounts
  utils: add missing global.h include
  utils: add struct vfstest_info
  utils: move helpers into utils
  missing: move sys_execveat() to missing.h
  utils: add struct test_suite
  vfstests: split idmapped mount tests into separate suite
  vfstest: split out btrfs idmapped mounts test
  vfstest: split out remaining idmapped mount tests
  vfs/idmapped-mounts: remove invalid test

 .gitignore                                    |     4 +-
 common/rc                                     |    32 +-
 src/Makefile                                  |     2 +-
 src/detached_mounts_propagation.c             |     2 +-
 src/feature.c                                 |     2 +-
 src/idmapped-mounts/idmapped-mounts.c         | 14625 ----------------
 src/idmapped-mounts/utils.c                   |   425 -
 src/idmapped-mounts/utils.h                   |   130 -
 src/{idmapped-mounts => vfs}/Makefile         |    14 +-
 src/vfs/btrfs-idmapped-mounts.c               |  3854 ++++
 src/vfs/btrfs-idmapped-mounts.h               |    15 +
 src/vfs/idmapped-mounts.c                     |  7747 ++++++++
 src/vfs/idmapped-mounts.h                     |    18 +
 src/{idmapped-mounts => vfs}/missing.h        |    11 +
 src/{idmapped-mounts => vfs}/mount-idmapped.c |     0
 src/vfs/utils.c                               |  1050 ++
 src/vfs/utils.h                               |   373 +
 src/vfs/vfstest.c                             |  2073 +++
 tests/btrfs/245                               |     2 +-
 tests/generic/633                             |     2 +-
 tests/generic/644                             |     2 +-
 tests/generic/645                             |     2 +-
 tests/generic/656                             |     2 +-
 tests/xfs/152                                 |     4 +-
 tests/xfs/153                                 |     2 +-
 25 files changed, 15177 insertions(+), 15216 deletions(-)
 delete mode 100644 src/idmapped-mounts/idmapped-mounts.c
 delete mode 100644 src/idmapped-mounts/utils.c
 delete mode 100644 src/idmapped-mounts/utils.h
 rename src/{idmapped-mounts => vfs}/Makefile (59%)
 create mode 100644 src/vfs/btrfs-idmapped-mounts.c
 create mode 100644 src/vfs/btrfs-idmapped-mounts.h
 create mode 100644 src/vfs/idmapped-mounts.c
 create mode 100644 src/vfs/idmapped-mounts.h
 rename src/{idmapped-mounts => vfs}/missing.h (93%)
 rename src/{idmapped-mounts => vfs}/mount-idmapped.c (100%)
 create mode 100644 src/vfs/utils.c
 create mode 100644 src/vfs/utils.h
 create mode 100644 src/vfs/vfstest.c


base-commit: 87cf32ad3fa234e3d5ec501e0f86516bef91d805
-- 
2.34.1


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v2 01/13] idmapped-mounts: make all tests set their required feature flags
  2022-05-12 16:52 [PATCH v2 00/13] rename & split tests Christian Brauner
@ 2022-05-12 16:52 ` Christian Brauner
  2022-05-16  5:50   ` Christoph Hellwig
  2022-05-12 16:52 ` [PATCH v2 02/13] src: rename idmapped-mounts folder Christian Brauner
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 24+ messages in thread
From: Christian Brauner @ 2022-05-12 16:52 UTC (permalink / raw)
  To: Zorro Lang, fstests
  Cc: Christian Brauner, Dave Chinner, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong

In commit 858a19c5e9b0 ("idmapped_mounts: Prepare for support for more
features") we introduced a flag argument to check for multiple features
and then decide based on the flag argument whether a test requires a
given feature and skip it if the kernel doesn't provide it.

However, not all tests were ported to the new flag argument and still
use the old infrastructure. Port them all.

Fixes: 858a19c5e9b0 ("idmapped_mounts: Prepare for support for more features")
Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>
---
 src/idmapped-mounts/idmapped-mounts.c | 56 +++++++++++++--------------
 src/idmapped-mounts/utils.h           |  4 ++
 2 files changed, 30 insertions(+), 30 deletions(-)

diff --git a/src/idmapped-mounts/idmapped-mounts.c b/src/idmapped-mounts/idmapped-mounts.c
index f3873e52..e06185b7 100644
--- a/src/idmapped-mounts/idmapped-mounts.c
+++ b/src/idmapped-mounts/idmapped-mounts.c
@@ -14308,10 +14308,6 @@ static const struct option longopts[] = {
 	{NULL,					0,			0,	  0},
 };
 
-/* Flags for which functionality is required by the test */
-#define T_REQUIRE_IDMAPPED_MOUNTS (1U << 0)
-#define T_REQUIRE_USERNS (1U << 1)
-
 struct t_idmapped_mounts {
 	int (*test)(void);
 	unsigned int support_flags;
@@ -14369,46 +14365,46 @@ struct t_idmapped_mounts {
 };
 
 struct t_idmapped_mounts fscaps_in_ancestor_userns[] = {
-	{ fscaps_idmapped_mounts_in_userns_valid_in_ancestor_userns,	true,	"fscaps on idmapped mounts in user namespace writing fscap valid in ancestor userns",		},
+	{ fscaps_idmapped_mounts_in_userns_valid_in_ancestor_userns,	T_REQUIRE_IDMAPPED_MOUNTS,	"fscaps on idmapped mounts in user namespace writing fscap valid in ancestor userns",		},
 };
 
 struct t_idmapped_mounts t_nested_userns[] = {
-	{ nested_userns,						true,	"test that nested user namespaces behave correctly when attached to idmapped mounts",		},
+	{ nested_userns,						T_REQUIRE_IDMAPPED_MOUNTS,	"test that nested user namespaces behave correctly when attached to idmapped mounts",		},
 };
 
 struct t_idmapped_mounts t_btrfs[] = {
-	{ btrfs_subvolumes_fsids_mapped,				true,	"test subvolumes with mapped fsids",								},
-	{ btrfs_subvolumes_fsids_mapped_userns,				true, 	"test subvolumes with mapped fsids inside user namespace",					},
-	{ btrfs_subvolumes_fsids_mapped_user_subvol_rm_allowed,		true, 	"test subvolume deletion with user_subvol_rm_allowed mount option",				},
-	{ btrfs_subvolumes_fsids_mapped_userns_user_subvol_rm_allowed,	true, 	"test subvolume deletion with user_subvol_rm_allowed mount option inside user namespace",	},
-	{ btrfs_subvolumes_fsids_unmapped,				true, 	"test subvolumes with unmapped fsids",								},
-	{ btrfs_subvolumes_fsids_unmapped_userns,			true, 	"test subvolumes with unmapped fsids inside user namespace",					},
-	{ btrfs_snapshots_fsids_mapped,					true, 	"test snapshots with mapped fsids",								},
-	{ btrfs_snapshots_fsids_mapped_userns,				true, 	"test snapshots with mapped fsids inside user namespace",					},
-	{ btrfs_snapshots_fsids_mapped_user_subvol_rm_allowed,		true, 	"test snapshots deletion with user_subvol_rm_allowed mount option",				},
-	{ btrfs_snapshots_fsids_mapped_userns_user_subvol_rm_allowed,	true, 	"test snapshots deletion with user_subvol_rm_allowed mount option inside user namespace",	},
-	{ btrfs_snapshots_fsids_unmapped,				true, 	"test snapshots with unmapped fsids",								},
-	{ btrfs_snapshots_fsids_unmapped_userns,			true, 	"test snapshots with unmapped fsids inside user namespace",					},
-	{ btrfs_delete_by_spec_id,					true, 	"test subvolume deletion by spec id",								},
-	{ btrfs_subvolumes_setflags_fsids_mapped,			true, 	"test subvolume flags with mapped fsids",							},
-	{ btrfs_subvolumes_setflags_fsids_mapped_userns,		true, 	"test subvolume flags with mapped fsids inside user namespace",					},
-	{ btrfs_subvolumes_setflags_fsids_unmapped,			true, 	"test subvolume flags with unmapped fsids",							},
-	{ btrfs_subvolumes_setflags_fsids_unmapped_userns,		true, 	"test subvolume flags with unmapped fsids inside user namespace",				},
-	{ btrfs_snapshots_setflags_fsids_mapped,			true, 	"test snapshots flags with mapped fsids",							},
-	{ btrfs_snapshots_setflags_fsids_mapped_userns,			true, 	"test snapshots flags with mapped fsids inside user namespace",					},
-	{ btrfs_snapshots_setflags_fsids_unmapped,			true, 	"test snapshots flags with unmapped fsids",							},
-	{ btrfs_snapshots_setflags_fsids_unmapped_userns,		true, 	"test snapshots flags with unmapped fsids inside user namespace",				},
-	{ btrfs_subvolume_lookup_user,					true, 	"test unprivileged subvolume lookup",								},
+	{ btrfs_subvolumes_fsids_mapped,				T_REQUIRE_IDMAPPED_MOUNTS,	"test subvolumes with mapped fsids",								},
+	{ btrfs_subvolumes_fsids_mapped_userns,				T_REQUIRE_IDMAPPED_MOUNTS, 	"test subvolumes with mapped fsids inside user namespace",					},
+	{ btrfs_subvolumes_fsids_mapped_user_subvol_rm_allowed,		T_REQUIRE_IDMAPPED_MOUNTS, 	"test subvolume deletion with user_subvol_rm_allowed mount option",				},
+	{ btrfs_subvolumes_fsids_mapped_userns_user_subvol_rm_allowed,	T_REQUIRE_IDMAPPED_MOUNTS, 	"test subvolume deletion with user_subvol_rm_allowed mount option inside user namespace",	},
+	{ btrfs_subvolumes_fsids_unmapped,				T_REQUIRE_IDMAPPED_MOUNTS, 	"test subvolumes with unmapped fsids",								},
+	{ btrfs_subvolumes_fsids_unmapped_userns,			T_REQUIRE_IDMAPPED_MOUNTS, 	"test subvolumes with unmapped fsids inside user namespace",					},
+	{ btrfs_snapshots_fsids_mapped,					T_REQUIRE_IDMAPPED_MOUNTS, 	"test snapshots with mapped fsids",								},
+	{ btrfs_snapshots_fsids_mapped_userns,				T_REQUIRE_IDMAPPED_MOUNTS, 	"test snapshots with mapped fsids inside user namespace",					},
+	{ btrfs_snapshots_fsids_mapped_user_subvol_rm_allowed,		T_REQUIRE_IDMAPPED_MOUNTS, 	"test snapshots deletion with user_subvol_rm_allowed mount option",				},
+	{ btrfs_snapshots_fsids_mapped_userns_user_subvol_rm_allowed,	T_REQUIRE_IDMAPPED_MOUNTS, 	"test snapshots deletion with user_subvol_rm_allowed mount option inside user namespace",	},
+	{ btrfs_snapshots_fsids_unmapped,				T_REQUIRE_IDMAPPED_MOUNTS, 	"test snapshots with unmapped fsids",								},
+	{ btrfs_snapshots_fsids_unmapped_userns,			T_REQUIRE_IDMAPPED_MOUNTS, 	"test snapshots with unmapped fsids inside user namespace",					},
+	{ btrfs_delete_by_spec_id,					T_REQUIRE_IDMAPPED_MOUNTS, 	"test subvolume deletion by spec id",								},
+	{ btrfs_subvolumes_setflags_fsids_mapped,			T_REQUIRE_IDMAPPED_MOUNTS, 	"test subvolume flags with mapped fsids",							},
+	{ btrfs_subvolumes_setflags_fsids_mapped_userns,		T_REQUIRE_IDMAPPED_MOUNTS, 	"test subvolume flags with mapped fsids inside user namespace",					},
+	{ btrfs_subvolumes_setflags_fsids_unmapped,			T_REQUIRE_IDMAPPED_MOUNTS, 	"test subvolume flags with unmapped fsids",							},
+	{ btrfs_subvolumes_setflags_fsids_unmapped_userns,		T_REQUIRE_IDMAPPED_MOUNTS, 	"test subvolume flags with unmapped fsids inside user namespace",				},
+	{ btrfs_snapshots_setflags_fsids_mapped,			T_REQUIRE_IDMAPPED_MOUNTS, 	"test snapshots flags with mapped fsids",							},
+	{ btrfs_snapshots_setflags_fsids_mapped_userns,			T_REQUIRE_IDMAPPED_MOUNTS, 	"test snapshots flags with mapped fsids inside user namespace",					},
+	{ btrfs_snapshots_setflags_fsids_unmapped,			T_REQUIRE_IDMAPPED_MOUNTS, 	"test snapshots flags with unmapped fsids",							},
+	{ btrfs_snapshots_setflags_fsids_unmapped_userns,		T_REQUIRE_IDMAPPED_MOUNTS, 	"test snapshots flags with unmapped fsids inside user namespace",				},
+	{ btrfs_subvolume_lookup_user,					T_REQUIRE_IDMAPPED_MOUNTS, 	"test unprivileged subvolume lookup",								},
 };
 
 /* Test for commit 968219708108 ("fs: handle circular mappings correctly"). */
 struct t_idmapped_mounts t_setattr_fix_968219708108[] = {
-	{ setattr_fix_968219708108,					true,	"test that setattr works correctly",								},
+	{ setattr_fix_968219708108,					T_REQUIRE_IDMAPPED_MOUNTS,	"test that setattr works correctly",								},
 };
 
 /* Test for commit 705191b03d50 ("fs: fix acl translation"). */
 struct t_idmapped_mounts t_setxattr_fix_705191b03d50[] = {
-	{ setxattr_fix_705191b03d50,					false,	"test that setxattr works correctly for userns mountable filesystems",				},
+	{ setxattr_fix_705191b03d50,					T_REQUIRE_USERNS,		"test that setxattr works correctly for userns mountable filesystems",				},
 };
 
 static bool run_test(struct t_idmapped_mounts suite[], size_t suite_size)
diff --git a/src/idmapped-mounts/utils.h b/src/idmapped-mounts/utils.h
index afb3c228..a62a4844 100644
--- a/src/idmapped-mounts/utils.h
+++ b/src/idmapped-mounts/utils.h
@@ -20,6 +20,10 @@
 
 #include "missing.h"
 
+/* Flags for which functionality is required by the test */
+#define T_REQUIRE_IDMAPPED_MOUNTS (1U << 0)
+#define T_REQUIRE_USERNS (1U << 1)
+
 /* Maximum number of nested user namespaces in the kernel. */
 #define MAX_USERNS_LEVEL 32
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 02/13] src: rename idmapped-mounts folder
  2022-05-12 16:52 [PATCH v2 00/13] rename & split tests Christian Brauner
  2022-05-12 16:52 ` [PATCH v2 01/13] idmapped-mounts: make all tests set their required feature flags Christian Brauner
@ 2022-05-12 16:52 ` Christian Brauner
  2022-05-12 16:52 ` [PATCH v2 03/13] src/vfs: rename idmapped-mounts.c file Christian Brauner
                   ` (10 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Christian Brauner @ 2022-05-12 16:52 UTC (permalink / raw)
  To: Zorro Lang, fstests
  Cc: Christian Brauner, Dave Chinner, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong

The idmapped mounts test suite has grown to cover a lot of generic vfs
functionality that is not concerned with idmapped mounts at all.

As was discussed upstream it's time to rename it to something that
reflects its generic nature. So rename it from idmapped-mounts to vfs.

Cc: Dave Chinner <david@fromorbit.com>
Cc: Amir Goldstein <amir73il@gmail.com>
Cc: Eryu Guan <guaneryu@gmail.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Zorro Lang <zlang@redhat.com>
Cc: "Darrick J. Wong" <djwong@kernel.org>
Cc: fstests <fstests@vger.kernel.org>
Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>
---
 .gitignore                                    |  4 +--
 common/rc                                     | 28 +++++++++----------
 src/Makefile                                  |  2 +-
 src/detached_mounts_propagation.c             |  2 +-
 src/feature.c                                 |  2 +-
 src/{idmapped-mounts => vfs}/Makefile         |  4 +--
 .../idmapped-mounts.c                         |  0
 src/{idmapped-mounts => vfs}/missing.h        |  0
 src/{idmapped-mounts => vfs}/mount-idmapped.c |  0
 src/{idmapped-mounts => vfs}/utils.c          |  0
 src/{idmapped-mounts => vfs}/utils.h          |  0
 tests/btrfs/245                               |  2 +-
 tests/generic/633                             |  2 +-
 tests/generic/644                             |  2 +-
 tests/generic/645                             |  2 +-
 tests/generic/656                             |  2 +-
 tests/xfs/152                                 |  2 +-
 tests/xfs/153                                 |  2 +-
 18 files changed, 28 insertions(+), 28 deletions(-)
 rename src/{idmapped-mounts => vfs}/Makefile (86%)
 rename src/{idmapped-mounts => vfs}/idmapped-mounts.c (100%)
 rename src/{idmapped-mounts => vfs}/missing.h (100%)
 rename src/{idmapped-mounts => vfs}/mount-idmapped.c (100%)
 rename src/{idmapped-mounts => vfs}/utils.c (100%)
 rename src/{idmapped-mounts => vfs}/utils.h (100%)

diff --git a/.gitignore b/.gitignore
index 5f24909e..b9e42635 100644
--- a/.gitignore
+++ b/.gitignore
@@ -192,8 +192,8 @@ tags
 /src/aio-dio-regress/aio-last-ref-held-by-io
 /src/aio-dio-regress/aiocp
 /src/aio-dio-regress/aiodio_sparse2
-/src/idmapped-mounts/idmapped-mounts
-/src/idmapped-mounts/mount-idmapped
+/src/vfs/idmapped-mounts
+/src/vfs/mount-idmapped
 /src/log-writes/replay-log
 /src/perf/*.pyc
 
diff --git a/common/rc b/common/rc
index e907e362..df724a96 100644
--- a/common/rc
+++ b/common/rc
@@ -355,23 +355,23 @@ _scratch_mount_idmapped()
 	if [ "$type" = "u" ]; then
 		# This means root will be able to create files as uid %id in
 		# the underlying filesystem by going through the idmapped mount.
-		$here/src/idmapped-mounts/mount-idmapped --map-mount u:0:$id:1 \
-							 --map-mount u:$id:0:1 \
-							 --map-mount g:0:0:1 \
-							 "$SCRATCH_MNT" "$SCRATCH_MNT" || _fail "mount-idmapped failed"
+		$here/src/vfs/mount-idmapped --map-mount u:0:$id:1 \
+					     --map-mount u:$id:0:1 \
+					     --map-mount g:0:0:1 \
+					     "$SCRATCH_MNT" "$SCRATCH_MNT" || _fail "mount-idmapped failed"
 	elif [ "$type" = "g" ]; then
 		# This means root will be able to create files as gid %id in
 		# the underlying filesystem by going through the idmapped mount.
-		$here/src/idmapped-mounts/mount-idmapped --map-mount g:0:$id:1 \
-							 --map-mount g:$id:0:1 \
-							 --map-mount u:0:0:1 \
-							 "$SCRATCH_MNT" "$SCRATCH_MNT" || _fail "mount-idmapped failed"
+		$here/src/vfs/mount-idmapped --map-mount g:0:$id:1 \
+					     --map-mount g:$id:0:1 \
+					     --map-mount u:0:0:1 \
+					     "$SCRATCH_MNT" "$SCRATCH_MNT" || _fail "mount-idmapped failed"
 	elif [ "$type" = "b" ]; then
 		# This means root will be able to create files as uid and gid
 		# %id in the underlying filesystem by going through the idmapped mount.
-		$here/src/idmapped-mounts/mount-idmapped --map-mount b:0:$id:1 \
-							 --map-mount b:$id:0:1 \
-							 "$SCRATCH_MNT" "$SCRATCH_MNT" || _fail "mount-idmapped failed"
+		$here/src/vfs/mount-idmapped --map-mount b:0:$id:1 \
+					     --map-mount b:$id:0:1 \
+					     "$SCRATCH_MNT" "$SCRATCH_MNT" || _fail "mount-idmapped failed"
 	else
 		_fail "usage: either \"u\" (uid), \"g\" (gid), or \"b\" (uid and gid) must be specified "
 	fi
@@ -480,7 +480,7 @@ _idmapped_mount()
 	# {g,u}id 10000000 and $(id -u fsgqa) + 10000000. We change ownership
         # of $mnt so {g,u} id 0 can actually create objects in there.
 	chown 10000000:10000000 $mnt || return 1
-	$here/src/idmapped-mounts/mount-idmapped \
+	$here/src/vfs/mount-idmapped \
 		--map-mount b:10000000:0:100000000000 \
 		$mnt $tmp
 	if [ $? -ne 0 ]; then
@@ -2349,12 +2349,12 @@ _require_mount_setattr()
 # test whether idmapped mounts are supported
 _require_idmapped_mounts()
 {
-        IDMAPPED_MOUNTS_TEST=$here/src/idmapped-mounts/idmapped-mounts
+        IDMAPPED_MOUNTS_TEST=$here/src/vfs/idmapped-mounts
         [ -x $IDMAPPED_MOUNTS_TEST ] || _notrun "idmapped-mounts utilities required"
 
 	_require_mount_setattr
 
-	$here/src/idmapped-mounts/idmapped-mounts --supported \
+	$here/src/vfs/idmapped-mounts --supported \
 		--device "$TEST_DEV" \
 		--mount "$TEST_DIR" \
 		--fstype "$FSTYP"
diff --git a/src/Makefile b/src/Makefile
index 24aef09b..7eeb08ef 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -76,7 +76,7 @@ TARGETS += uring_read_fault
 LLDLIBS += -luring
 endif
 
-SUBDIRS += idmapped-mounts
+SUBDIRS += vfs
 ifeq ($(HAVE_LIBCAP), true)
 LLDLIBS += -lcap
 endif
diff --git a/src/detached_mounts_propagation.c b/src/detached_mounts_propagation.c
index d4bc87f9..17db2c02 100644
--- a/src/detached_mounts_propagation.c
+++ b/src/detached_mounts_propagation.c
@@ -26,7 +26,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include "idmapped-mounts/missing.h"
+#include "vfs/missing.h"
 
 static bool is_shared_mountpoint(const char *path)
 {
diff --git a/src/feature.c b/src/feature.c
index bc0b0b30..941f96fb 100644
--- a/src/feature.c
+++ b/src/feature.c
@@ -46,7 +46,7 @@
 #include <liburing.h>
 #endif
 
-#include "idmapped-mounts/missing.h"
+#include "vfs/missing.h"
 
 #ifndef USRQUOTA
 #define USRQUOTA  0
diff --git a/src/idmapped-mounts/Makefile b/src/vfs/Makefile
similarity index 86%
rename from src/idmapped-mounts/Makefile
rename to src/vfs/Makefile
index ad4ddc99..2df3daf8 100644
--- a/src/idmapped-mounts/Makefile
+++ b/src/vfs/Makefile
@@ -34,7 +34,7 @@ mount-idmapped: $(CFILES_MOUNT_IDMAPPED)
 	$(Q)$(LTLINK) $(CFILES_MOUNT_IDMAPPED) -o $@ $(CFLAGS) $(LDFLAGS) $(LDLIBS)
 
 install:
-	$(INSTALL) -m 755 -d $(PKG_LIB_DIR)/src/idmapped-mounts
-	$(INSTALL) -m 755 $(TARGETS) $(PKG_LIB_DIR)/src/idmapped-mounts
+	$(INSTALL) -m 755 -d $(PKG_LIB_DIR)/src/vfs
+	$(INSTALL) -m 755 $(TARGETS) $(PKG_LIB_DIR)/src/vfs
 
 -include .dep
diff --git a/src/idmapped-mounts/idmapped-mounts.c b/src/vfs/idmapped-mounts.c
similarity index 100%
rename from src/idmapped-mounts/idmapped-mounts.c
rename to src/vfs/idmapped-mounts.c
diff --git a/src/idmapped-mounts/missing.h b/src/vfs/missing.h
similarity index 100%
rename from src/idmapped-mounts/missing.h
rename to src/vfs/missing.h
diff --git a/src/idmapped-mounts/mount-idmapped.c b/src/vfs/mount-idmapped.c
similarity index 100%
rename from src/idmapped-mounts/mount-idmapped.c
rename to src/vfs/mount-idmapped.c
diff --git a/src/idmapped-mounts/utils.c b/src/vfs/utils.c
similarity index 100%
rename from src/idmapped-mounts/utils.c
rename to src/vfs/utils.c
diff --git a/src/idmapped-mounts/utils.h b/src/vfs/utils.h
similarity index 100%
rename from src/idmapped-mounts/utils.h
rename to src/vfs/utils.h
diff --git a/tests/btrfs/245 b/tests/btrfs/245
index f3380ac2..6403f878 100755
--- a/tests/btrfs/245
+++ b/tests/btrfs/245
@@ -26,7 +26,7 @@ _scratch_mount "-o user_subvol_rm_allowed" >> $seqres.full
 
 echo "Silence is golden"
 
-$here/src/idmapped-mounts/idmapped-mounts --test-btrfs --device "$TEST_DEV" \
+$here/src/vfs/idmapped-mounts --test-btrfs --device "$TEST_DEV" \
 	--mountpoint "$TEST_DIR" --scratch-device "$SCRATCH_DEV" \
 	--scratch-mountpoint "$SCRATCH_MNT" --fstype "$FSTYP"
 
diff --git a/tests/generic/633 b/tests/generic/633
index 38280647..2054b646 100755
--- a/tests/generic/633
+++ b/tests/generic/633
@@ -19,7 +19,7 @@ _require_test
 
 echo "Silence is golden"
 
-$here/src/idmapped-mounts/idmapped-mounts --test-core --device "$TEST_DEV" \
+$here/src/vfs/idmapped-mounts --test-core --device "$TEST_DEV" \
 	--mount "$TEST_DIR" --fstype "$FSTYP"
 
 status=$?
diff --git a/tests/generic/644 b/tests/generic/644
index 9ed5a511..17fc0539 100755
--- a/tests/generic/644
+++ b/tests/generic/644
@@ -21,7 +21,7 @@ _require_test
 
 echo "Silence is golden"
 
-$here/src/idmapped-mounts/idmapped-mounts --test-fscaps-regression \
+$here/src/vfs/idmapped-mounts --test-fscaps-regression \
 	--device "$TEST_DEV" --mount "$TEST_DIR" --fstype "$FSTYP"
 
 status=$?
diff --git a/tests/generic/645 b/tests/generic/645
index ffe30bb4..f1209ad0 100755
--- a/tests/generic/645
+++ b/tests/generic/645
@@ -21,7 +21,7 @@ _require_test
 
 echo "Silence is golden"
 
-$here/src/idmapped-mounts/idmapped-mounts --test-nested-userns \
+$here/src/vfs/idmapped-mounts --test-nested-userns \
 	--device "$TEST_DEV" --mount "$TEST_DIR" --fstype "$FSTYP"
 
 status=$?
diff --git a/tests/generic/656 b/tests/generic/656
index 1231de31..9e95ac96 100755
--- a/tests/generic/656
+++ b/tests/generic/656
@@ -26,7 +26,7 @@ _require_group fsgqa2
 
 echo "Silence is golden"
 
-$here/src/idmapped-mounts/idmapped-mounts --test-setattr-fix-968219708108 \
+$here/src/vfs/idmapped-mounts --test-setattr-fix-968219708108 \
 	--device "$TEST_DEV" --mount "$TEST_DIR" --fstype "$FSTYP"
 
 status=$?
diff --git a/tests/xfs/152 b/tests/xfs/152
index 129d9c06..de9b8fc6 100755
--- a/tests/xfs/152
+++ b/tests/xfs/152
@@ -34,7 +34,7 @@ _cleanup()
 # real QA test starts here
 _supported_fs xfs
 _require_idmapped_mounts
-_require_test_program "idmapped-mounts/mount-idmapped"
+_require_test_program "vfs/mount-idmapped"
 _require_scratch
 _require_xfs_quota
 _require_user fsgqa
diff --git a/tests/xfs/153 b/tests/xfs/153
index 37303701..8e1430c0 100755
--- a/tests/xfs/153
+++ b/tests/xfs/153
@@ -34,7 +34,7 @@ _require_scratch
 _require_xfs_quota
 _require_user fsgqa
 _require_idmapped_mounts
-_require_test_program "idmapped-mounts/mount-idmapped"
+_require_test_program "vfs/mount-idmapped"
 
 _scratch_mkfs >/dev/null 2>&1
 _scratch_mount
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 03/13] src/vfs: rename idmapped-mounts.c file
  2022-05-12 16:52 [PATCH v2 00/13] rename & split tests Christian Brauner
  2022-05-12 16:52 ` [PATCH v2 01/13] idmapped-mounts: make all tests set their required feature flags Christian Brauner
  2022-05-12 16:52 ` [PATCH v2 02/13] src: rename idmapped-mounts folder Christian Brauner
@ 2022-05-12 16:52 ` Christian Brauner
  2022-05-12 16:52 ` [PATCH v2 04/13] vfstest: rename struct t_idmapped_mounts Christian Brauner
                   ` (9 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Christian Brauner @ 2022-05-12 16:52 UTC (permalink / raw)
  To: Zorro Lang, fstests
  Cc: Christian Brauner, Dave Chinner, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong

The idmapped mounts test suite has grown to cover a lot of generic vfs
functionality that is not concerned with idmapped mounts at all.

As was discussed upstream it's time to rename it to something that
reflects its generic nature. So rename the source file from
idmapped-mounts.c to vfstest.c before we split it into multiple source
files in the next patches.

Cc: Dave Chinner <david@fromorbit.com>
Cc: Amir Goldstein <amir73il@gmail.com>
Cc: Eryu Guan <guaneryu@gmail.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Zorro Lang <zlang@redhat.com>
Cc: "Darrick J. Wong" <djwong@kernel.org>
Cc: fstests <fstests@vger.kernel.org>
Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>
---
 .gitignore                               |  2 +-
 common/rc                                |  8 ++++----
 src/vfs/Makefile                         |  8 ++++----
 src/vfs/{idmapped-mounts.c => vfstest.c} | 14 +++++++-------
 tests/btrfs/245                          |  2 +-
 tests/generic/633                        |  2 +-
 tests/generic/644                        |  2 +-
 tests/generic/645                        |  2 +-
 tests/generic/656                        |  2 +-
 tests/xfs/152                            |  2 +-
 10 files changed, 22 insertions(+), 22 deletions(-)
 rename src/vfs/{idmapped-mounts.c => vfstest.c} (99%)

diff --git a/.gitignore b/.gitignore
index b9e42635..88c79412 100644
--- a/.gitignore
+++ b/.gitignore
@@ -192,7 +192,7 @@ tags
 /src/aio-dio-regress/aio-last-ref-held-by-io
 /src/aio-dio-regress/aiocp
 /src/aio-dio-regress/aiodio_sparse2
-/src/vfs/idmapped-mounts
+/src/vfs/vfstest
 /src/vfs/mount-idmapped
 /src/log-writes/replay-log
 /src/perf/*.pyc
diff --git a/common/rc b/common/rc
index df724a96..28e4c11f 100644
--- a/common/rc
+++ b/common/rc
@@ -2349,18 +2349,18 @@ _require_mount_setattr()
 # test whether idmapped mounts are supported
 _require_idmapped_mounts()
 {
-        IDMAPPED_MOUNTS_TEST=$here/src/vfs/idmapped-mounts
-        [ -x $IDMAPPED_MOUNTS_TEST ] || _notrun "idmapped-mounts utilities required"
+        IDMAPPED_MOUNTS_TEST=$here/src/vfs/vfstest
+        [ -x $IDMAPPED_MOUNTS_TEST ] || _notrun "vfstest utilities required"
 
 	_require_mount_setattr
 
-	$here/src/vfs/idmapped-mounts --supported \
+	$here/src/vfs/vfstest --idmapped-mounts-supported \
 		--device "$TEST_DEV" \
 		--mount "$TEST_DIR" \
 		--fstype "$FSTYP"
 
 	if [ $? -ne 0 ]; then
-		_notrun "idmapped-mounts not support by $FSTYP"
+		_notrun "vfstest not support by $FSTYP"
 	fi
 }
 
diff --git a/src/vfs/Makefile b/src/vfs/Makefile
index 2df3daf8..adef9ff3 100644
--- a/src/vfs/Makefile
+++ b/src/vfs/Makefile
@@ -3,8 +3,8 @@
 TOPDIR = ../..
 include $(TOPDIR)/include/builddefs
 
-TARGETS = idmapped-mounts mount-idmapped
-CFILES_IDMAPPED_MOUNTS = idmapped-mounts.c utils.c
+TARGETS = vfstest mount-idmapped
+CFILES_VFSTEST = vfstest.c utils.c
 CFILES_MOUNT_IDMAPPED = mount-idmapped.c utils.c
 
 HFILES = missing.h utils.h
@@ -25,9 +25,9 @@ depend: .dep
 
 include $(BUILDRULES)
 
-idmapped-mounts: $(CFILES_IDMAPPED_MOUNTS)
+vfstest: $(CFILES_VFSTEST)
 	@echo "    [CC]    $@"
-	$(Q)$(LTLINK) $(CFILES_IDMAPPED_MOUNTS) -o $@ $(CFLAGS) $(LDFLAGS) $(LDLIBS)
+	$(Q)$(LTLINK) $(CFILES_VFSTEST) -o $@ $(CFLAGS) $(LDFLAGS) $(LDLIBS)
 
 mount-idmapped: $(CFILES_MOUNT_IDMAPPED)
 	@echo "    [CC]    $@"
diff --git a/src/vfs/idmapped-mounts.c b/src/vfs/vfstest.c
similarity index 99%
rename from src/vfs/idmapped-mounts.c
rename to src/vfs/vfstest.c
index e06185b7..7f57b777 100644
--- a/src/vfs/idmapped-mounts.c
+++ b/src/vfs/vfstest.c
@@ -14278,7 +14278,7 @@ static void usage(void)
 	fprintf(stderr, "--fstype                            Filesystem type used in the tests\n");
 	fprintf(stderr, "--help                              Print help\n");
 	fprintf(stderr, "--mountpoint                        Mountpoint of device\n");
-	fprintf(stderr, "--supported                         Test whether idmapped mounts are supported on this filesystem\n");
+	fprintf(stderr, "--idmapped-mounts-supported	     Test whether idmapped mounts are supported on this filesystem\n");
 	fprintf(stderr, "--scratch-mountpoint                Mountpoint of scratch device used in the tests\n");
 	fprintf(stderr, "--scratch-device                    Scratch device used in the tests\n");
 	fprintf(stderr, "--test-core                         Run core idmapped mount testsuite\n");
@@ -14297,7 +14297,7 @@ static const struct option longopts[] = {
 	{"mountpoint",				required_argument,	0,	'm'},
 	{"scratch-mountpoint",			required_argument,	0,	'a'},
 	{"scratch-device",			required_argument,	0,	'e'},
-	{"supported",				no_argument,		0,	's'},
+	{"idmapped-mounts-supported",		no_argument,		0,	's'},
 	{"help",				no_argument,		0,	'h'},
 	{"test-core",				no_argument,		0,	'c'},
 	{"test-fscaps-regression",		no_argument,		0,	'g'},
@@ -14494,9 +14494,9 @@ int main(int argc, char *argv[])
 {
 	int fret, ret;
 	int index = 0;
-	bool supported = false, test_btrfs = false, test_core = false,
-	     test_fscaps_regression = false, test_nested_userns = false,
-	     test_setattr_fix_968219708108 = false,
+	bool idmapped_mounts_supported = false, test_btrfs = false,
+	     test_core = false, test_fscaps_regression = false,
+	     test_nested_userns = false, test_setattr_fix_968219708108 = false,
 	     test_setxattr_fix_705191b03d50 = false;
 
 	while ((ret = getopt_long_only(argc, argv, "", longopts, &index)) != -1) {
@@ -14511,7 +14511,7 @@ int main(int argc, char *argv[])
 			t_mountpoint = optarg;
 			break;
 		case 's':
-			supported = true;
+			idmapped_mounts_supported = true;
 			break;
 		case 'c':
 			test_core = true;
@@ -14570,7 +14570,7 @@ int main(int argc, char *argv[])
 		die("failed to open %s", t_mountpoint_scratch);
 
 	t_fs_allow_idmap = fs_allow_idmap();
-	if (supported) {
+	if (idmapped_mounts_supported) {
 		/*
 		 * Caller just wants to know whether the filesystem we're on
 		 * supports idmapped mounts.
diff --git a/tests/btrfs/245 b/tests/btrfs/245
index 6403f878..dadc8492 100755
--- a/tests/btrfs/245
+++ b/tests/btrfs/245
@@ -26,7 +26,7 @@ _scratch_mount "-o user_subvol_rm_allowed" >> $seqres.full
 
 echo "Silence is golden"
 
-$here/src/vfs/idmapped-mounts --test-btrfs --device "$TEST_DEV" \
+$here/src/vfs/vfstest --test-btrfs --device "$TEST_DEV" \
 	--mountpoint "$TEST_DIR" --scratch-device "$SCRATCH_DEV" \
 	--scratch-mountpoint "$SCRATCH_MNT" --fstype "$FSTYP"
 
diff --git a/tests/generic/633 b/tests/generic/633
index 2054b646..9b29dbf1 100755
--- a/tests/generic/633
+++ b/tests/generic/633
@@ -19,7 +19,7 @@ _require_test
 
 echo "Silence is golden"
 
-$here/src/vfs/idmapped-mounts --test-core --device "$TEST_DEV" \
+$here/src/vfs/vfstest --test-core --device "$TEST_DEV" \
 	--mount "$TEST_DIR" --fstype "$FSTYP"
 
 status=$?
diff --git a/tests/generic/644 b/tests/generic/644
index 17fc0539..edf9b03e 100755
--- a/tests/generic/644
+++ b/tests/generic/644
@@ -21,7 +21,7 @@ _require_test
 
 echo "Silence is golden"
 
-$here/src/vfs/idmapped-mounts --test-fscaps-regression \
+$here/src/vfs/vfstest --test-fscaps-regression \
 	--device "$TEST_DEV" --mount "$TEST_DIR" --fstype "$FSTYP"
 
 status=$?
diff --git a/tests/generic/645 b/tests/generic/645
index f1209ad0..74e0f589 100755
--- a/tests/generic/645
+++ b/tests/generic/645
@@ -21,7 +21,7 @@ _require_test
 
 echo "Silence is golden"
 
-$here/src/vfs/idmapped-mounts --test-nested-userns \
+$here/src/vfs/vfstest --test-nested-userns \
 	--device "$TEST_DEV" --mount "$TEST_DIR" --fstype "$FSTYP"
 
 status=$?
diff --git a/tests/generic/656 b/tests/generic/656
index 9e95ac96..5c090cfa 100755
--- a/tests/generic/656
+++ b/tests/generic/656
@@ -26,7 +26,7 @@ _require_group fsgqa2
 
 echo "Silence is golden"
 
-$here/src/vfs/idmapped-mounts --test-setattr-fix-968219708108 \
+$here/src/vfs/vfstest --test-setattr-fix-968219708108 \
 	--device "$TEST_DEV" --mount "$TEST_DIR" --fstype "$FSTYP"
 
 status=$?
diff --git a/tests/xfs/152 b/tests/xfs/152
index de9b8fc6..dd33801d 100755
--- a/tests/xfs/152
+++ b/tests/xfs/152
@@ -246,7 +246,7 @@ qmount_idmapped()
 	mkdir -p "${SCRATCH_MNT}/unmapped"
 	mkdir -p "${SCRATCH_MNT}/idmapped"
 
-	$here/src/idmapped-mounts/mount-idmapped \
+	$here/src/vfs/mount-idmapped \
 		--map-mount b:$id:$id2:1 \
 		--map-mount b:0:0:1 \
 		"$SCRATCH_MNT/unmapped" "$SCRATCH_MNT/idmapped" || _fail "mount-idmapped failed"
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 04/13] vfstest: rename struct t_idmapped_mounts
  2022-05-12 16:52 [PATCH v2 00/13] rename & split tests Christian Brauner
                   ` (2 preceding siblings ...)
  2022-05-12 16:52 ` [PATCH v2 03/13] src/vfs: rename idmapped-mounts.c file Christian Brauner
@ 2022-05-12 16:52 ` Christian Brauner
  2022-05-12 16:52 ` [PATCH v2 05/13] utils: add missing global.h include Christian Brauner
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Christian Brauner @ 2022-05-12 16:52 UTC (permalink / raw)
  To: Zorro Lang, fstests
  Cc: Christian Brauner, Dave Chinner, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong

The idmapped mounts test suite has grown to cover a lot of generic vfs
functionality that is not concerned with idmapped mounts at all.

As was discussed upstream it's time to rename it to something that
reflects its generic nature.

Rename the basic structure used for the tests from struct
t_idmapped_mounts to struct test_struct.

Cc: Dave Chinner <david@fromorbit.com>
Cc: Amir Goldstein <amir73il@gmail.com>
Cc: Eryu Guan <guaneryu@gmail.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Zorro Lang <zlang@redhat.com>
Cc: "Darrick J. Wong" <djwong@kernel.org>
Cc: fstests <fstests@vger.kernel.org>
Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>
---
 src/vfs/utils.h   |  6 ++++++
 src/vfs/vfstest.c | 20 ++++++++------------
 2 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/src/vfs/utils.h b/src/vfs/utils.h
index a62a4844..c1024646 100644
--- a/src/vfs/utils.h
+++ b/src/vfs/utils.h
@@ -53,6 +53,12 @@
 		__internal_ret__;                             \
 	})
 
+struct test_struct {
+	int (*test)(void);
+	unsigned int support_flags;
+	const char *description;
+};
+
 typedef enum idmap_type_t {
 	ID_TYPE_UID,
 	ID_TYPE_GID
diff --git a/src/vfs/vfstest.c b/src/vfs/vfstest.c
index 7f57b777..e615c4e1 100644
--- a/src/vfs/vfstest.c
+++ b/src/vfs/vfstest.c
@@ -14308,11 +14308,7 @@ static const struct option longopts[] = {
 	{NULL,					0,			0,	  0},
 };
 
-struct t_idmapped_mounts {
-	int (*test)(void);
-	unsigned int support_flags;
-	const char *description;
-} basic_suite[] = {
+struct test_struct basic_suite[] = {
 	{ acls,								T_REQUIRE_IDMAPPED_MOUNTS,	"posix acls on regular mounts",									},
 	{ create_in_userns,						T_REQUIRE_IDMAPPED_MOUNTS,	"create operations in user namespace",								},
 	{ device_node_in_userns,					T_REQUIRE_IDMAPPED_MOUNTS,	"device node in user namespace",								},
@@ -14364,15 +14360,15 @@ struct t_idmapped_mounts {
 	{ threaded_idmapped_mount_interactions,				T_REQUIRE_IDMAPPED_MOUNTS,	"threaded operations on idmapped mounts",							},
 };
 
-struct t_idmapped_mounts fscaps_in_ancestor_userns[] = {
+struct test_struct fscaps_in_ancestor_userns[] = {
 	{ fscaps_idmapped_mounts_in_userns_valid_in_ancestor_userns,	T_REQUIRE_IDMAPPED_MOUNTS,	"fscaps on idmapped mounts in user namespace writing fscap valid in ancestor userns",		},
 };
 
-struct t_idmapped_mounts t_nested_userns[] = {
+struct test_struct t_nested_userns[] = {
 	{ nested_userns,						T_REQUIRE_IDMAPPED_MOUNTS,	"test that nested user namespaces behave correctly when attached to idmapped mounts",		},
 };
 
-struct t_idmapped_mounts t_btrfs[] = {
+struct test_struct t_btrfs[] = {
 	{ btrfs_subvolumes_fsids_mapped,				T_REQUIRE_IDMAPPED_MOUNTS,	"test subvolumes with mapped fsids",								},
 	{ btrfs_subvolumes_fsids_mapped_userns,				T_REQUIRE_IDMAPPED_MOUNTS, 	"test subvolumes with mapped fsids inside user namespace",					},
 	{ btrfs_subvolumes_fsids_mapped_user_subvol_rm_allowed,		T_REQUIRE_IDMAPPED_MOUNTS, 	"test subvolume deletion with user_subvol_rm_allowed mount option",				},
@@ -14398,21 +14394,21 @@ struct t_idmapped_mounts t_btrfs[] = {
 };
 
 /* Test for commit 968219708108 ("fs: handle circular mappings correctly"). */
-struct t_idmapped_mounts t_setattr_fix_968219708108[] = {
+struct test_struct t_setattr_fix_968219708108[] = {
 	{ setattr_fix_968219708108,					T_REQUIRE_IDMAPPED_MOUNTS,	"test that setattr works correctly",								},
 };
 
 /* Test for commit 705191b03d50 ("fs: fix acl translation"). */
-struct t_idmapped_mounts t_setxattr_fix_705191b03d50[] = {
+struct test_struct t_setxattr_fix_705191b03d50[] = {
 	{ setxattr_fix_705191b03d50,					T_REQUIRE_USERNS,		"test that setxattr works correctly for userns mountable filesystems",				},
 };
 
-static bool run_test(struct t_idmapped_mounts suite[], size_t suite_size)
+static bool run_test(struct test_struct suite[], size_t suite_size)
 {
 	int i;
 
 	for (i = 0; i < suite_size; i++) {
-		struct t_idmapped_mounts *t = &suite[i];
+		struct test_struct *t = &suite[i];
 		int ret;
 		pid_t pid;
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 05/13] utils: add missing global.h include
  2022-05-12 16:52 [PATCH v2 00/13] rename & split tests Christian Brauner
                   ` (3 preceding siblings ...)
  2022-05-12 16:52 ` [PATCH v2 04/13] vfstest: rename struct t_idmapped_mounts Christian Brauner
@ 2022-05-12 16:52 ` Christian Brauner
  2022-05-12 16:52 ` [PATCH v2 07/13] utils: move helpers into utils Christian Brauner
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Christian Brauner @ 2022-05-12 16:52 UTC (permalink / raw)
  To: Zorro Lang, fstests
  Cc: Christian Brauner, Dave Chinner, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong

Make sure that utils.{c,h} have access to the necessary defines created
during the configure stage when building xfstests.

Cc: Dave Chinner <david@fromorbit.com>
Cc: Amir Goldstein <amir73il@gmail.com>
Cc: Eryu Guan <guaneryu@gmail.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Zorro Lang <zlang@redhat.com>
Cc: "Darrick J. Wong" <djwong@kernel.org>
Cc: fstests <fstests@vger.kernel.org>
Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>
---
 src/vfs/utils.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/vfs/utils.h b/src/vfs/utils.h
index c1024646..e32865a6 100644
--- a/src/vfs/utils.h
+++ b/src/vfs/utils.h
@@ -3,6 +3,8 @@
 #ifndef __IDMAP_UTILS_H
 #define __IDMAP_UTILS_H
 
+#include "../global.h"
+
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE
 #endif
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 07/13] utils: move helpers into utils
  2022-05-12 16:52 [PATCH v2 00/13] rename & split tests Christian Brauner
                   ` (4 preceding siblings ...)
  2022-05-12 16:52 ` [PATCH v2 05/13] utils: add missing global.h include Christian Brauner
@ 2022-05-12 16:52 ` Christian Brauner
  2022-05-12 16:52 ` [PATCH v2 08/13] missing: move sys_execveat() to missing.h Christian Brauner
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Christian Brauner @ 2022-05-12 16:52 UTC (permalink / raw)
  To: Zorro Lang, fstests
  Cc: Christian Brauner, Dave Chinner, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong

In order to split the test suite into multiple source files we need to
be able to access generic helpers in all source files not just one. So
move all generic helpers into utils.{c,h}.

Cc: Dave Chinner <david@fromorbit.com>
Cc: Amir Goldstein <amir73il@gmail.com>
Cc: Eryu Guan <guaneryu@gmail.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Zorro Lang <zlang@redhat.com>
Cc: "Darrick J. Wong" <djwong@kernel.org>
Cc: fstests <fstests@vger.kernel.org>
Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>
---
 src/vfs/utils.c   | 495 ++++++++++++++++++++++++++++++++++
 src/vfs/utils.h   | 196 ++++++++++++++
 src/vfs/vfstest.c | 659 ----------------------------------------------
 3 files changed, 691 insertions(+), 659 deletions(-)

diff --git a/src/vfs/utils.c b/src/vfs/utils.c
index faf06fcd..1634e5c8 100644
--- a/src/vfs/utils.c
+++ b/src/vfs/utils.c
@@ -9,12 +9,14 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/eventfd.h>
+#include <sys/fsuid.h>
 #include <sys/mount.h>
 #include <sys/prctl.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <sys/xattr.h>
 
 #include "utils.h"
 
@@ -423,3 +425,496 @@ int add_map_entry(struct list *head,
 	list_add_tail(head, new_list);
 	return 0;
 }
+
+/* __expected_uid_gid - check whether file is owned by the provided uid and gid */
+bool __expected_uid_gid(int dfd, const char *path, int flags,
+			uid_t expected_uid, gid_t expected_gid, bool log)
+{
+	int ret;
+	struct stat st;
+
+	ret = fstatat(dfd, path, &st, flags);
+	if (ret < 0)
+		return log_errno(false, "failure: fstatat");
+
+	if (log && st.st_uid != expected_uid)
+		log_stderr("failure: uid(%d) != expected_uid(%d)", st.st_uid, expected_uid);
+
+	if (log && st.st_gid != expected_gid)
+		log_stderr("failure: gid(%d) != expected_gid(%d)", st.st_gid, expected_gid);
+
+	errno = 0; /* Don't report misleading errno. */
+	return st.st_uid == expected_uid && st.st_gid == expected_gid;
+}
+
+/* caps_down - lower all effective caps */
+int caps_down(void)
+{
+	bool fret = false;
+#ifdef HAVE_SYS_CAPABILITY_H
+	cap_t caps = NULL;
+	int ret = -1;
+
+	caps = cap_get_proc();
+	if (!caps)
+		goto out;
+
+	ret = cap_clear_flag(caps, CAP_EFFECTIVE);
+	if (ret)
+		goto out;
+
+	ret = cap_set_proc(caps);
+	if (ret)
+		goto out;
+
+	fret = true;
+
+out:
+	cap_free(caps);
+#endif
+	return fret;
+}
+
+/* caps_down_fsetid - lower CAP_FSETID effective cap */
+int caps_down_fsetid(void)
+{
+	bool fret = false;
+#ifdef HAVE_SYS_CAPABILITY_H
+	cap_t caps = NULL;
+	cap_value_t cap = CAP_FSETID;
+	int ret = -1;
+
+	caps = cap_get_proc();
+	if (!caps)
+		goto out;
+
+	ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, 0);
+	if (ret)
+		goto out;
+
+	ret = cap_set_proc(caps);
+	if (ret)
+		goto out;
+
+	fret = true;
+
+out:
+	cap_free(caps);
+#endif
+	return fret;
+}
+
+#ifdef HAVE_LIBURING_H
+int io_uring_openat_with_creds(struct io_uring *ring, int dfd, const char *path,
+			       int cred_id, bool with_link, int *ret_cqe)
+{
+	struct io_uring_cqe *cqe;
+	struct io_uring_sqe *sqe;
+	int ret, i, to_submit = 1;
+
+	if (with_link) {
+		sqe = io_uring_get_sqe(ring);
+		if (!sqe)
+			return log_error_errno(-EINVAL, EINVAL, "failure: io_uring_sqe");
+		io_uring_prep_nop(sqe);
+		sqe->flags |= IOSQE_IO_LINK;
+		sqe->user_data = 1;
+		to_submit++;
+	}
+
+	sqe = io_uring_get_sqe(ring);
+	if (!sqe)
+		return log_error_errno(-EINVAL, EINVAL, "failure: io_uring_sqe");
+	io_uring_prep_openat(sqe, dfd, path, O_RDONLY | O_CLOEXEC, 0);
+	sqe->user_data = 2;
+
+	if (cred_id != -1)
+		sqe->personality = cred_id;
+
+	ret = io_uring_submit(ring);
+	if (ret != to_submit) {
+		log_stderr("failure: io_uring_submit");
+		goto out;
+	}
+
+	for (i = 0; i < to_submit; i++) {
+		ret = io_uring_wait_cqe(ring, &cqe);
+		if (ret < 0) {
+			log_stderr("failure: io_uring_wait_cqe");
+			goto out;
+		}
+
+		ret = cqe->res;
+		/*
+		 * Make sure caller can identify that this is a proper io_uring
+		 * failure and not some earlier error.
+		 */
+		if (ret_cqe)
+			*ret_cqe = ret;
+		io_uring_cqe_seen(ring, cqe);
+	}
+	log_debug("Ran test");
+out:
+	return ret;
+}
+#endif /* HAVE_LIBURING_H */
+
+/* caps_up - raise all permitted caps */
+int caps_up(void)
+{
+	bool fret = false;
+#ifdef HAVE_SYS_CAPABILITY_H
+	cap_t caps = NULL;
+	cap_value_t cap;
+	int ret = -1;
+
+	caps = cap_get_proc();
+	if (!caps)
+		goto out;
+
+	for (cap = 0; cap <= CAP_LAST_CAP; cap++) {
+		cap_flag_value_t flag;
+
+		ret = cap_get_flag(caps, cap, CAP_PERMITTED, &flag);
+		if (ret) {
+			if (errno == EINVAL)
+				break;
+			else
+				goto out;
+		}
+
+		ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag);
+		if (ret)
+			goto out;
+	}
+
+	ret = cap_set_proc(caps);
+	if (ret)
+		goto out;
+
+	fret = true;
+out:
+	cap_free(caps);
+#endif
+	return fret;
+}
+
+/* chown_r - recursively change ownership of all files */
+int chown_r(int fd, const char *path, uid_t uid, gid_t gid)
+{
+	int dfd, ret;
+	DIR *dir;
+	struct dirent *direntp;
+
+	dfd = openat(fd, path, O_CLOEXEC | O_DIRECTORY);
+	if (dfd < 0)
+		return -1;
+
+	dir = fdopendir(dfd);
+	if (!dir) {
+		close(dfd);
+		return -1;
+	}
+
+	while ((direntp = readdir(dir))) {
+		struct stat st;
+
+		if (!strcmp(direntp->d_name, ".") ||
+		    !strcmp(direntp->d_name, ".."))
+			continue;
+
+		ret = fstatat(dfd, direntp->d_name, &st, AT_SYMLINK_NOFOLLOW);
+		if (ret < 0 && errno != ENOENT)
+			break;
+
+		if (S_ISDIR(st.st_mode))
+			ret = chown_r(dfd, direntp->d_name, uid, gid);
+		else
+			ret = fchownat(dfd, direntp->d_name, uid, gid, AT_SYMLINK_NOFOLLOW);
+		if (ret < 0 && errno != ENOENT)
+			break;
+	}
+
+	ret = fchownat(fd, path, uid, gid, AT_SYMLINK_NOFOLLOW);
+	closedir(dir);
+	return ret;
+}
+
+/* expected_dummy_vfs_caps_uid - check vfs caps are stored with the provided uid */
+bool expected_dummy_vfs_caps_uid(int fd, uid_t expected_uid)
+{
+#define __cap_raised_permitted(x, ns_cap_data)                                 \
+	((ns_cap_data.data[(x) >> 5].permitted) & (1 << ((x)&31)))
+	struct vfs_ns_cap_data ns_xattr = {};
+	ssize_t ret;
+
+	ret = fgetxattr(fd, "security.capability", &ns_xattr, sizeof(ns_xattr));
+	if (ret < 0 || ret == 0)
+		return false;
+
+	if (ns_xattr.magic_etc & VFS_CAP_REVISION_3) {
+
+		if (le32_to_cpu(ns_xattr.rootid) != expected_uid) {
+			errno = EINVAL;
+			log_stderr("failure: rootid(%d) != expected_rootid(%d)", le32_to_cpu(ns_xattr.rootid), expected_uid);
+		}
+
+		return (le32_to_cpu(ns_xattr.rootid) == expected_uid) &&
+		       (__cap_raised_permitted(CAP_NET_RAW, ns_xattr) > 0);
+	} else {
+		log_stderr("failure: fscaps version");
+	}
+
+	return false;
+}
+
+/* set_dummy_vfs_caps - set dummy vfs caps for the provided uid */
+int set_dummy_vfs_caps(int fd, int flags, int rootuid)
+{
+#define __raise_cap_permitted(x, ns_cap_data)                                  \
+	ns_cap_data.data[(x) >> 5].permitted |= (1 << ((x)&31))
+
+	struct vfs_ns_cap_data ns_xattr;
+
+	memset(&ns_xattr, 0, sizeof(ns_xattr));
+	__raise_cap_permitted(CAP_NET_RAW, ns_xattr);
+	ns_xattr.magic_etc |= VFS_CAP_REVISION_3 | VFS_CAP_FLAGS_EFFECTIVE;
+	ns_xattr.rootid = cpu_to_le32(rootuid);
+
+	return fsetxattr(fd, "security.capability",
+			 &ns_xattr, sizeof(ns_xattr), flags);
+}
+
+bool protected_symlinks_enabled(void)
+{
+	static int enabled = -1;
+
+	if (enabled == -1) {
+		int fd;
+		ssize_t ret;
+		char buf[256];
+
+		enabled = 0;
+
+		fd = open("/proc/sys/fs/protected_symlinks", O_RDONLY | O_CLOEXEC);
+		if (fd < 0)
+			return false;
+
+		ret = read(fd, buf, sizeof(buf));
+		close(fd);
+		if (ret < 0)
+			return false;
+
+		if (atoi(buf) >= 1)
+			enabled = 1;
+        }
+
+	return enabled == 1;
+}
+
+static bool is_xfs(const char *fstype)
+{
+	static int enabled = -1;
+
+	if (enabled == -1)
+		enabled = !strcmp(fstype, "xfs");
+
+	return enabled;
+}
+
+bool xfs_irix_sgid_inherit_enabled(const char *fstype)
+{
+	static int enabled = -1;
+
+	if (enabled == -1) {
+		int fd;
+		ssize_t ret;
+		char buf[256];
+
+		enabled = 0;
+
+		if (is_xfs(fstype)) {
+			fd = open("/proc/sys/fs/xfs/irix_sgid_inherit", O_RDONLY | O_CLOEXEC);
+			if (fd < 0)
+				return false;
+
+			ret = read(fd, buf, sizeof(buf));
+			close(fd);
+			if (ret < 0)
+				return false;
+
+			if (atoi(buf) >= 1)
+				enabled = 1;
+		}
+        }
+
+	return enabled == 1;
+}
+
+bool expected_file_size(int dfd, const char *path, int flags, off_t expected_size)
+{
+	int ret;
+	struct stat st;
+
+	ret = fstatat(dfd, path, &st, flags);
+	if (ret < 0)
+		return log_errno(false, "failure: fstatat");
+
+	if (st.st_size != expected_size)
+		return log_errno(false, "failure: st_size(%zu) != expected_size(%zu)",
+				 (size_t)st.st_size, (size_t)expected_size);
+
+	return true;
+}
+
+/* is_setid - check whether file is S_ISUID and S_ISGID */
+bool is_setid(int dfd, const char *path, int flags)
+{
+	int ret;
+	struct stat st;
+
+	ret = fstatat(dfd, path, &st, flags);
+	if (ret < 0)
+		return false;
+
+	errno = 0; /* Don't report misleading errno. */
+	return (st.st_mode & S_ISUID) || (st.st_mode & S_ISGID);
+}
+
+/* is_setgid - check whether file or directory is S_ISGID */
+bool is_setgid(int dfd, const char *path, int flags)
+{
+	int ret;
+	struct stat st;
+
+	ret = fstatat(dfd, path, &st, flags);
+	if (ret < 0)
+		return false;
+
+	errno = 0; /* Don't report misleading errno. */
+	return (st.st_mode & S_ISGID);
+}
+
+/* is_sticky - check whether file is S_ISVTX */
+bool is_sticky(int dfd, const char *path, int flags)
+{
+	int ret;
+	struct stat st;
+
+	ret = fstatat(dfd, path, &st, flags);
+	if (ret < 0)
+		return false;
+
+	errno = 0; /* Don't report misleading errno. */
+	return (st.st_mode & S_ISVTX) > 0;
+}
+
+bool switch_resids(uid_t uid, gid_t gid)
+{
+	if (setresgid(gid, gid, gid))
+		return log_errno(false, "failure: setregid");
+
+	if (setresuid(uid, uid, uid))
+		return log_errno(false, "failure: setresuid");
+
+	if (setfsgid(-1) != gid)
+		return log_errno(false, "failure: setfsgid(-1)");
+
+	if (setfsuid(-1) != uid)
+		return log_errno(false, "failure: setfsuid(-1)");
+
+	return true;
+}
+
+/* rm_r - recursively remove all files */
+int rm_r(int fd, const char *path)
+{
+	int dfd, ret;
+	DIR *dir;
+	struct dirent *direntp;
+
+	if (!path || strcmp(path, "") == 0)
+		return -1;
+
+	dfd = openat(fd, path, O_CLOEXEC | O_DIRECTORY);
+	if (dfd < 0)
+		return -1;
+
+	dir = fdopendir(dfd);
+	if (!dir) {
+		close(dfd);
+		return -1;
+	}
+
+	while ((direntp = readdir(dir))) {
+		struct stat st;
+
+		if (!strcmp(direntp->d_name, ".") ||
+		    !strcmp(direntp->d_name, ".."))
+			continue;
+
+		ret = fstatat(dfd, direntp->d_name, &st, AT_SYMLINK_NOFOLLOW);
+		if (ret < 0 && errno != ENOENT)
+			break;
+
+		if (S_ISDIR(st.st_mode))
+			ret = rm_r(dfd, direntp->d_name);
+		else
+			ret = unlinkat(dfd, direntp->d_name, 0);
+		if (ret < 0 && errno != ENOENT)
+			break;
+	}
+
+	ret = unlinkat(fd, path, AT_REMOVEDIR);
+	closedir(dir);
+	return ret;
+}
+
+/* fd_to_fd - transfer data from one fd to another */
+int fd_to_fd(int from, int to)
+{
+	for (;;) {
+		uint8_t buf[PATH_MAX];
+		uint8_t *p = buf;
+		ssize_t bytes_to_write;
+		ssize_t bytes_read;
+
+		bytes_read = read_nointr(from, buf, sizeof buf);
+		if (bytes_read < 0)
+			return -1;
+		if (bytes_read == 0)
+			break;
+
+		bytes_to_write = (size_t)bytes_read;
+		do {
+			ssize_t bytes_written;
+
+			bytes_written = write_nointr(to, p, bytes_to_write);
+			if (bytes_written < 0)
+				return -1;
+
+			bytes_to_write -= bytes_written;
+			p += bytes_written;
+		} while (bytes_to_write > 0);
+	}
+
+	return 0;
+}
+
+bool openat_tmpfile_supported(int dirfd)
+{
+	int fd = -1;
+
+	fd = openat(dirfd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
+	if (fd == -1) {
+		if (errno == ENOTSUP)
+			return false;
+		else
+			return log_errno(false, "failure: create");
+	}
+
+	if (close(fd))
+		log_stderr("failure: close");
+
+	return true;
+}
diff --git a/src/vfs/utils.h b/src/vfs/utils.h
index 54947ec7..d8ec3c5e 100644
--- a/src/vfs/utils.h
+++ b/src/vfs/utils.h
@@ -17,15 +17,41 @@
 #include <stdlib.h>
 #include <string.h>
 #include <syscall.h>
+#include <sys/fsuid.h>
 #include <sys/types.h>
 #include <unistd.h>
 
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#endif
+
+#ifdef HAVE_LIBURING_H
+#include <liburing.h>
+#endif
+
 #include "missing.h"
 
 /* Flags for which functionality is required by the test */
 #define T_REQUIRE_IDMAPPED_MOUNTS (1U << 0)
 #define T_REQUIRE_USERNS (1U << 1)
 
+#define T_DIR1 "idmapped_mounts_1"
+#define FILE1 "file1"
+#define FILE1_RENAME "file1_rename"
+#define FILE2 "file2"
+#define FILE3 "file3"
+#define FILE2_RENAME "file2_rename"
+#define DIR1 "dir1"
+#define DIR2 "dir2"
+#define DIR3 "dir3"
+#define DIR1_RENAME "dir1_rename"
+#define HARDLINK1 "hardlink1"
+#define SYMLINK1 "symlink1"
+#define SYMLINK_USER1 "symlink_user1"
+#define SYMLINK_USER2 "symlink_user2"
+#define SYMLINK_USER3 "symlink_user3"
+#define CHRDEV1 "chrdev1"
+
 /* Maximum number of nested user namespaces in the kernel. */
 #define MAX_USERNS_LEVEL 32
 
@@ -41,6 +67,37 @@
 			      ? 10          \
 			      : sizeof(type) <= 8 ? 20 : sizeof(int[-2 * (sizeof(type) > 8)])))
 
+#define log_stderr(format, ...)                                                         \
+	fprintf(stderr, "%s: %d: %s - %m - " format "\n", __FILE__, __LINE__, __func__, \
+		##__VA_ARGS__)
+
+#ifdef DEBUG_TRACE
+#define log_debug(format, ...)                                           \
+	fprintf(stderr, "%s: %d: %s - " format "\n", __FILE__, __LINE__, \
+		__func__, ##__VA_ARGS__)
+#else
+#define log_debug(format, ...)
+#endif
+
+#define log_error_errno(__ret__, __errno__, format, ...)      \
+	({                                                    \
+		typeof(__ret__) __internal_ret__ = (__ret__); \
+		errno = (__errno__);                          \
+		log_stderr(format, ##__VA_ARGS__);            \
+		__internal_ret__;                             \
+	})
+
+#define log_errno(__ret__, format, ...) log_error_errno(__ret__, errno, format, ##__VA_ARGS__)
+
+#define die_errno(__errno__, format, ...)          \
+	({                                         \
+		errno = (__errno__);               \
+		log_stderr(format, ##__VA_ARGS__); \
+		exit(EXIT_FAILURE);                \
+	})
+
+#define die(format, ...) die_errno(errno, format, ##__VA_ARGS__)
+
 #define syserror(format, ...)                           \
 	({                                              \
 		fprintf(stderr, "%m - " format "\n", ##__VA_ARGS__); \
@@ -55,6 +112,68 @@
 		__internal_ret__;                             \
 	})
 
+#define safe_close(fd)      \
+	if (fd >= 0) {           \
+		int _e_ = errno; \
+		close(fd);       \
+		errno = _e_;     \
+		fd = -EBADF;     \
+	}
+
+#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
+
+#ifndef CAP_NET_RAW
+#define CAP_NET_RAW 13
+#endif
+
+#ifndef VFS_CAP_FLAGS_EFFECTIVE
+#define VFS_CAP_FLAGS_EFFECTIVE 0x000001
+#endif
+
+#ifndef VFS_CAP_U32_3
+#define VFS_CAP_U32_3 2
+#endif
+
+#ifndef VFS_CAP_U32
+#define VFS_CAP_U32 VFS_CAP_U32_3
+#endif
+
+#ifndef VFS_CAP_REVISION_1
+#define VFS_CAP_REVISION_1 0x01000000
+#endif
+
+#ifndef VFS_CAP_REVISION_2
+#define VFS_CAP_REVISION_2 0x02000000
+#endif
+
+#ifndef VFS_CAP_REVISION_3
+#define VFS_CAP_REVISION_3 0x03000000
+struct vfs_ns_cap_data {
+	__le32 magic_etc;
+	struct {
+		__le32 permitted;
+		__le32 inheritable;
+	} data[VFS_CAP_U32];
+	__le32 rootid;
+};
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define cpu_to_le16(w16) le16_to_cpu(w16)
+#define le16_to_cpu(w16) ((u_int16_t)((u_int16_t)(w16) >> 8) | (u_int16_t)((u_int16_t)(w16) << 8))
+#define cpu_to_le32(w32) le32_to_cpu(w32)
+#define le32_to_cpu(w32)                                                                       \
+	((u_int32_t)((u_int32_t)(w32) >> 24) | (u_int32_t)(((u_int32_t)(w32) >> 8) & 0xFF00) | \
+	 (u_int32_t)(((u_int32_t)(w32) << 8) & 0xFF0000) | (u_int32_t)((u_int32_t)(w32) << 24))
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+#define cpu_to_le16(w16) ((u_int16_t)(w16))
+#define le16_to_cpu(w16) ((u_int16_t)(w16))
+#define cpu_to_le32(w32) ((u_int32_t)(w32))
+#define le32_to_cpu(w32) ((u_int32_t)(w32))
+#else
+#error Expected endianess macro to be set
+#endif
+
 struct vfstest_info {
 	uid_t t_overflowuid;
 	gid_t t_overflowgid;
@@ -159,9 +278,86 @@ extern int get_userns_fd_from_idmap(struct list *idmap);
 extern ssize_t read_nointr(int fd, void *buf, size_t count);
 extern int wait_for_pid(pid_t pid);
 extern ssize_t write_nointr(int fd, const void *buf, size_t count);
+
+extern int caps_down(void);
+extern int caps_down_fsetid(void);
+extern int caps_up(void);
+static inline bool caps_supported(void)
+{
+	bool ret = false;
+
+#ifdef HAVE_SYS_CAPABILITY_H
+	ret = true;
+#endif
+
+	return ret;
+}
+extern bool expected_dummy_vfs_caps_uid(int fd, uid_t expected_uid);
+extern int set_dummy_vfs_caps(int fd, int flags, int rootuid);
+
 extern bool switch_ids(uid_t uid, gid_t gid);
+
 extern int create_userns_hierarchy(struct userns_hierarchy *h);
 extern int add_map_entry(struct list *head, __u32 id_host, __u32 id_ns,
 			 __u32 range, idmap_type_t map_type);
 
+extern bool __expected_uid_gid(int dfd, const char *path, int flags,
+			       uid_t expected_uid, gid_t expected_gid, bool log);
+static inline bool expected_uid_gid(int dfd, const char *path, int flags,
+				    uid_t expected_uid, gid_t expected_gid)
+{
+	return __expected_uid_gid(dfd, path, flags, expected_uid, expected_gid, true);
+}
+
+static inline bool switch_userns(int fd, uid_t uid, gid_t gid, bool drop_caps)
+{
+	if (setns(fd, CLONE_NEWUSER))
+		return log_errno(false, "failure: setns");
+
+	if (!switch_ids(uid, gid))
+		return log_errno(false, "failure: switch_ids");
+
+	if (drop_caps && !caps_down())
+		return log_errno(false, "failure: caps_down");
+
+	return true;
+}
+
+extern bool switch_resids(uid_t uid, gid_t gid);
+
+static inline bool switch_fsids(uid_t fsuid, gid_t fsgid)
+{
+	if (setfsgid(fsgid))
+		return log_errno(false, "failure: setfsgid");
+
+	if (setfsgid(-1) != fsgid)
+		return log_errno(false, "failure: setfsgid(-1)");
+
+	if (setfsuid(fsuid))
+		return log_errno(false, "failure: setfsuid");
+
+	if (setfsuid(-1) != fsuid)
+		return log_errno(false, "failure: setfsuid(-1)");
+
+	return true;
+}
+
+#ifdef HAVE_LIBURING_H
+extern int io_uring_openat_with_creds(struct io_uring *ring, int dfd,
+				      const char *path, int cred_id,
+				      bool with_link, int *ret_cqe);
+#endif /* HAVE_LIBURING_H */
+
+extern int chown_r(int fd, const char *path, uid_t uid, gid_t gid);
+extern int rm_r(int fd, const char *path);
+extern int fd_to_fd(int from, int to);
+extern bool protected_symlinks_enabled(void);
+extern bool xfs_irix_sgid_inherit_enabled(const char *fstype);
+extern bool expected_file_size(int dfd, const char *path, int flags,
+			       off_t expected_size);
+extern bool is_setid(int dfd, const char *path, int flags);
+extern bool is_setgid(int dfd, const char *path, int flags);
+extern bool is_sticky(int dfd, const char *path, int flags);
+extern bool openat_tmpfile_supported(int dirfd);
+
 #endif /* __IDMAP_UTILS_H */
diff --git a/src/vfs/vfstest.c b/src/vfs/vfstest.c
index 86b79499..8a68565c 100644
--- a/src/vfs/vfstest.c
+++ b/src/vfs/vfstest.c
@@ -35,67 +35,9 @@
 #include <linux/btrfs_tree.h>
 #endif
 
-#ifdef HAVE_SYS_CAPABILITY_H
-#include <sys/capability.h>
-#endif
-
-#ifdef HAVE_LIBURING_H
-#include <liburing.h>
-#endif
-
 #include "missing.h"
 #include "utils.h"
 
-#define T_DIR1 "idmapped_mounts_1"
-#define FILE1 "file1"
-#define FILE1_RENAME "file1_rename"
-#define FILE2 "file2"
-#define FILE2_RENAME "file2_rename"
-#define FILE3 "file3"
-#define DIR1 "dir1"
-#define DIR2 "dir2"
-#define DIR3 "dir3"
-#define DIR1_RENAME "dir1_rename"
-#define HARDLINK1 "hardlink1"
-#define SYMLINK1 "symlink1"
-#define SYMLINK_USER1 "symlink_user1"
-#define SYMLINK_USER2 "symlink_user2"
-#define SYMLINK_USER3 "symlink_user3"
-#define CHRDEV1 "chrdev1"
-
-#define log_stderr(format, ...)                                                         \
-	fprintf(stderr, "%s: %d: %s - %m - " format "\n", __FILE__, __LINE__, __func__, \
-		##__VA_ARGS__)
-
-#ifdef DEBUG_TRACE
-#define log_debug(format, ...)                                           \
-	fprintf(stderr, "%s: %d: %s - " format "\n", __FILE__, __LINE__, \
-		__func__, ##__VA_ARGS__)
-#else
-#define log_debug(format, ...)
-#endif
-
-#define log_error_errno(__ret__, __errno__, format, ...)      \
-	({                                                    \
-		typeof(__ret__) __internal_ret__ = (__ret__); \
-		errno = (__errno__);                          \
-		log_stderr(format, ##__VA_ARGS__);            \
-		__internal_ret__;                             \
-	})
-
-#define log_errno(__ret__, format, ...) log_error_errno(__ret__, errno, format, ##__VA_ARGS__)
-
-#define die_errno(__errno__, format, ...)          \
-	({                                         \
-		errno = (__errno__);               \
-		log_stderr(format, ##__VA_ARGS__); \
-		exit(EXIT_FAILURE);                \
-	})
-
-#define die(format, ...) die_errno(errno, format, ##__VA_ARGS__)
-
-#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))
-
 static char t_buf[PATH_MAX];
 
 static void init_vfstest_info(struct vfstest_info *info)
@@ -149,418 +91,6 @@ static void stash_overflowgid(struct vfstest_info *info)
 	info->t_overflowgid = atoi(buf);
 }
 
-static bool is_xfs(const char *fstype)
-{
-	static int enabled = -1;
-
-	if (enabled == -1)
-		enabled = !strcmp(fstype, "xfs");
-
-	return enabled;
-}
-
-static bool protected_symlinks_enabled(void)
-{
-	static int enabled = -1;
-
-	if (enabled == -1) {
-		int fd;
-		ssize_t ret;
-		char buf[256];
-
-		enabled = 0;
-
-		fd = open("/proc/sys/fs/protected_symlinks", O_RDONLY | O_CLOEXEC);
-		if (fd < 0)
-			return false;
-
-		ret = read(fd, buf, sizeof(buf));
-		close(fd);
-		if (ret < 0)
-			return false;
-
-		if (atoi(buf) >= 1)
-			enabled = 1;
-        }
-
-	return enabled == 1;
-}
-
-static bool xfs_irix_sgid_inherit_enabled(const char *fstype)
-{
-	static int enabled = -1;
-
-	if (enabled == -1) {
-		int fd;
-		ssize_t ret;
-		char buf[256];
-
-		enabled = 0;
-
-		if (is_xfs(fstype)) {
-			fd = open("/proc/sys/fs/xfs/irix_sgid_inherit", O_RDONLY | O_CLOEXEC);
-			if (fd < 0)
-				return false;
-
-			ret = read(fd, buf, sizeof(buf));
-			close(fd);
-			if (ret < 0)
-				return false;
-
-			if (atoi(buf) >= 1)
-				enabled = 1;
-		}
-        }
-
-	return enabled == 1;
-}
-
-static inline bool caps_supported(void)
-{
-	bool ret = false;
-
-#ifdef HAVE_SYS_CAPABILITY_H
-	ret = true;
-#endif
-
-	return ret;
-}
-
-/* caps_down_fsetid - lower CAP_FSETID effective cap */
-static int caps_down_fsetid(void)
-{
-	bool fret = false;
-#ifdef HAVE_SYS_CAPABILITY_H
-	cap_t caps = NULL;
-	cap_value_t cap = CAP_FSETID;
-	int ret = -1;
-
-	caps = cap_get_proc();
-	if (!caps)
-		goto out;
-
-	ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, 0);
-	if (ret)
-		goto out;
-
-	ret = cap_set_proc(caps);
-	if (ret)
-		goto out;
-
-	fret = true;
-
-out:
-	cap_free(caps);
-#endif
-	return fret;
-}
-
-/* caps_down - lower all effective caps */
-static int caps_down(void)
-{
-	bool fret = false;
-#ifdef HAVE_SYS_CAPABILITY_H
-	cap_t caps = NULL;
-	int ret = -1;
-
-	caps = cap_get_proc();
-	if (!caps)
-		goto out;
-
-	ret = cap_clear_flag(caps, CAP_EFFECTIVE);
-	if (ret)
-		goto out;
-
-	ret = cap_set_proc(caps);
-	if (ret)
-		goto out;
-
-	fret = true;
-
-out:
-	cap_free(caps);
-#endif
-	return fret;
-}
-
-/* caps_up - raise all permitted caps */
-static int caps_up(void)
-{
-	bool fret = false;
-#ifdef HAVE_SYS_CAPABILITY_H
-	cap_t caps = NULL;
-	cap_value_t cap;
-	int ret = -1;
-
-	caps = cap_get_proc();
-	if (!caps)
-		goto out;
-
-	for (cap = 0; cap <= CAP_LAST_CAP; cap++) {
-		cap_flag_value_t flag;
-
-		ret = cap_get_flag(caps, cap, CAP_PERMITTED, &flag);
-		if (ret) {
-			if (errno == EINVAL)
-				break;
-			else
-				goto out;
-		}
-
-		ret = cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, flag);
-		if (ret)
-			goto out;
-	}
-
-	ret = cap_set_proc(caps);
-	if (ret)
-		goto out;
-
-	fret = true;
-out:
-	cap_free(caps);
-#endif
-	return fret;
-}
-
-static bool openat_tmpfile_supported(int dirfd)
-{
-	int fd = -1;
-
-	fd = openat(dirfd, ".", O_TMPFILE | O_RDWR, S_IXGRP | S_ISGID);
-	if (fd == -1) {
-		if (errno == ENOTSUP)
-			return false;
-		else
-			return log_errno(false, "failure: create");
-	}
-
-	if (close(fd))
-		log_stderr("failure: close");
-
-	return true;
-}
-
-/* __expected_uid_gid - check whether file is owned by the provided uid and gid */
-static bool __expected_uid_gid(int dfd, const char *path, int flags,
-			       uid_t expected_uid, gid_t expected_gid, bool log)
-{
-	int ret;
-	struct stat st;
-
-	ret = fstatat(dfd, path, &st, flags);
-	if (ret < 0)
-		return log_errno(false, "failure: fstatat");
-
-	if (log && st.st_uid != expected_uid)
-		log_stderr("failure: uid(%d) != expected_uid(%d)", st.st_uid, expected_uid);
-
-	if (log && st.st_gid != expected_gid)
-		log_stderr("failure: gid(%d) != expected_gid(%d)", st.st_gid, expected_gid);
-
-	errno = 0; /* Don't report misleading errno. */
-	return st.st_uid == expected_uid && st.st_gid == expected_gid;
-}
-
-static bool expected_uid_gid(int dfd, const char *path, int flags,
-			     uid_t expected_uid, gid_t expected_gid)
-{
-	return __expected_uid_gid(dfd, path, flags,
-				  expected_uid, expected_gid, true);
-}
-
-static bool expected_file_size(int dfd, const char *path,
-			       int flags, off_t expected_size)
-{
-	int ret;
-	struct stat st;
-
-	ret = fstatat(dfd, path, &st, flags);
-	if (ret < 0)
-		return log_errno(false, "failure: fstatat");
-
-	if (st.st_size != expected_size)
-		return log_errno(false, "failure: st_size(%zu) != expected_size(%zu)",
-				 (size_t)st.st_size, (size_t)expected_size);
-
-	return true;
-}
-
-/* is_setid - check whether file is S_ISUID and S_ISGID */
-static bool is_setid(int dfd, const char *path, int flags)
-{
-	int ret;
-	struct stat st;
-
-	ret = fstatat(dfd, path, &st, flags);
-	if (ret < 0)
-		return false;
-
-	errno = 0; /* Don't report misleading errno. */
-	return (st.st_mode & S_ISUID) || (st.st_mode & S_ISGID);
-}
-
-/* is_setgid - check whether file or directory is S_ISGID */
-static bool is_setgid(int dfd, const char *path, int flags)
-{
-	int ret;
-	struct stat st;
-
-	ret = fstatat(dfd, path, &st, flags);
-	if (ret < 0)
-		return false;
-
-	errno = 0; /* Don't report misleading errno. */
-	return (st.st_mode & S_ISGID);
-}
-
-/* is_sticky - check whether file is S_ISVTX */
-static bool is_sticky(int dfd, const char *path, int flags)
-{
-	int ret;
-	struct stat st;
-
-	ret = fstatat(dfd, path, &st, flags);
-	if (ret < 0)
-		return false;
-
-	errno = 0; /* Don't report misleading errno. */
-	return (st.st_mode & S_ISVTX) > 0;
-}
-
-static inline bool switch_fsids(uid_t fsuid, gid_t fsgid)
-{
-	if (setfsgid(fsgid))
-		return log_errno(false, "failure: setfsgid");
-
-	if (setfsgid(-1) != fsgid)
-		return log_errno(false, "failure: setfsgid(-1)");
-
-	if (setfsuid(fsuid))
-		return log_errno(false, "failure: setfsuid");
-
-	if (setfsuid(-1) != fsuid)
-		return log_errno(false, "failure: setfsuid(-1)");
-
-	return true;
-}
-
-static inline bool switch_resids(uid_t uid, gid_t gid)
-{
-	if (setresgid(gid, gid, gid))
-		return log_errno(false, "failure: setregid");
-
-	if (setresuid(uid, uid, uid))
-		return log_errno(false, "failure: setresuid");
-
-	if (setfsgid(-1) != gid)
-		return log_errno(false, "failure: setfsgid(-1)");
-
-	if (setfsuid(-1) != uid)
-		return log_errno(false, "failure: setfsuid(-1)");
-
-	return true;
-}
-
-static inline bool switch_userns(int fd, uid_t uid, gid_t gid, bool drop_caps)
-{
-	if (setns(fd, CLONE_NEWUSER))
-		return log_errno(false, "failure: setns");
-
-	if (!switch_ids(uid, gid))
-		return log_errno(false, "failure: switch_ids");
-
-	if (drop_caps && !caps_down())
-		return log_errno(false, "failure: caps_down");
-
-	return true;
-}
-
-/* rm_r - recursively remove all files */
-static int rm_r(int fd, const char *path)
-{
-	int dfd, ret;
-	DIR *dir;
-	struct dirent *direntp;
-
-	if (!path || strcmp(path, "") == 0)
-		return -1;
-
-	dfd = openat(fd, path, O_CLOEXEC | O_DIRECTORY);
-	if (dfd < 0)
-		return -1;
-
-	dir = fdopendir(dfd);
-	if (!dir) {
-		close(dfd);
-		return -1;
-	}
-
-	while ((direntp = readdir(dir))) {
-		struct stat st;
-
-		if (!strcmp(direntp->d_name, ".") ||
-		    !strcmp(direntp->d_name, ".."))
-			continue;
-
-		ret = fstatat(dfd, direntp->d_name, &st, AT_SYMLINK_NOFOLLOW);
-		if (ret < 0 && errno != ENOENT)
-			break;
-
-		if (S_ISDIR(st.st_mode))
-			ret = rm_r(dfd, direntp->d_name);
-		else
-			ret = unlinkat(dfd, direntp->d_name, 0);
-		if (ret < 0 && errno != ENOENT)
-			break;
-	}
-
-	ret = unlinkat(fd, path, AT_REMOVEDIR);
-	closedir(dir);
-	return ret;
-}
-
-/* chown_r - recursively change ownership of all files */
-static int chown_r(int fd, const char *path, uid_t uid, gid_t gid)
-{
-	int dfd, ret;
-	DIR *dir;
-	struct dirent *direntp;
-
-	dfd = openat(fd, path, O_CLOEXEC | O_DIRECTORY);
-	if (dfd < 0)
-		return -1;
-
-	dir = fdopendir(dfd);
-	if (!dir) {
-		close(dfd);
-		return -1;
-	}
-
-	while ((direntp = readdir(dir))) {
-		struct stat st;
-
-		if (!strcmp(direntp->d_name, ".") ||
-		    !strcmp(direntp->d_name, ".."))
-			continue;
-
-		ret = fstatat(dfd, direntp->d_name, &st, AT_SYMLINK_NOFOLLOW);
-		if (ret < 0 && errno != ENOENT)
-			break;
-
-		if (S_ISDIR(st.st_mode))
-			ret = chown_r(dfd, direntp->d_name, uid, gid);
-		else
-			ret = fchownat(dfd, direntp->d_name, uid, gid, AT_SYMLINK_NOFOLLOW);
-		if (ret < 0 && errno != ENOENT)
-			break;
-	}
-
-	ret = fchownat(fd, path, uid, gid, AT_SYMLINK_NOFOLLOW);
-	closedir(dir);
-	return ret;
-}
-
 /*
  * There'll be scenarios where you'll want to see the attributes associated with
  * a directory tree during debugging or just to make sure things look correct.
@@ -696,37 +226,6 @@ __attribute__((unused)) static int print_r(int fd, const char *path)
 }
 #endif
 
-/* fd_to_fd - transfer data from one fd to another */
-static int fd_to_fd(int from, int to)
-{
-	for (;;) {
-		uint8_t buf[PATH_MAX];
-		uint8_t *p = buf;
-		ssize_t bytes_to_write;
-		ssize_t bytes_read;
-
-		bytes_read = read_nointr(from, buf, sizeof buf);
-		if (bytes_read < 0)
-			return -1;
-		if (bytes_read == 0)
-			break;
-
-		bytes_to_write = (size_t)bytes_read;
-		do {
-			ssize_t bytes_written;
-
-			bytes_written = write_nointr(to, p, bytes_to_write);
-			if (bytes_written < 0)
-				return -1;
-
-			bytes_to_write -= bytes_written;
-			p += bytes_written;
-		} while (bytes_to_write > 0);
-	}
-
-	return 0;
-}
-
 static int sys_execveat(int fd, const char *path, char **argv, char **envp,
 			int flags)
 {
@@ -738,111 +237,6 @@ static int sys_execveat(int fd, const char *path, char **argv, char **envp,
 #endif
 }
 
-#ifndef CAP_NET_RAW
-#define CAP_NET_RAW 13
-#endif
-
-#ifndef VFS_CAP_FLAGS_EFFECTIVE
-#define VFS_CAP_FLAGS_EFFECTIVE 0x000001
-#endif
-
-#ifndef VFS_CAP_U32_3
-#define VFS_CAP_U32_3 2
-#endif
-
-#ifndef VFS_CAP_U32
-#define VFS_CAP_U32 VFS_CAP_U32_3
-#endif
-
-#ifndef VFS_CAP_REVISION_1
-#define VFS_CAP_REVISION_1 0x01000000
-#endif
-
-#ifndef VFS_CAP_REVISION_2
-#define VFS_CAP_REVISION_2 0x02000000
-#endif
-
-#ifndef VFS_CAP_REVISION_3
-#define VFS_CAP_REVISION_3 0x03000000
-struct vfs_ns_cap_data {
-	__le32 magic_etc;
-	struct {
-		__le32 permitted;
-		__le32 inheritable;
-	} data[VFS_CAP_U32];
-	__le32 rootid;
-};
-#endif
-
-#if __BYTE_ORDER == __BIG_ENDIAN
-#define cpu_to_le16(w16) le16_to_cpu(w16)
-#define le16_to_cpu(w16) ((u_int16_t)((u_int16_t)(w16) >> 8) | (u_int16_t)((u_int16_t)(w16) << 8))
-#define cpu_to_le32(w32) le32_to_cpu(w32)
-#define le32_to_cpu(w32)                                                                       \
-	((u_int32_t)((u_int32_t)(w32) >> 24) | (u_int32_t)(((u_int32_t)(w32) >> 8) & 0xFF00) | \
-	 (u_int32_t)(((u_int32_t)(w32) << 8) & 0xFF0000) | (u_int32_t)((u_int32_t)(w32) << 24))
-#elif __BYTE_ORDER == __LITTLE_ENDIAN
-#define cpu_to_le16(w16) ((u_int16_t)(w16))
-#define le16_to_cpu(w16) ((u_int16_t)(w16))
-#define cpu_to_le32(w32) ((u_int32_t)(w32))
-#define le32_to_cpu(w32) ((u_int32_t)(w32))
-#else
-#error Expected endianess macro to be set
-#endif
-
-/* expected_dummy_vfs_caps_uid - check vfs caps are stored with the provided uid */
-static bool expected_dummy_vfs_caps_uid(int fd, uid_t expected_uid)
-{
-#define __cap_raised_permitted(x, ns_cap_data)                                 \
-	((ns_cap_data.data[(x) >> 5].permitted) & (1 << ((x)&31)))
-	struct vfs_ns_cap_data ns_xattr = {};
-	ssize_t ret;
-
-	ret = fgetxattr(fd, "security.capability", &ns_xattr, sizeof(ns_xattr));
-	if (ret < 0 || ret == 0)
-		return false;
-
-	if (ns_xattr.magic_etc & VFS_CAP_REVISION_3) {
-
-		if (le32_to_cpu(ns_xattr.rootid) != expected_uid) {
-			errno = EINVAL;
-			log_stderr("failure: rootid(%d) != expected_rootid(%d)", le32_to_cpu(ns_xattr.rootid), expected_uid);
-		}
-
-		return (le32_to_cpu(ns_xattr.rootid) == expected_uid) &&
-		       (__cap_raised_permitted(CAP_NET_RAW, ns_xattr) > 0);
-	} else {
-		log_stderr("failure: fscaps version");
-	}
-
-	return false;
-}
-
-/* set_dummy_vfs_caps - set dummy vfs caps for the provided uid */
-static int set_dummy_vfs_caps(int fd, int flags, int rootuid)
-{
-#define __raise_cap_permitted(x, ns_cap_data)                                  \
-	ns_cap_data.data[(x) >> 5].permitted |= (1 << ((x)&31))
-
-	struct vfs_ns_cap_data ns_xattr;
-
-	memset(&ns_xattr, 0, sizeof(ns_xattr));
-	__raise_cap_permitted(CAP_NET_RAW, ns_xattr);
-	ns_xattr.magic_etc |= VFS_CAP_REVISION_3 | VFS_CAP_FLAGS_EFFECTIVE;
-	ns_xattr.rootid = cpu_to_le32(rootuid);
-
-	return fsetxattr(fd, "security.capability",
-			 &ns_xattr, sizeof(ns_xattr), flags);
-}
-
-#define safe_close(fd)      \
-	if (fd >= 0) {           \
-		int _e_ = errno; \
-		close(fd);       \
-		errno = _e_;     \
-		fd = -EBADF;     \
-	}
-
 static void test_setup(struct vfstest_info *info)
 {
 	if (mkdirat(info->t_mnt_fd, T_DIR1, 0777))
@@ -6972,59 +6366,6 @@ out:
 }
 
 #ifdef HAVE_LIBURING_H
-static int io_uring_openat_with_creds(struct io_uring *ring, int dfd, const char *path, int cred_id,
-				      bool with_link, int *ret_cqe)
-{
-	struct io_uring_cqe *cqe;
-	struct io_uring_sqe *sqe;
-	int ret, i, to_submit = 1;
-
-	if (with_link) {
-		sqe = io_uring_get_sqe(ring);
-		if (!sqe)
-			return log_error_errno(-EINVAL, EINVAL, "failure: io_uring_sqe");
-		io_uring_prep_nop(sqe);
-		sqe->flags |= IOSQE_IO_LINK;
-		sqe->user_data = 1;
-		to_submit++;
-	}
-
-	sqe = io_uring_get_sqe(ring);
-	if (!sqe)
-		return log_error_errno(-EINVAL, EINVAL, "failure: io_uring_sqe");
-	io_uring_prep_openat(sqe, dfd, path, O_RDONLY | O_CLOEXEC, 0);
-	sqe->user_data = 2;
-
-	if (cred_id != -1)
-		sqe->personality = cred_id;
-
-	ret = io_uring_submit(ring);
-	if (ret != to_submit) {
-		log_stderr("failure: io_uring_submit");
-		goto out;
-	}
-
-	for (i = 0; i < to_submit; i++) {
-		ret = io_uring_wait_cqe(ring, &cqe);
-		if (ret < 0) {
-			log_stderr("failure: io_uring_wait_cqe");
-			goto out;
-		}
-
-		ret = cqe->res;
-		/*
-		 * Make sure caller can identify that this is a proper io_uring
-		 * failure and not some earlier error.
-		 */
-		if (ret_cqe)
-			*ret_cqe = ret;
-		io_uring_cqe_seen(ring, cqe);
-	}
-	log_debug("Ran test");
-out:
-	return ret;
-}
-
 static int io_uring(const struct vfstest_info *info)
 {
 	int fret = -1;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 08/13] missing: move sys_execveat() to missing.h
  2022-05-12 16:52 [PATCH v2 00/13] rename & split tests Christian Brauner
                   ` (5 preceding siblings ...)
  2022-05-12 16:52 ` [PATCH v2 07/13] utils: move helpers into utils Christian Brauner
@ 2022-05-12 16:52 ` Christian Brauner
  2022-05-12 16:52 ` [PATCH v2 09/13] utils: add struct test_suite Christian Brauner
                   ` (5 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Christian Brauner @ 2022-05-12 16:52 UTC (permalink / raw)
  To: Zorro Lang, fstests
  Cc: Christian Brauner, Dave Chinner, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong

The missing.h header provides syscalls potentially missing from the used
libc. Move the sys_execveat() definition into it. It doesn't belong into
vfstest.c.

Cc: Dave Chinner <david@fromorbit.com>
Cc: Amir Goldstein <amir73il@gmail.com>
Cc: Eryu Guan <guaneryu@gmail.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Zorro Lang <zlang@redhat.com>
Cc: "Darrick J. Wong" <djwong@kernel.org>
Cc: fstests <fstests@vger.kernel.org>
Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>
---
 src/vfs/missing.h | 11 +++++++++++
 src/vfs/vfstest.c | 11 -----------
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/src/vfs/missing.h b/src/vfs/missing.h
index c4f4cc32..059e742d 100644
--- a/src/vfs/missing.h
+++ b/src/vfs/missing.h
@@ -148,4 +148,15 @@ static inline int sys_umount2(const char *path, int flags)
 	return syscall(__NR_umount2, path, flags);
 }
 
+static inline int sys_execveat(int fd, const char *path, char **argv,
+			       char **envp, int flags)
+{
+#ifdef __NR_execveat
+	return syscall(__NR_execveat, fd, path, argv, envp, flags);
+#else
+	errno = ENOSYS;
+	return -1;
+#endif
+}
+
 #endif /* __IDMAP_MISSING_H */
diff --git a/src/vfs/vfstest.c b/src/vfs/vfstest.c
index 8a68565c..1d71b25b 100644
--- a/src/vfs/vfstest.c
+++ b/src/vfs/vfstest.c
@@ -226,17 +226,6 @@ __attribute__((unused)) static int print_r(int fd, const char *path)
 }
 #endif
 
-static int sys_execveat(int fd, const char *path, char **argv, char **envp,
-			int flags)
-{
-#ifdef __NR_execveat
-	return syscall(__NR_execveat, fd, path, argv, envp, flags);
-#else
-	errno = ENOSYS;
-	return -1;
-#endif
-}
-
 static void test_setup(struct vfstest_info *info)
 {
 	if (mkdirat(info->t_mnt_fd, T_DIR1, 0777))
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 09/13] utils: add struct test_suite
  2022-05-12 16:52 [PATCH v2 00/13] rename & split tests Christian Brauner
                   ` (6 preceding siblings ...)
  2022-05-12 16:52 ` [PATCH v2 08/13] missing: move sys_execveat() to missing.h Christian Brauner
@ 2022-05-12 16:52 ` Christian Brauner
  2022-05-12 16:52 ` [PATCH v2 12/13] vfstest: split out remaining idmapped mount tests Christian Brauner
                   ` (4 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Christian Brauner @ 2022-05-12 16:52 UTC (permalink / raw)
  To: Zorro Lang, fstests
  Cc: Christian Brauner, Dave Chinner, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong

Provide a convenient wrapper struct which provides the tests and the
number of tests. The struct can be kept local to each source file so we
are sure that the tests and number of tests is correct.

In vfstest.c we provide a run_suite() function which expects a struct
test_suite and runs the tests provided by that suite.

Cc: Dave Chinner <david@fromorbit.com>
Cc: Amir Goldstein <amir73il@gmail.com>
Cc: Eryu Guan <guaneryu@gmail.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Zorro Lang <zlang@redhat.com>
Cc: "Darrick J. Wong" <djwong@kernel.org>
Cc: fstests <fstests@vger.kernel.org>
Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>
---
 src/vfs/utils.h   |  5 ++++
 src/vfs/vfstest.c | 66 +++++++++++++++++++++++++++++++++++------------
 2 files changed, 54 insertions(+), 17 deletions(-)

diff --git a/src/vfs/utils.h b/src/vfs/utils.h
index d8ec3c5e..226daea7 100644
--- a/src/vfs/utils.h
+++ b/src/vfs/utils.h
@@ -205,6 +205,11 @@ struct test_struct {
 	const char *description;
 };
 
+struct test_suite {
+	size_t nr_tests;
+	const struct test_struct *tests;
+};
+
 typedef enum idmap_type_t {
 	ID_TYPE_UID,
 	ID_TYPE_GID
diff --git a/src/vfs/vfstest.c b/src/vfs/vfstest.c
index 1d71b25b..6e14cff9 100644
--- a/src/vfs/vfstest.c
+++ b/src/vfs/vfstest.c
@@ -13633,7 +13633,7 @@ static const struct option longopts[] = {
 	{NULL,					0,			0,	  0},
 };
 
-struct test_struct basic_suite[] = {
+static const struct test_struct t_basic[] = {
 	{ acls,								T_REQUIRE_IDMAPPED_MOUNTS,	"posix acls on regular mounts",									},
 	{ create_in_userns,						T_REQUIRE_IDMAPPED_MOUNTS,	"create operations in user namespace",								},
 	{ device_node_in_userns,					T_REQUIRE_IDMAPPED_MOUNTS,	"device node in user namespace",								},
@@ -13685,15 +13685,30 @@ struct test_struct basic_suite[] = {
 	{ threaded_idmapped_mount_interactions,				T_REQUIRE_IDMAPPED_MOUNTS,	"threaded operations on idmapped mounts",							},
 };
 
-struct test_struct fscaps_in_ancestor_userns[] = {
+static const struct test_suite s_basic = {
+	.tests = t_basic,
+	.nr_tests = ARRAY_SIZE(t_basic),
+};
+
+static const struct test_struct t_fscaps_in_ancestor_userns[] = {
 	{ fscaps_idmapped_mounts_in_userns_valid_in_ancestor_userns,	T_REQUIRE_IDMAPPED_MOUNTS,	"fscaps on idmapped mounts in user namespace writing fscap valid in ancestor userns",		},
 };
 
-struct test_struct t_nested_userns[] = {
+static const struct test_suite s_fscaps_in_ancestor_userns = {
+	.tests = t_fscaps_in_ancestor_userns,
+	.nr_tests = ARRAY_SIZE(t_fscaps_in_ancestor_userns),
+};
+
+static const struct test_struct t_nested_userns[] = {
 	{ nested_userns,						T_REQUIRE_IDMAPPED_MOUNTS,	"test that nested user namespaces behave correctly when attached to idmapped mounts",		},
 };
 
-struct test_struct t_btrfs[] = {
+static const struct test_suite s_nested_userns = {
+	.tests = t_nested_userns,
+	.nr_tests = ARRAY_SIZE(t_nested_userns),
+};
+
+static const struct test_struct t_btrfs[] = {
 	{ btrfs_subvolumes_fsids_mapped,				T_REQUIRE_IDMAPPED_MOUNTS,	"test subvolumes with mapped fsids",								},
 	{ btrfs_subvolumes_fsids_mapped_userns,				T_REQUIRE_IDMAPPED_MOUNTS, 	"test subvolumes with mapped fsids inside user namespace",					},
 	{ btrfs_subvolumes_fsids_mapped_user_subvol_rm_allowed,		T_REQUIRE_IDMAPPED_MOUNTS, 	"test subvolume deletion with user_subvol_rm_allowed mount option",				},
@@ -13718,14 +13733,29 @@ struct test_struct t_btrfs[] = {
 	{ btrfs_subvolume_lookup_user,					T_REQUIRE_IDMAPPED_MOUNTS, 	"test unprivileged subvolume lookup",								},
 };
 
+static const struct test_suite s_btrfs = {
+	.tests = t_btrfs,
+	.nr_tests = ARRAY_SIZE(t_btrfs),
+};
+
 /* Test for commit 968219708108 ("fs: handle circular mappings correctly"). */
-struct test_struct t_setattr_fix_968219708108[] = {
+static const struct test_struct t_setattr_fix_968219708108[] = {
 	{ setattr_fix_968219708108,					T_REQUIRE_IDMAPPED_MOUNTS,	"test that setattr works correctly",								},
 };
 
+static const struct test_suite s_setattr_fix_968219708108 = {
+	.tests = t_setattr_fix_968219708108,
+	.nr_tests = ARRAY_SIZE(t_setattr_fix_968219708108),
+};
+
 /* Test for commit 705191b03d50 ("fs: fix acl translation"). */
-struct test_struct t_setxattr_fix_705191b03d50[] = {
-	{ setxattr_fix_705191b03d50,					T_REQUIRE_USERNS,		"test that setxattr works correctly for userns mountable filesystems",				},
+static const struct test_struct t_setxattr_fix_705191b03d50[] = {
+	{ setxattr_fix_705191b03d50,					T_REQUIRE_USERNS,	"test that setxattr works correctly for userns mountable filesystems",				},
+};
+
+static const struct test_suite s_setxattr_fix_705191b03d50 = {
+	.tests = t_setxattr_fix_705191b03d50,
+	.nr_tests = ARRAY_SIZE(t_setxattr_fix_705191b03d50),
 };
 
 static bool run_test(struct vfstest_info *info, const struct test_struct suite[], size_t suite_size)
@@ -13772,6 +13802,12 @@ static bool run_test(struct vfstest_info *info, const struct test_struct suite[]
 	return true;
 }
 
+static inline bool run_suite(struct vfstest_info *info,
+			     const struct test_suite *suite)
+{
+	return run_test(info, suite->tests, suite->nr_tests);
+}
+
 static bool fs_allow_idmap(const struct vfstest_info *info)
 {
 	int ret;
@@ -13913,29 +13949,25 @@ int main(int argc, char *argv[])
 
 	fret = EXIT_FAILURE;
 
-	if (test_core && !run_test(&info, basic_suite, ARRAY_SIZE(basic_suite)))
+	if (test_core && !run_suite(&info, &s_basic))
 		goto out;
 
 	if (test_fscaps_regression &&
-	    !run_test(&info, fscaps_in_ancestor_userns,
-		      ARRAY_SIZE(fscaps_in_ancestor_userns)))
+	    !run_suite(&info, &s_fscaps_in_ancestor_userns))
 		goto out;
 
-	if (test_nested_userns &&
-	    !run_test(&info, t_nested_userns, ARRAY_SIZE(t_nested_userns)))
+	if (test_nested_userns && !run_suite(&info, &s_nested_userns))
 		goto out;
 
-	if (test_btrfs && !run_test(&info, t_btrfs, ARRAY_SIZE(t_btrfs)))
+	if (test_btrfs && !run_suite(&info, &s_btrfs))
 		goto out;
 
 	if (test_setattr_fix_968219708108 &&
-	    !run_test(&info, t_setattr_fix_968219708108,
-		      ARRAY_SIZE(t_setattr_fix_968219708108)))
+	    !run_suite(&info, &s_setattr_fix_968219708108))
 		goto out;
 
 	if (test_setxattr_fix_705191b03d50 &&
-	    !run_test(&info, t_setxattr_fix_705191b03d50,
-		      ARRAY_SIZE(t_setxattr_fix_705191b03d50)))
+	    !run_suite(&info, &s_setxattr_fix_705191b03d50))
 		goto out;
 
 	fret = EXIT_SUCCESS;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 12/13] vfstest: split out remaining idmapped mount tests
  2022-05-12 16:52 [PATCH v2 00/13] rename & split tests Christian Brauner
                   ` (7 preceding siblings ...)
  2022-05-12 16:52 ` [PATCH v2 09/13] utils: add struct test_suite Christian Brauner
@ 2022-05-12 16:52 ` Christian Brauner
  2022-05-12 16:52 ` [PATCH v2 13/13] vfs/idmapped-mounts: remove invalid test Christian Brauner
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 24+ messages in thread
From: Christian Brauner @ 2022-05-12 16:52 UTC (permalink / raw)
  To: Zorro Lang, fstests
  Cc: Christian Brauner, Dave Chinner, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong

Split out all the remaining idmapped mount tests into the idmapped
mounts source file.

Cc: Dave Chinner <david@fromorbit.com>
Cc: Amir Goldstein <amir73il@gmail.com>
Cc: Eryu Guan <guaneryu@gmail.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Zorro Lang <zlang@redhat.com>
Cc: "Darrick J. Wong" <djwong@kernel.org>
Cc: fstests <fstests@vger.kernel.org>
Acked-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>
---
 src/vfs/idmapped-mounts.c | 1255 +++++++++++++++++++++++++++++++++
 src/vfs/idmapped-mounts.h |    3 +
 src/vfs/utils.c           |  130 ++++
 src/vfs/utils.h           |    5 +
 src/vfs/vfstest.c         | 1394 +------------------------------------
 5 files changed, 1394 insertions(+), 1393 deletions(-)

diff --git a/src/vfs/idmapped-mounts.c b/src/vfs/idmapped-mounts.c
index e746ae89..d3763c2d 100644
--- a/src/vfs/idmapped-mounts.c
+++ b/src/vfs/idmapped-mounts.c
@@ -6678,6 +6678,1232 @@ out:
 	return fret;
 }
 
+static int nested_userns(const struct vfstest_info *info)
+{
+	int fret = -1;
+	int ret;
+	pid_t pid;
+	unsigned int id;
+	struct list *it, *next;
+	struct userns_hierarchy hierarchy[] = {
+		{ .level = 1, .fd_userns = -EBADF, },
+		{ .level = 2, .fd_userns = -EBADF, },
+		{ .level = 3, .fd_userns = -EBADF, },
+		{ .level = 4, .fd_userns = -EBADF, },
+		/* Dummy entry that marks the end. */
+		{ .level = MAX_USERNS_LEVEL, .fd_userns = -EBADF, },
+	};
+	struct mount_attr attr_level1 = {
+		.attr_set	= MOUNT_ATTR_IDMAP,
+		.userns_fd	= -EBADF,
+	};
+	struct mount_attr attr_level2 = {
+		.attr_set	= MOUNT_ATTR_IDMAP,
+		.userns_fd	= -EBADF,
+	};
+	struct mount_attr attr_level3 = {
+		.attr_set	= MOUNT_ATTR_IDMAP,
+		.userns_fd	= -EBADF,
+	};
+	struct mount_attr attr_level4 = {
+		.attr_set	= MOUNT_ATTR_IDMAP,
+		.userns_fd	= -EBADF,
+	};
+	int fd_dir1 = -EBADF,
+	    fd_open_tree_level1 = -EBADF,
+	    fd_open_tree_level2 = -EBADF,
+	    fd_open_tree_level3 = -EBADF,
+	    fd_open_tree_level4 = -EBADF;
+	const unsigned int id_file_range = 10000;
+
+	list_init(&hierarchy[0].id_map);
+	list_init(&hierarchy[1].id_map);
+	list_init(&hierarchy[2].id_map);
+	list_init(&hierarchy[3].id_map);
+
+	/*
+	 * Give a large map to the outermost user namespace so we can create
+	 * comfortable nested maps.
+	 */
+	ret = add_map_entry(&hierarchy[0].id_map, 1000000, 0, 1000000000, ID_TYPE_UID);
+	if (ret) {
+		log_stderr("failure: adding uidmap for userns at level 1");
+		goto out;
+	}
+
+	ret = add_map_entry(&hierarchy[0].id_map, 1000000, 0, 1000000000, ID_TYPE_GID);
+	if (ret) {
+		log_stderr("failure: adding gidmap for userns at level 1");
+		goto out;
+	}
+
+	/* This is uid:0->2000000:100000000 in init userns. */
+	ret = add_map_entry(&hierarchy[1].id_map, 1000000, 0, 100000000, ID_TYPE_UID);
+	if (ret) {
+		log_stderr("failure: adding uidmap for userns at level 2");
+		goto out;
+	}
+
+	/* This is gid:0->2000000:100000000 in init userns. */
+	ret = add_map_entry(&hierarchy[1].id_map, 1000000, 0, 100000000, ID_TYPE_GID);
+	if (ret) {
+		log_stderr("failure: adding gidmap for userns at level 2");
+		goto out;
+	}
+
+	/* This is uid:0->3000000:999 in init userns. */
+	ret = add_map_entry(&hierarchy[2].id_map, 1000000, 0, 999, ID_TYPE_UID);
+	if (ret) {
+		log_stderr("failure: adding uidmap for userns at level 3");
+		goto out;
+	}
+
+	/* This is gid:0->3000000:999 in the init userns. */
+	ret = add_map_entry(&hierarchy[2].id_map, 1000000, 0, 999, ID_TYPE_GID);
+	if (ret) {
+		log_stderr("failure: adding gidmap for userns at level 3");
+		goto out;
+	}
+
+	/* id 999 will remain unmapped. */
+
+	/* This is uid:1000->2001000:1 in init userns. */
+	ret = add_map_entry(&hierarchy[2].id_map, 1000, 1000, 1, ID_TYPE_UID);
+	if (ret) {
+		log_stderr("failure: adding uidmap for userns at level 3");
+		goto out;
+	}
+
+	/* This is gid:1000->2001000:1 in init userns. */
+	ret = add_map_entry(&hierarchy[2].id_map, 1000, 1000, 1, ID_TYPE_GID);
+	if (ret) {
+		log_stderr("failure: adding gidmap for userns at level 3");
+		goto out;
+	}
+
+	/* This is uid:1001->3001001:10000 in init userns. */
+	ret = add_map_entry(&hierarchy[2].id_map, 1001001, 1001, 10000000, ID_TYPE_UID);
+	if (ret) {
+		log_stderr("failure: adding uidmap for userns at level 3");
+		goto out;
+	}
+
+	/* This is gid:1001->3001001:10000 in init userns. */
+	ret = add_map_entry(&hierarchy[2].id_map, 1001001, 1001, 10000000, ID_TYPE_GID);
+	if (ret) {
+		log_stderr("failure: adding gidmap for userns at level 3");
+		goto out;
+	}
+
+	/* Don't write a mapping in the 4th userns. */
+	list_empty(&hierarchy[4].id_map);
+
+	/* Create the actual userns hierarchy. */
+	ret = create_userns_hierarchy(hierarchy);
+	if (ret) {
+		log_stderr("failure: create userns hierarchy");
+		goto out;
+	}
+
+	attr_level1.userns_fd = hierarchy[0].fd_userns;
+	attr_level2.userns_fd = hierarchy[1].fd_userns;
+	attr_level3.userns_fd = hierarchy[2].fd_userns;
+	attr_level4.userns_fd = hierarchy[3].fd_userns;
+
+	/*
+	 * Create one directory where we create files for each uid/gid within
+	 * the first userns.
+	 */
+	if (mkdirat(info->t_dir1_fd, DIR1, 0777)) {
+		log_stderr("failure: mkdirat");
+		goto out;
+	}
+
+	fd_dir1 = openat(info->t_dir1_fd, DIR1, O_DIRECTORY | O_CLOEXEC);
+	if (fd_dir1 < 0) {
+		log_stderr("failure: openat");
+		goto out;
+	}
+
+	for (id = 0; id <= id_file_range; id++) {
+		char file[256];
+
+		snprintf(file, sizeof(file), DIR1 "/" FILE1 "_%u", id);
+
+		if (mknodat(info->t_dir1_fd, file, S_IFREG | 0644, 0)) {
+			log_stderr("failure: create %s", file);
+			goto out;
+		}
+
+		if (fchownat(info->t_dir1_fd, file, id, id, AT_SYMLINK_NOFOLLOW)) {
+			log_stderr("failure: fchownat %s", file);
+			goto out;
+		}
+
+		if (!expected_uid_gid(info->t_dir1_fd, file, 0, id, id)) {
+			log_stderr("failure: check ownership %s", file);
+			goto out;
+		}
+	}
+
+	/* Create detached mounts for all the user namespaces. */
+	fd_open_tree_level1 = sys_open_tree(info->t_dir1_fd, DIR1,
+					    AT_NO_AUTOMOUNT |
+					    AT_SYMLINK_NOFOLLOW |
+					    OPEN_TREE_CLOEXEC |
+					    OPEN_TREE_CLONE);
+	if (fd_open_tree_level1 < 0) {
+		log_stderr("failure: sys_open_tree");
+		goto out;
+	}
+
+	fd_open_tree_level2 = sys_open_tree(info->t_dir1_fd, DIR1,
+					    AT_NO_AUTOMOUNT |
+					    AT_SYMLINK_NOFOLLOW |
+					    OPEN_TREE_CLOEXEC |
+					    OPEN_TREE_CLONE);
+	if (fd_open_tree_level2 < 0) {
+		log_stderr("failure: sys_open_tree");
+		goto out;
+	}
+
+	fd_open_tree_level3 = sys_open_tree(info->t_dir1_fd, DIR1,
+					    AT_NO_AUTOMOUNT |
+					    AT_SYMLINK_NOFOLLOW |
+					    OPEN_TREE_CLOEXEC |
+					    OPEN_TREE_CLONE);
+	if (fd_open_tree_level3 < 0) {
+		log_stderr("failure: sys_open_tree");
+		goto out;
+	}
+
+	fd_open_tree_level4 = sys_open_tree(info->t_dir1_fd, DIR1,
+					    AT_NO_AUTOMOUNT |
+					    AT_SYMLINK_NOFOLLOW |
+					    OPEN_TREE_CLOEXEC |
+					    OPEN_TREE_CLONE);
+	if (fd_open_tree_level4 < 0) {
+		log_stderr("failure: sys_open_tree");
+		goto out;
+	}
+
+	/* Turn detached mounts into detached idmapped mounts. */
+	if (sys_mount_setattr(fd_open_tree_level1, "", AT_EMPTY_PATH,
+			      &attr_level1, sizeof(attr_level1))) {
+		log_stderr("failure: sys_mount_setattr");
+		goto out;
+	}
+
+	if (sys_mount_setattr(fd_open_tree_level2, "", AT_EMPTY_PATH,
+			      &attr_level2, sizeof(attr_level2))) {
+		log_stderr("failure: sys_mount_setattr");
+		goto out;
+	}
+
+	if (sys_mount_setattr(fd_open_tree_level3, "", AT_EMPTY_PATH,
+			      &attr_level3, sizeof(attr_level3))) {
+		log_stderr("failure: sys_mount_setattr");
+		goto out;
+	}
+
+	if (sys_mount_setattr(fd_open_tree_level4, "", AT_EMPTY_PATH,
+			      &attr_level4, sizeof(attr_level4))) {
+		log_stderr("failure: sys_mount_setattr");
+		goto out;
+	}
+
+	/* Verify that ownership looks correct for callers in the init userns. */
+	for (id = 0; id <= id_file_range; id++) {
+		bool bret;
+		unsigned int id_level1, id_level2, id_level3;
+		char file[256];
+
+		snprintf(file, sizeof(file), FILE1 "_%u", id);
+
+		id_level1 = id + 1000000;
+		if (!expected_uid_gid(fd_open_tree_level1, file, 0, id_level1, id_level1)) {
+			log_stderr("failure: check ownership %s", file);
+			goto out;
+		}
+
+		id_level2 = id + 2000000;
+		if (!expected_uid_gid(fd_open_tree_level2, file, 0, id_level2, id_level2)) {
+			log_stderr("failure: check ownership %s", file);
+			goto out;
+		}
+
+		if (id == 999) {
+			/* This id is unmapped. */
+			bret = expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid);
+		} else if (id == 1000) {
+			id_level3 = id + 2000000; /* We punched a hole in the map at 1000. */
+			bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
+		} else {
+			id_level3 = id + 3000000; /* Rest is business as usual. */
+			bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
+		}
+		if (!bret) {
+			log_stderr("failure: check ownership %s", file);
+			goto out;
+		}
+
+		if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid)) {
+			log_stderr("failure: check ownership %s", file);
+			goto out;
+		}
+	}
+
+	/* Verify that ownership looks correct for callers in the first userns. */
+	pid = fork();
+	if (pid < 0) {
+		log_stderr("failure: fork");
+		goto out;
+	}
+	if (pid == 0) {
+		if (!switch_userns(attr_level1.userns_fd, 0, 0, false))
+			die("failure: switch_userns");
+
+		for (id = 0; id <= id_file_range; id++) {
+			bool bret;
+			unsigned int id_level1, id_level2, id_level3;
+			char file[256];
+
+			snprintf(file, sizeof(file), FILE1 "_%u", id);
+
+			id_level1 = id;
+			if (!expected_uid_gid(fd_open_tree_level1, file, 0, id_level1, id_level1))
+				die("failure: check ownership %s", file);
+
+			id_level2 = id + 1000000;
+			if (!expected_uid_gid(fd_open_tree_level2, file, 0, id_level2, id_level2))
+				die("failure: check ownership %s", file);
+
+			if (id == 999) {
+				/* This id is unmapped. */
+				bret = expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid);
+			} else if (id == 1000) {
+				id_level3 = id + 1000000; /* We punched a hole in the map at 1000. */
+				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
+			} else {
+				id_level3 = id + 2000000; /* Rest is business as usual. */
+				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
+			}
+			if (!bret)
+				die("failure: check ownership %s", file);
+
+			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+		}
+
+		exit(EXIT_SUCCESS);
+	}
+	if (wait_for_pid(pid))
+		goto out;
+
+	/* Verify that ownership looks correct for callers in the second userns. */
+	pid = fork();
+	if (pid < 0) {
+		log_stderr("failure: fork");
+		goto out;
+	}
+	if (pid == 0) {
+		if (!switch_userns(attr_level2.userns_fd, 0, 0, false))
+			die("failure: switch_userns");
+
+		for (id = 0; id <= id_file_range; id++) {
+			bool bret;
+			unsigned int id_level2, id_level3;
+			char file[256];
+
+			snprintf(file, sizeof(file), FILE1 "_%u", id);
+
+			if (!expected_uid_gid(fd_open_tree_level1, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+
+			id_level2 = id;
+			if (!expected_uid_gid(fd_open_tree_level2, file, 0, id_level2, id_level2))
+				die("failure: check ownership %s", file);
+
+			if (id == 999) {
+				/* This id is unmapped. */
+				bret = expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid);
+			} else if (id == 1000) {
+				id_level3 = id; /* We punched a hole in the map at 1000. */
+				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
+			} else {
+				id_level3 = id + 1000000; /* Rest is business as usual. */
+				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
+			}
+			if (!bret)
+				die("failure: check ownership %s", file);
+
+			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+		}
+
+		exit(EXIT_SUCCESS);
+	}
+	if (wait_for_pid(pid))
+		goto out;
+
+	/* Verify that ownership looks correct for callers in the third userns. */
+	pid = fork();
+	if (pid < 0) {
+		log_stderr("failure: fork");
+		goto out;
+	}
+	if (pid == 0) {
+		if (!switch_userns(attr_level3.userns_fd, 0, 0, false))
+			die("failure: switch_userns");
+
+		for (id = 0; id <= id_file_range; id++) {
+			bool bret;
+			unsigned int id_level2, id_level3;
+			char file[256];
+
+			snprintf(file, sizeof(file), FILE1 "_%u", id);
+
+			if (!expected_uid_gid(fd_open_tree_level1, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+
+			if (id == 1000) {
+				/*
+				 * The idmapping of the third userns has a hole
+				 * at uid/gid 1000. That means:
+				 * - 1000->userns_0(2000000) // init userns
+				 * - 1000->userns_1(2000000) // level 1
+				 * - 1000->userns_2(1000000) // level 2
+				 * - 1000->userns_3(1000)    // level 3 (because level 3 has a hole)
+				 */
+				id_level2 = id;
+				bret = expected_uid_gid(fd_open_tree_level2, file, 0, id_level2, id_level2);
+			} else {
+				bret = expected_uid_gid(fd_open_tree_level2, file, 0, info->t_overflowuid, info->t_overflowgid);
+			}
+			if (!bret)
+				die("failure: check ownership %s", file);
+
+
+			if (id == 999) {
+				/* This id is unmapped. */
+				bret = expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid);
+			} else {
+				id_level3 = id; /* Rest is business as usual. */
+				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
+			}
+			if (!bret)
+				die("failure: check ownership %s", file);
+
+			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+		}
+
+		exit(EXIT_SUCCESS);
+	}
+	if (wait_for_pid(pid))
+		goto out;
+
+	/* Verify that ownership looks correct for callers in the fourth userns. */
+	pid = fork();
+	if (pid < 0) {
+		log_stderr("failure: fork");
+		goto out;
+	}
+	if (pid == 0) {
+		if (setns(attr_level4.userns_fd, CLONE_NEWUSER))
+			die("failure: switch_userns");
+
+		for (id = 0; id <= id_file_range; id++) {
+			char file[256];
+
+			snprintf(file, sizeof(file), FILE1 "_%u", id);
+
+			if (!expected_uid_gid(fd_open_tree_level1, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+
+			if (!expected_uid_gid(fd_open_tree_level2, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+
+			if (!expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+
+			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+		}
+
+		exit(EXIT_SUCCESS);
+	}
+	if (wait_for_pid(pid))
+		goto out;
+
+	/* Verify that chown works correctly for callers in the first userns. */
+	pid = fork();
+	if (pid < 0) {
+		log_stderr("failure: fork");
+		goto out;
+	}
+	if (pid == 0) {
+		if (!switch_userns(attr_level1.userns_fd, 0, 0, false))
+			die("failure: switch_userns");
+
+		for (id = 0; id <= id_file_range; id++) {
+			bool bret;
+			unsigned int id_level1, id_level2, id_level3, id_new;
+			char file[256];
+
+			snprintf(file, sizeof(file), FILE1 "_%u", id);
+
+			id_new = id + 1;
+			if (fchownat(fd_open_tree_level1, file, id_new, id_new, AT_SYMLINK_NOFOLLOW))
+				die("failure: fchownat %s", file);
+
+			id_level1 = id_new;
+			if (!expected_uid_gid(fd_open_tree_level1, file, 0, id_level1, id_level1))
+				die("failure: check ownership %s", file);
+
+			id_level2 = id_new + 1000000;
+			if (!expected_uid_gid(fd_open_tree_level2, file, 0, id_level2, id_level2))
+				die("failure: check ownership %s", file);
+
+			if (id_new == 999) {
+				/* This id is unmapped. */
+				bret = expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid);
+			} else if (id_new == 1000) {
+				id_level3 = id_new + 1000000; /* We punched a hole in the map at 1000. */
+				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
+			} else {
+				id_level3 = id_new + 2000000; /* Rest is business as usual. */
+				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
+			}
+			if (!bret)
+				die("failure: check ownership %s", file);
+
+			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+
+			/* Revert ownership. */
+			if (fchownat(fd_open_tree_level1, file, id, id, AT_SYMLINK_NOFOLLOW))
+				die("failure: fchownat %s", file);
+		}
+
+		exit(EXIT_SUCCESS);
+	}
+	if (wait_for_pid(pid))
+		goto out;
+
+	/* Verify that chown works correctly for callers in the second userns. */
+	pid = fork();
+	if (pid < 0) {
+		log_stderr("failure: fork");
+		goto out;
+	}
+	if (pid == 0) {
+		if (!switch_userns(attr_level2.userns_fd, 0, 0, false))
+			die("failure: switch_userns");
+
+		for (id = 0; id <= id_file_range; id++) {
+			bool bret;
+			unsigned int id_level2, id_level3, id_new;
+			char file[256];
+
+			snprintf(file, sizeof(file), FILE1 "_%u", id);
+
+			id_new = id + 1;
+			if (fchownat(fd_open_tree_level2, file, id_new, id_new, AT_SYMLINK_NOFOLLOW))
+				die("failure: fchownat %s", file);
+
+			if (!expected_uid_gid(fd_open_tree_level1, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+
+			id_level2 = id_new;
+			if (!expected_uid_gid(fd_open_tree_level2, file, 0, id_level2, id_level2))
+				die("failure: check ownership %s", file);
+
+			if (id_new == 999) {
+				/* This id is unmapped. */
+				bret = expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid);
+			} else if (id_new == 1000) {
+				id_level3 = id_new; /* We punched a hole in the map at 1000. */
+				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
+			} else {
+				id_level3 = id_new + 1000000; /* Rest is business as usual. */
+				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
+			}
+			if (!bret)
+				die("failure: check ownership %s", file);
+
+			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+
+			/* Revert ownership. */
+			if (fchownat(fd_open_tree_level2, file, id, id, AT_SYMLINK_NOFOLLOW))
+				die("failure: fchownat %s", file);
+		}
+
+		exit(EXIT_SUCCESS);
+	}
+	if (wait_for_pid(pid))
+		goto out;
+
+	/* Verify that chown works correctly for callers in the third userns. */
+	pid = fork();
+	if (pid < 0) {
+		log_stderr("failure: fork");
+		goto out;
+	}
+	if (pid == 0) {
+		if (!switch_userns(attr_level3.userns_fd, 0, 0, false))
+			die("failure: switch_userns");
+
+		for (id = 0; id <= id_file_range; id++) {
+			unsigned int id_new;
+			char file[256];
+
+			snprintf(file, sizeof(file), FILE1 "_%u", id);
+
+			id_new = id + 1;
+			if (id_new == 999 || id_new == 1000) {
+				/*
+				 * We can't change ownership as we can't
+				 * chown from or to an unmapped id.
+				 */
+				if (!fchownat(fd_open_tree_level3, file, id_new, id_new, AT_SYMLINK_NOFOLLOW))
+					die("failure: fchownat %s", file);
+			} else {
+				if (fchownat(fd_open_tree_level3, file, id_new, id_new, AT_SYMLINK_NOFOLLOW))
+					die("failure: fchownat %s", file);
+			}
+
+			if (!expected_uid_gid(fd_open_tree_level1, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+
+			/* There's no id 1000 anymore as we changed ownership for id 1000 to 1001 above. */
+			if (!expected_uid_gid(fd_open_tree_level2, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+
+			if (id_new == 999) {
+				/*
+				 * We did not change ownership as we can't
+				 * chown to an unmapped id.
+				 */
+				if (!expected_uid_gid(fd_open_tree_level3, file, 0, id, id))
+					die("failure: check ownership %s", file);
+			} else if (id_new == 1000) {
+				/*
+				 * We did not change ownership as we can't
+				 * chown from an unmapped id.
+				 */
+				if (!expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid))
+					die("failure: check ownership %s", file);
+			} else {
+				if (!expected_uid_gid(fd_open_tree_level3, file, 0, id_new, id_new))
+					die("failure: check ownership %s", file);
+			}
+
+			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+
+			/* Revert ownership. */
+			if (id_new != 999 && id_new != 1000) {
+				if (fchownat(fd_open_tree_level3, file, id, id, AT_SYMLINK_NOFOLLOW))
+					die("failure: fchownat %s", file);
+			}
+		}
+
+		exit(EXIT_SUCCESS);
+	}
+	if (wait_for_pid(pid))
+		goto out;
+
+	/* Verify that chown works correctly for callers in the fourth userns. */
+	pid = fork();
+	if (pid < 0) {
+		log_stderr("failure: fork");
+		goto out;
+	}
+	if (pid == 0) {
+		if (setns(attr_level4.userns_fd, CLONE_NEWUSER))
+			die("failure: switch_userns");
+
+		for (id = 0; id <= id_file_range; id++) {
+			char file[256];
+			unsigned long id_new;
+
+			snprintf(file, sizeof(file), FILE1 "_%u", id);
+
+			id_new = id + 1;
+			if (!fchownat(fd_open_tree_level4, file, id_new, id_new, AT_SYMLINK_NOFOLLOW))
+				die("failure: fchownat %s", file);
+
+			if (!expected_uid_gid(fd_open_tree_level1, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+
+			if (!expected_uid_gid(fd_open_tree_level2, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+
+			if (!expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+
+			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
+				die("failure: check ownership %s", file);
+
+		}
+
+		exit(EXIT_SUCCESS);
+	}
+	if (wait_for_pid(pid))
+		goto out;
+
+	fret = 0;
+	log_debug("Ran test");
+
+out:
+	list_for_each_safe(it, &hierarchy[0].id_map, next) {
+		list_del(it);
+		free(it->elem);
+		free(it);
+	}
+
+	list_for_each_safe(it, &hierarchy[1].id_map, next) {
+		list_del(it);
+		free(it->elem);
+		free(it);
+	}
+
+	list_for_each_safe(it, &hierarchy[2].id_map, next) {
+		list_del(it);
+		free(it->elem);
+		free(it);
+	}
+
+	safe_close(hierarchy[0].fd_userns);
+	safe_close(hierarchy[1].fd_userns);
+	safe_close(hierarchy[2].fd_userns);
+	safe_close(fd_dir1);
+	safe_close(fd_open_tree_level1);
+	safe_close(fd_open_tree_level2);
+	safe_close(fd_open_tree_level3);
+	safe_close(fd_open_tree_level4);
+	return fret;
+}
+
+#define USER1 "fsgqa"
+#define USER2 "fsgqa2"
+
+/**
+ * lookup_ids - lookup uid and gid for a username
+ * @name: [in]  name of the user
+ * @uid:  [out] pointer to the user-ID
+ * @gid:  [out] pointer to the group-ID
+ *
+ * Lookup the uid and gid of a user.
+ *
+ * Return: On success, true is returned.
+ *         On error, false is returned.
+ */
+static bool lookup_ids(const char *name, uid_t *uid, gid_t *gid)
+{
+	bool bret = false;
+	struct passwd *pwentp = NULL;
+	struct passwd pwent;
+	char *buf;
+	ssize_t bufsize;
+	int ret;
+
+	bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+	if (bufsize < 0)
+		bufsize = 1024;
+
+	buf = malloc(bufsize);
+	if (!buf)
+		return bret;
+
+	ret = getpwnam_r(name, &pwent, buf, bufsize, &pwentp);
+	if (!ret && pwentp) {
+		*uid = pwent.pw_uid;
+		*gid = pwent.pw_gid;
+		bret = true;
+	}
+
+	free(buf);
+	return bret;
+}
+
+/**
+ * setattr_fix_968219708108 - test for commit 968219708108 ("fs: handle circular mappings correctly")
+ *
+ * Test that ->setattr() works correctly for idmapped mounts with circular
+ * idmappings such as:
+ *
+ * b:1000:1001:1
+ * b:1001:1000:1
+ *
+ * Assume a directory /source with two files:
+ *
+ * /source/file1 | 1000:1000
+ * /source/file2 | 1001:1001
+ *
+ * and we create an idmapped mount of /source at /target with an idmapped of:
+ *
+ * mnt_userns:        1000:1001:1
+ *                    1001:1000:1
+ *
+ * In the idmapped mount file1 will be owned by uid 1001 and file2 by uid 1000:
+ *
+ * /target/file1 | 1001:1001
+ * /target/file2 | 1000:1000
+ *
+ * Because in essence the idmapped mount switches ownership for {g,u}id 1000
+ * and {g,u}id 1001.
+ *
+ * 1. A user with fs{g,u}id 1000 must be allowed to setattr /target/file2 from
+ *    {g,u}id 1000 in the idmapped mount to {g,u}id 1000.
+ * 2. A user with fs{g,u}id 1001 must be allowed to setattr /target/file1 from
+ *    {g,u}id 1001 in the idmapped mount to {g,u}id 1001.
+ * 3. A user with fs{g,u}id 1000 must fail to setattr /target/file1 from
+ *    {g,u}id 1001 in the idmapped mount to {g,u}id 1000.
+ *    This must fail with EPERM. The caller's fs{g,u}id doesn't match the
+ *    {g,u}id of the file.
+ * 4. A user with fs{g,u}id 1001 must fail to setattr /target/file2 from
+ *    {g,u}id 1000 in the idmapped mount to {g,u}id 1000.
+ *    This must fail with EPERM. The caller's fs{g,u}id doesn't match the
+ *    {g,u}id of the file.
+ * 5. Both, a user with fs{g,u}id 1000 and a user with fs{g,u}id 1001, must
+ *    fail to setattr /target/file1 owned by {g,u}id 1001 in the idmapped mount
+ *    and /target/file2 owned by {g,u}id 1000 in the idmapped mount to any
+ *    {g,u}id apart from {g,u}id 1000 or 1001 with EINVAL.
+ *    Only {g,u}id 1000 and 1001 have a mapping in the idmapped mount. Other
+ *    {g,u}id are unmapped.
+ */
+static int setattr_fix_968219708108(const struct vfstest_info *info)
+{
+	int fret = -1;
+	int open_tree_fd = -EBADF;
+	struct mount_attr attr = {
+		.attr_set	= MOUNT_ATTR_IDMAP,
+		.userns_fd	= -EBADF,
+	};
+	int ret;
+	uid_t user1_uid, user2_uid;
+	gid_t user1_gid, user2_gid;
+	pid_t pid;
+	struct list idmap;
+	struct list *it_cur, *it_next;
+
+	if (!caps_supported())
+		return 0;
+
+	list_init(&idmap);
+
+	if (!lookup_ids(USER1, &user1_uid, &user1_gid)) {
+		log_stderr("failure: lookup_user");
+		goto out;
+	}
+
+	if (!lookup_ids(USER2, &user2_uid, &user2_gid)) {
+		log_stderr("failure: lookup_user");
+		goto out;
+	}
+
+	log_debug("Found " USER1 " with uid(%d) and gid(%d) and " USER2 " with uid(%d) and gid(%d)",
+		  user1_uid, user1_gid, user2_uid, user2_gid);
+
+	if (mkdirat(info->t_dir1_fd, DIR1, 0777)) {
+		log_stderr("failure: mkdirat");
+		goto out;
+	}
+
+	if (mknodat(info->t_dir1_fd, DIR1 "/" FILE1, S_IFREG | 0644, 0)) {
+		log_stderr("failure: mknodat");
+		goto out;
+	}
+
+	if (chown_r(info->t_mnt_fd, T_DIR1, user1_uid, user1_gid)) {
+		log_stderr("failure: chown_r");
+		goto out;
+	}
+
+	if (mknodat(info->t_dir1_fd, DIR1 "/" FILE2, S_IFREG | 0644, 0)) {
+		log_stderr("failure: mknodat");
+		goto out;
+	}
+
+	if (fchownat(info->t_dir1_fd, DIR1 "/" FILE2, user2_uid, user2_gid, AT_SYMLINK_NOFOLLOW)) {
+		log_stderr("failure: fchownat");
+		goto out;
+	}
+
+	print_r(info->t_mnt_fd, T_DIR1);
+
+	/* u:1000:1001:1 */
+	ret = add_map_entry(&idmap, user1_uid, user2_uid, 1, ID_TYPE_UID);
+	if (ret) {
+		log_stderr("failure: add_map_entry");
+		goto out;
+	}
+
+	/* u:1001:1000:1 */
+	ret = add_map_entry(&idmap, user2_uid, user1_uid, 1, ID_TYPE_UID);
+	if (ret) {
+		log_stderr("failure: add_map_entry");
+		goto out;
+	}
+
+	/* g:1000:1001:1 */
+	ret = add_map_entry(&idmap, user1_gid, user2_gid, 1, ID_TYPE_GID);
+	if (ret) {
+		log_stderr("failure: add_map_entry");
+		goto out;
+	}
+
+	/* g:1001:1000:1 */
+	ret = add_map_entry(&idmap, user2_gid, user1_gid, 1, ID_TYPE_GID);
+	if (ret) {
+		log_stderr("failure: add_map_entry");
+		goto out;
+	}
+
+	attr.userns_fd = get_userns_fd_from_idmap(&idmap);
+	if (attr.userns_fd < 0) {
+		log_stderr("failure: get_userns_fd");
+		goto out;
+	}
+
+	open_tree_fd = sys_open_tree(info->t_dir1_fd, DIR1,
+				     AT_NO_AUTOMOUNT |
+				     AT_SYMLINK_NOFOLLOW |
+				     OPEN_TREE_CLOEXEC |
+				     OPEN_TREE_CLONE |
+				     AT_RECURSIVE);
+	if (open_tree_fd < 0) {
+		log_stderr("failure: sys_open_tree");
+		goto out;
+	}
+
+	if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) {
+		log_stderr("failure: sys_mount_setattr");
+		goto out;
+	}
+
+	print_r(open_tree_fd, "");
+
+	pid = fork();
+	if (pid < 0) {
+		log_stderr("failure: fork");
+		goto out;
+	}
+	if (pid == 0) {
+		/* switch to {g,u}id 1001 */
+		if (!switch_resids(user2_uid, user2_gid))
+			die("failure: switch_resids");
+
+		/* drop all capabilities */
+		if (!caps_down())
+			die("failure: caps_down");
+
+		/*
+		 * The {g,u}id 0 is not mapped in this idmapped mount so this
+		 * needs to fail with EINVAL.
+		 */
+		if (!fchownat(open_tree_fd, FILE1, 0, 0, AT_SYMLINK_NOFOLLOW))
+			die("failure: change ownership");
+		if (errno != EINVAL)
+			die("failure: errno");
+
+		/*
+		 * A user with fs{g,u}id 1001 must be allowed to change
+		 * ownership of /target/file1 owned by {g,u}id 1001 in this
+		 * idmapped mount to {g,u}id 1001.
+		 */
+		if (fchownat(open_tree_fd, FILE1, user2_uid, user2_gid,
+			     AT_SYMLINK_NOFOLLOW))
+			die("failure: change ownership");
+
+		/* Verify that the ownership is still {g,u}id 1001. */
+		if (!expected_uid_gid(open_tree_fd, FILE1, AT_SYMLINK_NOFOLLOW,
+				      user2_uid, user2_gid))
+			die("failure: check ownership");
+
+		/*
+		 * A user with fs{g,u}id 1001 must not be allowed to change
+		 * ownership of /target/file1 owned by {g,u}id 1001 in this
+		 * idmapped mount to {g,u}id 1000.
+		 */
+		if (!fchownat(open_tree_fd, FILE1, user1_uid, user1_gid,
+			      AT_SYMLINK_NOFOLLOW))
+			die("failure: change ownership");
+		if (errno != EPERM)
+			die("failure: errno");
+
+		/* Verify that the ownership is still {g,u}id 1001. */
+		if (!expected_uid_gid(open_tree_fd, FILE1, AT_SYMLINK_NOFOLLOW,
+				      user2_uid, user2_gid))
+			die("failure: check ownership");
+
+		/*
+		 * A user with fs{g,u}id 1001 must not be allowed to change
+		 * ownership of /target/file2 owned by {g,u}id 1000 in this
+		 * idmapped mount to {g,u}id 1000.
+		 */
+		if (!fchownat(open_tree_fd, FILE2, user1_uid, user1_gid,
+			      AT_SYMLINK_NOFOLLOW))
+			die("failure: change ownership");
+		if (errno != EPERM)
+			die("failure: errno");
+
+		/* Verify that the ownership is still {g,u}id 1000. */
+		if (!expected_uid_gid(open_tree_fd, FILE2, AT_SYMLINK_NOFOLLOW,
+				      user1_uid, user1_gid))
+			die("failure: check ownership");
+
+		/*
+		 * A user with fs{g,u}id 1001 must not be allowed to change
+		 * ownership of /target/file2 owned by {g,u}id 1000 in this
+		 * idmapped mount to {g,u}id 1001.
+		 */
+		if (!fchownat(open_tree_fd, FILE2, user2_uid, user2_gid,
+			      AT_SYMLINK_NOFOLLOW))
+			die("failure: change ownership");
+		if (errno != EPERM)
+			die("failure: errno");
+
+		/* Verify that the ownership is still {g,u}id 1000. */
+		if (!expected_uid_gid(open_tree_fd, FILE2, AT_SYMLINK_NOFOLLOW,
+				      user1_uid, user1_gid))
+			die("failure: check ownership");
+
+		exit(EXIT_SUCCESS);
+	}
+	if (wait_for_pid(pid))
+		goto out;
+
+	pid = fork();
+	if (pid < 0) {
+		log_stderr("failure: fork");
+		goto out;
+	}
+	if (pid == 0) {
+		/* switch to {g,u}id 1000 */
+		if (!switch_resids(user1_uid, user1_gid))
+			die("failure: switch_resids");
+
+		/* drop all capabilities */
+		if (!caps_down())
+			die("failure: caps_down");
+
+		/*
+		 * The {g,u}id 0 is not mapped in this idmapped mount so this
+		 * needs to fail with EINVAL.
+		 */
+		if (!fchownat(open_tree_fd, FILE1, 0, 0, AT_SYMLINK_NOFOLLOW))
+			die("failure: change ownership");
+		if (errno != EINVAL)
+			die("failure: errno");
+
+		/*
+		 * A user with fs{g,u}id 1000 must be allowed to change
+		 * ownership of /target/file2 owned by {g,u}id 1000 in this
+		 * idmapped mount to {g,u}id 1000.
+		 */
+		if (fchownat(open_tree_fd, FILE2, user1_uid, user1_gid,
+			     AT_SYMLINK_NOFOLLOW))
+			die("failure: change ownership");
+
+		/* Verify that the ownership is still {g,u}id 1000. */
+		if (!expected_uid_gid(open_tree_fd, FILE2, AT_SYMLINK_NOFOLLOW,
+				      user1_uid, user1_gid))
+			die("failure: check ownership");
+
+		/*
+		 * A user with fs{g,u}id 1000 must not be allowed to change
+		 * ownership of /target/file2 owned by {g,u}id 1000 in this
+		 * idmapped mount to {g,u}id 1001.
+		 */
+		if (!fchownat(open_tree_fd, FILE2, user2_uid, user2_gid,
+			      AT_SYMLINK_NOFOLLOW))
+			die("failure: change ownership");
+		if (errno != EPERM)
+			die("failure: errno");
+
+		/* Verify that the ownership is still {g,u}id 1000. */
+		if (!expected_uid_gid(open_tree_fd, FILE2, AT_SYMLINK_NOFOLLOW,
+				      user1_uid, user1_gid))
+			die("failure: check ownership");
+
+		/*
+		 * A user with fs{g,u}id 1000 must not be allowed to change
+		 * ownership of /target/file1 owned by {g,u}id 1001 in this
+		 * idmapped mount to {g,u}id 1000.
+		 */
+		if (!fchownat(open_tree_fd, FILE1, user1_uid, user1_gid,
+			     AT_SYMLINK_NOFOLLOW))
+			die("failure: change ownership");
+		if (errno != EPERM)
+			die("failure: errno");
+
+		/* Verify that the ownership is still {g,u}id 1001. */
+		if (!expected_uid_gid(open_tree_fd, FILE1, AT_SYMLINK_NOFOLLOW,
+				      user2_uid, user2_gid))
+			die("failure: check ownership");
+
+		/*
+		 * A user with fs{g,u}id 1000 must not be allowed to change
+		 * ownership of /target/file1 owned by {g,u}id 1001 in this
+		 * idmapped mount to {g,u}id 1001.
+		 */
+		if (!fchownat(open_tree_fd, FILE1, user2_uid, user2_gid,
+			      AT_SYMLINK_NOFOLLOW))
+			die("failure: change ownership");
+		if (errno != EPERM)
+			die("failure: errno");
+
+		/* Verify that the ownership is still {g,u}id 1001. */
+		if (!expected_uid_gid(open_tree_fd, FILE1, AT_SYMLINK_NOFOLLOW,
+				      user2_uid, user2_gid))
+			die("failure: check ownership");
+
+		exit(EXIT_SUCCESS);
+	}
+	if (wait_for_pid(pid))
+		goto out;
+
+	fret = 0;
+	log_debug("Ran test");
+out:
+	safe_close(attr.userns_fd);
+	safe_close(open_tree_fd);
+
+	list_for_each_safe(it_cur, &idmap, it_next) {
+		list_del(it_cur);
+		free(it_cur->elem);
+		free(it_cur);
+	}
+
+	return fret;
+}
+
+/**
+ * setxattr_fix_705191b03d50 - test for commit 705191b03d50 ("fs: fix acl translation").
+ */
+static int setxattr_fix_705191b03d50(const struct vfstest_info *info)
+{
+	int fret = -1;
+	int fd_userns = -EBADF;
+	int ret;
+	uid_t user1_uid;
+	gid_t user1_gid;
+	pid_t pid;
+	struct list idmap;
+	struct list *it_cur, *it_next;
+
+	list_init(&idmap);
+
+	if (!lookup_ids(USER1, &user1_uid, &user1_gid)) {
+		log_stderr("failure: lookup_user");
+		goto out;
+	}
+
+	log_debug("Found " USER1 " with uid(%d) and gid(%d)", user1_uid, user1_gid);
+
+	if (mkdirat(info->t_dir1_fd, DIR1, 0777)) {
+		log_stderr("failure: mkdirat");
+		goto out;
+	}
+
+	if (chown_r(info->t_mnt_fd, T_DIR1, user1_uid, user1_gid)) {
+		log_stderr("failure: chown_r");
+		goto out;
+	}
+
+	print_r(info->t_mnt_fd, T_DIR1);
+
+	/* u:0:user1_uid:1 */
+	ret = add_map_entry(&idmap, user1_uid, 0, 1, ID_TYPE_UID);
+	if (ret) {
+		log_stderr("failure: add_map_entry");
+		goto out;
+	}
+
+	/* g:0:user1_gid:1 */
+	ret = add_map_entry(&idmap, user1_gid, 0, 1, ID_TYPE_GID);
+	if (ret) {
+		log_stderr("failure: add_map_entry");
+		goto out;
+	}
+
+	/* u:100:10000:100 */
+	ret = add_map_entry(&idmap, 10000, 100, 100, ID_TYPE_UID);
+	if (ret) {
+		log_stderr("failure: add_map_entry");
+		goto out;
+	}
+
+	/* g:100:10000:100 */
+	ret = add_map_entry(&idmap, 10000, 100, 100, ID_TYPE_GID);
+	if (ret) {
+		log_stderr("failure: add_map_entry");
+		goto out;
+	}
+
+	fd_userns = get_userns_fd_from_idmap(&idmap);
+	if (fd_userns < 0) {
+		log_stderr("failure: get_userns_fd");
+		goto out;
+	}
+
+	pid = fork();
+	if (pid < 0) {
+		log_stderr("failure: fork");
+		goto out;
+	}
+	if (pid == 0) {
+		if (!switch_userns(fd_userns, 0, 0, false))
+			die("failure: switch_userns");
+
+		/* create separate mount namespace */
+		if (unshare(CLONE_NEWNS))
+			die("failure: create new mount namespace");
+
+		/* turn off mount propagation */
+		if (sys_mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0))
+			die("failure: turn mount propagation off");
+
+		snprintf(t_buf, sizeof(t_buf), "%s/%s/%s", info->t_mountpoint, T_DIR1, DIR1);
+
+		if (sys_mount("none", t_buf, "tmpfs", 0, "mode=0755"))
+			die("failure: mount");
+
+		snprintf(t_buf, sizeof(t_buf), "%s/%s/%s/%s", info->t_mountpoint, T_DIR1, DIR1, DIR3);
+		if (mkdir(t_buf, 0700))
+			die("failure: mkdir");
+
+		snprintf(t_buf, sizeof(t_buf), "setfacl -m u:100:rwx %s/%s/%s/%s", info->t_mountpoint, T_DIR1, DIR1, DIR3);
+		if (system(t_buf))
+			die("failure: system");
+
+		snprintf(t_buf, sizeof(t_buf), "getfacl -n -p %s/%s/%s/%s | grep -q user:100:rwx", info->t_mountpoint, T_DIR1, DIR1, DIR3);
+		if (system(t_buf))
+			die("failure: system");
+
+		exit(EXIT_SUCCESS);
+	}
+	if (wait_for_pid(pid))
+		goto out;
+
+	fret = 0;
+	log_debug("Ran test");
+out:
+	safe_close(fd_userns);
+
+	list_for_each_safe(it_cur, &idmap, it_next) {
+		list_del(it_cur);
+		free(it_cur->elem);
+		free(it_cur);
+	}
+
+	return fret;
+}
+
 static const struct test_struct t_idmapped_mounts[] = {
 	{ acls,                                                         true,   "posix acls on regular mounts",                                                                 },
 	{ create_in_userns,                                             true,   "create operations in user namespace",                                                          },
@@ -6731,3 +7957,32 @@ const struct test_suite s_fscaps_in_ancestor_userns = {
 	.tests		= t_fscaps_in_ancestor_userns,
 	.nr_tests	= ARRAY_SIZE(t_fscaps_in_ancestor_userns),
 };
+
+static const struct test_struct t_nested_userns[] = {
+	{ nested_userns,						T_REQUIRE_IDMAPPED_MOUNTS,	"test that nested user namespaces behave correctly when attached to idmapped mounts",		},
+};
+
+const struct test_suite s_nested_userns = {
+	.tests = t_nested_userns,
+	.nr_tests = ARRAY_SIZE(t_nested_userns),
+};
+
+/* Test for commit 968219708108 ("fs: handle circular mappings correctly"). */
+static const struct test_struct t_setattr_fix_968219708108[] = {
+	{ setattr_fix_968219708108,					T_REQUIRE_IDMAPPED_MOUNTS,	"test that setattr works correctly",								},
+};
+
+const struct test_suite s_setattr_fix_968219708108 = {
+	.tests = t_setattr_fix_968219708108,
+	.nr_tests = ARRAY_SIZE(t_setattr_fix_968219708108),
+};
+
+/* Test for commit 705191b03d50 ("fs: fix acl translation"). */
+static const struct test_struct t_setxattr_fix_705191b03d50[] = {
+	{ setxattr_fix_705191b03d50,					T_REQUIRE_USERNS,	"test that setxattr works correctly for userns mountable filesystems",				},
+};
+
+const struct test_suite s_setxattr_fix_705191b03d50 = {
+	.tests = t_setxattr_fix_705191b03d50,
+	.nr_tests = ARRAY_SIZE(t_setxattr_fix_705191b03d50),
+};
diff --git a/src/vfs/idmapped-mounts.h b/src/vfs/idmapped-mounts.h
index 37c8886d..ff21ea2c 100644
--- a/src/vfs/idmapped-mounts.h
+++ b/src/vfs/idmapped-mounts.h
@@ -11,5 +11,8 @@
 
 extern const struct test_suite s_idmapped_mounts;
 extern const struct test_suite s_fscaps_in_ancestor_userns;
+extern const struct test_suite s_nested_userns;
+extern const struct test_suite s_setattr_fix_968219708108;
+extern const struct test_suite s_setxattr_fix_705191b03d50;
 
 #endif /* __IDMAPPED_MOUNTS_H */
diff --git a/src/vfs/utils.c b/src/vfs/utils.c
index 1634e5c8..1388edda 100644
--- a/src/vfs/utils.c
+++ b/src/vfs/utils.c
@@ -918,3 +918,133 @@ bool openat_tmpfile_supported(int dirfd)
 
 	return true;
 }
+
+/*
+ * There'll be scenarios where you'll want to see the attributes associated with
+ * a directory tree during debugging or just to make sure things look correct.
+ * Simply uncomment and place the print_r() helper where you need it.
+ */
+#ifdef DEBUG_TRACE
+static int fd_cloexec(int fd, bool cloexec)
+{
+	int oflags, nflags;
+
+	oflags = fcntl(fd, F_GETFD, 0);
+	if (oflags < 0)
+		return -errno;
+
+	if (cloexec)
+		nflags = oflags | FD_CLOEXEC;
+	else
+		nflags = oflags & ~FD_CLOEXEC;
+
+	if (nflags == oflags)
+		return 0;
+
+	if (fcntl(fd, F_SETFD, nflags) < 0)
+		return -errno;
+
+	return 0;
+}
+
+static inline int dup_cloexec(int fd)
+{
+	int fd_dup;
+
+	fd_dup = dup(fd);
+	if (fd_dup < 0)
+		return -errno;
+
+	if (fd_cloexec(fd_dup, true)) {
+		close(fd_dup);
+		return -errno;
+	}
+
+	return fd_dup;
+}
+
+int print_r(int fd, const char *path)
+{
+	int ret = 0;
+	int dfd, dfd_dup;
+	DIR *dir;
+	struct dirent *direntp;
+	struct stat st;
+
+	if (!path || *path == '\0') {
+		char buf[sizeof("/proc/self/fd/") + 30];
+
+		ret = snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd);
+		if (ret < 0 || (size_t)ret >= sizeof(buf))
+			return -1;
+
+		/*
+		 * O_PATH file descriptors can't be used so we need to re-open
+		 * just in case.
+		 */
+		dfd = openat(-EBADF, buf, O_CLOEXEC | O_DIRECTORY, 0);
+	} else {
+		dfd = openat(fd, path, O_CLOEXEC | O_DIRECTORY, 0);
+	}
+	if (dfd < 0)
+		return -1;
+
+	/*
+	 * When fdopendir() below succeeds it assumes ownership of the fd so we
+	 * to make sure we always have an fd that fdopendir() can own which is
+	 * why we dup() in the case where the caller wants us to operate on the
+	 * fd directly.
+	 */
+	dfd_dup = dup_cloexec(dfd);
+	if (dfd_dup < 0) {
+		close(dfd);
+		return -1;
+	}
+
+	dir = fdopendir(dfd);
+	if (!dir) {
+		close(dfd);
+		close(dfd_dup);
+		return -1;
+	}
+	/* Transfer ownership to fdopendir(). */
+	dfd = -EBADF;
+
+	while ((direntp = readdir(dir))) {
+		if (!strcmp(direntp->d_name, ".") ||
+		    !strcmp(direntp->d_name, ".."))
+			continue;
+
+		ret = fstatat(dfd_dup, direntp->d_name, &st, AT_SYMLINK_NOFOLLOW);
+		if (ret < 0 && errno != ENOENT)
+			break;
+
+		ret = 0;
+		if (S_ISDIR(st.st_mode))
+			ret = print_r(dfd_dup, direntp->d_name);
+		else
+			fprintf(stderr, "mode(%o):uid(%d):gid(%d) -> %d/%s\n",
+				(st.st_mode & ~S_IFMT), st.st_uid, st.st_gid,
+				dfd_dup, direntp->d_name);
+		if (ret < 0 && errno != ENOENT)
+			break;
+	}
+
+	if (!path || *path == '\0')
+		ret = fstatat(fd, "", &st,
+			      AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW |
+			      AT_EMPTY_PATH);
+	else
+		ret = fstatat(fd, path, &st,
+			      AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW);
+	if (!ret)
+		fprintf(stderr, "mode(%o):uid(%d):gid(%d) -> %s\n",
+			(st.st_mode & ~S_IFMT), st.st_uid, st.st_gid,
+			(path && *path) ? path : "(null)");
+
+	close(dfd_dup);
+	closedir(dir);
+
+	return ret;
+}
+#endif
diff --git a/src/vfs/utils.h b/src/vfs/utils.h
index 226daea7..7fb702fd 100644
--- a/src/vfs/utils.h
+++ b/src/vfs/utils.h
@@ -355,6 +355,11 @@ extern int io_uring_openat_with_creds(struct io_uring *ring, int dfd,
 
 extern int chown_r(int fd, const char *path, uid_t uid, gid_t gid);
 extern int rm_r(int fd, const char *path);
+#ifdef DEBUG_TRACE
+extern int print_r(int fd, const char *path);
+#else
+static inline int print_r(int fd, const char *path) { return 0; }
+#endif
 extern int fd_to_fd(int from, int to);
 extern bool protected_symlinks_enabled(void);
 extern bool xfs_irix_sgid_inherit_enabled(const char *fstype);
diff --git a/src/vfs/vfstest.c b/src/vfs/vfstest.c
index f9399b26..67aa3bda 100644
--- a/src/vfs/vfstest.c
+++ b/src/vfs/vfstest.c
@@ -28,8 +28,6 @@
 #include "missing.h"
 #include "utils.h"
 
-static char t_buf[PATH_MAX];
-
 static void init_vfstest_info(struct vfstest_info *info)
 {
 	info->t_overflowuid		= 65534;
@@ -81,141 +79,6 @@ static void stash_overflowgid(struct vfstest_info *info)
 	info->t_overflowgid = atoi(buf);
 }
 
-/*
- * There'll be scenarios where you'll want to see the attributes associated with
- * a directory tree during debugging or just to make sure things look correct.
- * Simply uncomment and place the print_r() helper where you need it.
- */
-#ifdef DEBUG_TRACE
-static int fd_cloexec(int fd, bool cloexec)
-{
-	int oflags, nflags;
-
-	oflags = fcntl(fd, F_GETFD, 0);
-	if (oflags < 0)
-		return -errno;
-
-	if (cloexec)
-		nflags = oflags | FD_CLOEXEC;
-	else
-		nflags = oflags & ~FD_CLOEXEC;
-
-	if (nflags == oflags)
-		return 0;
-
-	if (fcntl(fd, F_SETFD, nflags) < 0)
-		return -errno;
-
-	return 0;
-}
-
-static inline int dup_cloexec(int fd)
-{
-	int fd_dup;
-
-	fd_dup = dup(fd);
-	if (fd_dup < 0)
-		return -errno;
-
-	if (fd_cloexec(fd_dup, true)) {
-		close(fd_dup);
-		return -errno;
-	}
-
-	return fd_dup;
-}
-
-__attribute__((unused)) static int print_r(int fd, const char *path)
-{
-	int ret = 0;
-	int dfd, dfd_dup;
-	DIR *dir;
-	struct dirent *direntp;
-	struct stat st;
-
-	if (!path || *path == '\0') {
-		char buf[sizeof("/proc/self/fd/") + 30];
-
-		ret = snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd);
-		if (ret < 0 || (size_t)ret >= sizeof(buf))
-			return -1;
-
-		/*
-		 * O_PATH file descriptors can't be used so we need to re-open
-		 * just in case.
-		 */
-		dfd = openat(-EBADF, buf, O_CLOEXEC | O_DIRECTORY, 0);
-	} else {
-		dfd = openat(fd, path, O_CLOEXEC | O_DIRECTORY, 0);
-	}
-	if (dfd < 0)
-		return -1;
-
-	/*
-	 * When fdopendir() below succeeds it assumes ownership of the fd so we
-	 * to make sure we always have an fd that fdopendir() can own which is
-	 * why we dup() in the case where the caller wants us to operate on the
-	 * fd directly.
-	 */
-	dfd_dup = dup_cloexec(dfd);
-	if (dfd_dup < 0) {
-		close(dfd);
-		return -1;
-	}
-
-	dir = fdopendir(dfd);
-	if (!dir) {
-		close(dfd);
-		close(dfd_dup);
-		return -1;
-	}
-	/* Transfer ownership to fdopendir(). */
-	dfd = -EBADF;
-
-	while ((direntp = readdir(dir))) {
-		if (!strcmp(direntp->d_name, ".") ||
-		    !strcmp(direntp->d_name, ".."))
-			continue;
-
-		ret = fstatat(dfd_dup, direntp->d_name, &st, AT_SYMLINK_NOFOLLOW);
-		if (ret < 0 && errno != ENOENT)
-			break;
-
-		ret = 0;
-		if (S_ISDIR(st.st_mode))
-			ret = print_r(dfd_dup, direntp->d_name);
-		else
-			fprintf(stderr, "mode(%o):uid(%d):gid(%d) -> %d/%s\n",
-				(st.st_mode & ~S_IFMT), st.st_uid, st.st_gid,
-				dfd_dup, direntp->d_name);
-		if (ret < 0 && errno != ENOENT)
-			break;
-	}
-
-	if (!path || *path == '\0')
-		ret = fstatat(fd, "", &st,
-			      AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW |
-			      AT_EMPTY_PATH);
-	else
-		ret = fstatat(fd, path, &st,
-			      AT_NO_AUTOMOUNT | AT_SYMLINK_NOFOLLOW);
-	if (!ret)
-		fprintf(stderr, "mode(%o):uid(%d):gid(%d) -> %s\n",
-			(st.st_mode & ~S_IFMT), st.st_uid, st.st_gid,
-			(path && *path) ? path : "(null)");
-
-	close(dfd_dup);
-	closedir(dir);
-
-	return ret;
-}
-#else
-__attribute__((unused)) static int print_r(int fd, const char *path)
-{
-	return 0;
-}
-#endif
-
 static void test_setup(struct vfstest_info *info)
 {
 	if (mkdirat(info->t_mnt_fd, T_DIR1, 0777))
@@ -1926,1232 +1789,6 @@ out:
 	return fret;
 }
 
-static int nested_userns(const struct vfstest_info *info)
-{
-	int fret = -1;
-	int ret;
-	pid_t pid;
-	unsigned int id;
-	struct list *it, *next;
-	struct userns_hierarchy hierarchy[] = {
-		{ .level = 1, .fd_userns = -EBADF, },
-		{ .level = 2, .fd_userns = -EBADF, },
-		{ .level = 3, .fd_userns = -EBADF, },
-		{ .level = 4, .fd_userns = -EBADF, },
-		/* Dummy entry that marks the end. */
-		{ .level = MAX_USERNS_LEVEL, .fd_userns = -EBADF, },
-	};
-	struct mount_attr attr_level1 = {
-		.attr_set	= MOUNT_ATTR_IDMAP,
-		.userns_fd	= -EBADF,
-	};
-	struct mount_attr attr_level2 = {
-		.attr_set	= MOUNT_ATTR_IDMAP,
-		.userns_fd	= -EBADF,
-	};
-	struct mount_attr attr_level3 = {
-		.attr_set	= MOUNT_ATTR_IDMAP,
-		.userns_fd	= -EBADF,
-	};
-	struct mount_attr attr_level4 = {
-		.attr_set	= MOUNT_ATTR_IDMAP,
-		.userns_fd	= -EBADF,
-	};
-	int fd_dir1 = -EBADF,
-	    fd_open_tree_level1 = -EBADF,
-	    fd_open_tree_level2 = -EBADF,
-	    fd_open_tree_level3 = -EBADF,
-	    fd_open_tree_level4 = -EBADF;
-	const unsigned int id_file_range = 10000;
-
-	list_init(&hierarchy[0].id_map);
-	list_init(&hierarchy[1].id_map);
-	list_init(&hierarchy[2].id_map);
-	list_init(&hierarchy[3].id_map);
-
-	/*
-	 * Give a large map to the outermost user namespace so we can create
-	 * comfortable nested maps.
-	 */
-	ret = add_map_entry(&hierarchy[0].id_map, 1000000, 0, 1000000000, ID_TYPE_UID);
-	if (ret) {
-		log_stderr("failure: adding uidmap for userns at level 1");
-		goto out;
-	}
-
-	ret = add_map_entry(&hierarchy[0].id_map, 1000000, 0, 1000000000, ID_TYPE_GID);
-	if (ret) {
-		log_stderr("failure: adding gidmap for userns at level 1");
-		goto out;
-	}
-
-	/* This is uid:0->2000000:100000000 in init userns. */
-	ret = add_map_entry(&hierarchy[1].id_map, 1000000, 0, 100000000, ID_TYPE_UID);
-	if (ret) {
-		log_stderr("failure: adding uidmap for userns at level 2");
-		goto out;
-	}
-
-	/* This is gid:0->2000000:100000000 in init userns. */
-	ret = add_map_entry(&hierarchy[1].id_map, 1000000, 0, 100000000, ID_TYPE_GID);
-	if (ret) {
-		log_stderr("failure: adding gidmap for userns at level 2");
-		goto out;
-	}
-
-	/* This is uid:0->3000000:999 in init userns. */
-	ret = add_map_entry(&hierarchy[2].id_map, 1000000, 0, 999, ID_TYPE_UID);
-	if (ret) {
-		log_stderr("failure: adding uidmap for userns at level 3");
-		goto out;
-	}
-
-	/* This is gid:0->3000000:999 in the init userns. */
-	ret = add_map_entry(&hierarchy[2].id_map, 1000000, 0, 999, ID_TYPE_GID);
-	if (ret) {
-		log_stderr("failure: adding gidmap for userns at level 3");
-		goto out;
-	}
-
-	/* id 999 will remain unmapped. */
-
-	/* This is uid:1000->2001000:1 in init userns. */
-	ret = add_map_entry(&hierarchy[2].id_map, 1000, 1000, 1, ID_TYPE_UID);
-	if (ret) {
-		log_stderr("failure: adding uidmap for userns at level 3");
-		goto out;
-	}
-
-	/* This is gid:1000->2001000:1 in init userns. */
-	ret = add_map_entry(&hierarchy[2].id_map, 1000, 1000, 1, ID_TYPE_GID);
-	if (ret) {
-		log_stderr("failure: adding gidmap for userns at level 3");
-		goto out;
-	}
-
-	/* This is uid:1001->3001001:10000 in init userns. */
-	ret = add_map_entry(&hierarchy[2].id_map, 1001001, 1001, 10000000, ID_TYPE_UID);
-	if (ret) {
-		log_stderr("failure: adding uidmap for userns at level 3");
-		goto out;
-	}
-
-	/* This is gid:1001->3001001:10000 in init userns. */
-	ret = add_map_entry(&hierarchy[2].id_map, 1001001, 1001, 10000000, ID_TYPE_GID);
-	if (ret) {
-		log_stderr("failure: adding gidmap for userns at level 3");
-		goto out;
-	}
-
-	/* Don't write a mapping in the 4th userns. */
-	list_empty(&hierarchy[4].id_map);
-
-	/* Create the actual userns hierarchy. */
-	ret = create_userns_hierarchy(hierarchy);
-	if (ret) {
-		log_stderr("failure: create userns hierarchy");
-		goto out;
-	}
-
-	attr_level1.userns_fd = hierarchy[0].fd_userns;
-	attr_level2.userns_fd = hierarchy[1].fd_userns;
-	attr_level3.userns_fd = hierarchy[2].fd_userns;
-	attr_level4.userns_fd = hierarchy[3].fd_userns;
-
-	/*
-	 * Create one directory where we create files for each uid/gid within
-	 * the first userns.
-	 */
-	if (mkdirat(info->t_dir1_fd, DIR1, 0777)) {
-		log_stderr("failure: mkdirat");
-		goto out;
-	}
-
-	fd_dir1 = openat(info->t_dir1_fd, DIR1, O_DIRECTORY | O_CLOEXEC);
-	if (fd_dir1 < 0) {
-		log_stderr("failure: openat");
-		goto out;
-	}
-
-	for (id = 0; id <= id_file_range; id++) {
-		char file[256];
-
-		snprintf(file, sizeof(file), DIR1 "/" FILE1 "_%u", id);
-
-		if (mknodat(info->t_dir1_fd, file, S_IFREG | 0644, 0)) {
-			log_stderr("failure: create %s", file);
-			goto out;
-		}
-
-		if (fchownat(info->t_dir1_fd, file, id, id, AT_SYMLINK_NOFOLLOW)) {
-			log_stderr("failure: fchownat %s", file);
-			goto out;
-		}
-
-		if (!expected_uid_gid(info->t_dir1_fd, file, 0, id, id)) {
-			log_stderr("failure: check ownership %s", file);
-			goto out;
-		}
-	}
-
-	/* Create detached mounts for all the user namespaces. */
-	fd_open_tree_level1 = sys_open_tree(info->t_dir1_fd, DIR1,
-					    AT_NO_AUTOMOUNT |
-					    AT_SYMLINK_NOFOLLOW |
-					    OPEN_TREE_CLOEXEC |
-					    OPEN_TREE_CLONE);
-	if (fd_open_tree_level1 < 0) {
-		log_stderr("failure: sys_open_tree");
-		goto out;
-	}
-
-	fd_open_tree_level2 = sys_open_tree(info->t_dir1_fd, DIR1,
-					    AT_NO_AUTOMOUNT |
-					    AT_SYMLINK_NOFOLLOW |
-					    OPEN_TREE_CLOEXEC |
-					    OPEN_TREE_CLONE);
-	if (fd_open_tree_level2 < 0) {
-		log_stderr("failure: sys_open_tree");
-		goto out;
-	}
-
-	fd_open_tree_level3 = sys_open_tree(info->t_dir1_fd, DIR1,
-					    AT_NO_AUTOMOUNT |
-					    AT_SYMLINK_NOFOLLOW |
-					    OPEN_TREE_CLOEXEC |
-					    OPEN_TREE_CLONE);
-	if (fd_open_tree_level3 < 0) {
-		log_stderr("failure: sys_open_tree");
-		goto out;
-	}
-
-	fd_open_tree_level4 = sys_open_tree(info->t_dir1_fd, DIR1,
-					    AT_NO_AUTOMOUNT |
-					    AT_SYMLINK_NOFOLLOW |
-					    OPEN_TREE_CLOEXEC |
-					    OPEN_TREE_CLONE);
-	if (fd_open_tree_level4 < 0) {
-		log_stderr("failure: sys_open_tree");
-		goto out;
-	}
-
-	/* Turn detached mounts into detached idmapped mounts. */
-	if (sys_mount_setattr(fd_open_tree_level1, "", AT_EMPTY_PATH,
-			      &attr_level1, sizeof(attr_level1))) {
-		log_stderr("failure: sys_mount_setattr");
-		goto out;
-	}
-
-	if (sys_mount_setattr(fd_open_tree_level2, "", AT_EMPTY_PATH,
-			      &attr_level2, sizeof(attr_level2))) {
-		log_stderr("failure: sys_mount_setattr");
-		goto out;
-	}
-
-	if (sys_mount_setattr(fd_open_tree_level3, "", AT_EMPTY_PATH,
-			      &attr_level3, sizeof(attr_level3))) {
-		log_stderr("failure: sys_mount_setattr");
-		goto out;
-	}
-
-	if (sys_mount_setattr(fd_open_tree_level4, "", AT_EMPTY_PATH,
-			      &attr_level4, sizeof(attr_level4))) {
-		log_stderr("failure: sys_mount_setattr");
-		goto out;
-	}
-
-	/* Verify that ownership looks correct for callers in the init userns. */
-	for (id = 0; id <= id_file_range; id++) {
-		bool bret;
-		unsigned int id_level1, id_level2, id_level3;
-		char file[256];
-
-		snprintf(file, sizeof(file), FILE1 "_%u", id);
-
-		id_level1 = id + 1000000;
-		if (!expected_uid_gid(fd_open_tree_level1, file, 0, id_level1, id_level1)) {
-			log_stderr("failure: check ownership %s", file);
-			goto out;
-		}
-
-		id_level2 = id + 2000000;
-		if (!expected_uid_gid(fd_open_tree_level2, file, 0, id_level2, id_level2)) {
-			log_stderr("failure: check ownership %s", file);
-			goto out;
-		}
-
-		if (id == 999) {
-			/* This id is unmapped. */
-			bret = expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid);
-		} else if (id == 1000) {
-			id_level3 = id + 2000000; /* We punched a hole in the map at 1000. */
-			bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
-		} else {
-			id_level3 = id + 3000000; /* Rest is business as usual. */
-			bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
-		}
-		if (!bret) {
-			log_stderr("failure: check ownership %s", file);
-			goto out;
-		}
-
-		if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid)) {
-			log_stderr("failure: check ownership %s", file);
-			goto out;
-		}
-	}
-
-	/* Verify that ownership looks correct for callers in the first userns. */
-	pid = fork();
-	if (pid < 0) {
-		log_stderr("failure: fork");
-		goto out;
-	}
-	if (pid == 0) {
-		if (!switch_userns(attr_level1.userns_fd, 0, 0, false))
-			die("failure: switch_userns");
-
-		for (id = 0; id <= id_file_range; id++) {
-			bool bret;
-			unsigned int id_level1, id_level2, id_level3;
-			char file[256];
-
-			snprintf(file, sizeof(file), FILE1 "_%u", id);
-
-			id_level1 = id;
-			if (!expected_uid_gid(fd_open_tree_level1, file, 0, id_level1, id_level1))
-				die("failure: check ownership %s", file);
-
-			id_level2 = id + 1000000;
-			if (!expected_uid_gid(fd_open_tree_level2, file, 0, id_level2, id_level2))
-				die("failure: check ownership %s", file);
-
-			if (id == 999) {
-				/* This id is unmapped. */
-				bret = expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid);
-			} else if (id == 1000) {
-				id_level3 = id + 1000000; /* We punched a hole in the map at 1000. */
-				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
-			} else {
-				id_level3 = id + 2000000; /* Rest is business as usual. */
-				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
-			}
-			if (!bret)
-				die("failure: check ownership %s", file);
-
-			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-		}
-
-		exit(EXIT_SUCCESS);
-	}
-	if (wait_for_pid(pid))
-		goto out;
-
-	/* Verify that ownership looks correct for callers in the second userns. */
-	pid = fork();
-	if (pid < 0) {
-		log_stderr("failure: fork");
-		goto out;
-	}
-	if (pid == 0) {
-		if (!switch_userns(attr_level2.userns_fd, 0, 0, false))
-			die("failure: switch_userns");
-
-		for (id = 0; id <= id_file_range; id++) {
-			bool bret;
-			unsigned int id_level2, id_level3;
-			char file[256];
-
-			snprintf(file, sizeof(file), FILE1 "_%u", id);
-
-			if (!expected_uid_gid(fd_open_tree_level1, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-
-			id_level2 = id;
-			if (!expected_uid_gid(fd_open_tree_level2, file, 0, id_level2, id_level2))
-				die("failure: check ownership %s", file);
-
-			if (id == 999) {
-				/* This id is unmapped. */
-				bret = expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid);
-			} else if (id == 1000) {
-				id_level3 = id; /* We punched a hole in the map at 1000. */
-				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
-			} else {
-				id_level3 = id + 1000000; /* Rest is business as usual. */
-				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
-			}
-			if (!bret)
-				die("failure: check ownership %s", file);
-
-			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-		}
-
-		exit(EXIT_SUCCESS);
-	}
-	if (wait_for_pid(pid))
-		goto out;
-
-	/* Verify that ownership looks correct for callers in the third userns. */
-	pid = fork();
-	if (pid < 0) {
-		log_stderr("failure: fork");
-		goto out;
-	}
-	if (pid == 0) {
-		if (!switch_userns(attr_level3.userns_fd, 0, 0, false))
-			die("failure: switch_userns");
-
-		for (id = 0; id <= id_file_range; id++) {
-			bool bret;
-			unsigned int id_level2, id_level3;
-			char file[256];
-
-			snprintf(file, sizeof(file), FILE1 "_%u", id);
-
-			if (!expected_uid_gid(fd_open_tree_level1, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-
-			if (id == 1000) {
-				/*
-				 * The idmapping of the third userns has a hole
-				 * at uid/gid 1000. That means:
-				 * - 1000->userns_0(2000000) // init userns
-				 * - 1000->userns_1(2000000) // level 1
-				 * - 1000->userns_2(1000000) // level 2
-				 * - 1000->userns_3(1000)    // level 3 (because level 3 has a hole)
-				 */
-				id_level2 = id;
-				bret = expected_uid_gid(fd_open_tree_level2, file, 0, id_level2, id_level2);
-			} else {
-				bret = expected_uid_gid(fd_open_tree_level2, file, 0, info->t_overflowuid, info->t_overflowgid);
-			}
-			if (!bret)
-				die("failure: check ownership %s", file);
-
-
-			if (id == 999) {
-				/* This id is unmapped. */
-				bret = expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid);
-			} else {
-				id_level3 = id; /* Rest is business as usual. */
-				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
-			}
-			if (!bret)
-				die("failure: check ownership %s", file);
-
-			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-		}
-
-		exit(EXIT_SUCCESS);
-	}
-	if (wait_for_pid(pid))
-		goto out;
-
-	/* Verify that ownership looks correct for callers in the fourth userns. */
-	pid = fork();
-	if (pid < 0) {
-		log_stderr("failure: fork");
-		goto out;
-	}
-	if (pid == 0) {
-		if (setns(attr_level4.userns_fd, CLONE_NEWUSER))
-			die("failure: switch_userns");
-
-		for (id = 0; id <= id_file_range; id++) {
-			char file[256];
-
-			snprintf(file, sizeof(file), FILE1 "_%u", id);
-
-			if (!expected_uid_gid(fd_open_tree_level1, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-
-			if (!expected_uid_gid(fd_open_tree_level2, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-
-			if (!expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-
-			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-		}
-
-		exit(EXIT_SUCCESS);
-	}
-	if (wait_for_pid(pid))
-		goto out;
-
-	/* Verify that chown works correctly for callers in the first userns. */
-	pid = fork();
-	if (pid < 0) {
-		log_stderr("failure: fork");
-		goto out;
-	}
-	if (pid == 0) {
-		if (!switch_userns(attr_level1.userns_fd, 0, 0, false))
-			die("failure: switch_userns");
-
-		for (id = 0; id <= id_file_range; id++) {
-			bool bret;
-			unsigned int id_level1, id_level2, id_level3, id_new;
-			char file[256];
-
-			snprintf(file, sizeof(file), FILE1 "_%u", id);
-
-			id_new = id + 1;
-			if (fchownat(fd_open_tree_level1, file, id_new, id_new, AT_SYMLINK_NOFOLLOW))
-				die("failure: fchownat %s", file);
-
-			id_level1 = id_new;
-			if (!expected_uid_gid(fd_open_tree_level1, file, 0, id_level1, id_level1))
-				die("failure: check ownership %s", file);
-
-			id_level2 = id_new + 1000000;
-			if (!expected_uid_gid(fd_open_tree_level2, file, 0, id_level2, id_level2))
-				die("failure: check ownership %s", file);
-
-			if (id_new == 999) {
-				/* This id is unmapped. */
-				bret = expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid);
-			} else if (id_new == 1000) {
-				id_level3 = id_new + 1000000; /* We punched a hole in the map at 1000. */
-				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
-			} else {
-				id_level3 = id_new + 2000000; /* Rest is business as usual. */
-				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
-			}
-			if (!bret)
-				die("failure: check ownership %s", file);
-
-			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-
-			/* Revert ownership. */
-			if (fchownat(fd_open_tree_level1, file, id, id, AT_SYMLINK_NOFOLLOW))
-				die("failure: fchownat %s", file);
-		}
-
-		exit(EXIT_SUCCESS);
-	}
-	if (wait_for_pid(pid))
-		goto out;
-
-	/* Verify that chown works correctly for callers in the second userns. */
-	pid = fork();
-	if (pid < 0) {
-		log_stderr("failure: fork");
-		goto out;
-	}
-	if (pid == 0) {
-		if (!switch_userns(attr_level2.userns_fd, 0, 0, false))
-			die("failure: switch_userns");
-
-		for (id = 0; id <= id_file_range; id++) {
-			bool bret;
-			unsigned int id_level2, id_level3, id_new;
-			char file[256];
-
-			snprintf(file, sizeof(file), FILE1 "_%u", id);
-
-			id_new = id + 1;
-			if (fchownat(fd_open_tree_level2, file, id_new, id_new, AT_SYMLINK_NOFOLLOW))
-				die("failure: fchownat %s", file);
-
-			if (!expected_uid_gid(fd_open_tree_level1, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-
-			id_level2 = id_new;
-			if (!expected_uid_gid(fd_open_tree_level2, file, 0, id_level2, id_level2))
-				die("failure: check ownership %s", file);
-
-			if (id_new == 999) {
-				/* This id is unmapped. */
-				bret = expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid);
-			} else if (id_new == 1000) {
-				id_level3 = id_new; /* We punched a hole in the map at 1000. */
-				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
-			} else {
-				id_level3 = id_new + 1000000; /* Rest is business as usual. */
-				bret = expected_uid_gid(fd_open_tree_level3, file, 0, id_level3, id_level3);
-			}
-			if (!bret)
-				die("failure: check ownership %s", file);
-
-			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-
-			/* Revert ownership. */
-			if (fchownat(fd_open_tree_level2, file, id, id, AT_SYMLINK_NOFOLLOW))
-				die("failure: fchownat %s", file);
-		}
-
-		exit(EXIT_SUCCESS);
-	}
-	if (wait_for_pid(pid))
-		goto out;
-
-	/* Verify that chown works correctly for callers in the third userns. */
-	pid = fork();
-	if (pid < 0) {
-		log_stderr("failure: fork");
-		goto out;
-	}
-	if (pid == 0) {
-		if (!switch_userns(attr_level3.userns_fd, 0, 0, false))
-			die("failure: switch_userns");
-
-		for (id = 0; id <= id_file_range; id++) {
-			unsigned int id_new;
-			char file[256];
-
-			snprintf(file, sizeof(file), FILE1 "_%u", id);
-
-			id_new = id + 1;
-			if (id_new == 999 || id_new == 1000) {
-				/*
-				 * We can't change ownership as we can't
-				 * chown from or to an unmapped id.
-				 */
-				if (!fchownat(fd_open_tree_level3, file, id_new, id_new, AT_SYMLINK_NOFOLLOW))
-					die("failure: fchownat %s", file);
-			} else {
-				if (fchownat(fd_open_tree_level3, file, id_new, id_new, AT_SYMLINK_NOFOLLOW))
-					die("failure: fchownat %s", file);
-			}
-
-			if (!expected_uid_gid(fd_open_tree_level1, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-
-			/* There's no id 1000 anymore as we changed ownership for id 1000 to 1001 above. */
-			if (!expected_uid_gid(fd_open_tree_level2, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-
-			if (id_new == 999) {
-				/*
-				 * We did not change ownership as we can't
-				 * chown to an unmapped id.
-				 */
-				if (!expected_uid_gid(fd_open_tree_level3, file, 0, id, id))
-					die("failure: check ownership %s", file);
-			} else if (id_new == 1000) {
-				/*
-				 * We did not change ownership as we can't
-				 * chown from an unmapped id.
-				 */
-				if (!expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid))
-					die("failure: check ownership %s", file);
-			} else {
-				if (!expected_uid_gid(fd_open_tree_level3, file, 0, id_new, id_new))
-					die("failure: check ownership %s", file);
-			}
-
-			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-
-			/* Revert ownership. */
-			if (id_new != 999 && id_new != 1000) {
-				if (fchownat(fd_open_tree_level3, file, id, id, AT_SYMLINK_NOFOLLOW))
-					die("failure: fchownat %s", file);
-			}
-		}
-
-		exit(EXIT_SUCCESS);
-	}
-	if (wait_for_pid(pid))
-		goto out;
-
-	/* Verify that chown works correctly for callers in the fourth userns. */
-	pid = fork();
-	if (pid < 0) {
-		log_stderr("failure: fork");
-		goto out;
-	}
-	if (pid == 0) {
-		if (setns(attr_level4.userns_fd, CLONE_NEWUSER))
-			die("failure: switch_userns");
-
-		for (id = 0; id <= id_file_range; id++) {
-			char file[256];
-			unsigned long id_new;
-
-			snprintf(file, sizeof(file), FILE1 "_%u", id);
-
-			id_new = id + 1;
-			if (!fchownat(fd_open_tree_level4, file, id_new, id_new, AT_SYMLINK_NOFOLLOW))
-				die("failure: fchownat %s", file);
-
-			if (!expected_uid_gid(fd_open_tree_level1, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-
-			if (!expected_uid_gid(fd_open_tree_level2, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-
-			if (!expected_uid_gid(fd_open_tree_level3, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-
-			if (!expected_uid_gid(fd_open_tree_level4, file, 0, info->t_overflowuid, info->t_overflowgid))
-				die("failure: check ownership %s", file);
-
-		}
-
-		exit(EXIT_SUCCESS);
-	}
-	if (wait_for_pid(pid))
-		goto out;
-
-	fret = 0;
-	log_debug("Ran test");
-
-out:
-	list_for_each_safe(it, &hierarchy[0].id_map, next) {
-		list_del(it);
-		free(it->elem);
-		free(it);
-	}
-
-	list_for_each_safe(it, &hierarchy[1].id_map, next) {
-		list_del(it);
-		free(it->elem);
-		free(it);
-	}
-
-	list_for_each_safe(it, &hierarchy[2].id_map, next) {
-		list_del(it);
-		free(it->elem);
-		free(it);
-	}
-
-	safe_close(hierarchy[0].fd_userns);
-	safe_close(hierarchy[1].fd_userns);
-	safe_close(hierarchy[2].fd_userns);
-	safe_close(fd_dir1);
-	safe_close(fd_open_tree_level1);
-	safe_close(fd_open_tree_level2);
-	safe_close(fd_open_tree_level3);
-	safe_close(fd_open_tree_level4);
-	return fret;
-}
-
-#define USER1 "fsgqa"
-#define USER2 "fsgqa2"
-
-/**
- * lookup_ids - lookup uid and gid for a username
- * @name: [in]  name of the user
- * @uid:  [out] pointer to the user-ID
- * @gid:  [out] pointer to the group-ID
- *
- * Lookup the uid and gid of a user.
- *
- * Return: On success, true is returned.
- *         On error, false is returned.
- */
-static bool lookup_ids(const char *name, uid_t *uid, gid_t *gid)
-{
-	bool bret = false;
-	struct passwd *pwentp = NULL;
-	struct passwd pwent;
-	char *buf;
-	ssize_t bufsize;
-	int ret;
-
-	bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
-	if (bufsize < 0)
-		bufsize = 1024;
-
-	buf = malloc(bufsize);
-	if (!buf)
-		return bret;
-
-	ret = getpwnam_r(name, &pwent, buf, bufsize, &pwentp);
-	if (!ret && pwentp) {
-		*uid = pwent.pw_uid;
-		*gid = pwent.pw_gid;
-		bret = true;
-	}
-
-	free(buf);
-	return bret;
-}
-
-/**
- * setattr_fix_968219708108 - test for commit 968219708108 ("fs: handle circular mappings correctly")
- *
- * Test that ->setattr() works correctly for idmapped mounts with circular
- * idmappings such as:
- *
- * b:1000:1001:1
- * b:1001:1000:1
- *
- * Assume a directory /source with two files:
- *
- * /source/file1 | 1000:1000
- * /source/file2 | 1001:1001
- *
- * and we create an idmapped mount of /source at /target with an idmapped of:
- *
- * mnt_userns:        1000:1001:1
- *                    1001:1000:1
- *
- * In the idmapped mount file1 will be owned by uid 1001 and file2 by uid 1000:
- *
- * /target/file1 | 1001:1001
- * /target/file2 | 1000:1000
- *
- * Because in essence the idmapped mount switches ownership for {g,u}id 1000
- * and {g,u}id 1001.
- *
- * 1. A user with fs{g,u}id 1000 must be allowed to setattr /target/file2 from
- *    {g,u}id 1000 in the idmapped mount to {g,u}id 1000.
- * 2. A user with fs{g,u}id 1001 must be allowed to setattr /target/file1 from
- *    {g,u}id 1001 in the idmapped mount to {g,u}id 1001.
- * 3. A user with fs{g,u}id 1000 must fail to setattr /target/file1 from
- *    {g,u}id 1001 in the idmapped mount to {g,u}id 1000.
- *    This must fail with EPERM. The caller's fs{g,u}id doesn't match the
- *    {g,u}id of the file.
- * 4. A user with fs{g,u}id 1001 must fail to setattr /target/file2 from
- *    {g,u}id 1000 in the idmapped mount to {g,u}id 1000.
- *    This must fail with EPERM. The caller's fs{g,u}id doesn't match the
- *    {g,u}id of the file.
- * 5. Both, a user with fs{g,u}id 1000 and a user with fs{g,u}id 1001, must
- *    fail to setattr /target/file1 owned by {g,u}id 1001 in the idmapped mount
- *    and /target/file2 owned by {g,u}id 1000 in the idmapped mount to any
- *    {g,u}id apart from {g,u}id 1000 or 1001 with EINVAL.
- *    Only {g,u}id 1000 and 1001 have a mapping in the idmapped mount. Other
- *    {g,u}id are unmapped.
- */
-static int setattr_fix_968219708108(const struct vfstest_info *info)
-{
-	int fret = -1;
-	int open_tree_fd = -EBADF;
-	struct mount_attr attr = {
-		.attr_set	= MOUNT_ATTR_IDMAP,
-		.userns_fd	= -EBADF,
-	};
-	int ret;
-	uid_t user1_uid, user2_uid;
-	gid_t user1_gid, user2_gid;
-	pid_t pid;
-	struct list idmap;
-	struct list *it_cur, *it_next;
-
-	if (!caps_supported())
-		return 0;
-
-	list_init(&idmap);
-
-	if (!lookup_ids(USER1, &user1_uid, &user1_gid)) {
-		log_stderr("failure: lookup_user");
-		goto out;
-	}
-
-	if (!lookup_ids(USER2, &user2_uid, &user2_gid)) {
-		log_stderr("failure: lookup_user");
-		goto out;
-	}
-
-	log_debug("Found " USER1 " with uid(%d) and gid(%d) and " USER2 " with uid(%d) and gid(%d)",
-		  user1_uid, user1_gid, user2_uid, user2_gid);
-
-	if (mkdirat(info->t_dir1_fd, DIR1, 0777)) {
-		log_stderr("failure: mkdirat");
-		goto out;
-	}
-
-	if (mknodat(info->t_dir1_fd, DIR1 "/" FILE1, S_IFREG | 0644, 0)) {
-		log_stderr("failure: mknodat");
-		goto out;
-	}
-
-	if (chown_r(info->t_mnt_fd, T_DIR1, user1_uid, user1_gid)) {
-		log_stderr("failure: chown_r");
-		goto out;
-	}
-
-	if (mknodat(info->t_dir1_fd, DIR1 "/" FILE2, S_IFREG | 0644, 0)) {
-		log_stderr("failure: mknodat");
-		goto out;
-	}
-
-	if (fchownat(info->t_dir1_fd, DIR1 "/" FILE2, user2_uid, user2_gid, AT_SYMLINK_NOFOLLOW)) {
-		log_stderr("failure: fchownat");
-		goto out;
-	}
-
-	print_r(info->t_mnt_fd, T_DIR1);
-
-	/* u:1000:1001:1 */
-	ret = add_map_entry(&idmap, user1_uid, user2_uid, 1, ID_TYPE_UID);
-	if (ret) {
-		log_stderr("failure: add_map_entry");
-		goto out;
-	}
-
-	/* u:1001:1000:1 */
-	ret = add_map_entry(&idmap, user2_uid, user1_uid, 1, ID_TYPE_UID);
-	if (ret) {
-		log_stderr("failure: add_map_entry");
-		goto out;
-	}
-
-	/* g:1000:1001:1 */
-	ret = add_map_entry(&idmap, user1_gid, user2_gid, 1, ID_TYPE_GID);
-	if (ret) {
-		log_stderr("failure: add_map_entry");
-		goto out;
-	}
-
-	/* g:1001:1000:1 */
-	ret = add_map_entry(&idmap, user2_gid, user1_gid, 1, ID_TYPE_GID);
-	if (ret) {
-		log_stderr("failure: add_map_entry");
-		goto out;
-	}
-
-	attr.userns_fd = get_userns_fd_from_idmap(&idmap);
-	if (attr.userns_fd < 0) {
-		log_stderr("failure: get_userns_fd");
-		goto out;
-	}
-
-	open_tree_fd = sys_open_tree(info->t_dir1_fd, DIR1,
-				     AT_NO_AUTOMOUNT |
-				     AT_SYMLINK_NOFOLLOW |
-				     OPEN_TREE_CLOEXEC |
-				     OPEN_TREE_CLONE |
-				     AT_RECURSIVE);
-	if (open_tree_fd < 0) {
-		log_stderr("failure: sys_open_tree");
-		goto out;
-	}
-
-	if (sys_mount_setattr(open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) {
-		log_stderr("failure: sys_mount_setattr");
-		goto out;
-	}
-
-	print_r(open_tree_fd, "");
-
-	pid = fork();
-	if (pid < 0) {
-		log_stderr("failure: fork");
-		goto out;
-	}
-	if (pid == 0) {
-		/* switch to {g,u}id 1001 */
-		if (!switch_resids(user2_uid, user2_gid))
-			die("failure: switch_resids");
-
-		/* drop all capabilities */
-		if (!caps_down())
-			die("failure: caps_down");
-
-		/*
-		 * The {g,u}id 0 is not mapped in this idmapped mount so this
-		 * needs to fail with EINVAL.
-		 */
-		if (!fchownat(open_tree_fd, FILE1, 0, 0, AT_SYMLINK_NOFOLLOW))
-			die("failure: change ownership");
-		if (errno != EINVAL)
-			die("failure: errno");
-
-		/*
-		 * A user with fs{g,u}id 1001 must be allowed to change
-		 * ownership of /target/file1 owned by {g,u}id 1001 in this
-		 * idmapped mount to {g,u}id 1001.
-		 */
-		if (fchownat(open_tree_fd, FILE1, user2_uid, user2_gid,
-			     AT_SYMLINK_NOFOLLOW))
-			die("failure: change ownership");
-
-		/* Verify that the ownership is still {g,u}id 1001. */
-		if (!expected_uid_gid(open_tree_fd, FILE1, AT_SYMLINK_NOFOLLOW,
-				      user2_uid, user2_gid))
-			die("failure: check ownership");
-
-		/*
-		 * A user with fs{g,u}id 1001 must not be allowed to change
-		 * ownership of /target/file1 owned by {g,u}id 1001 in this
-		 * idmapped mount to {g,u}id 1000.
-		 */
-		if (!fchownat(open_tree_fd, FILE1, user1_uid, user1_gid,
-			      AT_SYMLINK_NOFOLLOW))
-			die("failure: change ownership");
-		if (errno != EPERM)
-			die("failure: errno");
-
-		/* Verify that the ownership is still {g,u}id 1001. */
-		if (!expected_uid_gid(open_tree_fd, FILE1, AT_SYMLINK_NOFOLLOW,
-				      user2_uid, user2_gid))
-			die("failure: check ownership");
-
-		/*
-		 * A user with fs{g,u}id 1001 must not be allowed to change
-		 * ownership of /target/file2 owned by {g,u}id 1000 in this
-		 * idmapped mount to {g,u}id 1000.
-		 */
-		if (!fchownat(open_tree_fd, FILE2, user1_uid, user1_gid,
-			      AT_SYMLINK_NOFOLLOW))
-			die("failure: change ownership");
-		if (errno != EPERM)
-			die("failure: errno");
-
-		/* Verify that the ownership is still {g,u}id 1000. */
-		if (!expected_uid_gid(open_tree_fd, FILE2, AT_SYMLINK_NOFOLLOW,
-				      user1_uid, user1_gid))
-			die("failure: check ownership");
-
-		/*
-		 * A user with fs{g,u}id 1001 must not be allowed to change
-		 * ownership of /target/file2 owned by {g,u}id 1000 in this
-		 * idmapped mount to {g,u}id 1001.
-		 */
-		if (!fchownat(open_tree_fd, FILE2, user2_uid, user2_gid,
-			      AT_SYMLINK_NOFOLLOW))
-			die("failure: change ownership");
-		if (errno != EPERM)
-			die("failure: errno");
-
-		/* Verify that the ownership is still {g,u}id 1000. */
-		if (!expected_uid_gid(open_tree_fd, FILE2, AT_SYMLINK_NOFOLLOW,
-				      user1_uid, user1_gid))
-			die("failure: check ownership");
-
-		exit(EXIT_SUCCESS);
-	}
-	if (wait_for_pid(pid))
-		goto out;
-
-	pid = fork();
-	if (pid < 0) {
-		log_stderr("failure: fork");
-		goto out;
-	}
-	if (pid == 0) {
-		/* switch to {g,u}id 1000 */
-		if (!switch_resids(user1_uid, user1_gid))
-			die("failure: switch_resids");
-
-		/* drop all capabilities */
-		if (!caps_down())
-			die("failure: caps_down");
-
-		/*
-		 * The {g,u}id 0 is not mapped in this idmapped mount so this
-		 * needs to fail with EINVAL.
-		 */
-		if (!fchownat(open_tree_fd, FILE1, 0, 0, AT_SYMLINK_NOFOLLOW))
-			die("failure: change ownership");
-		if (errno != EINVAL)
-			die("failure: errno");
-
-		/*
-		 * A user with fs{g,u}id 1000 must be allowed to change
-		 * ownership of /target/file2 owned by {g,u}id 1000 in this
-		 * idmapped mount to {g,u}id 1000.
-		 */
-		if (fchownat(open_tree_fd, FILE2, user1_uid, user1_gid,
-			     AT_SYMLINK_NOFOLLOW))
-			die("failure: change ownership");
-
-		/* Verify that the ownership is still {g,u}id 1000. */
-		if (!expected_uid_gid(open_tree_fd, FILE2, AT_SYMLINK_NOFOLLOW,
-				      user1_uid, user1_gid))
-			die("failure: check ownership");
-
-		/*
-		 * A user with fs{g,u}id 1000 must not be allowed to change
-		 * ownership of /target/file2 owned by {g,u}id 1000 in this
-		 * idmapped mount to {g,u}id 1001.
-		 */
-		if (!fchownat(open_tree_fd, FILE2, user2_uid, user2_gid,
-			      AT_SYMLINK_NOFOLLOW))
-			die("failure: change ownership");
-		if (errno != EPERM)
-			die("failure: errno");
-
-		/* Verify that the ownership is still {g,u}id 1000. */
-		if (!expected_uid_gid(open_tree_fd, FILE2, AT_SYMLINK_NOFOLLOW,
-				      user1_uid, user1_gid))
-			die("failure: check ownership");
-
-		/*
-		 * A user with fs{g,u}id 1000 must not be allowed to change
-		 * ownership of /target/file1 owned by {g,u}id 1001 in this
-		 * idmapped mount to {g,u}id 1000.
-		 */
-		if (!fchownat(open_tree_fd, FILE1, user1_uid, user1_gid,
-			     AT_SYMLINK_NOFOLLOW))
-			die("failure: change ownership");
-		if (errno != EPERM)
-			die("failure: errno");
-
-		/* Verify that the ownership is still {g,u}id 1001. */
-		if (!expected_uid_gid(open_tree_fd, FILE1, AT_SYMLINK_NOFOLLOW,
-				      user2_uid, user2_gid))
-			die("failure: check ownership");
-
-		/*
-		 * A user with fs{g,u}id 1000 must not be allowed to change
-		 * ownership of /target/file1 owned by {g,u}id 1001 in this
-		 * idmapped mount to {g,u}id 1001.
-		 */
-		if (!fchownat(open_tree_fd, FILE1, user2_uid, user2_gid,
-			      AT_SYMLINK_NOFOLLOW))
-			die("failure: change ownership");
-		if (errno != EPERM)
-			die("failure: errno");
-
-		/* Verify that the ownership is still {g,u}id 1001. */
-		if (!expected_uid_gid(open_tree_fd, FILE1, AT_SYMLINK_NOFOLLOW,
-				      user2_uid, user2_gid))
-			die("failure: check ownership");
-
-		exit(EXIT_SUCCESS);
-	}
-	if (wait_for_pid(pid))
-		goto out;
-
-	fret = 0;
-	log_debug("Ran test");
-out:
-	safe_close(attr.userns_fd);
-	safe_close(open_tree_fd);
-
-	list_for_each_safe(it_cur, &idmap, it_next) {
-		list_del(it_cur);
-		free(it_cur->elem);
-		free(it_cur);
-	}
-
-	return fret;
-}
-
-/**
- * setxattr_fix_705191b03d50 - test for commit 705191b03d50 ("fs: fix acl translation").
- */
-static int setxattr_fix_705191b03d50(const struct vfstest_info *info)
-{
-	int fret = -1;
-	int fd_userns = -EBADF;
-	int ret;
-	uid_t user1_uid;
-	gid_t user1_gid;
-	pid_t pid;
-	struct list idmap;
-	struct list *it_cur, *it_next;
-
-	list_init(&idmap);
-
-	if (!lookup_ids(USER1, &user1_uid, &user1_gid)) {
-		log_stderr("failure: lookup_user");
-		goto out;
-	}
-
-	log_debug("Found " USER1 " with uid(%d) and gid(%d)", user1_uid, user1_gid);
-
-	if (mkdirat(info->t_dir1_fd, DIR1, 0777)) {
-		log_stderr("failure: mkdirat");
-		goto out;
-	}
-
-	if (chown_r(info->t_mnt_fd, T_DIR1, user1_uid, user1_gid)) {
-		log_stderr("failure: chown_r");
-		goto out;
-	}
-
-	print_r(info->t_mnt_fd, T_DIR1);
-
-	/* u:0:user1_uid:1 */
-	ret = add_map_entry(&idmap, user1_uid, 0, 1, ID_TYPE_UID);
-	if (ret) {
-		log_stderr("failure: add_map_entry");
-		goto out;
-	}
-
-	/* g:0:user1_gid:1 */
-	ret = add_map_entry(&idmap, user1_gid, 0, 1, ID_TYPE_GID);
-	if (ret) {
-		log_stderr("failure: add_map_entry");
-		goto out;
-	}
-
-	/* u:100:10000:100 */
-	ret = add_map_entry(&idmap, 10000, 100, 100, ID_TYPE_UID);
-	if (ret) {
-		log_stderr("failure: add_map_entry");
-		goto out;
-	}
-
-	/* g:100:10000:100 */
-	ret = add_map_entry(&idmap, 10000, 100, 100, ID_TYPE_GID);
-	if (ret) {
-		log_stderr("failure: add_map_entry");
-		goto out;
-	}
-
-	fd_userns = get_userns_fd_from_idmap(&idmap);
-	if (fd_userns < 0) {
-		log_stderr("failure: get_userns_fd");
-		goto out;
-	}
-
-	pid = fork();
-	if (pid < 0) {
-		log_stderr("failure: fork");
-		goto out;
-	}
-	if (pid == 0) {
-		if (!switch_userns(fd_userns, 0, 0, false))
-			die("failure: switch_userns");
-
-		/* create separate mount namespace */
-		if (unshare(CLONE_NEWNS))
-			die("failure: create new mount namespace");
-
-		/* turn off mount propagation */
-		if (sys_mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0))
-			die("failure: turn mount propagation off");
-
-		snprintf(t_buf, sizeof(t_buf), "%s/%s/%s", info->t_mountpoint, T_DIR1, DIR1);
-
-		if (sys_mount("none", t_buf, "tmpfs", 0, "mode=0755"))
-			die("failure: mount");
-
-		snprintf(t_buf, sizeof(t_buf), "%s/%s/%s/%s", info->t_mountpoint, T_DIR1, DIR1, DIR3);
-		if (mkdir(t_buf, 0700))
-			die("failure: mkdir");
-
-		snprintf(t_buf, sizeof(t_buf), "setfacl -m u:100:rwx %s/%s/%s/%s", info->t_mountpoint, T_DIR1, DIR1, DIR3);
-		if (system(t_buf))
-			die("failure: system");
-
-		snprintf(t_buf, sizeof(t_buf), "getfacl -n -p %s/%s/%s/%s | grep -q user:100:rwx", info->t_mountpoint, T_DIR1, DIR1, DIR3);
-		if (system(t_buf))
-			die("failure: system");
-
-		exit(EXIT_SUCCESS);
-	}
-	if (wait_for_pid(pid))
-		goto out;
-
-	fret = 0;
-	log_debug("Ran test");
-out:
-	safe_close(fd_userns);
-
-	list_for_each_safe(it_cur, &idmap, it_next) {
-		list_del(it_cur);
-		free(it_cur->elem);
-		free(it_cur);
-	}
-
-	return fret;
-}
-
 static void usage(void)
 {
 	fprintf(stderr, "Description:\n");
@@ -3162,7 +1799,7 @@ static void usage(void)
 	fprintf(stderr, "--fstype                            Filesystem type used in the tests\n");
 	fprintf(stderr, "--help                              Print help\n");
 	fprintf(stderr, "--mountpoint                        Mountpoint of device\n");
-	fprintf(stderr, "--idmapped-mounts-supported	     Test whether idmapped mounts are supported on this filesystem\n");
+	fprintf(stderr, "--idmapped-mounts-supported         Test whether idmapped mounts are supported on this filesystem\n");
 	fprintf(stderr, "--scratch-mountpoint                Mountpoint of scratch device used in the tests\n");
 	fprintf(stderr, "--scratch-device                    Scratch device used in the tests\n");
 	fprintf(stderr, "--test-core                         Run core idmapped mount testsuite\n");
@@ -3214,35 +1851,6 @@ static const struct test_suite s_basic = {
 	.nr_tests = ARRAY_SIZE(t_basic),
 };
 
-static const struct test_struct t_nested_userns[] = {
-	{ nested_userns,						T_REQUIRE_IDMAPPED_MOUNTS,	"test that nested user namespaces behave correctly when attached to idmapped mounts",		},
-};
-
-static const struct test_suite s_nested_userns = {
-	.tests = t_nested_userns,
-	.nr_tests = ARRAY_SIZE(t_nested_userns),
-};
-
-/* Test for commit 968219708108 ("fs: handle circular mappings correctly"). */
-static const struct test_struct t_setattr_fix_968219708108[] = {
-	{ setattr_fix_968219708108,					T_REQUIRE_IDMAPPED_MOUNTS,	"test that setattr works correctly",								},
-};
-
-static const struct test_suite s_setattr_fix_968219708108 = {
-	.tests = t_setattr_fix_968219708108,
-	.nr_tests = ARRAY_SIZE(t_setattr_fix_968219708108),
-};
-
-/* Test for commit 705191b03d50 ("fs: fix acl translation"). */
-static const struct test_struct t_setxattr_fix_705191b03d50[] = {
-	{ setxattr_fix_705191b03d50,					T_REQUIRE_USERNS,	"test that setxattr works correctly for userns mountable filesystems",				},
-};
-
-static const struct test_suite s_setxattr_fix_705191b03d50 = {
-	.tests = t_setxattr_fix_705191b03d50,
-	.nr_tests = ARRAY_SIZE(t_setxattr_fix_705191b03d50),
-};
-
 static bool run_test(struct vfstest_info *info, const struct test_struct suite[], size_t suite_size)
 {
 	int i;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v2 13/13] vfs/idmapped-mounts: remove invalid test
  2022-05-12 16:52 [PATCH v2 00/13] rename & split tests Christian Brauner
                   ` (8 preceding siblings ...)
  2022-05-12 16:52 ` [PATCH v2 12/13] vfstest: split out remaining idmapped mount tests Christian Brauner
@ 2022-05-12 16:52 ` Christian Brauner
  2022-05-16  5:53   ` Christoph Hellwig
  2022-05-12 20:19 ` [PATCH v2 00/13] rename & split tests Zorro Lang
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 24+ messages in thread
From: Christian Brauner @ 2022-05-12 16:52 UTC (permalink / raw)
  To: Zorro Lang, fstests
  Cc: Christian Brauner, Dave Chinner, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong

The threaded mount interaction test validates that the idmapping of a
mount behaves correctly when changed while another process already has a
writable file descriptor to that mount. In newer kernels it isn't
possible to change a mount's idmapped if someone has a writable file
descriptor to that mount.

Link: e1bbcd277a53 ("fs: hold writers when changing mount's idmapping")
Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org>
---
 src/vfs/idmapped-mounts.c | 241 --------------------------------------
 src/vfs/vfstest.c         |   1 -
 2 files changed, 242 deletions(-)

diff --git a/src/vfs/idmapped-mounts.c b/src/vfs/idmapped-mounts.c
index d3763c2d..63297d5f 100644
--- a/src/vfs/idmapped-mounts.c
+++ b/src/vfs/idmapped-mounts.c
@@ -6438,246 +6438,6 @@ out:
 	return fret;
 }
 
-#define PTR_TO_INT(p) ((int)((intptr_t)(p)))
-#define INT_TO_PTR(u) ((void *)((intptr_t)(u)))
-
-struct threaded_args {
-	const struct vfstest_info *info;
-	int open_tree_fd;
-};
-
-static void *idmapped_mount_create_cb(void *data)
-{
-	int fret = EXIT_FAILURE;
-	struct mount_attr attr = {
-		.attr_set = MOUNT_ATTR_IDMAP,
-	};
-	struct threaded_args *args = data;
-
-	/* Changing mount properties on a detached mount. */
-	attr.userns_fd	= get_userns_fd(0, 10000, 10000);
-	if (attr.userns_fd < 0) {
-		log_stderr("failure: get_userns_fd");
-		goto out;
-	}
-
-	if (sys_mount_setattr(args->open_tree_fd, "", AT_EMPTY_PATH, &attr, sizeof(attr))) {
-		log_stderr("failure: sys_mount_setattr");
-		goto out;
-	}
-
-	fret = EXIT_SUCCESS;
-
-out:
-	safe_close(attr.userns_fd);
-	pthread_exit(INT_TO_PTR(fret));
-}
-
-/* This tries to verify that we never see an inconistent ownership on-disk and
- * can't write invalid ids to disk. To do this we create a race between
- * idmapping a mount and creating files on it.
- * Note, while it is perfectly fine to see overflowuid and overflowgid as owner
- * if we create files through the open_tree_fd before the mount is idmapped but
- * look at the files after the mount has been idmapped in this test it can never
- * be the case that we see overflowuid and overflowgid when we access the file
- * through a non-idmapped mount (in the initial user namespace).
- */
-static void *idmapped_mount_operations_cb(void *data)
-{
-	int file1_fd = -EBADF, file2_fd = -EBADF, dir1_fd = -EBADF,
-	    dir1_fd2 = -EBADF, fret = EXIT_FAILURE;
-	struct threaded_args *args = data;
-	const struct vfstest_info *info = args->info;
-
-	if (!switch_fsids(10000, 10000)) {
-		log_stderr("failure: switch fsids");
-		goto out;
-	}
-
-	file1_fd = openat(args->open_tree_fd, FILE1,
-			  O_CREAT | O_EXCL | O_CLOEXEC, 0644);
-	if (file1_fd < 0) {
-		log_stderr("failure: openat");
-		goto out;
-	}
-
-	file2_fd = openat(args->open_tree_fd, FILE2,
-			  O_CREAT | O_EXCL | O_CLOEXEC, 0644);
-	if (file2_fd < 0) {
-		log_stderr("failure: openat");
-		goto out;
-	}
-
-	if (mkdirat(args->open_tree_fd, DIR1, 0777)) {
-		log_stderr("failure: mkdirat");
-		goto out;
-	}
-
-	dir1_fd = openat(args->open_tree_fd, DIR1,
-			 O_RDONLY | O_DIRECTORY | O_CLOEXEC);
-	if (dir1_fd < 0) {
-		log_stderr("failure: openat");
-		goto out;
-	}
-
-	if (!__expected_uid_gid(args->open_tree_fd, FILE1, 0, 0, 0, false) &&
-	    !__expected_uid_gid(args->open_tree_fd, FILE1, 0, 10000, 10000, false) &&
-	    !__expected_uid_gid(args->open_tree_fd, FILE1, 0, info->t_overflowuid, info->t_overflowgid, false)) {
-		log_stderr("failure: expected_uid_gid");
-		goto out;
-	}
-
-	if (!__expected_uid_gid(args->open_tree_fd, FILE2, 0, 0, 0, false) &&
-	    !__expected_uid_gid(args->open_tree_fd, FILE2, 0, 10000, 10000, false) &&
-	    !__expected_uid_gid(args->open_tree_fd, FILE2, 0, info->t_overflowuid, info->t_overflowgid, false)) {
-		log_stderr("failure: expected_uid_gid");
-		goto out;
-	}
-
-	if (!__expected_uid_gid(args->open_tree_fd, DIR1, 0, 0, 0, false) &&
-	    !__expected_uid_gid(args->open_tree_fd, DIR1, 0, 10000, 10000, false) &&
-	    !__expected_uid_gid(args->open_tree_fd, DIR1, 0, info->t_overflowuid, info->t_overflowgid, false)) {
-		log_stderr("failure: expected_uid_gid");
-		goto out;
-	}
-
-	if (!__expected_uid_gid(dir1_fd, "", AT_EMPTY_PATH, 0, 0, false) &&
-	    !__expected_uid_gid(dir1_fd, "", AT_EMPTY_PATH, 10000, 10000, false) &&
-	    !__expected_uid_gid(dir1_fd, "", AT_EMPTY_PATH, info->t_overflowuid, info->t_overflowgid, false)) {
-		log_stderr("failure: expected_uid_gid");
-		goto out;
-	}
-
-	dir1_fd2 = openat(info->t_dir1_fd, DIR1,
-			 O_RDONLY | O_DIRECTORY | O_CLOEXEC);
-	if (dir1_fd2 < 0) {
-		log_stderr("failure: openat");
-		goto out;
-	}
-
-        if (!__expected_uid_gid(info->t_dir1_fd, FILE1, 0, 0, 0, false) &&
-	    !__expected_uid_gid(info->t_dir1_fd, FILE1, 0, 10000, 10000, false)) {
-		log_stderr("failure: expected_uid_gid");
-		goto out;
-	}
-
-	if (!__expected_uid_gid(info->t_dir1_fd, FILE2, 0, 0, 0, false) &&
-	    !__expected_uid_gid(info->t_dir1_fd, FILE2, 0, 10000, 10000, false)) {
-		log_stderr("failure: expected_uid_gid");
-		goto out;
-	}
-
-	if (!__expected_uid_gid(info->t_dir1_fd, DIR1, 0, 0, 0, false) &&
-	    !__expected_uid_gid(info->t_dir1_fd, DIR1, 0, 10000, 10000, false)) {
-		log_stderr("failure: expected_uid_gid");
-		goto out;
-	}
-
-	if (!__expected_uid_gid(info->t_dir1_fd, DIR1, 0, 0, 0, false) &&
-	    !__expected_uid_gid(info->t_dir1_fd, DIR1, 0, 10000, 10000, false)) {
-		log_stderr("failure: expected_uid_gid");
-		goto out;
-	}
-
-	if (!__expected_uid_gid(dir1_fd2, "", AT_EMPTY_PATH, 0, 0, false) &&
-	    !__expected_uid_gid(dir1_fd2, "", AT_EMPTY_PATH, 10000, 10000, false)) {
-		log_stderr("failure: expected_uid_gid");
-		goto out;
-	}
-
-	fret = EXIT_SUCCESS;
-
-out:
-	safe_close(file1_fd);
-	safe_close(file2_fd);
-	safe_close(dir1_fd);
-	safe_close(dir1_fd2);
-
-	pthread_exit(INT_TO_PTR(fret));
-}
-
-static int threaded_idmapped_mount_interactions(const struct vfstest_info *info)
-{
-	int i;
-	int fret = -1;
-	pid_t pid;
-	pthread_attr_t thread_attr;
-	pthread_t threads[2];
-
-	pthread_attr_init(&thread_attr);
-
-	for (i = 0; i < 1000; i++) {
-		int ret1 = 0, ret2 = 0, tret1 = 0, tret2 = 0;
-
-		pid = fork();
-		if (pid < 0) {
-			log_stderr("failure: fork");
-			goto out;
-		}
-		if (pid == 0) {
-			int open_tree_fd = -EBADF;
-			struct threaded_args args = {
-				.info = info,
-				.open_tree_fd = -EBADF,
-			};
-
-			open_tree_fd = sys_open_tree(info->t_dir1_fd, "",
-						     AT_EMPTY_PATH |
-						     AT_NO_AUTOMOUNT |
-						     AT_SYMLINK_NOFOLLOW |
-						     OPEN_TREE_CLOEXEC |
-						     OPEN_TREE_CLONE);
-			if (open_tree_fd < 0)
-				die("failure: sys_open_tree");
-
-			args.open_tree_fd = open_tree_fd;
-
-			if (pthread_create(&threads[0], &thread_attr,
-					   idmapped_mount_create_cb,
-					   &args))
-				die("failure: pthread_create");
-
-			if (pthread_create(&threads[1], &thread_attr,
-					   idmapped_mount_operations_cb,
-					   &args))
-				die("failure: pthread_create");
-
-			ret1 = pthread_join(threads[0], INT_TO_PTR(tret1));
-			ret2 = pthread_join(threads[1], INT_TO_PTR(tret2));
-
-			if (ret1) {
-				errno = ret1;
-				die("failure: pthread_join");
-			}
-
-			if (ret2) {
-				errno = ret2;
-				die("failure: pthread_join");
-			}
-
-			if (tret1 || tret2)
-				exit(EXIT_FAILURE);
-
-			exit(EXIT_SUCCESS);
-
-		}
-
-		if (wait_for_pid(pid)) {
-			log_stderr("failure: iteration %d", i);
-			goto out;
-		}
-
-		rm_r(info->t_dir1_fd, ".");
-
-	}
-
-	fret = 0;
-	log_debug("Ran test");
-
-out:
-	return fret;
-}
-
 static int nested_userns(const struct vfstest_info *info)
 {
 	int fret = -1;
@@ -7941,7 +7701,6 @@ static const struct test_struct t_idmapped_mounts[] = {
 	{ sticky_bit_rename_idmapped_mounts_in_userns,			true,	"sticky bit rename operations on idmapped mounts in user namespace",				},
 	{ symlink_idmapped_mounts,					true,	"symlink from idmapped mounts",									},
 	{ symlink_idmapped_mounts_in_userns,				true,	"symlink from idmapped mounts in user namespace",						},
-	{ threaded_idmapped_mount_interactions,				true,	"threaded operations on idmapped mounts",							},
 };
 
 const struct test_suite s_idmapped_mounts = {
diff --git a/src/vfs/vfstest.c b/src/vfs/vfstest.c
index 67aa3bda..29ac0bec 100644
--- a/src/vfs/vfstest.c
+++ b/src/vfs/vfstest.c
@@ -13,7 +13,6 @@
 #include <limits.h>
 #include <linux/limits.h>
 #include <linux/types.h>
-#include <pthread.h>
 #include <pwd.h>
 #include <sched.h>
 #include <stdbool.h>
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 00/13] rename & split tests
  2022-05-12 16:52 [PATCH v2 00/13] rename & split tests Christian Brauner
                   ` (9 preceding siblings ...)
  2022-05-12 16:52 ` [PATCH v2 13/13] vfs/idmapped-mounts: remove invalid test Christian Brauner
@ 2022-05-12 20:19 ` Zorro Lang
  2022-05-14 17:59 ` Zorro Lang
  2022-05-21 23:13 ` Dave Chinner
  12 siblings, 0 replies; 24+ messages in thread
From: Zorro Lang @ 2022-05-12 20:19 UTC (permalink / raw)
  To: Christian Brauner; +Cc: fstests, linux-fsdevel

On Thu, May 12, 2022 at 06:52:37PM +0200, Christian Brauner wrote:
> From: "Christian Brauner (Microsoft)" <brauner@kernel.org>
> 
> Hey everyone,
> 
> Please note that this patch series contains patches that will be
> rejected by the fstests mailing list because of the amount of changes
> they contain. So tools like b4 will not be able to find the whole patch
> series on a mailing list. In case it's helpful I've added the
> "fstests.vfstest.for-next" tag which can be pulled. Otherwise it's
> possible to simply use the patch series as it appears in your inbox.

Thanks Christian! I've merged this patchset to my local testing branch, and
haven't found regressions for now. That's good to me.

Due to it *blocks* all other idmapped related patches, so if VFS (idmapped)
forks don't have objections, and if no big regressions either, I'd like to merge
this patchset at first, to free this *blocking*, then we can fix small issues
bit by bit. If anyone has review points, please point out ASAP :)

Thanks,
Zorro

> 
> All vfstests pass:
> 
> #### btrfs ####
> ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
> FSTYP         -- btrfs
> PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
> MKFS_OPTIONS  -- /dev/sda4
> MOUNT_OPTIONS -- /dev/sda4 /mnt/scratch
> 
> btrfs/245 52s ...  54s
> generic/633 58s ...  51s
> generic/644 60s ...  49s
> generic/645 161s ...  143s
> generic/656 63s ...  55s
> Ran: btrfs/245 generic/633 generic/644 generic/645 generic/656
> Passed all 5 tests
> 
> #### ext4 ####
> ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
> FSTYP         -- ext4
> PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
> MKFS_OPTIONS  -- /dev/sda4
> MOUNT_OPTIONS -- -o acl,user_xattr /dev/sda4 /mnt/scratch
> 
> generic/633 47s ...  50s
> generic/644 46s ...  49s
> generic/645 135s ...  139s
> generic/656 53s ...  54s
> Ran: generic/633 generic/644 generic/645 generic/656
> Passed all 4 tests
> 
> #### xfs ####
> ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
> FSTYP         -- xfs (debug)
> PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
> MKFS_OPTIONS  -- -f /dev/sda4
> MOUNT_OPTIONS -- /dev/sda4 /mnt/scratch
> 
> generic/633 58s ...  58s
> generic/644 62s ...  60s
> generic/645 161s ...  161s
> generic/656 62s ...  63s
> xfs/152 133s ...  133s
> xfs/153 94s ...  92s
> Ran: generic/633 generic/644 generic/645 generic/656 xfs/152 xfs/153
> Passed all 6 tests
> 
> /* v2 */
> * This rebases the patchset on top the for-next branch.
> * Last week we merged 858a19c5e9b0 ("idmapped_mounts: Prepare for
>   support for more features"). The patch switched feature checking from
>   a boolean to a flag. It failed convert all tests. This adds a patch to
>   fix this in patch 01/13.
> * A patch has been added to remove an invalid test. The semantics for a
>   specific corner-case where we allowed a mount's idmapping to change
>   while there were active writers will be altered.
> 
> /* v1 */
> As announced multiple times already we need to rename and split the
> idmapped mount testsuite into separate source files and also give it a
> better name to reflect the fact that it covers a lot more than just
> idmapped mounts.
> 
> I have decided against compiling different binaries for now. Instead we
> compile a single vfstest binary that can be called with various command
> line switches to run the various test suites. This is not different than
> what we did for the idmapped-mounts binary. Of course, nothing prevents
> us from using multiple binaries in the future.
> 
> Thanks!
> Christian
> 
> Christian Brauner (13):
>   idmapped-mounts: make all tests set their required feature flags
>   src: rename idmapped-mounts folder
>   src/vfs: rename idmapped-mounts.c file
>   vfstest: rename struct t_idmapped_mounts
>   utils: add missing global.h include
>   utils: add struct vfstest_info
>   utils: move helpers into utils
>   missing: move sys_execveat() to missing.h
>   utils: add struct test_suite
>   vfstests: split idmapped mount tests into separate suite
>   vfstest: split out btrfs idmapped mounts test
>   vfstest: split out remaining idmapped mount tests
>   vfs/idmapped-mounts: remove invalid test
> 
>  .gitignore                                    |     4 +-
>  common/rc                                     |    32 +-
>  src/Makefile                                  |     2 +-
>  src/detached_mounts_propagation.c             |     2 +-
>  src/feature.c                                 |     2 +-
>  src/idmapped-mounts/idmapped-mounts.c         | 14625 ----------------
>  src/idmapped-mounts/utils.c                   |   425 -
>  src/idmapped-mounts/utils.h                   |   130 -
>  src/{idmapped-mounts => vfs}/Makefile         |    14 +-
>  src/vfs/btrfs-idmapped-mounts.c               |  3854 ++++
>  src/vfs/btrfs-idmapped-mounts.h               |    15 +
>  src/vfs/idmapped-mounts.c                     |  7747 ++++++++
>  src/vfs/idmapped-mounts.h                     |    18 +
>  src/{idmapped-mounts => vfs}/missing.h        |    11 +
>  src/{idmapped-mounts => vfs}/mount-idmapped.c |     0
>  src/vfs/utils.c                               |  1050 ++
>  src/vfs/utils.h                               |   373 +
>  src/vfs/vfstest.c                             |  2073 +++
>  tests/btrfs/245                               |     2 +-
>  tests/generic/633                             |     2 +-
>  tests/generic/644                             |     2 +-
>  tests/generic/645                             |     2 +-
>  tests/generic/656                             |     2 +-
>  tests/xfs/152                                 |     4 +-
>  tests/xfs/153                                 |     2 +-
>  25 files changed, 15177 insertions(+), 15216 deletions(-)
>  delete mode 100644 src/idmapped-mounts/idmapped-mounts.c
>  delete mode 100644 src/idmapped-mounts/utils.c
>  delete mode 100644 src/idmapped-mounts/utils.h
>  rename src/{idmapped-mounts => vfs}/Makefile (59%)
>  create mode 100644 src/vfs/btrfs-idmapped-mounts.c
>  create mode 100644 src/vfs/btrfs-idmapped-mounts.h
>  create mode 100644 src/vfs/idmapped-mounts.c
>  create mode 100644 src/vfs/idmapped-mounts.h
>  rename src/{idmapped-mounts => vfs}/missing.h (93%)
>  rename src/{idmapped-mounts => vfs}/mount-idmapped.c (100%)
>  create mode 100644 src/vfs/utils.c
>  create mode 100644 src/vfs/utils.h
>  create mode 100644 src/vfs/vfstest.c
> 
> 
> base-commit: 87cf32ad3fa234e3d5ec501e0f86516bef91d805
> -- 
> 2.34.1
> 


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 00/13] rename & split tests
  2022-05-12 16:52 [PATCH v2 00/13] rename & split tests Christian Brauner
                   ` (10 preceding siblings ...)
  2022-05-12 20:19 ` [PATCH v2 00/13] rename & split tests Zorro Lang
@ 2022-05-14 17:59 ` Zorro Lang
  2022-05-16  8:02   ` Christian Brauner
  2022-05-21 23:13 ` Dave Chinner
  12 siblings, 1 reply; 24+ messages in thread
From: Zorro Lang @ 2022-05-14 17:59 UTC (permalink / raw)
  To: Christian Brauner; +Cc: fstests

On Thu, May 12, 2022 at 06:52:37PM +0200, Christian Brauner wrote:
> From: "Christian Brauner (Microsoft)" <brauner@kernel.org>
> 
> Hey everyone,
> 
> Please note that this patch series contains patches that will be
> rejected by the fstests mailing list because of the amount of changes
> they contain. So tools like b4 will not be able to find the whole patch
> series on a mailing list. In case it's helpful I've added the
> "fstests.vfstest.for-next" tag which can be pulled. Otherwise it's
> possible to simply use the patch series as it appears in your inbox.

Hi Christian,

After a full round regression test, I just found this patchset cause
generic/689 fail as [1], so I think it still need below fix [2]. If
you won't send a fixed version before fstests release this sunday, I
will fix it by myself, to help this patchset be merged this week. Due
to it's not worth waiting one more week for this.

Thanks,
Zorro

[1]
/var/lib/xfstests/tests/generic/689: line 29: /var/lib/xfstests/src/idmapped-mounts/idmapped-mounts: No such file or directory

[2]
diff --git a/tests/generic/689 b/tests/generic/689
index 670f8e5a..10dc58ed 100755
--- a/tests/generic/689
+++ b/tests/generic/689
@@ -11,7 +11,7 @@
 # 705191b03d50 ("fs: fix acl translation")
 #
 . ./common/preamble
-_begin_fstest auto quick perms
+_begin_fstest auto quick idmapped perms
 
 # Import common functions.
 . ./common/filter
@@ -26,7 +26,7 @@ _require_group fsgqa
 
 echo "Silence is golden"
 
-$here/src/idmapped-mounts/idmapped-mounts --test-setxattr-fix-705191b03d50 \
+$here/src/vfs/vfstest --test-setxattr-fix-705191b03d50 \
        --device "$TEST_DEV" --mount "$TEST_DIR" --fstype "$FSTYP"
 
 status=$?

> 
> All vfstests pass:
> 
> #### btrfs ####
> ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
> FSTYP         -- btrfs
> PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
> MKFS_OPTIONS  -- /dev/sda4
> MOUNT_OPTIONS -- /dev/sda4 /mnt/scratch
> 
> btrfs/245 52s ...  54s
> generic/633 58s ...  51s
> generic/644 60s ...  49s
> generic/645 161s ...  143s
> generic/656 63s ...  55s
> Ran: btrfs/245 generic/633 generic/644 generic/645 generic/656
> Passed all 5 tests
> 
> #### ext4 ####
> ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
> FSTYP         -- ext4
> PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
> MKFS_OPTIONS  -- /dev/sda4
> MOUNT_OPTIONS -- -o acl,user_xattr /dev/sda4 /mnt/scratch
> 
> generic/633 47s ...  50s
> generic/644 46s ...  49s
> generic/645 135s ...  139s
> generic/656 53s ...  54s
> Ran: generic/633 generic/644 generic/645 generic/656
> Passed all 4 tests
> 
> #### xfs ####
> ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
> FSTYP         -- xfs (debug)
> PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
> MKFS_OPTIONS  -- -f /dev/sda4
> MOUNT_OPTIONS -- /dev/sda4 /mnt/scratch
> 
> generic/633 58s ...  58s
> generic/644 62s ...  60s
> generic/645 161s ...  161s
> generic/656 62s ...  63s
> xfs/152 133s ...  133s
> xfs/153 94s ...  92s
> Ran: generic/633 generic/644 generic/645 generic/656 xfs/152 xfs/153
> Passed all 6 tests
> 
> /* v2 */
> * This rebases the patchset on top the for-next branch.
> * Last week we merged 858a19c5e9b0 ("idmapped_mounts: Prepare for
>   support for more features"). The patch switched feature checking from
>   a boolean to a flag. It failed convert all tests. This adds a patch to
>   fix this in patch 01/13.
> * A patch has been added to remove an invalid test. The semantics for a
>   specific corner-case where we allowed a mount's idmapping to change
>   while there were active writers will be altered.
> 
> /* v1 */
> As announced multiple times already we need to rename and split the
> idmapped mount testsuite into separate source files and also give it a
> better name to reflect the fact that it covers a lot more than just
> idmapped mounts.
> 
> I have decided against compiling different binaries for now. Instead we
> compile a single vfstest binary that can be called with various command
> line switches to run the various test suites. This is not different than
> what we did for the idmapped-mounts binary. Of course, nothing prevents
> us from using multiple binaries in the future.
> 
> Thanks!
> Christian
> 
> Christian Brauner (13):
>   idmapped-mounts: make all tests set their required feature flags
>   src: rename idmapped-mounts folder
>   src/vfs: rename idmapped-mounts.c file
>   vfstest: rename struct t_idmapped_mounts
>   utils: add missing global.h include
>   utils: add struct vfstest_info
>   utils: move helpers into utils
>   missing: move sys_execveat() to missing.h
>   utils: add struct test_suite
>   vfstests: split idmapped mount tests into separate suite
>   vfstest: split out btrfs idmapped mounts test
>   vfstest: split out remaining idmapped mount tests
>   vfs/idmapped-mounts: remove invalid test
> 
>  .gitignore                                    |     4 +-
>  common/rc                                     |    32 +-
>  src/Makefile                                  |     2 +-
>  src/detached_mounts_propagation.c             |     2 +-
>  src/feature.c                                 |     2 +-
>  src/idmapped-mounts/idmapped-mounts.c         | 14625 ----------------
>  src/idmapped-mounts/utils.c                   |   425 -
>  src/idmapped-mounts/utils.h                   |   130 -
>  src/{idmapped-mounts => vfs}/Makefile         |    14 +-
>  src/vfs/btrfs-idmapped-mounts.c               |  3854 ++++
>  src/vfs/btrfs-idmapped-mounts.h               |    15 +
>  src/vfs/idmapped-mounts.c                     |  7747 ++++++++
>  src/vfs/idmapped-mounts.h                     |    18 +
>  src/{idmapped-mounts => vfs}/missing.h        |    11 +
>  src/{idmapped-mounts => vfs}/mount-idmapped.c |     0
>  src/vfs/utils.c                               |  1050 ++
>  src/vfs/utils.h                               |   373 +
>  src/vfs/vfstest.c                             |  2073 +++
>  tests/btrfs/245                               |     2 +-
>  tests/generic/633                             |     2 +-
>  tests/generic/644                             |     2 +-
>  tests/generic/645                             |     2 +-
>  tests/generic/656                             |     2 +-
>  tests/xfs/152                                 |     4 +-
>  tests/xfs/153                                 |     2 +-
>  25 files changed, 15177 insertions(+), 15216 deletions(-)
>  delete mode 100644 src/idmapped-mounts/idmapped-mounts.c
>  delete mode 100644 src/idmapped-mounts/utils.c
>  delete mode 100644 src/idmapped-mounts/utils.h
>  rename src/{idmapped-mounts => vfs}/Makefile (59%)
>  create mode 100644 src/vfs/btrfs-idmapped-mounts.c
>  create mode 100644 src/vfs/btrfs-idmapped-mounts.h
>  create mode 100644 src/vfs/idmapped-mounts.c
>  create mode 100644 src/vfs/idmapped-mounts.h
>  rename src/{idmapped-mounts => vfs}/missing.h (93%)
>  rename src/{idmapped-mounts => vfs}/mount-idmapped.c (100%)
>  create mode 100644 src/vfs/utils.c
>  create mode 100644 src/vfs/utils.h
>  create mode 100644 src/vfs/vfstest.c
> 
> 
> base-commit: 87cf32ad3fa234e3d5ec501e0f86516bef91d805
> -- 
> 2.34.1
> 


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 01/13] idmapped-mounts: make all tests set their required feature flags
  2022-05-12 16:52 ` [PATCH v2 01/13] idmapped-mounts: make all tests set their required feature flags Christian Brauner
@ 2022-05-16  5:50   ` Christoph Hellwig
  0 siblings, 0 replies; 24+ messages in thread
From: Christoph Hellwig @ 2022-05-16  5:50 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Zorro Lang, fstests, Dave Chinner, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong

Looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 13/13] vfs/idmapped-mounts: remove invalid test
  2022-05-12 16:52 ` [PATCH v2 13/13] vfs/idmapped-mounts: remove invalid test Christian Brauner
@ 2022-05-16  5:53   ` Christoph Hellwig
  0 siblings, 0 replies; 24+ messages in thread
From: Christoph Hellwig @ 2022-05-16  5:53 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Zorro Lang, fstests, Dave Chinner, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong

Looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 00/13] rename & split tests
  2022-05-14 17:59 ` Zorro Lang
@ 2022-05-16  8:02   ` Christian Brauner
  0 siblings, 0 replies; 24+ messages in thread
From: Christian Brauner @ 2022-05-16  8:02 UTC (permalink / raw)
  To: Zorro Lang; +Cc: fstests

On Sun, May 15, 2022 at 01:59:43AM +0800, Zorro Lang wrote:
> On Thu, May 12, 2022 at 06:52:37PM +0200, Christian Brauner wrote:
> > From: "Christian Brauner (Microsoft)" <brauner@kernel.org>
> > 
> > Hey everyone,
> > 
> > Please note that this patch series contains patches that will be
> > rejected by the fstests mailing list because of the amount of changes
> > they contain. So tools like b4 will not be able to find the whole patch
> > series on a mailing list. In case it's helpful I've added the
> > "fstests.vfstest.for-next" tag which can be pulled. Otherwise it's
> > possible to simply use the patch series as it appears in your inbox.
> 
> Hi Christian,

Hi Zorro,

> 
> After a full round regression test, I just found this patchset cause
> generic/689 fail as [1], so I think it still need below fix [2]. If
> you won't send a fixed version before fstests release this sunday, I
> will fix it by myself, to help this patchset be merged this week. Due
> to it's not worth waiting one more week for this.

Thank you!

> 
> Thanks,
> Zorro
> 
> [1]
> /var/lib/xfstests/tests/generic/689: line 29: /var/lib/xfstests/src/idmapped-mounts/idmapped-mounts: No such file or directory
> 
> [2]
> diff --git a/tests/generic/689 b/tests/generic/689
> index 670f8e5a..10dc58ed 100755
> --- a/tests/generic/689
> +++ b/tests/generic/689
> @@ -11,7 +11,7 @@
>  # 705191b03d50 ("fs: fix acl translation")
>  #
>  . ./common/preamble
> -_begin_fstest auto quick perms
> +_begin_fstest auto quick idmapped perms

Ah, I see. I didn't catch this because I forgot to add the test to the
idmapped group.

Thanks,
Christian

>  
>  # Import common functions.
>  . ./common/filter
> @@ -26,7 +26,7 @@ _require_group fsgqa
>  
>  echo "Silence is golden"
>  
> -$here/src/idmapped-mounts/idmapped-mounts --test-setxattr-fix-705191b03d50 \
> +$here/src/vfs/vfstest --test-setxattr-fix-705191b03d50 \
>         --device "$TEST_DEV" --mount "$TEST_DIR" --fstype "$FSTYP"
>  
>  status=$?
> 
> > 
> > All vfstests pass:
> > 
> > #### btrfs ####
> > ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
> > FSTYP         -- btrfs
> > PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
> > MKFS_OPTIONS  -- /dev/sda4
> > MOUNT_OPTIONS -- /dev/sda4 /mnt/scratch
> > 
> > btrfs/245 52s ...  54s
> > generic/633 58s ...  51s
> > generic/644 60s ...  49s
> > generic/645 161s ...  143s
> > generic/656 63s ...  55s
> > Ran: btrfs/245 generic/633 generic/644 generic/645 generic/656
> > Passed all 5 tests
> > 
> > #### ext4 ####
> > ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
> > FSTYP         -- ext4
> > PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
> > MKFS_OPTIONS  -- /dev/sda4
> > MOUNT_OPTIONS -- -o acl,user_xattr /dev/sda4 /mnt/scratch
> > 
> > generic/633 47s ...  50s
> > generic/644 46s ...  49s
> > generic/645 135s ...  139s
> > generic/656 53s ...  54s
> > Ran: generic/633 generic/644 generic/645 generic/656
> > Passed all 4 tests
> > 
> > #### xfs ####
> > ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
> > FSTYP         -- xfs (debug)
> > PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
> > MKFS_OPTIONS  -- -f /dev/sda4
> > MOUNT_OPTIONS -- /dev/sda4 /mnt/scratch
> > 
> > generic/633 58s ...  58s
> > generic/644 62s ...  60s
> > generic/645 161s ...  161s
> > generic/656 62s ...  63s
> > xfs/152 133s ...  133s
> > xfs/153 94s ...  92s
> > Ran: generic/633 generic/644 generic/645 generic/656 xfs/152 xfs/153
> > Passed all 6 tests
> > 
> > /* v2 */
> > * This rebases the patchset on top the for-next branch.
> > * Last week we merged 858a19c5e9b0 ("idmapped_mounts: Prepare for
> >   support for more features"). The patch switched feature checking from
> >   a boolean to a flag. It failed convert all tests. This adds a patch to
> >   fix this in patch 01/13.
> > * A patch has been added to remove an invalid test. The semantics for a
> >   specific corner-case where we allowed a mount's idmapping to change
> >   while there were active writers will be altered.
> > 
> > /* v1 */
> > As announced multiple times already we need to rename and split the
> > idmapped mount testsuite into separate source files and also give it a
> > better name to reflect the fact that it covers a lot more than just
> > idmapped mounts.
> > 
> > I have decided against compiling different binaries for now. Instead we
> > compile a single vfstest binary that can be called with various command
> > line switches to run the various test suites. This is not different than
> > what we did for the idmapped-mounts binary. Of course, nothing prevents
> > us from using multiple binaries in the future.
> > 
> > Thanks!
> > Christian
> > 
> > Christian Brauner (13):
> >   idmapped-mounts: make all tests set their required feature flags
> >   src: rename idmapped-mounts folder
> >   src/vfs: rename idmapped-mounts.c file
> >   vfstest: rename struct t_idmapped_mounts
> >   utils: add missing global.h include
> >   utils: add struct vfstest_info
> >   utils: move helpers into utils
> >   missing: move sys_execveat() to missing.h
> >   utils: add struct test_suite
> >   vfstests: split idmapped mount tests into separate suite
> >   vfstest: split out btrfs idmapped mounts test
> >   vfstest: split out remaining idmapped mount tests
> >   vfs/idmapped-mounts: remove invalid test
> > 
> >  .gitignore                                    |     4 +-
> >  common/rc                                     |    32 +-
> >  src/Makefile                                  |     2 +-
> >  src/detached_mounts_propagation.c             |     2 +-
> >  src/feature.c                                 |     2 +-
> >  src/idmapped-mounts/idmapped-mounts.c         | 14625 ----------------
> >  src/idmapped-mounts/utils.c                   |   425 -
> >  src/idmapped-mounts/utils.h                   |   130 -
> >  src/{idmapped-mounts => vfs}/Makefile         |    14 +-
> >  src/vfs/btrfs-idmapped-mounts.c               |  3854 ++++
> >  src/vfs/btrfs-idmapped-mounts.h               |    15 +
> >  src/vfs/idmapped-mounts.c                     |  7747 ++++++++
> >  src/vfs/idmapped-mounts.h                     |    18 +
> >  src/{idmapped-mounts => vfs}/missing.h        |    11 +
> >  src/{idmapped-mounts => vfs}/mount-idmapped.c |     0
> >  src/vfs/utils.c                               |  1050 ++
> >  src/vfs/utils.h                               |   373 +
> >  src/vfs/vfstest.c                             |  2073 +++
> >  tests/btrfs/245                               |     2 +-
> >  tests/generic/633                             |     2 +-
> >  tests/generic/644                             |     2 +-
> >  tests/generic/645                             |     2 +-
> >  tests/generic/656                             |     2 +-
> >  tests/xfs/152                                 |     4 +-
> >  tests/xfs/153                                 |     2 +-
> >  25 files changed, 15177 insertions(+), 15216 deletions(-)
> >  delete mode 100644 src/idmapped-mounts/idmapped-mounts.c
> >  delete mode 100644 src/idmapped-mounts/utils.c
> >  delete mode 100644 src/idmapped-mounts/utils.h
> >  rename src/{idmapped-mounts => vfs}/Makefile (59%)
> >  create mode 100644 src/vfs/btrfs-idmapped-mounts.c
> >  create mode 100644 src/vfs/btrfs-idmapped-mounts.h
> >  create mode 100644 src/vfs/idmapped-mounts.c
> >  create mode 100644 src/vfs/idmapped-mounts.h
> >  rename src/{idmapped-mounts => vfs}/missing.h (93%)
> >  rename src/{idmapped-mounts => vfs}/mount-idmapped.c (100%)
> >  create mode 100644 src/vfs/utils.c
> >  create mode 100644 src/vfs/utils.h
> >  create mode 100644 src/vfs/vfstest.c
> > 
> > 
> > base-commit: 87cf32ad3fa234e3d5ec501e0f86516bef91d805
> > -- 
> > 2.34.1
> > 
> 

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 00/13] rename & split tests
  2022-05-12 16:52 [PATCH v2 00/13] rename & split tests Christian Brauner
                   ` (11 preceding siblings ...)
  2022-05-14 17:59 ` Zorro Lang
@ 2022-05-21 23:13 ` Dave Chinner
  2022-05-22  1:07   ` Jens Axboe
  2022-05-23  9:44   ` Christian Brauner
  12 siblings, 2 replies; 24+ messages in thread
From: Dave Chinner @ 2022-05-21 23:13 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Zorro Lang, fstests, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong, io-uring

[cc io_uring]

On Thu, May 12, 2022 at 06:52:37PM +0200, Christian Brauner wrote:
> From: "Christian Brauner (Microsoft)" <brauner@kernel.org>
> 
> Hey everyone,
> 
> Please note that this patch series contains patches that will be
> rejected by the fstests mailing list because of the amount of changes
> they contain. So tools like b4 will not be able to find the whole patch
> series on a mailing list. In case it's helpful I've added the
> "fstests.vfstest.for-next" tag which can be pulled. Otherwise it's
> possible to simply use the patch series as it appears in your inbox.
> 
> All vfstests pass:

[...]

> #### xfs ####
> ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
> FSTYP         -- xfs (debug)
> PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
> MKFS_OPTIONS  -- -f /dev/sda4
> MOUNT_OPTIONS -- /dev/sda4 /mnt/scratch
> 
> generic/633 58s ...  58s
> generic/644 62s ...  60s
> generic/645 161s ...  161s
> generic/656 62s ...  63s
> xfs/152 133s ...  133s
> xfs/153 94s ...  92s
> Ran: generic/633 generic/644 generic/645 generic/656 xfs/152 xfs/153
> Passed all 6 tests

I'm not sure if it's this series that has introduced a test bug or
triggered a latent issue in the kernel, but I've started seeing
generic/633 throw audit subsystem warnings on a single test machine
as of late Friday:

[ 7285.015888] WARNING: CPU: 3 PID: 2147118 at kernel/auditsc.c:2035 __audit_syscall_entry+0x113/0x140
[ 7285.019973] Modules linked in:
[ 7285.021281] CPU: 3 PID: 2147118 Comm: vfstest Not tainted 5.18.0-rc7-dgc+ #1250
[ 7285.024341] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
[ 7285.027782] RIP: 0010:__audit_syscall_entry+0x113/0x140
[ 7285.029923] Code: 24 e8 c1 6b ff ff 48 8b 34 24 85 c0 48 8b 54 24 08 48 8b 4c 24 10 4c 8b 44 24 18 0f 84 72 ff ff ff 48 83 c4 20 5b 5d 41 5c c3 <0f> 0b 85 c0 75 14 48 83 c4 20 48 c7 c7 70 45 7f 82 5b 5d 41 5c e9
[ 7285.037563] RSP: 0018:ffffc900012f7ed0 EFLAGS: 00010202
[ 7285.039748] RAX: 0000000000000000 RBX: ffff8880aaf18800 RCX: 000000000000003c
[ 7285.043126] RDX: 00000000000000e7 RSI: 0000000000000000 RDI: ffff888102104f00
[ 7285.046120] RBP: 00000000000000e7 R08: fffffffffffffe2c R09: 0000000000000002
[ 7285.049108] R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000000000
[ 7285.052058] R13: ffffc900012f7f58 R14: 00000000000000e7 R15: 0000000000000000
[ 7285.055030] FS:  00007f7906d6c740(0000) GS:ffff88813bd80000(0000) knlGS:0000000000000000
[ 7285.058396] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 7285.060788] CR2: 00007f3ffa7e9bb8 CR3: 000000010bb00000 CR4: 00000000000006e0
[ 7285.063735] Call Trace:
[ 7285.064796]  <TASK>
[ 7285.065759]  syscall_trace_enter.constprop.0+0x122/0x1a0
[ 7285.067978]  do_syscall_64+0x16/0x80
[ 7285.069497]  entry_SYSCALL_64_after_hwframe+0x44/0xae
[ 7285.071590] RIP: 0033:0x7f7906e35f49
[ 7285.073118] Code: 00 4c 8b 05 29 6f 10 00 be e7 00 00 00 ba 3c 00 00 00 eb 12 0f 1f 44 00 00 89 d0 0f 05 48 3d 00 f0 ff ff 77 1c f4 89 f0 0f 05 <48> 3d 00 f0 ff ff 76 e7 f7 d8 64 41 89 00 eb df 0f 1f 80 00 00 00
[ 7285.078021] RSP: 002b:00007ffeee52db88 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7
[ 7285.079995] RAX: ffffffffffffffda RBX: 00007f7906f39920 RCX: 00007f7906e35f49
[ 7285.081869] RDX: 000000000000003c RSI: 00000000000000e7 RDI: 0000000000000000
[ 7285.083729] RBP: 0000000000000000 R08: ffffffffffffff88 R09: 0000000000000001
[ 7285.085594] R10: fffffffffffffe2c R11: 0000000000000246 R12: 00007f7906f39920
[ 7285.087457] R13: 0000000000000001 R14: 00007f7906f3ee28 R15: 0000000000000000
[ 7285.089320]  </TASK>
[ 7285.089949] ---[ end trace 0000000000000000 ]---
[ 7285.091182] audit_panic: 22 callbacks suppressed
[ 7285.091185] audit: unrecoverable error in audit_syscall_entry()

Adn faddr_to_line tells me it is this:

void __audit_syscall_entry(int major, unsigned long a1, unsigned long a2,
                           unsigned long a3, unsigned long a4)
{
        struct audit_context *context = audit_context();
        enum audit_state     state;

        if (!audit_enabled || !context)
                return;

>>>>>>  WARN_ON(context->context != AUDIT_CTX_UNUSED);  <<<<<<
        WARN_ON(context->name_count);
        if (context->context != AUDIT_CTX_UNUSED || context->name_count) {
                audit_panic("unrecoverable error in audit_syscall_entry()");
                return;
        }
.....

I have no clue how the audit subsystem works, I don't even know how
to enable it, and I've never seen audit messages on the console of
this test machine. I have other test machines that have audit
enabled, and they do not dump warnings like this - the only thing I
see in the logs for those machines is this:

 run xfstest generic/633
 process 'vfstest' launched '/dev/fd/4/file1' with NULL argv: empty string added
 XFS (pmem0): Unmounting Filesystem
 XFS (pmem0): Mounting V5 Filesystem
 XFS (pmem0): Ending clean mount
 run xfstest generic/634

That's waht I was seeing from this test machine earlier in the week,
too - I've been running 5.18-rc7 as the base kernel all week - so
I'm not sure .....

Oooooohhhh.

/* The per-task audit context. */
struct audit_context {
        int                 dummy;      /* must be the first element */
        enum {
                AUDIT_CTX_UNUSED,       /* audit_context is currently unused */
                AUDIT_CTX_SYSCALL,      /* in use by syscall */
                AUDIT_CTX_URING,        /* in use by io_uring */
        } context;
....

And that reminded me of something. I commented on #xfs on Friday
afternoon:

[20/5/22 15:04] <dchinner> so of the 3.5 hours run time on the machine that jsut completed, it looks like about a dozen tests are responsible for an hour of that runtime...
[20/5/22 15:05] <dchinner> but it was a clean run with no failures in 1055 tests run.
[20/5/22 15:06] <dchinner> But there's some WTFs like this in it:
[20/5/22 15:06] <dchinner> generic/678     [not run] kernel does not support IO_URING
[20/5/22 15:08] <dchinner> yet the same kernel on a different machine:
[20/5/22 15:08] <dchinner> generic/678 11s ...  3s
[20/5/22 15:08] <dchinner> and they have the same userspace, too....

Yeah, the machine that complained about "kernel does not support
IO_URING" is the one that is throwing these warnings now. It wasn't
that the kernel didn't support io-uring, it was that the machine was
missing the liburing-dev library. I installed it and rebuilt
fstests. These audit failures co-incide with the first test runs
with io-uring enabled. And vfstest uses io_uring if fstests enables
it.

Hence this now smells like a pre-existing issue -  either a test bug
or an io_uring task audit context leak. Anyone got any ideas?

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 00/13] rename & split tests
  2022-05-21 23:13 ` Dave Chinner
@ 2022-05-22  1:07   ` Jens Axboe
  2022-05-22  2:19     ` Jens Axboe
  2022-05-23  9:44   ` Christian Brauner
  1 sibling, 1 reply; 24+ messages in thread
From: Jens Axboe @ 2022-05-22  1:07 UTC (permalink / raw)
  To: Dave Chinner, Christian Brauner
  Cc: Zorro Lang, fstests, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong, io-uring

On 5/21/22 5:13 PM, Dave Chinner wrote:
> [cc io_uring]
> 
> On Thu, May 12, 2022 at 06:52:37PM +0200, Christian Brauner wrote:
>> From: "Christian Brauner (Microsoft)" <brauner@kernel.org>
>>
>> Hey everyone,
>>
>> Please note that this patch series contains patches that will be
>> rejected by the fstests mailing list because of the amount of changes
>> they contain. So tools like b4 will not be able to find the whole patch
>> series on a mailing list. In case it's helpful I've added the
>> "fstests.vfstest.for-next" tag which can be pulled. Otherwise it's
>> possible to simply use the patch series as it appears in your inbox.
>>
>> All vfstests pass:
> 
> [...]
> 
>> #### xfs ####
>> ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
>> FSTYP         -- xfs (debug)
>> PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
>> MKFS_OPTIONS  -- -f /dev/sda4
>> MOUNT_OPTIONS -- /dev/sda4 /mnt/scratch
>>
>> generic/633 58s ...  58s
>> generic/644 62s ...  60s
>> generic/645 161s ...  161s
>> generic/656 62s ...  63s
>> xfs/152 133s ...  133s
>> xfs/153 94s ...  92s
>> Ran: generic/633 generic/644 generic/645 generic/656 xfs/152 xfs/153
>> Passed all 6 tests
> 
> I'm not sure if it's this series that has introduced a test bug or
> triggered a latent issue in the kernel, but I've started seeing
> generic/633 throw audit subsystem warnings on a single test machine
> as of late Friday:
> 
> [ 7285.015888] WARNING: CPU: 3 PID: 2147118 at kernel/auditsc.c:2035 __audit_syscall_entry+0x113/0x140

Does your kernel have this commit?

commit 69e9cd66ae1392437234a63a3a1d60b6655f92ef
Author: Julian Orth <ju.orth@gmail.com>
Date:   Tue May 17 12:32:53 2022 +0200

    audit,io_uring,io-wq: call __audit_uring_exit for dummy contexts

-- 
Jens Axboe


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 00/13] rename & split tests
  2022-05-22  1:07   ` Jens Axboe
@ 2022-05-22  2:19     ` Jens Axboe
  2022-05-23  0:13       ` Dave Chinner
  0 siblings, 1 reply; 24+ messages in thread
From: Jens Axboe @ 2022-05-22  2:19 UTC (permalink / raw)
  To: Dave Chinner, Christian Brauner
  Cc: Zorro Lang, fstests, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong, io-uring

On 5/21/22 7:07 PM, Jens Axboe wrote:
> On 5/21/22 5:13 PM, Dave Chinner wrote:
>> [cc io_uring]
>>
>> On Thu, May 12, 2022 at 06:52:37PM +0200, Christian Brauner wrote:
>>> From: "Christian Brauner (Microsoft)" <brauner@kernel.org>
>>>
>>> Hey everyone,
>>>
>>> Please note that this patch series contains patches that will be
>>> rejected by the fstests mailing list because of the amount of changes
>>> they contain. So tools like b4 will not be able to find the whole patch
>>> series on a mailing list. In case it's helpful I've added the
>>> "fstests.vfstest.for-next" tag which can be pulled. Otherwise it's
>>> possible to simply use the patch series as it appears in your inbox.
>>>
>>> All vfstests pass:
>>
>> [...]
>>
>>> #### xfs ####
>>> ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
>>> FSTYP         -- xfs (debug)
>>> PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
>>> MKFS_OPTIONS  -- -f /dev/sda4
>>> MOUNT_OPTIONS -- /dev/sda4 /mnt/scratch
>>>
>>> generic/633 58s ...  58s
>>> generic/644 62s ...  60s
>>> generic/645 161s ...  161s
>>> generic/656 62s ...  63s
>>> xfs/152 133s ...  133s
>>> xfs/153 94s ...  92s
>>> Ran: generic/633 generic/644 generic/645 generic/656 xfs/152 xfs/153
>>> Passed all 6 tests
>>
>> I'm not sure if it's this series that has introduced a test bug or
>> triggered a latent issue in the kernel, but I've started seeing
>> generic/633 throw audit subsystem warnings on a single test machine
>> as of late Friday:
>>
>> [ 7285.015888] WARNING: CPU: 3 PID: 2147118 at kernel/auditsc.c:2035 __audit_syscall_entry+0x113/0x140
> 
> Does your kernel have this commit?
> 
> commit 69e9cd66ae1392437234a63a3a1d60b6655f92ef
> Author: Julian Orth <ju.orth@gmail.com>
> Date:   Tue May 17 12:32:53 2022 +0200
> 
>     audit,io_uring,io-wq: call __audit_uring_exit for dummy contexts

I could not reproduce either with or without your patch when I finally
got that test going and figure out how to turn on audit and get it
enabled. I don't run with that.

But looking at your line numbers, I think you're missing the above
commit. The WARN_ON_ONCE() matches up with it NOT being applied, which
is most likely why it triggers for you. It's in Linus's tree, but not in
-rc7.

-- 
Jens Axboe


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 00/13] rename & split tests
  2022-05-22  2:19     ` Jens Axboe
@ 2022-05-23  0:13       ` Dave Chinner
  2022-05-23  0:57         ` Jens Axboe
  0 siblings, 1 reply; 24+ messages in thread
From: Dave Chinner @ 2022-05-23  0:13 UTC (permalink / raw)
  To: Jens Axboe
  Cc: Christian Brauner, Zorro Lang, fstests, Eryu Guan,
	Amir Goldstein, Christoph Hellwig, Darrick J. Wong, io-uring

On Sat, May 21, 2022 at 08:19:51PM -0600, Jens Axboe wrote:
> On 5/21/22 7:07 PM, Jens Axboe wrote:
> > On 5/21/22 5:13 PM, Dave Chinner wrote:
> >> [cc io_uring]
> >>
> >> On Thu, May 12, 2022 at 06:52:37PM +0200, Christian Brauner wrote:
> >>> From: "Christian Brauner (Microsoft)" <brauner@kernel.org>
> >>>
> >>> Hey everyone,
> >>>
> >>> Please note that this patch series contains patches that will be
> >>> rejected by the fstests mailing list because of the amount of changes
> >>> they contain. So tools like b4 will not be able to find the whole patch
> >>> series on a mailing list. In case it's helpful I've added the
> >>> "fstests.vfstest.for-next" tag which can be pulled. Otherwise it's
> >>> possible to simply use the patch series as it appears in your inbox.
> >>>
> >>> All vfstests pass:
> >>
> >> [...]
> >>
> >>> #### xfs ####
> >>> ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
> >>> FSTYP         -- xfs (debug)
> >>> PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
> >>> MKFS_OPTIONS  -- -f /dev/sda4
> >>> MOUNT_OPTIONS -- /dev/sda4 /mnt/scratch
> >>>
> >>> generic/633 58s ...  58s
> >>> generic/644 62s ...  60s
> >>> generic/645 161s ...  161s
> >>> generic/656 62s ...  63s
> >>> xfs/152 133s ...  133s
> >>> xfs/153 94s ...  92s
> >>> Ran: generic/633 generic/644 generic/645 generic/656 xfs/152 xfs/153
> >>> Passed all 6 tests
> >>
> >> I'm not sure if it's this series that has introduced a test bug or
> >> triggered a latent issue in the kernel, but I've started seeing
> >> generic/633 throw audit subsystem warnings on a single test machine
> >> as of late Friday:
> >>
> >> [ 7285.015888] WARNING: CPU: 3 PID: 2147118 at kernel/auditsc.c:2035 __audit_syscall_entry+0x113/0x140
> > 
> > Does your kernel have this commit?
> > 
> > commit 69e9cd66ae1392437234a63a3a1d60b6655f92ef
> > Author: Julian Orth <ju.orth@gmail.com>
> > Date:   Tue May 17 12:32:53 2022 +0200
> > 
> >     audit,io_uring,io-wq: call __audit_uring_exit for dummy contexts

No, that wasn't in -rc7.

> I could not reproduce either with or without your patch when I finally
> got that test going and figure out how to turn on audit and get it
> enabled. I don't run with that.

Ok. Given that this has been broken for over a year and nobody
has noticed until late .18-rcX, it might be worth adding an audit
enabled VM to your io-uring test farm....

Cheers,

Dve.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 00/13] rename & split tests
  2022-05-23  0:13       ` Dave Chinner
@ 2022-05-23  0:57         ` Jens Axboe
  2022-05-23 10:41           ` Dave Chinner
  0 siblings, 1 reply; 24+ messages in thread
From: Jens Axboe @ 2022-05-23  0:57 UTC (permalink / raw)
  To: Dave Chinner
  Cc: Christian Brauner, Zorro Lang, fstests, Eryu Guan,
	Amir Goldstein, Christoph Hellwig, Darrick J. Wong, io-uring

On 5/22/22 6:13 PM, Dave Chinner wrote:
> On Sat, May 21, 2022 at 08:19:51PM -0600, Jens Axboe wrote:
>> On 5/21/22 7:07 PM, Jens Axboe wrote:
>>> On 5/21/22 5:13 PM, Dave Chinner wrote:
>>>> [cc io_uring]
>>>>
>>>> On Thu, May 12, 2022 at 06:52:37PM +0200, Christian Brauner wrote:
>>>>> From: "Christian Brauner (Microsoft)" <brauner@kernel.org>
>>>>>
>>>>> Hey everyone,
>>>>>
>>>>> Please note that this patch series contains patches that will be
>>>>> rejected by the fstests mailing list because of the amount of changes
>>>>> they contain. So tools like b4 will not be able to find the whole patch
>>>>> series on a mailing list. In case it's helpful I've added the
>>>>> "fstests.vfstest.for-next" tag which can be pulled. Otherwise it's
>>>>> possible to simply use the patch series as it appears in your inbox.
>>>>>
>>>>> All vfstests pass:
>>>>
>>>> [...]
>>>>
>>>>> #### xfs ####
>>>>> ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
>>>>> FSTYP         -- xfs (debug)
>>>>> PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
>>>>> MKFS_OPTIONS  -- -f /dev/sda4
>>>>> MOUNT_OPTIONS -- /dev/sda4 /mnt/scratch
>>>>>
>>>>> generic/633 58s ...  58s
>>>>> generic/644 62s ...  60s
>>>>> generic/645 161s ...  161s
>>>>> generic/656 62s ...  63s
>>>>> xfs/152 133s ...  133s
>>>>> xfs/153 94s ...  92s
>>>>> Ran: generic/633 generic/644 generic/645 generic/656 xfs/152 xfs/153
>>>>> Passed all 6 tests
>>>>
>>>> I'm not sure if it's this series that has introduced a test bug or
>>>> triggered a latent issue in the kernel, but I've started seeing
>>>> generic/633 throw audit subsystem warnings on a single test machine
>>>> as of late Friday:
>>>>
>>>> [ 7285.015888] WARNING: CPU: 3 PID: 2147118 at kernel/auditsc.c:2035 __audit_syscall_entry+0x113/0x140
>>>
>>> Does your kernel have this commit?
>>>
>>> commit 69e9cd66ae1392437234a63a3a1d60b6655f92ef
>>> Author: Julian Orth <ju.orth@gmail.com>
>>> Date:   Tue May 17 12:32:53 2022 +0200
>>>
>>>     audit,io_uring,io-wq: call __audit_uring_exit for dummy contexts
> 
> No, that wasn't in -rc7.
> 
>> I could not reproduce either with or without your patch when I finally
>> got that test going and figure out how to turn on audit and get it
>> enabled. I don't run with that.
> 
> Ok. Given that this has been broken for over a year and nobody
> has noticed until late .18-rcX, it might be worth adding an audit
> enabled VM to your io-uring test farm....

It was in the 5.16 release, so it's ~4 months ago. Don't disagree on the
testing, though I do think that's mostly on the audit side. I had no
hand in any of that code.

From my experience trying to reproduce it yesterday, my test distros
don't even enable it and you have to both fiddle the config and add a
boot parameter to even turn it on. And then it still didn't trigger for
me.

I'll see if I can add something to the testing mix for this.

-- 
Jens Axboe


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 00/13] rename & split tests
  2022-05-21 23:13 ` Dave Chinner
  2022-05-22  1:07   ` Jens Axboe
@ 2022-05-23  9:44   ` Christian Brauner
  1 sibling, 0 replies; 24+ messages in thread
From: Christian Brauner @ 2022-05-23  9:44 UTC (permalink / raw)
  To: Dave Chinner
  Cc: Zorro Lang, fstests, Eryu Guan, Amir Goldstein,
	Christoph Hellwig, Darrick J. Wong, io-uring

On Sun, May 22, 2022 at 09:13:50AM +1000, Dave Chinner wrote:
> [cc io_uring]
> 
> On Thu, May 12, 2022 at 06:52:37PM +0200, Christian Brauner wrote:
> > From: "Christian Brauner (Microsoft)" <brauner@kernel.org>
> > 
> > Hey everyone,
> > 
> > Please note that this patch series contains patches that will be
> > rejected by the fstests mailing list because of the amount of changes
> > they contain. So tools like b4 will not be able to find the whole patch
> > series on a mailing list. In case it's helpful I've added the
> > "fstests.vfstest.for-next" tag which can be pulled. Otherwise it's
> > possible to simply use the patch series as it appears in your inbox.
> > 
> > All vfstests pass:
> 
> [...]
> 
> > #### xfs ####
> > ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
> > FSTYP         -- xfs (debug)
> > PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
> > MKFS_OPTIONS  -- -f /dev/sda4
> > MOUNT_OPTIONS -- /dev/sda4 /mnt/scratch
> > 
> > generic/633 58s ...  58s
> > generic/644 62s ...  60s
> > generic/645 161s ...  161s
> > generic/656 62s ...  63s
> > xfs/152 133s ...  133s
> > xfs/153 94s ...  92s
> > Ran: generic/633 generic/644 generic/645 generic/656 xfs/152 xfs/153
> > Passed all 6 tests
> 
> I'm not sure if it's this series that has introduced a test bug or
> triggered a latent issue in the kernel, but I've started seeing
> generic/633 throw audit subsystem warnings on a single test machine
> as of late Friday:
> 
> [ 7285.015888] WARNING: CPU: 3 PID: 2147118 at kernel/auditsc.c:2035 __audit_syscall_entry+0x113/0x140
> [ 7285.019973] Modules linked in:
> [ 7285.021281] CPU: 3 PID: 2147118 Comm: vfstest Not tainted 5.18.0-rc7-dgc+ #1250
> [ 7285.024341] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014
> [ 7285.027782] RIP: 0010:__audit_syscall_entry+0x113/0x140
> [ 7285.029923] Code: 24 e8 c1 6b ff ff 48 8b 34 24 85 c0 48 8b 54 24 08 48 8b 4c 24 10 4c 8b 44 24 18 0f 84 72 ff ff ff 48 83 c4 20 5b 5d 41 5c c3 <0f> 0b 85 c0 75 14 48 83 c4 20 48 c7 c7 70 45 7f 82 5b 5d 41 5c e9
> [ 7285.037563] RSP: 0018:ffffc900012f7ed0 EFLAGS: 00010202
> [ 7285.039748] RAX: 0000000000000000 RBX: ffff8880aaf18800 RCX: 000000000000003c
> [ 7285.043126] RDX: 00000000000000e7 RSI: 0000000000000000 RDI: ffff888102104f00
> [ 7285.046120] RBP: 00000000000000e7 R08: fffffffffffffe2c R09: 0000000000000002
> [ 7285.049108] R10: 0000000000000001 R11: 0000000000000000 R12: 0000000000000000
> [ 7285.052058] R13: ffffc900012f7f58 R14: 00000000000000e7 R15: 0000000000000000
> [ 7285.055030] FS:  00007f7906d6c740(0000) GS:ffff88813bd80000(0000) knlGS:0000000000000000
> [ 7285.058396] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [ 7285.060788] CR2: 00007f3ffa7e9bb8 CR3: 000000010bb00000 CR4: 00000000000006e0
> [ 7285.063735] Call Trace:
> [ 7285.064796]  <TASK>
> [ 7285.065759]  syscall_trace_enter.constprop.0+0x122/0x1a0
> [ 7285.067978]  do_syscall_64+0x16/0x80
> [ 7285.069497]  entry_SYSCALL_64_after_hwframe+0x44/0xae
> [ 7285.071590] RIP: 0033:0x7f7906e35f49
> [ 7285.073118] Code: 00 4c 8b 05 29 6f 10 00 be e7 00 00 00 ba 3c 00 00 00 eb 12 0f 1f 44 00 00 89 d0 0f 05 48 3d 00 f0 ff ff 77 1c f4 89 f0 0f 05 <48> 3d 00 f0 ff ff 76 e7 f7 d8 64 41 89 00 eb df 0f 1f 80 00 00 00
> [ 7285.078021] RSP: 002b:00007ffeee52db88 EFLAGS: 00000246 ORIG_RAX: 00000000000000e7
> [ 7285.079995] RAX: ffffffffffffffda RBX: 00007f7906f39920 RCX: 00007f7906e35f49
> [ 7285.081869] RDX: 000000000000003c RSI: 00000000000000e7 RDI: 0000000000000000
> [ 7285.083729] RBP: 0000000000000000 R08: ffffffffffffff88 R09: 0000000000000001
> [ 7285.085594] R10: fffffffffffffe2c R11: 0000000000000246 R12: 00007f7906f39920
> [ 7285.087457] R13: 0000000000000001 R14: 00007f7906f3ee28 R15: 0000000000000000
> [ 7285.089320]  </TASK>
> [ 7285.089949] ---[ end trace 0000000000000000 ]---
> [ 7285.091182] audit_panic: 22 callbacks suppressed
> [ 7285.091185] audit: unrecoverable error in audit_syscall_entry()
> 
> Adn faddr_to_line tells me it is this:
> 
> void __audit_syscall_entry(int major, unsigned long a1, unsigned long a2,
>                            unsigned long a3, unsigned long a4)
> {
>         struct audit_context *context = audit_context();
>         enum audit_state     state;
> 
>         if (!audit_enabled || !context)
>                 return;
> 
> >>>>>>  WARN_ON(context->context != AUDIT_CTX_UNUSED);  <<<<<<
>         WARN_ON(context->name_count);
>         if (context->context != AUDIT_CTX_UNUSED || context->name_count) {
>                 audit_panic("unrecoverable error in audit_syscall_entry()");
>                 return;
>         }
> .....
> 
> I have no clue how the audit subsystem works, I don't even know how
> to enable it, and I've never seen audit messages on the console of
> this test machine. I have other test machines that have audit
> enabled, and they do not dump warnings like this - the only thing I
> see in the logs for those machines is this:
> 
>  run xfstest generic/633
>  process 'vfstest' launched '/dev/fd/4/file1' with NULL argv: empty string added
>  XFS (pmem0): Unmounting Filesystem
>  XFS (pmem0): Mounting V5 Filesystem
>  XFS (pmem0): Ending clean mount
>  run xfstest generic/634
> 
> That's waht I was seeing from this test machine earlier in the week,
> too - I've been running 5.18-rc7 as the base kernel all week - so
> I'm not sure .....
> 
> Oooooohhhh.
> 
> /* The per-task audit context. */
> struct audit_context {
>         int                 dummy;      /* must be the first element */
>         enum {
>                 AUDIT_CTX_UNUSED,       /* audit_context is currently unused */
>                 AUDIT_CTX_SYSCALL,      /* in use by syscall */
>                 AUDIT_CTX_URING,        /* in use by io_uring */
>         } context;
> ....
> 
> And that reminded me of something. I commented on #xfs on Friday
> afternoon:
> 
> [20/5/22 15:04] <dchinner> so of the 3.5 hours run time on the machine that jsut completed, it looks like about a dozen tests are responsible for an hour of that runtime...
> [20/5/22 15:05] <dchinner> but it was a clean run with no failures in 1055 tests run.
> [20/5/22 15:06] <dchinner> But there's some WTFs like this in it:
> [20/5/22 15:06] <dchinner> generic/678     [not run] kernel does not support IO_URING
> [20/5/22 15:08] <dchinner> yet the same kernel on a different machine:
> [20/5/22 15:08] <dchinner> generic/678 11s ...  3s
> [20/5/22 15:08] <dchinner> and they have the same userspace, too....
> 
> Yeah, the machine that complained about "kernel does not support
> IO_URING" is the one that is throwing these warnings now. It wasn't
> that the kernel didn't support io-uring, it was that the machine was
> missing the liburing-dev library. I installed it and rebuilt
> fstests. These audit failures co-incide with the first test runs
> with io-uring enabled. And vfstest uses io_uring if fstests enables
> it.
> 
> Hence this now smells like a pre-existing issue -  either a test bug
> or an io_uring task audit context leak. Anyone got any ideas?

I see this is unrelated to the test thankfully and can be considered
fixed afaict.

Thanks for taking care of this everyone!
Christian

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 00/13] rename & split tests
  2022-05-23  0:57         ` Jens Axboe
@ 2022-05-23 10:41           ` Dave Chinner
  2022-05-23 12:28             ` Jens Axboe
  0 siblings, 1 reply; 24+ messages in thread
From: Dave Chinner @ 2022-05-23 10:41 UTC (permalink / raw)
  To: Jens Axboe
  Cc: Christian Brauner, Zorro Lang, fstests, Eryu Guan,
	Amir Goldstein, Christoph Hellwig, Darrick J. Wong, io-uring

On Sun, May 22, 2022 at 06:57:04PM -0600, Jens Axboe wrote:
> On 5/22/22 6:13 PM, Dave Chinner wrote:
> > On Sat, May 21, 2022 at 08:19:51PM -0600, Jens Axboe wrote:
> >> On 5/21/22 7:07 PM, Jens Axboe wrote:
> >>> On 5/21/22 5:13 PM, Dave Chinner wrote:
> >>>> [cc io_uring]
> >>>>
> >>>> On Thu, May 12, 2022 at 06:52:37PM +0200, Christian Brauner wrote:
> >>>>> From: "Christian Brauner (Microsoft)" <brauner@kernel.org>
> >>>>>
> >>>>> Hey everyone,
> >>>>>
> >>>>> Please note that this patch series contains patches that will be
> >>>>> rejected by the fstests mailing list because of the amount of changes
> >>>>> they contain. So tools like b4 will not be able to find the whole patch
> >>>>> series on a mailing list. In case it's helpful I've added the
> >>>>> "fstests.vfstest.for-next" tag which can be pulled. Otherwise it's
> >>>>> possible to simply use the patch series as it appears in your inbox.
> >>>>>
> >>>>> All vfstests pass:
> >>>>
> >>>> [...]
> >>>>
> >>>>> #### xfs ####
> >>>>> ubuntu@imp1-vm:~/src/git/xfstests$ sudo ./check -g idmapped
> >>>>> FSTYP         -- xfs (debug)
> >>>>> PLATFORM      -- Linux/x86_64 imp1-vm 5.18.0-rc4-fs-mnt-hold-writers-8a2e2350494f #107 SMP PREEMPT_DYNAMIC Mon May 9 12:12:34 UTC 2022
> >>>>> MKFS_OPTIONS  -- -f /dev/sda4
> >>>>> MOUNT_OPTIONS -- /dev/sda4 /mnt/scratch
> >>>>>
> >>>>> generic/633 58s ...  58s
> >>>>> generic/644 62s ...  60s
> >>>>> generic/645 161s ...  161s
> >>>>> generic/656 62s ...  63s
> >>>>> xfs/152 133s ...  133s
> >>>>> xfs/153 94s ...  92s
> >>>>> Ran: generic/633 generic/644 generic/645 generic/656 xfs/152 xfs/153
> >>>>> Passed all 6 tests
> >>>>
> >>>> I'm not sure if it's this series that has introduced a test bug or
> >>>> triggered a latent issue in the kernel, but I've started seeing
> >>>> generic/633 throw audit subsystem warnings on a single test machine
> >>>> as of late Friday:
> >>>>
> >>>> [ 7285.015888] WARNING: CPU: 3 PID: 2147118 at kernel/auditsc.c:2035 __audit_syscall_entry+0x113/0x140
> >>>
> >>> Does your kernel have this commit?
> >>>
> >>> commit 69e9cd66ae1392437234a63a3a1d60b6655f92ef
> >>> Author: Julian Orth <ju.orth@gmail.com>
> >>> Date:   Tue May 17 12:32:53 2022 +0200
> >>>
> >>>     audit,io_uring,io-wq: call __audit_uring_exit for dummy contexts
> > 
> > No, that wasn't in -rc7.
> > 
> >> I could not reproduce either with or without your patch when I finally
> >> got that test going and figure out how to turn on audit and get it
> >> enabled. I don't run with that.
> > 
> > Ok. Given that this has been broken for over a year and nobody
> > has noticed until late .18-rcX, it might be worth adding an audit
> > enabled VM to your io-uring test farm....
> 
> It was in the 5.16 release, so it's ~4 months ago. Don't disagree on the

Huh. The commit that it fixes is dated Feb 2021:

commit 5bd2182d58e9d9c6279b7a8a2f9b41add0e7f9cb
Author: Paul Moore <paul@paul-moore.com>
Date:   Tue Feb 16 19:46:48 2021 -0500

    audit,io_uring,io-wq: add some basic audit support to io_uring

I guess it must have sat in a tree somewhere for 6 months before
before being merged.

> testing, though I do think that's mostly on the audit side. I had no
> hand in any of that code.

Fair enough.

> From my experience trying to reproduce it yesterday, my test distros
> don't even enable it and you have to both fiddle the config and add a
> boot parameter to even turn it on. And then it still didn't trigger for
> me.

I have machines with audit enabled as it seems to be the debian
default these days. I haven't explicitly turned it on - it's just
there. I guess it came along with selinux being enabled on these
test VMs - I have "selinux=1 security=selinux" on the kernel CLI for
these VMs.

Apart from that, I have no clue as to why this one particular
VM tripped this and none of the others with similar selinux/audit
configs have had any problems...

> I'll see if I can add something to the testing mix for this.

Thanks!

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v2 00/13] rename & split tests
  2022-05-23 10:41           ` Dave Chinner
@ 2022-05-23 12:28             ` Jens Axboe
  0 siblings, 0 replies; 24+ messages in thread
From: Jens Axboe @ 2022-05-23 12:28 UTC (permalink / raw)
  To: Dave Chinner
  Cc: Christian Brauner, Zorro Lang, fstests, Eryu Guan,
	Amir Goldstein, Christoph Hellwig, Darrick J. Wong, io-uring

On 5/23/22 4:41 AM, Dave Chinner wrote:
>> From my experience trying to reproduce it yesterday, my test distros
>> don't even enable it and you have to both fiddle the config and add a
>> boot parameter to even turn it on. And then it still didn't trigger for
>> me.
> 
> I have machines with audit enabled as it seems to be the debian
> default these days. I haven't explicitly turned it on - it's just
> there. I guess it came along with selinux being enabled on these
> test VMs - I have "selinux=1 security=selinux" on the kernel CLI for
> these VMs.

Hmm, I am running debian on these. Anyway, I'll get one configured so
that it triggers this issue, and use that going forward. Maybe I need
selinux too, not just audit, and explicitly enable it with the boot
parameters.

-- 
Jens Axboe


^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2022-05-23 12:28 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-12 16:52 [PATCH v2 00/13] rename & split tests Christian Brauner
2022-05-12 16:52 ` [PATCH v2 01/13] idmapped-mounts: make all tests set their required feature flags Christian Brauner
2022-05-16  5:50   ` Christoph Hellwig
2022-05-12 16:52 ` [PATCH v2 02/13] src: rename idmapped-mounts folder Christian Brauner
2022-05-12 16:52 ` [PATCH v2 03/13] src/vfs: rename idmapped-mounts.c file Christian Brauner
2022-05-12 16:52 ` [PATCH v2 04/13] vfstest: rename struct t_idmapped_mounts Christian Brauner
2022-05-12 16:52 ` [PATCH v2 05/13] utils: add missing global.h include Christian Brauner
2022-05-12 16:52 ` [PATCH v2 07/13] utils: move helpers into utils Christian Brauner
2022-05-12 16:52 ` [PATCH v2 08/13] missing: move sys_execveat() to missing.h Christian Brauner
2022-05-12 16:52 ` [PATCH v2 09/13] utils: add struct test_suite Christian Brauner
2022-05-12 16:52 ` [PATCH v2 12/13] vfstest: split out remaining idmapped mount tests Christian Brauner
2022-05-12 16:52 ` [PATCH v2 13/13] vfs/idmapped-mounts: remove invalid test Christian Brauner
2022-05-16  5:53   ` Christoph Hellwig
2022-05-12 20:19 ` [PATCH v2 00/13] rename & split tests Zorro Lang
2022-05-14 17:59 ` Zorro Lang
2022-05-16  8:02   ` Christian Brauner
2022-05-21 23:13 ` Dave Chinner
2022-05-22  1:07   ` Jens Axboe
2022-05-22  2:19     ` Jens Axboe
2022-05-23  0:13       ` Dave Chinner
2022-05-23  0:57         ` Jens Axboe
2022-05-23 10:41           ` Dave Chinner
2022-05-23 12:28             ` Jens Axboe
2022-05-23  9:44   ` Christian Brauner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.