1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 5789Sahrens * Common Development and Distribution License, Version 1.0 only 6789Sahrens * (the "License"). You may not use this file except in compliance 7789Sahrens * with the License. 8789Sahrens * 9789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10789Sahrens * or http://www.opensolaris.org/os/licensing. 11789Sahrens * See the License for the specific language governing permissions 12789Sahrens * and limitations under the License. 13789Sahrens * 14789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 15789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16789Sahrens * If applicable, add the following below this CDDL HEADER, with the 17789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 18789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 19789Sahrens * 20789Sahrens * CDDL HEADER END 21789Sahrens */ 22789Sahrens /* 231204Slling * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24789Sahrens * Use is subject to license terms. 25789Sahrens */ 26789Sahrens 27789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 28789Sahrens 29789Sahrens #include <assert.h> 30789Sahrens #include <errno.h> 31789Sahrens #include <libgen.h> 32789Sahrens #include <libintl.h> 33789Sahrens #include <libuutil.h> 34789Sahrens #include <locale.h> 35789Sahrens #include <stddef.h> 36789Sahrens #include <stdio.h> 37789Sahrens #include <stdlib.h> 38789Sahrens #include <strings.h> 39789Sahrens #include <unistd.h> 40789Sahrens #include <fcntl.h> 41789Sahrens #include <zone.h> 42789Sahrens #include <sys/mkdev.h> 43789Sahrens #include <sys/mntent.h> 44789Sahrens #include <sys/mnttab.h> 45789Sahrens #include <sys/mount.h> 46789Sahrens #include <sys/stat.h> 47789Sahrens 48789Sahrens #include <libzfs.h> 49789Sahrens 50789Sahrens #include "zfs_iter.h" 51789Sahrens 52789Sahrens static FILE *mnttab_file; 53789Sahrens 54789Sahrens static int zfs_do_clone(int argc, char **argv); 55789Sahrens static int zfs_do_create(int argc, char **argv); 56789Sahrens static int zfs_do_destroy(int argc, char **argv); 57789Sahrens static int zfs_do_get(int argc, char **argv); 58789Sahrens static int zfs_do_inherit(int argc, char **argv); 59789Sahrens static int zfs_do_list(int argc, char **argv); 60789Sahrens static int zfs_do_mount(int argc, char **argv); 61789Sahrens static int zfs_do_rename(int argc, char **argv); 62789Sahrens static int zfs_do_rollback(int argc, char **argv); 63789Sahrens static int zfs_do_set(int argc, char **argv); 64789Sahrens static int zfs_do_snapshot(int argc, char **argv); 65789Sahrens static int zfs_do_unmount(int argc, char **argv); 66789Sahrens static int zfs_do_share(int argc, char **argv); 67789Sahrens static int zfs_do_unshare(int argc, char **argv); 68789Sahrens static int zfs_do_backup(int argc, char **argv); 69789Sahrens static int zfs_do_restore(int argc, char **argv); 70789Sahrens 71789Sahrens /* 72789Sahrens * These libumem hooks provide a reasonable set of defaults for the allocator's 73789Sahrens * debugging facilities. 74789Sahrens */ 75789Sahrens const char * 76789Sahrens _umem_debug_init() 77789Sahrens { 78789Sahrens return ("default,verbose"); /* $UMEM_DEBUG setting */ 79789Sahrens } 80789Sahrens 81789Sahrens const char * 82789Sahrens _umem_logging_init(void) 83789Sahrens { 84789Sahrens return ("fail,contents"); /* $UMEM_LOGGING setting */ 85789Sahrens } 86789Sahrens 87*1387Seschrock typedef enum { 88*1387Seschrock HELP_BACKUP, 89*1387Seschrock HELP_CLONE, 90*1387Seschrock HELP_CREATE, 91*1387Seschrock HELP_DESTROY, 92*1387Seschrock HELP_GET, 93*1387Seschrock HELP_INHERIT, 94*1387Seschrock HELP_LIST, 95*1387Seschrock HELP_MOUNT, 96*1387Seschrock HELP_RENAME, 97*1387Seschrock HELP_RESTORE, 98*1387Seschrock HELP_ROLLBACK, 99*1387Seschrock HELP_SET, 100*1387Seschrock HELP_SHARE, 101*1387Seschrock HELP_SNAPSHOT, 102*1387Seschrock HELP_UNMOUNT, 103*1387Seschrock HELP_UNSHARE 104*1387Seschrock } zfs_help_t; 105*1387Seschrock 106789Sahrens typedef struct zfs_command { 107789Sahrens const char *name; 108789Sahrens int (*func)(int argc, char **argv); 109*1387Seschrock zfs_help_t usage; 110789Sahrens } zfs_command_t; 111789Sahrens 112789Sahrens /* 113789Sahrens * Master command table. Each ZFS command has a name, associated function, and 114*1387Seschrock * usage message. Unfortunately, the usage messages need to be 115*1387Seschrock * iternationalized, so we have to have a function to return the usage message 116*1387Seschrock * based on a command index. 117*1387Seschrock * 118*1387Seschrock * These commands are organized according to how they are displayed in the usage 119*1387Seschrock * message. An empty command (one with a NULL name) indicates an empty line in 120*1387Seschrock * the generic usage message. 121789Sahrens */ 122789Sahrens static zfs_command_t command_table[] = { 123*1387Seschrock { "create", zfs_do_create, HELP_CREATE }, 124*1387Seschrock { "destroy", zfs_do_destroy, HELP_DESTROY }, 125789Sahrens { NULL }, 126*1387Seschrock { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT }, 127*1387Seschrock { "rollback", zfs_do_rollback, HELP_ROLLBACK }, 128*1387Seschrock { "clone", zfs_do_clone, HELP_CLONE }, 129*1387Seschrock { "rename", zfs_do_rename, HELP_RENAME }, 130789Sahrens { NULL }, 131*1387Seschrock { "list", zfs_do_list, HELP_LIST }, 132789Sahrens { NULL }, 133*1387Seschrock { "set", zfs_do_set, HELP_SET }, 134*1387Seschrock { "get", zfs_do_get, HELP_GET }, 135*1387Seschrock { "inherit", zfs_do_inherit, HELP_INHERIT }, 136789Sahrens { NULL }, 137*1387Seschrock { "mount", zfs_do_mount, HELP_MOUNT }, 138789Sahrens { NULL }, 139*1387Seschrock { "unmount", zfs_do_unmount, HELP_UNMOUNT }, 140789Sahrens { NULL }, 141*1387Seschrock { "share", zfs_do_share, HELP_SHARE }, 142789Sahrens { NULL }, 143*1387Seschrock { "unshare", zfs_do_unshare, HELP_UNSHARE }, 144789Sahrens { NULL }, 145*1387Seschrock { "backup", zfs_do_backup, HELP_BACKUP }, 146*1387Seschrock { "restore", zfs_do_restore, HELP_RESTORE }, 147789Sahrens }; 148789Sahrens 149789Sahrens #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) 150789Sahrens 151789Sahrens zfs_command_t *current_command; 152789Sahrens 153*1387Seschrock static const char * 154*1387Seschrock get_usage(zfs_help_t idx) 155*1387Seschrock { 156*1387Seschrock switch (idx) { 157*1387Seschrock case HELP_BACKUP: 158*1387Seschrock return (gettext("\tbackup [-i <snapshot>] <snapshot>\n")); 159*1387Seschrock case HELP_CLONE: 160*1387Seschrock return (gettext("\tclone <snapshot> <filesystem|volume>\n")); 161*1387Seschrock case HELP_CREATE: 162*1387Seschrock return (gettext("\tcreate <filesystem>\n" 163*1387Seschrock "\tcreate [-s] [-b blocksize] -V <size> <volume>\n")); 164*1387Seschrock case HELP_DESTROY: 165*1387Seschrock return (gettext("\tdestroy [-rRf] " 166*1387Seschrock "<filesystem|volume|snapshot>\n")); 167*1387Seschrock case HELP_GET: 168*1387Seschrock return (gettext("\tget [-rHp] [-o field[,field]...] " 169*1387Seschrock "[-s source[,source]...]\n" 170*1387Seschrock "\t <all | property[,property]...> " 171*1387Seschrock "<filesystem|volume|snapshot> ...\n")); 172*1387Seschrock case HELP_INHERIT: 173*1387Seschrock return (gettext("\tinherit [-r] <property> " 174*1387Seschrock "<filesystem|volume> ...\n")); 175*1387Seschrock case HELP_LIST: 176*1387Seschrock return (gettext("\tlist [-rH] [-o property[,property]...] " 177*1387Seschrock "[-t type[,type]...]\n" 178*1387Seschrock "\t [filesystem|volume|snapshot] ...\n")); 179*1387Seschrock case HELP_MOUNT: 180*1387Seschrock return (gettext("\tmount\n" 181*1387Seschrock "\tmount [-o opts] [-O] -a\n" 182*1387Seschrock "\tmount [-o opts] [-O] <filesystem>\n")); 183*1387Seschrock case HELP_RENAME: 184*1387Seschrock return (gettext("\trename <filesystem|volume|snapshot> " 185*1387Seschrock "<filesystem|volume|snapshot>\n")); 186*1387Seschrock case HELP_RESTORE: 187*1387Seschrock return (gettext("\trestore [-vn] <filesystem|volume|snapshot>\n" 188*1387Seschrock "\trestore [-vn] -d <filesystem>\n")); 189*1387Seschrock case HELP_ROLLBACK: 190*1387Seschrock return (gettext("\trollback [-rRf] <snapshot>\n")); 191*1387Seschrock case HELP_SET: 192*1387Seschrock return (gettext("\tset <property=value> " 193*1387Seschrock "<filesystem|volume> ...\n")); 194*1387Seschrock case HELP_SHARE: 195*1387Seschrock return (gettext("\tshare -a\n" 196*1387Seschrock "\tshare <filesystem>\n")); 197*1387Seschrock case HELP_SNAPSHOT: 198*1387Seschrock return (gettext("\tsnapshot <filesystem@name|volume@name>\n")); 199*1387Seschrock case HELP_UNMOUNT: 200*1387Seschrock return (gettext("\tunmount [-f] -a\n" 201*1387Seschrock "\tunmount [-f] <filesystem|mountpoint>\n")); 202*1387Seschrock case HELP_UNSHARE: 203*1387Seschrock return (gettext("\tunshare [-f] -a\n" 204*1387Seschrock "\tunshare [-f] <filesystem|mountpoint>\n")); 205*1387Seschrock } 206*1387Seschrock 207*1387Seschrock abort(); 208*1387Seschrock /* NOTREACHED */ 209*1387Seschrock } 210*1387Seschrock 211789Sahrens /* 212789Sahrens * Utility function to guarantee malloc() success. 213789Sahrens */ 214789Sahrens void * 215789Sahrens safe_malloc(size_t size) 216789Sahrens { 217789Sahrens void *data; 218789Sahrens 219789Sahrens if ((data = calloc(1, size)) == NULL) { 220789Sahrens (void) fprintf(stderr, "internal error: out of memory\n"); 221789Sahrens exit(1); 222789Sahrens } 223789Sahrens 224789Sahrens return (data); 225789Sahrens } 226789Sahrens 227789Sahrens /* 228789Sahrens * Display usage message. If we're inside a command, display only the usage for 229789Sahrens * that command. Otherwise, iterate over the entire command table and display 230789Sahrens * a complete usage message. 231789Sahrens */ 232789Sahrens static void 233789Sahrens usage(int requested) 234789Sahrens { 235789Sahrens int i; 236789Sahrens int show_properties = FALSE; 237789Sahrens FILE *fp = requested ? stdout : stderr; 238789Sahrens 239789Sahrens if (current_command == NULL) { 240789Sahrens 241789Sahrens (void) fprintf(fp, gettext("usage: zfs command args ...\n")); 242789Sahrens (void) fprintf(fp, 243789Sahrens gettext("where 'command' is one of the following:\n\n")); 244789Sahrens 245789Sahrens for (i = 0; i < NCOMMAND; i++) { 246789Sahrens if (command_table[i].name == NULL) 247789Sahrens (void) fprintf(fp, "\n"); 248789Sahrens else 249789Sahrens (void) fprintf(fp, "%s", 250*1387Seschrock get_usage(command_table[i].usage)); 251789Sahrens } 252789Sahrens 253789Sahrens (void) fprintf(fp, gettext("\nEach dataset is of the form: " 254789Sahrens "pool/[dataset/]*dataset[@name]\n")); 255789Sahrens } else { 256789Sahrens (void) fprintf(fp, gettext("usage:\n")); 257*1387Seschrock (void) fprintf(fp, "%s", get_usage(current_command->usage)); 258789Sahrens } 259789Sahrens 260789Sahrens if (current_command == NULL || 261789Sahrens strcmp(current_command->name, "set") == 0 || 262789Sahrens strcmp(current_command->name, "get") == 0 || 263789Sahrens strcmp(current_command->name, "inherit") == 0 || 264789Sahrens strcmp(current_command->name, "list") == 0) 265789Sahrens show_properties = TRUE; 266789Sahrens 267789Sahrens if (show_properties) { 268789Sahrens 269789Sahrens (void) fprintf(fp, 270789Sahrens gettext("\nThe following properties are supported:\n")); 271789Sahrens 272789Sahrens (void) fprintf(fp, "\n\t%-13s %s %s %s\n\n", 273789Sahrens "PROPERTY", "EDIT", "INHERIT", "VALUES"); 274789Sahrens 275789Sahrens for (i = 0; i < ZFS_NPROP_VISIBLE; i++) { 276789Sahrens (void) fprintf(fp, "\t%-13s ", zfs_prop_to_name(i)); 277789Sahrens 278789Sahrens if (zfs_prop_readonly(i)) 279789Sahrens (void) fprintf(fp, " NO "); 280789Sahrens else 281789Sahrens (void) fprintf(fp, " YES "); 282789Sahrens 283789Sahrens if (zfs_prop_inheritable(i)) 284789Sahrens (void) fprintf(fp, " YES "); 285789Sahrens else 286789Sahrens (void) fprintf(fp, " NO "); 287789Sahrens 288789Sahrens if (zfs_prop_values(i) == NULL) 289789Sahrens (void) fprintf(fp, "-\n"); 290789Sahrens else 291789Sahrens (void) fprintf(fp, "%s\n", zfs_prop_values(i)); 292789Sahrens } 293789Sahrens (void) fprintf(fp, gettext("\nSizes are specified in bytes " 294789Sahrens "with standard units such as K, M, G, etc.\n")); 295789Sahrens } 296789Sahrens 297789Sahrens exit(requested ? 0 : 2); 298789Sahrens } 299789Sahrens 300789Sahrens /* 301789Sahrens * zfs clone <fs, snap, vol> fs 302789Sahrens * 303789Sahrens * Given an existing dataset, create a writable copy whose initial contents 304789Sahrens * are the same as the source. The newly created dataset maintains a 305789Sahrens * dependency on the original; the original cannot be destroyed so long as 306789Sahrens * the clone exists. 307789Sahrens */ 308789Sahrens static int 309789Sahrens zfs_do_clone(int argc, char **argv) 310789Sahrens { 311789Sahrens zfs_handle_t *zhp; 312789Sahrens int ret; 313789Sahrens 314789Sahrens /* check options */ 315789Sahrens if (argc > 1 && argv[1][0] == '-') { 316789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 317789Sahrens argv[1][1]); 318789Sahrens usage(FALSE); 319789Sahrens } 320789Sahrens 321789Sahrens /* check number of arguments */ 322789Sahrens if (argc < 2) { 323789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 324789Sahrens "argument\n")); 325789Sahrens usage(FALSE); 326789Sahrens } 327789Sahrens if (argc < 3) { 328789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 329789Sahrens "argument\n")); 330789Sahrens usage(FALSE); 331789Sahrens } 332789Sahrens if (argc > 3) { 333789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 334789Sahrens usage(FALSE); 335789Sahrens } 336789Sahrens 337789Sahrens /* open the source dataset */ 338789Sahrens if ((zhp = zfs_open(argv[1], ZFS_TYPE_SNAPSHOT)) == NULL) 339789Sahrens return (1); 340789Sahrens 341789Sahrens /* pass to libzfs */ 342789Sahrens ret = zfs_clone(zhp, argv[2]); 343789Sahrens 344789Sahrens /* create the mountpoint if necessary */ 345789Sahrens if (ret == 0) { 346789Sahrens zfs_handle_t *clone = zfs_open(argv[2], ZFS_TYPE_ANY); 347789Sahrens if (clone != NULL) { 348789Sahrens if ((ret = zfs_mount(clone, NULL, 0)) == 0) 349789Sahrens ret = zfs_share(clone); 350789Sahrens zfs_close(clone); 351789Sahrens } 352789Sahrens } 353789Sahrens 354789Sahrens zfs_close(zhp); 355789Sahrens 356789Sahrens return (ret == 0 ? 0 : 1); 357789Sahrens } 358789Sahrens 359789Sahrens /* 360789Sahrens * zfs create fs 361789Sahrens * zfs create [-s] -V vol size 362789Sahrens * 363789Sahrens * Create a new dataset. This command can be used to create filesystems 364789Sahrens * and volumes. Snapshot creation is handled by 'zfs snapshot'. 365789Sahrens * For volumes, the user must specify a size to be used. 366789Sahrens * 367789Sahrens * The '-s' flag applies only to volumes, and indicates that we should not try 368789Sahrens * to set the reservation for this volume. By default we set a reservation 369789Sahrens * equal to the size for any volume. 370789Sahrens */ 371789Sahrens static int 372789Sahrens zfs_do_create(int argc, char **argv) 373789Sahrens { 374789Sahrens zfs_type_t type = ZFS_TYPE_FILESYSTEM; 375789Sahrens zfs_handle_t *zhp; 376789Sahrens char *size = NULL; 377789Sahrens char *blocksize = NULL; 378789Sahrens int c; 379789Sahrens int noreserve = FALSE; 380789Sahrens int ret; 381789Sahrens 382789Sahrens /* check options */ 383789Sahrens while ((c = getopt(argc, argv, ":V:b:s")) != -1) { 384789Sahrens switch (c) { 385789Sahrens case 'V': 386789Sahrens type = ZFS_TYPE_VOLUME; 387789Sahrens size = optarg; 388789Sahrens break; 389789Sahrens case 'b': 390789Sahrens blocksize = optarg; 391789Sahrens break; 392789Sahrens case 's': 393789Sahrens noreserve = TRUE; 394789Sahrens break; 395789Sahrens case ':': 396789Sahrens (void) fprintf(stderr, gettext("missing size " 397789Sahrens "argument\n")); 398789Sahrens usage(FALSE); 399789Sahrens break; 400789Sahrens case '?': 401789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 402789Sahrens optopt); 403789Sahrens usage(FALSE); 404789Sahrens } 405789Sahrens } 406789Sahrens 407789Sahrens if (noreserve && type != ZFS_TYPE_VOLUME) { 408789Sahrens (void) fprintf(stderr, gettext("'-s' can only be used when " 409789Sahrens "creating a volume\n")); 410789Sahrens usage(FALSE); 411789Sahrens } 412789Sahrens 413789Sahrens argc -= optind; 414789Sahrens argv += optind; 415789Sahrens 416789Sahrens /* check number of arguments */ 417789Sahrens if (argc == 0) { 418789Sahrens (void) fprintf(stderr, gettext("missing %s argument\n"), 419789Sahrens zfs_type_to_name(type)); 420789Sahrens usage(FALSE); 421789Sahrens } 422789Sahrens if (argc > 1) { 423789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 424789Sahrens usage(FALSE); 425789Sahrens } 426789Sahrens 427789Sahrens /* pass to libzfs */ 428789Sahrens if (zfs_create(argv[0], type, size, blocksize) != 0) 429789Sahrens return (1); 430789Sahrens 431789Sahrens if ((zhp = zfs_open(argv[0], ZFS_TYPE_ANY)) == NULL) 432789Sahrens return (1); 433789Sahrens 434789Sahrens /* 435789Sahrens * Volume handling. By default, we try to create a reservation of equal 436789Sahrens * size for the volume. If we can't do this, then destroy the dataset 437789Sahrens * and report an error. 438789Sahrens */ 439789Sahrens if (type == ZFS_TYPE_VOLUME && !noreserve) { 440789Sahrens if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, size) != 0) { 441789Sahrens (void) fprintf(stderr, gettext("use '-s' to create a " 442789Sahrens "volume without a matching reservation\n")); 443789Sahrens (void) zfs_destroy(zhp); 444789Sahrens return (1); 445789Sahrens } 446789Sahrens } 447789Sahrens 448789Sahrens /* 449789Sahrens * Mount and/or share the new filesystem as appropriate. We provide a 450789Sahrens * verbose error message to let the user know that their filesystem was 451789Sahrens * in fact created, even if we failed to mount or share it. 452789Sahrens */ 453789Sahrens if (zfs_mount(zhp, NULL, 0) != 0) { 454789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 455789Sahrens "created, but not mounted\n")); 456789Sahrens ret = 1; 457789Sahrens } else if (zfs_share(zhp) != 0) { 458789Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 459789Sahrens "created, but not shared\n")); 460789Sahrens ret = 1; 461789Sahrens } else { 462789Sahrens ret = 0; 463789Sahrens } 464789Sahrens 465789Sahrens zfs_close(zhp); 466789Sahrens return (ret); 467789Sahrens } 468789Sahrens 469789Sahrens /* 470789Sahrens * zfs destroy [-rf] <fs, snap, vol> 471789Sahrens * 472789Sahrens * -r Recursively destroy all children 473789Sahrens * -R Recursively destroy all dependents, including clones 474789Sahrens * -f Force unmounting of any dependents 475789Sahrens * 476789Sahrens * Destroys the given dataset. By default, it will unmount any filesystems, 477789Sahrens * and refuse to destroy a dataset that has any dependents. A dependent can 478789Sahrens * either be a child, or a clone of a child. 479789Sahrens */ 480789Sahrens typedef struct destroy_cbdata { 481789Sahrens int cb_first; 482789Sahrens int cb_force; 483789Sahrens int cb_recurse; 484789Sahrens int cb_error; 485789Sahrens int cb_needforce; 486789Sahrens int cb_doclones; 487789Sahrens zfs_handle_t *cb_target; 488789Sahrens } destroy_cbdata_t; 489789Sahrens 490789Sahrens /* 491789Sahrens * Check for any dependents based on the '-r' or '-R' flags. 492789Sahrens */ 493789Sahrens static int 494789Sahrens destroy_check_dependent(zfs_handle_t *zhp, void *data) 495789Sahrens { 496789Sahrens destroy_cbdata_t *cbp = data; 497789Sahrens const char *tname = zfs_get_name(cbp->cb_target); 498789Sahrens const char *name = zfs_get_name(zhp); 499789Sahrens 500789Sahrens if (strncmp(tname, name, strlen(tname)) == 0 && 501789Sahrens (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { 502789Sahrens /* 503789Sahrens * This is a direct descendant, not a clone somewhere else in 504789Sahrens * the hierarchy. 505789Sahrens */ 506789Sahrens if (cbp->cb_recurse) 507789Sahrens goto out; 508789Sahrens 509789Sahrens if (cbp->cb_first) { 510789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 511789Sahrens "%s has children\n"), 512789Sahrens zfs_get_name(cbp->cb_target), 513789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 514789Sahrens (void) fprintf(stderr, gettext("use '-r' to destroy " 515789Sahrens "the following datasets:\n")); 516789Sahrens cbp->cb_first = 0; 517789Sahrens cbp->cb_error = 1; 518789Sahrens } 519789Sahrens 520789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 521789Sahrens } else { 522789Sahrens /* 523789Sahrens * This is a clone. We only want to report this if the '-r' 524789Sahrens * wasn't specified, or the target is a snapshot. 525789Sahrens */ 526789Sahrens if (!cbp->cb_recurse && 527789Sahrens zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) 528789Sahrens goto out; 529789Sahrens 530789Sahrens if (cbp->cb_first) { 531789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 532789Sahrens "%s has dependent clones\n"), 533789Sahrens zfs_get_name(cbp->cb_target), 534789Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 535789Sahrens (void) fprintf(stderr, gettext("use '-R' to destroy " 536789Sahrens "the following datasets:\n")); 537789Sahrens cbp->cb_first = 0; 538789Sahrens cbp->cb_error = 1; 539789Sahrens } 540789Sahrens 541789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 542789Sahrens } 543789Sahrens 544789Sahrens out: 545789Sahrens zfs_close(zhp); 546789Sahrens return (0); 547789Sahrens } 548789Sahrens 549789Sahrens static int 550789Sahrens destroy_callback(zfs_handle_t *zhp, void *data) 551789Sahrens { 552789Sahrens destroy_cbdata_t *cbp = data; 553789Sahrens 554789Sahrens /* 555789Sahrens * Ignore pools (which we've already flagged as an error before getting 556789Sahrens * here. 557789Sahrens */ 558789Sahrens if (strchr(zfs_get_name(zhp), '/') == NULL && 559789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 560789Sahrens zfs_close(zhp); 561789Sahrens return (0); 562789Sahrens } 563789Sahrens 564789Sahrens /* 565789Sahrens * Bail out on the first error. 566789Sahrens */ 567789Sahrens if (zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0 || 568789Sahrens zfs_destroy(zhp) != 0) { 569789Sahrens zfs_close(zhp); 570789Sahrens return (-1); 571789Sahrens } 572789Sahrens 573789Sahrens zfs_close(zhp); 574789Sahrens return (0); 575789Sahrens } 576789Sahrens 577789Sahrens 578789Sahrens static int 579789Sahrens zfs_do_destroy(int argc, char **argv) 580789Sahrens { 581789Sahrens destroy_cbdata_t cb = { 0 }; 582789Sahrens int c; 583789Sahrens zfs_handle_t *zhp; 584789Sahrens 585789Sahrens /* check options */ 586789Sahrens while ((c = getopt(argc, argv, "frR")) != -1) { 587789Sahrens switch (c) { 588789Sahrens case 'f': 589789Sahrens cb.cb_force = 1; 590789Sahrens break; 591789Sahrens case 'r': 592789Sahrens cb.cb_recurse = 1; 593789Sahrens break; 594789Sahrens case 'R': 595789Sahrens cb.cb_recurse = 1; 596789Sahrens cb.cb_doclones = 1; 597789Sahrens break; 598789Sahrens case '?': 599789Sahrens default: 600789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 601789Sahrens optopt); 602789Sahrens usage(FALSE); 603789Sahrens } 604789Sahrens } 605789Sahrens 606789Sahrens argc -= optind; 607789Sahrens argv += optind; 608789Sahrens 609789Sahrens /* check number of arguments */ 610789Sahrens if (argc == 0) { 611789Sahrens (void) fprintf(stderr, gettext("missing path argument\n")); 612789Sahrens usage(FALSE); 613789Sahrens } 614789Sahrens if (argc > 1) { 615789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 616789Sahrens usage(FALSE); 617789Sahrens } 618789Sahrens 619789Sahrens /* Open the given dataset */ 620789Sahrens if ((zhp = zfs_open(argv[0], ZFS_TYPE_ANY)) == NULL) 621789Sahrens return (1); 622789Sahrens 623789Sahrens cb.cb_target = zhp; 624789Sahrens 625789Sahrens /* 626789Sahrens * Perform an explicit check for pools before going any further. 627789Sahrens */ 628789Sahrens if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 629789Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 630789Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 631789Sahrens "operation does not apply to pools\n"), 632789Sahrens zfs_get_name(zhp)); 633789Sahrens (void) fprintf(stderr, gettext("use 'zfs destroy -r " 634789Sahrens "%s' to destroy all datasets in the pool\n"), 635789Sahrens zfs_get_name(zhp)); 636789Sahrens (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 637789Sahrens "to destroy the pool itself\n"), zfs_get_name(zhp)); 638789Sahrens zfs_close(zhp); 639789Sahrens return (1); 640789Sahrens } 641789Sahrens 642789Sahrens 643789Sahrens /* 644789Sahrens * Check for any dependents and/or clones. 645789Sahrens */ 646789Sahrens cb.cb_first = 1; 647789Sahrens if (!cb.cb_doclones) 648789Sahrens (void) zfs_iter_dependents(zhp, destroy_check_dependent, &cb); 649789Sahrens 650789Sahrens if (cb.cb_error) { 651789Sahrens zfs_close(zhp); 652789Sahrens return (1); 653789Sahrens } 654789Sahrens 655789Sahrens /* 656789Sahrens * Do the real thing. 657789Sahrens */ 658789Sahrens if (zfs_iter_dependents(zhp, destroy_callback, &cb) == 0 && 659789Sahrens destroy_callback(zhp, &cb) == 0) 660789Sahrens return (0); 661789Sahrens 662789Sahrens return (1); 663789Sahrens } 664789Sahrens 665789Sahrens /* 666866Seschrock * zfs get [-rHp] [-o field[,field]...] [-s source[,source]...] 667866Seschrock * < all | property[,property]... > < fs | snap | vol > ... 668789Sahrens * 669789Sahrens * -r recurse over any child datasets 670789Sahrens * -H scripted mode. Headers are stripped, and fields are separated 671789Sahrens * by tabs instead of spaces. 672789Sahrens * -o Set of fields to display. One of "name,property,value,source". 673789Sahrens * Default is all four. 674789Sahrens * -s Set of sources to allow. One of 675789Sahrens * "local,default,inherited,temporary,none". Default is all 676789Sahrens * five. 677789Sahrens * -p Display values in parsable (literal) format. 678789Sahrens * 679789Sahrens * Prints properties for the given datasets. The user can control which 680789Sahrens * columns to display as well as which property types to allow. 681789Sahrens */ 682789Sahrens typedef struct get_cbdata { 683789Sahrens int cb_scripted; 684789Sahrens int cb_sources; 685789Sahrens int cb_literal; 686789Sahrens int cb_columns[4]; 687789Sahrens zfs_prop_t cb_prop[ZFS_NPROP_ALL]; 688789Sahrens int cb_nprop; 689866Seschrock int cb_isall; 690789Sahrens } get_cbdata_t; 691789Sahrens 692789Sahrens #define GET_COL_NAME 1 693789Sahrens #define GET_COL_PROPERTY 2 694789Sahrens #define GET_COL_VALUE 3 695789Sahrens #define GET_COL_SOURCE 4 696789Sahrens 697789Sahrens /* 698789Sahrens * Display a single line of output, according to the settings in the callback 699789Sahrens * structure. 700789Sahrens */ 701789Sahrens static void 702789Sahrens print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, zfs_prop_t prop, 703789Sahrens const char *value, zfs_source_t sourcetype, const char *source) 704789Sahrens { 705789Sahrens int i; 706789Sahrens int width; 707789Sahrens const char *str; 708789Sahrens char buf[128]; 709789Sahrens 710789Sahrens /* 711789Sahrens * Ignore those source types that the user has chosen to ignore. 712789Sahrens */ 713789Sahrens if ((sourcetype & cbp->cb_sources) == 0) 714789Sahrens return; 715789Sahrens 716789Sahrens for (i = 0; i < 4; i++) { 717789Sahrens switch (cbp->cb_columns[i]) { 718789Sahrens case GET_COL_NAME: 719789Sahrens width = 15; 720789Sahrens str = zfs_get_name(zhp); 721789Sahrens break; 722789Sahrens 723789Sahrens case GET_COL_PROPERTY: 724789Sahrens width = 13; 725789Sahrens str = zfs_prop_to_name(prop); 726789Sahrens break; 727789Sahrens 728789Sahrens case GET_COL_VALUE: 729789Sahrens width = 25; 730789Sahrens str = value; 731789Sahrens break; 732789Sahrens 733789Sahrens case GET_COL_SOURCE: 734789Sahrens width = 15; 735789Sahrens switch (sourcetype) { 736789Sahrens case ZFS_SRC_NONE: 737789Sahrens str = "-"; 738789Sahrens break; 739789Sahrens 740789Sahrens case ZFS_SRC_DEFAULT: 741789Sahrens str = "default"; 742789Sahrens break; 743789Sahrens 744789Sahrens case ZFS_SRC_LOCAL: 745789Sahrens str = "local"; 746789Sahrens break; 747789Sahrens 748789Sahrens case ZFS_SRC_TEMPORARY: 749789Sahrens str = "temporary"; 750789Sahrens break; 751789Sahrens 752789Sahrens case ZFS_SRC_INHERITED: 753789Sahrens (void) snprintf(buf, sizeof (buf), 754789Sahrens "inherited from %s", source); 755789Sahrens str = buf; 756789Sahrens break; 757789Sahrens } 758789Sahrens break; 759789Sahrens 760789Sahrens default: 761789Sahrens continue; 762789Sahrens } 763789Sahrens 764789Sahrens if (cbp->cb_columns[i + 1] == 0) 765789Sahrens (void) printf("%s", str); 766789Sahrens else if (cbp->cb_scripted) 767789Sahrens (void) printf("%s\t", str); 768789Sahrens else 769789Sahrens (void) printf("%-*s ", width, str); 770789Sahrens 771789Sahrens } 772789Sahrens 773789Sahrens (void) printf("\n"); 774789Sahrens } 775789Sahrens 776789Sahrens /* 777789Sahrens * Invoked to display the properties for a single dataset. 778789Sahrens */ 779789Sahrens static int 780789Sahrens get_callback(zfs_handle_t *zhp, void *data) 781789Sahrens { 782789Sahrens char buf[ZFS_MAXPROPLEN]; 783789Sahrens zfs_source_t sourcetype; 784789Sahrens char source[ZFS_MAXNAMELEN]; 785789Sahrens get_cbdata_t *cbp = data; 786789Sahrens int i; 787789Sahrens 788866Seschrock for (i = 0; i < cbp->cb_nprop; i++) { 789866Seschrock if (zfs_prop_get(zhp, cbp->cb_prop[i], buf, 790866Seschrock sizeof (buf), &sourcetype, source, sizeof (source), 791866Seschrock cbp->cb_literal) != 0) { 792866Seschrock if (cbp->cb_isall) 793866Seschrock continue; 794866Seschrock (void) strlcpy(buf, "-", sizeof (buf)); 795866Seschrock sourcetype = ZFS_SRC_NONE; 796866Seschrock } 797789Sahrens 798866Seschrock print_one_property(zhp, cbp, cbp->cb_prop[i], 799866Seschrock buf, sourcetype, source); 800789Sahrens } 801789Sahrens 802789Sahrens return (0); 803789Sahrens } 804789Sahrens 805789Sahrens static int 806789Sahrens zfs_do_get(int argc, char **argv) 807789Sahrens { 808789Sahrens get_cbdata_t cb = { 0 }; 809789Sahrens int recurse = 0; 810789Sahrens int c; 811866Seschrock char *value, *fields, *badopt; 812789Sahrens int i; 813866Seschrock int ret; 814789Sahrens 815789Sahrens /* 816789Sahrens * Set up default columns and sources. 817789Sahrens */ 818789Sahrens cb.cb_sources = ZFS_SRC_ALL; 819789Sahrens cb.cb_columns[0] = GET_COL_NAME; 820789Sahrens cb.cb_columns[1] = GET_COL_PROPERTY; 821789Sahrens cb.cb_columns[2] = GET_COL_VALUE; 822789Sahrens cb.cb_columns[3] = GET_COL_SOURCE; 823789Sahrens 824789Sahrens /* check options */ 825789Sahrens while ((c = getopt(argc, argv, ":o:s:rHp")) != -1) { 826789Sahrens switch (c) { 827789Sahrens case 'p': 828789Sahrens cb.cb_literal = TRUE; 829789Sahrens break; 830789Sahrens case 'r': 831789Sahrens recurse = TRUE; 832789Sahrens break; 833789Sahrens case 'H': 834789Sahrens cb.cb_scripted = TRUE; 835789Sahrens break; 836789Sahrens case ':': 837789Sahrens (void) fprintf(stderr, gettext("missing argument for " 838789Sahrens "'%c' option\n"), optopt); 839789Sahrens usage(FALSE); 840789Sahrens break; 841789Sahrens case 'o': 842789Sahrens /* 843789Sahrens * Process the set of columns to display. We zero out 844789Sahrens * the structure to give us a blank slate. 845789Sahrens */ 846789Sahrens bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 847789Sahrens i = 0; 848789Sahrens while (*optarg != '\0') { 849789Sahrens static char *col_subopts[] = 850789Sahrens { "name", "property", "value", "source", 851789Sahrens NULL }; 852789Sahrens 853789Sahrens if (i == 4) { 854789Sahrens (void) fprintf(stderr, gettext("too " 855789Sahrens "many fields given to -o " 856789Sahrens "option\n")); 857789Sahrens usage(FALSE); 858789Sahrens } 859789Sahrens 860789Sahrens switch (getsubopt(&optarg, col_subopts, 861789Sahrens &value)) { 862789Sahrens case 0: 863789Sahrens cb.cb_columns[i++] = GET_COL_NAME; 864789Sahrens break; 865789Sahrens case 1: 866789Sahrens cb.cb_columns[i++] = GET_COL_PROPERTY; 867789Sahrens break; 868789Sahrens case 2: 869789Sahrens cb.cb_columns[i++] = GET_COL_VALUE; 870789Sahrens break; 871789Sahrens case 3: 872789Sahrens cb.cb_columns[i++] = GET_COL_SOURCE; 873789Sahrens break; 874789Sahrens default: 875789Sahrens (void) fprintf(stderr, 876789Sahrens gettext("invalid column name " 877789Sahrens "'%s'\n"), value); 878789Sahrens usage(FALSE); 879789Sahrens } 880789Sahrens } 881789Sahrens break; 882789Sahrens 883789Sahrens case 's': 884789Sahrens cb.cb_sources = 0; 885789Sahrens while (*optarg != '\0') { 886789Sahrens static char *source_subopts[] = { 887789Sahrens "local", "default", "inherited", 888789Sahrens "temporary", "none", NULL }; 889789Sahrens 890789Sahrens switch (getsubopt(&optarg, source_subopts, 891789Sahrens &value)) { 892789Sahrens case 0: 893789Sahrens cb.cb_sources |= ZFS_SRC_LOCAL; 894789Sahrens break; 895789Sahrens case 1: 896789Sahrens cb.cb_sources |= ZFS_SRC_DEFAULT; 897789Sahrens break; 898789Sahrens case 2: 899789Sahrens cb.cb_sources |= ZFS_SRC_INHERITED; 900789Sahrens break; 901789Sahrens case 3: 902789Sahrens cb.cb_sources |= ZFS_SRC_TEMPORARY; 903789Sahrens break; 904789Sahrens case 4: 905789Sahrens cb.cb_sources |= ZFS_SRC_NONE; 906789Sahrens break; 907789Sahrens default: 908789Sahrens (void) fprintf(stderr, 909789Sahrens gettext("invalid source " 910789Sahrens "'%s'\n"), value); 911789Sahrens usage(FALSE); 912789Sahrens } 913789Sahrens } 914789Sahrens break; 915789Sahrens 916789Sahrens case '?': 917789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 918789Sahrens optopt); 919789Sahrens usage(FALSE); 920789Sahrens } 921789Sahrens } 922789Sahrens 923789Sahrens argc -= optind; 924789Sahrens argv += optind; 925789Sahrens 926789Sahrens if (argc < 1) { 927789Sahrens (void) fprintf(stderr, gettext("missing property " 928789Sahrens "argument\n")); 929789Sahrens usage(FALSE); 930789Sahrens } 931789Sahrens 932789Sahrens fields = argv[0]; 933789Sahrens 934789Sahrens /* 935866Seschrock * If the user specifies 'all', the behavior of 'zfs get' is slightly 936866Seschrock * different, because we don't show properties which don't apply to the 937866Seschrock * given dataset. 938789Sahrens */ 939866Seschrock if (strcmp(fields, "all") == 0) 940866Seschrock cb.cb_isall = TRUE; 941789Sahrens 942866Seschrock if ((ret = zfs_get_proplist(fields, cb.cb_prop, ZFS_NPROP_ALL, 943866Seschrock &cb.cb_nprop, &badopt)) != 0) { 944866Seschrock if (ret == EINVAL) 945866Seschrock (void) fprintf(stderr, gettext("invalid property " 946866Seschrock "'%s'\n"), badopt); 947866Seschrock else 948866Seschrock (void) fprintf(stderr, gettext("too many properties " 949866Seschrock "specified\n")); 950866Seschrock usage(FALSE); 951789Sahrens } 952789Sahrens 953789Sahrens argc--; 954789Sahrens argv++; 955789Sahrens 956789Sahrens /* check for at least one dataset name */ 957789Sahrens if (argc < 1) { 958789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 959789Sahrens usage(FALSE); 960789Sahrens } 961789Sahrens 962789Sahrens /* 963789Sahrens * Print out any headers 964789Sahrens */ 965789Sahrens if (!cb.cb_scripted) { 966789Sahrens int i; 967789Sahrens for (i = 0; i < 4; i++) { 968789Sahrens switch (cb.cb_columns[i]) { 969789Sahrens case GET_COL_NAME: 970789Sahrens (void) printf("%-15s ", "NAME"); 971789Sahrens break; 972789Sahrens case GET_COL_PROPERTY: 973789Sahrens (void) printf("%-13s ", "PROPERTY"); 974789Sahrens break; 975789Sahrens case GET_COL_VALUE: 976789Sahrens (void) printf("%-25s ", "VALUE"); 977789Sahrens break; 978789Sahrens case GET_COL_SOURCE: 979789Sahrens (void) printf("%s", "SOURCE"); 980789Sahrens break; 981789Sahrens } 982789Sahrens } 983789Sahrens (void) printf("\n"); 984789Sahrens } 985789Sahrens 986789Sahrens /* run for each object */ 987789Sahrens return (zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, 988789Sahrens get_callback, &cb)); 989789Sahrens } 990789Sahrens 991789Sahrens /* 992789Sahrens * inherit [-r] <property> <fs|vol> ... 993789Sahrens * 994789Sahrens * -r Recurse over all children 995789Sahrens * 996789Sahrens * For each dataset specified on the command line, inherit the given property 997789Sahrens * from its parent. Inheriting a property at the pool level will cause it to 998789Sahrens * use the default value. The '-r' flag will recurse over all children, and is 999789Sahrens * useful for setting a property on a hierarchy-wide basis, regardless of any 1000789Sahrens * local modifications for each dataset. 1001789Sahrens */ 1002789Sahrens static int 1003789Sahrens inherit_callback(zfs_handle_t *zhp, void *data) 1004789Sahrens { 1005789Sahrens zfs_prop_t prop = (zfs_prop_t)data; 1006789Sahrens 1007789Sahrens return (zfs_prop_inherit(zhp, prop) != 0); 1008789Sahrens } 1009789Sahrens 1010789Sahrens static int 1011789Sahrens zfs_do_inherit(int argc, char **argv) 1012789Sahrens { 1013789Sahrens int recurse = 0; 1014789Sahrens int c; 1015789Sahrens zfs_prop_t prop; 1016789Sahrens char *propname; 1017789Sahrens 1018789Sahrens /* check options */ 1019789Sahrens while ((c = getopt(argc, argv, "r")) != -1) { 1020789Sahrens switch (c) { 1021789Sahrens case 'r': 1022789Sahrens recurse = TRUE; 1023789Sahrens break; 1024789Sahrens case '?': 1025789Sahrens default: 1026789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1027789Sahrens optopt); 1028789Sahrens usage(FALSE); 1029789Sahrens } 1030789Sahrens } 1031789Sahrens 1032789Sahrens argc -= optind; 1033789Sahrens argv += optind; 1034789Sahrens 1035789Sahrens /* check number of arguments */ 1036789Sahrens if (argc < 1) { 1037789Sahrens (void) fprintf(stderr, gettext("missing property argument\n")); 1038789Sahrens usage(FALSE); 1039789Sahrens } 1040789Sahrens if (argc < 2) { 1041789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 1042789Sahrens usage(FALSE); 1043789Sahrens } 1044789Sahrens 1045789Sahrens propname = argv[0]; 1046789Sahrens 1047789Sahrens /* 1048789Sahrens * Get and validate the property before iterating over the datasets. We 1049789Sahrens * do this now so as to avoid printing out an error message for each and 1050789Sahrens * every dataset. 1051789Sahrens */ 1052789Sahrens if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) { 1053789Sahrens (void) fprintf(stderr, gettext("invalid property '%s'\n"), 1054789Sahrens propname); 1055789Sahrens usage(FALSE); 1056789Sahrens } 1057789Sahrens if (zfs_prop_readonly(prop)) { 1058789Sahrens (void) fprintf(stderr, gettext("%s property is read-only\n"), 1059789Sahrens propname); 1060789Sahrens return (1); 1061789Sahrens } 1062789Sahrens if (!zfs_prop_inheritable(prop)) { 1063789Sahrens (void) fprintf(stderr, gettext("%s property cannot be " 1064789Sahrens "inherited\n"), propname); 1065789Sahrens (void) fprintf(stderr, gettext("use 'zfs set %s=none' to " 1066789Sahrens "clear\n"), propname); 1067789Sahrens return (1); 1068789Sahrens } 1069789Sahrens 1070789Sahrens return (zfs_for_each(argc - 1, argv + 1, recurse, 1071789Sahrens ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, 1072789Sahrens inherit_callback, (void *)prop)); 1073789Sahrens } 1074789Sahrens 1075789Sahrens /* 1076866Seschrock * list [-rH] [-o property[,property]...] [-t type[,type]...] <dataset> ... 1077789Sahrens * 1078789Sahrens * -r Recurse over all children 1079789Sahrens * -H Scripted mode; elide headers and separate colums by tabs 1080789Sahrens * -o Control which fields to display. 1081866Seschrock * -t Control which object types to display. 1082789Sahrens * 1083789Sahrens * When given no arguments, lists all filesystems in the system. 1084789Sahrens * Otherwise, list the specified datasets, optionally recursing down them if 1085789Sahrens * '-r' is specified. 1086789Sahrens */ 1087789Sahrens typedef struct list_cbdata { 1088866Seschrock int cb_first; 1089866Seschrock int cb_scripted; 1090866Seschrock zfs_prop_t cb_fields[ZFS_NPROP_ALL]; 1091866Seschrock int cb_fieldcount; 1092789Sahrens } list_cbdata_t; 1093789Sahrens 1094789Sahrens /* 1095789Sahrens * Given a list of columns to display, output appropriate headers for each one. 1096789Sahrens */ 1097789Sahrens static void 1098866Seschrock print_header(zfs_prop_t *fields, size_t count) 1099789Sahrens { 1100789Sahrens int i; 1101789Sahrens 1102789Sahrens for (i = 0; i < count; i++) { 1103789Sahrens if (i != 0) 1104789Sahrens (void) printf(" "); 1105789Sahrens if (i == count - 1) 1106789Sahrens (void) printf("%s", zfs_prop_column_name(fields[i])); 1107789Sahrens else /* LINTED - format specifier */ 1108789Sahrens (void) printf(zfs_prop_column_format(fields[i]), 1109789Sahrens zfs_prop_column_name(fields[i])); 1110789Sahrens } 1111789Sahrens 1112789Sahrens (void) printf("\n"); 1113789Sahrens } 1114789Sahrens 1115789Sahrens /* 1116789Sahrens * Given a dataset and a list of fields, print out all the properties according 1117789Sahrens * to the described layout. 1118789Sahrens */ 1119789Sahrens static void 1120866Seschrock print_dataset(zfs_handle_t *zhp, zfs_prop_t *fields, size_t count, int scripted) 1121789Sahrens { 1122789Sahrens int i; 1123789Sahrens char property[ZFS_MAXPROPLEN]; 1124789Sahrens 1125789Sahrens for (i = 0; i < count; i++) { 1126789Sahrens if (i != 0) { 1127789Sahrens if (scripted) 1128789Sahrens (void) printf("\t"); 1129789Sahrens else 1130789Sahrens (void) printf(" "); 1131789Sahrens } 1132789Sahrens 1133789Sahrens if (zfs_prop_get(zhp, fields[i], property, 1134789Sahrens sizeof (property), NULL, NULL, 0, FALSE) != 0) 1135789Sahrens (void) strlcpy(property, "-", sizeof (property)); 1136789Sahrens 1137866Seschrock /* 1138866Seschrock * If this is being called in scripted mode, or if this is the 1139866Seschrock * last column and it is left-justified, don't include a width 1140866Seschrock * format specifier. 1141866Seschrock */ 1142866Seschrock if (scripted || (i == count - 1 && 1143866Seschrock strchr(zfs_prop_column_format(fields[i]), '-') != NULL)) 1144789Sahrens (void) printf("%s", property); 1145789Sahrens else /* LINTED - format specifier */ 1146789Sahrens (void) printf(zfs_prop_column_format(fields[i]), 1147789Sahrens property); 1148789Sahrens } 1149789Sahrens 1150789Sahrens (void) printf("\n"); 1151789Sahrens } 1152789Sahrens 1153789Sahrens /* 1154789Sahrens * Generic callback function to list a dataset or snapshot. 1155789Sahrens */ 1156789Sahrens static int 1157789Sahrens list_callback(zfs_handle_t *zhp, void *data) 1158789Sahrens { 1159789Sahrens list_cbdata_t *cbp = data; 1160789Sahrens 1161789Sahrens if (cbp->cb_first) { 1162789Sahrens if (!cbp->cb_scripted) 1163789Sahrens print_header(cbp->cb_fields, cbp->cb_fieldcount); 1164789Sahrens cbp->cb_first = FALSE; 1165789Sahrens } 1166789Sahrens 1167789Sahrens print_dataset(zhp, cbp->cb_fields, cbp->cb_fieldcount, 1168789Sahrens cbp->cb_scripted); 1169789Sahrens 1170789Sahrens return (0); 1171789Sahrens } 1172789Sahrens 1173789Sahrens static int 1174789Sahrens zfs_do_list(int argc, char **argv) 1175789Sahrens { 1176789Sahrens int c; 1177789Sahrens int recurse = 0; 1178789Sahrens int scripted = FALSE; 1179789Sahrens static char default_fields[] = 1180789Sahrens "name,used,available,referenced,mountpoint"; 1181789Sahrens int types = ZFS_TYPE_ANY; 1182789Sahrens char *fields = NULL; 1183789Sahrens char *basic_fields = default_fields; 1184789Sahrens list_cbdata_t cb = { 0 }; 1185789Sahrens char *value; 1186789Sahrens int ret; 1187789Sahrens char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL }; 1188866Seschrock char *badopt; 1189866Seschrock int alloffset; 1190789Sahrens 1191789Sahrens /* check options */ 1192789Sahrens while ((c = getopt(argc, argv, ":o:rt:H")) != -1) { 1193789Sahrens switch (c) { 1194789Sahrens case 'o': 1195789Sahrens fields = optarg; 1196789Sahrens break; 1197789Sahrens case 'r': 1198789Sahrens recurse = TRUE; 1199789Sahrens break; 1200789Sahrens case 'H': 1201789Sahrens scripted = TRUE; 1202789Sahrens break; 1203789Sahrens case 't': 1204789Sahrens types = 0; 1205789Sahrens while (*optarg != '\0') { 1206789Sahrens switch (getsubopt(&optarg, type_subopts, 1207789Sahrens &value)) { 1208789Sahrens case 0: 1209789Sahrens types |= ZFS_TYPE_FILESYSTEM; 1210789Sahrens break; 1211789Sahrens case 1: 1212789Sahrens types |= ZFS_TYPE_VOLUME; 1213789Sahrens break; 1214789Sahrens case 2: 1215789Sahrens types |= ZFS_TYPE_SNAPSHOT; 1216789Sahrens break; 1217789Sahrens default: 1218789Sahrens (void) fprintf(stderr, 1219789Sahrens gettext("invalid type '%s'\n"), 1220789Sahrens value); 1221789Sahrens usage(FALSE); 1222789Sahrens } 1223789Sahrens } 1224789Sahrens break; 1225789Sahrens case ':': 1226789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1227789Sahrens "'%c' option\n"), optopt); 1228789Sahrens usage(FALSE); 1229789Sahrens break; 1230789Sahrens case '?': 1231789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1232789Sahrens optopt); 1233789Sahrens usage(FALSE); 1234789Sahrens } 1235789Sahrens } 1236789Sahrens 1237789Sahrens argc -= optind; 1238789Sahrens argv += optind; 1239789Sahrens 1240789Sahrens if (fields == NULL) 1241789Sahrens fields = basic_fields; 1242789Sahrens 1243866Seschrock /* 1244866Seschrock * If the user specifies '-o all', the zfs_get_proplist() doesn't 1245866Seschrock * normally include the name of the dataset. For 'zfs list', we always 1246866Seschrock * want this property to be first. 1247866Seschrock */ 1248866Seschrock if (strcmp(fields, "all") == 0) { 1249866Seschrock cb.cb_fields[0] = ZFS_PROP_NAME; 1250866Seschrock alloffset = 1; 1251866Seschrock } else { 1252866Seschrock alloffset = 0; 1253789Sahrens } 1254789Sahrens 1255866Seschrock if ((ret = zfs_get_proplist(fields, cb.cb_fields + alloffset, 1256866Seschrock ZFS_NPROP_ALL - alloffset, &cb.cb_fieldcount, &badopt)) != 0) { 1257866Seschrock if (ret == EINVAL) 1258866Seschrock (void) fprintf(stderr, gettext("invalid property " 1259866Seschrock "'%s'\n"), badopt); 1260866Seschrock else 1261866Seschrock (void) fprintf(stderr, gettext("too many properties " 1262866Seschrock "specified\n")); 1263866Seschrock usage(FALSE); 1264866Seschrock } 1265866Seschrock 1266866Seschrock cb.cb_fieldcount += alloffset; 1267789Sahrens cb.cb_scripted = scripted; 1268789Sahrens cb.cb_first = TRUE; 1269789Sahrens 1270789Sahrens ret = zfs_for_each(argc, argv, recurse, types, list_callback, &cb); 1271789Sahrens 1272789Sahrens if (ret == 0 && cb.cb_first == TRUE) 1273789Sahrens (void) printf(gettext("no datasets available\n")); 1274789Sahrens 1275789Sahrens return (ret); 1276789Sahrens } 1277789Sahrens 1278789Sahrens /* 1279789Sahrens * zfs rename <fs | snap | vol> <fs | snap | vol> 1280789Sahrens * 1281789Sahrens * Renames the given dataset to another of the same type. 1282789Sahrens */ 1283789Sahrens /* ARGSUSED */ 1284789Sahrens static int 1285789Sahrens zfs_do_rename(int argc, char **argv) 1286789Sahrens { 1287789Sahrens zfs_handle_t *zhp; 1288789Sahrens int ret = 1; 1289789Sahrens 1290789Sahrens /* check options */ 1291789Sahrens if (argc > 1 && argv[1][0] == '-') { 1292789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1293789Sahrens argv[1][1]); 1294789Sahrens usage(FALSE); 1295789Sahrens } 1296789Sahrens 1297789Sahrens /* check number of arguments */ 1298789Sahrens if (argc < 2) { 1299789Sahrens (void) fprintf(stderr, gettext("missing source dataset " 1300789Sahrens "argument\n")); 1301789Sahrens usage(FALSE); 1302789Sahrens } 1303789Sahrens if (argc < 3) { 1304789Sahrens (void) fprintf(stderr, gettext("missing target dataset " 1305789Sahrens "argument\n")); 1306789Sahrens usage(FALSE); 1307789Sahrens } 1308789Sahrens if (argc > 3) { 1309789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 1310789Sahrens usage(FALSE); 1311789Sahrens } 1312789Sahrens 1313789Sahrens if ((zhp = zfs_open(argv[1], ZFS_TYPE_ANY)) == NULL) 1314789Sahrens return (1); 1315789Sahrens 1316789Sahrens if (zfs_rename(zhp, argv[2]) != 0) 1317789Sahrens goto error; 1318789Sahrens 1319789Sahrens ret = 0; 1320789Sahrens error: 1321789Sahrens zfs_close(zhp); 1322789Sahrens return (ret); 1323789Sahrens } 1324789Sahrens 1325789Sahrens /* 1326789Sahrens * zfs rollback [-rfR] <snapshot> 1327789Sahrens * 1328789Sahrens * -r Delete any intervening snapshots before doing rollback 1329789Sahrens * -R Delete any snapshots and their clones 1330789Sahrens * -f Force unmount filesystems, even if they are in use. 1331789Sahrens * 1332789Sahrens * Given a filesystem, rollback to a specific snapshot, discarding any changes 1333789Sahrens * since then and making it the active dataset. If more recent snapshots exist, 1334789Sahrens * the command will complain unless the '-r' flag is given. 1335789Sahrens */ 1336789Sahrens typedef struct rollback_cbdata { 1337789Sahrens uint64_t cb_create; 1338789Sahrens int cb_first; 1339789Sahrens int cb_doclones; 1340789Sahrens char *cb_target; 1341789Sahrens int cb_error; 1342789Sahrens int cb_recurse; 1343789Sahrens int cb_dependent; 1344789Sahrens } rollback_cbdata_t; 1345789Sahrens 1346789Sahrens /* 1347789Sahrens * Report any snapshots more recent than the one specified. Used when '-r' is 1348789Sahrens * not specified. We reuse this same callback for the snapshot dependents - if 1349789Sahrens * 'cb_dependent' is set, then this is a dependent and we should report it 1350789Sahrens * without checking the transaction group. 1351789Sahrens */ 1352789Sahrens static int 1353789Sahrens rollback_check(zfs_handle_t *zhp, void *data) 1354789Sahrens { 1355789Sahrens rollback_cbdata_t *cbp = data; 1356789Sahrens 1357789Sahrens if (cbp->cb_doclones) 1358789Sahrens return (0); 1359789Sahrens 1360789Sahrens if (!cbp->cb_dependent) { 1361789Sahrens if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && 13621294Slling zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 1363789Sahrens zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 1364789Sahrens cbp->cb_create) { 1365789Sahrens 1366789Sahrens if (cbp->cb_first && !cbp->cb_recurse) { 1367789Sahrens (void) fprintf(stderr, gettext("cannot " 1368789Sahrens "rollback to '%s': more recent snapshots " 1369789Sahrens "exist\n"), 1370789Sahrens cbp->cb_target); 1371789Sahrens (void) fprintf(stderr, gettext("use '-r' to " 1372789Sahrens "force deletion of the following " 1373789Sahrens "snapshots:\n")); 1374789Sahrens cbp->cb_first = 0; 1375789Sahrens cbp->cb_error = 1; 1376789Sahrens } 1377789Sahrens 1378789Sahrens if (cbp->cb_recurse) { 1379789Sahrens cbp->cb_dependent = TRUE; 1380789Sahrens (void) zfs_iter_dependents(zhp, rollback_check, 1381789Sahrens cbp); 1382789Sahrens cbp->cb_dependent = FALSE; 1383789Sahrens } else { 1384789Sahrens (void) fprintf(stderr, "%s\n", 1385789Sahrens zfs_get_name(zhp)); 1386789Sahrens } 1387789Sahrens } 1388789Sahrens } else { 1389789Sahrens if (cbp->cb_first && cbp->cb_recurse) { 1390789Sahrens (void) fprintf(stderr, gettext("cannot rollback to " 1391789Sahrens "'%s': clones of previous snapshots exist\n"), 1392789Sahrens cbp->cb_target); 1393789Sahrens (void) fprintf(stderr, gettext("use '-R' to " 1394789Sahrens "force deletion of the following clones and " 1395789Sahrens "dependents:\n")); 1396789Sahrens cbp->cb_first = 0; 1397789Sahrens cbp->cb_error = 1; 1398789Sahrens } 1399789Sahrens 1400789Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1401789Sahrens } 1402789Sahrens 1403789Sahrens zfs_close(zhp); 1404789Sahrens return (0); 1405789Sahrens } 1406789Sahrens 1407789Sahrens static int 1408789Sahrens zfs_do_rollback(int argc, char **argv) 1409789Sahrens { 1410789Sahrens int ret; 1411789Sahrens int c; 1412789Sahrens rollback_cbdata_t cb = { 0 }; 1413789Sahrens zfs_handle_t *zhp, *snap; 1414789Sahrens char parentname[ZFS_MAXNAMELEN]; 1415789Sahrens char *delim; 14161294Slling int force = 0; 1417789Sahrens 1418789Sahrens /* check options */ 1419789Sahrens while ((c = getopt(argc, argv, "rfR")) != -1) { 1420789Sahrens switch (c) { 1421789Sahrens case 'f': 14221294Slling force = 1; 1423789Sahrens break; 1424789Sahrens case 'r': 1425789Sahrens cb.cb_recurse = 1; 1426789Sahrens break; 1427789Sahrens case 'R': 1428789Sahrens cb.cb_recurse = 1; 1429789Sahrens cb.cb_doclones = 1; 1430789Sahrens break; 1431789Sahrens case '?': 1432789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1433789Sahrens optopt); 1434789Sahrens usage(FALSE); 1435789Sahrens } 1436789Sahrens } 1437789Sahrens 1438789Sahrens argc -= optind; 1439789Sahrens argv += optind; 1440789Sahrens 1441789Sahrens /* check number of arguments */ 1442789Sahrens if (argc < 1) { 1443789Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 1444789Sahrens usage(FALSE); 1445789Sahrens } 1446789Sahrens if (argc > 1) { 1447789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 1448789Sahrens usage(FALSE); 1449789Sahrens } 1450789Sahrens 1451789Sahrens /* open the snapshot */ 14521294Slling if ((snap = zfs_open(argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1453789Sahrens return (1); 1454789Sahrens 14551294Slling /* open the parent dataset */ 14561294Slling (void) strlcpy(parentname, argv[0], sizeof (parentname)); 1457789Sahrens verify((delim = strrchr(parentname, '@')) != NULL); 1458789Sahrens *delim = '\0'; 1459789Sahrens if ((zhp = zfs_open(parentname, ZFS_TYPE_ANY)) == NULL) { 1460789Sahrens zfs_close(snap); 1461789Sahrens return (1); 1462789Sahrens } 1463789Sahrens 1464789Sahrens /* 1465789Sahrens * Check for more recent snapshots and/or clones based on the presence 1466789Sahrens * of '-r' and '-R'. 1467789Sahrens */ 14681294Slling cb.cb_target = argv[0]; 14691294Slling cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 1470789Sahrens cb.cb_first = 1; 1471789Sahrens cb.cb_error = 0; 1472789Sahrens (void) zfs_iter_children(zhp, rollback_check, &cb); 1473789Sahrens 1474789Sahrens if ((ret = cb.cb_error) != 0) 1475789Sahrens goto out; 1476789Sahrens 1477789Sahrens /* 14781294Slling * Rollback parent to the given snapshot. 1479789Sahrens */ 14801294Slling ret = zfs_rollback(zhp, snap, force); 1481789Sahrens 1482789Sahrens out: 1483789Sahrens zfs_close(snap); 1484789Sahrens zfs_close(zhp); 1485789Sahrens 1486789Sahrens if (ret == 0) 1487789Sahrens return (0); 1488789Sahrens else 1489789Sahrens return (1); 1490789Sahrens } 1491789Sahrens 1492789Sahrens /* 1493789Sahrens * zfs set property=value { fs | snap | vol } ... 1494789Sahrens * 1495789Sahrens * Sets the given property for all datasets specified on the command line. 1496789Sahrens */ 1497789Sahrens typedef struct set_cbdata { 1498789Sahrens char *cb_propname; 1499789Sahrens char *cb_value; 1500789Sahrens zfs_prop_t cb_prop; 1501789Sahrens } set_cbdata_t; 1502789Sahrens 1503789Sahrens static int 1504789Sahrens set_callback(zfs_handle_t *zhp, void *data) 1505789Sahrens { 1506789Sahrens set_cbdata_t *cbp = data; 1507789Sahrens int ret = 1; 1508789Sahrens 1509789Sahrens /* don't allow setting of properties for snapshots */ 1510789Sahrens if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { 1511789Sahrens (void) fprintf(stderr, gettext("cannot set %s property for " 1512789Sahrens "'%s': snapshot properties cannot be modified\n"), 1513789Sahrens cbp->cb_propname, zfs_get_name(zhp)); 1514789Sahrens return (1); 1515789Sahrens } 1516789Sahrens 1517789Sahrens /* 15181133Seschrock * If we're changing the volsize, make sure the value is appropriate, 15191133Seschrock * and set the reservation if this is a non-sparse volume. 1520789Sahrens */ 1521789Sahrens if (cbp->cb_prop == ZFS_PROP_VOLSIZE && 15221133Seschrock zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 1523789Sahrens uint64_t volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1524789Sahrens uint64_t avail = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE); 15251133Seschrock uint64_t reservation = zfs_prop_get_int(zhp, 15261133Seschrock ZFS_PROP_RESERVATION); 15271133Seschrock uint64_t blocksize = zfs_prop_get_int(zhp, 15281133Seschrock ZFS_PROP_VOLBLOCKSIZE); 1529789Sahrens uint64_t value; 1530789Sahrens 1531789Sahrens verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); 1532789Sahrens 15331133Seschrock if (value % blocksize != 0) { 15341133Seschrock char buf[64]; 15351133Seschrock 15361133Seschrock zfs_nicenum(blocksize, buf, sizeof (buf)); 15371133Seschrock (void) fprintf(stderr, gettext("cannot set %s for " 15381133Seschrock "'%s': must be a multiple of volume block size " 15391133Seschrock "(%s)\n"), cbp->cb_propname, zfs_get_name(zhp), 15401133Seschrock buf); 1541789Sahrens return (1); 1542789Sahrens } 1543789Sahrens 15441133Seschrock if (value == 0) { 15451133Seschrock (void) fprintf(stderr, gettext("cannot set %s for " 15461133Seschrock "'%s': cannot be zero\n"), cbp->cb_propname, 15471133Seschrock zfs_get_name(zhp)); 1548789Sahrens return (1); 1549789Sahrens } 15501133Seschrock 15511133Seschrock if (volsize == reservation) { 15521133Seschrock if (value > volsize && (value - volsize) > avail) { 15531133Seschrock (void) fprintf(stderr, gettext("cannot set " 15541133Seschrock "%s property for '%s': volume size exceeds " 15551133Seschrock "amount of available space\n"), 15561133Seschrock cbp->cb_propname, zfs_get_name(zhp)); 15571133Seschrock return (1); 15581133Seschrock } 15591133Seschrock 15601133Seschrock if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, 15611133Seschrock cbp->cb_value) != 0) { 15621133Seschrock (void) fprintf(stderr, gettext("volsize and " 15631133Seschrock "reservation must remain equal\n")); 15641133Seschrock return (1); 15651133Seschrock } 15661133Seschrock } 1567789Sahrens } 1568789Sahrens 1569789Sahrens /* 1570789Sahrens * Do not allow the reservation to be set above the volume size. We do 1571789Sahrens * this here instead of inside libzfs because libzfs violates this rule 1572789Sahrens * internally. 1573789Sahrens */ 1574789Sahrens if (cbp->cb_prop == ZFS_PROP_RESERVATION && 1575789Sahrens zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 1576789Sahrens uint64_t value; 1577789Sahrens uint64_t volsize; 1578789Sahrens 1579789Sahrens volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1580789Sahrens if (strcmp(cbp->cb_value, "none") == 0) 1581789Sahrens value = 0; 1582789Sahrens else 1583789Sahrens verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); 1584789Sahrens 1585789Sahrens if (value > volsize) { 1586789Sahrens (void) fprintf(stderr, gettext("cannot set %s " 1587789Sahrens "for '%s': size is greater than current " 1588789Sahrens "volume size\n"), cbp->cb_propname, 1589789Sahrens zfs_get_name(zhp)); 1590789Sahrens return (-1); 1591789Sahrens } 1592789Sahrens } 1593789Sahrens 1594789Sahrens if (zfs_prop_set(zhp, cbp->cb_prop, cbp->cb_value) != 0) 1595789Sahrens return (1); 1596789Sahrens 1597789Sahrens ret = 0; 1598789Sahrens error: 1599789Sahrens return (ret); 1600789Sahrens } 1601789Sahrens 1602789Sahrens static int 1603789Sahrens zfs_do_set(int argc, char **argv) 1604789Sahrens { 1605789Sahrens set_cbdata_t cb; 1606789Sahrens 1607789Sahrens /* check for options */ 1608789Sahrens if (argc > 1 && argv[1][0] == '-') { 1609789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1610789Sahrens argv[1][1]); 1611789Sahrens usage(FALSE); 1612789Sahrens } 1613789Sahrens 1614789Sahrens /* check number of arguments */ 1615789Sahrens if (argc < 2) { 1616789Sahrens (void) fprintf(stderr, gettext("missing property=value " 1617789Sahrens "argument\n")); 1618789Sahrens usage(FALSE); 1619789Sahrens } 1620789Sahrens if (argc < 3) { 1621789Sahrens (void) fprintf(stderr, gettext("missing dataset name\n")); 1622789Sahrens usage(FALSE); 1623789Sahrens } 1624789Sahrens 1625789Sahrens /* validate property=value argument */ 1626789Sahrens cb.cb_propname = argv[1]; 1627789Sahrens if ((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) { 1628789Sahrens (void) fprintf(stderr, gettext("missing value in " 1629789Sahrens "property=value argument\n")); 1630789Sahrens usage(FALSE); 1631789Sahrens } 1632789Sahrens 1633789Sahrens *cb.cb_value = '\0'; 1634789Sahrens cb.cb_value++; 1635789Sahrens 1636789Sahrens if (*cb.cb_propname == '\0') { 1637789Sahrens (void) fprintf(stderr, 1638789Sahrens gettext("missing property in property=value argument\n")); 1639789Sahrens usage(FALSE); 1640789Sahrens } 1641789Sahrens if (*cb.cb_value == '\0') { 1642789Sahrens (void) fprintf(stderr, 1643789Sahrens gettext("missing value in property=value argument\n")); 1644789Sahrens usage(FALSE); 1645789Sahrens } 1646789Sahrens 1647789Sahrens /* get the property type */ 1648789Sahrens if ((cb.cb_prop = zfs_name_to_prop(cb.cb_propname)) == 1649789Sahrens ZFS_PROP_INVAL) { 1650789Sahrens (void) fprintf(stderr, 1651789Sahrens gettext("invalid property '%s'\n"), cb.cb_propname); 1652789Sahrens usage(FALSE); 1653789Sahrens } 1654789Sahrens 1655789Sahrens /* 1656789Sahrens * Validate that the value is appropriate for this property. We do this 1657789Sahrens * once now so we don't generate multiple errors each time we try to 1658789Sahrens * apply it to a dataset. 1659789Sahrens */ 1660789Sahrens if (zfs_prop_validate(cb.cb_prop, cb.cb_value, NULL) != 0) 1661789Sahrens return (1); 1662789Sahrens 1663789Sahrens return (zfs_for_each(argc - 2, argv + 2, FALSE, 1664789Sahrens ZFS_TYPE_ANY, set_callback, &cb)); 1665789Sahrens } 1666789Sahrens 1667789Sahrens /* 1668789Sahrens * zfs snapshot <fs@snap> 1669789Sahrens * 1670789Sahrens * Creates a snapshot with the given name. While functionally equivalent to 1671789Sahrens * 'zfs create', it is a separate command to diffferentiate intent. 1672789Sahrens */ 1673789Sahrens static int 1674789Sahrens zfs_do_snapshot(int argc, char **argv) 1675789Sahrens { 1676789Sahrens /* check options */ 1677789Sahrens if (argc > 1 && argv[1][0] == '-') { 1678789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1679789Sahrens argv[1][1]); 1680789Sahrens usage(FALSE); 1681789Sahrens } 1682789Sahrens 1683789Sahrens /* check number of arguments */ 1684789Sahrens if (argc < 2) { 1685789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 1686789Sahrens usage(FALSE); 1687789Sahrens } 1688789Sahrens if (argc > 2) { 1689789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 1690789Sahrens usage(FALSE); 1691789Sahrens } 1692789Sahrens 1693789Sahrens return (zfs_snapshot(argv[1]) != 0); 1694789Sahrens } 1695789Sahrens 1696789Sahrens /* 1697789Sahrens * zfs backup [-i <fs@snap>] <fs@snap> 1698789Sahrens * 1699789Sahrens * Send a backup stream to stdout. 1700789Sahrens */ 1701789Sahrens static int 1702789Sahrens zfs_do_backup(int argc, char **argv) 1703789Sahrens { 1704789Sahrens char *fromname = NULL; 1705789Sahrens zfs_handle_t *zhp_from = NULL, *zhp_to; 1706789Sahrens int c, err; 1707789Sahrens 1708789Sahrens /* check options */ 1709789Sahrens while ((c = getopt(argc, argv, ":i:")) != -1) { 1710789Sahrens switch (c) { 1711789Sahrens case 'i': 1712789Sahrens fromname = optarg; 1713789Sahrens break; 1714789Sahrens case ':': 1715789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1716789Sahrens "'%c' option\n"), optopt); 1717789Sahrens usage(FALSE); 1718789Sahrens break; 1719789Sahrens case '?': 1720789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1721789Sahrens optopt); 1722789Sahrens usage(FALSE); 1723789Sahrens } 1724789Sahrens } 1725789Sahrens 1726789Sahrens argc -= optind; 1727789Sahrens argv += optind; 1728789Sahrens 1729789Sahrens /* check number of arguments */ 1730789Sahrens if (argc < 1) { 1731789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 1732789Sahrens usage(FALSE); 1733789Sahrens } 1734789Sahrens if (argc > 1) { 1735789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 1736789Sahrens usage(FALSE); 1737789Sahrens } 1738789Sahrens 1739789Sahrens if (isatty(STDOUT_FILENO)) { 1740789Sahrens (void) fprintf(stderr, 1741789Sahrens gettext("Error: Backup stream can not be written " 1742789Sahrens "to a terminal.\n" 1743789Sahrens "You must redirect standard output.\n")); 1744789Sahrens return (1); 1745789Sahrens } 1746789Sahrens 1747789Sahrens if (fromname) { 1748789Sahrens if ((zhp_from = zfs_open(fromname, ZFS_TYPE_SNAPSHOT)) == NULL) 1749789Sahrens return (1); 1750789Sahrens } 1751789Sahrens if ((zhp_to = zfs_open(argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1752789Sahrens return (1); 1753789Sahrens 1754789Sahrens err = zfs_backup(zhp_to, zhp_from); 1755789Sahrens 1756789Sahrens if (zhp_from) 1757789Sahrens zfs_close(zhp_from); 1758789Sahrens zfs_close(zhp_to); 1759789Sahrens 1760789Sahrens return (err != 0); 1761789Sahrens } 1762789Sahrens 1763789Sahrens /* 1764789Sahrens * zfs restore <fs@snap> 1765789Sahrens * 1766789Sahrens * Restore a backup stream from stdin. 1767789Sahrens */ 1768789Sahrens static int 1769789Sahrens zfs_do_restore(int argc, char **argv) 1770789Sahrens { 1771789Sahrens int c, err; 1772789Sahrens int isprefix = FALSE; 1773789Sahrens int dryrun = FALSE; 1774789Sahrens int verbose = FALSE; 1775789Sahrens 1776789Sahrens /* check options */ 1777789Sahrens while ((c = getopt(argc, argv, ":dnv")) != -1) { 1778789Sahrens switch (c) { 1779789Sahrens case 'd': 1780789Sahrens isprefix = TRUE; 1781789Sahrens break; 1782789Sahrens case 'n': 1783789Sahrens dryrun = TRUE; 1784789Sahrens break; 1785789Sahrens case 'v': 1786789Sahrens verbose = TRUE; 1787789Sahrens break; 1788789Sahrens case ':': 1789789Sahrens (void) fprintf(stderr, gettext("missing argument for " 1790789Sahrens "'%c' option\n"), optopt); 1791789Sahrens usage(FALSE); 1792789Sahrens break; 1793789Sahrens case '?': 1794789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1795789Sahrens optopt); 1796789Sahrens usage(FALSE); 1797789Sahrens } 1798789Sahrens } 1799789Sahrens 1800789Sahrens argc -= optind; 1801789Sahrens argv += optind; 1802789Sahrens 1803789Sahrens /* check number of arguments */ 1804789Sahrens if (argc < 1) { 1805789Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 1806789Sahrens usage(FALSE); 1807789Sahrens } 1808789Sahrens if (argc > 1) { 1809789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 1810789Sahrens usage(FALSE); 1811789Sahrens } 1812789Sahrens 1813789Sahrens if (isatty(STDIN_FILENO)) { 1814789Sahrens (void) fprintf(stderr, 1815789Sahrens gettext("Error: Backup stream can not be read " 1816789Sahrens "from a terminal.\n" 1817789Sahrens "You must redirect standard input.\n")); 1818789Sahrens return (1); 1819789Sahrens } 1820789Sahrens 1821789Sahrens err = zfs_restore(argv[0], isprefix, verbose, dryrun); 1822789Sahrens return (err != 0); 1823789Sahrens } 1824789Sahrens 18251356Seschrock typedef struct get_all_cbdata { 18261356Seschrock zfs_handle_t **cb_handles; 18271356Seschrock size_t cb_alloc; 18281356Seschrock size_t cb_used; 18291356Seschrock } get_all_cbdata_t; 18301356Seschrock 18311356Seschrock static int 18321356Seschrock get_one_filesystem(zfs_handle_t *zhp, void *data) 18331356Seschrock { 18341356Seschrock get_all_cbdata_t *cbp = data; 18351356Seschrock 18361356Seschrock /* 18371356Seschrock * Skip any zvols 18381356Seschrock */ 18391356Seschrock if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 18401356Seschrock zfs_close(zhp); 18411356Seschrock return (0); 18421356Seschrock } 18431356Seschrock 18441356Seschrock if (cbp->cb_alloc == cbp->cb_used) { 18451356Seschrock zfs_handle_t **handles; 18461356Seschrock 18471356Seschrock if (cbp->cb_alloc == 0) 18481356Seschrock cbp->cb_alloc = 64; 18491356Seschrock else 18501356Seschrock cbp->cb_alloc *= 2; 18511356Seschrock 18521356Seschrock handles = safe_malloc(cbp->cb_alloc * sizeof (void *)); 18531356Seschrock 18541356Seschrock if (cbp->cb_handles) { 18551356Seschrock bcopy(cbp->cb_handles, handles, 18561356Seschrock cbp->cb_used * sizeof (void *)); 18571356Seschrock free(cbp->cb_handles); 18581356Seschrock } 18591356Seschrock 18601356Seschrock cbp->cb_handles = handles; 18611356Seschrock } 18621356Seschrock 18631356Seschrock cbp->cb_handles[cbp->cb_used++] = zhp; 18641356Seschrock 18651356Seschrock return (zfs_iter_filesystems(zhp, get_one_filesystem, data)); 18661356Seschrock } 18671356Seschrock 18681356Seschrock static void 18691356Seschrock get_all_filesystems(zfs_handle_t ***fslist, size_t *count) 18701356Seschrock { 18711356Seschrock get_all_cbdata_t cb = { 0 }; 18721356Seschrock 18731356Seschrock (void) zfs_iter_root(get_one_filesystem, &cb); 18741356Seschrock 18751356Seschrock *fslist = cb.cb_handles; 18761356Seschrock *count = cb.cb_used; 18771356Seschrock } 18781356Seschrock 18791356Seschrock static int 18801356Seschrock mountpoint_compare(const void *a, const void *b) 18811356Seschrock { 18821356Seschrock zfs_handle_t **za = (zfs_handle_t **)a; 18831356Seschrock zfs_handle_t **zb = (zfs_handle_t **)b; 18841356Seschrock char mounta[MAXPATHLEN]; 18851356Seschrock char mountb[MAXPATHLEN]; 18861356Seschrock 18871356Seschrock verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta, 18881356Seschrock sizeof (mounta), NULL, NULL, 0, FALSE) == 0); 18891356Seschrock verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb, 18901356Seschrock sizeof (mountb), NULL, NULL, 0, FALSE) == 0); 18911356Seschrock 18921356Seschrock return (strcmp(mounta, mountb)); 18931356Seschrock } 1894789Sahrens 1895789Sahrens /* 1896789Sahrens * Generic callback for sharing or mounting filesystems. Because the code is so 1897789Sahrens * similar, we have a common function with an extra parameter to determine which 1898789Sahrens * mode we are using. 1899789Sahrens */ 1900789Sahrens #define OP_SHARE 0x1 1901789Sahrens #define OP_MOUNT 0x2 1902789Sahrens 1903789Sahrens typedef struct share_mount_cbdata { 1904789Sahrens int cb_type; 1905789Sahrens int cb_explicit; 1906789Sahrens int cb_flags; 1907789Sahrens const char *cb_options; 1908789Sahrens } share_mount_cbdata_t; 1909789Sahrens 1910789Sahrens /* 1911789Sahrens * Share or mount the filesystem. 1912789Sahrens */ 1913789Sahrens static int 1914789Sahrens share_mount_callback(zfs_handle_t *zhp, void *data) 1915789Sahrens { 1916789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 1917789Sahrens char shareopts[ZFS_MAXPROPLEN]; 1918789Sahrens share_mount_cbdata_t *cbp = data; 1919789Sahrens const char *cmdname = cbp->cb_type == OP_SHARE ? "share" : "mount"; 1920789Sahrens struct mnttab mnt; 1921789Sahrens uint64_t zoned; 1922789Sahrens 1923789Sahrens if (cbp->cb_options == NULL) 1924789Sahrens mnt.mnt_mntopts = ""; 1925789Sahrens else 1926789Sahrens mnt.mnt_mntopts = (char *)cbp->cb_options; 1927789Sahrens 1928789Sahrens /* 1929789Sahrens * Check to make sure we can mount/share this dataset. If we are in the 1930789Sahrens * global zone and the filesystem is exported to a local zone, or if we 1931789Sahrens * are in a local zone and the filesystem is not exported, then it is an 1932789Sahrens * error. 1933789Sahrens */ 1934789Sahrens zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 1935789Sahrens 1936789Sahrens if (zoned && getzoneid() == GLOBAL_ZONEID) { 1937789Sahrens if (!cbp->cb_explicit) 1938789Sahrens return (0); 1939789Sahrens 1940789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': dataset is " 1941789Sahrens "exported to a local zone\n"), cmdname, zfs_get_name(zhp)); 1942789Sahrens return (1); 1943789Sahrens 1944789Sahrens } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 1945789Sahrens if (!cbp->cb_explicit) 1946789Sahrens return (0); 1947789Sahrens 1948789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': permission " 1949789Sahrens "denied\n"), cmdname, zfs_get_name(zhp)); 1950789Sahrens return (1); 1951789Sahrens } 1952789Sahrens 1953789Sahrens /* 1954789Sahrens * Inore any filesystems which don't apply to us. This includes those 1955789Sahrens * with a legacy mountpoint, or those with legacy share options. 1956789Sahrens */ 1957789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 1958789Sahrens sizeof (mountpoint), NULL, NULL, 0, FALSE) == 0); 1959789Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 1960789Sahrens sizeof (shareopts), NULL, NULL, 0, FALSE) == 0); 1961789Sahrens 1962789Sahrens if (cbp->cb_type == OP_SHARE) { 1963789Sahrens if (strcmp(shareopts, "off") == 0) { 1964789Sahrens if (!cbp->cb_explicit) 1965789Sahrens return (0); 1966789Sahrens 1967789Sahrens (void) fprintf(stderr, gettext("cannot share '%s': " 1968789Sahrens "legacy share\n"), zfs_get_name(zhp)); 1969789Sahrens (void) fprintf(stderr, gettext("use share(1M) to " 1970789Sahrens "share this filesystem\n")); 1971789Sahrens return (1); 1972789Sahrens } 1973789Sahrens } 1974789Sahrens 1975789Sahrens /* 1976789Sahrens * We cannot share or mount legacy filesystems. If the shareopts is 1977789Sahrens * non-legacy but the mountpoint is legacy, we treat it as a legacy 1978789Sahrens * share. 1979789Sahrens */ 1980789Sahrens if (strcmp(mountpoint, "legacy") == 0) { 1981789Sahrens if (!cbp->cb_explicit) 1982789Sahrens return (0); 1983789Sahrens 1984789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': " 1985789Sahrens "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 1986789Sahrens (void) fprintf(stderr, gettext("use %s to " 1987789Sahrens "%s this filesystem\n"), cbp->cb_type == OP_SHARE ? 1988789Sahrens "share(1M)" : "mount(1M)", cmdname); 1989789Sahrens return (1); 1990789Sahrens } 1991789Sahrens 1992789Sahrens if (strcmp(mountpoint, "none") == 0) { 1993789Sahrens if (!cbp->cb_explicit) 1994789Sahrens return (0); 1995789Sahrens 1996789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': no " 1997789Sahrens "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 1998789Sahrens return (1); 1999789Sahrens } 2000789Sahrens 2001789Sahrens /* 2002789Sahrens * At this point, we have verified that the mountpoint and/or shareopts 2003789Sahrens * are appropriate for auto management. Determine if the filesystem is 2004789Sahrens * currently mounted or shared, and abort if this is an explicit 2005789Sahrens * request. 2006789Sahrens */ 2007789Sahrens switch (cbp->cb_type) { 2008789Sahrens case OP_SHARE: 2009789Sahrens if (zfs_is_shared(zhp, NULL)) { 2010789Sahrens if (cbp->cb_explicit) { 2011789Sahrens (void) fprintf(stderr, gettext("cannot share " 2012789Sahrens "'%s': filesystem already shared\n"), 2013789Sahrens zfs_get_name(zhp)); 2014789Sahrens return (1); 2015789Sahrens } else { 2016789Sahrens return (0); 2017789Sahrens } 2018789Sahrens } 2019789Sahrens break; 2020789Sahrens 2021789Sahrens case OP_MOUNT: 2022789Sahrens if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 2023789Sahrens zfs_is_mounted(zhp, NULL)) { 2024789Sahrens if (cbp->cb_explicit) { 2025789Sahrens (void) fprintf(stderr, gettext("cannot mount " 2026789Sahrens "'%s': filesystem already mounted\n"), 2027789Sahrens zfs_get_name(zhp)); 2028789Sahrens return (1); 2029789Sahrens } else { 2030789Sahrens return (0); 2031789Sahrens } 2032789Sahrens } 2033789Sahrens break; 2034789Sahrens } 2035789Sahrens 2036789Sahrens /* 2037789Sahrens * Mount and optionally share the filesystem. 2038789Sahrens */ 2039789Sahrens switch (cbp->cb_type) { 2040789Sahrens case OP_SHARE: 2041789Sahrens { 2042789Sahrens if (!zfs_is_mounted(zhp, NULL) && 2043789Sahrens zfs_mount(zhp, NULL, 0) != 0) 2044789Sahrens return (1); 2045789Sahrens 2046789Sahrens if (zfs_share(zhp) != 0) 2047789Sahrens return (1); 2048789Sahrens } 2049789Sahrens break; 2050789Sahrens 2051789Sahrens case OP_MOUNT: 2052789Sahrens if (zfs_mount(zhp, cbp->cb_options, cbp->cb_flags) != 0) 2053789Sahrens return (1); 2054789Sahrens break; 2055789Sahrens } 2056789Sahrens 2057789Sahrens return (0); 2058789Sahrens } 2059789Sahrens 2060789Sahrens static int 2061789Sahrens share_or_mount(int type, int argc, char **argv) 2062789Sahrens { 2063789Sahrens int do_all = 0; 2064789Sahrens int c, ret; 2065789Sahrens share_mount_cbdata_t cb = { 0 }; 2066789Sahrens 2067789Sahrens cb.cb_type = type; 2068789Sahrens 2069789Sahrens /* check options */ 2070789Sahrens while ((c = getopt(argc, argv, type == OP_MOUNT ? ":ao:O" : "a")) 2071789Sahrens != -1) { 2072789Sahrens switch (c) { 2073789Sahrens case 'a': 2074789Sahrens do_all = 1; 2075789Sahrens break; 2076789Sahrens case 'o': 2077789Sahrens cb.cb_options = optarg; 2078789Sahrens break; 2079789Sahrens case 'O': 2080789Sahrens cb.cb_flags |= MS_OVERLAY; 2081789Sahrens break; 2082789Sahrens case ':': 2083789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2084789Sahrens "'%c' option\n"), optopt); 2085789Sahrens usage(FALSE); 2086789Sahrens break; 2087789Sahrens case '?': 2088789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2089789Sahrens optopt); 2090789Sahrens usage(FALSE); 2091789Sahrens } 2092789Sahrens } 2093789Sahrens 2094789Sahrens argc -= optind; 2095789Sahrens argv += optind; 2096789Sahrens 2097789Sahrens /* check number of arguments */ 2098789Sahrens if (do_all) { 20991356Seschrock zfs_handle_t **fslist = NULL; 21001356Seschrock size_t i, count = 0; 21011356Seschrock 2102789Sahrens if (argc != 0) { 2103789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2104789Sahrens usage(FALSE); 2105789Sahrens } 2106789Sahrens 21071356Seschrock get_all_filesystems(&fslist, &count); 21081356Seschrock 21091356Seschrock if (count == 0) 21101356Seschrock return (0); 21111356Seschrock 21121356Seschrock qsort(fslist, count, sizeof (void *), mountpoint_compare); 21131356Seschrock 21141356Seschrock for (i = 0; i < count; i++) { 21151356Seschrock if ((ret = share_mount_callback(fslist[i], &cb)) != 0) 21161356Seschrock break; 21171356Seschrock } 21181356Seschrock 21191356Seschrock for (i = 0; i < count; i++) 21201356Seschrock zfs_close(fslist[i]); 21211356Seschrock 21221356Seschrock free(fslist); 2123789Sahrens } else if (argc == 0) { 2124789Sahrens struct mnttab entry; 2125789Sahrens 2126789Sahrens if (type == OP_SHARE) { 2127789Sahrens (void) fprintf(stderr, gettext("missing filesystem " 2128789Sahrens "argument\n")); 2129789Sahrens usage(FALSE); 2130789Sahrens } 2131789Sahrens 2132789Sahrens /* 2133789Sahrens * When mount is given no arguments, go through /etc/mnttab and 2134789Sahrens * display any active ZFS mounts. We hide any snapshots, since 2135789Sahrens * they are controlled automatically. 2136789Sahrens */ 2137789Sahrens rewind(mnttab_file); 2138789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2139789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 2140789Sahrens strchr(entry.mnt_special, '@') != NULL) 2141789Sahrens continue; 2142789Sahrens 2143789Sahrens (void) printf("%-30s %s\n", entry.mnt_special, 2144789Sahrens entry.mnt_mountp); 2145789Sahrens } 2146789Sahrens 2147789Sahrens ret = 0; 2148789Sahrens } else { 2149789Sahrens zfs_handle_t *zhp; 2150789Sahrens 2151789Sahrens if (argc > 1) { 2152789Sahrens (void) fprintf(stderr, 2153789Sahrens gettext("too many arguments\n")); 2154789Sahrens usage(FALSE); 2155789Sahrens } 2156789Sahrens 2157789Sahrens if ((zhp = zfs_open(argv[0], ZFS_TYPE_FILESYSTEM)) == NULL) 2158789Sahrens ret = 1; 2159789Sahrens else { 2160789Sahrens cb.cb_explicit = TRUE; 2161789Sahrens ret = share_mount_callback(zhp, &cb); 2162789Sahrens zfs_close(zhp); 2163789Sahrens } 2164789Sahrens } 2165789Sahrens 2166789Sahrens return (ret); 2167789Sahrens } 2168789Sahrens 2169789Sahrens /* 2170789Sahrens * zfs mount -a 2171789Sahrens * zfs mount filesystem 2172789Sahrens * 2173789Sahrens * Mount all filesystems, or mount the given filesystem. 2174789Sahrens */ 2175789Sahrens static int 2176789Sahrens zfs_do_mount(int argc, char **argv) 2177789Sahrens { 2178789Sahrens return (share_or_mount(OP_MOUNT, argc, argv)); 2179789Sahrens } 2180789Sahrens 2181789Sahrens /* 2182789Sahrens * zfs share -a 2183789Sahrens * zfs share filesystem 2184789Sahrens * 2185789Sahrens * Share all filesystems, or share the given filesystem. 2186789Sahrens */ 2187789Sahrens static int 2188789Sahrens zfs_do_share(int argc, char **argv) 2189789Sahrens { 2190789Sahrens return (share_or_mount(OP_SHARE, argc, argv)); 2191789Sahrens } 2192789Sahrens 2193789Sahrens typedef struct unshare_unmount_node { 2194789Sahrens zfs_handle_t *un_zhp; 2195789Sahrens char *un_mountp; 2196789Sahrens uu_avl_node_t un_avlnode; 2197789Sahrens } unshare_unmount_node_t; 2198789Sahrens 2199789Sahrens /* ARGSUSED */ 2200789Sahrens static int 2201789Sahrens unshare_unmount_compare(const void *larg, const void *rarg, void *unused) 2202789Sahrens { 2203789Sahrens const unshare_unmount_node_t *l = larg; 2204789Sahrens const unshare_unmount_node_t *r = rarg; 2205789Sahrens 2206789Sahrens return (strcmp(l->un_mountp, r->un_mountp)); 2207789Sahrens } 2208789Sahrens 2209789Sahrens /* 2210789Sahrens * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 2211789Sahrens * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 2212789Sahrens * and unmount it appropriately. 2213789Sahrens */ 2214789Sahrens static int 2215789Sahrens unshare_unmount_path(int type, char *path, int flags, int is_manual) 2216789Sahrens { 2217789Sahrens zfs_handle_t *zhp; 2218789Sahrens int ret; 2219789Sahrens struct stat64 statbuf; 2220789Sahrens struct extmnttab entry; 2221789Sahrens const char *cmdname = (type == OP_SHARE) ? "unshare" : "unmount"; 2222789Sahrens char property[ZFS_MAXPROPLEN]; 2223789Sahrens 2224789Sahrens /* 2225789Sahrens * Search for the path in /etc/mnttab. Rather than looking for the 2226789Sahrens * specific path, which can be fooled by non-standard paths (i.e. ".." 2227789Sahrens * or "//"), we stat() the path and search for the corresponding 2228789Sahrens * (major,minor) device pair. 2229789Sahrens */ 2230789Sahrens if (stat64(path, &statbuf) != 0) { 2231789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 2232789Sahrens cmdname, path, strerror(errno)); 2233789Sahrens return (1); 2234789Sahrens } 2235789Sahrens 2236789Sahrens /* 2237789Sahrens * Search for the given (major,minor) pair in the mount table. 2238789Sahrens */ 2239789Sahrens rewind(mnttab_file); 2240789Sahrens while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 2241789Sahrens if (entry.mnt_major == major(statbuf.st_dev) && 2242789Sahrens entry.mnt_minor == minor(statbuf.st_dev)) 2243789Sahrens break; 2244789Sahrens } 2245789Sahrens if (ret != 0) { 2246789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not " 2247789Sahrens "currently mounted\n"), cmdname, path); 2248789Sahrens return (1); 2249789Sahrens } 2250789Sahrens 2251789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 2252789Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 2253789Sahrens "filesystem\n"), cmdname, path); 2254789Sahrens return (1); 2255789Sahrens } 2256789Sahrens 2257789Sahrens if ((zhp = zfs_open(entry.mnt_special, ZFS_TYPE_FILESYSTEM)) == NULL) 2258789Sahrens return (1); 2259789Sahrens 2260789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2261789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 2262789Sahrens sizeof (property), NULL, NULL, 0, FALSE) == 0); 2263789Sahrens 2264789Sahrens if (type == OP_SHARE) { 2265789Sahrens if (strcmp(property, "off") == 0) { 2266789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2267789Sahrens "'%s': legacy share\n"), path); 2268789Sahrens (void) fprintf(stderr, gettext("use " 2269789Sahrens "unshare(1M) to unshare this filesystem\n")); 2270789Sahrens ret = 1; 2271789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2272789Sahrens (void) fprintf(stderr, gettext("cannot unshare '%s': " 2273789Sahrens "not currently shared\n"), path); 2274789Sahrens ret = 1; 2275789Sahrens } else { 2276789Sahrens ret = zfs_unshareall(zhp); 2277789Sahrens } 2278789Sahrens } else { 22791264Slling if (is_manual) { 22801264Slling ret = zfs_unmount(zhp, NULL, flags); 22811264Slling } else if (strcmp(property, "legacy") == 0) { 22821264Slling (void) fprintf(stderr, gettext("cannot unmount " 22831264Slling "'%s': legacy mountpoint\n"), 22841264Slling zfs_get_name(zhp)); 22851264Slling (void) fprintf(stderr, gettext("use umount(1M) " 22861264Slling "to unmount this filesystem\n")); 22871264Slling ret = 1; 2288789Sahrens } else { 2289789Sahrens ret = zfs_unmountall(zhp, flags); 2290789Sahrens } 2291789Sahrens } 2292789Sahrens 2293789Sahrens zfs_close(zhp); 2294789Sahrens 2295789Sahrens return (ret != 0); 2296789Sahrens } 2297789Sahrens 2298789Sahrens /* 2299789Sahrens * Generic callback for unsharing or unmounting a filesystem. 2300789Sahrens */ 2301789Sahrens static int 2302789Sahrens unshare_unmount(int type, int argc, char **argv) 2303789Sahrens { 2304789Sahrens int do_all = 0; 2305789Sahrens int flags = 0; 2306789Sahrens int ret = 0; 2307789Sahrens int c; 2308789Sahrens zfs_handle_t *zhp; 2309789Sahrens char property[ZFS_MAXPROPLEN]; 2310789Sahrens 2311789Sahrens /* check options */ 2312789Sahrens while ((c = getopt(argc, argv, type == OP_SHARE ? "a" : "af")) != -1) { 2313789Sahrens switch (c) { 2314789Sahrens case 'a': 2315789Sahrens do_all = 1; 2316789Sahrens break; 2317789Sahrens case 'f': 2318789Sahrens flags = MS_FORCE; 2319789Sahrens break; 2320789Sahrens case '?': 2321789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2322789Sahrens optopt); 2323789Sahrens usage(FALSE); 2324789Sahrens } 2325789Sahrens } 2326789Sahrens 2327789Sahrens argc -= optind; 2328789Sahrens argv += optind; 2329789Sahrens 2330789Sahrens /* ensure correct number of arguments */ 2331789Sahrens if (do_all) { 2332789Sahrens if (argc != 0) { 2333789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2334789Sahrens usage(FALSE); 2335789Sahrens } 2336789Sahrens } else if (argc != 1) { 2337789Sahrens if (argc == 0) 2338789Sahrens (void) fprintf(stderr, 2339789Sahrens gettext("missing filesystem argument\n")); 2340789Sahrens else 2341789Sahrens (void) fprintf(stderr, 2342789Sahrens gettext("too many arguments\n")); 2343789Sahrens usage(FALSE); 2344789Sahrens } 2345789Sahrens 2346789Sahrens if (do_all) { 2347789Sahrens /* 2348789Sahrens * We could make use of zfs_for_each() to walk all datasets in 2349789Sahrens * the system, but this would be very inefficient, especially 2350789Sahrens * since we would have to linearly search /etc/mnttab for each 2351789Sahrens * one. Instead, do one pass through /etc/mnttab looking for 2352789Sahrens * zfs entries and call zfs_unmount() for each one. 2353789Sahrens * 2354789Sahrens * Things get a little tricky if the administrator has created 2355789Sahrens * mountpoints beneath other ZFS filesystems. In this case, we 2356789Sahrens * have to unmount the deepest filesystems first. To accomplish 2357789Sahrens * this, we place all the mountpoints in an AVL tree sorted by 2358789Sahrens * the special type (dataset name), and walk the result in 2359789Sahrens * reverse to make sure to get any snapshots first. 2360789Sahrens */ 2361789Sahrens struct mnttab entry; 2362789Sahrens uu_avl_pool_t *pool; 2363789Sahrens uu_avl_t *tree; 2364789Sahrens unshare_unmount_node_t *node; 2365789Sahrens uu_avl_index_t idx; 2366789Sahrens uu_avl_walk_t *walk; 2367789Sahrens 2368789Sahrens if ((pool = uu_avl_pool_create("unmount_pool", 2369789Sahrens sizeof (unshare_unmount_node_t), 2370789Sahrens offsetof(unshare_unmount_node_t, un_avlnode), 2371789Sahrens unshare_unmount_compare, 2372789Sahrens UU_DEFAULT)) == NULL) { 2373789Sahrens (void) fprintf(stderr, gettext("internal error: " 2374789Sahrens "out of memory\n")); 2375789Sahrens exit(1); 2376789Sahrens } 2377789Sahrens 2378789Sahrens if ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL) { 2379789Sahrens (void) fprintf(stderr, gettext("internal error: " 2380789Sahrens "out of memory\n")); 2381789Sahrens exit(1); 2382789Sahrens } 2383789Sahrens 2384789Sahrens rewind(mnttab_file); 2385789Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2386789Sahrens 2387789Sahrens /* ignore non-ZFS entries */ 2388789Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 2389789Sahrens continue; 2390789Sahrens 2391789Sahrens /* ignore snapshots */ 2392789Sahrens if (strchr(entry.mnt_special, '@') != NULL) 2393789Sahrens continue; 2394789Sahrens 2395789Sahrens if ((zhp = zfs_open(entry.mnt_special, 2396789Sahrens ZFS_TYPE_FILESYSTEM)) == NULL) { 2397789Sahrens ret = 1; 2398789Sahrens continue; 2399789Sahrens } 2400789Sahrens 2401789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2402789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 2403789Sahrens property, sizeof (property), NULL, NULL, 2404789Sahrens 0, FALSE) == 0); 2405789Sahrens 2406789Sahrens /* Ignore legacy mounts and shares */ 2407789Sahrens if ((type == OP_SHARE && 2408789Sahrens strcmp(property, "off") == 0) || 2409789Sahrens (type == OP_MOUNT && 2410789Sahrens strcmp(property, "legacy") == 0)) { 2411789Sahrens zfs_close(zhp); 2412789Sahrens continue; 2413789Sahrens } 2414789Sahrens 2415789Sahrens node = safe_malloc(sizeof (unshare_unmount_node_t)); 2416789Sahrens node->un_zhp = zhp; 2417789Sahrens 2418789Sahrens if ((node->un_mountp = strdup(entry.mnt_mountp)) == 2419789Sahrens NULL) { 2420789Sahrens (void) fprintf(stderr, gettext("internal error:" 2421789Sahrens " out of memory\n")); 2422789Sahrens exit(1); 2423789Sahrens } 2424789Sahrens 2425789Sahrens uu_avl_node_init(node, &node->un_avlnode, pool); 2426789Sahrens 2427789Sahrens if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 2428789Sahrens uu_avl_insert(tree, node, idx); 2429789Sahrens } else { 2430789Sahrens zfs_close(node->un_zhp); 2431789Sahrens free(node->un_mountp); 2432789Sahrens free(node); 2433789Sahrens } 2434789Sahrens } 2435789Sahrens 2436789Sahrens /* 2437789Sahrens * Walk the AVL tree in reverse, unmounting each filesystem and 2438789Sahrens * removing it from the AVL tree in the process. 2439789Sahrens */ 2440789Sahrens if ((walk = uu_avl_walk_start(tree, 2441789Sahrens UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) { 2442789Sahrens (void) fprintf(stderr, 2443789Sahrens gettext("internal error: out of memory")); 2444789Sahrens exit(1); 2445789Sahrens } 2446789Sahrens 2447789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 2448789Sahrens uu_avl_remove(tree, node); 2449789Sahrens 2450789Sahrens switch (type) { 2451789Sahrens case OP_SHARE: 2452789Sahrens if (zfs_unshare(node->un_zhp, 2453789Sahrens node->un_mountp) != 0) 2454789Sahrens ret = 1; 2455789Sahrens break; 2456789Sahrens 2457789Sahrens case OP_MOUNT: 2458789Sahrens if (zfs_unmount(node->un_zhp, 2459789Sahrens node->un_mountp, flags) != 0) 2460789Sahrens ret = 1; 2461789Sahrens break; 2462789Sahrens } 2463789Sahrens 2464789Sahrens zfs_close(node->un_zhp); 2465789Sahrens free(node->un_mountp); 2466789Sahrens free(node); 2467789Sahrens } 2468789Sahrens 2469789Sahrens uu_avl_walk_end(walk); 2470789Sahrens uu_avl_destroy(tree); 2471789Sahrens uu_avl_pool_destroy(pool); 2472789Sahrens } else { 2473789Sahrens /* 2474789Sahrens * We have an argument, but it may be a full path or a ZFS 2475789Sahrens * filesystem. Pass full paths off to unmount_path() (shared by 2476789Sahrens * manual_unmount), otherwise open the filesystem and pass to 2477789Sahrens * zfs_unmount(). 2478789Sahrens */ 2479789Sahrens if (argv[0][0] == '/') 2480789Sahrens return (unshare_unmount_path(type, argv[0], 2481789Sahrens flags, FALSE)); 2482789Sahrens 2483789Sahrens if ((zhp = zfs_open(argv[0], ZFS_TYPE_FILESYSTEM)) == NULL) 2484789Sahrens return (1); 2485789Sahrens 2486789Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2487789Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 2488789Sahrens sizeof (property), NULL, NULL, 0, FALSE) == 0); 2489789Sahrens 2490789Sahrens switch (type) { 2491789Sahrens case OP_SHARE: 2492789Sahrens if (strcmp(property, "off") == 0) { 2493789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2494789Sahrens "'%s': legacy share\n"), zfs_get_name(zhp)); 2495789Sahrens (void) fprintf(stderr, gettext("use unshare(1M)" 2496789Sahrens " to unshare this filesystem\n")); 2497789Sahrens ret = 1; 2498789Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2499789Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2500789Sahrens "'%s': not currently shared\n"), 2501789Sahrens zfs_get_name(zhp)); 2502789Sahrens ret = 1; 2503789Sahrens } else if (zfs_unshareall(zhp) != 0) { 2504789Sahrens ret = 1; 2505789Sahrens } 2506789Sahrens break; 2507789Sahrens 2508789Sahrens case OP_MOUNT: 2509789Sahrens if (strcmp(property, "legacy") == 0) { 2510789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2511789Sahrens "'%s': legacy mountpoint\n"), 2512789Sahrens zfs_get_name(zhp)); 2513789Sahrens (void) fprintf(stderr, gettext("use umount(1M) " 2514789Sahrens "to unmount this filesystem\n")); 2515789Sahrens ret = 1; 2516789Sahrens } else if (!zfs_is_mounted(zhp, NULL)) { 2517789Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2518789Sahrens "'%s': not currently mounted\n"), 2519789Sahrens zfs_get_name(zhp)); 2520789Sahrens ret = 1; 2521789Sahrens } else if (zfs_unmountall(zhp, flags) != 0) { 2522789Sahrens ret = 1; 2523789Sahrens } 2524789Sahrens } 2525789Sahrens 2526789Sahrens zfs_close(zhp); 2527789Sahrens } 2528789Sahrens 2529789Sahrens return (ret); 2530789Sahrens } 2531789Sahrens 2532789Sahrens /* 2533789Sahrens * zfs unmount -a 2534789Sahrens * zfs unmount filesystem 2535789Sahrens * 2536789Sahrens * Unmount all filesystems, or a specific ZFS filesystem. 2537789Sahrens */ 2538789Sahrens static int 2539789Sahrens zfs_do_unmount(int argc, char **argv) 2540789Sahrens { 2541789Sahrens return (unshare_unmount(OP_MOUNT, argc, argv)); 2542789Sahrens } 2543789Sahrens 2544789Sahrens /* 2545789Sahrens * zfs unshare -a 2546789Sahrens * zfs unshare filesystem 2547789Sahrens * 2548789Sahrens * Unshare all filesystems, or a specific ZFS filesystem. 2549789Sahrens */ 2550789Sahrens static int 2551789Sahrens zfs_do_unshare(int argc, char **argv) 2552789Sahrens { 2553789Sahrens return (unshare_unmount(OP_SHARE, argc, argv)); 2554789Sahrens } 2555789Sahrens 2556789Sahrens /* 2557789Sahrens * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 2558789Sahrens * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 2559789Sahrens */ 2560789Sahrens static int 2561789Sahrens manual_mount(int argc, char **argv) 2562789Sahrens { 2563789Sahrens zfs_handle_t *zhp; 2564789Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2565789Sahrens char mntopts[MNT_LINE_MAX] = { '\0' }; 2566789Sahrens int ret; 2567789Sahrens int c; 2568789Sahrens int flags = 0; 2569789Sahrens char *dataset, *path; 2570789Sahrens 2571789Sahrens /* check options */ 2572789Sahrens while ((c = getopt(argc, argv, ":o:O")) != -1) { 2573789Sahrens switch (c) { 2574789Sahrens case 'o': 2575789Sahrens (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 2576789Sahrens break; 2577789Sahrens case 'O': 2578789Sahrens flags |= MS_OVERLAY; 2579789Sahrens break; 2580789Sahrens case ':': 2581789Sahrens (void) fprintf(stderr, gettext("missing argument for " 2582789Sahrens "'%c' option\n"), optopt); 2583789Sahrens usage(FALSE); 2584789Sahrens break; 2585789Sahrens case '?': 2586789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2587789Sahrens optopt); 2588789Sahrens (void) fprintf(stderr, gettext("usage: mount [-o opts] " 2589789Sahrens "<path>\n")); 2590789Sahrens return (2); 2591789Sahrens } 2592789Sahrens } 2593789Sahrens 2594789Sahrens argc -= optind; 2595789Sahrens argv += optind; 2596789Sahrens 2597789Sahrens /* check that we only have two arguments */ 2598789Sahrens if (argc != 2) { 2599789Sahrens if (argc == 0) 2600789Sahrens (void) fprintf(stderr, gettext("missing dataset " 2601789Sahrens "argument\n")); 2602789Sahrens else if (argc == 1) 2603789Sahrens (void) fprintf(stderr, 2604789Sahrens gettext("missing mountpoint argument\n")); 2605789Sahrens else 2606789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2607789Sahrens (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 2608789Sahrens return (2); 2609789Sahrens } 2610789Sahrens 2611789Sahrens dataset = argv[0]; 2612789Sahrens path = argv[1]; 2613789Sahrens 2614789Sahrens /* try to open the dataset */ 2615789Sahrens if ((zhp = zfs_open(dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 2616789Sahrens return (1); 2617789Sahrens 2618789Sahrens (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 2619789Sahrens sizeof (mountpoint), NULL, NULL, 0, FALSE); 2620789Sahrens 2621789Sahrens /* check for legacy mountpoint and complain appropriately */ 2622789Sahrens ret = 0; 2623789Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 2624789Sahrens if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS, 2625789Sahrens NULL, 0, mntopts, sizeof (mntopts)) != 0) { 2626789Sahrens (void) fprintf(stderr, gettext("mount failed: %s\n"), 2627789Sahrens strerror(errno)); 2628789Sahrens ret = 1; 2629789Sahrens } 2630789Sahrens } else { 2631789Sahrens (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 2632789Sahrens "mounted using 'mount -F zfs'\n"), dataset); 2633789Sahrens (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 2634789Sahrens "instead.\n"), path); 2635789Sahrens (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' " 2636789Sahrens "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n")); 2637789Sahrens (void) fprintf(stderr, gettext("See zfs(1M) for more " 2638789Sahrens "information.\n")); 2639789Sahrens ret = 1; 2640789Sahrens } 2641789Sahrens 2642789Sahrens return (ret); 2643789Sahrens } 2644789Sahrens 2645789Sahrens /* 2646789Sahrens * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 2647789Sahrens * unmounts of non-legacy filesystems, as this is the dominant administrative 2648789Sahrens * interface. 2649789Sahrens */ 2650789Sahrens static int 2651789Sahrens manual_unmount(int argc, char **argv) 2652789Sahrens { 2653789Sahrens int flags = 0; 2654789Sahrens int c; 2655789Sahrens 2656789Sahrens /* check options */ 2657789Sahrens while ((c = getopt(argc, argv, "f")) != -1) { 2658789Sahrens switch (c) { 2659789Sahrens case 'f': 2660789Sahrens flags = MS_FORCE; 2661789Sahrens break; 2662789Sahrens case '?': 2663789Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2664789Sahrens optopt); 2665789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] " 2666789Sahrens "<path>\n")); 2667789Sahrens return (2); 2668789Sahrens } 2669789Sahrens } 2670789Sahrens 2671789Sahrens argc -= optind; 2672789Sahrens argv += optind; 2673789Sahrens 2674789Sahrens /* check arguments */ 2675789Sahrens if (argc != 1) { 2676789Sahrens if (argc == 0) 2677789Sahrens (void) fprintf(stderr, gettext("missing path " 2678789Sahrens "argument\n")); 2679789Sahrens else 2680789Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2681789Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 2682789Sahrens return (2); 2683789Sahrens } 2684789Sahrens 2685789Sahrens return (unshare_unmount_path(OP_MOUNT, argv[0], flags, TRUE)); 2686789Sahrens } 2687789Sahrens 2688789Sahrens static int 2689789Sahrens volcheck(zpool_handle_t *zhp, void *data) 2690789Sahrens { 2691789Sahrens int isinit = (int)data; 2692789Sahrens 2693789Sahrens if (isinit) 2694789Sahrens return (zpool_create_zvol_links(zhp)); 2695789Sahrens else 2696789Sahrens return (zpool_remove_zvol_links(zhp)); 2697789Sahrens } 2698789Sahrens 2699789Sahrens /* 2700789Sahrens * Iterate over all pools in the system and either create or destroy /dev/zvol 2701789Sahrens * links, depending on the value of 'isinit'. 2702789Sahrens */ 2703789Sahrens static int 2704789Sahrens do_volcheck(int isinit) 2705789Sahrens { 2706789Sahrens return (zpool_iter(volcheck, (void *)isinit) ? 1 : 0); 2707789Sahrens } 2708789Sahrens 2709789Sahrens int 2710789Sahrens main(int argc, char **argv) 2711789Sahrens { 2712789Sahrens int ret; 2713789Sahrens int i; 2714789Sahrens char *progname; 2715789Sahrens char *cmdname; 2716789Sahrens 2717789Sahrens (void) setlocale(LC_ALL, ""); 2718789Sahrens (void) textdomain(TEXT_DOMAIN); 2719789Sahrens 2720789Sahrens opterr = 0; 2721789Sahrens 2722789Sahrens if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 2723789Sahrens (void) fprintf(stderr, gettext("internal error: unable to " 2724789Sahrens "open %s\n"), MNTTAB); 2725789Sahrens return (1); 2726789Sahrens } 2727789Sahrens 2728789Sahrens /* 2729789Sahrens * This command also doubles as the /etc/fs mount and unmount program. 2730789Sahrens * Determine if we should take this behavior based on argv[0]. 2731789Sahrens */ 2732789Sahrens progname = basename(argv[0]); 2733789Sahrens if (strcmp(progname, "mount") == 0) { 2734789Sahrens ret = manual_mount(argc, argv); 2735789Sahrens } else if (strcmp(progname, "umount") == 0) { 2736789Sahrens ret = manual_unmount(argc, argv); 2737789Sahrens } else { 2738789Sahrens /* 2739789Sahrens * Make sure the user has specified some command. 2740789Sahrens */ 2741789Sahrens if (argc < 2) { 2742789Sahrens (void) fprintf(stderr, gettext("missing command\n")); 2743789Sahrens usage(FALSE); 2744789Sahrens } 2745789Sahrens 2746789Sahrens cmdname = argv[1]; 2747789Sahrens 2748789Sahrens /* 2749789Sahrens * The 'umount' command is an alias for 'unmount' 2750789Sahrens */ 2751789Sahrens if (strcmp(cmdname, "umount") == 0) 2752789Sahrens cmdname = "unmount"; 2753789Sahrens 2754789Sahrens /* 2755789Sahrens * Special case '-?' 2756789Sahrens */ 2757789Sahrens if (strcmp(cmdname, "-?") == 0) 2758789Sahrens usage(TRUE); 2759789Sahrens 2760789Sahrens /* 2761789Sahrens * 'volinit' and 'volfini' do not appear in the usage message, 2762789Sahrens * so we have to special case them here. 2763789Sahrens */ 2764789Sahrens if (strcmp(cmdname, "volinit") == 0) 2765789Sahrens return (do_volcheck(TRUE)); 2766789Sahrens else if (strcmp(cmdname, "volfini") == 0) 2767789Sahrens return (do_volcheck(FALSE)); 2768789Sahrens 2769789Sahrens /* 2770789Sahrens * Run the appropriate command. 2771789Sahrens */ 2772789Sahrens for (i = 0; i < NCOMMAND; i++) { 2773789Sahrens if (command_table[i].name == NULL) 2774789Sahrens continue; 2775789Sahrens 2776789Sahrens if (strcmp(cmdname, command_table[i].name) == 0) { 2777789Sahrens current_command = &command_table[i]; 2778789Sahrens ret = command_table[i].func(argc - 1, argv + 1); 2779789Sahrens break; 2780789Sahrens } 2781789Sahrens } 2782789Sahrens 2783789Sahrens if (i == NCOMMAND) { 2784789Sahrens (void) fprintf(stderr, gettext("unrecognized " 2785789Sahrens "command '%s'\n"), cmdname); 2786789Sahrens usage(FALSE); 2787789Sahrens } 2788789Sahrens } 2789789Sahrens 2790789Sahrens (void) fclose(mnttab_file); 2791789Sahrens 2792789Sahrens /* 2793789Sahrens * The 'ZFS_ABORT' environment variable causes us to dump core on exit 2794789Sahrens * for the purposes of running ::findleaks. 2795789Sahrens */ 2796789Sahrens if (getenv("ZFS_ABORT") != NULL) { 2797789Sahrens (void) printf("dumping core by request\n"); 2798789Sahrens abort(); 2799789Sahrens } 2800789Sahrens 2801789Sahrens return (ret); 2802789Sahrens } 2803