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