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