aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorWill Andrews <[email protected]>2021-02-21 10:19:43 -0600
committerBrian Behlendorf <[email protected]>2022-06-10 09:51:46 -0700
commit4ed5e25074ffec266df38556d9b3a928c5e0dee9 (patch)
tree930f2397ca27e885ca33c05728802d7f19021f08 /lib
parenta1aa8f14c864b6851649f9c3e74e9f12e6518edd (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.h2
-rw-r--r--lib/libspl/include/zone.h12
-rw-r--r--lib/libspl/os/linux/zone.c32
-rw-r--r--lib/libuutil/libuutil.abi2
-rw-r--r--lib/libzfs/libzfs.abi9
-rw-r--r--lib/libzfs/libzfs_util.c6
-rw-r--r--lib/libzfs/os/linux/libzfs_util_os.c69
-rw-r--r--lib/libzfs_core/libzfs_core.abi2
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>