1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 51544Seschrock * Common Development and Distribution License (the "License"). 61544Seschrock * You may not use this file except in compliance with the License. 7789Sahrens * 8789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9789Sahrens * or http://www.opensolaris.org/os/licensing. 10789Sahrens * See the License for the specific language governing permissions 11789Sahrens * and limitations under the License. 12789Sahrens * 13789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15789Sahrens * If applicable, add the following below this CDDL HEADER, with the 16789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18789Sahrens * 19789Sahrens * CDDL HEADER END 20789Sahrens */ 21789Sahrens /* 221204Slling * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23789Sahrens * Use is subject to license terms. 24789Sahrens */ 25789Sahrens 26789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 27789Sahrens 28789Sahrens #include <assert.h> 29789Sahrens #include <errno.h> 30789Sahrens #include <libgen.h> 31789Sahrens #include <libintl.h> 32789Sahrens #include <libuutil.h> 33789Sahrens #include <locale.h> 34789Sahrens #include <stddef.h> 35789Sahrens #include <stdio.h> 36789Sahrens #include <stdlib.h> 37789Sahrens #include <strings.h> 38789Sahrens #include <unistd.h> 39789Sahrens #include <fcntl.h> 40789Sahrens #include <zone.h> 41789Sahrens #include <sys/mkdev.h> 42789Sahrens #include <sys/mntent.h> 43789Sahrens #include <sys/mnttab.h> 44789Sahrens #include <sys/mount.h> 45789Sahrens #include <sys/stat.h> 46789Sahrens 47789Sahrens #include <libzfs.h> 48789Sahrens 49789Sahrens #include "zfs_iter.h" 502082Seschrock #include "zfs_util.h" 512082Seschrock 522082Seschrock libzfs_handle_t *g_zfs; 53789Sahrens 54789Sahrens static FILE *mnttab_file; 55789Sahrens 56789Sahrens static int zfs_do_clone(int argc, char **argv); 57789Sahrens static int zfs_do_create(int argc, char **argv); 58789Sahrens static int zfs_do_destroy(int argc, char **argv); 59789Sahrens static int zfs_do_get(int argc, char **argv); 60789Sahrens static int zfs_do_inherit(int argc, char **argv); 61789Sahrens static int zfs_do_list(int argc, char **argv); 62789Sahrens static int zfs_do_mount(int argc, char **argv); 63789Sahrens static int zfs_do_rename(int argc, char **argv); 64789Sahrens static int zfs_do_rollback(int argc, char **argv); 65789Sahrens static int zfs_do_set(int argc, char **argv); 66789Sahrens static int zfs_do_snapshot(int argc, char **argv); 67789Sahrens static int zfs_do_unmount(int argc, char **argv); 68789Sahrens static int zfs_do_share(int argc, char **argv); 69789Sahrens static int zfs_do_unshare(int argc, char **argv); 701749Sahrens static int zfs_do_send(int argc, char **argv); 711749Sahrens static int zfs_do_receive(int argc, char **argv); 722082Seschrock static int zfs_do_promote(int argc, char **argv); 73789Sahrens 74789Sahrens /* 75789Sahrens * These libumem hooks provide a reasonable set of defaults for the allocator's 76789Sahrens * debugging facilities. 77789Sahrens */ 78789Sahrens const char * 79789Sahrens _umem_debug_init() 80789Sahrens { 81789Sahrens return ("default,verbose"); /* $UMEM_DEBUG setting */ 82789Sahrens } 83789Sahrens 84789Sahrens const char * 85789Sahrens _umem_logging_init(void) 86789Sahrens { 87789Sahrens return ("fail,contents"); /* $UMEM_LOGGING setting */ 88789Sahrens } 89789Sahrens 901387Seschrock typedef enum { 911387Seschrock HELP_CLONE, 921387Seschrock HELP_CREATE, 931387Seschrock HELP_DESTROY, 941387Seschrock HELP_GET, 951387Seschrock HELP_INHERIT, 961387Seschrock HELP_LIST, 971387Seschrock HELP_MOUNT, 982082Seschrock HELP_PROMOTE, 991749Sahrens HELP_RECEIVE, 1001387Seschrock HELP_RENAME, 1011387Seschrock HELP_ROLLBACK, 1021749Sahrens HELP_SEND, 1031387Seschrock HELP_SET, 1041387Seschrock HELP_SHARE, 1051387Seschrock HELP_SNAPSHOT, 1061387Seschrock HELP_UNMOUNT, 1071387Seschrock HELP_UNSHARE 1081387Seschrock } zfs_help_t; 1091387Seschrock 110789Sahrens typedef struct zfs_command { 111789Sahrens const char *name; 112789Sahrens int (*func)(int argc, char **argv); 1131387Seschrock zfs_help_t usage; 114789Sahrens } zfs_command_t; 115789Sahrens 116789Sahrens /* 117789Sahrens * Master command table. Each ZFS command has a name, associated function, and 1181544Seschrock * usage message. The usage messages need to be internationalized, so we have 1191544Seschrock * to have a function to return the usage message based on a command index. 1201387Seschrock * 1211387Seschrock * These commands are organized according to how they are displayed in the usage 1221387Seschrock * message. An empty command (one with a NULL name) indicates an empty line in 1231387Seschrock * the generic usage message. 124789Sahrens */ 125789Sahrens static zfs_command_t command_table[] = { 1261387Seschrock { "create", zfs_do_create, HELP_CREATE }, 1271387Seschrock { "destroy", zfs_do_destroy, HELP_DESTROY }, 128789Sahrens { NULL }, 1291387Seschrock { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT }, 1301387Seschrock { "rollback", zfs_do_rollback, HELP_ROLLBACK }, 1311387Seschrock { "clone", zfs_do_clone, HELP_CLONE }, 1322082Seschrock { "promote", zfs_do_promote, HELP_PROMOTE }, 1331387Seschrock { "rename", zfs_do_rename, HELP_RENAME }, 134789Sahrens { NULL }, 1351387Seschrock { "list", zfs_do_list, HELP_LIST }, 136789Sahrens { NULL }, 1371387Seschrock { "set", zfs_do_set, HELP_SET }, 1381387Seschrock { "get", zfs_do_get, HELP_GET }, 1391387Seschrock { "inherit", zfs_do_inherit, HELP_INHERIT }, 140789Sahrens { NULL }, 1411387Seschrock { "mount", zfs_do_mount, HELP_MOUNT }, 142789Sahrens { NULL }, 1431387Seschrock { "unmount", zfs_do_unmount, HELP_UNMOUNT }, 144789Sahrens { NULL }, 1451387Seschrock { "share", zfs_do_share, HELP_SHARE }, 146789Sahrens { NULL }, 1471387Seschrock { "unshare", zfs_do_unshare, HELP_UNSHARE }, 148789Sahrens { NULL }, 1491749Sahrens { "send", zfs_do_send, HELP_SEND }, 1501749Sahrens { "receive", zfs_do_receive, HELP_RECEIVE }, 151789Sahrens }; 152789Sahrens 153789Sahrens #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) 154789Sahrens 155789Sahrens zfs_command_t *current_command; 156789Sahrens 1571387Seschrock static const char * 1581387Seschrock get_usage(zfs_help_t idx) 1591387Seschrock { 1601387Seschrock switch (idx) { 1611387Seschrock case HELP_CLONE: 1621387Seschrock return (gettext("\tclone <snapshot> <filesystem|volume>\n")); 1631387Seschrock case HELP_CREATE: 1641387Seschrock return (gettext("\tcreate <filesystem>\n" 1651387Seschrock "\tcreate [-s] [-b blocksize] -V <size> <volume>\n")); 1661387Seschrock case HELP_DESTROY: 1671387Seschrock return (gettext("\tdestroy [-rRf] " 1681387Seschrock "<filesystem|volume|snapshot>\n")); 1691387Seschrock case HELP_GET: 1701387Seschrock return (gettext("\tget [-rHp] [-o field[,field]...] " 1711387Seschrock "[-s source[,source]...]\n" 1721387Seschrock "\t <all | property[,property]...> " 1731387Seschrock "<filesystem|volume|snapshot> ...\n")); 1741387Seschrock case HELP_INHERIT: 1751387Seschrock return (gettext("\tinherit [-r] <property> " 1761387Seschrock "<filesystem|volume> ...\n")); 1771387Seschrock case HELP_LIST: 1781387Seschrock return (gettext("\tlist [-rH] [-o property[,property]...] " 1791387Seschrock "[-t type[,type]...]\n" 1801387Seschrock "\t [filesystem|volume|snapshot] ...\n")); 1811387Seschrock case HELP_MOUNT: 1821387Seschrock return (gettext("\tmount\n" 1831387Seschrock "\tmount [-o opts] [-O] -a\n" 1841387Seschrock "\tmount [-o opts] [-O] <filesystem>\n")); 1852082Seschrock case HELP_PROMOTE: 1862082Seschrock return (gettext("\tpromote <clone filesystem>\n")); 1871749Sahrens case HELP_RECEIVE: 1881749Sahrens return (gettext("\treceive [-vn] <filesystem|volume|snapshot>\n" 1891749Sahrens "\treceive [-vn] -d <filesystem>\n")); 1901387Seschrock case HELP_RENAME: 1911387Seschrock return (gettext("\trename <filesystem|volume|snapshot> " 1921387Seschrock "<filesystem|volume|snapshot>\n")); 1931387Seschrock case HELP_ROLLBACK: 1941387Seschrock return (gettext("\trollback [-rRf] <snapshot>\n")); 1951749Sahrens case HELP_SEND: 1961749Sahrens return (gettext("\tsend [-i <snapshot>] <snapshot>\n")); 1971387Seschrock case HELP_SET: 1981387Seschrock return (gettext("\tset <property=value> " 1991387Seschrock "<filesystem|volume> ...\n")); 2001387Seschrock case HELP_SHARE: 2011387Seschrock return (gettext("\tshare -a\n" 2021387Seschrock "\tshare <filesystem>\n")); 2031387Seschrock case HELP_SNAPSHOT: 2041387Seschrock return (gettext("\tsnapshot <filesystem@name|volume@name>\n")); 2051387Seschrock case HELP_UNMOUNT: 2061387Seschrock return (gettext("\tunmount [-f] -a\n" 2071387Seschrock "\tunmount [-f] <filesystem|mountpoint>\n")); 2081387Seschrock case HELP_UNSHARE: 2091387Seschrock return (gettext("\tunshare [-f] -a\n" 2101387Seschrock "\tunshare [-f] <filesystem|mountpoint>\n")); 2111387Seschrock } 2121387Seschrock 2131387Seschrock abort(); 2141387Seschrock /* NOTREACHED */ 2151387Seschrock } 2161387Seschrock 217789Sahrens /* 218789Sahrens * Utility function to guarantee malloc() success. 219789Sahrens */ 220789Sahrens void * 221789Sahrens safe_malloc(size_t size) 222789Sahrens { 223789Sahrens void *data; 224789Sahrens 225789Sahrens if ((data = calloc(1, size)) == NULL) { 226789Sahrens (void) fprintf(stderr, "internal error: out of memory\n"); 227789Sahrens exit(1); 228789Sahrens } 229789Sahrens 230789Sahrens return (data); 231789Sahrens } 232789Sahrens 233789Sahrens /* 234789Sahrens * Display usage message. If we're inside a command, display only the usage for 235789Sahrens * that command. Otherwise, iterate over the entire command table and display 236789Sahrens * a complete usage message. 237789Sahrens */ 238789Sahrens static void 2392082Seschrock usage(boolean_t requested) 240789Sahrens { 241789Sahrens int i; 2422082Seschrock boolean_t show_properties = B_FALSE; 243789Sahrens FILE *fp = requested ? stdout : stderr; 244789Sahrens 245789Sahrens if (current_command == NULL) { 246789Sahrens 247789Sahrens (void) fprintf(fp, gettext("usage: zfs command args ...\n")); 248789Sahrens (void) fprintf(fp, 249789Sahrens gettext("where 'command' is one of the following:\n\n")); 250789Sahrens 251789Sahrens for (i = 0; i < NCOMMAND; i++) { 252789Sahrens if (command_table[i].name == NULL) 253789Sahrens (void) fprintf(fp, "\n"); 254789Sahrens else 255789Sahrens (void) fprintf(fp, "%s", 2561387Seschrock get_usage(command_table[i].usage)); 257789Sahrens } 258789Sahrens 259789Sahrens (void) fprintf(fp, gettext("\nEach dataset is of the form: " 260789Sahrens "pool/[dataset/]*dataset[@name]\n")); 261789Sahrens } else { 262789Sahrens (void) fprintf(fp, gettext("usage:\n")); 2631387Seschrock (void) fprintf(fp, "%s", get_usage(current_command->usage)); 264789Sahrens } 265789Sahrens 266789Sahrens if (current_command == NULL || 267789Sahrens strcmp(current_command->name, "set") == 0 || 268789Sahrens strcmp(current_command->name, "get") == 0 || 269789Sahrens strcmp(current_command->name, "inherit") == 0 || 270789Sahrens strcmp(current_command->name, "list") == 0) 2712082Seschrock show_properties = B_TRUE; 272789Sahrens 273789Sahrens if (show_properties) { 274789Sahrens 275789Sahrens (void) fprintf(fp, 276789Sahrens gettext("\nThe following properties are supported:\n")); 277789Sahrens 278789Sahrens (void) fprintf(fp, "\n\t%-13s %s %s %s\n\n", 279789Sahrens "PROPERTY", "EDIT", "INHERIT", "VALUES"); 280789Sahrens 281789Sahrens for (i = 0; i < ZFS_NPROP_VISIBLE; i++) { 282789Sahrens (void) fprintf(fp, "\t%-13s ", zfs_prop_to_name(i)); 283789Sahrens 284789Sahrens if (zfs_prop_readonly(i)) 285789Sahrens (void) fprintf(fp, " NO "); 286789Sahrens else 287789Sahrens (void) fprintf(fp, " YES "); 288789Sahrens 289789Sahrens if (zfs_prop_inheritable(i)) 290789Sahrens (void) fprintf(fp, " YES "); 291789Sahrens else 292789Sahrens (void) fprintf(fp, " NO "); 293789Sahrens 294789Sahrens if (zfs_prop_values(i) == NULL) 295789Sahrens (void) fprintf(fp, "-\n"); 296789Sahrens else 297789Sahrens (void) fprintf(fp, "%s\n", zfs_prop_values(i)); 298789Sahrens } 299789Sahrens (void) fprintf(fp, gettext("\nSizes are specified in bytes " 300789Sahrens "with standard units such as K, M, G, etc.\n")); 301789Sahrens } 302789Sahrens 303789Sahrens exit(requested ? 0 : 2); 304789Sahrens } 305789Sahrens 306789Sahrens /* 307789Sahrens * zfs clone <fs, snap, vol> fs 308789Sahrens * 309789Sahrens * Given an existing dataset, create a writable copy whose initial contents 310789Sahrens * are the same as the source. The newly created dataset maintains a 311789Sahrens * dependency on the original; the original cannot be destroyed so long as 312789Sahrens * the clone exists. 313789Sahrens */ 314789Sahrens static int 315789Sahrens zfs_do_clone(int argc, char **argv) 316789Sahrens { 317789Sahrens zfs_handle_t *zhp; 318789Sahrens int ret; 319789Sahrens 320789Sahrens /* check options */ 321789Sahrens if (argc > 1 && argv[1][0] == '-') { 322789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 323789Sahrens argv[1][1]); 3242082Seschrock usage(B_FALSE); 325789Sahrens } 326789Sahrens 327789Sahrens /* check number of arguments */ 328789Sahrens if (argc < 2) { 329789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 330789Sahrens "argument\n")); 3312082Seschrock usage(B_FALSE); 332789Sahrens } 333789Sahrens if (argc < 3) { 334789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 335789Sahrens "argument\n")); 3362082Seschrock usage(B_FALSE); 337789Sahrens } 338789Sahrens if (argc > 3) { 339789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 3402082Seschrock usage(B_FALSE); 341789Sahrens } 342789Sahrens 343789Sahrens /* open the source dataset */ 3442082Seschrock if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_SNAPSHOT)) == NULL) 345789Sahrens return (1); 346789Sahrens 347789Sahrens /* pass to libzfs */ 348789Sahrens ret = zfs_clone(zhp, argv[2]); 349789Sahrens 350789Sahrens /* create the mountpoint if necessary */ 351789Sahrens if (ret == 0) { 3522082Seschrock zfs_handle_t *clone = zfs_open(g_zfs, argv[2], ZFS_TYPE_ANY); 353789Sahrens if (clone != NULL) { 354789Sahrens if ((ret = zfs_mount(clone, NULL, 0)) == 0) 355789Sahrens ret = zfs_share(clone); 356789Sahrens zfs_close(clone); 357789Sahrens } 358789Sahrens } 359789Sahrens 360789Sahrens zfs_close(zhp); 361789Sahrens 362789Sahrens return (ret == 0 ? 0 : 1); 363789Sahrens } 364789Sahrens 365789Sahrens /* 366789Sahrens * zfs create fs 367789Sahrens * zfs create [-s] -V vol size 368789Sahrens * 369789Sahrens * Create a new dataset. This command can be used to create filesystems 370789Sahrens * and volumes. Snapshot creation is handled by 'zfs snapshot'. 371789Sahrens * For volumes, the user must specify a size to be used. 372789Sahrens * 373789Sahrens * The '-s' flag applies only to volumes, and indicates that we should not try 374789Sahrens * to set the reservation for this volume. By default we set a reservation 375789Sahrens * equal to the size for any volume. 376789Sahrens */ 377789Sahrens static int 378789Sahrens zfs_do_create(int argc, char **argv) 379789Sahrens { 380789Sahrens zfs_type_t type = ZFS_TYPE_FILESYSTEM; 381789Sahrens zfs_handle_t *zhp; 382789Sahrens char *size = NULL; 383789Sahrens char *blocksize = NULL; 384789Sahrens int c; 3852082Seschrock boolean_t noreserve = B_FALSE; 386789Sahrens int ret; 387789Sahrens 388789Sahrens /* check options */ 389789Sahrens while ((c = getopt(argc, argv, ":V:b:s")) != -1) { 390789Sahrens switch (c) { 391789Sahrens case 'V': 392789Sahrens type = ZFS_TYPE_VOLUME; 393789Sahrens size = optarg; 394789Sahrens break; 395789Sahrens case 'b': 396789Sahrens blocksize = optarg; 397789Sahrens break; 398789Sahrens case 's': 3992082Seschrock noreserve = B_TRUE; 400789Sahrens break; 401789Sahrens case ':': 402789Sahrens (void) fprintf(stderr, gettext("missing size " 403789Sahrens "argument\n")); 4042082Seschrock usage(B_FALSE); 405789Sahrens break; 406789Sahrens case '?': 407789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 408789Sahrens optopt); 4092082Seschrock usage(B_FALSE); 410789Sahrens } 411789Sahrens } 412789Sahrens 413789Sahrens if (noreserve && type != ZFS_TYPE_VOLUME) { 414789Sahrens (void) fprintf(stderr, gettext("'-s' can only be used when " 415789Sahrens "creating a volume\n")); 4162082Seschrock usage(B_FALSE); 417789Sahrens } 418789Sahrens 419789Sahrens argc -= optind; 420789Sahrens argv += optind; 421789Sahrens 422789Sahrens /* check number of arguments */ 423789Sahrens if (argc == 0) { 424789Sahrens (void) fprintf(stderr, gettext("missing %s argument\n"), 425789Sahrens zfs_type_to_name(type)); 4262082Seschrock usage(B_FALSE); 427789Sahrens } 428789Sahrens if (argc > 1) { 429789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 4302082Seschrock usage(B_FALSE); 431789Sahrens } 432789Sahrens 433789Sahrens /* pass to libzfs */ 4342082Seschrock if (zfs_create(g_zfs, argv[0], type, size, blocksize) != 0) 435789Sahrens return (1); 436789Sahrens 4372082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 438789Sahrens return (1); 439789Sahrens 440789Sahrens /* 441789Sahrens * Volume handling. By default, we try to create a reservation of equal 442789Sahrens * size for the volume. If we can't do this, then destroy the dataset 443789Sahrens * and report an error. 444789Sahrens */ 445789Sahrens if (type == ZFS_TYPE_VOLUME && !noreserve) { 446789Sahrens if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, size) != 0) { 447789Sahrens (void) fprintf(stderr, gettext("use '-s' to create a " 448789Sahrens "volume without a matching reservation\n")); 449789Sahrens (void) zfs_destroy(zhp); 450789Sahrens return (1); 451789Sahrens } 452789Sahrens } 453789Sahrens 454789Sahrens /* 455789Sahrens * Mount and/or share the new filesystem as appropriate. We provide a 456789Sahrens * verbose error message to let the user know that their filesystem was 457789Sahrens * in fact created, even if we failed to mount or share it. 458789Sahrens */ 459789Sahrens if (zfs_mount(zhp, NULL, 0) != 0) { 460789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 461789Sahrens "created, but not mounted\n")); 462789Sahrens ret = 1; 463789Sahrens } else if (zfs_share(zhp) != 0) { 464789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 465789Sahrens "created, but not shared\n")); 466789Sahrens ret = 1; 467789Sahrens } else { 468789Sahrens ret = 0; 469789Sahrens } 470789Sahrens 471789Sahrens zfs_close(zhp); 472789Sahrens return (ret); 473789Sahrens } 474789Sahrens 475789Sahrens /* 476789Sahrens * zfs destroy [-rf] <fs, snap, vol> 477789Sahrens * 478789Sahrens * -r Recursively destroy all children 479789Sahrens * -R Recursively destroy all dependents, including clones 480789Sahrens * -f Force unmounting of any dependents 481789Sahrens * 482789Sahrens * Destroys the given dataset. By default, it will unmount any filesystems, 483789Sahrens * and refuse to destroy a dataset that has any dependents. A dependent can 484789Sahrens * either be a child, or a clone of a child. 485789Sahrens */ 486789Sahrens typedef struct destroy_cbdata { 4872082Seschrock boolean_t cb_first; 488789Sahrens int cb_force; 489789Sahrens int cb_recurse; 490789Sahrens int cb_error; 491789Sahrens int cb_needforce; 492789Sahrens int cb_doclones; 493789Sahrens zfs_handle_t *cb_target; 494789Sahrens } destroy_cbdata_t; 495789Sahrens 496789Sahrens /* 497789Sahrens * Check for any dependents based on the '-r' or '-R' flags. 498789Sahrens */ 499789Sahrens static int 500789Sahrens destroy_check_dependent(zfs_handle_t *zhp, void *data) 501789Sahrens { 502789Sahrens destroy_cbdata_t *cbp = data; 503789Sahrens const char *tname = zfs_get_name(cbp->cb_target); 504789Sahrens const char *name = zfs_get_name(zhp); 505789Sahrens 506789Sahrens if (strncmp(tname, name, strlen(tname)) == 0 && 507789Sahrens (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { 508789Sahrens /* 509789Sahrens * This is a direct descendant, not a clone somewhere else in 510789Sahrens * the hierarchy. 511789Sahrens */ 512789Sahrens if (cbp->cb_recurse) 513789Sahrens goto out; 514789Sahrens 515789Sahrens if (cbp->cb_first) { 516789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 517789Sahrens "%s has children\n"), 518789Sahrens zfs_get_name(cbp->cb_target), 519789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 520789Sahrens (void) fprintf(stderr, gettext("use '-r' to destroy " 521789Sahrens "the following datasets:\n")); 5222082Seschrock cbp->cb_first = B_FALSE; 523789Sahrens cbp->cb_error = 1; 524789Sahrens } 525789Sahrens 526789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 527789Sahrens } else { 528789Sahrens /* 529789Sahrens * This is a clone. We only want to report this if the '-r' 530789Sahrens * wasn't specified, or the target is a snapshot. 531789Sahrens */ 532789Sahrens if (!cbp->cb_recurse && 533789Sahrens zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) 534789Sahrens goto out; 535789Sahrens 536789Sahrens if (cbp->cb_first) { 537789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 538789Sahrens "%s has dependent clones\n"), 539789Sahrens zfs_get_name(cbp->cb_target), 540789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 541789Sahrens (void) fprintf(stderr, gettext("use '-R' to destroy " 542789Sahrens "the following datasets:\n")); 5432082Seschrock cbp->cb_first = B_FALSE; 544789Sahrens cbp->cb_error = 1; 545789Sahrens } 546789Sahrens 547789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 548789Sahrens } 549789Sahrens 550789Sahrens out: 551789Sahrens zfs_close(zhp); 552789Sahrens return (0); 553789Sahrens } 554789Sahrens 555789Sahrens static int 556789Sahrens destroy_callback(zfs_handle_t *zhp, void *data) 557789Sahrens { 558789Sahrens destroy_cbdata_t *cbp = data; 559789Sahrens 560789Sahrens /* 561789Sahrens * Ignore pools (which we've already flagged as an error before getting 562789Sahrens * here. 563789Sahrens */ 564789Sahrens if (strchr(zfs_get_name(zhp), '/') == NULL && 565789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 566789Sahrens zfs_close(zhp); 567789Sahrens return (0); 568789Sahrens } 569789Sahrens 570789Sahrens /* 571789Sahrens * Bail out on the first error. 572789Sahrens */ 573789Sahrens if (zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0 || 574789Sahrens zfs_destroy(zhp) != 0) { 575789Sahrens zfs_close(zhp); 576789Sahrens return (-1); 577789Sahrens } 578789Sahrens 579789Sahrens zfs_close(zhp); 580789Sahrens return (0); 581789Sahrens } 582789Sahrens 583789Sahrens 584789Sahrens static int 585789Sahrens zfs_do_destroy(int argc, char **argv) 586789Sahrens { 587789Sahrens destroy_cbdata_t cb = { 0 }; 588789Sahrens int c; 589789Sahrens zfs_handle_t *zhp; 590789Sahrens 591789Sahrens /* check options */ 592789Sahrens while ((c = getopt(argc, argv, "frR")) != -1) { 593789Sahrens switch (c) { 594789Sahrens case 'f': 595789Sahrens cb.cb_force = 1; 596789Sahrens break; 597789Sahrens case 'r': 598789Sahrens cb.cb_recurse = 1; 599789Sahrens break; 600789Sahrens case 'R': 601789Sahrens cb.cb_recurse = 1; 602789Sahrens cb.cb_doclones = 1; 603789Sahrens break; 604789Sahrens case '?': 605789Sahrens default: 606789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 607789Sahrens optopt); 6082082Seschrock usage(B_FALSE); 609789Sahrens } 610789Sahrens } 611789Sahrens 612789Sahrens argc -= optind; 613789Sahrens argv += optind; 614789Sahrens 615789Sahrens /* check number of arguments */ 616789Sahrens if (argc == 0) { 617789Sahrens (void) fprintf(stderr, gettext("missing path argument\n")); 6182082Seschrock usage(B_FALSE); 619789Sahrens } 620789Sahrens if (argc > 1) { 621789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 6222082Seschrock usage(B_FALSE); 623789Sahrens } 624789Sahrens 625789Sahrens /* Open the given dataset */ 6262082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_ANY)) == NULL) 627789Sahrens return (1); 628789Sahrens 629789Sahrens cb.cb_target = zhp; 630789Sahrens 631789Sahrens /* 632789Sahrens * Perform an explicit check for pools before going any further. 633789Sahrens */ 634789Sahrens if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 635789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 636789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 637789Sahrens "operation does not apply to pools\n"), 638789Sahrens zfs_get_name(zhp)); 639789Sahrens (void) fprintf(stderr, gettext("use 'zfs destroy -r " 640789Sahrens "%s' to destroy all datasets in the pool\n"), 641789Sahrens zfs_get_name(zhp)); 642789Sahrens (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 643789Sahrens "to destroy the pool itself\n"), zfs_get_name(zhp)); 644789Sahrens zfs_close(zhp); 645789Sahrens return (1); 646789Sahrens } 647789Sahrens 648789Sahrens 649789Sahrens /* 650789Sahrens * Check for any dependents and/or clones. 651789Sahrens */ 6522082Seschrock cb.cb_first = B_TRUE; 653789Sahrens if (!cb.cb_doclones) 654789Sahrens (void) zfs_iter_dependents(zhp, destroy_check_dependent, &cb); 655789Sahrens 656789Sahrens if (cb.cb_error) { 657789Sahrens zfs_close(zhp); 658789Sahrens return (1); 659789Sahrens } 660789Sahrens 661789Sahrens /* 662789Sahrens * Do the real thing. 663789Sahrens */ 664789Sahrens if (zfs_iter_dependents(zhp, destroy_callback, &cb) == 0 && 665789Sahrens destroy_callback(zhp, &cb) == 0) 666789Sahrens return (0); 667789Sahrens 668789Sahrens return (1); 669789Sahrens } 670789Sahrens 671789Sahrens /* 672866Seschrock * zfs get [-rHp] [-o field[,field]...] [-s source[,source]...] 673866Seschrock * < all | property[,property]... > < fs | snap | vol > ... 674789Sahrens * 675789Sahrens * -r recurse over any child datasets 676789Sahrens * -H scripted mode. Headers are stripped, and fields are separated 677789Sahrens * by tabs instead of spaces. 678789Sahrens * -o Set of fields to display. One of "name,property,value,source". 679789Sahrens * Default is all four. 680789Sahrens * -s Set of sources to allow. One of 681789Sahrens * "local,default,inherited,temporary,none". Default is all 682789Sahrens * five. 683789Sahrens * -p Display values in parsable (literal) format. 684789Sahrens * 685789Sahrens * Prints properties for the given datasets. The user can control which 686789Sahrens * columns to display as well as which property types to allow. 687789Sahrens */ 688789Sahrens typedef struct get_cbdata { 689789Sahrens int cb_sources; 690789Sahrens int cb_columns[4]; 6912082Seschrock int cb_nprop; 6922082Seschrock boolean_t cb_scripted; 6932082Seschrock boolean_t cb_literal; 6942082Seschrock boolean_t cb_isall; 695789Sahrens zfs_prop_t cb_prop[ZFS_NPROP_ALL]; 696789Sahrens } get_cbdata_t; 697789Sahrens 698789Sahrens #define GET_COL_NAME 1 699789Sahrens #define GET_COL_PROPERTY 2 700789Sahrens #define GET_COL_VALUE 3 701789Sahrens #define GET_COL_SOURCE 4 702789Sahrens 703789Sahrens /* 704789Sahrens * Display a single line of output, according to the settings in the callback 705789Sahrens * structure. 706789Sahrens */ 707789Sahrens static void 708789Sahrens print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, zfs_prop_t prop, 709789Sahrens const char *value, zfs_source_t sourcetype, const char *source) 710789Sahrens { 711789Sahrens int i; 712789Sahrens int width; 713789Sahrens const char *str; 714789Sahrens char buf[128]; 715789Sahrens 716789Sahrens /* 717789Sahrens * Ignore those source types that the user has chosen to ignore. 718789Sahrens */ 719789Sahrens if ((sourcetype & cbp->cb_sources) == 0) 720789Sahrens return; 721789Sahrens 722789Sahrens for (i = 0; i < 4; i++) { 723789Sahrens switch (cbp->cb_columns[i]) { 724789Sahrens case GET_COL_NAME: 725789Sahrens width = 15; 726789Sahrens str = zfs_get_name(zhp); 727789Sahrens break; 728789Sahrens 729789Sahrens case GET_COL_PROPERTY: 730789Sahrens width = 13; 731789Sahrens str = zfs_prop_to_name(prop); 732789Sahrens break; 733789Sahrens 734789Sahrens case GET_COL_VALUE: 735789Sahrens width = 25; 736789Sahrens str = value; 737789Sahrens break; 738789Sahrens 739789Sahrens case GET_COL_SOURCE: 740789Sahrens width = 15; 741789Sahrens switch (sourcetype) { 742789Sahrens case ZFS_SRC_NONE: 743789Sahrens str = "-"; 744789Sahrens break; 745789Sahrens 746789Sahrens case ZFS_SRC_DEFAULT: 747789Sahrens str = "default"; 748789Sahrens break; 749789Sahrens 750789Sahrens case ZFS_SRC_LOCAL: 751789Sahrens str = "local"; 752789Sahrens break; 753789Sahrens 754789Sahrens case ZFS_SRC_TEMPORARY: 755789Sahrens str = "temporary"; 756789Sahrens break; 757789Sahrens 758789Sahrens case ZFS_SRC_INHERITED: 759789Sahrens (void) snprintf(buf, sizeof (buf), 760789Sahrens "inherited from %s", source); 761789Sahrens str = buf; 762789Sahrens break; 763789Sahrens } 764789Sahrens break; 765789Sahrens 766789Sahrens default: 767789Sahrens continue; 768789Sahrens } 769789Sahrens 770789Sahrens if (cbp->cb_columns[i + 1] == 0) 771789Sahrens (void) printf("%s", str); 772789Sahrens else if (cbp->cb_scripted) 773789Sahrens (void) printf("%s\t", str); 774789Sahrens else 775789Sahrens (void) printf("%-*s ", width, str); 776789Sahrens 777789Sahrens } 778789Sahrens 779789Sahrens (void) printf("\n"); 780789Sahrens } 781789Sahrens 782789Sahrens /* 783789Sahrens * Invoked to display the properties for a single dataset. 784789Sahrens */ 785789Sahrens static int 786789Sahrens get_callback(zfs_handle_t *zhp, void *data) 787789Sahrens { 788789Sahrens char buf[ZFS_MAXPROPLEN]; 789789Sahrens zfs_source_t sourcetype; 790789Sahrens char source[ZFS_MAXNAMELEN]; 791789Sahrens get_cbdata_t *cbp = data; 792789Sahrens int i; 793789Sahrens 794866Seschrock for (i = 0; i < cbp->cb_nprop; i++) { 795866Seschrock if (zfs_prop_get(zhp, cbp->cb_prop[i], buf, 796866Seschrock sizeof (buf), &sourcetype, source, sizeof (source), 797866Seschrock cbp->cb_literal) != 0) { 798866Seschrock if (cbp->cb_isall) 799866Seschrock continue; 800866Seschrock (void) strlcpy(buf, "-", sizeof (buf)); 801866Seschrock sourcetype = ZFS_SRC_NONE; 802866Seschrock } 803789Sahrens 804866Seschrock print_one_property(zhp, cbp, cbp->cb_prop[i], 805866Seschrock buf, sourcetype, source); 806789Sahrens } 807789Sahrens 808789Sahrens return (0); 809789Sahrens } 810789Sahrens 811789Sahrens static int 812789Sahrens zfs_do_get(int argc, char **argv) 813789Sahrens { 814789Sahrens get_cbdata_t cb = { 0 }; 8152082Seschrock boolean_t recurse = B_FALSE; 816789Sahrens int c; 817866Seschrock char *value, *fields, *badopt; 818789Sahrens int i; 819866Seschrock int ret; 820789Sahrens 821789Sahrens /* 822789Sahrens * Set up default columns and sources. 823789Sahrens */ 824789Sahrens cb.cb_sources = ZFS_SRC_ALL; 825789Sahrens cb.cb_columns[0] = GET_COL_NAME; 826789Sahrens cb.cb_columns[1] = GET_COL_PROPERTY; 827789Sahrens cb.cb_columns[2] = GET_COL_VALUE; 828789Sahrens cb.cb_columns[3] = GET_COL_SOURCE; 829789Sahrens 830789Sahrens /* check options */ 831789Sahrens while ((c = getopt(argc, argv, ":o:s:rHp")) != -1) { 832789Sahrens switch (c) { 833789Sahrens case 'p': 8342082Seschrock cb.cb_literal = B_TRUE; 835789Sahrens break; 836789Sahrens case 'r': 8372082Seschrock recurse = B_TRUE; 838789Sahrens break; 839789Sahrens case 'H': 8402082Seschrock cb.cb_scripted = B_TRUE; 841789Sahrens break; 842789Sahrens case ':': 843789Sahrens (void) fprintf(stderr, gettext("missing argument for " 844789Sahrens "'%c' option\n"), optopt); 8452082Seschrock usage(B_FALSE); 846789Sahrens break; 847789Sahrens case 'o': 848789Sahrens /* 849789Sahrens * Process the set of columns to display. We zero out 850789Sahrens * the structure to give us a blank slate. 851789Sahrens */ 852789Sahrens bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 853789Sahrens i = 0; 854789Sahrens while (*optarg != '\0') { 855789Sahrens static char *col_subopts[] = 856789Sahrens { "name", "property", "value", "source", 857789Sahrens NULL }; 858789Sahrens 859789Sahrens if (i == 4) { 860789Sahrens (void) fprintf(stderr, gettext("too " 861789Sahrens "many fields given to -o " 862789Sahrens "option\n")); 8632082Seschrock usage(B_FALSE); 864789Sahrens } 865789Sahrens 866789Sahrens switch (getsubopt(&optarg, col_subopts, 867789Sahrens &value)) { 868789Sahrens case 0: 869789Sahrens cb.cb_columns[i++] = GET_COL_NAME; 870789Sahrens break; 871789Sahrens case 1: 872789Sahrens cb.cb_columns[i++] = GET_COL_PROPERTY; 873789Sahrens break; 874789Sahrens case 2: 875789Sahrens cb.cb_columns[i++] = GET_COL_VALUE; 876789Sahrens break; 877789Sahrens case 3: 878789Sahrens cb.cb_columns[i++] = GET_COL_SOURCE; 879789Sahrens break; 880789Sahrens default: 881789Sahrens (void) fprintf(stderr, 882789Sahrens gettext("invalid column name " 883789Sahrens "'%s'\n"), value); 8842082Seschrock usage(B_FALSE); 885789Sahrens } 886789Sahrens } 887789Sahrens break; 888789Sahrens 889789Sahrens case 's': 890789Sahrens cb.cb_sources = 0; 891789Sahrens while (*optarg != '\0') { 892789Sahrens static char *source_subopts[] = { 893789Sahrens "local", "default", "inherited", 894789Sahrens "temporary", "none", NULL }; 895789Sahrens 896789Sahrens switch (getsubopt(&optarg, source_subopts, 897789Sahrens &value)) { 898789Sahrens case 0: 899789Sahrens cb.cb_sources |= ZFS_SRC_LOCAL; 900789Sahrens break; 901789Sahrens case 1: 902789Sahrens cb.cb_sources |= ZFS_SRC_DEFAULT; 903789Sahrens break; 904789Sahrens case 2: 905789Sahrens cb.cb_sources |= ZFS_SRC_INHERITED; 906789Sahrens break; 907789Sahrens case 3: 908789Sahrens cb.cb_sources |= ZFS_SRC_TEMPORARY; 909789Sahrens break; 910789Sahrens case 4: 911789Sahrens cb.cb_sources |= ZFS_SRC_NONE; 912789Sahrens break; 913789Sahrens default: 914789Sahrens (void) fprintf(stderr, 915789Sahrens gettext("invalid source " 916789Sahrens "'%s'\n"), value); 9172082Seschrock usage(B_FALSE); 918789Sahrens } 919789Sahrens } 920789Sahrens break; 921789Sahrens 922789Sahrens case '?': 923789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 924789Sahrens optopt); 9252082Seschrock usage(B_FALSE); 926789Sahrens } 927789Sahrens } 928789Sahrens 929789Sahrens argc -= optind; 930789Sahrens argv += optind; 931789Sahrens 932789Sahrens if (argc < 1) { 933789Sahrens (void) fprintf(stderr, gettext("missing property " 934789Sahrens "argument\n")); 9352082Seschrock usage(B_FALSE); 936789Sahrens } 937789Sahrens 938789Sahrens fields = argv[0]; 939789Sahrens 940789Sahrens /* 941866Seschrock * If the user specifies 'all', the behavior of 'zfs get' is slightly 942866Seschrock * different, because we don't show properties which don't apply to the 943866Seschrock * given dataset. 944789Sahrens */ 945866Seschrock if (strcmp(fields, "all") == 0) 9462082Seschrock cb.cb_isall = B_TRUE; 947789Sahrens 948866Seschrock if ((ret = zfs_get_proplist(fields, cb.cb_prop, ZFS_NPROP_ALL, 949866Seschrock &cb.cb_nprop, &badopt)) != 0) { 950866Seschrock if (ret == EINVAL) 951866Seschrock (void) fprintf(stderr, gettext("invalid property " 952866Seschrock "'%s'\n"), badopt); 953866Seschrock else 954866Seschrock (void) fprintf(stderr, gettext("too many properties " 955866Seschrock "specified\n")); 9562082Seschrock usage(B_FALSE); 957789Sahrens } 958789Sahrens 959789Sahrens argc--; 960789Sahrens argv++; 961789Sahrens 962789Sahrens /* check for at least one dataset name */ 963789Sahrens if (argc < 1) { 964789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 9652082Seschrock usage(B_FALSE); 966789Sahrens } 967789Sahrens 968789Sahrens /* 969789Sahrens * Print out any headers 970789Sahrens */ 971789Sahrens if (!cb.cb_scripted) { 972789Sahrens int i; 973789Sahrens for (i = 0; i < 4; i++) { 974789Sahrens switch (cb.cb_columns[i]) { 975789Sahrens case GET_COL_NAME: 976789Sahrens (void) printf("%-15s ", "NAME"); 977789Sahrens break; 978789Sahrens case GET_COL_PROPERTY: 979789Sahrens (void) printf("%-13s ", "PROPERTY"); 980789Sahrens break; 981789Sahrens case GET_COL_VALUE: 982789Sahrens (void) printf("%-25s ", "VALUE"); 983789Sahrens break; 984789Sahrens case GET_COL_SOURCE: 985789Sahrens (void) printf("%s", "SOURCE"); 986789Sahrens break; 987789Sahrens } 988789Sahrens } 989789Sahrens (void) printf("\n"); 990789Sahrens } 991789Sahrens 992789Sahrens /* run for each object */ 993789Sahrens return (zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, 994789Sahrens get_callback, &cb)); 995789Sahrens } 996789Sahrens 997789Sahrens /* 998789Sahrens * inherit [-r] <property> <fs|vol> ... 999789Sahrens * 1000789Sahrens * -r Recurse over all children 1001789Sahrens * 1002789Sahrens * For each dataset specified on the command line, inherit the given property 1003789Sahrens * from its parent. Inheriting a property at the pool level will cause it to 1004789Sahrens * use the default value. The '-r' flag will recurse over all children, and is 1005789Sahrens * useful for setting a property on a hierarchy-wide basis, regardless of any 1006789Sahrens * local modifications for each dataset. 1007789Sahrens */ 1008789Sahrens static int 1009789Sahrens inherit_callback(zfs_handle_t *zhp, void *data) 1010789Sahrens { 1011789Sahrens zfs_prop_t prop = (zfs_prop_t)data; 1012789Sahrens 1013789Sahrens return (zfs_prop_inherit(zhp, prop) != 0); 1014789Sahrens } 1015789Sahrens 1016789Sahrens static int 1017789Sahrens zfs_do_inherit(int argc, char **argv) 1018789Sahrens { 10192082Seschrock boolean_t recurse = B_FALSE; 1020789Sahrens int c; 1021789Sahrens zfs_prop_t prop; 1022789Sahrens char *propname; 1023789Sahrens 1024789Sahrens /* check options */ 1025789Sahrens while ((c = getopt(argc, argv, "r")) != -1) { 1026789Sahrens switch (c) { 1027789Sahrens case 'r': 10282082Seschrock recurse = B_TRUE; 1029789Sahrens break; 1030789Sahrens case '?': 1031789Sahrens default: 1032789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1033789Sahrens optopt); 10342082Seschrock usage(B_FALSE); 1035789Sahrens } 1036789Sahrens } 1037789Sahrens 1038789Sahrens argc -= optind; 1039789Sahrens argv += optind; 1040789Sahrens 1041789Sahrens /* check number of arguments */ 1042789Sahrens if (argc < 1) { 1043789Sahrens (void) fprintf(stderr, gettext("missing property argument\n")); 10442082Seschrock usage(B_FALSE); 1045789Sahrens } 1046789Sahrens if (argc < 2) { 1047789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 10482082Seschrock usage(B_FALSE); 1049789Sahrens } 1050789Sahrens 1051789Sahrens propname = argv[0]; 1052789Sahrens 1053789Sahrens /* 1054789Sahrens * Get and validate the property before iterating over the datasets. We 1055789Sahrens * do this now so as to avoid printing out an error message for each and 1056789Sahrens * every dataset. 1057789Sahrens */ 1058789Sahrens if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) { 1059789Sahrens (void) fprintf(stderr, gettext("invalid property '%s'\n"), 1060789Sahrens propname); 10612082Seschrock usage(B_FALSE); 1062789Sahrens } 1063789Sahrens if (zfs_prop_readonly(prop)) { 1064789Sahrens (void) fprintf(stderr, gettext("%s property is read-only\n"), 1065789Sahrens propname); 1066789Sahrens return (1); 1067789Sahrens } 1068789Sahrens if (!zfs_prop_inheritable(prop)) { 1069789Sahrens (void) fprintf(stderr, gettext("%s property cannot be " 1070789Sahrens "inherited\n"), propname); 1071789Sahrens (void) fprintf(stderr, gettext("use 'zfs set %s=none' to " 1072789Sahrens "clear\n"), propname); 1073789Sahrens return (1); 1074789Sahrens } 1075789Sahrens 1076789Sahrens return (zfs_for_each(argc - 1, argv + 1, recurse, 1077789Sahrens ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, 1078789Sahrens inherit_callback, (void *)prop)); 1079789Sahrens } 1080789Sahrens 1081789Sahrens /* 1082866Seschrock * list [-rH] [-o property[,property]...] [-t type[,type]...] <dataset> ... 1083789Sahrens * 1084789Sahrens * -r Recurse over all children 1085789Sahrens * -H Scripted mode; elide headers and separate colums by tabs 1086789Sahrens * -o Control which fields to display. 1087866Seschrock * -t Control which object types to display. 1088789Sahrens * 1089789Sahrens * When given no arguments, lists all filesystems in the system. 1090789Sahrens * Otherwise, list the specified datasets, optionally recursing down them if 1091789Sahrens * '-r' is specified. 1092789Sahrens */ 1093789Sahrens typedef struct list_cbdata { 10942082Seschrock boolean_t cb_first; 10952082Seschrock boolean_t cb_scripted; 1096866Seschrock zfs_prop_t cb_fields[ZFS_NPROP_ALL]; 1097866Seschrock int cb_fieldcount; 1098789Sahrens } list_cbdata_t; 1099789Sahrens 1100789Sahrens /* 1101789Sahrens * Given a list of columns to display, output appropriate headers for each one. 1102789Sahrens */ 1103789Sahrens static void 1104866Seschrock print_header(zfs_prop_t *fields, size_t count) 1105789Sahrens { 1106789Sahrens int i; 1107789Sahrens 1108789Sahrens for (i = 0; i < count; i++) { 1109789Sahrens if (i != 0) 1110789Sahrens (void) printf(" "); 1111789Sahrens if (i == count - 1) 1112789Sahrens (void) printf("%s", zfs_prop_column_name(fields[i])); 1113789Sahrens else /* LINTED - format specifier */ 1114789Sahrens (void) printf(zfs_prop_column_format(fields[i]), 1115789Sahrens zfs_prop_column_name(fields[i])); 1116789Sahrens } 1117789Sahrens 1118789Sahrens (void) printf("\n"); 1119789Sahrens } 1120789Sahrens 1121789Sahrens /* 1122789Sahrens * Given a dataset and a list of fields, print out all the properties according 1123789Sahrens * to the described layout. 1124789Sahrens */ 1125789Sahrens static void 1126866Seschrock print_dataset(zfs_handle_t *zhp, zfs_prop_t *fields, size_t count, int scripted) 1127789Sahrens { 1128789Sahrens int i; 1129789Sahrens char property[ZFS_MAXPROPLEN]; 1130789Sahrens 1131789Sahrens for (i = 0; i < count; i++) { 1132789Sahrens if (i != 0) { 1133789Sahrens if (scripted) 1134789Sahrens (void) printf("\t"); 1135789Sahrens else 1136789Sahrens (void) printf(" "); 1137789Sahrens } 1138789Sahrens 1139789Sahrens if (zfs_prop_get(zhp, fields[i], property, 11402082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) != 0) 1141789Sahrens (void) strlcpy(property, "-", sizeof (property)); 1142789Sahrens 1143866Seschrock /* 1144866Seschrock * If this is being called in scripted mode, or if this is the 1145866Seschrock * last column and it is left-justified, don't include a width 1146866Seschrock * format specifier. 1147866Seschrock */ 1148866Seschrock if (scripted || (i == count - 1 && 1149866Seschrock strchr(zfs_prop_column_format(fields[i]), '-') != NULL)) 1150789Sahrens (void) printf("%s", property); 1151789Sahrens else /* LINTED - format specifier */ 1152789Sahrens (void) printf(zfs_prop_column_format(fields[i]), 1153789Sahrens property); 1154789Sahrens } 1155789Sahrens 1156789Sahrens (void) printf("\n"); 1157789Sahrens } 1158789Sahrens 1159789Sahrens /* 1160789Sahrens * Generic callback function to list a dataset or snapshot. 1161789Sahrens */ 1162789Sahrens static int 1163789Sahrens list_callback(zfs_handle_t *zhp, void *data) 1164789Sahrens { 1165789Sahrens list_cbdata_t *cbp = data; 1166789Sahrens 1167789Sahrens if (cbp->cb_first) { 1168789Sahrens if (!cbp->cb_scripted) 1169789Sahrens print_header(cbp->cb_fields, cbp->cb_fieldcount); 11702082Seschrock cbp->cb_first = B_FALSE; 1171789Sahrens } 1172789Sahrens 1173789Sahrens print_dataset(zhp, cbp->cb_fields, cbp->cb_fieldcount, 1174789Sahrens cbp->cb_scripted); 1175789Sahrens 1176789Sahrens return (0); 1177789Sahrens } 1178789Sahrens 1179789Sahrens static int 1180789Sahrens zfs_do_list(int argc, char **argv) 1181789Sahrens { 1182789Sahrens int c; 11832082Seschrock boolean_t recurse = B_FALSE; 11842082Seschrock boolean_t scripted = B_FALSE; 1185789Sahrens static char default_fields[] = 1186789Sahrens "name,used,available,referenced,mountpoint"; 1187789Sahrens int types = ZFS_TYPE_ANY; 1188789Sahrens char *fields = NULL; 1189789Sahrens char *basic_fields = default_fields; 1190789Sahrens list_cbdata_t cb = { 0 }; 1191789Sahrens char *value; 1192789Sahrens int ret; 1193789Sahrens char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL }; 1194866Seschrock char *badopt; 1195866Seschrock int alloffset; 1196789Sahrens 1197789Sahrens /* check options */ 1198789Sahrens while ((c = getopt(argc, argv, ":o:rt:H")) != -1) { 1199789Sahrens switch (c) { 1200789Sahrens case 'o': 1201789Sahrens fields = optarg; 1202789Sahrens break; 1203789Sahrens case 'r': 12042082Seschrock recurse = B_TRUE; 1205789Sahrens break; 1206789Sahrens case 'H': 12072082Seschrock scripted = B_TRUE; 1208789Sahrens break; 1209789Sahrens case 't': 1210789Sahrens types = 0; 1211789Sahrens while (*optarg != '\0') { 1212789Sahrens switch (getsubopt(&optarg, type_subopts, 1213789Sahrens &value)) { 1214789Sahrens case 0: 1215789Sahrens types |= ZFS_TYPE_FILESYSTEM; 1216789Sahrens break; 1217789Sahrens case 1: 1218789Sahrens types |= ZFS_TYPE_VOLUME; 1219789Sahrens break; 1220789Sahrens case 2: 1221789Sahrens types |= ZFS_TYPE_SNAPSHOT; 1222789Sahrens break; 1223789Sahrens default: 1224789Sahrens (void) fprintf(stderr, 1225789Sahrens gettext("invalid type '%s'\n"), 1226789Sahrens value); 12272082Seschrock usage(B_FALSE); 1228789Sahrens } 1229789Sahrens } 1230789Sahrens break; 1231789Sahrens case ':': 1232789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1233789Sahrens "'%c' option\n"), optopt); 12342082Seschrock usage(B_FALSE); 1235789Sahrens break; 1236789Sahrens case '?': 1237789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1238789Sahrens optopt); 12392082Seschrock usage(B_FALSE); 1240789Sahrens } 1241789Sahrens } 1242789Sahrens 1243789Sahrens argc -= optind; 1244789Sahrens argv += optind; 1245789Sahrens 1246789Sahrens if (fields == NULL) 1247789Sahrens fields = basic_fields; 1248789Sahrens 1249866Seschrock /* 1250866Seschrock * If the user specifies '-o all', the zfs_get_proplist() doesn't 1251866Seschrock * normally include the name of the dataset. For 'zfs list', we always 1252866Seschrock * want this property to be first. 1253866Seschrock */ 1254866Seschrock if (strcmp(fields, "all") == 0) { 1255866Seschrock cb.cb_fields[0] = ZFS_PROP_NAME; 1256866Seschrock alloffset = 1; 1257866Seschrock } else { 1258866Seschrock alloffset = 0; 1259789Sahrens } 1260789Sahrens 1261866Seschrock if ((ret = zfs_get_proplist(fields, cb.cb_fields + alloffset, 1262866Seschrock ZFS_NPROP_ALL - alloffset, &cb.cb_fieldcount, &badopt)) != 0) { 1263866Seschrock if (ret == EINVAL) 1264866Seschrock (void) fprintf(stderr, gettext("invalid property " 1265866Seschrock "'%s'\n"), badopt); 1266866Seschrock else 1267866Seschrock (void) fprintf(stderr, gettext("too many properties " 1268866Seschrock "specified\n")); 12692082Seschrock usage(B_FALSE); 1270866Seschrock } 1271866Seschrock 1272866Seschrock cb.cb_fieldcount += alloffset; 1273789Sahrens cb.cb_scripted = scripted; 12742082Seschrock cb.cb_first = B_TRUE; 1275789Sahrens 1276789Sahrens ret = zfs_for_each(argc, argv, recurse, types, list_callback, &cb); 1277789Sahrens 12782082Seschrock if (ret == 0 && cb.cb_first) 1279789Sahrens (void) printf(gettext("no datasets available\n")); 1280789Sahrens 1281789Sahrens return (ret); 1282789Sahrens } 1283789Sahrens 1284789Sahrens /* 1285789Sahrens * zfs rename <fs | snap | vol> <fs | snap | vol> 1286789Sahrens * 1287789Sahrens * Renames the given dataset to another of the same type. 1288789Sahrens */ 1289789Sahrens /* ARGSUSED */ 1290789Sahrens static int 1291789Sahrens zfs_do_rename(int argc, char **argv) 1292789Sahrens { 1293789Sahrens zfs_handle_t *zhp; 12942082Seschrock int ret; 1295789Sahrens 1296789Sahrens /* check options */ 1297789Sahrens if (argc > 1 && argv[1][0] == '-') { 1298789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1299789Sahrens argv[1][1]); 13002082Seschrock usage(B_FALSE); 1301789Sahrens } 1302789Sahrens 1303789Sahrens /* check number of arguments */ 1304789Sahrens if (argc < 2) { 1305789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 1306789Sahrens "argument\n")); 13072082Seschrock usage(B_FALSE); 1308789Sahrens } 1309789Sahrens if (argc < 3) { 1310789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 1311789Sahrens "argument\n")); 13122082Seschrock usage(B_FALSE); 1313789Sahrens } 1314789Sahrens if (argc > 3) { 1315789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 13162082Seschrock usage(B_FALSE); 1317789Sahrens } 1318789Sahrens 13192082Seschrock if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_ANY)) == NULL) 1320789Sahrens return (1); 1321789Sahrens 13222082Seschrock ret = (zfs_rename(zhp, argv[2]) != 0); 13232082Seschrock 13242082Seschrock zfs_close(zhp); 13252082Seschrock return (ret); 13262082Seschrock } 13272082Seschrock 13282082Seschrock /* 13292082Seschrock * zfs promote <fs> 13302082Seschrock * 13312082Seschrock * Promotes the given clone fs to be the parent 13322082Seschrock */ 13332082Seschrock /* ARGSUSED */ 13342082Seschrock static int 13352082Seschrock zfs_do_promote(int argc, char **argv) 13362082Seschrock { 13372082Seschrock zfs_handle_t *zhp; 13382082Seschrock int ret; 13392082Seschrock 13402082Seschrock /* check options */ 13412082Seschrock if (argc > 1 && argv[1][0] == '-') { 13422082Seschrock (void) fprintf(stderr, gettext("invalid option '%c'\n"), 13432082Seschrock argv[1][1]); 13442082Seschrock usage(B_FALSE); 13452082Seschrock } 13462082Seschrock 13472082Seschrock /* check number of arguments */ 13482082Seschrock if (argc < 2) { 13492082Seschrock (void) fprintf(stderr, gettext("missing clone filesystem" 13502082Seschrock "argument\n")); 13512082Seschrock usage(B_FALSE); 13522082Seschrock } 13532082Seschrock if (argc > 2) { 13542082Seschrock (void) fprintf(stderr, gettext("too many arguments\n")); 13552082Seschrock usage(B_FALSE); 13562082Seschrock } 13572082Seschrock 13582082Seschrock zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 13592082Seschrock if (zhp == NULL) 13602082Seschrock return (1); 13612082Seschrock 13622082Seschrock ret = (zfs_promote(zhp) != 0); 13632082Seschrock 1364789Sahrens zfs_close(zhp); 1365789Sahrens return (ret); 1366789Sahrens } 1367789Sahrens 1368789Sahrens /* 1369789Sahrens * zfs rollback [-rfR] <snapshot> 1370789Sahrens * 1371789Sahrens * -r Delete any intervening snapshots before doing rollback 1372789Sahrens * -R Delete any snapshots and their clones 1373789Sahrens * -f Force unmount filesystems, even if they are in use. 1374789Sahrens * 1375789Sahrens * Given a filesystem, rollback to a specific snapshot, discarding any changes 1376789Sahrens * since then and making it the active dataset. If more recent snapshots exist, 1377789Sahrens * the command will complain unless the '-r' flag is given. 1378789Sahrens */ 1379789Sahrens typedef struct rollback_cbdata { 1380789Sahrens uint64_t cb_create; 13812082Seschrock boolean_t cb_first; 1382789Sahrens int cb_doclones; 1383789Sahrens char *cb_target; 1384789Sahrens int cb_error; 13852082Seschrock boolean_t cb_recurse; 13862082Seschrock boolean_t cb_dependent; 1387789Sahrens } rollback_cbdata_t; 1388789Sahrens 1389789Sahrens /* 1390789Sahrens * Report any snapshots more recent than the one specified. Used when '-r' is 1391789Sahrens * not specified. We reuse this same callback for the snapshot dependents - if 1392789Sahrens * 'cb_dependent' is set, then this is a dependent and we should report it 1393789Sahrens * without checking the transaction group. 1394789Sahrens */ 1395789Sahrens static int 1396789Sahrens rollback_check(zfs_handle_t *zhp, void *data) 1397789Sahrens { 1398789Sahrens rollback_cbdata_t *cbp = data; 1399789Sahrens 14002082Seschrock if (cbp->cb_doclones) { 14012082Seschrock zfs_close(zhp); 1402789Sahrens return (0); 14032082Seschrock } 1404789Sahrens 1405789Sahrens if (!cbp->cb_dependent) { 1406789Sahrens if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && 14071294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 1408789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 1409789Sahrens cbp->cb_create) { 1410789Sahrens 1411789Sahrens if (cbp->cb_first && !cbp->cb_recurse) { 1412789Sahrens (void) fprintf(stderr, gettext("cannot " 1413789Sahrens "rollback to '%s': more recent snapshots " 1414789Sahrens "exist\n"), 1415789Sahrens cbp->cb_target); 1416789Sahrens (void) fprintf(stderr, gettext("use '-r' to " 1417789Sahrens "force deletion of the following " 1418789Sahrens "snapshots:\n")); 1419789Sahrens cbp->cb_first = 0; 1420789Sahrens cbp->cb_error = 1; 1421789Sahrens } 1422789Sahrens 1423789Sahrens if (cbp->cb_recurse) { 14242082Seschrock cbp->cb_dependent = B_TRUE; 1425789Sahrens (void) zfs_iter_dependents(zhp, rollback_check, 1426789Sahrens cbp); 14272082Seschrock cbp->cb_dependent = B_FALSE; 1428789Sahrens } else { 1429789Sahrens (void) fprintf(stderr, "%s\n", 1430789Sahrens zfs_get_name(zhp)); 1431789Sahrens } 1432789Sahrens } 1433789Sahrens } else { 1434789Sahrens if (cbp->cb_first && cbp->cb_recurse) { 1435789Sahrens (void) fprintf(stderr, gettext("cannot rollback to " 1436789Sahrens "'%s': clones of previous snapshots exist\n"), 1437789Sahrens cbp->cb_target); 1438789Sahrens (void) fprintf(stderr, gettext("use '-R' to " 1439789Sahrens "force deletion of the following clones and " 1440789Sahrens "dependents:\n")); 1441789Sahrens cbp->cb_first = 0; 1442789Sahrens cbp->cb_error = 1; 1443789Sahrens } 1444789Sahrens 1445789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1446789Sahrens } 1447789Sahrens 1448789Sahrens zfs_close(zhp); 1449789Sahrens return (0); 1450789Sahrens } 1451789Sahrens 1452789Sahrens static int 1453789Sahrens zfs_do_rollback(int argc, char **argv) 1454789Sahrens { 1455789Sahrens int ret; 1456789Sahrens int c; 1457789Sahrens rollback_cbdata_t cb = { 0 }; 1458789Sahrens zfs_handle_t *zhp, *snap; 1459789Sahrens char parentname[ZFS_MAXNAMELEN]; 1460789Sahrens char *delim; 14611294Slling int force = 0; 1462789Sahrens 1463789Sahrens /* check options */ 1464789Sahrens while ((c = getopt(argc, argv, "rfR")) != -1) { 1465789Sahrens switch (c) { 1466789Sahrens case 'f': 14671294Slling force = 1; 1468789Sahrens break; 1469789Sahrens case 'r': 1470789Sahrens cb.cb_recurse = 1; 1471789Sahrens break; 1472789Sahrens case 'R': 1473789Sahrens cb.cb_recurse = 1; 1474789Sahrens cb.cb_doclones = 1; 1475789Sahrens break; 1476789Sahrens case '?': 1477789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1478789Sahrens optopt); 14792082Seschrock usage(B_FALSE); 1480789Sahrens } 1481789Sahrens } 1482789Sahrens 1483789Sahrens argc -= optind; 1484789Sahrens argv += optind; 1485789Sahrens 1486789Sahrens /* check number of arguments */ 1487789Sahrens if (argc < 1) { 1488789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 14892082Seschrock usage(B_FALSE); 1490789Sahrens } 1491789Sahrens if (argc > 1) { 1492789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 14932082Seschrock usage(B_FALSE); 1494789Sahrens } 1495789Sahrens 1496789Sahrens /* open the snapshot */ 14972082Seschrock if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1498789Sahrens return (1); 1499789Sahrens 15001294Slling /* open the parent dataset */ 15011294Slling (void) strlcpy(parentname, argv[0], sizeof (parentname)); 1502789Sahrens verify((delim = strrchr(parentname, '@')) != NULL); 1503789Sahrens *delim = '\0'; 15042082Seschrock if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_ANY)) == NULL) { 1505789Sahrens zfs_close(snap); 1506789Sahrens return (1); 1507789Sahrens } 1508789Sahrens 1509789Sahrens /* 1510789Sahrens * Check for more recent snapshots and/or clones based on the presence 1511789Sahrens * of '-r' and '-R'. 1512789Sahrens */ 15131294Slling cb.cb_target = argv[0]; 15141294Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 15152082Seschrock cb.cb_first = B_TRUE; 1516789Sahrens cb.cb_error = 0; 1517789Sahrens (void) zfs_iter_children(zhp, rollback_check, &cb); 1518789Sahrens 1519789Sahrens if ((ret = cb.cb_error) != 0) 1520789Sahrens goto out; 1521789Sahrens 1522789Sahrens /* 15231294Slling * Rollback parent to the given snapshot. 1524789Sahrens */ 15251294Slling ret = zfs_rollback(zhp, snap, force); 1526789Sahrens 1527789Sahrens out: 1528789Sahrens zfs_close(snap); 1529789Sahrens zfs_close(zhp); 1530789Sahrens 1531789Sahrens if (ret == 0) 1532789Sahrens return (0); 1533789Sahrens else 1534789Sahrens return (1); 1535789Sahrens } 1536789Sahrens 1537789Sahrens /* 1538789Sahrens * zfs set property=value { fs | snap | vol } ... 1539789Sahrens * 1540789Sahrens * Sets the given property for all datasets specified on the command line. 1541789Sahrens */ 1542789Sahrens typedef struct set_cbdata { 1543789Sahrens char *cb_propname; 1544789Sahrens char *cb_value; 1545789Sahrens zfs_prop_t cb_prop; 1546789Sahrens } set_cbdata_t; 1547789Sahrens 1548789Sahrens static int 1549789Sahrens set_callback(zfs_handle_t *zhp, void *data) 1550789Sahrens { 1551789Sahrens set_cbdata_t *cbp = data; 1552789Sahrens int ret = 1; 1553789Sahrens 1554789Sahrens /* don't allow setting of properties for snapshots */ 1555789Sahrens if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { 1556789Sahrens (void) fprintf(stderr, gettext("cannot set %s property for " 1557789Sahrens "'%s': snapshot properties cannot be modified\n"), 1558789Sahrens cbp->cb_propname, zfs_get_name(zhp)); 1559789Sahrens return (1); 1560789Sahrens } 1561789Sahrens 1562789Sahrens /* 15631133Seschrock * If we're changing the volsize, make sure the value is appropriate, 15641133Seschrock * and set the reservation if this is a non-sparse volume. 1565789Sahrens */ 1566789Sahrens if (cbp->cb_prop == ZFS_PROP_VOLSIZE && 15671133Seschrock zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 1568789Sahrens uint64_t volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1569789Sahrens uint64_t avail = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE); 15701133Seschrock uint64_t reservation = zfs_prop_get_int(zhp, 15711133Seschrock ZFS_PROP_RESERVATION); 15721133Seschrock uint64_t blocksize = zfs_prop_get_int(zhp, 15731133Seschrock ZFS_PROP_VOLBLOCKSIZE); 1574789Sahrens uint64_t value; 1575789Sahrens 1576789Sahrens verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); 1577789Sahrens 15781133Seschrock if (value % blocksize != 0) { 15791133Seschrock char buf[64]; 15801133Seschrock 15811133Seschrock zfs_nicenum(blocksize, buf, sizeof (buf)); 15821133Seschrock (void) fprintf(stderr, gettext("cannot set %s for " 15831133Seschrock "'%s': must be a multiple of volume block size " 15841133Seschrock "(%s)\n"), cbp->cb_propname, zfs_get_name(zhp), 15851133Seschrock buf); 1586789Sahrens return (1); 1587789Sahrens } 1588789Sahrens 15891133Seschrock if (value == 0) { 15901133Seschrock (void) fprintf(stderr, gettext("cannot set %s for " 15911133Seschrock "'%s': cannot be zero\n"), cbp->cb_propname, 15921133Seschrock zfs_get_name(zhp)); 1593789Sahrens return (1); 1594789Sahrens } 15951133Seschrock 15961133Seschrock if (volsize == reservation) { 15971133Seschrock if (value > volsize && (value - volsize) > avail) { 15981133Seschrock (void) fprintf(stderr, gettext("cannot set " 15991133Seschrock "%s property for '%s': volume size exceeds " 16001133Seschrock "amount of available space\n"), 16011133Seschrock cbp->cb_propname, zfs_get_name(zhp)); 16021133Seschrock return (1); 16031133Seschrock } 16041133Seschrock 16051133Seschrock if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, 16061133Seschrock cbp->cb_value) != 0) { 16071133Seschrock (void) fprintf(stderr, gettext("volsize and " 16081133Seschrock "reservation must remain equal\n")); 16091133Seschrock return (1); 16101133Seschrock } 16111133Seschrock } 1612789Sahrens } 1613789Sahrens 1614789Sahrens /* 1615789Sahrens * Do not allow the reservation to be set above the volume size. We do 1616789Sahrens * this here instead of inside libzfs because libzfs violates this rule 1617789Sahrens * internally. 1618789Sahrens */ 1619789Sahrens if (cbp->cb_prop == ZFS_PROP_RESERVATION && 1620789Sahrens zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 1621789Sahrens uint64_t value; 1622789Sahrens uint64_t volsize; 1623789Sahrens 1624789Sahrens volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1625789Sahrens if (strcmp(cbp->cb_value, "none") == 0) 1626789Sahrens value = 0; 1627789Sahrens else 1628789Sahrens verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); 1629789Sahrens 1630789Sahrens if (value > volsize) { 1631789Sahrens (void) fprintf(stderr, gettext("cannot set %s " 1632789Sahrens "for '%s': size is greater than current " 1633789Sahrens "volume size\n"), cbp->cb_propname, 1634789Sahrens zfs_get_name(zhp)); 1635789Sahrens return (-1); 1636789Sahrens } 1637789Sahrens } 1638789Sahrens 1639*2169Snd150628 if (zfs_prop_set(zhp, cbp->cb_prop, cbp->cb_value) != 0) { 1640*2169Snd150628 switch (libzfs_errno(g_zfs)) { 1641*2169Snd150628 case EZFS_MOUNTFAILED: 1642*2169Snd150628 (void) fprintf(stderr, gettext("property may be set " 1643*2169Snd150628 "but unable to remount filesystem\n")); 1644*2169Snd150628 break; 1645*2169Snd150628 case EZFS_SHAREFAILED: 1646*2169Snd150628 (void) fprintf(stderr, gettext("property may be set " 1647*2169Snd150628 "but unable to reshare filesystem\n")); 1648*2169Snd150628 break; 1649*2169Snd150628 } 1650789Sahrens return (1); 1651*2169Snd150628 } 1652789Sahrens ret = 0; 1653789Sahrens error: 1654789Sahrens return (ret); 1655789Sahrens } 1656789Sahrens 1657789Sahrens static int 1658789Sahrens zfs_do_set(int argc, char **argv) 1659789Sahrens { 1660789Sahrens set_cbdata_t cb; 1661789Sahrens 1662789Sahrens /* check for options */ 1663789Sahrens if (argc > 1 && argv[1][0] == '-') { 1664789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1665789Sahrens argv[1][1]); 16662082Seschrock usage(B_FALSE); 1667789Sahrens } 1668789Sahrens 1669789Sahrens /* check number of arguments */ 1670789Sahrens if (argc < 2) { 1671789Sahrens (void) fprintf(stderr, gettext("missing property=value " 1672789Sahrens "argument\n")); 16732082Seschrock usage(B_FALSE); 1674789Sahrens } 1675789Sahrens if (argc < 3) { 1676789Sahrens (void) fprintf(stderr, gettext("missing dataset name\n")); 16772082Seschrock usage(B_FALSE); 1678789Sahrens } 1679789Sahrens 1680789Sahrens /* validate property=value argument */ 1681789Sahrens cb.cb_propname = argv[1]; 1682789Sahrens if ((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) { 1683789Sahrens (void) fprintf(stderr, gettext("missing value in " 1684789Sahrens "property=value argument\n")); 16852082Seschrock usage(B_FALSE); 1686789Sahrens } 1687789Sahrens 1688789Sahrens *cb.cb_value = '\0'; 1689789Sahrens cb.cb_value++; 1690789Sahrens 1691789Sahrens if (*cb.cb_propname == '\0') { 1692789Sahrens (void) fprintf(stderr, 1693789Sahrens gettext("missing property in property=value argument\n")); 16942082Seschrock usage(B_FALSE); 1695789Sahrens } 1696789Sahrens if (*cb.cb_value == '\0') { 1697789Sahrens (void) fprintf(stderr, 1698789Sahrens gettext("missing value in property=value argument\n")); 16992082Seschrock usage(B_FALSE); 1700789Sahrens } 1701789Sahrens 1702789Sahrens /* get the property type */ 1703789Sahrens if ((cb.cb_prop = zfs_name_to_prop(cb.cb_propname)) == 1704789Sahrens ZFS_PROP_INVAL) { 1705789Sahrens (void) fprintf(stderr, 1706789Sahrens gettext("invalid property '%s'\n"), cb.cb_propname); 17072082Seschrock usage(B_FALSE); 1708789Sahrens } 1709789Sahrens 1710789Sahrens /* 1711789Sahrens * Validate that the value is appropriate for this property. We do this 1712789Sahrens * once now so we don't generate multiple errors each time we try to 1713789Sahrens * apply it to a dataset. 1714789Sahrens */ 17152082Seschrock if (zfs_prop_validate(g_zfs, cb.cb_prop, cb.cb_value, NULL) != 0) 1716789Sahrens return (1); 1717789Sahrens 17182082Seschrock return (zfs_for_each(argc - 2, argv + 2, B_FALSE, 1719789Sahrens ZFS_TYPE_ANY, set_callback, &cb)); 1720789Sahrens } 1721789Sahrens 1722789Sahrens /* 1723789Sahrens * zfs snapshot <fs@snap> 1724789Sahrens * 1725789Sahrens * Creates a snapshot with the given name. While functionally equivalent to 1726789Sahrens * 'zfs create', it is a separate command to diffferentiate intent. 1727789Sahrens */ 1728789Sahrens static int 1729789Sahrens zfs_do_snapshot(int argc, char **argv) 1730789Sahrens { 1731789Sahrens /* check options */ 1732789Sahrens if (argc > 1 && argv[1][0] == '-') { 1733789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1734789Sahrens argv[1][1]); 17352082Seschrock usage(B_FALSE); 1736789Sahrens } 1737789Sahrens 1738789Sahrens /* check number of arguments */ 1739789Sahrens if (argc < 2) { 1740789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 17412082Seschrock usage(B_FALSE); 1742789Sahrens } 1743789Sahrens if (argc > 2) { 1744789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 17452082Seschrock usage(B_FALSE); 1746789Sahrens } 1747789Sahrens 17482082Seschrock return (zfs_snapshot(g_zfs, argv[1]) != 0); 1749789Sahrens } 1750789Sahrens 1751789Sahrens /* 17521749Sahrens * zfs send [-i <fs@snap>] <fs@snap> 1753789Sahrens * 1754789Sahrens * Send a backup stream to stdout. 1755789Sahrens */ 1756789Sahrens static int 17571749Sahrens zfs_do_send(int argc, char **argv) 1758789Sahrens { 1759789Sahrens char *fromname = NULL; 1760789Sahrens zfs_handle_t *zhp_from = NULL, *zhp_to; 1761789Sahrens int c, err; 1762789Sahrens 1763789Sahrens /* check options */ 1764789Sahrens while ((c = getopt(argc, argv, ":i:")) != -1) { 1765789Sahrens switch (c) { 1766789Sahrens case 'i': 1767789Sahrens fromname = optarg; 1768789Sahrens break; 1769789Sahrens case ':': 1770789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1771789Sahrens "'%c' option\n"), optopt); 17722082Seschrock usage(B_FALSE); 1773789Sahrens break; 1774789Sahrens case '?': 1775789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1776789Sahrens optopt); 17772082Seschrock usage(B_FALSE); 1778789Sahrens } 1779789Sahrens } 1780789Sahrens 1781789Sahrens argc -= optind; 1782789Sahrens argv += optind; 1783789Sahrens 1784789Sahrens /* check number of arguments */ 1785789Sahrens if (argc < 1) { 1786789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 17872082Seschrock usage(B_FALSE); 1788789Sahrens } 1789789Sahrens if (argc > 1) { 1790789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 17912082Seschrock usage(B_FALSE); 1792789Sahrens } 1793789Sahrens 1794789Sahrens if (isatty(STDOUT_FILENO)) { 1795789Sahrens (void) fprintf(stderr, 17961749Sahrens gettext("Error: Stream can not be written " 1797789Sahrens "to a terminal.\n" 1798789Sahrens "You must redirect standard output.\n")); 1799789Sahrens return (1); 1800789Sahrens } 1801789Sahrens 1802789Sahrens if (fromname) { 18032082Seschrock if ((zhp_from = zfs_open(g_zfs, fromname, 18042082Seschrock ZFS_TYPE_SNAPSHOT)) == NULL) 1805789Sahrens return (1); 1806789Sahrens } 18072082Seschrock if ((zhp_to = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1808789Sahrens return (1); 1809789Sahrens 18101749Sahrens err = zfs_send(zhp_to, zhp_from); 1811789Sahrens 1812789Sahrens if (zhp_from) 1813789Sahrens zfs_close(zhp_from); 1814789Sahrens zfs_close(zhp_to); 1815789Sahrens 1816789Sahrens return (err != 0); 1817789Sahrens } 1818789Sahrens 1819789Sahrens /* 18201749Sahrens * zfs receive <fs@snap> 1821789Sahrens * 1822789Sahrens * Restore a backup stream from stdin. 1823789Sahrens */ 1824789Sahrens static int 18251749Sahrens zfs_do_receive(int argc, char **argv) 1826789Sahrens { 1827789Sahrens int c, err; 18282082Seschrock boolean_t isprefix = B_FALSE; 18292082Seschrock boolean_t dryrun = B_FALSE; 18302082Seschrock boolean_t verbose = B_FALSE; 1831789Sahrens 1832789Sahrens /* check options */ 1833789Sahrens while ((c = getopt(argc, argv, ":dnv")) != -1) { 1834789Sahrens switch (c) { 1835789Sahrens case 'd': 18362082Seschrock isprefix = B_TRUE; 1837789Sahrens break; 1838789Sahrens case 'n': 18392082Seschrock dryrun = B_TRUE; 1840789Sahrens break; 1841789Sahrens case 'v': 18422082Seschrock verbose = B_TRUE; 1843789Sahrens break; 1844789Sahrens case ':': 1845789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1846789Sahrens "'%c' option\n"), optopt); 18472082Seschrock usage(B_FALSE); 1848789Sahrens break; 1849789Sahrens case '?': 1850789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1851789Sahrens optopt); 18522082Seschrock usage(B_FALSE); 1853789Sahrens } 1854789Sahrens } 1855789Sahrens 1856789Sahrens argc -= optind; 1857789Sahrens argv += optind; 1858789Sahrens 1859789Sahrens /* check number of arguments */ 1860789Sahrens if (argc < 1) { 1861789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 18622082Seschrock usage(B_FALSE); 1863789Sahrens } 1864789Sahrens if (argc > 1) { 1865789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 18662082Seschrock usage(B_FALSE); 1867789Sahrens } 1868789Sahrens 1869789Sahrens if (isatty(STDIN_FILENO)) { 1870789Sahrens (void) fprintf(stderr, 1871789Sahrens gettext("Error: Backup stream can not be read " 1872789Sahrens "from a terminal.\n" 1873789Sahrens "You must redirect standard input.\n")); 1874789Sahrens return (1); 1875789Sahrens } 1876789Sahrens 18772082Seschrock err = zfs_receive(g_zfs, argv[0], isprefix, verbose, dryrun); 1878789Sahrens return (err != 0); 1879789Sahrens } 1880789Sahrens 18811356Seschrock typedef struct get_all_cbdata { 18821356Seschrock zfs_handle_t **cb_handles; 18831356Seschrock size_t cb_alloc; 18841356Seschrock size_t cb_used; 18851356Seschrock } get_all_cbdata_t; 18861356Seschrock 18871356Seschrock static int 18881356Seschrock get_one_filesystem(zfs_handle_t *zhp, void *data) 18891356Seschrock { 18901356Seschrock get_all_cbdata_t *cbp = data; 18911356Seschrock 18921356Seschrock /* 18931356Seschrock * Skip any zvols 18941356Seschrock */ 18951356Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 18961356Seschrock zfs_close(zhp); 18971356Seschrock return (0); 18981356Seschrock } 18991356Seschrock 19001356Seschrock if (cbp->cb_alloc == cbp->cb_used) { 19011356Seschrock zfs_handle_t **handles; 19021356Seschrock 19031356Seschrock if (cbp->cb_alloc == 0) 19041356Seschrock cbp->cb_alloc = 64; 19051356Seschrock else 19061356Seschrock cbp->cb_alloc *= 2; 19071356Seschrock 19081356Seschrock handles = safe_malloc(cbp->cb_alloc * sizeof (void *)); 19091356Seschrock 19101356Seschrock if (cbp->cb_handles) { 19111356Seschrock bcopy(cbp->cb_handles, handles, 19121356Seschrock cbp->cb_used * sizeof (void *)); 19131356Seschrock free(cbp->cb_handles); 19141356Seschrock } 19151356Seschrock 19161356Seschrock cbp->cb_handles = handles; 19171356Seschrock } 19181356Seschrock 19191356Seschrock cbp->cb_handles[cbp->cb_used++] = zhp; 19201356Seschrock 19211356Seschrock return (zfs_iter_filesystems(zhp, get_one_filesystem, data)); 19221356Seschrock } 19231356Seschrock 19241356Seschrock static void 19251356Seschrock get_all_filesystems(zfs_handle_t ***fslist, size_t *count) 19261356Seschrock { 19271356Seschrock get_all_cbdata_t cb = { 0 }; 19281356Seschrock 19292082Seschrock (void) zfs_iter_root(g_zfs, get_one_filesystem, &cb); 19301356Seschrock 19311356Seschrock *fslist = cb.cb_handles; 19321356Seschrock *count = cb.cb_used; 19331356Seschrock } 19341356Seschrock 19351356Seschrock static int 19361356Seschrock mountpoint_compare(const void *a, const void *b) 19371356Seschrock { 19381356Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 19391356Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 19401356Seschrock char mounta[MAXPATHLEN]; 19411356Seschrock char mountb[MAXPATHLEN]; 19421356Seschrock 19431356Seschrock verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 19442082Seschrock sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 19451356Seschrock verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 19462082Seschrock sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 19471356Seschrock 19481356Seschrock return (strcmp(mounta, mountb)); 19491356Seschrock } 1950789Sahrens 1951789Sahrens /* 1952789Sahrens * Generic callback for sharing or mounting filesystems. Because the code is so 1953789Sahrens * similar, we have a common function with an extra parameter to determine which 1954789Sahrens * mode we are using. 1955789Sahrens */ 1956789Sahrens #define OP_SHARE 0x1 1957789Sahrens #define OP_MOUNT 0x2 1958789Sahrens 1959789Sahrens typedef struct share_mount_cbdata { 1960789Sahrens int cb_type; 1961789Sahrens int cb_explicit; 1962789Sahrens int cb_flags; 1963789Sahrens const char *cb_options; 1964789Sahrens } share_mount_cbdata_t; 1965789Sahrens 1966789Sahrens /* 1967789Sahrens * Share or mount the filesystem. 1968789Sahrens */ 1969789Sahrens static int 1970789Sahrens share_mount_callback(zfs_handle_t *zhp, void *data) 1971789Sahrens { 1972789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 1973789Sahrens char shareopts[ZFS_MAXPROPLEN]; 1974789Sahrens share_mount_cbdata_t *cbp = data; 1975789Sahrens const char *cmdname = cbp->cb_type == OP_SHARE ? "share" : "mount"; 1976789Sahrens struct mnttab mnt; 1977789Sahrens uint64_t zoned; 1978789Sahrens 1979789Sahrens if (cbp->cb_options == NULL) 1980789Sahrens mnt.mnt_mntopts = ""; 1981789Sahrens else 1982789Sahrens mnt.mnt_mntopts = (char *)cbp->cb_options; 1983789Sahrens 1984789Sahrens /* 1985789Sahrens * Check to make sure we can mount/share this dataset. If we are in the 1986789Sahrens * global zone and the filesystem is exported to a local zone, or if we 1987789Sahrens * are in a local zone and the filesystem is not exported, then it is an 1988789Sahrens * error. 1989789Sahrens */ 1990789Sahrens zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 1991789Sahrens 1992789Sahrens if (zoned && getzoneid() == GLOBAL_ZONEID) { 1993789Sahrens if (!cbp->cb_explicit) 1994789Sahrens return (0); 1995789Sahrens 1996789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': dataset is " 1997789Sahrens "exported to a local zone\n"), cmdname, zfs_get_name(zhp)); 1998789Sahrens return (1); 1999789Sahrens 2000789Sahrens } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 2001789Sahrens if (!cbp->cb_explicit) 2002789Sahrens return (0); 2003789Sahrens 2004789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': permission " 2005789Sahrens "denied\n"), cmdname, zfs_get_name(zhp)); 2006789Sahrens return (1); 2007789Sahrens } 2008789Sahrens 2009789Sahrens /* 2010789Sahrens * Inore any filesystems which don't apply to us. This includes those 2011789Sahrens * with a legacy mountpoint, or those with legacy share options. 2012789Sahrens */ 2013789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 20142082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); 2015789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 20162082Seschrock sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 2017789Sahrens 2018789Sahrens if (cbp->cb_type == OP_SHARE) { 2019789Sahrens if (strcmp(shareopts, "off") == 0) { 2020789Sahrens if (!cbp->cb_explicit) 2021789Sahrens return (0); 2022789Sahrens 2023789Sahrens (void) fprintf(stderr, gettext("cannot share '%s': " 2024789Sahrens "legacy share\n"), zfs_get_name(zhp)); 2025789Sahrens (void) fprintf(stderr, gettext("use share(1M) to " 2026789Sahrens "share this filesystem\n")); 2027789Sahrens return (1); 2028789Sahrens } 2029789Sahrens } 2030789Sahrens 2031789Sahrens /* 2032789Sahrens * We cannot share or mount legacy filesystems. If the shareopts is 2033789Sahrens * non-legacy but the mountpoint is legacy, we treat it as a legacy 2034789Sahrens * share. 2035789Sahrens */ 2036789Sahrens if (strcmp(mountpoint, "legacy") == 0) { 2037789Sahrens if (!cbp->cb_explicit) 2038789Sahrens return (0); 2039789Sahrens 2040789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': " 2041789Sahrens "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 2042789Sahrens (void) fprintf(stderr, gettext("use %s to " 2043789Sahrens "%s this filesystem\n"), cbp->cb_type == OP_SHARE ? 2044789Sahrens "share(1M)" : "mount(1M)", cmdname); 2045789Sahrens return (1); 2046789Sahrens } 2047789Sahrens 2048789Sahrens if (strcmp(mountpoint, "none") == 0) { 2049789Sahrens if (!cbp->cb_explicit) 2050789Sahrens return (0); 2051789Sahrens 2052789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': no " 2053789Sahrens "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 2054789Sahrens return (1); 2055789Sahrens } 2056789Sahrens 2057789Sahrens /* 2058789Sahrens * At this point, we have verified that the mountpoint and/or shareopts 2059789Sahrens * are appropriate for auto management. Determine if the filesystem is 2060789Sahrens * currently mounted or shared, and abort if this is an explicit 2061789Sahrens * request. 2062789Sahrens */ 2063789Sahrens switch (cbp->cb_type) { 2064789Sahrens case OP_SHARE: 2065789Sahrens if (zfs_is_shared(zhp, NULL)) { 2066789Sahrens if (cbp->cb_explicit) { 2067789Sahrens (void) fprintf(stderr, gettext("cannot share " 2068789Sahrens "'%s': filesystem already shared\n"), 2069789Sahrens zfs_get_name(zhp)); 2070789Sahrens return (1); 2071789Sahrens } else { 2072789Sahrens return (0); 2073789Sahrens } 2074789Sahrens } 2075789Sahrens break; 2076789Sahrens 2077789Sahrens case OP_MOUNT: 2078789Sahrens if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 2079789Sahrens zfs_is_mounted(zhp, NULL)) { 2080789Sahrens if (cbp->cb_explicit) { 2081789Sahrens (void) fprintf(stderr, gettext("cannot mount " 2082789Sahrens "'%s': filesystem already mounted\n"), 2083789Sahrens zfs_get_name(zhp)); 2084789Sahrens return (1); 2085789Sahrens } else { 2086789Sahrens return (0); 2087789Sahrens } 2088789Sahrens } 2089789Sahrens break; 2090789Sahrens } 2091789Sahrens 2092789Sahrens /* 2093789Sahrens * Mount and optionally share the filesystem. 2094789Sahrens */ 2095789Sahrens switch (cbp->cb_type) { 2096789Sahrens case OP_SHARE: 2097789Sahrens { 2098789Sahrens if (!zfs_is_mounted(zhp, NULL) && 2099789Sahrens zfs_mount(zhp, NULL, 0) != 0) 2100789Sahrens return (1); 2101789Sahrens 2102789Sahrens if (zfs_share(zhp) != 0) 2103789Sahrens return (1); 2104789Sahrens } 2105789Sahrens break; 2106789Sahrens 2107789Sahrens case OP_MOUNT: 2108789Sahrens if (zfs_mount(zhp, cbp->cb_options, cbp->cb_flags) != 0) 2109789Sahrens return (1); 2110789Sahrens break; 2111789Sahrens } 2112789Sahrens 2113789Sahrens return (0); 2114789Sahrens } 2115789Sahrens 2116789Sahrens static int 2117789Sahrens share_or_mount(int type, int argc, char **argv) 2118789Sahrens { 2119789Sahrens int do_all = 0; 2120789Sahrens int c, ret; 2121789Sahrens share_mount_cbdata_t cb = { 0 }; 2122789Sahrens 2123789Sahrens cb.cb_type = type; 2124789Sahrens 2125789Sahrens /* check options */ 2126789Sahrens while ((c = getopt(argc, argv, type == OP_MOUNT ? ":ao:O" : "a")) 2127789Sahrens != -1) { 2128789Sahrens switch (c) { 2129789Sahrens case 'a': 2130789Sahrens do_all = 1; 2131789Sahrens break; 2132789Sahrens case 'o': 2133789Sahrens cb.cb_options = optarg; 2134789Sahrens break; 2135789Sahrens case 'O': 2136789Sahrens cb.cb_flags |= MS_OVERLAY; 2137789Sahrens break; 2138789Sahrens case ':': 2139789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2140789Sahrens "'%c' option\n"), optopt); 21412082Seschrock usage(B_FALSE); 2142789Sahrens break; 2143789Sahrens case '?': 2144789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2145789Sahrens optopt); 21462082Seschrock usage(B_FALSE); 2147789Sahrens } 2148789Sahrens } 2149789Sahrens 2150789Sahrens argc -= optind; 2151789Sahrens argv += optind; 2152789Sahrens 2153789Sahrens /* check number of arguments */ 2154789Sahrens if (do_all) { 21551356Seschrock zfs_handle_t **fslist = NULL; 21561356Seschrock size_t i, count = 0; 21571356Seschrock 2158789Sahrens if (argc != 0) { 2159789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 21602082Seschrock usage(B_FALSE); 2161789Sahrens } 2162789Sahrens 21631356Seschrock get_all_filesystems(&fslist, &count); 21641356Seschrock 21651356Seschrock if (count == 0) 21661356Seschrock return (0); 21671356Seschrock 21681356Seschrock qsort(fslist, count, sizeof (void *), mountpoint_compare); 21691356Seschrock 21701356Seschrock for (i = 0; i < count; i++) { 21711356Seschrock if ((ret = share_mount_callback(fslist[i], &cb)) != 0) 21721356Seschrock break; 21731356Seschrock } 21741356Seschrock 21751356Seschrock for (i = 0; i < count; i++) 21761356Seschrock zfs_close(fslist[i]); 21771356Seschrock 21781356Seschrock free(fslist); 2179789Sahrens } else if (argc == 0) { 2180789Sahrens struct mnttab entry; 2181789Sahrens 2182789Sahrens if (type == OP_SHARE) { 2183789Sahrens (void) fprintf(stderr, gettext("missing filesystem " 2184789Sahrens "argument\n")); 21852082Seschrock usage(B_FALSE); 2186789Sahrens } 2187789Sahrens 2188789Sahrens /* 2189789Sahrens * When mount is given no arguments, go through /etc/mnttab and 2190789Sahrens * display any active ZFS mounts. We hide any snapshots, since 2191789Sahrens * they are controlled automatically. 2192789Sahrens */ 2193789Sahrens rewind(mnttab_file); 2194789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2195789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 2196789Sahrens strchr(entry.mnt_special, '@') != NULL) 2197789Sahrens continue; 2198789Sahrens 2199789Sahrens (void) printf("%-30s %s\n", entry.mnt_special, 2200789Sahrens entry.mnt_mountp); 2201789Sahrens } 2202789Sahrens 2203789Sahrens ret = 0; 2204789Sahrens } else { 2205789Sahrens zfs_handle_t *zhp; 2206789Sahrens 2207789Sahrens if (argc > 1) { 2208789Sahrens (void) fprintf(stderr, 2209789Sahrens gettext("too many arguments\n")); 22102082Seschrock usage(B_FALSE); 2211789Sahrens } 2212789Sahrens 22132082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], 22142082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2215789Sahrens ret = 1; 2216789Sahrens else { 22172082Seschrock cb.cb_explicit = B_TRUE; 2218789Sahrens ret = share_mount_callback(zhp, &cb); 2219789Sahrens zfs_close(zhp); 2220789Sahrens } 2221789Sahrens } 2222789Sahrens 2223789Sahrens return (ret); 2224789Sahrens } 2225789Sahrens 2226789Sahrens /* 2227789Sahrens * zfs mount -a 2228789Sahrens * zfs mount filesystem 2229789Sahrens * 2230789Sahrens * Mount all filesystems, or mount the given filesystem. 2231789Sahrens */ 2232789Sahrens static int 2233789Sahrens zfs_do_mount(int argc, char **argv) 2234789Sahrens { 2235789Sahrens return (share_or_mount(OP_MOUNT, argc, argv)); 2236789Sahrens } 2237789Sahrens 2238789Sahrens /* 2239789Sahrens * zfs share -a 2240789Sahrens * zfs share filesystem 2241789Sahrens * 2242789Sahrens * Share all filesystems, or share the given filesystem. 2243789Sahrens */ 2244789Sahrens static int 2245789Sahrens zfs_do_share(int argc, char **argv) 2246789Sahrens { 2247789Sahrens return (share_or_mount(OP_SHARE, argc, argv)); 2248789Sahrens } 2249789Sahrens 2250789Sahrens typedef struct unshare_unmount_node { 2251789Sahrens zfs_handle_t *un_zhp; 2252789Sahrens char *un_mountp; 2253789Sahrens uu_avl_node_t un_avlnode; 2254789Sahrens } unshare_unmount_node_t; 2255789Sahrens 2256789Sahrens /* ARGSUSED */ 2257789Sahrens static int 2258789Sahrens unshare_unmount_compare(const void *larg, const void *rarg, void *unused) 2259789Sahrens { 2260789Sahrens const unshare_unmount_node_t *l = larg; 2261789Sahrens const unshare_unmount_node_t *r = rarg; 2262789Sahrens 2263789Sahrens return (strcmp(l->un_mountp, r->un_mountp)); 2264789Sahrens } 2265789Sahrens 2266789Sahrens /* 2267789Sahrens * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 2268789Sahrens * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 2269789Sahrens * and unmount it appropriately. 2270789Sahrens */ 2271789Sahrens static int 22722082Seschrock unshare_unmount_path(int type, char *path, int flags, boolean_t is_manual) 2273789Sahrens { 2274789Sahrens zfs_handle_t *zhp; 2275789Sahrens int ret; 2276789Sahrens struct stat64 statbuf; 2277789Sahrens struct extmnttab entry; 2278789Sahrens const char *cmdname = (type == OP_SHARE) ? "unshare" : "unmount"; 2279789Sahrens char property[ZFS_MAXPROPLEN]; 2280789Sahrens 2281789Sahrens /* 2282789Sahrens * Search for the path in /etc/mnttab. Rather than looking for the 2283789Sahrens * specific path, which can be fooled by non-standard paths (i.e. ".." 2284789Sahrens * or "//"), we stat() the path and search for the corresponding 2285789Sahrens * (major,minor) device pair. 2286789Sahrens */ 2287789Sahrens if (stat64(path, &statbuf) != 0) { 2288789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 2289789Sahrens cmdname, path, strerror(errno)); 2290789Sahrens return (1); 2291789Sahrens } 2292789Sahrens 2293789Sahrens /* 2294789Sahrens * Search for the given (major,minor) pair in the mount table. 2295789Sahrens */ 2296789Sahrens rewind(mnttab_file); 2297789Sahrens while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 2298789Sahrens if (entry.mnt_major == major(statbuf.st_dev) && 2299789Sahrens entry.mnt_minor == minor(statbuf.st_dev)) 2300789Sahrens break; 2301789Sahrens } 2302789Sahrens if (ret != 0) { 2303789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not " 2304789Sahrens "currently mounted\n"), cmdname, path); 2305789Sahrens return (1); 2306789Sahrens } 2307789Sahrens 2308789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 2309789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 2310789Sahrens "filesystem\n"), cmdname, path); 2311789Sahrens return (1); 2312789Sahrens } 2313789Sahrens 23142082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 23152082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2316789Sahrens return (1); 2317789Sahrens 2318789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2319789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 23202082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2321789Sahrens 2322789Sahrens if (type == OP_SHARE) { 2323789Sahrens if (strcmp(property, "off") == 0) { 2324789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2325789Sahrens "'%s': legacy share\n"), path); 2326789Sahrens (void) fprintf(stderr, gettext("use " 2327789Sahrens "unshare(1M) to unshare this filesystem\n")); 2328789Sahrens ret = 1; 2329789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2330789Sahrens (void) fprintf(stderr, gettext("cannot unshare '%s': " 2331789Sahrens "not currently shared\n"), path); 2332789Sahrens ret = 1; 2333789Sahrens } else { 2334789Sahrens ret = zfs_unshareall(zhp); 2335789Sahrens } 2336789Sahrens } else { 23371264Slling if (is_manual) { 23381264Slling ret = zfs_unmount(zhp, NULL, flags); 23391264Slling } else if (strcmp(property, "legacy") == 0) { 23401264Slling (void) fprintf(stderr, gettext("cannot unmount " 23411264Slling "'%s': legacy mountpoint\n"), 23421264Slling zfs_get_name(zhp)); 23431264Slling (void) fprintf(stderr, gettext("use umount(1M) " 23441264Slling "to unmount this filesystem\n")); 23451264Slling ret = 1; 2346789Sahrens } else { 2347789Sahrens ret = zfs_unmountall(zhp, flags); 2348789Sahrens } 2349789Sahrens } 2350789Sahrens 2351789Sahrens zfs_close(zhp); 2352789Sahrens 2353789Sahrens return (ret != 0); 2354789Sahrens } 2355789Sahrens 2356789Sahrens /* 2357789Sahrens * Generic callback for unsharing or unmounting a filesystem. 2358789Sahrens */ 2359789Sahrens static int 2360789Sahrens unshare_unmount(int type, int argc, char **argv) 2361789Sahrens { 2362789Sahrens int do_all = 0; 2363789Sahrens int flags = 0; 2364789Sahrens int ret = 0; 2365789Sahrens int c; 2366789Sahrens zfs_handle_t *zhp; 2367789Sahrens char property[ZFS_MAXPROPLEN]; 2368789Sahrens 2369789Sahrens /* check options */ 2370789Sahrens while ((c = getopt(argc, argv, type == OP_SHARE ? "a" : "af")) != -1) { 2371789Sahrens switch (c) { 2372789Sahrens case 'a': 2373789Sahrens do_all = 1; 2374789Sahrens break; 2375789Sahrens case 'f': 2376789Sahrens flags = MS_FORCE; 2377789Sahrens break; 2378789Sahrens case '?': 2379789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2380789Sahrens optopt); 23812082Seschrock usage(B_FALSE); 2382789Sahrens } 2383789Sahrens } 2384789Sahrens 2385789Sahrens argc -= optind; 2386789Sahrens argv += optind; 2387789Sahrens 2388789Sahrens /* ensure correct number of arguments */ 2389789Sahrens if (do_all) { 2390789Sahrens if (argc != 0) { 2391789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 23922082Seschrock usage(B_FALSE); 2393789Sahrens } 2394789Sahrens } else if (argc != 1) { 2395789Sahrens if (argc == 0) 2396789Sahrens (void) fprintf(stderr, 2397789Sahrens gettext("missing filesystem argument\n")); 2398789Sahrens else 2399789Sahrens (void) fprintf(stderr, 2400789Sahrens gettext("too many arguments\n")); 24012082Seschrock usage(B_FALSE); 2402789Sahrens } 2403789Sahrens 2404789Sahrens if (do_all) { 2405789Sahrens /* 2406789Sahrens * We could make use of zfs_for_each() to walk all datasets in 2407789Sahrens * the system, but this would be very inefficient, especially 2408789Sahrens * since we would have to linearly search /etc/mnttab for each 2409789Sahrens * one. Instead, do one pass through /etc/mnttab looking for 2410789Sahrens * zfs entries and call zfs_unmount() for each one. 2411789Sahrens * 2412789Sahrens * Things get a little tricky if the administrator has created 2413789Sahrens * mountpoints beneath other ZFS filesystems. In this case, we 2414789Sahrens * have to unmount the deepest filesystems first. To accomplish 2415789Sahrens * this, we place all the mountpoints in an AVL tree sorted by 2416789Sahrens * the special type (dataset name), and walk the result in 2417789Sahrens * reverse to make sure to get any snapshots first. 2418789Sahrens */ 2419789Sahrens struct mnttab entry; 2420789Sahrens uu_avl_pool_t *pool; 2421789Sahrens uu_avl_t *tree; 2422789Sahrens unshare_unmount_node_t *node; 2423789Sahrens uu_avl_index_t idx; 2424789Sahrens uu_avl_walk_t *walk; 2425789Sahrens 2426789Sahrens if ((pool = uu_avl_pool_create("unmount_pool", 2427789Sahrens sizeof (unshare_unmount_node_t), 2428789Sahrens offsetof(unshare_unmount_node_t, un_avlnode), 2429789Sahrens unshare_unmount_compare, 2430789Sahrens UU_DEFAULT)) == NULL) { 2431789Sahrens (void) fprintf(stderr, gettext("internal error: " 2432789Sahrens "out of memory\n")); 2433789Sahrens exit(1); 2434789Sahrens } 2435789Sahrens 2436789Sahrens if ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL) { 2437789Sahrens (void) fprintf(stderr, gettext("internal error: " 2438789Sahrens "out of memory\n")); 2439789Sahrens exit(1); 2440789Sahrens } 2441789Sahrens 2442789Sahrens rewind(mnttab_file); 2443789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2444789Sahrens 2445789Sahrens /* ignore non-ZFS entries */ 2446789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 2447789Sahrens continue; 2448789Sahrens 2449789Sahrens /* ignore snapshots */ 2450789Sahrens if (strchr(entry.mnt_special, '@') != NULL) 2451789Sahrens continue; 2452789Sahrens 24532082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 2454789Sahrens ZFS_TYPE_FILESYSTEM)) == NULL) { 2455789Sahrens ret = 1; 2456789Sahrens continue; 2457789Sahrens } 2458789Sahrens 2459789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2460789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 2461789Sahrens property, sizeof (property), NULL, NULL, 24622082Seschrock 0, B_FALSE) == 0); 2463789Sahrens 2464789Sahrens /* Ignore legacy mounts and shares */ 2465789Sahrens if ((type == OP_SHARE && 2466789Sahrens strcmp(property, "off") == 0) || 2467789Sahrens (type == OP_MOUNT && 2468789Sahrens strcmp(property, "legacy") == 0)) { 2469789Sahrens zfs_close(zhp); 2470789Sahrens continue; 2471789Sahrens } 2472789Sahrens 2473789Sahrens node = safe_malloc(sizeof (unshare_unmount_node_t)); 2474789Sahrens node->un_zhp = zhp; 2475789Sahrens 2476789Sahrens if ((node->un_mountp = strdup(entry.mnt_mountp)) == 2477789Sahrens NULL) { 2478789Sahrens (void) fprintf(stderr, gettext("internal error:" 2479789Sahrens " out of memory\n")); 2480789Sahrens exit(1); 2481789Sahrens } 2482789Sahrens 2483789Sahrens uu_avl_node_init(node, &node->un_avlnode, pool); 2484789Sahrens 2485789Sahrens if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 2486789Sahrens uu_avl_insert(tree, node, idx); 2487789Sahrens } else { 2488789Sahrens zfs_close(node->un_zhp); 2489789Sahrens free(node->un_mountp); 2490789Sahrens free(node); 2491789Sahrens } 2492789Sahrens } 2493789Sahrens 2494789Sahrens /* 2495789Sahrens * Walk the AVL tree in reverse, unmounting each filesystem and 2496789Sahrens * removing it from the AVL tree in the process. 2497789Sahrens */ 2498789Sahrens if ((walk = uu_avl_walk_start(tree, 2499789Sahrens UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) { 2500789Sahrens (void) fprintf(stderr, 2501789Sahrens gettext("internal error: out of memory")); 2502789Sahrens exit(1); 2503789Sahrens } 2504789Sahrens 2505789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 2506789Sahrens uu_avl_remove(tree, node); 2507789Sahrens 2508789Sahrens switch (type) { 2509789Sahrens case OP_SHARE: 2510789Sahrens if (zfs_unshare(node->un_zhp, 2511789Sahrens node->un_mountp) != 0) 2512789Sahrens ret = 1; 2513789Sahrens break; 2514789Sahrens 2515789Sahrens case OP_MOUNT: 2516789Sahrens if (zfs_unmount(node->un_zhp, 2517789Sahrens node->un_mountp, flags) != 0) 2518789Sahrens ret = 1; 2519789Sahrens break; 2520789Sahrens } 2521789Sahrens 2522789Sahrens zfs_close(node->un_zhp); 2523789Sahrens free(node->un_mountp); 2524789Sahrens free(node); 2525789Sahrens } 2526789Sahrens 2527789Sahrens uu_avl_walk_end(walk); 2528789Sahrens uu_avl_destroy(tree); 2529789Sahrens uu_avl_pool_destroy(pool); 2530789Sahrens } else { 2531789Sahrens /* 2532789Sahrens * We have an argument, but it may be a full path or a ZFS 2533789Sahrens * filesystem. Pass full paths off to unmount_path() (shared by 2534789Sahrens * manual_unmount), otherwise open the filesystem and pass to 2535789Sahrens * zfs_unmount(). 2536789Sahrens */ 2537789Sahrens if (argv[0][0] == '/') 2538789Sahrens return (unshare_unmount_path(type, argv[0], 25392082Seschrock flags, B_FALSE)); 25402082Seschrock 25412082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], 25422082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2543789Sahrens return (1); 2544789Sahrens 2545789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2546789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 25472082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2548789Sahrens 2549789Sahrens switch (type) { 2550789Sahrens case OP_SHARE: 2551789Sahrens if (strcmp(property, "off") == 0) { 2552789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2553789Sahrens "'%s': legacy share\n"), zfs_get_name(zhp)); 2554789Sahrens (void) fprintf(stderr, gettext("use unshare(1M)" 2555789Sahrens " to unshare this filesystem\n")); 2556789Sahrens ret = 1; 2557789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2558789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2559789Sahrens "'%s': not currently shared\n"), 2560789Sahrens zfs_get_name(zhp)); 2561789Sahrens ret = 1; 2562789Sahrens } else if (zfs_unshareall(zhp) != 0) { 2563789Sahrens ret = 1; 2564789Sahrens } 2565789Sahrens break; 2566789Sahrens 2567789Sahrens case OP_MOUNT: 2568789Sahrens if (strcmp(property, "legacy") == 0) { 2569789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2570789Sahrens "'%s': legacy mountpoint\n"), 2571789Sahrens zfs_get_name(zhp)); 2572789Sahrens (void) fprintf(stderr, gettext("use umount(1M) " 2573789Sahrens "to unmount this filesystem\n")); 2574789Sahrens ret = 1; 2575789Sahrens } else if (!zfs_is_mounted(zhp, NULL)) { 2576789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2577789Sahrens "'%s': not currently mounted\n"), 2578789Sahrens zfs_get_name(zhp)); 2579789Sahrens ret = 1; 2580789Sahrens } else if (zfs_unmountall(zhp, flags) != 0) { 2581789Sahrens ret = 1; 2582789Sahrens } 2583789Sahrens } 2584789Sahrens 2585789Sahrens zfs_close(zhp); 2586789Sahrens } 2587789Sahrens 2588789Sahrens return (ret); 2589789Sahrens } 2590789Sahrens 2591789Sahrens /* 2592789Sahrens * zfs unmount -a 2593789Sahrens * zfs unmount filesystem 2594789Sahrens * 2595789Sahrens * Unmount all filesystems, or a specific ZFS filesystem. 2596789Sahrens */ 2597789Sahrens static int 2598789Sahrens zfs_do_unmount(int argc, char **argv) 2599789Sahrens { 2600789Sahrens return (unshare_unmount(OP_MOUNT, argc, argv)); 2601789Sahrens } 2602789Sahrens 2603789Sahrens /* 2604789Sahrens * zfs unshare -a 2605789Sahrens * zfs unshare filesystem 2606789Sahrens * 2607789Sahrens * Unshare all filesystems, or a specific ZFS filesystem. 2608789Sahrens */ 2609789Sahrens static int 2610789Sahrens zfs_do_unshare(int argc, char **argv) 2611789Sahrens { 2612789Sahrens return (unshare_unmount(OP_SHARE, argc, argv)); 2613789Sahrens } 2614789Sahrens 2615789Sahrens /* 2616789Sahrens * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 2617789Sahrens * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 2618789Sahrens */ 2619789Sahrens static int 2620789Sahrens manual_mount(int argc, char **argv) 2621789Sahrens { 2622789Sahrens zfs_handle_t *zhp; 2623789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2624789Sahrens char mntopts[MNT_LINE_MAX] = { '\0' }; 2625789Sahrens int ret; 2626789Sahrens int c; 2627789Sahrens int flags = 0; 2628789Sahrens char *dataset, *path; 2629789Sahrens 2630789Sahrens /* check options */ 26311544Seschrock while ((c = getopt(argc, argv, ":mo:O")) != -1) { 2632789Sahrens switch (c) { 2633789Sahrens case 'o': 2634789Sahrens (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 2635789Sahrens break; 2636789Sahrens case 'O': 2637789Sahrens flags |= MS_OVERLAY; 2638789Sahrens break; 26391544Seschrock case 'm': 26401544Seschrock flags |= MS_NOMNTTAB; 26411544Seschrock break; 2642789Sahrens case ':': 2643789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2644789Sahrens "'%c' option\n"), optopt); 26452082Seschrock usage(B_FALSE); 2646789Sahrens break; 2647789Sahrens case '?': 2648789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2649789Sahrens optopt); 2650789Sahrens (void) fprintf(stderr, gettext("usage: mount [-o opts] " 2651789Sahrens "<path>\n")); 2652789Sahrens return (2); 2653789Sahrens } 2654789Sahrens } 2655789Sahrens 2656789Sahrens argc -= optind; 2657789Sahrens argv += optind; 2658789Sahrens 2659789Sahrens /* check that we only have two arguments */ 2660789Sahrens if (argc != 2) { 2661789Sahrens if (argc == 0) 2662789Sahrens (void) fprintf(stderr, gettext("missing dataset " 2663789Sahrens "argument\n")); 2664789Sahrens else if (argc == 1) 2665789Sahrens (void) fprintf(stderr, 2666789Sahrens gettext("missing mountpoint argument\n")); 2667789Sahrens else 2668789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2669789Sahrens (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 2670789Sahrens return (2); 2671789Sahrens } 2672789Sahrens 2673789Sahrens dataset = argv[0]; 2674789Sahrens path = argv[1]; 2675789Sahrens 2676789Sahrens /* try to open the dataset */ 26772082Seschrock if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 2678789Sahrens return (1); 2679789Sahrens 2680789Sahrens (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 26812082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE); 2682789Sahrens 2683789Sahrens /* check for legacy mountpoint and complain appropriately */ 2684789Sahrens ret = 0; 2685789Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 2686789Sahrens if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS, 2687789Sahrens NULL, 0, mntopts, sizeof (mntopts)) != 0) { 2688789Sahrens (void) fprintf(stderr, gettext("mount failed: %s\n"), 2689789Sahrens strerror(errno)); 2690789Sahrens ret = 1; 2691789Sahrens } 2692789Sahrens } else { 2693789Sahrens (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 2694789Sahrens "mounted using 'mount -F zfs'\n"), dataset); 2695789Sahrens (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 2696789Sahrens "instead.\n"), path); 2697789Sahrens (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' " 2698789Sahrens "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n")); 2699789Sahrens (void) fprintf(stderr, gettext("See zfs(1M) for more " 2700789Sahrens "information.\n")); 2701789Sahrens ret = 1; 2702789Sahrens } 2703789Sahrens 2704789Sahrens return (ret); 2705789Sahrens } 2706789Sahrens 2707789Sahrens /* 2708789Sahrens * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 2709789Sahrens * unmounts of non-legacy filesystems, as this is the dominant administrative 2710789Sahrens * interface. 2711789Sahrens */ 2712789Sahrens static int 2713789Sahrens manual_unmount(int argc, char **argv) 2714789Sahrens { 2715789Sahrens int flags = 0; 2716789Sahrens int c; 2717789Sahrens 2718789Sahrens /* check options */ 2719789Sahrens while ((c = getopt(argc, argv, "f")) != -1) { 2720789Sahrens switch (c) { 2721789Sahrens case 'f': 2722789Sahrens flags = MS_FORCE; 2723789Sahrens break; 2724789Sahrens case '?': 2725789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2726789Sahrens optopt); 2727789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] " 2728789Sahrens "<path>\n")); 2729789Sahrens return (2); 2730789Sahrens } 2731789Sahrens } 2732789Sahrens 2733789Sahrens argc -= optind; 2734789Sahrens argv += optind; 2735789Sahrens 2736789Sahrens /* check arguments */ 2737789Sahrens if (argc != 1) { 2738789Sahrens if (argc == 0) 2739789Sahrens (void) fprintf(stderr, gettext("missing path " 2740789Sahrens "argument\n")); 2741789Sahrens else 2742789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2743789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 2744789Sahrens return (2); 2745789Sahrens } 2746789Sahrens 27472082Seschrock return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE)); 2748789Sahrens } 2749789Sahrens 2750789Sahrens static int 2751789Sahrens volcheck(zpool_handle_t *zhp, void *data) 2752789Sahrens { 2753789Sahrens int isinit = (int)data; 2754789Sahrens 2755789Sahrens if (isinit) 2756789Sahrens return (zpool_create_zvol_links(zhp)); 2757789Sahrens else 2758789Sahrens return (zpool_remove_zvol_links(zhp)); 2759789Sahrens } 2760789Sahrens 2761789Sahrens /* 2762789Sahrens * Iterate over all pools in the system and either create or destroy /dev/zvol 2763789Sahrens * links, depending on the value of 'isinit'. 2764789Sahrens */ 2765789Sahrens static int 27662082Seschrock do_volcheck(boolean_t isinit) 2767789Sahrens { 27682082Seschrock return (zpool_iter(g_zfs, volcheck, (void *)isinit) ? 1 : 0); 2769789Sahrens } 2770789Sahrens 2771789Sahrens int 2772789Sahrens main(int argc, char **argv) 2773789Sahrens { 2774789Sahrens int ret; 2775789Sahrens int i; 2776789Sahrens char *progname; 2777789Sahrens char *cmdname; 2778789Sahrens 2779789Sahrens (void) setlocale(LC_ALL, ""); 2780789Sahrens (void) textdomain(TEXT_DOMAIN); 2781789Sahrens 2782789Sahrens opterr = 0; 2783789Sahrens 27842082Seschrock if ((g_zfs = libzfs_init()) == NULL) { 27852082Seschrock (void) fprintf(stderr, gettext("internal error: failed to " 27862082Seschrock "initialize ZFS library\n")); 27872082Seschrock return (1); 27882082Seschrock } 27892082Seschrock 27902082Seschrock libzfs_print_on_error(g_zfs, B_TRUE); 27912082Seschrock 2792789Sahrens if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 2793789Sahrens (void) fprintf(stderr, gettext("internal error: unable to " 2794789Sahrens "open %s\n"), MNTTAB); 2795789Sahrens return (1); 2796789Sahrens } 2797789Sahrens 2798789Sahrens /* 2799789Sahrens * This command also doubles as the /etc/fs mount and unmount program. 2800789Sahrens * Determine if we should take this behavior based on argv[0]. 2801789Sahrens */ 2802789Sahrens progname = basename(argv[0]); 2803789Sahrens if (strcmp(progname, "mount") == 0) { 2804789Sahrens ret = manual_mount(argc, argv); 2805789Sahrens } else if (strcmp(progname, "umount") == 0) { 2806789Sahrens ret = manual_unmount(argc, argv); 2807789Sahrens } else { 2808789Sahrens /* 2809789Sahrens * Make sure the user has specified some command. 2810789Sahrens */ 2811789Sahrens if (argc < 2) { 2812789Sahrens (void) fprintf(stderr, gettext("missing command\n")); 28132082Seschrock usage(B_FALSE); 2814789Sahrens } 2815789Sahrens 2816789Sahrens cmdname = argv[1]; 2817789Sahrens 2818789Sahrens /* 2819789Sahrens * The 'umount' command is an alias for 'unmount' 2820789Sahrens */ 2821789Sahrens if (strcmp(cmdname, "umount") == 0) 2822789Sahrens cmdname = "unmount"; 2823789Sahrens 2824789Sahrens /* 28251749Sahrens * The 'recv' command is an alias for 'receive' 28261749Sahrens */ 28271749Sahrens if (strcmp(cmdname, "recv") == 0) 28281749Sahrens cmdname = "receive"; 28291749Sahrens 28301749Sahrens /* 2831789Sahrens * Special case '-?' 2832789Sahrens */ 2833789Sahrens if (strcmp(cmdname, "-?") == 0) 28342082Seschrock usage(B_TRUE); 2835789Sahrens 2836789Sahrens /* 2837789Sahrens * 'volinit' and 'volfini' do not appear in the usage message, 2838789Sahrens * so we have to special case them here. 2839789Sahrens */ 2840789Sahrens if (strcmp(cmdname, "volinit") == 0) 28412082Seschrock return (do_volcheck(B_TRUE)); 2842789Sahrens else if (strcmp(cmdname, "volfini") == 0) 28432082Seschrock return (do_volcheck(B_FALSE)); 2844789Sahrens 2845789Sahrens /* 2846789Sahrens * Run the appropriate command. 2847789Sahrens */ 2848789Sahrens for (i = 0; i < NCOMMAND; i++) { 2849789Sahrens if (command_table[i].name == NULL) 2850789Sahrens continue; 2851789Sahrens 2852789Sahrens if (strcmp(cmdname, command_table[i].name) == 0) { 2853789Sahrens current_command = &command_table[i]; 2854789Sahrens ret = command_table[i].func(argc - 1, argv + 1); 2855789Sahrens break; 2856789Sahrens } 2857789Sahrens } 2858789Sahrens 2859789Sahrens if (i == NCOMMAND) { 2860789Sahrens (void) fprintf(stderr, gettext("unrecognized " 2861789Sahrens "command '%s'\n"), cmdname); 28622082Seschrock usage(B_FALSE); 2863789Sahrens } 2864789Sahrens } 2865789Sahrens 2866789Sahrens (void) fclose(mnttab_file); 2867789Sahrens 28682082Seschrock libzfs_fini(g_zfs); 28692082Seschrock 2870789Sahrens /* 2871789Sahrens * The 'ZFS_ABORT' environment variable causes us to dump core on exit 2872789Sahrens * for the purposes of running ::findleaks. 2873789Sahrens */ 2874789Sahrens if (getenv("ZFS_ABORT") != NULL) { 2875789Sahrens (void) printf("dumping core by request\n"); 2876789Sahrens abort(); 2877789Sahrens } 2878789Sahrens 2879789Sahrens return (ret); 2880789Sahrens } 2881