Skip to content

Commit 2d286df

Browse files
Will Andrewsallanjude0mp
authored andcommitted
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 openzfs#12263
1 parent 67c5c02 commit 2d286df

File tree

33 files changed

+1166
-15
lines changed

33 files changed

+1166
-15
lines changed

cmd/zfs/zfs_main.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,11 @@ static int zfs_do_jail(int argc, char **argv);
127127
static int zfs_do_unjail(int argc, char **argv);
128128
#endif
129129

130+
#ifdef __linux__
131+
static int zfs_do_zone(int argc, char **argv);
132+
static int zfs_do_unzone(int argc, char **argv);
133+
#endif
134+
130135
/*
131136
* Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
132137
*/
@@ -184,6 +189,8 @@ typedef enum {
184189
HELP_JAIL,
185190
HELP_UNJAIL,
186191
HELP_WAIT,
192+
HELP_ZONE,
193+
HELP_UNZONE,
187194
} zfs_help_t;
188195

189196
typedef struct zfs_command {
@@ -254,6 +261,11 @@ static zfs_command_t command_table[] = {
254261
{ "jail", zfs_do_jail, HELP_JAIL },
255262
{ "unjail", zfs_do_unjail, HELP_UNJAIL },
256263
#endif
264+
265+
#ifdef __linux__
266+
{ "zone", zfs_do_zone, HELP_ZONE },
267+
{ "unzone", zfs_do_unzone, HELP_UNZONE },
268+
#endif
257269
};
258270

259271
#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))
@@ -415,6 +427,10 @@ get_usage(zfs_help_t idx)
415427
return (gettext("\tunjail <jailid|jailname> <filesystem>\n"));
416428
case HELP_WAIT:
417429
return (gettext("\twait [-t <activity>] <filesystem>\n"));
430+
case HELP_ZONE:
431+
return (gettext("\tzone <nsfile> <filesystem>\n"));
432+
case HELP_UNZONE:
433+
return (gettext("\tunzone <nsfile> <filesystem>\n"));
418434
default:
419435
__builtin_unreachable();
420436
}
@@ -8692,6 +8708,50 @@ main(int argc, char **argv)
86928708
return (ret);
86938709
}
86948710

8711+
/*
8712+
* zfs zone nsfile filesystem
8713+
*
8714+
* Add or delete the given dataset to/from the namespace.
8715+
*/
8716+
#ifdef __linux__
8717+
static int
8718+
zfs_do_zone_impl(int argc, char **argv, boolean_t attach)
8719+
{
8720+
zfs_handle_t *zhp;
8721+
int ret;
8722+
8723+
if (argc < 3) {
8724+
(void) fprintf(stderr, gettext("missing argument(s)\n"));
8725+
usage(B_FALSE);
8726+
}
8727+
if (argc > 3) {
8728+
(void) fprintf(stderr, gettext("too many arguments\n"));
8729+
usage(B_FALSE);
8730+
}
8731+
8732+
zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM);
8733+
if (zhp == NULL)
8734+
return (1);
8735+
8736+
ret = (zfs_userns(zhp, argv[1], attach) != 0);
8737+
8738+
zfs_close(zhp);
8739+
return (ret);
8740+
}
8741+
8742+
static int
8743+
zfs_do_zone(int argc, char **argv)
8744+
{
8745+
return (zfs_do_zone_impl(argc, argv, B_TRUE));
8746+
}
8747+
8748+
static int
8749+
zfs_do_unzone(int argc, char **argv)
8750+
{
8751+
return (zfs_do_zone_impl(argc, argv, B_FALSE));
8752+
}
8753+
#endif
8754+
86958755
#ifdef __FreeBSD__
86968756
#include <sys/jail.h>
86978757
#include <jail.h>

