generic: add fstests for idmapped mounts

Add a test suite to verify the behavior of idmapped mounts. The test
suite also includes a range of vfs tests to verify that no regressions
are introduced by idmapped mounts. The following tests are currently
available with more to come in the future:

01. posix acls on regular and idmapped mounts
02. create operations in user namespace
03. device node creation in user namespace
04. expected ownership on idmapped mounts
05. fscaps on regular mounts
06. fscaps on idmapped mounts
07. fscaps on idmapped mounts in user namespace
08. fscaps on idmapped mounts in user namespace
    with different id mappings
09. mapped fsids
10. unmapped fsids
11. cross mount hardlink
12. cross idmapped mount hardlink
13. hardlinks from idmapped mounts
14. hardlinks from idmapped mounts in user namespace
15. io_uring
16. io_uring in user namespace
17. io_uring from idmapped mounts
18. io_uring from idmapped mounts in user namespace
19. io_uring from idmapped mounts with unmapped ids
20. io_uring from idmapped mounts with unmapped ids in user namespace
21. following protected symlinks on regular mounts
22. following protected symlinks on idmapped mounts
23. following protected symlinks on idmapped mounts in user namespace
24. cross mount rename
25. cross idmapped mount rename
26. rename from idmapped mounts
27. rename from idmapped mounts in user namespace
28. symlink from regular mounts
29. symlink from idmapped mounts
30. symlink from idmapped mounts in user namespace
31. setid binaries on regular mounts
32. setid binaries on idmapped mounts
33. setid binaries on idmapped mounts in user namespace
34. setid binaries on idmapped mounts in user namespace
    with different id mappings
35. sticky bit unlink operations on regular mounts
36. sticky bit unlink operations on idmapped mounts
37. sticky bit unlink operations on idmapped mounts in user namespace
38. sticky bit rename operations on regular mounts
39. sticky bit rename operations on idmapped mounts
40. sticky bit rename operations on idmapped mounts in user namespace
41. create operations in directories with setgid bit set
42. create operations in directories with setgid bit set
    on idmapped mounts
43. create operations in directories with setgid bit set
    on idmapped mounts in user namespace
44. verify create operations and ownership with racing threads idmapping
    a mount
45. setattr truncate operations on regular mounts
46. setattr truncate operations on idmapped mounts
47. setattr truncate operations on idmapped mounts in user namespace

Here's some sample output when running with DEBUG_TRACE defined:

