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" 1802379Ssjelinek "\t [-s property [-s property]...]" 1812379Ssjelinek " [-S property [-S property]...]\n" 1821387Seschrock "\t [filesystem|volume|snapshot] ...\n")); 1831387Seschrock case HELP_MOUNT: 1841387Seschrock return (gettext("\tmount\n" 1851387Seschrock "\tmount [-o opts] [-O] -a\n" 1861387Seschrock "\tmount [-o opts] [-O] <filesystem>\n")); 1872082Seschrock case HELP_PROMOTE: 1882082Seschrock return (gettext("\tpromote <clone filesystem>\n")); 1891749Sahrens case HELP_RECEIVE: 1901749Sahrens return (gettext("\treceive [-vn] <filesystem|volume|snapshot>\n" 1911749Sahrens "\treceive [-vn] -d <filesystem>\n")); 1921387Seschrock case HELP_RENAME: 1931387Seschrock return (gettext("\trename <filesystem|volume|snapshot> " 1941387Seschrock "<filesystem|volume|snapshot>\n")); 1951387Seschrock case HELP_ROLLBACK: 1961387Seschrock return (gettext("\trollback [-rRf] <snapshot>\n")); 1971749Sahrens case HELP_SEND: 1981749Sahrens return (gettext("\tsend [-i <snapshot>] <snapshot>\n")); 1991387Seschrock case HELP_SET: 2001387Seschrock return (gettext("\tset <property=value> " 2011387Seschrock "<filesystem|volume> ...\n")); 2021387Seschrock case HELP_SHARE: 2031387Seschrock return (gettext("\tshare -a\n" 2041387Seschrock "\tshare <filesystem>\n")); 2051387Seschrock case HELP_SNAPSHOT: 2062199Sahrens return (gettext("\tsnapshot [-r] " 2072199Sahrens "<filesystem@name|volume@name>\n")); 2081387Seschrock case HELP_UNMOUNT: 2091387Seschrock return (gettext("\tunmount [-f] -a\n" 2101387Seschrock "\tunmount [-f] <filesystem|mountpoint>\n")); 2111387Seschrock case HELP_UNSHARE: 2121387Seschrock return (gettext("\tunshare [-f] -a\n" 2131387Seschrock "\tunshare [-f] <filesystem|mountpoint>\n")); 2141387Seschrock } 2151387Seschrock 2161387Seschrock abort(); 2171387Seschrock /* NOTREACHED */ 2181387Seschrock } 2191387Seschrock 220789Sahrens /* 221789Sahrens * Utility function to guarantee malloc() success. 222789Sahrens */ 223789Sahrens void * 224789Sahrens safe_malloc(size_t size) 225789Sahrens { 226789Sahrens void *data; 227789Sahrens 228789Sahrens if ((data = calloc(1, size)) == NULL) { 229789Sahrens (void) fprintf(stderr, "internal error: out of memory\n"); 230789Sahrens exit(1); 231789Sahrens } 232789Sahrens 233789Sahrens return (data); 234789Sahrens } 235789Sahrens 236789Sahrens /* 237789Sahrens * Display usage message. If we're inside a command, display only the usage for 238789Sahrens * that command. Otherwise, iterate over the entire command table and display 239789Sahrens * a complete usage message. 240789Sahrens */ 241789Sahrens static void 2422082Seschrock usage(boolean_t requested) 243789Sahrens { 244789Sahrens int i; 2452082Seschrock boolean_t show_properties = B_FALSE; 246789Sahrens FILE *fp = requested ? stdout : stderr; 247789Sahrens 248789Sahrens if (current_command == NULL) { 249789Sahrens 250789Sahrens (void) fprintf(fp, gettext("usage: zfs command args ...\n")); 251789Sahrens (void) fprintf(fp, 252789Sahrens gettext("where 'command' is one of the following:\n\n")); 253789Sahrens 254789Sahrens for (i = 0; i < NCOMMAND; i++) { 255789Sahrens if (command_table[i].name == NULL) 256789Sahrens (void) fprintf(fp, "\n"); 257789Sahrens else 258789Sahrens (void) fprintf(fp, "%s", 2591387Seschrock get_usage(command_table[i].usage)); 260789Sahrens } 261789Sahrens 262789Sahrens (void) fprintf(fp, gettext("\nEach dataset is of the form: " 263789Sahrens "pool/[dataset/]*dataset[@name]\n")); 264789Sahrens } else { 265789Sahrens (void) fprintf(fp, gettext("usage:\n")); 2661387Seschrock (void) fprintf(fp, "%s", get_usage(current_command->usage)); 267789Sahrens } 268789Sahrens 2692190Sdarrenm if (current_command != NULL && 2702190Sdarrenm (strcmp(current_command->name, "set") == 0 || 271789Sahrens strcmp(current_command->name, "get") == 0 || 272789Sahrens strcmp(current_command->name, "inherit") == 0 || 2732190Sdarrenm strcmp(current_command->name, "list") == 0)) 2742082Seschrock show_properties = B_TRUE; 275789Sahrens 276789Sahrens if (show_properties) { 277789Sahrens 278789Sahrens (void) fprintf(fp, 279789Sahrens gettext("\nThe following properties are supported:\n")); 280789Sahrens 281789Sahrens (void) fprintf(fp, "\n\t%-13s %s %s %s\n\n", 282789Sahrens "PROPERTY", "EDIT", "INHERIT", "VALUES"); 283789Sahrens 284789Sahrens for (i = 0; i < ZFS_NPROP_VISIBLE; i++) { 285789Sahrens (void) fprintf(fp, "\t%-13s ", zfs_prop_to_name(i)); 286789Sahrens 287789Sahrens if (zfs_prop_readonly(i)) 288789Sahrens (void) fprintf(fp, " NO "); 289789Sahrens else 290789Sahrens (void) fprintf(fp, " YES "); 291789Sahrens 292789Sahrens if (zfs_prop_inheritable(i)) 293789Sahrens (void) fprintf(fp, " YES "); 294789Sahrens else 295789Sahrens (void) fprintf(fp, " NO "); 296789Sahrens 297789Sahrens if (zfs_prop_values(i) == NULL) 298789Sahrens (void) fprintf(fp, "-\n"); 299789Sahrens else 300789Sahrens (void) fprintf(fp, "%s\n", zfs_prop_values(i)); 301789Sahrens } 302789Sahrens (void) fprintf(fp, gettext("\nSizes are specified in bytes " 303789Sahrens "with standard units such as K, M, G, etc.\n")); 3042190Sdarrenm } else { 3052190Sdarrenm /* 3062190Sdarrenm * TRANSLATION NOTE: 3072190Sdarrenm * "zfs set|get" must not be localised this is the 3082190Sdarrenm * command name and arguments. 3092190Sdarrenm */ 3102190Sdarrenm (void) fprintf(fp, 3112190Sdarrenm gettext("\nFor the property list, run: zfs set|get\n")); 312789Sahrens } 313789Sahrens 314789Sahrens exit(requested ? 0 : 2); 315789Sahrens } 316789Sahrens 317789Sahrens /* 318789Sahrens * zfs clone <fs, snap, vol> fs 319789Sahrens * 320789Sahrens * Given an existing dataset, create a writable copy whose initial contents 321789Sahrens * are the same as the source. The newly created dataset maintains a 322789Sahrens * dependency on the original; the original cannot be destroyed so long as 323789Sahrens * the clone exists. 324789Sahrens */ 325789Sahrens static int 326789Sahrens zfs_do_clone(int argc, char **argv) 327789Sahrens { 328789Sahrens zfs_handle_t *zhp; 329789Sahrens int ret; 330789Sahrens 331789Sahrens /* check options */ 332789Sahrens if (argc > 1 && argv[1][0] == '-') { 333789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 334789Sahrens argv[1][1]); 3352082Seschrock usage(B_FALSE); 336789Sahrens } 337789Sahrens 338789Sahrens /* check number of arguments */ 339789Sahrens if (argc < 2) { 340789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 341789Sahrens "argument\n")); 3422082Seschrock usage(B_FALSE); 343789Sahrens } 344789Sahrens if (argc < 3) { 345789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 346789Sahrens "argument\n")); 3472082Seschrock usage(B_FALSE); 348789Sahrens } 349789Sahrens if (argc > 3) { 350789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 3512082Seschrock usage(B_FALSE); 352789Sahrens } 353789Sahrens 354789Sahrens /* open the source dataset */ 3552082Seschrock if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_SNAPSHOT)) == NULL) 356789Sahrens return (1); 357789Sahrens 358789Sahrens /* pass to libzfs */ 359789Sahrens ret = zfs_clone(zhp, argv[2]); 360789Sahrens 361789Sahrens /* create the mountpoint if necessary */ 362789Sahrens if (ret == 0) { 3632082Seschrock zfs_handle_t *clone = zfs_open(g_zfs, argv[2], ZFS_TYPE_ANY); 364789Sahrens if (clone != NULL) { 365789Sahrens if ((ret = zfs_mount(clone, NULL, 0)) == 0) 366789Sahrens ret = zfs_share(clone); 367789Sahrens zfs_close(clone); 368789Sahrens } 369789Sahrens } 370789Sahrens 371789Sahrens zfs_close(zhp); 372789Sahrens 373789Sahrens return (ret == 0 ? 0 : 1); 374789Sahrens } 375789Sahrens 376789Sahrens /* 377789Sahrens * zfs create fs 3782199Sahrens * zfs create [-s] [-b blocksize] -V vol size 379789Sahrens * 380789Sahrens * Create a new dataset. This command can be used to create filesystems 381789Sahrens * and volumes. Snapshot creation is handled by 'zfs snapshot'. 382789Sahrens * For volumes, the user must specify a size to be used. 383789Sahrens * 384789Sahrens * The '-s' flag applies only to volumes, and indicates that we should not try 385789Sahrens * to set the reservation for this volume. By default we set a reservation 386789Sahrens * equal to the size for any volume. 387789Sahrens */ 388789Sahrens static int 389789Sahrens zfs_do_create(int argc, char **argv) 390789Sahrens { 391789Sahrens zfs_type_t type = ZFS_TYPE_FILESYSTEM; 392789Sahrens zfs_handle_t *zhp; 393789Sahrens char *size = NULL; 394789Sahrens char *blocksize = NULL; 395789Sahrens int c; 3962082Seschrock boolean_t noreserve = B_FALSE; 397789Sahrens int ret; 398789Sahrens 399789Sahrens /* check options */ 400789Sahrens while ((c = getopt(argc, argv, ":V:b:s")) != -1) { 401789Sahrens switch (c) { 402789Sahrens case 'V': 403789Sahrens type = ZFS_TYPE_VOLUME; 404789Sahrens size = optarg; 405789Sahrens break; 406789Sahrens case 'b': 407789Sahrens blocksize = optarg; 408789Sahrens break; 409789Sahrens case 's': 4102082Seschrock noreserve = B_TRUE; 411789Sahrens break; 412789Sahrens case ':': 413789Sahrens (void) fprintf(stderr, gettext("missing size " 414789Sahrens "argument\n")); 4152082Seschrock usage(B_FALSE); 416789Sahrens break; 417789Sahrens case '?': 418789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 419789Sahrens optopt); 4202082Seschrock usage(B_FALSE); 421789Sahrens } 422789Sahrens } 423789Sahrens 424789Sahrens if (noreserve && type != ZFS_TYPE_VOLUME) { 425789Sahrens (void) fprintf(stderr, gettext("'-s' can only be used when " 426789Sahrens "creating a volume\n")); 4272082Seschrock usage(B_FALSE); 428789Sahrens } 429789Sahrens 430789Sahrens argc -= optind; 431789Sahrens argv += optind; 432789Sahrens 433789Sahrens /* check number of arguments */ 434789Sahrens if (argc == 0) { 435789Sahrens (void) fprintf(stderr, gettext("missing %s argument\n"), 436789Sahrens zfs_type_to_name(type)); 4372082Seschrock usage(B_FALSE); 438789Sahrens } 439789Sahrens if (argc > 1) { 440789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 4412082Seschrock usage(B_FALSE); 442789Sahrens } 443789Sahrens 444789Sahrens /* pass to libzfs */ 4452082Seschrock if (zfs_create(g_zfs, argv[0], type, size, blocksize) != 0) 446789Sahrens return (1); 447789Sahrens 4482082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 449789Sahrens return (1); 450789Sahrens 451789Sahrens /* 452789Sahrens * Volume handling. By default, we try to create a reservation of equal 453789Sahrens * size for the volume. If we can't do this, then destroy the dataset 454789Sahrens * and report an error. 455789Sahrens */ 456789Sahrens if (type == ZFS_TYPE_VOLUME && !noreserve) { 457789Sahrens if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, size) != 0) { 458789Sahrens (void) fprintf(stderr, gettext("use '-s' to create a " 459789Sahrens "volume without a matching reservation\n")); 460789Sahrens (void) zfs_destroy(zhp); 461789Sahrens return (1); 462789Sahrens } 463789Sahrens } 464789Sahrens 465789Sahrens /* 466789Sahrens * Mount and/or share the new filesystem as appropriate. We provide a 467789Sahrens * verbose error message to let the user know that their filesystem was 468789Sahrens * in fact created, even if we failed to mount or share it. 469789Sahrens */ 470789Sahrens if (zfs_mount(zhp, NULL, 0) != 0) { 471789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 472789Sahrens "created, but not mounted\n")); 473789Sahrens ret = 1; 474789Sahrens } else if (zfs_share(zhp) != 0) { 475789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 476789Sahrens "created, but not shared\n")); 477789Sahrens ret = 1; 478789Sahrens } else { 479789Sahrens ret = 0; 480789Sahrens } 481789Sahrens 482789Sahrens zfs_close(zhp); 483789Sahrens return (ret); 484789Sahrens } 485789Sahrens 486789Sahrens /* 487789Sahrens * zfs destroy [-rf] <fs, snap, vol> 488789Sahrens * 489789Sahrens * -r Recursively destroy all children 490789Sahrens * -R Recursively destroy all dependents, including clones 491789Sahrens * -f Force unmounting of any dependents 492789Sahrens * 493789Sahrens * Destroys the given dataset. By default, it will unmount any filesystems, 494789Sahrens * and refuse to destroy a dataset that has any dependents. A dependent can 495789Sahrens * either be a child, or a clone of a child. 496789Sahrens */ 497789Sahrens typedef struct destroy_cbdata { 4982082Seschrock boolean_t cb_first; 499789Sahrens int cb_force; 500789Sahrens int cb_recurse; 501789Sahrens int cb_error; 502789Sahrens int cb_needforce; 503789Sahrens int cb_doclones; 504789Sahrens zfs_handle_t *cb_target; 5052199Sahrens char *cb_snapname; 506789Sahrens } destroy_cbdata_t; 507789Sahrens 508789Sahrens /* 509789Sahrens * Check for any dependents based on the '-r' or '-R' flags. 510789Sahrens */ 511789Sahrens static int 512789Sahrens destroy_check_dependent(zfs_handle_t *zhp, void *data) 513789Sahrens { 514789Sahrens destroy_cbdata_t *cbp = data; 515789Sahrens const char *tname = zfs_get_name(cbp->cb_target); 516789Sahrens const char *name = zfs_get_name(zhp); 517789Sahrens 518789Sahrens if (strncmp(tname, name, strlen(tname)) == 0 && 519789Sahrens (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { 520789Sahrens /* 521789Sahrens * This is a direct descendant, not a clone somewhere else in 522789Sahrens * the hierarchy. 523789Sahrens */ 524789Sahrens if (cbp->cb_recurse) 525789Sahrens goto out; 526789Sahrens 527789Sahrens if (cbp->cb_first) { 528789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 529789Sahrens "%s has children\n"), 530789Sahrens zfs_get_name(cbp->cb_target), 531789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 532789Sahrens (void) fprintf(stderr, gettext("use '-r' to destroy " 533789Sahrens "the following datasets:\n")); 5342082Seschrock cbp->cb_first = B_FALSE; 535789Sahrens cbp->cb_error = 1; 536789Sahrens } 537789Sahrens 538789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 539789Sahrens } else { 540789Sahrens /* 541789Sahrens * This is a clone. We only want to report this if the '-r' 542789Sahrens * wasn't specified, or the target is a snapshot. 543789Sahrens */ 544789Sahrens if (!cbp->cb_recurse && 545789Sahrens zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) 546789Sahrens goto out; 547789Sahrens 548789Sahrens if (cbp->cb_first) { 549789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 550789Sahrens "%s has dependent clones\n"), 551789Sahrens zfs_get_name(cbp->cb_target), 552789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 553789Sahrens (void) fprintf(stderr, gettext("use '-R' to destroy " 554789Sahrens "the following datasets:\n")); 5552082Seschrock cbp->cb_first = B_FALSE; 556789Sahrens cbp->cb_error = 1; 557789Sahrens } 558789Sahrens 559789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 560789Sahrens } 561789Sahrens 562789Sahrens out: 563789Sahrens zfs_close(zhp); 564789Sahrens return (0); 565789Sahrens } 566789Sahrens 567789Sahrens static int 568789Sahrens destroy_callback(zfs_handle_t *zhp, void *data) 569789Sahrens { 570789Sahrens destroy_cbdata_t *cbp = data; 571789Sahrens 572789Sahrens /* 573789Sahrens * Ignore pools (which we've already flagged as an error before getting 574789Sahrens * here. 575789Sahrens */ 576789Sahrens if (strchr(zfs_get_name(zhp), '/') == NULL && 577789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 578789Sahrens zfs_close(zhp); 579789Sahrens return (0); 580789Sahrens } 581789Sahrens 582789Sahrens /* 583789Sahrens * Bail out on the first error. 584789Sahrens */ 585789Sahrens if (zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0 || 586789Sahrens zfs_destroy(zhp) != 0) { 587789Sahrens zfs_close(zhp); 588789Sahrens return (-1); 589789Sahrens } 590789Sahrens 591789Sahrens zfs_close(zhp); 592789Sahrens return (0); 593789Sahrens } 594789Sahrens 5952199Sahrens static int 5962199Sahrens destroy_snap_clones(zfs_handle_t *zhp, void *arg) 5972199Sahrens { 5982199Sahrens destroy_cbdata_t *cbp = arg; 5992199Sahrens char thissnap[MAXPATHLEN]; 6002199Sahrens zfs_handle_t *szhp; 6012199Sahrens 6022199Sahrens (void) snprintf(thissnap, sizeof (thissnap), 6032199Sahrens "%s@%s", zfs_get_name(zhp), cbp->cb_snapname); 6042199Sahrens 6052199Sahrens libzfs_print_on_error(g_zfs, B_FALSE); 6062199Sahrens szhp = zfs_open(g_zfs, thissnap, ZFS_TYPE_SNAPSHOT); 6072199Sahrens libzfs_print_on_error(g_zfs, B_TRUE); 6082199Sahrens if (szhp) { 6092199Sahrens /* 6102199Sahrens * Destroy any clones of this snapshot 6112199Sahrens */ 612*2474Seschrock if (zfs_iter_dependents(szhp, B_FALSE, destroy_callback, 613*2474Seschrock cbp) != 0) { 614*2474Seschrock zfs_close(szhp); 615*2474Seschrock return (-1); 616*2474Seschrock } 6172199Sahrens zfs_close(szhp); 6182199Sahrens } 6192199Sahrens 6202199Sahrens return (zfs_iter_filesystems(zhp, destroy_snap_clones, arg)); 6212199Sahrens } 622789Sahrens 623789Sahrens static int 624789Sahrens zfs_do_destroy(int argc, char **argv) 625789Sahrens { 626789Sahrens destroy_cbdata_t cb = { 0 }; 627789Sahrens int c; 628789Sahrens zfs_handle_t *zhp; 6292199Sahrens char *cp; 630789Sahrens 631789Sahrens /* check options */ 632789Sahrens while ((c = getopt(argc, argv, "frR")) != -1) { 633789Sahrens switch (c) { 634789Sahrens case 'f': 635789Sahrens cb.cb_force = 1; 636789Sahrens break; 637789Sahrens case 'r': 638789Sahrens cb.cb_recurse = 1; 639789Sahrens break; 640789Sahrens case 'R': 641789Sahrens cb.cb_recurse = 1; 642789Sahrens cb.cb_doclones = 1; 643789Sahrens break; 644789Sahrens case '?': 645789Sahrens default: 646789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 647789Sahrens optopt); 6482082Seschrock usage(B_FALSE); 649789Sahrens } 650789Sahrens } 651789Sahrens 652789Sahrens argc -= optind; 653789Sahrens argv += optind; 654789Sahrens 655789Sahrens /* check number of arguments */ 656789Sahrens if (argc == 0) { 657789Sahrens (void) fprintf(stderr, gettext("missing path argument\n")); 6582082Seschrock usage(B_FALSE); 659789Sahrens } 660789Sahrens if (argc > 1) { 661789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 6622082Seschrock usage(B_FALSE); 663789Sahrens } 664789Sahrens 6652199Sahrens /* 6662199Sahrens * If we are doing recursive destroy of a snapshot, then the 6672199Sahrens * named snapshot may not exist. Go straight to libzfs. 6682199Sahrens */ 6692199Sahrens if (cb.cb_recurse && (cp = strchr(argv[0], '@'))) { 6702199Sahrens int ret; 6712199Sahrens 6722199Sahrens *cp = '\0'; 6732199Sahrens if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 6742199Sahrens return (1); 6752199Sahrens *cp = '@'; 6762199Sahrens cp++; 6772199Sahrens 6782199Sahrens if (cb.cb_doclones) { 6792199Sahrens cb.cb_snapname = cp; 680*2474Seschrock if (destroy_snap_clones(zhp, &cb) != 0) { 681*2474Seschrock zfs_close(zhp); 682*2474Seschrock return (1); 683*2474Seschrock } 6842199Sahrens } 6852199Sahrens 6862199Sahrens ret = zfs_destroy_snaps(zhp, cp); 6872199Sahrens zfs_close(zhp); 6882199Sahrens if (ret) { 6892199Sahrens (void) fprintf(stderr, 6902199Sahrens gettext("no snapshots destroyed\n")); 6912199Sahrens } 6922199Sahrens return (ret != 0); 6932199Sahrens } 6942199Sahrens 6952199Sahrens 696789Sahrens /* Open the given dataset */ 6972082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 698789Sahrens return (1); 699789Sahrens 700789Sahrens cb.cb_target = zhp; 701789Sahrens 702789Sahrens /* 703789Sahrens * Perform an explicit check for pools before going any further. 704789Sahrens */ 705789Sahrens if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 706789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 707789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 708789Sahrens "operation does not apply to pools\n"), 709789Sahrens zfs_get_name(zhp)); 710789Sahrens (void) fprintf(stderr, gettext("use 'zfs destroy -r " 711789Sahrens "%s' to destroy all datasets in the pool\n"), 712789Sahrens zfs_get_name(zhp)); 713789Sahrens (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 714789Sahrens "to destroy the pool itself\n"), zfs_get_name(zhp)); 715789Sahrens zfs_close(zhp); 716789Sahrens return (1); 717789Sahrens } 718789Sahrens 719789Sahrens /* 720789Sahrens * Check for any dependents and/or clones. 721789Sahrens */ 7222082Seschrock cb.cb_first = B_TRUE; 723*2474Seschrock if (!cb.cb_doclones && 724*2474Seschrock zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent, 725*2474Seschrock &cb) != 0) { 726*2474Seschrock zfs_close(zhp); 727*2474Seschrock return (1); 728*2474Seschrock } 729*2474Seschrock 730*2474Seschrock 731*2474Seschrock if (cb.cb_error || 732*2474Seschrock zfs_iter_dependents(zhp, B_FALSE, destroy_callback, &cb) != 0) { 733789Sahrens zfs_close(zhp); 734789Sahrens return (1); 735789Sahrens } 736789Sahrens 737789Sahrens /* 738*2474Seschrock * Do the real thing. The callback will close the handle regardless of 739*2474Seschrock * whether it succeeds or not. 740789Sahrens */ 741*2474Seschrock if (destroy_callback(zhp, &cb) != 0) 742*2474Seschrock return (1); 743*2474Seschrock 744*2474Seschrock return (0); 745789Sahrens } 746789Sahrens 747789Sahrens /* 748866Seschrock * zfs get [-rHp] [-o field[,field]...] [-s source[,source]...] 749866Seschrock * < all | property[,property]... > < fs | snap | vol > ... 750789Sahrens * 751789Sahrens * -r recurse over any child datasets 752789Sahrens * -H scripted mode. Headers are stripped, and fields are separated 753789Sahrens * by tabs instead of spaces. 754789Sahrens * -o Set of fields to display. One of "name,property,value,source". 755789Sahrens * Default is all four. 756789Sahrens * -s Set of sources to allow. One of 757789Sahrens * "local,default,inherited,temporary,none". Default is all 758789Sahrens * five. 759789Sahrens * -p Display values in parsable (literal) format. 760789Sahrens * 761789Sahrens * Prints properties for the given datasets. The user can control which 762789Sahrens * columns to display as well as which property types to allow. 763789Sahrens */ 764789Sahrens typedef struct get_cbdata { 765789Sahrens int cb_sources; 766789Sahrens int cb_columns[4]; 7672082Seschrock int cb_nprop; 7682082Seschrock boolean_t cb_scripted; 7692082Seschrock boolean_t cb_literal; 7702082Seschrock boolean_t cb_isall; 771789Sahrens zfs_prop_t cb_prop[ZFS_NPROP_ALL]; 772789Sahrens } get_cbdata_t; 773789Sahrens 774789Sahrens #define GET_COL_NAME 1 775789Sahrens #define GET_COL_PROPERTY 2 776789Sahrens #define GET_COL_VALUE 3 777789Sahrens #define GET_COL_SOURCE 4 778789Sahrens 779789Sahrens /* 780789Sahrens * Display a single line of output, according to the settings in the callback 781789Sahrens * structure. 782789Sahrens */ 783789Sahrens static void 784789Sahrens print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, zfs_prop_t prop, 785789Sahrens const char *value, zfs_source_t sourcetype, const char *source) 786789Sahrens { 787789Sahrens int i; 788789Sahrens int width; 789789Sahrens const char *str; 790789Sahrens char buf[128]; 791789Sahrens 792789Sahrens /* 793789Sahrens * Ignore those source types that the user has chosen to ignore. 794789Sahrens */ 795789Sahrens if ((sourcetype & cbp->cb_sources) == 0) 796789Sahrens return; 797789Sahrens 798789Sahrens for (i = 0; i < 4; i++) { 799789Sahrens switch (cbp->cb_columns[i]) { 800789Sahrens case GET_COL_NAME: 801789Sahrens width = 15; 802789Sahrens str = zfs_get_name(zhp); 803789Sahrens break; 804789Sahrens 805789Sahrens case GET_COL_PROPERTY: 806789Sahrens width = 13; 807789Sahrens str = zfs_prop_to_name(prop); 808789Sahrens break; 809789Sahrens 810789Sahrens case GET_COL_VALUE: 811789Sahrens width = 25; 812789Sahrens str = value; 813789Sahrens break; 814789Sahrens 815789Sahrens case GET_COL_SOURCE: 816789Sahrens width = 15; 817789Sahrens switch (sourcetype) { 818789Sahrens case ZFS_SRC_NONE: 819789Sahrens str = "-"; 820789Sahrens break; 821789Sahrens 822789Sahrens case ZFS_SRC_DEFAULT: 823789Sahrens str = "default"; 824789Sahrens break; 825789Sahrens 826789Sahrens case ZFS_SRC_LOCAL: 827789Sahrens str = "local"; 828789Sahrens break; 829789Sahrens 830789Sahrens case ZFS_SRC_TEMPORARY: 831789Sahrens str = "temporary"; 832789Sahrens break; 833789Sahrens 834789Sahrens case ZFS_SRC_INHERITED: 835789Sahrens (void) snprintf(buf, sizeof (buf), 836789Sahrens "inherited from %s", source); 837789Sahrens str = buf; 838789Sahrens break; 839789Sahrens } 840789Sahrens break; 841789Sahrens 842789Sahrens default: 843789Sahrens continue; 844789Sahrens } 845789Sahrens 846789Sahrens if (cbp->cb_columns[i + 1] == 0) 847789Sahrens (void) printf("%s", str); 848789Sahrens else if (cbp->cb_scripted) 849789Sahrens (void) printf("%s\t", str); 850789Sahrens else 851789Sahrens (void) printf("%-*s ", width, str); 852789Sahrens 853789Sahrens } 854789Sahrens 855789Sahrens (void) printf("\n"); 856789Sahrens } 857789Sahrens 858789Sahrens /* 859789Sahrens * Invoked to display the properties for a single dataset. 860789Sahrens */ 861789Sahrens static int 862789Sahrens get_callback(zfs_handle_t *zhp, void *data) 863789Sahrens { 864789Sahrens char buf[ZFS_MAXPROPLEN]; 865789Sahrens zfs_source_t sourcetype; 866789Sahrens char source[ZFS_MAXNAMELEN]; 867789Sahrens get_cbdata_t *cbp = data; 868789Sahrens int i; 869789Sahrens 870866Seschrock for (i = 0; i < cbp->cb_nprop; i++) { 871866Seschrock if (zfs_prop_get(zhp, cbp->cb_prop[i], buf, 872866Seschrock sizeof (buf), &sourcetype, source, sizeof (source), 873866Seschrock cbp->cb_literal) != 0) { 874866Seschrock if (cbp->cb_isall) 875866Seschrock continue; 876866Seschrock (void) strlcpy(buf, "-", sizeof (buf)); 877866Seschrock sourcetype = ZFS_SRC_NONE; 878866Seschrock } 879789Sahrens 880866Seschrock print_one_property(zhp, cbp, cbp->cb_prop[i], 881866Seschrock buf, sourcetype, source); 882789Sahrens } 883789Sahrens 884789Sahrens return (0); 885789Sahrens } 886789Sahrens 887789Sahrens static int 888789Sahrens zfs_do_get(int argc, char **argv) 889789Sahrens { 890789Sahrens get_cbdata_t cb = { 0 }; 8912082Seschrock boolean_t recurse = B_FALSE; 892789Sahrens int c; 893866Seschrock char *value, *fields, *badopt; 894789Sahrens int i; 895866Seschrock int ret; 896789Sahrens 897789Sahrens /* 898789Sahrens * Set up default columns and sources. 899789Sahrens */ 900789Sahrens cb.cb_sources = ZFS_SRC_ALL; 901789Sahrens cb.cb_columns[0] = GET_COL_NAME; 902789Sahrens cb.cb_columns[1] = GET_COL_PROPERTY; 903789Sahrens cb.cb_columns[2] = GET_COL_VALUE; 904789Sahrens cb.cb_columns[3] = GET_COL_SOURCE; 905789Sahrens 906789Sahrens /* check options */ 907789Sahrens while ((c = getopt(argc, argv, ":o:s:rHp")) != -1) { 908789Sahrens switch (c) { 909789Sahrens case 'p': 9102082Seschrock cb.cb_literal = B_TRUE; 911789Sahrens break; 912789Sahrens case 'r': 9132082Seschrock recurse = B_TRUE; 914789Sahrens break; 915789Sahrens case 'H': 9162082Seschrock cb.cb_scripted = B_TRUE; 917789Sahrens break; 918789Sahrens case ':': 919789Sahrens (void) fprintf(stderr, gettext("missing argument for " 920789Sahrens "'%c' option\n"), optopt); 9212082Seschrock usage(B_FALSE); 922789Sahrens break; 923789Sahrens case 'o': 924789Sahrens /* 925789Sahrens * Process the set of columns to display. We zero out 926789Sahrens * the structure to give us a blank slate. 927789Sahrens */ 928789Sahrens bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 929789Sahrens i = 0; 930789Sahrens while (*optarg != '\0') { 931789Sahrens static char *col_subopts[] = 932789Sahrens { "name", "property", "value", "source", 933789Sahrens NULL }; 934789Sahrens 935789Sahrens if (i == 4) { 936789Sahrens (void) fprintf(stderr, gettext("too " 937789Sahrens "many fields given to -o " 938789Sahrens "option\n")); 9392082Seschrock usage(B_FALSE); 940789Sahrens } 941789Sahrens 942789Sahrens switch (getsubopt(&optarg, col_subopts, 943789Sahrens &value)) { 944789Sahrens case 0: 945789Sahrens cb.cb_columns[i++] = GET_COL_NAME; 946789Sahrens break; 947789Sahrens case 1: 948789Sahrens cb.cb_columns[i++] = GET_COL_PROPERTY; 949789Sahrens break; 950789Sahrens case 2: 951789Sahrens cb.cb_columns[i++] = GET_COL_VALUE; 952789Sahrens break; 953789Sahrens case 3: 954789Sahrens cb.cb_columns[i++] = GET_COL_SOURCE; 955789Sahrens break; 956789Sahrens default: 957789Sahrens (void) fprintf(stderr, 958789Sahrens gettext("invalid column name " 959789Sahrens "'%s'\n"), value); 9602082Seschrock usage(B_FALSE); 961789Sahrens } 962789Sahrens } 963789Sahrens break; 964789Sahrens 965789Sahrens case 's': 966789Sahrens cb.cb_sources = 0; 967789Sahrens while (*optarg != '\0') { 968789Sahrens static char *source_subopts[] = { 969789Sahrens "local", "default", "inherited", 970789Sahrens "temporary", "none", NULL }; 971789Sahrens 972789Sahrens switch (getsubopt(&optarg, source_subopts, 973789Sahrens &value)) { 974789Sahrens case 0: 975789Sahrens cb.cb_sources |= ZFS_SRC_LOCAL; 976789Sahrens break; 977789Sahrens case 1: 978789Sahrens cb.cb_sources |= ZFS_SRC_DEFAULT; 979789Sahrens break; 980789Sahrens case 2: 981789Sahrens cb.cb_sources |= ZFS_SRC_INHERITED; 982789Sahrens break; 983789Sahrens case 3: 984789Sahrens cb.cb_sources |= ZFS_SRC_TEMPORARY; 985789Sahrens break; 986789Sahrens case 4: 987789Sahrens cb.cb_sources |= ZFS_SRC_NONE; 988789Sahrens break; 989789Sahrens default: 990789Sahrens (void) fprintf(stderr, 991789Sahrens gettext("invalid source " 992789Sahrens "'%s'\n"), value); 9932082Seschrock usage(B_FALSE); 994789Sahrens } 995789Sahrens } 996789Sahrens break; 997789Sahrens 998789Sahrens case '?': 999789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1000789Sahrens optopt); 10012082Seschrock usage(B_FALSE); 1002789Sahrens } 1003789Sahrens } 1004789Sahrens 1005789Sahrens argc -= optind; 1006789Sahrens argv += optind; 1007789Sahrens 1008789Sahrens if (argc < 1) { 1009789Sahrens (void) fprintf(stderr, gettext("missing property " 1010789Sahrens "argument\n")); 10112082Seschrock usage(B_FALSE); 1012789Sahrens } 1013789Sahrens 1014789Sahrens fields = argv[0]; 1015789Sahrens 1016789Sahrens /* 1017866Seschrock * If the user specifies 'all', the behavior of 'zfs get' is slightly 1018866Seschrock * different, because we don't show properties which don't apply to the 1019866Seschrock * given dataset. 1020789Sahrens */ 1021866Seschrock if (strcmp(fields, "all") == 0) 10222082Seschrock cb.cb_isall = B_TRUE; 1023789Sahrens 1024866Seschrock if ((ret = zfs_get_proplist(fields, cb.cb_prop, ZFS_NPROP_ALL, 1025866Seschrock &cb.cb_nprop, &badopt)) != 0) { 1026866Seschrock if (ret == EINVAL) 1027866Seschrock (void) fprintf(stderr, gettext("invalid property " 1028866Seschrock "'%s'\n"), badopt); 1029866Seschrock else 1030866Seschrock (void) fprintf(stderr, gettext("too many properties " 1031866Seschrock "specified\n")); 10322082Seschrock usage(B_FALSE); 1033789Sahrens } 1034789Sahrens 1035789Sahrens argc--; 1036789Sahrens argv++; 1037789Sahrens 1038789Sahrens /* check for at least one dataset name */ 1039789Sahrens if (argc < 1) { 1040789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 10412082Seschrock usage(B_FALSE); 1042789Sahrens } 1043789Sahrens 1044789Sahrens /* 1045789Sahrens * Print out any headers 1046789Sahrens */ 1047789Sahrens if (!cb.cb_scripted) { 1048789Sahrens int i; 1049789Sahrens for (i = 0; i < 4; i++) { 1050789Sahrens switch (cb.cb_columns[i]) { 1051789Sahrens case GET_COL_NAME: 1052789Sahrens (void) printf("%-15s ", "NAME"); 1053789Sahrens break; 1054789Sahrens case GET_COL_PROPERTY: 1055789Sahrens (void) printf("%-13s ", "PROPERTY"); 1056789Sahrens break; 1057789Sahrens case GET_COL_VALUE: 1058789Sahrens (void) printf("%-25s ", "VALUE"); 1059789Sahrens break; 1060789Sahrens case GET_COL_SOURCE: 1061789Sahrens (void) printf("%s", "SOURCE"); 1062789Sahrens break; 1063789Sahrens } 1064789Sahrens } 1065789Sahrens (void) printf("\n"); 1066789Sahrens } 1067789Sahrens 1068789Sahrens /* run for each object */ 10692379Ssjelinek return (zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, NULL, 1070789Sahrens get_callback, &cb)); 10712379Ssjelinek 1072789Sahrens } 1073789Sahrens 1074789Sahrens /* 1075789Sahrens * inherit [-r] <property> <fs|vol> ... 1076789Sahrens * 1077789Sahrens * -r Recurse over all children 1078789Sahrens * 1079789Sahrens * For each dataset specified on the command line, inherit the given property 1080789Sahrens * from its parent. Inheriting a property at the pool level will cause it to 1081789Sahrens * use the default value. The '-r' flag will recurse over all children, and is 1082789Sahrens * useful for setting a property on a hierarchy-wide basis, regardless of any 1083789Sahrens * local modifications for each dataset. 1084789Sahrens */ 1085789Sahrens static int 1086789Sahrens inherit_callback(zfs_handle_t *zhp, void *data) 1087789Sahrens { 1088789Sahrens zfs_prop_t prop = (zfs_prop_t)data; 1089789Sahrens 1090789Sahrens return (zfs_prop_inherit(zhp, prop) != 0); 1091789Sahrens } 1092789Sahrens 1093789Sahrens static int 1094789Sahrens zfs_do_inherit(int argc, char **argv) 1095789Sahrens { 10962082Seschrock boolean_t recurse = B_FALSE; 1097789Sahrens int c; 1098789Sahrens zfs_prop_t prop; 1099789Sahrens char *propname; 1100789Sahrens 1101789Sahrens /* check options */ 1102789Sahrens while ((c = getopt(argc, argv, "r")) != -1) { 1103789Sahrens switch (c) { 1104789Sahrens case 'r': 11052082Seschrock recurse = B_TRUE; 1106789Sahrens break; 1107789Sahrens case '?': 1108789Sahrens default: 1109789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1110789Sahrens optopt); 11112082Seschrock usage(B_FALSE); 1112789Sahrens } 1113789Sahrens } 1114789Sahrens 1115789Sahrens argc -= optind; 1116789Sahrens argv += optind; 1117789Sahrens 1118789Sahrens /* check number of arguments */ 1119789Sahrens if (argc < 1) { 1120789Sahrens (void) fprintf(stderr, gettext("missing property argument\n")); 11212082Seschrock usage(B_FALSE); 1122789Sahrens } 1123789Sahrens if (argc < 2) { 1124789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 11252082Seschrock usage(B_FALSE); 1126789Sahrens } 1127789Sahrens 1128789Sahrens propname = argv[0]; 1129789Sahrens 1130789Sahrens /* 1131789Sahrens * Get and validate the property before iterating over the datasets. We 1132789Sahrens * do this now so as to avoid printing out an error message for each and 1133789Sahrens * every dataset. 1134789Sahrens */ 1135789Sahrens if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) { 1136789Sahrens (void) fprintf(stderr, gettext("invalid property '%s'\n"), 1137789Sahrens propname); 11382082Seschrock usage(B_FALSE); 1139789Sahrens } 1140789Sahrens if (zfs_prop_readonly(prop)) { 1141789Sahrens (void) fprintf(stderr, gettext("%s property is read-only\n"), 1142789Sahrens propname); 1143789Sahrens return (1); 1144789Sahrens } 1145789Sahrens if (!zfs_prop_inheritable(prop)) { 1146789Sahrens (void) fprintf(stderr, gettext("%s property cannot be " 1147789Sahrens "inherited\n"), propname); 1148789Sahrens (void) fprintf(stderr, gettext("use 'zfs set %s=none' to " 1149789Sahrens "clear\n"), propname); 1150789Sahrens return (1); 1151789Sahrens } 1152789Sahrens 1153789Sahrens return (zfs_for_each(argc - 1, argv + 1, recurse, 11542379Ssjelinek ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, 1155789Sahrens inherit_callback, (void *)prop)); 1156789Sahrens } 1157789Sahrens 1158789Sahrens /* 11592379Ssjelinek * list [-rH] [-o property[,property]...] [-t type[,type]...] 11602379Ssjelinek * [-s property [-s property]...] [-S property [-S property]...] 11612379Ssjelinek * <dataset> ... 1162789Sahrens * 1163789Sahrens * -r Recurse over all children 1164789Sahrens * -H Scripted mode; elide headers and separate colums by tabs 1165789Sahrens * -o Control which fields to display. 1166866Seschrock * -t Control which object types to display. 11672379Ssjelinek * -s Specify sort columns, descending order. 11682379Ssjelinek * -S Specify sort columns, ascending order. 1169789Sahrens * 1170789Sahrens * When given no arguments, lists all filesystems in the system. 1171789Sahrens * Otherwise, list the specified datasets, optionally recursing down them if 1172789Sahrens * '-r' is specified. 1173789Sahrens */ 1174789Sahrens typedef struct list_cbdata { 11752082Seschrock boolean_t cb_first; 11762082Seschrock boolean_t cb_scripted; 1177866Seschrock zfs_prop_t cb_fields[ZFS_NPROP_ALL]; 1178866Seschrock int cb_fieldcount; 1179789Sahrens } list_cbdata_t; 1180789Sahrens 1181789Sahrens /* 1182789Sahrens * Given a list of columns to display, output appropriate headers for each one. 1183789Sahrens */ 1184789Sahrens static void 1185866Seschrock print_header(zfs_prop_t *fields, size_t count) 1186789Sahrens { 1187789Sahrens int i; 1188789Sahrens 1189789Sahrens for (i = 0; i < count; i++) { 1190789Sahrens if (i != 0) 1191789Sahrens (void) printf(" "); 1192789Sahrens if (i == count - 1) 1193789Sahrens (void) printf("%s", zfs_prop_column_name(fields[i])); 1194789Sahrens else /* LINTED - format specifier */ 1195789Sahrens (void) printf(zfs_prop_column_format(fields[i]), 1196789Sahrens zfs_prop_column_name(fields[i])); 1197789Sahrens } 1198789Sahrens 1199789Sahrens (void) printf("\n"); 1200789Sahrens } 1201789Sahrens 1202789Sahrens /* 1203789Sahrens * Given a dataset and a list of fields, print out all the properties according 1204789Sahrens * to the described layout. 1205789Sahrens */ 1206789Sahrens static void 1207866Seschrock print_dataset(zfs_handle_t *zhp, zfs_prop_t *fields, size_t count, int scripted) 1208789Sahrens { 1209789Sahrens int i; 1210789Sahrens char property[ZFS_MAXPROPLEN]; 1211789Sahrens 1212789Sahrens for (i = 0; i < count; i++) { 1213789Sahrens if (i != 0) { 1214789Sahrens if (scripted) 1215789Sahrens (void) printf("\t"); 1216789Sahrens else 1217789Sahrens (void) printf(" "); 1218789Sahrens } 1219789Sahrens 1220789Sahrens if (zfs_prop_get(zhp, fields[i], property, 12212082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) != 0) 1222789Sahrens (void) strlcpy(property, "-", sizeof (property)); 1223789Sahrens 1224866Seschrock /* 1225866Seschrock * If this is being called in scripted mode, or if this is the 1226866Seschrock * last column and it is left-justified, don't include a width 1227866Seschrock * format specifier. 1228866Seschrock */ 1229866Seschrock if (scripted || (i == count - 1 && 1230866Seschrock strchr(zfs_prop_column_format(fields[i]), '-') != NULL)) 1231789Sahrens (void) printf("%s", property); 1232789Sahrens else /* LINTED - format specifier */ 1233789Sahrens (void) printf(zfs_prop_column_format(fields[i]), 1234789Sahrens property); 1235789Sahrens } 1236789Sahrens 1237789Sahrens (void) printf("\n"); 1238789Sahrens } 1239789Sahrens 1240789Sahrens /* 1241789Sahrens * Generic callback function to list a dataset or snapshot. 1242789Sahrens */ 1243789Sahrens static int 1244789Sahrens list_callback(zfs_handle_t *zhp, void *data) 1245789Sahrens { 1246789Sahrens list_cbdata_t *cbp = data; 1247789Sahrens 1248789Sahrens if (cbp->cb_first) { 1249789Sahrens if (!cbp->cb_scripted) 1250789Sahrens print_header(cbp->cb_fields, cbp->cb_fieldcount); 12512082Seschrock cbp->cb_first = B_FALSE; 1252789Sahrens } 1253789Sahrens 1254789Sahrens print_dataset(zhp, cbp->cb_fields, cbp->cb_fieldcount, 1255789Sahrens cbp->cb_scripted); 1256789Sahrens 1257789Sahrens return (0); 1258789Sahrens } 1259789Sahrens 1260789Sahrens static int 1261789Sahrens zfs_do_list(int argc, char **argv) 1262789Sahrens { 1263789Sahrens int c; 12642082Seschrock boolean_t recurse = B_FALSE; 12652082Seschrock boolean_t scripted = B_FALSE; 1266789Sahrens static char default_fields[] = 1267789Sahrens "name,used,available,referenced,mountpoint"; 1268789Sahrens int types = ZFS_TYPE_ANY; 1269789Sahrens char *fields = NULL; 1270789Sahrens char *basic_fields = default_fields; 1271789Sahrens list_cbdata_t cb = { 0 }; 1272789Sahrens char *value; 1273789Sahrens int ret; 1274789Sahrens char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL }; 1275866Seschrock char *badopt; 1276866Seschrock int alloffset; 12772379Ssjelinek zfs_sort_column_t *sortcol = NULL; 1278789Sahrens 1279789Sahrens /* check options */ 12802379Ssjelinek while ((c = getopt(argc, argv, ":o:rt:Hs:S:")) != -1) { 12812379Ssjelinek zfs_prop_t prop; 12822379Ssjelinek 1283789Sahrens switch (c) { 1284789Sahrens case 'o': 1285789Sahrens fields = optarg; 1286789Sahrens break; 1287789Sahrens case 'r': 12882082Seschrock recurse = B_TRUE; 1289789Sahrens break; 1290789Sahrens case 'H': 12912082Seschrock scripted = B_TRUE; 1292789Sahrens break; 12932379Ssjelinek case 's': 12942379Ssjelinek if ((prop = zfs_name_to_prop(optarg)) == 12952379Ssjelinek ZFS_PROP_INVAL) { 12962379Ssjelinek (void) fprintf(stderr, 12972379Ssjelinek gettext("invalid property '%s'\n"), optarg); 12982379Ssjelinek usage(B_FALSE); 12992379Ssjelinek } 13002379Ssjelinek zfs_add_sort_column(&sortcol, prop, B_FALSE); 13012379Ssjelinek break; 13022379Ssjelinek case 'S': 13032379Ssjelinek if ((prop = zfs_name_to_prop(optarg)) == 13042379Ssjelinek ZFS_PROP_INVAL) { 13052379Ssjelinek (void) fprintf(stderr, 13062379Ssjelinek gettext("invalid property '%s'\n"), optarg); 13072379Ssjelinek usage(B_FALSE); 13082379Ssjelinek } 13092379Ssjelinek zfs_add_sort_column(&sortcol, prop, B_TRUE); 13102379Ssjelinek break; 1311789Sahrens case 't': 1312789Sahrens types = 0; 1313789Sahrens while (*optarg != '\0') { 1314789Sahrens switch (getsubopt(&optarg, type_subopts, 1315789Sahrens &value)) { 1316789Sahrens case 0: 1317789Sahrens types |= ZFS_TYPE_FILESYSTEM; 1318789Sahrens break; 1319789Sahrens case 1: 1320789Sahrens types |= ZFS_TYPE_VOLUME; 1321789Sahrens break; 1322789Sahrens case 2: 1323789Sahrens types |= ZFS_TYPE_SNAPSHOT; 1324789Sahrens break; 1325789Sahrens default: 1326789Sahrens (void) fprintf(stderr, 1327789Sahrens gettext("invalid type '%s'\n"), 1328789Sahrens value); 13292082Seschrock usage(B_FALSE); 1330789Sahrens } 1331789Sahrens } 1332789Sahrens break; 1333789Sahrens case ':': 1334789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1335789Sahrens "'%c' option\n"), optopt); 13362082Seschrock usage(B_FALSE); 1337789Sahrens break; 1338789Sahrens case '?': 1339789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1340789Sahrens optopt); 13412082Seschrock usage(B_FALSE); 1342789Sahrens } 1343789Sahrens } 1344789Sahrens 1345789Sahrens argc -= optind; 1346789Sahrens argv += optind; 1347789Sahrens 1348789Sahrens if (fields == NULL) 1349789Sahrens fields = basic_fields; 1350789Sahrens 1351866Seschrock /* 1352866Seschrock * If the user specifies '-o all', the zfs_get_proplist() doesn't 1353866Seschrock * normally include the name of the dataset. For 'zfs list', we always 1354866Seschrock * want this property to be first. 1355866Seschrock */ 1356866Seschrock if (strcmp(fields, "all") == 0) { 1357866Seschrock cb.cb_fields[0] = ZFS_PROP_NAME; 1358866Seschrock alloffset = 1; 1359866Seschrock } else { 1360866Seschrock alloffset = 0; 1361789Sahrens } 1362789Sahrens 1363866Seschrock if ((ret = zfs_get_proplist(fields, cb.cb_fields + alloffset, 1364866Seschrock ZFS_NPROP_ALL - alloffset, &cb.cb_fieldcount, &badopt)) != 0) { 1365866Seschrock if (ret == EINVAL) 1366866Seschrock (void) fprintf(stderr, gettext("invalid property " 1367866Seschrock "'%s'\n"), badopt); 1368866Seschrock else 1369866Seschrock (void) fprintf(stderr, gettext("too many properties " 1370866Seschrock "specified\n")); 13712082Seschrock usage(B_FALSE); 1372866Seschrock } 1373866Seschrock 1374866Seschrock cb.cb_fieldcount += alloffset; 1375789Sahrens cb.cb_scripted = scripted; 13762082Seschrock cb.cb_first = B_TRUE; 1377789Sahrens 13782379Ssjelinek ret = zfs_for_each(argc, argv, recurse, types, sortcol, 13792379Ssjelinek list_callback, &cb); 13802379Ssjelinek 13812379Ssjelinek zfs_free_sort_columns(sortcol); 1382789Sahrens 13832082Seschrock if (ret == 0 && cb.cb_first) 1384789Sahrens (void) printf(gettext("no datasets available\n")); 1385789Sahrens 1386789Sahrens return (ret); 1387789Sahrens } 1388789Sahrens 1389789Sahrens /* 1390789Sahrens * zfs rename <fs | snap | vol> <fs | snap | vol> 1391789Sahrens * 1392789Sahrens * Renames the given dataset to another of the same type. 1393789Sahrens */ 1394789Sahrens /* ARGSUSED */ 1395789Sahrens static int 1396789Sahrens zfs_do_rename(int argc, char **argv) 1397789Sahrens { 1398789Sahrens zfs_handle_t *zhp; 13992082Seschrock int ret; 1400789Sahrens 1401789Sahrens /* check options */ 1402789Sahrens if (argc > 1 && argv[1][0] == '-') { 1403789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1404789Sahrens argv[1][1]); 14052082Seschrock usage(B_FALSE); 1406789Sahrens } 1407789Sahrens 1408789Sahrens /* check number of arguments */ 1409789Sahrens if (argc < 2) { 1410789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 1411789Sahrens "argument\n")); 14122082Seschrock usage(B_FALSE); 1413789Sahrens } 1414789Sahrens if (argc < 3) { 1415789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 1416789Sahrens "argument\n")); 14172082Seschrock usage(B_FALSE); 1418789Sahrens } 1419789Sahrens if (argc > 3) { 1420789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 14212082Seschrock usage(B_FALSE); 1422789Sahrens } 1423789Sahrens 14242082Seschrock if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_ANY)) == NULL) 1425789Sahrens return (1); 1426789Sahrens 14272082Seschrock ret = (zfs_rename(zhp, argv[2]) != 0); 14282082Seschrock 14292082Seschrock zfs_close(zhp); 14302082Seschrock return (ret); 14312082Seschrock } 14322082Seschrock 14332082Seschrock /* 14342082Seschrock * zfs promote <fs> 14352082Seschrock * 14362082Seschrock * Promotes the given clone fs to be the parent 14372082Seschrock */ 14382082Seschrock /* ARGSUSED */ 14392082Seschrock static int 14402082Seschrock zfs_do_promote(int argc, char **argv) 14412082Seschrock { 14422082Seschrock zfs_handle_t *zhp; 14432082Seschrock int ret; 14442082Seschrock 14452082Seschrock /* check options */ 14462082Seschrock if (argc > 1 && argv[1][0] == '-') { 14472082Seschrock (void) fprintf(stderr, gettext("invalid option '%c'\n"), 14482082Seschrock argv[1][1]); 14492082Seschrock usage(B_FALSE); 14502082Seschrock } 14512082Seschrock 14522082Seschrock /* check number of arguments */ 14532082Seschrock if (argc < 2) { 14542082Seschrock (void) fprintf(stderr, gettext("missing clone filesystem" 14552082Seschrock "argument\n")); 14562082Seschrock usage(B_FALSE); 14572082Seschrock } 14582082Seschrock if (argc > 2) { 14592082Seschrock (void) fprintf(stderr, gettext("too many arguments\n")); 14602082Seschrock usage(B_FALSE); 14612082Seschrock } 14622082Seschrock 14632082Seschrock zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 14642082Seschrock if (zhp == NULL) 14652082Seschrock return (1); 14662082Seschrock 14672082Seschrock ret = (zfs_promote(zhp) != 0); 14682082Seschrock 1469789Sahrens zfs_close(zhp); 1470789Sahrens return (ret); 1471789Sahrens } 1472789Sahrens 1473789Sahrens /* 1474789Sahrens * zfs rollback [-rfR] <snapshot> 1475789Sahrens * 1476789Sahrens * -r Delete any intervening snapshots before doing rollback 1477789Sahrens * -R Delete any snapshots and their clones 1478789Sahrens * -f Force unmount filesystems, even if they are in use. 1479789Sahrens * 1480789Sahrens * Given a filesystem, rollback to a specific snapshot, discarding any changes 1481789Sahrens * since then and making it the active dataset. If more recent snapshots exist, 1482789Sahrens * the command will complain unless the '-r' flag is given. 1483789Sahrens */ 1484789Sahrens typedef struct rollback_cbdata { 1485789Sahrens uint64_t cb_create; 14862082Seschrock boolean_t cb_first; 1487789Sahrens int cb_doclones; 1488789Sahrens char *cb_target; 1489789Sahrens int cb_error; 14902082Seschrock boolean_t cb_recurse; 14912082Seschrock boolean_t cb_dependent; 1492789Sahrens } rollback_cbdata_t; 1493789Sahrens 1494789Sahrens /* 1495789Sahrens * Report any snapshots more recent than the one specified. Used when '-r' is 1496789Sahrens * not specified. We reuse this same callback for the snapshot dependents - if 1497789Sahrens * 'cb_dependent' is set, then this is a dependent and we should report it 1498789Sahrens * without checking the transaction group. 1499789Sahrens */ 1500789Sahrens static int 1501789Sahrens rollback_check(zfs_handle_t *zhp, void *data) 1502789Sahrens { 1503789Sahrens rollback_cbdata_t *cbp = data; 1504789Sahrens 15052082Seschrock if (cbp->cb_doclones) { 15062082Seschrock zfs_close(zhp); 1507789Sahrens return (0); 15082082Seschrock } 1509789Sahrens 1510789Sahrens if (!cbp->cb_dependent) { 1511789Sahrens if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && 15121294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 1513789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 1514789Sahrens cbp->cb_create) { 1515789Sahrens 1516789Sahrens if (cbp->cb_first && !cbp->cb_recurse) { 1517789Sahrens (void) fprintf(stderr, gettext("cannot " 1518789Sahrens "rollback to '%s': more recent snapshots " 1519789Sahrens "exist\n"), 1520789Sahrens cbp->cb_target); 1521789Sahrens (void) fprintf(stderr, gettext("use '-r' to " 1522789Sahrens "force deletion of the following " 1523789Sahrens "snapshots:\n")); 1524789Sahrens cbp->cb_first = 0; 1525789Sahrens cbp->cb_error = 1; 1526789Sahrens } 1527789Sahrens 1528789Sahrens if (cbp->cb_recurse) { 15292082Seschrock cbp->cb_dependent = B_TRUE; 1530*2474Seschrock if (zfs_iter_dependents(zhp, B_TRUE, 1531*2474Seschrock rollback_check, cbp) != 0) { 1532*2474Seschrock zfs_close(zhp); 1533*2474Seschrock return (-1); 1534*2474Seschrock } 15352082Seschrock cbp->cb_dependent = B_FALSE; 1536789Sahrens } else { 1537789Sahrens (void) fprintf(stderr, "%s\n", 1538789Sahrens zfs_get_name(zhp)); 1539789Sahrens } 1540789Sahrens } 1541789Sahrens } else { 1542789Sahrens if (cbp->cb_first && cbp->cb_recurse) { 1543789Sahrens (void) fprintf(stderr, gettext("cannot rollback to " 1544789Sahrens "'%s': clones of previous snapshots exist\n"), 1545789Sahrens cbp->cb_target); 1546789Sahrens (void) fprintf(stderr, gettext("use '-R' to " 1547789Sahrens "force deletion of the following clones and " 1548789Sahrens "dependents:\n")); 1549789Sahrens cbp->cb_first = 0; 1550789Sahrens cbp->cb_error = 1; 1551789Sahrens } 1552789Sahrens 1553789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1554789Sahrens } 1555789Sahrens 1556789Sahrens zfs_close(zhp); 1557789Sahrens return (0); 1558789Sahrens } 1559789Sahrens 1560789Sahrens static int 1561789Sahrens zfs_do_rollback(int argc, char **argv) 1562789Sahrens { 1563789Sahrens int ret; 1564789Sahrens int c; 1565789Sahrens rollback_cbdata_t cb = { 0 }; 1566789Sahrens zfs_handle_t *zhp, *snap; 1567789Sahrens char parentname[ZFS_MAXNAMELEN]; 1568789Sahrens char *delim; 15691294Slling int force = 0; 1570789Sahrens 1571789Sahrens /* check options */ 1572789Sahrens while ((c = getopt(argc, argv, "rfR")) != -1) { 1573789Sahrens switch (c) { 1574789Sahrens case 'f': 15751294Slling force = 1; 1576789Sahrens break; 1577789Sahrens case 'r': 1578789Sahrens cb.cb_recurse = 1; 1579789Sahrens break; 1580789Sahrens case 'R': 1581789Sahrens cb.cb_recurse = 1; 1582789Sahrens cb.cb_doclones = 1; 1583789Sahrens break; 1584789Sahrens case '?': 1585789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1586789Sahrens optopt); 15872082Seschrock usage(B_FALSE); 1588789Sahrens } 1589789Sahrens } 1590789Sahrens 1591789Sahrens argc -= optind; 1592789Sahrens argv += optind; 1593789Sahrens 1594789Sahrens /* check number of arguments */ 1595789Sahrens if (argc < 1) { 1596789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 15972082Seschrock usage(B_FALSE); 1598789Sahrens } 1599789Sahrens if (argc > 1) { 1600789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 16012082Seschrock usage(B_FALSE); 1602789Sahrens } 1603789Sahrens 1604789Sahrens /* open the snapshot */ 16052082Seschrock if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1606789Sahrens return (1); 1607789Sahrens 16081294Slling /* open the parent dataset */ 16091294Slling (void) strlcpy(parentname, argv[0], sizeof (parentname)); 1610789Sahrens verify((delim = strrchr(parentname, '@')) != NULL); 1611789Sahrens *delim = '\0'; 16122082Seschrock if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_ANY)) == NULL) { 1613789Sahrens zfs_close(snap); 1614789Sahrens return (1); 1615789Sahrens } 1616789Sahrens 1617789Sahrens /* 1618789Sahrens * Check for more recent snapshots and/or clones based on the presence 1619789Sahrens * of '-r' and '-R'. 1620789Sahrens */ 16211294Slling cb.cb_target = argv[0]; 16221294Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 16232082Seschrock cb.cb_first = B_TRUE; 1624789Sahrens cb.cb_error = 0; 1625*2474Seschrock if ((ret = zfs_iter_children(zhp, rollback_check, &cb)) != 0) 1626*2474Seschrock goto out; 1627789Sahrens 1628789Sahrens if ((ret = cb.cb_error) != 0) 1629789Sahrens goto out; 1630789Sahrens 1631789Sahrens /* 16321294Slling * Rollback parent to the given snapshot. 1633789Sahrens */ 16341294Slling ret = zfs_rollback(zhp, snap, force); 1635789Sahrens 1636789Sahrens out: 1637789Sahrens zfs_close(snap); 1638789Sahrens zfs_close(zhp); 1639789Sahrens 1640789Sahrens if (ret == 0) 1641789Sahrens return (0); 1642789Sahrens else 1643789Sahrens return (1); 1644789Sahrens } 1645789Sahrens 1646789Sahrens /* 1647789Sahrens * zfs set property=value { fs | snap | vol } ... 1648789Sahrens * 1649789Sahrens * Sets the given property for all datasets specified on the command line. 1650789Sahrens */ 1651789Sahrens typedef struct set_cbdata { 1652789Sahrens char *cb_propname; 1653789Sahrens char *cb_value; 1654789Sahrens zfs_prop_t cb_prop; 1655789Sahrens } set_cbdata_t; 1656789Sahrens 1657789Sahrens static int 1658789Sahrens set_callback(zfs_handle_t *zhp, void *data) 1659789Sahrens { 1660789Sahrens set_cbdata_t *cbp = data; 1661789Sahrens int ret = 1; 1662789Sahrens 1663789Sahrens /* don't allow setting of properties for snapshots */ 1664789Sahrens if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { 1665789Sahrens (void) fprintf(stderr, gettext("cannot set %s property for " 1666789Sahrens "'%s': snapshot properties cannot be modified\n"), 1667789Sahrens cbp->cb_propname, zfs_get_name(zhp)); 1668789Sahrens return (1); 1669789Sahrens } 1670789Sahrens 1671789Sahrens /* 16721133Seschrock * If we're changing the volsize, make sure the value is appropriate, 16731133Seschrock * and set the reservation if this is a non-sparse volume. 1674789Sahrens */ 1675789Sahrens if (cbp->cb_prop == ZFS_PROP_VOLSIZE && 16761133Seschrock zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 1677789Sahrens uint64_t volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1678789Sahrens uint64_t avail = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE); 16791133Seschrock uint64_t reservation = zfs_prop_get_int(zhp, 16801133Seschrock ZFS_PROP_RESERVATION); 16811133Seschrock uint64_t blocksize = zfs_prop_get_int(zhp, 16821133Seschrock ZFS_PROP_VOLBLOCKSIZE); 1683789Sahrens uint64_t value; 1684789Sahrens 1685789Sahrens verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); 1686789Sahrens 16871133Seschrock if (value % blocksize != 0) { 16881133Seschrock char buf[64]; 16891133Seschrock 16901133Seschrock zfs_nicenum(blocksize, buf, sizeof (buf)); 16911133Seschrock (void) fprintf(stderr, gettext("cannot set %s for " 16921133Seschrock "'%s': must be a multiple of volume block size " 16931133Seschrock "(%s)\n"), cbp->cb_propname, zfs_get_name(zhp), 16941133Seschrock buf); 1695789Sahrens return (1); 1696789Sahrens } 1697789Sahrens 16981133Seschrock if (value == 0) { 16991133Seschrock (void) fprintf(stderr, gettext("cannot set %s for " 17001133Seschrock "'%s': cannot be zero\n"), cbp->cb_propname, 17011133Seschrock zfs_get_name(zhp)); 1702789Sahrens return (1); 1703789Sahrens } 17041133Seschrock 17051133Seschrock if (volsize == reservation) { 17061133Seschrock if (value > volsize && (value - volsize) > avail) { 17071133Seschrock (void) fprintf(stderr, gettext("cannot set " 17081133Seschrock "%s property for '%s': volume size exceeds " 17091133Seschrock "amount of available space\n"), 17101133Seschrock cbp->cb_propname, zfs_get_name(zhp)); 17111133Seschrock return (1); 17121133Seschrock } 17131133Seschrock 17141133Seschrock if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, 17151133Seschrock cbp->cb_value) != 0) { 17161133Seschrock (void) fprintf(stderr, gettext("volsize and " 17171133Seschrock "reservation must remain equal\n")); 17181133Seschrock return (1); 17191133Seschrock } 17201133Seschrock } 1721789Sahrens } 1722789Sahrens 1723789Sahrens /* 1724789Sahrens * Do not allow the reservation to be set above the volume size. We do 1725789Sahrens * this here instead of inside libzfs because libzfs violates this rule 1726789Sahrens * internally. 1727789Sahrens */ 1728789Sahrens if (cbp->cb_prop == ZFS_PROP_RESERVATION && 1729789Sahrens zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 1730789Sahrens uint64_t value; 1731789Sahrens uint64_t volsize; 1732789Sahrens 1733789Sahrens volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1734789Sahrens if (strcmp(cbp->cb_value, "none") == 0) 1735789Sahrens value = 0; 1736789Sahrens else 1737789Sahrens verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); 1738789Sahrens 1739789Sahrens if (value > volsize) { 1740789Sahrens (void) fprintf(stderr, gettext("cannot set %s " 1741789Sahrens "for '%s': size is greater than current " 1742789Sahrens "volume size\n"), cbp->cb_propname, 1743789Sahrens zfs_get_name(zhp)); 1744789Sahrens return (-1); 1745789Sahrens } 1746789Sahrens } 1747789Sahrens 17482169Snd150628 if (zfs_prop_set(zhp, cbp->cb_prop, cbp->cb_value) != 0) { 17492169Snd150628 switch (libzfs_errno(g_zfs)) { 17502169Snd150628 case EZFS_MOUNTFAILED: 17512169Snd150628 (void) fprintf(stderr, gettext("property may be set " 17522169Snd150628 "but unable to remount filesystem\n")); 17532169Snd150628 break; 17542169Snd150628 case EZFS_SHAREFAILED: 17552169Snd150628 (void) fprintf(stderr, gettext("property may be set " 17562169Snd150628 "but unable to reshare filesystem\n")); 17572169Snd150628 break; 17582169Snd150628 } 1759789Sahrens return (1); 17602169Snd150628 } 1761789Sahrens ret = 0; 1762789Sahrens error: 1763789Sahrens return (ret); 1764789Sahrens } 1765789Sahrens 1766789Sahrens static int 1767789Sahrens zfs_do_set(int argc, char **argv) 1768789Sahrens { 1769789Sahrens set_cbdata_t cb; 1770789Sahrens 1771789Sahrens /* check for options */ 1772789Sahrens if (argc > 1 && argv[1][0] == '-') { 1773789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1774789Sahrens argv[1][1]); 17752082Seschrock usage(B_FALSE); 1776789Sahrens } 1777789Sahrens 1778789Sahrens /* check number of arguments */ 1779789Sahrens if (argc < 2) { 1780789Sahrens (void) fprintf(stderr, gettext("missing property=value " 1781789Sahrens "argument\n")); 17822082Seschrock usage(B_FALSE); 1783789Sahrens } 1784789Sahrens if (argc < 3) { 1785789Sahrens (void) fprintf(stderr, gettext("missing dataset name\n")); 17862082Seschrock usage(B_FALSE); 1787789Sahrens } 1788789Sahrens 1789789Sahrens /* validate property=value argument */ 1790789Sahrens cb.cb_propname = argv[1]; 1791789Sahrens if ((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) { 1792789Sahrens (void) fprintf(stderr, gettext("missing value in " 1793789Sahrens "property=value argument\n")); 17942082Seschrock usage(B_FALSE); 1795789Sahrens } 1796789Sahrens 1797789Sahrens *cb.cb_value = '\0'; 1798789Sahrens cb.cb_value++; 1799789Sahrens 1800789Sahrens if (*cb.cb_propname == '\0') { 1801789Sahrens (void) fprintf(stderr, 1802789Sahrens gettext("missing property in property=value argument\n")); 18032082Seschrock usage(B_FALSE); 1804789Sahrens } 1805789Sahrens if (*cb.cb_value == '\0') { 1806789Sahrens (void) fprintf(stderr, 1807789Sahrens gettext("missing value in property=value argument\n")); 18082082Seschrock usage(B_FALSE); 1809789Sahrens } 1810789Sahrens 1811789Sahrens /* get the property type */ 1812789Sahrens if ((cb.cb_prop = zfs_name_to_prop(cb.cb_propname)) == 1813789Sahrens ZFS_PROP_INVAL) { 1814789Sahrens (void) fprintf(stderr, 1815789Sahrens gettext("invalid property '%s'\n"), cb.cb_propname); 18162082Seschrock usage(B_FALSE); 1817789Sahrens } 1818789Sahrens 1819789Sahrens /* 1820789Sahrens * Validate that the value is appropriate for this property. We do this 1821789Sahrens * once now so we don't generate multiple errors each time we try to 1822789Sahrens * apply it to a dataset. 1823789Sahrens */ 18242082Seschrock if (zfs_prop_validate(g_zfs, cb.cb_prop, cb.cb_value, NULL) != 0) 1825789Sahrens return (1); 1826789Sahrens 18272082Seschrock return (zfs_for_each(argc - 2, argv + 2, B_FALSE, 18282379Ssjelinek ZFS_TYPE_ANY, NULL, set_callback, &cb)); 1829789Sahrens } 1830789Sahrens 1831789Sahrens /* 18322199Sahrens * zfs snapshot [-r] <fs@snap> 1833789Sahrens * 1834789Sahrens * Creates a snapshot with the given name. While functionally equivalent to 1835789Sahrens * 'zfs create', it is a separate command to diffferentiate intent. 1836789Sahrens */ 1837789Sahrens static int 1838789Sahrens zfs_do_snapshot(int argc, char **argv) 1839789Sahrens { 18402199Sahrens int recursive = B_FALSE; 18412199Sahrens int ret; 18422199Sahrens char c; 18432199Sahrens 1844789Sahrens /* check options */ 18452199Sahrens while ((c = getopt(argc, argv, ":r")) != -1) { 18462199Sahrens switch (c) { 18472199Sahrens case 'r': 18482199Sahrens recursive = B_TRUE; 18492199Sahrens break; 18502199Sahrens case '?': 18512199Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 18522199Sahrens optopt); 18532199Sahrens usage(B_FALSE); 18542199Sahrens } 1855789Sahrens } 1856789Sahrens 18572199Sahrens argc -= optind; 18582199Sahrens argv += optind; 18592199Sahrens 1860789Sahrens /* check number of arguments */ 18612199Sahrens if (argc < 1) { 1862789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 18632082Seschrock usage(B_FALSE); 1864789Sahrens } 18652199Sahrens if (argc > 1) { 1866789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 18672082Seschrock usage(B_FALSE); 1868789Sahrens } 1869789Sahrens 18702199Sahrens ret = zfs_snapshot(g_zfs, argv[0], recursive); 18712199Sahrens if (ret && recursive) 18722199Sahrens (void) fprintf(stderr, gettext("no snapshots were created\n")); 18732199Sahrens return (ret != 0); 18742199Sahrens 1875789Sahrens } 1876789Sahrens 1877789Sahrens /* 18781749Sahrens * zfs send [-i <fs@snap>] <fs@snap> 1879789Sahrens * 1880789Sahrens * Send a backup stream to stdout. 1881789Sahrens */ 1882789Sahrens static int 18831749Sahrens zfs_do_send(int argc, char **argv) 1884789Sahrens { 1885789Sahrens char *fromname = NULL; 1886789Sahrens zfs_handle_t *zhp_from = NULL, *zhp_to; 1887789Sahrens int c, err; 1888789Sahrens 1889789Sahrens /* check options */ 1890789Sahrens while ((c = getopt(argc, argv, ":i:")) != -1) { 1891789Sahrens switch (c) { 1892789Sahrens case 'i': 1893789Sahrens fromname = optarg; 1894789Sahrens break; 1895789Sahrens case ':': 1896789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1897789Sahrens "'%c' option\n"), optopt); 18982082Seschrock usage(B_FALSE); 1899789Sahrens break; 1900789Sahrens case '?': 1901789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1902789Sahrens optopt); 19032082Seschrock usage(B_FALSE); 1904789Sahrens } 1905789Sahrens } 1906789Sahrens 1907789Sahrens argc -= optind; 1908789Sahrens argv += optind; 1909789Sahrens 1910789Sahrens /* check number of arguments */ 1911789Sahrens if (argc < 1) { 1912789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 19132082Seschrock usage(B_FALSE); 1914789Sahrens } 1915789Sahrens if (argc > 1) { 1916789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 19172082Seschrock usage(B_FALSE); 1918789Sahrens } 1919789Sahrens 1920789Sahrens if (isatty(STDOUT_FILENO)) { 1921789Sahrens (void) fprintf(stderr, 19221749Sahrens gettext("Error: Stream can not be written " 1923789Sahrens "to a terminal.\n" 1924789Sahrens "You must redirect standard output.\n")); 1925789Sahrens return (1); 1926789Sahrens } 1927789Sahrens 1928789Sahrens if (fromname) { 19292082Seschrock if ((zhp_from = zfs_open(g_zfs, fromname, 19302082Seschrock ZFS_TYPE_SNAPSHOT)) == NULL) 1931789Sahrens return (1); 1932789Sahrens } 19332082Seschrock if ((zhp_to = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1934789Sahrens return (1); 1935789Sahrens 19361749Sahrens err = zfs_send(zhp_to, zhp_from); 1937789Sahrens 1938789Sahrens if (zhp_from) 1939789Sahrens zfs_close(zhp_from); 1940789Sahrens zfs_close(zhp_to); 1941789Sahrens 1942789Sahrens return (err != 0); 1943789Sahrens } 1944789Sahrens 1945789Sahrens /* 19461749Sahrens * zfs receive <fs@snap> 1947789Sahrens * 1948789Sahrens * Restore a backup stream from stdin. 1949789Sahrens */ 1950789Sahrens static int 19511749Sahrens zfs_do_receive(int argc, char **argv) 1952789Sahrens { 1953789Sahrens int c, err; 19542082Seschrock boolean_t isprefix = B_FALSE; 19552082Seschrock boolean_t dryrun = B_FALSE; 19562082Seschrock boolean_t verbose = B_FALSE; 1957789Sahrens 1958789Sahrens /* check options */ 1959789Sahrens while ((c = getopt(argc, argv, ":dnv")) != -1) { 1960789Sahrens switch (c) { 1961789Sahrens case 'd': 19622082Seschrock isprefix = B_TRUE; 1963789Sahrens break; 1964789Sahrens case 'n': 19652082Seschrock dryrun = B_TRUE; 1966789Sahrens break; 1967789Sahrens case 'v': 19682082Seschrock verbose = B_TRUE; 1969789Sahrens break; 1970789Sahrens case ':': 1971789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1972789Sahrens "'%c' option\n"), optopt); 19732082Seschrock usage(B_FALSE); 1974789Sahrens break; 1975789Sahrens case '?': 1976789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1977789Sahrens optopt); 19782082Seschrock usage(B_FALSE); 1979789Sahrens } 1980789Sahrens } 1981789Sahrens 1982789Sahrens argc -= optind; 1983789Sahrens argv += optind; 1984789Sahrens 1985789Sahrens /* check number of arguments */ 1986789Sahrens if (argc < 1) { 1987789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 19882082Seschrock usage(B_FALSE); 1989789Sahrens } 1990789Sahrens if (argc > 1) { 1991789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 19922082Seschrock usage(B_FALSE); 1993789Sahrens } 1994789Sahrens 1995789Sahrens if (isatty(STDIN_FILENO)) { 1996789Sahrens (void) fprintf(stderr, 1997789Sahrens gettext("Error: Backup stream can not be read " 1998789Sahrens "from a terminal.\n" 1999789Sahrens "You must redirect standard input.\n")); 2000789Sahrens return (1); 2001789Sahrens } 2002789Sahrens 20032082Seschrock err = zfs_receive(g_zfs, argv[0], isprefix, verbose, dryrun); 2004789Sahrens return (err != 0); 2005789Sahrens } 2006789Sahrens 20071356Seschrock typedef struct get_all_cbdata { 20081356Seschrock zfs_handle_t **cb_handles; 20091356Seschrock size_t cb_alloc; 20101356Seschrock size_t cb_used; 20111356Seschrock } get_all_cbdata_t; 20121356Seschrock 20131356Seschrock static int 20141356Seschrock get_one_filesystem(zfs_handle_t *zhp, void *data) 20151356Seschrock { 20161356Seschrock get_all_cbdata_t *cbp = data; 20171356Seschrock 20181356Seschrock /* 20191356Seschrock * Skip any zvols 20201356Seschrock */ 20211356Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 20221356Seschrock zfs_close(zhp); 20231356Seschrock return (0); 20241356Seschrock } 20251356Seschrock 20261356Seschrock if (cbp->cb_alloc == cbp->cb_used) { 20271356Seschrock zfs_handle_t **handles; 20281356Seschrock 20291356Seschrock if (cbp->cb_alloc == 0) 20301356Seschrock cbp->cb_alloc = 64; 20311356Seschrock else 20321356Seschrock cbp->cb_alloc *= 2; 20331356Seschrock 20341356Seschrock handles = safe_malloc(cbp->cb_alloc * sizeof (void *)); 20351356Seschrock 20361356Seschrock if (cbp->cb_handles) { 20371356Seschrock bcopy(cbp->cb_handles, handles, 20381356Seschrock cbp->cb_used * sizeof (void *)); 20391356Seschrock free(cbp->cb_handles); 20401356Seschrock } 20411356Seschrock 20421356Seschrock cbp->cb_handles = handles; 20431356Seschrock } 20441356Seschrock 20451356Seschrock cbp->cb_handles[cbp->cb_used++] = zhp; 20461356Seschrock 20471356Seschrock return (zfs_iter_filesystems(zhp, get_one_filesystem, data)); 20481356Seschrock } 20491356Seschrock 20501356Seschrock static void 20511356Seschrock get_all_filesystems(zfs_handle_t ***fslist, size_t *count) 20521356Seschrock { 20531356Seschrock get_all_cbdata_t cb = { 0 }; 20541356Seschrock 20552082Seschrock (void) zfs_iter_root(g_zfs, get_one_filesystem, &cb); 20561356Seschrock 20571356Seschrock *fslist = cb.cb_handles; 20581356Seschrock *count = cb.cb_used; 20591356Seschrock } 20601356Seschrock 20611356Seschrock static int 20621356Seschrock mountpoint_compare(const void *a, const void *b) 20631356Seschrock { 20641356Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 20651356Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 20661356Seschrock char mounta[MAXPATHLEN]; 20671356Seschrock char mountb[MAXPATHLEN]; 20681356Seschrock 20691356Seschrock verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 20702082Seschrock sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 20711356Seschrock verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 20722082Seschrock sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 20731356Seschrock 20741356Seschrock return (strcmp(mounta, mountb)); 20751356Seschrock } 2076789Sahrens 2077789Sahrens /* 2078789Sahrens * Generic callback for sharing or mounting filesystems. Because the code is so 2079789Sahrens * similar, we have a common function with an extra parameter to determine which 2080789Sahrens * mode we are using. 2081789Sahrens */ 2082789Sahrens #define OP_SHARE 0x1 2083789Sahrens #define OP_MOUNT 0x2 2084789Sahrens 2085789Sahrens typedef struct share_mount_cbdata { 2086789Sahrens int cb_type; 2087789Sahrens int cb_explicit; 2088789Sahrens int cb_flags; 2089789Sahrens const char *cb_options; 2090789Sahrens } share_mount_cbdata_t; 2091789Sahrens 2092789Sahrens /* 2093789Sahrens * Share or mount the filesystem. 2094789Sahrens */ 2095789Sahrens static int 2096789Sahrens share_mount_callback(zfs_handle_t *zhp, void *data) 2097789Sahrens { 2098789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2099789Sahrens char shareopts[ZFS_MAXPROPLEN]; 2100789Sahrens share_mount_cbdata_t *cbp = data; 2101789Sahrens const char *cmdname = cbp->cb_type == OP_SHARE ? "share" : "mount"; 2102789Sahrens struct mnttab mnt; 2103789Sahrens uint64_t zoned; 2104789Sahrens 2105789Sahrens if (cbp->cb_options == NULL) 2106789Sahrens mnt.mnt_mntopts = ""; 2107789Sahrens else 2108789Sahrens mnt.mnt_mntopts = (char *)cbp->cb_options; 2109789Sahrens 2110789Sahrens /* 2111789Sahrens * Check to make sure we can mount/share this dataset. If we are in the 2112789Sahrens * global zone and the filesystem is exported to a local zone, or if we 2113789Sahrens * are in a local zone and the filesystem is not exported, then it is an 2114789Sahrens * error. 2115789Sahrens */ 2116789Sahrens zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2117789Sahrens 2118789Sahrens if (zoned && getzoneid() == GLOBAL_ZONEID) { 2119789Sahrens if (!cbp->cb_explicit) 2120789Sahrens return (0); 2121789Sahrens 2122789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': dataset is " 2123789Sahrens "exported to a local zone\n"), cmdname, zfs_get_name(zhp)); 2124789Sahrens return (1); 2125789Sahrens 2126789Sahrens } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 2127789Sahrens if (!cbp->cb_explicit) 2128789Sahrens return (0); 2129789Sahrens 2130789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': permission " 2131789Sahrens "denied\n"), cmdname, zfs_get_name(zhp)); 2132789Sahrens return (1); 2133789Sahrens } 2134789Sahrens 2135789Sahrens /* 2136789Sahrens * Inore any filesystems which don't apply to us. This includes those 2137789Sahrens * with a legacy mountpoint, or those with legacy share options. 2138789Sahrens */ 2139789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 21402082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); 2141789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 21422082Seschrock sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 2143789Sahrens 2144789Sahrens if (cbp->cb_type == OP_SHARE) { 2145789Sahrens if (strcmp(shareopts, "off") == 0) { 2146789Sahrens if (!cbp->cb_explicit) 2147789Sahrens return (0); 2148789Sahrens 2149789Sahrens (void) fprintf(stderr, gettext("cannot share '%s': " 2150789Sahrens "legacy share\n"), zfs_get_name(zhp)); 2151789Sahrens (void) fprintf(stderr, gettext("use share(1M) to " 2152789Sahrens "share this filesystem\n")); 2153789Sahrens return (1); 2154789Sahrens } 2155789Sahrens } 2156789Sahrens 2157789Sahrens /* 2158789Sahrens * We cannot share or mount legacy filesystems. If the shareopts is 2159789Sahrens * non-legacy but the mountpoint is legacy, we treat it as a legacy 2160789Sahrens * share. 2161789Sahrens */ 2162789Sahrens if (strcmp(mountpoint, "legacy") == 0) { 2163789Sahrens if (!cbp->cb_explicit) 2164789Sahrens return (0); 2165789Sahrens 2166789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': " 2167789Sahrens "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 2168789Sahrens (void) fprintf(stderr, gettext("use %s to " 2169789Sahrens "%s this filesystem\n"), cbp->cb_type == OP_SHARE ? 2170789Sahrens "share(1M)" : "mount(1M)", cmdname); 2171789Sahrens return (1); 2172789Sahrens } 2173789Sahrens 2174789Sahrens if (strcmp(mountpoint, "none") == 0) { 2175789Sahrens if (!cbp->cb_explicit) 2176789Sahrens return (0); 2177789Sahrens 2178789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': no " 2179789Sahrens "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 2180789Sahrens return (1); 2181789Sahrens } 2182789Sahrens 2183789Sahrens /* 2184789Sahrens * At this point, we have verified that the mountpoint and/or shareopts 2185789Sahrens * are appropriate for auto management. Determine if the filesystem is 2186789Sahrens * currently mounted or shared, and abort if this is an explicit 2187789Sahrens * request. 2188789Sahrens */ 2189789Sahrens switch (cbp->cb_type) { 2190789Sahrens case OP_SHARE: 2191789Sahrens if (zfs_is_shared(zhp, NULL)) { 2192789Sahrens if (cbp->cb_explicit) { 2193789Sahrens (void) fprintf(stderr, gettext("cannot share " 2194789Sahrens "'%s': filesystem already shared\n"), 2195789Sahrens zfs_get_name(zhp)); 2196789Sahrens return (1); 2197789Sahrens } else { 2198789Sahrens return (0); 2199789Sahrens } 2200789Sahrens } 2201789Sahrens break; 2202789Sahrens 2203789Sahrens case OP_MOUNT: 2204789Sahrens if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 2205789Sahrens zfs_is_mounted(zhp, NULL)) { 2206789Sahrens if (cbp->cb_explicit) { 2207789Sahrens (void) fprintf(stderr, gettext("cannot mount " 2208789Sahrens "'%s': filesystem already mounted\n"), 2209789Sahrens zfs_get_name(zhp)); 2210789Sahrens return (1); 2211789Sahrens } else { 2212789Sahrens return (0); 2213789Sahrens } 2214789Sahrens } 2215789Sahrens break; 2216789Sahrens } 2217789Sahrens 2218789Sahrens /* 2219789Sahrens * Mount and optionally share the filesystem. 2220789Sahrens */ 2221789Sahrens switch (cbp->cb_type) { 2222789Sahrens case OP_SHARE: 2223789Sahrens { 2224789Sahrens if (!zfs_is_mounted(zhp, NULL) && 2225789Sahrens zfs_mount(zhp, NULL, 0) != 0) 2226789Sahrens return (1); 2227789Sahrens 2228789Sahrens if (zfs_share(zhp) != 0) 2229789Sahrens return (1); 2230789Sahrens } 2231789Sahrens break; 2232789Sahrens 2233789Sahrens case OP_MOUNT: 2234789Sahrens if (zfs_mount(zhp, cbp->cb_options, cbp->cb_flags) != 0) 2235789Sahrens return (1); 2236789Sahrens break; 2237789Sahrens } 2238789Sahrens 2239789Sahrens return (0); 2240789Sahrens } 2241789Sahrens 2242789Sahrens static int 2243789Sahrens share_or_mount(int type, int argc, char **argv) 2244789Sahrens { 2245789Sahrens int do_all = 0; 22462372Slling int c, ret = 0; 2247789Sahrens share_mount_cbdata_t cb = { 0 }; 2248789Sahrens 2249789Sahrens cb.cb_type = type; 2250789Sahrens 2251789Sahrens /* check options */ 2252789Sahrens while ((c = getopt(argc, argv, type == OP_MOUNT ? ":ao:O" : "a")) 2253789Sahrens != -1) { 2254789Sahrens switch (c) { 2255789Sahrens case 'a': 2256789Sahrens do_all = 1; 2257789Sahrens break; 2258789Sahrens case 'o': 2259789Sahrens cb.cb_options = optarg; 2260789Sahrens break; 2261789Sahrens case 'O': 2262789Sahrens cb.cb_flags |= MS_OVERLAY; 2263789Sahrens break; 2264789Sahrens case ':': 2265789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2266789Sahrens "'%c' option\n"), optopt); 22672082Seschrock usage(B_FALSE); 2268789Sahrens break; 2269789Sahrens case '?': 2270789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2271789Sahrens optopt); 22722082Seschrock usage(B_FALSE); 2273789Sahrens } 2274789Sahrens } 2275789Sahrens 2276789Sahrens argc -= optind; 2277789Sahrens argv += optind; 2278789Sahrens 2279789Sahrens /* check number of arguments */ 2280789Sahrens if (do_all) { 22811356Seschrock zfs_handle_t **fslist = NULL; 22821356Seschrock size_t i, count = 0; 22831356Seschrock 2284789Sahrens if (argc != 0) { 2285789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 22862082Seschrock usage(B_FALSE); 2287789Sahrens } 2288789Sahrens 22891356Seschrock get_all_filesystems(&fslist, &count); 22901356Seschrock 22911356Seschrock if (count == 0) 22921356Seschrock return (0); 22931356Seschrock 22941356Seschrock qsort(fslist, count, sizeof (void *), mountpoint_compare); 22951356Seschrock 22961356Seschrock for (i = 0; i < count; i++) { 22972369Slling if (share_mount_callback(fslist[i], &cb) != 0) 22982369Slling ret = 1; 22991356Seschrock } 23001356Seschrock 23011356Seschrock for (i = 0; i < count; i++) 23021356Seschrock zfs_close(fslist[i]); 23031356Seschrock 23041356Seschrock free(fslist); 2305789Sahrens } else if (argc == 0) { 2306789Sahrens struct mnttab entry; 2307789Sahrens 2308789Sahrens if (type == OP_SHARE) { 2309789Sahrens (void) fprintf(stderr, gettext("missing filesystem " 2310789Sahrens "argument\n")); 23112082Seschrock usage(B_FALSE); 2312789Sahrens } 2313789Sahrens 2314789Sahrens /* 2315789Sahrens * When mount is given no arguments, go through /etc/mnttab and 2316789Sahrens * display any active ZFS mounts. We hide any snapshots, since 2317789Sahrens * they are controlled automatically. 2318789Sahrens */ 2319789Sahrens rewind(mnttab_file); 2320789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2321789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 2322789Sahrens strchr(entry.mnt_special, '@') != NULL) 2323789Sahrens continue; 2324789Sahrens 2325789Sahrens (void) printf("%-30s %s\n", entry.mnt_special, 2326789Sahrens entry.mnt_mountp); 2327789Sahrens } 2328789Sahrens 2329789Sahrens } else { 2330789Sahrens zfs_handle_t *zhp; 2331789Sahrens 2332789Sahrens if (argc > 1) { 2333789Sahrens (void) fprintf(stderr, 2334789Sahrens gettext("too many arguments\n")); 23352082Seschrock usage(B_FALSE); 2336789Sahrens } 2337789Sahrens 23382082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], 23392082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2340789Sahrens ret = 1; 2341789Sahrens else { 23422082Seschrock cb.cb_explicit = B_TRUE; 2343789Sahrens ret = share_mount_callback(zhp, &cb); 2344789Sahrens zfs_close(zhp); 2345789Sahrens } 2346789Sahrens } 2347789Sahrens 2348789Sahrens return (ret); 2349789Sahrens } 2350789Sahrens 2351789Sahrens /* 2352789Sahrens * zfs mount -a 2353789Sahrens * zfs mount filesystem 2354789Sahrens * 2355789Sahrens * Mount all filesystems, or mount the given filesystem. 2356789Sahrens */ 2357789Sahrens static int 2358789Sahrens zfs_do_mount(int argc, char **argv) 2359789Sahrens { 2360789Sahrens return (share_or_mount(OP_MOUNT, argc, argv)); 2361789Sahrens } 2362789Sahrens 2363789Sahrens /* 2364789Sahrens * zfs share -a 2365789Sahrens * zfs share filesystem 2366789Sahrens * 2367789Sahrens * Share all filesystems, or share the given filesystem. 2368789Sahrens */ 2369789Sahrens static int 2370789Sahrens zfs_do_share(int argc, char **argv) 2371789Sahrens { 2372789Sahrens return (share_or_mount(OP_SHARE, argc, argv)); 2373789Sahrens } 2374789Sahrens 2375789Sahrens typedef struct unshare_unmount_node { 2376789Sahrens zfs_handle_t *un_zhp; 2377789Sahrens char *un_mountp; 2378789Sahrens uu_avl_node_t un_avlnode; 2379789Sahrens } unshare_unmount_node_t; 2380789Sahrens 2381789Sahrens /* ARGSUSED */ 2382789Sahrens static int 2383789Sahrens unshare_unmount_compare(const void *larg, const void *rarg, void *unused) 2384789Sahrens { 2385789Sahrens const unshare_unmount_node_t *l = larg; 2386789Sahrens const unshare_unmount_node_t *r = rarg; 2387789Sahrens 2388789Sahrens return (strcmp(l->un_mountp, r->un_mountp)); 2389789Sahrens } 2390789Sahrens 2391789Sahrens /* 2392789Sahrens * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 2393789Sahrens * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 2394789Sahrens * and unmount it appropriately. 2395789Sahrens */ 2396789Sahrens static int 23972082Seschrock unshare_unmount_path(int type, char *path, int flags, boolean_t is_manual) 2398789Sahrens { 2399789Sahrens zfs_handle_t *zhp; 2400789Sahrens int ret; 2401789Sahrens struct stat64 statbuf; 2402789Sahrens struct extmnttab entry; 2403789Sahrens const char *cmdname = (type == OP_SHARE) ? "unshare" : "unmount"; 2404789Sahrens char property[ZFS_MAXPROPLEN]; 2405789Sahrens 2406789Sahrens /* 2407789Sahrens * Search for the path in /etc/mnttab. Rather than looking for the 2408789Sahrens * specific path, which can be fooled by non-standard paths (i.e. ".." 2409789Sahrens * or "//"), we stat() the path and search for the corresponding 2410789Sahrens * (major,minor) device pair. 2411789Sahrens */ 2412789Sahrens if (stat64(path, &statbuf) != 0) { 2413789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 2414789Sahrens cmdname, path, strerror(errno)); 2415789Sahrens return (1); 2416789Sahrens } 2417789Sahrens 2418789Sahrens /* 2419789Sahrens * Search for the given (major,minor) pair in the mount table. 2420789Sahrens */ 2421789Sahrens rewind(mnttab_file); 2422789Sahrens while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 2423789Sahrens if (entry.mnt_major == major(statbuf.st_dev) && 2424789Sahrens entry.mnt_minor == minor(statbuf.st_dev)) 2425789Sahrens break; 2426789Sahrens } 2427789Sahrens if (ret != 0) { 2428789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not " 2429789Sahrens "currently mounted\n"), cmdname, path); 2430789Sahrens return (1); 2431789Sahrens } 2432789Sahrens 2433789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 2434789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 2435789Sahrens "filesystem\n"), cmdname, path); 2436789Sahrens return (1); 2437789Sahrens } 2438789Sahrens 24392082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 24402082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2441789Sahrens return (1); 2442789Sahrens 2443789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2444789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 24452082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2446789Sahrens 2447789Sahrens if (type == OP_SHARE) { 2448789Sahrens if (strcmp(property, "off") == 0) { 2449789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2450789Sahrens "'%s': legacy share\n"), path); 2451789Sahrens (void) fprintf(stderr, gettext("use " 2452789Sahrens "unshare(1M) to unshare this filesystem\n")); 2453789Sahrens ret = 1; 2454789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2455789Sahrens (void) fprintf(stderr, gettext("cannot unshare '%s': " 2456789Sahrens "not currently shared\n"), path); 2457789Sahrens ret = 1; 2458789Sahrens } else { 2459789Sahrens ret = zfs_unshareall(zhp); 2460789Sahrens } 2461789Sahrens } else { 24621264Slling if (is_manual) { 24631264Slling ret = zfs_unmount(zhp, NULL, flags); 24641264Slling } else if (strcmp(property, "legacy") == 0) { 24651264Slling (void) fprintf(stderr, gettext("cannot unmount " 24661264Slling "'%s': legacy mountpoint\n"), 24671264Slling zfs_get_name(zhp)); 24681264Slling (void) fprintf(stderr, gettext("use umount(1M) " 24691264Slling "to unmount this filesystem\n")); 24701264Slling ret = 1; 2471789Sahrens } else { 2472789Sahrens ret = zfs_unmountall(zhp, flags); 2473789Sahrens } 2474789Sahrens } 2475789Sahrens 2476789Sahrens zfs_close(zhp); 2477789Sahrens 2478789Sahrens return (ret != 0); 2479789Sahrens } 2480789Sahrens 2481789Sahrens /* 2482789Sahrens * Generic callback for unsharing or unmounting a filesystem. 2483789Sahrens */ 2484789Sahrens static int 2485789Sahrens unshare_unmount(int type, int argc, char **argv) 2486789Sahrens { 2487789Sahrens int do_all = 0; 2488789Sahrens int flags = 0; 2489789Sahrens int ret = 0; 2490789Sahrens int c; 2491789Sahrens zfs_handle_t *zhp; 2492789Sahrens char property[ZFS_MAXPROPLEN]; 2493789Sahrens 2494789Sahrens /* check options */ 2495789Sahrens while ((c = getopt(argc, argv, type == OP_SHARE ? "a" : "af")) != -1) { 2496789Sahrens switch (c) { 2497789Sahrens case 'a': 2498789Sahrens do_all = 1; 2499789Sahrens break; 2500789Sahrens case 'f': 2501789Sahrens flags = MS_FORCE; 2502789Sahrens break; 2503789Sahrens case '?': 2504789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2505789Sahrens optopt); 25062082Seschrock usage(B_FALSE); 2507789Sahrens } 2508789Sahrens } 2509789Sahrens 2510789Sahrens argc -= optind; 2511789Sahrens argv += optind; 2512789Sahrens 2513789Sahrens /* ensure correct number of arguments */ 2514789Sahrens if (do_all) { 2515789Sahrens if (argc != 0) { 2516789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 25172082Seschrock usage(B_FALSE); 2518789Sahrens } 2519789Sahrens } else if (argc != 1) { 2520789Sahrens if (argc == 0) 2521789Sahrens (void) fprintf(stderr, 2522789Sahrens gettext("missing filesystem argument\n")); 2523789Sahrens else 2524789Sahrens (void) fprintf(stderr, 2525789Sahrens gettext("too many arguments\n")); 25262082Seschrock usage(B_FALSE); 2527789Sahrens } 2528789Sahrens 2529789Sahrens if (do_all) { 2530789Sahrens /* 2531789Sahrens * We could make use of zfs_for_each() to walk all datasets in 2532789Sahrens * the system, but this would be very inefficient, especially 2533789Sahrens * since we would have to linearly search /etc/mnttab for each 2534789Sahrens * one. Instead, do one pass through /etc/mnttab looking for 2535789Sahrens * zfs entries and call zfs_unmount() for each one. 2536789Sahrens * 2537789Sahrens * Things get a little tricky if the administrator has created 2538789Sahrens * mountpoints beneath other ZFS filesystems. In this case, we 2539789Sahrens * have to unmount the deepest filesystems first. To accomplish 2540789Sahrens * this, we place all the mountpoints in an AVL tree sorted by 2541789Sahrens * the special type (dataset name), and walk the result in 2542789Sahrens * reverse to make sure to get any snapshots first. 2543789Sahrens */ 2544789Sahrens struct mnttab entry; 2545789Sahrens uu_avl_pool_t *pool; 2546789Sahrens uu_avl_t *tree; 2547789Sahrens unshare_unmount_node_t *node; 2548789Sahrens uu_avl_index_t idx; 2549789Sahrens uu_avl_walk_t *walk; 2550789Sahrens 2551789Sahrens if ((pool = uu_avl_pool_create("unmount_pool", 2552789Sahrens sizeof (unshare_unmount_node_t), 2553789Sahrens offsetof(unshare_unmount_node_t, un_avlnode), 2554789Sahrens unshare_unmount_compare, 2555789Sahrens UU_DEFAULT)) == NULL) { 2556789Sahrens (void) fprintf(stderr, gettext("internal error: " 2557789Sahrens "out of memory\n")); 2558789Sahrens exit(1); 2559789Sahrens } 2560789Sahrens 2561789Sahrens if ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL) { 2562789Sahrens (void) fprintf(stderr, gettext("internal error: " 2563789Sahrens "out of memory\n")); 2564789Sahrens exit(1); 2565789Sahrens } 2566789Sahrens 2567789Sahrens rewind(mnttab_file); 2568789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2569789Sahrens 2570789Sahrens /* ignore non-ZFS entries */ 2571789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 2572789Sahrens continue; 2573789Sahrens 2574789Sahrens /* ignore snapshots */ 2575789Sahrens if (strchr(entry.mnt_special, '@') != NULL) 2576789Sahrens continue; 2577789Sahrens 25782082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 2579789Sahrens ZFS_TYPE_FILESYSTEM)) == NULL) { 2580789Sahrens ret = 1; 2581789Sahrens continue; 2582789Sahrens } 2583789Sahrens 2584789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2585789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 2586789Sahrens property, sizeof (property), NULL, NULL, 25872082Seschrock 0, B_FALSE) == 0); 2588789Sahrens 2589789Sahrens /* Ignore legacy mounts and shares */ 2590789Sahrens if ((type == OP_SHARE && 2591789Sahrens strcmp(property, "off") == 0) || 2592789Sahrens (type == OP_MOUNT && 2593789Sahrens strcmp(property, "legacy") == 0)) { 2594789Sahrens zfs_close(zhp); 2595789Sahrens continue; 2596789Sahrens } 2597789Sahrens 2598789Sahrens node = safe_malloc(sizeof (unshare_unmount_node_t)); 2599789Sahrens node->un_zhp = zhp; 2600789Sahrens 2601789Sahrens if ((node->un_mountp = strdup(entry.mnt_mountp)) == 2602789Sahrens NULL) { 2603789Sahrens (void) fprintf(stderr, gettext("internal error:" 2604789Sahrens " out of memory\n")); 2605789Sahrens exit(1); 2606789Sahrens } 2607789Sahrens 2608789Sahrens uu_avl_node_init(node, &node->un_avlnode, pool); 2609789Sahrens 2610789Sahrens if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 2611789Sahrens uu_avl_insert(tree, node, idx); 2612789Sahrens } else { 2613789Sahrens zfs_close(node->un_zhp); 2614789Sahrens free(node->un_mountp); 2615789Sahrens free(node); 2616789Sahrens } 2617789Sahrens } 2618789Sahrens 2619789Sahrens /* 2620789Sahrens * Walk the AVL tree in reverse, unmounting each filesystem and 2621789Sahrens * removing it from the AVL tree in the process. 2622789Sahrens */ 2623789Sahrens if ((walk = uu_avl_walk_start(tree, 2624789Sahrens UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) { 2625789Sahrens (void) fprintf(stderr, 2626789Sahrens gettext("internal error: out of memory")); 2627789Sahrens exit(1); 2628789Sahrens } 2629789Sahrens 2630789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 2631789Sahrens uu_avl_remove(tree, node); 2632789Sahrens 2633789Sahrens switch (type) { 2634789Sahrens case OP_SHARE: 2635789Sahrens if (zfs_unshare(node->un_zhp, 2636789Sahrens node->un_mountp) != 0) 2637789Sahrens ret = 1; 2638789Sahrens break; 2639789Sahrens 2640789Sahrens case OP_MOUNT: 2641789Sahrens if (zfs_unmount(node->un_zhp, 2642789Sahrens node->un_mountp, flags) != 0) 2643789Sahrens ret = 1; 2644789Sahrens break; 2645789Sahrens } 2646789Sahrens 2647789Sahrens zfs_close(node->un_zhp); 2648789Sahrens free(node->un_mountp); 2649789Sahrens free(node); 2650789Sahrens } 2651789Sahrens 2652789Sahrens uu_avl_walk_end(walk); 2653789Sahrens uu_avl_destroy(tree); 2654789Sahrens uu_avl_pool_destroy(pool); 2655789Sahrens } else { 2656789Sahrens /* 2657789Sahrens * We have an argument, but it may be a full path or a ZFS 2658789Sahrens * filesystem. Pass full paths off to unmount_path() (shared by 2659789Sahrens * manual_unmount), otherwise open the filesystem and pass to 2660789Sahrens * zfs_unmount(). 2661789Sahrens */ 2662789Sahrens if (argv[0][0] == '/') 2663789Sahrens return (unshare_unmount_path(type, argv[0], 26642082Seschrock flags, B_FALSE)); 26652082Seschrock 26662082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], 26672082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2668789Sahrens return (1); 2669789Sahrens 2670789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2671789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 26722082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2673789Sahrens 2674789Sahrens switch (type) { 2675789Sahrens case OP_SHARE: 2676789Sahrens if (strcmp(property, "off") == 0) { 2677789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2678789Sahrens "'%s': legacy share\n"), zfs_get_name(zhp)); 2679789Sahrens (void) fprintf(stderr, gettext("use unshare(1M)" 2680789Sahrens " to unshare this filesystem\n")); 2681789Sahrens ret = 1; 2682789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2683789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2684789Sahrens "'%s': not currently shared\n"), 2685789Sahrens zfs_get_name(zhp)); 2686789Sahrens ret = 1; 2687789Sahrens } else if (zfs_unshareall(zhp) != 0) { 2688789Sahrens ret = 1; 2689789Sahrens } 2690789Sahrens break; 2691789Sahrens 2692789Sahrens case OP_MOUNT: 2693789Sahrens if (strcmp(property, "legacy") == 0) { 2694789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2695789Sahrens "'%s': legacy mountpoint\n"), 2696789Sahrens zfs_get_name(zhp)); 2697789Sahrens (void) fprintf(stderr, gettext("use umount(1M) " 2698789Sahrens "to unmount this filesystem\n")); 2699789Sahrens ret = 1; 2700789Sahrens } else if (!zfs_is_mounted(zhp, NULL)) { 2701789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2702789Sahrens "'%s': not currently mounted\n"), 2703789Sahrens zfs_get_name(zhp)); 2704789Sahrens ret = 1; 2705789Sahrens } else if (zfs_unmountall(zhp, flags) != 0) { 2706789Sahrens ret = 1; 2707789Sahrens } 2708789Sahrens } 2709789Sahrens 2710789Sahrens zfs_close(zhp); 2711789Sahrens } 2712789Sahrens 2713789Sahrens return (ret); 2714789Sahrens } 2715789Sahrens 2716789Sahrens /* 2717789Sahrens * zfs unmount -a 2718789Sahrens * zfs unmount filesystem 2719789Sahrens * 2720789Sahrens * Unmount all filesystems, or a specific ZFS filesystem. 2721789Sahrens */ 2722789Sahrens static int 2723789Sahrens zfs_do_unmount(int argc, char **argv) 2724789Sahrens { 2725789Sahrens return (unshare_unmount(OP_MOUNT, argc, argv)); 2726789Sahrens } 2727789Sahrens 2728789Sahrens /* 2729789Sahrens * zfs unshare -a 2730789Sahrens * zfs unshare filesystem 2731789Sahrens * 2732789Sahrens * Unshare all filesystems, or a specific ZFS filesystem. 2733789Sahrens */ 2734789Sahrens static int 2735789Sahrens zfs_do_unshare(int argc, char **argv) 2736789Sahrens { 2737789Sahrens return (unshare_unmount(OP_SHARE, argc, argv)); 2738789Sahrens } 2739789Sahrens 2740789Sahrens /* 2741789Sahrens * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 2742789Sahrens * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 2743789Sahrens */ 2744789Sahrens static int 2745789Sahrens manual_mount(int argc, char **argv) 2746789Sahrens { 2747789Sahrens zfs_handle_t *zhp; 2748789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2749789Sahrens char mntopts[MNT_LINE_MAX] = { '\0' }; 2750789Sahrens int ret; 2751789Sahrens int c; 2752789Sahrens int flags = 0; 2753789Sahrens char *dataset, *path; 2754789Sahrens 2755789Sahrens /* check options */ 27561544Seschrock while ((c = getopt(argc, argv, ":mo:O")) != -1) { 2757789Sahrens switch (c) { 2758789Sahrens case 'o': 2759789Sahrens (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 2760789Sahrens break; 2761789Sahrens case 'O': 2762789Sahrens flags |= MS_OVERLAY; 2763789Sahrens break; 27641544Seschrock case 'm': 27651544Seschrock flags |= MS_NOMNTTAB; 27661544Seschrock break; 2767789Sahrens case ':': 2768789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2769789Sahrens "'%c' option\n"), optopt); 27702082Seschrock usage(B_FALSE); 2771789Sahrens break; 2772789Sahrens case '?': 2773789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2774789Sahrens optopt); 2775789Sahrens (void) fprintf(stderr, gettext("usage: mount [-o opts] " 2776789Sahrens "<path>\n")); 2777789Sahrens return (2); 2778789Sahrens } 2779789Sahrens } 2780789Sahrens 2781789Sahrens argc -= optind; 2782789Sahrens argv += optind; 2783789Sahrens 2784789Sahrens /* check that we only have two arguments */ 2785789Sahrens if (argc != 2) { 2786789Sahrens if (argc == 0) 2787789Sahrens (void) fprintf(stderr, gettext("missing dataset " 2788789Sahrens "argument\n")); 2789789Sahrens else if (argc == 1) 2790789Sahrens (void) fprintf(stderr, 2791789Sahrens gettext("missing mountpoint argument\n")); 2792789Sahrens else 2793789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2794789Sahrens (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 2795789Sahrens return (2); 2796789Sahrens } 2797789Sahrens 2798789Sahrens dataset = argv[0]; 2799789Sahrens path = argv[1]; 2800789Sahrens 2801789Sahrens /* try to open the dataset */ 28022082Seschrock if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 2803789Sahrens return (1); 2804789Sahrens 2805789Sahrens (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 28062082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE); 2807789Sahrens 2808789Sahrens /* check for legacy mountpoint and complain appropriately */ 2809789Sahrens ret = 0; 2810789Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 2811789Sahrens if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS, 2812789Sahrens NULL, 0, mntopts, sizeof (mntopts)) != 0) { 2813789Sahrens (void) fprintf(stderr, gettext("mount failed: %s\n"), 2814789Sahrens strerror(errno)); 2815789Sahrens ret = 1; 2816789Sahrens } 2817789Sahrens } else { 2818789Sahrens (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 2819789Sahrens "mounted using 'mount -F zfs'\n"), dataset); 2820789Sahrens (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 2821789Sahrens "instead.\n"), path); 2822789Sahrens (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' " 2823789Sahrens "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n")); 2824789Sahrens (void) fprintf(stderr, gettext("See zfs(1M) for more " 2825789Sahrens "information.\n")); 2826789Sahrens ret = 1; 2827789Sahrens } 2828789Sahrens 2829789Sahrens return (ret); 2830789Sahrens } 2831789Sahrens 2832789Sahrens /* 2833789Sahrens * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 2834789Sahrens * unmounts of non-legacy filesystems, as this is the dominant administrative 2835789Sahrens * interface. 2836789Sahrens */ 2837789Sahrens static int 2838789Sahrens manual_unmount(int argc, char **argv) 2839789Sahrens { 2840789Sahrens int flags = 0; 2841789Sahrens int c; 2842789Sahrens 2843789Sahrens /* check options */ 2844789Sahrens while ((c = getopt(argc, argv, "f")) != -1) { 2845789Sahrens switch (c) { 2846789Sahrens case 'f': 2847789Sahrens flags = MS_FORCE; 2848789Sahrens break; 2849789Sahrens case '?': 2850789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2851789Sahrens optopt); 2852789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] " 2853789Sahrens "<path>\n")); 2854789Sahrens return (2); 2855789Sahrens } 2856789Sahrens } 2857789Sahrens 2858789Sahrens argc -= optind; 2859789Sahrens argv += optind; 2860789Sahrens 2861789Sahrens /* check arguments */ 2862789Sahrens if (argc != 1) { 2863789Sahrens if (argc == 0) 2864789Sahrens (void) fprintf(stderr, gettext("missing path " 2865789Sahrens "argument\n")); 2866789Sahrens else 2867789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2868789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 2869789Sahrens return (2); 2870789Sahrens } 2871789Sahrens 28722082Seschrock return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE)); 2873789Sahrens } 2874789Sahrens 2875789Sahrens static int 2876789Sahrens volcheck(zpool_handle_t *zhp, void *data) 2877789Sahrens { 2878789Sahrens int isinit = (int)data; 2879789Sahrens 2880789Sahrens if (isinit) 2881789Sahrens return (zpool_create_zvol_links(zhp)); 2882789Sahrens else 2883789Sahrens return (zpool_remove_zvol_links(zhp)); 2884789Sahrens } 2885789Sahrens 2886789Sahrens /* 2887789Sahrens * Iterate over all pools in the system and either create or destroy /dev/zvol 2888789Sahrens * links, depending on the value of 'isinit'. 2889789Sahrens */ 2890789Sahrens static int 28912082Seschrock do_volcheck(boolean_t isinit) 2892789Sahrens { 28932082Seschrock return (zpool_iter(g_zfs, volcheck, (void *)isinit) ? 1 : 0); 2894789Sahrens } 2895789Sahrens 2896789Sahrens int 2897789Sahrens main(int argc, char **argv) 2898789Sahrens { 2899789Sahrens int ret; 2900789Sahrens int i; 2901789Sahrens char *progname; 2902789Sahrens char *cmdname; 2903789Sahrens 2904789Sahrens (void) setlocale(LC_ALL, ""); 2905789Sahrens (void) textdomain(TEXT_DOMAIN); 2906789Sahrens 2907789Sahrens opterr = 0; 2908789Sahrens 29092082Seschrock if ((g_zfs = libzfs_init()) == NULL) { 29102082Seschrock (void) fprintf(stderr, gettext("internal error: failed to " 29112082Seschrock "initialize ZFS library\n")); 29122082Seschrock return (1); 29132082Seschrock } 29142082Seschrock 29152082Seschrock libzfs_print_on_error(g_zfs, B_TRUE); 29162082Seschrock 2917789Sahrens if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 2918789Sahrens (void) fprintf(stderr, gettext("internal error: unable to " 2919789Sahrens "open %s\n"), MNTTAB); 2920789Sahrens return (1); 2921789Sahrens } 2922789Sahrens 2923789Sahrens /* 2924789Sahrens * This command also doubles as the /etc/fs mount and unmount program. 2925789Sahrens * Determine if we should take this behavior based on argv[0]. 2926789Sahrens */ 2927789Sahrens progname = basename(argv[0]); 2928789Sahrens if (strcmp(progname, "mount") == 0) { 2929789Sahrens ret = manual_mount(argc, argv); 2930789Sahrens } else if (strcmp(progname, "umount") == 0) { 2931789Sahrens ret = manual_unmount(argc, argv); 2932789Sahrens } else { 2933789Sahrens /* 2934789Sahrens * Make sure the user has specified some command. 2935789Sahrens */ 2936789Sahrens if (argc < 2) { 2937789Sahrens (void) fprintf(stderr, gettext("missing command\n")); 29382082Seschrock usage(B_FALSE); 2939789Sahrens } 2940789Sahrens 2941789Sahrens cmdname = argv[1]; 2942789Sahrens 2943789Sahrens /* 2944789Sahrens * The 'umount' command is an alias for 'unmount' 2945789Sahrens */ 2946789Sahrens if (strcmp(cmdname, "umount") == 0) 2947789Sahrens cmdname = "unmount"; 2948789Sahrens 2949789Sahrens /* 29501749Sahrens * The 'recv' command is an alias for 'receive' 29511749Sahrens */ 29521749Sahrens if (strcmp(cmdname, "recv") == 0) 29531749Sahrens cmdname = "receive"; 29541749Sahrens 29551749Sahrens /* 2956789Sahrens * Special case '-?' 2957789Sahrens */ 2958789Sahrens if (strcmp(cmdname, "-?") == 0) 29592082Seschrock usage(B_TRUE); 2960789Sahrens 2961789Sahrens /* 2962789Sahrens * 'volinit' and 'volfini' do not appear in the usage message, 2963789Sahrens * so we have to special case them here. 2964789Sahrens */ 2965789Sahrens if (strcmp(cmdname, "volinit") == 0) 29662082Seschrock return (do_volcheck(B_TRUE)); 2967789Sahrens else if (strcmp(cmdname, "volfini") == 0) 29682082Seschrock return (do_volcheck(B_FALSE)); 2969789Sahrens 2970789Sahrens /* 2971789Sahrens * Run the appropriate command. 2972789Sahrens */ 2973789Sahrens for (i = 0; i < NCOMMAND; i++) { 2974789Sahrens if (command_table[i].name == NULL) 2975789Sahrens continue; 2976789Sahrens 2977789Sahrens if (strcmp(cmdname, command_table[i].name) == 0) { 2978789Sahrens current_command = &command_table[i]; 2979789Sahrens ret = command_table[i].func(argc - 1, argv + 1); 2980789Sahrens break; 2981789Sahrens } 2982789Sahrens } 2983789Sahrens 2984789Sahrens if (i == NCOMMAND) { 2985789Sahrens (void) fprintf(stderr, gettext("unrecognized " 2986789Sahrens "command '%s'\n"), cmdname); 29872082Seschrock usage(B_FALSE); 2988789Sahrens } 2989789Sahrens } 2990789Sahrens 2991789Sahrens (void) fclose(mnttab_file); 2992789Sahrens 29932082Seschrock libzfs_fini(g_zfs); 29942082Seschrock 2995789Sahrens /* 2996789Sahrens * The 'ZFS_ABORT' environment variable causes us to dump core on exit 2997789Sahrens * for the purposes of running ::findleaks. 2998789Sahrens */ 2999789Sahrens if (getenv("ZFS_ABORT") != NULL) { 3000789Sahrens (void) printf("dumping core by request\n"); 3001789Sahrens abort(); 3002789Sahrens } 3003789Sahrens 3004789Sahrens return (ret); 3005789Sahrens } 3006