config/kernel-user-ns-inum.m4

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
dnl #
2+
dnl # 3.18 API change
3+
dnl # struct user_namespace inum moved from .proc_inum to .ns.inum.
4+
dnl #
5+
AC_DEFUN([ZFS_AC_KERNEL_SRC_USER_NS_COMMON_INUM], [
6+
ZFS_LINUX_TEST_SRC([user_ns_common_inum], [
7+
#include <linux/user_namespace.h>
8+
], [
9+
struct user_namespace uns;
10+
uns.ns.inum = 0;
11+
])
12+
])
13+
14+
AC_DEFUN([ZFS_AC_KERNEL_USER_NS_COMMON_INUM], [
15+
AC_MSG_CHECKING([whether user_namespace->ns.inum exists])
16+
ZFS_LINUX_TEST_RESULT([user_ns_common_inum], [
17+
AC_MSG_RESULT(yes)
18+
AC_DEFINE(HAVE_USER_NS_COMMON_INUM, 1,
19+
[user_namespace->ns.inum exists])
20+
],[
21+
AC_MSG_RESULT(no)
22+
])
23+
])

config/kernel.m4

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
145145
ZFS_AC_KERNEL_SRC_KTHREAD
146146
ZFS_AC_KERNEL_SRC_ZERO_PAGE
147147
ZFS_AC_KERNEL_SRC___COPY_FROM_USER_INATOMIC
148+
ZFS_AC_KERNEL_SRC_USER_NS_COMMON_INUM
148149
149150
AC_MSG_CHECKING([for available kernel interfaces])
150151
ZFS_LINUX_TEST_COMPILE_ALL([kabi])
@@ -263,6 +264,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
263264
ZFS_AC_KERNEL_KTHREAD
264265
ZFS_AC_KERNEL_ZERO_PAGE
265266
ZFS_AC_KERNEL___COPY_FROM_USER_INATOMIC
267+
ZFS_AC_KERNEL_USER_NS_COMMON_INUM
266268
])
267269

268270
dnl #

contrib/pyzfs/libzfs_core/_constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ def enum(*sequential, **named):
100100
'ZFS_ERR_REBUILD_IN_PROGRESS',
101101
'ZFS_ERR_BADPROP',
102102
'ZFS_ERR_VDEV_NOTSUP',
103+
'ZFS_ERR_NOT_USER_NAMESPACE',
103104
],
104105
{}
105106
)

include/libzfs.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ typedef enum zfs_error {
150150
EZFS_EXPORT_IN_PROGRESS, /* currently exporting the pool */
151151
EZFS_REBUILDING, /* resilvering (sequential reconstrution) */
152152
EZFS_VDEV_NOTSUP, /* ops not supported for this type of vdev */
153+
EZFS_NOT_USER_NAMESPACE, /* a file is not a user namespace */
153154
EZFS_UNKNOWN
154155
} zfs_error_t;
155156

@@ -979,6 +980,15 @@ _LIBZFS_H int zpool_nextboot(libzfs_handle_t *, uint64_t, uint64_t,
979980

980981
#endif /* __FreeBSD__ */
981982

983+
#ifdef __linux__
984+
985+
/*
986+
* Add or delete the given filesystem to/from the given user namespace.
987+
*/
988+
_LIBZFS_H int zfs_userns(zfs_handle_t *zhp, const char *nspath, int attach);
989+
990+
#endif
991+
982992
#ifdef __cplusplus
983993
}
984994
#endif

include/os/linux/spl/sys/zone.h

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,34 @@
2525
#define _SPL_ZONE_H
2626

2727
#include <sys/byteorder.h>
28+
#include <sys/cred.h>
2829

29-
#define GLOBAL_ZONEID 0
30+
#include <linux/cred.h>
31+
#include <linux/user_namespace.h>
3032

