1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 51544Seschrock * Common Development and Distribution License (the "License"). 61544Seschrock * You may not use this file except in compliance with the License. 7789Sahrens * 8789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9789Sahrens * or http://www.opensolaris.org/os/licensing. 10789Sahrens * See the License for the specific language governing permissions 11789Sahrens * and limitations under the License. 12789Sahrens * 13789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15789Sahrens * If applicable, add the following below this CDDL HEADER, with the 16789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18789Sahrens * 19789Sahrens * CDDL HEADER END 20789Sahrens */ 21789Sahrens /* 221204Slling * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23789Sahrens * Use is subject to license terms. 24789Sahrens */ 25789Sahrens 26789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 27789Sahrens 28789Sahrens #include <assert.h> 292676Seschrock #include <ctype.h> 30789Sahrens #include <errno.h> 31789Sahrens #include <libgen.h> 32789Sahrens #include <libintl.h> 33789Sahrens #include <libuutil.h> 34789Sahrens #include <locale.h> 35789Sahrens #include <stddef.h> 36789Sahrens #include <stdio.h> 37789Sahrens #include <stdlib.h> 38789Sahrens #include <strings.h> 39789Sahrens #include <unistd.h> 40789Sahrens #include <fcntl.h> 41789Sahrens #include <zone.h> 42789Sahrens #include <sys/mkdev.h> 43789Sahrens #include <sys/mntent.h> 44789Sahrens #include <sys/mnttab.h> 45789Sahrens #include <sys/mount.h> 46789Sahrens #include <sys/stat.h> 47789Sahrens 48789Sahrens #include <libzfs.h> 49789Sahrens 50789Sahrens #include "zfs_iter.h" 512082Seschrock #include "zfs_util.h" 522082Seschrock 532082Seschrock libzfs_handle_t *g_zfs; 54789Sahrens 55789Sahrens static FILE *mnttab_file; 56789Sahrens 57789Sahrens static int zfs_do_clone(int argc, char **argv); 58789Sahrens static int zfs_do_create(int argc, char **argv); 59789Sahrens static int zfs_do_destroy(int argc, char **argv); 60789Sahrens static int zfs_do_get(int argc, char **argv); 61789Sahrens static int zfs_do_inherit(int argc, char **argv); 62789Sahrens static int zfs_do_list(int argc, char **argv); 63789Sahrens static int zfs_do_mount(int argc, char **argv); 64789Sahrens static int zfs_do_rename(int argc, char **argv); 65789Sahrens static int zfs_do_rollback(int argc, char **argv); 66789Sahrens static int zfs_do_set(int argc, char **argv); 67789Sahrens static int zfs_do_snapshot(int argc, char **argv); 68789Sahrens static int zfs_do_unmount(int argc, char **argv); 69789Sahrens static int zfs_do_share(int argc, char **argv); 70789Sahrens static int zfs_do_unshare(int argc, char **argv); 711749Sahrens static int zfs_do_send(int argc, char **argv); 721749Sahrens static int zfs_do_receive(int argc, char **argv); 732082Seschrock static int zfs_do_promote(int argc, char **argv); 74789Sahrens 75789Sahrens /* 76789Sahrens * These libumem hooks provide a reasonable set of defaults for the allocator's 77789Sahrens * debugging facilities. 78789Sahrens */ 79789Sahrens const char * 80789Sahrens _umem_debug_init() 81789Sahrens { 82789Sahrens return ("default,verbose"); /* $UMEM_DEBUG setting */ 83789Sahrens } 84789Sahrens 85789Sahrens const char * 86789Sahrens _umem_logging_init(void) 87789Sahrens { 88789Sahrens return ("fail,contents"); /* $UMEM_LOGGING setting */ 89789Sahrens } 90789Sahrens 911387Seschrock typedef enum { 921387Seschrock HELP_CLONE, 931387Seschrock HELP_CREATE, 941387Seschrock HELP_DESTROY, 951387Seschrock HELP_GET, 961387Seschrock HELP_INHERIT, 971387Seschrock HELP_LIST, 981387Seschrock HELP_MOUNT, 992082Seschrock HELP_PROMOTE, 1001749Sahrens HELP_RECEIVE, 1011387Seschrock HELP_RENAME, 1021387Seschrock HELP_ROLLBACK, 1031749Sahrens HELP_SEND, 1041387Seschrock HELP_SET, 1051387Seschrock HELP_SHARE, 1061387Seschrock HELP_SNAPSHOT, 1071387Seschrock HELP_UNMOUNT, 1081387Seschrock HELP_UNSHARE 1091387Seschrock } zfs_help_t; 1101387Seschrock 111789Sahrens typedef struct zfs_command { 112789Sahrens const char *name; 113789Sahrens int (*func)(int argc, char **argv); 1141387Seschrock zfs_help_t usage; 115789Sahrens } zfs_command_t; 116789Sahrens 117789Sahrens /* 118789Sahrens * Master command table. Each ZFS command has a name, associated function, and 1191544Seschrock * usage message. The usage messages need to be internationalized, so we have 1201544Seschrock * to have a function to return the usage message based on a command index. 1211387Seschrock * 1221387Seschrock * These commands are organized according to how they are displayed in the usage 1231387Seschrock * message. An empty command (one with a NULL name) indicates an empty line in 1241387Seschrock * the generic usage message. 125789Sahrens */ 126789Sahrens static zfs_command_t command_table[] = { 1271387Seschrock { "create", zfs_do_create, HELP_CREATE }, 1281387Seschrock { "destroy", zfs_do_destroy, HELP_DESTROY }, 129789Sahrens { NULL }, 1301387Seschrock { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT }, 1311387Seschrock { "rollback", zfs_do_rollback, HELP_ROLLBACK }, 1321387Seschrock { "clone", zfs_do_clone, HELP_CLONE }, 1332082Seschrock { "promote", zfs_do_promote, HELP_PROMOTE }, 1341387Seschrock { "rename", zfs_do_rename, HELP_RENAME }, 135789Sahrens { NULL }, 1361387Seschrock { "list", zfs_do_list, HELP_LIST }, 137789Sahrens { NULL }, 1381387Seschrock { "set", zfs_do_set, HELP_SET }, 1391387Seschrock { "get", zfs_do_get, HELP_GET }, 1401387Seschrock { "inherit", zfs_do_inherit, HELP_INHERIT }, 141789Sahrens { NULL }, 1421387Seschrock { "mount", zfs_do_mount, HELP_MOUNT }, 143789Sahrens { NULL }, 1441387Seschrock { "unmount", zfs_do_unmount, HELP_UNMOUNT }, 145789Sahrens { NULL }, 1461387Seschrock { "share", zfs_do_share, HELP_SHARE }, 147789Sahrens { NULL }, 1481387Seschrock { "unshare", zfs_do_unshare, HELP_UNSHARE }, 149789Sahrens { NULL }, 1501749Sahrens { "send", zfs_do_send, HELP_SEND }, 1511749Sahrens { "receive", zfs_do_receive, HELP_RECEIVE }, 152789Sahrens }; 153789Sahrens 154789Sahrens #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) 155789Sahrens 156789Sahrens zfs_command_t *current_command; 157789Sahrens 1581387Seschrock static const char * 1591387Seschrock get_usage(zfs_help_t idx) 1601387Seschrock { 1611387Seschrock switch (idx) { 1621387Seschrock case HELP_CLONE: 1631387Seschrock return (gettext("\tclone <snapshot> <filesystem|volume>\n")); 1641387Seschrock case HELP_CREATE: 1652676Seschrock return (gettext("\tcreate [[-o property=value] ... ] " 1662676Seschrock "<filesystem>\n" 1672676Seschrock "\tcreate [-s] [-b blocksize] [[-o property=value] ...]\n" 1682676Seschrock "\t -V <size> <volume>\n")); 1691387Seschrock case HELP_DESTROY: 1701387Seschrock return (gettext("\tdestroy [-rRf] " 1711387Seschrock "<filesystem|volume|snapshot>\n")); 1721387Seschrock case HELP_GET: 1731387Seschrock return (gettext("\tget [-rHp] [-o field[,field]...] " 1741387Seschrock "[-s source[,source]...]\n" 1751387Seschrock "\t <all | property[,property]...> " 1762676Seschrock "[filesystem|volume|snapshot] ...\n")); 1771387Seschrock case HELP_INHERIT: 1781387Seschrock return (gettext("\tinherit [-r] <property> " 1791387Seschrock "<filesystem|volume> ...\n")); 1801387Seschrock case HELP_LIST: 1811387Seschrock return (gettext("\tlist [-rH] [-o property[,property]...] " 1821387Seschrock "[-t type[,type]...]\n" 1832379Ssjelinek "\t [-s property [-s property]...]" 1842379Ssjelinek " [-S property [-S property]...]\n" 1851387Seschrock "\t [filesystem|volume|snapshot] ...\n")); 1861387Seschrock case HELP_MOUNT: 1871387Seschrock return (gettext("\tmount\n" 1881387Seschrock "\tmount [-o opts] [-O] -a\n" 1891387Seschrock "\tmount [-o opts] [-O] <filesystem>\n")); 1902082Seschrock case HELP_PROMOTE: 1912082Seschrock return (gettext("\tpromote <clone filesystem>\n")); 1921749Sahrens case HELP_RECEIVE: 1932665Snd150628 return (gettext("\treceive [-vnF] <filesystem|volume|" 1942665Snd150628 "snapshot>\n" 1952665Snd150628 "\treceive [-vnF] -d <filesystem>\n")); 1961387Seschrock case HELP_RENAME: 1971387Seschrock return (gettext("\trename <filesystem|volume|snapshot> " 1981387Seschrock "<filesystem|volume|snapshot>\n")); 1991387Seschrock case HELP_ROLLBACK: 2001387Seschrock return (gettext("\trollback [-rRf] <snapshot>\n")); 2011749Sahrens case HELP_SEND: 2021749Sahrens return (gettext("\tsend [-i <snapshot>] <snapshot>\n")); 2031387Seschrock case HELP_SET: 2041387Seschrock return (gettext("\tset <property=value> " 2051387Seschrock "<filesystem|volume> ...\n")); 2061387Seschrock case HELP_SHARE: 2071387Seschrock return (gettext("\tshare -a\n" 2081387Seschrock "\tshare <filesystem>\n")); 2091387Seschrock case HELP_SNAPSHOT: 2102199Sahrens return (gettext("\tsnapshot [-r] " 2112199Sahrens "<filesystem@name|volume@name>\n")); 2121387Seschrock case HELP_UNMOUNT: 2131387Seschrock return (gettext("\tunmount [-f] -a\n" 2141387Seschrock "\tunmount [-f] <filesystem|mountpoint>\n")); 2151387Seschrock case HELP_UNSHARE: 2161387Seschrock return (gettext("\tunshare [-f] -a\n" 2171387Seschrock "\tunshare [-f] <filesystem|mountpoint>\n")); 2181387Seschrock } 2191387Seschrock 2201387Seschrock abort(); 2211387Seschrock /* NOTREACHED */ 2221387Seschrock } 2231387Seschrock 224789Sahrens /* 225789Sahrens * Utility function to guarantee malloc() success. 226789Sahrens */ 227789Sahrens void * 228789Sahrens safe_malloc(size_t size) 229789Sahrens { 230789Sahrens void *data; 231789Sahrens 232789Sahrens if ((data = calloc(1, size)) == NULL) { 233789Sahrens (void) fprintf(stderr, "internal error: out of memory\n"); 234789Sahrens exit(1); 235789Sahrens } 236789Sahrens 237789Sahrens return (data); 238789Sahrens } 239789Sahrens 240789Sahrens /* 241789Sahrens * Display usage message. If we're inside a command, display only the usage for 242789Sahrens * that command. Otherwise, iterate over the entire command table and display 243789Sahrens * a complete usage message. 244789Sahrens */ 245789Sahrens static void 2462082Seschrock usage(boolean_t requested) 247789Sahrens { 248789Sahrens int i; 2492082Seschrock boolean_t show_properties = B_FALSE; 250789Sahrens FILE *fp = requested ? stdout : stderr; 251789Sahrens 252789Sahrens if (current_command == NULL) { 253789Sahrens 254789Sahrens (void) fprintf(fp, gettext("usage: zfs command args ...\n")); 255789Sahrens (void) fprintf(fp, 256789Sahrens gettext("where 'command' is one of the following:\n\n")); 257789Sahrens 258789Sahrens for (i = 0; i < NCOMMAND; i++) { 259789Sahrens if (command_table[i].name == NULL) 260789Sahrens (void) fprintf(fp, "\n"); 261789Sahrens else 262789Sahrens (void) fprintf(fp, "%s", 2631387Seschrock get_usage(command_table[i].usage)); 264789Sahrens } 265789Sahrens 266789Sahrens (void) fprintf(fp, gettext("\nEach dataset is of the form: " 267789Sahrens "pool/[dataset/]*dataset[@name]\n")); 268789Sahrens } else { 269789Sahrens (void) fprintf(fp, gettext("usage:\n")); 2701387Seschrock (void) fprintf(fp, "%s", get_usage(current_command->usage)); 271789Sahrens } 272789Sahrens 2732190Sdarrenm if (current_command != NULL && 2742190Sdarrenm (strcmp(current_command->name, "set") == 0 || 275789Sahrens strcmp(current_command->name, "get") == 0 || 276789Sahrens strcmp(current_command->name, "inherit") == 0 || 2772190Sdarrenm strcmp(current_command->name, "list") == 0)) 2782082Seschrock show_properties = B_TRUE; 279789Sahrens 280789Sahrens if (show_properties) { 281789Sahrens 282789Sahrens (void) fprintf(fp, 283789Sahrens gettext("\nThe following properties are supported:\n")); 284789Sahrens 285789Sahrens (void) fprintf(fp, "\n\t%-13s %s %s %s\n\n", 286789Sahrens "PROPERTY", "EDIT", "INHERIT", "VALUES"); 287789Sahrens 288789Sahrens for (i = 0; i < ZFS_NPROP_VISIBLE; i++) { 289789Sahrens (void) fprintf(fp, "\t%-13s ", zfs_prop_to_name(i)); 290789Sahrens 291789Sahrens if (zfs_prop_readonly(i)) 292789Sahrens (void) fprintf(fp, " NO "); 293789Sahrens else 294789Sahrens (void) fprintf(fp, " YES "); 295789Sahrens 296789Sahrens if (zfs_prop_inheritable(i)) 297789Sahrens (void) fprintf(fp, " YES "); 298789Sahrens else 299789Sahrens (void) fprintf(fp, " NO "); 300789Sahrens 301789Sahrens if (zfs_prop_values(i) == NULL) 302789Sahrens (void) fprintf(fp, "-\n"); 303789Sahrens else 304789Sahrens (void) fprintf(fp, "%s\n", zfs_prop_values(i)); 305789Sahrens } 306789Sahrens (void) fprintf(fp, gettext("\nSizes are specified in bytes " 307789Sahrens "with standard units such as K, M, G, etc.\n")); 3082676Seschrock (void) fprintf(fp, gettext("\n\nUser-defined properties can " 3092676Seschrock "be specified by using a name containing a colon (:).\n")); 3102190Sdarrenm } else { 3112190Sdarrenm /* 3122190Sdarrenm * TRANSLATION NOTE: 3132190Sdarrenm * "zfs set|get" must not be localised this is the 3142190Sdarrenm * command name and arguments. 3152190Sdarrenm */ 3162190Sdarrenm (void) fprintf(fp, 3172190Sdarrenm gettext("\nFor the property list, run: zfs set|get\n")); 318789Sahrens } 319789Sahrens 3202676Seschrock /* 3212676Seschrock * See comments at end of main(). 3222676Seschrock */ 3232676Seschrock if (getenv("ZFS_ABORT") != NULL) { 3242676Seschrock (void) printf("dumping core by request\n"); 3252676Seschrock abort(); 3262676Seschrock } 3272676Seschrock 328789Sahrens exit(requested ? 0 : 2); 329789Sahrens } 330789Sahrens 331789Sahrens /* 332789Sahrens * zfs clone <fs, snap, vol> fs 333789Sahrens * 334789Sahrens * Given an existing dataset, create a writable copy whose initial contents 335789Sahrens * are the same as the source. The newly created dataset maintains a 336789Sahrens * dependency on the original; the original cannot be destroyed so long as 337789Sahrens * the clone exists. 338789Sahrens */ 339789Sahrens static int 340789Sahrens zfs_do_clone(int argc, char **argv) 341789Sahrens { 342789Sahrens zfs_handle_t *zhp; 343789Sahrens int ret; 344789Sahrens 345789Sahrens /* check options */ 346789Sahrens if (argc > 1 && argv[1][0] == '-') { 347789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 348789Sahrens argv[1][1]); 3492082Seschrock usage(B_FALSE); 350789Sahrens } 351789Sahrens 352789Sahrens /* check number of arguments */ 353789Sahrens if (argc < 2) { 354789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 355789Sahrens "argument\n")); 3562082Seschrock usage(B_FALSE); 357789Sahrens } 358789Sahrens if (argc < 3) { 359789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 360789Sahrens "argument\n")); 3612082Seschrock usage(B_FALSE); 362789Sahrens } 363789Sahrens if (argc > 3) { 364789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 3652082Seschrock usage(B_FALSE); 366789Sahrens } 367789Sahrens 368789Sahrens /* open the source dataset */ 3692082Seschrock if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_SNAPSHOT)) == NULL) 370789Sahrens return (1); 371789Sahrens 372789Sahrens /* pass to libzfs */ 3732676Seschrock ret = zfs_clone(zhp, argv[2], NULL); 374789Sahrens 375789Sahrens /* create the mountpoint if necessary */ 376789Sahrens if (ret == 0) { 3772082Seschrock zfs_handle_t *clone = zfs_open(g_zfs, argv[2], ZFS_TYPE_ANY); 378789Sahrens if (clone != NULL) { 379789Sahrens if ((ret = zfs_mount(clone, NULL, 0)) == 0) 380789Sahrens ret = zfs_share(clone); 381789Sahrens zfs_close(clone); 382789Sahrens } 383*2926Sek110237 zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE); 384789Sahrens } 385789Sahrens 386789Sahrens zfs_close(zhp); 387789Sahrens 388789Sahrens return (ret == 0 ? 0 : 1); 389789Sahrens } 390789Sahrens 391789Sahrens /* 3922676Seschrock * zfs create [-o prop=value] ... fs 3932676Seschrock * zfs create [-s] [-b blocksize] [-o prop=value] ... -V vol size 394789Sahrens * 395789Sahrens * Create a new dataset. This command can be used to create filesystems 396789Sahrens * and volumes. Snapshot creation is handled by 'zfs snapshot'. 397789Sahrens * For volumes, the user must specify a size to be used. 398789Sahrens * 399789Sahrens * The '-s' flag applies only to volumes, and indicates that we should not try 400789Sahrens * to set the reservation for this volume. By default we set a reservation 401789Sahrens * equal to the size for any volume. 402789Sahrens */ 403789Sahrens static int 404789Sahrens zfs_do_create(int argc, char **argv) 405789Sahrens { 406789Sahrens zfs_type_t type = ZFS_TYPE_FILESYSTEM; 4072676Seschrock zfs_handle_t *zhp = NULL; 4082676Seschrock uint64_t volsize; 409789Sahrens int c; 4102082Seschrock boolean_t noreserve = B_FALSE; 4112676Seschrock int ret = 1; 4122676Seschrock nvlist_t *props = NULL; 4132676Seschrock uint64_t intval; 4142676Seschrock char *propname; 415*2926Sek110237 char *propval = NULL; 416*2926Sek110237 char *strval; 4172676Seschrock 4182676Seschrock if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) { 4192676Seschrock (void) fprintf(stderr, gettext("internal error: " 4202676Seschrock "out of memory\n")); 4212676Seschrock return (1); 4222676Seschrock } 423789Sahrens 424789Sahrens /* check options */ 4252676Seschrock while ((c = getopt(argc, argv, ":V:b:so:")) != -1) { 426789Sahrens switch (c) { 427789Sahrens case 'V': 428789Sahrens type = ZFS_TYPE_VOLUME; 4292676Seschrock if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 4302676Seschrock (void) fprintf(stderr, gettext("bad volume " 4312676Seschrock "size '%s': %s\n"), optarg, 4322676Seschrock libzfs_error_description(g_zfs)); 4332676Seschrock goto error; 4342676Seschrock } 4352676Seschrock 4362676Seschrock if (nvlist_add_uint64(props, 4372676Seschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), 4382676Seschrock intval) != 0) { 4392676Seschrock (void) fprintf(stderr, gettext("internal " 4402676Seschrock "error: out of memory\n")); 4412676Seschrock goto error; 4422676Seschrock } 4432676Seschrock volsize = intval; 444789Sahrens break; 445789Sahrens case 'b': 4462676Seschrock if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 4472676Seschrock (void) fprintf(stderr, gettext("bad volume " 4482676Seschrock "block size '%s': %s\n"), optarg, 4492676Seschrock libzfs_error_description(g_zfs)); 4502676Seschrock goto error; 4512676Seschrock } 4522676Seschrock 4532676Seschrock if (nvlist_add_uint64(props, 4542676Seschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 4552676Seschrock intval) != 0) { 4562676Seschrock (void) fprintf(stderr, gettext("internal " 4572676Seschrock "error: out of memory\n")); 4582676Seschrock goto error; 4592676Seschrock } 4602676Seschrock break; 4612676Seschrock case 'o': 4622676Seschrock propname = optarg; 4632676Seschrock if ((propval = strchr(propname, '=')) == NULL) { 4642676Seschrock (void) fprintf(stderr, gettext("missing " 4652676Seschrock "'=' for -o option\n")); 4662676Seschrock goto error; 4672676Seschrock } 4682676Seschrock *propval = '\0'; 4692676Seschrock propval++; 4702676Seschrock if (nvlist_lookup_string(props, propname, 4712676Seschrock &strval) == 0) { 4722676Seschrock (void) fprintf(stderr, gettext("property '%s' " 4732676Seschrock "specified multiple times\n"), propname); 4742676Seschrock goto error; 4752676Seschrock } 4762676Seschrock if (nvlist_add_string(props, propname, propval) != 0) { 4772676Seschrock (void) fprintf(stderr, gettext("internal " 4782676Seschrock "error: out of memory\n")); 4792676Seschrock goto error; 4802676Seschrock } 481789Sahrens break; 482789Sahrens case 's': 4832082Seschrock noreserve = B_TRUE; 484789Sahrens break; 485789Sahrens case ':': 486789Sahrens (void) fprintf(stderr, gettext("missing size " 487789Sahrens "argument\n")); 4882676Seschrock goto badusage; 489789Sahrens break; 490789Sahrens case '?': 491789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 492789Sahrens optopt); 4932676Seschrock goto badusage; 494789Sahrens } 495789Sahrens } 496789Sahrens 497789Sahrens if (noreserve && type != ZFS_TYPE_VOLUME) { 498789Sahrens (void) fprintf(stderr, gettext("'-s' can only be used when " 499789Sahrens "creating a volume\n")); 5002676Seschrock goto badusage; 501789Sahrens } 502789Sahrens 503789Sahrens argc -= optind; 504789Sahrens argv += optind; 505789Sahrens 506789Sahrens /* check number of arguments */ 507789Sahrens if (argc == 0) { 508789Sahrens (void) fprintf(stderr, gettext("missing %s argument\n"), 509789Sahrens zfs_type_to_name(type)); 5102676Seschrock goto badusage; 511789Sahrens } 512789Sahrens if (argc > 1) { 513789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 5142676Seschrock goto badusage; 5152676Seschrock } 5162676Seschrock 5172676Seschrock if (type == ZFS_TYPE_VOLUME && !noreserve && 5182676Seschrock nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_RESERVATION), 5192676Seschrock &strval) != 0) { 5202676Seschrock if (nvlist_add_uint64(props, 5212676Seschrock zfs_prop_to_name(ZFS_PROP_RESERVATION), 5222676Seschrock volsize) != 0) { 5232676Seschrock (void) fprintf(stderr, gettext("internal " 5242676Seschrock "error: out of memory\n")); 5252676Seschrock nvlist_free(props); 5262676Seschrock return (1); 5272676Seschrock } 528789Sahrens } 529789Sahrens 530789Sahrens /* pass to libzfs */ 5312676Seschrock if (zfs_create(g_zfs, argv[0], type, props) != 0) 5322676Seschrock goto error; 533789Sahrens 534*2926Sek110237 if (propval != NULL) 535*2926Sek110237 *(propval - 1) = '='; 536*2926Sek110237 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0], 537*2926Sek110237 B_FALSE, B_FALSE); 538*2926Sek110237 5392082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 5402676Seschrock goto error; 541789Sahrens 542789Sahrens /* 543789Sahrens * Mount and/or share the new filesystem as appropriate. We provide a 544789Sahrens * verbose error message to let the user know that their filesystem was 545789Sahrens * in fact created, even if we failed to mount or share it. 546789Sahrens */ 547789Sahrens if (zfs_mount(zhp, NULL, 0) != 0) { 548789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 549789Sahrens "created, but not mounted\n")); 550789Sahrens ret = 1; 551789Sahrens } else if (zfs_share(zhp) != 0) { 552789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 553789Sahrens "created, but not shared\n")); 554789Sahrens ret = 1; 555789Sahrens } else { 556789Sahrens ret = 0; 557789Sahrens } 558789Sahrens 5592676Seschrock error: 5602676Seschrock if (zhp) 5612676Seschrock zfs_close(zhp); 5622676Seschrock nvlist_free(props); 563789Sahrens return (ret); 5642676Seschrock badusage: 5652676Seschrock nvlist_free(props); 5662676Seschrock usage(B_FALSE); 5672676Seschrock return (2); 568789Sahrens } 569789Sahrens 570789Sahrens /* 571789Sahrens * zfs destroy [-rf] <fs, snap, vol> 572789Sahrens * 573789Sahrens * -r Recursively destroy all children 574789Sahrens * -R Recursively destroy all dependents, including clones 575789Sahrens * -f Force unmounting of any dependents 576789Sahrens * 577789Sahrens * Destroys the given dataset. By default, it will unmount any filesystems, 578789Sahrens * and refuse to destroy a dataset that has any dependents. A dependent can 579789Sahrens * either be a child, or a clone of a child. 580789Sahrens */ 581789Sahrens typedef struct destroy_cbdata { 5822082Seschrock boolean_t cb_first; 583789Sahrens int cb_force; 584789Sahrens int cb_recurse; 585789Sahrens int cb_error; 586789Sahrens int cb_needforce; 587789Sahrens int cb_doclones; 588789Sahrens zfs_handle_t *cb_target; 5892199Sahrens char *cb_snapname; 590789Sahrens } destroy_cbdata_t; 591789Sahrens 592789Sahrens /* 593789Sahrens * Check for any dependents based on the '-r' or '-R' flags. 594789Sahrens */ 595789Sahrens static int 596789Sahrens destroy_check_dependent(zfs_handle_t *zhp, void *data) 597789Sahrens { 598789Sahrens destroy_cbdata_t *cbp = data; 599789Sahrens const char *tname = zfs_get_name(cbp->cb_target); 600789Sahrens const char *name = zfs_get_name(zhp); 601789Sahrens 602789Sahrens if (strncmp(tname, name, strlen(tname)) == 0 && 603789Sahrens (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { 604789Sahrens /* 605789Sahrens * This is a direct descendant, not a clone somewhere else in 606789Sahrens * the hierarchy. 607789Sahrens */ 608789Sahrens if (cbp->cb_recurse) 609789Sahrens goto out; 610789Sahrens 611789Sahrens if (cbp->cb_first) { 612789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 613789Sahrens "%s has children\n"), 614789Sahrens zfs_get_name(cbp->cb_target), 615789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 616789Sahrens (void) fprintf(stderr, gettext("use '-r' to destroy " 617789Sahrens "the following datasets:\n")); 6182082Seschrock cbp->cb_first = B_FALSE; 619789Sahrens cbp->cb_error = 1; 620789Sahrens } 621789Sahrens 622789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 623789Sahrens } else { 624789Sahrens /* 625789Sahrens * This is a clone. We only want to report this if the '-r' 626789Sahrens * wasn't specified, or the target is a snapshot. 627789Sahrens */ 628789Sahrens if (!cbp->cb_recurse && 629789Sahrens zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) 630789Sahrens goto out; 631789Sahrens 632789Sahrens if (cbp->cb_first) { 633789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 634789Sahrens "%s has dependent clones\n"), 635789Sahrens zfs_get_name(cbp->cb_target), 636789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 637789Sahrens (void) fprintf(stderr, gettext("use '-R' to destroy " 638789Sahrens "the following datasets:\n")); 6392082Seschrock cbp->cb_first = B_FALSE; 640789Sahrens cbp->cb_error = 1; 641789Sahrens } 642789Sahrens 643789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 644789Sahrens } 645789Sahrens 646789Sahrens out: 647789Sahrens zfs_close(zhp); 648789Sahrens return (0); 649789Sahrens } 650789Sahrens 651789Sahrens static int 652789Sahrens destroy_callback(zfs_handle_t *zhp, void *data) 653789Sahrens { 654789Sahrens destroy_cbdata_t *cbp = data; 655789Sahrens 656789Sahrens /* 657789Sahrens * Ignore pools (which we've already flagged as an error before getting 658789Sahrens * here. 659789Sahrens */ 660789Sahrens if (strchr(zfs_get_name(zhp), '/') == NULL && 661789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 662789Sahrens zfs_close(zhp); 663789Sahrens return (0); 664789Sahrens } 665789Sahrens 666789Sahrens /* 667789Sahrens * Bail out on the first error. 668789Sahrens */ 669789Sahrens if (zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0 || 670789Sahrens zfs_destroy(zhp) != 0) { 671789Sahrens zfs_close(zhp); 672789Sahrens return (-1); 673789Sahrens } 674789Sahrens 675789Sahrens zfs_close(zhp); 676789Sahrens return (0); 677789Sahrens } 678789Sahrens 6792199Sahrens static int 6802199Sahrens destroy_snap_clones(zfs_handle_t *zhp, void *arg) 6812199Sahrens { 6822199Sahrens destroy_cbdata_t *cbp = arg; 6832199Sahrens char thissnap[MAXPATHLEN]; 6842199Sahrens zfs_handle_t *szhp; 6852199Sahrens 6862199Sahrens (void) snprintf(thissnap, sizeof (thissnap), 6872199Sahrens "%s@%s", zfs_get_name(zhp), cbp->cb_snapname); 6882199Sahrens 6892199Sahrens libzfs_print_on_error(g_zfs, B_FALSE); 6902199Sahrens szhp = zfs_open(g_zfs, thissnap, ZFS_TYPE_SNAPSHOT); 6912199Sahrens libzfs_print_on_error(g_zfs, B_TRUE); 6922199Sahrens if (szhp) { 6932199Sahrens /* 6942199Sahrens * Destroy any clones of this snapshot 6952199Sahrens */ 6962474Seschrock if (zfs_iter_dependents(szhp, B_FALSE, destroy_callback, 6972474Seschrock cbp) != 0) { 6982474Seschrock zfs_close(szhp); 6992474Seschrock return (-1); 7002474Seschrock } 7012199Sahrens zfs_close(szhp); 7022199Sahrens } 7032199Sahrens 7042199Sahrens return (zfs_iter_filesystems(zhp, destroy_snap_clones, arg)); 7052199Sahrens } 706789Sahrens 707789Sahrens static int 708789Sahrens zfs_do_destroy(int argc, char **argv) 709789Sahrens { 710789Sahrens destroy_cbdata_t cb = { 0 }; 711789Sahrens int c; 712789Sahrens zfs_handle_t *zhp; 7132199Sahrens char *cp; 714789Sahrens 715789Sahrens /* check options */ 716789Sahrens while ((c = getopt(argc, argv, "frR")) != -1) { 717789Sahrens switch (c) { 718789Sahrens case 'f': 719789Sahrens cb.cb_force = 1; 720789Sahrens break; 721789Sahrens case 'r': 722789Sahrens cb.cb_recurse = 1; 723789Sahrens break; 724789Sahrens case 'R': 725789Sahrens cb.cb_recurse = 1; 726789Sahrens cb.cb_doclones = 1; 727789Sahrens break; 728789Sahrens case '?': 729789Sahrens default: 730789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 731789Sahrens optopt); 7322082Seschrock usage(B_FALSE); 733789Sahrens } 734789Sahrens } 735789Sahrens 736789Sahrens argc -= optind; 737789Sahrens argv += optind; 738789Sahrens 739789Sahrens /* check number of arguments */ 740789Sahrens if (argc == 0) { 741789Sahrens (void) fprintf(stderr, gettext("missing path argument\n")); 7422082Seschrock usage(B_FALSE); 743789Sahrens } 744789Sahrens if (argc > 1) { 745789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 7462082Seschrock usage(B_FALSE); 747789Sahrens } 748789Sahrens 7492199Sahrens /* 7502199Sahrens * If we are doing recursive destroy of a snapshot, then the 7512199Sahrens * named snapshot may not exist. Go straight to libzfs. 7522199Sahrens */ 7532199Sahrens if (cb.cb_recurse && (cp = strchr(argv[0], '@'))) { 7542199Sahrens int ret; 7552199Sahrens 7562199Sahrens *cp = '\0'; 7572199Sahrens if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 7582199Sahrens return (1); 7592199Sahrens *cp = '@'; 7602199Sahrens cp++; 7612199Sahrens 7622199Sahrens if (cb.cb_doclones) { 7632199Sahrens cb.cb_snapname = cp; 7642474Seschrock if (destroy_snap_clones(zhp, &cb) != 0) { 7652474Seschrock zfs_close(zhp); 7662474Seschrock return (1); 7672474Seschrock } 7682199Sahrens } 7692199Sahrens 7702199Sahrens ret = zfs_destroy_snaps(zhp, cp); 7712199Sahrens zfs_close(zhp); 7722199Sahrens if (ret) { 7732199Sahrens (void) fprintf(stderr, 7742199Sahrens gettext("no snapshots destroyed\n")); 7752199Sahrens } 7762199Sahrens return (ret != 0); 7772199Sahrens } 7782199Sahrens 7792199Sahrens 780789Sahrens /* Open the given dataset */ 7812082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 782789Sahrens return (1); 783789Sahrens 784789Sahrens cb.cb_target = zhp; 785789Sahrens 786789Sahrens /* 787789Sahrens * Perform an explicit check for pools before going any further. 788789Sahrens */ 789789Sahrens if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 790789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 791789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 792789Sahrens "operation does not apply to pools\n"), 793789Sahrens zfs_get_name(zhp)); 794789Sahrens (void) fprintf(stderr, gettext("use 'zfs destroy -r " 795789Sahrens "%s' to destroy all datasets in the pool\n"), 796789Sahrens zfs_get_name(zhp)); 797789Sahrens (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 798789Sahrens "to destroy the pool itself\n"), zfs_get_name(zhp)); 799789Sahrens zfs_close(zhp); 800789Sahrens return (1); 801789Sahrens } 802789Sahrens 803789Sahrens /* 804789Sahrens * Check for any dependents and/or clones. 805789Sahrens */ 8062082Seschrock cb.cb_first = B_TRUE; 8072474Seschrock if (!cb.cb_doclones && 8082474Seschrock zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent, 8092474Seschrock &cb) != 0) { 8102474Seschrock zfs_close(zhp); 8112474Seschrock return (1); 8122474Seschrock } 8132474Seschrock 8142474Seschrock 8152474Seschrock if (cb.cb_error || 8162474Seschrock zfs_iter_dependents(zhp, B_FALSE, destroy_callback, &cb) != 0) { 817789Sahrens zfs_close(zhp); 818789Sahrens return (1); 819789Sahrens } 820789Sahrens 821789Sahrens /* 8222474Seschrock * Do the real thing. The callback will close the handle regardless of 8232474Seschrock * whether it succeeds or not. 824789Sahrens */ 8252474Seschrock if (destroy_callback(zhp, &cb) != 0) 8262474Seschrock return (1); 8272474Seschrock 828*2926Sek110237 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0], 829*2926Sek110237 B_FALSE, B_FALSE); 830*2926Sek110237 8312474Seschrock return (0); 832789Sahrens } 833789Sahrens 834789Sahrens /* 835866Seschrock * zfs get [-rHp] [-o field[,field]...] [-s source[,source]...] 836866Seschrock * < all | property[,property]... > < fs | snap | vol > ... 837789Sahrens * 838789Sahrens * -r recurse over any child datasets 839789Sahrens * -H scripted mode. Headers are stripped, and fields are separated 840789Sahrens * by tabs instead of spaces. 841789Sahrens * -o Set of fields to display. One of "name,property,value,source". 842789Sahrens * Default is all four. 843789Sahrens * -s Set of sources to allow. One of 844789Sahrens * "local,default,inherited,temporary,none". Default is all 845789Sahrens * five. 846789Sahrens * -p Display values in parsable (literal) format. 847789Sahrens * 848789Sahrens * Prints properties for the given datasets. The user can control which 849789Sahrens * columns to display as well as which property types to allow. 850789Sahrens */ 851789Sahrens typedef struct get_cbdata { 852789Sahrens int cb_sources; 853789Sahrens int cb_columns[4]; 8542676Seschrock int cb_colwidths[5]; 8552082Seschrock boolean_t cb_scripted; 8562082Seschrock boolean_t cb_literal; 8572676Seschrock boolean_t cb_first; 8582676Seschrock zfs_proplist_t *cb_proplist; 859789Sahrens } get_cbdata_t; 860789Sahrens 861789Sahrens #define GET_COL_NAME 1 862789Sahrens #define GET_COL_PROPERTY 2 863789Sahrens #define GET_COL_VALUE 3 864789Sahrens #define GET_COL_SOURCE 4 865789Sahrens 866789Sahrens /* 8672676Seschrock * Print the column headers for 'zfs get'. 8682676Seschrock */ 8692676Seschrock static void 8702676Seschrock print_get_headers(get_cbdata_t *cbp) 8712676Seschrock { 8722676Seschrock zfs_proplist_t *pl = cbp->cb_proplist; 8732676Seschrock int i; 8742676Seschrock char *title; 8752676Seschrock size_t len; 8762676Seschrock 8772676Seschrock cbp->cb_first = B_FALSE; 8782676Seschrock if (cbp->cb_scripted) 8792676Seschrock return; 8802676Seschrock 8812676Seschrock /* 8822676Seschrock * Start with the length of the column headers. 8832676Seschrock */ 8842676Seschrock cbp->cb_colwidths[GET_COL_NAME] = strlen(gettext("NAME")); 8852676Seschrock cbp->cb_colwidths[GET_COL_PROPERTY] = strlen(gettext("PROPERTY")); 8862676Seschrock cbp->cb_colwidths[GET_COL_VALUE] = strlen(gettext("VALUE")); 8872676Seschrock cbp->cb_colwidths[GET_COL_SOURCE] = strlen(gettext("SOURCE")); 8882676Seschrock 8892676Seschrock /* 8902676Seschrock * Go through and calculate the widths for each column. For the 8912676Seschrock * 'source' column, we kludge it up by taking the worst-case scenario of 8922676Seschrock * inheriting from the longest name. This is acceptable because in the 8932676Seschrock * majority of cases 'SOURCE' is the last column displayed, and we don't 8942676Seschrock * use the width anyway. Note that the 'VALUE' column can be oversized, 8952676Seschrock * if the name of the property is much longer the any values we find. 8962676Seschrock */ 8972676Seschrock for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) { 8982676Seschrock /* 8992676Seschrock * 'PROPERTY' column 9002676Seschrock */ 9012676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 9022676Seschrock len = strlen(zfs_prop_to_name(pl->pl_prop)); 9032676Seschrock if (len > cbp->cb_colwidths[GET_COL_PROPERTY]) 9042676Seschrock cbp->cb_colwidths[GET_COL_PROPERTY] = len; 9052676Seschrock } else { 9062676Seschrock len = strlen(pl->pl_user_prop); 9072676Seschrock if (len > cbp->cb_colwidths[GET_COL_PROPERTY]) 9082676Seschrock cbp->cb_colwidths[GET_COL_PROPERTY] = len; 9092676Seschrock } 9102676Seschrock 9112676Seschrock /* 9122676Seschrock * 'VALUE' column 9132676Seschrock */ 9142676Seschrock if ((pl->pl_prop != ZFS_PROP_NAME || !pl->pl_all) && 9152676Seschrock pl->pl_width > cbp->cb_colwidths[GET_COL_VALUE]) 9162676Seschrock cbp->cb_colwidths[GET_COL_VALUE] = pl->pl_width; 9172676Seschrock 9182676Seschrock /* 9192676Seschrock * 'NAME' and 'SOURCE' columns 9202676Seschrock */ 9212676Seschrock if (pl->pl_prop == ZFS_PROP_NAME && 9222676Seschrock pl->pl_width > cbp->cb_colwidths[GET_COL_NAME]) { 9232676Seschrock cbp->cb_colwidths[GET_COL_NAME] = pl->pl_width; 9242676Seschrock cbp->cb_colwidths[GET_COL_SOURCE] = pl->pl_width + 9252676Seschrock strlen(gettext("inherited from")); 9262676Seschrock } 9272676Seschrock } 9282676Seschrock 9292676Seschrock /* 9302676Seschrock * Now go through and print the headers. 9312676Seschrock */ 9322676Seschrock for (i = 0; i < 4; i++) { 9332676Seschrock switch (cbp->cb_columns[i]) { 9342676Seschrock case GET_COL_NAME: 9352676Seschrock title = gettext("NAME"); 9362676Seschrock break; 9372676Seschrock case GET_COL_PROPERTY: 9382676Seschrock title = gettext("PROPERTY"); 9392676Seschrock break; 9402676Seschrock case GET_COL_VALUE: 9412676Seschrock title = gettext("VALUE"); 9422676Seschrock break; 9432676Seschrock case GET_COL_SOURCE: 9442676Seschrock title = gettext("SOURCE"); 9452676Seschrock break; 9462676Seschrock default: 9472676Seschrock title = NULL; 9482676Seschrock } 9492676Seschrock 9502676Seschrock if (title != NULL) { 9512676Seschrock if (i == 3 || cbp->cb_columns[i + 1] == 0) 9522676Seschrock (void) printf("%s", title); 9532676Seschrock else 9542676Seschrock (void) printf("%-*s ", 9552676Seschrock cbp->cb_colwidths[cbp->cb_columns[i]], 9562676Seschrock title); 9572676Seschrock } 9582676Seschrock } 9592676Seschrock (void) printf("\n"); 9602676Seschrock } 9612676Seschrock 9622676Seschrock /* 963789Sahrens * Display a single line of output, according to the settings in the callback 964789Sahrens * structure. 965789Sahrens */ 966789Sahrens static void 9672676Seschrock print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, const char *propname, 968789Sahrens const char *value, zfs_source_t sourcetype, const char *source) 969789Sahrens { 970789Sahrens int i; 971789Sahrens const char *str; 972789Sahrens char buf[128]; 973789Sahrens 974789Sahrens /* 975789Sahrens * Ignore those source types that the user has chosen to ignore. 976789Sahrens */ 977789Sahrens if ((sourcetype & cbp->cb_sources) == 0) 978789Sahrens return; 979789Sahrens 9802676Seschrock if (cbp->cb_first) 9812676Seschrock print_get_headers(cbp); 9822676Seschrock 983789Sahrens for (i = 0; i < 4; i++) { 984789Sahrens switch (cbp->cb_columns[i]) { 985789Sahrens case GET_COL_NAME: 986789Sahrens str = zfs_get_name(zhp); 987789Sahrens break; 988789Sahrens 989789Sahrens case GET_COL_PROPERTY: 9902676Seschrock str = propname; 991789Sahrens break; 992789Sahrens 993789Sahrens case GET_COL_VALUE: 994789Sahrens str = value; 995789Sahrens break; 996789Sahrens 997789Sahrens case GET_COL_SOURCE: 998789Sahrens switch (sourcetype) { 999789Sahrens case ZFS_SRC_NONE: 1000789Sahrens str = "-"; 1001789Sahrens break; 1002789Sahrens 1003789Sahrens case ZFS_SRC_DEFAULT: 1004789Sahrens str = "default"; 1005789Sahrens break; 1006789Sahrens 1007789Sahrens case ZFS_SRC_LOCAL: 1008789Sahrens str = "local"; 1009789Sahrens break; 1010789Sahrens 1011789Sahrens case ZFS_SRC_TEMPORARY: 1012789Sahrens str = "temporary"; 1013789Sahrens break; 1014789Sahrens 1015789Sahrens case ZFS_SRC_INHERITED: 1016789Sahrens (void) snprintf(buf, sizeof (buf), 1017789Sahrens "inherited from %s", source); 1018789Sahrens str = buf; 1019789Sahrens break; 1020789Sahrens } 1021789Sahrens break; 1022789Sahrens 1023789Sahrens default: 1024789Sahrens continue; 1025789Sahrens } 1026789Sahrens 1027789Sahrens if (cbp->cb_columns[i + 1] == 0) 1028789Sahrens (void) printf("%s", str); 1029789Sahrens else if (cbp->cb_scripted) 1030789Sahrens (void) printf("%s\t", str); 1031789Sahrens else 10322676Seschrock (void) printf("%-*s ", 10332676Seschrock cbp->cb_colwidths[cbp->cb_columns[i]], 10342676Seschrock str); 1035789Sahrens 1036789Sahrens } 1037789Sahrens 1038789Sahrens (void) printf("\n"); 1039789Sahrens } 1040789Sahrens 1041789Sahrens /* 1042789Sahrens * Invoked to display the properties for a single dataset. 1043789Sahrens */ 1044789Sahrens static int 1045789Sahrens get_callback(zfs_handle_t *zhp, void *data) 1046789Sahrens { 1047789Sahrens char buf[ZFS_MAXPROPLEN]; 1048789Sahrens zfs_source_t sourcetype; 1049789Sahrens char source[ZFS_MAXNAMELEN]; 1050789Sahrens get_cbdata_t *cbp = data; 10512676Seschrock nvlist_t *userprop = zfs_get_user_props(zhp); 10522676Seschrock zfs_proplist_t *pl = cbp->cb_proplist; 10532676Seschrock nvlist_t *propval; 10542676Seschrock char *strval; 10552676Seschrock char *sourceval; 10562676Seschrock 10572676Seschrock for (; pl != NULL; pl = pl->pl_next) { 10582676Seschrock /* 10592676Seschrock * Skip the special fake placeholder. This will also skip over 10602676Seschrock * the name property when 'all' is specified. 10612676Seschrock */ 10622676Seschrock if (pl->pl_prop == ZFS_PROP_NAME && 10632676Seschrock pl == cbp->cb_proplist) 10642676Seschrock continue; 10652676Seschrock 10662676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 10672676Seschrock if (zfs_prop_get(zhp, pl->pl_prop, buf, 10682676Seschrock sizeof (buf), &sourcetype, source, 10692676Seschrock sizeof (source), 10702676Seschrock cbp->cb_literal) != 0) { 10712676Seschrock if (pl->pl_all) 10722676Seschrock continue; 10732676Seschrock sourcetype = ZFS_SRC_NONE; 10742676Seschrock (void) strlcpy(buf, "-", sizeof (buf)); 10752676Seschrock } 10762676Seschrock 10772676Seschrock print_one_property(zhp, cbp, 10782676Seschrock zfs_prop_to_name(pl->pl_prop), 10792676Seschrock buf, sourcetype, source); 10802676Seschrock } else { 10812676Seschrock if (nvlist_lookup_nvlist(userprop, 10822676Seschrock pl->pl_user_prop, &propval) != 0) { 10832676Seschrock if (pl->pl_all) 10842676Seschrock continue; 10852676Seschrock sourcetype = ZFS_SRC_NONE; 10862676Seschrock strval = "-"; 10872676Seschrock } else { 10882676Seschrock verify(nvlist_lookup_string(propval, 10892676Seschrock ZFS_PROP_VALUE, &strval) == 0); 10902676Seschrock verify(nvlist_lookup_string(propval, 10912676Seschrock ZFS_PROP_SOURCE, &sourceval) == 0); 10922676Seschrock 10932676Seschrock if (strcmp(sourceval, 10942676Seschrock zfs_get_name(zhp)) == 0) { 10952676Seschrock sourcetype = ZFS_SRC_LOCAL; 10962676Seschrock } else { 10972676Seschrock sourcetype = ZFS_SRC_INHERITED; 10982676Seschrock (void) strlcpy(source, 10992676Seschrock sourceval, sizeof (source)); 11002676Seschrock } 11012676Seschrock } 11022676Seschrock 11032676Seschrock print_one_property(zhp, cbp, 11042676Seschrock pl->pl_user_prop, strval, sourcetype, 11052676Seschrock source); 1106866Seschrock } 1107789Sahrens } 1108789Sahrens 1109789Sahrens return (0); 1110789Sahrens } 1111789Sahrens 1112789Sahrens static int 1113789Sahrens zfs_do_get(int argc, char **argv) 1114789Sahrens { 1115789Sahrens get_cbdata_t cb = { 0 }; 11162082Seschrock boolean_t recurse = B_FALSE; 11172676Seschrock int i, c; 11182676Seschrock char *value, *fields; 1119866Seschrock int ret; 11202676Seschrock zfs_proplist_t fake_name = { 0 }; 1121789Sahrens 1122789Sahrens /* 1123789Sahrens * Set up default columns and sources. 1124789Sahrens */ 1125789Sahrens cb.cb_sources = ZFS_SRC_ALL; 1126789Sahrens cb.cb_columns[0] = GET_COL_NAME; 1127789Sahrens cb.cb_columns[1] = GET_COL_PROPERTY; 1128789Sahrens cb.cb_columns[2] = GET_COL_VALUE; 1129789Sahrens cb.cb_columns[3] = GET_COL_SOURCE; 1130789Sahrens 1131789Sahrens /* check options */ 1132789Sahrens while ((c = getopt(argc, argv, ":o:s:rHp")) != -1) { 1133789Sahrens switch (c) { 1134789Sahrens case 'p': 11352082Seschrock cb.cb_literal = B_TRUE; 1136789Sahrens break; 1137789Sahrens case 'r': 11382082Seschrock recurse = B_TRUE; 1139789Sahrens break; 1140789Sahrens case 'H': 11412082Seschrock cb.cb_scripted = B_TRUE; 1142789Sahrens break; 1143789Sahrens case ':': 1144789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1145789Sahrens "'%c' option\n"), optopt); 11462082Seschrock usage(B_FALSE); 1147789Sahrens break; 1148789Sahrens case 'o': 1149789Sahrens /* 1150789Sahrens * Process the set of columns to display. We zero out 1151789Sahrens * the structure to give us a blank slate. 1152789Sahrens */ 1153789Sahrens bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 1154789Sahrens i = 0; 1155789Sahrens while (*optarg != '\0') { 1156789Sahrens static char *col_subopts[] = 1157789Sahrens { "name", "property", "value", "source", 1158789Sahrens NULL }; 1159789Sahrens 1160789Sahrens if (i == 4) { 1161789Sahrens (void) fprintf(stderr, gettext("too " 1162789Sahrens "many fields given to -o " 1163789Sahrens "option\n")); 11642082Seschrock usage(B_FALSE); 1165789Sahrens } 1166789Sahrens 1167789Sahrens switch (getsubopt(&optarg, col_subopts, 1168789Sahrens &value)) { 1169789Sahrens case 0: 1170789Sahrens cb.cb_columns[i++] = GET_COL_NAME; 1171789Sahrens break; 1172789Sahrens case 1: 1173789Sahrens cb.cb_columns[i++] = GET_COL_PROPERTY; 1174789Sahrens break; 1175789Sahrens case 2: 1176789Sahrens cb.cb_columns[i++] = GET_COL_VALUE; 1177789Sahrens break; 1178789Sahrens case 3: 1179789Sahrens cb.cb_columns[i++] = GET_COL_SOURCE; 1180789Sahrens break; 1181789Sahrens default: 1182789Sahrens (void) fprintf(stderr, 1183789Sahrens gettext("invalid column name " 1184789Sahrens "'%s'\n"), value); 11852082Seschrock usage(B_FALSE); 1186789Sahrens } 1187789Sahrens } 1188789Sahrens break; 1189789Sahrens 1190789Sahrens case 's': 1191789Sahrens cb.cb_sources = 0; 1192789Sahrens while (*optarg != '\0') { 1193789Sahrens static char *source_subopts[] = { 1194789Sahrens "local", "default", "inherited", 1195789Sahrens "temporary", "none", NULL }; 1196789Sahrens 1197789Sahrens switch (getsubopt(&optarg, source_subopts, 1198789Sahrens &value)) { 1199789Sahrens case 0: 1200789Sahrens cb.cb_sources |= ZFS_SRC_LOCAL; 1201789Sahrens break; 1202789Sahrens case 1: 1203789Sahrens cb.cb_sources |= ZFS_SRC_DEFAULT; 1204789Sahrens break; 1205789Sahrens case 2: 1206789Sahrens cb.cb_sources |= ZFS_SRC_INHERITED; 1207789Sahrens break; 1208789Sahrens case 3: 1209789Sahrens cb.cb_sources |= ZFS_SRC_TEMPORARY; 1210789Sahrens break; 1211789Sahrens case 4: 1212789Sahrens cb.cb_sources |= ZFS_SRC_NONE; 1213789Sahrens break; 1214789Sahrens default: 1215789Sahrens (void) fprintf(stderr, 1216789Sahrens gettext("invalid source " 1217789Sahrens "'%s'\n"), value); 12182082Seschrock usage(B_FALSE); 1219789Sahrens } 1220789Sahrens } 1221789Sahrens break; 1222789Sahrens 1223789Sahrens case '?': 1224789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1225789Sahrens optopt); 12262082Seschrock usage(B_FALSE); 1227789Sahrens } 1228789Sahrens } 1229789Sahrens 1230789Sahrens argc -= optind; 1231789Sahrens argv += optind; 1232789Sahrens 1233789Sahrens if (argc < 1) { 1234789Sahrens (void) fprintf(stderr, gettext("missing property " 1235789Sahrens "argument\n")); 12362082Seschrock usage(B_FALSE); 1237789Sahrens } 1238789Sahrens 1239789Sahrens fields = argv[0]; 1240789Sahrens 12412676Seschrock if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0) 12422082Seschrock usage(B_FALSE); 1243789Sahrens 1244789Sahrens argc--; 1245789Sahrens argv++; 1246789Sahrens 1247789Sahrens /* 12482676Seschrock * As part of zfs_expand_proplist(), we keep track of the maximum column 12492676Seschrock * width for each property. For the 'NAME' (and 'SOURCE') columns, we 12502676Seschrock * need to know the maximum name length. However, the user likely did 12512676Seschrock * not specify 'name' as one of the properties to fetch, so we need to 12522676Seschrock * make sure we always include at least this property for 12532676Seschrock * print_get_headers() to work properly. 1254789Sahrens */ 12552676Seschrock if (cb.cb_proplist != NULL) { 12562676Seschrock fake_name.pl_prop = ZFS_PROP_NAME; 12572676Seschrock fake_name.pl_width = strlen(gettext("NAME")); 12582676Seschrock fake_name.pl_next = cb.cb_proplist; 12592676Seschrock cb.cb_proplist = &fake_name; 1260789Sahrens } 1261789Sahrens 12622676Seschrock cb.cb_first = B_TRUE; 12632676Seschrock 1264789Sahrens /* run for each object */ 12652676Seschrock ret = zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, NULL, 12662676Seschrock &cb.cb_proplist, get_callback, &cb); 12672676Seschrock 12682676Seschrock if (cb.cb_proplist == &fake_name) 12692676Seschrock zfs_free_proplist(fake_name.pl_next); 12702676Seschrock else 12712676Seschrock zfs_free_proplist(cb.cb_proplist); 12722676Seschrock 12732676Seschrock return (ret); 1274789Sahrens } 1275789Sahrens 1276789Sahrens /* 1277789Sahrens * inherit [-r] <property> <fs|vol> ... 1278789Sahrens * 1279789Sahrens * -r Recurse over all children 1280789Sahrens * 1281789Sahrens * For each dataset specified on the command line, inherit the given property 1282789Sahrens * from its parent. Inheriting a property at the pool level will cause it to 1283789Sahrens * use the default value. The '-r' flag will recurse over all children, and is 1284789Sahrens * useful for setting a property on a hierarchy-wide basis, regardless of any 1285789Sahrens * local modifications for each dataset. 1286789Sahrens */ 1287*2926Sek110237 typedef struct inherit_cbdata { 1288*2926Sek110237 char *cb_propname; 1289*2926Sek110237 boolean_t cb_any_successful; 1290*2926Sek110237 } inherit_cbdata_t; 1291*2926Sek110237 1292789Sahrens static int 1293789Sahrens inherit_callback(zfs_handle_t *zhp, void *data) 1294789Sahrens { 1295*2926Sek110237 inherit_cbdata_t *cbp = data; 1296*2926Sek110237 int ret; 1297*2926Sek110237 1298*2926Sek110237 ret = zfs_prop_inherit(zhp, cbp->cb_propname); 1299*2926Sek110237 if (ret == 0) 1300*2926Sek110237 cbp->cb_any_successful = B_TRUE; 1301*2926Sek110237 return (ret != 0); 1302789Sahrens } 1303789Sahrens 1304789Sahrens static int 1305789Sahrens zfs_do_inherit(int argc, char **argv) 1306789Sahrens { 13072082Seschrock boolean_t recurse = B_FALSE; 1308789Sahrens int c; 1309789Sahrens zfs_prop_t prop; 1310*2926Sek110237 inherit_cbdata_t cb; 1311*2926Sek110237 int ret; 1312789Sahrens 1313789Sahrens /* check options */ 1314789Sahrens while ((c = getopt(argc, argv, "r")) != -1) { 1315789Sahrens switch (c) { 1316789Sahrens case 'r': 13172082Seschrock recurse = B_TRUE; 1318789Sahrens break; 1319789Sahrens case '?': 1320789Sahrens default: 1321789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1322789Sahrens optopt); 13232082Seschrock usage(B_FALSE); 1324789Sahrens } 1325789Sahrens } 1326789Sahrens 1327789Sahrens argc -= optind; 1328789Sahrens argv += optind; 1329789Sahrens 1330789Sahrens /* check number of arguments */ 1331789Sahrens if (argc < 1) { 1332789Sahrens (void) fprintf(stderr, gettext("missing property argument\n")); 13332082Seschrock usage(B_FALSE); 1334789Sahrens } 1335789Sahrens if (argc < 2) { 1336789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 13372082Seschrock usage(B_FALSE); 1338789Sahrens } 1339789Sahrens 1340*2926Sek110237 cb.cb_propname = argv[0]; 13412676Seschrock argc--; 13422676Seschrock argv++; 13432676Seschrock 1344*2926Sek110237 if ((prop = zfs_name_to_prop(cb.cb_propname)) != ZFS_PROP_INVAL) { 13452676Seschrock if (zfs_prop_readonly(prop)) { 13462676Seschrock (void) fprintf(stderr, gettext( 13472676Seschrock "%s property is read-only\n"), 1348*2926Sek110237 cb.cb_propname); 13492676Seschrock return (1); 13502676Seschrock } 13512676Seschrock if (!zfs_prop_inheritable(prop)) { 13522676Seschrock (void) fprintf(stderr, gettext("'%s' property cannot " 1353*2926Sek110237 "be inherited\n"), cb.cb_propname); 13542676Seschrock if (prop == ZFS_PROP_QUOTA || 13552676Seschrock prop == ZFS_PROP_RESERVATION) 13562676Seschrock (void) fprintf(stderr, gettext("use 'zfs set " 1357*2926Sek110237 "%s=none' to clear\n"), cb.cb_propname); 13582676Seschrock return (1); 13592676Seschrock } 1360*2926Sek110237 } else if (!zfs_prop_user(cb.cb_propname)) { 13612676Seschrock (void) fprintf(stderr, gettext( 13622676Seschrock "invalid property '%s'\n"), 1363*2926Sek110237 cb.cb_propname); 13642082Seschrock usage(B_FALSE); 1365789Sahrens } 13662676Seschrock 1367*2926Sek110237 cb.cb_any_successful = B_FALSE; 1368*2926Sek110237 1369*2926Sek110237 ret = zfs_for_each(argc, argv, recurse, 13702676Seschrock ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL, 1371*2926Sek110237 inherit_callback, &cb); 1372*2926Sek110237 1373*2926Sek110237 if (cb.cb_any_successful) { 1374*2926Sek110237 zpool_log_history(g_zfs, argc + optind + 1, argv - optind - 1, 1375*2926Sek110237 argv[0], B_FALSE, B_FALSE); 1376*2926Sek110237 } 1377*2926Sek110237 1378*2926Sek110237 return (ret); 1379789Sahrens } 1380789Sahrens 1381789Sahrens /* 13822379Ssjelinek * list [-rH] [-o property[,property]...] [-t type[,type]...] 13832379Ssjelinek * [-s property [-s property]...] [-S property [-S property]...] 13842379Ssjelinek * <dataset> ... 1385789Sahrens * 1386789Sahrens * -r Recurse over all children 1387789Sahrens * -H Scripted mode; elide headers and separate colums by tabs 1388789Sahrens * -o Control which fields to display. 1389866Seschrock * -t Control which object types to display. 13902379Ssjelinek * -s Specify sort columns, descending order. 13912379Ssjelinek * -S Specify sort columns, ascending order. 1392789Sahrens * 1393789Sahrens * When given no arguments, lists all filesystems in the system. 1394789Sahrens * Otherwise, list the specified datasets, optionally recursing down them if 1395789Sahrens * '-r' is specified. 1396789Sahrens */ 1397789Sahrens typedef struct list_cbdata { 13982082Seschrock boolean_t cb_first; 13992082Seschrock boolean_t cb_scripted; 14002676Seschrock zfs_proplist_t *cb_proplist; 1401789Sahrens } list_cbdata_t; 1402789Sahrens 1403789Sahrens /* 1404789Sahrens * Given a list of columns to display, output appropriate headers for each one. 1405789Sahrens */ 1406789Sahrens static void 14072676Seschrock print_header(zfs_proplist_t *pl) 1408789Sahrens { 14092676Seschrock char headerbuf[ZFS_MAXPROPLEN]; 14102676Seschrock const char *header; 1411789Sahrens int i; 14122676Seschrock boolean_t first = B_TRUE; 14132676Seschrock boolean_t right_justify; 14142676Seschrock 14152676Seschrock for (; pl != NULL; pl = pl->pl_next) { 14162676Seschrock if (!first) { 1417789Sahrens (void) printf(" "); 14182676Seschrock } else { 14192676Seschrock first = B_FALSE; 14202676Seschrock } 14212676Seschrock 14222676Seschrock right_justify = B_FALSE; 14232676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 14242676Seschrock header = zfs_prop_column_name(pl->pl_prop); 14252676Seschrock right_justify = zfs_prop_align_right(pl->pl_prop); 14262676Seschrock } else { 14272676Seschrock for (i = 0; pl->pl_user_prop[i] != '\0'; i++) 14282676Seschrock headerbuf[i] = toupper(pl->pl_user_prop[i]); 14292676Seschrock headerbuf[i] = '\0'; 14302676Seschrock header = headerbuf; 14312676Seschrock } 14322676Seschrock 14332676Seschrock if (pl->pl_next == NULL && !right_justify) 14342676Seschrock (void) printf("%s", header); 14352676Seschrock else if (right_justify) 14362676Seschrock (void) printf("%*s", pl->pl_width, header); 14372676Seschrock else 14382676Seschrock (void) printf("%-*s", pl->pl_width, header); 1439789Sahrens } 1440789Sahrens 1441789Sahrens (void) printf("\n"); 1442789Sahrens } 1443789Sahrens 1444789Sahrens /* 1445789Sahrens * Given a dataset and a list of fields, print out all the properties according 1446789Sahrens * to the described layout. 1447789Sahrens */ 1448789Sahrens static void 14492676Seschrock print_dataset(zfs_handle_t *zhp, zfs_proplist_t *pl, int scripted) 1450789Sahrens { 14512676Seschrock boolean_t first = B_TRUE; 1452789Sahrens char property[ZFS_MAXPROPLEN]; 14532676Seschrock nvlist_t *userprops = zfs_get_user_props(zhp); 14542676Seschrock nvlist_t *propval; 14552676Seschrock char *propstr; 14562676Seschrock boolean_t right_justify; 14572676Seschrock int width; 14582676Seschrock 14592676Seschrock for (; pl != NULL; pl = pl->pl_next) { 14602676Seschrock if (!first) { 1461789Sahrens if (scripted) 1462789Sahrens (void) printf("\t"); 1463789Sahrens else 1464789Sahrens (void) printf(" "); 14652676Seschrock } else { 14662676Seschrock first = B_FALSE; 1467789Sahrens } 1468789Sahrens 14692676Seschrock right_justify = B_FALSE; 14702676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 14712676Seschrock if (zfs_prop_get(zhp, pl->pl_prop, property, 14722676Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) != 0) 14732676Seschrock propstr = "-"; 14742676Seschrock else 14752676Seschrock propstr = property; 14762676Seschrock 14772676Seschrock right_justify = zfs_prop_align_right(pl->pl_prop); 14782676Seschrock } else { 14792676Seschrock if (nvlist_lookup_nvlist(userprops, 14802676Seschrock pl->pl_user_prop, &propval) != 0) 14812676Seschrock propstr = "-"; 14822676Seschrock else 14832676Seschrock verify(nvlist_lookup_string(propval, 14842676Seschrock ZFS_PROP_VALUE, &propstr) == 0); 14852676Seschrock } 14862676Seschrock 14872676Seschrock width = pl->pl_width; 1488789Sahrens 1489866Seschrock /* 1490866Seschrock * If this is being called in scripted mode, or if this is the 1491866Seschrock * last column and it is left-justified, don't include a width 1492866Seschrock * format specifier. 1493866Seschrock */ 14942676Seschrock if (scripted || (pl->pl_next == NULL && !right_justify)) 14952676Seschrock (void) printf("%s", propstr); 14962676Seschrock else if (right_justify) 14972676Seschrock (void) printf("%*s", width, propstr); 14982676Seschrock else 14992676Seschrock (void) printf("%-*s", width, propstr); 1500789Sahrens } 1501789Sahrens 1502789Sahrens (void) printf("\n"); 1503789Sahrens } 1504789Sahrens 1505789Sahrens /* 1506789Sahrens * Generic callback function to list a dataset or snapshot. 1507789Sahrens */ 1508789Sahrens static int 1509789Sahrens list_callback(zfs_handle_t *zhp, void *data) 1510789Sahrens { 1511789Sahrens list_cbdata_t *cbp = data; 1512789Sahrens 1513789Sahrens if (cbp->cb_first) { 1514789Sahrens if (!cbp->cb_scripted) 15152676Seschrock print_header(cbp->cb_proplist); 15162082Seschrock cbp->cb_first = B_FALSE; 1517789Sahrens } 1518789Sahrens 15192676Seschrock print_dataset(zhp, cbp->cb_proplist, cbp->cb_scripted); 1520789Sahrens 1521789Sahrens return (0); 1522789Sahrens } 1523789Sahrens 1524789Sahrens static int 1525789Sahrens zfs_do_list(int argc, char **argv) 1526789Sahrens { 1527789Sahrens int c; 15282082Seschrock boolean_t recurse = B_FALSE; 15292082Seschrock boolean_t scripted = B_FALSE; 1530789Sahrens static char default_fields[] = 1531789Sahrens "name,used,available,referenced,mountpoint"; 1532789Sahrens int types = ZFS_TYPE_ANY; 1533789Sahrens char *fields = NULL; 1534789Sahrens char *basic_fields = default_fields; 1535789Sahrens list_cbdata_t cb = { 0 }; 1536789Sahrens char *value; 1537789Sahrens int ret; 1538789Sahrens char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL }; 15392379Ssjelinek zfs_sort_column_t *sortcol = NULL; 1540789Sahrens 1541789Sahrens /* check options */ 15422379Ssjelinek while ((c = getopt(argc, argv, ":o:rt:Hs:S:")) != -1) { 1543789Sahrens switch (c) { 1544789Sahrens case 'o': 1545789Sahrens fields = optarg; 1546789Sahrens break; 1547789Sahrens case 'r': 15482082Seschrock recurse = B_TRUE; 1549789Sahrens break; 1550789Sahrens case 'H': 15512082Seschrock scripted = B_TRUE; 1552789Sahrens break; 15532379Ssjelinek case 's': 15542676Seschrock if (zfs_add_sort_column(&sortcol, optarg, 15552676Seschrock B_FALSE) != 0) { 15562379Ssjelinek (void) fprintf(stderr, 15572379Ssjelinek gettext("invalid property '%s'\n"), optarg); 15582379Ssjelinek usage(B_FALSE); 15592379Ssjelinek } 15602379Ssjelinek break; 15612379Ssjelinek case 'S': 15622676Seschrock if (zfs_add_sort_column(&sortcol, optarg, 15632676Seschrock B_TRUE) != 0) { 15642379Ssjelinek (void) fprintf(stderr, 15652379Ssjelinek gettext("invalid property '%s'\n"), optarg); 15662379Ssjelinek usage(B_FALSE); 15672379Ssjelinek } 15682379Ssjelinek break; 1569789Sahrens case 't': 1570789Sahrens types = 0; 1571789Sahrens while (*optarg != '\0') { 1572789Sahrens switch (getsubopt(&optarg, type_subopts, 1573789Sahrens &value)) { 1574789Sahrens case 0: 1575789Sahrens types |= ZFS_TYPE_FILESYSTEM; 1576789Sahrens break; 1577789Sahrens case 1: 1578789Sahrens types |= ZFS_TYPE_VOLUME; 1579789Sahrens break; 1580789Sahrens case 2: 1581789Sahrens types |= ZFS_TYPE_SNAPSHOT; 1582789Sahrens break; 1583789Sahrens default: 1584789Sahrens (void) fprintf(stderr, 1585789Sahrens gettext("invalid type '%s'\n"), 1586789Sahrens value); 15872082Seschrock usage(B_FALSE); 1588789Sahrens } 1589789Sahrens } 1590789Sahrens break; 1591789Sahrens case ':': 1592789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1593789Sahrens "'%c' option\n"), optopt); 15942082Seschrock usage(B_FALSE); 1595789Sahrens break; 1596789Sahrens case '?': 1597789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1598789Sahrens optopt); 15992082Seschrock usage(B_FALSE); 1600789Sahrens } 1601789Sahrens } 1602789Sahrens 1603789Sahrens argc -= optind; 1604789Sahrens argv += optind; 1605789Sahrens 1606789Sahrens if (fields == NULL) 1607789Sahrens fields = basic_fields; 1608789Sahrens 1609866Seschrock /* 1610866Seschrock * If the user specifies '-o all', the zfs_get_proplist() doesn't 1611866Seschrock * normally include the name of the dataset. For 'zfs list', we always 1612866Seschrock * want this property to be first. 1613866Seschrock */ 16142676Seschrock if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0) 16152082Seschrock usage(B_FALSE); 16162676Seschrock 1617789Sahrens cb.cb_scripted = scripted; 16182082Seschrock cb.cb_first = B_TRUE; 1619789Sahrens 16202676Seschrock ret = zfs_for_each(argc, argv, recurse, types, sortcol, &cb.cb_proplist, 16212379Ssjelinek list_callback, &cb); 16222379Ssjelinek 16232676Seschrock zfs_free_proplist(cb.cb_proplist); 16242379Ssjelinek zfs_free_sort_columns(sortcol); 1625789Sahrens 16262082Seschrock if (ret == 0 && cb.cb_first) 1627789Sahrens (void) printf(gettext("no datasets available\n")); 1628789Sahrens 1629789Sahrens return (ret); 1630789Sahrens } 1631789Sahrens 1632789Sahrens /* 1633789Sahrens * zfs rename <fs | snap | vol> <fs | snap | vol> 1634789Sahrens * 1635789Sahrens * Renames the given dataset to another of the same type. 1636789Sahrens */ 1637789Sahrens /* ARGSUSED */ 1638789Sahrens static int 1639789Sahrens zfs_do_rename(int argc, char **argv) 1640789Sahrens { 1641789Sahrens zfs_handle_t *zhp; 16422082Seschrock int ret; 1643789Sahrens 1644789Sahrens /* check options */ 1645789Sahrens if (argc > 1 && argv[1][0] == '-') { 1646789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1647789Sahrens argv[1][1]); 16482082Seschrock usage(B_FALSE); 1649789Sahrens } 1650789Sahrens 1651789Sahrens /* check number of arguments */ 1652789Sahrens if (argc < 2) { 1653789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 1654789Sahrens "argument\n")); 16552082Seschrock usage(B_FALSE); 1656789Sahrens } 1657789Sahrens if (argc < 3) { 1658789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 1659789Sahrens "argument\n")); 16602082Seschrock usage(B_FALSE); 1661789Sahrens } 1662789Sahrens if (argc > 3) { 1663789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 16642082Seschrock usage(B_FALSE); 1665789Sahrens } 1666789Sahrens 16672082Seschrock if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_ANY)) == NULL) 1668789Sahrens return (1); 1669789Sahrens 16702082Seschrock ret = (zfs_rename(zhp, argv[2]) != 0); 16712082Seschrock 1672*2926Sek110237 if (!ret) 1673*2926Sek110237 zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE); 1674*2926Sek110237 16752082Seschrock zfs_close(zhp); 16762082Seschrock return (ret); 16772082Seschrock } 16782082Seschrock 16792082Seschrock /* 16802082Seschrock * zfs promote <fs> 16812082Seschrock * 16822082Seschrock * Promotes the given clone fs to be the parent 16832082Seschrock */ 16842082Seschrock /* ARGSUSED */ 16852082Seschrock static int 16862082Seschrock zfs_do_promote(int argc, char **argv) 16872082Seschrock { 16882082Seschrock zfs_handle_t *zhp; 16892082Seschrock int ret; 16902082Seschrock 16912082Seschrock /* check options */ 16922082Seschrock if (argc > 1 && argv[1][0] == '-') { 16932082Seschrock (void) fprintf(stderr, gettext("invalid option '%c'\n"), 16942082Seschrock argv[1][1]); 16952082Seschrock usage(B_FALSE); 16962082Seschrock } 16972082Seschrock 16982082Seschrock /* check number of arguments */ 16992082Seschrock if (argc < 2) { 17002082Seschrock (void) fprintf(stderr, gettext("missing clone filesystem" 17012597Snd150628 " argument\n")); 17022082Seschrock usage(B_FALSE); 17032082Seschrock } 17042082Seschrock if (argc > 2) { 17052082Seschrock (void) fprintf(stderr, gettext("too many arguments\n")); 17062082Seschrock usage(B_FALSE); 17072082Seschrock } 17082082Seschrock 17092082Seschrock zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 17102082Seschrock if (zhp == NULL) 17112082Seschrock return (1); 17122082Seschrock 17132082Seschrock ret = (zfs_promote(zhp) != 0); 17142082Seschrock 1715*2926Sek110237 if (!ret) 1716*2926Sek110237 zpool_log_history(g_zfs, argc, argv, argv[1], B_FALSE, B_FALSE); 1717*2926Sek110237 1718789Sahrens zfs_close(zhp); 1719789Sahrens return (ret); 1720789Sahrens } 1721789Sahrens 1722789Sahrens /* 1723789Sahrens * zfs rollback [-rfR] <snapshot> 1724789Sahrens * 1725789Sahrens * -r Delete any intervening snapshots before doing rollback 1726789Sahrens * -R Delete any snapshots and their clones 1727789Sahrens * -f Force unmount filesystems, even if they are in use. 1728789Sahrens * 1729789Sahrens * Given a filesystem, rollback to a specific snapshot, discarding any changes 1730789Sahrens * since then and making it the active dataset. If more recent snapshots exist, 1731789Sahrens * the command will complain unless the '-r' flag is given. 1732789Sahrens */ 1733789Sahrens typedef struct rollback_cbdata { 1734789Sahrens uint64_t cb_create; 17352082Seschrock boolean_t cb_first; 1736789Sahrens int cb_doclones; 1737789Sahrens char *cb_target; 1738789Sahrens int cb_error; 17392082Seschrock boolean_t cb_recurse; 17402082Seschrock boolean_t cb_dependent; 1741789Sahrens } rollback_cbdata_t; 1742789Sahrens 1743789Sahrens /* 1744789Sahrens * Report any snapshots more recent than the one specified. Used when '-r' is 1745789Sahrens * not specified. We reuse this same callback for the snapshot dependents - if 1746789Sahrens * 'cb_dependent' is set, then this is a dependent and we should report it 1747789Sahrens * without checking the transaction group. 1748789Sahrens */ 1749789Sahrens static int 1750789Sahrens rollback_check(zfs_handle_t *zhp, void *data) 1751789Sahrens { 1752789Sahrens rollback_cbdata_t *cbp = data; 1753789Sahrens 17542082Seschrock if (cbp->cb_doclones) { 17552082Seschrock zfs_close(zhp); 1756789Sahrens return (0); 17572082Seschrock } 1758789Sahrens 1759789Sahrens if (!cbp->cb_dependent) { 1760789Sahrens if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && 17611294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 1762789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 1763789Sahrens cbp->cb_create) { 1764789Sahrens 1765789Sahrens if (cbp->cb_first && !cbp->cb_recurse) { 1766789Sahrens (void) fprintf(stderr, gettext("cannot " 1767789Sahrens "rollback to '%s': more recent snapshots " 1768789Sahrens "exist\n"), 1769789Sahrens cbp->cb_target); 1770789Sahrens (void) fprintf(stderr, gettext("use '-r' to " 1771789Sahrens "force deletion of the following " 1772789Sahrens "snapshots:\n")); 1773789Sahrens cbp->cb_first = 0; 1774789Sahrens cbp->cb_error = 1; 1775789Sahrens } 1776789Sahrens 1777789Sahrens if (cbp->cb_recurse) { 17782082Seschrock cbp->cb_dependent = B_TRUE; 17792474Seschrock if (zfs_iter_dependents(zhp, B_TRUE, 17802474Seschrock rollback_check, cbp) != 0) { 17812474Seschrock zfs_close(zhp); 17822474Seschrock return (-1); 17832474Seschrock } 17842082Seschrock cbp->cb_dependent = B_FALSE; 1785789Sahrens } else { 1786789Sahrens (void) fprintf(stderr, "%s\n", 1787789Sahrens zfs_get_name(zhp)); 1788789Sahrens } 1789789Sahrens } 1790789Sahrens } else { 1791789Sahrens if (cbp->cb_first && cbp->cb_recurse) { 1792789Sahrens (void) fprintf(stderr, gettext("cannot rollback to " 1793789Sahrens "'%s': clones of previous snapshots exist\n"), 1794789Sahrens cbp->cb_target); 1795789Sahrens (void) fprintf(stderr, gettext("use '-R' to " 1796789Sahrens "force deletion of the following clones and " 1797789Sahrens "dependents:\n")); 1798789Sahrens cbp->cb_first = 0; 1799789Sahrens cbp->cb_error = 1; 1800789Sahrens } 1801789Sahrens 1802789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1803789Sahrens } 1804789Sahrens 1805789Sahrens zfs_close(zhp); 1806789Sahrens return (0); 1807789Sahrens } 1808789Sahrens 1809789Sahrens static int 1810789Sahrens zfs_do_rollback(int argc, char **argv) 1811789Sahrens { 1812789Sahrens int ret; 1813789Sahrens int c; 1814789Sahrens rollback_cbdata_t cb = { 0 }; 1815789Sahrens zfs_handle_t *zhp, *snap; 1816789Sahrens char parentname[ZFS_MAXNAMELEN]; 1817789Sahrens char *delim; 18181294Slling int force = 0; 1819789Sahrens 1820789Sahrens /* check options */ 1821789Sahrens while ((c = getopt(argc, argv, "rfR")) != -1) { 1822789Sahrens switch (c) { 1823789Sahrens case 'f': 18241294Slling force = 1; 1825789Sahrens break; 1826789Sahrens case 'r': 1827789Sahrens cb.cb_recurse = 1; 1828789Sahrens break; 1829789Sahrens case 'R': 1830789Sahrens cb.cb_recurse = 1; 1831789Sahrens cb.cb_doclones = 1; 1832789Sahrens break; 1833789Sahrens case '?': 1834789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1835789Sahrens optopt); 18362082Seschrock usage(B_FALSE); 1837789Sahrens } 1838789Sahrens } 1839789Sahrens 1840789Sahrens argc -= optind; 1841789Sahrens argv += optind; 1842789Sahrens 1843789Sahrens /* check number of arguments */ 1844789Sahrens if (argc < 1) { 1845789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 18462082Seschrock usage(B_FALSE); 1847789Sahrens } 1848789Sahrens if (argc > 1) { 1849789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 18502082Seschrock usage(B_FALSE); 1851789Sahrens } 1852789Sahrens 1853789Sahrens /* open the snapshot */ 18542082Seschrock if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1855789Sahrens return (1); 1856789Sahrens 18571294Slling /* open the parent dataset */ 18581294Slling (void) strlcpy(parentname, argv[0], sizeof (parentname)); 1859789Sahrens verify((delim = strrchr(parentname, '@')) != NULL); 1860789Sahrens *delim = '\0'; 18612082Seschrock if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_ANY)) == NULL) { 1862789Sahrens zfs_close(snap); 1863789Sahrens return (1); 1864789Sahrens } 1865789Sahrens 1866789Sahrens /* 1867789Sahrens * Check for more recent snapshots and/or clones based on the presence 1868789Sahrens * of '-r' and '-R'. 1869789Sahrens */ 18701294Slling cb.cb_target = argv[0]; 18711294Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 18722082Seschrock cb.cb_first = B_TRUE; 1873789Sahrens cb.cb_error = 0; 18742474Seschrock if ((ret = zfs_iter_children(zhp, rollback_check, &cb)) != 0) 18752474Seschrock goto out; 1876789Sahrens 1877789Sahrens if ((ret = cb.cb_error) != 0) 1878789Sahrens goto out; 1879789Sahrens 1880789Sahrens /* 18811294Slling * Rollback parent to the given snapshot. 1882789Sahrens */ 18831294Slling ret = zfs_rollback(zhp, snap, force); 1884789Sahrens 1885*2926Sek110237 if (!ret) { 1886*2926Sek110237 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0], 1887*2926Sek110237 B_FALSE, B_FALSE); 1888*2926Sek110237 } 1889*2926Sek110237 1890789Sahrens out: 1891789Sahrens zfs_close(snap); 1892789Sahrens zfs_close(zhp); 1893789Sahrens 1894789Sahrens if (ret == 0) 1895789Sahrens return (0); 1896789Sahrens else 1897789Sahrens return (1); 1898789Sahrens } 1899789Sahrens 1900789Sahrens /* 1901789Sahrens * zfs set property=value { fs | snap | vol } ... 1902789Sahrens * 1903789Sahrens * Sets the given property for all datasets specified on the command line. 1904789Sahrens */ 1905789Sahrens typedef struct set_cbdata { 1906789Sahrens char *cb_propname; 1907789Sahrens char *cb_value; 1908*2926Sek110237 boolean_t cb_any_successful; 1909789Sahrens } set_cbdata_t; 1910789Sahrens 1911789Sahrens static int 1912789Sahrens set_callback(zfs_handle_t *zhp, void *data) 1913789Sahrens { 1914789Sahrens set_cbdata_t *cbp = data; 1915789Sahrens 19162676Seschrock if (zfs_prop_set(zhp, cbp->cb_propname, cbp->cb_value) != 0) { 19172169Snd150628 switch (libzfs_errno(g_zfs)) { 19182169Snd150628 case EZFS_MOUNTFAILED: 19192169Snd150628 (void) fprintf(stderr, gettext("property may be set " 19202169Snd150628 "but unable to remount filesystem\n")); 19212169Snd150628 break; 19222169Snd150628 case EZFS_SHAREFAILED: 19232169Snd150628 (void) fprintf(stderr, gettext("property may be set " 19242169Snd150628 "but unable to reshare filesystem\n")); 19252169Snd150628 break; 19262169Snd150628 } 1927789Sahrens return (1); 19282169Snd150628 } 1929*2926Sek110237 cbp->cb_any_successful = B_TRUE; 19302856Snd150628 return (0); 1931789Sahrens } 1932789Sahrens 1933789Sahrens static int 1934789Sahrens zfs_do_set(int argc, char **argv) 1935789Sahrens { 1936789Sahrens set_cbdata_t cb; 1937*2926Sek110237 int ret; 1938789Sahrens 1939789Sahrens /* check for options */ 1940789Sahrens if (argc > 1 && argv[1][0] == '-') { 1941789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1942789Sahrens argv[1][1]); 19432082Seschrock usage(B_FALSE); 1944789Sahrens } 1945789Sahrens 1946789Sahrens /* check number of arguments */ 1947789Sahrens if (argc < 2) { 1948789Sahrens (void) fprintf(stderr, gettext("missing property=value " 1949789Sahrens "argument\n")); 19502082Seschrock usage(B_FALSE); 1951789Sahrens } 1952789Sahrens if (argc < 3) { 1953789Sahrens (void) fprintf(stderr, gettext("missing dataset name\n")); 19542082Seschrock usage(B_FALSE); 1955789Sahrens } 1956789Sahrens 1957789Sahrens /* validate property=value argument */ 1958789Sahrens cb.cb_propname = argv[1]; 1959789Sahrens if ((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) { 1960789Sahrens (void) fprintf(stderr, gettext("missing value in " 1961789Sahrens "property=value argument\n")); 19622082Seschrock usage(B_FALSE); 1963789Sahrens } 1964789Sahrens 1965789Sahrens *cb.cb_value = '\0'; 1966789Sahrens cb.cb_value++; 1967*2926Sek110237 cb.cb_any_successful = B_FALSE; 1968789Sahrens 1969789Sahrens if (*cb.cb_propname == '\0') { 1970789Sahrens (void) fprintf(stderr, 1971789Sahrens gettext("missing property in property=value argument\n")); 19722082Seschrock usage(B_FALSE); 1973789Sahrens } 1974789Sahrens 1975*2926Sek110237 ret = zfs_for_each(argc - 2, argv + 2, B_FALSE, 1976*2926Sek110237 ZFS_TYPE_ANY, NULL, NULL, set_callback, &cb); 1977*2926Sek110237 1978*2926Sek110237 if (cb.cb_any_successful) { 1979*2926Sek110237 *(cb.cb_value - 1) = '='; 1980*2926Sek110237 zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE); 1981*2926Sek110237 } 1982*2926Sek110237 1983*2926Sek110237 return (ret); 1984789Sahrens } 1985789Sahrens 1986789Sahrens /* 19872199Sahrens * zfs snapshot [-r] <fs@snap> 1988789Sahrens * 1989789Sahrens * Creates a snapshot with the given name. While functionally equivalent to 1990789Sahrens * 'zfs create', it is a separate command to diffferentiate intent. 1991789Sahrens */ 1992789Sahrens static int 1993789Sahrens zfs_do_snapshot(int argc, char **argv) 1994789Sahrens { 19952199Sahrens int recursive = B_FALSE; 19962199Sahrens int ret; 19972199Sahrens char c; 19982199Sahrens 1999789Sahrens /* check options */ 20002199Sahrens while ((c = getopt(argc, argv, ":r")) != -1) { 20012199Sahrens switch (c) { 20022199Sahrens case 'r': 20032199Sahrens recursive = B_TRUE; 20042199Sahrens break; 20052199Sahrens case '?': 20062199Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 20072199Sahrens optopt); 20082199Sahrens usage(B_FALSE); 20092199Sahrens } 2010789Sahrens } 2011789Sahrens 20122199Sahrens argc -= optind; 20132199Sahrens argv += optind; 20142199Sahrens 2015789Sahrens /* check number of arguments */ 20162199Sahrens if (argc < 1) { 2017789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 20182082Seschrock usage(B_FALSE); 2019789Sahrens } 20202199Sahrens if (argc > 1) { 2021789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 20222082Seschrock usage(B_FALSE); 2023789Sahrens } 2024789Sahrens 20252199Sahrens ret = zfs_snapshot(g_zfs, argv[0], recursive); 20262199Sahrens if (ret && recursive) 20272199Sahrens (void) fprintf(stderr, gettext("no snapshots were created\n")); 2028*2926Sek110237 if (!ret) { 2029*2926Sek110237 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0], 2030*2926Sek110237 B_FALSE, B_FALSE); 2031*2926Sek110237 } 20322199Sahrens return (ret != 0); 2033789Sahrens } 2034789Sahrens 2035789Sahrens /* 20362885Sahrens * zfs send [-i <@snap>] <fs@snap> 2037789Sahrens * 2038789Sahrens * Send a backup stream to stdout. 2039789Sahrens */ 2040789Sahrens static int 20411749Sahrens zfs_do_send(int argc, char **argv) 2042789Sahrens { 2043789Sahrens char *fromname = NULL; 20442885Sahrens char *cp; 20452885Sahrens zfs_handle_t *zhp; 2046789Sahrens int c, err; 2047789Sahrens 2048789Sahrens /* check options */ 2049789Sahrens while ((c = getopt(argc, argv, ":i:")) != -1) { 2050789Sahrens switch (c) { 2051789Sahrens case 'i': 20522885Sahrens if (fromname) 20532885Sahrens usage(B_FALSE); 2054789Sahrens fromname = optarg; 2055789Sahrens break; 2056789Sahrens case ':': 2057789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2058789Sahrens "'%c' option\n"), optopt); 20592082Seschrock usage(B_FALSE); 2060789Sahrens break; 2061789Sahrens case '?': 2062789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2063789Sahrens optopt); 20642082Seschrock usage(B_FALSE); 2065789Sahrens } 2066789Sahrens } 2067789Sahrens 2068789Sahrens argc -= optind; 2069789Sahrens argv += optind; 2070789Sahrens 2071789Sahrens /* check number of arguments */ 2072789Sahrens if (argc < 1) { 2073789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 20742082Seschrock usage(B_FALSE); 2075789Sahrens } 2076789Sahrens if (argc > 1) { 2077789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 20782082Seschrock usage(B_FALSE); 2079789Sahrens } 2080789Sahrens 2081789Sahrens if (isatty(STDOUT_FILENO)) { 2082789Sahrens (void) fprintf(stderr, 20832885Sahrens gettext("Error: Stream can not be written to a terminal.\n" 2084789Sahrens "You must redirect standard output.\n")); 2085789Sahrens return (1); 2086789Sahrens } 2087789Sahrens 20882885Sahrens if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 20892665Snd150628 return (1); 20902665Snd150628 20912885Sahrens /* 20922885Sahrens * If they specified the full path to the snapshot, chop off 20932885Sahrens * everything except the short name of the snapshot. 20942885Sahrens */ 20952885Sahrens if (fromname && (cp = strchr(fromname, '@')) != NULL) { 20962885Sahrens if (cp != fromname && 20972885Sahrens strncmp(argv[0], fromname, cp - fromname + 1)) { 20982885Sahrens (void) fprintf(stderr, 20992885Sahrens gettext("incremental source must be " 21002885Sahrens "in same filesystem\n")); 21012885Sahrens usage(B_FALSE); 21022665Snd150628 } 21032885Sahrens fromname = cp + 1; 21042885Sahrens if (strchr(fromname, '@') || strchr(fromname, '/')) { 21052885Sahrens (void) fprintf(stderr, 21062885Sahrens gettext("invalid incremental source\n")); 21072885Sahrens usage(B_FALSE); 21082885Sahrens } 2109789Sahrens } 2110789Sahrens 21112885Sahrens err = zfs_send(zhp, fromname); 21122885Sahrens zfs_close(zhp); 2113789Sahrens 2114789Sahrens return (err != 0); 2115789Sahrens } 2116789Sahrens 2117789Sahrens /* 21181749Sahrens * zfs receive <fs@snap> 2119789Sahrens * 2120789Sahrens * Restore a backup stream from stdin. 2121789Sahrens */ 2122789Sahrens static int 21231749Sahrens zfs_do_receive(int argc, char **argv) 2124789Sahrens { 2125789Sahrens int c, err; 21262082Seschrock boolean_t isprefix = B_FALSE; 21272082Seschrock boolean_t dryrun = B_FALSE; 21282082Seschrock boolean_t verbose = B_FALSE; 21292665Snd150628 boolean_t force = B_FALSE; 2130789Sahrens 2131789Sahrens /* check options */ 21322665Snd150628 while ((c = getopt(argc, argv, ":dnvF")) != -1) { 2133789Sahrens switch (c) { 2134789Sahrens case 'd': 21352082Seschrock isprefix = B_TRUE; 2136789Sahrens break; 2137789Sahrens case 'n': 21382082Seschrock dryrun = B_TRUE; 2139789Sahrens break; 2140789Sahrens case 'v': 21412082Seschrock verbose = B_TRUE; 2142789Sahrens break; 21432665Snd150628 case 'F': 21442665Snd150628 force = B_TRUE; 21452665Snd150628 break; 2146789Sahrens case ':': 2147789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2148789Sahrens "'%c' option\n"), optopt); 21492082Seschrock usage(B_FALSE); 2150789Sahrens break; 2151789Sahrens case '?': 2152789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2153789Sahrens optopt); 21542082Seschrock usage(B_FALSE); 2155789Sahrens } 2156789Sahrens } 2157789Sahrens 2158789Sahrens argc -= optind; 2159789Sahrens argv += optind; 2160789Sahrens 2161789Sahrens /* check number of arguments */ 2162789Sahrens if (argc < 1) { 2163789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 21642082Seschrock usage(B_FALSE); 2165789Sahrens } 2166789Sahrens if (argc > 1) { 2167789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 21682082Seschrock usage(B_FALSE); 2169789Sahrens } 2170789Sahrens 2171789Sahrens if (isatty(STDIN_FILENO)) { 2172789Sahrens (void) fprintf(stderr, 2173789Sahrens gettext("Error: Backup stream can not be read " 2174789Sahrens "from a terminal.\n" 2175789Sahrens "You must redirect standard input.\n")); 2176789Sahrens return (1); 2177789Sahrens } 2178789Sahrens 21792665Snd150628 err = zfs_receive(g_zfs, argv[0], isprefix, verbose, dryrun, force); 2180*2926Sek110237 2181*2926Sek110237 if (!err) { 2182*2926Sek110237 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0], 2183*2926Sek110237 B_FALSE, B_FALSE); 2184*2926Sek110237 } 2185*2926Sek110237 2186789Sahrens return (err != 0); 2187789Sahrens } 2188789Sahrens 21891356Seschrock typedef struct get_all_cbdata { 21901356Seschrock zfs_handle_t **cb_handles; 21911356Seschrock size_t cb_alloc; 21921356Seschrock size_t cb_used; 21931356Seschrock } get_all_cbdata_t; 21941356Seschrock 21951356Seschrock static int 21961356Seschrock get_one_filesystem(zfs_handle_t *zhp, void *data) 21971356Seschrock { 21981356Seschrock get_all_cbdata_t *cbp = data; 21991356Seschrock 22001356Seschrock /* 22011356Seschrock * Skip any zvols 22021356Seschrock */ 22031356Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 22041356Seschrock zfs_close(zhp); 22051356Seschrock return (0); 22061356Seschrock } 22071356Seschrock 22081356Seschrock if (cbp->cb_alloc == cbp->cb_used) { 22091356Seschrock zfs_handle_t **handles; 22101356Seschrock 22111356Seschrock if (cbp->cb_alloc == 0) 22121356Seschrock cbp->cb_alloc = 64; 22131356Seschrock else 22141356Seschrock cbp->cb_alloc *= 2; 22151356Seschrock 22161356Seschrock handles = safe_malloc(cbp->cb_alloc * sizeof (void *)); 22171356Seschrock 22181356Seschrock if (cbp->cb_handles) { 22191356Seschrock bcopy(cbp->cb_handles, handles, 22201356Seschrock cbp->cb_used * sizeof (void *)); 22211356Seschrock free(cbp->cb_handles); 22221356Seschrock } 22231356Seschrock 22241356Seschrock cbp->cb_handles = handles; 22251356Seschrock } 22261356Seschrock 22271356Seschrock cbp->cb_handles[cbp->cb_used++] = zhp; 22281356Seschrock 22291356Seschrock return (zfs_iter_filesystems(zhp, get_one_filesystem, data)); 22301356Seschrock } 22311356Seschrock 22321356Seschrock static void 22331356Seschrock get_all_filesystems(zfs_handle_t ***fslist, size_t *count) 22341356Seschrock { 22351356Seschrock get_all_cbdata_t cb = { 0 }; 22361356Seschrock 22372082Seschrock (void) zfs_iter_root(g_zfs, get_one_filesystem, &cb); 22381356Seschrock 22391356Seschrock *fslist = cb.cb_handles; 22401356Seschrock *count = cb.cb_used; 22411356Seschrock } 22421356Seschrock 22431356Seschrock static int 22441356Seschrock mountpoint_compare(const void *a, const void *b) 22451356Seschrock { 22461356Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 22471356Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 22481356Seschrock char mounta[MAXPATHLEN]; 22491356Seschrock char mountb[MAXPATHLEN]; 22501356Seschrock 22511356Seschrock verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 22522082Seschrock sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 22531356Seschrock verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 22542082Seschrock sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 22551356Seschrock 22561356Seschrock return (strcmp(mounta, mountb)); 22571356Seschrock } 2258789Sahrens 2259789Sahrens /* 2260789Sahrens * Generic callback for sharing or mounting filesystems. Because the code is so 2261789Sahrens * similar, we have a common function with an extra parameter to determine which 2262789Sahrens * mode we are using. 2263789Sahrens */ 2264789Sahrens #define OP_SHARE 0x1 2265789Sahrens #define OP_MOUNT 0x2 2266789Sahrens 2267789Sahrens typedef struct share_mount_cbdata { 2268789Sahrens int cb_type; 2269789Sahrens int cb_explicit; 2270789Sahrens int cb_flags; 2271789Sahrens const char *cb_options; 2272789Sahrens } share_mount_cbdata_t; 2273789Sahrens 2274789Sahrens /* 2275789Sahrens * Share or mount the filesystem. 2276789Sahrens */ 2277789Sahrens static int 2278789Sahrens share_mount_callback(zfs_handle_t *zhp, void *data) 2279789Sahrens { 2280789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2281789Sahrens char shareopts[ZFS_MAXPROPLEN]; 2282789Sahrens share_mount_cbdata_t *cbp = data; 2283789Sahrens const char *cmdname = cbp->cb_type == OP_SHARE ? "share" : "mount"; 2284789Sahrens struct mnttab mnt; 22852676Seschrock uint64_t zoned, canmount; 2286789Sahrens 2287789Sahrens if (cbp->cb_options == NULL) 2288789Sahrens mnt.mnt_mntopts = ""; 2289789Sahrens else 2290789Sahrens mnt.mnt_mntopts = (char *)cbp->cb_options; 2291789Sahrens 2292789Sahrens /* 2293789Sahrens * Check to make sure we can mount/share this dataset. If we are in the 2294789Sahrens * global zone and the filesystem is exported to a local zone, or if we 2295789Sahrens * are in a local zone and the filesystem is not exported, then it is an 2296789Sahrens * error. 2297789Sahrens */ 2298789Sahrens zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2299789Sahrens 2300789Sahrens if (zoned && getzoneid() == GLOBAL_ZONEID) { 2301789Sahrens if (!cbp->cb_explicit) 2302789Sahrens return (0); 2303789Sahrens 2304789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': dataset is " 2305789Sahrens "exported to a local zone\n"), cmdname, zfs_get_name(zhp)); 2306789Sahrens return (1); 2307789Sahrens 2308789Sahrens } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 2309789Sahrens if (!cbp->cb_explicit) 2310789Sahrens return (0); 2311789Sahrens 2312789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': permission " 2313789Sahrens "denied\n"), cmdname, zfs_get_name(zhp)); 2314789Sahrens return (1); 2315789Sahrens } 2316789Sahrens 2317789Sahrens /* 2318789Sahrens * Inore any filesystems which don't apply to us. This includes those 2319789Sahrens * with a legacy mountpoint, or those with legacy share options. 2320789Sahrens */ 2321789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 23222082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); 2323789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 23242082Seschrock sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 23252676Seschrock canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); 2326789Sahrens 2327789Sahrens if (cbp->cb_type == OP_SHARE) { 2328789Sahrens if (strcmp(shareopts, "off") == 0) { 2329789Sahrens if (!cbp->cb_explicit) 2330789Sahrens return (0); 2331789Sahrens 2332789Sahrens (void) fprintf(stderr, gettext("cannot share '%s': " 2333789Sahrens "legacy share\n"), zfs_get_name(zhp)); 2334789Sahrens (void) fprintf(stderr, gettext("use share(1M) to " 2335789Sahrens "share this filesystem\n")); 2336789Sahrens return (1); 2337789Sahrens } 2338789Sahrens } 2339789Sahrens 2340789Sahrens /* 2341789Sahrens * We cannot share or mount legacy filesystems. If the shareopts is 2342789Sahrens * non-legacy but the mountpoint is legacy, we treat it as a legacy 2343789Sahrens * share. 2344789Sahrens */ 2345789Sahrens if (strcmp(mountpoint, "legacy") == 0) { 2346789Sahrens if (!cbp->cb_explicit) 2347789Sahrens return (0); 2348789Sahrens 2349789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': " 2350789Sahrens "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 2351789Sahrens (void) fprintf(stderr, gettext("use %s to " 2352789Sahrens "%s this filesystem\n"), cbp->cb_type == OP_SHARE ? 2353789Sahrens "share(1M)" : "mount(1M)", cmdname); 2354789Sahrens return (1); 2355789Sahrens } 2356789Sahrens 2357789Sahrens if (strcmp(mountpoint, "none") == 0) { 2358789Sahrens if (!cbp->cb_explicit) 2359789Sahrens return (0); 2360789Sahrens 2361789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': no " 2362789Sahrens "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 2363789Sahrens return (1); 2364789Sahrens } 2365789Sahrens 23662676Seschrock if (!canmount) { 23672676Seschrock if (!cbp->cb_explicit) 23682676Seschrock return (0); 23692676Seschrock 23702676Seschrock (void) fprintf(stderr, gettext("cannot %s '%s': 'canmount' " 23712676Seschrock "property is set to 'off'\n"), cmdname, zfs_get_name(zhp)); 23722676Seschrock return (1); 23732676Seschrock } 23742676Seschrock 2375789Sahrens /* 2376789Sahrens * At this point, we have verified that the mountpoint and/or shareopts 2377789Sahrens * are appropriate for auto management. Determine if the filesystem is 2378789Sahrens * currently mounted or shared, and abort if this is an explicit 2379789Sahrens * request. 2380789Sahrens */ 2381789Sahrens switch (cbp->cb_type) { 2382789Sahrens case OP_SHARE: 2383789Sahrens if (zfs_is_shared(zhp, NULL)) { 2384789Sahrens if (cbp->cb_explicit) { 2385789Sahrens (void) fprintf(stderr, gettext("cannot share " 2386789Sahrens "'%s': filesystem already shared\n"), 2387789Sahrens zfs_get_name(zhp)); 2388789Sahrens return (1); 2389789Sahrens } else { 2390789Sahrens return (0); 2391789Sahrens } 2392789Sahrens } 2393789Sahrens break; 2394789Sahrens 2395789Sahrens case OP_MOUNT: 2396789Sahrens if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 2397789Sahrens zfs_is_mounted(zhp, NULL)) { 2398789Sahrens if (cbp->cb_explicit) { 2399789Sahrens (void) fprintf(stderr, gettext("cannot mount " 2400789Sahrens "'%s': filesystem already mounted\n"), 2401789Sahrens zfs_get_name(zhp)); 2402789Sahrens return (1); 2403789Sahrens } else { 2404789Sahrens return (0); 2405789Sahrens } 2406789Sahrens } 2407789Sahrens break; 2408789Sahrens } 2409789Sahrens 2410789Sahrens /* 2411789Sahrens * Mount and optionally share the filesystem. 2412789Sahrens */ 2413789Sahrens switch (cbp->cb_type) { 2414789Sahrens case OP_SHARE: 2415789Sahrens { 2416789Sahrens if (!zfs_is_mounted(zhp, NULL) && 2417789Sahrens zfs_mount(zhp, NULL, 0) != 0) 2418789Sahrens return (1); 2419789Sahrens 2420789Sahrens if (zfs_share(zhp) != 0) 2421789Sahrens return (1); 2422789Sahrens } 2423789Sahrens break; 2424789Sahrens 2425789Sahrens case OP_MOUNT: 2426789Sahrens if (zfs_mount(zhp, cbp->cb_options, cbp->cb_flags) != 0) 2427789Sahrens return (1); 2428789Sahrens break; 2429789Sahrens } 2430789Sahrens 2431789Sahrens return (0); 2432789Sahrens } 2433789Sahrens 2434789Sahrens static int 2435789Sahrens share_or_mount(int type, int argc, char **argv) 2436789Sahrens { 2437789Sahrens int do_all = 0; 24382372Slling int c, ret = 0; 2439789Sahrens share_mount_cbdata_t cb = { 0 }; 2440789Sahrens 2441789Sahrens cb.cb_type = type; 2442789Sahrens 2443789Sahrens /* check options */ 2444789Sahrens while ((c = getopt(argc, argv, type == OP_MOUNT ? ":ao:O" : "a")) 2445789Sahrens != -1) { 2446789Sahrens switch (c) { 2447789Sahrens case 'a': 2448789Sahrens do_all = 1; 2449789Sahrens break; 2450789Sahrens case 'o': 2451789Sahrens cb.cb_options = optarg; 2452789Sahrens break; 2453789Sahrens case 'O': 2454789Sahrens cb.cb_flags |= MS_OVERLAY; 2455789Sahrens break; 2456789Sahrens case ':': 2457789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2458789Sahrens "'%c' option\n"), optopt); 24592082Seschrock usage(B_FALSE); 2460789Sahrens break; 2461789Sahrens case '?': 2462789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2463789Sahrens optopt); 24642082Seschrock usage(B_FALSE); 2465789Sahrens } 2466789Sahrens } 2467789Sahrens 2468789Sahrens argc -= optind; 2469789Sahrens argv += optind; 2470789Sahrens 2471789Sahrens /* check number of arguments */ 2472789Sahrens if (do_all) { 24731356Seschrock zfs_handle_t **fslist = NULL; 24741356Seschrock size_t i, count = 0; 24751356Seschrock 2476789Sahrens if (argc != 0) { 2477789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 24782082Seschrock usage(B_FALSE); 2479789Sahrens } 2480789Sahrens 24811356Seschrock get_all_filesystems(&fslist, &count); 24821356Seschrock 24831356Seschrock if (count == 0) 24841356Seschrock return (0); 24851356Seschrock 24861356Seschrock qsort(fslist, count, sizeof (void *), mountpoint_compare); 24871356Seschrock 24881356Seschrock for (i = 0; i < count; i++) { 24892369Slling if (share_mount_callback(fslist[i], &cb) != 0) 24902369Slling ret = 1; 24911356Seschrock } 24921356Seschrock 24931356Seschrock for (i = 0; i < count; i++) 24941356Seschrock zfs_close(fslist[i]); 24951356Seschrock 24961356Seschrock free(fslist); 2497789Sahrens } else if (argc == 0) { 2498789Sahrens struct mnttab entry; 2499789Sahrens 2500789Sahrens if (type == OP_SHARE) { 2501789Sahrens (void) fprintf(stderr, gettext("missing filesystem " 2502789Sahrens "argument\n")); 25032082Seschrock usage(B_FALSE); 2504789Sahrens } 2505789Sahrens 2506789Sahrens /* 2507789Sahrens * When mount is given no arguments, go through /etc/mnttab and 2508789Sahrens * display any active ZFS mounts. We hide any snapshots, since 2509789Sahrens * they are controlled automatically. 2510789Sahrens */ 2511789Sahrens rewind(mnttab_file); 2512789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2513789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 2514789Sahrens strchr(entry.mnt_special, '@') != NULL) 2515789Sahrens continue; 2516789Sahrens 2517789Sahrens (void) printf("%-30s %s\n", entry.mnt_special, 2518789Sahrens entry.mnt_mountp); 2519789Sahrens } 2520789Sahrens 2521789Sahrens } else { 2522789Sahrens zfs_handle_t *zhp; 2523789Sahrens 2524789Sahrens if (argc > 1) { 2525789Sahrens (void) fprintf(stderr, 2526789Sahrens gettext("too many arguments\n")); 25272082Seschrock usage(B_FALSE); 2528789Sahrens } 2529789Sahrens 25302082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], 25312082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2532789Sahrens ret = 1; 2533789Sahrens else { 25342082Seschrock cb.cb_explicit = B_TRUE; 2535789Sahrens ret = share_mount_callback(zhp, &cb); 2536789Sahrens zfs_close(zhp); 2537789Sahrens } 2538789Sahrens } 2539789Sahrens 2540789Sahrens return (ret); 2541789Sahrens } 2542789Sahrens 2543789Sahrens /* 2544789Sahrens * zfs mount -a 2545789Sahrens * zfs mount filesystem 2546789Sahrens * 2547789Sahrens * Mount all filesystems, or mount the given filesystem. 2548789Sahrens */ 2549789Sahrens static int 2550789Sahrens zfs_do_mount(int argc, char **argv) 2551789Sahrens { 2552789Sahrens return (share_or_mount(OP_MOUNT, argc, argv)); 2553789Sahrens } 2554789Sahrens 2555789Sahrens /* 2556789Sahrens * zfs share -a 2557789Sahrens * zfs share filesystem 2558789Sahrens * 2559789Sahrens * Share all filesystems, or share the given filesystem. 2560789Sahrens */ 2561789Sahrens static int 2562789Sahrens zfs_do_share(int argc, char **argv) 2563789Sahrens { 2564789Sahrens return (share_or_mount(OP_SHARE, argc, argv)); 2565789Sahrens } 2566789Sahrens 2567789Sahrens typedef struct unshare_unmount_node { 2568789Sahrens zfs_handle_t *un_zhp; 2569789Sahrens char *un_mountp; 2570789Sahrens uu_avl_node_t un_avlnode; 2571789Sahrens } unshare_unmount_node_t; 2572789Sahrens 2573789Sahrens /* ARGSUSED */ 2574789Sahrens static int 2575789Sahrens unshare_unmount_compare(const void *larg, const void *rarg, void *unused) 2576789Sahrens { 2577789Sahrens const unshare_unmount_node_t *l = larg; 2578789Sahrens const unshare_unmount_node_t *r = rarg; 2579789Sahrens 2580789Sahrens return (strcmp(l->un_mountp, r->un_mountp)); 2581789Sahrens } 2582789Sahrens 2583789Sahrens /* 2584789Sahrens * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 2585789Sahrens * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 2586789Sahrens * and unmount it appropriately. 2587789Sahrens */ 2588789Sahrens static int 25892082Seschrock unshare_unmount_path(int type, char *path, int flags, boolean_t is_manual) 2590789Sahrens { 2591789Sahrens zfs_handle_t *zhp; 2592789Sahrens int ret; 2593789Sahrens struct stat64 statbuf; 2594789Sahrens struct extmnttab entry; 2595789Sahrens const char *cmdname = (type == OP_SHARE) ? "unshare" : "unmount"; 2596789Sahrens char property[ZFS_MAXPROPLEN]; 2597789Sahrens 2598789Sahrens /* 2599789Sahrens * Search for the path in /etc/mnttab. Rather than looking for the 2600789Sahrens * specific path, which can be fooled by non-standard paths (i.e. ".." 2601789Sahrens * or "//"), we stat() the path and search for the corresponding 2602789Sahrens * (major,minor) device pair. 2603789Sahrens */ 2604789Sahrens if (stat64(path, &statbuf) != 0) { 2605789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 2606789Sahrens cmdname, path, strerror(errno)); 2607789Sahrens return (1); 2608789Sahrens } 2609789Sahrens 2610789Sahrens /* 2611789Sahrens * Search for the given (major,minor) pair in the mount table. 2612789Sahrens */ 2613789Sahrens rewind(mnttab_file); 2614789Sahrens while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 2615789Sahrens if (entry.mnt_major == major(statbuf.st_dev) && 2616789Sahrens entry.mnt_minor == minor(statbuf.st_dev)) 2617789Sahrens break; 2618789Sahrens } 2619789Sahrens if (ret != 0) { 2620789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not " 2621789Sahrens "currently mounted\n"), cmdname, path); 2622789Sahrens return (1); 2623789Sahrens } 2624789Sahrens 2625789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 2626789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 2627789Sahrens "filesystem\n"), cmdname, path); 2628789Sahrens return (1); 2629789Sahrens } 2630789Sahrens 26312082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 26322082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2633789Sahrens return (1); 2634789Sahrens 2635789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2636789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 26372082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2638789Sahrens 2639789Sahrens if (type == OP_SHARE) { 2640789Sahrens if (strcmp(property, "off") == 0) { 2641789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2642789Sahrens "'%s': legacy share\n"), path); 2643789Sahrens (void) fprintf(stderr, gettext("use " 2644789Sahrens "unshare(1M) to unshare this filesystem\n")); 2645789Sahrens ret = 1; 2646789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2647789Sahrens (void) fprintf(stderr, gettext("cannot unshare '%s': " 2648789Sahrens "not currently shared\n"), path); 2649789Sahrens ret = 1; 2650789Sahrens } else { 2651789Sahrens ret = zfs_unshareall(zhp); 2652789Sahrens } 2653789Sahrens } else { 26541264Slling if (is_manual) { 26551264Slling ret = zfs_unmount(zhp, NULL, flags); 26561264Slling } else if (strcmp(property, "legacy") == 0) { 26571264Slling (void) fprintf(stderr, gettext("cannot unmount " 26581264Slling "'%s': legacy mountpoint\n"), 26591264Slling zfs_get_name(zhp)); 26601264Slling (void) fprintf(stderr, gettext("use umount(1M) " 26611264Slling "to unmount this filesystem\n")); 26621264Slling ret = 1; 2663789Sahrens } else { 2664789Sahrens ret = zfs_unmountall(zhp, flags); 2665789Sahrens } 2666789Sahrens } 2667789Sahrens 2668789Sahrens zfs_close(zhp); 2669789Sahrens 2670789Sahrens return (ret != 0); 2671789Sahrens } 2672789Sahrens 2673789Sahrens /* 2674789Sahrens * Generic callback for unsharing or unmounting a filesystem. 2675789Sahrens */ 2676789Sahrens static int 2677789Sahrens unshare_unmount(int type, int argc, char **argv) 2678789Sahrens { 2679789Sahrens int do_all = 0; 2680789Sahrens int flags = 0; 2681789Sahrens int ret = 0; 2682789Sahrens int c; 2683789Sahrens zfs_handle_t *zhp; 2684789Sahrens char property[ZFS_MAXPROPLEN]; 2685789Sahrens 2686789Sahrens /* check options */ 2687789Sahrens while ((c = getopt(argc, argv, type == OP_SHARE ? "a" : "af")) != -1) { 2688789Sahrens switch (c) { 2689789Sahrens case 'a': 2690789Sahrens do_all = 1; 2691789Sahrens break; 2692789Sahrens case 'f': 2693789Sahrens flags = MS_FORCE; 2694789Sahrens break; 2695789Sahrens case '?': 2696789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2697789Sahrens optopt); 26982082Seschrock usage(B_FALSE); 2699789Sahrens } 2700789Sahrens } 2701789Sahrens 2702789Sahrens argc -= optind; 2703789Sahrens argv += optind; 2704789Sahrens 2705789Sahrens /* ensure correct number of arguments */ 2706789Sahrens if (do_all) { 2707789Sahrens if (argc != 0) { 2708789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 27092082Seschrock usage(B_FALSE); 2710789Sahrens } 2711789Sahrens } else if (argc != 1) { 2712789Sahrens if (argc == 0) 2713789Sahrens (void) fprintf(stderr, 2714789Sahrens gettext("missing filesystem argument\n")); 2715789Sahrens else 2716789Sahrens (void) fprintf(stderr, 2717789Sahrens gettext("too many arguments\n")); 27182082Seschrock usage(B_FALSE); 2719789Sahrens } 2720789Sahrens 2721789Sahrens if (do_all) { 2722789Sahrens /* 2723789Sahrens * We could make use of zfs_for_each() to walk all datasets in 2724789Sahrens * the system, but this would be very inefficient, especially 2725789Sahrens * since we would have to linearly search /etc/mnttab for each 2726789Sahrens * one. Instead, do one pass through /etc/mnttab looking for 2727789Sahrens * zfs entries and call zfs_unmount() for each one. 2728789Sahrens * 2729789Sahrens * Things get a little tricky if the administrator has created 2730789Sahrens * mountpoints beneath other ZFS filesystems. In this case, we 2731789Sahrens * have to unmount the deepest filesystems first. To accomplish 2732789Sahrens * this, we place all the mountpoints in an AVL tree sorted by 2733789Sahrens * the special type (dataset name), and walk the result in 2734789Sahrens * reverse to make sure to get any snapshots first. 2735789Sahrens */ 2736789Sahrens struct mnttab entry; 2737789Sahrens uu_avl_pool_t *pool; 2738789Sahrens uu_avl_t *tree; 2739789Sahrens unshare_unmount_node_t *node; 2740789Sahrens uu_avl_index_t idx; 2741789Sahrens uu_avl_walk_t *walk; 2742789Sahrens 2743789Sahrens if ((pool = uu_avl_pool_create("unmount_pool", 2744789Sahrens sizeof (unshare_unmount_node_t), 2745789Sahrens offsetof(unshare_unmount_node_t, un_avlnode), 2746789Sahrens unshare_unmount_compare, 2747789Sahrens UU_DEFAULT)) == NULL) { 2748789Sahrens (void) fprintf(stderr, gettext("internal error: " 2749789Sahrens "out of memory\n")); 2750789Sahrens exit(1); 2751789Sahrens } 2752789Sahrens 2753789Sahrens if ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL) { 2754789Sahrens (void) fprintf(stderr, gettext("internal error: " 2755789Sahrens "out of memory\n")); 2756789Sahrens exit(1); 2757789Sahrens } 2758789Sahrens 2759789Sahrens rewind(mnttab_file); 2760789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2761789Sahrens 2762789Sahrens /* ignore non-ZFS entries */ 2763789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 2764789Sahrens continue; 2765789Sahrens 2766789Sahrens /* ignore snapshots */ 2767789Sahrens if (strchr(entry.mnt_special, '@') != NULL) 2768789Sahrens continue; 2769789Sahrens 27702082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 2771789Sahrens ZFS_TYPE_FILESYSTEM)) == NULL) { 2772789Sahrens ret = 1; 2773789Sahrens continue; 2774789Sahrens } 2775789Sahrens 2776789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2777789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 2778789Sahrens property, sizeof (property), NULL, NULL, 27792082Seschrock 0, B_FALSE) == 0); 2780789Sahrens 2781789Sahrens /* Ignore legacy mounts and shares */ 2782789Sahrens if ((type == OP_SHARE && 2783789Sahrens strcmp(property, "off") == 0) || 2784789Sahrens (type == OP_MOUNT && 2785789Sahrens strcmp(property, "legacy") == 0)) { 2786789Sahrens zfs_close(zhp); 2787789Sahrens continue; 2788789Sahrens } 2789789Sahrens 2790789Sahrens node = safe_malloc(sizeof (unshare_unmount_node_t)); 2791789Sahrens node->un_zhp = zhp; 2792789Sahrens 2793789Sahrens if ((node->un_mountp = strdup(entry.mnt_mountp)) == 2794789Sahrens NULL) { 2795789Sahrens (void) fprintf(stderr, gettext("internal error:" 2796789Sahrens " out of memory\n")); 2797789Sahrens exit(1); 2798789Sahrens } 2799789Sahrens 2800789Sahrens uu_avl_node_init(node, &node->un_avlnode, pool); 2801789Sahrens 2802789Sahrens if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 2803789Sahrens uu_avl_insert(tree, node, idx); 2804789Sahrens } else { 2805789Sahrens zfs_close(node->un_zhp); 2806789Sahrens free(node->un_mountp); 2807789Sahrens free(node); 2808789Sahrens } 2809789Sahrens } 2810789Sahrens 2811789Sahrens /* 2812789Sahrens * Walk the AVL tree in reverse, unmounting each filesystem and 2813789Sahrens * removing it from the AVL tree in the process. 2814789Sahrens */ 2815789Sahrens if ((walk = uu_avl_walk_start(tree, 2816789Sahrens UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) { 2817789Sahrens (void) fprintf(stderr, 2818789Sahrens gettext("internal error: out of memory")); 2819789Sahrens exit(1); 2820789Sahrens } 2821789Sahrens 2822789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 2823789Sahrens uu_avl_remove(tree, node); 2824789Sahrens 2825789Sahrens switch (type) { 2826789Sahrens case OP_SHARE: 2827789Sahrens if (zfs_unshare(node->un_zhp, 2828789Sahrens node->un_mountp) != 0) 2829789Sahrens ret = 1; 2830789Sahrens break; 2831789Sahrens 2832789Sahrens case OP_MOUNT: 2833789Sahrens if (zfs_unmount(node->un_zhp, 2834789Sahrens node->un_mountp, flags) != 0) 2835789Sahrens ret = 1; 2836789Sahrens break; 2837789Sahrens } 2838789Sahrens 2839789Sahrens zfs_close(node->un_zhp); 2840789Sahrens free(node->un_mountp); 2841789Sahrens free(node); 2842789Sahrens } 2843789Sahrens 2844789Sahrens uu_avl_walk_end(walk); 2845789Sahrens uu_avl_destroy(tree); 2846789Sahrens uu_avl_pool_destroy(pool); 2847789Sahrens } else { 2848789Sahrens /* 2849789Sahrens * We have an argument, but it may be a full path or a ZFS 2850789Sahrens * filesystem. Pass full paths off to unmount_path() (shared by 2851789Sahrens * manual_unmount), otherwise open the filesystem and pass to 2852789Sahrens * zfs_unmount(). 2853789Sahrens */ 2854789Sahrens if (argv[0][0] == '/') 2855789Sahrens return (unshare_unmount_path(type, argv[0], 28562082Seschrock flags, B_FALSE)); 28572082Seschrock 28582082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], 28592082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2860789Sahrens return (1); 2861789Sahrens 2862789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2863789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 28642082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2865789Sahrens 2866789Sahrens switch (type) { 2867789Sahrens case OP_SHARE: 2868789Sahrens if (strcmp(property, "off") == 0) { 2869789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2870789Sahrens "'%s': legacy share\n"), zfs_get_name(zhp)); 2871789Sahrens (void) fprintf(stderr, gettext("use unshare(1M)" 2872789Sahrens " to unshare this filesystem\n")); 2873789Sahrens ret = 1; 2874789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2875789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2876789Sahrens "'%s': not currently shared\n"), 2877789Sahrens zfs_get_name(zhp)); 2878789Sahrens ret = 1; 2879789Sahrens } else if (zfs_unshareall(zhp) != 0) { 2880789Sahrens ret = 1; 2881789Sahrens } 2882789Sahrens break; 2883789Sahrens 2884789Sahrens case OP_MOUNT: 2885789Sahrens if (strcmp(property, "legacy") == 0) { 2886789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2887789Sahrens "'%s': legacy mountpoint\n"), 2888789Sahrens zfs_get_name(zhp)); 2889789Sahrens (void) fprintf(stderr, gettext("use umount(1M) " 2890789Sahrens "to unmount this filesystem\n")); 2891789Sahrens ret = 1; 2892789Sahrens } else if (!zfs_is_mounted(zhp, NULL)) { 2893789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2894789Sahrens "'%s': not currently mounted\n"), 2895789Sahrens zfs_get_name(zhp)); 2896789Sahrens ret = 1; 2897789Sahrens } else if (zfs_unmountall(zhp, flags) != 0) { 2898789Sahrens ret = 1; 2899789Sahrens } 2900789Sahrens } 2901789Sahrens 2902789Sahrens zfs_close(zhp); 2903789Sahrens } 2904789Sahrens 2905789Sahrens return (ret); 2906789Sahrens } 2907789Sahrens 2908789Sahrens /* 2909789Sahrens * zfs unmount -a 2910789Sahrens * zfs unmount filesystem 2911789Sahrens * 2912789Sahrens * Unmount all filesystems, or a specific ZFS filesystem. 2913789Sahrens */ 2914789Sahrens static int 2915789Sahrens zfs_do_unmount(int argc, char **argv) 2916789Sahrens { 2917789Sahrens return (unshare_unmount(OP_MOUNT, argc, argv)); 2918789Sahrens } 2919789Sahrens 2920789Sahrens /* 2921789Sahrens * zfs unshare -a 2922789Sahrens * zfs unshare filesystem 2923789Sahrens * 2924789Sahrens * Unshare all filesystems, or a specific ZFS filesystem. 2925789Sahrens */ 2926789Sahrens static int 2927789Sahrens zfs_do_unshare(int argc, char **argv) 2928789Sahrens { 2929789Sahrens return (unshare_unmount(OP_SHARE, argc, argv)); 2930789Sahrens } 2931789Sahrens 2932789Sahrens /* 2933789Sahrens * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 2934789Sahrens * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 2935789Sahrens */ 2936789Sahrens static int 2937789Sahrens manual_mount(int argc, char **argv) 2938789Sahrens { 2939789Sahrens zfs_handle_t *zhp; 2940789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2941789Sahrens char mntopts[MNT_LINE_MAX] = { '\0' }; 2942789Sahrens int ret; 2943789Sahrens int c; 2944789Sahrens int flags = 0; 2945789Sahrens char *dataset, *path; 2946789Sahrens 2947789Sahrens /* check options */ 29481544Seschrock while ((c = getopt(argc, argv, ":mo:O")) != -1) { 2949789Sahrens switch (c) { 2950789Sahrens case 'o': 2951789Sahrens (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 2952789Sahrens break; 2953789Sahrens case 'O': 2954789Sahrens flags |= MS_OVERLAY; 2955789Sahrens break; 29561544Seschrock case 'm': 29571544Seschrock flags |= MS_NOMNTTAB; 29581544Seschrock break; 2959789Sahrens case ':': 2960789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2961789Sahrens "'%c' option\n"), optopt); 29622082Seschrock usage(B_FALSE); 2963789Sahrens break; 2964789Sahrens case '?': 2965789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2966789Sahrens optopt); 2967789Sahrens (void) fprintf(stderr, gettext("usage: mount [-o opts] " 2968789Sahrens "<path>\n")); 2969789Sahrens return (2); 2970789Sahrens } 2971789Sahrens } 2972789Sahrens 2973789Sahrens argc -= optind; 2974789Sahrens argv += optind; 2975789Sahrens 2976789Sahrens /* check that we only have two arguments */ 2977789Sahrens if (argc != 2) { 2978789Sahrens if (argc == 0) 2979789Sahrens (void) fprintf(stderr, gettext("missing dataset " 2980789Sahrens "argument\n")); 2981789Sahrens else if (argc == 1) 2982789Sahrens (void) fprintf(stderr, 2983789Sahrens gettext("missing mountpoint argument\n")); 2984789Sahrens else 2985789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2986789Sahrens (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 2987789Sahrens return (2); 2988789Sahrens } 2989789Sahrens 2990789Sahrens dataset = argv[0]; 2991789Sahrens path = argv[1]; 2992789Sahrens 2993789Sahrens /* try to open the dataset */ 29942082Seschrock if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 2995789Sahrens return (1); 2996789Sahrens 2997789Sahrens (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 29982082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE); 2999789Sahrens 3000789Sahrens /* check for legacy mountpoint and complain appropriately */ 3001789Sahrens ret = 0; 3002789Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 3003789Sahrens if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS, 3004789Sahrens NULL, 0, mntopts, sizeof (mntopts)) != 0) { 3005789Sahrens (void) fprintf(stderr, gettext("mount failed: %s\n"), 3006789Sahrens strerror(errno)); 3007789Sahrens ret = 1; 3008789Sahrens } 3009789Sahrens } else { 3010789Sahrens (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 3011789Sahrens "mounted using 'mount -F zfs'\n"), dataset); 3012789Sahrens (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 3013789Sahrens "instead.\n"), path); 3014789Sahrens (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' " 3015789Sahrens "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n")); 3016789Sahrens (void) fprintf(stderr, gettext("See zfs(1M) for more " 3017789Sahrens "information.\n")); 3018789Sahrens ret = 1; 3019789Sahrens } 3020789Sahrens 3021789Sahrens return (ret); 3022789Sahrens } 3023789Sahrens 3024789Sahrens /* 3025789Sahrens * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 3026789Sahrens * unmounts of non-legacy filesystems, as this is the dominant administrative 3027789Sahrens * interface. 3028789Sahrens */ 3029789Sahrens static int 3030789Sahrens manual_unmount(int argc, char **argv) 3031789Sahrens { 3032789Sahrens int flags = 0; 3033789Sahrens int c; 3034789Sahrens 3035789Sahrens /* check options */ 3036789Sahrens while ((c = getopt(argc, argv, "f")) != -1) { 3037789Sahrens switch (c) { 3038789Sahrens case 'f': 3039789Sahrens flags = MS_FORCE; 3040789Sahrens break; 3041789Sahrens case '?': 3042789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3043789Sahrens optopt); 3044789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] " 3045789Sahrens "<path>\n")); 3046789Sahrens return (2); 3047789Sahrens } 3048789Sahrens } 3049789Sahrens 3050789Sahrens argc -= optind; 3051789Sahrens argv += optind; 3052789Sahrens 3053789Sahrens /* check arguments */ 3054789Sahrens if (argc != 1) { 3055789Sahrens if (argc == 0) 3056789Sahrens (void) fprintf(stderr, gettext("missing path " 3057789Sahrens "argument\n")); 3058789Sahrens else 3059789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 3060789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 3061789Sahrens return (2); 3062789Sahrens } 3063789Sahrens 30642082Seschrock return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE)); 3065789Sahrens } 3066789Sahrens 3067789Sahrens static int 3068789Sahrens volcheck(zpool_handle_t *zhp, void *data) 3069789Sahrens { 30702856Snd150628 boolean_t isinit = *((boolean_t *)data); 3071789Sahrens 3072789Sahrens if (isinit) 3073789Sahrens return (zpool_create_zvol_links(zhp)); 3074789Sahrens else 3075789Sahrens return (zpool_remove_zvol_links(zhp)); 3076789Sahrens } 3077789Sahrens 3078789Sahrens /* 3079789Sahrens * Iterate over all pools in the system and either create or destroy /dev/zvol 3080789Sahrens * links, depending on the value of 'isinit'. 3081789Sahrens */ 3082789Sahrens static int 30832082Seschrock do_volcheck(boolean_t isinit) 3084789Sahrens { 30852856Snd150628 return (zpool_iter(g_zfs, volcheck, &isinit) ? 1 : 0); 3086789Sahrens } 3087789Sahrens 3088789Sahrens int 3089789Sahrens main(int argc, char **argv) 3090789Sahrens { 3091789Sahrens int ret; 3092789Sahrens int i; 3093789Sahrens char *progname; 3094789Sahrens char *cmdname; 3095789Sahrens 3096789Sahrens (void) setlocale(LC_ALL, ""); 3097789Sahrens (void) textdomain(TEXT_DOMAIN); 3098789Sahrens 3099789Sahrens opterr = 0; 3100789Sahrens 31012082Seschrock if ((g_zfs = libzfs_init()) == NULL) { 31022082Seschrock (void) fprintf(stderr, gettext("internal error: failed to " 31032082Seschrock "initialize ZFS library\n")); 31042082Seschrock return (1); 31052082Seschrock } 31062082Seschrock 31072082Seschrock libzfs_print_on_error(g_zfs, B_TRUE); 31082082Seschrock 3109789Sahrens if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 3110789Sahrens (void) fprintf(stderr, gettext("internal error: unable to " 3111789Sahrens "open %s\n"), MNTTAB); 3112789Sahrens return (1); 3113789Sahrens } 3114789Sahrens 3115789Sahrens /* 3116789Sahrens * This command also doubles as the /etc/fs mount and unmount program. 3117789Sahrens * Determine if we should take this behavior based on argv[0]. 3118789Sahrens */ 3119789Sahrens progname = basename(argv[0]); 3120789Sahrens if (strcmp(progname, "mount") == 0) { 3121789Sahrens ret = manual_mount(argc, argv); 3122789Sahrens } else if (strcmp(progname, "umount") == 0) { 3123789Sahrens ret = manual_unmount(argc, argv); 3124789Sahrens } else { 3125789Sahrens /* 3126789Sahrens * Make sure the user has specified some command. 3127789Sahrens */ 3128789Sahrens if (argc < 2) { 3129789Sahrens (void) fprintf(stderr, gettext("missing command\n")); 31302082Seschrock usage(B_FALSE); 3131789Sahrens } 3132789Sahrens 3133789Sahrens cmdname = argv[1]; 3134789Sahrens 3135789Sahrens /* 3136789Sahrens * The 'umount' command is an alias for 'unmount' 3137789Sahrens */ 3138789Sahrens if (strcmp(cmdname, "umount") == 0) 3139789Sahrens cmdname = "unmount"; 3140789Sahrens 3141789Sahrens /* 31421749Sahrens * The 'recv' command is an alias for 'receive' 31431749Sahrens */ 31441749Sahrens if (strcmp(cmdname, "recv") == 0) 31451749Sahrens cmdname = "receive"; 31461749Sahrens 31471749Sahrens /* 3148789Sahrens * Special case '-?' 3149789Sahrens */ 3150789Sahrens if (strcmp(cmdname, "-?") == 0) 31512082Seschrock usage(B_TRUE); 3152789Sahrens 3153789Sahrens /* 3154789Sahrens * 'volinit' and 'volfini' do not appear in the usage message, 3155789Sahrens * so we have to special case them here. 3156789Sahrens */ 3157789Sahrens if (strcmp(cmdname, "volinit") == 0) 31582082Seschrock return (do_volcheck(B_TRUE)); 3159789Sahrens else if (strcmp(cmdname, "volfini") == 0) 31602082Seschrock return (do_volcheck(B_FALSE)); 3161789Sahrens 3162789Sahrens /* 3163789Sahrens * Run the appropriate command. 3164789Sahrens */ 3165789Sahrens for (i = 0; i < NCOMMAND; i++) { 3166789Sahrens if (command_table[i].name == NULL) 3167789Sahrens continue; 3168789Sahrens 3169789Sahrens if (strcmp(cmdname, command_table[i].name) == 0) { 3170789Sahrens current_command = &command_table[i]; 3171789Sahrens ret = command_table[i].func(argc - 1, argv + 1); 3172789Sahrens break; 3173789Sahrens } 3174789Sahrens } 3175789Sahrens 3176789Sahrens if (i == NCOMMAND) { 3177789Sahrens (void) fprintf(stderr, gettext("unrecognized " 3178789Sahrens "command '%s'\n"), cmdname); 31792082Seschrock usage(B_FALSE); 3180789Sahrens } 3181789Sahrens } 3182789Sahrens 3183789Sahrens (void) fclose(mnttab_file); 3184789Sahrens 31852082Seschrock libzfs_fini(g_zfs); 31862082Seschrock 3187789Sahrens /* 3188789Sahrens * The 'ZFS_ABORT' environment variable causes us to dump core on exit 3189789Sahrens * for the purposes of running ::findleaks. 3190789Sahrens */ 3191789Sahrens if (getenv("ZFS_ABORT") != NULL) { 3192789Sahrens (void) printf("dumping core by request\n"); 3193789Sahrens abort(); 3194789Sahrens } 3195789Sahrens 3196789Sahrens return (ret); 3197789Sahrens } 3198