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 */ 213126Sahl 22789Sahrens /* 233504Sahl * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24789Sahrens * Use is subject to license terms. 25789Sahrens */ 26789Sahrens 27789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 28789Sahrens 29789Sahrens #include <assert.h> 302676Seschrock #include <ctype.h> 31789Sahrens #include <errno.h> 32789Sahrens #include <libgen.h> 33789Sahrens #include <libintl.h> 34789Sahrens #include <libuutil.h> 35789Sahrens #include <locale.h> 36789Sahrens #include <stddef.h> 37789Sahrens #include <stdio.h> 38789Sahrens #include <stdlib.h> 39789Sahrens #include <strings.h> 40789Sahrens #include <unistd.h> 41789Sahrens #include <fcntl.h> 42789Sahrens #include <zone.h> 43789Sahrens #include <sys/mkdev.h> 44789Sahrens #include <sys/mntent.h> 45789Sahrens #include <sys/mnttab.h> 46789Sahrens #include <sys/mount.h> 47789Sahrens #include <sys/stat.h> 48789Sahrens 49789Sahrens #include <libzfs.h> 50789Sahrens 51789Sahrens #include "zfs_iter.h" 522082Seschrock #include "zfs_util.h" 532082Seschrock 542082Seschrock libzfs_handle_t *g_zfs; 55789Sahrens 56789Sahrens static FILE *mnttab_file; 57789Sahrens 58789Sahrens static int zfs_do_clone(int argc, char **argv); 59789Sahrens static int zfs_do_create(int argc, char **argv); 60789Sahrens static int zfs_do_destroy(int argc, char **argv); 61789Sahrens static int zfs_do_get(int argc, char **argv); 62789Sahrens static int zfs_do_inherit(int argc, char **argv); 63789Sahrens static int zfs_do_list(int argc, char **argv); 64789Sahrens static int zfs_do_mount(int argc, char **argv); 65789Sahrens static int zfs_do_rename(int argc, char **argv); 66789Sahrens static int zfs_do_rollback(int argc, char **argv); 67789Sahrens static int zfs_do_set(int argc, char **argv); 68789Sahrens static int zfs_do_snapshot(int argc, char **argv); 69789Sahrens static int zfs_do_unmount(int argc, char **argv); 70789Sahrens static int zfs_do_share(int argc, char **argv); 71789Sahrens static int zfs_do_unshare(int argc, char **argv); 721749Sahrens static int zfs_do_send(int argc, char **argv); 731749Sahrens static int zfs_do_receive(int argc, char **argv); 742082Seschrock static int zfs_do_promote(int argc, char **argv); 75789Sahrens 76789Sahrens /* 77789Sahrens * These libumem hooks provide a reasonable set of defaults for the allocator's 78789Sahrens * debugging facilities. 79789Sahrens */ 80789Sahrens const char * 813126Sahl _umem_debug_init(void) 82789Sahrens { 83789Sahrens return ("default,verbose"); /* $UMEM_DEBUG setting */ 84789Sahrens } 85789Sahrens 86789Sahrens const char * 87789Sahrens _umem_logging_init(void) 88789Sahrens { 89789Sahrens return ("fail,contents"); /* $UMEM_LOGGING setting */ 90789Sahrens } 91789Sahrens 921387Seschrock typedef enum { 931387Seschrock HELP_CLONE, 941387Seschrock HELP_CREATE, 951387Seschrock HELP_DESTROY, 961387Seschrock HELP_GET, 971387Seschrock HELP_INHERIT, 981387Seschrock HELP_LIST, 991387Seschrock HELP_MOUNT, 1002082Seschrock HELP_PROMOTE, 1011749Sahrens HELP_RECEIVE, 1021387Seschrock HELP_RENAME, 1031387Seschrock HELP_ROLLBACK, 1041749Sahrens HELP_SEND, 1051387Seschrock HELP_SET, 1061387Seschrock HELP_SHARE, 1071387Seschrock HELP_SNAPSHOT, 1081387Seschrock HELP_UNMOUNT, 1091387Seschrock HELP_UNSHARE 1101387Seschrock } zfs_help_t; 1111387Seschrock 112789Sahrens typedef struct zfs_command { 113789Sahrens const char *name; 114789Sahrens int (*func)(int argc, char **argv); 1151387Seschrock zfs_help_t usage; 116789Sahrens } zfs_command_t; 117789Sahrens 118789Sahrens /* 119789Sahrens * Master command table. Each ZFS command has a name, associated function, and 1201544Seschrock * usage message. The usage messages need to be internationalized, so we have 1211544Seschrock * to have a function to return the usage message based on a command index. 1221387Seschrock * 1231387Seschrock * These commands are organized according to how they are displayed in the usage 1241387Seschrock * message. An empty command (one with a NULL name) indicates an empty line in 1251387Seschrock * the generic usage message. 126789Sahrens */ 127789Sahrens static zfs_command_t command_table[] = { 1281387Seschrock { "create", zfs_do_create, HELP_CREATE }, 1291387Seschrock { "destroy", zfs_do_destroy, HELP_DESTROY }, 130789Sahrens { NULL }, 1311387Seschrock { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT }, 1321387Seschrock { "rollback", zfs_do_rollback, HELP_ROLLBACK }, 1331387Seschrock { "clone", zfs_do_clone, HELP_CLONE }, 1342082Seschrock { "promote", zfs_do_promote, HELP_PROMOTE }, 1351387Seschrock { "rename", zfs_do_rename, HELP_RENAME }, 136789Sahrens { NULL }, 1371387Seschrock { "list", zfs_do_list, HELP_LIST }, 138789Sahrens { NULL }, 1391387Seschrock { "set", zfs_do_set, HELP_SET }, 1401387Seschrock { "get", zfs_do_get, HELP_GET }, 1411387Seschrock { "inherit", zfs_do_inherit, HELP_INHERIT }, 142789Sahrens { NULL }, 1431387Seschrock { "mount", zfs_do_mount, HELP_MOUNT }, 144789Sahrens { NULL }, 1451387Seschrock { "unmount", zfs_do_unmount, HELP_UNMOUNT }, 146789Sahrens { NULL }, 1471387Seschrock { "share", zfs_do_share, HELP_SHARE }, 148789Sahrens { NULL }, 1491387Seschrock { "unshare", zfs_do_unshare, HELP_UNSHARE }, 150789Sahrens { NULL }, 1511749Sahrens { "send", zfs_do_send, HELP_SEND }, 1521749Sahrens { "receive", zfs_do_receive, HELP_RECEIVE }, 153789Sahrens }; 154789Sahrens 155789Sahrens #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) 156789Sahrens 157789Sahrens zfs_command_t *current_command; 158789Sahrens 1591387Seschrock static const char * 1601387Seschrock get_usage(zfs_help_t idx) 1611387Seschrock { 1621387Seschrock switch (idx) { 1631387Seschrock case HELP_CLONE: 1641387Seschrock return (gettext("\tclone <snapshot> <filesystem|volume>\n")); 1651387Seschrock case HELP_CREATE: 1662676Seschrock return (gettext("\tcreate [[-o property=value] ... ] " 1672676Seschrock "<filesystem>\n" 1682676Seschrock "\tcreate [-s] [-b blocksize] [[-o property=value] ...]\n" 1692676Seschrock "\t -V <size> <volume>\n")); 1701387Seschrock case HELP_DESTROY: 1711387Seschrock return (gettext("\tdestroy [-rRf] " 1721387Seschrock "<filesystem|volume|snapshot>\n")); 1731387Seschrock case HELP_GET: 1741387Seschrock return (gettext("\tget [-rHp] [-o field[,field]...] " 1751387Seschrock "[-s source[,source]...]\n" 1761387Seschrock "\t <all | property[,property]...> " 1772676Seschrock "[filesystem|volume|snapshot] ...\n")); 1781387Seschrock case HELP_INHERIT: 1791387Seschrock return (gettext("\tinherit [-r] <property> " 1801387Seschrock "<filesystem|volume> ...\n")); 1811387Seschrock case HELP_LIST: 1821387Seschrock return (gettext("\tlist [-rH] [-o property[,property]...] " 1831387Seschrock "[-t type[,type]...]\n" 1842379Ssjelinek "\t [-s property [-s property]...]" 1852379Ssjelinek " [-S property [-S property]...]\n" 1861387Seschrock "\t [filesystem|volume|snapshot] ...\n")); 1871387Seschrock case HELP_MOUNT: 1881387Seschrock return (gettext("\tmount\n" 1891387Seschrock "\tmount [-o opts] [-O] -a\n" 1901387Seschrock "\tmount [-o opts] [-O] <filesystem>\n")); 1912082Seschrock case HELP_PROMOTE: 1922082Seschrock return (gettext("\tpromote <clone filesystem>\n")); 1931749Sahrens case HELP_RECEIVE: 1942665Snd150628 return (gettext("\treceive [-vnF] <filesystem|volume|" 1952665Snd150628 "snapshot>\n" 1962665Snd150628 "\treceive [-vnF] -d <filesystem>\n")); 1971387Seschrock case HELP_RENAME: 1981387Seschrock return (gettext("\trename <filesystem|volume|snapshot> " 1991387Seschrock "<filesystem|volume|snapshot>\n")); 2001387Seschrock case HELP_ROLLBACK: 2011387Seschrock return (gettext("\trollback [-rRf] <snapshot>\n")); 2021749Sahrens case HELP_SEND: 2031749Sahrens return (gettext("\tsend [-i <snapshot>] <snapshot>\n")); 2041387Seschrock case HELP_SET: 2051387Seschrock return (gettext("\tset <property=value> " 2061387Seschrock "<filesystem|volume> ...\n")); 2071387Seschrock case HELP_SHARE: 2081387Seschrock return (gettext("\tshare -a\n" 2091387Seschrock "\tshare <filesystem>\n")); 2101387Seschrock case HELP_SNAPSHOT: 2112199Sahrens return (gettext("\tsnapshot [-r] " 2122199Sahrens "<filesystem@name|volume@name>\n")); 2131387Seschrock case HELP_UNMOUNT: 2141387Seschrock return (gettext("\tunmount [-f] -a\n" 2151387Seschrock "\tunmount [-f] <filesystem|mountpoint>\n")); 2161387Seschrock case HELP_UNSHARE: 2171387Seschrock return (gettext("\tunshare [-f] -a\n" 2181387Seschrock "\tunshare [-f] <filesystem|mountpoint>\n")); 2191387Seschrock } 2201387Seschrock 2211387Seschrock abort(); 2221387Seschrock /* NOTREACHED */ 2231387Seschrock } 2241387Seschrock 225789Sahrens /* 226789Sahrens * Utility function to guarantee malloc() success. 227789Sahrens */ 228789Sahrens void * 229789Sahrens safe_malloc(size_t size) 230789Sahrens { 231789Sahrens void *data; 232789Sahrens 233789Sahrens if ((data = calloc(1, size)) == NULL) { 234789Sahrens (void) fprintf(stderr, "internal error: out of memory\n"); 235789Sahrens exit(1); 236789Sahrens } 237789Sahrens 238789Sahrens return (data); 239789Sahrens } 240789Sahrens 241789Sahrens /* 242789Sahrens * Display usage message. If we're inside a command, display only the usage for 243789Sahrens * that command. Otherwise, iterate over the entire command table and display 244789Sahrens * a complete usage message. 245789Sahrens */ 246789Sahrens static void 2472082Seschrock usage(boolean_t requested) 248789Sahrens { 249789Sahrens int i; 2502082Seschrock boolean_t show_properties = B_FALSE; 251789Sahrens FILE *fp = requested ? stdout : stderr; 252789Sahrens 253789Sahrens if (current_command == NULL) { 254789Sahrens 255789Sahrens (void) fprintf(fp, gettext("usage: zfs command args ...\n")); 256789Sahrens (void) fprintf(fp, 257789Sahrens gettext("where 'command' is one of the following:\n\n")); 258789Sahrens 259789Sahrens for (i = 0; i < NCOMMAND; i++) { 260789Sahrens if (command_table[i].name == NULL) 261789Sahrens (void) fprintf(fp, "\n"); 262789Sahrens else 263789Sahrens (void) fprintf(fp, "%s", 2641387Seschrock get_usage(command_table[i].usage)); 265789Sahrens } 266789Sahrens 267789Sahrens (void) fprintf(fp, gettext("\nEach dataset is of the form: " 268789Sahrens "pool/[dataset/]*dataset[@name]\n")); 269789Sahrens } else { 270789Sahrens (void) fprintf(fp, gettext("usage:\n")); 2711387Seschrock (void) fprintf(fp, "%s", get_usage(current_command->usage)); 272789Sahrens } 273789Sahrens 2742190Sdarrenm if (current_command != NULL && 2752190Sdarrenm (strcmp(current_command->name, "set") == 0 || 276789Sahrens strcmp(current_command->name, "get") == 0 || 277789Sahrens strcmp(current_command->name, "inherit") == 0 || 2782190Sdarrenm strcmp(current_command->name, "list") == 0)) 2792082Seschrock show_properties = B_TRUE; 280789Sahrens 281789Sahrens if (show_properties) { 282789Sahrens 283789Sahrens (void) fprintf(fp, 284789Sahrens gettext("\nThe following properties are supported:\n")); 285789Sahrens 286789Sahrens (void) fprintf(fp, "\n\t%-13s %s %s %s\n\n", 287789Sahrens "PROPERTY", "EDIT", "INHERIT", "VALUES"); 288789Sahrens 289789Sahrens for (i = 0; i < ZFS_NPROP_VISIBLE; i++) { 290789Sahrens (void) fprintf(fp, "\t%-13s ", zfs_prop_to_name(i)); 291789Sahrens 292789Sahrens if (zfs_prop_readonly(i)) 293789Sahrens (void) fprintf(fp, " NO "); 294789Sahrens else 295789Sahrens (void) fprintf(fp, " YES "); 296789Sahrens 297789Sahrens if (zfs_prop_inheritable(i)) 298789Sahrens (void) fprintf(fp, " YES "); 299789Sahrens else 300789Sahrens (void) fprintf(fp, " NO "); 301789Sahrens 302789Sahrens if (zfs_prop_values(i) == NULL) 303789Sahrens (void) fprintf(fp, "-\n"); 304789Sahrens else 305789Sahrens (void) fprintf(fp, "%s\n", zfs_prop_values(i)); 306789Sahrens } 307789Sahrens (void) fprintf(fp, gettext("\nSizes are specified in bytes " 308789Sahrens "with standard units such as K, M, G, etc.\n")); 3092676Seschrock (void) fprintf(fp, gettext("\n\nUser-defined properties can " 3102676Seschrock "be specified by using a name containing a colon (:).\n")); 3112190Sdarrenm } else { 3122190Sdarrenm /* 3132190Sdarrenm * TRANSLATION NOTE: 3142190Sdarrenm * "zfs set|get" must not be localised this is the 3152190Sdarrenm * command name and arguments. 3162190Sdarrenm */ 3172190Sdarrenm (void) fprintf(fp, 3182190Sdarrenm gettext("\nFor the property list, run: zfs set|get\n")); 319789Sahrens } 320789Sahrens 3212676Seschrock /* 3222676Seschrock * See comments at end of main(). 3232676Seschrock */ 3242676Seschrock if (getenv("ZFS_ABORT") != NULL) { 3252676Seschrock (void) printf("dumping core by request\n"); 3262676Seschrock abort(); 3272676Seschrock } 3282676Seschrock 329789Sahrens exit(requested ? 0 : 2); 330789Sahrens } 331789Sahrens 332789Sahrens /* 333789Sahrens * zfs clone <fs, snap, vol> fs 334789Sahrens * 335789Sahrens * Given an existing dataset, create a writable copy whose initial contents 336789Sahrens * are the same as the source. The newly created dataset maintains a 337789Sahrens * dependency on the original; the original cannot be destroyed so long as 338789Sahrens * the clone exists. 339789Sahrens */ 340789Sahrens static int 341789Sahrens zfs_do_clone(int argc, char **argv) 342789Sahrens { 343789Sahrens zfs_handle_t *zhp; 344789Sahrens int ret; 345789Sahrens 346789Sahrens /* check options */ 347789Sahrens if (argc > 1 && argv[1][0] == '-') { 348789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 349789Sahrens argv[1][1]); 3502082Seschrock usage(B_FALSE); 351789Sahrens } 352789Sahrens 353789Sahrens /* check number of arguments */ 354789Sahrens if (argc < 2) { 355789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 356789Sahrens "argument\n")); 3572082Seschrock usage(B_FALSE); 358789Sahrens } 359789Sahrens if (argc < 3) { 360789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 361789Sahrens "argument\n")); 3622082Seschrock usage(B_FALSE); 363789Sahrens } 364789Sahrens if (argc > 3) { 365789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 3662082Seschrock usage(B_FALSE); 367789Sahrens } 368789Sahrens 369789Sahrens /* open the source dataset */ 3702082Seschrock if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_SNAPSHOT)) == NULL) 371789Sahrens return (1); 372789Sahrens 373789Sahrens /* pass to libzfs */ 3742676Seschrock ret = zfs_clone(zhp, argv[2], NULL); 375789Sahrens 376789Sahrens /* create the mountpoint if necessary */ 377789Sahrens if (ret == 0) { 3782082Seschrock zfs_handle_t *clone = zfs_open(g_zfs, argv[2], ZFS_TYPE_ANY); 379789Sahrens if (clone != NULL) { 380789Sahrens if ((ret = zfs_mount(clone, NULL, 0)) == 0) 381789Sahrens ret = zfs_share(clone); 382789Sahrens zfs_close(clone); 383789Sahrens } 3842926Sek110237 zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE); 385789Sahrens } 386789Sahrens 387789Sahrens zfs_close(zhp); 388789Sahrens 389789Sahrens return (ret == 0 ? 0 : 1); 390789Sahrens } 391789Sahrens 392789Sahrens /* 3932676Seschrock * zfs create [-o prop=value] ... fs 3942676Seschrock * zfs create [-s] [-b blocksize] [-o prop=value] ... -V vol size 395789Sahrens * 396789Sahrens * Create a new dataset. This command can be used to create filesystems 397789Sahrens * and volumes. Snapshot creation is handled by 'zfs snapshot'. 398789Sahrens * For volumes, the user must specify a size to be used. 399789Sahrens * 400789Sahrens * The '-s' flag applies only to volumes, and indicates that we should not try 401789Sahrens * to set the reservation for this volume. By default we set a reservation 402789Sahrens * equal to the size for any volume. 403789Sahrens */ 404789Sahrens static int 405789Sahrens zfs_do_create(int argc, char **argv) 406789Sahrens { 407789Sahrens zfs_type_t type = ZFS_TYPE_FILESYSTEM; 4082676Seschrock zfs_handle_t *zhp = NULL; 4092676Seschrock uint64_t volsize; 410789Sahrens int c; 4112082Seschrock boolean_t noreserve = B_FALSE; 4122676Seschrock int ret = 1; 4132676Seschrock nvlist_t *props = NULL; 4142676Seschrock uint64_t intval; 4152676Seschrock char *propname; 4162926Sek110237 char *propval = NULL; 4172926Sek110237 char *strval; 4182676Seschrock 4192676Seschrock if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) { 4202676Seschrock (void) fprintf(stderr, gettext("internal error: " 4212676Seschrock "out of memory\n")); 4222676Seschrock return (1); 4232676Seschrock } 424789Sahrens 425789Sahrens /* check options */ 4262676Seschrock while ((c = getopt(argc, argv, ":V:b:so:")) != -1) { 427789Sahrens switch (c) { 428789Sahrens case 'V': 429789Sahrens type = ZFS_TYPE_VOLUME; 4302676Seschrock if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 4312676Seschrock (void) fprintf(stderr, gettext("bad volume " 4322676Seschrock "size '%s': %s\n"), optarg, 4332676Seschrock libzfs_error_description(g_zfs)); 4342676Seschrock goto error; 4352676Seschrock } 4362676Seschrock 4372676Seschrock if (nvlist_add_uint64(props, 4382676Seschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), 4392676Seschrock intval) != 0) { 4402676Seschrock (void) fprintf(stderr, gettext("internal " 4412676Seschrock "error: out of memory\n")); 4422676Seschrock goto error; 4432676Seschrock } 4442676Seschrock volsize = intval; 445789Sahrens break; 446789Sahrens case 'b': 4472676Seschrock if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 4482676Seschrock (void) fprintf(stderr, gettext("bad volume " 4492676Seschrock "block size '%s': %s\n"), optarg, 4502676Seschrock libzfs_error_description(g_zfs)); 4512676Seschrock goto error; 4522676Seschrock } 4532676Seschrock 4542676Seschrock if (nvlist_add_uint64(props, 4552676Seschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 4562676Seschrock intval) != 0) { 4572676Seschrock (void) fprintf(stderr, gettext("internal " 4582676Seschrock "error: out of memory\n")); 4592676Seschrock goto error; 4602676Seschrock } 4612676Seschrock break; 4622676Seschrock case 'o': 4632676Seschrock propname = optarg; 4642676Seschrock if ((propval = strchr(propname, '=')) == NULL) { 4652676Seschrock (void) fprintf(stderr, gettext("missing " 4662676Seschrock "'=' for -o option\n")); 4672676Seschrock goto error; 4682676Seschrock } 4692676Seschrock *propval = '\0'; 4702676Seschrock propval++; 4712676Seschrock if (nvlist_lookup_string(props, propname, 4722676Seschrock &strval) == 0) { 4732676Seschrock (void) fprintf(stderr, gettext("property '%s' " 4742676Seschrock "specified multiple times\n"), propname); 4752676Seschrock goto error; 4762676Seschrock } 4772676Seschrock if (nvlist_add_string(props, propname, propval) != 0) { 4782676Seschrock (void) fprintf(stderr, gettext("internal " 4792676Seschrock "error: out of memory\n")); 4802676Seschrock goto error; 4812676Seschrock } 482789Sahrens break; 483789Sahrens case 's': 4842082Seschrock noreserve = B_TRUE; 485789Sahrens break; 486789Sahrens case ':': 487789Sahrens (void) fprintf(stderr, gettext("missing size " 488789Sahrens "argument\n")); 4892676Seschrock goto badusage; 490789Sahrens break; 491789Sahrens case '?': 492789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 493789Sahrens optopt); 4942676Seschrock goto badusage; 495789Sahrens } 496789Sahrens } 497789Sahrens 498789Sahrens if (noreserve && type != ZFS_TYPE_VOLUME) { 499789Sahrens (void) fprintf(stderr, gettext("'-s' can only be used when " 500789Sahrens "creating a volume\n")); 5012676Seschrock goto badusage; 502789Sahrens } 503789Sahrens 504789Sahrens argc -= optind; 505789Sahrens argv += optind; 506789Sahrens 507789Sahrens /* check number of arguments */ 508789Sahrens if (argc == 0) { 509789Sahrens (void) fprintf(stderr, gettext("missing %s argument\n"), 510789Sahrens zfs_type_to_name(type)); 5112676Seschrock goto badusage; 512789Sahrens } 513789Sahrens if (argc > 1) { 514789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 5152676Seschrock goto badusage; 5162676Seschrock } 5172676Seschrock 5182676Seschrock if (type == ZFS_TYPE_VOLUME && !noreserve && 5192676Seschrock nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_RESERVATION), 5202676Seschrock &strval) != 0) { 5212676Seschrock if (nvlist_add_uint64(props, 5222676Seschrock zfs_prop_to_name(ZFS_PROP_RESERVATION), 5232676Seschrock volsize) != 0) { 5242676Seschrock (void) fprintf(stderr, gettext("internal " 5252676Seschrock "error: out of memory\n")); 5262676Seschrock nvlist_free(props); 5272676Seschrock return (1); 5282676Seschrock } 529789Sahrens } 530789Sahrens 531789Sahrens /* pass to libzfs */ 5322676Seschrock if (zfs_create(g_zfs, argv[0], type, props) != 0) 5332676Seschrock goto error; 534789Sahrens 5352926Sek110237 if (propval != NULL) 5362926Sek110237 *(propval - 1) = '='; 5372926Sek110237 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0], 5382926Sek110237 B_FALSE, B_FALSE); 5392926Sek110237 5402082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 5412676Seschrock goto error; 542789Sahrens 543789Sahrens /* 544789Sahrens * Mount and/or share the new filesystem as appropriate. We provide a 545789Sahrens * verbose error message to let the user know that their filesystem was 546789Sahrens * in fact created, even if we failed to mount or share it. 547789Sahrens */ 548789Sahrens if (zfs_mount(zhp, NULL, 0) != 0) { 549789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 550789Sahrens "created, but not mounted\n")); 551789Sahrens ret = 1; 552789Sahrens } else if (zfs_share(zhp) != 0) { 553789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 554789Sahrens "created, but not shared\n")); 555789Sahrens ret = 1; 556789Sahrens } else { 557789Sahrens ret = 0; 558789Sahrens } 559789Sahrens 5602676Seschrock error: 5612676Seschrock if (zhp) 5622676Seschrock zfs_close(zhp); 5632676Seschrock nvlist_free(props); 564789Sahrens return (ret); 5652676Seschrock badusage: 5662676Seschrock nvlist_free(props); 5672676Seschrock usage(B_FALSE); 5682676Seschrock return (2); 569789Sahrens } 570789Sahrens 571789Sahrens /* 572789Sahrens * zfs destroy [-rf] <fs, snap, vol> 573789Sahrens * 574789Sahrens * -r Recursively destroy all children 575789Sahrens * -R Recursively destroy all dependents, including clones 576789Sahrens * -f Force unmounting of any dependents 577789Sahrens * 578789Sahrens * Destroys the given dataset. By default, it will unmount any filesystems, 579789Sahrens * and refuse to destroy a dataset that has any dependents. A dependent can 580789Sahrens * either be a child, or a clone of a child. 581789Sahrens */ 582789Sahrens typedef struct destroy_cbdata { 5832082Seschrock boolean_t cb_first; 584789Sahrens int cb_force; 585789Sahrens int cb_recurse; 586789Sahrens int cb_error; 587789Sahrens int cb_needforce; 588789Sahrens int cb_doclones; 5893265Sahrens boolean_t cb_closezhp; 590789Sahrens zfs_handle_t *cb_target; 5912199Sahrens char *cb_snapname; 592789Sahrens } destroy_cbdata_t; 593789Sahrens 594789Sahrens /* 595789Sahrens * Check for any dependents based on the '-r' or '-R' flags. 596789Sahrens */ 597789Sahrens static int 598789Sahrens destroy_check_dependent(zfs_handle_t *zhp, void *data) 599789Sahrens { 600789Sahrens destroy_cbdata_t *cbp = data; 601789Sahrens const char *tname = zfs_get_name(cbp->cb_target); 602789Sahrens const char *name = zfs_get_name(zhp); 603789Sahrens 604789Sahrens if (strncmp(tname, name, strlen(tname)) == 0 && 605789Sahrens (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { 606789Sahrens /* 607789Sahrens * This is a direct descendant, not a clone somewhere else in 608789Sahrens * the hierarchy. 609789Sahrens */ 610789Sahrens if (cbp->cb_recurse) 611789Sahrens goto out; 612789Sahrens 613789Sahrens if (cbp->cb_first) { 614789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 615789Sahrens "%s has children\n"), 616789Sahrens zfs_get_name(cbp->cb_target), 617789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 618789Sahrens (void) fprintf(stderr, gettext("use '-r' to destroy " 619789Sahrens "the following datasets:\n")); 6202082Seschrock cbp->cb_first = B_FALSE; 621789Sahrens cbp->cb_error = 1; 622789Sahrens } 623789Sahrens 624789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 625789Sahrens } else { 626789Sahrens /* 627789Sahrens * This is a clone. We only want to report this if the '-r' 628789Sahrens * wasn't specified, or the target is a snapshot. 629789Sahrens */ 630789Sahrens if (!cbp->cb_recurse && 631789Sahrens zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) 632789Sahrens goto out; 633789Sahrens 634789Sahrens if (cbp->cb_first) { 635789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 636789Sahrens "%s has dependent clones\n"), 637789Sahrens zfs_get_name(cbp->cb_target), 638789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 639789Sahrens (void) fprintf(stderr, gettext("use '-R' to destroy " 640789Sahrens "the following datasets:\n")); 6412082Seschrock cbp->cb_first = B_FALSE; 642789Sahrens cbp->cb_error = 1; 643789Sahrens } 644789Sahrens 645789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 646789Sahrens } 647789Sahrens 648789Sahrens out: 649789Sahrens zfs_close(zhp); 650789Sahrens return (0); 651789Sahrens } 652789Sahrens 653789Sahrens static int 654789Sahrens destroy_callback(zfs_handle_t *zhp, void *data) 655789Sahrens { 656789Sahrens destroy_cbdata_t *cbp = data; 657789Sahrens 658789Sahrens /* 659789Sahrens * Ignore pools (which we've already flagged as an error before getting 660789Sahrens * here. 661789Sahrens */ 662789Sahrens if (strchr(zfs_get_name(zhp), '/') == NULL && 663789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 664789Sahrens zfs_close(zhp); 665789Sahrens return (0); 666789Sahrens } 667789Sahrens 668789Sahrens /* 669789Sahrens * Bail out on the first error. 670789Sahrens */ 671789Sahrens if (zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0 || 672789Sahrens zfs_destroy(zhp) != 0) { 673789Sahrens zfs_close(zhp); 674789Sahrens return (-1); 675789Sahrens } 676789Sahrens 677789Sahrens zfs_close(zhp); 678789Sahrens return (0); 679789Sahrens } 680789Sahrens 6812199Sahrens static int 6822199Sahrens destroy_snap_clones(zfs_handle_t *zhp, void *arg) 6832199Sahrens { 6842199Sahrens destroy_cbdata_t *cbp = arg; 6852199Sahrens char thissnap[MAXPATHLEN]; 6862199Sahrens zfs_handle_t *szhp; 6873265Sahrens boolean_t closezhp = cbp->cb_closezhp; 6883265Sahrens int rv; 6892199Sahrens 6902199Sahrens (void) snprintf(thissnap, sizeof (thissnap), 6912199Sahrens "%s@%s", zfs_get_name(zhp), cbp->cb_snapname); 6922199Sahrens 6932199Sahrens libzfs_print_on_error(g_zfs, B_FALSE); 6942199Sahrens szhp = zfs_open(g_zfs, thissnap, ZFS_TYPE_SNAPSHOT); 6952199Sahrens libzfs_print_on_error(g_zfs, B_TRUE); 6962199Sahrens if (szhp) { 6972199Sahrens /* 6982199Sahrens * Destroy any clones of this snapshot 6992199Sahrens */ 7002474Seschrock if (zfs_iter_dependents(szhp, B_FALSE, destroy_callback, 7012474Seschrock cbp) != 0) { 7022474Seschrock zfs_close(szhp); 7033265Sahrens if (closezhp) 7043265Sahrens zfs_close(zhp); 7052474Seschrock return (-1); 7062474Seschrock } 7072199Sahrens zfs_close(szhp); 7082199Sahrens } 7092199Sahrens 7103265Sahrens cbp->cb_closezhp = B_TRUE; 7113265Sahrens rv = zfs_iter_filesystems(zhp, destroy_snap_clones, arg); 7123265Sahrens if (closezhp) 7133265Sahrens zfs_close(zhp); 7143265Sahrens return (rv); 7152199Sahrens } 716789Sahrens 717789Sahrens static int 718789Sahrens zfs_do_destroy(int argc, char **argv) 719789Sahrens { 720789Sahrens destroy_cbdata_t cb = { 0 }; 721789Sahrens int c; 722789Sahrens zfs_handle_t *zhp; 7232199Sahrens char *cp; 724789Sahrens 725789Sahrens /* check options */ 726789Sahrens while ((c = getopt(argc, argv, "frR")) != -1) { 727789Sahrens switch (c) { 728789Sahrens case 'f': 729789Sahrens cb.cb_force = 1; 730789Sahrens break; 731789Sahrens case 'r': 732789Sahrens cb.cb_recurse = 1; 733789Sahrens break; 734789Sahrens case 'R': 735789Sahrens cb.cb_recurse = 1; 736789Sahrens cb.cb_doclones = 1; 737789Sahrens break; 738789Sahrens case '?': 739789Sahrens default: 740789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 741789Sahrens optopt); 7422082Seschrock usage(B_FALSE); 743789Sahrens } 744789Sahrens } 745789Sahrens 746789Sahrens argc -= optind; 747789Sahrens argv += optind; 748789Sahrens 749789Sahrens /* check number of arguments */ 750789Sahrens if (argc == 0) { 751789Sahrens (void) fprintf(stderr, gettext("missing path argument\n")); 7522082Seschrock usage(B_FALSE); 753789Sahrens } 754789Sahrens if (argc > 1) { 755789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 7562082Seschrock usage(B_FALSE); 757789Sahrens } 758789Sahrens 7592199Sahrens /* 7602199Sahrens * If we are doing recursive destroy of a snapshot, then the 7612199Sahrens * named snapshot may not exist. Go straight to libzfs. 7622199Sahrens */ 7632199Sahrens if (cb.cb_recurse && (cp = strchr(argv[0], '@'))) { 7642199Sahrens int ret; 7652199Sahrens 7662199Sahrens *cp = '\0'; 7672199Sahrens if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 7682199Sahrens return (1); 7692199Sahrens *cp = '@'; 7702199Sahrens cp++; 7712199Sahrens 7722199Sahrens if (cb.cb_doclones) { 7732199Sahrens cb.cb_snapname = cp; 7742474Seschrock if (destroy_snap_clones(zhp, &cb) != 0) { 7752474Seschrock zfs_close(zhp); 7762474Seschrock return (1); 7772474Seschrock } 7782199Sahrens } 7792199Sahrens 7802199Sahrens ret = zfs_destroy_snaps(zhp, cp); 7812199Sahrens zfs_close(zhp); 7822199Sahrens if (ret) { 7832199Sahrens (void) fprintf(stderr, 7842199Sahrens gettext("no snapshots destroyed\n")); 7852199Sahrens } 7862199Sahrens return (ret != 0); 7872199Sahrens } 7882199Sahrens 7892199Sahrens 790789Sahrens /* Open the given dataset */ 7912082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 792789Sahrens return (1); 793789Sahrens 794789Sahrens cb.cb_target = zhp; 795789Sahrens 796789Sahrens /* 797789Sahrens * Perform an explicit check for pools before going any further. 798789Sahrens */ 799789Sahrens if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 800789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 801789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 802789Sahrens "operation does not apply to pools\n"), 803789Sahrens zfs_get_name(zhp)); 804789Sahrens (void) fprintf(stderr, gettext("use 'zfs destroy -r " 805789Sahrens "%s' to destroy all datasets in the pool\n"), 806789Sahrens zfs_get_name(zhp)); 807789Sahrens (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 808789Sahrens "to destroy the pool itself\n"), zfs_get_name(zhp)); 809789Sahrens zfs_close(zhp); 810789Sahrens return (1); 811789Sahrens } 812789Sahrens 813789Sahrens /* 814789Sahrens * Check for any dependents and/or clones. 815789Sahrens */ 8162082Seschrock cb.cb_first = B_TRUE; 8172474Seschrock if (!cb.cb_doclones && 8182474Seschrock zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent, 8192474Seschrock &cb) != 0) { 8202474Seschrock zfs_close(zhp); 8212474Seschrock return (1); 8222474Seschrock } 8232474Seschrock 8242474Seschrock 8252474Seschrock if (cb.cb_error || 8262474Seschrock zfs_iter_dependents(zhp, B_FALSE, destroy_callback, &cb) != 0) { 827789Sahrens zfs_close(zhp); 828789Sahrens return (1); 829789Sahrens } 830789Sahrens 831789Sahrens /* 8322474Seschrock * Do the real thing. The callback will close the handle regardless of 8332474Seschrock * whether it succeeds or not. 834789Sahrens */ 8352474Seschrock if (destroy_callback(zhp, &cb) != 0) 8362474Seschrock return (1); 8372474Seschrock 8382926Sek110237 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0], 8392926Sek110237 B_FALSE, B_FALSE); 8402926Sek110237 8412474Seschrock return (0); 842789Sahrens } 843789Sahrens 844789Sahrens /* 845866Seschrock * zfs get [-rHp] [-o field[,field]...] [-s source[,source]...] 846866Seschrock * < all | property[,property]... > < fs | snap | vol > ... 847789Sahrens * 848789Sahrens * -r recurse over any child datasets 849789Sahrens * -H scripted mode. Headers are stripped, and fields are separated 850789Sahrens * by tabs instead of spaces. 851789Sahrens * -o Set of fields to display. One of "name,property,value,source". 852789Sahrens * Default is all four. 853789Sahrens * -s Set of sources to allow. One of 854789Sahrens * "local,default,inherited,temporary,none". Default is all 855789Sahrens * five. 856789Sahrens * -p Display values in parsable (literal) format. 857789Sahrens * 858789Sahrens * Prints properties for the given datasets. The user can control which 859789Sahrens * columns to display as well as which property types to allow. 860789Sahrens */ 861789Sahrens typedef struct get_cbdata { 862789Sahrens int cb_sources; 863789Sahrens int cb_columns[4]; 8642676Seschrock int cb_colwidths[5]; 8652082Seschrock boolean_t cb_scripted; 8662082Seschrock boolean_t cb_literal; 8672676Seschrock boolean_t cb_first; 8682676Seschrock zfs_proplist_t *cb_proplist; 869789Sahrens } get_cbdata_t; 870789Sahrens 871789Sahrens #define GET_COL_NAME 1 872789Sahrens #define GET_COL_PROPERTY 2 873789Sahrens #define GET_COL_VALUE 3 874789Sahrens #define GET_COL_SOURCE 4 875789Sahrens 876789Sahrens /* 8772676Seschrock * Print the column headers for 'zfs get'. 8782676Seschrock */ 8792676Seschrock static void 8802676Seschrock print_get_headers(get_cbdata_t *cbp) 8812676Seschrock { 8822676Seschrock zfs_proplist_t *pl = cbp->cb_proplist; 8832676Seschrock int i; 8842676Seschrock char *title; 8852676Seschrock size_t len; 8862676Seschrock 8872676Seschrock cbp->cb_first = B_FALSE; 8882676Seschrock if (cbp->cb_scripted) 8892676Seschrock return; 8902676Seschrock 8912676Seschrock /* 8922676Seschrock * Start with the length of the column headers. 8932676Seschrock */ 8942676Seschrock cbp->cb_colwidths[GET_COL_NAME] = strlen(gettext("NAME")); 8952676Seschrock cbp->cb_colwidths[GET_COL_PROPERTY] = strlen(gettext("PROPERTY")); 8962676Seschrock cbp->cb_colwidths[GET_COL_VALUE] = strlen(gettext("VALUE")); 8972676Seschrock cbp->cb_colwidths[GET_COL_SOURCE] = strlen(gettext("SOURCE")); 8982676Seschrock 8992676Seschrock /* 9002676Seschrock * Go through and calculate the widths for each column. For the 9012676Seschrock * 'source' column, we kludge it up by taking the worst-case scenario of 9022676Seschrock * inheriting from the longest name. This is acceptable because in the 9032676Seschrock * majority of cases 'SOURCE' is the last column displayed, and we don't 9042676Seschrock * use the width anyway. Note that the 'VALUE' column can be oversized, 9052676Seschrock * if the name of the property is much longer the any values we find. 9062676Seschrock */ 9072676Seschrock for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) { 9082676Seschrock /* 9092676Seschrock * 'PROPERTY' column 9102676Seschrock */ 9112676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 9122676Seschrock len = strlen(zfs_prop_to_name(pl->pl_prop)); 9132676Seschrock if (len > cbp->cb_colwidths[GET_COL_PROPERTY]) 9142676Seschrock cbp->cb_colwidths[GET_COL_PROPERTY] = len; 9152676Seschrock } else { 9162676Seschrock len = strlen(pl->pl_user_prop); 9172676Seschrock if (len > cbp->cb_colwidths[GET_COL_PROPERTY]) 9182676Seschrock cbp->cb_colwidths[GET_COL_PROPERTY] = len; 9192676Seschrock } 9202676Seschrock 9212676Seschrock /* 9222676Seschrock * 'VALUE' column 9232676Seschrock */ 9242676Seschrock if ((pl->pl_prop != ZFS_PROP_NAME || !pl->pl_all) && 9252676Seschrock pl->pl_width > cbp->cb_colwidths[GET_COL_VALUE]) 9262676Seschrock cbp->cb_colwidths[GET_COL_VALUE] = pl->pl_width; 9272676Seschrock 9282676Seschrock /* 9292676Seschrock * 'NAME' and 'SOURCE' columns 9302676Seschrock */ 9312676Seschrock if (pl->pl_prop == ZFS_PROP_NAME && 9322676Seschrock pl->pl_width > cbp->cb_colwidths[GET_COL_NAME]) { 9332676Seschrock cbp->cb_colwidths[GET_COL_NAME] = pl->pl_width; 9342676Seschrock cbp->cb_colwidths[GET_COL_SOURCE] = pl->pl_width + 9352676Seschrock strlen(gettext("inherited from")); 9362676Seschrock } 9372676Seschrock } 9382676Seschrock 9392676Seschrock /* 9402676Seschrock * Now go through and print the headers. 9412676Seschrock */ 9422676Seschrock for (i = 0; i < 4; i++) { 9432676Seschrock switch (cbp->cb_columns[i]) { 9442676Seschrock case GET_COL_NAME: 9452676Seschrock title = gettext("NAME"); 9462676Seschrock break; 9472676Seschrock case GET_COL_PROPERTY: 9482676Seschrock title = gettext("PROPERTY"); 9492676Seschrock break; 9502676Seschrock case GET_COL_VALUE: 9512676Seschrock title = gettext("VALUE"); 9522676Seschrock break; 9532676Seschrock case GET_COL_SOURCE: 9542676Seschrock title = gettext("SOURCE"); 9552676Seschrock break; 9562676Seschrock default: 9572676Seschrock title = NULL; 9582676Seschrock } 9592676Seschrock 9602676Seschrock if (title != NULL) { 9612676Seschrock if (i == 3 || cbp->cb_columns[i + 1] == 0) 9622676Seschrock (void) printf("%s", title); 9632676Seschrock else 9642676Seschrock (void) printf("%-*s ", 9652676Seschrock cbp->cb_colwidths[cbp->cb_columns[i]], 9662676Seschrock title); 9672676Seschrock } 9682676Seschrock } 9692676Seschrock (void) printf("\n"); 9702676Seschrock } 9712676Seschrock 9722676Seschrock /* 973789Sahrens * Display a single line of output, according to the settings in the callback 974789Sahrens * structure. 975789Sahrens */ 976789Sahrens static void 9772676Seschrock print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, const char *propname, 978789Sahrens const char *value, zfs_source_t sourcetype, const char *source) 979789Sahrens { 980789Sahrens int i; 981789Sahrens const char *str; 982789Sahrens char buf[128]; 983789Sahrens 984789Sahrens /* 985789Sahrens * Ignore those source types that the user has chosen to ignore. 986789Sahrens */ 987789Sahrens if ((sourcetype & cbp->cb_sources) == 0) 988789Sahrens return; 989789Sahrens 9902676Seschrock if (cbp->cb_first) 9912676Seschrock print_get_headers(cbp); 9922676Seschrock 993789Sahrens for (i = 0; i < 4; i++) { 994789Sahrens switch (cbp->cb_columns[i]) { 995789Sahrens case GET_COL_NAME: 996789Sahrens str = zfs_get_name(zhp); 997789Sahrens break; 998789Sahrens 999789Sahrens case GET_COL_PROPERTY: 10002676Seschrock str = propname; 1001789Sahrens break; 1002789Sahrens 1003789Sahrens case GET_COL_VALUE: 1004789Sahrens str = value; 1005789Sahrens break; 1006789Sahrens 1007789Sahrens case GET_COL_SOURCE: 1008789Sahrens switch (sourcetype) { 1009789Sahrens case ZFS_SRC_NONE: 1010789Sahrens str = "-"; 1011789Sahrens break; 1012789Sahrens 1013789Sahrens case ZFS_SRC_DEFAULT: 1014789Sahrens str = "default"; 1015789Sahrens break; 1016789Sahrens 1017789Sahrens case ZFS_SRC_LOCAL: 1018789Sahrens str = "local"; 1019789Sahrens break; 1020789Sahrens 1021789Sahrens case ZFS_SRC_TEMPORARY: 1022789Sahrens str = "temporary"; 1023789Sahrens break; 1024789Sahrens 1025789Sahrens case ZFS_SRC_INHERITED: 1026789Sahrens (void) snprintf(buf, sizeof (buf), 1027789Sahrens "inherited from %s", source); 1028789Sahrens str = buf; 1029789Sahrens break; 1030789Sahrens } 1031789Sahrens break; 1032789Sahrens 1033789Sahrens default: 1034789Sahrens continue; 1035789Sahrens } 1036789Sahrens 1037789Sahrens if (cbp->cb_columns[i + 1] == 0) 1038789Sahrens (void) printf("%s", str); 1039789Sahrens else if (cbp->cb_scripted) 1040789Sahrens (void) printf("%s\t", str); 1041789Sahrens else 10422676Seschrock (void) printf("%-*s ", 10432676Seschrock cbp->cb_colwidths[cbp->cb_columns[i]], 10442676Seschrock str); 1045789Sahrens 1046789Sahrens } 1047789Sahrens 1048789Sahrens (void) printf("\n"); 1049789Sahrens } 1050789Sahrens 1051789Sahrens /* 1052789Sahrens * Invoked to display the properties for a single dataset. 1053789Sahrens */ 1054789Sahrens static int 1055789Sahrens get_callback(zfs_handle_t *zhp, void *data) 1056789Sahrens { 1057789Sahrens char buf[ZFS_MAXPROPLEN]; 1058789Sahrens zfs_source_t sourcetype; 1059789Sahrens char source[ZFS_MAXNAMELEN]; 1060789Sahrens get_cbdata_t *cbp = data; 10612676Seschrock nvlist_t *userprop = zfs_get_user_props(zhp); 10622676Seschrock zfs_proplist_t *pl = cbp->cb_proplist; 10632676Seschrock nvlist_t *propval; 10642676Seschrock char *strval; 10652676Seschrock char *sourceval; 10662676Seschrock 10672676Seschrock for (; pl != NULL; pl = pl->pl_next) { 10682676Seschrock /* 10692676Seschrock * Skip the special fake placeholder. This will also skip over 10702676Seschrock * the name property when 'all' is specified. 10712676Seschrock */ 10722676Seschrock if (pl->pl_prop == ZFS_PROP_NAME && 10732676Seschrock pl == cbp->cb_proplist) 10742676Seschrock continue; 10752676Seschrock 10762676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 10772676Seschrock if (zfs_prop_get(zhp, pl->pl_prop, buf, 10782676Seschrock sizeof (buf), &sourcetype, source, 10792676Seschrock sizeof (source), 10802676Seschrock cbp->cb_literal) != 0) { 10812676Seschrock if (pl->pl_all) 10822676Seschrock continue; 10832676Seschrock sourcetype = ZFS_SRC_NONE; 10842676Seschrock (void) strlcpy(buf, "-", sizeof (buf)); 10852676Seschrock } 10862676Seschrock 10872676Seschrock print_one_property(zhp, cbp, 10882676Seschrock zfs_prop_to_name(pl->pl_prop), 10892676Seschrock buf, sourcetype, source); 10902676Seschrock } else { 10912676Seschrock if (nvlist_lookup_nvlist(userprop, 10922676Seschrock pl->pl_user_prop, &propval) != 0) { 10932676Seschrock if (pl->pl_all) 10942676Seschrock continue; 10952676Seschrock sourcetype = ZFS_SRC_NONE; 10962676Seschrock strval = "-"; 10972676Seschrock } else { 10982676Seschrock verify(nvlist_lookup_string(propval, 10992676Seschrock ZFS_PROP_VALUE, &strval) == 0); 11002676Seschrock verify(nvlist_lookup_string(propval, 11012676Seschrock ZFS_PROP_SOURCE, &sourceval) == 0); 11022676Seschrock 11032676Seschrock if (strcmp(sourceval, 11042676Seschrock zfs_get_name(zhp)) == 0) { 11052676Seschrock sourcetype = ZFS_SRC_LOCAL; 11062676Seschrock } else { 11072676Seschrock sourcetype = ZFS_SRC_INHERITED; 11082676Seschrock (void) strlcpy(source, 11092676Seschrock sourceval, sizeof (source)); 11102676Seschrock } 11112676Seschrock } 11122676Seschrock 11132676Seschrock print_one_property(zhp, cbp, 11142676Seschrock pl->pl_user_prop, strval, sourcetype, 11152676Seschrock source); 1116866Seschrock } 1117789Sahrens } 1118789Sahrens 1119789Sahrens return (0); 1120789Sahrens } 1121789Sahrens 1122789Sahrens static int 1123789Sahrens zfs_do_get(int argc, char **argv) 1124789Sahrens { 1125789Sahrens get_cbdata_t cb = { 0 }; 11262082Seschrock boolean_t recurse = B_FALSE; 11272676Seschrock int i, c; 11282676Seschrock char *value, *fields; 1129866Seschrock int ret; 11302676Seschrock zfs_proplist_t fake_name = { 0 }; 1131789Sahrens 1132789Sahrens /* 1133789Sahrens * Set up default columns and sources. 1134789Sahrens */ 1135789Sahrens cb.cb_sources = ZFS_SRC_ALL; 1136789Sahrens cb.cb_columns[0] = GET_COL_NAME; 1137789Sahrens cb.cb_columns[1] = GET_COL_PROPERTY; 1138789Sahrens cb.cb_columns[2] = GET_COL_VALUE; 1139789Sahrens cb.cb_columns[3] = GET_COL_SOURCE; 1140789Sahrens 1141789Sahrens /* check options */ 1142789Sahrens while ((c = getopt(argc, argv, ":o:s:rHp")) != -1) { 1143789Sahrens switch (c) { 1144789Sahrens case 'p': 11452082Seschrock cb.cb_literal = B_TRUE; 1146789Sahrens break; 1147789Sahrens case 'r': 11482082Seschrock recurse = B_TRUE; 1149789Sahrens break; 1150789Sahrens case 'H': 11512082Seschrock cb.cb_scripted = B_TRUE; 1152789Sahrens break; 1153789Sahrens case ':': 1154789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1155789Sahrens "'%c' option\n"), optopt); 11562082Seschrock usage(B_FALSE); 1157789Sahrens break; 1158789Sahrens case 'o': 1159789Sahrens /* 1160789Sahrens * Process the set of columns to display. We zero out 1161789Sahrens * the structure to give us a blank slate. 1162789Sahrens */ 1163789Sahrens bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 1164789Sahrens i = 0; 1165789Sahrens while (*optarg != '\0') { 1166789Sahrens static char *col_subopts[] = 1167789Sahrens { "name", "property", "value", "source", 1168789Sahrens NULL }; 1169789Sahrens 1170789Sahrens if (i == 4) { 1171789Sahrens (void) fprintf(stderr, gettext("too " 1172789Sahrens "many fields given to -o " 1173789Sahrens "option\n")); 11742082Seschrock usage(B_FALSE); 1175789Sahrens } 1176789Sahrens 1177789Sahrens switch (getsubopt(&optarg, col_subopts, 1178789Sahrens &value)) { 1179789Sahrens case 0: 1180789Sahrens cb.cb_columns[i++] = GET_COL_NAME; 1181789Sahrens break; 1182789Sahrens case 1: 1183789Sahrens cb.cb_columns[i++] = GET_COL_PROPERTY; 1184789Sahrens break; 1185789Sahrens case 2: 1186789Sahrens cb.cb_columns[i++] = GET_COL_VALUE; 1187789Sahrens break; 1188789Sahrens case 3: 1189789Sahrens cb.cb_columns[i++] = GET_COL_SOURCE; 1190789Sahrens break; 1191789Sahrens default: 1192789Sahrens (void) fprintf(stderr, 1193789Sahrens gettext("invalid column name " 1194789Sahrens "'%s'\n"), value); 11952082Seschrock usage(B_FALSE); 1196789Sahrens } 1197789Sahrens } 1198789Sahrens break; 1199789Sahrens 1200789Sahrens case 's': 1201789Sahrens cb.cb_sources = 0; 1202789Sahrens while (*optarg != '\0') { 1203789Sahrens static char *source_subopts[] = { 1204789Sahrens "local", "default", "inherited", 1205789Sahrens "temporary", "none", NULL }; 1206789Sahrens 1207789Sahrens switch (getsubopt(&optarg, source_subopts, 1208789Sahrens &value)) { 1209789Sahrens case 0: 1210789Sahrens cb.cb_sources |= ZFS_SRC_LOCAL; 1211789Sahrens break; 1212789Sahrens case 1: 1213789Sahrens cb.cb_sources |= ZFS_SRC_DEFAULT; 1214789Sahrens break; 1215789Sahrens case 2: 1216789Sahrens cb.cb_sources |= ZFS_SRC_INHERITED; 1217789Sahrens break; 1218789Sahrens case 3: 1219789Sahrens cb.cb_sources |= ZFS_SRC_TEMPORARY; 1220789Sahrens break; 1221789Sahrens case 4: 1222789Sahrens cb.cb_sources |= ZFS_SRC_NONE; 1223789Sahrens break; 1224789Sahrens default: 1225789Sahrens (void) fprintf(stderr, 1226789Sahrens gettext("invalid source " 1227789Sahrens "'%s'\n"), value); 12282082Seschrock usage(B_FALSE); 1229789Sahrens } 1230789Sahrens } 1231789Sahrens break; 1232789Sahrens 1233789Sahrens case '?': 1234789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1235789Sahrens optopt); 12362082Seschrock usage(B_FALSE); 1237789Sahrens } 1238789Sahrens } 1239789Sahrens 1240789Sahrens argc -= optind; 1241789Sahrens argv += optind; 1242789Sahrens 1243789Sahrens if (argc < 1) { 1244789Sahrens (void) fprintf(stderr, gettext("missing property " 1245789Sahrens "argument\n")); 12462082Seschrock usage(B_FALSE); 1247789Sahrens } 1248789Sahrens 1249789Sahrens fields = argv[0]; 1250789Sahrens 12512676Seschrock if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0) 12522082Seschrock usage(B_FALSE); 1253789Sahrens 1254789Sahrens argc--; 1255789Sahrens argv++; 1256789Sahrens 1257789Sahrens /* 12582676Seschrock * As part of zfs_expand_proplist(), we keep track of the maximum column 12592676Seschrock * width for each property. For the 'NAME' (and 'SOURCE') columns, we 12602676Seschrock * need to know the maximum name length. However, the user likely did 12612676Seschrock * not specify 'name' as one of the properties to fetch, so we need to 12622676Seschrock * make sure we always include at least this property for 12632676Seschrock * print_get_headers() to work properly. 1264789Sahrens */ 12652676Seschrock if (cb.cb_proplist != NULL) { 12662676Seschrock fake_name.pl_prop = ZFS_PROP_NAME; 12672676Seschrock fake_name.pl_width = strlen(gettext("NAME")); 12682676Seschrock fake_name.pl_next = cb.cb_proplist; 12692676Seschrock cb.cb_proplist = &fake_name; 1270789Sahrens } 1271789Sahrens 12722676Seschrock cb.cb_first = B_TRUE; 12732676Seschrock 1274789Sahrens /* run for each object */ 12752676Seschrock ret = zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, NULL, 1276*3635Sck153898 &cb.cb_proplist, get_callback, &cb, B_FALSE); 12772676Seschrock 12782676Seschrock if (cb.cb_proplist == &fake_name) 12792676Seschrock zfs_free_proplist(fake_name.pl_next); 12802676Seschrock else 12812676Seschrock zfs_free_proplist(cb.cb_proplist); 12822676Seschrock 12832676Seschrock return (ret); 1284789Sahrens } 1285789Sahrens 1286789Sahrens /* 1287789Sahrens * inherit [-r] <property> <fs|vol> ... 1288789Sahrens * 1289789Sahrens * -r Recurse over all children 1290789Sahrens * 1291789Sahrens * For each dataset specified on the command line, inherit the given property 1292789Sahrens * from its parent. Inheriting a property at the pool level will cause it to 1293789Sahrens * use the default value. The '-r' flag will recurse over all children, and is 1294789Sahrens * useful for setting a property on a hierarchy-wide basis, regardless of any 1295789Sahrens * local modifications for each dataset. 1296789Sahrens */ 12972926Sek110237 typedef struct inherit_cbdata { 12982926Sek110237 char *cb_propname; 12992926Sek110237 boolean_t cb_any_successful; 13002926Sek110237 } inherit_cbdata_t; 13012926Sek110237 1302789Sahrens static int 1303789Sahrens inherit_callback(zfs_handle_t *zhp, void *data) 1304789Sahrens { 13052926Sek110237 inherit_cbdata_t *cbp = data; 13062926Sek110237 int ret; 13072926Sek110237 13082926Sek110237 ret = zfs_prop_inherit(zhp, cbp->cb_propname); 13092926Sek110237 if (ret == 0) 13102926Sek110237 cbp->cb_any_successful = B_TRUE; 13112926Sek110237 return (ret != 0); 1312789Sahrens } 1313789Sahrens 1314789Sahrens static int 1315789Sahrens zfs_do_inherit(int argc, char **argv) 1316789Sahrens { 13172082Seschrock boolean_t recurse = B_FALSE; 1318789Sahrens int c; 1319789Sahrens zfs_prop_t prop; 13202926Sek110237 inherit_cbdata_t cb; 13212926Sek110237 int ret; 1322789Sahrens 1323789Sahrens /* check options */ 1324789Sahrens while ((c = getopt(argc, argv, "r")) != -1) { 1325789Sahrens switch (c) { 1326789Sahrens case 'r': 13272082Seschrock recurse = B_TRUE; 1328789Sahrens break; 1329789Sahrens case '?': 1330789Sahrens default: 1331789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1332789Sahrens optopt); 13332082Seschrock usage(B_FALSE); 1334789Sahrens } 1335789Sahrens } 1336789Sahrens 1337789Sahrens argc -= optind; 1338789Sahrens argv += optind; 1339789Sahrens 1340789Sahrens /* check number of arguments */ 1341789Sahrens if (argc < 1) { 1342789Sahrens (void) fprintf(stderr, gettext("missing property argument\n")); 13432082Seschrock usage(B_FALSE); 1344789Sahrens } 1345789Sahrens if (argc < 2) { 1346789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 13472082Seschrock usage(B_FALSE); 1348789Sahrens } 1349789Sahrens 13502926Sek110237 cb.cb_propname = argv[0]; 13512676Seschrock argc--; 13522676Seschrock argv++; 13532676Seschrock 13542926Sek110237 if ((prop = zfs_name_to_prop(cb.cb_propname)) != ZFS_PROP_INVAL) { 13552676Seschrock if (zfs_prop_readonly(prop)) { 13562676Seschrock (void) fprintf(stderr, gettext( 13572676Seschrock "%s property is read-only\n"), 13582926Sek110237 cb.cb_propname); 13592676Seschrock return (1); 13602676Seschrock } 13612676Seschrock if (!zfs_prop_inheritable(prop)) { 13622676Seschrock (void) fprintf(stderr, gettext("'%s' property cannot " 13632926Sek110237 "be inherited\n"), cb.cb_propname); 13642676Seschrock if (prop == ZFS_PROP_QUOTA || 13652676Seschrock prop == ZFS_PROP_RESERVATION) 13662676Seschrock (void) fprintf(stderr, gettext("use 'zfs set " 13672926Sek110237 "%s=none' to clear\n"), cb.cb_propname); 13682676Seschrock return (1); 13692676Seschrock } 13702926Sek110237 } else if (!zfs_prop_user(cb.cb_propname)) { 13712676Seschrock (void) fprintf(stderr, gettext( 13722676Seschrock "invalid property '%s'\n"), 13732926Sek110237 cb.cb_propname); 13742082Seschrock usage(B_FALSE); 1375789Sahrens } 13762676Seschrock 13772926Sek110237 cb.cb_any_successful = B_FALSE; 13782926Sek110237 13792926Sek110237 ret = zfs_for_each(argc, argv, recurse, 13802676Seschrock ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL, 1381*3635Sck153898 inherit_callback, &cb, B_FALSE); 13822926Sek110237 13832926Sek110237 if (cb.cb_any_successful) { 13842926Sek110237 zpool_log_history(g_zfs, argc + optind + 1, argv - optind - 1, 13852926Sek110237 argv[0], B_FALSE, B_FALSE); 13862926Sek110237 } 13872926Sek110237 13882926Sek110237 return (ret); 1389789Sahrens } 1390789Sahrens 1391789Sahrens /* 13922379Ssjelinek * list [-rH] [-o property[,property]...] [-t type[,type]...] 13932379Ssjelinek * [-s property [-s property]...] [-S property [-S property]...] 13942379Ssjelinek * <dataset> ... 1395789Sahrens * 1396789Sahrens * -r Recurse over all children 1397789Sahrens * -H Scripted mode; elide headers and separate colums by tabs 1398789Sahrens * -o Control which fields to display. 1399866Seschrock * -t Control which object types to display. 14002379Ssjelinek * -s Specify sort columns, descending order. 14012379Ssjelinek * -S Specify sort columns, ascending order. 1402789Sahrens * 1403789Sahrens * When given no arguments, lists all filesystems in the system. 1404789Sahrens * Otherwise, list the specified datasets, optionally recursing down them if 1405789Sahrens * '-r' is specified. 1406789Sahrens */ 1407789Sahrens typedef struct list_cbdata { 14082082Seschrock boolean_t cb_first; 14092082Seschrock boolean_t cb_scripted; 14102676Seschrock zfs_proplist_t *cb_proplist; 1411789Sahrens } list_cbdata_t; 1412789Sahrens 1413789Sahrens /* 1414789Sahrens * Given a list of columns to display, output appropriate headers for each one. 1415789Sahrens */ 1416789Sahrens static void 14172676Seschrock print_header(zfs_proplist_t *pl) 1418789Sahrens { 14192676Seschrock char headerbuf[ZFS_MAXPROPLEN]; 14202676Seschrock const char *header; 1421789Sahrens int i; 14222676Seschrock boolean_t first = B_TRUE; 14232676Seschrock boolean_t right_justify; 14242676Seschrock 14252676Seschrock for (; pl != NULL; pl = pl->pl_next) { 14262676Seschrock if (!first) { 1427789Sahrens (void) printf(" "); 14282676Seschrock } else { 14292676Seschrock first = B_FALSE; 14302676Seschrock } 14312676Seschrock 14322676Seschrock right_justify = B_FALSE; 14332676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 14342676Seschrock header = zfs_prop_column_name(pl->pl_prop); 14352676Seschrock right_justify = zfs_prop_align_right(pl->pl_prop); 14362676Seschrock } else { 14372676Seschrock for (i = 0; pl->pl_user_prop[i] != '\0'; i++) 14382676Seschrock headerbuf[i] = toupper(pl->pl_user_prop[i]); 14392676Seschrock headerbuf[i] = '\0'; 14402676Seschrock header = headerbuf; 14412676Seschrock } 14422676Seschrock 14432676Seschrock if (pl->pl_next == NULL && !right_justify) 14442676Seschrock (void) printf("%s", header); 14452676Seschrock else if (right_justify) 14462676Seschrock (void) printf("%*s", pl->pl_width, header); 14472676Seschrock else 14482676Seschrock (void) printf("%-*s", pl->pl_width, header); 1449789Sahrens } 1450789Sahrens 1451789Sahrens (void) printf("\n"); 1452789Sahrens } 1453789Sahrens 1454789Sahrens /* 1455789Sahrens * Given a dataset and a list of fields, print out all the properties according 1456789Sahrens * to the described layout. 1457789Sahrens */ 1458789Sahrens static void 14592676Seschrock print_dataset(zfs_handle_t *zhp, zfs_proplist_t *pl, int scripted) 1460789Sahrens { 14612676Seschrock boolean_t first = B_TRUE; 1462789Sahrens char property[ZFS_MAXPROPLEN]; 14632676Seschrock nvlist_t *userprops = zfs_get_user_props(zhp); 14642676Seschrock nvlist_t *propval; 14652676Seschrock char *propstr; 14662676Seschrock boolean_t right_justify; 14672676Seschrock int width; 14682676Seschrock 14692676Seschrock for (; pl != NULL; pl = pl->pl_next) { 14702676Seschrock if (!first) { 1471789Sahrens if (scripted) 1472789Sahrens (void) printf("\t"); 1473789Sahrens else 1474789Sahrens (void) printf(" "); 14752676Seschrock } else { 14762676Seschrock first = B_FALSE; 1477789Sahrens } 1478789Sahrens 14792676Seschrock right_justify = B_FALSE; 14802676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 14812676Seschrock if (zfs_prop_get(zhp, pl->pl_prop, property, 14822676Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) != 0) 14832676Seschrock propstr = "-"; 14842676Seschrock else 14852676Seschrock propstr = property; 14862676Seschrock 14872676Seschrock right_justify = zfs_prop_align_right(pl->pl_prop); 14882676Seschrock } else { 14892676Seschrock if (nvlist_lookup_nvlist(userprops, 14902676Seschrock pl->pl_user_prop, &propval) != 0) 14912676Seschrock propstr = "-"; 14922676Seschrock else 14932676Seschrock verify(nvlist_lookup_string(propval, 14942676Seschrock ZFS_PROP_VALUE, &propstr) == 0); 14952676Seschrock } 14962676Seschrock 14972676Seschrock width = pl->pl_width; 1498789Sahrens 1499866Seschrock /* 1500866Seschrock * If this is being called in scripted mode, or if this is the 1501866Seschrock * last column and it is left-justified, don't include a width 1502866Seschrock * format specifier. 1503866Seschrock */ 15042676Seschrock if (scripted || (pl->pl_next == NULL && !right_justify)) 15052676Seschrock (void) printf("%s", propstr); 15062676Seschrock else if (right_justify) 15072676Seschrock (void) printf("%*s", width, propstr); 15082676Seschrock else 15092676Seschrock (void) printf("%-*s", width, propstr); 1510789Sahrens } 1511789Sahrens 1512789Sahrens (void) printf("\n"); 1513789Sahrens } 1514789Sahrens 1515789Sahrens /* 1516789Sahrens * Generic callback function to list a dataset or snapshot. 1517789Sahrens */ 1518789Sahrens static int 1519789Sahrens list_callback(zfs_handle_t *zhp, void *data) 1520789Sahrens { 1521789Sahrens list_cbdata_t *cbp = data; 1522789Sahrens 1523789Sahrens if (cbp->cb_first) { 1524789Sahrens if (!cbp->cb_scripted) 15252676Seschrock print_header(cbp->cb_proplist); 15262082Seschrock cbp->cb_first = B_FALSE; 1527789Sahrens } 1528789Sahrens 15292676Seschrock print_dataset(zhp, cbp->cb_proplist, cbp->cb_scripted); 1530789Sahrens 1531789Sahrens return (0); 1532789Sahrens } 1533789Sahrens 1534789Sahrens static int 1535789Sahrens zfs_do_list(int argc, char **argv) 1536789Sahrens { 1537789Sahrens int c; 15382082Seschrock boolean_t recurse = B_FALSE; 15392082Seschrock boolean_t scripted = B_FALSE; 1540789Sahrens static char default_fields[] = 1541789Sahrens "name,used,available,referenced,mountpoint"; 1542789Sahrens int types = ZFS_TYPE_ANY; 1543789Sahrens char *fields = NULL; 1544789Sahrens char *basic_fields = default_fields; 1545789Sahrens list_cbdata_t cb = { 0 }; 1546789Sahrens char *value; 1547789Sahrens int ret; 1548789Sahrens char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL }; 15492379Ssjelinek zfs_sort_column_t *sortcol = NULL; 1550789Sahrens 1551789Sahrens /* check options */ 15522379Ssjelinek while ((c = getopt(argc, argv, ":o:rt:Hs:S:")) != -1) { 1553789Sahrens switch (c) { 1554789Sahrens case 'o': 1555789Sahrens fields = optarg; 1556789Sahrens break; 1557789Sahrens case 'r': 15582082Seschrock recurse = B_TRUE; 1559789Sahrens break; 1560789Sahrens case 'H': 15612082Seschrock scripted = B_TRUE; 1562789Sahrens break; 15632379Ssjelinek case 's': 15642676Seschrock if (zfs_add_sort_column(&sortcol, optarg, 15652676Seschrock B_FALSE) != 0) { 15662379Ssjelinek (void) fprintf(stderr, 15672379Ssjelinek gettext("invalid property '%s'\n"), optarg); 15682379Ssjelinek usage(B_FALSE); 15692379Ssjelinek } 15702379Ssjelinek break; 15712379Ssjelinek case 'S': 15722676Seschrock if (zfs_add_sort_column(&sortcol, optarg, 15732676Seschrock B_TRUE) != 0) { 15742379Ssjelinek (void) fprintf(stderr, 15752379Ssjelinek gettext("invalid property '%s'\n"), optarg); 15762379Ssjelinek usage(B_FALSE); 15772379Ssjelinek } 15782379Ssjelinek break; 1579789Sahrens case 't': 1580789Sahrens types = 0; 1581789Sahrens while (*optarg != '\0') { 1582789Sahrens switch (getsubopt(&optarg, type_subopts, 1583789Sahrens &value)) { 1584789Sahrens case 0: 1585789Sahrens types |= ZFS_TYPE_FILESYSTEM; 1586789Sahrens break; 1587789Sahrens case 1: 1588789Sahrens types |= ZFS_TYPE_VOLUME; 1589789Sahrens break; 1590789Sahrens case 2: 1591789Sahrens types |= ZFS_TYPE_SNAPSHOT; 1592789Sahrens break; 1593789Sahrens default: 1594789Sahrens (void) fprintf(stderr, 1595789Sahrens gettext("invalid type '%s'\n"), 1596789Sahrens value); 15972082Seschrock usage(B_FALSE); 1598789Sahrens } 1599789Sahrens } 1600789Sahrens break; 1601789Sahrens case ':': 1602789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1603789Sahrens "'%c' option\n"), optopt); 16042082Seschrock usage(B_FALSE); 1605789Sahrens break; 1606789Sahrens case '?': 1607789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1608789Sahrens optopt); 16092082Seschrock usage(B_FALSE); 1610789Sahrens } 1611789Sahrens } 1612789Sahrens 1613789Sahrens argc -= optind; 1614789Sahrens argv += optind; 1615789Sahrens 1616789Sahrens if (fields == NULL) 1617789Sahrens fields = basic_fields; 1618789Sahrens 1619866Seschrock /* 1620866Seschrock * If the user specifies '-o all', the zfs_get_proplist() doesn't 1621866Seschrock * normally include the name of the dataset. For 'zfs list', we always 1622866Seschrock * want this property to be first. 1623866Seschrock */ 16242676Seschrock if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0) 16252082Seschrock usage(B_FALSE); 16262676Seschrock 1627789Sahrens cb.cb_scripted = scripted; 16282082Seschrock cb.cb_first = B_TRUE; 1629789Sahrens 16302676Seschrock ret = zfs_for_each(argc, argv, recurse, types, sortcol, &cb.cb_proplist, 1631*3635Sck153898 list_callback, &cb, B_TRUE); 16322379Ssjelinek 16332676Seschrock zfs_free_proplist(cb.cb_proplist); 16342379Ssjelinek zfs_free_sort_columns(sortcol); 1635789Sahrens 16362082Seschrock if (ret == 0 && cb.cb_first) 1637789Sahrens (void) printf(gettext("no datasets available\n")); 1638789Sahrens 1639789Sahrens return (ret); 1640789Sahrens } 1641789Sahrens 1642789Sahrens /* 1643789Sahrens * zfs rename <fs | snap | vol> <fs | snap | vol> 1644789Sahrens * 1645789Sahrens * Renames the given dataset to another of the same type. 1646789Sahrens */ 1647789Sahrens /* ARGSUSED */ 1648789Sahrens static int 1649789Sahrens zfs_do_rename(int argc, char **argv) 1650789Sahrens { 1651789Sahrens zfs_handle_t *zhp; 16522082Seschrock int ret; 1653789Sahrens 1654789Sahrens /* check options */ 1655789Sahrens if (argc > 1 && argv[1][0] == '-') { 1656789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1657789Sahrens argv[1][1]); 16582082Seschrock usage(B_FALSE); 1659789Sahrens } 1660789Sahrens 1661789Sahrens /* check number of arguments */ 1662789Sahrens if (argc < 2) { 1663789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 1664789Sahrens "argument\n")); 16652082Seschrock usage(B_FALSE); 1666789Sahrens } 1667789Sahrens if (argc < 3) { 1668789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 1669789Sahrens "argument\n")); 16702082Seschrock usage(B_FALSE); 1671789Sahrens } 1672789Sahrens if (argc > 3) { 1673789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 16742082Seschrock usage(B_FALSE); 1675789Sahrens } 1676789Sahrens 16772082Seschrock if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_ANY)) == NULL) 1678789Sahrens return (1); 1679789Sahrens 16802082Seschrock ret = (zfs_rename(zhp, argv[2]) != 0); 16812082Seschrock 16822926Sek110237 if (!ret) 16832926Sek110237 zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE); 16842926Sek110237 16852082Seschrock zfs_close(zhp); 16862082Seschrock return (ret); 16872082Seschrock } 16882082Seschrock 16892082Seschrock /* 16902082Seschrock * zfs promote <fs> 16912082Seschrock * 16922082Seschrock * Promotes the given clone fs to be the parent 16932082Seschrock */ 16942082Seschrock /* ARGSUSED */ 16952082Seschrock static int 16962082Seschrock zfs_do_promote(int argc, char **argv) 16972082Seschrock { 16982082Seschrock zfs_handle_t *zhp; 16992082Seschrock int ret; 17002082Seschrock 17012082Seschrock /* check options */ 17022082Seschrock if (argc > 1 && argv[1][0] == '-') { 17032082Seschrock (void) fprintf(stderr, gettext("invalid option '%c'\n"), 17042082Seschrock argv[1][1]); 17052082Seschrock usage(B_FALSE); 17062082Seschrock } 17072082Seschrock 17082082Seschrock /* check number of arguments */ 17092082Seschrock if (argc < 2) { 17102082Seschrock (void) fprintf(stderr, gettext("missing clone filesystem" 17112597Snd150628 " argument\n")); 17122082Seschrock usage(B_FALSE); 17132082Seschrock } 17142082Seschrock if (argc > 2) { 17152082Seschrock (void) fprintf(stderr, gettext("too many arguments\n")); 17162082Seschrock usage(B_FALSE); 17172082Seschrock } 17182082Seschrock 17192082Seschrock zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 17202082Seschrock if (zhp == NULL) 17212082Seschrock return (1); 17222082Seschrock 17232082Seschrock ret = (zfs_promote(zhp) != 0); 17242082Seschrock 17252926Sek110237 if (!ret) 17262926Sek110237 zpool_log_history(g_zfs, argc, argv, argv[1], B_FALSE, B_FALSE); 17272926Sek110237 1728789Sahrens zfs_close(zhp); 1729789Sahrens return (ret); 1730789Sahrens } 1731789Sahrens 1732789Sahrens /* 1733789Sahrens * zfs rollback [-rfR] <snapshot> 1734789Sahrens * 1735789Sahrens * -r Delete any intervening snapshots before doing rollback 1736789Sahrens * -R Delete any snapshots and their clones 1737789Sahrens * -f Force unmount filesystems, even if they are in use. 1738789Sahrens * 1739789Sahrens * Given a filesystem, rollback to a specific snapshot, discarding any changes 1740789Sahrens * since then and making it the active dataset. If more recent snapshots exist, 1741789Sahrens * the command will complain unless the '-r' flag is given. 1742789Sahrens */ 1743789Sahrens typedef struct rollback_cbdata { 1744789Sahrens uint64_t cb_create; 17452082Seschrock boolean_t cb_first; 1746789Sahrens int cb_doclones; 1747789Sahrens char *cb_target; 1748789Sahrens int cb_error; 17492082Seschrock boolean_t cb_recurse; 17502082Seschrock boolean_t cb_dependent; 1751789Sahrens } rollback_cbdata_t; 1752789Sahrens 1753789Sahrens /* 1754789Sahrens * Report any snapshots more recent than the one specified. Used when '-r' is 1755789Sahrens * not specified. We reuse this same callback for the snapshot dependents - if 1756789Sahrens * 'cb_dependent' is set, then this is a dependent and we should report it 1757789Sahrens * without checking the transaction group. 1758789Sahrens */ 1759789Sahrens static int 1760789Sahrens rollback_check(zfs_handle_t *zhp, void *data) 1761789Sahrens { 1762789Sahrens rollback_cbdata_t *cbp = data; 1763789Sahrens 17642082Seschrock if (cbp->cb_doclones) { 17652082Seschrock zfs_close(zhp); 1766789Sahrens return (0); 17672082Seschrock } 1768789Sahrens 1769789Sahrens if (!cbp->cb_dependent) { 1770789Sahrens if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && 17711294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 1772789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 1773789Sahrens cbp->cb_create) { 1774789Sahrens 1775789Sahrens if (cbp->cb_first && !cbp->cb_recurse) { 1776789Sahrens (void) fprintf(stderr, gettext("cannot " 1777789Sahrens "rollback to '%s': more recent snapshots " 1778789Sahrens "exist\n"), 1779789Sahrens cbp->cb_target); 1780789Sahrens (void) fprintf(stderr, gettext("use '-r' to " 1781789Sahrens "force deletion of the following " 1782789Sahrens "snapshots:\n")); 1783789Sahrens cbp->cb_first = 0; 1784789Sahrens cbp->cb_error = 1; 1785789Sahrens } 1786789Sahrens 1787789Sahrens if (cbp->cb_recurse) { 17882082Seschrock cbp->cb_dependent = B_TRUE; 17892474Seschrock if (zfs_iter_dependents(zhp, B_TRUE, 17902474Seschrock rollback_check, cbp) != 0) { 17912474Seschrock zfs_close(zhp); 17922474Seschrock return (-1); 17932474Seschrock } 17942082Seschrock cbp->cb_dependent = B_FALSE; 1795789Sahrens } else { 1796789Sahrens (void) fprintf(stderr, "%s\n", 1797789Sahrens zfs_get_name(zhp)); 1798789Sahrens } 1799789Sahrens } 1800789Sahrens } else { 1801789Sahrens if (cbp->cb_first && cbp->cb_recurse) { 1802789Sahrens (void) fprintf(stderr, gettext("cannot rollback to " 1803789Sahrens "'%s': clones of previous snapshots exist\n"), 1804789Sahrens cbp->cb_target); 1805789Sahrens (void) fprintf(stderr, gettext("use '-R' to " 1806789Sahrens "force deletion of the following clones and " 1807789Sahrens "dependents:\n")); 1808789Sahrens cbp->cb_first = 0; 1809789Sahrens cbp->cb_error = 1; 1810789Sahrens } 1811789Sahrens 1812789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1813789Sahrens } 1814789Sahrens 1815789Sahrens zfs_close(zhp); 1816789Sahrens return (0); 1817789Sahrens } 1818789Sahrens 1819789Sahrens static int 1820789Sahrens zfs_do_rollback(int argc, char **argv) 1821789Sahrens { 1822789Sahrens int ret; 1823789Sahrens int c; 1824789Sahrens rollback_cbdata_t cb = { 0 }; 1825789Sahrens zfs_handle_t *zhp, *snap; 1826789Sahrens char parentname[ZFS_MAXNAMELEN]; 1827789Sahrens char *delim; 18281294Slling int force = 0; 1829789Sahrens 1830789Sahrens /* check options */ 1831789Sahrens while ((c = getopt(argc, argv, "rfR")) != -1) { 1832789Sahrens switch (c) { 1833789Sahrens case 'f': 18341294Slling force = 1; 1835789Sahrens break; 1836789Sahrens case 'r': 1837789Sahrens cb.cb_recurse = 1; 1838789Sahrens break; 1839789Sahrens case 'R': 1840789Sahrens cb.cb_recurse = 1; 1841789Sahrens cb.cb_doclones = 1; 1842789Sahrens break; 1843789Sahrens case '?': 1844789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1845789Sahrens optopt); 18462082Seschrock usage(B_FALSE); 1847789Sahrens } 1848789Sahrens } 1849789Sahrens 1850789Sahrens argc -= optind; 1851789Sahrens argv += optind; 1852789Sahrens 1853789Sahrens /* check number of arguments */ 1854789Sahrens if (argc < 1) { 1855789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 18562082Seschrock usage(B_FALSE); 1857789Sahrens } 1858789Sahrens if (argc > 1) { 1859789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 18602082Seschrock usage(B_FALSE); 1861789Sahrens } 1862789Sahrens 1863789Sahrens /* open the snapshot */ 18642082Seschrock if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1865789Sahrens return (1); 1866789Sahrens 18671294Slling /* open the parent dataset */ 18681294Slling (void) strlcpy(parentname, argv[0], sizeof (parentname)); 1869789Sahrens verify((delim = strrchr(parentname, '@')) != NULL); 1870789Sahrens *delim = '\0'; 18712082Seschrock if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_ANY)) == NULL) { 1872789Sahrens zfs_close(snap); 1873789Sahrens return (1); 1874789Sahrens } 1875789Sahrens 1876789Sahrens /* 1877789Sahrens * Check for more recent snapshots and/or clones based on the presence 1878789Sahrens * of '-r' and '-R'. 1879789Sahrens */ 18801294Slling cb.cb_target = argv[0]; 18811294Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 18822082Seschrock cb.cb_first = B_TRUE; 1883789Sahrens cb.cb_error = 0; 18842474Seschrock if ((ret = zfs_iter_children(zhp, rollback_check, &cb)) != 0) 18852474Seschrock goto out; 1886789Sahrens 1887789Sahrens if ((ret = cb.cb_error) != 0) 1888789Sahrens goto out; 1889789Sahrens 1890789Sahrens /* 18911294Slling * Rollback parent to the given snapshot. 1892789Sahrens */ 18931294Slling ret = zfs_rollback(zhp, snap, force); 1894789Sahrens 18952926Sek110237 if (!ret) { 18962926Sek110237 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0], 18972926Sek110237 B_FALSE, B_FALSE); 18982926Sek110237 } 18992926Sek110237 1900789Sahrens out: 1901789Sahrens zfs_close(snap); 1902789Sahrens zfs_close(zhp); 1903789Sahrens 1904789Sahrens if (ret == 0) 1905789Sahrens return (0); 1906789Sahrens else 1907789Sahrens return (1); 1908789Sahrens } 1909789Sahrens 1910789Sahrens /* 1911789Sahrens * zfs set property=value { fs | snap | vol } ... 1912789Sahrens * 1913789Sahrens * Sets the given property for all datasets specified on the command line. 1914789Sahrens */ 1915789Sahrens typedef struct set_cbdata { 1916789Sahrens char *cb_propname; 1917789Sahrens char *cb_value; 19182926Sek110237 boolean_t cb_any_successful; 1919789Sahrens } set_cbdata_t; 1920789Sahrens 1921789Sahrens static int 1922789Sahrens set_callback(zfs_handle_t *zhp, void *data) 1923789Sahrens { 1924789Sahrens set_cbdata_t *cbp = data; 1925789Sahrens 19262676Seschrock if (zfs_prop_set(zhp, cbp->cb_propname, cbp->cb_value) != 0) { 19272169Snd150628 switch (libzfs_errno(g_zfs)) { 19282169Snd150628 case EZFS_MOUNTFAILED: 19292169Snd150628 (void) fprintf(stderr, gettext("property may be set " 19302169Snd150628 "but unable to remount filesystem\n")); 19312169Snd150628 break; 19323126Sahl case EZFS_SHARENFSFAILED: 19332169Snd150628 (void) fprintf(stderr, gettext("property may be set " 19342169Snd150628 "but unable to reshare filesystem\n")); 19352169Snd150628 break; 19362169Snd150628 } 1937789Sahrens return (1); 19382169Snd150628 } 19392926Sek110237 cbp->cb_any_successful = B_TRUE; 19402856Snd150628 return (0); 1941789Sahrens } 1942789Sahrens 1943789Sahrens static int 1944789Sahrens zfs_do_set(int argc, char **argv) 1945789Sahrens { 1946789Sahrens set_cbdata_t cb; 19472926Sek110237 int ret; 1948789Sahrens 1949789Sahrens /* check for options */ 1950789Sahrens if (argc > 1 && argv[1][0] == '-') { 1951789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1952789Sahrens argv[1][1]); 19532082Seschrock usage(B_FALSE); 1954789Sahrens } 1955789Sahrens 1956789Sahrens /* check number of arguments */ 1957789Sahrens if (argc < 2) { 1958789Sahrens (void) fprintf(stderr, gettext("missing property=value " 1959789Sahrens "argument\n")); 19602082Seschrock usage(B_FALSE); 1961789Sahrens } 1962789Sahrens if (argc < 3) { 1963789Sahrens (void) fprintf(stderr, gettext("missing dataset name\n")); 19642082Seschrock usage(B_FALSE); 1965789Sahrens } 1966789Sahrens 1967789Sahrens /* validate property=value argument */ 1968789Sahrens cb.cb_propname = argv[1]; 1969789Sahrens if ((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) { 1970789Sahrens (void) fprintf(stderr, gettext("missing value in " 1971789Sahrens "property=value argument\n")); 19722082Seschrock usage(B_FALSE); 1973789Sahrens } 1974789Sahrens 1975789Sahrens *cb.cb_value = '\0'; 1976789Sahrens cb.cb_value++; 19772926Sek110237 cb.cb_any_successful = B_FALSE; 1978789Sahrens 1979789Sahrens if (*cb.cb_propname == '\0') { 1980789Sahrens (void) fprintf(stderr, 1981789Sahrens gettext("missing property in property=value argument\n")); 19822082Seschrock usage(B_FALSE); 1983789Sahrens } 1984789Sahrens 19852926Sek110237 ret = zfs_for_each(argc - 2, argv + 2, B_FALSE, 1986*3635Sck153898 ZFS_TYPE_ANY, NULL, NULL, set_callback, &cb, B_FALSE); 19872926Sek110237 19882926Sek110237 if (cb.cb_any_successful) { 19892926Sek110237 *(cb.cb_value - 1) = '='; 19902926Sek110237 zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE); 19912926Sek110237 } 19922926Sek110237 19932926Sek110237 return (ret); 1994789Sahrens } 1995789Sahrens 1996789Sahrens /* 19972199Sahrens * zfs snapshot [-r] <fs@snap> 1998789Sahrens * 1999789Sahrens * Creates a snapshot with the given name. While functionally equivalent to 2000789Sahrens * 'zfs create', it is a separate command to diffferentiate intent. 2001789Sahrens */ 2002789Sahrens static int 2003789Sahrens zfs_do_snapshot(int argc, char **argv) 2004789Sahrens { 20052199Sahrens int recursive = B_FALSE; 20062199Sahrens int ret; 20072199Sahrens char c; 20082199Sahrens 2009789Sahrens /* check options */ 20102199Sahrens while ((c = getopt(argc, argv, ":r")) != -1) { 20112199Sahrens switch (c) { 20122199Sahrens case 'r': 20132199Sahrens recursive = B_TRUE; 20142199Sahrens break; 20152199Sahrens case '?': 20162199Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 20172199Sahrens optopt); 20182199Sahrens usage(B_FALSE); 20192199Sahrens } 2020789Sahrens } 2021789Sahrens 20222199Sahrens argc -= optind; 20232199Sahrens argv += optind; 20242199Sahrens 2025789Sahrens /* check number of arguments */ 20262199Sahrens if (argc < 1) { 2027789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 20282082Seschrock usage(B_FALSE); 2029789Sahrens } 20302199Sahrens if (argc > 1) { 2031789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 20322082Seschrock usage(B_FALSE); 2033789Sahrens } 2034789Sahrens 20352199Sahrens ret = zfs_snapshot(g_zfs, argv[0], recursive); 20362199Sahrens if (ret && recursive) 20372199Sahrens (void) fprintf(stderr, gettext("no snapshots were created\n")); 20382926Sek110237 if (!ret) { 20392926Sek110237 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0], 20402926Sek110237 B_FALSE, B_FALSE); 20412926Sek110237 } 20422199Sahrens return (ret != 0); 2043789Sahrens } 2044789Sahrens 2045789Sahrens /* 20462885Sahrens * zfs send [-i <@snap>] <fs@snap> 2047789Sahrens * 2048789Sahrens * Send a backup stream to stdout. 2049789Sahrens */ 2050789Sahrens static int 20511749Sahrens zfs_do_send(int argc, char **argv) 2052789Sahrens { 2053789Sahrens char *fromname = NULL; 20542885Sahrens char *cp; 20552885Sahrens zfs_handle_t *zhp; 2056789Sahrens int c, err; 2057789Sahrens 2058789Sahrens /* check options */ 2059789Sahrens while ((c = getopt(argc, argv, ":i:")) != -1) { 2060789Sahrens switch (c) { 2061789Sahrens case 'i': 20622885Sahrens if (fromname) 20632885Sahrens usage(B_FALSE); 2064789Sahrens fromname = optarg; 2065789Sahrens break; 2066789Sahrens case ':': 2067789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2068789Sahrens "'%c' option\n"), optopt); 20692082Seschrock usage(B_FALSE); 2070789Sahrens break; 2071789Sahrens case '?': 2072789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2073789Sahrens optopt); 20742082Seschrock usage(B_FALSE); 2075789Sahrens } 2076789Sahrens } 2077789Sahrens 2078789Sahrens argc -= optind; 2079789Sahrens argv += optind; 2080789Sahrens 2081789Sahrens /* check number of arguments */ 2082789Sahrens if (argc < 1) { 2083789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 20842082Seschrock usage(B_FALSE); 2085789Sahrens } 2086789Sahrens if (argc > 1) { 2087789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 20882082Seschrock usage(B_FALSE); 2089789Sahrens } 2090789Sahrens 2091789Sahrens if (isatty(STDOUT_FILENO)) { 2092789Sahrens (void) fprintf(stderr, 20932885Sahrens gettext("Error: Stream can not be written to a terminal.\n" 2094789Sahrens "You must redirect standard output.\n")); 2095789Sahrens return (1); 2096789Sahrens } 2097789Sahrens 20982885Sahrens if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 20992665Snd150628 return (1); 21002665Snd150628 21012885Sahrens /* 21022885Sahrens * If they specified the full path to the snapshot, chop off 21032885Sahrens * everything except the short name of the snapshot. 21042885Sahrens */ 21052885Sahrens if (fromname && (cp = strchr(fromname, '@')) != NULL) { 21062885Sahrens if (cp != fromname && 21072885Sahrens strncmp(argv[0], fromname, cp - fromname + 1)) { 21082885Sahrens (void) fprintf(stderr, 21092885Sahrens gettext("incremental source must be " 21102885Sahrens "in same filesystem\n")); 21112885Sahrens usage(B_FALSE); 21122665Snd150628 } 21132885Sahrens fromname = cp + 1; 21142885Sahrens if (strchr(fromname, '@') || strchr(fromname, '/')) { 21152885Sahrens (void) fprintf(stderr, 21162885Sahrens gettext("invalid incremental source\n")); 21172885Sahrens usage(B_FALSE); 21182885Sahrens } 2119789Sahrens } 2120789Sahrens 21213504Sahl err = zfs_send(zhp, fromname, STDOUT_FILENO); 21222885Sahrens zfs_close(zhp); 2123789Sahrens 2124789Sahrens return (err != 0); 2125789Sahrens } 2126789Sahrens 2127789Sahrens /* 21281749Sahrens * zfs receive <fs@snap> 2129789Sahrens * 2130789Sahrens * Restore a backup stream from stdin. 2131789Sahrens */ 2132789Sahrens static int 21331749Sahrens zfs_do_receive(int argc, char **argv) 2134789Sahrens { 2135789Sahrens int c, err; 21362082Seschrock boolean_t isprefix = B_FALSE; 21372082Seschrock boolean_t dryrun = B_FALSE; 21382082Seschrock boolean_t verbose = B_FALSE; 21392665Snd150628 boolean_t force = B_FALSE; 2140789Sahrens 2141789Sahrens /* check options */ 21422665Snd150628 while ((c = getopt(argc, argv, ":dnvF")) != -1) { 2143789Sahrens switch (c) { 2144789Sahrens case 'd': 21452082Seschrock isprefix = B_TRUE; 2146789Sahrens break; 2147789Sahrens case 'n': 21482082Seschrock dryrun = B_TRUE; 2149789Sahrens break; 2150789Sahrens case 'v': 21512082Seschrock verbose = B_TRUE; 2152789Sahrens break; 21532665Snd150628 case 'F': 21542665Snd150628 force = B_TRUE; 21552665Snd150628 break; 2156789Sahrens case ':': 2157789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2158789Sahrens "'%c' option\n"), optopt); 21592082Seschrock usage(B_FALSE); 2160789Sahrens break; 2161789Sahrens case '?': 2162789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2163789Sahrens optopt); 21642082Seschrock usage(B_FALSE); 2165789Sahrens } 2166789Sahrens } 2167789Sahrens 2168789Sahrens argc -= optind; 2169789Sahrens argv += optind; 2170789Sahrens 2171789Sahrens /* check number of arguments */ 2172789Sahrens if (argc < 1) { 2173789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 21742082Seschrock usage(B_FALSE); 2175789Sahrens } 2176789Sahrens if (argc > 1) { 2177789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 21782082Seschrock usage(B_FALSE); 2179789Sahrens } 2180789Sahrens 2181789Sahrens if (isatty(STDIN_FILENO)) { 2182789Sahrens (void) fprintf(stderr, 2183789Sahrens gettext("Error: Backup stream can not be read " 2184789Sahrens "from a terminal.\n" 2185789Sahrens "You must redirect standard input.\n")); 2186789Sahrens return (1); 2187789Sahrens } 2188789Sahrens 21893504Sahl err = zfs_receive(g_zfs, argv[0], isprefix, verbose, dryrun, force, 21903504Sahl STDIN_FILENO); 21912926Sek110237 21922926Sek110237 if (!err) { 21932926Sek110237 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0], 21942926Sek110237 B_FALSE, B_FALSE); 21952926Sek110237 } 21962926Sek110237 2197789Sahrens return (err != 0); 2198789Sahrens } 2199789Sahrens 22001356Seschrock typedef struct get_all_cbdata { 22011356Seschrock zfs_handle_t **cb_handles; 22021356Seschrock size_t cb_alloc; 22031356Seschrock size_t cb_used; 22043126Sahl uint_t cb_types; 22051356Seschrock } get_all_cbdata_t; 22061356Seschrock 22071356Seschrock static int 22083126Sahl get_one_dataset(zfs_handle_t *zhp, void *data) 22091356Seschrock { 22101356Seschrock get_all_cbdata_t *cbp = data; 22113126Sahl zfs_type_t type = zfs_get_type(zhp); 22121356Seschrock 22131356Seschrock /* 22143126Sahl * Interate over any nested datasets. 22151356Seschrock */ 22163126Sahl if (type == ZFS_TYPE_FILESYSTEM && 22173126Sahl zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) { 22183265Sahrens zfs_close(zhp); 22193126Sahl return (1); 22203126Sahl } 22213126Sahl 22223126Sahl /* 22233126Sahl * Skip any datasets whose type does not match. 22243126Sahl */ 22253126Sahl if ((type & cbp->cb_types) == 0) { 22261356Seschrock zfs_close(zhp); 22271356Seschrock return (0); 22281356Seschrock } 22291356Seschrock 22301356Seschrock if (cbp->cb_alloc == cbp->cb_used) { 22311356Seschrock zfs_handle_t **handles; 22321356Seschrock 22331356Seschrock if (cbp->cb_alloc == 0) 22341356Seschrock cbp->cb_alloc = 64; 22351356Seschrock else 22361356Seschrock cbp->cb_alloc *= 2; 22371356Seschrock 22381356Seschrock handles = safe_malloc(cbp->cb_alloc * sizeof (void *)); 22391356Seschrock 22401356Seschrock if (cbp->cb_handles) { 22411356Seschrock bcopy(cbp->cb_handles, handles, 22421356Seschrock cbp->cb_used * sizeof (void *)); 22431356Seschrock free(cbp->cb_handles); 22441356Seschrock } 22451356Seschrock 22461356Seschrock cbp->cb_handles = handles; 22471356Seschrock } 22481356Seschrock 22491356Seschrock cbp->cb_handles[cbp->cb_used++] = zhp; 22501356Seschrock 22513126Sahl return (0); 22521356Seschrock } 22531356Seschrock 22541356Seschrock static void 22553126Sahl get_all_datasets(uint_t types, zfs_handle_t ***dslist, size_t *count) 22561356Seschrock { 22571356Seschrock get_all_cbdata_t cb = { 0 }; 22583126Sahl cb.cb_types = types; 22593126Sahl 22603126Sahl (void) zfs_iter_root(g_zfs, get_one_dataset, &cb); 22613126Sahl 22623126Sahl *dslist = cb.cb_handles; 22631356Seschrock *count = cb.cb_used; 22641356Seschrock } 22651356Seschrock 22661356Seschrock static int 22673126Sahl dataset_cmp(const void *a, const void *b) 22681356Seschrock { 22691356Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 22701356Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 22711356Seschrock char mounta[MAXPATHLEN]; 22721356Seschrock char mountb[MAXPATHLEN]; 22733126Sahl boolean_t gota, gotb; 22743126Sahl 22753126Sahl if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0) 22763126Sahl verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 22773126Sahl sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 22783126Sahl if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0) 22793126Sahl verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 22803126Sahl sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 22813126Sahl 22823126Sahl if (gota && gotb) 22833126Sahl return (strcmp(mounta, mountb)); 22843126Sahl 22853126Sahl if (gota) 22863126Sahl return (-1); 22873126Sahl if (gotb) 22883126Sahl return (1); 22893126Sahl 22903126Sahl return (strcmp(zfs_get_name(a), zfs_get_name(b))); 22911356Seschrock } 2292789Sahrens 2293789Sahrens /* 2294789Sahrens * Generic callback for sharing or mounting filesystems. Because the code is so 2295789Sahrens * similar, we have a common function with an extra parameter to determine which 2296789Sahrens * mode we are using. 2297789Sahrens */ 2298789Sahrens #define OP_SHARE 0x1 2299789Sahrens #define OP_MOUNT 0x2 2300789Sahrens 2301789Sahrens /* 23023126Sahl * Share or mount a dataset. 2303789Sahrens */ 2304789Sahrens static int 23053126Sahl share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit, 23063126Sahl const char *options) 2307789Sahrens { 2308789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2309789Sahrens char shareopts[ZFS_MAXPROPLEN]; 23103126Sahl const char *cmdname = op == OP_SHARE ? "share" : "mount"; 2311789Sahrens struct mnttab mnt; 23122676Seschrock uint64_t zoned, canmount; 23133126Sahl zfs_type_t type = zfs_get_type(zhp); 23143126Sahl 23153126Sahl assert(type & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)); 23163126Sahl 23173126Sahl if (type == ZFS_TYPE_FILESYSTEM) { 23183126Sahl /* 23193126Sahl * Check to make sure we can mount/share this dataset. If we 23203126Sahl * are in the global zone and the filesystem is exported to a 23213126Sahl * local zone, or if we are in a local zone and the 23223126Sahl * filesystem is not exported, then it is an error. 23233126Sahl */ 23243126Sahl zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 23253126Sahl 23263126Sahl if (zoned && getzoneid() == GLOBAL_ZONEID) { 23273126Sahl if (!explicit) 23283126Sahl return (0); 23293126Sahl 23303126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 23313126Sahl "dataset is exported to a local zone\n"), cmdname, 23323126Sahl zfs_get_name(zhp)); 23333126Sahl return (1); 23343126Sahl 23353126Sahl } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 23363126Sahl if (!explicit) 23373126Sahl return (0); 23383126Sahl 23393126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 23403126Sahl "permission denied\n"), cmdname, 23413126Sahl zfs_get_name(zhp)); 23423126Sahl return (1); 23433126Sahl } 23443126Sahl 23453126Sahl /* 23463126Sahl * Ignore any filesystems which don't apply to us. This 23473126Sahl * includes those with a legacy mountpoint, or those with 23483126Sahl * legacy share options. 23493126Sahl */ 23503126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 23513126Sahl sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); 23523126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 23533126Sahl sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 23543126Sahl canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); 23553126Sahl 23563126Sahl if (op == OP_SHARE && strcmp(shareopts, "off") == 0) { 23573126Sahl if (!explicit) 2358789Sahrens return (0); 2359789Sahrens 2360789Sahrens (void) fprintf(stderr, gettext("cannot share '%s': " 2361789Sahrens "legacy share\n"), zfs_get_name(zhp)); 2362789Sahrens (void) fprintf(stderr, gettext("use share(1M) to " 2363789Sahrens "share this filesystem\n")); 2364789Sahrens return (1); 2365789Sahrens } 23663126Sahl 23673126Sahl /* 23683126Sahl * We cannot share or mount legacy filesystems. If the 23693126Sahl * shareopts is non-legacy but the mountpoint is legacy, we 23703126Sahl * treat it as a legacy share. 23713126Sahl */ 23723126Sahl if (strcmp(mountpoint, "legacy") == 0) { 23733126Sahl if (!explicit) 23743126Sahl return (0); 23753126Sahl 23763126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 23773126Sahl "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 23783126Sahl (void) fprintf(stderr, gettext("use %s to " 23793126Sahl "%s this filesystem\n"), op == OP_SHARE ? 23803126Sahl "share(1M)" : "mount(1M)", cmdname); 23813126Sahl return (1); 23823126Sahl } 23833126Sahl 23843126Sahl if (strcmp(mountpoint, "none") == 0) { 23853126Sahl if (!explicit) 23863126Sahl return (0); 23873126Sahl 23883126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': no " 23893126Sahl "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 23903126Sahl return (1); 23913126Sahl } 23923126Sahl 23933126Sahl if (!canmount) { 23943126Sahl if (!explicit) 23953126Sahl return (0); 23963126Sahl 23973126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 23983126Sahl "'canmount' property is set to 'off'\n"), cmdname, 23993126Sahl zfs_get_name(zhp)); 24003126Sahl return (1); 24013126Sahl } 24023126Sahl 24033126Sahl /* 24043126Sahl * At this point, we have verified that the mountpoint and/or 24053126Sahl * shareopts are appropriate for auto management. If the 24063126Sahl * filesystem is already mounted or shared, return (failing 24073126Sahl * for explicit requests); otherwise mount or share the 24083126Sahl * filesystem. 24093126Sahl */ 24103126Sahl switch (op) { 24113126Sahl case OP_SHARE: 24123126Sahl if (zfs_is_shared_nfs(zhp, NULL)) { 24133126Sahl if (!explicit) 24143126Sahl return (0); 24153126Sahl 2416789Sahrens (void) fprintf(stderr, gettext("cannot share " 2417789Sahrens "'%s': filesystem already shared\n"), 2418789Sahrens zfs_get_name(zhp)); 2419789Sahrens return (1); 2420789Sahrens } 24213126Sahl 24223126Sahl if (!zfs_is_mounted(zhp, NULL) && 24233126Sahl zfs_mount(zhp, NULL, 0) != 0) 24243126Sahl return (1); 24253126Sahl 24263126Sahl if (zfs_share_nfs(zhp) != 0) 24273126Sahl return (1); 24283126Sahl break; 24293126Sahl 24303126Sahl case OP_MOUNT: 24313126Sahl if (options == NULL) 24323126Sahl mnt.mnt_mntopts = ""; 24333126Sahl else 24343126Sahl mnt.mnt_mntopts = (char *)options; 24353126Sahl 24363126Sahl if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 24373126Sahl zfs_is_mounted(zhp, NULL)) { 24383126Sahl if (!explicit) 24393126Sahl return (0); 24403126Sahl 2441789Sahrens (void) fprintf(stderr, gettext("cannot mount " 2442789Sahrens "'%s': filesystem already mounted\n"), 2443789Sahrens zfs_get_name(zhp)); 2444789Sahrens return (1); 2445789Sahrens } 24463126Sahl 24473126Sahl if (zfs_mount(zhp, options, flags) != 0) 24483126Sahl return (1); 24493126Sahl break; 2450789Sahrens } 24513126Sahl } else { 24523126Sahl assert(op == OP_SHARE); 24533126Sahl 24543126Sahl /* 24553126Sahl * Ignore any volumes that aren't shared. 24563126Sahl */ 24573126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, shareopts, 24583126Sahl sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 24593126Sahl 24603126Sahl if (strcmp(shareopts, "off") == 0) { 24613126Sahl if (!explicit) 24623126Sahl return (0); 24633126Sahl 24643126Sahl (void) fprintf(stderr, gettext("cannot share '%s': " 24653126Sahl "'shareiscsi' property not set\n"), 24663126Sahl zfs_get_name(zhp)); 24673126Sahl (void) fprintf(stderr, gettext("set 'shareiscsi' " 24683126Sahl "property or use iscsitadm(1M) to share this " 24693126Sahl "volume\n")); 24703126Sahl return (1); 2471789Sahrens } 24723126Sahl 24733126Sahl if (zfs_is_shared_iscsi(zhp)) { 24743126Sahl if (!explicit) 24753126Sahl return (0); 24763126Sahl 24773126Sahl (void) fprintf(stderr, gettext("cannot share " 24783126Sahl "'%s': volume already shared\n"), 24793126Sahl zfs_get_name(zhp)); 2480789Sahrens return (1); 24813126Sahl } 24823126Sahl 24833126Sahl if (zfs_share_iscsi(zhp) != 0) 24843126Sahl return (1); 2485789Sahrens } 2486789Sahrens 2487789Sahrens return (0); 2488789Sahrens } 2489789Sahrens 2490789Sahrens static int 24913126Sahl share_mount(int op, int argc, char **argv) 2492789Sahrens { 2493789Sahrens int do_all = 0; 24942372Slling int c, ret = 0; 24953126Sahl const char *options = NULL; 24963126Sahl int types, flags = 0; 2497789Sahrens 2498789Sahrens /* check options */ 24993126Sahl while ((c = getopt(argc, argv, op == OP_MOUNT ? ":ao:O" : "a")) 2500789Sahrens != -1) { 2501789Sahrens switch (c) { 2502789Sahrens case 'a': 2503789Sahrens do_all = 1; 2504789Sahrens break; 2505789Sahrens case 'o': 25063126Sahl options = optarg; 2507789Sahrens break; 2508789Sahrens case 'O': 25093126Sahl flags |= MS_OVERLAY; 2510789Sahrens break; 2511789Sahrens case ':': 2512789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2513789Sahrens "'%c' option\n"), optopt); 25142082Seschrock usage(B_FALSE); 2515789Sahrens break; 2516789Sahrens case '?': 2517789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2518789Sahrens optopt); 25192082Seschrock usage(B_FALSE); 2520789Sahrens } 2521789Sahrens } 2522789Sahrens 2523789Sahrens argc -= optind; 2524789Sahrens argv += optind; 2525789Sahrens 2526789Sahrens /* check number of arguments */ 2527789Sahrens if (do_all) { 25283126Sahl zfs_handle_t **dslist = NULL; 25291356Seschrock size_t i, count = 0; 25301356Seschrock 25313126Sahl if (op == OP_MOUNT) { 25323126Sahl types = ZFS_TYPE_FILESYSTEM; 25333126Sahl } else if (argc > 0) { 25343126Sahl if (strcmp(argv[0], "nfs") == 0) { 25353126Sahl types = ZFS_TYPE_FILESYSTEM; 25363126Sahl } else if (strcmp(argv[0], "iscsi") == 0) { 25373126Sahl types = ZFS_TYPE_VOLUME; 25383126Sahl } else { 25393126Sahl (void) fprintf(stderr, gettext("share type " 25403126Sahl "must be 'nfs' or 'iscsi'\n")); 25413126Sahl usage(B_FALSE); 25423126Sahl } 25433126Sahl 25443126Sahl argc--; 25453126Sahl argv++; 25463126Sahl } else { 25473126Sahl types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME; 25483126Sahl } 25493126Sahl 2550789Sahrens if (argc != 0) { 2551789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 25522082Seschrock usage(B_FALSE); 2553789Sahrens } 2554789Sahrens 25553126Sahl get_all_datasets(types, &dslist, &count); 25561356Seschrock 25571356Seschrock if (count == 0) 25581356Seschrock return (0); 25591356Seschrock 25603126Sahl qsort(dslist, count, sizeof (void *), dataset_cmp); 25611356Seschrock 25621356Seschrock for (i = 0; i < count; i++) { 25633126Sahl if (share_mount_one(dslist[i], op, flags, B_FALSE, 25643126Sahl options) != 0) 25652369Slling ret = 1; 25663126Sahl zfs_close(dslist[i]); 25671356Seschrock } 25681356Seschrock 25693126Sahl free(dslist); 2570789Sahrens } else if (argc == 0) { 2571789Sahrens struct mnttab entry; 2572789Sahrens 25733126Sahl if (op == OP_SHARE) { 2574789Sahrens (void) fprintf(stderr, gettext("missing filesystem " 2575789Sahrens "argument\n")); 25762082Seschrock usage(B_FALSE); 2577789Sahrens } 2578789Sahrens 2579789Sahrens /* 2580789Sahrens * When mount is given no arguments, go through /etc/mnttab and 2581789Sahrens * display any active ZFS mounts. We hide any snapshots, since 2582789Sahrens * they are controlled automatically. 2583789Sahrens */ 2584789Sahrens rewind(mnttab_file); 2585789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2586789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 2587789Sahrens strchr(entry.mnt_special, '@') != NULL) 2588789Sahrens continue; 2589789Sahrens 2590789Sahrens (void) printf("%-30s %s\n", entry.mnt_special, 2591789Sahrens entry.mnt_mountp); 2592789Sahrens } 2593789Sahrens 2594789Sahrens } else { 2595789Sahrens zfs_handle_t *zhp; 2596789Sahrens 25973126Sahl types = ZFS_TYPE_FILESYSTEM; 25983126Sahl if (op == OP_SHARE) 25993126Sahl types |= ZFS_TYPE_VOLUME; 26003126Sahl 2601789Sahrens if (argc > 1) { 2602789Sahrens (void) fprintf(stderr, 2603789Sahrens gettext("too many arguments\n")); 26042082Seschrock usage(B_FALSE); 2605789Sahrens } 2606789Sahrens 26073126Sahl if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) { 2608789Sahrens ret = 1; 26093126Sahl } else { 26103126Sahl ret = share_mount_one(zhp, op, flags, B_TRUE, 26113126Sahl options); 2612789Sahrens zfs_close(zhp); 2613789Sahrens } 2614789Sahrens } 2615789Sahrens 2616789Sahrens return (ret); 2617789Sahrens } 2618789Sahrens 2619789Sahrens /* 26203126Sahl * zfs mount -a [nfs | iscsi] 2621789Sahrens * zfs mount filesystem 2622789Sahrens * 2623789Sahrens * Mount all filesystems, or mount the given filesystem. 2624789Sahrens */ 2625789Sahrens static int 2626789Sahrens zfs_do_mount(int argc, char **argv) 2627789Sahrens { 26283126Sahl return (share_mount(OP_MOUNT, argc, argv)); 2629789Sahrens } 2630789Sahrens 2631789Sahrens /* 26323126Sahl * zfs share -a [nfs | iscsi] 2633789Sahrens * zfs share filesystem 2634789Sahrens * 2635789Sahrens * Share all filesystems, or share the given filesystem. 2636789Sahrens */ 2637789Sahrens static int 2638789Sahrens zfs_do_share(int argc, char **argv) 2639789Sahrens { 26403126Sahl return (share_mount(OP_SHARE, argc, argv)); 2641789Sahrens } 2642789Sahrens 2643789Sahrens typedef struct unshare_unmount_node { 2644789Sahrens zfs_handle_t *un_zhp; 2645789Sahrens char *un_mountp; 2646789Sahrens uu_avl_node_t un_avlnode; 2647789Sahrens } unshare_unmount_node_t; 2648789Sahrens 2649789Sahrens /* ARGSUSED */ 2650789Sahrens static int 2651789Sahrens unshare_unmount_compare(const void *larg, const void *rarg, void *unused) 2652789Sahrens { 2653789Sahrens const unshare_unmount_node_t *l = larg; 2654789Sahrens const unshare_unmount_node_t *r = rarg; 2655789Sahrens 2656789Sahrens return (strcmp(l->un_mountp, r->un_mountp)); 2657789Sahrens } 2658789Sahrens 2659789Sahrens /* 2660789Sahrens * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 2661789Sahrens * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 2662789Sahrens * and unmount it appropriately. 2663789Sahrens */ 2664789Sahrens static int 26653126Sahl unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) 2666789Sahrens { 2667789Sahrens zfs_handle_t *zhp; 2668789Sahrens int ret; 2669789Sahrens struct stat64 statbuf; 2670789Sahrens struct extmnttab entry; 26713126Sahl const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount"; 2672789Sahrens char property[ZFS_MAXPROPLEN]; 2673789Sahrens 2674789Sahrens /* 2675789Sahrens * Search for the path in /etc/mnttab. Rather than looking for the 2676789Sahrens * specific path, which can be fooled by non-standard paths (i.e. ".." 2677789Sahrens * or "//"), we stat() the path and search for the corresponding 2678789Sahrens * (major,minor) device pair. 2679789Sahrens */ 2680789Sahrens if (stat64(path, &statbuf) != 0) { 2681789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 2682789Sahrens cmdname, path, strerror(errno)); 2683789Sahrens return (1); 2684789Sahrens } 2685789Sahrens 2686789Sahrens /* 2687789Sahrens * Search for the given (major,minor) pair in the mount table. 2688789Sahrens */ 2689789Sahrens rewind(mnttab_file); 2690789Sahrens while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 2691789Sahrens if (entry.mnt_major == major(statbuf.st_dev) && 2692789Sahrens entry.mnt_minor == minor(statbuf.st_dev)) 2693789Sahrens break; 2694789Sahrens } 2695789Sahrens if (ret != 0) { 2696789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not " 2697789Sahrens "currently mounted\n"), cmdname, path); 2698789Sahrens return (1); 2699789Sahrens } 2700789Sahrens 2701789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 2702789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 2703789Sahrens "filesystem\n"), cmdname, path); 2704789Sahrens return (1); 2705789Sahrens } 2706789Sahrens 27072082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 27082082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2709789Sahrens return (1); 2710789Sahrens 27113126Sahl verify(zfs_prop_get(zhp, op == OP_SHARE ? 2712789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 27132082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2714789Sahrens 27153126Sahl if (op == OP_SHARE) { 2716789Sahrens if (strcmp(property, "off") == 0) { 2717789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2718789Sahrens "'%s': legacy share\n"), path); 2719789Sahrens (void) fprintf(stderr, gettext("use " 2720789Sahrens "unshare(1M) to unshare this filesystem\n")); 2721789Sahrens ret = 1; 27223126Sahl } else if (!zfs_is_shared_nfs(zhp, NULL)) { 2723789Sahrens (void) fprintf(stderr, gettext("cannot unshare '%s': " 2724789Sahrens "not currently shared\n"), path); 2725789Sahrens ret = 1; 2726789Sahrens } else { 27273126Sahl ret = zfs_unshareall_nfs(zhp); 2728789Sahrens } 2729789Sahrens } else { 27301264Slling if (is_manual) { 27311264Slling ret = zfs_unmount(zhp, NULL, flags); 27321264Slling } else if (strcmp(property, "legacy") == 0) { 27331264Slling (void) fprintf(stderr, gettext("cannot unmount " 27341264Slling "'%s': legacy mountpoint\n"), 27351264Slling zfs_get_name(zhp)); 27361264Slling (void) fprintf(stderr, gettext("use umount(1M) " 27371264Slling "to unmount this filesystem\n")); 27381264Slling ret = 1; 2739789Sahrens } else { 2740789Sahrens ret = zfs_unmountall(zhp, flags); 2741789Sahrens } 2742789Sahrens } 2743789Sahrens 2744789Sahrens zfs_close(zhp); 2745789Sahrens 2746789Sahrens return (ret != 0); 2747789Sahrens } 2748789Sahrens 2749789Sahrens /* 2750789Sahrens * Generic callback for unsharing or unmounting a filesystem. 2751789Sahrens */ 2752789Sahrens static int 27533126Sahl unshare_unmount(int op, int argc, char **argv) 2754789Sahrens { 2755789Sahrens int do_all = 0; 2756789Sahrens int flags = 0; 2757789Sahrens int ret = 0; 27583126Sahl int types, c; 2759789Sahrens zfs_handle_t *zhp; 2760789Sahrens char property[ZFS_MAXPROPLEN]; 2761789Sahrens 2762789Sahrens /* check options */ 27633126Sahl while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) { 2764789Sahrens switch (c) { 2765789Sahrens case 'a': 2766789Sahrens do_all = 1; 2767789Sahrens break; 2768789Sahrens case 'f': 2769789Sahrens flags = MS_FORCE; 2770789Sahrens break; 2771789Sahrens case '?': 2772789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2773789Sahrens optopt); 27742082Seschrock usage(B_FALSE); 2775789Sahrens } 2776789Sahrens } 2777789Sahrens 2778789Sahrens argc -= optind; 2779789Sahrens argv += optind; 2780789Sahrens 2781789Sahrens if (do_all) { 2782789Sahrens /* 2783789Sahrens * We could make use of zfs_for_each() to walk all datasets in 2784789Sahrens * the system, but this would be very inefficient, especially 2785789Sahrens * since we would have to linearly search /etc/mnttab for each 2786789Sahrens * one. Instead, do one pass through /etc/mnttab looking for 2787789Sahrens * zfs entries and call zfs_unmount() for each one. 2788789Sahrens * 2789789Sahrens * Things get a little tricky if the administrator has created 2790789Sahrens * mountpoints beneath other ZFS filesystems. In this case, we 2791789Sahrens * have to unmount the deepest filesystems first. To accomplish 2792789Sahrens * this, we place all the mountpoints in an AVL tree sorted by 2793789Sahrens * the special type (dataset name), and walk the result in 2794789Sahrens * reverse to make sure to get any snapshots first. 2795789Sahrens */ 2796789Sahrens struct mnttab entry; 2797789Sahrens uu_avl_pool_t *pool; 2798789Sahrens uu_avl_t *tree; 2799789Sahrens unshare_unmount_node_t *node; 2800789Sahrens uu_avl_index_t idx; 2801789Sahrens uu_avl_walk_t *walk; 2802789Sahrens 28033126Sahl if (argc != 0) { 28043126Sahl (void) fprintf(stderr, gettext("too many arguments\n")); 28053126Sahl usage(B_FALSE); 28063126Sahl } 28073126Sahl 2808789Sahrens if ((pool = uu_avl_pool_create("unmount_pool", 2809789Sahrens sizeof (unshare_unmount_node_t), 2810789Sahrens offsetof(unshare_unmount_node_t, un_avlnode), 2811789Sahrens unshare_unmount_compare, 2812789Sahrens UU_DEFAULT)) == NULL) { 2813789Sahrens (void) fprintf(stderr, gettext("internal error: " 2814789Sahrens "out of memory\n")); 2815789Sahrens exit(1); 2816789Sahrens } 2817789Sahrens 2818789Sahrens if ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL) { 2819789Sahrens (void) fprintf(stderr, gettext("internal error: " 2820789Sahrens "out of memory\n")); 2821789Sahrens exit(1); 2822789Sahrens } 2823789Sahrens 2824789Sahrens rewind(mnttab_file); 2825789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2826789Sahrens 2827789Sahrens /* ignore non-ZFS entries */ 2828789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 2829789Sahrens continue; 2830789Sahrens 2831789Sahrens /* ignore snapshots */ 2832789Sahrens if (strchr(entry.mnt_special, '@') != NULL) 2833789Sahrens continue; 2834789Sahrens 28352082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 2836789Sahrens ZFS_TYPE_FILESYSTEM)) == NULL) { 2837789Sahrens ret = 1; 2838789Sahrens continue; 2839789Sahrens } 2840789Sahrens 28413126Sahl verify(zfs_prop_get(zhp, op == OP_SHARE ? 2842789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 2843789Sahrens property, sizeof (property), NULL, NULL, 28442082Seschrock 0, B_FALSE) == 0); 2845789Sahrens 2846789Sahrens /* Ignore legacy mounts and shares */ 28473126Sahl if ((op == OP_SHARE && 2848789Sahrens strcmp(property, "off") == 0) || 28493126Sahl (op == OP_MOUNT && 2850789Sahrens strcmp(property, "legacy") == 0)) { 2851789Sahrens zfs_close(zhp); 2852789Sahrens continue; 2853789Sahrens } 2854789Sahrens 2855789Sahrens node = safe_malloc(sizeof (unshare_unmount_node_t)); 2856789Sahrens node->un_zhp = zhp; 2857789Sahrens 2858789Sahrens if ((node->un_mountp = strdup(entry.mnt_mountp)) == 2859789Sahrens NULL) { 2860789Sahrens (void) fprintf(stderr, gettext("internal error:" 2861789Sahrens " out of memory\n")); 2862789Sahrens exit(1); 2863789Sahrens } 2864789Sahrens 2865789Sahrens uu_avl_node_init(node, &node->un_avlnode, pool); 2866789Sahrens 2867789Sahrens if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 2868789Sahrens uu_avl_insert(tree, node, idx); 2869789Sahrens } else { 2870789Sahrens zfs_close(node->un_zhp); 2871789Sahrens free(node->un_mountp); 2872789Sahrens free(node); 2873789Sahrens } 2874789Sahrens } 2875789Sahrens 2876789Sahrens /* 2877789Sahrens * Walk the AVL tree in reverse, unmounting each filesystem and 2878789Sahrens * removing it from the AVL tree in the process. 2879789Sahrens */ 2880789Sahrens if ((walk = uu_avl_walk_start(tree, 2881789Sahrens UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) { 2882789Sahrens (void) fprintf(stderr, 2883789Sahrens gettext("internal error: out of memory")); 2884789Sahrens exit(1); 2885789Sahrens } 2886789Sahrens 2887789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 2888789Sahrens uu_avl_remove(tree, node); 2889789Sahrens 28903126Sahl switch (op) { 2891789Sahrens case OP_SHARE: 28923126Sahl if (zfs_unshare_nfs(node->un_zhp, 2893789Sahrens node->un_mountp) != 0) 2894789Sahrens ret = 1; 2895789Sahrens break; 2896789Sahrens 2897789Sahrens case OP_MOUNT: 2898789Sahrens if (zfs_unmount(node->un_zhp, 2899789Sahrens node->un_mountp, flags) != 0) 2900789Sahrens ret = 1; 2901789Sahrens break; 2902789Sahrens } 2903789Sahrens 2904789Sahrens zfs_close(node->un_zhp); 2905789Sahrens free(node->un_mountp); 2906789Sahrens free(node); 2907789Sahrens } 2908789Sahrens 2909789Sahrens uu_avl_walk_end(walk); 2910789Sahrens uu_avl_destroy(tree); 2911789Sahrens uu_avl_pool_destroy(pool); 29123126Sahl 29133126Sahl if (op == OP_SHARE) { 29143126Sahl /* 29153126Sahl * Finally, unshare any volumes shared via iSCSI. 29163126Sahl */ 29173126Sahl zfs_handle_t **dslist = NULL; 29183126Sahl size_t i, count = 0; 29193126Sahl 29203126Sahl get_all_datasets(ZFS_TYPE_VOLUME, &dslist, &count); 29213126Sahl 29223126Sahl if (count != 0) { 29233126Sahl qsort(dslist, count, sizeof (void *), 29243126Sahl dataset_cmp); 29253126Sahl 29263126Sahl for (i = 0; i < count; i++) { 29273126Sahl if (zfs_unshare_iscsi(dslist[i]) != 0) 29283126Sahl ret = 1; 29293126Sahl zfs_close(dslist[i]); 29303126Sahl } 29313126Sahl 29323126Sahl free(dslist); 29333126Sahl } 29343126Sahl } 2935789Sahrens } else { 29363126Sahl if (argc != 1) { 29373126Sahl if (argc == 0) 29383126Sahl (void) fprintf(stderr, 29393126Sahl gettext("missing filesystem argument\n")); 29403126Sahl else 29413126Sahl (void) fprintf(stderr, 29423126Sahl gettext("too many arguments\n")); 29433126Sahl usage(B_FALSE); 29443126Sahl } 29453126Sahl 2946789Sahrens /* 2947789Sahrens * We have an argument, but it may be a full path or a ZFS 2948789Sahrens * filesystem. Pass full paths off to unmount_path() (shared by 2949789Sahrens * manual_unmount), otherwise open the filesystem and pass to 2950789Sahrens * zfs_unmount(). 2951789Sahrens */ 2952789Sahrens if (argv[0][0] == '/') 29533126Sahl return (unshare_unmount_path(op, argv[0], 29542082Seschrock flags, B_FALSE)); 29552082Seschrock 29563126Sahl types = ZFS_TYPE_FILESYSTEM; 29573126Sahl if (op == OP_SHARE) 29583126Sahl types |= ZFS_TYPE_VOLUME; 29593126Sahl 29603126Sahl if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) 2961789Sahrens return (1); 2962789Sahrens 29633126Sahl if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 29643126Sahl verify(zfs_prop_get(zhp, op == OP_SHARE ? 29653126Sahl ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 29663126Sahl sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 29673126Sahl 29683126Sahl switch (op) { 29693126Sahl case OP_SHARE: 29703126Sahl if (strcmp(property, "off") == 0) { 29713126Sahl (void) fprintf(stderr, gettext("cannot " 29723126Sahl "unshare '%s': legacy share\n"), 29733126Sahl zfs_get_name(zhp)); 29743126Sahl (void) fprintf(stderr, gettext("use " 29753126Sahl "unshare(1M) to unshare this " 29763126Sahl "filesystem\n")); 29773126Sahl ret = 1; 29783126Sahl } else if (!zfs_is_shared_nfs(zhp, NULL)) { 29793126Sahl (void) fprintf(stderr, gettext("cannot " 29803126Sahl "unshare '%s': not currently " 29813126Sahl "shared\n"), zfs_get_name(zhp)); 29823126Sahl ret = 1; 29833126Sahl } else if (zfs_unshareall_nfs(zhp) != 0) { 29843126Sahl ret = 1; 29853126Sahl } 29863126Sahl break; 29873126Sahl 29883126Sahl case OP_MOUNT: 29893126Sahl if (strcmp(property, "legacy") == 0) { 29903126Sahl (void) fprintf(stderr, gettext("cannot " 29913126Sahl "unmount '%s': legacy " 29923126Sahl "mountpoint\n"), zfs_get_name(zhp)); 29933126Sahl (void) fprintf(stderr, gettext("use " 29943126Sahl "umount(1M) to unmount this " 29953126Sahl "filesystem\n")); 29963126Sahl ret = 1; 29973126Sahl } else if (!zfs_is_mounted(zhp, NULL)) { 29983126Sahl (void) fprintf(stderr, gettext("cannot " 29993126Sahl "unmount '%s': not currently " 30003126Sahl "mounted\n"), 30013126Sahl zfs_get_name(zhp)); 30023126Sahl ret = 1; 30033126Sahl } else if (zfs_unmountall(zhp, flags) != 0) { 30043126Sahl ret = 1; 30053126Sahl } 30063126Sahl break; 30073126Sahl } 30083126Sahl } else { 30093126Sahl assert(op == OP_SHARE); 30103126Sahl 30113126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, property, 30123126Sahl sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 30133126Sahl 3014789Sahrens if (strcmp(property, "off") == 0) { 3015789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 30163126Sahl "'%s': 'shareiscsi' property not set\n"), 30173126Sahl zfs_get_name(zhp)); 30183126Sahl (void) fprintf(stderr, gettext("set " 30193126Sahl "'shareiscsi' property or use " 30203126Sahl "iscsitadm(1M) to share this volume\n")); 3021789Sahrens ret = 1; 30223126Sahl } else if (!zfs_is_shared_iscsi(zhp)) { 30233126Sahl (void) fprintf(stderr, gettext("cannot " 30243126Sahl "unshare '%s': not currently shared\n"), 3025789Sahrens zfs_get_name(zhp)); 3026789Sahrens ret = 1; 30273126Sahl } else if (zfs_unshare_iscsi(zhp) != 0) { 3028789Sahrens ret = 1; 3029789Sahrens } 3030789Sahrens } 3031789Sahrens 3032789Sahrens zfs_close(zhp); 3033789Sahrens } 3034789Sahrens 3035789Sahrens return (ret); 3036789Sahrens } 3037789Sahrens 3038789Sahrens /* 3039789Sahrens * zfs unmount -a 3040789Sahrens * zfs unmount filesystem 3041789Sahrens * 3042789Sahrens * Unmount all filesystems, or a specific ZFS filesystem. 3043789Sahrens */ 3044789Sahrens static int 3045789Sahrens zfs_do_unmount(int argc, char **argv) 3046789Sahrens { 3047789Sahrens return (unshare_unmount(OP_MOUNT, argc, argv)); 3048789Sahrens } 3049789Sahrens 3050789Sahrens /* 3051789Sahrens * zfs unshare -a 3052789Sahrens * zfs unshare filesystem 3053789Sahrens * 3054789Sahrens * Unshare all filesystems, or a specific ZFS filesystem. 3055789Sahrens */ 3056789Sahrens static int 3057789Sahrens zfs_do_unshare(int argc, char **argv) 3058789Sahrens { 3059789Sahrens return (unshare_unmount(OP_SHARE, argc, argv)); 3060789Sahrens } 3061789Sahrens 3062789Sahrens /* 3063789Sahrens * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 3064789Sahrens * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 3065789Sahrens */ 3066789Sahrens static int 3067789Sahrens manual_mount(int argc, char **argv) 3068789Sahrens { 3069789Sahrens zfs_handle_t *zhp; 3070789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 3071789Sahrens char mntopts[MNT_LINE_MAX] = { '\0' }; 3072789Sahrens int ret; 3073789Sahrens int c; 3074789Sahrens int flags = 0; 3075789Sahrens char *dataset, *path; 3076789Sahrens 3077789Sahrens /* check options */ 30781544Seschrock while ((c = getopt(argc, argv, ":mo:O")) != -1) { 3079789Sahrens switch (c) { 3080789Sahrens case 'o': 3081789Sahrens (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 3082789Sahrens break; 3083789Sahrens case 'O': 3084789Sahrens flags |= MS_OVERLAY; 3085789Sahrens break; 30861544Seschrock case 'm': 30871544Seschrock flags |= MS_NOMNTTAB; 30881544Seschrock break; 3089789Sahrens case ':': 3090789Sahrens (void) fprintf(stderr, gettext("missing argument for " 3091789Sahrens "'%c' option\n"), optopt); 30922082Seschrock usage(B_FALSE); 3093789Sahrens break; 3094789Sahrens case '?': 3095789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3096789Sahrens optopt); 3097789Sahrens (void) fprintf(stderr, gettext("usage: mount [-o opts] " 3098789Sahrens "<path>\n")); 3099789Sahrens return (2); 3100789Sahrens } 3101789Sahrens } 3102789Sahrens 3103789Sahrens argc -= optind; 3104789Sahrens argv += optind; 3105789Sahrens 3106789Sahrens /* check that we only have two arguments */ 3107789Sahrens if (argc != 2) { 3108789Sahrens if (argc == 0) 3109789Sahrens (void) fprintf(stderr, gettext("missing dataset " 3110789Sahrens "argument\n")); 3111789Sahrens else if (argc == 1) 3112789Sahrens (void) fprintf(stderr, 3113789Sahrens gettext("missing mountpoint argument\n")); 3114789Sahrens else 3115789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 3116789Sahrens (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 3117789Sahrens return (2); 3118789Sahrens } 3119789Sahrens 3120789Sahrens dataset = argv[0]; 3121789Sahrens path = argv[1]; 3122789Sahrens 3123789Sahrens /* try to open the dataset */ 31242082Seschrock if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 3125789Sahrens return (1); 3126789Sahrens 3127789Sahrens (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 31282082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE); 3129789Sahrens 3130789Sahrens /* check for legacy mountpoint and complain appropriately */ 3131789Sahrens ret = 0; 3132789Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 3133789Sahrens if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS, 3134789Sahrens NULL, 0, mntopts, sizeof (mntopts)) != 0) { 3135789Sahrens (void) fprintf(stderr, gettext("mount failed: %s\n"), 3136789Sahrens strerror(errno)); 3137789Sahrens ret = 1; 3138789Sahrens } 3139789Sahrens } else { 3140789Sahrens (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 3141789Sahrens "mounted using 'mount -F zfs'\n"), dataset); 3142789Sahrens (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 3143789Sahrens "instead.\n"), path); 3144789Sahrens (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' " 3145789Sahrens "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n")); 3146789Sahrens (void) fprintf(stderr, gettext("See zfs(1M) for more " 3147789Sahrens "information.\n")); 3148789Sahrens ret = 1; 3149789Sahrens } 3150789Sahrens 3151789Sahrens return (ret); 3152789Sahrens } 3153789Sahrens 3154789Sahrens /* 3155789Sahrens * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 3156789Sahrens * unmounts of non-legacy filesystems, as this is the dominant administrative 3157789Sahrens * interface. 3158789Sahrens */ 3159789Sahrens static int 3160789Sahrens manual_unmount(int argc, char **argv) 3161789Sahrens { 3162789Sahrens int flags = 0; 3163789Sahrens int c; 3164789Sahrens 3165789Sahrens /* check options */ 3166789Sahrens while ((c = getopt(argc, argv, "f")) != -1) { 3167789Sahrens switch (c) { 3168789Sahrens case 'f': 3169789Sahrens flags = MS_FORCE; 3170789Sahrens break; 3171789Sahrens case '?': 3172789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3173789Sahrens optopt); 3174789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] " 3175789Sahrens "<path>\n")); 3176789Sahrens return (2); 3177789Sahrens } 3178789Sahrens } 3179789Sahrens 3180789Sahrens argc -= optind; 3181789Sahrens argv += optind; 3182789Sahrens 3183789Sahrens /* check arguments */ 3184789Sahrens if (argc != 1) { 3185789Sahrens if (argc == 0) 3186789Sahrens (void) fprintf(stderr, gettext("missing path " 3187789Sahrens "argument\n")); 3188789Sahrens else 3189789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 3190789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 3191789Sahrens return (2); 3192789Sahrens } 3193789Sahrens 31942082Seschrock return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE)); 3195789Sahrens } 3196789Sahrens 3197789Sahrens static int 3198789Sahrens volcheck(zpool_handle_t *zhp, void *data) 3199789Sahrens { 32002856Snd150628 boolean_t isinit = *((boolean_t *)data); 3201789Sahrens 3202789Sahrens if (isinit) 3203789Sahrens return (zpool_create_zvol_links(zhp)); 3204789Sahrens else 3205789Sahrens return (zpool_remove_zvol_links(zhp)); 3206789Sahrens } 3207789Sahrens 3208789Sahrens /* 3209789Sahrens * Iterate over all pools in the system and either create or destroy /dev/zvol 3210789Sahrens * links, depending on the value of 'isinit'. 3211789Sahrens */ 3212789Sahrens static int 32132082Seschrock do_volcheck(boolean_t isinit) 3214789Sahrens { 32152856Snd150628 return (zpool_iter(g_zfs, volcheck, &isinit) ? 1 : 0); 3216789Sahrens } 3217789Sahrens 3218789Sahrens int 3219789Sahrens main(int argc, char **argv) 3220789Sahrens { 3221789Sahrens int ret; 3222789Sahrens int i; 3223789Sahrens char *progname; 3224789Sahrens char *cmdname; 3225789Sahrens 3226789Sahrens (void) setlocale(LC_ALL, ""); 3227789Sahrens (void) textdomain(TEXT_DOMAIN); 3228789Sahrens 3229789Sahrens opterr = 0; 3230789Sahrens 32312082Seschrock if ((g_zfs = libzfs_init()) == NULL) { 32322082Seschrock (void) fprintf(stderr, gettext("internal error: failed to " 32332082Seschrock "initialize ZFS library\n")); 32342082Seschrock return (1); 32352082Seschrock } 32362082Seschrock 32372082Seschrock libzfs_print_on_error(g_zfs, B_TRUE); 32382082Seschrock 3239789Sahrens if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 3240789Sahrens (void) fprintf(stderr, gettext("internal error: unable to " 3241789Sahrens "open %s\n"), MNTTAB); 3242789Sahrens return (1); 3243789Sahrens } 3244789Sahrens 3245789Sahrens /* 3246789Sahrens * This command also doubles as the /etc/fs mount and unmount program. 3247789Sahrens * Determine if we should take this behavior based on argv[0]. 3248789Sahrens */ 3249789Sahrens progname = basename(argv[0]); 3250789Sahrens if (strcmp(progname, "mount") == 0) { 3251789Sahrens ret = manual_mount(argc, argv); 3252789Sahrens } else if (strcmp(progname, "umount") == 0) { 3253789Sahrens ret = manual_unmount(argc, argv); 3254789Sahrens } else { 3255789Sahrens /* 3256789Sahrens * Make sure the user has specified some command. 3257789Sahrens */ 3258789Sahrens if (argc < 2) { 3259789Sahrens (void) fprintf(stderr, gettext("missing command\n")); 32602082Seschrock usage(B_FALSE); 3261789Sahrens } 3262789Sahrens 3263789Sahrens cmdname = argv[1]; 3264789Sahrens 3265789Sahrens /* 3266789Sahrens * The 'umount' command is an alias for 'unmount' 3267789Sahrens */ 3268789Sahrens if (strcmp(cmdname, "umount") == 0) 3269789Sahrens cmdname = "unmount"; 3270789Sahrens 3271789Sahrens /* 32721749Sahrens * The 'recv' command is an alias for 'receive' 32731749Sahrens */ 32741749Sahrens if (strcmp(cmdname, "recv") == 0) 32751749Sahrens cmdname = "receive"; 32761749Sahrens 32771749Sahrens /* 3278789Sahrens * Special case '-?' 3279789Sahrens */ 3280789Sahrens if (strcmp(cmdname, "-?") == 0) 32812082Seschrock usage(B_TRUE); 3282789Sahrens 3283789Sahrens /* 3284789Sahrens * 'volinit' and 'volfini' do not appear in the usage message, 3285789Sahrens * so we have to special case them here. 3286789Sahrens */ 3287789Sahrens if (strcmp(cmdname, "volinit") == 0) 32882082Seschrock return (do_volcheck(B_TRUE)); 3289789Sahrens else if (strcmp(cmdname, "volfini") == 0) 32902082Seschrock return (do_volcheck(B_FALSE)); 3291789Sahrens 3292789Sahrens /* 3293789Sahrens * Run the appropriate command. 3294789Sahrens */ 3295789Sahrens for (i = 0; i < NCOMMAND; i++) { 3296789Sahrens if (command_table[i].name == NULL) 3297789Sahrens continue; 3298789Sahrens 3299789Sahrens if (strcmp(cmdname, command_table[i].name) == 0) { 3300789Sahrens current_command = &command_table[i]; 3301789Sahrens ret = command_table[i].func(argc - 1, argv + 1); 3302789Sahrens break; 3303789Sahrens } 3304789Sahrens } 3305789Sahrens 3306789Sahrens if (i == NCOMMAND) { 3307789Sahrens (void) fprintf(stderr, gettext("unrecognized " 3308789Sahrens "command '%s'\n"), cmdname); 33092082Seschrock usage(B_FALSE); 3310789Sahrens } 3311789Sahrens } 3312789Sahrens 3313789Sahrens (void) fclose(mnttab_file); 3314789Sahrens 33152082Seschrock libzfs_fini(g_zfs); 33162082Seschrock 3317789Sahrens /* 3318789Sahrens * The 'ZFS_ABORT' environment variable causes us to dump core on exit 3319789Sahrens * for the purposes of running ::findleaks. 3320789Sahrens */ 3321789Sahrens if (getenv("ZFS_ABORT") != NULL) { 3322789Sahrens (void) printf("dumping core by request\n"); 3323789Sahrens abort(); 3324789Sahrens } 3325789Sahrens 3326789Sahrens return (ret); 3327789Sahrens } 3328