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