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 */ 21*3126Sahl 22789Sahrens /* 231204Slling * Copyright 2006 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 * 81*3126Sahl _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; 589789Sahrens zfs_handle_t *cb_target; 5902199Sahrens char *cb_snapname; 591789Sahrens } destroy_cbdata_t; 592789Sahrens 593789Sahrens /* 594789Sahrens * Check for any dependents based on the '-r' or '-R' flags. 595789Sahrens */ 596789Sahrens static int 597789Sahrens destroy_check_dependent(zfs_handle_t *zhp, void *data) 598789Sahrens { 599789Sahrens destroy_cbdata_t *cbp = data; 600789Sahrens const char *tname = zfs_get_name(cbp->cb_target); 601789Sahrens const char *name = zfs_get_name(zhp); 602789Sahrens 603789Sahrens if (strncmp(tname, name, strlen(tname)) == 0 && 604789Sahrens (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { 605789Sahrens /* 606789Sahrens * This is a direct descendant, not a clone somewhere else in 607789Sahrens * the hierarchy. 608789Sahrens */ 609789Sahrens if (cbp->cb_recurse) 610789Sahrens goto out; 611789Sahrens 612789Sahrens if (cbp->cb_first) { 613789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 614789Sahrens "%s has children\n"), 615789Sahrens zfs_get_name(cbp->cb_target), 616789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 617789Sahrens (void) fprintf(stderr, gettext("use '-r' to destroy " 618789Sahrens "the following datasets:\n")); 6192082Seschrock cbp->cb_first = B_FALSE; 620789Sahrens cbp->cb_error = 1; 621789Sahrens } 622789Sahrens 623789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 624789Sahrens } else { 625789Sahrens /* 626789Sahrens * This is a clone. We only want to report this if the '-r' 627789Sahrens * wasn't specified, or the target is a snapshot. 628789Sahrens */ 629789Sahrens if (!cbp->cb_recurse && 630789Sahrens zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) 631789Sahrens goto out; 632789Sahrens 633789Sahrens if (cbp->cb_first) { 634789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 635789Sahrens "%s has dependent clones\n"), 636789Sahrens zfs_get_name(cbp->cb_target), 637789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 638789Sahrens (void) fprintf(stderr, gettext("use '-R' to destroy " 639789Sahrens "the following datasets:\n")); 6402082Seschrock cbp->cb_first = B_FALSE; 641789Sahrens cbp->cb_error = 1; 642789Sahrens } 643789Sahrens 644789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 645789Sahrens } 646789Sahrens 647789Sahrens out: 648789Sahrens zfs_close(zhp); 649789Sahrens return (0); 650789Sahrens } 651789Sahrens 652789Sahrens static int 653789Sahrens destroy_callback(zfs_handle_t *zhp, void *data) 654789Sahrens { 655789Sahrens destroy_cbdata_t *cbp = data; 656789Sahrens 657789Sahrens /* 658789Sahrens * Ignore pools (which we've already flagged as an error before getting 659789Sahrens * here. 660789Sahrens */ 661789Sahrens if (strchr(zfs_get_name(zhp), '/') == NULL && 662789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 663789Sahrens zfs_close(zhp); 664789Sahrens return (0); 665789Sahrens } 666789Sahrens 667789Sahrens /* 668789Sahrens * Bail out on the first error. 669789Sahrens */ 670789Sahrens if (zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0 || 671789Sahrens zfs_destroy(zhp) != 0) { 672789Sahrens zfs_close(zhp); 673789Sahrens return (-1); 674789Sahrens } 675789Sahrens 676789Sahrens zfs_close(zhp); 677789Sahrens return (0); 678789Sahrens } 679789Sahrens 6802199Sahrens static int 6812199Sahrens destroy_snap_clones(zfs_handle_t *zhp, void *arg) 6822199Sahrens { 6832199Sahrens destroy_cbdata_t *cbp = arg; 6842199Sahrens char thissnap[MAXPATHLEN]; 6852199Sahrens zfs_handle_t *szhp; 6862199Sahrens 6872199Sahrens (void) snprintf(thissnap, sizeof (thissnap), 6882199Sahrens "%s@%s", zfs_get_name(zhp), cbp->cb_snapname); 6892199Sahrens 6902199Sahrens libzfs_print_on_error(g_zfs, B_FALSE); 6912199Sahrens szhp = zfs_open(g_zfs, thissnap, ZFS_TYPE_SNAPSHOT); 6922199Sahrens libzfs_print_on_error(g_zfs, B_TRUE); 6932199Sahrens if (szhp) { 6942199Sahrens /* 6952199Sahrens * Destroy any clones of this snapshot 6962199Sahrens */ 6972474Seschrock if (zfs_iter_dependents(szhp, B_FALSE, destroy_callback, 6982474Seschrock cbp) != 0) { 6992474Seschrock zfs_close(szhp); 7002474Seschrock return (-1); 7012474Seschrock } 7022199Sahrens zfs_close(szhp); 7032199Sahrens } 7042199Sahrens 7052199Sahrens return (zfs_iter_filesystems(zhp, destroy_snap_clones, arg)); 7062199Sahrens } 707789Sahrens 708789Sahrens static int 709789Sahrens zfs_do_destroy(int argc, char **argv) 710789Sahrens { 711789Sahrens destroy_cbdata_t cb = { 0 }; 712789Sahrens int c; 713789Sahrens zfs_handle_t *zhp; 7142199Sahrens char *cp; 715789Sahrens 716789Sahrens /* check options */ 717789Sahrens while ((c = getopt(argc, argv, "frR")) != -1) { 718789Sahrens switch (c) { 719789Sahrens case 'f': 720789Sahrens cb.cb_force = 1; 721789Sahrens break; 722789Sahrens case 'r': 723789Sahrens cb.cb_recurse = 1; 724789Sahrens break; 725789Sahrens case 'R': 726789Sahrens cb.cb_recurse = 1; 727789Sahrens cb.cb_doclones = 1; 728789Sahrens break; 729789Sahrens case '?': 730789Sahrens default: 731789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 732789Sahrens optopt); 7332082Seschrock usage(B_FALSE); 734789Sahrens } 735789Sahrens } 736789Sahrens 737789Sahrens argc -= optind; 738789Sahrens argv += optind; 739789Sahrens 740789Sahrens /* check number of arguments */ 741789Sahrens if (argc == 0) { 742789Sahrens (void) fprintf(stderr, gettext("missing path argument\n")); 7432082Seschrock usage(B_FALSE); 744789Sahrens } 745789Sahrens if (argc > 1) { 746789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 7472082Seschrock usage(B_FALSE); 748789Sahrens } 749789Sahrens 7502199Sahrens /* 7512199Sahrens * If we are doing recursive destroy of a snapshot, then the 7522199Sahrens * named snapshot may not exist. Go straight to libzfs. 7532199Sahrens */ 7542199Sahrens if (cb.cb_recurse && (cp = strchr(argv[0], '@'))) { 7552199Sahrens int ret; 7562199Sahrens 7572199Sahrens *cp = '\0'; 7582199Sahrens if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 7592199Sahrens return (1); 7602199Sahrens *cp = '@'; 7612199Sahrens cp++; 7622199Sahrens 7632199Sahrens if (cb.cb_doclones) { 7642199Sahrens cb.cb_snapname = cp; 7652474Seschrock if (destroy_snap_clones(zhp, &cb) != 0) { 7662474Seschrock zfs_close(zhp); 7672474Seschrock return (1); 7682474Seschrock } 7692199Sahrens } 7702199Sahrens 7712199Sahrens ret = zfs_destroy_snaps(zhp, cp); 7722199Sahrens zfs_close(zhp); 7732199Sahrens if (ret) { 7742199Sahrens (void) fprintf(stderr, 7752199Sahrens gettext("no snapshots destroyed\n")); 7762199Sahrens } 7772199Sahrens return (ret != 0); 7782199Sahrens } 7792199Sahrens 7802199Sahrens 781789Sahrens /* Open the given dataset */ 7822082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 783789Sahrens return (1); 784789Sahrens 785789Sahrens cb.cb_target = zhp; 786789Sahrens 787789Sahrens /* 788789Sahrens * Perform an explicit check for pools before going any further. 789789Sahrens */ 790789Sahrens if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 791789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 792789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 793789Sahrens "operation does not apply to pools\n"), 794789Sahrens zfs_get_name(zhp)); 795789Sahrens (void) fprintf(stderr, gettext("use 'zfs destroy -r " 796789Sahrens "%s' to destroy all datasets in the pool\n"), 797789Sahrens zfs_get_name(zhp)); 798789Sahrens (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 799789Sahrens "to destroy the pool itself\n"), zfs_get_name(zhp)); 800789Sahrens zfs_close(zhp); 801789Sahrens return (1); 802789Sahrens } 803789Sahrens 804789Sahrens /* 805789Sahrens * Check for any dependents and/or clones. 806789Sahrens */ 8072082Seschrock cb.cb_first = B_TRUE; 8082474Seschrock if (!cb.cb_doclones && 8092474Seschrock zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent, 8102474Seschrock &cb) != 0) { 8112474Seschrock zfs_close(zhp); 8122474Seschrock return (1); 8132474Seschrock } 8142474Seschrock 8152474Seschrock 8162474Seschrock if (cb.cb_error || 8172474Seschrock zfs_iter_dependents(zhp, B_FALSE, destroy_callback, &cb) != 0) { 818789Sahrens zfs_close(zhp); 819789Sahrens return (1); 820789Sahrens } 821789Sahrens 822789Sahrens /* 8232474Seschrock * Do the real thing. The callback will close the handle regardless of 8242474Seschrock * whether it succeeds or not. 825789Sahrens */ 8262474Seschrock if (destroy_callback(zhp, &cb) != 0) 8272474Seschrock return (1); 8282474Seschrock 8292926Sek110237 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0], 8302926Sek110237 B_FALSE, B_FALSE); 8312926Sek110237 8322474Seschrock return (0); 833789Sahrens } 834789Sahrens 835789Sahrens /* 836866Seschrock * zfs get [-rHp] [-o field[,field]...] [-s source[,source]...] 837866Seschrock * < all | property[,property]... > < fs | snap | vol > ... 838789Sahrens * 839789Sahrens * -r recurse over any child datasets 840789Sahrens * -H scripted mode. Headers are stripped, and fields are separated 841789Sahrens * by tabs instead of spaces. 842789Sahrens * -o Set of fields to display. One of "name,property,value,source". 843789Sahrens * Default is all four. 844789Sahrens * -s Set of sources to allow. One of 845789Sahrens * "local,default,inherited,temporary,none". Default is all 846789Sahrens * five. 847789Sahrens * -p Display values in parsable (literal) format. 848789Sahrens * 849789Sahrens * Prints properties for the given datasets. The user can control which 850789Sahrens * columns to display as well as which property types to allow. 851789Sahrens */ 852789Sahrens typedef struct get_cbdata { 853789Sahrens int cb_sources; 854789Sahrens int cb_columns[4]; 8552676Seschrock int cb_colwidths[5]; 8562082Seschrock boolean_t cb_scripted; 8572082Seschrock boolean_t cb_literal; 8582676Seschrock boolean_t cb_first; 8592676Seschrock zfs_proplist_t *cb_proplist; 860789Sahrens } get_cbdata_t; 861789Sahrens 862789Sahrens #define GET_COL_NAME 1 863789Sahrens #define GET_COL_PROPERTY 2 864789Sahrens #define GET_COL_VALUE 3 865789Sahrens #define GET_COL_SOURCE 4 866789Sahrens 867789Sahrens /* 8682676Seschrock * Print the column headers for 'zfs get'. 8692676Seschrock */ 8702676Seschrock static void 8712676Seschrock print_get_headers(get_cbdata_t *cbp) 8722676Seschrock { 8732676Seschrock zfs_proplist_t *pl = cbp->cb_proplist; 8742676Seschrock int i; 8752676Seschrock char *title; 8762676Seschrock size_t len; 8772676Seschrock 8782676Seschrock cbp->cb_first = B_FALSE; 8792676Seschrock if (cbp->cb_scripted) 8802676Seschrock return; 8812676Seschrock 8822676Seschrock /* 8832676Seschrock * Start with the length of the column headers. 8842676Seschrock */ 8852676Seschrock cbp->cb_colwidths[GET_COL_NAME] = strlen(gettext("NAME")); 8862676Seschrock cbp->cb_colwidths[GET_COL_PROPERTY] = strlen(gettext("PROPERTY")); 8872676Seschrock cbp->cb_colwidths[GET_COL_VALUE] = strlen(gettext("VALUE")); 8882676Seschrock cbp->cb_colwidths[GET_COL_SOURCE] = strlen(gettext("SOURCE")); 8892676Seschrock 8902676Seschrock /* 8912676Seschrock * Go through and calculate the widths for each column. For the 8922676Seschrock * 'source' column, we kludge it up by taking the worst-case scenario of 8932676Seschrock * inheriting from the longest name. This is acceptable because in the 8942676Seschrock * majority of cases 'SOURCE' is the last column displayed, and we don't 8952676Seschrock * use the width anyway. Note that the 'VALUE' column can be oversized, 8962676Seschrock * if the name of the property is much longer the any values we find. 8972676Seschrock */ 8982676Seschrock for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) { 8992676Seschrock /* 9002676Seschrock * 'PROPERTY' column 9012676Seschrock */ 9022676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 9032676Seschrock len = strlen(zfs_prop_to_name(pl->pl_prop)); 9042676Seschrock if (len > cbp->cb_colwidths[GET_COL_PROPERTY]) 9052676Seschrock cbp->cb_colwidths[GET_COL_PROPERTY] = len; 9062676Seschrock } else { 9072676Seschrock len = strlen(pl->pl_user_prop); 9082676Seschrock if (len > cbp->cb_colwidths[GET_COL_PROPERTY]) 9092676Seschrock cbp->cb_colwidths[GET_COL_PROPERTY] = len; 9102676Seschrock } 9112676Seschrock 9122676Seschrock /* 9132676Seschrock * 'VALUE' column 9142676Seschrock */ 9152676Seschrock if ((pl->pl_prop != ZFS_PROP_NAME || !pl->pl_all) && 9162676Seschrock pl->pl_width > cbp->cb_colwidths[GET_COL_VALUE]) 9172676Seschrock cbp->cb_colwidths[GET_COL_VALUE] = pl->pl_width; 9182676Seschrock 9192676Seschrock /* 9202676Seschrock * 'NAME' and 'SOURCE' columns 9212676Seschrock */ 9222676Seschrock if (pl->pl_prop == ZFS_PROP_NAME && 9232676Seschrock pl->pl_width > cbp->cb_colwidths[GET_COL_NAME]) { 9242676Seschrock cbp->cb_colwidths[GET_COL_NAME] = pl->pl_width; 9252676Seschrock cbp->cb_colwidths[GET_COL_SOURCE] = pl->pl_width + 9262676Seschrock strlen(gettext("inherited from")); 9272676Seschrock } 9282676Seschrock } 9292676Seschrock 9302676Seschrock /* 9312676Seschrock * Now go through and print the headers. 9322676Seschrock */ 9332676Seschrock for (i = 0; i < 4; i++) { 9342676Seschrock switch (cbp->cb_columns[i]) { 9352676Seschrock case GET_COL_NAME: 9362676Seschrock title = gettext("NAME"); 9372676Seschrock break; 9382676Seschrock case GET_COL_PROPERTY: 9392676Seschrock title = gettext("PROPERTY"); 9402676Seschrock break; 9412676Seschrock case GET_COL_VALUE: 9422676Seschrock title = gettext("VALUE"); 9432676Seschrock break; 9442676Seschrock case GET_COL_SOURCE: 9452676Seschrock title = gettext("SOURCE"); 9462676Seschrock break; 9472676Seschrock default: 9482676Seschrock title = NULL; 9492676Seschrock } 9502676Seschrock 9512676Seschrock if (title != NULL) { 9522676Seschrock if (i == 3 || cbp->cb_columns[i + 1] == 0) 9532676Seschrock (void) printf("%s", title); 9542676Seschrock else 9552676Seschrock (void) printf("%-*s ", 9562676Seschrock cbp->cb_colwidths[cbp->cb_columns[i]], 9572676Seschrock title); 9582676Seschrock } 9592676Seschrock } 9602676Seschrock (void) printf("\n"); 9612676Seschrock } 9622676Seschrock 9632676Seschrock /* 964789Sahrens * Display a single line of output, according to the settings in the callback 965789Sahrens * structure. 966789Sahrens */ 967789Sahrens static void 9682676Seschrock print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, const char *propname, 969789Sahrens const char *value, zfs_source_t sourcetype, const char *source) 970789Sahrens { 971789Sahrens int i; 972789Sahrens const char *str; 973789Sahrens char buf[128]; 974789Sahrens 975789Sahrens /* 976789Sahrens * Ignore those source types that the user has chosen to ignore. 977789Sahrens */ 978789Sahrens if ((sourcetype & cbp->cb_sources) == 0) 979789Sahrens return; 980789Sahrens 9812676Seschrock if (cbp->cb_first) 9822676Seschrock print_get_headers(cbp); 9832676Seschrock 984789Sahrens for (i = 0; i < 4; i++) { 985789Sahrens switch (cbp->cb_columns[i]) { 986789Sahrens case GET_COL_NAME: 987789Sahrens str = zfs_get_name(zhp); 988789Sahrens break; 989789Sahrens 990789Sahrens case GET_COL_PROPERTY: 9912676Seschrock str = propname; 992789Sahrens break; 993789Sahrens 994789Sahrens case GET_COL_VALUE: 995789Sahrens str = value; 996789Sahrens break; 997789Sahrens 998789Sahrens case GET_COL_SOURCE: 999789Sahrens switch (sourcetype) { 1000789Sahrens case ZFS_SRC_NONE: 1001789Sahrens str = "-"; 1002789Sahrens break; 1003789Sahrens 1004789Sahrens case ZFS_SRC_DEFAULT: 1005789Sahrens str = "default"; 1006789Sahrens break; 1007789Sahrens 1008789Sahrens case ZFS_SRC_LOCAL: 1009789Sahrens str = "local"; 1010789Sahrens break; 1011789Sahrens 1012789Sahrens case ZFS_SRC_TEMPORARY: 1013789Sahrens str = "temporary"; 1014789Sahrens break; 1015789Sahrens 1016789Sahrens case ZFS_SRC_INHERITED: 1017789Sahrens (void) snprintf(buf, sizeof (buf), 1018789Sahrens "inherited from %s", source); 1019789Sahrens str = buf; 1020789Sahrens break; 1021789Sahrens } 1022789Sahrens break; 1023789Sahrens 1024789Sahrens default: 1025789Sahrens continue; 1026789Sahrens } 1027789Sahrens 1028789Sahrens if (cbp->cb_columns[i + 1] == 0) 1029789Sahrens (void) printf("%s", str); 1030789Sahrens else if (cbp->cb_scripted) 1031789Sahrens (void) printf("%s\t", str); 1032789Sahrens else 10332676Seschrock (void) printf("%-*s ", 10342676Seschrock cbp->cb_colwidths[cbp->cb_columns[i]], 10352676Seschrock str); 1036789Sahrens 1037789Sahrens } 1038789Sahrens 1039789Sahrens (void) printf("\n"); 1040789Sahrens } 1041789Sahrens 1042789Sahrens /* 1043789Sahrens * Invoked to display the properties for a single dataset. 1044789Sahrens */ 1045789Sahrens static int 1046789Sahrens get_callback(zfs_handle_t *zhp, void *data) 1047789Sahrens { 1048789Sahrens char buf[ZFS_MAXPROPLEN]; 1049789Sahrens zfs_source_t sourcetype; 1050789Sahrens char source[ZFS_MAXNAMELEN]; 1051789Sahrens get_cbdata_t *cbp = data; 10522676Seschrock nvlist_t *userprop = zfs_get_user_props(zhp); 10532676Seschrock zfs_proplist_t *pl = cbp->cb_proplist; 10542676Seschrock nvlist_t *propval; 10552676Seschrock char *strval; 10562676Seschrock char *sourceval; 10572676Seschrock 10582676Seschrock for (; pl != NULL; pl = pl->pl_next) { 10592676Seschrock /* 10602676Seschrock * Skip the special fake placeholder. This will also skip over 10612676Seschrock * the name property when 'all' is specified. 10622676Seschrock */ 10632676Seschrock if (pl->pl_prop == ZFS_PROP_NAME && 10642676Seschrock pl == cbp->cb_proplist) 10652676Seschrock continue; 10662676Seschrock 10672676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 10682676Seschrock if (zfs_prop_get(zhp, pl->pl_prop, buf, 10692676Seschrock sizeof (buf), &sourcetype, source, 10702676Seschrock sizeof (source), 10712676Seschrock cbp->cb_literal) != 0) { 10722676Seschrock if (pl->pl_all) 10732676Seschrock continue; 10742676Seschrock sourcetype = ZFS_SRC_NONE; 10752676Seschrock (void) strlcpy(buf, "-", sizeof (buf)); 10762676Seschrock } 10772676Seschrock 10782676Seschrock print_one_property(zhp, cbp, 10792676Seschrock zfs_prop_to_name(pl->pl_prop), 10802676Seschrock buf, sourcetype, source); 10812676Seschrock } else { 10822676Seschrock if (nvlist_lookup_nvlist(userprop, 10832676Seschrock pl->pl_user_prop, &propval) != 0) { 10842676Seschrock if (pl->pl_all) 10852676Seschrock continue; 10862676Seschrock sourcetype = ZFS_SRC_NONE; 10872676Seschrock strval = "-"; 10882676Seschrock } else { 10892676Seschrock verify(nvlist_lookup_string(propval, 10902676Seschrock ZFS_PROP_VALUE, &strval) == 0); 10912676Seschrock verify(nvlist_lookup_string(propval, 10922676Seschrock ZFS_PROP_SOURCE, &sourceval) == 0); 10932676Seschrock 10942676Seschrock if (strcmp(sourceval, 10952676Seschrock zfs_get_name(zhp)) == 0) { 10962676Seschrock sourcetype = ZFS_SRC_LOCAL; 10972676Seschrock } else { 10982676Seschrock sourcetype = ZFS_SRC_INHERITED; 10992676Seschrock (void) strlcpy(source, 11002676Seschrock sourceval, sizeof (source)); 11012676Seschrock } 11022676Seschrock } 11032676Seschrock 11042676Seschrock print_one_property(zhp, cbp, 11052676Seschrock pl->pl_user_prop, strval, sourcetype, 11062676Seschrock source); 1107866Seschrock } 1108789Sahrens } 1109789Sahrens 1110789Sahrens return (0); 1111789Sahrens } 1112789Sahrens 1113789Sahrens static int 1114789Sahrens zfs_do_get(int argc, char **argv) 1115789Sahrens { 1116789Sahrens get_cbdata_t cb = { 0 }; 11172082Seschrock boolean_t recurse = B_FALSE; 11182676Seschrock int i, c; 11192676Seschrock char *value, *fields; 1120866Seschrock int ret; 11212676Seschrock zfs_proplist_t fake_name = { 0 }; 1122789Sahrens 1123789Sahrens /* 1124789Sahrens * Set up default columns and sources. 1125789Sahrens */ 1126789Sahrens cb.cb_sources = ZFS_SRC_ALL; 1127789Sahrens cb.cb_columns[0] = GET_COL_NAME; 1128789Sahrens cb.cb_columns[1] = GET_COL_PROPERTY; 1129789Sahrens cb.cb_columns[2] = GET_COL_VALUE; 1130789Sahrens cb.cb_columns[3] = GET_COL_SOURCE; 1131789Sahrens 1132789Sahrens /* check options */ 1133789Sahrens while ((c = getopt(argc, argv, ":o:s:rHp")) != -1) { 1134789Sahrens switch (c) { 1135789Sahrens case 'p': 11362082Seschrock cb.cb_literal = B_TRUE; 1137789Sahrens break; 1138789Sahrens case 'r': 11392082Seschrock recurse = B_TRUE; 1140789Sahrens break; 1141789Sahrens case 'H': 11422082Seschrock cb.cb_scripted = B_TRUE; 1143789Sahrens break; 1144789Sahrens case ':': 1145789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1146789Sahrens "'%c' option\n"), optopt); 11472082Seschrock usage(B_FALSE); 1148789Sahrens break; 1149789Sahrens case 'o': 1150789Sahrens /* 1151789Sahrens * Process the set of columns to display. We zero out 1152789Sahrens * the structure to give us a blank slate. 1153789Sahrens */ 1154789Sahrens bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 1155789Sahrens i = 0; 1156789Sahrens while (*optarg != '\0') { 1157789Sahrens static char *col_subopts[] = 1158789Sahrens { "name", "property", "value", "source", 1159789Sahrens NULL }; 1160789Sahrens 1161789Sahrens if (i == 4) { 1162789Sahrens (void) fprintf(stderr, gettext("too " 1163789Sahrens "many fields given to -o " 1164789Sahrens "option\n")); 11652082Seschrock usage(B_FALSE); 1166789Sahrens } 1167789Sahrens 1168789Sahrens switch (getsubopt(&optarg, col_subopts, 1169789Sahrens &value)) { 1170789Sahrens case 0: 1171789Sahrens cb.cb_columns[i++] = GET_COL_NAME; 1172789Sahrens break; 1173789Sahrens case 1: 1174789Sahrens cb.cb_columns[i++] = GET_COL_PROPERTY; 1175789Sahrens break; 1176789Sahrens case 2: 1177789Sahrens cb.cb_columns[i++] = GET_COL_VALUE; 1178789Sahrens break; 1179789Sahrens case 3: 1180789Sahrens cb.cb_columns[i++] = GET_COL_SOURCE; 1181789Sahrens break; 1182789Sahrens default: 1183789Sahrens (void) fprintf(stderr, 1184789Sahrens gettext("invalid column name " 1185789Sahrens "'%s'\n"), value); 11862082Seschrock usage(B_FALSE); 1187789Sahrens } 1188789Sahrens } 1189789Sahrens break; 1190789Sahrens 1191789Sahrens case 's': 1192789Sahrens cb.cb_sources = 0; 1193789Sahrens while (*optarg != '\0') { 1194789Sahrens static char *source_subopts[] = { 1195789Sahrens "local", "default", "inherited", 1196789Sahrens "temporary", "none", NULL }; 1197789Sahrens 1198789Sahrens switch (getsubopt(&optarg, source_subopts, 1199789Sahrens &value)) { 1200789Sahrens case 0: 1201789Sahrens cb.cb_sources |= ZFS_SRC_LOCAL; 1202789Sahrens break; 1203789Sahrens case 1: 1204789Sahrens cb.cb_sources |= ZFS_SRC_DEFAULT; 1205789Sahrens break; 1206789Sahrens case 2: 1207789Sahrens cb.cb_sources |= ZFS_SRC_INHERITED; 1208789Sahrens break; 1209789Sahrens case 3: 1210789Sahrens cb.cb_sources |= ZFS_SRC_TEMPORARY; 1211789Sahrens break; 1212789Sahrens case 4: 1213789Sahrens cb.cb_sources |= ZFS_SRC_NONE; 1214789Sahrens break; 1215789Sahrens default: 1216789Sahrens (void) fprintf(stderr, 1217789Sahrens gettext("invalid source " 1218789Sahrens "'%s'\n"), value); 12192082Seschrock usage(B_FALSE); 1220789Sahrens } 1221789Sahrens } 1222789Sahrens break; 1223789Sahrens 1224789Sahrens case '?': 1225789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1226789Sahrens optopt); 12272082Seschrock usage(B_FALSE); 1228789Sahrens } 1229789Sahrens } 1230789Sahrens 1231789Sahrens argc -= optind; 1232789Sahrens argv += optind; 1233789Sahrens 1234789Sahrens if (argc < 1) { 1235789Sahrens (void) fprintf(stderr, gettext("missing property " 1236789Sahrens "argument\n")); 12372082Seschrock usage(B_FALSE); 1238789Sahrens } 1239789Sahrens 1240789Sahrens fields = argv[0]; 1241789Sahrens 12422676Seschrock if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0) 12432082Seschrock usage(B_FALSE); 1244789Sahrens 1245789Sahrens argc--; 1246789Sahrens argv++; 1247789Sahrens 1248789Sahrens /* 12492676Seschrock * As part of zfs_expand_proplist(), we keep track of the maximum column 12502676Seschrock * width for each property. For the 'NAME' (and 'SOURCE') columns, we 12512676Seschrock * need to know the maximum name length. However, the user likely did 12522676Seschrock * not specify 'name' as one of the properties to fetch, so we need to 12532676Seschrock * make sure we always include at least this property for 12542676Seschrock * print_get_headers() to work properly. 1255789Sahrens */ 12562676Seschrock if (cb.cb_proplist != NULL) { 12572676Seschrock fake_name.pl_prop = ZFS_PROP_NAME; 12582676Seschrock fake_name.pl_width = strlen(gettext("NAME")); 12592676Seschrock fake_name.pl_next = cb.cb_proplist; 12602676Seschrock cb.cb_proplist = &fake_name; 1261789Sahrens } 1262789Sahrens 12632676Seschrock cb.cb_first = B_TRUE; 12642676Seschrock 1265789Sahrens /* run for each object */ 12662676Seschrock ret = zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, NULL, 12672676Seschrock &cb.cb_proplist, get_callback, &cb); 12682676Seschrock 12692676Seschrock if (cb.cb_proplist == &fake_name) 12702676Seschrock zfs_free_proplist(fake_name.pl_next); 12712676Seschrock else 12722676Seschrock zfs_free_proplist(cb.cb_proplist); 12732676Seschrock 12742676Seschrock return (ret); 1275789Sahrens } 1276789Sahrens 1277789Sahrens /* 1278789Sahrens * inherit [-r] <property> <fs|vol> ... 1279789Sahrens * 1280789Sahrens * -r Recurse over all children 1281789Sahrens * 1282789Sahrens * For each dataset specified on the command line, inherit the given property 1283789Sahrens * from its parent. Inheriting a property at the pool level will cause it to 1284789Sahrens * use the default value. The '-r' flag will recurse over all children, and is 1285789Sahrens * useful for setting a property on a hierarchy-wide basis, regardless of any 1286789Sahrens * local modifications for each dataset. 1287789Sahrens */ 12882926Sek110237 typedef struct inherit_cbdata { 12892926Sek110237 char *cb_propname; 12902926Sek110237 boolean_t cb_any_successful; 12912926Sek110237 } inherit_cbdata_t; 12922926Sek110237 1293789Sahrens static int 1294789Sahrens inherit_callback(zfs_handle_t *zhp, void *data) 1295789Sahrens { 12962926Sek110237 inherit_cbdata_t *cbp = data; 12972926Sek110237 int ret; 12982926Sek110237 12992926Sek110237 ret = zfs_prop_inherit(zhp, cbp->cb_propname); 13002926Sek110237 if (ret == 0) 13012926Sek110237 cbp->cb_any_successful = B_TRUE; 13022926Sek110237 return (ret != 0); 1303789Sahrens } 1304789Sahrens 1305789Sahrens static int 1306789Sahrens zfs_do_inherit(int argc, char **argv) 1307789Sahrens { 13082082Seschrock boolean_t recurse = B_FALSE; 1309789Sahrens int c; 1310789Sahrens zfs_prop_t prop; 13112926Sek110237 inherit_cbdata_t cb; 13122926Sek110237 int ret; 1313789Sahrens 1314789Sahrens /* check options */ 1315789Sahrens while ((c = getopt(argc, argv, "r")) != -1) { 1316789Sahrens switch (c) { 1317789Sahrens case 'r': 13182082Seschrock recurse = B_TRUE; 1319789Sahrens break; 1320789Sahrens case '?': 1321789Sahrens default: 1322789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1323789Sahrens optopt); 13242082Seschrock usage(B_FALSE); 1325789Sahrens } 1326789Sahrens } 1327789Sahrens 1328789Sahrens argc -= optind; 1329789Sahrens argv += optind; 1330789Sahrens 1331789Sahrens /* check number of arguments */ 1332789Sahrens if (argc < 1) { 1333789Sahrens (void) fprintf(stderr, gettext("missing property argument\n")); 13342082Seschrock usage(B_FALSE); 1335789Sahrens } 1336789Sahrens if (argc < 2) { 1337789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 13382082Seschrock usage(B_FALSE); 1339789Sahrens } 1340789Sahrens 13412926Sek110237 cb.cb_propname = argv[0]; 13422676Seschrock argc--; 13432676Seschrock argv++; 13442676Seschrock 13452926Sek110237 if ((prop = zfs_name_to_prop(cb.cb_propname)) != ZFS_PROP_INVAL) { 13462676Seschrock if (zfs_prop_readonly(prop)) { 13472676Seschrock (void) fprintf(stderr, gettext( 13482676Seschrock "%s property is read-only\n"), 13492926Sek110237 cb.cb_propname); 13502676Seschrock return (1); 13512676Seschrock } 13522676Seschrock if (!zfs_prop_inheritable(prop)) { 13532676Seschrock (void) fprintf(stderr, gettext("'%s' property cannot " 13542926Sek110237 "be inherited\n"), cb.cb_propname); 13552676Seschrock if (prop == ZFS_PROP_QUOTA || 13562676Seschrock prop == ZFS_PROP_RESERVATION) 13572676Seschrock (void) fprintf(stderr, gettext("use 'zfs set " 13582926Sek110237 "%s=none' to clear\n"), cb.cb_propname); 13592676Seschrock return (1); 13602676Seschrock } 13612926Sek110237 } else if (!zfs_prop_user(cb.cb_propname)) { 13622676Seschrock (void) fprintf(stderr, gettext( 13632676Seschrock "invalid property '%s'\n"), 13642926Sek110237 cb.cb_propname); 13652082Seschrock usage(B_FALSE); 1366789Sahrens } 13672676Seschrock 13682926Sek110237 cb.cb_any_successful = B_FALSE; 13692926Sek110237 13702926Sek110237 ret = zfs_for_each(argc, argv, recurse, 13712676Seschrock ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL, 13722926Sek110237 inherit_callback, &cb); 13732926Sek110237 13742926Sek110237 if (cb.cb_any_successful) { 13752926Sek110237 zpool_log_history(g_zfs, argc + optind + 1, argv - optind - 1, 13762926Sek110237 argv[0], B_FALSE, B_FALSE); 13772926Sek110237 } 13782926Sek110237 13792926Sek110237 return (ret); 1380789Sahrens } 1381789Sahrens 1382789Sahrens /* 13832379Ssjelinek * list [-rH] [-o property[,property]...] [-t type[,type]...] 13842379Ssjelinek * [-s property [-s property]...] [-S property [-S property]...] 13852379Ssjelinek * <dataset> ... 1386789Sahrens * 1387789Sahrens * -r Recurse over all children 1388789Sahrens * -H Scripted mode; elide headers and separate colums by tabs 1389789Sahrens * -o Control which fields to display. 1390866Seschrock * -t Control which object types to display. 13912379Ssjelinek * -s Specify sort columns, descending order. 13922379Ssjelinek * -S Specify sort columns, ascending order. 1393789Sahrens * 1394789Sahrens * When given no arguments, lists all filesystems in the system. 1395789Sahrens * Otherwise, list the specified datasets, optionally recursing down them if 1396789Sahrens * '-r' is specified. 1397789Sahrens */ 1398789Sahrens typedef struct list_cbdata { 13992082Seschrock boolean_t cb_first; 14002082Seschrock boolean_t cb_scripted; 14012676Seschrock zfs_proplist_t *cb_proplist; 1402789Sahrens } list_cbdata_t; 1403789Sahrens 1404789Sahrens /* 1405789Sahrens * Given a list of columns to display, output appropriate headers for each one. 1406789Sahrens */ 1407789Sahrens static void 14082676Seschrock print_header(zfs_proplist_t *pl) 1409789Sahrens { 14102676Seschrock char headerbuf[ZFS_MAXPROPLEN]; 14112676Seschrock const char *header; 1412789Sahrens int i; 14132676Seschrock boolean_t first = B_TRUE; 14142676Seschrock boolean_t right_justify; 14152676Seschrock 14162676Seschrock for (; pl != NULL; pl = pl->pl_next) { 14172676Seschrock if (!first) { 1418789Sahrens (void) printf(" "); 14192676Seschrock } else { 14202676Seschrock first = B_FALSE; 14212676Seschrock } 14222676Seschrock 14232676Seschrock right_justify = B_FALSE; 14242676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 14252676Seschrock header = zfs_prop_column_name(pl->pl_prop); 14262676Seschrock right_justify = zfs_prop_align_right(pl->pl_prop); 14272676Seschrock } else { 14282676Seschrock for (i = 0; pl->pl_user_prop[i] != '\0'; i++) 14292676Seschrock headerbuf[i] = toupper(pl->pl_user_prop[i]); 14302676Seschrock headerbuf[i] = '\0'; 14312676Seschrock header = headerbuf; 14322676Seschrock } 14332676Seschrock 14342676Seschrock if (pl->pl_next == NULL && !right_justify) 14352676Seschrock (void) printf("%s", header); 14362676Seschrock else if (right_justify) 14372676Seschrock (void) printf("%*s", pl->pl_width, header); 14382676Seschrock else 14392676Seschrock (void) printf("%-*s", pl->pl_width, header); 1440789Sahrens } 1441789Sahrens 1442789Sahrens (void) printf("\n"); 1443789Sahrens } 1444789Sahrens 1445789Sahrens /* 1446789Sahrens * Given a dataset and a list of fields, print out all the properties according 1447789Sahrens * to the described layout. 1448789Sahrens */ 1449789Sahrens static void 14502676Seschrock print_dataset(zfs_handle_t *zhp, zfs_proplist_t *pl, int scripted) 1451789Sahrens { 14522676Seschrock boolean_t first = B_TRUE; 1453789Sahrens char property[ZFS_MAXPROPLEN]; 14542676Seschrock nvlist_t *userprops = zfs_get_user_props(zhp); 14552676Seschrock nvlist_t *propval; 14562676Seschrock char *propstr; 14572676Seschrock boolean_t right_justify; 14582676Seschrock int width; 14592676Seschrock 14602676Seschrock for (; pl != NULL; pl = pl->pl_next) { 14612676Seschrock if (!first) { 1462789Sahrens if (scripted) 1463789Sahrens (void) printf("\t"); 1464789Sahrens else 1465789Sahrens (void) printf(" "); 14662676Seschrock } else { 14672676Seschrock first = B_FALSE; 1468789Sahrens } 1469789Sahrens 14702676Seschrock right_justify = B_FALSE; 14712676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 14722676Seschrock if (zfs_prop_get(zhp, pl->pl_prop, property, 14732676Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) != 0) 14742676Seschrock propstr = "-"; 14752676Seschrock else 14762676Seschrock propstr = property; 14772676Seschrock 14782676Seschrock right_justify = zfs_prop_align_right(pl->pl_prop); 14792676Seschrock } else { 14802676Seschrock if (nvlist_lookup_nvlist(userprops, 14812676Seschrock pl->pl_user_prop, &propval) != 0) 14822676Seschrock propstr = "-"; 14832676Seschrock else 14842676Seschrock verify(nvlist_lookup_string(propval, 14852676Seschrock ZFS_PROP_VALUE, &propstr) == 0); 14862676Seschrock } 14872676Seschrock 14882676Seschrock width = pl->pl_width; 1489789Sahrens 1490866Seschrock /* 1491866Seschrock * If this is being called in scripted mode, or if this is the 1492866Seschrock * last column and it is left-justified, don't include a width 1493866Seschrock * format specifier. 1494866Seschrock */ 14952676Seschrock if (scripted || (pl->pl_next == NULL && !right_justify)) 14962676Seschrock (void) printf("%s", propstr); 14972676Seschrock else if (right_justify) 14982676Seschrock (void) printf("%*s", width, propstr); 14992676Seschrock else 15002676Seschrock (void) printf("%-*s", width, propstr); 1501789Sahrens } 1502789Sahrens 1503789Sahrens (void) printf("\n"); 1504789Sahrens } 1505789Sahrens 1506789Sahrens /* 1507789Sahrens * Generic callback function to list a dataset or snapshot. 1508789Sahrens */ 1509789Sahrens static int 1510789Sahrens list_callback(zfs_handle_t *zhp, void *data) 1511789Sahrens { 1512789Sahrens list_cbdata_t *cbp = data; 1513789Sahrens 1514789Sahrens if (cbp->cb_first) { 1515789Sahrens if (!cbp->cb_scripted) 15162676Seschrock print_header(cbp->cb_proplist); 15172082Seschrock cbp->cb_first = B_FALSE; 1518789Sahrens } 1519789Sahrens 15202676Seschrock print_dataset(zhp, cbp->cb_proplist, cbp->cb_scripted); 1521789Sahrens 1522789Sahrens return (0); 1523789Sahrens } 1524789Sahrens 1525789Sahrens static int 1526789Sahrens zfs_do_list(int argc, char **argv) 1527789Sahrens { 1528789Sahrens int c; 15292082Seschrock boolean_t recurse = B_FALSE; 15302082Seschrock boolean_t scripted = B_FALSE; 1531789Sahrens static char default_fields[] = 1532789Sahrens "name,used,available,referenced,mountpoint"; 1533789Sahrens int types = ZFS_TYPE_ANY; 1534789Sahrens char *fields = NULL; 1535789Sahrens char *basic_fields = default_fields; 1536789Sahrens list_cbdata_t cb = { 0 }; 1537789Sahrens char *value; 1538789Sahrens int ret; 1539789Sahrens char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL }; 15402379Ssjelinek zfs_sort_column_t *sortcol = NULL; 1541789Sahrens 1542789Sahrens /* check options */ 15432379Ssjelinek while ((c = getopt(argc, argv, ":o:rt:Hs:S:")) != -1) { 1544789Sahrens switch (c) { 1545789Sahrens case 'o': 1546789Sahrens fields = optarg; 1547789Sahrens break; 1548789Sahrens case 'r': 15492082Seschrock recurse = B_TRUE; 1550789Sahrens break; 1551789Sahrens case 'H': 15522082Seschrock scripted = B_TRUE; 1553789Sahrens break; 15542379Ssjelinek case 's': 15552676Seschrock if (zfs_add_sort_column(&sortcol, optarg, 15562676Seschrock B_FALSE) != 0) { 15572379Ssjelinek (void) fprintf(stderr, 15582379Ssjelinek gettext("invalid property '%s'\n"), optarg); 15592379Ssjelinek usage(B_FALSE); 15602379Ssjelinek } 15612379Ssjelinek break; 15622379Ssjelinek case 'S': 15632676Seschrock if (zfs_add_sort_column(&sortcol, optarg, 15642676Seschrock B_TRUE) != 0) { 15652379Ssjelinek (void) fprintf(stderr, 15662379Ssjelinek gettext("invalid property '%s'\n"), optarg); 15672379Ssjelinek usage(B_FALSE); 15682379Ssjelinek } 15692379Ssjelinek break; 1570789Sahrens case 't': 1571789Sahrens types = 0; 1572789Sahrens while (*optarg != '\0') { 1573789Sahrens switch (getsubopt(&optarg, type_subopts, 1574789Sahrens &value)) { 1575789Sahrens case 0: 1576789Sahrens types |= ZFS_TYPE_FILESYSTEM; 1577789Sahrens break; 1578789Sahrens case 1: 1579789Sahrens types |= ZFS_TYPE_VOLUME; 1580789Sahrens break; 1581789Sahrens case 2: 1582789Sahrens types |= ZFS_TYPE_SNAPSHOT; 1583789Sahrens break; 1584789Sahrens default: 1585789Sahrens (void) fprintf(stderr, 1586789Sahrens gettext("invalid type '%s'\n"), 1587789Sahrens value); 15882082Seschrock usage(B_FALSE); 1589789Sahrens } 1590789Sahrens } 1591789Sahrens break; 1592789Sahrens case ':': 1593789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1594789Sahrens "'%c' option\n"), optopt); 15952082Seschrock usage(B_FALSE); 1596789Sahrens break; 1597789Sahrens case '?': 1598789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1599789Sahrens optopt); 16002082Seschrock usage(B_FALSE); 1601789Sahrens } 1602789Sahrens } 1603789Sahrens 1604789Sahrens argc -= optind; 1605789Sahrens argv += optind; 1606789Sahrens 1607789Sahrens if (fields == NULL) 1608789Sahrens fields = basic_fields; 1609789Sahrens 1610866Seschrock /* 1611866Seschrock * If the user specifies '-o all', the zfs_get_proplist() doesn't 1612866Seschrock * normally include the name of the dataset. For 'zfs list', we always 1613866Seschrock * want this property to be first. 1614866Seschrock */ 16152676Seschrock if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0) 16162082Seschrock usage(B_FALSE); 16172676Seschrock 1618789Sahrens cb.cb_scripted = scripted; 16192082Seschrock cb.cb_first = B_TRUE; 1620789Sahrens 16212676Seschrock ret = zfs_for_each(argc, argv, recurse, types, sortcol, &cb.cb_proplist, 16222379Ssjelinek list_callback, &cb); 16232379Ssjelinek 16242676Seschrock zfs_free_proplist(cb.cb_proplist); 16252379Ssjelinek zfs_free_sort_columns(sortcol); 1626789Sahrens 16272082Seschrock if (ret == 0 && cb.cb_first) 1628789Sahrens (void) printf(gettext("no datasets available\n")); 1629789Sahrens 1630789Sahrens return (ret); 1631789Sahrens } 1632789Sahrens 1633789Sahrens /* 1634789Sahrens * zfs rename <fs | snap | vol> <fs | snap | vol> 1635789Sahrens * 1636789Sahrens * Renames the given dataset to another of the same type. 1637789Sahrens */ 1638789Sahrens /* ARGSUSED */ 1639789Sahrens static int 1640789Sahrens zfs_do_rename(int argc, char **argv) 1641789Sahrens { 1642789Sahrens zfs_handle_t *zhp; 16432082Seschrock int ret; 1644789Sahrens 1645789Sahrens /* check options */ 1646789Sahrens if (argc > 1 && argv[1][0] == '-') { 1647789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1648789Sahrens argv[1][1]); 16492082Seschrock usage(B_FALSE); 1650789Sahrens } 1651789Sahrens 1652789Sahrens /* check number of arguments */ 1653789Sahrens if (argc < 2) { 1654789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 1655789Sahrens "argument\n")); 16562082Seschrock usage(B_FALSE); 1657789Sahrens } 1658789Sahrens if (argc < 3) { 1659789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 1660789Sahrens "argument\n")); 16612082Seschrock usage(B_FALSE); 1662789Sahrens } 1663789Sahrens if (argc > 3) { 1664789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 16652082Seschrock usage(B_FALSE); 1666789Sahrens } 1667789Sahrens 16682082Seschrock if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_ANY)) == NULL) 1669789Sahrens return (1); 1670789Sahrens 16712082Seschrock ret = (zfs_rename(zhp, argv[2]) != 0); 16722082Seschrock 16732926Sek110237 if (!ret) 16742926Sek110237 zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE); 16752926Sek110237 16762082Seschrock zfs_close(zhp); 16772082Seschrock return (ret); 16782082Seschrock } 16792082Seschrock 16802082Seschrock /* 16812082Seschrock * zfs promote <fs> 16822082Seschrock * 16832082Seschrock * Promotes the given clone fs to be the parent 16842082Seschrock */ 16852082Seschrock /* ARGSUSED */ 16862082Seschrock static int 16872082Seschrock zfs_do_promote(int argc, char **argv) 16882082Seschrock { 16892082Seschrock zfs_handle_t *zhp; 16902082Seschrock int ret; 16912082Seschrock 16922082Seschrock /* check options */ 16932082Seschrock if (argc > 1 && argv[1][0] == '-') { 16942082Seschrock (void) fprintf(stderr, gettext("invalid option '%c'\n"), 16952082Seschrock argv[1][1]); 16962082Seschrock usage(B_FALSE); 16972082Seschrock } 16982082Seschrock 16992082Seschrock /* check number of arguments */ 17002082Seschrock if (argc < 2) { 17012082Seschrock (void) fprintf(stderr, gettext("missing clone filesystem" 17022597Snd150628 " argument\n")); 17032082Seschrock usage(B_FALSE); 17042082Seschrock } 17052082Seschrock if (argc > 2) { 17062082Seschrock (void) fprintf(stderr, gettext("too many arguments\n")); 17072082Seschrock usage(B_FALSE); 17082082Seschrock } 17092082Seschrock 17102082Seschrock zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 17112082Seschrock if (zhp == NULL) 17122082Seschrock return (1); 17132082Seschrock 17142082Seschrock ret = (zfs_promote(zhp) != 0); 17152082Seschrock 17162926Sek110237 if (!ret) 17172926Sek110237 zpool_log_history(g_zfs, argc, argv, argv[1], B_FALSE, B_FALSE); 17182926Sek110237 1719789Sahrens zfs_close(zhp); 1720789Sahrens return (ret); 1721789Sahrens } 1722789Sahrens 1723789Sahrens /* 1724789Sahrens * zfs rollback [-rfR] <snapshot> 1725789Sahrens * 1726789Sahrens * -r Delete any intervening snapshots before doing rollback 1727789Sahrens * -R Delete any snapshots and their clones 1728789Sahrens * -f Force unmount filesystems, even if they are in use. 1729789Sahrens * 1730789Sahrens * Given a filesystem, rollback to a specific snapshot, discarding any changes 1731789Sahrens * since then and making it the active dataset. If more recent snapshots exist, 1732789Sahrens * the command will complain unless the '-r' flag is given. 1733789Sahrens */ 1734789Sahrens typedef struct rollback_cbdata { 1735789Sahrens uint64_t cb_create; 17362082Seschrock boolean_t cb_first; 1737789Sahrens int cb_doclones; 1738789Sahrens char *cb_target; 1739789Sahrens int cb_error; 17402082Seschrock boolean_t cb_recurse; 17412082Seschrock boolean_t cb_dependent; 1742789Sahrens } rollback_cbdata_t; 1743789Sahrens 1744789Sahrens /* 1745789Sahrens * Report any snapshots more recent than the one specified. Used when '-r' is 1746789Sahrens * not specified. We reuse this same callback for the snapshot dependents - if 1747789Sahrens * 'cb_dependent' is set, then this is a dependent and we should report it 1748789Sahrens * without checking the transaction group. 1749789Sahrens */ 1750789Sahrens static int 1751789Sahrens rollback_check(zfs_handle_t *zhp, void *data) 1752789Sahrens { 1753789Sahrens rollback_cbdata_t *cbp = data; 1754789Sahrens 17552082Seschrock if (cbp->cb_doclones) { 17562082Seschrock zfs_close(zhp); 1757789Sahrens return (0); 17582082Seschrock } 1759789Sahrens 1760789Sahrens if (!cbp->cb_dependent) { 1761789Sahrens if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && 17621294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 1763789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 1764789Sahrens cbp->cb_create) { 1765789Sahrens 1766789Sahrens if (cbp->cb_first && !cbp->cb_recurse) { 1767789Sahrens (void) fprintf(stderr, gettext("cannot " 1768789Sahrens "rollback to '%s': more recent snapshots " 1769789Sahrens "exist\n"), 1770789Sahrens cbp->cb_target); 1771789Sahrens (void) fprintf(stderr, gettext("use '-r' to " 1772789Sahrens "force deletion of the following " 1773789Sahrens "snapshots:\n")); 1774789Sahrens cbp->cb_first = 0; 1775789Sahrens cbp->cb_error = 1; 1776789Sahrens } 1777789Sahrens 1778789Sahrens if (cbp->cb_recurse) { 17792082Seschrock cbp->cb_dependent = B_TRUE; 17802474Seschrock if (zfs_iter_dependents(zhp, B_TRUE, 17812474Seschrock rollback_check, cbp) != 0) { 17822474Seschrock zfs_close(zhp); 17832474Seschrock return (-1); 17842474Seschrock } 17852082Seschrock cbp->cb_dependent = B_FALSE; 1786789Sahrens } else { 1787789Sahrens (void) fprintf(stderr, "%s\n", 1788789Sahrens zfs_get_name(zhp)); 1789789Sahrens } 1790789Sahrens } 1791789Sahrens } else { 1792789Sahrens if (cbp->cb_first && cbp->cb_recurse) { 1793789Sahrens (void) fprintf(stderr, gettext("cannot rollback to " 1794789Sahrens "'%s': clones of previous snapshots exist\n"), 1795789Sahrens cbp->cb_target); 1796789Sahrens (void) fprintf(stderr, gettext("use '-R' to " 1797789Sahrens "force deletion of the following clones and " 1798789Sahrens "dependents:\n")); 1799789Sahrens cbp->cb_first = 0; 1800789Sahrens cbp->cb_error = 1; 1801789Sahrens } 1802789Sahrens 1803789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1804789Sahrens } 1805789Sahrens 1806789Sahrens zfs_close(zhp); 1807789Sahrens return (0); 1808789Sahrens } 1809789Sahrens 1810789Sahrens static int 1811789Sahrens zfs_do_rollback(int argc, char **argv) 1812789Sahrens { 1813789Sahrens int ret; 1814789Sahrens int c; 1815789Sahrens rollback_cbdata_t cb = { 0 }; 1816789Sahrens zfs_handle_t *zhp, *snap; 1817789Sahrens char parentname[ZFS_MAXNAMELEN]; 1818789Sahrens char *delim; 18191294Slling int force = 0; 1820789Sahrens 1821789Sahrens /* check options */ 1822789Sahrens while ((c = getopt(argc, argv, "rfR")) != -1) { 1823789Sahrens switch (c) { 1824789Sahrens case 'f': 18251294Slling force = 1; 1826789Sahrens break; 1827789Sahrens case 'r': 1828789Sahrens cb.cb_recurse = 1; 1829789Sahrens break; 1830789Sahrens case 'R': 1831789Sahrens cb.cb_recurse = 1; 1832789Sahrens cb.cb_doclones = 1; 1833789Sahrens break; 1834789Sahrens case '?': 1835789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1836789Sahrens optopt); 18372082Seschrock usage(B_FALSE); 1838789Sahrens } 1839789Sahrens } 1840789Sahrens 1841789Sahrens argc -= optind; 1842789Sahrens argv += optind; 1843789Sahrens 1844789Sahrens /* check number of arguments */ 1845789Sahrens if (argc < 1) { 1846789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 18472082Seschrock usage(B_FALSE); 1848789Sahrens } 1849789Sahrens if (argc > 1) { 1850789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 18512082Seschrock usage(B_FALSE); 1852789Sahrens } 1853789Sahrens 1854789Sahrens /* open the snapshot */ 18552082Seschrock if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1856789Sahrens return (1); 1857789Sahrens 18581294Slling /* open the parent dataset */ 18591294Slling (void) strlcpy(parentname, argv[0], sizeof (parentname)); 1860789Sahrens verify((delim = strrchr(parentname, '@')) != NULL); 1861789Sahrens *delim = '\0'; 18622082Seschrock if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_ANY)) == NULL) { 1863789Sahrens zfs_close(snap); 1864789Sahrens return (1); 1865789Sahrens } 1866789Sahrens 1867789Sahrens /* 1868789Sahrens * Check for more recent snapshots and/or clones based on the presence 1869789Sahrens * of '-r' and '-R'. 1870789Sahrens */ 18711294Slling cb.cb_target = argv[0]; 18721294Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 18732082Seschrock cb.cb_first = B_TRUE; 1874789Sahrens cb.cb_error = 0; 18752474Seschrock if ((ret = zfs_iter_children(zhp, rollback_check, &cb)) != 0) 18762474Seschrock goto out; 1877789Sahrens 1878789Sahrens if ((ret = cb.cb_error) != 0) 1879789Sahrens goto out; 1880789Sahrens 1881789Sahrens /* 18821294Slling * Rollback parent to the given snapshot. 1883789Sahrens */ 18841294Slling ret = zfs_rollback(zhp, snap, force); 1885789Sahrens 18862926Sek110237 if (!ret) { 18872926Sek110237 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0], 18882926Sek110237 B_FALSE, B_FALSE); 18892926Sek110237 } 18902926Sek110237 1891789Sahrens out: 1892789Sahrens zfs_close(snap); 1893789Sahrens zfs_close(zhp); 1894789Sahrens 1895789Sahrens if (ret == 0) 1896789Sahrens return (0); 1897789Sahrens else 1898789Sahrens return (1); 1899789Sahrens } 1900789Sahrens 1901789Sahrens /* 1902789Sahrens * zfs set property=value { fs | snap | vol } ... 1903789Sahrens * 1904789Sahrens * Sets the given property for all datasets specified on the command line. 1905789Sahrens */ 1906789Sahrens typedef struct set_cbdata { 1907789Sahrens char *cb_propname; 1908789Sahrens char *cb_value; 19092926Sek110237 boolean_t cb_any_successful; 1910789Sahrens } set_cbdata_t; 1911789Sahrens 1912789Sahrens static int 1913789Sahrens set_callback(zfs_handle_t *zhp, void *data) 1914789Sahrens { 1915789Sahrens set_cbdata_t *cbp = data; 1916789Sahrens 19172676Seschrock if (zfs_prop_set(zhp, cbp->cb_propname, cbp->cb_value) != 0) { 19182169Snd150628 switch (libzfs_errno(g_zfs)) { 19192169Snd150628 case EZFS_MOUNTFAILED: 19202169Snd150628 (void) fprintf(stderr, gettext("property may be set " 19212169Snd150628 "but unable to remount filesystem\n")); 19222169Snd150628 break; 1923*3126Sahl case EZFS_SHARENFSFAILED: 19242169Snd150628 (void) fprintf(stderr, gettext("property may be set " 19252169Snd150628 "but unable to reshare filesystem\n")); 19262169Snd150628 break; 19272169Snd150628 } 1928789Sahrens return (1); 19292169Snd150628 } 19302926Sek110237 cbp->cb_any_successful = B_TRUE; 19312856Snd150628 return (0); 1932789Sahrens } 1933789Sahrens 1934789Sahrens static int 1935789Sahrens zfs_do_set(int argc, char **argv) 1936789Sahrens { 1937789Sahrens set_cbdata_t cb; 19382926Sek110237 int ret; 1939789Sahrens 1940789Sahrens /* check for options */ 1941789Sahrens if (argc > 1 && argv[1][0] == '-') { 1942789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1943789Sahrens argv[1][1]); 19442082Seschrock usage(B_FALSE); 1945789Sahrens } 1946789Sahrens 1947789Sahrens /* check number of arguments */ 1948789Sahrens if (argc < 2) { 1949789Sahrens (void) fprintf(stderr, gettext("missing property=value " 1950789Sahrens "argument\n")); 19512082Seschrock usage(B_FALSE); 1952789Sahrens } 1953789Sahrens if (argc < 3) { 1954789Sahrens (void) fprintf(stderr, gettext("missing dataset name\n")); 19552082Seschrock usage(B_FALSE); 1956789Sahrens } 1957789Sahrens 1958789Sahrens /* validate property=value argument */ 1959789Sahrens cb.cb_propname = argv[1]; 1960789Sahrens if ((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) { 1961789Sahrens (void) fprintf(stderr, gettext("missing value in " 1962789Sahrens "property=value argument\n")); 19632082Seschrock usage(B_FALSE); 1964789Sahrens } 1965789Sahrens 1966789Sahrens *cb.cb_value = '\0'; 1967789Sahrens cb.cb_value++; 19682926Sek110237 cb.cb_any_successful = B_FALSE; 1969789Sahrens 1970789Sahrens if (*cb.cb_propname == '\0') { 1971789Sahrens (void) fprintf(stderr, 1972789Sahrens gettext("missing property in property=value argument\n")); 19732082Seschrock usage(B_FALSE); 1974789Sahrens } 1975789Sahrens 19762926Sek110237 ret = zfs_for_each(argc - 2, argv + 2, B_FALSE, 19772926Sek110237 ZFS_TYPE_ANY, NULL, NULL, set_callback, &cb); 19782926Sek110237 19792926Sek110237 if (cb.cb_any_successful) { 19802926Sek110237 *(cb.cb_value - 1) = '='; 19812926Sek110237 zpool_log_history(g_zfs, argc, argv, argv[2], B_FALSE, B_FALSE); 19822926Sek110237 } 19832926Sek110237 19842926Sek110237 return (ret); 1985789Sahrens } 1986789Sahrens 1987789Sahrens /* 19882199Sahrens * zfs snapshot [-r] <fs@snap> 1989789Sahrens * 1990789Sahrens * Creates a snapshot with the given name. While functionally equivalent to 1991789Sahrens * 'zfs create', it is a separate command to diffferentiate intent. 1992789Sahrens */ 1993789Sahrens static int 1994789Sahrens zfs_do_snapshot(int argc, char **argv) 1995789Sahrens { 19962199Sahrens int recursive = B_FALSE; 19972199Sahrens int ret; 19982199Sahrens char c; 19992199Sahrens 2000789Sahrens /* check options */ 20012199Sahrens while ((c = getopt(argc, argv, ":r")) != -1) { 20022199Sahrens switch (c) { 20032199Sahrens case 'r': 20042199Sahrens recursive = B_TRUE; 20052199Sahrens break; 20062199Sahrens case '?': 20072199Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 20082199Sahrens optopt); 20092199Sahrens usage(B_FALSE); 20102199Sahrens } 2011789Sahrens } 2012789Sahrens 20132199Sahrens argc -= optind; 20142199Sahrens argv += optind; 20152199Sahrens 2016789Sahrens /* check number of arguments */ 20172199Sahrens if (argc < 1) { 2018789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 20192082Seschrock usage(B_FALSE); 2020789Sahrens } 20212199Sahrens if (argc > 1) { 2022789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 20232082Seschrock usage(B_FALSE); 2024789Sahrens } 2025789Sahrens 20262199Sahrens ret = zfs_snapshot(g_zfs, argv[0], recursive); 20272199Sahrens if (ret && recursive) 20282199Sahrens (void) fprintf(stderr, gettext("no snapshots were created\n")); 20292926Sek110237 if (!ret) { 20302926Sek110237 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0], 20312926Sek110237 B_FALSE, B_FALSE); 20322926Sek110237 } 20332199Sahrens return (ret != 0); 2034789Sahrens } 2035789Sahrens 2036789Sahrens /* 20372885Sahrens * zfs send [-i <@snap>] <fs@snap> 2038789Sahrens * 2039789Sahrens * Send a backup stream to stdout. 2040789Sahrens */ 2041789Sahrens static int 20421749Sahrens zfs_do_send(int argc, char **argv) 2043789Sahrens { 2044789Sahrens char *fromname = NULL; 20452885Sahrens char *cp; 20462885Sahrens zfs_handle_t *zhp; 2047789Sahrens int c, err; 2048789Sahrens 2049789Sahrens /* check options */ 2050789Sahrens while ((c = getopt(argc, argv, ":i:")) != -1) { 2051789Sahrens switch (c) { 2052789Sahrens case 'i': 20532885Sahrens if (fromname) 20542885Sahrens usage(B_FALSE); 2055789Sahrens fromname = optarg; 2056789Sahrens break; 2057789Sahrens case ':': 2058789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2059789Sahrens "'%c' option\n"), optopt); 20602082Seschrock usage(B_FALSE); 2061789Sahrens break; 2062789Sahrens case '?': 2063789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2064789Sahrens optopt); 20652082Seschrock usage(B_FALSE); 2066789Sahrens } 2067789Sahrens } 2068789Sahrens 2069789Sahrens argc -= optind; 2070789Sahrens argv += optind; 2071789Sahrens 2072789Sahrens /* check number of arguments */ 2073789Sahrens if (argc < 1) { 2074789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 20752082Seschrock usage(B_FALSE); 2076789Sahrens } 2077789Sahrens if (argc > 1) { 2078789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 20792082Seschrock usage(B_FALSE); 2080789Sahrens } 2081789Sahrens 2082789Sahrens if (isatty(STDOUT_FILENO)) { 2083789Sahrens (void) fprintf(stderr, 20842885Sahrens gettext("Error: Stream can not be written to a terminal.\n" 2085789Sahrens "You must redirect standard output.\n")); 2086789Sahrens return (1); 2087789Sahrens } 2088789Sahrens 20892885Sahrens if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 20902665Snd150628 return (1); 20912665Snd150628 20922885Sahrens /* 20932885Sahrens * If they specified the full path to the snapshot, chop off 20942885Sahrens * everything except the short name of the snapshot. 20952885Sahrens */ 20962885Sahrens if (fromname && (cp = strchr(fromname, '@')) != NULL) { 20972885Sahrens if (cp != fromname && 20982885Sahrens strncmp(argv[0], fromname, cp - fromname + 1)) { 20992885Sahrens (void) fprintf(stderr, 21002885Sahrens gettext("incremental source must be " 21012885Sahrens "in same filesystem\n")); 21022885Sahrens usage(B_FALSE); 21032665Snd150628 } 21042885Sahrens fromname = cp + 1; 21052885Sahrens if (strchr(fromname, '@') || strchr(fromname, '/')) { 21062885Sahrens (void) fprintf(stderr, 21072885Sahrens gettext("invalid incremental source\n")); 21082885Sahrens usage(B_FALSE); 21092885Sahrens } 2110789Sahrens } 2111789Sahrens 21122885Sahrens err = zfs_send(zhp, fromname); 21132885Sahrens zfs_close(zhp); 2114789Sahrens 2115789Sahrens return (err != 0); 2116789Sahrens } 2117789Sahrens 2118789Sahrens /* 21191749Sahrens * zfs receive <fs@snap> 2120789Sahrens * 2121789Sahrens * Restore a backup stream from stdin. 2122789Sahrens */ 2123789Sahrens static int 21241749Sahrens zfs_do_receive(int argc, char **argv) 2125789Sahrens { 2126789Sahrens int c, err; 21272082Seschrock boolean_t isprefix = B_FALSE; 21282082Seschrock boolean_t dryrun = B_FALSE; 21292082Seschrock boolean_t verbose = B_FALSE; 21302665Snd150628 boolean_t force = B_FALSE; 2131789Sahrens 2132789Sahrens /* check options */ 21332665Snd150628 while ((c = getopt(argc, argv, ":dnvF")) != -1) { 2134789Sahrens switch (c) { 2135789Sahrens case 'd': 21362082Seschrock isprefix = B_TRUE; 2137789Sahrens break; 2138789Sahrens case 'n': 21392082Seschrock dryrun = B_TRUE; 2140789Sahrens break; 2141789Sahrens case 'v': 21422082Seschrock verbose = B_TRUE; 2143789Sahrens break; 21442665Snd150628 case 'F': 21452665Snd150628 force = B_TRUE; 21462665Snd150628 break; 2147789Sahrens case ':': 2148789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2149789Sahrens "'%c' option\n"), optopt); 21502082Seschrock usage(B_FALSE); 2151789Sahrens break; 2152789Sahrens case '?': 2153789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2154789Sahrens optopt); 21552082Seschrock usage(B_FALSE); 2156789Sahrens } 2157789Sahrens } 2158789Sahrens 2159789Sahrens argc -= optind; 2160789Sahrens argv += optind; 2161789Sahrens 2162789Sahrens /* check number of arguments */ 2163789Sahrens if (argc < 1) { 2164789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 21652082Seschrock usage(B_FALSE); 2166789Sahrens } 2167789Sahrens if (argc > 1) { 2168789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 21692082Seschrock usage(B_FALSE); 2170789Sahrens } 2171789Sahrens 2172789Sahrens if (isatty(STDIN_FILENO)) { 2173789Sahrens (void) fprintf(stderr, 2174789Sahrens gettext("Error: Backup stream can not be read " 2175789Sahrens "from a terminal.\n" 2176789Sahrens "You must redirect standard input.\n")); 2177789Sahrens return (1); 2178789Sahrens } 2179789Sahrens 21802665Snd150628 err = zfs_receive(g_zfs, argv[0], isprefix, verbose, dryrun, force); 21812926Sek110237 21822926Sek110237 if (!err) { 21832926Sek110237 zpool_log_history(g_zfs, argc + optind, argv - optind, argv[0], 21842926Sek110237 B_FALSE, B_FALSE); 21852926Sek110237 } 21862926Sek110237 2187789Sahrens return (err != 0); 2188789Sahrens } 2189789Sahrens 21901356Seschrock typedef struct get_all_cbdata { 21911356Seschrock zfs_handle_t **cb_handles; 21921356Seschrock size_t cb_alloc; 21931356Seschrock size_t cb_used; 2194*3126Sahl uint_t cb_types; 21951356Seschrock } get_all_cbdata_t; 21961356Seschrock 21971356Seschrock static int 2198*3126Sahl get_one_dataset(zfs_handle_t *zhp, void *data) 21991356Seschrock { 22001356Seschrock get_all_cbdata_t *cbp = data; 2201*3126Sahl zfs_type_t type = zfs_get_type(zhp); 22021356Seschrock 22031356Seschrock /* 2204*3126Sahl * Interate over any nested datasets. 22051356Seschrock */ 2206*3126Sahl if (type == ZFS_TYPE_FILESYSTEM && 2207*3126Sahl zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) { 2208*3126Sahl return (1); 2209*3126Sahl } 2210*3126Sahl 2211*3126Sahl /* 2212*3126Sahl * Skip any datasets whose type does not match. 2213*3126Sahl */ 2214*3126Sahl if ((type & cbp->cb_types) == 0) { 22151356Seschrock zfs_close(zhp); 22161356Seschrock return (0); 22171356Seschrock } 22181356Seschrock 22191356Seschrock if (cbp->cb_alloc == cbp->cb_used) { 22201356Seschrock zfs_handle_t **handles; 22211356Seschrock 22221356Seschrock if (cbp->cb_alloc == 0) 22231356Seschrock cbp->cb_alloc = 64; 22241356Seschrock else 22251356Seschrock cbp->cb_alloc *= 2; 22261356Seschrock 22271356Seschrock handles = safe_malloc(cbp->cb_alloc * sizeof (void *)); 22281356Seschrock 22291356Seschrock if (cbp->cb_handles) { 22301356Seschrock bcopy(cbp->cb_handles, handles, 22311356Seschrock cbp->cb_used * sizeof (void *)); 22321356Seschrock free(cbp->cb_handles); 22331356Seschrock } 22341356Seschrock 22351356Seschrock cbp->cb_handles = handles; 22361356Seschrock } 22371356Seschrock 22381356Seschrock cbp->cb_handles[cbp->cb_used++] = zhp; 22391356Seschrock 2240*3126Sahl return (0); 22411356Seschrock } 22421356Seschrock 22431356Seschrock static void 2244*3126Sahl get_all_datasets(uint_t types, zfs_handle_t ***dslist, size_t *count) 22451356Seschrock { 22461356Seschrock get_all_cbdata_t cb = { 0 }; 2247*3126Sahl cb.cb_types = types; 2248*3126Sahl 2249*3126Sahl (void) zfs_iter_root(g_zfs, get_one_dataset, &cb); 2250*3126Sahl 2251*3126Sahl *dslist = cb.cb_handles; 22521356Seschrock *count = cb.cb_used; 22531356Seschrock } 22541356Seschrock 22551356Seschrock static int 2256*3126Sahl dataset_cmp(const void *a, const void *b) 22571356Seschrock { 22581356Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 22591356Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 22601356Seschrock char mounta[MAXPATHLEN]; 22611356Seschrock char mountb[MAXPATHLEN]; 2262*3126Sahl boolean_t gota, gotb; 2263*3126Sahl 2264*3126Sahl if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0) 2265*3126Sahl verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 2266*3126Sahl sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 2267*3126Sahl if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0) 2268*3126Sahl verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 2269*3126Sahl sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 2270*3126Sahl 2271*3126Sahl if (gota && gotb) 2272*3126Sahl return (strcmp(mounta, mountb)); 2273*3126Sahl 2274*3126Sahl if (gota) 2275*3126Sahl return (-1); 2276*3126Sahl if (gotb) 2277*3126Sahl return (1); 2278*3126Sahl 2279*3126Sahl return (strcmp(zfs_get_name(a), zfs_get_name(b))); 22801356Seschrock } 2281789Sahrens 2282789Sahrens /* 2283789Sahrens * Generic callback for sharing or mounting filesystems. Because the code is so 2284789Sahrens * similar, we have a common function with an extra parameter to determine which 2285789Sahrens * mode we are using. 2286789Sahrens */ 2287789Sahrens #define OP_SHARE 0x1 2288789Sahrens #define OP_MOUNT 0x2 2289789Sahrens 2290789Sahrens /* 2291*3126Sahl * Share or mount a dataset. 2292789Sahrens */ 2293789Sahrens static int 2294*3126Sahl share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit, 2295*3126Sahl const char *options) 2296789Sahrens { 2297789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2298789Sahrens char shareopts[ZFS_MAXPROPLEN]; 2299*3126Sahl const char *cmdname = op == OP_SHARE ? "share" : "mount"; 2300789Sahrens struct mnttab mnt; 23012676Seschrock uint64_t zoned, canmount; 2302*3126Sahl zfs_type_t type = zfs_get_type(zhp); 2303*3126Sahl 2304*3126Sahl assert(type & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)); 2305*3126Sahl 2306*3126Sahl if (type == ZFS_TYPE_FILESYSTEM) { 2307*3126Sahl /* 2308*3126Sahl * Check to make sure we can mount/share this dataset. If we 2309*3126Sahl * are in the global zone and the filesystem is exported to a 2310*3126Sahl * local zone, or if we are in a local zone and the 2311*3126Sahl * filesystem is not exported, then it is an error. 2312*3126Sahl */ 2313*3126Sahl zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2314*3126Sahl 2315*3126Sahl if (zoned && getzoneid() == GLOBAL_ZONEID) { 2316*3126Sahl if (!explicit) 2317*3126Sahl return (0); 2318*3126Sahl 2319*3126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 2320*3126Sahl "dataset is exported to a local zone\n"), cmdname, 2321*3126Sahl zfs_get_name(zhp)); 2322*3126Sahl return (1); 2323*3126Sahl 2324*3126Sahl } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 2325*3126Sahl if (!explicit) 2326*3126Sahl return (0); 2327*3126Sahl 2328*3126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 2329*3126Sahl "permission denied\n"), cmdname, 2330*3126Sahl zfs_get_name(zhp)); 2331*3126Sahl return (1); 2332*3126Sahl } 2333*3126Sahl 2334*3126Sahl /* 2335*3126Sahl * Ignore any filesystems which don't apply to us. This 2336*3126Sahl * includes those with a legacy mountpoint, or those with 2337*3126Sahl * legacy share options. 2338*3126Sahl */ 2339*3126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 2340*3126Sahl sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); 2341*3126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 2342*3126Sahl sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 2343*3126Sahl canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); 2344*3126Sahl 2345*3126Sahl if (op == OP_SHARE && strcmp(shareopts, "off") == 0) { 2346*3126Sahl if (!explicit) 2347789Sahrens return (0); 2348789Sahrens 2349789Sahrens (void) fprintf(stderr, gettext("cannot share '%s': " 2350789Sahrens "legacy share\n"), zfs_get_name(zhp)); 2351789Sahrens (void) fprintf(stderr, gettext("use share(1M) to " 2352789Sahrens "share this filesystem\n")); 2353789Sahrens return (1); 2354789Sahrens } 2355*3126Sahl 2356*3126Sahl /* 2357*3126Sahl * We cannot share or mount legacy filesystems. If the 2358*3126Sahl * shareopts is non-legacy but the mountpoint is legacy, we 2359*3126Sahl * treat it as a legacy share. 2360*3126Sahl */ 2361*3126Sahl if (strcmp(mountpoint, "legacy") == 0) { 2362*3126Sahl if (!explicit) 2363*3126Sahl return (0); 2364*3126Sahl 2365*3126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 2366*3126Sahl "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 2367*3126Sahl (void) fprintf(stderr, gettext("use %s to " 2368*3126Sahl "%s this filesystem\n"), op == OP_SHARE ? 2369*3126Sahl "share(1M)" : "mount(1M)", cmdname); 2370*3126Sahl return (1); 2371*3126Sahl } 2372*3126Sahl 2373*3126Sahl if (strcmp(mountpoint, "none") == 0) { 2374*3126Sahl if (!explicit) 2375*3126Sahl return (0); 2376*3126Sahl 2377*3126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': no " 2378*3126Sahl "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 2379*3126Sahl return (1); 2380*3126Sahl } 2381*3126Sahl 2382*3126Sahl if (!canmount) { 2383*3126Sahl if (!explicit) 2384*3126Sahl return (0); 2385*3126Sahl 2386*3126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 2387*3126Sahl "'canmount' property is set to 'off'\n"), cmdname, 2388*3126Sahl zfs_get_name(zhp)); 2389*3126Sahl return (1); 2390*3126Sahl } 2391*3126Sahl 2392*3126Sahl /* 2393*3126Sahl * At this point, we have verified that the mountpoint and/or 2394*3126Sahl * shareopts are appropriate for auto management. If the 2395*3126Sahl * filesystem is already mounted or shared, return (failing 2396*3126Sahl * for explicit requests); otherwise mount or share the 2397*3126Sahl * filesystem. 2398*3126Sahl */ 2399*3126Sahl switch (op) { 2400*3126Sahl case OP_SHARE: 2401*3126Sahl if (zfs_is_shared_nfs(zhp, NULL)) { 2402*3126Sahl if (!explicit) 2403*3126Sahl return (0); 2404*3126Sahl 2405789Sahrens (void) fprintf(stderr, gettext("cannot share " 2406789Sahrens "'%s': filesystem already shared\n"), 2407789Sahrens zfs_get_name(zhp)); 2408789Sahrens return (1); 2409789Sahrens } 2410*3126Sahl 2411*3126Sahl if (!zfs_is_mounted(zhp, NULL) && 2412*3126Sahl zfs_mount(zhp, NULL, 0) != 0) 2413*3126Sahl return (1); 2414*3126Sahl 2415*3126Sahl if (zfs_share_nfs(zhp) != 0) 2416*3126Sahl return (1); 2417*3126Sahl break; 2418*3126Sahl 2419*3126Sahl case OP_MOUNT: 2420*3126Sahl if (options == NULL) 2421*3126Sahl mnt.mnt_mntopts = ""; 2422*3126Sahl else 2423*3126Sahl mnt.mnt_mntopts = (char *)options; 2424*3126Sahl 2425*3126Sahl if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 2426*3126Sahl zfs_is_mounted(zhp, NULL)) { 2427*3126Sahl if (!explicit) 2428*3126Sahl return (0); 2429*3126Sahl 2430789Sahrens (void) fprintf(stderr, gettext("cannot mount " 2431789Sahrens "'%s': filesystem already mounted\n"), 2432789Sahrens zfs_get_name(zhp)); 2433789Sahrens return (1); 2434789Sahrens } 2435*3126Sahl 2436*3126Sahl if (zfs_mount(zhp, options, flags) != 0) 2437*3126Sahl return (1); 2438*3126Sahl break; 2439789Sahrens } 2440*3126Sahl } else { 2441*3126Sahl assert(op == OP_SHARE); 2442*3126Sahl 2443*3126Sahl /* 2444*3126Sahl * Ignore any volumes that aren't shared. 2445*3126Sahl */ 2446*3126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, shareopts, 2447*3126Sahl sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 2448*3126Sahl 2449*3126Sahl if (strcmp(shareopts, "off") == 0) { 2450*3126Sahl if (!explicit) 2451*3126Sahl return (0); 2452*3126Sahl 2453*3126Sahl (void) fprintf(stderr, gettext("cannot share '%s': " 2454*3126Sahl "'shareiscsi' property not set\n"), 2455*3126Sahl zfs_get_name(zhp)); 2456*3126Sahl (void) fprintf(stderr, gettext("set 'shareiscsi' " 2457*3126Sahl "property or use iscsitadm(1M) to share this " 2458*3126Sahl "volume\n")); 2459*3126Sahl return (1); 2460789Sahrens } 2461*3126Sahl 2462*3126Sahl if (zfs_is_shared_iscsi(zhp)) { 2463*3126Sahl if (!explicit) 2464*3126Sahl return (0); 2465*3126Sahl 2466*3126Sahl (void) fprintf(stderr, gettext("cannot share " 2467*3126Sahl "'%s': volume already shared\n"), 2468*3126Sahl zfs_get_name(zhp)); 2469789Sahrens return (1); 2470*3126Sahl } 2471*3126Sahl 2472*3126Sahl if (zfs_share_iscsi(zhp) != 0) 2473*3126Sahl return (1); 2474789Sahrens } 2475789Sahrens 2476789Sahrens return (0); 2477789Sahrens } 2478789Sahrens 2479789Sahrens static int 2480*3126Sahl share_mount(int op, int argc, char **argv) 2481789Sahrens { 2482789Sahrens int do_all = 0; 24832372Slling int c, ret = 0; 2484*3126Sahl const char *options = NULL; 2485*3126Sahl int types, flags = 0; 2486789Sahrens 2487789Sahrens /* check options */ 2488*3126Sahl while ((c = getopt(argc, argv, op == OP_MOUNT ? ":ao:O" : "a")) 2489789Sahrens != -1) { 2490789Sahrens switch (c) { 2491789Sahrens case 'a': 2492789Sahrens do_all = 1; 2493789Sahrens break; 2494789Sahrens case 'o': 2495*3126Sahl options = optarg; 2496789Sahrens break; 2497789Sahrens case 'O': 2498*3126Sahl flags |= MS_OVERLAY; 2499789Sahrens break; 2500789Sahrens case ':': 2501789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2502789Sahrens "'%c' option\n"), optopt); 25032082Seschrock usage(B_FALSE); 2504789Sahrens break; 2505789Sahrens case '?': 2506789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2507789Sahrens optopt); 25082082Seschrock usage(B_FALSE); 2509789Sahrens } 2510789Sahrens } 2511789Sahrens 2512789Sahrens argc -= optind; 2513789Sahrens argv += optind; 2514789Sahrens 2515789Sahrens /* check number of arguments */ 2516789Sahrens if (do_all) { 2517*3126Sahl zfs_handle_t **dslist = NULL; 25181356Seschrock size_t i, count = 0; 25191356Seschrock 2520*3126Sahl if (op == OP_MOUNT) { 2521*3126Sahl types = ZFS_TYPE_FILESYSTEM; 2522*3126Sahl } else if (argc > 0) { 2523*3126Sahl if (strcmp(argv[0], "nfs") == 0) { 2524*3126Sahl types = ZFS_TYPE_FILESYSTEM; 2525*3126Sahl } else if (strcmp(argv[0], "iscsi") == 0) { 2526*3126Sahl types = ZFS_TYPE_VOLUME; 2527*3126Sahl } else { 2528*3126Sahl (void) fprintf(stderr, gettext("share type " 2529*3126Sahl "must be 'nfs' or 'iscsi'\n")); 2530*3126Sahl usage(B_FALSE); 2531*3126Sahl } 2532*3126Sahl 2533*3126Sahl argc--; 2534*3126Sahl argv++; 2535*3126Sahl } else { 2536*3126Sahl types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME; 2537*3126Sahl } 2538*3126Sahl 2539789Sahrens if (argc != 0) { 2540789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 25412082Seschrock usage(B_FALSE); 2542789Sahrens } 2543789Sahrens 2544*3126Sahl get_all_datasets(types, &dslist, &count); 25451356Seschrock 25461356Seschrock if (count == 0) 25471356Seschrock return (0); 25481356Seschrock 2549*3126Sahl qsort(dslist, count, sizeof (void *), dataset_cmp); 25501356Seschrock 25511356Seschrock for (i = 0; i < count; i++) { 2552*3126Sahl if (share_mount_one(dslist[i], op, flags, B_FALSE, 2553*3126Sahl options) != 0) 25542369Slling ret = 1; 2555*3126Sahl zfs_close(dslist[i]); 25561356Seschrock } 25571356Seschrock 2558*3126Sahl free(dslist); 2559789Sahrens } else if (argc == 0) { 2560789Sahrens struct mnttab entry; 2561789Sahrens 2562*3126Sahl if (op == OP_SHARE) { 2563789Sahrens (void) fprintf(stderr, gettext("missing filesystem " 2564789Sahrens "argument\n")); 25652082Seschrock usage(B_FALSE); 2566789Sahrens } 2567789Sahrens 2568789Sahrens /* 2569789Sahrens * When mount is given no arguments, go through /etc/mnttab and 2570789Sahrens * display any active ZFS mounts. We hide any snapshots, since 2571789Sahrens * they are controlled automatically. 2572789Sahrens */ 2573789Sahrens rewind(mnttab_file); 2574789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2575789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 2576789Sahrens strchr(entry.mnt_special, '@') != NULL) 2577789Sahrens continue; 2578789Sahrens 2579789Sahrens (void) printf("%-30s %s\n", entry.mnt_special, 2580789Sahrens entry.mnt_mountp); 2581789Sahrens } 2582789Sahrens 2583789Sahrens } else { 2584789Sahrens zfs_handle_t *zhp; 2585789Sahrens 2586*3126Sahl types = ZFS_TYPE_FILESYSTEM; 2587*3126Sahl if (op == OP_SHARE) 2588*3126Sahl types |= ZFS_TYPE_VOLUME; 2589*3126Sahl 2590789Sahrens if (argc > 1) { 2591789Sahrens (void) fprintf(stderr, 2592789Sahrens gettext("too many arguments\n")); 25932082Seschrock usage(B_FALSE); 2594789Sahrens } 2595789Sahrens 2596*3126Sahl if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) { 2597789Sahrens ret = 1; 2598*3126Sahl } else { 2599*3126Sahl ret = share_mount_one(zhp, op, flags, B_TRUE, 2600*3126Sahl options); 2601789Sahrens zfs_close(zhp); 2602789Sahrens } 2603789Sahrens } 2604789Sahrens 2605789Sahrens return (ret); 2606789Sahrens } 2607789Sahrens 2608789Sahrens /* 2609*3126Sahl * zfs mount -a [nfs | iscsi] 2610789Sahrens * zfs mount filesystem 2611789Sahrens * 2612789Sahrens * Mount all filesystems, or mount the given filesystem. 2613789Sahrens */ 2614789Sahrens static int 2615789Sahrens zfs_do_mount(int argc, char **argv) 2616789Sahrens { 2617*3126Sahl return (share_mount(OP_MOUNT, argc, argv)); 2618789Sahrens } 2619789Sahrens 2620789Sahrens /* 2621*3126Sahl * zfs share -a [nfs | iscsi] 2622789Sahrens * zfs share filesystem 2623789Sahrens * 2624789Sahrens * Share all filesystems, or share the given filesystem. 2625789Sahrens */ 2626789Sahrens static int 2627789Sahrens zfs_do_share(int argc, char **argv) 2628789Sahrens { 2629*3126Sahl return (share_mount(OP_SHARE, argc, argv)); 2630789Sahrens } 2631789Sahrens 2632789Sahrens typedef struct unshare_unmount_node { 2633789Sahrens zfs_handle_t *un_zhp; 2634789Sahrens char *un_mountp; 2635789Sahrens uu_avl_node_t un_avlnode; 2636789Sahrens } unshare_unmount_node_t; 2637789Sahrens 2638789Sahrens /* ARGSUSED */ 2639789Sahrens static int 2640789Sahrens unshare_unmount_compare(const void *larg, const void *rarg, void *unused) 2641789Sahrens { 2642789Sahrens const unshare_unmount_node_t *l = larg; 2643789Sahrens const unshare_unmount_node_t *r = rarg; 2644789Sahrens 2645789Sahrens return (strcmp(l->un_mountp, r->un_mountp)); 2646789Sahrens } 2647789Sahrens 2648789Sahrens /* 2649789Sahrens * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 2650789Sahrens * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 2651789Sahrens * and unmount it appropriately. 2652789Sahrens */ 2653789Sahrens static int 2654*3126Sahl unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) 2655789Sahrens { 2656789Sahrens zfs_handle_t *zhp; 2657789Sahrens int ret; 2658789Sahrens struct stat64 statbuf; 2659789Sahrens struct extmnttab entry; 2660*3126Sahl const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount"; 2661789Sahrens char property[ZFS_MAXPROPLEN]; 2662789Sahrens 2663789Sahrens /* 2664789Sahrens * Search for the path in /etc/mnttab. Rather than looking for the 2665789Sahrens * specific path, which can be fooled by non-standard paths (i.e. ".." 2666789Sahrens * or "//"), we stat() the path and search for the corresponding 2667789Sahrens * (major,minor) device pair. 2668789Sahrens */ 2669789Sahrens if (stat64(path, &statbuf) != 0) { 2670789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 2671789Sahrens cmdname, path, strerror(errno)); 2672789Sahrens return (1); 2673789Sahrens } 2674789Sahrens 2675789Sahrens /* 2676789Sahrens * Search for the given (major,minor) pair in the mount table. 2677789Sahrens */ 2678789Sahrens rewind(mnttab_file); 2679789Sahrens while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 2680789Sahrens if (entry.mnt_major == major(statbuf.st_dev) && 2681789Sahrens entry.mnt_minor == minor(statbuf.st_dev)) 2682789Sahrens break; 2683789Sahrens } 2684789Sahrens if (ret != 0) { 2685789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not " 2686789Sahrens "currently mounted\n"), cmdname, path); 2687789Sahrens return (1); 2688789Sahrens } 2689789Sahrens 2690789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 2691789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 2692789Sahrens "filesystem\n"), cmdname, path); 2693789Sahrens return (1); 2694789Sahrens } 2695789Sahrens 26962082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 26972082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2698789Sahrens return (1); 2699789Sahrens 2700*3126Sahl verify(zfs_prop_get(zhp, op == OP_SHARE ? 2701789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 27022082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2703789Sahrens 2704*3126Sahl if (op == OP_SHARE) { 2705789Sahrens if (strcmp(property, "off") == 0) { 2706789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2707789Sahrens "'%s': legacy share\n"), path); 2708789Sahrens (void) fprintf(stderr, gettext("use " 2709789Sahrens "unshare(1M) to unshare this filesystem\n")); 2710789Sahrens ret = 1; 2711*3126Sahl } else if (!zfs_is_shared_nfs(zhp, NULL)) { 2712789Sahrens (void) fprintf(stderr, gettext("cannot unshare '%s': " 2713789Sahrens "not currently shared\n"), path); 2714789Sahrens ret = 1; 2715789Sahrens } else { 2716*3126Sahl ret = zfs_unshareall_nfs(zhp); 2717789Sahrens } 2718789Sahrens } else { 27191264Slling if (is_manual) { 27201264Slling ret = zfs_unmount(zhp, NULL, flags); 27211264Slling } else if (strcmp(property, "legacy") == 0) { 27221264Slling (void) fprintf(stderr, gettext("cannot unmount " 27231264Slling "'%s': legacy mountpoint\n"), 27241264Slling zfs_get_name(zhp)); 27251264Slling (void) fprintf(stderr, gettext("use umount(1M) " 27261264Slling "to unmount this filesystem\n")); 27271264Slling ret = 1; 2728789Sahrens } else { 2729789Sahrens ret = zfs_unmountall(zhp, flags); 2730789Sahrens } 2731789Sahrens } 2732789Sahrens 2733789Sahrens zfs_close(zhp); 2734789Sahrens 2735789Sahrens return (ret != 0); 2736789Sahrens } 2737789Sahrens 2738789Sahrens /* 2739789Sahrens * Generic callback for unsharing or unmounting a filesystem. 2740789Sahrens */ 2741789Sahrens static int 2742*3126Sahl unshare_unmount(int op, int argc, char **argv) 2743789Sahrens { 2744789Sahrens int do_all = 0; 2745789Sahrens int flags = 0; 2746789Sahrens int ret = 0; 2747*3126Sahl int types, c; 2748789Sahrens zfs_handle_t *zhp; 2749789Sahrens char property[ZFS_MAXPROPLEN]; 2750789Sahrens 2751789Sahrens /* check options */ 2752*3126Sahl while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) { 2753789Sahrens switch (c) { 2754789Sahrens case 'a': 2755789Sahrens do_all = 1; 2756789Sahrens break; 2757789Sahrens case 'f': 2758789Sahrens flags = MS_FORCE; 2759789Sahrens break; 2760789Sahrens case '?': 2761789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2762789Sahrens optopt); 27632082Seschrock usage(B_FALSE); 2764789Sahrens } 2765789Sahrens } 2766789Sahrens 2767789Sahrens argc -= optind; 2768789Sahrens argv += optind; 2769789Sahrens 2770789Sahrens if (do_all) { 2771789Sahrens /* 2772789Sahrens * We could make use of zfs_for_each() to walk all datasets in 2773789Sahrens * the system, but this would be very inefficient, especially 2774789Sahrens * since we would have to linearly search /etc/mnttab for each 2775789Sahrens * one. Instead, do one pass through /etc/mnttab looking for 2776789Sahrens * zfs entries and call zfs_unmount() for each one. 2777789Sahrens * 2778789Sahrens * Things get a little tricky if the administrator has created 2779789Sahrens * mountpoints beneath other ZFS filesystems. In this case, we 2780789Sahrens * have to unmount the deepest filesystems first. To accomplish 2781789Sahrens * this, we place all the mountpoints in an AVL tree sorted by 2782789Sahrens * the special type (dataset name), and walk the result in 2783789Sahrens * reverse to make sure to get any snapshots first. 2784789Sahrens */ 2785789Sahrens struct mnttab entry; 2786789Sahrens uu_avl_pool_t *pool; 2787789Sahrens uu_avl_t *tree; 2788789Sahrens unshare_unmount_node_t *node; 2789789Sahrens uu_avl_index_t idx; 2790789Sahrens uu_avl_walk_t *walk; 2791789Sahrens 2792*3126Sahl if (argc != 0) { 2793*3126Sahl (void) fprintf(stderr, gettext("too many arguments\n")); 2794*3126Sahl usage(B_FALSE); 2795*3126Sahl } 2796*3126Sahl 2797789Sahrens if ((pool = uu_avl_pool_create("unmount_pool", 2798789Sahrens sizeof (unshare_unmount_node_t), 2799789Sahrens offsetof(unshare_unmount_node_t, un_avlnode), 2800789Sahrens unshare_unmount_compare, 2801789Sahrens UU_DEFAULT)) == NULL) { 2802789Sahrens (void) fprintf(stderr, gettext("internal error: " 2803789Sahrens "out of memory\n")); 2804789Sahrens exit(1); 2805789Sahrens } 2806789Sahrens 2807789Sahrens if ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL) { 2808789Sahrens (void) fprintf(stderr, gettext("internal error: " 2809789Sahrens "out of memory\n")); 2810789Sahrens exit(1); 2811789Sahrens } 2812789Sahrens 2813789Sahrens rewind(mnttab_file); 2814789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2815789Sahrens 2816789Sahrens /* ignore non-ZFS entries */ 2817789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 2818789Sahrens continue; 2819789Sahrens 2820789Sahrens /* ignore snapshots */ 2821789Sahrens if (strchr(entry.mnt_special, '@') != NULL) 2822789Sahrens continue; 2823789Sahrens 28242082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 2825789Sahrens ZFS_TYPE_FILESYSTEM)) == NULL) { 2826789Sahrens ret = 1; 2827789Sahrens continue; 2828789Sahrens } 2829789Sahrens 2830*3126Sahl verify(zfs_prop_get(zhp, op == OP_SHARE ? 2831789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 2832789Sahrens property, sizeof (property), NULL, NULL, 28332082Seschrock 0, B_FALSE) == 0); 2834789Sahrens 2835789Sahrens /* Ignore legacy mounts and shares */ 2836*3126Sahl if ((op == OP_SHARE && 2837789Sahrens strcmp(property, "off") == 0) || 2838*3126Sahl (op == OP_MOUNT && 2839789Sahrens strcmp(property, "legacy") == 0)) { 2840789Sahrens zfs_close(zhp); 2841789Sahrens continue; 2842789Sahrens } 2843789Sahrens 2844789Sahrens node = safe_malloc(sizeof (unshare_unmount_node_t)); 2845789Sahrens node->un_zhp = zhp; 2846789Sahrens 2847789Sahrens if ((node->un_mountp = strdup(entry.mnt_mountp)) == 2848789Sahrens NULL) { 2849789Sahrens (void) fprintf(stderr, gettext("internal error:" 2850789Sahrens " out of memory\n")); 2851789Sahrens exit(1); 2852789Sahrens } 2853789Sahrens 2854789Sahrens uu_avl_node_init(node, &node->un_avlnode, pool); 2855789Sahrens 2856789Sahrens if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 2857789Sahrens uu_avl_insert(tree, node, idx); 2858789Sahrens } else { 2859789Sahrens zfs_close(node->un_zhp); 2860789Sahrens free(node->un_mountp); 2861789Sahrens free(node); 2862789Sahrens } 2863789Sahrens } 2864789Sahrens 2865789Sahrens /* 2866789Sahrens * Walk the AVL tree in reverse, unmounting each filesystem and 2867789Sahrens * removing it from the AVL tree in the process. 2868789Sahrens */ 2869789Sahrens if ((walk = uu_avl_walk_start(tree, 2870789Sahrens UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) { 2871789Sahrens (void) fprintf(stderr, 2872789Sahrens gettext("internal error: out of memory")); 2873789Sahrens exit(1); 2874789Sahrens } 2875789Sahrens 2876789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 2877789Sahrens uu_avl_remove(tree, node); 2878789Sahrens 2879*3126Sahl switch (op) { 2880789Sahrens case OP_SHARE: 2881*3126Sahl if (zfs_unshare_nfs(node->un_zhp, 2882789Sahrens node->un_mountp) != 0) 2883789Sahrens ret = 1; 2884789Sahrens break; 2885789Sahrens 2886789Sahrens case OP_MOUNT: 2887789Sahrens if (zfs_unmount(node->un_zhp, 2888789Sahrens node->un_mountp, flags) != 0) 2889789Sahrens ret = 1; 2890789Sahrens break; 2891789Sahrens } 2892789Sahrens 2893789Sahrens zfs_close(node->un_zhp); 2894789Sahrens free(node->un_mountp); 2895789Sahrens free(node); 2896789Sahrens } 2897789Sahrens 2898789Sahrens uu_avl_walk_end(walk); 2899789Sahrens uu_avl_destroy(tree); 2900789Sahrens uu_avl_pool_destroy(pool); 2901*3126Sahl 2902*3126Sahl if (op == OP_SHARE) { 2903*3126Sahl /* 2904*3126Sahl * Finally, unshare any volumes shared via iSCSI. 2905*3126Sahl */ 2906*3126Sahl zfs_handle_t **dslist = NULL; 2907*3126Sahl size_t i, count = 0; 2908*3126Sahl 2909*3126Sahl get_all_datasets(ZFS_TYPE_VOLUME, &dslist, &count); 2910*3126Sahl 2911*3126Sahl if (count != 0) { 2912*3126Sahl qsort(dslist, count, sizeof (void *), 2913*3126Sahl dataset_cmp); 2914*3126Sahl 2915*3126Sahl for (i = 0; i < count; i++) { 2916*3126Sahl if (zfs_unshare_iscsi(dslist[i]) != 0) 2917*3126Sahl ret = 1; 2918*3126Sahl zfs_close(dslist[i]); 2919*3126Sahl } 2920*3126Sahl 2921*3126Sahl free(dslist); 2922*3126Sahl } 2923*3126Sahl } 2924789Sahrens } else { 2925*3126Sahl if (argc != 1) { 2926*3126Sahl if (argc == 0) 2927*3126Sahl (void) fprintf(stderr, 2928*3126Sahl gettext("missing filesystem argument\n")); 2929*3126Sahl else 2930*3126Sahl (void) fprintf(stderr, 2931*3126Sahl gettext("too many arguments\n")); 2932*3126Sahl usage(B_FALSE); 2933*3126Sahl } 2934*3126Sahl 2935789Sahrens /* 2936789Sahrens * We have an argument, but it may be a full path or a ZFS 2937789Sahrens * filesystem. Pass full paths off to unmount_path() (shared by 2938789Sahrens * manual_unmount), otherwise open the filesystem and pass to 2939789Sahrens * zfs_unmount(). 2940789Sahrens */ 2941789Sahrens if (argv[0][0] == '/') 2942*3126Sahl return (unshare_unmount_path(op, argv[0], 29432082Seschrock flags, B_FALSE)); 29442082Seschrock 2945*3126Sahl types = ZFS_TYPE_FILESYSTEM; 2946*3126Sahl if (op == OP_SHARE) 2947*3126Sahl types |= ZFS_TYPE_VOLUME; 2948*3126Sahl 2949*3126Sahl if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) 2950789Sahrens return (1); 2951789Sahrens 2952*3126Sahl if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 2953*3126Sahl verify(zfs_prop_get(zhp, op == OP_SHARE ? 2954*3126Sahl ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 2955*3126Sahl sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2956*3126Sahl 2957*3126Sahl switch (op) { 2958*3126Sahl case OP_SHARE: 2959*3126Sahl if (strcmp(property, "off") == 0) { 2960*3126Sahl (void) fprintf(stderr, gettext("cannot " 2961*3126Sahl "unshare '%s': legacy share\n"), 2962*3126Sahl zfs_get_name(zhp)); 2963*3126Sahl (void) fprintf(stderr, gettext("use " 2964*3126Sahl "unshare(1M) to unshare this " 2965*3126Sahl "filesystem\n")); 2966*3126Sahl ret = 1; 2967*3126Sahl } else if (!zfs_is_shared_nfs(zhp, NULL)) { 2968*3126Sahl (void) fprintf(stderr, gettext("cannot " 2969*3126Sahl "unshare '%s': not currently " 2970*3126Sahl "shared\n"), zfs_get_name(zhp)); 2971*3126Sahl ret = 1; 2972*3126Sahl } else if (zfs_unshareall_nfs(zhp) != 0) { 2973*3126Sahl ret = 1; 2974*3126Sahl } 2975*3126Sahl break; 2976*3126Sahl 2977*3126Sahl case OP_MOUNT: 2978*3126Sahl if (strcmp(property, "legacy") == 0) { 2979*3126Sahl (void) fprintf(stderr, gettext("cannot " 2980*3126Sahl "unmount '%s': legacy " 2981*3126Sahl "mountpoint\n"), zfs_get_name(zhp)); 2982*3126Sahl (void) fprintf(stderr, gettext("use " 2983*3126Sahl "umount(1M) to unmount this " 2984*3126Sahl "filesystem\n")); 2985*3126Sahl ret = 1; 2986*3126Sahl } else if (!zfs_is_mounted(zhp, NULL)) { 2987*3126Sahl (void) fprintf(stderr, gettext("cannot " 2988*3126Sahl "unmount '%s': not currently " 2989*3126Sahl "mounted\n"), 2990*3126Sahl zfs_get_name(zhp)); 2991*3126Sahl ret = 1; 2992*3126Sahl } else if (zfs_unmountall(zhp, flags) != 0) { 2993*3126Sahl ret = 1; 2994*3126Sahl } 2995*3126Sahl break; 2996*3126Sahl } 2997*3126Sahl } else { 2998*3126Sahl assert(op == OP_SHARE); 2999*3126Sahl 3000*3126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, property, 3001*3126Sahl sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 3002*3126Sahl 3003789Sahrens if (strcmp(property, "off") == 0) { 3004789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 3005*3126Sahl "'%s': 'shareiscsi' property not set\n"), 3006*3126Sahl zfs_get_name(zhp)); 3007*3126Sahl (void) fprintf(stderr, gettext("set " 3008*3126Sahl "'shareiscsi' property or use " 3009*3126Sahl "iscsitadm(1M) to share this volume\n")); 3010789Sahrens ret = 1; 3011*3126Sahl } else if (!zfs_is_shared_iscsi(zhp)) { 3012*3126Sahl (void) fprintf(stderr, gettext("cannot " 3013*3126Sahl "unshare '%s': not currently shared\n"), 3014789Sahrens zfs_get_name(zhp)); 3015789Sahrens ret = 1; 3016*3126Sahl } else if (zfs_unshare_iscsi(zhp) != 0) { 3017789Sahrens ret = 1; 3018789Sahrens } 3019789Sahrens } 3020789Sahrens 3021789Sahrens zfs_close(zhp); 3022789Sahrens } 3023789Sahrens 3024789Sahrens return (ret); 3025789Sahrens } 3026789Sahrens 3027789Sahrens /* 3028789Sahrens * zfs unmount -a 3029789Sahrens * zfs unmount filesystem 3030789Sahrens * 3031789Sahrens * Unmount all filesystems, or a specific ZFS filesystem. 3032789Sahrens */ 3033789Sahrens static int 3034789Sahrens zfs_do_unmount(int argc, char **argv) 3035789Sahrens { 3036789Sahrens return (unshare_unmount(OP_MOUNT, argc, argv)); 3037789Sahrens } 3038789Sahrens 3039789Sahrens /* 3040789Sahrens * zfs unshare -a 3041789Sahrens * zfs unshare filesystem 3042789Sahrens * 3043789Sahrens * Unshare all filesystems, or a specific ZFS filesystem. 3044789Sahrens */ 3045789Sahrens static int 3046789Sahrens zfs_do_unshare(int argc, char **argv) 3047789Sahrens { 3048789Sahrens return (unshare_unmount(OP_SHARE, argc, argv)); 3049789Sahrens } 3050789Sahrens 3051789Sahrens /* 3052789Sahrens * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 3053789Sahrens * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 3054789Sahrens */ 3055789Sahrens static int 3056789Sahrens manual_mount(int argc, char **argv) 3057789Sahrens { 3058789Sahrens zfs_handle_t *zhp; 3059789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 3060789Sahrens char mntopts[MNT_LINE_MAX] = { '\0' }; 3061789Sahrens int ret; 3062789Sahrens int c; 3063789Sahrens int flags = 0; 3064789Sahrens char *dataset, *path; 3065789Sahrens 3066789Sahrens /* check options */ 30671544Seschrock while ((c = getopt(argc, argv, ":mo:O")) != -1) { 3068789Sahrens switch (c) { 3069789Sahrens case 'o': 3070789Sahrens (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 3071789Sahrens break; 3072789Sahrens case 'O': 3073789Sahrens flags |= MS_OVERLAY; 3074789Sahrens break; 30751544Seschrock case 'm': 30761544Seschrock flags |= MS_NOMNTTAB; 30771544Seschrock break; 3078789Sahrens case ':': 3079789Sahrens (void) fprintf(stderr, gettext("missing argument for " 3080789Sahrens "'%c' option\n"), optopt); 30812082Seschrock usage(B_FALSE); 3082789Sahrens break; 3083789Sahrens case '?': 3084789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3085789Sahrens optopt); 3086789Sahrens (void) fprintf(stderr, gettext("usage: mount [-o opts] " 3087789Sahrens "<path>\n")); 3088789Sahrens return (2); 3089789Sahrens } 3090789Sahrens } 3091789Sahrens 3092789Sahrens argc -= optind; 3093789Sahrens argv += optind; 3094789Sahrens 3095789Sahrens /* check that we only have two arguments */ 3096789Sahrens if (argc != 2) { 3097789Sahrens if (argc == 0) 3098789Sahrens (void) fprintf(stderr, gettext("missing dataset " 3099789Sahrens "argument\n")); 3100789Sahrens else if (argc == 1) 3101789Sahrens (void) fprintf(stderr, 3102789Sahrens gettext("missing mountpoint argument\n")); 3103789Sahrens else 3104789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 3105789Sahrens (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 3106789Sahrens return (2); 3107789Sahrens } 3108789Sahrens 3109789Sahrens dataset = argv[0]; 3110789Sahrens path = argv[1]; 3111789Sahrens 3112789Sahrens /* try to open the dataset */ 31132082Seschrock if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 3114789Sahrens return (1); 3115789Sahrens 3116789Sahrens (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 31172082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE); 3118789Sahrens 3119789Sahrens /* check for legacy mountpoint and complain appropriately */ 3120789Sahrens ret = 0; 3121789Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 3122789Sahrens if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS, 3123789Sahrens NULL, 0, mntopts, sizeof (mntopts)) != 0) { 3124789Sahrens (void) fprintf(stderr, gettext("mount failed: %s\n"), 3125789Sahrens strerror(errno)); 3126789Sahrens ret = 1; 3127789Sahrens } 3128789Sahrens } else { 3129789Sahrens (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 3130789Sahrens "mounted using 'mount -F zfs'\n"), dataset); 3131789Sahrens (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 3132789Sahrens "instead.\n"), path); 3133789Sahrens (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' " 3134789Sahrens "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n")); 3135789Sahrens (void) fprintf(stderr, gettext("See zfs(1M) for more " 3136789Sahrens "information.\n")); 3137789Sahrens ret = 1; 3138789Sahrens } 3139789Sahrens 3140789Sahrens return (ret); 3141789Sahrens } 3142789Sahrens 3143789Sahrens /* 3144789Sahrens * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 3145789Sahrens * unmounts of non-legacy filesystems, as this is the dominant administrative 3146789Sahrens * interface. 3147789Sahrens */ 3148789Sahrens static int 3149789Sahrens manual_unmount(int argc, char **argv) 3150789Sahrens { 3151789Sahrens int flags = 0; 3152789Sahrens int c; 3153789Sahrens 3154789Sahrens /* check options */ 3155789Sahrens while ((c = getopt(argc, argv, "f")) != -1) { 3156789Sahrens switch (c) { 3157789Sahrens case 'f': 3158789Sahrens flags = MS_FORCE; 3159789Sahrens break; 3160789Sahrens case '?': 3161789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3162789Sahrens optopt); 3163789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] " 3164789Sahrens "<path>\n")); 3165789Sahrens return (2); 3166789Sahrens } 3167789Sahrens } 3168789Sahrens 3169789Sahrens argc -= optind; 3170789Sahrens argv += optind; 3171789Sahrens 3172789Sahrens /* check arguments */ 3173789Sahrens if (argc != 1) { 3174789Sahrens if (argc == 0) 3175789Sahrens (void) fprintf(stderr, gettext("missing path " 3176789Sahrens "argument\n")); 3177789Sahrens else 3178789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 3179789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 3180789Sahrens return (2); 3181789Sahrens } 3182789Sahrens 31832082Seschrock return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE)); 3184789Sahrens } 3185789Sahrens 3186789Sahrens static int 3187789Sahrens volcheck(zpool_handle_t *zhp, void *data) 3188789Sahrens { 31892856Snd150628 boolean_t isinit = *((boolean_t *)data); 3190789Sahrens 3191789Sahrens if (isinit) 3192789Sahrens return (zpool_create_zvol_links(zhp)); 3193789Sahrens else 3194789Sahrens return (zpool_remove_zvol_links(zhp)); 3195789Sahrens } 3196789Sahrens 3197789Sahrens /* 3198789Sahrens * Iterate over all pools in the system and either create or destroy /dev/zvol 3199789Sahrens * links, depending on the value of 'isinit'. 3200789Sahrens */ 3201789Sahrens static int 32022082Seschrock do_volcheck(boolean_t isinit) 3203789Sahrens { 32042856Snd150628 return (zpool_iter(g_zfs, volcheck, &isinit) ? 1 : 0); 3205789Sahrens } 3206789Sahrens 3207789Sahrens int 3208789Sahrens main(int argc, char **argv) 3209789Sahrens { 3210789Sahrens int ret; 3211789Sahrens int i; 3212789Sahrens char *progname; 3213789Sahrens char *cmdname; 3214789Sahrens 3215789Sahrens (void) setlocale(LC_ALL, ""); 3216789Sahrens (void) textdomain(TEXT_DOMAIN); 3217789Sahrens 3218789Sahrens opterr = 0; 3219789Sahrens 32202082Seschrock if ((g_zfs = libzfs_init()) == NULL) { 32212082Seschrock (void) fprintf(stderr, gettext("internal error: failed to " 32222082Seschrock "initialize ZFS library\n")); 32232082Seschrock return (1); 32242082Seschrock } 32252082Seschrock 32262082Seschrock libzfs_print_on_error(g_zfs, B_TRUE); 32272082Seschrock 3228789Sahrens if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 3229789Sahrens (void) fprintf(stderr, gettext("internal error: unable to " 3230789Sahrens "open %s\n"), MNTTAB); 3231789Sahrens return (1); 3232789Sahrens } 3233789Sahrens 3234789Sahrens /* 3235789Sahrens * This command also doubles as the /etc/fs mount and unmount program. 3236789Sahrens * Determine if we should take this behavior based on argv[0]. 3237789Sahrens */ 3238789Sahrens progname = basename(argv[0]); 3239789Sahrens if (strcmp(progname, "mount") == 0) { 3240789Sahrens ret = manual_mount(argc, argv); 3241789Sahrens } else if (strcmp(progname, "umount") == 0) { 3242789Sahrens ret = manual_unmount(argc, argv); 3243789Sahrens } else { 3244789Sahrens /* 3245789Sahrens * Make sure the user has specified some command. 3246789Sahrens */ 3247789Sahrens if (argc < 2) { 3248789Sahrens (void) fprintf(stderr, gettext("missing command\n")); 32492082Seschrock usage(B_FALSE); 3250789Sahrens } 3251789Sahrens 3252789Sahrens cmdname = argv[1]; 3253789Sahrens 3254789Sahrens /* 3255789Sahrens * The 'umount' command is an alias for 'unmount' 3256789Sahrens */ 3257789Sahrens if (strcmp(cmdname, "umount") == 0) 3258789Sahrens cmdname = "unmount"; 3259789Sahrens 3260789Sahrens /* 32611749Sahrens * The 'recv' command is an alias for 'receive' 32621749Sahrens */ 32631749Sahrens if (strcmp(cmdname, "recv") == 0) 32641749Sahrens cmdname = "receive"; 32651749Sahrens 32661749Sahrens /* 3267789Sahrens * Special case '-?' 3268789Sahrens */ 3269789Sahrens if (strcmp(cmdname, "-?") == 0) 32702082Seschrock usage(B_TRUE); 3271789Sahrens 3272789Sahrens /* 3273789Sahrens * 'volinit' and 'volfini' do not appear in the usage message, 3274789Sahrens * so we have to special case them here. 3275789Sahrens */ 3276789Sahrens if (strcmp(cmdname, "volinit") == 0) 32772082Seschrock return (do_volcheck(B_TRUE)); 3278789Sahrens else if (strcmp(cmdname, "volfini") == 0) 32792082Seschrock return (do_volcheck(B_FALSE)); 3280789Sahrens 3281789Sahrens /* 3282789Sahrens * Run the appropriate command. 3283789Sahrens */ 3284789Sahrens for (i = 0; i < NCOMMAND; i++) { 3285789Sahrens if (command_table[i].name == NULL) 3286789Sahrens continue; 3287789Sahrens 3288789Sahrens if (strcmp(cmdname, command_table[i].name) == 0) { 3289789Sahrens current_command = &command_table[i]; 3290789Sahrens ret = command_table[i].func(argc - 1, argv + 1); 3291789Sahrens break; 3292789Sahrens } 3293789Sahrens } 3294789Sahrens 3295789Sahrens if (i == NCOMMAND) { 3296789Sahrens (void) fprintf(stderr, gettext("unrecognized " 3297789Sahrens "command '%s'\n"), cmdname); 32982082Seschrock usage(B_FALSE); 3299789Sahrens } 3300789Sahrens } 3301789Sahrens 3302789Sahrens (void) fclose(mnttab_file); 3303789Sahrens 33042082Seschrock libzfs_fini(g_zfs); 33052082Seschrock 3306789Sahrens /* 3307789Sahrens * The 'ZFS_ABORT' environment variable causes us to dump core on exit 3308789Sahrens * for the purposes of running ::findleaks. 3309789Sahrens */ 3310789Sahrens if (getenv("ZFS_ABORT") != NULL) { 3311789Sahrens (void) printf("dumping core by request\n"); 3312789Sahrens abort(); 3313789Sahrens } 3314789Sahrens 3315789Sahrens return (ret); 3316789Sahrens } 3317