diff options
author | Will Andrews <[email protected]> | 2021-02-21 10:19:43 -0600 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2022-06-10 09:51:46 -0700 |
commit | 4ed5e25074ffec266df38556d9b3a928c5e0dee9 (patch) | |
tree | 930f2397ca27e885ca33c05728802d7f19021f08 /lib | |
parent | a1aa8f14c864b6851649f9c3e74e9f12e6518edd (diff) |
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 <[email protected]>
Signed-off-by: Will Andrews <[email protected]>
Signed-off-by: Allan Jude <[email protected]>
Signed-off-by: Mateusz Piotrowski <[email protected]>
Co-authored-by: Allan Jude <[email protected]>
Co-authored-by: Mateusz Piotrowski <[email protected]>
Sponsored-by: Buddy <https://buddy.works>
Closes #12263
Diffstat (limited to 'lib')
-rw-r--r-- | lib/libspl/include/sys/types.h | 2 | ||||
-rw-r--r-- | lib/libspl/include/zone.h | 12 | ||||
-rw-r--r-- | lib/libspl/os/linux/zone.c | 32 | ||||
-rw-r--r-- | lib/libuutil/libuutil.abi | 2 | ||||
-rw-r--r-- | lib/libzfs/libzfs.abi | 9 | ||||
-rw-r--r-- | lib/libzfs/libzfs_util.c | 6 | ||||
-rw-r--r-- | lib/libzfs/os/linux/libzfs_util_os.c | 69 | ||||
-rw-r--r-- | lib/libzfs_core/libzfs_core.abi | 2 |
8 files changed, 128 insertions, 6 deletions
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 <inttypes.h> #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 <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> + #include <zone.h> 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 @@ </function-decl> </abi-instr> <abi-instr address-size='64' path='os/linux/zone.c' language='LANG_C99'> - <typedef-decl name='zoneid_t' type-id='95e97e5e' id='4da03624'/> + <typedef-decl name='zoneid_t' type-id='3502e3ff' id='4da03624'/> <function-decl name='getzoneid' mangled-name='getzoneid' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='getzoneid'> <return type-id='4da03624'/> </function-decl> 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 @@ <elf-symbol name='zfs_unmountall' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='zfs_unshare' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='zfs_unshareall' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> + <elf-symbol name='zfs_userns' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='zfs_userspace' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='zfs_valid_proplist' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> <elf-symbol name='zfs_version_kernel' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/> @@ -1537,7 +1538,7 @@ </function-decl> </abi-instr> <abi-instr address-size='64' path='lib/libspl/os/linux/zone.c' language='LANG_C99'> - <typedef-decl name='zoneid_t' type-id='95e97e5e' id='4da03624'/> + <typedef-decl name='zoneid_t' type-id='3502e3ff' id='4da03624'/> <function-decl name='getzoneid' mangled-name='getzoneid' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='getzoneid'> <return type-id='4da03624'/> </function-decl> @@ -4414,6 +4415,12 @@ <function-decl name='zfs_version_kernel' mangled-name='zfs_version_kernel' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='zfs_version_kernel'> <return type-id='26a90f95'/> </function-decl> + <function-decl name='zfs_userns' mangled-name='zfs_userns' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='zfs_userns'> + <parameter type-id='9200a744' name='zhp'/> + <parameter type-id='80f4b756' name='nspath'/> + <parameter type-id='95e97e5e' name='attach'/> + <return type-id='95e97e5e'/> + </function-decl> </abi-instr> <abi-instr address-size='64' path='lib/libzutil/os/linux/zutil_device_path_os.c' language='LANG_C99'> <function-decl name='zfs_append_partition' mangled-name='zfs_append_partition' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='zfs_append_partition'> 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 <alloca.h> #include <errno.h> @@ -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 @@ </function-decl> </abi-instr> <abi-instr address-size='64' path='os/linux/zone.c' language='LANG_C99'> - <typedef-decl name='zoneid_t' type-id='95e97e5e' id='4da03624'/> + <typedef-decl name='zoneid_t' type-id='3502e3ff' id='4da03624'/> <function-decl name='getzoneid' mangled-name='getzoneid' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='getzoneid'> <return type-id='4da03624'/> </function-decl> |