X-Git-Url: https://git.camperquake.de/gitweb.cgi?a=blobdiff_plain;f=cmd%2Fzfs%2Fzfs_main.c;h=e20d57000a4433cc62ba84000124c0c2263ff8d4;hb=ad60af8e1bfe7ccdbc41f754023e06fa4e2699bc;hp=2aa3e2ef12ab6283a64d7da38b9e50186844c13a;hpb=3b8cfee8af1d966eea75389e2a2e53a5a8dca600;p=zfs.git diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index 2aa3e2e..e20d570 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2011 Nexenta Systems, Inc. All rights reserved. */ #include @@ -41,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -51,7 +53,13 @@ #include #include +#include +#include #include +#ifdef HAVE_IDMAP +#include +#include +#endif /* HAVE_IDMAP */ #include "zfs_iter.h" #include "zfs_util.h" @@ -61,7 +69,6 @@ libzfs_handle_t *g_zfs; static FILE *mnttab_file; static char history_str[HIS_MAX_RECORD_LEN]; -const char *pypath = "/usr/lib/zfs/pyzfs.py"; static int zfs_do_clone(int argc, char **argv); static int zfs_do_create(int argc, char **argv); @@ -82,8 +89,10 @@ static int zfs_do_send(int argc, char **argv); static int zfs_do_receive(int argc, char **argv); static int zfs_do_promote(int argc, char **argv); static int zfs_do_userspace(int argc, char **argv); -static int zfs_do_python(int argc, char **argv); +static int zfs_do_allow(int argc, char **argv); +static int zfs_do_unallow(int argc, char **argv); static int zfs_do_hold(int argc, char **argv); +static int zfs_do_holds(int argc, char **argv); static int zfs_do_release(int argc, char **argv); static int zfs_do_diff(int argc, char **argv); @@ -176,12 +185,12 @@ static zfs_command_t command_table[] = { { "send", zfs_do_send, HELP_SEND }, { "receive", zfs_do_receive, HELP_RECEIVE }, { NULL }, - { "allow", zfs_do_python, HELP_ALLOW }, + { "allow", zfs_do_allow, HELP_ALLOW }, { NULL }, - { "unallow", zfs_do_python, HELP_UNALLOW }, + { "unallow", zfs_do_unallow, HELP_UNALLOW }, { NULL }, { "hold", zfs_do_hold, HELP_HOLD }, - { "holds", zfs_do_python, HELP_HOLDS }, + { "holds", zfs_do_holds, HELP_HOLDS }, { "release", zfs_do_release, HELP_RELEASE }, { "diff", zfs_do_diff, HELP_DIFF }, }; @@ -220,7 +229,7 @@ get_usage(zfs_help_t idx) return (gettext("\tlist [-rH][-d max] " "[-o property[,...]] [-t type[,...]] [-s property] ...\n" "\t [-S property] ... " - "[filesystem|volume|snapshot] ...\n")); + "[filesystem|volume|snapshot|snap] ...\n")); case HELP_MOUNT: return (gettext("\tmount\n" "\tmount [-vO] [-o opts] <-a | filesystem>\n")); @@ -238,14 +247,14 @@ get_usage(zfs_help_t idx) case HELP_ROLLBACK: return (gettext("\trollback [-rRf] \n")); case HELP_SEND: - return (gettext("\tsend [-RDp] [-[iI] snapshot] \n")); + return (gettext("\tsend [-vRDp] [-[iI] snapshot] \n")); case HELP_SET: return (gettext("\tset " " ...\n")); case HELP_SHARE: return (gettext("\tshare <-a | filesystem>\n")); case HELP_SNAPSHOT: - return (gettext("\tsnapshot [-r] [-o property=value] ... " + return (gettext("\tsnapshot|snap [-r] [-o property=value] ... " "\n")); case HELP_UNMOUNT: return (gettext("\tunmount [-f] " @@ -318,7 +327,6 @@ safe_malloc(size_t size) return (data); } -#ifdef HAVE_ZPL static char * safe_strdup(char *str) { @@ -329,7 +337,6 @@ safe_strdup(char *str) return (dupstr); } -#endif /* HAVE_ZPL */ /* * Callback routine that will print out information for each of @@ -497,7 +504,6 @@ parse_depth(char *opt, int *flags) #define PROGRESS_DELAY 2 /* seconds */ -#ifdef HAVE_ZPL static char *pt_reverse = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"; static time_t pt_begin; static char *pt_header = NULL; @@ -549,7 +555,6 @@ finish_progress(char *done) free(pt_header); pt_header = NULL; } -#endif /* HAVE_ZPL */ /* * zfs clone [-p] [-o prop=value] ... @@ -567,7 +572,7 @@ zfs_do_clone(int argc, char **argv) zfs_handle_t *zhp = NULL; boolean_t parents = B_FALSE; nvlist_t *props; - int ret; + int ret = 0; int c; if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) @@ -631,7 +636,6 @@ zfs_do_clone(int argc, char **argv) ret = zfs_clone(zhp, argv[1], props); /* create the mountpoint if necessary */ -#ifdef HAVE_ZPL if (ret == 0) { zfs_handle_t *clone; @@ -643,7 +647,6 @@ zfs_do_clone(int argc, char **argv) zfs_close(clone); } } -#endif /* HAVE_ZPL */ zfs_close(zhp); nvlist_free(props); @@ -831,7 +834,6 @@ zfs_do_create(int argc, char **argv) * verbose error message to let the user know that their filesystem was * in fact created, even if we failed to mount or share it. */ -#ifdef HAVE_ZPL if (canmount == ZFS_CANMOUNT_ON) { if (zfs_mount(zhp, NULL, 0) != 0) { (void) fprintf(stderr, gettext("filesystem " @@ -843,7 +845,6 @@ zfs_do_create(int argc, char **argv) ret = 1; } } -#endif /* HAVE_ZPL */ error: if (zhp) @@ -1057,7 +1058,7 @@ zfs_do_destroy(int argc, char **argv) * named snapshot may not exist. Go straight to libzfs. */ if (cb.cb_recurse && (cp = strchr(argv[0], '@'))) { - int ret; + int ret = 0; *cp = '\0'; if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL) @@ -1282,9 +1283,9 @@ static int zfs_do_get(int argc, char **argv) { zprop_get_cbdata_t cb = { 0 }; - int i, c, flags = 0; + int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS; char *value, *fields; - int ret; + int ret = 0; int limit = 0; zprop_list_t fake_name = { 0 }; @@ -1521,7 +1522,7 @@ zfs_do_inherit(int argc, char **argv) zfs_prop_t prop; inherit_cbdata_t cb = { 0 }; char *propname; - int ret; + int ret = 0; int flags = 0; boolean_t received = B_FALSE; @@ -1727,7 +1728,7 @@ zfs_do_upgrade(int argc, char **argv) { boolean_t all = B_FALSE; boolean_t showversions = B_FALSE; - int ret; + int ret = 0; upgrade_cbdata_t cb = { 0 }; signed char c; int flags = ZFS_ITER_ARGS_CAN_BE_PATHS; @@ -1832,81 +1833,731 @@ zfs_do_upgrade(int argc, char **argv) return (ret); } +#define USTYPE_USR_BIT (0) +#define USTYPE_GRP_BIT (1) +#define USTYPE_PSX_BIT (2) +#define USTYPE_SMB_BIT (3) + +#define USTYPE_USR (1 << USTYPE_USR_BIT) +#define USTYPE_GRP (1 << USTYPE_GRP_BIT) + +#define USTYPE_PSX (1 << USTYPE_PSX_BIT) +#define USTYPE_SMB (1 << USTYPE_SMB_BIT) + +#define USTYPE_PSX_USR (USTYPE_PSX | USTYPE_USR) +#define USTYPE_SMB_USR (USTYPE_SMB | USTYPE_USR) +#define USTYPE_PSX_GRP (USTYPE_PSX | USTYPE_GRP) +#define USTYPE_SMB_GRP (USTYPE_SMB | USTYPE_GRP) +#define USTYPE_ALL (USTYPE_PSX_USR | USTYPE_SMB_USR \ + | USTYPE_PSX_GRP | USTYPE_SMB_GRP) + + +#define USPROP_USED_BIT (0) +#define USPROP_QUOTA_BIT (1) + +#define USPROP_USED (1 << USPROP_USED_BIT) +#define USPROP_QUOTA (1 << USPROP_QUOTA_BIT) + +typedef struct us_node { + nvlist_t *usn_nvl; + uu_avl_node_t usn_avlnode; + uu_list_node_t usn_listnode; +} us_node_t; + +typedef struct us_cbdata { + nvlist_t **cb_nvlp; + uu_avl_pool_t *cb_avl_pool; + uu_avl_t *cb_avl; + boolean_t cb_numname; + boolean_t cb_nicenum; + boolean_t cb_sid2posix; + zfs_userquota_prop_t cb_prop; + zfs_sort_column_t *cb_sortcol; + size_t cb_max_typelen; + size_t cb_max_namelen; + size_t cb_max_usedlen; + size_t cb_max_quotalen; +} us_cbdata_t; + +typedef struct { + zfs_sort_column_t *si_sortcol; + boolean_t si_num_name; + boolean_t si_parsable; +} us_sort_info_t; + +static int +us_compare(const void *larg, const void *rarg, void *unused) +{ + const us_node_t *l = larg; + const us_node_t *r = rarg; + int rc = 0; + us_sort_info_t *si = (us_sort_info_t *)unused; + zfs_sort_column_t *sortcol = si->si_sortcol; + boolean_t num_name = si->si_num_name; + nvlist_t *lnvl = l->usn_nvl; + nvlist_t *rnvl = r->usn_nvl; + + for (; sortcol != NULL; sortcol = sortcol->sc_next) { + char *lvstr = ""; + char *rvstr = ""; + uint32_t lv32 = 0; + uint32_t rv32 = 0; + uint64_t lv64 = 0; + uint64_t rv64 = 0; + zfs_prop_t prop = sortcol->sc_prop; + const char *propname = NULL; + boolean_t reverse = sortcol->sc_reverse; + + switch (prop) { + case ZFS_PROP_TYPE: + propname = "type"; + (void) nvlist_lookup_uint32(lnvl, propname, &lv32); + (void) nvlist_lookup_uint32(rnvl, propname, &rv32); + if (rv32 != lv32) + rc = (rv32 > lv32) ? 1 : -1; + break; + case ZFS_PROP_NAME: + propname = "name"; + if (num_name) { + (void) nvlist_lookup_uint32(lnvl, propname, + &lv32); + (void) nvlist_lookup_uint32(rnvl, propname, + &rv32); + if (rv32 != lv32) + rc = (rv32 > lv32) ? 1 : -1; + } else { + (void) nvlist_lookup_string(lnvl, propname, + &lvstr); + (void) nvlist_lookup_string(rnvl, propname, + &rvstr); + rc = strcmp(lvstr, rvstr); + } + break; + + case ZFS_PROP_USED: + case ZFS_PROP_QUOTA: + if (ZFS_PROP_USED == prop) + propname = "used"; + else + propname = "quota"; + (void) nvlist_lookup_uint64(lnvl, propname, &lv64); + (void) nvlist_lookup_uint64(rnvl, propname, &rv64); + if (rv64 != lv64) + rc = (rv64 > lv64) ? 1 : -1; + default: + break; + } + + if (rc) { + if (rc < 0) + return (reverse ? 1 : -1); + else + return (reverse ? -1 : 1); + } + } + + return (rc); +} + +static inline const char * +us_type2str(unsigned field_type) +{ + switch (field_type) { + case USTYPE_PSX_USR: + return ("POSIX User"); + case USTYPE_PSX_GRP: + return ("POSIX Group"); + case USTYPE_SMB_USR: + return ("SMB User"); + case USTYPE_SMB_GRP: + return ("SMB Group"); + default: + return ("Undefined"); + } +} + /* * zfs userspace */ static int userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space) { - zfs_userquota_prop_t *typep = arg; - zfs_userquota_prop_t p = *typep; + us_cbdata_t *cb = (us_cbdata_t *)arg; + zfs_userquota_prop_t prop = cb->cb_prop; char *name = NULL; - char *ug, *propname; + char *propname; char namebuf[32]; char sizebuf[32]; + us_node_t *node; + uu_avl_pool_t *avl_pool = cb->cb_avl_pool; + uu_avl_t *avl = cb->cb_avl; + uu_avl_index_t idx; + nvlist_t *props; + us_node_t *n; + zfs_sort_column_t *sortcol = cb->cb_sortcol; + unsigned type; + const char *typestr; + size_t namelen; + size_t typelen; + size_t sizelen; + us_sort_info_t sortinfo = { sortcol, cb->cb_numname }; if (domain == NULL || domain[0] == '\0') { - if (p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) { + /* POSIX */ + if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) { + type = USTYPE_PSX_GRP; struct group *g = getgrgid(rid); if (g) name = g->gr_name; } else { + type = USTYPE_PSX_USR; struct passwd *p = getpwuid(rid); if (p) name = p->pw_name; } + } else { +#ifdef HAVE_IDMAP + char sid[ZFS_MAXNAMELEN+32]; + uid_t id; + uint64_t classes; + int err = 0; + directory_error_t e; + + (void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid); + /* SMB */ + if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) { + type = USTYPE_SMB_GRP; + err = sid_to_id(sid, B_FALSE, &id); + } else { + type = USTYPE_SMB_USR; + err = sid_to_id(sid, B_TRUE, &id); + } + + if (err == 0) { + rid = id; + + e = directory_name_from_sid(NULL, sid, &name, &classes); + if (e != NULL) { + directory_error_free(e); + return (NULL); + } + + if (name == NULL) + name = sid; + } +#else + return (-1); +#endif /* HAVE_IDMAP */ } - if (p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) - ug = "group"; - else - ug = "user"; +/* + * if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) + * ug = "group"; + * else + * ug = "user"; + */ - if (p == ZFS_PROP_USERUSED || p == ZFS_PROP_GROUPUSED) + if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED) propname = "used"; else propname = "quota"; - if (name == NULL) { - (void) snprintf(namebuf, sizeof (namebuf), - "%llu", (longlong_t)rid); + (void) snprintf(namebuf, sizeof (namebuf), "%u", rid); + if (name == NULL) name = namebuf; + + if (cb->cb_nicenum) + zfs_nicenum(space, sizebuf, sizeof (sizebuf)); + else + (void) sprintf(sizebuf, "%llu", (u_longlong_t)space); + + node = safe_malloc(sizeof (us_node_t)); + uu_avl_node_init(node, &node->usn_avlnode, avl_pool); + + if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) { + free(node); + return (-1); + } + + if (nvlist_add_uint32(props, "type", type) != 0) + nomem(); + + if (cb->cb_numname) { + if (nvlist_add_uint32(props, "name", rid) != 0) + nomem(); + namelen = strlen(namebuf); + } else { + if (nvlist_add_string(props, "name", name) != 0) + nomem(); + namelen = strlen(name); + } + + typestr = us_type2str(type); + typelen = strlen(gettext(typestr)); + if (typelen > cb->cb_max_typelen) + cb->cb_max_typelen = typelen; + + if (namelen > cb->cb_max_namelen) + cb->cb_max_namelen = namelen; + + sizelen = strlen(sizebuf); + if (0 == strcmp(propname, "used")) { + if (sizelen > cb->cb_max_usedlen) + cb->cb_max_usedlen = sizelen; + } else { + if (sizelen > cb->cb_max_quotalen) + cb->cb_max_quotalen = sizelen; + } + + node->usn_nvl = props; + + n = uu_avl_find(avl, node, &sortinfo, &idx); + if (n == NULL) + uu_avl_insert(avl, node, idx); + else { + nvlist_free(props); + free(node); + node = n; + props = node->usn_nvl; } - zfs_nicenum(space, sizebuf, sizeof (sizebuf)); - (void) printf("%s %s %s%c%s %s\n", propname, ug, domain, - domain[0] ? '-' : ' ', name, sizebuf); + if (nvlist_add_uint64(props, propname, space) != 0) + nomem(); + + return (0); +} + +static inline boolean_t +usprop_check(zfs_userquota_prop_t p, unsigned types, unsigned props) +{ + unsigned type; + unsigned prop; + + switch (p) { + case ZFS_PROP_USERUSED: + type = USTYPE_USR; + prop = USPROP_USED; + break; + case ZFS_PROP_USERQUOTA: + type = USTYPE_USR; + prop = USPROP_QUOTA; + break; + case ZFS_PROP_GROUPUSED: + type = USTYPE_GRP; + prop = USPROP_USED; + break; + case ZFS_PROP_GROUPQUOTA: + type = USTYPE_GRP; + prop = USPROP_QUOTA; + break; + default: /* ALL */ + return (B_TRUE); + }; + + return (type & types && prop & props); +} + +#define USFIELD_TYPE (1 << 0) +#define USFIELD_NAME (1 << 1) +#define USFIELD_USED (1 << 2) +#define USFIELD_QUOTA (1 << 3) +#define USFIELD_ALL (USFIELD_TYPE | USFIELD_NAME | USFIELD_USED | USFIELD_QUOTA) + +static int +parsefields(unsigned *fieldsp, char **names, unsigned *bits, size_t len) +{ + char *field = optarg; + char *delim; + + do { + int i; + boolean_t found = B_FALSE; + delim = strchr(field, ','); + if (delim != NULL) + *delim = '\0'; + + for (i = 0; i < len; i++) + if (0 == strcmp(field, names[i])) { + found = B_TRUE; + *fieldsp |= bits[i]; + break; + } + + if (!found) { + (void) fprintf(stderr, gettext("invalid type '%s'" + "for -t option\n"), field); + return (-1); + } + + field = delim + 1; + } while (delim); return (0); } + +static char *type_names[] = { "posixuser", "smbuser", "posixgroup", "smbgroup", + "all" }; +static unsigned type_bits[] = { + USTYPE_PSX_USR, + USTYPE_SMB_USR, + USTYPE_PSX_GRP, + USTYPE_SMB_GRP, + USTYPE_ALL +}; + +static char *us_field_names[] = { "type", "name", "used", "quota" }; +static unsigned us_field_bits[] = { + USFIELD_TYPE, + USFIELD_NAME, + USFIELD_USED, + USFIELD_QUOTA +}; + +static void +print_us_node(boolean_t scripted, boolean_t parseable, unsigned fields, + size_t type_width, size_t name_width, size_t used_width, + size_t quota_width, us_node_t *node) +{ + nvlist_t *nvl = node->usn_nvl; + nvpair_t *nvp = NULL; + char valstr[ZFS_MAXNAMELEN]; + boolean_t first = B_TRUE; + boolean_t quota_found = B_FALSE; + + if (fields & USFIELD_QUOTA && !nvlist_exists(nvl, "quota")) + if (nvlist_add_string(nvl, "quota", "none") != 0) + nomem(); + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + char *pname = nvpair_name(nvp); + data_type_t type = nvpair_type(nvp); + uint32_t val32 = 0; + uint64_t val64 = 0; + char *strval = NULL; + unsigned field = 0; + unsigned width = 0; + int i; + for (i = 0; i < 4; i++) { + if (0 == strcmp(pname, us_field_names[i])) { + field = us_field_bits[i]; + break; + } + } + + if (!(field & fields)) + continue; + + switch (type) { + case DATA_TYPE_UINT32: + (void) nvpair_value_uint32(nvp, &val32); + break; + case DATA_TYPE_UINT64: + (void) nvpair_value_uint64(nvp, &val64); + break; + case DATA_TYPE_STRING: + (void) nvpair_value_string(nvp, &strval); + break; + default: + (void) fprintf(stderr, "Invalid data type\n"); + } + + if (!first) { + if (scripted) + (void) printf("\t"); + else + (void) printf(" "); + } + + switch (field) { + case USFIELD_TYPE: + strval = (char *)us_type2str(val32); + width = type_width; + break; + case USFIELD_NAME: + if (type == DATA_TYPE_UINT64) { + (void) sprintf(valstr, "%llu", + (u_longlong_t) val64); + strval = valstr; + } + width = name_width; + break; + case USFIELD_USED: + case USFIELD_QUOTA: + if (type == DATA_TYPE_UINT64) { + (void) nvpair_value_uint64(nvp, &val64); + if (parseable) + (void) sprintf(valstr, "%llu", + (u_longlong_t) val64); + else + zfs_nicenum(val64, valstr, + sizeof (valstr)); + strval = valstr; + } + + if (field == USFIELD_USED) + width = used_width; + else { + quota_found = B_FALSE; + width = quota_width; + } + + break; + } + + if (field == USFIELD_QUOTA && !quota_found) + (void) printf("%*s", width, strval); + else { + if (type == DATA_TYPE_STRING) + (void) printf("%-*s", width, strval); + else + (void) printf("%*s", width, strval); + } + + first = B_FALSE; + + } + + (void) printf("\n"); +} + +static void +print_us(boolean_t scripted, boolean_t parsable, unsigned fields, + unsigned type_width, unsigned name_width, unsigned used_width, + unsigned quota_width, boolean_t rmnode, uu_avl_t *avl) +{ + static char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA" }; + us_node_t *node; + const char *col; + int i; + int width[4] = { type_width, name_width, used_width, quota_width }; + + if (!scripted) { + boolean_t first = B_TRUE; + for (i = 0; i < 4; i++) { + unsigned field = us_field_bits[i]; + if (!(field & fields)) + continue; + + col = gettext(us_field_hdr[i]); + if (field == USFIELD_TYPE || field == USFIELD_NAME) + (void) printf(first?"%-*s":" %-*s", width[i], + col); + else + (void) printf(first?"%*s":" %*s", width[i], + col); + first = B_FALSE; + } + (void) printf("\n"); + } + + for (node = uu_avl_first(avl); node != NULL; + node = uu_avl_next(avl, node)) { + print_us_node(scripted, parsable, fields, type_width, + name_width, used_width, used_width, node); + if (rmnode) + nvlist_free(node->usn_nvl); + } +} + static int zfs_do_userspace(int argc, char **argv) { zfs_handle_t *zhp; zfs_userquota_prop_t p; - int error; + uu_avl_pool_t *avl_pool; + uu_avl_t *avl_tree; + uu_avl_walk_t *walk; - /* - * Try the python version. If the execv fails, we'll continue - * and do a simplistic implementation. - */ - (void) execv(pypath, argv-1); + char *cmd; + boolean_t scripted = B_FALSE; + boolean_t prtnum = B_FALSE; + boolean_t parseable = B_FALSE; + boolean_t sid2posix = B_FALSE; + int error = 0; + int c; + zfs_sort_column_t *default_sortcol = NULL; + zfs_sort_column_t *sortcol = NULL; + unsigned types = USTYPE_PSX_USR | USTYPE_SMB_USR; + unsigned fields = 0; + unsigned props = USPROP_USED | USPROP_QUOTA; + us_cbdata_t cb; + us_node_t *node; + boolean_t resort_avl = B_FALSE; + + if (argc < 2) + usage(B_FALSE); + + cmd = argv[0]; + if (0 == strcmp(cmd, "groupspace")) + /* toggle default group types */ + types = USTYPE_PSX_GRP | USTYPE_SMB_GRP; + + /* check options */ + while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) { + switch (c) { + case 'n': + prtnum = B_TRUE; + break; + case 'H': + scripted = B_TRUE; + break; + case 'p': + parseable = B_TRUE; + break; + case 'o': + if (parsefields(&fields, us_field_names, us_field_bits, + 4) != 0) + return (1); + break; + case 's': + if (zfs_add_sort_column(&sortcol, optarg, + B_FALSE) != 0) { + (void) fprintf(stderr, + gettext("invalid property '%s'\n"), optarg); + usage(B_FALSE); + } + break; + case 'S': + if (zfs_add_sort_column(&sortcol, optarg, + B_TRUE) != 0) { + (void) fprintf(stderr, + gettext("invalid property '%s'\n"), optarg); + usage(B_FALSE); + } + break; + case 't': + if (parsefields(&types, type_names, type_bits, 5)) + return (1); + break; + case 'i': + sid2posix = B_TRUE; + break; + case ':': + (void) fprintf(stderr, gettext("missing argument for " + "'%c' option\n"), optopt); + usage(B_FALSE); + break; + case '?': + (void) fprintf(stderr, gettext("invalid option '%c'\n"), + optopt); + usage(B_FALSE); + } + } + + argc -= optind; + argv += optind; + + /* ok, now we have sorted by default colums (type,name) avl tree */ + if (sortcol) { + zfs_sort_column_t *sc; + for (sc = sortcol; sc; sc = sc->sc_next) { + if (sc->sc_prop == ZFS_PROP_QUOTA) { + resort_avl = B_TRUE; + break; + } + } + } - (void) printf("internal error: %s not found\n" - "falling back on built-in implementation, " - "some features will not work\n", pypath); + if (!fields) + fields = USFIELD_ALL; if ((zhp = zfs_open(g_zfs, argv[argc-1], ZFS_TYPE_DATASET)) == NULL) return (1); - (void) printf("PROP TYPE NAME VALUE\n"); + if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t), + offsetof(us_node_t, usn_avlnode), + us_compare, UU_DEFAULT)) == NULL) + nomem(); + if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) + nomem(); + + if (sortcol && !resort_avl) + cb.cb_sortcol = sortcol; + else { + (void) zfs_add_sort_column(&default_sortcol, "type", B_FALSE); + (void) zfs_add_sort_column(&default_sortcol, "name", B_FALSE); + cb.cb_sortcol = default_sortcol; + } + cb.cb_numname = prtnum; + cb.cb_nicenum = !parseable; + cb.cb_avl_pool = avl_pool; + cb.cb_avl = avl_tree; + cb.cb_sid2posix = sid2posix; + cb.cb_max_typelen = strlen(gettext("TYPE")); + cb.cb_max_namelen = strlen(gettext("NAME")); + cb.cb_max_usedlen = strlen(gettext("USED")); + cb.cb_max_quotalen = strlen(gettext("QUOTA")); for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) { - error = zfs_userspace(zhp, p, userspace_cb, &p); + if (!usprop_check(p, types, props)) + continue; + + cb.cb_prop = p; + error = zfs_userspace(zhp, p, userspace_cb, &cb); if (error) break; } + + + if (resort_avl) { + us_node_t *node; + us_node_t *rmnode; + uu_list_pool_t *listpool; + uu_list_t *list; + uu_avl_index_t idx = 0; + uu_list_index_t idx2 = 0; + listpool = uu_list_pool_create("tmplist", sizeof (us_node_t), + offsetof(us_node_t, usn_listnode), NULL, + UU_DEFAULT); + list = uu_list_create(listpool, NULL, UU_DEFAULT); + + node = uu_avl_first(avl_tree); + uu_list_node_init(node, &node->usn_listnode, listpool); + while (node != NULL) { + rmnode = node; + node = uu_avl_next(avl_tree, node); + uu_avl_remove(avl_tree, rmnode); + if (uu_list_find(list, rmnode, NULL, &idx2) == NULL) { + uu_list_insert(list, rmnode, idx2); + } + } + + for (node = uu_list_first(list); node != NULL; + node = uu_list_next(list, node)) { + us_sort_info_t sortinfo = { sortcol, cb.cb_numname }; + if (uu_avl_find(avl_tree, node, &sortinfo, &idx) == + NULL) + uu_avl_insert(avl_tree, node, idx); + } + + uu_list_destroy(list); + } + + /* print & free node`s nvlist memory */ + print_us(scripted, parseable, fields, cb.cb_max_typelen, + cb.cb_max_namelen, cb.cb_max_usedlen, + cb.cb_max_quotalen, B_TRUE, cb.cb_avl); + + if (sortcol) + zfs_free_sort_columns(sortcol); + zfs_free_sort_columns(default_sortcol); + + /* + * Finally, clean up the AVL tree. + */ + if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) + nomem(); + + while ((node = uu_avl_walk_next(walk)) != NULL) { + uu_avl_remove(cb.cb_avl, node); + free(node); + } + + uu_avl_walk_end(walk); + uu_avl_destroy(avl_tree); + uu_avl_pool_destroy(avl_pool); + return (error); } @@ -2074,7 +2725,7 @@ zfs_do_list(int argc, char **argv) list_cbdata_t cb = { 0 }; char *value; int limit = 0; - int ret; + int ret = 0; zfs_sort_column_t *sortcol = NULL; int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS; @@ -2115,7 +2766,7 @@ zfs_do_list(int argc, char **argv) flags &= ~ZFS_ITER_PROP_LISTSNAPS; while (*optarg != '\0') { static char *type_subopts[] = { "filesystem", - "volume", "snapshot", "all", NULL }; + "volume", "snapshot", "snap", "all", NULL }; switch (getsubopt(&optarg, type_subopts, &value)) { @@ -2126,9 +2777,10 @@ zfs_do_list(int argc, char **argv) types |= ZFS_TYPE_VOLUME; break; case 2: + case 3: types |= ZFS_TYPE_SNAPSHOT; break; - case 3: + case 4: types = ZFS_TYPE_DATASET; break; @@ -2183,7 +2835,7 @@ zfs_do_list(int argc, char **argv) zfs_free_sort_columns(sortcol); if (ret == 0 && cb.cb_first && !cb.cb_scripted) - (void) printf(gettext("no datasets available\n")); + (void) fprintf(stderr, gettext("no datasets available\n")); return (ret); } @@ -2203,7 +2855,7 @@ zfs_do_rename(int argc, char **argv) { zfs_handle_t *zhp; int c; - int ret; + int ret = 0; boolean_t recurse = B_FALSE; boolean_t parents = B_FALSE; @@ -2282,7 +2934,7 @@ static int zfs_do_promote(int argc, char **argv) { zfs_handle_t *zhp; - int ret; + int ret = 0; /* check options */ if (argc > 1 && argv[1][0] == '-') { @@ -2340,7 +2992,6 @@ typedef struct rollback_cbdata { * 'cb_dependent' is set, then this is a dependent and we should report it * without checking the transaction group. */ -#ifdef HAVE_ZPL static int rollback_check(zfs_handle_t *zhp, void *data) { @@ -2400,13 +3051,11 @@ rollback_check(zfs_handle_t *zhp, void *data) zfs_close(zhp); return (0); } -#endif /* HAVE_ZPL */ static int zfs_do_rollback(int argc, char **argv) { -#ifdef HAVE_ZPL - int ret; + int ret = 0; int c; boolean_t force = B_FALSE; rollback_cbdata_t cb = { 0 }; @@ -2487,9 +3136,6 @@ out: return (0); else return (1); -#else - return ENOSYS; -#endif /*HAVE_ZPL*/ } /* @@ -2527,7 +3173,7 @@ static int zfs_do_set(int argc, char **argv) { set_cbdata_t cb; - int ret; + int ret = 0; /* check for options */ if (argc > 1 && argv[1][0] == '-') { @@ -2581,7 +3227,7 @@ static int zfs_do_snapshot(int argc, char **argv) { boolean_t recursive = B_FALSE; - int ret; + int ret = 0; signed char c; nvlist_t *props; @@ -2844,14 +3490,1370 @@ zfs_do_receive(int argc, char **argv) return (err != 0); } -static int -zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding) -{ - int errors = 0; - int i; - const char *tag; - boolean_t recursive = B_FALSE; - boolean_t temphold = B_FALSE; +/* + * allow/unallow stuff + */ +/* copied from zfs/sys/dsl_deleg.h */ +#define ZFS_DELEG_PERM_CREATE "create" +#define ZFS_DELEG_PERM_DESTROY "destroy" +#define ZFS_DELEG_PERM_SNAPSHOT "snapshot" +#define ZFS_DELEG_PERM_ROLLBACK "rollback" +#define ZFS_DELEG_PERM_CLONE "clone" +#define ZFS_DELEG_PERM_PROMOTE "promote" +#define ZFS_DELEG_PERM_RENAME "rename" +#define ZFS_DELEG_PERM_MOUNT "mount" +#define ZFS_DELEG_PERM_SHARE "share" +#define ZFS_DELEG_PERM_SEND "send" +#define ZFS_DELEG_PERM_RECEIVE "receive" +#define ZFS_DELEG_PERM_ALLOW "allow" +#define ZFS_DELEG_PERM_USERPROP "userprop" +#define ZFS_DELEG_PERM_VSCAN "vscan" /* ??? */ +#define ZFS_DELEG_PERM_USERQUOTA "userquota" +#define ZFS_DELEG_PERM_GROUPQUOTA "groupquota" +#define ZFS_DELEG_PERM_USERUSED "userused" +#define ZFS_DELEG_PERM_GROUPUSED "groupused" +#define ZFS_DELEG_PERM_HOLD "hold" +#define ZFS_DELEG_PERM_RELEASE "release" +#define ZFS_DELEG_PERM_DIFF "diff" + +#define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE + +static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = { + { ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW }, + { ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE }, + { ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE }, + { ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY }, + { ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF}, + { ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD }, + { ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT }, + { ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE }, + { ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE }, + { ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE }, + { ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME }, + { ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK }, + { ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND }, + { ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE }, + { ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT }, + + { ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA }, + { ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED }, + { ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP }, + { ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA }, + { ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED }, + { NULL, ZFS_DELEG_NOTE_NONE } +}; + +/* permission structure */ +typedef struct deleg_perm { + zfs_deleg_who_type_t dp_who_type; + const char *dp_name; + boolean_t dp_local; + boolean_t dp_descend; +} deleg_perm_t; + +/* */ +typedef struct deleg_perm_node { + deleg_perm_t dpn_perm; + + uu_avl_node_t dpn_avl_node; +} deleg_perm_node_t; + +typedef struct fs_perm fs_perm_t; + +/* permissions set */ +typedef struct who_perm { + zfs_deleg_who_type_t who_type; + const char *who_name; /* id */ + char who_ug_name[256]; /* user/group name */ + fs_perm_t *who_fsperm; /* uplink */ + + uu_avl_t *who_deleg_perm_avl; /* permissions */ +} who_perm_t; + +/* */ +typedef struct who_perm_node { + who_perm_t who_perm; + uu_avl_node_t who_avl_node; +} who_perm_node_t; + +typedef struct fs_perm_set fs_perm_set_t; +/* fs permissions */ +struct fs_perm { + const char *fsp_name; + + uu_avl_t *fsp_sc_avl; /* sets,create */ + uu_avl_t *fsp_uge_avl; /* user,group,everyone */ + + fs_perm_set_t *fsp_set; /* uplink */ +}; + +/* */ +typedef struct fs_perm_node { + fs_perm_t fspn_fsperm; + uu_avl_t *fspn_avl; + + uu_list_node_t fspn_list_node; +} fs_perm_node_t; + +/* top level structure */ +struct fs_perm_set { + uu_list_pool_t *fsps_list_pool; + uu_list_t *fsps_list; /* list of fs_perms */ + + uu_avl_pool_t *fsps_named_set_avl_pool; + uu_avl_pool_t *fsps_who_perm_avl_pool; + uu_avl_pool_t *fsps_deleg_perm_avl_pool; +}; + +static inline const char * +deleg_perm_type(zfs_deleg_note_t note) +{ + /* subcommands */ + switch (note) { + /* SUBCOMMANDS */ + /* OTHER */ + case ZFS_DELEG_NOTE_GROUPQUOTA: + case ZFS_DELEG_NOTE_GROUPUSED: + case ZFS_DELEG_NOTE_USERPROP: + case ZFS_DELEG_NOTE_USERQUOTA: + case ZFS_DELEG_NOTE_USERUSED: + /* other */ + return (gettext("other")); + default: + return (gettext("subcommand")); + } +} + +static int inline +who_type2weight(zfs_deleg_who_type_t who_type) +{ + int res; + switch (who_type) { + case ZFS_DELEG_NAMED_SET_SETS: + case ZFS_DELEG_NAMED_SET: + res = 0; + break; + case ZFS_DELEG_CREATE_SETS: + case ZFS_DELEG_CREATE: + res = 1; + break; + case ZFS_DELEG_USER_SETS: + case ZFS_DELEG_USER: + res = 2; + break; + case ZFS_DELEG_GROUP_SETS: + case ZFS_DELEG_GROUP: + res = 3; + break; + case ZFS_DELEG_EVERYONE_SETS: + case ZFS_DELEG_EVERYONE: + res = 4; + break; + default: + res = -1; + } + + return (res); +} + +/* ARGSUSED */ +static int +who_perm_compare(const void *larg, const void *rarg, void *unused) +{ + const who_perm_node_t *l = larg; + const who_perm_node_t *r = rarg; + zfs_deleg_who_type_t ltype = l->who_perm.who_type; + zfs_deleg_who_type_t rtype = r->who_perm.who_type; + int lweight = who_type2weight(ltype); + int rweight = who_type2weight(rtype); + int res = lweight - rweight; + if (res == 0) + res = strncmp(l->who_perm.who_name, r->who_perm.who_name, + ZFS_MAX_DELEG_NAME-1); + + if (res == 0) + return (0); + if (res > 0) + return (1); + else + return (-1); +} + +/* ARGSUSED */ +static int +deleg_perm_compare(const void *larg, const void *rarg, void *unused) +{ + const deleg_perm_node_t *l = larg; + const deleg_perm_node_t *r = rarg; + int res = strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name, + ZFS_MAX_DELEG_NAME-1); + + if (res == 0) + return (0); + + if (res > 0) + return (1); + else + return (-1); +} + +static inline void +fs_perm_set_init(fs_perm_set_t *fspset) +{ + bzero(fspset, sizeof (fs_perm_set_t)); + + if ((fspset->fsps_list_pool = uu_list_pool_create("fsps_list_pool", + sizeof (fs_perm_node_t), offsetof(fs_perm_node_t, fspn_list_node), + NULL, UU_DEFAULT)) == NULL) + nomem(); + if ((fspset->fsps_list = uu_list_create(fspset->fsps_list_pool, NULL, + UU_DEFAULT)) == NULL) + nomem(); + + if ((fspset->fsps_named_set_avl_pool = uu_avl_pool_create( + "named_set_avl_pool", sizeof (who_perm_node_t), offsetof( + who_perm_node_t, who_avl_node), who_perm_compare, + UU_DEFAULT)) == NULL) + nomem(); + + if ((fspset->fsps_who_perm_avl_pool = uu_avl_pool_create( + "who_perm_avl_pool", sizeof (who_perm_node_t), offsetof( + who_perm_node_t, who_avl_node), who_perm_compare, + UU_DEFAULT)) == NULL) + nomem(); + + if ((fspset->fsps_deleg_perm_avl_pool = uu_avl_pool_create( + "deleg_perm_avl_pool", sizeof (deleg_perm_node_t), offsetof( + deleg_perm_node_t, dpn_avl_node), deleg_perm_compare, UU_DEFAULT)) + == NULL) + nomem(); +} + +static inline void fs_perm_fini(fs_perm_t *); +static inline void who_perm_fini(who_perm_t *); + +static inline void +fs_perm_set_fini(fs_perm_set_t *fspset) +{ + fs_perm_node_t *node = uu_list_first(fspset->fsps_list); + + while (node != NULL) { + fs_perm_node_t *next_node = + uu_list_next(fspset->fsps_list, node); + fs_perm_t *fsperm = &node->fspn_fsperm; + fs_perm_fini(fsperm); + uu_list_remove(fspset->fsps_list, node); + free(node); + node = next_node; + } + + uu_avl_pool_destroy(fspset->fsps_named_set_avl_pool); + uu_avl_pool_destroy(fspset->fsps_who_perm_avl_pool); + uu_avl_pool_destroy(fspset->fsps_deleg_perm_avl_pool); +} + +static inline void +deleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type, + const char *name) +{ + deleg_perm->dp_who_type = type; + deleg_perm->dp_name = name; +} + +static inline void +who_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm, + zfs_deleg_who_type_t type, const char *name) +{ + uu_avl_pool_t *pool; + pool = fsperm->fsp_set->fsps_deleg_perm_avl_pool; + + bzero(who_perm, sizeof (who_perm_t)); + + if ((who_perm->who_deleg_perm_avl = uu_avl_create(pool, NULL, + UU_DEFAULT)) == NULL) + nomem(); + + who_perm->who_type = type; + who_perm->who_name = name; + who_perm->who_fsperm = fsperm; +} + +static inline void +who_perm_fini(who_perm_t *who_perm) +{ + deleg_perm_node_t *node = uu_avl_first(who_perm->who_deleg_perm_avl); + + while (node != NULL) { + deleg_perm_node_t *next_node = + uu_avl_next(who_perm->who_deleg_perm_avl, node); + + uu_avl_remove(who_perm->who_deleg_perm_avl, node); + free(node); + node = next_node; + } + + uu_avl_destroy(who_perm->who_deleg_perm_avl); +} + +static inline void +fs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname) +{ + uu_avl_pool_t *nset_pool = fspset->fsps_named_set_avl_pool; + uu_avl_pool_t *who_pool = fspset->fsps_who_perm_avl_pool; + + bzero(fsperm, sizeof (fs_perm_t)); + + if ((fsperm->fsp_sc_avl = uu_avl_create(nset_pool, NULL, UU_DEFAULT)) + == NULL) + nomem(); + + if ((fsperm->fsp_uge_avl = uu_avl_create(who_pool, NULL, UU_DEFAULT)) + == NULL) + nomem(); + + fsperm->fsp_set = fspset; + fsperm->fsp_name = fsname; +} + +static inline void +fs_perm_fini(fs_perm_t *fsperm) +{ + who_perm_node_t *node = uu_avl_first(fsperm->fsp_sc_avl); + while (node != NULL) { + who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_sc_avl, + node); + who_perm_t *who_perm = &node->who_perm; + who_perm_fini(who_perm); + uu_avl_remove(fsperm->fsp_sc_avl, node); + free(node); + node = next_node; + } + + node = uu_avl_first(fsperm->fsp_uge_avl); + while (node != NULL) { + who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_uge_avl, + node); + who_perm_t *who_perm = &node->who_perm; + who_perm_fini(who_perm); + uu_avl_remove(fsperm->fsp_uge_avl, node); + free(node); + node = next_node; + } + + uu_avl_destroy(fsperm->fsp_sc_avl); + uu_avl_destroy(fsperm->fsp_uge_avl); +} + +static void inline +set_deleg_perm_node(uu_avl_t *avl, deleg_perm_node_t *node, + zfs_deleg_who_type_t who_type, const char *name, char locality) +{ + uu_avl_index_t idx = 0; + + deleg_perm_node_t *found_node = NULL; + deleg_perm_t *deleg_perm = &node->dpn_perm; + + deleg_perm_init(deleg_perm, who_type, name); + + if ((found_node = uu_avl_find(avl, node, NULL, &idx)) + == NULL) + uu_avl_insert(avl, node, idx); + else { + node = found_node; + deleg_perm = &node->dpn_perm; + } + + + switch (locality) { + case ZFS_DELEG_LOCAL: + deleg_perm->dp_local = B_TRUE; + break; + case ZFS_DELEG_DESCENDENT: + deleg_perm->dp_descend = B_TRUE; + break; + case ZFS_DELEG_NA: + break; + default: + assert(B_FALSE); /* invalid locality */ + } +} + +static inline int +parse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality) +{ + nvpair_t *nvp = NULL; + fs_perm_set_t *fspset = who_perm->who_fsperm->fsp_set; + uu_avl_t *avl = who_perm->who_deleg_perm_avl; + zfs_deleg_who_type_t who_type = who_perm->who_type; + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + const char *name = nvpair_name(nvp); + data_type_t type = nvpair_type(nvp); + uu_avl_pool_t *avl_pool = fspset->fsps_deleg_perm_avl_pool; + deleg_perm_node_t *node = + safe_malloc(sizeof (deleg_perm_node_t)); + + VERIFY(type == DATA_TYPE_BOOLEAN); + + uu_avl_node_init(node, &node->dpn_avl_node, avl_pool); + set_deleg_perm_node(avl, node, who_type, name, locality); + } + + return (0); +} + +static inline int +parse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl) +{ + nvpair_t *nvp = NULL; + fs_perm_set_t *fspset = fsperm->fsp_set; + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + nvlist_t *nvl2 = NULL; + const char *name = nvpair_name(nvp); + uu_avl_t *avl = NULL; + uu_avl_pool_t *avl_pool = NULL; + zfs_deleg_who_type_t perm_type = name[0]; + char perm_locality = name[1]; + const char *perm_name = name + 3; + boolean_t is_set = B_TRUE; + who_perm_t *who_perm = NULL; + + assert('$' == name[2]); + + if (nvpair_value_nvlist(nvp, &nvl2) != 0) + return (-1); + + switch (perm_type) { + case ZFS_DELEG_CREATE: + case ZFS_DELEG_CREATE_SETS: + case ZFS_DELEG_NAMED_SET: + case ZFS_DELEG_NAMED_SET_SETS: + avl_pool = fspset->fsps_named_set_avl_pool; + avl = fsperm->fsp_sc_avl; + break; + case ZFS_DELEG_USER: + case ZFS_DELEG_USER_SETS: + case ZFS_DELEG_GROUP: + case ZFS_DELEG_GROUP_SETS: + case ZFS_DELEG_EVERYONE: + case ZFS_DELEG_EVERYONE_SETS: + avl_pool = fspset->fsps_who_perm_avl_pool; + avl = fsperm->fsp_uge_avl; + break; + default: + break; + } + + if (is_set) { + who_perm_node_t *found_node = NULL; + who_perm_node_t *node = safe_malloc( + sizeof (who_perm_node_t)); + who_perm = &node->who_perm; + uu_avl_index_t idx = 0; + + uu_avl_node_init(node, &node->who_avl_node, avl_pool); + who_perm_init(who_perm, fsperm, perm_type, perm_name); + + if ((found_node = uu_avl_find(avl, node, NULL, &idx)) + == NULL) { + if (avl == fsperm->fsp_uge_avl) { + uid_t rid = 0; + struct passwd *p = NULL; + struct group *g = NULL; + const char *nice_name = NULL; + + switch (perm_type) { + case ZFS_DELEG_USER_SETS: + case ZFS_DELEG_USER: + rid = atoi(perm_name); + p = getpwuid(rid); + if (p) + nice_name = p->pw_name; + break; + case ZFS_DELEG_GROUP_SETS: + case ZFS_DELEG_GROUP: + rid = atoi(perm_name); + g = getgrgid(rid); + if (g) + nice_name = g->gr_name; + break; + default: + break; + } + + if (nice_name != NULL) + (void) strlcpy( + node->who_perm.who_ug_name, + nice_name, 256); + } + + uu_avl_insert(avl, node, idx); + } else { + node = found_node; + who_perm = &node->who_perm; + } + } + + (void) parse_who_perm(who_perm, nvl2, perm_locality); + } + + return (0); +} + +static inline int +parse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl) +{ + nvpair_t *nvp = NULL; + uu_avl_index_t idx = 0; + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + nvlist_t *nvl2 = NULL; + const char *fsname = nvpair_name(nvp); + data_type_t type = nvpair_type(nvp); + fs_perm_t *fsperm = NULL; + fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t)); + if (node == NULL) + nomem(); + + fsperm = &node->fspn_fsperm; + + VERIFY(DATA_TYPE_NVLIST == type); + + uu_list_node_init(node, &node->fspn_list_node, + fspset->fsps_list_pool); + + idx = uu_list_numnodes(fspset->fsps_list); + fs_perm_init(fsperm, fspset, fsname); + + if (nvpair_value_nvlist(nvp, &nvl2) != 0) + return (-1); + + (void) parse_fs_perm(fsperm, nvl2); + + uu_list_insert(fspset->fsps_list, node, idx); + } + + return (0); +} + +static inline const char * +deleg_perm_comment(zfs_deleg_note_t note) +{ + const char *str = ""; + + /* subcommands */ + switch (note) { + /* SUBCOMMANDS */ + case ZFS_DELEG_NOTE_ALLOW: + str = gettext("Must also have the permission that is being" + "\n\t\t\t\tallowed"); + break; + case ZFS_DELEG_NOTE_CLONE: + str = gettext("Must also have the 'create' ability and 'mount'" + "\n\t\t\t\tability in the origin file system"); + break; + case ZFS_DELEG_NOTE_CREATE: + str = gettext("Must also have the 'mount' ability"); + break; + case ZFS_DELEG_NOTE_DESTROY: + str = gettext("Must also have the 'mount' ability"); + break; + case ZFS_DELEG_NOTE_DIFF: + str = gettext("Allows lookup of paths within a dataset;" + "\n\t\t\t\tgiven an object number. Ordinary users need this" + "\n\t\t\t\tin order to use zfs diff"); + break; + case ZFS_DELEG_NOTE_HOLD: + str = gettext("Allows adding a user hold to a snapshot"); + break; + case ZFS_DELEG_NOTE_MOUNT: + str = gettext("Allows mount/umount of ZFS datasets"); + break; + case ZFS_DELEG_NOTE_PROMOTE: + str = gettext("Must also have the 'mount'\n\t\t\t\tand" + " 'promote' ability in the origin file system"); + break; + case ZFS_DELEG_NOTE_RECEIVE: + str = gettext("Must also have the 'mount' and 'create'" + " ability"); + break; + case ZFS_DELEG_NOTE_RELEASE: + str = gettext("Allows releasing a user hold which\n\t\t\t\t" + "might destroy the snapshot"); + break; + case ZFS_DELEG_NOTE_RENAME: + str = gettext("Must also have the 'mount' and 'create'" + "\n\t\t\t\tability in the new parent"); + break; + case ZFS_DELEG_NOTE_ROLLBACK: + str = gettext(""); + break; + case ZFS_DELEG_NOTE_SEND: + str = gettext(""); + break; + case ZFS_DELEG_NOTE_SHARE: + str = gettext("Allows sharing file systems over NFS or SMB" + "\n\t\t\t\tprotocols"); + break; + case ZFS_DELEG_NOTE_SNAPSHOT: + str = gettext(""); + break; +/* + * case ZFS_DELEG_NOTE_VSCAN: + * str = gettext(""); + * break; + */ + /* OTHER */ + case ZFS_DELEG_NOTE_GROUPQUOTA: + str = gettext("Allows accessing any groupquota@... property"); + break; + case ZFS_DELEG_NOTE_GROUPUSED: + str = gettext("Allows reading any groupused@... property"); + break; + case ZFS_DELEG_NOTE_USERPROP: + str = gettext("Allows changing any user property"); + break; + case ZFS_DELEG_NOTE_USERQUOTA: + str = gettext("Allows accessing any userquota@... property"); + break; + case ZFS_DELEG_NOTE_USERUSED: + str = gettext("Allows reading any userused@... property"); + break; + /* other */ + default: + str = ""; + } + + return (str); +} + +struct allow_opts { + boolean_t local; + boolean_t descend; + boolean_t user; + boolean_t group; + boolean_t everyone; + boolean_t create; + boolean_t set; + boolean_t recursive; /* unallow only */ + boolean_t prt_usage; + + boolean_t prt_perms; + char *who; + char *perms; + const char *dataset; +}; + +static inline int +prop_cmp(const void *a, const void *b) +{ + const char *str1 = *(const char **)a; + const char *str2 = *(const char **)b; + return (strcmp(str1, str2)); +} + +static void +allow_usage(boolean_t un, boolean_t requested, const char *msg) +{ + const char *opt_desc[] = { + "-h", gettext("show this help message and exit"), + "-l", gettext("set permission locally"), + "-d", gettext("set permission for descents"), + "-u", gettext("set permission for user"), + "-g", gettext("set permission for group"), + "-e", gettext("set permission for everyone"), + "-c", gettext("set create time permission"), + "-s", gettext("define permission set"), + /* unallow only */ + "-r", gettext("remove permissions recursively"), + }; + size_t unallow_size = sizeof (opt_desc) / sizeof (char *); + size_t allow_size = unallow_size - 2; + const char *props[ZFS_NUM_PROPS]; + int i; + size_t count = 0; + FILE *fp = requested ? stdout : stderr; + zprop_desc_t *pdtbl = zfs_prop_get_table(); + const char *fmt = gettext("%-16s %-14s\t%s\n"); + + (void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW : + HELP_ALLOW)); + (void) fprintf(fp, gettext("Options:\n")); + for (i = 0; i < (un ? unallow_size : allow_size); i++) { + const char *opt = opt_desc[i++]; + const char *optdsc = opt_desc[i]; + (void) fprintf(fp, gettext(" %-10s %s\n"), opt, optdsc); + } + + (void) fprintf(fp, gettext("\nThe following permissions are " + "supported:\n\n")); + (void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"), + gettext("NOTES")); + for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) { + const char *perm_name = zfs_deleg_perm_tbl[i].z_perm; + zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note; + const char *perm_type = deleg_perm_type(perm_note); + const char *perm_comment = deleg_perm_comment(perm_note); + (void) fprintf(fp, fmt, perm_name, perm_type, perm_comment); + } + + for (i = 0; i < ZFS_NUM_PROPS; i++) { + zprop_desc_t *pd = &pdtbl[i]; + if (pd->pd_visible != B_TRUE) + continue; + + if (pd->pd_attr == PROP_READONLY) + continue; + + props[count++] = pd->pd_name; + } + props[count] = NULL; + + qsort(props, count, sizeof (char *), prop_cmp); + + for (i = 0; i < count; i++) + (void) fprintf(fp, fmt, props[i], gettext("property"), ""); + + if (msg != NULL) + (void) fprintf(fp, gettext("\nzfs: error: %s"), msg); + + exit(requested ? 0 : 2); +} + +static inline const char * +munge_args(int argc, char **argv, boolean_t un, size_t expected_argc, + char **permsp) +{ + if (un && argc == expected_argc - 1) + *permsp = NULL; + else if (argc == expected_argc) + *permsp = argv[argc - 2]; + else + allow_usage(un, B_FALSE, + gettext("wrong number of parameters\n")); + + return (argv[argc - 1]); +} + +static void +parse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts) +{ + int uge_sum = opts->user + opts->group + opts->everyone; + int csuge_sum = opts->create + opts->set + uge_sum; + int ldcsuge_sum = csuge_sum + opts->local + opts->descend; + int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum; + + if (uge_sum > 1) + allow_usage(un, B_FALSE, + gettext("-u, -g, and -e are mutually exclusive\n")); + + if (opts->prt_usage) { + if (argc == 0 && all_sum == 0) + allow_usage(un, B_TRUE, NULL); + else + usage(B_FALSE); + } + + if (opts->set) { + if (csuge_sum > 1) + allow_usage(un, B_FALSE, + gettext("invalid options combined with -s\n")); + + opts->dataset = munge_args(argc, argv, un, 3, &opts->perms); + if (argv[0][0] != '@') + allow_usage(un, B_FALSE, + gettext("invalid set name: missing '@' prefix\n")); + opts->who = argv[0]; + } else if (opts->create) { + if (ldcsuge_sum > 1) + allow_usage(un, B_FALSE, + gettext("invalid options combined with -c\n")); + opts->dataset = munge_args(argc, argv, un, 2, &opts->perms); + } else if (opts->everyone) { + if (csuge_sum > 1) + allow_usage(un, B_FALSE, + gettext("invalid options combined with -e\n")); + opts->dataset = munge_args(argc, argv, un, 2, &opts->perms); + } else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone") + == 0) { + opts->everyone = B_TRUE; + argc--; + argv++; + opts->dataset = munge_args(argc, argv, un, 2, &opts->perms); + } else if (argc == 1) { + opts->prt_perms = B_TRUE; + opts->dataset = argv[argc-1]; + } else { + opts->dataset = munge_args(argc, argv, un, 3, &opts->perms); + opts->who = argv[0]; + } + + if (!opts->local && !opts->descend) { + opts->local = B_TRUE; + opts->descend = B_TRUE; + } +} + +static void +store_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend, + const char *who, char *perms, nvlist_t *top_nvl) +{ + int i; + char ld[2] = { '\0', '\0' }; + char who_buf[ZFS_MAXNAMELEN+32]; + char base_type = ZFS_DELEG_WHO_UNKNOWN; + char set_type = ZFS_DELEG_WHO_UNKNOWN; + nvlist_t *base_nvl = NULL; + nvlist_t *set_nvl = NULL; + nvlist_t *nvl; + + if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0) + nomem(); + if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) != 0) + nomem(); + + switch (type) { + case ZFS_DELEG_NAMED_SET_SETS: + case ZFS_DELEG_NAMED_SET: + set_type = ZFS_DELEG_NAMED_SET_SETS; + base_type = ZFS_DELEG_NAMED_SET; + ld[0] = ZFS_DELEG_NA; + break; + case ZFS_DELEG_CREATE_SETS: + case ZFS_DELEG_CREATE: + set_type = ZFS_DELEG_CREATE_SETS; + base_type = ZFS_DELEG_CREATE; + ld[0] = ZFS_DELEG_NA; + break; + case ZFS_DELEG_USER_SETS: + case ZFS_DELEG_USER: + set_type = ZFS_DELEG_USER_SETS; + base_type = ZFS_DELEG_USER; + if (local) + ld[0] = ZFS_DELEG_LOCAL; + if (descend) + ld[1] = ZFS_DELEG_DESCENDENT; + break; + case ZFS_DELEG_GROUP_SETS: + case ZFS_DELEG_GROUP: + set_type = ZFS_DELEG_GROUP_SETS; + base_type = ZFS_DELEG_GROUP; + if (local) + ld[0] = ZFS_DELEG_LOCAL; + if (descend) + ld[1] = ZFS_DELEG_DESCENDENT; + break; + case ZFS_DELEG_EVERYONE_SETS: + case ZFS_DELEG_EVERYONE: + set_type = ZFS_DELEG_EVERYONE_SETS; + base_type = ZFS_DELEG_EVERYONE; + if (local) + ld[0] = ZFS_DELEG_LOCAL; + if (descend) + ld[1] = ZFS_DELEG_DESCENDENT; + default: + break; + } + + if (perms != NULL) { + char *curr = perms; + char *end = curr + strlen(perms); + + while (curr < end) { + char *delim = strchr(curr, ','); + if (delim == NULL) + delim = end; + else + *delim = '\0'; + + if (curr[0] == '@') + nvl = set_nvl; + else + nvl = base_nvl; + + (void) nvlist_add_boolean(nvl, curr); + if (delim != end) + *delim = ','; + curr = delim + 1; + } + + for (i = 0; i < 2; i++) { + char locality = ld[i]; + if (locality == 0) + continue; + + if (!nvlist_empty(base_nvl)) { + if (who != NULL) + (void) snprintf(who_buf, + sizeof (who_buf), "%c%c$%s", + base_type, locality, who); + else + (void) snprintf(who_buf, + sizeof (who_buf), "%c%c$", + base_type, locality); + + (void) nvlist_add_nvlist(top_nvl, who_buf, + base_nvl); + } + + + if (!nvlist_empty(set_nvl)) { + if (who != NULL) + (void) snprintf(who_buf, + sizeof (who_buf), "%c%c$%s", + set_type, locality, who); + else + (void) snprintf(who_buf, + sizeof (who_buf), "%c%c$", + set_type, locality); + + (void) nvlist_add_nvlist(top_nvl, who_buf, + set_nvl); + } + } + } else { + for (i = 0; i < 2; i++) { + char locality = ld[i]; + if (locality == 0) + continue; + + if (who != NULL) + (void) snprintf(who_buf, sizeof (who_buf), + "%c%c$%s", base_type, locality, who); + else + (void) snprintf(who_buf, sizeof (who_buf), + "%c%c$", base_type, locality); + (void) nvlist_add_boolean(top_nvl, who_buf); + + if (who != NULL) + (void) snprintf(who_buf, sizeof (who_buf), + "%c%c$%s", set_type, locality, who); + else + (void) snprintf(who_buf, sizeof (who_buf), + "%c%c$", set_type, locality); + (void) nvlist_add_boolean(top_nvl, who_buf); + } + } +} + +static int +construct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp) +{ + if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0) + nomem(); + + if (opts->set) { + store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local, + opts->descend, opts->who, opts->perms, *nvlp); + } else if (opts->create) { + store_allow_perm(ZFS_DELEG_CREATE, opts->local, + opts->descend, NULL, opts->perms, *nvlp); + } else if (opts->everyone) { + store_allow_perm(ZFS_DELEG_EVERYONE, opts->local, + opts->descend, NULL, opts->perms, *nvlp); + } else { + char *curr = opts->who; + char *end = curr + strlen(curr); + + while (curr < end) { + const char *who; + zfs_deleg_who_type_t who_type = ZFS_DELEG_WHO_UNKNOWN; + char *endch; + char *delim = strchr(curr, ','); + char errbuf[256]; + char id[64]; + struct passwd *p = NULL; + struct group *g = NULL; + + uid_t rid; + if (delim == NULL) + delim = end; + else + *delim = '\0'; + + rid = (uid_t)strtol(curr, &endch, 0); + if (opts->user) { + who_type = ZFS_DELEG_USER; + if (*endch != '\0') + p = getpwnam(curr); + else + p = getpwuid(rid); + + if (p != NULL) + rid = p->pw_uid; + else { + (void) snprintf(errbuf, 256, gettext( + "invalid user %s"), curr); + allow_usage(un, B_TRUE, errbuf); + } + } else if (opts->group) { + who_type = ZFS_DELEG_GROUP; + if (*endch != '\0') + g = getgrnam(curr); + else + g = getgrgid(rid); + + if (g != NULL) + rid = g->gr_gid; + else { + (void) snprintf(errbuf, 256, gettext( + "invalid group %s"), curr); + allow_usage(un, B_TRUE, errbuf); + } + } else { + if (*endch != '\0') { + p = getpwnam(curr); + } else { + p = getpwuid(rid); + } + + if (p == NULL) { + if (*endch != '\0') { + g = getgrnam(curr); + } else { + g = getgrgid(rid); + } + } + + if (p != NULL) { + who_type = ZFS_DELEG_USER; + rid = p->pw_uid; + } else if (g != NULL) { + who_type = ZFS_DELEG_GROUP; + rid = g->gr_gid; + } else { + (void) snprintf(errbuf, 256, gettext( + "invalid user/group %s"), curr); + allow_usage(un, B_TRUE, errbuf); + } + } + + (void) sprintf(id, "%u", rid); + who = id; + + store_allow_perm(who_type, opts->local, + opts->descend, who, opts->perms, *nvlp); + curr = delim + 1; + } + } + + return (0); +} + +static void +print_set_creat_perms(uu_avl_t *who_avl) +{ + const char *sc_title[] = { + gettext("Permission sets:\n"), + gettext("Create time permissions:\n"), + NULL + }; + const char **title_ptr = sc_title; + who_perm_node_t *who_node = NULL; + int prev_weight = -1; + + for (who_node = uu_avl_first(who_avl); who_node != NULL; + who_node = uu_avl_next(who_avl, who_node)) { + uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl; + zfs_deleg_who_type_t who_type = who_node->who_perm.who_type; + const char *who_name = who_node->who_perm.who_name; + int weight = who_type2weight(who_type); + boolean_t first = B_TRUE; + deleg_perm_node_t *deleg_node; + + if (prev_weight != weight) { + (void) printf("%s", *title_ptr++); + prev_weight = weight; + } + + if (who_name == NULL || strnlen(who_name, 1) == 0) + (void) printf("\t"); + else + (void) printf("\t%s ", who_name); + + for (deleg_node = uu_avl_first(avl); deleg_node != NULL; + deleg_node = uu_avl_next(avl, deleg_node)) { + if (first) { + (void) printf("%s", + deleg_node->dpn_perm.dp_name); + first = B_FALSE; + } else + (void) printf(",%s", + deleg_node->dpn_perm.dp_name); + } + + (void) printf("\n"); + } +} + +static void inline +print_uge_deleg_perms(uu_avl_t *who_avl, boolean_t local, boolean_t descend, + const char *title) +{ + who_perm_node_t *who_node = NULL; + boolean_t prt_title = B_TRUE; + uu_avl_walk_t *walk; + + if ((walk = uu_avl_walk_start(who_avl, UU_WALK_ROBUST)) == NULL) + nomem(); + + while ((who_node = uu_avl_walk_next(walk)) != NULL) { + const char *who_name = who_node->who_perm.who_name; + const char *nice_who_name = who_node->who_perm.who_ug_name; + uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl; + zfs_deleg_who_type_t who_type = who_node->who_perm.who_type; + char delim = ' '; + deleg_perm_node_t *deleg_node; + boolean_t prt_who = B_TRUE; + + for (deleg_node = uu_avl_first(avl); + deleg_node != NULL; + deleg_node = uu_avl_next(avl, deleg_node)) { + if (local != deleg_node->dpn_perm.dp_local || + descend != deleg_node->dpn_perm.dp_descend) + continue; + + if (prt_who) { + const char *who = NULL; + if (prt_title) { + prt_title = B_FALSE; + (void) printf("%s", title); + } + + switch (who_type) { + case ZFS_DELEG_USER_SETS: + case ZFS_DELEG_USER: + who = gettext("user"); + if (nice_who_name) + who_name = nice_who_name; + break; + case ZFS_DELEG_GROUP_SETS: + case ZFS_DELEG_GROUP: + who = gettext("group"); + if (nice_who_name) + who_name = nice_who_name; + break; + case ZFS_DELEG_EVERYONE_SETS: + case ZFS_DELEG_EVERYONE: + who = gettext("everyone"); + who_name = NULL; + default: + break; + } + + prt_who = B_FALSE; + if (who_name == NULL) + (void) printf("\t%s", who); + else + (void) printf("\t%s %s", who, who_name); + } + + (void) printf("%c%s", delim, + deleg_node->dpn_perm.dp_name); + delim = ','; + } + + if (!prt_who) + (void) printf("\n"); + } + + uu_avl_walk_end(walk); +} + +static void +print_fs_perms(fs_perm_set_t *fspset) +{ + fs_perm_node_t *node = NULL; + char buf[ZFS_MAXNAMELEN+32]; + const char *dsname = buf; + + for (node = uu_list_first(fspset->fsps_list); node != NULL; + node = uu_list_next(fspset->fsps_list, node)) { + uu_avl_t *sc_avl = node->fspn_fsperm.fsp_sc_avl; + uu_avl_t *uge_avl = node->fspn_fsperm.fsp_uge_avl; + int left = 0; + + (void) snprintf(buf, ZFS_MAXNAMELEN+32, + gettext("---- Permissions on %s "), + node->fspn_fsperm.fsp_name); + (void) printf("%s", dsname); + left = 70 - strlen(buf); + while (left-- > 0) + (void) printf("-"); + (void) printf("\n"); + + print_set_creat_perms(sc_avl); + print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE, + gettext("Local permissions:\n")); + print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE, + gettext("Descendent permissions:\n")); + print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE, + gettext("Local+Descendent permissions:\n")); + } +} + +static fs_perm_set_t fs_perm_set = { NULL, NULL, NULL, NULL }; + +struct deleg_perms { + boolean_t un; + nvlist_t *nvl; +}; + +static int +set_deleg_perms(zfs_handle_t *zhp, void *data) +{ + struct deleg_perms *perms = (struct deleg_perms *)data; + zfs_type_t zfs_type = zfs_get_type(zhp); + + if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME) + return (0); + + return (zfs_set_fsacl(zhp, perms->un, perms->nvl)); +} + +static int +zfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un) +{ + zfs_handle_t *zhp; + nvlist_t *perm_nvl = NULL; + nvlist_t *update_perm_nvl = NULL; + int error = 1; + int c; + struct allow_opts opts = { 0 }; + + const char *optstr = un ? "ldugecsrh" : "ldugecsh"; + + /* check opts */ + while ((c = getopt(argc, argv, optstr)) != -1) { + switch (c) { + case 'l': + opts.local = B_TRUE; + break; + case 'd': + opts.descend = B_TRUE; + break; + case 'u': + opts.user = B_TRUE; + break; + case 'g': + opts.group = B_TRUE; + break; + case 'e': + opts.everyone = B_TRUE; + break; + case 's': + opts.set = B_TRUE; + break; + case 'c': + opts.create = B_TRUE; + break; + case 'r': + opts.recursive = B_TRUE; + break; + case ':': + (void) fprintf(stderr, gettext("missing argument for " + "'%c' option\n"), optopt); + usage(B_FALSE); + break; + case 'h': + opts.prt_usage = B_TRUE; + break; + case '?': + (void) fprintf(stderr, gettext("invalid option '%c'\n"), + optopt); + usage(B_FALSE); + } + } + + argc -= optind; + argv += optind; + + /* check arguments */ + parse_allow_args(argc, argv, un, &opts); + + /* try to open the dataset */ + if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM)) + == NULL) { + (void) fprintf(stderr, "Failed to open Dataset *%s*\n", + opts.dataset); + return (-1); + } + + if (zfs_get_fsacl(zhp, &perm_nvl) != 0) + goto cleanup2; + + fs_perm_set_init(&fs_perm_set); + if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) { + (void) fprintf(stderr, "Failed to parse fsacl permissionsn"); + goto cleanup1; + } + + if (opts.prt_perms) + print_fs_perms(&fs_perm_set); + else { + (void) construct_fsacl_list(un, &opts, &update_perm_nvl); + if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0) + goto cleanup0; + + if (un && opts.recursive) { + struct deleg_perms data = { un, update_perm_nvl }; + if (zfs_iter_filesystems(zhp, set_deleg_perms, + &data) != 0) + goto cleanup0; + } + } + + error = 0; + +cleanup0: + nvlist_free(perm_nvl); + if (update_perm_nvl != NULL) + nvlist_free(update_perm_nvl); +cleanup1: + fs_perm_set_fini(&fs_perm_set); +cleanup2: + zfs_close(zhp); + + return (error); +} + +/* + * zfs allow [-r] [-t] ... + * + * -r Recursively hold + * -t Temporary hold (hidden option) + * + * Apply a user-hold with the given tag to the list of snapshots. + */ +static int +zfs_do_allow(int argc, char **argv) +{ + return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE)); +} + +/* + * zfs unallow [-r] [-t] ... + * + * -r Recursively hold + * -t Temporary hold (hidden option) + * + * Apply a user-hold with the given tag to the list of snapshots. + */ +static int +zfs_do_unallow(int argc, char **argv) +{ + return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE)); +} + +static int +zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding) +{ + int errors = 0; + int i; + const char *tag; + boolean_t recursive = B_FALSE; + boolean_t temphold = B_FALSE; const char *opts = holding ? "rt" : "r"; int c; @@ -2861,8 +4863,225 @@ zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding) case 'r': recursive = B_TRUE; break; - case 't': - temphold = B_TRUE; + case 't': + temphold = B_TRUE; + break; + case '?': + (void) fprintf(stderr, gettext("invalid option '%c'\n"), + optopt); + usage(B_FALSE); + } + } + + argc -= optind; + argv += optind; + + /* check number of arguments */ + if (argc < 2) + usage(B_FALSE); + + tag = argv[0]; + --argc; + ++argv; + + if (holding && tag[0] == '.') { + /* tags starting with '.' are reserved for libzfs */ + (void) fprintf(stderr, gettext("tag may not start with '.'\n")); + usage(B_FALSE); + } + + for (i = 0; i < argc; ++i) { + zfs_handle_t *zhp; + char parent[ZFS_MAXNAMELEN]; + const char *delim; + char *path = argv[i]; + + delim = strchr(path, '@'); + if (delim == NULL) { + (void) fprintf(stderr, + gettext("'%s' is not a snapshot\n"), path); + ++errors; + continue; + } + (void) strncpy(parent, path, delim - path); + parent[delim - path] = '\0'; + + zhp = zfs_open(g_zfs, parent, + ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); + if (zhp == NULL) { + ++errors; + continue; + } + if (holding) { + if (zfs_hold(zhp, delim+1, tag, recursive, + temphold, B_FALSE, -1, 0, 0) != 0) + ++errors; + } else { + if (zfs_release(zhp, delim+1, tag, recursive) != 0) + ++errors; + } + zfs_close(zhp); + } + + return (errors != 0); +} + +/* + * zfs hold [-r] [-t] ... + * + * -r Recursively hold + * -t Temporary hold (hidden option) + * + * Apply a user-hold with the given tag to the list of snapshots. + */ +static int +zfs_do_hold(int argc, char **argv) +{ + return (zfs_do_hold_rele_impl(argc, argv, B_TRUE)); +} + +/* + * zfs release [-r] ... + * + * -r Recursively release + * + * Release a user-hold with the given tag from the list of snapshots. + */ +static int +zfs_do_release(int argc, char **argv) +{ + return (zfs_do_hold_rele_impl(argc, argv, B_FALSE)); +} + +typedef struct holds_cbdata { + boolean_t cb_recursive; + const char *cb_snapname; + nvlist_t **cb_nvlp; + size_t cb_max_namelen; + size_t cb_max_taglen; +} holds_cbdata_t; + +#define STRFTIME_FMT_STR "%a %b %e %k:%M %Y" +#define DATETIME_BUF_LEN (32) +/* + * + */ +static void +print_holds(boolean_t scripted, int nwidth, int tagwidth, nvlist_t *nvl) +{ + int i; + nvpair_t *nvp = NULL; + char *hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" }; + const char *col; + + if (!scripted) { + for (i = 0; i < 3; i++) { + col = gettext(hdr_cols[i]); + if (i < 2) + (void) printf("%-*s ", i ? tagwidth : nwidth, + col); + else + (void) printf("%s\n", col); + } + } + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + char *zname = nvpair_name(nvp); + nvlist_t *nvl2; + nvpair_t *nvp2 = NULL; + (void) nvpair_value_nvlist(nvp, &nvl2); + while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) { + char tsbuf[DATETIME_BUF_LEN]; + char *tagname = nvpair_name(nvp2); + uint64_t val = 0; + time_t time; + struct tm t; + char sep = scripted ? '\t' : ' '; + int sepnum = scripted ? 1 : 2; + + (void) nvpair_value_uint64(nvp2, &val); + time = (time_t)val; + (void) localtime_r(&time, &t); + (void) strftime(tsbuf, DATETIME_BUF_LEN, + gettext(STRFTIME_FMT_STR), &t); + + (void) printf("%-*s%*c%-*s%*c%s\n", nwidth, zname, + sepnum, sep, tagwidth, tagname, sepnum, sep, tsbuf); + } + } +} + +/* + * Generic callback function to list a dataset or snapshot. + */ +static int +holds_callback(zfs_handle_t *zhp, void *data) +{ + holds_cbdata_t *cbp = data; + nvlist_t *top_nvl = *cbp->cb_nvlp; + nvlist_t *nvl = NULL; + nvpair_t *nvp = NULL; + const char *zname = zfs_get_name(zhp); + size_t znamelen = strnlen(zname, ZFS_MAXNAMELEN); + + if (cbp->cb_recursive) { + const char *snapname; + char *delim = strchr(zname, '@'); + if (delim == NULL) + return (0); + + snapname = delim + 1; + if (strcmp(cbp->cb_snapname, snapname)) + return (0); + } + + if (zfs_get_holds(zhp, &nvl) != 0) + return (-1); + + if (znamelen > cbp->cb_max_namelen) + cbp->cb_max_namelen = znamelen; + + while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { + const char *tag = nvpair_name(nvp); + size_t taglen = strnlen(tag, MAXNAMELEN); + if (taglen > cbp->cb_max_taglen) + cbp->cb_max_taglen = taglen; + } + + return (nvlist_add_nvlist(top_nvl, zname, nvl)); +} + +/* + * zfs holds [-r] ... + * + * -r Recursively hold + */ +static int +zfs_do_holds(int argc, char **argv) +{ + int errors = 0; + int c; + int i; + boolean_t scripted = B_FALSE; + boolean_t recursive = B_FALSE; + const char *opts = "rH"; + nvlist_t *nvl; + + int types = ZFS_TYPE_SNAPSHOT; + holds_cbdata_t cb = { 0 }; + + int limit = 0; + int ret = 0; + int flags = 0; + + /* check options */ + while ((c = getopt(argc, argv, opts)) != -1) { + switch (c) { + case 'r': + recursive = B_TRUE; + break; + case 'H': + scripted = B_TRUE; break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), @@ -2871,91 +5090,67 @@ zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding) } } + if (recursive) { + types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME; + flags |= ZFS_ITER_RECURSE; + } + argc -= optind; argv += optind; /* check number of arguments */ - if (argc < 2) + if (argc < 1) usage(B_FALSE); - tag = argv[0]; - --argc; - ++argv; - - if (holding && tag[0] == '.') { - /* tags starting with '.' are reserved for libzfs */ - (void) fprintf(stderr, gettext("tag may not start with '.'\n")); - usage(B_FALSE); - } + if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) + nomem(); for (i = 0; i < argc; ++i) { - zfs_handle_t *zhp; - char parent[ZFS_MAXNAMELEN]; + char *snapshot = argv[i]; const char *delim; - char *path = argv[i]; + const char *snapname; - delim = strchr(path, '@'); + delim = strchr(snapshot, '@'); if (delim == NULL) { (void) fprintf(stderr, - gettext("'%s' is not a snapshot\n"), path); + gettext("'%s' is not a snapshot\n"), snapshot); ++errors; continue; } - (void) strncpy(parent, path, delim - path); - parent[delim - path] = '\0'; + snapname = delim + 1; + if (recursive) + snapshot[delim - snapshot] = '\0'; - zhp = zfs_open(g_zfs, parent, - ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); - if (zhp == NULL) { + cb.cb_recursive = recursive; + cb.cb_snapname = snapname; + cb.cb_nvlp = &nvl; + + /* + * 1. collect holds data, set format options + */ + ret = zfs_for_each(argc, argv, flags, types, NULL, NULL, limit, + holds_callback, &cb); + if (ret != 0) ++errors; - continue; - } - if (holding) { - if (zfs_hold(zhp, delim+1, tag, recursive, - temphold, B_FALSE, -1, 0, 0) != 0) - ++errors; - } else { - if (zfs_release(zhp, delim+1, tag, recursive) != 0) - ++errors; - } - zfs_close(zhp); } - return (errors != 0); -} + /* + * 2. print holds data + */ + print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl); -/* - * zfs hold [-r] [-t] ... - * - * -r Recursively hold - * -t Temporary hold (hidden option) - * - * Apply a user-hold with the given tag to the list of snapshots. - */ -static int -zfs_do_hold(int argc, char **argv) -{ - return (zfs_do_hold_rele_impl(argc, argv, B_TRUE)); -} + if (nvlist_empty(nvl)) + (void) fprintf(stderr, gettext("no datasets available\n")); -/* - * zfs release [-r] ... - * - * -r Recursively release - * - * Release a user-hold with the given tag from the list of snapshots. - */ -static int -zfs_do_release(int argc, char **argv) -{ - return (zfs_do_hold_rele_impl(argc, argv, B_FALSE)); + nvlist_free(nvl); + + return (0 != errors); } #define CHECK_SPINNER 30 #define SPINNER_TIME 3 /* seconds */ #define MOUNT_TIME 5 /* seconds */ -#ifdef HAVE_ZPL static int get_one_dataset(zfs_handle_t *zhp, void *data) { @@ -3297,7 +5492,6 @@ share_mount(int op, int argc, char **argv) /* option validation is done later */ append_options(options, optarg); break; - case 'O': flags |= MS_OVERLAY; break; @@ -3368,7 +5562,7 @@ share_mount(int op, int argc, char **argv) } /* - * When mount is given no arguments, go through /etc/mnttab and + * When mount is given no arguments, go through /etc/mtab and * display any active ZFS mounts. We hide any snapshots, since * they are controlled automatically. */ @@ -3403,7 +5597,6 @@ share_mount(int op, int argc, char **argv) return (ret); } -#endif /* HAVE_ZPL */ /* * zfs mount -a [nfs] @@ -3414,11 +5607,7 @@ share_mount(int op, int argc, char **argv) static int zfs_do_mount(int argc, char **argv) { -#ifdef HAVE_ZPL return (share_mount(OP_MOUNT, argc, argv)); -#else - return ENOSYS; -#endif /* HAVE_ZPL */ } /* @@ -3430,14 +5619,9 @@ zfs_do_mount(int argc, char **argv) static int zfs_do_share(int argc, char **argv) { -#ifdef HAVE_ZPL return (share_mount(OP_SHARE, argc, argv)); -#else - return ENOSYS; -#endif /* HAVE_ZPL */ } -#ifdef HAVE_ZPL typedef struct unshare_unmount_node { zfs_handle_t *un_zhp; char *un_mountp; @@ -3456,21 +5640,21 @@ unshare_unmount_compare(const void *larg, const void *rarg, void *unused) /* * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an - * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, + * absolute path, find the entry /etc/mtab, verify that its a ZFS filesystem, * and unmount it appropriately. */ static int unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) { zfs_handle_t *zhp; - int ret; + int ret = 0; struct stat64 statbuf; struct extmnttab entry; const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount"; ino_t path_inode; /* - * Search for the path in /etc/mnttab. Rather than looking for the + * Search for the path in /etc/mtab. Rather than looking for the * specific path, which can be fooled by non-standard paths (i.e. ".." * or "//"), we stat() the path and search for the corresponding * (major,minor) device pair. @@ -3497,7 +5681,7 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) "currently mounted\n"), cmdname, path); return (1); } - (void) fprintf(stderr, gettext("warning: %s not in mnttab\n"), + (void) fprintf(stderr, gettext("warning: %s not in mtab\n"), path); if ((ret = umount2(path, flags)) != 0) (void) fprintf(stderr, gettext("%s: %s\n"), path, @@ -3539,8 +5723,8 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) strcmp(smbshare_prop, "off") == 0) { (void) fprintf(stderr, gettext("cannot unshare " "'%s': legacy share\n"), path); - (void) fprintf(stderr, gettext("use " - "unshare(1M) to unshare this filesystem\n")); + (void) fprintf(stderr, gettext("use exportfs(8) " + "or smbcontrol(1) to unshare this filesystem\n")); } else if (!zfs_is_shared(zhp)) { (void) fprintf(stderr, gettext("cannot unshare '%s': " "not currently shared\n"), path); @@ -3559,7 +5743,7 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) (void) fprintf(stderr, gettext("cannot unmount " "'%s': legacy mountpoint\n"), zfs_get_name(zhp)); - (void) fprintf(stderr, gettext("use umount(1M) " + (void) fprintf(stderr, gettext("use umount(8) " "to unmount this filesystem\n")); } else { ret = zfs_unmountall(zhp, flags); @@ -3609,8 +5793,8 @@ unshare_unmount(int op, int argc, char **argv) /* * We could make use of zfs_for_each() to walk all datasets in * the system, but this would be very inefficient, especially - * since we would have to linearly search /etc/mnttab for each - * one. Instead, do one pass through /etc/mnttab looking for + * since we would have to linearly search /etc/mtab for each + * one. Instead, do one pass through /etc/mtab looking for * zfs entries and call zfs_unmount() for each one. * * Things get a little tricky if the administrator has created @@ -3821,7 +6005,6 @@ unshare_unmount(int op, int argc, char **argv) return (ret); } -#endif /* HAVE_ZPL */ /* * zfs unmount -a @@ -3832,11 +6015,7 @@ unshare_unmount(int op, int argc, char **argv) static int zfs_do_unmount(int argc, char **argv) { -#ifdef HAVE_ZPL return (unshare_unmount(OP_MOUNT, argc, argv)); -#else - return ENOSYS; -#endif /* HAVE_ZPL */ } /* @@ -3848,319 +6027,7 @@ zfs_do_unmount(int argc, char **argv) static int zfs_do_unshare(int argc, char **argv) { -#ifdef HAVE_ZPL return (unshare_unmount(OP_SHARE, argc, argv)); -#else - return ENOSYS; -#endif /* HAVE_ZPL */ -} - -/* ARGSUSED */ -static int -zfs_do_python(int argc, char **argv) -{ - (void) execv(pypath, argv-1); - (void) printf("internal error: %s not found\n", pypath); - return (-1); -} - -typedef struct option_map { - const char *name; - int mask; -} option_map_t; - -static const option_map_t option_map[] = { - /* Canonicalized filesystem independent options from mount(8) */ - { MNTOPT_NOAUTO, MS_COMMENT }, - { MNTOPT_DEFAULTS, MS_COMMENT }, - { MNTOPT_NODEVICES, MS_NODEV }, - { MNTOPT_DIRSYNC, MS_DIRSYNC }, - { MNTOPT_NOEXEC, MS_NOEXEC }, - { MNTOPT_GROUP, MS_GROUP }, - { MNTOPT_NETDEV, MS_COMMENT }, - { MNTOPT_NOFAIL, MS_COMMENT }, - { MNTOPT_NOSUID, MS_NOSUID }, - { MNTOPT_OWNER, MS_OWNER }, - { MNTOPT_REMOUNT, MS_REMOUNT }, - { MNTOPT_RO, MS_RDONLY }, - { MNTOPT_SYNC, MS_SYNCHRONOUS }, - { MNTOPT_USER, MS_USERS }, - { MNTOPT_USERS, MS_USERS }, -#ifdef MS_NOATIME - { MNTOPT_NOATIME, MS_NOATIME }, -#endif -#ifdef MS_NODIRATIME - { MNTOPT_NODIRATIME, MS_NODIRATIME }, -#endif -#ifdef MS_RELATIME - { MNTOPT_RELATIME, MS_RELATIME }, -#endif -#ifdef MS_STRICTATIME - { MNTOPT_DFRATIME, MS_STRICTATIME }, -#endif -#ifdef HAVE_SELINUX - { MNTOPT_CONTEXT, MS_COMMENT }, - { MNTOPT_FSCONTEXT, MS_COMMENT }, - { MNTOPT_DEFCONTEXT, MS_COMMENT }, - { MNTOPT_ROOTCONTEXT, MS_COMMENT }, -#endif -#ifdef MS_I_VERSION - { MNTOPT_IVERSION, MS_I_VERSION }, -#endif -#ifdef MS_MANDLOCK - { MNTOPT_NBMAND, MS_MANDLOCK }, -#endif - /* Valid options not found in mount(8) */ - { MNTOPT_BIND, MS_BIND }, - { MNTOPT_RBIND, MS_BIND|MS_REC }, - { MNTOPT_COMMENT, MS_COMMENT }, - { MNTOPT_BOOTWAIT, MS_COMMENT }, - { MNTOPT_NOBOOTWAIT, MS_COMMENT }, - { MNTOPT_OPTIONAL, MS_COMMENT }, - { MNTOPT_SHOWTHROUGH, MS_COMMENT }, -#ifdef MS_NOSUB - { MNTOPT_NOSUB, MS_NOSUB }, -#endif -#ifdef MS_SILENT - { MNTOPT_QUIET, MS_SILENT }, -#endif - /* Custom zfs options */ - { MNTOPT_NOXATTR, MS_COMMENT }, - { NULL, 0 } }; - -/* - * Break the mount option in to a name/value pair. The name is - * validated against the option map and mount flags set accordingly. - */ -static int -parse_option(char *mntopt, unsigned long *mntflags, int sloppy) -{ - const option_map_t *opt; - char *ptr, *name, *value = NULL; - int rc; - - name = strdup(mntopt); - if (name == NULL) - return (ENOMEM); - - for (ptr = name; ptr && *ptr; ptr++) { - if (*ptr == '=') { - *ptr = '\0'; - value = ptr+1; - break; - } - } - - for (opt = option_map; opt->name != NULL; opt++) { - if (strncmp(name, opt->name, strlen(name)) == 0) { - *mntflags |= opt->mask; - - /* MS_USERS implies default user options */ - if (opt->mask & (MS_USERS)) - *mntflags |= (MS_NOEXEC|MS_NOSUID|MS_NODEV); - - /* MS_OWNER|MS_GROUP imply default owner options */ - if (opt->mask & (MS_OWNER | MS_GROUP)) - *mntflags |= (MS_NOSUID|MS_NODEV); - - rc = 0; - goto out; - } - } - - if (!sloppy) - rc = ENOENT; -out: - /* If required further process on the value may be done here */ - free(name); - return (rc); -} - -/* - * Translate the mount option string in to MS_* mount flags for the - * kernel vfs. When sloppy is non-zero unknown options will be ignored - * otherwise they are considered fatal are copied in to badopt. - */ -static int -parse_options(char *mntopts, unsigned long *mntflags, int sloppy, char *badopt) -{ - int rc = 0, quote = 0; - char *ptr, *opt, *opts; - - opts = strdup(mntopts); - if (opts == NULL) - return (ENOMEM); - - *mntflags = 0; - opt = NULL; - - /* - * Scan through all mount options which must be comma delimited. - * We must be careful to notice regions which are double quoted - * and skip commas in these regions. Each option is then checked - * to determine if it is a known option. - */ - for (ptr = opts; ptr && *ptr; ptr++) { - if (opt == NULL) - opt = ptr; - - if (*ptr == '"') - quote = !quote; - - if (quote) - continue; - - if ((*ptr == ',') || (*ptr == '\0')) { - *ptr = '\0'; - rc = parse_option(opt, mntflags, sloppy); - if (rc) { - strcpy(badopt, opt); - goto out; - } - - opt = NULL; - } - } -out: - free(opts); - return (rc); -} - -/* - * Called when invoked as /sbin/mount.zfs, mount helper for mount(8). - */ -static int -manual_mount(int argc, char **argv) -{ - zfs_handle_t *zhp; - char legacy[ZFS_MAXPROPLEN]; - char mntopts[MNT_LINE_MAX] = { '\0' }; - char badopt[MNT_LINE_MAX] = { '\0' }; - char *dataset, *mntpoint; - unsigned long mntflags; - int sloppy = 0, fake = 0, verbose = 0; - int rc, c; - - /* check options */ - while ((c = getopt(argc, argv, "sfnvo:h?")) != -1) { - switch (c) { - case 's': - sloppy = 1; - break; - case 'f': - fake = 1; - break; - case 'n': - /* Ignored, handled by mount(8) */ - break; - case 'v': - verbose++; - break; - case 'o': - (void) strlcpy(mntopts, optarg, sizeof (mntopts)); - break; - case 'h': - case '?': - (void) fprintf(stderr, gettext("Invalid option '%c'\n"), - optopt); - (void) fprintf(stderr, gettext("Usage: mount.zfs " - "[-sfnv] [-o options] \n")); - return (MOUNT_USAGE); - } - } - - argc -= optind; - argv += optind; - - /* check that we only have two arguments */ - if (argc != 2) { - if (argc == 0) - (void) fprintf(stderr, gettext("missing dataset " - "argument\n")); - else if (argc == 1) - (void) fprintf(stderr, - gettext("missing mountpoint argument\n")); - else - (void) fprintf(stderr, gettext("too many arguments\n")); - (void) fprintf(stderr, "usage: mount \n"); - return (2); - } - - dataset = argv[0]; - path = argv[1]; - - /* try to open the dataset */ - if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) - return (1); - - (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, - sizeof (mountpoint), NULL, NULL, 0, B_FALSE); - - /* check for legacy mountpoint and complain appropriately */ - ret = 0; - if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { - if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS, - NULL, 0, mntopts, sizeof (mntopts)) != 0) { - (void) fprintf(stderr, gettext("mount failed: %s\n"), - strerror(errno)); - ret = 1; - } - } else { - (void) fprintf(stderr, gettext("filesystem '%s' cannot be " - "mounted using 'mount -F zfs'\n"), dataset); - (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " - "instead.\n"), path); - (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' " - "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n")); - (void) fprintf(stderr, gettext("See zfs(1M) for more " - "information.\n")); - ret = 1; - } - - return (ret); -} - -/* - * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow - * unmounts of non-legacy filesystems, as this is the dominant administrative - * interface. - */ -static int -manual_unmount(int argc, char **argv) -{ - int flags = 0; - int c; - - /* check options */ - while ((c = getopt(argc, argv, "f")) != -1) { - switch (c) { - case 'f': - flags = MS_FORCE; - break; - case '?': - (void) fprintf(stderr, gettext("invalid option '%c'\n"), - optopt); - (void) fprintf(stderr, gettext("usage: unmount [-f] " - "\n")); - return (2); - } - } - - argc -= optind; - argv += optind; - - /* check arguments */ - if (argc != 1) { - if (argc == 0) - (void) fprintf(stderr, gettext("missing path " - "argument\n")); - else - (void) fprintf(stderr, gettext("too many arguments\n")); - (void) fprintf(stderr, gettext("usage: unmount [-f] \n")); - return (2); - } - - return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE)); } static int @@ -4188,7 +6055,7 @@ zfs_do_diff(int argc, char **argv) char *tosnap = NULL; char *fromsnap = NULL; char *atp, *copy; - int err; + int err = 0; int c; while ((c = getopt(argc, argv, "FHt")) != -1) { @@ -4258,9 +6125,8 @@ zfs_do_diff(int argc, char **argv) int main(int argc, char **argv) { - int ret; + int ret = 0; int i = 0; - char *progname; char *cmdname; (void) setlocale(LC_ALL, ""); @@ -4275,76 +6141,69 @@ main(int argc, char **argv) } /* - * This command also doubles as the /etc/fs mount and unmount program. - * Determine if we should take this behavior based on argv[0]. + * Make sure the user has specified some command. */ - progname = basename(argv[0]); - if (strcmp(progname, "mount.zfs") == 0) { - ret = manual_mount(argc, argv); - } else if (strcmp(progname, "umount.zfs") == 0) { - ret = manual_unmount(argc, argv); - } else { - /* - * Make sure the user has specified some command. - */ - if (argc < 2) { - (void) fprintf(stderr, gettext("missing command\n")); - usage(B_FALSE); - } + if (argc < 2) { + (void) fprintf(stderr, gettext("missing command\n")); + usage(B_FALSE); + } - cmdname = argv[1]; + cmdname = argv[1]; - /* - * The 'umount' command is an alias for 'unmount' - */ - if (strcmp(cmdname, "umount") == 0) - cmdname = "unmount"; + /* + * The 'umount' command is an alias for 'unmount' + */ + if (strcmp(cmdname, "umount") == 0) + cmdname = "unmount"; - /* - * The 'recv' command is an alias for 'receive' - */ - if (strcmp(cmdname, "recv") == 0) - cmdname = "receive"; + /* + * The 'recv' command is an alias for 'receive' + */ + if (strcmp(cmdname, "recv") == 0) + cmdname = "receive"; - /* - * Special case '-?' - */ - if ((strcmp(cmdname, "-?") == 0) || - (strcmp(cmdname, "--help") == 0)) - usage(B_TRUE); + /* + * The 'snap' command is an alias for 'snapshot' + */ + if (strcmp(cmdname, "snap") == 0) + cmdname = "snapshot"; - if ((g_zfs = libzfs_init()) == NULL) - return (1); + /* + * Special case '-?' + */ + if ((strcmp(cmdname, "-?") == 0) || + (strcmp(cmdname, "--help") == 0)) + usage(B_TRUE); + + if ((g_zfs = libzfs_init()) == NULL) + return (1); - zpool_set_history_str("zfs", argc, argv, history_str); - verify(zpool_stage_history(g_zfs, history_str) == 0); + zpool_set_history_str("zfs", argc, argv, history_str); + verify(zpool_stage_history(g_zfs, history_str) == 0); - libzfs_print_on_error(g_zfs, B_TRUE); + libzfs_print_on_error(g_zfs, B_TRUE); - /* - * Run the appropriate command. - */ - libzfs_mnttab_cache(g_zfs, B_TRUE); - if (find_command_idx(cmdname, &i) == 0) { - current_command = &command_table[i]; - ret = command_table[i].func(argc - 1, argv + 1); - } else if (strchr(cmdname, '=') != NULL) { - verify(find_command_idx("set", &i) == 0); - current_command = &command_table[i]; - ret = command_table[i].func(argc, argv); - } else { - (void) fprintf(stderr, gettext("unrecognized " - "command '%s'\n"), cmdname); - usage(B_FALSE); - ret = 1; - } - libzfs_mnttab_cache(g_zfs, B_FALSE); + /* + * Run the appropriate command. + */ + libzfs_mnttab_cache(g_zfs, B_FALSE); + if (find_command_idx(cmdname, &i) == 0) { + current_command = &command_table[i]; + ret = command_table[i].func(argc - 1, argv + 1); + } else if (strchr(cmdname, '=') != NULL) { + verify(find_command_idx("set", &i) == 0); + current_command = &command_table[i]; + ret = command_table[i].func(argc, argv); + } else { + (void) fprintf(stderr, gettext("unrecognized " + "command '%s'\n"), cmdname); + usage(B_FALSE); + ret = 1; } + libzfs_fini(g_zfs); (void) fclose(mnttab_file); - libzfs_fini(g_zfs); - /* * The 'ZFS_ABORT' environment variable causes us to dump core on exit * for the purposes of running ::findleaks.