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" 50*2082Seschrock #include "zfs_util.h" 51*2082Seschrock 52*2082Seschrock 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); 72*2082Seschrock 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, 98*2082Seschrock 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 }, 132*2082Seschrock { "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")); 185*2082Seschrock case HELP_PROMOTE: 186*2082Seschrock 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 239*2082Seschrock usage(boolean_t requested) 240789Sahrens { 241789Sahrens int i; 242*2082Seschrock 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) 271*2082Seschrock 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]); 324*2082Seschrock 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")); 331*2082Seschrock usage(B_FALSE); 332789Sahrens } 333789Sahrens if (argc < 3) { 334789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 335789Sahrens "argument\n")); 336*2082Seschrock usage(B_FALSE); 337789Sahrens } 338789Sahrens if (argc > 3) { 339789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 340*2082Seschrock usage(B_FALSE); 341789Sahrens } 342789Sahrens 343789Sahrens /* open the source dataset */ 344*2082Seschrock 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) { 352*2082Seschrock 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; 385*2082Seschrock 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': 399*2082Seschrock noreserve = B_TRUE; 400789Sahrens break; 401789Sahrens case ':': 402789Sahrens (void) fprintf(stderr, gettext("missing size " 403789Sahrens "argument\n")); 404*2082Seschrock usage(B_FALSE); 405789Sahrens break; 406789Sahrens case '?': 407789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 408789Sahrens optopt); 409*2082Seschrock 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")); 416*2082Seschrock 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)); 426*2082Seschrock usage(B_FALSE); 427789Sahrens } 428789Sahrens if (argc > 1) { 429789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 430*2082Seschrock usage(B_FALSE); 431789Sahrens } 432789Sahrens 433789Sahrens /* pass to libzfs */ 434*2082Seschrock if (zfs_create(g_zfs, argv[0], type, size, blocksize) != 0) 435789Sahrens return (1); 436789Sahrens 437*2082Seschrock 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 { 487*2082Seschrock 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")); 522*2082Seschrock 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")); 543*2082Seschrock 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); 608*2082Seschrock 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")); 618*2082Seschrock usage(B_FALSE); 619789Sahrens } 620789Sahrens if (argc > 1) { 621789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 622*2082Seschrock usage(B_FALSE); 623789Sahrens } 624789Sahrens 625789Sahrens /* Open the given dataset */ 626*2082Seschrock 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 */ 652*2082Seschrock 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]; 691*2082Seschrock int cb_nprop; 692*2082Seschrock boolean_t cb_scripted; 693*2082Seschrock boolean_t cb_literal; 694*2082Seschrock 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 }; 815*2082Seschrock 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': 834*2082Seschrock cb.cb_literal = B_TRUE; 835789Sahrens break; 836789Sahrens case 'r': 837*2082Seschrock recurse = B_TRUE; 838789Sahrens break; 839789Sahrens case 'H': 840*2082Seschrock cb.cb_scripted = B_TRUE; 841789Sahrens break; 842789Sahrens case ':': 843789Sahrens (void) fprintf(stderr, gettext("missing argument for " 844789Sahrens "'%c' option\n"), optopt); 845*2082Seschrock 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")); 863*2082Seschrock 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); 884*2082Seschrock 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); 917*2082Seschrock usage(B_FALSE); 918789Sahrens } 919789Sahrens } 920789Sahrens break; 921789Sahrens 922789Sahrens case '?': 923789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 924789Sahrens optopt); 925*2082Seschrock 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")); 935*2082Seschrock 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) 946*2082Seschrock 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")); 956*2082Seschrock 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")); 965*2082Seschrock 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 { 1019*2082Seschrock 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': 1028*2082Seschrock recurse = B_TRUE; 1029789Sahrens break; 1030789Sahrens case '?': 1031789Sahrens default: 1032789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1033789Sahrens optopt); 1034*2082Seschrock 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")); 1044*2082Seschrock usage(B_FALSE); 1045789Sahrens } 1046789Sahrens if (argc < 2) { 1047789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 1048*2082Seschrock 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); 1061*2082Seschrock 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 { 1094*2082Seschrock boolean_t cb_first; 1095*2082Seschrock 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, 1140*2082Seschrock 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); 1170*2082Seschrock 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; 1183*2082Seschrock boolean_t recurse = B_FALSE; 1184*2082Seschrock 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': 1204*2082Seschrock recurse = B_TRUE; 1205789Sahrens break; 1206789Sahrens case 'H': 1207*2082Seschrock 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); 1227*2082Seschrock usage(B_FALSE); 1228789Sahrens } 1229789Sahrens } 1230789Sahrens break; 1231789Sahrens case ':': 1232789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1233789Sahrens "'%c' option\n"), optopt); 1234*2082Seschrock usage(B_FALSE); 1235789Sahrens break; 1236789Sahrens case '?': 1237789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1238789Sahrens optopt); 1239*2082Seschrock 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")); 1269*2082Seschrock usage(B_FALSE); 1270866Seschrock } 1271866Seschrock 1272866Seschrock cb.cb_fieldcount += alloffset; 1273789Sahrens cb.cb_scripted = scripted; 1274*2082Seschrock cb.cb_first = B_TRUE; 1275789Sahrens 1276789Sahrens ret = zfs_for_each(argc, argv, recurse, types, list_callback, &cb); 1277789Sahrens 1278*2082Seschrock 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; 1294*2082Seschrock 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]); 1300*2082Seschrock 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")); 1307*2082Seschrock usage(B_FALSE); 1308789Sahrens } 1309789Sahrens if (argc < 3) { 1310789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 1311789Sahrens "argument\n")); 1312*2082Seschrock usage(B_FALSE); 1313789Sahrens } 1314789Sahrens if (argc > 3) { 1315789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 1316*2082Seschrock usage(B_FALSE); 1317789Sahrens } 1318789Sahrens 1319*2082Seschrock if ((zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_ANY)) == NULL) 1320789Sahrens return (1); 1321789Sahrens 1322*2082Seschrock ret = (zfs_rename(zhp, argv[2]) != 0); 1323*2082Seschrock 1324*2082Seschrock zfs_close(zhp); 1325*2082Seschrock return (ret); 1326*2082Seschrock } 1327*2082Seschrock 1328*2082Seschrock /* 1329*2082Seschrock * zfs promote <fs> 1330*2082Seschrock * 1331*2082Seschrock * Promotes the given clone fs to be the parent 1332*2082Seschrock */ 1333*2082Seschrock /* ARGSUSED */ 1334*2082Seschrock static int 1335*2082Seschrock zfs_do_promote(int argc, char **argv) 1336*2082Seschrock { 1337*2082Seschrock zfs_handle_t *zhp; 1338*2082Seschrock int ret; 1339*2082Seschrock 1340*2082Seschrock /* check options */ 1341*2082Seschrock if (argc > 1 && argv[1][0] == '-') { 1342*2082Seschrock (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1343*2082Seschrock argv[1][1]); 1344*2082Seschrock usage(B_FALSE); 1345*2082Seschrock } 1346*2082Seschrock 1347*2082Seschrock /* check number of arguments */ 1348*2082Seschrock if (argc < 2) { 1349*2082Seschrock (void) fprintf(stderr, gettext("missing clone filesystem" 1350*2082Seschrock "argument\n")); 1351*2082Seschrock usage(B_FALSE); 1352*2082Seschrock } 1353*2082Seschrock if (argc > 2) { 1354*2082Seschrock (void) fprintf(stderr, gettext("too many arguments\n")); 1355*2082Seschrock usage(B_FALSE); 1356*2082Seschrock } 1357*2082Seschrock 1358*2082Seschrock zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 1359*2082Seschrock if (zhp == NULL) 1360*2082Seschrock return (1); 1361*2082Seschrock 1362*2082Seschrock ret = (zfs_promote(zhp) != 0); 1363*2082Seschrock 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; 1381*2082Seschrock boolean_t cb_first; 1382789Sahrens int cb_doclones; 1383789Sahrens char *cb_target; 1384789Sahrens int cb_error; 1385*2082Seschrock boolean_t cb_recurse; 1386*2082Seschrock 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 1400*2082Seschrock if (cbp->cb_doclones) { 1401*2082Seschrock zfs_close(zhp); 1402789Sahrens return (0); 1403*2082Seschrock } 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) { 1424*2082Seschrock cbp->cb_dependent = B_TRUE; 1425789Sahrens (void) zfs_iter_dependents(zhp, rollback_check, 1426789Sahrens cbp); 1427*2082Seschrock 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); 1479*2082Seschrock 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")); 1489*2082Seschrock usage(B_FALSE); 1490789Sahrens } 1491789Sahrens if (argc > 1) { 1492789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 1493*2082Seschrock usage(B_FALSE); 1494789Sahrens } 1495789Sahrens 1496789Sahrens /* open the snapshot */ 1497*2082Seschrock 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'; 1504*2082Seschrock 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); 1515*2082Seschrock 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 1639789Sahrens if (zfs_prop_set(zhp, cbp->cb_prop, cbp->cb_value) != 0) 1640789Sahrens return (1); 1641789Sahrens 1642789Sahrens ret = 0; 1643789Sahrens error: 1644789Sahrens return (ret); 1645789Sahrens } 1646789Sahrens 1647789Sahrens static int 1648789Sahrens zfs_do_set(int argc, char **argv) 1649789Sahrens { 1650789Sahrens set_cbdata_t cb; 1651789Sahrens 1652789Sahrens /* check for options */ 1653789Sahrens if (argc > 1 && argv[1][0] == '-') { 1654789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1655789Sahrens argv[1][1]); 1656*2082Seschrock usage(B_FALSE); 1657789Sahrens } 1658789Sahrens 1659789Sahrens /* check number of arguments */ 1660789Sahrens if (argc < 2) { 1661789Sahrens (void) fprintf(stderr, gettext("missing property=value " 1662789Sahrens "argument\n")); 1663*2082Seschrock usage(B_FALSE); 1664789Sahrens } 1665789Sahrens if (argc < 3) { 1666789Sahrens (void) fprintf(stderr, gettext("missing dataset name\n")); 1667*2082Seschrock usage(B_FALSE); 1668789Sahrens } 1669789Sahrens 1670789Sahrens /* validate property=value argument */ 1671789Sahrens cb.cb_propname = argv[1]; 1672789Sahrens if ((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) { 1673789Sahrens (void) fprintf(stderr, gettext("missing value in " 1674789Sahrens "property=value argument\n")); 1675*2082Seschrock usage(B_FALSE); 1676789Sahrens } 1677789Sahrens 1678789Sahrens *cb.cb_value = '\0'; 1679789Sahrens cb.cb_value++; 1680789Sahrens 1681789Sahrens if (*cb.cb_propname == '\0') { 1682789Sahrens (void) fprintf(stderr, 1683789Sahrens gettext("missing property in property=value argument\n")); 1684*2082Seschrock usage(B_FALSE); 1685789Sahrens } 1686789Sahrens if (*cb.cb_value == '\0') { 1687789Sahrens (void) fprintf(stderr, 1688789Sahrens gettext("missing value in property=value argument\n")); 1689*2082Seschrock usage(B_FALSE); 1690789Sahrens } 1691789Sahrens 1692789Sahrens /* get the property type */ 1693789Sahrens if ((cb.cb_prop = zfs_name_to_prop(cb.cb_propname)) == 1694789Sahrens ZFS_PROP_INVAL) { 1695789Sahrens (void) fprintf(stderr, 1696789Sahrens gettext("invalid property '%s'\n"), cb.cb_propname); 1697*2082Seschrock usage(B_FALSE); 1698789Sahrens } 1699789Sahrens 1700789Sahrens /* 1701789Sahrens * Validate that the value is appropriate for this property. We do this 1702789Sahrens * once now so we don't generate multiple errors each time we try to 1703789Sahrens * apply it to a dataset. 1704789Sahrens */ 1705*2082Seschrock if (zfs_prop_validate(g_zfs, cb.cb_prop, cb.cb_value, NULL) != 0) 1706789Sahrens return (1); 1707789Sahrens 1708*2082Seschrock return (zfs_for_each(argc - 2, argv + 2, B_FALSE, 1709789Sahrens ZFS_TYPE_ANY, set_callback, &cb)); 1710789Sahrens } 1711789Sahrens 1712789Sahrens /* 1713789Sahrens * zfs snapshot <fs@snap> 1714789Sahrens * 1715789Sahrens * Creates a snapshot with the given name. While functionally equivalent to 1716789Sahrens * 'zfs create', it is a separate command to diffferentiate intent. 1717789Sahrens */ 1718789Sahrens static int 1719789Sahrens zfs_do_snapshot(int argc, char **argv) 1720789Sahrens { 1721789Sahrens /* check options */ 1722789Sahrens if (argc > 1 && argv[1][0] == '-') { 1723789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1724789Sahrens argv[1][1]); 1725*2082Seschrock usage(B_FALSE); 1726789Sahrens } 1727789Sahrens 1728789Sahrens /* check number of arguments */ 1729789Sahrens if (argc < 2) { 1730789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 1731*2082Seschrock usage(B_FALSE); 1732789Sahrens } 1733789Sahrens if (argc > 2) { 1734789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 1735*2082Seschrock usage(B_FALSE); 1736789Sahrens } 1737789Sahrens 1738*2082Seschrock return (zfs_snapshot(g_zfs, argv[1]) != 0); 1739789Sahrens } 1740789Sahrens 1741789Sahrens /* 17421749Sahrens * zfs send [-i <fs@snap>] <fs@snap> 1743789Sahrens * 1744789Sahrens * Send a backup stream to stdout. 1745789Sahrens */ 1746789Sahrens static int 17471749Sahrens zfs_do_send(int argc, char **argv) 1748789Sahrens { 1749789Sahrens char *fromname = NULL; 1750789Sahrens zfs_handle_t *zhp_from = NULL, *zhp_to; 1751789Sahrens int c, err; 1752789Sahrens 1753789Sahrens /* check options */ 1754789Sahrens while ((c = getopt(argc, argv, ":i:")) != -1) { 1755789Sahrens switch (c) { 1756789Sahrens case 'i': 1757789Sahrens fromname = optarg; 1758789Sahrens break; 1759789Sahrens case ':': 1760789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1761789Sahrens "'%c' option\n"), optopt); 1762*2082Seschrock usage(B_FALSE); 1763789Sahrens break; 1764789Sahrens case '?': 1765789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1766789Sahrens optopt); 1767*2082Seschrock usage(B_FALSE); 1768789Sahrens } 1769789Sahrens } 1770789Sahrens 1771789Sahrens argc -= optind; 1772789Sahrens argv += optind; 1773789Sahrens 1774789Sahrens /* check number of arguments */ 1775789Sahrens if (argc < 1) { 1776789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 1777*2082Seschrock usage(B_FALSE); 1778789Sahrens } 1779789Sahrens if (argc > 1) { 1780789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 1781*2082Seschrock usage(B_FALSE); 1782789Sahrens } 1783789Sahrens 1784789Sahrens if (isatty(STDOUT_FILENO)) { 1785789Sahrens (void) fprintf(stderr, 17861749Sahrens gettext("Error: Stream can not be written " 1787789Sahrens "to a terminal.\n" 1788789Sahrens "You must redirect standard output.\n")); 1789789Sahrens return (1); 1790789Sahrens } 1791789Sahrens 1792789Sahrens if (fromname) { 1793*2082Seschrock if ((zhp_from = zfs_open(g_zfs, fromname, 1794*2082Seschrock ZFS_TYPE_SNAPSHOT)) == NULL) 1795789Sahrens return (1); 1796789Sahrens } 1797*2082Seschrock if ((zhp_to = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1798789Sahrens return (1); 1799789Sahrens 18001749Sahrens err = zfs_send(zhp_to, zhp_from); 1801789Sahrens 1802789Sahrens if (zhp_from) 1803789Sahrens zfs_close(zhp_from); 1804789Sahrens zfs_close(zhp_to); 1805789Sahrens 1806789Sahrens return (err != 0); 1807789Sahrens } 1808789Sahrens 1809789Sahrens /* 18101749Sahrens * zfs receive <fs@snap> 1811789Sahrens * 1812789Sahrens * Restore a backup stream from stdin. 1813789Sahrens */ 1814789Sahrens static int 18151749Sahrens zfs_do_receive(int argc, char **argv) 1816789Sahrens { 1817789Sahrens int c, err; 1818*2082Seschrock boolean_t isprefix = B_FALSE; 1819*2082Seschrock boolean_t dryrun = B_FALSE; 1820*2082Seschrock boolean_t verbose = B_FALSE; 1821789Sahrens 1822789Sahrens /* check options */ 1823789Sahrens while ((c = getopt(argc, argv, ":dnv")) != -1) { 1824789Sahrens switch (c) { 1825789Sahrens case 'd': 1826*2082Seschrock isprefix = B_TRUE; 1827789Sahrens break; 1828789Sahrens case 'n': 1829*2082Seschrock dryrun = B_TRUE; 1830789Sahrens break; 1831789Sahrens case 'v': 1832*2082Seschrock verbose = B_TRUE; 1833789Sahrens break; 1834789Sahrens case ':': 1835789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1836789Sahrens "'%c' option\n"), optopt); 1837*2082Seschrock usage(B_FALSE); 1838789Sahrens break; 1839789Sahrens case '?': 1840789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1841789Sahrens optopt); 1842*2082Seschrock usage(B_FALSE); 1843789Sahrens } 1844789Sahrens } 1845789Sahrens 1846789Sahrens argc -= optind; 1847789Sahrens argv += optind; 1848789Sahrens 1849789Sahrens /* check number of arguments */ 1850789Sahrens if (argc < 1) { 1851789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 1852*2082Seschrock usage(B_FALSE); 1853789Sahrens } 1854789Sahrens if (argc > 1) { 1855789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 1856*2082Seschrock usage(B_FALSE); 1857789Sahrens } 1858789Sahrens 1859789Sahrens if (isatty(STDIN_FILENO)) { 1860789Sahrens (void) fprintf(stderr, 1861789Sahrens gettext("Error: Backup stream can not be read " 1862789Sahrens "from a terminal.\n" 1863789Sahrens "You must redirect standard input.\n")); 1864789Sahrens return (1); 1865789Sahrens } 1866789Sahrens 1867*2082Seschrock err = zfs_receive(g_zfs, argv[0], isprefix, verbose, dryrun); 1868789Sahrens return (err != 0); 1869789Sahrens } 1870789Sahrens 18711356Seschrock typedef struct get_all_cbdata { 18721356Seschrock zfs_handle_t **cb_handles; 18731356Seschrock size_t cb_alloc; 18741356Seschrock size_t cb_used; 18751356Seschrock } get_all_cbdata_t; 18761356Seschrock 18771356Seschrock static int 18781356Seschrock get_one_filesystem(zfs_handle_t *zhp, void *data) 18791356Seschrock { 18801356Seschrock get_all_cbdata_t *cbp = data; 18811356Seschrock 18821356Seschrock /* 18831356Seschrock * Skip any zvols 18841356Seschrock */ 18851356Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 18861356Seschrock zfs_close(zhp); 18871356Seschrock return (0); 18881356Seschrock } 18891356Seschrock 18901356Seschrock if (cbp->cb_alloc == cbp->cb_used) { 18911356Seschrock zfs_handle_t **handles; 18921356Seschrock 18931356Seschrock if (cbp->cb_alloc == 0) 18941356Seschrock cbp->cb_alloc = 64; 18951356Seschrock else 18961356Seschrock cbp->cb_alloc *= 2; 18971356Seschrock 18981356Seschrock handles = safe_malloc(cbp->cb_alloc * sizeof (void *)); 18991356Seschrock 19001356Seschrock if (cbp->cb_handles) { 19011356Seschrock bcopy(cbp->cb_handles, handles, 19021356Seschrock cbp->cb_used * sizeof (void *)); 19031356Seschrock free(cbp->cb_handles); 19041356Seschrock } 19051356Seschrock 19061356Seschrock cbp->cb_handles = handles; 19071356Seschrock } 19081356Seschrock 19091356Seschrock cbp->cb_handles[cbp->cb_used++] = zhp; 19101356Seschrock 19111356Seschrock return (zfs_iter_filesystems(zhp, get_one_filesystem, data)); 19121356Seschrock } 19131356Seschrock 19141356Seschrock static void 19151356Seschrock get_all_filesystems(zfs_handle_t ***fslist, size_t *count) 19161356Seschrock { 19171356Seschrock get_all_cbdata_t cb = { 0 }; 19181356Seschrock 1919*2082Seschrock (void) zfs_iter_root(g_zfs, get_one_filesystem, &cb); 19201356Seschrock 19211356Seschrock *fslist = cb.cb_handles; 19221356Seschrock *count = cb.cb_used; 19231356Seschrock } 19241356Seschrock 19251356Seschrock static int 19261356Seschrock mountpoint_compare(const void *a, const void *b) 19271356Seschrock { 19281356Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 19291356Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 19301356Seschrock char mounta[MAXPATHLEN]; 19311356Seschrock char mountb[MAXPATHLEN]; 19321356Seschrock 19331356Seschrock verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 1934*2082Seschrock sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 19351356Seschrock verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 1936*2082Seschrock sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 19371356Seschrock 19381356Seschrock return (strcmp(mounta, mountb)); 19391356Seschrock } 1940789Sahrens 1941789Sahrens /* 1942789Sahrens * Generic callback for sharing or mounting filesystems. Because the code is so 1943789Sahrens * similar, we have a common function with an extra parameter to determine which 1944789Sahrens * mode we are using. 1945789Sahrens */ 1946789Sahrens #define OP_SHARE 0x1 1947789Sahrens #define OP_MOUNT 0x2 1948789Sahrens 1949789Sahrens typedef struct share_mount_cbdata { 1950789Sahrens int cb_type; 1951789Sahrens int cb_explicit; 1952789Sahrens int cb_flags; 1953789Sahrens const char *cb_options; 1954789Sahrens } share_mount_cbdata_t; 1955789Sahrens 1956789Sahrens /* 1957789Sahrens * Share or mount the filesystem. 1958789Sahrens */ 1959789Sahrens static int 1960789Sahrens share_mount_callback(zfs_handle_t *zhp, void *data) 1961789Sahrens { 1962789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 1963789Sahrens char shareopts[ZFS_MAXPROPLEN]; 1964789Sahrens share_mount_cbdata_t *cbp = data; 1965789Sahrens const char *cmdname = cbp->cb_type == OP_SHARE ? "share" : "mount"; 1966789Sahrens struct mnttab mnt; 1967789Sahrens uint64_t zoned; 1968789Sahrens 1969789Sahrens if (cbp->cb_options == NULL) 1970789Sahrens mnt.mnt_mntopts = ""; 1971789Sahrens else 1972789Sahrens mnt.mnt_mntopts = (char *)cbp->cb_options; 1973789Sahrens 1974789Sahrens /* 1975789Sahrens * Check to make sure we can mount/share this dataset. If we are in the 1976789Sahrens * global zone and the filesystem is exported to a local zone, or if we 1977789Sahrens * are in a local zone and the filesystem is not exported, then it is an 1978789Sahrens * error. 1979789Sahrens */ 1980789Sahrens zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 1981789Sahrens 1982789Sahrens if (zoned && getzoneid() == GLOBAL_ZONEID) { 1983789Sahrens if (!cbp->cb_explicit) 1984789Sahrens return (0); 1985789Sahrens 1986789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': dataset is " 1987789Sahrens "exported to a local zone\n"), cmdname, zfs_get_name(zhp)); 1988789Sahrens return (1); 1989789Sahrens 1990789Sahrens } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 1991789Sahrens if (!cbp->cb_explicit) 1992789Sahrens return (0); 1993789Sahrens 1994789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': permission " 1995789Sahrens "denied\n"), cmdname, zfs_get_name(zhp)); 1996789Sahrens return (1); 1997789Sahrens } 1998789Sahrens 1999789Sahrens /* 2000789Sahrens * Inore any filesystems which don't apply to us. This includes those 2001789Sahrens * with a legacy mountpoint, or those with legacy share options. 2002789Sahrens */ 2003789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 2004*2082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); 2005789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 2006*2082Seschrock sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 2007789Sahrens 2008789Sahrens if (cbp->cb_type == OP_SHARE) { 2009789Sahrens if (strcmp(shareopts, "off") == 0) { 2010789Sahrens if (!cbp->cb_explicit) 2011789Sahrens return (0); 2012789Sahrens 2013789Sahrens (void) fprintf(stderr, gettext("cannot share '%s': " 2014789Sahrens "legacy share\n"), zfs_get_name(zhp)); 2015789Sahrens (void) fprintf(stderr, gettext("use share(1M) to " 2016789Sahrens "share this filesystem\n")); 2017789Sahrens return (1); 2018789Sahrens } 2019789Sahrens } 2020789Sahrens 2021789Sahrens /* 2022789Sahrens * We cannot share or mount legacy filesystems. If the shareopts is 2023789Sahrens * non-legacy but the mountpoint is legacy, we treat it as a legacy 2024789Sahrens * share. 2025789Sahrens */ 2026789Sahrens if (strcmp(mountpoint, "legacy") == 0) { 2027789Sahrens if (!cbp->cb_explicit) 2028789Sahrens return (0); 2029789Sahrens 2030789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': " 2031789Sahrens "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 2032789Sahrens (void) fprintf(stderr, gettext("use %s to " 2033789Sahrens "%s this filesystem\n"), cbp->cb_type == OP_SHARE ? 2034789Sahrens "share(1M)" : "mount(1M)", cmdname); 2035789Sahrens return (1); 2036789Sahrens } 2037789Sahrens 2038789Sahrens if (strcmp(mountpoint, "none") == 0) { 2039789Sahrens if (!cbp->cb_explicit) 2040789Sahrens return (0); 2041789Sahrens 2042789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': no " 2043789Sahrens "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 2044789Sahrens return (1); 2045789Sahrens } 2046789Sahrens 2047789Sahrens /* 2048789Sahrens * At this point, we have verified that the mountpoint and/or shareopts 2049789Sahrens * are appropriate for auto management. Determine if the filesystem is 2050789Sahrens * currently mounted or shared, and abort if this is an explicit 2051789Sahrens * request. 2052789Sahrens */ 2053789Sahrens switch (cbp->cb_type) { 2054789Sahrens case OP_SHARE: 2055789Sahrens if (zfs_is_shared(zhp, NULL)) { 2056789Sahrens if (cbp->cb_explicit) { 2057789Sahrens (void) fprintf(stderr, gettext("cannot share " 2058789Sahrens "'%s': filesystem already shared\n"), 2059789Sahrens zfs_get_name(zhp)); 2060789Sahrens return (1); 2061789Sahrens } else { 2062789Sahrens return (0); 2063789Sahrens } 2064789Sahrens } 2065789Sahrens break; 2066789Sahrens 2067789Sahrens case OP_MOUNT: 2068789Sahrens if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 2069789Sahrens zfs_is_mounted(zhp, NULL)) { 2070789Sahrens if (cbp->cb_explicit) { 2071789Sahrens (void) fprintf(stderr, gettext("cannot mount " 2072789Sahrens "'%s': filesystem already mounted\n"), 2073789Sahrens zfs_get_name(zhp)); 2074789Sahrens return (1); 2075789Sahrens } else { 2076789Sahrens return (0); 2077789Sahrens } 2078789Sahrens } 2079789Sahrens break; 2080789Sahrens } 2081789Sahrens 2082789Sahrens /* 2083789Sahrens * Mount and optionally share the filesystem. 2084789Sahrens */ 2085789Sahrens switch (cbp->cb_type) { 2086789Sahrens case OP_SHARE: 2087789Sahrens { 2088789Sahrens if (!zfs_is_mounted(zhp, NULL) && 2089789Sahrens zfs_mount(zhp, NULL, 0) != 0) 2090789Sahrens return (1); 2091789Sahrens 2092789Sahrens if (zfs_share(zhp) != 0) 2093789Sahrens return (1); 2094789Sahrens } 2095789Sahrens break; 2096789Sahrens 2097789Sahrens case OP_MOUNT: 2098789Sahrens if (zfs_mount(zhp, cbp->cb_options, cbp->cb_flags) != 0) 2099789Sahrens return (1); 2100789Sahrens break; 2101789Sahrens } 2102789Sahrens 2103789Sahrens return (0); 2104789Sahrens } 2105789Sahrens 2106789Sahrens static int 2107789Sahrens share_or_mount(int type, int argc, char **argv) 2108789Sahrens { 2109789Sahrens int do_all = 0; 2110789Sahrens int c, ret; 2111789Sahrens share_mount_cbdata_t cb = { 0 }; 2112789Sahrens 2113789Sahrens cb.cb_type = type; 2114789Sahrens 2115789Sahrens /* check options */ 2116789Sahrens while ((c = getopt(argc, argv, type == OP_MOUNT ? ":ao:O" : "a")) 2117789Sahrens != -1) { 2118789Sahrens switch (c) { 2119789Sahrens case 'a': 2120789Sahrens do_all = 1; 2121789Sahrens break; 2122789Sahrens case 'o': 2123789Sahrens cb.cb_options = optarg; 2124789Sahrens break; 2125789Sahrens case 'O': 2126789Sahrens cb.cb_flags |= MS_OVERLAY; 2127789Sahrens break; 2128789Sahrens case ':': 2129789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2130789Sahrens "'%c' option\n"), optopt); 2131*2082Seschrock usage(B_FALSE); 2132789Sahrens break; 2133789Sahrens case '?': 2134789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2135789Sahrens optopt); 2136*2082Seschrock usage(B_FALSE); 2137789Sahrens } 2138789Sahrens } 2139789Sahrens 2140789Sahrens argc -= optind; 2141789Sahrens argv += optind; 2142789Sahrens 2143789Sahrens /* check number of arguments */ 2144789Sahrens if (do_all) { 21451356Seschrock zfs_handle_t **fslist = NULL; 21461356Seschrock size_t i, count = 0; 21471356Seschrock 2148789Sahrens if (argc != 0) { 2149789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2150*2082Seschrock usage(B_FALSE); 2151789Sahrens } 2152789Sahrens 21531356Seschrock get_all_filesystems(&fslist, &count); 21541356Seschrock 21551356Seschrock if (count == 0) 21561356Seschrock return (0); 21571356Seschrock 21581356Seschrock qsort(fslist, count, sizeof (void *), mountpoint_compare); 21591356Seschrock 21601356Seschrock for (i = 0; i < count; i++) { 21611356Seschrock if ((ret = share_mount_callback(fslist[i], &cb)) != 0) 21621356Seschrock break; 21631356Seschrock } 21641356Seschrock 21651356Seschrock for (i = 0; i < count; i++) 21661356Seschrock zfs_close(fslist[i]); 21671356Seschrock 21681356Seschrock free(fslist); 2169789Sahrens } else if (argc == 0) { 2170789Sahrens struct mnttab entry; 2171789Sahrens 2172789Sahrens if (type == OP_SHARE) { 2173789Sahrens (void) fprintf(stderr, gettext("missing filesystem " 2174789Sahrens "argument\n")); 2175*2082Seschrock usage(B_FALSE); 2176789Sahrens } 2177789Sahrens 2178789Sahrens /* 2179789Sahrens * When mount is given no arguments, go through /etc/mnttab and 2180789Sahrens * display any active ZFS mounts. We hide any snapshots, since 2181789Sahrens * they are controlled automatically. 2182789Sahrens */ 2183789Sahrens rewind(mnttab_file); 2184789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2185789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 2186789Sahrens strchr(entry.mnt_special, '@') != NULL) 2187789Sahrens continue; 2188789Sahrens 2189789Sahrens (void) printf("%-30s %s\n", entry.mnt_special, 2190789Sahrens entry.mnt_mountp); 2191789Sahrens } 2192789Sahrens 2193789Sahrens ret = 0; 2194789Sahrens } else { 2195789Sahrens zfs_handle_t *zhp; 2196789Sahrens 2197789Sahrens if (argc > 1) { 2198789Sahrens (void) fprintf(stderr, 2199789Sahrens gettext("too many arguments\n")); 2200*2082Seschrock usage(B_FALSE); 2201789Sahrens } 2202789Sahrens 2203*2082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], 2204*2082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2205789Sahrens ret = 1; 2206789Sahrens else { 2207*2082Seschrock cb.cb_explicit = B_TRUE; 2208789Sahrens ret = share_mount_callback(zhp, &cb); 2209789Sahrens zfs_close(zhp); 2210789Sahrens } 2211789Sahrens } 2212789Sahrens 2213789Sahrens return (ret); 2214789Sahrens } 2215789Sahrens 2216789Sahrens /* 2217789Sahrens * zfs mount -a 2218789Sahrens * zfs mount filesystem 2219789Sahrens * 2220789Sahrens * Mount all filesystems, or mount the given filesystem. 2221789Sahrens */ 2222789Sahrens static int 2223789Sahrens zfs_do_mount(int argc, char **argv) 2224789Sahrens { 2225789Sahrens return (share_or_mount(OP_MOUNT, argc, argv)); 2226789Sahrens } 2227789Sahrens 2228789Sahrens /* 2229789Sahrens * zfs share -a 2230789Sahrens * zfs share filesystem 2231789Sahrens * 2232789Sahrens * Share all filesystems, or share the given filesystem. 2233789Sahrens */ 2234789Sahrens static int 2235789Sahrens zfs_do_share(int argc, char **argv) 2236789Sahrens { 2237789Sahrens return (share_or_mount(OP_SHARE, argc, argv)); 2238789Sahrens } 2239789Sahrens 2240789Sahrens typedef struct unshare_unmount_node { 2241789Sahrens zfs_handle_t *un_zhp; 2242789Sahrens char *un_mountp; 2243789Sahrens uu_avl_node_t un_avlnode; 2244789Sahrens } unshare_unmount_node_t; 2245789Sahrens 2246789Sahrens /* ARGSUSED */ 2247789Sahrens static int 2248789Sahrens unshare_unmount_compare(const void *larg, const void *rarg, void *unused) 2249789Sahrens { 2250789Sahrens const unshare_unmount_node_t *l = larg; 2251789Sahrens const unshare_unmount_node_t *r = rarg; 2252789Sahrens 2253789Sahrens return (strcmp(l->un_mountp, r->un_mountp)); 2254789Sahrens } 2255789Sahrens 2256789Sahrens /* 2257789Sahrens * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 2258789Sahrens * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 2259789Sahrens * and unmount it appropriately. 2260789Sahrens */ 2261789Sahrens static int 2262*2082Seschrock unshare_unmount_path(int type, char *path, int flags, boolean_t is_manual) 2263789Sahrens { 2264789Sahrens zfs_handle_t *zhp; 2265789Sahrens int ret; 2266789Sahrens struct stat64 statbuf; 2267789Sahrens struct extmnttab entry; 2268789Sahrens const char *cmdname = (type == OP_SHARE) ? "unshare" : "unmount"; 2269789Sahrens char property[ZFS_MAXPROPLEN]; 2270789Sahrens 2271789Sahrens /* 2272789Sahrens * Search for the path in /etc/mnttab. Rather than looking for the 2273789Sahrens * specific path, which can be fooled by non-standard paths (i.e. ".." 2274789Sahrens * or "//"), we stat() the path and search for the corresponding 2275789Sahrens * (major,minor) device pair. 2276789Sahrens */ 2277789Sahrens if (stat64(path, &statbuf) != 0) { 2278789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 2279789Sahrens cmdname, path, strerror(errno)); 2280789Sahrens return (1); 2281789Sahrens } 2282789Sahrens 2283789Sahrens /* 2284789Sahrens * Search for the given (major,minor) pair in the mount table. 2285789Sahrens */ 2286789Sahrens rewind(mnttab_file); 2287789Sahrens while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 2288789Sahrens if (entry.mnt_major == major(statbuf.st_dev) && 2289789Sahrens entry.mnt_minor == minor(statbuf.st_dev)) 2290789Sahrens break; 2291789Sahrens } 2292789Sahrens if (ret != 0) { 2293789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not " 2294789Sahrens "currently mounted\n"), cmdname, path); 2295789Sahrens return (1); 2296789Sahrens } 2297789Sahrens 2298789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 2299789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 2300789Sahrens "filesystem\n"), cmdname, path); 2301789Sahrens return (1); 2302789Sahrens } 2303789Sahrens 2304*2082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 2305*2082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2306789Sahrens return (1); 2307789Sahrens 2308789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2309789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 2310*2082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2311789Sahrens 2312789Sahrens if (type == OP_SHARE) { 2313789Sahrens if (strcmp(property, "off") == 0) { 2314789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2315789Sahrens "'%s': legacy share\n"), path); 2316789Sahrens (void) fprintf(stderr, gettext("use " 2317789Sahrens "unshare(1M) to unshare this filesystem\n")); 2318789Sahrens ret = 1; 2319789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2320789Sahrens (void) fprintf(stderr, gettext("cannot unshare '%s': " 2321789Sahrens "not currently shared\n"), path); 2322789Sahrens ret = 1; 2323789Sahrens } else { 2324789Sahrens ret = zfs_unshareall(zhp); 2325789Sahrens } 2326789Sahrens } else { 23271264Slling if (is_manual) { 23281264Slling ret = zfs_unmount(zhp, NULL, flags); 23291264Slling } else if (strcmp(property, "legacy") == 0) { 23301264Slling (void) fprintf(stderr, gettext("cannot unmount " 23311264Slling "'%s': legacy mountpoint\n"), 23321264Slling zfs_get_name(zhp)); 23331264Slling (void) fprintf(stderr, gettext("use umount(1M) " 23341264Slling "to unmount this filesystem\n")); 23351264Slling ret = 1; 2336789Sahrens } else { 2337789Sahrens ret = zfs_unmountall(zhp, flags); 2338789Sahrens } 2339789Sahrens } 2340789Sahrens 2341789Sahrens zfs_close(zhp); 2342789Sahrens 2343789Sahrens return (ret != 0); 2344789Sahrens } 2345789Sahrens 2346789Sahrens /* 2347789Sahrens * Generic callback for unsharing or unmounting a filesystem. 2348789Sahrens */ 2349789Sahrens static int 2350789Sahrens unshare_unmount(int type, int argc, char **argv) 2351789Sahrens { 2352789Sahrens int do_all = 0; 2353789Sahrens int flags = 0; 2354789Sahrens int ret = 0; 2355789Sahrens int c; 2356789Sahrens zfs_handle_t *zhp; 2357789Sahrens char property[ZFS_MAXPROPLEN]; 2358789Sahrens 2359789Sahrens /* check options */ 2360789Sahrens while ((c = getopt(argc, argv, type == OP_SHARE ? "a" : "af")) != -1) { 2361789Sahrens switch (c) { 2362789Sahrens case 'a': 2363789Sahrens do_all = 1; 2364789Sahrens break; 2365789Sahrens case 'f': 2366789Sahrens flags = MS_FORCE; 2367789Sahrens break; 2368789Sahrens case '?': 2369789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2370789Sahrens optopt); 2371*2082Seschrock usage(B_FALSE); 2372789Sahrens } 2373789Sahrens } 2374789Sahrens 2375789Sahrens argc -= optind; 2376789Sahrens argv += optind; 2377789Sahrens 2378789Sahrens /* ensure correct number of arguments */ 2379789Sahrens if (do_all) { 2380789Sahrens if (argc != 0) { 2381789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2382*2082Seschrock usage(B_FALSE); 2383789Sahrens } 2384789Sahrens } else if (argc != 1) { 2385789Sahrens if (argc == 0) 2386789Sahrens (void) fprintf(stderr, 2387789Sahrens gettext("missing filesystem argument\n")); 2388789Sahrens else 2389789Sahrens (void) fprintf(stderr, 2390789Sahrens gettext("too many arguments\n")); 2391*2082Seschrock usage(B_FALSE); 2392789Sahrens } 2393789Sahrens 2394789Sahrens if (do_all) { 2395789Sahrens /* 2396789Sahrens * We could make use of zfs_for_each() to walk all datasets in 2397789Sahrens * the system, but this would be very inefficient, especially 2398789Sahrens * since we would have to linearly search /etc/mnttab for each 2399789Sahrens * one. Instead, do one pass through /etc/mnttab looking for 2400789Sahrens * zfs entries and call zfs_unmount() for each one. 2401789Sahrens * 2402789Sahrens * Things get a little tricky if the administrator has created 2403789Sahrens * mountpoints beneath other ZFS filesystems. In this case, we 2404789Sahrens * have to unmount the deepest filesystems first. To accomplish 2405789Sahrens * this, we place all the mountpoints in an AVL tree sorted by 2406789Sahrens * the special type (dataset name), and walk the result in 2407789Sahrens * reverse to make sure to get any snapshots first. 2408789Sahrens */ 2409789Sahrens struct mnttab entry; 2410789Sahrens uu_avl_pool_t *pool; 2411789Sahrens uu_avl_t *tree; 2412789Sahrens unshare_unmount_node_t *node; 2413789Sahrens uu_avl_index_t idx; 2414789Sahrens uu_avl_walk_t *walk; 2415789Sahrens 2416789Sahrens if ((pool = uu_avl_pool_create("unmount_pool", 2417789Sahrens sizeof (unshare_unmount_node_t), 2418789Sahrens offsetof(unshare_unmount_node_t, un_avlnode), 2419789Sahrens unshare_unmount_compare, 2420789Sahrens UU_DEFAULT)) == NULL) { 2421789Sahrens (void) fprintf(stderr, gettext("internal error: " 2422789Sahrens "out of memory\n")); 2423789Sahrens exit(1); 2424789Sahrens } 2425789Sahrens 2426789Sahrens if ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL) { 2427789Sahrens (void) fprintf(stderr, gettext("internal error: " 2428789Sahrens "out of memory\n")); 2429789Sahrens exit(1); 2430789Sahrens } 2431789Sahrens 2432789Sahrens rewind(mnttab_file); 2433789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2434789Sahrens 2435789Sahrens /* ignore non-ZFS entries */ 2436789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 2437789Sahrens continue; 2438789Sahrens 2439789Sahrens /* ignore snapshots */ 2440789Sahrens if (strchr(entry.mnt_special, '@') != NULL) 2441789Sahrens continue; 2442789Sahrens 2443*2082Seschrock if ((zhp = zfs_open(g_zfs, entry.mnt_special, 2444789Sahrens ZFS_TYPE_FILESYSTEM)) == NULL) { 2445789Sahrens ret = 1; 2446789Sahrens continue; 2447789Sahrens } 2448789Sahrens 2449789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2450789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 2451789Sahrens property, sizeof (property), NULL, NULL, 2452*2082Seschrock 0, B_FALSE) == 0); 2453789Sahrens 2454789Sahrens /* Ignore legacy mounts and shares */ 2455789Sahrens if ((type == OP_SHARE && 2456789Sahrens strcmp(property, "off") == 0) || 2457789Sahrens (type == OP_MOUNT && 2458789Sahrens strcmp(property, "legacy") == 0)) { 2459789Sahrens zfs_close(zhp); 2460789Sahrens continue; 2461789Sahrens } 2462789Sahrens 2463789Sahrens node = safe_malloc(sizeof (unshare_unmount_node_t)); 2464789Sahrens node->un_zhp = zhp; 2465789Sahrens 2466789Sahrens if ((node->un_mountp = strdup(entry.mnt_mountp)) == 2467789Sahrens NULL) { 2468789Sahrens (void) fprintf(stderr, gettext("internal error:" 2469789Sahrens " out of memory\n")); 2470789Sahrens exit(1); 2471789Sahrens } 2472789Sahrens 2473789Sahrens uu_avl_node_init(node, &node->un_avlnode, pool); 2474789Sahrens 2475789Sahrens if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 2476789Sahrens uu_avl_insert(tree, node, idx); 2477789Sahrens } else { 2478789Sahrens zfs_close(node->un_zhp); 2479789Sahrens free(node->un_mountp); 2480789Sahrens free(node); 2481789Sahrens } 2482789Sahrens } 2483789Sahrens 2484789Sahrens /* 2485789Sahrens * Walk the AVL tree in reverse, unmounting each filesystem and 2486789Sahrens * removing it from the AVL tree in the process. 2487789Sahrens */ 2488789Sahrens if ((walk = uu_avl_walk_start(tree, 2489789Sahrens UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) { 2490789Sahrens (void) fprintf(stderr, 2491789Sahrens gettext("internal error: out of memory")); 2492789Sahrens exit(1); 2493789Sahrens } 2494789Sahrens 2495789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 2496789Sahrens uu_avl_remove(tree, node); 2497789Sahrens 2498789Sahrens switch (type) { 2499789Sahrens case OP_SHARE: 2500789Sahrens if (zfs_unshare(node->un_zhp, 2501789Sahrens node->un_mountp) != 0) 2502789Sahrens ret = 1; 2503789Sahrens break; 2504789Sahrens 2505789Sahrens case OP_MOUNT: 2506789Sahrens if (zfs_unmount(node->un_zhp, 2507789Sahrens node->un_mountp, flags) != 0) 2508789Sahrens ret = 1; 2509789Sahrens break; 2510789Sahrens } 2511789Sahrens 2512789Sahrens zfs_close(node->un_zhp); 2513789Sahrens free(node->un_mountp); 2514789Sahrens free(node); 2515789Sahrens } 2516789Sahrens 2517789Sahrens uu_avl_walk_end(walk); 2518789Sahrens uu_avl_destroy(tree); 2519789Sahrens uu_avl_pool_destroy(pool); 2520789Sahrens } else { 2521789Sahrens /* 2522789Sahrens * We have an argument, but it may be a full path or a ZFS 2523789Sahrens * filesystem. Pass full paths off to unmount_path() (shared by 2524789Sahrens * manual_unmount), otherwise open the filesystem and pass to 2525789Sahrens * zfs_unmount(). 2526789Sahrens */ 2527789Sahrens if (argv[0][0] == '/') 2528789Sahrens return (unshare_unmount_path(type, argv[0], 2529*2082Seschrock flags, B_FALSE)); 2530*2082Seschrock 2531*2082Seschrock if ((zhp = zfs_open(g_zfs, argv[0], 2532*2082Seschrock ZFS_TYPE_FILESYSTEM)) == NULL) 2533789Sahrens return (1); 2534789Sahrens 2535789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2536789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 2537*2082Seschrock sizeof (property), NULL, NULL, 0, B_FALSE) == 0); 2538789Sahrens 2539789Sahrens switch (type) { 2540789Sahrens case OP_SHARE: 2541789Sahrens if (strcmp(property, "off") == 0) { 2542789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2543789Sahrens "'%s': legacy share\n"), zfs_get_name(zhp)); 2544789Sahrens (void) fprintf(stderr, gettext("use unshare(1M)" 2545789Sahrens " to unshare this filesystem\n")); 2546789Sahrens ret = 1; 2547789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2548789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2549789Sahrens "'%s': not currently shared\n"), 2550789Sahrens zfs_get_name(zhp)); 2551789Sahrens ret = 1; 2552789Sahrens } else if (zfs_unshareall(zhp) != 0) { 2553789Sahrens ret = 1; 2554789Sahrens } 2555789Sahrens break; 2556789Sahrens 2557789Sahrens case OP_MOUNT: 2558789Sahrens if (strcmp(property, "legacy") == 0) { 2559789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2560789Sahrens "'%s': legacy mountpoint\n"), 2561789Sahrens zfs_get_name(zhp)); 2562789Sahrens (void) fprintf(stderr, gettext("use umount(1M) " 2563789Sahrens "to unmount this filesystem\n")); 2564789Sahrens ret = 1; 2565789Sahrens } else if (!zfs_is_mounted(zhp, NULL)) { 2566789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2567789Sahrens "'%s': not currently mounted\n"), 2568789Sahrens zfs_get_name(zhp)); 2569789Sahrens ret = 1; 2570789Sahrens } else if (zfs_unmountall(zhp, flags) != 0) { 2571789Sahrens ret = 1; 2572789Sahrens } 2573789Sahrens } 2574789Sahrens 2575789Sahrens zfs_close(zhp); 2576789Sahrens } 2577789Sahrens 2578789Sahrens return (ret); 2579789Sahrens } 2580789Sahrens 2581789Sahrens /* 2582789Sahrens * zfs unmount -a 2583789Sahrens * zfs unmount filesystem 2584789Sahrens * 2585789Sahrens * Unmount all filesystems, or a specific ZFS filesystem. 2586789Sahrens */ 2587789Sahrens static int 2588789Sahrens zfs_do_unmount(int argc, char **argv) 2589789Sahrens { 2590789Sahrens return (unshare_unmount(OP_MOUNT, argc, argv)); 2591789Sahrens } 2592789Sahrens 2593789Sahrens /* 2594789Sahrens * zfs unshare -a 2595789Sahrens * zfs unshare filesystem 2596789Sahrens * 2597789Sahrens * Unshare all filesystems, or a specific ZFS filesystem. 2598789Sahrens */ 2599789Sahrens static int 2600789Sahrens zfs_do_unshare(int argc, char **argv) 2601789Sahrens { 2602789Sahrens return (unshare_unmount(OP_SHARE, argc, argv)); 2603789Sahrens } 2604789Sahrens 2605789Sahrens /* 2606789Sahrens * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 2607789Sahrens * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 2608789Sahrens */ 2609789Sahrens static int 2610789Sahrens manual_mount(int argc, char **argv) 2611789Sahrens { 2612789Sahrens zfs_handle_t *zhp; 2613789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2614789Sahrens char mntopts[MNT_LINE_MAX] = { '\0' }; 2615789Sahrens int ret; 2616789Sahrens int c; 2617789Sahrens int flags = 0; 2618789Sahrens char *dataset, *path; 2619789Sahrens 2620789Sahrens /* check options */ 26211544Seschrock while ((c = getopt(argc, argv, ":mo:O")) != -1) { 2622789Sahrens switch (c) { 2623789Sahrens case 'o': 2624789Sahrens (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 2625789Sahrens break; 2626789Sahrens case 'O': 2627789Sahrens flags |= MS_OVERLAY; 2628789Sahrens break; 26291544Seschrock case 'm': 26301544Seschrock flags |= MS_NOMNTTAB; 26311544Seschrock break; 2632789Sahrens case ':': 2633789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2634789Sahrens "'%c' option\n"), optopt); 2635*2082Seschrock usage(B_FALSE); 2636789Sahrens break; 2637789Sahrens case '?': 2638789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2639789Sahrens optopt); 2640789Sahrens (void) fprintf(stderr, gettext("usage: mount [-o opts] " 2641789Sahrens "<path>\n")); 2642789Sahrens return (2); 2643789Sahrens } 2644789Sahrens } 2645789Sahrens 2646789Sahrens argc -= optind; 2647789Sahrens argv += optind; 2648789Sahrens 2649789Sahrens /* check that we only have two arguments */ 2650789Sahrens if (argc != 2) { 2651789Sahrens if (argc == 0) 2652789Sahrens (void) fprintf(stderr, gettext("missing dataset " 2653789Sahrens "argument\n")); 2654789Sahrens else if (argc == 1) 2655789Sahrens (void) fprintf(stderr, 2656789Sahrens gettext("missing mountpoint argument\n")); 2657789Sahrens else 2658789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2659789Sahrens (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 2660789Sahrens return (2); 2661789Sahrens } 2662789Sahrens 2663789Sahrens dataset = argv[0]; 2664789Sahrens path = argv[1]; 2665789Sahrens 2666789Sahrens /* try to open the dataset */ 2667*2082Seschrock if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 2668789Sahrens return (1); 2669789Sahrens 2670789Sahrens (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 2671*2082Seschrock sizeof (mountpoint), NULL, NULL, 0, B_FALSE); 2672789Sahrens 2673789Sahrens /* check for legacy mountpoint and complain appropriately */ 2674789Sahrens ret = 0; 2675789Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 2676789Sahrens if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS, 2677789Sahrens NULL, 0, mntopts, sizeof (mntopts)) != 0) { 2678789Sahrens (void) fprintf(stderr, gettext("mount failed: %s\n"), 2679789Sahrens strerror(errno)); 2680789Sahrens ret = 1; 2681789Sahrens } 2682789Sahrens } else { 2683789Sahrens (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 2684789Sahrens "mounted using 'mount -F zfs'\n"), dataset); 2685789Sahrens (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 2686789Sahrens "instead.\n"), path); 2687789Sahrens (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' " 2688789Sahrens "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n")); 2689789Sahrens (void) fprintf(stderr, gettext("See zfs(1M) for more " 2690789Sahrens "information.\n")); 2691789Sahrens ret = 1; 2692789Sahrens } 2693789Sahrens 2694789Sahrens return (ret); 2695789Sahrens } 2696789Sahrens 2697789Sahrens /* 2698789Sahrens * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 2699789Sahrens * unmounts of non-legacy filesystems, as this is the dominant administrative 2700789Sahrens * interface. 2701789Sahrens */ 2702789Sahrens static int 2703789Sahrens manual_unmount(int argc, char **argv) 2704789Sahrens { 2705789Sahrens int flags = 0; 2706789Sahrens int c; 2707789Sahrens 2708789Sahrens /* check options */ 2709789Sahrens while ((c = getopt(argc, argv, "f")) != -1) { 2710789Sahrens switch (c) { 2711789Sahrens case 'f': 2712789Sahrens flags = MS_FORCE; 2713789Sahrens break; 2714789Sahrens case '?': 2715789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2716789Sahrens optopt); 2717789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] " 2718789Sahrens "<path>\n")); 2719789Sahrens return (2); 2720789Sahrens } 2721789Sahrens } 2722789Sahrens 2723789Sahrens argc -= optind; 2724789Sahrens argv += optind; 2725789Sahrens 2726789Sahrens /* check arguments */ 2727789Sahrens if (argc != 1) { 2728789Sahrens if (argc == 0) 2729789Sahrens (void) fprintf(stderr, gettext("missing path " 2730789Sahrens "argument\n")); 2731789Sahrens else 2732789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2733789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 2734789Sahrens return (2); 2735789Sahrens } 2736789Sahrens 2737*2082Seschrock return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE)); 2738789Sahrens } 2739789Sahrens 2740789Sahrens static int 2741789Sahrens volcheck(zpool_handle_t *zhp, void *data) 2742789Sahrens { 2743789Sahrens int isinit = (int)data; 2744789Sahrens 2745789Sahrens if (isinit) 2746789Sahrens return (zpool_create_zvol_links(zhp)); 2747789Sahrens else 2748789Sahrens return (zpool_remove_zvol_links(zhp)); 2749789Sahrens } 2750789Sahrens 2751789Sahrens /* 2752789Sahrens * Iterate over all pools in the system and either create or destroy /dev/zvol 2753789Sahrens * links, depending on the value of 'isinit'. 2754789Sahrens */ 2755789Sahrens static int 2756*2082Seschrock do_volcheck(boolean_t isinit) 2757789Sahrens { 2758*2082Seschrock return (zpool_iter(g_zfs, volcheck, (void *)isinit) ? 1 : 0); 2759789Sahrens } 2760789Sahrens 2761789Sahrens int 2762789Sahrens main(int argc, char **argv) 2763789Sahrens { 2764789Sahrens int ret; 2765789Sahrens int i; 2766789Sahrens char *progname; 2767789Sahrens char *cmdname; 2768789Sahrens 2769789Sahrens (void) setlocale(LC_ALL, ""); 2770789Sahrens (void) textdomain(TEXT_DOMAIN); 2771789Sahrens 2772789Sahrens opterr = 0; 2773789Sahrens 2774*2082Seschrock if ((g_zfs = libzfs_init()) == NULL) { 2775*2082Seschrock (void) fprintf(stderr, gettext("internal error: failed to " 2776*2082Seschrock "initialize ZFS library\n")); 2777*2082Seschrock return (1); 2778*2082Seschrock } 2779*2082Seschrock 2780*2082Seschrock libzfs_print_on_error(g_zfs, B_TRUE); 2781*2082Seschrock 2782789Sahrens if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 2783789Sahrens (void) fprintf(stderr, gettext("internal error: unable to " 2784789Sahrens "open %s\n"), MNTTAB); 2785789Sahrens return (1); 2786789Sahrens } 2787789Sahrens 2788789Sahrens /* 2789789Sahrens * This command also doubles as the /etc/fs mount and unmount program. 2790789Sahrens * Determine if we should take this behavior based on argv[0]. 2791789Sahrens */ 2792789Sahrens progname = basename(argv[0]); 2793789Sahrens if (strcmp(progname, "mount") == 0) { 2794789Sahrens ret = manual_mount(argc, argv); 2795789Sahrens } else if (strcmp(progname, "umount") == 0) { 2796789Sahrens ret = manual_unmount(argc, argv); 2797789Sahrens } else { 2798789Sahrens /* 2799789Sahrens * Make sure the user has specified some command. 2800789Sahrens */ 2801789Sahrens if (argc < 2) { 2802789Sahrens (void) fprintf(stderr, gettext("missing command\n")); 2803*2082Seschrock usage(B_FALSE); 2804789Sahrens } 2805789Sahrens 2806789Sahrens cmdname = argv[1]; 2807789Sahrens 2808789Sahrens /* 2809789Sahrens * The 'umount' command is an alias for 'unmount' 2810789Sahrens */ 2811789Sahrens if (strcmp(cmdname, "umount") == 0) 2812789Sahrens cmdname = "unmount"; 2813789Sahrens 2814789Sahrens /* 28151749Sahrens * The 'recv' command is an alias for 'receive' 28161749Sahrens */ 28171749Sahrens if (strcmp(cmdname, "recv") == 0) 28181749Sahrens cmdname = "receive"; 28191749Sahrens 28201749Sahrens /* 2821789Sahrens * Special case '-?' 2822789Sahrens */ 2823789Sahrens if (strcmp(cmdname, "-?") == 0) 2824*2082Seschrock usage(B_TRUE); 2825789Sahrens 2826789Sahrens /* 2827789Sahrens * 'volinit' and 'volfini' do not appear in the usage message, 2828789Sahrens * so we have to special case them here. 2829789Sahrens */ 2830789Sahrens if (strcmp(cmdname, "volinit") == 0) 2831*2082Seschrock return (do_volcheck(B_TRUE)); 2832789Sahrens else if (strcmp(cmdname, "volfini") == 0) 2833*2082Seschrock return (do_volcheck(B_FALSE)); 2834789Sahrens 2835789Sahrens /* 2836789Sahrens * Run the appropriate command. 2837789Sahrens */ 2838789Sahrens for (i = 0; i < NCOMMAND; i++) { 2839789Sahrens if (command_table[i].name == NULL) 2840789Sahrens continue; 2841789Sahrens 2842789Sahrens if (strcmp(cmdname, command_table[i].name) == 0) { 2843789Sahrens current_command = &command_table[i]; 2844789Sahrens ret = command_table[i].func(argc - 1, argv + 1); 2845789Sahrens break; 2846789Sahrens } 2847789Sahrens } 2848789Sahrens 2849789Sahrens if (i == NCOMMAND) { 2850789Sahrens (void) fprintf(stderr, gettext("unrecognized " 2851789Sahrens "command '%s'\n"), cmdname); 2852*2082Seschrock usage(B_FALSE); 2853789Sahrens } 2854789Sahrens } 2855789Sahrens 2856789Sahrens (void) fclose(mnttab_file); 2857789Sahrens 2858*2082Seschrock libzfs_fini(g_zfs); 2859*2082Seschrock 2860789Sahrens /* 2861789Sahrens * The 'ZFS_ABORT' environment variable causes us to dump core on exit 2862789Sahrens * for the purposes of running ::findleaks. 2863789Sahrens */ 2864789Sahrens if (getenv("ZFS_ABORT") != NULL) { 2865789Sahrens (void) printf("dumping core by request\n"); 2866789Sahrens abort(); 2867789Sahrens } 2868789Sahrens 2869789Sahrens return (ret); 2870789Sahrens } 2871