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