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 http://www.opensolaris.org/os/licensing.
  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, 2014 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 2013 Nexenta Systems, Inc.  All rights reserved.
  29  */
  30 
  31 #include <assert.h>
  32 #include <ctype.h>
  33 #include <errno.h>
  34 #include <libgen.h>
  35 #include <libintl.h>
  36 #include <libuutil.h>
  37 #include <libnvpair.h>
  38 #include <locale.h>
  39 #include <stddef.h>
  40 #include <stdio.h>
  41 #include <stdlib.h>
  42 #include <strings.h>
  43 #include <unistd.h>
  44 #include <fcntl.h>
  45 #include <zone.h>
  46 #include <grp.h>
  47 #include <pwd.h>
  48 #include <signal.h>
  49 #include <sys/list.h>
  50 #include <sys/mkdev.h>
  51 #include <sys/mntent.h>
  52 #include <sys/mnttab.h>
  53 #include <sys/mount.h>
  54 #include <sys/stat.h>
  55 #include <sys/fs/zfs.h>
  56 #include <sys/types.h>
  57 #include <time.h>
  58 
  59 #include <libzfs.h>
  60 #include <libzfs_core.h>
  61 #include <zfs_prop.h>
  62 #include <zfs_deleg.h>
  63 #include <libuutil.h>
  64 #include <aclutils.h>
  65 #include <directory.h>
  66 #include <idmap.h>
  67 
  68 #include "zfs_iter.h"
  69 #include "zfs_util.h"
  70 #include "zfs_comutil.h"
  71 
  72 libzfs_handle_t *g_zfs;
  73 
  74 static FILE *mnttab_file;
  75 static char history_str[HIS_MAX_RECORD_LEN];
  76 static boolean_t log_history = B_TRUE;
  77 
  78 static int zfs_do_clone(int argc, char **argv);
  79 static int zfs_do_create(int argc, char **argv);
  80 static int zfs_do_destroy(int argc, char **argv);
  81 static int zfs_do_get(int argc, char **argv);
  82 static int zfs_do_inherit(int argc, char **argv);
  83 static int zfs_do_list(int argc, char **argv);
  84 static int zfs_do_mount(int argc, char **argv);
  85 static int zfs_do_rename(int argc, char **argv);
  86 static int zfs_do_rollback(int argc, char **argv);
  87 static int zfs_do_set(int argc, char **argv);
  88 static int zfs_do_upgrade(int argc, char **argv);
  89 static int zfs_do_snapshot(int argc, char **argv);
  90 static int zfs_do_unmount(int argc, char **argv);
  91 static int zfs_do_share(int argc, char **argv);
  92 static int zfs_do_unshare(int argc, char **argv);
  93 static int zfs_do_send(int argc, char **argv);
  94 static int zfs_do_receive(int argc, char **argv);
  95 static int zfs_do_promote(int argc, char **argv);
  96 static int zfs_do_userspace(int argc, char **argv);
  97 static int zfs_do_allow(int argc, char **argv);
  98 static int zfs_do_unallow(int argc, char **argv);
  99 static int zfs_do_hold(int argc, char **argv);
 100 static int zfs_do_holds(int argc, char **argv);
 101 static int zfs_do_release(int argc, char **argv);
 102 static int zfs_do_diff(int argc, char **argv);
 103 static int zfs_do_bookmark(int argc, char **argv);
 104 
 105 /*
 106  * Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
 107  */
 108 
 109 #ifdef DEBUG
 110 const char *
 111 _umem_debug_init(void)
 112 {
 113         return ("default,verbose"); /* $UMEM_DEBUG setting */
 114 }
 115 
 116 const char *
 117 _umem_logging_init(void)
 118 {
 119         return ("fail,contents"); /* $UMEM_LOGGING setting */
 120 }
 121 #endif
 122 
 123 typedef enum {
 124         HELP_CLONE,
 125         HELP_CREATE,
 126         HELP_DESTROY,
 127         HELP_GET,
 128         HELP_INHERIT,
 129         HELP_UPGRADE,
 130         HELP_LIST,
 131         HELP_MOUNT,
 132         HELP_PROMOTE,
 133         HELP_RECEIVE,
 134         HELP_RENAME,
 135         HELP_ROLLBACK,
 136         HELP_SEND,
 137         HELP_SET,
 138         HELP_SHARE,
 139         HELP_SNAPSHOT,
 140         HELP_UNMOUNT,
 141         HELP_UNSHARE,
 142         HELP_ALLOW,
 143         HELP_UNALLOW,
 144         HELP_USERSPACE,
 145         HELP_GROUPSPACE,
 146         HELP_HOLD,
 147         HELP_HOLDS,
 148         HELP_RELEASE,
 149         HELP_DIFF,
 150         HELP_BOOKMARK,
 151 } zfs_help_t;
 152 
 153 typedef struct zfs_command {
 154         const char      *name;
 155         int             (*func)(int argc, char **argv);
 156         zfs_help_t      usage;
 157 } zfs_command_t;
 158 
 159 /*
 160  * Master command table.  Each ZFS command has a name, associated function, and
 161  * usage message.  The usage messages need to be internationalized, so we have
 162  * to have a function to return the usage message based on a command index.
 163  *
 164  * These commands are organized according to how they are displayed in the usage
 165  * message.  An empty command (one with a NULL name) indicates an empty line in
 166  * the generic usage message.
 167  */
 168 static zfs_command_t command_table[] = {
 169         { "create",     zfs_do_create,          HELP_CREATE             },
 170         { "destroy",    zfs_do_destroy,         HELP_DESTROY            },
 171         { NULL },
 172         { "snapshot",   zfs_do_snapshot,        HELP_SNAPSHOT           },
 173         { "rollback",   zfs_do_rollback,        HELP_ROLLBACK           },
 174         { "clone",      zfs_do_clone,           HELP_CLONE              },
 175         { "promote",    zfs_do_promote,         HELP_PROMOTE            },
 176         { "rename",     zfs_do_rename,          HELP_RENAME             },
 177         { "bookmark",   zfs_do_bookmark,        HELP_BOOKMARK           },
 178         { NULL },
 179         { "list",       zfs_do_list,            HELP_LIST               },
 180         { NULL },
 181         { "set",        zfs_do_set,             HELP_SET                },
 182         { "get",        zfs_do_get,             HELP_GET                },
 183         { "inherit",    zfs_do_inherit,         HELP_INHERIT            },
 184         { "upgrade",    zfs_do_upgrade,         HELP_UPGRADE            },
 185         { "userspace",  zfs_do_userspace,       HELP_USERSPACE          },
 186         { "groupspace", zfs_do_userspace,       HELP_GROUPSPACE         },
 187         { NULL },
 188         { "mount",      zfs_do_mount,           HELP_MOUNT              },
 189         { "unmount",    zfs_do_unmount,         HELP_UNMOUNT            },
 190         { "share",      zfs_do_share,           HELP_SHARE              },
 191         { "unshare",    zfs_do_unshare,         HELP_UNSHARE            },
 192         { NULL },
 193         { "send",       zfs_do_send,            HELP_SEND               },
 194         { "receive",    zfs_do_receive,         HELP_RECEIVE            },
 195         { NULL },
 196         { "allow",      zfs_do_allow,           HELP_ALLOW              },
 197         { NULL },
 198         { "unallow",    zfs_do_unallow,         HELP_UNALLOW            },
 199         { NULL },
 200         { "hold",       zfs_do_hold,            HELP_HOLD               },
 201         { "holds",      zfs_do_holds,           HELP_HOLDS              },
 202         { "release",    zfs_do_release,         HELP_RELEASE            },
 203         { "diff",       zfs_do_diff,            HELP_DIFF               },
 204 };
 205 
 206 #define NCOMMAND        (sizeof (command_table) / sizeof (command_table[0]))
 207 
 208 zfs_command_t *current_command;
 209 
 210 static const char *
 211 get_usage(zfs_help_t idx)
 212 {
 213         switch (idx) {
 214         case HELP_CLONE:
 215                 return (gettext("\tclone [-p] [-o property=value] ... "
 216                     "<snapshot> <filesystem|volume>\n"));
 217         case HELP_CREATE:
 218                 return (gettext("\tcreate [-p] [-o property=value] ... "
 219                     "<filesystem>\n"
 220                     "\tcreate [-ps] [-b blocksize] [-o property=value] ... "
 221                     "-V <size> <volume>\n"));
 222         case HELP_DESTROY:
 223                 return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
 224                     "\tdestroy [-dnpRrv] "
 225                     "<filesystem|volume>@<snap>[%<snap>][,...]\n"
 226                     "\tdestroy <filesystem|volume>#<bookmark>\n"));
 227         case HELP_GET:
 228                 return (gettext("\tget [-rHp] [-d max] "
 229                     "[-o \"all\" | field[,...]]\n"
 230                     "\t    [-t type[,...]] [-s source[,...]]\n"
 231                     "\t    <\"all\" | property[,...]> "
 232                     "[filesystem|volume|snapshot] ...\n"));
 233         case HELP_INHERIT:
 234                 return (gettext("\tinherit [-rS] <property> "
 235                     "<filesystem|volume|snapshot> ...\n"));
 236         case HELP_UPGRADE:
 237                 return (gettext("\tupgrade [-v]\n"
 238                     "\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
 239         case HELP_LIST:
 240                 return (gettext("\tlist [-Hp] [-r|-d max] [-o property[,...]] "
 241                     "[-s property]...\n\t    [-S property]... [-t type[,...]] "
 242                     "[filesystem|volume|snapshot] ...\n"));
 243         case HELP_MOUNT:
 244                 return (gettext("\tmount\n"
 245                     "\tmount [-vO] [-o opts] <-a | filesystem>\n"));
 246         case HELP_PROMOTE:
 247                 return (gettext("\tpromote <clone-filesystem>\n"));
 248         case HELP_RECEIVE:
 249                 return (gettext("\treceive [-vnFu] <filesystem|volume|"
 250                     "snapshot>\n"
 251                     "\treceive [-vnFu] [-o origin=<snapshot>] [-d | -e] "
 252                     "<filesystem>\n"));
 253         case HELP_RENAME:
 254                 return (gettext("\trename [-f] <filesystem|volume|snapshot> "
 255                     "<filesystem|volume|snapshot>\n"
 256                     "\trename [-f] -p <filesystem|volume> <filesystem|volume>\n"
 257                     "\trename -r <snapshot> <snapshot>\n"));
 258         case HELP_ROLLBACK:
 259                 return (gettext("\trollback [-rRf] <snapshot>\n"));
 260         case HELP_SEND:
 261                 return (gettext("\tsend [-DnPpRvLe] [-[iI] snapshot] "
 262                     "<snapshot>\n"
 263                     "\tsend [-Le] [-i snapshot|bookmark] "
 264                     "<filesystem|volume|snapshot>\n"));
 265         case HELP_SET:
 266                 return (gettext("\tset <property=value> ... "
 267                     "<filesystem|volume|snapshot> ...\n"));
 268         case HELP_SHARE:
 269                 return (gettext("\tshare <-a | filesystem>\n"));
 270         case HELP_SNAPSHOT:
 271                 return (gettext("\tsnapshot [-r] [-o property=value] ... "
 272                     "<filesystem|volume>@<snap> ...\n"));
 273         case HELP_UNMOUNT:
 274                 return (gettext("\tunmount [-f] "
 275                     "<-a | filesystem|mountpoint>\n"));
 276         case HELP_UNSHARE:
 277                 return (gettext("\tunshare "
 278                     "<-a | filesystem|mountpoint>\n"));
 279         case HELP_ALLOW:
 280                 return (gettext("\tallow <filesystem|volume>\n"
 281                     "\tallow [-ldug] "
 282                     "<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n"
 283                     "\t    <filesystem|volume>\n"
 284                     "\tallow [-ld] -e <perm|@setname>[,...] "
 285                     "<filesystem|volume>\n"
 286                     "\tallow -c <perm|@setname>[,...] <filesystem|volume>\n"
 287                     "\tallow -s @setname <perm|@setname>[,...] "
 288                     "<filesystem|volume>\n"));
 289         case HELP_UNALLOW:
 290                 return (gettext("\tunallow [-rldug] "
 291                     "<\"everyone\"|user|group>[,...]\n"
 292                     "\t    [<perm|@setname>[,...]] <filesystem|volume>\n"
 293                     "\tunallow [-rld] -e [<perm|@setname>[,...]] "
 294                     "<filesystem|volume>\n"
 295                     "\tunallow [-r] -c [<perm|@setname>[,...]] "
 296                     "<filesystem|volume>\n"
 297                     "\tunallow [-r] -s @setname [<perm|@setname>[,...]] "
 298                     "<filesystem|volume>\n"));
 299         case HELP_USERSPACE:
 300                 return (gettext("\tuserspace [-Hinp] [-o field[,...]] "
 301                     "[-s field] ...\n"
 302                     "\t    [-S field] ... [-t type[,...]] "
 303                     "<filesystem|snapshot>\n"));
 304         case HELP_GROUPSPACE:
 305                 return (gettext("\tgroupspace [-Hinp] [-o field[,...]] "
 306                     "[-s field] ...\n"
 307                     "\t    [-S field] ... [-t type[,...]] "
 308                     "<filesystem|snapshot>\n"));
 309         case HELP_HOLD:
 310                 return (gettext("\thold [-r] <tag> <snapshot> ...\n"));
 311         case HELP_HOLDS:
 312                 return (gettext("\tholds [-r] <snapshot> ...\n"));
 313         case HELP_RELEASE:
 314                 return (gettext("\trelease [-r] <tag> <snapshot> ...\n"));
 315         case HELP_DIFF:
 316                 return (gettext("\tdiff [-FHt] <snapshot> "
 317                     "[snapshot|filesystem]\n"));
 318         case HELP_BOOKMARK:
 319                 return (gettext("\tbookmark <snapshot> <bookmark>\n"));
 320         }
 321 
 322         abort();
 323         /* NOTREACHED */
 324 }
 325 
 326 void
 327 nomem(void)
 328 {
 329         (void) fprintf(stderr, gettext("internal error: out of memory\n"));
 330         exit(1);
 331 }
 332 
 333 /*
 334  * Utility function to guarantee malloc() success.
 335  */
 336 
 337 void *
 338 safe_malloc(size_t size)
 339 {
 340         void *data;
 341 
 342         if ((data = calloc(1, size)) == NULL)
 343                 nomem();
 344 
 345         return (data);
 346 }
 347 
 348 static char *
 349 safe_strdup(char *str)
 350 {
 351         char *dupstr = strdup(str);
 352 
 353         if (dupstr == NULL)
 354                 nomem();
 355 
 356         return (dupstr);
 357 }
 358 
 359 /*
 360  * Callback routine that will print out information for each of
 361  * the properties.
 362  */
 363 static int
 364 usage_prop_cb(int prop, void *cb)
 365 {
 366         FILE *fp = cb;
 367 
 368         (void) fprintf(fp, "\t%-15s ", zfs_prop_to_name(prop));
 369 
 370         if (zfs_prop_readonly(prop))
 371                 (void) fprintf(fp, " NO    ");
 372         else
 373                 (void) fprintf(fp, "YES    ");
 374 
 375         if (zfs_prop_inheritable(prop))
 376                 (void) fprintf(fp, "  YES   ");
 377         else
 378                 (void) fprintf(fp, "   NO   ");
 379 
 380         if (zfs_prop_values(prop) == NULL)
 381                 (void) fprintf(fp, "-\n");
 382         else
 383                 (void) fprintf(fp, "%s\n", zfs_prop_values(prop));
 384 
 385         return (ZPROP_CONT);
 386 }
 387 
 388 /*
 389  * Display usage message.  If we're inside a command, display only the usage for
 390  * that command.  Otherwise, iterate over the entire command table and display
 391  * a complete usage message.
 392  */
 393 static void
 394 usage(boolean_t requested)
 395 {
 396         int i;
 397         boolean_t show_properties = B_FALSE;
 398         FILE *fp = requested ? stdout : stderr;
 399 
 400         if (current_command == NULL) {
 401 
 402                 (void) fprintf(fp, gettext("usage: zfs command args ...\n"));
 403                 (void) fprintf(fp,
 404                     gettext("where 'command' is one of the following:\n\n"));
 405 
 406                 for (i = 0; i < NCOMMAND; i++) {
 407                         if (command_table[i].name == NULL)
 408                                 (void) fprintf(fp, "\n");
 409                         else
 410                                 (void) fprintf(fp, "%s",
 411                                     get_usage(command_table[i].usage));
 412                 }
 413 
 414                 (void) fprintf(fp, gettext("\nEach dataset is of the form: "
 415                     "pool/[dataset/]*dataset[@name]\n"));
 416         } else {
 417                 (void) fprintf(fp, gettext("usage:\n"));
 418                 (void) fprintf(fp, "%s", get_usage(current_command->usage));
 419         }
 420 
 421         if (current_command != NULL &&
 422             (strcmp(current_command->name, "set") == 0 ||
 423             strcmp(current_command->name, "get") == 0 ||
 424             strcmp(current_command->name, "inherit") == 0 ||
 425             strcmp(current_command->name, "list") == 0))
 426                 show_properties = B_TRUE;
 427 
 428         if (show_properties) {
 429                 (void) fprintf(fp,
 430                     gettext("\nThe following properties are supported:\n"));
 431 
 432                 (void) fprintf(fp, "\n\t%-14s %s  %s   %s\n\n",
 433                     "PROPERTY", "EDIT", "INHERIT", "VALUES");
 434 
 435                 /* Iterate over all properties */
 436                 (void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE,
 437                     ZFS_TYPE_DATASET);
 438 
 439                 (void) fprintf(fp, "\t%-15s ", "userused@...");
 440                 (void) fprintf(fp, " NO       NO   <size>\n");
 441                 (void) fprintf(fp, "\t%-15s ", "groupused@...");
 442                 (void) fprintf(fp, " NO       NO   <size>\n");
 443                 (void) fprintf(fp, "\t%-15s ", "userquota@...");
 444                 (void) fprintf(fp, "YES       NO   <size> | none\n");
 445                 (void) fprintf(fp, "\t%-15s ", "groupquota@...");
 446                 (void) fprintf(fp, "YES       NO   <size> | none\n");
 447                 (void) fprintf(fp, "\t%-15s ", "written@<snap>");
 448                 (void) fprintf(fp, " NO       NO   <size>\n");
 449 
 450                 (void) fprintf(fp, gettext("\nSizes are specified in bytes "
 451                     "with standard units such as K, M, G, etc.\n"));
 452                 (void) fprintf(fp, gettext("\nUser-defined properties can "
 453                     "be specified by using a name containing a colon (:).\n"));
 454                 (void) fprintf(fp, gettext("\nThe {user|group}{used|quota}@ "
 455                     "properties must be appended with\n"
 456                     "a user or group specifier of one of these forms:\n"
 457                     "    POSIX name      (eg: \"matt\")\n"
 458                     "    POSIX id        (eg: \"126829\")\n"
 459                     "    SMB name@domain (eg: \"matt@sun\")\n"
 460                     "    SMB SID         (eg: \"S-1-234-567-89\")\n"));
 461         } else {
 462                 (void) fprintf(fp,
 463                     gettext("\nFor the property list, run: %s\n"),
 464                     "zfs set|get");
 465                 (void) fprintf(fp,
 466                     gettext("\nFor the delegated permission list, run: %s\n"),
 467                     "zfs allow|unallow");
 468         }
 469 
 470         /*
 471          * See comments at end of main().
 472          */
 473         if (getenv("ZFS_ABORT") != NULL) {
 474                 (void) printf("dumping core by request\n");
 475                 abort();
 476         }
 477 
 478         exit(requested ? 0 : 2);
 479 }
 480 
 481 /*
 482  * Take a property=value argument string and add it to the given nvlist.
 483  * Modifies the argument inplace.
 484  */
 485 static int
 486 parseprop(nvlist_t *props, char *propname)
 487 {
 488         char *propval, *strval;
 489 
 490         if ((propval = strchr(propname, '=')) == NULL) {
 491                 (void) fprintf(stderr, gettext("missing "
 492                     "'=' for property=value argument\n"));
 493                 return (-1);
 494         }
 495         *propval = '\0';
 496         propval++;
 497         if (nvlist_lookup_string(props, propname, &strval) == 0) {
 498                 (void) fprintf(stderr, gettext("property '%s' "
 499                     "specified multiple times\n"), propname);
 500                 return (-1);
 501         }
 502         if (nvlist_add_string(props, propname, propval) != 0)
 503                 nomem();
 504         return (0);
 505 }
 506 
 507 static int
 508 parse_depth(char *opt, int *flags)
 509 {
 510         char *tmp;
 511         int depth;
 512 
 513         depth = (int)strtol(opt, &tmp, 0);
 514         if (*tmp) {
 515                 (void) fprintf(stderr,
 516                     gettext("%s is not an integer\n"), optarg);
 517                 usage(B_FALSE);
 518         }
 519         if (depth < 0) {
 520                 (void) fprintf(stderr,
 521                     gettext("Depth can not be negative.\n"));
 522                 usage(B_FALSE);
 523         }
 524         *flags |= (ZFS_ITER_DEPTH_LIMIT|ZFS_ITER_RECURSE);
 525         return (depth);
 526 }
 527 
 528 #define PROGRESS_DELAY 2                /* seconds */
 529 
 530 static char *pt_reverse = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
 531 static time_t pt_begin;
 532 static char *pt_header = NULL;
 533 static boolean_t pt_shown;
 534 
 535 static void
 536 start_progress_timer(void)
 537 {
 538         pt_begin = time(NULL) + PROGRESS_DELAY;
 539         pt_shown = B_FALSE;
 540 }
 541 
 542 static void
 543 set_progress_header(char *header)
 544 {
 545         assert(pt_header == NULL);
 546         pt_header = safe_strdup(header);
 547         if (pt_shown) {
 548                 (void) printf("%s: ", header);
 549                 (void) fflush(stdout);
 550         }
 551 }
 552 
 553 static void
 554 update_progress(char *update)
 555 {
 556         if (!pt_shown && time(NULL) > pt_begin) {
 557                 int len = strlen(update);
 558 
 559                 (void) printf("%s: %s%*.*s", pt_header, update, len, len,
 560                     pt_reverse);
 561                 (void) fflush(stdout);
 562                 pt_shown = B_TRUE;
 563         } else if (pt_shown) {
 564                 int len = strlen(update);
 565 
 566                 (void) printf("%s%*.*s", update, len, len, pt_reverse);
 567                 (void) fflush(stdout);
 568         }
 569 }
 570 
 571 static void
 572 finish_progress(char *done)
 573 {
 574         if (pt_shown) {
 575                 (void) printf("%s\n", done);
 576                 (void) fflush(stdout);
 577         }
 578         free(pt_header);
 579         pt_header = NULL;
 580 }
 581 
 582 /*
 583  * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
 584  *
 585  * Given an existing dataset, create a writable copy whose initial contents
 586  * are the same as the source.  The newly created dataset maintains a
 587  * dependency on the original; the original cannot be destroyed so long as
 588  * the clone exists.
 589  *
 590  * The '-p' flag creates all the non-existing ancestors of the target first.
 591  */
 592 static int
 593 zfs_do_clone(int argc, char **argv)
 594 {
 595         zfs_handle_t *zhp = NULL;
 596         boolean_t parents = B_FALSE;
 597         nvlist_t *props;
 598         int ret = 0;
 599         int c;
 600 
 601         if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
 602                 nomem();
 603 
 604         /* check options */
 605         while ((c = getopt(argc, argv, "o:p")) != -1) {
 606                 switch (c) {
 607                 case 'o':
 608                         if (parseprop(props, optarg) != 0)
 609                                 return (1);
 610                         break;
 611                 case 'p':
 612                         parents = B_TRUE;
 613                         break;
 614                 case '?':
 615                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 616                             optopt);
 617                         goto usage;
 618                 }
 619         }
 620 
 621         argc -= optind;
 622         argv += optind;
 623 
 624         /* check number of arguments */
 625         if (argc < 1) {
 626                 (void) fprintf(stderr, gettext("missing source dataset "
 627                     "argument\n"));
 628                 goto usage;
 629         }
 630         if (argc < 2) {
 631                 (void) fprintf(stderr, gettext("missing target dataset "
 632                     "argument\n"));
 633                 goto usage;
 634         }
 635         if (argc > 2) {
 636                 (void) fprintf(stderr, gettext("too many arguments\n"));
 637                 goto usage;
 638         }
 639 
 640         /* open the source dataset */
 641         if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
 642                 return (1);
 643 
 644         if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM |
 645             ZFS_TYPE_VOLUME)) {
 646                 /*
 647                  * Now create the ancestors of the target dataset.  If the
 648                  * target already exists and '-p' option was used we should not
 649                  * complain.
 650                  */
 651                 if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM |
 652                     ZFS_TYPE_VOLUME))
 653                         return (0);
 654                 if (zfs_create_ancestors(g_zfs, argv[1]) != 0)
 655                         return (1);
 656         }
 657 
 658         /* pass to libzfs */
 659         ret = zfs_clone(zhp, argv[1], props);
 660 
 661         /* create the mountpoint if necessary */
 662         if (ret == 0) {
 663                 zfs_handle_t *clone;
 664 
 665                 clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_DATASET);
 666                 if (clone != NULL) {
 667                         if (zfs_get_type(clone) != ZFS_TYPE_VOLUME)
 668                                 if ((ret = zfs_mount(clone, NULL, 0)) == 0)
 669                                         ret = zfs_share(clone);
 670                         zfs_close(clone);
 671                 }
 672         }
 673 
 674         zfs_close(zhp);
 675         nvlist_free(props);
 676 
 677         return (!!ret);
 678 
 679 usage:
 680         if (zhp)
 681                 zfs_close(zhp);
 682         nvlist_free(props);
 683         usage(B_FALSE);
 684         return (-1);
 685 }
 686 
 687 /*
 688  * zfs create [-p] [-o prop=value] ... fs
 689  * zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size
 690  *
 691  * Create a new dataset.  This command can be used to create filesystems
 692  * and volumes.  Snapshot creation is handled by 'zfs snapshot'.
 693  * For volumes, the user must specify a size to be used.
 694  *
 695  * The '-s' flag applies only to volumes, and indicates that we should not try
 696  * to set the reservation for this volume.  By default we set a reservation
 697  * equal to the size for any volume.  For pools with SPA_VERSION >=
 698  * SPA_VERSION_REFRESERVATION, we set a refreservation instead.
 699  *
 700  * The '-p' flag creates all the non-existing ancestors of the target first.
 701  */
 702 static int
 703 zfs_do_create(int argc, char **argv)
 704 {
 705         zfs_type_t type = ZFS_TYPE_FILESYSTEM;
 706         zfs_handle_t *zhp = NULL;
 707         uint64_t volsize;
 708         int c;
 709         boolean_t noreserve = B_FALSE;
 710         boolean_t bflag = B_FALSE;
 711         boolean_t parents = B_FALSE;
 712         int ret = 1;
 713         nvlist_t *props;
 714         uint64_t intval;
 715         int canmount = ZFS_CANMOUNT_OFF;
 716 
 717         if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
 718                 nomem();
 719 
 720         /* check options */
 721         while ((c = getopt(argc, argv, ":V:b:so:p")) != -1) {
 722                 switch (c) {
 723                 case 'V':
 724                         type = ZFS_TYPE_VOLUME;
 725                         if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
 726                                 (void) fprintf(stderr, gettext("bad volume "
 727                                     "size '%s': %s\n"), optarg,
 728                                     libzfs_error_description(g_zfs));
 729                                 goto error;
 730                         }
 731 
 732                         if (nvlist_add_uint64(props,
 733                             zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0)
 734                                 nomem();
 735                         volsize = intval;
 736                         break;
 737                 case 'p':
 738                         parents = B_TRUE;
 739                         break;
 740                 case 'b':
 741                         bflag = B_TRUE;
 742                         if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
 743                                 (void) fprintf(stderr, gettext("bad volume "
 744                                     "block size '%s': %s\n"), optarg,
 745                                     libzfs_error_description(g_zfs));
 746                                 goto error;
 747                         }
 748 
 749                         if (nvlist_add_uint64(props,
 750                             zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
 751                             intval) != 0)
 752                                 nomem();
 753                         break;
 754                 case 'o':
 755                         if (parseprop(props, optarg) != 0)
 756                                 goto error;
 757                         break;
 758                 case 's':
 759                         noreserve = B_TRUE;
 760                         break;
 761                 case ':':
 762                         (void) fprintf(stderr, gettext("missing size "
 763                             "argument\n"));
 764                         goto badusage;
 765                 case '?':
 766                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
 767                             optopt);
 768                         goto badusage;
 769                 }
 770         }
 771 
 772         if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) {
 773                 (void) fprintf(stderr, gettext("'-s' and '-b' can only be "
 774                     "used when creating a volume\n"));
 775                 goto badusage;
 776         }
 777 
 778         argc -= optind;
 779         argv += optind;
 780 
 781         /* check number of arguments */
 782         if (argc == 0) {
 783                 (void) fprintf(stderr, gettext("missing %s argument\n"),
 784                     zfs_type_to_name(type));
 785                 goto badusage;
 786         }
 787         if (argc > 1) {
 788                 (void) fprintf(stderr, gettext("too many arguments\n"));
 789                 goto badusage;
 790         }
 791 
 792         if (type == ZFS_TYPE_VOLUME && !noreserve) {
 793                 zpool_handle_t *zpool_handle;
 794                 nvlist_t *real_props;
 795                 uint64_t spa_version;
 796                 char *p;
 797                 zfs_prop_t resv_prop;
 798                 char *strval;
 799                 char msg[1024];
 800 
 801                 if (p = strchr(argv[0], '/'))
 802                         *p = '\0';
 803                 zpool_handle = zpool_open(g_zfs, argv[0]);
 804                 if (p != NULL)
 805                         *p = '/';
 806                 if (zpool_handle == NULL)
 807                         goto error;
 808                 spa_version = zpool_get_prop_int(zpool_handle,
 809                     ZPOOL_PROP_VERSION, NULL);
 810                 zpool_close(zpool_handle);
 811                 if (spa_version >= SPA_VERSION_REFRESERVATION)
 812                         resv_prop = ZFS_PROP_REFRESERVATION;
 813                 else
 814                         resv_prop = ZFS_PROP_RESERVATION;
 815 
 816                 (void) snprintf(msg, sizeof (msg),
 817                     gettext("cannot create '%s'"), argv[0]);
 818                 if (props && (real_props = zfs_valid_proplist(g_zfs, type,
 819                     props, 0, NULL, msg)) == NULL)
 820                         goto error;
 821 
 822                 volsize = zvol_volsize_to_reservation(volsize, real_props);
 823                 nvlist_free(real_props);
 824 
 825                 if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop),
 826                     &strval) != 0) {
 827                         if (nvlist_add_uint64(props,
 828                             zfs_prop_to_name(resv_prop), volsize) != 0) {
 829                                 nvlist_free(props);
 830                                 nomem();
 831                         }
 832                 }
 833         }
 834 
 835         if (parents && zfs_name_valid(argv[0], type)) {
 836                 /*
 837                  * Now create the ancestors of target dataset.  If the target
 838                  * already exists and '-p' option was used we should not
 839                  * complain.
 840                  */
 841                 if (zfs_dataset_exists(g_zfs, argv[0], type)) {
 842                         ret = 0;
 843                         goto error;
 844                 }
 845                 if (zfs_create_ancestors(g_zfs, argv[0]) != 0)
 846                         goto error;
 847         }
 848 
 849         /* pass to libzfs */
 850         if (zfs_create(g_zfs, argv[0], type, props) != 0)
 851                 goto error;
 852 
 853         if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
 854                 goto error;
 855 
 856         ret = 0;
 857         /*
 858          * if the user doesn't want the dataset automatically mounted,
 859          * then skip the mount/share step
 860          */
 861         if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type))
 862                 canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
 863 
 864         /*
 865          * Mount and/or share the new filesystem as appropriate.  We provide a
 866          * verbose error message to let the user know that their filesystem was
 867          * in fact created, even if we failed to mount or share it.
 868          */
 869         if (canmount == ZFS_CANMOUNT_ON) {
 870                 if (zfs_mount(zhp, NULL, 0) != 0) {
 871                         (void) fprintf(stderr, gettext("filesystem "
 872                             "successfully created, but not mounted\n"));
 873                         ret = 1;
 874                 } else if (zfs_share(zhp) != 0) {
 875                         (void) fprintf(stderr, gettext("filesystem "
 876                             "successfully created, but not shared\n"));
 877                         ret = 1;
 878                 }
 879         }
 880 
 881 error:
 882         if (zhp)
 883                 zfs_close(zhp);
 884         nvlist_free(props);
 885         return (ret);
 886 badusage:
 887         nvlist_free(props);
 888         usage(B_FALSE);
 889         return (2);
 890 }
 891 
 892 /*
 893  * zfs destroy [-rRf] <fs, vol>
 894  * zfs destroy [-rRd] <snap>
 895  *
 896  *      -r      Recursively destroy all children
 897  *      -R      Recursively destroy all dependents, including clones
 898  *      -f      Force unmounting of any dependents
 899  *      -d      If we can't destroy now, mark for deferred destruction
 900  *
 901  * Destroys the given dataset.  By default, it will unmount any filesystems,
 902  * and refuse to destroy a dataset that has any dependents.  A dependent can
 903  * either be a child, or a clone of a child.
 904  */
 905 typedef struct destroy_cbdata {
 906         boolean_t       cb_first;
 907         boolean_t       cb_force;
 908         boolean_t       cb_recurse;
 909         boolean_t       cb_error;
 910         boolean_t       cb_doclones;
 911         zfs_handle_t    *cb_target;
 912         boolean_t       cb_defer_destroy;
 913         boolean_t       cb_verbose;
 914         boolean_t       cb_parsable;
 915         boolean_t       cb_dryrun;
 916         nvlist_t        *cb_nvl;
 917         nvlist_t        *cb_batchedsnaps;
 918 
 919         /* first snap in contiguous run */
 920         char            *cb_firstsnap;
 921         /* previous snap in contiguous run */
 922         char            *cb_prevsnap;
 923         int64_t         cb_snapused;
 924         char            *cb_snapspec;
 925         char            *cb_bookmark;
 926 } destroy_cbdata_t;
 927 
 928 /*
 929  * Check for any dependents based on the '-r' or '-R' flags.
 930  */
 931 static int
 932 destroy_check_dependent(zfs_handle_t *zhp, void *data)
 933 {
 934         destroy_cbdata_t *cbp = data;
 935         const char *tname = zfs_get_name(cbp->cb_target);
 936         const char *name = zfs_get_name(zhp);
 937 
 938         if (strncmp(tname, name, strlen(tname)) == 0 &&
 939             (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) {
 940                 /*
 941                  * This is a direct descendant, not a clone somewhere else in
 942                  * the hierarchy.
 943                  */
 944                 if (cbp->cb_recurse)
 945                         goto out;
 946 
 947                 if (cbp->cb_first) {
 948                         (void) fprintf(stderr, gettext("cannot destroy '%s': "
 949                             "%s has children\n"),
 950                             zfs_get_name(cbp->cb_target),
 951                             zfs_type_to_name(zfs_get_type(cbp->cb_target)));
 952                         (void) fprintf(stderr, gettext("use '-r' to destroy "
 953                             "the following datasets:\n"));
 954                         cbp->cb_first = B_FALSE;
 955                         cbp->cb_error = B_TRUE;
 956                 }
 957 
 958                 (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
 959         } else {
 960                 /*
 961                  * This is a clone.  We only want to report this if the '-r'
 962                  * wasn't specified, or the target is a snapshot.
 963                  */
 964                 if (!cbp->cb_recurse &&
 965                     zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT)
 966                         goto out;
 967 
 968                 if (cbp->cb_first) {
 969                         (void) fprintf(stderr, gettext("cannot destroy '%s': "
 970                             "%s has dependent clones\n"),
 971                             zfs_get_name(cbp->cb_target),
 972                             zfs_type_to_name(zfs_get_type(cbp->cb_target)));
 973                         (void) fprintf(stderr, gettext("use '-R' to destroy "
 974                             "the following datasets:\n"));
 975                         cbp->cb_first = B_FALSE;
 976                         cbp->cb_error = B_TRUE;
 977                         cbp->cb_dryrun = B_TRUE;
 978                 }
 979 
 980                 (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
 981         }
 982 
 983 out:
 984         zfs_close(zhp);
 985         return (0);
 986 }
 987 
 988 static int
 989 destroy_callback(zfs_handle_t *zhp, void *data)
 990 {
 991         destroy_cbdata_t *cb = data;
 992         const char *name = zfs_get_name(zhp);
 993 
 994         if (cb->cb_verbose) {
 995                 if (cb->cb_parsable) {
 996                         (void) printf("destroy\t%s\n", name);
 997                 } else if (cb->cb_dryrun) {
 998                         (void) printf(gettext("would destroy %s\n"),
 999                             name);
1000                 } else {
1001                         (void) printf(gettext("will destroy %s\n"),
1002                             name);
1003                 }
1004         }
1005 
1006         /*
1007          * Ignore pools (which we've already flagged as an error before getting
1008          * here).
1009          */
1010         if (strchr(zfs_get_name(zhp), '/') == NULL &&
1011             zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
1012                 zfs_close(zhp);
1013                 return (0);
1014         }
1015         if (cb->cb_dryrun) {
1016                 zfs_close(zhp);
1017                 return (0);
1018         }
1019 
1020         /*
1021          * We batch up all contiguous snapshots (even of different
1022          * filesystems) and destroy them with one ioctl.  We can't
1023          * simply do all snap deletions and then all fs deletions,
1024          * because we must delete a clone before its origin.
1025          */
1026         if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) {
1027                 fnvlist_add_boolean(cb->cb_batchedsnaps, name);
1028         } else {
1029                 int error = zfs_destroy_snaps_nvl(g_zfs,
1030                     cb->cb_batchedsnaps, B_FALSE);
1031                 fnvlist_free(cb->cb_batchedsnaps);
1032                 cb->cb_batchedsnaps = fnvlist_alloc();
1033 
1034                 if (error != 0 ||
1035                     zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 ||
1036                     zfs_destroy(zhp, cb->cb_defer_destroy) != 0) {
1037                         zfs_close(zhp);
1038                         return (-1);
1039                 }
1040         }
1041 
1042         zfs_close(zhp);
1043         return (0);
1044 }
1045 
1046 static int
1047 destroy_print_cb(zfs_handle_t *zhp, void *arg)
1048 {
1049         destroy_cbdata_t *cb = arg;
1050         const char *name = zfs_get_name(zhp);
1051         int err = 0;
1052 
1053         if (nvlist_exists(cb->cb_nvl, name)) {
1054                 if (cb->cb_firstsnap == NULL)
1055                         cb->cb_firstsnap = strdup(name);
1056                 if (cb->cb_prevsnap != NULL)
1057                         free(cb->cb_prevsnap);
1058                 /* this snap continues the current range */
1059                 cb->cb_prevsnap = strdup(name);
1060                 if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL)
1061                         nomem();
1062                 if (cb->cb_verbose) {
1063                         if (cb->cb_parsable) {
1064                                 (void) printf("destroy\t%s\n", name);
1065                         } else if (cb->cb_dryrun) {
1066                                 (void) printf(gettext("would destroy %s\n"),
1067                                     name);
1068                         } else {
1069                                 (void) printf(gettext("will destroy %s\n"),
1070                                     name);
1071                         }
1072                 }
1073         } else if (cb->cb_firstsnap != NULL) {
1074                 /* end of this range */
1075                 uint64_t used = 0;
1076                 err = lzc_snaprange_space(cb->cb_firstsnap,
1077                     cb->cb_prevsnap, &used);
1078                 cb->cb_snapused += used;
1079                 free(cb->cb_firstsnap);
1080                 cb->cb_firstsnap = NULL;
1081                 free(cb->cb_prevsnap);
1082                 cb->cb_prevsnap = NULL;
1083         }
1084         zfs_close(zhp);
1085         return (err);
1086 }
1087 
1088 static int
1089 destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb)
1090 {
1091         int err = 0;
1092         assert(cb->cb_firstsnap == NULL);
1093         assert(cb->cb_prevsnap == NULL);
1094         err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb);
1095         if (cb->cb_firstsnap != NULL) {
1096                 uint64_t used = 0;
1097                 if (err == 0) {
1098                         err = lzc_snaprange_space(cb->cb_firstsnap,
1099                             cb->cb_prevsnap, &used);
1100                 }
1101                 cb->cb_snapused += used;
1102                 free(cb->cb_firstsnap);
1103                 cb->cb_firstsnap = NULL;
1104                 free(cb->cb_prevsnap);
1105                 cb->cb_prevsnap = NULL;
1106         }
1107         return (err);
1108 }
1109 
1110 static int
1111 snapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg)
1112 {
1113         destroy_cbdata_t *cb = arg;
1114         int err = 0;
1115 
1116         /* Check for clones. */
1117         if (!cb->cb_doclones && !cb->cb_defer_destroy) {
1118                 cb->cb_target = zhp;
1119                 cb->cb_first = B_TRUE;
1120                 err = zfs_iter_dependents(zhp, B_TRUE,
1121                     destroy_check_dependent, cb);
1122         }
1123 
1124         if (err == 0) {
1125                 if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp)))
1126                         nomem();
1127         }
1128         zfs_close(zhp);
1129         return (err);
1130 }
1131 
1132 static int
1133 gather_snapshots(zfs_handle_t *zhp, void *arg)
1134 {
1135         destroy_cbdata_t *cb = arg;
1136         int err = 0;
1137 
1138         err = zfs_iter_snapspec(zhp, cb->cb_snapspec, snapshot_to_nvl_cb, cb);
1139         if (err == ENOENT)
1140                 err = 0;
1141         if (err != 0)
1142                 goto out;
1143 
1144         if (cb->cb_verbose) {
1145                 err = destroy_print_snapshots(zhp, cb);
1146                 if (err != 0)
1147                         goto out;
1148         }
1149 
1150         if (cb->cb_recurse)
1151                 err = zfs_iter_filesystems(zhp, gather_snapshots, cb);
1152 
1153 out:
1154         zfs_close(zhp);
1155         return (err);
1156 }
1157 
1158 static int
1159 destroy_clones(destroy_cbdata_t *cb)
1160 {
1161         nvpair_t *pair;
1162         for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL);
1163             pair != NULL;
1164             pair = nvlist_next_nvpair(cb->cb_nvl, pair)) {
1165                 zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair),
1166                     ZFS_TYPE_SNAPSHOT);
1167                 if (zhp != NULL) {
1168                         boolean_t defer = cb->cb_defer_destroy;
1169                         int err = 0;
1170 
1171                         /*
1172                          * We can't defer destroy non-snapshots, so set it to
1173                          * false while destroying the clones.
1174                          */
1175                         cb->cb_defer_destroy = B_FALSE;
1176                         err = zfs_iter_dependents(zhp, B_FALSE,
1177                             destroy_callback, cb);
1178                         cb->cb_defer_destroy = defer;
1179                         zfs_close(zhp);
1180                         if (err != 0)
1181                                 return (err);
1182                 }
1183         }
1184         return (0);
1185 }
1186 
1187 static int
1188 zfs_do_destroy(int argc, char **argv)
1189 {
1190         destroy_cbdata_t cb = { 0 };
1191         int rv = 0;
1192         int err = 0;
1193         int c;
1194         zfs_handle_t *zhp = NULL;
1195         char *at, *pound;
1196         zfs_type_t type = ZFS_TYPE_DATASET;
1197 
1198         /* check options */
1199         while ((c = getopt(argc, argv, "vpndfrR")) != -1) {
1200                 switch (c) {
1201                 case 'v':
1202                         cb.cb_verbose = B_TRUE;
1203                         break;
1204                 case 'p':
1205                         cb.cb_verbose = B_TRUE;
1206                         cb.cb_parsable = B_TRUE;
1207                         break;
1208                 case 'n':
1209                         cb.cb_dryrun = B_TRUE;
1210                         break;
1211                 case 'd':
1212                         cb.cb_defer_destroy = B_TRUE;
1213                         type = ZFS_TYPE_SNAPSHOT;
1214                         break;
1215                 case 'f':
1216                         cb.cb_force = B_TRUE;
1217                         break;
1218                 case 'r':
1219                         cb.cb_recurse = B_TRUE;
1220                         break;
1221                 case 'R':
1222                         cb.cb_recurse = B_TRUE;
1223                         cb.cb_doclones = B_TRUE;
1224                         break;
1225                 case '?':
1226                 default:
1227                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1228                             optopt);
1229                         usage(B_FALSE);
1230                 }
1231         }
1232 
1233         argc -= optind;
1234         argv += optind;
1235 
1236         /* check number of arguments */
1237         if (argc == 0) {
1238                 (void) fprintf(stderr, gettext("missing dataset argument\n"));
1239                 usage(B_FALSE);
1240         }
1241         if (argc > 1) {
1242                 (void) fprintf(stderr, gettext("too many arguments\n"));
1243                 usage(B_FALSE);
1244         }
1245 
1246         at = strchr(argv[0], '@');
1247         pound = strchr(argv[0], '#');
1248         if (at != NULL) {
1249 
1250                 /* Build the list of snaps to destroy in cb_nvl. */
1251                 cb.cb_nvl = fnvlist_alloc();
1252 
1253                 *at = '\0';
1254                 zhp = zfs_open(g_zfs, argv[0],
1255                     ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
1256                 if (zhp == NULL)
1257                         return (1);
1258 
1259                 cb.cb_snapspec = at + 1;
1260                 if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 ||
1261                     cb.cb_error) {
1262                         rv = 1;
1263                         goto out;
1264                 }
1265 
1266                 if (nvlist_empty(cb.cb_nvl)) {
1267                         (void) fprintf(stderr, gettext("could not find any "
1268                             "snapshots to destroy; check snapshot names.\n"));
1269                         rv = 1;
1270                         goto out;
1271                 }
1272 
1273                 if (cb.cb_verbose) {
1274                         char buf[16];
1275                         zfs_nicenum(cb.cb_snapused, buf, sizeof (buf));
1276                         if (cb.cb_parsable) {
1277                                 (void) printf("reclaim\t%llu\n",
1278                                     cb.cb_snapused);
1279                         } else if (cb.cb_dryrun) {
1280                                 (void) printf(gettext("would reclaim %s\n"),
1281                                     buf);
1282                         } else {
1283                                 (void) printf(gettext("will reclaim %s\n"),
1284                                     buf);
1285                         }
1286                 }
1287 
1288                 if (!cb.cb_dryrun) {
1289                         if (cb.cb_doclones) {
1290                                 cb.cb_batchedsnaps = fnvlist_alloc();
1291                                 err = destroy_clones(&cb);
1292                                 if (err == 0) {
1293                                         err = zfs_destroy_snaps_nvl(g_zfs,
1294                                             cb.cb_batchedsnaps, B_FALSE);
1295                                 }
1296                                 if (err != 0) {
1297                                         rv = 1;
1298                                         goto out;
1299                                 }
1300                         }
1301                         if (err == 0) {
1302                                 err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl,
1303                                     cb.cb_defer_destroy);
1304                         }
1305                 }
1306 
1307                 if (err != 0)
1308                         rv = 1;
1309         } else if (pound != NULL) {
1310                 int err;
1311                 nvlist_t *nvl;
1312 
1313                 if (cb.cb_dryrun) {
1314                         (void) fprintf(stderr,
1315                             "dryrun is not supported with bookmark\n");
1316                         return (-1);
1317                 }
1318 
1319                 if (cb.cb_defer_destroy) {
1320                         (void) fprintf(stderr,
1321                             "defer destroy is not supported with bookmark\n");
1322                         return (-1);
1323                 }
1324 
1325                 if (cb.cb_recurse) {
1326                         (void) fprintf(stderr,
1327                             "recursive is not supported with bookmark\n");
1328                         return (-1);
1329                 }
1330 
1331                 if (!zfs_bookmark_exists(argv[0])) {
1332                         (void) fprintf(stderr, gettext("bookmark '%s' "
1333                             "does not exist.\n"), argv[0]);
1334                         return (1);
1335                 }
1336 
1337                 nvl = fnvlist_alloc();
1338                 fnvlist_add_boolean(nvl, argv[0]);
1339 
1340                 err = lzc_destroy_bookmarks(nvl, NULL);
1341                 if (err != 0) {
1342                         (void) zfs_standard_error(g_zfs, err,
1343                             "cannot destroy bookmark");
1344                 }
1345 
1346                 nvlist_free(cb.cb_nvl);
1347 
1348                 return (err);
1349         } else {
1350                 /* Open the given dataset */
1351                 if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL)
1352                         return (1);
1353 
1354                 cb.cb_target = zhp;
1355 
1356                 /*
1357                  * Perform an explicit check for pools before going any further.
1358                  */
1359                 if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL &&
1360                     zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
1361                         (void) fprintf(stderr, gettext("cannot destroy '%s': "
1362                             "operation does not apply to pools\n"),
1363                             zfs_get_name(zhp));
1364                         (void) fprintf(stderr, gettext("use 'zfs destroy -r "
1365                             "%s' to destroy all datasets in the pool\n"),
1366                             zfs_get_name(zhp));
1367                         (void) fprintf(stderr, gettext("use 'zpool destroy %s' "
1368                             "to destroy the pool itself\n"), zfs_get_name(zhp));
1369                         rv = 1;
1370                         goto out;
1371                 }
1372 
1373                 /*
1374                  * Check for any dependents and/or clones.
1375                  */
1376                 cb.cb_first = B_TRUE;
1377                 if (!cb.cb_doclones &&
1378                     zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent,
1379                     &cb) != 0) {
1380                         rv = 1;
1381                         goto out;
1382                 }
1383 
1384                 if (cb.cb_error) {
1385                         rv = 1;
1386                         goto out;
1387                 }
1388 
1389                 cb.cb_batchedsnaps = fnvlist_alloc();
1390                 if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback,
1391                     &cb) != 0) {
1392                         rv = 1;
1393                         goto out;
1394                 }
1395 
1396                 /*
1397                  * Do the real thing.  The callback will close the
1398                  * handle regardless of whether it succeeds or not.
1399                  */
1400                 err = destroy_callback(zhp, &cb);
1401                 zhp = NULL;
1402                 if (err == 0) {
1403                         err = zfs_destroy_snaps_nvl(g_zfs,
1404                             cb.cb_batchedsnaps, cb.cb_defer_destroy);
1405                 }
1406                 if (err != 0)
1407                         rv = 1;
1408         }
1409 
1410 out:
1411         fnvlist_free(cb.cb_batchedsnaps);
1412         fnvlist_free(cb.cb_nvl);
1413         if (zhp != NULL)
1414                 zfs_close(zhp);
1415         return (rv);
1416 }
1417 
1418 static boolean_t
1419 is_recvd_column(zprop_get_cbdata_t *cbp)
1420 {
1421         int i;
1422         zfs_get_column_t col;
1423 
1424         for (i = 0; i < ZFS_GET_NCOLS &&
1425             (col = cbp->cb_columns[i]) != GET_COL_NONE; i++)
1426                 if (col == GET_COL_RECVD)
1427                         return (B_TRUE);
1428         return (B_FALSE);
1429 }
1430 
1431 /*
1432  * zfs get [-rHp] [-o all | field[,field]...] [-s source[,source]...]
1433  *      < all | property[,property]... > < fs | snap | vol > ...
1434  *
1435  *      -r      recurse over any child datasets
1436  *      -H      scripted mode.  Headers are stripped, and fields are separated
1437  *              by tabs instead of spaces.
1438  *      -o      Set of fields to display.  One of "name,property,value,
1439  *              received,source". Default is "name,property,value,source".
1440  *              "all" is an alias for all five.
1441  *      -s      Set of sources to allow.  One of
1442  *              "local,default,inherited,received,temporary,none".  Default is
1443  *              all six.
1444  *      -p      Display values in parsable (literal) format.
1445  *
1446  *  Prints properties for the given datasets.  The user can control which
1447  *  columns to display as well as which property types to allow.
1448  */
1449 
1450 /*
1451  * Invoked to display the properties for a single dataset.
1452  */
1453 static int
1454 get_callback(zfs_handle_t *zhp, void *data)
1455 {
1456         char buf[ZFS_MAXPROPLEN];
1457         char rbuf[ZFS_MAXPROPLEN];
1458         zprop_source_t sourcetype;
1459         char source[ZFS_MAXNAMELEN];
1460         zprop_get_cbdata_t *cbp = data;
1461         nvlist_t *user_props = zfs_get_user_props(zhp);
1462         zprop_list_t *pl = cbp->cb_proplist;
1463         nvlist_t *propval;
1464         char *strval;
1465         char *sourceval;
1466         boolean_t received = is_recvd_column(cbp);
1467 
1468         for (; pl != NULL; pl = pl->pl_next) {
1469                 char *recvdval = NULL;
1470                 /*
1471                  * Skip the special fake placeholder.  This will also skip over
1472                  * the name property when 'all' is specified.
1473                  */
1474                 if (pl->pl_prop == ZFS_PROP_NAME &&
1475                     pl == cbp->cb_proplist)
1476                         continue;
1477 
1478                 if (pl->pl_prop != ZPROP_INVAL) {
1479                         if (zfs_prop_get(zhp, pl->pl_prop, buf,
1480                             sizeof (buf), &sourcetype, source,
1481                             sizeof (source),
1482                             cbp->cb_literal) != 0) {
1483                                 if (pl->pl_all)
1484                                         continue;
1485                                 if (!zfs_prop_valid_for_type(pl->pl_prop,
1486                                     ZFS_TYPE_DATASET)) {
1487                                         (void) fprintf(stderr,
1488                                             gettext("No such property '%s'\n"),
1489                                             zfs_prop_to_name(pl->pl_prop));
1490                                         continue;
1491                                 }
1492                                 sourcetype = ZPROP_SRC_NONE;
1493                                 (void) strlcpy(buf, "-", sizeof (buf));
1494                         }
1495 
1496                         if (received && (zfs_prop_get_recvd(zhp,
1497                             zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf),
1498                             cbp->cb_literal) == 0))
1499                                 recvdval = rbuf;
1500 
1501                         zprop_print_one_property(zfs_get_name(zhp), cbp,
1502                             zfs_prop_to_name(pl->pl_prop),
1503                             buf, sourcetype, source, recvdval);
1504                 } else if (zfs_prop_userquota(pl->pl_user_prop)) {
1505                         sourcetype = ZPROP_SRC_LOCAL;
1506 
1507                         if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
1508                             buf, sizeof (buf), cbp->cb_literal) != 0) {
1509                                 sourcetype = ZPROP_SRC_NONE;
1510                                 (void) strlcpy(buf, "-", sizeof (buf));
1511                         }
1512 
1513                         zprop_print_one_property(zfs_get_name(zhp), cbp,
1514                             pl->pl_user_prop, buf, sourcetype, source, NULL);
1515                 } else if (zfs_prop_written(pl->pl_user_prop)) {
1516                         sourcetype = ZPROP_SRC_LOCAL;
1517 
1518                         if (zfs_prop_get_written(zhp, pl->pl_user_prop,
1519                             buf, sizeof (buf), cbp->cb_literal) != 0) {
1520                                 sourcetype = ZPROP_SRC_NONE;
1521                                 (void) strlcpy(buf, "-", sizeof (buf));
1522                         }
1523 
1524                         zprop_print_one_property(zfs_get_name(zhp), cbp,
1525                             pl->pl_user_prop, buf, sourcetype, source, NULL);
1526                 } else {
1527                         if (nvlist_lookup_nvlist(user_props,
1528                             pl->pl_user_prop, &propval) != 0) {
1529                                 if (pl->pl_all)
1530                                         continue;
1531                                 sourcetype = ZPROP_SRC_NONE;
1532                                 strval = "-";
1533                         } else {
1534                                 verify(nvlist_lookup_string(propval,
1535                                     ZPROP_VALUE, &strval) == 0);
1536                                 verify(nvlist_lookup_string(propval,
1537                                     ZPROP_SOURCE, &sourceval) == 0);
1538 
1539                                 if (strcmp(sourceval,
1540                                     zfs_get_name(zhp)) == 0) {
1541                                         sourcetype = ZPROP_SRC_LOCAL;
1542                                 } else if (strcmp(sourceval,
1543                                     ZPROP_SOURCE_VAL_RECVD) == 0) {
1544                                         sourcetype = ZPROP_SRC_RECEIVED;
1545                                 } else {
1546                                         sourcetype = ZPROP_SRC_INHERITED;
1547                                         (void) strlcpy(source,
1548                                             sourceval, sizeof (source));
1549                                 }
1550                         }
1551 
1552                         if (received && (zfs_prop_get_recvd(zhp,
1553                             pl->pl_user_prop, rbuf, sizeof (rbuf),
1554                             cbp->cb_literal) == 0))
1555                                 recvdval = rbuf;
1556 
1557                         zprop_print_one_property(zfs_get_name(zhp), cbp,
1558                             pl->pl_user_prop, strval, sourcetype,
1559                             source, recvdval);
1560                 }
1561         }
1562 
1563         return (0);
1564 }
1565 
1566 static int
1567 zfs_do_get(int argc, char **argv)
1568 {
1569         zprop_get_cbdata_t cb = { 0 };
1570         int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
1571         int types = ZFS_TYPE_DATASET;
1572         char *value, *fields;
1573         int ret = 0;
1574         int limit = 0;
1575         zprop_list_t fake_name = { 0 };
1576 
1577         /*
1578          * Set up default columns and sources.
1579          */
1580         cb.cb_sources = ZPROP_SRC_ALL;
1581         cb.cb_columns[0] = GET_COL_NAME;
1582         cb.cb_columns[1] = GET_COL_PROPERTY;
1583         cb.cb_columns[2] = GET_COL_VALUE;
1584         cb.cb_columns[3] = GET_COL_SOURCE;
1585         cb.cb_type = ZFS_TYPE_DATASET;
1586 
1587         /* check options */
1588         while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) {
1589                 switch (c) {
1590                 case 'p':
1591                         cb.cb_literal = B_TRUE;
1592                         break;
1593                 case 'd':
1594                         limit = parse_depth(optarg, &flags);
1595                         break;
1596                 case 'r':
1597                         flags |= ZFS_ITER_RECURSE;
1598                         break;
1599                 case 'H':
1600                         cb.cb_scripted = B_TRUE;
1601                         break;
1602                 case ':':
1603                         (void) fprintf(stderr, gettext("missing argument for "
1604                             "'%c' option\n"), optopt);
1605                         usage(B_FALSE);
1606                         break;
1607                 case 'o':
1608                         /*
1609                          * Process the set of columns to display.  We zero out
1610                          * the structure to give us a blank slate.
1611                          */
1612                         bzero(&cb.cb_columns, sizeof (cb.cb_columns));
1613                         i = 0;
1614                         while (*optarg != '\0') {
1615                                 static char *col_subopts[] =
1616                                     { "name", "property", "value", "received",
1617                                     "source", "all", NULL };
1618 
1619                                 if (i == ZFS_GET_NCOLS) {
1620                                         (void) fprintf(stderr, gettext("too "
1621                                             "many fields given to -o "
1622                                             "option\n"));
1623                                         usage(B_FALSE);
1624                                 }
1625 
1626                                 switch (getsubopt(&optarg, col_subopts,
1627                                     &value)) {
1628                                 case 0:
1629                                         cb.cb_columns[i++] = GET_COL_NAME;
1630                                         break;
1631                                 case 1:
1632                                         cb.cb_columns[i++] = GET_COL_PROPERTY;
1633                                         break;
1634                                 case 2:
1635                                         cb.cb_columns[i++] = GET_COL_VALUE;
1636                                         break;
1637                                 case 3:
1638                                         cb.cb_columns[i++] = GET_COL_RECVD;
1639                                         flags |= ZFS_ITER_RECVD_PROPS;
1640                                         break;
1641                                 case 4:
1642                                         cb.cb_columns[i++] = GET_COL_SOURCE;
1643                                         break;
1644                                 case 5:
1645                                         if (i > 0) {
1646                                                 (void) fprintf(stderr,
1647                                                     gettext("\"all\" conflicts "
1648                                                     "with specific fields "
1649                                                     "given to -o option\n"));
1650                                                 usage(B_FALSE);
1651                                         }
1652                                         cb.cb_columns[0] = GET_COL_NAME;
1653                                         cb.cb_columns[1] = GET_COL_PROPERTY;
1654                                         cb.cb_columns[2] = GET_COL_VALUE;
1655                                         cb.cb_columns[3] = GET_COL_RECVD;
1656                                         cb.cb_columns[4] = GET_COL_SOURCE;
1657                                         flags |= ZFS_ITER_RECVD_PROPS;
1658                                         i = ZFS_GET_NCOLS;
1659                                         break;
1660                                 default:
1661                                         (void) fprintf(stderr,
1662                                             gettext("invalid column name "
1663                                             "'%s'\n"), value);
1664                                         usage(B_FALSE);
1665                                 }
1666                         }
1667                         break;
1668 
1669                 case 's':
1670                         cb.cb_sources = 0;
1671                         while (*optarg != '\0') {
1672                                 static char *source_subopts[] = {
1673                                         "local", "default", "inherited",
1674                                         "received", "temporary", "none",
1675                                         NULL };
1676 
1677                                 switch (getsubopt(&optarg, source_subopts,
1678                                     &value)) {
1679                                 case 0:
1680                                         cb.cb_sources |= ZPROP_SRC_LOCAL;
1681                                         break;
1682                                 case 1:
1683                                         cb.cb_sources |= ZPROP_SRC_DEFAULT;
1684                                         break;
1685                                 case 2:
1686                                         cb.cb_sources |= ZPROP_SRC_INHERITED;
1687                                         break;
1688                                 case 3:
1689                                         cb.cb_sources |= ZPROP_SRC_RECEIVED;
1690                                         break;
1691                                 case 4:
1692                                         cb.cb_sources |= ZPROP_SRC_TEMPORARY;
1693                                         break;
1694                                 case 5:
1695                                         cb.cb_sources |= ZPROP_SRC_NONE;
1696                                         break;
1697                                 default:
1698                                         (void) fprintf(stderr,
1699                                             gettext("invalid source "
1700                                             "'%s'\n"), value);
1701                                         usage(B_FALSE);
1702                                 }
1703                         }
1704                         break;
1705 
1706                 case 't':
1707                         types = 0;
1708                         flags &= ~ZFS_ITER_PROP_LISTSNAPS;
1709                         while (*optarg != '\0') {
1710                                 static char *type_subopts[] = { "filesystem",
1711                                     "volume", "snapshot", "bookmark",
1712                                     "all", NULL };
1713 
1714                                 switch (getsubopt(&optarg, type_subopts,
1715                                     &value)) {
1716                                 case 0:
1717                                         types |= ZFS_TYPE_FILESYSTEM;
1718                                         break;
1719                                 case 1:
1720                                         types |= ZFS_TYPE_VOLUME;
1721                                         break;
1722                                 case 2:
1723                                         types |= ZFS_TYPE_SNAPSHOT;
1724                                         break;
1725                                 case 3:
1726                                         types |= ZFS_TYPE_BOOKMARK;
1727                                         break;
1728                                 case 4:
1729                                         types = ZFS_TYPE_DATASET |
1730                                             ZFS_TYPE_BOOKMARK;
1731                                         break;
1732 
1733                                 default:
1734                                         (void) fprintf(stderr,
1735                                             gettext("invalid type '%s'\n"),
1736                                             value);
1737                                         usage(B_FALSE);
1738                                 }
1739                         }
1740                         break;
1741 
1742                 case '?':
1743                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1744                             optopt);
1745                         usage(B_FALSE);
1746                 }
1747         }
1748 
1749         argc -= optind;
1750         argv += optind;
1751 
1752         if (argc < 1) {
1753                 (void) fprintf(stderr, gettext("missing property "
1754                     "argument\n"));
1755                 usage(B_FALSE);
1756         }
1757 
1758         fields = argv[0];
1759 
1760         if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
1761             != 0)
1762                 usage(B_FALSE);
1763 
1764         argc--;
1765         argv++;
1766 
1767         /*
1768          * As part of zfs_expand_proplist(), we keep track of the maximum column
1769          * width for each property.  For the 'NAME' (and 'SOURCE') columns, we
1770          * need to know the maximum name length.  However, the user likely did
1771          * not specify 'name' as one of the properties to fetch, so we need to
1772          * make sure we always include at least this property for
1773          * print_get_headers() to work properly.
1774          */
1775         if (cb.cb_proplist != NULL) {
1776                 fake_name.pl_prop = ZFS_PROP_NAME;
1777                 fake_name.pl_width = strlen(gettext("NAME"));
1778                 fake_name.pl_next = cb.cb_proplist;
1779                 cb.cb_proplist = &fake_name;
1780         }
1781 
1782         cb.cb_first = B_TRUE;
1783 
1784         /* run for each object */
1785         ret = zfs_for_each(argc, argv, flags, types, NULL,
1786             &cb.cb_proplist, limit, get_callback, &cb);
1787 
1788         if (cb.cb_proplist == &fake_name)
1789                 zprop_free_list(fake_name.pl_next);
1790         else
1791                 zprop_free_list(cb.cb_proplist);
1792 
1793         return (ret);
1794 }
1795 
1796 /*
1797  * inherit [-rS] <property> <fs|vol> ...
1798  *
1799  *      -r      Recurse over all children
1800  *      -S      Revert to received value, if any
1801  *
1802  * For each dataset specified on the command line, inherit the given property
1803  * from its parent.  Inheriting a property at the pool level will cause it to
1804  * use the default value.  The '-r' flag will recurse over all children, and is
1805  * useful for setting a property on a hierarchy-wide basis, regardless of any
1806  * local modifications for each dataset.
1807  */
1808 
1809 typedef struct inherit_cbdata {
1810         const char *cb_propname;
1811         boolean_t cb_received;
1812 } inherit_cbdata_t;
1813 
1814 static int
1815 inherit_recurse_cb(zfs_handle_t *zhp, void *data)
1816 {
1817         inherit_cbdata_t *cb = data;
1818         zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname);
1819 
1820         /*
1821          * If we're doing it recursively, then ignore properties that
1822          * are not valid for this type of dataset.
1823          */
1824         if (prop != ZPROP_INVAL &&
1825             !zfs_prop_valid_for_type(prop, zfs_get_type(zhp)))
1826                 return (0);
1827 
1828         return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
1829 }
1830 
1831 static int
1832 inherit_cb(zfs_handle_t *zhp, void *data)
1833 {
1834         inherit_cbdata_t *cb = data;
1835 
1836         return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
1837 }
1838 
1839 static int
1840 zfs_do_inherit(int argc, char **argv)
1841 {
1842         int c;
1843         zfs_prop_t prop;
1844         inherit_cbdata_t cb = { 0 };
1845         char *propname;
1846         int ret = 0;
1847         int flags = 0;
1848         boolean_t received = B_FALSE;
1849 
1850         /* check options */
1851         while ((c = getopt(argc, argv, "rS")) != -1) {
1852                 switch (c) {
1853                 case 'r':
1854                         flags |= ZFS_ITER_RECURSE;
1855                         break;
1856                 case 'S':
1857                         received = B_TRUE;
1858                         break;
1859                 case '?':
1860                 default:
1861                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
1862                             optopt);
1863                         usage(B_FALSE);
1864                 }
1865         }
1866 
1867         argc -= optind;
1868         argv += optind;
1869 
1870         /* check number of arguments */
1871         if (argc < 1) {
1872                 (void) fprintf(stderr, gettext("missing property argument\n"));
1873                 usage(B_FALSE);
1874         }
1875         if (argc < 2) {
1876                 (void) fprintf(stderr, gettext("missing dataset argument\n"));
1877                 usage(B_FALSE);
1878         }
1879 
1880         propname = argv[0];
1881         argc--;
1882         argv++;
1883 
1884         if ((prop = zfs_name_to_prop(propname)) != ZPROP_INVAL) {
1885                 if (zfs_prop_readonly(prop)) {
1886                         (void) fprintf(stderr, gettext(
1887                             "%s property is read-only\n"),
1888                             propname);
1889                         return (1);
1890                 }
1891                 if (!zfs_prop_inheritable(prop) && !received) {
1892                         (void) fprintf(stderr, gettext("'%s' property cannot "
1893                             "be inherited\n"), propname);
1894                         if (prop == ZFS_PROP_QUOTA ||
1895                             prop == ZFS_PROP_RESERVATION ||
1896                             prop == ZFS_PROP_REFQUOTA ||
1897                             prop == ZFS_PROP_REFRESERVATION) {
1898                                 (void) fprintf(stderr, gettext("use 'zfs set "
1899                                     "%s=none' to clear\n"), propname);
1900                                 (void) fprintf(stderr, gettext("use 'zfs "
1901                                     "inherit -S %s' to revert to received "
1902                                     "value\n"), propname);
1903                         }
1904                         return (1);
1905                 }
1906                 if (received && (prop == ZFS_PROP_VOLSIZE ||
1907                     prop == ZFS_PROP_VERSION)) {
1908                         (void) fprintf(stderr, gettext("'%s' property cannot "
1909                             "be reverted to a received value\n"), propname);
1910                         return (1);
1911                 }
1912         } else if (!zfs_prop_user(propname)) {
1913                 (void) fprintf(stderr, gettext("invalid property '%s'\n"),
1914                     propname);
1915                 usage(B_FALSE);
1916         }
1917 
1918         cb.cb_propname = propname;
1919         cb.cb_received = received;
1920 
1921         if (flags & ZFS_ITER_RECURSE) {
1922                 ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
1923                     NULL, NULL, 0, inherit_recurse_cb, &cb);
1924         } else {
1925                 ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
1926                     NULL, NULL, 0, inherit_cb, &cb);
1927         }
1928 
1929         return (ret);
1930 }
1931 
1932 typedef struct upgrade_cbdata {
1933         uint64_t cb_numupgraded;
1934         uint64_t cb_numsamegraded;
1935         uint64_t cb_numfailed;
1936         uint64_t cb_version;
1937         boolean_t cb_newer;
1938         boolean_t cb_foundone;
1939         char cb_lastfs[ZFS_MAXNAMELEN];
1940 } upgrade_cbdata_t;
1941 
1942 static int
1943 same_pool(zfs_handle_t *zhp, const char *name)
1944 {
1945         int len1 = strcspn(name, "/@");
1946         const char *zhname = zfs_get_name(zhp);
1947         int len2 = strcspn(zhname, "/@");
1948 
1949         if (len1 != len2)
1950                 return (B_FALSE);
1951         return (strncmp(name, zhname, len1) == 0);
1952 }
1953 
1954 static int
1955 upgrade_list_callback(zfs_handle_t *zhp, void *data)
1956 {
1957         upgrade_cbdata_t *cb = data;
1958         int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
1959 
1960         /* list if it's old/new */
1961         if ((!cb->cb_newer && version < ZPL_VERSION) ||
1962             (cb->cb_newer && version > ZPL_VERSION)) {
1963                 char *str;
1964                 if (cb->cb_newer) {
1965                         str = gettext("The following filesystems are "
1966                             "formatted using a newer software version and\n"
1967                             "cannot be accessed on the current system.\n\n");
1968                 } else {
1969                         str = gettext("The following filesystems are "
1970                             "out of date, and can be upgraded.  After being\n"
1971                             "upgraded, these filesystems (and any 'zfs send' "
1972                             "streams generated from\n"
1973                             "subsequent snapshots) will no longer be "
1974                             "accessible by older software versions.\n\n");
1975                 }
1976 
1977                 if (!cb->cb_foundone) {
1978                         (void) puts(str);
1979                         (void) printf(gettext("VER  FILESYSTEM\n"));
1980                         (void) printf(gettext("---  ------------\n"));
1981                         cb->cb_foundone = B_TRUE;
1982                 }
1983 
1984                 (void) printf("%2u   %s\n", version, zfs_get_name(zhp));
1985         }
1986 
1987         return (0);
1988 }
1989 
1990 static int
1991 upgrade_set_callback(zfs_handle_t *zhp, void *data)
1992 {
1993         upgrade_cbdata_t *cb = data;
1994         int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
1995         int needed_spa_version;
1996         int spa_version;
1997 
1998         if (zfs_spa_version(zhp, &spa_version) < 0)
1999                 return (-1);
2000 
2001         needed_spa_version = zfs_spa_version_map(cb->cb_version);
2002 
2003         if (needed_spa_version < 0)
2004                 return (-1);
2005 
2006         if (spa_version < needed_spa_version) {
2007                 /* can't upgrade */
2008                 (void) printf(gettext("%s: can not be "
2009                     "upgraded; the pool version needs to first "
2010                     "be upgraded\nto version %d\n\n"),
2011                     zfs_get_name(zhp), needed_spa_version);
2012                 cb->cb_numfailed++;
2013                 return (0);
2014         }
2015 
2016         /* upgrade */
2017         if (version < cb->cb_version) {
2018                 char verstr[16];
2019                 (void) snprintf(verstr, sizeof (verstr),
2020                     "%llu", cb->cb_version);
2021                 if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {
2022                         /*
2023                          * If they did "zfs upgrade -a", then we could
2024                          * be doing ioctls to different pools.  We need
2025                          * to log this history once to each pool, and bypass
2026                          * the normal history logging that happens in main().
2027                          */
2028                         (void) zpool_log_history(g_zfs, history_str);
2029                         log_history = B_FALSE;
2030                 }
2031                 if (zfs_prop_set(zhp, "version", verstr) == 0)
2032                         cb->cb_numupgraded++;
2033                 else
2034                         cb->cb_numfailed++;
2035                 (void) strcpy(cb->cb_lastfs, zfs_get_name(zhp));
2036         } else if (version > cb->cb_version) {
2037                 /* can't downgrade */
2038                 (void) printf(gettext("%s: can not be downgraded; "
2039                     "it is already at version %u\n"),
2040                     zfs_get_name(zhp), version);
2041                 cb->cb_numfailed++;
2042         } else {
2043                 cb->cb_numsamegraded++;
2044         }
2045         return (0);
2046 }
2047 
2048 /*
2049  * zfs upgrade
2050  * zfs upgrade -v
2051  * zfs upgrade [-r] [-V <version>] <-a | filesystem>
2052  */
2053 static int
2054 zfs_do_upgrade(int argc, char **argv)
2055 {
2056         boolean_t all = B_FALSE;
2057         boolean_t showversions = B_FALSE;
2058         int ret = 0;
2059         upgrade_cbdata_t cb = { 0 };
2060         char c;
2061         int flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
2062 
2063         /* check options */
2064         while ((c = getopt(argc, argv, "rvV:a")) != -1) {
2065                 switch (c) {
2066                 case 'r':
2067                         flags |= ZFS_ITER_RECURSE;
2068                         break;
2069                 case 'v':
2070                         showversions = B_TRUE;
2071                         break;
2072                 case 'V':
2073                         if (zfs_prop_string_to_index(ZFS_PROP_VERSION,
2074                             optarg, &cb.cb_version) != 0) {
2075                                 (void) fprintf(stderr,
2076                                     gettext("invalid version %s\n"), optarg);
2077                                 usage(B_FALSE);
2078                         }
2079                         break;
2080                 case 'a':
2081                         all = B_TRUE;
2082                         break;
2083                 case '?':
2084                 default:
2085                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
2086                             optopt);
2087                         usage(B_FALSE);
2088                 }
2089         }
2090 
2091         argc -= optind;
2092         argv += optind;
2093 
2094         if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version))
2095                 usage(B_FALSE);
2096         if (showversions && (flags & ZFS_ITER_RECURSE || all ||
2097             cb.cb_version || argc))
2098                 usage(B_FALSE);
2099         if ((all || argc) && (showversions))
2100                 usage(B_FALSE);
2101         if (all && argc)
2102                 usage(B_FALSE);
2103 
2104         if (showversions) {
2105                 /* Show info on available versions. */
2106                 (void) printf(gettext("The following filesystem versions are "
2107                     "supported:\n\n"));
2108                 (void) printf(gettext("VER  DESCRIPTION\n"));
2109                 (void) printf("---  -----------------------------------------"
2110                     "---------------\n");
2111                 (void) printf(gettext(" 1   Initial ZFS filesystem version\n"));
2112                 (void) printf(gettext(" 2   Enhanced directory entries\n"));
2113                 (void) printf(gettext(" 3   Case insensitive and filesystem "
2114                     "user identifier (FUID)\n"));
2115                 (void) printf(gettext(" 4   userquota, groupquota "
2116                     "properties\n"));
2117                 (void) printf(gettext(" 5   System attributes\n"));
2118                 (void) printf(gettext("\nFor more information on a particular "
2119                     "version, including supported releases,\n"));
2120                 (void) printf("see the ZFS Administration Guide.\n\n");
2121                 ret = 0;
2122         } else if (argc || all) {
2123                 /* Upgrade filesystems */
2124                 if (cb.cb_version == 0)
2125                         cb.cb_version = ZPL_VERSION;
2126                 ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM,
2127                     NULL, NULL, 0, upgrade_set_callback, &cb);
2128                 (void) printf(gettext("%llu filesystems upgraded\n"),
2129                     cb.cb_numupgraded);
2130                 if (cb.cb_numsamegraded) {
2131                         (void) printf(gettext("%llu filesystems already at "
2132                             "this version\n"),
2133                             cb.cb_numsamegraded);
2134                 }
2135                 if (cb.cb_numfailed != 0)
2136                         ret = 1;
2137         } else {
2138                 /* List old-version filesytems */
2139                 boolean_t found;
2140                 (void) printf(gettext("This system is currently running "
2141                     "ZFS filesystem version %llu.\n\n"), ZPL_VERSION);
2142 
2143                 flags |= ZFS_ITER_RECURSE;
2144                 ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
2145                     NULL, NULL, 0, upgrade_list_callback, &cb);
2146 
2147                 found = cb.cb_foundone;
2148                 cb.cb_foundone = B_FALSE;
2149                 cb.cb_newer = B_TRUE;
2150 
2151                 ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
2152                     NULL, NULL, 0, upgrade_list_callback, &cb);
2153 
2154                 if (!cb.cb_foundone && !found) {
2155                         (void) printf(gettext("All filesystems are "
2156                             "formatted with the current version.\n"));
2157                 }
2158         }
2159 
2160         return (ret);
2161 }
2162 
2163 /*
2164  * zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
2165  *               [-S field [-S field]...] [-t type[,...]] filesystem | snapshot
2166  * zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
2167  *                [-S field [-S field]...] [-t type[,...]] filesystem | snapshot
2168  *
2169  *      -H      Scripted mode; elide headers and separate columns by tabs.
2170  *      -i      Translate SID to POSIX ID.
2171  *      -n      Print numeric ID instead of user/group name.
2172  *      -o      Control which fields to display.
2173  *      -p      Use exact (parsable) numeric output.
2174  *      -s      Specify sort columns, descending order.
2175  *      -S      Specify sort columns, ascending order.
2176  *      -t      Control which object types to display.
2177  *
2178  *      Displays space consumed by, and quotas on, each user in the specified
2179  *      filesystem or snapshot.
2180  */
2181 
2182 /* us_field_types, us_field_hdr and us_field_names should be kept in sync */
2183 enum us_field_types {
2184         USFIELD_TYPE,
2185         USFIELD_NAME,
2186         USFIELD_USED,
2187         USFIELD_QUOTA
2188 };
2189 static char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA" };
2190 static char *us_field_names[] = { "type", "name", "used", "quota" };
2191 #define USFIELD_LAST    (sizeof (us_field_names) / sizeof (char *))
2192 
2193 #define USTYPE_PSX_GRP  (1 << 0)
2194 #define USTYPE_PSX_USR  (1 << 1)
2195 #define USTYPE_SMB_GRP  (1 << 2)
2196 #define USTYPE_SMB_USR  (1 << 3)
2197 #define USTYPE_ALL      \
2198         (USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR)
2199 
2200 static int us_type_bits[] = {
2201         USTYPE_PSX_GRP,
2202         USTYPE_PSX_USR,
2203         USTYPE_SMB_GRP,
2204         USTYPE_SMB_USR,
2205         USTYPE_ALL
2206 };
2207 static char *us_type_names[] = { "posixgroup", "posixuser", "smbgroup",
2208         "smbuser", "all" };
2209 
2210 typedef struct us_node {
2211         nvlist_t        *usn_nvl;
2212         uu_avl_node_t   usn_avlnode;
2213         uu_list_node_t  usn_listnode;
2214 } us_node_t;
2215 
2216 typedef struct us_cbdata {
2217         nvlist_t        **cb_nvlp;
2218         uu_avl_pool_t   *cb_avl_pool;
2219         uu_avl_t        *cb_avl;
2220         boolean_t       cb_numname;
2221         boolean_t       cb_nicenum;
2222         boolean_t       cb_sid2posix;
2223         zfs_userquota_prop_t cb_prop;
2224         zfs_sort_column_t *cb_sortcol;
2225         size_t          cb_width[USFIELD_LAST];
2226 } us_cbdata_t;
2227 
2228 static boolean_t us_populated = B_FALSE;
2229 
2230 typedef struct {
2231         zfs_sort_column_t *si_sortcol;
2232         boolean_t       si_numname;
2233 } us_sort_info_t;
2234 
2235 static int
2236 us_field_index(char *field)
2237 {
2238         int i;
2239 
2240         for (i = 0; i < USFIELD_LAST; i++) {
2241                 if (strcmp(field, us_field_names[i]) == 0)
2242                         return (i);
2243         }
2244 
2245         return (-1);
2246 }
2247 
2248 static int
2249 us_compare(const void *larg, const void *rarg, void *unused)
2250 {
2251         const us_node_t *l = larg;
2252         const us_node_t *r = rarg;
2253         us_sort_info_t *si = (us_sort_info_t *)unused;
2254         zfs_sort_column_t *sortcol = si->si_sortcol;
2255         boolean_t numname = si->si_numname;
2256         nvlist_t *lnvl = l->usn_nvl;
2257         nvlist_t *rnvl = r->usn_nvl;
2258         int rc = 0;
2259         boolean_t lvb, rvb;
2260 
2261         for (; sortcol != NULL; sortcol = sortcol->sc_next) {
2262                 char *lvstr = "";
2263                 char *rvstr = "";
2264                 uint32_t lv32 = 0;
2265                 uint32_t rv32 = 0;
2266                 uint64_t lv64 = 0;
2267                 uint64_t rv64 = 0;
2268                 zfs_prop_t prop = sortcol->sc_prop;
2269                 const char *propname = NULL;
2270                 boolean_t reverse = sortcol->sc_reverse;
2271 
2272                 switch (prop) {
2273                 case ZFS_PROP_TYPE:
2274                         propname = "type";
2275                         (void) nvlist_lookup_uint32(lnvl, propname, &lv32);
2276                         (void) nvlist_lookup_uint32(rnvl, propname, &rv32);
2277                         if (rv32 != lv32)
2278                                 rc = (rv32 < lv32) ? 1 : -1;
2279                         break;
2280                 case ZFS_PROP_NAME:
2281                         propname = "name";
2282                         if (numname) {
2283                                 (void) nvlist_lookup_uint64(lnvl, propname,
2284                                     &lv64);
2285                                 (void) nvlist_lookup_uint64(rnvl, propname,
2286                                     &rv64);
2287                                 if (rv64 != lv64)
2288                                         rc = (rv64 < lv64) ? 1 : -1;
2289                         } else {
2290                                 (void) nvlist_lookup_string(lnvl, propname,
2291                                     &lvstr);
2292                                 (void) nvlist_lookup_string(rnvl, propname,
2293                                     &rvstr);
2294                                 rc = strcmp(lvstr, rvstr);
2295                         }
2296                         break;
2297                 case ZFS_PROP_USED:
2298                 case ZFS_PROP_QUOTA:
2299                         if (!us_populated)
2300                                 break;
2301                         if (prop == ZFS_PROP_USED)
2302                                 propname = "used";
2303                         else
2304                                 propname = "quota";
2305                         (void) nvlist_lookup_uint64(lnvl, propname, &lv64);
2306                         (void) nvlist_lookup_uint64(rnvl, propname, &rv64);
2307                         if (rv64 != lv64)
2308                                 rc = (rv64 < lv64) ? 1 : -1;
2309                         break;
2310                 }
2311 
2312                 if (rc != 0) {
2313                         if (rc < 0)
2314                                 return (reverse ? 1 : -1);
2315                         else
2316                                 return (reverse ? -1 : 1);
2317                 }
2318         }
2319 
2320         /*
2321          * If entries still seem to be the same, check if they are of the same
2322          * type (smbentity is added only if we are doing SID to POSIX ID
2323          * translation where we can have duplicate type/name combinations).
2324          */
2325         if (nvlist_lookup_boolean_value(lnvl, "smbentity", &lvb) == 0 &&
2326             nvlist_lookup_boolean_value(rnvl, "smbentity", &rvb) == 0 &&
2327             lvb != rvb)
2328                 return (lvb < rvb ? -1 : 1);
2329 
2330         return (0);
2331 }
2332 
2333 static inline const char *
2334 us_type2str(unsigned field_type)
2335 {
2336         switch (field_type) {
2337         case USTYPE_PSX_USR:
2338                 return ("POSIX User");
2339         case USTYPE_PSX_GRP:
2340                 return ("POSIX Group");
2341         case USTYPE_SMB_USR:
2342                 return ("SMB User");
2343         case USTYPE_SMB_GRP:
2344                 return ("SMB Group");
2345         default:
2346                 return ("Undefined");
2347         }
2348 }
2349 
2350 static int
2351 userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space)
2352 {
2353         us_cbdata_t *cb = (us_cbdata_t *)arg;
2354         zfs_userquota_prop_t prop = cb->cb_prop;
2355         char *name = NULL;
2356         char *propname;
2357         char sizebuf[32];
2358         us_node_t *node;
2359         uu_avl_pool_t *avl_pool = cb->cb_avl_pool;
2360         uu_avl_t *avl = cb->cb_avl;
2361         uu_avl_index_t idx;
2362         nvlist_t *props;
2363         us_node_t *n;
2364         zfs_sort_column_t *sortcol = cb->cb_sortcol;
2365         unsigned type;
2366         const char *typestr;
2367         size_t namelen;
2368         size_t typelen;
2369         size_t sizelen;
2370         int typeidx, nameidx, sizeidx;
2371         us_sort_info_t sortinfo = { sortcol, cb->cb_numname };
2372         boolean_t smbentity = B_FALSE;
2373 
2374         if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
2375                 nomem();
2376         node = safe_malloc(sizeof (us_node_t));
2377         uu_avl_node_init(node, &node->usn_avlnode, avl_pool);
2378         node->usn_nvl = props;
2379 
2380         if (domain != NULL && domain[0] != '\0') {
2381                 /* SMB */
2382                 char sid[ZFS_MAXNAMELEN + 32];
2383                 uid_t id;
2384                 int err;
2385                 int flag = IDMAP_REQ_FLG_USE_CACHE;
2386 
2387                 smbentity = B_TRUE;
2388 
2389                 (void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid);
2390 
2391                 if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {
2392                         type = USTYPE_SMB_GRP;
2393                         err = sid_to_id(sid, B_FALSE, &id);
2394                 } else {
2395                         type = USTYPE_SMB_USR;
2396                         err = sid_to_id(sid, B_TRUE, &id);
2397                 }
2398 
2399                 if (err == 0) {
2400                         rid = id;
2401                         if (!cb->cb_sid2posix) {
2402                                 if (type == USTYPE_SMB_USR) {
2403                                         (void) idmap_getwinnamebyuid(rid, flag,
2404                                             &name, NULL);
2405                                 } else {
2406                                         (void) idmap_getwinnamebygid(rid, flag,
2407                                             &name, NULL);
2408                                 }
2409                                 if (name == NULL)
2410                                         name = sid;
2411                         }
2412                 }
2413         }
2414 
2415         if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') {
2416                 /* POSIX or -i */
2417                 if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {
2418                         type = USTYPE_PSX_GRP;
2419                         if (!cb->cb_numname) {
2420                                 struct group *g;
2421 
2422                                 if ((g = getgrgid(rid)) != NULL)
2423                                         name = g->gr_name;
2424                         }
2425                 } else {
2426                         type = USTYPE_PSX_USR;
2427                         if (!cb->cb_numname) {
2428                                 struct passwd *p;
2429 
2430                                 if ((p = getpwuid(rid)) != NULL)
2431                                         name = p->pw_name;
2432                         }
2433                 }
2434         }
2435 
2436         /*
2437          * Make sure that the type/name combination is unique when doing
2438          * SID to POSIX ID translation (hence changing the type from SMB to
2439          * POSIX).
2440          */
2441         if (cb->cb_sid2posix &&
2442             nvlist_add_boolean_value(props, "smbentity", smbentity) != 0)
2443                 nomem();
2444 
2445         /* Calculate/update width of TYPE field */
2446         typestr = us_type2str(type);
2447         typelen = strlen(gettext(typestr));
2448         typeidx = us_field_index("type");
2449         if (typelen > cb->cb_width[typeidx])
2450                 cb->cb_width[typeidx] = typelen;
2451         if (nvlist_add_uint32(props, "type", type) != 0)
2452                 nomem();
2453 
2454         /* Calculate/update width of NAME field */
2455         if ((cb->cb_numname && cb->cb_sid2posix) || name == NULL) {
2456                 if (nvlist_add_uint64(props, "name", rid) != 0)
2457                         nomem();
2458                 namelen = snprintf(NULL, 0, "%u", rid);
2459         } else {
2460                 if (nvlist_add_string(props, "name", name) != 0)
2461                         nomem();
2462                 namelen = strlen(name);
2463         }
2464         nameidx = us_field_index("name");
2465         if (namelen > cb->cb_width[nameidx])
2466                 cb->cb_width[nameidx] = namelen;
2467 
2468         /*
2469          * Check if this type/name combination is in the list and update it;
2470          * otherwise add new node to the list.
2471          */
2472         if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) {
2473                 uu_avl_insert(avl, node, idx);
2474         } else {
2475                 nvlist_free(props);
2476                 free(node);
2477                 node = n;
2478                 props = node->usn_nvl;
2479         }
2480 
2481         /* Calculate/update width of USED/QUOTA fields */
2482         if (cb->cb_nicenum)
2483                 zfs_nicenum(space, sizebuf, sizeof (sizebuf));
2484         else
2485                 (void) snprintf(sizebuf, sizeof (sizebuf), "%llu", space);
2486         sizelen = strlen(sizebuf);
2487         if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED) {
2488                 propname = "used";
2489                 if (!nvlist_exists(props, "quota"))
2490                         (void) nvlist_add_uint64(props, "quota", 0);
2491         } else {
2492                 propname = "quota";
2493                 if (!nvlist_exists(props, "used"))
2494                         (void) nvlist_add_uint64(props, "used", 0);
2495         }
2496         sizeidx = us_field_index(propname);
2497         if (sizelen > cb->cb_width[sizeidx])
2498                 cb->cb_width[sizeidx] = sizelen;
2499 
2500         if (nvlist_add_uint64(props, propname, space) != 0)
2501                 nomem();
2502 
2503         return (0);
2504 }
2505 
2506 static void
2507 print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types,
2508     size_t *width, us_node_t *node)
2509 {
2510         nvlist_t *nvl = node->usn_nvl;
2511         char valstr[ZFS_MAXNAMELEN];
2512         boolean_t first = B_TRUE;
2513         int cfield = 0;
2514         int field;
2515         uint32_t ustype;
2516 
2517         /* Check type */
2518         (void) nvlist_lookup_uint32(nvl, "type", &ustype);
2519         if (!(ustype & types))
2520                 return;
2521 
2522         while ((field = fields[cfield]) != USFIELD_LAST) {
2523                 nvpair_t *nvp = NULL;
2524                 data_type_t type;
2525                 uint32_t val32;
2526                 uint64_t val64;
2527                 char *strval = NULL;
2528 
2529                 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
2530                         if (strcmp(nvpair_name(nvp),
2531                             us_field_names[field]) == 0)
2532                                 break;
2533                 }
2534 
2535                 type = nvpair_type(nvp);
2536                 switch (type) {
2537                 case DATA_TYPE_UINT32:
2538                         (void) nvpair_value_uint32(nvp, &val32);
2539                         break;
2540                 case DATA_TYPE_UINT64:
2541                         (void) nvpair_value_uint64(nvp, &val64);
2542                         break;
2543                 case DATA_TYPE_STRING:
2544                         (void) nvpair_value_string(nvp, &strval);
2545                         break;
2546                 default:
2547                         (void) fprintf(stderr, "invalid data type\n");
2548                 }
2549 
2550                 switch (field) {
2551                 case USFIELD_TYPE:
2552                         strval = (char *)us_type2str(val32);
2553                         break;
2554                 case USFIELD_NAME:
2555                         if (type == DATA_TYPE_UINT64) {
2556                                 (void) sprintf(valstr, "%llu", val64);
2557                                 strval = valstr;
2558                         }
2559                         break;
2560                 case USFIELD_USED:
2561                 case USFIELD_QUOTA:
2562                         if (type == DATA_TYPE_UINT64) {
2563                                 if (parsable) {
2564                                         (void) sprintf(valstr, "%llu", val64);
2565                                 } else {
2566                                         zfs_nicenum(val64, valstr,
2567                                             sizeof (valstr));
2568                                 }
2569                                 if (field == USFIELD_QUOTA &&
2570                                     strcmp(valstr, "0") == 0)
2571                                         strval = "none";
2572                                 else
2573                                         strval = valstr;
2574                         }
2575                         break;
2576                 }
2577 
2578                 if (!first) {
2579                         if (scripted)
2580                                 (void) printf("\t");
2581                         else
2582                                 (void) printf("  ");
2583                 }
2584                 if (scripted)
2585                         (void) printf("%s", strval);
2586                 else if (field == USFIELD_TYPE || field == USFIELD_NAME)
2587                         (void) printf("%-*s", width[field], strval);
2588                 else
2589                         (void) printf("%*s", width[field], strval);
2590 
2591                 first = B_FALSE;
2592                 cfield++;
2593         }
2594 
2595         (void) printf("\n");
2596 }
2597 
2598 static void
2599 print_us(boolean_t scripted, boolean_t parsable, int *fields, int types,
2600     size_t *width, boolean_t rmnode, uu_avl_t *avl)
2601 {
2602         us_node_t *node;
2603         const char *col;
2604         int cfield = 0;
2605         int field;
2606 
2607         if (!scripted) {
2608                 boolean_t first = B_TRUE;
2609 
2610                 while ((field = fields[cfield]) != USFIELD_LAST) {
2611                         col = gettext(us_field_hdr[field]);
2612                         if (field == USFIELD_TYPE || field == USFIELD_NAME) {
2613                                 (void) printf(first ? "%-*s" : "  %-*s",
2614                                     width[field], col);
2615                         } else {
2616                                 (void) printf(first ? "%*s" : "  %*s",
2617                                     width[field], col);
2618                         }
2619                         first = B_FALSE;
2620                         cfield++;
2621                 }
2622                 (void) printf("\n");
2623         }
2624 
2625         for (node = uu_avl_first(avl); node; node = uu_avl_next(avl, node)) {
2626                 print_us_node(scripted, parsable, fields, types, width, node);
2627                 if (rmnode)
2628                         nvlist_free(node->usn_nvl);
2629         }
2630 }
2631 
2632 static int
2633 zfs_do_userspace(int argc, char **argv)
2634 {
2635         zfs_handle_t *zhp;
2636         zfs_userquota_prop_t p;
2637         uu_avl_pool_t *avl_pool;
2638         uu_avl_t *avl_tree;
2639         uu_avl_walk_t *walk;
2640         char *delim;
2641         char deffields[] = "type,name,used,quota";
2642         char *ofield = NULL;
2643         char *tfield = NULL;
2644         int cfield = 0;
2645         int fields[256];
2646         int i;
2647         boolean_t scripted = B_FALSE;
2648         boolean_t prtnum = B_FALSE;
2649         boolean_t parsable = B_FALSE;
2650         boolean_t sid2posix = B_FALSE;
2651         int ret = 0;
2652         int c;
2653         zfs_sort_column_t *sortcol = NULL;
2654         int types = USTYPE_PSX_USR | USTYPE_SMB_USR;
2655         us_cbdata_t cb;
2656         us_node_t *node;
2657         us_node_t *rmnode;
2658         uu_list_pool_t *listpool;
2659         uu_list_t *list;
2660         uu_avl_index_t idx = 0;
2661         uu_list_index_t idx2 = 0;
2662 
2663         if (argc < 2)
2664                 usage(B_FALSE);
2665 
2666         if (strcmp(argv[0], "groupspace") == 0)
2667                 /* Toggle default group types */
2668                 types = USTYPE_PSX_GRP | USTYPE_SMB_GRP;
2669 
2670         while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) {
2671                 switch (c) {
2672                 case 'n':
2673                         prtnum = B_TRUE;
2674                         break;
2675                 case 'H':
2676                         scripted = B_TRUE;
2677                         break;
2678                 case 'p':
2679                         parsable = B_TRUE;
2680                         break;
2681                 case 'o':
2682                         ofield = optarg;
2683                         break;
2684                 case 's':
2685                 case 'S':
2686                         if (zfs_add_sort_column(&sortcol, optarg,
2687                             c == 's' ? B_FALSE : B_TRUE) != 0) {
2688                                 (void) fprintf(stderr,
2689                                     gettext("invalid field '%s'\n"), optarg);
2690                                 usage(B_FALSE);
2691                         }
2692                         break;
2693                 case 't':
2694                         tfield = optarg;
2695                         break;
2696                 case 'i':
2697                         sid2posix = B_TRUE;
2698                         break;
2699                 case ':':
2700                         (void) fprintf(stderr, gettext("missing argument for "
2701                             "'%c' option\n"), optopt);
2702                         usage(B_FALSE);
2703                         break;
2704                 case '?':
2705                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
2706                             optopt);
2707                         usage(B_FALSE);
2708                 }
2709         }
2710 
2711         argc -= optind;
2712         argv += optind;
2713 
2714         if (argc < 1) {
2715                 (void) fprintf(stderr, gettext("missing dataset name\n"));
2716                 usage(B_FALSE);
2717         }
2718         if (argc > 1) {
2719                 (void) fprintf(stderr, gettext("too many arguments\n"));
2720                 usage(B_FALSE);
2721         }
2722 
2723         /* Use default output fields if not specified using -o */
2724         if (ofield == NULL)
2725                 ofield = deffields;
2726         do {
2727                 if ((delim = strchr(ofield, ',')) != NULL)
2728                         *delim = '\0';
2729                 if ((fields[cfield++] = us_field_index(ofield)) == -1) {
2730                         (void) fprintf(stderr, gettext("invalid type '%s' "
2731                             "for -o option\n"), ofield);
2732                         return (-1);
2733                 }
2734                 if (delim != NULL)
2735                         ofield = delim + 1;
2736         } while (delim != NULL);
2737         fields[cfield] = USFIELD_LAST;
2738 
2739         /* Override output types (-t option) */
2740         if (tfield != NULL) {
2741                 types = 0;
2742 
2743                 do {
2744                         boolean_t found = B_FALSE;
2745 
2746                         if ((delim = strchr(tfield, ',')) != NULL)
2747                                 *delim = '\0';
2748                         for (i = 0; i < sizeof (us_type_bits) / sizeof (int);
2749                             i++) {
2750                                 if (strcmp(tfield, us_type_names[i]) == 0) {
2751                                         found = B_TRUE;
2752                                         types |= us_type_bits[i];
2753                                         break;
2754                                 }
2755                         }
2756                         if (!found) {
2757                                 (void) fprintf(stderr, gettext("invalid type "
2758                                     "'%s' for -t option\n"), tfield);
2759                                 return (-1);
2760                         }
2761                         if (delim != NULL)
2762                                 tfield = delim + 1;
2763                 } while (delim != NULL);
2764         }
2765 
2766         if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
2767                 return (1);
2768 
2769         if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t),
2770             offsetof(us_node_t, usn_avlnode), us_compare, UU_DEFAULT)) == NULL)
2771                 nomem();
2772         if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL)
2773                 nomem();
2774 
2775         /* Always add default sorting columns */
2776         (void) zfs_add_sort_column(&sortcol, "type", B_FALSE);
2777         (void) zfs_add_sort_column(&sortcol, "name", B_FALSE);
2778 
2779         cb.cb_sortcol = sortcol;
2780         cb.cb_numname = prtnum;
2781         cb.cb_nicenum = !parsable;
2782         cb.cb_avl_pool = avl_pool;
2783         cb.cb_avl = avl_tree;
2784         cb.cb_sid2posix = sid2posix;
2785 
2786         for (i = 0; i < USFIELD_LAST; i++)
2787                 cb.cb_width[i] = strlen(gettext(us_field_hdr[i]));
2788 
2789         for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) {
2790                 if (((p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA) &&
2791                     !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) ||
2792                     ((p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) &&
2793                     !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))))
2794                         continue;
2795                 cb.cb_prop = p;
2796                 if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0)
2797                         return (ret);
2798         }
2799 
2800         /* Sort the list */
2801         if ((node = uu_avl_first(avl_tree)) == NULL)
2802                 return (0);
2803 
2804         us_populated = B_TRUE;
2805 
2806         listpool = uu_list_pool_create("tmplist", sizeof (us_node_t),
2807             offsetof(us_node_t, usn_listnode), NULL, UU_DEFAULT);
2808         list = uu_list_create(listpool, NULL, UU_DEFAULT);
2809         uu_list_node_init(node, &node->usn_listnode, listpool);
2810 
2811         while (node != NULL) {
2812                 rmnode = node;
2813                 node = uu_avl_next(avl_tree, node);
2814                 uu_avl_remove(avl_tree, rmnode);
2815                 if (uu_list_find(list, rmnode, NULL, &idx2) == NULL)
2816                         uu_list_insert(list, rmnode, idx2);
2817         }
2818 
2819         for (node = uu_list_first(list); node != NULL;
2820             node = uu_list_next(list, node)) {
2821                 us_sort_info_t sortinfo = { sortcol, cb.cb_numname };
2822 
2823                 if (uu_avl_find(avl_tree, node, &sortinfo, &idx) == NULL)
2824                         uu_avl_insert(avl_tree, node, idx);
2825         }
2826 
2827         uu_list_destroy(list);
2828         uu_list_pool_destroy(listpool);
2829 
2830         /* Print and free node nvlist memory */
2831         print_us(scripted, parsable, fields, types, cb.cb_width, B_TRUE,
2832             cb.cb_avl);
2833 
2834         zfs_free_sort_columns(sortcol);
2835 
2836         /* Clean up the AVL tree */
2837         if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL)
2838                 nomem();
2839 
2840         while ((node = uu_avl_walk_next(walk)) != NULL) {
2841                 uu_avl_remove(cb.cb_avl, node);
2842                 free(node);
2843         }
2844 
2845         uu_avl_walk_end(walk);
2846         uu_avl_destroy(avl_tree);
2847         uu_avl_pool_destroy(avl_pool);
2848 
2849         return (ret);
2850 }
2851 
2852 /*
2853  * list [-Hp][-r|-d max] [-o property[,...]] [-s property] ... [-S property] ...
2854  *      [-t type[,...]] [filesystem|volume|snapshot] ...
2855  *
2856  *      -H      Scripted mode; elide headers and separate columns by tabs.
2857  *      -p      Display values in parsable (literal) format.
2858  *      -r      Recurse over all children.
2859  *      -d      Limit recursion by depth.
2860  *      -o      Control which fields to display.
2861  *      -s      Specify sort columns, descending order.
2862  *      -S      Specify sort columns, ascending order.
2863  *      -t      Control which object types to display.
2864  *
2865  * When given no arguments, list all filesystems in the system.
2866  * Otherwise, list the specified datasets, optionally recursing down them if
2867  * '-r' is specified.
2868  */
2869 typedef struct list_cbdata {
2870         boolean_t       cb_first;
2871         boolean_t       cb_literal;
2872         boolean_t       cb_scripted;
2873         zprop_list_t    *cb_proplist;
2874 } list_cbdata_t;
2875 
2876 /*
2877  * Given a list of columns to display, output appropriate headers for each one.
2878  */
2879 static void
2880 print_header(list_cbdata_t *cb)
2881 {
2882         zprop_list_t *pl = cb->cb_proplist;
2883         char headerbuf[ZFS_MAXPROPLEN];
2884         const char *header;
2885         int i;
2886         boolean_t first = B_TRUE;
2887         boolean_t right_justify;
2888 
2889         for (; pl != NULL; pl = pl->pl_next) {
2890                 if (!first) {
2891                         (void) printf("  ");
2892                 } else {
2893                         first = B_FALSE;
2894                 }
2895 
2896                 right_justify = B_FALSE;
2897                 if (pl->pl_prop != ZPROP_INVAL) {
2898                         header = zfs_prop_column_name(pl->pl_prop);
2899                         right_justify = zfs_prop_align_right(pl->pl_prop);
2900                 } else {
2901                         for (i = 0; pl->pl_user_prop[i] != '\0'; i++)
2902                                 headerbuf[i] = toupper(pl->pl_user_prop[i]);
2903                         headerbuf[i] = '\0';
2904                         header = headerbuf;
2905                 }
2906 
2907                 if (pl->pl_next == NULL && !right_justify)
2908                         (void) printf("%s", header);
2909                 else if (right_justify)
2910                         (void) printf("%*s", pl->pl_width, header);
2911                 else
2912                         (void) printf("%-*s", pl->pl_width, header);
2913         }
2914 
2915         (void) printf("\n");
2916 }
2917 
2918 /*
2919  * Given a dataset and a list of fields, print out all the properties according
2920  * to the described layout.
2921  */
2922 static void
2923 print_dataset(zfs_handle_t *zhp, list_cbdata_t *cb)
2924 {
2925         zprop_list_t *pl = cb->cb_proplist;
2926         boolean_t first = B_TRUE;
2927         char property[ZFS_MAXPROPLEN];
2928         nvlist_t *userprops = zfs_get_user_props(zhp);
2929         nvlist_t *propval;
2930         char *propstr;
2931         boolean_t right_justify;
2932 
2933         for (; pl != NULL; pl = pl->pl_next) {
2934                 if (!first) {
2935                         if (cb->cb_scripted)
2936                                 (void) printf("\t");
2937                         else
2938                                 (void) printf("  ");
2939                 } else {
2940                         first = B_FALSE;
2941                 }
2942 
2943                 if (pl->pl_prop != ZPROP_INVAL) {
2944                         if (zfs_prop_get(zhp, pl->pl_prop, property,
2945                             sizeof (property), NULL, NULL, 0,
2946                             cb->cb_literal) != 0)
2947                                 propstr = "-";
2948                         else
2949                                 propstr = property;
2950                         right_justify = zfs_prop_align_right(pl->pl_prop);
2951                 } else if (zfs_prop_userquota(pl->pl_user_prop)) {
2952                         if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
2953                             property, sizeof (property), cb->cb_literal) != 0)
2954                                 propstr = "-";
2955                         else
2956                                 propstr = property;
2957                         right_justify = B_TRUE;
2958                 } else if (zfs_prop_written(pl->pl_user_prop)) {
2959                         if (zfs_prop_get_written(zhp, pl->pl_user_prop,
2960                             property, sizeof (property), cb->cb_literal) != 0)
2961                                 propstr = "-";
2962                         else
2963                                 propstr = property;
2964                         right_justify = B_TRUE;
2965                 } else {
2966                         if (nvlist_lookup_nvlist(userprops,
2967                             pl->pl_user_prop, &propval) != 0)
2968                                 propstr = "-";
2969                         else
2970                                 verify(nvlist_lookup_string(propval,
2971                                     ZPROP_VALUE, &propstr) == 0);
2972                         right_justify = B_FALSE;
2973                 }
2974 
2975                 /*
2976                  * If this is being called in scripted mode, or if this is the
2977                  * last column and it is left-justified, don't include a width
2978                  * format specifier.
2979                  */
2980                 if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify))
2981                         (void) printf("%s", propstr);
2982                 else if (right_justify)
2983                         (void) printf("%*s", pl->pl_width, propstr);
2984                 else
2985                         (void) printf("%-*s", pl->pl_width, propstr);
2986         }
2987 
2988         (void) printf("\n");
2989 }
2990 
2991 /*
2992  * Generic callback function to list a dataset or snapshot.
2993  */
2994 static int
2995 list_callback(zfs_handle_t *zhp, void *data)
2996 {
2997         list_cbdata_t *cbp = data;
2998 
2999         if (cbp->cb_first) {
3000                 if (!cbp->cb_scripted)
3001                         print_header(cbp);
3002                 cbp->cb_first = B_FALSE;
3003         }
3004 
3005         print_dataset(zhp, cbp);
3006 
3007         return (0);
3008 }
3009 
3010 static int
3011 zfs_do_list(int argc, char **argv)
3012 {
3013         int c;
3014         static char default_fields[] =
3015             "name,used,available,referenced,mountpoint";
3016         int types = ZFS_TYPE_DATASET;
3017         boolean_t types_specified = B_FALSE;
3018         char *fields = NULL;
3019         list_cbdata_t cb = { 0 };
3020         char *value;
3021         int limit = 0;
3022         int ret = 0;
3023         zfs_sort_column_t *sortcol = NULL;
3024         int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS;
3025 
3026         /* check options */
3027         while ((c = getopt(argc, argv, "HS:d:o:prs:t:")) != -1) {
3028                 switch (c) {
3029                 case 'o':
3030                         fields = optarg;
3031                         break;
3032                 case 'p':
3033                         cb.cb_literal = B_TRUE;
3034                         flags |= ZFS_ITER_LITERAL_PROPS;
3035                         break;
3036                 case 'd':
3037                         limit = parse_depth(optarg, &flags);
3038                         break;
3039                 case 'r':
3040                         flags |= ZFS_ITER_RECURSE;
3041                         break;
3042                 case 'H':
3043                         cb.cb_scripted = B_TRUE;
3044                         break;
3045                 case 's':
3046                         if (zfs_add_sort_column(&sortcol, optarg,
3047                             B_FALSE) != 0) {
3048                                 (void) fprintf(stderr,
3049                                     gettext("invalid property '%s'\n"), optarg);
3050                                 usage(B_FALSE);
3051                         }
3052                         break;
3053                 case 'S':
3054                         if (zfs_add_sort_column(&sortcol, optarg,
3055                             B_TRUE) != 0) {
3056                                 (void) fprintf(stderr,
3057                                     gettext("invalid property '%s'\n"), optarg);
3058                                 usage(B_FALSE);
3059                         }
3060                         break;
3061                 case 't':
3062                         types = 0;
3063                         types_specified = B_TRUE;
3064                         flags &= ~ZFS_ITER_PROP_LISTSNAPS;
3065                         while (*optarg != '\0') {
3066                                 static char *type_subopts[] = { "filesystem",
3067                                     "volume", "snapshot", "snap", "bookmark",
3068                                     "all", NULL };
3069 
3070                                 switch (getsubopt(&optarg, type_subopts,
3071                                     &value)) {
3072                                 case 0:
3073                                         types |= ZFS_TYPE_FILESYSTEM;
3074                                         break;
3075                                 case 1:
3076                                         types |= ZFS_TYPE_VOLUME;
3077                                         break;
3078                                 case 2:
3079                                 case 3:
3080                                         types |= ZFS_TYPE_SNAPSHOT;
3081                                         break;
3082                                 case 4:
3083                                         types |= ZFS_TYPE_BOOKMARK;
3084                                         break;
3085                                 case 5:
3086                                         types = ZFS_TYPE_DATASET |
3087                                             ZFS_TYPE_BOOKMARK;
3088                                         break;
3089                                 default:
3090                                         (void) fprintf(stderr,
3091                                             gettext("invalid type '%s'\n"),
3092                                             value);
3093                                         usage(B_FALSE);
3094                                 }
3095                         }
3096                         break;
3097                 case ':':
3098                         (void) fprintf(stderr, gettext("missing argument for "
3099                             "'%c' option\n"), optopt);
3100                         usage(B_FALSE);
3101                         break;
3102                 case '?':
3103                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3104                             optopt);
3105                         usage(B_FALSE);
3106                 }
3107         }
3108 
3109         argc -= optind;
3110         argv += optind;
3111 
3112         if (fields == NULL)
3113                 fields = default_fields;
3114 
3115         /*
3116          * If "-o space" and no types were specified, don't display snapshots.
3117          */
3118         if (strcmp(fields, "space") == 0 && types_specified == B_FALSE)
3119                 types &= ~ZFS_TYPE_SNAPSHOT;
3120 
3121         /*
3122          * If the user specifies '-o all', the zprop_get_list() doesn't
3123          * normally include the name of the dataset.  For 'zfs list', we always
3124          * want this property to be first.
3125          */
3126         if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
3127             != 0)
3128                 usage(B_FALSE);
3129 
3130         cb.cb_first = B_TRUE;
3131 
3132         ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist,
3133             limit, list_callback, &cb);
3134 
3135         zprop_free_list(cb.cb_proplist);
3136         zfs_free_sort_columns(sortcol);
3137 
3138         if (ret == 0 && cb.cb_first && !cb.cb_scripted)
3139                 (void) printf(gettext("no datasets available\n"));
3140 
3141         return (ret);
3142 }
3143 
3144 /*
3145  * zfs rename [-f] <fs | snap | vol> <fs | snap | vol>
3146  * zfs rename [-f] -p <fs | vol> <fs | vol>
3147  * zfs rename -r <snap> <snap>
3148  *
3149  * Renames the given dataset to another of the same type.
3150  *
3151  * The '-p' flag creates all the non-existing ancestors of the target first.
3152  */
3153 /* ARGSUSED */
3154 static int
3155 zfs_do_rename(int argc, char **argv)
3156 {
3157         zfs_handle_t *zhp;
3158         int c;
3159         int ret = 0;
3160         boolean_t recurse = B_FALSE;
3161         boolean_t parents = B_FALSE;
3162         boolean_t force_unmount = B_FALSE;
3163 
3164         /* check options */
3165         while ((c = getopt(argc, argv, "prf")) != -1) {
3166                 switch (c) {
3167                 case 'p':
3168                         parents = B_TRUE;
3169                         break;
3170                 case 'r':
3171                         recurse = B_TRUE;
3172                         break;
3173                 case 'f':
3174                         force_unmount = B_TRUE;
3175                         break;
3176                 case '?':
3177                 default:
3178                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3179                             optopt);
3180                         usage(B_FALSE);
3181                 }
3182         }
3183 
3184         argc -= optind;
3185         argv += optind;
3186 
3187         /* check number of arguments */
3188         if (argc < 1) {
3189                 (void) fprintf(stderr, gettext("missing source dataset "
3190                     "argument\n"));
3191                 usage(B_FALSE);
3192         }
3193         if (argc < 2) {
3194                 (void) fprintf(stderr, gettext("missing target dataset "
3195                     "argument\n"));
3196                 usage(B_FALSE);
3197         }
3198         if (argc > 2) {
3199                 (void) fprintf(stderr, gettext("too many arguments\n"));
3200                 usage(B_FALSE);
3201         }
3202 
3203         if (recurse && parents) {
3204                 (void) fprintf(stderr, gettext("-p and -r options are mutually "
3205                     "exclusive\n"));
3206                 usage(B_FALSE);
3207         }
3208 
3209         if (recurse && strchr(argv[0], '@') == 0) {
3210                 (void) fprintf(stderr, gettext("source dataset for recursive "
3211                     "rename must be a snapshot\n"));
3212                 usage(B_FALSE);
3213         }
3214 
3215         if ((zhp = zfs_open(g_zfs, argv[0], parents ? ZFS_TYPE_FILESYSTEM |
3216             ZFS_TYPE_VOLUME : ZFS_TYPE_DATASET)) == NULL)
3217                 return (1);
3218 
3219         /* If we were asked and the name looks good, try to create ancestors. */
3220         if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) &&
3221             zfs_create_ancestors(g_zfs, argv[1]) != 0) {
3222                 zfs_close(zhp);
3223                 return (1);
3224         }
3225 
3226         ret = (zfs_rename(zhp, argv[1], recurse, force_unmount) != 0);
3227 
3228         zfs_close(zhp);
3229         return (ret);
3230 }
3231 
3232 /*
3233  * zfs promote <fs>
3234  *
3235  * Promotes the given clone fs to be the parent
3236  */
3237 /* ARGSUSED */
3238 static int
3239 zfs_do_promote(int argc, char **argv)
3240 {
3241         zfs_handle_t *zhp;
3242         int ret = 0;
3243 
3244         /* check options */
3245         if (argc > 1 && argv[1][0] == '-') {
3246                 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3247                     argv[1][1]);
3248                 usage(B_FALSE);
3249         }
3250 
3251         /* check number of arguments */
3252         if (argc < 2) {
3253                 (void) fprintf(stderr, gettext("missing clone filesystem"
3254                     " argument\n"));
3255                 usage(B_FALSE);
3256         }
3257         if (argc > 2) {
3258                 (void) fprintf(stderr, gettext("too many arguments\n"));
3259                 usage(B_FALSE);
3260         }
3261 
3262         zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
3263         if (zhp == NULL)
3264                 return (1);
3265 
3266         ret = (zfs_promote(zhp) != 0);
3267 
3268 
3269         zfs_close(zhp);
3270         return (ret);
3271 }
3272 
3273 /*
3274  * zfs rollback [-rRf] <snapshot>
3275  *
3276  *      -r      Delete any intervening snapshots before doing rollback
3277  *      -R      Delete any snapshots and their clones
3278  *      -f      ignored for backwards compatability
3279  *
3280  * Given a filesystem, rollback to a specific snapshot, discarding any changes
3281  * since then and making it the active dataset.  If more recent snapshots exist,
3282  * the command will complain unless the '-r' flag is given.
3283  */
3284 typedef struct rollback_cbdata {
3285         uint64_t        cb_create;
3286         boolean_t       cb_first;
3287         int             cb_doclones;
3288         char            *cb_target;
3289         int             cb_error;
3290         boolean_t       cb_recurse;
3291 } rollback_cbdata_t;
3292 
3293 static int
3294 rollback_check_dependent(zfs_handle_t *zhp, void *data)
3295 {
3296         rollback_cbdata_t *cbp = data;
3297 
3298         if (cbp->cb_first && cbp->cb_recurse) {
3299                 (void) fprintf(stderr, gettext("cannot rollback to "
3300                     "'%s': clones of previous snapshots exist\n"),
3301                     cbp->cb_target);
3302                 (void) fprintf(stderr, gettext("use '-R' to "
3303                     "force deletion of the following clones and "
3304                     "dependents:\n"));
3305                 cbp->cb_first = 0;
3306                 cbp->cb_error = 1;
3307         }
3308 
3309         (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
3310 
3311         zfs_close(zhp);
3312         return (0);
3313 }
3314 
3315 /*
3316  * Report any snapshots more recent than the one specified.  Used when '-r' is
3317  * not specified.  We reuse this same callback for the snapshot dependents - if
3318  * 'cb_dependent' is set, then this is a dependent and we should report it
3319  * without checking the transaction group.
3320  */
3321 static int
3322 rollback_check(zfs_handle_t *zhp, void *data)
3323 {
3324         rollback_cbdata_t *cbp = data;
3325 
3326         if (cbp->cb_doclones) {
3327                 zfs_close(zhp);
3328                 return (0);
3329         }
3330 
3331         if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {
3332                 if (cbp->cb_first && !cbp->cb_recurse) {
3333                         (void) fprintf(stderr, gettext("cannot "
3334                             "rollback to '%s': more recent snapshots "
3335                             "or bookmarks exist\n"),
3336                             cbp->cb_target);
3337                         (void) fprintf(stderr, gettext("use '-r' to "
3338                             "force deletion of the following "
3339                             "snapshots and bookmarks:\n"));
3340                         cbp->cb_first = 0;
3341                         cbp->cb_error = 1;
3342                 }
3343 
3344                 if (cbp->cb_recurse) {
3345                         if (zfs_iter_dependents(zhp, B_TRUE,
3346                             rollback_check_dependent, cbp) != 0) {
3347                                 zfs_close(zhp);
3348                                 return (-1);
3349                         }
3350                 } else {
3351                         (void) fprintf(stderr, "%s\n",
3352                             zfs_get_name(zhp));
3353                 }
3354         }
3355         zfs_close(zhp);
3356         return (0);
3357 }
3358 
3359 static int
3360 zfs_do_rollback(int argc, char **argv)
3361 {
3362         int ret = 0;
3363         int c;
3364         boolean_t force = B_FALSE;
3365         rollback_cbdata_t cb = { 0 };
3366         zfs_handle_t *zhp, *snap;
3367         char parentname[ZFS_MAXNAMELEN];
3368         char *delim;
3369 
3370         /* check options */
3371         while ((c = getopt(argc, argv, "rRf")) != -1) {
3372                 switch (c) {
3373                 case 'r':
3374                         cb.cb_recurse = 1;
3375                         break;
3376                 case 'R':
3377                         cb.cb_recurse = 1;
3378                         cb.cb_doclones = 1;
3379                         break;
3380                 case 'f':
3381                         force = B_TRUE;
3382                         break;
3383                 case '?':
3384                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3385                             optopt);
3386                         usage(B_FALSE);
3387                 }
3388         }
3389 
3390         argc -= optind;
3391         argv += optind;
3392 
3393         /* check number of arguments */
3394         if (argc < 1) {
3395                 (void) fprintf(stderr, gettext("missing dataset argument\n"));
3396                 usage(B_FALSE);
3397         }
3398         if (argc > 1) {
3399                 (void) fprintf(stderr, gettext("too many arguments\n"));
3400                 usage(B_FALSE);
3401         }
3402 
3403         /* open the snapshot */
3404         if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
3405                 return (1);
3406 
3407         /* open the parent dataset */
3408         (void) strlcpy(parentname, argv[0], sizeof (parentname));
3409         verify((delim = strrchr(parentname, '@')) != NULL);
3410         *delim = '\0';
3411         if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_DATASET)) == NULL) {
3412                 zfs_close(snap);
3413                 return (1);
3414         }
3415 
3416         /*
3417          * Check for more recent snapshots and/or clones based on the presence
3418          * of '-r' and '-R'.
3419          */
3420         cb.cb_target = argv[0];
3421         cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
3422         cb.cb_first = B_TRUE;
3423         cb.cb_error = 0;
3424         if ((ret = zfs_iter_snapshots(zhp, rollback_check, &cb)) != 0)
3425                 goto out;
3426         if ((ret = zfs_iter_bookmarks(zhp, rollback_check, &cb)) != 0)
3427                 goto out;
3428 
3429         if ((ret = cb.cb_error) != 0)
3430                 goto out;
3431 
3432         /*
3433          * Rollback parent to the given snapshot.
3434          */
3435         ret = zfs_rollback(zhp, snap, force);
3436 
3437 out:
3438         zfs_close(snap);
3439         zfs_close(zhp);
3440 
3441         if (ret == 0)
3442                 return (0);
3443         else
3444                 return (1);
3445 }
3446 
3447 /*
3448  * zfs set property=value ... { fs | snap | vol } ...
3449  *
3450  * Sets the given properties for all datasets specified on the command line.
3451  */
3452 
3453 static int
3454 set_callback(zfs_handle_t *zhp, void *data)
3455 {
3456         nvlist_t *props = data;
3457 
3458         if (zfs_prop_set_list(zhp, props) != 0) {
3459                 switch (libzfs_errno(g_zfs)) {
3460                 case EZFS_MOUNTFAILED:
3461                         (void) fprintf(stderr, gettext("property may be set "
3462                             "but unable to remount filesystem\n"));
3463                         break;
3464                 case EZFS_SHARENFSFAILED:
3465                         (void) fprintf(stderr, gettext("property may be set "
3466                             "but unable to reshare filesystem\n"));
3467                         break;
3468                 }
3469                 return (1);
3470         }
3471         return (0);
3472 }
3473 
3474 static int
3475 zfs_do_set(int argc, char **argv)
3476 {
3477         nvlist_t *props = NULL;
3478         int ds_start = -1; /* argv idx of first dataset arg */
3479         int ret = 0;
3480 
3481         /* check for options */
3482         if (argc > 1 && argv[1][0] == '-') {
3483                 (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3484                     argv[1][1]);
3485                 usage(B_FALSE);
3486         }
3487 
3488         /* check number of arguments */
3489         if (argc < 2) {
3490                 (void) fprintf(stderr, gettext("missing arguments\n"));
3491                 usage(B_FALSE);
3492         }
3493         if (argc < 3) {
3494                 if (strchr(argv[1], '=') == NULL) {
3495                         (void) fprintf(stderr, gettext("missing property=value "
3496                             "argument(s)\n"));
3497                 } else {
3498                         (void) fprintf(stderr, gettext("missing dataset "
3499                             "name(s)\n"));
3500                 }
3501                 usage(B_FALSE);
3502         }
3503 
3504         /* validate argument order:  prop=val args followed by dataset args */
3505         for (int i = 1; i < argc; i++) {
3506                 if (strchr(argv[i], '=') != NULL) {
3507                         if (ds_start > 0) {
3508                                 /* out-of-order prop=val argument */
3509                                 (void) fprintf(stderr, gettext("invalid "
3510                                     "argument order\n"), i);
3511                                 usage(B_FALSE);
3512                         }
3513                 } else if (ds_start < 0) {
3514                         ds_start = i;
3515                 }
3516         }
3517         if (ds_start < 0) {
3518                 (void) fprintf(stderr, gettext("missing dataset name(s)\n"));
3519                 usage(B_FALSE);
3520         }
3521 
3522         /* Populate a list of property settings */
3523         if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
3524                 nomem();
3525         for (int i = 1; i < ds_start; i++) {
3526                 if ((ret = parseprop(props, argv[i])) != 0)
3527                         goto error;
3528         }
3529 
3530         ret = zfs_for_each(argc - ds_start, argv + ds_start, 0,
3531             ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, props);
3532 
3533 error:
3534         nvlist_free(props);
3535         return (ret);
3536 }
3537 
3538 typedef struct snap_cbdata {
3539         nvlist_t *sd_nvl;
3540         boolean_t sd_recursive;
3541         const char *sd_snapname;
3542 } snap_cbdata_t;
3543 
3544 static int
3545 zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
3546 {
3547         snap_cbdata_t *sd = arg;
3548         char *name;
3549         int rv = 0;
3550         int error;
3551 
3552         if (sd->sd_recursive &&
3553             zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) != 0) {
3554                 zfs_close(zhp);
3555                 return (0);
3556         }
3557 
3558         error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
3559         if (error == -1)
3560                 nomem();
3561         fnvlist_add_boolean(sd->sd_nvl, name);
3562         free(name);
3563 
3564         if (sd->sd_recursive)
3565                 rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
3566         zfs_close(zhp);
3567         return (rv);
3568 }
3569 
3570 /*
3571  * zfs snapshot [-r] [-o prop=value] ... <fs@snap>
3572  *
3573  * Creates a snapshot with the given name.  While functionally equivalent to
3574  * 'zfs create', it is a separate command to differentiate intent.
3575  */
3576 static int
3577 zfs_do_snapshot(int argc, char **argv)
3578 {
3579         int ret = 0;
3580         char c;
3581         nvlist_t *props;
3582         snap_cbdata_t sd = { 0 };
3583         boolean_t multiple_snaps = B_FALSE;
3584 
3585         if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
3586                 nomem();
3587         if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0)
3588                 nomem();
3589 
3590         /* check options */
3591         while ((c = getopt(argc, argv, "ro:")) != -1) {
3592                 switch (c) {
3593                 case 'o':
3594                         if (parseprop(props, optarg) != 0)
3595                                 return (1);
3596                         break;
3597                 case 'r':
3598                         sd.sd_recursive = B_TRUE;
3599                         multiple_snaps = B_TRUE;
3600                         break;
3601                 case '?':
3602                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3603                             optopt);
3604                         goto usage;
3605                 }
3606         }
3607 
3608         argc -= optind;
3609         argv += optind;
3610 
3611         /* check number of arguments */
3612         if (argc < 1) {
3613                 (void) fprintf(stderr, gettext("missing snapshot argument\n"));
3614                 goto usage;
3615         }
3616 
3617         if (argc > 1)
3618                 multiple_snaps = B_TRUE;
3619         for (; argc > 0; argc--, argv++) {
3620                 char *atp;
3621                 zfs_handle_t *zhp;
3622 
3623                 atp = strchr(argv[0], '@');
3624                 if (atp == NULL)
3625                         goto usage;
3626                 *atp = '\0';
3627                 sd.sd_snapname = atp + 1;
3628                 zhp = zfs_open(g_zfs, argv[0],
3629                     ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
3630                 if (zhp == NULL)
3631                         goto usage;
3632                 if (zfs_snapshot_cb(zhp, &sd) != 0)
3633                         goto usage;
3634         }
3635 
3636         ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props);
3637         nvlist_free(sd.sd_nvl);
3638         nvlist_free(props);
3639         if (ret != 0 && multiple_snaps)
3640                 (void) fprintf(stderr, gettext("no snapshots were created\n"));
3641         return (ret != 0);
3642 
3643 usage:
3644         nvlist_free(sd.sd_nvl);
3645         nvlist_free(props);
3646         usage(B_FALSE);
3647         return (-1);
3648 }
3649 
3650 /*
3651  * Send a backup stream to stdout.
3652  */
3653 static int
3654 zfs_do_send(int argc, char **argv)
3655 {
3656         char *fromname = NULL;
3657         char *toname = NULL;
3658         char *cp;
3659         zfs_handle_t *zhp;
3660         sendflags_t flags = { 0 };
3661         int c, err;
3662         nvlist_t *dbgnv = NULL;
3663         boolean_t extraverbose = B_FALSE;
3664 
3665         /* check options */
3666         while ((c = getopt(argc, argv, ":i:I:RDpvnPLe")) != -1) {
3667                 switch (c) {
3668                 case 'i':
3669                         if (fromname)
3670                                 usage(B_FALSE);
3671                         fromname = optarg;
3672                         break;
3673                 case 'I':
3674                         if (fromname)
3675                                 usage(B_FALSE);
3676                         fromname = optarg;
3677                         flags.doall = B_TRUE;
3678                         break;
3679                 case 'R':
3680                         flags.replicate = B_TRUE;
3681                         break;
3682                 case 'p':
3683                         flags.props = B_TRUE;
3684                         break;
3685                 case 'P':
3686                         flags.parsable = B_TRUE;
3687                         flags.verbose = B_TRUE;
3688                         break;
3689                 case 'v':
3690                         if (flags.verbose)
3691                                 extraverbose = B_TRUE;
3692                         flags.verbose = B_TRUE;
3693                         flags.progress = B_TRUE;
3694                         break;
3695                 case 'D':
3696                         flags.dedup = B_TRUE;
3697                         break;
3698                 case 'n':
3699                         flags.dryrun = B_TRUE;
3700                         break;
3701                 case 'L':
3702                         flags.largeblock = B_TRUE;
3703                         break;
3704                 case 'e':
3705                         flags.embed_data = B_TRUE;
3706                         break;
3707                 case ':':
3708                         (void) fprintf(stderr, gettext("missing argument for "
3709                             "'%c' option\n"), optopt);
3710                         usage(B_FALSE);
3711                         break;
3712                 case '?':
3713                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3714                             optopt);
3715                         usage(B_FALSE);
3716                 }
3717         }
3718 
3719         argc -= optind;
3720         argv += optind;
3721 
3722         /* check number of arguments */
3723         if (argc < 1) {
3724                 (void) fprintf(stderr, gettext("missing snapshot argument\n"));
3725                 usage(B_FALSE);
3726         }
3727         if (argc > 1) {
3728                 (void) fprintf(stderr, gettext("too many arguments\n"));
3729                 usage(B_FALSE);
3730         }
3731 
3732         if (!flags.dryrun && isatty(STDOUT_FILENO)) {
3733                 (void) fprintf(stderr,
3734                     gettext("Error: Stream can not be written to a terminal.\n"
3735                     "You must redirect standard output.\n"));
3736                 return (1);
3737         }
3738 
3739         /*
3740          * Special case sending a filesystem, or from a bookmark.
3741          */
3742         if (strchr(argv[0], '@') == NULL ||
3743             (fromname && strchr(fromname, '#') != NULL)) {
3744                 char frombuf[ZFS_MAXNAMELEN];
3745                 enum lzc_send_flags lzc_flags = 0;
3746 
3747                 if (flags.replicate || flags.doall || flags.props ||
3748                     flags.dedup || flags.dryrun || flags.verbose ||
3749                     flags.progress) {
3750                         (void) fprintf(stderr,
3751                             gettext("Error: "
3752                             "Unsupported flag with filesystem or bookmark.\n"));
3753                         return (1);
3754                 }
3755 
3756                 zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);
3757                 if (zhp == NULL)
3758                         return (1);
3759 
3760                 if (flags.largeblock)
3761                         lzc_flags |= LZC_SEND_FLAG_LARGE_BLOCK;
3762                 if (flags.embed_data)
3763                         lzc_flags |= LZC_SEND_FLAG_EMBED_DATA;
3764 
3765                 if (fromname != NULL &&
3766                     (fromname[0] == '#' || fromname[0] == '@')) {
3767                         /*
3768                          * Incremental source name begins with # or @.
3769                          * Default to same fs as target.
3770                          */
3771                         (void) strncpy(frombuf, argv[0], sizeof (frombuf));
3772                         cp = strchr(frombuf, '@');
3773                         if (cp != NULL)
3774                                 *cp = '\0';
3775                         (void) strlcat(frombuf, fromname, sizeof (frombuf));
3776                         fromname = frombuf;
3777                 }
3778                 err = zfs_send_one(zhp, fromname, STDOUT_FILENO, lzc_flags);
3779                 zfs_close(zhp);
3780                 return (err != 0);
3781         }
3782 
3783         cp = strchr(argv[0], '@');
3784         *cp = '\0';
3785         toname = cp + 1;
3786         zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
3787         if (zhp == NULL)
3788                 return (1);
3789 
3790         /*
3791          * If they specified the full path to the snapshot, chop off
3792          * everything except the short name of the snapshot, but special
3793          * case if they specify the origin.
3794          */
3795         if (fromname && (cp = strchr(fromname, '@')) != NULL) {
3796                 char origin[ZFS_MAXNAMELEN];
3797                 zprop_source_t src;
3798 
3799                 (void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN,
3800                     origin, sizeof (origin), &src, NULL, 0, B_FALSE);
3801 
3802                 if (strcmp(origin, fromname) == 0) {
3803                         fromname = NULL;
3804                         flags.fromorigin = B_TRUE;
3805                 } else {
3806                         *cp = '\0';
3807                         if (cp != fromname && strcmp(argv[0], fromname)) {
3808                                 (void) fprintf(stderr,
3809                                     gettext("incremental source must be "
3810                                     "in same filesystem\n"));
3811                                 usage(B_FALSE);
3812                         }
3813                         fromname = cp + 1;
3814                         if (strchr(fromname, '@') || strchr(fromname, '/')) {
3815                                 (void) fprintf(stderr,
3816                                     gettext("invalid incremental source\n"));
3817                                 usage(B_FALSE);
3818                         }
3819                 }
3820         }
3821 
3822         if (flags.replicate && fromname == NULL)
3823                 flags.doall = B_TRUE;
3824 
3825         err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO, NULL, 0,
3826             extraverbose ? &dbgnv : NULL);
3827 
3828         if (extraverbose && dbgnv != NULL) {
3829                 /*
3830                  * dump_nvlist prints to stdout, but that's been
3831                  * redirected to a file.  Make it print to stderr
3832                  * instead.
3833                  */
3834                 (void) dup2(STDERR_FILENO, STDOUT_FILENO);
3835                 dump_nvlist(dbgnv, 0);
3836                 nvlist_free(dbgnv);
3837         }
3838         zfs_close(zhp);
3839 
3840         return (err != 0);
3841 }
3842 
3843 /*
3844  * zfs receive [-vnFu] [-d | -e] <fs@snap>
3845  *
3846  * Restore a backup stream from stdin.
3847  */
3848 static int
3849 zfs_do_receive(int argc, char **argv)
3850 {
3851         int c, err;
3852         recvflags_t flags = { 0 };
3853         nvlist_t *props;
3854         nvpair_t *nvp = NULL;
3855 
3856         if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
3857                 nomem();
3858 
3859         /* check options */
3860         while ((c = getopt(argc, argv, ":o:denuvF")) != -1) {
3861                 switch (c) {
3862                 case 'o':
3863                         if (parseprop(props, optarg) != 0)
3864                                 return (1);
3865                         break;
3866                 case 'd':
3867                         flags.isprefix = B_TRUE;
3868                         break;
3869                 case 'e':
3870                         flags.isprefix = B_TRUE;
3871                         flags.istail = B_TRUE;
3872                         break;
3873                 case 'n':
3874                         flags.dryrun = B_TRUE;
3875                         break;
3876                 case 'u':
3877                         flags.nomount = B_TRUE;
3878                         break;
3879                 case 'v':
3880                         flags.verbose = B_TRUE;
3881                         break;
3882                 case 'F':
3883                         flags.force = B_TRUE;
3884                         break;
3885                 case ':':
3886                         (void) fprintf(stderr, gettext("missing argument for "
3887                             "'%c' option\n"), optopt);
3888                         usage(B_FALSE);
3889                         break;
3890                 case '?':
3891                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
3892                             optopt);
3893                         usage(B_FALSE);
3894                 }
3895         }
3896 
3897         argc -= optind;
3898         argv += optind;
3899 
3900         /* check number of arguments */
3901         if (argc < 1) {
3902                 (void) fprintf(stderr, gettext("missing snapshot argument\n"));
3903                 usage(B_FALSE);
3904         }
3905         if (argc > 1) {
3906                 (void) fprintf(stderr, gettext("too many arguments\n"));
3907                 usage(B_FALSE);
3908         }
3909 
3910         while ((nvp = nvlist_next_nvpair(props, nvp))) {
3911                 if (strcmp(nvpair_name(nvp), "origin") != 0) {
3912                         (void) fprintf(stderr, gettext("invalid option"));
3913                         usage(B_FALSE);
3914                 }
3915         }
3916 
3917         if (isatty(STDIN_FILENO)) {
3918                 (void) fprintf(stderr,
3919                     gettext("Error: Backup stream can not be read "
3920                     "from a terminal.\n"
3921                     "You must redirect standard input.\n"));
3922                 return (1);
3923         }
3924 
3925         err = zfs_receive(g_zfs, argv[0], props, &flags, STDIN_FILENO, NULL);
3926 
3927         return (err != 0);
3928 }
3929 
3930 /*
3931  * allow/unallow stuff
3932  */
3933 /* copied from zfs/sys/dsl_deleg.h */
3934 #define ZFS_DELEG_PERM_CREATE           "create"
3935 #define ZFS_DELEG_PERM_DESTROY          "destroy"
3936 #define ZFS_DELEG_PERM_SNAPSHOT         "snapshot"
3937 #define ZFS_DELEG_PERM_ROLLBACK         "rollback"
3938 #define ZFS_DELEG_PERM_CLONE            "clone"
3939 #define ZFS_DELEG_PERM_PROMOTE          "promote"
3940 #define ZFS_DELEG_PERM_RENAME           "rename"
3941 #define ZFS_DELEG_PERM_MOUNT            "mount"
3942 #define ZFS_DELEG_PERM_SHARE            "share"
3943 #define ZFS_DELEG_PERM_SEND             "send"
3944 #define ZFS_DELEG_PERM_RECEIVE          "receive"
3945 #define ZFS_DELEG_PERM_ALLOW            "allow"
3946 #define ZFS_DELEG_PERM_USERPROP         "userprop"
3947 #define ZFS_DELEG_PERM_VSCAN            "vscan" /* ??? */
3948 #define ZFS_DELEG_PERM_USERQUOTA        "userquota"
3949 #define ZFS_DELEG_PERM_GROUPQUOTA       "groupquota"
3950 #define ZFS_DELEG_PERM_USERUSED         "userused"
3951 #define ZFS_DELEG_PERM_GROUPUSED        "groupused"
3952 #define ZFS_DELEG_PERM_HOLD             "hold"
3953 #define ZFS_DELEG_PERM_RELEASE          "release"
3954 #define ZFS_DELEG_PERM_DIFF             "diff"
3955 #define ZFS_DELEG_PERM_BOOKMARK         "bookmark"
3956 
3957 #define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE
3958 
3959 static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = {
3960         { ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW },
3961         { ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE },
3962         { ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE },
3963         { ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY },
3964         { ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF},
3965         { ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD },
3966         { ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT },
3967         { ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE },
3968         { ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE },
3969         { ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE },
3970         { ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },
3971         { ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },
3972         { ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND },
3973         { ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },
3974         { ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },
3975         { ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK },
3976 
3977         { ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA },
3978         { ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED },
3979         { ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },
3980         { ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA },
3981         { ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED },
3982         { NULL, ZFS_DELEG_NOTE_NONE }
3983 };
3984 
3985 /* permission structure */
3986 typedef struct deleg_perm {
3987         zfs_deleg_who_type_t    dp_who_type;
3988         const char              *dp_name;
3989         boolean_t               dp_local;
3990         boolean_t               dp_descend;
3991 } deleg_perm_t;
3992 
3993 /* */
3994 typedef struct deleg_perm_node {
3995         deleg_perm_t            dpn_perm;
3996 
3997         uu_avl_node_t           dpn_avl_node;
3998 } deleg_perm_node_t;
3999 
4000 typedef struct fs_perm fs_perm_t;
4001 
4002 /* permissions set */
4003 typedef struct who_perm {
4004         zfs_deleg_who_type_t    who_type;
4005         const char              *who_name;              /* id */
4006         char                    who_ug_name[256];       /* user/group name */
4007         fs_perm_t               *who_fsperm;            /* uplink */
4008 
4009         uu_avl_t                *who_deleg_perm_avl;    /* permissions */
4010 } who_perm_t;
4011 
4012 /* */
4013 typedef struct who_perm_node {
4014         who_perm_t      who_perm;
4015         uu_avl_node_t   who_avl_node;
4016 } who_perm_node_t;
4017 
4018 typedef struct fs_perm_set fs_perm_set_t;
4019 /* fs permissions */
4020 struct fs_perm {
4021         const char              *fsp_name;
4022 
4023         uu_avl_t                *fsp_sc_avl;    /* sets,create */
4024         uu_avl_t                *fsp_uge_avl;   /* user,group,everyone */
4025 
4026         fs_perm_set_t           *fsp_set;       /* uplink */
4027 };
4028 
4029 /* */
4030 typedef struct fs_perm_node {
4031         fs_perm_t       fspn_fsperm;
4032         uu_avl_t        *fspn_avl;
4033 
4034         uu_list_node_t  fspn_list_node;
4035 } fs_perm_node_t;
4036 
4037 /* top level structure */
4038 struct fs_perm_set {
4039         uu_list_pool_t  *fsps_list_pool;
4040         uu_list_t       *fsps_list; /* list of fs_perms */
4041 
4042         uu_avl_pool_t   *fsps_named_set_avl_pool;
4043         uu_avl_pool_t   *fsps_who_perm_avl_pool;
4044         uu_avl_pool_t   *fsps_deleg_perm_avl_pool;
4045 };
4046 
4047 static inline const char *
4048 deleg_perm_type(zfs_deleg_note_t note)
4049 {
4050         /* subcommands */
4051         switch (note) {
4052                 /* SUBCOMMANDS */
4053                 /* OTHER */
4054         case ZFS_DELEG_NOTE_GROUPQUOTA:
4055         case ZFS_DELEG_NOTE_GROUPUSED:
4056         case ZFS_DELEG_NOTE_USERPROP:
4057         case ZFS_DELEG_NOTE_USERQUOTA:
4058         case ZFS_DELEG_NOTE_USERUSED:
4059                 /* other */
4060                 return (gettext("other"));
4061         default:
4062                 return (gettext("subcommand"));
4063         }
4064 }
4065 
4066 static int inline
4067 who_type2weight(zfs_deleg_who_type_t who_type)
4068 {
4069         int res;
4070         switch (who_type) {
4071                 case ZFS_DELEG_NAMED_SET_SETS:
4072                 case ZFS_DELEG_NAMED_SET:
4073                         res = 0;
4074                         break;
4075                 case ZFS_DELEG_CREATE_SETS:
4076                 case ZFS_DELEG_CREATE:
4077                         res = 1;
4078                         break;
4079                 case ZFS_DELEG_USER_SETS:
4080                 case ZFS_DELEG_USER:
4081                         res = 2;
4082                         break;
4083                 case ZFS_DELEG_GROUP_SETS:
4084                 case ZFS_DELEG_GROUP:
4085                         res = 3;
4086                         break;
4087                 case ZFS_DELEG_EVERYONE_SETS:
4088                 case ZFS_DELEG_EVERYONE:
4089                         res = 4;
4090                         break;
4091                 default:
4092                         res = -1;
4093         }
4094 
4095         return (res);
4096 }
4097 
4098 /* ARGSUSED */
4099 static int
4100 who_perm_compare(const void *larg, const void *rarg, void *unused)
4101 {
4102         const who_perm_node_t *l = larg;
4103         const who_perm_node_t *r = rarg;
4104         zfs_deleg_who_type_t ltype = l->who_perm.who_type;
4105         zfs_deleg_who_type_t rtype = r->who_perm.who_type;
4106         int lweight = who_type2weight(ltype);
4107         int rweight = who_type2weight(rtype);
4108         int res = lweight - rweight;
4109         if (res == 0)
4110                 res = strncmp(l->who_perm.who_name, r->who_perm.who_name,
4111                     ZFS_MAX_DELEG_NAME-1);
4112 
4113         if (res == 0)
4114                 return (0);
4115         if (res > 0)
4116                 return (1);
4117         else
4118                 return (-1);
4119 }
4120 
4121 /* ARGSUSED */
4122 static int
4123 deleg_perm_compare(const void *larg, const void *rarg, void *unused)
4124 {
4125         const deleg_perm_node_t *l = larg;
4126         const deleg_perm_node_t *r = rarg;
4127         int res =  strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name,
4128             ZFS_MAX_DELEG_NAME-1);
4129 
4130         if (res == 0)
4131                 return (0);
4132 
4133         if (res > 0)
4134                 return (1);
4135         else
4136                 return (-1);
4137 }
4138 
4139 static inline void
4140 fs_perm_set_init(fs_perm_set_t *fspset)
4141 {
4142         bzero(fspset, sizeof (fs_perm_set_t));
4143 
4144         if ((fspset->fsps_list_pool = uu_list_pool_create("fsps_list_pool",
4145             sizeof (fs_perm_node_t), offsetof(fs_perm_node_t, fspn_list_node),
4146             NULL, UU_DEFAULT)) == NULL)
4147                 nomem();
4148         if ((fspset->fsps_list = uu_list_create(fspset->fsps_list_pool, NULL,
4149             UU_DEFAULT)) == NULL)
4150                 nomem();
4151 
4152         if ((fspset->fsps_named_set_avl_pool = uu_avl_pool_create(
4153             "named_set_avl_pool", sizeof (who_perm_node_t), offsetof(
4154             who_perm_node_t, who_avl_node), who_perm_compare,
4155             UU_DEFAULT)) == NULL)
4156                 nomem();
4157 
4158         if ((fspset->fsps_who_perm_avl_pool = uu_avl_pool_create(
4159             "who_perm_avl_pool", sizeof (who_perm_node_t), offsetof(
4160             who_perm_node_t, who_avl_node), who_perm_compare,
4161             UU_DEFAULT)) == NULL)
4162                 nomem();
4163 
4164         if ((fspset->fsps_deleg_perm_avl_pool = uu_avl_pool_create(
4165             "deleg_perm_avl_pool", sizeof (deleg_perm_node_t), offsetof(
4166             deleg_perm_node_t, dpn_avl_node), deleg_perm_compare, UU_DEFAULT))
4167             == NULL)
4168                 nomem();
4169 }
4170 
4171 static inline void fs_perm_fini(fs_perm_t *);
4172 static inline void who_perm_fini(who_perm_t *);
4173 
4174 static inline void
4175 fs_perm_set_fini(fs_perm_set_t *fspset)
4176 {
4177         fs_perm_node_t *node = uu_list_first(fspset->fsps_list);
4178 
4179         while (node != NULL) {
4180                 fs_perm_node_t *next_node =
4181                     uu_list_next(fspset->fsps_list, node);
4182                 fs_perm_t *fsperm = &node->fspn_fsperm;
4183                 fs_perm_fini(fsperm);
4184                 uu_list_remove(fspset->fsps_list, node);
4185                 free(node);
4186                 node = next_node;
4187         }
4188 
4189         uu_avl_pool_destroy(fspset->fsps_named_set_avl_pool);
4190         uu_avl_pool_destroy(fspset->fsps_who_perm_avl_pool);
4191         uu_avl_pool_destroy(fspset->fsps_deleg_perm_avl_pool);
4192 }
4193 
4194 static inline void
4195 deleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type,
4196     const char *name)
4197 {
4198         deleg_perm->dp_who_type = type;
4199         deleg_perm->dp_name = name;
4200 }
4201 
4202 static inline void
4203 who_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm,
4204     zfs_deleg_who_type_t type, const char *name)
4205 {
4206         uu_avl_pool_t   *pool;
4207         pool = fsperm->fsp_set->fsps_deleg_perm_avl_pool;
4208 
4209         bzero(who_perm, sizeof (who_perm_t));
4210 
4211         if ((who_perm->who_deleg_perm_avl = uu_avl_create(pool, NULL,
4212             UU_DEFAULT)) == NULL)
4213                 nomem();
4214 
4215         who_perm->who_type = type;
4216         who_perm->who_name = name;
4217         who_perm->who_fsperm = fsperm;
4218 }
4219 
4220 static inline void
4221 who_perm_fini(who_perm_t *who_perm)
4222 {
4223         deleg_perm_node_t *node = uu_avl_first(who_perm->who_deleg_perm_avl);
4224 
4225         while (node != NULL) {
4226                 deleg_perm_node_t *next_node =
4227                     uu_avl_next(who_perm->who_deleg_perm_avl, node);
4228 
4229                 uu_avl_remove(who_perm->who_deleg_perm_avl, node);
4230                 free(node);
4231                 node = next_node;
4232         }
4233 
4234         uu_avl_destroy(who_perm->who_deleg_perm_avl);
4235 }
4236 
4237 static inline void
4238 fs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname)
4239 {
4240         uu_avl_pool_t   *nset_pool = fspset->fsps_named_set_avl_pool;
4241         uu_avl_pool_t   *who_pool = fspset->fsps_who_perm_avl_pool;
4242 
4243         bzero(fsperm, sizeof (fs_perm_t));
4244 
4245         if ((fsperm->fsp_sc_avl = uu_avl_create(nset_pool, NULL, UU_DEFAULT))
4246             == NULL)
4247                 nomem();
4248 
4249         if ((fsperm->fsp_uge_avl = uu_avl_create(who_pool, NULL, UU_DEFAULT))
4250             == NULL)
4251                 nomem();
4252 
4253         fsperm->fsp_set = fspset;
4254         fsperm->fsp_name = fsname;
4255 }
4256 
4257 static inline void
4258 fs_perm_fini(fs_perm_t *fsperm)
4259 {
4260         who_perm_node_t *node = uu_avl_first(fsperm->fsp_sc_avl);
4261         while (node != NULL) {
4262                 who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_sc_avl,
4263                     node);
4264                 who_perm_t *who_perm = &node->who_perm;
4265                 who_perm_fini(who_perm);
4266                 uu_avl_remove(fsperm->fsp_sc_avl, node);
4267                 free(node);
4268                 node = next_node;
4269         }
4270 
4271         node = uu_avl_first(fsperm->fsp_uge_avl);
4272         while (node != NULL) {
4273                 who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_uge_avl,
4274                     node);
4275                 who_perm_t *who_perm = &node->who_perm;
4276                 who_perm_fini(who_perm);
4277                 uu_avl_remove(fsperm->fsp_uge_avl, node);
4278                 free(node);
4279                 node = next_node;
4280         }
4281 
4282         uu_avl_destroy(fsperm->fsp_sc_avl);
4283         uu_avl_destroy(fsperm->fsp_uge_avl);
4284 }
4285 
4286 static void inline
4287 set_deleg_perm_node(uu_avl_t *avl, deleg_perm_node_t *node,
4288     zfs_deleg_who_type_t who_type, const char *name, char locality)
4289 {
4290         uu_avl_index_t idx = 0;
4291 
4292         deleg_perm_node_t *found_node = NULL;
4293         deleg_perm_t    *deleg_perm = &node->dpn_perm;
4294 
4295         deleg_perm_init(deleg_perm, who_type, name);
4296 
4297         if ((found_node = uu_avl_find(avl, node, NULL, &idx))
4298             == NULL)
4299                 uu_avl_insert(avl, node, idx);
4300         else {
4301                 node = found_node;
4302                 deleg_perm = &node->dpn_perm;
4303         }
4304 
4305 
4306         switch (locality) {
4307         case ZFS_DELEG_LOCAL:
4308                 deleg_perm->dp_local = B_TRUE;
4309                 break;
4310         case ZFS_DELEG_DESCENDENT:
4311                 deleg_perm->dp_descend = B_TRUE;
4312                 break;
4313         case ZFS_DELEG_NA:
4314                 break;
4315         default:
4316                 assert(B_FALSE); /* invalid locality */
4317         }
4318 }
4319 
4320 static inline int
4321 parse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality)
4322 {
4323         nvpair_t *nvp = NULL;
4324         fs_perm_set_t *fspset = who_perm->who_fsperm->fsp_set;
4325         uu_avl_t *avl = who_perm->who_deleg_perm_avl;
4326         zfs_deleg_who_type_t who_type = who_perm->who_type;
4327 
4328         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
4329                 const char *name = nvpair_name(nvp);
4330                 data_type_t type = nvpair_type(nvp);
4331                 uu_avl_pool_t *avl_pool = fspset->fsps_deleg_perm_avl_pool;
4332                 deleg_perm_node_t *node =
4333                     safe_malloc(sizeof (deleg_perm_node_t));
4334 
4335                 assert(type == DATA_TYPE_BOOLEAN);
4336 
4337                 uu_avl_node_init(node, &node->dpn_avl_node, avl_pool);
4338                 set_deleg_perm_node(avl, node, who_type, name, locality);
4339         }
4340 
4341         return (0);
4342 }
4343 
4344 static inline int
4345 parse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl)
4346 {
4347         nvpair_t *nvp = NULL;
4348         fs_perm_set_t *fspset = fsperm->fsp_set;
4349 
4350         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
4351                 nvlist_t *nvl2 = NULL;
4352                 const char *name = nvpair_name(nvp);
4353                 uu_avl_t *avl = NULL;
4354                 uu_avl_pool_t *avl_pool;
4355                 zfs_deleg_who_type_t perm_type = name[0];
4356                 char perm_locality = name[1];
4357                 const char *perm_name = name + 3;
4358                 boolean_t is_set = B_TRUE;
4359                 who_perm_t *who_perm = NULL;
4360 
4361                 assert('$' == name[2]);
4362 
4363                 if (nvpair_value_nvlist(nvp, &nvl2) != 0)
4364                         return (-1);
4365 
4366                 switch (perm_type) {
4367                 case ZFS_DELEG_CREATE:
4368                 case ZFS_DELEG_CREATE_SETS:
4369                 case ZFS_DELEG_NAMED_SET:
4370                 case ZFS_DELEG_NAMED_SET_SETS:
4371                         avl_pool = fspset->fsps_named_set_avl_pool;
4372                         avl = fsperm->fsp_sc_avl;
4373                         break;
4374                 case ZFS_DELEG_USER:
4375                 case ZFS_DELEG_USER_SETS:
4376                 case ZFS_DELEG_GROUP:
4377                 case ZFS_DELEG_GROUP_SETS:
4378                 case ZFS_DELEG_EVERYONE:
4379                 case ZFS_DELEG_EVERYONE_SETS:
4380                         avl_pool = fspset->fsps_who_perm_avl_pool;
4381                         avl = fsperm->fsp_uge_avl;
4382                         break;
4383                 }
4384 
4385                 if (is_set) {
4386                         who_perm_node_t *found_node = NULL;
4387                         who_perm_node_t *node = safe_malloc(
4388                             sizeof (who_perm_node_t));
4389                         who_perm = &node->who_perm;
4390                         uu_avl_index_t idx = 0;
4391 
4392                         uu_avl_node_init(node, &node->who_avl_node, avl_pool);
4393                         who_perm_init(who_perm, fsperm, perm_type, perm_name);
4394 
4395                         if ((found_node = uu_avl_find(avl, node, NULL, &idx))
4396                             == NULL) {
4397                                 if (avl == fsperm->fsp_uge_avl) {
4398                                         uid_t rid = 0;
4399                                         struct passwd *p = NULL;
4400                                         struct group *g = NULL;
4401                                         const char *nice_name = NULL;
4402 
4403                                         switch (perm_type) {
4404                                         case ZFS_DELEG_USER_SETS:
4405                                         case ZFS_DELEG_USER:
4406                                                 rid = atoi(perm_name);
4407                                                 p = getpwuid(rid);
4408                                                 if (p)
4409                                                         nice_name = p->pw_name;
4410                                                 break;
4411                                         case ZFS_DELEG_GROUP_SETS:
4412                                         case ZFS_DELEG_GROUP:
4413                                                 rid = atoi(perm_name);
4414                                                 g = getgrgid(rid);
4415                                                 if (g)
4416                                                         nice_name = g->gr_name;
4417                                                 break;
4418                                         }
4419 
4420                                         if (nice_name != NULL)
4421                                                 (void) strlcpy(
4422                                                     node->who_perm.who_ug_name,
4423                                                     nice_name, 256);
4424                                 }
4425 
4426                                 uu_avl_insert(avl, node, idx);
4427                         } else {
4428                                 node = found_node;
4429                                 who_perm = &node->who_perm;
4430                         }
4431                 }
4432 
4433                 (void) parse_who_perm(who_perm, nvl2, perm_locality);
4434         }
4435 
4436         return (0);
4437 }
4438 
4439 static inline int
4440 parse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl)
4441 {
4442         nvpair_t *nvp = NULL;
4443         uu_avl_index_t idx = 0;
4444 
4445         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
4446                 nvlist_t *nvl2 = NULL;
4447                 const char *fsname = nvpair_name(nvp);
4448                 data_type_t type = nvpair_type(nvp);
4449                 fs_perm_t *fsperm = NULL;
4450                 fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t));
4451                 if (node == NULL)
4452                         nomem();
4453 
4454                 fsperm = &node->fspn_fsperm;
4455 
4456                 assert(DATA_TYPE_NVLIST == type);
4457 
4458                 uu_list_node_init(node, &node->fspn_list_node,
4459                     fspset->fsps_list_pool);
4460 
4461                 idx = uu_list_numnodes(fspset->fsps_list);
4462                 fs_perm_init(fsperm, fspset, fsname);
4463 
4464                 if (nvpair_value_nvlist(nvp, &nvl2) != 0)
4465                         return (-1);
4466 
4467                 (void) parse_fs_perm(fsperm, nvl2);
4468 
4469                 uu_list_insert(fspset->fsps_list, node, idx);
4470         }
4471 
4472         return (0);
4473 }
4474 
4475 static inline const char *
4476 deleg_perm_comment(zfs_deleg_note_t note)
4477 {
4478         const char *str = "";
4479 
4480         /* subcommands */
4481         switch (note) {
4482                 /* SUBCOMMANDS */
4483         case ZFS_DELEG_NOTE_ALLOW:
4484                 str = gettext("Must also have the permission that is being"
4485                     "\n\t\t\t\tallowed");
4486                 break;
4487         case ZFS_DELEG_NOTE_CLONE:
4488                 str = gettext("Must also have the 'create' ability and 'mount'"
4489                     "\n\t\t\t\tability in the origin file system");
4490                 break;
4491         case ZFS_DELEG_NOTE_CREATE:
4492                 str = gettext("Must also have the 'mount' ability");
4493                 break;
4494         case ZFS_DELEG_NOTE_DESTROY:
4495                 str = gettext("Must also have the 'mount' ability");
4496                 break;
4497         case ZFS_DELEG_NOTE_DIFF:
4498                 str = gettext("Allows lookup of paths within a dataset;"
4499                     "\n\t\t\t\tgiven an object number. Ordinary users need this"
4500                     "\n\t\t\t\tin order to use zfs diff");
4501                 break;
4502         case ZFS_DELEG_NOTE_HOLD:
4503                 str = gettext("Allows adding a user hold to a snapshot");
4504                 break;
4505         case ZFS_DELEG_NOTE_MOUNT:
4506                 str = gettext("Allows mount/umount of ZFS datasets");
4507                 break;
4508         case ZFS_DELEG_NOTE_PROMOTE:
4509                 str = gettext("Must also have the 'mount'\n\t\t\t\tand"
4510                     " 'promote' ability in the origin file system");
4511                 break;
4512         case ZFS_DELEG_NOTE_RECEIVE:
4513                 str = gettext("Must also have the 'mount' and 'create'"
4514                     " ability");
4515                 break;
4516         case ZFS_DELEG_NOTE_RELEASE:
4517                 str = gettext("Allows releasing a user hold which\n\t\t\t\t"
4518                     "might destroy the snapshot");
4519                 break;
4520         case ZFS_DELEG_NOTE_RENAME:
4521                 str = gettext("Must also have the 'mount' and 'create'"
4522                     "\n\t\t\t\tability in the new parent");
4523                 break;
4524         case ZFS_DELEG_NOTE_ROLLBACK:
4525                 str = gettext("");
4526                 break;
4527         case ZFS_DELEG_NOTE_SEND:
4528                 str = gettext("");
4529                 break;
4530         case ZFS_DELEG_NOTE_SHARE:
4531                 str = gettext("Allows sharing file systems over NFS or SMB"
4532                     "\n\t\t\t\tprotocols");
4533                 break;
4534         case ZFS_DELEG_NOTE_SNAPSHOT:
4535                 str = gettext("");
4536                 break;
4537 /*
4538  *      case ZFS_DELEG_NOTE_VSCAN:
4539  *              str = gettext("");
4540  *              break;
4541  */
4542                 /* OTHER */
4543         case ZFS_DELEG_NOTE_GROUPQUOTA:
4544                 str = gettext("Allows accessing any groupquota@... property");
4545                 break;
4546         case ZFS_DELEG_NOTE_GROUPUSED:
4547                 str = gettext("Allows reading any groupused@... property");
4548                 break;
4549         case ZFS_DELEG_NOTE_USERPROP:
4550                 str = gettext("Allows changing any user property");
4551                 break;
4552         case ZFS_DELEG_NOTE_USERQUOTA:
4553                 str = gettext("Allows accessing any userquota@... property");
4554                 break;
4555         case ZFS_DELEG_NOTE_USERUSED:
4556                 str = gettext("Allows reading any userused@... property");
4557                 break;
4558                 /* other */
4559         default:
4560                 str = "";
4561         }
4562 
4563         return (str);
4564 }
4565 
4566 struct allow_opts {
4567         boolean_t local;
4568         boolean_t descend;
4569         boolean_t user;
4570         boolean_t group;
4571         boolean_t everyone;
4572         boolean_t create;
4573         boolean_t set;
4574         boolean_t recursive; /* unallow only */
4575         boolean_t prt_usage;
4576 
4577         boolean_t prt_perms;
4578         char *who;
4579         char *perms;
4580         const char *dataset;
4581 };
4582 
4583 static inline int
4584 prop_cmp(const void *a, const void *b)
4585 {
4586         const char *str1 = *(const char **)a;
4587         const char *str2 = *(const char **)b;
4588         return (strcmp(str1, str2));
4589 }
4590 
4591 static void
4592 allow_usage(boolean_t un, boolean_t requested, const char *msg)
4593 {
4594         const char *opt_desc[] = {
4595                 "-h", gettext("show this help message and exit"),
4596                 "-l", gettext("set permission locally"),
4597                 "-d", gettext("set permission for descents"),
4598                 "-u", gettext("set permission for user"),
4599                 "-g", gettext("set permission for group"),
4600                 "-e", gettext("set permission for everyone"),
4601                 "-c", gettext("set create time permission"),
4602                 "-s", gettext("define permission set"),
4603                 /* unallow only */
4604                 "-r", gettext("remove permissions recursively"),
4605         };
4606         size_t unallow_size = sizeof (opt_desc) / sizeof (char *);
4607         size_t allow_size = unallow_size - 2;
4608         const char *props[ZFS_NUM_PROPS];
4609         int i;
4610         size_t count = 0;
4611         FILE *fp = requested ? stdout : stderr;
4612         zprop_desc_t *pdtbl = zfs_prop_get_table();
4613         const char *fmt = gettext("%-16s %-14s\t%s\n");
4614 
4615         (void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW :
4616             HELP_ALLOW));
4617         (void) fprintf(fp, gettext("Options:\n"));
4618         for (int i = 0; i < (un ? unallow_size : allow_size); i++) {
4619                 const char *opt = opt_desc[i++];
4620                 const char *optdsc = opt_desc[i];
4621                 (void) fprintf(fp, gettext("  %-10s  %s\n"), opt, optdsc);
4622         }
4623 
4624         (void) fprintf(fp, gettext("\nThe following permissions are "
4625             "supported:\n\n"));
4626         (void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"),
4627             gettext("NOTES"));
4628         for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) {
4629                 const char *perm_name = zfs_deleg_perm_tbl[i].z_perm;
4630                 zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note;
4631                 const char *perm_type = deleg_perm_type(perm_note);
4632                 const char *perm_comment = deleg_perm_comment(perm_note);
4633                 (void) fprintf(fp, fmt, perm_name, perm_type, perm_comment);
4634         }
4635 
4636         for (i = 0; i < ZFS_NUM_PROPS; i++) {
4637                 zprop_desc_t *pd = &pdtbl[i];
4638                 if (pd->pd_visible != B_TRUE)
4639                         continue;
4640 
4641                 if (pd->pd_attr == PROP_READONLY)
4642                         continue;
4643 
4644                 props[count++] = pd->pd_name;
4645         }
4646         props[count] = NULL;
4647 
4648         qsort(props, count, sizeof (char *), prop_cmp);
4649 
4650         for (i = 0; i < count; i++)
4651                 (void) fprintf(fp, fmt, props[i], gettext("property"), "");
4652 
4653         if (msg != NULL)
4654                 (void) fprintf(fp, gettext("\nzfs: error: %s"), msg);
4655 
4656         exit(requested ? 0 : 2);
4657 }
4658 
4659 static inline const char *
4660 munge_args(int argc, char **argv, boolean_t un, size_t expected_argc,
4661     char **permsp)
4662 {
4663         if (un && argc == expected_argc - 1)
4664                 *permsp = NULL;
4665         else if (argc == expected_argc)
4666                 *permsp = argv[argc - 2];
4667         else
4668                 allow_usage(un, B_FALSE,
4669                     gettext("wrong number of parameters\n"));
4670 
4671         return (argv[argc - 1]);
4672 }
4673 
4674 static void
4675 parse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts)
4676 {
4677         int uge_sum = opts->user + opts->group + opts->everyone;
4678         int csuge_sum = opts->create + opts->set + uge_sum;
4679         int ldcsuge_sum = csuge_sum + opts->local + opts->descend;
4680         int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum;
4681 
4682         if (uge_sum > 1)
4683                 allow_usage(un, B_FALSE,
4684                     gettext("-u, -g, and -e are mutually exclusive\n"));
4685 
4686         if (opts->prt_usage)
4687                 if (argc == 0 && all_sum == 0)
4688                         allow_usage(un, B_TRUE, NULL);
4689                 else
4690                         usage(B_FALSE);
4691 
4692         if (opts->set) {
4693                 if (csuge_sum > 1)
4694                         allow_usage(un, B_FALSE,
4695                             gettext("invalid options combined with -s\n"));
4696 
4697                 opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
4698                 if (argv[0][0] != '@')
4699                         allow_usage(un, B_FALSE,
4700                             gettext("invalid set name: missing '@' prefix\n"));
4701                 opts->who = argv[0];
4702         } else if (opts->create) {
4703                 if (ldcsuge_sum > 1)
4704                         allow_usage(un, B_FALSE,
4705                             gettext("invalid options combined with -c\n"));
4706                 opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
4707         } else if (opts->everyone) {
4708                 if (csuge_sum > 1)
4709                         allow_usage(un, B_FALSE,
4710                             gettext("invalid options combined with -e\n"));
4711                 opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
4712         } else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone")
4713             == 0) {
4714                 opts->everyone = B_TRUE;
4715                 argc--;
4716                 argv++;
4717                 opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
4718         } else if (argc == 1 && !un) {
4719                 opts->prt_perms = B_TRUE;
4720                 opts->dataset = argv[argc-1];
4721         } else {
4722                 opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
4723                 opts->who = argv[0];
4724         }
4725 
4726         if (!opts->local && !opts->descend) {
4727                 opts->local = B_TRUE;
4728                 opts->descend = B_TRUE;
4729         }
4730 }
4731 
4732 static void
4733 store_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend,
4734     const char *who, char *perms, nvlist_t *top_nvl)
4735 {
4736         int i;
4737         char ld[2] = { '\0', '\0' };
4738         char who_buf[ZFS_MAXNAMELEN+32];
4739         char base_type;
4740         char set_type;
4741         nvlist_t *base_nvl = NULL;
4742         nvlist_t *set_nvl = NULL;
4743         nvlist_t *nvl;
4744 
4745         if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0)
4746                 nomem();
4747         if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) !=  0)
4748                 nomem();
4749 
4750         switch (type) {
4751         case ZFS_DELEG_NAMED_SET_SETS:
4752         case ZFS_DELEG_NAMED_SET:
4753                 set_type = ZFS_DELEG_NAMED_SET_SETS;
4754                 base_type = ZFS_DELEG_NAMED_SET;
4755                 ld[0] = ZFS_DELEG_NA;
4756                 break;
4757         case ZFS_DELEG_CREATE_SETS:
4758         case ZFS_DELEG_CREATE:
4759                 set_type = ZFS_DELEG_CREATE_SETS;
4760                 base_type = ZFS_DELEG_CREATE;
4761                 ld[0] = ZFS_DELEG_NA;
4762                 break;
4763         case ZFS_DELEG_USER_SETS:
4764         case ZFS_DELEG_USER:
4765                 set_type = ZFS_DELEG_USER_SETS;
4766                 base_type = ZFS_DELEG_USER;
4767                 if (local)
4768                         ld[0] = ZFS_DELEG_LOCAL;
4769                 if (descend)
4770                         ld[1] = ZFS_DELEG_DESCENDENT;
4771                 break;
4772         case ZFS_DELEG_GROUP_SETS:
4773         case ZFS_DELEG_GROUP:
4774                 set_type = ZFS_DELEG_GROUP_SETS;
4775                 base_type = ZFS_DELEG_GROUP;
4776                 if (local)
4777                         ld[0] = ZFS_DELEG_LOCAL;
4778                 if (descend)
4779                         ld[1] = ZFS_DELEG_DESCENDENT;
4780                 break;
4781         case ZFS_DELEG_EVERYONE_SETS:
4782         case ZFS_DELEG_EVERYONE:
4783                 set_type = ZFS_DELEG_EVERYONE_SETS;
4784                 base_type = ZFS_DELEG_EVERYONE;
4785                 if (local)
4786                         ld[0] = ZFS_DELEG_LOCAL;
4787                 if (descend)
4788                         ld[1] = ZFS_DELEG_DESCENDENT;
4789         }
4790 
4791         if (perms != NULL) {
4792                 char *curr = perms;
4793                 char *end = curr + strlen(perms);
4794 
4795                 while (curr < end) {
4796                         char *delim = strchr(curr, ',');
4797                         if (delim == NULL)
4798                                 delim = end;
4799                         else
4800                                 *delim = '\0';
4801 
4802                         if (curr[0] == '@')
4803                                 nvl = set_nvl;
4804                         else
4805                                 nvl = base_nvl;
4806 
4807                         (void) nvlist_add_boolean(nvl, curr);
4808                         if (delim != end)
4809                                 *delim = ',';
4810                         curr = delim + 1;
4811                 }
4812 
4813                 for (i = 0; i < 2; i++) {
4814                         char locality = ld[i];
4815                         if (locality == 0)
4816                                 continue;
4817 
4818                         if (!nvlist_empty(base_nvl)) {
4819                                 if (who != NULL)
4820                                         (void) snprintf(who_buf,
4821                                             sizeof (who_buf), "%c%c$%s",
4822                                             base_type, locality, who);
4823                                 else
4824                                         (void) snprintf(who_buf,
4825                                             sizeof (who_buf), "%c%c$",
4826                                             base_type, locality);
4827 
4828                                 (void) nvlist_add_nvlist(top_nvl, who_buf,
4829                                     base_nvl);
4830                         }
4831 
4832 
4833                         if (!nvlist_empty(set_nvl)) {
4834                                 if (who != NULL)
4835                                         (void) snprintf(who_buf,
4836                                             sizeof (who_buf), "%c%c$%s",
4837                                             set_type, locality, who);
4838                                 else
4839                                         (void) snprintf(who_buf,
4840                                             sizeof (who_buf), "%c%c$",
4841                                             set_type, locality);
4842 
4843                                 (void) nvlist_add_nvlist(top_nvl, who_buf,
4844                                     set_nvl);
4845                         }
4846                 }
4847         } else {
4848                 for (i = 0; i < 2; i++) {
4849                         char locality = ld[i];
4850                         if (locality == 0)
4851                                 continue;
4852 
4853                         if (who != NULL)
4854                                 (void) snprintf(who_buf, sizeof (who_buf),
4855                                     "%c%c$%s", base_type, locality, who);
4856                         else
4857                                 (void) snprintf(who_buf, sizeof (who_buf),
4858                                     "%c%c$", base_type, locality);
4859                         (void) nvlist_add_boolean(top_nvl, who_buf);
4860 
4861                         if (who != NULL)
4862                                 (void) snprintf(who_buf, sizeof (who_buf),
4863                                     "%c%c$%s", set_type, locality, who);
4864                         else
4865                                 (void) snprintf(who_buf, sizeof (who_buf),
4866                                     "%c%c$", set_type, locality);
4867                         (void) nvlist_add_boolean(top_nvl, who_buf);
4868                 }
4869         }
4870 }
4871 
4872 static int
4873 construct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp)
4874 {
4875         if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0)
4876                 nomem();
4877 
4878         if (opts->set) {
4879                 store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local,
4880                     opts->descend, opts->who, opts->perms, *nvlp);
4881         } else if (opts->create) {
4882                 store_allow_perm(ZFS_DELEG_CREATE, opts->local,
4883                     opts->descend, NULL, opts->perms, *nvlp);
4884         } else if (opts->everyone) {
4885                 store_allow_perm(ZFS_DELEG_EVERYONE, opts->local,
4886                     opts->descend, NULL, opts->perms, *nvlp);
4887         } else {
4888                 char *curr = opts->who;
4889                 char *end = curr + strlen(curr);
4890 
4891                 while (curr < end) {
4892                         const char *who;
4893                         zfs_deleg_who_type_t who_type;
4894                         char *endch;
4895                         char *delim = strchr(curr, ',');
4896                         char errbuf[256];
4897                         char id[64];
4898                         struct passwd *p = NULL;
4899                         struct group *g = NULL;
4900 
4901                         uid_t rid;
4902                         if (delim == NULL)
4903                                 delim = end;
4904                         else
4905                                 *delim = '\0';
4906 
4907                         rid = (uid_t)strtol(curr, &endch, 0);
4908                         if (opts->user) {
4909                                 who_type = ZFS_DELEG_USER;
4910                                 if (*endch != '\0')
4911                                         p = getpwnam(curr);
4912                                 else
4913                                         p = getpwuid(rid);
4914 
4915                                 if (p != NULL)
4916                                         rid = p->pw_uid;
4917                                 else {
4918                                         (void) snprintf(errbuf, 256, gettext(
4919                                             "invalid user %s"), curr);
4920                                         allow_usage(un, B_TRUE, errbuf);
4921                                 }
4922                         } else if (opts->group) {
4923                                 who_type = ZFS_DELEG_GROUP;
4924                                 if (*endch != '\0')
4925                                         g = getgrnam(curr);
4926                                 else
4927                                         g = getgrgid(rid);
4928 
4929                                 if (g != NULL)
4930                                         rid = g->gr_gid;
4931                                 else {
4932                                         (void) snprintf(errbuf, 256, gettext(
4933                                             "invalid group %s"),  curr);
4934                                         allow_usage(un, B_TRUE, errbuf);
4935                                 }
4936                         } else {
4937                                 if (*endch != '\0') {
4938                                         p = getpwnam(curr);
4939                                 } else {
4940                                         p = getpwuid(rid);
4941                                 }
4942 
4943                                 if (p == NULL)
4944                                         if (*endch != '\0') {
4945                                                 g = getgrnam(curr);
4946                                         } else {
4947                                                 g = getgrgid(rid);
4948                                         }
4949 
4950                                 if (p != NULL) {
4951                                         who_type = ZFS_DELEG_USER;
4952                                         rid = p->pw_uid;
4953                                 } else if (g != NULL) {
4954                                         who_type = ZFS_DELEG_GROUP;
4955                                         rid = g->gr_gid;
4956                                 } else {
4957                                         (void) snprintf(errbuf, 256, gettext(
4958                                             "invalid user/group %s"), curr);
4959                                         allow_usage(un, B_TRUE, errbuf);
4960                                 }
4961                         }
4962 
4963                         (void) sprintf(id, "%u", rid);
4964                         who = id;
4965 
4966                         store_allow_perm(who_type, opts->local,
4967                             opts->descend, who, opts->perms, *nvlp);
4968                         curr = delim + 1;
4969                 }
4970         }
4971 
4972         return (0);
4973 }
4974 
4975 static void
4976 print_set_creat_perms(uu_avl_t *who_avl)
4977 {
4978         const char *sc_title[] = {
4979                 gettext("Permission sets:\n"),
4980                 gettext("Create time permissions:\n"),
4981                 NULL
4982         };
4983         const char **title_ptr = sc_title;
4984         who_perm_node_t *who_node = NULL;
4985         int prev_weight = -1;
4986 
4987         for (who_node = uu_avl_first(who_avl); who_node != NULL;
4988             who_node = uu_avl_next(who_avl, who_node)) {
4989                 uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
4990                 zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
4991                 const char *who_name = who_node->who_perm.who_name;
4992                 int weight = who_type2weight(who_type);
4993                 boolean_t first = B_TRUE;
4994                 deleg_perm_node_t *deleg_node;
4995 
4996                 if (prev_weight != weight) {
4997                         (void) printf(*title_ptr++);
4998                         prev_weight = weight;
4999                 }
5000 
5001                 if (who_name == NULL || strnlen(who_name, 1) == 0)
5002                         (void) printf("\t");
5003                 else
5004                         (void) printf("\t%s ", who_name);
5005 
5006                 for (deleg_node = uu_avl_first(avl); deleg_node != NULL;
5007                     deleg_node = uu_avl_next(avl, deleg_node)) {
5008                         if (first) {
5009                                 (void) printf("%s",
5010                                     deleg_node->dpn_perm.dp_name);
5011                                 first = B_FALSE;
5012                         } else
5013                                 (void) printf(",%s",
5014                                     deleg_node->dpn_perm.dp_name);
5015                 }
5016 
5017                 (void) printf("\n");
5018         }
5019 }
5020 
5021 static void inline
5022 print_uge_deleg_perms(uu_avl_t *who_avl, boolean_t local, boolean_t descend,
5023     const char *title)
5024 {
5025         who_perm_node_t *who_node = NULL;
5026         boolean_t prt_title = B_TRUE;
5027         uu_avl_walk_t *walk;
5028 
5029         if ((walk = uu_avl_walk_start(who_avl, UU_WALK_ROBUST)) == NULL)
5030                 nomem();
5031 
5032         while ((who_node = uu_avl_walk_next(walk)) != NULL) {
5033                 const char *who_name = who_node->who_perm.who_name;
5034                 const char *nice_who_name = who_node->who_perm.who_ug_name;
5035                 uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
5036                 zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
5037                 char delim = ' ';
5038                 deleg_perm_node_t *deleg_node;
5039                 boolean_t prt_who = B_TRUE;
5040 
5041                 for (deleg_node = uu_avl_first(avl);
5042                     deleg_node != NULL;
5043                     deleg_node = uu_avl_next(avl, deleg_node)) {
5044                         if (local != deleg_node->dpn_perm.dp_local ||
5045                             descend != deleg_node->dpn_perm.dp_descend)
5046                                 continue;
5047 
5048                         if (prt_who) {
5049                                 const char *who = NULL;
5050                                 if (prt_title) {
5051                                         prt_title = B_FALSE;
5052                                         (void) printf(title);
5053                                 }
5054 
5055                                 switch (who_type) {
5056                                 case ZFS_DELEG_USER_SETS:
5057                                 case ZFS_DELEG_USER:
5058                                         who = gettext("user");
5059                                         if (nice_who_name)
5060                                                 who_name  = nice_who_name;
5061                                         break;
5062                                 case ZFS_DELEG_GROUP_SETS:
5063                                 case ZFS_DELEG_GROUP:
5064                                         who = gettext("group");
5065                                         if (nice_who_name)
5066                                                 who_name  = nice_who_name;
5067                                         break;
5068                                 case ZFS_DELEG_EVERYONE_SETS:
5069                                 case ZFS_DELEG_EVERYONE:
5070                                         who = gettext("everyone");
5071                                         who_name = NULL;
5072                                 }
5073 
5074                                 prt_who = B_FALSE;
5075                                 if (who_name == NULL)
5076                                         (void) printf("\t%s", who);
5077                                 else
5078                                         (void) printf("\t%s %s", who, who_name);
5079                         }
5080 
5081                         (void) printf("%c%s", delim,
5082                             deleg_node->dpn_perm.dp_name);
5083                         delim = ',';
5084                 }
5085 
5086                 if (!prt_who)
5087                         (void) printf("\n");
5088         }
5089 
5090         uu_avl_walk_end(walk);
5091 }
5092 
5093 static void
5094 print_fs_perms(fs_perm_set_t *fspset)
5095 {
5096         fs_perm_node_t *node = NULL;
5097         char buf[ZFS_MAXNAMELEN+32];
5098         const char *dsname = buf;
5099 
5100         for (node = uu_list_first(fspset->fsps_list); node != NULL;
5101             node = uu_list_next(fspset->fsps_list, node)) {
5102                 uu_avl_t *sc_avl = node->fspn_fsperm.fsp_sc_avl;
5103                 uu_avl_t *uge_avl = node->fspn_fsperm.fsp_uge_avl;
5104                 int left = 0;
5105 
5106                 (void) snprintf(buf, ZFS_MAXNAMELEN+32,
5107                     gettext("---- Permissions on %s "),
5108                     node->fspn_fsperm.fsp_name);
5109                 (void) printf(dsname);
5110                 left = 70 - strlen(buf);
5111                 while (left-- > 0)
5112                         (void) printf("-");
5113                 (void) printf("\n");
5114 
5115                 print_set_creat_perms(sc_avl);
5116                 print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE,
5117                     gettext("Local permissions:\n"));
5118                 print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE,
5119                     gettext("Descendent permissions:\n"));
5120                 print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE,
5121                     gettext("Local+Descendent permissions:\n"));
5122         }
5123 }
5124 
5125 static fs_perm_set_t fs_perm_set = { NULL, NULL, NULL, NULL };
5126 
5127 struct deleg_perms {
5128         boolean_t un;
5129         nvlist_t *nvl;
5130 };
5131 
5132 static int
5133 set_deleg_perms(zfs_handle_t *zhp, void *data)
5134 {
5135         struct deleg_perms *perms = (struct deleg_perms *)data;
5136         zfs_type_t zfs_type = zfs_get_type(zhp);
5137 
5138         if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME)
5139                 return (0);
5140 
5141         return (zfs_set_fsacl(zhp, perms->un, perms->nvl));
5142 }
5143 
5144 static int
5145 zfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un)
5146 {
5147         zfs_handle_t *zhp;
5148         nvlist_t *perm_nvl = NULL;
5149         nvlist_t *update_perm_nvl = NULL;
5150         int error = 1;
5151         int c;
5152         struct allow_opts opts = { 0 };
5153 
5154         const char *optstr = un ? "ldugecsrh" : "ldugecsh";
5155 
5156         /* check opts */
5157         while ((c = getopt(argc, argv, optstr)) != -1) {
5158                 switch (c) {
5159                 case 'l':
5160                         opts.local = B_TRUE;
5161                         break;
5162                 case 'd':
5163                         opts.descend = B_TRUE;
5164                         break;
5165                 case 'u':
5166                         opts.user = B_TRUE;
5167                         break;
5168                 case 'g':
5169                         opts.group = B_TRUE;
5170                         break;
5171                 case 'e':
5172                         opts.everyone = B_TRUE;
5173                         break;
5174                 case 's':
5175                         opts.set = B_TRUE;
5176                         break;
5177                 case 'c':
5178                         opts.create = B_TRUE;
5179                         break;
5180                 case 'r':
5181                         opts.recursive = B_TRUE;
5182                         break;
5183                 case ':':
5184                         (void) fprintf(stderr, gettext("missing argument for "
5185                             "'%c' option\n"), optopt);
5186                         usage(B_FALSE);
5187                         break;
5188                 case 'h':
5189                         opts.prt_usage = B_TRUE;
5190                         break;
5191                 case '?':
5192                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
5193                             optopt);
5194                         usage(B_FALSE);
5195                 }
5196         }
5197 
5198         argc -= optind;
5199         argv += optind;
5200 
5201         /* check arguments */
5202         parse_allow_args(argc, argv, un, &opts);
5203 
5204         /* try to open the dataset */
5205         if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM |
5206             ZFS_TYPE_VOLUME)) == NULL) {
5207                 (void) fprintf(stderr, "Failed to open dataset: %s\n",
5208                     opts.dataset);
5209                 return (-1);
5210         }
5211 
5212         if (zfs_get_fsacl(zhp, &perm_nvl) != 0)
5213                 goto cleanup2;
5214 
5215         fs_perm_set_init(&fs_perm_set);
5216         if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) {
5217                 (void) fprintf(stderr, "Failed to parse fsacl permissions\n");
5218                 goto cleanup1;
5219         }
5220 
5221         if (opts.prt_perms)
5222                 print_fs_perms(&fs_perm_set);
5223         else {
5224                 (void) construct_fsacl_list(un, &opts, &update_perm_nvl);
5225                 if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0)
5226                         goto cleanup0;
5227 
5228                 if (un && opts.recursive) {
5229                         struct deleg_perms data = { un, update_perm_nvl };
5230                         if (zfs_iter_filesystems(zhp, set_deleg_perms,
5231                             &data) != 0)
5232                                 goto cleanup0;
5233                 }
5234         }
5235 
5236         error = 0;
5237 
5238 cleanup0:
5239         nvlist_free(perm_nvl);
5240         if (update_perm_nvl != NULL)
5241                 nvlist_free(update_perm_nvl);
5242 cleanup1:
5243         fs_perm_set_fini(&fs_perm_set);
5244 cleanup2:
5245         zfs_close(zhp);
5246 
5247         return (error);
5248 }
5249 
5250 static int
5251 zfs_do_allow(int argc, char **argv)
5252 {
5253         return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE));
5254 }
5255 
5256 static int
5257 zfs_do_unallow(int argc, char **argv)
5258 {
5259         return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE));
5260 }
5261 
5262 static int
5263 zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
5264 {
5265         int errors = 0;
5266         int i;
5267         const char *tag;
5268         boolean_t recursive = B_FALSE;
5269         const char *opts = holding ? "rt" : "r";
5270         int c;
5271 
5272         /* check options */
5273         while ((c = getopt(argc, argv, opts)) != -1) {
5274                 switch (c) {
5275                 case 'r':
5276                         recursive = B_TRUE;
5277                         break;
5278                 case '?':
5279                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
5280                             optopt);
5281                         usage(B_FALSE);
5282                 }
5283         }
5284 
5285         argc -= optind;
5286         argv += optind;
5287 
5288         /* check number of arguments */
5289         if (argc < 2)
5290                 usage(B_FALSE);
5291 
5292         tag = argv[0];
5293         --argc;
5294         ++argv;
5295 
5296         if (holding && tag[0] == '.') {
5297                 /* tags starting with '.' are reserved for libzfs */
5298                 (void) fprintf(stderr, gettext("tag may not start with '.'\n"));
5299                 usage(B_FALSE);
5300         }
5301 
5302         for (i = 0; i < argc; ++i) {
5303                 zfs_handle_t *zhp;
5304                 char parent[ZFS_MAXNAMELEN];
5305                 const char *delim;
5306                 char *path = argv[i];
5307 
5308                 delim = strchr(path, '@');
5309                 if (delim == NULL) {
5310                         (void) fprintf(stderr,
5311                             gettext("'%s' is not a snapshot\n"), path);
5312                         ++errors;
5313                         continue;
5314                 }
5315                 (void) strncpy(parent, path, delim - path);
5316                 parent[delim - path] = '\0';
5317 
5318                 zhp = zfs_open(g_zfs, parent,
5319                     ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
5320                 if (zhp == NULL) {
5321                         ++errors;
5322                         continue;
5323                 }
5324                 if (holding) {
5325                         if (zfs_hold(zhp, delim+1, tag, recursive, -1) != 0)
5326                                 ++errors;
5327                 } else {
5328                         if (zfs_release(zhp, delim+1, tag, recursive) != 0)
5329                                 ++errors;
5330                 }
5331                 zfs_close(zhp);
5332         }
5333 
5334         return (errors != 0);
5335 }
5336 
5337 /*
5338  * zfs hold [-r] [-t] <tag> <snap> ...
5339  *
5340  *      -r      Recursively hold
5341  *
5342  * Apply a user-hold with the given tag to the list of snapshots.
5343  */
5344 static int
5345 zfs_do_hold(int argc, char **argv)
5346 {
5347         return (zfs_do_hold_rele_impl(argc, argv, B_TRUE));
5348 }
5349 
5350 /*
5351  * zfs release [-r] <tag> <snap> ...
5352  *
5353  *      -r      Recursively release
5354  *
5355  * Release a user-hold with the given tag from the list of snapshots.
5356  */
5357 static int
5358 zfs_do_release(int argc, char **argv)
5359 {
5360         return (zfs_do_hold_rele_impl(argc, argv, B_FALSE));
5361 }
5362 
5363 typedef struct holds_cbdata {
5364         boolean_t       cb_recursive;
5365         const char      *cb_snapname;
5366         nvlist_t        **cb_nvlp;
5367         size_t          cb_max_namelen;
5368         size_t          cb_max_taglen;
5369 } holds_cbdata_t;
5370 
5371 #define STRFTIME_FMT_STR "%a %b %e %k:%M %Y"
5372 #define DATETIME_BUF_LEN (32)
5373 /*
5374  *
5375  */
5376 static void
5377 print_holds(boolean_t scripted, size_t nwidth, size_t tagwidth, nvlist_t *nvl)
5378 {
5379         int i;
5380         nvpair_t *nvp = NULL;
5381         char *hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" };
5382         const char *col;
5383 
5384         if (!scripted) {
5385                 for (i = 0; i < 3; i++) {
5386                         col = gettext(hdr_cols[i]);
5387                         if (i < 2)
5388                                 (void) printf("%-*s  ", i ? tagwidth : nwidth,
5389                                     col);
5390                         else
5391                                 (void) printf("%s\n", col);
5392                 }
5393         }
5394 
5395         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
5396                 char *zname = nvpair_name(nvp);
5397                 nvlist_t *nvl2;
5398                 nvpair_t *nvp2 = NULL;
5399                 (void) nvpair_value_nvlist(nvp, &nvl2);
5400                 while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) {
5401                         char tsbuf[DATETIME_BUF_LEN];
5402                         char *tagname = nvpair_name(nvp2);
5403                         uint64_t val = 0;
5404                         time_t time;
5405                         struct tm t;
5406                         char sep = scripted ? '\t' : ' ';
5407                         size_t sepnum = scripted ? 1 : 2;
5408 
5409                         (void) nvpair_value_uint64(nvp2, &val);
5410                         time = (time_t)val;
5411                         (void) localtime_r(&time, &t);
5412                         (void) strftime(tsbuf, DATETIME_BUF_LEN,
5413                             gettext(STRFTIME_FMT_STR), &t);
5414 
5415                         (void) printf("%-*s%*c%-*s%*c%s\n", nwidth, zname,
5416                             sepnum, sep, tagwidth, tagname, sepnum, sep, tsbuf);
5417                 }
5418         }
5419 }
5420 
5421 /*
5422  * Generic callback function to list a dataset or snapshot.
5423  */
5424 static int
5425 holds_callback(zfs_handle_t *zhp, void *data)
5426 {
5427         holds_cbdata_t *cbp = data;
5428         nvlist_t *top_nvl = *cbp->cb_nvlp;
5429         nvlist_t *nvl = NULL;
5430         nvpair_t *nvp = NULL;
5431         const char *zname = zfs_get_name(zhp);
5432         size_t znamelen = strnlen(zname, ZFS_MAXNAMELEN);
5433 
5434         if (cbp->cb_recursive) {
5435                 const char *snapname;
5436                 char *delim  = strchr(zname, '@');
5437                 if (delim == NULL)
5438                         return (0);
5439 
5440                 snapname = delim + 1;
5441                 if (strcmp(cbp->cb_snapname, snapname))
5442                         return (0);
5443         }
5444 
5445         if (zfs_get_holds(zhp, &nvl) != 0)
5446                 return (-1);
5447 
5448         if (znamelen > cbp->cb_max_namelen)
5449                 cbp->cb_max_namelen  = znamelen;
5450 
5451         while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
5452                 const char *tag = nvpair_name(nvp);
5453                 size_t taglen = strnlen(tag, MAXNAMELEN);
5454                 if (taglen > cbp->cb_max_taglen)
5455                         cbp->cb_max_taglen  = taglen;
5456         }
5457 
5458         return (nvlist_add_nvlist(top_nvl, zname, nvl));
5459 }
5460 
5461 /*
5462  * zfs holds [-r] <snap> ...
5463  *
5464  *      -r      Recursively hold
5465  */
5466 static int
5467 zfs_do_holds(int argc, char **argv)
5468 {
5469         int errors = 0;
5470         int c;
5471         int i;
5472         boolean_t scripted = B_FALSE;
5473         boolean_t recursive = B_FALSE;
5474         const char *opts = "rH";
5475         nvlist_t *nvl;
5476 
5477         int types = ZFS_TYPE_SNAPSHOT;
5478         holds_cbdata_t cb = { 0 };
5479 
5480         int limit = 0;
5481         int ret = 0;
5482         int flags = 0;
5483 
5484         /* check options */
5485         while ((c = getopt(argc, argv, opts)) != -1) {
5486                 switch (c) {
5487                 case 'r':
5488                         recursive = B_TRUE;
5489                         break;
5490                 case 'H':
5491                         scripted = B_TRUE;
5492                         break;
5493                 case '?':
5494                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
5495                             optopt);
5496                         usage(B_FALSE);
5497                 }
5498         }
5499 
5500         if (recursive) {
5501                 types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
5502                 flags |= ZFS_ITER_RECURSE;
5503         }
5504 
5505         argc -= optind;
5506         argv += optind;
5507 
5508         /* check number of arguments */
5509         if (argc < 1)
5510                 usage(B_FALSE);
5511 
5512         if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
5513                 nomem();
5514 
5515         for (i = 0; i < argc; ++i) {
5516                 char *snapshot = argv[i];
5517                 const char *delim;
5518                 const char *snapname;
5519 
5520                 delim = strchr(snapshot, '@');
5521                 if (delim == NULL) {
5522                         (void) fprintf(stderr,
5523                             gettext("'%s' is not a snapshot\n"), snapshot);
5524                         ++errors;
5525                         continue;
5526                 }
5527                 snapname = delim + 1;
5528                 if (recursive)
5529                         snapshot[delim - snapshot] = '\0';
5530 
5531                 cb.cb_recursive = recursive;
5532                 cb.cb_snapname = snapname;
5533                 cb.cb_nvlp = &nvl;
5534 
5535                 /*
5536                  *  1. collect holds data, set format options
5537                  */
5538                 ret = zfs_for_each(argc, argv, flags, types, NULL, NULL, limit,
5539                     holds_callback, &cb);
5540                 if (ret != 0)
5541                         ++errors;
5542         }
5543 
5544         /*
5545          *  2. print holds data
5546          */
5547         print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl);
5548 
5549         if (nvlist_empty(nvl))
5550                 (void) printf(gettext("no datasets available\n"));
5551 
5552         nvlist_free(nvl);
5553 
5554         return (0 != errors);
5555 }
5556 
5557 #define CHECK_SPINNER 30
5558 #define SPINNER_TIME 3          /* seconds */
5559 #define MOUNT_TIME 5            /* seconds */
5560 
5561 static int
5562 get_one_dataset(zfs_handle_t *zhp, void *data)
5563 {
5564         static char *spin[] = { "-", "\\", "|", "/" };
5565         static int spinval = 0;
5566         static int spincheck = 0;
5567         static time_t last_spin_time = (time_t)0;
5568         get_all_cb_t *cbp = data;
5569         zfs_type_t type = zfs_get_type(zhp);
5570 
5571         if (cbp->cb_verbose) {
5572                 if (--spincheck < 0) {
5573                         time_t now = time(NULL);
5574                         if (last_spin_time + SPINNER_TIME < now) {
5575                                 update_progress(spin[spinval++ % 4]);
5576                                 last_spin_time = now;
5577                         }
5578                         spincheck = CHECK_SPINNER;
5579                 }
5580         }
5581 
5582         /*
5583          * Interate over any nested datasets.
5584          */
5585         if (zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) {
5586                 zfs_close(zhp);
5587                 return (1);
5588         }
5589 
5590         /*
5591          * Skip any datasets whose type does not match.
5592          */
5593         if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
5594                 zfs_close(zhp);
5595                 return (0);
5596         }
5597         libzfs_add_handle(cbp, zhp);
5598         assert(cbp->cb_used <= cbp->cb_alloc);
5599 
5600         return (0);
5601 }
5602 
5603 static void
5604 get_all_datasets(zfs_handle_t ***dslist, size_t *count, boolean_t verbose)
5605 {
5606         get_all_cb_t cb = { 0 };
5607         cb.cb_verbose = verbose;
5608         cb.cb_getone = get_one_dataset;
5609 
5610         if (verbose)
5611                 set_progress_header(gettext("Reading ZFS config"));
5612         (void) zfs_iter_root(g_zfs, get_one_dataset, &cb);
5613 
5614         *dslist = cb.cb_handles;
5615         *count = cb.cb_used;
5616 
5617         if (verbose)
5618                 finish_progress(gettext("done."));
5619 }
5620 
5621 /*
5622  * Generic callback for sharing or mounting filesystems.  Because the code is so
5623  * similar, we have a common function with an extra parameter to determine which
5624  * mode we are using.
5625  */
5626 #define OP_SHARE        0x1
5627 #define OP_MOUNT        0x2
5628 
5629 /*
5630  * Share or mount a dataset.
5631  */
5632 static int
5633 share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
5634     boolean_t explicit, const char *options)
5635 {
5636         char mountpoint[ZFS_MAXPROPLEN];
5637         char shareopts[ZFS_MAXPROPLEN];
5638         char smbshareopts[ZFS_MAXPROPLEN];
5639         const char *cmdname = op == OP_SHARE ? "share" : "mount";
5640         struct mnttab mnt;
5641         uint64_t zoned, canmount;
5642         boolean_t shared_nfs, shared_smb;
5643 
5644         assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM);
5645 
5646         /*
5647          * Check to make sure we can mount/share this dataset.  If we
5648          * are in the global zone and the filesystem is exported to a
5649          * local zone, or if we are in a local zone and the
5650          * filesystem is not exported, then it is an error.
5651          */
5652         zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
5653 
5654         if (zoned && getzoneid() == GLOBAL_ZONEID) {
5655                 if (!explicit)
5656                         return (0);
5657 
5658                 (void) fprintf(stderr, gettext("cannot %s '%s': "
5659                     "dataset is exported to a local zone\n"), cmdname,
5660                     zfs_get_name(zhp));
5661                 return (1);
5662 
5663         } else if (!zoned && getzoneid() != GLOBAL_ZONEID) {
5664                 if (!explicit)
5665                         return (0);
5666 
5667                 (void) fprintf(stderr, gettext("cannot %s '%s': "
5668                     "permission denied\n"), cmdname,
5669                     zfs_get_name(zhp));
5670                 return (1);
5671         }
5672 
5673         /*
5674          * Ignore any filesystems which don't apply to us. This
5675          * includes those with a legacy mountpoint, or those with
5676          * legacy share options.
5677          */
5678         verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
5679             sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
5680         verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,
5681             sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);
5682         verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts,
5683             sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0);
5684 
5685         if (op == OP_SHARE && strcmp(shareopts, "off") == 0 &&
5686             strcmp(smbshareopts, "off") == 0) {
5687                 if (!explicit)
5688                         return (0);
5689 
5690                 (void) fprintf(stderr, gettext("cannot share '%s': "
5691                     "legacy share\n"), zfs_get_name(zhp));
5692                 (void) fprintf(stderr, gettext("use share(1M) to "
5693                     "share this filesystem, or set "
5694                     "sharenfs property on\n"));
5695                 return (1);
5696         }
5697 
5698         /*
5699          * We cannot share or mount legacy filesystems. If the
5700          * shareopts is non-legacy but the mountpoint is legacy, we
5701          * treat it as a legacy share.
5702          */
5703         if (strcmp(mountpoint, "legacy") == 0) {
5704                 if (!explicit)
5705                         return (0);
5706 
5707                 (void) fprintf(stderr, gettext("cannot %s '%s': "
5708                     "legacy mountpoint\n"), cmdname, zfs_get_name(zhp));
5709                 (void) fprintf(stderr, gettext("use %s(1M) to "
5710                     "%s this filesystem\n"), cmdname, cmdname);
5711                 return (1);
5712         }
5713 
5714         if (strcmp(mountpoint, "none") == 0) {
5715                 if (!explicit)
5716                         return (0);
5717 
5718                 (void) fprintf(stderr, gettext("cannot %s '%s': no "
5719                     "mountpoint set\n"), cmdname, zfs_get_name(zhp));
5720                 return (1);
5721         }
5722 
5723         /*
5724          * canmount     explicit        outcome
5725          * on           no              pass through
5726          * on           yes             pass through
5727          * off          no              return 0
5728          * off          yes             display error, return 1
5729          * noauto       no              return 0
5730          * noauto       yes             pass through
5731          */
5732         canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
5733         if (canmount == ZFS_CANMOUNT_OFF) {
5734                 if (!explicit)
5735                         return (0);
5736 
5737                 (void) fprintf(stderr, gettext("cannot %s '%s': "
5738                     "'canmount' property is set to 'off'\n"), cmdname,
5739                     zfs_get_name(zhp));
5740                 return (1);
5741         } else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) {
5742                 return (0);
5743         }
5744 
5745         /*
5746          * At this point, we have verified that the mountpoint and/or
5747          * shareopts are appropriate for auto management. If the
5748          * filesystem is already mounted or shared, return (failing
5749          * for explicit requests); otherwise mount or share the
5750          * filesystem.
5751          */
5752         switch (op) {
5753         case OP_SHARE:
5754 
5755                 shared_nfs = zfs_is_shared_nfs(zhp, NULL);
5756                 shared_smb = zfs_is_shared_smb(zhp, NULL);
5757 
5758                 if (shared_nfs && shared_smb ||
5759                     (shared_nfs && strcmp(shareopts, "on") == 0 &&
5760                     strcmp(smbshareopts, "off") == 0) ||
5761                     (shared_smb && strcmp(smbshareopts, "on") == 0 &&
5762                     strcmp(shareopts, "off") == 0)) {
5763                         if (!explicit)
5764                                 return (0);
5765 
5766                         (void) fprintf(stderr, gettext("cannot share "
5767                             "'%s': filesystem already shared\n"),
5768                             zfs_get_name(zhp));
5769                         return (1);
5770                 }
5771 
5772                 if (!zfs_is_mounted(zhp, NULL) &&
5773                     zfs_mount(zhp, NULL, 0) != 0)
5774                         return (1);
5775 
5776                 if (protocol == NULL) {
5777                         if (zfs_shareall(zhp) != 0)
5778                                 return (1);
5779                 } else if (strcmp(protocol, "nfs") == 0) {
5780                         if (zfs_share_nfs(zhp))
5781                                 return (1);
5782                 } else if (strcmp(protocol, "smb") == 0) {
5783                         if (zfs_share_smb(zhp))
5784                                 return (1);
5785                 } else {
5786                         (void) fprintf(stderr, gettext("cannot share "
5787                             "'%s': invalid share type '%s' "
5788                             "specified\n"),
5789                             zfs_get_name(zhp), protocol);
5790                         return (1);
5791                 }
5792 
5793                 break;
5794 
5795         case OP_MOUNT:
5796                 if (options == NULL)
5797                         mnt.mnt_mntopts = "";
5798                 else
5799                         mnt.mnt_mntopts = (char *)options;
5800 
5801                 if (!hasmntopt(&mnt, MNTOPT_REMOUNT) &&
5802                     zfs_is_mounted(zhp, NULL)) {
5803                         if (!explicit)
5804                                 return (0);
5805 
5806                         (void) fprintf(stderr, gettext("cannot mount "
5807                             "'%s': filesystem already mounted\n"),
5808                             zfs_get_name(zhp));
5809                         return (1);
5810                 }
5811 
5812                 if (zfs_mount(zhp, options, flags) != 0)
5813                         return (1);
5814                 break;
5815         }
5816 
5817         return (0);
5818 }
5819 
5820 /*
5821  * Reports progress in the form "(current/total)".  Not thread-safe.
5822  */
5823 static void
5824 report_mount_progress(int current, int total)
5825 {
5826         static time_t last_progress_time = 0;
5827         time_t now = time(NULL);
5828         char info[32];
5829 
5830         /* report 1..n instead of 0..n-1 */
5831         ++current;
5832 
5833         /* display header if we're here for the first time */
5834         if (current == 1) {
5835                 set_progress_header(gettext("Mounting ZFS filesystems"));
5836         } else if (current != total && last_progress_time + MOUNT_TIME >= now) {
5837                 /* too soon to report again */
5838                 return;
5839         }
5840 
5841         last_progress_time = now;
5842 
5843         (void) sprintf(info, "(%d/%d)", current, total);
5844 
5845         if (current == total)
5846                 finish_progress(info);
5847         else
5848                 update_progress(info);
5849 }
5850 
5851 static void
5852 append_options(char *mntopts, char *newopts)
5853 {
5854         int len = strlen(mntopts);
5855 
5856         /* original length plus new string to append plus 1 for the comma */
5857         if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) {
5858                 (void) fprintf(stderr, gettext("the opts argument for "
5859                     "'%c' option is too long (more than %d chars)\n"),
5860                     "-o", MNT_LINE_MAX);
5861                 usage(B_FALSE);
5862         }
5863 
5864         if (*mntopts)
5865                 mntopts[len++] = ',';
5866 
5867         (void) strcpy(&mntopts[len], newopts);
5868 }
5869 
5870 static int
5871 share_mount(int op, int argc, char **argv)
5872 {
5873         int do_all = 0;
5874         boolean_t verbose = B_FALSE;
5875         int c, ret = 0;
5876         char *options = NULL;
5877         int flags = 0;
5878 
5879         /* check options */
5880         while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:O" : "a"))
5881             != -1) {
5882                 switch (c) {
5883                 case 'a':
5884                         do_all = 1;
5885                         break;
5886                 case 'v':
5887                         verbose = B_TRUE;
5888                         break;
5889                 case 'o':
5890                         if (*optarg == '\0') {
5891                                 (void) fprintf(stderr, gettext("empty mount "
5892                                     "options (-o) specified\n"));
5893                                 usage(B_FALSE);
5894                         }
5895 
5896                         if (options == NULL)
5897                                 options = safe_malloc(MNT_LINE_MAX + 1);
5898 
5899                         /* option validation is done later */
5900                         append_options(options, optarg);
5901                         break;
5902 
5903                 case 'O':
5904                         flags |= MS_OVERLAY;
5905                         break;
5906                 case ':':
5907                         (void) fprintf(stderr, gettext("missing argument for "
5908                             "'%c' option\n"), optopt);
5909                         usage(B_FALSE);
5910                         break;
5911                 case '?':
5912                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
5913                             optopt);
5914                         usage(B_FALSE);
5915                 }
5916         }
5917 
5918         argc -= optind;
5919         argv += optind;
5920 
5921         /* check number of arguments */
5922         if (do_all) {
5923                 zfs_handle_t **dslist = NULL;
5924                 size_t i, count = 0;
5925                 char *protocol = NULL;
5926 
5927                 if (op == OP_SHARE && argc > 0) {
5928                         if (strcmp(argv[0], "nfs") != 0 &&
5929                             strcmp(argv[0], "smb") != 0) {
5930                                 (void) fprintf(stderr, gettext("share type "
5931                                     "must be 'nfs' or 'smb'\n"));
5932                                 usage(B_FALSE);
5933                         }
5934                         protocol = argv[0];
5935                         argc--;
5936                         argv++;
5937                 }
5938 
5939                 if (argc != 0) {
5940                         (void) fprintf(stderr, gettext("too many arguments\n"));
5941                         usage(B_FALSE);
5942                 }
5943 
5944                 start_progress_timer();
5945                 get_all_datasets(&dslist, &count, verbose);
5946 
5947                 if (count == 0)
5948                         return (0);
5949 
5950                 qsort(dslist, count, sizeof (void *), libzfs_dataset_cmp);
5951 
5952                 for (i = 0; i < count; i++) {
5953                         if (verbose)
5954                                 report_mount_progress(i, count);
5955 
5956                         if (share_mount_one(dslist[i], op, flags, protocol,
5957                             B_FALSE, options) != 0)
5958                                 ret = 1;
5959                         zfs_close(dslist[i]);
5960                 }
5961 
5962                 free(dslist);
5963         } else if (argc == 0) {
5964                 struct mnttab entry;
5965 
5966                 if ((op == OP_SHARE) || (options != NULL)) {
5967                         (void) fprintf(stderr, gettext("missing filesystem "
5968                             "argument (specify -a for all)\n"));
5969                         usage(B_FALSE);
5970                 }
5971 
5972                 /*
5973                  * When mount is given no arguments, go through /etc/mnttab and
5974                  * display any active ZFS mounts.  We hide any snapshots, since
5975                  * they are controlled automatically.
5976                  */
5977                 rewind(mnttab_file);
5978                 while (getmntent(mnttab_file, &entry) == 0) {
5979                         if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 ||
5980                             strchr(entry.mnt_special, '@') != NULL)
5981                                 continue;
5982 
5983                         (void) printf("%-30s  %s\n", entry.mnt_special,
5984                             entry.mnt_mountp);
5985                 }
5986 
5987         } else {
5988                 zfs_handle_t *zhp;
5989 
5990                 if (argc > 1) {
5991                         (void) fprintf(stderr,
5992                             gettext("too many arguments\n"));
5993                         usage(B_FALSE);
5994                 }
5995 
5996                 if ((zhp = zfs_open(g_zfs, argv[0],
5997                     ZFS_TYPE_FILESYSTEM)) == NULL) {
5998                         ret = 1;
5999                 } else {
6000                         ret = share_mount_one(zhp, op, flags, NULL, B_TRUE,
6001                             options);
6002                         zfs_close(zhp);
6003                 }
6004         }
6005 
6006         return (ret);
6007 }
6008 
6009 /*
6010  * zfs mount -a [nfs]
6011  * zfs mount filesystem
6012  *
6013  * Mount all filesystems, or mount the given filesystem.
6014  */
6015 static int
6016 zfs_do_mount(int argc, char **argv)
6017 {
6018         return (share_mount(OP_MOUNT, argc, argv));
6019 }
6020 
6021 /*
6022  * zfs share -a [nfs | smb]
6023  * zfs share filesystem
6024  *
6025  * Share all filesystems, or share the given filesystem.
6026  */
6027 static int
6028 zfs_do_share(int argc, char **argv)
6029 {
6030         return (share_mount(OP_SHARE, argc, argv));
6031 }
6032 
6033 typedef struct unshare_unmount_node {
6034         zfs_handle_t    *un_zhp;
6035         char            *un_mountp;
6036         uu_avl_node_t   un_avlnode;
6037 } unshare_unmount_node_t;
6038 
6039 /* ARGSUSED */
6040 static int
6041 unshare_unmount_compare(const void *larg, const void *rarg, void *unused)
6042 {
6043         const unshare_unmount_node_t *l = larg;
6044         const unshare_unmount_node_t *r = rarg;
6045 
6046         return (strcmp(l->un_mountp, r->un_mountp));
6047 }
6048 
6049 /*
6050  * Convenience routine used by zfs_do_umount() and manual_unmount().  Given an
6051  * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem,
6052  * and unmount it appropriately.
6053  */
6054 static int
6055 unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
6056 {
6057         zfs_handle_t *zhp;
6058         int ret = 0;
6059         struct stat64 statbuf;
6060         struct extmnttab entry;
6061         const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount";
6062         ino_t path_inode;
6063 
6064         /*
6065          * Search for the path in /etc/mnttab.  Rather than looking for the
6066          * specific path, which can be fooled by non-standard paths (i.e. ".."
6067          * or "//"), we stat() the path and search for the corresponding
6068          * (major,minor) device pair.
6069          */
6070         if (stat64(path, &statbuf) != 0) {
6071                 (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
6072                     cmdname, path, strerror(errno));
6073                 return (1);
6074         }
6075         path_inode = statbuf.st_ino;
6076 
6077         /*
6078          * Search for the given (major,minor) pair in the mount table.
6079          */
6080         rewind(mnttab_file);
6081         while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) {
6082                 if (entry.mnt_major == major(statbuf.st_dev) &&
6083                     entry.mnt_minor == minor(statbuf.st_dev))
6084                         break;
6085         }
6086         if (ret != 0) {
6087                 if (op == OP_SHARE) {
6088                         (void) fprintf(stderr, gettext("cannot %s '%s': not "
6089                             "currently mounted\n"), cmdname, path);
6090                         return (1);
6091                 }
6092                 (void) fprintf(stderr, gettext("warning: %s not in mnttab\n"),
6093                     path);
6094                 if ((ret = umount2(path, flags)) != 0)
6095                         (void) fprintf(stderr, gettext("%s: %s\n"), path,
6096                             strerror(errno));
6097                 return (ret != 0);
6098         }
6099 
6100         if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) {
6101                 (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS "
6102                     "filesystem\n"), cmdname, path);
6103                 return (1);
6104         }
6105 
6106         if ((zhp = zfs_open(g_zfs, entry.mnt_special,
6107             ZFS_TYPE_FILESYSTEM)) == NULL)
6108                 return (1);
6109 
6110         ret = 1;
6111         if (stat64(entry.mnt_mountp, &statbuf) != 0) {
6112                 (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
6113                     cmdname, path, strerror(errno));
6114                 goto out;
6115         } else if (statbuf.st_ino != path_inode) {
6116                 (void) fprintf(stderr, gettext("cannot "
6117                     "%s '%s': not a mountpoint\n"), cmdname, path);
6118                 goto out;
6119         }
6120 
6121         if (op == OP_SHARE) {
6122                 char nfs_mnt_prop[ZFS_MAXPROPLEN];
6123                 char smbshare_prop[ZFS_MAXPROPLEN];
6124 
6125                 verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, nfs_mnt_prop,
6126                     sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0);
6127                 verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshare_prop,
6128                     sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0);
6129 
6130                 if (strcmp(nfs_mnt_prop, "off") == 0 &&
6131                     strcmp(smbshare_prop, "off") == 0) {
6132                         (void) fprintf(stderr, gettext("cannot unshare "
6133                             "'%s': legacy share\n"), path);
6134                         (void) fprintf(stderr, gettext("use "
6135                             "unshare(1M) to unshare this filesystem\n"));
6136                 } else if (!zfs_is_shared(zhp)) {
6137                         (void) fprintf(stderr, gettext("cannot unshare '%s': "
6138                             "not currently shared\n"), path);
6139                 } else {
6140                         ret = zfs_unshareall_bypath(zhp, path);
6141                 }
6142         } else {
6143                 char mtpt_prop[ZFS_MAXPROPLEN];
6144 
6145                 verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop,
6146                     sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0);
6147 
6148                 if (is_manual) {
6149                         ret = zfs_unmount(zhp, NULL, flags);
6150                 } else if (strcmp(mtpt_prop, "legacy") == 0) {
6151                         (void) fprintf(stderr, gettext("cannot unmount "
6152                             "'%s': legacy mountpoint\n"),
6153                             zfs_get_name(zhp));
6154                         (void) fprintf(stderr, gettext("use umount(1M) "
6155                             "to unmount this filesystem\n"));
6156                 } else {
6157                         ret = zfs_unmountall(zhp, flags);
6158                 }
6159         }
6160 
6161 out:
6162         zfs_close(zhp);
6163 
6164         return (ret != 0);
6165 }
6166 
6167 /*
6168  * Generic callback for unsharing or unmounting a filesystem.
6169  */
6170 static int
6171 unshare_unmount(int op, int argc, char **argv)
6172 {
6173         int do_all = 0;
6174         int flags = 0;
6175         int ret = 0;
6176         int c;
6177         zfs_handle_t *zhp;
6178         char nfs_mnt_prop[ZFS_MAXPROPLEN];
6179         char sharesmb[ZFS_MAXPROPLEN];
6180 
6181         /* check options */
6182         while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) {
6183                 switch (c) {
6184                 case 'a':
6185                         do_all = 1;
6186                         break;
6187                 case 'f':
6188                         flags = MS_FORCE;
6189                         break;
6190                 case '?':
6191                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
6192                             optopt);
6193                         usage(B_FALSE);
6194                 }
6195         }
6196 
6197         argc -= optind;
6198         argv += optind;
6199 
6200         if (do_all) {
6201                 /*
6202                  * We could make use of zfs_for_each() to walk all datasets in
6203                  * the system, but this would be very inefficient, especially
6204                  * since we would have to linearly search /etc/mnttab for each
6205                  * one.  Instead, do one pass through /etc/mnttab looking for
6206                  * zfs entries and call zfs_unmount() for each one.
6207                  *
6208                  * Things get a little tricky if the administrator has created
6209                  * mountpoints beneath other ZFS filesystems.  In this case, we
6210                  * have to unmount the deepest filesystems first.  To accomplish
6211                  * this, we place all the mountpoints in an AVL tree sorted by
6212                  * the special type (dataset name), and walk the result in
6213                  * reverse to make sure to get any snapshots first.
6214                  */
6215                 struct mnttab entry;
6216                 uu_avl_pool_t *pool;
6217                 uu_avl_t *tree;
6218                 unshare_unmount_node_t *node;
6219                 uu_avl_index_t idx;
6220                 uu_avl_walk_t *walk;
6221 
6222                 if (argc != 0) {
6223                         (void) fprintf(stderr, gettext("too many arguments\n"));
6224                         usage(B_FALSE);
6225                 }
6226 
6227                 if (((pool = uu_avl_pool_create("unmount_pool",
6228                     sizeof (unshare_unmount_node_t),
6229                     offsetof(unshare_unmount_node_t, un_avlnode),
6230                     unshare_unmount_compare, UU_DEFAULT)) == NULL) ||
6231                     ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL))
6232                         nomem();
6233 
6234                 rewind(mnttab_file);
6235                 while (getmntent(mnttab_file, &entry) == 0) {
6236 
6237                         /* ignore non-ZFS entries */
6238                         if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
6239                                 continue;
6240 
6241                         /* ignore snapshots */
6242                         if (strchr(entry.mnt_special, '@') != NULL)
6243                                 continue;
6244 
6245                         if ((zhp = zfs_open(g_zfs, entry.mnt_special,
6246                             ZFS_TYPE_FILESYSTEM)) == NULL) {
6247                                 ret = 1;
6248                                 continue;
6249                         }
6250 
6251                         switch (op) {
6252                         case OP_SHARE:
6253                                 verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
6254                                     nfs_mnt_prop,
6255                                     sizeof (nfs_mnt_prop),
6256                                     NULL, NULL, 0, B_FALSE) == 0);
6257                                 if (strcmp(nfs_mnt_prop, "off") != 0)
6258                                         break;
6259                                 verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
6260                                     nfs_mnt_prop,
6261                                     sizeof (nfs_mnt_prop),
6262                                     NULL, NULL, 0, B_FALSE) == 0);
6263                                 if (strcmp(nfs_mnt_prop, "off") == 0)
6264                                         continue;
6265                                 break;
6266                         case OP_MOUNT:
6267                                 /* Ignore legacy mounts */
6268                                 verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
6269                                     nfs_mnt_prop,
6270                                     sizeof (nfs_mnt_prop),
6271                                     NULL, NULL, 0, B_FALSE) == 0);
6272                                 if (strcmp(nfs_mnt_prop, "legacy") == 0)
6273                                         continue;
6274                                 /* Ignore canmount=noauto mounts */
6275                                 if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) ==
6276                                     ZFS_CANMOUNT_NOAUTO)
6277                                         continue;
6278                         default:
6279                                 break;
6280                         }
6281 
6282                         node = safe_malloc(sizeof (unshare_unmount_node_t));
6283                         node->un_zhp = zhp;
6284                         node->un_mountp = safe_strdup(entry.mnt_mountp);
6285 
6286                         uu_avl_node_init(node, &node->un_avlnode, pool);
6287 
6288                         if (uu_avl_find(tree, node, NULL, &idx) == NULL) {
6289                                 uu_avl_insert(tree, node, idx);
6290                         } else {
6291                                 zfs_close(node->un_zhp);
6292                                 free(node->un_mountp);
6293                                 free(node);
6294                         }
6295                 }
6296 
6297                 /*
6298                  * Walk the AVL tree in reverse, unmounting each filesystem and
6299                  * removing it from the AVL tree in the process.
6300                  */
6301                 if ((walk = uu_avl_walk_start(tree,
6302                     UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL)
6303                         nomem();
6304 
6305                 while ((node = uu_avl_walk_next(walk)) != NULL) {
6306                         uu_avl_remove(tree, node);
6307 
6308                         switch (op) {
6309                         case OP_SHARE:
6310                                 if (zfs_unshareall_bypath(node->un_zhp,
6311                                     node->un_mountp) != 0)
6312                                         ret = 1;
6313                                 break;
6314 
6315                         case OP_MOUNT:
6316                                 if (zfs_unmount(node->un_zhp,
6317                                     node->un_mountp, flags) != 0)
6318                                         ret = 1;
6319                                 break;
6320                         }
6321 
6322                         zfs_close(node->un_zhp);
6323                         free(node->un_mountp);
6324                         free(node);
6325                 }
6326 
6327                 uu_avl_walk_end(walk);
6328                 uu_avl_destroy(tree);
6329                 uu_avl_pool_destroy(pool);
6330 
6331         } else {
6332                 if (argc != 1) {
6333                         if (argc == 0)
6334                                 (void) fprintf(stderr,
6335                                     gettext("missing filesystem argument\n"));
6336                         else
6337                                 (void) fprintf(stderr,
6338                                     gettext("too many arguments\n"));
6339                         usage(B_FALSE);
6340                 }
6341 
6342                 /*
6343                  * We have an argument, but it may be a full path or a ZFS
6344                  * filesystem.  Pass full paths off to unmount_path() (shared by
6345                  * manual_unmount), otherwise open the filesystem and pass to
6346                  * zfs_unmount().
6347                  */
6348                 if (argv[0][0] == '/')
6349                         return (unshare_unmount_path(op, argv[0],
6350                             flags, B_FALSE));
6351 
6352                 if ((zhp = zfs_open(g_zfs, argv[0],
6353                     ZFS_TYPE_FILESYSTEM)) == NULL)
6354                         return (1);
6355 
6356                 verify(zfs_prop_get(zhp, op == OP_SHARE ?
6357                     ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT,
6358                     nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL,
6359                     NULL, 0, B_FALSE) == 0);
6360 
6361                 switch (op) {
6362                 case OP_SHARE:
6363                         verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
6364                             nfs_mnt_prop,
6365                             sizeof (nfs_mnt_prop),
6366                             NULL, NULL, 0, B_FALSE) == 0);
6367                         verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
6368                             sharesmb, sizeof (sharesmb), NULL, NULL,
6369                             0, B_FALSE) == 0);
6370 
6371                         if (strcmp(nfs_mnt_prop, "off") == 0 &&
6372                             strcmp(sharesmb, "off") == 0) {
6373                                 (void) fprintf(stderr, gettext("cannot "
6374                                     "unshare '%s': legacy share\n"),
6375                                     zfs_get_name(zhp));
6376                                 (void) fprintf(stderr, gettext("use "
6377                                     "unshare(1M) to unshare this "
6378                                     "filesystem\n"));
6379                                 ret = 1;
6380                         } else if (!zfs_is_shared(zhp)) {
6381                                 (void) fprintf(stderr, gettext("cannot "
6382                                     "unshare '%s': not currently "
6383                                     "shared\n"), zfs_get_name(zhp));
6384                                 ret = 1;
6385                         } else if (zfs_unshareall(zhp) != 0) {
6386                                 ret = 1;
6387                         }
6388                         break;
6389 
6390                 case OP_MOUNT:
6391                         if (strcmp(nfs_mnt_prop, "legacy") == 0) {
6392                                 (void) fprintf(stderr, gettext("cannot "
6393                                     "unmount '%s': legacy "
6394                                     "mountpoint\n"), zfs_get_name(zhp));
6395                                 (void) fprintf(stderr, gettext("use "
6396                                     "umount(1M) to unmount this "
6397                                     "filesystem\n"));
6398                                 ret = 1;
6399                         } else if (!zfs_is_mounted(zhp, NULL)) {
6400                                 (void) fprintf(stderr, gettext("cannot "
6401                                     "unmount '%s': not currently "
6402                                     "mounted\n"),
6403                                     zfs_get_name(zhp));
6404                                 ret = 1;
6405                         } else if (zfs_unmountall(zhp, flags) != 0) {
6406                                 ret = 1;
6407                         }
6408                         break;
6409                 }
6410 
6411                 zfs_close(zhp);
6412         }
6413 
6414         return (ret);
6415 }
6416 
6417 /*
6418  * zfs unmount -a
6419  * zfs unmount filesystem
6420  *
6421  * Unmount all filesystems, or a specific ZFS filesystem.
6422  */
6423 static int
6424 zfs_do_unmount(int argc, char **argv)
6425 {
6426         return (unshare_unmount(OP_MOUNT, argc, argv));
6427 }
6428 
6429 /*
6430  * zfs unshare -a
6431  * zfs unshare filesystem
6432  *
6433  * Unshare all filesystems, or a specific ZFS filesystem.
6434  */
6435 static int
6436 zfs_do_unshare(int argc, char **argv)
6437 {
6438         return (unshare_unmount(OP_SHARE, argc, argv));
6439 }
6440 
6441 /*
6442  * Called when invoked as /etc/fs/zfs/mount.  Do the mount if the mountpoint is
6443  * 'legacy'.  Otherwise, complain that use should be using 'zfs mount'.
6444  */
6445 static int
6446 manual_mount(int argc, char **argv)
6447 {
6448         zfs_handle_t *zhp;
6449         char mountpoint[ZFS_MAXPROPLEN];
6450         char mntopts[MNT_LINE_MAX] = { '\0' };
6451         int ret = 0;
6452         int c;
6453         int flags = 0;
6454         char *dataset, *path;
6455 
6456         /* check options */
6457         while ((c = getopt(argc, argv, ":mo:O")) != -1) {
6458                 switch (c) {
6459                 case 'o':
6460                         (void) strlcpy(mntopts, optarg, sizeof (mntopts));
6461                         break;
6462                 case 'O':
6463                         flags |= MS_OVERLAY;
6464                         break;
6465                 case 'm':
6466                         flags |= MS_NOMNTTAB;
6467                         break;
6468                 case ':':
6469                         (void) fprintf(stderr, gettext("missing argument for "
6470                             "'%c' option\n"), optopt);
6471                         usage(B_FALSE);
6472                         break;
6473                 case '?':
6474                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
6475                             optopt);
6476                         (void) fprintf(stderr, gettext("usage: mount [-o opts] "
6477                             "<path>\n"));
6478                         return (2);
6479                 }
6480         }
6481 
6482         argc -= optind;
6483         argv += optind;
6484 
6485         /* check that we only have two arguments */
6486         if (argc != 2) {
6487                 if (argc == 0)
6488                         (void) fprintf(stderr, gettext("missing dataset "
6489                             "argument\n"));
6490                 else if (argc == 1)
6491                         (void) fprintf(stderr,
6492                             gettext("missing mountpoint argument\n"));
6493                 else
6494                         (void) fprintf(stderr, gettext("too many arguments\n"));
6495                 (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n");
6496                 return (2);
6497         }
6498 
6499         dataset = argv[0];
6500         path = argv[1];
6501 
6502         /* try to open the dataset */
6503         if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL)
6504                 return (1);
6505 
6506         (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
6507             sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
6508 
6509         /* check for legacy mountpoint and complain appropriately */
6510         ret = 0;
6511         if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
6512                 if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS,
6513                     NULL, 0, mntopts, sizeof (mntopts)) != 0) {
6514                         (void) fprintf(stderr, gettext("mount failed: %s\n"),
6515                             strerror(errno));
6516                         ret = 1;
6517                 }
6518         } else {
6519                 (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
6520                     "mounted using 'mount -F zfs'\n"), dataset);
6521                 (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' "
6522                     "instead.\n"), path);
6523                 (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' "
6524                     "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n"));
6525                 (void) fprintf(stderr, gettext("See zfs(1M) for more "
6526                     "information.\n"));
6527                 ret = 1;
6528         }
6529 
6530         return (ret);
6531 }
6532 
6533 /*
6534  * Called when invoked as /etc/fs/zfs/umount.  Unlike a manual mount, we allow
6535  * unmounts of non-legacy filesystems, as this is the dominant administrative
6536  * interface.
6537  */
6538 static int
6539 manual_unmount(int argc, char **argv)
6540 {
6541         int flags = 0;
6542         int c;
6543 
6544         /* check options */
6545         while ((c = getopt(argc, argv, "f")) != -1) {
6546                 switch (c) {
6547                 case 'f':
6548                         flags = MS_FORCE;
6549                         break;
6550                 case '?':
6551                         (void) fprintf(stderr, gettext("invalid option '%c'\n"),
6552                             optopt);
6553                         (void) fprintf(stderr, gettext("usage: unmount [-f] "
6554                             "<path>\n"));
6555                         return (2);
6556                 }
6557         }
6558 
6559         argc -= optind;
6560         argv += optind;
6561 
6562         /* check arguments */
6563         if (argc != 1) {
6564                 if (argc == 0)
6565                         (void) fprintf(stderr, gettext("missing path "
6566                             "argument\n"));
6567                 else
6568                         (void) fprintf(stderr, gettext("too many arguments\n"));
6569                 (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n"));
6570                 return (2);
6571         }
6572 
6573         return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE));
6574 }
6575 
6576 static int
6577 find_command_idx(char *command, int *idx)
6578 {
6579         int i;
6580 
6581         for (i = 0; i < NCOMMAND; i++) {
6582                 if (command_table[i].name == NULL)
6583                         continue;
6584 
6585                 if (strcmp(command, command_table[i].name) == 0) {
6586                         *idx = i;
6587                         return (0);
6588                 }
6589         }
6590         return (1);
6591 }
6592 
6593 static int
6594 zfs_do_diff(int argc, char **argv)
6595 {
6596         zfs_handle_t *zhp;
6597         int flags = 0;
6598         char *tosnap = NULL;
6599         char *fromsnap = NULL;
6600         char *atp, *copy;
6601         int err = 0;
6602         int c;
6603 
6604         while ((c = getopt(argc, argv, "FHt")) != -1) {
6605                 switch (c) {
6606                 case 'F':
6607                         flags |= ZFS_DIFF_CLASSIFY;
6608                         break;
6609                 case 'H':
6610                         flags |= ZFS_DIFF_PARSEABLE;
6611                         break;
6612                 case 't':
6613                         flags |= ZFS_DIFF_TIMESTAMP;
6614                         break;
6615                 default:
6616                         (void) fprintf(stderr,
6617                             gettext("invalid option '%c'\n"), optopt);
6618                         usage(B_FALSE);
6619                 }
6620         }
6621 
6622         argc -= optind;
6623         argv += optind;
6624 
6625         if (argc < 1) {
6626                 (void) fprintf(stderr,
6627                 gettext("must provide at least one snapshot name\n"));
6628                 usage(B_FALSE);
6629         }
6630 
6631         if (argc > 2) {
6632                 (void) fprintf(stderr, gettext("too many arguments\n"));
6633                 usage(B_FALSE);
6634         }
6635 
6636         fromsnap = argv[0];
6637         tosnap = (argc == 2) ? argv[1] : NULL;
6638 
6639         copy = NULL;
6640         if (*fromsnap != '@')
6641                 copy = strdup(fromsnap);
6642         else if (tosnap)
6643                 copy = strdup(tosnap);
6644         if (copy == NULL)
6645                 usage(B_FALSE);
6646 
6647         if (atp = strchr(copy, '@'))
6648                 *atp = '\0';
6649 
6650         if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL)
6651                 return (1);
6652 
6653         free(copy);
6654 
6655         /*
6656          * Ignore SIGPIPE so that the library can give us
6657          * information on any failure
6658          */
6659         (void) sigignore(SIGPIPE);
6660 
6661         err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags);
6662 
6663         zfs_close(zhp);
6664 
6665         return (err != 0);
6666 }
6667 
6668 /*
6669  * zfs bookmark <fs@snap> <fs#bmark>
6670  *
6671  * Creates a bookmark with the given name from the given snapshot.
6672  */
6673 static int
6674 zfs_do_bookmark(int argc, char **argv)
6675 {
6676         char snapname[ZFS_MAXNAMELEN];
6677         zfs_handle_t *zhp;
6678         nvlist_t *nvl;
6679         int ret = 0;
6680         int c;
6681 
6682         /* check options */
6683         while ((c = getopt(argc, argv, "")) != -1) {
6684                 switch (c) {
6685                 case '?':
6686                         (void) fprintf(stderr,
6687                             gettext("invalid option '%c'\n"), optopt);
6688                         goto usage;
6689                 }
6690         }
6691 
6692         argc -= optind;
6693         argv += optind;
6694 
6695         /* check number of arguments */
6696         if (argc < 1) {
6697                 (void) fprintf(stderr, gettext("missing snapshot argument\n"));
6698                 goto usage;
6699         }
6700         if (argc < 2) {
6701                 (void) fprintf(stderr, gettext("missing bookmark argument\n"));
6702                 goto usage;
6703         }
6704 
6705         if (strchr(argv[1], '#') == NULL) {
6706                 (void) fprintf(stderr,
6707                     gettext("invalid bookmark name '%s' -- "
6708                     "must contain a '#'\n"), argv[1]);
6709                 goto usage;
6710         }
6711 
6712         if (argv[0][0] == '@') {
6713                 /*
6714                  * Snapshot name begins with @.
6715                  * Default to same fs as bookmark.
6716                  */
6717                 (void) strncpy(snapname, argv[1], sizeof (snapname));
6718                 *strchr(snapname, '#') = '\0';
6719                 (void) strlcat(snapname, argv[0], sizeof (snapname));
6720         } else {
6721                 (void) strncpy(snapname, argv[0], sizeof (snapname));
6722         }
6723         zhp = zfs_open(g_zfs, snapname, ZFS_TYPE_SNAPSHOT);
6724         if (zhp == NULL)
6725                 goto usage;
6726         zfs_close(zhp);
6727 
6728 
6729         nvl = fnvlist_alloc();
6730         fnvlist_add_string(nvl, argv[1], snapname);
6731         ret = lzc_bookmark(nvl, NULL);
6732         fnvlist_free(nvl);
6733 
6734         if (ret != 0) {
6735                 const char *err_msg;
6736                 char errbuf[1024];
6737 
6738                 (void) snprintf(errbuf, sizeof (errbuf),
6739                     dgettext(TEXT_DOMAIN,
6740                     "cannot create bookmark '%s'"), argv[1]);
6741 
6742                 switch (ret) {
6743                 case EXDEV:
6744                         err_msg = "bookmark is in a different pool";
6745                         break;
6746                 case EEXIST:
6747                         err_msg = "bookmark exists";
6748                         break;
6749                 case EINVAL:
6750                         err_msg = "invalid argument";
6751                         break;
6752                 case ENOTSUP:
6753                         err_msg = "bookmark feature not enabled";
6754                         break;
6755                 case ENOSPC:
6756                         err_msg = "out of space";
6757                         break;
6758                 default:
6759                         err_msg = "unknown error";
6760                         break;
6761                 }
6762                 (void) fprintf(stderr, "%s: %s\n", errbuf,
6763                     dgettext(TEXT_DOMAIN, err_msg));
6764         }
6765 
6766         return (ret != 0);
6767 
6768 usage:
6769         usage(B_FALSE);
6770         return (-1);
6771 }
6772 
6773 int
6774 main(int argc, char **argv)
6775 {
6776         int ret = 0;
6777         int i;
6778         char *progname;
6779         char *cmdname;
6780 
6781         (void) setlocale(LC_ALL, "");
6782         (void) textdomain(TEXT_DOMAIN);
6783 
6784         opterr = 0;
6785 
6786         if ((g_zfs = libzfs_init()) == NULL) {
6787                 (void) fprintf(stderr, gettext("internal error: failed to "
6788                     "initialize ZFS library\n"));
6789                 return (1);
6790         }
6791 
6792         zfs_save_arguments(argc, argv, history_str, sizeof (history_str));
6793 
6794         libzfs_print_on_error(g_zfs, B_TRUE);
6795 
6796         if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) {
6797                 (void) fprintf(stderr, gettext("internal error: unable to "
6798                     "open %s\n"), MNTTAB);
6799                 return (1);
6800         }
6801 
6802         /*
6803          * This command also doubles as the /etc/fs mount and unmount program.
6804          * Determine if we should take this behavior based on argv[0].
6805          */
6806         progname = basename(argv[0]);
6807         if (strcmp(progname, "mount") == 0) {
6808                 ret = manual_mount(argc, argv);
6809         } else if (strcmp(progname, "umount") == 0) {
6810                 ret = manual_unmount(argc, argv);
6811         } else {
6812                 /*
6813                  * Make sure the user has specified some command.
6814                  */
6815                 if (argc < 2) {
6816                         (void) fprintf(stderr, gettext("missing command\n"));
6817                         usage(B_FALSE);
6818                 }
6819 
6820                 cmdname = argv[1];
6821 
6822                 /*
6823                  * The 'umount' command is an alias for 'unmount'
6824                  */
6825                 if (strcmp(cmdname, "umount") == 0)
6826                         cmdname = "unmount";
6827 
6828                 /*
6829                  * The 'recv' command is an alias for 'receive'
6830                  */
6831                 if (strcmp(cmdname, "recv") == 0)
6832                         cmdname = "receive";
6833 
6834                 /*
6835                  * The 'snap' command is an alias for 'snapshot'
6836                  */
6837                 if (strcmp(cmdname, "snap") == 0)
6838                         cmdname = "snapshot";
6839 
6840                 /*
6841                  * Special case '-?'
6842                  */
6843                 if (strcmp(cmdname, "-?") == 0)
6844                         usage(B_TRUE);
6845 
6846                 /*
6847                  * Run the appropriate command.
6848                  */
6849                 libzfs_mnttab_cache(g_zfs, B_TRUE);
6850                 if (find_command_idx(cmdname, &i) == 0) {
6851                         current_command = &command_table[i];
6852                         ret = command_table[i].func(argc - 1, argv + 1);
6853                 } else if (strchr(cmdname, '=') != NULL) {
6854                         verify(find_command_idx("set", &i) == 0);
6855                         current_command = &command_table[i];
6856                         ret = command_table[i].func(argc, argv);
6857                 } else {
6858                         (void) fprintf(stderr, gettext("unrecognized "
6859                             "command '%s'\n"), cmdname);
6860                         usage(B_FALSE);
6861                 }
6862                 libzfs_mnttab_cache(g_zfs, B_FALSE);
6863         }
6864 
6865         (void) fclose(mnttab_file);
6866 
6867         if (ret == 0 && log_history)
6868                 (void) zpool_log_history(g_zfs, history_str);
6869 
6870         libzfs_fini(g_zfs);
6871 
6872         /*
6873          * The 'ZFS_ABORT' environment variable causes us to dump core on exit
6874          * for the purposes of running ::findleaks.
6875          */
6876         if (getenv("ZFS_ABORT") != NULL) {
6877                 (void) printf("dumping core by request\n");
6878                 abort();
6879         }
6880 
6881         return (ret);
6882 }