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