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