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