Cc: Amir Goldstein <amir73il@gmail.com>
Cc: Eryu Guan <guan@eryu.me>
Cc: fstests@vger.kernel.org
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
Signed-off-by: Eryu Guan <guaneryu@gmail.com>
This commit is contained in:
Christian Brauner
2021-04-12 13:57:04 +02:00
committed by Eryu Guan
parent a7f90c1433
commit 0d1af68e1a
18 changed files with 9398 additions and 70 deletions
+1
View File
@@ -178,6 +178,7 @@
/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/log-writes/replay-log
/src/perf/*.pyc
+3 -2
View File
@@ -8,13 +8,14 @@ _______________________
sudo apt-get install xfslibs-dev uuid-dev libtool-bin \
e2fsprogs automake gcc libuuid1 quota attr make \
libacl1-dev libaio-dev xfsprogs libgdbm-dev gawk fio dbench \
uuid-runtime python sqlite3 liburing-dev
uuid-runtime python sqlite3 liburing-dev libcap-dev
For Fedora, RHEL, or CentOS:
yum install acl attr automake bc dbench dump e2fsprogs fio \
gawk gcc indent libtool lvm2 make psmisc quota sed \
xfsdump xfsprogs \
libacl-devel libaio-devel libuuid-devel \
xfsprogs-devel btrfs-progs-devel python sqlite liburing-devel
xfsprogs-devel btrfs-progs-devel python sqlite liburing-devel \
libcap-devel
(Older distributions may require xfsprogs-qa-devel as well.)
(Note that for RHEL and CentOS, you may need the EPEL repo.)
- run make
+34
View File
@@ -1980,6 +1980,40 @@ _require_io_uring()
esac
}
# test whether the mount_setattr syscall is available
_require_mount_setattr()
{
$here/src/feature -r
case $? in
0)
;;
1)
_notrun "kernel does not support mount_setattr syscall"
;;
*)
_fail "unexpected error testing for mount_setattr support"
;;
esac
}
# test whether idmapped mounts are supported
_require_idmapped_mounts()
{
IDMAPPED_MOUNTS_TEST=$here/src/idmapped-mounts/idmapped-mounts
[ -x $IDMAPPED_MOUNTS_TEST ] || _notrun "idmapped-mounts utilities required"
_require_mount_setattr
$here/src/idmapped-mounts/idmapped-mounts --supported \
--device "$TEST_DEV" \
--mount "$TEST_DIR" \
--fstype "$FSTYP"
if [ $? -ne 0 ]; then
_notrun "idmapped-mounts not support by $FSTYP"
fi
}
# this test requires that a test program exists under src/
# $1 - command (require)
#
+2
View File
@@ -58,6 +58,7 @@ AC_PACKAGE_NEED_ACLINIT_LIBACL
AC_PACKAGE_WANT_GDBM
AC_PACKAGE_WANT_AIO
AC_PACKAGE_WANT_URING
AC_PACKAGE_WANT_LIBCAP
AC_PACKAGE_WANT_LINUX_FIEMAP_H
AC_PACKAGE_WANT_FALLOCATE
AC_PACKAGE_WANT_OPEN_BY_HANDLE_AT
@@ -68,6 +69,7 @@ AC_PACKAGE_WANT_LIBBTRFSUTIL
AC_HAVE_COPY_FILE_RANGE
AC_CHECK_FUNCS([renameat2])
AC_CHECK_TYPES([struct mount_attr], [], [], [[#include <linux/mount.h>]])
AC_CONFIG_HEADER(include/config.h)
AC_CONFIG_FILES([include/builddefs])
+1
View File
@@ -61,6 +61,7 @@ ENABLE_SHARED = @enable_shared@
HAVE_DB = @have_db@
HAVE_AIO = @have_aio@
HAVE_URING = @have_uring@
HAVE_LIBCAP = @have_libcap@
HAVE_FALLOCATE = @have_fallocate@
HAVE_OPEN_BY_HANDLE_AT = @have_open_by_handle_at@
HAVE_FIEMAP = @have_fiemap@
+1
View File
@@ -11,6 +11,7 @@ LSRCFILES = \
package_aiodev.m4 \
package_gdbmdev.m4 \
package_globals.m4 \
package_libcap.m4 \
package_libcdev.m4 \
package_liburing.m4 \
package_ncurses.m4 \
+4
View File
@@ -0,0 +1,4 @@
AC_DEFUN([AC_PACKAGE_WANT_LIBCAP],
[ AC_CHECK_HEADERS(sys/capability.h, [ have_libcap=true ], [ have_libcap=false ])
AC_SUBST(have_libcap)
])
+5
View File
@@ -71,6 +71,11 @@ ifeq ($(HAVE_URING), true)
LLDLIBS += -luring
endif
SUBDIRS += idmapped-mounts
ifeq ($(HAVE_LIBCAP), true)
LLDLIBS += -lcap
endif
CFILES = $(TARGETS:=.c)
LDIRT = $(TARGETS) fssum
+1 -64
View File
@@ -26,70 +26,7 @@
#include <sys/types.h>
#include <unistd.h>
/* open_tree() */
#ifndef OPEN_TREE_CLONE
#define OPEN_TREE_CLONE 1
#endif
#ifndef OPEN_TREE_CLOEXEC
#define OPEN_TREE_CLOEXEC O_CLOEXEC
#endif
#ifndef __NR_open_tree
#if defined __alpha__
#define __NR_open_tree 538
#elif defined _MIPS_SIM
#if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */
#define __NR_open_tree 4428
#endif
#if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */
#define __NR_open_tree 6428
#endif
#if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */
#define __NR_open_tree 5428
#endif
#elif defined __ia64__
#define __NR_open_tree (428 + 1024)
#else
#define __NR_open_tree 428
#endif
#endif
/* move_mount() */
#ifndef MOVE_MOUNT_F_EMPTY_PATH
#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
#endif
#ifndef __NR_move_mount
#if defined __alpha__
#define __NR_move_mount 539
#elif defined _MIPS_SIM
#if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */
#define __NR_move_mount 4429
#endif
#if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */
#define __NR_move_mount 6429
#endif
#if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */
#define __NR_move_mount 5429
#endif
#elif defined __ia64__
#define __NR_move_mount (428 + 1024)
#else
#define __NR_move_mount 429
#endif
#endif
static inline int sys_open_tree(int dfd, const char *filename, unsigned int flags)
{
return syscall(__NR_open_tree, dfd, filename, flags);
}
static inline int sys_move_mount(int from_dfd, const char *from_pathname, int to_dfd,
const char *to_pathname, unsigned int flags)
{
return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags);
}
#include "idmapped-mounts/missing.h"
static bool is_shared_mountpoint(const char *path)
{
+35 -3
View File
@@ -19,6 +19,7 @@
*
* Test for machine features
* -A test whether AIO syscalls are available
* -r test whether mount_setattr syscall is supported
* -R test whether IO_URING syscalls are available
* -o report a number of online cpus
* -s report pagesize
@@ -30,6 +31,7 @@
#include <sys/quota.h>
#include <sys/resource.h>
#include <signal.h>
#include <syscall.h>
#include <unistd.h>
#ifdef HAVE_XFS_XQM_H
@@ -44,6 +46,8 @@
#include <liburing.h>
#endif
#include "idmapped-mounts/missing.h"
#ifndef USRQUOTA
#define USRQUOTA 0
#endif
@@ -64,7 +68,7 @@ usage(void)
fprintf(stderr, "Usage: feature [-v] -<q|u|g|p|U|G|P> <filesystem>\n");
fprintf(stderr, " feature [-v] -c <file>\n");
fprintf(stderr, " feature [-v] -t <file>\n");
fprintf(stderr, " feature -A | -R | -o | -s | -w\n");
fprintf(stderr, " feature -A | -r | -R | -o | -s | -w\n");
exit(1);
}
@@ -243,6 +247,27 @@ check_uring_support(void)
#endif
}
static int
check_mount_setattr_support(void)
{
int err;
struct mount_attr attr = {
.attr_set = MOUNT_ATTR_IDMAP,
.userns_fd = -EBADF,
};
/* mount_setattr() syscall not supported. */
err = sys_mount_setattr(-EBADF, "", AT_EMPTY_PATH, NULL, 0);
if (err && errno == ENOSYS)
return 1;
/* idmapped mounts not supported */
err = sys_mount_setattr(-EBADF, ".", AT_EMPTY_PATH, &attr, sizeof(attr));
if (err && errno == E2BIG)
return 1;
return 0;
}
int
main(int argc, char **argv)
@@ -256,6 +281,7 @@ main(int argc, char **argv)
int pflag = 0;
int Pflag = 0;
int qflag = 0;
int rflag = 0;
int Rflag = 0;
int sflag = 0;
int uflag = 0;
@@ -264,7 +290,7 @@ main(int argc, char **argv)
int oflag = 0;
char *fs = NULL;
while ((c = getopt(argc, argv, "ActgGopPqRsuUvw")) != EOF) {
while ((c = getopt(argc, argv, "ActgGopPqrRsuUvw")) != EOF) {
switch (c) {
case 'A':
Aflag++;
@@ -293,6 +319,9 @@ main(int argc, char **argv)
case 'q':
qflag++;
break;
case 'r':
rflag++;
break;
case 'R':
Rflag++;
break;
@@ -321,7 +350,7 @@ main(int argc, char **argv)
if (optind != argc-1) /* need a device */
usage();
fs = argv[argc-1];
} else if (Aflag || Rflag || wflag || sflag || oflag) {
} else if (Aflag || rflag || Rflag || wflag || sflag || oflag) {
if (optind != argc)
usage();
} else
@@ -349,6 +378,9 @@ main(int argc, char **argv)
if (Aflag)
return(check_aio_support());
if (rflag)
return(check_mount_setattr_support());
if (Rflag)
return(check_uring_support());
+35
View File
@@ -0,0 +1,35 @@
# SPDX-License-Identifier: GPL-2.0
TOPDIR = ../..
include $(TOPDIR)/include/builddefs
TARGETS = idmapped-mounts
CFILES = idmapped-mounts.c utils.c
HFILES = missing.h utils.h
LLDLIBS += -pthread
LDIRT = $(TARGETS)
ifeq ($(HAVE_LIBCAP), true)
LLDLIBS += -lcap
endif
ifeq ($(HAVE_URING), true)
LLDLIBS += -luring
endif
default: depend $(TARGETS)
depend: .dep
include $(BUILDRULES)
$(TARGETS): $(CFILES)
@echo " [CC] $@"
$(Q)$(LTLINK) $(CFILES) -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
-include .dep
File diff suppressed because it is too large Load Diff
+151
View File
@@ -0,0 +1,151 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __IDMAP_MISSING_H
#define __IDMAP_MISSING_H
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "../global.h"
#include <errno.h>
#include <linux/types.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <syscall.h>
#include <unistd.h>
#ifndef __NR_mount_setattr
#if defined __alpha__
#define __NR_mount_setattr 552
#elif defined _MIPS_SIM
#if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */
#define __NR_mount_setattr (442 + 4000)
#endif
#if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */
#define __NR_mount_setattr (442 + 6000)
#endif
#if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */
#define __NR_mount_setattr (442 + 5000)
#endif
#elif defined __ia64__
#define __NR_mount_setattr (442 + 1024)
#else
#define __NR_mount_setattr 442
#endif
#endif
#ifndef __NR_open_tree
#if defined __alpha__
#define __NR_open_tree 538
#elif defined _MIPS_SIM
#if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */
#define __NR_open_tree 4428
#endif
#if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */
#define __NR_open_tree 6428
#endif
#if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */
#define __NR_open_tree 5428
#endif
#elif defined __ia64__
#define __NR_open_tree (428 + 1024)
#else
#define __NR_open_tree 428
#endif
#endif
#ifndef __NR_move_mount
#if defined __alpha__
#define __NR_move_mount 539
#elif defined _MIPS_SIM
#if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */
#define __NR_move_mount 4429
#endif
#if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */
#define __NR_move_mount 6429
#endif
#if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */
#define __NR_move_mount 5429
#endif
#elif defined __ia64__
#define __NR_move_mount (428 + 1024)
#else
#define __NR_move_mount 429
#endif
#endif
#ifndef MNT_DETACH
#define MNT_DETACH 2
#endif
#ifndef MS_REC
#define MS_REC 1638
#endif
#ifndef MS_PRIVATE
#define MS_PRIVATE (1 << 18)
#endif
#ifndef MOVE_MOUNT_F_EMPTY_PATH
#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
#endif
#ifndef MOUNT_ATTR_IDMAP
#define MOUNT_ATTR_IDMAP 0x00100000
#endif
#ifndef OPEN_TREE_CLONE
#define OPEN_TREE_CLONE 1
#endif
#ifndef OPEN_TREE_CLOEXEC
#define OPEN_TREE_CLOEXEC O_CLOEXEC
#endif
#ifndef AT_RECURSIVE
#define AT_RECURSIVE 0x8000 /* Apply to the entire subtree */
#endif
#ifndef HAVE_STRUCT_MOUNT_ATTR
struct mount_attr {
__u64 attr_set;
__u64 attr_clr;
__u64 propagation;
__u64 userns_fd;
};
#endif
static inline int sys_mount_setattr(int dfd, const char *path, unsigned int flags,
struct mount_attr *attr, size_t size)
{
return syscall(__NR_mount_setattr, dfd, path, flags, attr, size);
}
static inline int sys_open_tree(int dfd, const char *filename, unsigned int flags)
{
return syscall(__NR_open_tree, dfd, filename, flags);
}
static inline int sys_move_mount(int from_dfd, const char *from_pathname, int to_dfd,
const char *to_pathname, unsigned int flags)
{
return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags);
}
static inline int sys_mount(const char *source, const char *target, const char *fstype,
unsigned long int flags, const void *data)
{
return syscall(__NR_mount, source, target, fstype, flags, data);
}
static inline int sys_umount2(const char *path, int flags)
{
return syscall(__NR_umount2, path, flags);
}
#endif /* __IDMAP_MISSING_H */
+134
View File
@@ -0,0 +1,134 @@
// SPDX-License-Identifier: GPL-2.0
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <fcntl.h>
#include <linux/limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "utils.h"
ssize_t read_nointr(int fd, void *buf, size_t count)
{
ssize_t ret;
do {
ret = read(fd, buf, count);
} while (ret < 0 && errno == EINTR);
return ret;
}
ssize_t write_nointr(int fd, const void *buf, size_t count)
{
ssize_t ret;
do {
ret = write(fd, buf, count);
} while (ret < 0 && errno == EINTR);
return ret;
}
static int write_file(const char *path, const void *buf, size_t count)
{
int fd;
ssize_t ret;
fd = open(path, O_WRONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW);
if (fd < 0)
return -1;
ret = write_nointr(fd, buf, count);
close(fd);
if (ret < 0 || (size_t)ret != count)
return -1;
return 0;
}
static int map_ids(pid_t pid, unsigned long nsid, unsigned long hostid,
unsigned long range)
{
char map[100], procfile[256];
snprintf(procfile, sizeof(procfile), "/proc/%d/uid_map", pid);
snprintf(map, sizeof(map), "%lu %lu %lu", nsid, hostid, range);
if (write_file(procfile, map, strlen(map)))
return -1;
snprintf(procfile, sizeof(procfile), "/proc/%d/gid_map", pid);
snprintf(map, sizeof(map), "%lu %lu %lu", nsid, hostid, range);
if (write_file(procfile, map, strlen(map)))
return -1;
return 0;
}
#define __STACK_SIZE (8 * 1024 * 1024)
pid_t do_clone(int (*fn)(void *), void *arg, int flags)
{
void *stack;
stack = malloc(__STACK_SIZE);
if (!stack)
return -ENOMEM;
#ifdef __ia64__
return __clone2(fn, stack, __STACK_SIZE, flags | SIGCHLD, arg, NULL);
#else
return clone(fn, stack + __STACK_SIZE, flags | SIGCHLD, arg, NULL);
#endif
}
static int get_userns_fd_cb(void *data)
{
return kill(getpid(), SIGSTOP);
}
int get_userns_fd(unsigned long nsid, unsigned long hostid, unsigned long range)
{
int ret;
pid_t pid;
char path[256];
pid = do_clone(get_userns_fd_cb, NULL, CLONE_NEWUSER);
if (pid < 0)
return -errno;
ret = map_ids(pid, nsid, hostid, range);
if (ret < 0)
return ret;
snprintf(path, sizeof(path), "/proc/%d/ns/user", pid);
ret = open(path, O_RDONLY | O_CLOEXEC);
kill(pid, SIGKILL);
wait_for_pid(pid);
return ret;
}
int wait_for_pid(pid_t pid)
{
int status, ret;
again:
ret = waitpid(pid, &status, 0);
if (ret == -1) {
if (errno == EINTR)
goto again;
return -1;
}
if (!WIFEXITED(status))
return -1;
return WEXITSTATUS(status);
}
+29
View File
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __IDMAP_UTILS_H
#define __IDMAP_UTILS_H
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <errno.h>
#include <linux/types.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include "missing.h"
extern pid_t do_clone(int (*fn)(void *), void *arg, int flags);
extern int get_userns_fd(unsigned long nsid, unsigned long hostid,
unsigned long range);
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);
#endif /* __IDMAP_UTILS_H */
+42
View File
@@ -0,0 +1,42 @@
#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2021 Christian Brauner. All Rights Reserved.
#
# FS QA Test 633
#
# Test that idmapped mounts behave correctly.
#
seq=`basename $0`
seqres=$RESULT_DIR/$seq
echo "QA output created by $seq"
here=`pwd`
tmp=/tmp/$$
status=1 # failure is the default!
trap "_cleanup; exit \$status" 0 1 2 3 15
_cleanup()
{
cd /
rm -f $tmp.*
}
# get standard environment, filters and checks
. ./common/rc
. ./common/filter
# remove previous $seqres.full before test
rm -f $seqres.full
# real QA test starts here
_supported_fs generic
_require_idmapped_mounts
_require_test
echo "Silence is golden"
$here/src/idmapped-mounts/idmapped-mounts --device "$TEST_DEV" --mount "$TEST_DIR" --fstype "$FSTYP"
status=$?
exit
+2
View File
@@ -0,0 +1,2 @@
QA output created by 633
Silence is golden
+1
View File
@@ -635,3 +635,4 @@
630 auto quick rw dedupe clone
631 auto rw overlay rename
632 auto quick mount
633 auto quick atime attr cap idmapped io_uring mount perms rw unlink