1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 51544Seschrock * Common Development and Distribution License (the "License"). 61544Seschrock * You may not use this file except in compliance with the License. 7789Sahrens * 8789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9789Sahrens * or http://www.opensolaris.org/os/licensing. 10789Sahrens * See the License for the specific language governing permissions 11789Sahrens * and limitations under the License. 12789Sahrens * 13789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15789Sahrens * If applicable, add the following below this CDDL HEADER, with the 16789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18789Sahrens * 19789Sahrens * CDDL HEADER END 20789Sahrens */ 21789Sahrens /* 221204Slling * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23789Sahrens * Use is subject to license terms. 24789Sahrens */ 25789Sahrens 26789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 27789Sahrens 28789Sahrens #include <assert.h> 29789Sahrens #include <errno.h> 30789Sahrens #include <libgen.h> 31789Sahrens #include <libintl.h> 32789Sahrens #include <libuutil.h> 33789Sahrens #include <locale.h> 34789Sahrens #include <stddef.h> 35789Sahrens #include <stdio.h> 36789Sahrens #include <stdlib.h> 37789Sahrens #include <strings.h> 38789Sahrens #include <unistd.h> 39789Sahrens #include <fcntl.h> 40789Sahrens #include <zone.h> 41789Sahrens #include <sys/mkdev.h> 42789Sahrens #include <sys/mntent.h> 43789Sahrens #include <sys/mnttab.h> 44789Sahrens #include <sys/mount.h> 45789Sahrens #include <sys/stat.h> 46789Sahrens 47789Sahrens #include <libzfs.h> 48789Sahrens 49789Sahrens #include "zfs_iter.h" 502082Seschrock #include "zfs_util.h" 512082Seschrock 522082Seschrock libzfs_handle_t *g_zfs; 53789Sahrens 54789Sahrens static FILE *mnttab_file; 55789Sahrens 56789Sahrens static int zfs_do_clone(int argc, char **argv); 57789Sahrens static int zfs_do_create(int argc, char **argv); 58789Sahrens static int zfs_do_destroy(int argc, char **argv); 59789Sahrens static int zfs_do_get(int argc, char **argv); 60789Sahrens static int zfs_do_inherit(int argc, char **argv); 61789Sahrens static int zfs_do_list(int argc, char **argv); 62789Sahrens static int zfs_do_mount(int argc, char **argv); 63789Sahrens static int zfs_do_rename(int argc, char **argv); 64789Sahrens static int zfs_do_rollback(int argc, char **argv); 65789Sahrens static int zfs_do_set(int argc, char **argv); 66789Sahrens static int zfs_do_snapshot(int argc, char **argv); 67789Sahrens static int zfs_do_unmount(int argc, char **argv); 68789Sahrens static int zfs_do_share(int argc, char **argv); 69789Sahrens static int zfs_do_unshare(int argc, char **argv); 701749Sahrens static int zfs_do_send(int argc, char **argv); 711749Sahrens static int zfs_do_receive(int argc, char **argv); 722082Seschrock static int zfs_do_promote(int argc, char **argv); 73789Sahrens 74789Sahrens /* 75789Sahrens * These libumem hooks provide a reasonable set of defaults for the allocator's 76789Sahrens * debugging facilities. 77789Sahrens */ 78789Sahrens const char * 79789Sahrens _umem_debug_init() 80789Sahrens { 81789Sahrens return ("default,verbose"); /* $UMEM_DEBUG setting */ 82789Sahrens } 83789Sahrens 84789Sahrens const char * 85789Sahrens _umem_logging_init(void) 86789Sahrens { 87789Sahrens return ("fail,contents"); /* $UMEM_LOGGING setting */ 88789Sahrens } 89789Sahrens 901387Seschrock typedef enum { 911387Seschrock HELP_CLONE, 921387Seschrock HELP_CREATE, 931387Seschrock HELP_DESTROY, 941387Seschrock HELP_GET, 951387Seschrock HELP_INHERIT, 961387Seschrock HELP_LIST, 971387Seschrock HELP_MOUNT, 982082Seschrock HELP_PROMOTE, 991749Sahrens HELP_RECEIVE, 1001387Seschrock HELP_RENAME, 1011387Seschrock HELP_ROLLBACK, 1021749Sahrens HELP_SEND, 1031387Seschrock HELP_SET, 1041387Seschrock HELP_SHARE, 1051387Seschrock HELP_SNAPSHOT, 1061387Seschrock HELP_UNMOUNT, 1071387Seschrock HELP_UNSHARE 1081387Seschrock } zfs_help_t; 1091387Seschrock 110789Sahrens typedef struct zfs_command { 111789Sahrens const char *name; 112789Sahrens int (*func)(int argc, char **argv); 1131387Seschrock zfs_help_t usage; 114789Sahrens } zfs_command_t; 115789Sahrens 116789Sahrens /* 117789Sahrens * Master command table. Each ZFS command has a name, associated function, and 1181544Seschrock * usage message. The usage messages need to be internationalized, so we have 1191544Seschrock * to have a function to return the usage message based on a command index. 1201387Seschrock * 1211387Seschrock * These commands are organized according to how they are displayed in the usage 1221387Seschrock * message. An empty command (one with a NULL name) indicates an empty line in 1231387Seschrock * the generic usage message. 124789Sahrens */ 125789Sahrens static zfs_command_t command_table[] = { 1261387Seschrock { "create", zfs_do_create, HELP_CREATE }, 1271387Seschrock { "destroy", zfs_do_destroy, HELP_DESTROY }, 128789Sahrens { NULL }, 1291387Seschrock { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT }, 1301387Seschrock { "rollback", zfs_do_rollback, HELP_ROLLBACK }, 1311387Seschrock { "clone", zfs_do_clone, HELP_CLONE }, 1322082Seschrock { "promote", zfs_do_promote, HELP_PROMOTE }, 1331387Seschrock { "rename", zfs_do_rename, HELP_RENAME }, 134789Sahrens { NULL }, 1351387Seschrock { "list", zfs_do_list, HELP_LIST }, 136789Sahrens { NULL }, 1371387Seschrock { "set", zfs_do_set, HELP_SET }, 1381387Seschrock { "get", zfs_do_get, HELP_GET }, 1391387Seschrock { "inherit", zfs_do_inherit, HELP_INHERIT }, 140789Sahrens { NULL }, 1411387Seschrock { "mount", zfs_do_mount, HELP_MOUNT }, 142789Sahrens { NULL }, 1431387Seschrock { "unmount", zfs_do_unmount, HELP_UNMOUNT }, 144789Sahrens { NULL }, 1451387Seschrock { "share", zfs_do_share, HELP_SHARE }, 146789Sahrens { NULL }, 1471387Seschrock { "unshare", zfs_do_unshare, HELP_UNSHARE }, 148789Sahrens { NULL }, 1491749Sahrens { "send", zfs_do_send, HELP_SEND }, 1501749Sahrens { "receive", zfs_do_receive, HELP_RECEIVE }, 151789Sahrens }; 152789Sahrens 153789Sahrens #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) 154789Sahrens 155789Sahrens zfs_command_t *current_command; 156789Sahrens 1571387Seschrock static const char * 1581387Seschrock get_usage(zfs_help_t idx) 1591387Seschrock { 1601387Seschrock switch (idx) { 1611387Seschrock case HELP_CLONE: 1621387Seschrock return (gettext("\tclone <snapshot> <filesystem|volume>\n")); 1631387Seschrock case HELP_CREATE: 1641387Seschrock return (gettext("\tcreate <filesystem>\n" 1651387Seschrock "\tcreate [-s] [-b blocksize] -V <size> <volume>\n")); 1661387Seschrock case HELP_DESTROY: 1671387Seschrock return (gettext("\tdestroy [-rRf] " 1681387Seschrock "<filesystem|volume|snapshot>\n")); 1691387Seschrock case HELP_GET: 1701387Seschrock return (gettext("\tget [-rHp] [-o field[,field]...] " 1711387Seschrock "[-s source[,source]...]\n" 1721387Seschrock "\t <all | property[,property]...> " 1731387Seschrock "<filesystem|volume|snapshot> ...\n")); 1741387Seschrock case HELP_INHERIT: 1751387Seschrock return (gettext("\tinherit [-r] <property> " 1761387Seschrock "<filesystem|volume> ...\n")); 1771387Seschrock case HELP_LIST: 1781387Seschrock return (gettext("\tlist [-rH] [-o property[,property]...] " 1791387Seschrock "[-t type[,type]...]\n" 1802379Ssjelinek "\t [-s property [-s property]...]" 1812379Ssjelinek " [-S property [-S property]...]\n" 1821387Seschrock "\t [filesystem|volume|snapshot] ...\n")); 1831387Seschrock case HELP_MOUNT: 1841387Seschrock return (gettext("\tmount\n" 1851387Seschrock "\tmount [-o opts] [-O] -a\n" 1861387Seschrock "\tmount [-o opts] [-O] <filesystem>\n")); 1872082Seschrock case HELP_PROMOTE: 1882082Seschrock return (gettext("\tpromote <clone filesystem>\n")); 1891749Sahrens case HELP_RECEIVE: 190*2665Snd150628 return (gettext("\treceive [-vnF] <filesystem|volume|" 191*2665Snd150628 "snapshot>\n" 192*2665Snd150628 "\treceive [-vnF] -d <filesystem>\n")); 1931387Seschrock case HELP_RENAME: 1941387Seschrock return (gettext("\trename <filesystem|volume|snapshot> " 1951387Seschrock "<filesystem|volume|snapshot>\n")); 1961387Seschrock case HELP_ROLLBACK: 1971387Seschrock return (gettext("\trollback [-rRf] <snapshot>\n")); 1981749Sahrens case HELP_SEND: 1991749Sahrens return (gettext("\tsend [-i <snapshot>] <snapshot>\n")); 2001387Seschrock case HELP_SET: 2011387Seschrock return (gettext("\tset <property=value> " 2021387Seschrock "<filesystem|volume> ...\n")); 2031387Seschrock case HELP_SHARE: 2041387Seschrock return (gettext("\tshare -a\n" 2051387Seschrock "\tshare <filesystem>\n")); 2061387Seschrock case HELP_SNAPSHOT: 2072199Sahrens return (gettext("\tsnapshot [-r] " 2082199Sahrens "<filesystem@name|volume@name>\n")); 2091387Seschrock case HELP_UNMOUNT: 2101387Seschrock return (gettext("\tunmount [-f] -a\n" 2111387Seschrock "\tunmount [-f] <filesystem|mountpoint>\n")); 2121387Seschrock case HELP_UNSHARE: 2131387Seschrock return (gettext("\tunshare [-f] -a\n" 2141387Seschrock "\tunshare [-f] <filesystem|mountpoint>\n")); 2151387Seschrock } 2161387Seschrock 2171387Seschrock abort(); 2181387Seschrock /* NOTREACHED */ 2191387Seschrock } 2201387Seschrock 221789Sahrens /* 222789Sahrens * Utility function to guarantee malloc() success. 223789Sahrens */ 224789Sahrens void * 225789Sahrens safe_malloc(size_t size) 226789Sahrens { 227789Sahrens void *data; 228789Sahrens 229789Sahrens if ((data = calloc(1, size)) == NULL) { 230789Sahrens (void) fprintf(stderr, "internal error: out of memory\n"); 231789Sahrens exit(1); 232789Sahrens } 233789Sahrens 234789Sahrens return (data); 235789Sahrens } 236789Sahrens 237789Sahrens /* 238789Sahrens * Display usage message. If we're inside a command, display only the usage for 239789Sahrens * that command. Otherwise, iterate over the entire command table and display 240789Sahrens * a complete usage message. 241789Sahrens */ 242789Sahrens static void 2432082Seschrock usage(boolean_t requested) 244789Sahrens { 245789Sahrens int i; 2462082Seschrock boolean_t show_properties = B_FALSE; 247789Sahrens FILE *fp = requested ? stdout : stderr; 248789Sahrens 249789Sahrens if (current_command == NULL) { 250789Sahrens 251789Sahrens (void) fprintf(fp, gettext("usage: zfs command args ...\n")); 252789Sahrens (void) fprintf(fp, 253789Sahrens gettext("where 'command' is one of the following:\n\n")); 254789Sahrens 255789Sahrens for (i = 0; i < NCOMMAND; i++) { 256789Sahrens if (command_table[i].name == NULL) 257789Sahrens (void) fprintf(fp, "\n"); 258789Sahrens else 259789Sahrens (void) fprintf(fp, "%s", 2601387Seschrock get_usage(command_table[i].usage)); 261789Sahrens } 262789Sahrens 263789Sahrens (void) fprintf(fp, gettext("\nEach dataset is of the form: " 264789Sahrens "pool/[dataset/]*dataset[@name]\n")); 265789Sahrens } else { 266789Sahrens (void) fprintf(fp, gettext("usage:\n")); 2671387Seschrock (void) fprintf(fp, "%s", get_usage(current_command->usage)); 268789Sahrens } 269789Sahrens 2702190Sdarrenm if (current_command != NULL && 2712190Sdarrenm (strcmp(current_command->name, "set") == 0 || 272789Sahrens strcmp(current_command->name, "get") == 0 || 273789Sahrens strcmp(current_command->name, "inherit") == 0 || 2742190Sdarrenm strcmp(current_command->name, "list") == 0)) 2752082Seschrock show_properties = B_TRUE; 276789Sahrens 277789Sahrens if (show_properties) { 278789Sahrens 279789Sahrens (void) fprintf(fp, 280789Sahrens gettext("\nThe following properties are supported:\n")); 281789Sahrens 282789Sahrens (void) fprintf(fp, "\n\t%-13s %s %s %s\n\n", 283789Sahrens "PROPERTY", "EDIT", "INHERIT", "VALUES"); 284789Sahrens 285789Sahrens for (i = 0; i < ZFS_NPROP_VISIBLE; i++) { 286789Sahrens (void) fprintf(fp, "\t%-13s ", zfs_prop_to_name(i)); 287789Sahrens 288789Sahrens if (zfs_prop_readonly(i)) 289789Sahrens (void) fprintf(fp, " NO "); 290789Sahrens else 291789Sahrens (void) fprintf(fp, " YES "); 292789Sahrens 293789Sahrens if (zfs_prop_inheritable(i)) 294789Sahrens (void) fprintf(fp, " YES "); 295789Sahrens else 296789Sahrens (void) fprintf(fp, " NO "); 297789Sahrens 298789Sahrens if (zfs_prop_values(i) == NULL) 299789Sahrens (void) fprintf(fp, "-\n"); 300789Sahrens else 301789Sahrens (void) fprintf(fp, "%s\n", zfs_prop_values(i)); 302789Sahrens } 303789Sahrens (void) fprintf(fp, gettext("\nSizes are specified in bytes " 304789Sahrens "with standard units such as K, M, G, etc.\n")); 3052190Sdarrenm } else { 3062190Sdarrenm /* 3072190Sdarrenm * TRANSLATION NOTE: 3082190Sdarrenm * "zfs set|get" must not be localised this is the 3092190Sdarrenm * command name and arguments. 3102190Sdarrenm */ 3112190Sdarrenm (void) fprintf(fp, 3122190Sdarrenm gettext("\nFor the property list, run: zfs set|get\n")); 313789Sahrens } 314789Sahrens 315789Sahrens exit(requested ? 0 : 2); 316789Sahrens } 317789Sahrens 318789Sahrens /* 319789Sahrens * zfs clone <fs, snap, vol> fs 320789Sahrens * 321789Sahrens * Given an existing dataset, create a writable copy whose initial contents 322789Sahrens * are the same as the source. The newly created dataset maintains a 323789Sahrens * dependency on the original; the original cannot be destroyed so long as 324789Sahrens * the clone exists. 325789Sahrens */ 326789Sahrens static int 327789Sahrens zfs_do_clone(int argc, char **argv) 328789Sahrens { 329789Sahrens zfs_handle_t *zhp; 330789Sahrens int ret; 331789Sahrens 332789Sahrens /* check options */ 333789Sahrens if (argc > 1 && argv[1][0] == '-') { 334789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 335789Sahrens argv[1][1]); 3362082Seschrock usage(B_FALSE); 337789Sahrens } 338789Sahrens 339789Sahrens /* check number of arguments */ 340789Sahrens if (argc < 2) { 341789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 342789Sahrens "argument\n")); 3432082Seschrock usage(B_FALSE); 344789Sahrens } 345789Sahrens if (argc < 3) { 346789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 347789Sahrens "argument\n")); 3482082Seschrock usage(B_FALSE); 349789Sahrens } 350789Sahrens if (argc > 3) { 351789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 3522082Seschrock usage(B_FALSE); 353789Sahrens } 354789Sahrens 355789Sahrens /* open the source dataset */ 3562082Seschrock if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_SNAPSHOT)) == NULL) 357789Sahrens return (1); 358789Sahrens 359789Sahrens /* pass to libzfs */ 360789Sahrens ret = zfs_clone(zhp, argv[2]); 361789Sahrens 362789Sahrens /* create the mountpoint if necessary */ 363789Sahrens if (ret == 0) { 3642082Seschrock zfs_handle_t *clone = zfs_open(g_zfs, argv[2], ZFS_TYPE_ANY); 365789Sahrens if (clone != NULL) { 366789Sahrens if ((ret = zfs_mount(clone, NULL, 0)) == 0) 367789Sahrens ret = zfs_share(clone); 368789Sahrens zfs_close(clone); 369789Sahrens } 370789Sahrens } 371789Sahrens 372789Sahrens zfs_close(zhp); 373789Sahrens 374789Sahrens return (ret == 0 ? 0 : 1); 375789Sahrens } 376789Sahrens 377789Sahrens /* 378789Sahrens * zfs create fs 3792199Sahrens * zfs create [-s] [-b blocksize] -V vol size 380789Sahrens * 381789Sahrens * Create a new dataset. This command can be used to create filesystems 382789Sahrens * and volumes. Snapshot creation is handled by 'zfs snapshot'. 383789Sahrens * For volumes, the user must specify a size to be used. 384789Sahrens * 385789Sahrens * The '-s' flag applies only to volumes, and indicates that we should not try 386789Sahrens * to set the reservation for this volume. By default we set a reservation 387789Sahrens * equal to the size for any volume. 388789Sahrens */ 389789Sahrens static int 390789Sahrens zfs_do_create(int argc, char **argv) 391789Sahrens { 392789Sahrens zfs_type_t type = ZFS_TYPE_FILESYSTEM; 393789Sahrens zfs_handle_t *zhp; 394789Sahrens char *size = NULL; 395789Sahrens char *blocksize = NULL; 396789Sahrens int c; 3972082Seschrock boolean_t noreserve = B_FALSE; 398789Sahrens int ret; 399789Sahrens 400789Sahrens /* check options */ 401789Sahrens while ((c = getopt(argc, argv, ":V:b:s")) != -1) { 402789Sahrens switch (c) { 403789Sahrens case 'V': 404789Sahrens type = ZFS_TYPE_VOLUME; 405789Sahrens size = optarg; 406789Sahrens break; 407789Sahrens case 'b': 408789Sahrens blocksize = optarg; 409789Sahrens break; 410789Sahrens case 's': 4112082Seschrock noreserve = B_TRUE; 412789Sahrens break; 413789Sahrens case ':': 414789Sahrens (void) fprintf(stderr, gettext("missing size " 415789Sahrens "argument\n")); 4162082Seschrock usage(B_FALSE); 417789Sahrens break; 418789Sahrens case '?': 419789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 420789Sahrens optopt); 4212082Seschrock usage(B_FALSE); 422789Sahrens } 423789Sahrens } 424789Sahrens 425789Sahrens if (noreserve && type != ZFS_TYPE_VOLUME) { 426789Sahrens (void) fprintf(stderr, gettext("'-s' can only be used when " 427789Sahrens "creating a volume\n")); 4282082Seschrock usage(B_FALSE); 429789Sahrens } 430789Sahrens 431789Sahrens argc -= optind; 432789Sahrens argv += optind; 433789Sahrens 434789Sahrens /* check number of arguments */ 435789Sahrens if (argc == 0) { 436789Sahrens (void) fprintf(stderr, gettext("missing %s argument\n"), 437789Sahrens zfs_type_to_name(type)); 4382082Seschrock usage(B_FALSE); 439789Sahrens } 440789Sahrens if (argc > 1) { 441789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 4422082Seschrock usage(B_FALSE); 443789Sahrens } 444789Sahrens 445789Sahrens /* pass to libzfs */ 4462082Seschrock if (zfs_create(g_zfs, argv[0], type, size, blocksize) != 0) 447789Sahrens return (1); 448789Sahrens 4492082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 450789Sahrens return (1); 451789Sahrens 452789Sahrens /* 453789Sahrens * Volume handling. By default, we try to create a reservation of equal 454789Sahrens * size for the volume. If we can't do this, then destroy the dataset 455789Sahrens * and report an error. 456789Sahrens */ 457789Sahrens if (type == ZFS_TYPE_VOLUME && !noreserve) { 458789Sahrens if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, size) != 0) { 459789Sahrens (void) fprintf(stderr, gettext("use '-s' to create a " 460789Sahrens "volume without a matching reservation\n")); 461789Sahrens (void) zfs_destroy(zhp); 462789Sahrens return (1); 463789Sahrens } 464789Sahrens } 465789Sahrens 466789Sahrens /* 467789Sahrens * Mount and/or share the new filesystem as appropriate. We provide a 468789Sahrens * verbose error message to let the user know that their filesystem was 469789Sahrens * in fact created, even if we failed to mount or share it. 470789Sahrens */ 471789Sahrens if (zfs_mount(zhp, NULL, 0) != 0) { 472789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 473789Sahrens "created, but not mounted\n")); 474789Sahrens ret = 1; 475789Sahrens } else if (zfs_share(zhp) != 0) { 476789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 477789Sahrens "created, but not shared\n")); 478789Sahrens ret = 1; 479789Sahrens } else { 480789Sahrens ret = 0; 481789Sahrens } 482789Sahrens 483789Sahrens zfs_close(zhp); 484789Sahrens return (ret); 485789Sahrens } 486789Sahrens 487789Sahrens /* 488789Sahrens * zfs destroy [-rf] <fs, snap, vol> 489789Sahrens * 490789Sahrens * -r Recursively destroy all children 491789Sahrens * -R Recursively destroy all dependents, including clones 492789Sahrens * -f Force unmounting of any dependents 493789Sahrens * 494789Sahrens * Destroys the given dataset. By default, it will unmount any filesystems, 495789Sahrens * and refuse to destroy a dataset that has any dependents. A dependent can 496789Sahrens * either be a child, or a clone of a child. 497789Sahrens */ 498789Sahrens typedef struct destroy_cbdata { 4992082Seschrock boolean_t cb_first; 500789Sahrens int cb_force; 501789Sahrens int cb_recurse; 502789Sahrens int cb_error; 503789Sahrens int cb_needforce; 504789Sahrens int cb_doclones; 505789Sahrens zfs_handle_t *cb_target; 5062199Sahrens char *cb_snapname; 507789Sahrens } destroy_cbdata_t; 508789Sahrens 509789Sahrens /* 510789Sahrens * Check for any dependents based on the '-r' or '-R' flags. 511789Sahrens */ 512789Sahrens static int 513789Sahrens destroy_check_dependent(zfs_handle_t *zhp, void *data) 514789Sahrens { 515789Sahrens destroy_cbdata_t *cbp = data; 516789Sahrens const char *tname = zfs_get_name(cbp->cb_target); 517789Sahrens const char *name = zfs_get_name(zhp); 518789Sahrens 519789Sahrens if (strncmp(tname, name, strlen(tname)) == 0 && 520789Sahrens (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { 521789Sahrens /* 522789Sahrens * This is a direct descendant, not a clone somewhere else in 523789Sahrens * the hierarchy. 524789Sahrens */ 525789Sahrens if (cbp->cb_recurse) 526789Sahrens goto out; 527789Sahrens 528789Sahrens if (cbp->cb_first) { 529789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 530789Sahrens "%s has children\n"), 531789Sahrens zfs_get_name(cbp->cb_target), 532789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 533789Sahrens (void) fprintf(stderr, gettext("use '-r' to destroy " 534789Sahrens "the following datasets:\n")); 5352082Seschrock cbp->cb_first = B_FALSE; 536789Sahrens cbp->cb_error = 1; 537789Sahrens } 538789Sahrens 539789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 540789Sahrens } else { 541789Sahrens /* 542789Sahrens * This is a clone. We only want to report this if the '-r' 543789Sahrens * wasn't specified, or the target is a snapshot. 544789Sahrens */ 545789Sahrens if (!cbp->cb_recurse && 546789Sahrens zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) 547789Sahrens goto out; 548789Sahrens 549789Sahrens if (cbp->cb_first) { 550789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 551789Sahrens "%s has dependent clones\n"), 552789Sahrens zfs_get_name(cbp->cb_target), 553789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 554789Sahrens (void) fprintf(stderr, gettext("use '-R' to destroy " 555789Sahrens "the following datasets:\n")); 5562082Seschrock cbp->cb_first = B_FALSE; 557789Sahrens cbp->cb_error = 1; 558789Sahrens } 559789Sahrens 560789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 561789Sahrens } 562789Sahrens 563789Sahrens out: 564789Sahrens zfs_close(zhp); 565789Sahrens return (0); 566789Sahrens } 567789Sahrens 568789Sahrens static int 569789Sahrens destroy_callback(zfs_handle_t *zhp, void *data) 570789Sahrens { 571789Sahrens destroy_cbdata_t *cbp = data; 572789Sahrens 573789Sahrens /* 574789Sahrens * Ignore pools (which we've already flagged as an error before getting 575789Sahrens * here. 576789Sahrens */ 577789Sahrens if (strchr(zfs_get_name(zhp), '/') == NULL && 578789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 579789Sahrens zfs_close(zhp); 580789Sahrens return (0); 581789Sahrens } 582789Sahrens 583789Sahrens /* 584789Sahrens * Bail out on the first error. 585789Sahrens */ 586789Sahrens if (zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0 || 587789Sahrens zfs_destroy(zhp) != 0) { 588789Sahrens zfs_close(zhp); 589789Sahrens return (-1); 590789Sahrens } 591789Sahrens 592789Sahrens zfs_close(zhp); 593789Sahrens return (0); 594789Sahrens } 595789Sahrens 5962199Sahrens static int 5972199Sahrens destroy_snap_clones(zfs_handle_t *zhp, void *arg) 5982199Sahrens { 5992199Sahrens destroy_cbdata_t *cbp = arg; 6002199Sahrens char thissnap[MAXPATHLEN]; 6012199Sahrens zfs_handle_t *szhp; 6022199Sahrens 6032199Sahrens (void) snprintf(thissnap, sizeof (thissnap), 6042199Sahrens "%s@%s", zfs_get_name(zhp), cbp->cb_snapname); 6052199Sahrens 6062199Sahrens libzfs_print_on_error(g_zfs, B_FALSE); 6072199Sahrens szhp = zfs_open(g_zfs, thissnap, ZFS_TYPE_SNAPSHOT); 6082199Sahrens libzfs_print_on_error(g_zfs, B_TRUE); 6092199Sahrens if (szhp) { 6102199Sahrens /* 6112199Sahrens * Destroy any clones of this snapshot 6122199Sahrens */ 6132474Seschrock if (zfs_iter_dependents(szhp, B_FALSE, destroy_callback, 6142474Seschrock cbp) != 0) { 6152474Seschrock zfs_close(szhp); 6162474Seschrock return (-1); 6172474Seschrock } 6182199Sahrens zfs_close(szhp); 6192199Sahrens } 6202199Sahrens 6212199Sahrens return (zfs_iter_filesystems(zhp, destroy_snap_clones, arg)); 6222199Sahrens } 623789Sahrens 624789Sahrens static int 625789Sahrens zfs_do_destroy(int argc, char **argv) 626789Sahrens { 627789Sahrens destroy_cbdata_t cb = { 0 }; 628789Sahrens int c; 629789Sahrens zfs_handle_t *zhp; 6302199Sahrens char *cp; 631789Sahrens 632789Sahrens /* check options */ 633789Sahrens while ((c = getopt(argc, argv, "frR")) != -1) { 634789Sahrens switch (c) { 635789Sahrens case 'f': 636789Sahrens cb.cb_force = 1; 637789Sahrens break; 638789Sahrens case 'r': 639789Sahrens cb.cb_recurse = 1; 640789Sahrens break; 641789Sahrens case 'R': 642789Sahrens cb.cb_recurse = 1; 643789Sahrens cb.cb_doclones = 1; 644789Sahrens break; 645789Sahrens case '?': 646789Sahrens default: 647789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 648789Sahrens optopt); 6492082Seschrock usage(B_FALSE); 650789Sahrens } 651789Sahrens } 652789Sahrens 653789Sahrens argc -= optind; 654789Sahrens argv += optind; 655789Sahrens 656789Sahrens /* check number of arguments */ 657789Sahrens if (argc == 0) { 658789Sahrens (void) fprintf(stderr, gettext("missing path argument\n")); 6592082Seschrock usage(B_FALSE); 660789Sahrens } 661789Sahrens if (argc > 1) { 662789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 6632082Seschrock usage(B_FALSE); 664789Sahrens } 665789Sahrens 6662199Sahrens /* 6672199Sahrens * If we are doing recursive destroy of a snapshot, then the 6682199Sahrens * named snapshot may not exist. Go straight to libzfs. 6692199Sahrens */ 6702199Sahrens if (cb.cb_recurse && (cp = strchr(argv[0], '@'))) { 6712199Sahrens int ret; 6722199Sahrens 6732199Sahrens *cp = '\0'; 6742199Sahrens if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 6752199Sahrens return (1); 6762199Sahrens *cp = '@'; 6772199Sahrens cp++; 6782199Sahrens 6792199Sahrens if (cb.cb_doclones) { 6802199Sahrens cb.cb_snapname = cp; 6812474Seschrock if (destroy_snap_clones(zhp, &cb) != 0) { 6822474Seschrock zfs_close(zhp); 6832474Seschrock return (1); 6842474Seschrock } 6852199Sahrens } 6862199Sahrens 6872199Sahrens ret = zfs_destroy_snaps(zhp, cp); 6882199Sahrens zfs_close(zhp); 6892199Sahrens if (ret) { 6902199Sahrens (void) fprintf(stderr, 6912199Sahrens gettext("no snapshots destroyed\n")); 6922199Sahrens } 6932199Sahrens return (ret != 0); 6942199Sahrens } 6952199Sahrens 6962199Sahrens 697789Sahrens /* Open the given dataset */ 6982082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 699789Sahrens return (1); 700789Sahrens 701789Sahrens cb.cb_target = zhp; 702789Sahrens 703789Sahrens /* 704789Sahrens * Perform an explicit check for pools before going any further. 705789Sahrens */ 706789Sahrens if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 707789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 708789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 709789Sahrens "operation does not apply to pools\n"), 710789Sahrens zfs_get_name(zhp)); 711789Sahrens (void) fprintf(stderr, gettext("use 'zfs destroy -r " 712789Sahrens "%s' to destroy all datasets in the pool\n"), 713789Sahrens zfs_get_name(zhp)); 714789Sahrens (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 715789Sahrens "to destroy the pool itself\n"), zfs_get_name(zhp)); 716789Sahrens zfs_close(zhp); 717789Sahrens return (1); 718789Sahrens } 719789Sahrens 720789Sahrens /* 721789Sahrens * Check for any dependents and/or clones. 722789Sahrens */ 7232082Seschrock cb.cb_first = B_TRUE; 7242474Seschrock if (!cb.cb_doclones && 7252474Seschrock zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent, 7262474Seschrock &cb) != 0) { 7272474Seschrock zfs_close(zhp); 7282474Seschrock return (1); 7292474Seschrock } 7302474Seschrock 7312474Seschrock 7322474Seschrock if (cb.cb_error || 7332474Seschrock zfs_iter_dependents(zhp, B_FALSE, destroy_callback, &cb) != 0) { 734789Sahrens zfs_close(zhp); 735789Sahrens return (1); 736789Sahrens } 737789Sahrens 738789Sahrens /* 7392474Seschrock * Do the real thing. The callback will close the handle regardless of 7402474Seschrock * whether it succeeds or not. 741789Sahrens */ 7422474Seschrock if (destroy_callback(zhp, &cb) != 0) 7432474Seschrock return (1); 7442474Seschrock 7452474Seschrock return (0); 746789Sahrens } 747789Sahrens 748789Sahrens /* 749866Seschrock * zfs get [-rHp] [-o field[,field]...] [-s source[,source]...] 750866Seschrock * < all | property[,property]... > < fs | snap | vol > ... 751789Sahrens * 752789Sahrens * -r recurse over any child datasets 753789Sahrens * -H scripted mode. Headers are stripped, and fields are separated 754789Sahrens * by tabs instead of spaces. 755789Sahrens * -o Set of fields to display. One of "name,property,value,source". 756789Sahrens * Default is all four. 757789Sahrens * -s Set of sources to allow. One of 758789Sahrens * "local,default,inherited,temporary,none". Default is all 759789Sahrens * five. 760789Sahrens * -p Display values in parsable (literal) format. 761789Sahrens * 762789Sahrens * Prints properties for the given datasets. The user can control which 763789Sahrens * columns to display as well as which property types to allow. 764789Sahrens */ 765789Sahrens typedef struct get_cbdata { 766789Sahrens int cb_sources; 767789Sahrens int cb_columns[4]; 7682082Seschrock int cb_nprop; 7692082Seschrock boolean_t cb_scripted; 7702082Seschrock boolean_t cb_literal; 7712082Seschrock boolean_t cb_isall; 772789Sahrens zfs_prop_t cb_prop[ZFS_NPROP_ALL]; 773789Sahrens } get_cbdata_t; 774789Sahrens 775789Sahrens #define GET_COL_NAME 1 776789Sahrens #define GET_COL_PROPERTY 2 777789Sahrens #define GET_COL_VALUE 3 778789Sahrens #define GET_COL_SOURCE 4 779789Sahrens 780789Sahrens /* 781789Sahrens * Display a single line of output, according to the settings in the callback 782789Sahrens * structure. 783789Sahrens */ 784789Sahrens static void 785789Sahrens print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, zfs_prop_t prop, 786789Sahrens const char *value, zfs_source_t sourcetype, const char *source) 787789Sahrens { 788789Sahrens int i; 789789Sahrens int width; 790789Sahrens const char *str; 791789Sahrens char buf[128]; 792789Sahrens 793789Sahrens /* 794789Sahrens * Ignore those source types that the user has chosen to ignore. 795789Sahrens */ 796789Sahrens if ((sourcetype & cbp->cb_sources) == 0) 797789Sahrens return; 798789Sahrens 799789Sahrens for (i = 0; i < 4; i++) { 800789Sahrens switch (cbp->cb_columns[i]) { 801789Sahrens case GET_COL_NAME: 802789Sahrens width = 15; 803789Sahrens str = zfs_get_name(zhp); 804789Sahrens break; 805789Sahrens 806789Sahrens case GET_COL_PROPERTY: 807789Sahrens width = 13; 808789Sahrens str = zfs_prop_to_name(prop); 809789Sahrens break; 810789Sahrens 811789Sahrens case GET_COL_VALUE: 812789Sahrens width = 25; 813789Sahrens str = value; 814789Sahrens break; 815789Sahrens 816789Sahrens case GET_COL_SOURCE: 817789Sahrens width = 15; 818789Sahrens switch (sourcetype) { 819789Sahrens case ZFS_SRC_NONE: 820789Sahrens str = "-"; 821789Sahrens break; 822789Sahrens 823789Sahrens case ZFS_SRC_DEFAULT: 824789Sahrens str = "default"; 825789Sahrens break; 826789Sahrens 827789Sahrens case ZFS_SRC_LOCAL: 828789Sahrens str = "local"; 829789Sahrens break; 830789Sahrens 831789Sahrens case ZFS_SRC_TEMPORARY: 832789Sahrens str = "temporary"; 833789Sahrens break; 834789Sahrens 835789Sahrens case ZFS_SRC_INHERITED: 836789Sahrens (void) snprintf(buf, sizeof (buf), 837789Sahrens "inherited from %s", source); 838789Sahrens str = buf; 839789Sahrens break; 840789Sahrens } 841789Sahrens break; 842789Sahrens 843789Sahrens default: 844789Sahrens continue; 845789Sahrens } 846789Sahrens 847789Sahrens if (cbp->cb_columns[i + 1] == 0) 848789Sahrens (void) printf("%s", str); 849789Sahrens else if (cbp->cb_scripted) 850789Sahrens (void) printf("%s\t", str); 851789Sahrens else 852789Sahrens (void) printf("%-*s ", width, str); 853789Sahrens 854789Sahrens } 855789Sahrens 856789Sahrens (void) printf("\n"); 857789Sahrens } 858789Sahrens 859789Sahrens /* 860789Sahrens * Invoked to display the properties for a single dataset. 861789Sahrens */ 862789Sahrens static int 863789Sahrens get_callback(zfs_handle_t *zhp, void *data) 864789Sahrens { 865789Sahrens char buf[ZFS_MAXPROPLEN]; 866789Sahrens zfs_source_t sourcetype; 867789Sahrens char source[ZFS_MAXNAMELEN]; 868789Sahrens get_cbdata_t *cbp = data; 869789Sahrens int i; 870789Sahrens 871866Seschrock for (i = 0; i < cbp->cb_nprop; i++) { 872866Seschrock if (zfs_prop_get(zhp, cbp->cb_prop[i], buf, 873866Seschrock sizeof (buf), &sourcetype, source, sizeof (source), 874866Seschrock cbp->cb_literal) != 0) { 875866Seschrock if (cbp->cb_isall) 876866Seschrock continue; 877866Seschrock (void) strlcpy(buf, "-", sizeof (buf)); 878866Seschrock sourcetype = ZFS_SRC_NONE; 879866Seschrock } 880789Sahrens 881866Seschrock print_one_property(zhp, cbp, cbp->cb_prop[i], 882866Seschrock buf, sourcetype, source); 883789Sahrens } 884789Sahrens 885789Sahrens return (0); 886789Sahrens } 887789Sahrens 888789Sahrens static int 889789Sahrens zfs_do_get(int argc, char **argv) 890789Sahrens { 891789Sahrens get_cbdata_t cb = { 0 }; 8922082Seschrock boolean_t recurse = B_FALSE; 893789Sahrens int c; 894866Seschrock char *value, *fields, *badopt; 895789Sahrens int i; 896866Seschrock int ret; 897789Sahrens 898789Sahrens /* 899789Sahrens * Set up default columns and sources. 900789Sahrens */ 901789Sahrens cb.cb_sources = ZFS_SRC_ALL; 902789Sahrens cb.cb_columns[0] = GET_COL_NAME; 903789Sahrens cb.cb_columns[1] = GET_COL_PROPERTY; 904789Sahrens cb.cb_columns[2] = GET_COL_VALUE; 905789Sahrens cb.cb_columns[3] = GET_COL_SOURCE; 906789Sahrens 907789Sahrens /* check options */ 908789Sahrens while ((c = getopt(argc, argv, ":o:s:rHp")) != -1) { 909789Sahrens switch (c) { 910789Sahrens case 'p': 9112082Seschrock cb.cb_literal = B_TRUE; 912789Sahrens break; 913789Sahrens case 'r': 9142082Seschrock recurse = B_TRUE; 915789Sahrens break; 916789Sahrens case 'H': 9172082Seschrock cb.cb_scripted = B_TRUE; 918789Sahrens break; 919789Sahrens case ':': 920789Sahrens (void) fprintf(stderr, gettext("missing argument for " 921789Sahrens "'%c' option\n"), optopt); 9222082Seschrock usage(B_FALSE); 923789Sahrens break; 924789Sahrens case 'o': 925789Sahrens /* 926789Sahrens * Process the set of columns to display. We zero out 927789Sahrens * the structure to give us a blank slate. 928789Sahrens */ 929789Sahrens bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 930789Sahrens i = 0; 931789Sahrens while (*optarg != '\0') { 932789Sahrens static char *col_subopts[] = 933789Sahrens { "name", "property", "value", "source", 934789Sahrens NULL }; 935789Sahrens 936789Sahrens if (i == 4) { 937789Sahrens (void) fprintf(stderr, gettext("too " 938789Sahrens "many fields given to -o " 939789Sahrens "option\n")); 9402082Seschrock usage(B_FALSE); 941789Sahrens } 942789Sahrens 943789Sahrens switch (getsubopt(&optarg, col_subopts, 944789Sahrens &value)) { 945789Sahrens case 0: 946789Sahrens cb.cb_columns[i++] = GET_COL_NAME; 947789Sahrens break; 948789Sahrens case 1: 949789Sahrens cb.cb_columns[i++] = GET_COL_PROPERTY; 950789Sahrens break; 951789Sahrens case 2: 952789Sahrens cb.cb_columns[i++] = GET_COL_VALUE; 953789Sahrens break; 954789Sahrens case 3: 955789Sahrens cb.cb_columns[i++] = GET_COL_SOURCE; 956789Sahrens break; 957789Sahrens default: 958789Sahrens (void) fprintf(stderr, 959789Sahrens gettext("invalid column name " 960789Sahrens "'%s'\n"), value); 9612082Seschrock usage(B_FALSE); 962789Sahrens } 963789Sahrens } 964789Sahrens break; 965789Sahrens 966789Sahrens case 's': 967789Sahrens cb.cb_sources = 0; 968789Sahrens while (*optarg != '\0') { 969789Sahrens static char *source_subopts[] = { 970789Sahrens "local", "default", "inherited", 971789Sahrens "temporary", "none", NULL }; 972789Sahrens 973789Sahrens switch (getsubopt(&optarg, source_subopts, 974789Sahrens &value)) { 975789Sahrens case 0: 976789Sahrens cb.cb_sources |= ZFS_SRC_LOCAL; 977789Sahrens break; 978789Sahrens case 1: 979789Sahrens cb.cb_sources |= ZFS_SRC_DEFAULT; 980789Sahrens break; 981789Sahrens case 2: 982789Sahrens cb.cb_sources |= ZFS_SRC_INHERITED; 983789Sahrens break; 984789Sahrens case 3: 985789Sahrens cb.cb_sources |= ZFS_SRC_TEMPORARY; 986789Sahrens break; 987789Sahrens case 4: 988789Sahrens cb.cb_sources |= ZFS_SRC_NONE; 989789Sahrens break; 990789Sahrens default: 991789Sahrens (void) fprintf(stderr, 992789Sahrens gettext("invalid source " 993789Sahrens "'%s'\n"), value); 9942082Seschrock usage(B_FALSE); 995789Sahrens } 996789Sahrens } 997789Sahrens break; 998789Sahrens 999789Sahrens case '?': 1000789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1001789Sahrens optopt); 10022082Seschrock usage(B_FALSE); 1003789Sahrens } 1004789Sahrens } 1005789Sahrens 1006789Sahrens argc -= optind; 1007789Sahrens argv += optind; 1008789Sahrens 1009789Sahrens if (argc < 1) { 1010789Sahrens (void) fprintf(stderr, gettext("missing property " 1011789Sahrens "argument\n")); 10122082Seschrock usage(B_FALSE); 1013789Sahrens } 1014789Sahrens 1015789Sahrens fields = argv[0]; 1016789Sahrens 1017789Sahrens /* 1018866Seschrock * If the user specifies 'all', the behavior of 'zfs get' is slightly 1019866Seschrock * different, because we don't show properties which don't apply to the 1020866Seschrock * given dataset. 1021789Sahrens */ 1022866Seschrock if (strcmp(fields, "all") == 0) 10232082Seschrock cb.cb_isall = B_TRUE; 1024789Sahrens 1025866Seschrock if ((ret = zfs_get_proplist(fields, cb.cb_prop, ZFS_NPROP_ALL, 1026866Seschrock &cb.cb_nprop, &badopt)) != 0) { 1027866Seschrock if (ret == EINVAL) 1028866Seschrock (void) fprintf(stderr, gettext("invalid property " 1029866Seschrock "'%s'\n"), badopt); 1030866Seschrock else 1031866Seschrock (void) fprintf(stderr, gettext("too many properties " 1032866Seschrock "specified\n")); 10332082Seschrock usage(B_FALSE); 1034789Sahrens } 1035789Sahrens 1036789Sahrens argc--; 1037789Sahrens argv++; 1038789Sahrens 1039789Sahrens /* check for at least one dataset name */ 1040789Sahrens if (argc < 1) { 1041789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 10422082Seschrock usage(B_FALSE); 1043789Sahrens } 1044789Sahrens 1045789Sahrens /* 1046789Sahrens * Print out any headers 1047789Sahrens */ 1048789Sahrens if (!cb.cb_scripted) { 1049789Sahrens int i; 1050789Sahrens for (i = 0; i < 4; i++) { 1051789Sahrens switch (cb.cb_columns[i]) { 1052789Sahrens case GET_COL_NAME: 1053789Sahrens (void) printf("%-15s ", "NAME"); 1054789Sahrens break; 1055789Sahrens case GET_COL_PROPERTY: 1056789Sahrens (void) printf("%-13s ", "PROPERTY"); 1057789Sahrens break; 1058789Sahrens case GET_COL_VALUE: 1059789Sahrens (void) printf("%-25s ", "VALUE"); 1060789Sahrens break; 1061789Sahrens case GET_COL_SOURCE: 1062789Sahrens (void) printf("%s", "SOURCE"); 1063789Sahrens break; 1064789Sahrens } 1065789Sahrens } 1066789Sahrens (void) printf("\n"); 1067789Sahrens } 1068789Sahrens 1069789Sahrens /* run for each object */ 10702379Ssjelinek return (zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, NULL, 1071789Sahrens get_callback, &cb)); 10722379Ssjelinek 1073789Sahrens } 1074789Sahrens 1075789Sahrens /* 1076789Sahrens * inherit [-r] <property> <fs|vol> ... 1077789Sahrens * 1078789Sahrens * -r Recurse over all children 1079789Sahrens * 1080789Sahrens * For each dataset specified on the command line, inherit the given property 1081789Sahrens * from its parent. Inheriting a property at the pool level will cause it to 1082789Sahrens * use the default value. The '-r' flag will recurse over all children, and is 1083789Sahrens * useful for setting a property on a hierarchy-wide basis, regardless of any 1084789Sahrens * local modifications for each dataset. 1085789Sahrens */ 1086789Sahrens static int 1087789Sahrens inherit_callback(zfs_handle_t *zhp, void *data) 1088789Sahrens { 1089789Sahrens zfs_prop_t prop = (zfs_prop_t)data; 1090789Sahrens 1091789Sahrens return (zfs_prop_inherit(zhp, prop) != 0); 1092789Sahrens } 1093789Sahrens 1094789Sahrens static int 1095789Sahrens zfs_do_inherit(int argc, char **argv) 1096789Sahrens { 10972082Seschrock boolean_t recurse = B_FALSE; 1098789Sahrens int c; 1099789Sahrens zfs_prop_t prop; 1100789Sahrens char *propname; 1101789Sahrens 1102789Sahrens /* check options */ 1103789Sahrens while ((c = getopt(argc, argv, "r")) != -1) { 1104789Sahrens switch (c) { 1105789Sahrens case 'r': 11062082Seschrock recurse = B_TRUE; 1107789Sahrens break; 1108789Sahrens case '?': 1109789Sahrens default: 1110789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1111789Sahrens optopt); 11122082Seschrock usage(B_FALSE); 1113789Sahrens } 1114789Sahrens } 1115789Sahrens 1116789Sahrens argc -= optind; 1117789Sahrens argv += optind; 1118789Sahrens 1119789Sahrens /* check number of arguments */ 1120789Sahrens if (argc < 1) { 1121789Sahrens (void) fprintf(stderr, gettext("missing property argument\n")); 11222082Seschrock usage(B_FALSE); 1123789Sahrens } 1124789Sahrens if (argc < 2) { 1125789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 11262082Seschrock usage(B_FALSE); 1127789Sahrens } 1128789Sahrens 1129789Sahrens propname = argv[0]; 1130789Sahrens 1131789Sahrens /* 1132789Sahrens * Get and validate the property before iterating over the datasets. We 1133789Sahrens * do this now so as to avoid printing out an error message for each and 1134789Sahrens * every dataset. 1135789Sahrens */ 1136789Sahrens if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) { 1137789Sahrens (void) fprintf(stderr, gettext("invalid property '%s'\n"), 1138789Sahrens propname); 11392082Seschrock usage(B_FALSE); 1140789Sahrens } 1141789Sahrens if (zfs_prop_readonly(prop)) { 1142789Sahrens (void) fprintf(stderr, gettext("%s property is read-only\n"), 1143789Sahrens propname); 1144789Sahrens return (1); 1145789Sahrens } 1146789Sahrens if (!zfs_prop_inheritable(prop)) { 1147789Sahrens (void) fprintf(stderr, gettext("%s property cannot be " 1148789Sahrens "inherited\n"), propname); 1149789Sahrens (void) fprintf(stderr, gettext("use 'zfs set %s=none' to " 1150789Sahrens "clear\n"), propname); 1151789Sahrens return (1); 1152789Sahrens } 1153789Sahrens 1154789Sahrens return (zfs_for_each(argc - 1, argv + 1, recurse, 11552379Ssjelinek ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, 1156789Sahrens inherit_callback, (void *)prop)); 1157789Sahrens } 1158789Sahrens 1159789Sahrens /* 11602379Ssjelinek * list [-rH] [-o property[,property]...] [-t type[,type]...] 11612379Ssjelinek * [-s property [-s property]...] [-S property [-S property]...] 11622379Ssjelinek * <dataset> ... 1163789Sahrens * 1164789Sahrens * -r Recurse over all children 1165789Sahrens * -H Scripted mode; elide headers and separate colums by tabs 1166789Sahrens * -o Control which fields to display. 1167866Seschrock * -t Control which object types to display. 11682379Ssjelinek * -s Specify sort columns, descending order. 11692379Ssjelinek * -S Specify sort columns, ascending order. 1170789Sahrens * 1171789Sahrens * When given no arguments, lists all filesystems in the system. 1172789Sahrens * Otherwise, list the specified datasets, optionally recursing down them if 1173789Sahrens * '-r' is specified. 1174789Sahrens */ 1175789Sahrens typedef struct list_cbdata { 11762082Seschrock boolean_t cb_first; 11772082Seschrock boolean_t cb_scripted; 1178866Seschrock zfs_prop_t cb_fields[ZFS_NPROP_ALL]; 1179866Seschrock int cb_fieldcount; 1180789Sahrens } list_cbdata_t; 1181789Sahrens 1182789Sahrens /* 1183789Sahrens * Given a list of columns to display, output appropriate headers for each one. 1184789Sahrens */ 1185789Sahrens static void 1186866Seschrock print_header(zfs_prop_t *fields, size_t count) 1187789Sahrens { 1188789Sahrens int i; 1189789Sahrens 1190789Sahrens for (i = 0; i < count; i++) { 1191789Sahrens if (i != 0) 1192789Sahrens (void) printf(" "); 1193789Sahrens if (i == count - 1) 1194789Sahrens (void) printf("%s", zfs_prop_column_name(fields[i])); 1195789Sahrens else /* LINTED - format specifier */ 1196789Sahrens (void) printf(zfs_prop_column_format(fields[i]), 1197789Sahrens zfs_prop_column_name(fields[i])); 1198789Sahrens } 1199789Sahrens 1200789Sahrens (void) printf("\n"); 1201789Sahrens } 1202789Sahrens 1203789Sahrens /* 1204789Sahrens * Given a dataset and a list of fields, print out all the properties according 1205789Sahrens * to the described layout. 1206789Sahrens */ 1207789Sahrens static void 1208866Seschrock print_dataset(zfs_handle_t *zhp, zfs_prop_t *fields, size_t count, int scripted) 1209789Sahrens { 1210789Sahrens int i; 1211789Sahrens char property[ZFS_MAXPROPLEN]; 1212789Sahrens 1213789Sahrens for (i = 0; i < count; i++) { 1214789Sahrens if (i != 0) { 1215789Sahrens if (scripted) 1216789Sahrens (void) printf("\t"); 1217789Sahrens else 1218789Sahrens (void) printf(" "); 1219789Sahrens } 1220789Sahrens 1221789Sahrens if (zfs_prop_get(zhp, fields[i], property, 12222082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) != 0) 1223789Sahrens (void) strlcpy(property, "-", sizeof (property)); 1224789Sahrens 1225866Seschrock /* 1226866Seschrock * If this is being called in scripted mode, or if this is the 1227866Seschrock * last column and it is left-justified, don't include a width 1228866Seschrock * format specifier. 1229866Seschrock */ 1230866Seschrock if (scripted || (i == count - 1 && 1231866Seschrock strchr(zfs_prop_column_format(fields[i]), '-') != NULL)) 1232789Sahrens (void) printf("%s", property); 1233789Sahrens else /* LINTED - format specifier */ 1234789Sahrens (void) printf(zfs_prop_column_format(fields[i]), 1235789Sahrens property); 1236789Sahrens } 1237789Sahrens 1238789Sahrens (void) printf("\n"); 1239789Sahrens } 1240789Sahrens 1241789Sahrens /* 1242789Sahrens * Generic callback function to list a dataset or snapshot. 1243789Sahrens */ 1244789Sahrens static int 1245789Sahrens list_callback(zfs_handle_t *zhp, void *data) 1246789Sahrens { 1247789Sahrens list_cbdata_t *cbp = data; 1248789Sahrens 1249789Sahrens if (cbp->cb_first) { 1250789Sahrens if (!cbp->cb_scripted) 1251789Sahrens print_header(cbp->cb_fields, cbp->cb_fieldcount); 12522082Seschrock cbp->cb_first = B_FALSE; 1253789Sahrens } 1254789Sahrens 1255789Sahrens print_dataset(zhp, cbp->cb_fields, cbp->cb_fieldcount, 1256789Sahrens cbp->cb_scripted); 1257789Sahrens 1258789Sahrens return (0); 1259789Sahrens } 1260789Sahrens 1261789Sahrens static int 1262789Sahrens zfs_do_list(int argc, char **argv) 1263789Sahrens { 1264789Sahrens int c; 12652082Seschrock boolean_t recurse = B_FALSE; 12662082Seschrock boolean_t scripted = B_FALSE; 1267789Sahrens static char default_fields[] = 1268789Sahrens "name,used,available,referenced,mountpoint"; 1269789Sahrens int types = ZFS_TYPE_ANY; 1270789Sahrens char *fields = NULL; 1271789Sahrens char *basic_fields = default_fields; 1272789Sahrens list_cbdata_t cb = { 0 }; 1273789Sahrens char *value; 1274789Sahrens int ret; 1275789Sahrens char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL }; 1276866Seschrock char *badopt; 1277866Seschrock int alloffset; 12782379Ssjelinek zfs_sort_column_t *sortcol = NULL; 1279789Sahrens 1280789Sahrens /* check options */ 12812379Ssjelinek while ((c = getopt(argc, argv, ":o:rt:Hs:S:")) != -1) { 12822379Ssjelinek zfs_prop_t prop; 12832379Ssjelinek 1284789Sahrens switch (c) { 1285789Sahrens case 'o': 1286789Sahrens fields = optarg; 1287789Sahrens break; 1288789Sahrens case 'r': 12892082Seschrock recurse = B_TRUE; 1290789Sahrens break; 1291789Sahrens case 'H': 12922082Seschrock scripted = B_TRUE; 1293789Sahrens break; 12942379Ssjelinek case 's': 12952379Ssjelinek if ((prop = zfs_name_to_prop(optarg)) == 12962379Ssjelinek ZFS_PROP_INVAL) { 12972379Ssjelinek (void) fprintf(stderr, 12982379Ssjelinek gettext("invalid property '%s'\n"), optarg); 12992379Ssjelinek usage(B_FALSE); 13002379Ssjelinek } 13012379Ssjelinek zfs_add_sort_column(&sortcol, prop, B_FALSE); 13022379Ssjelinek break; 13032379Ssjelinek case 'S': 13042379Ssjelinek if ((prop = zfs_name_to_prop(optarg)) == 13052379Ssjelinek ZFS_PROP_INVAL) { 13062379Ssjelinek (void) fprintf(stderr, 13072379Ssjelinek gettext("invalid property '%s'\n"), optarg); 13082379Ssjelinek usage(B_FALSE); 13092379Ssjelinek } 13102379Ssjelinek zfs_add_sort_column(&sortcol, prop, B_TRUE); 13112379Ssjelinek break; 1312789Sahrens case 't': 1313789Sahrens types = 0; 1314789Sahrens while (*optarg != '\0') { 1315789Sahrens switch (getsubopt(&optarg, type_subopts, 1316789Sahrens &value)) { 1317789Sahrens case 0: 1318789Sahrens types |= ZFS_TYPE_FILESYSTEM; 1319789Sahrens break; 1320789Sahrens case 1: 1321789Sahrens types |= ZFS_TYPE_VOLUME; 1322789Sahrens break; 1323789Sahrens case 2: 1324789Sahrens types |= ZFS_TYPE_SNAPSHOT; 1325789Sahrens break; 1326789Sahrens default: 1327789Sahrens (void) fprintf(stderr, 1328789Sahrens gettext("invalid type '%s'\n"), 1329789Sahrens value); 13302082Seschrock usage(B_FALSE); 1331789Sahrens } 1332789Sahrens } 1333789Sahrens break; 1334789Sahrens case ':': 1335789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1336789Sahrens "'%c' option\n"), optopt); 13372082Seschrock usage(B_FALSE); 1338789Sahrens break; 1339789Sahrens case '?': 1340789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1341789Sahrens optopt); 13422082Seschrock usage(B_FALSE); 1343789Sahrens } 1344789Sahrens } 1345789Sahrens 1346789Sahrens argc -= optind; 1347789Sahrens argv += optind; 1348789Sahrens 1349789Sahrens if (fields == NULL) 1350789Sahrens fields = basic_fields; 1351789Sahrens 1352866Seschrock /* 1353866Seschrock * If the user specifies '-o all', the zfs_get_proplist() doesn't 1354866Seschrock * normally include the name of the dataset. For 'zfs list', we always 1355866Seschrock * want this property to be first. 1356866Seschrock */ 1357866Seschrock if (strcmp(fields, "all") == 0) { 1358866Seschrock cb.cb_fields[0] = ZFS_PROP_NAME; 1359866Seschrock alloffset = 1; 1360866Seschrock } else { 1361866Seschrock alloffset = 0; 1362789Sahrens } 1363789Sahrens 1364866Seschrock if ((ret = zfs_get_proplist(fields, cb.cb_fields + alloffset, 1365866Seschrock ZFS_NPROP_ALL - alloffset, &cb.cb_fieldcount, &badopt)) != 0) { 1366866Seschrock if (ret == EINVAL) 1367866Seschrock (void) fprintf(stderr, gettext("invalid property " 1368866Seschrock "'%s'\n"), badopt); 1369866Seschrock else 1370866Seschrock (void) fprintf(stderr, gettext("too many properties " 1371866Seschrock "specified\n")); 13722082Seschrock usage(B_FALSE); 1373866Seschrock } 1374866Seschrock 1375866Seschrock cb.cb_fieldcount += alloffset; 1376789Sahrens cb.cb_scripted = scripted; 13772082Seschrock cb.cb_first = B_TRUE; 1378789Sahrens 13792379Ssjelinek ret = zfs_for_each(argc, argv, recurse, types, sortcol, 13802379Ssjelinek list_callback, &cb); 13812379Ssjelinek 13822379Ssjelinek zfs_free_sort_columns(sortcol); 1383789Sahrens 13842082Seschrock if (ret == 0 && cb.cb_first) 1385789Sahrens (void) printf(gettext("no datasets available\n")); 1386789Sahrens 1387789Sahrens return (ret); 1388789Sahrens } 1389789Sahrens 1390789Sahrens /* 1391789Sahrens * zfs rename <fs | snap | vol> <fs | snap | vol> 1392789Sahrens * 1393789Sahrens * Renames the given dataset to another of the same type. 1394789Sahrens */ 1395789Sahrens /* ARGSUSED */ 1396789Sahrens static int 1397789Sahrens zfs_do_rename(int argc, char **argv) 1398789Sahrens { 1399789Sahrens zfs_handle_t *zhp; 14002082Seschrock int ret; 1401789Sahrens 1402789Sahrens /* check options */ 1403789Sahrens if (argc > 1 && argv[1][0] == '-') { 1404789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1405789Sahrens argv[1][1]); 14062082Seschrock usage(B_FALSE); 1407789Sahrens } 1408789Sahrens 1409789Sahrens /* check number of arguments */ 1410789Sahrens if (argc < 2) { 1411789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 1412789Sahrens "argument\n")); 14132082Seschrock usage(B_FALSE); 1414789Sahrens } 1415789Sahrens if (argc < 3) { 1416789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 1417789Sahrens "argument\n")); 14182082Seschrock usage(B_FALSE); 1419789Sahrens } 1420789Sahrens if (argc > 3) { 1421789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 14222082Seschrock usage(B_FALSE); 1423789Sahrens } 1424789Sahrens 14252082Seschrock if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_ANY)) == NULL) 1426789Sahrens return (1); 1427789Sahrens 14282082Seschrock ret = (zfs_rename(zhp, argv[2]) != 0); 14292082Seschrock 14302082Seschrock zfs_close(zhp); 14312082Seschrock return (ret); 14322082Seschrock } 14332082Seschrock 14342082Seschrock /* 14352082Seschrock * zfs promote <fs> 14362082Seschrock * 14372082Seschrock * Promotes the given clone fs to be the parent 14382082Seschrock */ 14392082Seschrock /* ARGSUSED */ 14402082Seschrock static int 14412082Seschrock zfs_do_promote(int argc, char **argv) 14422082Seschrock { 14432082Seschrock zfs_handle_t *zhp; 14442082Seschrock int ret; 14452082Seschrock 14462082Seschrock /* check options */ 14472082Seschrock if (argc > 1 && argv[1][0] == '-') { 14482082Seschrock (void) fprintf(stderr, gettext("invalid option '%c'\n"), 14492082Seschrock argv[1][1]); 14502082Seschrock usage(B_FALSE); 14512082Seschrock } 14522082Seschrock 14532082Seschrock /* check number of arguments */ 14542082Seschrock if (argc < 2) { 14552082Seschrock (void) fprintf(stderr, gettext("missing clone filesystem" 14562597Snd150628 " argument\n")); 14572082Seschrock usage(B_FALSE); 14582082Seschrock } 14592082Seschrock if (argc > 2) { 14602082Seschrock (void) fprintf(stderr, gettext("too many arguments\n")); 14612082Seschrock usage(B_FALSE); 14622082Seschrock } 14632082Seschrock 14642082Seschrock zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 14652082Seschrock if (zhp == NULL) 14662082Seschrock return (1); 14672082Seschrock 14682082Seschrock ret = (zfs_promote(zhp) != 0); 14692082Seschrock 1470789Sahrens zfs_close(zhp); 1471789Sahrens return (ret); 1472789Sahrens } 1473789Sahrens 1474789Sahrens /* 1475789Sahrens * zfs rollback [-rfR] <snapshot> 1476789Sahrens * 1477789Sahrens * -r Delete any intervening snapshots before doing rollback 1478789Sahrens * -R Delete any snapshots and their clones 1479789Sahrens * -f Force unmount filesystems, even if they are in use. 1480789Sahrens * 1481789Sahrens * Given a filesystem, rollback to a specific snapshot, discarding any changes 1482789Sahrens * since then and making it the active dataset. If more recent snapshots exist, 1483789Sahrens * the command will complain unless the '-r' flag is given. 1484789Sahrens */ 1485789Sahrens typedef struct rollback_cbdata { 1486789Sahrens uint64_t cb_create; 14872082Seschrock boolean_t cb_first; 1488789Sahrens int cb_doclones; 1489789Sahrens char *cb_target; 1490789Sahrens int cb_error; 14912082Seschrock boolean_t cb_recurse; 14922082Seschrock boolean_t cb_dependent; 1493789Sahrens } rollback_cbdata_t; 1494789Sahrens 1495789Sahrens /* 1496789Sahrens * Report any snapshots more recent than the one specified. Used when '-r' is 1497789Sahrens * not specified. We reuse this same callback for the snapshot dependents - if 1498789Sahrens * 'cb_dependent' is set, then this is a dependent and we should report it 1499789Sahrens * without checking the transaction group. 1500789Sahrens */ 1501789Sahrens static int 1502789Sahrens rollback_check(zfs_handle_t *zhp, void *data) 1503789Sahrens { 1504789Sahrens rollback_cbdata_t *cbp = data; 1505789Sahrens 15062082Seschrock if (cbp->cb_doclones) { 15072082Seschrock zfs_close(zhp); 1508789Sahrens return (0); 15092082Seschrock } 1510789Sahrens 1511789Sahrens if (!cbp->cb_dependent) { 1512789Sahrens if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && 15131294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 1514789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 1515789Sahrens cbp->cb_create) { 1516789Sahrens 1517789Sahrens if (cbp->cb_first && !cbp->cb_recurse) { 1518789Sahrens (void) fprintf(stderr, gettext("cannot " 1519789Sahrens "rollback to '%s': more recent snapshots " 1520789Sahrens "exist\n"), 1521789Sahrens cbp->cb_target); 1522789Sahrens (void) fprintf(stderr, gettext("use '-r' to " 1523789Sahrens "force deletion of the following " 1524789Sahrens "snapshots:\n")); 1525789Sahrens cbp->cb_first = 0; 1526789Sahrens cbp->cb_error = 1; 1527789Sahrens } 1528789Sahrens 1529789Sahrens if (cbp->cb_recurse) { 15302082Seschrock cbp->cb_dependent = B_TRUE; 15312474Seschrock if (zfs_iter_dependents(zhp, B_TRUE, 15322474Seschrock rollback_check, cbp) != 0) { 15332474Seschrock zfs_close(zhp); 15342474Seschrock return (-1); 15352474Seschrock } 15362082Seschrock cbp->cb_dependent = B_FALSE; 1537789Sahrens } else { 1538789Sahrens (void) fprintf(stderr, "%s\n", 1539789Sahrens zfs_get_name(zhp)); 1540789Sahrens } 1541789Sahrens } 1542789Sahrens } else { 1543789Sahrens if (cbp->cb_first && cbp->cb_recurse) { 1544789Sahrens (void) fprintf(stderr, gettext("cannot rollback to " 1545789Sahrens "'%s': clones of previous snapshots exist\n"), 1546789Sahrens cbp->cb_target); 1547789Sahrens (void) fprintf(stderr, gettext("use '-R' to " 1548789Sahrens "force deletion of the following clones and " 1549789Sahrens "dependents:\n")); 1550789Sahrens cbp->cb_first = 0; 1551789Sahrens cbp->cb_error = 1; 1552789Sahrens } 1553789Sahrens 1554789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1555789Sahrens } 1556789Sahrens 1557789Sahrens zfs_close(zhp); 1558789Sahrens return (0); 1559789Sahrens } 1560789Sahrens 1561789Sahrens static int 1562789Sahrens zfs_do_rollback(int argc, char **argv) 1563789Sahrens { 1564789Sahrens int ret; 1565789Sahrens int c; 1566789Sahrens rollback_cbdata_t cb = { 0 }; 1567789Sahrens zfs_handle_t *zhp, *snap; 1568789Sahrens char parentname[ZFS_MAXNAMELEN]; 1569789Sahrens char *delim; 15701294Slling int force = 0; 1571789Sahrens 1572789Sahrens /* check options */ 1573789Sahrens while ((c = getopt(argc, argv, "rfR")) != -1) { 1574789Sahrens switch (c) { 1575789Sahrens case 'f': 15761294Slling force = 1; 1577789Sahrens break; 1578789Sahrens case 'r': 1579789Sahrens cb.cb_recurse = 1; 1580789Sahrens break; 1581789Sahrens case 'R': 1582789Sahrens cb.cb_recurse = 1; 1583789Sahrens cb.cb_doclones = 1; 1584789Sahrens break; 1585789Sahrens case '?': 1586789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1587789Sahrens optopt); 15882082Seschrock usage(B_FALSE); 1589789Sahrens } 1590789Sahrens } 1591789Sahrens 1592789Sahrens argc -= optind; 1593789Sahrens argv += optind; 1594789Sahrens 1595789Sahrens /* check number of arguments */ 1596789Sahrens if (argc < 1) { 1597789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 15982082Seschrock usage(B_FALSE); 1599789Sahrens } 1600789Sahrens if (argc > 1) { 1601789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 16022082Seschrock usage(B_FALSE); 1603789Sahrens } 1604789Sahrens 1605789Sahrens /* open the snapshot */ 16062082Seschrock if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1607789Sahrens return (1); 1608789Sahrens 16091294Slling /* open the parent dataset */ 16101294Slling (void) strlcpy(parentname, argv[0], sizeof (parentname)); 1611789Sahrens verify((delim = strrchr(parentname, '@')) != NULL); 1612789Sahrens *delim = '\0'; 16132082Seschrock if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_ANY)) == NULL) { 1614789Sahrens zfs_close(snap); 1615789Sahrens return (1); 1616789Sahrens } 1617789Sahrens 1618789Sahrens /* 1619789Sahrens * Check for more recent snapshots and/or clones based on the presence 1620789Sahrens * of '-r' and '-R'. 1621789Sahrens */ 16221294Slling cb.cb_target = argv[0]; 16231294Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 16242082Seschrock cb.cb_first = B_TRUE; 1625789Sahrens cb.cb_error = 0; 16262474Seschrock if ((ret = zfs_iter_children(zhp, rollback_check, &cb)) != 0) 16272474Seschrock goto out; 1628789Sahrens 1629789Sahrens if ((ret = cb.cb_error) != 0) 1630789Sahrens goto out; 1631789Sahrens 1632789Sahrens /* 16331294Slling * Rollback parent to the given snapshot. 1634789Sahrens */ 16351294Slling ret = zfs_rollback(zhp, snap, force); 1636789Sahrens 1637789Sahrens out: 1638789Sahrens zfs_close(snap); 1639789Sahrens zfs_close(zhp); 1640789Sahrens 1641789Sahrens if (ret == 0) 1642789Sahrens return (0); 1643789Sahrens else 1644789Sahrens return (1); 1645789Sahrens } 1646789Sahrens 1647789Sahrens /* 1648789Sahrens * zfs set property=value { fs | snap | vol } ... 1649789Sahrens * 1650789Sahrens * Sets the given property for all datasets specified on the command line. 1651789Sahrens */ 1652789Sahrens typedef struct set_cbdata { 1653789Sahrens char *cb_propname; 1654789Sahrens char *cb_value; 1655789Sahrens zfs_prop_t cb_prop; 1656789Sahrens } set_cbdata_t; 1657789Sahrens 1658789Sahrens static int 1659789Sahrens set_callback(zfs_handle_t *zhp, void *data) 1660789Sahrens { 1661789Sahrens set_cbdata_t *cbp = data; 1662789Sahrens int ret = 1; 1663789Sahrens 1664789Sahrens /* don't allow setting of properties for snapshots */ 1665789Sahrens if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { 1666789Sahrens (void) fprintf(stderr, gettext("cannot set %s property for " 1667789Sahrens "'%s': snapshot properties cannot be modified\n"), 1668789Sahrens cbp->cb_propname, zfs_get_name(zhp)); 1669789Sahrens return (1); 1670789Sahrens } 1671789Sahrens 1672789Sahrens /* 16731133Seschrock * If we're changing the volsize, make sure the value is appropriate, 16741133Seschrock * and set the reservation if this is a non-sparse volume. 1675789Sahrens */ 1676789Sahrens if (cbp->cb_prop == ZFS_PROP_VOLSIZE && 16771133Seschrock zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 1678789Sahrens uint64_t volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1679789Sahrens uint64_t avail = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE); 16801133Seschrock uint64_t reservation = zfs_prop_get_int(zhp, 16811133Seschrock ZFS_PROP_RESERVATION); 16821133Seschrock uint64_t blocksize = zfs_prop_get_int(zhp, 16831133Seschrock ZFS_PROP_VOLBLOCKSIZE); 1684789Sahrens uint64_t value; 1685789Sahrens 1686789Sahrens verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); 1687789Sahrens 16881133Seschrock if (value % blocksize != 0) { 16891133Seschrock char buf[64]; 16901133Seschrock 16911133Seschrock zfs_nicenum(blocksize, buf, sizeof (buf)); 16921133Seschrock (void) fprintf(stderr, gettext("cannot set %s for " 16931133Seschrock "'%s': must be a multiple of volume block size " 16941133Seschrock "(%s)\n"), cbp->cb_propname, zfs_get_name(zhp), 16951133Seschrock buf); 1696789Sahrens return (1); 1697789Sahrens } 1698789Sahrens 16991133Seschrock if (value == 0) { 17001133Seschrock (void) fprintf(stderr, gettext("cannot set %s for " 17011133Seschrock "'%s': cannot be zero\n"), cbp->cb_propname, 17021133Seschrock zfs_get_name(zhp)); 1703789Sahrens return (1); 1704789Sahrens } 17051133Seschrock 17061133Seschrock if (volsize == reservation) { 17071133Seschrock if (value > volsize && (value - volsize) > avail) { 17081133Seschrock (void) fprintf(stderr, gettext("cannot set " 17091133Seschrock "%s property for '%s': volume size exceeds " 17101133Seschrock "amount of available space\n"), 17111133Seschrock cbp->cb_propname, zfs_get_name(zhp)); 17121133Seschrock return (1); 17131133Seschrock } 17141133Seschrock 17151133Seschrock if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, 17161133Seschrock cbp->cb_value) != 0) { 17171133Seschrock (void) fprintf(stderr, gettext("volsize and " 17181133Seschrock "reservation must remain equal\n")); 17191133Seschrock return (1); 17201133Seschrock } 17211133Seschrock } 1722789Sahrens } 1723789Sahrens 1724789Sahrens /* 1725789Sahrens * Do not allow the reservation to be set above the volume size. We do 1726789Sahrens * this here instead of inside libzfs because libzfs violates this rule 1727789Sahrens * internally. 1728789Sahrens */ 1729789Sahrens if (cbp->cb_prop == ZFS_PROP_RESERVATION && 1730789Sahrens zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 1731789Sahrens uint64_t value; 1732789Sahrens uint64_t volsize; 1733789Sahrens 1734789Sahrens volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1735789Sahrens if (strcmp(cbp->cb_value, "none") == 0) 1736789Sahrens value = 0; 1737789Sahrens else 1738789Sahrens verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); 1739789Sahrens 1740789Sahrens if (value > volsize) { 1741789Sahrens (void) fprintf(stderr, gettext("cannot set %s " 1742789Sahrens "for '%s': size is greater than current " 1743789Sahrens "volume size\n"), cbp->cb_propname, 1744789Sahrens zfs_get_name(zhp)); 1745789Sahrens return (-1); 1746789Sahrens } 1747789Sahrens } 1748789Sahrens 17492169Snd150628 if (zfs_prop_set(zhp, cbp->cb_prop, cbp->cb_value) != 0) { 17502169Snd150628 switch (libzfs_errno(g_zfs)) { 17512169Snd150628 case EZFS_MOUNTFAILED: 17522169Snd150628 (void) fprintf(stderr, gettext("property may be set " 17532169Snd150628 "but unable to remount filesystem\n")); 17542169Snd150628 break; 17552169Snd150628 case EZFS_SHAREFAILED: 17562169Snd150628 (void) fprintf(stderr, gettext("property may be set " 17572169Snd150628 "but unable to reshare filesystem\n")); 17582169Snd150628 break; 17592169Snd150628 } 1760789Sahrens return (1); 17612169Snd150628 } 1762789Sahrens ret = 0; 1763789Sahrens error: 1764789Sahrens return (ret); 1765789Sahrens } 1766789Sahrens 1767789Sahrens static int 1768789Sahrens zfs_do_set(int argc, char **argv) 1769789Sahrens { 1770789Sahrens set_cbdata_t cb; 1771789Sahrens 1772789Sahrens /* check for options */ 1773789Sahrens if (argc > 1 && argv[1][0] == '-') { 1774789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1775789Sahrens argv[1][1]); 17762082Seschrock usage(B_FALSE); 1777789Sahrens } 1778789Sahrens 1779789Sahrens /* check number of arguments */ 1780789Sahrens if (argc < 2) { 1781789Sahrens (void) fprintf(stderr, gettext("missing property=value " 1782789Sahrens "argument\n")); 17832082Seschrock usage(B_FALSE); 1784789Sahrens } 1785789Sahrens if (argc < 3) { 1786789Sahrens (void) fprintf(stderr, gettext("missing dataset name\n")); 17872082Seschrock usage(B_FALSE); 1788789Sahrens } 1789789Sahrens 1790789Sahrens /* validate property=value argument */ 1791789Sahrens cb.cb_propname = argv[1]; 1792789Sahrens if ((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) { 1793789Sahrens (void) fprintf(stderr, gettext("missing value in " 1794789Sahrens "property=value argument\n")); 17952082Seschrock usage(B_FALSE); 1796789Sahrens } 1797789Sahrens 1798789Sahrens *cb.cb_value = '\0'; 1799789Sahrens cb.cb_value++; 1800789Sahrens 1801789Sahrens if (*cb.cb_propname == '\0') { 1802789Sahrens (void) fprintf(stderr, 1803789Sahrens gettext("missing property in property=value argument\n")); 18042082Seschrock usage(B_FALSE); 1805789Sahrens } 1806789Sahrens if (*cb.cb_value == '\0') { 1807789Sahrens (void) fprintf(stderr, 1808789Sahrens gettext("missing value in property=value argument\n")); 18092082Seschrock usage(B_FALSE); 1810789Sahrens } 1811789Sahrens 1812789Sahrens /* get the property type */ 1813789Sahrens if ((cb.cb_prop = zfs_name_to_prop(cb.cb_propname)) == 1814789Sahrens ZFS_PROP_INVAL) { 1815789Sahrens (void) fprintf(stderr, 1816789Sahrens gettext("invalid property '%s'\n"), cb.cb_propname); 18172082Seschrock usage(B_FALSE); 1818789Sahrens } 1819789Sahrens 1820789Sahrens /* 1821789Sahrens * Validate that the value is appropriate for this property. We do this 1822789Sahrens * once now so we don't generate multiple errors each time we try to 1823789Sahrens * apply it to a dataset. 1824789Sahrens */ 18252082Seschrock if (zfs_prop_validate(g_zfs, cb.cb_prop, cb.cb_value, NULL) != 0) 1826789Sahrens return (1); 1827789Sahrens 18282082Seschrock return (zfs_for_each(argc - 2, argv + 2, B_FALSE, 18292379Ssjelinek ZFS_TYPE_ANY, NULL, set_callback, &cb)); 1830789Sahrens } 1831789Sahrens 1832789Sahrens /* 18332199Sahrens * zfs snapshot [-r] <fs@snap> 1834789Sahrens * 1835789Sahrens * Creates a snapshot with the given name. While functionally equivalent to 1836789Sahrens * 'zfs create', it is a separate command to diffferentiate intent. 1837789Sahrens */ 1838789Sahrens static int 1839789Sahrens zfs_do_snapshot(int argc, char **argv) 1840789Sahrens { 18412199Sahrens int recursive = B_FALSE; 18422199Sahrens int ret; 18432199Sahrens char c; 18442199Sahrens 1845789Sahrens /* check options */ 18462199Sahrens while ((c = getopt(argc, argv, ":r")) != -1) { 18472199Sahrens switch (c) { 18482199Sahrens case 'r': 18492199Sahrens recursive = B_TRUE; 18502199Sahrens break; 18512199Sahrens case '?': 18522199Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 18532199Sahrens optopt); 18542199Sahrens usage(B_FALSE); 18552199Sahrens } 1856789Sahrens } 1857789Sahrens 18582199Sahrens argc -= optind; 18592199Sahrens argv += optind; 18602199Sahrens 1861789Sahrens /* check number of arguments */ 18622199Sahrens if (argc < 1) { 1863789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 18642082Seschrock usage(B_FALSE); 1865789Sahrens } 18662199Sahrens if (argc > 1) { 1867789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 18682082Seschrock usage(B_FALSE); 1869789Sahrens } 1870789Sahrens 18712199Sahrens ret = zfs_snapshot(g_zfs, argv[0], recursive); 18722199Sahrens if (ret && recursive) 18732199Sahrens (void) fprintf(stderr, gettext("no snapshots were created\n")); 18742199Sahrens return (ret != 0); 18752199Sahrens 1876789Sahrens } 1877789Sahrens 1878789Sahrens /* 18791749Sahrens * zfs send [-i <fs@snap>] <fs@snap> 1880789Sahrens * 1881789Sahrens * Send a backup stream to stdout. 1882789Sahrens */ 1883789Sahrens static int 18841749Sahrens zfs_do_send(int argc, char **argv) 1885789Sahrens { 1886789Sahrens char *fromname = NULL; 1887789Sahrens zfs_handle_t *zhp_from = NULL, *zhp_to; 1888789Sahrens int c, err; 1889*2665Snd150628 char fullname[MAXPATHLEN]; 1890789Sahrens 1891789Sahrens /* check options */ 1892789Sahrens while ((c = getopt(argc, argv, ":i:")) != -1) { 1893789Sahrens switch (c) { 1894789Sahrens case 'i': 1895789Sahrens fromname = optarg; 1896789Sahrens break; 1897789Sahrens case ':': 1898789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1899789Sahrens "'%c' option\n"), optopt); 19002082Seschrock usage(B_FALSE); 1901789Sahrens break; 1902789Sahrens case '?': 1903789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1904789Sahrens optopt); 19052082Seschrock usage(B_FALSE); 1906789Sahrens } 1907789Sahrens } 1908789Sahrens 1909789Sahrens argc -= optind; 1910789Sahrens argv += optind; 1911789Sahrens 1912789Sahrens /* check number of arguments */ 1913789Sahrens if (argc < 1) { 1914789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 19152082Seschrock usage(B_FALSE); 1916789Sahrens } 1917789Sahrens if (argc > 1) { 1918789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 19192082Seschrock usage(B_FALSE); 1920789Sahrens } 1921789Sahrens 1922789Sahrens if (isatty(STDOUT_FILENO)) { 1923789Sahrens (void) fprintf(stderr, 19241749Sahrens gettext("Error: Stream can not be written " 1925789Sahrens "to a terminal.\n" 1926789Sahrens "You must redirect standard output.\n")); 1927789Sahrens return (1); 1928789Sahrens } 1929789Sahrens 1930*2665Snd150628 if ((zhp_to = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1931*2665Snd150628 return (1); 1932*2665Snd150628 1933789Sahrens if (fromname) { 1934*2665Snd150628 1935*2665Snd150628 /* 1936*2665Snd150628 * If fromname is an abbreviated snapshot name, 1937*2665Snd150628 * then reconstruct the name of the parent dataset 1938*2665Snd150628 */ 1939*2665Snd150628 if ((strchr(fromname, '@') == NULL) || 1940*2665Snd150628 *fromname == '@') { 1941*2665Snd150628 char *cp; 1942*2665Snd150628 cp = strchr(argv[0], '@'); 1943*2665Snd150628 if (strchr(fromname, '@') == NULL) 1944*2665Snd150628 *(++cp) = '\0'; 1945*2665Snd150628 else 1946*2665Snd150628 *cp = '\0'; 1947*2665Snd150628 (void) strncpy(fullname, argv[0], sizeof (fullname)); 1948*2665Snd150628 (void) strlcat(fullname, fromname, sizeof (fullname)); 1949*2665Snd150628 fromname = fullname; 1950*2665Snd150628 } 1951*2665Snd150628 19522082Seschrock if ((zhp_from = zfs_open(g_zfs, fromname, 19532082Seschrock ZFS_TYPE_SNAPSHOT)) == NULL) 1954789Sahrens return (1); 1955789Sahrens } 1956789Sahrens 19571749Sahrens err = zfs_send(zhp_to, zhp_from); 1958789Sahrens 1959789Sahrens if (zhp_from) 1960789Sahrens zfs_close(zhp_from); 1961789Sahrens zfs_close(zhp_to); 1962789Sahrens 1963789Sahrens return (err != 0); 1964789Sahrens } 1965789Sahrens 1966789Sahrens /* 19671749Sahrens * zfs receive <fs@snap> 1968789Sahrens * 1969789Sahrens * Restore a backup stream from stdin. 1970789Sahrens */ 1971789Sahrens static int 19721749Sahrens zfs_do_receive(int argc, char **argv) 1973789Sahrens { 1974789Sahrens int c, err; 19752082Seschrock boolean_t isprefix = B_FALSE; 19762082Seschrock boolean_t dryrun = B_FALSE; 19772082Seschrock boolean_t verbose = B_FALSE; 1978*2665Snd150628 boolean_t force = B_FALSE; 1979789Sahrens 1980789Sahrens /* check options */ 1981*2665Snd150628 while ((c = getopt(argc, argv, ":dnvF")) != -1) { 1982789Sahrens switch (c) { 1983789Sahrens case 'd': 19842082Seschrock isprefix = B_TRUE; 1985789Sahrens break; 1986789Sahrens case 'n': 19872082Seschrock dryrun = B_TRUE; 1988789Sahrens break; 1989789Sahrens case 'v': 19902082Seschrock verbose = B_TRUE; 1991789Sahrens break; 1992*2665Snd150628 case 'F': 1993*2665Snd150628 force = B_TRUE; 1994*2665Snd150628 break; 1995789Sahrens case ':': 1996789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1997789Sahrens "'%c' option\n"), optopt); 19982082Seschrock usage(B_FALSE); 1999789Sahrens break; 2000789Sahrens case '?': 2001789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2002789Sahrens optopt); 20032082Seschrock usage(B_FALSE); 2004789Sahrens } 2005789Sahrens } 2006789Sahrens 2007789Sahrens argc -= optind; 2008789Sahrens argv += optind; 2009789Sahrens 2010789Sahrens /* check number of arguments */ 2011789Sahrens if (argc < 1) { 2012789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 20132082Seschrock usage(B_FALSE); 2014789Sahrens } 2015789Sahrens if (argc > 1) { 2016789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 20172082Seschrock usage(B_FALSE); 2018789Sahrens } 2019789Sahrens 2020789Sahrens if (isatty(STDIN_FILENO)) { 2021789Sahrens (void) fprintf(stderr, 2022789Sahrens gettext("Error: Backup stream can not be read " 2023789Sahrens "from a terminal.\n" 2024789Sahrens "You must redirect standard input.\n")); 2025789Sahrens return (1); 2026789Sahrens } 2027789Sahrens 2028*2665Snd150628 err = zfs_receive(g_zfs, argv[0], isprefix, verbose, dryrun, force); 2029789Sahrens return (err != 0); 2030789Sahrens } 2031789Sahrens 20321356Seschrock typedef struct get_all_cbdata { 20331356Seschrock zfs_handle_t **cb_handles; 20341356Seschrock size_t cb_alloc; 20351356Seschrock size_t cb_used; 20361356Seschrock } get_all_cbdata_t; 20371356Seschrock 20381356Seschrock static int 20391356Seschrock get_one_filesystem(zfs_handle_t *zhp, void *data) 20401356Seschrock { 20411356Seschrock get_all_cbdata_t *cbp = data; 20421356Seschrock 20431356Seschrock /* 20441356Seschrock * Skip any zvols 20451356Seschrock */ 20461356Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 20471356Seschrock zfs_close(zhp); 20481356Seschrock return (0); 20491356Seschrock } 20501356Seschrock 20511356Seschrock if (cbp->cb_alloc == cbp->cb_used) { 20521356Seschrock zfs_handle_t **handles; 20531356Seschrock 20541356Seschrock if (cbp->cb_alloc == 0) 20551356Seschrock cbp->cb_alloc = 64; 20561356Seschrock else 20571356Seschrock cbp->cb_alloc *= 2; 20581356Seschrock 20591356Seschrock handles = safe_malloc(cbp->cb_alloc * sizeof (void *)); 20601356Seschrock 20611356Seschrock if (cbp->cb_handles) { 20621356Seschrock bcopy(cbp->cb_handles, handles, 20631356Seschrock cbp->cb_used * sizeof (void *)); 20641356Seschrock free(cbp->cb_handles); 20651356Seschrock } 20661356Seschrock 20671356Seschrock cbp->cb_handles = handles; 20681356Seschrock } 20691356Seschrock 20701356Seschrock cbp->cb_handles[cbp->cb_used++] = zhp; 20711356Seschrock 20721356Seschrock return (zfs_iter_filesystems(zhp, get_one_filesystem, data)); 20731356Seschrock } 20741356Seschrock 20751356Seschrock static void 20761356Seschrock get_all_filesystems(zfs_handle_t ***fslist, size_t *count) 20771356Seschrock { 20781356Seschrock get_all_cbdata_t cb = { 0 }; 20791356Seschrock 20802082Seschrock (void) zfs_iter_root(g_zfs, get_one_filesystem, &cb); 20811356Seschrock 20821356Seschrock *fslist = cb.cb_handles; 20831356Seschrock *count = cb.cb_used; 20841356Seschrock } 20851356Seschrock 20861356Seschrock static int 20871356Seschrock mountpoint_compare(const void *a, const void *b) 20881356Seschrock { 20891356Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 20901356Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 20911356Seschrock char mounta[MAXPATHLEN]; 20921356Seschrock char mountb[MAXPATHLEN]; 20931356Seschrock 20941356Seschrock verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 20952082Seschrock sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 20961356Seschrock verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 20972082Seschrock sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 20981356Seschrock 20991356Seschrock return (strcmp(mounta, mountb)); 21001356Seschrock } 2101789Sahrens 2102789Sahrens /* 2103789Sahrens * Generic callback for sharing or mounting filesystems. Because the code is so 2104789Sahrens * similar, we have a common function with an extra parameter to determine which 2105789Sahrens * mode we are using. 2106789Sahrens */ 2107789Sahrens #define OP_SHARE 0x1 2108789Sahrens #define OP_MOUNT 0x2 2109789Sahrens 2110789Sahrens typedef struct share_mount_cbdata { 2111789Sahrens int cb_type; 2112789Sahrens int cb_explicit; 2113789Sahrens int cb_flags; 2114789Sahrens const char *cb_options; 2115789Sahrens } share_mount_cbdata_t; 2116789Sahrens 2117789Sahrens /* 2118789Sahrens * Share or mount the filesystem. 2119789Sahrens */ 2120789Sahrens static int 2121789Sahrens share_mount_callback(zfs_handle_t *zhp, void *data) 2122789Sahrens { 2123789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2124789Sahrens char shareopts[ZFS_MAXPROPLEN]; 2125789Sahrens share_mount_cbdata_t *cbp = data; 2126789Sahrens const char *cmdname = cbp->cb_type == OP_SHARE ? "share" : "mount"; 2127789Sahrens struct mnttab mnt; 2128789Sahrens uint64_t zoned; 2129789Sahrens 2130789Sahrens if (cbp->cb_options == NULL) 2131789Sahrens mnt.mnt_mntopts = ""; 2132789Sahrens else 2133789Sahrens mnt.mnt_mntopts = (char *)cbp->cb_options; 2134789Sahrens 2135789Sahrens /* 2136789Sahrens * Check to make sure we can mount/share this dataset. If we are in the 2137789Sahrens * global zone and the filesystem is exported to a local zone, or if we 2138789Sahrens * are in a local zone and the filesystem is not exported, then it is an 2139789Sahrens * error. 2140789Sahrens */ 2141789Sahrens zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 2142789Sahrens 2143789Sahrens if (zoned && getzoneid() == GLOBAL_ZONEID) { 2144789Sahrens if (!cbp->cb_explicit) 2145789Sahrens return (0); 2146789Sahrens 2147789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': dataset is " 2148789Sahrens "exported to a local zone\n"), cmdname, zfs_get_name(zhp)); 2149789Sahrens return (1); 2150789Sahrens 2151789Sahrens } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 2152789Sahrens if (!cbp->cb_explicit) 2153789Sahrens return (0); 2154789Sahrens 2155789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': permission " 2156789Sahrens "denied\n"), cmdname, zfs_get_name(zhp)); 2157789Sahrens return (1); 2158789Sahrens } 2159789Sahrens 2160789Sahrens /* 2161789Sahrens * Inore any filesystems which don't apply to us. This includes those 2162789Sahrens * with a legacy mountpoint, or those with legacy share options. 2163789Sahrens */ 2164789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 21652082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); 2166789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 21672082Seschrock sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 2168789Sahrens 2169789Sahrens if (cbp->cb_type == OP_SHARE) { 2170789Sahrens if (strcmp(shareopts, "off") == 0) { 2171789Sahrens if (!cbp->cb_explicit) 2172789Sahrens return (0); 2173789Sahrens 2174789Sahrens (void) fprintf(stderr, gettext("cannot share '%s': " 2175789Sahrens "legacy share\n"), zfs_get_name(zhp)); 2176789Sahrens (void) fprintf(stderr, gettext("use share(1M) to " 2177789Sahrens "share this filesystem\n")); 2178789Sahrens return (1); 2179789Sahrens } 2180789Sahrens } 2181789Sahrens 2182789Sahrens /* 2183789Sahrens * We cannot share or mount legacy filesystems. If the shareopts is 2184789Sahrens * non-legacy but the mountpoint is legacy, we treat it as a legacy 2185789Sahrens * share. 2186789Sahrens */ 2187789Sahrens if (strcmp(mountpoint, "legacy") == 0) { 2188789Sahrens if (!cbp->cb_explicit) 2189789Sahrens return (0); 2190789Sahrens 2191789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': " 2192789Sahrens "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 2193789Sahrens (void) fprintf(stderr, gettext("use %s to " 2194789Sahrens "%s this filesystem\n"), cbp->cb_type == OP_SHARE ? 2195789Sahrens "share(1M)" : "mount(1M)", cmdname); 2196789Sahrens return (1); 2197789Sahrens } 2198789Sahrens 2199789Sahrens if (strcmp(mountpoint, "none") == 0) { 2200789Sahrens if (!cbp->cb_explicit) 2201789Sahrens return (0); 2202789Sahrens 2203789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': no " 2204789Sahrens "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 2205789Sahrens return (1); 2206789Sahrens } 2207789Sahrens 2208789Sahrens /* 2209789Sahrens * At this point, we have verified that the mountpoint and/or shareopts 2210789Sahrens * are appropriate for auto management. Determine if the filesystem is 2211789Sahrens * currently mounted or shared, and abort if this is an explicit 2212789Sahrens * request. 2213789Sahrens */ 2214789Sahrens switch (cbp->cb_type) { 2215789Sahrens case OP_SHARE: 2216789Sahrens if (zfs_is_shared(zhp, NULL)) { 2217789Sahrens if (cbp->cb_explicit) { 2218789Sahrens (void) fprintf(stderr, gettext("cannot share " 2219789Sahrens "'%s': filesystem already shared\n"), 2220789Sahrens zfs_get_name(zhp)); 2221789Sahrens return (1); 2222789Sahrens } else { 2223789Sahrens return (0); 2224789Sahrens } 2225789Sahrens } 2226789Sahrens break; 2227789Sahrens 2228789Sahrens case OP_MOUNT: 2229789Sahrens if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 2230789Sahrens zfs_is_mounted(zhp, NULL)) { 2231789Sahrens if (cbp->cb_explicit) { 2232789Sahrens (void) fprintf(stderr, gettext("cannot mount " 2233789Sahrens "'%s': filesystem already mounted\n"), 2234789Sahrens zfs_get_name(zhp)); 2235789Sahrens return (1); 2236789Sahrens } else { 2237789Sahrens return (0); 2238789Sahrens } 2239789Sahrens } 2240789Sahrens break; 2241789Sahrens } 2242789Sahrens 2243789Sahrens /* 2244789Sahrens * Mount and optionally share the filesystem. 2245789Sahrens */ 2246789Sahrens switch (cbp->cb_type) { 2247789Sahrens case OP_SHARE: 2248789Sahrens { 2249789Sahrens if (!zfs_is_mounted(zhp, NULL) && 2250789Sahrens zfs_mount(zhp, NULL, 0) != 0) 2251789Sahrens return (1); 2252789Sahrens 2253789Sahrens if (zfs_share(zhp) != 0) 2254789Sahrens return (1); 2255789Sahrens } 2256789Sahrens break; 2257789Sahrens 2258789Sahrens case OP_MOUNT: 2259789Sahrens if (zfs_mount(zhp, cbp->cb_options, cbp->cb_flags) != 0) 2260789Sahrens return (1); 2261789Sahrens break; 2262789Sahrens } 2263789Sahrens 2264789Sahrens return (0); 2265789Sahrens } 2266789Sahrens 2267789Sahrens static int 2268789Sahrens share_or_mount(int type, int argc, char **argv) 2269789Sahrens { 2270789Sahrens int do_all = 0; 22712372Slling int c, ret = 0; 2272789Sahrens share_mount_cbdata_t cb = { 0 }; 2273789Sahrens 2274789Sahrens cb.cb_type = type; 2275789Sahrens 2276789Sahrens /* check options */ 2277789Sahrens while ((c = getopt(argc, argv, type == OP_MOUNT ? ":ao:O" : "a")) 2278789Sahrens != -1) { 2279789Sahrens switch (c) { 2280789Sahrens case 'a': 2281789Sahrens do_all = 1; 2282789Sahrens break; 2283789Sahrens case 'o': 2284789Sahrens cb.cb_options = optarg; 2285789Sahrens break; 2286789Sahrens case 'O': 2287789Sahrens cb.cb_flags |= MS_OVERLAY; 2288789Sahrens break; 2289789Sahrens case ':': 2290789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2291789Sahrens "'%c' option\n"), optopt); 22922082Seschrock usage(B_FALSE); 2293789Sahrens break; 2294789Sahrens case '?': 2295789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2296789Sahrens optopt); 22972082Seschrock usage(B_FALSE); 2298789Sahrens } 2299789Sahrens } 2300789Sahrens 2301789Sahrens argc -= optind; 2302789Sahrens argv += optind; 2303789Sahrens 2304789Sahrens /* check number of arguments */ 2305789Sahrens if (do_all) { 23061356Seschrock zfs_handle_t **fslist = NULL; 23071356Seschrock size_t i, count = 0; 23081356Seschrock 2309789Sahrens if (argc != 0) { 2310789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 23112082Seschrock usage(B_FALSE); 2312789Sahrens } 2313789Sahrens 23141356Seschrock get_all_filesystems(&fslist, &count); 23151356Seschrock 23161356Seschrock if (count == 0) 23171356Seschrock return (0); 23181356Seschrock 23191356Seschrock qsort(fslist, count, sizeof (void *), mountpoint_compare); 23201356Seschrock 23211356Seschrock for (i = 0; i < count; i++) { 23222369Slling if (share_mount_callback(fslist[i], &cb) != 0) 23232369Slling ret = 1; 23241356Seschrock } 23251356Seschrock 23261356Seschrock for (i = 0; i < count; i++) 23271356Seschrock zfs_close(fslist[i]); 23281356Seschrock 23291356Seschrock free(fslist); 2330789Sahrens } else if (argc == 0) { 2331789Sahrens struct mnttab entry; 2332789Sahrens 2333789Sahrens if (type == OP_SHARE) { 2334789Sahrens (void) fprintf(stderr, gettext("missing filesystem " 2335789Sahrens "argument\n")); 23362082Seschrock usage(B_FALSE); 2337789Sahrens } 2338789Sahrens 2339789Sahrens /* 2340789Sahrens * When mount is given no arguments, go through /etc/mnttab and 2341789Sahrens * display any active ZFS mounts. We hide any snapshots, since 2342789Sahrens * they are controlled automatically. 2343789Sahrens */ 2344789Sahrens rewind(mnttab_file); 2345789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2346789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 2347789Sahrens strchr(entry.mnt_special, '@') != NULL) 2348789Sahrens continue; 2349789Sahrens 2350789Sahrens (void) printf("%-30s %s\n", entry.mnt_special, 2351789Sahrens entry.mnt_mountp); 2352789Sahrens } 2353789Sahrens 2354789Sahrens } else { 2355789Sahrens zfs_handle_t *zhp; 2356789Sahrens 2357789Sahrens if (argc > 1) { 2358789Sahrens (void) fprintf(stderr, 2359789Sahrens gettext("too many arguments\n")); 23602082Seschrock usage(B_FALSE); 2361789Sahrens } 2362789Sahrens 23632082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], 23642082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2365789Sahrens ret = 1; 2366789Sahrens else { 23672082Seschrock cb.cb_explicit = B_TRUE; 2368789Sahrens ret = share_mount_callback(zhp, &cb); 2369789Sahrens zfs_close(zhp); 2370789Sahrens } 2371789Sahrens } 2372789Sahrens 2373789Sahrens return (ret); 2374789Sahrens } 2375789Sahrens 2376789Sahrens /* 2377789Sahrens * zfs mount -a 2378789Sahrens * zfs mount filesystem 2379789Sahrens * 2380789Sahrens * Mount all filesystems, or mount the given filesystem. 2381789Sahrens */ 2382789Sahrens static int 2383789Sahrens zfs_do_mount(int argc, char **argv) 2384789Sahrens { 2385789Sahrens return (share_or_mount(OP_MOUNT, argc, argv)); 2386789Sahrens } 2387789Sahrens 2388789Sahrens /* 2389789Sahrens * zfs share -a 2390789Sahrens * zfs share filesystem 2391789Sahrens * 2392789Sahrens * Share all filesystems, or share the given filesystem. 2393789Sahrens */ 2394789Sahrens static int 2395789Sahrens zfs_do_share(int argc, char **argv) 2396789Sahrens { 2397789Sahrens return (share_or_mount(OP_SHARE, argc, argv)); 2398789Sahrens } 2399789Sahrens 2400789Sahrens typedef struct unshare_unmount_node { 2401789Sahrens zfs_handle_t *un_zhp; 2402789Sahrens char *un_mountp; 2403789Sahrens uu_avl_node_t un_avlnode; 2404789Sahrens } unshare_unmount_node_t; 2405789Sahrens 2406789Sahrens /* ARGSUSED */ 2407789Sahrens static int 2408789Sahrens unshare_unmount_compare(const void *larg, const void *rarg, void *unused) 2409789Sahrens { 2410789Sahrens const unshare_unmount_node_t *l = larg; 2411789Sahrens const unshare_unmount_node_t *r = rarg; 2412789Sahrens 2413789Sahrens return (strcmp(l->un_mountp, r->un_mountp)); 2414789Sahrens } 2415789Sahrens 2416789Sahrens /* 2417789Sahrens * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 2418789Sahrens * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 2419789Sahrens * and unmount it appropriately. 2420789Sahrens */ 2421789Sahrens static int 24222082Seschrock unshare_unmount_path(int type, char *path, int flags, boolean_t is_manual) 2423789Sahrens { 2424789Sahrens zfs_handle_t *zhp; 2425789Sahrens int ret; 2426789Sahrens struct stat64 statbuf; 2427789Sahrens struct extmnttab entry; 2428789Sahrens const char *cmdname = (type == OP_SHARE) ? "unshare" : "unmount"; 2429789Sahrens char property[ZFS_MAXPROPLEN]; 2430789Sahrens 2431789Sahrens /* 2432789Sahrens * Search for the path in /etc/mnttab. Rather than looking for the 2433789Sahrens * specific path, which can be fooled by non-standard paths (i.e. ".." 2434789Sahrens * or "//"), we stat() the path and search for the corresponding 2435789Sahrens * (major,minor) device pair. 2436789Sahrens */ 2437789Sahrens if (stat64(path, &statbuf) != 0) { 2438789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 2439789Sahrens cmdname, path, strerror(errno)); 2440789Sahrens return (1); 2441789Sahrens } 2442789Sahrens 2443789Sahrens /* 2444789Sahrens * Search for the given (major,minor) pair in the mount table. 2445789Sahrens */ 2446789Sahrens rewind(mnttab_file); 2447789Sahrens while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 2448789Sahrens if (entry.mnt_major == major(statbuf.st_dev) && 2449789Sahrens entry.mnt_minor == minor(statbuf.st_dev)) 2450789Sahrens break; 2451789Sahrens } 2452789Sahrens if (ret != 0) { 2453789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not " 2454789Sahrens "currently mounted\n"), cmdname, path); 2455789Sahrens return (1); 2456789Sahrens } 2457789Sahrens 2458789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 2459789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 2460789Sahrens "filesystem\n"), cmdname, path); 2461789Sahrens return (1); 2462789Sahrens } 2463789Sahrens 24642082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 24652082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2466789Sahrens return (1); 2467789Sahrens 2468789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2469789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 24702082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2471789Sahrens 2472789Sahrens if (type == OP_SHARE) { 2473789Sahrens if (strcmp(property, "off") == 0) { 2474789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2475789Sahrens "'%s': legacy share\n"), path); 2476789Sahrens (void) fprintf(stderr, gettext("use " 2477789Sahrens "unshare(1M) to unshare this filesystem\n")); 2478789Sahrens ret = 1; 2479789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2480789Sahrens (void) fprintf(stderr, gettext("cannot unshare '%s': " 2481789Sahrens "not currently shared\n"), path); 2482789Sahrens ret = 1; 2483789Sahrens } else { 2484789Sahrens ret = zfs_unshareall(zhp); 2485789Sahrens } 2486789Sahrens } else { 24871264Slling if (is_manual) { 24881264Slling ret = zfs_unmount(zhp, NULL, flags); 24891264Slling } else if (strcmp(property, "legacy") == 0) { 24901264Slling (void) fprintf(stderr, gettext("cannot unmount " 24911264Slling "'%s': legacy mountpoint\n"), 24921264Slling zfs_get_name(zhp)); 24931264Slling (void) fprintf(stderr, gettext("use umount(1M) " 24941264Slling "to unmount this filesystem\n")); 24951264Slling ret = 1; 2496789Sahrens } else { 2497789Sahrens ret = zfs_unmountall(zhp, flags); 2498789Sahrens } 2499789Sahrens } 2500789Sahrens 2501789Sahrens zfs_close(zhp); 2502789Sahrens 2503789Sahrens return (ret != 0); 2504789Sahrens } 2505789Sahrens 2506789Sahrens /* 2507789Sahrens * Generic callback for unsharing or unmounting a filesystem. 2508789Sahrens */ 2509789Sahrens static int 2510789Sahrens unshare_unmount(int type, int argc, char **argv) 2511789Sahrens { 2512789Sahrens int do_all = 0; 2513789Sahrens int flags = 0; 2514789Sahrens int ret = 0; 2515789Sahrens int c; 2516789Sahrens zfs_handle_t *zhp; 2517789Sahrens char property[ZFS_MAXPROPLEN]; 2518789Sahrens 2519789Sahrens /* check options */ 2520789Sahrens while ((c = getopt(argc, argv, type == OP_SHARE ? "a" : "af")) != -1) { 2521789Sahrens switch (c) { 2522789Sahrens case 'a': 2523789Sahrens do_all = 1; 2524789Sahrens break; 2525789Sahrens case 'f': 2526789Sahrens flags = MS_FORCE; 2527789Sahrens break; 2528789Sahrens case '?': 2529789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2530789Sahrens optopt); 25312082Seschrock usage(B_FALSE); 2532789Sahrens } 2533789Sahrens } 2534789Sahrens 2535789Sahrens argc -= optind; 2536789Sahrens argv += optind; 2537789Sahrens 2538789Sahrens /* ensure correct number of arguments */ 2539789Sahrens if (do_all) { 2540789Sahrens if (argc != 0) { 2541789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 25422082Seschrock usage(B_FALSE); 2543789Sahrens } 2544789Sahrens } else if (argc != 1) { 2545789Sahrens if (argc == 0) 2546789Sahrens (void) fprintf(stderr, 2547789Sahrens gettext("missing filesystem argument\n")); 2548789Sahrens else 2549789Sahrens (void) fprintf(stderr, 2550789Sahrens gettext("too many arguments\n")); 25512082Seschrock usage(B_FALSE); 2552789Sahrens } 2553789Sahrens 2554789Sahrens if (do_all) { 2555789Sahrens /* 2556789Sahrens * We could make use of zfs_for_each() to walk all datasets in 2557789Sahrens * the system, but this would be very inefficient, especially 2558789Sahrens * since we would have to linearly search /etc/mnttab for each 2559789Sahrens * one. Instead, do one pass through /etc/mnttab looking for 2560789Sahrens * zfs entries and call zfs_unmount() for each one. 2561789Sahrens * 2562789Sahrens * Things get a little tricky if the administrator has created 2563789Sahrens * mountpoints beneath other ZFS filesystems. In this case, we 2564789Sahrens * have to unmount the deepest filesystems first. To accomplish 2565789Sahrens * this, we place all the mountpoints in an AVL tree sorted by 2566789Sahrens * the special type (dataset name), and walk the result in 2567789Sahrens * reverse to make sure to get any snapshots first. 2568789Sahrens */ 2569789Sahrens struct mnttab entry; 2570789Sahrens uu_avl_pool_t *pool; 2571789Sahrens uu_avl_t *tree; 2572789Sahrens unshare_unmount_node_t *node; 2573789Sahrens uu_avl_index_t idx; 2574789Sahrens uu_avl_walk_t *walk; 2575789Sahrens 2576789Sahrens if ((pool = uu_avl_pool_create("unmount_pool", 2577789Sahrens sizeof (unshare_unmount_node_t), 2578789Sahrens offsetof(unshare_unmount_node_t, un_avlnode), 2579789Sahrens unshare_unmount_compare, 2580789Sahrens UU_DEFAULT)) == NULL) { 2581789Sahrens (void) fprintf(stderr, gettext("internal error: " 2582789Sahrens "out of memory\n")); 2583789Sahrens exit(1); 2584789Sahrens } 2585789Sahrens 2586789Sahrens if ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL) { 2587789Sahrens (void) fprintf(stderr, gettext("internal error: " 2588789Sahrens "out of memory\n")); 2589789Sahrens exit(1); 2590789Sahrens } 2591789Sahrens 2592789Sahrens rewind(mnttab_file); 2593789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2594789Sahrens 2595789Sahrens /* ignore non-ZFS entries */ 2596789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 2597789Sahrens continue; 2598789Sahrens 2599789Sahrens /* ignore snapshots */ 2600789Sahrens if (strchr(entry.mnt_special, '@') != NULL) 2601789Sahrens continue; 2602789Sahrens 26032082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 2604789Sahrens ZFS_TYPE_FILESYSTEM)) == NULL) { 2605789Sahrens ret = 1; 2606789Sahrens continue; 2607789Sahrens } 2608789Sahrens 2609789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2610789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 2611789Sahrens property, sizeof (property), NULL, NULL, 26122082Seschrock 0, B_FALSE) == 0); 2613789Sahrens 2614789Sahrens /* Ignore legacy mounts and shares */ 2615789Sahrens if ((type == OP_SHARE && 2616789Sahrens strcmp(property, "off") == 0) || 2617789Sahrens (type == OP_MOUNT && 2618789Sahrens strcmp(property, "legacy") == 0)) { 2619789Sahrens zfs_close(zhp); 2620789Sahrens continue; 2621789Sahrens } 2622789Sahrens 2623789Sahrens node = safe_malloc(sizeof (unshare_unmount_node_t)); 2624789Sahrens node->un_zhp = zhp; 2625789Sahrens 2626789Sahrens if ((node->un_mountp = strdup(entry.mnt_mountp)) == 2627789Sahrens NULL) { 2628789Sahrens (void) fprintf(stderr, gettext("internal error:" 2629789Sahrens " out of memory\n")); 2630789Sahrens exit(1); 2631789Sahrens } 2632789Sahrens 2633789Sahrens uu_avl_node_init(node, &node->un_avlnode, pool); 2634789Sahrens 2635789Sahrens if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 2636789Sahrens uu_avl_insert(tree, node, idx); 2637789Sahrens } else { 2638789Sahrens zfs_close(node->un_zhp); 2639789Sahrens free(node->un_mountp); 2640789Sahrens free(node); 2641789Sahrens } 2642789Sahrens } 2643789Sahrens 2644789Sahrens /* 2645789Sahrens * Walk the AVL tree in reverse, unmounting each filesystem and 2646789Sahrens * removing it from the AVL tree in the process. 2647789Sahrens */ 2648789Sahrens if ((walk = uu_avl_walk_start(tree, 2649789Sahrens UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) { 2650789Sahrens (void) fprintf(stderr, 2651789Sahrens gettext("internal error: out of memory")); 2652789Sahrens exit(1); 2653789Sahrens } 2654789Sahrens 2655789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 2656789Sahrens uu_avl_remove(tree, node); 2657789Sahrens 2658789Sahrens switch (type) { 2659789Sahrens case OP_SHARE: 2660789Sahrens if (zfs_unshare(node->un_zhp, 2661789Sahrens node->un_mountp) != 0) 2662789Sahrens ret = 1; 2663789Sahrens break; 2664789Sahrens 2665789Sahrens case OP_MOUNT: 2666789Sahrens if (zfs_unmount(node->un_zhp, 2667789Sahrens node->un_mountp, flags) != 0) 2668789Sahrens ret = 1; 2669789Sahrens break; 2670789Sahrens } 2671789Sahrens 2672789Sahrens zfs_close(node->un_zhp); 2673789Sahrens free(node->un_mountp); 2674789Sahrens free(node); 2675789Sahrens } 2676789Sahrens 2677789Sahrens uu_avl_walk_end(walk); 2678789Sahrens uu_avl_destroy(tree); 2679789Sahrens uu_avl_pool_destroy(pool); 2680789Sahrens } else { 2681789Sahrens /* 2682789Sahrens * We have an argument, but it may be a full path or a ZFS 2683789Sahrens * filesystem. Pass full paths off to unmount_path() (shared by 2684789Sahrens * manual_unmount), otherwise open the filesystem and pass to 2685789Sahrens * zfs_unmount(). 2686789Sahrens */ 2687789Sahrens if (argv[0][0] == '/') 2688789Sahrens return (unshare_unmount_path(type, argv[0], 26892082Seschrock flags, B_FALSE)); 26902082Seschrock 26912082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], 26922082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2693789Sahrens return (1); 2694789Sahrens 2695789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2696789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 26972082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2698789Sahrens 2699789Sahrens switch (type) { 2700789Sahrens case OP_SHARE: 2701789Sahrens if (strcmp(property, "off") == 0) { 2702789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2703789Sahrens "'%s': legacy share\n"), zfs_get_name(zhp)); 2704789Sahrens (void) fprintf(stderr, gettext("use unshare(1M)" 2705789Sahrens " to unshare this filesystem\n")); 2706789Sahrens ret = 1; 2707789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2708789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2709789Sahrens "'%s': not currently shared\n"), 2710789Sahrens zfs_get_name(zhp)); 2711789Sahrens ret = 1; 2712789Sahrens } else if (zfs_unshareall(zhp) != 0) { 2713789Sahrens ret = 1; 2714789Sahrens } 2715789Sahrens break; 2716789Sahrens 2717789Sahrens case OP_MOUNT: 2718789Sahrens if (strcmp(property, "legacy") == 0) { 2719789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2720789Sahrens "'%s': legacy mountpoint\n"), 2721789Sahrens zfs_get_name(zhp)); 2722789Sahrens (void) fprintf(stderr, gettext("use umount(1M) " 2723789Sahrens "to unmount this filesystem\n")); 2724789Sahrens ret = 1; 2725789Sahrens } else if (!zfs_is_mounted(zhp, NULL)) { 2726789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2727789Sahrens "'%s': not currently mounted\n"), 2728789Sahrens zfs_get_name(zhp)); 2729789Sahrens ret = 1; 2730789Sahrens } else if (zfs_unmountall(zhp, flags) != 0) { 2731789Sahrens ret = 1; 2732789Sahrens } 2733789Sahrens } 2734789Sahrens 2735789Sahrens zfs_close(zhp); 2736789Sahrens } 2737789Sahrens 2738789Sahrens return (ret); 2739789Sahrens } 2740789Sahrens 2741789Sahrens /* 2742789Sahrens * zfs unmount -a 2743789Sahrens * zfs unmount filesystem 2744789Sahrens * 2745789Sahrens * Unmount all filesystems, or a specific ZFS filesystem. 2746789Sahrens */ 2747789Sahrens static int 2748789Sahrens zfs_do_unmount(int argc, char **argv) 2749789Sahrens { 2750789Sahrens return (unshare_unmount(OP_MOUNT, argc, argv)); 2751789Sahrens } 2752789Sahrens 2753789Sahrens /* 2754789Sahrens * zfs unshare -a 2755789Sahrens * zfs unshare filesystem 2756789Sahrens * 2757789Sahrens * Unshare all filesystems, or a specific ZFS filesystem. 2758789Sahrens */ 2759789Sahrens static int 2760789Sahrens zfs_do_unshare(int argc, char **argv) 2761789Sahrens { 2762789Sahrens return (unshare_unmount(OP_SHARE, argc, argv)); 2763789Sahrens } 2764789Sahrens 2765789Sahrens /* 2766789Sahrens * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 2767789Sahrens * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 2768789Sahrens */ 2769789Sahrens static int 2770789Sahrens manual_mount(int argc, char **argv) 2771789Sahrens { 2772789Sahrens zfs_handle_t *zhp; 2773789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2774789Sahrens char mntopts[MNT_LINE_MAX] = { '\0' }; 2775789Sahrens int ret; 2776789Sahrens int c; 2777789Sahrens int flags = 0; 2778789Sahrens char *dataset, *path; 2779789Sahrens 2780789Sahrens /* check options */ 27811544Seschrock while ((c = getopt(argc, argv, ":mo:O")) != -1) { 2782789Sahrens switch (c) { 2783789Sahrens case 'o': 2784789Sahrens (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 2785789Sahrens break; 2786789Sahrens case 'O': 2787789Sahrens flags |= MS_OVERLAY; 2788789Sahrens break; 27891544Seschrock case 'm': 27901544Seschrock flags |= MS_NOMNTTAB; 27911544Seschrock break; 2792789Sahrens case ':': 2793789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2794789Sahrens "'%c' option\n"), optopt); 27952082Seschrock usage(B_FALSE); 2796789Sahrens break; 2797789Sahrens case '?': 2798789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2799789Sahrens optopt); 2800789Sahrens (void) fprintf(stderr, gettext("usage: mount [-o opts] " 2801789Sahrens "<path>\n")); 2802789Sahrens return (2); 2803789Sahrens } 2804789Sahrens } 2805789Sahrens 2806789Sahrens argc -= optind; 2807789Sahrens argv += optind; 2808789Sahrens 2809789Sahrens /* check that we only have two arguments */ 2810789Sahrens if (argc != 2) { 2811789Sahrens if (argc == 0) 2812789Sahrens (void) fprintf(stderr, gettext("missing dataset " 2813789Sahrens "argument\n")); 2814789Sahrens else if (argc == 1) 2815789Sahrens (void) fprintf(stderr, 2816789Sahrens gettext("missing mountpoint argument\n")); 2817789Sahrens else 2818789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2819789Sahrens (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 2820789Sahrens return (2); 2821789Sahrens } 2822789Sahrens 2823789Sahrens dataset = argv[0]; 2824789Sahrens path = argv[1]; 2825789Sahrens 2826789Sahrens /* try to open the dataset */ 28272082Seschrock if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 2828789Sahrens return (1); 2829789Sahrens 2830789Sahrens (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 28312082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE); 2832789Sahrens 2833789Sahrens /* check for legacy mountpoint and complain appropriately */ 2834789Sahrens ret = 0; 2835789Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 2836789Sahrens if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS, 2837789Sahrens NULL, 0, mntopts, sizeof (mntopts)) != 0) { 2838789Sahrens (void) fprintf(stderr, gettext("mount failed: %s\n"), 2839789Sahrens strerror(errno)); 2840789Sahrens ret = 1; 2841789Sahrens } 2842789Sahrens } else { 2843789Sahrens (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 2844789Sahrens "mounted using 'mount -F zfs'\n"), dataset); 2845789Sahrens (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 2846789Sahrens "instead.\n"), path); 2847789Sahrens (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' " 2848789Sahrens "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n")); 2849789Sahrens (void) fprintf(stderr, gettext("See zfs(1M) for more " 2850789Sahrens "information.\n")); 2851789Sahrens ret = 1; 2852789Sahrens } 2853789Sahrens 2854789Sahrens return (ret); 2855789Sahrens } 2856789Sahrens 2857789Sahrens /* 2858789Sahrens * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 2859789Sahrens * unmounts of non-legacy filesystems, as this is the dominant administrative 2860789Sahrens * interface. 2861789Sahrens */ 2862789Sahrens static int 2863789Sahrens manual_unmount(int argc, char **argv) 2864789Sahrens { 2865789Sahrens int flags = 0; 2866789Sahrens int c; 2867789Sahrens 2868789Sahrens /* check options */ 2869789Sahrens while ((c = getopt(argc, argv, "f")) != -1) { 2870789Sahrens switch (c) { 2871789Sahrens case 'f': 2872789Sahrens flags = MS_FORCE; 2873789Sahrens break; 2874789Sahrens case '?': 2875789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2876789Sahrens optopt); 2877789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] " 2878789Sahrens "<path>\n")); 2879789Sahrens return (2); 2880789Sahrens } 2881789Sahrens } 2882789Sahrens 2883789Sahrens argc -= optind; 2884789Sahrens argv += optind; 2885789Sahrens 2886789Sahrens /* check arguments */ 2887789Sahrens if (argc != 1) { 2888789Sahrens if (argc == 0) 2889789Sahrens (void) fprintf(stderr, gettext("missing path " 2890789Sahrens "argument\n")); 2891789Sahrens else 2892789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2893789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 2894789Sahrens return (2); 2895789Sahrens } 2896789Sahrens 28972082Seschrock return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE)); 2898789Sahrens } 2899789Sahrens 2900789Sahrens static int 2901789Sahrens volcheck(zpool_handle_t *zhp, void *data) 2902789Sahrens { 2903789Sahrens int isinit = (int)data; 2904789Sahrens 2905789Sahrens if (isinit) 2906789Sahrens return (zpool_create_zvol_links(zhp)); 2907789Sahrens else 2908789Sahrens return (zpool_remove_zvol_links(zhp)); 2909789Sahrens } 2910789Sahrens 2911789Sahrens /* 2912789Sahrens * Iterate over all pools in the system and either create or destroy /dev/zvol 2913789Sahrens * links, depending on the value of 'isinit'. 2914789Sahrens */ 2915789Sahrens static int 29162082Seschrock do_volcheck(boolean_t isinit) 2917789Sahrens { 29182082Seschrock return (zpool_iter(g_zfs, volcheck, (void *)isinit) ? 1 : 0); 2919789Sahrens } 2920789Sahrens 2921789Sahrens int 2922789Sahrens main(int argc, char **argv) 2923789Sahrens { 2924789Sahrens int ret; 2925789Sahrens int i; 2926789Sahrens char *progname; 2927789Sahrens char *cmdname; 2928789Sahrens 2929789Sahrens (void) setlocale(LC_ALL, ""); 2930789Sahrens (void) textdomain(TEXT_DOMAIN); 2931789Sahrens 2932789Sahrens opterr = 0; 2933789Sahrens 29342082Seschrock if ((g_zfs = libzfs_init()) == NULL) { 29352082Seschrock (void) fprintf(stderr, gettext("internal error: failed to " 29362082Seschrock "initialize ZFS library\n")); 29372082Seschrock return (1); 29382082Seschrock } 29392082Seschrock 29402082Seschrock libzfs_print_on_error(g_zfs, B_TRUE); 29412082Seschrock 2942789Sahrens if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 2943789Sahrens (void) fprintf(stderr, gettext("internal error: unable to " 2944789Sahrens "open %s\n"), MNTTAB); 2945789Sahrens return (1); 2946789Sahrens } 2947789Sahrens 2948789Sahrens /* 2949789Sahrens * This command also doubles as the /etc/fs mount and unmount program. 2950789Sahrens * Determine if we should take this behavior based on argv[0]. 2951789Sahrens */ 2952789Sahrens progname = basename(argv[0]); 2953789Sahrens if (strcmp(progname, "mount") == 0) { 2954789Sahrens ret = manual_mount(argc, argv); 2955789Sahrens } else if (strcmp(progname, "umount") == 0) { 2956789Sahrens ret = manual_unmount(argc, argv); 2957789Sahrens } else { 2958789Sahrens /* 2959789Sahrens * Make sure the user has specified some command. 2960789Sahrens */ 2961789Sahrens if (argc < 2) { 2962789Sahrens (void) fprintf(stderr, gettext("missing command\n")); 29632082Seschrock usage(B_FALSE); 2964789Sahrens } 2965789Sahrens 2966789Sahrens cmdname = argv[1]; 2967789Sahrens 2968789Sahrens /* 2969789Sahrens * The 'umount' command is an alias for 'unmount' 2970789Sahrens */ 2971789Sahrens if (strcmp(cmdname, "umount") == 0) 2972789Sahrens cmdname = "unmount"; 2973789Sahrens 2974789Sahrens /* 29751749Sahrens * The 'recv' command is an alias for 'receive' 29761749Sahrens */ 29771749Sahrens if (strcmp(cmdname, "recv") == 0) 29781749Sahrens cmdname = "receive"; 29791749Sahrens 29801749Sahrens /* 2981789Sahrens * Special case '-?' 2982789Sahrens */ 2983789Sahrens if (strcmp(cmdname, "-?") == 0) 29842082Seschrock usage(B_TRUE); 2985789Sahrens 2986789Sahrens /* 2987789Sahrens * 'volinit' and 'volfini' do not appear in the usage message, 2988789Sahrens * so we have to special case them here. 2989789Sahrens */ 2990789Sahrens if (strcmp(cmdname, "volinit") == 0) 29912082Seschrock return (do_volcheck(B_TRUE)); 2992789Sahrens else if (strcmp(cmdname, "volfini") == 0) 29932082Seschrock return (do_volcheck(B_FALSE)); 2994789Sahrens 2995789Sahrens /* 2996789Sahrens * Run the appropriate command. 2997789Sahrens */ 2998789Sahrens for (i = 0; i < NCOMMAND; i++) { 2999789Sahrens if (command_table[i].name == NULL) 3000789Sahrens continue; 3001789Sahrens 3002789Sahrens if (strcmp(cmdname, command_table[i].name) == 0) { 3003789Sahrens current_command = &command_table[i]; 3004789Sahrens ret = command_table[i].func(argc - 1, argv + 1); 3005789Sahrens break; 3006789Sahrens } 3007789Sahrens } 3008789Sahrens 3009789Sahrens if (i == NCOMMAND) { 3010789Sahrens (void) fprintf(stderr, gettext("unrecognized " 3011789Sahrens "command '%s'\n"), cmdname); 30122082Seschrock usage(B_FALSE); 3013789Sahrens } 3014789Sahrens } 3015789Sahrens 3016789Sahrens (void) fclose(mnttab_file); 3017789Sahrens 30182082Seschrock libzfs_fini(g_zfs); 30192082Seschrock 3020789Sahrens /* 3021789Sahrens * The 'ZFS_ABORT' environment variable causes us to dump core on exit 3022789Sahrens * for the purposes of running ::findleaks. 3023789Sahrens */ 3024789Sahrens if (getenv("ZFS_ABORT") != NULL) { 3025789Sahrens (void) printf("dumping core by request\n"); 3026789Sahrens abort(); 3027789Sahrens } 3028789Sahrens 3029789Sahrens return (ret); 3030789Sahrens } 3031