1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 51544Seschrock * Common Development and Distribution License (the "License"). 61544Seschrock * You may not use this file except in compliance with the License. 7789Sahrens * 8789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9789Sahrens * or http://www.opensolaris.org/os/licensing. 10789Sahrens * See the License for the specific language governing permissions 11789Sahrens * and limitations under the License. 12789Sahrens * 13789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15789Sahrens * If applicable, add the following below this CDDL HEADER, with the 16789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18789Sahrens * 19789Sahrens * CDDL HEADER END 20789Sahrens */ 21789Sahrens /* 221204Slling * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23789Sahrens * Use is subject to license terms. 24789Sahrens */ 25789Sahrens 26789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 27789Sahrens 28789Sahrens #include <assert.h> 29789Sahrens #include <errno.h> 30789Sahrens #include <libgen.h> 31789Sahrens #include <libintl.h> 32789Sahrens #include <libuutil.h> 33789Sahrens #include <locale.h> 34789Sahrens #include <stddef.h> 35789Sahrens #include <stdio.h> 36789Sahrens #include <stdlib.h> 37789Sahrens #include <strings.h> 38789Sahrens #include <unistd.h> 39789Sahrens #include <fcntl.h> 40789Sahrens #include <zone.h> 41789Sahrens #include <sys/mkdev.h> 42789Sahrens #include <sys/mntent.h> 43789Sahrens #include <sys/mnttab.h> 44789Sahrens #include <sys/mount.h> 45789Sahrens #include <sys/stat.h> 46789Sahrens 47789Sahrens #include <libzfs.h> 48789Sahrens 49789Sahrens #include "zfs_iter.h" 502082Seschrock #include "zfs_util.h" 512082Seschrock 522082Seschrock libzfs_handle_t *g_zfs; 53789Sahrens 54789Sahrens static FILE *mnttab_file; 55789Sahrens 56789Sahrens static int zfs_do_clone(int argc, char **argv); 57789Sahrens static int zfs_do_create(int argc, char **argv); 58789Sahrens static int zfs_do_destroy(int argc, char **argv); 59789Sahrens static int zfs_do_get(int argc, char **argv); 60789Sahrens static int zfs_do_inherit(int argc, char **argv); 61789Sahrens static int zfs_do_list(int argc, char **argv); 62789Sahrens static int zfs_do_mount(int argc, char **argv); 63789Sahrens static int zfs_do_rename(int argc, char **argv); 64789Sahrens static int zfs_do_rollback(int argc, char **argv); 65789Sahrens static int zfs_do_set(int argc, char **argv); 66789Sahrens static int zfs_do_snapshot(int argc, char **argv); 67789Sahrens static int zfs_do_unmount(int argc, char **argv); 68789Sahrens static int zfs_do_share(int argc, char **argv); 69789Sahrens static int zfs_do_unshare(int argc, char **argv); 701749Sahrens static int zfs_do_send(int argc, char **argv); 711749Sahrens static int zfs_do_receive(int argc, char **argv); 722082Seschrock static int zfs_do_promote(int argc, char **argv); 73789Sahrens 74789Sahrens /* 75789Sahrens * These libumem hooks provide a reasonable set of defaults for the allocator's 76789Sahrens * debugging facilities. 77789Sahrens */ 78789Sahrens const char * 79789Sahrens _umem_debug_init() 80789Sahrens { 81789Sahrens return ("default,verbose"); /* $UMEM_DEBUG setting */ 82789Sahrens } 83789Sahrens 84789Sahrens const char * 85789Sahrens _umem_logging_init(void) 86789Sahrens { 87789Sahrens return ("fail,contents"); /* $UMEM_LOGGING setting */ 88789Sahrens } 89789Sahrens 901387Seschrock typedef enum { 911387Seschrock HELP_CLONE, 921387Seschrock HELP_CREATE, 931387Seschrock HELP_DESTROY, 941387Seschrock HELP_GET, 951387Seschrock HELP_INHERIT, 961387Seschrock HELP_LIST, 971387Seschrock HELP_MOUNT, 982082Seschrock HELP_PROMOTE, 991749Sahrens HELP_RECEIVE, 1001387Seschrock HELP_RENAME, 1011387Seschrock HELP_ROLLBACK, 1021749Sahrens HELP_SEND, 1031387Seschrock HELP_SET, 1041387Seschrock HELP_SHARE, 1051387Seschrock HELP_SNAPSHOT, 1061387Seschrock HELP_UNMOUNT, 1071387Seschrock HELP_UNSHARE 1081387Seschrock } zfs_help_t; 1091387Seschrock 110789Sahrens typedef struct zfs_command { 111789Sahrens const char *name; 112789Sahrens int (*func)(int argc, char **argv); 1131387Seschrock zfs_help_t usage; 114789Sahrens } zfs_command_t; 115789Sahrens 116789Sahrens /* 117789Sahrens * Master command table. Each ZFS command has a name, associated function, and 1181544Seschrock * usage message. The usage messages need to be internationalized, so we have 1191544Seschrock * to have a function to return the usage message based on a command index. 1201387Seschrock * 1211387Seschrock * These commands are organized according to how they are displayed in the usage 1221387Seschrock * message. An empty command (one with a NULL name) indicates an empty line in 1231387Seschrock * the generic usage message. 124789Sahrens */ 125789Sahrens static zfs_command_t command_table[] = { 1261387Seschrock { "create", zfs_do_create, HELP_CREATE }, 1271387Seschrock { "destroy", zfs_do_destroy, HELP_DESTROY }, 128789Sahrens { NULL }, 1291387Seschrock { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT }, 1301387Seschrock { "rollback", zfs_do_rollback, HELP_ROLLBACK }, 1311387Seschrock { "clone", zfs_do_clone, HELP_CLONE }, 1322082Seschrock { "promote", zfs_do_promote, HELP_PROMOTE }, 1331387Seschrock { "rename", zfs_do_rename, HELP_RENAME }, 134789Sahrens { NULL }, 1351387Seschrock { "list", zfs_do_list, HELP_LIST }, 136789Sahrens { NULL }, 1371387Seschrock { "set", zfs_do_set, HELP_SET }, 1381387Seschrock { "get", zfs_do_get, HELP_GET }, 1391387Seschrock { "inherit", zfs_do_inherit, HELP_INHERIT }, 140789Sahrens { NULL }, 1411387Seschrock { "mount", zfs_do_mount, HELP_MOUNT }, 142789Sahrens { NULL }, 1431387Seschrock { "unmount", zfs_do_unmount, HELP_UNMOUNT }, 144789Sahrens { NULL }, 1451387Seschrock { "share", zfs_do_share, HELP_SHARE }, 146789Sahrens { NULL }, 1471387Seschrock { "unshare", zfs_do_unshare, HELP_UNSHARE }, 148789Sahrens { NULL }, 1491749Sahrens { "send", zfs_do_send, HELP_SEND }, 1501749Sahrens { "receive", zfs_do_receive, HELP_RECEIVE }, 151789Sahrens }; 152789Sahrens 153789Sahrens #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) 154789Sahrens 155789Sahrens zfs_command_t *current_command; 156789Sahrens 1571387Seschrock static const char * 1581387Seschrock get_usage(zfs_help_t idx) 1591387Seschrock { 1601387Seschrock switch (idx) { 1611387Seschrock case HELP_CLONE: 1621387Seschrock return (gettext("\tclone <snapshot> <filesystem|volume>\n")); 1631387Seschrock case HELP_CREATE: 1641387Seschrock return (gettext("\tcreate <filesystem>\n" 1651387Seschrock "\tcreate [-s] [-b blocksize] -V <size> <volume>\n")); 1661387Seschrock case HELP_DESTROY: 1671387Seschrock return (gettext("\tdestroy [-rRf] " 1681387Seschrock "<filesystem|volume|snapshot>\n")); 1691387Seschrock case HELP_GET: 1701387Seschrock return (gettext("\tget [-rHp] [-o field[,field]...] " 1711387Seschrock "[-s source[,source]...]\n" 1721387Seschrock "\t <all | property[,property]...> " 1731387Seschrock "<filesystem|volume|snapshot> ...\n")); 1741387Seschrock case HELP_INHERIT: 1751387Seschrock return (gettext("\tinherit [-r] <property> " 1761387Seschrock "<filesystem|volume> ...\n")); 1771387Seschrock case HELP_LIST: 1781387Seschrock return (gettext("\tlist [-rH] [-o property[,property]...] " 1791387Seschrock "[-t type[,type]...]\n" 1801387Seschrock "\t [filesystem|volume|snapshot] ...\n")); 1811387Seschrock case HELP_MOUNT: 1821387Seschrock return (gettext("\tmount\n" 1831387Seschrock "\tmount [-o opts] [-O] -a\n" 1841387Seschrock "\tmount [-o opts] [-O] <filesystem>\n")); 1852082Seschrock case HELP_PROMOTE: 1862082Seschrock return (gettext("\tpromote <clone filesystem>\n")); 1871749Sahrens case HELP_RECEIVE: 1881749Sahrens return (gettext("\treceive [-vn] <filesystem|volume|snapshot>\n" 1891749Sahrens "\treceive [-vn] -d <filesystem>\n")); 1901387Seschrock case HELP_RENAME: 1911387Seschrock return (gettext("\trename <filesystem|volume|snapshot> " 1921387Seschrock "<filesystem|volume|snapshot>\n")); 1931387Seschrock case HELP_ROLLBACK: 1941387Seschrock return (gettext("\trollback [-rRf] <snapshot>\n")); 1951749Sahrens case HELP_SEND: 1961749Sahrens return (gettext("\tsend [-i <snapshot>] <snapshot>\n")); 1971387Seschrock case HELP_SET: 1981387Seschrock return (gettext("\tset <property=value> " 1991387Seschrock "<filesystem|volume> ...\n")); 2001387Seschrock case HELP_SHARE: 2011387Seschrock return (gettext("\tshare -a\n" 2021387Seschrock "\tshare <filesystem>\n")); 2031387Seschrock case HELP_SNAPSHOT: 204*2199Sahrens return (gettext("\tsnapshot [-r] " 205*2199Sahrens "<filesystem@name|volume@name>\n")); 2061387Seschrock case HELP_UNMOUNT: 2071387Seschrock return (gettext("\tunmount [-f] -a\n" 2081387Seschrock "\tunmount [-f] <filesystem|mountpoint>\n")); 2091387Seschrock case HELP_UNSHARE: 2101387Seschrock return (gettext("\tunshare [-f] -a\n" 2111387Seschrock "\tunshare [-f] <filesystem|mountpoint>\n")); 2121387Seschrock } 2131387Seschrock 2141387Seschrock abort(); 2151387Seschrock /* NOTREACHED */ 2161387Seschrock } 2171387Seschrock 218789Sahrens /* 219789Sahrens * Utility function to guarantee malloc() success. 220789Sahrens */ 221789Sahrens void * 222789Sahrens safe_malloc(size_t size) 223789Sahrens { 224789Sahrens void *data; 225789Sahrens 226789Sahrens if ((data = calloc(1, size)) == NULL) { 227789Sahrens (void) fprintf(stderr, "internal error: out of memory\n"); 228789Sahrens exit(1); 229789Sahrens } 230789Sahrens 231789Sahrens return (data); 232789Sahrens } 233789Sahrens 234789Sahrens /* 235789Sahrens * Display usage message. If we're inside a command, display only the usage for 236789Sahrens * that command. Otherwise, iterate over the entire command table and display 237789Sahrens * a complete usage message. 238789Sahrens */ 239789Sahrens static void 2402082Seschrock usage(boolean_t requested) 241789Sahrens { 242789Sahrens int i; 2432082Seschrock boolean_t show_properties = B_FALSE; 244789Sahrens FILE *fp = requested ? stdout : stderr; 245789Sahrens 246789Sahrens if (current_command == NULL) { 247789Sahrens 248789Sahrens (void) fprintf(fp, gettext("usage: zfs command args ...\n")); 249789Sahrens (void) fprintf(fp, 250789Sahrens gettext("where 'command' is one of the following:\n\n")); 251789Sahrens 252789Sahrens for (i = 0; i < NCOMMAND; i++) { 253789Sahrens if (command_table[i].name == NULL) 254789Sahrens (void) fprintf(fp, "\n"); 255789Sahrens else 256789Sahrens (void) fprintf(fp, "%s", 2571387Seschrock get_usage(command_table[i].usage)); 258789Sahrens } 259789Sahrens 260789Sahrens (void) fprintf(fp, gettext("\nEach dataset is of the form: " 261789Sahrens "pool/[dataset/]*dataset[@name]\n")); 262789Sahrens } else { 263789Sahrens (void) fprintf(fp, gettext("usage:\n")); 2641387Seschrock (void) fprintf(fp, "%s", get_usage(current_command->usage)); 265789Sahrens } 266789Sahrens 2672190Sdarrenm if (current_command != NULL && 2682190Sdarrenm (strcmp(current_command->name, "set") == 0 || 269789Sahrens strcmp(current_command->name, "get") == 0 || 270789Sahrens strcmp(current_command->name, "inherit") == 0 || 2712190Sdarrenm strcmp(current_command->name, "list") == 0)) 2722082Seschrock show_properties = B_TRUE; 273789Sahrens 274789Sahrens if (show_properties) { 275789Sahrens 276789Sahrens (void) fprintf(fp, 277789Sahrens gettext("\nThe following properties are supported:\n")); 278789Sahrens 279789Sahrens (void) fprintf(fp, "\n\t%-13s %s %s %s\n\n", 280789Sahrens "PROPERTY", "EDIT", "INHERIT", "VALUES"); 281789Sahrens 282789Sahrens for (i = 0; i < ZFS_NPROP_VISIBLE; i++) { 283789Sahrens (void) fprintf(fp, "\t%-13s ", zfs_prop_to_name(i)); 284789Sahrens 285789Sahrens if (zfs_prop_readonly(i)) 286789Sahrens (void) fprintf(fp, " NO "); 287789Sahrens else 288789Sahrens (void) fprintf(fp, " YES "); 289789Sahrens 290789Sahrens if (zfs_prop_inheritable(i)) 291789Sahrens (void) fprintf(fp, " YES "); 292789Sahrens else 293789Sahrens (void) fprintf(fp, " NO "); 294789Sahrens 295789Sahrens if (zfs_prop_values(i) == NULL) 296789Sahrens (void) fprintf(fp, "-\n"); 297789Sahrens else 298789Sahrens (void) fprintf(fp, "%s\n", zfs_prop_values(i)); 299789Sahrens } 300789Sahrens (void) fprintf(fp, gettext("\nSizes are specified in bytes " 301789Sahrens "with standard units such as K, M, G, etc.\n")); 3022190Sdarrenm } else { 3032190Sdarrenm /* 3042190Sdarrenm * TRANSLATION NOTE: 3052190Sdarrenm * "zfs set|get" must not be localised this is the 3062190Sdarrenm * command name and arguments. 3072190Sdarrenm */ 3082190Sdarrenm (void) fprintf(fp, 3092190Sdarrenm gettext("\nFor the property list, run: zfs set|get\n")); 310789Sahrens } 311789Sahrens 312789Sahrens exit(requested ? 0 : 2); 313789Sahrens } 314789Sahrens 315789Sahrens /* 316789Sahrens * zfs clone <fs, snap, vol> fs 317789Sahrens * 318789Sahrens * Given an existing dataset, create a writable copy whose initial contents 319789Sahrens * are the same as the source. The newly created dataset maintains a 320789Sahrens * dependency on the original; the original cannot be destroyed so long as 321789Sahrens * the clone exists. 322789Sahrens */ 323789Sahrens static int 324789Sahrens zfs_do_clone(int argc, char **argv) 325789Sahrens { 326789Sahrens zfs_handle_t *zhp; 327789Sahrens int ret; 328789Sahrens 329789Sahrens /* check options */ 330789Sahrens if (argc > 1 && argv[1][0] == '-') { 331789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 332789Sahrens argv[1][1]); 3332082Seschrock usage(B_FALSE); 334789Sahrens } 335789Sahrens 336789Sahrens /* check number of arguments */ 337789Sahrens if (argc < 2) { 338789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 339789Sahrens "argument\n")); 3402082Seschrock usage(B_FALSE); 341789Sahrens } 342789Sahrens if (argc < 3) { 343789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 344789Sahrens "argument\n")); 3452082Seschrock usage(B_FALSE); 346789Sahrens } 347789Sahrens if (argc > 3) { 348789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 3492082Seschrock usage(B_FALSE); 350789Sahrens } 351789Sahrens 352789Sahrens /* open the source dataset */ 3532082Seschrock if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_SNAPSHOT)) == NULL) 354789Sahrens return (1); 355789Sahrens 356789Sahrens /* pass to libzfs */ 357789Sahrens ret = zfs_clone(zhp, argv[2]); 358789Sahrens 359789Sahrens /* create the mountpoint if necessary */ 360789Sahrens if (ret == 0) { 3612082Seschrock zfs_handle_t *clone = zfs_open(g_zfs, argv[2], ZFS_TYPE_ANY); 362789Sahrens if (clone != NULL) { 363789Sahrens if ((ret = zfs_mount(clone, NULL, 0)) == 0) 364789Sahrens ret = zfs_share(clone); 365789Sahrens zfs_close(clone); 366789Sahrens } 367789Sahrens } 368789Sahrens 369789Sahrens zfs_close(zhp); 370789Sahrens 371789Sahrens return (ret == 0 ? 0 : 1); 372789Sahrens } 373789Sahrens 374789Sahrens /* 375789Sahrens * zfs create fs 376*2199Sahrens * zfs create [-s] [-b blocksize] -V vol size 377789Sahrens * 378789Sahrens * Create a new dataset. This command can be used to create filesystems 379789Sahrens * and volumes. Snapshot creation is handled by 'zfs snapshot'. 380789Sahrens * For volumes, the user must specify a size to be used. 381789Sahrens * 382789Sahrens * The '-s' flag applies only to volumes, and indicates that we should not try 383789Sahrens * to set the reservation for this volume. By default we set a reservation 384789Sahrens * equal to the size for any volume. 385789Sahrens */ 386789Sahrens static int 387789Sahrens zfs_do_create(int argc, char **argv) 388789Sahrens { 389789Sahrens zfs_type_t type = ZFS_TYPE_FILESYSTEM; 390789Sahrens zfs_handle_t *zhp; 391789Sahrens char *size = NULL; 392789Sahrens char *blocksize = NULL; 393789Sahrens int c; 3942082Seschrock boolean_t noreserve = B_FALSE; 395789Sahrens int ret; 396789Sahrens 397789Sahrens /* check options */ 398789Sahrens while ((c = getopt(argc, argv, ":V:b:s")) != -1) { 399789Sahrens switch (c) { 400789Sahrens case 'V': 401789Sahrens type = ZFS_TYPE_VOLUME; 402789Sahrens size = optarg; 403789Sahrens break; 404789Sahrens case 'b': 405789Sahrens blocksize = optarg; 406789Sahrens break; 407789Sahrens case 's': 4082082Seschrock noreserve = B_TRUE; 409789Sahrens break; 410789Sahrens case ':': 411789Sahrens (void) fprintf(stderr, gettext("missing size " 412789Sahrens "argument\n")); 4132082Seschrock usage(B_FALSE); 414789Sahrens break; 415789Sahrens case '?': 416789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 417789Sahrens optopt); 4182082Seschrock usage(B_FALSE); 419789Sahrens } 420789Sahrens } 421789Sahrens 422789Sahrens if (noreserve && type != ZFS_TYPE_VOLUME) { 423789Sahrens (void) fprintf(stderr, gettext("'-s' can only be used when " 424789Sahrens "creating a volume\n")); 4252082Seschrock usage(B_FALSE); 426789Sahrens } 427789Sahrens 428789Sahrens argc -= optind; 429789Sahrens argv += optind; 430789Sahrens 431789Sahrens /* check number of arguments */ 432789Sahrens if (argc == 0) { 433789Sahrens (void) fprintf(stderr, gettext("missing %s argument\n"), 434789Sahrens zfs_type_to_name(type)); 4352082Seschrock usage(B_FALSE); 436789Sahrens } 437789Sahrens if (argc > 1) { 438789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 4392082Seschrock usage(B_FALSE); 440789Sahrens } 441789Sahrens 442789Sahrens /* pass to libzfs */ 4432082Seschrock if (zfs_create(g_zfs, argv[0], type, size, blocksize) != 0) 444789Sahrens return (1); 445789Sahrens 4462082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 447789Sahrens return (1); 448789Sahrens 449789Sahrens /* 450789Sahrens * Volume handling. By default, we try to create a reservation of equal 451789Sahrens * size for the volume. If we can't do this, then destroy the dataset 452789Sahrens * and report an error. 453789Sahrens */ 454789Sahrens if (type == ZFS_TYPE_VOLUME && !noreserve) { 455789Sahrens if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, size) != 0) { 456789Sahrens (void) fprintf(stderr, gettext("use '-s' to create a " 457789Sahrens "volume without a matching reservation\n")); 458789Sahrens (void) zfs_destroy(zhp); 459789Sahrens return (1); 460789Sahrens } 461789Sahrens } 462789Sahrens 463789Sahrens /* 464789Sahrens * Mount and/or share the new filesystem as appropriate. We provide a 465789Sahrens * verbose error message to let the user know that their filesystem was 466789Sahrens * in fact created, even if we failed to mount or share it. 467789Sahrens */ 468789Sahrens if (zfs_mount(zhp, NULL, 0) != 0) { 469789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 470789Sahrens "created, but not mounted\n")); 471789Sahrens ret = 1; 472789Sahrens } else if (zfs_share(zhp) != 0) { 473789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 474789Sahrens "created, but not shared\n")); 475789Sahrens ret = 1; 476789Sahrens } else { 477789Sahrens ret = 0; 478789Sahrens } 479789Sahrens 480789Sahrens zfs_close(zhp); 481789Sahrens return (ret); 482789Sahrens } 483789Sahrens 484789Sahrens /* 485789Sahrens * zfs destroy [-rf] <fs, snap, vol> 486789Sahrens * 487789Sahrens * -r Recursively destroy all children 488789Sahrens * -R Recursively destroy all dependents, including clones 489789Sahrens * -f Force unmounting of any dependents 490789Sahrens * 491789Sahrens * Destroys the given dataset. By default, it will unmount any filesystems, 492789Sahrens * and refuse to destroy a dataset that has any dependents. A dependent can 493789Sahrens * either be a child, or a clone of a child. 494789Sahrens */ 495789Sahrens typedef struct destroy_cbdata { 4962082Seschrock boolean_t cb_first; 497789Sahrens int cb_force; 498789Sahrens int cb_recurse; 499789Sahrens int cb_error; 500789Sahrens int cb_needforce; 501789Sahrens int cb_doclones; 502789Sahrens zfs_handle_t *cb_target; 503*2199Sahrens char *cb_snapname; 504789Sahrens } destroy_cbdata_t; 505789Sahrens 506789Sahrens /* 507789Sahrens * Check for any dependents based on the '-r' or '-R' flags. 508789Sahrens */ 509789Sahrens static int 510789Sahrens destroy_check_dependent(zfs_handle_t *zhp, void *data) 511789Sahrens { 512789Sahrens destroy_cbdata_t *cbp = data; 513789Sahrens const char *tname = zfs_get_name(cbp->cb_target); 514789Sahrens const char *name = zfs_get_name(zhp); 515789Sahrens 516789Sahrens if (strncmp(tname, name, strlen(tname)) == 0 && 517789Sahrens (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { 518789Sahrens /* 519789Sahrens * This is a direct descendant, not a clone somewhere else in 520789Sahrens * the hierarchy. 521789Sahrens */ 522789Sahrens if (cbp->cb_recurse) 523789Sahrens goto out; 524789Sahrens 525789Sahrens if (cbp->cb_first) { 526789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 527789Sahrens "%s has children\n"), 528789Sahrens zfs_get_name(cbp->cb_target), 529789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 530789Sahrens (void) fprintf(stderr, gettext("use '-r' to destroy " 531789Sahrens "the following datasets:\n")); 5322082Seschrock cbp->cb_first = B_FALSE; 533789Sahrens cbp->cb_error = 1; 534789Sahrens } 535789Sahrens 536789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 537789Sahrens } else { 538789Sahrens /* 539789Sahrens * This is a clone. We only want to report this if the '-r' 540789Sahrens * wasn't specified, or the target is a snapshot. 541789Sahrens */ 542789Sahrens if (!cbp->cb_recurse && 543789Sahrens zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) 544789Sahrens goto out; 545789Sahrens 546789Sahrens if (cbp->cb_first) { 547789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 548789Sahrens "%s has dependent clones\n"), 549789Sahrens zfs_get_name(cbp->cb_target), 550789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 551789Sahrens (void) fprintf(stderr, gettext("use '-R' to destroy " 552789Sahrens "the following datasets:\n")); 5532082Seschrock cbp->cb_first = B_FALSE; 554789Sahrens cbp->cb_error = 1; 555789Sahrens } 556789Sahrens 557789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 558789Sahrens } 559789Sahrens 560789Sahrens out: 561789Sahrens zfs_close(zhp); 562789Sahrens return (0); 563789Sahrens } 564789Sahrens 565789Sahrens static int 566789Sahrens destroy_callback(zfs_handle_t *zhp, void *data) 567789Sahrens { 568789Sahrens destroy_cbdata_t *cbp = data; 569789Sahrens 570789Sahrens /* 571789Sahrens * Ignore pools (which we've already flagged as an error before getting 572789Sahrens * here. 573789Sahrens */ 574789Sahrens if (strchr(zfs_get_name(zhp), '/') == NULL && 575789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 576789Sahrens zfs_close(zhp); 577789Sahrens return (0); 578789Sahrens } 579789Sahrens 580789Sahrens /* 581789Sahrens * Bail out on the first error. 582789Sahrens */ 583789Sahrens if (zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0 || 584789Sahrens zfs_destroy(zhp) != 0) { 585789Sahrens zfs_close(zhp); 586789Sahrens return (-1); 587789Sahrens } 588789Sahrens 589789Sahrens zfs_close(zhp); 590789Sahrens return (0); 591789Sahrens } 592789Sahrens 593*2199Sahrens static int 594*2199Sahrens destroy_snap_clones(zfs_handle_t *zhp, void *arg) 595*2199Sahrens { 596*2199Sahrens destroy_cbdata_t *cbp = arg; 597*2199Sahrens char thissnap[MAXPATHLEN]; 598*2199Sahrens zfs_handle_t *szhp; 599*2199Sahrens 600*2199Sahrens (void) snprintf(thissnap, sizeof (thissnap), 601*2199Sahrens "%s@%s", zfs_get_name(zhp), cbp->cb_snapname); 602*2199Sahrens 603*2199Sahrens libzfs_print_on_error(g_zfs, B_FALSE); 604*2199Sahrens szhp = zfs_open(g_zfs, thissnap, ZFS_TYPE_SNAPSHOT); 605*2199Sahrens libzfs_print_on_error(g_zfs, B_TRUE); 606*2199Sahrens if (szhp) { 607*2199Sahrens /* 608*2199Sahrens * Destroy any clones of this snapshot 609*2199Sahrens */ 610*2199Sahrens (void) zfs_iter_dependents(szhp, destroy_callback, cbp); 611*2199Sahrens zfs_close(szhp); 612*2199Sahrens } 613*2199Sahrens 614*2199Sahrens return (zfs_iter_filesystems(zhp, destroy_snap_clones, arg)); 615*2199Sahrens } 616789Sahrens 617789Sahrens static int 618789Sahrens zfs_do_destroy(int argc, char **argv) 619789Sahrens { 620789Sahrens destroy_cbdata_t cb = { 0 }; 621789Sahrens int c; 622789Sahrens zfs_handle_t *zhp; 623*2199Sahrens char *cp; 624789Sahrens 625789Sahrens /* check options */ 626789Sahrens while ((c = getopt(argc, argv, "frR")) != -1) { 627789Sahrens switch (c) { 628789Sahrens case 'f': 629789Sahrens cb.cb_force = 1; 630789Sahrens break; 631789Sahrens case 'r': 632789Sahrens cb.cb_recurse = 1; 633789Sahrens break; 634789Sahrens case 'R': 635789Sahrens cb.cb_recurse = 1; 636789Sahrens cb.cb_doclones = 1; 637789Sahrens break; 638789Sahrens case '?': 639789Sahrens default: 640789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 641789Sahrens optopt); 6422082Seschrock usage(B_FALSE); 643789Sahrens } 644789Sahrens } 645789Sahrens 646789Sahrens argc -= optind; 647789Sahrens argv += optind; 648789Sahrens 649789Sahrens /* check number of arguments */ 650789Sahrens if (argc == 0) { 651789Sahrens (void) fprintf(stderr, gettext("missing path argument\n")); 6522082Seschrock usage(B_FALSE); 653789Sahrens } 654789Sahrens if (argc > 1) { 655789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 6562082Seschrock usage(B_FALSE); 657789Sahrens } 658789Sahrens 659*2199Sahrens /* 660*2199Sahrens * If we are doing recursive destroy of a snapshot, then the 661*2199Sahrens * named snapshot may not exist. Go straight to libzfs. 662*2199Sahrens */ 663*2199Sahrens if (cb.cb_recurse && (cp = strchr(argv[0], '@'))) { 664*2199Sahrens int ret; 665*2199Sahrens 666*2199Sahrens *cp = '\0'; 667*2199Sahrens if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 668*2199Sahrens return (1); 669*2199Sahrens *cp = '@'; 670*2199Sahrens cp++; 671*2199Sahrens 672*2199Sahrens if (cb.cb_doclones) { 673*2199Sahrens cb.cb_snapname = cp; 674*2199Sahrens (void) destroy_snap_clones(zhp, &cb); 675*2199Sahrens } 676*2199Sahrens 677*2199Sahrens ret = zfs_destroy_snaps(zhp, cp); 678*2199Sahrens zfs_close(zhp); 679*2199Sahrens if (ret) { 680*2199Sahrens (void) fprintf(stderr, 681*2199Sahrens gettext("no snapshots destroyed\n")); 682*2199Sahrens } 683*2199Sahrens return (ret != 0); 684*2199Sahrens } 685*2199Sahrens 686*2199Sahrens 687789Sahrens /* Open the given dataset */ 6882082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 689789Sahrens return (1); 690789Sahrens 691789Sahrens cb.cb_target = zhp; 692789Sahrens 693789Sahrens /* 694789Sahrens * Perform an explicit check for pools before going any further. 695789Sahrens */ 696789Sahrens if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 697789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 698789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 699789Sahrens "operation does not apply to pools\n"), 700789Sahrens zfs_get_name(zhp)); 701789Sahrens (void) fprintf(stderr, gettext("use 'zfs destroy -r " 702789Sahrens "%s' to destroy all datasets in the pool\n"), 703789Sahrens zfs_get_name(zhp)); 704789Sahrens (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 705789Sahrens "to destroy the pool itself\n"), zfs_get_name(zhp)); 706789Sahrens zfs_close(zhp); 707789Sahrens return (1); 708789Sahrens } 709789Sahrens 710789Sahrens /* 711789Sahrens * Check for any dependents and/or clones. 712789Sahrens */ 7132082Seschrock cb.cb_first = B_TRUE; 714789Sahrens if (!cb.cb_doclones) 715789Sahrens (void) zfs_iter_dependents(zhp, destroy_check_dependent, &cb); 716789Sahrens 717789Sahrens if (cb.cb_error) { 718789Sahrens zfs_close(zhp); 719789Sahrens return (1); 720789Sahrens } 721789Sahrens 722789Sahrens /* 723789Sahrens * Do the real thing. 724789Sahrens */ 725789Sahrens if (zfs_iter_dependents(zhp, destroy_callback, &cb) == 0 && 726789Sahrens destroy_callback(zhp, &cb) == 0) 727789Sahrens return (0); 728789Sahrens 729789Sahrens return (1); 730789Sahrens } 731789Sahrens 732789Sahrens /* 733866Seschrock * zfs get [-rHp] [-o field[,field]...] [-s source[,source]...] 734866Seschrock * < all | property[,property]... > < fs | snap | vol > ... 735789Sahrens * 736789Sahrens * -r recurse over any child datasets 737789Sahrens * -H scripted mode. Headers are stripped, and fields are separated 738789Sahrens * by tabs instead of spaces. 739789Sahrens * -o Set of fields to display. One of "name,property,value,source". 740789Sahrens * Default is all four. 741789Sahrens * -s Set of sources to allow. One of 742789Sahrens * "local,default,inherited,temporary,none". Default is all 743789Sahrens * five. 744789Sahrens * -p Display values in parsable (literal) format. 745789Sahrens * 746789Sahrens * Prints properties for the given datasets. The user can control which 747789Sahrens * columns to display as well as which property types to allow. 748789Sahrens */ 749789Sahrens typedef struct get_cbdata { 750789Sahrens int cb_sources; 751789Sahrens int cb_columns[4]; 7522082Seschrock int cb_nprop; 7532082Seschrock boolean_t cb_scripted; 7542082Seschrock boolean_t cb_literal; 7552082Seschrock boolean_t cb_isall; 756789Sahrens zfs_prop_t cb_prop[ZFS_NPROP_ALL]; 757789Sahrens } get_cbdata_t; 758789Sahrens 759789Sahrens #define GET_COL_NAME 1 760789Sahrens #define GET_COL_PROPERTY 2 761789Sahrens #define GET_COL_VALUE 3 762789Sahrens #define GET_COL_SOURCE 4 763789Sahrens 764789Sahrens /* 765789Sahrens * Display a single line of output, according to the settings in the callback 766789Sahrens * structure. 767789Sahrens */ 768789Sahrens static void 769789Sahrens print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, zfs_prop_t prop, 770789Sahrens const char *value, zfs_source_t sourcetype, const char *source) 771789Sahrens { 772789Sahrens int i; 773789Sahrens int width; 774789Sahrens const char *str; 775789Sahrens char buf[128]; 776789Sahrens 777789Sahrens /* 778789Sahrens * Ignore those source types that the user has chosen to ignore. 779789Sahrens */ 780789Sahrens if ((sourcetype & cbp->cb_sources) == 0) 781789Sahrens return; 782789Sahrens 783789Sahrens for (i = 0; i < 4; i++) { 784789Sahrens switch (cbp->cb_columns[i]) { 785789Sahrens case GET_COL_NAME: 786789Sahrens width = 15; 787789Sahrens str = zfs_get_name(zhp); 788789Sahrens break; 789789Sahrens 790789Sahrens case GET_COL_PROPERTY: 791789Sahrens width = 13; 792789Sahrens str = zfs_prop_to_name(prop); 793789Sahrens break; 794789Sahrens 795789Sahrens case GET_COL_VALUE: 796789Sahrens width = 25; 797789Sahrens str = value; 798789Sahrens break; 799789Sahrens 800789Sahrens case GET_COL_SOURCE: 801789Sahrens width = 15; 802789Sahrens switch (sourcetype) { 803789Sahrens case ZFS_SRC_NONE: 804789Sahrens str = "-"; 805789Sahrens break; 806789Sahrens 807789Sahrens case ZFS_SRC_DEFAULT: 808789Sahrens str = "default"; 809789Sahrens break; 810789Sahrens 811789Sahrens case ZFS_SRC_LOCAL: 812789Sahrens str = "local"; 813789Sahrens break; 814789Sahrens 815789Sahrens case ZFS_SRC_TEMPORARY: 816789Sahrens str = "temporary"; 817789Sahrens break; 818789Sahrens 819789Sahrens case ZFS_SRC_INHERITED: 820789Sahrens (void) snprintf(buf, sizeof (buf), 821789Sahrens "inherited from %s", source); 822789Sahrens str = buf; 823789Sahrens break; 824789Sahrens } 825789Sahrens break; 826789Sahrens 827789Sahrens default: 828789Sahrens continue; 829789Sahrens } 830789Sahrens 831789Sahrens if (cbp->cb_columns[i + 1] == 0) 832789Sahrens (void) printf("%s", str); 833789Sahrens else if (cbp->cb_scripted) 834789Sahrens (void) printf("%s\t", str); 835789Sahrens else 836789Sahrens (void) printf("%-*s ", width, str); 837789Sahrens 838789Sahrens } 839789Sahrens 840789Sahrens (void) printf("\n"); 841789Sahrens } 842789Sahrens 843789Sahrens /* 844789Sahrens * Invoked to display the properties for a single dataset. 845789Sahrens */ 846789Sahrens static int 847789Sahrens get_callback(zfs_handle_t *zhp, void *data) 848789Sahrens { 849789Sahrens char buf[ZFS_MAXPROPLEN]; 850789Sahrens zfs_source_t sourcetype; 851789Sahrens char source[ZFS_MAXNAMELEN]; 852789Sahrens get_cbdata_t *cbp = data; 853789Sahrens int i; 854789Sahrens 855866Seschrock for (i = 0; i < cbp->cb_nprop; i++) { 856866Seschrock if (zfs_prop_get(zhp, cbp->cb_prop[i], buf, 857866Seschrock sizeof (buf), &sourcetype, source, sizeof (source), 858866Seschrock cbp->cb_literal) != 0) { 859866Seschrock if (cbp->cb_isall) 860866Seschrock continue; 861866Seschrock (void) strlcpy(buf, "-", sizeof (buf)); 862866Seschrock sourcetype = ZFS_SRC_NONE; 863866Seschrock } 864789Sahrens 865866Seschrock print_one_property(zhp, cbp, cbp->cb_prop[i], 866866Seschrock buf, sourcetype, source); 867789Sahrens } 868789Sahrens 869789Sahrens return (0); 870789Sahrens } 871789Sahrens 872789Sahrens static int 873789Sahrens zfs_do_get(int argc, char **argv) 874789Sahrens { 875789Sahrens get_cbdata_t cb = { 0 }; 8762082Seschrock boolean_t recurse = B_FALSE; 877789Sahrens int c; 878866Seschrock char *value, *fields, *badopt; 879789Sahrens int i; 880866Seschrock int ret; 881789Sahrens 882789Sahrens /* 883789Sahrens * Set up default columns and sources. 884789Sahrens */ 885789Sahrens cb.cb_sources = ZFS_SRC_ALL; 886789Sahrens cb.cb_columns[0] = GET_COL_NAME; 887789Sahrens cb.cb_columns[1] = GET_COL_PROPERTY; 888789Sahrens cb.cb_columns[2] = GET_COL_VALUE; 889789Sahrens cb.cb_columns[3] = GET_COL_SOURCE; 890789Sahrens 891789Sahrens /* check options */ 892789Sahrens while ((c = getopt(argc, argv, ":o:s:rHp")) != -1) { 893789Sahrens switch (c) { 894789Sahrens case 'p': 8952082Seschrock cb.cb_literal = B_TRUE; 896789Sahrens break; 897789Sahrens case 'r': 8982082Seschrock recurse = B_TRUE; 899789Sahrens break; 900789Sahrens case 'H': 9012082Seschrock cb.cb_scripted = B_TRUE; 902789Sahrens break; 903789Sahrens case ':': 904789Sahrens (void) fprintf(stderr, gettext("missing argument for " 905789Sahrens "'%c' option\n"), optopt); 9062082Seschrock usage(B_FALSE); 907789Sahrens break; 908789Sahrens case 'o': 909789Sahrens /* 910789Sahrens * Process the set of columns to display. We zero out 911789Sahrens * the structure to give us a blank slate. 912789Sahrens */ 913789Sahrens bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 914789Sahrens i = 0; 915789Sahrens while (*optarg != '\0') { 916789Sahrens static char *col_subopts[] = 917789Sahrens { "name", "property", "value", "source", 918789Sahrens NULL }; 919789Sahrens 920789Sahrens if (i == 4) { 921789Sahrens (void) fprintf(stderr, gettext("too " 922789Sahrens "many fields given to -o " 923789Sahrens "option\n")); 9242082Seschrock usage(B_FALSE); 925789Sahrens } 926789Sahrens 927789Sahrens switch (getsubopt(&optarg, col_subopts, 928789Sahrens &value)) { 929789Sahrens case 0: 930789Sahrens cb.cb_columns[i++] = GET_COL_NAME; 931789Sahrens break; 932789Sahrens case 1: 933789Sahrens cb.cb_columns[i++] = GET_COL_PROPERTY; 934789Sahrens break; 935789Sahrens case 2: 936789Sahrens cb.cb_columns[i++] = GET_COL_VALUE; 937789Sahrens break; 938789Sahrens case 3: 939789Sahrens cb.cb_columns[i++] = GET_COL_SOURCE; 940789Sahrens break; 941789Sahrens default: 942789Sahrens (void) fprintf(stderr, 943789Sahrens gettext("invalid column name " 944789Sahrens "'%s'\n"), value); 9452082Seschrock usage(B_FALSE); 946789Sahrens } 947789Sahrens } 948789Sahrens break; 949789Sahrens 950789Sahrens case 's': 951789Sahrens cb.cb_sources = 0; 952789Sahrens while (*optarg != '\0') { 953789Sahrens static char *source_subopts[] = { 954789Sahrens "local", "default", "inherited", 955789Sahrens "temporary", "none", NULL }; 956789Sahrens 957789Sahrens switch (getsubopt(&optarg, source_subopts, 958789Sahrens &value)) { 959789Sahrens case 0: 960789Sahrens cb.cb_sources |= ZFS_SRC_LOCAL; 961789Sahrens break; 962789Sahrens case 1: 963789Sahrens cb.cb_sources |= ZFS_SRC_DEFAULT; 964789Sahrens break; 965789Sahrens case 2: 966789Sahrens cb.cb_sources |= ZFS_SRC_INHERITED; 967789Sahrens break; 968789Sahrens case 3: 969789Sahrens cb.cb_sources |= ZFS_SRC_TEMPORARY; 970789Sahrens break; 971789Sahrens case 4: 972789Sahrens cb.cb_sources |= ZFS_SRC_NONE; 973789Sahrens break; 974789Sahrens default: 975789Sahrens (void) fprintf(stderr, 976789Sahrens gettext("invalid source " 977789Sahrens "'%s'\n"), value); 9782082Seschrock usage(B_FALSE); 979789Sahrens } 980789Sahrens } 981789Sahrens break; 982789Sahrens 983789Sahrens case '?': 984789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 985789Sahrens optopt); 9862082Seschrock usage(B_FALSE); 987789Sahrens } 988789Sahrens } 989789Sahrens 990789Sahrens argc -= optind; 991789Sahrens argv += optind; 992789Sahrens 993789Sahrens if (argc < 1) { 994789Sahrens (void) fprintf(stderr, gettext("missing property " 995789Sahrens "argument\n")); 9962082Seschrock usage(B_FALSE); 997789Sahrens } 998789Sahrens 999789Sahrens fields = argv[0]; 1000789Sahrens 1001789Sahrens /* 1002866Seschrock * If the user specifies 'all', the behavior of 'zfs get' is slightly 1003866Seschrock * different, because we don't show properties which don't apply to the 1004866Seschrock * given dataset. 1005789Sahrens */ 1006866Seschrock if (strcmp(fields, "all") == 0) 10072082Seschrock cb.cb_isall = B_TRUE; 1008789Sahrens 1009866Seschrock if ((ret = zfs_get_proplist(fields, cb.cb_prop, ZFS_NPROP_ALL, 1010866Seschrock &cb.cb_nprop, &badopt)) != 0) { 1011866Seschrock if (ret == EINVAL) 1012866Seschrock (void) fprintf(stderr, gettext("invalid property " 1013866Seschrock "'%s'\n"), badopt); 1014866Seschrock else 1015866Seschrock (void) fprintf(stderr, gettext("too many properties " 1016866Seschrock "specified\n")); 10172082Seschrock usage(B_FALSE); 1018789Sahrens } 1019789Sahrens 1020789Sahrens argc--; 1021789Sahrens argv++; 1022789Sahrens 1023789Sahrens /* check for at least one dataset name */ 1024789Sahrens if (argc < 1) { 1025789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 10262082Seschrock usage(B_FALSE); 1027789Sahrens } 1028789Sahrens 1029789Sahrens /* 1030789Sahrens * Print out any headers 1031789Sahrens */ 1032789Sahrens if (!cb.cb_scripted) { 1033789Sahrens int i; 1034789Sahrens for (i = 0; i < 4; i++) { 1035789Sahrens switch (cb.cb_columns[i]) { 1036789Sahrens case GET_COL_NAME: 1037789Sahrens (void) printf("%-15s ", "NAME"); 1038789Sahrens break; 1039789Sahrens case GET_COL_PROPERTY: 1040789Sahrens (void) printf("%-13s ", "PROPERTY"); 1041789Sahrens break; 1042789Sahrens case GET_COL_VALUE: 1043789Sahrens (void) printf("%-25s ", "VALUE"); 1044789Sahrens break; 1045789Sahrens case GET_COL_SOURCE: 1046789Sahrens (void) printf("%s", "SOURCE"); 1047789Sahrens break; 1048789Sahrens } 1049789Sahrens } 1050789Sahrens (void) printf("\n"); 1051789Sahrens } 1052789Sahrens 1053789Sahrens /* run for each object */ 1054789Sahrens return (zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, 1055789Sahrens get_callback, &cb)); 1056789Sahrens } 1057789Sahrens 1058789Sahrens /* 1059789Sahrens * inherit [-r] <property> <fs|vol> ... 1060789Sahrens * 1061789Sahrens * -r Recurse over all children 1062789Sahrens * 1063789Sahrens * For each dataset specified on the command line, inherit the given property 1064789Sahrens * from its parent. Inheriting a property at the pool level will cause it to 1065789Sahrens * use the default value. The '-r' flag will recurse over all children, and is 1066789Sahrens * useful for setting a property on a hierarchy-wide basis, regardless of any 1067789Sahrens * local modifications for each dataset. 1068789Sahrens */ 1069789Sahrens static int 1070789Sahrens inherit_callback(zfs_handle_t *zhp, void *data) 1071789Sahrens { 1072789Sahrens zfs_prop_t prop = (zfs_prop_t)data; 1073789Sahrens 1074789Sahrens return (zfs_prop_inherit(zhp, prop) != 0); 1075789Sahrens } 1076789Sahrens 1077789Sahrens static int 1078789Sahrens zfs_do_inherit(int argc, char **argv) 1079789Sahrens { 10802082Seschrock boolean_t recurse = B_FALSE; 1081789Sahrens int c; 1082789Sahrens zfs_prop_t prop; 1083789Sahrens char *propname; 1084789Sahrens 1085789Sahrens /* check options */ 1086789Sahrens while ((c = getopt(argc, argv, "r")) != -1) { 1087789Sahrens switch (c) { 1088789Sahrens case 'r': 10892082Seschrock recurse = B_TRUE; 1090789Sahrens break; 1091789Sahrens case '?': 1092789Sahrens default: 1093789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1094789Sahrens optopt); 10952082Seschrock usage(B_FALSE); 1096789Sahrens } 1097789Sahrens } 1098789Sahrens 1099789Sahrens argc -= optind; 1100789Sahrens argv += optind; 1101789Sahrens 1102789Sahrens /* check number of arguments */ 1103789Sahrens if (argc < 1) { 1104789Sahrens (void) fprintf(stderr, gettext("missing property argument\n")); 11052082Seschrock usage(B_FALSE); 1106789Sahrens } 1107789Sahrens if (argc < 2) { 1108789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 11092082Seschrock usage(B_FALSE); 1110789Sahrens } 1111789Sahrens 1112789Sahrens propname = argv[0]; 1113789Sahrens 1114789Sahrens /* 1115789Sahrens * Get and validate the property before iterating over the datasets. We 1116789Sahrens * do this now so as to avoid printing out an error message for each and 1117789Sahrens * every dataset. 1118789Sahrens */ 1119789Sahrens if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) { 1120789Sahrens (void) fprintf(stderr, gettext("invalid property '%s'\n"), 1121789Sahrens propname); 11222082Seschrock usage(B_FALSE); 1123789Sahrens } 1124789Sahrens if (zfs_prop_readonly(prop)) { 1125789Sahrens (void) fprintf(stderr, gettext("%s property is read-only\n"), 1126789Sahrens propname); 1127789Sahrens return (1); 1128789Sahrens } 1129789Sahrens if (!zfs_prop_inheritable(prop)) { 1130789Sahrens (void) fprintf(stderr, gettext("%s property cannot be " 1131789Sahrens "inherited\n"), propname); 1132789Sahrens (void) fprintf(stderr, gettext("use 'zfs set %s=none' to " 1133789Sahrens "clear\n"), propname); 1134789Sahrens return (1); 1135789Sahrens } 1136789Sahrens 1137789Sahrens return (zfs_for_each(argc - 1, argv + 1, recurse, 1138789Sahrens ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, 1139789Sahrens inherit_callback, (void *)prop)); 1140789Sahrens } 1141789Sahrens 1142789Sahrens /* 1143866Seschrock * list [-rH] [-o property[,property]...] [-t type[,type]...] <dataset> ... 1144789Sahrens * 1145789Sahrens * -r Recurse over all children 1146789Sahrens * -H Scripted mode; elide headers and separate colums by tabs 1147789Sahrens * -o Control which fields to display. 1148866Seschrock * -t Control which object types to display. 1149789Sahrens * 1150789Sahrens * When given no arguments, lists all filesystems in the system. 1151789Sahrens * Otherwise, list the specified datasets, optionally recursing down them if 1152789Sahrens * '-r' is specified. 1153789Sahrens */ 1154789Sahrens typedef struct list_cbdata { 11552082Seschrock boolean_t cb_first; 11562082Seschrock boolean_t cb_scripted; 1157866Seschrock zfs_prop_t cb_fields[ZFS_NPROP_ALL]; 1158866Seschrock int cb_fieldcount; 1159789Sahrens } list_cbdata_t; 1160789Sahrens 1161789Sahrens /* 1162789Sahrens * Given a list of columns to display, output appropriate headers for each one. 1163789Sahrens */ 1164789Sahrens static void 1165866Seschrock print_header(zfs_prop_t *fields, size_t count) 1166789Sahrens { 1167789Sahrens int i; 1168789Sahrens 1169789Sahrens for (i = 0; i < count; i++) { 1170789Sahrens if (i != 0) 1171789Sahrens (void) printf(" "); 1172789Sahrens if (i == count - 1) 1173789Sahrens (void) printf("%s", zfs_prop_column_name(fields[i])); 1174789Sahrens else /* LINTED - format specifier */ 1175789Sahrens (void) printf(zfs_prop_column_format(fields[i]), 1176789Sahrens zfs_prop_column_name(fields[i])); 1177789Sahrens } 1178789Sahrens 1179789Sahrens (void) printf("\n"); 1180789Sahrens } 1181789Sahrens 1182789Sahrens /* 1183789Sahrens * Given a dataset and a list of fields, print out all the properties according 1184789Sahrens * to the described layout. 1185789Sahrens */ 1186789Sahrens static void 1187866Seschrock print_dataset(zfs_handle_t *zhp, zfs_prop_t *fields, size_t count, int scripted) 1188789Sahrens { 1189789Sahrens int i; 1190789Sahrens char property[ZFS_MAXPROPLEN]; 1191789Sahrens 1192789Sahrens for (i = 0; i < count; i++) { 1193789Sahrens if (i != 0) { 1194789Sahrens if (scripted) 1195789Sahrens (void) printf("\t"); 1196789Sahrens else 1197789Sahrens (void) printf(" "); 1198789Sahrens } 1199789Sahrens 1200789Sahrens if (zfs_prop_get(zhp, fields[i], property, 12012082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) != 0) 1202789Sahrens (void) strlcpy(property, "-", sizeof (property)); 1203789Sahrens 1204866Seschrock /* 1205866Seschrock * If this is being called in scripted mode, or if this is the 1206866Seschrock * last column and it is left-justified, don't include a width 1207866Seschrock * format specifier. 1208866Seschrock */ 1209866Seschrock if (scripted || (i == count - 1 && 1210866Seschrock strchr(zfs_prop_column_format(fields[i]), '-') != NULL)) 1211789Sahrens (void) printf("%s", property); 1212789Sahrens else /* LINTED - format specifier */ 1213789Sahrens (void) printf(zfs_prop_column_format(fields[i]), 1214789Sahrens property); 1215789Sahrens } 1216789Sahrens 1217789Sahrens (void) printf("\n"); 1218789Sahrens } 1219789Sahrens 1220789Sahrens /* 1221789Sahrens * Generic callback function to list a dataset or snapshot. 1222789Sahrens */ 1223789Sahrens static int 1224789Sahrens list_callback(zfs_handle_t *zhp, void *data) 1225789Sahrens { 1226789Sahrens list_cbdata_t *cbp = data; 1227789Sahrens 1228789Sahrens if (cbp->cb_first) { 1229789Sahrens if (!cbp->cb_scripted) 1230789Sahrens print_header(cbp->cb_fields, cbp->cb_fieldcount); 12312082Seschrock cbp->cb_first = B_FALSE; 1232789Sahrens } 1233789Sahrens 1234789Sahrens print_dataset(zhp, cbp->cb_fields, cbp->cb_fieldcount, 1235789Sahrens cbp->cb_scripted); 1236789Sahrens 1237789Sahrens return (0); 1238789Sahrens } 1239789Sahrens 1240789Sahrens static int 1241789Sahrens zfs_do_list(int argc, char **argv) 1242789Sahrens { 1243789Sahrens int c; 12442082Seschrock boolean_t recurse = B_FALSE; 12452082Seschrock boolean_t scripted = B_FALSE; 1246789Sahrens static char default_fields[] = 1247789Sahrens "name,used,available,referenced,mountpoint"; 1248789Sahrens int types = ZFS_TYPE_ANY; 1249789Sahrens char *fields = NULL; 1250789Sahrens char *basic_fields = default_fields; 1251789Sahrens list_cbdata_t cb = { 0 }; 1252789Sahrens char *value; 1253789Sahrens int ret; 1254789Sahrens char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL }; 1255866Seschrock char *badopt; 1256866Seschrock int alloffset; 1257789Sahrens 1258789Sahrens /* check options */ 1259789Sahrens while ((c = getopt(argc, argv, ":o:rt:H")) != -1) { 1260789Sahrens switch (c) { 1261789Sahrens case 'o': 1262789Sahrens fields = optarg; 1263789Sahrens break; 1264789Sahrens case 'r': 12652082Seschrock recurse = B_TRUE; 1266789Sahrens break; 1267789Sahrens case 'H': 12682082Seschrock scripted = B_TRUE; 1269789Sahrens break; 1270789Sahrens case 't': 1271789Sahrens types = 0; 1272789Sahrens while (*optarg != '\0') { 1273789Sahrens switch (getsubopt(&optarg, type_subopts, 1274789Sahrens &value)) { 1275789Sahrens case 0: 1276789Sahrens types |= ZFS_TYPE_FILESYSTEM; 1277789Sahrens break; 1278789Sahrens case 1: 1279789Sahrens types |= ZFS_TYPE_VOLUME; 1280789Sahrens break; 1281789Sahrens case 2: 1282789Sahrens types |= ZFS_TYPE_SNAPSHOT; 1283789Sahrens break; 1284789Sahrens default: 1285789Sahrens (void) fprintf(stderr, 1286789Sahrens gettext("invalid type '%s'\n"), 1287789Sahrens value); 12882082Seschrock usage(B_FALSE); 1289789Sahrens } 1290789Sahrens } 1291789Sahrens break; 1292789Sahrens case ':': 1293789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1294789Sahrens "'%c' option\n"), optopt); 12952082Seschrock usage(B_FALSE); 1296789Sahrens break; 1297789Sahrens case '?': 1298789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1299789Sahrens optopt); 13002082Seschrock usage(B_FALSE); 1301789Sahrens } 1302789Sahrens } 1303789Sahrens 1304789Sahrens argc -= optind; 1305789Sahrens argv += optind; 1306789Sahrens 1307789Sahrens if (fields == NULL) 1308789Sahrens fields = basic_fields; 1309789Sahrens 1310866Seschrock /* 1311866Seschrock * If the user specifies '-o all', the zfs_get_proplist() doesn't 1312866Seschrock * normally include the name of the dataset. For 'zfs list', we always 1313866Seschrock * want this property to be first. 1314866Seschrock */ 1315866Seschrock if (strcmp(fields, "all") == 0) { 1316866Seschrock cb.cb_fields[0] = ZFS_PROP_NAME; 1317866Seschrock alloffset = 1; 1318866Seschrock } else { 1319866Seschrock alloffset = 0; 1320789Sahrens } 1321789Sahrens 1322866Seschrock if ((ret = zfs_get_proplist(fields, cb.cb_fields + alloffset, 1323866Seschrock ZFS_NPROP_ALL - alloffset, &cb.cb_fieldcount, &badopt)) != 0) { 1324866Seschrock if (ret == EINVAL) 1325866Seschrock (void) fprintf(stderr, gettext("invalid property " 1326866Seschrock "'%s'\n"), badopt); 1327866Seschrock else 1328866Seschrock (void) fprintf(stderr, gettext("too many properties " 1329866Seschrock "specified\n")); 13302082Seschrock usage(B_FALSE); 1331866Seschrock } 1332866Seschrock 1333866Seschrock cb.cb_fieldcount += alloffset; 1334789Sahrens cb.cb_scripted = scripted; 13352082Seschrock cb.cb_first = B_TRUE; 1336789Sahrens 1337789Sahrens ret = zfs_for_each(argc, argv, recurse, types, list_callback, &cb); 1338789Sahrens 13392082Seschrock if (ret == 0 && cb.cb_first) 1340789Sahrens (void) printf(gettext("no datasets available\n")); 1341789Sahrens 1342789Sahrens return (ret); 1343789Sahrens } 1344789Sahrens 1345789Sahrens /* 1346789Sahrens * zfs rename <fs | snap | vol> <fs | snap | vol> 1347789Sahrens * 1348789Sahrens * Renames the given dataset to another of the same type. 1349789Sahrens */ 1350789Sahrens /* ARGSUSED */ 1351789Sahrens static int 1352789Sahrens zfs_do_rename(int argc, char **argv) 1353789Sahrens { 1354789Sahrens zfs_handle_t *zhp; 13552082Seschrock int ret; 1356789Sahrens 1357789Sahrens /* check options */ 1358789Sahrens if (argc > 1 && argv[1][0] == '-') { 1359789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1360789Sahrens argv[1][1]); 13612082Seschrock usage(B_FALSE); 1362789Sahrens } 1363789Sahrens 1364789Sahrens /* check number of arguments */ 1365789Sahrens if (argc < 2) { 1366789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 1367789Sahrens "argument\n")); 13682082Seschrock usage(B_FALSE); 1369789Sahrens } 1370789Sahrens if (argc < 3) { 1371789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 1372789Sahrens "argument\n")); 13732082Seschrock usage(B_FALSE); 1374789Sahrens } 1375789Sahrens if (argc > 3) { 1376789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 13772082Seschrock usage(B_FALSE); 1378789Sahrens } 1379789Sahrens 13802082Seschrock if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_ANY)) == NULL) 1381789Sahrens return (1); 1382789Sahrens 13832082Seschrock ret = (zfs_rename(zhp, argv[2]) != 0); 13842082Seschrock 13852082Seschrock zfs_close(zhp); 13862082Seschrock return (ret); 13872082Seschrock } 13882082Seschrock 13892082Seschrock /* 13902082Seschrock * zfs promote <fs> 13912082Seschrock * 13922082Seschrock * Promotes the given clone fs to be the parent 13932082Seschrock */ 13942082Seschrock /* ARGSUSED */ 13952082Seschrock static int 13962082Seschrock zfs_do_promote(int argc, char **argv) 13972082Seschrock { 13982082Seschrock zfs_handle_t *zhp; 13992082Seschrock int ret; 14002082Seschrock 14012082Seschrock /* check options */ 14022082Seschrock if (argc > 1 && argv[1][0] == '-') { 14032082Seschrock (void) fprintf(stderr, gettext("invalid option '%c'\n"), 14042082Seschrock argv[1][1]); 14052082Seschrock usage(B_FALSE); 14062082Seschrock } 14072082Seschrock 14082082Seschrock /* check number of arguments */ 14092082Seschrock if (argc < 2) { 14102082Seschrock (void) fprintf(stderr, gettext("missing clone filesystem" 14112082Seschrock "argument\n")); 14122082Seschrock usage(B_FALSE); 14132082Seschrock } 14142082Seschrock if (argc > 2) { 14152082Seschrock (void) fprintf(stderr, gettext("too many arguments\n")); 14162082Seschrock usage(B_FALSE); 14172082Seschrock } 14182082Seschrock 14192082Seschrock zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 14202082Seschrock if (zhp == NULL) 14212082Seschrock return (1); 14222082Seschrock 14232082Seschrock ret = (zfs_promote(zhp) != 0); 14242082Seschrock 1425789Sahrens zfs_close(zhp); 1426789Sahrens return (ret); 1427789Sahrens } 1428789Sahrens 1429789Sahrens /* 1430789Sahrens * zfs rollback [-rfR] <snapshot> 1431789Sahrens * 1432789Sahrens * -r Delete any intervening snapshots before doing rollback 1433789Sahrens * -R Delete any snapshots and their clones 1434789Sahrens * -f Force unmount filesystems, even if they are in use. 1435789Sahrens * 1436789Sahrens * Given a filesystem, rollback to a specific snapshot, discarding any changes 1437789Sahrens * since then and making it the active dataset. If more recent snapshots exist, 1438789Sahrens * the command will complain unless the '-r' flag is given. 1439789Sahrens */ 1440789Sahrens typedef struct rollback_cbdata { 1441789Sahrens uint64_t cb_create; 14422082Seschrock boolean_t cb_first; 1443789Sahrens int cb_doclones; 1444789Sahrens char *cb_target; 1445789Sahrens int cb_error; 14462082Seschrock boolean_t cb_recurse; 14472082Seschrock boolean_t cb_dependent; 1448789Sahrens } rollback_cbdata_t; 1449789Sahrens 1450789Sahrens /* 1451789Sahrens * Report any snapshots more recent than the one specified. Used when '-r' is 1452789Sahrens * not specified. We reuse this same callback for the snapshot dependents - if 1453789Sahrens * 'cb_dependent' is set, then this is a dependent and we should report it 1454789Sahrens * without checking the transaction group. 1455789Sahrens */ 1456789Sahrens static int 1457789Sahrens rollback_check(zfs_handle_t *zhp, void *data) 1458789Sahrens { 1459789Sahrens rollback_cbdata_t *cbp = data; 1460789Sahrens 14612082Seschrock if (cbp->cb_doclones) { 14622082Seschrock zfs_close(zhp); 1463789Sahrens return (0); 14642082Seschrock } 1465789Sahrens 1466789Sahrens if (!cbp->cb_dependent) { 1467789Sahrens if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && 14681294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 1469789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 1470789Sahrens cbp->cb_create) { 1471789Sahrens 1472789Sahrens if (cbp->cb_first && !cbp->cb_recurse) { 1473789Sahrens (void) fprintf(stderr, gettext("cannot " 1474789Sahrens "rollback to '%s': more recent snapshots " 1475789Sahrens "exist\n"), 1476789Sahrens cbp->cb_target); 1477789Sahrens (void) fprintf(stderr, gettext("use '-r' to " 1478789Sahrens "force deletion of the following " 1479789Sahrens "snapshots:\n")); 1480789Sahrens cbp->cb_first = 0; 1481789Sahrens cbp->cb_error = 1; 1482789Sahrens } 1483789Sahrens 1484789Sahrens if (cbp->cb_recurse) { 14852082Seschrock cbp->cb_dependent = B_TRUE; 1486789Sahrens (void) zfs_iter_dependents(zhp, rollback_check, 1487789Sahrens cbp); 14882082Seschrock cbp->cb_dependent = B_FALSE; 1489789Sahrens } else { 1490789Sahrens (void) fprintf(stderr, "%s\n", 1491789Sahrens zfs_get_name(zhp)); 1492789Sahrens } 1493789Sahrens } 1494789Sahrens } else { 1495789Sahrens if (cbp->cb_first && cbp->cb_recurse) { 1496789Sahrens (void) fprintf(stderr, gettext("cannot rollback to " 1497789Sahrens "'%s': clones of previous snapshots exist\n"), 1498789Sahrens cbp->cb_target); 1499789Sahrens (void) fprintf(stderr, gettext("use '-R' to " 1500789Sahrens "force deletion of the following clones and " 1501789Sahrens "dependents:\n")); 1502789Sahrens cbp->cb_first = 0; 1503789Sahrens cbp->cb_error = 1; 1504789Sahrens } 1505789Sahrens 1506789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1507789Sahrens } 1508789Sahrens 1509789Sahrens zfs_close(zhp); 1510789Sahrens return (0); 1511789Sahrens } 1512789Sahrens 1513789Sahrens static int 1514789Sahrens zfs_do_rollback(int argc, char **argv) 1515789Sahrens { 1516789Sahrens int ret; 1517789Sahrens int c; 1518789Sahrens rollback_cbdata_t cb = { 0 }; 1519789Sahrens zfs_handle_t *zhp, *snap; 1520789Sahrens char parentname[ZFS_MAXNAMELEN]; 1521789Sahrens char *delim; 15221294Slling int force = 0; 1523789Sahrens 1524789Sahrens /* check options */ 1525789Sahrens while ((c = getopt(argc, argv, "rfR")) != -1) { 1526789Sahrens switch (c) { 1527789Sahrens case 'f': 15281294Slling force = 1; 1529789Sahrens break; 1530789Sahrens case 'r': 1531789Sahrens cb.cb_recurse = 1; 1532789Sahrens break; 1533789Sahrens case 'R': 1534789Sahrens cb.cb_recurse = 1; 1535789Sahrens cb.cb_doclones = 1; 1536789Sahrens break; 1537789Sahrens case '?': 1538789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1539789Sahrens optopt); 15402082Seschrock usage(B_FALSE); 1541789Sahrens } 1542789Sahrens } 1543789Sahrens 1544789Sahrens argc -= optind; 1545789Sahrens argv += optind; 1546789Sahrens 1547789Sahrens /* check number of arguments */ 1548789Sahrens if (argc < 1) { 1549789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 15502082Seschrock usage(B_FALSE); 1551789Sahrens } 1552789Sahrens if (argc > 1) { 1553789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 15542082Seschrock usage(B_FALSE); 1555789Sahrens } 1556789Sahrens 1557789Sahrens /* open the snapshot */ 15582082Seschrock if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1559789Sahrens return (1); 1560789Sahrens 15611294Slling /* open the parent dataset */ 15621294Slling (void) strlcpy(parentname, argv[0], sizeof (parentname)); 1563789Sahrens verify((delim = strrchr(parentname, '@')) != NULL); 1564789Sahrens *delim = '\0'; 15652082Seschrock if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_ANY)) == NULL) { 1566789Sahrens zfs_close(snap); 1567789Sahrens return (1); 1568789Sahrens } 1569789Sahrens 1570789Sahrens /* 1571789Sahrens * Check for more recent snapshots and/or clones based on the presence 1572789Sahrens * of '-r' and '-R'. 1573789Sahrens */ 15741294Slling cb.cb_target = argv[0]; 15751294Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 15762082Seschrock cb.cb_first = B_TRUE; 1577789Sahrens cb.cb_error = 0; 1578789Sahrens (void) zfs_iter_children(zhp, rollback_check, &cb); 1579789Sahrens 1580789Sahrens if ((ret = cb.cb_error) != 0) 1581789Sahrens goto out; 1582789Sahrens 1583789Sahrens /* 15841294Slling * Rollback parent to the given snapshot. 1585789Sahrens */ 15861294Slling ret = zfs_rollback(zhp, snap, force); 1587789Sahrens 1588789Sahrens out: 1589789Sahrens zfs_close(snap); 1590789Sahrens zfs_close(zhp); 1591789Sahrens 1592789Sahrens if (ret == 0) 1593789Sahrens return (0); 1594789Sahrens else 1595789Sahrens return (1); 1596789Sahrens } 1597789Sahrens 1598789Sahrens /* 1599789Sahrens * zfs set property=value { fs | snap | vol } ... 1600789Sahrens * 1601789Sahrens * Sets the given property for all datasets specified on the command line. 1602789Sahrens */ 1603789Sahrens typedef struct set_cbdata { 1604789Sahrens char *cb_propname; 1605789Sahrens char *cb_value; 1606789Sahrens zfs_prop_t cb_prop; 1607789Sahrens } set_cbdata_t; 1608789Sahrens 1609789Sahrens static int 1610789Sahrens set_callback(zfs_handle_t *zhp, void *data) 1611789Sahrens { 1612789Sahrens set_cbdata_t *cbp = data; 1613789Sahrens int ret = 1; 1614789Sahrens 1615789Sahrens /* don't allow setting of properties for snapshots */ 1616789Sahrens if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { 1617789Sahrens (void) fprintf(stderr, gettext("cannot set %s property for " 1618789Sahrens "'%s': snapshot properties cannot be modified\n"), 1619789Sahrens cbp->cb_propname, zfs_get_name(zhp)); 1620789Sahrens return (1); 1621789Sahrens } 1622789Sahrens 1623789Sahrens /* 16241133Seschrock * If we're changing the volsize, make sure the value is appropriate, 16251133Seschrock * and set the reservation if this is a non-sparse volume. 1626789Sahrens */ 1627789Sahrens if (cbp->cb_prop == ZFS_PROP_VOLSIZE && 16281133Seschrock zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 1629789Sahrens uint64_t volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1630789Sahrens uint64_t avail = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE); 16311133Seschrock uint64_t reservation = zfs_prop_get_int(zhp, 16321133Seschrock ZFS_PROP_RESERVATION); 16331133Seschrock uint64_t blocksize = zfs_prop_get_int(zhp, 16341133Seschrock ZFS_PROP_VOLBLOCKSIZE); 1635789Sahrens uint64_t value; 1636789Sahrens 1637789Sahrens verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); 1638789Sahrens 16391133Seschrock if (value % blocksize != 0) { 16401133Seschrock char buf[64]; 16411133Seschrock 16421133Seschrock zfs_nicenum(blocksize, buf, sizeof (buf)); 16431133Seschrock (void) fprintf(stderr, gettext("cannot set %s for " 16441133Seschrock "'%s': must be a multiple of volume block size " 16451133Seschrock "(%s)\n"), cbp->cb_propname, zfs_get_name(zhp), 16461133Seschrock buf); 1647789Sahrens return (1); 1648789Sahrens } 1649789Sahrens 16501133Seschrock if (value == 0) { 16511133Seschrock (void) fprintf(stderr, gettext("cannot set %s for " 16521133Seschrock "'%s': cannot be zero\n"), cbp->cb_propname, 16531133Seschrock zfs_get_name(zhp)); 1654789Sahrens return (1); 1655789Sahrens } 16561133Seschrock 16571133Seschrock if (volsize == reservation) { 16581133Seschrock if (value > volsize && (value - volsize) > avail) { 16591133Seschrock (void) fprintf(stderr, gettext("cannot set " 16601133Seschrock "%s property for '%s': volume size exceeds " 16611133Seschrock "amount of available space\n"), 16621133Seschrock cbp->cb_propname, zfs_get_name(zhp)); 16631133Seschrock return (1); 16641133Seschrock } 16651133Seschrock 16661133Seschrock if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, 16671133Seschrock cbp->cb_value) != 0) { 16681133Seschrock (void) fprintf(stderr, gettext("volsize and " 16691133Seschrock "reservation must remain equal\n")); 16701133Seschrock return (1); 16711133Seschrock } 16721133Seschrock } 1673789Sahrens } 1674789Sahrens 1675789Sahrens /* 1676789Sahrens * Do not allow the reservation to be set above the volume size. We do 1677789Sahrens * this here instead of inside libzfs because libzfs violates this rule 1678789Sahrens * internally. 1679789Sahrens */ 1680789Sahrens if (cbp->cb_prop == ZFS_PROP_RESERVATION && 1681789Sahrens zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 1682789Sahrens uint64_t value; 1683789Sahrens uint64_t volsize; 1684789Sahrens 1685789Sahrens volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1686789Sahrens if (strcmp(cbp->cb_value, "none") == 0) 1687789Sahrens value = 0; 1688789Sahrens else 1689789Sahrens verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); 1690789Sahrens 1691789Sahrens if (value > volsize) { 1692789Sahrens (void) fprintf(stderr, gettext("cannot set %s " 1693789Sahrens "for '%s': size is greater than current " 1694789Sahrens "volume size\n"), cbp->cb_propname, 1695789Sahrens zfs_get_name(zhp)); 1696789Sahrens return (-1); 1697789Sahrens } 1698789Sahrens } 1699789Sahrens 17002169Snd150628 if (zfs_prop_set(zhp, cbp->cb_prop, cbp->cb_value) != 0) { 17012169Snd150628 switch (libzfs_errno(g_zfs)) { 17022169Snd150628 case EZFS_MOUNTFAILED: 17032169Snd150628 (void) fprintf(stderr, gettext("property may be set " 17042169Snd150628 "but unable to remount filesystem\n")); 17052169Snd150628 break; 17062169Snd150628 case EZFS_SHAREFAILED: 17072169Snd150628 (void) fprintf(stderr, gettext("property may be set " 17082169Snd150628 "but unable to reshare filesystem\n")); 17092169Snd150628 break; 17102169Snd150628 } 1711789Sahrens return (1); 17122169Snd150628 } 1713789Sahrens ret = 0; 1714789Sahrens error: 1715789Sahrens return (ret); 1716789Sahrens } 1717789Sahrens 1718789Sahrens static int 1719789Sahrens zfs_do_set(int argc, char **argv) 1720789Sahrens { 1721789Sahrens set_cbdata_t cb; 1722789Sahrens 1723789Sahrens /* check for options */ 1724789Sahrens if (argc > 1 && argv[1][0] == '-') { 1725789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1726789Sahrens argv[1][1]); 17272082Seschrock usage(B_FALSE); 1728789Sahrens } 1729789Sahrens 1730789Sahrens /* check number of arguments */ 1731789Sahrens if (argc < 2) { 1732789Sahrens (void) fprintf(stderr, gettext("missing property=value " 1733789Sahrens "argument\n")); 17342082Seschrock usage(B_FALSE); 1735789Sahrens } 1736789Sahrens if (argc < 3) { 1737789Sahrens (void) fprintf(stderr, gettext("missing dataset name\n")); 17382082Seschrock usage(B_FALSE); 1739789Sahrens } 1740789Sahrens 1741789Sahrens /* validate property=value argument */ 1742789Sahrens cb.cb_propname = argv[1]; 1743789Sahrens if ((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) { 1744789Sahrens (void) fprintf(stderr, gettext("missing value in " 1745789Sahrens "property=value argument\n")); 17462082Seschrock usage(B_FALSE); 1747789Sahrens } 1748789Sahrens 1749789Sahrens *cb.cb_value = '\0'; 1750789Sahrens cb.cb_value++; 1751789Sahrens 1752789Sahrens if (*cb.cb_propname == '\0') { 1753789Sahrens (void) fprintf(stderr, 1754789Sahrens gettext("missing property in property=value argument\n")); 17552082Seschrock usage(B_FALSE); 1756789Sahrens } 1757789Sahrens if (*cb.cb_value == '\0') { 1758789Sahrens (void) fprintf(stderr, 1759789Sahrens gettext("missing value in property=value argument\n")); 17602082Seschrock usage(B_FALSE); 1761789Sahrens } 1762789Sahrens 1763789Sahrens /* get the property type */ 1764789Sahrens if ((cb.cb_prop = zfs_name_to_prop(cb.cb_propname)) == 1765789Sahrens ZFS_PROP_INVAL) { 1766789Sahrens (void) fprintf(stderr, 1767789Sahrens gettext("invalid property '%s'\n"), cb.cb_propname); 17682082Seschrock usage(B_FALSE); 1769789Sahrens } 1770789Sahrens 1771789Sahrens /* 1772789Sahrens * Validate that the value is appropriate for this property. We do this 1773789Sahrens * once now so we don't generate multiple errors each time we try to 1774789Sahrens * apply it to a dataset. 1775789Sahrens */ 17762082Seschrock if (zfs_prop_validate(g_zfs, cb.cb_prop, cb.cb_value, NULL) != 0) 1777789Sahrens return (1); 1778789Sahrens 17792082Seschrock return (zfs_for_each(argc - 2, argv + 2, B_FALSE, 1780789Sahrens ZFS_TYPE_ANY, set_callback, &cb)); 1781789Sahrens } 1782789Sahrens 1783789Sahrens /* 1784*2199Sahrens * zfs snapshot [-r] <fs@snap> 1785789Sahrens * 1786789Sahrens * Creates a snapshot with the given name. While functionally equivalent to 1787789Sahrens * 'zfs create', it is a separate command to diffferentiate intent. 1788789Sahrens */ 1789789Sahrens static int 1790789Sahrens zfs_do_snapshot(int argc, char **argv) 1791789Sahrens { 1792*2199Sahrens int recursive = B_FALSE; 1793*2199Sahrens int ret; 1794*2199Sahrens char c; 1795*2199Sahrens 1796789Sahrens /* check options */ 1797*2199Sahrens while ((c = getopt(argc, argv, ":r")) != -1) { 1798*2199Sahrens switch (c) { 1799*2199Sahrens case 'r': 1800*2199Sahrens recursive = B_TRUE; 1801*2199Sahrens break; 1802*2199Sahrens case '?': 1803*2199Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1804*2199Sahrens optopt); 1805*2199Sahrens usage(B_FALSE); 1806*2199Sahrens } 1807789Sahrens } 1808789Sahrens 1809*2199Sahrens argc -= optind; 1810*2199Sahrens argv += optind; 1811*2199Sahrens 1812789Sahrens /* check number of arguments */ 1813*2199Sahrens if (argc < 1) { 1814789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 18152082Seschrock usage(B_FALSE); 1816789Sahrens } 1817*2199Sahrens if (argc > 1) { 1818789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 18192082Seschrock usage(B_FALSE); 1820789Sahrens } 1821789Sahrens 1822*2199Sahrens ret = zfs_snapshot(g_zfs, argv[0], recursive); 1823*2199Sahrens if (ret && recursive) 1824*2199Sahrens (void) fprintf(stderr, gettext("no snapshots were created\n")); 1825*2199Sahrens return (ret != 0); 1826*2199Sahrens 1827789Sahrens } 1828789Sahrens 1829789Sahrens /* 18301749Sahrens * zfs send [-i <fs@snap>] <fs@snap> 1831789Sahrens * 1832789Sahrens * Send a backup stream to stdout. 1833789Sahrens */ 1834789Sahrens static int 18351749Sahrens zfs_do_send(int argc, char **argv) 1836789Sahrens { 1837789Sahrens char *fromname = NULL; 1838789Sahrens zfs_handle_t *zhp_from = NULL, *zhp_to; 1839789Sahrens int c, err; 1840789Sahrens 1841789Sahrens /* check options */ 1842789Sahrens while ((c = getopt(argc, argv, ":i:")) != -1) { 1843789Sahrens switch (c) { 1844789Sahrens case 'i': 1845789Sahrens fromname = optarg; 1846789Sahrens break; 1847789Sahrens case ':': 1848789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1849789Sahrens "'%c' option\n"), optopt); 18502082Seschrock usage(B_FALSE); 1851789Sahrens break; 1852789Sahrens case '?': 1853789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1854789Sahrens optopt); 18552082Seschrock usage(B_FALSE); 1856789Sahrens } 1857789Sahrens } 1858789Sahrens 1859789Sahrens argc -= optind; 1860789Sahrens argv += optind; 1861789Sahrens 1862789Sahrens /* check number of arguments */ 1863789Sahrens if (argc < 1) { 1864789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 18652082Seschrock usage(B_FALSE); 1866789Sahrens } 1867789Sahrens if (argc > 1) { 1868789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 18692082Seschrock usage(B_FALSE); 1870789Sahrens } 1871789Sahrens 1872789Sahrens if (isatty(STDOUT_FILENO)) { 1873789Sahrens (void) fprintf(stderr, 18741749Sahrens gettext("Error: Stream can not be written " 1875789Sahrens "to a terminal.\n" 1876789Sahrens "You must redirect standard output.\n")); 1877789Sahrens return (1); 1878789Sahrens } 1879789Sahrens 1880789Sahrens if (fromname) { 18812082Seschrock if ((zhp_from = zfs_open(g_zfs, fromname, 18822082Seschrock ZFS_TYPE_SNAPSHOT)) == NULL) 1883789Sahrens return (1); 1884789Sahrens } 18852082Seschrock if ((zhp_to = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1886789Sahrens return (1); 1887789Sahrens 18881749Sahrens err = zfs_send(zhp_to, zhp_from); 1889789Sahrens 1890789Sahrens if (zhp_from) 1891789Sahrens zfs_close(zhp_from); 1892789Sahrens zfs_close(zhp_to); 1893789Sahrens 1894789Sahrens return (err != 0); 1895789Sahrens } 1896789Sahrens 1897789Sahrens /* 18981749Sahrens * zfs receive <fs@snap> 1899789Sahrens * 1900789Sahrens * Restore a backup stream from stdin. 1901789Sahrens */ 1902789Sahrens static int 19031749Sahrens zfs_do_receive(int argc, char **argv) 1904789Sahrens { 1905789Sahrens int c, err; 19062082Seschrock boolean_t isprefix = B_FALSE; 19072082Seschrock boolean_t dryrun = B_FALSE; 19082082Seschrock boolean_t verbose = B_FALSE; 1909789Sahrens 1910789Sahrens /* check options */ 1911789Sahrens while ((c = getopt(argc, argv, ":dnv")) != -1) { 1912789Sahrens switch (c) { 1913789Sahrens case 'd': 19142082Seschrock isprefix = B_TRUE; 1915789Sahrens break; 1916789Sahrens case 'n': 19172082Seschrock dryrun = B_TRUE; 1918789Sahrens break; 1919789Sahrens case 'v': 19202082Seschrock verbose = B_TRUE; 1921789Sahrens break; 1922789Sahrens case ':': 1923789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1924789Sahrens "'%c' option\n"), optopt); 19252082Seschrock usage(B_FALSE); 1926789Sahrens break; 1927789Sahrens case '?': 1928789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1929789Sahrens optopt); 19302082Seschrock usage(B_FALSE); 1931789Sahrens } 1932789Sahrens } 1933789Sahrens 1934789Sahrens argc -= optind; 1935789Sahrens argv += optind; 1936789Sahrens 1937789Sahrens /* check number of arguments */ 1938789Sahrens if (argc < 1) { 1939789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 19402082Seschrock usage(B_FALSE); 1941789Sahrens } 1942789Sahrens if (argc > 1) { 1943789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 19442082Seschrock usage(B_FALSE); 1945789Sahrens } 1946789Sahrens 1947789Sahrens if (isatty(STDIN_FILENO)) { 1948789Sahrens (void) fprintf(stderr, 1949789Sahrens gettext("Error: Backup stream can not be read " 1950789Sahrens "from a terminal.\n" 1951789Sahrens "You must redirect standard input.\n")); 1952789Sahrens return (1); 1953789Sahrens } 1954789Sahrens 19552082Seschrock err = zfs_receive(g_zfs, argv[0], isprefix, verbose, dryrun); 1956789Sahrens return (err != 0); 1957789Sahrens } 1958789Sahrens 19591356Seschrock typedef struct get_all_cbdata { 19601356Seschrock zfs_handle_t **cb_handles; 19611356Seschrock size_t cb_alloc; 19621356Seschrock size_t cb_used; 19631356Seschrock } get_all_cbdata_t; 19641356Seschrock 19651356Seschrock static int 19661356Seschrock get_one_filesystem(zfs_handle_t *zhp, void *data) 19671356Seschrock { 19681356Seschrock get_all_cbdata_t *cbp = data; 19691356Seschrock 19701356Seschrock /* 19711356Seschrock * Skip any zvols 19721356Seschrock */ 19731356Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 19741356Seschrock zfs_close(zhp); 19751356Seschrock return (0); 19761356Seschrock } 19771356Seschrock 19781356Seschrock if (cbp->cb_alloc == cbp->cb_used) { 19791356Seschrock zfs_handle_t **handles; 19801356Seschrock 19811356Seschrock if (cbp->cb_alloc == 0) 19821356Seschrock cbp->cb_alloc = 64; 19831356Seschrock else 19841356Seschrock cbp->cb_alloc *= 2; 19851356Seschrock 19861356Seschrock handles = safe_malloc(cbp->cb_alloc * sizeof (void *)); 19871356Seschrock 19881356Seschrock if (cbp->cb_handles) { 19891356Seschrock bcopy(cbp->cb_handles, handles, 19901356Seschrock cbp->cb_used * sizeof (void *)); 19911356Seschrock free(cbp->cb_handles); 19921356Seschrock } 19931356Seschrock 19941356Seschrock cbp->cb_handles = handles; 19951356Seschrock } 19961356Seschrock 19971356Seschrock cbp->cb_handles[cbp->cb_used++] = zhp; 19981356Seschrock 19991356Seschrock return (zfs_iter_filesystems(zhp, get_one_filesystem, data)); 20001356Seschrock } 20011356Seschrock 20021356Seschrock static void 20031356Seschrock get_all_filesystems(zfs_handle_t ***fslist, size_t *count) 20041356Seschrock { 20051356Seschrock get_all_cbdata_t cb = { 0 }; 20061356Seschrock 20072082Seschrock (void) zfs_iter_root(g_zfs, get_one_filesystem, &cb); 20081356Seschrock 20091356Seschrock *fslist = cb.cb_handles; 20101356Seschrock *count = cb.cb_used; 20111356Seschrock } 20121356Seschrock 20131356Seschrock static int 20141356Seschrock mountpoint_compare(const void *a, const void *b) 20151356Seschrock { 20161356Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 20171356Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 20181356Seschrock char mounta[MAXPATHLEN]; 20191356Seschrock char mountb[MAXPATHLEN]; 20201356Seschrock 20211356Seschrock verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 20222082Seschrock sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 20231356Seschrock verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 20242082Seschrock sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 20251356Seschrock 20261356Seschrock return (strcmp(mounta, mountb)); 20271356Seschrock } 2028789Sahrens 2029789Sahrens /* 2030789Sahrens * Generic callback for sharing or mounting filesystems. Because the code is so 2031789Sahrens * similar, we have a common function with an extra parameter to determine which 2032789Sahrens * mode we are using. 2033789Sahrens */ 2034789Sahrens #define OP_SHARE 0x1 2035789Sahrens #define OP_MOUNT 0x2 2036789Sahrens 2037789Sahrens typedef struct share_mount_cbdata { 2038789Sahrens int cb_type; 2039789Sahrens int cb_explicit; 2040789Sahrens int cb_flags; 2041789Sahrens const char *cb_options; 2042789Sahrens } share_mount_cbdata_t; 2043789Sahrens 2044789Sahrens /* 2045789Sahrens * Share or mount the filesystem. 2046789Sahrens */ 2047789Sahrens static int 2048789Sahrens share_mount_callback(zfs_handle_t *zhp, void *data) 2049789Sahrens { 2050789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2051789Sahrens char shareopts[ZFS_MAXPROPLEN]; 2052789Sahrens share_mount_cbdata_t *cbp = data; 2053789Sahrens const char *cmdname = cbp->cb_type == OP_SHARE ? "share" : "mount"; 2054789Sahrens struct mnttab mnt; 2055789Sahrens uint64_t zoned; 2056789Sahrens 2057789Sahrens if (cbp->cb_options == NULL) 2058789Sahrens mnt.mnt_mntopts = ""; 2059789Sahrens else 2060789Sahrens mnt.mnt_mntopts = (char *)cbp->cb_options; 2061789Sahrens 2062789Sahrens /* 2063789Sahrens * Check to make sure we can mount/share this dataset. If we are in the 2064789Sahrens * global zone and the filesystem is exported to a local zone, or if we 2065789Sahrens * are in a local zone and the filesystem is not exported, then it is an 2066789Sahrens * error. 2067789Sahrens */ 2068789Sahrens zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2069789Sahrens 2070789Sahrens if (zoned && getzoneid() == GLOBAL_ZONEID) { 2071789Sahrens if (!cbp->cb_explicit) 2072789Sahrens return (0); 2073789Sahrens 2074789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': dataset is " 2075789Sahrens "exported to a local zone\n"), cmdname, zfs_get_name(zhp)); 2076789Sahrens return (1); 2077789Sahrens 2078789Sahrens } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 2079789Sahrens if (!cbp->cb_explicit) 2080789Sahrens return (0); 2081789Sahrens 2082789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': permission " 2083789Sahrens "denied\n"), cmdname, zfs_get_name(zhp)); 2084789Sahrens return (1); 2085789Sahrens } 2086789Sahrens 2087789Sahrens /* 2088789Sahrens * Inore any filesystems which don't apply to us. This includes those 2089789Sahrens * with a legacy mountpoint, or those with legacy share options. 2090789Sahrens */ 2091789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 20922082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); 2093789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 20942082Seschrock sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 2095789Sahrens 2096789Sahrens if (cbp->cb_type == OP_SHARE) { 2097789Sahrens if (strcmp(shareopts, "off") == 0) { 2098789Sahrens if (!cbp->cb_explicit) 2099789Sahrens return (0); 2100789Sahrens 2101789Sahrens (void) fprintf(stderr, gettext("cannot share '%s': " 2102789Sahrens "legacy share\n"), zfs_get_name(zhp)); 2103789Sahrens (void) fprintf(stderr, gettext("use share(1M) to " 2104789Sahrens "share this filesystem\n")); 2105789Sahrens return (1); 2106789Sahrens } 2107789Sahrens } 2108789Sahrens 2109789Sahrens /* 2110789Sahrens * We cannot share or mount legacy filesystems. If the shareopts is 2111789Sahrens * non-legacy but the mountpoint is legacy, we treat it as a legacy 2112789Sahrens * share. 2113789Sahrens */ 2114789Sahrens if (strcmp(mountpoint, "legacy") == 0) { 2115789Sahrens if (!cbp->cb_explicit) 2116789Sahrens return (0); 2117789Sahrens 2118789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': " 2119789Sahrens "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 2120789Sahrens (void) fprintf(stderr, gettext("use %s to " 2121789Sahrens "%s this filesystem\n"), cbp->cb_type == OP_SHARE ? 2122789Sahrens "share(1M)" : "mount(1M)", cmdname); 2123789Sahrens return (1); 2124789Sahrens } 2125789Sahrens 2126789Sahrens if (strcmp(mountpoint, "none") == 0) { 2127789Sahrens if (!cbp->cb_explicit) 2128789Sahrens return (0); 2129789Sahrens 2130789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': no " 2131789Sahrens "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 2132789Sahrens return (1); 2133789Sahrens } 2134789Sahrens 2135789Sahrens /* 2136789Sahrens * At this point, we have verified that the mountpoint and/or shareopts 2137789Sahrens * are appropriate for auto management. Determine if the filesystem is 2138789Sahrens * currently mounted or shared, and abort if this is an explicit 2139789Sahrens * request. 2140789Sahrens */ 2141789Sahrens switch (cbp->cb_type) { 2142789Sahrens case OP_SHARE: 2143789Sahrens if (zfs_is_shared(zhp, NULL)) { 2144789Sahrens if (cbp->cb_explicit) { 2145789Sahrens (void) fprintf(stderr, gettext("cannot share " 2146789Sahrens "'%s': filesystem already shared\n"), 2147789Sahrens zfs_get_name(zhp)); 2148789Sahrens return (1); 2149789Sahrens } else { 2150789Sahrens return (0); 2151789Sahrens } 2152789Sahrens } 2153789Sahrens break; 2154789Sahrens 2155789Sahrens case OP_MOUNT: 2156789Sahrens if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 2157789Sahrens zfs_is_mounted(zhp, NULL)) { 2158789Sahrens if (cbp->cb_explicit) { 2159789Sahrens (void) fprintf(stderr, gettext("cannot mount " 2160789Sahrens "'%s': filesystem already mounted\n"), 2161789Sahrens zfs_get_name(zhp)); 2162789Sahrens return (1); 2163789Sahrens } else { 2164789Sahrens return (0); 2165789Sahrens } 2166789Sahrens } 2167789Sahrens break; 2168789Sahrens } 2169789Sahrens 2170789Sahrens /* 2171789Sahrens * Mount and optionally share the filesystem. 2172789Sahrens */ 2173789Sahrens switch (cbp->cb_type) { 2174789Sahrens case OP_SHARE: 2175789Sahrens { 2176789Sahrens if (!zfs_is_mounted(zhp, NULL) && 2177789Sahrens zfs_mount(zhp, NULL, 0) != 0) 2178789Sahrens return (1); 2179789Sahrens 2180789Sahrens if (zfs_share(zhp) != 0) 2181789Sahrens return (1); 2182789Sahrens } 2183789Sahrens break; 2184789Sahrens 2185789Sahrens case OP_MOUNT: 2186789Sahrens if (zfs_mount(zhp, cbp->cb_options, cbp->cb_flags) != 0) 2187789Sahrens return (1); 2188789Sahrens break; 2189789Sahrens } 2190789Sahrens 2191789Sahrens return (0); 2192789Sahrens } 2193789Sahrens 2194789Sahrens static int 2195789Sahrens share_or_mount(int type, int argc, char **argv) 2196789Sahrens { 2197789Sahrens int do_all = 0; 2198789Sahrens int c, ret; 2199789Sahrens share_mount_cbdata_t cb = { 0 }; 2200789Sahrens 2201789Sahrens cb.cb_type = type; 2202789Sahrens 2203789Sahrens /* check options */ 2204789Sahrens while ((c = getopt(argc, argv, type == OP_MOUNT ? ":ao:O" : "a")) 2205789Sahrens != -1) { 2206789Sahrens switch (c) { 2207789Sahrens case 'a': 2208789Sahrens do_all = 1; 2209789Sahrens break; 2210789Sahrens case 'o': 2211789Sahrens cb.cb_options = optarg; 2212789Sahrens break; 2213789Sahrens case 'O': 2214789Sahrens cb.cb_flags |= MS_OVERLAY; 2215789Sahrens break; 2216789Sahrens case ':': 2217789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2218789Sahrens "'%c' option\n"), optopt); 22192082Seschrock usage(B_FALSE); 2220789Sahrens break; 2221789Sahrens case '?': 2222789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2223789Sahrens optopt); 22242082Seschrock usage(B_FALSE); 2225789Sahrens } 2226789Sahrens } 2227789Sahrens 2228789Sahrens argc -= optind; 2229789Sahrens argv += optind; 2230789Sahrens 2231789Sahrens /* check number of arguments */ 2232789Sahrens if (do_all) { 22331356Seschrock zfs_handle_t **fslist = NULL; 22341356Seschrock size_t i, count = 0; 22351356Seschrock 2236789Sahrens if (argc != 0) { 2237789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 22382082Seschrock usage(B_FALSE); 2239789Sahrens } 2240789Sahrens 22411356Seschrock get_all_filesystems(&fslist, &count); 22421356Seschrock 22431356Seschrock if (count == 0) 22441356Seschrock return (0); 22451356Seschrock 22461356Seschrock qsort(fslist, count, sizeof (void *), mountpoint_compare); 22471356Seschrock 22481356Seschrock for (i = 0; i < count; i++) { 22491356Seschrock if ((ret = share_mount_callback(fslist[i], &cb)) != 0) 22501356Seschrock break; 22511356Seschrock } 22521356Seschrock 22531356Seschrock for (i = 0; i < count; i++) 22541356Seschrock zfs_close(fslist[i]); 22551356Seschrock 22561356Seschrock free(fslist); 2257789Sahrens } else if (argc == 0) { 2258789Sahrens struct mnttab entry; 2259789Sahrens 2260789Sahrens if (type == OP_SHARE) { 2261789Sahrens (void) fprintf(stderr, gettext("missing filesystem " 2262789Sahrens "argument\n")); 22632082Seschrock usage(B_FALSE); 2264789Sahrens } 2265789Sahrens 2266789Sahrens /* 2267789Sahrens * When mount is given no arguments, go through /etc/mnttab and 2268789Sahrens * display any active ZFS mounts. We hide any snapshots, since 2269789Sahrens * they are controlled automatically. 2270789Sahrens */ 2271789Sahrens rewind(mnttab_file); 2272789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2273789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 2274789Sahrens strchr(entry.mnt_special, '@') != NULL) 2275789Sahrens continue; 2276789Sahrens 2277789Sahrens (void) printf("%-30s %s\n", entry.mnt_special, 2278789Sahrens entry.mnt_mountp); 2279789Sahrens } 2280789Sahrens 2281789Sahrens ret = 0; 2282789Sahrens } else { 2283789Sahrens zfs_handle_t *zhp; 2284789Sahrens 2285789Sahrens if (argc > 1) { 2286789Sahrens (void) fprintf(stderr, 2287789Sahrens gettext("too many arguments\n")); 22882082Seschrock usage(B_FALSE); 2289789Sahrens } 2290789Sahrens 22912082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], 22922082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2293789Sahrens ret = 1; 2294789Sahrens else { 22952082Seschrock cb.cb_explicit = B_TRUE; 2296789Sahrens ret = share_mount_callback(zhp, &cb); 2297789Sahrens zfs_close(zhp); 2298789Sahrens } 2299789Sahrens } 2300789Sahrens 2301789Sahrens return (ret); 2302789Sahrens } 2303789Sahrens 2304789Sahrens /* 2305789Sahrens * zfs mount -a 2306789Sahrens * zfs mount filesystem 2307789Sahrens * 2308789Sahrens * Mount all filesystems, or mount the given filesystem. 2309789Sahrens */ 2310789Sahrens static int 2311789Sahrens zfs_do_mount(int argc, char **argv) 2312789Sahrens { 2313789Sahrens return (share_or_mount(OP_MOUNT, argc, argv)); 2314789Sahrens } 2315789Sahrens 2316789Sahrens /* 2317789Sahrens * zfs share -a 2318789Sahrens * zfs share filesystem 2319789Sahrens * 2320789Sahrens * Share all filesystems, or share the given filesystem. 2321789Sahrens */ 2322789Sahrens static int 2323789Sahrens zfs_do_share(int argc, char **argv) 2324789Sahrens { 2325789Sahrens return (share_or_mount(OP_SHARE, argc, argv)); 2326789Sahrens } 2327789Sahrens 2328789Sahrens typedef struct unshare_unmount_node { 2329789Sahrens zfs_handle_t *un_zhp; 2330789Sahrens char *un_mountp; 2331789Sahrens uu_avl_node_t un_avlnode; 2332789Sahrens } unshare_unmount_node_t; 2333789Sahrens 2334789Sahrens /* ARGSUSED */ 2335789Sahrens static int 2336789Sahrens unshare_unmount_compare(const void *larg, const void *rarg, void *unused) 2337789Sahrens { 2338789Sahrens const unshare_unmount_node_t *l = larg; 2339789Sahrens const unshare_unmount_node_t *r = rarg; 2340789Sahrens 2341789Sahrens return (strcmp(l->un_mountp, r->un_mountp)); 2342789Sahrens } 2343789Sahrens 2344789Sahrens /* 2345789Sahrens * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 2346789Sahrens * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 2347789Sahrens * and unmount it appropriately. 2348789Sahrens */ 2349789Sahrens static int 23502082Seschrock unshare_unmount_path(int type, char *path, int flags, boolean_t is_manual) 2351789Sahrens { 2352789Sahrens zfs_handle_t *zhp; 2353789Sahrens int ret; 2354789Sahrens struct stat64 statbuf; 2355789Sahrens struct extmnttab entry; 2356789Sahrens const char *cmdname = (type == OP_SHARE) ? "unshare" : "unmount"; 2357789Sahrens char property[ZFS_MAXPROPLEN]; 2358789Sahrens 2359789Sahrens /* 2360789Sahrens * Search for the path in /etc/mnttab. Rather than looking for the 2361789Sahrens * specific path, which can be fooled by non-standard paths (i.e. ".." 2362789Sahrens * or "//"), we stat() the path and search for the corresponding 2363789Sahrens * (major,minor) device pair. 2364789Sahrens */ 2365789Sahrens if (stat64(path, &statbuf) != 0) { 2366789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 2367789Sahrens cmdname, path, strerror(errno)); 2368789Sahrens return (1); 2369789Sahrens } 2370789Sahrens 2371789Sahrens /* 2372789Sahrens * Search for the given (major,minor) pair in the mount table. 2373789Sahrens */ 2374789Sahrens rewind(mnttab_file); 2375789Sahrens while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 2376789Sahrens if (entry.mnt_major == major(statbuf.st_dev) && 2377789Sahrens entry.mnt_minor == minor(statbuf.st_dev)) 2378789Sahrens break; 2379789Sahrens } 2380789Sahrens if (ret != 0) { 2381789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not " 2382789Sahrens "currently mounted\n"), cmdname, path); 2383789Sahrens return (1); 2384789Sahrens } 2385789Sahrens 2386789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 2387789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 2388789Sahrens "filesystem\n"), cmdname, path); 2389789Sahrens return (1); 2390789Sahrens } 2391789Sahrens 23922082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 23932082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2394789Sahrens return (1); 2395789Sahrens 2396789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2397789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 23982082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2399789Sahrens 2400789Sahrens if (type == OP_SHARE) { 2401789Sahrens if (strcmp(property, "off") == 0) { 2402789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2403789Sahrens "'%s': legacy share\n"), path); 2404789Sahrens (void) fprintf(stderr, gettext("use " 2405789Sahrens "unshare(1M) to unshare this filesystem\n")); 2406789Sahrens ret = 1; 2407789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2408789Sahrens (void) fprintf(stderr, gettext("cannot unshare '%s': " 2409789Sahrens "not currently shared\n"), path); 2410789Sahrens ret = 1; 2411789Sahrens } else { 2412789Sahrens ret = zfs_unshareall(zhp); 2413789Sahrens } 2414789Sahrens } else { 24151264Slling if (is_manual) { 24161264Slling ret = zfs_unmount(zhp, NULL, flags); 24171264Slling } else if (strcmp(property, "legacy") == 0) { 24181264Slling (void) fprintf(stderr, gettext("cannot unmount " 24191264Slling "'%s': legacy mountpoint\n"), 24201264Slling zfs_get_name(zhp)); 24211264Slling (void) fprintf(stderr, gettext("use umount(1M) " 24221264Slling "to unmount this filesystem\n")); 24231264Slling ret = 1; 2424789Sahrens } else { 2425789Sahrens ret = zfs_unmountall(zhp, flags); 2426789Sahrens } 2427789Sahrens } 2428789Sahrens 2429789Sahrens zfs_close(zhp); 2430789Sahrens 2431789Sahrens return (ret != 0); 2432789Sahrens } 2433789Sahrens 2434789Sahrens /* 2435789Sahrens * Generic callback for unsharing or unmounting a filesystem. 2436789Sahrens */ 2437789Sahrens static int 2438789Sahrens unshare_unmount(int type, int argc, char **argv) 2439789Sahrens { 2440789Sahrens int do_all = 0; 2441789Sahrens int flags = 0; 2442789Sahrens int ret = 0; 2443789Sahrens int c; 2444789Sahrens zfs_handle_t *zhp; 2445789Sahrens char property[ZFS_MAXPROPLEN]; 2446789Sahrens 2447789Sahrens /* check options */ 2448789Sahrens while ((c = getopt(argc, argv, type == OP_SHARE ? "a" : "af")) != -1) { 2449789Sahrens switch (c) { 2450789Sahrens case 'a': 2451789Sahrens do_all = 1; 2452789Sahrens break; 2453789Sahrens case 'f': 2454789Sahrens flags = MS_FORCE; 2455789Sahrens break; 2456789Sahrens case '?': 2457789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2458789Sahrens optopt); 24592082Seschrock usage(B_FALSE); 2460789Sahrens } 2461789Sahrens } 2462789Sahrens 2463789Sahrens argc -= optind; 2464789Sahrens argv += optind; 2465789Sahrens 2466789Sahrens /* ensure correct number of arguments */ 2467789Sahrens if (do_all) { 2468789Sahrens if (argc != 0) { 2469789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 24702082Seschrock usage(B_FALSE); 2471789Sahrens } 2472789Sahrens } else if (argc != 1) { 2473789Sahrens if (argc == 0) 2474789Sahrens (void) fprintf(stderr, 2475789Sahrens gettext("missing filesystem argument\n")); 2476789Sahrens else 2477789Sahrens (void) fprintf(stderr, 2478789Sahrens gettext("too many arguments\n")); 24792082Seschrock usage(B_FALSE); 2480789Sahrens } 2481789Sahrens 2482789Sahrens if (do_all) { 2483789Sahrens /* 2484789Sahrens * We could make use of zfs_for_each() to walk all datasets in 2485789Sahrens * the system, but this would be very inefficient, especially 2486789Sahrens * since we would have to linearly search /etc/mnttab for each 2487789Sahrens * one. Instead, do one pass through /etc/mnttab looking for 2488789Sahrens * zfs entries and call zfs_unmount() for each one. 2489789Sahrens * 2490789Sahrens * Things get a little tricky if the administrator has created 2491789Sahrens * mountpoints beneath other ZFS filesystems. In this case, we 2492789Sahrens * have to unmount the deepest filesystems first. To accomplish 2493789Sahrens * this, we place all the mountpoints in an AVL tree sorted by 2494789Sahrens * the special type (dataset name), and walk the result in 2495789Sahrens * reverse to make sure to get any snapshots first. 2496789Sahrens */ 2497789Sahrens struct mnttab entry; 2498789Sahrens uu_avl_pool_t *pool; 2499789Sahrens uu_avl_t *tree; 2500789Sahrens unshare_unmount_node_t *node; 2501789Sahrens uu_avl_index_t idx; 2502789Sahrens uu_avl_walk_t *walk; 2503789Sahrens 2504789Sahrens if ((pool = uu_avl_pool_create("unmount_pool", 2505789Sahrens sizeof (unshare_unmount_node_t), 2506789Sahrens offsetof(unshare_unmount_node_t, un_avlnode), 2507789Sahrens unshare_unmount_compare, 2508789Sahrens UU_DEFAULT)) == NULL) { 2509789Sahrens (void) fprintf(stderr, gettext("internal error: " 2510789Sahrens "out of memory\n")); 2511789Sahrens exit(1); 2512789Sahrens } 2513789Sahrens 2514789Sahrens if ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL) { 2515789Sahrens (void) fprintf(stderr, gettext("internal error: " 2516789Sahrens "out of memory\n")); 2517789Sahrens exit(1); 2518789Sahrens } 2519789Sahrens 2520789Sahrens rewind(mnttab_file); 2521789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2522789Sahrens 2523789Sahrens /* ignore non-ZFS entries */ 2524789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 2525789Sahrens continue; 2526789Sahrens 2527789Sahrens /* ignore snapshots */ 2528789Sahrens if (strchr(entry.mnt_special, '@') != NULL) 2529789Sahrens continue; 2530789Sahrens 25312082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 2532789Sahrens ZFS_TYPE_FILESYSTEM)) == NULL) { 2533789Sahrens ret = 1; 2534789Sahrens continue; 2535789Sahrens } 2536789Sahrens 2537789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2538789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 2539789Sahrens property, sizeof (property), NULL, NULL, 25402082Seschrock 0, B_FALSE) == 0); 2541789Sahrens 2542789Sahrens /* Ignore legacy mounts and shares */ 2543789Sahrens if ((type == OP_SHARE && 2544789Sahrens strcmp(property, "off") == 0) || 2545789Sahrens (type == OP_MOUNT && 2546789Sahrens strcmp(property, "legacy") == 0)) { 2547789Sahrens zfs_close(zhp); 2548789Sahrens continue; 2549789Sahrens } 2550789Sahrens 2551789Sahrens node = safe_malloc(sizeof (unshare_unmount_node_t)); 2552789Sahrens node->un_zhp = zhp; 2553789Sahrens 2554789Sahrens if ((node->un_mountp = strdup(entry.mnt_mountp)) == 2555789Sahrens NULL) { 2556789Sahrens (void) fprintf(stderr, gettext("internal error:" 2557789Sahrens " out of memory\n")); 2558789Sahrens exit(1); 2559789Sahrens } 2560789Sahrens 2561789Sahrens uu_avl_node_init(node, &node->un_avlnode, pool); 2562789Sahrens 2563789Sahrens if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 2564789Sahrens uu_avl_insert(tree, node, idx); 2565789Sahrens } else { 2566789Sahrens zfs_close(node->un_zhp); 2567789Sahrens free(node->un_mountp); 2568789Sahrens free(node); 2569789Sahrens } 2570789Sahrens } 2571789Sahrens 2572789Sahrens /* 2573789Sahrens * Walk the AVL tree in reverse, unmounting each filesystem and 2574789Sahrens * removing it from the AVL tree in the process. 2575789Sahrens */ 2576789Sahrens if ((walk = uu_avl_walk_start(tree, 2577789Sahrens UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) { 2578789Sahrens (void) fprintf(stderr, 2579789Sahrens gettext("internal error: out of memory")); 2580789Sahrens exit(1); 2581789Sahrens } 2582789Sahrens 2583789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 2584789Sahrens uu_avl_remove(tree, node); 2585789Sahrens 2586789Sahrens switch (type) { 2587789Sahrens case OP_SHARE: 2588789Sahrens if (zfs_unshare(node->un_zhp, 2589789Sahrens node->un_mountp) != 0) 2590789Sahrens ret = 1; 2591789Sahrens break; 2592789Sahrens 2593789Sahrens case OP_MOUNT: 2594789Sahrens if (zfs_unmount(node->un_zhp, 2595789Sahrens node->un_mountp, flags) != 0) 2596789Sahrens ret = 1; 2597789Sahrens break; 2598789Sahrens } 2599789Sahrens 2600789Sahrens zfs_close(node->un_zhp); 2601789Sahrens free(node->un_mountp); 2602789Sahrens free(node); 2603789Sahrens } 2604789Sahrens 2605789Sahrens uu_avl_walk_end(walk); 2606789Sahrens uu_avl_destroy(tree); 2607789Sahrens uu_avl_pool_destroy(pool); 2608789Sahrens } else { 2609789Sahrens /* 2610789Sahrens * We have an argument, but it may be a full path or a ZFS 2611789Sahrens * filesystem. Pass full paths off to unmount_path() (shared by 2612789Sahrens * manual_unmount), otherwise open the filesystem and pass to 2613789Sahrens * zfs_unmount(). 2614789Sahrens */ 2615789Sahrens if (argv[0][0] == '/') 2616789Sahrens return (unshare_unmount_path(type, argv[0], 26172082Seschrock flags, B_FALSE)); 26182082Seschrock 26192082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], 26202082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2621789Sahrens return (1); 2622789Sahrens 2623789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2624789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 26252082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2626789Sahrens 2627789Sahrens switch (type) { 2628789Sahrens case OP_SHARE: 2629789Sahrens if (strcmp(property, "off") == 0) { 2630789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2631789Sahrens "'%s': legacy share\n"), zfs_get_name(zhp)); 2632789Sahrens (void) fprintf(stderr, gettext("use unshare(1M)" 2633789Sahrens " to unshare this filesystem\n")); 2634789Sahrens ret = 1; 2635789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2636789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2637789Sahrens "'%s': not currently shared\n"), 2638789Sahrens zfs_get_name(zhp)); 2639789Sahrens ret = 1; 2640789Sahrens } else if (zfs_unshareall(zhp) != 0) { 2641789Sahrens ret = 1; 2642789Sahrens } 2643789Sahrens break; 2644789Sahrens 2645789Sahrens case OP_MOUNT: 2646789Sahrens if (strcmp(property, "legacy") == 0) { 2647789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2648789Sahrens "'%s': legacy mountpoint\n"), 2649789Sahrens zfs_get_name(zhp)); 2650789Sahrens (void) fprintf(stderr, gettext("use umount(1M) " 2651789Sahrens "to unmount this filesystem\n")); 2652789Sahrens ret = 1; 2653789Sahrens } else if (!zfs_is_mounted(zhp, NULL)) { 2654789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2655789Sahrens "'%s': not currently mounted\n"), 2656789Sahrens zfs_get_name(zhp)); 2657789Sahrens ret = 1; 2658789Sahrens } else if (zfs_unmountall(zhp, flags) != 0) { 2659789Sahrens ret = 1; 2660789Sahrens } 2661789Sahrens } 2662789Sahrens 2663789Sahrens zfs_close(zhp); 2664789Sahrens } 2665789Sahrens 2666789Sahrens return (ret); 2667789Sahrens } 2668789Sahrens 2669789Sahrens /* 2670789Sahrens * zfs unmount -a 2671789Sahrens * zfs unmount filesystem 2672789Sahrens * 2673789Sahrens * Unmount all filesystems, or a specific ZFS filesystem. 2674789Sahrens */ 2675789Sahrens static int 2676789Sahrens zfs_do_unmount(int argc, char **argv) 2677789Sahrens { 2678789Sahrens return (unshare_unmount(OP_MOUNT, argc, argv)); 2679789Sahrens } 2680789Sahrens 2681789Sahrens /* 2682789Sahrens * zfs unshare -a 2683789Sahrens * zfs unshare filesystem 2684789Sahrens * 2685789Sahrens * Unshare all filesystems, or a specific ZFS filesystem. 2686789Sahrens */ 2687789Sahrens static int 2688789Sahrens zfs_do_unshare(int argc, char **argv) 2689789Sahrens { 2690789Sahrens return (unshare_unmount(OP_SHARE, argc, argv)); 2691789Sahrens } 2692789Sahrens 2693789Sahrens /* 2694789Sahrens * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 2695789Sahrens * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 2696789Sahrens */ 2697789Sahrens static int 2698789Sahrens manual_mount(int argc, char **argv) 2699789Sahrens { 2700789Sahrens zfs_handle_t *zhp; 2701789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2702789Sahrens char mntopts[MNT_LINE_MAX] = { '\0' }; 2703789Sahrens int ret; 2704789Sahrens int c; 2705789Sahrens int flags = 0; 2706789Sahrens char *dataset, *path; 2707789Sahrens 2708789Sahrens /* check options */ 27091544Seschrock while ((c = getopt(argc, argv, ":mo:O")) != -1) { 2710789Sahrens switch (c) { 2711789Sahrens case 'o': 2712789Sahrens (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 2713789Sahrens break; 2714789Sahrens case 'O': 2715789Sahrens flags |= MS_OVERLAY; 2716789Sahrens break; 27171544Seschrock case 'm': 27181544Seschrock flags |= MS_NOMNTTAB; 27191544Seschrock break; 2720789Sahrens case ':': 2721789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2722789Sahrens "'%c' option\n"), optopt); 27232082Seschrock usage(B_FALSE); 2724789Sahrens break; 2725789Sahrens case '?': 2726789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2727789Sahrens optopt); 2728789Sahrens (void) fprintf(stderr, gettext("usage: mount [-o opts] " 2729789Sahrens "<path>\n")); 2730789Sahrens return (2); 2731789Sahrens } 2732789Sahrens } 2733789Sahrens 2734789Sahrens argc -= optind; 2735789Sahrens argv += optind; 2736789Sahrens 2737789Sahrens /* check that we only have two arguments */ 2738789Sahrens if (argc != 2) { 2739789Sahrens if (argc == 0) 2740789Sahrens (void) fprintf(stderr, gettext("missing dataset " 2741789Sahrens "argument\n")); 2742789Sahrens else if (argc == 1) 2743789Sahrens (void) fprintf(stderr, 2744789Sahrens gettext("missing mountpoint argument\n")); 2745789Sahrens else 2746789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2747789Sahrens (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 2748789Sahrens return (2); 2749789Sahrens } 2750789Sahrens 2751789Sahrens dataset = argv[0]; 2752789Sahrens path = argv[1]; 2753789Sahrens 2754789Sahrens /* try to open the dataset */ 27552082Seschrock if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 2756789Sahrens return (1); 2757789Sahrens 2758789Sahrens (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 27592082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE); 2760789Sahrens 2761789Sahrens /* check for legacy mountpoint and complain appropriately */ 2762789Sahrens ret = 0; 2763789Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 2764789Sahrens if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS, 2765789Sahrens NULL, 0, mntopts, sizeof (mntopts)) != 0) { 2766789Sahrens (void) fprintf(stderr, gettext("mount failed: %s\n"), 2767789Sahrens strerror(errno)); 2768789Sahrens ret = 1; 2769789Sahrens } 2770789Sahrens } else { 2771789Sahrens (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 2772789Sahrens "mounted using 'mount -F zfs'\n"), dataset); 2773789Sahrens (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 2774789Sahrens "instead.\n"), path); 2775789Sahrens (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' " 2776789Sahrens "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n")); 2777789Sahrens (void) fprintf(stderr, gettext("See zfs(1M) for more " 2778789Sahrens "information.\n")); 2779789Sahrens ret = 1; 2780789Sahrens } 2781789Sahrens 2782789Sahrens return (ret); 2783789Sahrens } 2784789Sahrens 2785789Sahrens /* 2786789Sahrens * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 2787789Sahrens * unmounts of non-legacy filesystems, as this is the dominant administrative 2788789Sahrens * interface. 2789789Sahrens */ 2790789Sahrens static int 2791789Sahrens manual_unmount(int argc, char **argv) 2792789Sahrens { 2793789Sahrens int flags = 0; 2794789Sahrens int c; 2795789Sahrens 2796789Sahrens /* check options */ 2797789Sahrens while ((c = getopt(argc, argv, "f")) != -1) { 2798789Sahrens switch (c) { 2799789Sahrens case 'f': 2800789Sahrens flags = MS_FORCE; 2801789Sahrens break; 2802789Sahrens case '?': 2803789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2804789Sahrens optopt); 2805789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] " 2806789Sahrens "<path>\n")); 2807789Sahrens return (2); 2808789Sahrens } 2809789Sahrens } 2810789Sahrens 2811789Sahrens argc -= optind; 2812789Sahrens argv += optind; 2813789Sahrens 2814789Sahrens /* check arguments */ 2815789Sahrens if (argc != 1) { 2816789Sahrens if (argc == 0) 2817789Sahrens (void) fprintf(stderr, gettext("missing path " 2818789Sahrens "argument\n")); 2819789Sahrens else 2820789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2821789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 2822789Sahrens return (2); 2823789Sahrens } 2824789Sahrens 28252082Seschrock return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE)); 2826789Sahrens } 2827789Sahrens 2828789Sahrens static int 2829789Sahrens volcheck(zpool_handle_t *zhp, void *data) 2830789Sahrens { 2831789Sahrens int isinit = (int)data; 2832789Sahrens 2833789Sahrens if (isinit) 2834789Sahrens return (zpool_create_zvol_links(zhp)); 2835789Sahrens else 2836789Sahrens return (zpool_remove_zvol_links(zhp)); 2837789Sahrens } 2838789Sahrens 2839789Sahrens /* 2840789Sahrens * Iterate over all pools in the system and either create or destroy /dev/zvol 2841789Sahrens * links, depending on the value of 'isinit'. 2842789Sahrens */ 2843789Sahrens static int 28442082Seschrock do_volcheck(boolean_t isinit) 2845789Sahrens { 28462082Seschrock return (zpool_iter(g_zfs, volcheck, (void *)isinit) ? 1 : 0); 2847789Sahrens } 2848789Sahrens 2849789Sahrens int 2850789Sahrens main(int argc, char **argv) 2851789Sahrens { 2852789Sahrens int ret; 2853789Sahrens int i; 2854789Sahrens char *progname; 2855789Sahrens char *cmdname; 2856789Sahrens 2857789Sahrens (void) setlocale(LC_ALL, ""); 2858789Sahrens (void) textdomain(TEXT_DOMAIN); 2859789Sahrens 2860789Sahrens opterr = 0; 2861789Sahrens 28622082Seschrock if ((g_zfs = libzfs_init()) == NULL) { 28632082Seschrock (void) fprintf(stderr, gettext("internal error: failed to " 28642082Seschrock "initialize ZFS library\n")); 28652082Seschrock return (1); 28662082Seschrock } 28672082Seschrock 28682082Seschrock libzfs_print_on_error(g_zfs, B_TRUE); 28692082Seschrock 2870789Sahrens if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 2871789Sahrens (void) fprintf(stderr, gettext("internal error: unable to " 2872789Sahrens "open %s\n"), MNTTAB); 2873789Sahrens return (1); 2874789Sahrens } 2875789Sahrens 2876789Sahrens /* 2877789Sahrens * This command also doubles as the /etc/fs mount and unmount program. 2878789Sahrens * Determine if we should take this behavior based on argv[0]. 2879789Sahrens */ 2880789Sahrens progname = basename(argv[0]); 2881789Sahrens if (strcmp(progname, "mount") == 0) { 2882789Sahrens ret = manual_mount(argc, argv); 2883789Sahrens } else if (strcmp(progname, "umount") == 0) { 2884789Sahrens ret = manual_unmount(argc, argv); 2885789Sahrens } else { 2886789Sahrens /* 2887789Sahrens * Make sure the user has specified some command. 2888789Sahrens */ 2889789Sahrens if (argc < 2) { 2890789Sahrens (void) fprintf(stderr, gettext("missing command\n")); 28912082Seschrock usage(B_FALSE); 2892789Sahrens } 2893789Sahrens 2894789Sahrens cmdname = argv[1]; 2895789Sahrens 2896789Sahrens /* 2897789Sahrens * The 'umount' command is an alias for 'unmount' 2898789Sahrens */ 2899789Sahrens if (strcmp(cmdname, "umount") == 0) 2900789Sahrens cmdname = "unmount"; 2901789Sahrens 2902789Sahrens /* 29031749Sahrens * The 'recv' command is an alias for 'receive' 29041749Sahrens */ 29051749Sahrens if (strcmp(cmdname, "recv") == 0) 29061749Sahrens cmdname = "receive"; 29071749Sahrens 29081749Sahrens /* 2909789Sahrens * Special case '-?' 2910789Sahrens */ 2911789Sahrens if (strcmp(cmdname, "-?") == 0) 29122082Seschrock usage(B_TRUE); 2913789Sahrens 2914789Sahrens /* 2915789Sahrens * 'volinit' and 'volfini' do not appear in the usage message, 2916789Sahrens * so we have to special case them here. 2917789Sahrens */ 2918789Sahrens if (strcmp(cmdname, "volinit") == 0) 29192082Seschrock return (do_volcheck(B_TRUE)); 2920789Sahrens else if (strcmp(cmdname, "volfini") == 0) 29212082Seschrock return (do_volcheck(B_FALSE)); 2922789Sahrens 2923789Sahrens /* 2924789Sahrens * Run the appropriate command. 2925789Sahrens */ 2926789Sahrens for (i = 0; i < NCOMMAND; i++) { 2927789Sahrens if (command_table[i].name == NULL) 2928789Sahrens continue; 2929789Sahrens 2930789Sahrens if (strcmp(cmdname, command_table[i].name) == 0) { 2931789Sahrens current_command = &command_table[i]; 2932789Sahrens ret = command_table[i].func(argc - 1, argv + 1); 2933789Sahrens break; 2934789Sahrens } 2935789Sahrens } 2936789Sahrens 2937789Sahrens if (i == NCOMMAND) { 2938789Sahrens (void) fprintf(stderr, gettext("unrecognized " 2939789Sahrens "command '%s'\n"), cmdname); 29402082Seschrock usage(B_FALSE); 2941789Sahrens } 2942789Sahrens } 2943789Sahrens 2944789Sahrens (void) fclose(mnttab_file); 2945789Sahrens 29462082Seschrock libzfs_fini(g_zfs); 29472082Seschrock 2948789Sahrens /* 2949789Sahrens * The 'ZFS_ABORT' environment variable causes us to dump core on exit 2950789Sahrens * for the purposes of running ::findleaks. 2951789Sahrens */ 2952789Sahrens if (getenv("ZFS_ABORT") != NULL) { 2953789Sahrens (void) printf("dumping core by request\n"); 2954789Sahrens abort(); 2955789Sahrens } 2956789Sahrens 2957789Sahrens return (ret); 2958789Sahrens } 2959