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