The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/contrib/openzfs/cmd/zfs/zfs_main.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*
    2  * CDDL HEADER START
    3  *
    4  * The contents of this file are subject to the terms of the
    5  * Common Development and Distribution License (the "License").
    6  * You may not use this file except in compliance with the License.
    7  *
    8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
    9  * or https://opensource.org/licenses/CDDL-1.0.
   10  * See the License for the specific language governing permissions
   11  * and limitations under the License.
   12  *
   13  * When distributing Covered Code, include this CDDL HEADER in each
   14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
   15  * If applicable, add the following below this CDDL HEADER, with the
   16  * fields enclosed by brackets "[]" replaced with your own identifying
   17  * information: Portions Copyright [yyyy] [name of copyright owner]
   18  *
   19  * CDDL HEADER END
   20  */
   21 
   22 /*
   23  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
   24  * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
   25  * Copyright 2012 Milan Jurik. All rights reserved.
   26  * Copyright (c) 2012, Joyent, Inc. All rights reserved.
   27  * Copyright (c) 2013 Steven Hartland.  All rights reserved.
   28  * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>.
   29  * Copyright 2016 Nexenta Systems, Inc.
   30  * Copyright (c) 2019 Datto Inc.
   31  * Copyright (c) 2019, loli10K <ezomori.nozomu@gmail.com>
   32  * Copyright 2019 Joyent, Inc.
   33  * Copyright (c) 2019, 2020 by Christian Schwarz. All rights reserved.
   34  */
   35 
   36 #include <assert.h>
   37 #include <ctype.h>
   38 #include <sys/debug.h>
   39 #include <errno.h>
   40 #include <getopt.h>
   41 #include <libgen.h>
   42 #include <libintl.h>
   43 #include <libuutil.h>
   44 #include <libnvpair.h>
   45 #include <locale.h>
   46 #include <stddef.h>
   47 #include <stdio.h>
   48 #include <stdlib.h>
   49 #include <string.h>
   50 #include <unistd.h>
   51 #include <fcntl.h>
   52 #include <zone.h>
   53 #include <grp.h>
   54 #include <pwd.h>
   55 #include <umem.h>
   56 #include <pthread.h>
   57 #include <signal.h>
   58 #include <sys/list.h>
   59 #include <sys/mkdev.h>
   60 #include <sys/mntent.h>
   61 #include <sys/mnttab.h>
   62 #include <sys/mount.h>
   63 #include <sys/stat.h>
   64 #include <sys/fs/zfs.h>
   65 #include <sys/systeminfo.h>
   66 #include <sys/types.h>
   67 #include <time.h>
   68 #include <sys/zfs_project.h>
   69 
   70 #include <libzfs.h>
   71 #include <libzfs_core.h>
   72 #include <zfs_prop.h>
   73 #include <zfs_deleg.h>
   74 #include <libzutil.h>
   75 #ifdef HAVE_IDMAP
   76 #include <aclutils.h>
   77 #include <directory.h>
   78 #endif /* HAVE_IDMAP */
   79 
   80 #include "zfs_iter.h"
   81 #include "zfs_util.h"
   82 #include "zfs_comutil.h"
   83 #include "zfs_projectutil.h"
   84 
   85 libzfs_handle_t *g_zfs;
   86 
   87 static char history_str[HIS_MAX_RECORD_LEN];
   88 static boolean_t log_history = B_TRUE;
   89 
   90 static int zfs_do_clone(int argc, char **argv);
   91 static int zfs_do_create(int argc, char **argv);
   92 static int zfs_do_destroy(int argc, char **argv);
   93 static int zfs_do_get(int argc, char **argv);
   94 static int zfs_do_inherit(int argc, char **argv);
   95 static int zfs_do_list(int argc, char **argv);
   96 static int zfs_do_mount(int argc, char **argv);
   97 static int zfs_do_rename(int argc, char **argv);
   98 static int zfs_do_rollback(int argc, char **argv);
   99 static int zfs_do_set(int argc, char **argv);
  100 static int zfs_do_upgrade(int argc, char **argv);
  101 static int zfs_do_snapshot(int argc, char **argv);
  102 static int zfs_do_unmount(int argc, char **argv);
  103 static int zfs_do_share(int argc, char **argv);
  104 static int zfs_do_unshare(int argc, char **argv);
  105 static int zfs_do_send(int argc, char **argv);
  106 static int zfs_do_receive(int argc, char **argv);
  107 static int zfs_do_promote(int argc, char **argv);
  108 static int zfs_do_userspace(int argc, char **argv);
  109 static int zfs_do_allow(int argc, char **argv);
  110 static int zfs_do_unallow(int argc, char **argv);
  111 static int zfs_do_hold(int argc, char **argv);
  112 static int zfs_do_holds(int argc, char **argv);
  113 static int zfs_do_release(int argc, char **argv);
  114 static int zfs_do_diff(int argc, char **argv);
  115 static int zfs_do_bookmark(int argc, char **argv);
  116 static int zfs_do_channel_program(int argc, char **argv);
  117 static int zfs_do_load_key(int argc, char **argv);
  118 static int zfs_do_unload_key(int argc, char **argv);
  119 static int zfs_do_change_key(int argc, char **argv);
  120 static int zfs_do_project(int argc, char **argv);
  121 static int zfs_do_version(int argc, char **argv);
  122 static int zfs_do_redact(int argc, char **argv);
  123 static int zfs_do_wait(int argc, char **argv);
  124 
  125 #ifdef __FreeBSD__
  126 static int zfs_do_jail(int argc, char **argv);
  127 static int zfs_do_unjail(int argc, char **argv);
  128 #endif
  129 
  130 #ifdef __linux__
  131 static int zfs_do_zone(int argc, char **argv);
  132 static int zfs_do_unzone(int argc, char **argv);
  133 #endif
  134 
  135 /*
  136  * Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
  137  */
  138 
  139 #ifdef DEBUG
  140 const char *
  141 _umem_debug_init(void)
  142 {
  143         return ("default,verbose"); /* $UMEM_DEBUG setting */
  144 }
  145 
  146 const char *
  147 _umem_logging_init(void)
  148 {
  149         return ("fail,contents"); /* $UMEM_LOGGING setting */
  150 }
  151 #endif
  152 
  153 typedef enum {
  154         HELP_CLONE,
  155         HELP_CREATE,
  156         HELP_DESTROY,
  157         HELP_GET,
  158         HELP_INHERIT,
  159         HELP_UPGRADE,
  160         HELP_LIST,
  161         HELP_MOUNT,
  162         HELP_PROMOTE,
  163         HELP_RECEIVE,
  164         HELP_RENAME,
  165         HELP_ROLLBACK,
  166         HELP_SEND,
  167         HELP_SET,
  168         HELP_SHARE,
  169         HELP_SNAPSHOT,
  170         HELP_UNMOUNT,
  171         HELP_UNSHARE,
  172         HELP_ALLOW,
  173         HELP_UNALLOW,
  174         HELP_USERSPACE,
  175         HELP_GROUPSPACE,
  176         HELP_PROJECTSPACE,
  177         HELP_PROJECT,
  178         HELP_HOLD,
  179         HELP_HOLDS,
  180         HELP_RELEASE,
  181         HELP_DIFF,
  182         HELP_BOOKMARK,
  183         HELP_CHANNEL_PROGRAM,
  184         HELP_LOAD_KEY,
  185         HELP_UNLOAD_KEY,
  186         HELP_CHANGE_KEY,
  187         HELP_VERSION,
  188         HELP_REDACT,
  189         HELP_JAIL,
  190         HELP_UNJAIL,
  191         HELP_WAIT,
  192         HELP_ZONE,
  193         HELP_UNZONE,
  194 } zfs_help_t;
  195 
  196 typedef struct zfs_command {
  197         const char      *name;
  198         int             (*func)(int argc, char **argv);
  199         zfs_help_t      usage;
  200 } zfs_command_t;
  201 
  202 /*
  203  * Master command table.  Each ZFS command has a name, associated function, and
  204  * usage message.  The usage messages need to be internationalized, so we have
  205  * to have a function to return the usage message based on a command index.
  206  *
  207  * These commands are organized according to how they are displayed in the usage
  208  * message.  An empty command (one with a NULL name) indicates an empty line in
  209  * the generic usage message.
  210  */
  211 static zfs_command_t command_table[] = {
  212         { "version",    zfs_do_version,         HELP_VERSION            },
  213         { NULL },
  214         { "create",     zfs_do_create,          HELP_CREATE             },
  215         { "destroy",    zfs_do_destroy,         HELP_DESTROY            },
  216         { NULL },
  217         { "snapshot",   zfs_do_snapshot,        HELP_SNAPSHOT           },
  218         { "rollback",   zfs_do_rollback,        HELP_ROLLBACK           },
  219         { "clone",      zfs_do_clone,           HELP_CLONE              },
  220         { "promote",    zfs_do_promote,         HELP_PROMOTE            },
  221         { "rename",     zfs_do_rename,          HELP_RENAME             },
  222         { "bookmark",   zfs_do_bookmark,        HELP_BOOKMARK           },
  223         { "program",    zfs_do_channel_program, HELP_CHANNEL_PROGRAM    },
  224         { NULL },
  225         { "list",       zfs_do_list,            HELP_LIST               },
  226         { NULL },
  227         { "set",        zfs_do_set,             HELP_SET                },
  228         { "get",        zfs_do_get,             HELP_GET                },
  229         { "inherit",    zfs_do_inherit,         HELP_INHERIT            },
  230         { "upgrade",    zfs_do_upgrade,         HELP_UPGRADE            },
  231         { NULL },
  232         { "userspace",  zfs_do_userspace,       HELP_USERSPACE          },
  233         { "groupspace", zfs_do_userspace,       HELP_GROUPSPACE         },
  234         { "projectspace", zfs_do_userspace,     HELP_PROJECTSPACE       },
  235         { NULL },
  236         { "project",    zfs_do_project,         HELP_PROJECT            },
  237         { NULL },
  238         { "mount",      zfs_do_mount,           HELP_MOUNT              },
  239         { "unmount",    zfs_do_unmount,         HELP_UNMOUNT            },
  240         { "share",      zfs_do_share,           HELP_SHARE              },
  241         { "unshare",    zfs_do_unshare,         HELP_UNSHARE            },
  242         { NULL },
  243         { "send",       zfs_do_send,            HELP_SEND               },
  244         { "receive",    zfs_do_receive,         HELP_RECEIVE            },
  245         { NULL },
  246         { "allow",      zfs_do_allow,           HELP_ALLOW              },
  247         { NULL },
  248         { "unallow",    zfs_do_unallow,         HELP_UNALLOW            },
  249         { NULL },
  250         { "hold",       zfs_do_hold,            HELP_HOLD               },
  251         { "holds",      zfs_do_holds,           HELP_HOLDS              },
  252         { "release",    zfs_do_release,         HELP_RELEASE            },
  253         { "diff",       zfs_do_diff,            HELP_DIFF               },
  254         { "load-key",   zfs_do_load_key,        HELP_LOAD_KEY           },
  255         { "unload-key", zfs_do_unload_key,      HELP_UNLOAD_KEY         },
  256         { "change-key", zfs_do_change_key,      HELP_CHANGE_KEY         },
  257         { "redact",     zfs_do_redact,          HELP_REDACT             },
  258         { "wait",       zfs_do_wait,            HELP_WAIT               },
  259 
  260 #ifdef __FreeBSD__
  261         { "jail",       zfs_do_jail,            HELP_JAIL               },
  262         { "unjail",     zfs_do_unjail,          HELP_UNJAIL             },
  263 #endif
  264 
  265 #ifdef __linux__
  266         { "zone",       zfs_do_zone,            HELP_ZONE               },
  267         { "unzone",     zfs_do_unzone,          HELP_UNZONE             },
  268 #endif
  269 };
  270 
  271 #define NCOMMAND        (sizeof (command_table) / sizeof (command_table[0]))
  272 
  273 zfs_command_t *current_command;
  274 
  275 static const char *
  276 get_usage(zfs_help_t idx)
  277 {
  278         switch (idx) {
  279         case HELP_CLONE:
  280                 return (gettext("\tclone [-p] [-o property=value] ... "
  281                     "<snapshot> <filesystem|volume>\n"));
  282         case HELP_CREATE:
  283                 return (gettext("\tcreate [-Pnpuv] [-o property=value] ... "
  284                     "<filesystem>\n"
  285                     "\tcreate [-Pnpsv] [-b blocksize] [-o property=value] ... "
  286                     "-V <size> <volume>\n"));
  287         case HELP_DESTROY:
  288                 return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
  289                     "\tdestroy [-dnpRrv] "
  290                     "<filesystem|volume>@<snap>[%<snap>][,...]\n"
  291                     "\tdestroy <filesystem|volume>#<bookmark>\n"));
  292         case HELP_GET:
  293                 return (gettext("\tget [-rHp] [-d max] "
  294                     "[-o \"all\" | field[,...]]\n"
  295                     "\t    [-t type[,...]] [-s source[,...]]\n"
  296                     "\t    <\"all\" | property[,...]> "
  297                     "[filesystem|volume|snapshot|bookmark] ...\n"));
  298         case HELP_INHERIT:
  299                 return (gettext("\tinherit [-rS] <property> "
  300                     "<filesystem|volume|snapshot> ...\n"));
  301         case HELP_UPGRADE:
  302                 return (gettext("\tupgrade [-v]\n"
  303                     "\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
  304         case HELP_LIST:
  305                 return (gettext("\tlist [-Hp] [-r|-d max] [-o property[,...]] "
  306                     "[-s property]...\n\t    [-S property]... [-t type[,...]] "
  307                     "[filesystem|volume|snapshot] ...\n"));
  308         case HELP_MOUNT:
  309                 return (gettext("\tmount\n"
  310                     "\tmount [-flvO] [-o opts] <-a | filesystem>\n"));
  311         case HELP_PROMOTE:
  312                 return (gettext("\tpromote <clone-filesystem>\n"));
  313         case HELP_RECEIVE:
  314                 return (gettext("\treceive [-vMnsFhu] "
  315                     "[-o <property>=<value>] ... [-x <property>] ...\n"
  316                     "\t    <filesystem|volume|snapshot>\n"
  317                     "\treceive [-vMnsFhu] [-o <property>=<value>] ... "
  318                     "[-x <property>] ... \n"
  319                     "\t    [-d | -e] <filesystem>\n"
  320                     "\treceive -A <filesystem|volume>\n"));
  321         case HELP_RENAME:
  322                 return (gettext("\trename [-f] <filesystem|volume|snapshot> "
  323                     "<filesystem|volume|snapshot>\n"
  324                     "\trename -p [-f] <filesystem|volume> <filesystem|volume>\n"
  325                     "\trename -u [-f] <filesystem> <filesystem>\n"
  326                     "\trename -r <snapshot> <snapshot>\n"));
  327         case HELP_ROLLBACK:
  328                 return (gettext("\trollback [-rRf] <snapshot>\n"));
  329         case HELP_SEND:
  330                 return (gettext("\tsend [-DLPbcehnpsVvw] "
  331                     "[-i|-I snapshot]\n"
  332                     "\t     [-R [-X dataset[,dataset]...]]     <snapshot>\n"
  333                     "\tsend [-DnVvPLecw] [-i snapshot|bookmark] "
  334                     "<filesystem|volume|snapshot>\n"
  335                     "\tsend [-DnPpVvLec] [-i bookmark|snapshot] "
  336                     "--redact <bookmark> <snapshot>\n"
  337                     "\tsend [-nVvPe] -t <receive_resume_token>\n"
  338                     "\tsend [-PnVv] --saved filesystem\n"));
  339         case HELP_SET:
  340                 return (gettext("\tset <property=value> ... "
  341                     "<filesystem|volume|snapshot> ...\n"));
  342         case HELP_SHARE:
  343                 return (gettext("\tshare [-l] <-a [nfs|smb] | filesystem>\n"));
  344         case HELP_SNAPSHOT:
  345                 return (gettext("\tsnapshot [-r] [-o property=value] ... "
  346                     "<filesystem|volume>@<snap> ...\n"));
  347         case HELP_UNMOUNT:
  348                 return (gettext("\tunmount [-fu] "
  349                     "<-a | filesystem|mountpoint>\n"));
  350         case HELP_UNSHARE:
  351                 return (gettext("\tunshare "
  352                     "<-a [nfs|smb] | filesystem|mountpoint>\n"));
  353         case HELP_ALLOW:
  354                 return (gettext("\tallow <filesystem|volume>\n"
  355                     "\tallow [-ldug] "
  356                     "<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n"
  357                     "\t    <filesystem|volume>\n"
  358                     "\tallow [-ld] -e <perm|@setname>[,...] "
  359                     "<filesystem|volume>\n"
  360                     "\tallow -c <perm|@setname>[,...] <filesystem|volume>\n"
  361                     "\tallow -s @setname <perm|@setname>[,...] "
  362                     "<filesystem|volume>\n"));
  363         case HELP_UNALLOW:
  364                 return (gettext("\tunallow [-rldug] "
  365                     "<\"everyone\"|user|group>[,...]\n"
  366                     "\t    [<perm|@setname>[,...]] <filesystem|volume>\n"
  367                     "\tunallow [-rld] -e [<perm|@setname>[,...]] "
  368                     "<filesystem|volume>\n"
  369                     "\tunallow [-r] -c [<perm|@setname>[,...]] "
  370                     "<filesystem|volume>\n"
  371                     "\tunallow [-r] -s @setname [<perm|@setname>[,...]] "
  372                     "<filesystem|volume>\n"));
  373         case HELP_USERSPACE:
  374                 return (gettext("\tuserspace [-Hinp] [-o field[,...]] "
  375                     "[-s field] ...\n"
  376                     "\t    [-S field] ... [-t type[,...]] "
  377                     "<filesystem|snapshot|path>\n"));
  378         case HELP_GROUPSPACE:
  379                 return (gettext("\tgroupspace [-Hinp] [-o field[,...]] "
  380                     "[-s field] ...\n"
  381                     "\t    [-S field] ... [-t type[,...]] "
  382                     "<filesystem|snapshot|path>\n"));
  383         case HELP_PROJECTSPACE:
  384                 return (gettext("\tprojectspace [-Hp] [-o field[,...]] "
  385                     "[-s field] ... \n"
  386                     "\t    [-S field] ... <filesystem|snapshot|path>\n"));
  387         case HELP_PROJECT:
  388                 return (gettext("\tproject [-d|-r] <directory|file ...>\n"
  389                     "\tproject -c [-0] [-d|-r] [-p id] <directory|file ...>\n"
  390                     "\tproject -C [-k] [-r] <directory ...>\n"
  391                     "\tproject [-p id] [-r] [-s] <directory ...>\n"));
  392         case HELP_HOLD:
  393                 return (gettext("\thold [-r] <tag> <snapshot> ...\n"));
  394         case HELP_HOLDS:
  395                 return (gettext("\tholds [-rHp] <snapshot> ...\n"));
  396         case HELP_RELEASE:
  397                 return (gettext("\trelease [-r] <tag> <snapshot> ...\n"));
  398         case HELP_DIFF:
  399                 return (gettext("\tdiff [-FHth] <snapshot> "
  400                     "[snapshot|filesystem]\n"));
  401         case HELP_BOOKMARK:
  402                 return (gettext("\tbookmark <snapshot|bookmark> "
  403                     "<newbookmark>\n"));
  404         case HELP_CHANNEL_PROGRAM:
  405                 return (gettext("\tprogram [-jn] [-t <instruction limit>] "
  406                     "[-m <memory limit (b)>]\n"
  407                     "\t    <pool> <program file> [lua args...]\n"));
  408         case HELP_LOAD_KEY:
  409                 return (gettext("\tload-key [-rn] [-L <keylocation>] "
  410                     "<-a | filesystem|volume>\n"));
  411         case HELP_UNLOAD_KEY:
  412                 return (gettext("\tunload-key [-r] "
  413                     "<-a | filesystem|volume>\n"));
  414         case HELP_CHANGE_KEY:
  415                 return (gettext("\tchange-key [-l] [-o keyformat=<value>]\n"
  416                     "\t    [-o keylocation=<value>] [-o pbkdf2iters=<value>]\n"
  417                     "\t    <filesystem|volume>\n"
  418                     "\tchange-key -i [-l] <filesystem|volume>\n"));
  419         case HELP_VERSION:
  420                 return (gettext("\tversion\n"));
  421         case HELP_REDACT:
  422                 return (gettext("\tredact <snapshot> <bookmark> "
  423                     "<redaction_snapshot> ...\n"));
  424         case HELP_JAIL:
  425                 return (gettext("\tjail <jailid|jailname> <filesystem>\n"));
  426         case HELP_UNJAIL:
  427                 return (gettext("\tunjail <jailid|jailname> <filesystem>\n"));
  428         case HELP_WAIT:
  429                 return (gettext("\twait [-t <activity>] <filesystem>\n"));
  430         case HELP_ZONE:
  431                 return (gettext("\tzone <nsfile> <filesystem>\n"));
  432         case HELP_UNZONE:
  433                 return (gettext("\tunzone <nsfile> <filesystem>\n"));
  434         default:
  435                 __builtin_unreachable();
  436         }
  437 }
  438 
  439 void
  440 nomem(void)
  441 {
  442         (void) fprintf(stderr, gettext("internal error: out of memory\n"));
  443         exit(1);
  444 }
  445 
  446 /*
  447  * Utility function to guarantee malloc() success.
  448  */
  449 
  450 void *
  451 safe_malloc(size_t size)
  452 {
  453         void *data;
  454 
  455         if ((data = calloc(1, size)) == NULL)
  456                 nomem();
  457 
  458         return (data);
  459 }
  460 
  461 static void *
  462 safe_realloc(void *data, size_t size)
  463 {
  464         void *newp;
  465         if ((newp = realloc(data, size)) == NULL) {
  466                 free(data);
  467                 nomem();
  468         }
  469 
  470         return (newp);
  471 }
  472 
  473 static char *
  474 safe_strdup(const char *str)
  475 {
  476         char *dupstr = strdup(str);
  477 
  478         if (dupstr == NULL)
  479                 nomem();
  480 
  481         return (dupstr);
  482 }
  483 
  484 /*
  485  * Callback routine that will print out information for each of
  486  * the properties.
  487  */
  488 static int
  489 usage_prop_cb(int prop, void *cb)
  490 {
  491         FILE *fp = cb;
  492 
  493         (void) fprintf(fp, "\t%-15s ", zfs_prop_to_name(prop));
  494 
  495         if (zfs_prop_readonly(prop))
  496                 (void) fprintf(fp, " NO    ");
  497         else
  498                 (void) fprintf(fp, "YES    ");
  499 
  500         if (zfs_prop_inheritable(prop))
  501                 (void) fprintf(fp, "  YES   ");
  502         else
  503                 (void) fprintf(fp, "   NO   ");
  504 
  505         (void) fprintf(fp, "%s\n", zfs_prop_values(prop) ?: "-");
  506 
  507         return (ZPROP_CONT);
  508 }
  509 
  510 /*
  511  * Display usage message.  If we're inside a command, display only the usage for
  512  * that command.  Otherwise, iterate over the entire command table and display
  513  * a complete usage message.
  514  */
  515 static __attribute__((noreturn)) void
  516 usage(boolean_t requested)
  517 {
  518         int i;
  519         boolean_t show_properties = B_FALSE;
  520         FILE *fp = requested ? stdout : stderr;
  521 
  522         if (current_command == NULL) {
  523 
  524                 (void) fprintf(fp, gettext("usage: zfs command args ...\n"));
  525                 (void) fprintf(fp,
  526                     gettext("where 'command' is one of the following:\n\n"));
  527 
  528                 for (i = 0; i < NCOMMAND; i++) {
  529                         if (command_table[i].name == NULL)
  530                                 (void) fprintf(fp, "\n");
  531                         else
  532                                 (void) fprintf(fp, "%s",
  533                                     get_usage(command_table[i].usage));
  534                 }
  535 
  536                 (void) fprintf(fp, gettext("\nEach dataset is of the form: "
  537                     "pool/[dataset/]*dataset[@name]\n"));
  538         } else {
  539                 (void) fprintf(fp, gettext("usage:\n"));
  540                 (void) fprintf(fp, "%s", get_usage(current_command->usage));
  541         }
  542 
  543         if (current_command != NULL &&
  544             (strcmp(current_command->name, "set") == 0 ||
  545             strcmp(current_command->name, "get") == 0 ||
  546             strcmp(current_command->name, "inherit") == 0 ||
  547             strcmp(current_command->name, "list") == 0))
  548                 show_properties = B_TRUE;
  549 
  550         if (show_properties) {
  551                 (void) fprintf(fp, "%s",
  552                     gettext("\nThe following properties are supported:\n"));
  553 
  554                 (void) fprintf(fp, "\n\t%-14s %s  %s   %s\n\n",
  555                     "PROPERTY", "EDIT", "INHERIT", "VALUES");
  556 
  557                 /* Iterate over all properties */
  558                 (void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE,
  559                     ZFS_TYPE_DATASET);
  560 
  561                 (void) fprintf(fp, "\t%-15s ", "userused@...");
  562                 (void) fprintf(fp, " NO       NO   <size>\n");
  563                 (void) fprintf(fp, "\t%-15s ", "groupused@...");
  564                 (void) fprintf(fp, " NO       NO   <size>\n");
  565                 (void) fprintf(fp, "\t%-15s ", "projectused@...");
  566                 (void) fprintf(fp, " NO       NO   <size>\n");
  567                 (void) fprintf(fp, "\t%-15s ", "userobjused@...");
  568                 (void) fprintf(fp, " NO       NO   <size>\n");
  569                 (void) fprintf(fp, "\t%-15s ", "groupobjused@...");
  570                 (void) fprintf(fp, " NO       NO   <size>\n");
  571                 (void) fprintf(fp, "\t%-15s ", "projectobjused@...");
  572                 (void) fprintf(fp, " NO       NO   <size>\n");
  573                 (void) fprintf(fp, "\t%-15s ", "userquota@...");
  574                 (void) fprintf(fp, "YES       NO   <size> | none\n");
  575                 (void) fprintf(fp, "\t%-15s ", "groupquota@...");
  576                 (void) fprintf(fp, "YES       NO   <size> | none\n");
  577                 (void) fprintf(fp, "\t%-15s ", "projectquota@...");
  578                 (void) fprintf(fp, "YES       NO   <size> | none\n");
  579                 (void) fprintf(fp, "\t%-15s ", "userobjquota@...");
  580                 (void) fprintf(fp, "YES       NO   <size> | none\n");
  581                 (void) fprintf(fp, "\t%-15s ", "groupobjquota@...");
  582                 (void) fprintf(fp, "YES       NO   <size> | none\n");
  583                 (void) fprintf(fp, "\t%-15s ", "projectobjquota@...");
  584                 (void) fprintf(fp, "YES       NO   <size> | none\n");
  585                 (void) fprintf(fp, "\t%-15s ", "written@<snap>");
  586                 (void) fprintf(fp, " NO       NO   <size>\n");
  587                 (void) fprintf(fp, "\t%-15s ", "written#<bookmark>");
  588                 (void) fprintf(fp, " NO       NO   <size>\n");
  589 
  590                 (void) fprintf(fp, gettext("\nSizes are specified in bytes "
  591                     "with standard units such as K, M, G, etc.\n"));
  592                 (void) fprintf(fp, "%s", gettext("\nUser-defined properties "
  593                     "can be specified by using a name containing a colon "
  594                     "(:).\n"));
  595                 (void) fprintf(fp, gettext("\nThe {user|group|project}"
  596                     "[obj]{used|quota}@ properties must be appended with\n"
  597                     "a user|group|project specifier of one of these forms:\n"
  598                     "    POSIX name      (eg: \"matt\")\n"
  599                     "    POSIX id        (eg: \"126829\")\n"
  600                     "    SMB name@domain (eg: \"matt@sun\")\n"
  601                     "    SMB SID         (eg: \"S-1-234-567-89\")\n"));
  602         } else {
  603                 (void) fprintf(fp,
  604                     gettext("\nFor the property list, run: %s\n"),
  605                     "zfs set|get");
  606                 (void) fprintf(fp,
  607                     gettext("\nFor the delegated permission list, run: %s\n"),
  608                     "zfs allow|unallow");
  609         }
  610 
  611         /*
  612          * See comments at end of main().
  613          */
  614         if (getenv("ZFS_ABORT") != NULL) {
  615                 (void) printf("dumping core by request\n");
  616                 abort();
  617         }
  618 
  619         exit(requested ? 0 : 2);
  620 }
  621 
  622 /*
  623  * Take a property=value argument string and add it to the given nvlist.
  624  * Modifies the argument inplace.
  625  */
  626 static boolean_t
  627 parseprop(nvlist_t *props, char *propname)
  628 {
  629         char *propval;
  630 
  631         if ((propval = strchr(propname, '=')) == NULL) {
  632                 (void) fprintf(stderr, gettext("missing "
  633                     "'=' for property=value argument\n"));
  634                 return (B_FALSE);
  635         }
  636         *propval = '\0';
  637         propval++;
  638         if (nvlist_exists(props, propname)) {
  639                 (void) fprintf(stderr, gettext("property '%s' "
  640                     "specified multiple times\n"), propname);
  641                 return (B_FALSE);
  642         }
  643         if (nvlist_add_string(props, propname, propval) != 0)
  644                 nomem();
  645         return (B_TRUE);
  646 }
  647 
  648 /*
  649  * Take a property name argument and add it to the given nvlist.
  650  * Modifies the argument inplace.
  651  */
  652 static boolean_t
  653 parsepropname(nvlist_t *props, char *propname)
  654 {
  655         if (strchr(propname, '=') != NULL) {
  656                 (void) fprintf(stderr, gettext("invalid character "
  657                     "'=' in property argument\n"));
  658                 return (B_FALSE);
  659         }
  660         if (nvlist_exists(props, propname)) {
  661                 (void) fprintf(stderr, gettext("property '%s' "
  662                     "specified multiple times\n"), propname);
  663                 return (B_FALSE);
  664         }
  665         if (nvlist_add_boolean(props, propname) != 0)
  666                 nomem();
  667         return (B_TRUE);
  668 }
  669 
  670 static int
  671 parse_depth(char *opt, int *flags)
  672 {
  673         char *tmp;
  674         int depth;
  675 
  676         depth = (int)strtol(opt, &tmp, 0);
  677         if (*tmp) {
  678                 (void) fprintf(stderr,
  679                     gettext("%s is not an integer\n"), optarg);
  680                 usage(B_FALSE);
  681         }
  682         if (depth < 0) {
  683                 (void) fprintf(stderr,
  684                     gettext("Depth can not be negative.\n"));
  685                 usage(B_FALSE);
  686         }
  687         *flags |= (ZFS_ITER_DEPTH_LIMIT|ZFS_ITER_RECURSE);
  688         return (depth);
  689 }
  690 
  691 #define PROGRESS_DELAY 2                /* seconds */
  692 
  693 static const char *pt_reverse =
  694         "\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";
  695 static time_t pt_begin;
  696 static char *pt_header = NULL;
  697 static boolean_t pt_shown;
  698 
  699 static void
  700 start_progress_timer(void)
  701 {
  702         pt_begin = time(NULL) + PROGRESS_DELAY;
  703         pt_shown = B_FALSE;
  704 }
  705 
  706 static void
  707 set_progress_header(const char *header)
  708 {
  709         assert(pt_header == NULL);
  710         pt_header = safe_strdup(header);
  711         if (pt_shown) {
  712                 (void) printf("%s: ", header);
  713                 (void) fflush(stdout);
  714         }
  715 }
  716 
  717 static void
  718 update_progress(const char *update)
  719 {
  720         if (!pt_shown && time(NULL) > pt_begin) {
  721                 int len = strlen(update);
  722 
  723                 (void) printf("%s: %s%*.*s", pt_header, update, len, len,
  724                     pt_reverse);
  725                 (void) fflush(stdout);
  726                 pt_shown = B_TRUE;
  727         } else if (pt_shown) {
  728                 int len = strlen(update);
  729 
  730                 (void) printf("%s%*.*s", update, len, len, pt_reverse);
  731                 (void) fflush(stdout);
  732         }
  733 }
  734 
  735 static void
  736 finish_progress(const char *done)
  737 {
  738         if (pt_shown) {
  739                 (void) puts(done);
  740                 (void) fflush(stdout);
  741         }
  742         free(pt_header);
  743         pt_header = NULL;
  744 }
  745 
  746 static int
  747 zfs_mount_and_share(libzfs_handle_t *hdl, const char *dataset, zfs_type_t type)
  748 {
  749         zfs_handle_t *zhp = NULL;
  750         int ret = 0;
  751 
  752         zhp = zfs_open(hdl, dataset, type);
  753         if (zhp == NULL)
  754                 return (1);
  755 
  756         /*
  757          * Volumes may neither be mounted or shared.  Potentially in the
  758          * future filesystems detected on these volumes could be mounted.
  759          */
  760         if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
  761                 zfs_close(zhp);
  762                 return (0);
  763         }
  764 
  765         /*
  766          * Mount and/or share the new filesystem as appropriate.  We provide a
  767          * verbose error message to let the user know that their filesystem was
  768          * in fact created, even if we failed to mount or share it.
  769          *
  770          * If the user doesn't want the dataset automatically mounted, then
  771          * skip the mount/share step
  772          */
  773         if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type, B_FALSE) &&
  774             zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON) {
  775                 if (zfs_mount_delegation_check()) {
  776                         (void) fprintf(stderr, gettext("filesystem "
  777                             "successfully created, but it may only be "
  778                             "mounted by root\n"));
  779                         ret = 1;
  780                 } else if (zfs_mount(zhp, NULL, 0) != 0) {
  781                         (void) fprintf(stderr, gettext("filesystem "
  782                             "successfully created, but not mounted\n"));
  783                         ret = 1;
  784                 } else if (zfs_share(zhp, NULL) != 0) {
  785                         (void) fprintf(stderr, gettext("filesystem "
  786                             "successfully created, but not shared\n"));
  787                         ret = 1;
  788                 }
  789                 zfs_commit_shares(NULL);
  790         }
  791 
  792         zfs_close(zhp);
  793 
  794         return (ret);
  795 }
  796 
  797 /*
  798  * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
  799  *
  800  * Given an existing dataset, create a writable copy whose initial contents
  801  * are the same as the source.  The newly created dataset maintains a
  802  * dependency on the original; the original cannot be destroyed so long as
  803  * the clone exists.
  804  *
  805  * The '-p' flag creates all the non-existing ancestors of the target first.
  806  */
  807 static int
  808 zfs_do_clone(int argc, char **argv)
  809 {
  810         zfs_handle_t *zhp = NULL;
  811         boolean_t parents = B_FALSE;
  812         nvlist_t *props;
  813         int ret = 0;
  814         int c;
  815 
  816         if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
  817                 nomem();
  818 
  819         /* check options */
  820         while ((c = getopt(argc, argv, "o:p")) != -1) {
  821                 switch (c) {
  822                 case 'o':
  823                         if (!parseprop(props, optarg)) {
  824                                 nvlist_free(props);
  825                                 return (1);
  826                         }
  827                         break;
  828                 case 'p':
  829                         parents = B_TRUE;
  830                         break;
  831                 case '?':
  832                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
  833                             optopt);
  834                         goto usage;
  835                 }
  836         }
  837 
  838         argc -= optind;
  839         argv += optind;
  840 
  841         /* check number of arguments */
  842         if (argc < 1) {
  843                 (void) fprintf(stderr, gettext("missing source dataset "
  844                     "argument\n"));
  845                 goto usage;
  846         }
  847         if (argc < 2) {
  848                 (void) fprintf(stderr, gettext("missing target dataset "
  849                     "argument\n"));
  850                 goto usage;
  851         }
  852         if (argc > 2) {
  853                 (void) fprintf(stderr, gettext("too many arguments\n"));
  854                 goto usage;
  855         }
  856 
  857         /* open the source dataset */
  858         if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) {
  859                 nvlist_free(props);
  860                 return (1);
  861         }
  862 
  863         if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM |
  864             ZFS_TYPE_VOLUME)) {
  865                 /*
  866                  * Now create the ancestors of the target dataset.  If the
  867                  * target already exists and '-p' option was used we should not
  868                  * complain.
  869                  */
  870                 if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM |
  871                     ZFS_TYPE_VOLUME)) {
  872                         zfs_close(zhp);
  873                         nvlist_free(props);
  874                         return (0);
  875                 }
  876                 if (zfs_create_ancestors(g_zfs, argv[1]) != 0) {
  877                         zfs_close(zhp);
  878                         nvlist_free(props);
  879                         return (1);
  880                 }
  881         }
  882 
  883         /* pass to libzfs */
  884         ret = zfs_clone(zhp, argv[1], props);
  885 
  886         /* create the mountpoint if necessary */
  887         if (ret == 0) {
  888                 if (log_history) {
  889                         (void) zpool_log_history(g_zfs, history_str);
  890                         log_history = B_FALSE;
  891                 }
  892 
  893                 ret = zfs_mount_and_share(g_zfs, argv[1], ZFS_TYPE_DATASET);
  894         }
  895 
  896         zfs_close(zhp);
  897         nvlist_free(props);
  898 
  899         return (!!ret);
  900 
  901 usage:
  902         ASSERT3P(zhp, ==, NULL);
  903         nvlist_free(props);
  904         usage(B_FALSE);
  905         return (-1);
  906 }
  907 
  908 /*
  909  * Return a default volblocksize for the pool which always uses more than
  910  * half of the data sectors.  This primarily applies to dRAID which always
  911  * writes full stripe widths.
  912  */
  913 static uint64_t
  914 default_volblocksize(zpool_handle_t *zhp, nvlist_t *props)
  915 {
  916         uint64_t volblocksize, asize = SPA_MINBLOCKSIZE;
  917         nvlist_t *tree, **vdevs;
  918         uint_t nvdevs;
  919 
  920         nvlist_t *config = zpool_get_config(zhp, NULL);
  921 
  922         if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &tree) != 0 ||
  923             nvlist_lookup_nvlist_array(tree, ZPOOL_CONFIG_CHILDREN,
  924             &vdevs, &nvdevs) != 0) {
  925                 return (ZVOL_DEFAULT_BLOCKSIZE);
  926         }
  927 
  928         for (int i = 0; i < nvdevs; i++) {
  929                 nvlist_t *nv = vdevs[i];
  930                 uint64_t ashift, ndata, nparity;
  931 
  932                 if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ASHIFT, &ashift) != 0)
  933                         continue;
  934 
  935                 if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_DRAID_NDATA,
  936                     &ndata) == 0) {
  937                         /* dRAID minimum allocation width */
  938                         asize = MAX(asize, ndata * (1ULL << ashift));
  939                 } else if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NPARITY,
  940                     &nparity) == 0) {
  941                         /* raidz minimum allocation width */
  942                         if (nparity == 1)
  943                                 asize = MAX(asize, 2 * (1ULL << ashift));
  944                         else
  945                                 asize = MAX(asize, 4 * (1ULL << ashift));
  946                 } else {
  947                         /* mirror or (non-redundant) leaf vdev */
  948                         asize = MAX(asize, 1ULL << ashift);
  949                 }
  950         }
  951 
  952         /*
  953          * Calculate the target volblocksize such that more than half
  954          * of the asize is used. The following table is for 4k sectors.
  955          *
  956          * n   asize   blksz  used  |   n   asize   blksz  used
  957          * -------------------------+---------------------------------
  958          * 1   4,096   8,192  100%  |   9  36,864  32,768   88%
  959          * 2   8,192   8,192  100%  |  10  40,960  32,768   80%
  960          * 3  12,288   8,192   66%  |  11  45,056  32,768   72%
  961          * 4  16,384  16,384  100%  |  12  49,152  32,768   66%
  962          * 5  20,480  16,384   80%  |  13  53,248  32,768   61%
  963          * 6  24,576  16,384   66%  |  14  57,344  32,768   57%
  964          * 7  28,672  16,384   57%  |  15  61,440  32,768   53%
  965          * 8  32,768  32,768  100%  |  16  65,536  65,636  100%
  966          *
  967          * This is primarily a concern for dRAID which always allocates
  968          * a full stripe width.  For dRAID the default stripe width is
  969          * n=8 in which case the volblocksize is set to 32k. Ignoring
  970          * compression there are no unused sectors.  This same reasoning
  971          * applies to raidz[2,3] so target 4 sectors to minimize waste.
  972          */
  973         uint64_t tgt_volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
  974         while (tgt_volblocksize * 2 <= asize)
  975                 tgt_volblocksize *= 2;
  976 
  977         const char *prop = zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE);
  978         if (nvlist_lookup_uint64(props, prop, &volblocksize) == 0) {
  979 
  980                 /* Issue a warning when a non-optimal size is requested. */
  981                 if (volblocksize < ZVOL_DEFAULT_BLOCKSIZE) {
  982                         (void) fprintf(stderr, gettext("Warning: "
  983                             "volblocksize (%llu) is less than the default "
  984                             "minimum block size (%llu).\nTo reduce wasted "
  985                             "space a volblocksize of %llu is recommended.\n"),
  986                             (u_longlong_t)volblocksize,
  987                             (u_longlong_t)ZVOL_DEFAULT_BLOCKSIZE,
  988                             (u_longlong_t)tgt_volblocksize);
  989                 } else if (volblocksize < tgt_volblocksize) {
  990                         (void) fprintf(stderr, gettext("Warning: "
  991                             "volblocksize (%llu) is much less than the "
  992                             "minimum allocation\nunit (%llu), which wastes "
  993                             "at least %llu%% of space. To reduce wasted "
  994                             "space,\nuse a larger volblocksize (%llu is "
  995                             "recommended), fewer dRAID data disks\n"
  996                             "per group, or smaller sector size (ashift).\n"),
  997                             (u_longlong_t)volblocksize, (u_longlong_t)asize,
  998                             (u_longlong_t)((100 * (asize - volblocksize)) /
  999                             asize), (u_longlong_t)tgt_volblocksize);
 1000                 }
 1001         } else {
 1002                 volblocksize = tgt_volblocksize;
 1003                 fnvlist_add_uint64(props, prop, volblocksize);
 1004         }
 1005 
 1006         return (volblocksize);
 1007 }
 1008 
 1009 /*
 1010  * zfs create [-Pnpv] [-o prop=value] ... fs
 1011  * zfs create [-Pnpsv] [-b blocksize] [-o prop=value] ... -V vol size
 1012  *
 1013  * Create a new dataset.  This command can be used to create filesystems
 1014  * and volumes.  Snapshot creation is handled by 'zfs snapshot'.
 1015  * For volumes, the user must specify a size to be used.
 1016  *
 1017  * The '-s' flag applies only to volumes, and indicates that we should not try
 1018  * to set the reservation for this volume.  By default we set a reservation
 1019  * equal to the size for any volume.  For pools with SPA_VERSION >=
 1020  * SPA_VERSION_REFRESERVATION, we set a refreservation instead.
 1021  *
 1022  * The '-p' flag creates all the non-existing ancestors of the target first.
 1023  *
 1024  * The '-n' flag is no-op (dry run) mode.  This will perform a user-space sanity
 1025  * check of arguments and properties, but does not check for permissions,
 1026  * available space, etc.
 1027  *
 1028  * The '-u' flag prevents the newly created file system from being mounted.
 1029  *
 1030  * The '-v' flag is for verbose output.
 1031  *
 1032  * The '-P' flag is used for parseable output.  It implies '-v'.
 1033  */
 1034 static int
 1035 zfs_do_create(int argc, char **argv)
 1036 {
 1037         zfs_type_t type = ZFS_TYPE_FILESYSTEM;
 1038         zpool_handle_t *zpool_handle = NULL;
 1039         nvlist_t *real_props = NULL;
 1040         uint64_t volsize = 0;
 1041         int c;
 1042         boolean_t noreserve = B_FALSE;
 1043         boolean_t bflag = B_FALSE;
 1044         boolean_t parents = B_FALSE;
 1045         boolean_t dryrun = B_FALSE;
 1046         boolean_t nomount = B_FALSE;
 1047         boolean_t verbose = B_FALSE;
 1048         boolean_t parseable = B_FALSE;
 1049         int ret = 1;
 1050         nvlist_t *props;
 1051         uint64_t intval;
 1052         char *strval;
 1053 
 1054         if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
 1055                 nomem();
 1056 
 1057         /* check options */
 1058         while ((c = getopt(argc, argv, ":PV:b:nso:puv")) != -1) {
 1059                 switch (c) {
 1060                 case 'V':
 1061                         type = ZFS_TYPE_VOLUME;
 1062                         if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
 1063                                 (void) fprintf(stderr, gettext("bad volume "
 1064                                     "size '%s': %s\n"), optarg,
 1065                                     libzfs_error_description(g_zfs));
 1066                                 goto error;
 1067                         }
 1068 
 1069                         if (nvlist_add_uint64(props,
 1070                             zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0)
 1071                                 nomem();
 1072                         volsize = intval;
 1073                         break;
 1074                 case 'P':
 1075                         verbose = B_TRUE;
 1076                         parseable = B_TRUE;
 1077                         break;
 1078                 case 'p':
 1079                         parents = B_TRUE;
 1080                         break;
 1081                 case 'b':
 1082                         bflag = B_TRUE;
 1083                         if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
 1084                                 (void) fprintf(stderr, gettext("bad volume "
 1085                                     "block size '%s': %s\n"), optarg,
 1086                                     libzfs_error_description(g_zfs));
 1087                                 goto error;
 1088                         }
 1089 
 1090                         if (nvlist_add_uint64(props,
 1091                             zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
 1092                             intval) != 0)
 1093                                 nomem();
 1094                         break;
 1095                 case 'n':
 1096                         dryrun = B_TRUE;
 1097                         break;
 1098                 case 'o':
 1099                         if (!parseprop(props, optarg))
 1100                                 goto error;
 1101                         break;
 1102                 case 's':
 1103                         noreserve = B_TRUE;
 1104                         break;
 1105                 case 'u':
 1106                         nomount = B_TRUE;
 1107                         break;
 1108                 case 'v':
 1109                         verbose = B_TRUE;
 1110                         break;
 1111                 case ':':
 1112                         (void) fprintf(stderr, gettext("missing size "
 1113                             "argument\n"));
 1114                         goto badusage;
 1115                 case '?':
 1116                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 1117                             optopt);
 1118                         goto badusage;
 1119                 }
 1120         }
 1121 
 1122         if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) {
 1123                 (void) fprintf(stderr, gettext("'-s' and '-b' can only be "
 1124                     "used when creating a volume\n"));
 1125                 goto badusage;
 1126         }
 1127         if (nomount && type != ZFS_TYPE_FILESYSTEM) {
 1128                 (void) fprintf(stderr, gettext("'-u' can only be "
 1129                     "used when creating a filesystem\n"));
 1130                 goto badusage;
 1131         }
 1132 
 1133         argc -= optind;
 1134         argv += optind;
 1135 
 1136         /* check number of arguments */
 1137         if (argc == 0) {
 1138                 (void) fprintf(stderr, gettext("missing %s argument\n"),
 1139                     zfs_type_to_name(type));
 1140                 goto badusage;
 1141         }
 1142         if (argc > 1) {
 1143                 (void) fprintf(stderr, gettext("too many arguments\n"));
 1144                 goto badusage;
 1145         }
 1146 
 1147         if (dryrun || type == ZFS_TYPE_VOLUME) {
 1148                 char msg[ZFS_MAX_DATASET_NAME_LEN * 2];
 1149                 char *p;
 1150 
 1151                 if ((p = strchr(argv[0], '/')) != NULL)
 1152                         *p = '\0';
 1153                 zpool_handle = zpool_open(g_zfs, argv[0]);
 1154                 if (p != NULL)
 1155                         *p = '/';
 1156                 if (zpool_handle == NULL)
 1157                         goto error;
 1158 
 1159                 (void) snprintf(msg, sizeof (msg),
 1160                     dryrun ? gettext("cannot verify '%s'") :
 1161                     gettext("cannot create '%s'"), argv[0]);
 1162                 if (props && (real_props = zfs_valid_proplist(g_zfs, type,
 1163                     props, 0, NULL, zpool_handle, B_TRUE, msg)) == NULL) {
 1164                         zpool_close(zpool_handle);
 1165                         goto error;
 1166                 }
 1167         }
 1168 
 1169         if (type == ZFS_TYPE_VOLUME) {
 1170                 const char *prop = zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE);
 1171                 uint64_t volblocksize = default_volblocksize(zpool_handle,
 1172                     real_props);
 1173 
 1174                 if (volblocksize != ZVOL_DEFAULT_BLOCKSIZE &&
 1175                     nvlist_lookup_string(props, prop, &strval) != 0) {
 1176                         if (asprintf(&strval, "%llu",
 1177                             (u_longlong_t)volblocksize) == -1)
 1178                                 nomem();
 1179                         nvlist_add_string(props, prop, strval);
 1180                         free(strval);
 1181                 }
 1182 
 1183                 /*
 1184                  * If volsize is not a multiple of volblocksize, round it
 1185                  * up to the nearest multiple of the volblocksize.
 1186                  */
 1187                 if (volsize % volblocksize) {
 1188                         volsize = P2ROUNDUP_TYPED(volsize, volblocksize,
 1189                             uint64_t);
 1190 
 1191                         if (nvlist_add_uint64(props,
 1192                             zfs_prop_to_name(ZFS_PROP_VOLSIZE), volsize) != 0) {
 1193                                 nvlist_free(props);
 1194                                 nomem();
 1195                         }
 1196                 }
 1197         }
 1198 
 1199         if (type == ZFS_TYPE_VOLUME && !noreserve) {
 1200                 uint64_t spa_version;
 1201                 zfs_prop_t resv_prop;
 1202 
 1203                 spa_version = zpool_get_prop_int(zpool_handle,
 1204                     ZPOOL_PROP_VERSION, NULL);
 1205                 if (spa_version >= SPA_VERSION_REFRESERVATION)
 1206                         resv_prop = ZFS_PROP_REFRESERVATION;
 1207                 else
 1208                         resv_prop = ZFS_PROP_RESERVATION;
 1209 
 1210                 volsize = zvol_volsize_to_reservation(zpool_handle, volsize,
 1211                     real_props);
 1212 
 1213                 if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop),
 1214                     &strval) != 0) {
 1215                         if (nvlist_add_uint64(props,
 1216                             zfs_prop_to_name(resv_prop), volsize) != 0) {
 1217                                 nvlist_free(props);
 1218                                 nomem();
 1219                         }
 1220                 }
 1221         }
 1222         if (zpool_handle != NULL) {
 1223                 zpool_close(zpool_handle);
 1224                 nvlist_free(real_props);
 1225         }
 1226 
 1227         if (parents && zfs_name_valid(argv[0], type)) {
 1228                 /*
 1229                  * Now create the ancestors of target dataset.  If the target
 1230                  * already exists and '-p' option was used we should not
 1231                  * complain.
 1232                  */
 1233                 if (zfs_dataset_exists(g_zfs, argv[0], type)) {
 1234                         ret = 0;
 1235                         goto error;
 1236                 }
 1237                 if (verbose) {
 1238                         (void) printf(parseable ? "create_ancestors\t%s\n" :
 1239                             dryrun ?  "would create ancestors of %s\n" :
 1240                             "create ancestors of %s\n", argv[0]);
 1241                 }
 1242                 if (!dryrun) {
 1243                         if (zfs_create_ancestors(g_zfs, argv[0]) != 0) {
 1244                                 goto error;
 1245                         }
 1246                 }
 1247         }
 1248 
 1249         if (verbose) {
 1250                 nvpair_t *nvp = NULL;
 1251                 (void) printf(parseable ? "create\t%s\n" :
 1252                     dryrun ? "would create %s\n" : "create %s\n", argv[0]);
 1253                 while ((nvp = nvlist_next_nvpair(props, nvp)) != NULL) {
 1254                         uint64_t uval;
 1255                         char *sval;
 1256 
 1257                         switch (nvpair_type(nvp)) {
 1258                         case DATA_TYPE_UINT64:
 1259                                 VERIFY0(nvpair_value_uint64(nvp, &uval));
 1260                                 (void) printf(parseable ?
 1261                                     "property\t%s\t%llu\n" : "\t%s=%llu\n",
 1262                                     nvpair_name(nvp), (u_longlong_t)uval);
 1263                                 break;
 1264                         case DATA_TYPE_STRING:
 1265                                 VERIFY0(nvpair_value_string(nvp, &sval));
 1266                                 (void) printf(parseable ?
 1267                                     "property\t%s\t%s\n" : "\t%s=%s\n",
 1268                                     nvpair_name(nvp), sval);
 1269                                 break;
 1270                         default:
 1271                                 (void) fprintf(stderr, "property '%s' "
 1272                                     "has illegal type %d\n",
 1273                                     nvpair_name(nvp), nvpair_type(nvp));
 1274                                 abort();
 1275                         }
 1276                 }
 1277         }
 1278         if (dryrun) {
 1279                 ret = 0;
 1280                 goto error;
 1281         }
 1282 
 1283         /* pass to libzfs */
 1284         if (zfs_create(g_zfs, argv[0], type, props) != 0)
 1285                 goto error;
 1286 
 1287         if (log_history) {
 1288                 (void) zpool_log_history(g_zfs, history_str);
 1289                 log_history = B_FALSE;
 1290         }
 1291 
 1292         if (nomount) {
 1293                 ret = 0;
 1294                 goto error;
 1295         }
 1296 
 1297         ret = zfs_mount_and_share(g_zfs, argv[0], ZFS_TYPE_DATASET);
 1298 error:
 1299         nvlist_free(props);
 1300         return (ret);
 1301 badusage:
 1302         nvlist_free(props);
 1303         usage(B_FALSE);
 1304         return (2);
 1305 }
 1306 
 1307 /*
 1308  * zfs destroy [-rRf] <fs, vol>
 1309  * zfs destroy [-rRd] <snap>
 1310  *
 1311  *      -r      Recursively destroy all children
 1312  *      -R      Recursively destroy all dependents, including clones
 1313  *      -f      Force unmounting of any dependents
 1314  *      -d      If we can't destroy now, mark for deferred destruction
 1315  *
 1316  * Destroys the given dataset.  By default, it will unmount any filesystems,
 1317  * and refuse to destroy a dataset that has any dependents.  A dependent can
 1318  * either be a child, or a clone of a child.
 1319  */
 1320 typedef struct destroy_cbdata {
 1321         boolean_t       cb_first;
 1322         boolean_t       cb_force;
 1323         boolean_t       cb_recurse;
 1324         boolean_t       cb_error;
 1325         boolean_t       cb_doclones;
 1326         zfs_handle_t    *cb_target;
 1327         boolean_t       cb_defer_destroy;
 1328         boolean_t       cb_verbose;
 1329         boolean_t       cb_parsable;
 1330         boolean_t       cb_dryrun;
 1331         nvlist_t        *cb_nvl;
 1332         nvlist_t        *cb_batchedsnaps;
 1333 
 1334         /* first snap in contiguous run */
 1335         char            *cb_firstsnap;
 1336         /* previous snap in contiguous run */
 1337         char            *cb_prevsnap;
 1338         int64_t         cb_snapused;
 1339         char            *cb_snapspec;
 1340         char            *cb_bookmark;
 1341         uint64_t        cb_snap_count;
 1342 } destroy_cbdata_t;
 1343 
 1344 /*
 1345  * Check for any dependents based on the '-r' or '-R' flags.
 1346  */
 1347 static int
 1348 destroy_check_dependent(zfs_handle_t *zhp, void *data)
 1349 {
 1350         destroy_cbdata_t *cbp = data;
 1351         const char *tname = zfs_get_name(cbp->cb_target);
 1352         const char *name = zfs_get_name(zhp);
 1353 
 1354         if (strncmp(tname, name, strlen(tname)) == 0 &&
 1355             (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) {
 1356                 /*
 1357                  * This is a direct descendant, not a clone somewhere else in
 1358                  * the hierarchy.
 1359                  */
 1360                 if (cbp->cb_recurse)
 1361                         goto out;
 1362 
 1363                 if (cbp->cb_first) {
 1364                         (void) fprintf(stderr, gettext("cannot destroy '%s': "
 1365                             "%s has children\n"),
 1366                             zfs_get_name(cbp->cb_target),
 1367                             zfs_type_to_name(zfs_get_type(cbp->cb_target)));
 1368                         (void) fprintf(stderr, gettext("use '-r' to destroy "
 1369                             "the following datasets:\n"));
 1370                         cbp->cb_first = B_FALSE;
 1371                         cbp->cb_error = B_TRUE;
 1372                 }
 1373 
 1374                 (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
 1375         } else {
 1376                 /*
 1377                  * This is a clone.  We only want to report this if the '-r'
 1378                  * wasn't specified, or the target is a snapshot.
 1379                  */
 1380                 if (!cbp->cb_recurse &&
 1381                     zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT)
 1382                         goto out;
 1383 
 1384                 if (cbp->cb_first) {
 1385                         (void) fprintf(stderr, gettext("cannot destroy '%s': "
 1386                             "%s has dependent clones\n"),
 1387                             zfs_get_name(cbp->cb_target),
 1388                             zfs_type_to_name(zfs_get_type(cbp->cb_target)));
 1389                         (void) fprintf(stderr, gettext("use '-R' to destroy "
 1390                             "the following datasets:\n"));
 1391                         cbp->cb_first = B_FALSE;
 1392                         cbp->cb_error = B_TRUE;
 1393                         cbp->cb_dryrun = B_TRUE;
 1394                 }
 1395 
 1396                 (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
 1397         }
 1398 
 1399 out:
 1400         zfs_close(zhp);
 1401         return (0);
 1402 }
 1403 
 1404 static int
 1405 destroy_batched(destroy_cbdata_t *cb)
 1406 {
 1407         int error = zfs_destroy_snaps_nvl(g_zfs,
 1408             cb->cb_batchedsnaps, B_FALSE);
 1409         fnvlist_free(cb->cb_batchedsnaps);
 1410         cb->cb_batchedsnaps = fnvlist_alloc();
 1411         return (error);
 1412 }
 1413 
 1414 static int
 1415 destroy_callback(zfs_handle_t *zhp, void *data)
 1416 {
 1417         destroy_cbdata_t *cb = data;
 1418         const char *name = zfs_get_name(zhp);
 1419         int error;
 1420 
 1421         if (cb->cb_verbose) {
 1422                 if (cb->cb_parsable) {
 1423                         (void) printf("destroy\t%s\n", name);
 1424                 } else if (cb->cb_dryrun) {
 1425                         (void) printf(gettext("would destroy %s\n"),
 1426                             name);
 1427                 } else {
 1428                         (void) printf(gettext("will destroy %s\n"),
 1429                             name);
 1430                 }
 1431         }
 1432 
 1433         /*
 1434          * Ignore pools (which we've already flagged as an error before getting
 1435          * here).
 1436          */
 1437         if (strchr(zfs_get_name(zhp), '/') == NULL &&
 1438             zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
 1439                 zfs_close(zhp);
 1440                 return (0);
 1441         }
 1442         if (cb->cb_dryrun) {
 1443                 zfs_close(zhp);
 1444                 return (0);
 1445         }
 1446 
 1447         /*
 1448          * We batch up all contiguous snapshots (even of different
 1449          * filesystems) and destroy them with one ioctl.  We can't
 1450          * simply do all snap deletions and then all fs deletions,
 1451          * because we must delete a clone before its origin.
 1452          */
 1453         if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) {
 1454                 cb->cb_snap_count++;
 1455                 fnvlist_add_boolean(cb->cb_batchedsnaps, name);
 1456                 if (cb->cb_snap_count % 10 == 0 && cb->cb_defer_destroy) {
 1457                         error = destroy_batched(cb);
 1458                         if (error != 0) {
 1459                                 zfs_close(zhp);
 1460                                 return (-1);
 1461                         }
 1462                 }
 1463         } else {
 1464                 error = destroy_batched(cb);
 1465                 if (error != 0 ||
 1466                     zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 ||
 1467                     zfs_destroy(zhp, cb->cb_defer_destroy) != 0) {
 1468                         zfs_close(zhp);
 1469                         /*
 1470                          * When performing a recursive destroy we ignore errors
 1471                          * so that the recursive destroy could continue
 1472                          * destroying past problem datasets
 1473                          */
 1474                         if (cb->cb_recurse) {
 1475                                 cb->cb_error = B_TRUE;
 1476                                 return (0);
 1477                         }
 1478                         return (-1);
 1479                 }
 1480         }
 1481 
 1482         zfs_close(zhp);
 1483         return (0);
 1484 }
 1485 
 1486 static int
 1487 destroy_print_cb(zfs_handle_t *zhp, void *arg)
 1488 {
 1489         destroy_cbdata_t *cb = arg;
 1490         const char *name = zfs_get_name(zhp);
 1491         int err = 0;
 1492 
 1493         if (nvlist_exists(cb->cb_nvl, name)) {
 1494                 if (cb->cb_firstsnap == NULL)
 1495                         cb->cb_firstsnap = strdup(name);
 1496                 if (cb->cb_prevsnap != NULL)
 1497                         free(cb->cb_prevsnap);
 1498                 /* this snap continues the current range */
 1499                 cb->cb_prevsnap = strdup(name);
 1500                 if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL)
 1501                         nomem();
 1502                 if (cb->cb_verbose) {
 1503                         if (cb->cb_parsable) {
 1504                                 (void) printf("destroy\t%s\n", name);
 1505                         } else if (cb->cb_dryrun) {
 1506                                 (void) printf(gettext("would destroy %s\n"),
 1507                                     name);
 1508                         } else {
 1509                                 (void) printf(gettext("will destroy %s\n"),
 1510                                     name);
 1511                         }
 1512                 }
 1513         } else if (cb->cb_firstsnap != NULL) {
 1514                 /* end of this range */
 1515                 uint64_t used = 0;
 1516                 err = lzc_snaprange_space(cb->cb_firstsnap,
 1517                     cb->cb_prevsnap, &used);
 1518                 cb->cb_snapused += used;
 1519                 free(cb->cb_firstsnap);
 1520                 cb->cb_firstsnap = NULL;
 1521                 free(cb->cb_prevsnap);
 1522                 cb->cb_prevsnap = NULL;
 1523         }
 1524         zfs_close(zhp);
 1525         return (err);
 1526 }
 1527 
 1528 static int
 1529 destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb)
 1530 {
 1531         int err;
 1532         assert(cb->cb_firstsnap == NULL);
 1533         assert(cb->cb_prevsnap == NULL);
 1534         err = zfs_iter_snapshots_sorted(fs_zhp, 0, destroy_print_cb, cb, 0, 0);
 1535         if (cb->cb_firstsnap != NULL) {
 1536                 uint64_t used = 0;
 1537                 if (err == 0) {
 1538                         err = lzc_snaprange_space(cb->cb_firstsnap,
 1539                             cb->cb_prevsnap, &used);
 1540                 }
 1541                 cb->cb_snapused += used;
 1542                 free(cb->cb_firstsnap);
 1543                 cb->cb_firstsnap = NULL;
 1544                 free(cb->cb_prevsnap);
 1545                 cb->cb_prevsnap = NULL;
 1546         }
 1547         return (err);
 1548 }
 1549 
 1550 static int
 1551 snapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg)
 1552 {
 1553         destroy_cbdata_t *cb = arg;
 1554         int err = 0;
 1555 
 1556         /* Check for clones. */
 1557         if (!cb->cb_doclones && !cb->cb_defer_destroy) {
 1558                 cb->cb_target = zhp;
 1559                 cb->cb_first = B_TRUE;
 1560                 err = zfs_iter_dependents(zhp, 0, B_TRUE,
 1561                     destroy_check_dependent, cb);
 1562         }
 1563 
 1564         if (err == 0) {
 1565                 if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp)))
 1566                         nomem();
 1567         }
 1568         zfs_close(zhp);
 1569         return (err);
 1570 }
 1571 
 1572 static int
 1573 gather_snapshots(zfs_handle_t *zhp, void *arg)
 1574 {
 1575         destroy_cbdata_t *cb = arg;
 1576         int err = 0;
 1577 
 1578         err = zfs_iter_snapspec(zhp, 0, cb->cb_snapspec,
 1579             snapshot_to_nvl_cb, cb);
 1580         if (err == ENOENT)
 1581                 err = 0;
 1582         if (err != 0)
 1583                 goto out;
 1584 
 1585         if (cb->cb_verbose) {
 1586                 err = destroy_print_snapshots(zhp, cb);
 1587                 if (err != 0)
 1588                         goto out;
 1589         }
 1590 
 1591         if (cb->cb_recurse)
 1592                 err = zfs_iter_filesystems(zhp, 0, gather_snapshots, cb);
 1593 
 1594 out:
 1595         zfs_close(zhp);
 1596         return (err);
 1597 }
 1598 
 1599 static int
 1600 destroy_clones(destroy_cbdata_t *cb)
 1601 {
 1602         nvpair_t *pair;
 1603         for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL);
 1604             pair != NULL;
 1605             pair = nvlist_next_nvpair(cb->cb_nvl, pair)) {
 1606                 zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair),
 1607                     ZFS_TYPE_SNAPSHOT);
 1608                 if (zhp != NULL) {
 1609                         boolean_t defer = cb->cb_defer_destroy;
 1610                         int err;
 1611 
 1612                         /*
 1613                          * We can't defer destroy non-snapshots, so set it to
 1614                          * false while destroying the clones.
 1615                          */
 1616                         cb->cb_defer_destroy = B_FALSE;
 1617                         err = zfs_iter_dependents(zhp, 0, B_FALSE,
 1618                             destroy_callback, cb);
 1619                         cb->cb_defer_destroy = defer;
 1620                         zfs_close(zhp);
 1621                         if (err != 0)
 1622                                 return (err);
 1623                 }
 1624         }
 1625         return (0);
 1626 }
 1627 
 1628 static int
 1629 zfs_do_destroy(int argc, char **argv)
 1630 {
 1631         destroy_cbdata_t cb = { 0 };
 1632         int rv = 0;
 1633         int err = 0;
 1634         int c;
 1635         zfs_handle_t *zhp = NULL;
 1636         char *at, *pound;
 1637         zfs_type_t type = ZFS_TYPE_DATASET;
 1638 
 1639         /* check options */
 1640         while ((c = getopt(argc, argv, "vpndfrR")) != -1) {
 1641                 switch (c) {
 1642                 case 'v':
 1643                         cb.cb_verbose = B_TRUE;
 1644                         break;
 1645                 case 'p':
 1646                         cb.cb_verbose = B_TRUE;
 1647                         cb.cb_parsable = B_TRUE;
 1648                         break;
 1649                 case 'n':
 1650                         cb.cb_dryrun = B_TRUE;
 1651                         break;
 1652                 case 'd':
 1653                         cb.cb_defer_destroy = B_TRUE;
 1654                         type = ZFS_TYPE_SNAPSHOT;
 1655                         break;
 1656                 case 'f':
 1657                         cb.cb_force = B_TRUE;
 1658                         break;
 1659                 case 'r':
 1660                         cb.cb_recurse = B_TRUE;
 1661                         break;
 1662                 case 'R':
 1663                         cb.cb_recurse = B_TRUE;
 1664                         cb.cb_doclones = B_TRUE;
 1665                         break;
 1666                 case '?':
 1667                 default:
 1668                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 1669                             optopt);
 1670                         usage(B_FALSE);
 1671                 }
 1672         }
 1673 
 1674         argc -= optind;
 1675         argv += optind;
 1676 
 1677         /* check number of arguments */
 1678         if (argc == 0) {
 1679                 (void) fprintf(stderr, gettext("missing dataset argument\n"));
 1680                 usage(B_FALSE);
 1681         }
 1682         if (argc > 1) {
 1683                 (void) fprintf(stderr, gettext("too many arguments\n"));
 1684                 usage(B_FALSE);
 1685         }
 1686 
 1687         at = strchr(argv[0], '@');
 1688         pound = strchr(argv[0], '#');
 1689         if (at != NULL) {
 1690 
 1691                 /* Build the list of snaps to destroy in cb_nvl. */
 1692                 cb.cb_nvl = fnvlist_alloc();
 1693 
 1694                 *at = '\0';
 1695                 zhp = zfs_open(g_zfs, argv[0],
 1696                     ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
 1697                 if (zhp == NULL) {
 1698                         nvlist_free(cb.cb_nvl);
 1699                         return (1);
 1700                 }
 1701 
 1702                 cb.cb_snapspec = at + 1;
 1703                 if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 ||
 1704                     cb.cb_error) {
 1705                         rv = 1;
 1706                         goto out;
 1707                 }
 1708 
 1709                 if (nvlist_empty(cb.cb_nvl)) {
 1710                         (void) fprintf(stderr, gettext("could not find any "
 1711                             "snapshots to destroy; check snapshot names.\n"));
 1712                         rv = 1;
 1713                         goto out;
 1714                 }
 1715 
 1716                 if (cb.cb_verbose) {
 1717                         char buf[16];
 1718                         zfs_nicebytes(cb.cb_snapused, buf, sizeof (buf));
 1719                         if (cb.cb_parsable) {
 1720                                 (void) printf("reclaim\t%llu\n",
 1721                                     (u_longlong_t)cb.cb_snapused);
 1722                         } else if (cb.cb_dryrun) {
 1723                                 (void) printf(gettext("would reclaim %s\n"),
 1724                                     buf);
 1725                         } else {
 1726                                 (void) printf(gettext("will reclaim %s\n"),
 1727                                     buf);
 1728                         }
 1729                 }
 1730 
 1731                 if (!cb.cb_dryrun) {
 1732                         if (cb.cb_doclones) {
 1733                                 cb.cb_batchedsnaps = fnvlist_alloc();
 1734                                 err = destroy_clones(&cb);
 1735                                 if (err == 0) {
 1736                                         err = zfs_destroy_snaps_nvl(g_zfs,
 1737                                             cb.cb_batchedsnaps, B_FALSE);
 1738                                 }
 1739                                 if (err != 0) {
 1740                                         rv = 1;
 1741                                         goto out;
 1742                                 }
 1743                         }
 1744                         if (err == 0) {
 1745                                 err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl,
 1746                                     cb.cb_defer_destroy);
 1747                         }
 1748                 }
 1749 
 1750                 if (err != 0)
 1751                         rv = 1;
 1752         } else if (pound != NULL) {
 1753                 int err;
 1754                 nvlist_t *nvl;
 1755 
 1756                 if (cb.cb_dryrun) {
 1757                         (void) fprintf(stderr,
 1758                             "dryrun is not supported with bookmark\n");
 1759                         return (-1);
 1760                 }
 1761 
 1762                 if (cb.cb_defer_destroy) {
 1763                         (void) fprintf(stderr,
 1764                             "defer destroy is not supported with bookmark\n");
 1765                         return (-1);
 1766                 }
 1767 
 1768                 if (cb.cb_recurse) {
 1769                         (void) fprintf(stderr,
 1770                             "recursive is not supported with bookmark\n");
 1771                         return (-1);
 1772                 }
 1773 
 1774                 /*
 1775                  * Unfortunately, zfs_bookmark() doesn't honor the
 1776                  * casesensitivity setting.  However, we can't simply
 1777                  * remove this check, because lzc_destroy_bookmarks()
 1778                  * ignores non-existent bookmarks, so this is necessary
 1779                  * to get a proper error message.
 1780                  */
 1781                 if (!zfs_bookmark_exists(argv[0])) {
 1782                         (void) fprintf(stderr, gettext("bookmark '%s' "
 1783                             "does not exist.\n"), argv[0]);
 1784                         return (1);
 1785                 }
 1786 
 1787                 nvl = fnvlist_alloc();
 1788                 fnvlist_add_boolean(nvl, argv[0]);
 1789 
 1790                 err = lzc_destroy_bookmarks(nvl, NULL);
 1791                 if (err != 0) {
 1792                         (void) zfs_standard_error(g_zfs, err,
 1793                             "cannot destroy bookmark");
 1794                 }
 1795 
 1796                 nvlist_free(nvl);
 1797 
 1798                 return (err);
 1799         } else {
 1800                 /* Open the given dataset */
 1801                 if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL)
 1802                         return (1);
 1803 
 1804                 cb.cb_target = zhp;
 1805 
 1806                 /*
 1807                  * Perform an explicit check for pools before going any further.
 1808                  */
 1809                 if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL &&
 1810                     zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
 1811                         (void) fprintf(stderr, gettext("cannot destroy '%s': "
 1812                             "operation does not apply to pools\n"),
 1813                             zfs_get_name(zhp));
 1814                         (void) fprintf(stderr, gettext("use 'zfs destroy -r "
 1815                             "%s' to destroy all datasets in the pool\n"),
 1816                             zfs_get_name(zhp));
 1817                         (void) fprintf(stderr, gettext("use 'zpool destroy %s' "
 1818                             "to destroy the pool itself\n"), zfs_get_name(zhp));
 1819                         rv = 1;
 1820                         goto out;
 1821                 }
 1822 
 1823                 /*
 1824                  * Check for any dependents and/or clones.
 1825                  */
 1826                 cb.cb_first = B_TRUE;
 1827                 if (!cb.cb_doclones &&
 1828                     zfs_iter_dependents(zhp, 0, B_TRUE, destroy_check_dependent,
 1829                     &cb) != 0) {
 1830                         rv = 1;
 1831                         goto out;
 1832                 }
 1833 
 1834                 if (cb.cb_error) {
 1835                         rv = 1;
 1836                         goto out;
 1837                 }
 1838                 cb.cb_batchedsnaps = fnvlist_alloc();
 1839                 if (zfs_iter_dependents(zhp, 0, B_FALSE, destroy_callback,
 1840                     &cb) != 0) {
 1841                         rv = 1;
 1842                         goto out;
 1843                 }
 1844 
 1845                 /*
 1846                  * Do the real thing.  The callback will close the
 1847                  * handle regardless of whether it succeeds or not.
 1848                  */
 1849                 err = destroy_callback(zhp, &cb);
 1850                 zhp = NULL;
 1851                 if (err == 0) {
 1852                         err = zfs_destroy_snaps_nvl(g_zfs,
 1853                             cb.cb_batchedsnaps, cb.cb_defer_destroy);
 1854                 }
 1855                 if (err != 0 || cb.cb_error == B_TRUE)
 1856                         rv = 1;
 1857         }
 1858 
 1859 out:
 1860         fnvlist_free(cb.cb_batchedsnaps);
 1861         fnvlist_free(cb.cb_nvl);
 1862         if (zhp != NULL)
 1863                 zfs_close(zhp);
 1864         return (rv);
 1865 }
 1866 
 1867 static boolean_t
 1868 is_recvd_column(zprop_get_cbdata_t *cbp)
 1869 {
 1870         int i;
 1871         zfs_get_column_t col;
 1872 
 1873         for (i = 0; i < ZFS_GET_NCOLS &&
 1874             (col = cbp->cb_columns[i]) != GET_COL_NONE; i++)
 1875                 if (col == GET_COL_RECVD)
 1876                         return (B_TRUE);
 1877         return (B_FALSE);
 1878 }
 1879 
 1880 /*
 1881  * zfs get [-rHp] [-o all | field[,field]...] [-s source[,source]...]
 1882  *      < all | property[,property]... > < fs | snap | vol > ...
 1883  *
 1884  *      -r      recurse over any child datasets
 1885  *      -H      scripted mode.  Headers are stripped, and fields are separated
 1886  *              by tabs instead of spaces.
 1887  *      -o      Set of fields to display.  One of "name,property,value,
 1888  *              received,source". Default is "name,property,value,source".
 1889  *              "all" is an alias for all five.
 1890  *      -s      Set of sources to allow.  One of
 1891  *              "local,default,inherited,received,temporary,none".  Default is
 1892  *              all six.
 1893  *      -p      Display values in parsable (literal) format.
 1894  *
 1895  *  Prints properties for the given datasets.  The user can control which
 1896  *  columns to display as well as which property types to allow.
 1897  */
 1898 
 1899 /*
 1900  * Invoked to display the properties for a single dataset.
 1901  */
 1902 static int
 1903 get_callback(zfs_handle_t *zhp, void *data)
 1904 {
 1905         char buf[ZFS_MAXPROPLEN];
 1906         char rbuf[ZFS_MAXPROPLEN];
 1907         zprop_source_t sourcetype;
 1908         char source[ZFS_MAX_DATASET_NAME_LEN];
 1909         zprop_get_cbdata_t *cbp = data;
 1910         nvlist_t *user_props = zfs_get_user_props(zhp);
 1911         zprop_list_t *pl = cbp->cb_proplist;
 1912         nvlist_t *propval;
 1913         const char *strval;
 1914         const char *sourceval;
 1915         boolean_t received = is_recvd_column(cbp);
 1916 
 1917         for (; pl != NULL; pl = pl->pl_next) {
 1918                 char *recvdval = NULL;
 1919                 /*
 1920                  * Skip the special fake placeholder.  This will also skip over
 1921                  * the name property when 'all' is specified.
 1922                  */
 1923                 if (pl->pl_prop == ZFS_PROP_NAME &&
 1924                     pl == cbp->cb_proplist)
 1925                         continue;
 1926 
 1927                 if (pl->pl_prop != ZPROP_USERPROP) {
 1928                         if (zfs_prop_get(zhp, pl->pl_prop, buf,
 1929                             sizeof (buf), &sourcetype, source,
 1930                             sizeof (source),
 1931                             cbp->cb_literal) != 0) {
 1932                                 if (pl->pl_all)
 1933                                         continue;
 1934                                 if (!zfs_prop_valid_for_type(pl->pl_prop,
 1935                                     ZFS_TYPE_DATASET, B_FALSE)) {
 1936                                         (void) fprintf(stderr,
 1937                                             gettext("No such property '%s'\n"),
 1938                                             zfs_prop_to_name(pl->pl_prop));
 1939                                         continue;
 1940                                 }
 1941                                 sourcetype = ZPROP_SRC_NONE;
 1942                                 (void) strlcpy(buf, "-", sizeof (buf));
 1943                         }
 1944 
 1945                         if (received && (zfs_prop_get_recvd(zhp,
 1946                             zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf),
 1947                             cbp->cb_literal) == 0))
 1948                                 recvdval = rbuf;
 1949 
 1950                         zprop_print_one_property(zfs_get_name(zhp), cbp,
 1951                             zfs_prop_to_name(pl->pl_prop),
 1952                             buf, sourcetype, source, recvdval);
 1953                 } else if (zfs_prop_userquota(pl->pl_user_prop)) {
 1954                         sourcetype = ZPROP_SRC_LOCAL;
 1955 
 1956                         if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
 1957                             buf, sizeof (buf), cbp->cb_literal) != 0) {
 1958                                 sourcetype = ZPROP_SRC_NONE;
 1959                                 (void) strlcpy(buf, "-", sizeof (buf));
 1960                         }
 1961 
 1962                         zprop_print_one_property(zfs_get_name(zhp), cbp,
 1963                             pl->pl_user_prop, buf, sourcetype, source, NULL);
 1964                 } else if (zfs_prop_written(pl->pl_user_prop)) {
 1965                         sourcetype = ZPROP_SRC_LOCAL;
 1966 
 1967                         if (zfs_prop_get_written(zhp, pl->pl_user_prop,
 1968                             buf, sizeof (buf), cbp->cb_literal) != 0) {
 1969                                 sourcetype = ZPROP_SRC_NONE;
 1970                                 (void) strlcpy(buf, "-", sizeof (buf));
 1971                         }
 1972 
 1973                         zprop_print_one_property(zfs_get_name(zhp), cbp,
 1974                             pl->pl_user_prop, buf, sourcetype, source, NULL);
 1975                 } else {
 1976                         if (nvlist_lookup_nvlist(user_props,
 1977                             pl->pl_user_prop, &propval) != 0) {
 1978                                 if (pl->pl_all)
 1979                                         continue;
 1980                                 sourcetype = ZPROP_SRC_NONE;
 1981                                 strval = "-";
 1982                         } else {
 1983                                 strval = fnvlist_lookup_string(propval,
 1984                                     ZPROP_VALUE);
 1985                                 sourceval = fnvlist_lookup_string(propval,
 1986                                     ZPROP_SOURCE);
 1987 
 1988                                 if (strcmp(sourceval,
 1989                                     zfs_get_name(zhp)) == 0) {
 1990                                         sourcetype = ZPROP_SRC_LOCAL;
 1991                                 } else if (strcmp(sourceval,
 1992                                     ZPROP_SOURCE_VAL_RECVD) == 0) {
 1993                                         sourcetype = ZPROP_SRC_RECEIVED;
 1994                                 } else {
 1995                                         sourcetype = ZPROP_SRC_INHERITED;
 1996                                         (void) strlcpy(source,
 1997                                             sourceval, sizeof (source));
 1998                                 }
 1999                         }
 2000 
 2001                         if (received && (zfs_prop_get_recvd(zhp,
 2002                             pl->pl_user_prop, rbuf, sizeof (rbuf),
 2003                             cbp->cb_literal) == 0))
 2004                                 recvdval = rbuf;
 2005 
 2006                         zprop_print_one_property(zfs_get_name(zhp), cbp,
 2007                             pl->pl_user_prop, strval, sourcetype,
 2008                             source, recvdval);
 2009                 }
 2010         }
 2011 
 2012         return (0);
 2013 }
 2014 
 2015 static int
 2016 zfs_do_get(int argc, char **argv)
 2017 {
 2018         zprop_get_cbdata_t cb = { 0 };
 2019         int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
 2020         int types = ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK;
 2021         char *fields;
 2022         int ret = 0;
 2023         int limit = 0;
 2024         zprop_list_t fake_name = { 0 };
 2025 
 2026         /*
 2027          * Set up default columns and sources.
 2028          */
 2029         cb.cb_sources = ZPROP_SRC_ALL;
 2030         cb.cb_columns[0] = GET_COL_NAME;
 2031         cb.cb_columns[1] = GET_COL_PROPERTY;
 2032         cb.cb_columns[2] = GET_COL_VALUE;
 2033         cb.cb_columns[3] = GET_COL_SOURCE;
 2034         cb.cb_type = ZFS_TYPE_DATASET;
 2035 
 2036         /* check options */
 2037         while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) {
 2038                 switch (c) {
 2039                 case 'p':
 2040                         cb.cb_literal = B_TRUE;
 2041                         break;
 2042                 case 'd':
 2043                         limit = parse_depth(optarg, &flags);
 2044                         break;
 2045                 case 'r':
 2046                         flags |= ZFS_ITER_RECURSE;
 2047                         break;
 2048                 case 'H':
 2049                         cb.cb_scripted = B_TRUE;
 2050                         break;
 2051                 case ':':
 2052                         (void) fprintf(stderr, gettext("missing argument for "
 2053                             "'%c' option\n"), optopt);
 2054                         usage(B_FALSE);
 2055                         break;
 2056                 case 'o':
 2057                         /*
 2058                          * Process the set of columns to display.  We zero out
 2059                          * the structure to give us a blank slate.
 2060                          */
 2061                         memset(&cb.cb_columns, 0, sizeof (cb.cb_columns));
 2062 
 2063                         i = 0;
 2064                         for (char *tok; (tok = strsep(&optarg, ",")); ) {
 2065                                 static const char *const col_subopts[] =
 2066                                 { "name", "property", "value",
 2067                                     "received", "source", "all" };
 2068                                 static const zfs_get_column_t col_subopt_col[] =
 2069                                 { GET_COL_NAME, GET_COL_PROPERTY, GET_COL_VALUE,
 2070                                     GET_COL_RECVD, GET_COL_SOURCE };
 2071                                 static const int col_subopt_flags[] =
 2072                                 { 0, 0, 0, ZFS_ITER_RECVD_PROPS, 0 };
 2073 
 2074                                 if (i == ZFS_GET_NCOLS) {
 2075                                         (void) fprintf(stderr, gettext("too "
 2076                                             "many fields given to -o "
 2077                                             "option\n"));
 2078                                         usage(B_FALSE);
 2079                                 }
 2080 
 2081                                 for (c = 0; c < ARRAY_SIZE(col_subopts); ++c)
 2082                                         if (strcmp(tok, col_subopts[c]) == 0)
 2083                                                 goto found;
 2084 
 2085                                 (void) fprintf(stderr,
 2086                                     gettext("invalid column name '%s'\n"), tok);
 2087                                 usage(B_FALSE);
 2088 
 2089 found:
 2090                                 if (c >= 5) {
 2091                                         if (i > 0) {
 2092                                                 (void) fprintf(stderr,
 2093                                                     gettext("\"all\" conflicts "
 2094                                                     "with specific fields "
 2095                                                     "given to -o option\n"));
 2096                                                 usage(B_FALSE);
 2097                                         }
 2098 
 2099                                         memcpy(cb.cb_columns, col_subopt_col,
 2100                                             sizeof (col_subopt_col));
 2101                                         flags |= ZFS_ITER_RECVD_PROPS;
 2102                                         i = ZFS_GET_NCOLS;
 2103                                 } else {
 2104                                         cb.cb_columns[i++] = col_subopt_col[c];
 2105                                         flags |= col_subopt_flags[c];
 2106                                 }
 2107                         }
 2108                         break;
 2109 
 2110                 case 's':
 2111                         cb.cb_sources = 0;
 2112 
 2113                         for (char *tok; (tok = strsep(&optarg, ",")); ) {
 2114                                 static const char *const source_opt[] = {
 2115                                         "local", "default",
 2116                                         "inherited", "received",
 2117                                         "temporary", "none" };
 2118                                 static const int source_flg[] = {
 2119                                         ZPROP_SRC_LOCAL, ZPROP_SRC_DEFAULT,
 2120                                         ZPROP_SRC_INHERITED, ZPROP_SRC_RECEIVED,
 2121                                         ZPROP_SRC_TEMPORARY, ZPROP_SRC_NONE };
 2122 
 2123                                 for (i = 0; i < ARRAY_SIZE(source_opt); ++i)
 2124                                         if (strcmp(tok, source_opt[i]) == 0) {
 2125                                                 cb.cb_sources |= source_flg[i];
 2126                                                 goto found2;
 2127                                         }
 2128 
 2129                                 (void) fprintf(stderr,
 2130                                     gettext("invalid source '%s'\n"), tok);
 2131                                 usage(B_FALSE);
 2132 found2:;
 2133                         }
 2134                         break;
 2135 
 2136                 case 't':
 2137                         types = 0;
 2138                         flags &= ~ZFS_ITER_PROP_LISTSNAPS;
 2139 
 2140                         for (char *tok; (tok = strsep(&optarg, ",")); ) {
 2141                                 static const char *const type_opts[] = {
 2142                                         "filesystem", "volume",
 2143                                         "snapshot", "snap",
 2144                                         "bookmark",
 2145                                         "all" };
 2146                                 static const int type_types[] = {
 2147                                         ZFS_TYPE_FILESYSTEM, ZFS_TYPE_VOLUME,
 2148                                         ZFS_TYPE_SNAPSHOT, ZFS_TYPE_SNAPSHOT,
 2149                                         ZFS_TYPE_BOOKMARK,
 2150                                         ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK };
 2151 
 2152                                 for (i = 0; i < ARRAY_SIZE(type_opts); ++i)
 2153                                         if (strcmp(tok, type_opts[i]) == 0) {
 2154                                                 types |= type_types[i];
 2155                                                 goto found3;
 2156                                         }
 2157 
 2158                                 (void) fprintf(stderr,
 2159                                     gettext("invalid type '%s'\n"), tok);
 2160                                 usage(B_FALSE);
 2161 found3:;
 2162                         }
 2163                         break;
 2164 
 2165                 case '?':
 2166                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 2167                             optopt);
 2168                         usage(B_FALSE);
 2169                 }
 2170         }
 2171 
 2172         argc -= optind;
 2173         argv += optind;
 2174 
 2175         if (argc < 1) {
 2176                 (void) fprintf(stderr, gettext("missing property "
 2177                     "argument\n"));
 2178                 usage(B_FALSE);
 2179         }
 2180 
 2181         fields = argv[0];
 2182 
 2183         /*
 2184          * Handle users who want to get all snapshots or bookmarks
 2185          * of a dataset (ex. 'zfs get -t snapshot refer <dataset>').
 2186          */
 2187         if ((types == ZFS_TYPE_SNAPSHOT || types == ZFS_TYPE_BOOKMARK) &&
 2188             argc > 1 && (flags & ZFS_ITER_RECURSE) == 0 && limit == 0) {
 2189                 flags |= (ZFS_ITER_DEPTH_LIMIT | ZFS_ITER_RECURSE);
 2190                 limit = 1;
 2191         }
 2192 
 2193         if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
 2194             != 0)
 2195                 usage(B_FALSE);
 2196 
 2197         argc--;
 2198         argv++;
 2199 
 2200         /*
 2201          * As part of zfs_expand_proplist(), we keep track of the maximum column
 2202          * width for each property.  For the 'NAME' (and 'SOURCE') columns, we
 2203          * need to know the maximum name length.  However, the user likely did
 2204          * not specify 'name' as one of the properties to fetch, so we need to
 2205          * make sure we always include at least this property for
 2206          * print_get_headers() to work properly.
 2207          */
 2208         if (cb.cb_proplist != NULL) {
 2209                 fake_name.pl_prop = ZFS_PROP_NAME;
 2210                 fake_name.pl_width = strlen(gettext("NAME"));
 2211                 fake_name.pl_next = cb.cb_proplist;
 2212                 cb.cb_proplist = &fake_name;
 2213         }
 2214 
 2215         cb.cb_first = B_TRUE;
 2216 
 2217         /* run for each object */
 2218         ret = zfs_for_each(argc, argv, flags, types, NULL,
 2219             &cb.cb_proplist, limit, get_callback, &cb);
 2220 
 2221         if (cb.cb_proplist == &fake_name)
 2222                 zprop_free_list(fake_name.pl_next);
 2223         else
 2224                 zprop_free_list(cb.cb_proplist);
 2225 
 2226         return (ret);
 2227 }
 2228 
 2229 /*
 2230  * inherit [-rS] <property> <fs|vol> ...
 2231  *
 2232  *      -r      Recurse over all children
 2233  *      -S      Revert to received value, if any
 2234  *
 2235  * For each dataset specified on the command line, inherit the given property
 2236  * from its parent.  Inheriting a property at the pool level will cause it to
 2237  * use the default value.  The '-r' flag will recurse over all children, and is
 2238  * useful for setting a property on a hierarchy-wide basis, regardless of any
 2239  * local modifications for each dataset.
 2240  */
 2241 
 2242 typedef struct inherit_cbdata {
 2243         const char *cb_propname;
 2244         boolean_t cb_received;
 2245 } inherit_cbdata_t;
 2246 
 2247 static int
 2248 inherit_recurse_cb(zfs_handle_t *zhp, void *data)
 2249 {
 2250         inherit_cbdata_t *cb = data;
 2251         zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname);
 2252 
 2253         /*
 2254          * If we're doing it recursively, then ignore properties that
 2255          * are not valid for this type of dataset.
 2256          */
 2257         if (prop != ZPROP_INVAL &&
 2258             !zfs_prop_valid_for_type(prop, zfs_get_type(zhp), B_FALSE))
 2259                 return (0);
 2260 
 2261         return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
 2262 }
 2263 
 2264 static int
 2265 inherit_cb(zfs_handle_t *zhp, void *data)
 2266 {
 2267         inherit_cbdata_t *cb = data;
 2268 
 2269         return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
 2270 }
 2271 
 2272 static int
 2273 zfs_do_inherit(int argc, char **argv)
 2274 {
 2275         int c;
 2276         zfs_prop_t prop;
 2277         inherit_cbdata_t cb = { 0 };
 2278         char *propname;
 2279         int ret = 0;
 2280         int flags = 0;
 2281         boolean_t received = B_FALSE;
 2282 
 2283         /* check options */
 2284         while ((c = getopt(argc, argv, "rS")) != -1) {
 2285                 switch (c) {
 2286                 case 'r':
 2287                         flags |= ZFS_ITER_RECURSE;
 2288                         break;
 2289                 case 'S':
 2290                         received = B_TRUE;
 2291                         break;
 2292                 case '?':
 2293                 default:
 2294                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 2295                             optopt);
 2296                         usage(B_FALSE);
 2297                 }
 2298         }
 2299 
 2300         argc -= optind;
 2301         argv += optind;
 2302 
 2303         /* check number of arguments */
 2304         if (argc < 1) {
 2305                 (void) fprintf(stderr, gettext("missing property argument\n"));
 2306                 usage(B_FALSE);
 2307         }
 2308         if (argc < 2) {
 2309                 (void) fprintf(stderr, gettext("missing dataset argument\n"));
 2310                 usage(B_FALSE);
 2311         }
 2312 
 2313         propname = argv[0];
 2314         argc--;
 2315         argv++;
 2316 
 2317         if ((prop = zfs_name_to_prop(propname)) != ZPROP_USERPROP) {
 2318                 if (zfs_prop_readonly(prop)) {
 2319                         (void) fprintf(stderr, gettext(
 2320                             "%s property is read-only\n"),
 2321                             propname);
 2322                         return (1);
 2323                 }
 2324                 if (!zfs_prop_inheritable(prop) && !received) {
 2325                         (void) fprintf(stderr, gettext("'%s' property cannot "
 2326                             "be inherited\n"), propname);
 2327                         if (prop == ZFS_PROP_QUOTA ||
 2328                             prop == ZFS_PROP_RESERVATION ||
 2329                             prop == ZFS_PROP_REFQUOTA ||
 2330                             prop == ZFS_PROP_REFRESERVATION) {
 2331                                 (void) fprintf(stderr, gettext("use 'zfs set "
 2332                                     "%s=none' to clear\n"), propname);
 2333                                 (void) fprintf(stderr, gettext("use 'zfs "
 2334                                     "inherit -S %s' to revert to received "
 2335                                     "value\n"), propname);
 2336                         }
 2337                         return (1);
 2338                 }
 2339                 if (received && (prop == ZFS_PROP_VOLSIZE ||
 2340                     prop == ZFS_PROP_VERSION)) {
 2341                         (void) fprintf(stderr, gettext("'%s' property cannot "
 2342                             "be reverted to a received value\n"), propname);
 2343                         return (1);
 2344                 }
 2345         } else if (!zfs_prop_user(propname)) {
 2346                 (void) fprintf(stderr, gettext("invalid property '%s'\n"),
 2347                     propname);
 2348                 usage(B_FALSE);
 2349         }
 2350 
 2351         cb.cb_propname = propname;
 2352         cb.cb_received = received;
 2353 
 2354         if (flags & ZFS_ITER_RECURSE) {
 2355                 ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
 2356                     NULL, NULL, 0, inherit_recurse_cb, &cb);
 2357         } else {
 2358                 ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
 2359                     NULL, NULL, 0, inherit_cb, &cb);
 2360         }
 2361 
 2362         return (ret);
 2363 }
 2364 
 2365 typedef struct upgrade_cbdata {
 2366         uint64_t cb_numupgraded;
 2367         uint64_t cb_numsamegraded;
 2368         uint64_t cb_numfailed;
 2369         uint64_t cb_version;
 2370         boolean_t cb_newer;
 2371         boolean_t cb_foundone;
 2372         char cb_lastfs[ZFS_MAX_DATASET_NAME_LEN];
 2373 } upgrade_cbdata_t;
 2374 
 2375 static int
 2376 same_pool(zfs_handle_t *zhp, const char *name)
 2377 {
 2378         int len1 = strcspn(name, "/@");
 2379         const char *zhname = zfs_get_name(zhp);
 2380         int len2 = strcspn(zhname, "/@");
 2381 
 2382         if (len1 != len2)
 2383                 return (B_FALSE);
 2384         return (strncmp(name, zhname, len1) == 0);
 2385 }
 2386 
 2387 static int
 2388 upgrade_list_callback(zfs_handle_t *zhp, void *data)
 2389 {
 2390         upgrade_cbdata_t *cb = data;
 2391         int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
 2392 
 2393         /* list if it's old/new */
 2394         if ((!cb->cb_newer && version < ZPL_VERSION) ||
 2395             (cb->cb_newer && version > ZPL_VERSION)) {
 2396                 char *str;
 2397                 if (cb->cb_newer) {
 2398                         str = gettext("The following filesystems are "
 2399                             "formatted using a newer software version and\n"
 2400                             "cannot be accessed on the current system.\n\n");
 2401                 } else {
 2402                         str = gettext("The following filesystems are "
 2403                             "out of date, and can be upgraded.  After being\n"
 2404                             "upgraded, these filesystems (and any 'zfs send' "
 2405                             "streams generated from\n"
 2406                             "subsequent snapshots) will no longer be "
 2407                             "accessible by older software versions.\n\n");
 2408                 }
 2409 
 2410                 if (!cb->cb_foundone) {
 2411                         (void) puts(str);
 2412                         (void) printf(gettext("VER  FILESYSTEM\n"));
 2413                         (void) printf(gettext("---  ------------\n"));
 2414                         cb->cb_foundone = B_TRUE;
 2415                 }
 2416 
 2417                 (void) printf("%2u   %s\n", version, zfs_get_name(zhp));
 2418         }
 2419 
 2420         return (0);
 2421 }
 2422 
 2423 static int
 2424 upgrade_set_callback(zfs_handle_t *zhp, void *data)
 2425 {
 2426         upgrade_cbdata_t *cb = data;
 2427         int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
 2428         int needed_spa_version;
 2429         int spa_version;
 2430 
 2431         if (zfs_spa_version(zhp, &spa_version) < 0)
 2432                 return (-1);
 2433 
 2434         needed_spa_version = zfs_spa_version_map(cb->cb_version);
 2435 
 2436         if (needed_spa_version < 0)
 2437                 return (-1);
 2438 
 2439         if (spa_version < needed_spa_version) {
 2440                 /* can't upgrade */
 2441                 (void) printf(gettext("%s: can not be "
 2442                     "upgraded; the pool version needs to first "
 2443                     "be upgraded\nto version %d\n\n"),
 2444                     zfs_get_name(zhp), needed_spa_version);
 2445                 cb->cb_numfailed++;
 2446                 return (0);
 2447         }
 2448 
 2449         /* upgrade */
 2450         if (version < cb->cb_version) {
 2451                 char verstr[24];
 2452                 (void) snprintf(verstr, sizeof (verstr),
 2453                     "%llu", (u_longlong_t)cb->cb_version);
 2454                 if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {
 2455                         /*
 2456                          * If they did "zfs upgrade -a", then we could
 2457                          * be doing ioctls to different pools.  We need
 2458                          * to log this history once to each pool, and bypass
 2459                          * the normal history logging that happens in main().
 2460                          */
 2461                         (void) zpool_log_history(g_zfs, history_str);
 2462                         log_history = B_FALSE;
 2463                 }
 2464                 if (zfs_prop_set(zhp, "version", verstr) == 0)
 2465                         cb->cb_numupgraded++;
 2466                 else
 2467                         cb->cb_numfailed++;
 2468                 (void) strlcpy(cb->cb_lastfs, zfs_get_name(zhp),
 2469                     sizeof (cb->cb_lastfs));
 2470         } else if (version > cb->cb_version) {
 2471                 /* can't downgrade */
 2472                 (void) printf(gettext("%s: can not be downgraded; "
 2473                     "it is already at version %u\n"),
 2474                     zfs_get_name(zhp), version);
 2475                 cb->cb_numfailed++;
 2476         } else {
 2477                 cb->cb_numsamegraded++;
 2478         }
 2479         return (0);
 2480 }
 2481 
 2482 /*
 2483  * zfs upgrade
 2484  * zfs upgrade -v
 2485  * zfs upgrade [-r] [-V <version>] <-a | filesystem>
 2486  */
 2487 static int
 2488 zfs_do_upgrade(int argc, char **argv)
 2489 {
 2490         boolean_t all = B_FALSE;
 2491         boolean_t showversions = B_FALSE;
 2492         int ret = 0;
 2493         upgrade_cbdata_t cb = { 0 };
 2494         int c;
 2495         int flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
 2496 
 2497         /* check options */
 2498         while ((c = getopt(argc, argv, "rvV:a")) != -1) {
 2499                 switch (c) {
 2500                 case 'r':
 2501                         flags |= ZFS_ITER_RECURSE;
 2502                         break;
 2503                 case 'v':
 2504                         showversions = B_TRUE;
 2505                         break;
 2506                 case 'V':
 2507                         if (zfs_prop_string_to_index(ZFS_PROP_VERSION,
 2508                             optarg, &cb.cb_version) != 0) {
 2509                                 (void) fprintf(stderr,
 2510                                     gettext("invalid version %s\n"), optarg);
 2511                                 usage(B_FALSE);
 2512                         }
 2513                         break;
 2514                 case 'a':
 2515                         all = B_TRUE;
 2516                         break;
 2517                 case '?':
 2518                 default:
 2519                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 2520                             optopt);
 2521                         usage(B_FALSE);
 2522                 }
 2523         }
 2524 
 2525         argc -= optind;
 2526         argv += optind;
 2527 
 2528         if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version))
 2529                 usage(B_FALSE);
 2530         if (showversions && (flags & ZFS_ITER_RECURSE || all ||
 2531             cb.cb_version || argc))
 2532                 usage(B_FALSE);
 2533         if ((all || argc) && (showversions))
 2534                 usage(B_FALSE);
 2535         if (all && argc)
 2536                 usage(B_FALSE);
 2537 
 2538         if (showversions) {
 2539                 /* Show info on available versions. */
 2540                 (void) printf(gettext("The following filesystem versions are "
 2541                     "supported:\n\n"));
 2542                 (void) printf(gettext("VER  DESCRIPTION\n"));
 2543                 (void) printf("---  -----------------------------------------"
 2544                     "---------------\n");
 2545                 (void) printf(gettext(" 1   Initial ZFS filesystem version\n"));
 2546                 (void) printf(gettext(" 2   Enhanced directory entries\n"));
 2547                 (void) printf(gettext(" 3   Case insensitive and filesystem "
 2548                     "user identifier (FUID)\n"));
 2549                 (void) printf(gettext(" 4   userquota, groupquota "
 2550                     "properties\n"));
 2551                 (void) printf(gettext(" 5   System attributes\n"));
 2552                 (void) printf(gettext("\nFor more information on a particular "
 2553                     "version, including supported releases,\n"));
 2554                 (void) printf("see the ZFS Administration Guide.\n\n");
 2555                 ret = 0;
 2556         } else if (argc || all) {
 2557                 /* Upgrade filesystems */
 2558                 if (cb.cb_version == 0)
 2559                         cb.cb_version = ZPL_VERSION;
 2560                 ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM,
 2561                     NULL, NULL, 0, upgrade_set_callback, &cb);
 2562                 (void) printf(gettext("%llu filesystems upgraded\n"),
 2563                     (u_longlong_t)cb.cb_numupgraded);
 2564                 if (cb.cb_numsamegraded) {
 2565                         (void) printf(gettext("%llu filesystems already at "
 2566                             "this version\n"),
 2567                             (u_longlong_t)cb.cb_numsamegraded);
 2568                 }
 2569                 if (cb.cb_numfailed != 0)
 2570                         ret = 1;
 2571         } else {
 2572                 /* List old-version filesystems */
 2573                 boolean_t found;
 2574                 (void) printf(gettext("This system is currently running "
 2575                     "ZFS filesystem version %llu.\n\n"), ZPL_VERSION);
 2576 
 2577                 flags |= ZFS_ITER_RECURSE;
 2578                 ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
 2579                     NULL, NULL, 0, upgrade_list_callback, &cb);
 2580 
 2581                 found = cb.cb_foundone;
 2582                 cb.cb_foundone = B_FALSE;
 2583                 cb.cb_newer = B_TRUE;
 2584 
 2585                 ret |= zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
 2586                     NULL, NULL, 0, upgrade_list_callback, &cb);
 2587 
 2588                 if (!cb.cb_foundone && !found) {
 2589                         (void) printf(gettext("All filesystems are "
 2590                             "formatted with the current version.\n"));
 2591                 }
 2592         }
 2593 
 2594         return (ret);
 2595 }
 2596 
 2597 /*
 2598  * zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
 2599  *               [-S field [-S field]...] [-t type[,...]]
 2600  *               filesystem | snapshot | path
 2601  * zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
 2602  *                [-S field [-S field]...] [-t type[,...]]
 2603  *                filesystem | snapshot | path
 2604  * zfs projectspace [-Hp] [-o field[,...]] [-s field [-s field]...]
 2605  *                [-S field [-S field]...] filesystem | snapshot | path
 2606  *
 2607  *      -H      Scripted mode; elide headers and separate columns by tabs.
 2608  *      -i      Translate SID to POSIX ID.
 2609  *      -n      Print numeric ID instead of user/group name.
 2610  *      -o      Control which fields to display.
 2611  *      -p      Use exact (parsable) numeric output.
 2612  *      -s      Specify sort columns, descending order.
 2613  *      -S      Specify sort columns, ascending order.
 2614  *      -t      Control which object types to display.
 2615  *
 2616  *      Displays space consumed by, and quotas on, each user in the specified
 2617  *      filesystem or snapshot.
 2618  */
 2619 
 2620 /* us_field_types, us_field_hdr and us_field_names should be kept in sync */
 2621 enum us_field_types {
 2622         USFIELD_TYPE,
 2623         USFIELD_NAME,
 2624         USFIELD_USED,
 2625         USFIELD_QUOTA,
 2626         USFIELD_OBJUSED,
 2627         USFIELD_OBJQUOTA
 2628 };
 2629 static const char *const us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA",
 2630                                     "OBJUSED", "OBJQUOTA" };
 2631 static const char *const us_field_names[] = { "type", "name", "used", "quota",
 2632                                     "objused", "objquota" };
 2633 #define USFIELD_LAST    (sizeof (us_field_names) / sizeof (char *))
 2634 
 2635 #define USTYPE_PSX_GRP  (1 << 0)
 2636 #define USTYPE_PSX_USR  (1 << 1)
 2637 #define USTYPE_SMB_GRP  (1 << 2)
 2638 #define USTYPE_SMB_USR  (1 << 3)
 2639 #define USTYPE_PROJ     (1 << 4)
 2640 #define USTYPE_ALL      \
 2641         (USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR | \
 2642             USTYPE_PROJ)
 2643 
 2644 static int us_type_bits[] = {
 2645         USTYPE_PSX_GRP,
 2646         USTYPE_PSX_USR,
 2647         USTYPE_SMB_GRP,
 2648         USTYPE_SMB_USR,
 2649         USTYPE_ALL
 2650 };
 2651 static const char *const us_type_names[] = { "posixgroup", "posixuser",
 2652         "smbgroup", "smbuser", "all" };
 2653 
 2654 typedef struct us_node {
 2655         nvlist_t        *usn_nvl;
 2656         uu_avl_node_t   usn_avlnode;
 2657         uu_list_node_t  usn_listnode;
 2658 } us_node_t;
 2659 
 2660 typedef struct us_cbdata {
 2661         nvlist_t        **cb_nvlp;
 2662         uu_avl_pool_t   *cb_avl_pool;
 2663         uu_avl_t        *cb_avl;
 2664         boolean_t       cb_numname;
 2665         boolean_t       cb_nicenum;
 2666         boolean_t       cb_sid2posix;
 2667         zfs_userquota_prop_t cb_prop;
 2668         zfs_sort_column_t *cb_sortcol;
 2669         size_t          cb_width[USFIELD_LAST];
 2670 } us_cbdata_t;
 2671 
 2672 static boolean_t us_populated = B_FALSE;
 2673 
 2674 typedef struct {
 2675         zfs_sort_column_t *si_sortcol;
 2676         boolean_t       si_numname;
 2677 } us_sort_info_t;
 2678 
 2679 static int
 2680 us_field_index(const char *field)
 2681 {
 2682         for (int i = 0; i < USFIELD_LAST; i++) {
 2683                 if (strcmp(field, us_field_names[i]) == 0)
 2684                         return (i);
 2685         }
 2686 
 2687         return (-1);
 2688 }
 2689 
 2690 static int
 2691 us_compare(const void *larg, const void *rarg, void *unused)
 2692 {
 2693         const us_node_t *l = larg;
 2694         const us_node_t *r = rarg;
 2695         us_sort_info_t *si = (us_sort_info_t *)unused;
 2696         zfs_sort_column_t *sortcol = si->si_sortcol;
 2697         boolean_t numname = si->si_numname;
 2698         nvlist_t *lnvl = l->usn_nvl;
 2699         nvlist_t *rnvl = r->usn_nvl;
 2700         int rc = 0;
 2701         boolean_t lvb, rvb;
 2702 
 2703         for (; sortcol != NULL; sortcol = sortcol->sc_next) {
 2704                 char *lvstr = (char *)"";
 2705                 char *rvstr = (char *)"";
 2706                 uint32_t lv32 = 0;
 2707                 uint32_t rv32 = 0;
 2708                 uint64_t lv64 = 0;
 2709                 uint64_t rv64 = 0;
 2710                 zfs_prop_t prop = sortcol->sc_prop;
 2711                 const char *propname = NULL;
 2712                 boolean_t reverse = sortcol->sc_reverse;
 2713 
 2714                 switch (prop) {
 2715                 case ZFS_PROP_TYPE:
 2716                         propname = "type";
 2717                         (void) nvlist_lookup_uint32(lnvl, propname, &lv32);
 2718                         (void) nvlist_lookup_uint32(rnvl, propname, &rv32);
 2719                         if (rv32 != lv32)
 2720                                 rc = (rv32 < lv32) ? 1 : -1;
 2721                         break;
 2722                 case ZFS_PROP_NAME:
 2723                         propname = "name";
 2724                         if (numname) {
 2725 compare_nums:
 2726                                 (void) nvlist_lookup_uint64(lnvl, propname,
 2727                                     &lv64);
 2728                                 (void) nvlist_lookup_uint64(rnvl, propname,
 2729                                     &rv64);
 2730                                 if (rv64 != lv64)
 2731                                         rc = (rv64 < lv64) ? 1 : -1;
 2732                         } else {
 2733                                 if ((nvlist_lookup_string(lnvl, propname,
 2734                                     &lvstr) == ENOENT) ||
 2735                                     (nvlist_lookup_string(rnvl, propname,
 2736                                     &rvstr) == ENOENT)) {
 2737                                         goto compare_nums;
 2738                                 }
 2739                                 rc = strcmp(lvstr, rvstr);
 2740                         }
 2741                         break;
 2742                 case ZFS_PROP_USED:
 2743                 case ZFS_PROP_QUOTA:
 2744                         if (!us_populated)
 2745                                 break;
 2746                         if (prop == ZFS_PROP_USED)
 2747                                 propname = "used";
 2748                         else
 2749                                 propname = "quota";
 2750                         (void) nvlist_lookup_uint64(lnvl, propname, &lv64);
 2751                         (void) nvlist_lookup_uint64(rnvl, propname, &rv64);
 2752                         if (rv64 != lv64)
 2753                                 rc = (rv64 < lv64) ? 1 : -1;
 2754                         break;
 2755 
 2756                 default:
 2757                         break;
 2758                 }
 2759 
 2760                 if (rc != 0) {
 2761                         if (rc < 0)
 2762                                 return (reverse ? 1 : -1);
 2763                         else
 2764                                 return (reverse ? -1 : 1);
 2765                 }
 2766         }
 2767 
 2768         /*
 2769          * If entries still seem to be the same, check if they are of the same
 2770          * type (smbentity is added only if we are doing SID to POSIX ID
 2771          * translation where we can have duplicate type/name combinations).
 2772          */
 2773         if (nvlist_lookup_boolean_value(lnvl, "smbentity", &lvb) == 0 &&
 2774             nvlist_lookup_boolean_value(rnvl, "smbentity", &rvb) == 0 &&
 2775             lvb != rvb)
 2776                 return (lvb < rvb ? -1 : 1);
 2777 
 2778         return (0);
 2779 }
 2780 
 2781 static boolean_t
 2782 zfs_prop_is_user(unsigned p)
 2783 {
 2784         return (p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA ||
 2785             p == ZFS_PROP_USEROBJUSED || p == ZFS_PROP_USEROBJQUOTA);
 2786 }
 2787 
 2788 static boolean_t
 2789 zfs_prop_is_group(unsigned p)
 2790 {
 2791         return (p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA ||
 2792             p == ZFS_PROP_GROUPOBJUSED || p == ZFS_PROP_GROUPOBJQUOTA);
 2793 }
 2794 
 2795 static boolean_t
 2796 zfs_prop_is_project(unsigned p)
 2797 {
 2798         return (p == ZFS_PROP_PROJECTUSED || p == ZFS_PROP_PROJECTQUOTA ||
 2799             p == ZFS_PROP_PROJECTOBJUSED || p == ZFS_PROP_PROJECTOBJQUOTA);
 2800 }
 2801 
 2802 static inline const char *
 2803 us_type2str(unsigned field_type)
 2804 {
 2805         switch (field_type) {
 2806         case USTYPE_PSX_USR:
 2807                 return ("POSIX User");
 2808         case USTYPE_PSX_GRP:
 2809                 return ("POSIX Group");
 2810         case USTYPE_SMB_USR:
 2811                 return ("SMB User");
 2812         case USTYPE_SMB_GRP:
 2813                 return ("SMB Group");
 2814         case USTYPE_PROJ:
 2815                 return ("Project");
 2816         default:
 2817                 return ("Undefined");
 2818         }
 2819 }
 2820 
 2821 static int
 2822 userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space)
 2823 {
 2824         us_cbdata_t *cb = (us_cbdata_t *)arg;
 2825         zfs_userquota_prop_t prop = cb->cb_prop;
 2826         char *name = NULL;
 2827         const char *propname;
 2828         char sizebuf[32];
 2829         us_node_t *node;
 2830         uu_avl_pool_t *avl_pool = cb->cb_avl_pool;
 2831         uu_avl_t *avl = cb->cb_avl;
 2832         uu_avl_index_t idx;
 2833         nvlist_t *props;
 2834         us_node_t *n;
 2835         zfs_sort_column_t *sortcol = cb->cb_sortcol;
 2836         unsigned type = 0;
 2837         const char *typestr;
 2838         size_t namelen;
 2839         size_t typelen;
 2840         size_t sizelen;
 2841         int typeidx, nameidx, sizeidx;
 2842         us_sort_info_t sortinfo = { sortcol, cb->cb_numname };
 2843         boolean_t smbentity = B_FALSE;
 2844 
 2845         if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
 2846                 nomem();
 2847         node = safe_malloc(sizeof (us_node_t));
 2848         uu_avl_node_init(node, &node->usn_avlnode, avl_pool);
 2849         node->usn_nvl = props;
 2850 
 2851         if (domain != NULL && domain[0] != '\0') {
 2852 #ifdef HAVE_IDMAP
 2853                 /* SMB */
 2854                 char sid[MAXNAMELEN + 32];
 2855                 uid_t id;
 2856                 uint64_t classes;
 2857                 int err;
 2858                 directory_error_t e;
 2859 
 2860                 smbentity = B_TRUE;
 2861 
 2862                 (void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid);
 2863 
 2864                 if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {
 2865                         type = USTYPE_SMB_GRP;
 2866                         err = sid_to_id(sid, B_FALSE, &id);
 2867                 } else {
 2868                         type = USTYPE_SMB_USR;
 2869                         err = sid_to_id(sid, B_TRUE, &id);
 2870                 }
 2871 
 2872                 if (err == 0) {
 2873                         rid = id;
 2874                         if (!cb->cb_sid2posix) {
 2875                                 e = directory_name_from_sid(NULL, sid, &name,
 2876                                     &classes);
 2877                                 if (e != NULL)
 2878                                         directory_error_free(e);
 2879                                 if (name == NULL)
 2880                                         name = sid;
 2881                         }
 2882                 }
 2883 #else
 2884                 nvlist_free(props);
 2885                 free(node);
 2886 
 2887                 return (-1);
 2888 #endif /* HAVE_IDMAP */
 2889         }
 2890 
 2891         if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') {
 2892                 /* POSIX or -i */
 2893                 if (zfs_prop_is_group(prop)) {
 2894                         type = USTYPE_PSX_GRP;
 2895                         if (!cb->cb_numname) {
 2896                                 struct group *g;
 2897 
 2898                                 if ((g = getgrgid(rid)) != NULL)
 2899                                         name = g->gr_name;
 2900                         }
 2901                 } else if (zfs_prop_is_user(prop)) {
 2902                         type = USTYPE_PSX_USR;
 2903                         if (!cb->cb_numname) {
 2904                                 struct passwd *p;
 2905 
 2906                                 if ((p = getpwuid(rid)) != NULL)
 2907                                         name = p->pw_name;
 2908                         }
 2909                 } else {
 2910                         type = USTYPE_PROJ;
 2911                 }
 2912         }
 2913 
 2914         /*
 2915          * Make sure that the type/name combination is unique when doing
 2916          * SID to POSIX ID translation (hence changing the type from SMB to
 2917          * POSIX).
 2918          */
 2919         if (cb->cb_sid2posix &&
 2920             nvlist_add_boolean_value(props, "smbentity", smbentity) != 0)
 2921                 nomem();
 2922 
 2923         /* Calculate/update width of TYPE field */
 2924         typestr = us_type2str(type);
 2925         typelen = strlen(gettext(typestr));
 2926         typeidx = us_field_index("type");
 2927         if (typelen > cb->cb_width[typeidx])
 2928                 cb->cb_width[typeidx] = typelen;
 2929         if (nvlist_add_uint32(props, "type", type) != 0)
 2930                 nomem();
 2931 
 2932         /* Calculate/update width of NAME field */
 2933         if ((cb->cb_numname && cb->cb_sid2posix) || name == NULL) {
 2934                 if (nvlist_add_uint64(props, "name", rid) != 0)
 2935                         nomem();
 2936                 namelen = snprintf(NULL, 0, "%u", rid);
 2937         } else {
 2938                 if (nvlist_add_string(props, "name", name) != 0)
 2939                         nomem();
 2940                 namelen = strlen(name);
 2941         }
 2942         nameidx = us_field_index("name");
 2943         if (nameidx >= 0 && namelen > cb->cb_width[nameidx])
 2944                 cb->cb_width[nameidx] = namelen;
 2945 
 2946         /*
 2947          * Check if this type/name combination is in the list and update it;
 2948          * otherwise add new node to the list.
 2949          */
 2950         if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) {
 2951                 uu_avl_insert(avl, node, idx);
 2952         } else {
 2953                 nvlist_free(props);
 2954                 free(node);
 2955                 node = n;
 2956                 props = node->usn_nvl;
 2957         }
 2958 
 2959         /* Calculate/update width of USED/QUOTA fields */
 2960         if (cb->cb_nicenum) {
 2961                 if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED ||
 2962                     prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA ||
 2963                     prop == ZFS_PROP_PROJECTUSED ||
 2964                     prop == ZFS_PROP_PROJECTQUOTA) {
 2965                         zfs_nicebytes(space, sizebuf, sizeof (sizebuf));
 2966                 } else {
 2967                         zfs_nicenum(space, sizebuf, sizeof (sizebuf));
 2968                 }
 2969         } else {
 2970                 (void) snprintf(sizebuf, sizeof (sizebuf), "%llu",
 2971                     (u_longlong_t)space);
 2972         }
 2973         sizelen = strlen(sizebuf);
 2974         if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED ||
 2975             prop == ZFS_PROP_PROJECTUSED) {
 2976                 propname = "used";
 2977                 if (!nvlist_exists(props, "quota"))
 2978                         (void) nvlist_add_uint64(props, "quota", 0);
 2979         } else if (prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA ||
 2980             prop == ZFS_PROP_PROJECTQUOTA) {
 2981                 propname = "quota";
 2982                 if (!nvlist_exists(props, "used"))
 2983                         (void) nvlist_add_uint64(props, "used", 0);
 2984         } else if (prop == ZFS_PROP_USEROBJUSED ||
 2985             prop == ZFS_PROP_GROUPOBJUSED || prop == ZFS_PROP_PROJECTOBJUSED) {
 2986                 propname = "objused";
 2987                 if (!nvlist_exists(props, "objquota"))
 2988                         (void) nvlist_add_uint64(props, "objquota", 0);
 2989         } else if (prop == ZFS_PROP_USEROBJQUOTA ||
 2990             prop == ZFS_PROP_GROUPOBJQUOTA ||
 2991             prop == ZFS_PROP_PROJECTOBJQUOTA) {
 2992                 propname = "objquota";
 2993                 if (!nvlist_exists(props, "objused"))
 2994                         (void) nvlist_add_uint64(props, "objused", 0);
 2995         } else {
 2996                 return (-1);
 2997         }
 2998         sizeidx = us_field_index(propname);
 2999         if (sizeidx >= 0 && sizelen > cb->cb_width[sizeidx])
 3000                 cb->cb_width[sizeidx] = sizelen;
 3001 
 3002         if (nvlist_add_uint64(props, propname, space) != 0)
 3003                 nomem();
 3004 
 3005         return (0);
 3006 }
 3007 
 3008 static void
 3009 print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types,
 3010     size_t *width, us_node_t *node)
 3011 {
 3012         nvlist_t *nvl = node->usn_nvl;
 3013         char valstr[MAXNAMELEN];
 3014         boolean_t first = B_TRUE;
 3015         int cfield = 0;
 3016         int field;
 3017         uint32_t ustype;
 3018 
 3019         /* Check type */
 3020         (void) nvlist_lookup_uint32(nvl, "type", &ustype);
 3021         if (!(ustype & types))
 3022                 return;
 3023 
 3024         while ((field = fields[cfield]) != USFIELD_LAST) {
 3025                 nvpair_t *nvp = NULL;
 3026                 data_type_t type;
 3027                 uint32_t val32 = -1;
 3028                 uint64_t val64 = -1;
 3029                 const char *strval = "-";
 3030 
 3031                 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL)
 3032                         if (strcmp(nvpair_name(nvp),
 3033                             us_field_names[field]) == 0)
 3034                                 break;
 3035 
 3036                 type = nvp == NULL ? DATA_TYPE_UNKNOWN : nvpair_type(nvp);
 3037                 switch (type) {
 3038                 case DATA_TYPE_UINT32:
 3039                         val32 = fnvpair_value_uint32(nvp);
 3040                         break;
 3041                 case DATA_TYPE_UINT64:
 3042                         val64 = fnvpair_value_uint64(nvp);
 3043                         break;
 3044                 case DATA_TYPE_STRING:
 3045                         strval = fnvpair_value_string(nvp);
 3046                         break;
 3047                 case DATA_TYPE_UNKNOWN:
 3048                         break;
 3049                 default:
 3050                         (void) fprintf(stderr, "invalid data type\n");
 3051                 }
 3052 
 3053                 switch (field) {
 3054                 case USFIELD_TYPE:
 3055                         if (type == DATA_TYPE_UINT32)
 3056                                 strval = us_type2str(val32);
 3057                         break;
 3058                 case USFIELD_NAME:
 3059                         if (type == DATA_TYPE_UINT64) {
 3060                                 (void) sprintf(valstr, "%llu",
 3061                                     (u_longlong_t)val64);
 3062                                 strval = valstr;
 3063                         }
 3064                         break;
 3065                 case USFIELD_USED:
 3066                 case USFIELD_QUOTA:
 3067                         if (type == DATA_TYPE_UINT64) {
 3068                                 if (parsable) {
 3069                                         (void) sprintf(valstr, "%llu",
 3070                                             (u_longlong_t)val64);
 3071                                         strval = valstr;
 3072                                 } else if (field == USFIELD_QUOTA &&
 3073                                     val64 == 0) {
 3074                                         strval = "none";
 3075                                 } else {
 3076                                         zfs_nicebytes(val64, valstr,
 3077                                             sizeof (valstr));
 3078                                         strval = valstr;
 3079                                 }
 3080                         }
 3081                         break;
 3082                 case USFIELD_OBJUSED:
 3083                 case USFIELD_OBJQUOTA:
 3084                         if (type == DATA_TYPE_UINT64) {
 3085                                 if (parsable) {
 3086                                         (void) sprintf(valstr, "%llu",
 3087                                             (u_longlong_t)val64);
 3088                                         strval = valstr;
 3089                                 } else if (field == USFIELD_OBJQUOTA &&
 3090                                     val64 == 0) {
 3091                                         strval = "none";
 3092                                 } else {
 3093                                         zfs_nicenum(val64, valstr,
 3094                                             sizeof (valstr));
 3095                                         strval = valstr;
 3096                                 }
 3097                         }
 3098                         break;
 3099                 }
 3100 
 3101                 if (!first) {
 3102                         if (scripted)
 3103                                 (void) putchar('\t');
 3104                         else
 3105                                 (void) fputs("  ", stdout);
 3106                 }
 3107                 if (scripted)
 3108                         (void) fputs(strval, stdout);
 3109                 else if (field == USFIELD_TYPE || field == USFIELD_NAME)
 3110                         (void) printf("%-*s", (int)width[field], strval);
 3111                 else
 3112                         (void) printf("%*s", (int)width[field], strval);
 3113 
 3114                 first = B_FALSE;
 3115                 cfield++;
 3116         }
 3117 
 3118         (void) putchar('\n');
 3119 }
 3120 
 3121 static void
 3122 print_us(boolean_t scripted, boolean_t parsable, int *fields, int types,
 3123     size_t *width, boolean_t rmnode, uu_avl_t *avl)
 3124 {
 3125         us_node_t *node;
 3126         const char *col;
 3127         int cfield = 0;
 3128         int field;
 3129 
 3130         if (!scripted) {
 3131                 boolean_t first = B_TRUE;
 3132 
 3133                 while ((field = fields[cfield]) != USFIELD_LAST) {
 3134                         col = gettext(us_field_hdr[field]);
 3135                         if (field == USFIELD_TYPE || field == USFIELD_NAME) {
 3136                                 (void) printf(first ? "%-*s" : "  %-*s",
 3137                                     (int)width[field], col);
 3138                         } else {
 3139                                 (void) printf(first ? "%*s" : "  %*s",
 3140                                     (int)width[field], col);
 3141                         }
 3142                         first = B_FALSE;
 3143                         cfield++;
 3144                 }
 3145                 (void) printf("\n");
 3146         }
 3147 
 3148         for (node = uu_avl_first(avl); node; node = uu_avl_next(avl, node)) {
 3149                 print_us_node(scripted, parsable, fields, types, width, node);
 3150                 if (rmnode)
 3151                         nvlist_free(node->usn_nvl);
 3152         }
 3153 }
 3154 
 3155 static int
 3156 zfs_do_userspace(int argc, char **argv)
 3157 {
 3158         zfs_handle_t *zhp;
 3159         zfs_userquota_prop_t p;
 3160         uu_avl_pool_t *avl_pool;
 3161         uu_avl_t *avl_tree;
 3162         uu_avl_walk_t *walk;
 3163         char *delim;
 3164         char deffields[] = "type,name,used,quota,objused,objquota";
 3165         char *ofield = NULL;
 3166         char *tfield = NULL;
 3167         int cfield = 0;
 3168         int fields[256];
 3169         int i;
 3170         boolean_t scripted = B_FALSE;
 3171         boolean_t prtnum = B_FALSE;
 3172         boolean_t parsable = B_FALSE;
 3173         boolean_t sid2posix = B_FALSE;
 3174         int ret = 0;
 3175         int c;
 3176         zfs_sort_column_t *sortcol = NULL;
 3177         int types = USTYPE_PSX_USR | USTYPE_SMB_USR;
 3178         us_cbdata_t cb;
 3179         us_node_t *node;
 3180         us_node_t *rmnode;
 3181         uu_list_pool_t *listpool;
 3182         uu_list_t *list;
 3183         uu_avl_index_t idx = 0;
 3184         uu_list_index_t idx2 = 0;
 3185 
 3186         if (argc < 2)
 3187                 usage(B_FALSE);
 3188 
 3189         if (strcmp(argv[0], "groupspace") == 0) {
 3190                 /* Toggle default group types */
 3191                 types = USTYPE_PSX_GRP | USTYPE_SMB_GRP;
 3192         } else if (strcmp(argv[0], "projectspace") == 0) {
 3193                 types = USTYPE_PROJ;
 3194                 prtnum = B_TRUE;
 3195         }
 3196 
 3197         while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) {
 3198                 switch (c) {
 3199                 case 'n':
 3200                         if (types == USTYPE_PROJ) {
 3201                                 (void) fprintf(stderr,
 3202                                     gettext("invalid option 'n'\n"));
 3203                                 usage(B_FALSE);
 3204                         }
 3205                         prtnum = B_TRUE;
 3206                         break;
 3207                 case 'H':
 3208                         scripted = B_TRUE;
 3209                         break;
 3210                 case 'p':
 3211                         parsable = B_TRUE;
 3212                         break;
 3213                 case 'o':
 3214                         ofield = optarg;
 3215                         break;
 3216                 case 's':
 3217                 case 'S':
 3218                         if (zfs_add_sort_column(&sortcol, optarg,
 3219                             c == 's' ? B_FALSE : B_TRUE) != 0) {
 3220                                 (void) fprintf(stderr,
 3221                                     gettext("invalid field '%s'\n"), optarg);
 3222                                 usage(B_FALSE);
 3223                         }
 3224                         break;
 3225                 case 't':
 3226                         if (types == USTYPE_PROJ) {
 3227                                 (void) fprintf(stderr,
 3228                                     gettext("invalid option 't'\n"));
 3229                                 usage(B_FALSE);
 3230                         }
 3231                         tfield = optarg;
 3232                         break;
 3233                 case 'i':
 3234                         if (types == USTYPE_PROJ) {
 3235                                 (void) fprintf(stderr,
 3236                                     gettext("invalid option 'i'\n"));
 3237                                 usage(B_FALSE);
 3238                         }
 3239                         sid2posix = B_TRUE;
 3240                         break;
 3241                 case ':':
 3242                         (void) fprintf(stderr, gettext("missing argument for "
 3243                             "'%c' option\n"), optopt);
 3244                         usage(B_FALSE);
 3245                         break;
 3246                 case '?':
 3247                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 3248                             optopt);
 3249                         usage(B_FALSE);
 3250                 }
 3251         }
 3252 
 3253         argc -= optind;
 3254         argv += optind;
 3255 
 3256         if (argc < 1) {
 3257                 (void) fprintf(stderr, gettext("missing dataset name\n"));
 3258                 usage(B_FALSE);
 3259         }
 3260         if (argc > 1) {
 3261                 (void) fprintf(stderr, gettext("too many arguments\n"));
 3262                 usage(B_FALSE);
 3263         }
 3264 
 3265         /* Use default output fields if not specified using -o */
 3266         if (ofield == NULL)
 3267                 ofield = deffields;
 3268         do {
 3269                 if ((delim = strchr(ofield, ',')) != NULL)
 3270                         *delim = '\0';
 3271                 if ((fields[cfield++] = us_field_index(ofield)) == -1) {
 3272                         (void) fprintf(stderr, gettext("invalid type '%s' "
 3273                             "for -o option\n"), ofield);
 3274                         return (-1);
 3275                 }
 3276                 if (delim != NULL)
 3277                         ofield = delim + 1;
 3278         } while (delim != NULL);
 3279         fields[cfield] = USFIELD_LAST;
 3280 
 3281         /* Override output types (-t option) */
 3282         if (tfield != NULL) {
 3283                 types = 0;
 3284 
 3285                 do {
 3286                         boolean_t found = B_FALSE;
 3287 
 3288                         if ((delim = strchr(tfield, ',')) != NULL)
 3289                                 *delim = '\0';
 3290                         for (i = 0; i < sizeof (us_type_bits) / sizeof (int);
 3291                             i++) {
 3292                                 if (strcmp(tfield, us_type_names[i]) == 0) {
 3293                                         found = B_TRUE;
 3294                                         types |= us_type_bits[i];
 3295                                         break;
 3296                                 }
 3297                         }
 3298                         if (!found) {
 3299                                 (void) fprintf(stderr, gettext("invalid type "
 3300                                     "'%s' for -t option\n"), tfield);
 3301                                 return (-1);
 3302                         }
 3303                         if (delim != NULL)
 3304                                 tfield = delim + 1;
 3305                 } while (delim != NULL);
 3306         }
 3307 
 3308         if ((zhp = zfs_path_to_zhandle(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM |
 3309             ZFS_TYPE_SNAPSHOT)) == NULL)
 3310                 return (1);
 3311         if (zfs_get_underlying_type(zhp) != ZFS_TYPE_FILESYSTEM) {
 3312                 (void) fprintf(stderr, gettext("operation is only applicable "
 3313                     "to filesystems and their snapshots\n"));
 3314                 zfs_close(zhp);
 3315                 return (1);
 3316         }
 3317 
 3318         if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t),
 3319             offsetof(us_node_t, usn_avlnode), us_compare, UU_DEFAULT)) == NULL)
 3320                 nomem();
 3321         if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL)
 3322                 nomem();
 3323 
 3324         /* Always add default sorting columns */
 3325         (void) zfs_add_sort_column(&sortcol, "type", B_FALSE);
 3326         (void) zfs_add_sort_column(&sortcol, "name", B_FALSE);
 3327 
 3328         cb.cb_sortcol = sortcol;
 3329         cb.cb_numname = prtnum;
 3330         cb.cb_nicenum = !parsable;
 3331         cb.cb_avl_pool = avl_pool;
 3332         cb.cb_avl = avl_tree;
 3333         cb.cb_sid2posix = sid2posix;
 3334 
 3335         for (i = 0; i < USFIELD_LAST; i++)
 3336                 cb.cb_width[i] = strlen(gettext(us_field_hdr[i]));
 3337 
 3338         for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) {
 3339                 if ((zfs_prop_is_user(p) &&
 3340                     !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) ||
 3341                     (zfs_prop_is_group(p) &&
 3342                     !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))) ||
 3343                     (zfs_prop_is_project(p) && types != USTYPE_PROJ))
 3344                         continue;
 3345 
 3346                 cb.cb_prop = p;
 3347                 if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0) {
 3348                         zfs_close(zhp);
 3349                         return (ret);
 3350                 }
 3351         }
 3352         zfs_close(zhp);
 3353 
 3354         /* Sort the list */
 3355         if ((node = uu_avl_first(avl_tree)) == NULL)
 3356                 return (0);
 3357 
 3358         us_populated = B_TRUE;
 3359 
 3360         listpool = uu_list_pool_create("tmplist", sizeof (us_node_t),
 3361             offsetof(us_node_t, usn_listnode), NULL, UU_DEFAULT);
 3362         list = uu_list_create(listpool, NULL, UU_DEFAULT);
 3363         uu_list_node_init(node, &node->usn_listnode, listpool);
 3364 
 3365         while (node != NULL) {
 3366                 rmnode = node;
 3367                 node = uu_avl_next(avl_tree, node);
 3368                 uu_avl_remove(avl_tree, rmnode);
 3369                 if (uu_list_find(list, rmnode, NULL, &idx2) == NULL)
 3370                         uu_list_insert(list, rmnode, idx2);
 3371         }
 3372 
 3373         for (node = uu_list_first(list); node != NULL;
 3374             node = uu_list_next(list, node)) {
 3375                 us_sort_info_t sortinfo = { sortcol, cb.cb_numname };
 3376 
 3377                 if (uu_avl_find(avl_tree, node, &sortinfo, &idx) == NULL)
 3378                         uu_avl_insert(avl_tree, node, idx);
 3379         }
 3380 
 3381         uu_list_destroy(list);
 3382         uu_list_pool_destroy(listpool);
 3383 
 3384         /* Print and free node nvlist memory */
 3385         print_us(scripted, parsable, fields, types, cb.cb_width, B_TRUE,
 3386             cb.cb_avl);
 3387 
 3388         zfs_free_sort_columns(sortcol);
 3389 
 3390         /* Clean up the AVL tree */
 3391         if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL)
 3392                 nomem();
 3393 
 3394         while ((node = uu_avl_walk_next(walk)) != NULL) {
 3395                 uu_avl_remove(cb.cb_avl, node);
 3396                 free(node);
 3397         }
 3398 
 3399         uu_avl_walk_end(walk);
 3400         uu_avl_destroy(avl_tree);
 3401         uu_avl_pool_destroy(avl_pool);
 3402 
 3403         return (ret);
 3404 }
 3405 
 3406 /*
 3407  * list [-Hp][-r|-d max] [-o property[,...]] [-s property] ... [-S property]
 3408  *      [-t type[,...]] [filesystem|volume|snapshot] ...
 3409  *
 3410  *      -H      Scripted mode; elide headers and separate columns by tabs
 3411  *      -p      Display values in parsable (literal) format.
 3412  *      -r      Recurse over all children
 3413  *      -d      Limit recursion by depth.
 3414  *      -o      Control which fields to display.
 3415  *      -s      Specify sort columns, descending order.
 3416  *      -S      Specify sort columns, ascending order.
 3417  *      -t      Control which object types to display.
 3418  *
 3419  * When given no arguments, list all filesystems in the system.
 3420  * Otherwise, list the specified datasets, optionally recursing down them if
 3421  * '-r' is specified.
 3422  */
 3423 typedef struct list_cbdata {
 3424         boolean_t       cb_first;
 3425         boolean_t       cb_literal;
 3426         boolean_t       cb_scripted;
 3427         zprop_list_t    *cb_proplist;
 3428 } list_cbdata_t;
 3429 
 3430 /*
 3431  * Given a list of columns to display, output appropriate headers for each one.
 3432  */
 3433 static void
 3434 print_header(list_cbdata_t *cb)
 3435 {
 3436         zprop_list_t *pl = cb->cb_proplist;
 3437         char headerbuf[ZFS_MAXPROPLEN];
 3438         const char *header;
 3439         int i;
 3440         boolean_t first = B_TRUE;
 3441         boolean_t right_justify;
 3442 
 3443         for (; pl != NULL; pl = pl->pl_next) {
 3444                 if (!first) {
 3445                         (void) printf("  ");
 3446                 } else {
 3447                         first = B_FALSE;
 3448                 }
 3449 
 3450                 right_justify = B_FALSE;
 3451                 if (pl->pl_prop != ZPROP_USERPROP) {
 3452                         header = zfs_prop_column_name(pl->pl_prop);
 3453                         right_justify = zfs_prop_align_right(pl->pl_prop);
 3454                 } else {
 3455                         for (i = 0; pl->pl_user_prop[i] != '\0'; i++)
 3456                                 headerbuf[i] = toupper(pl->pl_user_prop[i]);
 3457                         headerbuf[i] = '\0';
 3458                         header = headerbuf;
 3459                 }
 3460 
 3461                 if (pl->pl_next == NULL && !right_justify)
 3462                         (void) printf("%s", header);
 3463                 else if (right_justify)
 3464                         (void) printf("%*s", (int)pl->pl_width, header);
 3465                 else
 3466                         (void) printf("%-*s", (int)pl->pl_width, header);
 3467         }
 3468 
 3469         (void) printf("\n");
 3470 }
 3471 
 3472 /*
 3473  * Given a dataset and a list of fields, print out all the properties according
 3474  * to the described layout.
 3475  */
 3476 static void
 3477 print_dataset(zfs_handle_t *zhp, list_cbdata_t *cb)
 3478 {
 3479         zprop_list_t *pl = cb->cb_proplist;
 3480         boolean_t first = B_TRUE;
 3481         char property[ZFS_MAXPROPLEN];
 3482         nvlist_t *userprops = zfs_get_user_props(zhp);
 3483         nvlist_t *propval;
 3484         const char *propstr;
 3485         boolean_t right_justify;
 3486 
 3487         for (; pl != NULL; pl = pl->pl_next) {
 3488                 if (!first) {
 3489                         if (cb->cb_scripted)
 3490                                 (void) putchar('\t');
 3491                         else
 3492                                 (void) fputs("  ", stdout);
 3493                 } else {
 3494                         first = B_FALSE;
 3495                 }
 3496 
 3497                 if (pl->pl_prop == ZFS_PROP_NAME) {
 3498                         (void) strlcpy(property, zfs_get_name(zhp),
 3499                             sizeof (property));
 3500                         propstr = property;
 3501                         right_justify = zfs_prop_align_right(pl->pl_prop);
 3502                 } else if (pl->pl_prop != ZPROP_USERPROP) {
 3503                         if (zfs_prop_get(zhp, pl->pl_prop, property,
 3504                             sizeof (property), NULL, NULL, 0,
 3505                             cb->cb_literal) != 0)
 3506                                 propstr = "-";
 3507                         else
 3508                                 propstr = property;
 3509                         right_justify = zfs_prop_align_right(pl->pl_prop);
 3510                 } else if (zfs_prop_userquota(pl->pl_user_prop)) {
 3511                         if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
 3512                             property, sizeof (property), cb->cb_literal) != 0)
 3513                                 propstr = "-";
 3514                         else
 3515                                 propstr = property;
 3516                         right_justify = B_TRUE;
 3517                 } else if (zfs_prop_written(pl->pl_user_prop)) {
 3518                         if (zfs_prop_get_written(zhp, pl->pl_user_prop,
 3519                             property, sizeof (property), cb->cb_literal) != 0)
 3520                                 propstr = "-";
 3521                         else
 3522                                 propstr = property;
 3523                         right_justify = B_TRUE;
 3524                 } else {
 3525                         if (nvlist_lookup_nvlist(userprops,
 3526                             pl->pl_user_prop, &propval) != 0)
 3527                                 propstr = "-";
 3528                         else
 3529                                 propstr = fnvlist_lookup_string(propval,
 3530                                     ZPROP_VALUE);
 3531                         right_justify = B_FALSE;
 3532                 }
 3533 
 3534                 /*
 3535                  * If this is being called in scripted mode, or if this is the
 3536                  * last column and it is left-justified, don't include a width
 3537                  * format specifier.
 3538                  */
 3539                 if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify))
 3540                         (void) fputs(propstr, stdout);
 3541                 else if (right_justify)
 3542                         (void) printf("%*s", (int)pl->pl_width, propstr);
 3543                 else
 3544                         (void) printf("%-*s", (int)pl->pl_width, propstr);
 3545         }
 3546 
 3547         (void) putchar('\n');
 3548 }
 3549 
 3550 /*
 3551  * Generic callback function to list a dataset or snapshot.
 3552  */
 3553 static int
 3554 list_callback(zfs_handle_t *zhp, void *data)
 3555 {
 3556         list_cbdata_t *cbp = data;
 3557 
 3558         if (cbp->cb_first) {
 3559                 if (!cbp->cb_scripted)
 3560                         print_header(cbp);
 3561                 cbp->cb_first = B_FALSE;
 3562         }
 3563 
 3564         print_dataset(zhp, cbp);
 3565 
 3566         return (0);
 3567 }
 3568 
 3569 static int
 3570 zfs_do_list(int argc, char **argv)
 3571 {
 3572         int c;
 3573         char default_fields[] =
 3574             "name,used,available,referenced,mountpoint";
 3575         int types = ZFS_TYPE_DATASET;
 3576         boolean_t types_specified = B_FALSE;
 3577         char *fields = default_fields;
 3578         list_cbdata_t cb = { 0 };
 3579         int limit = 0;
 3580         int ret = 0;
 3581         zfs_sort_column_t *sortcol = NULL;
 3582         int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS;
 3583 
 3584         /* check options */
 3585         while ((c = getopt(argc, argv, "HS:d:o:prs:t:")) != -1) {
 3586                 switch (c) {
 3587                 case 'o':
 3588                         fields = optarg;
 3589                         break;
 3590                 case 'p':
 3591                         cb.cb_literal = B_TRUE;
 3592                         flags |= ZFS_ITER_LITERAL_PROPS;
 3593                         break;
 3594                 case 'd':
 3595                         limit = parse_depth(optarg, &flags);
 3596                         break;
 3597                 case 'r':
 3598                         flags |= ZFS_ITER_RECURSE;
 3599                         break;
 3600                 case 'H':
 3601                         cb.cb_scripted = B_TRUE;
 3602                         break;
 3603                 case 's':
 3604                         if (zfs_add_sort_column(&sortcol, optarg,
 3605                             B_FALSE) != 0) {
 3606                                 (void) fprintf(stderr,
 3607                                     gettext("invalid property '%s'\n"), optarg);
 3608                                 usage(B_FALSE);
 3609                         }
 3610                         break;
 3611                 case 'S':
 3612                         if (zfs_add_sort_column(&sortcol, optarg,
 3613                             B_TRUE) != 0) {
 3614                                 (void) fprintf(stderr,
 3615                                     gettext("invalid property '%s'\n"), optarg);
 3616                                 usage(B_FALSE);
 3617                         }
 3618                         break;
 3619                 case 't':
 3620                         types = 0;
 3621                         types_specified = B_TRUE;
 3622                         flags &= ~ZFS_ITER_PROP_LISTSNAPS;
 3623 
 3624                         for (char *tok; (tok = strsep(&optarg, ",")); ) {
 3625                                 static const char *const type_subopts[] = {
 3626                                         "filesystem", "volume",
 3627                                         "snapshot", "snap",
 3628                                         "bookmark",
 3629                                         "all" };
 3630                                 static const int type_types[] = {
 3631                                         ZFS_TYPE_FILESYSTEM, ZFS_TYPE_VOLUME,
 3632                                         ZFS_TYPE_SNAPSHOT, ZFS_TYPE_SNAPSHOT,
 3633                                         ZFS_TYPE_BOOKMARK,
 3634                                         ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK };
 3635 
 3636                                 for (c = 0; c < ARRAY_SIZE(type_subopts); ++c)
 3637                                         if (strcmp(tok, type_subopts[c]) == 0) {
 3638                                                 types |= type_types[c];
 3639                                                 goto found3;
 3640                                         }
 3641 
 3642                                 (void) fprintf(stderr,
 3643                                     gettext("invalid type '%s'\n"), tok);
 3644                                 usage(B_FALSE);
 3645 found3:;
 3646                         }
 3647                         break;
 3648                 case ':':
 3649                         (void) fprintf(stderr, gettext("missing argument for "
 3650                             "'%c' option\n"), optopt);
 3651                         usage(B_FALSE);
 3652                         break;
 3653                 case '?':
 3654                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 3655                             optopt);
 3656                         usage(B_FALSE);
 3657                 }
 3658         }
 3659 
 3660         argc -= optind;
 3661         argv += optind;
 3662 
 3663         /*
 3664          * If "-o space" and no types were specified, don't display snapshots.
 3665          */
 3666         if (strcmp(fields, "space") == 0 && types_specified == B_FALSE)
 3667                 types &= ~ZFS_TYPE_SNAPSHOT;
 3668 
 3669         /*
 3670          * Handle users who want to list all snapshots or bookmarks
 3671          * of the current dataset (ex. 'zfs list -t snapshot <dataset>').
 3672          */
 3673         if ((types == ZFS_TYPE_SNAPSHOT || types == ZFS_TYPE_BOOKMARK) &&
 3674             argc > 0 && (flags & ZFS_ITER_RECURSE) == 0 && limit == 0) {
 3675                 flags |= (ZFS_ITER_DEPTH_LIMIT | ZFS_ITER_RECURSE);
 3676                 limit = 1;
 3677         }
 3678 
 3679         /*
 3680          * If the user specifies '-o all', the zprop_get_list() doesn't
 3681          * normally include the name of the dataset.  For 'zfs list', we always
 3682          * want this property to be first.
 3683          */
 3684         if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
 3685             != 0)
 3686                 usage(B_FALSE);
 3687 
 3688         cb.cb_first = B_TRUE;
 3689 
 3690         /*
 3691          * If we are only going to list and sort by properties that are "fast"
 3692          * then we can use "simple" mode and avoid populating the properties
 3693          * nvlist.
 3694          */
 3695         if (zfs_list_only_by_fast(cb.cb_proplist) &&
 3696             zfs_sort_only_by_fast(sortcol))
 3697                 flags |= ZFS_ITER_SIMPLE;
 3698 
 3699         ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist,
 3700             limit, list_callback, &cb);
 3701 
 3702         zprop_free_list(cb.cb_proplist);
 3703         zfs_free_sort_columns(sortcol);
 3704 
 3705         if (ret == 0 && cb.cb_first && !cb.cb_scripted)
 3706                 (void) fprintf(stderr, gettext("no datasets available\n"));
 3707 
 3708         return (ret);
 3709 }
 3710 
 3711 /*
 3712  * zfs rename [-fu] <fs | snap | vol> <fs | snap | vol>
 3713  * zfs rename [-f] -p <fs | vol> <fs | vol>
 3714  * zfs rename [-u] -r <snap> <snap>
 3715  *
 3716  * Renames the given dataset to another of the same type.
 3717  *
 3718  * The '-p' flag creates all the non-existing ancestors of the target first.
 3719  * The '-u' flag prevents file systems from being remounted during rename.
 3720  */
 3721 static int
 3722 zfs_do_rename(int argc, char **argv)
 3723 {
 3724         zfs_handle_t *zhp;
 3725         renameflags_t flags = { 0 };
 3726         int c;
 3727         int ret = 0;
 3728         int types;
 3729         boolean_t parents = B_FALSE;
 3730 
 3731         /* check options */
 3732         while ((c = getopt(argc, argv, "pruf")) != -1) {
 3733                 switch (c) {
 3734                 case 'p':
 3735                         parents = B_TRUE;
 3736                         break;
 3737                 case 'r':
 3738                         flags.recursive = B_TRUE;
 3739                         break;
 3740                 case 'u':
 3741                         flags.nounmount = B_TRUE;
 3742                         break;
 3743                 case 'f':
 3744                         flags.forceunmount = B_TRUE;
 3745                         break;
 3746                 case '?':
 3747                 default:
 3748                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 3749                             optopt);
 3750                         usage(B_FALSE);
 3751                 }
 3752         }
 3753 
 3754         argc -= optind;
 3755         argv += optind;
 3756 
 3757         /* check number of arguments */
 3758         if (argc < 1) {
 3759                 (void) fprintf(stderr, gettext("missing source dataset "
 3760                     "argument\n"));
 3761                 usage(B_FALSE);
 3762         }
 3763         if (argc < 2) {
 3764                 (void) fprintf(stderr, gettext("missing target dataset "
 3765                     "argument\n"));
 3766                 usage(B_FALSE);
 3767         }
 3768         if (argc > 2) {
 3769                 (void) fprintf(stderr, gettext("too many arguments\n"));
 3770                 usage(B_FALSE);
 3771         }
 3772 
 3773         if (flags.recursive && parents) {
 3774                 (void) fprintf(stderr, gettext("-p and -r options are mutually "
 3775                     "exclusive\n"));
 3776                 usage(B_FALSE);
 3777         }
 3778 
 3779         if (flags.nounmount && parents) {
 3780                 (void) fprintf(stderr, gettext("-u and -p options are mutually "
 3781                     "exclusive\n"));
 3782                 usage(B_FALSE);
 3783         }
 3784 
 3785         if (flags.recursive && strchr(argv[0], '@') == 0) {
 3786                 (void) fprintf(stderr, gettext("source dataset for recursive "
 3787                     "rename must be a snapshot\n"));
 3788                 usage(B_FALSE);
 3789         }
 3790 
 3791         if (flags.nounmount)
 3792                 types = ZFS_TYPE_FILESYSTEM;
 3793         else if (parents)
 3794                 types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
 3795         else
 3796                 types = ZFS_TYPE_DATASET;
 3797 
 3798         if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL)
 3799                 return (1);
 3800 
 3801         /* If we were asked and the name looks good, try to create ancestors. */
 3802         if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) &&
 3803             zfs_create_ancestors(g_zfs, argv[1]) != 0) {
 3804                 zfs_close(zhp);
 3805                 return (1);
 3806         }
 3807 
 3808         ret = (zfs_rename(zhp, argv[1], flags) != 0);
 3809 
 3810         zfs_close(zhp);
 3811         return (ret);
 3812 }
 3813 
 3814 /*
 3815  * zfs promote <fs>
 3816  *
 3817  * Promotes the given clone fs to be the parent
 3818  */
 3819 static int
 3820 zfs_do_promote(int argc, char **argv)
 3821 {
 3822         zfs_handle_t *zhp;
 3823         int ret = 0;
 3824 
 3825         /* check options */
 3826         if (argc > 1 && argv[1][0] == '-') {
 3827                 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 3828                     argv[1][1]);
 3829                 usage(B_FALSE);
 3830         }
 3831 
 3832         /* check number of arguments */
 3833         if (argc < 2) {
 3834                 (void) fprintf(stderr, gettext("missing clone filesystem"
 3835                     " argument\n"));
 3836                 usage(B_FALSE);
 3837         }
 3838         if (argc > 2) {
 3839                 (void) fprintf(stderr, gettext("too many arguments\n"));
 3840                 usage(B_FALSE);
 3841         }
 3842 
 3843         zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
 3844         if (zhp == NULL)
 3845                 return (1);
 3846 
 3847         ret = (zfs_promote(zhp) != 0);
 3848 
 3849 
 3850         zfs_close(zhp);
 3851         return (ret);
 3852 }
 3853 
 3854 static int
 3855 zfs_do_redact(int argc, char **argv)
 3856 {
 3857         char *snap = NULL;
 3858         char *bookname = NULL;
 3859         char **rsnaps = NULL;
 3860         int numrsnaps = 0;
 3861         argv++;
 3862         argc--;
 3863         if (argc < 3) {
 3864                 (void) fprintf(stderr, gettext("too few arguments\n"));
 3865                 usage(B_FALSE);
 3866         }
 3867 
 3868         snap = argv[0];
 3869         bookname = argv[1];
 3870         rsnaps = argv + 2;
 3871         numrsnaps = argc - 2;
 3872 
 3873         nvlist_t *rsnapnv = fnvlist_alloc();
 3874 
 3875         for (int i = 0; i < numrsnaps; i++) {
 3876                 fnvlist_add_boolean(rsnapnv, rsnaps[i]);
 3877         }
 3878 
 3879         int err = lzc_redact(snap, bookname, rsnapnv);
 3880         fnvlist_free(rsnapnv);
 3881 
 3882         switch (err) {
 3883         case 0:
 3884                 break;
 3885         case ENOENT:
 3886                 (void) fprintf(stderr,
 3887                     gettext("provided snapshot %s does not exist\n"), snap);
 3888                 break;
 3889         case EEXIST:
 3890                 (void) fprintf(stderr, gettext("specified redaction bookmark "
 3891                     "(%s) provided already exists\n"), bookname);
 3892                 break;
 3893         case ENAMETOOLONG:
 3894                 (void) fprintf(stderr, gettext("provided bookmark name cannot "
 3895                     "be used, final name would be too long\n"));
 3896                 break;
 3897         case E2BIG:
 3898                 (void) fprintf(stderr, gettext("too many redaction snapshots "
 3899                     "specified\n"));
 3900                 break;
 3901         case EINVAL:
 3902                 if (strchr(bookname, '#') != NULL)
 3903                         (void) fprintf(stderr, gettext(
 3904                             "redaction bookmark name must not contain '#'\n"));
 3905                 else
 3906                         (void) fprintf(stderr, gettext(
 3907                             "redaction snapshot must be descendent of "
 3908                             "snapshot being redacted\n"));
 3909                 break;
 3910         case EALREADY:
 3911                 (void) fprintf(stderr, gettext("attempted to redact redacted "
 3912                     "dataset or with respect to redacted dataset\n"));
 3913                 break;
 3914         case ENOTSUP:
 3915                 (void) fprintf(stderr, gettext("redaction bookmarks feature "
 3916                     "not enabled\n"));
 3917                 break;
 3918         case EXDEV:
 3919                 (void) fprintf(stderr, gettext("potentially invalid redaction "
 3920                     "snapshot; full dataset names required\n"));
 3921                 break;
 3922         default:
 3923                 (void) fprintf(stderr, gettext("internal error: %s\n"),
 3924                     strerror(errno));
 3925         }
 3926 
 3927         return (err);
 3928 }
 3929 
 3930 /*
 3931  * zfs rollback [-rRf] <snapshot>
 3932  *
 3933  *      -r      Delete any intervening snapshots before doing rollback
 3934  *      -R      Delete any snapshots and their clones
 3935  *      -f      ignored for backwards compatibility
 3936  *
 3937  * Given a filesystem, rollback to a specific snapshot, discarding any changes
 3938  * since then and making it the active dataset.  If more recent snapshots exist,
 3939  * the command will complain unless the '-r' flag is given.
 3940  */
 3941 typedef struct rollback_cbdata {
 3942         uint64_t        cb_create;
 3943         uint8_t         cb_younger_ds_printed;
 3944         boolean_t       cb_first;
 3945         int             cb_doclones;
 3946         char            *cb_target;
 3947         int             cb_error;
 3948         boolean_t       cb_recurse;
 3949 } rollback_cbdata_t;
 3950 
 3951 static int
 3952 rollback_check_dependent(zfs_handle_t *zhp, void *data)
 3953 {
 3954         rollback_cbdata_t *cbp = data;
 3955 
 3956         if (cbp->cb_first && cbp->cb_recurse) {
 3957                 (void) fprintf(stderr, gettext("cannot rollback to "
 3958                     "'%s': clones of previous snapshots exist\n"),
 3959                     cbp->cb_target);
 3960                 (void) fprintf(stderr, gettext("use '-R' to "
 3961                     "force deletion of the following clones and "
 3962                     "dependents:\n"));
 3963                 cbp->cb_first = 0;
 3964                 cbp->cb_error = 1;
 3965         }
 3966 
 3967         (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
 3968 
 3969         zfs_close(zhp);
 3970         return (0);
 3971 }
 3972 
 3973 
 3974 /*
 3975  * Report some snapshots/bookmarks more recent than the one specified.
 3976  * Used when '-r' is not specified. We reuse this same callback for the
 3977  * snapshot dependents - if 'cb_dependent' is set, then this is a
 3978  * dependent and we should report it without checking the transaction group.
 3979  */
 3980 static int
 3981 rollback_check(zfs_handle_t *zhp, void *data)
 3982 {
 3983         rollback_cbdata_t *cbp = data;
 3984         /*
 3985          * Max number of younger snapshots and/or bookmarks to display before
 3986          * we stop the iteration.
 3987          */
 3988         const uint8_t max_younger = 32;
 3989 
 3990         if (cbp->cb_doclones) {
 3991                 zfs_close(zhp);
 3992                 return (0);
 3993         }
 3994 
 3995         if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {
 3996                 if (cbp->cb_first && !cbp->cb_recurse) {
 3997                         (void) fprintf(stderr, gettext("cannot "
 3998                             "rollback to '%s': more recent snapshots "
 3999                             "or bookmarks exist\n"),
 4000                             cbp->cb_target);
 4001                         (void) fprintf(stderr, gettext("use '-r' to "
 4002                             "force deletion of the following "
 4003                             "snapshots and bookmarks:\n"));
 4004                         cbp->cb_first = 0;
 4005                         cbp->cb_error = 1;
 4006                 }
 4007 
 4008                 if (cbp->cb_recurse) {
 4009                         if (zfs_iter_dependents(zhp, 0, B_TRUE,
 4010                             rollback_check_dependent, cbp) != 0) {
 4011                                 zfs_close(zhp);
 4012                                 return (-1);
 4013                         }
 4014                 } else {
 4015                         (void) fprintf(stderr, "%s\n",
 4016                             zfs_get_name(zhp));
 4017                         cbp->cb_younger_ds_printed++;
 4018                 }
 4019         }
 4020         zfs_close(zhp);
 4021 
 4022         if (cbp->cb_younger_ds_printed == max_younger) {
 4023                 /*
 4024                  * This non-recursive rollback is going to fail due to the
 4025                  * presence of snapshots and/or bookmarks that are younger than
 4026                  * the rollback target.
 4027                  * We printed some of the offending objects, now we stop
 4028                  * zfs_iter_snapshot/bookmark iteration so we can fail fast and
 4029                  * avoid iterating over the rest of the younger objects
 4030                  */
 4031                 (void) fprintf(stderr, gettext("Output limited to %d "
 4032                     "snapshots/bookmarks\n"), max_younger);
 4033                 return (-1);
 4034         }
 4035         return (0);
 4036 }
 4037 
 4038 static int
 4039 zfs_do_rollback(int argc, char **argv)
 4040 {
 4041         int ret = 0;
 4042         int c;
 4043         boolean_t force = B_FALSE;
 4044         rollback_cbdata_t cb = { 0 };
 4045         zfs_handle_t *zhp, *snap;
 4046         char parentname[ZFS_MAX_DATASET_NAME_LEN];
 4047         char *delim;
 4048         uint64_t min_txg = 0;
 4049 
 4050         /* check options */
 4051         while ((c = getopt(argc, argv, "rRf")) != -1) {
 4052                 switch (c) {
 4053                 case 'r':
 4054                         cb.cb_recurse = 1;
 4055                         break;
 4056                 case 'R':
 4057                         cb.cb_recurse = 1;
 4058                         cb.cb_doclones = 1;
 4059                         break;
 4060                 case 'f':
 4061                         force = B_TRUE;
 4062                         break;
 4063                 case '?':
 4064                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 4065                             optopt);
 4066                         usage(B_FALSE);
 4067                 }
 4068         }
 4069 
 4070         argc -= optind;
 4071         argv += optind;
 4072 
 4073         /* check number of arguments */
 4074         if (argc < 1) {
 4075                 (void) fprintf(stderr, gettext("missing dataset argument\n"));
 4076                 usage(B_FALSE);
 4077         }
 4078         if (argc > 1) {
 4079                 (void) fprintf(stderr, gettext("too many arguments\n"));
 4080                 usage(B_FALSE);
 4081         }
 4082 
 4083         /* open the snapshot */
 4084         if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
 4085                 return (1);
 4086 
 4087         /* open the parent dataset */
 4088         (void) strlcpy(parentname, argv[0], sizeof (parentname));
 4089         verify((delim = strrchr(parentname, '@')) != NULL);
 4090         *delim = '\0';
 4091         if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_DATASET)) == NULL) {
 4092                 zfs_close(snap);
 4093                 return (1);
 4094         }
 4095 
 4096         /*
 4097          * Check for more recent snapshots and/or clones based on the presence
 4098          * of '-r' and '-R'.
 4099          */
 4100         cb.cb_target = argv[0];
 4101         cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
 4102         cb.cb_first = B_TRUE;
 4103         cb.cb_error = 0;
 4104 
 4105         if (cb.cb_create > 0)
 4106                 min_txg = cb.cb_create;
 4107 
 4108         if ((ret = zfs_iter_snapshots(zhp, 0, rollback_check, &cb,
 4109             min_txg, 0)) != 0)
 4110                 goto out;
 4111         if ((ret = zfs_iter_bookmarks(zhp, 0, rollback_check, &cb)) != 0)
 4112                 goto out;
 4113 
 4114         if ((ret = cb.cb_error) != 0)
 4115                 goto out;
 4116 
 4117         /*
 4118          * Rollback parent to the given snapshot.
 4119          */
 4120         ret = zfs_rollback(zhp, snap, force);
 4121 
 4122 out:
 4123         zfs_close(snap);
 4124         zfs_close(zhp);
 4125 
 4126         if (ret == 0)
 4127                 return (0);
 4128         else
 4129                 return (1);
 4130 }
 4131 
 4132 /*
 4133  * zfs set property=value ... { fs | snap | vol } ...
 4134  *
 4135  * Sets the given properties for all datasets specified on the command line.
 4136  */
 4137 
 4138 static int
 4139 set_callback(zfs_handle_t *zhp, void *data)
 4140 {
 4141         nvlist_t *props = data;
 4142 
 4143         if (zfs_prop_set_list(zhp, props) != 0) {
 4144                 switch (libzfs_errno(g_zfs)) {
 4145                 case EZFS_MOUNTFAILED:
 4146                         (void) fprintf(stderr, gettext("property may be set "
 4147                             "but unable to remount filesystem\n"));
 4148                         break;
 4149                 case EZFS_SHARENFSFAILED:
 4150                         (void) fprintf(stderr, gettext("property may be set "
 4151                             "but unable to reshare filesystem\n"));
 4152                         break;
 4153                 }
 4154                 return (1);
 4155         }
 4156         return (0);
 4157 }
 4158 
 4159 static int
 4160 zfs_do_set(int argc, char **argv)
 4161 {
 4162         nvlist_t *props = NULL;
 4163         int ds_start = -1; /* argv idx of first dataset arg */
 4164         int ret = 0;
 4165         int i;
 4166 
 4167         /* check for options */
 4168         if (argc > 1 && argv[1][0] == '-') {
 4169                 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 4170                     argv[1][1]);
 4171                 usage(B_FALSE);
 4172         }
 4173 
 4174         /* check number of arguments */
 4175         if (argc < 2) {
 4176                 (void) fprintf(stderr, gettext("missing arguments\n"));
 4177                 usage(B_FALSE);
 4178         }
 4179         if (argc < 3) {
 4180                 if (strchr(argv[1], '=') == NULL) {
 4181                         (void) fprintf(stderr, gettext("missing property=value "
 4182                             "argument(s)\n"));
 4183                 } else {
 4184                         (void) fprintf(stderr, gettext("missing dataset "
 4185                             "name(s)\n"));
 4186                 }
 4187                 usage(B_FALSE);
 4188         }
 4189 
 4190         /* validate argument order:  prop=val args followed by dataset args */
 4191         for (i = 1; i < argc; i++) {
 4192                 if (strchr(argv[i], '=') != NULL) {
 4193                         if (ds_start > 0) {
 4194                                 /* out-of-order prop=val argument */
 4195                                 (void) fprintf(stderr, gettext("invalid "
 4196                                     "argument order\n"));
 4197                                 usage(B_FALSE);
 4198                         }
 4199                 } else if (ds_start < 0) {
 4200                         ds_start = i;
 4201                 }
 4202         }
 4203         if (ds_start < 0) {
 4204                 (void) fprintf(stderr, gettext("missing dataset name(s)\n"));
 4205                 usage(B_FALSE);
 4206         }
 4207 
 4208         /* Populate a list of property settings */
 4209         if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
 4210                 nomem();
 4211         for (i = 1; i < ds_start; i++) {
 4212                 if (!parseprop(props, argv[i])) {
 4213                         ret = -1;
 4214                         goto error;
 4215                 }
 4216         }
 4217 
 4218         ret = zfs_for_each(argc - ds_start, argv + ds_start, 0,
 4219             ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, props);
 4220 
 4221 error:
 4222         nvlist_free(props);
 4223         return (ret);
 4224 }
 4225 
 4226 typedef struct snap_cbdata {
 4227         nvlist_t *sd_nvl;
 4228         boolean_t sd_recursive;
 4229         const char *sd_snapname;
 4230 } snap_cbdata_t;
 4231 
 4232 static int
 4233 zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
 4234 {
 4235         snap_cbdata_t *sd = arg;
 4236         char *name;
 4237         int rv = 0;
 4238         int error;
 4239 
 4240         if (sd->sd_recursive &&
 4241             zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) != 0) {
 4242                 zfs_close(zhp);
 4243                 return (0);
 4244         }
 4245 
 4246         error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
 4247         if (error == -1)
 4248                 nomem();
 4249         fnvlist_add_boolean(sd->sd_nvl, name);
 4250         free(name);
 4251 
 4252         if (sd->sd_recursive)
 4253                 rv = zfs_iter_filesystems(zhp, 0, zfs_snapshot_cb, sd);
 4254         zfs_close(zhp);
 4255         return (rv);
 4256 }
 4257 
 4258 /*
 4259  * zfs snapshot [-r] [-o prop=value] ... <fs@snap>
 4260  *
 4261  * Creates a snapshot with the given name.  While functionally equivalent to
 4262  * 'zfs create', it is a separate command to differentiate intent.
 4263  */
 4264 static int
 4265 zfs_do_snapshot(int argc, char **argv)
 4266 {
 4267         int ret = 0;
 4268         int c;
 4269         nvlist_t *props;
 4270         snap_cbdata_t sd = { 0 };
 4271         boolean_t multiple_snaps = B_FALSE;
 4272 
 4273         if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
 4274                 nomem();
 4275         if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0)
 4276                 nomem();
 4277 
 4278         /* check options */
 4279         while ((c = getopt(argc, argv, "ro:")) != -1) {
 4280                 switch (c) {
 4281                 case 'o':
 4282                         if (!parseprop(props, optarg)) {
 4283                                 nvlist_free(sd.sd_nvl);
 4284                                 nvlist_free(props);
 4285                                 return (1);
 4286                         }
 4287                         break;
 4288                 case 'r':
 4289                         sd.sd_recursive = B_TRUE;
 4290                         multiple_snaps = B_TRUE;
 4291                         break;
 4292                 case '?':
 4293                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 4294                             optopt);
 4295                         goto usage;
 4296                 }
 4297         }
 4298 
 4299         argc -= optind;
 4300         argv += optind;
 4301 
 4302         /* check number of arguments */
 4303         if (argc < 1) {
 4304                 (void) fprintf(stderr, gettext("missing snapshot argument\n"));
 4305                 goto usage;
 4306         }
 4307 
 4308         if (argc > 1)
 4309                 multiple_snaps = B_TRUE;
 4310         for (; argc > 0; argc--, argv++) {
 4311                 char *atp;
 4312                 zfs_handle_t *zhp;
 4313 
 4314                 atp = strchr(argv[0], '@');
 4315                 if (atp == NULL)
 4316                         goto usage;
 4317                 *atp = '\0';
 4318                 sd.sd_snapname = atp + 1;
 4319                 zhp = zfs_open(g_zfs, argv[0],
 4320                     ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
 4321                 if (zhp == NULL)
 4322                         goto usage;
 4323                 if (zfs_snapshot_cb(zhp, &sd) != 0)
 4324                         goto usage;
 4325         }
 4326 
 4327         ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props);
 4328         nvlist_free(sd.sd_nvl);
 4329         nvlist_free(props);
 4330         if (ret != 0 && multiple_snaps)
 4331                 (void) fprintf(stderr, gettext("no snapshots were created\n"));
 4332         return (ret != 0);
 4333 
 4334 usage:
 4335         nvlist_free(sd.sd_nvl);
 4336         nvlist_free(props);
 4337         usage(B_FALSE);
 4338         return (-1);
 4339 }
 4340 
 4341 /*
 4342  * Array of prefixes to exclude –
 4343  * a linear search, even if executed for each dataset,
 4344  * is plenty good enough.
 4345  */
 4346 typedef struct zfs_send_exclude_arg {
 4347         size_t count;
 4348         const char **list;
 4349 } zfs_send_exclude_arg_t;
 4350 
 4351 static boolean_t
 4352 zfs_do_send_exclude(zfs_handle_t *zhp, void *context)
 4353 {
 4354         zfs_send_exclude_arg_t *excludes = context;
 4355         const char *name = zfs_get_name(zhp);
 4356 
 4357         for (size_t i = 0; i < excludes->count; ++i) {
 4358                 size_t len = strlen(excludes->list[i]);
 4359                 if (strncmp(name, excludes->list[i], len) == 0 &&
 4360                     memchr("/@", name[len], sizeof ("/@")))
 4361                         return (B_FALSE);
 4362         }
 4363 
 4364         return (B_TRUE);
 4365 }
 4366 
 4367 /*
 4368  * Send a backup stream to stdout.
 4369  */
 4370 static int
 4371 zfs_do_send(int argc, char **argv)
 4372 {
 4373         char *fromname = NULL;
 4374         char *toname = NULL;
 4375         char *resume_token = NULL;
 4376         char *cp;
 4377         zfs_handle_t *zhp;
 4378         sendflags_t flags = { 0 };
 4379         int c, err;
 4380         nvlist_t *dbgnv = NULL;
 4381         char *redactbook = NULL;
 4382         zfs_send_exclude_arg_t excludes = { 0 };
 4383 
 4384         struct option long_options[] = {
 4385                 {"replicate",   no_argument,            NULL, 'R'},
 4386                 {"skip-missing",        no_argument,    NULL, 's'},
 4387                 {"redact",      required_argument,      NULL, 'd'},
 4388                 {"props",       no_argument,            NULL, 'p'},
 4389                 {"parsable",    no_argument,            NULL, 'P'},
 4390                 {"dedup",       no_argument,            NULL, 'D'},
 4391                 {"proctitle",   no_argument,            NULL, 'V'},
 4392                 {"verbose",     no_argument,            NULL, 'v'},
 4393                 {"dryrun",      no_argument,            NULL, 'n'},
 4394                 {"large-block", no_argument,            NULL, 'L'},
 4395                 {"embed",       no_argument,            NULL, 'e'},
 4396                 {"resume",      required_argument,      NULL, 't'},
 4397                 {"compressed",  no_argument,            NULL, 'c'},
 4398                 {"raw",         no_argument,            NULL, 'w'},
 4399                 {"backup",      no_argument,            NULL, 'b'},
 4400                 {"holds",       no_argument,            NULL, 'h'},
 4401                 {"saved",       no_argument,            NULL, 'S'},
 4402                 {"exclude",     required_argument,      NULL, 'X'},
 4403                 {0, 0, 0, 0}
 4404         };
 4405 
 4406         /* check options */
 4407         while ((c = getopt_long(argc, argv, ":i:I:RsDpVvnPLeht:cwbd:SX:",
 4408             long_options, NULL)) != -1) {
 4409                 switch (c) {
 4410                 case 'X':
 4411                         for (char *ds; (ds = strsep(&optarg, ",")) != NULL; ) {
 4412                                 if (!zfs_name_valid(ds, ZFS_TYPE_DATASET) ||
 4413                                     strchr(ds, '/') == NULL) {
 4414                                         (void) fprintf(stderr, gettext("-X %s: "
 4415                                             "not a valid non-root dataset name"
 4416                                             ".\n"), ds);
 4417                                         usage(B_FALSE);
 4418                                 }
 4419                                 excludes.list = safe_realloc(excludes.list,
 4420                                     sizeof (char *) * (excludes.count + 1));
 4421                                 excludes.list[excludes.count++] = ds;
 4422                         }
 4423                         break;
 4424                 case 'i':
 4425                         if (fromname)
 4426                                 usage(B_FALSE);
 4427                         fromname = optarg;
 4428                         break;
 4429                 case 'I':
 4430                         if (fromname)
 4431                                 usage(B_FALSE);
 4432                         fromname = optarg;
 4433                         flags.doall = B_TRUE;
 4434                         break;
 4435                 case 'R':
 4436                         flags.replicate = B_TRUE;
 4437                         break;
 4438                 case 's':
 4439                         flags.skipmissing = B_TRUE;
 4440                         break;
 4441                 case 'd':
 4442                         redactbook = optarg;
 4443                         break;
 4444                 case 'p':
 4445                         flags.props = B_TRUE;
 4446                         break;
 4447                 case 'b':
 4448                         flags.backup = B_TRUE;
 4449                         break;
 4450                 case 'h':
 4451                         flags.holds = B_TRUE;
 4452                         break;
 4453                 case 'P':
 4454                         flags.parsable = B_TRUE;
 4455                         break;
 4456                 case 'V':
 4457                         flags.progressastitle = B_TRUE;
 4458                         break;
 4459                 case 'v':
 4460                         flags.verbosity++;
 4461                         flags.progress = B_TRUE;
 4462                         break;
 4463                 case 'D':
 4464                         (void) fprintf(stderr,
 4465                             gettext("WARNING: deduplicated send is no "
 4466                             "longer supported.  A regular,\n"
 4467                             "non-deduplicated stream will be generated.\n\n"));
 4468                         break;
 4469                 case 'n':
 4470                         flags.dryrun = B_TRUE;
 4471                         break;
 4472                 case 'L':
 4473                         flags.largeblock = B_TRUE;
 4474                         break;
 4475                 case 'e':
 4476                         flags.embed_data = B_TRUE;
 4477                         break;
 4478                 case 't':
 4479                         resume_token = optarg;
 4480                         break;
 4481                 case 'c':
 4482                         flags.compress = B_TRUE;
 4483                         break;
 4484                 case 'w':
 4485                         flags.raw = B_TRUE;
 4486                         flags.compress = B_TRUE;
 4487                         flags.embed_data = B_TRUE;
 4488                         flags.largeblock = B_TRUE;
 4489                         break;
 4490                 case 'S':
 4491                         flags.saved = B_TRUE;
 4492                         break;
 4493                 case ':':
 4494                         /*
 4495                          * If a parameter was not passed, optopt contains the
 4496                          * value that would normally lead us into the
 4497                          * appropriate case statement.  If it's > 256, then this
 4498                          * must be a longopt and we should look at argv to get
 4499                          * the string.  Otherwise it's just the character, so we
 4500                          * should use it directly.
 4501                          */
 4502                         if (optopt <= UINT8_MAX) {
 4503                                 (void) fprintf(stderr,
 4504                                     gettext("missing argument for '%c' "
 4505                                     "option\n"), optopt);
 4506                         } else {
 4507                                 (void) fprintf(stderr,
 4508                                     gettext("missing argument for '%s' "
 4509                                     "option\n"), argv[optind - 1]);
 4510                         }
 4511                         free(excludes.list);
 4512                         usage(B_FALSE);
 4513                         break;
 4514                 case '?':
 4515                 default:
 4516                         /*
 4517                          * If an invalid flag was passed, optopt contains the
 4518                          * character if it was a short flag, or 0 if it was a
 4519                          * longopt.
 4520                          */
 4521                         if (optopt != 0) {
 4522                                 (void) fprintf(stderr,
 4523                                     gettext("invalid option '%c'\n"), optopt);
 4524                         } else {
 4525                                 (void) fprintf(stderr,
 4526                                     gettext("invalid option '%s'\n"),
 4527                                     argv[optind - 1]);
 4528 
 4529                         }
 4530                         free(excludes.list);
 4531                         usage(B_FALSE);
 4532                 }
 4533         }
 4534 
 4535         if (flags.parsable && flags.verbosity == 0)
 4536                 flags.verbosity = 1;
 4537 
 4538         if (excludes.count > 0 && !flags.replicate) {
 4539                 free(excludes.list);
 4540                 (void) fprintf(stderr, gettext("Cannot specify "
 4541                     "dataset exclusion (-X) on a non-recursive "
 4542                     "send.\n"));
 4543                 return (1);
 4544         }
 4545 
 4546         argc -= optind;
 4547         argv += optind;
 4548 
 4549         if (resume_token != NULL) {
 4550                 if (fromname != NULL || flags.replicate || flags.props ||
 4551                     flags.backup || flags.holds ||
 4552                     flags.saved || redactbook != NULL) {
 4553                         free(excludes.list);
 4554                         (void) fprintf(stderr,
 4555                             gettext("invalid flags combined with -t\n"));
 4556                         usage(B_FALSE);
 4557                 }
 4558                 if (argc > 0) {
 4559                         free(excludes.list);
 4560                         (void) fprintf(stderr, gettext("too many arguments\n"));
 4561                         usage(B_FALSE);
 4562                 }
 4563         } else {
 4564                 if (argc < 1) {
 4565                         free(excludes.list);
 4566                         (void) fprintf(stderr,
 4567                             gettext("missing snapshot argument\n"));
 4568                         usage(B_FALSE);
 4569                 }
 4570                 if (argc > 1) {
 4571                         free(excludes.list);
 4572                         (void) fprintf(stderr, gettext("too many arguments\n"));
 4573                         usage(B_FALSE);
 4574                 }
 4575         }
 4576 
 4577         if (flags.saved) {
 4578                 if (fromname != NULL || flags.replicate || flags.props ||
 4579                     flags.doall || flags.backup ||
 4580                     flags.holds || flags.largeblock || flags.embed_data ||
 4581                     flags.compress || flags.raw || redactbook != NULL) {
 4582                         free(excludes.list);
 4583 
 4584                         (void) fprintf(stderr, gettext("incompatible flags "
 4585                             "combined with saved send flag\n"));
 4586                         usage(B_FALSE);
 4587                 }
 4588                 if (strchr(argv[0], '@') != NULL) {
 4589                         free(excludes.list);
 4590 
 4591                         (void) fprintf(stderr, gettext("saved send must "
 4592                             "specify the dataset with partially-received "
 4593                             "state\n"));
 4594                         usage(B_FALSE);
 4595                 }
 4596         }
 4597 
 4598         if (flags.raw && redactbook != NULL) {
 4599                 free(excludes.list);
 4600                 (void) fprintf(stderr,
 4601                     gettext("Error: raw sends may not be redacted.\n"));
 4602                 return (1);
 4603         }
 4604 
 4605         if (!flags.dryrun && isatty(STDOUT_FILENO)) {
 4606                 free(excludes.list);
 4607                 (void) fprintf(stderr,
 4608                     gettext("Error: Stream can not be written to a terminal.\n"
 4609                     "You must redirect standard output.\n"));
 4610                 return (1);
 4611         }
 4612 
 4613         if (flags.saved) {
 4614                 zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);
 4615                 if (zhp == NULL) {
 4616                         free(excludes.list);
 4617                         return (1);
 4618                 }
 4619 
 4620                 err = zfs_send_saved(zhp, &flags, STDOUT_FILENO,
 4621                     resume_token);
 4622                 free(excludes.list);
 4623                 zfs_close(zhp);
 4624                 return (err != 0);
 4625         } else if (resume_token != NULL) {
 4626                 free(excludes.list);
 4627                 return (zfs_send_resume(g_zfs, &flags, STDOUT_FILENO,
 4628                     resume_token));
 4629         }
 4630 
 4631         if (flags.skipmissing && !flags.replicate) {
 4632                 free(excludes.list);
 4633                 (void) fprintf(stderr,
 4634                     gettext("skip-missing flag can only be used in "
 4635                     "conjunction with replicate\n"));
 4636                 usage(B_FALSE);
 4637         }
 4638 
 4639         /*
 4640          * For everything except -R and -I, use the new, cleaner code path.
 4641          */
 4642         if (!(flags.replicate || flags.doall)) {
 4643                 char frombuf[ZFS_MAX_DATASET_NAME_LEN];
 4644 
 4645                 if (fromname != NULL && (strchr(fromname, '#') == NULL &&
 4646                     strchr(fromname, '@') == NULL)) {
 4647                         /*
 4648                          * Neither bookmark or snapshot was specified.  Print a
 4649                          * warning, and assume snapshot.
 4650                          */
 4651                         (void) fprintf(stderr, "Warning: incremental source "
 4652                             "didn't specify type, assuming snapshot. Use '@' "
 4653                             "or '#' prefix to avoid ambiguity.\n");
 4654                         (void) snprintf(frombuf, sizeof (frombuf), "@%s",
 4655                             fromname);
 4656                         fromname = frombuf;
 4657                 }
 4658                 if (fromname != NULL &&
 4659                     (fromname[0] == '#' || fromname[0] == '@')) {
 4660                         /*
 4661                          * Incremental source name begins with # or @.
 4662                          * Default to same fs as target.
 4663                          */
 4664                         char tmpbuf[ZFS_MAX_DATASET_NAME_LEN];
 4665                         (void) strlcpy(tmpbuf, fromname, sizeof (tmpbuf));
 4666                         (void) strlcpy(frombuf, argv[0], sizeof (frombuf));
 4667                         cp = strchr(frombuf, '@');
 4668                         if (cp != NULL)
 4669                                 *cp = '\0';
 4670                         (void) strlcat(frombuf, tmpbuf, sizeof (frombuf));
 4671                         fromname = frombuf;
 4672                 }
 4673 
 4674                 zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);
 4675                 if (zhp == NULL) {
 4676                         free(excludes.list);
 4677                         return (1);
 4678                 }
 4679                 err = zfs_send_one(zhp, fromname, STDOUT_FILENO, &flags,
 4680                     redactbook);
 4681 
 4682                 free(excludes.list);
 4683                 zfs_close(zhp);
 4684                 return (err != 0);
 4685         }
 4686 
 4687         if (fromname != NULL && strchr(fromname, '#')) {
 4688                 (void) fprintf(stderr,
 4689                     gettext("Error: multiple snapshots cannot be "
 4690                     "sent from a bookmark.\n"));
 4691                 free(excludes.list);
 4692                 return (1);
 4693         }
 4694 
 4695         if (redactbook != NULL) {
 4696                 (void) fprintf(stderr, gettext("Error: multiple snapshots "
 4697                     "cannot be sent redacted.\n"));
 4698                 free(excludes.list);
 4699                 return (1);
 4700         }
 4701 
 4702         if ((cp = strchr(argv[0], '@')) == NULL) {
 4703                 (void) fprintf(stderr, gettext("Error: "
 4704                     "Unsupported flag with filesystem or bookmark.\n"));
 4705                 free(excludes.list);
 4706                 return (1);
 4707         }
 4708         *cp = '\0';
 4709         toname = cp + 1;
 4710         zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
 4711         if (zhp == NULL) {
 4712                 free(excludes.list);
 4713                 return (1);
 4714         }
 4715 
 4716         /*
 4717          * If they specified the full path to the snapshot, chop off
 4718          * everything except the short name of the snapshot, but special
 4719          * case if they specify the origin.
 4720          */
 4721         if (fromname && (cp = strchr(fromname, '@')) != NULL) {
 4722                 char origin[ZFS_MAX_DATASET_NAME_LEN];
 4723                 zprop_source_t src;
 4724 
 4725                 (void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN,
 4726                     origin, sizeof (origin), &src, NULL, 0, B_FALSE);
 4727 
 4728                 if (strcmp(origin, fromname) == 0) {
 4729                         fromname = NULL;
 4730                         flags.fromorigin = B_TRUE;
 4731                 } else {
 4732                         *cp = '\0';
 4733                         if (cp != fromname && strcmp(argv[0], fromname)) {
 4734                                 zfs_close(zhp);
 4735                                 free(excludes.list);
 4736                                 (void) fprintf(stderr,
 4737                                     gettext("incremental source must be "
 4738                                     "in same filesystem\n"));
 4739                                 usage(B_FALSE);
 4740                         }
 4741                         fromname = cp + 1;
 4742                         if (strchr(fromname, '@') || strchr(fromname, '/')) {
 4743                                 zfs_close(zhp);
 4744                                 free(excludes.list);
 4745                                 (void) fprintf(stderr,
 4746                                     gettext("invalid incremental source\n"));
 4747                                 usage(B_FALSE);
 4748                         }
 4749                 }
 4750         }
 4751 
 4752         if (flags.replicate && fromname == NULL)
 4753                 flags.doall = B_TRUE;
 4754 
 4755         err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO,
 4756             excludes.count > 0 ? zfs_do_send_exclude : NULL,
 4757             &excludes, flags.verbosity >= 3 ? &dbgnv : NULL);
 4758 
 4759         if (flags.verbosity >= 3 && dbgnv != NULL) {
 4760                 /*
 4761                  * dump_nvlist prints to stdout, but that's been
 4762                  * redirected to a file.  Make it print to stderr
 4763                  * instead.
 4764                  */
 4765                 (void) dup2(STDERR_FILENO, STDOUT_FILENO);
 4766                 dump_nvlist(dbgnv, 0);
 4767                 nvlist_free(dbgnv);
 4768         }
 4769 
 4770         zfs_close(zhp);
 4771         free(excludes.list);
 4772         return (err != 0);
 4773 }
 4774 
 4775 /*
 4776  * Restore a backup stream from stdin.
 4777  */
 4778 static int
 4779 zfs_do_receive(int argc, char **argv)
 4780 {
 4781         int c, err = 0;
 4782         recvflags_t flags = { 0 };
 4783         boolean_t abort_resumable = B_FALSE;
 4784         nvlist_t *props;
 4785 
 4786         if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
 4787                 nomem();
 4788 
 4789         /* check options */
 4790         while ((c = getopt(argc, argv, ":o:x:dehMnuvFsAc")) != -1) {
 4791                 switch (c) {
 4792                 case 'o':
 4793                         if (!parseprop(props, optarg)) {
 4794                                 nvlist_free(props);
 4795                                 usage(B_FALSE);
 4796                         }
 4797                         break;
 4798                 case 'x':
 4799                         if (!parsepropname(props, optarg)) {
 4800                                 nvlist_free(props);
 4801                                 usage(B_FALSE);
 4802                         }
 4803                         break;
 4804                 case 'd':
 4805                         if (flags.istail) {
 4806                                 (void) fprintf(stderr, gettext("invalid option "
 4807                                     "combination: -d and -e are mutually "
 4808                                     "exclusive\n"));
 4809                                 usage(B_FALSE);
 4810                         }
 4811                         flags.isprefix = B_TRUE;
 4812                         break;
 4813                 case 'e':
 4814                         if (flags.isprefix) {
 4815                                 (void) fprintf(stderr, gettext("invalid option "
 4816                                     "combination: -d and -e are mutually "
 4817                                     "exclusive\n"));
 4818                                 usage(B_FALSE);
 4819                         }
 4820                         flags.istail = B_TRUE;
 4821                         break;
 4822                 case 'h':
 4823                         flags.skipholds = B_TRUE;
 4824                         break;
 4825                 case 'M':
 4826                         flags.forceunmount = B_TRUE;
 4827                         break;
 4828                 case 'n':
 4829                         flags.dryrun = B_TRUE;
 4830                         break;
 4831                 case 'u':
 4832                         flags.nomount = B_TRUE;
 4833                         break;
 4834                 case 'v':
 4835                         flags.verbose = B_TRUE;
 4836                         break;
 4837                 case 's':
 4838                         flags.resumable = B_TRUE;
 4839                         break;
 4840                 case 'F':
 4841                         flags.force = B_TRUE;
 4842                         break;
 4843                 case 'A':
 4844                         abort_resumable = B_TRUE;
 4845                         break;
 4846                 case 'c':
 4847                         flags.heal = B_TRUE;
 4848                         break;
 4849                 case ':':
 4850                         (void) fprintf(stderr, gettext("missing argument for "
 4851                             "'%c' option\n"), optopt);
 4852                         usage(B_FALSE);
 4853                         break;
 4854                 case '?':
 4855                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 4856                             optopt);
 4857                         usage(B_FALSE);
 4858                 }
 4859         }
 4860 
 4861         argc -= optind;
 4862         argv += optind;
 4863 
 4864         /* zfs recv -e (use "tail" name) implies -d (remove dataset "head") */
 4865         if (flags.istail)
 4866                 flags.isprefix = B_TRUE;
 4867 
 4868         /* check number of arguments */
 4869         if (argc < 1) {
 4870                 (void) fprintf(stderr, gettext("missing snapshot argument\n"));
 4871                 usage(B_FALSE);
 4872         }
 4873         if (argc > 1) {
 4874                 (void) fprintf(stderr, gettext("too many arguments\n"));
 4875                 usage(B_FALSE);
 4876         }
 4877 
 4878         if (abort_resumable) {
 4879                 if (flags.isprefix || flags.istail || flags.dryrun ||
 4880                     flags.resumable || flags.nomount) {
 4881                         (void) fprintf(stderr, gettext("invalid option\n"));
 4882                         usage(B_FALSE);
 4883                 }
 4884 
 4885                 char namebuf[ZFS_MAX_DATASET_NAME_LEN];
 4886                 (void) snprintf(namebuf, sizeof (namebuf),
 4887                     "%s/%%recv", argv[0]);
 4888 
 4889                 if (zfs_dataset_exists(g_zfs, namebuf,
 4890                     ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) {
 4891                         zfs_handle_t *zhp = zfs_open(g_zfs,
 4892                             namebuf, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
 4893                         if (zhp == NULL) {
 4894                                 nvlist_free(props);
 4895                                 return (1);
 4896                         }
 4897                         err = zfs_destroy(zhp, B_FALSE);
 4898                         zfs_close(zhp);
 4899                 } else {
 4900                         zfs_handle_t *zhp = zfs_open(g_zfs,
 4901                             argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
 4902                         if (zhp == NULL)
 4903                                 usage(B_FALSE);
 4904                         if (!zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) ||
 4905                             zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
 4906                             NULL, 0, NULL, NULL, 0, B_TRUE) == -1) {
 4907                                 (void) fprintf(stderr,
 4908                                     gettext("'%s' does not have any "
 4909                                     "resumable receive state to abort\n"),
 4910                                     argv[0]);
 4911                                 nvlist_free(props);
 4912                                 zfs_close(zhp);
 4913                                 return (1);
 4914                         }
 4915                         err = zfs_destroy(zhp, B_FALSE);
 4916                         zfs_close(zhp);
 4917                 }
 4918                 nvlist_free(props);
 4919                 return (err != 0);
 4920         }
 4921 
 4922         if (isatty(STDIN_FILENO)) {
 4923                 (void) fprintf(stderr,
 4924                     gettext("Error: Backup stream can not be read "
 4925                     "from a terminal.\n"
 4926                     "You must redirect standard input.\n"));
 4927                 nvlist_free(props);
 4928                 return (1);
 4929         }
 4930         err = zfs_receive(g_zfs, argv[0], props, &flags, STDIN_FILENO, NULL);
 4931         nvlist_free(props);
 4932 
 4933         return (err != 0);
 4934 }
 4935 
 4936 /*
 4937  * allow/unallow stuff
 4938  */
 4939 /* copied from zfs/sys/dsl_deleg.h */
 4940 #define ZFS_DELEG_PERM_CREATE           "create"
 4941 #define ZFS_DELEG_PERM_DESTROY          "destroy"
 4942 #define ZFS_DELEG_PERM_SNAPSHOT         "snapshot"
 4943 #define ZFS_DELEG_PERM_ROLLBACK         "rollback"
 4944 #define ZFS_DELEG_PERM_CLONE            "clone"
 4945 #define ZFS_DELEG_PERM_PROMOTE          "promote"
 4946 #define ZFS_DELEG_PERM_RENAME           "rename"
 4947 #define ZFS_DELEG_PERM_MOUNT            "mount"
 4948 #define ZFS_DELEG_PERM_SHARE            "share"
 4949 #define ZFS_DELEG_PERM_SEND             "send"
 4950 #define ZFS_DELEG_PERM_RECEIVE          "receive"
 4951 #define ZFS_DELEG_PERM_ALLOW            "allow"
 4952 #define ZFS_DELEG_PERM_USERPROP         "userprop"
 4953 #define ZFS_DELEG_PERM_VSCAN            "vscan" /* ??? */
 4954 #define ZFS_DELEG_PERM_USERQUOTA        "userquota"
 4955 #define ZFS_DELEG_PERM_GROUPQUOTA       "groupquota"
 4956 #define ZFS_DELEG_PERM_USERUSED         "userused"
 4957 #define ZFS_DELEG_PERM_GROUPUSED        "groupused"
 4958 #define ZFS_DELEG_PERM_USEROBJQUOTA     "userobjquota"
 4959 #define ZFS_DELEG_PERM_GROUPOBJQUOTA    "groupobjquota"
 4960 #define ZFS_DELEG_PERM_USEROBJUSED      "userobjused"
 4961 #define ZFS_DELEG_PERM_GROUPOBJUSED     "groupobjused"
 4962 
 4963 #define ZFS_DELEG_PERM_HOLD             "hold"
 4964 #define ZFS_DELEG_PERM_RELEASE          "release"
 4965 #define ZFS_DELEG_PERM_DIFF             "diff"
 4966 #define ZFS_DELEG_PERM_BOOKMARK         "bookmark"
 4967 #define ZFS_DELEG_PERM_LOAD_KEY         "load-key"
 4968 #define ZFS_DELEG_PERM_CHANGE_KEY       "change-key"
 4969 
 4970 #define ZFS_DELEG_PERM_PROJECTUSED      "projectused"
 4971 #define ZFS_DELEG_PERM_PROJECTQUOTA     "projectquota"
 4972 #define ZFS_DELEG_PERM_PROJECTOBJUSED   "projectobjused"
 4973 #define ZFS_DELEG_PERM_PROJECTOBJQUOTA  "projectobjquota"
 4974 
 4975 #define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE
 4976 
 4977 static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = {
 4978         { ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW },
 4979         { ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE },
 4980         { ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE },
 4981         { ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY },
 4982         { ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF},
 4983         { ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD },
 4984         { ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT },
 4985         { ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE },
 4986         { ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE },
 4987         { ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE },
 4988         { ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },
 4989         { ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },
 4990         { ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND },
 4991         { ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },
 4992         { ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },
 4993         { ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK },
 4994         { ZFS_DELEG_PERM_LOAD_KEY, ZFS_DELEG_NOTE_LOAD_KEY },
 4995         { ZFS_DELEG_PERM_CHANGE_KEY, ZFS_DELEG_NOTE_CHANGE_KEY },
 4996 
 4997         { ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA },
 4998         { ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED },
 4999         { ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },
 5000         { ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA },
 5001         { ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED },
 5002         { ZFS_DELEG_PERM_USEROBJQUOTA, ZFS_DELEG_NOTE_USEROBJQUOTA },
 5003         { ZFS_DELEG_PERM_USEROBJUSED, ZFS_DELEG_NOTE_USEROBJUSED },
 5004         { ZFS_DELEG_PERM_GROUPOBJQUOTA, ZFS_DELEG_NOTE_GROUPOBJQUOTA },
 5005         { ZFS_DELEG_PERM_GROUPOBJUSED, ZFS_DELEG_NOTE_GROUPOBJUSED },
 5006         { ZFS_DELEG_PERM_PROJECTUSED, ZFS_DELEG_NOTE_PROJECTUSED },
 5007         { ZFS_DELEG_PERM_PROJECTQUOTA, ZFS_DELEG_NOTE_PROJECTQUOTA },
 5008         { ZFS_DELEG_PERM_PROJECTOBJUSED, ZFS_DELEG_NOTE_PROJECTOBJUSED },
 5009         { ZFS_DELEG_PERM_PROJECTOBJQUOTA, ZFS_DELEG_NOTE_PROJECTOBJQUOTA },
 5010         { NULL, ZFS_DELEG_NOTE_NONE }
 5011 };
 5012 
 5013 /* permission structure */
 5014 typedef struct deleg_perm {
 5015         zfs_deleg_who_type_t    dp_who_type;
 5016         const char              *dp_name;
 5017         boolean_t               dp_local;
 5018         boolean_t               dp_descend;
 5019 } deleg_perm_t;
 5020 
 5021 /* */
 5022 typedef struct deleg_perm_node {
 5023         deleg_perm_t            dpn_perm;
 5024 
 5025         uu_avl_node_t           dpn_avl_node;
 5026 } deleg_perm_node_t;
 5027 
 5028 typedef struct fs_perm fs_perm_t;
 5029 
 5030 /* permissions set */
 5031 typedef struct who_perm {
 5032         zfs_deleg_who_type_t    who_type;
 5033         const char              *who_name;              /* id */
 5034         char                    who_ug_name[256];       /* user/group name */
 5035         fs_perm_t               *who_fsperm;            /* uplink */
 5036 
 5037         uu_avl_t                *who_deleg_perm_avl;    /* permissions */
 5038 } who_perm_t;
 5039 
 5040 /* */
 5041 typedef struct who_perm_node {
 5042         who_perm_t      who_perm;
 5043         uu_avl_node_t   who_avl_node;
 5044 } who_perm_node_t;
 5045 
 5046 typedef struct fs_perm_set fs_perm_set_t;
 5047 /* fs permissions */
 5048 struct fs_perm {
 5049         const char              *fsp_name;
 5050 
 5051         uu_avl_t                *fsp_sc_avl;    /* sets,create */
 5052         uu_avl_t                *fsp_uge_avl;   /* user,group,everyone */
 5053 
 5054         fs_perm_set_t           *fsp_set;       /* uplink */
 5055 };
 5056 
 5057 /* */
 5058 typedef struct fs_perm_node {
 5059         fs_perm_t       fspn_fsperm;
 5060         uu_avl_t        *fspn_avl;
 5061 
 5062         uu_list_node_t  fspn_list_node;
 5063 } fs_perm_node_t;
 5064 
 5065 /* top level structure */
 5066 struct fs_perm_set {
 5067         uu_list_pool_t  *fsps_list_pool;
 5068         uu_list_t       *fsps_list; /* list of fs_perms */
 5069 
 5070         uu_avl_pool_t   *fsps_named_set_avl_pool;
 5071         uu_avl_pool_t   *fsps_who_perm_avl_pool;
 5072         uu_avl_pool_t   *fsps_deleg_perm_avl_pool;
 5073 };
 5074 
 5075 static inline const char *
 5076 deleg_perm_type(zfs_deleg_note_t note)
 5077 {
 5078         /* subcommands */
 5079         switch (note) {
 5080                 /* SUBCOMMANDS */
 5081                 /* OTHER */
 5082         case ZFS_DELEG_NOTE_GROUPQUOTA:
 5083         case ZFS_DELEG_NOTE_GROUPUSED:
 5084         case ZFS_DELEG_NOTE_USERPROP:
 5085         case ZFS_DELEG_NOTE_USERQUOTA:
 5086         case ZFS_DELEG_NOTE_USERUSED:
 5087         case ZFS_DELEG_NOTE_USEROBJQUOTA:
 5088         case ZFS_DELEG_NOTE_USEROBJUSED:
 5089         case ZFS_DELEG_NOTE_GROUPOBJQUOTA:
 5090         case ZFS_DELEG_NOTE_GROUPOBJUSED:
 5091         case ZFS_DELEG_NOTE_PROJECTUSED:
 5092         case ZFS_DELEG_NOTE_PROJECTQUOTA:
 5093         case ZFS_DELEG_NOTE_PROJECTOBJUSED:
 5094         case ZFS_DELEG_NOTE_PROJECTOBJQUOTA:
 5095                 /* other */
 5096                 return (gettext("other"));
 5097         default:
 5098                 return (gettext("subcommand"));
 5099         }
 5100 }
 5101 
 5102 static int
 5103 who_type2weight(zfs_deleg_who_type_t who_type)
 5104 {
 5105         int res;
 5106         switch (who_type) {
 5107                 case ZFS_DELEG_NAMED_SET_SETS:
 5108                 case ZFS_DELEG_NAMED_SET:
 5109                         res = 0;
 5110                         break;
 5111                 case ZFS_DELEG_CREATE_SETS:
 5112                 case ZFS_DELEG_CREATE:
 5113                         res = 1;
 5114                         break;
 5115                 case ZFS_DELEG_USER_SETS:
 5116                 case ZFS_DELEG_USER:
 5117                         res = 2;
 5118                         break;
 5119                 case ZFS_DELEG_GROUP_SETS:
 5120                 case ZFS_DELEG_GROUP:
 5121                         res = 3;
 5122                         break;
 5123                 case ZFS_DELEG_EVERYONE_SETS:
 5124                 case ZFS_DELEG_EVERYONE:
 5125                         res = 4;
 5126                         break;
 5127                 default:
 5128                         res = -1;
 5129         }
 5130 
 5131         return (res);
 5132 }
 5133 
 5134 static int
 5135 who_perm_compare(const void *larg, const void *rarg, void *unused)
 5136 {
 5137         (void) unused;
 5138         const who_perm_node_t *l = larg;
 5139         const who_perm_node_t *r = rarg;
 5140         zfs_deleg_who_type_t ltype = l->who_perm.who_type;
 5141         zfs_deleg_who_type_t rtype = r->who_perm.who_type;
 5142         int lweight = who_type2weight(ltype);
 5143         int rweight = who_type2weight(rtype);
 5144         int res = lweight - rweight;
 5145         if (res == 0)
 5146                 res = strncmp(l->who_perm.who_name, r->who_perm.who_name,
 5147                     ZFS_MAX_DELEG_NAME-1);
 5148 
 5149         if (res == 0)
 5150                 return (0);
 5151         if (res > 0)
 5152                 return (1);
 5153         else
 5154                 return (-1);
 5155 }
 5156 
 5157 static int
 5158 deleg_perm_compare(const void *larg, const void *rarg, void *unused)
 5159 {
 5160         (void) unused;
 5161         const deleg_perm_node_t *l = larg;
 5162         const deleg_perm_node_t *r = rarg;
 5163         int res =  strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name,
 5164             ZFS_MAX_DELEG_NAME-1);
 5165 
 5166         if (res == 0)
 5167                 return (0);
 5168 
 5169         if (res > 0)
 5170                 return (1);
 5171         else
 5172                 return (-1);
 5173 }
 5174 
 5175 static inline void
 5176 fs_perm_set_init(fs_perm_set_t *fspset)
 5177 {
 5178         memset(fspset, 0, sizeof (fs_perm_set_t));
 5179 
 5180         if ((fspset->fsps_list_pool = uu_list_pool_create("fsps_list_pool",
 5181             sizeof (fs_perm_node_t), offsetof(fs_perm_node_t, fspn_list_node),
 5182             NULL, UU_DEFAULT)) == NULL)
 5183                 nomem();
 5184         if ((fspset->fsps_list = uu_list_create(fspset->fsps_list_pool, NULL,
 5185             UU_DEFAULT)) == NULL)
 5186                 nomem();
 5187 
 5188         if ((fspset->fsps_named_set_avl_pool = uu_avl_pool_create(
 5189             "named_set_avl_pool", sizeof (who_perm_node_t), offsetof(
 5190             who_perm_node_t, who_avl_node), who_perm_compare,
 5191             UU_DEFAULT)) == NULL)
 5192                 nomem();
 5193 
 5194         if ((fspset->fsps_who_perm_avl_pool = uu_avl_pool_create(
 5195             "who_perm_avl_pool", sizeof (who_perm_node_t), offsetof(
 5196             who_perm_node_t, who_avl_node), who_perm_compare,
 5197             UU_DEFAULT)) == NULL)
 5198                 nomem();
 5199 
 5200         if ((fspset->fsps_deleg_perm_avl_pool = uu_avl_pool_create(
 5201             "deleg_perm_avl_pool", sizeof (deleg_perm_node_t), offsetof(
 5202             deleg_perm_node_t, dpn_avl_node), deleg_perm_compare, UU_DEFAULT))
 5203             == NULL)
 5204                 nomem();
 5205 }
 5206 
 5207 static inline void fs_perm_fini(fs_perm_t *);
 5208 static inline void who_perm_fini(who_perm_t *);
 5209 
 5210 static inline void
 5211 fs_perm_set_fini(fs_perm_set_t *fspset)
 5212 {
 5213         fs_perm_node_t *node = uu_list_first(fspset->fsps_list);
 5214 
 5215         while (node != NULL) {
 5216                 fs_perm_node_t *next_node =
 5217                     uu_list_next(fspset->fsps_list, node);
 5218                 fs_perm_t *fsperm = &node->fspn_fsperm;
 5219                 fs_perm_fini(fsperm);
 5220                 uu_list_remove(fspset->fsps_list, node);
 5221                 free(node);
 5222                 node = next_node;
 5223         }
 5224 
 5225         uu_avl_pool_destroy(fspset->fsps_named_set_avl_pool);
 5226         uu_avl_pool_destroy(fspset->fsps_who_perm_avl_pool);
 5227         uu_avl_pool_destroy(fspset->fsps_deleg_perm_avl_pool);
 5228 }
 5229 
 5230 static inline void
 5231 deleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type,
 5232     const char *name)
 5233 {
 5234         deleg_perm->dp_who_type = type;
 5235         deleg_perm->dp_name = name;
 5236 }
 5237 
 5238 static inline void
 5239 who_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm,
 5240     zfs_deleg_who_type_t type, const char *name)
 5241 {
 5242         uu_avl_pool_t   *pool;
 5243         pool = fsperm->fsp_set->fsps_deleg_perm_avl_pool;
 5244 
 5245         memset(who_perm, 0, sizeof (who_perm_t));
 5246 
 5247         if ((who_perm->who_deleg_perm_avl = uu_avl_create(pool, NULL,
 5248             UU_DEFAULT)) == NULL)
 5249                 nomem();
 5250 
 5251         who_perm->who_type = type;
 5252         who_perm->who_name = name;
 5253         who_perm->who_fsperm = fsperm;
 5254 }
 5255 
 5256 static inline void
 5257 who_perm_fini(who_perm_t *who_perm)
 5258 {
 5259         deleg_perm_node_t *node = uu_avl_first(who_perm->who_deleg_perm_avl);
 5260 
 5261         while (node != NULL) {
 5262                 deleg_perm_node_t *next_node =
 5263                     uu_avl_next(who_perm->who_deleg_perm_avl, node);
 5264 
 5265                 uu_avl_remove(who_perm->who_deleg_perm_avl, node);
 5266                 free(node);
 5267                 node = next_node;
 5268         }
 5269 
 5270         uu_avl_destroy(who_perm->who_deleg_perm_avl);
 5271 }
 5272 
 5273 static inline void
 5274 fs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname)
 5275 {
 5276         uu_avl_pool_t   *nset_pool = fspset->fsps_named_set_avl_pool;
 5277         uu_avl_pool_t   *who_pool = fspset->fsps_who_perm_avl_pool;
 5278 
 5279         memset(fsperm, 0, sizeof (fs_perm_t));
 5280 
 5281         if ((fsperm->fsp_sc_avl = uu_avl_create(nset_pool, NULL, UU_DEFAULT))
 5282             == NULL)
 5283                 nomem();
 5284 
 5285         if ((fsperm->fsp_uge_avl = uu_avl_create(who_pool, NULL, UU_DEFAULT))
 5286             == NULL)
 5287                 nomem();
 5288 
 5289         fsperm->fsp_set = fspset;
 5290         fsperm->fsp_name = fsname;
 5291 }
 5292 
 5293 static inline void
 5294 fs_perm_fini(fs_perm_t *fsperm)
 5295 {
 5296         who_perm_node_t *node = uu_avl_first(fsperm->fsp_sc_avl);
 5297         while (node != NULL) {
 5298                 who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_sc_avl,
 5299                     node);
 5300                 who_perm_t *who_perm = &node->who_perm;
 5301                 who_perm_fini(who_perm);
 5302                 uu_avl_remove(fsperm->fsp_sc_avl, node);
 5303                 free(node);
 5304                 node = next_node;
 5305         }
 5306 
 5307         node = uu_avl_first(fsperm->fsp_uge_avl);
 5308         while (node != NULL) {
 5309                 who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_uge_avl,
 5310                     node);
 5311                 who_perm_t *who_perm = &node->who_perm;
 5312                 who_perm_fini(who_perm);
 5313                 uu_avl_remove(fsperm->fsp_uge_avl, node);
 5314                 free(node);
 5315                 node = next_node;
 5316         }
 5317 
 5318         uu_avl_destroy(fsperm->fsp_sc_avl);
 5319         uu_avl_destroy(fsperm->fsp_uge_avl);
 5320 }
 5321 
 5322 static void
 5323 set_deleg_perm_node(uu_avl_t *avl, deleg_perm_node_t *node,
 5324     zfs_deleg_who_type_t who_type, const char *name, char locality)
 5325 {
 5326         uu_avl_index_t idx = 0;
 5327 
 5328         deleg_perm_node_t *found_node = NULL;
 5329         deleg_perm_t    *deleg_perm = &node->dpn_perm;
 5330 
 5331         deleg_perm_init(deleg_perm, who_type, name);
 5332 
 5333         if ((found_node = uu_avl_find(avl, node, NULL, &idx))
 5334             == NULL)
 5335                 uu_avl_insert(avl, node, idx);
 5336         else {
 5337                 node = found_node;
 5338                 deleg_perm = &node->dpn_perm;
 5339         }
 5340 
 5341 
 5342         switch (locality) {
 5343         case ZFS_DELEG_LOCAL:
 5344                 deleg_perm->dp_local = B_TRUE;
 5345                 break;
 5346         case ZFS_DELEG_DESCENDENT:
 5347                 deleg_perm->dp_descend = B_TRUE;
 5348                 break;
 5349         case ZFS_DELEG_NA:
 5350                 break;
 5351         default:
 5352                 assert(B_FALSE); /* invalid locality */
 5353         }
 5354 }
 5355 
 5356 static inline int
 5357 parse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality)
 5358 {
 5359         nvpair_t *nvp = NULL;
 5360         fs_perm_set_t *fspset = who_perm->who_fsperm->fsp_set;
 5361         uu_avl_t *avl = who_perm->who_deleg_perm_avl;
 5362         zfs_deleg_who_type_t who_type = who_perm->who_type;
 5363 
 5364         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
 5365                 const char *name = nvpair_name(nvp);
 5366                 data_type_t type = nvpair_type(nvp);
 5367                 uu_avl_pool_t *avl_pool = fspset->fsps_deleg_perm_avl_pool;
 5368                 deleg_perm_node_t *node =
 5369                     safe_malloc(sizeof (deleg_perm_node_t));
 5370 
 5371                 VERIFY(type == DATA_TYPE_BOOLEAN);
 5372 
 5373                 uu_avl_node_init(node, &node->dpn_avl_node, avl_pool);
 5374                 set_deleg_perm_node(avl, node, who_type, name, locality);
 5375         }
 5376 
 5377         return (0);
 5378 }
 5379 
 5380 static inline int
 5381 parse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl)
 5382 {
 5383         nvpair_t *nvp = NULL;
 5384         fs_perm_set_t *fspset = fsperm->fsp_set;
 5385 
 5386         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
 5387                 nvlist_t *nvl2 = NULL;
 5388                 const char *name = nvpair_name(nvp);
 5389                 uu_avl_t *avl = NULL;
 5390                 uu_avl_pool_t *avl_pool = NULL;
 5391                 zfs_deleg_who_type_t perm_type = name[0];
 5392                 char perm_locality = name[1];
 5393                 const char *perm_name = name + 3;
 5394                 who_perm_t *who_perm = NULL;
 5395 
 5396                 assert('$' == name[2]);
 5397 
 5398                 if (nvpair_value_nvlist(nvp, &nvl2) != 0)
 5399                         return (-1);
 5400 
 5401                 switch (perm_type) {
 5402                 case ZFS_DELEG_CREATE:
 5403                 case ZFS_DELEG_CREATE_SETS:
 5404                 case ZFS_DELEG_NAMED_SET:
 5405                 case ZFS_DELEG_NAMED_SET_SETS:
 5406                         avl_pool = fspset->fsps_named_set_avl_pool;
 5407                         avl = fsperm->fsp_sc_avl;
 5408                         break;
 5409                 case ZFS_DELEG_USER:
 5410                 case ZFS_DELEG_USER_SETS:
 5411                 case ZFS_DELEG_GROUP:
 5412                 case ZFS_DELEG_GROUP_SETS:
 5413                 case ZFS_DELEG_EVERYONE:
 5414                 case ZFS_DELEG_EVERYONE_SETS:
 5415                         avl_pool = fspset->fsps_who_perm_avl_pool;
 5416                         avl = fsperm->fsp_uge_avl;
 5417                         break;
 5418 
 5419                 default:
 5420                         assert(!"unhandled zfs_deleg_who_type_t");
 5421                 }
 5422 
 5423                 who_perm_node_t *found_node = NULL;
 5424                 who_perm_node_t *node = safe_malloc(
 5425                     sizeof (who_perm_node_t));
 5426                 who_perm = &node->who_perm;
 5427                 uu_avl_index_t idx = 0;
 5428 
 5429                 uu_avl_node_init(node, &node->who_avl_node, avl_pool);
 5430                 who_perm_init(who_perm, fsperm, perm_type, perm_name);
 5431 
 5432                 if ((found_node = uu_avl_find(avl, node, NULL, &idx))
 5433                     == NULL) {
 5434                         if (avl == fsperm->fsp_uge_avl) {
 5435                                 uid_t rid = 0;
 5436                                 struct passwd *p = NULL;
 5437                                 struct group *g = NULL;
 5438                                 const char *nice_name = NULL;
 5439 
 5440                                 switch (perm_type) {
 5441                                 case ZFS_DELEG_USER_SETS:
 5442                                 case ZFS_DELEG_USER:
 5443                                         rid = atoi(perm_name);
 5444                                         p = getpwuid(rid);
 5445                                         if (p)
 5446                                                 nice_name = p->pw_name;
 5447                                         break;
 5448                                 case ZFS_DELEG_GROUP_SETS:
 5449                                 case ZFS_DELEG_GROUP:
 5450                                         rid = atoi(perm_name);
 5451                                         g = getgrgid(rid);
 5452                                         if (g)
 5453                                                 nice_name = g->gr_name;
 5454                                         break;
 5455 
 5456                                 default:
 5457                                         break;
 5458                                 }
 5459 
 5460                                 if (nice_name != NULL) {
 5461                                         (void) strlcpy(
 5462                                             node->who_perm.who_ug_name,
 5463                                             nice_name, 256);
 5464                                 } else {
 5465                                         /* User or group unknown */
 5466                                         (void) snprintf(
 5467                                             node->who_perm.who_ug_name,
 5468                                             sizeof (node->who_perm.who_ug_name),
 5469                                             "(unknown: %d)", rid);
 5470                                 }
 5471                         }
 5472 
 5473                         uu_avl_insert(avl, node, idx);
 5474                 } else {
 5475                         node = found_node;
 5476                         who_perm = &node->who_perm;
 5477                 }
 5478 
 5479                 assert(who_perm != NULL);
 5480                 (void) parse_who_perm(who_perm, nvl2, perm_locality);
 5481         }
 5482 
 5483         return (0);
 5484 }
 5485 
 5486 static inline int
 5487 parse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl)
 5488 {
 5489         nvpair_t *nvp = NULL;
 5490         uu_avl_index_t idx = 0;
 5491 
 5492         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
 5493                 nvlist_t *nvl2 = NULL;
 5494                 const char *fsname = nvpair_name(nvp);
 5495                 data_type_t type = nvpair_type(nvp);
 5496                 fs_perm_t *fsperm = NULL;
 5497                 fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t));
 5498 
 5499                 fsperm = &node->fspn_fsperm;
 5500 
 5501                 VERIFY(DATA_TYPE_NVLIST == type);
 5502 
 5503                 uu_list_node_init(node, &node->fspn_list_node,
 5504                     fspset->fsps_list_pool);
 5505 
 5506                 idx = uu_list_numnodes(fspset->fsps_list);
 5507                 fs_perm_init(fsperm, fspset, fsname);
 5508 
 5509                 if (nvpair_value_nvlist(nvp, &nvl2) != 0)
 5510                         return (-1);
 5511 
 5512                 (void) parse_fs_perm(fsperm, nvl2);
 5513 
 5514                 uu_list_insert(fspset->fsps_list, node, idx);
 5515         }
 5516 
 5517         return (0);
 5518 }
 5519 
 5520 static inline const char *
 5521 deleg_perm_comment(zfs_deleg_note_t note)
 5522 {
 5523         const char *str = "";
 5524 
 5525         /* subcommands */
 5526         switch (note) {
 5527                 /* SUBCOMMANDS */
 5528         case ZFS_DELEG_NOTE_ALLOW:
 5529                 str = gettext("Must also have the permission that is being"
 5530                     "\n\t\t\t\tallowed");
 5531                 break;
 5532         case ZFS_DELEG_NOTE_CLONE:
 5533                 str = gettext("Must also have the 'create' ability and 'mount'"
 5534                     "\n\t\t\t\tability in the origin file system");
 5535                 break;
 5536         case ZFS_DELEG_NOTE_CREATE:
 5537                 str = gettext("Must also have the 'mount' ability");
 5538                 break;
 5539         case ZFS_DELEG_NOTE_DESTROY:
 5540                 str = gettext("Must also have the 'mount' ability");
 5541                 break;
 5542         case ZFS_DELEG_NOTE_DIFF:
 5543                 str = gettext("Allows lookup of paths within a dataset;"
 5544                     "\n\t\t\t\tgiven an object number. Ordinary users need this"
 5545                     "\n\t\t\t\tin order to use zfs diff");
 5546                 break;
 5547         case ZFS_DELEG_NOTE_HOLD:
 5548                 str = gettext("Allows adding a user hold to a snapshot");
 5549                 break;
 5550         case ZFS_DELEG_NOTE_MOUNT:
 5551                 str = gettext("Allows mount/umount of ZFS datasets");
 5552                 break;
 5553         case ZFS_DELEG_NOTE_PROMOTE:
 5554                 str = gettext("Must also have the 'mount'\n\t\t\t\tand"
 5555                     " 'promote' ability in the origin file system");
 5556                 break;
 5557         case ZFS_DELEG_NOTE_RECEIVE:
 5558                 str = gettext("Must also have the 'mount' and 'create'"
 5559                     " ability");
 5560                 break;
 5561         case ZFS_DELEG_NOTE_RELEASE:
 5562                 str = gettext("Allows releasing a user hold which\n\t\t\t\t"
 5563                     "might destroy the snapshot");
 5564                 break;
 5565         case ZFS_DELEG_NOTE_RENAME:
 5566                 str = gettext("Must also have the 'mount' and 'create'"
 5567                     "\n\t\t\t\tability in the new parent");
 5568                 break;
 5569         case ZFS_DELEG_NOTE_ROLLBACK:
 5570                 str = gettext("");
 5571                 break;
 5572         case ZFS_DELEG_NOTE_SEND:
 5573                 str = gettext("");
 5574                 break;
 5575         case ZFS_DELEG_NOTE_SHARE:
 5576                 str = gettext("Allows sharing file systems over NFS or SMB"
 5577                     "\n\t\t\t\tprotocols");
 5578                 break;
 5579         case ZFS_DELEG_NOTE_SNAPSHOT:
 5580                 str = gettext("");
 5581                 break;
 5582         case ZFS_DELEG_NOTE_LOAD_KEY:
 5583                 str = gettext("Allows loading or unloading an encryption key");
 5584                 break;
 5585         case ZFS_DELEG_NOTE_CHANGE_KEY:
 5586                 str = gettext("Allows changing or adding an encryption key");
 5587                 break;
 5588 /*
 5589  *      case ZFS_DELEG_NOTE_VSCAN:
 5590  *              str = gettext("");
 5591  *              break;
 5592  */
 5593                 /* OTHER */
 5594         case ZFS_DELEG_NOTE_GROUPQUOTA:
 5595                 str = gettext("Allows accessing any groupquota@... property");
 5596                 break;
 5597         case ZFS_DELEG_NOTE_GROUPUSED:
 5598                 str = gettext("Allows reading any groupused@... property");
 5599                 break;
 5600         case ZFS_DELEG_NOTE_USERPROP:
 5601                 str = gettext("Allows changing any user property");
 5602                 break;
 5603         case ZFS_DELEG_NOTE_USERQUOTA:
 5604                 str = gettext("Allows accessing any userquota@... property");
 5605                 break;
 5606         case ZFS_DELEG_NOTE_USERUSED:
 5607                 str = gettext("Allows reading any userused@... property");
 5608                 break;
 5609         case ZFS_DELEG_NOTE_USEROBJQUOTA:
 5610                 str = gettext("Allows accessing any userobjquota@... property");
 5611                 break;
 5612         case ZFS_DELEG_NOTE_GROUPOBJQUOTA:
 5613                 str = gettext("Allows accessing any \n\t\t\t\t"
 5614                     "groupobjquota@... property");
 5615                 break;
 5616         case ZFS_DELEG_NOTE_GROUPOBJUSED:
 5617                 str = gettext("Allows reading any groupobjused@... property");
 5618                 break;
 5619         case ZFS_DELEG_NOTE_USEROBJUSED:
 5620                 str = gettext("Allows reading any userobjused@... property");
 5621                 break;
 5622         case ZFS_DELEG_NOTE_PROJECTQUOTA:
 5623                 str = gettext("Allows accessing any projectquota@... property");
 5624                 break;
 5625         case ZFS_DELEG_NOTE_PROJECTOBJQUOTA:
 5626                 str = gettext("Allows accessing any \n\t\t\t\t"
 5627                     "projectobjquota@... property");
 5628                 break;
 5629         case ZFS_DELEG_NOTE_PROJECTUSED:
 5630                 str = gettext("Allows reading any projectused@... property");
 5631                 break;
 5632         case ZFS_DELEG_NOTE_PROJECTOBJUSED:
 5633                 str = gettext("Allows accessing any \n\t\t\t\t"
 5634                     "projectobjused@... property");
 5635                 break;
 5636                 /* other */
 5637         default:
 5638                 str = "";
 5639         }
 5640 
 5641         return (str);
 5642 }
 5643 
 5644 struct allow_opts {
 5645         boolean_t local;
 5646         boolean_t descend;
 5647         boolean_t user;
 5648         boolean_t group;
 5649         boolean_t everyone;
 5650         boolean_t create;
 5651         boolean_t set;
 5652         boolean_t recursive; /* unallow only */
 5653         boolean_t prt_usage;
 5654 
 5655         boolean_t prt_perms;
 5656         char *who;
 5657         char *perms;
 5658         const char *dataset;
 5659 };
 5660 
 5661 static inline int
 5662 prop_cmp(const void *a, const void *b)
 5663 {
 5664         const char *str1 = *(const char **)a;
 5665         const char *str2 = *(const char **)b;
 5666         return (strcmp(str1, str2));
 5667 }
 5668 
 5669 static void
 5670 allow_usage(boolean_t un, boolean_t requested, const char *msg)
 5671 {
 5672         const char *opt_desc[] = {
 5673                 "-h", gettext("show this help message and exit"),
 5674                 "-l", gettext("set permission locally"),
 5675                 "-d", gettext("set permission for descents"),
 5676                 "-u", gettext("set permission for user"),
 5677                 "-g", gettext("set permission for group"),
 5678                 "-e", gettext("set permission for everyone"),
 5679                 "-c", gettext("set create time permission"),
 5680                 "-s", gettext("define permission set"),
 5681                 /* unallow only */
 5682                 "-r", gettext("remove permissions recursively"),
 5683         };
 5684         size_t unallow_size = sizeof (opt_desc) / sizeof (char *);
 5685         size_t allow_size = unallow_size - 2;
 5686         const char *props[ZFS_NUM_PROPS];
 5687         int i;
 5688         size_t count = 0;
 5689         FILE *fp = requested ? stdout : stderr;
 5690         zprop_desc_t *pdtbl = zfs_prop_get_table();
 5691         const char *fmt = gettext("%-16s %-14s\t%s\n");
 5692 
 5693         (void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW :
 5694             HELP_ALLOW));
 5695         (void) fprintf(fp, gettext("Options:\n"));
 5696         for (i = 0; i < (un ? unallow_size : allow_size); i += 2) {
 5697                 const char *opt = opt_desc[i];
 5698                 const char *optdsc = opt_desc[i + 1];
 5699                 (void) fprintf(fp, gettext("  %-10s  %s\n"), opt, optdsc);
 5700         }
 5701 
 5702         (void) fprintf(fp, gettext("\nThe following permissions are "
 5703             "supported:\n\n"));
 5704         (void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"),
 5705             gettext("NOTES"));
 5706         for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) {
 5707                 const char *perm_name = zfs_deleg_perm_tbl[i].z_perm;
 5708                 zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note;
 5709                 const char *perm_type = deleg_perm_type(perm_note);
 5710                 const char *perm_comment = deleg_perm_comment(perm_note);
 5711                 (void) fprintf(fp, fmt, perm_name, perm_type, perm_comment);
 5712         }
 5713 
 5714         for (i = 0; i < ZFS_NUM_PROPS; i++) {
 5715                 zprop_desc_t *pd = &pdtbl[i];
 5716                 if (pd->pd_visible != B_TRUE)
 5717                         continue;
 5718 
 5719                 if (pd->pd_attr == PROP_READONLY)
 5720                         continue;
 5721 
 5722                 props[count++] = pd->pd_name;
 5723         }
 5724         props[count] = NULL;
 5725 
 5726         qsort(props, count, sizeof (char *), prop_cmp);
 5727 
 5728         for (i = 0; i < count; i++)
 5729                 (void) fprintf(fp, fmt, props[i], gettext("property"), "");
 5730 
 5731         if (msg != NULL)
 5732                 (void) fprintf(fp, gettext("\nzfs: error: %s"), msg);
 5733 
 5734         exit(requested ? 0 : 2);
 5735 }
 5736 
 5737 static inline const char *
 5738 munge_args(int argc, char **argv, boolean_t un, size_t expected_argc,
 5739     char **permsp)
 5740 {
 5741         if (un && argc == expected_argc - 1)
 5742                 *permsp = NULL;
 5743         else if (argc == expected_argc)
 5744                 *permsp = argv[argc - 2];
 5745         else
 5746                 allow_usage(un, B_FALSE,
 5747                     gettext("wrong number of parameters\n"));
 5748 
 5749         return (argv[argc - 1]);
 5750 }
 5751 
 5752 static void
 5753 parse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts)
 5754 {
 5755         int uge_sum = opts->user + opts->group + opts->everyone;
 5756         int csuge_sum = opts->create + opts->set + uge_sum;
 5757         int ldcsuge_sum = csuge_sum + opts->local + opts->descend;
 5758         int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum;
 5759 
 5760         if (uge_sum > 1)
 5761                 allow_usage(un, B_FALSE,
 5762                     gettext("-u, -g, and -e are mutually exclusive\n"));
 5763 
 5764         if (opts->prt_usage) {
 5765                 if (argc == 0 && all_sum == 0)
 5766                         allow_usage(un, B_TRUE, NULL);
 5767                 else
 5768                         usage(B_FALSE);
 5769         }
 5770 
 5771         if (opts->set) {
 5772                 if (csuge_sum > 1)
 5773                         allow_usage(un, B_FALSE,
 5774                             gettext("invalid options combined with -s\n"));
 5775 
 5776                 opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
 5777                 if (argv[0][0] != '@')
 5778                         allow_usage(un, B_FALSE,
 5779                             gettext("invalid set name: missing '@' prefix\n"));
 5780                 opts->who = argv[0];
 5781         } else if (opts->create) {
 5782                 if (ldcsuge_sum > 1)
 5783                         allow_usage(un, B_FALSE,
 5784                             gettext("invalid options combined with -c\n"));
 5785                 opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
 5786         } else if (opts->everyone) {
 5787                 if (csuge_sum > 1)
 5788                         allow_usage(un, B_FALSE,
 5789                             gettext("invalid options combined with -e\n"));
 5790                 opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
 5791         } else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone")
 5792             == 0) {
 5793                 opts->everyone = B_TRUE;
 5794                 argc--;
 5795                 argv++;
 5796                 opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
 5797         } else if (argc == 1 && !un) {
 5798                 opts->prt_perms = B_TRUE;
 5799                 opts->dataset = argv[argc-1];
 5800         } else {
 5801                 opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
 5802                 opts->who = argv[0];
 5803         }
 5804 
 5805         if (!opts->local && !opts->descend) {
 5806                 opts->local = B_TRUE;
 5807                 opts->descend = B_TRUE;
 5808         }
 5809 }
 5810 
 5811 static void
 5812 store_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend,
 5813     const char *who, char *perms, nvlist_t *top_nvl)
 5814 {
 5815         int i;
 5816         char ld[2] = { '\0', '\0' };
 5817         char who_buf[MAXNAMELEN + 32];
 5818         char base_type = '\0';
 5819         char set_type = '\0';
 5820         nvlist_t *base_nvl = NULL;
 5821         nvlist_t *set_nvl = NULL;
 5822         nvlist_t *nvl;
 5823 
 5824         if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0)
 5825                 nomem();
 5826         if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) !=  0)
 5827                 nomem();
 5828 
 5829         switch (type) {
 5830         case ZFS_DELEG_NAMED_SET_SETS:
 5831         case ZFS_DELEG_NAMED_SET:
 5832                 set_type = ZFS_DELEG_NAMED_SET_SETS;
 5833                 base_type = ZFS_DELEG_NAMED_SET;
 5834                 ld[0] = ZFS_DELEG_NA;
 5835                 break;
 5836         case ZFS_DELEG_CREATE_SETS:
 5837         case ZFS_DELEG_CREATE:
 5838                 set_type = ZFS_DELEG_CREATE_SETS;
 5839                 base_type = ZFS_DELEG_CREATE;
 5840                 ld[0] = ZFS_DELEG_NA;
 5841                 break;
 5842         case ZFS_DELEG_USER_SETS:
 5843         case ZFS_DELEG_USER:
 5844                 set_type = ZFS_DELEG_USER_SETS;
 5845                 base_type = ZFS_DELEG_USER;
 5846                 if (local)
 5847                         ld[0] = ZFS_DELEG_LOCAL;
 5848                 if (descend)
 5849                         ld[1] = ZFS_DELEG_DESCENDENT;
 5850                 break;
 5851         case ZFS_DELEG_GROUP_SETS:
 5852         case ZFS_DELEG_GROUP:
 5853                 set_type = ZFS_DELEG_GROUP_SETS;
 5854                 base_type = ZFS_DELEG_GROUP;
 5855                 if (local)
 5856                         ld[0] = ZFS_DELEG_LOCAL;
 5857                 if (descend)
 5858                         ld[1] = ZFS_DELEG_DESCENDENT;
 5859                 break;
 5860         case ZFS_DELEG_EVERYONE_SETS:
 5861         case ZFS_DELEG_EVERYONE:
 5862                 set_type = ZFS_DELEG_EVERYONE_SETS;
 5863                 base_type = ZFS_DELEG_EVERYONE;
 5864                 if (local)
 5865                         ld[0] = ZFS_DELEG_LOCAL;
 5866                 if (descend)
 5867                         ld[1] = ZFS_DELEG_DESCENDENT;
 5868                 break;
 5869 
 5870         default:
 5871                 assert(set_type != '\0' && base_type != '\0');
 5872         }
 5873 
 5874         if (perms != NULL) {
 5875                 char *curr = perms;
 5876                 char *end = curr + strlen(perms);
 5877 
 5878                 while (curr < end) {
 5879                         char *delim = strchr(curr, ',');
 5880                         if (delim == NULL)
 5881                                 delim = end;
 5882                         else
 5883                                 *delim = '\0';
 5884 
 5885                         if (curr[0] == '@')
 5886                                 nvl = set_nvl;
 5887                         else
 5888                                 nvl = base_nvl;
 5889 
 5890                         (void) nvlist_add_boolean(nvl, curr);
 5891                         if (delim != end)
 5892                                 *delim = ',';
 5893                         curr = delim + 1;
 5894                 }
 5895 
 5896                 for (i = 0; i < 2; i++) {
 5897                         char locality = ld[i];
 5898                         if (locality == 0)
 5899                                 continue;
 5900 
 5901                         if (!nvlist_empty(base_nvl)) {
 5902                                 if (who != NULL)
 5903                                         (void) snprintf(who_buf,
 5904                                             sizeof (who_buf), "%c%c$%s",
 5905                                             base_type, locality, who);
 5906                                 else
 5907                                         (void) snprintf(who_buf,
 5908                                             sizeof (who_buf), "%c%c$",
 5909                                             base_type, locality);
 5910 
 5911                                 (void) nvlist_add_nvlist(top_nvl, who_buf,
 5912                                     base_nvl);
 5913                         }
 5914 
 5915 
 5916                         if (!nvlist_empty(set_nvl)) {
 5917                                 if (who != NULL)
 5918                                         (void) snprintf(who_buf,
 5919                                             sizeof (who_buf), "%c%c$%s",
 5920                                             set_type, locality, who);
 5921                                 else
 5922                                         (void) snprintf(who_buf,
 5923                                             sizeof (who_buf), "%c%c$",
 5924                                             set_type, locality);
 5925 
 5926                                 (void) nvlist_add_nvlist(top_nvl, who_buf,
 5927                                     set_nvl);
 5928                         }
 5929                 }
 5930         } else {
 5931                 for (i = 0; i < 2; i++) {
 5932                         char locality = ld[i];
 5933                         if (locality == 0)
 5934                                 continue;
 5935 
 5936                         if (who != NULL)
 5937                                 (void) snprintf(who_buf, sizeof (who_buf),
 5938                                     "%c%c$%s", base_type, locality, who);
 5939                         else
 5940                                 (void) snprintf(who_buf, sizeof (who_buf),
 5941                                     "%c%c$", base_type, locality);
 5942                         (void) nvlist_add_boolean(top_nvl, who_buf);
 5943 
 5944                         if (who != NULL)
 5945                                 (void) snprintf(who_buf, sizeof (who_buf),
 5946                                     "%c%c$%s", set_type, locality, who);
 5947                         else
 5948                                 (void) snprintf(who_buf, sizeof (who_buf),
 5949                                     "%c%c$", set_type, locality);
 5950                         (void) nvlist_add_boolean(top_nvl, who_buf);
 5951                 }
 5952         }
 5953 }
 5954 
 5955 static int
 5956 construct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp)
 5957 {
 5958         if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0)
 5959                 nomem();
 5960 
 5961         if (opts->set) {
 5962                 store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local,
 5963                     opts->descend, opts->who, opts->perms, *nvlp);
 5964         } else if (opts->create) {
 5965                 store_allow_perm(ZFS_DELEG_CREATE, opts->local,
 5966                     opts->descend, NULL, opts->perms, *nvlp);
 5967         } else if (opts->everyone) {
 5968                 store_allow_perm(ZFS_DELEG_EVERYONE, opts->local,
 5969                     opts->descend, NULL, opts->perms, *nvlp);
 5970         } else {
 5971                 char *curr = opts->who;
 5972                 char *end = curr + strlen(curr);
 5973 
 5974                 while (curr < end) {
 5975                         const char *who;
 5976                         zfs_deleg_who_type_t who_type = ZFS_DELEG_WHO_UNKNOWN;
 5977                         char *endch;
 5978                         char *delim = strchr(curr, ',');
 5979                         char errbuf[256];
 5980                         char id[64];
 5981                         struct passwd *p = NULL;
 5982                         struct group *g = NULL;
 5983 
 5984                         uid_t rid;
 5985                         if (delim == NULL)
 5986                                 delim = end;
 5987                         else
 5988                                 *delim = '\0';
 5989 
 5990                         rid = (uid_t)strtol(curr, &endch, 0);
 5991                         if (opts->user) {
 5992                                 who_type = ZFS_DELEG_USER;
 5993                                 if (*endch != '\0')
 5994                                         p = getpwnam(curr);
 5995                                 else
 5996                                         p = getpwuid(rid);
 5997 
 5998                                 if (p != NULL)
 5999                                         rid = p->pw_uid;
 6000                                 else if (*endch != '\0') {
 6001                                         (void) snprintf(errbuf, 256, gettext(
 6002                                             "invalid user %s\n"), curr);
 6003                                         allow_usage(un, B_TRUE, errbuf);
 6004                                 }
 6005                         } else if (opts->group) {
 6006                                 who_type = ZFS_DELEG_GROUP;
 6007                                 if (*endch != '\0')
 6008                                         g = getgrnam(curr);
 6009                                 else
 6010                                         g = getgrgid(rid);
 6011 
 6012                                 if (g != NULL)
 6013                                         rid = g->gr_gid;
 6014                                 else if (*endch != '\0') {
 6015                                         (void) snprintf(errbuf, 256, gettext(
 6016                                             "invalid group %s\n"),  curr);
 6017                                         allow_usage(un, B_TRUE, errbuf);
 6018                                 }
 6019                         } else {
 6020                                 if (*endch != '\0') {
 6021                                         p = getpwnam(curr);
 6022                                 } else {
 6023                                         p = getpwuid(rid);
 6024                                 }
 6025 
 6026                                 if (p == NULL) {
 6027                                         if (*endch != '\0') {
 6028                                                 g = getgrnam(curr);
 6029                                         } else {
 6030                                                 g = getgrgid(rid);
 6031                                         }
 6032                                 }
 6033 
 6034                                 if (p != NULL) {
 6035                                         who_type = ZFS_DELEG_USER;
 6036                                         rid = p->pw_uid;
 6037                                 } else if (g != NULL) {
 6038                                         who_type = ZFS_DELEG_GROUP;
 6039                                         rid = g->gr_gid;
 6040                                 } else {
 6041                                         (void) snprintf(errbuf, 256, gettext(
 6042                                             "invalid user/group %s\n"), curr);
 6043                                         allow_usage(un, B_TRUE, errbuf);
 6044                                 }
 6045                         }
 6046 
 6047                         (void) sprintf(id, "%u", rid);
 6048                         who = id;
 6049 
 6050                         store_allow_perm(who_type, opts->local,
 6051                             opts->descend, who, opts->perms, *nvlp);
 6052                         curr = delim + 1;
 6053                 }
 6054         }
 6055 
 6056         return (0);
 6057 }
 6058 
 6059 static void
 6060 print_set_creat_perms(uu_avl_t *who_avl)
 6061 {
 6062         const char *sc_title[] = {
 6063                 gettext("Permission sets:\n"),
 6064                 gettext("Create time permissions:\n"),
 6065                 NULL
 6066         };
 6067         who_perm_node_t *who_node = NULL;
 6068         int prev_weight = -1;
 6069 
 6070         for (who_node = uu_avl_first(who_avl); who_node != NULL;
 6071             who_node = uu_avl_next(who_avl, who_node)) {
 6072                 uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
 6073                 zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
 6074                 const char *who_name = who_node->who_perm.who_name;
 6075                 int weight = who_type2weight(who_type);
 6076                 boolean_t first = B_TRUE;
 6077                 deleg_perm_node_t *deleg_node;
 6078 
 6079                 if (prev_weight != weight) {
 6080                         (void) printf("%s", sc_title[weight]);
 6081                         prev_weight = weight;
 6082                 }
 6083 
 6084                 if (who_name == NULL || strnlen(who_name, 1) == 0)
 6085                         (void) printf("\t");
 6086                 else
 6087                         (void) printf("\t%s ", who_name);
 6088 
 6089                 for (deleg_node = uu_avl_first(avl); deleg_node != NULL;
 6090                     deleg_node = uu_avl_next(avl, deleg_node)) {
 6091                         if (first) {
 6092                                 (void) printf("%s",
 6093                                     deleg_node->dpn_perm.dp_name);
 6094                                 first = B_FALSE;
 6095                         } else
 6096                                 (void) printf(",%s",
 6097                                     deleg_node->dpn_perm.dp_name);
 6098                 }
 6099 
 6100                 (void) printf("\n");
 6101         }
 6102 }
 6103 
 6104 static void
 6105 print_uge_deleg_perms(uu_avl_t *who_avl, boolean_t local, boolean_t descend,
 6106     const char *title)
 6107 {
 6108         who_perm_node_t *who_node = NULL;
 6109         boolean_t prt_title = B_TRUE;
 6110         uu_avl_walk_t *walk;
 6111 
 6112         if ((walk = uu_avl_walk_start(who_avl, UU_WALK_ROBUST)) == NULL)
 6113                 nomem();
 6114 
 6115         while ((who_node = uu_avl_walk_next(walk)) != NULL) {
 6116                 const char *who_name = who_node->who_perm.who_name;
 6117                 const char *nice_who_name = who_node->who_perm.who_ug_name;
 6118                 uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
 6119                 zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
 6120                 char delim = ' ';
 6121                 deleg_perm_node_t *deleg_node;
 6122                 boolean_t prt_who = B_TRUE;
 6123 
 6124                 for (deleg_node = uu_avl_first(avl);
 6125                     deleg_node != NULL;
 6126                     deleg_node = uu_avl_next(avl, deleg_node)) {
 6127                         if (local != deleg_node->dpn_perm.dp_local ||
 6128                             descend != deleg_node->dpn_perm.dp_descend)
 6129                                 continue;
 6130 
 6131                         if (prt_who) {
 6132                                 const char *who = NULL;
 6133                                 if (prt_title) {
 6134                                         prt_title = B_FALSE;
 6135                                         (void) printf("%s", title);
 6136                                 }
 6137 
 6138                                 switch (who_type) {
 6139                                 case ZFS_DELEG_USER_SETS:
 6140                                 case ZFS_DELEG_USER:
 6141                                         who = gettext("user");
 6142                                         if (nice_who_name)
 6143                                                 who_name  = nice_who_name;
 6144                                         break;
 6145                                 case ZFS_DELEG_GROUP_SETS:
 6146                                 case ZFS_DELEG_GROUP:
 6147                                         who = gettext("group");
 6148                                         if (nice_who_name)
 6149                                                 who_name  = nice_who_name;
 6150                                         break;
 6151                                 case ZFS_DELEG_EVERYONE_SETS:
 6152                                 case ZFS_DELEG_EVERYONE:
 6153                                         who = gettext("everyone");
 6154                                         who_name = NULL;
 6155                                         break;
 6156 
 6157                                 default:
 6158                                         assert(who != NULL);
 6159                                 }
 6160 
 6161                                 prt_who = B_FALSE;
 6162                                 if (who_name == NULL)
 6163                                         (void) printf("\t%s", who);
 6164                                 else
 6165                                         (void) printf("\t%s %s", who, who_name);
 6166                         }
 6167 
 6168                         (void) printf("%c%s", delim,
 6169                             deleg_node->dpn_perm.dp_name);
 6170                         delim = ',';
 6171                 }
 6172 
 6173                 if (!prt_who)
 6174                         (void) printf("\n");
 6175         }
 6176 
 6177         uu_avl_walk_end(walk);
 6178 }
 6179 
 6180 static void
 6181 print_fs_perms(fs_perm_set_t *fspset)
 6182 {
 6183         fs_perm_node_t *node = NULL;
 6184         char buf[MAXNAMELEN + 32];
 6185         const char *dsname = buf;
 6186 
 6187         for (node = uu_list_first(fspset->fsps_list); node != NULL;
 6188             node = uu_list_next(fspset->fsps_list, node)) {
 6189                 uu_avl_t *sc_avl = node->fspn_fsperm.fsp_sc_avl;
 6190                 uu_avl_t *uge_avl = node->fspn_fsperm.fsp_uge_avl;
 6191                 int left = 0;
 6192 
 6193                 (void) snprintf(buf, sizeof (buf),
 6194                     gettext("---- Permissions on %s "),
 6195                     node->fspn_fsperm.fsp_name);
 6196                 (void) printf("%s", dsname);
 6197                 left = 70 - strlen(buf);
 6198                 while (left-- > 0)
 6199                         (void) printf("-");
 6200                 (void) printf("\n");
 6201 
 6202                 print_set_creat_perms(sc_avl);
 6203                 print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE,
 6204                     gettext("Local permissions:\n"));
 6205                 print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE,
 6206                     gettext("Descendent permissions:\n"));
 6207                 print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE,
 6208                     gettext("Local+Descendent permissions:\n"));
 6209         }
 6210 }
 6211 
 6212 static fs_perm_set_t fs_perm_set = { NULL, NULL, NULL, NULL };
 6213 
 6214 struct deleg_perms {
 6215         boolean_t un;
 6216         nvlist_t *nvl;
 6217 };
 6218 
 6219 static int
 6220 set_deleg_perms(zfs_handle_t *zhp, void *data)
 6221 {
 6222         struct deleg_perms *perms = (struct deleg_perms *)data;
 6223         zfs_type_t zfs_type = zfs_get_type(zhp);
 6224 
 6225         if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME)
 6226                 return (0);
 6227 
 6228         return (zfs_set_fsacl(zhp, perms->un, perms->nvl));
 6229 }
 6230 
 6231 static int
 6232 zfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un)
 6233 {
 6234         zfs_handle_t *zhp;
 6235         nvlist_t *perm_nvl = NULL;
 6236         nvlist_t *update_perm_nvl = NULL;
 6237         int error = 1;
 6238         int c;
 6239         struct allow_opts opts = { 0 };
 6240 
 6241         const char *optstr = un ? "ldugecsrh" : "ldugecsh";
 6242 
 6243         /* check opts */
 6244         while ((c = getopt(argc, argv, optstr)) != -1) {
 6245                 switch (c) {
 6246                 case 'l':
 6247                         opts.local = B_TRUE;
 6248                         break;
 6249                 case 'd':
 6250                         opts.descend = B_TRUE;
 6251                         break;
 6252                 case 'u':
 6253                         opts.user = B_TRUE;
 6254                         break;
 6255                 case 'g':
 6256                         opts.group = B_TRUE;
 6257                         break;
 6258                 case 'e':
 6259                         opts.everyone = B_TRUE;
 6260                         break;
 6261                 case 's':
 6262                         opts.set = B_TRUE;
 6263                         break;
 6264                 case 'c':
 6265                         opts.create = B_TRUE;
 6266                         break;
 6267                 case 'r':
 6268                         opts.recursive = B_TRUE;
 6269                         break;
 6270                 case ':':
 6271                         (void) fprintf(stderr, gettext("missing argument for "
 6272                             "'%c' option\n"), optopt);
 6273                         usage(B_FALSE);
 6274                         break;
 6275                 case 'h':
 6276                         opts.prt_usage = B_TRUE;
 6277                         break;
 6278                 case '?':
 6279                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 6280                             optopt);
 6281                         usage(B_FALSE);
 6282                 }
 6283         }
 6284 
 6285         argc -= optind;
 6286         argv += optind;
 6287 
 6288         /* check arguments */
 6289         parse_allow_args(argc, argv, un, &opts);
 6290 
 6291         /* try to open the dataset */
 6292         if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM |
 6293             ZFS_TYPE_VOLUME)) == NULL) {
 6294                 (void) fprintf(stderr, "Failed to open dataset: %s\n",
 6295                     opts.dataset);
 6296                 return (-1);
 6297         }
 6298 
 6299         if (zfs_get_fsacl(zhp, &perm_nvl) != 0)
 6300                 goto cleanup2;
 6301 
 6302         fs_perm_set_init(&fs_perm_set);
 6303         if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) {
 6304                 (void) fprintf(stderr, "Failed to parse fsacl permissions\n");
 6305                 goto cleanup1;
 6306         }
 6307 
 6308         if (opts.prt_perms)
 6309                 print_fs_perms(&fs_perm_set);
 6310         else {
 6311                 (void) construct_fsacl_list(un, &opts, &update_perm_nvl);
 6312                 if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0)
 6313                         goto cleanup0;
 6314 
 6315                 if (un && opts.recursive) {
 6316                         struct deleg_perms data = { un, update_perm_nvl };
 6317                         if (zfs_iter_filesystems(zhp, 0, set_deleg_perms,
 6318                             &data) != 0)
 6319                                 goto cleanup0;
 6320                 }
 6321         }
 6322 
 6323         error = 0;
 6324 
 6325 cleanup0:
 6326         nvlist_free(perm_nvl);
 6327         nvlist_free(update_perm_nvl);
 6328 cleanup1:
 6329         fs_perm_set_fini(&fs_perm_set);
 6330 cleanup2:
 6331         zfs_close(zhp);
 6332 
 6333         return (error);
 6334 }
 6335 
 6336 static int
 6337 zfs_do_allow(int argc, char **argv)
 6338 {
 6339         return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE));
 6340 }
 6341 
 6342 static int
 6343 zfs_do_unallow(int argc, char **argv)
 6344 {
 6345         return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE));
 6346 }
 6347 
 6348 static int
 6349 zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
 6350 {
 6351         int errors = 0;
 6352         int i;
 6353         const char *tag;
 6354         boolean_t recursive = B_FALSE;
 6355         const char *opts = holding ? "rt" : "r";
 6356         int c;
 6357 
 6358         /* check options */
 6359         while ((c = getopt(argc, argv, opts)) != -1) {
 6360                 switch (c) {
 6361                 case 'r':
 6362                         recursive = B_TRUE;
 6363                         break;
 6364                 case '?':
 6365                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 6366                             optopt);
 6367                         usage(B_FALSE);
 6368                 }
 6369         }
 6370 
 6371         argc -= optind;
 6372         argv += optind;
 6373 
 6374         /* check number of arguments */
 6375         if (argc < 2)
 6376                 usage(B_FALSE);
 6377 
 6378         tag = argv[0];
 6379         --argc;
 6380         ++argv;
 6381 
 6382         if (holding && tag[0] == '.') {
 6383                 /* tags starting with '.' are reserved for libzfs */
 6384                 (void) fprintf(stderr, gettext("tag may not start with '.'\n"));
 6385                 usage(B_FALSE);
 6386         }
 6387 
 6388         for (i = 0; i < argc; ++i) {
 6389                 zfs_handle_t *zhp;
 6390                 char parent[ZFS_MAX_DATASET_NAME_LEN];
 6391                 const char *delim;
 6392                 char *path = argv[i];
 6393 
 6394                 delim = strchr(path, '@');
 6395                 if (delim == NULL) {
 6396                         (void) fprintf(stderr,
 6397                             gettext("'%s' is not a snapshot\n"), path);
 6398                         ++errors;
 6399                         continue;
 6400                 }
 6401                 (void) strlcpy(parent, path, MIN(sizeof (parent),
 6402                     delim - path + 1));
 6403 
 6404                 zhp = zfs_open(g_zfs, parent,
 6405                     ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
 6406                 if (zhp == NULL) {
 6407                         ++errors;
 6408                         continue;
 6409                 }
 6410                 if (holding) {
 6411                         if (zfs_hold(zhp, delim+1, tag, recursive, -1) != 0)
 6412                                 ++errors;
 6413                 } else {
 6414                         if (zfs_release(zhp, delim+1, tag, recursive) != 0)
 6415                                 ++errors;
 6416                 }
 6417                 zfs_close(zhp);
 6418         }
 6419 
 6420         return (errors != 0);
 6421 }
 6422 
 6423 /*
 6424  * zfs hold [-r] [-t] <tag> <snap> ...
 6425  *
 6426  *      -r      Recursively hold
 6427  *
 6428  * Apply a user-hold with the given tag to the list of snapshots.
 6429  */
 6430 static int
 6431 zfs_do_hold(int argc, char **argv)
 6432 {
 6433         return (zfs_do_hold_rele_impl(argc, argv, B_TRUE));
 6434 }
 6435 
 6436 /*
 6437  * zfs release [-r] <tag> <snap> ...
 6438  *
 6439  *      -r      Recursively release
 6440  *
 6441  * Release a user-hold with the given tag from the list of snapshots.
 6442  */
 6443 static int
 6444 zfs_do_release(int argc, char **argv)
 6445 {
 6446         return (zfs_do_hold_rele_impl(argc, argv, B_FALSE));
 6447 }
 6448 
 6449 typedef struct holds_cbdata {
 6450         boolean_t       cb_recursive;
 6451         const char      *cb_snapname;
 6452         nvlist_t        **cb_nvlp;
 6453         size_t          cb_max_namelen;
 6454         size_t          cb_max_taglen;
 6455 } holds_cbdata_t;
 6456 
 6457 #define STRFTIME_FMT_STR "%a %b %e %H:%M %Y"
 6458 #define DATETIME_BUF_LEN (32)
 6459 /*
 6460  *
 6461  */
 6462 static void
 6463 print_holds(boolean_t scripted, int nwidth, int tagwidth, nvlist_t *nvl,
 6464     boolean_t parsable)
 6465 {
 6466         int i;
 6467         nvpair_t *nvp = NULL;
 6468         const char *const hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" };
 6469         const char *col;
 6470 
 6471         if (!scripted) {
 6472                 for (i = 0; i < 3; i++) {
 6473                         col = gettext(hdr_cols[i]);
 6474                         if (i < 2)
 6475                                 (void) printf("%-*s  ", i ? tagwidth : nwidth,
 6476                                     col);
 6477                         else
 6478                                 (void) printf("%s\n", col);
 6479                 }
 6480         }
 6481 
 6482         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
 6483                 char *zname = nvpair_name(nvp);
 6484                 nvlist_t *nvl2;
 6485                 nvpair_t *nvp2 = NULL;
 6486                 (void) nvpair_value_nvlist(nvp, &nvl2);
 6487                 while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) {
 6488                         char tsbuf[DATETIME_BUF_LEN];
 6489                         const char *tagname = nvpair_name(nvp2);
 6490                         uint64_t val = 0;
 6491                         time_t time;
 6492                         struct tm t;
 6493 
 6494                         (void) nvpair_value_uint64(nvp2, &val);
 6495                         time = (time_t)val;
 6496                         (void) localtime_r(&time, &t);
 6497                         (void) strftime(tsbuf, DATETIME_BUF_LEN,
 6498                             gettext(STRFTIME_FMT_STR), &t);
 6499 
 6500                         if (scripted) {
 6501                                 if (parsable) {
 6502                                         (void) printf("%s\t%s\t%ld\n", zname,
 6503                                             tagname, (unsigned long)time);
 6504                                 } else {
 6505                                         (void) printf("%s\t%s\t%s\n", zname,
 6506                                             tagname, tsbuf);
 6507                                 }
 6508                         } else {
 6509                                 if (parsable) {
 6510                                         (void) printf("%-*s  %-*s  %ld\n",
 6511                                             nwidth, zname, tagwidth,
 6512                                             tagname, (unsigned long)time);
 6513                                 } else {
 6514                                         (void) printf("%-*s  %-*s  %s\n",
 6515                                             nwidth, zname, tagwidth,
 6516                                             tagname, tsbuf);
 6517                                 }
 6518                         }
 6519                 }
 6520         }
 6521 }
 6522 
 6523 /*
 6524  * Generic callback function to list a dataset or snapshot.
 6525  */
 6526 static int
 6527 holds_callback(zfs_handle_t *zhp, void *data)
 6528 {
 6529         holds_cbdata_t *cbp = data;
 6530         nvlist_t *top_nvl = *cbp->cb_nvlp;
 6531         nvlist_t *nvl = NULL;
 6532         nvpair_t *nvp = NULL;
 6533         const char *zname = zfs_get_name(zhp);
 6534         size_t znamelen = strlen(zname);
 6535 
 6536         if (cbp->cb_recursive) {
 6537                 const char *snapname;
 6538                 char *delim  = strchr(zname, '@');
 6539                 if (delim == NULL)
 6540                         return (0);
 6541 
 6542                 snapname = delim + 1;
 6543                 if (strcmp(cbp->cb_snapname, snapname))
 6544                         return (0);
 6545         }
 6546 
 6547         if (zfs_get_holds(zhp, &nvl) != 0)
 6548                 return (-1);
 6549 
 6550         if (znamelen > cbp->cb_max_namelen)
 6551                 cbp->cb_max_namelen  = znamelen;
 6552 
 6553         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
 6554                 const char *tag = nvpair_name(nvp);
 6555                 size_t taglen = strlen(tag);
 6556                 if (taglen > cbp->cb_max_taglen)
 6557                         cbp->cb_max_taglen  = taglen;
 6558         }
 6559 
 6560         return (nvlist_add_nvlist(top_nvl, zname, nvl));
 6561 }
 6562 
 6563 /*
 6564  * zfs holds [-rHp] <snap> ...
 6565  *
 6566  *      -r      Lists holds that are set on the named snapshots recursively.
 6567  *      -H      Scripted mode; elide headers and separate columns by tabs.
 6568  *      -p      Display values in parsable (literal) format.
 6569  */
 6570 static int
 6571 zfs_do_holds(int argc, char **argv)
 6572 {
 6573         int c;
 6574         boolean_t errors = B_FALSE;
 6575         boolean_t scripted = B_FALSE;
 6576         boolean_t recursive = B_FALSE;
 6577         boolean_t parsable = B_FALSE;
 6578 
 6579         int types = ZFS_TYPE_SNAPSHOT;
 6580         holds_cbdata_t cb = { 0 };
 6581 
 6582         int limit = 0;
 6583         int ret = 0;
 6584         int flags = 0;
 6585 
 6586         /* check options */
 6587         while ((c = getopt(argc, argv, "rHp")) != -1) {
 6588                 switch (c) {
 6589                 case 'r':
 6590                         recursive = B_TRUE;
 6591                         break;
 6592                 case 'H':
 6593                         scripted = B_TRUE;
 6594                         break;
 6595                 case 'p':
 6596                         parsable = B_TRUE;
 6597                         break;
 6598                 case '?':
 6599                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 6600                             optopt);
 6601                         usage(B_FALSE);
 6602                 }
 6603         }
 6604 
 6605         if (recursive) {
 6606                 types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
 6607                 flags |= ZFS_ITER_RECURSE;
 6608         }
 6609 
 6610         argc -= optind;
 6611         argv += optind;
 6612 
 6613         /* check number of arguments */
 6614         if (argc < 1)
 6615                 usage(B_FALSE);
 6616 
 6617         nvlist_t *nvl = fnvlist_alloc();
 6618 
 6619         for (int i = 0; i < argc; ++i) {
 6620                 char *snapshot = argv[i];
 6621                 const char *delim;
 6622                 const char *snapname;
 6623 
 6624                 delim = strchr(snapshot, '@');
 6625                 if (delim == NULL) {
 6626                         (void) fprintf(stderr,
 6627                             gettext("'%s' is not a snapshot\n"), snapshot);
 6628                         errors = B_TRUE;
 6629                         continue;
 6630                 }
 6631                 snapname = delim + 1;
 6632                 if (recursive)
 6633                         snapshot[delim - snapshot] = '\0';
 6634 
 6635                 cb.cb_recursive = recursive;
 6636                 cb.cb_snapname = snapname;
 6637                 cb.cb_nvlp = &nvl;
 6638 
 6639                 /*
 6640                  *  1. collect holds data, set format options
 6641                  */
 6642                 ret = zfs_for_each(1, argv + i, flags, types, NULL, NULL, limit,
 6643                     holds_callback, &cb);
 6644                 if (ret != 0)
 6645                         errors = B_TRUE;
 6646         }
 6647 
 6648         /*
 6649          *  2. print holds data
 6650          */
 6651         print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl,
 6652             parsable);
 6653 
 6654         if (nvlist_empty(nvl))
 6655                 (void) fprintf(stderr, gettext("no datasets available\n"));
 6656 
 6657         nvlist_free(nvl);
 6658 
 6659         return (errors);
 6660 }
 6661 
 6662 #define CHECK_SPINNER 30
 6663 #define SPINNER_TIME 3          /* seconds */
 6664 #define MOUNT_TIME 1            /* seconds */
 6665 
 6666 typedef struct get_all_state {
 6667         boolean_t       ga_verbose;
 6668         get_all_cb_t    *ga_cbp;
 6669 } get_all_state_t;
 6670 
 6671 static int
 6672 get_one_dataset(zfs_handle_t *zhp, void *data)
 6673 {
 6674         static const char *const spin[] = { "-", "\\", "|", "/" };
 6675         static int spinval = 0;
 6676         static int spincheck = 0;
 6677         static time_t last_spin_time = (time_t)0;
 6678         get_all_state_t *state = data;
 6679         zfs_type_t type = zfs_get_type(zhp);
 6680 
 6681         if (state->ga_verbose) {
 6682                 if (--spincheck < 0) {
 6683                         time_t now = time(NULL);
 6684                         if (last_spin_time + SPINNER_TIME < now) {
 6685                                 update_progress(spin[spinval++ % 4]);
 6686                                 last_spin_time = now;
 6687                         }
 6688                         spincheck = CHECK_SPINNER;
 6689                 }
 6690         }
 6691 
 6692         /*
 6693          * Iterate over any nested datasets.
 6694          */
 6695         if (zfs_iter_filesystems(zhp, 0, get_one_dataset, data) != 0) {
 6696                 zfs_close(zhp);
 6697                 return (1);
 6698         }
 6699 
 6700         /*
 6701          * Skip any datasets whose type does not match.
 6702          */
 6703         if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
 6704                 zfs_close(zhp);
 6705                 return (0);
 6706         }
 6707         libzfs_add_handle(state->ga_cbp, zhp);
 6708         assert(state->ga_cbp->cb_used <= state->ga_cbp->cb_alloc);
 6709 
 6710         return (0);
 6711 }
 6712 
 6713 static void
 6714 get_all_datasets(get_all_cb_t *cbp, boolean_t verbose)
 6715 {
 6716         get_all_state_t state = {
 6717             .ga_verbose = verbose,
 6718             .ga_cbp = cbp
 6719         };
 6720 
 6721         if (verbose)
 6722                 set_progress_header(gettext("Reading ZFS config"));
 6723         (void) zfs_iter_root(g_zfs, get_one_dataset, &state);
 6724 
 6725         if (verbose)
 6726                 finish_progress(gettext("done."));
 6727 }
 6728 
 6729 /*
 6730  * Generic callback for sharing or mounting filesystems.  Because the code is so
 6731  * similar, we have a common function with an extra parameter to determine which
 6732  * mode we are using.
 6733  */
 6734 typedef enum { OP_SHARE, OP_MOUNT } share_mount_op_t;
 6735 
 6736 typedef struct share_mount_state {
 6737         share_mount_op_t        sm_op;
 6738         boolean_t       sm_verbose;
 6739         int     sm_flags;
 6740         char    *sm_options;
 6741         enum sa_protocol        sm_proto; /* only valid for OP_SHARE */
 6742         pthread_mutex_t sm_lock; /* protects the remaining fields */
 6743         uint_t  sm_total; /* number of filesystems to process */
 6744         uint_t  sm_done; /* number of filesystems processed */
 6745         int     sm_status; /* -1 if any of the share/mount operations failed */
 6746 } share_mount_state_t;
 6747 
 6748 /*
 6749  * Share or mount a dataset.
 6750  */
 6751 static int
 6752 share_mount_one(zfs_handle_t *zhp, int op, int flags, enum sa_protocol protocol,
 6753     boolean_t explicit, const char *options)
 6754 {
 6755         char mountpoint[ZFS_MAXPROPLEN];
 6756         char shareopts[ZFS_MAXPROPLEN];
 6757         char smbshareopts[ZFS_MAXPROPLEN];
 6758         const char *cmdname = op == OP_SHARE ? "share" : "mount";
 6759         struct mnttab mnt;
 6760         uint64_t zoned, canmount;
 6761         boolean_t shared_nfs, shared_smb;
 6762 
 6763         assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM);
 6764 
 6765         /*
 6766          * Check to make sure we can mount/share this dataset.  If we
 6767          * are in the global zone and the filesystem is exported to a
 6768          * local zone, or if we are in a local zone and the
 6769          * filesystem is not exported, then it is an error.
 6770          */
 6771         zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
 6772 
 6773         if (zoned && getzoneid() == GLOBAL_ZONEID) {
 6774                 if (!explicit)
 6775                         return (0);
 6776 
 6777                 (void) fprintf(stderr, gettext("cannot %s '%s': "
 6778                     "dataset is exported to a local zone\n"), cmdname,
 6779                     zfs_get_name(zhp));
 6780                 return (1);
 6781 
 6782         } else if (!zoned && getzoneid() != GLOBAL_ZONEID) {
 6783                 if (!explicit)
 6784                         return (0);
 6785 
 6786                 (void) fprintf(stderr, gettext("cannot %s '%s': "
 6787                     "permission denied\n"), cmdname,
 6788                     zfs_get_name(zhp));
 6789                 return (1);
 6790         }
 6791 
 6792         /*
 6793          * Ignore any filesystems which don't apply to us. This
 6794          * includes those with a legacy mountpoint, or those with
 6795          * legacy share options.
 6796          */
 6797         verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
 6798             sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
 6799         verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,
 6800             sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);
 6801         verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts,
 6802             sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0);
 6803 
 6804         if (op == OP_SHARE && strcmp(shareopts, "off") == 0 &&
 6805             strcmp(smbshareopts, "off") == 0) {
 6806                 if (!explicit)
 6807                         return (0);
 6808 
 6809                 (void) fprintf(stderr, gettext("cannot share '%s': "
 6810                     "legacy share\n"), zfs_get_name(zhp));
 6811                 (void) fprintf(stderr, gettext("use exports(5) or "
 6812                     "smb.conf(5) to share this filesystem, or set "
 6813                     "the sharenfs or sharesmb property\n"));
 6814                 return (1);
 6815         }
 6816 
 6817         /*
 6818          * We cannot share or mount legacy filesystems. If the
 6819          * shareopts is non-legacy but the mountpoint is legacy, we
 6820          * treat it as a legacy share.
 6821          */
 6822         if (strcmp(mountpoint, "legacy") == 0) {
 6823                 if (!explicit)
 6824                         return (0);
 6825 
 6826                 (void) fprintf(stderr, gettext("cannot %s '%s': "
 6827                     "legacy mountpoint\n"), cmdname, zfs_get_name(zhp));
 6828                 (void) fprintf(stderr, gettext("use %s(8) to "
 6829                     "%s this filesystem\n"), cmdname, cmdname);
 6830                 return (1);
 6831         }
 6832 
 6833         if (strcmp(mountpoint, "none") == 0) {
 6834                 if (!explicit)
 6835                         return (0);
 6836 
 6837                 (void) fprintf(stderr, gettext("cannot %s '%s': no "
 6838                     "mountpoint set\n"), cmdname, zfs_get_name(zhp));
 6839                 return (1);
 6840         }
 6841 
 6842         /*
 6843          * canmount     explicit        outcome
 6844          * on           no              pass through
 6845          * on           yes             pass through
 6846          * off          no              return 0
 6847          * off          yes             display error, return 1
 6848          * noauto       no              return 0
 6849          * noauto       yes             pass through
 6850          */
 6851         canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
 6852         if (canmount == ZFS_CANMOUNT_OFF) {
 6853                 if (!explicit)
 6854                         return (0);
 6855 
 6856                 (void) fprintf(stderr, gettext("cannot %s '%s': "
 6857                     "'canmount' property is set to 'off'\n"), cmdname,
 6858                     zfs_get_name(zhp));
 6859                 return (1);
 6860         } else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) {
 6861                 /*
 6862                  * When performing a 'zfs mount -a', we skip any mounts for
 6863                  * datasets that have 'noauto' set. Sharing a dataset with
 6864                  * 'noauto' set is only allowed if it's mounted.
 6865                  */
 6866                 if (op == OP_MOUNT)
 6867                         return (0);
 6868                 if (op == OP_SHARE && !zfs_is_mounted(zhp, NULL)) {
 6869                         /* also purge it from existing exports */
 6870                         zfs_unshare(zhp, mountpoint, NULL);
 6871                         return (0);
 6872                 }
 6873         }
 6874 
 6875         /*
 6876          * If this filesystem is encrypted and does not have
 6877          * a loaded key, we can not mount it.
 6878          */
 6879         if ((flags & MS_CRYPT) == 0 &&
 6880             zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF &&
 6881             zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS) ==
 6882             ZFS_KEYSTATUS_UNAVAILABLE) {
 6883                 if (!explicit)
 6884                         return (0);
 6885 
 6886                 (void) fprintf(stderr, gettext("cannot %s '%s': "
 6887                     "encryption key not loaded\n"), cmdname, zfs_get_name(zhp));
 6888                 return (1);
 6889         }
 6890 
 6891         /*
 6892          * If this filesystem is inconsistent and has a receive resume
 6893          * token, we can not mount it.
 6894          */
 6895         if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) &&
 6896             zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
 6897             NULL, 0, NULL, NULL, 0, B_TRUE) == 0) {
 6898                 if (!explicit)
 6899                         return (0);
 6900 
 6901                 (void) fprintf(stderr, gettext("cannot %s '%s': "
 6902                     "Contains partially-completed state from "
 6903                     "\"zfs receive -s\", which can be resumed with "
 6904                     "\"zfs send -t\"\n"),
 6905                     cmdname, zfs_get_name(zhp));
 6906                 return (1);
 6907         }
 6908 
 6909         if (zfs_prop_get_int(zhp, ZFS_PROP_REDACTED) && !(flags & MS_FORCE)) {
 6910                 if (!explicit)
 6911                         return (0);
 6912 
 6913                 (void) fprintf(stderr, gettext("cannot %s '%s': "
 6914                     "Dataset is not complete, was created by receiving "
 6915                     "a redacted zfs send stream.\n"), cmdname,
 6916                     zfs_get_name(zhp));
 6917                 return (1);
 6918         }
 6919 
 6920         /*
 6921          * At this point, we have verified that the mountpoint and/or
 6922          * shareopts are appropriate for auto management. If the
 6923          * filesystem is already mounted or shared, return (failing
 6924          * for explicit requests); otherwise mount or share the
 6925          * filesystem.
 6926          */
 6927         switch (op) {
 6928         case OP_SHARE: {
 6929                 enum sa_protocol prot[] = {SA_PROTOCOL_NFS, SA_NO_PROTOCOL};
 6930                 shared_nfs = zfs_is_shared(zhp, NULL, prot);
 6931                 *prot = SA_PROTOCOL_SMB;
 6932                 shared_smb = zfs_is_shared(zhp, NULL, prot);
 6933 
 6934                 if ((shared_nfs && shared_smb) ||
 6935                     (shared_nfs && strcmp(shareopts, "on") == 0 &&
 6936                     strcmp(smbshareopts, "off") == 0) ||
 6937                     (shared_smb && strcmp(smbshareopts, "on") == 0 &&
 6938                     strcmp(shareopts, "off") == 0)) {
 6939                         if (!explicit)
 6940                                 return (0);
 6941 
 6942                         (void) fprintf(stderr, gettext("cannot share "
 6943                             "'%s': filesystem already shared\n"),
 6944                             zfs_get_name(zhp));
 6945                         return (1);
 6946                 }
 6947 
 6948                 if (!zfs_is_mounted(zhp, NULL) &&
 6949                     zfs_mount(zhp, NULL, flags) != 0)
 6950                         return (1);
 6951 
 6952                 *prot = protocol;
 6953                 if (zfs_share(zhp, protocol == SA_NO_PROTOCOL ? NULL : prot))
 6954                         return (1);
 6955 
 6956         }
 6957                 break;
 6958 
 6959         case OP_MOUNT:
 6960                 mnt.mnt_mntopts = (char *)(options ?: "");
 6961 
 6962                 if (!hasmntopt(&mnt, MNTOPT_REMOUNT) &&
 6963                     zfs_is_mounted(zhp, NULL)) {
 6964                         if (!explicit)
 6965                                 return (0);
 6966 
 6967                         (void) fprintf(stderr, gettext("cannot mount "
 6968                             "'%s': filesystem already mounted\n"),
 6969                             zfs_get_name(zhp));
 6970                         return (1);
 6971                 }
 6972 
 6973                 if (zfs_mount(zhp, options, flags) != 0)
 6974                         return (1);
 6975                 break;
 6976         }
 6977 
 6978         return (0);
 6979 }
 6980 
 6981 /*
 6982  * Reports progress in the form "(current/total)".  Not thread-safe.
 6983  */
 6984 static void
 6985 report_mount_progress(int current, int total)
 6986 {
 6987         static time_t last_progress_time = 0;
 6988         time_t now = time(NULL);
 6989         char info[32];
 6990 
 6991         /* display header if we're here for the first time */
 6992         if (current == 1) {
 6993                 set_progress_header(gettext("Mounting ZFS filesystems"));
 6994         } else if (current != total && last_progress_time + MOUNT_TIME >= now) {
 6995                 /* too soon to report again */
 6996                 return;
 6997         }
 6998 
 6999         last_progress_time = now;
 7000 
 7001         (void) sprintf(info, "(%d/%d)", current, total);
 7002 
 7003         if (current == total)
 7004                 finish_progress(info);
 7005         else
 7006                 update_progress(info);
 7007 }
 7008 
 7009 /*
 7010  * zfs_foreach_mountpoint() callback that mounts or shares one filesystem and
 7011  * updates the progress meter.
 7012  */
 7013 static int
 7014 share_mount_one_cb(zfs_handle_t *zhp, void *arg)
 7015 {
 7016         share_mount_state_t *sms = arg;
 7017         int ret;
 7018 
 7019         ret = share_mount_one(zhp, sms->sm_op, sms->sm_flags, sms->sm_proto,
 7020             B_FALSE, sms->sm_options);
 7021 
 7022         pthread_mutex_lock(&sms->sm_lock);
 7023         if (ret != 0)
 7024                 sms->sm_status = ret;
 7025         sms->sm_done++;
 7026         if (sms->sm_verbose)
 7027                 report_mount_progress(sms->sm_done, sms->sm_total);
 7028         pthread_mutex_unlock(&sms->sm_lock);
 7029         return (ret);
 7030 }
 7031 
 7032 static void
 7033 append_options(char *mntopts, char *newopts)
 7034 {
 7035         int len = strlen(mntopts);
 7036 
 7037         /* original length plus new string to append plus 1 for the comma */
 7038         if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) {
 7039                 (void) fprintf(stderr, gettext("the opts argument for "
 7040                     "'%s' option is too long (more than %d chars)\n"),
 7041                     "-o", MNT_LINE_MAX);
 7042                 usage(B_FALSE);
 7043         }
 7044 
 7045         if (*mntopts)
 7046                 mntopts[len++] = ',';
 7047 
 7048         (void) strcpy(&mntopts[len], newopts);
 7049 }
 7050 
 7051 static enum sa_protocol
 7052 sa_protocol_decode(const char *protocol)
 7053 {
 7054         for (enum sa_protocol i = 0; i < ARRAY_SIZE(sa_protocol_names); ++i)
 7055                 if (strcmp(protocol, sa_protocol_names[i]) == 0)
 7056                         return (i);
 7057 
 7058         (void) fputs(gettext("share type must be one of: "), stderr);
 7059         for (enum sa_protocol i = 0;
 7060             i < ARRAY_SIZE(sa_protocol_names); ++i)
 7061                 (void) fprintf(stderr, "%s%s",
 7062                     i != 0 ? ", " : "", sa_protocol_names[i]);
 7063         (void) fputc('\n', stderr);
 7064         usage(B_FALSE);
 7065 }
 7066 
 7067 static int
 7068 share_mount(int op, int argc, char **argv)
 7069 {
 7070         int do_all = 0;
 7071         boolean_t verbose = B_FALSE;
 7072         int c, ret = 0;
 7073         char *options = NULL;
 7074         int flags = 0;
 7075 
 7076         /* check options */
 7077         while ((c = getopt(argc, argv, op == OP_MOUNT ? ":alvo:Of" : "al"))
 7078             != -1) {
 7079                 switch (c) {
 7080                 case 'a':
 7081                         do_all = 1;
 7082                         break;
 7083                 case 'v':
 7084                         verbose = B_TRUE;
 7085                         break;
 7086                 case 'l':
 7087                         flags |= MS_CRYPT;
 7088                         break;
 7089                 case 'o':
 7090                         if (*optarg == '\0') {
 7091                                 (void) fprintf(stderr, gettext("empty mount "
 7092                                     "options (-o) specified\n"));
 7093                                 usage(B_FALSE);
 7094                         }
 7095 
 7096                         if (options == NULL)
 7097                                 options = safe_malloc(MNT_LINE_MAX + 1);
 7098 
 7099                         /* option validation is done later */
 7100                         append_options(options, optarg);
 7101                         break;
 7102                 case 'O':
 7103                         flags |= MS_OVERLAY;
 7104                         break;
 7105                 case 'f':
 7106                         flags |= MS_FORCE;
 7107                         break;
 7108                 case ':':
 7109                         (void) fprintf(stderr, gettext("missing argument for "
 7110                             "'%c' option\n"), optopt);
 7111                         usage(B_FALSE);
 7112                         break;
 7113                 case '?':
 7114                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 7115                             optopt);
 7116                         usage(B_FALSE);
 7117                 }
 7118         }
 7119 
 7120         argc -= optind;
 7121         argv += optind;
 7122 
 7123         /* check number of arguments */
 7124         if (do_all) {
 7125                 enum sa_protocol protocol = SA_NO_PROTOCOL;
 7126 
 7127                 if (op == OP_SHARE && argc > 0) {
 7128                         protocol = sa_protocol_decode(argv[0]);
 7129                         argc--;
 7130                         argv++;
 7131                 }
 7132 
 7133                 if (argc != 0) {
 7134                         (void) fprintf(stderr, gettext("too many arguments\n"));
 7135                         usage(B_FALSE);
 7136                 }
 7137 
 7138                 start_progress_timer();
 7139                 get_all_cb_t cb = { 0 };
 7140                 get_all_datasets(&cb, verbose);
 7141 
 7142                 if (cb.cb_used == 0) {
 7143                         free(options);
 7144                         return (0);
 7145                 }
 7146 
 7147                 share_mount_state_t share_mount_state = { 0 };
 7148                 share_mount_state.sm_op = op;
 7149                 share_mount_state.sm_verbose = verbose;
 7150                 share_mount_state.sm_flags = flags;
 7151                 share_mount_state.sm_options = options;
 7152                 share_mount_state.sm_proto = protocol;
 7153                 share_mount_state.sm_total = cb.cb_used;
 7154                 pthread_mutex_init(&share_mount_state.sm_lock, NULL);
 7155 
 7156                 /* For a 'zfs share -a' operation start with a clean slate. */
 7157                 zfs_truncate_shares(NULL);
 7158 
 7159                 /*
 7160                  * libshare isn't mt-safe, so only do the operation in parallel
 7161                  * if we're mounting. Additionally, the key-loading option must
 7162                  * be serialized so that we can prompt the user for their keys
 7163                  * in a consistent manner.
 7164                  */
 7165                 zfs_foreach_mountpoint(g_zfs, cb.cb_handles, cb.cb_used,
 7166                     share_mount_one_cb, &share_mount_state,
 7167                     op == OP_MOUNT && !(flags & MS_CRYPT));
 7168                 zfs_commit_shares(NULL);
 7169 
 7170                 ret = share_mount_state.sm_status;
 7171 
 7172                 for (int i = 0; i < cb.cb_used; i++)
 7173                         zfs_close(cb.cb_handles[i]);
 7174                 free(cb.cb_handles);
 7175         } else if (argc == 0) {
 7176                 FILE *mnttab;
 7177                 struct mnttab entry;
 7178 
 7179                 if ((op == OP_SHARE) || (options != NULL)) {
 7180                         (void) fprintf(stderr, gettext("missing filesystem "
 7181                             "argument (specify -a for all)\n"));
 7182                         usage(B_FALSE);
 7183                 }
 7184 
 7185                 /*
 7186                  * When mount is given no arguments, go through
 7187                  * /proc/self/mounts and display any active ZFS mounts.
 7188                  * We hide any snapshots, since they are controlled
 7189                  * automatically.
 7190                  */
 7191 
 7192                 if ((mnttab = fopen(MNTTAB, "re")) == NULL) {
 7193                         free(options);
 7194                         return (ENOENT);
 7195                 }
 7196 
 7197                 while (getmntent(mnttab, &entry) == 0) {
 7198                         if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 ||
 7199                             strchr(entry.mnt_special, '@') != NULL)
 7200                                 continue;
 7201 
 7202                         (void) printf("%-30s  %s\n", entry.mnt_special,
 7203                             entry.mnt_mountp);
 7204                 }
 7205 
 7206                 (void) fclose(mnttab);
 7207         } else {
 7208                 zfs_handle_t *zhp;
 7209 
 7210                 if (argc > 1) {
 7211                         (void) fprintf(stderr,
 7212                             gettext("too many arguments\n"));
 7213                         usage(B_FALSE);
 7214                 }
 7215 
 7216                 if ((zhp = zfs_open(g_zfs, argv[0],
 7217                     ZFS_TYPE_FILESYSTEM)) == NULL) {
 7218                         ret = 1;
 7219                 } else {
 7220                         ret = share_mount_one(zhp, op, flags, SA_NO_PROTOCOL,
 7221                             B_TRUE, options);
 7222                         zfs_commit_shares(NULL);
 7223                         zfs_close(zhp);
 7224                 }
 7225         }
 7226 
 7227         free(options);
 7228         return (ret);
 7229 }
 7230 
 7231 /*
 7232  * zfs mount -a
 7233  * zfs mount filesystem
 7234  *
 7235  * Mount all filesystems, or mount the given filesystem.
 7236  */
 7237 static int
 7238 zfs_do_mount(int argc, char **argv)
 7239 {
 7240         return (share_mount(OP_MOUNT, argc, argv));
 7241 }
 7242 
 7243 /*
 7244  * zfs share -a [nfs | smb]
 7245  * zfs share filesystem
 7246  *
 7247  * Share all filesystems, or share the given filesystem.
 7248  */
 7249 static int
 7250 zfs_do_share(int argc, char **argv)
 7251 {
 7252         return (share_mount(OP_SHARE, argc, argv));
 7253 }
 7254 
 7255 typedef struct unshare_unmount_node {
 7256         zfs_handle_t    *un_zhp;
 7257         char            *un_mountp;
 7258         uu_avl_node_t   un_avlnode;
 7259 } unshare_unmount_node_t;
 7260 
 7261 static int
 7262 unshare_unmount_compare(const void *larg, const void *rarg, void *unused)
 7263 {
 7264         (void) unused;
 7265         const unshare_unmount_node_t *l = larg;
 7266         const unshare_unmount_node_t *r = rarg;
 7267 
 7268         return (strcmp(l->un_mountp, r->un_mountp));
 7269 }
 7270 
 7271 /*
 7272  * Convenience routine used by zfs_do_umount() and manual_unmount().  Given an
 7273  * absolute path, find the entry /proc/self/mounts, verify that it's a
 7274  * ZFS filesystem, and unmount it appropriately.
 7275  */
 7276 static int
 7277 unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
 7278 {
 7279         zfs_handle_t *zhp;
 7280         int ret = 0;
 7281         struct stat64 statbuf;
 7282         struct extmnttab entry;
 7283         const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount";
 7284         ino_t path_inode;
 7285 
 7286         /*
 7287          * Search for the given (major,minor) pair in the mount table.
 7288          */
 7289 
 7290         if (getextmntent(path, &entry, &statbuf) != 0) {
 7291                 if (op == OP_SHARE) {
 7292                         (void) fprintf(stderr, gettext("cannot %s '%s': not "
 7293                             "currently mounted\n"), cmdname, path);
 7294                         return (1);
 7295                 }
 7296                 (void) fprintf(stderr, gettext("warning: %s not in"
 7297                     "/proc/self/mounts\n"), path);
 7298                 if ((ret = umount2(path, flags)) != 0)
 7299                         (void) fprintf(stderr, gettext("%s: %s\n"), path,
 7300                             strerror(errno));
 7301                 return (ret != 0);
 7302         }
 7303         path_inode = statbuf.st_ino;
 7304 
 7305         if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) {
 7306                 (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS "
 7307                     "filesystem\n"), cmdname, path);
 7308                 return (1);
 7309         }
 7310 
 7311         if ((zhp = zfs_open(g_zfs, entry.mnt_special,
 7312             ZFS_TYPE_FILESYSTEM)) == NULL)
 7313                 return (1);
 7314 
 7315         ret = 1;
 7316         if (stat64(entry.mnt_mountp, &statbuf) != 0) {
 7317                 (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
 7318                     cmdname, path, strerror(errno));
 7319                 goto out;
 7320         } else if (statbuf.st_ino != path_inode) {
 7321                 (void) fprintf(stderr, gettext("cannot "
 7322                     "%s '%s': not a mountpoint\n"), cmdname, path);
 7323                 goto out;
 7324         }
 7325 
 7326         if (op == OP_SHARE) {
 7327                 char nfs_mnt_prop[ZFS_MAXPROPLEN];
 7328                 char smbshare_prop[ZFS_MAXPROPLEN];
 7329 
 7330                 verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, nfs_mnt_prop,
 7331                     sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0);
 7332                 verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshare_prop,
 7333                     sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0);
 7334 
 7335                 if (strcmp(nfs_mnt_prop, "off") == 0 &&
 7336                     strcmp(smbshare_prop, "off") == 0) {
 7337                         (void) fprintf(stderr, gettext("cannot unshare "
 7338                             "'%s': legacy share\n"), path);
 7339                         (void) fprintf(stderr, gettext("use exportfs(8) "
 7340                             "or smbcontrol(1) to unshare this filesystem\n"));
 7341                 } else if (!zfs_is_shared(zhp, NULL, NULL)) {
 7342                         (void) fprintf(stderr, gettext("cannot unshare '%s': "
 7343                             "not currently shared\n"), path);
 7344                 } else {
 7345                         ret = zfs_unshare(zhp, path, NULL);
 7346                         zfs_commit_shares(NULL);
 7347                 }
 7348         } else {
 7349                 char mtpt_prop[ZFS_MAXPROPLEN];
 7350 
 7351                 verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop,
 7352                     sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0);
 7353 
 7354                 if (is_manual) {
 7355                         ret = zfs_unmount(zhp, NULL, flags);
 7356                 } else if (strcmp(mtpt_prop, "legacy") == 0) {
 7357                         (void) fprintf(stderr, gettext("cannot unmount "
 7358                             "'%s': legacy mountpoint\n"),
 7359                             zfs_get_name(zhp));
 7360                         (void) fprintf(stderr, gettext("use umount(8) "
 7361                             "to unmount this filesystem\n"));
 7362                 } else {
 7363                         ret = zfs_unmountall(zhp, flags);
 7364                 }
 7365         }
 7366 
 7367 out:
 7368         zfs_close(zhp);
 7369 
 7370         return (ret != 0);
 7371 }
 7372 
 7373 /*
 7374  * Generic callback for unsharing or unmounting a filesystem.
 7375  */
 7376 static int
 7377 unshare_unmount(int op, int argc, char **argv)
 7378 {
 7379         int do_all = 0;
 7380         int flags = 0;
 7381         int ret = 0;
 7382         int c;
 7383         zfs_handle_t *zhp;
 7384         char nfs_mnt_prop[ZFS_MAXPROPLEN];
 7385         char sharesmb[ZFS_MAXPROPLEN];
 7386 
 7387         /* check options */
 7388         while ((c = getopt(argc, argv, op == OP_SHARE ? ":a" : "afu")) != -1) {
 7389                 switch (c) {
 7390                 case 'a':
 7391                         do_all = 1;
 7392                         break;
 7393                 case 'f':
 7394                         flags |= MS_FORCE;
 7395                         break;
 7396                 case 'u':
 7397                         flags |= MS_CRYPT;
 7398                         break;
 7399                 case ':':
 7400                         (void) fprintf(stderr, gettext("missing argument for "
 7401                             "'%c' option\n"), optopt);
 7402                         usage(B_FALSE);
 7403                         break;
 7404                 case '?':
 7405                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 7406                             optopt);
 7407                         usage(B_FALSE);
 7408                 }
 7409         }
 7410 
 7411         argc -= optind;
 7412         argv += optind;
 7413 
 7414         if (do_all) {
 7415                 /*
 7416                  * We could make use of zfs_for_each() to walk all datasets in
 7417                  * the system, but this would be very inefficient, especially
 7418                  * since we would have to linearly search /proc/self/mounts for
 7419                  * each one. Instead, do one pass through /proc/self/mounts
 7420                  * looking for zfs entries and call zfs_unmount() for each one.
 7421                  *
 7422                  * Things get a little tricky if the administrator has created
 7423                  * mountpoints beneath other ZFS filesystems.  In this case, we
 7424                  * have to unmount the deepest filesystems first.  To accomplish
 7425                  * this, we place all the mountpoints in an AVL tree sorted by
 7426                  * the special type (dataset name), and walk the result in
 7427                  * reverse to make sure to get any snapshots first.
 7428                  */
 7429                 FILE *mnttab;
 7430                 struct mnttab entry;
 7431                 uu_avl_pool_t *pool;
 7432                 uu_avl_t *tree = NULL;
 7433                 unshare_unmount_node_t *node;
 7434                 uu_avl_index_t idx;
 7435                 uu_avl_walk_t *walk;
 7436                 enum sa_protocol *protocol = NULL,
 7437                     single_protocol[] = {SA_NO_PROTOCOL, SA_NO_PROTOCOL};
 7438 
 7439                 if (op == OP_SHARE && argc > 0) {
 7440                         *single_protocol = sa_protocol_decode(argv[0]);
 7441                         protocol = single_protocol;
 7442                         argc--;
 7443                         argv++;
 7444                 }
 7445 
 7446                 if (argc != 0) {
 7447                         (void) fprintf(stderr, gettext("too many arguments\n"));
 7448                         usage(B_FALSE);
 7449                 }
 7450 
 7451                 if (((pool = uu_avl_pool_create("unmount_pool",
 7452                     sizeof (unshare_unmount_node_t),
 7453                     offsetof(unshare_unmount_node_t, un_avlnode),
 7454                     unshare_unmount_compare, UU_DEFAULT)) == NULL) ||
 7455                     ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL))
 7456                         nomem();
 7457 
 7458                 if ((mnttab = fopen(MNTTAB, "re")) == NULL) {
 7459                         uu_avl_destroy(tree);
 7460                         uu_avl_pool_destroy(pool);
 7461                         return (ENOENT);
 7462                 }
 7463 
 7464                 while (getmntent(mnttab, &entry) == 0) {
 7465 
 7466                         /* ignore non-ZFS entries */
 7467                         if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
 7468                                 continue;
 7469 
 7470                         /* ignore snapshots */
 7471                         if (strchr(entry.mnt_special, '@') != NULL)
 7472                                 continue;
 7473 
 7474                         if ((zhp = zfs_open(g_zfs, entry.mnt_special,
 7475                             ZFS_TYPE_FILESYSTEM)) == NULL) {
 7476                                 ret = 1;
 7477                                 continue;
 7478                         }
 7479 
 7480                         /*
 7481                          * Ignore datasets that are excluded/restricted by
 7482                          * parent pool name.
 7483                          */
 7484                         if (zpool_skip_pool(zfs_get_pool_name(zhp))) {
 7485                                 zfs_close(zhp);
 7486                                 continue;
 7487                         }
 7488 
 7489                         switch (op) {
 7490                         case OP_SHARE:
 7491                                 verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
 7492                                     nfs_mnt_prop,
 7493                                     sizeof (nfs_mnt_prop),
 7494                                     NULL, NULL, 0, B_FALSE) == 0);
 7495                                 if (strcmp(nfs_mnt_prop, "off") != 0)
 7496                                         break;
 7497                                 verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
 7498                                     nfs_mnt_prop,
 7499                                     sizeof (nfs_mnt_prop),
 7500                                     NULL, NULL, 0, B_FALSE) == 0);
 7501                                 if (strcmp(nfs_mnt_prop, "off") == 0)
 7502                                         continue;
 7503                                 break;
 7504                         case OP_MOUNT:
 7505                                 /* Ignore legacy mounts */
 7506                                 verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
 7507                                     nfs_mnt_prop,
 7508                                     sizeof (nfs_mnt_prop),
 7509                                     NULL, NULL, 0, B_FALSE) == 0);
 7510                                 if (strcmp(nfs_mnt_prop, "legacy") == 0)
 7511                                         continue;
 7512                                 /* Ignore canmount=noauto mounts */
 7513                                 if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) ==
 7514                                     ZFS_CANMOUNT_NOAUTO)
 7515                                         continue;
 7516                                 break;
 7517                         default:
 7518                                 break;
 7519                         }
 7520 
 7521                         node = safe_malloc(sizeof (unshare_unmount_node_t));
 7522                         node->un_zhp = zhp;
 7523                         node->un_mountp = safe_strdup(entry.mnt_mountp);
 7524 
 7525                         uu_avl_node_init(node, &node->un_avlnode, pool);
 7526 
 7527                         if (uu_avl_find(tree, node, NULL, &idx) == NULL) {
 7528                                 uu_avl_insert(tree, node, idx);
 7529                         } else {
 7530                                 zfs_close(node->un_zhp);
 7531                                 free(node->un_mountp);
 7532                                 free(node);
 7533                         }
 7534                 }
 7535                 (void) fclose(mnttab);
 7536 
 7537                 /*
 7538                  * Walk the AVL tree in reverse, unmounting each filesystem and
 7539                  * removing it from the AVL tree in the process.
 7540                  */
 7541                 if ((walk = uu_avl_walk_start(tree,
 7542                     UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL)
 7543                         nomem();
 7544 
 7545                 while ((node = uu_avl_walk_next(walk)) != NULL) {
 7546                         const char *mntarg = NULL;
 7547 
 7548                         uu_avl_remove(tree, node);
 7549                         switch (op) {
 7550                         case OP_SHARE:
 7551                                 if (zfs_unshare(node->un_zhp,
 7552                                     node->un_mountp, protocol) != 0)
 7553                                         ret = 1;
 7554                                 break;
 7555 
 7556                         case OP_MOUNT:
 7557                                 if (zfs_unmount(node->un_zhp,
 7558                                     mntarg, flags) != 0)
 7559                                         ret = 1;
 7560                                 break;
 7561                         }
 7562 
 7563                         zfs_close(node->un_zhp);
 7564                         free(node->un_mountp);
 7565                         free(node);
 7566                 }
 7567 
 7568                 if (op == OP_SHARE)
 7569                         zfs_commit_shares(protocol);
 7570 
 7571                 uu_avl_walk_end(walk);
 7572                 uu_avl_destroy(tree);
 7573                 uu_avl_pool_destroy(pool);
 7574 
 7575         } else {
 7576                 if (argc != 1) {
 7577                         if (argc == 0)
 7578                                 (void) fprintf(stderr,
 7579                                     gettext("missing filesystem argument\n"));
 7580                         else
 7581                                 (void) fprintf(stderr,
 7582                                     gettext("too many arguments\n"));
 7583                         usage(B_FALSE);
 7584                 }
 7585 
 7586                 /*
 7587                  * We have an argument, but it may be a full path or a ZFS
 7588                  * filesystem.  Pass full paths off to unmount_path() (shared by
 7589                  * manual_unmount), otherwise open the filesystem and pass to
 7590                  * zfs_unmount().
 7591                  */
 7592                 if (argv[0][0] == '/')
 7593                         return (unshare_unmount_path(op, argv[0],
 7594                             flags, B_FALSE));
 7595 
 7596                 if ((zhp = zfs_open(g_zfs, argv[0],
 7597                     ZFS_TYPE_FILESYSTEM)) == NULL)
 7598                         return (1);
 7599 
 7600                 verify(zfs_prop_get(zhp, op == OP_SHARE ?
 7601                     ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT,
 7602                     nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL,
 7603                     NULL, 0, B_FALSE) == 0);
 7604 
 7605                 switch (op) {
 7606                 case OP_SHARE:
 7607                         verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
 7608                             nfs_mnt_prop,
 7609                             sizeof (nfs_mnt_prop),
 7610                             NULL, NULL, 0, B_FALSE) == 0);
 7611                         verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
 7612                             sharesmb, sizeof (sharesmb), NULL, NULL,
 7613                             0, B_FALSE) == 0);
 7614 
 7615                         if (strcmp(nfs_mnt_prop, "off") == 0 &&
 7616                             strcmp(sharesmb, "off") == 0) {
 7617                                 (void) fprintf(stderr, gettext("cannot "
 7618                                     "unshare '%s': legacy share\n"),
 7619                                     zfs_get_name(zhp));
 7620                                 (void) fprintf(stderr, gettext("use "
 7621                                     "exports(5) or smb.conf(5) to unshare "
 7622                                     "this filesystem\n"));
 7623                                 ret = 1;
 7624                         } else if (!zfs_is_shared(zhp, NULL, NULL)) {
 7625                                 (void) fprintf(stderr, gettext("cannot "
 7626                                     "unshare '%s': not currently "
 7627                                     "shared\n"), zfs_get_name(zhp));
 7628                                 ret = 1;
 7629                         } else if (zfs_unshareall(zhp, NULL) != 0) {
 7630                                 ret = 1;
 7631                         }
 7632                         break;
 7633 
 7634                 case OP_MOUNT:
 7635                         if (strcmp(nfs_mnt_prop, "legacy") == 0) {
 7636                                 (void) fprintf(stderr, gettext("cannot "
 7637                                     "unmount '%s': legacy "
 7638                                     "mountpoint\n"), zfs_get_name(zhp));
 7639                                 (void) fprintf(stderr, gettext("use "
 7640                                     "umount(8) to unmount this "
 7641                                     "filesystem\n"));
 7642                                 ret = 1;
 7643                         } else if (!zfs_is_mounted(zhp, NULL)) {
 7644                                 (void) fprintf(stderr, gettext("cannot "
 7645                                     "unmount '%s': not currently "
 7646                                     "mounted\n"),
 7647                                     zfs_get_name(zhp));
 7648                                 ret = 1;
 7649                         } else if (zfs_unmountall(zhp, flags) != 0) {
 7650                                 ret = 1;
 7651                         }
 7652                         break;
 7653                 }
 7654 
 7655                 zfs_close(zhp);
 7656         }
 7657 
 7658         return (ret);
 7659 }
 7660 
 7661 /*
 7662  * zfs unmount [-fu] -a
 7663  * zfs unmount [-fu] filesystem
 7664  *
 7665  * Unmount all filesystems, or a specific ZFS filesystem.
 7666  */
 7667 static int
 7668 zfs_do_unmount(int argc, char **argv)
 7669 {
 7670         return (unshare_unmount(OP_MOUNT, argc, argv));
 7671 }
 7672 
 7673 /*
 7674  * zfs unshare -a
 7675  * zfs unshare filesystem
 7676  *
 7677  * Unshare all filesystems, or a specific ZFS filesystem.
 7678  */
 7679 static int
 7680 zfs_do_unshare(int argc, char **argv)
 7681 {
 7682         return (unshare_unmount(OP_SHARE, argc, argv));
 7683 }
 7684 
 7685 static int
 7686 find_command_idx(const char *command, int *idx)
 7687 {
 7688         int i;
 7689 
 7690         for (i = 0; i < NCOMMAND; i++) {
 7691                 if (command_table[i].name == NULL)
 7692                         continue;
 7693 
 7694                 if (strcmp(command, command_table[i].name) == 0) {
 7695                         *idx = i;
 7696                         return (0);
 7697                 }
 7698         }
 7699         return (1);
 7700 }
 7701 
 7702 static int
 7703 zfs_do_diff(int argc, char **argv)
 7704 {
 7705         zfs_handle_t *zhp;
 7706         int flags = 0;
 7707         char *tosnap = NULL;
 7708         char *fromsnap = NULL;
 7709         char *atp, *copy;
 7710         int err = 0;
 7711         int c;
 7712         struct sigaction sa;
 7713 
 7714         while ((c = getopt(argc, argv, "FHth")) != -1) {
 7715                 switch (c) {
 7716                 case 'F':
 7717                         flags |= ZFS_DIFF_CLASSIFY;
 7718                         break;
 7719                 case 'H':
 7720                         flags |= ZFS_DIFF_PARSEABLE;
 7721                         break;
 7722                 case 't':
 7723                         flags |= ZFS_DIFF_TIMESTAMP;
 7724                         break;
 7725                 case 'h':
 7726                         flags |= ZFS_DIFF_NO_MANGLE;
 7727                         break;
 7728                 default:
 7729                         (void) fprintf(stderr,
 7730                             gettext("invalid option '%c'\n"), optopt);
 7731                         usage(B_FALSE);
 7732                 }
 7733         }
 7734 
 7735         argc -= optind;
 7736         argv += optind;
 7737 
 7738         if (argc < 1) {
 7739                 (void) fprintf(stderr,
 7740                     gettext("must provide at least one snapshot name\n"));
 7741                 usage(B_FALSE);
 7742         }
 7743 
 7744         if (argc > 2) {
 7745                 (void) fprintf(stderr, gettext("too many arguments\n"));
 7746                 usage(B_FALSE);
 7747         }
 7748 
 7749         fromsnap = argv[0];
 7750         tosnap = (argc == 2) ? argv[1] : NULL;
 7751 
 7752         copy = NULL;
 7753         if (*fromsnap != '@')
 7754                 copy = strdup(fromsnap);
 7755         else if (tosnap)
 7756                 copy = strdup(tosnap);
 7757         if (copy == NULL)
 7758                 usage(B_FALSE);
 7759 
 7760         if ((atp = strchr(copy, '@')) != NULL)
 7761                 *atp = '\0';
 7762 
 7763         if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL) {
 7764                 free(copy);
 7765                 return (1);
 7766         }
 7767         free(copy);
 7768 
 7769         /*
 7770          * Ignore SIGPIPE so that the library can give us
 7771          * information on any failure
 7772          */
 7773         if (sigemptyset(&sa.sa_mask) == -1) {
 7774                 err = errno;
 7775                 goto out;
 7776         }
 7777         sa.sa_flags = 0;
 7778         sa.sa_handler = SIG_IGN;
 7779         if (sigaction(SIGPIPE, &sa, NULL) == -1) {
 7780                 err = errno;
 7781                 goto out;
 7782         }
 7783 
 7784         err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags);
 7785 out:
 7786         zfs_close(zhp);
 7787 
 7788         return (err != 0);
 7789 }
 7790 
 7791 /*
 7792  * zfs bookmark <fs@source>|<fs#source> <fs#bookmark>
 7793  *
 7794  * Creates a bookmark with the given name from the source snapshot
 7795  * or creates a copy of an existing source bookmark.
 7796  */
 7797 static int
 7798 zfs_do_bookmark(int argc, char **argv)
 7799 {
 7800         char *source, *bookname;
 7801         char expbuf[ZFS_MAX_DATASET_NAME_LEN];
 7802         int source_type;
 7803         nvlist_t *nvl;
 7804         int ret = 0;
 7805         int c;
 7806 
 7807         /* check options */
 7808         while ((c = getopt(argc, argv, "")) != -1) {
 7809                 switch (c) {
 7810                 case '?':
 7811                         (void) fprintf(stderr,
 7812                             gettext("invalid option '%c'\n"), optopt);
 7813                         goto usage;
 7814                 }
 7815         }
 7816 
 7817         argc -= optind;
 7818         argv += optind;
 7819 
 7820         /* check number of arguments */
 7821         if (argc < 1) {
 7822                 (void) fprintf(stderr, gettext("missing source argument\n"));
 7823                 goto usage;
 7824         }
 7825         if (argc < 2) {
 7826                 (void) fprintf(stderr, gettext("missing bookmark argument\n"));
 7827                 goto usage;
 7828         }
 7829 
 7830         source = argv[0];
 7831         bookname = argv[1];
 7832 
 7833         if (strchr(source, '@') == NULL && strchr(source, '#') == NULL) {
 7834                 (void) fprintf(stderr,
 7835                     gettext("invalid source name '%s': "
 7836                     "must contain a '@' or '#'\n"), source);
 7837                 goto usage;
 7838         }
 7839         if (strchr(bookname, '#') == NULL) {
 7840                 (void) fprintf(stderr,
 7841                     gettext("invalid bookmark name '%s': "
 7842                     "must contain a '#'\n"), bookname);
 7843                 goto usage;
 7844         }
 7845 
 7846         /*
 7847          * expand source or bookname to full path:
 7848          * one of them may be specified as short name
 7849          */
 7850         {
 7851                 char **expand;
 7852                 char *source_short, *bookname_short;
 7853                 source_short = strpbrk(source, "@#");
 7854                 bookname_short = strpbrk(bookname, "#");
 7855                 if (source_short == source &&
 7856                     bookname_short == bookname) {
 7857                         (void) fprintf(stderr, gettext(
 7858                             "either source or bookmark must be specified as "
 7859                             "full dataset paths"));
 7860                         goto usage;
 7861                 } else if (source_short != source &&
 7862                     bookname_short != bookname) {
 7863                         expand = NULL;
 7864                 } else if (source_short != source) {
 7865                         strlcpy(expbuf, source, sizeof (expbuf));
 7866                         expand = &bookname;
 7867                 } else if (bookname_short != bookname) {
 7868                         strlcpy(expbuf, bookname, sizeof (expbuf));
 7869                         expand = &source;
 7870                 } else {
 7871                         abort();
 7872                 }
 7873                 if (expand != NULL) {
 7874                         *strpbrk(expbuf, "@#") = '\0'; /* dataset name in buf */
 7875                         (void) strlcat(expbuf, *expand, sizeof (expbuf));
 7876                         *expand = expbuf;
 7877                 }
 7878         }
 7879 
 7880         /* determine source type */
 7881         switch (*strpbrk(source, "@#")) {
 7882                 case '@': source_type = ZFS_TYPE_SNAPSHOT; break;
 7883                 case '#': source_type = ZFS_TYPE_BOOKMARK; break;
 7884                 default: abort();
 7885         }
 7886 
 7887         /* test the source exists */
 7888         zfs_handle_t *zhp;
 7889         zhp = zfs_open(g_zfs, source, source_type);
 7890         if (zhp == NULL)
 7891                 goto usage;
 7892         zfs_close(zhp);
 7893 
 7894         nvl = fnvlist_alloc();
 7895         fnvlist_add_string(nvl, bookname, source);
 7896         ret = lzc_bookmark(nvl, NULL);
 7897         fnvlist_free(nvl);
 7898 
 7899         if (ret != 0) {
 7900                 const char *err_msg = NULL;
 7901                 char errbuf[1024];
 7902 
 7903                 (void) snprintf(errbuf, sizeof (errbuf),
 7904                     dgettext(TEXT_DOMAIN,
 7905                     "cannot create bookmark '%s'"), bookname);
 7906 
 7907                 switch (ret) {
 7908                 case EXDEV:
 7909                         err_msg = "bookmark is in a different pool";
 7910                         break;
 7911                 case ZFS_ERR_BOOKMARK_SOURCE_NOT_ANCESTOR:
 7912                         err_msg = "source is not an ancestor of the "
 7913                             "new bookmark's dataset";
 7914                         break;
 7915                 case EEXIST:
 7916                         err_msg = "bookmark exists";
 7917                         break;
 7918                 case EINVAL:
 7919                         err_msg = "invalid argument";
 7920                         break;
 7921                 case ENOTSUP:
 7922                         err_msg = "bookmark feature not enabled";
 7923                         break;
 7924                 case ENOSPC:
 7925                         err_msg = "out of space";
 7926                         break;
 7927                 case ENOENT:
 7928                         err_msg = "dataset does not exist";
 7929                         break;
 7930                 default:
 7931                         (void) zfs_standard_error(g_zfs, ret, errbuf);
 7932                         break;
 7933                 }
 7934                 if (err_msg != NULL) {
 7935                         (void) fprintf(stderr, "%s: %s\n", errbuf,
 7936                             dgettext(TEXT_DOMAIN, err_msg));
 7937                 }
 7938         }
 7939 
 7940         return (ret != 0);
 7941 
 7942 usage:
 7943         usage(B_FALSE);
 7944         return (-1);
 7945 }
 7946 
 7947 static int
 7948 zfs_do_channel_program(int argc, char **argv)
 7949 {
 7950         int ret, fd, c;
 7951         size_t progsize, progread;
 7952         nvlist_t *outnvl = NULL;
 7953         uint64_t instrlimit = ZCP_DEFAULT_INSTRLIMIT;
 7954         uint64_t memlimit = ZCP_DEFAULT_MEMLIMIT;
 7955         boolean_t sync_flag = B_TRUE, json_output = B_FALSE;
 7956         zpool_handle_t *zhp;
 7957 
 7958         /* check options */
 7959         while ((c = getopt(argc, argv, "nt:m:j")) != -1) {
 7960                 switch (c) {
 7961                 case 't':
 7962                 case 'm': {
 7963                         uint64_t arg;
 7964                         char *endp;
 7965 
 7966                         errno = 0;
 7967                         arg = strtoull(optarg, &endp, 0);
 7968                         if (errno != 0 || *endp != '\0') {
 7969                                 (void) fprintf(stderr, gettext(
 7970                                     "invalid argument "
 7971                                     "'%s': expected integer\n"), optarg);
 7972                                 goto usage;
 7973                         }
 7974 
 7975                         if (c == 't') {
 7976                                 instrlimit = arg;
 7977                         } else {
 7978                                 ASSERT3U(c, ==, 'm');
 7979                                 memlimit = arg;
 7980                         }
 7981                         break;
 7982                 }
 7983                 case 'n': {
 7984                         sync_flag = B_FALSE;
 7985                         break;
 7986                 }
 7987                 case 'j': {
 7988                         json_output = B_TRUE;
 7989                         break;
 7990                 }
 7991                 case '?':
 7992                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 7993                             optopt);
 7994                         goto usage;
 7995                 }
 7996         }
 7997 
 7998         argc -= optind;
 7999         argv += optind;
 8000 
 8001         if (argc < 2) {
 8002                 (void) fprintf(stderr,
 8003                     gettext("invalid number of arguments\n"));
 8004                 goto usage;
 8005         }
 8006 
 8007         const char *poolname = argv[0];
 8008         const char *filename = argv[1];
 8009         if (strcmp(filename, "-") == 0) {
 8010                 fd = 0;
 8011                 filename = "standard input";
 8012         } else if ((fd = open(filename, O_RDONLY)) < 0) {
 8013                 (void) fprintf(stderr, gettext("cannot open '%s': %s\n"),
 8014                     filename, strerror(errno));
 8015                 return (1);
 8016         }
 8017 
 8018         if ((zhp = zpool_open(g_zfs, poolname)) == NULL) {
 8019                 (void) fprintf(stderr, gettext("cannot open pool '%s'\n"),
 8020                     poolname);
 8021                 if (fd != 0)
 8022                         (void) close(fd);
 8023                 return (1);
 8024         }
 8025         zpool_close(zhp);
 8026 
 8027         /*
 8028          * Read in the channel program, expanding the program buffer as
 8029          * necessary.
 8030          */
 8031         progread = 0;
 8032         progsize = 1024;
 8033         char *progbuf = safe_malloc(progsize);
 8034         do {
 8035                 ret = read(fd, progbuf + progread, progsize - progread);
 8036                 progread += ret;
 8037                 if (progread == progsize && ret > 0) {
 8038                         progsize *= 2;
 8039                         progbuf = safe_realloc(progbuf, progsize);
 8040                 }
 8041         } while (ret > 0);
 8042 
 8043         if (fd != 0)
 8044                 (void) close(fd);
 8045         if (ret < 0) {
 8046                 free(progbuf);
 8047                 (void) fprintf(stderr,
 8048                     gettext("cannot read '%s': %s\n"),
 8049                     filename, strerror(errno));
 8050                 return (1);
 8051         }
 8052         progbuf[progread] = '\0';
 8053 
 8054         /*
 8055          * Any remaining arguments are passed as arguments to the lua script as
 8056          * a string array:
 8057          * {
 8058          *      "argv" -> [ "arg 1", ... "arg n" ],
 8059          * }
 8060          */
 8061         nvlist_t *argnvl = fnvlist_alloc();
 8062         fnvlist_add_string_array(argnvl, ZCP_ARG_CLIARGV,
 8063             (const char **)argv + 2, argc - 2);
 8064 
 8065         if (sync_flag) {
 8066                 ret = lzc_channel_program(poolname, progbuf,
 8067                     instrlimit, memlimit, argnvl, &outnvl);
 8068         } else {
 8069                 ret = lzc_channel_program_nosync(poolname, progbuf,
 8070                     instrlimit, memlimit, argnvl, &outnvl);
 8071         }
 8072 
 8073         if (ret != 0) {
 8074                 /*
 8075                  * On error, report the error message handed back by lua if one
 8076                  * exists.  Otherwise, generate an appropriate error message,
 8077                  * falling back on strerror() for an unexpected return code.
 8078                  */
 8079                 const char *errstring = NULL;
 8080                 const char *msg = gettext("Channel program execution failed");
 8081                 uint64_t instructions = 0;
 8082                 if (outnvl != NULL && nvlist_exists(outnvl, ZCP_RET_ERROR)) {
 8083                         char *es = NULL;
 8084                         (void) nvlist_lookup_string(outnvl,
 8085                             ZCP_RET_ERROR, &es);
 8086                         if (es == NULL)
 8087                                 errstring = strerror(ret);
 8088                         else
 8089                                 errstring = es;
 8090                         if (ret == ETIME) {
 8091                                 (void) nvlist_lookup_uint64(outnvl,
 8092                                     ZCP_ARG_INSTRLIMIT, &instructions);
 8093                         }
 8094                 } else {
 8095                         switch (ret) {
 8096                         case EINVAL:
 8097                                 errstring =
 8098                                     "Invalid instruction or memory limit.";
 8099                                 break;
 8100                         case ENOMEM:
 8101                                 errstring = "Return value too large.";
 8102                                 break;
 8103                         case ENOSPC:
 8104                                 errstring = "Memory limit exhausted.";
 8105                                 break;
 8106                         case ETIME:
 8107                                 errstring = "Timed out.";
 8108                                 break;
 8109                         case EPERM:
 8110                                 errstring = "Permission denied. Channel "
 8111                                     "programs must be run as root.";
 8112                                 break;
 8113                         default:
 8114                                 (void) zfs_standard_error(g_zfs, ret, msg);
 8115                         }
 8116                 }
 8117                 if (errstring != NULL)
 8118                         (void) fprintf(stderr, "%s:\n%s\n", msg, errstring);
 8119 
 8120                 if (ret == ETIME && instructions != 0)
 8121                         (void) fprintf(stderr,
 8122                             gettext("%llu Lua instructions\n"),
 8123                             (u_longlong_t)instructions);
 8124         } else {
 8125                 if (json_output) {
 8126                         (void) nvlist_print_json(stdout, outnvl);
 8127                 } else if (nvlist_empty(outnvl)) {
 8128                         (void) fprintf(stdout, gettext("Channel program fully "
 8129                             "executed and did not produce output.\n"));
 8130                 } else {
 8131                         (void) fprintf(stdout, gettext("Channel program fully "
 8132                             "executed and produced output:\n"));
 8133                         dump_nvlist(outnvl, 4);
 8134                 }
 8135         }
 8136 
 8137         free(progbuf);
 8138         fnvlist_free(outnvl);
 8139         fnvlist_free(argnvl);
 8140         return (ret != 0);
 8141 
 8142 usage:
 8143         usage(B_FALSE);
 8144         return (-1);
 8145 }
 8146 
 8147 
 8148 typedef struct loadkey_cbdata {
 8149         boolean_t cb_loadkey;
 8150         boolean_t cb_recursive;
 8151         boolean_t cb_noop;
 8152         char *cb_keylocation;
 8153         uint64_t cb_numfailed;
 8154         uint64_t cb_numattempted;
 8155 } loadkey_cbdata_t;
 8156 
 8157 static int
 8158 load_key_callback(zfs_handle_t *zhp, void *data)
 8159 {
 8160         int ret;
 8161         boolean_t is_encroot;
 8162         loadkey_cbdata_t *cb = data;
 8163         uint64_t keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);
 8164 
 8165         /*
 8166          * If we are working recursively, we want to skip loading / unloading
 8167          * keys for non-encryption roots and datasets whose keys are already
 8168          * in the desired end-state.
 8169          */
 8170         if (cb->cb_recursive) {
 8171                 ret = zfs_crypto_get_encryption_root(zhp, &is_encroot, NULL);
 8172                 if (ret != 0)
 8173                         return (ret);
 8174                 if (!is_encroot)
 8175                         return (0);
 8176 
 8177                 if ((cb->cb_loadkey && keystatus == ZFS_KEYSTATUS_AVAILABLE) ||
 8178                     (!cb->cb_loadkey && keystatus == ZFS_KEYSTATUS_UNAVAILABLE))
 8179                         return (0);
 8180         }
 8181 
 8182         cb->cb_numattempted++;
 8183 
 8184         if (cb->cb_loadkey)
 8185                 ret = zfs_crypto_load_key(zhp, cb->cb_noop, cb->cb_keylocation);
 8186         else
 8187                 ret = zfs_crypto_unload_key(zhp);
 8188 
 8189         if (ret != 0) {
 8190                 cb->cb_numfailed++;
 8191                 return (ret);
 8192         }
 8193 
 8194         return (0);
 8195 }
 8196 
 8197 static int
 8198 load_unload_keys(int argc, char **argv, boolean_t loadkey)
 8199 {
 8200         int c, ret = 0, flags = 0;
 8201         boolean_t do_all = B_FALSE;
 8202         loadkey_cbdata_t cb = { 0 };
 8203 
 8204         cb.cb_loadkey = loadkey;
 8205 
 8206         while ((c = getopt(argc, argv, "anrL:")) != -1) {
 8207                 /* noop and alternate keylocations only apply to zfs load-key */
 8208                 if (loadkey) {
 8209                         switch (c) {
 8210                         case 'n':
 8211                                 cb.cb_noop = B_TRUE;
 8212                                 continue;
 8213                         case 'L':
 8214                                 cb.cb_keylocation = optarg;
 8215                                 continue;
 8216                         default:
 8217                                 break;
 8218                         }
 8219                 }
 8220 
 8221                 switch (c) {
 8222                 case 'a':
 8223                         do_all = B_TRUE;
 8224                         cb.cb_recursive = B_TRUE;
 8225                         break;
 8226                 case 'r':
 8227                         flags |= ZFS_ITER_RECURSE;
 8228                         cb.cb_recursive = B_TRUE;
 8229                         break;
 8230                 default:
 8231                         (void) fprintf(stderr,
 8232                             gettext("invalid option '%c'\n"), optopt);
 8233                         usage(B_FALSE);
 8234                 }
 8235         }
 8236 
 8237         argc -= optind;
 8238         argv += optind;
 8239 
 8240         if (!do_all && argc == 0) {
 8241                 (void) fprintf(stderr,
 8242                     gettext("Missing dataset argument or -a option\n"));
 8243                 usage(B_FALSE);
 8244         }
 8245 
 8246         if (do_all && argc != 0) {
 8247                 (void) fprintf(stderr,
 8248                     gettext("Cannot specify dataset with -a option\n"));
 8249                 usage(B_FALSE);
 8250         }
 8251 
 8252         if (cb.cb_recursive && cb.cb_keylocation != NULL &&
 8253             strcmp(cb.cb_keylocation, "prompt") != 0) {
 8254                 (void) fprintf(stderr, gettext("alternate keylocation may only "
 8255                     "be 'prompt' with -r or -a\n"));
 8256                 usage(B_FALSE);
 8257         }
 8258 
 8259         ret = zfs_for_each(argc, argv, flags,
 8260             ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL, 0,
 8261             load_key_callback, &cb);
 8262 
 8263         if (cb.cb_noop || (cb.cb_recursive && cb.cb_numattempted != 0)) {
 8264                 (void) printf(gettext("%llu / %llu key(s) successfully %s\n"),
 8265                     (u_longlong_t)(cb.cb_numattempted - cb.cb_numfailed),
 8266                     (u_longlong_t)cb.cb_numattempted,
 8267                     loadkey ? (cb.cb_noop ? "verified" : "loaded") :
 8268                     "unloaded");
 8269         }
 8270 
 8271         if (cb.cb_numfailed != 0)
 8272                 ret = -1;
 8273 
 8274         return (ret);
 8275 }
 8276 
 8277 static int
 8278 zfs_do_load_key(int argc, char **argv)
 8279 {
 8280         return (load_unload_keys(argc, argv, B_TRUE));
 8281 }
 8282 
 8283 
 8284 static int
 8285 zfs_do_unload_key(int argc, char **argv)
 8286 {
 8287         return (load_unload_keys(argc, argv, B_FALSE));
 8288 }
 8289 
 8290 static int
 8291 zfs_do_change_key(int argc, char **argv)
 8292 {
 8293         int c, ret;
 8294         uint64_t keystatus;
 8295         boolean_t loadkey = B_FALSE, inheritkey = B_FALSE;
 8296         zfs_handle_t *zhp = NULL;
 8297         nvlist_t *props = fnvlist_alloc();
 8298 
 8299         while ((c = getopt(argc, argv, "lio:")) != -1) {
 8300                 switch (c) {
 8301                 case 'l':
 8302                         loadkey = B_TRUE;
 8303                         break;
 8304                 case 'i':
 8305                         inheritkey = B_TRUE;
 8306                         break;
 8307                 case 'o':
 8308                         if (!parseprop(props, optarg)) {
 8309                                 nvlist_free(props);
 8310                                 return (1);
 8311                         }
 8312                         break;
 8313                 default:
 8314                         (void) fprintf(stderr,
 8315                             gettext("invalid option '%c'\n"), optopt);
 8316                         usage(B_FALSE);
 8317                 }
 8318         }
 8319 
 8320         if (inheritkey && !nvlist_empty(props)) {
 8321                 (void) fprintf(stderr,
 8322                     gettext("Properties not allowed for inheriting\n"));
 8323                 usage(B_FALSE);
 8324         }
 8325 
 8326         argc -= optind;
 8327         argv += optind;
 8328 
 8329         if (argc < 1) {
 8330                 (void) fprintf(stderr, gettext("Missing dataset argument\n"));
 8331                 usage(B_FALSE);
 8332         }
 8333 
 8334         if (argc > 1) {
 8335                 (void) fprintf(stderr, gettext("Too many arguments\n"));
 8336                 usage(B_FALSE);
 8337         }
 8338 
 8339         zhp = zfs_open(g_zfs, argv[argc - 1],
 8340             ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
 8341         if (zhp == NULL)
 8342                 usage(B_FALSE);
 8343 
 8344         if (loadkey) {
 8345                 keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);
 8346                 if (keystatus != ZFS_KEYSTATUS_AVAILABLE) {
 8347                         ret = zfs_crypto_load_key(zhp, B_FALSE, NULL);
 8348                         if (ret != 0) {
 8349                                 nvlist_free(props);
 8350                                 zfs_close(zhp);
 8351                                 return (-1);
 8352                         }
 8353                 }
 8354 
 8355                 /* refresh the properties so the new keystatus is visible */
 8356                 zfs_refresh_properties(zhp);
 8357         }
 8358 
 8359         ret = zfs_crypto_rewrap(zhp, props, inheritkey);
 8360         if (ret != 0) {
 8361                 nvlist_free(props);
 8362                 zfs_close(zhp);
 8363                 return (-1);
 8364         }
 8365 
 8366         nvlist_free(props);
 8367         zfs_close(zhp);
 8368         return (0);
 8369 }
 8370 
 8371 /*
 8372  * 1) zfs project [-d|-r] <file|directory ...>
 8373  *    List project ID and inherit flag of file(s) or directories.
 8374  *    -d: List the directory itself, not its children.
 8375  *    -r: List subdirectories recursively.
 8376  *
 8377  * 2) zfs project -C [-k] [-r] <file|directory ...>
 8378  *    Clear project inherit flag and/or ID on the file(s) or directories.
 8379  *    -k: Keep the project ID unchanged. If not specified, the project ID
 8380  *        will be reset as zero.
 8381  *    -r: Clear on subdirectories recursively.
 8382  *
 8383  * 3) zfs project -c [-0] [-d|-r] [-p id] <file|directory ...>
 8384  *    Check project ID and inherit flag on the file(s) or directories,
 8385  *    report the outliers.
 8386  *    -0: Print file name followed by a NUL instead of newline.
 8387  *    -d: Check the directory itself, not its children.
 8388  *    -p: Specify the referenced ID for comparing with the target file(s)
 8389  *        or directories' project IDs. If not specified, the target (top)
 8390  *        directory's project ID will be used as the referenced one.
 8391  *    -r: Check subdirectories recursively.
 8392  *
 8393  * 4) zfs project [-p id] [-r] [-s] <file|directory ...>
 8394  *    Set project ID and/or inherit flag on the file(s) or directories.
 8395  *    -p: Set the project ID as the given id.
 8396  *    -r: Set on subdirectories recursively. If not specify "-p" option,
 8397  *        it will use top-level directory's project ID as the given id,
 8398  *        then set both project ID and inherit flag on all descendants
 8399  *        of the top-level directory.
 8400  *    -s: Set project inherit flag.
 8401  */
 8402 static int
 8403 zfs_do_project(int argc, char **argv)
 8404 {
 8405         zfs_project_control_t zpc = {
 8406                 .zpc_expected_projid = ZFS_INVALID_PROJID,
 8407                 .zpc_op = ZFS_PROJECT_OP_DEFAULT,
 8408                 .zpc_dironly = B_FALSE,
 8409                 .zpc_keep_projid = B_FALSE,
 8410                 .zpc_newline = B_TRUE,
 8411                 .zpc_recursive = B_FALSE,
 8412                 .zpc_set_flag = B_FALSE,
 8413         };
 8414         int ret = 0, c;
 8415 
 8416         if (argc < 2)
 8417                 usage(B_FALSE);
 8418 
 8419         while ((c = getopt(argc, argv, "0Ccdkp:rs")) != -1) {
 8420                 switch (c) {
 8421                 case '':
 8422                         zpc.zpc_newline = B_FALSE;
 8423                         break;
 8424                 case 'C':
 8425                         if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {
 8426                                 (void) fprintf(stderr, gettext("cannot "
 8427                                     "specify '-C' '-c' '-s' together\n"));
 8428                                 usage(B_FALSE);
 8429                         }
 8430 
 8431                         zpc.zpc_op = ZFS_PROJECT_OP_CLEAR;
 8432                         break;
 8433                 case 'c':
 8434                         if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {
 8435                                 (void) fprintf(stderr, gettext("cannot "
 8436                                     "specify '-C' '-c' '-s' together\n"));
 8437                                 usage(B_FALSE);
 8438                         }
 8439 
 8440                         zpc.zpc_op = ZFS_PROJECT_OP_CHECK;
 8441                         break;
 8442                 case 'd':
 8443                         zpc.zpc_dironly = B_TRUE;
 8444                         /* overwrite "-r" option */
 8445                         zpc.zpc_recursive = B_FALSE;
 8446                         break;
 8447                 case 'k':
 8448                         zpc.zpc_keep_projid = B_TRUE;
 8449                         break;
 8450                 case 'p': {
 8451                         char *endptr;
 8452 
 8453                         errno = 0;
 8454                         zpc.zpc_expected_projid = strtoull(optarg, &endptr, 0);
 8455                         if (errno != 0 || *endptr != '\0') {
 8456                                 (void) fprintf(stderr,
 8457                                     gettext("project ID must be less than "
 8458                                     "%u\n"), UINT32_MAX);
 8459                                 usage(B_FALSE);
 8460                         }
 8461                         if (zpc.zpc_expected_projid >= UINT32_MAX) {
 8462                                 (void) fprintf(stderr,
 8463                                     gettext("invalid project ID\n"));
 8464                                 usage(B_FALSE);
 8465                         }
 8466                         break;
 8467                 }
 8468                 case 'r':
 8469                         zpc.zpc_recursive = B_TRUE;
 8470                         /* overwrite "-d" option */
 8471                         zpc.zpc_dironly = B_FALSE;
 8472                         break;
 8473                 case 's':
 8474                         if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {
 8475                                 (void) fprintf(stderr, gettext("cannot "
 8476                                     "specify '-C' '-c' '-s' together\n"));
 8477                                 usage(B_FALSE);
 8478                         }
 8479 
 8480                         zpc.zpc_set_flag = B_TRUE;
 8481                         zpc.zpc_op = ZFS_PROJECT_OP_SET;
 8482                         break;
 8483                 default:
 8484                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 8485                             optopt);
 8486                         usage(B_FALSE);
 8487                 }
 8488         }
 8489 
 8490         if (zpc.zpc_op == ZFS_PROJECT_OP_DEFAULT) {
 8491                 if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID)
 8492                         zpc.zpc_op = ZFS_PROJECT_OP_SET;
 8493                 else
 8494                         zpc.zpc_op = ZFS_PROJECT_OP_LIST;
 8495         }
 8496 
 8497         switch (zpc.zpc_op) {
 8498         case ZFS_PROJECT_OP_LIST:
 8499                 if (zpc.zpc_keep_projid) {
 8500                         (void) fprintf(stderr,
 8501                             gettext("'-k' is only valid together with '-C'\n"));
 8502                         usage(B_FALSE);
 8503                 }
 8504                 if (!zpc.zpc_newline) {
 8505                         (void) fprintf(stderr,
 8506                             gettext("'-0' is only valid together with '-c'\n"));
 8507                         usage(B_FALSE);
 8508                 }
 8509                 break;
 8510         case ZFS_PROJECT_OP_CHECK:
 8511                 if (zpc.zpc_keep_projid) {
 8512                         (void) fprintf(stderr,
 8513                             gettext("'-k' is only valid together with '-C'\n"));
 8514                         usage(B_FALSE);
 8515                 }
 8516                 break;
 8517         case ZFS_PROJECT_OP_CLEAR:
 8518                 if (zpc.zpc_dironly) {
 8519                         (void) fprintf(stderr,
 8520                             gettext("'-d' is useless together with '-C'\n"));
 8521                         usage(B_FALSE);
 8522                 }
 8523                 if (!zpc.zpc_newline) {
 8524                         (void) fprintf(stderr,
 8525                             gettext("'-0' is only valid together with '-c'\n"));
 8526                         usage(B_FALSE);
 8527                 }
 8528                 if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID) {
 8529                         (void) fprintf(stderr,
 8530                             gettext("'-p' is useless together with '-C'\n"));
 8531                         usage(B_FALSE);
 8532                 }
 8533                 break;
 8534         case ZFS_PROJECT_OP_SET:
 8535                 if (zpc.zpc_dironly) {
 8536                         (void) fprintf(stderr,
 8537                             gettext("'-d' is useless for set project ID and/or "
 8538                             "inherit flag\n"));
 8539                         usage(B_FALSE);
 8540                 }
 8541                 if (zpc.zpc_keep_projid) {
 8542                         (void) fprintf(stderr,
 8543                             gettext("'-k' is only valid together with '-C'\n"));
 8544                         usage(B_FALSE);
 8545                 }
 8546                 if (!zpc.zpc_newline) {
 8547                         (void) fprintf(stderr,
 8548                             gettext("'-0' is only valid together with '-c'\n"));
 8549                         usage(B_FALSE);
 8550                 }
 8551                 break;
 8552         default:
 8553                 ASSERT(0);
 8554                 break;
 8555         }
 8556 
 8557         argv += optind;
 8558         argc -= optind;
 8559         if (argc == 0) {
 8560                 (void) fprintf(stderr,
 8561                     gettext("missing file or directory target(s)\n"));
 8562                 usage(B_FALSE);
 8563         }
 8564 
 8565         for (int i = 0; i < argc; i++) {
 8566                 int err;
 8567 
 8568                 err = zfs_project_handle(argv[i], &zpc);
 8569                 if (err && !ret)
 8570                         ret = err;
 8571         }
 8572 
 8573         return (ret);
 8574 }
 8575 
 8576 static int
 8577 zfs_do_wait(int argc, char **argv)
 8578 {
 8579         boolean_t enabled[ZFS_WAIT_NUM_ACTIVITIES];
 8580         int error = 0, i;
 8581         int c;
 8582 
 8583         /* By default, wait for all types of activity. */
 8584         for (i = 0; i < ZFS_WAIT_NUM_ACTIVITIES; i++)
 8585                 enabled[i] = B_TRUE;
 8586 
 8587         while ((c = getopt(argc, argv, "t:")) != -1) {
 8588                 switch (c) {
 8589                 case 't':
 8590                         /* Reset activities array */
 8591                         memset(&enabled, 0, sizeof (enabled));
 8592 
 8593                         for (char *tok; (tok = strsep(&optarg, ",")); ) {
 8594                                 static const char *const col_subopts[
 8595                                     ZFS_WAIT_NUM_ACTIVITIES] = { "deleteq" };
 8596 
 8597                                 for (i = 0; i < ARRAY_SIZE(col_subopts); ++i)
 8598                                         if (strcmp(tok, col_subopts[i]) == 0) {
 8599                                                 enabled[i] = B_TRUE;
 8600                                                 goto found;
 8601                                         }
 8602 
 8603                                 (void) fprintf(stderr,
 8604                                     gettext("invalid activity '%s'\n"), tok);
 8605                                 usage(B_FALSE);
 8606 found:;
 8607                         }
 8608                         break;
 8609                 case '?':
 8610                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 8611                             optopt);
 8612                         usage(B_FALSE);
 8613                 }
 8614         }
 8615 
 8616         argv += optind;
 8617         argc -= optind;
 8618         if (argc < 1) {
 8619                 (void) fprintf(stderr, gettext("missing 'filesystem' "
 8620                     "argument\n"));
 8621                 usage(B_FALSE);
 8622         }
 8623         if (argc > 1) {
 8624                 (void) fprintf(stderr, gettext("too many arguments\n"));
 8625                 usage(B_FALSE);
 8626         }
 8627 
 8628         zfs_handle_t *zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM);
 8629         if (zhp == NULL)
 8630                 return (1);
 8631 
 8632         for (;;) {
 8633                 boolean_t missing = B_FALSE;
 8634                 boolean_t any_waited = B_FALSE;
 8635 
 8636                 for (int i = 0; i < ZFS_WAIT_NUM_ACTIVITIES; i++) {
 8637                         boolean_t waited;
 8638 
 8639                         if (!enabled[i])
 8640                                 continue;
 8641 
 8642                         error = zfs_wait_status(zhp, i, &missing, &waited);
 8643                         if (error != 0 || missing)
 8644                                 break;
 8645 
 8646                         any_waited = (any_waited || waited);
 8647                 }
 8648 
 8649                 if (error != 0 || missing || !any_waited)
 8650                         break;
 8651         }
 8652 
 8653         zfs_close(zhp);
 8654 
 8655         return (error);
 8656 }
 8657 
 8658 /*
 8659  * Display version message
 8660  */
 8661 static int
 8662 zfs_do_version(int argc, char **argv)
 8663 {
 8664         (void) argc, (void) argv;
 8665         return (zfs_version_print() != 0);
 8666 }
 8667 
 8668 int
 8669 main(int argc, char **argv)
 8670 {
 8671         int ret = 0;
 8672         int i = 0;
 8673         const char *cmdname;
 8674         char **newargv;
 8675         extern char **environ;
 8676 
 8677         (void) setlocale(LC_ALL, "");
 8678         (void) setlocale(LC_NUMERIC, "C");
 8679         (void) textdomain(TEXT_DOMAIN);
 8680 
 8681         opterr = 0;
 8682 
 8683         /*
 8684          * Make sure the user has specified some command.
 8685          */
 8686         if (argc < 2) {
 8687                 (void) fprintf(stderr, gettext("missing command\n"));
 8688                 usage(B_FALSE);
 8689         }
 8690 
 8691         cmdname = argv[1];
 8692 
 8693         /*
 8694          * The 'umount' command is an alias for 'unmount'
 8695          */
 8696         if (strcmp(cmdname, "umount") == 0)
 8697                 cmdname = "unmount";
 8698 
 8699         /*
 8700          * The 'recv' command is an alias for 'receive'
 8701          */
 8702         if (strcmp(cmdname, "recv") == 0)
 8703                 cmdname = "receive";
 8704 
 8705         /*
 8706          * The 'snap' command is an alias for 'snapshot'
 8707          */
 8708         if (strcmp(cmdname, "snap") == 0)
 8709                 cmdname = "snapshot";
 8710 
 8711         /*
 8712          * Special case '-?'
 8713          */
 8714         if ((strcmp(cmdname, "-?") == 0) ||
 8715             (strcmp(cmdname, "--help") == 0))
 8716                 usage(B_TRUE);
 8717 
 8718         /*
 8719          * Special case '-V|--version'
 8720          */
 8721         if ((strcmp(cmdname, "-V") == 0) || (strcmp(cmdname, "--version") == 0))
 8722                 return (zfs_do_version(argc, argv));
 8723 
 8724         if ((g_zfs = libzfs_init()) == NULL) {
 8725                 (void) fprintf(stderr, "%s\n", libzfs_error_init(errno));
 8726                 return (1);
 8727         }
 8728 
 8729         zfs_save_arguments(argc, argv, history_str, sizeof (history_str));
 8730 
 8731         libzfs_print_on_error(g_zfs, B_TRUE);
 8732 
 8733         zfs_setproctitle_init(argc, argv, environ);
 8734 
 8735         /*
 8736          * Many commands modify input strings for string parsing reasons.
 8737          * We create a copy to protect the original argv.
 8738          */
 8739         newargv = safe_malloc((argc + 1) * sizeof (newargv[0]));
 8740         for (i = 0; i < argc; i++)
 8741                 newargv[i] = strdup(argv[i]);
 8742         newargv[argc] = NULL;
 8743 
 8744         /*
 8745          * Run the appropriate command.
 8746          */
 8747         libzfs_mnttab_cache(g_zfs, B_TRUE);
 8748         if (find_command_idx(cmdname, &i) == 0) {
 8749                 current_command = &command_table[i];
 8750                 ret = command_table[i].func(argc - 1, newargv + 1);
 8751         } else if (strchr(cmdname, '=') != NULL) {
 8752                 verify(find_command_idx("set", &i) == 0);
 8753                 current_command = &command_table[i];
 8754                 ret = command_table[i].func(argc, newargv);
 8755         } else {
 8756                 (void) fprintf(stderr, gettext("unrecognized "
 8757                     "command '%s'\n"), cmdname);
 8758                 usage(B_FALSE);
 8759                 ret = 1;
 8760         }
 8761 
 8762         for (i = 0; i < argc; i++)
 8763                 free(newargv[i]);
 8764         free(newargv);
 8765 
 8766         if (ret == 0 && log_history)
 8767                 (void) zpool_log_history(g_zfs, history_str);
 8768 
 8769         libzfs_fini(g_zfs);
 8770 
 8771         /*
 8772          * The 'ZFS_ABORT' environment variable causes us to dump core on exit
 8773          * for the purposes of running ::findleaks.
 8774          */
 8775         if (getenv("ZFS_ABORT") != NULL) {
 8776                 (void) printf("dumping core by request\n");
 8777                 abort();
 8778         }
 8779 
 8780         return (ret);
 8781 }
 8782 
 8783 /*
 8784  * zfs zone nsfile filesystem
 8785  *
 8786  * Add or delete the given dataset to/from the namespace.
 8787  */
 8788 #ifdef __linux__
 8789 static int
 8790 zfs_do_zone_impl(int argc, char **argv, boolean_t attach)
 8791 {
 8792         zfs_handle_t *zhp;
 8793         int ret;
 8794 
 8795         if (argc < 3) {
 8796                 (void) fprintf(stderr, gettext("missing argument(s)\n"));
 8797                 usage(B_FALSE);
 8798         }
 8799         if (argc > 3) {
 8800                 (void) fprintf(stderr, gettext("too many arguments\n"));
 8801                 usage(B_FALSE);
 8802         }
 8803 
 8804         zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM);
 8805         if (zhp == NULL)
 8806                 return (1);
 8807 
 8808         ret = (zfs_userns(zhp, argv[1], attach) != 0);
 8809 
 8810         zfs_close(zhp);
 8811         return (ret);
 8812 }
 8813 
 8814 static int
 8815 zfs_do_zone(int argc, char **argv)
 8816 {
 8817         return (zfs_do_zone_impl(argc, argv, B_TRUE));
 8818 }
 8819 
 8820 static int
 8821 zfs_do_unzone(int argc, char **argv)
 8822 {
 8823         return (zfs_do_zone_impl(argc, argv, B_FALSE));
 8824 }
 8825 #endif
 8826 
 8827 #ifdef __FreeBSD__
 8828 #include <sys/jail.h>
 8829 #include <jail.h>
 8830 /*
 8831  * Attach/detach the given dataset to/from the given jail
 8832  */
 8833 static int
 8834 zfs_do_jail_impl(int argc, char **argv, boolean_t attach)
 8835 {
 8836         zfs_handle_t *zhp;
 8837         int jailid, ret;
 8838 
 8839         /* check number of arguments */
 8840         if (argc < 3) {
 8841                 (void) fprintf(stderr, gettext("missing argument(s)\n"));
 8842                 usage(B_FALSE);
 8843         }
 8844         if (argc > 3) {
 8845                 (void) fprintf(stderr, gettext("too many arguments\n"));
 8846                 usage(B_FALSE);
 8847         }
 8848 
 8849         jailid = jail_getid(argv[1]);
 8850         if (jailid < 0) {
 8851                 (void) fprintf(stderr, gettext("invalid jail id or name\n"));
 8852                 usage(B_FALSE);
 8853         }
 8854 
 8855         zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM);
 8856         if (zhp == NULL)
 8857                 return (1);
 8858 
 8859         ret = (zfs_jail(zhp, jailid, attach) != 0);
 8860 
 8861         zfs_close(zhp);
 8862         return (ret);
 8863 }
 8864 
 8865 /*
 8866  * zfs jail jailid filesystem
 8867  *
 8868  * Attach the given dataset to the given jail
 8869  */
 8870 static int
 8871 zfs_do_jail(int argc, char **argv)
 8872 {
 8873         return (zfs_do_jail_impl(argc, argv, B_TRUE));
 8874 }
 8875 
 8876 /*
 8877  * zfs unjail jailid filesystem
 8878  *
 8879  * Detach the given dataset from the given jail
 8880  */
 8881 static int
 8882 zfs_do_unjail(int argc, char **argv)
 8883 {
 8884         return (zfs_do_jail_impl(argc, argv, B_FALSE));
 8885 }
 8886 #endif

Cache object: 08c5c752f2aac595fb38d0409adda90b


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.