Skip to content

Commit 32ba0d1

Browse files
Will Andrewsallanjude
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 $ readlink /proc/$$/ns/user user:[4026532291] ``` 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 userns add 4026532291 containers/unpriv ``` Back to the user: ``` $ 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 ``` Signed-off-by: Will Andrews <will.andrews@klarasystems.com> Signed-off-by: Allan Jude <allan@klarasystems.com> Sponsored-by: Buddy <https://buddy.works>
1 parent a409e96 commit 32ba0d1

File tree

27 files changed

+941
-159
lines changed

27 files changed

+941
-159
lines changed

cmd/zfs/zfs_main.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ 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_userns(int argc, char **argv);
132+
#endif
133+
130134
/*
131135
* Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
132136
*/
@@ -184,6 +188,7 @@ typedef enum {
184188
HELP_JAIL,
185189
HELP_UNJAIL,
186190
HELP_WAIT,
191+
HELP_USERNS,
187192
} zfs_help_t;
188193

189194
typedef struct zfs_command {
@@ -254,6 +259,10 @@ static zfs_command_t command_table[] = {
254259
{ "jail", zfs_do_jail, HELP_JAIL },
255260
{ "unjail", zfs_do_unjail, HELP_UNJAIL },
256261
#endif
262+
263+
#ifdef __linux__
264+
{ "userns", zfs_do_userns, HELP_USERNS },
265+
#endif
257266
};
258267

259268
#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))
@@ -414,6 +423,9 @@ get_usage(zfs_help_t idx)
414423
return (gettext("\tunjail <jailid|jailname> <filesystem>\n"));
415424
case HELP_WAIT:
416425
return (gettext("\twait [-t <activity>] <filesystem>\n"));
426+
case HELP_USERNS:
427+
return (gettext("\tuserns <attach|detach> <nsnum> "
428+
"<filesystem>\n"));
417429
default:
418430
__builtin_unreachable();
419431
}
@@ -8727,6 +8739,55 @@ main(int argc, char **argv)
87278739
return (ret);
87288740
}
87298741

8742+
/*
8743+
* zfs userns attach|detach nsnum filesystem
8744+
*
8745+
* Add or delete the given dataset to/from the namespace.
8746+
*/
8747+
#ifdef __linux__
8748+
static int
8749+
zfs_do_userns(int argc, char **argv)
8750+
{
8751+
zfs_handle_t *zhp;
8752+
unsigned long nsnum;
8753+
int ret;
8754+
int attach;
8755+
8756+
if (argc < 4) {
8757+
(void) fprintf(stderr, gettext("missing argument(s)\n"));
8758+
usage(B_FALSE);
8759+
}
8760+
if (argc > 4) {
8761+
(void) fprintf(stderr, gettext("too many arguments\n"));
8762+
usage(B_FALSE);
8763+
}
8764+
8765+
if (strcmp(argv[1], "attach") == 0) {
8766+
attach = 1;
8767+
} else if (strcmp(argv[1], "detach") == 0) {
8768+
attach = 0;
8769+
} else {
8770+
(void) fprintf(stderr, gettext("invalid subcommand\n"));
8771+
usage(B_FALSE);
8772+
}
8773+
8774+
nsnum = strtoul(argv[2], NULL, 10);
8775+
if (nsnum > UINT_MAX) {
8776+
(void) fprintf(stderr, gettext("invalid namespace number\n"));
8777+
usage(B_FALSE);
8778+
}
8779+
8780+
zhp = zfs_open(g_zfs, argv[3], ZFS_TYPE_FILESYSTEM);
8781+
if (zhp == NULL)
8782+
return (1);
8783+
8784+
ret = (zfs_userns(zhp, (unsigned int)nsnum, attach) != 0);
8785+
8786+
zfs_close(zhp);
8787+
return (ret);
8788+
}
8789+
#endif
8790+
87308791
#ifdef __FreeBSD__
87318792
#include <sys/jail.h>
87328793
#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
@@ -134,6 +134,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
134134
ZFS_AC_KERNEL_SRC_SET_SPECIAL_STATE
135135
ZFS_AC_KERNEL_SRC_VFS_SET_PAGE_DIRTY_NOBUFFERS
136136
ZFS_AC_KERNEL_SRC_STANDALONE_LINUX_STDARG
137+
ZFS_AC_KERNEL_SRC_USER_NS_COMMON_INUM
137138
138139
AC_MSG_CHECKING([for available kernel interfaces])
139140
ZFS_LINUX_TEST_COMPILE_ALL([kabi])
@@ -241,6 +242,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
241242
ZFS_AC_KERNEL_SET_SPECIAL_STATE
242243
ZFS_AC_KERNEL_VFS_SET_PAGE_DIRTY_NOBUFFERS
243244
ZFS_AC_KERNEL_STANDALONE_LINUX_STDARG
245+
ZFS_AC_KERNEL_USER_NS_COMMON_INUM
244246
])
245247

246248
dnl #

include/libzfs.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,15 @@ _LIBZFS_H int zpool_nextboot(libzfs_handle_t *, uint64_t, uint64_t,
962962

963963
#endif /* __FreeBSD__ */
964964

965+
#ifdef __linux__
966+
967+
/*
968+
* Add or delete the given filesystem to/from the given user namespace.
969+
*/
970+
_LIBZFS_H int zfs_userns(zfs_handle_t *zhp, unsigned int nsnum, int attach);
971+
972+
#endif
973+
965974
#ifdef __cplusplus
966975
}
967976
#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 *, unsigned int);
37+
38+
/*
39+
* Detach the given dataset from the given user namespace.
40+
*/
41+
extern int zone_dataset_detach(cred_t *, const char *, unsigned 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 */

0 commit comments

Comments
 (0)