diff options
| author | Alex Wilson <alex.wilson@joyent.com> | 2018-09-17 01:38:51 +0000 |
|---|---|---|
| committer | Jason King <jason.king@joyent.com> | 2020-03-20 06:21:16 +0000 |
| commit | 08a0f5ea09ca188edbadd95cfdfddc2160992ba4 (patch) | |
| tree | 1aba48089fd03bd513180ee100343b01bf4e610f /usr/src | |
| parent | 9f9bdc6d9964c15e63aa7abeb78eff3f478b0cfc (diff) | |
| download | illumos-joyent-08a0f5ea09ca188edbadd95cfdfddc2160992ba4.tar.gz | |
OS-4278 delegated datasets could have better in-zone names
(in progress)
Diffstat (limited to 'usr/src')
| -rw-r--r-- | usr/src/cmd/zoneadmd/Makefile.com | 2 | ||||
| -rw-r--r-- | usr/src/cmd/zoneadmd/vplat.c | 92 | ||||
| -rw-r--r-- | usr/src/cmd/zonecfg/zonecfg.c | 32 | ||||
| -rw-r--r-- | usr/src/cmd/zonecfg/zonecfg.h | 5 | ||||
| -rw-r--r-- | usr/src/cmd/zonecfg/zonecfg_grammar.y | 7 | ||||
| -rw-r--r-- | usr/src/cmd/zonecfg/zonecfg_lex.l | 4 | ||||
| -rw-r--r-- | usr/src/common/nvpair/nvpair.c | 12 | ||||
| -rw-r--r-- | usr/src/head/libzonecfg.h | 1 | ||||
| -rw-r--r-- | usr/src/lib/brand/lx/zone/platform.xml | 3 | ||||
| -rw-r--r-- | usr/src/lib/libzonecfg/common/libzonecfg.c | 38 | ||||
| -rw-r--r-- | usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 | 3 | ||||
| -rw-r--r-- | usr/src/lib/libzpool/common/sys/zfs_context.h | 2 | ||||
| -rw-r--r-- | usr/src/uts/common/brand/lx/os/lx_brand.c | 9 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/dev/sdev_zvolops.c | 47 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/zfs/spa_config.c | 2 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/zfs/zfs_ioctl.c | 830 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/zfs/zfs_vfsops.c | 26 | ||||
| -rw-r--r-- | usr/src/uts/common/fs/zfs/zvol.c | 24 | ||||
| -rw-r--r-- | usr/src/uts/common/os/zone.c | 352 | ||||
| -rw-r--r-- | usr/src/uts/common/sys/nvpair.h | 3 | ||||
| -rw-r--r-- | usr/src/uts/common/sys/zone.h | 15 |
21 files changed, 1374 insertions, 135 deletions
diff --git a/usr/src/cmd/zoneadmd/Makefile.com b/usr/src/cmd/zoneadmd/Makefile.com index 9725021b17..5ac034922b 100644 --- a/usr/src/cmd/zoneadmd/Makefile.com +++ b/usr/src/cmd/zoneadmd/Makefile.com @@ -35,7 +35,7 @@ OBJS= zoneadmd.o zcons.o zfd.o vplat.o log.o CFLAGS += $(CCVERBOSE) LDLIBS += -lsocket -lzonecfg -lnsl -ldevinfo -ldevice -lnvpair \ -lgen -lbsm -lcontract -lzfs -luuid -lbrand -ldladm -ltsnet -ltsol \ - -linetutil -lscf -lppt + -linetutil -lproc -lscf -lppt -lcustr CSTD= $(CSTD_GNU99) diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c index 01332d43e8..5372d9dc34 100644 --- a/usr/src/cmd/zoneadmd/vplat.c +++ b/usr/src/cmd/zoneadmd/vplat.c @@ -131,6 +131,8 @@ #include <sys/priv.h> #include <libinetutil.h> +#include <libcustr.h> + #define V4_ADDR_LEN 32 #define V6_ADDR_LEN 128 @@ -3508,17 +3510,16 @@ get_implicit_datasets(zlog_t *zlogp, char **retstr) } static int -get_datasets(zlog_t *zlogp, char **bufp, size_t *bufsizep) +get_datasets(zlog_t *zlogp, custr_t **datasetsp) { struct zone_dstab dstab; - size_t total, offset, len; int error = -1; - char *str = NULL; char *implicit_datasets = NULL; int implicit_len = 0; + custr_t *cu = NULL; + int count = 0; - *bufp = NULL; - *bufsizep = 0; + *datasetsp = NULL; if (get_implicit_datasets(zlogp, &implicit_datasets) != 0) { zerror(zlogp, B_FALSE, "getting implicit datasets failed"); @@ -3530,22 +3531,7 @@ get_datasets(zlog_t *zlogp, char **bufp, size_t *bufsizep) goto out; } - total = 0; - while (zonecfg_getdsent(snap_hndl, &dstab) == Z_OK) - total += strlen(dstab.zone_dataset_name) + 1; - (void) zonecfg_enddsent(snap_hndl); - - if (implicit_datasets != NULL) - implicit_len = strlen(implicit_datasets); - if (implicit_len > 0) - total += implicit_len + 1; - - if (total == 0) { - error = 0; - goto out; - } - - if ((str = malloc(total)) == NULL) { + if (custr_alloc(&cu) != 0) { zerror(zlogp, B_TRUE, "memory allocation failed"); goto out; } @@ -3554,29 +3540,48 @@ get_datasets(zlog_t *zlogp, char **bufp, size_t *bufsizep) zerror(zlogp, B_FALSE, "%s failed", "zonecfg_setdsent"); goto out; } - offset = 0; while (zonecfg_getdsent(snap_hndl, &dstab) == Z_OK) { - len = strlen(dstab.zone_dataset_name); - (void) strlcpy(str + offset, dstab.zone_dataset_name, - total - offset); - offset += len; - if (offset < total - 1) - str[offset++] = ','; + const char *alias = dstab.zone_dataset_alias[0] != '\0' ? + dstab.zone_dataset_alias : NULL; + + if (count++ > 0 && custr_appendc(cu, ',') != 0) { + zerror(zlogp, B_TRUE, "memory allocation failed"); + goto out; + } + + if (custr_append(cu, dstab.zone_dataset_name) != 0) { + zerror(zlogp, B_TRUE, "memory allocation failed"); + goto out; + } + + if (alias != NULL && custr_append_printf(cu, "|%s", + alias) != 0) { + zerror(zlogp, B_TRUE, "memory allocation failed"); + goto out; + } } (void) zonecfg_enddsent(snap_hndl); - if (implicit_len > 0) - (void) strlcpy(str + offset, implicit_datasets, total - offset); + if (implicit_len > 0) { + if (count++ > 0 && custr_appendc(cu, ',') != 0) { + zerror(zlogp, B_TRUE, "memory allocation failed"); + goto out; + } + if (custr_append(cu, implicit_datasets) != 0) { + zerror(zlogp, B_TRUE, "memory allocation failed"); + goto out; + } + } error = 0; - *bufp = str; - *bufsizep = total; out: - if (error != 0 && str != NULL) - free(str); - if (implicit_datasets != NULL) - free(implicit_datasets); + if (error != 0) { + custr_free(cu); + } else { + *datasetsp = cu; + } + free(implicit_datasets); return (error); } @@ -4707,8 +4712,9 @@ vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd, zoneid_t zone_did) char rootpath[MAXPATHLEN]; char *rctlbuf = NULL; size_t rctlbufsz = 0; - char *zfsbuf = NULL; + const char *zfsbuf = NULL; size_t zfsbufsz = 0; + custr_t *datasets = NULL; zoneid_t zoneid = -1; int xerr; char *kzone; @@ -4755,10 +4761,14 @@ vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd, zoneid_t zone_did) goto error; } - if (get_datasets(zlogp, &zfsbuf, &zfsbufsz) != 0) { + if (get_datasets(zlogp, &datasets) != 0) { zerror(zlogp, B_FALSE, "Unable to get list of ZFS datasets"); goto error; } + if (datasets != NULL) { + zfsbuf = custr_cstr(datasets); + zfsbufsz = custr_len(datasets); + } if (mount_cmd == Z_MNT_BOOT && is_system_labeled()) { zcent = get_zone_label(zlogp, privs); @@ -4945,10 +4955,8 @@ error: (void) zone_shutdown(zoneid); (void) zone_destroy(zoneid); } - if (rctlbuf != NULL) - free(rctlbuf); - if (zfsbuf != NULL) - free(zfsbuf); + free(rctlbuf); + custr_free(datasets); priv_freeset(privs); if (fp != NULL) zonecfg_close_scratch(fp); diff --git a/usr/src/cmd/zonecfg/zonecfg.c b/usr/src/cmd/zonecfg/zonecfg.c index 20f30e6e0c..154fd71492 100644 --- a/usr/src/cmd/zonecfg/zonecfg.c +++ b/usr/src/cmd/zonecfg/zonecfg.c @@ -249,6 +249,7 @@ char *prop_types[] = { "default", "lower", "upper", + "alias", NULL }; @@ -1234,6 +1235,8 @@ usage(boolean_t verbose, uint_t flags) (void) fprintf(fp, gettext("Valid commands:\n")); (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), pt_to_str(PT_NAME), gettext("<name>")); + (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), + pt_to_str(PT_ALIAS), gettext("<alias>")); break; case RT_DCPU: (void) fprintf(fp, gettext("The '%s' resource scope " @@ -1437,8 +1440,8 @@ usage(boolean_t verbose, uint_t flags) (void) fprintf(fp, "\t%s\t\t%s, %s, %s\n", rt_to_str(RT_ATTR), pt_to_str(PT_NAME), pt_to_str(PT_TYPE), pt_to_str(PT_VALUE)); - (void) fprintf(fp, "\t%s\t\t%s\n", rt_to_str(RT_DATASET), - pt_to_str(PT_NAME)); + (void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_DATASET), + pt_to_str(PT_NAME), pt_to_str(PT_ALIAS)); (void) fprintf(fp, "\t%s\t%s, %s\n", rt_to_str(RT_DCPU), pt_to_str(PT_NCPUS), pt_to_str(PT_IMPORTANCE)); (void) fprintf(fp, "\t%s\t%s\n", rt_to_str(RT_PCAP), @@ -2170,6 +2173,9 @@ export_func(cmd_t *cmd) (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD), rt_to_str(RT_DATASET)); export_prop(of, PT_NAME, dstab.zone_dataset_name); + if (dstab.zone_dataset_alias[0] != '\0') { + export_prop(of, PT_ALIAS, dstab.zone_dataset_alias); + } (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); } (void) zonecfg_enddsent(handle); @@ -3171,6 +3177,7 @@ fill_in_dstab(cmd_t *cmd, struct zone_dstab *dstab, boolean_t fill_in_only) return (err); dstab->zone_dataset_name[0] = '\0'; + dstab->zone_dataset_alias[0] = '\0'; for (i = 0; i < cmd->cmd_prop_nv_pairs; i++) { pp = cmd->cmd_property_ptr[i]; if (pp->pv_type != PROP_VAL_SIMPLE || pp->pv_simple == NULL) { @@ -3183,6 +3190,10 @@ fill_in_dstab(cmd_t *cmd, struct zone_dstab *dstab, boolean_t fill_in_only) (void) strlcpy(dstab->zone_dataset_name, pp->pv_simple, sizeof (dstab->zone_dataset_name)); break; + case PT_ALIAS: + (void) strlcpy(dstab->zone_dataset_alias, pp->pv_simple, + sizeof (dstab->zone_dataset_alias)); + break; default: zone_perror(pt_to_str(cmd->cmd_prop_name[i]), Z_NO_PROPERTY_TYPE, B_TRUE); @@ -4071,6 +4082,13 @@ clear_property(cmd_t *cmd) return; } break; + case RT_DATASET: + if (prop_type == PT_ALIAS) { + in_progress_dstab.zone_dataset_alias[0] = '\0'; + need_to_commit = B_TRUE; + return; + } + break; case RT_DCPU: if (prop_type == PT_IMPORTANCE) { in_progress_psettab.zone_importance[0] = '\0'; @@ -5177,6 +5195,11 @@ set_func(cmd_t *cmd) prop_id, sizeof (in_progress_dstab.zone_dataset_name)); return; + case PT_ALIAS: + (void) strlcpy(in_progress_dstab.zone_dataset_alias, + prop_id, + sizeof (in_progress_dstab.zone_dataset_alias)); + return; default: break; } @@ -5883,6 +5906,7 @@ output_ds(FILE *fp, struct zone_dstab *dstab) { (void) fprintf(fp, "%s:\n", rt_to_str(RT_DATASET)); output_prop(fp, PT_NAME, dstab->zone_dataset_name, B_TRUE); + output_prop(fp, PT_ALIAS, dstab->zone_dataset_alias, B_TRUE); } static void @@ -5904,6 +5928,10 @@ info_ds(zone_dochandle_t handle, FILE *fp, cmd_t *cmd) strcmp(user.zone_dataset_name, lookup.zone_dataset_name) != 0) continue; /* no match */ + if (strlen(user.zone_dataset_alias) > 0 && + strcmp(user.zone_dataset_alias, + lookup.zone_dataset_alias) != 0) + continue; /* no match */ output_ds(fp, &lookup); output = B_TRUE; } diff --git a/usr/src/cmd/zonecfg/zonecfg.h b/usr/src/cmd/zonecfg/zonecfg.h index e4ae4d4d61..426aed7331 100644 --- a/usr/src/cmd/zonecfg/zonecfg.h +++ b/usr/src/cmd/zonecfg/zonecfg.h @@ -21,7 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, Joyent Inc. All rights reserved. + * Copyright (c) 2018, Joyent Inc. All rights reserved. */ #ifndef _ZONECFG_H @@ -150,9 +150,10 @@ extern "C" { #define PT_DEFAULT 48 #define PT_LOWER 49 #define PT_UPPER 50 +#define PT_ALIAS 51 #define PT_MIN PT_UNKNOWN -#define PT_MAX PT_UPPER +#define PT_MAX PT_ALIAS #define MAX_EQ_PROP_PAIRS 3 diff --git a/usr/src/cmd/zonecfg/zonecfg_grammar.y b/usr/src/cmd/zonecfg/zonecfg_grammar.y index d8b2fadb2f..019f2fd989 100644 --- a/usr/src/cmd/zonecfg/zonecfg_grammar.y +++ b/usr/src/cmd/zonecfg/zonecfg_grammar.y @@ -22,7 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013, Joyent Inc. All rights reserved. + * Copyright 2018, Joyent Inc. All rights reserved. */ /* @@ -137,7 +137,7 @@ complex_piece_func(int cp_type, const char *str, complex_property_ptr_t cp_next) %token OPEN_PAREN CLOSE_PAREN COMMA DATASET LIMITPRIV BOOTARGS BRAND PSET PCAP %token MCAP NCPUS IMPORTANCE SHARES MAXLWPS MAXSHMMEM MAXSHMIDS MAXMSGIDS %token MAXSEMIDS LOCKED SWAP SCHED CLEAR DEFROUTER ADMIN USER AUTHS MAXPROCS -%token ZFSPRI MAC VLANID GNIC NPROP UUID SECFLAGS +%token ZFSPRI MAC VLANID GNIC NPROP UUID SECFLAGS ALIAS %token DEFAULT UPPER LOWER %type <strval> TOKEN EQUAL OPEN_SQ_BRACKET CLOSE_SQ_BRACKET @@ -148,7 +148,7 @@ complex_piece_func(int cp_type, const char *str, complex_property_ptr_t cp_next) %type <ival> property_name SPECIAL RAW DIR OPTIONS TYPE ADDRESS PHYSICAL NAME MATCH ZONENAME ZONEPATH AUTOBOOT POOL LIMITPRIV BOOTARGS VALUE PRIV LIMIT ACTION BRAND SCHED IPTYPE DEFROUTER HOSTID USER AUTHS FS_ALLOWED - ALLOWED_ADDRESS MAC VLANID GNIC NPROP UUID DEFAULT UPPER LOWER + ALLOWED_ADDRESS MAC VLANID GNIC NPROP UUID DEFAULT UPPER LOWER ALIAS %type <cmd> command %type <cmd> add_command ADD %type <cmd> cancel_command CANCEL @@ -1091,6 +1091,7 @@ property_name: SPECIAL { $$ = PT_SPECIAL; } | DEFAULT { $$ = PT_DEFAULT; } | UPPER { $$ = PT_UPPER; } | LOWER { $$ = PT_LOWER; } + | ALIAS { $$ = PT_ALIAS; } /* * The grammar builds data structures from the bottom up. Thus various diff --git a/usr/src/cmd/zonecfg/zonecfg_lex.l b/usr/src/cmd/zonecfg/zonecfg_lex.l index 05b41df48b..db6da387e5 100644 --- a/usr/src/cmd/zonecfg/zonecfg_lex.l +++ b/usr/src/cmd/zonecfg/zonecfg_lex.l @@ -22,7 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, Joyent Inc. All rights reserved. + * Copyright (c) 2018, Joyent Inc. All rights reserved. */ #include <assert.h> @@ -330,6 +330,8 @@ static char *create_token(char *s); <TSTATE>zfs-io-priority { return ZFSPRI; } <CSTATE>zfs-io-priority { return ZFSPRI; } +<TSTATE>alias { return ALIAS; } + <TSTATE>default { return DEFAULT; } <CSTATE>default { return DEFAULT; } diff --git a/usr/src/common/nvpair/nvpair.c b/usr/src/common/nvpair/nvpair.c index 0e1ec15d61..d2bab7a723 100644 --- a/usr/src/common/nvpair/nvpair.c +++ b/usr/src/common/nvpair/nvpair.c @@ -2253,6 +2253,18 @@ nvlist_add_nvpair(nvlist_t *nvl, nvpair_t *nvp) NVP_NELEM(nvp), NVP_VALUE(nvp))); } +#if defined(_KERNEL) +int +nvlist_add_named_nvpair(nvlist_t *nvl, const char *name, nvpair_t *nvp) +{ + if (nvl == NULL || nvp == NULL || name == NULL) + return (EINVAL); + + return (nvlist_add_common(nvl, name, NVP_TYPE(nvp), + NVP_NELEM(nvp), NVP_VALUE(nvp))); +} +#endif + /* * Merge the supplied nvlists and put the result in dst. * The merged list will contain all names specified in both lists, diff --git a/usr/src/head/libzonecfg.h b/usr/src/head/libzonecfg.h index 4121b6a490..fc9506a093 100644 --- a/usr/src/head/libzonecfg.h +++ b/usr/src/head/libzonecfg.h @@ -253,6 +253,7 @@ struct zone_attrtab { struct zone_dstab { char zone_dataset_name[MAXNAMELEN]; + char zone_dataset_alias[MAXNAMELEN]; }; struct zone_psettab { diff --git a/usr/src/lib/brand/lx/zone/platform.xml b/usr/src/lib/brand/lx/zone/platform.xml index 060343e38f..7097ae8e1c 100644 --- a/usr/src/lib/brand/lx/zone/platform.xml +++ b/usr/src/lib/brand/lx/zone/platform.xml @@ -98,8 +98,7 @@ <device match="urandom" /> <device match="zero" /> <device match="zfs" /> - <device match="zvol/dsk/%P/%z/*" /> - <device match="zvol/rdsk/%P/%z/*" /> + <device match="zvol/*" /> <!-- Devices to create in exclusive IP zone only --> <device match="dld" ip-type="exclusive" /> diff --git a/usr/src/lib/libzonecfg/common/libzonecfg.c b/usr/src/lib/libzonecfg/common/libzonecfg.c index e709b7dba8..0e7525882e 100644 --- a/usr/src/lib/libzonecfg/common/libzonecfg.c +++ b/usr/src/lib/libzonecfg/common/libzonecfg.c @@ -107,6 +107,7 @@ #define DTD_ATTR_ACTION (const xmlChar *) "action" #define DTD_ATTR_ADDRESS (const xmlChar *) "address" +#define DTD_ATTR_ALIAS (const xmlChar *) "alias" #define DTD_ATTR_ALLOWED_ADDRESS (const xmlChar *) "allowed-address" #define DTD_ATTR_AUTOBOOT (const xmlChar *) "autoboot" #define DTD_ATTR_IPTYPE (const xmlChar *) "ip-type" @@ -7021,6 +7022,11 @@ zonecfg_add_ds_core(zone_dochandle_t handle, struct zone_dstab *tabptr) if ((err = newprop(newnode, DTD_ATTR_NAME, tabptr->zone_dataset_name)) != Z_OK) return (err); + if (tabptr->zone_dataset_alias[0] != '\0') { + if ((err = newprop(newnode, DTD_ATTR_ALIAS, + tabptr->zone_dataset_alias)) != Z_OK) + return (err); + } return (Z_OK); } @@ -7051,7 +7057,9 @@ zonecfg_delete_ds_core(zone_dochandle_t handle, struct zone_dstab *tabptr) continue; if (match_prop(cur, DTD_ATTR_NAME, - tabptr->zone_dataset_name)) { + tabptr->zone_dataset_name) && + match_prop(cur, DTD_ATTR_ALIAS, + tabptr->zone_dataset_alias)) { xmlUnlinkNode(cur); xmlFreeNode(cur); return (Z_OK); @@ -7106,6 +7114,7 @@ zonecfg_lookup_ds(zone_dochandle_t handle, struct zone_dstab *tabptr) xmlNodePtr cur, firstmatch; int err; char dataset[MAXNAMELEN]; + char alias[MAXNAMELEN]; if (tabptr == NULL) return (Z_INVAL); @@ -7129,6 +7138,25 @@ zonecfg_lookup_ds(zone_dochandle_t handle, struct zone_dstab *tabptr) return (Z_INSUFFICIENT_SPEC); } } + if (strlen(tabptr->zone_dataset_alias) > 0) { + if ((fetchprop(cur, DTD_ATTR_ALIAS, alias, + sizeof (alias)) == Z_OK)) { + if (strcmp(tabptr->zone_dataset_alias, + alias) == 0) { + if (firstmatch == NULL) + firstmatch = cur; + else if (firstmatch != cur) + return (Z_INSUFFICIENT_SPEC); + } else { + /* + * If another property matched but this + * one doesn't then reset firstmatch. + */ + if (firstmatch == cur) + firstmatch = NULL; + } + } + } } if (firstmatch == NULL) return (Z_NO_RESOURCE_ID); @@ -7138,6 +7166,9 @@ zonecfg_lookup_ds(zone_dochandle_t handle, struct zone_dstab *tabptr) if ((err = fetchprop(cur, DTD_ATTR_NAME, tabptr->zone_dataset_name, sizeof (tabptr->zone_dataset_name))) != Z_OK) return (err); + if ((err = fetchprop(cur, DTD_ATTR_ALIAS, tabptr->zone_dataset_alias, + sizeof (tabptr->zone_dataset_alias))) != Z_OK) + return (err); return (Z_OK); } @@ -7173,6 +7204,11 @@ zonecfg_getdsent(zone_dochandle_t handle, struct zone_dstab *tabptr) handle->zone_dh_cur = handle->zone_dh_top; return (err); } + if ((err = fetchprop(cur, DTD_ATTR_ALIAS, tabptr->zone_dataset_alias, + sizeof (tabptr->zone_dataset_alias))) != Z_OK) { + handle->zone_dh_cur = handle->zone_dh_top; + return (err); + } handle->zone_dh_cur = cur->next; return (Z_OK); diff --git a/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 b/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 index 228bb8ace2..ee72fa0759 100644 --- a/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 +++ b/usr/src/lib/libzonecfg/dtd/zonecfg.dtd.1 @@ -100,7 +100,8 @@ <!ELEMENT dataset EMPTY> -<!ATTLIST dataset name CDATA #REQUIRED> +<!ATTLIST dataset name CDATA #REQUIRED + alias CDATA ""> <!ELEMENT package EMPTY> diff --git a/usr/src/lib/libzpool/common/sys/zfs_context.h b/usr/src/lib/libzpool/common/sys/zfs_context.h index 3c7d304d65..91cceb0f63 100644 --- a/usr/src/lib/libzpool/common/sys/zfs_context.h +++ b/usr/src/lib/libzpool/common/sys/zfs_context.h @@ -324,7 +324,7 @@ typedef struct callb_cpr { mutex_exit((cp)->cc_lockp); \ } -#define zone_dataset_visible(x, y) (1) +#define zone_dataset_visible(x, y, z) (1) #define INGLOBALZONE(z) (1) extern uint32_t zone_get_hostid(void *zonep); diff --git a/usr/src/uts/common/brand/lx/os/lx_brand.c b/usr/src/uts/common/brand/lx/os/lx_brand.c index fed6be37cf..34f2f4f945 100644 --- a/usr/src/uts/common/brand/lx/os/lx_brand.c +++ b/usr/src/uts/common/brand/lx/os/lx_brand.c @@ -1018,7 +1018,8 @@ lx_zone_get_zvols(zone_t *zone, ldi_handle_t lh, minor_t *emul_minor) strchr(zc->zc_name, '%') != NULL) continue; - if (!zone_dataset_visible_inzone(zone, zc->zc_name, &w)) + if (!zone_dataset_visible_inzone(zone, zc->zc_name, &w, + B_TRUE)) continue; if (zc->zc_objset_stats.dds_type == DMU_OST_ZVOL) { @@ -1029,6 +1030,12 @@ lx_zone_get_zvols(zone_t *zone, ldi_handle_t lh, minor_t *emul_minor) /* Create a virtual disk entry for the zvol */ vd = kmem_zalloc(sizeof (lx_virt_disk_t), KM_SLEEP); + + rc = zone_dataset_alias_inzone(zc->zc_name, + zv->lzd_name, MAXPATHLEN, zone); + if (rc != 0) + continue; + vd->lxvd_type = LXVD_ZVOL; (void) snprintf(vd->lxvd_name, sizeof (vd->lxvd_name), diff --git a/usr/src/uts/common/fs/dev/sdev_zvolops.c b/usr/src/uts/common/fs/dev/sdev_zvolops.c index e236eb3f72..ae35638836 100644 --- a/usr/src/uts/common/fs/dev/sdev_zvolops.c +++ b/usr/src/uts/common/fs/dev/sdev_zvolops.c @@ -447,6 +447,8 @@ devzvol_create_pool_dirs(struct vnode *dvp) nvlist_t *nv = NULL; nvpair_t *elem = NULL; int pools = 0; + zone_t *zone = curzone; + zone_dataset_t *zd; int rc; sdcmn_err13(("devzvol_create_pool_dirs")); @@ -480,6 +482,17 @@ devzvol_create_pool_dirs(struct vnode *dvp) VN_RELE(vp); pools++; } + for (zd = list_head(&zone->zone_datasets); zd != NULL; + zd = list_next(&zone->zone_datasets, zd)) { + struct vnode *vp; + if (zd->zd_alias == NULL) + continue; + rc = VOP_LOOKUP(dvp, zd->zd_alias, &vp, NULL, 0, + NULL, kcred, NULL, 0, NULL); + if (rc == 0) + VN_RELE(vp); + pools++; + } nvlist_free(nv); mutex_enter(&devzvol_mtx); if (devzvol_isopen && pools == 0) { @@ -734,12 +747,31 @@ devzvol_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, * but prof_lookup will also find it via sdev_cache_lookup. */ if (res == ENOENT) { - /* - * We have to create the sdev node for the dymamically - * created zvol. - */ - if (devzvol_mk_ngz_node(parent, nm) != 0) - return (ENOENT); + if (strcmp(parent->sdev_path, ZVOL_DIR) == 0) { + /* + * If this is the top-level dir, make sure + * that dsk/rdsk dirs have been created. + * + * devzvol_mk_ngz_node doesn't work on these, + * and if we do a lookup through here having + * never done a readdir() before on /dev/zvol, + * these nodes won't exist yet. + */ + struct vnode *vp; + (void) devname_lookup_func(parent, "dsk", &vp, + cred, devzvol_create_dir, SDEV_VATTR); + VN_RELE(vp); + (void) devname_lookup_func(parent, "rdsk", &vp, + cred, devzvol_create_dir, SDEV_VATTR); + VN_RELE(vp); + } else { + /* + * We have to create the sdev node for the + * dynamically created zvol. + */ + if (devzvol_mk_ngz_node(parent, nm) != 0) + return (ENOENT); + } res = prof_lookup(dvp, nm, vpp, cred); } @@ -863,6 +895,9 @@ sdev_iter_datasets(struct vnode *dvp, int arg, char *name) sdcmn_err13((" name %s", zc->zc_name)); if (strchr(zc->zc_name, '$') || strchr(zc->zc_name, '%')) goto skip; + if (strrchr(zc->zc_name, '/') == NULL) { + goto skip; + } ptr = strrchr(zc->zc_name, '/') + 1; rc = devzvol_lookup(dvp, ptr, &vpp, NULL, 0, NULL, kcred, NULL, NULL, NULL); diff --git a/usr/src/uts/common/fs/zfs/spa_config.c b/usr/src/uts/common/fs/zfs/spa_config.c index 4719696ca4..25383f0059 100644 --- a/usr/src/uts/common/fs/zfs/spa_config.c +++ b/usr/src/uts/common/fs/zfs/spa_config.c @@ -330,7 +330,7 @@ spa_all_configs(uint64_t *generation) mutex_enter(&spa_namespace_lock); while ((spa = spa_next(spa)) != NULL) { if (INGLOBALZONE(curproc) || - zone_dataset_visible(spa_name(spa), NULL)) { + zone_dataset_visible(spa_name(spa), NULL, B_TRUE)) { mutex_enter(&spa->spa_props_lock); fnvlist_add_nvlist(pools, spa_name(spa), spa->spa_config); diff --git a/usr/src/uts/common/fs/zfs/zfs_ioctl.c b/usr/src/uts/common/fs/zfs/zfs_ioctl.c index 153dcf1502..94cbe34d9f 100644 --- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c +++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c @@ -222,6 +222,8 @@ static uint_t zfs_allow_log_key; typedef int zfs_ioc_legacy_func_t(zfs_cmd_t *); typedef int zfs_ioc_func_t(const char *, nvlist_t *, nvlist_t *); typedef int zfs_secpolicy_func_t(zfs_cmd_t *, nvlist_t *, cred_t *); +typedef int zfs_alias_func_t(zfs_cmd_t *, nvlist_t **, nvlist_t **, cred_t *); +typedef int zfs_unalias_func_t(zfs_cmd_t *, nvlist_t **, cred_t *); typedef enum { NO_NAME, @@ -240,6 +242,8 @@ typedef struct zfs_ioc_vec { zfs_ioc_func_t *zvec_func; zfs_secpolicy_func_t *zvec_secpolicy; zfs_ioc_namecheck_t zvec_namecheck; + zfs_alias_func_t *zvec_alias; + zfs_unalias_func_t *zvec_unalias; boolean_t zvec_allow_log; zfs_ioc_poolcheck_t zvec_pool_check; boolean_t zvec_smush_outnvlist; @@ -311,6 +315,449 @@ __dprintf(const char *file, const char *func, int line, const char *fmt, ...) char *, newfile, char *, func, int, line, char *, buf); } +/* ARGSUSED */ +static int +zfs_unalias_noop(zfs_cmd_t *zc, nvlist_t **innvl, cred_t *cr) +{ + return (0); +} + +/* ARGSUSED */ +static int +zfs_alias_noop(zfs_cmd_t *zc, nvlist_t **innvl, nvlist_t **outnvl, cred_t *cr) +{ + return (0); +} + +/* ARGSUSED */ +static int +zfs_unalias_dsname(zfs_cmd_t *zc, nvlist_t **innvl, cred_t *cr) +{ + char *oname; + int error; + + oname = kmem_zalloc(MAXPATHLEN, KM_SLEEP); + (void) strcpy(oname, zc->zc_name); + error = zone_dataset_unalias(oname, zc->zc_name, MAXPATHLEN); + kmem_free(oname, MAXPATHLEN); + return (error); +} + +/* ARGSUSED */ +static int +zfs_unalias_value(zfs_cmd_t *zc, nvlist_t **innvl, cred_t *cr) +{ + char *oname; + int error; + + oname = kmem_zalloc(MAXPATHLEN * 2, KM_SLEEP); + (void) strcpy(oname, zc->zc_value); + error = zone_dataset_unalias(oname, zc->zc_value, MAXPATHLEN * 2); + kmem_free(oname, MAXPATHLEN * 2); + + if (error != 0) + return (error); + + error = zfs_unalias_dsname(zc, innvl, cr); + + return (error); +} + +/* ARGSUSED */ +static int +zfs_unalias_poolname(zfs_cmd_t *zc, nvlist_t **innvl, cred_t *cr) +{ + zone_t *zone = curzone; + zone_dataset_t *zd = NULL; + char *ptr; + size_t len; + + if (zc->zc_name[0] == '\0') + return (0); + + zc->zc_action_handle = 0; + + /* + * First look for any straight-through zone dataset entries that mean + * that this poolname should not be aliased. + */ + for (zd = list_head(&zone->zone_datasets); zd != NULL; + zd = list_next(&zone->zone_datasets, zd)) { + if (zd->zd_alias != NULL) + continue; + + len = strlen(zd->zd_dataset); + ptr = strchr(zd->zd_dataset, '/'); + if (ptr != NULL) + len = (ptr - zd->zd_dataset); + if (bcmp(zc->zc_name, zd->zd_dataset, len) == 0 && + strlen(zc->zc_name) == len) { + /* + * Stash the zone_dataset_t that matches in the + * zfs_cmd_t, so that we can pull it back out later in + * zfs_alias_poolname. The zc_action_handle field is + * never used by any pool-related ioctls (only by + * IOC_RECV), so overwriting it should be ok. + */ + zc->zc_action_handle = (uint64_t)((uintptr_t)zd); + return (0); + } + } + /* Now try to find an alias. */ + for (zd = list_head(&zone->zone_datasets); zd != NULL; + zd = list_next(&zone->zone_datasets, zd)) { + if (zd->zd_alias == NULL) + continue; + + len = strlen(zd->zd_alias); + if (bcmp(zc->zc_name, zd->zd_alias, len) == 0 + && strlen(zc->zc_name) == len) { + zc->zc_action_handle = (uint64_t)((uintptr_t)zd); + (void) strcpy(zc->zc_name, zd->zd_dataset); + ptr = strchr(zc->zc_name, '/'); + if (ptr != NULL) + *ptr = '\0'; + break; + } + } + + return (0); +} + +/* ARGSUSED */ +static int +zfs_unalias_oneprop_common(zfs_cmd_t *zc, nvlist_t **innvl, cred_t *cr, + const char *key) +{ + char *firstsnap; + char *nfirstsnap = NULL; + int error; + size_t len; + + if (nvlist_lookup_string(*innvl, key, &firstsnap) != 0) + return (EINVAL); + + nfirstsnap = kmem_zalloc(MAXPATHLEN * 2, KM_SLEEP); + error = zone_dataset_unalias(firstsnap, nfirstsnap, MAXPATHLEN * 2); + if (error != 0) + goto out; + error = nvlist_add_string(*innvl, key, nfirstsnap); + if (error != 0) + goto out; + error = zfs_unalias_dsname(zc, innvl, cr); + +out: + if (nfirstsnap != NULL) + kmem_free(nfirstsnap, MAXPATHLEN * 2); + return (error); +} + +/* ARGSUSED */ +static int +zfs_unalias_origin(zfs_cmd_t *zc, nvlist_t **innvl, cred_t *cr) +{ + return (zfs_unalias_oneprop_common(zc, innvl, cr, "origin")); +} + +/* ARGSUSED */ +static int +zfs_unalias_from(zfs_cmd_t *zc, nvlist_t **innvl, cred_t *cr) +{ + return (zfs_unalias_oneprop_common(zc, innvl, cr, "from")); +} + +/* ARGSUSED */ +static int +zfs_unalias_fromsnap(zfs_cmd_t *zc, nvlist_t **innvl, cred_t *cr) +{ + return (zfs_unalias_oneprop_common(zc, innvl, cr, "fromsnap")); +} + +/* ARGSUSED */ +static int +zfs_unalias_firstsnap(zfs_cmd_t *zc, nvlist_t **innvl, cred_t *cr) +{ + return (zfs_unalias_oneprop_common(zc, innvl, cr, "firstsnap")); +} + +/* ARGSUSED */ +static int +zfs_alias_dsname(zfs_cmd_t *zc, nvlist_t **innvl, nvlist_t **outnvl, cred_t *cr) +{ + char *oname; + int error; + if (zc->zc_name[0] == '\0') + return (0); + + /* Otherwise, try to alias as if it's a dataset. */ + oname = kmem_zalloc(MAXPATHLEN, KM_SLEEP); + (void) strcpy(oname, zc->zc_name); + error = zone_dataset_alias(oname, zc->zc_name, MAXPATHLEN); + kmem_free(oname, MAXPATHLEN); + /* But ignore EINVAL, in case it's not actually one. */ + if (error == EINVAL) + error = 0; + return (error); +} + +/* ARGSUSED */ +static int +zfs_alias_value(zfs_cmd_t *zc, nvlist_t **innvl, nvlist_t **outnvl, cred_t *cr) +{ + char *oname; + int error; + if (zc->zc_value[0] == '\0') + return (0); + + oname = kmem_zalloc(MAXPATHLEN * 2, KM_SLEEP); + (void) strcpy(oname, zc->zc_value); + error = zone_dataset_alias(oname, zc->zc_value, MAXPATHLEN * 2); + kmem_free(oname, MAXPATHLEN * 2); + + if (error != 0) + return (error); + + error = zfs_alias_dsname(zc, innvl, outnvl, cr); + + return (error); +} + +/* ARGSUSED */ +static int +zfs_alias_string_value(zfs_cmd_t *zc, nvlist_t **innvl, nvlist_t **outnvl, + cred_t *cr) +{ + char *oname; + int error; + if (zc->zc_string[0] == '\0') + return (0); + + oname = kmem_zalloc(MAXNAMELEN, KM_SLEEP); + (void) strcpy(oname, zc->zc_string); + error = zone_dataset_alias(oname, zc->zc_string, MAXNAMELEN); + kmem_free(oname, MAXNAMELEN); + + if (error != 0) + return (error); + + error = zfs_alias_value(zc, innvl, outnvl, cr); + + return (error); +} + +/* ARGSUSED */ +static int +zfs_alias_poolname(zfs_cmd_t *zc, nvlist_t **innvl, nvlist_t **outnvl, + cred_t *cr) +{ + zone_dataset_t *zd; + + if (zc->zc_name[0] == '\0') + return (0); + + /* + * We stashed the pointer to the zone_dataset_t in the zc_action_handle + * member back in zfs_unalias_poolname. + */ + zd = (zone_dataset_t *)((uintptr_t)zc->zc_action_handle); + VERIFY(zd != NULL); + + /* Zero it so we don't leak the pointer back to userland. */ + zc->zc_action_handle = 0; + + if (zd->zd_alias == NULL) + return (0); + + (void) strcpy(zc->zc_name, zd->zd_alias); + + return (0); +} + +static int +zfs_map_nvlist_kv_str(nvlist_t *nvl, nvlist_t *innvl, boolean_t dovalues, + int (*func)(const char *, char *, size_t)) +{ + nvpair_t *pair; + int error = 0; + char *nname = NULL, *nvalue = NULL; + + for (pair = nvlist_next_nvpair(innvl, NULL); pair != NULL; + pair = nvlist_next_nvpair(innvl, pair)) { + const char *name = nvpair_name(pair); + char *value; + + if (dovalues) { + if (nvpair_type(pair) != DATA_TYPE_STRING) { + error = EINVAL; + goto out; + } + error = nvpair_value_string(pair, &value); + if (error != 0) + goto out; + } + + if (nname == NULL) + nname = kmem_zalloc(MAXPATHLEN * 2, KM_SLEEP); + error = (*func)(name, nname, MAXPATHLEN * 2); + if (error != 0) + goto out; + + if (dovalues) { + if (nvalue == NULL) + nvalue = kmem_zalloc(MAXPATHLEN * 2, KM_SLEEP); + error = (*func)(value, nvalue, MAXPATHLEN * 2); + if (error != 0) + goto out; + + error = nvlist_add_string(nvl, nname, nvalue); + if (error != 0) + goto out; + } else { + error = nvlist_add_named_nvpair(nvl, nname, pair); + if (error != 0) + goto out; + } + } + +out: + if (nname != NULL) + kmem_free(nname, MAXPATHLEN * 2); + if (nvalue != NULL) + kmem_free(nvalue, MAXPATHLEN * 2); + return (error); +} + +static int +zfs_unalias_nvl_common(nvlist_t **innvl, boolean_t dovalues) +{ + nvlist_t *nvl = NULL; + int error; + + if (*innvl == NULL) + return (EINVAL); + + error = nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); + if (error != 0) + goto out; + + error = zfs_map_nvlist_kv_str(nvl, *innvl, dovalues, + zone_dataset_unalias); + if (error != 0) + goto out; + + nvlist_free(*innvl); + *innvl = nvl; + nvl = NULL; + +out: + nvlist_free(nvl); + return (error); +} + +/* ARGSUSED */ +static int +zfs_unalias_nvlkeys(zfs_cmd_t *zc, nvlist_t **innvl, cred_t *cr) +{ + int error; + error = zfs_unalias_poolname(zc, innvl, cr); + if (error) + return (error); + error = zfs_unalias_nvl_common(innvl, B_FALSE); + return (error); +} + +/* ARGSUSED */ +static int +zfs_unalias_nvl(zfs_cmd_t *zc, nvlist_t **innvl, cred_t *cr) +{ + int error; + error = zfs_unalias_poolname(zc, innvl, cr); + if (error) + return (error); + error = zfs_unalias_nvl_common(innvl, B_TRUE); + return (error); +} + +static int +zfs_alias_nvl_common(nvlist_t **outnvl, boolean_t dovalues) +{ + nvlist_t *nvl = NULL; + int error; + + if (*outnvl == NULL) + return (EINVAL); + + error = nvlist_alloc(&nvl, NV_UNIQUE_NAME, KM_SLEEP); + if (error != 0) + goto out; + + error = zfs_map_nvlist_kv_str(nvl, *outnvl, dovalues, + zone_dataset_alias); + if (error != 0) + goto out; + + nvlist_free(*outnvl); + *outnvl = nvl; + nvl = NULL; + +out: + nvlist_free(nvl); + return (error); +} + +/* ARGSUSED */ +static int +zfs_alias_nvlkeys(zfs_cmd_t *zc, nvlist_t **innvl, nvlist_t **outnvl, + cred_t *cr) +{ + int error; + error = zfs_alias_poolname(zc, innvl, outnvl, cr); + if (error) + return (error); + error = zfs_alias_nvl_common(outnvl, B_FALSE); + return (error); +} + +/* ARGSUSED */ +static int +zfs_unalias_subnvl_common(zfs_cmd_t *zc, nvlist_t **innvl, cred_t *cr, + const char *key, boolean_t dovalues) +{ + nvlist_t *snaps; + int error; + + error = zfs_unalias_poolname(zc, innvl, cr); + if (error) + return (error); + + if (*innvl == NULL) + return (EINVAL); + if (nvlist_lookup_nvlist(*innvl, key, &snaps) != 0) + return (EINVAL); + + error = zfs_unalias_nvl_common(&snaps, dovalues); + if (error != 0) + return (error); + + error = nvlist_add_nvlist(*innvl, key, snaps); + nvlist_free(snaps); + return (error); +} + +/* ARGSUSED */ +static int +zfs_unalias_holds(zfs_cmd_t *zc, nvlist_t **innvl, cred_t *cr) +{ + return (zfs_unalias_subnvl_common(zc, innvl, cr, "holds", B_FALSE)); +} + +/* ARGSUSED */ +static int +zfs_unalias_snaps(zfs_cmd_t *zc, nvlist_t **innvl, cred_t *cr) +{ + return (zfs_unalias_subnvl_common(zc, innvl, cr, "snaps", B_FALSE)); +} + static void history_str_free(char *buf) { @@ -433,7 +880,18 @@ static int zfs_secpolicy_read(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) { if (INGLOBALZONE(curproc) || - zone_dataset_visible(zc->zc_name, NULL)) + zone_dataset_visible(zc->zc_name, NULL, B_FALSE)) + return (0); + + return (SET_ERROR(ENOENT)); +} + +/* ARGSUSED */ +static int +zfs_secpolicy_read_pool(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr) +{ + if (INGLOBALZONE(curproc) || + zone_dataset_visible(zc->zc_name, NULL, B_TRUE)) return (0); return (SET_ERROR(ENOENT)); @@ -449,7 +907,7 @@ zfs_dozonecheck_impl(const char *dataset, uint64_t zoned, cred_t *cr) * so they don't see EPERM on something they shouldn't know about. */ if (!INGLOBALZONE(curproc) && - !zone_dataset_visible(dataset, &writable)) + !zone_dataset_visible(dataset, &writable, B_FALSE)) return (SET_ERROR(ENOENT)); if (INGLOBALZONE(curproc)) { @@ -1669,6 +2127,92 @@ zfs_ioc_pool_export(zfs_cmd_t *zc) return (error); } +static boolean_t +zfs_zone_ds_matches_alias(const char *dataset, zone_dataset_t *zd) +{ + size_t len; + + len = strlen(zd->zd_dataset); + if (strlen(dataset) >= len && + bcmp(dataset, zd->zd_dataset, len) == 0 && + (dataset[len] == '\0' || dataset[len] == '/' || + dataset[len] == '@')) { + return (B_TRUE); + } + + len = strlen(dataset); + if (dataset[len - 1] == '/') + len--; /* Ignore trailing slash */ + if (len < strlen(zd->zd_dataset) && + bcmp(dataset, zd->zd_dataset, len) == 0 && + zd->zd_dataset[len] == '/') { + return (B_TRUE); + } + + return (B_FALSE); +} + +static int +zfs_alias_pool_configs(nvlist_t **configs) +{ + nvlist_t *all = *configs; + nvlist_t *pool = NULL; + nvpair_t *elem = NULL; + const char *name; + zone_dataset_t *zd; + boolean_t found; + int error = 0; + zone_t *zone = curzone; + + *configs = NULL; + VERIFY(nvlist_alloc(configs, NV_UNIQUE_NAME, KM_SLEEP) == 0); + + /* + * For each pool, we want to create an entry in the final list, + * and possibly rename it to reflect an alias. It's also possible + * we will need to do both (for two delegated datasets under the + * same pool, one aliased and one not). + * + * Since we made configs with NV_UNIQUE_NAME, re-adding the same + * pool multiple times is not really a problem, so we just do a + * naive nested loop over all of the possible combinations. + */ + while ((elem = nvlist_next_nvpair(all, elem)) != NULL) { + name = nvpair_name(elem); + VERIFY(nvpair_value_nvlist(elem, &pool) == 0); + + found = B_FALSE; + for (zd = list_head(&zone->zone_datasets); zd != NULL; + zd = list_next(&zone->zone_datasets, zd)) { + if (zfs_zone_ds_matches_alias(name, zd) == B_TRUE) { + if (zd->zd_alias == NULL) { + VERIFY(nvlist_add_string(pool, + "name", name) == 0); + VERIFY(nvlist_add_nvlist(*configs, + name, pool) == 0); + } else { + VERIFY(nvlist_add_string(pool, + "name", zd->zd_alias) == 0); + VERIFY(nvlist_add_nvlist(*configs, + zd->zd_alias, pool) == 0); + } + found = B_TRUE; + } + } + /* + * If we found no zone_dataset_t, it was a VFS-style pass- + * through into the zone. This kind has no aliasing, so + * add the pool under its usual name. + */ + if (found == B_FALSE) + VERIFY(nvlist_add_nvlist(*configs, name, pool) == 0); + } + +out: + nvlist_free(all); + return (error); +} + static int zfs_ioc_pool_configs(zfs_cmd_t *zc) { @@ -1678,6 +2222,14 @@ zfs_ioc_pool_configs(zfs_cmd_t *zc) if ((configs = spa_all_configs(&zc->zc_cookie)) == NULL) return (SET_ERROR(EEXIST)); + if (!INGLOBALZONE(curproc)) { + error = zfs_alias_pool_configs(&configs); + if (error != 0) { + nvlist_free(configs); + return (SET_ERROR(error)); + } + } + error = put_nvlist(zc, configs); nvlist_free(configs); @@ -1698,12 +2250,31 @@ static int zfs_ioc_pool_stats(zfs_cmd_t *zc) { nvlist_t *config; + zone_dataset_t *zd; int error; int ret = 0; error = spa_get_stats(zc->zc_name, &config, zc->zc_value, sizeof (zc->zc_value)); + /* + * If we're returning the name property to a non-global zone, apply + * aliases as needed. + */ + if (config != NULL && !INGLOBALZONE(curproc)) { + /* + * We stashed the pointer to the zone_dataset_t in the + * zc_action_handle member back in zfs_unalias_poolname. + */ + zd = (zone_dataset_t *)((uintptr_t)zc->zc_action_handle); + if (zd == NULL) + return (EINVAL); + + if (zd->zd_alias != NULL) + VERIFY0(nvlist_add_string(config, "name", + zd->zd_alias)); + } + if (config != NULL) { ret = put_nvlist(zc, config); nvlist_free(config); @@ -2140,6 +2711,90 @@ zfs_ioc_vdev_setfru(zfs_cmd_t *zc) } static int +zfs_alias_propsrcs(nvlist_t **outnvl) +{ + nvlist_t *prop; + nvpair_t *pair; + int error = 0; + boolean_t remorigin = B_FALSE; + char *src; + char *newsrc = kmem_zalloc(MAXPATHLEN * 2, KM_SLEEP); + + if (*outnvl == NULL) + goto out; + + for (pair = nvlist_next_nvpair(*outnvl, NULL); pair != NULL; + pair = nvlist_next_nvpair(*outnvl, pair)) { + const char *name = nvpair_name(pair); + if (nvpair_type(pair) != DATA_TYPE_NVLIST) { + error = EINVAL; + goto out; + } + if ((error = nvpair_value_nvlist(pair, &prop)) != 0) + goto out; + + if (strcmp(name, "origin") == 0 || + strcmp(name, "prevsnap") == 0) { + error = nvlist_lookup_string(prop, "value", &src); + if (error != 0) + goto out; + error = zone_dataset_alias(src, newsrc, MAXPATHLEN * 2); + if (error == EINVAL) { + remorigin = B_TRUE; + } else if (error != 0) { + goto out; + } + error = nvlist_add_string(prop, "value", newsrc); + if (error != 0) + goto out; + } + if (strcmp(name, "clones") == 0) { + nvlist_t *clones; + error = nvlist_lookup_nvlist(prop, "value", &clones); + if (error != 0) + goto out; + error = zfs_alias_nvl_common(&clones, B_FALSE); + if (error != 0) + goto out; + error = nvlist_add_nvlist(prop, "value", clones); + nvlist_free(clones); + if (error != 0) + goto out; + } + + if (nvlist_lookup_string(prop, "source", &src) != 0) + continue; + if (strlen(src) == 0) + continue; + + error = zone_dataset_alias(src, newsrc, MAXPATHLEN * 2); + if (error == EINVAL) { + /* + * This is a parent of an aliased dataset and has no + * representation in the zone. + */ + (void) strcpy(newsrc, "global zone"); + } else if (error != 0) { + goto out; + } + + if ((error = nvlist_add_string(prop, "source", newsrc)) != 0) + goto out; + } + + /* + * If the dataset has an origin that's outside the namespace available + * to this zone, just drop the origin property. + */ + if (remorigin) + error = nvlist_remove_all(*outnvl, "origin"); + +out: + kmem_free(newsrc, MAXPATHLEN * 2); + return (error); +} + +static int zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os, boolean_t cachedpropsonly) { @@ -2166,7 +2821,10 @@ zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os, return (error); VERIFY0(error); } - error = put_nvlist(zc, nv); + if (!INGLOBALZONE(curproc)) + error = zfs_alias_propsrcs(&nv); + if (error == 0) + error = put_nvlist(zc, nv); nvlist_free(nv); } @@ -2320,8 +2978,10 @@ dataset_name_hidden(const char *name) return (B_TRUE); if (strchr(name, '%') != NULL) return (B_TRUE); - if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL)) + if (!INGLOBALZONE(curproc) && !zone_dataset_visible(name, NULL, + B_FALSE)) { return (B_TRUE); + } return (B_FALSE); } @@ -3021,6 +3681,7 @@ zfs_ioc_pool_get_props(zfs_cmd_t *zc) spa_t *spa; int error; nvlist_t *nvp = NULL; + nvlist_t *prop; if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) { /* @@ -3037,6 +3698,25 @@ zfs_ioc_pool_get_props(zfs_cmd_t *zc) spa_close(spa, FTAG); } + /* Apply aliases to pool info given to non-global zones. */ + if (error == 0 && zc->zc_nvlist_dst != 0 && + !INGLOBALZONE(curproc) && + nvlist_lookup_nvlist(nvp, "name", &prop) == 0) { + zone_dataset_t *zd; + + /* + * We stashed the pointer to the zone_dataset_t in the + * zc_action_handle member back in zfs_unalias_poolname. + */ + zd = (zone_dataset_t *)((uintptr_t)zc->zc_action_handle); + if (zd == NULL) + return (EINVAL); + + if (zd->zd_alias != NULL) + VERIFY0(nvlist_add_string(prop, "value", + zd->zd_alias)); + } + if (error == 0 && zc->zc_nvlist_dst != 0) error = put_nvlist(zc, nvp); else @@ -3652,6 +4332,7 @@ zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) nvlist_t *snaps; nvpair_t *pair; boolean_t defer; + int error; if (nvlist_lookup_nvlist(innvl, "snaps", &snaps) != 0) return (SET_ERROR(EINVAL)); @@ -3662,7 +4343,9 @@ zfs_ioc_destroy_snaps(const char *poolname, nvlist_t *innvl, nvlist_t *outnvl) zfs_unmount_snap(nvpair_name(pair)); } - return (dsl_destroy_snapshots_nvl(snaps, defer, outnvl)); + error = dsl_destroy_snapshots_nvl(snaps, defer, outnvl); + + return (error); } /* @@ -6442,6 +7125,7 @@ static void zfs_ioctl_register_legacy(const char *name, zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck, + zfs_unalias_func_t *unalias, zfs_alias_func_t *alias, boolean_t log_history, zfs_ioc_poolcheck_t pool_check) { zfs_ioc_vec_t *vec = &zfs_ioc_vec[ioc - ZFS_IOC_FIRST]; @@ -6455,6 +7139,8 @@ zfs_ioctl_register_legacy(const char *name, zfs_ioc_t ioc, vec->zvec_legacy_func = func; vec->zvec_secpolicy = secpolicy; vec->zvec_namecheck = namecheck; + vec->zvec_unalias = unalias; + vec->zvec_alias = alias; vec->zvec_allow_log = log_history; vec->zvec_pool_check = pool_check; } @@ -6466,6 +7152,7 @@ zfs_ioctl_register_legacy(const char *name, zfs_ioc_t ioc, static void zfs_ioctl_register(const char *name, zfs_ioc_t ioc, zfs_ioc_func_t *func, zfs_secpolicy_func_t *secpolicy, zfs_ioc_namecheck_t namecheck, + zfs_unalias_func_t *unalias, zfs_alias_func_t *alias, zfs_ioc_poolcheck_t pool_check, boolean_t smush_outnvlist, boolean_t allow_log) { @@ -6483,6 +7170,8 @@ zfs_ioctl_register(const char *name, zfs_ioc_t ioc, zfs_ioc_func_t *func, vec->zvec_func = func; vec->zvec_secpolicy = secpolicy; vec->zvec_namecheck = namecheck; + vec->zvec_unalias = unalias; + vec->zvec_alias = alias; vec->zvec_pool_check = pool_check; vec->zvec_smush_outnvlist = smush_outnvlist; vec->zvec_allow_log = allow_log; @@ -6493,40 +7182,45 @@ zfs_ioctl_register_pool(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, zfs_secpolicy_func_t *secpolicy, boolean_t log_history, zfs_ioc_poolcheck_t pool_check) { - zfs_ioctl_register_legacy(NULL, ioc, func, secpolicy, - POOL_NAME, log_history, pool_check); + zfs_ioctl_register_legacy(ioc, func, secpolicy, + POOL_NAME, zfs_unalias_poolname, zfs_alias_poolname, + log_history, pool_check); } static void zfs_ioctl_register_dataset_nolog(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, zfs_secpolicy_func_t *secpolicy, zfs_ioc_poolcheck_t pool_check) { - zfs_ioctl_register_legacy(NULL, ioc, func, secpolicy, - DATASET_NAME, B_FALSE, pool_check); + zfs_ioctl_register_legacy(ioc, func, secpolicy, + DATASET_NAME, zfs_unalias_dsname, zfs_alias_dsname, + B_FALSE, pool_check); } static void zfs_ioctl_register_pool_modify(const char *name, zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func) { - zfs_ioctl_register_legacy(name, ioc, func, zfs_secpolicy_config, - POOL_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); + zfs_ioctl_register_legacy(ioc, func, zfs_secpolicy_config, + POOL_NAME, zfs_unalias_poolname, zfs_alias_poolname, + B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); } static void zfs_ioctl_register_pool_meta(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, zfs_secpolicy_func_t *secpolicy) { - zfs_ioctl_register_legacy(NULL, ioc, func, secpolicy, - NO_NAME, B_FALSE, POOL_CHECK_NONE); + zfs_ioctl_register_legacy(ioc, func, secpolicy, + NO_NAME, zfs_unalias_poolname, zfs_alias_poolname, + B_FALSE, POOL_CHECK_NONE); } static void zfs_ioctl_register_dataset_read_secpolicy(zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, zfs_secpolicy_func_t *secpolicy) { - zfs_ioctl_register_legacy(NULL, ioc, func, secpolicy, - DATASET_NAME, B_FALSE, POOL_CHECK_SUSPENDED); + zfs_ioctl_register_legacy(ioc, func, secpolicy, + DATASET_NAME, zfs_unalias_dsname, zfs_alias_dsname, B_FALSE, + POOL_CHECK_SUSPENDED); } static void @@ -6540,8 +7234,9 @@ static void zfs_ioctl_register_dataset_modify(const char *name, zfs_ioc_t ioc, zfs_ioc_legacy_func_t *func, zfs_secpolicy_func_t *secpolicy) { - zfs_ioctl_register_legacy(name, ioc, func, secpolicy, - DATASET_NAME, B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); + zfs_ioctl_register_legacy(ioc, func, secpolicy, + DATASET_NAME, zfs_unalias_dsname, zfs_alias_dsname, B_TRUE, + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); } static void @@ -6549,30 +7244,37 @@ zfs_ioctl_init(void) { zfs_ioctl_register("snapshot", ZFS_IOC_SNAPSHOT, zfs_ioc_snapshot, zfs_secpolicy_snapshot, POOL_NAME, + zfs_unalias_snaps, zfs_alias_nvlkeys, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); zfs_ioctl_register("log_history", ZFS_IOC_LOG_HISTORY, zfs_ioc_log_history, zfs_secpolicy_log_history, NO_NAME, + zfs_unalias_noop, zfs_alias_noop, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_FALSE); zfs_ioctl_register("space_snaps", ZFS_IOC_SPACE_SNAPS, zfs_ioc_space_snaps, zfs_secpolicy_read, DATASET_NAME, + zfs_unalias_firstsnap, zfs_alias_dsname, POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); zfs_ioctl_register("send", ZFS_IOC_SEND_NEW, zfs_ioc_send_new, zfs_secpolicy_send_new, DATASET_NAME, + zfs_unalias_fromsnap, zfs_alias_dsname, POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); zfs_ioctl_register("send_space", ZFS_IOC_SEND_SPACE, zfs_ioc_send_space, zfs_secpolicy_read, DATASET_NAME, + zfs_unalias_from, zfs_alias_dsname, POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); zfs_ioctl_register("create", ZFS_IOC_CREATE, zfs_ioc_create, zfs_secpolicy_create_clone, DATASET_NAME, + zfs_unalias_dsname, zfs_alias_dsname, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); zfs_ioctl_register("clone", ZFS_IOC_CLONE, zfs_ioc_clone, zfs_secpolicy_create_clone, DATASET_NAME, + zfs_unalias_origin, zfs_alias_dsname, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); zfs_ioctl_register("remap", ZFS_IOC_REMAP, @@ -6581,34 +7283,41 @@ zfs_ioctl_init(void) zfs_ioctl_register("destroy_snaps", ZFS_IOC_DESTROY_SNAPS, zfs_ioc_destroy_snaps, zfs_secpolicy_destroy_snaps, POOL_NAME, + zfs_unalias_snaps, zfs_alias_nvlkeys, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); zfs_ioctl_register("hold", ZFS_IOC_HOLD, zfs_ioc_hold, zfs_secpolicy_hold, POOL_NAME, + zfs_unalias_holds, zfs_alias_nvlkeys, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); zfs_ioctl_register("release", ZFS_IOC_RELEASE, zfs_ioc_release, zfs_secpolicy_release, POOL_NAME, + zfs_unalias_nvlkeys, zfs_alias_nvlkeys, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); zfs_ioctl_register("get_holds", ZFS_IOC_GET_HOLDS, zfs_ioc_get_holds, zfs_secpolicy_read, DATASET_NAME, + zfs_unalias_dsname, zfs_alias_dsname, POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); zfs_ioctl_register("rollback", ZFS_IOC_ROLLBACK, zfs_ioc_rollback, zfs_secpolicy_rollback, DATASET_NAME, + zfs_unalias_dsname, zfs_alias_dsname, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_FALSE, B_TRUE); zfs_ioctl_register("bookmark", ZFS_IOC_BOOKMARK, zfs_ioc_bookmark, zfs_secpolicy_bookmark, POOL_NAME, + zfs_unalias_nvl, zfs_alias_nvlkeys, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); zfs_ioctl_register("get_bookmarks", ZFS_IOC_GET_BOOKMARKS, zfs_ioc_get_bookmarks, zfs_secpolicy_read, DATASET_NAME, + zfs_unalias_dsname, zfs_alias_dsname, POOL_CHECK_SUSPENDED, B_FALSE, B_FALSE); zfs_ioctl_register("destroy_bookmarks", ZFS_IOC_DESTROY_BOOKMARKS, zfs_ioc_destroy_bookmarks, zfs_secpolicy_destroy_bookmarks, - POOL_NAME, + POOL_NAME, zfs_unalias_nvlkeys, zfs_alias_nvlkeys, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY, B_TRUE, B_TRUE); zfs_ioctl_register("channel_program", ZFS_IOC_CHANNEL_PROGRAM, @@ -6650,9 +7359,9 @@ zfs_ioctl_init(void) /* IOCTLS that use the legacy function signature */ - zfs_ioctl_register_legacy("pool_freeze", ZFS_IOC_POOL_FREEZE, - zfs_ioc_pool_freeze, zfs_secpolicy_config, NO_NAME, B_FALSE, - POOL_CHECK_READONLY); + zfs_ioctl_register_legacy(ZFS_IOC_POOL_FREEZE, zfs_ioc_pool_freeze, + zfs_secpolicy_config, NO_NAME, zfs_unalias_poolname, + zfs_alias_poolname, B_FALSE, POOL_CHECK_READONLY); zfs_ioctl_register_pool(ZFS_IOC_POOL_CREATE, zfs_ioc_pool_create, zfs_secpolicy_config, B_TRUE, POOL_CHECK_NONE); @@ -6703,9 +7412,9 @@ zfs_ioctl_init(void) zfs_secpolicy_config, B_FALSE, POOL_CHECK_NONE); zfs_ioctl_register_pool(ZFS_IOC_POOL_STATS, zfs_ioc_pool_stats, - zfs_secpolicy_read, B_FALSE, POOL_CHECK_NONE); + zfs_secpolicy_read_pool, B_FALSE, POOL_CHECK_NONE); zfs_ioctl_register_pool(ZFS_IOC_POOL_GET_PROPS, zfs_ioc_pool_get_props, - zfs_secpolicy_read, B_FALSE, POOL_CHECK_NONE); + zfs_secpolicy_read_pool, B_FALSE, POOL_CHECK_NONE); zfs_ioctl_register_pool(ZFS_IOC_ERROR_LOG, zfs_ioc_error_log, zfs_secpolicy_inject, B_FALSE, POOL_CHECK_SUSPENDED); @@ -6724,8 +7433,10 @@ zfs_ioctl_init(void) zfs_ioctl_register_pool(ZFS_IOC_POOL_REOPEN, zfs_ioc_pool_reopen, zfs_secpolicy_config, B_TRUE, POOL_CHECK_SUSPENDED); - zfs_ioctl_register_dataset_read(ZFS_IOC_SPACE_WRITTEN, - zfs_ioc_space_written); + zfs_ioctl_register_legacy(ZFS_IOC_SPACE_WRITTEN, + zfs_ioc_space_written, zfs_secpolicy_read, DATASET_NAME, + zfs_unalias_value, zfs_alias_value, B_FALSE, + POOL_CHECK_SUSPENDED); zfs_ioctl_register_dataset_read(ZFS_IOC_OBJSET_RECVD_PROPS, zfs_ioc_objset_recvd_props); zfs_ioctl_register_dataset_read(ZFS_IOC_NEXT_OBJ, @@ -6743,8 +7454,10 @@ zfs_ioctl_init(void) zfs_ioctl_register_dataset_read(ZFS_IOC_SEND_PROGRESS, zfs_ioc_send_progress); - zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_DIFF, - zfs_ioc_diff, zfs_secpolicy_diff); + zfs_ioctl_register_legacy(ZFS_IOC_DIFF, + zfs_ioc_diff, zfs_secpolicy_diff, DATASET_NAME, + zfs_unalias_value, zfs_alias_value, B_FALSE, + POOL_CHECK_SUSPENDED); zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_OBJ_TO_STATS, zfs_ioc_obj_to_stats, zfs_secpolicy_diff); zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_OBJ_TO_PATH, @@ -6756,17 +7469,23 @@ zfs_ioctl_init(void) zfs_ioctl_register_dataset_read_secpolicy(ZFS_IOC_SEND, zfs_ioc_send, zfs_secpolicy_send); - zfs_ioctl_register_dataset_modify("set_prop", ZFS_IOC_SET_PROP, - zfs_ioc_set_prop, zfs_secpolicy_none); - zfs_ioctl_register_dataset_modify("destroy", ZFS_IOC_DESTROY, - zfs_ioc_destroy, zfs_secpolicy_destroy); - zfs_ioctl_register_dataset_modify("rename", ZFS_IOC_RENAME, - zfs_ioc_rename, zfs_secpolicy_rename); - zfs_ioctl_register_dataset_modify("recv", ZFS_IOC_RECV, zfs_ioc_recv, - zfs_secpolicy_recv); - zfs_ioctl_register_dataset_modify("promote", ZFS_IOC_PROMOTE, - zfs_ioc_promote, zfs_secpolicy_promote); - zfs_ioctl_register_dataset_modify("inherit_prop", ZFS_IOC_INHERIT_PROP, + zfs_ioctl_register_dataset_modify(ZFS_IOC_SET_PROP, zfs_ioc_set_prop, + zfs_secpolicy_none); + zfs_ioctl_register_dataset_modify(ZFS_IOC_DESTROY, zfs_ioc_destroy, + zfs_secpolicy_destroy); + zfs_ioctl_register_legacy(ZFS_IOC_RENAME, zfs_ioc_rename, + zfs_secpolicy_rename, DATASET_NAME, + zfs_unalias_value, zfs_alias_value, + B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); + zfs_ioctl_register_legacy(ZFS_IOC_RECV, zfs_ioc_recv, + zfs_secpolicy_recv, DATASET_NAME, + zfs_unalias_value, zfs_alias_value, + B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); + zfs_ioctl_register_legacy(ZFS_IOC_PROMOTE, zfs_ioc_promote, + zfs_secpolicy_promote, DATASET_NAME, + zfs_unalias_value, zfs_alias_string_value, + B_TRUE, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); + zfs_ioctl_register_dataset_modify(ZFS_IOC_INHERIT_PROP, zfs_ioc_inherit_prop, zfs_secpolicy_inherit_prop); zfs_ioctl_register_dataset_modify("set_fsacl", ZFS_IOC_SET_FSACL, zfs_ioc_set_fsacl, zfs_secpolicy_set_fsacl); @@ -6952,11 +7671,22 @@ zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) goto out; } + /* Make certain zc_name is NULL-terminated. */ + zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; + /* - * Ensure that all pool/dataset names are valid before we pass down to - * the lower layers. + * Non-global zones can have dataset aliases which need to be applied + * before we pass down to lower layers. + */ + if (!INGLOBALZONE(curproc)) { + error = (*vec->zvec_unalias)(zc, &innvl, cr); + if (error != 0) + goto out; + } + + /* + * Ensure that all pool/dataset names are valid. */ - zc->zc_name[sizeof (zc->zc_name) - 1] = '\0'; switch (vec->zvec_namecheck) { case POOL_NAME: if (pool_namecheck(zc->zc_name, NULL, NULL) != 0) @@ -7037,6 +7767,14 @@ zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) } fnvlist_free(lognv); + /* + * Successful return in non-global zone: alias anything in the + * zc or outnvl that we need to before smush + copyout. + */ + if (error == 0 && !INGLOBALZONE(curproc)) { + error = (*vec->zvec_alias)(zc, &innvl, &outnvl, cr); + } + if (!nvlist_empty(outnvl) || zc->zc_nvlist_dst_size != 0) { int smusherror = 0; if (vec->zvec_smush_outnvlist) { @@ -7078,6 +7816,16 @@ zfsdev_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cr, int *rvalp) fnvlist_free(lognv); kmem_free(msg, len); } + + /* + * On successful returns to a non-global zone, make sure any + * necessary un-aliasing we applied earlier to zc_name is + * returned to the way it was before. + */ + if (error == 0 && !INGLOBALZONE(curproc)) { + nvlist_t *outnvl = NULL; + error = (*vec->zvec_alias)(zc, &innvl, &outnvl, cr); + } } out: diff --git a/usr/src/uts/common/fs/zfs/zfs_vfsops.c b/usr/src/uts/common/fs/zfs/zfs_vfsops.c index 6b61cd7a84..3711f906e1 100644 --- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c +++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c @@ -1819,6 +1819,7 @@ out: static int zfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) { + boolean_t free_osname = B_FALSE; char *osname; pathname_t spn; int error = 0; @@ -1853,7 +1854,23 @@ zfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) if (error = pn_get(uap->spec, fromspace, &spn)) return (error); - osname = spn.pn_path; + + /* + * Non-global zones can be configured to know a delegated dataset by + * an alias, rather than the full objset name. + */ + if (!INGLOBALZONE(curproc)) { + free_osname = B_TRUE; + osname = kmem_alloc(MAXPATHLEN, KM_SLEEP); + + if (zone_dataset_unalias(spn.pn_path, osname, + MAXPATHLEN) != 0) { + error = SET_ERROR(EINVAL); + goto out; + } + } else { + osname = spn.pn_path; + } /* * Check for mount privilege? @@ -1891,8 +1908,8 @@ zfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) * Refuse to mount a filesystem if we are in a local zone and the * dataset is not visible. */ - if (!INGLOBALZONE(curproc) && - (!zone_dataset_visible(osname, &canwrite) || !canwrite)) { + if (!INGLOBALZONE(curproc) && (!zone_dataset_visible(osname, + &canwrite, B_FALSE) || !canwrite)) { error = SET_ERROR(EPERM); goto out; } @@ -1922,6 +1939,9 @@ zfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) VFS_HOLD(mvp->v_vfsp); out: + if (free_osname) { + kmem_free(osname, MAXPATHLEN); + } pn_free(&spn); return (error); } diff --git a/usr/src/uts/common/fs/zfs/zvol.c b/usr/src/uts/common/fs/zfs/zvol.c index 3ed5977c20..742a0b9c40 100644 --- a/usr/src/uts/common/fs/zfs/zvol.c +++ b/usr/src/uts/common/fs/zfs/zvol.c @@ -475,9 +475,19 @@ zil_replay_func_t *zvol_replay_vector[TX_MAX_TYPE] = { }; int -zvol_name2minor(const char *name, minor_t *minor) +zvol_name2minor(const char *iname, minor_t *minor) { zvol_state_t *zv; + int error; + const char *name = iname; + char namebuf[MAXPATHLEN]; + + if (!INGLOBALZONE(curproc)) { + error = zone_dataset_unalias(iname, namebuf, MAXPATHLEN); + if (error != 0) + return (error); + name = namebuf; + } mutex_enter(&zfsdev_state_lock); zv = zvol_minor_lookup(name); @@ -491,8 +501,9 @@ zvol_name2minor(const char *name, minor_t *minor) * Create a minor node (plus a whole lot more) for the specified volume. */ int -zvol_create_minor(const char *name) +zvol_create_minor(const char *iname) { + zfs_soft_state_t *zs; zvol_state_t *zv; objset_t *os; @@ -500,6 +511,15 @@ zvol_create_minor(const char *name) minor_t minor = 0; char chrbuf[30], blkbuf[30]; int error; + char namebuf[MAXPATHLEN]; + const char *name = iname; + + if (!INGLOBALZONE(curproc)) { + error = zone_dataset_unalias(iname, namebuf, MAXPATHLEN); + if (error != 0) + return (error); + name = namebuf; + } mutex_enter(&zfsdev_state_lock); diff --git a/usr/src/uts/common/os/zone.c b/usr/src/uts/common/os/zone.c index fa841df9ff..8293ff6ea3 100644 --- a/usr/src/uts/common/os/zone.c +++ b/usr/src/uts/common/os/zone.c @@ -1378,6 +1378,9 @@ zone_free_datasets(zone_t *zone) next = list_next(&zone->zone_datasets, t); list_remove(&zone->zone_datasets, t); kmem_free(t->zd_dataset, strlen(t->zd_dataset) + 1); + if (t->zd_alias != NULL) { + kmem_free(t->zd_alias, strlen(t->zd_alias) + 1); + } kmem_free(t, sizeof (*t)); } list_destroy(&zone->zone_datasets); @@ -4985,15 +4988,67 @@ zone_set_label(zone_t *zone, const bslabel_t *lab, uint32_t doi) } /* - * Parses a comma-separated list of ZFS datasets into a per-zone dictionary. + * Check for a valid ZFS dataset name. This function mirrors the rules + * for ZFS dataset names checked by "dataset_namecheck()". + */ + +static boolean_t +parse_zfs_valid_char(char c) +{ + return ((c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == '-' || c == '_' || c == '.' || c == ':' || c == ' '); +} + +static void +parse_zfs_insert(zone_t *zone, const char *dataset, size_t dataset_len, + const char *alias, size_t alias_len) +{ + zone_dataset_t *zd = kmem_zalloc(sizeof (zone_dataset_t), KM_SLEEP); + + VERIFY(dataset != NULL); + VERIFY(dataset_len > 0); + zd->zd_dataset = kmem_alloc(dataset_len + 1, KM_SLEEP); + bcopy(dataset, zd->zd_dataset, dataset_len); + zd->zd_dataset[dataset_len] = '\0'; + + if (alias != NULL) { + VERIFY(alias_len > 0); + zd->zd_alias = kmem_alloc(alias_len + 1, KM_SLEEP); + bcopy(alias, zd->zd_alias, alias_len); + zd->zd_alias[alias_len] = '\0'; + } else { + VERIFY(alias_len == 0); + } + + list_insert_head(&zone->zone_datasets, zd); +} + +/* + * Parses a list of ZFS datasets into a per-zone dictionary. Each dataset + * in the list is separated by a comma. If a dataset should be known by + * an alias from within the zone, that alias is specified after a pipe. + * The alias must be a valid dataset name with exactly one component. + * A trailing comma at the end of the list is allowed. + * + * For example: + * + * Without any aliases: "one,two/a/b/c,three" + * With an alias: "long/dataset/name|tank,another,athird" */ static int parse_zfs(zone_t *zone, caddr_t ubuf, size_t buflen) { + int ret = 0; char *kbuf; - char *dataset, *next; - zone_dataset_t *zd; - size_t len; + enum parse_zfs_state { + PZS_REST, + PZS_DATASET, + PZS_ALIAS + } pzs = PZS_REST; + const char *dataset = NULL, *alias = NULL; + size_t dataset_len = 0, alias_len = 0; if (ubuf == NULL || buflen == 0) return (0); @@ -5006,31 +5061,127 @@ parse_zfs(zone_t *zone, caddr_t ubuf, size_t buflen) return (EFAULT); } - dataset = next = kbuf; - for (;;) { - zd = kmem_alloc(sizeof (zone_dataset_t), KM_SLEEP); + for (size_t i = 0; i < buflen; i++) { + char c = kbuf[i]; - next = strchr(dataset, ','); + switch (pzs) { + case PZS_REST: + if (c == '\0') { + goto out; - if (next == NULL) - len = strlen(dataset); - else - len = next - dataset; + } else if (parse_zfs_valid_char(c)) { + /* + * This is the beginning of a valid dataset + * name. + */ + dataset = &kbuf[i]; + dataset_len = 1; + alias = NULL; + alias_len = 0; + pzs = PZS_DATASET; + + } else if (c != ',') { + /* + * A trailing comma is allowed. + */ + ret = EINVAL; + goto out; + } + break; - zd->zd_dataset = kmem_alloc(len + 1, KM_SLEEP); - bcopy(dataset, zd->zd_dataset, len); - zd->zd_dataset[len] = '\0'; + case PZS_DATASET: + if (c == '\0' || c == ',') { + /* + * End of dataset name, without an alias. + * Commit to dictionary. + */ + parse_zfs_insert(zone, dataset, dataset_len, + alias, alias_len); - list_insert_head(&zone->zone_datasets, zd); + pzs = PZS_REST; + if (c == '\0') { + goto out; + } + + } else if (c == '|') { + /* + * The pipe separates this dataset name from + * its alias name. + */ + pzs = PZS_ALIAS; - if (next == NULL) + } else if (parse_zfs_valid_char(c) || c == '/') { + /* + * The slash ('/') character is allowed within + * a dataset name, but not an alias name, + * so we mention it explicitly here. + */ + dataset_len++; + + } else { + ret = EINVAL; + goto out; + } + break; + + case PZS_ALIAS: + if (c == '\0' || c == ',') { + if (alias == NULL) { + /* + * If there was an alias separator, + * there must then be an alias string. + */ + ret = EINVAL; + goto out; + } + + /* + * End of dataset name with an alias. Commit + * to dictionary. + */ + parse_zfs_insert(zone, dataset, dataset_len, + alias, alias_len); + + pzs = PZS_REST; + if (c == '\0') { + goto out; + } + + } else if (parse_zfs_valid_char(c)) { + if (alias == NULL) { + alias = &kbuf[i]; + alias_len = 1; + } else { + alias_len++; + } + + } else { + ret = EINVAL; + goto out; + } break; - dataset = next + 1; + default: + panic("parse_zfs(): unexpected state"); + } + } + +out: + if (ret == 0) { + if ((pzs == PZS_ALIAS && alias != NULL) || + pzs == PZS_DATASET) { + parse_zfs_insert(zone, dataset, dataset_len, + alias, alias_len); + } else if (pzs != PZS_REST) { + /* + * The string ended unexpectedly. + */ + ret = EINVAL; + } } kmem_free(kbuf, buflen); - return (0); + return (ret); } /* @@ -7542,11 +7693,157 @@ zone_shutdown_global(void) } /* + * Gets the un-aliased system name for an aliased dataset within a zone. + * + * Returns 0 upon success and fills out the provided 'dataset' buffer. + */ +int +zone_dataset_unalias_inzone(const char *alias, char *dataset, size_t sz, + zone_t *zone) +{ + zone_dataset_t *zd; + size_t len, alen; + boolean_t found = B_FALSE; + char *suffix; + const char *asuffix; + + VERIFY(alias != NULL); + VERIFY(dataset != NULL); + + if (alias[0] == '\0') { + dataset[0] = '\0'; + return (0); + } + + /* + * Walk the list once, looking for datasets which match exactly, or + * specify a dataset underneath an exported dataset. If found, exit + * the loop after unsetting 'ret', so we can process it further. + */ + for (zd = list_head(&zone->zone_datasets); zd != NULL; + zd = list_next(&zone->zone_datasets, zd)) { + if (zd->zd_alias == NULL) + continue; + + alen = strlen(zd->zd_alias); + if (strlen(alias) >= alen && + bcmp(alias, zd->zd_alias, alen) == 0 && + (alias[alen] == '\0' || alias[alen] == '/' || + alias[alen] == '@' || alias[alen] == '#')) { + found = B_TRUE; + break; + } + } + + /* + * If we didn't find any zone_dataset_t that matched, assume the + * dataset's system name is the same as the name the zone used + * (ie, assume no aliasing is in effect). + */ + if (found == B_FALSE) { + len = strlen(alias); + if (len >= sz - 1) + return (ENOSPC); + bcopy(alias, dataset, len); + dataset[len] = '\0'; + return (0); + } + + /* If aliased: place dataset name, then suffix in buffer. */ + len = strlen(zd->zd_dataset); + asuffix = &alias[alen]; + if (strlen(asuffix) + len >= sz - 1) + return (ENOSPC); + bcopy(zd->zd_dataset, dataset, len); + suffix = &dataset[len]; + (void) strcpy(suffix, asuffix); + + return (0); +} + +int +zone_dataset_unalias(const char *alias, char *dataset, size_t sz) +{ + return (zone_dataset_unalias_inzone(alias, dataset, sz, curzone)); +} + +/* + * Gets the aliased name for a dataset within a zone. + * + * Returns 0 upon success and fills out the provided 'alias' buffer. + */ +int +zone_dataset_alias_inzone(const char *dataset, char *alias, size_t sz, + zone_t *zone) +{ + zone_dataset_t *zd; + size_t len, alen; + boolean_t found = B_FALSE; + const char *suffix; + char *asuffix; + + VERIFY(alias != NULL); + VERIFY(dataset != NULL); + + if (dataset[0] == '\0') { + alias[0] = '\0'; + return (0); + } + + /* + * Walk the list once, looking for datasets which match exactly, or + * specify a dataset underneath an exported dataset. If found, exit + * the loop after unsetting 'ret', so we can process it further. + */ + for (zd = list_head(&zone->zone_datasets); zd != NULL; + zd = list_next(&zone->zone_datasets, zd)) { + len = strlen(zd->zd_dataset); + if (strlen(dataset) >= len && + bcmp(dataset, zd->zd_dataset, len) == 0 && + (dataset[len] == '\0' || dataset[len] == '/' || + dataset[len] == '@' || dataset[len] == '#')) { + found = B_TRUE; + break; + } + } + + if (found == B_FALSE) + return (EINVAL); + + if (zd->zd_alias == NULL) { + /* Simple case: just copy the whole name. */ + alen = strlen(dataset); + if (alen >= sz - 1) + return (ENOSPC); + bcopy(dataset, alias, alen); + alias[alen] = '\0'; + } else { + /* If aliased: place alias, then suffix in buffer. */ + alen = strlen(zd->zd_alias); + suffix = &dataset[len]; + if (strlen(suffix) + alen >= sz - 1) + return (ENOSPC); + bcopy(zd->zd_alias, alias, alen); + asuffix = &alias[alen]; + (void) strcpy(asuffix, suffix); + } + + return (0); +} + +int +zone_dataset_alias(const char *dataset, char *alias, size_t sz) +{ + return (zone_dataset_alias_inzone(dataset, alias, sz, curzone)); +} + +/* * Returns true if the named dataset is visible in the specified zone. * The 'write' parameter is set to 1 if the dataset is also writable. */ int -zone_dataset_visible_inzone(zone_t *zone, const char *dataset, int *write) +zone_dataset_visible_inzone(zone_t *zone, const char *dataset, int *write, + boolean_t with_aliased_parents) { static int zfstype = -1; zone_dataset_t *zd; @@ -7564,7 +7861,6 @@ zone_dataset_visible_inzone(zone_t *zone, const char *dataset, int *write) */ for (zd = list_head(&zone->zone_datasets); zd != NULL; zd = list_next(&zone->zone_datasets, zd)) { - len = strlen(zd->zd_dataset); if (strlen(dataset) >= len && bcmp(dataset, zd->zd_dataset, len) == 0 && @@ -7586,6 +7882,14 @@ zone_dataset_visible_inzone(zone_t *zone, const char *dataset, int *write) for (zd = list_head(&zone->zone_datasets); zd != NULL; zd = list_next(&zone->zone_datasets, zd)) { + if (!with_aliased_parents && zd->zd_alias != NULL) { + /* + * Ignore parent datasets when the exported dataset + * has an alias. + */ + continue; + } + len = strlen(dataset); if (dataset[len - 1] == '/') len--; /* Ignore trailing slash */ @@ -7660,11 +7964,13 @@ zone_dataset_visible_inzone(zone_t *zone, const char *dataset, int *write) * The 'write' parameter is set to 1 if the dataset is also writable. */ int -zone_dataset_visible(const char *dataset, int *write) +zone_dataset_visible(const char *dataset, int *write, + boolean_t with_aliased_parents) { zone_t *zone = curproc->p_zone; - return (zone_dataset_visible_inzone(zone, dataset, write)); + return (zone_dataset_visible_inzone(zone, dataset, write, + with_aliased_parents)); } /* diff --git a/usr/src/uts/common/sys/nvpair.h b/usr/src/uts/common/sys/nvpair.h index cf3f761c8c..80a2a47400 100644 --- a/usr/src/uts/common/sys/nvpair.h +++ b/usr/src/uts/common/sys/nvpair.h @@ -169,6 +169,9 @@ int nvlist_xdup(nvlist_t *, nvlist_t **, nv_alloc_t *); nv_alloc_t *nvlist_lookup_nv_alloc(nvlist_t *); int nvlist_add_nvpair(nvlist_t *, nvpair_t *); +#if defined(_KERNEL) +int nvlist_add_named_nvpair(nvlist_t *, const char *, nvpair_t *); +#endif int nvlist_add_boolean(nvlist_t *, const char *); int nvlist_add_boolean_value(nvlist_t *, const char *, boolean_t); int nvlist_add_byte(nvlist_t *, const char *, uchar_t); diff --git a/usr/src/uts/common/sys/zone.h b/usr/src/uts/common/sys/zone.h index f6bfe6626d..11d85b2865 100644 --- a/usr/src/uts/common/sys/zone.h +++ b/usr/src/uts/common/sys/zone.h @@ -412,6 +412,7 @@ typedef struct zone_ref { */ typedef struct zone_dataset { char *zd_dataset; + char *zd_alias; list_node_t zd_linkage; } zone_dataset_t; @@ -1015,8 +1016,18 @@ extern int zone_ncpus_online_get(zone_t *); /* * Returns true if the named pool/dataset is visible in the current zone. */ -extern int zone_dataset_visible(const char *, int *); -extern int zone_dataset_visible_inzone(zone_t *, const char *, int *); +extern int zone_dataset_visible(const char *, int *, boolean_t); +extern int zone_dataset_visible_inzone(zone_t *, const char *, int *, + boolean_t); + +/* + * Used to convert between a zone's local alias name for a dataset and + * the system's name. + */ +extern int zone_dataset_alias(const char *, char *, size_t); +extern int zone_dataset_unalias(const char *, char *, size_t); +extern int zone_dataset_alias_inzone(const char *, char *, size_t, zone_t *); +extern int zone_dataset_unalias_inzone(const char *, char *, size_t, zone_t *); /* * zone version of kadmin() |
