1*fa9e4066Sahrens /* 2*fa9e4066Sahrens * CDDL HEADER START 3*fa9e4066Sahrens * 4*fa9e4066Sahrens * The contents of this file are subject to the terms of the 5*fa9e4066Sahrens * Common Development and Distribution License, Version 1.0 only 6*fa9e4066Sahrens * (the "License"). You may not use this file except in compliance 7*fa9e4066Sahrens * with the License. 8*fa9e4066Sahrens * 9*fa9e4066Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*fa9e4066Sahrens * or http://www.opensolaris.org/os/licensing. 11*fa9e4066Sahrens * See the License for the specific language governing permissions 12*fa9e4066Sahrens * and limitations under the License. 13*fa9e4066Sahrens * 14*fa9e4066Sahrens * When distributing Covered Code, include this CDDL HEADER in each 15*fa9e4066Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*fa9e4066Sahrens * If applicable, add the following below this CDDL HEADER, with the 17*fa9e4066Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 18*fa9e4066Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 19*fa9e4066Sahrens * 20*fa9e4066Sahrens * CDDL HEADER END 21*fa9e4066Sahrens */ 22*fa9e4066Sahrens /* 23*fa9e4066Sahrens * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*fa9e4066Sahrens * Use is subject to license terms. 25*fa9e4066Sahrens */ 26*fa9e4066Sahrens 27*fa9e4066Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 28*fa9e4066Sahrens 29*fa9e4066Sahrens #include <assert.h> 30*fa9e4066Sahrens #include <errno.h> 31*fa9e4066Sahrens #include <libgen.h> 32*fa9e4066Sahrens #include <libintl.h> 33*fa9e4066Sahrens #include <libuutil.h> 34*fa9e4066Sahrens #include <locale.h> 35*fa9e4066Sahrens #include <stddef.h> 36*fa9e4066Sahrens #include <stdio.h> 37*fa9e4066Sahrens #include <stdlib.h> 38*fa9e4066Sahrens #include <strings.h> 39*fa9e4066Sahrens #include <unistd.h> 40*fa9e4066Sahrens #include <fcntl.h> 41*fa9e4066Sahrens #include <zone.h> 42*fa9e4066Sahrens #include <sys/mkdev.h> 43*fa9e4066Sahrens #include <sys/mntent.h> 44*fa9e4066Sahrens #include <sys/mnttab.h> 45*fa9e4066Sahrens #include <sys/mount.h> 46*fa9e4066Sahrens #include <sys/stat.h> 47*fa9e4066Sahrens 48*fa9e4066Sahrens #include <libzfs.h> 49*fa9e4066Sahrens 50*fa9e4066Sahrens #include "zfs_iter.h" 51*fa9e4066Sahrens 52*fa9e4066Sahrens static FILE *mnttab_file; 53*fa9e4066Sahrens 54*fa9e4066Sahrens static int zfs_do_clone(int argc, char **argv); 55*fa9e4066Sahrens static int zfs_do_create(int argc, char **argv); 56*fa9e4066Sahrens static int zfs_do_destroy(int argc, char **argv); 57*fa9e4066Sahrens static int zfs_do_get(int argc, char **argv); 58*fa9e4066Sahrens static int zfs_do_inherit(int argc, char **argv); 59*fa9e4066Sahrens static int zfs_do_list(int argc, char **argv); 60*fa9e4066Sahrens static int zfs_do_mount(int argc, char **argv); 61*fa9e4066Sahrens static int zfs_do_rename(int argc, char **argv); 62*fa9e4066Sahrens static int zfs_do_rollback(int argc, char **argv); 63*fa9e4066Sahrens static int zfs_do_set(int argc, char **argv); 64*fa9e4066Sahrens static int zfs_do_snapshot(int argc, char **argv); 65*fa9e4066Sahrens static int zfs_do_unmount(int argc, char **argv); 66*fa9e4066Sahrens static int zfs_do_share(int argc, char **argv); 67*fa9e4066Sahrens static int zfs_do_unshare(int argc, char **argv); 68*fa9e4066Sahrens static int zfs_do_backup(int argc, char **argv); 69*fa9e4066Sahrens static int zfs_do_restore(int argc, char **argv); 70*fa9e4066Sahrens 71*fa9e4066Sahrens /* 72*fa9e4066Sahrens * These libumem hooks provide a reasonable set of defaults for the allocator's 73*fa9e4066Sahrens * debugging facilities. 74*fa9e4066Sahrens */ 75*fa9e4066Sahrens const char * 76*fa9e4066Sahrens _umem_debug_init() 77*fa9e4066Sahrens { 78*fa9e4066Sahrens return ("default,verbose"); /* $UMEM_DEBUG setting */ 79*fa9e4066Sahrens } 80*fa9e4066Sahrens 81*fa9e4066Sahrens const char * 82*fa9e4066Sahrens _umem_logging_init(void) 83*fa9e4066Sahrens { 84*fa9e4066Sahrens return ("fail,contents"); /* $UMEM_LOGGING setting */ 85*fa9e4066Sahrens } 86*fa9e4066Sahrens 87*fa9e4066Sahrens typedef struct zfs_command { 88*fa9e4066Sahrens const char *name; 89*fa9e4066Sahrens int (*func)(int argc, char **argv); 90*fa9e4066Sahrens const char *usage; 91*fa9e4066Sahrens } zfs_command_t; 92*fa9e4066Sahrens 93*fa9e4066Sahrens /* 94*fa9e4066Sahrens * Master command table. Each ZFS command has a name, associated function, and 95*fa9e4066Sahrens * usage message. These commands are organized according to how they are 96*fa9e4066Sahrens * displayed in the usage message. An empty command (one with a NULL name) 97*fa9e4066Sahrens * indicates an empty line in the generic usage message. A command with a NULL 98*fa9e4066Sahrens * usage message indicates an alias for an existing command, and is not 99*fa9e4066Sahrens * displayed in the general usage message. 100*fa9e4066Sahrens */ 101*fa9e4066Sahrens static zfs_command_t command_table[] = { 102*fa9e4066Sahrens { "create", zfs_do_create, 103*fa9e4066Sahrens "\tcreate <filesystem>\n" 104*fa9e4066Sahrens "\tcreate [-s] [-b blocksize] -V <size> <volume>\n" }, 105*fa9e4066Sahrens { "destroy", zfs_do_destroy, 106*fa9e4066Sahrens "\tdestroy [-rRf] <filesystem|volume|snapshot>\n" }, 107*fa9e4066Sahrens { NULL }, 108*fa9e4066Sahrens { "snapshot", zfs_do_snapshot, 109*fa9e4066Sahrens "\tsnapshot <filesystem@name|volume@name>\n" }, 110*fa9e4066Sahrens { "rollback", zfs_do_rollback, 111*fa9e4066Sahrens "\trollback [-rRf] <snapshot>\n" }, 112*fa9e4066Sahrens { "clone", zfs_do_clone, 113*fa9e4066Sahrens "\tclone <snapshot> <filesystem|volume>\n" }, 114*fa9e4066Sahrens { "rename", zfs_do_rename, 115*fa9e4066Sahrens "\trename <filesystems|volume|snapshot> " 116*fa9e4066Sahrens "<filesystem|volume|snapshot>\n" }, 117*fa9e4066Sahrens { NULL }, 118*fa9e4066Sahrens { "list", zfs_do_list, 119*fa9e4066Sahrens "\tlist [-rH] [-o property[,property]...] [-t type[,type]...]\n" 120*fa9e4066Sahrens "\t [filesystem|volume|snapshot] ...\n" }, 121*fa9e4066Sahrens { NULL }, 122*fa9e4066Sahrens { "set", zfs_do_set, 123*fa9e4066Sahrens "\tset <property=value> <filesystem|volume> ...\n" }, 124*fa9e4066Sahrens { "get", zfs_do_get, 125*fa9e4066Sahrens "\tget [-rHp] [-o field[,field]...] [-s source[,source]...]\n" 126*fa9e4066Sahrens "\t <all | property[,property]...> " 127*fa9e4066Sahrens "<filesystem|volume|snapshot> ...\n" }, 128*fa9e4066Sahrens { "inherit", zfs_do_inherit, 129*fa9e4066Sahrens "\tinherit [-r] <property> <filesystem|volume> ...\n" }, 130*fa9e4066Sahrens { NULL }, 131*fa9e4066Sahrens { "mount", zfs_do_mount, 132*fa9e4066Sahrens "\tmount\n" 133*fa9e4066Sahrens "\tmount [-o opts] [-O] -a\n" 134*fa9e4066Sahrens "\tmount [-o opts] [-O] <filesystem>\n" }, 135*fa9e4066Sahrens { NULL }, 136*fa9e4066Sahrens { "unmount", zfs_do_unmount, 137*fa9e4066Sahrens "\tunmount [-f] -a\n" 138*fa9e4066Sahrens "\tunmount [-f] <filesystem|mountpoint>\n" }, 139*fa9e4066Sahrens { NULL }, 140*fa9e4066Sahrens { "share", zfs_do_share, 141*fa9e4066Sahrens "\tshare -a\n" 142*fa9e4066Sahrens "\tshare <filesystem>\n" }, 143*fa9e4066Sahrens { NULL }, 144*fa9e4066Sahrens { "unshare", zfs_do_unshare, 145*fa9e4066Sahrens "\tunshare [-f] -a\n" 146*fa9e4066Sahrens "\tunshare [-f] <filesystem|mountpoint>\n" }, 147*fa9e4066Sahrens { NULL }, 148*fa9e4066Sahrens { "backup", zfs_do_backup, 149*fa9e4066Sahrens "\tbackup [-i <snapshot>] <snapshot>\n" }, 150*fa9e4066Sahrens { "restore", zfs_do_restore, 151*fa9e4066Sahrens "\trestore [-vn] <filesystem|volume|snapshot>\n" 152*fa9e4066Sahrens "\trestore [-vn] -d <filesystem>\n" }, 153*fa9e4066Sahrens }; 154*fa9e4066Sahrens 155*fa9e4066Sahrens #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) 156*fa9e4066Sahrens 157*fa9e4066Sahrens zfs_command_t *current_command; 158*fa9e4066Sahrens 159*fa9e4066Sahrens /* 160*fa9e4066Sahrens * Utility function to guarantee malloc() success. 161*fa9e4066Sahrens */ 162*fa9e4066Sahrens void * 163*fa9e4066Sahrens safe_malloc(size_t size) 164*fa9e4066Sahrens { 165*fa9e4066Sahrens void *data; 166*fa9e4066Sahrens 167*fa9e4066Sahrens if ((data = calloc(1, size)) == NULL) { 168*fa9e4066Sahrens (void) fprintf(stderr, "internal error: out of memory\n"); 169*fa9e4066Sahrens exit(1); 170*fa9e4066Sahrens } 171*fa9e4066Sahrens 172*fa9e4066Sahrens return (data); 173*fa9e4066Sahrens } 174*fa9e4066Sahrens 175*fa9e4066Sahrens /* 176*fa9e4066Sahrens * Display usage message. If we're inside a command, display only the usage for 177*fa9e4066Sahrens * that command. Otherwise, iterate over the entire command table and display 178*fa9e4066Sahrens * a complete usage message. 179*fa9e4066Sahrens */ 180*fa9e4066Sahrens static void 181*fa9e4066Sahrens usage(int requested) 182*fa9e4066Sahrens { 183*fa9e4066Sahrens int i; 184*fa9e4066Sahrens int show_properties = FALSE; 185*fa9e4066Sahrens FILE *fp = requested ? stdout : stderr; 186*fa9e4066Sahrens 187*fa9e4066Sahrens if (current_command == NULL) { 188*fa9e4066Sahrens 189*fa9e4066Sahrens (void) fprintf(fp, gettext("usage: zfs command args ...\n")); 190*fa9e4066Sahrens (void) fprintf(fp, 191*fa9e4066Sahrens gettext("where 'command' is one of the following:\n\n")); 192*fa9e4066Sahrens 193*fa9e4066Sahrens for (i = 0; i < NCOMMAND; i++) { 194*fa9e4066Sahrens if (command_table[i].name == NULL) 195*fa9e4066Sahrens (void) fprintf(fp, "\n"); 196*fa9e4066Sahrens else 197*fa9e4066Sahrens (void) fprintf(fp, "%s", 198*fa9e4066Sahrens command_table[i].usage); 199*fa9e4066Sahrens } 200*fa9e4066Sahrens 201*fa9e4066Sahrens (void) fprintf(fp, gettext("\nEach dataset is of the form: " 202*fa9e4066Sahrens "pool/[dataset/]*dataset[@name]\n")); 203*fa9e4066Sahrens } else { 204*fa9e4066Sahrens (void) fprintf(fp, gettext("usage:\n")); 205*fa9e4066Sahrens (void) fprintf(fp, current_command->usage); 206*fa9e4066Sahrens } 207*fa9e4066Sahrens 208*fa9e4066Sahrens if (current_command == NULL || 209*fa9e4066Sahrens strcmp(current_command->name, "set") == 0 || 210*fa9e4066Sahrens strcmp(current_command->name, "get") == 0 || 211*fa9e4066Sahrens strcmp(current_command->name, "inherit") == 0 || 212*fa9e4066Sahrens strcmp(current_command->name, "list") == 0) 213*fa9e4066Sahrens show_properties = TRUE; 214*fa9e4066Sahrens 215*fa9e4066Sahrens if (show_properties) { 216*fa9e4066Sahrens 217*fa9e4066Sahrens (void) fprintf(fp, 218*fa9e4066Sahrens gettext("\nThe following properties are supported:\n")); 219*fa9e4066Sahrens 220*fa9e4066Sahrens (void) fprintf(fp, "\n\t%-13s %s %s %s\n\n", 221*fa9e4066Sahrens "PROPERTY", "EDIT", "INHERIT", "VALUES"); 222*fa9e4066Sahrens 223*fa9e4066Sahrens for (i = 0; i < ZFS_NPROP_VISIBLE; i++) { 224*fa9e4066Sahrens (void) fprintf(fp, "\t%-13s ", zfs_prop_to_name(i)); 225*fa9e4066Sahrens 226*fa9e4066Sahrens if (zfs_prop_readonly(i)) 227*fa9e4066Sahrens (void) fprintf(fp, " NO "); 228*fa9e4066Sahrens else 229*fa9e4066Sahrens (void) fprintf(fp, " YES "); 230*fa9e4066Sahrens 231*fa9e4066Sahrens if (zfs_prop_inheritable(i)) 232*fa9e4066Sahrens (void) fprintf(fp, " YES "); 233*fa9e4066Sahrens else 234*fa9e4066Sahrens (void) fprintf(fp, " NO "); 235*fa9e4066Sahrens 236*fa9e4066Sahrens if (zfs_prop_values(i) == NULL) 237*fa9e4066Sahrens (void) fprintf(fp, "-\n"); 238*fa9e4066Sahrens else 239*fa9e4066Sahrens (void) fprintf(fp, "%s\n", zfs_prop_values(i)); 240*fa9e4066Sahrens } 241*fa9e4066Sahrens (void) fprintf(fp, gettext("\nSizes are specified in bytes " 242*fa9e4066Sahrens "with standard units such as K, M, G, etc.\n")); 243*fa9e4066Sahrens } 244*fa9e4066Sahrens 245*fa9e4066Sahrens exit(requested ? 0 : 2); 246*fa9e4066Sahrens } 247*fa9e4066Sahrens 248*fa9e4066Sahrens /* 249*fa9e4066Sahrens * zfs clone <fs, snap, vol> fs 250*fa9e4066Sahrens * 251*fa9e4066Sahrens * Given an existing dataset, create a writable copy whose initial contents 252*fa9e4066Sahrens * are the same as the source. The newly created dataset maintains a 253*fa9e4066Sahrens * dependency on the original; the original cannot be destroyed so long as 254*fa9e4066Sahrens * the clone exists. 255*fa9e4066Sahrens */ 256*fa9e4066Sahrens static int 257*fa9e4066Sahrens zfs_do_clone(int argc, char **argv) 258*fa9e4066Sahrens { 259*fa9e4066Sahrens zfs_handle_t *zhp; 260*fa9e4066Sahrens int ret; 261*fa9e4066Sahrens 262*fa9e4066Sahrens /* check options */ 263*fa9e4066Sahrens if (argc > 1 && argv[1][0] == '-') { 264*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 265*fa9e4066Sahrens argv[1][1]); 266*fa9e4066Sahrens usage(FALSE); 267*fa9e4066Sahrens } 268*fa9e4066Sahrens 269*fa9e4066Sahrens /* check number of arguments */ 270*fa9e4066Sahrens if (argc < 2) { 271*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing source dataset " 272*fa9e4066Sahrens "argument\n")); 273*fa9e4066Sahrens usage(FALSE); 274*fa9e4066Sahrens } 275*fa9e4066Sahrens if (argc < 3) { 276*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing target dataset " 277*fa9e4066Sahrens "argument\n")); 278*fa9e4066Sahrens usage(FALSE); 279*fa9e4066Sahrens } 280*fa9e4066Sahrens if (argc > 3) { 281*fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 282*fa9e4066Sahrens usage(FALSE); 283*fa9e4066Sahrens } 284*fa9e4066Sahrens 285*fa9e4066Sahrens /* open the source dataset */ 286*fa9e4066Sahrens if ((zhp = zfs_open(argv[1], ZFS_TYPE_SNAPSHOT)) == NULL) 287*fa9e4066Sahrens return (1); 288*fa9e4066Sahrens 289*fa9e4066Sahrens /* pass to libzfs */ 290*fa9e4066Sahrens ret = zfs_clone(zhp, argv[2]); 291*fa9e4066Sahrens 292*fa9e4066Sahrens /* create the mountpoint if necessary */ 293*fa9e4066Sahrens if (ret == 0) { 294*fa9e4066Sahrens zfs_handle_t *clone = zfs_open(argv[2], ZFS_TYPE_ANY); 295*fa9e4066Sahrens if (clone != NULL) { 296*fa9e4066Sahrens if ((ret = zfs_mount(clone, NULL, 0)) == 0) 297*fa9e4066Sahrens ret = zfs_share(clone); 298*fa9e4066Sahrens zfs_close(clone); 299*fa9e4066Sahrens } 300*fa9e4066Sahrens } 301*fa9e4066Sahrens 302*fa9e4066Sahrens zfs_close(zhp); 303*fa9e4066Sahrens 304*fa9e4066Sahrens return (ret == 0 ? 0 : 1); 305*fa9e4066Sahrens } 306*fa9e4066Sahrens 307*fa9e4066Sahrens /* 308*fa9e4066Sahrens * zfs create fs 309*fa9e4066Sahrens * zfs create [-s] -V vol size 310*fa9e4066Sahrens * 311*fa9e4066Sahrens * Create a new dataset. This command can be used to create filesystems 312*fa9e4066Sahrens * and volumes. Snapshot creation is handled by 'zfs snapshot'. 313*fa9e4066Sahrens * For volumes, the user must specify a size to be used. 314*fa9e4066Sahrens * 315*fa9e4066Sahrens * The '-s' flag applies only to volumes, and indicates that we should not try 316*fa9e4066Sahrens * to set the reservation for this volume. By default we set a reservation 317*fa9e4066Sahrens * equal to the size for any volume. 318*fa9e4066Sahrens */ 319*fa9e4066Sahrens static int 320*fa9e4066Sahrens zfs_do_create(int argc, char **argv) 321*fa9e4066Sahrens { 322*fa9e4066Sahrens zfs_type_t type = ZFS_TYPE_FILESYSTEM; 323*fa9e4066Sahrens zfs_handle_t *zhp; 324*fa9e4066Sahrens char *size = NULL; 325*fa9e4066Sahrens char *blocksize = NULL; 326*fa9e4066Sahrens int c; 327*fa9e4066Sahrens int noreserve = FALSE; 328*fa9e4066Sahrens int ret; 329*fa9e4066Sahrens 330*fa9e4066Sahrens /* check options */ 331*fa9e4066Sahrens while ((c = getopt(argc, argv, ":V:b:s")) != -1) { 332*fa9e4066Sahrens switch (c) { 333*fa9e4066Sahrens case 'V': 334*fa9e4066Sahrens type = ZFS_TYPE_VOLUME; 335*fa9e4066Sahrens size = optarg; 336*fa9e4066Sahrens break; 337*fa9e4066Sahrens case 'b': 338*fa9e4066Sahrens blocksize = optarg; 339*fa9e4066Sahrens break; 340*fa9e4066Sahrens case 's': 341*fa9e4066Sahrens noreserve = TRUE; 342*fa9e4066Sahrens break; 343*fa9e4066Sahrens case ':': 344*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing size " 345*fa9e4066Sahrens "argument\n")); 346*fa9e4066Sahrens usage(FALSE); 347*fa9e4066Sahrens break; 348*fa9e4066Sahrens case '?': 349*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 350*fa9e4066Sahrens optopt); 351*fa9e4066Sahrens usage(FALSE); 352*fa9e4066Sahrens } 353*fa9e4066Sahrens } 354*fa9e4066Sahrens 355*fa9e4066Sahrens if (noreserve && type != ZFS_TYPE_VOLUME) { 356*fa9e4066Sahrens (void) fprintf(stderr, gettext("'-s' can only be used when " 357*fa9e4066Sahrens "creating a volume\n")); 358*fa9e4066Sahrens usage(FALSE); 359*fa9e4066Sahrens } 360*fa9e4066Sahrens 361*fa9e4066Sahrens argc -= optind; 362*fa9e4066Sahrens argv += optind; 363*fa9e4066Sahrens 364*fa9e4066Sahrens /* check number of arguments */ 365*fa9e4066Sahrens if (argc == 0) { 366*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing %s argument\n"), 367*fa9e4066Sahrens zfs_type_to_name(type)); 368*fa9e4066Sahrens usage(FALSE); 369*fa9e4066Sahrens } 370*fa9e4066Sahrens if (argc > 1) { 371*fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 372*fa9e4066Sahrens usage(FALSE); 373*fa9e4066Sahrens } 374*fa9e4066Sahrens 375*fa9e4066Sahrens /* pass to libzfs */ 376*fa9e4066Sahrens if (zfs_create(argv[0], type, size, blocksize) != 0) 377*fa9e4066Sahrens return (1); 378*fa9e4066Sahrens 379*fa9e4066Sahrens if ((zhp = zfs_open(argv[0], ZFS_TYPE_ANY)) == NULL) 380*fa9e4066Sahrens return (1); 381*fa9e4066Sahrens 382*fa9e4066Sahrens /* 383*fa9e4066Sahrens * Volume handling. By default, we try to create a reservation of equal 384*fa9e4066Sahrens * size for the volume. If we can't do this, then destroy the dataset 385*fa9e4066Sahrens * and report an error. 386*fa9e4066Sahrens */ 387*fa9e4066Sahrens if (type == ZFS_TYPE_VOLUME && !noreserve) { 388*fa9e4066Sahrens if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, size) != 0) { 389*fa9e4066Sahrens (void) fprintf(stderr, gettext("use '-s' to create a " 390*fa9e4066Sahrens "volume without a matching reservation\n")); 391*fa9e4066Sahrens (void) zfs_destroy(zhp); 392*fa9e4066Sahrens return (1); 393*fa9e4066Sahrens } 394*fa9e4066Sahrens } 395*fa9e4066Sahrens 396*fa9e4066Sahrens /* 397*fa9e4066Sahrens * Mount and/or share the new filesystem as appropriate. We provide a 398*fa9e4066Sahrens * verbose error message to let the user know that their filesystem was 399*fa9e4066Sahrens * in fact created, even if we failed to mount or share it. 400*fa9e4066Sahrens */ 401*fa9e4066Sahrens if (zfs_mount(zhp, NULL, 0) != 0) { 402*fa9e4066Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 403*fa9e4066Sahrens "created, but not mounted\n")); 404*fa9e4066Sahrens ret = 1; 405*fa9e4066Sahrens } else if (zfs_share(zhp) != 0) { 406*fa9e4066Sahrens (void) fprintf(stderr, gettext("filesystem successfully " 407*fa9e4066Sahrens "created, but not shared\n")); 408*fa9e4066Sahrens ret = 1; 409*fa9e4066Sahrens } else { 410*fa9e4066Sahrens ret = 0; 411*fa9e4066Sahrens } 412*fa9e4066Sahrens 413*fa9e4066Sahrens zfs_close(zhp); 414*fa9e4066Sahrens return (ret); 415*fa9e4066Sahrens } 416*fa9e4066Sahrens 417*fa9e4066Sahrens /* 418*fa9e4066Sahrens * zfs destroy [-rf] <fs, snap, vol> 419*fa9e4066Sahrens * 420*fa9e4066Sahrens * -r Recursively destroy all children 421*fa9e4066Sahrens * -R Recursively destroy all dependents, including clones 422*fa9e4066Sahrens * -f Force unmounting of any dependents 423*fa9e4066Sahrens * 424*fa9e4066Sahrens * Destroys the given dataset. By default, it will unmount any filesystems, 425*fa9e4066Sahrens * and refuse to destroy a dataset that has any dependents. A dependent can 426*fa9e4066Sahrens * either be a child, or a clone of a child. 427*fa9e4066Sahrens */ 428*fa9e4066Sahrens typedef struct destroy_cbdata { 429*fa9e4066Sahrens int cb_first; 430*fa9e4066Sahrens int cb_force; 431*fa9e4066Sahrens int cb_recurse; 432*fa9e4066Sahrens int cb_error; 433*fa9e4066Sahrens int cb_needforce; 434*fa9e4066Sahrens int cb_doclones; 435*fa9e4066Sahrens zfs_handle_t *cb_target; 436*fa9e4066Sahrens } destroy_cbdata_t; 437*fa9e4066Sahrens 438*fa9e4066Sahrens /* 439*fa9e4066Sahrens * Check for any dependents based on the '-r' or '-R' flags. 440*fa9e4066Sahrens */ 441*fa9e4066Sahrens static int 442*fa9e4066Sahrens destroy_check_dependent(zfs_handle_t *zhp, void *data) 443*fa9e4066Sahrens { 444*fa9e4066Sahrens destroy_cbdata_t *cbp = data; 445*fa9e4066Sahrens const char *tname = zfs_get_name(cbp->cb_target); 446*fa9e4066Sahrens const char *name = zfs_get_name(zhp); 447*fa9e4066Sahrens 448*fa9e4066Sahrens if (strncmp(tname, name, strlen(tname)) == 0 && 449*fa9e4066Sahrens (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { 450*fa9e4066Sahrens /* 451*fa9e4066Sahrens * This is a direct descendant, not a clone somewhere else in 452*fa9e4066Sahrens * the hierarchy. 453*fa9e4066Sahrens */ 454*fa9e4066Sahrens if (cbp->cb_recurse) 455*fa9e4066Sahrens goto out; 456*fa9e4066Sahrens 457*fa9e4066Sahrens if (cbp->cb_first) { 458*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 459*fa9e4066Sahrens "%s has children\n"), 460*fa9e4066Sahrens zfs_get_name(cbp->cb_target), 461*fa9e4066Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 462*fa9e4066Sahrens (void) fprintf(stderr, gettext("use '-r' to destroy " 463*fa9e4066Sahrens "the following datasets:\n")); 464*fa9e4066Sahrens cbp->cb_first = 0; 465*fa9e4066Sahrens cbp->cb_error = 1; 466*fa9e4066Sahrens } 467*fa9e4066Sahrens 468*fa9e4066Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 469*fa9e4066Sahrens } else { 470*fa9e4066Sahrens /* 471*fa9e4066Sahrens * This is a clone. We only want to report this if the '-r' 472*fa9e4066Sahrens * wasn't specified, or the target is a snapshot. 473*fa9e4066Sahrens */ 474*fa9e4066Sahrens if (!cbp->cb_recurse && 475*fa9e4066Sahrens zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) 476*fa9e4066Sahrens goto out; 477*fa9e4066Sahrens 478*fa9e4066Sahrens if (cbp->cb_first) { 479*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 480*fa9e4066Sahrens "%s has dependent clones\n"), 481*fa9e4066Sahrens zfs_get_name(cbp->cb_target), 482*fa9e4066Sahrens zfs_type_to_name(zfs_get_type(cbp->cb_target))); 483*fa9e4066Sahrens (void) fprintf(stderr, gettext("use '-R' to destroy " 484*fa9e4066Sahrens "the following datasets:\n")); 485*fa9e4066Sahrens cbp->cb_first = 0; 486*fa9e4066Sahrens cbp->cb_error = 1; 487*fa9e4066Sahrens } 488*fa9e4066Sahrens 489*fa9e4066Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 490*fa9e4066Sahrens } 491*fa9e4066Sahrens 492*fa9e4066Sahrens out: 493*fa9e4066Sahrens zfs_close(zhp); 494*fa9e4066Sahrens return (0); 495*fa9e4066Sahrens } 496*fa9e4066Sahrens 497*fa9e4066Sahrens static int 498*fa9e4066Sahrens destroy_callback(zfs_handle_t *zhp, void *data) 499*fa9e4066Sahrens { 500*fa9e4066Sahrens destroy_cbdata_t *cbp = data; 501*fa9e4066Sahrens 502*fa9e4066Sahrens /* 503*fa9e4066Sahrens * Ignore pools (which we've already flagged as an error before getting 504*fa9e4066Sahrens * here. 505*fa9e4066Sahrens */ 506*fa9e4066Sahrens if (strchr(zfs_get_name(zhp), '/') == NULL && 507*fa9e4066Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 508*fa9e4066Sahrens zfs_close(zhp); 509*fa9e4066Sahrens return (0); 510*fa9e4066Sahrens } 511*fa9e4066Sahrens 512*fa9e4066Sahrens /* 513*fa9e4066Sahrens * Bail out on the first error. 514*fa9e4066Sahrens */ 515*fa9e4066Sahrens if (zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0 || 516*fa9e4066Sahrens zfs_destroy(zhp) != 0) { 517*fa9e4066Sahrens zfs_close(zhp); 518*fa9e4066Sahrens return (-1); 519*fa9e4066Sahrens } 520*fa9e4066Sahrens 521*fa9e4066Sahrens zfs_close(zhp); 522*fa9e4066Sahrens return (0); 523*fa9e4066Sahrens } 524*fa9e4066Sahrens 525*fa9e4066Sahrens 526*fa9e4066Sahrens static int 527*fa9e4066Sahrens zfs_do_destroy(int argc, char **argv) 528*fa9e4066Sahrens { 529*fa9e4066Sahrens destroy_cbdata_t cb = { 0 }; 530*fa9e4066Sahrens int c; 531*fa9e4066Sahrens zfs_handle_t *zhp; 532*fa9e4066Sahrens 533*fa9e4066Sahrens /* check options */ 534*fa9e4066Sahrens while ((c = getopt(argc, argv, "frR")) != -1) { 535*fa9e4066Sahrens switch (c) { 536*fa9e4066Sahrens case 'f': 537*fa9e4066Sahrens cb.cb_force = 1; 538*fa9e4066Sahrens break; 539*fa9e4066Sahrens case 'r': 540*fa9e4066Sahrens cb.cb_recurse = 1; 541*fa9e4066Sahrens break; 542*fa9e4066Sahrens case 'R': 543*fa9e4066Sahrens cb.cb_recurse = 1; 544*fa9e4066Sahrens cb.cb_doclones = 1; 545*fa9e4066Sahrens break; 546*fa9e4066Sahrens case '?': 547*fa9e4066Sahrens default: 548*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 549*fa9e4066Sahrens optopt); 550*fa9e4066Sahrens usage(FALSE); 551*fa9e4066Sahrens } 552*fa9e4066Sahrens } 553*fa9e4066Sahrens 554*fa9e4066Sahrens argc -= optind; 555*fa9e4066Sahrens argv += optind; 556*fa9e4066Sahrens 557*fa9e4066Sahrens /* check number of arguments */ 558*fa9e4066Sahrens if (argc == 0) { 559*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing path argument\n")); 560*fa9e4066Sahrens usage(FALSE); 561*fa9e4066Sahrens } 562*fa9e4066Sahrens if (argc > 1) { 563*fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 564*fa9e4066Sahrens usage(FALSE); 565*fa9e4066Sahrens } 566*fa9e4066Sahrens 567*fa9e4066Sahrens /* Open the given dataset */ 568*fa9e4066Sahrens if ((zhp = zfs_open(argv[0], ZFS_TYPE_ANY)) == NULL) 569*fa9e4066Sahrens return (1); 570*fa9e4066Sahrens 571*fa9e4066Sahrens cb.cb_target = zhp; 572*fa9e4066Sahrens 573*fa9e4066Sahrens /* 574*fa9e4066Sahrens * Perform an explicit check for pools before going any further. 575*fa9e4066Sahrens */ 576*fa9e4066Sahrens if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 577*fa9e4066Sahrens zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 578*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot destroy '%s': " 579*fa9e4066Sahrens "operation does not apply to pools\n"), 580*fa9e4066Sahrens zfs_get_name(zhp)); 581*fa9e4066Sahrens (void) fprintf(stderr, gettext("use 'zfs destroy -r " 582*fa9e4066Sahrens "%s' to destroy all datasets in the pool\n"), 583*fa9e4066Sahrens zfs_get_name(zhp)); 584*fa9e4066Sahrens (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 585*fa9e4066Sahrens "to destroy the pool itself\n"), zfs_get_name(zhp)); 586*fa9e4066Sahrens zfs_close(zhp); 587*fa9e4066Sahrens return (1); 588*fa9e4066Sahrens } 589*fa9e4066Sahrens 590*fa9e4066Sahrens 591*fa9e4066Sahrens /* 592*fa9e4066Sahrens * Check for any dependents and/or clones. 593*fa9e4066Sahrens */ 594*fa9e4066Sahrens cb.cb_first = 1; 595*fa9e4066Sahrens if (!cb.cb_doclones) 596*fa9e4066Sahrens (void) zfs_iter_dependents(zhp, destroy_check_dependent, &cb); 597*fa9e4066Sahrens 598*fa9e4066Sahrens if (cb.cb_error) { 599*fa9e4066Sahrens zfs_close(zhp); 600*fa9e4066Sahrens return (1); 601*fa9e4066Sahrens } 602*fa9e4066Sahrens 603*fa9e4066Sahrens /* 604*fa9e4066Sahrens * Do the real thing. 605*fa9e4066Sahrens */ 606*fa9e4066Sahrens if (zfs_iter_dependents(zhp, destroy_callback, &cb) == 0 && 607*fa9e4066Sahrens destroy_callback(zhp, &cb) == 0) 608*fa9e4066Sahrens return (0); 609*fa9e4066Sahrens 610*fa9e4066Sahrens return (1); 611*fa9e4066Sahrens } 612*fa9e4066Sahrens 613*fa9e4066Sahrens /* 614*fa9e4066Sahrens * zfs get [-rH] [-o field[,field]...] [-s source[,source]...] 615*fa9e4066Sahrens * prop[,prop...] < fs | snap | vol > ... 616*fa9e4066Sahrens * 617*fa9e4066Sahrens * -r recurse over any child datasets 618*fa9e4066Sahrens * -H scripted mode. Headers are stripped, and fields are separated 619*fa9e4066Sahrens * by tabs instead of spaces. 620*fa9e4066Sahrens * -o Set of fields to display. One of "name,property,value,source". 621*fa9e4066Sahrens * Default is all four. 622*fa9e4066Sahrens * -s Set of sources to allow. One of 623*fa9e4066Sahrens * "local,default,inherited,temporary,none". Default is all 624*fa9e4066Sahrens * five. 625*fa9e4066Sahrens * -p Display values in parsable (literal) format. 626*fa9e4066Sahrens * 627*fa9e4066Sahrens * Prints properties for the given datasets. The user can control which 628*fa9e4066Sahrens * columns to display as well as which property types to allow. 629*fa9e4066Sahrens */ 630*fa9e4066Sahrens typedef struct get_cbdata { 631*fa9e4066Sahrens int cb_scripted; 632*fa9e4066Sahrens int cb_sources; 633*fa9e4066Sahrens int cb_literal; 634*fa9e4066Sahrens int cb_columns[4]; 635*fa9e4066Sahrens zfs_prop_t cb_prop[ZFS_NPROP_ALL]; 636*fa9e4066Sahrens int cb_nprop; 637*fa9e4066Sahrens } get_cbdata_t; 638*fa9e4066Sahrens 639*fa9e4066Sahrens #define GET_COL_NAME 1 640*fa9e4066Sahrens #define GET_COL_PROPERTY 2 641*fa9e4066Sahrens #define GET_COL_VALUE 3 642*fa9e4066Sahrens #define GET_COL_SOURCE 4 643*fa9e4066Sahrens 644*fa9e4066Sahrens /* 645*fa9e4066Sahrens * Display a single line of output, according to the settings in the callback 646*fa9e4066Sahrens * structure. 647*fa9e4066Sahrens */ 648*fa9e4066Sahrens static void 649*fa9e4066Sahrens print_one_property(zfs_handle_t *zhp, get_cbdata_t *cbp, zfs_prop_t prop, 650*fa9e4066Sahrens const char *value, zfs_source_t sourcetype, const char *source) 651*fa9e4066Sahrens { 652*fa9e4066Sahrens int i; 653*fa9e4066Sahrens int width; 654*fa9e4066Sahrens const char *str; 655*fa9e4066Sahrens char buf[128]; 656*fa9e4066Sahrens 657*fa9e4066Sahrens /* 658*fa9e4066Sahrens * Ignore those source types that the user has chosen to ignore. 659*fa9e4066Sahrens */ 660*fa9e4066Sahrens if ((sourcetype & cbp->cb_sources) == 0) 661*fa9e4066Sahrens return; 662*fa9e4066Sahrens 663*fa9e4066Sahrens for (i = 0; i < 4; i++) { 664*fa9e4066Sahrens switch (cbp->cb_columns[i]) { 665*fa9e4066Sahrens case GET_COL_NAME: 666*fa9e4066Sahrens width = 15; 667*fa9e4066Sahrens str = zfs_get_name(zhp); 668*fa9e4066Sahrens break; 669*fa9e4066Sahrens 670*fa9e4066Sahrens case GET_COL_PROPERTY: 671*fa9e4066Sahrens width = 13; 672*fa9e4066Sahrens str = zfs_prop_to_name(prop); 673*fa9e4066Sahrens break; 674*fa9e4066Sahrens 675*fa9e4066Sahrens case GET_COL_VALUE: 676*fa9e4066Sahrens width = 25; 677*fa9e4066Sahrens str = value; 678*fa9e4066Sahrens break; 679*fa9e4066Sahrens 680*fa9e4066Sahrens case GET_COL_SOURCE: 681*fa9e4066Sahrens width = 15; 682*fa9e4066Sahrens switch (sourcetype) { 683*fa9e4066Sahrens case ZFS_SRC_NONE: 684*fa9e4066Sahrens str = "-"; 685*fa9e4066Sahrens break; 686*fa9e4066Sahrens 687*fa9e4066Sahrens case ZFS_SRC_DEFAULT: 688*fa9e4066Sahrens str = "default"; 689*fa9e4066Sahrens break; 690*fa9e4066Sahrens 691*fa9e4066Sahrens case ZFS_SRC_LOCAL: 692*fa9e4066Sahrens str = "local"; 693*fa9e4066Sahrens break; 694*fa9e4066Sahrens 695*fa9e4066Sahrens case ZFS_SRC_TEMPORARY: 696*fa9e4066Sahrens str = "temporary"; 697*fa9e4066Sahrens break; 698*fa9e4066Sahrens 699*fa9e4066Sahrens case ZFS_SRC_INHERITED: 700*fa9e4066Sahrens (void) snprintf(buf, sizeof (buf), 701*fa9e4066Sahrens "inherited from %s", source); 702*fa9e4066Sahrens str = buf; 703*fa9e4066Sahrens break; 704*fa9e4066Sahrens } 705*fa9e4066Sahrens break; 706*fa9e4066Sahrens 707*fa9e4066Sahrens default: 708*fa9e4066Sahrens continue; 709*fa9e4066Sahrens } 710*fa9e4066Sahrens 711*fa9e4066Sahrens if (cbp->cb_columns[i + 1] == 0) 712*fa9e4066Sahrens (void) printf("%s", str); 713*fa9e4066Sahrens else if (cbp->cb_scripted) 714*fa9e4066Sahrens (void) printf("%s\t", str); 715*fa9e4066Sahrens else 716*fa9e4066Sahrens (void) printf("%-*s ", width, str); 717*fa9e4066Sahrens 718*fa9e4066Sahrens } 719*fa9e4066Sahrens 720*fa9e4066Sahrens (void) printf("\n"); 721*fa9e4066Sahrens } 722*fa9e4066Sahrens 723*fa9e4066Sahrens /* 724*fa9e4066Sahrens * Invoked to display the properties for a single dataset. 725*fa9e4066Sahrens */ 726*fa9e4066Sahrens static int 727*fa9e4066Sahrens get_callback(zfs_handle_t *zhp, void *data) 728*fa9e4066Sahrens { 729*fa9e4066Sahrens char buf[ZFS_MAXPROPLEN]; 730*fa9e4066Sahrens zfs_source_t sourcetype; 731*fa9e4066Sahrens char source[ZFS_MAXNAMELEN]; 732*fa9e4066Sahrens get_cbdata_t *cbp = data; 733*fa9e4066Sahrens int i; 734*fa9e4066Sahrens 735*fa9e4066Sahrens /* 736*fa9e4066Sahrens * If we've been given a list of properties, always list properties 737*fa9e4066Sahrens * in the order given. Otherwise, iterate over all properties and 738*fa9e4066Sahrens * determine if we should display them. 739*fa9e4066Sahrens */ 740*fa9e4066Sahrens if (cbp->cb_nprop != 0) { 741*fa9e4066Sahrens for (i = 0; i < cbp->cb_nprop; i++) { 742*fa9e4066Sahrens if (zfs_prop_get(zhp, cbp->cb_prop[i], buf, 743*fa9e4066Sahrens sizeof (buf), &sourcetype, source, sizeof (source), 744*fa9e4066Sahrens cbp->cb_literal) != 0) { 745*fa9e4066Sahrens (void) strlcpy(buf, "-", sizeof (buf)); 746*fa9e4066Sahrens sourcetype = ZFS_SRC_NONE; 747*fa9e4066Sahrens } 748*fa9e4066Sahrens 749*fa9e4066Sahrens print_one_property(zhp, cbp, cbp->cb_prop[i], 750*fa9e4066Sahrens buf, sourcetype, source); 751*fa9e4066Sahrens } 752*fa9e4066Sahrens } else { 753*fa9e4066Sahrens for (i = 0; i < ZFS_NPROP_VISIBLE; i++) { 754*fa9e4066Sahrens if (zfs_prop_get(zhp, i, buf, 755*fa9e4066Sahrens sizeof (buf), &sourcetype, source, sizeof (source), 756*fa9e4066Sahrens cbp->cb_literal) == 0) { 757*fa9e4066Sahrens print_one_property(zhp, cbp, i, 758*fa9e4066Sahrens buf, sourcetype, source); 759*fa9e4066Sahrens } 760*fa9e4066Sahrens } 761*fa9e4066Sahrens } 762*fa9e4066Sahrens 763*fa9e4066Sahrens return (0); 764*fa9e4066Sahrens } 765*fa9e4066Sahrens 766*fa9e4066Sahrens static int 767*fa9e4066Sahrens zfs_do_get(int argc, char **argv) 768*fa9e4066Sahrens { 769*fa9e4066Sahrens get_cbdata_t cb = { 0 }; 770*fa9e4066Sahrens int recurse = 0; 771*fa9e4066Sahrens int c; 772*fa9e4066Sahrens char **subopts = zfs_prop_column_subopts(); 773*fa9e4066Sahrens char **shortsubopts = zfs_prop_column_short_subopts(); 774*fa9e4066Sahrens int prop; 775*fa9e4066Sahrens char *value, *fields, *save_fields; 776*fa9e4066Sahrens int i; 777*fa9e4066Sahrens 778*fa9e4066Sahrens /* 779*fa9e4066Sahrens * Set up default columns and sources. 780*fa9e4066Sahrens */ 781*fa9e4066Sahrens cb.cb_sources = ZFS_SRC_ALL; 782*fa9e4066Sahrens cb.cb_columns[0] = GET_COL_NAME; 783*fa9e4066Sahrens cb.cb_columns[1] = GET_COL_PROPERTY; 784*fa9e4066Sahrens cb.cb_columns[2] = GET_COL_VALUE; 785*fa9e4066Sahrens cb.cb_columns[3] = GET_COL_SOURCE; 786*fa9e4066Sahrens 787*fa9e4066Sahrens /* check options */ 788*fa9e4066Sahrens while ((c = getopt(argc, argv, ":o:s:rHp")) != -1) { 789*fa9e4066Sahrens switch (c) { 790*fa9e4066Sahrens case 'p': 791*fa9e4066Sahrens cb.cb_literal = TRUE; 792*fa9e4066Sahrens break; 793*fa9e4066Sahrens case 'r': 794*fa9e4066Sahrens recurse = TRUE; 795*fa9e4066Sahrens break; 796*fa9e4066Sahrens case 'H': 797*fa9e4066Sahrens cb.cb_scripted = TRUE; 798*fa9e4066Sahrens break; 799*fa9e4066Sahrens case ':': 800*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing argument for " 801*fa9e4066Sahrens "'%c' option\n"), optopt); 802*fa9e4066Sahrens usage(FALSE); 803*fa9e4066Sahrens break; 804*fa9e4066Sahrens case 'o': 805*fa9e4066Sahrens /* 806*fa9e4066Sahrens * Process the set of columns to display. We zero out 807*fa9e4066Sahrens * the structure to give us a blank slate. 808*fa9e4066Sahrens */ 809*fa9e4066Sahrens bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 810*fa9e4066Sahrens i = 0; 811*fa9e4066Sahrens while (*optarg != '\0') { 812*fa9e4066Sahrens static char *col_subopts[] = 813*fa9e4066Sahrens { "name", "property", "value", "source", 814*fa9e4066Sahrens NULL }; 815*fa9e4066Sahrens 816*fa9e4066Sahrens if (i == 4) { 817*fa9e4066Sahrens (void) fprintf(stderr, gettext("too " 818*fa9e4066Sahrens "many fields given to -o " 819*fa9e4066Sahrens "option\n")); 820*fa9e4066Sahrens usage(FALSE); 821*fa9e4066Sahrens } 822*fa9e4066Sahrens 823*fa9e4066Sahrens switch (getsubopt(&optarg, col_subopts, 824*fa9e4066Sahrens &value)) { 825*fa9e4066Sahrens case 0: 826*fa9e4066Sahrens cb.cb_columns[i++] = GET_COL_NAME; 827*fa9e4066Sahrens break; 828*fa9e4066Sahrens case 1: 829*fa9e4066Sahrens cb.cb_columns[i++] = GET_COL_PROPERTY; 830*fa9e4066Sahrens break; 831*fa9e4066Sahrens case 2: 832*fa9e4066Sahrens cb.cb_columns[i++] = GET_COL_VALUE; 833*fa9e4066Sahrens break; 834*fa9e4066Sahrens case 3: 835*fa9e4066Sahrens cb.cb_columns[i++] = GET_COL_SOURCE; 836*fa9e4066Sahrens break; 837*fa9e4066Sahrens default: 838*fa9e4066Sahrens (void) fprintf(stderr, 839*fa9e4066Sahrens gettext("invalid column name " 840*fa9e4066Sahrens "'%s'\n"), value); 841*fa9e4066Sahrens usage(FALSE); 842*fa9e4066Sahrens } 843*fa9e4066Sahrens } 844*fa9e4066Sahrens break; 845*fa9e4066Sahrens 846*fa9e4066Sahrens case 's': 847*fa9e4066Sahrens cb.cb_sources = 0; 848*fa9e4066Sahrens while (*optarg != '\0') { 849*fa9e4066Sahrens static char *source_subopts[] = { 850*fa9e4066Sahrens "local", "default", "inherited", 851*fa9e4066Sahrens "temporary", "none", NULL }; 852*fa9e4066Sahrens 853*fa9e4066Sahrens switch (getsubopt(&optarg, source_subopts, 854*fa9e4066Sahrens &value)) { 855*fa9e4066Sahrens case 0: 856*fa9e4066Sahrens cb.cb_sources |= ZFS_SRC_LOCAL; 857*fa9e4066Sahrens break; 858*fa9e4066Sahrens case 1: 859*fa9e4066Sahrens cb.cb_sources |= ZFS_SRC_DEFAULT; 860*fa9e4066Sahrens break; 861*fa9e4066Sahrens case 2: 862*fa9e4066Sahrens cb.cb_sources |= ZFS_SRC_INHERITED; 863*fa9e4066Sahrens break; 864*fa9e4066Sahrens case 3: 865*fa9e4066Sahrens cb.cb_sources |= ZFS_SRC_TEMPORARY; 866*fa9e4066Sahrens break; 867*fa9e4066Sahrens case 4: 868*fa9e4066Sahrens cb.cb_sources |= ZFS_SRC_NONE; 869*fa9e4066Sahrens break; 870*fa9e4066Sahrens default: 871*fa9e4066Sahrens (void) fprintf(stderr, 872*fa9e4066Sahrens gettext("invalid source " 873*fa9e4066Sahrens "'%s'\n"), value); 874*fa9e4066Sahrens usage(FALSE); 875*fa9e4066Sahrens } 876*fa9e4066Sahrens } 877*fa9e4066Sahrens break; 878*fa9e4066Sahrens 879*fa9e4066Sahrens case '?': 880*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 881*fa9e4066Sahrens optopt); 882*fa9e4066Sahrens usage(FALSE); 883*fa9e4066Sahrens } 884*fa9e4066Sahrens } 885*fa9e4066Sahrens 886*fa9e4066Sahrens argc -= optind; 887*fa9e4066Sahrens argv += optind; 888*fa9e4066Sahrens 889*fa9e4066Sahrens if (argc < 1) { 890*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing property " 891*fa9e4066Sahrens "argument\n")); 892*fa9e4066Sahrens usage(FALSE); 893*fa9e4066Sahrens } 894*fa9e4066Sahrens 895*fa9e4066Sahrens fields = argv[0]; 896*fa9e4066Sahrens 897*fa9e4066Sahrens /* 898*fa9e4066Sahrens * Leaving 'cb_nprop' at 0 will cause the callback to iterate over all 899*fa9e4066Sahrens * known properties. 900*fa9e4066Sahrens */ 901*fa9e4066Sahrens if (strcmp(fields, "all") != 0) { 902*fa9e4066Sahrens while (*fields != '\0') { 903*fa9e4066Sahrens if (cb.cb_nprop == ZFS_NPROP_ALL) { 904*fa9e4066Sahrens (void) fprintf(stderr, gettext("too many " 905*fa9e4066Sahrens "properties given to -o option\n")); 906*fa9e4066Sahrens usage(FALSE); 907*fa9e4066Sahrens } 908*fa9e4066Sahrens 909*fa9e4066Sahrens save_fields = fields; 910*fa9e4066Sahrens if ((prop = getsubopt(&fields, subopts, 911*fa9e4066Sahrens &value)) == -1) { 912*fa9e4066Sahrens fields = save_fields; 913*fa9e4066Sahrens prop = getsubopt(&fields, shortsubopts, &value); 914*fa9e4066Sahrens } 915*fa9e4066Sahrens 916*fa9e4066Sahrens if (prop == -1) { 917*fa9e4066Sahrens (void) fprintf(stderr, 918*fa9e4066Sahrens gettext("invalid property '%s'\n"), value); 919*fa9e4066Sahrens usage(FALSE); 920*fa9e4066Sahrens } 921*fa9e4066Sahrens 922*fa9e4066Sahrens /* 923*fa9e4066Sahrens * The 'name' property is a one-off special for 'zfs 924*fa9e4066Sahrens * list', but is not a valid property for 'zfs get'. 925*fa9e4066Sahrens */ 926*fa9e4066Sahrens if (zfs_prop_column_name(prop) == NULL || 927*fa9e4066Sahrens prop == ZFS_PROP_NAME) { 928*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid " 929*fa9e4066Sahrens "property '%s'\n"), zfs_prop_to_name(prop)); 930*fa9e4066Sahrens usage(FALSE); 931*fa9e4066Sahrens } 932*fa9e4066Sahrens 933*fa9e4066Sahrens cb.cb_prop[cb.cb_nprop++] = prop; 934*fa9e4066Sahrens } 935*fa9e4066Sahrens } 936*fa9e4066Sahrens 937*fa9e4066Sahrens argc--; 938*fa9e4066Sahrens argv++; 939*fa9e4066Sahrens 940*fa9e4066Sahrens /* check for at least one dataset name */ 941*fa9e4066Sahrens if (argc < 1) { 942*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 943*fa9e4066Sahrens usage(FALSE); 944*fa9e4066Sahrens } 945*fa9e4066Sahrens 946*fa9e4066Sahrens /* 947*fa9e4066Sahrens * Print out any headers 948*fa9e4066Sahrens */ 949*fa9e4066Sahrens if (!cb.cb_scripted) { 950*fa9e4066Sahrens int i; 951*fa9e4066Sahrens for (i = 0; i < 4; i++) { 952*fa9e4066Sahrens switch (cb.cb_columns[i]) { 953*fa9e4066Sahrens case GET_COL_NAME: 954*fa9e4066Sahrens (void) printf("%-15s ", "NAME"); 955*fa9e4066Sahrens break; 956*fa9e4066Sahrens case GET_COL_PROPERTY: 957*fa9e4066Sahrens (void) printf("%-13s ", "PROPERTY"); 958*fa9e4066Sahrens break; 959*fa9e4066Sahrens case GET_COL_VALUE: 960*fa9e4066Sahrens (void) printf("%-25s ", "VALUE"); 961*fa9e4066Sahrens break; 962*fa9e4066Sahrens case GET_COL_SOURCE: 963*fa9e4066Sahrens (void) printf("%s", "SOURCE"); 964*fa9e4066Sahrens break; 965*fa9e4066Sahrens } 966*fa9e4066Sahrens } 967*fa9e4066Sahrens (void) printf("\n"); 968*fa9e4066Sahrens } 969*fa9e4066Sahrens 970*fa9e4066Sahrens free(subopts); 971*fa9e4066Sahrens for (i = 0; i < ZFS_NPROP_ALL; i++) 972*fa9e4066Sahrens if (shortsubopts[i][0]) 973*fa9e4066Sahrens free(shortsubopts[i]); 974*fa9e4066Sahrens free(shortsubopts); 975*fa9e4066Sahrens 976*fa9e4066Sahrens /* run for each object */ 977*fa9e4066Sahrens return (zfs_for_each(argc, argv, recurse, ZFS_TYPE_ANY, 978*fa9e4066Sahrens get_callback, &cb)); 979*fa9e4066Sahrens } 980*fa9e4066Sahrens 981*fa9e4066Sahrens /* 982*fa9e4066Sahrens * inherit [-r] <property> <fs|vol> ... 983*fa9e4066Sahrens * 984*fa9e4066Sahrens * -r Recurse over all children 985*fa9e4066Sahrens * 986*fa9e4066Sahrens * For each dataset specified on the command line, inherit the given property 987*fa9e4066Sahrens * from its parent. Inheriting a property at the pool level will cause it to 988*fa9e4066Sahrens * use the default value. The '-r' flag will recurse over all children, and is 989*fa9e4066Sahrens * useful for setting a property on a hierarchy-wide basis, regardless of any 990*fa9e4066Sahrens * local modifications for each dataset. 991*fa9e4066Sahrens */ 992*fa9e4066Sahrens static int 993*fa9e4066Sahrens inherit_callback(zfs_handle_t *zhp, void *data) 994*fa9e4066Sahrens { 995*fa9e4066Sahrens zfs_prop_t prop = (zfs_prop_t)data; 996*fa9e4066Sahrens 997*fa9e4066Sahrens return (zfs_prop_inherit(zhp, prop) != 0); 998*fa9e4066Sahrens } 999*fa9e4066Sahrens 1000*fa9e4066Sahrens static int 1001*fa9e4066Sahrens zfs_do_inherit(int argc, char **argv) 1002*fa9e4066Sahrens { 1003*fa9e4066Sahrens int recurse = 0; 1004*fa9e4066Sahrens int c; 1005*fa9e4066Sahrens zfs_prop_t prop; 1006*fa9e4066Sahrens char *propname; 1007*fa9e4066Sahrens 1008*fa9e4066Sahrens /* check options */ 1009*fa9e4066Sahrens while ((c = getopt(argc, argv, "r")) != -1) { 1010*fa9e4066Sahrens switch (c) { 1011*fa9e4066Sahrens case 'r': 1012*fa9e4066Sahrens recurse = TRUE; 1013*fa9e4066Sahrens break; 1014*fa9e4066Sahrens case '?': 1015*fa9e4066Sahrens default: 1016*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1017*fa9e4066Sahrens optopt); 1018*fa9e4066Sahrens usage(FALSE); 1019*fa9e4066Sahrens } 1020*fa9e4066Sahrens } 1021*fa9e4066Sahrens 1022*fa9e4066Sahrens argc -= optind; 1023*fa9e4066Sahrens argv += optind; 1024*fa9e4066Sahrens 1025*fa9e4066Sahrens /* check number of arguments */ 1026*fa9e4066Sahrens if (argc < 1) { 1027*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing property argument\n")); 1028*fa9e4066Sahrens usage(FALSE); 1029*fa9e4066Sahrens } 1030*fa9e4066Sahrens if (argc < 2) { 1031*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 1032*fa9e4066Sahrens usage(FALSE); 1033*fa9e4066Sahrens } 1034*fa9e4066Sahrens 1035*fa9e4066Sahrens propname = argv[0]; 1036*fa9e4066Sahrens 1037*fa9e4066Sahrens /* 1038*fa9e4066Sahrens * Get and validate the property before iterating over the datasets. We 1039*fa9e4066Sahrens * do this now so as to avoid printing out an error message for each and 1040*fa9e4066Sahrens * every dataset. 1041*fa9e4066Sahrens */ 1042*fa9e4066Sahrens if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) { 1043*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid property '%s'\n"), 1044*fa9e4066Sahrens propname); 1045*fa9e4066Sahrens usage(FALSE); 1046*fa9e4066Sahrens } 1047*fa9e4066Sahrens if (zfs_prop_readonly(prop)) { 1048*fa9e4066Sahrens (void) fprintf(stderr, gettext("%s property is read-only\n"), 1049*fa9e4066Sahrens propname); 1050*fa9e4066Sahrens return (1); 1051*fa9e4066Sahrens } 1052*fa9e4066Sahrens if (!zfs_prop_inheritable(prop)) { 1053*fa9e4066Sahrens (void) fprintf(stderr, gettext("%s property cannot be " 1054*fa9e4066Sahrens "inherited\n"), propname); 1055*fa9e4066Sahrens (void) fprintf(stderr, gettext("use 'zfs set %s=none' to " 1056*fa9e4066Sahrens "clear\n"), propname); 1057*fa9e4066Sahrens return (1); 1058*fa9e4066Sahrens } 1059*fa9e4066Sahrens 1060*fa9e4066Sahrens return (zfs_for_each(argc - 1, argv + 1, recurse, 1061*fa9e4066Sahrens ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, 1062*fa9e4066Sahrens inherit_callback, (void *)prop)); 1063*fa9e4066Sahrens } 1064*fa9e4066Sahrens 1065*fa9e4066Sahrens /* 1066*fa9e4066Sahrens * list [-rH] [-a | -s] [-o prop[,prop]*] [fs | vol] ... 1067*fa9e4066Sahrens * 1068*fa9e4066Sahrens * -r Recurse over all children 1069*fa9e4066Sahrens * -H Scripted mode; elide headers and separate colums by tabs 1070*fa9e4066Sahrens * -a Display all datasets 1071*fa9e4066Sahrens * -s Display only snapshots 1072*fa9e4066Sahrens * -o Control which fields to display. 1073*fa9e4066Sahrens * 1074*fa9e4066Sahrens * When given no arguments, lists all filesystems in the system. 1075*fa9e4066Sahrens * Otherwise, list the specified datasets, optionally recursing down them if 1076*fa9e4066Sahrens * '-r' is specified. 1077*fa9e4066Sahrens * 1078*fa9e4066Sahrens * If '-a' is given, then all datasets (including snapshots) are displayed. If 1079*fa9e4066Sahrens * '-s' is given, then only snapshots are displayed. Use of these options 1080*fa9e4066Sahrens * change the default set of fields output, which can still be overridden with 1081*fa9e4066Sahrens * '-o'. 1082*fa9e4066Sahrens */ 1083*fa9e4066Sahrens typedef struct list_cbdata { 1084*fa9e4066Sahrens int cb_first; 1085*fa9e4066Sahrens int cb_scripted; 1086*fa9e4066Sahrens int cb_fields[ZFS_NPROP_ALL]; 1087*fa9e4066Sahrens int cb_fieldcount; 1088*fa9e4066Sahrens } list_cbdata_t; 1089*fa9e4066Sahrens 1090*fa9e4066Sahrens /* 1091*fa9e4066Sahrens * Given a list of columns to display, output appropriate headers for each one. 1092*fa9e4066Sahrens */ 1093*fa9e4066Sahrens static void 1094*fa9e4066Sahrens print_header(int *fields, size_t count) 1095*fa9e4066Sahrens { 1096*fa9e4066Sahrens int i; 1097*fa9e4066Sahrens 1098*fa9e4066Sahrens for (i = 0; i < count; i++) { 1099*fa9e4066Sahrens if (i != 0) 1100*fa9e4066Sahrens (void) printf(" "); 1101*fa9e4066Sahrens if (i == count - 1) 1102*fa9e4066Sahrens (void) printf("%s", zfs_prop_column_name(fields[i])); 1103*fa9e4066Sahrens else /* LINTED - format specifier */ 1104*fa9e4066Sahrens (void) printf(zfs_prop_column_format(fields[i]), 1105*fa9e4066Sahrens zfs_prop_column_name(fields[i])); 1106*fa9e4066Sahrens } 1107*fa9e4066Sahrens 1108*fa9e4066Sahrens (void) printf("\n"); 1109*fa9e4066Sahrens } 1110*fa9e4066Sahrens 1111*fa9e4066Sahrens /* 1112*fa9e4066Sahrens * Given a dataset and a list of fields, print out all the properties according 1113*fa9e4066Sahrens * to the described layout. 1114*fa9e4066Sahrens */ 1115*fa9e4066Sahrens static void 1116*fa9e4066Sahrens print_dataset(zfs_handle_t *zhp, int *fields, size_t count, int scripted) 1117*fa9e4066Sahrens { 1118*fa9e4066Sahrens int i; 1119*fa9e4066Sahrens char property[ZFS_MAXPROPLEN]; 1120*fa9e4066Sahrens 1121*fa9e4066Sahrens for (i = 0; i < count; i++) { 1122*fa9e4066Sahrens if (i != 0) { 1123*fa9e4066Sahrens if (scripted) 1124*fa9e4066Sahrens (void) printf("\t"); 1125*fa9e4066Sahrens else 1126*fa9e4066Sahrens (void) printf(" "); 1127*fa9e4066Sahrens } 1128*fa9e4066Sahrens 1129*fa9e4066Sahrens if (zfs_prop_get(zhp, fields[i], property, 1130*fa9e4066Sahrens sizeof (property), NULL, NULL, 0, FALSE) != 0) 1131*fa9e4066Sahrens (void) strlcpy(property, "-", sizeof (property)); 1132*fa9e4066Sahrens 1133*fa9e4066Sahrens if (scripted || i == count - 1) 1134*fa9e4066Sahrens (void) printf("%s", property); 1135*fa9e4066Sahrens else /* LINTED - format specifier */ 1136*fa9e4066Sahrens (void) printf(zfs_prop_column_format(fields[i]), 1137*fa9e4066Sahrens property); 1138*fa9e4066Sahrens } 1139*fa9e4066Sahrens 1140*fa9e4066Sahrens (void) printf("\n"); 1141*fa9e4066Sahrens } 1142*fa9e4066Sahrens 1143*fa9e4066Sahrens /* 1144*fa9e4066Sahrens * Generic callback function to list a dataset or snapshot. 1145*fa9e4066Sahrens */ 1146*fa9e4066Sahrens static int 1147*fa9e4066Sahrens list_callback(zfs_handle_t *zhp, void *data) 1148*fa9e4066Sahrens { 1149*fa9e4066Sahrens list_cbdata_t *cbp = data; 1150*fa9e4066Sahrens 1151*fa9e4066Sahrens if (cbp->cb_first) { 1152*fa9e4066Sahrens if (!cbp->cb_scripted) 1153*fa9e4066Sahrens print_header(cbp->cb_fields, cbp->cb_fieldcount); 1154*fa9e4066Sahrens cbp->cb_first = FALSE; 1155*fa9e4066Sahrens } 1156*fa9e4066Sahrens 1157*fa9e4066Sahrens print_dataset(zhp, cbp->cb_fields, cbp->cb_fieldcount, 1158*fa9e4066Sahrens cbp->cb_scripted); 1159*fa9e4066Sahrens 1160*fa9e4066Sahrens return (0); 1161*fa9e4066Sahrens } 1162*fa9e4066Sahrens 1163*fa9e4066Sahrens static int 1164*fa9e4066Sahrens zfs_do_list(int argc, char **argv) 1165*fa9e4066Sahrens { 1166*fa9e4066Sahrens int c; 1167*fa9e4066Sahrens int recurse = 0; 1168*fa9e4066Sahrens int scripted = FALSE; 1169*fa9e4066Sahrens static char default_fields[] = 1170*fa9e4066Sahrens "name,used,available,referenced,mountpoint"; 1171*fa9e4066Sahrens int types = ZFS_TYPE_ANY; 1172*fa9e4066Sahrens char *fields = NULL; 1173*fa9e4066Sahrens char *basic_fields = default_fields; 1174*fa9e4066Sahrens list_cbdata_t cb = { 0 }; 1175*fa9e4066Sahrens char *value; 1176*fa9e4066Sahrens int ret; 1177*fa9e4066Sahrens char **subopts = zfs_prop_column_subopts(); 1178*fa9e4066Sahrens char **shortsubopts = zfs_prop_column_short_subopts(); 1179*fa9e4066Sahrens int prop; 1180*fa9e4066Sahrens char *type_subopts[] = { "filesystem", "volume", "snapshot", NULL }; 1181*fa9e4066Sahrens char *save_fields; 1182*fa9e4066Sahrens int i; 1183*fa9e4066Sahrens 1184*fa9e4066Sahrens /* check options */ 1185*fa9e4066Sahrens while ((c = getopt(argc, argv, ":o:rt:H")) != -1) { 1186*fa9e4066Sahrens switch (c) { 1187*fa9e4066Sahrens case 'o': 1188*fa9e4066Sahrens fields = optarg; 1189*fa9e4066Sahrens break; 1190*fa9e4066Sahrens case 'r': 1191*fa9e4066Sahrens recurse = TRUE; 1192*fa9e4066Sahrens break; 1193*fa9e4066Sahrens case 'H': 1194*fa9e4066Sahrens scripted = TRUE; 1195*fa9e4066Sahrens break; 1196*fa9e4066Sahrens case 't': 1197*fa9e4066Sahrens types = 0; 1198*fa9e4066Sahrens while (*optarg != '\0') { 1199*fa9e4066Sahrens switch (getsubopt(&optarg, type_subopts, 1200*fa9e4066Sahrens &value)) { 1201*fa9e4066Sahrens case 0: 1202*fa9e4066Sahrens types |= ZFS_TYPE_FILESYSTEM; 1203*fa9e4066Sahrens break; 1204*fa9e4066Sahrens case 1: 1205*fa9e4066Sahrens types |= ZFS_TYPE_VOLUME; 1206*fa9e4066Sahrens break; 1207*fa9e4066Sahrens case 2: 1208*fa9e4066Sahrens types |= ZFS_TYPE_SNAPSHOT; 1209*fa9e4066Sahrens break; 1210*fa9e4066Sahrens default: 1211*fa9e4066Sahrens (void) fprintf(stderr, 1212*fa9e4066Sahrens gettext("invalid type '%s'\n"), 1213*fa9e4066Sahrens value); 1214*fa9e4066Sahrens usage(FALSE); 1215*fa9e4066Sahrens } 1216*fa9e4066Sahrens } 1217*fa9e4066Sahrens break; 1218*fa9e4066Sahrens case ':': 1219*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing argument for " 1220*fa9e4066Sahrens "'%c' option\n"), optopt); 1221*fa9e4066Sahrens usage(FALSE); 1222*fa9e4066Sahrens break; 1223*fa9e4066Sahrens case '?': 1224*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1225*fa9e4066Sahrens optopt); 1226*fa9e4066Sahrens usage(FALSE); 1227*fa9e4066Sahrens } 1228*fa9e4066Sahrens } 1229*fa9e4066Sahrens 1230*fa9e4066Sahrens argc -= optind; 1231*fa9e4066Sahrens argv += optind; 1232*fa9e4066Sahrens 1233*fa9e4066Sahrens if (fields == NULL) 1234*fa9e4066Sahrens fields = basic_fields; 1235*fa9e4066Sahrens 1236*fa9e4066Sahrens while (*fields != '\0') { 1237*fa9e4066Sahrens if (cb.cb_fieldcount == ZFS_NPROP_ALL) { 1238*fa9e4066Sahrens (void) fprintf(stderr, gettext("too many " 1239*fa9e4066Sahrens "properties given to -o option\n")); 1240*fa9e4066Sahrens usage(FALSE); 1241*fa9e4066Sahrens } 1242*fa9e4066Sahrens 1243*fa9e4066Sahrens save_fields = fields; 1244*fa9e4066Sahrens if ((prop = getsubopt(&fields, subopts, &value)) == -1) { 1245*fa9e4066Sahrens fields = save_fields; 1246*fa9e4066Sahrens prop = getsubopt(&fields, shortsubopts, &value); 1247*fa9e4066Sahrens } 1248*fa9e4066Sahrens 1249*fa9e4066Sahrens if (prop == -1) { 1250*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid property " 1251*fa9e4066Sahrens "'%s'\n"), value); 1252*fa9e4066Sahrens usage(FALSE); 1253*fa9e4066Sahrens } 1254*fa9e4066Sahrens 1255*fa9e4066Sahrens if (zfs_prop_column_name(prop) == NULL) { 1256*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid property " 1257*fa9e4066Sahrens "'%s'\n"), zfs_prop_to_name(prop)); 1258*fa9e4066Sahrens usage(FALSE); 1259*fa9e4066Sahrens } 1260*fa9e4066Sahrens 1261*fa9e4066Sahrens cb.cb_fields[cb.cb_fieldcount++] = prop; 1262*fa9e4066Sahrens } 1263*fa9e4066Sahrens 1264*fa9e4066Sahrens cb.cb_scripted = scripted; 1265*fa9e4066Sahrens cb.cb_first = TRUE; 1266*fa9e4066Sahrens 1267*fa9e4066Sahrens ret = zfs_for_each(argc, argv, recurse, types, list_callback, &cb); 1268*fa9e4066Sahrens 1269*fa9e4066Sahrens if (ret == 0 && cb.cb_first == TRUE) 1270*fa9e4066Sahrens (void) printf(gettext("no datasets available\n")); 1271*fa9e4066Sahrens 1272*fa9e4066Sahrens free(subopts); 1273*fa9e4066Sahrens for (i = 0; i < ZFS_NPROP_ALL; i++) 1274*fa9e4066Sahrens if (shortsubopts[i][0]) 1275*fa9e4066Sahrens free(shortsubopts[i]); 1276*fa9e4066Sahrens free(shortsubopts); 1277*fa9e4066Sahrens 1278*fa9e4066Sahrens return (ret); 1279*fa9e4066Sahrens } 1280*fa9e4066Sahrens 1281*fa9e4066Sahrens /* 1282*fa9e4066Sahrens * zfs rename <fs | snap | vol> <fs | snap | vol> 1283*fa9e4066Sahrens * 1284*fa9e4066Sahrens * Renames the given dataset to another of the same type. 1285*fa9e4066Sahrens */ 1286*fa9e4066Sahrens /* ARGSUSED */ 1287*fa9e4066Sahrens static int 1288*fa9e4066Sahrens zfs_do_rename(int argc, char **argv) 1289*fa9e4066Sahrens { 1290*fa9e4066Sahrens zfs_handle_t *zhp; 1291*fa9e4066Sahrens int ret = 1; 1292*fa9e4066Sahrens 1293*fa9e4066Sahrens /* check options */ 1294*fa9e4066Sahrens if (argc > 1 && argv[1][0] == '-') { 1295*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1296*fa9e4066Sahrens argv[1][1]); 1297*fa9e4066Sahrens usage(FALSE); 1298*fa9e4066Sahrens } 1299*fa9e4066Sahrens 1300*fa9e4066Sahrens /* check number of arguments */ 1301*fa9e4066Sahrens if (argc < 2) { 1302*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing source dataset " 1303*fa9e4066Sahrens "argument\n")); 1304*fa9e4066Sahrens usage(FALSE); 1305*fa9e4066Sahrens } 1306*fa9e4066Sahrens if (argc < 3) { 1307*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing target dataset " 1308*fa9e4066Sahrens "argument\n")); 1309*fa9e4066Sahrens usage(FALSE); 1310*fa9e4066Sahrens } 1311*fa9e4066Sahrens if (argc > 3) { 1312*fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 1313*fa9e4066Sahrens usage(FALSE); 1314*fa9e4066Sahrens } 1315*fa9e4066Sahrens 1316*fa9e4066Sahrens if ((zhp = zfs_open(argv[1], ZFS_TYPE_ANY)) == NULL) 1317*fa9e4066Sahrens return (1); 1318*fa9e4066Sahrens 1319*fa9e4066Sahrens if (zfs_rename(zhp, argv[2]) != 0) 1320*fa9e4066Sahrens goto error; 1321*fa9e4066Sahrens 1322*fa9e4066Sahrens ret = 0; 1323*fa9e4066Sahrens error: 1324*fa9e4066Sahrens zfs_close(zhp); 1325*fa9e4066Sahrens return (ret); 1326*fa9e4066Sahrens } 1327*fa9e4066Sahrens 1328*fa9e4066Sahrens /* 1329*fa9e4066Sahrens * zfs rollback [-rfR] <snapshot> 1330*fa9e4066Sahrens * 1331*fa9e4066Sahrens * -r Delete any intervening snapshots before doing rollback 1332*fa9e4066Sahrens * -R Delete any snapshots and their clones 1333*fa9e4066Sahrens * -f Force unmount filesystems, even if they are in use. 1334*fa9e4066Sahrens * 1335*fa9e4066Sahrens * Given a filesystem, rollback to a specific snapshot, discarding any changes 1336*fa9e4066Sahrens * since then and making it the active dataset. If more recent snapshots exist, 1337*fa9e4066Sahrens * the command will complain unless the '-r' flag is given. 1338*fa9e4066Sahrens */ 1339*fa9e4066Sahrens typedef struct rollback_cbdata { 1340*fa9e4066Sahrens uint64_t cb_create; 1341*fa9e4066Sahrens int cb_first; 1342*fa9e4066Sahrens int cb_force; 1343*fa9e4066Sahrens int cb_doclones; 1344*fa9e4066Sahrens char *cb_target; 1345*fa9e4066Sahrens int cb_error; 1346*fa9e4066Sahrens int cb_recurse; 1347*fa9e4066Sahrens int cb_dependent; 1348*fa9e4066Sahrens } rollback_cbdata_t; 1349*fa9e4066Sahrens 1350*fa9e4066Sahrens /* 1351*fa9e4066Sahrens * Report any snapshots more recent than the one specified. Used when '-r' is 1352*fa9e4066Sahrens * not specified. We reuse this same callback for the snapshot dependents - if 1353*fa9e4066Sahrens * 'cb_dependent' is set, then this is a dependent and we should report it 1354*fa9e4066Sahrens * without checking the transaction group. 1355*fa9e4066Sahrens */ 1356*fa9e4066Sahrens static int 1357*fa9e4066Sahrens rollback_check(zfs_handle_t *zhp, void *data) 1358*fa9e4066Sahrens { 1359*fa9e4066Sahrens rollback_cbdata_t *cbp = data; 1360*fa9e4066Sahrens 1361*fa9e4066Sahrens if (cbp->cb_doclones) 1362*fa9e4066Sahrens return (0); 1363*fa9e4066Sahrens 1364*fa9e4066Sahrens if (!cbp->cb_dependent) { 1365*fa9e4066Sahrens if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && 1366*fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 1367*fa9e4066Sahrens cbp->cb_create) { 1368*fa9e4066Sahrens 1369*fa9e4066Sahrens if (cbp->cb_first && !cbp->cb_recurse) { 1370*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot " 1371*fa9e4066Sahrens "rollback to '%s': more recent snapshots " 1372*fa9e4066Sahrens "exist\n"), 1373*fa9e4066Sahrens cbp->cb_target); 1374*fa9e4066Sahrens (void) fprintf(stderr, gettext("use '-r' to " 1375*fa9e4066Sahrens "force deletion of the following " 1376*fa9e4066Sahrens "snapshots:\n")); 1377*fa9e4066Sahrens cbp->cb_first = 0; 1378*fa9e4066Sahrens cbp->cb_error = 1; 1379*fa9e4066Sahrens } 1380*fa9e4066Sahrens 1381*fa9e4066Sahrens if (cbp->cb_recurse) { 1382*fa9e4066Sahrens cbp->cb_dependent = TRUE; 1383*fa9e4066Sahrens (void) zfs_iter_dependents(zhp, rollback_check, 1384*fa9e4066Sahrens cbp); 1385*fa9e4066Sahrens cbp->cb_dependent = FALSE; 1386*fa9e4066Sahrens } else { 1387*fa9e4066Sahrens (void) fprintf(stderr, "%s\n", 1388*fa9e4066Sahrens zfs_get_name(zhp)); 1389*fa9e4066Sahrens } 1390*fa9e4066Sahrens } 1391*fa9e4066Sahrens } else { 1392*fa9e4066Sahrens if (cbp->cb_first && cbp->cb_recurse) { 1393*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot rollback to " 1394*fa9e4066Sahrens "'%s': clones of previous snapshots exist\n"), 1395*fa9e4066Sahrens cbp->cb_target); 1396*fa9e4066Sahrens (void) fprintf(stderr, gettext("use '-R' to " 1397*fa9e4066Sahrens "force deletion of the following clones and " 1398*fa9e4066Sahrens "dependents:\n")); 1399*fa9e4066Sahrens cbp->cb_first = 0; 1400*fa9e4066Sahrens cbp->cb_error = 1; 1401*fa9e4066Sahrens } 1402*fa9e4066Sahrens 1403*fa9e4066Sahrens (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1404*fa9e4066Sahrens } 1405*fa9e4066Sahrens 1406*fa9e4066Sahrens zfs_close(zhp); 1407*fa9e4066Sahrens return (0); 1408*fa9e4066Sahrens } 1409*fa9e4066Sahrens 1410*fa9e4066Sahrens /* 1411*fa9e4066Sahrens * Unmount any filesystems or snapshots that will need to be destroyed as part 1412*fa9e4066Sahrens * of the rollback process. 1413*fa9e4066Sahrens */ 1414*fa9e4066Sahrens static int 1415*fa9e4066Sahrens rollback_unmount(zfs_handle_t *zhp, void *data) 1416*fa9e4066Sahrens { 1417*fa9e4066Sahrens rollback_cbdata_t *cbp = data; 1418*fa9e4066Sahrens 1419*fa9e4066Sahrens if (!cbp->cb_dependent) { 1420*fa9e4066Sahrens if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && 1421*fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 1422*fa9e4066Sahrens cbp->cb_create) { 1423*fa9e4066Sahrens 1424*fa9e4066Sahrens cbp->cb_dependent = TRUE; 1425*fa9e4066Sahrens (void) zfs_iter_dependents(zhp, rollback_unmount, cbp); 1426*fa9e4066Sahrens cbp->cb_dependent = FALSE; 1427*fa9e4066Sahrens 1428*fa9e4066Sahrens if (zfs_unmount(zhp, NULL, 1429*fa9e4066Sahrens cbp->cb_force ? MS_FORCE: 0) != 0) 1430*fa9e4066Sahrens cbp->cb_error = 1; 1431*fa9e4066Sahrens } 1432*fa9e4066Sahrens } else if (zfs_unmount(zhp, NULL, cbp->cb_force ? MS_FORCE : 0) != 0) { 1433*fa9e4066Sahrens cbp->cb_error = 1; 1434*fa9e4066Sahrens } 1435*fa9e4066Sahrens 1436*fa9e4066Sahrens zfs_close(zhp); 1437*fa9e4066Sahrens return (0); 1438*fa9e4066Sahrens } 1439*fa9e4066Sahrens 1440*fa9e4066Sahrens /* 1441*fa9e4066Sahrens * Destroy any more recent snapshots. We invoke this callback on any dependents 1442*fa9e4066Sahrens * of the snapshot first. If the 'cb_dependent' member is non-zero, then this 1443*fa9e4066Sahrens * is a dependent and we should just destroy it without checking the transaction 1444*fa9e4066Sahrens * group. 1445*fa9e4066Sahrens */ 1446*fa9e4066Sahrens static int 1447*fa9e4066Sahrens rollback_destroy(zfs_handle_t *zhp, void *data) 1448*fa9e4066Sahrens { 1449*fa9e4066Sahrens rollback_cbdata_t *cbp = data; 1450*fa9e4066Sahrens 1451*fa9e4066Sahrens if (!cbp->cb_dependent) { 1452*fa9e4066Sahrens if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && 1453*fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 1454*fa9e4066Sahrens cbp->cb_create) { 1455*fa9e4066Sahrens 1456*fa9e4066Sahrens cbp->cb_dependent = TRUE; 1457*fa9e4066Sahrens (void) zfs_iter_dependents(zhp, rollback_destroy, cbp); 1458*fa9e4066Sahrens cbp->cb_dependent = FALSE; 1459*fa9e4066Sahrens 1460*fa9e4066Sahrens if (zfs_destroy(zhp) != 0) 1461*fa9e4066Sahrens cbp->cb_error = 1; 1462*fa9e4066Sahrens } 1463*fa9e4066Sahrens } else if (zfs_destroy(zhp) != 0) { 1464*fa9e4066Sahrens cbp->cb_error = 1; 1465*fa9e4066Sahrens } 1466*fa9e4066Sahrens 1467*fa9e4066Sahrens zfs_close(zhp); 1468*fa9e4066Sahrens return (0); 1469*fa9e4066Sahrens } 1470*fa9e4066Sahrens 1471*fa9e4066Sahrens static int 1472*fa9e4066Sahrens zfs_do_rollback(int argc, char **argv) 1473*fa9e4066Sahrens { 1474*fa9e4066Sahrens int ret; 1475*fa9e4066Sahrens int c; 1476*fa9e4066Sahrens rollback_cbdata_t cb = { 0 }; 1477*fa9e4066Sahrens int was_mounted; 1478*fa9e4066Sahrens zfs_handle_t *zhp, *snap; 1479*fa9e4066Sahrens char parentname[ZFS_MAXNAMELEN]; 1480*fa9e4066Sahrens char *delim; 1481*fa9e4066Sahrens 1482*fa9e4066Sahrens /* check options */ 1483*fa9e4066Sahrens while ((c = getopt(argc, argv, "rfR")) != -1) { 1484*fa9e4066Sahrens switch (c) { 1485*fa9e4066Sahrens case 'f': 1486*fa9e4066Sahrens cb.cb_force = TRUE; 1487*fa9e4066Sahrens break; 1488*fa9e4066Sahrens case 'r': 1489*fa9e4066Sahrens cb.cb_recurse = 1; 1490*fa9e4066Sahrens break; 1491*fa9e4066Sahrens case 'R': 1492*fa9e4066Sahrens cb.cb_recurse = 1; 1493*fa9e4066Sahrens cb.cb_doclones = 1; 1494*fa9e4066Sahrens break; 1495*fa9e4066Sahrens case '?': 1496*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1497*fa9e4066Sahrens optopt); 1498*fa9e4066Sahrens usage(FALSE); 1499*fa9e4066Sahrens } 1500*fa9e4066Sahrens } 1501*fa9e4066Sahrens 1502*fa9e4066Sahrens argc -= optind; 1503*fa9e4066Sahrens argv += optind; 1504*fa9e4066Sahrens 1505*fa9e4066Sahrens /* check number of arguments */ 1506*fa9e4066Sahrens if (argc < 1) { 1507*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing dataset argument\n")); 1508*fa9e4066Sahrens usage(FALSE); 1509*fa9e4066Sahrens } 1510*fa9e4066Sahrens if (argc > 1) { 1511*fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 1512*fa9e4066Sahrens usage(FALSE); 1513*fa9e4066Sahrens } 1514*fa9e4066Sahrens 1515*fa9e4066Sahrens cb.cb_target = argv[0]; 1516*fa9e4066Sahrens 1517*fa9e4066Sahrens /* open the snapshot */ 1518*fa9e4066Sahrens if ((snap = zfs_open(cb.cb_target, ZFS_TYPE_SNAPSHOT)) == NULL) 1519*fa9e4066Sahrens return (1); 1520*fa9e4066Sahrens 1521*fa9e4066Sahrens (void) strlcpy(parentname, cb.cb_target, sizeof (parentname)); 1522*fa9e4066Sahrens verify((delim = strrchr(parentname, '@')) != NULL); 1523*fa9e4066Sahrens *delim = '\0'; 1524*fa9e4066Sahrens if ((zhp = zfs_open(parentname, ZFS_TYPE_ANY)) == NULL) { 1525*fa9e4066Sahrens zfs_close(snap); 1526*fa9e4066Sahrens return (1); 1527*fa9e4066Sahrens } 1528*fa9e4066Sahrens 1529*fa9e4066Sahrens /* See if this dataset is mounted */ 1530*fa9e4066Sahrens was_mounted = zfs_is_mounted(zhp, NULL); 1531*fa9e4066Sahrens 1532*fa9e4066Sahrens cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 1533*fa9e4066Sahrens 1534*fa9e4066Sahrens /* 1535*fa9e4066Sahrens * Check for more recent snapshots and/or clones based on the presence 1536*fa9e4066Sahrens * of '-r' and '-R'. 1537*fa9e4066Sahrens */ 1538*fa9e4066Sahrens cb.cb_first = 1; 1539*fa9e4066Sahrens cb.cb_error = 0; 1540*fa9e4066Sahrens (void) zfs_iter_children(zhp, rollback_check, &cb); 1541*fa9e4066Sahrens 1542*fa9e4066Sahrens if ((ret = cb.cb_error) != 0) 1543*fa9e4066Sahrens goto out; 1544*fa9e4066Sahrens 1545*fa9e4066Sahrens cb.cb_error = 0; 1546*fa9e4066Sahrens 1547*fa9e4066Sahrens /* 1548*fa9e4066Sahrens * Unmount any snapshots as well as the dataset itself. 1549*fa9e4066Sahrens */ 1550*fa9e4066Sahrens if ((ret = zfs_iter_children(zhp, rollback_unmount, 1551*fa9e4066Sahrens &cb)) != 0 || (ret = zfs_unmount(zhp, NULL, 1552*fa9e4066Sahrens cb.cb_force ? MS_FORCE : 0)) != 0) 1553*fa9e4066Sahrens goto out; 1554*fa9e4066Sahrens 1555*fa9e4066Sahrens (void) zfs_iter_children(zhp, rollback_destroy, &cb); 1556*fa9e4066Sahrens 1557*fa9e4066Sahrens if ((ret = cb.cb_error) != 0) 1558*fa9e4066Sahrens goto out; 1559*fa9e4066Sahrens 1560*fa9e4066Sahrens /* 1561*fa9e4066Sahrens * Now that we have verified that the snapshot is the latest, rollback 1562*fa9e4066Sahrens * to the given snapshot. 1563*fa9e4066Sahrens */ 1564*fa9e4066Sahrens ret = zfs_rollback(zhp); 1565*fa9e4066Sahrens 1566*fa9e4066Sahrens /* 1567*fa9e4066Sahrens * We only want to re-mount the filesystem if it was mounted in the 1568*fa9e4066Sahrens * first place. 1569*fa9e4066Sahrens */ 1570*fa9e4066Sahrens if (was_mounted) 1571*fa9e4066Sahrens (void) zfs_mount(zhp, NULL, 0); 1572*fa9e4066Sahrens 1573*fa9e4066Sahrens out: 1574*fa9e4066Sahrens zfs_close(snap); 1575*fa9e4066Sahrens zfs_close(zhp); 1576*fa9e4066Sahrens 1577*fa9e4066Sahrens if (ret == 0) 1578*fa9e4066Sahrens return (0); 1579*fa9e4066Sahrens else 1580*fa9e4066Sahrens return (1); 1581*fa9e4066Sahrens } 1582*fa9e4066Sahrens 1583*fa9e4066Sahrens /* 1584*fa9e4066Sahrens * zfs set property=value { fs | snap | vol } ... 1585*fa9e4066Sahrens * 1586*fa9e4066Sahrens * Sets the given property for all datasets specified on the command line. 1587*fa9e4066Sahrens */ 1588*fa9e4066Sahrens typedef struct set_cbdata { 1589*fa9e4066Sahrens char *cb_propname; 1590*fa9e4066Sahrens char *cb_value; 1591*fa9e4066Sahrens zfs_prop_t cb_prop; 1592*fa9e4066Sahrens } set_cbdata_t; 1593*fa9e4066Sahrens 1594*fa9e4066Sahrens static int 1595*fa9e4066Sahrens set_callback(zfs_handle_t *zhp, void *data) 1596*fa9e4066Sahrens { 1597*fa9e4066Sahrens set_cbdata_t *cbp = data; 1598*fa9e4066Sahrens int ret = 1; 1599*fa9e4066Sahrens 1600*fa9e4066Sahrens /* don't allow setting of properties for snapshots */ 1601*fa9e4066Sahrens if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { 1602*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot set %s property for " 1603*fa9e4066Sahrens "'%s': snapshot properties cannot be modified\n"), 1604*fa9e4066Sahrens cbp->cb_propname, zfs_get_name(zhp)); 1605*fa9e4066Sahrens return (1); 1606*fa9e4066Sahrens } 1607*fa9e4066Sahrens 1608*fa9e4066Sahrens /* 1609*fa9e4066Sahrens * If we're changing the volsize, and the volsize and reservation are 1610*fa9e4066Sahrens * the same, then change the reservation as well. 1611*fa9e4066Sahrens */ 1612*fa9e4066Sahrens if (cbp->cb_prop == ZFS_PROP_VOLSIZE && 1613*fa9e4066Sahrens zfs_get_type(zhp) == ZFS_TYPE_VOLUME && 1614*fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE) == 1615*fa9e4066Sahrens zfs_prop_get_int(zhp, ZFS_PROP_RESERVATION)) { 1616*fa9e4066Sahrens uint64_t volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1617*fa9e4066Sahrens uint64_t avail = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE); 1618*fa9e4066Sahrens uint64_t value; 1619*fa9e4066Sahrens 1620*fa9e4066Sahrens verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); 1621*fa9e4066Sahrens 1622*fa9e4066Sahrens /* 1623*fa9e4066Sahrens * Warn about raising the volume size greater than the amount of 1624*fa9e4066Sahrens * available space. 1625*fa9e4066Sahrens */ 1626*fa9e4066Sahrens if (value > volsize && (value - volsize) > avail) { 1627*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot set " 1628*fa9e4066Sahrens "%s property for '%s': volume size exceeds " 1629*fa9e4066Sahrens "amount of available space\n"), 1630*fa9e4066Sahrens cbp->cb_propname, zfs_get_name(zhp)); 1631*fa9e4066Sahrens return (1); 1632*fa9e4066Sahrens } 1633*fa9e4066Sahrens 1634*fa9e4066Sahrens if (zfs_prop_set(zhp, ZFS_PROP_RESERVATION, 1635*fa9e4066Sahrens cbp->cb_value) != 0) { 1636*fa9e4066Sahrens (void) fprintf(stderr, gettext("volsize and " 1637*fa9e4066Sahrens "reservation must remain equal\n")); 1638*fa9e4066Sahrens return (1); 1639*fa9e4066Sahrens } 1640*fa9e4066Sahrens } 1641*fa9e4066Sahrens 1642*fa9e4066Sahrens /* 1643*fa9e4066Sahrens * Do not allow the reservation to be set above the volume size. We do 1644*fa9e4066Sahrens * this here instead of inside libzfs because libzfs violates this rule 1645*fa9e4066Sahrens * internally. 1646*fa9e4066Sahrens */ 1647*fa9e4066Sahrens if (cbp->cb_prop == ZFS_PROP_RESERVATION && 1648*fa9e4066Sahrens zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 1649*fa9e4066Sahrens uint64_t value; 1650*fa9e4066Sahrens uint64_t volsize; 1651*fa9e4066Sahrens 1652*fa9e4066Sahrens volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE); 1653*fa9e4066Sahrens if (strcmp(cbp->cb_value, "none") == 0) 1654*fa9e4066Sahrens value = 0; 1655*fa9e4066Sahrens else 1656*fa9e4066Sahrens verify(zfs_nicestrtonum(cbp->cb_value, &value) == 0); 1657*fa9e4066Sahrens 1658*fa9e4066Sahrens if (value > volsize) { 1659*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot set %s " 1660*fa9e4066Sahrens "for '%s': size is greater than current " 1661*fa9e4066Sahrens "volume size\n"), cbp->cb_propname, 1662*fa9e4066Sahrens zfs_get_name(zhp)); 1663*fa9e4066Sahrens return (-1); 1664*fa9e4066Sahrens } 1665*fa9e4066Sahrens } 1666*fa9e4066Sahrens 1667*fa9e4066Sahrens if (zfs_prop_set(zhp, cbp->cb_prop, cbp->cb_value) != 0) 1668*fa9e4066Sahrens return (1); 1669*fa9e4066Sahrens 1670*fa9e4066Sahrens ret = 0; 1671*fa9e4066Sahrens error: 1672*fa9e4066Sahrens return (ret); 1673*fa9e4066Sahrens } 1674*fa9e4066Sahrens 1675*fa9e4066Sahrens static int 1676*fa9e4066Sahrens zfs_do_set(int argc, char **argv) 1677*fa9e4066Sahrens { 1678*fa9e4066Sahrens set_cbdata_t cb; 1679*fa9e4066Sahrens 1680*fa9e4066Sahrens /* check for options */ 1681*fa9e4066Sahrens if (argc > 1 && argv[1][0] == '-') { 1682*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1683*fa9e4066Sahrens argv[1][1]); 1684*fa9e4066Sahrens usage(FALSE); 1685*fa9e4066Sahrens } 1686*fa9e4066Sahrens 1687*fa9e4066Sahrens /* check number of arguments */ 1688*fa9e4066Sahrens if (argc < 2) { 1689*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing property=value " 1690*fa9e4066Sahrens "argument\n")); 1691*fa9e4066Sahrens usage(FALSE); 1692*fa9e4066Sahrens } 1693*fa9e4066Sahrens if (argc < 3) { 1694*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing dataset name\n")); 1695*fa9e4066Sahrens usage(FALSE); 1696*fa9e4066Sahrens } 1697*fa9e4066Sahrens 1698*fa9e4066Sahrens /* validate property=value argument */ 1699*fa9e4066Sahrens cb.cb_propname = argv[1]; 1700*fa9e4066Sahrens if ((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) { 1701*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing value in " 1702*fa9e4066Sahrens "property=value argument\n")); 1703*fa9e4066Sahrens usage(FALSE); 1704*fa9e4066Sahrens } 1705*fa9e4066Sahrens 1706*fa9e4066Sahrens *cb.cb_value = '\0'; 1707*fa9e4066Sahrens cb.cb_value++; 1708*fa9e4066Sahrens 1709*fa9e4066Sahrens if (*cb.cb_propname == '\0') { 1710*fa9e4066Sahrens (void) fprintf(stderr, 1711*fa9e4066Sahrens gettext("missing property in property=value argument\n")); 1712*fa9e4066Sahrens usage(FALSE); 1713*fa9e4066Sahrens } 1714*fa9e4066Sahrens if (*cb.cb_value == '\0') { 1715*fa9e4066Sahrens (void) fprintf(stderr, 1716*fa9e4066Sahrens gettext("missing value in property=value argument\n")); 1717*fa9e4066Sahrens usage(FALSE); 1718*fa9e4066Sahrens } 1719*fa9e4066Sahrens 1720*fa9e4066Sahrens /* get the property type */ 1721*fa9e4066Sahrens if ((cb.cb_prop = zfs_name_to_prop(cb.cb_propname)) == 1722*fa9e4066Sahrens ZFS_PROP_INVAL) { 1723*fa9e4066Sahrens (void) fprintf(stderr, 1724*fa9e4066Sahrens gettext("invalid property '%s'\n"), cb.cb_propname); 1725*fa9e4066Sahrens usage(FALSE); 1726*fa9e4066Sahrens } 1727*fa9e4066Sahrens 1728*fa9e4066Sahrens /* 1729*fa9e4066Sahrens * Validate that the value is appropriate for this property. We do this 1730*fa9e4066Sahrens * once now so we don't generate multiple errors each time we try to 1731*fa9e4066Sahrens * apply it to a dataset. 1732*fa9e4066Sahrens */ 1733*fa9e4066Sahrens if (zfs_prop_validate(cb.cb_prop, cb.cb_value, NULL) != 0) 1734*fa9e4066Sahrens return (1); 1735*fa9e4066Sahrens 1736*fa9e4066Sahrens return (zfs_for_each(argc - 2, argv + 2, FALSE, 1737*fa9e4066Sahrens ZFS_TYPE_ANY, set_callback, &cb)); 1738*fa9e4066Sahrens } 1739*fa9e4066Sahrens 1740*fa9e4066Sahrens /* 1741*fa9e4066Sahrens * zfs snapshot <fs@snap> 1742*fa9e4066Sahrens * 1743*fa9e4066Sahrens * Creates a snapshot with the given name. While functionally equivalent to 1744*fa9e4066Sahrens * 'zfs create', it is a separate command to diffferentiate intent. 1745*fa9e4066Sahrens */ 1746*fa9e4066Sahrens static int 1747*fa9e4066Sahrens zfs_do_snapshot(int argc, char **argv) 1748*fa9e4066Sahrens { 1749*fa9e4066Sahrens /* check options */ 1750*fa9e4066Sahrens if (argc > 1 && argv[1][0] == '-') { 1751*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1752*fa9e4066Sahrens argv[1][1]); 1753*fa9e4066Sahrens usage(FALSE); 1754*fa9e4066Sahrens } 1755*fa9e4066Sahrens 1756*fa9e4066Sahrens /* check number of arguments */ 1757*fa9e4066Sahrens if (argc < 2) { 1758*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 1759*fa9e4066Sahrens usage(FALSE); 1760*fa9e4066Sahrens } 1761*fa9e4066Sahrens if (argc > 2) { 1762*fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 1763*fa9e4066Sahrens usage(FALSE); 1764*fa9e4066Sahrens } 1765*fa9e4066Sahrens 1766*fa9e4066Sahrens return (zfs_snapshot(argv[1]) != 0); 1767*fa9e4066Sahrens } 1768*fa9e4066Sahrens 1769*fa9e4066Sahrens /* 1770*fa9e4066Sahrens * zfs backup [-i <fs@snap>] <fs@snap> 1771*fa9e4066Sahrens * 1772*fa9e4066Sahrens * Send a backup stream to stdout. 1773*fa9e4066Sahrens */ 1774*fa9e4066Sahrens static int 1775*fa9e4066Sahrens zfs_do_backup(int argc, char **argv) 1776*fa9e4066Sahrens { 1777*fa9e4066Sahrens char *fromname = NULL; 1778*fa9e4066Sahrens zfs_handle_t *zhp_from = NULL, *zhp_to; 1779*fa9e4066Sahrens int c, err; 1780*fa9e4066Sahrens 1781*fa9e4066Sahrens /* check options */ 1782*fa9e4066Sahrens while ((c = getopt(argc, argv, ":i:")) != -1) { 1783*fa9e4066Sahrens switch (c) { 1784*fa9e4066Sahrens case 'i': 1785*fa9e4066Sahrens fromname = optarg; 1786*fa9e4066Sahrens break; 1787*fa9e4066Sahrens case ':': 1788*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing argument for " 1789*fa9e4066Sahrens "'%c' option\n"), optopt); 1790*fa9e4066Sahrens usage(FALSE); 1791*fa9e4066Sahrens break; 1792*fa9e4066Sahrens case '?': 1793*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1794*fa9e4066Sahrens optopt); 1795*fa9e4066Sahrens usage(FALSE); 1796*fa9e4066Sahrens } 1797*fa9e4066Sahrens } 1798*fa9e4066Sahrens 1799*fa9e4066Sahrens argc -= optind; 1800*fa9e4066Sahrens argv += optind; 1801*fa9e4066Sahrens 1802*fa9e4066Sahrens /* check number of arguments */ 1803*fa9e4066Sahrens if (argc < 1) { 1804*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 1805*fa9e4066Sahrens usage(FALSE); 1806*fa9e4066Sahrens } 1807*fa9e4066Sahrens if (argc > 1) { 1808*fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 1809*fa9e4066Sahrens usage(FALSE); 1810*fa9e4066Sahrens } 1811*fa9e4066Sahrens 1812*fa9e4066Sahrens if (isatty(STDOUT_FILENO)) { 1813*fa9e4066Sahrens (void) fprintf(stderr, 1814*fa9e4066Sahrens gettext("Error: Backup stream can not be written " 1815*fa9e4066Sahrens "to a terminal.\n" 1816*fa9e4066Sahrens "You must redirect standard output.\n")); 1817*fa9e4066Sahrens return (1); 1818*fa9e4066Sahrens } 1819*fa9e4066Sahrens 1820*fa9e4066Sahrens if (fromname) { 1821*fa9e4066Sahrens if ((zhp_from = zfs_open(fromname, ZFS_TYPE_SNAPSHOT)) == NULL) 1822*fa9e4066Sahrens return (1); 1823*fa9e4066Sahrens } 1824*fa9e4066Sahrens if ((zhp_to = zfs_open(argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 1825*fa9e4066Sahrens return (1); 1826*fa9e4066Sahrens 1827*fa9e4066Sahrens err = zfs_backup(zhp_to, zhp_from); 1828*fa9e4066Sahrens 1829*fa9e4066Sahrens if (zhp_from) 1830*fa9e4066Sahrens zfs_close(zhp_from); 1831*fa9e4066Sahrens zfs_close(zhp_to); 1832*fa9e4066Sahrens 1833*fa9e4066Sahrens return (err != 0); 1834*fa9e4066Sahrens } 1835*fa9e4066Sahrens 1836*fa9e4066Sahrens /* 1837*fa9e4066Sahrens * zfs restore <fs@snap> 1838*fa9e4066Sahrens * 1839*fa9e4066Sahrens * Restore a backup stream from stdin. 1840*fa9e4066Sahrens */ 1841*fa9e4066Sahrens static int 1842*fa9e4066Sahrens zfs_do_restore(int argc, char **argv) 1843*fa9e4066Sahrens { 1844*fa9e4066Sahrens int c, err; 1845*fa9e4066Sahrens int isprefix = FALSE; 1846*fa9e4066Sahrens int dryrun = FALSE; 1847*fa9e4066Sahrens int verbose = FALSE; 1848*fa9e4066Sahrens 1849*fa9e4066Sahrens /* check options */ 1850*fa9e4066Sahrens while ((c = getopt(argc, argv, ":dnv")) != -1) { 1851*fa9e4066Sahrens switch (c) { 1852*fa9e4066Sahrens case 'd': 1853*fa9e4066Sahrens isprefix = TRUE; 1854*fa9e4066Sahrens break; 1855*fa9e4066Sahrens case 'n': 1856*fa9e4066Sahrens dryrun = TRUE; 1857*fa9e4066Sahrens break; 1858*fa9e4066Sahrens case 'v': 1859*fa9e4066Sahrens verbose = TRUE; 1860*fa9e4066Sahrens break; 1861*fa9e4066Sahrens case ':': 1862*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing argument for " 1863*fa9e4066Sahrens "'%c' option\n"), optopt); 1864*fa9e4066Sahrens usage(FALSE); 1865*fa9e4066Sahrens break; 1866*fa9e4066Sahrens case '?': 1867*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1868*fa9e4066Sahrens optopt); 1869*fa9e4066Sahrens usage(FALSE); 1870*fa9e4066Sahrens } 1871*fa9e4066Sahrens } 1872*fa9e4066Sahrens 1873*fa9e4066Sahrens argc -= optind; 1874*fa9e4066Sahrens argv += optind; 1875*fa9e4066Sahrens 1876*fa9e4066Sahrens /* check number of arguments */ 1877*fa9e4066Sahrens if (argc < 1) { 1878*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing snapshot argument\n")); 1879*fa9e4066Sahrens usage(FALSE); 1880*fa9e4066Sahrens } 1881*fa9e4066Sahrens if (argc > 1) { 1882*fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 1883*fa9e4066Sahrens usage(FALSE); 1884*fa9e4066Sahrens } 1885*fa9e4066Sahrens 1886*fa9e4066Sahrens if (isatty(STDIN_FILENO)) { 1887*fa9e4066Sahrens (void) fprintf(stderr, 1888*fa9e4066Sahrens gettext("Error: Backup stream can not be read " 1889*fa9e4066Sahrens "from a terminal.\n" 1890*fa9e4066Sahrens "You must redirect standard input.\n")); 1891*fa9e4066Sahrens return (1); 1892*fa9e4066Sahrens } 1893*fa9e4066Sahrens 1894*fa9e4066Sahrens err = zfs_restore(argv[0], isprefix, verbose, dryrun); 1895*fa9e4066Sahrens return (err != 0); 1896*fa9e4066Sahrens } 1897*fa9e4066Sahrens 1898*fa9e4066Sahrens 1899*fa9e4066Sahrens /* 1900*fa9e4066Sahrens * Generic callback for sharing or mounting filesystems. Because the code is so 1901*fa9e4066Sahrens * similar, we have a common function with an extra parameter to determine which 1902*fa9e4066Sahrens * mode we are using. 1903*fa9e4066Sahrens */ 1904*fa9e4066Sahrens #define OP_SHARE 0x1 1905*fa9e4066Sahrens #define OP_MOUNT 0x2 1906*fa9e4066Sahrens 1907*fa9e4066Sahrens typedef struct share_mount_cbdata { 1908*fa9e4066Sahrens int cb_type; 1909*fa9e4066Sahrens int cb_explicit; 1910*fa9e4066Sahrens int cb_flags; 1911*fa9e4066Sahrens const char *cb_options; 1912*fa9e4066Sahrens } share_mount_cbdata_t; 1913*fa9e4066Sahrens 1914*fa9e4066Sahrens /* 1915*fa9e4066Sahrens * Share or mount the filesystem. 1916*fa9e4066Sahrens */ 1917*fa9e4066Sahrens static int 1918*fa9e4066Sahrens share_mount_callback(zfs_handle_t *zhp, void *data) 1919*fa9e4066Sahrens { 1920*fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN]; 1921*fa9e4066Sahrens char shareopts[ZFS_MAXPROPLEN]; 1922*fa9e4066Sahrens share_mount_cbdata_t *cbp = data; 1923*fa9e4066Sahrens const char *cmdname = cbp->cb_type == OP_SHARE ? "share" : "mount"; 1924*fa9e4066Sahrens struct mnttab mnt; 1925*fa9e4066Sahrens uint64_t zoned; 1926*fa9e4066Sahrens 1927*fa9e4066Sahrens if (cbp->cb_options == NULL) 1928*fa9e4066Sahrens mnt.mnt_mntopts = ""; 1929*fa9e4066Sahrens else 1930*fa9e4066Sahrens mnt.mnt_mntopts = (char *)cbp->cb_options; 1931*fa9e4066Sahrens 1932*fa9e4066Sahrens /* 1933*fa9e4066Sahrens * Check to make sure we can mount/share this dataset. If we are in the 1934*fa9e4066Sahrens * global zone and the filesystem is exported to a local zone, or if we 1935*fa9e4066Sahrens * are in a local zone and the filesystem is not exported, then it is an 1936*fa9e4066Sahrens * error. 1937*fa9e4066Sahrens */ 1938*fa9e4066Sahrens zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 1939*fa9e4066Sahrens 1940*fa9e4066Sahrens if (zoned && getzoneid() == GLOBAL_ZONEID) { 1941*fa9e4066Sahrens if (!cbp->cb_explicit) 1942*fa9e4066Sahrens return (0); 1943*fa9e4066Sahrens 1944*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': dataset is " 1945*fa9e4066Sahrens "exported to a local zone\n"), cmdname, zfs_get_name(zhp)); 1946*fa9e4066Sahrens return (1); 1947*fa9e4066Sahrens 1948*fa9e4066Sahrens } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 1949*fa9e4066Sahrens if (!cbp->cb_explicit) 1950*fa9e4066Sahrens return (0); 1951*fa9e4066Sahrens 1952*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': permission " 1953*fa9e4066Sahrens "denied\n"), cmdname, zfs_get_name(zhp)); 1954*fa9e4066Sahrens return (1); 1955*fa9e4066Sahrens } 1956*fa9e4066Sahrens 1957*fa9e4066Sahrens /* 1958*fa9e4066Sahrens * Inore any filesystems which don't apply to us. This includes those 1959*fa9e4066Sahrens * with a legacy mountpoint, or those with legacy share options. 1960*fa9e4066Sahrens */ 1961*fa9e4066Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 1962*fa9e4066Sahrens sizeof (mountpoint), NULL, NULL, 0, FALSE) == 0); 1963*fa9e4066Sahrens verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 1964*fa9e4066Sahrens sizeof (shareopts), NULL, NULL, 0, FALSE) == 0); 1965*fa9e4066Sahrens 1966*fa9e4066Sahrens if (cbp->cb_type == OP_SHARE) { 1967*fa9e4066Sahrens if (strcmp(shareopts, "off") == 0) { 1968*fa9e4066Sahrens if (!cbp->cb_explicit) 1969*fa9e4066Sahrens return (0); 1970*fa9e4066Sahrens 1971*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot share '%s': " 1972*fa9e4066Sahrens "legacy share\n"), zfs_get_name(zhp)); 1973*fa9e4066Sahrens (void) fprintf(stderr, gettext("use share(1M) to " 1974*fa9e4066Sahrens "share this filesystem\n")); 1975*fa9e4066Sahrens return (1); 1976*fa9e4066Sahrens } 1977*fa9e4066Sahrens } 1978*fa9e4066Sahrens 1979*fa9e4066Sahrens /* 1980*fa9e4066Sahrens * We cannot share or mount legacy filesystems. If the shareopts is 1981*fa9e4066Sahrens * non-legacy but the mountpoint is legacy, we treat it as a legacy 1982*fa9e4066Sahrens * share. 1983*fa9e4066Sahrens */ 1984*fa9e4066Sahrens if (strcmp(mountpoint, "legacy") == 0) { 1985*fa9e4066Sahrens if (!cbp->cb_explicit) 1986*fa9e4066Sahrens return (0); 1987*fa9e4066Sahrens 1988*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': " 1989*fa9e4066Sahrens "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 1990*fa9e4066Sahrens (void) fprintf(stderr, gettext("use %s to " 1991*fa9e4066Sahrens "%s this filesystem\n"), cbp->cb_type == OP_SHARE ? 1992*fa9e4066Sahrens "share(1M)" : "mount(1M)", cmdname); 1993*fa9e4066Sahrens return (1); 1994*fa9e4066Sahrens } 1995*fa9e4066Sahrens 1996*fa9e4066Sahrens if (strcmp(mountpoint, "none") == 0) { 1997*fa9e4066Sahrens if (!cbp->cb_explicit) 1998*fa9e4066Sahrens return (0); 1999*fa9e4066Sahrens 2000*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': no " 2001*fa9e4066Sahrens "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 2002*fa9e4066Sahrens return (1); 2003*fa9e4066Sahrens } 2004*fa9e4066Sahrens 2005*fa9e4066Sahrens /* 2006*fa9e4066Sahrens * At this point, we have verified that the mountpoint and/or shareopts 2007*fa9e4066Sahrens * are appropriate for auto management. Determine if the filesystem is 2008*fa9e4066Sahrens * currently mounted or shared, and abort if this is an explicit 2009*fa9e4066Sahrens * request. 2010*fa9e4066Sahrens */ 2011*fa9e4066Sahrens switch (cbp->cb_type) { 2012*fa9e4066Sahrens case OP_SHARE: 2013*fa9e4066Sahrens if (zfs_is_shared(zhp, NULL)) { 2014*fa9e4066Sahrens if (cbp->cb_explicit) { 2015*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot share " 2016*fa9e4066Sahrens "'%s': filesystem already shared\n"), 2017*fa9e4066Sahrens zfs_get_name(zhp)); 2018*fa9e4066Sahrens return (1); 2019*fa9e4066Sahrens } else { 2020*fa9e4066Sahrens return (0); 2021*fa9e4066Sahrens } 2022*fa9e4066Sahrens } 2023*fa9e4066Sahrens break; 2024*fa9e4066Sahrens 2025*fa9e4066Sahrens case OP_MOUNT: 2026*fa9e4066Sahrens if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 2027*fa9e4066Sahrens zfs_is_mounted(zhp, NULL)) { 2028*fa9e4066Sahrens if (cbp->cb_explicit) { 2029*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot mount " 2030*fa9e4066Sahrens "'%s': filesystem already mounted\n"), 2031*fa9e4066Sahrens zfs_get_name(zhp)); 2032*fa9e4066Sahrens return (1); 2033*fa9e4066Sahrens } else { 2034*fa9e4066Sahrens return (0); 2035*fa9e4066Sahrens } 2036*fa9e4066Sahrens } 2037*fa9e4066Sahrens break; 2038*fa9e4066Sahrens } 2039*fa9e4066Sahrens 2040*fa9e4066Sahrens /* 2041*fa9e4066Sahrens * Mount and optionally share the filesystem. 2042*fa9e4066Sahrens */ 2043*fa9e4066Sahrens switch (cbp->cb_type) { 2044*fa9e4066Sahrens case OP_SHARE: 2045*fa9e4066Sahrens { 2046*fa9e4066Sahrens if (!zfs_is_mounted(zhp, NULL) && 2047*fa9e4066Sahrens zfs_mount(zhp, NULL, 0) != 0) 2048*fa9e4066Sahrens return (1); 2049*fa9e4066Sahrens 2050*fa9e4066Sahrens if (zfs_share(zhp) != 0) 2051*fa9e4066Sahrens return (1); 2052*fa9e4066Sahrens } 2053*fa9e4066Sahrens break; 2054*fa9e4066Sahrens 2055*fa9e4066Sahrens case OP_MOUNT: 2056*fa9e4066Sahrens if (zfs_mount(zhp, cbp->cb_options, cbp->cb_flags) != 0) 2057*fa9e4066Sahrens return (1); 2058*fa9e4066Sahrens break; 2059*fa9e4066Sahrens } 2060*fa9e4066Sahrens 2061*fa9e4066Sahrens return (0); 2062*fa9e4066Sahrens } 2063*fa9e4066Sahrens 2064*fa9e4066Sahrens static int 2065*fa9e4066Sahrens share_or_mount(int type, int argc, char **argv) 2066*fa9e4066Sahrens { 2067*fa9e4066Sahrens int do_all = 0; 2068*fa9e4066Sahrens int c, ret; 2069*fa9e4066Sahrens share_mount_cbdata_t cb = { 0 }; 2070*fa9e4066Sahrens 2071*fa9e4066Sahrens cb.cb_type = type; 2072*fa9e4066Sahrens 2073*fa9e4066Sahrens /* check options */ 2074*fa9e4066Sahrens while ((c = getopt(argc, argv, type == OP_MOUNT ? ":ao:O" : "a")) 2075*fa9e4066Sahrens != -1) { 2076*fa9e4066Sahrens switch (c) { 2077*fa9e4066Sahrens case 'a': 2078*fa9e4066Sahrens do_all = 1; 2079*fa9e4066Sahrens break; 2080*fa9e4066Sahrens case 'o': 2081*fa9e4066Sahrens cb.cb_options = optarg; 2082*fa9e4066Sahrens break; 2083*fa9e4066Sahrens case 'O': 2084*fa9e4066Sahrens cb.cb_flags |= MS_OVERLAY; 2085*fa9e4066Sahrens break; 2086*fa9e4066Sahrens case ':': 2087*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing argument for " 2088*fa9e4066Sahrens "'%c' option\n"), optopt); 2089*fa9e4066Sahrens usage(FALSE); 2090*fa9e4066Sahrens break; 2091*fa9e4066Sahrens case '?': 2092*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2093*fa9e4066Sahrens optopt); 2094*fa9e4066Sahrens usage(FALSE); 2095*fa9e4066Sahrens } 2096*fa9e4066Sahrens } 2097*fa9e4066Sahrens 2098*fa9e4066Sahrens argc -= optind; 2099*fa9e4066Sahrens argv += optind; 2100*fa9e4066Sahrens 2101*fa9e4066Sahrens /* check number of arguments */ 2102*fa9e4066Sahrens if (do_all) { 2103*fa9e4066Sahrens if (argc != 0) { 2104*fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2105*fa9e4066Sahrens usage(FALSE); 2106*fa9e4066Sahrens } 2107*fa9e4066Sahrens 2108*fa9e4066Sahrens ret = zfs_for_each(argc, argv, TRUE, 2109*fa9e4066Sahrens ZFS_TYPE_FILESYSTEM, share_mount_callback, &cb); 2110*fa9e4066Sahrens } else if (argc == 0) { 2111*fa9e4066Sahrens struct mnttab entry; 2112*fa9e4066Sahrens 2113*fa9e4066Sahrens if (type == OP_SHARE) { 2114*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing filesystem " 2115*fa9e4066Sahrens "argument\n")); 2116*fa9e4066Sahrens usage(FALSE); 2117*fa9e4066Sahrens } 2118*fa9e4066Sahrens 2119*fa9e4066Sahrens /* 2120*fa9e4066Sahrens * When mount is given no arguments, go through /etc/mnttab and 2121*fa9e4066Sahrens * display any active ZFS mounts. We hide any snapshots, since 2122*fa9e4066Sahrens * they are controlled automatically. 2123*fa9e4066Sahrens */ 2124*fa9e4066Sahrens rewind(mnttab_file); 2125*fa9e4066Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2126*fa9e4066Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 2127*fa9e4066Sahrens strchr(entry.mnt_special, '@') != NULL) 2128*fa9e4066Sahrens continue; 2129*fa9e4066Sahrens 2130*fa9e4066Sahrens (void) printf("%-30s %s\n", entry.mnt_special, 2131*fa9e4066Sahrens entry.mnt_mountp); 2132*fa9e4066Sahrens } 2133*fa9e4066Sahrens 2134*fa9e4066Sahrens ret = 0; 2135*fa9e4066Sahrens } else { 2136*fa9e4066Sahrens zfs_handle_t *zhp; 2137*fa9e4066Sahrens 2138*fa9e4066Sahrens if (argc > 1) { 2139*fa9e4066Sahrens (void) fprintf(stderr, 2140*fa9e4066Sahrens gettext("too many arguments\n")); 2141*fa9e4066Sahrens usage(FALSE); 2142*fa9e4066Sahrens } 2143*fa9e4066Sahrens 2144*fa9e4066Sahrens if ((zhp = zfs_open(argv[0], ZFS_TYPE_FILESYSTEM)) == NULL) 2145*fa9e4066Sahrens ret = 1; 2146*fa9e4066Sahrens else { 2147*fa9e4066Sahrens cb.cb_explicit = TRUE; 2148*fa9e4066Sahrens ret = share_mount_callback(zhp, &cb); 2149*fa9e4066Sahrens zfs_close(zhp); 2150*fa9e4066Sahrens } 2151*fa9e4066Sahrens } 2152*fa9e4066Sahrens 2153*fa9e4066Sahrens return (ret); 2154*fa9e4066Sahrens } 2155*fa9e4066Sahrens 2156*fa9e4066Sahrens /* 2157*fa9e4066Sahrens * zfs mount -a 2158*fa9e4066Sahrens * zfs mount filesystem 2159*fa9e4066Sahrens * 2160*fa9e4066Sahrens * Mount all filesystems, or mount the given filesystem. 2161*fa9e4066Sahrens */ 2162*fa9e4066Sahrens static int 2163*fa9e4066Sahrens zfs_do_mount(int argc, char **argv) 2164*fa9e4066Sahrens { 2165*fa9e4066Sahrens return (share_or_mount(OP_MOUNT, argc, argv)); 2166*fa9e4066Sahrens } 2167*fa9e4066Sahrens 2168*fa9e4066Sahrens /* 2169*fa9e4066Sahrens * zfs share -a 2170*fa9e4066Sahrens * zfs share filesystem 2171*fa9e4066Sahrens * 2172*fa9e4066Sahrens * Share all filesystems, or share the given filesystem. 2173*fa9e4066Sahrens */ 2174*fa9e4066Sahrens static int 2175*fa9e4066Sahrens zfs_do_share(int argc, char **argv) 2176*fa9e4066Sahrens { 2177*fa9e4066Sahrens return (share_or_mount(OP_SHARE, argc, argv)); 2178*fa9e4066Sahrens } 2179*fa9e4066Sahrens 2180*fa9e4066Sahrens typedef struct unshare_unmount_node { 2181*fa9e4066Sahrens zfs_handle_t *un_zhp; 2182*fa9e4066Sahrens char *un_mountp; 2183*fa9e4066Sahrens uu_avl_node_t un_avlnode; 2184*fa9e4066Sahrens } unshare_unmount_node_t; 2185*fa9e4066Sahrens 2186*fa9e4066Sahrens /* ARGSUSED */ 2187*fa9e4066Sahrens static int 2188*fa9e4066Sahrens unshare_unmount_compare(const void *larg, const void *rarg, void *unused) 2189*fa9e4066Sahrens { 2190*fa9e4066Sahrens const unshare_unmount_node_t *l = larg; 2191*fa9e4066Sahrens const unshare_unmount_node_t *r = rarg; 2192*fa9e4066Sahrens 2193*fa9e4066Sahrens return (strcmp(l->un_mountp, r->un_mountp)); 2194*fa9e4066Sahrens } 2195*fa9e4066Sahrens 2196*fa9e4066Sahrens /* 2197*fa9e4066Sahrens * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 2198*fa9e4066Sahrens * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 2199*fa9e4066Sahrens * and unmount it appropriately. 2200*fa9e4066Sahrens */ 2201*fa9e4066Sahrens static int 2202*fa9e4066Sahrens unshare_unmount_path(int type, char *path, int flags, int is_manual) 2203*fa9e4066Sahrens { 2204*fa9e4066Sahrens zfs_handle_t *zhp; 2205*fa9e4066Sahrens int ret; 2206*fa9e4066Sahrens struct stat64 statbuf; 2207*fa9e4066Sahrens struct extmnttab entry; 2208*fa9e4066Sahrens const char *cmdname = (type == OP_SHARE) ? "unshare" : "unmount"; 2209*fa9e4066Sahrens char property[ZFS_MAXPROPLEN]; 2210*fa9e4066Sahrens 2211*fa9e4066Sahrens /* 2212*fa9e4066Sahrens * Search for the path in /etc/mnttab. Rather than looking for the 2213*fa9e4066Sahrens * specific path, which can be fooled by non-standard paths (i.e. ".." 2214*fa9e4066Sahrens * or "//"), we stat() the path and search for the corresponding 2215*fa9e4066Sahrens * (major,minor) device pair. 2216*fa9e4066Sahrens */ 2217*fa9e4066Sahrens if (stat64(path, &statbuf) != 0) { 2218*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 2219*fa9e4066Sahrens cmdname, path, strerror(errno)); 2220*fa9e4066Sahrens return (1); 2221*fa9e4066Sahrens } 2222*fa9e4066Sahrens 2223*fa9e4066Sahrens /* 2224*fa9e4066Sahrens * Search for the given (major,minor) pair in the mount table. 2225*fa9e4066Sahrens */ 2226*fa9e4066Sahrens rewind(mnttab_file); 2227*fa9e4066Sahrens while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 2228*fa9e4066Sahrens if (entry.mnt_major == major(statbuf.st_dev) && 2229*fa9e4066Sahrens entry.mnt_minor == minor(statbuf.st_dev)) 2230*fa9e4066Sahrens break; 2231*fa9e4066Sahrens } 2232*fa9e4066Sahrens if (ret != 0) { 2233*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not " 2234*fa9e4066Sahrens "currently mounted\n"), cmdname, path); 2235*fa9e4066Sahrens return (1); 2236*fa9e4066Sahrens } 2237*fa9e4066Sahrens 2238*fa9e4066Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 2239*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 2240*fa9e4066Sahrens "filesystem\n"), cmdname, path); 2241*fa9e4066Sahrens return (1); 2242*fa9e4066Sahrens } 2243*fa9e4066Sahrens 2244*fa9e4066Sahrens if ((zhp = zfs_open(entry.mnt_special, ZFS_TYPE_FILESYSTEM)) == NULL) 2245*fa9e4066Sahrens return (1); 2246*fa9e4066Sahrens 2247*fa9e4066Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2248*fa9e4066Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 2249*fa9e4066Sahrens sizeof (property), NULL, NULL, 0, FALSE) == 0); 2250*fa9e4066Sahrens 2251*fa9e4066Sahrens if (type == OP_SHARE) { 2252*fa9e4066Sahrens if (strcmp(property, "off") == 0) { 2253*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2254*fa9e4066Sahrens "'%s': legacy share\n"), path); 2255*fa9e4066Sahrens (void) fprintf(stderr, gettext("use " 2256*fa9e4066Sahrens "unshare(1M) to unshare this filesystem\n")); 2257*fa9e4066Sahrens ret = 1; 2258*fa9e4066Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2259*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot unshare '%s': " 2260*fa9e4066Sahrens "not currently shared\n"), path); 2261*fa9e4066Sahrens ret = 1; 2262*fa9e4066Sahrens } else { 2263*fa9e4066Sahrens ret = zfs_unshareall(zhp); 2264*fa9e4066Sahrens } 2265*fa9e4066Sahrens } else { 2266*fa9e4066Sahrens if (strcmp(property, "legacy") == 0 && !is_manual) { 2267*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2268*fa9e4066Sahrens "'%s': legacy mountpoint\n"), 2269*fa9e4066Sahrens zfs_get_name(zhp)); 2270*fa9e4066Sahrens (void) fprintf(stderr, gettext("use umount(1M) " 2271*fa9e4066Sahrens "to unmount this filesystem\n")); 2272*fa9e4066Sahrens ret = 1; 2273*fa9e4066Sahrens } else { 2274*fa9e4066Sahrens ret = zfs_unmountall(zhp, flags); 2275*fa9e4066Sahrens } 2276*fa9e4066Sahrens } 2277*fa9e4066Sahrens 2278*fa9e4066Sahrens zfs_close(zhp); 2279*fa9e4066Sahrens 2280*fa9e4066Sahrens return (ret != 0); 2281*fa9e4066Sahrens } 2282*fa9e4066Sahrens 2283*fa9e4066Sahrens /* 2284*fa9e4066Sahrens * Generic callback for unsharing or unmounting a filesystem. 2285*fa9e4066Sahrens */ 2286*fa9e4066Sahrens static int 2287*fa9e4066Sahrens unshare_unmount(int type, int argc, char **argv) 2288*fa9e4066Sahrens { 2289*fa9e4066Sahrens int do_all = 0; 2290*fa9e4066Sahrens int flags = 0; 2291*fa9e4066Sahrens int ret = 0; 2292*fa9e4066Sahrens int c; 2293*fa9e4066Sahrens zfs_handle_t *zhp; 2294*fa9e4066Sahrens char property[ZFS_MAXPROPLEN]; 2295*fa9e4066Sahrens 2296*fa9e4066Sahrens /* check options */ 2297*fa9e4066Sahrens while ((c = getopt(argc, argv, type == OP_SHARE ? "a" : "af")) != -1) { 2298*fa9e4066Sahrens switch (c) { 2299*fa9e4066Sahrens case 'a': 2300*fa9e4066Sahrens do_all = 1; 2301*fa9e4066Sahrens break; 2302*fa9e4066Sahrens case 'f': 2303*fa9e4066Sahrens flags = MS_FORCE; 2304*fa9e4066Sahrens break; 2305*fa9e4066Sahrens case '?': 2306*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2307*fa9e4066Sahrens optopt); 2308*fa9e4066Sahrens usage(FALSE); 2309*fa9e4066Sahrens } 2310*fa9e4066Sahrens } 2311*fa9e4066Sahrens 2312*fa9e4066Sahrens argc -= optind; 2313*fa9e4066Sahrens argv += optind; 2314*fa9e4066Sahrens 2315*fa9e4066Sahrens /* ensure correct number of arguments */ 2316*fa9e4066Sahrens if (do_all) { 2317*fa9e4066Sahrens if (argc != 0) { 2318*fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2319*fa9e4066Sahrens usage(FALSE); 2320*fa9e4066Sahrens } 2321*fa9e4066Sahrens } else if (argc != 1) { 2322*fa9e4066Sahrens if (argc == 0) 2323*fa9e4066Sahrens (void) fprintf(stderr, 2324*fa9e4066Sahrens gettext("missing filesystem argument\n")); 2325*fa9e4066Sahrens else 2326*fa9e4066Sahrens (void) fprintf(stderr, 2327*fa9e4066Sahrens gettext("too many arguments\n")); 2328*fa9e4066Sahrens usage(FALSE); 2329*fa9e4066Sahrens } 2330*fa9e4066Sahrens 2331*fa9e4066Sahrens if (do_all) { 2332*fa9e4066Sahrens /* 2333*fa9e4066Sahrens * We could make use of zfs_for_each() to walk all datasets in 2334*fa9e4066Sahrens * the system, but this would be very inefficient, especially 2335*fa9e4066Sahrens * since we would have to linearly search /etc/mnttab for each 2336*fa9e4066Sahrens * one. Instead, do one pass through /etc/mnttab looking for 2337*fa9e4066Sahrens * zfs entries and call zfs_unmount() for each one. 2338*fa9e4066Sahrens * 2339*fa9e4066Sahrens * Things get a little tricky if the administrator has created 2340*fa9e4066Sahrens * mountpoints beneath other ZFS filesystems. In this case, we 2341*fa9e4066Sahrens * have to unmount the deepest filesystems first. To accomplish 2342*fa9e4066Sahrens * this, we place all the mountpoints in an AVL tree sorted by 2343*fa9e4066Sahrens * the special type (dataset name), and walk the result in 2344*fa9e4066Sahrens * reverse to make sure to get any snapshots first. 2345*fa9e4066Sahrens */ 2346*fa9e4066Sahrens struct mnttab entry; 2347*fa9e4066Sahrens uu_avl_pool_t *pool; 2348*fa9e4066Sahrens uu_avl_t *tree; 2349*fa9e4066Sahrens unshare_unmount_node_t *node; 2350*fa9e4066Sahrens uu_avl_index_t idx; 2351*fa9e4066Sahrens uu_avl_walk_t *walk; 2352*fa9e4066Sahrens 2353*fa9e4066Sahrens if ((pool = uu_avl_pool_create("unmount_pool", 2354*fa9e4066Sahrens sizeof (unshare_unmount_node_t), 2355*fa9e4066Sahrens offsetof(unshare_unmount_node_t, un_avlnode), 2356*fa9e4066Sahrens unshare_unmount_compare, 2357*fa9e4066Sahrens UU_DEFAULT)) == NULL) { 2358*fa9e4066Sahrens (void) fprintf(stderr, gettext("internal error: " 2359*fa9e4066Sahrens "out of memory\n")); 2360*fa9e4066Sahrens exit(1); 2361*fa9e4066Sahrens } 2362*fa9e4066Sahrens 2363*fa9e4066Sahrens if ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL) { 2364*fa9e4066Sahrens (void) fprintf(stderr, gettext("internal error: " 2365*fa9e4066Sahrens "out of memory\n")); 2366*fa9e4066Sahrens exit(1); 2367*fa9e4066Sahrens } 2368*fa9e4066Sahrens 2369*fa9e4066Sahrens rewind(mnttab_file); 2370*fa9e4066Sahrens while (getmntent(mnttab_file, &entry) == 0) { 2371*fa9e4066Sahrens 2372*fa9e4066Sahrens /* ignore non-ZFS entries */ 2373*fa9e4066Sahrens if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 2374*fa9e4066Sahrens continue; 2375*fa9e4066Sahrens 2376*fa9e4066Sahrens /* ignore snapshots */ 2377*fa9e4066Sahrens if (strchr(entry.mnt_special, '@') != NULL) 2378*fa9e4066Sahrens continue; 2379*fa9e4066Sahrens 2380*fa9e4066Sahrens if ((zhp = zfs_open(entry.mnt_special, 2381*fa9e4066Sahrens ZFS_TYPE_FILESYSTEM)) == NULL) { 2382*fa9e4066Sahrens ret = 1; 2383*fa9e4066Sahrens continue; 2384*fa9e4066Sahrens } 2385*fa9e4066Sahrens 2386*fa9e4066Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2387*fa9e4066Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 2388*fa9e4066Sahrens property, sizeof (property), NULL, NULL, 2389*fa9e4066Sahrens 0, FALSE) == 0); 2390*fa9e4066Sahrens 2391*fa9e4066Sahrens /* Ignore legacy mounts and shares */ 2392*fa9e4066Sahrens if ((type == OP_SHARE && 2393*fa9e4066Sahrens strcmp(property, "off") == 0) || 2394*fa9e4066Sahrens (type == OP_MOUNT && 2395*fa9e4066Sahrens strcmp(property, "legacy") == 0)) { 2396*fa9e4066Sahrens zfs_close(zhp); 2397*fa9e4066Sahrens continue; 2398*fa9e4066Sahrens } 2399*fa9e4066Sahrens 2400*fa9e4066Sahrens node = safe_malloc(sizeof (unshare_unmount_node_t)); 2401*fa9e4066Sahrens node->un_zhp = zhp; 2402*fa9e4066Sahrens 2403*fa9e4066Sahrens if ((node->un_mountp = strdup(entry.mnt_mountp)) == 2404*fa9e4066Sahrens NULL) { 2405*fa9e4066Sahrens (void) fprintf(stderr, gettext("internal error:" 2406*fa9e4066Sahrens " out of memory\n")); 2407*fa9e4066Sahrens exit(1); 2408*fa9e4066Sahrens } 2409*fa9e4066Sahrens 2410*fa9e4066Sahrens uu_avl_node_init(node, &node->un_avlnode, pool); 2411*fa9e4066Sahrens 2412*fa9e4066Sahrens if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 2413*fa9e4066Sahrens uu_avl_insert(tree, node, idx); 2414*fa9e4066Sahrens } else { 2415*fa9e4066Sahrens zfs_close(node->un_zhp); 2416*fa9e4066Sahrens free(node->un_mountp); 2417*fa9e4066Sahrens free(node); 2418*fa9e4066Sahrens } 2419*fa9e4066Sahrens } 2420*fa9e4066Sahrens 2421*fa9e4066Sahrens /* 2422*fa9e4066Sahrens * Walk the AVL tree in reverse, unmounting each filesystem and 2423*fa9e4066Sahrens * removing it from the AVL tree in the process. 2424*fa9e4066Sahrens */ 2425*fa9e4066Sahrens if ((walk = uu_avl_walk_start(tree, 2426*fa9e4066Sahrens UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) { 2427*fa9e4066Sahrens (void) fprintf(stderr, 2428*fa9e4066Sahrens gettext("internal error: out of memory")); 2429*fa9e4066Sahrens exit(1); 2430*fa9e4066Sahrens } 2431*fa9e4066Sahrens 2432*fa9e4066Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 2433*fa9e4066Sahrens uu_avl_remove(tree, node); 2434*fa9e4066Sahrens 2435*fa9e4066Sahrens switch (type) { 2436*fa9e4066Sahrens case OP_SHARE: 2437*fa9e4066Sahrens if (zfs_unshare(node->un_zhp, 2438*fa9e4066Sahrens node->un_mountp) != 0) 2439*fa9e4066Sahrens ret = 1; 2440*fa9e4066Sahrens break; 2441*fa9e4066Sahrens 2442*fa9e4066Sahrens case OP_MOUNT: 2443*fa9e4066Sahrens if (zfs_unmount(node->un_zhp, 2444*fa9e4066Sahrens node->un_mountp, flags) != 0) 2445*fa9e4066Sahrens ret = 1; 2446*fa9e4066Sahrens break; 2447*fa9e4066Sahrens } 2448*fa9e4066Sahrens 2449*fa9e4066Sahrens zfs_close(node->un_zhp); 2450*fa9e4066Sahrens free(node->un_mountp); 2451*fa9e4066Sahrens free(node); 2452*fa9e4066Sahrens } 2453*fa9e4066Sahrens 2454*fa9e4066Sahrens uu_avl_walk_end(walk); 2455*fa9e4066Sahrens uu_avl_destroy(tree); 2456*fa9e4066Sahrens uu_avl_pool_destroy(pool); 2457*fa9e4066Sahrens } else { 2458*fa9e4066Sahrens /* 2459*fa9e4066Sahrens * We have an argument, but it may be a full path or a ZFS 2460*fa9e4066Sahrens * filesystem. Pass full paths off to unmount_path() (shared by 2461*fa9e4066Sahrens * manual_unmount), otherwise open the filesystem and pass to 2462*fa9e4066Sahrens * zfs_unmount(). 2463*fa9e4066Sahrens */ 2464*fa9e4066Sahrens if (argv[0][0] == '/') 2465*fa9e4066Sahrens return (unshare_unmount_path(type, argv[0], 2466*fa9e4066Sahrens flags, FALSE)); 2467*fa9e4066Sahrens 2468*fa9e4066Sahrens if ((zhp = zfs_open(argv[0], ZFS_TYPE_FILESYSTEM)) == NULL) 2469*fa9e4066Sahrens return (1); 2470*fa9e4066Sahrens 2471*fa9e4066Sahrens verify(zfs_prop_get(zhp, type == OP_SHARE ? 2472*fa9e4066Sahrens ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, property, 2473*fa9e4066Sahrens sizeof (property), NULL, NULL, 0, FALSE) == 0); 2474*fa9e4066Sahrens 2475*fa9e4066Sahrens switch (type) { 2476*fa9e4066Sahrens case OP_SHARE: 2477*fa9e4066Sahrens if (strcmp(property, "off") == 0) { 2478*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2479*fa9e4066Sahrens "'%s': legacy share\n"), zfs_get_name(zhp)); 2480*fa9e4066Sahrens (void) fprintf(stderr, gettext("use unshare(1M)" 2481*fa9e4066Sahrens " to unshare this filesystem\n")); 2482*fa9e4066Sahrens ret = 1; 2483*fa9e4066Sahrens } else if (!zfs_is_shared(zhp, NULL)) { 2484*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot unshare " 2485*fa9e4066Sahrens "'%s': not currently shared\n"), 2486*fa9e4066Sahrens zfs_get_name(zhp)); 2487*fa9e4066Sahrens ret = 1; 2488*fa9e4066Sahrens } else if (zfs_unshareall(zhp) != 0) { 2489*fa9e4066Sahrens ret = 1; 2490*fa9e4066Sahrens } 2491*fa9e4066Sahrens break; 2492*fa9e4066Sahrens 2493*fa9e4066Sahrens case OP_MOUNT: 2494*fa9e4066Sahrens if (strcmp(property, "legacy") == 0) { 2495*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2496*fa9e4066Sahrens "'%s': legacy mountpoint\n"), 2497*fa9e4066Sahrens zfs_get_name(zhp)); 2498*fa9e4066Sahrens (void) fprintf(stderr, gettext("use umount(1M) " 2499*fa9e4066Sahrens "to unmount this filesystem\n")); 2500*fa9e4066Sahrens ret = 1; 2501*fa9e4066Sahrens } else if (!zfs_is_mounted(zhp, NULL)) { 2502*fa9e4066Sahrens (void) fprintf(stderr, gettext("cannot unmount " 2503*fa9e4066Sahrens "'%s': not currently mounted\n"), 2504*fa9e4066Sahrens zfs_get_name(zhp)); 2505*fa9e4066Sahrens ret = 1; 2506*fa9e4066Sahrens } else if (zfs_unmountall(zhp, flags) != 0) { 2507*fa9e4066Sahrens ret = 1; 2508*fa9e4066Sahrens } 2509*fa9e4066Sahrens } 2510*fa9e4066Sahrens 2511*fa9e4066Sahrens zfs_close(zhp); 2512*fa9e4066Sahrens } 2513*fa9e4066Sahrens 2514*fa9e4066Sahrens return (ret); 2515*fa9e4066Sahrens } 2516*fa9e4066Sahrens 2517*fa9e4066Sahrens /* 2518*fa9e4066Sahrens * zfs unmount -a 2519*fa9e4066Sahrens * zfs unmount filesystem 2520*fa9e4066Sahrens * 2521*fa9e4066Sahrens * Unmount all filesystems, or a specific ZFS filesystem. 2522*fa9e4066Sahrens */ 2523*fa9e4066Sahrens static int 2524*fa9e4066Sahrens zfs_do_unmount(int argc, char **argv) 2525*fa9e4066Sahrens { 2526*fa9e4066Sahrens return (unshare_unmount(OP_MOUNT, argc, argv)); 2527*fa9e4066Sahrens } 2528*fa9e4066Sahrens 2529*fa9e4066Sahrens /* 2530*fa9e4066Sahrens * zfs unshare -a 2531*fa9e4066Sahrens * zfs unshare filesystem 2532*fa9e4066Sahrens * 2533*fa9e4066Sahrens * Unshare all filesystems, or a specific ZFS filesystem. 2534*fa9e4066Sahrens */ 2535*fa9e4066Sahrens static int 2536*fa9e4066Sahrens zfs_do_unshare(int argc, char **argv) 2537*fa9e4066Sahrens { 2538*fa9e4066Sahrens return (unshare_unmount(OP_SHARE, argc, argv)); 2539*fa9e4066Sahrens } 2540*fa9e4066Sahrens 2541*fa9e4066Sahrens /* 2542*fa9e4066Sahrens * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 2543*fa9e4066Sahrens * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 2544*fa9e4066Sahrens */ 2545*fa9e4066Sahrens static int 2546*fa9e4066Sahrens manual_mount(int argc, char **argv) 2547*fa9e4066Sahrens { 2548*fa9e4066Sahrens zfs_handle_t *zhp; 2549*fa9e4066Sahrens char mountpoint[ZFS_MAXPROPLEN]; 2550*fa9e4066Sahrens char mntopts[MNT_LINE_MAX] = { '\0' }; 2551*fa9e4066Sahrens int ret; 2552*fa9e4066Sahrens int c; 2553*fa9e4066Sahrens int flags = 0; 2554*fa9e4066Sahrens char *dataset, *path; 2555*fa9e4066Sahrens 2556*fa9e4066Sahrens /* check options */ 2557*fa9e4066Sahrens while ((c = getopt(argc, argv, ":o:O")) != -1) { 2558*fa9e4066Sahrens switch (c) { 2559*fa9e4066Sahrens case 'o': 2560*fa9e4066Sahrens (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 2561*fa9e4066Sahrens break; 2562*fa9e4066Sahrens case 'O': 2563*fa9e4066Sahrens flags |= MS_OVERLAY; 2564*fa9e4066Sahrens break; 2565*fa9e4066Sahrens case ':': 2566*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing argument for " 2567*fa9e4066Sahrens "'%c' option\n"), optopt); 2568*fa9e4066Sahrens usage(FALSE); 2569*fa9e4066Sahrens break; 2570*fa9e4066Sahrens case '?': 2571*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2572*fa9e4066Sahrens optopt); 2573*fa9e4066Sahrens (void) fprintf(stderr, gettext("usage: mount [-o opts] " 2574*fa9e4066Sahrens "<path>\n")); 2575*fa9e4066Sahrens return (2); 2576*fa9e4066Sahrens } 2577*fa9e4066Sahrens } 2578*fa9e4066Sahrens 2579*fa9e4066Sahrens argc -= optind; 2580*fa9e4066Sahrens argv += optind; 2581*fa9e4066Sahrens 2582*fa9e4066Sahrens /* check that we only have two arguments */ 2583*fa9e4066Sahrens if (argc != 2) { 2584*fa9e4066Sahrens if (argc == 0) 2585*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing dataset " 2586*fa9e4066Sahrens "argument\n")); 2587*fa9e4066Sahrens else if (argc == 1) 2588*fa9e4066Sahrens (void) fprintf(stderr, 2589*fa9e4066Sahrens gettext("missing mountpoint argument\n")); 2590*fa9e4066Sahrens else 2591*fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2592*fa9e4066Sahrens (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 2593*fa9e4066Sahrens return (2); 2594*fa9e4066Sahrens } 2595*fa9e4066Sahrens 2596*fa9e4066Sahrens dataset = argv[0]; 2597*fa9e4066Sahrens path = argv[1]; 2598*fa9e4066Sahrens 2599*fa9e4066Sahrens /* try to open the dataset */ 2600*fa9e4066Sahrens if ((zhp = zfs_open(dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 2601*fa9e4066Sahrens return (1); 2602*fa9e4066Sahrens 2603*fa9e4066Sahrens (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 2604*fa9e4066Sahrens sizeof (mountpoint), NULL, NULL, 0, FALSE); 2605*fa9e4066Sahrens 2606*fa9e4066Sahrens /* check for legacy mountpoint and complain appropriately */ 2607*fa9e4066Sahrens ret = 0; 2608*fa9e4066Sahrens if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 2609*fa9e4066Sahrens if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS, 2610*fa9e4066Sahrens NULL, 0, mntopts, sizeof (mntopts)) != 0) { 2611*fa9e4066Sahrens (void) fprintf(stderr, gettext("mount failed: %s\n"), 2612*fa9e4066Sahrens strerror(errno)); 2613*fa9e4066Sahrens ret = 1; 2614*fa9e4066Sahrens } 2615*fa9e4066Sahrens } else { 2616*fa9e4066Sahrens (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 2617*fa9e4066Sahrens "mounted using 'mount -F zfs'\n"), dataset); 2618*fa9e4066Sahrens (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 2619*fa9e4066Sahrens "instead.\n"), path); 2620*fa9e4066Sahrens (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' " 2621*fa9e4066Sahrens "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n")); 2622*fa9e4066Sahrens (void) fprintf(stderr, gettext("See zfs(1M) for more " 2623*fa9e4066Sahrens "information.\n")); 2624*fa9e4066Sahrens ret = 1; 2625*fa9e4066Sahrens } 2626*fa9e4066Sahrens 2627*fa9e4066Sahrens return (ret); 2628*fa9e4066Sahrens } 2629*fa9e4066Sahrens 2630*fa9e4066Sahrens /* 2631*fa9e4066Sahrens * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 2632*fa9e4066Sahrens * unmounts of non-legacy filesystems, as this is the dominant administrative 2633*fa9e4066Sahrens * interface. 2634*fa9e4066Sahrens */ 2635*fa9e4066Sahrens static int 2636*fa9e4066Sahrens manual_unmount(int argc, char **argv) 2637*fa9e4066Sahrens { 2638*fa9e4066Sahrens int flags = 0; 2639*fa9e4066Sahrens int c; 2640*fa9e4066Sahrens 2641*fa9e4066Sahrens /* check options */ 2642*fa9e4066Sahrens while ((c = getopt(argc, argv, "f")) != -1) { 2643*fa9e4066Sahrens switch (c) { 2644*fa9e4066Sahrens case 'f': 2645*fa9e4066Sahrens flags = MS_FORCE; 2646*fa9e4066Sahrens break; 2647*fa9e4066Sahrens case '?': 2648*fa9e4066Sahrens (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2649*fa9e4066Sahrens optopt); 2650*fa9e4066Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] " 2651*fa9e4066Sahrens "<path>\n")); 2652*fa9e4066Sahrens return (2); 2653*fa9e4066Sahrens } 2654*fa9e4066Sahrens } 2655*fa9e4066Sahrens 2656*fa9e4066Sahrens argc -= optind; 2657*fa9e4066Sahrens argv += optind; 2658*fa9e4066Sahrens 2659*fa9e4066Sahrens /* check arguments */ 2660*fa9e4066Sahrens if (argc != 1) { 2661*fa9e4066Sahrens if (argc == 0) 2662*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing path " 2663*fa9e4066Sahrens "argument\n")); 2664*fa9e4066Sahrens else 2665*fa9e4066Sahrens (void) fprintf(stderr, gettext("too many arguments\n")); 2666*fa9e4066Sahrens (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 2667*fa9e4066Sahrens return (2); 2668*fa9e4066Sahrens } 2669*fa9e4066Sahrens 2670*fa9e4066Sahrens return (unshare_unmount_path(OP_MOUNT, argv[0], flags, TRUE)); 2671*fa9e4066Sahrens } 2672*fa9e4066Sahrens 2673*fa9e4066Sahrens static int 2674*fa9e4066Sahrens volcheck(zpool_handle_t *zhp, void *data) 2675*fa9e4066Sahrens { 2676*fa9e4066Sahrens int isinit = (int)data; 2677*fa9e4066Sahrens 2678*fa9e4066Sahrens if (isinit) 2679*fa9e4066Sahrens return (zpool_create_zvol_links(zhp)); 2680*fa9e4066Sahrens else 2681*fa9e4066Sahrens return (zpool_remove_zvol_links(zhp)); 2682*fa9e4066Sahrens } 2683*fa9e4066Sahrens 2684*fa9e4066Sahrens /* 2685*fa9e4066Sahrens * Iterate over all pools in the system and either create or destroy /dev/zvol 2686*fa9e4066Sahrens * links, depending on the value of 'isinit'. 2687*fa9e4066Sahrens */ 2688*fa9e4066Sahrens static int 2689*fa9e4066Sahrens do_volcheck(int isinit) 2690*fa9e4066Sahrens { 2691*fa9e4066Sahrens return (zpool_iter(volcheck, (void *)isinit) ? 1 : 0); 2692*fa9e4066Sahrens } 2693*fa9e4066Sahrens 2694*fa9e4066Sahrens int 2695*fa9e4066Sahrens main(int argc, char **argv) 2696*fa9e4066Sahrens { 2697*fa9e4066Sahrens int ret; 2698*fa9e4066Sahrens int i; 2699*fa9e4066Sahrens char *progname; 2700*fa9e4066Sahrens char *cmdname; 2701*fa9e4066Sahrens 2702*fa9e4066Sahrens (void) setlocale(LC_ALL, ""); 2703*fa9e4066Sahrens (void) textdomain(TEXT_DOMAIN); 2704*fa9e4066Sahrens 2705*fa9e4066Sahrens opterr = 0; 2706*fa9e4066Sahrens 2707*fa9e4066Sahrens if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 2708*fa9e4066Sahrens (void) fprintf(stderr, gettext("internal error: unable to " 2709*fa9e4066Sahrens "open %s\n"), MNTTAB); 2710*fa9e4066Sahrens return (1); 2711*fa9e4066Sahrens } 2712*fa9e4066Sahrens 2713*fa9e4066Sahrens /* 2714*fa9e4066Sahrens * This command also doubles as the /etc/fs mount and unmount program. 2715*fa9e4066Sahrens * Determine if we should take this behavior based on argv[0]. 2716*fa9e4066Sahrens */ 2717*fa9e4066Sahrens progname = basename(argv[0]); 2718*fa9e4066Sahrens if (strcmp(progname, "mount") == 0) { 2719*fa9e4066Sahrens ret = manual_mount(argc, argv); 2720*fa9e4066Sahrens } else if (strcmp(progname, "umount") == 0) { 2721*fa9e4066Sahrens ret = manual_unmount(argc, argv); 2722*fa9e4066Sahrens } else { 2723*fa9e4066Sahrens /* 2724*fa9e4066Sahrens * Make sure the user has specified some command. 2725*fa9e4066Sahrens */ 2726*fa9e4066Sahrens if (argc < 2) { 2727*fa9e4066Sahrens (void) fprintf(stderr, gettext("missing command\n")); 2728*fa9e4066Sahrens usage(FALSE); 2729*fa9e4066Sahrens } 2730*fa9e4066Sahrens 2731*fa9e4066Sahrens cmdname = argv[1]; 2732*fa9e4066Sahrens 2733*fa9e4066Sahrens /* 2734*fa9e4066Sahrens * The 'umount' command is an alias for 'unmount' 2735*fa9e4066Sahrens */ 2736*fa9e4066Sahrens if (strcmp(cmdname, "umount") == 0) 2737*fa9e4066Sahrens cmdname = "unmount"; 2738*fa9e4066Sahrens 2739*fa9e4066Sahrens /* 2740*fa9e4066Sahrens * Special case '-?' 2741*fa9e4066Sahrens */ 2742*fa9e4066Sahrens if (strcmp(cmdname, "-?") == 0) 2743*fa9e4066Sahrens usage(TRUE); 2744*fa9e4066Sahrens 2745*fa9e4066Sahrens /* 2746*fa9e4066Sahrens * 'volinit' and 'volfini' do not appear in the usage message, 2747*fa9e4066Sahrens * so we have to special case them here. 2748*fa9e4066Sahrens */ 2749*fa9e4066Sahrens if (strcmp(cmdname, "volinit") == 0) 2750*fa9e4066Sahrens return (do_volcheck(TRUE)); 2751*fa9e4066Sahrens else if (strcmp(cmdname, "volfini") == 0) 2752*fa9e4066Sahrens return (do_volcheck(FALSE)); 2753*fa9e4066Sahrens 2754*fa9e4066Sahrens /* 2755*fa9e4066Sahrens * Run the appropriate command. 2756*fa9e4066Sahrens */ 2757*fa9e4066Sahrens for (i = 0; i < NCOMMAND; i++) { 2758*fa9e4066Sahrens if (command_table[i].name == NULL) 2759*fa9e4066Sahrens continue; 2760*fa9e4066Sahrens 2761*fa9e4066Sahrens if (strcmp(cmdname, command_table[i].name) == 0) { 2762*fa9e4066Sahrens current_command = &command_table[i]; 2763*fa9e4066Sahrens ret = command_table[i].func(argc - 1, argv + 1); 2764*fa9e4066Sahrens break; 2765*fa9e4066Sahrens } 2766*fa9e4066Sahrens } 2767*fa9e4066Sahrens 2768*fa9e4066Sahrens if (i == NCOMMAND) { 2769*fa9e4066Sahrens (void) fprintf(stderr, gettext("unrecognized " 2770*fa9e4066Sahrens "command '%s'\n"), cmdname); 2771*fa9e4066Sahrens usage(FALSE); 2772*fa9e4066Sahrens } 2773*fa9e4066Sahrens } 2774*fa9e4066Sahrens 2775*fa9e4066Sahrens (void) fclose(mnttab_file); 2776*fa9e4066Sahrens 2777*fa9e4066Sahrens /* 2778*fa9e4066Sahrens * The 'ZFS_ABORT' environment variable causes us to dump core on exit 2779*fa9e4066Sahrens * for the purposes of running ::findleaks. 2780*fa9e4066Sahrens */ 2781*fa9e4066Sahrens if (getenv("ZFS_ABORT") != NULL) { 2782*fa9e4066Sahrens (void) printf("dumping core by request\n"); 2783*fa9e4066Sahrens abort(); 2784*fa9e4066Sahrens } 2785*fa9e4066Sahrens 2786*fa9e4066Sahrens return (ret); 2787*fa9e4066Sahrens } 2788