mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-23 02:44:41 +03:00
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 <behlendorf1@llnl.gov> Signed-off-by: Will Andrews <will.andrews@klarasystems.com> Signed-off-by: Allan Jude <allan@klarasystems.com> Signed-off-by: Mateusz Piotrowski <mateusz.piotrowski@klarasystems.com> Co-authored-by: Allan Jude <allan@klarasystems.com> Co-authored-by: Mateusz Piotrowski <mateusz.piotrowski@klarasystems.com> Sponsored-by: Buddy <https://buddy.works> Closes #12263
This commit is contained in:
committed by
Brian Behlendorf
parent
a1aa8f14c8
commit
4ed5e25074
@@ -44,7 +44,7 @@
|
||||
#include <inttypes.h>
|
||||
#endif /* HAVE_INTTYPES */
|
||||
|
||||
typedef int zoneid_t;
|
||||
typedef uint_t zoneid_t;
|
||||
typedef int projid_t;
|
||||
|
||||
/*
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user