diff options
author | youzhongyang <[email protected]> | 2022-11-08 13:28:56 -0500 |
---|---|---|
committer | GitHub <[email protected]> | 2022-11-08 10:28:56 -0800 |
commit | f224eddf922a33ca4b86d83148e9e6fa155fc290 (patch) | |
tree | fbd3d2703fc4c00f0431c6e261c41b037f3f63ab /tests | |
parent | 109731cd73c56c378b4c71732b9b9d3504a7a7e1 (diff) |
Support idmapped mount in user namespace
Linux 5.17 commit torvalds/linux@5dfbfe71e enables "the idmapping
infrastructure to support idmapped mounts of filesystems mounted
with an idmapping". Update the OpenZFS accordingly to improve the
idmapped mount support.
This pull request contains the following changes:
- xattr setter functions are fixed to take mnt_ns argument. Without
this, cp -p would fail for an idmapped mount in a user namespace.
- idmap_util is enhanced/fixed for its use in a user ns context.
- One test case added to test idmapped mount in a user ns.
Reviewed-by: Christian Brauner <[email protected]>
Reviewed-by: Brian Behlendorf <[email protected]>
Signed-off-by: Youzhong Yang <[email protected]>
Closes #14097
Diffstat (limited to 'tests')
-rw-r--r-- | tests/runfiles/linux.run | 2 | ||||
-rwxr-xr-x | tests/test-runner/bin/zts-report.py.in | 1 | ||||
-rw-r--r-- | tests/zfs-tests/cmd/idmap_util.c | 49 | ||||
-rw-r--r-- | tests/zfs-tests/tests/Makefile.am | 3 | ||||
-rwxr-xr-x | tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_005.ksh | 138 |
5 files changed, 176 insertions, 17 deletions
diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run index 13f7efd96..52e824a88 100644 --- a/tests/runfiles/linux.run +++ b/tests/runfiles/linux.run @@ -200,5 +200,5 @@ tags = ['functional', 'zvol', 'zvol_misc'] [tests/functional/idmap_mount:Linux] tests = ['idmap_mount_001', 'idmap_mount_002', 'idmap_mount_003', - 'idmap_mount_004'] + 'idmap_mount_004', 'idmap_mount_005'] tags = ['functional', 'idmap_mount'] diff --git a/tests/test-runner/bin/zts-report.py.in b/tests/test-runner/bin/zts-report.py.in index 1cebf5082..66b041b6e 100755 --- a/tests/test-runner/bin/zts-report.py.in +++ b/tests/test-runner/bin/zts-report.py.in @@ -284,6 +284,7 @@ elif sys.platform.startswith('linux'): 'idmap_mount/idmap_mount_002': ['SKIP', idmap_reason], 'idmap_mount/idmap_mount_003': ['SKIP', idmap_reason], 'idmap_mount/idmap_mount_004': ['SKIP', idmap_reason], + 'idmap_mount/idmap_mount_005': ['SKIP', idmap_reason], }) diff --git a/tests/zfs-tests/cmd/idmap_util.c b/tests/zfs-tests/cmd/idmap_util.c index b6ca111ad..49483cbaa 100644 --- a/tests/zfs-tests/cmd/idmap_util.c +++ b/tests/zfs-tests/cmd/idmap_util.c @@ -36,6 +36,7 @@ #include <errno.h> #include <sched.h> #include <syscall.h> +#include <sys/socket.h> #include <sys/list.h> @@ -458,43 +459,56 @@ userns_fd_from_idmap(list_t *head) { pid_t pid; int ret, fd; - int pipe_fd[2]; + int fds[2]; char c; int saved_errno = 0; - /* pipe for bidirectional communication */ - ret = pipe(pipe_fd); + /* socketpair for bidirectional communication */ + ret = socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, fds); if (ret) { - log_errno("pipe"); + log_errno("socketpair"); return (-errno); } pid = fork(); if (pid < 0) { log_errno("fork"); - return (-errno); + fd = -errno; + goto out; } if (pid == 0) { /* child process */ - close(pipe_fd[0]); ret = unshare(CLONE_NEWUSER); if (ret == 0) { /* notify the parent of success */ - ret = write_buf(pipe_fd[1], "1", 1); + ret = write_buf(fds[1], "1", 1); + if (ret < 0) + saved_errno = errno; + else { + /* + * Until the parent has written to idmap, + * we cannot exit, otherwise the defunct + * process is owned by the real root, writing + * to its idmap ends up with EPERM in the + * context of a user ns + */ + ret = read_buf(fds[1], &c, 1); + if (ret < 0) + saved_errno = errno; + } } else { saved_errno = errno; log_errno("unshare"); - ret = write_buf(pipe_fd[1], "0", 1); + ret = write_buf(fds[1], "0", 1); + if (ret < 0) + saved_errno = errno; } - if (ret < 0) - saved_errno = errno; - close(pipe_fd[1]); exit(saved_errno); } + /* parent process */ - close(pipe_fd[1]); - ret = read_buf(pipe_fd[0], &c, 1); + ret = read_buf(fds[0], &c, 1); if (ret == 1 && c == '1') { ret = write_pid_idmaps(pid, head); if (!ret) { @@ -504,11 +518,15 @@ userns_fd_from_idmap(list_t *head) } else { fd = -ret; } + /* Let child know it can exit */ + (void) write_buf(fds[0], "1", 1); } else { fd = -EBADF; } - close(pipe_fd[0]); (void) wait_for_pid(pid); +out: + close(fds[0]); + close(fds[1]); return (fd); } @@ -532,7 +550,8 @@ is_idmap_supported(char *path) }; /* strtok_r() won't be happy with a const string */ - char *input = strdup("b:0:1000000:1000000"); + /* To check if idmapped mount can be done in a user ns, map 0 to 0 */ + char *input = strdup("b:0:0:1"); if (!input) { errno = ENOMEM; diff --git a/tests/zfs-tests/tests/Makefile.am b/tests/zfs-tests/tests/Makefile.am index 2d9902775..5b8458b73 100644 --- a/tests/zfs-tests/tests/Makefile.am +++ b/tests/zfs-tests/tests/Makefile.am @@ -2006,4 +2006,5 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/idmap_mount/idmap_mount_001.ksh \ functional/idmap_mount/idmap_mount_002.ksh \ functional/idmap_mount/idmap_mount_003.ksh \ - functional/idmap_mount/idmap_mount_004.ksh + functional/idmap_mount/idmap_mount_004.ksh \ + functional/idmap_mount/idmap_mount_005.ksh diff --git a/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_005.ksh b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_005.ksh new file mode 100755 index 000000000..a4ecea92c --- /dev/null +++ b/tests/zfs-tests/tests/functional/idmap_mount/idmap_mount_005.ksh @@ -0,0 +1,138 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or https://opensource.org/licenses/CDDL-1.0. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +. $STF_SUITE/tests/functional/idmap_mount/idmap_mount_common.kshlib + +# +# +# DESCRIPTION: +# Test idmapped mount in a user namespace +# +# STRATEGY: +# 1. Create a zoned dataset +# 2. Create a user namespace and designate the dataset to the zone +# 3. In the zone, mount the dataset to "idmap_test" +# 4. In the zone, idmap mount the dataset mountpoint to "idmap_dest" +# 5. Do some file operations in the idmapped mountpoint "idmap_dest" +# 6. Check the owner of files/folder in the mount point "idmap_test" +# 7. unmount the mountpoints in the zone +# 8. Remount the dataset in global zone to "idmap_test" +# 9. Check the owenr of filers/folder in the mountpoint "idmap_test" +# + +verify_runnable "global" + +export WORKDIR=$TESTDIR/idmap_test +export IDMAPDIR=$TESTDIR/idmap_dest + +function cleanup +{ + if [[ -v unshared_pid ]]; then + zfs unzone /proc/$unshared_pid/ns/user "$TESTPOOL/userns" + kill -TERM ${unshared_pid} + fi + if mountpoint $WORKDIR; then + log_must umount $WORKDIR + fi + log_must rm -rf $WORKDIR +} + +log_onexit cleanup + +if ! idmap_util -c $TESTDIR; then + log_unsupported "Idmap mount not supported." +fi + +unshare -Urm echo test +if [ "$?" -ne "0" ]; then + log_unsupported "Failed to create user namespace" +fi + +log_must zfs create -o zoned=off -o mountpoint=$WORKDIR "$TESTPOOL/userns" + +# "root" user and group in the user ns +log_must chown 1000000:1000000 $WORKDIR +log_must zfs set zoned=on "$TESTPOOL/userns" + +log_must mkdir -p $IDMAPDIR + +unshare -Um /usr/bin/sleep 2h & +unshared_pid=$! +if [ "$?" -ne "0" ]; then + log_unsupported "Failed to create user namespace" +fi +# wait for userns to be ready +sleep 1 +echo "0 1000000 1000000" > /proc/$unshared_pid/uid_map +if [ "$?" -ne "0" ]; then + log_unsupported "Failed to write to uid_map" +fi +echo "0 1000000 1000000" > /proc/$unshared_pid/gid_map +if [ "$?" -ne "0" ]; then + log_unsupported "Failed to write to gid_map" +fi + +NSENTER="nsenter -t $unshared_pid --all -S 0 -G 0" + +log_must zfs zone /proc/$unshared_pid/ns/user "$TESTPOOL/userns" +log_must $NSENTER zfs mount "$TESTPOOL/userns" +log_must $NSENTER chmod 777 $WORKDIR + +$NSENTER idmap_util -c $WORKDIR +if [ "$?" -ne "0" ]; then + log_unsupported "Idmapped mount not supported in a user namespace" +fi + +log_must $NSENTER idmap_util -m b:0:10000:100000 $WORKDIR $IDMAPDIR +log_must $NSENTER setpriv --reuid 11000 --regid 11000 --clear-groups touch $IDMAPDIR/file +log_must $NSENTER setpriv --reuid 11000 --regid 11000 --clear-groups mkdir $IDMAPDIR/folder +log_must $NSENTER setpriv --reuid 11000 --regid 11000 --clear-groups ln -s file $IDMAPDIR/file-soft +log_must $NSENTER setpriv --reuid 11000 --regid 11000 --clear-groups ln $IDMAPDIR/file $IDMAPDIR/file-hard + +log_must $NSENTER setpriv --reuid 11000 --regid 11000 --clear-groups cp -p $IDMAPDIR/file $IDMAPDIR/folder/file-p +log_must $NSENTER setpriv --reuid 11000 --regid 11000 --clear-groups cp $IDMAPDIR/file $IDMAPDIR/folder/file + +log_must test "1000 1000" = "$($NSENTER stat -c '%u %g' $WORKDIR/file)" +log_must test "1000 1000" = "$($NSENTER stat -c '%u %g' $WORKDIR/folder)" +log_must test "1000 1000" = "$($NSENTER stat -c '%u %g' $WORKDIR/file-soft)" +log_must test "1000 1000" = "$($NSENTER stat -c '%u %g' $WORKDIR/file-hard)" +log_must test "1000 1000" = "$($NSENTER stat -c '%u %g' $WORKDIR/folder/file-p)" +log_must test "1000 1000" = "$($NSENTER stat -c '%u %g' $WORKDIR/folder/file)" + +log_must $NSENTER umount $IDMAPDIR +log_must $NSENTER umount $WORKDIR + +log_must zfs unzone /proc/$unshared_pid/ns/user "$TESTPOOL/userns" +log_must kill -TERM $unshared_pid +unset unshared_pid +log_must zfs set zoned=off "$TESTPOOL/userns" +log_must zfs mount "$TESTPOOL/userns" + +log_must test "1001000 1001000" = "$(stat -c '%u %g' $WORKDIR/file)" +log_must test "1001000 1001000" = "$(stat -c '%u %g' $WORKDIR/folder)" +log_must test "1001000 1001000" = "$(stat -c '%u %g' $WORKDIR/file-soft)" +log_must test "1001000 1001000" = "$(stat -c '%u %g' $WORKDIR/file-hard)" +log_must test "1001000 1001000" = "$(stat -c '%u %g' $WORKDIR/folder/file-p)" +log_must test "1001000 1001000" = "$(stat -c '%u %g' $WORKDIR/folder/file)" + +log_pass "Testing idmapped mount in a user ns is successful." + |