31-
#define zone_dataset_visible(x, y) (1)
32-
#define crgetzoneid(x) (GLOBAL_ZONEID)
33-
#define INGLOBALZONE(z) (1)
33+
/*
34+
* Attach the given dataset to the given user namespace.
35+
*/
36+
extern int zone_dataset_attach(cred_t *, const char *, int);
37+
38+
/*
39+
* Detach the given dataset from the given user namespace.
40+
*/
41+
extern int zone_dataset_detach(cred_t *, const char *, int);
42+
43+
/*
44+
* Returns true if the named pool/dataset is visible in the current zone.
45+
*/
46+
extern int zone_dataset_visible(const char *dataset, int *write);
47+
48+
int spl_zone_init(void);
49+
void spl_zone_fini(void);
50+
51+
extern unsigned int crgetzoneid(const cred_t *);
52+
extern unsigned int global_zoneid(void);
53+
extern boolean_t inglobalzone(proc_t *);
54+
55+
#define INGLOBALZONE(x) inglobalzone(x)
56+
#define GLOBAL_ZONEID global_zoneid()
3457

3558
#endif /* SPL_ZONE_H */

include/sys/fs/zfs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,7 +1471,9 @@ typedef enum zfs_ioc {
14711471
ZFS_IOC_EVENTS_SEEK, /* 0x83 (Linux) */
14721472
ZFS_IOC_NEXTBOOT, /* 0x84 (FreeBSD) */
14731473
ZFS_IOC_JAIL, /* 0x85 (FreeBSD) */
1474+
ZFS_IOC_USERNS_ATTACH = ZFS_IOC_JAIL, /* 0x85 (Linux) */
14741475
ZFS_IOC_UNJAIL, /* 0x86 (FreeBSD) */
1476+
ZFS_IOC_USERNS_DETACH = ZFS_IOC_UNJAIL, /* 0x86 (Linux) */
14751477
ZFS_IOC_SET_BOOTENV, /* 0x87 */
14761478
ZFS_IOC_GET_BOOTENV, /* 0x88 */
14771479
ZFS_IOC_UNREGISTER_FS, /* 0x89 (Windows) */
@@ -1555,6 +1557,7 @@ typedef enum {
15551557
ZFS_ERR_REBUILD_IN_PROGRESS,
15561558
ZFS_ERR_BADPROP,
15571559
ZFS_ERR_VDEV_NOTSUP,
1560+
ZFS_ERR_NOT_USER_NAMESPACE,
15581561
} zfs_errno_t;
15591562

15601563
/*

lib/libspl/include/sys/types.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
#include <inttypes.h>
4545
#endif /* HAVE_INTTYPES */
4646

47-
typedef int zoneid_t;
47+
typedef uint_t zoneid_t;
4848
typedef int projid_t;
4949

5050
/*

lib/libspl/include/zone.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,17 @@
3333
extern "C" {
3434
#endif
3535

36-
#define GLOBAL_ZONEID 0
36+
#ifdef __FreeBSD__
37+
#define GLOBAL_ZONEID 0
38+
#else
39+
/*
40+
* Hardcoded in the kernel's root user namespace. A "better" way to get
41+
* this would be by using ioctl_ns(2), but this would need to be performed
42+
* recursively on NS_GET_PARENT and then NS_GET_USERNS. Also, that's only
43+
* supported since Linux 4.9.
44+
*/
45+
#define GLOBAL_ZONEID 4026531837U
46+
#endif
3747

3848
extern zoneid_t getzoneid(void);
3949

lib/libspl/os/linux/zone.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,40 @@
2323
* Use is subject to license terms.
2424
*/
2525

26+
#include <unistd.h>
27+
#include <stdio.h>
28+
#include <errno.h>
29+
#include <stdlib.h>
30+
#include <limits.h>
31+
#include <string.h>
32+
2633
#include <zone.h>
2734

2835
zoneid_t
2936
getzoneid(void)
3037
{
31-
return (GLOBAL_ZONEID);
38+
char path[PATH_MAX];
39+
char buf[128] = { '\0' };
40+
char *cp;
41+
42+
int c = snprintf(path, sizeof (path), "/proc/self/ns/user");
43+
/* This API doesn't have any error checking... */
44+
if (c < 0)
45+
return (0);
46+
47+
ssize_t r = readlink(path, buf, sizeof (buf) - 1);
48+
if (r < 0)
49+
return (0);
50+
51+
cp = strchr(buf, '[');
52+
if (cp == NULL)
53+
return (0);
54+
cp++;
55+
56+
unsigned long n = strtoul(cp, NULL, 10);
57+
if (n == ULONG_MAX && errno == ERANGE)
58+
return (0);
59+
zoneid_t z = (zoneid_t)n;
60+
61+
return (z);
3262
}

lib/libuutil/libuutil.abi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1081,7 +1081,7 @@
10811081
</function-decl>
10821082
</abi-instr>
10831083
<abi-instr address-size='64' path='os/linux/zone.c' language='LANG_C99'>
1084-
<typedef-decl name='zoneid_t' type-id='95e97e5e' id='4da03624'/>
1084+
<typedef-decl name='zoneid_t' type-id='3502e3ff' id='4da03624'/>
10851085
<function-decl name='getzoneid' mangled-name='getzoneid' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='getzoneid'>
10861086
<return type-id='4da03624'/>
10871087
</function-decl>

lib/libzfs/libzfs.abi

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@
433433
<elf-symbol name='zfs_unmountall' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
434434
<elf-symbol name='zfs_unshare' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
435435
<elf-symbol name='zfs_unshareall' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
436+
<elf-symbol name='zfs_userns' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
436437
<elf-symbol name='zfs_userspace' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
437438
<elf-symbol name='zfs_valid_proplist' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
438439
<elf-symbol name='zfs_version_kernel' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
@@ -1537,7 +1538,7 @@
15371538
</function-decl>
15381539
</abi-instr>
15391540
<abi-instr address-size='64' path='lib/libspl/os/linux/zone.c' language='LANG_C99'>
1540-
<typedef-decl name='zoneid_t' type-id='95e97e5e' id='4da03624'/>
1541+
<typedef-decl name='zoneid_t' type-id='3502e3ff' id='4da03624'/>
15411542
<function-decl name='getzoneid' mangled-name='getzoneid' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='getzoneid'>
15421543
<return type-id='4da03624'/>
15431544
</function-decl>
@@ -4414,6 +4415,12 @@
44144415
<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'>
44154416
<return type-id='26a90f95'/>
44164417
</function-decl>
4418+
<function-decl name='zfs_userns' mangled-name='zfs_userns' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='zfs_userns'>
4419+
<parameter type-id='9200a744' name='zhp'/>
4420+
<parameter type-id='80f4b756' name='nspath'/>
4421+
<parameter type-id='95e97e5e' name='attach'/>
4422+
<return type-id='95e97e5e'/>
4423+
</function-decl>
44174424
</abi-instr>
44184425
<abi-instr address-size='64' path='lib/libzutil/os/linux/zutil_device_path_os.c' language='LANG_C99'>
44194426
<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'>

lib/libzfs/libzfs_util.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,9 @@ libzfs_error_description(libzfs_handle_t *hdl)
298298
case EZFS_VDEV_NOTSUP:
299299
return (dgettext(TEXT_DOMAIN, "operation not supported "
300300
"on this type of vdev"));
301+
case EZFS_NOT_USER_NAMESPACE:
302+
return (dgettext(TEXT_DOMAIN, "the provided file "
303+
"was not a user namespace file"));
301304
case EZFS_UNKNOWN:
302305
return (dgettext(TEXT_DOMAIN, "unknown error"));
303306
default:
@@ -484,6 +487,9 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
484487
case ZFS_ERR_BADPROP:
485488
zfs_verror(hdl, EZFS_BADPROP, fmt, ap);
486489
break;
490+
case ZFS_ERR_NOT_USER_NAMESPACE:
491+
zfs_verror(hdl, EZFS_NOT_USER_NAMESPACE, fmt, ap);
492+
break;
487493
default:
488494
zfs_error_aux(hdl, "%s", strerror(error));
489495
zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);

0 commit comments

Comments
 (0)