From 4ed5e25074ffec266df38556d9b3a928c5e0dee9 Mon Sep 17 00:00:00 2001 From: Will Andrews Date: Sun, 21 Feb 2021 10:19:43 -0600 Subject: Add Linux namespace delegation support This allows ZFS datasets to be delegated to a user/mount namespace Within that namespace, only the delegated datasets are visible Works very similarly to Zones/Jailes on other ZFS OSes As a user: ``` $ unshare -Um $ zfs list no datasets available $ echo $$ 1234 ``` As root: ``` # zfs list NAME ZONED MOUNTPOINT containers off /containers containers/host off /containers/host containers/host/child off /containers/host/child containers/host/child/gchild off /containers/host/child/gchild containers/unpriv on /unpriv containers/unpriv/child on /unpriv/child containers/unpriv/child/gchild on /unpriv/child/gchild # zfs zone /proc/1234/ns/user containers/unpriv ``` Back to the user namespace: ``` $ zfs list NAME USED AVAIL REFER MOUNTPOINT containers 129M 47.8G 24K /containers containers/unpriv 128M 47.8G 24K /unpriv containers/unpriv/child 128M 47.8G 128M /unpriv/child ``` Reviewed-by: Brian Behlendorf Signed-off-by: Will Andrews Signed-off-by: Allan Jude Signed-off-by: Mateusz Piotrowski Co-authored-by: Allan Jude Co-authored-by: Mateusz Piotrowski Sponsored-by: Buddy Closes #12263 --- lib/libspl/include/sys/types.h | 2 +- lib/libspl/include/zone.h | 12 ++++++- lib/libspl/os/linux/zone.c | 32 ++++++++++++++++- lib/libuutil/libuutil.abi | 2 +- lib/libzfs/libzfs.abi | 9 ++++- lib/libzfs/libzfs_util.c | 6 ++++ lib/libzfs/os/linux/libzfs_util_os.c | 69 ++++++++++++++++++++++++++++++++++++ lib/libzfs_core/libzfs_core.abi | 2 +- 8 files changed, 128 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/libspl/include/sys/types.h b/lib/libspl/include/sys/types.h index f32c2188a..8dc38ae33 100644 --- a/lib/libspl/include/sys/types.h +++ b/lib/libspl/include/sys/types.h @@ -44,7 +44,7 @@ #include #endif /* HAVE_INTTYPES */ -typedef int zoneid_t; +typedef uint_t zoneid_t; typedef int projid_t; /* diff --git a/lib/libspl/include/zone.h b/lib/libspl/include/zone.h index b0ac2d9bc..0af4e7a2f 100644 --- a/lib/libspl/include/zone.h +++ b/lib/libspl/include/zone.h @@ -33,7 +33,17 @@ extern "C" { #endif -#define GLOBAL_ZONEID 0 +#ifdef __FreeBSD__ +#define GLOBAL_ZONEID 0 +#else +/* + * Hardcoded in the kernel's root user namespace. A "better" way to get + * this would be by using ioctl_ns(2), but this would need to be performed + * recursively on NS_GET_PARENT and then NS_GET_USERNS. Also, that's only + * supported since Linux 4.9. + */ +#define GLOBAL_ZONEID 4026531837U +#endif extern zoneid_t getzoneid(void); diff --git a/lib/libspl/os/linux/zone.c b/lib/libspl/os/linux/zone.c index 393a16ad5..65c02dfe7 100644 --- a/lib/libspl/os/linux/zone.c +++ b/lib/libspl/os/linux/zone.c @@ -23,10 +23,40 @@ * Use is subject to license terms. */ +#include +#include +#include +#include +#include +#include + #include zoneid_t getzoneid(void) { - return (GLOBAL_ZONEID); + char path[PATH_MAX]; + char buf[128] = { '\0' }; + char *cp; + + int c = snprintf(path, sizeof (path), "/proc/self/ns/user"); + /* This API doesn't have any error checking... */ + if (c < 0) + return (0); + + ssize_t r = readlink(path, buf, sizeof (buf) - 1); + if (r < 0) + return (0); + + cp = strchr(buf, '['); + if (cp == NULL) + return (0); + cp++; + + unsigned long n = strtoul(cp, NULL, 10); + if (n == ULONG_MAX && errno == ERANGE) + return (0); + zoneid_t z = (zoneid_t)n; + + return (z); } diff --git a/lib/libuutil/libuutil.abi b/lib/libuutil/libuutil.abi index 86220b44b..766d88430 100644 --- a/lib/libuutil/libuutil.abi +++ b/lib/libuutil/libuutil.abi @@ -1081,7 +1081,7 @@ - + diff --git a/lib/libzfs/libzfs.abi b/lib/libzfs/libzfs.abi index 9f9a2f907..fb5e01b82 100644 --- a/lib/libzfs/libzfs.abi +++ b/lib/libzfs/libzfs.abi @@ -433,6 +433,7 @@ + @@ -1537,7 +1538,7 @@ - + @@ -4414,6 +4415,12 @@ + + + + + + diff --git a/lib/libzfs/libzfs_util.c b/lib/libzfs/libzfs_util.c index 1c067e214..1d40cbbfc 100644 --- a/lib/libzfs/libzfs_util.c +++ b/lib/libzfs/libzfs_util.c @@ -299,6 +299,9 @@ libzfs_error_description(libzfs_handle_t *hdl) case EZFS_VDEV_NOTSUP: return (dgettext(TEXT_DOMAIN, "operation not supported " "on this type of vdev")); + case EZFS_NOT_USER_NAMESPACE: + return (dgettext(TEXT_DOMAIN, "the provided file " + "was not a user namespace file")); case EZFS_UNKNOWN: return (dgettext(TEXT_DOMAIN, "unknown error")); default: @@ -485,6 +488,9 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...) case ZFS_ERR_BADPROP: zfs_verror(hdl, EZFS_BADPROP, fmt, ap); break; + case ZFS_ERR_NOT_USER_NAMESPACE: + zfs_verror(hdl, EZFS_NOT_USER_NAMESPACE, fmt, ap); + break; default: zfs_error_aux(hdl, "%s", strerror(error)); zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap); diff --git a/lib/libzfs/os/linux/libzfs_util_os.c b/lib/libzfs/os/linux/libzfs_util_os.c index 9d6f574a5..7bd26ea98 100644 --- a/lib/libzfs/os/linux/libzfs_util_os.c +++ b/lib/libzfs/os/linux/libzfs_util_os.c @@ -19,6 +19,9 @@ * CDDL HEADER END */ +/* + * Copyright (c) 2021 Klara, Inc. + */ #include #include @@ -207,3 +210,69 @@ zfs_version_kernel(void) ret[read - 1] = '\0'; return (ret); } + +/* + * Add or delete the given filesystem to/from the given user namespace. + */ +int +zfs_userns(zfs_handle_t *zhp, const char *nspath, int attach) +{ + libzfs_handle_t *hdl = zhp->zfs_hdl; + zfs_cmd_t zc = {"\0"}; + char errbuf[1024]; + unsigned long cmd; + int ret; + + if (attach) { + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot add '%s' to namespace"), + zhp->zfs_name); + } else { + (void) snprintf(errbuf, sizeof (errbuf), + dgettext(TEXT_DOMAIN, "cannot remove '%s' from namespace"), + zhp->zfs_name); + } + + switch (zhp->zfs_type) { + case ZFS_TYPE_VOLUME: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "volumes can not be namespaced")); + return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); + case ZFS_TYPE_SNAPSHOT: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "snapshots can not be namespaced")); + return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); + case ZFS_TYPE_BOOKMARK: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "bookmarks can not be namespaced")); + return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); + case ZFS_TYPE_VDEV: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "vdevs can not be namespaced")); + return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); + case ZFS_TYPE_INVALID: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "invalid zfs_type_t: ZFS_TYPE_INVALID")); + return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); + case ZFS_TYPE_POOL: + zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, + "pools can not be namespaced")); + return (zfs_error(hdl, EZFS_BADTYPE, errbuf)); + case ZFS_TYPE_FILESYSTEM: + zfs_fallthrough; + } + assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM); + + (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name)); + zc.zc_objset_type = DMU_OST_ZFS; + zc.zc_cleanup_fd = open(nspath, O_RDONLY); + if (zc.zc_cleanup_fd < 0) { + return (zfs_error(hdl, EZFS_NOT_USER_NAMESPACE, errbuf)); + } + + cmd = attach ? ZFS_IOC_USERNS_ATTACH : ZFS_IOC_USERNS_DETACH; + if ((ret = zfs_ioctl(hdl, cmd, &zc)) != 0) + zfs_standard_error(hdl, errno, errbuf); + + return (ret); +} diff --git a/lib/libzfs_core/libzfs_core.abi b/lib/libzfs_core/libzfs_core.abi index 266007e4d..fae98469a 100644 --- a/lib/libzfs_core/libzfs_core.abi +++ b/lib/libzfs_core/libzfs_core.abi @@ -939,7 +939,7 @@ - + -- cgit v1.2.3