1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 51544Seschrock * Common Development and Distribution License (the "License"). 61544Seschrock * You may not use this file except in compliance with the License. 7789Sahrens * 8789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9789Sahrens * or http://www.opensolaris.org/os/licensing. 10789Sahrens * See the License for the specific language governing permissions 11789Sahrens * and limitations under the License. 12789Sahrens * 13789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15789Sahrens * If applicable, add the following below this CDDL HEADER, with the 16789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18789Sahrens * 19789Sahrens * CDDL HEADER END 20789Sahrens */ 213126Sahl 22789Sahrens /* 233504Sahl * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24789Sahrens * Use is subject to license terms. 25789Sahrens */ 26789Sahrens 27789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 28789Sahrens 29789Sahrens #include <assert.h> 302676Seschrock #include <ctype.h> 31789Sahrens #include <errno.h> 32789Sahrens #include <libgen.h> 33789Sahrens #include <libintl.h> 34789Sahrens #include <libuutil.h> 35*4543Smarks #include <libnvpair.h> 36789Sahrens #include <locale.h> 37789Sahrens #include <stddef.h> 38789Sahrens #include <stdio.h> 39789Sahrens #include <stdlib.h> 40789Sahrens #include <strings.h> 41789Sahrens #include <unistd.h> 42789Sahrens #include <fcntl.h> 43789Sahrens #include <zone.h> 44789Sahrens #include <sys/mkdev.h> 45789Sahrens #include <sys/mntent.h> 46789Sahrens #include <sys/mnttab.h> 47789Sahrens #include <sys/mount.h> 48789Sahrens #include <sys/stat.h> 49*4543Smarks #include <sys/avl.h> 50789Sahrens 51789Sahrens #include <libzfs.h> 52*4543Smarks #include <libuutil.h> 53789Sahrens 54789Sahrens #include "zfs_iter.h" 552082Seschrock #include "zfs_util.h" 562082Seschrock 572082Seschrock libzfs_handle_t *g_zfs; 58789Sahrens 59789Sahrens static FILE *mnttab_file; 60789Sahrens 61789Sahrens static int zfs_do_clone(int argc, char **argv); 62789Sahrens static int zfs_do_create(int argc, char **argv); 63789Sahrens static int zfs_do_destroy(int argc, char **argv); 64789Sahrens static int zfs_do_get(int argc, char **argv); 65789Sahrens static int zfs_do_inherit(int argc, char **argv); 66789Sahrens static int zfs_do_list(int argc, char **argv); 67789Sahrens static int zfs_do_mount(int argc, char **argv); 68789Sahrens static int zfs_do_rename(int argc, char **argv); 69789Sahrens static int zfs_do_rollback(int argc, char **argv); 70789Sahrens static int zfs_do_set(int argc, char **argv); 71789Sahrens static int zfs_do_snapshot(int argc, char **argv); 72789Sahrens static int zfs_do_unmount(int argc, char **argv); 73789Sahrens static int zfs_do_share(int argc, char **argv); 74789Sahrens static int zfs_do_unshare(int argc, char **argv); 751749Sahrens static int zfs_do_send(int argc, char **argv); 761749Sahrens static int zfs_do_receive(int argc, char **argv); 772082Seschrock static int zfs_do_promote(int argc, char **argv); 78*4543Smarks static int zfs_do_allow(int argc, char **argv); 79*4543Smarks static int zfs_do_unallow(int argc, char **argv); 80789Sahrens 81789Sahrens /* 82789Sahrens * These libumem hooks provide a reasonable set of defaults for the allocator's 83789Sahrens * debugging facilities. 84789Sahrens */ 85789Sahrens const char * 863126Sahl _umem_debug_init(void) 87789Sahrens { 88789Sahrens return ("default,verbose"); /* $UMEM_DEBUG setting */ 89789Sahrens } 90789Sahrens 91789Sahrens const char * 92789Sahrens _umem_logging_init(void) 93789Sahrens { 94789Sahrens return ("fail,contents"); /* $UMEM_LOGGING setting */ 95789Sahrens } 96789Sahrens 971387Seschrock typedef enum { 981387Seschrock HELP_CLONE, 991387Seschrock HELP_CREATE, 1001387Seschrock HELP_DESTROY, 1011387Seschrock HELP_GET, 1021387Seschrock HELP_INHERIT, 1031387Seschrock HELP_LIST, 1041387Seschrock HELP_MOUNT, 1052082Seschrock HELP_PROMOTE, 1061749Sahrens HELP_RECEIVE, 1071387Seschrock HELP_RENAME, 1081387Seschrock HELP_ROLLBACK, 1091749Sahrens HELP_SEND, 1101387Seschrock HELP_SET, 1111387Seschrock HELP_SHARE, 1121387Seschrock HELP_SNAPSHOT, 1131387Seschrock HELP_UNMOUNT, 114*4543Smarks HELP_UNSHARE, 115*4543Smarks HELP_ALLOW, 116*4543Smarks HELP_UNALLOW 1171387Seschrock } zfs_help_t; 1181387Seschrock 119789Sahrens typedef struct zfs_command { 120789Sahrens const char *name; 121789Sahrens int (*func)(int argc, char **argv); 1221387Seschrock zfs_help_t usage; 123789Sahrens } zfs_command_t; 124789Sahrens 125789Sahrens /* 126789Sahrens * Master command table. Each ZFS command has a name, associated function, and 1271544Seschrock * usage message. The usage messages need to be internationalized, so we have 1281544Seschrock * to have a function to return the usage message based on a command index. 1291387Seschrock * 1301387Seschrock * These commands are organized according to how they are displayed in the usage 1311387Seschrock * message. An empty command (one with a NULL name) indicates an empty line in 1321387Seschrock * the generic usage message. 133789Sahrens */ 134789Sahrens static zfs_command_t command_table[] = { 1351387Seschrock { "create", zfs_do_create, HELP_CREATE }, 1361387Seschrock { "destroy", zfs_do_destroy, HELP_DESTROY }, 137789Sahrens { NULL }, 1381387Seschrock { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT }, 1391387Seschrock { "rollback", zfs_do_rollback, HELP_ROLLBACK }, 1401387Seschrock { "clone", zfs_do_clone, HELP_CLONE }, 1412082Seschrock { "promote", zfs_do_promote, HELP_PROMOTE }, 1421387Seschrock { "rename", zfs_do_rename, HELP_RENAME }, 143789Sahrens { NULL }, 1441387Seschrock { "list", zfs_do_list, HELP_LIST }, 145789Sahrens { NULL }, 1461387Seschrock { "set", zfs_do_set, HELP_SET }, 1471387Seschrock { "get", zfs_do_get, HELP_GET }, 1481387Seschrock { "inherit", zfs_do_inherit, HELP_INHERIT }, 149789Sahrens { NULL }, 1501387Seschrock { "mount", zfs_do_mount, HELP_MOUNT }, 151789Sahrens { NULL }, 1521387Seschrock { "unmount", zfs_do_unmount, HELP_UNMOUNT }, 153789Sahrens { NULL }, 1541387Seschrock { "share", zfs_do_share, HELP_SHARE }, 155789Sahrens { NULL }, 1561387Seschrock { "unshare", zfs_do_unshare, HELP_UNSHARE }, 157789Sahrens { NULL }, 1581749Sahrens { "send", zfs_do_send, HELP_SEND }, 1591749Sahrens { "receive", zfs_do_receive, HELP_RECEIVE }, 160*4543Smarks { NULL }, 161*4543Smarks { "allow", zfs_do_allow, HELP_ALLOW }, 162*4543Smarks { NULL }, 163*4543Smarks { "unallow", zfs_do_unallow, HELP_UNALLOW }, 164789Sahrens }; 165789Sahrens 166789Sahrens #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) 167789Sahrens 168789Sahrens zfs_command_t *current_command; 169789Sahrens 1701387Seschrock static const char * 1711387Seschrock get_usage(zfs_help_t idx) 1721387Seschrock { 1731387Seschrock switch (idx) { 1741387Seschrock case HELP_CLONE: 1754490Svb160487 return (gettext("\tclone [-p] <snapshot> " 1764490Svb160487 "<filesystem|volume>\n")); 1771387Seschrock case HELP_CREATE: 1784490Svb160487 return (gettext("\tcreate [-p] [[-o property=value] ... ] " 1792676Seschrock "<filesystem>\n" 1804490Svb160487 "\tcreate [-ps] [-b blocksize] [[-o property=value] " 1814490Svb160487 "...]\n" 1822676Seschrock "\t -V <size> <volume>\n")); 1831387Seschrock case HELP_DESTROY: 1841387Seschrock return (gettext("\tdestroy [-rRf] " 1851387Seschrock "<filesystem|volume|snapshot>\n")); 1861387Seschrock case HELP_GET: 1871387Seschrock return (gettext("\tget [-rHp] [-o field[,field]...] " 1881387Seschrock "[-s source[,source]...]\n" 1891387Seschrock "\t <all | property[,property]...> " 1902676Seschrock "[filesystem|volume|snapshot] ...\n")); 1911387Seschrock case HELP_INHERIT: 1921387Seschrock return (gettext("\tinherit [-r] <property> " 1931387Seschrock "<filesystem|volume> ...\n")); 1941387Seschrock case HELP_LIST: 1951387Seschrock return (gettext("\tlist [-rH] [-o property[,property]...] " 1961387Seschrock "[-t type[,type]...]\n" 1972379Ssjelinek "\t [-s property [-s property]...]" 1982379Ssjelinek " [-S property [-S property]...]\n" 1991387Seschrock "\t [filesystem|volume|snapshot] ...\n")); 2001387Seschrock case HELP_MOUNT: 2011387Seschrock return (gettext("\tmount\n" 2021387Seschrock "\tmount [-o opts] [-O] -a\n" 2031387Seschrock "\tmount [-o opts] [-O] <filesystem>\n")); 2042082Seschrock case HELP_PROMOTE: 2052082Seschrock return (gettext("\tpromote <clone filesystem>\n")); 2061749Sahrens case HELP_RECEIVE: 2072665Snd150628 return (gettext("\treceive [-vnF] <filesystem|volume|" 2082665Snd150628 "snapshot>\n" 2092665Snd150628 "\treceive [-vnF] -d <filesystem>\n")); 2101387Seschrock case HELP_RENAME: 2111387Seschrock return (gettext("\trename <filesystem|volume|snapshot> " 2124007Smmusante "<filesystem|volume|snapshot>\n" 2134490Svb160487 "\trename -p <filesystem|volume> <filesystem|volume>\n" 2144007Smmusante "\trename -r <snapshot> <snapshot>")); 2151387Seschrock case HELP_ROLLBACK: 2161387Seschrock return (gettext("\trollback [-rRf] <snapshot>\n")); 2171749Sahrens case HELP_SEND: 2181749Sahrens return (gettext("\tsend [-i <snapshot>] <snapshot>\n")); 2191387Seschrock case HELP_SET: 2201387Seschrock return (gettext("\tset <property=value> " 2211387Seschrock "<filesystem|volume> ...\n")); 2221387Seschrock case HELP_SHARE: 2231387Seschrock return (gettext("\tshare -a\n" 2241387Seschrock "\tshare <filesystem>\n")); 2251387Seschrock case HELP_SNAPSHOT: 2262199Sahrens return (gettext("\tsnapshot [-r] " 2272199Sahrens "<filesystem@name|volume@name>\n")); 2281387Seschrock case HELP_UNMOUNT: 2291387Seschrock return (gettext("\tunmount [-f] -a\n" 2301387Seschrock "\tunmount [-f] <filesystem|mountpoint>\n")); 2311387Seschrock case HELP_UNSHARE: 2321387Seschrock return (gettext("\tunshare [-f] -a\n" 2331387Seschrock "\tunshare [-f] <filesystem|mountpoint>\n")); 234*4543Smarks case HELP_ALLOW: 235*4543Smarks return (gettext("\tallow [-l][-d] <everyone|user|group>[," 236*4543Smarks "<everyone|user|group>...]\n\t " 237*4543Smarks "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" 238*4543Smarks " <filesystem|volume\n" 239*4543Smarks "\tallow [-l] [-d] -u <user> " 240*4543Smarks "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" 241*4543Smarks " <filesystem|volume>\n" 242*4543Smarks "\tallow [-l] [-d] -g <group> " 243*4543Smarks "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" 244*4543Smarks " <filesystem|volume>\n" 245*4543Smarks "\tallow [-l] [-d] -e " 246*4543Smarks "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" 247*4543Smarks " <filesystem|volume>\n" 248*4543Smarks "\tallow -c " 249*4543Smarks "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" 250*4543Smarks " <filesystem|volume>\n" 251*4543Smarks "\tallow -s @setname " 252*4543Smarks "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" 253*4543Smarks " <filesystem|volume>\n")); 254*4543Smarks 255*4543Smarks case HELP_UNALLOW: 256*4543Smarks return (gettext("\tunallow [-r][-l][-d] <everyone|user|group>[," 257*4543Smarks "<everyone|user|group>...] \n\t " 258*4543Smarks "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" 259*4543Smarks " <filesystem|volume>\n" 260*4543Smarks "\tunallow [-r][-l][-d] -u user " 261*4543Smarks "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" 262*4543Smarks " <filesystem|volume>\n" 263*4543Smarks "\tunallow [-r][-l][-d] -g group " 264*4543Smarks "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" 265*4543Smarks " <filesystem|volume>\n" 266*4543Smarks "\tunallow [-r][-l][-d] -e " 267*4543Smarks "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" 268*4543Smarks " <filesystem|volume>\n" 269*4543Smarks "\tunallow [-r] -c " 270*4543Smarks "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" 271*4543Smarks " <filesystem|volume>\n" 272*4543Smarks "\tunallow [-r] -s @setname " 273*4543Smarks "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" 274*4543Smarks " <filesystem|volume> \n\t")); 2751387Seschrock } 2761387Seschrock 2771387Seschrock abort(); 2781387Seschrock /* NOTREACHED */ 2791387Seschrock } 2801387Seschrock 281789Sahrens /* 282789Sahrens * Utility function to guarantee malloc() success. 283789Sahrens */ 284789Sahrens void * 285789Sahrens safe_malloc(size_t size) 286789Sahrens { 287789Sahrens void *data; 288789Sahrens 289789Sahrens if ((data = calloc(1, size)) == NULL) { 290789Sahrens (void) fprintf(stderr, "internal error: out of memory\n"); 291789Sahrens exit(1); 292789Sahrens } 293789Sahrens 294789Sahrens return (data); 295789Sahrens } 296789Sahrens 297789Sahrens /* 2983654Sgw25295 * Callback routinue that will print out information for each of the 2993654Sgw25295 * the properties. 3003654Sgw25295 */ 3013654Sgw25295 static zfs_prop_t 3023654Sgw25295 usage_prop_cb(zfs_prop_t prop, void *cb) 3033654Sgw25295 { 3043654Sgw25295 FILE *fp = cb; 3053654Sgw25295 3063654Sgw25295 (void) fprintf(fp, "\t%-13s ", zfs_prop_to_name(prop)); 3073654Sgw25295 3083654Sgw25295 if (zfs_prop_readonly(prop)) 3093654Sgw25295 (void) fprintf(fp, " NO "); 3103654Sgw25295 else 3113654Sgw25295 (void) fprintf(fp, " YES "); 3123654Sgw25295 3133654Sgw25295 if (zfs_prop_inheritable(prop)) 3143654Sgw25295 (void) fprintf(fp, " YES "); 3153654Sgw25295 else 3163654Sgw25295 (void) fprintf(fp, " NO "); 3173654Sgw25295 3183654Sgw25295 if (zfs_prop_values(prop) == NULL) 3193654Sgw25295 (void) fprintf(fp, "-\n"); 3203654Sgw25295 else 3213654Sgw25295 (void) fprintf(fp, "%s\n", zfs_prop_values(prop)); 3223654Sgw25295 3233654Sgw25295 return (ZFS_PROP_CONT); 3243654Sgw25295 } 3253654Sgw25295 3263654Sgw25295 /* 327789Sahrens * Display usage message. If we're inside a command, display only the usage for 328789Sahrens * that command. Otherwise, iterate over the entire command table and display 329789Sahrens * a complete usage message. 330789Sahrens */ 331789Sahrens static void 3322082Seschrock usage(boolean_t requested) 333789Sahrens { 334789Sahrens int i; 3352082Seschrock boolean_t show_properties = B_FALSE; 336789Sahrens FILE *fp = requested ? stdout : stderr; 337789Sahrens 338789Sahrens if (current_command == NULL) { 339789Sahrens 340789Sahrens (void) fprintf(fp, gettext("usage: zfs command args ...\n")); 341789Sahrens (void) fprintf(fp, 342789Sahrens gettext("where 'command' is one of the following:\n\n")); 343789Sahrens 344789Sahrens for (i = 0; i < NCOMMAND; i++) { 345789Sahrens if (command_table[i].name == NULL) 346789Sahrens (void) fprintf(fp, "\n"); 347789Sahrens else 348789Sahrens (void) fprintf(fp, "%s", 3491387Seschrock get_usage(command_table[i].usage)); 350789Sahrens } 351789Sahrens 352789Sahrens (void) fprintf(fp, gettext("\nEach dataset is of the form: " 353789Sahrens "pool/[dataset/]*dataset[@name]\n")); 354789Sahrens } else { 355789Sahrens (void) fprintf(fp, gettext("usage:\n")); 3561387Seschrock (void) fprintf(fp, "%s", get_usage(current_command->usage)); 357789Sahrens } 358789Sahrens 3592190Sdarrenm if (current_command != NULL && 3602190Sdarrenm (strcmp(current_command->name, "set") == 0 || 361789Sahrens strcmp(current_command->name, "get") == 0 || 362789Sahrens strcmp(current_command->name, "inherit") == 0 || 3632190Sdarrenm strcmp(current_command->name, "list") == 0)) 3642082Seschrock show_properties = B_TRUE; 365789Sahrens 366789Sahrens if (show_properties) { 367789Sahrens 368789Sahrens (void) fprintf(fp, 369789Sahrens gettext("\nThe following properties are supported:\n")); 370789Sahrens 371789Sahrens (void) fprintf(fp, "\n\t%-13s %s %s %s\n\n", 372789Sahrens "PROPERTY", "EDIT", "INHERIT", "VALUES"); 373789Sahrens 3743654Sgw25295 /* Iterate over all properties */ 3753654Sgw25295 (void) zfs_prop_iter(usage_prop_cb, fp, B_FALSE); 3763654Sgw25295 377789Sahrens (void) fprintf(fp, gettext("\nSizes are specified in bytes " 378789Sahrens "with standard units such as K, M, G, etc.\n")); 3792676Seschrock (void) fprintf(fp, gettext("\n\nUser-defined properties can " 3802676Seschrock "be specified by using a name containing a colon (:).\n")); 3812190Sdarrenm } else { 3822190Sdarrenm /* 3832190Sdarrenm * TRANSLATION NOTE: 3842190Sdarrenm * "zfs set|get" must not be localised this is the 3852190Sdarrenm * command name and arguments. 3862190Sdarrenm */ 3872190Sdarrenm (void) fprintf(fp, 3882190Sdarrenm gettext("\nFor the property list, run: zfs set|get\n")); 389789Sahrens } 390789Sahrens 3912676Seschrock /* 3922676Seschrock * See comments at end of main(). 3932676Seschrock */ 3942676Seschrock if (getenv("ZFS_ABORT") != NULL) { 3952676Seschrock (void) printf("dumping core by request\n"); 3962676Seschrock abort(); 3972676Seschrock } 3982676Seschrock 399789Sahrens exit(requested ? 0 : 2); 400789Sahrens } 401789Sahrens 402789Sahrens /* 4034490Svb160487 * zfs clone [-p] <snap> <fs | vol> 404789Sahrens * 405789Sahrens * Given an existing dataset, create a writable copy whose initial contents 406789Sahrens * are the same as the source. The newly created dataset maintains a 407789Sahrens * dependency on the original; the original cannot be destroyed so long as 408789Sahrens * the clone exists. 4094490Svb160487 * 4104490Svb160487 * The '-p' flag creates all the non-existing ancestors of the target first. 411789Sahrens */ 412789Sahrens static int 413789Sahrens zfs_do_clone(int argc, char **argv) 414789Sahrens { 415789Sahrens zfs_handle_t *zhp; 4164490Svb160487 boolean_t parents = B_FALSE; 417789Sahrens int ret; 4184490Svb160487 int c; 419789Sahrens 420789Sahrens /* check options */ 4214490Svb160487 while ((c = getopt(argc, argv, "p")) != -1) { 4224490Svb160487 switch (c) { 4234490Svb160487 case 'p': 4244490Svb160487 parents = B_TRUE; 4254490Svb160487 break; 4264490Svb160487 case '?': 4274490Svb160487 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 4284490Svb160487 optopt); 4294490Svb160487 usage(B_FALSE); 4304490Svb160487 } 431789Sahrens } 432789Sahrens 4334490Svb160487 argc -= optind; 4344490Svb160487 argv += optind; 4354490Svb160487 436789Sahrens /* check number of arguments */ 4374490Svb160487 if (argc < 1) { 438789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 439789Sahrens "argument\n")); 4402082Seschrock usage(B_FALSE); 441789Sahrens } 4424490Svb160487 if (argc < 2) { 443789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 444789Sahrens "argument\n")); 4452082Seschrock usage(B_FALSE); 446789Sahrens } 4474490Svb160487 if (argc > 2) { 448789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 4492082Seschrock usage(B_FALSE); 450789Sahrens } 451789Sahrens 452789Sahrens /* open the source dataset */ 4534490Svb160487 if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 454789Sahrens return (1); 455789Sahrens 4564490Svb160487 if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM | 4574490Svb160487 ZFS_TYPE_VOLUME)) { 4584490Svb160487 /* 4594490Svb160487 * Now create the ancestors of the target dataset. If the 4604490Svb160487 * target already exists and '-p' option was used we should not 4614490Svb160487 * complain. 4624490Svb160487 */ 4634490Svb160487 if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | 4644490Svb160487 ZFS_TYPE_VOLUME)) 4654490Svb160487 return (0); 4664490Svb160487 if (zfs_create_ancestors(g_zfs, argv[1]) != 0) 4674490Svb160487 return (1); 4684490Svb160487 } 4694490Svb160487 470789Sahrens /* pass to libzfs */ 4714490Svb160487 ret = zfs_clone(zhp, argv[1], NULL); 472789Sahrens 473789Sahrens /* create the mountpoint if necessary */ 474789Sahrens if (ret == 0) { 4754490Svb160487 zfs_handle_t *clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_ANY); 476789Sahrens if (clone != NULL) { 477789Sahrens if ((ret = zfs_mount(clone, NULL, 0)) == 0) 478789Sahrens ret = zfs_share(clone); 479789Sahrens zfs_close(clone); 480789Sahrens } 481789Sahrens } 482789Sahrens 483789Sahrens zfs_close(zhp); 484789Sahrens 485789Sahrens return (ret == 0 ? 0 : 1); 486789Sahrens } 487789Sahrens 488789Sahrens /* 4894490Svb160487 * zfs create [-p] [-o prop=value] ... fs 4904490Svb160487 * zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size 491789Sahrens * 492789Sahrens * Create a new dataset. This command can be used to create filesystems 493789Sahrens * and volumes. Snapshot creation is handled by 'zfs snapshot'. 494789Sahrens * For volumes, the user must specify a size to be used. 495789Sahrens * 496789Sahrens * The '-s' flag applies only to volumes, and indicates that we should not try 497789Sahrens * to set the reservation for this volume. By default we set a reservation 498789Sahrens * equal to the size for any volume. 4994490Svb160487 * 5004490Svb160487 * The '-p' flag creates all the non-existing ancestors of the target first. 501789Sahrens */ 502789Sahrens static int 503789Sahrens zfs_do_create(int argc, char **argv) 504789Sahrens { 505789Sahrens zfs_type_t type = ZFS_TYPE_FILESYSTEM; 5062676Seschrock zfs_handle_t *zhp = NULL; 5072676Seschrock uint64_t volsize; 508789Sahrens int c; 5092082Seschrock boolean_t noreserve = B_FALSE; 5104490Svb160487 boolean_t parents = B_FALSE; 5112676Seschrock int ret = 1; 5122676Seschrock nvlist_t *props = NULL; 5132676Seschrock uint64_t intval; 5142676Seschrock char *propname; 5152926Sek110237 char *propval = NULL; 5162926Sek110237 char *strval; 5172676Seschrock 5182676Seschrock if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) { 5192676Seschrock (void) fprintf(stderr, gettext("internal error: " 5202676Seschrock "out of memory\n")); 5212676Seschrock return (1); 5222676Seschrock } 523789Sahrens 524789Sahrens /* check options */ 5254490Svb160487 while ((c = getopt(argc, argv, ":V:b:so:p")) != -1) { 526789Sahrens switch (c) { 527789Sahrens case 'V': 528789Sahrens type = ZFS_TYPE_VOLUME; 5292676Seschrock if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 5302676Seschrock (void) fprintf(stderr, gettext("bad volume " 5312676Seschrock "size '%s': %s\n"), optarg, 5322676Seschrock libzfs_error_description(g_zfs)); 5332676Seschrock goto error; 5342676Seschrock } 5352676Seschrock 5362676Seschrock if (nvlist_add_uint64(props, 5372676Seschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), 5382676Seschrock intval) != 0) { 5392676Seschrock (void) fprintf(stderr, gettext("internal " 5402676Seschrock "error: out of memory\n")); 5412676Seschrock goto error; 5422676Seschrock } 5432676Seschrock volsize = intval; 544789Sahrens break; 5454490Svb160487 case 'p': 5464490Svb160487 parents = B_TRUE; 5474490Svb160487 break; 548789Sahrens case 'b': 5492676Seschrock if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 5502676Seschrock (void) fprintf(stderr, gettext("bad volume " 5512676Seschrock "block size '%s': %s\n"), optarg, 5522676Seschrock libzfs_error_description(g_zfs)); 5532676Seschrock goto error; 5542676Seschrock } 5552676Seschrock 5562676Seschrock if (nvlist_add_uint64(props, 5572676Seschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 5582676Seschrock intval) != 0) { 5592676Seschrock (void) fprintf(stderr, gettext("internal " 5602676Seschrock "error: out of memory\n")); 5612676Seschrock goto error; 5622676Seschrock } 5632676Seschrock break; 5642676Seschrock case 'o': 5652676Seschrock propname = optarg; 5662676Seschrock if ((propval = strchr(propname, '=')) == NULL) { 5672676Seschrock (void) fprintf(stderr, gettext("missing " 5682676Seschrock "'=' for -o option\n")); 5692676Seschrock goto error; 5702676Seschrock } 5712676Seschrock *propval = '\0'; 5722676Seschrock propval++; 5732676Seschrock if (nvlist_lookup_string(props, propname, 5742676Seschrock &strval) == 0) { 5752676Seschrock (void) fprintf(stderr, gettext("property '%s' " 5762676Seschrock "specified multiple times\n"), propname); 5772676Seschrock goto error; 5782676Seschrock } 5792676Seschrock if (nvlist_add_string(props, propname, propval) != 0) { 5802676Seschrock (void) fprintf(stderr, gettext("internal " 5812676Seschrock "error: out of memory\n")); 5822676Seschrock goto error; 5832676Seschrock } 584789Sahrens break; 585789Sahrens case 's': 5862082Seschrock noreserve = B_TRUE; 587789Sahrens break; 588789Sahrens case ':': 589789Sahrens (void) fprintf(stderr, gettext("missing size " 590789Sahrens "argument\n")); 5912676Seschrock goto badusage; 592789Sahrens break; 593789Sahrens case '?': 594789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 595789Sahrens optopt); 5962676Seschrock goto badusage; 597789Sahrens } 598789Sahrens } 599789Sahrens 600789Sahrens if (noreserve && type != ZFS_TYPE_VOLUME) { 601789Sahrens (void) fprintf(stderr, gettext("'-s' can only be used when " 602789Sahrens "creating a volume\n")); 6032676Seschrock goto badusage; 604789Sahrens } 605789Sahrens 606789Sahrens argc -= optind; 607789Sahrens argv += optind; 608789Sahrens 609789Sahrens /* check number of arguments */ 610789Sahrens if (argc == 0) { 611789Sahrens (void) fprintf(stderr, gettext("missing %s argument\n"), 612789Sahrens zfs_type_to_name(type)); 6132676Seschrock goto badusage; 614789Sahrens } 615789Sahrens if (argc > 1) { 616789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 6172676Seschrock goto badusage; 6182676Seschrock } 6192676Seschrock 6202676Seschrock if (type == ZFS_TYPE_VOLUME && !noreserve && 6212676Seschrock nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_RESERVATION), 6222676Seschrock &strval) != 0) { 6232676Seschrock if (nvlist_add_uint64(props, 6242676Seschrock zfs_prop_to_name(ZFS_PROP_RESERVATION), 6252676Seschrock volsize) != 0) { 6262676Seschrock (void) fprintf(stderr, gettext("internal " 6272676Seschrock "error: out of memory\n")); 6282676Seschrock nvlist_free(props); 6292676Seschrock return (1); 6302676Seschrock } 631789Sahrens } 632789Sahrens 6334490Svb160487 if (parents && zfs_name_valid(argv[0], type)) { 6344490Svb160487 /* 6354490Svb160487 * Now create the ancestors of target dataset. If the target 6364490Svb160487 * already exists and '-p' option was used we should not 6374490Svb160487 * complain. 6384490Svb160487 */ 6394490Svb160487 if (zfs_dataset_exists(g_zfs, argv[0], type)) { 6404490Svb160487 ret = 0; 6414490Svb160487 goto error; 6424490Svb160487 } 6434490Svb160487 if (zfs_create_ancestors(g_zfs, argv[0]) != 0) 6444490Svb160487 goto error; 6454490Svb160487 } 6464490Svb160487 647789Sahrens /* pass to libzfs */ 6482676Seschrock if (zfs_create(g_zfs, argv[0], type, props) != 0) 6492676Seschrock goto error; 650789Sahrens 6512082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 6522676Seschrock goto error; 653789Sahrens 654789Sahrens /* 655789Sahrens * Mount and/or share the new filesystem as appropriate. We provide a 656789Sahrens * verbose error message to let the user know that their filesystem was 657789Sahrens * in fact created, even if we failed to mount or share it. 658789Sahrens */ 659789Sahrens if (zfs_mount(zhp, NULL, 0) != 0) { 660789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 661789Sahrens "created, but not mounted\n")); 662789Sahrens ret = 1; 663789Sahrens } else if (zfs_share(zhp) != 0) { 664789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 665789Sahrens "created, but not shared\n")); 666789Sahrens ret = 1; 667789Sahrens } else { 668789Sahrens ret = 0; 669789Sahrens } 670789Sahrens 6712676Seschrock error: 6722676Seschrock if (zhp) 6732676Seschrock zfs_close(zhp); 6742676Seschrock nvlist_free(props); 675789Sahrens return (ret); 6762676Seschrock badusage: 6772676Seschrock nvlist_free(props); 6782676Seschrock usage(B_FALSE); 6792676Seschrock return (2); 680789Sahrens } 681789Sahrens 682789Sahrens /* 683789Sahrens * zfs destroy [-rf] <fs, snap, vol> 684789Sahrens * 685789Sahrens * -r Recursively destroy all children 686789Sahrens * -R Recursively destroy all dependents, including clones 687789Sahrens * -f Force unmounting of any dependents 688789Sahrens * 689789Sahrens * Destroys the given dataset. By default, it will unmount any filesystems, 690789Sahrens * and refuse to destroy a dataset that has any dependents. A dependent can 691789Sahrens * either be a child, or a clone of a child. 692789Sahrens */ 693789Sahrens typedef struct destroy_cbdata { 6942082Seschrock boolean_t cb_first; 695789Sahrens int cb_force; 696789Sahrens int cb_recurse; 697789Sahrens int cb_error; 698789Sahrens int cb_needforce; 699789Sahrens int cb_doclones; 7003265Sahrens boolean_t cb_closezhp; 701789Sahrens zfs_handle_t *cb_target; 7022199Sahrens char *cb_snapname; 703789Sahrens } destroy_cbdata_t; 704789Sahrens 705789Sahrens /* 706789Sahrens * Check for any dependents based on the '-r' or '-R' flags. 707789Sahrens */ 708789Sahrens static int 709789Sahrens destroy_check_dependent(zfs_handle_t *zhp, void *data) 710789Sahrens { 711789Sahrens destroy_cbdata_t *cbp = data; 712789Sahrens const char *tname = zfs_get_name(cbp->cb_target); 713789Sahrens const char *name = zfs_get_name(zhp); 714789Sahrens 715789Sahrens if (strncmp(tname, name, strlen(tname)) == 0 && 716789Sahrens (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { 717789Sahrens /* 718789Sahrens * This is a direct descendant, not a clone somewhere else in 719789Sahrens * the hierarchy. 720789Sahrens */ 721789Sahrens if (cbp->cb_recurse) 722789Sahrens goto out; 723789Sahrens 724789Sahrens if (cbp->cb_first) { 725789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 726789Sahrens "%s has children\n"), 727789Sahrens zfs_get_name(cbp->cb_target), 728789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 729789Sahrens (void) fprintf(stderr, gettext("use '-r' to destroy " 730789Sahrens "the following datasets:\n")); 7312082Seschrock cbp->cb_first = B_FALSE; 732789Sahrens cbp->cb_error = 1; 733789Sahrens } 734789Sahrens 735789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 736789Sahrens } else { 737789Sahrens /* 738789Sahrens * This is a clone. We only want to report this if the '-r' 739789Sahrens * wasn't specified, or the target is a snapshot. 740789Sahrens */ 741789Sahrens if (!cbp->cb_recurse && 742789Sahrens zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) 743789Sahrens goto out; 744789Sahrens 745789Sahrens if (cbp->cb_first) { 746789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 747789Sahrens "%s has dependent clones\n"), 748789Sahrens zfs_get_name(cbp->cb_target), 749789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 750789Sahrens (void) fprintf(stderr, gettext("use '-R' to destroy " 751789Sahrens "the following datasets:\n")); 7522082Seschrock cbp->cb_first = B_FALSE; 753789Sahrens cbp->cb_error = 1; 754789Sahrens } 755789Sahrens 756789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 757789Sahrens } 758789Sahrens 759789Sahrens out: 760789Sahrens zfs_close(zhp); 761789Sahrens return (0); 762789Sahrens } 763789Sahrens 764789Sahrens static int 765789Sahrens destroy_callback(zfs_handle_t *zhp, void *data) 766789Sahrens { 767789Sahrens destroy_cbdata_t *cbp = data; 768789Sahrens 769789Sahrens /* 770789Sahrens * Ignore pools (which we've already flagged as an error before getting 771789Sahrens * here. 772789Sahrens */ 773789Sahrens if (strchr(zfs_get_name(zhp), '/') == NULL && 774789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 775789Sahrens zfs_close(zhp); 776789Sahrens return (0); 777789Sahrens } 778789Sahrens 779789Sahrens /* 780789Sahrens * Bail out on the first error. 781789Sahrens */ 782789Sahrens if (zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0 || 783789Sahrens zfs_destroy(zhp) != 0) { 784789Sahrens zfs_close(zhp); 785789Sahrens return (-1); 786789Sahrens } 787789Sahrens 788789Sahrens zfs_close(zhp); 789789Sahrens return (0); 790789Sahrens } 791789Sahrens 7922199Sahrens static int 7932199Sahrens destroy_snap_clones(zfs_handle_t *zhp, void *arg) 7942199Sahrens { 7952199Sahrens destroy_cbdata_t *cbp = arg; 7962199Sahrens char thissnap[MAXPATHLEN]; 7972199Sahrens zfs_handle_t *szhp; 7983265Sahrens boolean_t closezhp = cbp->cb_closezhp; 7993265Sahrens int rv; 8002199Sahrens 8012199Sahrens (void) snprintf(thissnap, sizeof (thissnap), 8022199Sahrens "%s@%s", zfs_get_name(zhp), cbp->cb_snapname); 8032199Sahrens 8042199Sahrens libzfs_print_on_error(g_zfs, B_FALSE); 8052199Sahrens szhp = zfs_open(g_zfs, thissnap, ZFS_TYPE_SNAPSHOT); 8062199Sahrens libzfs_print_on_error(g_zfs, B_TRUE); 8072199Sahrens if (szhp) { 8082199Sahrens /* 8092199Sahrens * Destroy any clones of this snapshot 8102199Sahrens */ 8112474Seschrock if (zfs_iter_dependents(szhp, B_FALSE, destroy_callback, 8122474Seschrock cbp) != 0) { 8132474Seschrock zfs_close(szhp); 8143265Sahrens if (closezhp) 8153265Sahrens zfs_close(zhp); 8162474Seschrock return (-1); 8172474Seschrock } 8182199Sahrens zfs_close(szhp); 8192199Sahrens } 8202199Sahrens 8213265Sahrens cbp->cb_closezhp = B_TRUE; 8223265Sahrens rv = zfs_iter_filesystems(zhp, destroy_snap_clones, arg); 8233265Sahrens if (closezhp) 8243265Sahrens zfs_close(zhp); 8253265Sahrens return (rv); 8262199Sahrens } 827789Sahrens 828789Sahrens static int 829789Sahrens zfs_do_destroy(int argc, char **argv) 830789Sahrens { 831789Sahrens destroy_cbdata_t cb = { 0 }; 832789Sahrens int c; 833789Sahrens zfs_handle_t *zhp; 8342199Sahrens char *cp; 835789Sahrens 836789Sahrens /* check options */ 837789Sahrens while ((c = getopt(argc, argv, "frR")) != -1) { 838789Sahrens switch (c) { 839789Sahrens case 'f': 840789Sahrens cb.cb_force = 1; 841789Sahrens break; 842789Sahrens case 'r': 843789Sahrens cb.cb_recurse = 1; 844789Sahrens break; 845789Sahrens case 'R': 846789Sahrens cb.cb_recurse = 1; 847789Sahrens cb.cb_doclones = 1; 848789Sahrens break; 849789Sahrens case '?': 850789Sahrens default: 851789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 852789Sahrens optopt); 8532082Seschrock usage(B_FALSE); 854789Sahrens } 855789Sahrens } 856789Sahrens 857789Sahrens argc -= optind; 858789Sahrens argv += optind; 859789Sahrens 860789Sahrens /* check number of arguments */ 861789Sahrens if (argc == 0) { 862789Sahrens (void) fprintf(stderr, gettext("missing path argument\n")); 8632082Seschrock usage(B_FALSE); 864789Sahrens } 865789Sahrens if (argc > 1) { 866789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 8672082Seschrock usage(B_FALSE); 868789Sahrens } 869789Sahrens 8702199Sahrens /* 8712199Sahrens * If we are doing recursive destroy of a snapshot, then the 8722199Sahrens * named snapshot may not exist. Go straight to libzfs. 8732199Sahrens */ 8742199Sahrens if (cb.cb_recurse && (cp = strchr(argv[0], '@'))) { 8752199Sahrens int ret; 8762199Sahrens 8772199Sahrens *cp = '\0'; 8782199Sahrens if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 8792199Sahrens return (1); 8802199Sahrens *cp = '@'; 8812199Sahrens cp++; 8822199Sahrens 8832199Sahrens if (cb.cb_doclones) { 8842199Sahrens cb.cb_snapname = cp; 8852474Seschrock if (destroy_snap_clones(zhp, &cb) != 0) { 8862474Seschrock zfs_close(zhp); 8872474Seschrock return (1); 8882474Seschrock } 8892199Sahrens } 8902199Sahrens 8912199Sahrens ret = zfs_destroy_snaps(zhp, cp); 8922199Sahrens zfs_close(zhp); 8932199Sahrens if (ret) { 8942199Sahrens (void) fprintf(stderr, 8952199Sahrens gettext("no snapshots destroyed\n")); 8962199Sahrens } 8972199Sahrens return (ret != 0); 8982199Sahrens } 8992199Sahrens 9002199Sahrens 901789Sahrens /* Open the given dataset */ 9022082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 903789Sahrens return (1); 904789Sahrens 905789Sahrens cb.cb_target = zhp; 906789Sahrens 907789Sahrens /* 908789Sahrens * Perform an explicit check for pools before going any further. 909789Sahrens */ 910789Sahrens if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 911789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 912789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 913789Sahrens "operation does not apply to pools\n"), 914789Sahrens zfs_get_name(zhp)); 915789Sahrens (void) fprintf(stderr, gettext("use 'zfs destroy -r " 916789Sahrens "%s' to destroy all datasets in the pool\n"), 917789Sahrens zfs_get_name(zhp)); 918789Sahrens (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 919789Sahrens "to destroy the pool itself\n"), zfs_get_name(zhp)); 920789Sahrens zfs_close(zhp); 921789Sahrens return (1); 922789Sahrens } 923789Sahrens 924789Sahrens /* 925789Sahrens * Check for any dependents and/or clones. 926789Sahrens */ 9272082Seschrock cb.cb_first = B_TRUE; 9282474Seschrock if (!cb.cb_doclones && 9292474Seschrock zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent, 9302474Seschrock &cb) != 0) { 9312474Seschrock zfs_close(zhp); 9322474Seschrock return (1); 9332474Seschrock } 9342474Seschrock 9352474Seschrock if (cb.cb_error || 9362474Seschrock zfs_iter_dependents(zhp, B_FALSE, destroy_callback, &cb) != 0) { 937789Sahrens zfs_close(zhp); 938789Sahrens return (1); 939789Sahrens } 940789Sahrens 941789Sahrens /* 9422474Seschrock * Do the real thing. The callback will close the handle regardless of 9432474Seschrock * whether it succeeds or not. 944789Sahrens */ 945*4543Smarks 9462474Seschrock if (destroy_callback(zhp, &cb) != 0) 9472474Seschrock return (1); 9482474Seschrock 9492926Sek110237 9502474Seschrock return (0); 951789Sahrens } 952789Sahrens 953789Sahrens /* 954866Seschrock * zfs get [-rHp] [-o field[,field]...] [-s source[,source]...] 955866Seschrock * < all | property[,property]... > < fs | snap | vol > ... 956789Sahrens * 957789Sahrens * -r recurse over any child datasets 958789Sahrens * -H scripted mode. Headers are stripped, and fields are separated 959789Sahrens * by tabs instead of spaces. 960789Sahrens * -o Set of fields to display. One of "name,property,value,source". 961789Sahrens * Default is all four. 962789Sahrens * -s Set of sources to allow. One of 963789Sahrens * "local,default,inherited,temporary,none". Default is all 964789Sahrens * five. 965789Sahrens * -p Display values in parsable (literal) format. 966789Sahrens * 967789Sahrens * Prints properties for the given datasets. The user can control which 968789Sahrens * columns to display as well as which property types to allow. 969789Sahrens */ 970789Sahrens 971789Sahrens /* 972789Sahrens * Invoked to display the properties for a single dataset. 973789Sahrens */ 974789Sahrens static int 975789Sahrens get_callback(zfs_handle_t *zhp, void *data) 976789Sahrens { 977789Sahrens char buf[ZFS_MAXPROPLEN]; 978789Sahrens zfs_source_t sourcetype; 979789Sahrens char source[ZFS_MAXNAMELEN]; 9803912Slling libzfs_get_cbdata_t *cbp = data; 9812676Seschrock nvlist_t *userprop = zfs_get_user_props(zhp); 9822676Seschrock zfs_proplist_t *pl = cbp->cb_proplist; 9832676Seschrock nvlist_t *propval; 9842676Seschrock char *strval; 9852676Seschrock char *sourceval; 9862676Seschrock 9872676Seschrock for (; pl != NULL; pl = pl->pl_next) { 9882676Seschrock /* 9892676Seschrock * Skip the special fake placeholder. This will also skip over 9902676Seschrock * the name property when 'all' is specified. 9912676Seschrock */ 9922676Seschrock if (pl->pl_prop == ZFS_PROP_NAME && 9932676Seschrock pl == cbp->cb_proplist) 9942676Seschrock continue; 9952676Seschrock 9962676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 9972676Seschrock if (zfs_prop_get(zhp, pl->pl_prop, buf, 9982676Seschrock sizeof (buf), &sourcetype, source, 9992676Seschrock sizeof (source), 10002676Seschrock cbp->cb_literal) != 0) { 10012676Seschrock if (pl->pl_all) 10022676Seschrock continue; 10033912Slling if (!zfs_prop_valid_for_type(pl->pl_prop, 10043912Slling ZFS_TYPE_ANY)) { 10053912Slling (void) fprintf(stderr, 10063912Slling gettext("No such property '%s'\n"), 10073912Slling zfs_prop_to_name(pl->pl_prop)); 10083912Slling continue; 10093912Slling } 10102676Seschrock sourcetype = ZFS_SRC_NONE; 10112676Seschrock (void) strlcpy(buf, "-", sizeof (buf)); 10122676Seschrock } 10132676Seschrock 10143912Slling libzfs_print_one_property(zfs_get_name(zhp), cbp, 10152676Seschrock zfs_prop_to_name(pl->pl_prop), 10162676Seschrock buf, sourcetype, source); 10172676Seschrock } else { 10182676Seschrock if (nvlist_lookup_nvlist(userprop, 10192676Seschrock pl->pl_user_prop, &propval) != 0) { 10202676Seschrock if (pl->pl_all) 10212676Seschrock continue; 10222676Seschrock sourcetype = ZFS_SRC_NONE; 10232676Seschrock strval = "-"; 10242676Seschrock } else { 10252676Seschrock verify(nvlist_lookup_string(propval, 10262676Seschrock ZFS_PROP_VALUE, &strval) == 0); 10272676Seschrock verify(nvlist_lookup_string(propval, 10282676Seschrock ZFS_PROP_SOURCE, &sourceval) == 0); 10292676Seschrock 10302676Seschrock if (strcmp(sourceval, 10312676Seschrock zfs_get_name(zhp)) == 0) { 10322676Seschrock sourcetype = ZFS_SRC_LOCAL; 10332676Seschrock } else { 10342676Seschrock sourcetype = ZFS_SRC_INHERITED; 10352676Seschrock (void) strlcpy(source, 10362676Seschrock sourceval, sizeof (source)); 10372676Seschrock } 10382676Seschrock } 10392676Seschrock 10403912Slling libzfs_print_one_property(zfs_get_name(zhp), cbp, 10412676Seschrock pl->pl_user_prop, strval, sourcetype, 10422676Seschrock source); 1043866Seschrock } 1044789Sahrens } 1045789Sahrens 1046789Sahrens return (0); 1047789Sahrens } 1048789Sahrens 1049789Sahrens static int 1050789Sahrens zfs_do_get(int argc, char **argv) 1051789Sahrens { 10523912Slling libzfs_get_cbdata_t cb = { 0 }; 10532082Seschrock boolean_t recurse = B_FALSE; 10542676Seschrock int i, c; 10552676Seschrock char *value, *fields; 1056866Seschrock int ret; 10572676Seschrock zfs_proplist_t fake_name = { 0 }; 1058789Sahrens 1059789Sahrens /* 1060789Sahrens * Set up default columns and sources. 1061789Sahrens */ 1062789Sahrens cb.cb_sources = ZFS_SRC_ALL; 1063789Sahrens cb.cb_columns[0] = GET_COL_NAME; 1064789Sahrens cb.cb_columns[1] = GET_COL_PROPERTY; 1065789Sahrens cb.cb_columns[2] = GET_COL_VALUE; 1066789Sahrens cb.cb_columns[3] = GET_COL_SOURCE; 1067789Sahrens 1068789Sahrens /* check options */ 1069789Sahrens while ((c = getopt(argc, argv, ":o:s:rHp")) != -1) { 1070789Sahrens switch (c) { 1071789Sahrens case 'p': 10722082Seschrock cb.cb_literal = B_TRUE; 1073789Sahrens break; 1074789Sahrens case 'r': 10752082Seschrock recurse = B_TRUE; 1076789Sahrens break; 1077789Sahrens case 'H': 10782082Seschrock cb.cb_scripted = B_TRUE; 1079789Sahrens break; 1080789Sahrens case ':': 1081789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1082789Sahrens "'%c' option\n"), optopt); 10832082Seschrock usage(B_FALSE); 1084789Sahrens break; 1085789Sahrens case 'o': 1086789Sahrens /* 1087789Sahrens * Process the set of columns to display. We zero out 1088789Sahrens * the structure to give us a blank slate. 1089789Sahrens */ 1090789Sahrens bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 1091789Sahrens i = 0; 1092789Sahrens while (*optarg != '\0') { 1093789Sahrens static char *col_subopts[] = 1094789Sahrens { "name", "property", "value", "source", 1095789Sahrens NULL }; 1096789Sahrens 1097789Sahrens if (i == 4) { 1098789Sahrens (void) fprintf(stderr, gettext("too " 1099789Sahrens "many fields given to -o " 1100789Sahrens "option\n")); 11012082Seschrock usage(B_FALSE); 1102789Sahrens } 1103789Sahrens 1104789Sahrens switch (getsubopt(&optarg, col_subopts, 1105789Sahrens &value)) { 1106789Sahrens case 0: 1107789Sahrens cb.cb_columns[i++] = GET_COL_NAME; 1108789Sahrens break; 1109789Sahrens case 1: 1110789Sahrens cb.cb_columns[i++] = GET_COL_PROPERTY; 1111789Sahrens break; 1112789Sahrens case 2: 1113789Sahrens cb.cb_columns[i++] = GET_COL_VALUE; 1114789Sahrens break; 1115789Sahrens case 3: 1116789Sahrens cb.cb_columns[i++] = GET_COL_SOURCE; 1117789Sahrens break; 1118789Sahrens default: 1119789Sahrens (void) fprintf(stderr, 1120789Sahrens gettext("invalid column name " 1121789Sahrens "'%s'\n"), value); 11223912Slling usage(B_FALSE); 1123789Sahrens } 1124789Sahrens } 1125789Sahrens break; 1126789Sahrens 1127789Sahrens case 's': 1128789Sahrens cb.cb_sources = 0; 1129789Sahrens while (*optarg != '\0') { 1130789Sahrens static char *source_subopts[] = { 1131789Sahrens "local", "default", "inherited", 1132789Sahrens "temporary", "none", NULL }; 1133789Sahrens 1134789Sahrens switch (getsubopt(&optarg, source_subopts, 1135789Sahrens &value)) { 1136789Sahrens case 0: 1137789Sahrens cb.cb_sources |= ZFS_SRC_LOCAL; 1138789Sahrens break; 1139789Sahrens case 1: 1140789Sahrens cb.cb_sources |= ZFS_SRC_DEFAULT; 1141789Sahrens break; 1142789Sahrens case 2: 1143789Sahrens cb.cb_sources |= ZFS_SRC_INHERITED; 1144789Sahrens break; 1145789Sahrens case 3: 1146789Sahrens cb.cb_sources |= ZFS_SRC_TEMPORARY; 1147789Sahrens break; 1148789Sahrens case 4: 1149789Sahrens cb.cb_sources |= ZFS_SRC_NONE; 1150789Sahrens break; 1151789Sahrens default: 1152789Sahrens (void) fprintf(stderr, 1153789Sahrens gettext("invalid source " 1154789Sahrens "'%s'\n"), value); 11553912Slling usage(B_FALSE); 1156789Sahrens } 1157789Sahrens } 1158789Sahrens break; 1159789Sahrens 1160789Sahrens case '?': 1161789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1162789Sahrens optopt); 11632082Seschrock usage(B_FALSE); 1164789Sahrens } 1165789Sahrens } 1166789Sahrens 1167789Sahrens argc -= optind; 1168789Sahrens argv += optind; 1169789Sahrens 1170789Sahrens if (argc < 1) { 1171789Sahrens (void) fprintf(stderr, gettext("missing property " 1172789Sahrens "argument\n")); 11732082Seschrock usage(B_FALSE); 1174789Sahrens } 1175789Sahrens 1176789Sahrens fields = argv[0]; 1177789Sahrens 11782676Seschrock if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0) 11792082Seschrock usage(B_FALSE); 1180789Sahrens 1181789Sahrens argc--; 1182789Sahrens argv++; 1183789Sahrens 1184789Sahrens /* 11852676Seschrock * As part of zfs_expand_proplist(), we keep track of the maximum column 11862676Seschrock * width for each property. For the 'NAME' (and 'SOURCE') columns, we 11872676Seschrock * need to know the maximum name length. However, the user likely did 11882676Seschrock * not specify 'name' as one of the properties to fetch, so we need to 11892676Seschrock * make sure we always include at least this property for 11902676Seschrock * print_get_headers() to work properly. 1191789Sahrens */ 11922676Seschrock if (cb.cb_proplist != NULL) { 11932676Seschrock fake_name.pl_prop = ZFS_PROP_NAME; 11942676Seschrock fake_name.pl_width = strlen(gettext("NAME")); 11952676Seschrock fake_name.pl_next = cb.cb_proplist; 11962676Seschrock cb.cb_proplist = &fake_name; 1197789Sahrens } 1198789Sahrens 11992676Seschrock cb.cb_first = B_TRUE; 12002676Seschrock 1201789Sahrens /* run for each object */ 12022676Seschrock ret = zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, NULL, 12033635Sck153898 &cb.cb_proplist, get_callback, &cb, B_FALSE); 12042676Seschrock 12052676Seschrock if (cb.cb_proplist == &fake_name) 12062676Seschrock zfs_free_proplist(fake_name.pl_next); 12072676Seschrock else 12082676Seschrock zfs_free_proplist(cb.cb_proplist); 12092676Seschrock 12102676Seschrock return (ret); 1211789Sahrens } 1212789Sahrens 1213789Sahrens /* 1214789Sahrens * inherit [-r] <property> <fs|vol> ... 1215789Sahrens * 1216789Sahrens * -r Recurse over all children 1217789Sahrens * 1218789Sahrens * For each dataset specified on the command line, inherit the given property 1219789Sahrens * from its parent. Inheriting a property at the pool level will cause it to 1220789Sahrens * use the default value. The '-r' flag will recurse over all children, and is 1221789Sahrens * useful for setting a property on a hierarchy-wide basis, regardless of any 1222789Sahrens * local modifications for each dataset. 1223789Sahrens */ 12242926Sek110237 1225789Sahrens static int 1226789Sahrens inherit_callback(zfs_handle_t *zhp, void *data) 1227789Sahrens { 1228*4543Smarks char *propname = data; 12292926Sek110237 int ret; 12302926Sek110237 1231*4543Smarks ret = zfs_prop_inherit(zhp, propname); 12322926Sek110237 return (ret != 0); 1233789Sahrens } 1234789Sahrens 1235789Sahrens static int 1236789Sahrens zfs_do_inherit(int argc, char **argv) 1237789Sahrens { 12382082Seschrock boolean_t recurse = B_FALSE; 1239789Sahrens int c; 1240789Sahrens zfs_prop_t prop; 1241*4543Smarks char *propname; 12422926Sek110237 int ret; 1243789Sahrens 1244789Sahrens /* check options */ 1245789Sahrens while ((c = getopt(argc, argv, "r")) != -1) { 1246789Sahrens switch (c) { 1247789Sahrens case 'r': 12482082Seschrock recurse = B_TRUE; 1249789Sahrens break; 1250789Sahrens case '?': 1251789Sahrens default: 1252789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1253789Sahrens optopt); 12542082Seschrock usage(B_FALSE); 1255789Sahrens } 1256789Sahrens } 1257789Sahrens 1258789Sahrens argc -= optind; 1259789Sahrens argv += optind; 1260789Sahrens 1261789Sahrens /* check number of arguments */ 1262789Sahrens if (argc < 1) { 1263789Sahrens (void) fprintf(stderr, gettext("missing property argument\n")); 12642082Seschrock usage(B_FALSE); 1265789Sahrens } 1266789Sahrens if (argc < 2) { 1267789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 12682082Seschrock usage(B_FALSE); 1269789Sahrens } 1270789Sahrens 1271*4543Smarks propname = argv[0]; 12722676Seschrock argc--; 12732676Seschrock argv++; 12742676Seschrock 1275*4543Smarks if ((prop = zfs_name_to_prop(propname)) != ZFS_PROP_INVAL) { 12762676Seschrock if (zfs_prop_readonly(prop)) { 12772676Seschrock (void) fprintf(stderr, gettext( 12782676Seschrock "%s property is read-only\n"), 1279*4543Smarks propname); 12802676Seschrock return (1); 12812676Seschrock } 12822676Seschrock if (!zfs_prop_inheritable(prop)) { 12832676Seschrock (void) fprintf(stderr, gettext("'%s' property cannot " 1284*4543Smarks "be inherited\n"), propname); 12852676Seschrock if (prop == ZFS_PROP_QUOTA || 12862676Seschrock prop == ZFS_PROP_RESERVATION) 12872676Seschrock (void) fprintf(stderr, gettext("use 'zfs set " 1288*4543Smarks "%s=none' to clear\n"), propname); 12892676Seschrock return (1); 12902676Seschrock } 1291*4543Smarks } else if (!zfs_prop_user(propname)) { 1292*4543Smarks (void) fprintf(stderr, gettext("invalid property '%s'\n"), 1293*4543Smarks propname); 12942082Seschrock usage(B_FALSE); 1295789Sahrens } 12962676Seschrock 12972926Sek110237 ret = zfs_for_each(argc, argv, recurse, 12982676Seschrock ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL, 1299*4543Smarks inherit_callback, propname, B_FALSE); 13002926Sek110237 13012926Sek110237 return (ret); 1302789Sahrens } 1303789Sahrens 1304789Sahrens /* 13052379Ssjelinek * list [-rH] [-o property[,property]...] [-t type[,type]...] 13062379Ssjelinek * [-s property [-s property]...] [-S property [-S property]...] 13072379Ssjelinek * <dataset> ... 1308789Sahrens * 1309789Sahrens * -r Recurse over all children 1310789Sahrens * -H Scripted mode; elide headers and separate colums by tabs 1311789Sahrens * -o Control which fields to display. 1312866Seschrock * -t Control which object types to display. 13132379Ssjelinek * -s Specify sort columns, descending order. 13142379Ssjelinek * -S Specify sort columns, ascending order. 1315789Sahrens * 1316789Sahrens * When given no arguments, lists all filesystems in the system. 1317789Sahrens * Otherwise, list the specified datasets, optionally recursing down them if 1318789Sahrens * '-r' is specified. 1319789Sahrens */ 1320789Sahrens typedef struct list_cbdata { 13212082Seschrock boolean_t cb_first; 13222082Seschrock boolean_t cb_scripted; 13232676Seschrock zfs_proplist_t *cb_proplist; 1324789Sahrens } list_cbdata_t; 1325789Sahrens 1326789Sahrens /* 1327789Sahrens * Given a list of columns to display, output appropriate headers for each one. 1328789Sahrens */ 1329789Sahrens static void 13302676Seschrock print_header(zfs_proplist_t *pl) 1331789Sahrens { 13322676Seschrock char headerbuf[ZFS_MAXPROPLEN]; 13332676Seschrock const char *header; 1334789Sahrens int i; 13352676Seschrock boolean_t first = B_TRUE; 13362676Seschrock boolean_t right_justify; 13372676Seschrock 13382676Seschrock for (; pl != NULL; pl = pl->pl_next) { 13392676Seschrock if (!first) { 1340789Sahrens (void) printf(" "); 13412676Seschrock } else { 13422676Seschrock first = B_FALSE; 13432676Seschrock } 13442676Seschrock 13452676Seschrock right_justify = B_FALSE; 13462676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 13472676Seschrock header = zfs_prop_column_name(pl->pl_prop); 13482676Seschrock right_justify = zfs_prop_align_right(pl->pl_prop); 13492676Seschrock } else { 13502676Seschrock for (i = 0; pl->pl_user_prop[i] != '\0'; i++) 13512676Seschrock headerbuf[i] = toupper(pl->pl_user_prop[i]); 13522676Seschrock headerbuf[i] = '\0'; 13532676Seschrock header = headerbuf; 13542676Seschrock } 13552676Seschrock 13562676Seschrock if (pl->pl_next == NULL && !right_justify) 13572676Seschrock (void) printf("%s", header); 13582676Seschrock else if (right_justify) 13592676Seschrock (void) printf("%*s", pl->pl_width, header); 13602676Seschrock else 13612676Seschrock (void) printf("%-*s", pl->pl_width, header); 1362789Sahrens } 1363789Sahrens 1364789Sahrens (void) printf("\n"); 1365789Sahrens } 1366789Sahrens 1367789Sahrens /* 1368789Sahrens * Given a dataset and a list of fields, print out all the properties according 1369789Sahrens * to the described layout. 1370789Sahrens */ 1371789Sahrens static void 13722676Seschrock print_dataset(zfs_handle_t *zhp, zfs_proplist_t *pl, int scripted) 1373789Sahrens { 13742676Seschrock boolean_t first = B_TRUE; 1375789Sahrens char property[ZFS_MAXPROPLEN]; 13762676Seschrock nvlist_t *userprops = zfs_get_user_props(zhp); 13772676Seschrock nvlist_t *propval; 13782676Seschrock char *propstr; 13792676Seschrock boolean_t right_justify; 13802676Seschrock int width; 13812676Seschrock 13822676Seschrock for (; pl != NULL; pl = pl->pl_next) { 13832676Seschrock if (!first) { 1384789Sahrens if (scripted) 1385789Sahrens (void) printf("\t"); 1386789Sahrens else 1387789Sahrens (void) printf(" "); 13882676Seschrock } else { 13892676Seschrock first = B_FALSE; 1390789Sahrens } 1391789Sahrens 13922676Seschrock right_justify = B_FALSE; 13932676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 13942676Seschrock if (zfs_prop_get(zhp, pl->pl_prop, property, 13952676Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) != 0) 13962676Seschrock propstr = "-"; 13972676Seschrock else 13982676Seschrock propstr = property; 13992676Seschrock 14002676Seschrock right_justify = zfs_prop_align_right(pl->pl_prop); 14012676Seschrock } else { 14022676Seschrock if (nvlist_lookup_nvlist(userprops, 14032676Seschrock pl->pl_user_prop, &propval) != 0) 14042676Seschrock propstr = "-"; 14052676Seschrock else 14062676Seschrock verify(nvlist_lookup_string(propval, 14072676Seschrock ZFS_PROP_VALUE, &propstr) == 0); 14082676Seschrock } 14092676Seschrock 14102676Seschrock width = pl->pl_width; 1411789Sahrens 1412866Seschrock /* 1413866Seschrock * If this is being called in scripted mode, or if this is the 1414866Seschrock * last column and it is left-justified, don't include a width 1415866Seschrock * format specifier. 1416866Seschrock */ 14172676Seschrock if (scripted || (pl->pl_next == NULL && !right_justify)) 14182676Seschrock (void) printf("%s", propstr); 14192676Seschrock else if (right_justify) 14202676Seschrock (void) printf("%*s", width, propstr); 14212676Seschrock else 14222676Seschrock (void) printf("%-*s", width, propstr); 1423789Sahrens } 1424789Sahrens 1425789Sahrens (void) printf("\n"); 1426789Sahrens } 1427789Sahrens 1428789Sahrens /* 1429789Sahrens * Generic callback function to list a dataset or snapshot. 1430789Sahrens */ 1431789Sahrens static int 1432789Sahrens list_callback(zfs_handle_t *zhp, void *data) 1433789Sahrens { 1434789Sahrens list_cbdata_t *cbp = data; 1435789Sahrens 1436789Sahrens if (cbp->cb_first) { 1437789Sahrens if (!cbp->cb_scripted) 14382676Seschrock print_header(cbp->cb_proplist); 14392082Seschrock cbp->cb_first = B_FALSE; 1440789Sahrens } 1441789Sahrens 14422676Seschrock print_dataset(zhp, cbp->cb_proplist, cbp->cb_scripted); 1443789Sahrens 1444789Sahrens return (0); 1445789Sahrens } 1446789Sahrens 1447789Sahrens static int 1448789Sahrens zfs_do_list(int argc, char **argv) 1449789Sahrens { 1450789Sahrens int c; 14512082Seschrock boolean_t recurse = B_FALSE; 14522082Seschrock boolean_t scripted = B_FALSE; 1453789Sahrens static char default_fields[] = 1454789Sahrens "name,used,available,referenced,mountpoint"; 1455789Sahrens int types = ZFS_TYPE_ANY; 1456789Sahrens char *fields = NULL; 1457789Sahrens char *basic_fields = default_fields; 1458789Sahrens list_cbdata_t cb = { 0 }; 1459789Sahrens char *value; 1460789Sahrens int ret; 1461789Sahrens char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL }; 14622379Ssjelinek zfs_sort_column_t *sortcol = NULL; 1463789Sahrens 1464789Sahrens /* check options */ 14652379Ssjelinek while ((c = getopt(argc, argv, ":o:rt:Hs:S:")) != -1) { 1466789Sahrens switch (c) { 1467789Sahrens case 'o': 1468789Sahrens fields = optarg; 1469789Sahrens break; 1470789Sahrens case 'r': 14712082Seschrock recurse = B_TRUE; 1472789Sahrens break; 1473789Sahrens case 'H': 14742082Seschrock scripted = B_TRUE; 1475789Sahrens break; 14762379Ssjelinek case 's': 14772676Seschrock if (zfs_add_sort_column(&sortcol, optarg, 14782676Seschrock B_FALSE) != 0) { 14792379Ssjelinek (void) fprintf(stderr, 14802379Ssjelinek gettext("invalid property '%s'\n"), optarg); 14812379Ssjelinek usage(B_FALSE); 14822379Ssjelinek } 14832379Ssjelinek break; 14842379Ssjelinek case 'S': 14852676Seschrock if (zfs_add_sort_column(&sortcol, optarg, 14862676Seschrock B_TRUE) != 0) { 14872379Ssjelinek (void) fprintf(stderr, 14882379Ssjelinek gettext("invalid property '%s'\n"), optarg); 14892379Ssjelinek usage(B_FALSE); 14902379Ssjelinek } 14912379Ssjelinek break; 1492789Sahrens case 't': 1493789Sahrens types = 0; 1494789Sahrens while (*optarg != '\0') { 1495789Sahrens switch (getsubopt(&optarg, type_subopts, 1496789Sahrens &value)) { 1497789Sahrens case 0: 1498789Sahrens types |= ZFS_TYPE_FILESYSTEM; 1499789Sahrens break; 1500789Sahrens case 1: 1501789Sahrens types |= ZFS_TYPE_VOLUME; 1502789Sahrens break; 1503789Sahrens case 2: 1504789Sahrens types |= ZFS_TYPE_SNAPSHOT; 1505789Sahrens break; 1506789Sahrens default: 1507789Sahrens (void) fprintf(stderr, 1508789Sahrens gettext("invalid type '%s'\n"), 1509789Sahrens value); 15102082Seschrock usage(B_FALSE); 1511789Sahrens } 1512789Sahrens } 1513789Sahrens break; 1514789Sahrens case ':': 1515789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1516789Sahrens "'%c' option\n"), optopt); 15172082Seschrock usage(B_FALSE); 1518789Sahrens break; 1519789Sahrens case '?': 1520789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1521789Sahrens optopt); 15222082Seschrock usage(B_FALSE); 1523789Sahrens } 1524789Sahrens } 1525789Sahrens 1526789Sahrens argc -= optind; 1527789Sahrens argv += optind; 1528789Sahrens 1529789Sahrens if (fields == NULL) 1530789Sahrens fields = basic_fields; 1531789Sahrens 1532866Seschrock /* 1533866Seschrock * If the user specifies '-o all', the zfs_get_proplist() doesn't 1534866Seschrock * normally include the name of the dataset. For 'zfs list', we always 1535866Seschrock * want this property to be first. 1536866Seschrock */ 15372676Seschrock if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0) 15382082Seschrock usage(B_FALSE); 15392676Seschrock 1540789Sahrens cb.cb_scripted = scripted; 15412082Seschrock cb.cb_first = B_TRUE; 1542789Sahrens 15432676Seschrock ret = zfs_for_each(argc, argv, recurse, types, sortcol, &cb.cb_proplist, 15443635Sck153898 list_callback, &cb, B_TRUE); 15452379Ssjelinek 15462676Seschrock zfs_free_proplist(cb.cb_proplist); 15472379Ssjelinek zfs_free_sort_columns(sortcol); 1548789Sahrens 15494221Smmusante if (ret == 0 && cb.cb_first && !cb.cb_scripted) 1550789Sahrens (void) printf(gettext("no datasets available\n")); 1551789Sahrens 1552789Sahrens return (ret); 1553789Sahrens } 1554789Sahrens 1555789Sahrens /* 15564490Svb160487 * zfs rename <fs | snap | vol> <fs | snap | vol> 15574490Svb160487 * zfs rename -p <fs | vol> <fs | vol> 15584490Svb160487 * zfs rename -r <snap> <snap> 1559789Sahrens * 1560789Sahrens * Renames the given dataset to another of the same type. 15614490Svb160487 * 15624490Svb160487 * The '-p' flag creates all the non-existing ancestors of the target first. 1563789Sahrens */ 1564789Sahrens /* ARGSUSED */ 1565789Sahrens static int 1566789Sahrens zfs_do_rename(int argc, char **argv) 1567789Sahrens { 1568789Sahrens zfs_handle_t *zhp; 15694007Smmusante int c; 15702082Seschrock int ret; 15714490Svb160487 boolean_t recurse = B_FALSE; 15724490Svb160487 boolean_t parents = B_FALSE; 1573789Sahrens 1574789Sahrens /* check options */ 15754490Svb160487 while ((c = getopt(argc, argv, "pr")) != -1) { 15764007Smmusante switch (c) { 15774490Svb160487 case 'p': 15784490Svb160487 parents = B_TRUE; 15794490Svb160487 break; 15804007Smmusante case 'r': 15814490Svb160487 recurse = B_TRUE; 15824007Smmusante break; 15834007Smmusante case '?': 15844007Smmusante default: 15854007Smmusante (void) fprintf(stderr, gettext("invalid option '%c'\n"), 15864007Smmusante optopt); 15874007Smmusante usage(B_FALSE); 15884007Smmusante } 1589789Sahrens } 1590789Sahrens 15914007Smmusante argc -= optind; 15924007Smmusante argv += optind; 15934007Smmusante 1594789Sahrens /* check number of arguments */ 15954007Smmusante if (argc < 1) { 1596789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 1597789Sahrens "argument\n")); 15982082Seschrock usage(B_FALSE); 1599789Sahrens } 16004007Smmusante if (argc < 2) { 1601789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 1602789Sahrens "argument\n")); 16032082Seschrock usage(B_FALSE); 1604789Sahrens } 16054007Smmusante if (argc > 2) { 1606789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 16072082Seschrock usage(B_FALSE); 1608789Sahrens } 1609789Sahrens 16104490Svb160487 if (recurse && parents) { 16114490Svb160487 (void) fprintf(stderr, gettext("-p and -r options are mutually " 16124490Svb160487 "exclusive\n")); 16134490Svb160487 usage(B_FALSE); 16144490Svb160487 } 16154490Svb160487 16164007Smmusante if (recurse && strchr(argv[0], '@') == 0) { 16174007Smmusante (void) fprintf(stderr, gettext("source dataset for recursive " 16184007Smmusante "rename must be a snapshot\n")); 16194007Smmusante usage(B_FALSE); 16204007Smmusante } 16214007Smmusante 16224490Svb160487 if ((zhp = zfs_open(g_zfs, argv[0], parents ? ZFS_TYPE_FILESYSTEM | 16234490Svb160487 ZFS_TYPE_VOLUME : ZFS_TYPE_ANY)) == NULL) 1624789Sahrens return (1); 1625789Sahrens 16264490Svb160487 /* If we were asked and the name looks good, try to create ancestors. */ 16274490Svb160487 if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) && 16284490Svb160487 zfs_create_ancestors(g_zfs, argv[1]) != 0) { 16294490Svb160487 zfs_close(zhp); 16304490Svb160487 return (1); 16314490Svb160487 } 16324490Svb160487 16334007Smmusante ret = (zfs_rename(zhp, argv[1], recurse) != 0); 16342082Seschrock 16352082Seschrock zfs_close(zhp); 16362082Seschrock return (ret); 16372082Seschrock } 16382082Seschrock 16392082Seschrock /* 16402082Seschrock * zfs promote <fs> 16412082Seschrock * 16422082Seschrock * Promotes the given clone fs to be the parent 16432082Seschrock */ 16442082Seschrock /* ARGSUSED */ 16452082Seschrock static int 16462082Seschrock zfs_do_promote(int argc, char **argv) 16472082Seschrock { 16482082Seschrock zfs_handle_t *zhp; 16492082Seschrock int ret; 16502082Seschrock 16512082Seschrock /* check options */ 16522082Seschrock if (argc > 1 && argv[1][0] == '-') { 16532082Seschrock (void) fprintf(stderr, gettext("invalid option '%c'\n"), 16542082Seschrock argv[1][1]); 16552082Seschrock usage(B_FALSE); 16562082Seschrock } 16572082Seschrock 16582082Seschrock /* check number of arguments */ 16592082Seschrock if (argc < 2) { 16602082Seschrock (void) fprintf(stderr, gettext("missing clone filesystem" 16612597Snd150628 " argument\n")); 16622082Seschrock usage(B_FALSE); 16632082Seschrock } 16642082Seschrock if (argc > 2) { 16652082Seschrock (void) fprintf(stderr, gettext("too many arguments\n")); 16662082Seschrock usage(B_FALSE); 16672082Seschrock } 16682082Seschrock 16692082Seschrock zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 16702082Seschrock if (zhp == NULL) 16712082Seschrock return (1); 16722082Seschrock 16732082Seschrock ret = (zfs_promote(zhp) != 0); 16742082Seschrock 16752926Sek110237 1676789Sahrens zfs_close(zhp); 1677789Sahrens return (ret); 1678789Sahrens } 1679789Sahrens 1680789Sahrens /* 1681789Sahrens * zfs rollback [-rfR] <snapshot> 1682789Sahrens * 1683789Sahrens * -r Delete any intervening snapshots before doing rollback 1684789Sahrens * -R Delete any snapshots and their clones 1685789Sahrens * -f Force unmount filesystems, even if they are in use. 1686789Sahrens * 1687789Sahrens * Given a filesystem, rollback to a specific snapshot, discarding any changes 1688789Sahrens * since then and making it the active dataset. If more recent snapshots exist, 1689789Sahrens * the command will complain unless the '-r' flag is given. 1690789Sahrens */ 1691789Sahrens typedef struct rollback_cbdata { 1692789Sahrens uint64_t cb_create; 16932082Seschrock boolean_t cb_first; 1694789Sahrens int cb_doclones; 1695789Sahrens char *cb_target; 1696789Sahrens int cb_error; 16972082Seschrock boolean_t cb_recurse; 16982082Seschrock boolean_t cb_dependent; 1699789Sahrens } rollback_cbdata_t; 1700789Sahrens 1701789Sahrens /* 1702789Sahrens * Report any snapshots more recent than the one specified. Used when '-r' is 1703789Sahrens * not specified. We reuse this same callback for the snapshot dependents - if 1704789Sahrens * 'cb_dependent' is set, then this is a dependent and we should report it 1705789Sahrens * without checking the transaction group. 1706789Sahrens */ 1707789Sahrens static int 1708789Sahrens rollback_check(zfs_handle_t *zhp, void *data) 1709789Sahrens { 1710789Sahrens rollback_cbdata_t *cbp = data; 1711789Sahrens 17122082Seschrock if (cbp->cb_doclones) { 17132082Seschrock zfs_close(zhp); 1714789Sahrens return (0); 17152082Seschrock } 1716789Sahrens 1717789Sahrens if (!cbp->cb_dependent) { 1718789Sahrens if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && 17191294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 1720789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 1721789Sahrens cbp->cb_create) { 1722789Sahrens 1723789Sahrens if (cbp->cb_first && !cbp->cb_recurse) { 1724789Sahrens (void) fprintf(stderr, gettext("cannot " 1725789Sahrens "rollback to '%s': more recent snapshots " 1726789Sahrens "exist\n"), 1727789Sahrens cbp->cb_target); 1728789Sahrens (void) fprintf(stderr, gettext("use '-r' to " 1729789Sahrens "force deletion of the following " 1730789Sahrens "snapshots:\n")); 1731789Sahrens cbp->cb_first = 0; 1732789Sahrens cbp->cb_error = 1; 1733789Sahrens } 1734789Sahrens 1735789Sahrens if (cbp->cb_recurse) { 17362082Seschrock cbp->cb_dependent = B_TRUE; 17372474Seschrock if (zfs_iter_dependents(zhp, B_TRUE, 17382474Seschrock rollback_check, cbp) != 0) { 17392474Seschrock zfs_close(zhp); 17402474Seschrock return (-1); 17412474Seschrock } 17422082Seschrock cbp->cb_dependent = B_FALSE; 1743789Sahrens } else { 1744789Sahrens (void) fprintf(stderr, "%s\n", 1745789Sahrens zfs_get_name(zhp)); 1746789Sahrens } 1747789Sahrens } 1748789Sahrens } else { 1749789Sahrens if (cbp->cb_first && cbp->cb_recurse) { 1750789Sahrens (void) fprintf(stderr, gettext("cannot rollback to " 1751789Sahrens "'%s': clones of previous snapshots exist\n"), 1752789Sahrens cbp->cb_target); 1753789Sahrens (void) fprintf(stderr, gettext("use '-R' to " 1754789Sahrens "force deletion of the following clones and " 1755789Sahrens "dependents:\n")); 1756789Sahrens cbp->cb_first = 0; 1757789Sahrens cbp->cb_error = 1; 1758789Sahrens } 1759789Sahrens 1760789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1761789Sahrens } 1762789Sahrens 1763789Sahrens zfs_close(zhp); 1764789Sahrens return (0); 1765789Sahrens } 1766789Sahrens 1767789Sahrens static int 1768789Sahrens zfs_do_rollback(int argc, char **argv) 1769789Sahrens { 1770789Sahrens int ret; 1771789Sahrens int c; 1772789Sahrens rollback_cbdata_t cb = { 0 }; 1773789Sahrens zfs_handle_t *zhp, *snap; 1774789Sahrens char parentname[ZFS_MAXNAMELEN]; 1775789Sahrens char *delim; 17761294Slling int force = 0; 1777789Sahrens 1778789Sahrens /* check options */ 1779789Sahrens while ((c = getopt(argc, argv, "rfR")) != -1) { 1780789Sahrens switch (c) { 1781789Sahrens case 'f': 17821294Slling force = 1; 1783789Sahrens break; 1784789Sahrens case 'r': 1785789Sahrens cb.cb_recurse = 1; 1786789Sahrens break; 1787789Sahrens case 'R': 1788789Sahrens cb.cb_recurse = 1; 1789789Sahrens cb.cb_doclones = 1; 1790789Sahrens break; 1791789Sahrens case '?': 1792789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1793789Sahrens optopt); 17942082Seschrock usage(B_FALSE); 1795789Sahrens } 1796789Sahrens } 1797789Sahrens 1798789Sahrens argc -= optind; 1799789Sahrens argv += optind; 1800789Sahrens 1801789Sahrens /* check number of arguments */ 1802789Sahrens if (argc < 1) { 1803789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 18042082Seschrock usage(B_FALSE); 1805789Sahrens } 1806789Sahrens if (argc > 1) { 1807789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 18082082Seschrock usage(B_FALSE); 1809789Sahrens } 1810789Sahrens 1811789Sahrens /* open the snapshot */ 18122082Seschrock if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1813789Sahrens return (1); 1814789Sahrens 18151294Slling /* open the parent dataset */ 18161294Slling (void) strlcpy(parentname, argv[0], sizeof (parentname)); 1817789Sahrens verify((delim = strrchr(parentname, '@')) != NULL); 1818789Sahrens *delim = '\0'; 18192082Seschrock if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_ANY)) == NULL) { 1820789Sahrens zfs_close(snap); 1821789Sahrens return (1); 1822789Sahrens } 1823789Sahrens 1824789Sahrens /* 1825789Sahrens * Check for more recent snapshots and/or clones based on the presence 1826789Sahrens * of '-r' and '-R'. 1827789Sahrens */ 18281294Slling cb.cb_target = argv[0]; 18291294Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 18302082Seschrock cb.cb_first = B_TRUE; 1831789Sahrens cb.cb_error = 0; 18322474Seschrock if ((ret = zfs_iter_children(zhp, rollback_check, &cb)) != 0) 18332474Seschrock goto out; 1834789Sahrens 1835789Sahrens if ((ret = cb.cb_error) != 0) 1836789Sahrens goto out; 1837789Sahrens 1838789Sahrens /* 18391294Slling * Rollback parent to the given snapshot. 1840789Sahrens */ 18411294Slling ret = zfs_rollback(zhp, snap, force); 1842789Sahrens 1843789Sahrens out: 1844789Sahrens zfs_close(snap); 1845789Sahrens zfs_close(zhp); 1846789Sahrens 1847789Sahrens if (ret == 0) 1848789Sahrens return (0); 1849789Sahrens else 1850789Sahrens return (1); 1851789Sahrens } 1852789Sahrens 1853789Sahrens /* 1854789Sahrens * zfs set property=value { fs | snap | vol } ... 1855789Sahrens * 1856789Sahrens * Sets the given property for all datasets specified on the command line. 1857789Sahrens */ 1858789Sahrens typedef struct set_cbdata { 1859789Sahrens char *cb_propname; 1860789Sahrens char *cb_value; 1861789Sahrens } set_cbdata_t; 1862789Sahrens 1863789Sahrens static int 1864789Sahrens set_callback(zfs_handle_t *zhp, void *data) 1865789Sahrens { 1866789Sahrens set_cbdata_t *cbp = data; 1867789Sahrens 18682676Seschrock if (zfs_prop_set(zhp, cbp->cb_propname, cbp->cb_value) != 0) { 18692169Snd150628 switch (libzfs_errno(g_zfs)) { 18702169Snd150628 case EZFS_MOUNTFAILED: 18712169Snd150628 (void) fprintf(stderr, gettext("property may be set " 18722169Snd150628 "but unable to remount filesystem\n")); 18732169Snd150628 break; 18743126Sahl case EZFS_SHARENFSFAILED: 18752169Snd150628 (void) fprintf(stderr, gettext("property may be set " 18762169Snd150628 "but unable to reshare filesystem\n")); 18772169Snd150628 break; 18782169Snd150628 } 1879789Sahrens return (1); 18802169Snd150628 } 18812856Snd150628 return (0); 1882789Sahrens } 1883789Sahrens 1884789Sahrens static int 1885789Sahrens zfs_do_set(int argc, char **argv) 1886789Sahrens { 1887789Sahrens set_cbdata_t cb; 18882926Sek110237 int ret; 1889789Sahrens 1890789Sahrens /* check for options */ 1891789Sahrens if (argc > 1 && argv[1][0] == '-') { 1892789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1893789Sahrens argv[1][1]); 18942082Seschrock usage(B_FALSE); 1895789Sahrens } 1896789Sahrens 1897789Sahrens /* check number of arguments */ 1898789Sahrens if (argc < 2) { 1899789Sahrens (void) fprintf(stderr, gettext("missing property=value " 1900789Sahrens "argument\n")); 19012082Seschrock usage(B_FALSE); 1902789Sahrens } 1903789Sahrens if (argc < 3) { 1904789Sahrens (void) fprintf(stderr, gettext("missing dataset name\n")); 19052082Seschrock usage(B_FALSE); 1906789Sahrens } 1907789Sahrens 1908789Sahrens /* validate property=value argument */ 1909789Sahrens cb.cb_propname = argv[1]; 1910789Sahrens if ((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) { 1911789Sahrens (void) fprintf(stderr, gettext("missing value in " 1912789Sahrens "property=value argument\n")); 19132082Seschrock usage(B_FALSE); 1914789Sahrens } 1915789Sahrens 1916789Sahrens *cb.cb_value = '\0'; 1917789Sahrens cb.cb_value++; 1918789Sahrens 1919789Sahrens if (*cb.cb_propname == '\0') { 1920789Sahrens (void) fprintf(stderr, 1921789Sahrens gettext("missing property in property=value argument\n")); 19222082Seschrock usage(B_FALSE); 1923789Sahrens } 1924789Sahrens 1925*4543Smarks 19262926Sek110237 ret = zfs_for_each(argc - 2, argv + 2, B_FALSE, 19273635Sck153898 ZFS_TYPE_ANY, NULL, NULL, set_callback, &cb, B_FALSE); 19282926Sek110237 19292926Sek110237 return (ret); 1930789Sahrens } 1931789Sahrens 1932789Sahrens /* 19332199Sahrens * zfs snapshot [-r] <fs@snap> 1934789Sahrens * 1935789Sahrens * Creates a snapshot with the given name. While functionally equivalent to 1936789Sahrens * 'zfs create', it is a separate command to diffferentiate intent. 1937789Sahrens */ 1938789Sahrens static int 1939789Sahrens zfs_do_snapshot(int argc, char **argv) 1940789Sahrens { 19414490Svb160487 boolean_t recursive = B_FALSE; 19422199Sahrens int ret; 19432199Sahrens char c; 19442199Sahrens 1945789Sahrens /* check options */ 19462199Sahrens while ((c = getopt(argc, argv, ":r")) != -1) { 19472199Sahrens switch (c) { 19482199Sahrens case 'r': 19492199Sahrens recursive = B_TRUE; 19502199Sahrens break; 19512199Sahrens case '?': 19522199Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 19532199Sahrens optopt); 19542199Sahrens usage(B_FALSE); 19552199Sahrens } 1956789Sahrens } 1957789Sahrens 19582199Sahrens argc -= optind; 19592199Sahrens argv += optind; 19602199Sahrens 1961789Sahrens /* check number of arguments */ 19622199Sahrens if (argc < 1) { 1963789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 19642082Seschrock usage(B_FALSE); 1965789Sahrens } 19662199Sahrens if (argc > 1) { 1967789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 19682082Seschrock usage(B_FALSE); 1969789Sahrens } 1970789Sahrens 19712199Sahrens ret = zfs_snapshot(g_zfs, argv[0], recursive); 19722199Sahrens if (ret && recursive) 19732199Sahrens (void) fprintf(stderr, gettext("no snapshots were created\n")); 19742199Sahrens return (ret != 0); 1975789Sahrens } 1976789Sahrens 1977789Sahrens /* 19782885Sahrens * zfs send [-i <@snap>] <fs@snap> 1979789Sahrens * 1980789Sahrens * Send a backup stream to stdout. 1981789Sahrens */ 1982789Sahrens static int 19831749Sahrens zfs_do_send(int argc, char **argv) 1984789Sahrens { 1985789Sahrens char *fromname = NULL; 19862885Sahrens char *cp; 19872885Sahrens zfs_handle_t *zhp; 1988789Sahrens int c, err; 1989789Sahrens 1990789Sahrens /* check options */ 1991789Sahrens while ((c = getopt(argc, argv, ":i:")) != -1) { 1992789Sahrens switch (c) { 1993789Sahrens case 'i': 19942885Sahrens if (fromname) 19952885Sahrens usage(B_FALSE); 1996789Sahrens fromname = optarg; 1997789Sahrens break; 1998789Sahrens case ':': 1999789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2000789Sahrens "'%c' option\n"), optopt); 20012082Seschrock usage(B_FALSE); 2002789Sahrens break; 2003789Sahrens case '?': 2004789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2005789Sahrens optopt); 20062082Seschrock usage(B_FALSE); 2007789Sahrens } 2008789Sahrens } 2009789Sahrens 2010789Sahrens argc -= optind; 2011789Sahrens argv += optind; 2012789Sahrens 2013789Sahrens /* check number of arguments */ 2014789Sahrens if (argc < 1) { 2015789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 20162082Seschrock usage(B_FALSE); 2017789Sahrens } 2018789Sahrens if (argc > 1) { 2019789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 20202082Seschrock usage(B_FALSE); 2021789Sahrens } 2022789Sahrens 2023789Sahrens if (isatty(STDOUT_FILENO)) { 2024789Sahrens (void) fprintf(stderr, 20252885Sahrens gettext("Error: Stream can not be written to a terminal.\n" 20263912Slling "You must redirect standard output.\n")); 2027789Sahrens return (1); 2028789Sahrens } 2029789Sahrens 20302885Sahrens if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 20312665Snd150628 return (1); 20322665Snd150628 20332885Sahrens /* 20342885Sahrens * If they specified the full path to the snapshot, chop off 20352885Sahrens * everything except the short name of the snapshot. 20362885Sahrens */ 20372885Sahrens if (fromname && (cp = strchr(fromname, '@')) != NULL) { 20382885Sahrens if (cp != fromname && 20392885Sahrens strncmp(argv[0], fromname, cp - fromname + 1)) { 20402885Sahrens (void) fprintf(stderr, 20412885Sahrens gettext("incremental source must be " 20422885Sahrens "in same filesystem\n")); 20432885Sahrens usage(B_FALSE); 20442665Snd150628 } 20452885Sahrens fromname = cp + 1; 20462885Sahrens if (strchr(fromname, '@') || strchr(fromname, '/')) { 20472885Sahrens (void) fprintf(stderr, 20482885Sahrens gettext("invalid incremental source\n")); 20492885Sahrens usage(B_FALSE); 20502885Sahrens } 2051789Sahrens } 2052789Sahrens 20533504Sahl err = zfs_send(zhp, fromname, STDOUT_FILENO); 20542885Sahrens zfs_close(zhp); 2055789Sahrens 2056789Sahrens return (err != 0); 2057789Sahrens } 2058789Sahrens 2059789Sahrens /* 20601749Sahrens * zfs receive <fs@snap> 2061789Sahrens * 2062789Sahrens * Restore a backup stream from stdin. 2063789Sahrens */ 2064789Sahrens static int 20651749Sahrens zfs_do_receive(int argc, char **argv) 2066789Sahrens { 2067789Sahrens int c, err; 20682082Seschrock boolean_t isprefix = B_FALSE; 20692082Seschrock boolean_t dryrun = B_FALSE; 20702082Seschrock boolean_t verbose = B_FALSE; 20712665Snd150628 boolean_t force = B_FALSE; 2072789Sahrens 2073789Sahrens /* check options */ 20742665Snd150628 while ((c = getopt(argc, argv, ":dnvF")) != -1) { 2075789Sahrens switch (c) { 2076789Sahrens case 'd': 20772082Seschrock isprefix = B_TRUE; 2078789Sahrens break; 2079789Sahrens case 'n': 20802082Seschrock dryrun = B_TRUE; 2081789Sahrens break; 2082789Sahrens case 'v': 20832082Seschrock verbose = B_TRUE; 2084789Sahrens break; 20852665Snd150628 case 'F': 20862665Snd150628 force = B_TRUE; 20872665Snd150628 break; 2088789Sahrens case ':': 2089789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2090789Sahrens "'%c' option\n"), optopt); 20912082Seschrock usage(B_FALSE); 2092789Sahrens break; 2093789Sahrens case '?': 2094789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2095789Sahrens optopt); 20962082Seschrock usage(B_FALSE); 2097789Sahrens } 2098789Sahrens } 2099789Sahrens 2100789Sahrens argc -= optind; 2101789Sahrens argv += optind; 2102789Sahrens 2103789Sahrens /* check number of arguments */ 2104789Sahrens if (argc < 1) { 2105789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 21062082Seschrock usage(B_FALSE); 2107789Sahrens } 2108789Sahrens if (argc > 1) { 2109789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 21102082Seschrock usage(B_FALSE); 2111789Sahrens } 2112789Sahrens 2113789Sahrens if (isatty(STDIN_FILENO)) { 2114789Sahrens (void) fprintf(stderr, 2115789Sahrens gettext("Error: Backup stream can not be read " 21163912Slling "from a terminal.\n" 21173912Slling "You must redirect standard input.\n")); 2118789Sahrens return (1); 2119789Sahrens } 2120789Sahrens 21213504Sahl err = zfs_receive(g_zfs, argv[0], isprefix, verbose, dryrun, force, 21223504Sahl STDIN_FILENO); 21232926Sek110237 2124*4543Smarks return (err != 0); 2125*4543Smarks } 2126*4543Smarks 2127*4543Smarks typedef struct allow_cb { 2128*4543Smarks int a_permcnt; 2129*4543Smarks size_t a_treeoffset; 2130*4543Smarks } allow_cb_t; 2131*4543Smarks 2132*4543Smarks static void 2133*4543Smarks zfs_print_perms(avl_tree_t *tree) 2134*4543Smarks { 2135*4543Smarks zfs_perm_node_t *permnode; 2136*4543Smarks 2137*4543Smarks permnode = avl_first(tree); 2138*4543Smarks while (permnode != NULL) { 2139*4543Smarks (void) printf("%s", permnode->z_pname); 2140*4543Smarks permnode = AVL_NEXT(tree, permnode); 2141*4543Smarks if (permnode) 2142*4543Smarks (void) printf(","); 2143*4543Smarks else 2144*4543Smarks (void) printf("\n"); 2145*4543Smarks } 2146*4543Smarks } 2147*4543Smarks 2148*4543Smarks /* 2149*4543Smarks * Iterate over user/groups/everyone/... and the call perm_iter 2150*4543Smarks * function to print actual permission when tree has >0 nodes. 2151*4543Smarks */ 2152*4543Smarks static void 2153*4543Smarks zfs_iter_perms(avl_tree_t *tree, const char *banner, allow_cb_t *cb) 2154*4543Smarks { 2155*4543Smarks zfs_allow_node_t *item; 2156*4543Smarks avl_tree_t *ptree; 2157*4543Smarks 2158*4543Smarks item = avl_first(tree); 2159*4543Smarks while (item) { 2160*4543Smarks ptree = (void *)((char *)item + cb->a_treeoffset); 2161*4543Smarks if (avl_numnodes(ptree)) { 2162*4543Smarks if (cb->a_permcnt++ == 0) 2163*4543Smarks (void) printf("%s\n", banner); 2164*4543Smarks (void) printf("\t%s", item->z_key); 2165*4543Smarks /* 2166*4543Smarks * Avoid an extra space being printed 2167*4543Smarks * for "everyone" which is keyed with a null 2168*4543Smarks * string 2169*4543Smarks */ 2170*4543Smarks if (item->z_key[0] != '\0') 2171*4543Smarks (void) printf(" "); 2172*4543Smarks zfs_print_perms(ptree); 2173*4543Smarks } 2174*4543Smarks item = AVL_NEXT(tree, item); 2175*4543Smarks } 2176*4543Smarks } 2177*4543Smarks 2178*4543Smarks #define LINES "-------------------------------------------------------------\n" 2179*4543Smarks static int 2180*4543Smarks zfs_print_allows(char *ds) 2181*4543Smarks { 2182*4543Smarks zfs_allow_t *curperms, *perms; 2183*4543Smarks zfs_handle_t *zhp; 2184*4543Smarks allow_cb_t allowcb = { 0 }; 2185*4543Smarks char banner[MAXPATHLEN]; 2186*4543Smarks 2187*4543Smarks if (ds[0] == '-') 2188*4543Smarks usage(B_FALSE); 2189*4543Smarks 2190*4543Smarks if (strrchr(ds, '@')) { 2191*4543Smarks (void) fprintf(stderr, gettext("Snapshots don't have 'allow'" 2192*4543Smarks " permissions\n")); 2193*4543Smarks return (1); 2194*4543Smarks } 2195*4543Smarks if ((zhp = zfs_open(g_zfs, ds, ZFS_TYPE_ANY)) == NULL) 2196*4543Smarks return (1); 2197*4543Smarks 2198*4543Smarks if (zfs_perm_get(zhp, &perms)) { 2199*4543Smarks (void) fprintf(stderr, 2200*4543Smarks gettext("Failed to retrieve 'allows' on %s\n"), ds); 2201*4543Smarks zfs_close(zhp); 2202*4543Smarks return (1); 2203*4543Smarks } 2204*4543Smarks 2205*4543Smarks zfs_close(zhp); 2206*4543Smarks 2207*4543Smarks if (perms != NULL) 2208*4543Smarks (void) printf("%s", LINES); 2209*4543Smarks for (curperms = perms; curperms; curperms = curperms->z_next) { 2210*4543Smarks 2211*4543Smarks (void) snprintf(banner, sizeof (banner), 2212*4543Smarks "Permission sets on (%s)", curperms->z_setpoint); 2213*4543Smarks allowcb.a_treeoffset = 2214*4543Smarks offsetof(zfs_allow_node_t, z_localdescend); 2215*4543Smarks allowcb.a_permcnt = 0; 2216*4543Smarks zfs_iter_perms(&curperms->z_sets, banner, &allowcb); 2217*4543Smarks 2218*4543Smarks (void) snprintf(banner, sizeof (banner), 2219*4543Smarks "Create time permissions on (%s)", curperms->z_setpoint); 2220*4543Smarks allowcb.a_treeoffset = 2221*4543Smarks offsetof(zfs_allow_node_t, z_localdescend); 2222*4543Smarks allowcb.a_permcnt = 0; 2223*4543Smarks zfs_iter_perms(&curperms->z_crperms, banner, &allowcb); 2224*4543Smarks 2225*4543Smarks 2226*4543Smarks (void) snprintf(banner, sizeof (banner), 2227*4543Smarks "Local permissions on (%s)", curperms->z_setpoint); 2228*4543Smarks allowcb.a_treeoffset = offsetof(zfs_allow_node_t, z_local); 2229*4543Smarks allowcb.a_permcnt = 0; 2230*4543Smarks zfs_iter_perms(&curperms->z_user, banner, &allowcb); 2231*4543Smarks zfs_iter_perms(&curperms->z_group, banner, &allowcb); 2232*4543Smarks zfs_iter_perms(&curperms->z_everyone, banner, &allowcb); 2233*4543Smarks 2234*4543Smarks (void) snprintf(banner, sizeof (banner), 2235*4543Smarks "Descendent permissions on (%s)", curperms->z_setpoint); 2236*4543Smarks allowcb.a_treeoffset = offsetof(zfs_allow_node_t, z_descend); 2237*4543Smarks allowcb.a_permcnt = 0; 2238*4543Smarks zfs_iter_perms(&curperms->z_user, banner, &allowcb); 2239*4543Smarks zfs_iter_perms(&curperms->z_group, banner, &allowcb); 2240*4543Smarks zfs_iter_perms(&curperms->z_everyone, banner, &allowcb); 2241*4543Smarks 2242*4543Smarks (void) snprintf(banner, sizeof (banner), 2243*4543Smarks "Local+Descendent permissions on (%s)", 2244*4543Smarks curperms->z_setpoint); 2245*4543Smarks allowcb.a_treeoffset = 2246*4543Smarks offsetof(zfs_allow_node_t, z_localdescend); 2247*4543Smarks allowcb.a_permcnt = 0; 2248*4543Smarks zfs_iter_perms(&curperms->z_user, banner, &allowcb); 2249*4543Smarks zfs_iter_perms(&curperms->z_group, banner, &allowcb); 2250*4543Smarks zfs_iter_perms(&curperms->z_everyone, banner, &allowcb); 2251*4543Smarks 2252*4543Smarks (void) printf("%s", LINES); 22532926Sek110237 } 2254*4543Smarks zfs_free_allows(perms); 2255*4543Smarks return (0); 2256*4543Smarks } 2257*4543Smarks 2258*4543Smarks #define ALLOWOPTIONS "ldcsu:g:e" 2259*4543Smarks #define UNALLOWOPTIONS "ldcsu:g:er" 2260*4543Smarks 2261*4543Smarks /* 2262*4543Smarks * Validate options, and build necessary datastructure to display/remove/add 2263*4543Smarks * permissions. 2264*4543Smarks * Returns 0 - If permissions should be added/removed 2265*4543Smarks * Returns 1 - If permissions should be displayed. 2266*4543Smarks * Returns -1 - on failure 2267*4543Smarks */ 2268*4543Smarks int 2269*4543Smarks parse_allow_args(int *argc, char **argv[], boolean_t unallow, 2270*4543Smarks char **ds, int *recurse, nvlist_t **zperms) 2271*4543Smarks { 2272*4543Smarks int c; 2273*4543Smarks char *options = unallow ? UNALLOWOPTIONS : ALLOWOPTIONS; 2274*4543Smarks zfs_deleg_inherit_t deleg_type = ZFS_DELEG_NONE; 2275*4543Smarks zfs_deleg_who_type_t who_type = ZFS_DELEG_WHO_UNKNOWN; 2276*4543Smarks char *who; 2277*4543Smarks char *perms = NULL; 2278*4543Smarks zfs_handle_t *zhp; 2279*4543Smarks 2280*4543Smarks while ((c = getopt(*argc, *argv, options)) != -1) { 2281*4543Smarks switch (c) { 2282*4543Smarks case 'l': 2283*4543Smarks if (who_type == ZFS_DELEG_CREATE || 2284*4543Smarks who_type == ZFS_DELEG_NAMED_SET) 2285*4543Smarks usage(B_FALSE); 2286*4543Smarks 2287*4543Smarks deleg_type |= ZFS_DELEG_PERM_LOCAL; 2288*4543Smarks break; 2289*4543Smarks case 'd': 2290*4543Smarks if (who_type == ZFS_DELEG_CREATE || 2291*4543Smarks who_type == ZFS_DELEG_NAMED_SET) 2292*4543Smarks usage(B_FALSE); 2293*4543Smarks 2294*4543Smarks deleg_type |= ZFS_DELEG_PERM_DESCENDENT; 2295*4543Smarks break; 2296*4543Smarks case 'r': 2297*4543Smarks *recurse = B_TRUE; 2298*4543Smarks break; 2299*4543Smarks case 'c': 2300*4543Smarks if (who_type != ZFS_DELEG_WHO_UNKNOWN) 2301*4543Smarks usage(B_FALSE); 2302*4543Smarks if (deleg_type) 2303*4543Smarks usage(B_FALSE); 2304*4543Smarks who_type = ZFS_DELEG_CREATE; 2305*4543Smarks break; 2306*4543Smarks case 's': 2307*4543Smarks if (who_type != ZFS_DELEG_WHO_UNKNOWN) 2308*4543Smarks usage(B_FALSE); 2309*4543Smarks if (deleg_type) 2310*4543Smarks usage(B_FALSE); 2311*4543Smarks who_type = ZFS_DELEG_NAMED_SET; 2312*4543Smarks break; 2313*4543Smarks case 'u': 2314*4543Smarks if (who_type != ZFS_DELEG_WHO_UNKNOWN) 2315*4543Smarks usage(B_FALSE); 2316*4543Smarks who_type = ZFS_DELEG_USER; 2317*4543Smarks who = optarg; 2318*4543Smarks break; 2319*4543Smarks case 'g': 2320*4543Smarks if (who_type != ZFS_DELEG_WHO_UNKNOWN) 2321*4543Smarks usage(B_FALSE); 2322*4543Smarks who_type = ZFS_DELEG_GROUP; 2323*4543Smarks who = optarg; 2324*4543Smarks break; 2325*4543Smarks case 'e': 2326*4543Smarks if (who_type != ZFS_DELEG_WHO_UNKNOWN) 2327*4543Smarks usage(B_FALSE); 2328*4543Smarks who_type = ZFS_DELEG_EVERYONE; 2329*4543Smarks break; 2330*4543Smarks default: 2331*4543Smarks usage(B_FALSE); 2332*4543Smarks break; 2333*4543Smarks } 2334*4543Smarks } 2335*4543Smarks 2336*4543Smarks if (deleg_type == 0) 2337*4543Smarks deleg_type = ZFS_DELEG_PERM_LOCALDESCENDENT; 2338*4543Smarks 2339*4543Smarks *argc -= optind; 2340*4543Smarks *argv += optind; 2341*4543Smarks 2342*4543Smarks if (unallow == B_FALSE && *argc == 1) { 2343*4543Smarks /* 2344*4543Smarks * Only print permissions if no options were processed 2345*4543Smarks */ 2346*4543Smarks if (optind == 1) 2347*4543Smarks return (1); 2348*4543Smarks else 2349*4543Smarks usage(B_FALSE); 2350*4543Smarks } 2351*4543Smarks 2352*4543Smarks /* 2353*4543Smarks * initialize variables for zfs_build_perms based on number 2354*4543Smarks * of arguments. 2355*4543Smarks * 3 arguments ==> zfs [un]allow joe perm,perm,perm <dataset> or 2356*4543Smarks * zfs [un]allow -s @set1 perm,perm <dataset> 2357*4543Smarks * 2 arguments ==> zfs [un]allow -c perm,perm <dataset> or 2358*4543Smarks * zfs [un]allow -u|-g <name> perm <dataset> or 2359*4543Smarks * zfs [un]allow -e perm,perm <dataset> 2360*4543Smarks * zfs unallow joe <dataset> 2361*4543Smarks * zfs unallow -s @set1 <dataset> 2362*4543Smarks * 1 argument ==> zfs [un]allow -e <dataset> or 2363*4543Smarks * zfs [un]allow -c <dataset> 2364*4543Smarks */ 2365*4543Smarks 2366*4543Smarks switch (*argc) { 2367*4543Smarks case 3: 2368*4543Smarks perms = (*argv)[1]; 2369*4543Smarks who = (*argv)[0]; 2370*4543Smarks *ds = (*argv)[2]; 2371*4543Smarks 2372*4543Smarks /* 2373*4543Smarks * advance argc/argv for do_allow cases. 2374*4543Smarks * for do_allow case make sure who have a know who type 2375*4543Smarks * and its not a permission set. 2376*4543Smarks */ 2377*4543Smarks if (unallow == B_TRUE) { 2378*4543Smarks *argc -= 2; 2379*4543Smarks *argv += 2; 2380*4543Smarks } else if (who_type != ZFS_DELEG_WHO_UNKNOWN && 2381*4543Smarks who_type != ZFS_DELEG_NAMED_SET) 2382*4543Smarks usage(B_FALSE); 2383*4543Smarks break; 2384*4543Smarks 2385*4543Smarks case 2: 2386*4543Smarks if (unallow == B_TRUE && (who_type == ZFS_DELEG_EVERYONE || 2387*4543Smarks who_type == ZFS_DELEG_CREATE || who != NULL)) { 2388*4543Smarks perms = (*argv)[0]; 2389*4543Smarks *ds = (*argv)[1]; 2390*4543Smarks } else { 2391*4543Smarks if (unallow == B_FALSE && 2392*4543Smarks (who_type == ZFS_DELEG_WHO_UNKNOWN || 2393*4543Smarks who_type == ZFS_DELEG_NAMED_SET)) 2394*4543Smarks usage(B_FALSE); 2395*4543Smarks else if (who_type == ZFS_DELEG_WHO_UNKNOWN || 2396*4543Smarks who_type == ZFS_DELEG_NAMED_SET) 2397*4543Smarks who = (*argv)[0]; 2398*4543Smarks else if (who_type != ZFS_DELEG_NAMED_SET) 2399*4543Smarks perms = (*argv)[0]; 2400*4543Smarks *ds = (*argv)[1]; 2401*4543Smarks } 2402*4543Smarks if (unallow == B_TRUE) { 2403*4543Smarks (*argc)--; 2404*4543Smarks (*argv)++; 2405*4543Smarks } 2406*4543Smarks break; 2407*4543Smarks 2408*4543Smarks case 1: 2409*4543Smarks if (unallow == B_FALSE) 2410*4543Smarks usage(B_FALSE); 2411*4543Smarks if (who == NULL && who_type != ZFS_DELEG_CREATE && 2412*4543Smarks who_type != ZFS_DELEG_EVERYONE) 2413*4543Smarks usage(B_FALSE); 2414*4543Smarks *ds = (*argv)[0]; 2415*4543Smarks break; 2416*4543Smarks 2417*4543Smarks default: 2418*4543Smarks usage(B_FALSE); 2419*4543Smarks } 2420*4543Smarks 2421*4543Smarks if (strrchr(*ds, '@')) { 2422*4543Smarks (void) fprintf(stderr, 2423*4543Smarks gettext("Can't set or remove 'allow' permissions " 2424*4543Smarks "on snapshots.\n")); 2425*4543Smarks return (-1); 2426*4543Smarks } 2427*4543Smarks 2428*4543Smarks if ((zhp = zfs_open(g_zfs, *ds, ZFS_TYPE_ANY)) == NULL) 2429*4543Smarks return (-1); 2430*4543Smarks 2431*4543Smarks if ((zfs_build_perms(zhp, who, perms, 2432*4543Smarks who_type, deleg_type, zperms)) != 0) { 2433*4543Smarks zfs_close(zhp); 2434*4543Smarks return (-1); 2435*4543Smarks } 2436*4543Smarks zfs_close(zhp); 2437*4543Smarks return (0); 2438*4543Smarks } 2439*4543Smarks 2440*4543Smarks static int 2441*4543Smarks zfs_do_allow(int argc, char **argv) 2442*4543Smarks { 2443*4543Smarks char *ds; 2444*4543Smarks nvlist_t *zperms = NULL; 2445*4543Smarks zfs_handle_t *zhp; 2446*4543Smarks int unused; 2447*4543Smarks int ret; 2448*4543Smarks 2449*4543Smarks if ((ret = parse_allow_args(&argc, &argv, B_FALSE, &ds, 2450*4543Smarks &unused, &zperms)) == -1) 2451*4543Smarks return (1); 2452*4543Smarks 2453*4543Smarks if (ret == 1) 2454*4543Smarks return (zfs_print_allows(argv[0])); 2455*4543Smarks 2456*4543Smarks if ((zhp = zfs_open(g_zfs, ds, ZFS_TYPE_ANY)) == NULL) 2457*4543Smarks return (1); 2458*4543Smarks 2459*4543Smarks if (zfs_perm_set(zhp, zperms)) { 2460*4543Smarks zfs_close(zhp); 2461*4543Smarks nvlist_free(zperms); 2462*4543Smarks return (1); 2463*4543Smarks } 2464*4543Smarks nvlist_free(zperms); 2465*4543Smarks zfs_close(zhp); 2466*4543Smarks 2467*4543Smarks return (0); 2468*4543Smarks } 2469*4543Smarks 2470*4543Smarks static int 2471*4543Smarks unallow_callback(zfs_handle_t *zhp, void *data) 2472*4543Smarks { 2473*4543Smarks nvlist_t *nvp = (nvlist_t *)data; 2474*4543Smarks int error; 2475*4543Smarks 2476*4543Smarks error = zfs_perm_remove(zhp, nvp); 2477*4543Smarks if (error) { 2478*4543Smarks (void) fprintf(stderr, gettext("Failed to remove permissions " 2479*4543Smarks "on %s\n"), zfs_get_name(zhp)); 2480*4543Smarks } 2481*4543Smarks return (error); 2482*4543Smarks } 2483*4543Smarks 2484*4543Smarks static int 2485*4543Smarks zfs_do_unallow(int argc, char **argv) 2486*4543Smarks { 2487*4543Smarks int recurse = B_FALSE; 2488*4543Smarks char *ds; 2489*4543Smarks int error; 2490*4543Smarks nvlist_t *zperms = NULL; 2491*4543Smarks 2492*4543Smarks if (parse_allow_args(&argc, &argv, B_TRUE, 2493*4543Smarks &ds, &recurse, &zperms) == -1) 2494*4543Smarks return (1); 2495*4543Smarks 2496*4543Smarks error = zfs_for_each(argc, argv, recurse, 2497*4543Smarks ZFS_TYPE_FILESYSTEM|ZFS_TYPE_VOLUME, NULL, 2498*4543Smarks NULL, unallow_callback, (void *)zperms, B_FALSE); 2499*4543Smarks 2500*4543Smarks if (zperms) 2501*4543Smarks nvlist_free(zperms); 2502*4543Smarks 2503*4543Smarks return (error); 2504789Sahrens } 2505789Sahrens 25061356Seschrock typedef struct get_all_cbdata { 25071356Seschrock zfs_handle_t **cb_handles; 25081356Seschrock size_t cb_alloc; 25091356Seschrock size_t cb_used; 25103126Sahl uint_t cb_types; 25111356Seschrock } get_all_cbdata_t; 25121356Seschrock 25131356Seschrock static int 25143126Sahl get_one_dataset(zfs_handle_t *zhp, void *data) 25151356Seschrock { 25161356Seschrock get_all_cbdata_t *cbp = data; 25173126Sahl zfs_type_t type = zfs_get_type(zhp); 25181356Seschrock 25191356Seschrock /* 25203126Sahl * Interate over any nested datasets. 25211356Seschrock */ 25223126Sahl if (type == ZFS_TYPE_FILESYSTEM && 25233126Sahl zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) { 25243265Sahrens zfs_close(zhp); 25253126Sahl return (1); 25263126Sahl } 25273126Sahl 25283126Sahl /* 25293126Sahl * Skip any datasets whose type does not match. 25303126Sahl */ 25313126Sahl if ((type & cbp->cb_types) == 0) { 25321356Seschrock zfs_close(zhp); 25331356Seschrock return (0); 25341356Seschrock } 25351356Seschrock 25361356Seschrock if (cbp->cb_alloc == cbp->cb_used) { 25371356Seschrock zfs_handle_t **handles; 25381356Seschrock 25391356Seschrock if (cbp->cb_alloc == 0) 25401356Seschrock cbp->cb_alloc = 64; 25411356Seschrock else 25421356Seschrock cbp->cb_alloc *= 2; 25431356Seschrock 25441356Seschrock handles = safe_malloc(cbp->cb_alloc * sizeof (void *)); 25451356Seschrock 25461356Seschrock if (cbp->cb_handles) { 25471356Seschrock bcopy(cbp->cb_handles, handles, 25481356Seschrock cbp->cb_used * sizeof (void *)); 25491356Seschrock free(cbp->cb_handles); 25501356Seschrock } 25511356Seschrock 25521356Seschrock cbp->cb_handles = handles; 25531356Seschrock } 25541356Seschrock 25551356Seschrock cbp->cb_handles[cbp->cb_used++] = zhp; 25561356Seschrock 25573126Sahl return (0); 25581356Seschrock } 25591356Seschrock 25601356Seschrock static void 25613126Sahl get_all_datasets(uint_t types, zfs_handle_t ***dslist, size_t *count) 25621356Seschrock { 25631356Seschrock get_all_cbdata_t cb = { 0 }; 25643126Sahl cb.cb_types = types; 25653126Sahl 25663126Sahl (void) zfs_iter_root(g_zfs, get_one_dataset, &cb); 25673126Sahl 25683126Sahl *dslist = cb.cb_handles; 25691356Seschrock *count = cb.cb_used; 25701356Seschrock } 25711356Seschrock 25721356Seschrock static int 25733126Sahl dataset_cmp(const void *a, const void *b) 25741356Seschrock { 25751356Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 25761356Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 25771356Seschrock char mounta[MAXPATHLEN]; 25781356Seschrock char mountb[MAXPATHLEN]; 25793126Sahl boolean_t gota, gotb; 25803126Sahl 25813126Sahl if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0) 25823126Sahl verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 25833126Sahl sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 25843126Sahl if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0) 25853126Sahl verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 25863126Sahl sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 25873126Sahl 25883126Sahl if (gota && gotb) 25893126Sahl return (strcmp(mounta, mountb)); 25903126Sahl 25913126Sahl if (gota) 25923126Sahl return (-1); 25933126Sahl if (gotb) 25943126Sahl return (1); 25953126Sahl 25963126Sahl return (strcmp(zfs_get_name(a), zfs_get_name(b))); 25971356Seschrock } 2598789Sahrens 2599789Sahrens /* 2600789Sahrens * Generic callback for sharing or mounting filesystems. Because the code is so 2601789Sahrens * similar, we have a common function with an extra parameter to determine which 2602789Sahrens * mode we are using. 2603789Sahrens */ 2604789Sahrens #define OP_SHARE 0x1 2605789Sahrens #define OP_MOUNT 0x2 2606789Sahrens 2607789Sahrens /* 26083126Sahl * Share or mount a dataset. 2609789Sahrens */ 2610789Sahrens static int 26113126Sahl share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit, 26123126Sahl const char *options) 2613789Sahrens { 2614789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2615789Sahrens char shareopts[ZFS_MAXPROPLEN]; 26163126Sahl const char *cmdname = op == OP_SHARE ? "share" : "mount"; 2617789Sahrens struct mnttab mnt; 26182676Seschrock uint64_t zoned, canmount; 26193126Sahl zfs_type_t type = zfs_get_type(zhp); 26203126Sahl 26213126Sahl assert(type & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)); 26223126Sahl 26233126Sahl if (type == ZFS_TYPE_FILESYSTEM) { 26243126Sahl /* 26253126Sahl * Check to make sure we can mount/share this dataset. If we 26263126Sahl * are in the global zone and the filesystem is exported to a 26273126Sahl * local zone, or if we are in a local zone and the 26283126Sahl * filesystem is not exported, then it is an error. 26293126Sahl */ 26303126Sahl zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 26313126Sahl 26323126Sahl if (zoned && getzoneid() == GLOBAL_ZONEID) { 26333126Sahl if (!explicit) 26343126Sahl return (0); 26353126Sahl 26363126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 26373126Sahl "dataset is exported to a local zone\n"), cmdname, 26383126Sahl zfs_get_name(zhp)); 26393126Sahl return (1); 26403126Sahl 26413126Sahl } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 26423126Sahl if (!explicit) 26433126Sahl return (0); 26443126Sahl 26453126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 26463126Sahl "permission denied\n"), cmdname, 26473126Sahl zfs_get_name(zhp)); 26483126Sahl return (1); 26493126Sahl } 26503126Sahl 26513126Sahl /* 26523126Sahl * Ignore any filesystems which don't apply to us. This 26533126Sahl * includes those with a legacy mountpoint, or those with 26543126Sahl * legacy share options. 26553126Sahl */ 26563126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 26573126Sahl sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); 26583126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 26593126Sahl sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 26603126Sahl canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); 26613126Sahl 26623126Sahl if (op == OP_SHARE && strcmp(shareopts, "off") == 0) { 26633126Sahl if (!explicit) 2664789Sahrens return (0); 2665789Sahrens 2666789Sahrens (void) fprintf(stderr, gettext("cannot share '%s': " 2667789Sahrens "legacy share\n"), zfs_get_name(zhp)); 2668789Sahrens (void) fprintf(stderr, gettext("use share(1M) to " 2669789Sahrens "share this filesystem\n")); 2670789Sahrens return (1); 2671789Sahrens } 26723126Sahl 26733126Sahl /* 26743126Sahl * We cannot share or mount legacy filesystems. If the 26753126Sahl * shareopts is non-legacy but the mountpoint is legacy, we 26763126Sahl * treat it as a legacy share. 26773126Sahl */ 26783126Sahl if (strcmp(mountpoint, "legacy") == 0) { 26793126Sahl if (!explicit) 26803126Sahl return (0); 26813126Sahl 26823126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 26833126Sahl "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 26843126Sahl (void) fprintf(stderr, gettext("use %s to " 26853126Sahl "%s this filesystem\n"), op == OP_SHARE ? 26863126Sahl "share(1M)" : "mount(1M)", cmdname); 26873126Sahl return (1); 26883126Sahl } 26893126Sahl 26903126Sahl if (strcmp(mountpoint, "none") == 0) { 26913126Sahl if (!explicit) 26923126Sahl return (0); 26933126Sahl 26943126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': no " 26953126Sahl "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 26963126Sahl return (1); 26973126Sahl } 26983126Sahl 26993126Sahl if (!canmount) { 27003126Sahl if (!explicit) 27013126Sahl return (0); 27023126Sahl 27033126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 27043126Sahl "'canmount' property is set to 'off'\n"), cmdname, 27053126Sahl zfs_get_name(zhp)); 27063126Sahl return (1); 27073126Sahl } 27083126Sahl 27093126Sahl /* 27103126Sahl * At this point, we have verified that the mountpoint and/or 27113126Sahl * shareopts are appropriate for auto management. If the 27123126Sahl * filesystem is already mounted or shared, return (failing 27133126Sahl * for explicit requests); otherwise mount or share the 27143126Sahl * filesystem. 27153126Sahl */ 27163126Sahl switch (op) { 27173126Sahl case OP_SHARE: 27183126Sahl if (zfs_is_shared_nfs(zhp, NULL)) { 27193126Sahl if (!explicit) 27203126Sahl return (0); 27213126Sahl 2722789Sahrens (void) fprintf(stderr, gettext("cannot share " 2723789Sahrens "'%s': filesystem already shared\n"), 2724789Sahrens zfs_get_name(zhp)); 2725789Sahrens return (1); 2726789Sahrens } 27273126Sahl 27283126Sahl if (!zfs_is_mounted(zhp, NULL) && 27293126Sahl zfs_mount(zhp, NULL, 0) != 0) 27303126Sahl return (1); 27313126Sahl 27323126Sahl if (zfs_share_nfs(zhp) != 0) 27333126Sahl return (1); 27343126Sahl break; 27353126Sahl 27363126Sahl case OP_MOUNT: 27373126Sahl if (options == NULL) 27383126Sahl mnt.mnt_mntopts = ""; 27393126Sahl else 27403126Sahl mnt.mnt_mntopts = (char *)options; 27413126Sahl 27423126Sahl if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 27433126Sahl zfs_is_mounted(zhp, NULL)) { 27443126Sahl if (!explicit) 27453126Sahl return (0); 27463126Sahl 2747789Sahrens (void) fprintf(stderr, gettext("cannot mount " 2748789Sahrens "'%s': filesystem already mounted\n"), 2749789Sahrens zfs_get_name(zhp)); 2750789Sahrens return (1); 2751789Sahrens } 27523126Sahl 27533126Sahl if (zfs_mount(zhp, options, flags) != 0) 27543126Sahl return (1); 27553126Sahl break; 2756789Sahrens } 27573126Sahl } else { 27583126Sahl assert(op == OP_SHARE); 27593126Sahl 27603126Sahl /* 27613126Sahl * Ignore any volumes that aren't shared. 27623126Sahl */ 27633126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, shareopts, 27643126Sahl sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 27653126Sahl 27663126Sahl if (strcmp(shareopts, "off") == 0) { 27673126Sahl if (!explicit) 27683126Sahl return (0); 27693126Sahl 27703126Sahl (void) fprintf(stderr, gettext("cannot share '%s': " 27713126Sahl "'shareiscsi' property not set\n"), 27723126Sahl zfs_get_name(zhp)); 27733126Sahl (void) fprintf(stderr, gettext("set 'shareiscsi' " 27743126Sahl "property or use iscsitadm(1M) to share this " 27753126Sahl "volume\n")); 27763126Sahl return (1); 2777789Sahrens } 27783126Sahl 27793126Sahl if (zfs_is_shared_iscsi(zhp)) { 27803126Sahl if (!explicit) 27813126Sahl return (0); 27823126Sahl 27833126Sahl (void) fprintf(stderr, gettext("cannot share " 27843126Sahl "'%s': volume already shared\n"), 27853126Sahl zfs_get_name(zhp)); 2786789Sahrens return (1); 27873126Sahl } 27883126Sahl 27893126Sahl if (zfs_share_iscsi(zhp) != 0) 27903126Sahl return (1); 2791789Sahrens } 2792789Sahrens 2793789Sahrens return (0); 2794789Sahrens } 2795789Sahrens 2796789Sahrens static int 27973126Sahl share_mount(int op, int argc, char **argv) 2798789Sahrens { 2799789Sahrens int do_all = 0; 28002372Slling int c, ret = 0; 28013126Sahl const char *options = NULL; 28023126Sahl int types, flags = 0; 2803789Sahrens 2804789Sahrens /* check options */ 28053126Sahl while ((c = getopt(argc, argv, op == OP_MOUNT ? ":ao:O" : "a")) 2806789Sahrens != -1) { 2807789Sahrens switch (c) { 2808789Sahrens case 'a': 2809789Sahrens do_all = 1; 2810789Sahrens break; 2811789Sahrens case 'o': 28123126Sahl options = optarg; 2813789Sahrens break; 2814789Sahrens case 'O': 28153126Sahl flags |= MS_OVERLAY; 2816789Sahrens break; 2817789Sahrens case ':': 2818789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2819789Sahrens "'%c' option\n"), optopt); 28202082Seschrock usage(B_FALSE); 2821789Sahrens break; 2822789Sahrens case '?': 2823789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2824789Sahrens optopt); 28252082Seschrock usage(B_FALSE); 2826789Sahrens } 2827789Sahrens } 2828789Sahrens 2829789Sahrens argc -= optind; 2830789Sahrens argv += optind; 2831789Sahrens 2832789Sahrens /* check number of arguments */ 2833789Sahrens if (do_all) { 28343126Sahl zfs_handle_t **dslist = NULL; 28351356Seschrock size_t i, count = 0; 28361356Seschrock 28373126Sahl if (op == OP_MOUNT) { 28383126Sahl types = ZFS_TYPE_FILESYSTEM; 28393126Sahl } else if (argc > 0) { 28403126Sahl if (strcmp(argv[0], "nfs") == 0) { 28413126Sahl types = ZFS_TYPE_FILESYSTEM; 28423126Sahl } else if (strcmp(argv[0], "iscsi") == 0) { 28433126Sahl types = ZFS_TYPE_VOLUME; 28443126Sahl } else { 28453126Sahl (void) fprintf(stderr, gettext("share type " 28463126Sahl "must be 'nfs' or 'iscsi'\n")); 28473126Sahl usage(B_FALSE); 28483126Sahl } 28493126Sahl 28503126Sahl argc--; 28513126Sahl argv++; 28523126Sahl } else { 28533126Sahl types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME; 28543126Sahl } 28553126Sahl 2856789Sahrens if (argc != 0) { 2857789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 28582082Seschrock usage(B_FALSE); 2859789Sahrens } 2860789Sahrens 28613126Sahl get_all_datasets(types, &dslist, &count); 28621356Seschrock 28631356Seschrock if (count == 0) 28641356Seschrock return (0); 28651356Seschrock 28663126Sahl qsort(dslist, count, sizeof (void *), dataset_cmp); 28671356Seschrock 28681356Seschrock for (i = 0; i < count; i++) { 28693126Sahl if (share_mount_one(dslist[i], op, flags, B_FALSE, 28703126Sahl options) != 0) 28712369Slling ret = 1; 28723126Sahl zfs_close(dslist[i]); 28731356Seschrock } 28741356Seschrock 28753126Sahl free(dslist); 2876789Sahrens } else if (argc == 0) { 2877789Sahrens struct mnttab entry; 2878789Sahrens 28793126Sahl if (op == OP_SHARE) { 2880789Sahrens (void) fprintf(stderr, gettext("missing filesystem " 2881789Sahrens "argument\n")); 28822082Seschrock usage(B_FALSE); 2883789Sahrens } 2884789Sahrens 2885789Sahrens /* 2886789Sahrens * When mount is given no arguments, go through /etc/mnttab and 2887789Sahrens * display any active ZFS mounts. We hide any snapshots, since 2888789Sahrens * they are controlled automatically. 2889789Sahrens */ 2890789Sahrens rewind(mnttab_file); 2891789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2892789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 2893789Sahrens strchr(entry.mnt_special, '@') != NULL) 2894789Sahrens continue; 2895789Sahrens 2896789Sahrens (void) printf("%-30s %s\n", entry.mnt_special, 2897789Sahrens entry.mnt_mountp); 2898789Sahrens } 2899789Sahrens 2900789Sahrens } else { 2901789Sahrens zfs_handle_t *zhp; 2902789Sahrens 29033126Sahl types = ZFS_TYPE_FILESYSTEM; 29043126Sahl if (op == OP_SHARE) 29053126Sahl types |= ZFS_TYPE_VOLUME; 29063126Sahl 2907789Sahrens if (argc > 1) { 2908789Sahrens (void) fprintf(stderr, 2909789Sahrens gettext("too many arguments\n")); 29102082Seschrock usage(B_FALSE); 2911789Sahrens } 2912789Sahrens 29133126Sahl if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) { 2914789Sahrens ret = 1; 29153126Sahl } else { 29163126Sahl ret = share_mount_one(zhp, op, flags, B_TRUE, 29173126Sahl options); 2918789Sahrens zfs_close(zhp); 2919789Sahrens } 2920789Sahrens } 2921789Sahrens 2922789Sahrens return (ret); 2923789Sahrens } 2924789Sahrens 2925789Sahrens /* 29263126Sahl * zfs mount -a [nfs | iscsi] 2927789Sahrens * zfs mount filesystem 2928789Sahrens * 2929789Sahrens * Mount all filesystems, or mount the given filesystem. 2930789Sahrens */ 2931789Sahrens static int 2932789Sahrens zfs_do_mount(int argc, char **argv) 2933789Sahrens { 29343126Sahl return (share_mount(OP_MOUNT, argc, argv)); 2935789Sahrens } 2936789Sahrens 2937789Sahrens /* 29383126Sahl * zfs share -a [nfs | iscsi] 2939789Sahrens * zfs share filesystem 2940789Sahrens * 2941789Sahrens * Share all filesystems, or share the given filesystem. 2942789Sahrens */ 2943789Sahrens static int 2944789Sahrens zfs_do_share(int argc, char **argv) 2945789Sahrens { 29463126Sahl return (share_mount(OP_SHARE, argc, argv)); 2947789Sahrens } 2948789Sahrens 2949789Sahrens typedef struct unshare_unmount_node { 2950789Sahrens zfs_handle_t *un_zhp; 2951789Sahrens char *un_mountp; 2952789Sahrens uu_avl_node_t un_avlnode; 2953789Sahrens } unshare_unmount_node_t; 2954789Sahrens 2955789Sahrens /* ARGSUSED */ 2956789Sahrens static int 2957789Sahrens unshare_unmount_compare(const void *larg, const void *rarg, void *unused) 2958789Sahrens { 2959789Sahrens const unshare_unmount_node_t *l = larg; 2960789Sahrens const unshare_unmount_node_t *r = rarg; 2961789Sahrens 2962789Sahrens return (strcmp(l->un_mountp, r->un_mountp)); 2963789Sahrens } 2964789Sahrens 2965789Sahrens /* 2966789Sahrens * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 2967789Sahrens * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 2968789Sahrens * and unmount it appropriately. 2969789Sahrens */ 2970789Sahrens static int 29713126Sahl unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) 2972789Sahrens { 2973789Sahrens zfs_handle_t *zhp; 2974789Sahrens int ret; 2975789Sahrens struct stat64 statbuf; 2976789Sahrens struct extmnttab entry; 29773126Sahl const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount"; 2978789Sahrens char property[ZFS_MAXPROPLEN]; 2979789Sahrens 2980789Sahrens /* 2981789Sahrens * Search for the path in /etc/mnttab. Rather than looking for the 2982789Sahrens * specific path, which can be fooled by non-standard paths (i.e. ".." 2983789Sahrens * or "//"), we stat() the path and search for the corresponding 2984789Sahrens * (major,minor) device pair. 2985789Sahrens */ 2986789Sahrens if (stat64(path, &statbuf) != 0) { 2987789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 2988789Sahrens cmdname, path, strerror(errno)); 2989789Sahrens return (1); 2990789Sahrens } 2991789Sahrens 2992789Sahrens /* 2993789Sahrens * Search for the given (major,minor) pair in the mount table. 2994789Sahrens */ 2995789Sahrens rewind(mnttab_file); 2996789Sahrens while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 2997789Sahrens if (entry.mnt_major == major(statbuf.st_dev) && 2998789Sahrens entry.mnt_minor == minor(statbuf.st_dev)) 2999789Sahrens break; 3000789Sahrens } 3001789Sahrens if (ret != 0) { 3002789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not " 3003789Sahrens "currently mounted\n"), cmdname, path); 3004789Sahrens return (1); 3005789Sahrens } 3006789Sahrens 3007789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 3008789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 3009789Sahrens "filesystem\n"), cmdname, path); 3010789Sahrens return (1); 3011789Sahrens } 3012789Sahrens 30132082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 30142082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 3015789Sahrens return (1); 3016789Sahrens 30173126Sahl verify(zfs_prop_get(zhp, op == OP_SHARE ? 30183912Slling ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 30193912Slling sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 3020789Sahrens 30213126Sahl if (op == OP_SHARE) { 3022789Sahrens if (strcmp(property, "off") == 0) { 3023789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 3024789Sahrens "'%s': legacy share\n"), path); 3025789Sahrens (void) fprintf(stderr, gettext("use " 3026789Sahrens "unshare(1M) to unshare this filesystem\n")); 3027789Sahrens ret = 1; 30283126Sahl } else if (!zfs_is_shared_nfs(zhp, NULL)) { 3029789Sahrens (void) fprintf(stderr, gettext("cannot unshare '%s': " 3030789Sahrens "not currently shared\n"), path); 3031789Sahrens ret = 1; 3032789Sahrens } else { 30333126Sahl ret = zfs_unshareall_nfs(zhp); 3034789Sahrens } 3035789Sahrens } else { 30361264Slling if (is_manual) { 30371264Slling ret = zfs_unmount(zhp, NULL, flags); 30381264Slling } else if (strcmp(property, "legacy") == 0) { 30391264Slling (void) fprintf(stderr, gettext("cannot unmount " 30401264Slling "'%s': legacy mountpoint\n"), 30411264Slling zfs_get_name(zhp)); 30421264Slling (void) fprintf(stderr, gettext("use umount(1M) " 30431264Slling "to unmount this filesystem\n")); 30441264Slling ret = 1; 3045789Sahrens } else { 3046789Sahrens ret = zfs_unmountall(zhp, flags); 3047789Sahrens } 3048789Sahrens } 3049789Sahrens 3050789Sahrens zfs_close(zhp); 3051789Sahrens 3052789Sahrens return (ret != 0); 3053789Sahrens } 3054789Sahrens 3055789Sahrens /* 3056789Sahrens * Generic callback for unsharing or unmounting a filesystem. 3057789Sahrens */ 3058789Sahrens static int 30593126Sahl unshare_unmount(int op, int argc, char **argv) 3060789Sahrens { 3061789Sahrens int do_all = 0; 3062789Sahrens int flags = 0; 3063789Sahrens int ret = 0; 30643126Sahl int types, c; 3065789Sahrens zfs_handle_t *zhp; 3066789Sahrens char property[ZFS_MAXPROPLEN]; 3067789Sahrens 3068789Sahrens /* check options */ 30693126Sahl while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) { 3070789Sahrens switch (c) { 3071789Sahrens case 'a': 3072789Sahrens do_all = 1; 3073789Sahrens break; 3074789Sahrens case 'f': 3075789Sahrens flags = MS_FORCE; 3076789Sahrens break; 3077789Sahrens case '?': 3078789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3079789Sahrens optopt); 30802082Seschrock usage(B_FALSE); 3081789Sahrens } 3082789Sahrens } 3083789Sahrens 3084789Sahrens argc -= optind; 3085789Sahrens argv += optind; 3086789Sahrens 3087789Sahrens if (do_all) { 3088789Sahrens /* 3089789Sahrens * We could make use of zfs_for_each() to walk all datasets in 3090789Sahrens * the system, but this would be very inefficient, especially 3091789Sahrens * since we would have to linearly search /etc/mnttab for each 3092789Sahrens * one. Instead, do one pass through /etc/mnttab looking for 3093789Sahrens * zfs entries and call zfs_unmount() for each one. 3094789Sahrens * 3095789Sahrens * Things get a little tricky if the administrator has created 3096789Sahrens * mountpoints beneath other ZFS filesystems. In this case, we 3097789Sahrens * have to unmount the deepest filesystems first. To accomplish 3098789Sahrens * this, we place all the mountpoints in an AVL tree sorted by 3099789Sahrens * the special type (dataset name), and walk the result in 3100789Sahrens * reverse to make sure to get any snapshots first. 3101789Sahrens */ 3102789Sahrens struct mnttab entry; 3103789Sahrens uu_avl_pool_t *pool; 3104789Sahrens uu_avl_t *tree; 3105789Sahrens unshare_unmount_node_t *node; 3106789Sahrens uu_avl_index_t idx; 3107789Sahrens uu_avl_walk_t *walk; 3108789Sahrens 31093126Sahl if (argc != 0) { 31103126Sahl (void) fprintf(stderr, gettext("too many arguments\n")); 31113126Sahl usage(B_FALSE); 31123126Sahl } 31133126Sahl 3114789Sahrens if ((pool = uu_avl_pool_create("unmount_pool", 3115789Sahrens sizeof (unshare_unmount_node_t), 3116789Sahrens offsetof(unshare_unmount_node_t, un_avlnode), 3117789Sahrens unshare_unmount_compare, 3118789Sahrens UU_DEFAULT)) == NULL) { 3119789Sahrens (void) fprintf(stderr, gettext("internal error: " 3120789Sahrens "out of memory\n")); 3121789Sahrens exit(1); 3122789Sahrens } 3123789Sahrens 3124789Sahrens if ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL) { 3125789Sahrens (void) fprintf(stderr, gettext("internal error: " 3126789Sahrens "out of memory\n")); 3127789Sahrens exit(1); 3128789Sahrens } 3129789Sahrens 3130789Sahrens rewind(mnttab_file); 3131789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 3132789Sahrens 3133789Sahrens /* ignore non-ZFS entries */ 3134789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 3135789Sahrens continue; 3136789Sahrens 3137789Sahrens /* ignore snapshots */ 3138789Sahrens if (strchr(entry.mnt_special, '@') != NULL) 3139789Sahrens continue; 3140789Sahrens 31412082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 3142789Sahrens ZFS_TYPE_FILESYSTEM)) == NULL) { 3143789Sahrens ret = 1; 3144789Sahrens continue; 3145789Sahrens } 3146789Sahrens 31473126Sahl verify(zfs_prop_get(zhp, op == OP_SHARE ? 3148789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 3149789Sahrens property, sizeof (property), NULL, NULL, 31502082Seschrock 0, B_FALSE) == 0); 3151789Sahrens 3152789Sahrens /* Ignore legacy mounts and shares */ 31533126Sahl if ((op == OP_SHARE && 3154789Sahrens strcmp(property, "off") == 0) || 31553126Sahl (op == OP_MOUNT && 3156789Sahrens strcmp(property, "legacy") == 0)) { 3157789Sahrens zfs_close(zhp); 3158789Sahrens continue; 3159789Sahrens } 3160789Sahrens 3161789Sahrens node = safe_malloc(sizeof (unshare_unmount_node_t)); 3162789Sahrens node->un_zhp = zhp; 3163789Sahrens 3164789Sahrens if ((node->un_mountp = strdup(entry.mnt_mountp)) == 3165789Sahrens NULL) { 3166789Sahrens (void) fprintf(stderr, gettext("internal error:" 3167789Sahrens " out of memory\n")); 3168789Sahrens exit(1); 3169789Sahrens } 3170789Sahrens 3171789Sahrens uu_avl_node_init(node, &node->un_avlnode, pool); 3172789Sahrens 3173789Sahrens if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 3174789Sahrens uu_avl_insert(tree, node, idx); 3175789Sahrens } else { 3176789Sahrens zfs_close(node->un_zhp); 3177789Sahrens free(node->un_mountp); 3178789Sahrens free(node); 3179789Sahrens } 3180789Sahrens } 3181789Sahrens 3182789Sahrens /* 3183789Sahrens * Walk the AVL tree in reverse, unmounting each filesystem and 3184789Sahrens * removing it from the AVL tree in the process. 3185789Sahrens */ 3186789Sahrens if ((walk = uu_avl_walk_start(tree, 3187789Sahrens UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) { 3188789Sahrens (void) fprintf(stderr, 3189789Sahrens gettext("internal error: out of memory")); 3190789Sahrens exit(1); 3191789Sahrens } 3192789Sahrens 3193789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 3194789Sahrens uu_avl_remove(tree, node); 3195789Sahrens 31963126Sahl switch (op) { 3197789Sahrens case OP_SHARE: 31983126Sahl if (zfs_unshare_nfs(node->un_zhp, 3199789Sahrens node->un_mountp) != 0) 3200789Sahrens ret = 1; 3201789Sahrens break; 3202789Sahrens 3203789Sahrens case OP_MOUNT: 3204789Sahrens if (zfs_unmount(node->un_zhp, 3205789Sahrens node->un_mountp, flags) != 0) 3206789Sahrens ret = 1; 3207789Sahrens break; 3208789Sahrens } 3209789Sahrens 3210789Sahrens zfs_close(node->un_zhp); 3211789Sahrens free(node->un_mountp); 3212789Sahrens free(node); 3213789Sahrens } 3214789Sahrens 3215789Sahrens uu_avl_walk_end(walk); 3216789Sahrens uu_avl_destroy(tree); 3217789Sahrens uu_avl_pool_destroy(pool); 32183126Sahl 32193126Sahl if (op == OP_SHARE) { 32203126Sahl /* 32213126Sahl * Finally, unshare any volumes shared via iSCSI. 32223126Sahl */ 32233126Sahl zfs_handle_t **dslist = NULL; 32243126Sahl size_t i, count = 0; 32253126Sahl 32263126Sahl get_all_datasets(ZFS_TYPE_VOLUME, &dslist, &count); 32273126Sahl 32283126Sahl if (count != 0) { 32293126Sahl qsort(dslist, count, sizeof (void *), 32303126Sahl dataset_cmp); 32313126Sahl 32323126Sahl for (i = 0; i < count; i++) { 32333126Sahl if (zfs_unshare_iscsi(dslist[i]) != 0) 32343126Sahl ret = 1; 32353126Sahl zfs_close(dslist[i]); 32363126Sahl } 32373126Sahl 32383126Sahl free(dslist); 32393126Sahl } 32403126Sahl } 3241789Sahrens } else { 32423126Sahl if (argc != 1) { 32433126Sahl if (argc == 0) 32443126Sahl (void) fprintf(stderr, 32453126Sahl gettext("missing filesystem argument\n")); 32463126Sahl else 32473126Sahl (void) fprintf(stderr, 32483126Sahl gettext("too many arguments\n")); 32493126Sahl usage(B_FALSE); 32503126Sahl } 32513126Sahl 3252789Sahrens /* 3253789Sahrens * We have an argument, but it may be a full path or a ZFS 3254789Sahrens * filesystem. Pass full paths off to unmount_path() (shared by 3255789Sahrens * manual_unmount), otherwise open the filesystem and pass to 3256789Sahrens * zfs_unmount(). 3257789Sahrens */ 3258789Sahrens if (argv[0][0] == '/') 32593126Sahl return (unshare_unmount_path(op, argv[0], 32603912Slling flags, B_FALSE)); 32612082Seschrock 32623126Sahl types = ZFS_TYPE_FILESYSTEM; 32633126Sahl if (op == OP_SHARE) 32643126Sahl types |= ZFS_TYPE_VOLUME; 32653126Sahl 32663126Sahl if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) 3267789Sahrens return (1); 3268789Sahrens 32693126Sahl if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 32703126Sahl verify(zfs_prop_get(zhp, op == OP_SHARE ? 32713126Sahl ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 32723126Sahl sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 32733126Sahl 32743126Sahl switch (op) { 32753126Sahl case OP_SHARE: 32763126Sahl if (strcmp(property, "off") == 0) { 32773126Sahl (void) fprintf(stderr, gettext("cannot " 32783126Sahl "unshare '%s': legacy share\n"), 32793126Sahl zfs_get_name(zhp)); 32803126Sahl (void) fprintf(stderr, gettext("use " 32813126Sahl "unshare(1M) to unshare this " 32823126Sahl "filesystem\n")); 32833126Sahl ret = 1; 32843126Sahl } else if (!zfs_is_shared_nfs(zhp, NULL)) { 32853126Sahl (void) fprintf(stderr, gettext("cannot " 32863126Sahl "unshare '%s': not currently " 32873126Sahl "shared\n"), zfs_get_name(zhp)); 32883126Sahl ret = 1; 32893126Sahl } else if (zfs_unshareall_nfs(zhp) != 0) { 32903126Sahl ret = 1; 32913126Sahl } 32923126Sahl break; 32933126Sahl 32943126Sahl case OP_MOUNT: 32953126Sahl if (strcmp(property, "legacy") == 0) { 32963126Sahl (void) fprintf(stderr, gettext("cannot " 32973126Sahl "unmount '%s': legacy " 32983126Sahl "mountpoint\n"), zfs_get_name(zhp)); 32993126Sahl (void) fprintf(stderr, gettext("use " 33003126Sahl "umount(1M) to unmount this " 33013126Sahl "filesystem\n")); 33023126Sahl ret = 1; 33033126Sahl } else if (!zfs_is_mounted(zhp, NULL)) { 33043126Sahl (void) fprintf(stderr, gettext("cannot " 33053126Sahl "unmount '%s': not currently " 33063126Sahl "mounted\n"), 33073126Sahl zfs_get_name(zhp)); 33083126Sahl ret = 1; 33093126Sahl } else if (zfs_unmountall(zhp, flags) != 0) { 33103126Sahl ret = 1; 33113126Sahl } 33123126Sahl break; 33133126Sahl } 33143126Sahl } else { 33153126Sahl assert(op == OP_SHARE); 33163126Sahl 33173126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, property, 33183126Sahl sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 33193126Sahl 3320789Sahrens if (strcmp(property, "off") == 0) { 3321789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 33223126Sahl "'%s': 'shareiscsi' property not set\n"), 33233126Sahl zfs_get_name(zhp)); 33243126Sahl (void) fprintf(stderr, gettext("set " 33253126Sahl "'shareiscsi' property or use " 33263126Sahl "iscsitadm(1M) to share this volume\n")); 3327789Sahrens ret = 1; 33283126Sahl } else if (!zfs_is_shared_iscsi(zhp)) { 33293126Sahl (void) fprintf(stderr, gettext("cannot " 33303126Sahl "unshare '%s': not currently shared\n"), 3331789Sahrens zfs_get_name(zhp)); 3332789Sahrens ret = 1; 33333126Sahl } else if (zfs_unshare_iscsi(zhp) != 0) { 3334789Sahrens ret = 1; 3335789Sahrens } 3336789Sahrens } 3337789Sahrens 3338789Sahrens zfs_close(zhp); 3339789Sahrens } 3340789Sahrens 3341789Sahrens return (ret); 3342789Sahrens } 3343789Sahrens 3344789Sahrens /* 3345789Sahrens * zfs unmount -a 3346789Sahrens * zfs unmount filesystem 3347789Sahrens * 3348789Sahrens * Unmount all filesystems, or a specific ZFS filesystem. 3349789Sahrens */ 3350789Sahrens static int 3351789Sahrens zfs_do_unmount(int argc, char **argv) 3352789Sahrens { 3353789Sahrens return (unshare_unmount(OP_MOUNT, argc, argv)); 3354789Sahrens } 3355789Sahrens 3356789Sahrens /* 3357789Sahrens * zfs unshare -a 3358789Sahrens * zfs unshare filesystem 3359789Sahrens * 3360789Sahrens * Unshare all filesystems, or a specific ZFS filesystem. 3361789Sahrens */ 3362789Sahrens static int 3363789Sahrens zfs_do_unshare(int argc, char **argv) 3364789Sahrens { 3365789Sahrens return (unshare_unmount(OP_SHARE, argc, argv)); 3366789Sahrens } 3367789Sahrens 3368789Sahrens /* 3369789Sahrens * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 3370789Sahrens * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 3371789Sahrens */ 3372789Sahrens static int 3373789Sahrens manual_mount(int argc, char **argv) 3374789Sahrens { 3375789Sahrens zfs_handle_t *zhp; 3376789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 3377789Sahrens char mntopts[MNT_LINE_MAX] = { '\0' }; 3378789Sahrens int ret; 3379789Sahrens int c; 3380789Sahrens int flags = 0; 3381789Sahrens char *dataset, *path; 3382789Sahrens 3383789Sahrens /* check options */ 33841544Seschrock while ((c = getopt(argc, argv, ":mo:O")) != -1) { 3385789Sahrens switch (c) { 3386789Sahrens case 'o': 3387789Sahrens (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 3388789Sahrens break; 3389789Sahrens case 'O': 3390789Sahrens flags |= MS_OVERLAY; 3391789Sahrens break; 33921544Seschrock case 'm': 33931544Seschrock flags |= MS_NOMNTTAB; 33941544Seschrock break; 3395789Sahrens case ':': 3396789Sahrens (void) fprintf(stderr, gettext("missing argument for " 3397789Sahrens "'%c' option\n"), optopt); 33982082Seschrock usage(B_FALSE); 3399789Sahrens break; 3400789Sahrens case '?': 3401789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3402789Sahrens optopt); 3403789Sahrens (void) fprintf(stderr, gettext("usage: mount [-o opts] " 3404789Sahrens "<path>\n")); 3405789Sahrens return (2); 3406789Sahrens } 3407789Sahrens } 3408789Sahrens 3409789Sahrens argc -= optind; 3410789Sahrens argv += optind; 3411789Sahrens 3412789Sahrens /* check that we only have two arguments */ 3413789Sahrens if (argc != 2) { 3414789Sahrens if (argc == 0) 3415789Sahrens (void) fprintf(stderr, gettext("missing dataset " 3416789Sahrens "argument\n")); 3417789Sahrens else if (argc == 1) 3418789Sahrens (void) fprintf(stderr, 3419789Sahrens gettext("missing mountpoint argument\n")); 3420789Sahrens else 3421789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 3422789Sahrens (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 3423789Sahrens return (2); 3424789Sahrens } 3425789Sahrens 3426789Sahrens dataset = argv[0]; 3427789Sahrens path = argv[1]; 3428789Sahrens 3429789Sahrens /* try to open the dataset */ 34302082Seschrock if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 3431789Sahrens return (1); 3432789Sahrens 3433789Sahrens (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 34342082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE); 3435789Sahrens 3436789Sahrens /* check for legacy mountpoint and complain appropriately */ 3437789Sahrens ret = 0; 3438789Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 3439789Sahrens if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS, 3440789Sahrens NULL, 0, mntopts, sizeof (mntopts)) != 0) { 3441789Sahrens (void) fprintf(stderr, gettext("mount failed: %s\n"), 3442789Sahrens strerror(errno)); 3443789Sahrens ret = 1; 3444789Sahrens } 3445789Sahrens } else { 3446789Sahrens (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 3447789Sahrens "mounted using 'mount -F zfs'\n"), dataset); 3448789Sahrens (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 3449789Sahrens "instead.\n"), path); 3450789Sahrens (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' " 3451789Sahrens "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n")); 3452789Sahrens (void) fprintf(stderr, gettext("See zfs(1M) for more " 3453789Sahrens "information.\n")); 3454789Sahrens ret = 1; 3455789Sahrens } 3456789Sahrens 3457789Sahrens return (ret); 3458789Sahrens } 3459789Sahrens 3460789Sahrens /* 3461789Sahrens * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 3462789Sahrens * unmounts of non-legacy filesystems, as this is the dominant administrative 3463789Sahrens * interface. 3464789Sahrens */ 3465789Sahrens static int 3466789Sahrens manual_unmount(int argc, char **argv) 3467789Sahrens { 3468789Sahrens int flags = 0; 3469789Sahrens int c; 3470789Sahrens 3471789Sahrens /* check options */ 3472789Sahrens while ((c = getopt(argc, argv, "f")) != -1) { 3473789Sahrens switch (c) { 3474789Sahrens case 'f': 3475789Sahrens flags = MS_FORCE; 3476789Sahrens break; 3477789Sahrens case '?': 3478789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3479789Sahrens optopt); 3480789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] " 3481789Sahrens "<path>\n")); 3482789Sahrens return (2); 3483789Sahrens } 3484789Sahrens } 3485789Sahrens 3486789Sahrens argc -= optind; 3487789Sahrens argv += optind; 3488789Sahrens 3489789Sahrens /* check arguments */ 3490789Sahrens if (argc != 1) { 3491789Sahrens if (argc == 0) 3492789Sahrens (void) fprintf(stderr, gettext("missing path " 3493789Sahrens "argument\n")); 3494789Sahrens else 3495789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 3496789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 3497789Sahrens return (2); 3498789Sahrens } 3499789Sahrens 35002082Seschrock return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE)); 3501789Sahrens } 3502789Sahrens 3503789Sahrens static int 3504789Sahrens volcheck(zpool_handle_t *zhp, void *data) 3505789Sahrens { 35062856Snd150628 boolean_t isinit = *((boolean_t *)data); 3507789Sahrens 3508789Sahrens if (isinit) 3509789Sahrens return (zpool_create_zvol_links(zhp)); 3510789Sahrens else 3511789Sahrens return (zpool_remove_zvol_links(zhp)); 3512789Sahrens } 3513789Sahrens 3514789Sahrens /* 3515789Sahrens * Iterate over all pools in the system and either create or destroy /dev/zvol 3516789Sahrens * links, depending on the value of 'isinit'. 3517789Sahrens */ 3518789Sahrens static int 35192082Seschrock do_volcheck(boolean_t isinit) 3520789Sahrens { 35212856Snd150628 return (zpool_iter(g_zfs, volcheck, &isinit) ? 1 : 0); 3522789Sahrens } 3523789Sahrens 3524*4543Smarks static int 3525*4543Smarks find_command_idx(char *command, int *idx) 3526*4543Smarks { 3527*4543Smarks int i; 3528*4543Smarks 3529*4543Smarks for (i = 0; i < NCOMMAND; i++) { 3530*4543Smarks if (command_table[i].name == NULL) 3531*4543Smarks continue; 3532*4543Smarks 3533*4543Smarks if (strcmp(command, command_table[i].name) == 0) { 3534*4543Smarks *idx = i; 3535*4543Smarks return (0); 3536*4543Smarks } 3537*4543Smarks } 3538*4543Smarks return (1); 3539*4543Smarks } 3540*4543Smarks 3541*4543Smarks zfs_prop_t 3542*4543Smarks propset_cb(zfs_prop_t prop, void *data) 3543*4543Smarks { 3544*4543Smarks char *cmdname = (char *)data; 3545*4543Smarks 3546*4543Smarks if (strcmp(cmdname, zfs_prop_to_name(prop)) == 0) 3547*4543Smarks return (prop); 3548*4543Smarks 3549*4543Smarks return (ZFS_PROP_CONT); 3550*4543Smarks } 3551*4543Smarks 3552789Sahrens int 3553789Sahrens main(int argc, char **argv) 3554789Sahrens { 3555789Sahrens int ret; 3556789Sahrens int i; 3557789Sahrens char *progname; 3558789Sahrens char *cmdname; 3559*4543Smarks char *str; 3560*4543Smarks boolean_t found = B_FALSE; 3561789Sahrens 3562789Sahrens (void) setlocale(LC_ALL, ""); 3563789Sahrens (void) textdomain(TEXT_DOMAIN); 3564789Sahrens 3565789Sahrens opterr = 0; 3566789Sahrens 35672082Seschrock if ((g_zfs = libzfs_init()) == NULL) { 35682082Seschrock (void) fprintf(stderr, gettext("internal error: failed to " 35692082Seschrock "initialize ZFS library\n")); 35702082Seschrock return (1); 35712082Seschrock } 35722082Seschrock 3573*4543Smarks zpool_stage_history(g_zfs, argc, argv, B_TRUE, B_FALSE); 3574*4543Smarks 35752082Seschrock libzfs_print_on_error(g_zfs, B_TRUE); 35762082Seschrock 3577789Sahrens if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 3578789Sahrens (void) fprintf(stderr, gettext("internal error: unable to " 3579789Sahrens "open %s\n"), MNTTAB); 3580789Sahrens return (1); 3581789Sahrens } 3582789Sahrens 3583789Sahrens /* 3584789Sahrens * This command also doubles as the /etc/fs mount and unmount program. 3585789Sahrens * Determine if we should take this behavior based on argv[0]. 3586789Sahrens */ 3587789Sahrens progname = basename(argv[0]); 3588789Sahrens if (strcmp(progname, "mount") == 0) { 3589789Sahrens ret = manual_mount(argc, argv); 3590789Sahrens } else if (strcmp(progname, "umount") == 0) { 3591789Sahrens ret = manual_unmount(argc, argv); 3592789Sahrens } else { 3593789Sahrens /* 3594789Sahrens * Make sure the user has specified some command. 3595789Sahrens */ 3596789Sahrens if (argc < 2) { 3597789Sahrens (void) fprintf(stderr, gettext("missing command\n")); 35982082Seschrock usage(B_FALSE); 3599789Sahrens } 3600789Sahrens 3601789Sahrens cmdname = argv[1]; 3602789Sahrens 3603789Sahrens /* 3604789Sahrens * The 'umount' command is an alias for 'unmount' 3605789Sahrens */ 3606789Sahrens if (strcmp(cmdname, "umount") == 0) 3607789Sahrens cmdname = "unmount"; 3608789Sahrens 3609789Sahrens /* 36101749Sahrens * The 'recv' command is an alias for 'receive' 36111749Sahrens */ 36121749Sahrens if (strcmp(cmdname, "recv") == 0) 36131749Sahrens cmdname = "receive"; 36141749Sahrens 36151749Sahrens /* 3616789Sahrens * Special case '-?' 3617789Sahrens */ 3618789Sahrens if (strcmp(cmdname, "-?") == 0) 36192082Seschrock usage(B_TRUE); 3620789Sahrens 3621789Sahrens /* 3622789Sahrens * 'volinit' and 'volfini' do not appear in the usage message, 3623789Sahrens * so we have to special case them here. 3624789Sahrens */ 3625789Sahrens if (strcmp(cmdname, "volinit") == 0) 36262082Seschrock return (do_volcheck(B_TRUE)); 3627789Sahrens else if (strcmp(cmdname, "volfini") == 0) 36282082Seschrock return (do_volcheck(B_FALSE)); 3629789Sahrens 3630789Sahrens /* 3631789Sahrens * Run the appropriate command. 3632789Sahrens */ 3633*4543Smarks if (find_command_idx(cmdname, &i) == 0) { 3634*4543Smarks current_command = &command_table[i]; 3635*4543Smarks ret = command_table[i].func(argc - 1, argv + 1); 3636*4543Smarks found = B_TRUE; 3637*4543Smarks } 3638*4543Smarks 3639*4543Smarks /* 3640*4543Smarks * Check and see if they are doing property=value 3641*4543Smarks */ 3642*4543Smarks if (found == B_FALSE && 3643*4543Smarks ((str = strchr(cmdname, '=')) != NULL)) { 3644*4543Smarks *str = '\0'; 3645*4543Smarks if (zfs_prop_iter(propset_cb, cmdname, 3646*4543Smarks B_FALSE) != ZFS_PROP_INVAL) 3647*4543Smarks found = B_TRUE; 3648*4543Smarks 3649*4543Smarks if (found == B_FALSE && zfs_prop_user(cmdname)) 3650*4543Smarks found = B_TRUE; 3651*4543Smarks 3652*4543Smarks if (found == B_TRUE && 3653*4543Smarks find_command_idx("set", &i) == 0) { 3654*4543Smarks *str = '='; 3655789Sahrens current_command = &command_table[i]; 3656*4543Smarks ret = command_table[i].func(argc, argv); 3657*4543Smarks } else { 3658*4543Smarks (void) fprintf(stderr, 3659*4543Smarks gettext("invalid property '%s'\n"), 3660*4543Smarks cmdname); 3661*4543Smarks found = B_TRUE; 3662*4543Smarks ret = 1; 3663789Sahrens } 3664*4543Smarks 3665789Sahrens } 3666789Sahrens 3667*4543Smarks if (found == B_FALSE) { 3668789Sahrens (void) fprintf(stderr, gettext("unrecognized " 3669789Sahrens "command '%s'\n"), cmdname); 36702082Seschrock usage(B_FALSE); 3671789Sahrens } 3672789Sahrens } 3673789Sahrens 3674789Sahrens (void) fclose(mnttab_file); 3675789Sahrens 36762082Seschrock libzfs_fini(g_zfs); 36772082Seschrock 3678789Sahrens /* 3679789Sahrens * The 'ZFS_ABORT' environment variable causes us to dump core on exit 3680789Sahrens * for the purposes of running ::findleaks. 3681789Sahrens */ 3682789Sahrens if (getenv("ZFS_ABORT") != NULL) { 3683789Sahrens (void) printf("dumping core by request\n"); 3684789Sahrens abort(); 3685789Sahrens } 3686789Sahrens 3687789Sahrens return (ret); 3688789Sahrens } 3689