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