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> 354543Smarks #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> 494543Smarks #include <sys/avl.h> 50789Sahrens 51789Sahrens #include <libzfs.h> 524543Smarks #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; 604577Sahrens static int first_argc; 614577Sahrens static char **first_argv; 62789Sahrens 63789Sahrens static int zfs_do_clone(int argc, char **argv); 64789Sahrens static int zfs_do_create(int argc, char **argv); 65789Sahrens static int zfs_do_destroy(int argc, char **argv); 66789Sahrens static int zfs_do_get(int argc, char **argv); 67789Sahrens static int zfs_do_inherit(int argc, char **argv); 68789Sahrens static int zfs_do_list(int argc, char **argv); 69789Sahrens static int zfs_do_mount(int argc, char **argv); 70789Sahrens static int zfs_do_rename(int argc, char **argv); 71789Sahrens static int zfs_do_rollback(int argc, char **argv); 72789Sahrens static int zfs_do_set(int argc, char **argv); 734577Sahrens static int zfs_do_upgrade(int argc, char **argv); 74789Sahrens static int zfs_do_snapshot(int argc, char **argv); 75789Sahrens static int zfs_do_unmount(int argc, char **argv); 76789Sahrens static int zfs_do_share(int argc, char **argv); 77789Sahrens static int zfs_do_unshare(int argc, char **argv); 781749Sahrens static int zfs_do_send(int argc, char **argv); 791749Sahrens static int zfs_do_receive(int argc, char **argv); 802082Seschrock static int zfs_do_promote(int argc, char **argv); 814543Smarks static int zfs_do_allow(int argc, char **argv); 824543Smarks static int zfs_do_unallow(int argc, char **argv); 83789Sahrens 84789Sahrens /* 85789Sahrens * These libumem hooks provide a reasonable set of defaults for the allocator's 86789Sahrens * debugging facilities. 87789Sahrens */ 88789Sahrens const char * 893126Sahl _umem_debug_init(void) 90789Sahrens { 91789Sahrens return ("default,verbose"); /* $UMEM_DEBUG setting */ 92789Sahrens } 93789Sahrens 94789Sahrens const char * 95789Sahrens _umem_logging_init(void) 96789Sahrens { 97789Sahrens return ("fail,contents"); /* $UMEM_LOGGING setting */ 98789Sahrens } 99789Sahrens 1001387Seschrock typedef enum { 1011387Seschrock HELP_CLONE, 1021387Seschrock HELP_CREATE, 1031387Seschrock HELP_DESTROY, 1041387Seschrock HELP_GET, 1051387Seschrock HELP_INHERIT, 1064577Sahrens HELP_UPGRADE, 1071387Seschrock HELP_LIST, 1081387Seschrock HELP_MOUNT, 1092082Seschrock HELP_PROMOTE, 1101749Sahrens HELP_RECEIVE, 1111387Seschrock HELP_RENAME, 1121387Seschrock HELP_ROLLBACK, 1131749Sahrens HELP_SEND, 1141387Seschrock HELP_SET, 1151387Seschrock HELP_SHARE, 1161387Seschrock HELP_SNAPSHOT, 1171387Seschrock HELP_UNMOUNT, 1184543Smarks HELP_UNSHARE, 1194543Smarks HELP_ALLOW, 1204543Smarks HELP_UNALLOW 1211387Seschrock } zfs_help_t; 1221387Seschrock 123789Sahrens typedef struct zfs_command { 124789Sahrens const char *name; 125789Sahrens int (*func)(int argc, char **argv); 1261387Seschrock zfs_help_t usage; 127789Sahrens } zfs_command_t; 128789Sahrens 129789Sahrens /* 130789Sahrens * Master command table. Each ZFS command has a name, associated function, and 1311544Seschrock * usage message. The usage messages need to be internationalized, so we have 1321544Seschrock * to have a function to return the usage message based on a command index. 1331387Seschrock * 1341387Seschrock * These commands are organized according to how they are displayed in the usage 1351387Seschrock * message. An empty command (one with a NULL name) indicates an empty line in 1361387Seschrock * the generic usage message. 137789Sahrens */ 138789Sahrens static zfs_command_t command_table[] = { 1391387Seschrock { "create", zfs_do_create, HELP_CREATE }, 1401387Seschrock { "destroy", zfs_do_destroy, HELP_DESTROY }, 141789Sahrens { NULL }, 1421387Seschrock { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT }, 1431387Seschrock { "rollback", zfs_do_rollback, HELP_ROLLBACK }, 1441387Seschrock { "clone", zfs_do_clone, HELP_CLONE }, 1452082Seschrock { "promote", zfs_do_promote, HELP_PROMOTE }, 1461387Seschrock { "rename", zfs_do_rename, HELP_RENAME }, 147789Sahrens { NULL }, 1481387Seschrock { "list", zfs_do_list, HELP_LIST }, 149789Sahrens { NULL }, 1501387Seschrock { "set", zfs_do_set, HELP_SET }, 1511387Seschrock { "get", zfs_do_get, HELP_GET }, 1521387Seschrock { "inherit", zfs_do_inherit, HELP_INHERIT }, 1534577Sahrens { "upgrade", zfs_do_upgrade, HELP_UPGRADE }, 154789Sahrens { NULL }, 1551387Seschrock { "mount", zfs_do_mount, HELP_MOUNT }, 156789Sahrens { NULL }, 1571387Seschrock { "unmount", zfs_do_unmount, HELP_UNMOUNT }, 158789Sahrens { NULL }, 1591387Seschrock { "share", zfs_do_share, HELP_SHARE }, 160789Sahrens { NULL }, 1611387Seschrock { "unshare", zfs_do_unshare, HELP_UNSHARE }, 162789Sahrens { NULL }, 1631749Sahrens { "send", zfs_do_send, HELP_SEND }, 1641749Sahrens { "receive", zfs_do_receive, HELP_RECEIVE }, 1654543Smarks { NULL }, 1664543Smarks { "allow", zfs_do_allow, HELP_ALLOW }, 1674543Smarks { NULL }, 1684543Smarks { "unallow", zfs_do_unallow, HELP_UNALLOW }, 169789Sahrens }; 170789Sahrens 171789Sahrens #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) 172789Sahrens 173789Sahrens zfs_command_t *current_command; 174789Sahrens 1751387Seschrock static const char * 1761387Seschrock get_usage(zfs_help_t idx) 1771387Seschrock { 1781387Seschrock switch (idx) { 1791387Seschrock case HELP_CLONE: 1804490Svb160487 return (gettext("\tclone [-p] <snapshot> " 1814490Svb160487 "<filesystem|volume>\n")); 1821387Seschrock case HELP_CREATE: 1834490Svb160487 return (gettext("\tcreate [-p] [[-o property=value] ... ] " 1842676Seschrock "<filesystem>\n" 1854490Svb160487 "\tcreate [-ps] [-b blocksize] [[-o property=value] " 1864490Svb160487 "...]\n" 1872676Seschrock "\t -V <size> <volume>\n")); 1881387Seschrock case HELP_DESTROY: 1891387Seschrock return (gettext("\tdestroy [-rRf] " 1901387Seschrock "<filesystem|volume|snapshot>\n")); 1911387Seschrock case HELP_GET: 1921387Seschrock return (gettext("\tget [-rHp] [-o field[,field]...] " 1931387Seschrock "[-s source[,source]...]\n" 1941387Seschrock "\t <all | property[,property]...> " 1952676Seschrock "[filesystem|volume|snapshot] ...\n")); 1961387Seschrock case HELP_INHERIT: 1971387Seschrock return (gettext("\tinherit [-r] <property> " 1981387Seschrock "<filesystem|volume> ...\n")); 1994577Sahrens case HELP_UPGRADE: 2004577Sahrens return (gettext("\tupgrade [-v]\n" 2014577Sahrens "\tupgrade [-r] [-V version] <-a | filesystem ...>\n")); 2021387Seschrock case HELP_LIST: 2031387Seschrock return (gettext("\tlist [-rH] [-o property[,property]...] " 2041387Seschrock "[-t type[,type]...]\n" 2052379Ssjelinek "\t [-s property [-s property]...]" 2062379Ssjelinek " [-S property [-S property]...]\n" 2071387Seschrock "\t [filesystem|volume|snapshot] ...\n")); 2081387Seschrock case HELP_MOUNT: 2091387Seschrock return (gettext("\tmount\n" 210*4737Smmusante "\tmount [-o opts] [-vO] -a\n" 211*4737Smmusante "\tmount [-o opts] [-vO] <filesystem>\n")); 2122082Seschrock case HELP_PROMOTE: 2132082Seschrock return (gettext("\tpromote <clone filesystem>\n")); 2141749Sahrens case HELP_RECEIVE: 2152665Snd150628 return (gettext("\treceive [-vnF] <filesystem|volume|" 2162665Snd150628 "snapshot>\n" 2172665Snd150628 "\treceive [-vnF] -d <filesystem>\n")); 2181387Seschrock case HELP_RENAME: 2191387Seschrock return (gettext("\trename <filesystem|volume|snapshot> " 2204007Smmusante "<filesystem|volume|snapshot>\n" 2214490Svb160487 "\trename -p <filesystem|volume> <filesystem|volume>\n" 2224007Smmusante "\trename -r <snapshot> <snapshot>")); 2231387Seschrock case HELP_ROLLBACK: 2241387Seschrock return (gettext("\trollback [-rRf] <snapshot>\n")); 2251749Sahrens case HELP_SEND: 2261749Sahrens return (gettext("\tsend [-i <snapshot>] <snapshot>\n")); 2271387Seschrock case HELP_SET: 2281387Seschrock return (gettext("\tset <property=value> " 2291387Seschrock "<filesystem|volume> ...\n")); 2301387Seschrock case HELP_SHARE: 2311387Seschrock return (gettext("\tshare -a\n" 2321387Seschrock "\tshare <filesystem>\n")); 2331387Seschrock case HELP_SNAPSHOT: 2342199Sahrens return (gettext("\tsnapshot [-r] " 2352199Sahrens "<filesystem@name|volume@name>\n")); 2361387Seschrock case HELP_UNMOUNT: 2371387Seschrock return (gettext("\tunmount [-f] -a\n" 2381387Seschrock "\tunmount [-f] <filesystem|mountpoint>\n")); 2391387Seschrock case HELP_UNSHARE: 2401387Seschrock return (gettext("\tunshare [-f] -a\n" 2411387Seschrock "\tunshare [-f] <filesystem|mountpoint>\n")); 2424543Smarks case HELP_ALLOW: 2434543Smarks return (gettext("\tallow [-l][-d] <everyone|user|group>[," 2444543Smarks "<everyone|user|group>...]\n\t " 2454543Smarks "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" 2464543Smarks " <filesystem|volume\n" 2474543Smarks "\tallow [-l] [-d] -u <user> " 2484543Smarks "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" 2494543Smarks " <filesystem|volume>\n" 2504543Smarks "\tallow [-l] [-d] -g <group> " 2514543Smarks "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" 2524543Smarks " <filesystem|volume>\n" 2534543Smarks "\tallow [-l] [-d] -e " 2544543Smarks "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" 2554543Smarks " <filesystem|volume>\n" 2564543Smarks "\tallow -c " 2574543Smarks "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" 2584543Smarks " <filesystem|volume>\n" 2594543Smarks "\tallow -s @setname " 2604543Smarks "<perm>|@<setname>[,<perm>|@<setname>...]\n\t" 2614543Smarks " <filesystem|volume>\n")); 2624543Smarks 2634543Smarks case HELP_UNALLOW: 2644543Smarks return (gettext("\tunallow [-r][-l][-d] <everyone|user|group>[," 2654543Smarks "<everyone|user|group>...] \n\t " 2664543Smarks "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" 2674543Smarks " <filesystem|volume>\n" 2684543Smarks "\tunallow [-r][-l][-d] -u user " 2694543Smarks "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" 2704543Smarks " <filesystem|volume>\n" 2714543Smarks "\tunallow [-r][-l][-d] -g group " 2724543Smarks "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" 2734543Smarks " <filesystem|volume>\n" 2744543Smarks "\tunallow [-r][-l][-d] -e " 2754543Smarks "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" 2764543Smarks " <filesystem|volume>\n" 2774543Smarks "\tunallow [-r] -c " 2784543Smarks "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" 2794543Smarks " <filesystem|volume>\n" 2804543Smarks "\tunallow [-r] -s @setname " 2814543Smarks "[<perm>|@<setname>[,<perm>|@<setname>...]]\n\t" 2824543Smarks " <filesystem|volume> \n\t")); 2831387Seschrock } 2841387Seschrock 2851387Seschrock abort(); 2861387Seschrock /* NOTREACHED */ 2871387Seschrock } 2881387Seschrock 289789Sahrens /* 290789Sahrens * Utility function to guarantee malloc() success. 291789Sahrens */ 292789Sahrens void * 293789Sahrens safe_malloc(size_t size) 294789Sahrens { 295789Sahrens void *data; 296789Sahrens 297789Sahrens if ((data = calloc(1, size)) == NULL) { 298789Sahrens (void) fprintf(stderr, "internal error: out of memory\n"); 299789Sahrens exit(1); 300789Sahrens } 301789Sahrens 302789Sahrens return (data); 303789Sahrens } 304789Sahrens 305789Sahrens /* 3063654Sgw25295 * Callback routinue that will print out information for each of the 3073654Sgw25295 * the properties. 3083654Sgw25295 */ 3093654Sgw25295 static zfs_prop_t 3103654Sgw25295 usage_prop_cb(zfs_prop_t prop, void *cb) 3113654Sgw25295 { 3123654Sgw25295 FILE *fp = cb; 3133654Sgw25295 3143654Sgw25295 (void) fprintf(fp, "\t%-13s ", zfs_prop_to_name(prop)); 3153654Sgw25295 3163654Sgw25295 if (zfs_prop_readonly(prop)) 3173654Sgw25295 (void) fprintf(fp, " NO "); 3183654Sgw25295 else 3193654Sgw25295 (void) fprintf(fp, " YES "); 3203654Sgw25295 3213654Sgw25295 if (zfs_prop_inheritable(prop)) 3223654Sgw25295 (void) fprintf(fp, " YES "); 3233654Sgw25295 else 3243654Sgw25295 (void) fprintf(fp, " NO "); 3253654Sgw25295 3263654Sgw25295 if (zfs_prop_values(prop) == NULL) 3273654Sgw25295 (void) fprintf(fp, "-\n"); 3283654Sgw25295 else 3293654Sgw25295 (void) fprintf(fp, "%s\n", zfs_prop_values(prop)); 3303654Sgw25295 3313654Sgw25295 return (ZFS_PROP_CONT); 3323654Sgw25295 } 3333654Sgw25295 3343654Sgw25295 /* 335789Sahrens * Display usage message. If we're inside a command, display only the usage for 336789Sahrens * that command. Otherwise, iterate over the entire command table and display 337789Sahrens * a complete usage message. 338789Sahrens */ 339789Sahrens static void 3402082Seschrock usage(boolean_t requested) 341789Sahrens { 342789Sahrens int i; 3432082Seschrock boolean_t show_properties = B_FALSE; 344789Sahrens FILE *fp = requested ? stdout : stderr; 345789Sahrens 346789Sahrens if (current_command == NULL) { 347789Sahrens 348789Sahrens (void) fprintf(fp, gettext("usage: zfs command args ...\n")); 349789Sahrens (void) fprintf(fp, 350789Sahrens gettext("where 'command' is one of the following:\n\n")); 351789Sahrens 352789Sahrens for (i = 0; i < NCOMMAND; i++) { 353789Sahrens if (command_table[i].name == NULL) 354789Sahrens (void) fprintf(fp, "\n"); 355789Sahrens else 356789Sahrens (void) fprintf(fp, "%s", 3571387Seschrock get_usage(command_table[i].usage)); 358789Sahrens } 359789Sahrens 360789Sahrens (void) fprintf(fp, gettext("\nEach dataset is of the form: " 361789Sahrens "pool/[dataset/]*dataset[@name]\n")); 362789Sahrens } else { 363789Sahrens (void) fprintf(fp, gettext("usage:\n")); 3641387Seschrock (void) fprintf(fp, "%s", get_usage(current_command->usage)); 365789Sahrens } 366789Sahrens 3672190Sdarrenm if (current_command != NULL && 3682190Sdarrenm (strcmp(current_command->name, "set") == 0 || 369789Sahrens strcmp(current_command->name, "get") == 0 || 370789Sahrens strcmp(current_command->name, "inherit") == 0 || 3712190Sdarrenm strcmp(current_command->name, "list") == 0)) 3722082Seschrock show_properties = B_TRUE; 373789Sahrens 374789Sahrens if (show_properties) { 375789Sahrens 376789Sahrens (void) fprintf(fp, 377789Sahrens gettext("\nThe following properties are supported:\n")); 378789Sahrens 379789Sahrens (void) fprintf(fp, "\n\t%-13s %s %s %s\n\n", 380789Sahrens "PROPERTY", "EDIT", "INHERIT", "VALUES"); 381789Sahrens 3823654Sgw25295 /* Iterate over all properties */ 3834597Stimf (void) zfs_prop_iter_ordered(usage_prop_cb, fp, B_FALSE); 3843654Sgw25295 385789Sahrens (void) fprintf(fp, gettext("\nSizes are specified in bytes " 386789Sahrens "with standard units such as K, M, G, etc.\n")); 3872676Seschrock (void) fprintf(fp, gettext("\n\nUser-defined properties can " 3882676Seschrock "be specified by using a name containing a colon (:).\n")); 3892190Sdarrenm } else { 3902190Sdarrenm /* 3912190Sdarrenm * TRANSLATION NOTE: 3922190Sdarrenm * "zfs set|get" must not be localised this is the 3932190Sdarrenm * command name and arguments. 3942190Sdarrenm */ 3952190Sdarrenm (void) fprintf(fp, 3962190Sdarrenm gettext("\nFor the property list, run: zfs set|get\n")); 397789Sahrens } 398789Sahrens 3992676Seschrock /* 4002676Seschrock * See comments at end of main(). 4012676Seschrock */ 4022676Seschrock if (getenv("ZFS_ABORT") != NULL) { 4032676Seschrock (void) printf("dumping core by request\n"); 4042676Seschrock abort(); 4052676Seschrock } 4062676Seschrock 407789Sahrens exit(requested ? 0 : 2); 408789Sahrens } 409789Sahrens 410789Sahrens /* 4114490Svb160487 * zfs clone [-p] <snap> <fs | vol> 412789Sahrens * 413789Sahrens * Given an existing dataset, create a writable copy whose initial contents 414789Sahrens * are the same as the source. The newly created dataset maintains a 415789Sahrens * dependency on the original; the original cannot be destroyed so long as 416789Sahrens * the clone exists. 4174490Svb160487 * 4184490Svb160487 * The '-p' flag creates all the non-existing ancestors of the target first. 419789Sahrens */ 420789Sahrens static int 421789Sahrens zfs_do_clone(int argc, char **argv) 422789Sahrens { 423789Sahrens zfs_handle_t *zhp; 4244490Svb160487 boolean_t parents = B_FALSE; 425789Sahrens int ret; 4264490Svb160487 int c; 427789Sahrens 428789Sahrens /* check options */ 4294490Svb160487 while ((c = getopt(argc, argv, "p")) != -1) { 4304490Svb160487 switch (c) { 4314490Svb160487 case 'p': 4324490Svb160487 parents = B_TRUE; 4334490Svb160487 break; 4344490Svb160487 case '?': 4354490Svb160487 (void) fprintf(stderr, gettext("invalid option '%c'\n"), 4364490Svb160487 optopt); 4374490Svb160487 usage(B_FALSE); 4384490Svb160487 } 439789Sahrens } 440789Sahrens 4414490Svb160487 argc -= optind; 4424490Svb160487 argv += optind; 4434490Svb160487 444789Sahrens /* check number of arguments */ 4454490Svb160487 if (argc < 1) { 446789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 447789Sahrens "argument\n")); 4482082Seschrock usage(B_FALSE); 449789Sahrens } 4504490Svb160487 if (argc < 2) { 451789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 452789Sahrens "argument\n")); 4532082Seschrock usage(B_FALSE); 454789Sahrens } 4554490Svb160487 if (argc > 2) { 456789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 4572082Seschrock usage(B_FALSE); 458789Sahrens } 459789Sahrens 460789Sahrens /* open the source dataset */ 4614490Svb160487 if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 462789Sahrens return (1); 463789Sahrens 4644490Svb160487 if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM | 4654490Svb160487 ZFS_TYPE_VOLUME)) { 4664490Svb160487 /* 4674490Svb160487 * Now create the ancestors of the target dataset. If the 4684490Svb160487 * target already exists and '-p' option was used we should not 4694490Svb160487 * complain. 4704490Svb160487 */ 4714490Svb160487 if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | 4724490Svb160487 ZFS_TYPE_VOLUME)) 4734490Svb160487 return (0); 4744490Svb160487 if (zfs_create_ancestors(g_zfs, argv[1]) != 0) 4754490Svb160487 return (1); 4764490Svb160487 } 4774490Svb160487 478789Sahrens /* pass to libzfs */ 4794490Svb160487 ret = zfs_clone(zhp, argv[1], NULL); 480789Sahrens 481789Sahrens /* create the mountpoint if necessary */ 482789Sahrens if (ret == 0) { 4834490Svb160487 zfs_handle_t *clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_ANY); 484789Sahrens if (clone != NULL) { 485789Sahrens if ((ret = zfs_mount(clone, NULL, 0)) == 0) 486789Sahrens ret = zfs_share(clone); 487789Sahrens zfs_close(clone); 488789Sahrens } 489789Sahrens } 490789Sahrens 491789Sahrens zfs_close(zhp); 492789Sahrens 493789Sahrens return (ret == 0 ? 0 : 1); 494789Sahrens } 495789Sahrens 496789Sahrens /* 4974490Svb160487 * zfs create [-p] [-o prop=value] ... fs 4984490Svb160487 * zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size 499789Sahrens * 500789Sahrens * Create a new dataset. This command can be used to create filesystems 501789Sahrens * and volumes. Snapshot creation is handled by 'zfs snapshot'. 502789Sahrens * For volumes, the user must specify a size to be used. 503789Sahrens * 504789Sahrens * The '-s' flag applies only to volumes, and indicates that we should not try 505789Sahrens * to set the reservation for this volume. By default we set a reservation 506789Sahrens * equal to the size for any volume. 5074490Svb160487 * 5084490Svb160487 * The '-p' flag creates all the non-existing ancestors of the target first. 509789Sahrens */ 510789Sahrens static int 511789Sahrens zfs_do_create(int argc, char **argv) 512789Sahrens { 513789Sahrens zfs_type_t type = ZFS_TYPE_FILESYSTEM; 5142676Seschrock zfs_handle_t *zhp = NULL; 5152676Seschrock uint64_t volsize; 516789Sahrens int c; 5172082Seschrock boolean_t noreserve = B_FALSE; 5184490Svb160487 boolean_t parents = B_FALSE; 5192676Seschrock int ret = 1; 5202676Seschrock nvlist_t *props = NULL; 5212676Seschrock uint64_t intval; 5222676Seschrock char *propname; 5232926Sek110237 char *propval = NULL; 5242926Sek110237 char *strval; 5252676Seschrock 5262676Seschrock if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) { 5272676Seschrock (void) fprintf(stderr, gettext("internal error: " 5282676Seschrock "out of memory\n")); 5292676Seschrock return (1); 5302676Seschrock } 531789Sahrens 532789Sahrens /* check options */ 5334490Svb160487 while ((c = getopt(argc, argv, ":V:b:so:p")) != -1) { 534789Sahrens switch (c) { 535789Sahrens case 'V': 536789Sahrens type = ZFS_TYPE_VOLUME; 5372676Seschrock if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 5382676Seschrock (void) fprintf(stderr, gettext("bad volume " 5392676Seschrock "size '%s': %s\n"), optarg, 5402676Seschrock libzfs_error_description(g_zfs)); 5412676Seschrock goto error; 5422676Seschrock } 5432676Seschrock 5442676Seschrock if (nvlist_add_uint64(props, 5452676Seschrock zfs_prop_to_name(ZFS_PROP_VOLSIZE), 5462676Seschrock intval) != 0) { 5472676Seschrock (void) fprintf(stderr, gettext("internal " 5482676Seschrock "error: out of memory\n")); 5492676Seschrock goto error; 5502676Seschrock } 5512676Seschrock volsize = intval; 552789Sahrens break; 5534490Svb160487 case 'p': 5544490Svb160487 parents = B_TRUE; 5554490Svb160487 break; 556789Sahrens case 'b': 5572676Seschrock if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 5582676Seschrock (void) fprintf(stderr, gettext("bad volume " 5592676Seschrock "block size '%s': %s\n"), optarg, 5602676Seschrock libzfs_error_description(g_zfs)); 5612676Seschrock goto error; 5622676Seschrock } 5632676Seschrock 5642676Seschrock if (nvlist_add_uint64(props, 5652676Seschrock zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 5662676Seschrock intval) != 0) { 5672676Seschrock (void) fprintf(stderr, gettext("internal " 5682676Seschrock "error: out of memory\n")); 5692676Seschrock goto error; 5702676Seschrock } 5712676Seschrock break; 5722676Seschrock case 'o': 5732676Seschrock propname = optarg; 5742676Seschrock if ((propval = strchr(propname, '=')) == NULL) { 5752676Seschrock (void) fprintf(stderr, gettext("missing " 5762676Seschrock "'=' for -o option\n")); 5772676Seschrock goto error; 5782676Seschrock } 5792676Seschrock *propval = '\0'; 5802676Seschrock propval++; 5812676Seschrock if (nvlist_lookup_string(props, propname, 5822676Seschrock &strval) == 0) { 5832676Seschrock (void) fprintf(stderr, gettext("property '%s' " 5842676Seschrock "specified multiple times\n"), propname); 5852676Seschrock goto error; 5862676Seschrock } 5872676Seschrock if (nvlist_add_string(props, propname, propval) != 0) { 5882676Seschrock (void) fprintf(stderr, gettext("internal " 5892676Seschrock "error: out of memory\n")); 5902676Seschrock goto error; 5912676Seschrock } 592789Sahrens break; 593789Sahrens case 's': 5942082Seschrock noreserve = B_TRUE; 595789Sahrens break; 596789Sahrens case ':': 597789Sahrens (void) fprintf(stderr, gettext("missing size " 598789Sahrens "argument\n")); 5992676Seschrock goto badusage; 600789Sahrens break; 601789Sahrens case '?': 602789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 603789Sahrens optopt); 6042676Seschrock goto badusage; 605789Sahrens } 606789Sahrens } 607789Sahrens 608789Sahrens if (noreserve && type != ZFS_TYPE_VOLUME) { 609789Sahrens (void) fprintf(stderr, gettext("'-s' can only be used when " 610789Sahrens "creating a volume\n")); 6112676Seschrock goto badusage; 612789Sahrens } 613789Sahrens 614789Sahrens argc -= optind; 615789Sahrens argv += optind; 616789Sahrens 617789Sahrens /* check number of arguments */ 618789Sahrens if (argc == 0) { 619789Sahrens (void) fprintf(stderr, gettext("missing %s argument\n"), 620789Sahrens zfs_type_to_name(type)); 6212676Seschrock goto badusage; 622789Sahrens } 623789Sahrens if (argc > 1) { 624789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 6252676Seschrock goto badusage; 6262676Seschrock } 6272676Seschrock 6282676Seschrock if (type == ZFS_TYPE_VOLUME && !noreserve && 6292676Seschrock nvlist_lookup_string(props, zfs_prop_to_name(ZFS_PROP_RESERVATION), 6302676Seschrock &strval) != 0) { 6312676Seschrock if (nvlist_add_uint64(props, 6322676Seschrock zfs_prop_to_name(ZFS_PROP_RESERVATION), 6332676Seschrock volsize) != 0) { 6342676Seschrock (void) fprintf(stderr, gettext("internal " 6352676Seschrock "error: out of memory\n")); 6362676Seschrock nvlist_free(props); 6372676Seschrock return (1); 6382676Seschrock } 639789Sahrens } 640789Sahrens 6414490Svb160487 if (parents && zfs_name_valid(argv[0], type)) { 6424490Svb160487 /* 6434490Svb160487 * Now create the ancestors of target dataset. If the target 6444490Svb160487 * already exists and '-p' option was used we should not 6454490Svb160487 * complain. 6464490Svb160487 */ 6474490Svb160487 if (zfs_dataset_exists(g_zfs, argv[0], type)) { 6484490Svb160487 ret = 0; 6494490Svb160487 goto error; 6504490Svb160487 } 6514490Svb160487 if (zfs_create_ancestors(g_zfs, argv[0]) != 0) 6524490Svb160487 goto error; 6534490Svb160487 } 6544490Svb160487 655789Sahrens /* pass to libzfs */ 6562676Seschrock if (zfs_create(g_zfs, argv[0], type, props) != 0) 6572676Seschrock goto error; 658789Sahrens 6592082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 6602676Seschrock goto error; 661789Sahrens 662789Sahrens /* 663789Sahrens * Mount and/or share the new filesystem as appropriate. We provide a 664789Sahrens * verbose error message to let the user know that their filesystem was 665789Sahrens * in fact created, even if we failed to mount or share it. 666789Sahrens */ 667789Sahrens if (zfs_mount(zhp, NULL, 0) != 0) { 668789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 669789Sahrens "created, but not mounted\n")); 670789Sahrens ret = 1; 671789Sahrens } else if (zfs_share(zhp) != 0) { 672789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 673789Sahrens "created, but not shared\n")); 674789Sahrens ret = 1; 675789Sahrens } else { 676789Sahrens ret = 0; 677789Sahrens } 678789Sahrens 6792676Seschrock error: 6802676Seschrock if (zhp) 6812676Seschrock zfs_close(zhp); 6822676Seschrock nvlist_free(props); 683789Sahrens return (ret); 6842676Seschrock badusage: 6852676Seschrock nvlist_free(props); 6862676Seschrock usage(B_FALSE); 6872676Seschrock return (2); 688789Sahrens } 689789Sahrens 690789Sahrens /* 691789Sahrens * zfs destroy [-rf] <fs, snap, vol> 692789Sahrens * 693789Sahrens * -r Recursively destroy all children 694789Sahrens * -R Recursively destroy all dependents, including clones 695789Sahrens * -f Force unmounting of any dependents 696789Sahrens * 697789Sahrens * Destroys the given dataset. By default, it will unmount any filesystems, 698789Sahrens * and refuse to destroy a dataset that has any dependents. A dependent can 699789Sahrens * either be a child, or a clone of a child. 700789Sahrens */ 701789Sahrens typedef struct destroy_cbdata { 7022082Seschrock boolean_t cb_first; 703789Sahrens int cb_force; 704789Sahrens int cb_recurse; 705789Sahrens int cb_error; 706789Sahrens int cb_needforce; 707789Sahrens int cb_doclones; 7083265Sahrens boolean_t cb_closezhp; 709789Sahrens zfs_handle_t *cb_target; 7102199Sahrens char *cb_snapname; 711789Sahrens } destroy_cbdata_t; 712789Sahrens 713789Sahrens /* 714789Sahrens * Check for any dependents based on the '-r' or '-R' flags. 715789Sahrens */ 716789Sahrens static int 717789Sahrens destroy_check_dependent(zfs_handle_t *zhp, void *data) 718789Sahrens { 719789Sahrens destroy_cbdata_t *cbp = data; 720789Sahrens const char *tname = zfs_get_name(cbp->cb_target); 721789Sahrens const char *name = zfs_get_name(zhp); 722789Sahrens 723789Sahrens if (strncmp(tname, name, strlen(tname)) == 0 && 724789Sahrens (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { 725789Sahrens /* 726789Sahrens * This is a direct descendant, not a clone somewhere else in 727789Sahrens * the hierarchy. 728789Sahrens */ 729789Sahrens if (cbp->cb_recurse) 730789Sahrens goto out; 731789Sahrens 732789Sahrens if (cbp->cb_first) { 733789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 734789Sahrens "%s has children\n"), 735789Sahrens zfs_get_name(cbp->cb_target), 736789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 737789Sahrens (void) fprintf(stderr, gettext("use '-r' to destroy " 738789Sahrens "the following datasets:\n")); 7392082Seschrock cbp->cb_first = B_FALSE; 740789Sahrens cbp->cb_error = 1; 741789Sahrens } 742789Sahrens 743789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 744789Sahrens } else { 745789Sahrens /* 746789Sahrens * This is a clone. We only want to report this if the '-r' 747789Sahrens * wasn't specified, or the target is a snapshot. 748789Sahrens */ 749789Sahrens if (!cbp->cb_recurse && 750789Sahrens zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) 751789Sahrens goto out; 752789Sahrens 753789Sahrens if (cbp->cb_first) { 754789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 755789Sahrens "%s has dependent clones\n"), 756789Sahrens zfs_get_name(cbp->cb_target), 757789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 758789Sahrens (void) fprintf(stderr, gettext("use '-R' to destroy " 759789Sahrens "the following datasets:\n")); 7602082Seschrock cbp->cb_first = B_FALSE; 761789Sahrens cbp->cb_error = 1; 762789Sahrens } 763789Sahrens 764789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 765789Sahrens } 766789Sahrens 767789Sahrens out: 768789Sahrens zfs_close(zhp); 769789Sahrens return (0); 770789Sahrens } 771789Sahrens 772789Sahrens static int 773789Sahrens destroy_callback(zfs_handle_t *zhp, void *data) 774789Sahrens { 775789Sahrens destroy_cbdata_t *cbp = data; 776789Sahrens 777789Sahrens /* 778789Sahrens * Ignore pools (which we've already flagged as an error before getting 779789Sahrens * here. 780789Sahrens */ 781789Sahrens if (strchr(zfs_get_name(zhp), '/') == NULL && 782789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 783789Sahrens zfs_close(zhp); 784789Sahrens return (0); 785789Sahrens } 786789Sahrens 787789Sahrens /* 788789Sahrens * Bail out on the first error. 789789Sahrens */ 790789Sahrens if (zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0 || 791789Sahrens zfs_destroy(zhp) != 0) { 792789Sahrens zfs_close(zhp); 793789Sahrens return (-1); 794789Sahrens } 795789Sahrens 796789Sahrens zfs_close(zhp); 797789Sahrens return (0); 798789Sahrens } 799789Sahrens 8002199Sahrens static int 8012199Sahrens destroy_snap_clones(zfs_handle_t *zhp, void *arg) 8022199Sahrens { 8032199Sahrens destroy_cbdata_t *cbp = arg; 8042199Sahrens char thissnap[MAXPATHLEN]; 8052199Sahrens zfs_handle_t *szhp; 8063265Sahrens boolean_t closezhp = cbp->cb_closezhp; 8073265Sahrens int rv; 8082199Sahrens 8092199Sahrens (void) snprintf(thissnap, sizeof (thissnap), 8102199Sahrens "%s@%s", zfs_get_name(zhp), cbp->cb_snapname); 8112199Sahrens 8122199Sahrens libzfs_print_on_error(g_zfs, B_FALSE); 8132199Sahrens szhp = zfs_open(g_zfs, thissnap, ZFS_TYPE_SNAPSHOT); 8142199Sahrens libzfs_print_on_error(g_zfs, B_TRUE); 8152199Sahrens if (szhp) { 8162199Sahrens /* 8172199Sahrens * Destroy any clones of this snapshot 8182199Sahrens */ 8192474Seschrock if (zfs_iter_dependents(szhp, B_FALSE, destroy_callback, 8202474Seschrock cbp) != 0) { 8212474Seschrock zfs_close(szhp); 8223265Sahrens if (closezhp) 8233265Sahrens zfs_close(zhp); 8242474Seschrock return (-1); 8252474Seschrock } 8262199Sahrens zfs_close(szhp); 8272199Sahrens } 8282199Sahrens 8293265Sahrens cbp->cb_closezhp = B_TRUE; 8303265Sahrens rv = zfs_iter_filesystems(zhp, destroy_snap_clones, arg); 8313265Sahrens if (closezhp) 8323265Sahrens zfs_close(zhp); 8333265Sahrens return (rv); 8342199Sahrens } 835789Sahrens 836789Sahrens static int 837789Sahrens zfs_do_destroy(int argc, char **argv) 838789Sahrens { 839789Sahrens destroy_cbdata_t cb = { 0 }; 840789Sahrens int c; 841789Sahrens zfs_handle_t *zhp; 8422199Sahrens char *cp; 843789Sahrens 844789Sahrens /* check options */ 845789Sahrens while ((c = getopt(argc, argv, "frR")) != -1) { 846789Sahrens switch (c) { 847789Sahrens case 'f': 848789Sahrens cb.cb_force = 1; 849789Sahrens break; 850789Sahrens case 'r': 851789Sahrens cb.cb_recurse = 1; 852789Sahrens break; 853789Sahrens case 'R': 854789Sahrens cb.cb_recurse = 1; 855789Sahrens cb.cb_doclones = 1; 856789Sahrens break; 857789Sahrens case '?': 858789Sahrens default: 859789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 860789Sahrens optopt); 8612082Seschrock usage(B_FALSE); 862789Sahrens } 863789Sahrens } 864789Sahrens 865789Sahrens argc -= optind; 866789Sahrens argv += optind; 867789Sahrens 868789Sahrens /* check number of arguments */ 869789Sahrens if (argc == 0) { 870789Sahrens (void) fprintf(stderr, gettext("missing path argument\n")); 8712082Seschrock usage(B_FALSE); 872789Sahrens } 873789Sahrens if (argc > 1) { 874789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 8752082Seschrock usage(B_FALSE); 876789Sahrens } 877789Sahrens 8782199Sahrens /* 8792199Sahrens * If we are doing recursive destroy of a snapshot, then the 8802199Sahrens * named snapshot may not exist. Go straight to libzfs. 8812199Sahrens */ 8822199Sahrens if (cb.cb_recurse && (cp = strchr(argv[0], '@'))) { 8832199Sahrens int ret; 8842199Sahrens 8852199Sahrens *cp = '\0'; 8862199Sahrens if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 8872199Sahrens return (1); 8882199Sahrens *cp = '@'; 8892199Sahrens cp++; 8902199Sahrens 8912199Sahrens if (cb.cb_doclones) { 8922199Sahrens cb.cb_snapname = cp; 8932474Seschrock if (destroy_snap_clones(zhp, &cb) != 0) { 8942474Seschrock zfs_close(zhp); 8952474Seschrock return (1); 8962474Seschrock } 8972199Sahrens } 8982199Sahrens 8992199Sahrens ret = zfs_destroy_snaps(zhp, cp); 9002199Sahrens zfs_close(zhp); 9012199Sahrens if (ret) { 9022199Sahrens (void) fprintf(stderr, 9032199Sahrens gettext("no snapshots destroyed\n")); 9042199Sahrens } 9052199Sahrens return (ret != 0); 9062199Sahrens } 9072199Sahrens 9082199Sahrens 909789Sahrens /* Open the given dataset */ 9102082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 911789Sahrens return (1); 912789Sahrens 913789Sahrens cb.cb_target = zhp; 914789Sahrens 915789Sahrens /* 916789Sahrens * Perform an explicit check for pools before going any further. 917789Sahrens */ 918789Sahrens if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 919789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 920789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 921789Sahrens "operation does not apply to pools\n"), 922789Sahrens zfs_get_name(zhp)); 923789Sahrens (void) fprintf(stderr, gettext("use 'zfs destroy -r " 924789Sahrens "%s' to destroy all datasets in the pool\n"), 925789Sahrens zfs_get_name(zhp)); 926789Sahrens (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 927789Sahrens "to destroy the pool itself\n"), zfs_get_name(zhp)); 928789Sahrens zfs_close(zhp); 929789Sahrens return (1); 930789Sahrens } 931789Sahrens 932789Sahrens /* 933789Sahrens * Check for any dependents and/or clones. 934789Sahrens */ 9352082Seschrock cb.cb_first = B_TRUE; 9362474Seschrock if (!cb.cb_doclones && 9372474Seschrock zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent, 9382474Seschrock &cb) != 0) { 9392474Seschrock zfs_close(zhp); 9402474Seschrock return (1); 9412474Seschrock } 9422474Seschrock 9432474Seschrock if (cb.cb_error || 9442474Seschrock zfs_iter_dependents(zhp, B_FALSE, destroy_callback, &cb) != 0) { 945789Sahrens zfs_close(zhp); 946789Sahrens return (1); 947789Sahrens } 948789Sahrens 949789Sahrens /* 9502474Seschrock * Do the real thing. The callback will close the handle regardless of 9512474Seschrock * whether it succeeds or not. 952789Sahrens */ 9534543Smarks 9542474Seschrock if (destroy_callback(zhp, &cb) != 0) 9552474Seschrock return (1); 9562474Seschrock 9572926Sek110237 9582474Seschrock return (0); 959789Sahrens } 960789Sahrens 961789Sahrens /* 962866Seschrock * zfs get [-rHp] [-o field[,field]...] [-s source[,source]...] 963866Seschrock * < all | property[,property]... > < fs | snap | vol > ... 964789Sahrens * 965789Sahrens * -r recurse over any child datasets 966789Sahrens * -H scripted mode. Headers are stripped, and fields are separated 967789Sahrens * by tabs instead of spaces. 968789Sahrens * -o Set of fields to display. One of "name,property,value,source". 969789Sahrens * Default is all four. 970789Sahrens * -s Set of sources to allow. One of 971789Sahrens * "local,default,inherited,temporary,none". Default is all 972789Sahrens * five. 973789Sahrens * -p Display values in parsable (literal) format. 974789Sahrens * 975789Sahrens * Prints properties for the given datasets. The user can control which 976789Sahrens * columns to display as well as which property types to allow. 977789Sahrens */ 978789Sahrens 979789Sahrens /* 980789Sahrens * Invoked to display the properties for a single dataset. 981789Sahrens */ 982789Sahrens static int 983789Sahrens get_callback(zfs_handle_t *zhp, void *data) 984789Sahrens { 985789Sahrens char buf[ZFS_MAXPROPLEN]; 986789Sahrens zfs_source_t sourcetype; 987789Sahrens char source[ZFS_MAXNAMELEN]; 9883912Slling libzfs_get_cbdata_t *cbp = data; 9892676Seschrock nvlist_t *userprop = zfs_get_user_props(zhp); 9902676Seschrock zfs_proplist_t *pl = cbp->cb_proplist; 9912676Seschrock nvlist_t *propval; 9922676Seschrock char *strval; 9932676Seschrock char *sourceval; 9942676Seschrock 9952676Seschrock for (; pl != NULL; pl = pl->pl_next) { 9962676Seschrock /* 9972676Seschrock * Skip the special fake placeholder. This will also skip over 9982676Seschrock * the name property when 'all' is specified. 9992676Seschrock */ 10002676Seschrock if (pl->pl_prop == ZFS_PROP_NAME && 10012676Seschrock pl == cbp->cb_proplist) 10022676Seschrock continue; 10032676Seschrock 10042676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 10052676Seschrock if (zfs_prop_get(zhp, pl->pl_prop, buf, 10062676Seschrock sizeof (buf), &sourcetype, source, 10072676Seschrock sizeof (source), 10082676Seschrock cbp->cb_literal) != 0) { 10092676Seschrock if (pl->pl_all) 10102676Seschrock continue; 10113912Slling if (!zfs_prop_valid_for_type(pl->pl_prop, 10123912Slling ZFS_TYPE_ANY)) { 10133912Slling (void) fprintf(stderr, 10143912Slling gettext("No such property '%s'\n"), 10153912Slling zfs_prop_to_name(pl->pl_prop)); 10163912Slling continue; 10173912Slling } 10182676Seschrock sourcetype = ZFS_SRC_NONE; 10192676Seschrock (void) strlcpy(buf, "-", sizeof (buf)); 10202676Seschrock } 10212676Seschrock 10223912Slling libzfs_print_one_property(zfs_get_name(zhp), cbp, 10232676Seschrock zfs_prop_to_name(pl->pl_prop), 10242676Seschrock buf, sourcetype, source); 10252676Seschrock } else { 10262676Seschrock if (nvlist_lookup_nvlist(userprop, 10272676Seschrock pl->pl_user_prop, &propval) != 0) { 10282676Seschrock if (pl->pl_all) 10292676Seschrock continue; 10302676Seschrock sourcetype = ZFS_SRC_NONE; 10312676Seschrock strval = "-"; 10322676Seschrock } else { 10332676Seschrock verify(nvlist_lookup_string(propval, 10342676Seschrock ZFS_PROP_VALUE, &strval) == 0); 10352676Seschrock verify(nvlist_lookup_string(propval, 10362676Seschrock ZFS_PROP_SOURCE, &sourceval) == 0); 10372676Seschrock 10382676Seschrock if (strcmp(sourceval, 10392676Seschrock zfs_get_name(zhp)) == 0) { 10402676Seschrock sourcetype = ZFS_SRC_LOCAL; 10412676Seschrock } else { 10422676Seschrock sourcetype = ZFS_SRC_INHERITED; 10432676Seschrock (void) strlcpy(source, 10442676Seschrock sourceval, sizeof (source)); 10452676Seschrock } 10462676Seschrock } 10472676Seschrock 10483912Slling libzfs_print_one_property(zfs_get_name(zhp), cbp, 10492676Seschrock pl->pl_user_prop, strval, sourcetype, 10502676Seschrock source); 1051866Seschrock } 1052789Sahrens } 1053789Sahrens 1054789Sahrens return (0); 1055789Sahrens } 1056789Sahrens 1057789Sahrens static int 1058789Sahrens zfs_do_get(int argc, char **argv) 1059789Sahrens { 10603912Slling libzfs_get_cbdata_t cb = { 0 }; 10612082Seschrock boolean_t recurse = B_FALSE; 10622676Seschrock int i, c; 10632676Seschrock char *value, *fields; 1064866Seschrock int ret; 10652676Seschrock zfs_proplist_t fake_name = { 0 }; 1066789Sahrens 1067789Sahrens /* 1068789Sahrens * Set up default columns and sources. 1069789Sahrens */ 1070789Sahrens cb.cb_sources = ZFS_SRC_ALL; 1071789Sahrens cb.cb_columns[0] = GET_COL_NAME; 1072789Sahrens cb.cb_columns[1] = GET_COL_PROPERTY; 1073789Sahrens cb.cb_columns[2] = GET_COL_VALUE; 1074789Sahrens cb.cb_columns[3] = GET_COL_SOURCE; 1075789Sahrens 1076789Sahrens /* check options */ 1077789Sahrens while ((c = getopt(argc, argv, ":o:s:rHp")) != -1) { 1078789Sahrens switch (c) { 1079789Sahrens case 'p': 10802082Seschrock cb.cb_literal = B_TRUE; 1081789Sahrens break; 1082789Sahrens case 'r': 10832082Seschrock recurse = B_TRUE; 1084789Sahrens break; 1085789Sahrens case 'H': 10862082Seschrock cb.cb_scripted = B_TRUE; 1087789Sahrens break; 1088789Sahrens case ':': 1089789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1090789Sahrens "'%c' option\n"), optopt); 10912082Seschrock usage(B_FALSE); 1092789Sahrens break; 1093789Sahrens case 'o': 1094789Sahrens /* 1095789Sahrens * Process the set of columns to display. We zero out 1096789Sahrens * the structure to give us a blank slate. 1097789Sahrens */ 1098789Sahrens bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 1099789Sahrens i = 0; 1100789Sahrens while (*optarg != '\0') { 1101789Sahrens static char *col_subopts[] = 1102789Sahrens { "name", "property", "value", "source", 1103789Sahrens NULL }; 1104789Sahrens 1105789Sahrens if (i == 4) { 1106789Sahrens (void) fprintf(stderr, gettext("too " 1107789Sahrens "many fields given to -o " 1108789Sahrens "option\n")); 11092082Seschrock usage(B_FALSE); 1110789Sahrens } 1111789Sahrens 1112789Sahrens switch (getsubopt(&optarg, col_subopts, 1113789Sahrens &value)) { 1114789Sahrens case 0: 1115789Sahrens cb.cb_columns[i++] = GET_COL_NAME; 1116789Sahrens break; 1117789Sahrens case 1: 1118789Sahrens cb.cb_columns[i++] = GET_COL_PROPERTY; 1119789Sahrens break; 1120789Sahrens case 2: 1121789Sahrens cb.cb_columns[i++] = GET_COL_VALUE; 1122789Sahrens break; 1123789Sahrens case 3: 1124789Sahrens cb.cb_columns[i++] = GET_COL_SOURCE; 1125789Sahrens break; 1126789Sahrens default: 1127789Sahrens (void) fprintf(stderr, 1128789Sahrens gettext("invalid column name " 1129789Sahrens "'%s'\n"), value); 11303912Slling usage(B_FALSE); 1131789Sahrens } 1132789Sahrens } 1133789Sahrens break; 1134789Sahrens 1135789Sahrens case 's': 1136789Sahrens cb.cb_sources = 0; 1137789Sahrens while (*optarg != '\0') { 1138789Sahrens static char *source_subopts[] = { 1139789Sahrens "local", "default", "inherited", 1140789Sahrens "temporary", "none", NULL }; 1141789Sahrens 1142789Sahrens switch (getsubopt(&optarg, source_subopts, 1143789Sahrens &value)) { 1144789Sahrens case 0: 1145789Sahrens cb.cb_sources |= ZFS_SRC_LOCAL; 1146789Sahrens break; 1147789Sahrens case 1: 1148789Sahrens cb.cb_sources |= ZFS_SRC_DEFAULT; 1149789Sahrens break; 1150789Sahrens case 2: 1151789Sahrens cb.cb_sources |= ZFS_SRC_INHERITED; 1152789Sahrens break; 1153789Sahrens case 3: 1154789Sahrens cb.cb_sources |= ZFS_SRC_TEMPORARY; 1155789Sahrens break; 1156789Sahrens case 4: 1157789Sahrens cb.cb_sources |= ZFS_SRC_NONE; 1158789Sahrens break; 1159789Sahrens default: 1160789Sahrens (void) fprintf(stderr, 1161789Sahrens gettext("invalid source " 1162789Sahrens "'%s'\n"), value); 11633912Slling usage(B_FALSE); 1164789Sahrens } 1165789Sahrens } 1166789Sahrens break; 1167789Sahrens 1168789Sahrens case '?': 1169789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1170789Sahrens optopt); 11712082Seschrock usage(B_FALSE); 1172789Sahrens } 1173789Sahrens } 1174789Sahrens 1175789Sahrens argc -= optind; 1176789Sahrens argv += optind; 1177789Sahrens 1178789Sahrens if (argc < 1) { 1179789Sahrens (void) fprintf(stderr, gettext("missing property " 1180789Sahrens "argument\n")); 11812082Seschrock usage(B_FALSE); 1182789Sahrens } 1183789Sahrens 1184789Sahrens fields = argv[0]; 1185789Sahrens 11862676Seschrock if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0) 11872082Seschrock usage(B_FALSE); 1188789Sahrens 1189789Sahrens argc--; 1190789Sahrens argv++; 1191789Sahrens 1192789Sahrens /* 11932676Seschrock * As part of zfs_expand_proplist(), we keep track of the maximum column 11942676Seschrock * width for each property. For the 'NAME' (and 'SOURCE') columns, we 11952676Seschrock * need to know the maximum name length. However, the user likely did 11962676Seschrock * not specify 'name' as one of the properties to fetch, so we need to 11972676Seschrock * make sure we always include at least this property for 11982676Seschrock * print_get_headers() to work properly. 1199789Sahrens */ 12002676Seschrock if (cb.cb_proplist != NULL) { 12012676Seschrock fake_name.pl_prop = ZFS_PROP_NAME; 12022676Seschrock fake_name.pl_width = strlen(gettext("NAME")); 12032676Seschrock fake_name.pl_next = cb.cb_proplist; 12042676Seschrock cb.cb_proplist = &fake_name; 1205789Sahrens } 1206789Sahrens 12072676Seschrock cb.cb_first = B_TRUE; 12082676Seschrock 1209789Sahrens /* run for each object */ 12102676Seschrock ret = zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, NULL, 12113635Sck153898 &cb.cb_proplist, get_callback, &cb, B_FALSE); 12122676Seschrock 12132676Seschrock if (cb.cb_proplist == &fake_name) 12142676Seschrock zfs_free_proplist(fake_name.pl_next); 12152676Seschrock else 12162676Seschrock zfs_free_proplist(cb.cb_proplist); 12172676Seschrock 12182676Seschrock return (ret); 1219789Sahrens } 1220789Sahrens 1221789Sahrens /* 1222789Sahrens * inherit [-r] <property> <fs|vol> ... 1223789Sahrens * 1224789Sahrens * -r Recurse over all children 1225789Sahrens * 1226789Sahrens * For each dataset specified on the command line, inherit the given property 1227789Sahrens * from its parent. Inheriting a property at the pool level will cause it to 1228789Sahrens * use the default value. The '-r' flag will recurse over all children, and is 1229789Sahrens * useful for setting a property on a hierarchy-wide basis, regardless of any 1230789Sahrens * local modifications for each dataset. 1231789Sahrens */ 12322926Sek110237 1233789Sahrens static int 1234789Sahrens inherit_callback(zfs_handle_t *zhp, void *data) 1235789Sahrens { 12364543Smarks char *propname = data; 12372926Sek110237 int ret; 12382926Sek110237 12394543Smarks ret = zfs_prop_inherit(zhp, propname); 12402926Sek110237 return (ret != 0); 1241789Sahrens } 1242789Sahrens 1243789Sahrens static int 1244789Sahrens zfs_do_inherit(int argc, char **argv) 1245789Sahrens { 12462082Seschrock boolean_t recurse = B_FALSE; 1247789Sahrens int c; 1248789Sahrens zfs_prop_t prop; 12494543Smarks char *propname; 12502926Sek110237 int ret; 1251789Sahrens 1252789Sahrens /* check options */ 1253789Sahrens while ((c = getopt(argc, argv, "r")) != -1) { 1254789Sahrens switch (c) { 1255789Sahrens case 'r': 12562082Seschrock recurse = B_TRUE; 1257789Sahrens break; 1258789Sahrens case '?': 1259789Sahrens default: 1260789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1261789Sahrens optopt); 12622082Seschrock usage(B_FALSE); 1263789Sahrens } 1264789Sahrens } 1265789Sahrens 1266789Sahrens argc -= optind; 1267789Sahrens argv += optind; 1268789Sahrens 1269789Sahrens /* check number of arguments */ 1270789Sahrens if (argc < 1) { 1271789Sahrens (void) fprintf(stderr, gettext("missing property argument\n")); 12722082Seschrock usage(B_FALSE); 1273789Sahrens } 1274789Sahrens if (argc < 2) { 1275789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 12762082Seschrock usage(B_FALSE); 1277789Sahrens } 1278789Sahrens 12794543Smarks propname = argv[0]; 12802676Seschrock argc--; 12812676Seschrock argv++; 12822676Seschrock 12834543Smarks if ((prop = zfs_name_to_prop(propname)) != ZFS_PROP_INVAL) { 12842676Seschrock if (zfs_prop_readonly(prop)) { 12852676Seschrock (void) fprintf(stderr, gettext( 12862676Seschrock "%s property is read-only\n"), 12874543Smarks propname); 12882676Seschrock return (1); 12892676Seschrock } 12902676Seschrock if (!zfs_prop_inheritable(prop)) { 12912676Seschrock (void) fprintf(stderr, gettext("'%s' property cannot " 12924543Smarks "be inherited\n"), propname); 12932676Seschrock if (prop == ZFS_PROP_QUOTA || 12942676Seschrock prop == ZFS_PROP_RESERVATION) 12952676Seschrock (void) fprintf(stderr, gettext("use 'zfs set " 12964543Smarks "%s=none' to clear\n"), propname); 12972676Seschrock return (1); 12982676Seschrock } 12994543Smarks } else if (!zfs_prop_user(propname)) { 13004543Smarks (void) fprintf(stderr, gettext("invalid property '%s'\n"), 13014543Smarks propname); 13022082Seschrock usage(B_FALSE); 1303789Sahrens } 13042676Seschrock 13052926Sek110237 ret = zfs_for_each(argc, argv, recurse, 13062676Seschrock ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL, 13074543Smarks inherit_callback, propname, B_FALSE); 13082926Sek110237 13092926Sek110237 return (ret); 1310789Sahrens } 1311789Sahrens 13124577Sahrens typedef struct upgrade_cbdata { 13134577Sahrens uint64_t cb_numupgraded; 13144577Sahrens uint64_t cb_numsamegraded; 13154577Sahrens uint64_t cb_numdowngradefailed; 13164577Sahrens uint64_t cb_version; 13174577Sahrens boolean_t cb_newer; 13184577Sahrens boolean_t cb_foundone; 13194577Sahrens char cb_lastfs[ZFS_MAXNAMELEN]; 13204577Sahrens } upgrade_cbdata_t; 13214577Sahrens 13224577Sahrens static int 13234577Sahrens same_pool(zfs_handle_t *zhp, const char *name) 13244577Sahrens { 13254577Sahrens int len1 = strcspn(name, "/@"); 13264577Sahrens const char *zhname = zfs_get_name(zhp); 13274577Sahrens int len2 = strcspn(zhname, "/@"); 13284577Sahrens 13294577Sahrens if (len1 != len2) 13304577Sahrens return (B_FALSE); 13314577Sahrens return (strncmp(name, zhname, len1) != 0); 13324577Sahrens } 13334577Sahrens 13344577Sahrens static int 13354577Sahrens upgrade_list_callback(zfs_handle_t *zhp, void *data) 13364577Sahrens { 13374577Sahrens upgrade_cbdata_t *cb = data; 13384577Sahrens int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 13394577Sahrens 13404577Sahrens /* list if it's old/new */ 13414577Sahrens if ((!cb->cb_newer && version < ZPL_VERSION) || 13424577Sahrens (cb->cb_newer && version > SPA_VERSION)) { 13434577Sahrens char *str; 13444577Sahrens if (cb->cb_newer) { 13454577Sahrens str = gettext("The following filesystems are " 13464577Sahrens "formatted using a newer software version and\n" 13474577Sahrens "cannot be accessed on the current system.\n\n"); 13484577Sahrens } else { 13494577Sahrens str = gettext("The following filesystems are " 13504577Sahrens "out of date, and can be upgraded. After being\n" 13514577Sahrens "upgraded, these filesystems (and any 'zfs send' " 13524577Sahrens "streams generated from\n" 13534577Sahrens "subsequent snapshots) will no longer be " 13544577Sahrens "accessible by older software versions.\n\n"); 13554577Sahrens } 13564577Sahrens 13574577Sahrens if (!cb->cb_foundone) { 13584577Sahrens (void) puts(str); 13594577Sahrens (void) printf(gettext("VER FILESYSTEM\n")); 13604577Sahrens (void) printf(gettext("--- ------------\n")); 13614577Sahrens cb->cb_foundone = B_TRUE; 13624577Sahrens } 13634577Sahrens 13644577Sahrens (void) printf("%2u %s\n", version, zfs_get_name(zhp)); 13654577Sahrens } 13664577Sahrens 13674577Sahrens return (0); 13684577Sahrens } 13694577Sahrens 13704577Sahrens static int 13714577Sahrens upgrade_set_callback(zfs_handle_t *zhp, void *data) 13724577Sahrens { 13734577Sahrens upgrade_cbdata_t *cb = data; 13744577Sahrens int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 13754577Sahrens 13764577Sahrens /* upgrade */ 13774577Sahrens if (version < cb->cb_version) { 13784577Sahrens char verstr[16]; 13794577Sahrens (void) snprintf(verstr, sizeof (verstr), "%u", cb->cb_version); 13804577Sahrens if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) { 13814577Sahrens /* 13824577Sahrens * If they did "zfs upgrade -a", then we could 13834577Sahrens * be doing ioctls to different pools. We need 13844577Sahrens * to log this history once to each pool. 13854577Sahrens */ 13864577Sahrens zpool_stage_history(g_zfs, first_argc, first_argv, 13874715Sek110237 B_TRUE); 13884577Sahrens } 13894577Sahrens if (zfs_prop_set(zhp, "version", verstr) == 0) 13904577Sahrens cb->cb_numupgraded++; 13914577Sahrens (void) strcpy(cb->cb_lastfs, zfs_get_name(zhp)); 13924577Sahrens } else if (version > cb->cb_version) { 13934577Sahrens /* can't downgrade */ 13944577Sahrens (void) printf(gettext("%s: can not be downgraded; " 13954577Sahrens "it is already at version %u\n"), 13964577Sahrens zfs_get_name(zhp), version); 13974577Sahrens cb->cb_numdowngradefailed++; 13984577Sahrens } else { 13994577Sahrens cb->cb_numsamegraded++; 14004577Sahrens } 14014577Sahrens return (0); 14024577Sahrens } 14034577Sahrens 14044577Sahrens /* 14054577Sahrens * zfs upgrade 14064577Sahrens * zfs upgrade -v 14074577Sahrens * zfs upgrade [-r] [-V <version>] <-a | filesystem> 14084577Sahrens */ 14094577Sahrens static int 14104577Sahrens zfs_do_upgrade(int argc, char **argv) 14114577Sahrens { 14124577Sahrens boolean_t recurse = B_FALSE; 14134577Sahrens boolean_t all = B_FALSE; 14144577Sahrens boolean_t showversions = B_FALSE; 14154577Sahrens int ret; 14164577Sahrens upgrade_cbdata_t cb = { 0 }; 14174577Sahrens char c; 14184577Sahrens 14194577Sahrens /* check options */ 14204577Sahrens while ((c = getopt(argc, argv, "rvV:a")) != -1) { 14214577Sahrens switch (c) { 14224577Sahrens case 'r': 14234577Sahrens recurse = B_TRUE; 14244577Sahrens break; 14254577Sahrens case 'v': 14264577Sahrens showversions = B_TRUE; 14274577Sahrens break; 14284577Sahrens case 'V': 14294577Sahrens if (zfs_prop_string_to_index(ZFS_PROP_VERSION, 14304577Sahrens optarg, &cb.cb_version) != 0) { 14314577Sahrens (void) fprintf(stderr, 14324577Sahrens gettext("invalid version %s\n"), optarg); 14334577Sahrens usage(B_FALSE); 14344577Sahrens } 14354577Sahrens break; 14364577Sahrens case 'a': 14374577Sahrens all = B_TRUE; 14384577Sahrens break; 14394577Sahrens case '?': 14404577Sahrens default: 14414577Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 14424577Sahrens optopt); 14434577Sahrens usage(B_FALSE); 14444577Sahrens } 14454577Sahrens } 14464577Sahrens 14474577Sahrens argc -= optind; 14484577Sahrens argv += optind; 14494577Sahrens 14504577Sahrens if ((!all && !argc) && (recurse | cb.cb_version)) 14514577Sahrens usage(B_FALSE); 14524577Sahrens if (showversions && (recurse || all || cb.cb_version || argc)) 14534577Sahrens usage(B_FALSE); 14544577Sahrens if ((all || argc) && (showversions)) 14554577Sahrens usage(B_FALSE); 14564577Sahrens if (all && argc) 14574577Sahrens usage(B_FALSE); 14584577Sahrens 14594577Sahrens if (showversions) { 14604577Sahrens /* Show info on available versions. */ 14614577Sahrens (void) printf(gettext("The following filesystem versions are " 14624577Sahrens "supported:\n\n")); 14634577Sahrens (void) printf(gettext("VER DESCRIPTION\n")); 14644577Sahrens (void) printf("--- -----------------------------------------" 14654577Sahrens "---------------\n"); 14664577Sahrens (void) printf(gettext(" 1 Initial ZFS filesystem version\n")); 14674577Sahrens (void) printf(gettext(" 2 Enhanced directory entries\n")); 14684577Sahrens (void) printf(gettext("\nFor more information on a particular " 14694577Sahrens "version, including supported releases, see:\n\n")); 14704577Sahrens (void) printf("http://www.opensolaris.org/os/community/zfs/" 14714577Sahrens "version/zpl/N\n\n"); 14724577Sahrens (void) printf(gettext("Where 'N' is the version number.\n")); 14734577Sahrens ret = 0; 14744577Sahrens } else if (argc || all) { 14754577Sahrens /* Upgrade filesystems */ 14764577Sahrens if (cb.cb_version == 0) 14774577Sahrens cb.cb_version = ZPL_VERSION; 14784577Sahrens ret = zfs_for_each(argc, argv, recurse, ZFS_TYPE_FILESYSTEM, 14794577Sahrens NULL, NULL, upgrade_set_callback, &cb, B_TRUE); 14804577Sahrens (void) printf(gettext("%llu filesystems upgraded\n"), 14814577Sahrens cb.cb_numupgraded); 14824577Sahrens if (cb.cb_numsamegraded) { 14834577Sahrens (void) printf(gettext("%llu filesystems already at " 14844577Sahrens "this version\n"), 14854577Sahrens cb.cb_numsamegraded); 14864577Sahrens } 14874577Sahrens if (cb.cb_numdowngradefailed != 0) 14884577Sahrens ret = 1; 14894577Sahrens } else { 14904577Sahrens /* List old-version filesytems */ 14914577Sahrens boolean_t found; 14924577Sahrens (void) printf(gettext("This system is currently running " 14934577Sahrens "ZFS filesystem version %llu.\n\n"), ZPL_VERSION); 14944577Sahrens 14954577Sahrens ret = zfs_for_each(0, NULL, B_TRUE, ZFS_TYPE_FILESYSTEM, 14964577Sahrens NULL, NULL, upgrade_list_callback, &cb, B_TRUE); 14974577Sahrens 14984577Sahrens found = cb.cb_foundone; 14994577Sahrens cb.cb_foundone = B_FALSE; 15004577Sahrens cb.cb_newer = B_TRUE; 15014577Sahrens 15024577Sahrens ret = zfs_for_each(0, NULL, B_TRUE, ZFS_TYPE_FILESYSTEM, 15034577Sahrens NULL, NULL, upgrade_list_callback, &cb, B_TRUE); 15044577Sahrens 15054577Sahrens if (!cb.cb_foundone && !found) { 15064577Sahrens (void) printf(gettext("All filesystems are " 15074577Sahrens "formatted with the current version.\n")); 15084577Sahrens } 15094577Sahrens } 15104577Sahrens 15114577Sahrens return (ret); 15124577Sahrens } 15134577Sahrens 1514789Sahrens /* 15152379Ssjelinek * list [-rH] [-o property[,property]...] [-t type[,type]...] 15162379Ssjelinek * [-s property [-s property]...] [-S property [-S property]...] 15172379Ssjelinek * <dataset> ... 1518789Sahrens * 1519789Sahrens * -r Recurse over all children 1520789Sahrens * -H Scripted mode; elide headers and separate colums by tabs 1521789Sahrens * -o Control which fields to display. 1522866Seschrock * -t Control which object types to display. 15232379Ssjelinek * -s Specify sort columns, descending order. 15242379Ssjelinek * -S Specify sort columns, ascending order. 1525789Sahrens * 1526789Sahrens * When given no arguments, lists all filesystems in the system. 1527789Sahrens * Otherwise, list the specified datasets, optionally recursing down them if 1528789Sahrens * '-r' is specified. 1529789Sahrens */ 1530789Sahrens typedef struct list_cbdata { 15312082Seschrock boolean_t cb_first; 15322082Seschrock boolean_t cb_scripted; 15332676Seschrock zfs_proplist_t *cb_proplist; 1534789Sahrens } list_cbdata_t; 1535789Sahrens 1536789Sahrens /* 1537789Sahrens * Given a list of columns to display, output appropriate headers for each one. 1538789Sahrens */ 1539789Sahrens static void 15402676Seschrock print_header(zfs_proplist_t *pl) 1541789Sahrens { 15422676Seschrock char headerbuf[ZFS_MAXPROPLEN]; 15432676Seschrock const char *header; 1544789Sahrens int i; 15452676Seschrock boolean_t first = B_TRUE; 15462676Seschrock boolean_t right_justify; 15472676Seschrock 15482676Seschrock for (; pl != NULL; pl = pl->pl_next) { 15492676Seschrock if (!first) { 1550789Sahrens (void) printf(" "); 15512676Seschrock } else { 15522676Seschrock first = B_FALSE; 15532676Seschrock } 15542676Seschrock 15552676Seschrock right_justify = B_FALSE; 15562676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 15572676Seschrock header = zfs_prop_column_name(pl->pl_prop); 15582676Seschrock right_justify = zfs_prop_align_right(pl->pl_prop); 15592676Seschrock } else { 15602676Seschrock for (i = 0; pl->pl_user_prop[i] != '\0'; i++) 15612676Seschrock headerbuf[i] = toupper(pl->pl_user_prop[i]); 15622676Seschrock headerbuf[i] = '\0'; 15632676Seschrock header = headerbuf; 15642676Seschrock } 15652676Seschrock 15662676Seschrock if (pl->pl_next == NULL && !right_justify) 15672676Seschrock (void) printf("%s", header); 15682676Seschrock else if (right_justify) 15692676Seschrock (void) printf("%*s", pl->pl_width, header); 15702676Seschrock else 15712676Seschrock (void) printf("%-*s", pl->pl_width, header); 1572789Sahrens } 1573789Sahrens 1574789Sahrens (void) printf("\n"); 1575789Sahrens } 1576789Sahrens 1577789Sahrens /* 1578789Sahrens * Given a dataset and a list of fields, print out all the properties according 1579789Sahrens * to the described layout. 1580789Sahrens */ 1581789Sahrens static void 15822676Seschrock print_dataset(zfs_handle_t *zhp, zfs_proplist_t *pl, int scripted) 1583789Sahrens { 15842676Seschrock boolean_t first = B_TRUE; 1585789Sahrens char property[ZFS_MAXPROPLEN]; 15862676Seschrock nvlist_t *userprops = zfs_get_user_props(zhp); 15872676Seschrock nvlist_t *propval; 15882676Seschrock char *propstr; 15892676Seschrock boolean_t right_justify; 15902676Seschrock int width; 15912676Seschrock 15922676Seschrock for (; pl != NULL; pl = pl->pl_next) { 15932676Seschrock if (!first) { 1594789Sahrens if (scripted) 1595789Sahrens (void) printf("\t"); 1596789Sahrens else 1597789Sahrens (void) printf(" "); 15982676Seschrock } else { 15992676Seschrock first = B_FALSE; 1600789Sahrens } 1601789Sahrens 16022676Seschrock right_justify = B_FALSE; 16032676Seschrock if (pl->pl_prop != ZFS_PROP_INVAL) { 16042676Seschrock if (zfs_prop_get(zhp, pl->pl_prop, property, 16052676Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) != 0) 16062676Seschrock propstr = "-"; 16072676Seschrock else 16082676Seschrock propstr = property; 16092676Seschrock 16102676Seschrock right_justify = zfs_prop_align_right(pl->pl_prop); 16112676Seschrock } else { 16122676Seschrock if (nvlist_lookup_nvlist(userprops, 16132676Seschrock pl->pl_user_prop, &propval) != 0) 16142676Seschrock propstr = "-"; 16152676Seschrock else 16162676Seschrock verify(nvlist_lookup_string(propval, 16172676Seschrock ZFS_PROP_VALUE, &propstr) == 0); 16182676Seschrock } 16192676Seschrock 16202676Seschrock width = pl->pl_width; 1621789Sahrens 1622866Seschrock /* 1623866Seschrock * If this is being called in scripted mode, or if this is the 1624866Seschrock * last column and it is left-justified, don't include a width 1625866Seschrock * format specifier. 1626866Seschrock */ 16272676Seschrock if (scripted || (pl->pl_next == NULL && !right_justify)) 16282676Seschrock (void) printf("%s", propstr); 16292676Seschrock else if (right_justify) 16302676Seschrock (void) printf("%*s", width, propstr); 16312676Seschrock else 16322676Seschrock (void) printf("%-*s", width, propstr); 1633789Sahrens } 1634789Sahrens 1635789Sahrens (void) printf("\n"); 1636789Sahrens } 1637789Sahrens 1638789Sahrens /* 1639789Sahrens * Generic callback function to list a dataset or snapshot. 1640789Sahrens */ 1641789Sahrens static int 1642789Sahrens list_callback(zfs_handle_t *zhp, void *data) 1643789Sahrens { 1644789Sahrens list_cbdata_t *cbp = data; 1645789Sahrens 1646789Sahrens if (cbp->cb_first) { 1647789Sahrens if (!cbp->cb_scripted) 16482676Seschrock print_header(cbp->cb_proplist); 16492082Seschrock cbp->cb_first = B_FALSE; 1650789Sahrens } 1651789Sahrens 16522676Seschrock print_dataset(zhp, cbp->cb_proplist, cbp->cb_scripted); 1653789Sahrens 1654789Sahrens return (0); 1655789Sahrens } 1656789Sahrens 1657789Sahrens static int 1658789Sahrens zfs_do_list(int argc, char **argv) 1659789Sahrens { 1660789Sahrens int c; 16612082Seschrock boolean_t recurse = B_FALSE; 16622082Seschrock boolean_t scripted = B_FALSE; 1663789Sahrens static char default_fields[] = 1664789Sahrens "name,used,available,referenced,mountpoint"; 1665789Sahrens int types = ZFS_TYPE_ANY; 1666789Sahrens char *fields = NULL; 1667789Sahrens char *basic_fields = default_fields; 1668789Sahrens list_cbdata_t cb = { 0 }; 1669789Sahrens char *value; 1670789Sahrens int ret; 1671789Sahrens char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL }; 16722379Ssjelinek zfs_sort_column_t *sortcol = NULL; 1673789Sahrens 1674789Sahrens /* check options */ 16752379Ssjelinek while ((c = getopt(argc, argv, ":o:rt:Hs:S:")) != -1) { 1676789Sahrens switch (c) { 1677789Sahrens case 'o': 1678789Sahrens fields = optarg; 1679789Sahrens break; 1680789Sahrens case 'r': 16812082Seschrock recurse = B_TRUE; 1682789Sahrens break; 1683789Sahrens case 'H': 16842082Seschrock scripted = B_TRUE; 1685789Sahrens break; 16862379Ssjelinek case 's': 16872676Seschrock if (zfs_add_sort_column(&sortcol, optarg, 16882676Seschrock B_FALSE) != 0) { 16892379Ssjelinek (void) fprintf(stderr, 16902379Ssjelinek gettext("invalid property '%s'\n"), optarg); 16912379Ssjelinek usage(B_FALSE); 16922379Ssjelinek } 16932379Ssjelinek break; 16942379Ssjelinek case 'S': 16952676Seschrock if (zfs_add_sort_column(&sortcol, optarg, 16962676Seschrock B_TRUE) != 0) { 16972379Ssjelinek (void) fprintf(stderr, 16982379Ssjelinek gettext("invalid property '%s'\n"), optarg); 16992379Ssjelinek usage(B_FALSE); 17002379Ssjelinek } 17012379Ssjelinek break; 1702789Sahrens case 't': 1703789Sahrens types = 0; 1704789Sahrens while (*optarg != '\0') { 1705789Sahrens switch (getsubopt(&optarg, type_subopts, 1706789Sahrens &value)) { 1707789Sahrens case 0: 1708789Sahrens types |= ZFS_TYPE_FILESYSTEM; 1709789Sahrens break; 1710789Sahrens case 1: 1711789Sahrens types |= ZFS_TYPE_VOLUME; 1712789Sahrens break; 1713789Sahrens case 2: 1714789Sahrens types |= ZFS_TYPE_SNAPSHOT; 1715789Sahrens break; 1716789Sahrens default: 1717789Sahrens (void) fprintf(stderr, 1718789Sahrens gettext("invalid type '%s'\n"), 1719789Sahrens value); 17202082Seschrock usage(B_FALSE); 1721789Sahrens } 1722789Sahrens } 1723789Sahrens break; 1724789Sahrens case ':': 1725789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1726789Sahrens "'%c' option\n"), optopt); 17272082Seschrock usage(B_FALSE); 1728789Sahrens break; 1729789Sahrens case '?': 1730789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1731789Sahrens optopt); 17322082Seschrock usage(B_FALSE); 1733789Sahrens } 1734789Sahrens } 1735789Sahrens 1736789Sahrens argc -= optind; 1737789Sahrens argv += optind; 1738789Sahrens 1739789Sahrens if (fields == NULL) 1740789Sahrens fields = basic_fields; 1741789Sahrens 1742866Seschrock /* 1743866Seschrock * If the user specifies '-o all', the zfs_get_proplist() doesn't 1744866Seschrock * normally include the name of the dataset. For 'zfs list', we always 1745866Seschrock * want this property to be first. 1746866Seschrock */ 17472676Seschrock if (zfs_get_proplist(g_zfs, fields, &cb.cb_proplist) != 0) 17482082Seschrock usage(B_FALSE); 17492676Seschrock 1750789Sahrens cb.cb_scripted = scripted; 17512082Seschrock cb.cb_first = B_TRUE; 1752789Sahrens 17532676Seschrock ret = zfs_for_each(argc, argv, recurse, types, sortcol, &cb.cb_proplist, 17543635Sck153898 list_callback, &cb, B_TRUE); 17552379Ssjelinek 17562676Seschrock zfs_free_proplist(cb.cb_proplist); 17572379Ssjelinek zfs_free_sort_columns(sortcol); 1758789Sahrens 17594221Smmusante if (ret == 0 && cb.cb_first && !cb.cb_scripted) 1760789Sahrens (void) printf(gettext("no datasets available\n")); 1761789Sahrens 1762789Sahrens return (ret); 1763789Sahrens } 1764789Sahrens 1765789Sahrens /* 17664490Svb160487 * zfs rename <fs | snap | vol> <fs | snap | vol> 17674490Svb160487 * zfs rename -p <fs | vol> <fs | vol> 17684490Svb160487 * zfs rename -r <snap> <snap> 1769789Sahrens * 1770789Sahrens * Renames the given dataset to another of the same type. 17714490Svb160487 * 17724490Svb160487 * The '-p' flag creates all the non-existing ancestors of the target first. 1773789Sahrens */ 1774789Sahrens /* ARGSUSED */ 1775789Sahrens static int 1776789Sahrens zfs_do_rename(int argc, char **argv) 1777789Sahrens { 1778789Sahrens zfs_handle_t *zhp; 17794007Smmusante int c; 17802082Seschrock int ret; 17814490Svb160487 boolean_t recurse = B_FALSE; 17824490Svb160487 boolean_t parents = B_FALSE; 1783789Sahrens 1784789Sahrens /* check options */ 17854490Svb160487 while ((c = getopt(argc, argv, "pr")) != -1) { 17864007Smmusante switch (c) { 17874490Svb160487 case 'p': 17884490Svb160487 parents = B_TRUE; 17894490Svb160487 break; 17904007Smmusante case 'r': 17914490Svb160487 recurse = B_TRUE; 17924007Smmusante break; 17934007Smmusante case '?': 17944007Smmusante default: 17954007Smmusante (void) fprintf(stderr, gettext("invalid option '%c'\n"), 17964007Smmusante optopt); 17974007Smmusante usage(B_FALSE); 17984007Smmusante } 1799789Sahrens } 1800789Sahrens 18014007Smmusante argc -= optind; 18024007Smmusante argv += optind; 18034007Smmusante 1804789Sahrens /* check number of arguments */ 18054007Smmusante if (argc < 1) { 1806789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 1807789Sahrens "argument\n")); 18082082Seschrock usage(B_FALSE); 1809789Sahrens } 18104007Smmusante if (argc < 2) { 1811789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 1812789Sahrens "argument\n")); 18132082Seschrock usage(B_FALSE); 1814789Sahrens } 18154007Smmusante if (argc > 2) { 1816789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 18172082Seschrock usage(B_FALSE); 1818789Sahrens } 1819789Sahrens 18204490Svb160487 if (recurse && parents) { 18214490Svb160487 (void) fprintf(stderr, gettext("-p and -r options are mutually " 18224490Svb160487 "exclusive\n")); 18234490Svb160487 usage(B_FALSE); 18244490Svb160487 } 18254490Svb160487 18264007Smmusante if (recurse && strchr(argv[0], '@') == 0) { 18274007Smmusante (void) fprintf(stderr, gettext("source dataset for recursive " 18284007Smmusante "rename must be a snapshot\n")); 18294007Smmusante usage(B_FALSE); 18304007Smmusante } 18314007Smmusante 18324490Svb160487 if ((zhp = zfs_open(g_zfs, argv[0], parents ? ZFS_TYPE_FILESYSTEM | 18334490Svb160487 ZFS_TYPE_VOLUME : ZFS_TYPE_ANY)) == NULL) 1834789Sahrens return (1); 1835789Sahrens 18364490Svb160487 /* If we were asked and the name looks good, try to create ancestors. */ 18374490Svb160487 if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) && 18384490Svb160487 zfs_create_ancestors(g_zfs, argv[1]) != 0) { 18394490Svb160487 zfs_close(zhp); 18404490Svb160487 return (1); 18414490Svb160487 } 18424490Svb160487 18434007Smmusante ret = (zfs_rename(zhp, argv[1], recurse) != 0); 18442082Seschrock 18452082Seschrock zfs_close(zhp); 18462082Seschrock return (ret); 18472082Seschrock } 18482082Seschrock 18492082Seschrock /* 18502082Seschrock * zfs promote <fs> 18512082Seschrock * 18522082Seschrock * Promotes the given clone fs to be the parent 18532082Seschrock */ 18542082Seschrock /* ARGSUSED */ 18552082Seschrock static int 18562082Seschrock zfs_do_promote(int argc, char **argv) 18572082Seschrock { 18582082Seschrock zfs_handle_t *zhp; 18592082Seschrock int ret; 18602082Seschrock 18612082Seschrock /* check options */ 18622082Seschrock if (argc > 1 && argv[1][0] == '-') { 18632082Seschrock (void) fprintf(stderr, gettext("invalid option '%c'\n"), 18642082Seschrock argv[1][1]); 18652082Seschrock usage(B_FALSE); 18662082Seschrock } 18672082Seschrock 18682082Seschrock /* check number of arguments */ 18692082Seschrock if (argc < 2) { 18702082Seschrock (void) fprintf(stderr, gettext("missing clone filesystem" 18712597Snd150628 " argument\n")); 18722082Seschrock usage(B_FALSE); 18732082Seschrock } 18742082Seschrock if (argc > 2) { 18752082Seschrock (void) fprintf(stderr, gettext("too many arguments\n")); 18762082Seschrock usage(B_FALSE); 18772082Seschrock } 18782082Seschrock 18792082Seschrock zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 18802082Seschrock if (zhp == NULL) 18812082Seschrock return (1); 18822082Seschrock 18832082Seschrock ret = (zfs_promote(zhp) != 0); 18842082Seschrock 18852926Sek110237 1886789Sahrens zfs_close(zhp); 1887789Sahrens return (ret); 1888789Sahrens } 1889789Sahrens 1890789Sahrens /* 1891789Sahrens * zfs rollback [-rfR] <snapshot> 1892789Sahrens * 1893789Sahrens * -r Delete any intervening snapshots before doing rollback 1894789Sahrens * -R Delete any snapshots and their clones 1895789Sahrens * -f Force unmount filesystems, even if they are in use. 1896789Sahrens * 1897789Sahrens * Given a filesystem, rollback to a specific snapshot, discarding any changes 1898789Sahrens * since then and making it the active dataset. If more recent snapshots exist, 1899789Sahrens * the command will complain unless the '-r' flag is given. 1900789Sahrens */ 1901789Sahrens typedef struct rollback_cbdata { 1902789Sahrens uint64_t cb_create; 19032082Seschrock boolean_t cb_first; 1904789Sahrens int cb_doclones; 1905789Sahrens char *cb_target; 1906789Sahrens int cb_error; 19072082Seschrock boolean_t cb_recurse; 19082082Seschrock boolean_t cb_dependent; 1909789Sahrens } rollback_cbdata_t; 1910789Sahrens 1911789Sahrens /* 1912789Sahrens * Report any snapshots more recent than the one specified. Used when '-r' is 1913789Sahrens * not specified. We reuse this same callback for the snapshot dependents - if 1914789Sahrens * 'cb_dependent' is set, then this is a dependent and we should report it 1915789Sahrens * without checking the transaction group. 1916789Sahrens */ 1917789Sahrens static int 1918789Sahrens rollback_check(zfs_handle_t *zhp, void *data) 1919789Sahrens { 1920789Sahrens rollback_cbdata_t *cbp = data; 1921789Sahrens 19222082Seschrock if (cbp->cb_doclones) { 19232082Seschrock zfs_close(zhp); 1924789Sahrens return (0); 19252082Seschrock } 1926789Sahrens 1927789Sahrens if (!cbp->cb_dependent) { 1928789Sahrens if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && 19291294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 1930789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 1931789Sahrens cbp->cb_create) { 1932789Sahrens 1933789Sahrens if (cbp->cb_first && !cbp->cb_recurse) { 1934789Sahrens (void) fprintf(stderr, gettext("cannot " 1935789Sahrens "rollback to '%s': more recent snapshots " 1936789Sahrens "exist\n"), 1937789Sahrens cbp->cb_target); 1938789Sahrens (void) fprintf(stderr, gettext("use '-r' to " 1939789Sahrens "force deletion of the following " 1940789Sahrens "snapshots:\n")); 1941789Sahrens cbp->cb_first = 0; 1942789Sahrens cbp->cb_error = 1; 1943789Sahrens } 1944789Sahrens 1945789Sahrens if (cbp->cb_recurse) { 19462082Seschrock cbp->cb_dependent = B_TRUE; 19472474Seschrock if (zfs_iter_dependents(zhp, B_TRUE, 19482474Seschrock rollback_check, cbp) != 0) { 19492474Seschrock zfs_close(zhp); 19502474Seschrock return (-1); 19512474Seschrock } 19522082Seschrock cbp->cb_dependent = B_FALSE; 1953789Sahrens } else { 1954789Sahrens (void) fprintf(stderr, "%s\n", 1955789Sahrens zfs_get_name(zhp)); 1956789Sahrens } 1957789Sahrens } 1958789Sahrens } else { 1959789Sahrens if (cbp->cb_first && cbp->cb_recurse) { 1960789Sahrens (void) fprintf(stderr, gettext("cannot rollback to " 1961789Sahrens "'%s': clones of previous snapshots exist\n"), 1962789Sahrens cbp->cb_target); 1963789Sahrens (void) fprintf(stderr, gettext("use '-R' to " 1964789Sahrens "force deletion of the following clones and " 1965789Sahrens "dependents:\n")); 1966789Sahrens cbp->cb_first = 0; 1967789Sahrens cbp->cb_error = 1; 1968789Sahrens } 1969789Sahrens 1970789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1971789Sahrens } 1972789Sahrens 1973789Sahrens zfs_close(zhp); 1974789Sahrens return (0); 1975789Sahrens } 1976789Sahrens 1977789Sahrens static int 1978789Sahrens zfs_do_rollback(int argc, char **argv) 1979789Sahrens { 1980789Sahrens int ret; 1981789Sahrens int c; 1982789Sahrens rollback_cbdata_t cb = { 0 }; 1983789Sahrens zfs_handle_t *zhp, *snap; 1984789Sahrens char parentname[ZFS_MAXNAMELEN]; 1985789Sahrens char *delim; 19861294Slling int force = 0; 1987789Sahrens 1988789Sahrens /* check options */ 1989789Sahrens while ((c = getopt(argc, argv, "rfR")) != -1) { 1990789Sahrens switch (c) { 1991789Sahrens case 'f': 19921294Slling force = 1; 1993789Sahrens break; 1994789Sahrens case 'r': 1995789Sahrens cb.cb_recurse = 1; 1996789Sahrens break; 1997789Sahrens case 'R': 1998789Sahrens cb.cb_recurse = 1; 1999789Sahrens cb.cb_doclones = 1; 2000789Sahrens break; 2001789Sahrens case '?': 2002789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2003789Sahrens optopt); 20042082Seschrock usage(B_FALSE); 2005789Sahrens } 2006789Sahrens } 2007789Sahrens 2008789Sahrens argc -= optind; 2009789Sahrens argv += optind; 2010789Sahrens 2011789Sahrens /* check number of arguments */ 2012789Sahrens if (argc < 1) { 2013789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 20142082Seschrock usage(B_FALSE); 2015789Sahrens } 2016789Sahrens if (argc > 1) { 2017789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 20182082Seschrock usage(B_FALSE); 2019789Sahrens } 2020789Sahrens 2021789Sahrens /* open the snapshot */ 20222082Seschrock if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 2023789Sahrens return (1); 2024789Sahrens 20251294Slling /* open the parent dataset */ 20261294Slling (void) strlcpy(parentname, argv[0], sizeof (parentname)); 2027789Sahrens verify((delim = strrchr(parentname, '@')) != NULL); 2028789Sahrens *delim = '\0'; 20292082Seschrock if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_ANY)) == NULL) { 2030789Sahrens zfs_close(snap); 2031789Sahrens return (1); 2032789Sahrens } 2033789Sahrens 2034789Sahrens /* 2035789Sahrens * Check for more recent snapshots and/or clones based on the presence 2036789Sahrens * of '-r' and '-R'. 2037789Sahrens */ 20381294Slling cb.cb_target = argv[0]; 20391294Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 20402082Seschrock cb.cb_first = B_TRUE; 2041789Sahrens cb.cb_error = 0; 20422474Seschrock if ((ret = zfs_iter_children(zhp, rollback_check, &cb)) != 0) 20432474Seschrock goto out; 2044789Sahrens 2045789Sahrens if ((ret = cb.cb_error) != 0) 2046789Sahrens goto out; 2047789Sahrens 2048789Sahrens /* 20491294Slling * Rollback parent to the given snapshot. 2050789Sahrens */ 20511294Slling ret = zfs_rollback(zhp, snap, force); 2052789Sahrens 2053789Sahrens out: 2054789Sahrens zfs_close(snap); 2055789Sahrens zfs_close(zhp); 2056789Sahrens 2057789Sahrens if (ret == 0) 2058789Sahrens return (0); 2059789Sahrens else 2060789Sahrens return (1); 2061789Sahrens } 2062789Sahrens 2063789Sahrens /* 2064789Sahrens * zfs set property=value { fs | snap | vol } ... 2065789Sahrens * 2066789Sahrens * Sets the given property for all datasets specified on the command line. 2067789Sahrens */ 2068789Sahrens typedef struct set_cbdata { 2069789Sahrens char *cb_propname; 2070789Sahrens char *cb_value; 2071789Sahrens } set_cbdata_t; 2072789Sahrens 2073789Sahrens static int 2074789Sahrens set_callback(zfs_handle_t *zhp, void *data) 2075789Sahrens { 2076789Sahrens set_cbdata_t *cbp = data; 2077789Sahrens 20782676Seschrock if (zfs_prop_set(zhp, cbp->cb_propname, cbp->cb_value) != 0) { 20792169Snd150628 switch (libzfs_errno(g_zfs)) { 20802169Snd150628 case EZFS_MOUNTFAILED: 20812169Snd150628 (void) fprintf(stderr, gettext("property may be set " 20822169Snd150628 "but unable to remount filesystem\n")); 20832169Snd150628 break; 20843126Sahl case EZFS_SHARENFSFAILED: 20852169Snd150628 (void) fprintf(stderr, gettext("property may be set " 20862169Snd150628 "but unable to reshare filesystem\n")); 20872169Snd150628 break; 20882169Snd150628 } 2089789Sahrens return (1); 20902169Snd150628 } 20912856Snd150628 return (0); 2092789Sahrens } 2093789Sahrens 2094789Sahrens static int 2095789Sahrens zfs_do_set(int argc, char **argv) 2096789Sahrens { 2097789Sahrens set_cbdata_t cb; 20982926Sek110237 int ret; 2099789Sahrens 2100789Sahrens /* check for options */ 2101789Sahrens if (argc > 1 && argv[1][0] == '-') { 2102789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2103789Sahrens argv[1][1]); 21042082Seschrock usage(B_FALSE); 2105789Sahrens } 2106789Sahrens 2107789Sahrens /* check number of arguments */ 2108789Sahrens if (argc < 2) { 2109789Sahrens (void) fprintf(stderr, gettext("missing property=value " 2110789Sahrens "argument\n")); 21112082Seschrock usage(B_FALSE); 2112789Sahrens } 2113789Sahrens if (argc < 3) { 2114789Sahrens (void) fprintf(stderr, gettext("missing dataset name\n")); 21152082Seschrock usage(B_FALSE); 2116789Sahrens } 2117789Sahrens 2118789Sahrens /* validate property=value argument */ 2119789Sahrens cb.cb_propname = argv[1]; 2120789Sahrens if ((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) { 2121789Sahrens (void) fprintf(stderr, gettext("missing value in " 2122789Sahrens "property=value argument\n")); 21232082Seschrock usage(B_FALSE); 2124789Sahrens } 2125789Sahrens 2126789Sahrens *cb.cb_value = '\0'; 2127789Sahrens cb.cb_value++; 2128789Sahrens 2129789Sahrens if (*cb.cb_propname == '\0') { 2130789Sahrens (void) fprintf(stderr, 2131789Sahrens gettext("missing property in property=value argument\n")); 21322082Seschrock usage(B_FALSE); 2133789Sahrens } 2134789Sahrens 21354543Smarks 21362926Sek110237 ret = zfs_for_each(argc - 2, argv + 2, B_FALSE, 21373635Sck153898 ZFS_TYPE_ANY, NULL, NULL, set_callback, &cb, B_FALSE); 21382926Sek110237 21392926Sek110237 return (ret); 2140789Sahrens } 2141789Sahrens 2142789Sahrens /* 21432199Sahrens * zfs snapshot [-r] <fs@snap> 2144789Sahrens * 2145789Sahrens * Creates a snapshot with the given name. While functionally equivalent to 2146789Sahrens * 'zfs create', it is a separate command to diffferentiate intent. 2147789Sahrens */ 2148789Sahrens static int 2149789Sahrens zfs_do_snapshot(int argc, char **argv) 2150789Sahrens { 21514490Svb160487 boolean_t recursive = B_FALSE; 21522199Sahrens int ret; 21532199Sahrens char c; 21542199Sahrens 2155789Sahrens /* check options */ 21562199Sahrens while ((c = getopt(argc, argv, ":r")) != -1) { 21572199Sahrens switch (c) { 21582199Sahrens case 'r': 21592199Sahrens recursive = B_TRUE; 21602199Sahrens break; 21612199Sahrens case '?': 21622199Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 21632199Sahrens optopt); 21642199Sahrens usage(B_FALSE); 21652199Sahrens } 2166789Sahrens } 2167789Sahrens 21682199Sahrens argc -= optind; 21692199Sahrens argv += optind; 21702199Sahrens 2171789Sahrens /* check number of arguments */ 21722199Sahrens if (argc < 1) { 2173789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 21742082Seschrock usage(B_FALSE); 2175789Sahrens } 21762199Sahrens if (argc > 1) { 2177789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 21782082Seschrock usage(B_FALSE); 2179789Sahrens } 2180789Sahrens 21812199Sahrens ret = zfs_snapshot(g_zfs, argv[0], recursive); 21822199Sahrens if (ret && recursive) 21832199Sahrens (void) fprintf(stderr, gettext("no snapshots were created\n")); 21842199Sahrens return (ret != 0); 2185789Sahrens } 2186789Sahrens 2187789Sahrens /* 21882885Sahrens * zfs send [-i <@snap>] <fs@snap> 2189789Sahrens * 2190789Sahrens * Send a backup stream to stdout. 2191789Sahrens */ 2192789Sahrens static int 21931749Sahrens zfs_do_send(int argc, char **argv) 2194789Sahrens { 2195789Sahrens char *fromname = NULL; 21962885Sahrens char *cp; 21972885Sahrens zfs_handle_t *zhp; 2198789Sahrens int c, err; 2199789Sahrens 2200789Sahrens /* check options */ 2201789Sahrens while ((c = getopt(argc, argv, ":i:")) != -1) { 2202789Sahrens switch (c) { 2203789Sahrens case 'i': 22042885Sahrens if (fromname) 22052885Sahrens usage(B_FALSE); 2206789Sahrens fromname = optarg; 2207789Sahrens break; 2208789Sahrens case ':': 2209789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2210789Sahrens "'%c' option\n"), optopt); 22112082Seschrock usage(B_FALSE); 2212789Sahrens break; 2213789Sahrens case '?': 2214789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2215789Sahrens optopt); 22162082Seschrock usage(B_FALSE); 2217789Sahrens } 2218789Sahrens } 2219789Sahrens 2220789Sahrens argc -= optind; 2221789Sahrens argv += optind; 2222789Sahrens 2223789Sahrens /* check number of arguments */ 2224789Sahrens if (argc < 1) { 2225789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 22262082Seschrock usage(B_FALSE); 2227789Sahrens } 2228789Sahrens if (argc > 1) { 2229789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 22302082Seschrock usage(B_FALSE); 2231789Sahrens } 2232789Sahrens 2233789Sahrens if (isatty(STDOUT_FILENO)) { 2234789Sahrens (void) fprintf(stderr, 22352885Sahrens gettext("Error: Stream can not be written to a terminal.\n" 22363912Slling "You must redirect standard output.\n")); 2237789Sahrens return (1); 2238789Sahrens } 2239789Sahrens 22402885Sahrens if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 22412665Snd150628 return (1); 22422665Snd150628 22432885Sahrens /* 22442885Sahrens * If they specified the full path to the snapshot, chop off 22452885Sahrens * everything except the short name of the snapshot. 22462885Sahrens */ 22472885Sahrens if (fromname && (cp = strchr(fromname, '@')) != NULL) { 22482885Sahrens if (cp != fromname && 22492885Sahrens strncmp(argv[0], fromname, cp - fromname + 1)) { 22502885Sahrens (void) fprintf(stderr, 22512885Sahrens gettext("incremental source must be " 22522885Sahrens "in same filesystem\n")); 22532885Sahrens usage(B_FALSE); 22542665Snd150628 } 22552885Sahrens fromname = cp + 1; 22562885Sahrens if (strchr(fromname, '@') || strchr(fromname, '/')) { 22572885Sahrens (void) fprintf(stderr, 22582885Sahrens gettext("invalid incremental source\n")); 22592885Sahrens usage(B_FALSE); 22602885Sahrens } 2261789Sahrens } 2262789Sahrens 22633504Sahl err = zfs_send(zhp, fromname, STDOUT_FILENO); 22642885Sahrens zfs_close(zhp); 2265789Sahrens 2266789Sahrens return (err != 0); 2267789Sahrens } 2268789Sahrens 2269789Sahrens /* 22701749Sahrens * zfs receive <fs@snap> 2271789Sahrens * 2272789Sahrens * Restore a backup stream from stdin. 2273789Sahrens */ 2274789Sahrens static int 22751749Sahrens zfs_do_receive(int argc, char **argv) 2276789Sahrens { 2277789Sahrens int c, err; 22782082Seschrock boolean_t isprefix = B_FALSE; 22792082Seschrock boolean_t dryrun = B_FALSE; 22802082Seschrock boolean_t verbose = B_FALSE; 22812665Snd150628 boolean_t force = B_FALSE; 2282789Sahrens 2283789Sahrens /* check options */ 22842665Snd150628 while ((c = getopt(argc, argv, ":dnvF")) != -1) { 2285789Sahrens switch (c) { 2286789Sahrens case 'd': 22872082Seschrock isprefix = B_TRUE; 2288789Sahrens break; 2289789Sahrens case 'n': 22902082Seschrock dryrun = B_TRUE; 2291789Sahrens break; 2292789Sahrens case 'v': 22932082Seschrock verbose = B_TRUE; 2294789Sahrens break; 22952665Snd150628 case 'F': 22962665Snd150628 force = B_TRUE; 22972665Snd150628 break; 2298789Sahrens case ':': 2299789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2300789Sahrens "'%c' option\n"), optopt); 23012082Seschrock usage(B_FALSE); 2302789Sahrens break; 2303789Sahrens case '?': 2304789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2305789Sahrens optopt); 23062082Seschrock usage(B_FALSE); 2307789Sahrens } 2308789Sahrens } 2309789Sahrens 2310789Sahrens argc -= optind; 2311789Sahrens argv += optind; 2312789Sahrens 2313789Sahrens /* check number of arguments */ 2314789Sahrens if (argc < 1) { 2315789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 23162082Seschrock usage(B_FALSE); 2317789Sahrens } 2318789Sahrens if (argc > 1) { 2319789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 23202082Seschrock usage(B_FALSE); 2321789Sahrens } 2322789Sahrens 2323789Sahrens if (isatty(STDIN_FILENO)) { 2324789Sahrens (void) fprintf(stderr, 2325789Sahrens gettext("Error: Backup stream can not be read " 23263912Slling "from a terminal.\n" 23273912Slling "You must redirect standard input.\n")); 2328789Sahrens return (1); 2329789Sahrens } 2330789Sahrens 23313504Sahl err = zfs_receive(g_zfs, argv[0], isprefix, verbose, dryrun, force, 23323504Sahl STDIN_FILENO); 23332926Sek110237 23344543Smarks return (err != 0); 23354543Smarks } 23364543Smarks 23374543Smarks typedef struct allow_cb { 23384543Smarks int a_permcnt; 23394543Smarks size_t a_treeoffset; 23404543Smarks } allow_cb_t; 23414543Smarks 23424543Smarks static void 23434543Smarks zfs_print_perms(avl_tree_t *tree) 23444543Smarks { 23454543Smarks zfs_perm_node_t *permnode; 23464543Smarks 23474543Smarks permnode = avl_first(tree); 23484543Smarks while (permnode != NULL) { 23494543Smarks (void) printf("%s", permnode->z_pname); 23504543Smarks permnode = AVL_NEXT(tree, permnode); 23514543Smarks if (permnode) 23524543Smarks (void) printf(","); 23534543Smarks else 23544543Smarks (void) printf("\n"); 23554543Smarks } 23564543Smarks } 23574543Smarks 23584543Smarks /* 23594543Smarks * Iterate over user/groups/everyone/... and the call perm_iter 23604543Smarks * function to print actual permission when tree has >0 nodes. 23614543Smarks */ 23624543Smarks static void 23634543Smarks zfs_iter_perms(avl_tree_t *tree, const char *banner, allow_cb_t *cb) 23644543Smarks { 23654543Smarks zfs_allow_node_t *item; 23664543Smarks avl_tree_t *ptree; 23674543Smarks 23684543Smarks item = avl_first(tree); 23694543Smarks while (item) { 23704543Smarks ptree = (void *)((char *)item + cb->a_treeoffset); 23714543Smarks if (avl_numnodes(ptree)) { 23724543Smarks if (cb->a_permcnt++ == 0) 23734543Smarks (void) printf("%s\n", banner); 23744543Smarks (void) printf("\t%s", item->z_key); 23754543Smarks /* 23764543Smarks * Avoid an extra space being printed 23774543Smarks * for "everyone" which is keyed with a null 23784543Smarks * string 23794543Smarks */ 23804543Smarks if (item->z_key[0] != '\0') 23814543Smarks (void) printf(" "); 23824543Smarks zfs_print_perms(ptree); 23834543Smarks } 23844543Smarks item = AVL_NEXT(tree, item); 23854543Smarks } 23864543Smarks } 23874543Smarks 23884543Smarks #define LINES "-------------------------------------------------------------\n" 23894543Smarks static int 23904543Smarks zfs_print_allows(char *ds) 23914543Smarks { 23924543Smarks zfs_allow_t *curperms, *perms; 23934543Smarks zfs_handle_t *zhp; 23944543Smarks allow_cb_t allowcb = { 0 }; 23954543Smarks char banner[MAXPATHLEN]; 23964543Smarks 23974543Smarks if (ds[0] == '-') 23984543Smarks usage(B_FALSE); 23994543Smarks 24004543Smarks if (strrchr(ds, '@')) { 24014543Smarks (void) fprintf(stderr, gettext("Snapshots don't have 'allow'" 24024543Smarks " permissions\n")); 24034543Smarks return (1); 24044543Smarks } 24054543Smarks if ((zhp = zfs_open(g_zfs, ds, ZFS_TYPE_ANY)) == NULL) 24064543Smarks return (1); 24074543Smarks 24084543Smarks if (zfs_perm_get(zhp, &perms)) { 24094543Smarks (void) fprintf(stderr, 24104543Smarks gettext("Failed to retrieve 'allows' on %s\n"), ds); 24114543Smarks zfs_close(zhp); 24124543Smarks return (1); 24134543Smarks } 24144543Smarks 24154543Smarks zfs_close(zhp); 24164543Smarks 24174543Smarks if (perms != NULL) 24184543Smarks (void) printf("%s", LINES); 24194543Smarks for (curperms = perms; curperms; curperms = curperms->z_next) { 24204543Smarks 24214543Smarks (void) snprintf(banner, sizeof (banner), 24224543Smarks "Permission sets on (%s)", curperms->z_setpoint); 24234543Smarks allowcb.a_treeoffset = 24244543Smarks offsetof(zfs_allow_node_t, z_localdescend); 24254543Smarks allowcb.a_permcnt = 0; 24264543Smarks zfs_iter_perms(&curperms->z_sets, banner, &allowcb); 24274543Smarks 24284543Smarks (void) snprintf(banner, sizeof (banner), 24294543Smarks "Create time permissions on (%s)", curperms->z_setpoint); 24304543Smarks allowcb.a_treeoffset = 24314543Smarks offsetof(zfs_allow_node_t, z_localdescend); 24324543Smarks allowcb.a_permcnt = 0; 24334543Smarks zfs_iter_perms(&curperms->z_crperms, banner, &allowcb); 24344543Smarks 24354543Smarks 24364543Smarks (void) snprintf(banner, sizeof (banner), 24374543Smarks "Local permissions on (%s)", curperms->z_setpoint); 24384543Smarks allowcb.a_treeoffset = offsetof(zfs_allow_node_t, z_local); 24394543Smarks allowcb.a_permcnt = 0; 24404543Smarks zfs_iter_perms(&curperms->z_user, banner, &allowcb); 24414543Smarks zfs_iter_perms(&curperms->z_group, banner, &allowcb); 24424543Smarks zfs_iter_perms(&curperms->z_everyone, banner, &allowcb); 24434543Smarks 24444543Smarks (void) snprintf(banner, sizeof (banner), 24454543Smarks "Descendent permissions on (%s)", curperms->z_setpoint); 24464543Smarks allowcb.a_treeoffset = offsetof(zfs_allow_node_t, z_descend); 24474543Smarks allowcb.a_permcnt = 0; 24484543Smarks zfs_iter_perms(&curperms->z_user, banner, &allowcb); 24494543Smarks zfs_iter_perms(&curperms->z_group, banner, &allowcb); 24504543Smarks zfs_iter_perms(&curperms->z_everyone, banner, &allowcb); 24514543Smarks 24524543Smarks (void) snprintf(banner, sizeof (banner), 24534543Smarks "Local+Descendent permissions on (%s)", 24544543Smarks curperms->z_setpoint); 24554543Smarks allowcb.a_treeoffset = 24564543Smarks offsetof(zfs_allow_node_t, z_localdescend); 24574543Smarks allowcb.a_permcnt = 0; 24584543Smarks zfs_iter_perms(&curperms->z_user, banner, &allowcb); 24594543Smarks zfs_iter_perms(&curperms->z_group, banner, &allowcb); 24604543Smarks zfs_iter_perms(&curperms->z_everyone, banner, &allowcb); 24614543Smarks 24624543Smarks (void) printf("%s", LINES); 24632926Sek110237 } 24644543Smarks zfs_free_allows(perms); 24654543Smarks return (0); 24664543Smarks } 24674543Smarks 24684543Smarks #define ALLOWOPTIONS "ldcsu:g:e" 24694543Smarks #define UNALLOWOPTIONS "ldcsu:g:er" 24704543Smarks 24714543Smarks /* 24724543Smarks * Validate options, and build necessary datastructure to display/remove/add 24734543Smarks * permissions. 24744543Smarks * Returns 0 - If permissions should be added/removed 24754543Smarks * Returns 1 - If permissions should be displayed. 24764543Smarks * Returns -1 - on failure 24774543Smarks */ 24784543Smarks int 24794543Smarks parse_allow_args(int *argc, char **argv[], boolean_t unallow, 24804543Smarks char **ds, int *recurse, nvlist_t **zperms) 24814543Smarks { 24824543Smarks int c; 24834543Smarks char *options = unallow ? UNALLOWOPTIONS : ALLOWOPTIONS; 24844543Smarks zfs_deleg_inherit_t deleg_type = ZFS_DELEG_NONE; 24854543Smarks zfs_deleg_who_type_t who_type = ZFS_DELEG_WHO_UNKNOWN; 24864543Smarks char *who; 24874543Smarks char *perms = NULL; 24884543Smarks zfs_handle_t *zhp; 24894543Smarks 24904543Smarks while ((c = getopt(*argc, *argv, options)) != -1) { 24914543Smarks switch (c) { 24924543Smarks case 'l': 24934543Smarks if (who_type == ZFS_DELEG_CREATE || 24944543Smarks who_type == ZFS_DELEG_NAMED_SET) 24954543Smarks usage(B_FALSE); 24964543Smarks 24974543Smarks deleg_type |= ZFS_DELEG_PERM_LOCAL; 24984543Smarks break; 24994543Smarks case 'd': 25004543Smarks if (who_type == ZFS_DELEG_CREATE || 25014543Smarks who_type == ZFS_DELEG_NAMED_SET) 25024543Smarks usage(B_FALSE); 25034543Smarks 25044543Smarks deleg_type |= ZFS_DELEG_PERM_DESCENDENT; 25054543Smarks break; 25064543Smarks case 'r': 25074543Smarks *recurse = B_TRUE; 25084543Smarks break; 25094543Smarks case 'c': 25104543Smarks if (who_type != ZFS_DELEG_WHO_UNKNOWN) 25114543Smarks usage(B_FALSE); 25124543Smarks if (deleg_type) 25134543Smarks usage(B_FALSE); 25144543Smarks who_type = ZFS_DELEG_CREATE; 25154543Smarks break; 25164543Smarks case 's': 25174543Smarks if (who_type != ZFS_DELEG_WHO_UNKNOWN) 25184543Smarks usage(B_FALSE); 25194543Smarks if (deleg_type) 25204543Smarks usage(B_FALSE); 25214543Smarks who_type = ZFS_DELEG_NAMED_SET; 25224543Smarks break; 25234543Smarks case 'u': 25244543Smarks if (who_type != ZFS_DELEG_WHO_UNKNOWN) 25254543Smarks usage(B_FALSE); 25264543Smarks who_type = ZFS_DELEG_USER; 25274543Smarks who = optarg; 25284543Smarks break; 25294543Smarks case 'g': 25304543Smarks if (who_type != ZFS_DELEG_WHO_UNKNOWN) 25314543Smarks usage(B_FALSE); 25324543Smarks who_type = ZFS_DELEG_GROUP; 25334543Smarks who = optarg; 25344543Smarks break; 25354543Smarks case 'e': 25364543Smarks if (who_type != ZFS_DELEG_WHO_UNKNOWN) 25374543Smarks usage(B_FALSE); 25384543Smarks who_type = ZFS_DELEG_EVERYONE; 25394543Smarks break; 25404543Smarks default: 25414543Smarks usage(B_FALSE); 25424543Smarks break; 25434543Smarks } 25444543Smarks } 25454543Smarks 25464543Smarks if (deleg_type == 0) 25474543Smarks deleg_type = ZFS_DELEG_PERM_LOCALDESCENDENT; 25484543Smarks 25494543Smarks *argc -= optind; 25504543Smarks *argv += optind; 25514543Smarks 25524543Smarks if (unallow == B_FALSE && *argc == 1) { 25534543Smarks /* 25544543Smarks * Only print permissions if no options were processed 25554543Smarks */ 25564543Smarks if (optind == 1) 25574543Smarks return (1); 25584543Smarks else 25594543Smarks usage(B_FALSE); 25604543Smarks } 25614543Smarks 25624543Smarks /* 25634543Smarks * initialize variables for zfs_build_perms based on number 25644543Smarks * of arguments. 25654543Smarks * 3 arguments ==> zfs [un]allow joe perm,perm,perm <dataset> or 25664543Smarks * zfs [un]allow -s @set1 perm,perm <dataset> 25674543Smarks * 2 arguments ==> zfs [un]allow -c perm,perm <dataset> or 25684543Smarks * zfs [un]allow -u|-g <name> perm <dataset> or 25694543Smarks * zfs [un]allow -e perm,perm <dataset> 25704543Smarks * zfs unallow joe <dataset> 25714543Smarks * zfs unallow -s @set1 <dataset> 25724543Smarks * 1 argument ==> zfs [un]allow -e <dataset> or 25734543Smarks * zfs [un]allow -c <dataset> 25744543Smarks */ 25754543Smarks 25764543Smarks switch (*argc) { 25774543Smarks case 3: 25784543Smarks perms = (*argv)[1]; 25794543Smarks who = (*argv)[0]; 25804543Smarks *ds = (*argv)[2]; 25814543Smarks 25824543Smarks /* 25834543Smarks * advance argc/argv for do_allow cases. 25844543Smarks * for do_allow case make sure who have a know who type 25854543Smarks * and its not a permission set. 25864543Smarks */ 25874543Smarks if (unallow == B_TRUE) { 25884543Smarks *argc -= 2; 25894543Smarks *argv += 2; 25904543Smarks } else if (who_type != ZFS_DELEG_WHO_UNKNOWN && 25914543Smarks who_type != ZFS_DELEG_NAMED_SET) 25924543Smarks usage(B_FALSE); 25934543Smarks break; 25944543Smarks 25954543Smarks case 2: 25964543Smarks if (unallow == B_TRUE && (who_type == ZFS_DELEG_EVERYONE || 25974543Smarks who_type == ZFS_DELEG_CREATE || who != NULL)) { 25984543Smarks perms = (*argv)[0]; 25994543Smarks *ds = (*argv)[1]; 26004543Smarks } else { 26014543Smarks if (unallow == B_FALSE && 26024543Smarks (who_type == ZFS_DELEG_WHO_UNKNOWN || 26034543Smarks who_type == ZFS_DELEG_NAMED_SET)) 26044543Smarks usage(B_FALSE); 26054543Smarks else if (who_type == ZFS_DELEG_WHO_UNKNOWN || 26064543Smarks who_type == ZFS_DELEG_NAMED_SET) 26074543Smarks who = (*argv)[0]; 26084543Smarks else if (who_type != ZFS_DELEG_NAMED_SET) 26094543Smarks perms = (*argv)[0]; 26104543Smarks *ds = (*argv)[1]; 26114543Smarks } 26124543Smarks if (unallow == B_TRUE) { 26134543Smarks (*argc)--; 26144543Smarks (*argv)++; 26154543Smarks } 26164543Smarks break; 26174543Smarks 26184543Smarks case 1: 26194543Smarks if (unallow == B_FALSE) 26204543Smarks usage(B_FALSE); 26214543Smarks if (who == NULL && who_type != ZFS_DELEG_CREATE && 26224543Smarks who_type != ZFS_DELEG_EVERYONE) 26234543Smarks usage(B_FALSE); 26244543Smarks *ds = (*argv)[0]; 26254543Smarks break; 26264543Smarks 26274543Smarks default: 26284543Smarks usage(B_FALSE); 26294543Smarks } 26304543Smarks 26314543Smarks if (strrchr(*ds, '@')) { 26324543Smarks (void) fprintf(stderr, 26334543Smarks gettext("Can't set or remove 'allow' permissions " 26344543Smarks "on snapshots.\n")); 26354543Smarks return (-1); 26364543Smarks } 26374543Smarks 26384543Smarks if ((zhp = zfs_open(g_zfs, *ds, ZFS_TYPE_ANY)) == NULL) 26394543Smarks return (-1); 26404543Smarks 26414543Smarks if ((zfs_build_perms(zhp, who, perms, 26424543Smarks who_type, deleg_type, zperms)) != 0) { 26434543Smarks zfs_close(zhp); 26444543Smarks return (-1); 26454543Smarks } 26464543Smarks zfs_close(zhp); 26474543Smarks return (0); 26484543Smarks } 26494543Smarks 26504543Smarks static int 26514543Smarks zfs_do_allow(int argc, char **argv) 26524543Smarks { 26534543Smarks char *ds; 26544543Smarks nvlist_t *zperms = NULL; 26554543Smarks zfs_handle_t *zhp; 26564543Smarks int unused; 26574543Smarks int ret; 26584543Smarks 26594543Smarks if ((ret = parse_allow_args(&argc, &argv, B_FALSE, &ds, 26604543Smarks &unused, &zperms)) == -1) 26614543Smarks return (1); 26624543Smarks 26634543Smarks if (ret == 1) 26644543Smarks return (zfs_print_allows(argv[0])); 26654543Smarks 26664543Smarks if ((zhp = zfs_open(g_zfs, ds, ZFS_TYPE_ANY)) == NULL) 26674543Smarks return (1); 26684543Smarks 26694543Smarks if (zfs_perm_set(zhp, zperms)) { 26704543Smarks zfs_close(zhp); 26714543Smarks nvlist_free(zperms); 26724543Smarks return (1); 26734543Smarks } 26744543Smarks nvlist_free(zperms); 26754543Smarks zfs_close(zhp); 26764543Smarks 26774543Smarks return (0); 26784543Smarks } 26794543Smarks 26804543Smarks static int 26814543Smarks unallow_callback(zfs_handle_t *zhp, void *data) 26824543Smarks { 26834543Smarks nvlist_t *nvp = (nvlist_t *)data; 26844543Smarks int error; 26854543Smarks 26864543Smarks error = zfs_perm_remove(zhp, nvp); 26874543Smarks if (error) { 26884543Smarks (void) fprintf(stderr, gettext("Failed to remove permissions " 26894543Smarks "on %s\n"), zfs_get_name(zhp)); 26904543Smarks } 26914543Smarks return (error); 26924543Smarks } 26934543Smarks 26944543Smarks static int 26954543Smarks zfs_do_unallow(int argc, char **argv) 26964543Smarks { 26974543Smarks int recurse = B_FALSE; 26984543Smarks char *ds; 26994543Smarks int error; 27004543Smarks nvlist_t *zperms = NULL; 27014543Smarks 27024543Smarks if (parse_allow_args(&argc, &argv, B_TRUE, 27034543Smarks &ds, &recurse, &zperms) == -1) 27044543Smarks return (1); 27054543Smarks 27064543Smarks error = zfs_for_each(argc, argv, recurse, 27074543Smarks ZFS_TYPE_FILESYSTEM|ZFS_TYPE_VOLUME, NULL, 27084543Smarks NULL, unallow_callback, (void *)zperms, B_FALSE); 27094543Smarks 27104543Smarks if (zperms) 27114543Smarks nvlist_free(zperms); 27124543Smarks 27134543Smarks return (error); 2714789Sahrens } 2715789Sahrens 27161356Seschrock typedef struct get_all_cbdata { 27171356Seschrock zfs_handle_t **cb_handles; 27181356Seschrock size_t cb_alloc; 27191356Seschrock size_t cb_used; 27203126Sahl uint_t cb_types; 2721*4737Smmusante boolean_t cb_verbose; 27221356Seschrock } get_all_cbdata_t; 27231356Seschrock 2724*4737Smmusante #define CHECK_SPINNER 30 2725*4737Smmusante #define SPINNER_TIME 3 /* seconds */ 2726*4737Smmusante #define MOUNT_TIME 5 /* seconds */ 2727*4737Smmusante 27281356Seschrock static int 27293126Sahl get_one_dataset(zfs_handle_t *zhp, void *data) 27301356Seschrock { 2731*4737Smmusante static char spin[] = { '-', '\\', '|', '/' }; 2732*4737Smmusante static int spinval = 0; 2733*4737Smmusante static int spincheck = 0; 2734*4737Smmusante static time_t last_spin_time = (time_t)0; 27351356Seschrock get_all_cbdata_t *cbp = data; 27363126Sahl zfs_type_t type = zfs_get_type(zhp); 27371356Seschrock 2738*4737Smmusante if (cbp->cb_verbose) { 2739*4737Smmusante if (--spincheck < 0) { 2740*4737Smmusante time_t now = time(NULL); 2741*4737Smmusante if (last_spin_time + SPINNER_TIME < now) { 2742*4737Smmusante (void) printf("\b%c", spin[spinval++ % 4]); 2743*4737Smmusante (void) fflush(stdout); 2744*4737Smmusante last_spin_time = now; 2745*4737Smmusante } 2746*4737Smmusante spincheck = CHECK_SPINNER; 2747*4737Smmusante } 2748*4737Smmusante } 2749*4737Smmusante 27501356Seschrock /* 27513126Sahl * Interate over any nested datasets. 27521356Seschrock */ 27533126Sahl if (type == ZFS_TYPE_FILESYSTEM && 27543126Sahl zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) { 27553265Sahrens zfs_close(zhp); 27563126Sahl return (1); 27573126Sahl } 27583126Sahl 27593126Sahl /* 27603126Sahl * Skip any datasets whose type does not match. 27613126Sahl */ 27623126Sahl if ((type & cbp->cb_types) == 0) { 27631356Seschrock zfs_close(zhp); 27641356Seschrock return (0); 27651356Seschrock } 27661356Seschrock 27671356Seschrock if (cbp->cb_alloc == cbp->cb_used) { 27681356Seschrock zfs_handle_t **handles; 27691356Seschrock 27701356Seschrock if (cbp->cb_alloc == 0) 27711356Seschrock cbp->cb_alloc = 64; 27721356Seschrock else 27731356Seschrock cbp->cb_alloc *= 2; 27741356Seschrock 27751356Seschrock handles = safe_malloc(cbp->cb_alloc * sizeof (void *)); 27761356Seschrock 27771356Seschrock if (cbp->cb_handles) { 27781356Seschrock bcopy(cbp->cb_handles, handles, 27791356Seschrock cbp->cb_used * sizeof (void *)); 27801356Seschrock free(cbp->cb_handles); 27811356Seschrock } 27821356Seschrock 27831356Seschrock cbp->cb_handles = handles; 27841356Seschrock } 27851356Seschrock 27861356Seschrock cbp->cb_handles[cbp->cb_used++] = zhp; 27871356Seschrock 27883126Sahl return (0); 27891356Seschrock } 27901356Seschrock 27911356Seschrock static void 2792*4737Smmusante get_all_datasets(uint_t types, zfs_handle_t ***dslist, size_t *count, 2793*4737Smmusante boolean_t verbose) 27941356Seschrock { 27951356Seschrock get_all_cbdata_t cb = { 0 }; 27963126Sahl cb.cb_types = types; 2797*4737Smmusante cb.cb_verbose = verbose; 2798*4737Smmusante 2799*4737Smmusante if (verbose) { 2800*4737Smmusante (void) printf("%s: *", gettext("Reading ZFS config")); 2801*4737Smmusante (void) fflush(stdout); 2802*4737Smmusante } 28033126Sahl 28043126Sahl (void) zfs_iter_root(g_zfs, get_one_dataset, &cb); 28053126Sahl 28063126Sahl *dslist = cb.cb_handles; 28071356Seschrock *count = cb.cb_used; 2808*4737Smmusante 2809*4737Smmusante if (verbose) { 2810*4737Smmusante (void) printf("\b%s\n", gettext("done.")); 2811*4737Smmusante } 28121356Seschrock } 28131356Seschrock 28141356Seschrock static int 28153126Sahl dataset_cmp(const void *a, const void *b) 28161356Seschrock { 28171356Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 28181356Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 28191356Seschrock char mounta[MAXPATHLEN]; 28201356Seschrock char mountb[MAXPATHLEN]; 28213126Sahl boolean_t gota, gotb; 28223126Sahl 28233126Sahl if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0) 28243126Sahl verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 28253126Sahl sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 28263126Sahl if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0) 28273126Sahl verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 28283126Sahl sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 28293126Sahl 28303126Sahl if (gota && gotb) 28313126Sahl return (strcmp(mounta, mountb)); 28323126Sahl 28333126Sahl if (gota) 28343126Sahl return (-1); 28353126Sahl if (gotb) 28363126Sahl return (1); 28373126Sahl 28383126Sahl return (strcmp(zfs_get_name(a), zfs_get_name(b))); 28391356Seschrock } 2840789Sahrens 2841789Sahrens /* 2842789Sahrens * Generic callback for sharing or mounting filesystems. Because the code is so 2843789Sahrens * similar, we have a common function with an extra parameter to determine which 2844789Sahrens * mode we are using. 2845789Sahrens */ 2846789Sahrens #define OP_SHARE 0x1 2847789Sahrens #define OP_MOUNT 0x2 2848789Sahrens 2849789Sahrens /* 28503126Sahl * Share or mount a dataset. 2851789Sahrens */ 2852789Sahrens static int 28533126Sahl share_mount_one(zfs_handle_t *zhp, int op, int flags, boolean_t explicit, 28543126Sahl const char *options) 2855789Sahrens { 2856789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2857789Sahrens char shareopts[ZFS_MAXPROPLEN]; 28583126Sahl const char *cmdname = op == OP_SHARE ? "share" : "mount"; 2859789Sahrens struct mnttab mnt; 28602676Seschrock uint64_t zoned, canmount; 28613126Sahl zfs_type_t type = zfs_get_type(zhp); 28623126Sahl 28633126Sahl assert(type & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)); 28643126Sahl 28653126Sahl if (type == ZFS_TYPE_FILESYSTEM) { 28663126Sahl /* 28673126Sahl * Check to make sure we can mount/share this dataset. If we 28683126Sahl * are in the global zone and the filesystem is exported to a 28693126Sahl * local zone, or if we are in a local zone and the 28703126Sahl * filesystem is not exported, then it is an error. 28713126Sahl */ 28723126Sahl zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 28733126Sahl 28743126Sahl if (zoned && getzoneid() == GLOBAL_ZONEID) { 28753126Sahl if (!explicit) 28763126Sahl return (0); 28773126Sahl 28783126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 28793126Sahl "dataset is exported to a local zone\n"), cmdname, 28803126Sahl zfs_get_name(zhp)); 28813126Sahl return (1); 28823126Sahl 28833126Sahl } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 28843126Sahl if (!explicit) 28853126Sahl return (0); 28863126Sahl 28873126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 28883126Sahl "permission denied\n"), cmdname, 28893126Sahl zfs_get_name(zhp)); 28903126Sahl return (1); 28913126Sahl } 28923126Sahl 28933126Sahl /* 28943126Sahl * Ignore any filesystems which don't apply to us. This 28953126Sahl * includes those with a legacy mountpoint, or those with 28963126Sahl * legacy share options. 28973126Sahl */ 28983126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 28993126Sahl sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); 29003126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 29013126Sahl sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 29023126Sahl canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); 29033126Sahl 29043126Sahl if (op == OP_SHARE && strcmp(shareopts, "off") == 0) { 29053126Sahl if (!explicit) 2906789Sahrens return (0); 2907789Sahrens 2908789Sahrens (void) fprintf(stderr, gettext("cannot share '%s': " 2909789Sahrens "legacy share\n"), zfs_get_name(zhp)); 2910789Sahrens (void) fprintf(stderr, gettext("use share(1M) to " 2911789Sahrens "share this filesystem\n")); 2912789Sahrens return (1); 2913789Sahrens } 29143126Sahl 29153126Sahl /* 29163126Sahl * We cannot share or mount legacy filesystems. If the 29173126Sahl * shareopts is non-legacy but the mountpoint is legacy, we 29183126Sahl * treat it as a legacy share. 29193126Sahl */ 29203126Sahl if (strcmp(mountpoint, "legacy") == 0) { 29213126Sahl if (!explicit) 29223126Sahl return (0); 29233126Sahl 29243126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 29253126Sahl "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 29263126Sahl (void) fprintf(stderr, gettext("use %s to " 29273126Sahl "%s this filesystem\n"), op == OP_SHARE ? 29283126Sahl "share(1M)" : "mount(1M)", cmdname); 29293126Sahl return (1); 29303126Sahl } 29313126Sahl 29323126Sahl if (strcmp(mountpoint, "none") == 0) { 29333126Sahl if (!explicit) 29343126Sahl return (0); 29353126Sahl 29363126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': no " 29373126Sahl "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 29383126Sahl return (1); 29393126Sahl } 29403126Sahl 29413126Sahl if (!canmount) { 29423126Sahl if (!explicit) 29433126Sahl return (0); 29443126Sahl 29453126Sahl (void) fprintf(stderr, gettext("cannot %s '%s': " 29463126Sahl "'canmount' property is set to 'off'\n"), cmdname, 29473126Sahl zfs_get_name(zhp)); 29483126Sahl return (1); 29493126Sahl } 29503126Sahl 29513126Sahl /* 29523126Sahl * At this point, we have verified that the mountpoint and/or 29533126Sahl * shareopts are appropriate for auto management. If the 29543126Sahl * filesystem is already mounted or shared, return (failing 29553126Sahl * for explicit requests); otherwise mount or share the 29563126Sahl * filesystem. 29573126Sahl */ 29583126Sahl switch (op) { 29593126Sahl case OP_SHARE: 29603126Sahl if (zfs_is_shared_nfs(zhp, NULL)) { 29613126Sahl if (!explicit) 29623126Sahl return (0); 29633126Sahl 2964789Sahrens (void) fprintf(stderr, gettext("cannot share " 2965789Sahrens "'%s': filesystem already shared\n"), 2966789Sahrens zfs_get_name(zhp)); 2967789Sahrens return (1); 2968789Sahrens } 29693126Sahl 29703126Sahl if (!zfs_is_mounted(zhp, NULL) && 29713126Sahl zfs_mount(zhp, NULL, 0) != 0) 29723126Sahl return (1); 29733126Sahl 29743126Sahl if (zfs_share_nfs(zhp) != 0) 29753126Sahl return (1); 29763126Sahl break; 29773126Sahl 29783126Sahl case OP_MOUNT: 29793126Sahl if (options == NULL) 29803126Sahl mnt.mnt_mntopts = ""; 29813126Sahl else 29823126Sahl mnt.mnt_mntopts = (char *)options; 29833126Sahl 29843126Sahl if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 29853126Sahl zfs_is_mounted(zhp, NULL)) { 29863126Sahl if (!explicit) 29873126Sahl return (0); 29883126Sahl 2989789Sahrens (void) fprintf(stderr, gettext("cannot mount " 2990789Sahrens "'%s': filesystem already mounted\n"), 2991789Sahrens zfs_get_name(zhp)); 2992789Sahrens return (1); 2993789Sahrens } 29943126Sahl 29953126Sahl if (zfs_mount(zhp, options, flags) != 0) 29963126Sahl return (1); 29973126Sahl break; 2998789Sahrens } 29993126Sahl } else { 30003126Sahl assert(op == OP_SHARE); 30013126Sahl 30023126Sahl /* 30033126Sahl * Ignore any volumes that aren't shared. 30043126Sahl */ 30053126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, shareopts, 30063126Sahl sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 30073126Sahl 30083126Sahl if (strcmp(shareopts, "off") == 0) { 30093126Sahl if (!explicit) 30103126Sahl return (0); 30113126Sahl 30123126Sahl (void) fprintf(stderr, gettext("cannot share '%s': " 30133126Sahl "'shareiscsi' property not set\n"), 30143126Sahl zfs_get_name(zhp)); 30153126Sahl (void) fprintf(stderr, gettext("set 'shareiscsi' " 30163126Sahl "property or use iscsitadm(1M) to share this " 30173126Sahl "volume\n")); 30183126Sahl return (1); 3019789Sahrens } 30203126Sahl 30213126Sahl if (zfs_is_shared_iscsi(zhp)) { 30223126Sahl if (!explicit) 30233126Sahl return (0); 30243126Sahl 30253126Sahl (void) fprintf(stderr, gettext("cannot share " 30263126Sahl "'%s': volume already shared\n"), 30273126Sahl zfs_get_name(zhp)); 3028789Sahrens return (1); 30293126Sahl } 30303126Sahl 30313126Sahl if (zfs_share_iscsi(zhp) != 0) 30323126Sahl return (1); 3033789Sahrens } 3034789Sahrens 3035789Sahrens return (0); 3036789Sahrens } 3037789Sahrens 3038*4737Smmusante /* 3039*4737Smmusante * Reports progress in the form "(current/total)". Not thread-safe. 3040*4737Smmusante */ 3041*4737Smmusante static void 3042*4737Smmusante report_mount_progress(int current, int total) 3043*4737Smmusante { 3044*4737Smmusante static int len; 3045*4737Smmusante static char *reverse = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" 3046*4737Smmusante "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"; 3047*4737Smmusante static time_t last_progress_time; 3048*4737Smmusante time_t now = time(NULL); 3049*4737Smmusante 3050*4737Smmusante /* report 1..n instead of 0..n-1 */ 3051*4737Smmusante ++current; 3052*4737Smmusante 3053*4737Smmusante /* display header if we're here for the first time */ 3054*4737Smmusante if (current == 1) { 3055*4737Smmusante (void) printf(gettext("Mounting ZFS filesystems: ")); 3056*4737Smmusante len = 0; 3057*4737Smmusante } else if (current != total && last_progress_time + MOUNT_TIME >= now) 3058*4737Smmusante return; /* too soon to report again */ 3059*4737Smmusante 3060*4737Smmusante last_progress_time = now; 3061*4737Smmusante 3062*4737Smmusante /* back up to prepare for overwriting */ 3063*4737Smmusante if (len) 3064*4737Smmusante (void) printf("%*.*s", len, len, reverse); 3065*4737Smmusante 3066*4737Smmusante /* We put a newline at the end if this is the last one. */ 3067*4737Smmusante len = printf("(%d/%d)%s", current, total, current == total ? "\n" : ""); 3068*4737Smmusante (void) fflush(stdout); 3069*4737Smmusante } 3070*4737Smmusante 3071789Sahrens static int 30723126Sahl share_mount(int op, int argc, char **argv) 3073789Sahrens { 3074789Sahrens int do_all = 0; 3075*4737Smmusante boolean_t verbose = B_FALSE; 30762372Slling int c, ret = 0; 30773126Sahl const char *options = NULL; 30783126Sahl int types, flags = 0; 3079789Sahrens 3080789Sahrens /* check options */ 3081*4737Smmusante while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:O" : "a")) 3082789Sahrens != -1) { 3083789Sahrens switch (c) { 3084789Sahrens case 'a': 3085789Sahrens do_all = 1; 3086789Sahrens break; 3087*4737Smmusante case 'v': 3088*4737Smmusante verbose = B_TRUE; 3089*4737Smmusante break; 3090789Sahrens case 'o': 30914717Srm160521 if (strlen(optarg) <= MNT_LINE_MAX) { 30924717Srm160521 options = optarg; 30934717Srm160521 break; 30944717Srm160521 } 30954717Srm160521 (void) fprintf(stderr, gettext("the opts argument for " 30964717Srm160521 "'%c' option is too long (more than %d chars)\n"), 30974717Srm160521 optopt, MNT_LINE_MAX); 30984717Srm160521 usage(B_FALSE); 3099789Sahrens break; 31004717Srm160521 3101789Sahrens case 'O': 31023126Sahl flags |= MS_OVERLAY; 3103789Sahrens break; 3104789Sahrens case ':': 3105789Sahrens (void) fprintf(stderr, gettext("missing argument for " 3106789Sahrens "'%c' option\n"), optopt); 31072082Seschrock usage(B_FALSE); 3108789Sahrens break; 3109789Sahrens case '?': 3110789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3111789Sahrens optopt); 31122082Seschrock usage(B_FALSE); 3113789Sahrens } 3114789Sahrens } 3115789Sahrens 3116789Sahrens argc -= optind; 3117789Sahrens argv += optind; 3118789Sahrens 3119789Sahrens /* check number of arguments */ 3120789Sahrens if (do_all) { 31213126Sahl zfs_handle_t **dslist = NULL; 31221356Seschrock size_t i, count = 0; 31231356Seschrock 31243126Sahl if (op == OP_MOUNT) { 31253126Sahl types = ZFS_TYPE_FILESYSTEM; 31263126Sahl } else if (argc > 0) { 31273126Sahl if (strcmp(argv[0], "nfs") == 0) { 31283126Sahl types = ZFS_TYPE_FILESYSTEM; 31293126Sahl } else if (strcmp(argv[0], "iscsi") == 0) { 31303126Sahl types = ZFS_TYPE_VOLUME; 31313126Sahl } else { 31323126Sahl (void) fprintf(stderr, gettext("share type " 31333126Sahl "must be 'nfs' or 'iscsi'\n")); 31343126Sahl usage(B_FALSE); 31353126Sahl } 31363126Sahl 31373126Sahl argc--; 31383126Sahl argv++; 31393126Sahl } else { 31403126Sahl types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME; 31413126Sahl } 31423126Sahl 3143789Sahrens if (argc != 0) { 3144789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 31452082Seschrock usage(B_FALSE); 3146789Sahrens } 3147789Sahrens 3148*4737Smmusante get_all_datasets(types, &dslist, &count, verbose); 31491356Seschrock 31501356Seschrock if (count == 0) 31511356Seschrock return (0); 31521356Seschrock 31533126Sahl qsort(dslist, count, sizeof (void *), dataset_cmp); 31541356Seschrock 31551356Seschrock for (i = 0; i < count; i++) { 3156*4737Smmusante if (verbose) 3157*4737Smmusante report_mount_progress(i, count); 3158*4737Smmusante 31593126Sahl if (share_mount_one(dslist[i], op, flags, B_FALSE, 31603126Sahl options) != 0) 31612369Slling ret = 1; 31623126Sahl zfs_close(dslist[i]); 31631356Seschrock } 31641356Seschrock 31653126Sahl free(dslist); 3166789Sahrens } else if (argc == 0) { 3167789Sahrens struct mnttab entry; 3168789Sahrens 31694717Srm160521 if ((op == OP_SHARE) || (options != NULL)) { 3170789Sahrens (void) fprintf(stderr, gettext("missing filesystem " 31714717Srm160521 "argument (specify -a for all)\n")); 31722082Seschrock usage(B_FALSE); 3173789Sahrens } 3174789Sahrens 3175789Sahrens /* 3176789Sahrens * When mount is given no arguments, go through /etc/mnttab and 3177789Sahrens * display any active ZFS mounts. We hide any snapshots, since 3178789Sahrens * they are controlled automatically. 3179789Sahrens */ 3180789Sahrens rewind(mnttab_file); 3181789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 3182789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 3183789Sahrens strchr(entry.mnt_special, '@') != NULL) 3184789Sahrens continue; 3185789Sahrens 3186789Sahrens (void) printf("%-30s %s\n", entry.mnt_special, 3187789Sahrens entry.mnt_mountp); 3188789Sahrens } 3189789Sahrens 3190789Sahrens } else { 3191789Sahrens zfs_handle_t *zhp; 3192789Sahrens 31933126Sahl types = ZFS_TYPE_FILESYSTEM; 31943126Sahl if (op == OP_SHARE) 31953126Sahl types |= ZFS_TYPE_VOLUME; 31963126Sahl 3197789Sahrens if (argc > 1) { 3198789Sahrens (void) fprintf(stderr, 3199789Sahrens gettext("too many arguments\n")); 32002082Seschrock usage(B_FALSE); 3201789Sahrens } 3202789Sahrens 32033126Sahl if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) { 3204789Sahrens ret = 1; 32053126Sahl } else { 32063126Sahl ret = share_mount_one(zhp, op, flags, B_TRUE, 32073126Sahl options); 3208789Sahrens zfs_close(zhp); 3209789Sahrens } 3210789Sahrens } 3211789Sahrens 3212789Sahrens return (ret); 3213789Sahrens } 3214789Sahrens 3215789Sahrens /* 32163126Sahl * zfs mount -a [nfs | iscsi] 3217789Sahrens * zfs mount filesystem 3218789Sahrens * 3219789Sahrens * Mount all filesystems, or mount the given filesystem. 3220789Sahrens */ 3221789Sahrens static int 3222789Sahrens zfs_do_mount(int argc, char **argv) 3223789Sahrens { 32243126Sahl return (share_mount(OP_MOUNT, argc, argv)); 3225789Sahrens } 3226789Sahrens 3227789Sahrens /* 32283126Sahl * zfs share -a [nfs | iscsi] 3229789Sahrens * zfs share filesystem 3230789Sahrens * 3231789Sahrens * Share all filesystems, or share the given filesystem. 3232789Sahrens */ 3233789Sahrens static int 3234789Sahrens zfs_do_share(int argc, char **argv) 3235789Sahrens { 32363126Sahl return (share_mount(OP_SHARE, argc, argv)); 3237789Sahrens } 3238789Sahrens 3239789Sahrens typedef struct unshare_unmount_node { 3240789Sahrens zfs_handle_t *un_zhp; 3241789Sahrens char *un_mountp; 3242789Sahrens uu_avl_node_t un_avlnode; 3243789Sahrens } unshare_unmount_node_t; 3244789Sahrens 3245789Sahrens /* ARGSUSED */ 3246789Sahrens static int 3247789Sahrens unshare_unmount_compare(const void *larg, const void *rarg, void *unused) 3248789Sahrens { 3249789Sahrens const unshare_unmount_node_t *l = larg; 3250789Sahrens const unshare_unmount_node_t *r = rarg; 3251789Sahrens 3252789Sahrens return (strcmp(l->un_mountp, r->un_mountp)); 3253789Sahrens } 3254789Sahrens 3255789Sahrens /* 3256789Sahrens * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 3257789Sahrens * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 3258789Sahrens * and unmount it appropriately. 3259789Sahrens */ 3260789Sahrens static int 32613126Sahl unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) 3262789Sahrens { 3263789Sahrens zfs_handle_t *zhp; 3264789Sahrens int ret; 3265789Sahrens struct stat64 statbuf; 3266789Sahrens struct extmnttab entry; 32673126Sahl const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount"; 3268789Sahrens char property[ZFS_MAXPROPLEN]; 3269789Sahrens 3270789Sahrens /* 3271789Sahrens * Search for the path in /etc/mnttab. Rather than looking for the 3272789Sahrens * specific path, which can be fooled by non-standard paths (i.e. ".." 3273789Sahrens * or "//"), we stat() the path and search for the corresponding 3274789Sahrens * (major,minor) device pair. 3275789Sahrens */ 3276789Sahrens if (stat64(path, &statbuf) != 0) { 3277789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 3278789Sahrens cmdname, path, strerror(errno)); 3279789Sahrens return (1); 3280789Sahrens } 3281789Sahrens 3282789Sahrens /* 3283789Sahrens * Search for the given (major,minor) pair in the mount table. 3284789Sahrens */ 3285789Sahrens rewind(mnttab_file); 3286789Sahrens while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 3287789Sahrens if (entry.mnt_major == major(statbuf.st_dev) && 3288789Sahrens entry.mnt_minor == minor(statbuf.st_dev)) 3289789Sahrens break; 3290789Sahrens } 3291789Sahrens if (ret != 0) { 3292789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not " 3293789Sahrens "currently mounted\n"), cmdname, path); 3294789Sahrens return (1); 3295789Sahrens } 3296789Sahrens 3297789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 3298789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 3299789Sahrens "filesystem\n"), cmdname, path); 3300789Sahrens return (1); 3301789Sahrens } 3302789Sahrens 33032082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 33042082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 3305789Sahrens return (1); 3306789Sahrens 33073126Sahl verify(zfs_prop_get(zhp, op == OP_SHARE ? 33083912Slling ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 33093912Slling sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 3310789Sahrens 33113126Sahl if (op == OP_SHARE) { 3312789Sahrens if (strcmp(property, "off") == 0) { 3313789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 3314789Sahrens "'%s': legacy share\n"), path); 3315789Sahrens (void) fprintf(stderr, gettext("use " 3316789Sahrens "unshare(1M) to unshare this filesystem\n")); 3317789Sahrens ret = 1; 33183126Sahl } else if (!zfs_is_shared_nfs(zhp, NULL)) { 3319789Sahrens (void) fprintf(stderr, gettext("cannot unshare '%s': " 3320789Sahrens "not currently shared\n"), path); 3321789Sahrens ret = 1; 3322789Sahrens } else { 33233126Sahl ret = zfs_unshareall_nfs(zhp); 3324789Sahrens } 3325789Sahrens } else { 33261264Slling if (is_manual) { 33271264Slling ret = zfs_unmount(zhp, NULL, flags); 33281264Slling } else if (strcmp(property, "legacy") == 0) { 33291264Slling (void) fprintf(stderr, gettext("cannot unmount " 33301264Slling "'%s': legacy mountpoint\n"), 33311264Slling zfs_get_name(zhp)); 33321264Slling (void) fprintf(stderr, gettext("use umount(1M) " 33331264Slling "to unmount this filesystem\n")); 33341264Slling ret = 1; 3335789Sahrens } else { 3336789Sahrens ret = zfs_unmountall(zhp, flags); 3337789Sahrens } 3338789Sahrens } 3339789Sahrens 3340789Sahrens zfs_close(zhp); 3341789Sahrens 3342789Sahrens return (ret != 0); 3343789Sahrens } 3344789Sahrens 3345789Sahrens /* 3346789Sahrens * Generic callback for unsharing or unmounting a filesystem. 3347789Sahrens */ 3348789Sahrens static int 33493126Sahl unshare_unmount(int op, int argc, char **argv) 3350789Sahrens { 3351789Sahrens int do_all = 0; 3352789Sahrens int flags = 0; 3353789Sahrens int ret = 0; 33543126Sahl int types, c; 3355789Sahrens zfs_handle_t *zhp; 3356789Sahrens char property[ZFS_MAXPROPLEN]; 3357789Sahrens 3358789Sahrens /* check options */ 33593126Sahl while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) { 3360789Sahrens switch (c) { 3361789Sahrens case 'a': 3362789Sahrens do_all = 1; 3363789Sahrens break; 3364789Sahrens case 'f': 3365789Sahrens flags = MS_FORCE; 3366789Sahrens break; 3367789Sahrens case '?': 3368789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3369789Sahrens optopt); 33702082Seschrock usage(B_FALSE); 3371789Sahrens } 3372789Sahrens } 3373789Sahrens 3374789Sahrens argc -= optind; 3375789Sahrens argv += optind; 3376789Sahrens 3377789Sahrens if (do_all) { 3378789Sahrens /* 3379789Sahrens * We could make use of zfs_for_each() to walk all datasets in 3380789Sahrens * the system, but this would be very inefficient, especially 3381789Sahrens * since we would have to linearly search /etc/mnttab for each 3382789Sahrens * one. Instead, do one pass through /etc/mnttab looking for 3383789Sahrens * zfs entries and call zfs_unmount() for each one. 3384789Sahrens * 3385789Sahrens * Things get a little tricky if the administrator has created 3386789Sahrens * mountpoints beneath other ZFS filesystems. In this case, we 3387789Sahrens * have to unmount the deepest filesystems first. To accomplish 3388789Sahrens * this, we place all the mountpoints in an AVL tree sorted by 3389789Sahrens * the special type (dataset name), and walk the result in 3390789Sahrens * reverse to make sure to get any snapshots first. 3391789Sahrens */ 3392789Sahrens struct mnttab entry; 3393789Sahrens uu_avl_pool_t *pool; 3394789Sahrens uu_avl_t *tree; 3395789Sahrens unshare_unmount_node_t *node; 3396789Sahrens uu_avl_index_t idx; 3397789Sahrens uu_avl_walk_t *walk; 3398789Sahrens 33993126Sahl if (argc != 0) { 34003126Sahl (void) fprintf(stderr, gettext("too many arguments\n")); 34013126Sahl usage(B_FALSE); 34023126Sahl } 34033126Sahl 3404789Sahrens if ((pool = uu_avl_pool_create("unmount_pool", 3405789Sahrens sizeof (unshare_unmount_node_t), 3406789Sahrens offsetof(unshare_unmount_node_t, un_avlnode), 3407789Sahrens unshare_unmount_compare, 3408789Sahrens UU_DEFAULT)) == NULL) { 3409789Sahrens (void) fprintf(stderr, gettext("internal error: " 3410789Sahrens "out of memory\n")); 3411789Sahrens exit(1); 3412789Sahrens } 3413789Sahrens 3414789Sahrens if ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL) { 3415789Sahrens (void) fprintf(stderr, gettext("internal error: " 3416789Sahrens "out of memory\n")); 3417789Sahrens exit(1); 3418789Sahrens } 3419789Sahrens 3420789Sahrens rewind(mnttab_file); 3421789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 3422789Sahrens 3423789Sahrens /* ignore non-ZFS entries */ 3424789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 3425789Sahrens continue; 3426789Sahrens 3427789Sahrens /* ignore snapshots */ 3428789Sahrens if (strchr(entry.mnt_special, '@') != NULL) 3429789Sahrens continue; 3430789Sahrens 34312082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 3432789Sahrens ZFS_TYPE_FILESYSTEM)) == NULL) { 3433789Sahrens ret = 1; 3434789Sahrens continue; 3435789Sahrens } 3436789Sahrens 34373126Sahl verify(zfs_prop_get(zhp, op == OP_SHARE ? 3438789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 3439789Sahrens property, sizeof (property), NULL, NULL, 34402082Seschrock 0, B_FALSE) == 0); 3441789Sahrens 3442789Sahrens /* Ignore legacy mounts and shares */ 34433126Sahl if ((op == OP_SHARE && 3444789Sahrens strcmp(property, "off") == 0) || 34453126Sahl (op == OP_MOUNT && 3446789Sahrens strcmp(property, "legacy") == 0)) { 3447789Sahrens zfs_close(zhp); 3448789Sahrens continue; 3449789Sahrens } 3450789Sahrens 3451789Sahrens node = safe_malloc(sizeof (unshare_unmount_node_t)); 3452789Sahrens node->un_zhp = zhp; 3453789Sahrens 3454789Sahrens if ((node->un_mountp = strdup(entry.mnt_mountp)) == 3455789Sahrens NULL) { 3456789Sahrens (void) fprintf(stderr, gettext("internal error:" 3457789Sahrens " out of memory\n")); 3458789Sahrens exit(1); 3459789Sahrens } 3460789Sahrens 3461789Sahrens uu_avl_node_init(node, &node->un_avlnode, pool); 3462789Sahrens 3463789Sahrens if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 3464789Sahrens uu_avl_insert(tree, node, idx); 3465789Sahrens } else { 3466789Sahrens zfs_close(node->un_zhp); 3467789Sahrens free(node->un_mountp); 3468789Sahrens free(node); 3469789Sahrens } 3470789Sahrens } 3471789Sahrens 3472789Sahrens /* 3473789Sahrens * Walk the AVL tree in reverse, unmounting each filesystem and 3474789Sahrens * removing it from the AVL tree in the process. 3475789Sahrens */ 3476789Sahrens if ((walk = uu_avl_walk_start(tree, 3477789Sahrens UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) { 3478789Sahrens (void) fprintf(stderr, 3479789Sahrens gettext("internal error: out of memory")); 3480789Sahrens exit(1); 3481789Sahrens } 3482789Sahrens 3483789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 3484789Sahrens uu_avl_remove(tree, node); 3485789Sahrens 34863126Sahl switch (op) { 3487789Sahrens case OP_SHARE: 34883126Sahl if (zfs_unshare_nfs(node->un_zhp, 3489789Sahrens node->un_mountp) != 0) 3490789Sahrens ret = 1; 3491789Sahrens break; 3492789Sahrens 3493789Sahrens case OP_MOUNT: 3494789Sahrens if (zfs_unmount(node->un_zhp, 3495789Sahrens node->un_mountp, flags) != 0) 3496789Sahrens ret = 1; 3497789Sahrens break; 3498789Sahrens } 3499789Sahrens 3500789Sahrens zfs_close(node->un_zhp); 3501789Sahrens free(node->un_mountp); 3502789Sahrens free(node); 3503789Sahrens } 3504789Sahrens 3505789Sahrens uu_avl_walk_end(walk); 3506789Sahrens uu_avl_destroy(tree); 3507789Sahrens uu_avl_pool_destroy(pool); 35083126Sahl 35093126Sahl if (op == OP_SHARE) { 35103126Sahl /* 35113126Sahl * Finally, unshare any volumes shared via iSCSI. 35123126Sahl */ 35133126Sahl zfs_handle_t **dslist = NULL; 35143126Sahl size_t i, count = 0; 35153126Sahl 3516*4737Smmusante get_all_datasets(ZFS_TYPE_VOLUME, &dslist, &count, 3517*4737Smmusante B_FALSE); 35183126Sahl 35193126Sahl if (count != 0) { 35203126Sahl qsort(dslist, count, sizeof (void *), 35213126Sahl dataset_cmp); 35223126Sahl 35233126Sahl for (i = 0; i < count; i++) { 35243126Sahl if (zfs_unshare_iscsi(dslist[i]) != 0) 35253126Sahl ret = 1; 35263126Sahl zfs_close(dslist[i]); 35273126Sahl } 35283126Sahl 35293126Sahl free(dslist); 35303126Sahl } 35313126Sahl } 3532789Sahrens } else { 35333126Sahl if (argc != 1) { 35343126Sahl if (argc == 0) 35353126Sahl (void) fprintf(stderr, 35363126Sahl gettext("missing filesystem argument\n")); 35373126Sahl else 35383126Sahl (void) fprintf(stderr, 35393126Sahl gettext("too many arguments\n")); 35403126Sahl usage(B_FALSE); 35413126Sahl } 35423126Sahl 3543789Sahrens /* 3544789Sahrens * We have an argument, but it may be a full path or a ZFS 3545789Sahrens * filesystem. Pass full paths off to unmount_path() (shared by 3546789Sahrens * manual_unmount), otherwise open the filesystem and pass to 3547789Sahrens * zfs_unmount(). 3548789Sahrens */ 3549789Sahrens if (argv[0][0] == '/') 35503126Sahl return (unshare_unmount_path(op, argv[0], 35513912Slling flags, B_FALSE)); 35522082Seschrock 35533126Sahl types = ZFS_TYPE_FILESYSTEM; 35543126Sahl if (op == OP_SHARE) 35553126Sahl types |= ZFS_TYPE_VOLUME; 35563126Sahl 35573126Sahl if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) 3558789Sahrens return (1); 3559789Sahrens 35603126Sahl if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 35613126Sahl verify(zfs_prop_get(zhp, op == OP_SHARE ? 35623126Sahl ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 35633126Sahl sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 35643126Sahl 35653126Sahl switch (op) { 35663126Sahl case OP_SHARE: 35673126Sahl if (strcmp(property, "off") == 0) { 35683126Sahl (void) fprintf(stderr, gettext("cannot " 35693126Sahl "unshare '%s': legacy share\n"), 35703126Sahl zfs_get_name(zhp)); 35713126Sahl (void) fprintf(stderr, gettext("use " 35723126Sahl "unshare(1M) to unshare this " 35733126Sahl "filesystem\n")); 35743126Sahl ret = 1; 35753126Sahl } else if (!zfs_is_shared_nfs(zhp, NULL)) { 35763126Sahl (void) fprintf(stderr, gettext("cannot " 35773126Sahl "unshare '%s': not currently " 35783126Sahl "shared\n"), zfs_get_name(zhp)); 35793126Sahl ret = 1; 35803126Sahl } else if (zfs_unshareall_nfs(zhp) != 0) { 35813126Sahl ret = 1; 35823126Sahl } 35833126Sahl break; 35843126Sahl 35853126Sahl case OP_MOUNT: 35863126Sahl if (strcmp(property, "legacy") == 0) { 35873126Sahl (void) fprintf(stderr, gettext("cannot " 35883126Sahl "unmount '%s': legacy " 35893126Sahl "mountpoint\n"), zfs_get_name(zhp)); 35903126Sahl (void) fprintf(stderr, gettext("use " 35913126Sahl "umount(1M) to unmount this " 35923126Sahl "filesystem\n")); 35933126Sahl ret = 1; 35943126Sahl } else if (!zfs_is_mounted(zhp, NULL)) { 35953126Sahl (void) fprintf(stderr, gettext("cannot " 35963126Sahl "unmount '%s': not currently " 35973126Sahl "mounted\n"), 35983126Sahl zfs_get_name(zhp)); 35993126Sahl ret = 1; 36003126Sahl } else if (zfs_unmountall(zhp, flags) != 0) { 36013126Sahl ret = 1; 36023126Sahl } 36033126Sahl break; 36043126Sahl } 36053126Sahl } else { 36063126Sahl assert(op == OP_SHARE); 36073126Sahl 36083126Sahl verify(zfs_prop_get(zhp, ZFS_PROP_SHAREISCSI, property, 36093126Sahl sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 36103126Sahl 3611789Sahrens if (strcmp(property, "off") == 0) { 3612789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 36133126Sahl "'%s': 'shareiscsi' property not set\n"), 36143126Sahl zfs_get_name(zhp)); 36153126Sahl (void) fprintf(stderr, gettext("set " 36163126Sahl "'shareiscsi' property or use " 36173126Sahl "iscsitadm(1M) to share this volume\n")); 3618789Sahrens ret = 1; 36193126Sahl } else if (!zfs_is_shared_iscsi(zhp)) { 36203126Sahl (void) fprintf(stderr, gettext("cannot " 36213126Sahl "unshare '%s': not currently shared\n"), 3622789Sahrens zfs_get_name(zhp)); 3623789Sahrens ret = 1; 36243126Sahl } else if (zfs_unshare_iscsi(zhp) != 0) { 3625789Sahrens ret = 1; 3626789Sahrens } 3627789Sahrens } 3628789Sahrens 3629789Sahrens zfs_close(zhp); 3630789Sahrens } 3631789Sahrens 3632789Sahrens return (ret); 3633789Sahrens } 3634789Sahrens 3635789Sahrens /* 3636789Sahrens * zfs unmount -a 3637789Sahrens * zfs unmount filesystem 3638789Sahrens * 3639789Sahrens * Unmount all filesystems, or a specific ZFS filesystem. 3640789Sahrens */ 3641789Sahrens static int 3642789Sahrens zfs_do_unmount(int argc, char **argv) 3643789Sahrens { 3644789Sahrens return (unshare_unmount(OP_MOUNT, argc, argv)); 3645789Sahrens } 3646789Sahrens 3647789Sahrens /* 3648789Sahrens * zfs unshare -a 3649789Sahrens * zfs unshare filesystem 3650789Sahrens * 3651789Sahrens * Unshare all filesystems, or a specific ZFS filesystem. 3652789Sahrens */ 3653789Sahrens static int 3654789Sahrens zfs_do_unshare(int argc, char **argv) 3655789Sahrens { 3656789Sahrens return (unshare_unmount(OP_SHARE, argc, argv)); 3657789Sahrens } 3658789Sahrens 3659789Sahrens /* 3660789Sahrens * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 3661789Sahrens * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 3662789Sahrens */ 3663789Sahrens static int 3664789Sahrens manual_mount(int argc, char **argv) 3665789Sahrens { 3666789Sahrens zfs_handle_t *zhp; 3667789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 3668789Sahrens char mntopts[MNT_LINE_MAX] = { '\0' }; 3669789Sahrens int ret; 3670789Sahrens int c; 3671789Sahrens int flags = 0; 3672789Sahrens char *dataset, *path; 3673789Sahrens 3674789Sahrens /* check options */ 36751544Seschrock while ((c = getopt(argc, argv, ":mo:O")) != -1) { 3676789Sahrens switch (c) { 3677789Sahrens case 'o': 3678789Sahrens (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 3679789Sahrens break; 3680789Sahrens case 'O': 3681789Sahrens flags |= MS_OVERLAY; 3682789Sahrens break; 36831544Seschrock case 'm': 36841544Seschrock flags |= MS_NOMNTTAB; 36851544Seschrock break; 3686789Sahrens case ':': 3687789Sahrens (void) fprintf(stderr, gettext("missing argument for " 3688789Sahrens "'%c' option\n"), optopt); 36892082Seschrock usage(B_FALSE); 3690789Sahrens break; 3691789Sahrens case '?': 3692789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3693789Sahrens optopt); 3694789Sahrens (void) fprintf(stderr, gettext("usage: mount [-o opts] " 3695789Sahrens "<path>\n")); 3696789Sahrens return (2); 3697789Sahrens } 3698789Sahrens } 3699789Sahrens 3700789Sahrens argc -= optind; 3701789Sahrens argv += optind; 3702789Sahrens 3703789Sahrens /* check that we only have two arguments */ 3704789Sahrens if (argc != 2) { 3705789Sahrens if (argc == 0) 3706789Sahrens (void) fprintf(stderr, gettext("missing dataset " 3707789Sahrens "argument\n")); 3708789Sahrens else if (argc == 1) 3709789Sahrens (void) fprintf(stderr, 3710789Sahrens gettext("missing mountpoint argument\n")); 3711789Sahrens else 3712789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 3713789Sahrens (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 3714789Sahrens return (2); 3715789Sahrens } 3716789Sahrens 3717789Sahrens dataset = argv[0]; 3718789Sahrens path = argv[1]; 3719789Sahrens 3720789Sahrens /* try to open the dataset */ 37212082Seschrock if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 3722789Sahrens return (1); 3723789Sahrens 3724789Sahrens (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 37252082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE); 3726789Sahrens 3727789Sahrens /* check for legacy mountpoint and complain appropriately */ 3728789Sahrens ret = 0; 3729789Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 3730789Sahrens if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS, 3731789Sahrens NULL, 0, mntopts, sizeof (mntopts)) != 0) { 3732789Sahrens (void) fprintf(stderr, gettext("mount failed: %s\n"), 3733789Sahrens strerror(errno)); 3734789Sahrens ret = 1; 3735789Sahrens } 3736789Sahrens } else { 3737789Sahrens (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 3738789Sahrens "mounted using 'mount -F zfs'\n"), dataset); 3739789Sahrens (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 3740789Sahrens "instead.\n"), path); 3741789Sahrens (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' " 3742789Sahrens "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n")); 3743789Sahrens (void) fprintf(stderr, gettext("See zfs(1M) for more " 3744789Sahrens "information.\n")); 3745789Sahrens ret = 1; 3746789Sahrens } 3747789Sahrens 3748789Sahrens return (ret); 3749789Sahrens } 3750789Sahrens 3751789Sahrens /* 3752789Sahrens * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 3753789Sahrens * unmounts of non-legacy filesystems, as this is the dominant administrative 3754789Sahrens * interface. 3755789Sahrens */ 3756789Sahrens static int 3757789Sahrens manual_unmount(int argc, char **argv) 3758789Sahrens { 3759789Sahrens int flags = 0; 3760789Sahrens int c; 3761789Sahrens 3762789Sahrens /* check options */ 3763789Sahrens while ((c = getopt(argc, argv, "f")) != -1) { 3764789Sahrens switch (c) { 3765789Sahrens case 'f': 3766789Sahrens flags = MS_FORCE; 3767789Sahrens break; 3768789Sahrens case '?': 3769789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3770789Sahrens optopt); 3771789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] " 3772789Sahrens "<path>\n")); 3773789Sahrens return (2); 3774789Sahrens } 3775789Sahrens } 3776789Sahrens 3777789Sahrens argc -= optind; 3778789Sahrens argv += optind; 3779789Sahrens 3780789Sahrens /* check arguments */ 3781789Sahrens if (argc != 1) { 3782789Sahrens if (argc == 0) 3783789Sahrens (void) fprintf(stderr, gettext("missing path " 3784789Sahrens "argument\n")); 3785789Sahrens else 3786789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 3787789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 3788789Sahrens return (2); 3789789Sahrens } 3790789Sahrens 37912082Seschrock return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE)); 3792789Sahrens } 3793789Sahrens 3794789Sahrens static int 3795789Sahrens volcheck(zpool_handle_t *zhp, void *data) 3796789Sahrens { 37972856Snd150628 boolean_t isinit = *((boolean_t *)data); 3798789Sahrens 3799789Sahrens if (isinit) 3800789Sahrens return (zpool_create_zvol_links(zhp)); 3801789Sahrens else 3802789Sahrens return (zpool_remove_zvol_links(zhp)); 3803789Sahrens } 3804789Sahrens 3805789Sahrens /* 3806789Sahrens * Iterate over all pools in the system and either create or destroy /dev/zvol 3807789Sahrens * links, depending on the value of 'isinit'. 3808789Sahrens */ 3809789Sahrens static int 38102082Seschrock do_volcheck(boolean_t isinit) 3811789Sahrens { 38122856Snd150628 return (zpool_iter(g_zfs, volcheck, &isinit) ? 1 : 0); 3813789Sahrens } 3814789Sahrens 38154543Smarks static int 38164543Smarks find_command_idx(char *command, int *idx) 38174543Smarks { 38184543Smarks int i; 38194543Smarks 38204543Smarks for (i = 0; i < NCOMMAND; i++) { 38214543Smarks if (command_table[i].name == NULL) 38224543Smarks continue; 38234543Smarks 38244543Smarks if (strcmp(command, command_table[i].name) == 0) { 38254543Smarks *idx = i; 38264543Smarks return (0); 38274543Smarks } 38284543Smarks } 38294543Smarks return (1); 38304543Smarks } 38314543Smarks 38324543Smarks zfs_prop_t 38334543Smarks propset_cb(zfs_prop_t prop, void *data) 38344543Smarks { 38354543Smarks char *cmdname = (char *)data; 38364543Smarks 38374543Smarks if (strcmp(cmdname, zfs_prop_to_name(prop)) == 0) 38384543Smarks return (prop); 38394543Smarks 38404543Smarks return (ZFS_PROP_CONT); 38414543Smarks } 38424543Smarks 3843789Sahrens int 3844789Sahrens main(int argc, char **argv) 3845789Sahrens { 3846789Sahrens int ret; 3847789Sahrens int i; 3848789Sahrens char *progname; 3849789Sahrens char *cmdname; 38504543Smarks char *str; 38514543Smarks boolean_t found = B_FALSE; 3852789Sahrens 3853789Sahrens (void) setlocale(LC_ALL, ""); 3854789Sahrens (void) textdomain(TEXT_DOMAIN); 3855789Sahrens 3856789Sahrens opterr = 0; 3857789Sahrens 38584577Sahrens first_argc = argc; 38594577Sahrens first_argv = argv; 38604577Sahrens 38612082Seschrock if ((g_zfs = libzfs_init()) == NULL) { 38622082Seschrock (void) fprintf(stderr, gettext("internal error: failed to " 38632082Seschrock "initialize ZFS library\n")); 38642082Seschrock return (1); 38652082Seschrock } 38662082Seschrock 38674715Sek110237 zpool_stage_history(g_zfs, argc, argv, B_TRUE); 38684543Smarks 38692082Seschrock libzfs_print_on_error(g_zfs, B_TRUE); 38702082Seschrock 3871789Sahrens if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 3872789Sahrens (void) fprintf(stderr, gettext("internal error: unable to " 3873789Sahrens "open %s\n"), MNTTAB); 3874789Sahrens return (1); 3875789Sahrens } 3876789Sahrens 3877789Sahrens /* 3878789Sahrens * This command also doubles as the /etc/fs mount and unmount program. 3879789Sahrens * Determine if we should take this behavior based on argv[0]. 3880789Sahrens */ 3881789Sahrens progname = basename(argv[0]); 3882789Sahrens if (strcmp(progname, "mount") == 0) { 3883789Sahrens ret = manual_mount(argc, argv); 3884789Sahrens } else if (strcmp(progname, "umount") == 0) { 3885789Sahrens ret = manual_unmount(argc, argv); 3886789Sahrens } else { 3887789Sahrens /* 3888789Sahrens * Make sure the user has specified some command. 3889789Sahrens */ 3890789Sahrens if (argc < 2) { 3891789Sahrens (void) fprintf(stderr, gettext("missing command\n")); 38922082Seschrock usage(B_FALSE); 3893789Sahrens } 3894789Sahrens 3895789Sahrens cmdname = argv[1]; 3896789Sahrens 3897789Sahrens /* 3898789Sahrens * The 'umount' command is an alias for 'unmount' 3899789Sahrens */ 3900789Sahrens if (strcmp(cmdname, "umount") == 0) 3901789Sahrens cmdname = "unmount"; 3902789Sahrens 3903789Sahrens /* 39041749Sahrens * The 'recv' command is an alias for 'receive' 39051749Sahrens */ 39061749Sahrens if (strcmp(cmdname, "recv") == 0) 39071749Sahrens cmdname = "receive"; 39081749Sahrens 39091749Sahrens /* 3910789Sahrens * Special case '-?' 3911789Sahrens */ 3912789Sahrens if (strcmp(cmdname, "-?") == 0) 39132082Seschrock usage(B_TRUE); 3914789Sahrens 3915789Sahrens /* 3916789Sahrens * 'volinit' and 'volfini' do not appear in the usage message, 3917789Sahrens * so we have to special case them here. 3918789Sahrens */ 3919789Sahrens if (strcmp(cmdname, "volinit") == 0) 39202082Seschrock return (do_volcheck(B_TRUE)); 3921789Sahrens else if (strcmp(cmdname, "volfini") == 0) 39222082Seschrock return (do_volcheck(B_FALSE)); 3923789Sahrens 3924789Sahrens /* 3925789Sahrens * Run the appropriate command. 3926789Sahrens */ 39274543Smarks if (find_command_idx(cmdname, &i) == 0) { 39284543Smarks current_command = &command_table[i]; 39294543Smarks ret = command_table[i].func(argc - 1, argv + 1); 39304543Smarks found = B_TRUE; 39314543Smarks } 39324543Smarks 39334543Smarks /* 39344543Smarks * Check and see if they are doing property=value 39354543Smarks */ 39364543Smarks if (found == B_FALSE && 39374543Smarks ((str = strchr(cmdname, '=')) != NULL)) { 39384543Smarks *str = '\0'; 39394543Smarks if (zfs_prop_iter(propset_cb, cmdname, 39404543Smarks B_FALSE) != ZFS_PROP_INVAL) 39414543Smarks found = B_TRUE; 39424543Smarks 39434543Smarks if (found == B_FALSE && zfs_prop_user(cmdname)) 39444543Smarks found = B_TRUE; 39454543Smarks 39464543Smarks if (found == B_TRUE && 39474543Smarks find_command_idx("set", &i) == 0) { 39484543Smarks *str = '='; 3949789Sahrens current_command = &command_table[i]; 39504543Smarks ret = command_table[i].func(argc, argv); 39514543Smarks } else { 39524543Smarks (void) fprintf(stderr, 39534543Smarks gettext("invalid property '%s'\n"), 39544543Smarks cmdname); 39554543Smarks found = B_TRUE; 39564543Smarks ret = 1; 3957789Sahrens } 39584543Smarks 3959789Sahrens } 3960789Sahrens 39614543Smarks if (found == B_FALSE) { 3962789Sahrens (void) fprintf(stderr, gettext("unrecognized " 3963789Sahrens "command '%s'\n"), cmdname); 39642082Seschrock usage(B_FALSE); 3965789Sahrens } 3966789Sahrens } 3967789Sahrens 3968789Sahrens (void) fclose(mnttab_file); 3969789Sahrens 39702082Seschrock libzfs_fini(g_zfs); 39712082Seschrock 3972789Sahrens /* 3973789Sahrens * The 'ZFS_ABORT' environment variable causes us to dump core on exit 3974789Sahrens * for the purposes of running ::findleaks. 3975789Sahrens */ 3976789Sahrens if (getenv("ZFS_ABORT") != NULL) { 3977789Sahrens (void) printf("dumping core by request\n"); 3978789Sahrens abort(); 3979789Sahrens } 3980789Sahrens 3981789Sahrens return (ret); 3982789Sahrens } 3983