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" 180*2379Ssjelinek "\t [-s property [-s property]...]" 181*2379Ssjelinek " [-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 */ 6122199Sahrens (void) zfs_iter_dependents(szhp, destroy_callback, cbp); 6132199Sahrens zfs_close(szhp); 6142199Sahrens } 6152199Sahrens 6162199Sahrens return (zfs_iter_filesystems(zhp, destroy_snap_clones, arg)); 6172199Sahrens } 618789Sahrens 619789Sahrens static int 620789Sahrens zfs_do_destroy(int argc, char **argv) 621789Sahrens { 622789Sahrens destroy_cbdata_t cb = { 0 }; 623789Sahrens int c; 624789Sahrens zfs_handle_t *zhp; 6252199Sahrens char *cp; 626789Sahrens 627789Sahrens /* check options */ 628789Sahrens while ((c = getopt(argc, argv, "frR")) != -1) { 629789Sahrens switch (c) { 630789Sahrens case 'f': 631789Sahrens cb.cb_force = 1; 632789Sahrens break; 633789Sahrens case 'r': 634789Sahrens cb.cb_recurse = 1; 635789Sahrens break; 636789Sahrens case 'R': 637789Sahrens cb.cb_recurse = 1; 638789Sahrens cb.cb_doclones = 1; 639789Sahrens break; 640789Sahrens case '?': 641789Sahrens default: 642789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 643789Sahrens optopt); 6442082Seschrock usage(B_FALSE); 645789Sahrens } 646789Sahrens } 647789Sahrens 648789Sahrens argc -= optind; 649789Sahrens argv += optind; 650789Sahrens 651789Sahrens /* check number of arguments */ 652789Sahrens if (argc == 0) { 653789Sahrens (void) fprintf(stderr, gettext("missing path argument\n")); 6542082Seschrock usage(B_FALSE); 655789Sahrens } 656789Sahrens if (argc > 1) { 657789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 6582082Seschrock usage(B_FALSE); 659789Sahrens } 660789Sahrens 6612199Sahrens /* 6622199Sahrens * If we are doing recursive destroy of a snapshot, then the 6632199Sahrens * named snapshot may not exist. Go straight to libzfs. 6642199Sahrens */ 6652199Sahrens if (cb.cb_recurse && (cp = strchr(argv[0], '@'))) { 6662199Sahrens int ret; 6672199Sahrens 6682199Sahrens *cp = '\0'; 6692199Sahrens if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 6702199Sahrens return (1); 6712199Sahrens *cp = '@'; 6722199Sahrens cp++; 6732199Sahrens 6742199Sahrens if (cb.cb_doclones) { 6752199Sahrens cb.cb_snapname = cp; 6762199Sahrens (void) destroy_snap_clones(zhp, &cb); 6772199Sahrens } 6782199Sahrens 6792199Sahrens ret = zfs_destroy_snaps(zhp, cp); 6802199Sahrens zfs_close(zhp); 6812199Sahrens if (ret) { 6822199Sahrens (void) fprintf(stderr, 6832199Sahrens gettext("no snapshots destroyed\n")); 6842199Sahrens } 6852199Sahrens return (ret != 0); 6862199Sahrens } 6872199Sahrens 6882199Sahrens 689789Sahrens /* Open the given dataset */ 6902082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 691789Sahrens return (1); 692789Sahrens 693789Sahrens cb.cb_target = zhp; 694789Sahrens 695789Sahrens /* 696789Sahrens * Perform an explicit check for pools before going any further. 697789Sahrens */ 698789Sahrens if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 699789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 700789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 701789Sahrens "operation does not apply to pools\n"), 702789Sahrens zfs_get_name(zhp)); 703789Sahrens (void) fprintf(stderr, gettext("use 'zfs destroy -r " 704789Sahrens "%s' to destroy all datasets in the pool\n"), 705789Sahrens zfs_get_name(zhp)); 706789Sahrens (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 707789Sahrens "to destroy the pool itself\n"), zfs_get_name(zhp)); 708789Sahrens zfs_close(zhp); 709789Sahrens return (1); 710789Sahrens } 711789Sahrens 712789Sahrens /* 713789Sahrens * Check for any dependents and/or clones. 714789Sahrens */ 7152082Seschrock cb.cb_first = B_TRUE; 716789Sahrens if (!cb.cb_doclones) 717789Sahrens (void) zfs_iter_dependents(zhp, destroy_check_dependent, &cb); 718789Sahrens 719789Sahrens if (cb.cb_error) { 720789Sahrens zfs_close(zhp); 721789Sahrens return (1); 722789Sahrens } 723789Sahrens 724789Sahrens /* 725789Sahrens * Do the real thing. 726789Sahrens */ 727789Sahrens if (zfs_iter_dependents(zhp, destroy_callback, &cb) == 0 && 728789Sahrens destroy_callback(zhp, &cb) == 0) 729789Sahrens return (0); 730789Sahrens 731789Sahrens return (1); 732789Sahrens } 733789Sahrens 734789Sahrens /* 735866Seschrock * zfs get [-rHp] [-o field[,field]...] [-s source[,source]...] 736866Seschrock * < all | property[,property]... > < fs | snap | vol > ... 737789Sahrens * 738789Sahrens * -r recurse over any child datasets 739789Sahrens * -H scripted mode. Headers are stripped, and fields are separated 740789Sahrens * by tabs instead of spaces. 741789Sahrens * -o Set of fields to display. One of "name,property,value,source". 742789Sahrens * Default is all four. 743789Sahrens * -s Set of sources to allow. One of 744789Sahrens * "local,default,inherited,temporary,none". Default is all 745789Sahrens * five. 746789Sahrens * -p Display values in parsable (literal) format. 747789Sahrens * 748789Sahrens * Prints properties for the given datasets. The user can control which 749789Sahrens * columns to display as well as which property types to allow. 750789Sahrens */ 751789Sahrens typedef struct get_cbdata { 752789Sahrens int cb_sources; 753789Sahrens int cb_columns[4]; 7542082Seschrock int cb_nprop; 7552082Seschrock boolean_t cb_scripted; 7562082Seschrock boolean_t cb_literal; 7572082Seschrock boolean_t cb_isall; 758789Sahrens zfs_prop_t cb_prop[ZFS_NPROP_ALL]; 759789Sahrens } get_cbdata_t; 760789Sahrens 761789Sahrens #define GET_COL_NAME 1 762789Sahrens #define GET_COL_PROPERTY 2 763789Sahrens #define GET_COL_VALUE 3 764789Sahrens #define GET_COL_SOURCE 4 765789Sahrens 766789Sahrens /* 767789Sahrens * Display a single line of output, according to the settings in the callback 768789Sahrens * structure. 769789Sahrens */ 770789Sahrens static void 771789Sahrens print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, zfs_prop_t prop, 772789Sahrens const char *value, zfs_source_t sourcetype, const char *source) 773789Sahrens { 774789Sahrens int i; 775789Sahrens int width; 776789Sahrens const char *str; 777789Sahrens char buf[128]; 778789Sahrens 779789Sahrens /* 780789Sahrens * Ignore those source types that the user has chosen to ignore. 781789Sahrens */ 782789Sahrens if ((sourcetype & cbp->cb_sources) == 0) 783789Sahrens return; 784789Sahrens 785789Sahrens for (i = 0; i < 4; i++) { 786789Sahrens switch (cbp->cb_columns[i]) { 787789Sahrens case GET_COL_NAME: 788789Sahrens width = 15; 789789Sahrens str = zfs_get_name(zhp); 790789Sahrens break; 791789Sahrens 792789Sahrens case GET_COL_PROPERTY: 793789Sahrens width = 13; 794789Sahrens str = zfs_prop_to_name(prop); 795789Sahrens break; 796789Sahrens 797789Sahrens case GET_COL_VALUE: 798789Sahrens width = 25; 799789Sahrens str = value; 800789Sahrens break; 801789Sahrens 802789Sahrens case GET_COL_SOURCE: 803789Sahrens width = 15; 804789Sahrens switch (sourcetype) { 805789Sahrens case ZFS_SRC_NONE: 806789Sahrens str = "-"; 807789Sahrens break; 808789Sahrens 809789Sahrens case ZFS_SRC_DEFAULT: 810789Sahrens str = "default"; 811789Sahrens break; 812789Sahrens 813789Sahrens case ZFS_SRC_LOCAL: 814789Sahrens str = "local"; 815789Sahrens break; 816789Sahrens 817789Sahrens case ZFS_SRC_TEMPORARY: 818789Sahrens str = "temporary"; 819789Sahrens break; 820789Sahrens 821789Sahrens case ZFS_SRC_INHERITED: 822789Sahrens (void) snprintf(buf, sizeof (buf), 823789Sahrens "inherited from %s", source); 824789Sahrens str = buf; 825789Sahrens break; 826789Sahrens } 827789Sahrens break; 828789Sahrens 829789Sahrens default: 830789Sahrens continue; 831789Sahrens } 832789Sahrens 833789Sahrens if (cbp->cb_columns[i + 1] == 0) 834789Sahrens (void) printf("%s", str); 835789Sahrens else if (cbp->cb_scripted) 836789Sahrens (void) printf("%s\t", str); 837789Sahrens else 838789Sahrens (void) printf("%-*s ", width, str); 839789Sahrens 840789Sahrens } 841789Sahrens 842789Sahrens (void) printf("\n"); 843789Sahrens } 844789Sahrens 845789Sahrens /* 846789Sahrens * Invoked to display the properties for a single dataset. 847789Sahrens */ 848789Sahrens static int 849789Sahrens get_callback(zfs_handle_t *zhp, void *data) 850789Sahrens { 851789Sahrens char buf[ZFS_MAXPROPLEN]; 852789Sahrens zfs_source_t sourcetype; 853789Sahrens char source[ZFS_MAXNAMELEN]; 854789Sahrens get_cbdata_t *cbp = data; 855789Sahrens int i; 856789Sahrens 857866Seschrock for (i = 0; i < cbp->cb_nprop; i++) { 858866Seschrock if (zfs_prop_get(zhp, cbp->cb_prop[i], buf, 859866Seschrock sizeof (buf), &sourcetype, source, sizeof (source), 860866Seschrock cbp->cb_literal) != 0) { 861866Seschrock if (cbp->cb_isall) 862866Seschrock continue; 863866Seschrock (void) strlcpy(buf, "-", sizeof (buf)); 864866Seschrock sourcetype = ZFS_SRC_NONE; 865866Seschrock } 866789Sahrens 867866Seschrock print_one_property(zhp, cbp, cbp->cb_prop[i], 868866Seschrock buf, sourcetype, source); 869789Sahrens } 870789Sahrens 871789Sahrens return (0); 872789Sahrens } 873789Sahrens 874789Sahrens static int 875789Sahrens zfs_do_get(int argc, char **argv) 876789Sahrens { 877789Sahrens get_cbdata_t cb = { 0 }; 8782082Seschrock boolean_t recurse = B_FALSE; 879789Sahrens int c; 880866Seschrock char *value, *fields, *badopt; 881789Sahrens int i; 882866Seschrock int ret; 883789Sahrens 884789Sahrens /* 885789Sahrens * Set up default columns and sources. 886789Sahrens */ 887789Sahrens cb.cb_sources = ZFS_SRC_ALL; 888789Sahrens cb.cb_columns[0] = GET_COL_NAME; 889789Sahrens cb.cb_columns[1] = GET_COL_PROPERTY; 890789Sahrens cb.cb_columns[2] = GET_COL_VALUE; 891789Sahrens cb.cb_columns[3] = GET_COL_SOURCE; 892789Sahrens 893789Sahrens /* check options */ 894789Sahrens while ((c = getopt(argc, argv, ":o:s:rHp")) != -1) { 895789Sahrens switch (c) { 896789Sahrens case 'p': 8972082Seschrock cb.cb_literal = B_TRUE; 898789Sahrens break; 899789Sahrens case 'r': 9002082Seschrock recurse = B_TRUE; 901789Sahrens break; 902789Sahrens case 'H': 9032082Seschrock cb.cb_scripted = B_TRUE; 904789Sahrens break; 905789Sahrens case ':': 906789Sahrens (void) fprintf(stderr, gettext("missing argument for " 907789Sahrens "'%c' option\n"), optopt); 9082082Seschrock usage(B_FALSE); 909789Sahrens break; 910789Sahrens case 'o': 911789Sahrens /* 912789Sahrens * Process the set of columns to display. We zero out 913789Sahrens * the structure to give us a blank slate. 914789Sahrens */ 915789Sahrens bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 916789Sahrens i = 0; 917789Sahrens while (*optarg != '\0') { 918789Sahrens static char *col_subopts[] = 919789Sahrens { "name", "property", "value", "source", 920789Sahrens NULL }; 921789Sahrens 922789Sahrens if (i == 4) { 923789Sahrens (void) fprintf(stderr, gettext("too " 924789Sahrens "many fields given to -o " 925789Sahrens "option\n")); 9262082Seschrock usage(B_FALSE); 927789Sahrens } 928789Sahrens 929789Sahrens switch (getsubopt(&optarg, col_subopts, 930789Sahrens &value)) { 931789Sahrens case 0: 932789Sahrens cb.cb_columns[i++] = GET_COL_NAME; 933789Sahrens break; 934789Sahrens case 1: 935789Sahrens cb.cb_columns[i++] = GET_COL_PROPERTY; 936789Sahrens break; 937789Sahrens case 2: 938789Sahrens cb.cb_columns[i++] = GET_COL_VALUE; 939789Sahrens break; 940789Sahrens case 3: 941789Sahrens cb.cb_columns[i++] = GET_COL_SOURCE; 942789Sahrens break; 943789Sahrens default: 944789Sahrens (void) fprintf(stderr, 945789Sahrens gettext("invalid column name " 946789Sahrens "'%s'\n"), value); 9472082Seschrock usage(B_FALSE); 948789Sahrens } 949789Sahrens } 950789Sahrens break; 951789Sahrens 952789Sahrens case 's': 953789Sahrens cb.cb_sources = 0; 954789Sahrens while (*optarg != '\0') { 955789Sahrens static char *source_subopts[] = { 956789Sahrens "local", "default", "inherited", 957789Sahrens "temporary", "none", NULL }; 958789Sahrens 959789Sahrens switch (getsubopt(&optarg, source_subopts, 960789Sahrens &value)) { 961789Sahrens case 0: 962789Sahrens cb.cb_sources |= ZFS_SRC_LOCAL; 963789Sahrens break; 964789Sahrens case 1: 965789Sahrens cb.cb_sources |= ZFS_SRC_DEFAULT; 966789Sahrens break; 967789Sahrens case 2: 968789Sahrens cb.cb_sources |= ZFS_SRC_INHERITED; 969789Sahrens break; 970789Sahrens case 3: 971789Sahrens cb.cb_sources |= ZFS_SRC_TEMPORARY; 972789Sahrens break; 973789Sahrens case 4: 974789Sahrens cb.cb_sources |= ZFS_SRC_NONE; 975789Sahrens break; 976789Sahrens default: 977789Sahrens (void) fprintf(stderr, 978789Sahrens gettext("invalid source " 979789Sahrens "'%s'\n"), value); 9802082Seschrock usage(B_FALSE); 981789Sahrens } 982789Sahrens } 983789Sahrens break; 984789Sahrens 985789Sahrens case '?': 986789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 987789Sahrens optopt); 9882082Seschrock usage(B_FALSE); 989789Sahrens } 990789Sahrens } 991789Sahrens 992789Sahrens argc -= optind; 993789Sahrens argv += optind; 994789Sahrens 995789Sahrens if (argc < 1) { 996789Sahrens (void) fprintf(stderr, gettext("missing property " 997789Sahrens "argument\n")); 9982082Seschrock usage(B_FALSE); 999789Sahrens } 1000789Sahrens 1001789Sahrens fields = argv[0]; 1002789Sahrens 1003789Sahrens /* 1004866Seschrock * If the user specifies 'all', the behavior of 'zfs get' is slightly 1005866Seschrock * different, because we don't show properties which don't apply to the 1006866Seschrock * given dataset. 1007789Sahrens */ 1008866Seschrock if (strcmp(fields, "all") == 0) 10092082Seschrock cb.cb_isall = B_TRUE; 1010789Sahrens 1011866Seschrock if ((ret = zfs_get_proplist(fields, cb.cb_prop, ZFS_NPROP_ALL, 1012866Seschrock &cb.cb_nprop, &badopt)) != 0) { 1013866Seschrock if (ret == EINVAL) 1014866Seschrock (void) fprintf(stderr, gettext("invalid property " 1015866Seschrock "'%s'\n"), badopt); 1016866Seschrock else 1017866Seschrock (void) fprintf(stderr, gettext("too many properties " 1018866Seschrock "specified\n")); 10192082Seschrock usage(B_FALSE); 1020789Sahrens } 1021789Sahrens 1022789Sahrens argc--; 1023789Sahrens argv++; 1024789Sahrens 1025789Sahrens /* check for at least one dataset name */ 1026789Sahrens if (argc < 1) { 1027789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 10282082Seschrock usage(B_FALSE); 1029789Sahrens } 1030789Sahrens 1031789Sahrens /* 1032789Sahrens * Print out any headers 1033789Sahrens */ 1034789Sahrens if (!cb.cb_scripted) { 1035789Sahrens int i; 1036789Sahrens for (i = 0; i < 4; i++) { 1037789Sahrens switch (cb.cb_columns[i]) { 1038789Sahrens case GET_COL_NAME: 1039789Sahrens (void) printf("%-15s ", "NAME"); 1040789Sahrens break; 1041789Sahrens case GET_COL_PROPERTY: 1042789Sahrens (void) printf("%-13s ", "PROPERTY"); 1043789Sahrens break; 1044789Sahrens case GET_COL_VALUE: 1045789Sahrens (void) printf("%-25s ", "VALUE"); 1046789Sahrens break; 1047789Sahrens case GET_COL_SOURCE: 1048789Sahrens (void) printf("%s", "SOURCE"); 1049789Sahrens break; 1050789Sahrens } 1051789Sahrens } 1052789Sahrens (void) printf("\n"); 1053789Sahrens } 1054789Sahrens 1055789Sahrens /* run for each object */ 1056*2379Ssjelinek return (zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, NULL, 1057789Sahrens get_callback, &cb)); 1058*2379Ssjelinek 1059789Sahrens } 1060789Sahrens 1061789Sahrens /* 1062789Sahrens * inherit [-r] <property> <fs|vol> ... 1063789Sahrens * 1064789Sahrens * -r Recurse over all children 1065789Sahrens * 1066789Sahrens * For each dataset specified on the command line, inherit the given property 1067789Sahrens * from its parent. Inheriting a property at the pool level will cause it to 1068789Sahrens * use the default value. The '-r' flag will recurse over all children, and is 1069789Sahrens * useful for setting a property on a hierarchy-wide basis, regardless of any 1070789Sahrens * local modifications for each dataset. 1071789Sahrens */ 1072789Sahrens static int 1073789Sahrens inherit_callback(zfs_handle_t *zhp, void *data) 1074789Sahrens { 1075789Sahrens zfs_prop_t prop = (zfs_prop_t)data; 1076789Sahrens 1077789Sahrens return (zfs_prop_inherit(zhp, prop) != 0); 1078789Sahrens } 1079789Sahrens 1080789Sahrens static int 1081789Sahrens zfs_do_inherit(int argc, char **argv) 1082789Sahrens { 10832082Seschrock boolean_t recurse = B_FALSE; 1084789Sahrens int c; 1085789Sahrens zfs_prop_t prop; 1086789Sahrens char *propname; 1087789Sahrens 1088789Sahrens /* check options */ 1089789Sahrens while ((c = getopt(argc, argv, "r")) != -1) { 1090789Sahrens switch (c) { 1091789Sahrens case 'r': 10922082Seschrock recurse = B_TRUE; 1093789Sahrens break; 1094789Sahrens case '?': 1095789Sahrens default: 1096789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1097789Sahrens optopt); 10982082Seschrock usage(B_FALSE); 1099789Sahrens } 1100789Sahrens } 1101789Sahrens 1102789Sahrens argc -= optind; 1103789Sahrens argv += optind; 1104789Sahrens 1105789Sahrens /* check number of arguments */ 1106789Sahrens if (argc < 1) { 1107789Sahrens (void) fprintf(stderr, gettext("missing property argument\n")); 11082082Seschrock usage(B_FALSE); 1109789Sahrens } 1110789Sahrens if (argc < 2) { 1111789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 11122082Seschrock usage(B_FALSE); 1113789Sahrens } 1114789Sahrens 1115789Sahrens propname = argv[0]; 1116789Sahrens 1117789Sahrens /* 1118789Sahrens * Get and validate the property before iterating over the datasets. We 1119789Sahrens * do this now so as to avoid printing out an error message for each and 1120789Sahrens * every dataset. 1121789Sahrens */ 1122789Sahrens if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) { 1123789Sahrens (void) fprintf(stderr, gettext("invalid property '%s'\n"), 1124789Sahrens propname); 11252082Seschrock usage(B_FALSE); 1126789Sahrens } 1127789Sahrens if (zfs_prop_readonly(prop)) { 1128789Sahrens (void) fprintf(stderr, gettext("%s property is read-only\n"), 1129789Sahrens propname); 1130789Sahrens return (1); 1131789Sahrens } 1132789Sahrens if (!zfs_prop_inheritable(prop)) { 1133789Sahrens (void) fprintf(stderr, gettext("%s property cannot be " 1134789Sahrens "inherited\n"), propname); 1135789Sahrens (void) fprintf(stderr, gettext("use 'zfs set %s=none' to " 1136789Sahrens "clear\n"), propname); 1137789Sahrens return (1); 1138789Sahrens } 1139789Sahrens 1140789Sahrens return (zfs_for_each(argc - 1, argv + 1, recurse, 1141*2379Ssjelinek ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, 1142789Sahrens inherit_callback, (void *)prop)); 1143789Sahrens } 1144789Sahrens 1145789Sahrens /* 1146*2379Ssjelinek * list [-rH] [-o property[,property]...] [-t type[,type]...] 1147*2379Ssjelinek * [-s property [-s property]...] [-S property [-S property]...] 1148*2379Ssjelinek * <dataset> ... 1149789Sahrens * 1150789Sahrens * -r Recurse over all children 1151789Sahrens * -H Scripted mode; elide headers and separate colums by tabs 1152789Sahrens * -o Control which fields to display. 1153866Seschrock * -t Control which object types to display. 1154*2379Ssjelinek * -s Specify sort columns, descending order. 1155*2379Ssjelinek * -S Specify sort columns, ascending order. 1156789Sahrens * 1157789Sahrens * When given no arguments, lists all filesystems in the system. 1158789Sahrens * Otherwise, list the specified datasets, optionally recursing down them if 1159789Sahrens * '-r' is specified. 1160789Sahrens */ 1161789Sahrens typedef struct list_cbdata { 11622082Seschrock boolean_t cb_first; 11632082Seschrock boolean_t cb_scripted; 1164866Seschrock zfs_prop_t cb_fields[ZFS_NPROP_ALL]; 1165866Seschrock int cb_fieldcount; 1166789Sahrens } list_cbdata_t; 1167789Sahrens 1168789Sahrens /* 1169789Sahrens * Given a list of columns to display, output appropriate headers for each one. 1170789Sahrens */ 1171789Sahrens static void 1172866Seschrock print_header(zfs_prop_t *fields, size_t count) 1173789Sahrens { 1174789Sahrens int i; 1175789Sahrens 1176789Sahrens for (i = 0; i < count; i++) { 1177789Sahrens if (i != 0) 1178789Sahrens (void) printf(" "); 1179789Sahrens if (i == count - 1) 1180789Sahrens (void) printf("%s", zfs_prop_column_name(fields[i])); 1181789Sahrens else /* LINTED - format specifier */ 1182789Sahrens (void) printf(zfs_prop_column_format(fields[i]), 1183789Sahrens zfs_prop_column_name(fields[i])); 1184789Sahrens } 1185789Sahrens 1186789Sahrens (void) printf("\n"); 1187789Sahrens } 1188789Sahrens 1189789Sahrens /* 1190789Sahrens * Given a dataset and a list of fields, print out all the properties according 1191789Sahrens * to the described layout. 1192789Sahrens */ 1193789Sahrens static void 1194866Seschrock print_dataset(zfs_handle_t *zhp, zfs_prop_t *fields, size_t count, int scripted) 1195789Sahrens { 1196789Sahrens int i; 1197789Sahrens char property[ZFS_MAXPROPLEN]; 1198789Sahrens 1199789Sahrens for (i = 0; i < count; i++) { 1200789Sahrens if (i != 0) { 1201789Sahrens if (scripted) 1202789Sahrens (void) printf("\t"); 1203789Sahrens else 1204789Sahrens (void) printf(" "); 1205789Sahrens } 1206789Sahrens 1207789Sahrens if (zfs_prop_get(zhp, fields[i], property, 12082082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) != 0) 1209789Sahrens (void) strlcpy(property, "-", sizeof (property)); 1210789Sahrens 1211866Seschrock /* 1212866Seschrock * If this is being called in scripted mode, or if this is the 1213866Seschrock * last column and it is left-justified, don't include a width 1214866Seschrock * format specifier. 1215866Seschrock */ 1216866Seschrock if (scripted || (i == count - 1 && 1217866Seschrock strchr(zfs_prop_column_format(fields[i]), '-') != NULL)) 1218789Sahrens (void) printf("%s", property); 1219789Sahrens else /* LINTED - format specifier */ 1220789Sahrens (void) printf(zfs_prop_column_format(fields[i]), 1221789Sahrens property); 1222789Sahrens } 1223789Sahrens 1224789Sahrens (void) printf("\n"); 1225789Sahrens } 1226789Sahrens 1227789Sahrens /* 1228789Sahrens * Generic callback function to list a dataset or snapshot. 1229789Sahrens */ 1230789Sahrens static int 1231789Sahrens list_callback(zfs_handle_t *zhp, void *data) 1232789Sahrens { 1233789Sahrens list_cbdata_t *cbp = data; 1234789Sahrens 1235789Sahrens if (cbp->cb_first) { 1236789Sahrens if (!cbp->cb_scripted) 1237789Sahrens print_header(cbp->cb_fields, cbp->cb_fieldcount); 12382082Seschrock cbp->cb_first = B_FALSE; 1239789Sahrens } 1240789Sahrens 1241789Sahrens print_dataset(zhp, cbp->cb_fields, cbp->cb_fieldcount, 1242789Sahrens cbp->cb_scripted); 1243789Sahrens 1244789Sahrens return (0); 1245789Sahrens } 1246789Sahrens 1247789Sahrens static int 1248789Sahrens zfs_do_list(int argc, char **argv) 1249789Sahrens { 1250789Sahrens int c; 12512082Seschrock boolean_t recurse = B_FALSE; 12522082Seschrock boolean_t scripted = B_FALSE; 1253789Sahrens static char default_fields[] = 1254789Sahrens "name,used,available,referenced,mountpoint"; 1255789Sahrens int types = ZFS_TYPE_ANY; 1256789Sahrens char *fields = NULL; 1257789Sahrens char *basic_fields = default_fields; 1258789Sahrens list_cbdata_t cb = { 0 }; 1259789Sahrens char *value; 1260789Sahrens int ret; 1261789Sahrens char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL }; 1262866Seschrock char *badopt; 1263866Seschrock int alloffset; 1264*2379Ssjelinek zfs_sort_column_t *sortcol = NULL; 1265789Sahrens 1266789Sahrens /* check options */ 1267*2379Ssjelinek while ((c = getopt(argc, argv, ":o:rt:Hs:S:")) != -1) { 1268*2379Ssjelinek zfs_prop_t prop; 1269*2379Ssjelinek 1270789Sahrens switch (c) { 1271789Sahrens case 'o': 1272789Sahrens fields = optarg; 1273789Sahrens break; 1274789Sahrens case 'r': 12752082Seschrock recurse = B_TRUE; 1276789Sahrens break; 1277789Sahrens case 'H': 12782082Seschrock scripted = B_TRUE; 1279789Sahrens break; 1280*2379Ssjelinek case 's': 1281*2379Ssjelinek if ((prop = zfs_name_to_prop(optarg)) == 1282*2379Ssjelinek ZFS_PROP_INVAL) { 1283*2379Ssjelinek (void) fprintf(stderr, 1284*2379Ssjelinek gettext("invalid property '%s'\n"), optarg); 1285*2379Ssjelinek usage(B_FALSE); 1286*2379Ssjelinek } 1287*2379Ssjelinek zfs_add_sort_column(&sortcol, prop, B_FALSE); 1288*2379Ssjelinek break; 1289*2379Ssjelinek case 'S': 1290*2379Ssjelinek if ((prop = zfs_name_to_prop(optarg)) == 1291*2379Ssjelinek ZFS_PROP_INVAL) { 1292*2379Ssjelinek (void) fprintf(stderr, 1293*2379Ssjelinek gettext("invalid property '%s'\n"), optarg); 1294*2379Ssjelinek usage(B_FALSE); 1295*2379Ssjelinek } 1296*2379Ssjelinek zfs_add_sort_column(&sortcol, prop, B_TRUE); 1297*2379Ssjelinek break; 1298789Sahrens case 't': 1299789Sahrens types = 0; 1300789Sahrens while (*optarg != '\0') { 1301789Sahrens switch (getsubopt(&optarg, type_subopts, 1302789Sahrens &value)) { 1303789Sahrens case 0: 1304789Sahrens types |= ZFS_TYPE_FILESYSTEM; 1305789Sahrens break; 1306789Sahrens case 1: 1307789Sahrens types |= ZFS_TYPE_VOLUME; 1308789Sahrens break; 1309789Sahrens case 2: 1310789Sahrens types |= ZFS_TYPE_SNAPSHOT; 1311789Sahrens break; 1312789Sahrens default: 1313789Sahrens (void) fprintf(stderr, 1314789Sahrens gettext("invalid type '%s'\n"), 1315789Sahrens value); 13162082Seschrock usage(B_FALSE); 1317789Sahrens } 1318789Sahrens } 1319789Sahrens break; 1320789Sahrens case ':': 1321789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1322789Sahrens "'%c' option\n"), optopt); 13232082Seschrock usage(B_FALSE); 1324789Sahrens break; 1325789Sahrens case '?': 1326789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1327789Sahrens optopt); 13282082Seschrock usage(B_FALSE); 1329789Sahrens } 1330789Sahrens } 1331789Sahrens 1332789Sahrens argc -= optind; 1333789Sahrens argv += optind; 1334789Sahrens 1335789Sahrens if (fields == NULL) 1336789Sahrens fields = basic_fields; 1337789Sahrens 1338866Seschrock /* 1339866Seschrock * If the user specifies '-o all', the zfs_get_proplist() doesn't 1340866Seschrock * normally include the name of the dataset. For 'zfs list', we always 1341866Seschrock * want this property to be first. 1342866Seschrock */ 1343866Seschrock if (strcmp(fields, "all") == 0) { 1344866Seschrock cb.cb_fields[0] = ZFS_PROP_NAME; 1345866Seschrock alloffset = 1; 1346866Seschrock } else { 1347866Seschrock alloffset = 0; 1348789Sahrens } 1349789Sahrens 1350866Seschrock if ((ret = zfs_get_proplist(fields, cb.cb_fields + alloffset, 1351866Seschrock ZFS_NPROP_ALL - alloffset, &cb.cb_fieldcount, &badopt)) != 0) { 1352866Seschrock if (ret == EINVAL) 1353866Seschrock (void) fprintf(stderr, gettext("invalid property " 1354866Seschrock "'%s'\n"), badopt); 1355866Seschrock else 1356866Seschrock (void) fprintf(stderr, gettext("too many properties " 1357866Seschrock "specified\n")); 13582082Seschrock usage(B_FALSE); 1359866Seschrock } 1360866Seschrock 1361866Seschrock cb.cb_fieldcount += alloffset; 1362789Sahrens cb.cb_scripted = scripted; 13632082Seschrock cb.cb_first = B_TRUE; 1364789Sahrens 1365*2379Ssjelinek ret = zfs_for_each(argc, argv, recurse, types, sortcol, 1366*2379Ssjelinek list_callback, &cb); 1367*2379Ssjelinek 1368*2379Ssjelinek zfs_free_sort_columns(sortcol); 1369789Sahrens 13702082Seschrock if (ret == 0 && cb.cb_first) 1371789Sahrens (void) printf(gettext("no datasets available\n")); 1372789Sahrens 1373789Sahrens return (ret); 1374789Sahrens } 1375789Sahrens 1376789Sahrens /* 1377789Sahrens * zfs rename <fs | snap | vol> <fs | snap | vol> 1378789Sahrens * 1379789Sahrens * Renames the given dataset to another of the same type. 1380789Sahrens */ 1381789Sahrens /* ARGSUSED */ 1382789Sahrens static int 1383789Sahrens zfs_do_rename(int argc, char **argv) 1384789Sahrens { 1385789Sahrens zfs_handle_t *zhp; 13862082Seschrock int ret; 1387789Sahrens 1388789Sahrens /* check options */ 1389789Sahrens if (argc > 1 && argv[1][0] == '-') { 1390789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1391789Sahrens argv[1][1]); 13922082Seschrock usage(B_FALSE); 1393789Sahrens } 1394789Sahrens 1395789Sahrens /* check number of arguments */ 1396789Sahrens if (argc < 2) { 1397789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 1398789Sahrens "argument\n")); 13992082Seschrock usage(B_FALSE); 1400789Sahrens } 1401789Sahrens if (argc < 3) { 1402789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 1403789Sahrens "argument\n")); 14042082Seschrock usage(B_FALSE); 1405789Sahrens } 1406789Sahrens if (argc > 3) { 1407789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 14082082Seschrock usage(B_FALSE); 1409789Sahrens } 1410789Sahrens 14112082Seschrock if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_ANY)) == NULL) 1412789Sahrens return (1); 1413789Sahrens 14142082Seschrock ret = (zfs_rename(zhp, argv[2]) != 0); 14152082Seschrock 14162082Seschrock zfs_close(zhp); 14172082Seschrock return (ret); 14182082Seschrock } 14192082Seschrock 14202082Seschrock /* 14212082Seschrock * zfs promote <fs> 14222082Seschrock * 14232082Seschrock * Promotes the given clone fs to be the parent 14242082Seschrock */ 14252082Seschrock /* ARGSUSED */ 14262082Seschrock static int 14272082Seschrock zfs_do_promote(int argc, char **argv) 14282082Seschrock { 14292082Seschrock zfs_handle_t *zhp; 14302082Seschrock int ret; 14312082Seschrock 14322082Seschrock /* check options */ 14332082Seschrock if (argc > 1 && argv[1][0] == '-') { 14342082Seschrock (void) fprintf(stderr, gettext("invalid option '%c'\n"), 14352082Seschrock argv[1][1]); 14362082Seschrock usage(B_FALSE); 14372082Seschrock } 14382082Seschrock 14392082Seschrock /* check number of arguments */ 14402082Seschrock if (argc < 2) { 14412082Seschrock (void) fprintf(stderr, gettext("missing clone filesystem" 14422082Seschrock "argument\n")); 14432082Seschrock usage(B_FALSE); 14442082Seschrock } 14452082Seschrock if (argc > 2) { 14462082Seschrock (void) fprintf(stderr, gettext("too many arguments\n")); 14472082Seschrock usage(B_FALSE); 14482082Seschrock } 14492082Seschrock 14502082Seschrock zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 14512082Seschrock if (zhp == NULL) 14522082Seschrock return (1); 14532082Seschrock 14542082Seschrock ret = (zfs_promote(zhp) != 0); 14552082Seschrock 1456789Sahrens zfs_close(zhp); 1457789Sahrens return (ret); 1458789Sahrens } 1459789Sahrens 1460789Sahrens /* 1461789Sahrens * zfs rollback [-rfR] <snapshot> 1462789Sahrens * 1463789Sahrens * -r Delete any intervening snapshots before doing rollback 1464789Sahrens * -R Delete any snapshots and their clones 1465789Sahrens * -f Force unmount filesystems, even if they are in use. 1466789Sahrens * 1467789Sahrens * Given a filesystem, rollback to a specific snapshot, discarding any changes 1468789Sahrens * since then and making it the active dataset. If more recent snapshots exist, 1469789Sahrens * the command will complain unless the '-r' flag is given. 1470789Sahrens */ 1471789Sahrens typedef struct rollback_cbdata { 1472789Sahrens uint64_t cb_create; 14732082Seschrock boolean_t cb_first; 1474789Sahrens int cb_doclones; 1475789Sahrens char *cb_target; 1476789Sahrens int cb_error; 14772082Seschrock boolean_t cb_recurse; 14782082Seschrock boolean_t cb_dependent; 1479789Sahrens } rollback_cbdata_t; 1480789Sahrens 1481789Sahrens /* 1482789Sahrens * Report any snapshots more recent than the one specified. Used when '-r' is 1483789Sahrens * not specified. We reuse this same callback for the snapshot dependents - if 1484789Sahrens * 'cb_dependent' is set, then this is a dependent and we should report it 1485789Sahrens * without checking the transaction group. 1486789Sahrens */ 1487789Sahrens static int 1488789Sahrens rollback_check(zfs_handle_t *zhp, void *data) 1489789Sahrens { 1490789Sahrens rollback_cbdata_t *cbp = data; 1491789Sahrens 14922082Seschrock if (cbp->cb_doclones) { 14932082Seschrock zfs_close(zhp); 1494789Sahrens return (0); 14952082Seschrock } 1496789Sahrens 1497789Sahrens if (!cbp->cb_dependent) { 1498789Sahrens if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && 14991294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 1500789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 1501789Sahrens cbp->cb_create) { 1502789Sahrens 1503789Sahrens if (cbp->cb_first && !cbp->cb_recurse) { 1504789Sahrens (void) fprintf(stderr, gettext("cannot " 1505789Sahrens "rollback to '%s': more recent snapshots " 1506789Sahrens "exist\n"), 1507789Sahrens cbp->cb_target); 1508789Sahrens (void) fprintf(stderr, gettext("use '-r' to " 1509789Sahrens "force deletion of the following " 1510789Sahrens "snapshots:\n")); 1511789Sahrens cbp->cb_first = 0; 1512789Sahrens cbp->cb_error = 1; 1513789Sahrens } 1514789Sahrens 1515789Sahrens if (cbp->cb_recurse) { 15162082Seschrock cbp->cb_dependent = B_TRUE; 1517789Sahrens (void) zfs_iter_dependents(zhp, rollback_check, 1518789Sahrens cbp); 15192082Seschrock cbp->cb_dependent = B_FALSE; 1520789Sahrens } else { 1521789Sahrens (void) fprintf(stderr, "%s\n", 1522789Sahrens zfs_get_name(zhp)); 1523789Sahrens } 1524789Sahrens } 1525789Sahrens } else { 1526789Sahrens if (cbp->cb_first && cbp->cb_recurse) { 1527789Sahrens (void) fprintf(stderr, gettext("cannot rollback to " 1528789Sahrens "'%s': clones of previous snapshots exist\n"), 1529789Sahrens cbp->cb_target); 1530789Sahrens (void) fprintf(stderr, gettext("use '-R' to " 1531789Sahrens "force deletion of the following clones and " 1532789Sahrens "dependents:\n")); 1533789Sahrens cbp->cb_first = 0; 1534789Sahrens cbp->cb_error = 1; 1535789Sahrens } 1536789Sahrens 1537789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1538789Sahrens } 1539789Sahrens 1540789Sahrens zfs_close(zhp); 1541789Sahrens return (0); 1542789Sahrens } 1543789Sahrens 1544789Sahrens static int 1545789Sahrens zfs_do_rollback(int argc, char **argv) 1546789Sahrens { 1547789Sahrens int ret; 1548789Sahrens int c; 1549789Sahrens rollback_cbdata_t cb = { 0 }; 1550789Sahrens zfs_handle_t *zhp, *snap; 1551789Sahrens char parentname[ZFS_MAXNAMELEN]; 1552789Sahrens char *delim; 15531294Slling int force = 0; 1554789Sahrens 1555789Sahrens /* check options */ 1556789Sahrens while ((c = getopt(argc, argv, "rfR")) != -1) { 1557789Sahrens switch (c) { 1558789Sahrens case 'f': 15591294Slling force = 1; 1560789Sahrens break; 1561789Sahrens case 'r': 1562789Sahrens cb.cb_recurse = 1; 1563789Sahrens break; 1564789Sahrens case 'R': 1565789Sahrens cb.cb_recurse = 1; 1566789Sahrens cb.cb_doclones = 1; 1567789Sahrens break; 1568789Sahrens case '?': 1569789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1570789Sahrens optopt); 15712082Seschrock usage(B_FALSE); 1572789Sahrens } 1573789Sahrens } 1574789Sahrens 1575789Sahrens argc -= optind; 1576789Sahrens argv += optind; 1577789Sahrens 1578789Sahrens /* check number of arguments */ 1579789Sahrens if (argc < 1) { 1580789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 15812082Seschrock usage(B_FALSE); 1582789Sahrens } 1583789Sahrens if (argc > 1) { 1584789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 15852082Seschrock usage(B_FALSE); 1586789Sahrens } 1587789Sahrens 1588789Sahrens /* open the snapshot */ 15892082Seschrock if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1590789Sahrens return (1); 1591789Sahrens 15921294Slling /* open the parent dataset */ 15931294Slling (void) strlcpy(parentname, argv[0], sizeof (parentname)); 1594789Sahrens verify((delim = strrchr(parentname, '@')) != NULL); 1595789Sahrens *delim = '\0'; 15962082Seschrock if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_ANY)) == NULL) { 1597789Sahrens zfs_close(snap); 1598789Sahrens return (1); 1599789Sahrens } 1600789Sahrens 1601789Sahrens /* 1602789Sahrens * Check for more recent snapshots and/or clones based on the presence 1603789Sahrens * of '-r' and '-R'. 1604789Sahrens */ 16051294Slling cb.cb_target = argv[0]; 16061294Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 16072082Seschrock cb.cb_first = B_TRUE; 1608789Sahrens cb.cb_error = 0; 1609789Sahrens (void) zfs_iter_children(zhp, rollback_check, &cb); 1610789Sahrens 1611789Sahrens if ((ret = cb.cb_error) != 0) 1612789Sahrens goto out; 1613789Sahrens 1614789Sahrens /* 16151294Slling * Rollback parent to the given snapshot. 1616789Sahrens */ 16171294Slling ret = zfs_rollback(zhp, snap, force); 1618789Sahrens 1619789Sahrens out: 1620789Sahrens zfs_close(snap); 1621789Sahrens zfs_close(zhp); 1622789Sahrens 1623789Sahrens if (ret == 0) 1624789Sahrens return (0); 1625789Sahrens else 1626789Sahrens return (1); 1627789Sahrens } 1628789Sahrens 1629789Sahrens /* 1630789Sahrens * zfs set property=value { fs | snap | vol } ... 1631789Sahrens * 1632789Sahrens * Sets the given property for all datasets specified on the command line. 1633789Sahrens */ 1634789Sahrens typedef struct set_cbdata { 1635789Sahrens char *cb_propname; 1636789Sahrens char *cb_value; 1637789Sahrens zfs_prop_t cb_prop; 1638789Sahrens } set_cbdata_t; 1639789Sahrens 1640789Sahrens static int 1641789Sahrens set_callback(zfs_handle_t *zhp, void *data) 1642789Sahrens { 1643789Sahrens set_cbdata_t *cbp = data; 1644789Sahrens int ret = 1; 1645789Sahrens 1646789Sahrens /* don't allow setting of properties for snapshots */ 1647789Sahrens if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { 1648789Sahrens (void) fprintf(stderr, gettext("cannot set %s property for " 1649789Sahrens "'%s': snapshot properties cannot be modified\n"), 1650789Sahrens cbp->cb_propname, zfs_get_name(zhp)); 1651789Sahrens return (1); 1652789Sahrens } 1653789Sahrens 1654789Sahrens /* 16551133Seschrock * If we're changing the volsize, make sure the value is appropriate, 16561133Seschrock * and set the reservation if this is a non-sparse volume. 1657789Sahrens */ 1658789Sahrens if (cbp->cb_prop == ZFS_PROP_VOLSIZE && 16591133Seschrock zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 1660789Sahrens uint64_t volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1661789Sahrens uint64_t avail = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE); 16621133Seschrock uint64_t reservation = zfs_prop_get_int(zhp, 16631133Seschrock ZFS_PROP_RESERVATION); 16641133Seschrock uint64_t blocksize = zfs_prop_get_int(zhp, 16651133Seschrock ZFS_PROP_VOLBLOCKSIZE); 1666789Sahrens uint64_t value; 1667789Sahrens 1668789Sahrens verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); 1669789Sahrens 16701133Seschrock if (value % blocksize != 0) { 16711133Seschrock char buf[64]; 16721133Seschrock 16731133Seschrock zfs_nicenum(blocksize, buf, sizeof (buf)); 16741133Seschrock (void) fprintf(stderr, gettext("cannot set %s for " 16751133Seschrock "'%s': must be a multiple of volume block size " 16761133Seschrock "(%s)\n"), cbp->cb_propname, zfs_get_name(zhp), 16771133Seschrock buf); 1678789Sahrens return (1); 1679789Sahrens } 1680789Sahrens 16811133Seschrock if (value == 0) { 16821133Seschrock (void) fprintf(stderr, gettext("cannot set %s for " 16831133Seschrock "'%s': cannot be zero\n"), cbp->cb_propname, 16841133Seschrock zfs_get_name(zhp)); 1685789Sahrens return (1); 1686789Sahrens } 16871133Seschrock 16881133Seschrock if (volsize == reservation) { 16891133Seschrock if (value > volsize && (value - volsize) > avail) { 16901133Seschrock (void) fprintf(stderr, gettext("cannot set " 16911133Seschrock "%s property for '%s': volume size exceeds " 16921133Seschrock "amount of available space\n"), 16931133Seschrock cbp->cb_propname, zfs_get_name(zhp)); 16941133Seschrock return (1); 16951133Seschrock } 16961133Seschrock 16971133Seschrock if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, 16981133Seschrock cbp->cb_value) != 0) { 16991133Seschrock (void) fprintf(stderr, gettext("volsize and " 17001133Seschrock "reservation must remain equal\n")); 17011133Seschrock return (1); 17021133Seschrock } 17031133Seschrock } 1704789Sahrens } 1705789Sahrens 1706789Sahrens /* 1707789Sahrens * Do not allow the reservation to be set above the volume size. We do 1708789Sahrens * this here instead of inside libzfs because libzfs violates this rule 1709789Sahrens * internally. 1710789Sahrens */ 1711789Sahrens if (cbp->cb_prop == ZFS_PROP_RESERVATION && 1712789Sahrens zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 1713789Sahrens uint64_t value; 1714789Sahrens uint64_t volsize; 1715789Sahrens 1716789Sahrens volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1717789Sahrens if (strcmp(cbp->cb_value, "none") == 0) 1718789Sahrens value = 0; 1719789Sahrens else 1720789Sahrens verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); 1721789Sahrens 1722789Sahrens if (value > volsize) { 1723789Sahrens (void) fprintf(stderr, gettext("cannot set %s " 1724789Sahrens "for '%s': size is greater than current " 1725789Sahrens "volume size\n"), cbp->cb_propname, 1726789Sahrens zfs_get_name(zhp)); 1727789Sahrens return (-1); 1728789Sahrens } 1729789Sahrens } 1730789Sahrens 17312169Snd150628 if (zfs_prop_set(zhp, cbp->cb_prop, cbp->cb_value) != 0) { 17322169Snd150628 switch (libzfs_errno(g_zfs)) { 17332169Snd150628 case EZFS_MOUNTFAILED: 17342169Snd150628 (void) fprintf(stderr, gettext("property may be set " 17352169Snd150628 "but unable to remount filesystem\n")); 17362169Snd150628 break; 17372169Snd150628 case EZFS_SHAREFAILED: 17382169Snd150628 (void) fprintf(stderr, gettext("property may be set " 17392169Snd150628 "but unable to reshare filesystem\n")); 17402169Snd150628 break; 17412169Snd150628 } 1742789Sahrens return (1); 17432169Snd150628 } 1744789Sahrens ret = 0; 1745789Sahrens error: 1746789Sahrens return (ret); 1747789Sahrens } 1748789Sahrens 1749789Sahrens static int 1750789Sahrens zfs_do_set(int argc, char **argv) 1751789Sahrens { 1752789Sahrens set_cbdata_t cb; 1753789Sahrens 1754789Sahrens /* check for options */ 1755789Sahrens if (argc > 1 && argv[1][0] == '-') { 1756789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1757789Sahrens argv[1][1]); 17582082Seschrock usage(B_FALSE); 1759789Sahrens } 1760789Sahrens 1761789Sahrens /* check number of arguments */ 1762789Sahrens if (argc < 2) { 1763789Sahrens (void) fprintf(stderr, gettext("missing property=value " 1764789Sahrens "argument\n")); 17652082Seschrock usage(B_FALSE); 1766789Sahrens } 1767789Sahrens if (argc < 3) { 1768789Sahrens (void) fprintf(stderr, gettext("missing dataset name\n")); 17692082Seschrock usage(B_FALSE); 1770789Sahrens } 1771789Sahrens 1772789Sahrens /* validate property=value argument */ 1773789Sahrens cb.cb_propname = argv[1]; 1774789Sahrens if ((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) { 1775789Sahrens (void) fprintf(stderr, gettext("missing value in " 1776789Sahrens "property=value argument\n")); 17772082Seschrock usage(B_FALSE); 1778789Sahrens } 1779789Sahrens 1780789Sahrens *cb.cb_value = '\0'; 1781789Sahrens cb.cb_value++; 1782789Sahrens 1783789Sahrens if (*cb.cb_propname == '\0') { 1784789Sahrens (void) fprintf(stderr, 1785789Sahrens gettext("missing property in property=value argument\n")); 17862082Seschrock usage(B_FALSE); 1787789Sahrens } 1788789Sahrens if (*cb.cb_value == '\0') { 1789789Sahrens (void) fprintf(stderr, 1790789Sahrens gettext("missing value in property=value argument\n")); 17912082Seschrock usage(B_FALSE); 1792789Sahrens } 1793789Sahrens 1794789Sahrens /* get the property type */ 1795789Sahrens if ((cb.cb_prop = zfs_name_to_prop(cb.cb_propname)) == 1796789Sahrens ZFS_PROP_INVAL) { 1797789Sahrens (void) fprintf(stderr, 1798789Sahrens gettext("invalid property '%s'\n"), cb.cb_propname); 17992082Seschrock usage(B_FALSE); 1800789Sahrens } 1801789Sahrens 1802789Sahrens /* 1803789Sahrens * Validate that the value is appropriate for this property. We do this 1804789Sahrens * once now so we don't generate multiple errors each time we try to 1805789Sahrens * apply it to a dataset. 1806789Sahrens */ 18072082Seschrock if (zfs_prop_validate(g_zfs, cb.cb_prop, cb.cb_value, NULL) != 0) 1808789Sahrens return (1); 1809789Sahrens 18102082Seschrock return (zfs_for_each(argc - 2, argv + 2, B_FALSE, 1811*2379Ssjelinek ZFS_TYPE_ANY, NULL, set_callback, &cb)); 1812789Sahrens } 1813789Sahrens 1814789Sahrens /* 18152199Sahrens * zfs snapshot [-r] <fs@snap> 1816789Sahrens * 1817789Sahrens * Creates a snapshot with the given name. While functionally equivalent to 1818789Sahrens * 'zfs create', it is a separate command to diffferentiate intent. 1819789Sahrens */ 1820789Sahrens static int 1821789Sahrens zfs_do_snapshot(int argc, char **argv) 1822789Sahrens { 18232199Sahrens int recursive = B_FALSE; 18242199Sahrens int ret; 18252199Sahrens char c; 18262199Sahrens 1827789Sahrens /* check options */ 18282199Sahrens while ((c = getopt(argc, argv, ":r")) != -1) { 18292199Sahrens switch (c) { 18302199Sahrens case 'r': 18312199Sahrens recursive = B_TRUE; 18322199Sahrens break; 18332199Sahrens case '?': 18342199Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 18352199Sahrens optopt); 18362199Sahrens usage(B_FALSE); 18372199Sahrens } 1838789Sahrens } 1839789Sahrens 18402199Sahrens argc -= optind; 18412199Sahrens argv += optind; 18422199Sahrens 1843789Sahrens /* check number of arguments */ 18442199Sahrens if (argc < 1) { 1845789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 18462082Seschrock usage(B_FALSE); 1847789Sahrens } 18482199Sahrens if (argc > 1) { 1849789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 18502082Seschrock usage(B_FALSE); 1851789Sahrens } 1852789Sahrens 18532199Sahrens ret = zfs_snapshot(g_zfs, argv[0], recursive); 18542199Sahrens if (ret && recursive) 18552199Sahrens (void) fprintf(stderr, gettext("no snapshots were created\n")); 18562199Sahrens return (ret != 0); 18572199Sahrens 1858789Sahrens } 1859789Sahrens 1860789Sahrens /* 18611749Sahrens * zfs send [-i <fs@snap>] <fs@snap> 1862789Sahrens * 1863789Sahrens * Send a backup stream to stdout. 1864789Sahrens */ 1865789Sahrens static int 18661749Sahrens zfs_do_send(int argc, char **argv) 1867789Sahrens { 1868789Sahrens char *fromname = NULL; 1869789Sahrens zfs_handle_t *zhp_from = NULL, *zhp_to; 1870789Sahrens int c, err; 1871789Sahrens 1872789Sahrens /* check options */ 1873789Sahrens while ((c = getopt(argc, argv, ":i:")) != -1) { 1874789Sahrens switch (c) { 1875789Sahrens case 'i': 1876789Sahrens fromname = optarg; 1877789Sahrens break; 1878789Sahrens case ':': 1879789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1880789Sahrens "'%c' option\n"), optopt); 18812082Seschrock usage(B_FALSE); 1882789Sahrens break; 1883789Sahrens case '?': 1884789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1885789Sahrens optopt); 18862082Seschrock usage(B_FALSE); 1887789Sahrens } 1888789Sahrens } 1889789Sahrens 1890789Sahrens argc -= optind; 1891789Sahrens argv += optind; 1892789Sahrens 1893789Sahrens /* check number of arguments */ 1894789Sahrens if (argc < 1) { 1895789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 18962082Seschrock usage(B_FALSE); 1897789Sahrens } 1898789Sahrens if (argc > 1) { 1899789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 19002082Seschrock usage(B_FALSE); 1901789Sahrens } 1902789Sahrens 1903789Sahrens if (isatty(STDOUT_FILENO)) { 1904789Sahrens (void) fprintf(stderr, 19051749Sahrens gettext("Error: Stream can not be written " 1906789Sahrens "to a terminal.\n" 1907789Sahrens "You must redirect standard output.\n")); 1908789Sahrens return (1); 1909789Sahrens } 1910789Sahrens 1911789Sahrens if (fromname) { 19122082Seschrock if ((zhp_from = zfs_open(g_zfs, fromname, 19132082Seschrock ZFS_TYPE_SNAPSHOT)) == NULL) 1914789Sahrens return (1); 1915789Sahrens } 19162082Seschrock if ((zhp_to = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1917789Sahrens return (1); 1918789Sahrens 19191749Sahrens err = zfs_send(zhp_to, zhp_from); 1920789Sahrens 1921789Sahrens if (zhp_from) 1922789Sahrens zfs_close(zhp_from); 1923789Sahrens zfs_close(zhp_to); 1924789Sahrens 1925789Sahrens return (err != 0); 1926789Sahrens } 1927789Sahrens 1928789Sahrens /* 19291749Sahrens * zfs receive <fs@snap> 1930789Sahrens * 1931789Sahrens * Restore a backup stream from stdin. 1932789Sahrens */ 1933789Sahrens static int 19341749Sahrens zfs_do_receive(int argc, char **argv) 1935789Sahrens { 1936789Sahrens int c, err; 19372082Seschrock boolean_t isprefix = B_FALSE; 19382082Seschrock boolean_t dryrun = B_FALSE; 19392082Seschrock boolean_t verbose = B_FALSE; 1940789Sahrens 1941789Sahrens /* check options */ 1942789Sahrens while ((c = getopt(argc, argv, ":dnv")) != -1) { 1943789Sahrens switch (c) { 1944789Sahrens case 'd': 19452082Seschrock isprefix = B_TRUE; 1946789Sahrens break; 1947789Sahrens case 'n': 19482082Seschrock dryrun = B_TRUE; 1949789Sahrens break; 1950789Sahrens case 'v': 19512082Seschrock verbose = B_TRUE; 1952789Sahrens break; 1953789Sahrens case ':': 1954789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1955789Sahrens "'%c' option\n"), optopt); 19562082Seschrock usage(B_FALSE); 1957789Sahrens break; 1958789Sahrens case '?': 1959789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1960789Sahrens optopt); 19612082Seschrock usage(B_FALSE); 1962789Sahrens } 1963789Sahrens } 1964789Sahrens 1965789Sahrens argc -= optind; 1966789Sahrens argv += optind; 1967789Sahrens 1968789Sahrens /* check number of arguments */ 1969789Sahrens if (argc < 1) { 1970789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 19712082Seschrock usage(B_FALSE); 1972789Sahrens } 1973789Sahrens if (argc > 1) { 1974789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 19752082Seschrock usage(B_FALSE); 1976789Sahrens } 1977789Sahrens 1978789Sahrens if (isatty(STDIN_FILENO)) { 1979789Sahrens (void) fprintf(stderr, 1980789Sahrens gettext("Error: Backup stream can not be read " 1981789Sahrens "from a terminal.\n" 1982789Sahrens "You must redirect standard input.\n")); 1983789Sahrens return (1); 1984789Sahrens } 1985789Sahrens 19862082Seschrock err = zfs_receive(g_zfs, argv[0], isprefix, verbose, dryrun); 1987789Sahrens return (err != 0); 1988789Sahrens } 1989789Sahrens 19901356Seschrock typedef struct get_all_cbdata { 19911356Seschrock zfs_handle_t **cb_handles; 19921356Seschrock size_t cb_alloc; 19931356Seschrock size_t cb_used; 19941356Seschrock } get_all_cbdata_t; 19951356Seschrock 19961356Seschrock static int 19971356Seschrock get_one_filesystem(zfs_handle_t *zhp, void *data) 19981356Seschrock { 19991356Seschrock get_all_cbdata_t *cbp = data; 20001356Seschrock 20011356Seschrock /* 20021356Seschrock * Skip any zvols 20031356Seschrock */ 20041356Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 20051356Seschrock zfs_close(zhp); 20061356Seschrock return (0); 20071356Seschrock } 20081356Seschrock 20091356Seschrock if (cbp->cb_alloc == cbp->cb_used) { 20101356Seschrock zfs_handle_t **handles; 20111356Seschrock 20121356Seschrock if (cbp->cb_alloc == 0) 20131356Seschrock cbp->cb_alloc = 64; 20141356Seschrock else 20151356Seschrock cbp->cb_alloc *= 2; 20161356Seschrock 20171356Seschrock handles = safe_malloc(cbp->cb_alloc * sizeof (void *)); 20181356Seschrock 20191356Seschrock if (cbp->cb_handles) { 20201356Seschrock bcopy(cbp->cb_handles, handles, 20211356Seschrock cbp->cb_used * sizeof (void *)); 20221356Seschrock free(cbp->cb_handles); 20231356Seschrock } 20241356Seschrock 20251356Seschrock cbp->cb_handles = handles; 20261356Seschrock } 20271356Seschrock 20281356Seschrock cbp->cb_handles[cbp->cb_used++] = zhp; 20291356Seschrock 20301356Seschrock return (zfs_iter_filesystems(zhp, get_one_filesystem, data)); 20311356Seschrock } 20321356Seschrock 20331356Seschrock static void 20341356Seschrock get_all_filesystems(zfs_handle_t ***fslist, size_t *count) 20351356Seschrock { 20361356Seschrock get_all_cbdata_t cb = { 0 }; 20371356Seschrock 20382082Seschrock (void) zfs_iter_root(g_zfs, get_one_filesystem, &cb); 20391356Seschrock 20401356Seschrock *fslist = cb.cb_handles; 20411356Seschrock *count = cb.cb_used; 20421356Seschrock } 20431356Seschrock 20441356Seschrock static int 20451356Seschrock mountpoint_compare(const void *a, const void *b) 20461356Seschrock { 20471356Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 20481356Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 20491356Seschrock char mounta[MAXPATHLEN]; 20501356Seschrock char mountb[MAXPATHLEN]; 20511356Seschrock 20521356Seschrock verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 20532082Seschrock sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 20541356Seschrock verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 20552082Seschrock sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 20561356Seschrock 20571356Seschrock return (strcmp(mounta, mountb)); 20581356Seschrock } 2059789Sahrens 2060789Sahrens /* 2061789Sahrens * Generic callback for sharing or mounting filesystems. Because the code is so 2062789Sahrens * similar, we have a common function with an extra parameter to determine which 2063789Sahrens * mode we are using. 2064789Sahrens */ 2065789Sahrens #define OP_SHARE 0x1 2066789Sahrens #define OP_MOUNT 0x2 2067789Sahrens 2068789Sahrens typedef struct share_mount_cbdata { 2069789Sahrens int cb_type; 2070789Sahrens int cb_explicit; 2071789Sahrens int cb_flags; 2072789Sahrens const char *cb_options; 2073789Sahrens } share_mount_cbdata_t; 2074789Sahrens 2075789Sahrens /* 2076789Sahrens * Share or mount the filesystem. 2077789Sahrens */ 2078789Sahrens static int 2079789Sahrens share_mount_callback(zfs_handle_t *zhp, void *data) 2080789Sahrens { 2081789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2082789Sahrens char shareopts[ZFS_MAXPROPLEN]; 2083789Sahrens share_mount_cbdata_t *cbp = data; 2084789Sahrens const char *cmdname = cbp->cb_type == OP_SHARE ? "share" : "mount"; 2085789Sahrens struct mnttab mnt; 2086789Sahrens uint64_t zoned; 2087789Sahrens 2088789Sahrens if (cbp->cb_options == NULL) 2089789Sahrens mnt.mnt_mntopts = ""; 2090789Sahrens else 2091789Sahrens mnt.mnt_mntopts = (char *)cbp->cb_options; 2092789Sahrens 2093789Sahrens /* 2094789Sahrens * Check to make sure we can mount/share this dataset. If we are in the 2095789Sahrens * global zone and the filesystem is exported to a local zone, or if we 2096789Sahrens * are in a local zone and the filesystem is not exported, then it is an 2097789Sahrens * error. 2098789Sahrens */ 2099789Sahrens zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2100789Sahrens 2101789Sahrens if (zoned && getzoneid() == GLOBAL_ZONEID) { 2102789Sahrens if (!cbp->cb_explicit) 2103789Sahrens return (0); 2104789Sahrens 2105789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': dataset is " 2106789Sahrens "exported to a local zone\n"), cmdname, zfs_get_name(zhp)); 2107789Sahrens return (1); 2108789Sahrens 2109789Sahrens } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 2110789Sahrens if (!cbp->cb_explicit) 2111789Sahrens return (0); 2112789Sahrens 2113789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': permission " 2114789Sahrens "denied\n"), cmdname, zfs_get_name(zhp)); 2115789Sahrens return (1); 2116789Sahrens } 2117789Sahrens 2118789Sahrens /* 2119789Sahrens * Inore any filesystems which don't apply to us. This includes those 2120789Sahrens * with a legacy mountpoint, or those with legacy share options. 2121789Sahrens */ 2122789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 21232082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); 2124789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 21252082Seschrock sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 2126789Sahrens 2127789Sahrens if (cbp->cb_type == OP_SHARE) { 2128789Sahrens if (strcmp(shareopts, "off") == 0) { 2129789Sahrens if (!cbp->cb_explicit) 2130789Sahrens return (0); 2131789Sahrens 2132789Sahrens (void) fprintf(stderr, gettext("cannot share '%s': " 2133789Sahrens "legacy share\n"), zfs_get_name(zhp)); 2134789Sahrens (void) fprintf(stderr, gettext("use share(1M) to " 2135789Sahrens "share this filesystem\n")); 2136789Sahrens return (1); 2137789Sahrens } 2138789Sahrens } 2139789Sahrens 2140789Sahrens /* 2141789Sahrens * We cannot share or mount legacy filesystems. If the shareopts is 2142789Sahrens * non-legacy but the mountpoint is legacy, we treat it as a legacy 2143789Sahrens * share. 2144789Sahrens */ 2145789Sahrens if (strcmp(mountpoint, "legacy") == 0) { 2146789Sahrens if (!cbp->cb_explicit) 2147789Sahrens return (0); 2148789Sahrens 2149789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': " 2150789Sahrens "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 2151789Sahrens (void) fprintf(stderr, gettext("use %s to " 2152789Sahrens "%s this filesystem\n"), cbp->cb_type == OP_SHARE ? 2153789Sahrens "share(1M)" : "mount(1M)", cmdname); 2154789Sahrens return (1); 2155789Sahrens } 2156789Sahrens 2157789Sahrens if (strcmp(mountpoint, "none") == 0) { 2158789Sahrens if (!cbp->cb_explicit) 2159789Sahrens return (0); 2160789Sahrens 2161789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': no " 2162789Sahrens "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 2163789Sahrens return (1); 2164789Sahrens } 2165789Sahrens 2166789Sahrens /* 2167789Sahrens * At this point, we have verified that the mountpoint and/or shareopts 2168789Sahrens * are appropriate for auto management. Determine if the filesystem is 2169789Sahrens * currently mounted or shared, and abort if this is an explicit 2170789Sahrens * request. 2171789Sahrens */ 2172789Sahrens switch (cbp->cb_type) { 2173789Sahrens case OP_SHARE: 2174789Sahrens if (zfs_is_shared(zhp, NULL)) { 2175789Sahrens if (cbp->cb_explicit) { 2176789Sahrens (void) fprintf(stderr, gettext("cannot share " 2177789Sahrens "'%s': filesystem already shared\n"), 2178789Sahrens zfs_get_name(zhp)); 2179789Sahrens return (1); 2180789Sahrens } else { 2181789Sahrens return (0); 2182789Sahrens } 2183789Sahrens } 2184789Sahrens break; 2185789Sahrens 2186789Sahrens case OP_MOUNT: 2187789Sahrens if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 2188789Sahrens zfs_is_mounted(zhp, NULL)) { 2189789Sahrens if (cbp->cb_explicit) { 2190789Sahrens (void) fprintf(stderr, gettext("cannot mount " 2191789Sahrens "'%s': filesystem already mounted\n"), 2192789Sahrens zfs_get_name(zhp)); 2193789Sahrens return (1); 2194789Sahrens } else { 2195789Sahrens return (0); 2196789Sahrens } 2197789Sahrens } 2198789Sahrens break; 2199789Sahrens } 2200789Sahrens 2201789Sahrens /* 2202789Sahrens * Mount and optionally share the filesystem. 2203789Sahrens */ 2204789Sahrens switch (cbp->cb_type) { 2205789Sahrens case OP_SHARE: 2206789Sahrens { 2207789Sahrens if (!zfs_is_mounted(zhp, NULL) && 2208789Sahrens zfs_mount(zhp, NULL, 0) != 0) 2209789Sahrens return (1); 2210789Sahrens 2211789Sahrens if (zfs_share(zhp) != 0) 2212789Sahrens return (1); 2213789Sahrens } 2214789Sahrens break; 2215789Sahrens 2216789Sahrens case OP_MOUNT: 2217789Sahrens if (zfs_mount(zhp, cbp->cb_options, cbp->cb_flags) != 0) 2218789Sahrens return (1); 2219789Sahrens break; 2220789Sahrens } 2221789Sahrens 2222789Sahrens return (0); 2223789Sahrens } 2224789Sahrens 2225789Sahrens static int 2226789Sahrens share_or_mount(int type, int argc, char **argv) 2227789Sahrens { 2228789Sahrens int do_all = 0; 22292372Slling int c, ret = 0; 2230789Sahrens share_mount_cbdata_t cb = { 0 }; 2231789Sahrens 2232789Sahrens cb.cb_type = type; 2233789Sahrens 2234789Sahrens /* check options */ 2235789Sahrens while ((c = getopt(argc, argv, type == OP_MOUNT ? ":ao:O" : "a")) 2236789Sahrens != -1) { 2237789Sahrens switch (c) { 2238789Sahrens case 'a': 2239789Sahrens do_all = 1; 2240789Sahrens break; 2241789Sahrens case 'o': 2242789Sahrens cb.cb_options = optarg; 2243789Sahrens break; 2244789Sahrens case 'O': 2245789Sahrens cb.cb_flags |= MS_OVERLAY; 2246789Sahrens break; 2247789Sahrens case ':': 2248789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2249789Sahrens "'%c' option\n"), optopt); 22502082Seschrock usage(B_FALSE); 2251789Sahrens break; 2252789Sahrens case '?': 2253789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2254789Sahrens optopt); 22552082Seschrock usage(B_FALSE); 2256789Sahrens } 2257789Sahrens } 2258789Sahrens 2259789Sahrens argc -= optind; 2260789Sahrens argv += optind; 2261789Sahrens 2262789Sahrens /* check number of arguments */ 2263789Sahrens if (do_all) { 22641356Seschrock zfs_handle_t **fslist = NULL; 22651356Seschrock size_t i, count = 0; 22661356Seschrock 2267789Sahrens if (argc != 0) { 2268789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 22692082Seschrock usage(B_FALSE); 2270789Sahrens } 2271789Sahrens 22721356Seschrock get_all_filesystems(&fslist, &count); 22731356Seschrock 22741356Seschrock if (count == 0) 22751356Seschrock return (0); 22761356Seschrock 22771356Seschrock qsort(fslist, count, sizeof (void *), mountpoint_compare); 22781356Seschrock 22791356Seschrock for (i = 0; i < count; i++) { 22802369Slling if (share_mount_callback(fslist[i], &cb) != 0) 22812369Slling ret = 1; 22821356Seschrock } 22831356Seschrock 22841356Seschrock for (i = 0; i < count; i++) 22851356Seschrock zfs_close(fslist[i]); 22861356Seschrock 22871356Seschrock free(fslist); 2288789Sahrens } else if (argc == 0) { 2289789Sahrens struct mnttab entry; 2290789Sahrens 2291789Sahrens if (type == OP_SHARE) { 2292789Sahrens (void) fprintf(stderr, gettext("missing filesystem " 2293789Sahrens "argument\n")); 22942082Seschrock usage(B_FALSE); 2295789Sahrens } 2296789Sahrens 2297789Sahrens /* 2298789Sahrens * When mount is given no arguments, go through /etc/mnttab and 2299789Sahrens * display any active ZFS mounts. We hide any snapshots, since 2300789Sahrens * they are controlled automatically. 2301789Sahrens */ 2302789Sahrens rewind(mnttab_file); 2303789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2304789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 2305789Sahrens strchr(entry.mnt_special, '@') != NULL) 2306789Sahrens continue; 2307789Sahrens 2308789Sahrens (void) printf("%-30s %s\n", entry.mnt_special, 2309789Sahrens entry.mnt_mountp); 2310789Sahrens } 2311789Sahrens 2312789Sahrens } else { 2313789Sahrens zfs_handle_t *zhp; 2314789Sahrens 2315789Sahrens if (argc > 1) { 2316789Sahrens (void) fprintf(stderr, 2317789Sahrens gettext("too many arguments\n")); 23182082Seschrock usage(B_FALSE); 2319789Sahrens } 2320789Sahrens 23212082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], 23222082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2323789Sahrens ret = 1; 2324789Sahrens else { 23252082Seschrock cb.cb_explicit = B_TRUE; 2326789Sahrens ret = share_mount_callback(zhp, &cb); 2327789Sahrens zfs_close(zhp); 2328789Sahrens } 2329789Sahrens } 2330789Sahrens 2331789Sahrens return (ret); 2332789Sahrens } 2333789Sahrens 2334789Sahrens /* 2335789Sahrens * zfs mount -a 2336789Sahrens * zfs mount filesystem 2337789Sahrens * 2338789Sahrens * Mount all filesystems, or mount the given filesystem. 2339789Sahrens */ 2340789Sahrens static int 2341789Sahrens zfs_do_mount(int argc, char **argv) 2342789Sahrens { 2343789Sahrens return (share_or_mount(OP_MOUNT, argc, argv)); 2344789Sahrens } 2345789Sahrens 2346789Sahrens /* 2347789Sahrens * zfs share -a 2348789Sahrens * zfs share filesystem 2349789Sahrens * 2350789Sahrens * Share all filesystems, or share the given filesystem. 2351789Sahrens */ 2352789Sahrens static int 2353789Sahrens zfs_do_share(int argc, char **argv) 2354789Sahrens { 2355789Sahrens return (share_or_mount(OP_SHARE, argc, argv)); 2356789Sahrens } 2357789Sahrens 2358789Sahrens typedef struct unshare_unmount_node { 2359789Sahrens zfs_handle_t *un_zhp; 2360789Sahrens char *un_mountp; 2361789Sahrens uu_avl_node_t un_avlnode; 2362789Sahrens } unshare_unmount_node_t; 2363789Sahrens 2364789Sahrens /* ARGSUSED */ 2365789Sahrens static int 2366789Sahrens unshare_unmount_compare(const void *larg, const void *rarg, void *unused) 2367789Sahrens { 2368789Sahrens const unshare_unmount_node_t *l = larg; 2369789Sahrens const unshare_unmount_node_t *r = rarg; 2370789Sahrens 2371789Sahrens return (strcmp(l->un_mountp, r->un_mountp)); 2372789Sahrens } 2373789Sahrens 2374789Sahrens /* 2375789Sahrens * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 2376789Sahrens * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 2377789Sahrens * and unmount it appropriately. 2378789Sahrens */ 2379789Sahrens static int 23802082Seschrock unshare_unmount_path(int type, char *path, int flags, boolean_t is_manual) 2381789Sahrens { 2382789Sahrens zfs_handle_t *zhp; 2383789Sahrens int ret; 2384789Sahrens struct stat64 statbuf; 2385789Sahrens struct extmnttab entry; 2386789Sahrens const char *cmdname = (type == OP_SHARE) ? "unshare" : "unmount"; 2387789Sahrens char property[ZFS_MAXPROPLEN]; 2388789Sahrens 2389789Sahrens /* 2390789Sahrens * Search for the path in /etc/mnttab. Rather than looking for the 2391789Sahrens * specific path, which can be fooled by non-standard paths (i.e. ".." 2392789Sahrens * or "//"), we stat() the path and search for the corresponding 2393789Sahrens * (major,minor) device pair. 2394789Sahrens */ 2395789Sahrens if (stat64(path, &statbuf) != 0) { 2396789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 2397789Sahrens cmdname, path, strerror(errno)); 2398789Sahrens return (1); 2399789Sahrens } 2400789Sahrens 2401789Sahrens /* 2402789Sahrens * Search for the given (major,minor) pair in the mount table. 2403789Sahrens */ 2404789Sahrens rewind(mnttab_file); 2405789Sahrens while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 2406789Sahrens if (entry.mnt_major == major(statbuf.st_dev) && 2407789Sahrens entry.mnt_minor == minor(statbuf.st_dev)) 2408789Sahrens break; 2409789Sahrens } 2410789Sahrens if (ret != 0) { 2411789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not " 2412789Sahrens "currently mounted\n"), cmdname, path); 2413789Sahrens return (1); 2414789Sahrens } 2415789Sahrens 2416789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 2417789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 2418789Sahrens "filesystem\n"), cmdname, path); 2419789Sahrens return (1); 2420789Sahrens } 2421789Sahrens 24222082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 24232082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2424789Sahrens return (1); 2425789Sahrens 2426789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2427789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 24282082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2429789Sahrens 2430789Sahrens if (type == OP_SHARE) { 2431789Sahrens if (strcmp(property, "off") == 0) { 2432789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2433789Sahrens "'%s': legacy share\n"), path); 2434789Sahrens (void) fprintf(stderr, gettext("use " 2435789Sahrens "unshare(1M) to unshare this filesystem\n")); 2436789Sahrens ret = 1; 2437789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2438789Sahrens (void) fprintf(stderr, gettext("cannot unshare '%s': " 2439789Sahrens "not currently shared\n"), path); 2440789Sahrens ret = 1; 2441789Sahrens } else { 2442789Sahrens ret = zfs_unshareall(zhp); 2443789Sahrens } 2444789Sahrens } else { 24451264Slling if (is_manual) { 24461264Slling ret = zfs_unmount(zhp, NULL, flags); 24471264Slling } else if (strcmp(property, "legacy") == 0) { 24481264Slling (void) fprintf(stderr, gettext("cannot unmount " 24491264Slling "'%s': legacy mountpoint\n"), 24501264Slling zfs_get_name(zhp)); 24511264Slling (void) fprintf(stderr, gettext("use umount(1M) " 24521264Slling "to unmount this filesystem\n")); 24531264Slling ret = 1; 2454789Sahrens } else { 2455789Sahrens ret = zfs_unmountall(zhp, flags); 2456789Sahrens } 2457789Sahrens } 2458789Sahrens 2459789Sahrens zfs_close(zhp); 2460789Sahrens 2461789Sahrens return (ret != 0); 2462789Sahrens } 2463789Sahrens 2464789Sahrens /* 2465789Sahrens * Generic callback for unsharing or unmounting a filesystem. 2466789Sahrens */ 2467789Sahrens static int 2468789Sahrens unshare_unmount(int type, int argc, char **argv) 2469789Sahrens { 2470789Sahrens int do_all = 0; 2471789Sahrens int flags = 0; 2472789Sahrens int ret = 0; 2473789Sahrens int c; 2474789Sahrens zfs_handle_t *zhp; 2475789Sahrens char property[ZFS_MAXPROPLEN]; 2476789Sahrens 2477789Sahrens /* check options */ 2478789Sahrens while ((c = getopt(argc, argv, type == OP_SHARE ? "a" : "af")) != -1) { 2479789Sahrens switch (c) { 2480789Sahrens case 'a': 2481789Sahrens do_all = 1; 2482789Sahrens break; 2483789Sahrens case 'f': 2484789Sahrens flags = MS_FORCE; 2485789Sahrens break; 2486789Sahrens case '?': 2487789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2488789Sahrens optopt); 24892082Seschrock usage(B_FALSE); 2490789Sahrens } 2491789Sahrens } 2492789Sahrens 2493789Sahrens argc -= optind; 2494789Sahrens argv += optind; 2495789Sahrens 2496789Sahrens /* ensure correct number of arguments */ 2497789Sahrens if (do_all) { 2498789Sahrens if (argc != 0) { 2499789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 25002082Seschrock usage(B_FALSE); 2501789Sahrens } 2502789Sahrens } else if (argc != 1) { 2503789Sahrens if (argc == 0) 2504789Sahrens (void) fprintf(stderr, 2505789Sahrens gettext("missing filesystem argument\n")); 2506789Sahrens else 2507789Sahrens (void) fprintf(stderr, 2508789Sahrens gettext("too many arguments\n")); 25092082Seschrock usage(B_FALSE); 2510789Sahrens } 2511789Sahrens 2512789Sahrens if (do_all) { 2513789Sahrens /* 2514789Sahrens * We could make use of zfs_for_each() to walk all datasets in 2515789Sahrens * the system, but this would be very inefficient, especially 2516789Sahrens * since we would have to linearly search /etc/mnttab for each 2517789Sahrens * one. Instead, do one pass through /etc/mnttab looking for 2518789Sahrens * zfs entries and call zfs_unmount() for each one. 2519789Sahrens * 2520789Sahrens * Things get a little tricky if the administrator has created 2521789Sahrens * mountpoints beneath other ZFS filesystems. In this case, we 2522789Sahrens * have to unmount the deepest filesystems first. To accomplish 2523789Sahrens * this, we place all the mountpoints in an AVL tree sorted by 2524789Sahrens * the special type (dataset name), and walk the result in 2525789Sahrens * reverse to make sure to get any snapshots first. 2526789Sahrens */ 2527789Sahrens struct mnttab entry; 2528789Sahrens uu_avl_pool_t *pool; 2529789Sahrens uu_avl_t *tree; 2530789Sahrens unshare_unmount_node_t *node; 2531789Sahrens uu_avl_index_t idx; 2532789Sahrens uu_avl_walk_t *walk; 2533789Sahrens 2534789Sahrens if ((pool = uu_avl_pool_create("unmount_pool", 2535789Sahrens sizeof (unshare_unmount_node_t), 2536789Sahrens offsetof(unshare_unmount_node_t, un_avlnode), 2537789Sahrens unshare_unmount_compare, 2538789Sahrens UU_DEFAULT)) == NULL) { 2539789Sahrens (void) fprintf(stderr, gettext("internal error: " 2540789Sahrens "out of memory\n")); 2541789Sahrens exit(1); 2542789Sahrens } 2543789Sahrens 2544789Sahrens if ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL) { 2545789Sahrens (void) fprintf(stderr, gettext("internal error: " 2546789Sahrens "out of memory\n")); 2547789Sahrens exit(1); 2548789Sahrens } 2549789Sahrens 2550789Sahrens rewind(mnttab_file); 2551789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2552789Sahrens 2553789Sahrens /* ignore non-ZFS entries */ 2554789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 2555789Sahrens continue; 2556789Sahrens 2557789Sahrens /* ignore snapshots */ 2558789Sahrens if (strchr(entry.mnt_special, '@') != NULL) 2559789Sahrens continue; 2560789Sahrens 25612082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 2562789Sahrens ZFS_TYPE_FILESYSTEM)) == NULL) { 2563789Sahrens ret = 1; 2564789Sahrens continue; 2565789Sahrens } 2566789Sahrens 2567789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2568789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 2569789Sahrens property, sizeof (property), NULL, NULL, 25702082Seschrock 0, B_FALSE) == 0); 2571789Sahrens 2572789Sahrens /* Ignore legacy mounts and shares */ 2573789Sahrens if ((type == OP_SHARE && 2574789Sahrens strcmp(property, "off") == 0) || 2575789Sahrens (type == OP_MOUNT && 2576789Sahrens strcmp(property, "legacy") == 0)) { 2577789Sahrens zfs_close(zhp); 2578789Sahrens continue; 2579789Sahrens } 2580789Sahrens 2581789Sahrens node = safe_malloc(sizeof (unshare_unmount_node_t)); 2582789Sahrens node->un_zhp = zhp; 2583789Sahrens 2584789Sahrens if ((node->un_mountp = strdup(entry.mnt_mountp)) == 2585789Sahrens NULL) { 2586789Sahrens (void) fprintf(stderr, gettext("internal error:" 2587789Sahrens " out of memory\n")); 2588789Sahrens exit(1); 2589789Sahrens } 2590789Sahrens 2591789Sahrens uu_avl_node_init(node, &node->un_avlnode, pool); 2592789Sahrens 2593789Sahrens if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 2594789Sahrens uu_avl_insert(tree, node, idx); 2595789Sahrens } else { 2596789Sahrens zfs_close(node->un_zhp); 2597789Sahrens free(node->un_mountp); 2598789Sahrens free(node); 2599789Sahrens } 2600789Sahrens } 2601789Sahrens 2602789Sahrens /* 2603789Sahrens * Walk the AVL tree in reverse, unmounting each filesystem and 2604789Sahrens * removing it from the AVL tree in the process. 2605789Sahrens */ 2606789Sahrens if ((walk = uu_avl_walk_start(tree, 2607789Sahrens UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) { 2608789Sahrens (void) fprintf(stderr, 2609789Sahrens gettext("internal error: out of memory")); 2610789Sahrens exit(1); 2611789Sahrens } 2612789Sahrens 2613789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 2614789Sahrens uu_avl_remove(tree, node); 2615789Sahrens 2616789Sahrens switch (type) { 2617789Sahrens case OP_SHARE: 2618789Sahrens if (zfs_unshare(node->un_zhp, 2619789Sahrens node->un_mountp) != 0) 2620789Sahrens ret = 1; 2621789Sahrens break; 2622789Sahrens 2623789Sahrens case OP_MOUNT: 2624789Sahrens if (zfs_unmount(node->un_zhp, 2625789Sahrens node->un_mountp, flags) != 0) 2626789Sahrens ret = 1; 2627789Sahrens break; 2628789Sahrens } 2629789Sahrens 2630789Sahrens zfs_close(node->un_zhp); 2631789Sahrens free(node->un_mountp); 2632789Sahrens free(node); 2633789Sahrens } 2634789Sahrens 2635789Sahrens uu_avl_walk_end(walk); 2636789Sahrens uu_avl_destroy(tree); 2637789Sahrens uu_avl_pool_destroy(pool); 2638789Sahrens } else { 2639789Sahrens /* 2640789Sahrens * We have an argument, but it may be a full path or a ZFS 2641789Sahrens * filesystem. Pass full paths off to unmount_path() (shared by 2642789Sahrens * manual_unmount), otherwise open the filesystem and pass to 2643789Sahrens * zfs_unmount(). 2644789Sahrens */ 2645789Sahrens if (argv[0][0] == '/') 2646789Sahrens return (unshare_unmount_path(type, argv[0], 26472082Seschrock flags, B_FALSE)); 26482082Seschrock 26492082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], 26502082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2651789Sahrens return (1); 2652789Sahrens 2653789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2654789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 26552082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2656789Sahrens 2657789Sahrens switch (type) { 2658789Sahrens case OP_SHARE: 2659789Sahrens if (strcmp(property, "off") == 0) { 2660789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2661789Sahrens "'%s': legacy share\n"), zfs_get_name(zhp)); 2662789Sahrens (void) fprintf(stderr, gettext("use unshare(1M)" 2663789Sahrens " to unshare this filesystem\n")); 2664789Sahrens ret = 1; 2665789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2666789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2667789Sahrens "'%s': not currently shared\n"), 2668789Sahrens zfs_get_name(zhp)); 2669789Sahrens ret = 1; 2670789Sahrens } else if (zfs_unshareall(zhp) != 0) { 2671789Sahrens ret = 1; 2672789Sahrens } 2673789Sahrens break; 2674789Sahrens 2675789Sahrens case OP_MOUNT: 2676789Sahrens if (strcmp(property, "legacy") == 0) { 2677789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2678789Sahrens "'%s': legacy mountpoint\n"), 2679789Sahrens zfs_get_name(zhp)); 2680789Sahrens (void) fprintf(stderr, gettext("use umount(1M) " 2681789Sahrens "to unmount this filesystem\n")); 2682789Sahrens ret = 1; 2683789Sahrens } else if (!zfs_is_mounted(zhp, NULL)) { 2684789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2685789Sahrens "'%s': not currently mounted\n"), 2686789Sahrens zfs_get_name(zhp)); 2687789Sahrens ret = 1; 2688789Sahrens } else if (zfs_unmountall(zhp, flags) != 0) { 2689789Sahrens ret = 1; 2690789Sahrens } 2691789Sahrens } 2692789Sahrens 2693789Sahrens zfs_close(zhp); 2694789Sahrens } 2695789Sahrens 2696789Sahrens return (ret); 2697789Sahrens } 2698789Sahrens 2699789Sahrens /* 2700789Sahrens * zfs unmount -a 2701789Sahrens * zfs unmount filesystem 2702789Sahrens * 2703789Sahrens * Unmount all filesystems, or a specific ZFS filesystem. 2704789Sahrens */ 2705789Sahrens static int 2706789Sahrens zfs_do_unmount(int argc, char **argv) 2707789Sahrens { 2708789Sahrens return (unshare_unmount(OP_MOUNT, argc, argv)); 2709789Sahrens } 2710789Sahrens 2711789Sahrens /* 2712789Sahrens * zfs unshare -a 2713789Sahrens * zfs unshare filesystem 2714789Sahrens * 2715789Sahrens * Unshare all filesystems, or a specific ZFS filesystem. 2716789Sahrens */ 2717789Sahrens static int 2718789Sahrens zfs_do_unshare(int argc, char **argv) 2719789Sahrens { 2720789Sahrens return (unshare_unmount(OP_SHARE, argc, argv)); 2721789Sahrens } 2722789Sahrens 2723789Sahrens /* 2724789Sahrens * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 2725789Sahrens * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 2726789Sahrens */ 2727789Sahrens static int 2728789Sahrens manual_mount(int argc, char **argv) 2729789Sahrens { 2730789Sahrens zfs_handle_t *zhp; 2731789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2732789Sahrens char mntopts[MNT_LINE_MAX] = { '\0' }; 2733789Sahrens int ret; 2734789Sahrens int c; 2735789Sahrens int flags = 0; 2736789Sahrens char *dataset, *path; 2737789Sahrens 2738789Sahrens /* check options */ 27391544Seschrock while ((c = getopt(argc, argv, ":mo:O")) != -1) { 2740789Sahrens switch (c) { 2741789Sahrens case 'o': 2742789Sahrens (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 2743789Sahrens break; 2744789Sahrens case 'O': 2745789Sahrens flags |= MS_OVERLAY; 2746789Sahrens break; 27471544Seschrock case 'm': 27481544Seschrock flags |= MS_NOMNTTAB; 27491544Seschrock break; 2750789Sahrens case ':': 2751789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2752789Sahrens "'%c' option\n"), optopt); 27532082Seschrock usage(B_FALSE); 2754789Sahrens break; 2755789Sahrens case '?': 2756789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2757789Sahrens optopt); 2758789Sahrens (void) fprintf(stderr, gettext("usage: mount [-o opts] " 2759789Sahrens "<path>\n")); 2760789Sahrens return (2); 2761789Sahrens } 2762789Sahrens } 2763789Sahrens 2764789Sahrens argc -= optind; 2765789Sahrens argv += optind; 2766789Sahrens 2767789Sahrens /* check that we only have two arguments */ 2768789Sahrens if (argc != 2) { 2769789Sahrens if (argc == 0) 2770789Sahrens (void) fprintf(stderr, gettext("missing dataset " 2771789Sahrens "argument\n")); 2772789Sahrens else if (argc == 1) 2773789Sahrens (void) fprintf(stderr, 2774789Sahrens gettext("missing mountpoint argument\n")); 2775789Sahrens else 2776789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2777789Sahrens (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 2778789Sahrens return (2); 2779789Sahrens } 2780789Sahrens 2781789Sahrens dataset = argv[0]; 2782789Sahrens path = argv[1]; 2783789Sahrens 2784789Sahrens /* try to open the dataset */ 27852082Seschrock if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 2786789Sahrens return (1); 2787789Sahrens 2788789Sahrens (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 27892082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE); 2790789Sahrens 2791789Sahrens /* check for legacy mountpoint and complain appropriately */ 2792789Sahrens ret = 0; 2793789Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 2794789Sahrens if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS, 2795789Sahrens NULL, 0, mntopts, sizeof (mntopts)) != 0) { 2796789Sahrens (void) fprintf(stderr, gettext("mount failed: %s\n"), 2797789Sahrens strerror(errno)); 2798789Sahrens ret = 1; 2799789Sahrens } 2800789Sahrens } else { 2801789Sahrens (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 2802789Sahrens "mounted using 'mount -F zfs'\n"), dataset); 2803789Sahrens (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 2804789Sahrens "instead.\n"), path); 2805789Sahrens (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' " 2806789Sahrens "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n")); 2807789Sahrens (void) fprintf(stderr, gettext("See zfs(1M) for more " 2808789Sahrens "information.\n")); 2809789Sahrens ret = 1; 2810789Sahrens } 2811789Sahrens 2812789Sahrens return (ret); 2813789Sahrens } 2814789Sahrens 2815789Sahrens /* 2816789Sahrens * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 2817789Sahrens * unmounts of non-legacy filesystems, as this is the dominant administrative 2818789Sahrens * interface. 2819789Sahrens */ 2820789Sahrens static int 2821789Sahrens manual_unmount(int argc, char **argv) 2822789Sahrens { 2823789Sahrens int flags = 0; 2824789Sahrens int c; 2825789Sahrens 2826789Sahrens /* check options */ 2827789Sahrens while ((c = getopt(argc, argv, "f")) != -1) { 2828789Sahrens switch (c) { 2829789Sahrens case 'f': 2830789Sahrens flags = MS_FORCE; 2831789Sahrens break; 2832789Sahrens case '?': 2833789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2834789Sahrens optopt); 2835789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] " 2836789Sahrens "<path>\n")); 2837789Sahrens return (2); 2838789Sahrens } 2839789Sahrens } 2840789Sahrens 2841789Sahrens argc -= optind; 2842789Sahrens argv += optind; 2843789Sahrens 2844789Sahrens /* check arguments */ 2845789Sahrens if (argc != 1) { 2846789Sahrens if (argc == 0) 2847789Sahrens (void) fprintf(stderr, gettext("missing path " 2848789Sahrens "argument\n")); 2849789Sahrens else 2850789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2851789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 2852789Sahrens return (2); 2853789Sahrens } 2854789Sahrens 28552082Seschrock return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE)); 2856789Sahrens } 2857789Sahrens 2858789Sahrens static int 2859789Sahrens volcheck(zpool_handle_t *zhp, void *data) 2860789Sahrens { 2861789Sahrens int isinit = (int)data; 2862789Sahrens 2863789Sahrens if (isinit) 2864789Sahrens return (zpool_create_zvol_links(zhp)); 2865789Sahrens else 2866789Sahrens return (zpool_remove_zvol_links(zhp)); 2867789Sahrens } 2868789Sahrens 2869789Sahrens /* 2870789Sahrens * Iterate over all pools in the system and either create or destroy /dev/zvol 2871789Sahrens * links, depending on the value of 'isinit'. 2872789Sahrens */ 2873789Sahrens static int 28742082Seschrock do_volcheck(boolean_t isinit) 2875789Sahrens { 28762082Seschrock return (zpool_iter(g_zfs, volcheck, (void *)isinit) ? 1 : 0); 2877789Sahrens } 2878789Sahrens 2879789Sahrens int 2880789Sahrens main(int argc, char **argv) 2881789Sahrens { 2882789Sahrens int ret; 2883789Sahrens int i; 2884789Sahrens char *progname; 2885789Sahrens char *cmdname; 2886789Sahrens 2887789Sahrens (void) setlocale(LC_ALL, ""); 2888789Sahrens (void) textdomain(TEXT_DOMAIN); 2889789Sahrens 2890789Sahrens opterr = 0; 2891789Sahrens 28922082Seschrock if ((g_zfs = libzfs_init()) == NULL) { 28932082Seschrock (void) fprintf(stderr, gettext("internal error: failed to " 28942082Seschrock "initialize ZFS library\n")); 28952082Seschrock return (1); 28962082Seschrock } 28972082Seschrock 28982082Seschrock libzfs_print_on_error(g_zfs, B_TRUE); 28992082Seschrock 2900789Sahrens if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 2901789Sahrens (void) fprintf(stderr, gettext("internal error: unable to " 2902789Sahrens "open %s\n"), MNTTAB); 2903789Sahrens return (1); 2904789Sahrens } 2905789Sahrens 2906789Sahrens /* 2907789Sahrens * This command also doubles as the /etc/fs mount and unmount program. 2908789Sahrens * Determine if we should take this behavior based on argv[0]. 2909789Sahrens */ 2910789Sahrens progname = basename(argv[0]); 2911789Sahrens if (strcmp(progname, "mount") == 0) { 2912789Sahrens ret = manual_mount(argc, argv); 2913789Sahrens } else if (strcmp(progname, "umount") == 0) { 2914789Sahrens ret = manual_unmount(argc, argv); 2915789Sahrens } else { 2916789Sahrens /* 2917789Sahrens * Make sure the user has specified some command. 2918789Sahrens */ 2919789Sahrens if (argc < 2) { 2920789Sahrens (void) fprintf(stderr, gettext("missing command\n")); 29212082Seschrock usage(B_FALSE); 2922789Sahrens } 2923789Sahrens 2924789Sahrens cmdname = argv[1]; 2925789Sahrens 2926789Sahrens /* 2927789Sahrens * The 'umount' command is an alias for 'unmount' 2928789Sahrens */ 2929789Sahrens if (strcmp(cmdname, "umount") == 0) 2930789Sahrens cmdname = "unmount"; 2931789Sahrens 2932789Sahrens /* 29331749Sahrens * The 'recv' command is an alias for 'receive' 29341749Sahrens */ 29351749Sahrens if (strcmp(cmdname, "recv") == 0) 29361749Sahrens cmdname = "receive"; 29371749Sahrens 29381749Sahrens /* 2939789Sahrens * Special case '-?' 2940789Sahrens */ 2941789Sahrens if (strcmp(cmdname, "-?") == 0) 29422082Seschrock usage(B_TRUE); 2943789Sahrens 2944789Sahrens /* 2945789Sahrens * 'volinit' and 'volfini' do not appear in the usage message, 2946789Sahrens * so we have to special case them here. 2947789Sahrens */ 2948789Sahrens if (strcmp(cmdname, "volinit") == 0) 29492082Seschrock return (do_volcheck(B_TRUE)); 2950789Sahrens else if (strcmp(cmdname, "volfini") == 0) 29512082Seschrock return (do_volcheck(B_FALSE)); 2952789Sahrens 2953789Sahrens /* 2954789Sahrens * Run the appropriate command. 2955789Sahrens */ 2956789Sahrens for (i = 0; i < NCOMMAND; i++) { 2957789Sahrens if (command_table[i].name == NULL) 2958789Sahrens continue; 2959789Sahrens 2960789Sahrens if (strcmp(cmdname, command_table[i].name) == 0) { 2961789Sahrens current_command = &command_table[i]; 2962789Sahrens ret = command_table[i].func(argc - 1, argv + 1); 2963789Sahrens break; 2964789Sahrens } 2965789Sahrens } 2966789Sahrens 2967789Sahrens if (i == NCOMMAND) { 2968789Sahrens (void) fprintf(stderr, gettext("unrecognized " 2969789Sahrens "command '%s'\n"), cmdname); 29702082Seschrock usage(B_FALSE); 2971789Sahrens } 2972789Sahrens } 2973789Sahrens 2974789Sahrens (void) fclose(mnttab_file); 2975789Sahrens 29762082Seschrock libzfs_fini(g_zfs); 29772082Seschrock 2978789Sahrens /* 2979789Sahrens * The 'ZFS_ABORT' environment variable causes us to dump core on exit 2980789Sahrens * for the purposes of running ::findleaks. 2981789Sahrens */ 2982789Sahrens if (getenv("ZFS_ABORT") != NULL) { 2983789Sahrens (void) printf("dumping core by request\n"); 2984789Sahrens abort(); 2985789Sahrens } 2986789Sahrens 2987789Sahrens return (ret); 2988789Sahrens } 2989