1*eda14cbcSMatt Macy /* 2*eda14cbcSMatt Macy * CDDL HEADER START 3*eda14cbcSMatt Macy * 4*eda14cbcSMatt Macy * The contents of this file are subject to the terms of the 5*eda14cbcSMatt Macy * Common Development and Distribution License (the "License"). 6*eda14cbcSMatt Macy * You may not use this file except in compliance with the License. 7*eda14cbcSMatt Macy * 8*eda14cbcSMatt Macy * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*eda14cbcSMatt Macy * or http://www.opensolaris.org/os/licensing. 10*eda14cbcSMatt Macy * See the License for the specific language governing permissions 11*eda14cbcSMatt Macy * and limitations under the License. 12*eda14cbcSMatt Macy * 13*eda14cbcSMatt Macy * When distributing Covered Code, include this CDDL HEADER in each 14*eda14cbcSMatt Macy * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*eda14cbcSMatt Macy * If applicable, add the following below this CDDL HEADER, with the 16*eda14cbcSMatt Macy * fields enclosed by brackets "[]" replaced with your own identifying 17*eda14cbcSMatt Macy * information: Portions Copyright [yyyy] [name of copyright owner] 18*eda14cbcSMatt Macy * 19*eda14cbcSMatt Macy * CDDL HEADER END 20*eda14cbcSMatt Macy */ 21*eda14cbcSMatt Macy 22*eda14cbcSMatt Macy /* 23*eda14cbcSMatt Macy * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 24*eda14cbcSMatt Macy * Copyright (c) 2011, 2020 by Delphix. All rights reserved. 25*eda14cbcSMatt Macy * Copyright 2012 Milan Jurik. All rights reserved. 26*eda14cbcSMatt Macy * Copyright (c) 2012, Joyent, Inc. All rights reserved. 27*eda14cbcSMatt Macy * Copyright (c) 2013 Steven Hartland. All rights reserved. 28*eda14cbcSMatt Macy * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>. 29*eda14cbcSMatt Macy * Copyright 2016 Nexenta Systems, Inc. 30*eda14cbcSMatt Macy * Copyright (c) 2019 Datto Inc. 31*eda14cbcSMatt Macy * Copyright (c) 2019, loli10K <ezomori.nozomu@gmail.com> 32*eda14cbcSMatt Macy * Copyright 2019 Joyent, Inc. 33*eda14cbcSMatt Macy * Copyright (c) 2019, 2020 by Christian Schwarz. All rights reserved. 34*eda14cbcSMatt Macy */ 35*eda14cbcSMatt Macy 36*eda14cbcSMatt Macy #include <assert.h> 37*eda14cbcSMatt Macy #include <ctype.h> 38*eda14cbcSMatt Macy #include <sys/debug.h> 39*eda14cbcSMatt Macy #include <errno.h> 40*eda14cbcSMatt Macy #include <getopt.h> 41*eda14cbcSMatt Macy #include <libgen.h> 42*eda14cbcSMatt Macy #include <libintl.h> 43*eda14cbcSMatt Macy #include <libuutil.h> 44*eda14cbcSMatt Macy #include <libnvpair.h> 45*eda14cbcSMatt Macy #include <locale.h> 46*eda14cbcSMatt Macy #include <stddef.h> 47*eda14cbcSMatt Macy #include <stdio.h> 48*eda14cbcSMatt Macy #include <stdlib.h> 49*eda14cbcSMatt Macy #include <strings.h> 50*eda14cbcSMatt Macy #include <unistd.h> 51*eda14cbcSMatt Macy #include <fcntl.h> 52*eda14cbcSMatt Macy #include <zone.h> 53*eda14cbcSMatt Macy #include <grp.h> 54*eda14cbcSMatt Macy #include <pwd.h> 55*eda14cbcSMatt Macy #include <signal.h> 56*eda14cbcSMatt Macy #include <sys/debug.h> 57*eda14cbcSMatt Macy #include <sys/list.h> 58*eda14cbcSMatt Macy #include <sys/mkdev.h> 59*eda14cbcSMatt Macy #include <sys/mntent.h> 60*eda14cbcSMatt Macy #include <sys/mnttab.h> 61*eda14cbcSMatt Macy #include <sys/mount.h> 62*eda14cbcSMatt Macy #include <sys/stat.h> 63*eda14cbcSMatt Macy #include <sys/fs/zfs.h> 64*eda14cbcSMatt Macy #include <sys/systeminfo.h> 65*eda14cbcSMatt Macy #include <sys/types.h> 66*eda14cbcSMatt Macy #include <time.h> 67*eda14cbcSMatt Macy #include <sys/zfs_project.h> 68*eda14cbcSMatt Macy 69*eda14cbcSMatt Macy #include <libzfs.h> 70*eda14cbcSMatt Macy #include <libzfs_core.h> 71*eda14cbcSMatt Macy #include <zfs_prop.h> 72*eda14cbcSMatt Macy #include <zfs_deleg.h> 73*eda14cbcSMatt Macy #include <libzutil.h> 74*eda14cbcSMatt Macy #include <libuutil.h> 75*eda14cbcSMatt Macy #ifdef HAVE_IDMAP 76*eda14cbcSMatt Macy #include <aclutils.h> 77*eda14cbcSMatt Macy #include <directory.h> 78*eda14cbcSMatt Macy #endif /* HAVE_IDMAP */ 79*eda14cbcSMatt Macy 80*eda14cbcSMatt Macy #include "zfs_iter.h" 81*eda14cbcSMatt Macy #include "zfs_util.h" 82*eda14cbcSMatt Macy #include "zfs_comutil.h" 83*eda14cbcSMatt Macy #include "libzfs_impl.h" 84*eda14cbcSMatt Macy #include "zfs_projectutil.h" 85*eda14cbcSMatt Macy 86*eda14cbcSMatt Macy libzfs_handle_t *g_zfs; 87*eda14cbcSMatt Macy 88*eda14cbcSMatt Macy static FILE *mnttab_file; 89*eda14cbcSMatt Macy static char history_str[HIS_MAX_RECORD_LEN]; 90*eda14cbcSMatt Macy static boolean_t log_history = B_TRUE; 91*eda14cbcSMatt Macy 92*eda14cbcSMatt Macy static int zfs_do_clone(int argc, char **argv); 93*eda14cbcSMatt Macy static int zfs_do_create(int argc, char **argv); 94*eda14cbcSMatt Macy static int zfs_do_destroy(int argc, char **argv); 95*eda14cbcSMatt Macy static int zfs_do_get(int argc, char **argv); 96*eda14cbcSMatt Macy static int zfs_do_inherit(int argc, char **argv); 97*eda14cbcSMatt Macy static int zfs_do_list(int argc, char **argv); 98*eda14cbcSMatt Macy static int zfs_do_mount(int argc, char **argv); 99*eda14cbcSMatt Macy static int zfs_do_rename(int argc, char **argv); 100*eda14cbcSMatt Macy static int zfs_do_rollback(int argc, char **argv); 101*eda14cbcSMatt Macy static int zfs_do_set(int argc, char **argv); 102*eda14cbcSMatt Macy static int zfs_do_upgrade(int argc, char **argv); 103*eda14cbcSMatt Macy static int zfs_do_snapshot(int argc, char **argv); 104*eda14cbcSMatt Macy static int zfs_do_unmount(int argc, char **argv); 105*eda14cbcSMatt Macy static int zfs_do_share(int argc, char **argv); 106*eda14cbcSMatt Macy static int zfs_do_unshare(int argc, char **argv); 107*eda14cbcSMatt Macy static int zfs_do_send(int argc, char **argv); 108*eda14cbcSMatt Macy static int zfs_do_receive(int argc, char **argv); 109*eda14cbcSMatt Macy static int zfs_do_promote(int argc, char **argv); 110*eda14cbcSMatt Macy static int zfs_do_userspace(int argc, char **argv); 111*eda14cbcSMatt Macy static int zfs_do_allow(int argc, char **argv); 112*eda14cbcSMatt Macy static int zfs_do_unallow(int argc, char **argv); 113*eda14cbcSMatt Macy static int zfs_do_hold(int argc, char **argv); 114*eda14cbcSMatt Macy static int zfs_do_holds(int argc, char **argv); 115*eda14cbcSMatt Macy static int zfs_do_release(int argc, char **argv); 116*eda14cbcSMatt Macy static int zfs_do_diff(int argc, char **argv); 117*eda14cbcSMatt Macy static int zfs_do_bookmark(int argc, char **argv); 118*eda14cbcSMatt Macy static int zfs_do_channel_program(int argc, char **argv); 119*eda14cbcSMatt Macy static int zfs_do_load_key(int argc, char **argv); 120*eda14cbcSMatt Macy static int zfs_do_unload_key(int argc, char **argv); 121*eda14cbcSMatt Macy static int zfs_do_change_key(int argc, char **argv); 122*eda14cbcSMatt Macy static int zfs_do_project(int argc, char **argv); 123*eda14cbcSMatt Macy static int zfs_do_version(int argc, char **argv); 124*eda14cbcSMatt Macy static int zfs_do_redact(int argc, char **argv); 125*eda14cbcSMatt Macy static int zfs_do_wait(int argc, char **argv); 126*eda14cbcSMatt Macy 127*eda14cbcSMatt Macy #ifdef __FreeBSD__ 128*eda14cbcSMatt Macy static int zfs_do_jail(int argc, char **argv); 129*eda14cbcSMatt Macy static int zfs_do_unjail(int argc, char **argv); 130*eda14cbcSMatt Macy #endif 131*eda14cbcSMatt Macy 132*eda14cbcSMatt Macy /* 133*eda14cbcSMatt Macy * Enable a reasonable set of defaults for libumem debugging on DEBUG builds. 134*eda14cbcSMatt Macy */ 135*eda14cbcSMatt Macy 136*eda14cbcSMatt Macy #ifdef DEBUG 137*eda14cbcSMatt Macy const char * 138*eda14cbcSMatt Macy _umem_debug_init(void) 139*eda14cbcSMatt Macy { 140*eda14cbcSMatt Macy return ("default,verbose"); /* $UMEM_DEBUG setting */ 141*eda14cbcSMatt Macy } 142*eda14cbcSMatt Macy 143*eda14cbcSMatt Macy const char * 144*eda14cbcSMatt Macy _umem_logging_init(void) 145*eda14cbcSMatt Macy { 146*eda14cbcSMatt Macy return ("fail,contents"); /* $UMEM_LOGGING setting */ 147*eda14cbcSMatt Macy } 148*eda14cbcSMatt Macy #endif 149*eda14cbcSMatt Macy 150*eda14cbcSMatt Macy typedef enum { 151*eda14cbcSMatt Macy HELP_CLONE, 152*eda14cbcSMatt Macy HELP_CREATE, 153*eda14cbcSMatt Macy HELP_DESTROY, 154*eda14cbcSMatt Macy HELP_GET, 155*eda14cbcSMatt Macy HELP_INHERIT, 156*eda14cbcSMatt Macy HELP_UPGRADE, 157*eda14cbcSMatt Macy HELP_LIST, 158*eda14cbcSMatt Macy HELP_MOUNT, 159*eda14cbcSMatt Macy HELP_PROMOTE, 160*eda14cbcSMatt Macy HELP_RECEIVE, 161*eda14cbcSMatt Macy HELP_RENAME, 162*eda14cbcSMatt Macy HELP_ROLLBACK, 163*eda14cbcSMatt Macy HELP_SEND, 164*eda14cbcSMatt Macy HELP_SET, 165*eda14cbcSMatt Macy HELP_SHARE, 166*eda14cbcSMatt Macy HELP_SNAPSHOT, 167*eda14cbcSMatt Macy HELP_UNMOUNT, 168*eda14cbcSMatt Macy HELP_UNSHARE, 169*eda14cbcSMatt Macy HELP_ALLOW, 170*eda14cbcSMatt Macy HELP_UNALLOW, 171*eda14cbcSMatt Macy HELP_USERSPACE, 172*eda14cbcSMatt Macy HELP_GROUPSPACE, 173*eda14cbcSMatt Macy HELP_PROJECTSPACE, 174*eda14cbcSMatt Macy HELP_PROJECT, 175*eda14cbcSMatt Macy HELP_HOLD, 176*eda14cbcSMatt Macy HELP_HOLDS, 177*eda14cbcSMatt Macy HELP_RELEASE, 178*eda14cbcSMatt Macy HELP_DIFF, 179*eda14cbcSMatt Macy HELP_BOOKMARK, 180*eda14cbcSMatt Macy HELP_CHANNEL_PROGRAM, 181*eda14cbcSMatt Macy HELP_LOAD_KEY, 182*eda14cbcSMatt Macy HELP_UNLOAD_KEY, 183*eda14cbcSMatt Macy HELP_CHANGE_KEY, 184*eda14cbcSMatt Macy HELP_VERSION, 185*eda14cbcSMatt Macy HELP_REDACT, 186*eda14cbcSMatt Macy HELP_JAIL, 187*eda14cbcSMatt Macy HELP_UNJAIL, 188*eda14cbcSMatt Macy HELP_WAIT, 189*eda14cbcSMatt Macy } zfs_help_t; 190*eda14cbcSMatt Macy 191*eda14cbcSMatt Macy typedef struct zfs_command { 192*eda14cbcSMatt Macy const char *name; 193*eda14cbcSMatt Macy int (*func)(int argc, char **argv); 194*eda14cbcSMatt Macy zfs_help_t usage; 195*eda14cbcSMatt Macy } zfs_command_t; 196*eda14cbcSMatt Macy 197*eda14cbcSMatt Macy /* 198*eda14cbcSMatt Macy * Master command table. Each ZFS command has a name, associated function, and 199*eda14cbcSMatt Macy * usage message. The usage messages need to be internationalized, so we have 200*eda14cbcSMatt Macy * to have a function to return the usage message based on a command index. 201*eda14cbcSMatt Macy * 202*eda14cbcSMatt Macy * These commands are organized according to how they are displayed in the usage 203*eda14cbcSMatt Macy * message. An empty command (one with a NULL name) indicates an empty line in 204*eda14cbcSMatt Macy * the generic usage message. 205*eda14cbcSMatt Macy */ 206*eda14cbcSMatt Macy static zfs_command_t command_table[] = { 207*eda14cbcSMatt Macy { "version", zfs_do_version, HELP_VERSION }, 208*eda14cbcSMatt Macy { NULL }, 209*eda14cbcSMatt Macy { "create", zfs_do_create, HELP_CREATE }, 210*eda14cbcSMatt Macy { "destroy", zfs_do_destroy, HELP_DESTROY }, 211*eda14cbcSMatt Macy { NULL }, 212*eda14cbcSMatt Macy { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT }, 213*eda14cbcSMatt Macy { "rollback", zfs_do_rollback, HELP_ROLLBACK }, 214*eda14cbcSMatt Macy { "clone", zfs_do_clone, HELP_CLONE }, 215*eda14cbcSMatt Macy { "promote", zfs_do_promote, HELP_PROMOTE }, 216*eda14cbcSMatt Macy { "rename", zfs_do_rename, HELP_RENAME }, 217*eda14cbcSMatt Macy { "bookmark", zfs_do_bookmark, HELP_BOOKMARK }, 218*eda14cbcSMatt Macy { "program", zfs_do_channel_program, HELP_CHANNEL_PROGRAM }, 219*eda14cbcSMatt Macy { NULL }, 220*eda14cbcSMatt Macy { "list", zfs_do_list, HELP_LIST }, 221*eda14cbcSMatt Macy { NULL }, 222*eda14cbcSMatt Macy { "set", zfs_do_set, HELP_SET }, 223*eda14cbcSMatt Macy { "get", zfs_do_get, HELP_GET }, 224*eda14cbcSMatt Macy { "inherit", zfs_do_inherit, HELP_INHERIT }, 225*eda14cbcSMatt Macy { "upgrade", zfs_do_upgrade, HELP_UPGRADE }, 226*eda14cbcSMatt Macy { NULL }, 227*eda14cbcSMatt Macy { "userspace", zfs_do_userspace, HELP_USERSPACE }, 228*eda14cbcSMatt Macy { "groupspace", zfs_do_userspace, HELP_GROUPSPACE }, 229*eda14cbcSMatt Macy { "projectspace", zfs_do_userspace, HELP_PROJECTSPACE }, 230*eda14cbcSMatt Macy { NULL }, 231*eda14cbcSMatt Macy { "project", zfs_do_project, HELP_PROJECT }, 232*eda14cbcSMatt Macy { NULL }, 233*eda14cbcSMatt Macy { "mount", zfs_do_mount, HELP_MOUNT }, 234*eda14cbcSMatt Macy { "unmount", zfs_do_unmount, HELP_UNMOUNT }, 235*eda14cbcSMatt Macy { "share", zfs_do_share, HELP_SHARE }, 236*eda14cbcSMatt Macy { "unshare", zfs_do_unshare, HELP_UNSHARE }, 237*eda14cbcSMatt Macy { NULL }, 238*eda14cbcSMatt Macy { "send", zfs_do_send, HELP_SEND }, 239*eda14cbcSMatt Macy { "receive", zfs_do_receive, HELP_RECEIVE }, 240*eda14cbcSMatt Macy { NULL }, 241*eda14cbcSMatt Macy { "allow", zfs_do_allow, HELP_ALLOW }, 242*eda14cbcSMatt Macy { NULL }, 243*eda14cbcSMatt Macy { "unallow", zfs_do_unallow, HELP_UNALLOW }, 244*eda14cbcSMatt Macy { NULL }, 245*eda14cbcSMatt Macy { "hold", zfs_do_hold, HELP_HOLD }, 246*eda14cbcSMatt Macy { "holds", zfs_do_holds, HELP_HOLDS }, 247*eda14cbcSMatt Macy { "release", zfs_do_release, HELP_RELEASE }, 248*eda14cbcSMatt Macy { "diff", zfs_do_diff, HELP_DIFF }, 249*eda14cbcSMatt Macy { "load-key", zfs_do_load_key, HELP_LOAD_KEY }, 250*eda14cbcSMatt Macy { "unload-key", zfs_do_unload_key, HELP_UNLOAD_KEY }, 251*eda14cbcSMatt Macy { "change-key", zfs_do_change_key, HELP_CHANGE_KEY }, 252*eda14cbcSMatt Macy { "redact", zfs_do_redact, HELP_REDACT }, 253*eda14cbcSMatt Macy { "wait", zfs_do_wait, HELP_WAIT }, 254*eda14cbcSMatt Macy 255*eda14cbcSMatt Macy #ifdef __FreeBSD__ 256*eda14cbcSMatt Macy { "jail", zfs_do_jail, HELP_JAIL }, 257*eda14cbcSMatt Macy { "unjail", zfs_do_unjail, HELP_UNJAIL }, 258*eda14cbcSMatt Macy #endif 259*eda14cbcSMatt Macy }; 260*eda14cbcSMatt Macy 261*eda14cbcSMatt Macy #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) 262*eda14cbcSMatt Macy 263*eda14cbcSMatt Macy zfs_command_t *current_command; 264*eda14cbcSMatt Macy 265*eda14cbcSMatt Macy static const char * 266*eda14cbcSMatt Macy get_usage(zfs_help_t idx) 267*eda14cbcSMatt Macy { 268*eda14cbcSMatt Macy switch (idx) { 269*eda14cbcSMatt Macy case HELP_CLONE: 270*eda14cbcSMatt Macy return (gettext("\tclone [-p] [-o property=value] ... " 271*eda14cbcSMatt Macy "<snapshot> <filesystem|volume>\n")); 272*eda14cbcSMatt Macy case HELP_CREATE: 273*eda14cbcSMatt Macy return (gettext("\tcreate [-Pnpv] [-o property=value] ... " 274*eda14cbcSMatt Macy "<filesystem>\n" 275*eda14cbcSMatt Macy "\tcreate [-Pnpsv] [-b blocksize] [-o property=value] ... " 276*eda14cbcSMatt Macy "-V <size> <volume>\n")); 277*eda14cbcSMatt Macy case HELP_DESTROY: 278*eda14cbcSMatt Macy return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n" 279*eda14cbcSMatt Macy "\tdestroy [-dnpRrv] " 280*eda14cbcSMatt Macy "<filesystem|volume>@<snap>[%<snap>][,...]\n" 281*eda14cbcSMatt Macy "\tdestroy <filesystem|volume>#<bookmark>\n")); 282*eda14cbcSMatt Macy case HELP_GET: 283*eda14cbcSMatt Macy return (gettext("\tget [-rHp] [-d max] " 284*eda14cbcSMatt Macy "[-o \"all\" | field[,...]]\n" 285*eda14cbcSMatt Macy "\t [-t type[,...]] [-s source[,...]]\n" 286*eda14cbcSMatt Macy "\t <\"all\" | property[,...]> " 287*eda14cbcSMatt Macy "[filesystem|volume|snapshot|bookmark] ...\n")); 288*eda14cbcSMatt Macy case HELP_INHERIT: 289*eda14cbcSMatt Macy return (gettext("\tinherit [-rS] <property> " 290*eda14cbcSMatt Macy "<filesystem|volume|snapshot> ...\n")); 291*eda14cbcSMatt Macy case HELP_UPGRADE: 292*eda14cbcSMatt Macy return (gettext("\tupgrade [-v]\n" 293*eda14cbcSMatt Macy "\tupgrade [-r] [-V version] <-a | filesystem ...>\n")); 294*eda14cbcSMatt Macy case HELP_LIST: 295*eda14cbcSMatt Macy return (gettext("\tlist [-Hp] [-r|-d max] [-o property[,...]] " 296*eda14cbcSMatt Macy "[-s property]...\n\t [-S property]... [-t type[,...]] " 297*eda14cbcSMatt Macy "[filesystem|volume|snapshot] ...\n")); 298*eda14cbcSMatt Macy case HELP_MOUNT: 299*eda14cbcSMatt Macy return (gettext("\tmount\n" 300*eda14cbcSMatt Macy "\tmount [-flvO] [-o opts] <-a | filesystem>\n")); 301*eda14cbcSMatt Macy case HELP_PROMOTE: 302*eda14cbcSMatt Macy return (gettext("\tpromote <clone-filesystem>\n")); 303*eda14cbcSMatt Macy case HELP_RECEIVE: 304*eda14cbcSMatt Macy return (gettext("\treceive [-vMnsFhu] " 305*eda14cbcSMatt Macy "[-o <property>=<value>] ... [-x <property>] ...\n" 306*eda14cbcSMatt Macy "\t <filesystem|volume|snapshot>\n" 307*eda14cbcSMatt Macy "\treceive [-vMnsFhu] [-o <property>=<value>] ... " 308*eda14cbcSMatt Macy "[-x <property>] ... \n" 309*eda14cbcSMatt Macy "\t [-d | -e] <filesystem>\n" 310*eda14cbcSMatt Macy "\treceive -A <filesystem|volume>\n")); 311*eda14cbcSMatt Macy case HELP_RENAME: 312*eda14cbcSMatt Macy return (gettext("\trename [-f] <filesystem|volume|snapshot> " 313*eda14cbcSMatt Macy "<filesystem|volume|snapshot>\n" 314*eda14cbcSMatt Macy "\trename [-f] -p <filesystem|volume> <filesystem|volume>\n" 315*eda14cbcSMatt Macy "\trename -r <snapshot> <snapshot>\n")); 316*eda14cbcSMatt Macy case HELP_ROLLBACK: 317*eda14cbcSMatt Macy return (gettext("\trollback [-rRf] <snapshot>\n")); 318*eda14cbcSMatt Macy case HELP_SEND: 319*eda14cbcSMatt Macy return (gettext("\tsend [-DnPpRvLecwhb] [-[i|I] snapshot] " 320*eda14cbcSMatt Macy "<snapshot>\n" 321*eda14cbcSMatt Macy "\tsend [-nvPLecw] [-i snapshot|bookmark] " 322*eda14cbcSMatt Macy "<filesystem|volume|snapshot>\n" 323*eda14cbcSMatt Macy "\tsend [-DnPpvLec] [-i bookmark|snapshot] " 324*eda14cbcSMatt Macy "--redact <bookmark> <snapshot>\n" 325*eda14cbcSMatt Macy "\tsend [-nvPe] -t <receive_resume_token>\n" 326*eda14cbcSMatt Macy "\tsend [-Pnv] --saved filesystem\n")); 327*eda14cbcSMatt Macy case HELP_SET: 328*eda14cbcSMatt Macy return (gettext("\tset <property=value> ... " 329*eda14cbcSMatt Macy "<filesystem|volume|snapshot> ...\n")); 330*eda14cbcSMatt Macy case HELP_SHARE: 331*eda14cbcSMatt Macy return (gettext("\tshare [-l] <-a [nfs|smb] | filesystem>\n")); 332*eda14cbcSMatt Macy case HELP_SNAPSHOT: 333*eda14cbcSMatt Macy return (gettext("\tsnapshot [-r] [-o property=value] ... " 334*eda14cbcSMatt Macy "<filesystem|volume>@<snap> ...\n")); 335*eda14cbcSMatt Macy case HELP_UNMOUNT: 336*eda14cbcSMatt Macy return (gettext("\tunmount [-fu] " 337*eda14cbcSMatt Macy "<-a | filesystem|mountpoint>\n")); 338*eda14cbcSMatt Macy case HELP_UNSHARE: 339*eda14cbcSMatt Macy return (gettext("\tunshare " 340*eda14cbcSMatt Macy "<-a [nfs|smb] | filesystem|mountpoint>\n")); 341*eda14cbcSMatt Macy case HELP_ALLOW: 342*eda14cbcSMatt Macy return (gettext("\tallow <filesystem|volume>\n" 343*eda14cbcSMatt Macy "\tallow [-ldug] " 344*eda14cbcSMatt Macy "<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n" 345*eda14cbcSMatt Macy "\t <filesystem|volume>\n" 346*eda14cbcSMatt Macy "\tallow [-ld] -e <perm|@setname>[,...] " 347*eda14cbcSMatt Macy "<filesystem|volume>\n" 348*eda14cbcSMatt Macy "\tallow -c <perm|@setname>[,...] <filesystem|volume>\n" 349*eda14cbcSMatt Macy "\tallow -s @setname <perm|@setname>[,...] " 350*eda14cbcSMatt Macy "<filesystem|volume>\n")); 351*eda14cbcSMatt Macy case HELP_UNALLOW: 352*eda14cbcSMatt Macy return (gettext("\tunallow [-rldug] " 353*eda14cbcSMatt Macy "<\"everyone\"|user|group>[,...]\n" 354*eda14cbcSMatt Macy "\t [<perm|@setname>[,...]] <filesystem|volume>\n" 355*eda14cbcSMatt Macy "\tunallow [-rld] -e [<perm|@setname>[,...]] " 356*eda14cbcSMatt Macy "<filesystem|volume>\n" 357*eda14cbcSMatt Macy "\tunallow [-r] -c [<perm|@setname>[,...]] " 358*eda14cbcSMatt Macy "<filesystem|volume>\n" 359*eda14cbcSMatt Macy "\tunallow [-r] -s @setname [<perm|@setname>[,...]] " 360*eda14cbcSMatt Macy "<filesystem|volume>\n")); 361*eda14cbcSMatt Macy case HELP_USERSPACE: 362*eda14cbcSMatt Macy return (gettext("\tuserspace [-Hinp] [-o field[,...]] " 363*eda14cbcSMatt Macy "[-s field] ...\n" 364*eda14cbcSMatt Macy "\t [-S field] ... [-t type[,...]] " 365*eda14cbcSMatt Macy "<filesystem|snapshot>\n")); 366*eda14cbcSMatt Macy case HELP_GROUPSPACE: 367*eda14cbcSMatt Macy return (gettext("\tgroupspace [-Hinp] [-o field[,...]] " 368*eda14cbcSMatt Macy "[-s field] ...\n" 369*eda14cbcSMatt Macy "\t [-S field] ... [-t type[,...]] " 370*eda14cbcSMatt Macy "<filesystem|snapshot>\n")); 371*eda14cbcSMatt Macy case HELP_PROJECTSPACE: 372*eda14cbcSMatt Macy return (gettext("\tprojectspace [-Hp] [-o field[,...]] " 373*eda14cbcSMatt Macy "[-s field] ... \n" 374*eda14cbcSMatt Macy "\t [-S field] ... <filesystem|snapshot>\n")); 375*eda14cbcSMatt Macy case HELP_PROJECT: 376*eda14cbcSMatt Macy return (gettext("\tproject [-d|-r] <directory|file ...>\n" 377*eda14cbcSMatt Macy "\tproject -c [-0] [-d|-r] [-p id] <directory|file ...>\n" 378*eda14cbcSMatt Macy "\tproject -C [-k] [-r] <directory ...>\n" 379*eda14cbcSMatt Macy "\tproject [-p id] [-r] [-s] <directory ...>\n")); 380*eda14cbcSMatt Macy case HELP_HOLD: 381*eda14cbcSMatt Macy return (gettext("\thold [-r] <tag> <snapshot> ...\n")); 382*eda14cbcSMatt Macy case HELP_HOLDS: 383*eda14cbcSMatt Macy return (gettext("\tholds [-rH] <snapshot> ...\n")); 384*eda14cbcSMatt Macy case HELP_RELEASE: 385*eda14cbcSMatt Macy return (gettext("\trelease [-r] <tag> <snapshot> ...\n")); 386*eda14cbcSMatt Macy case HELP_DIFF: 387*eda14cbcSMatt Macy return (gettext("\tdiff [-FHt] <snapshot> " 388*eda14cbcSMatt Macy "[snapshot|filesystem]\n")); 389*eda14cbcSMatt Macy case HELP_BOOKMARK: 390*eda14cbcSMatt Macy return (gettext("\tbookmark <snapshot|bookmark> " 391*eda14cbcSMatt Macy "<newbookmark>\n")); 392*eda14cbcSMatt Macy case HELP_CHANNEL_PROGRAM: 393*eda14cbcSMatt Macy return (gettext("\tprogram [-jn] [-t <instruction limit>] " 394*eda14cbcSMatt Macy "[-m <memory limit (b)>]\n" 395*eda14cbcSMatt Macy "\t <pool> <program file> [lua args...]\n")); 396*eda14cbcSMatt Macy case HELP_LOAD_KEY: 397*eda14cbcSMatt Macy return (gettext("\tload-key [-rn] [-L <keylocation>] " 398*eda14cbcSMatt Macy "<-a | filesystem|volume>\n")); 399*eda14cbcSMatt Macy case HELP_UNLOAD_KEY: 400*eda14cbcSMatt Macy return (gettext("\tunload-key [-r] " 401*eda14cbcSMatt Macy "<-a | filesystem|volume>\n")); 402*eda14cbcSMatt Macy case HELP_CHANGE_KEY: 403*eda14cbcSMatt Macy return (gettext("\tchange-key [-l] [-o keyformat=<value>]\n" 404*eda14cbcSMatt Macy "\t [-o keylocation=<value>] [-o pbkfd2iters=<value>]\n" 405*eda14cbcSMatt Macy "\t <filesystem|volume>\n" 406*eda14cbcSMatt Macy "\tchange-key -i [-l] <filesystem|volume>\n")); 407*eda14cbcSMatt Macy case HELP_VERSION: 408*eda14cbcSMatt Macy return (gettext("\tversion\n")); 409*eda14cbcSMatt Macy case HELP_REDACT: 410*eda14cbcSMatt Macy return (gettext("\tredact <snapshot> <bookmark> " 411*eda14cbcSMatt Macy "<redaction_snapshot> ...\n")); 412*eda14cbcSMatt Macy case HELP_JAIL: 413*eda14cbcSMatt Macy return (gettext("\tjail <jailid|jailname> <filesystem>\n")); 414*eda14cbcSMatt Macy case HELP_UNJAIL: 415*eda14cbcSMatt Macy return (gettext("\tunjail <jailid|jailname> <filesystem>\n")); 416*eda14cbcSMatt Macy case HELP_WAIT: 417*eda14cbcSMatt Macy return (gettext("\twait [-t <activity>] <filesystem>\n")); 418*eda14cbcSMatt Macy } 419*eda14cbcSMatt Macy 420*eda14cbcSMatt Macy abort(); 421*eda14cbcSMatt Macy /* NOTREACHED */ 422*eda14cbcSMatt Macy } 423*eda14cbcSMatt Macy 424*eda14cbcSMatt Macy void 425*eda14cbcSMatt Macy nomem(void) 426*eda14cbcSMatt Macy { 427*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("internal error: out of memory\n")); 428*eda14cbcSMatt Macy exit(1); 429*eda14cbcSMatt Macy } 430*eda14cbcSMatt Macy 431*eda14cbcSMatt Macy /* 432*eda14cbcSMatt Macy * Utility function to guarantee malloc() success. 433*eda14cbcSMatt Macy */ 434*eda14cbcSMatt Macy 435*eda14cbcSMatt Macy void * 436*eda14cbcSMatt Macy safe_malloc(size_t size) 437*eda14cbcSMatt Macy { 438*eda14cbcSMatt Macy void *data; 439*eda14cbcSMatt Macy 440*eda14cbcSMatt Macy if ((data = calloc(1, size)) == NULL) 441*eda14cbcSMatt Macy nomem(); 442*eda14cbcSMatt Macy 443*eda14cbcSMatt Macy return (data); 444*eda14cbcSMatt Macy } 445*eda14cbcSMatt Macy 446*eda14cbcSMatt Macy static void * 447*eda14cbcSMatt Macy safe_realloc(void *data, size_t size) 448*eda14cbcSMatt Macy { 449*eda14cbcSMatt Macy void *newp; 450*eda14cbcSMatt Macy if ((newp = realloc(data, size)) == NULL) { 451*eda14cbcSMatt Macy free(data); 452*eda14cbcSMatt Macy nomem(); 453*eda14cbcSMatt Macy } 454*eda14cbcSMatt Macy 455*eda14cbcSMatt Macy return (newp); 456*eda14cbcSMatt Macy } 457*eda14cbcSMatt Macy 458*eda14cbcSMatt Macy static char * 459*eda14cbcSMatt Macy safe_strdup(char *str) 460*eda14cbcSMatt Macy { 461*eda14cbcSMatt Macy char *dupstr = strdup(str); 462*eda14cbcSMatt Macy 463*eda14cbcSMatt Macy if (dupstr == NULL) 464*eda14cbcSMatt Macy nomem(); 465*eda14cbcSMatt Macy 466*eda14cbcSMatt Macy return (dupstr); 467*eda14cbcSMatt Macy } 468*eda14cbcSMatt Macy 469*eda14cbcSMatt Macy /* 470*eda14cbcSMatt Macy * Callback routine that will print out information for each of 471*eda14cbcSMatt Macy * the properties. 472*eda14cbcSMatt Macy */ 473*eda14cbcSMatt Macy static int 474*eda14cbcSMatt Macy usage_prop_cb(int prop, void *cb) 475*eda14cbcSMatt Macy { 476*eda14cbcSMatt Macy FILE *fp = cb; 477*eda14cbcSMatt Macy 478*eda14cbcSMatt Macy (void) fprintf(fp, "\t%-15s ", zfs_prop_to_name(prop)); 479*eda14cbcSMatt Macy 480*eda14cbcSMatt Macy if (zfs_prop_readonly(prop)) 481*eda14cbcSMatt Macy (void) fprintf(fp, " NO "); 482*eda14cbcSMatt Macy else 483*eda14cbcSMatt Macy (void) fprintf(fp, "YES "); 484*eda14cbcSMatt Macy 485*eda14cbcSMatt Macy if (zfs_prop_inheritable(prop)) 486*eda14cbcSMatt Macy (void) fprintf(fp, " YES "); 487*eda14cbcSMatt Macy else 488*eda14cbcSMatt Macy (void) fprintf(fp, " NO "); 489*eda14cbcSMatt Macy 490*eda14cbcSMatt Macy if (zfs_prop_values(prop) == NULL) 491*eda14cbcSMatt Macy (void) fprintf(fp, "-\n"); 492*eda14cbcSMatt Macy else 493*eda14cbcSMatt Macy (void) fprintf(fp, "%s\n", zfs_prop_values(prop)); 494*eda14cbcSMatt Macy 495*eda14cbcSMatt Macy return (ZPROP_CONT); 496*eda14cbcSMatt Macy } 497*eda14cbcSMatt Macy 498*eda14cbcSMatt Macy /* 499*eda14cbcSMatt Macy * Display usage message. If we're inside a command, display only the usage for 500*eda14cbcSMatt Macy * that command. Otherwise, iterate over the entire command table and display 501*eda14cbcSMatt Macy * a complete usage message. 502*eda14cbcSMatt Macy */ 503*eda14cbcSMatt Macy static void 504*eda14cbcSMatt Macy usage(boolean_t requested) 505*eda14cbcSMatt Macy { 506*eda14cbcSMatt Macy int i; 507*eda14cbcSMatt Macy boolean_t show_properties = B_FALSE; 508*eda14cbcSMatt Macy FILE *fp = requested ? stdout : stderr; 509*eda14cbcSMatt Macy 510*eda14cbcSMatt Macy if (current_command == NULL) { 511*eda14cbcSMatt Macy 512*eda14cbcSMatt Macy (void) fprintf(fp, gettext("usage: zfs command args ...\n")); 513*eda14cbcSMatt Macy (void) fprintf(fp, 514*eda14cbcSMatt Macy gettext("where 'command' is one of the following:\n\n")); 515*eda14cbcSMatt Macy 516*eda14cbcSMatt Macy for (i = 0; i < NCOMMAND; i++) { 517*eda14cbcSMatt Macy if (command_table[i].name == NULL) 518*eda14cbcSMatt Macy (void) fprintf(fp, "\n"); 519*eda14cbcSMatt Macy else 520*eda14cbcSMatt Macy (void) fprintf(fp, "%s", 521*eda14cbcSMatt Macy get_usage(command_table[i].usage)); 522*eda14cbcSMatt Macy } 523*eda14cbcSMatt Macy 524*eda14cbcSMatt Macy (void) fprintf(fp, gettext("\nEach dataset is of the form: " 525*eda14cbcSMatt Macy "pool/[dataset/]*dataset[@name]\n")); 526*eda14cbcSMatt Macy } else { 527*eda14cbcSMatt Macy (void) fprintf(fp, gettext("usage:\n")); 528*eda14cbcSMatt Macy (void) fprintf(fp, "%s", get_usage(current_command->usage)); 529*eda14cbcSMatt Macy } 530*eda14cbcSMatt Macy 531*eda14cbcSMatt Macy if (current_command != NULL && 532*eda14cbcSMatt Macy (strcmp(current_command->name, "set") == 0 || 533*eda14cbcSMatt Macy strcmp(current_command->name, "get") == 0 || 534*eda14cbcSMatt Macy strcmp(current_command->name, "inherit") == 0 || 535*eda14cbcSMatt Macy strcmp(current_command->name, "list") == 0)) 536*eda14cbcSMatt Macy show_properties = B_TRUE; 537*eda14cbcSMatt Macy 538*eda14cbcSMatt Macy if (show_properties) { 539*eda14cbcSMatt Macy (void) fprintf(fp, 540*eda14cbcSMatt Macy gettext("\nThe following properties are supported:\n")); 541*eda14cbcSMatt Macy 542*eda14cbcSMatt Macy (void) fprintf(fp, "\n\t%-14s %s %s %s\n\n", 543*eda14cbcSMatt Macy "PROPERTY", "EDIT", "INHERIT", "VALUES"); 544*eda14cbcSMatt Macy 545*eda14cbcSMatt Macy /* Iterate over all properties */ 546*eda14cbcSMatt Macy (void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE, 547*eda14cbcSMatt Macy ZFS_TYPE_DATASET); 548*eda14cbcSMatt Macy 549*eda14cbcSMatt Macy (void) fprintf(fp, "\t%-15s ", "userused@..."); 550*eda14cbcSMatt Macy (void) fprintf(fp, " NO NO <size>\n"); 551*eda14cbcSMatt Macy (void) fprintf(fp, "\t%-15s ", "groupused@..."); 552*eda14cbcSMatt Macy (void) fprintf(fp, " NO NO <size>\n"); 553*eda14cbcSMatt Macy (void) fprintf(fp, "\t%-15s ", "projectused@..."); 554*eda14cbcSMatt Macy (void) fprintf(fp, " NO NO <size>\n"); 555*eda14cbcSMatt Macy (void) fprintf(fp, "\t%-15s ", "userobjused@..."); 556*eda14cbcSMatt Macy (void) fprintf(fp, " NO NO <size>\n"); 557*eda14cbcSMatt Macy (void) fprintf(fp, "\t%-15s ", "groupobjused@..."); 558*eda14cbcSMatt Macy (void) fprintf(fp, " NO NO <size>\n"); 559*eda14cbcSMatt Macy (void) fprintf(fp, "\t%-15s ", "projectobjused@..."); 560*eda14cbcSMatt Macy (void) fprintf(fp, " NO NO <size>\n"); 561*eda14cbcSMatt Macy (void) fprintf(fp, "\t%-15s ", "userquota@..."); 562*eda14cbcSMatt Macy (void) fprintf(fp, "YES NO <size> | none\n"); 563*eda14cbcSMatt Macy (void) fprintf(fp, "\t%-15s ", "groupquota@..."); 564*eda14cbcSMatt Macy (void) fprintf(fp, "YES NO <size> | none\n"); 565*eda14cbcSMatt Macy (void) fprintf(fp, "\t%-15s ", "projectquota@..."); 566*eda14cbcSMatt Macy (void) fprintf(fp, "YES NO <size> | none\n"); 567*eda14cbcSMatt Macy (void) fprintf(fp, "\t%-15s ", "userobjquota@..."); 568*eda14cbcSMatt Macy (void) fprintf(fp, "YES NO <size> | none\n"); 569*eda14cbcSMatt Macy (void) fprintf(fp, "\t%-15s ", "groupobjquota@..."); 570*eda14cbcSMatt Macy (void) fprintf(fp, "YES NO <size> | none\n"); 571*eda14cbcSMatt Macy (void) fprintf(fp, "\t%-15s ", "projectobjquota@..."); 572*eda14cbcSMatt Macy (void) fprintf(fp, "YES NO <size> | none\n"); 573*eda14cbcSMatt Macy (void) fprintf(fp, "\t%-15s ", "written@<snap>"); 574*eda14cbcSMatt Macy (void) fprintf(fp, " NO NO <size>\n"); 575*eda14cbcSMatt Macy (void) fprintf(fp, "\t%-15s ", "written#<bookmark>"); 576*eda14cbcSMatt Macy (void) fprintf(fp, " NO NO <size>\n"); 577*eda14cbcSMatt Macy 578*eda14cbcSMatt Macy (void) fprintf(fp, gettext("\nSizes are specified in bytes " 579*eda14cbcSMatt Macy "with standard units such as K, M, G, etc.\n")); 580*eda14cbcSMatt Macy (void) fprintf(fp, gettext("\nUser-defined properties can " 581*eda14cbcSMatt Macy "be specified by using a name containing a colon (:).\n")); 582*eda14cbcSMatt Macy (void) fprintf(fp, gettext("\nThe {user|group|project}" 583*eda14cbcSMatt Macy "[obj]{used|quota}@ properties must be appended with\n" 584*eda14cbcSMatt Macy "a user|group|project specifier of one of these forms:\n" 585*eda14cbcSMatt Macy " POSIX name (eg: \"matt\")\n" 586*eda14cbcSMatt Macy " POSIX id (eg: \"126829\")\n" 587*eda14cbcSMatt Macy " SMB name@domain (eg: \"matt@sun\")\n" 588*eda14cbcSMatt Macy " SMB SID (eg: \"S-1-234-567-89\")\n")); 589*eda14cbcSMatt Macy } else { 590*eda14cbcSMatt Macy (void) fprintf(fp, 591*eda14cbcSMatt Macy gettext("\nFor the property list, run: %s\n"), 592*eda14cbcSMatt Macy "zfs set|get"); 593*eda14cbcSMatt Macy (void) fprintf(fp, 594*eda14cbcSMatt Macy gettext("\nFor the delegated permission list, run: %s\n"), 595*eda14cbcSMatt Macy "zfs allow|unallow"); 596*eda14cbcSMatt Macy } 597*eda14cbcSMatt Macy 598*eda14cbcSMatt Macy /* 599*eda14cbcSMatt Macy * See comments at end of main(). 600*eda14cbcSMatt Macy */ 601*eda14cbcSMatt Macy if (getenv("ZFS_ABORT") != NULL) { 602*eda14cbcSMatt Macy (void) printf("dumping core by request\n"); 603*eda14cbcSMatt Macy abort(); 604*eda14cbcSMatt Macy } 605*eda14cbcSMatt Macy 606*eda14cbcSMatt Macy exit(requested ? 0 : 2); 607*eda14cbcSMatt Macy } 608*eda14cbcSMatt Macy 609*eda14cbcSMatt Macy /* 610*eda14cbcSMatt Macy * Take a property=value argument string and add it to the given nvlist. 611*eda14cbcSMatt Macy * Modifies the argument inplace. 612*eda14cbcSMatt Macy */ 613*eda14cbcSMatt Macy static boolean_t 614*eda14cbcSMatt Macy parseprop(nvlist_t *props, char *propname) 615*eda14cbcSMatt Macy { 616*eda14cbcSMatt Macy char *propval; 617*eda14cbcSMatt Macy 618*eda14cbcSMatt Macy if ((propval = strchr(propname, '=')) == NULL) { 619*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing " 620*eda14cbcSMatt Macy "'=' for property=value argument\n")); 621*eda14cbcSMatt Macy return (B_FALSE); 622*eda14cbcSMatt Macy } 623*eda14cbcSMatt Macy *propval = '\0'; 624*eda14cbcSMatt Macy propval++; 625*eda14cbcSMatt Macy if (nvlist_exists(props, propname)) { 626*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("property '%s' " 627*eda14cbcSMatt Macy "specified multiple times\n"), propname); 628*eda14cbcSMatt Macy return (B_FALSE); 629*eda14cbcSMatt Macy } 630*eda14cbcSMatt Macy if (nvlist_add_string(props, propname, propval) != 0) 631*eda14cbcSMatt Macy nomem(); 632*eda14cbcSMatt Macy return (B_TRUE); 633*eda14cbcSMatt Macy } 634*eda14cbcSMatt Macy 635*eda14cbcSMatt Macy /* 636*eda14cbcSMatt Macy * Take a property name argument and add it to the given nvlist. 637*eda14cbcSMatt Macy * Modifies the argument inplace. 638*eda14cbcSMatt Macy */ 639*eda14cbcSMatt Macy static boolean_t 640*eda14cbcSMatt Macy parsepropname(nvlist_t *props, char *propname) 641*eda14cbcSMatt Macy { 642*eda14cbcSMatt Macy if (strchr(propname, '=') != NULL) { 643*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid character " 644*eda14cbcSMatt Macy "'=' in property argument\n")); 645*eda14cbcSMatt Macy return (B_FALSE); 646*eda14cbcSMatt Macy } 647*eda14cbcSMatt Macy if (nvlist_exists(props, propname)) { 648*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("property '%s' " 649*eda14cbcSMatt Macy "specified multiple times\n"), propname); 650*eda14cbcSMatt Macy return (B_FALSE); 651*eda14cbcSMatt Macy } 652*eda14cbcSMatt Macy if (nvlist_add_boolean(props, propname) != 0) 653*eda14cbcSMatt Macy nomem(); 654*eda14cbcSMatt Macy return (B_TRUE); 655*eda14cbcSMatt Macy } 656*eda14cbcSMatt Macy 657*eda14cbcSMatt Macy static int 658*eda14cbcSMatt Macy parse_depth(char *opt, int *flags) 659*eda14cbcSMatt Macy { 660*eda14cbcSMatt Macy char *tmp; 661*eda14cbcSMatt Macy int depth; 662*eda14cbcSMatt Macy 663*eda14cbcSMatt Macy depth = (int)strtol(opt, &tmp, 0); 664*eda14cbcSMatt Macy if (*tmp) { 665*eda14cbcSMatt Macy (void) fprintf(stderr, 666*eda14cbcSMatt Macy gettext("%s is not an integer\n"), optarg); 667*eda14cbcSMatt Macy usage(B_FALSE); 668*eda14cbcSMatt Macy } 669*eda14cbcSMatt Macy if (depth < 0) { 670*eda14cbcSMatt Macy (void) fprintf(stderr, 671*eda14cbcSMatt Macy gettext("Depth can not be negative.\n")); 672*eda14cbcSMatt Macy usage(B_FALSE); 673*eda14cbcSMatt Macy } 674*eda14cbcSMatt Macy *flags |= (ZFS_ITER_DEPTH_LIMIT|ZFS_ITER_RECURSE); 675*eda14cbcSMatt Macy return (depth); 676*eda14cbcSMatt Macy } 677*eda14cbcSMatt Macy 678*eda14cbcSMatt Macy #define PROGRESS_DELAY 2 /* seconds */ 679*eda14cbcSMatt Macy 680*eda14cbcSMatt Macy static char *pt_reverse = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"; 681*eda14cbcSMatt Macy static time_t pt_begin; 682*eda14cbcSMatt Macy static char *pt_header = NULL; 683*eda14cbcSMatt Macy static boolean_t pt_shown; 684*eda14cbcSMatt Macy 685*eda14cbcSMatt Macy static void 686*eda14cbcSMatt Macy start_progress_timer(void) 687*eda14cbcSMatt Macy { 688*eda14cbcSMatt Macy pt_begin = time(NULL) + PROGRESS_DELAY; 689*eda14cbcSMatt Macy pt_shown = B_FALSE; 690*eda14cbcSMatt Macy } 691*eda14cbcSMatt Macy 692*eda14cbcSMatt Macy static void 693*eda14cbcSMatt Macy set_progress_header(char *header) 694*eda14cbcSMatt Macy { 695*eda14cbcSMatt Macy assert(pt_header == NULL); 696*eda14cbcSMatt Macy pt_header = safe_strdup(header); 697*eda14cbcSMatt Macy if (pt_shown) { 698*eda14cbcSMatt Macy (void) printf("%s: ", header); 699*eda14cbcSMatt Macy (void) fflush(stdout); 700*eda14cbcSMatt Macy } 701*eda14cbcSMatt Macy } 702*eda14cbcSMatt Macy 703*eda14cbcSMatt Macy static void 704*eda14cbcSMatt Macy update_progress(char *update) 705*eda14cbcSMatt Macy { 706*eda14cbcSMatt Macy if (!pt_shown && time(NULL) > pt_begin) { 707*eda14cbcSMatt Macy int len = strlen(update); 708*eda14cbcSMatt Macy 709*eda14cbcSMatt Macy (void) printf("%s: %s%*.*s", pt_header, update, len, len, 710*eda14cbcSMatt Macy pt_reverse); 711*eda14cbcSMatt Macy (void) fflush(stdout); 712*eda14cbcSMatt Macy pt_shown = B_TRUE; 713*eda14cbcSMatt Macy } else if (pt_shown) { 714*eda14cbcSMatt Macy int len = strlen(update); 715*eda14cbcSMatt Macy 716*eda14cbcSMatt Macy (void) printf("%s%*.*s", update, len, len, pt_reverse); 717*eda14cbcSMatt Macy (void) fflush(stdout); 718*eda14cbcSMatt Macy } 719*eda14cbcSMatt Macy } 720*eda14cbcSMatt Macy 721*eda14cbcSMatt Macy static void 722*eda14cbcSMatt Macy finish_progress(char *done) 723*eda14cbcSMatt Macy { 724*eda14cbcSMatt Macy if (pt_shown) { 725*eda14cbcSMatt Macy (void) printf("%s\n", done); 726*eda14cbcSMatt Macy (void) fflush(stdout); 727*eda14cbcSMatt Macy } 728*eda14cbcSMatt Macy free(pt_header); 729*eda14cbcSMatt Macy pt_header = NULL; 730*eda14cbcSMatt Macy } 731*eda14cbcSMatt Macy 732*eda14cbcSMatt Macy static int 733*eda14cbcSMatt Macy zfs_mount_and_share(libzfs_handle_t *hdl, const char *dataset, zfs_type_t type) 734*eda14cbcSMatt Macy { 735*eda14cbcSMatt Macy zfs_handle_t *zhp = NULL; 736*eda14cbcSMatt Macy int ret = 0; 737*eda14cbcSMatt Macy 738*eda14cbcSMatt Macy zhp = zfs_open(hdl, dataset, type); 739*eda14cbcSMatt Macy if (zhp == NULL) 740*eda14cbcSMatt Macy return (1); 741*eda14cbcSMatt Macy 742*eda14cbcSMatt Macy /* 743*eda14cbcSMatt Macy * Volumes may neither be mounted or shared. Potentially in the 744*eda14cbcSMatt Macy * future filesystems detected on these volumes could be mounted. 745*eda14cbcSMatt Macy */ 746*eda14cbcSMatt Macy if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 747*eda14cbcSMatt Macy zfs_close(zhp); 748*eda14cbcSMatt Macy return (0); 749*eda14cbcSMatt Macy } 750*eda14cbcSMatt Macy 751*eda14cbcSMatt Macy /* 752*eda14cbcSMatt Macy * Mount and/or share the new filesystem as appropriate. We provide a 753*eda14cbcSMatt Macy * verbose error message to let the user know that their filesystem was 754*eda14cbcSMatt Macy * in fact created, even if we failed to mount or share it. 755*eda14cbcSMatt Macy * 756*eda14cbcSMatt Macy * If the user doesn't want the dataset automatically mounted, then 757*eda14cbcSMatt Macy * skip the mount/share step 758*eda14cbcSMatt Macy */ 759*eda14cbcSMatt Macy if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type, B_FALSE) && 760*eda14cbcSMatt Macy zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON) { 761*eda14cbcSMatt Macy if (zfs_mount_delegation_check()) { 762*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("filesystem " 763*eda14cbcSMatt Macy "successfully created, but it may only be " 764*eda14cbcSMatt Macy "mounted by root\n")); 765*eda14cbcSMatt Macy ret = 1; 766*eda14cbcSMatt Macy } else if (zfs_mount(zhp, NULL, 0) != 0) { 767*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("filesystem " 768*eda14cbcSMatt Macy "successfully created, but not mounted\n")); 769*eda14cbcSMatt Macy ret = 1; 770*eda14cbcSMatt Macy } else if (zfs_share(zhp) != 0) { 771*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("filesystem " 772*eda14cbcSMatt Macy "successfully created, but not shared\n")); 773*eda14cbcSMatt Macy ret = 1; 774*eda14cbcSMatt Macy } 775*eda14cbcSMatt Macy zfs_commit_all_shares(); 776*eda14cbcSMatt Macy } 777*eda14cbcSMatt Macy 778*eda14cbcSMatt Macy zfs_close(zhp); 779*eda14cbcSMatt Macy 780*eda14cbcSMatt Macy return (ret); 781*eda14cbcSMatt Macy } 782*eda14cbcSMatt Macy 783*eda14cbcSMatt Macy /* 784*eda14cbcSMatt Macy * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol> 785*eda14cbcSMatt Macy * 786*eda14cbcSMatt Macy * Given an existing dataset, create a writable copy whose initial contents 787*eda14cbcSMatt Macy * are the same as the source. The newly created dataset maintains a 788*eda14cbcSMatt Macy * dependency on the original; the original cannot be destroyed so long as 789*eda14cbcSMatt Macy * the clone exists. 790*eda14cbcSMatt Macy * 791*eda14cbcSMatt Macy * The '-p' flag creates all the non-existing ancestors of the target first. 792*eda14cbcSMatt Macy */ 793*eda14cbcSMatt Macy static int 794*eda14cbcSMatt Macy zfs_do_clone(int argc, char **argv) 795*eda14cbcSMatt Macy { 796*eda14cbcSMatt Macy zfs_handle_t *zhp = NULL; 797*eda14cbcSMatt Macy boolean_t parents = B_FALSE; 798*eda14cbcSMatt Macy nvlist_t *props; 799*eda14cbcSMatt Macy int ret = 0; 800*eda14cbcSMatt Macy int c; 801*eda14cbcSMatt Macy 802*eda14cbcSMatt Macy if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 803*eda14cbcSMatt Macy nomem(); 804*eda14cbcSMatt Macy 805*eda14cbcSMatt Macy /* check options */ 806*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "o:p")) != -1) { 807*eda14cbcSMatt Macy switch (c) { 808*eda14cbcSMatt Macy case 'o': 809*eda14cbcSMatt Macy if (!parseprop(props, optarg)) { 810*eda14cbcSMatt Macy nvlist_free(props); 811*eda14cbcSMatt Macy return (1); 812*eda14cbcSMatt Macy } 813*eda14cbcSMatt Macy break; 814*eda14cbcSMatt Macy case 'p': 815*eda14cbcSMatt Macy parents = B_TRUE; 816*eda14cbcSMatt Macy break; 817*eda14cbcSMatt Macy case '?': 818*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 819*eda14cbcSMatt Macy optopt); 820*eda14cbcSMatt Macy goto usage; 821*eda14cbcSMatt Macy } 822*eda14cbcSMatt Macy } 823*eda14cbcSMatt Macy 824*eda14cbcSMatt Macy argc -= optind; 825*eda14cbcSMatt Macy argv += optind; 826*eda14cbcSMatt Macy 827*eda14cbcSMatt Macy /* check number of arguments */ 828*eda14cbcSMatt Macy if (argc < 1) { 829*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing source dataset " 830*eda14cbcSMatt Macy "argument\n")); 831*eda14cbcSMatt Macy goto usage; 832*eda14cbcSMatt Macy } 833*eda14cbcSMatt Macy if (argc < 2) { 834*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing target dataset " 835*eda14cbcSMatt Macy "argument\n")); 836*eda14cbcSMatt Macy goto usage; 837*eda14cbcSMatt Macy } 838*eda14cbcSMatt Macy if (argc > 2) { 839*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many arguments\n")); 840*eda14cbcSMatt Macy goto usage; 841*eda14cbcSMatt Macy } 842*eda14cbcSMatt Macy 843*eda14cbcSMatt Macy /* open the source dataset */ 844*eda14cbcSMatt Macy if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) { 845*eda14cbcSMatt Macy nvlist_free(props); 846*eda14cbcSMatt Macy return (1); 847*eda14cbcSMatt Macy } 848*eda14cbcSMatt Macy 849*eda14cbcSMatt Macy if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM | 850*eda14cbcSMatt Macy ZFS_TYPE_VOLUME)) { 851*eda14cbcSMatt Macy /* 852*eda14cbcSMatt Macy * Now create the ancestors of the target dataset. If the 853*eda14cbcSMatt Macy * target already exists and '-p' option was used we should not 854*eda14cbcSMatt Macy * complain. 855*eda14cbcSMatt Macy */ 856*eda14cbcSMatt Macy if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | 857*eda14cbcSMatt Macy ZFS_TYPE_VOLUME)) { 858*eda14cbcSMatt Macy zfs_close(zhp); 859*eda14cbcSMatt Macy nvlist_free(props); 860*eda14cbcSMatt Macy return (0); 861*eda14cbcSMatt Macy } 862*eda14cbcSMatt Macy if (zfs_create_ancestors(g_zfs, argv[1]) != 0) { 863*eda14cbcSMatt Macy zfs_close(zhp); 864*eda14cbcSMatt Macy nvlist_free(props); 865*eda14cbcSMatt Macy return (1); 866*eda14cbcSMatt Macy } 867*eda14cbcSMatt Macy } 868*eda14cbcSMatt Macy 869*eda14cbcSMatt Macy /* pass to libzfs */ 870*eda14cbcSMatt Macy ret = zfs_clone(zhp, argv[1], props); 871*eda14cbcSMatt Macy 872*eda14cbcSMatt Macy /* create the mountpoint if necessary */ 873*eda14cbcSMatt Macy if (ret == 0) { 874*eda14cbcSMatt Macy if (log_history) { 875*eda14cbcSMatt Macy (void) zpool_log_history(g_zfs, history_str); 876*eda14cbcSMatt Macy log_history = B_FALSE; 877*eda14cbcSMatt Macy } 878*eda14cbcSMatt Macy 879*eda14cbcSMatt Macy ret = zfs_mount_and_share(g_zfs, argv[1], ZFS_TYPE_DATASET); 880*eda14cbcSMatt Macy } 881*eda14cbcSMatt Macy 882*eda14cbcSMatt Macy zfs_close(zhp); 883*eda14cbcSMatt Macy nvlist_free(props); 884*eda14cbcSMatt Macy 885*eda14cbcSMatt Macy return (!!ret); 886*eda14cbcSMatt Macy 887*eda14cbcSMatt Macy usage: 888*eda14cbcSMatt Macy ASSERT3P(zhp, ==, NULL); 889*eda14cbcSMatt Macy nvlist_free(props); 890*eda14cbcSMatt Macy usage(B_FALSE); 891*eda14cbcSMatt Macy return (-1); 892*eda14cbcSMatt Macy } 893*eda14cbcSMatt Macy 894*eda14cbcSMatt Macy /* 895*eda14cbcSMatt Macy * zfs create [-Pnpv] [-o prop=value] ... fs 896*eda14cbcSMatt Macy * zfs create [-Pnpsv] [-b blocksize] [-o prop=value] ... -V vol size 897*eda14cbcSMatt Macy * 898*eda14cbcSMatt Macy * Create a new dataset. This command can be used to create filesystems 899*eda14cbcSMatt Macy * and volumes. Snapshot creation is handled by 'zfs snapshot'. 900*eda14cbcSMatt Macy * For volumes, the user must specify a size to be used. 901*eda14cbcSMatt Macy * 902*eda14cbcSMatt Macy * The '-s' flag applies only to volumes, and indicates that we should not try 903*eda14cbcSMatt Macy * to set the reservation for this volume. By default we set a reservation 904*eda14cbcSMatt Macy * equal to the size for any volume. For pools with SPA_VERSION >= 905*eda14cbcSMatt Macy * SPA_VERSION_REFRESERVATION, we set a refreservation instead. 906*eda14cbcSMatt Macy * 907*eda14cbcSMatt Macy * The '-p' flag creates all the non-existing ancestors of the target first. 908*eda14cbcSMatt Macy * 909*eda14cbcSMatt Macy * The '-n' flag is no-op (dry run) mode. This will perform a user-space sanity 910*eda14cbcSMatt Macy * check of arguments and properties, but does not check for permissions, 911*eda14cbcSMatt Macy * available space, etc. 912*eda14cbcSMatt Macy * 913*eda14cbcSMatt Macy * The '-v' flag is for verbose output. 914*eda14cbcSMatt Macy * 915*eda14cbcSMatt Macy * The '-P' flag is used for parseable output. It implies '-v'. 916*eda14cbcSMatt Macy */ 917*eda14cbcSMatt Macy static int 918*eda14cbcSMatt Macy zfs_do_create(int argc, char **argv) 919*eda14cbcSMatt Macy { 920*eda14cbcSMatt Macy zfs_type_t type = ZFS_TYPE_FILESYSTEM; 921*eda14cbcSMatt Macy zpool_handle_t *zpool_handle = NULL; 922*eda14cbcSMatt Macy nvlist_t *real_props = NULL; 923*eda14cbcSMatt Macy uint64_t volsize = 0; 924*eda14cbcSMatt Macy int c; 925*eda14cbcSMatt Macy boolean_t noreserve = B_FALSE; 926*eda14cbcSMatt Macy boolean_t bflag = B_FALSE; 927*eda14cbcSMatt Macy boolean_t parents = B_FALSE; 928*eda14cbcSMatt Macy boolean_t dryrun = B_FALSE; 929*eda14cbcSMatt Macy boolean_t verbose = B_FALSE; 930*eda14cbcSMatt Macy boolean_t parseable = B_FALSE; 931*eda14cbcSMatt Macy int ret = 1; 932*eda14cbcSMatt Macy nvlist_t *props; 933*eda14cbcSMatt Macy uint64_t intval; 934*eda14cbcSMatt Macy 935*eda14cbcSMatt Macy if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 936*eda14cbcSMatt Macy nomem(); 937*eda14cbcSMatt Macy 938*eda14cbcSMatt Macy /* check options */ 939*eda14cbcSMatt Macy while ((c = getopt(argc, argv, ":PV:b:nso:pv")) != -1) { 940*eda14cbcSMatt Macy switch (c) { 941*eda14cbcSMatt Macy case 'V': 942*eda14cbcSMatt Macy type = ZFS_TYPE_VOLUME; 943*eda14cbcSMatt Macy if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 944*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("bad volume " 945*eda14cbcSMatt Macy "size '%s': %s\n"), optarg, 946*eda14cbcSMatt Macy libzfs_error_description(g_zfs)); 947*eda14cbcSMatt Macy goto error; 948*eda14cbcSMatt Macy } 949*eda14cbcSMatt Macy 950*eda14cbcSMatt Macy if (nvlist_add_uint64(props, 951*eda14cbcSMatt Macy zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0) 952*eda14cbcSMatt Macy nomem(); 953*eda14cbcSMatt Macy volsize = intval; 954*eda14cbcSMatt Macy break; 955*eda14cbcSMatt Macy case 'P': 956*eda14cbcSMatt Macy verbose = B_TRUE; 957*eda14cbcSMatt Macy parseable = B_TRUE; 958*eda14cbcSMatt Macy break; 959*eda14cbcSMatt Macy case 'p': 960*eda14cbcSMatt Macy parents = B_TRUE; 961*eda14cbcSMatt Macy break; 962*eda14cbcSMatt Macy case 'b': 963*eda14cbcSMatt Macy bflag = B_TRUE; 964*eda14cbcSMatt Macy if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 965*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("bad volume " 966*eda14cbcSMatt Macy "block size '%s': %s\n"), optarg, 967*eda14cbcSMatt Macy libzfs_error_description(g_zfs)); 968*eda14cbcSMatt Macy goto error; 969*eda14cbcSMatt Macy } 970*eda14cbcSMatt Macy 971*eda14cbcSMatt Macy if (nvlist_add_uint64(props, 972*eda14cbcSMatt Macy zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 973*eda14cbcSMatt Macy intval) != 0) 974*eda14cbcSMatt Macy nomem(); 975*eda14cbcSMatt Macy break; 976*eda14cbcSMatt Macy case 'n': 977*eda14cbcSMatt Macy dryrun = B_TRUE; 978*eda14cbcSMatt Macy break; 979*eda14cbcSMatt Macy case 'o': 980*eda14cbcSMatt Macy if (!parseprop(props, optarg)) 981*eda14cbcSMatt Macy goto error; 982*eda14cbcSMatt Macy break; 983*eda14cbcSMatt Macy case 's': 984*eda14cbcSMatt Macy noreserve = B_TRUE; 985*eda14cbcSMatt Macy break; 986*eda14cbcSMatt Macy case 'v': 987*eda14cbcSMatt Macy verbose = B_TRUE; 988*eda14cbcSMatt Macy break; 989*eda14cbcSMatt Macy case ':': 990*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing size " 991*eda14cbcSMatt Macy "argument\n")); 992*eda14cbcSMatt Macy goto badusage; 993*eda14cbcSMatt Macy case '?': 994*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 995*eda14cbcSMatt Macy optopt); 996*eda14cbcSMatt Macy goto badusage; 997*eda14cbcSMatt Macy } 998*eda14cbcSMatt Macy } 999*eda14cbcSMatt Macy 1000*eda14cbcSMatt Macy if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) { 1001*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("'-s' and '-b' can only be " 1002*eda14cbcSMatt Macy "used when creating a volume\n")); 1003*eda14cbcSMatt Macy goto badusage; 1004*eda14cbcSMatt Macy } 1005*eda14cbcSMatt Macy 1006*eda14cbcSMatt Macy argc -= optind; 1007*eda14cbcSMatt Macy argv += optind; 1008*eda14cbcSMatt Macy 1009*eda14cbcSMatt Macy /* check number of arguments */ 1010*eda14cbcSMatt Macy if (argc == 0) { 1011*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing %s argument\n"), 1012*eda14cbcSMatt Macy zfs_type_to_name(type)); 1013*eda14cbcSMatt Macy goto badusage; 1014*eda14cbcSMatt Macy } 1015*eda14cbcSMatt Macy if (argc > 1) { 1016*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many arguments\n")); 1017*eda14cbcSMatt Macy goto badusage; 1018*eda14cbcSMatt Macy } 1019*eda14cbcSMatt Macy 1020*eda14cbcSMatt Macy if (dryrun || (type == ZFS_TYPE_VOLUME && !noreserve)) { 1021*eda14cbcSMatt Macy char msg[ZFS_MAX_DATASET_NAME_LEN * 2]; 1022*eda14cbcSMatt Macy char *p; 1023*eda14cbcSMatt Macy 1024*eda14cbcSMatt Macy if ((p = strchr(argv[0], '/')) != NULL) 1025*eda14cbcSMatt Macy *p = '\0'; 1026*eda14cbcSMatt Macy zpool_handle = zpool_open(g_zfs, argv[0]); 1027*eda14cbcSMatt Macy if (p != NULL) 1028*eda14cbcSMatt Macy *p = '/'; 1029*eda14cbcSMatt Macy if (zpool_handle == NULL) 1030*eda14cbcSMatt Macy goto error; 1031*eda14cbcSMatt Macy 1032*eda14cbcSMatt Macy (void) snprintf(msg, sizeof (msg), 1033*eda14cbcSMatt Macy dryrun ? gettext("cannot verify '%s'") : 1034*eda14cbcSMatt Macy gettext("cannot create '%s'"), argv[0]); 1035*eda14cbcSMatt Macy if (props && (real_props = zfs_valid_proplist(g_zfs, type, 1036*eda14cbcSMatt Macy props, 0, NULL, zpool_handle, B_TRUE, msg)) == NULL) { 1037*eda14cbcSMatt Macy zpool_close(zpool_handle); 1038*eda14cbcSMatt Macy goto error; 1039*eda14cbcSMatt Macy } 1040*eda14cbcSMatt Macy } 1041*eda14cbcSMatt Macy 1042*eda14cbcSMatt Macy /* 1043*eda14cbcSMatt Macy * if volsize is not a multiple of volblocksize, round it up to the 1044*eda14cbcSMatt Macy * nearest multiple of the volblocksize 1045*eda14cbcSMatt Macy */ 1046*eda14cbcSMatt Macy if (type == ZFS_TYPE_VOLUME) { 1047*eda14cbcSMatt Macy uint64_t volblocksize; 1048*eda14cbcSMatt Macy 1049*eda14cbcSMatt Macy if (nvlist_lookup_uint64(props, 1050*eda14cbcSMatt Macy zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 1051*eda14cbcSMatt Macy &volblocksize) != 0) 1052*eda14cbcSMatt Macy volblocksize = ZVOL_DEFAULT_BLOCKSIZE; 1053*eda14cbcSMatt Macy 1054*eda14cbcSMatt Macy if (volsize % volblocksize) { 1055*eda14cbcSMatt Macy volsize = P2ROUNDUP_TYPED(volsize, volblocksize, 1056*eda14cbcSMatt Macy uint64_t); 1057*eda14cbcSMatt Macy 1058*eda14cbcSMatt Macy if (nvlist_add_uint64(props, 1059*eda14cbcSMatt Macy zfs_prop_to_name(ZFS_PROP_VOLSIZE), volsize) != 0) { 1060*eda14cbcSMatt Macy nvlist_free(props); 1061*eda14cbcSMatt Macy nomem(); 1062*eda14cbcSMatt Macy } 1063*eda14cbcSMatt Macy } 1064*eda14cbcSMatt Macy } 1065*eda14cbcSMatt Macy 1066*eda14cbcSMatt Macy 1067*eda14cbcSMatt Macy if (type == ZFS_TYPE_VOLUME && !noreserve) { 1068*eda14cbcSMatt Macy uint64_t spa_version; 1069*eda14cbcSMatt Macy zfs_prop_t resv_prop; 1070*eda14cbcSMatt Macy char *strval; 1071*eda14cbcSMatt Macy 1072*eda14cbcSMatt Macy spa_version = zpool_get_prop_int(zpool_handle, 1073*eda14cbcSMatt Macy ZPOOL_PROP_VERSION, NULL); 1074*eda14cbcSMatt Macy if (spa_version >= SPA_VERSION_REFRESERVATION) 1075*eda14cbcSMatt Macy resv_prop = ZFS_PROP_REFRESERVATION; 1076*eda14cbcSMatt Macy else 1077*eda14cbcSMatt Macy resv_prop = ZFS_PROP_RESERVATION; 1078*eda14cbcSMatt Macy 1079*eda14cbcSMatt Macy volsize = zvol_volsize_to_reservation(zpool_handle, volsize, 1080*eda14cbcSMatt Macy real_props); 1081*eda14cbcSMatt Macy 1082*eda14cbcSMatt Macy if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop), 1083*eda14cbcSMatt Macy &strval) != 0) { 1084*eda14cbcSMatt Macy if (nvlist_add_uint64(props, 1085*eda14cbcSMatt Macy zfs_prop_to_name(resv_prop), volsize) != 0) { 1086*eda14cbcSMatt Macy nvlist_free(props); 1087*eda14cbcSMatt Macy nomem(); 1088*eda14cbcSMatt Macy } 1089*eda14cbcSMatt Macy } 1090*eda14cbcSMatt Macy } 1091*eda14cbcSMatt Macy if (zpool_handle != NULL) { 1092*eda14cbcSMatt Macy zpool_close(zpool_handle); 1093*eda14cbcSMatt Macy nvlist_free(real_props); 1094*eda14cbcSMatt Macy } 1095*eda14cbcSMatt Macy 1096*eda14cbcSMatt Macy if (parents && zfs_name_valid(argv[0], type)) { 1097*eda14cbcSMatt Macy /* 1098*eda14cbcSMatt Macy * Now create the ancestors of target dataset. If the target 1099*eda14cbcSMatt Macy * already exists and '-p' option was used we should not 1100*eda14cbcSMatt Macy * complain. 1101*eda14cbcSMatt Macy */ 1102*eda14cbcSMatt Macy if (zfs_dataset_exists(g_zfs, argv[0], type)) { 1103*eda14cbcSMatt Macy ret = 0; 1104*eda14cbcSMatt Macy goto error; 1105*eda14cbcSMatt Macy } 1106*eda14cbcSMatt Macy if (verbose) { 1107*eda14cbcSMatt Macy (void) printf(parseable ? "create_ancestors\t%s\n" : 1108*eda14cbcSMatt Macy dryrun ? "would create ancestors of %s\n" : 1109*eda14cbcSMatt Macy "create ancestors of %s\n", argv[0]); 1110*eda14cbcSMatt Macy } 1111*eda14cbcSMatt Macy if (!dryrun) { 1112*eda14cbcSMatt Macy if (zfs_create_ancestors(g_zfs, argv[0]) != 0) { 1113*eda14cbcSMatt Macy goto error; 1114*eda14cbcSMatt Macy } 1115*eda14cbcSMatt Macy } 1116*eda14cbcSMatt Macy } 1117*eda14cbcSMatt Macy 1118*eda14cbcSMatt Macy if (verbose) { 1119*eda14cbcSMatt Macy nvpair_t *nvp = NULL; 1120*eda14cbcSMatt Macy (void) printf(parseable ? "create\t%s\n" : 1121*eda14cbcSMatt Macy dryrun ? "would create %s\n" : "create %s\n", argv[0]); 1122*eda14cbcSMatt Macy while ((nvp = nvlist_next_nvpair(props, nvp)) != NULL) { 1123*eda14cbcSMatt Macy uint64_t uval; 1124*eda14cbcSMatt Macy char *sval; 1125*eda14cbcSMatt Macy 1126*eda14cbcSMatt Macy switch (nvpair_type(nvp)) { 1127*eda14cbcSMatt Macy case DATA_TYPE_UINT64: 1128*eda14cbcSMatt Macy VERIFY0(nvpair_value_uint64(nvp, &uval)); 1129*eda14cbcSMatt Macy (void) printf(parseable ? 1130*eda14cbcSMatt Macy "property\t%s\t%llu\n" : "\t%s=%llu\n", 1131*eda14cbcSMatt Macy nvpair_name(nvp), (u_longlong_t)uval); 1132*eda14cbcSMatt Macy break; 1133*eda14cbcSMatt Macy case DATA_TYPE_STRING: 1134*eda14cbcSMatt Macy VERIFY0(nvpair_value_string(nvp, &sval)); 1135*eda14cbcSMatt Macy (void) printf(parseable ? 1136*eda14cbcSMatt Macy "property\t%s\t%s\n" : "\t%s=%s\n", 1137*eda14cbcSMatt Macy nvpair_name(nvp), sval); 1138*eda14cbcSMatt Macy break; 1139*eda14cbcSMatt Macy default: 1140*eda14cbcSMatt Macy (void) fprintf(stderr, "property '%s' " 1141*eda14cbcSMatt Macy "has illegal type %d\n", 1142*eda14cbcSMatt Macy nvpair_name(nvp), nvpair_type(nvp)); 1143*eda14cbcSMatt Macy abort(); 1144*eda14cbcSMatt Macy } 1145*eda14cbcSMatt Macy } 1146*eda14cbcSMatt Macy } 1147*eda14cbcSMatt Macy if (dryrun) { 1148*eda14cbcSMatt Macy ret = 0; 1149*eda14cbcSMatt Macy goto error; 1150*eda14cbcSMatt Macy } 1151*eda14cbcSMatt Macy 1152*eda14cbcSMatt Macy /* pass to libzfs */ 1153*eda14cbcSMatt Macy if (zfs_create(g_zfs, argv[0], type, props) != 0) 1154*eda14cbcSMatt Macy goto error; 1155*eda14cbcSMatt Macy 1156*eda14cbcSMatt Macy if (log_history) { 1157*eda14cbcSMatt Macy (void) zpool_log_history(g_zfs, history_str); 1158*eda14cbcSMatt Macy log_history = B_FALSE; 1159*eda14cbcSMatt Macy } 1160*eda14cbcSMatt Macy 1161*eda14cbcSMatt Macy ret = zfs_mount_and_share(g_zfs, argv[0], ZFS_TYPE_DATASET); 1162*eda14cbcSMatt Macy error: 1163*eda14cbcSMatt Macy nvlist_free(props); 1164*eda14cbcSMatt Macy return (ret); 1165*eda14cbcSMatt Macy badusage: 1166*eda14cbcSMatt Macy nvlist_free(props); 1167*eda14cbcSMatt Macy usage(B_FALSE); 1168*eda14cbcSMatt Macy return (2); 1169*eda14cbcSMatt Macy } 1170*eda14cbcSMatt Macy 1171*eda14cbcSMatt Macy /* 1172*eda14cbcSMatt Macy * zfs destroy [-rRf] <fs, vol> 1173*eda14cbcSMatt Macy * zfs destroy [-rRd] <snap> 1174*eda14cbcSMatt Macy * 1175*eda14cbcSMatt Macy * -r Recursively destroy all children 1176*eda14cbcSMatt Macy * -R Recursively destroy all dependents, including clones 1177*eda14cbcSMatt Macy * -f Force unmounting of any dependents 1178*eda14cbcSMatt Macy * -d If we can't destroy now, mark for deferred destruction 1179*eda14cbcSMatt Macy * 1180*eda14cbcSMatt Macy * Destroys the given dataset. By default, it will unmount any filesystems, 1181*eda14cbcSMatt Macy * and refuse to destroy a dataset that has any dependents. A dependent can 1182*eda14cbcSMatt Macy * either be a child, or a clone of a child. 1183*eda14cbcSMatt Macy */ 1184*eda14cbcSMatt Macy typedef struct destroy_cbdata { 1185*eda14cbcSMatt Macy boolean_t cb_first; 1186*eda14cbcSMatt Macy boolean_t cb_force; 1187*eda14cbcSMatt Macy boolean_t cb_recurse; 1188*eda14cbcSMatt Macy boolean_t cb_error; 1189*eda14cbcSMatt Macy boolean_t cb_doclones; 1190*eda14cbcSMatt Macy zfs_handle_t *cb_target; 1191*eda14cbcSMatt Macy boolean_t cb_defer_destroy; 1192*eda14cbcSMatt Macy boolean_t cb_verbose; 1193*eda14cbcSMatt Macy boolean_t cb_parsable; 1194*eda14cbcSMatt Macy boolean_t cb_dryrun; 1195*eda14cbcSMatt Macy nvlist_t *cb_nvl; 1196*eda14cbcSMatt Macy nvlist_t *cb_batchedsnaps; 1197*eda14cbcSMatt Macy 1198*eda14cbcSMatt Macy /* first snap in contiguous run */ 1199*eda14cbcSMatt Macy char *cb_firstsnap; 1200*eda14cbcSMatt Macy /* previous snap in contiguous run */ 1201*eda14cbcSMatt Macy char *cb_prevsnap; 1202*eda14cbcSMatt Macy int64_t cb_snapused; 1203*eda14cbcSMatt Macy char *cb_snapspec; 1204*eda14cbcSMatt Macy char *cb_bookmark; 1205*eda14cbcSMatt Macy uint64_t cb_snap_count; 1206*eda14cbcSMatt Macy } destroy_cbdata_t; 1207*eda14cbcSMatt Macy 1208*eda14cbcSMatt Macy /* 1209*eda14cbcSMatt Macy * Check for any dependents based on the '-r' or '-R' flags. 1210*eda14cbcSMatt Macy */ 1211*eda14cbcSMatt Macy static int 1212*eda14cbcSMatt Macy destroy_check_dependent(zfs_handle_t *zhp, void *data) 1213*eda14cbcSMatt Macy { 1214*eda14cbcSMatt Macy destroy_cbdata_t *cbp = data; 1215*eda14cbcSMatt Macy const char *tname = zfs_get_name(cbp->cb_target); 1216*eda14cbcSMatt Macy const char *name = zfs_get_name(zhp); 1217*eda14cbcSMatt Macy 1218*eda14cbcSMatt Macy if (strncmp(tname, name, strlen(tname)) == 0 && 1219*eda14cbcSMatt Macy (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { 1220*eda14cbcSMatt Macy /* 1221*eda14cbcSMatt Macy * This is a direct descendant, not a clone somewhere else in 1222*eda14cbcSMatt Macy * the hierarchy. 1223*eda14cbcSMatt Macy */ 1224*eda14cbcSMatt Macy if (cbp->cb_recurse) 1225*eda14cbcSMatt Macy goto out; 1226*eda14cbcSMatt Macy 1227*eda14cbcSMatt Macy if (cbp->cb_first) { 1228*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot destroy '%s': " 1229*eda14cbcSMatt Macy "%s has children\n"), 1230*eda14cbcSMatt Macy zfs_get_name(cbp->cb_target), 1231*eda14cbcSMatt Macy zfs_type_to_name(zfs_get_type(cbp->cb_target))); 1232*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("use '-r' to destroy " 1233*eda14cbcSMatt Macy "the following datasets:\n")); 1234*eda14cbcSMatt Macy cbp->cb_first = B_FALSE; 1235*eda14cbcSMatt Macy cbp->cb_error = B_TRUE; 1236*eda14cbcSMatt Macy } 1237*eda14cbcSMatt Macy 1238*eda14cbcSMatt Macy (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1239*eda14cbcSMatt Macy } else { 1240*eda14cbcSMatt Macy /* 1241*eda14cbcSMatt Macy * This is a clone. We only want to report this if the '-r' 1242*eda14cbcSMatt Macy * wasn't specified, or the target is a snapshot. 1243*eda14cbcSMatt Macy */ 1244*eda14cbcSMatt Macy if (!cbp->cb_recurse && 1245*eda14cbcSMatt Macy zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) 1246*eda14cbcSMatt Macy goto out; 1247*eda14cbcSMatt Macy 1248*eda14cbcSMatt Macy if (cbp->cb_first) { 1249*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot destroy '%s': " 1250*eda14cbcSMatt Macy "%s has dependent clones\n"), 1251*eda14cbcSMatt Macy zfs_get_name(cbp->cb_target), 1252*eda14cbcSMatt Macy zfs_type_to_name(zfs_get_type(cbp->cb_target))); 1253*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("use '-R' to destroy " 1254*eda14cbcSMatt Macy "the following datasets:\n")); 1255*eda14cbcSMatt Macy cbp->cb_first = B_FALSE; 1256*eda14cbcSMatt Macy cbp->cb_error = B_TRUE; 1257*eda14cbcSMatt Macy cbp->cb_dryrun = B_TRUE; 1258*eda14cbcSMatt Macy } 1259*eda14cbcSMatt Macy 1260*eda14cbcSMatt Macy (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1261*eda14cbcSMatt Macy } 1262*eda14cbcSMatt Macy 1263*eda14cbcSMatt Macy out: 1264*eda14cbcSMatt Macy zfs_close(zhp); 1265*eda14cbcSMatt Macy return (0); 1266*eda14cbcSMatt Macy } 1267*eda14cbcSMatt Macy 1268*eda14cbcSMatt Macy static int 1269*eda14cbcSMatt Macy destroy_batched(destroy_cbdata_t *cb) 1270*eda14cbcSMatt Macy { 1271*eda14cbcSMatt Macy int error = zfs_destroy_snaps_nvl(g_zfs, 1272*eda14cbcSMatt Macy cb->cb_batchedsnaps, B_FALSE); 1273*eda14cbcSMatt Macy fnvlist_free(cb->cb_batchedsnaps); 1274*eda14cbcSMatt Macy cb->cb_batchedsnaps = fnvlist_alloc(); 1275*eda14cbcSMatt Macy return (error); 1276*eda14cbcSMatt Macy } 1277*eda14cbcSMatt Macy 1278*eda14cbcSMatt Macy static int 1279*eda14cbcSMatt Macy destroy_callback(zfs_handle_t *zhp, void *data) 1280*eda14cbcSMatt Macy { 1281*eda14cbcSMatt Macy destroy_cbdata_t *cb = data; 1282*eda14cbcSMatt Macy const char *name = zfs_get_name(zhp); 1283*eda14cbcSMatt Macy int error; 1284*eda14cbcSMatt Macy 1285*eda14cbcSMatt Macy if (cb->cb_verbose) { 1286*eda14cbcSMatt Macy if (cb->cb_parsable) { 1287*eda14cbcSMatt Macy (void) printf("destroy\t%s\n", name); 1288*eda14cbcSMatt Macy } else if (cb->cb_dryrun) { 1289*eda14cbcSMatt Macy (void) printf(gettext("would destroy %s\n"), 1290*eda14cbcSMatt Macy name); 1291*eda14cbcSMatt Macy } else { 1292*eda14cbcSMatt Macy (void) printf(gettext("will destroy %s\n"), 1293*eda14cbcSMatt Macy name); 1294*eda14cbcSMatt Macy } 1295*eda14cbcSMatt Macy } 1296*eda14cbcSMatt Macy 1297*eda14cbcSMatt Macy /* 1298*eda14cbcSMatt Macy * Ignore pools (which we've already flagged as an error before getting 1299*eda14cbcSMatt Macy * here). 1300*eda14cbcSMatt Macy */ 1301*eda14cbcSMatt Macy if (strchr(zfs_get_name(zhp), '/') == NULL && 1302*eda14cbcSMatt Macy zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 1303*eda14cbcSMatt Macy zfs_close(zhp); 1304*eda14cbcSMatt Macy return (0); 1305*eda14cbcSMatt Macy } 1306*eda14cbcSMatt Macy if (cb->cb_dryrun) { 1307*eda14cbcSMatt Macy zfs_close(zhp); 1308*eda14cbcSMatt Macy return (0); 1309*eda14cbcSMatt Macy } 1310*eda14cbcSMatt Macy 1311*eda14cbcSMatt Macy /* 1312*eda14cbcSMatt Macy * We batch up all contiguous snapshots (even of different 1313*eda14cbcSMatt Macy * filesystems) and destroy them with one ioctl. We can't 1314*eda14cbcSMatt Macy * simply do all snap deletions and then all fs deletions, 1315*eda14cbcSMatt Macy * because we must delete a clone before its origin. 1316*eda14cbcSMatt Macy */ 1317*eda14cbcSMatt Macy if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { 1318*eda14cbcSMatt Macy cb->cb_snap_count++; 1319*eda14cbcSMatt Macy fnvlist_add_boolean(cb->cb_batchedsnaps, name); 1320*eda14cbcSMatt Macy if (cb->cb_snap_count % 10 == 0 && cb->cb_defer_destroy) 1321*eda14cbcSMatt Macy error = destroy_batched(cb); 1322*eda14cbcSMatt Macy } else { 1323*eda14cbcSMatt Macy error = destroy_batched(cb); 1324*eda14cbcSMatt Macy if (error != 0 || 1325*eda14cbcSMatt Macy zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 || 1326*eda14cbcSMatt Macy zfs_destroy(zhp, cb->cb_defer_destroy) != 0) { 1327*eda14cbcSMatt Macy zfs_close(zhp); 1328*eda14cbcSMatt Macy /* 1329*eda14cbcSMatt Macy * When performing a recursive destroy we ignore errors 1330*eda14cbcSMatt Macy * so that the recursive destroy could continue 1331*eda14cbcSMatt Macy * destroying past problem datasets 1332*eda14cbcSMatt Macy */ 1333*eda14cbcSMatt Macy if (cb->cb_recurse) { 1334*eda14cbcSMatt Macy cb->cb_error = B_TRUE; 1335*eda14cbcSMatt Macy return (0); 1336*eda14cbcSMatt Macy } 1337*eda14cbcSMatt Macy return (-1); 1338*eda14cbcSMatt Macy } 1339*eda14cbcSMatt Macy } 1340*eda14cbcSMatt Macy 1341*eda14cbcSMatt Macy zfs_close(zhp); 1342*eda14cbcSMatt Macy return (0); 1343*eda14cbcSMatt Macy } 1344*eda14cbcSMatt Macy 1345*eda14cbcSMatt Macy static int 1346*eda14cbcSMatt Macy destroy_print_cb(zfs_handle_t *zhp, void *arg) 1347*eda14cbcSMatt Macy { 1348*eda14cbcSMatt Macy destroy_cbdata_t *cb = arg; 1349*eda14cbcSMatt Macy const char *name = zfs_get_name(zhp); 1350*eda14cbcSMatt Macy int err = 0; 1351*eda14cbcSMatt Macy 1352*eda14cbcSMatt Macy if (nvlist_exists(cb->cb_nvl, name)) { 1353*eda14cbcSMatt Macy if (cb->cb_firstsnap == NULL) 1354*eda14cbcSMatt Macy cb->cb_firstsnap = strdup(name); 1355*eda14cbcSMatt Macy if (cb->cb_prevsnap != NULL) 1356*eda14cbcSMatt Macy free(cb->cb_prevsnap); 1357*eda14cbcSMatt Macy /* this snap continues the current range */ 1358*eda14cbcSMatt Macy cb->cb_prevsnap = strdup(name); 1359*eda14cbcSMatt Macy if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL) 1360*eda14cbcSMatt Macy nomem(); 1361*eda14cbcSMatt Macy if (cb->cb_verbose) { 1362*eda14cbcSMatt Macy if (cb->cb_parsable) { 1363*eda14cbcSMatt Macy (void) printf("destroy\t%s\n", name); 1364*eda14cbcSMatt Macy } else if (cb->cb_dryrun) { 1365*eda14cbcSMatt Macy (void) printf(gettext("would destroy %s\n"), 1366*eda14cbcSMatt Macy name); 1367*eda14cbcSMatt Macy } else { 1368*eda14cbcSMatt Macy (void) printf(gettext("will destroy %s\n"), 1369*eda14cbcSMatt Macy name); 1370*eda14cbcSMatt Macy } 1371*eda14cbcSMatt Macy } 1372*eda14cbcSMatt Macy } else if (cb->cb_firstsnap != NULL) { 1373*eda14cbcSMatt Macy /* end of this range */ 1374*eda14cbcSMatt Macy uint64_t used = 0; 1375*eda14cbcSMatt Macy err = lzc_snaprange_space(cb->cb_firstsnap, 1376*eda14cbcSMatt Macy cb->cb_prevsnap, &used); 1377*eda14cbcSMatt Macy cb->cb_snapused += used; 1378*eda14cbcSMatt Macy free(cb->cb_firstsnap); 1379*eda14cbcSMatt Macy cb->cb_firstsnap = NULL; 1380*eda14cbcSMatt Macy free(cb->cb_prevsnap); 1381*eda14cbcSMatt Macy cb->cb_prevsnap = NULL; 1382*eda14cbcSMatt Macy } 1383*eda14cbcSMatt Macy zfs_close(zhp); 1384*eda14cbcSMatt Macy return (err); 1385*eda14cbcSMatt Macy } 1386*eda14cbcSMatt Macy 1387*eda14cbcSMatt Macy static int 1388*eda14cbcSMatt Macy destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb) 1389*eda14cbcSMatt Macy { 1390*eda14cbcSMatt Macy int err; 1391*eda14cbcSMatt Macy assert(cb->cb_firstsnap == NULL); 1392*eda14cbcSMatt Macy assert(cb->cb_prevsnap == NULL); 1393*eda14cbcSMatt Macy err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb, 0, 0); 1394*eda14cbcSMatt Macy if (cb->cb_firstsnap != NULL) { 1395*eda14cbcSMatt Macy uint64_t used = 0; 1396*eda14cbcSMatt Macy if (err == 0) { 1397*eda14cbcSMatt Macy err = lzc_snaprange_space(cb->cb_firstsnap, 1398*eda14cbcSMatt Macy cb->cb_prevsnap, &used); 1399*eda14cbcSMatt Macy } 1400*eda14cbcSMatt Macy cb->cb_snapused += used; 1401*eda14cbcSMatt Macy free(cb->cb_firstsnap); 1402*eda14cbcSMatt Macy cb->cb_firstsnap = NULL; 1403*eda14cbcSMatt Macy free(cb->cb_prevsnap); 1404*eda14cbcSMatt Macy cb->cb_prevsnap = NULL; 1405*eda14cbcSMatt Macy } 1406*eda14cbcSMatt Macy return (err); 1407*eda14cbcSMatt Macy } 1408*eda14cbcSMatt Macy 1409*eda14cbcSMatt Macy static int 1410*eda14cbcSMatt Macy snapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg) 1411*eda14cbcSMatt Macy { 1412*eda14cbcSMatt Macy destroy_cbdata_t *cb = arg; 1413*eda14cbcSMatt Macy int err = 0; 1414*eda14cbcSMatt Macy 1415*eda14cbcSMatt Macy /* Check for clones. */ 1416*eda14cbcSMatt Macy if (!cb->cb_doclones && !cb->cb_defer_destroy) { 1417*eda14cbcSMatt Macy cb->cb_target = zhp; 1418*eda14cbcSMatt Macy cb->cb_first = B_TRUE; 1419*eda14cbcSMatt Macy err = zfs_iter_dependents(zhp, B_TRUE, 1420*eda14cbcSMatt Macy destroy_check_dependent, cb); 1421*eda14cbcSMatt Macy } 1422*eda14cbcSMatt Macy 1423*eda14cbcSMatt Macy if (err == 0) { 1424*eda14cbcSMatt Macy if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp))) 1425*eda14cbcSMatt Macy nomem(); 1426*eda14cbcSMatt Macy } 1427*eda14cbcSMatt Macy zfs_close(zhp); 1428*eda14cbcSMatt Macy return (err); 1429*eda14cbcSMatt Macy } 1430*eda14cbcSMatt Macy 1431*eda14cbcSMatt Macy static int 1432*eda14cbcSMatt Macy gather_snapshots(zfs_handle_t *zhp, void *arg) 1433*eda14cbcSMatt Macy { 1434*eda14cbcSMatt Macy destroy_cbdata_t *cb = arg; 1435*eda14cbcSMatt Macy int err = 0; 1436*eda14cbcSMatt Macy 1437*eda14cbcSMatt Macy err = zfs_iter_snapspec(zhp, cb->cb_snapspec, snapshot_to_nvl_cb, cb); 1438*eda14cbcSMatt Macy if (err == ENOENT) 1439*eda14cbcSMatt Macy err = 0; 1440*eda14cbcSMatt Macy if (err != 0) 1441*eda14cbcSMatt Macy goto out; 1442*eda14cbcSMatt Macy 1443*eda14cbcSMatt Macy if (cb->cb_verbose) { 1444*eda14cbcSMatt Macy err = destroy_print_snapshots(zhp, cb); 1445*eda14cbcSMatt Macy if (err != 0) 1446*eda14cbcSMatt Macy goto out; 1447*eda14cbcSMatt Macy } 1448*eda14cbcSMatt Macy 1449*eda14cbcSMatt Macy if (cb->cb_recurse) 1450*eda14cbcSMatt Macy err = zfs_iter_filesystems(zhp, gather_snapshots, cb); 1451*eda14cbcSMatt Macy 1452*eda14cbcSMatt Macy out: 1453*eda14cbcSMatt Macy zfs_close(zhp); 1454*eda14cbcSMatt Macy return (err); 1455*eda14cbcSMatt Macy } 1456*eda14cbcSMatt Macy 1457*eda14cbcSMatt Macy static int 1458*eda14cbcSMatt Macy destroy_clones(destroy_cbdata_t *cb) 1459*eda14cbcSMatt Macy { 1460*eda14cbcSMatt Macy nvpair_t *pair; 1461*eda14cbcSMatt Macy for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL); 1462*eda14cbcSMatt Macy pair != NULL; 1463*eda14cbcSMatt Macy pair = nvlist_next_nvpair(cb->cb_nvl, pair)) { 1464*eda14cbcSMatt Macy zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair), 1465*eda14cbcSMatt Macy ZFS_TYPE_SNAPSHOT); 1466*eda14cbcSMatt Macy if (zhp != NULL) { 1467*eda14cbcSMatt Macy boolean_t defer = cb->cb_defer_destroy; 1468*eda14cbcSMatt Macy int err; 1469*eda14cbcSMatt Macy 1470*eda14cbcSMatt Macy /* 1471*eda14cbcSMatt Macy * We can't defer destroy non-snapshots, so set it to 1472*eda14cbcSMatt Macy * false while destroying the clones. 1473*eda14cbcSMatt Macy */ 1474*eda14cbcSMatt Macy cb->cb_defer_destroy = B_FALSE; 1475*eda14cbcSMatt Macy err = zfs_iter_dependents(zhp, B_FALSE, 1476*eda14cbcSMatt Macy destroy_callback, cb); 1477*eda14cbcSMatt Macy cb->cb_defer_destroy = defer; 1478*eda14cbcSMatt Macy zfs_close(zhp); 1479*eda14cbcSMatt Macy if (err != 0) 1480*eda14cbcSMatt Macy return (err); 1481*eda14cbcSMatt Macy } 1482*eda14cbcSMatt Macy } 1483*eda14cbcSMatt Macy return (0); 1484*eda14cbcSMatt Macy } 1485*eda14cbcSMatt Macy 1486*eda14cbcSMatt Macy static int 1487*eda14cbcSMatt Macy zfs_do_destroy(int argc, char **argv) 1488*eda14cbcSMatt Macy { 1489*eda14cbcSMatt Macy destroy_cbdata_t cb = { 0 }; 1490*eda14cbcSMatt Macy int rv = 0; 1491*eda14cbcSMatt Macy int err = 0; 1492*eda14cbcSMatt Macy int c; 1493*eda14cbcSMatt Macy zfs_handle_t *zhp = NULL; 1494*eda14cbcSMatt Macy char *at, *pound; 1495*eda14cbcSMatt Macy zfs_type_t type = ZFS_TYPE_DATASET; 1496*eda14cbcSMatt Macy 1497*eda14cbcSMatt Macy /* check options */ 1498*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "vpndfrR")) != -1) { 1499*eda14cbcSMatt Macy switch (c) { 1500*eda14cbcSMatt Macy case 'v': 1501*eda14cbcSMatt Macy cb.cb_verbose = B_TRUE; 1502*eda14cbcSMatt Macy break; 1503*eda14cbcSMatt Macy case 'p': 1504*eda14cbcSMatt Macy cb.cb_verbose = B_TRUE; 1505*eda14cbcSMatt Macy cb.cb_parsable = B_TRUE; 1506*eda14cbcSMatt Macy break; 1507*eda14cbcSMatt Macy case 'n': 1508*eda14cbcSMatt Macy cb.cb_dryrun = B_TRUE; 1509*eda14cbcSMatt Macy break; 1510*eda14cbcSMatt Macy case 'd': 1511*eda14cbcSMatt Macy cb.cb_defer_destroy = B_TRUE; 1512*eda14cbcSMatt Macy type = ZFS_TYPE_SNAPSHOT; 1513*eda14cbcSMatt Macy break; 1514*eda14cbcSMatt Macy case 'f': 1515*eda14cbcSMatt Macy cb.cb_force = B_TRUE; 1516*eda14cbcSMatt Macy break; 1517*eda14cbcSMatt Macy case 'r': 1518*eda14cbcSMatt Macy cb.cb_recurse = B_TRUE; 1519*eda14cbcSMatt Macy break; 1520*eda14cbcSMatt Macy case 'R': 1521*eda14cbcSMatt Macy cb.cb_recurse = B_TRUE; 1522*eda14cbcSMatt Macy cb.cb_doclones = B_TRUE; 1523*eda14cbcSMatt Macy break; 1524*eda14cbcSMatt Macy case '?': 1525*eda14cbcSMatt Macy default: 1526*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1527*eda14cbcSMatt Macy optopt); 1528*eda14cbcSMatt Macy usage(B_FALSE); 1529*eda14cbcSMatt Macy } 1530*eda14cbcSMatt Macy } 1531*eda14cbcSMatt Macy 1532*eda14cbcSMatt Macy argc -= optind; 1533*eda14cbcSMatt Macy argv += optind; 1534*eda14cbcSMatt Macy 1535*eda14cbcSMatt Macy /* check number of arguments */ 1536*eda14cbcSMatt Macy if (argc == 0) { 1537*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing dataset argument\n")); 1538*eda14cbcSMatt Macy usage(B_FALSE); 1539*eda14cbcSMatt Macy } 1540*eda14cbcSMatt Macy if (argc > 1) { 1541*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many arguments\n")); 1542*eda14cbcSMatt Macy usage(B_FALSE); 1543*eda14cbcSMatt Macy } 1544*eda14cbcSMatt Macy 1545*eda14cbcSMatt Macy at = strchr(argv[0], '@'); 1546*eda14cbcSMatt Macy pound = strchr(argv[0], '#'); 1547*eda14cbcSMatt Macy if (at != NULL) { 1548*eda14cbcSMatt Macy 1549*eda14cbcSMatt Macy /* Build the list of snaps to destroy in cb_nvl. */ 1550*eda14cbcSMatt Macy cb.cb_nvl = fnvlist_alloc(); 1551*eda14cbcSMatt Macy 1552*eda14cbcSMatt Macy *at = '\0'; 1553*eda14cbcSMatt Macy zhp = zfs_open(g_zfs, argv[0], 1554*eda14cbcSMatt Macy ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 1555*eda14cbcSMatt Macy if (zhp == NULL) { 1556*eda14cbcSMatt Macy nvlist_free(cb.cb_nvl); 1557*eda14cbcSMatt Macy return (1); 1558*eda14cbcSMatt Macy } 1559*eda14cbcSMatt Macy 1560*eda14cbcSMatt Macy cb.cb_snapspec = at + 1; 1561*eda14cbcSMatt Macy if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 || 1562*eda14cbcSMatt Macy cb.cb_error) { 1563*eda14cbcSMatt Macy rv = 1; 1564*eda14cbcSMatt Macy goto out; 1565*eda14cbcSMatt Macy } 1566*eda14cbcSMatt Macy 1567*eda14cbcSMatt Macy if (nvlist_empty(cb.cb_nvl)) { 1568*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("could not find any " 1569*eda14cbcSMatt Macy "snapshots to destroy; check snapshot names.\n")); 1570*eda14cbcSMatt Macy rv = 1; 1571*eda14cbcSMatt Macy goto out; 1572*eda14cbcSMatt Macy } 1573*eda14cbcSMatt Macy 1574*eda14cbcSMatt Macy if (cb.cb_verbose) { 1575*eda14cbcSMatt Macy char buf[16]; 1576*eda14cbcSMatt Macy zfs_nicebytes(cb.cb_snapused, buf, sizeof (buf)); 1577*eda14cbcSMatt Macy if (cb.cb_parsable) { 1578*eda14cbcSMatt Macy (void) printf("reclaim\t%llu\n", 1579*eda14cbcSMatt Macy (u_longlong_t)cb.cb_snapused); 1580*eda14cbcSMatt Macy } else if (cb.cb_dryrun) { 1581*eda14cbcSMatt Macy (void) printf(gettext("would reclaim %s\n"), 1582*eda14cbcSMatt Macy buf); 1583*eda14cbcSMatt Macy } else { 1584*eda14cbcSMatt Macy (void) printf(gettext("will reclaim %s\n"), 1585*eda14cbcSMatt Macy buf); 1586*eda14cbcSMatt Macy } 1587*eda14cbcSMatt Macy } 1588*eda14cbcSMatt Macy 1589*eda14cbcSMatt Macy if (!cb.cb_dryrun) { 1590*eda14cbcSMatt Macy if (cb.cb_doclones) { 1591*eda14cbcSMatt Macy cb.cb_batchedsnaps = fnvlist_alloc(); 1592*eda14cbcSMatt Macy err = destroy_clones(&cb); 1593*eda14cbcSMatt Macy if (err == 0) { 1594*eda14cbcSMatt Macy err = zfs_destroy_snaps_nvl(g_zfs, 1595*eda14cbcSMatt Macy cb.cb_batchedsnaps, B_FALSE); 1596*eda14cbcSMatt Macy } 1597*eda14cbcSMatt Macy if (err != 0) { 1598*eda14cbcSMatt Macy rv = 1; 1599*eda14cbcSMatt Macy goto out; 1600*eda14cbcSMatt Macy } 1601*eda14cbcSMatt Macy } 1602*eda14cbcSMatt Macy if (err == 0) { 1603*eda14cbcSMatt Macy err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl, 1604*eda14cbcSMatt Macy cb.cb_defer_destroy); 1605*eda14cbcSMatt Macy } 1606*eda14cbcSMatt Macy } 1607*eda14cbcSMatt Macy 1608*eda14cbcSMatt Macy if (err != 0) 1609*eda14cbcSMatt Macy rv = 1; 1610*eda14cbcSMatt Macy } else if (pound != NULL) { 1611*eda14cbcSMatt Macy int err; 1612*eda14cbcSMatt Macy nvlist_t *nvl; 1613*eda14cbcSMatt Macy 1614*eda14cbcSMatt Macy if (cb.cb_dryrun) { 1615*eda14cbcSMatt Macy (void) fprintf(stderr, 1616*eda14cbcSMatt Macy "dryrun is not supported with bookmark\n"); 1617*eda14cbcSMatt Macy return (-1); 1618*eda14cbcSMatt Macy } 1619*eda14cbcSMatt Macy 1620*eda14cbcSMatt Macy if (cb.cb_defer_destroy) { 1621*eda14cbcSMatt Macy (void) fprintf(stderr, 1622*eda14cbcSMatt Macy "defer destroy is not supported with bookmark\n"); 1623*eda14cbcSMatt Macy return (-1); 1624*eda14cbcSMatt Macy } 1625*eda14cbcSMatt Macy 1626*eda14cbcSMatt Macy if (cb.cb_recurse) { 1627*eda14cbcSMatt Macy (void) fprintf(stderr, 1628*eda14cbcSMatt Macy "recursive is not supported with bookmark\n"); 1629*eda14cbcSMatt Macy return (-1); 1630*eda14cbcSMatt Macy } 1631*eda14cbcSMatt Macy 1632*eda14cbcSMatt Macy /* 1633*eda14cbcSMatt Macy * Unfortunately, zfs_bookmark() doesn't honor the 1634*eda14cbcSMatt Macy * casesensitivity setting. However, we can't simply 1635*eda14cbcSMatt Macy * remove this check, because lzc_destroy_bookmarks() 1636*eda14cbcSMatt Macy * ignores non-existent bookmarks, so this is necessary 1637*eda14cbcSMatt Macy * to get a proper error message. 1638*eda14cbcSMatt Macy */ 1639*eda14cbcSMatt Macy if (!zfs_bookmark_exists(argv[0])) { 1640*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("bookmark '%s' " 1641*eda14cbcSMatt Macy "does not exist.\n"), argv[0]); 1642*eda14cbcSMatt Macy return (1); 1643*eda14cbcSMatt Macy } 1644*eda14cbcSMatt Macy 1645*eda14cbcSMatt Macy nvl = fnvlist_alloc(); 1646*eda14cbcSMatt Macy fnvlist_add_boolean(nvl, argv[0]); 1647*eda14cbcSMatt Macy 1648*eda14cbcSMatt Macy err = lzc_destroy_bookmarks(nvl, NULL); 1649*eda14cbcSMatt Macy if (err != 0) { 1650*eda14cbcSMatt Macy (void) zfs_standard_error(g_zfs, err, 1651*eda14cbcSMatt Macy "cannot destroy bookmark"); 1652*eda14cbcSMatt Macy } 1653*eda14cbcSMatt Macy 1654*eda14cbcSMatt Macy nvlist_free(nvl); 1655*eda14cbcSMatt Macy 1656*eda14cbcSMatt Macy return (err); 1657*eda14cbcSMatt Macy } else { 1658*eda14cbcSMatt Macy /* Open the given dataset */ 1659*eda14cbcSMatt Macy if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL) 1660*eda14cbcSMatt Macy return (1); 1661*eda14cbcSMatt Macy 1662*eda14cbcSMatt Macy cb.cb_target = zhp; 1663*eda14cbcSMatt Macy 1664*eda14cbcSMatt Macy /* 1665*eda14cbcSMatt Macy * Perform an explicit check for pools before going any further. 1666*eda14cbcSMatt Macy */ 1667*eda14cbcSMatt Macy if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 1668*eda14cbcSMatt Macy zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 1669*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot destroy '%s': " 1670*eda14cbcSMatt Macy "operation does not apply to pools\n"), 1671*eda14cbcSMatt Macy zfs_get_name(zhp)); 1672*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("use 'zfs destroy -r " 1673*eda14cbcSMatt Macy "%s' to destroy all datasets in the pool\n"), 1674*eda14cbcSMatt Macy zfs_get_name(zhp)); 1675*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 1676*eda14cbcSMatt Macy "to destroy the pool itself\n"), zfs_get_name(zhp)); 1677*eda14cbcSMatt Macy rv = 1; 1678*eda14cbcSMatt Macy goto out; 1679*eda14cbcSMatt Macy } 1680*eda14cbcSMatt Macy 1681*eda14cbcSMatt Macy /* 1682*eda14cbcSMatt Macy * Check for any dependents and/or clones. 1683*eda14cbcSMatt Macy */ 1684*eda14cbcSMatt Macy cb.cb_first = B_TRUE; 1685*eda14cbcSMatt Macy if (!cb.cb_doclones && 1686*eda14cbcSMatt Macy zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent, 1687*eda14cbcSMatt Macy &cb) != 0) { 1688*eda14cbcSMatt Macy rv = 1; 1689*eda14cbcSMatt Macy goto out; 1690*eda14cbcSMatt Macy } 1691*eda14cbcSMatt Macy 1692*eda14cbcSMatt Macy if (cb.cb_error) { 1693*eda14cbcSMatt Macy rv = 1; 1694*eda14cbcSMatt Macy goto out; 1695*eda14cbcSMatt Macy } 1696*eda14cbcSMatt Macy cb.cb_batchedsnaps = fnvlist_alloc(); 1697*eda14cbcSMatt Macy if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback, 1698*eda14cbcSMatt Macy &cb) != 0) { 1699*eda14cbcSMatt Macy rv = 1; 1700*eda14cbcSMatt Macy goto out; 1701*eda14cbcSMatt Macy } 1702*eda14cbcSMatt Macy 1703*eda14cbcSMatt Macy /* 1704*eda14cbcSMatt Macy * Do the real thing. The callback will close the 1705*eda14cbcSMatt Macy * handle regardless of whether it succeeds or not. 1706*eda14cbcSMatt Macy */ 1707*eda14cbcSMatt Macy err = destroy_callback(zhp, &cb); 1708*eda14cbcSMatt Macy zhp = NULL; 1709*eda14cbcSMatt Macy if (err == 0) { 1710*eda14cbcSMatt Macy err = zfs_destroy_snaps_nvl(g_zfs, 1711*eda14cbcSMatt Macy cb.cb_batchedsnaps, cb.cb_defer_destroy); 1712*eda14cbcSMatt Macy } 1713*eda14cbcSMatt Macy if (err != 0 || cb.cb_error == B_TRUE) 1714*eda14cbcSMatt Macy rv = 1; 1715*eda14cbcSMatt Macy } 1716*eda14cbcSMatt Macy 1717*eda14cbcSMatt Macy out: 1718*eda14cbcSMatt Macy fnvlist_free(cb.cb_batchedsnaps); 1719*eda14cbcSMatt Macy fnvlist_free(cb.cb_nvl); 1720*eda14cbcSMatt Macy if (zhp != NULL) 1721*eda14cbcSMatt Macy zfs_close(zhp); 1722*eda14cbcSMatt Macy return (rv); 1723*eda14cbcSMatt Macy } 1724*eda14cbcSMatt Macy 1725*eda14cbcSMatt Macy static boolean_t 1726*eda14cbcSMatt Macy is_recvd_column(zprop_get_cbdata_t *cbp) 1727*eda14cbcSMatt Macy { 1728*eda14cbcSMatt Macy int i; 1729*eda14cbcSMatt Macy zfs_get_column_t col; 1730*eda14cbcSMatt Macy 1731*eda14cbcSMatt Macy for (i = 0; i < ZFS_GET_NCOLS && 1732*eda14cbcSMatt Macy (col = cbp->cb_columns[i]) != GET_COL_NONE; i++) 1733*eda14cbcSMatt Macy if (col == GET_COL_RECVD) 1734*eda14cbcSMatt Macy return (B_TRUE); 1735*eda14cbcSMatt Macy return (B_FALSE); 1736*eda14cbcSMatt Macy } 1737*eda14cbcSMatt Macy 1738*eda14cbcSMatt Macy /* 1739*eda14cbcSMatt Macy * zfs get [-rHp] [-o all | field[,field]...] [-s source[,source]...] 1740*eda14cbcSMatt Macy * < all | property[,property]... > < fs | snap | vol > ... 1741*eda14cbcSMatt Macy * 1742*eda14cbcSMatt Macy * -r recurse over any child datasets 1743*eda14cbcSMatt Macy * -H scripted mode. Headers are stripped, and fields are separated 1744*eda14cbcSMatt Macy * by tabs instead of spaces. 1745*eda14cbcSMatt Macy * -o Set of fields to display. One of "name,property,value, 1746*eda14cbcSMatt Macy * received,source". Default is "name,property,value,source". 1747*eda14cbcSMatt Macy * "all" is an alias for all five. 1748*eda14cbcSMatt Macy * -s Set of sources to allow. One of 1749*eda14cbcSMatt Macy * "local,default,inherited,received,temporary,none". Default is 1750*eda14cbcSMatt Macy * all six. 1751*eda14cbcSMatt Macy * -p Display values in parsable (literal) format. 1752*eda14cbcSMatt Macy * 1753*eda14cbcSMatt Macy * Prints properties for the given datasets. The user can control which 1754*eda14cbcSMatt Macy * columns to display as well as which property types to allow. 1755*eda14cbcSMatt Macy */ 1756*eda14cbcSMatt Macy 1757*eda14cbcSMatt Macy /* 1758*eda14cbcSMatt Macy * Invoked to display the properties for a single dataset. 1759*eda14cbcSMatt Macy */ 1760*eda14cbcSMatt Macy static int 1761*eda14cbcSMatt Macy get_callback(zfs_handle_t *zhp, void *data) 1762*eda14cbcSMatt Macy { 1763*eda14cbcSMatt Macy char buf[ZFS_MAXPROPLEN]; 1764*eda14cbcSMatt Macy char rbuf[ZFS_MAXPROPLEN]; 1765*eda14cbcSMatt Macy zprop_source_t sourcetype; 1766*eda14cbcSMatt Macy char source[ZFS_MAX_DATASET_NAME_LEN]; 1767*eda14cbcSMatt Macy zprop_get_cbdata_t *cbp = data; 1768*eda14cbcSMatt Macy nvlist_t *user_props = zfs_get_user_props(zhp); 1769*eda14cbcSMatt Macy zprop_list_t *pl = cbp->cb_proplist; 1770*eda14cbcSMatt Macy nvlist_t *propval; 1771*eda14cbcSMatt Macy char *strval; 1772*eda14cbcSMatt Macy char *sourceval; 1773*eda14cbcSMatt Macy boolean_t received = is_recvd_column(cbp); 1774*eda14cbcSMatt Macy 1775*eda14cbcSMatt Macy for (; pl != NULL; pl = pl->pl_next) { 1776*eda14cbcSMatt Macy char *recvdval = NULL; 1777*eda14cbcSMatt Macy /* 1778*eda14cbcSMatt Macy * Skip the special fake placeholder. This will also skip over 1779*eda14cbcSMatt Macy * the name property when 'all' is specified. 1780*eda14cbcSMatt Macy */ 1781*eda14cbcSMatt Macy if (pl->pl_prop == ZFS_PROP_NAME && 1782*eda14cbcSMatt Macy pl == cbp->cb_proplist) 1783*eda14cbcSMatt Macy continue; 1784*eda14cbcSMatt Macy 1785*eda14cbcSMatt Macy if (pl->pl_prop != ZPROP_INVAL) { 1786*eda14cbcSMatt Macy if (zfs_prop_get(zhp, pl->pl_prop, buf, 1787*eda14cbcSMatt Macy sizeof (buf), &sourcetype, source, 1788*eda14cbcSMatt Macy sizeof (source), 1789*eda14cbcSMatt Macy cbp->cb_literal) != 0) { 1790*eda14cbcSMatt Macy if (pl->pl_all) 1791*eda14cbcSMatt Macy continue; 1792*eda14cbcSMatt Macy if (!zfs_prop_valid_for_type(pl->pl_prop, 1793*eda14cbcSMatt Macy ZFS_TYPE_DATASET, B_FALSE)) { 1794*eda14cbcSMatt Macy (void) fprintf(stderr, 1795*eda14cbcSMatt Macy gettext("No such property '%s'\n"), 1796*eda14cbcSMatt Macy zfs_prop_to_name(pl->pl_prop)); 1797*eda14cbcSMatt Macy continue; 1798*eda14cbcSMatt Macy } 1799*eda14cbcSMatt Macy sourcetype = ZPROP_SRC_NONE; 1800*eda14cbcSMatt Macy (void) strlcpy(buf, "-", sizeof (buf)); 1801*eda14cbcSMatt Macy } 1802*eda14cbcSMatt Macy 1803*eda14cbcSMatt Macy if (received && (zfs_prop_get_recvd(zhp, 1804*eda14cbcSMatt Macy zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf), 1805*eda14cbcSMatt Macy cbp->cb_literal) == 0)) 1806*eda14cbcSMatt Macy recvdval = rbuf; 1807*eda14cbcSMatt Macy 1808*eda14cbcSMatt Macy zprop_print_one_property(zfs_get_name(zhp), cbp, 1809*eda14cbcSMatt Macy zfs_prop_to_name(pl->pl_prop), 1810*eda14cbcSMatt Macy buf, sourcetype, source, recvdval); 1811*eda14cbcSMatt Macy } else if (zfs_prop_userquota(pl->pl_user_prop)) { 1812*eda14cbcSMatt Macy sourcetype = ZPROP_SRC_LOCAL; 1813*eda14cbcSMatt Macy 1814*eda14cbcSMatt Macy if (zfs_prop_get_userquota(zhp, pl->pl_user_prop, 1815*eda14cbcSMatt Macy buf, sizeof (buf), cbp->cb_literal) != 0) { 1816*eda14cbcSMatt Macy sourcetype = ZPROP_SRC_NONE; 1817*eda14cbcSMatt Macy (void) strlcpy(buf, "-", sizeof (buf)); 1818*eda14cbcSMatt Macy } 1819*eda14cbcSMatt Macy 1820*eda14cbcSMatt Macy zprop_print_one_property(zfs_get_name(zhp), cbp, 1821*eda14cbcSMatt Macy pl->pl_user_prop, buf, sourcetype, source, NULL); 1822*eda14cbcSMatt Macy } else if (zfs_prop_written(pl->pl_user_prop)) { 1823*eda14cbcSMatt Macy sourcetype = ZPROP_SRC_LOCAL; 1824*eda14cbcSMatt Macy 1825*eda14cbcSMatt Macy if (zfs_prop_get_written(zhp, pl->pl_user_prop, 1826*eda14cbcSMatt Macy buf, sizeof (buf), cbp->cb_literal) != 0) { 1827*eda14cbcSMatt Macy sourcetype = ZPROP_SRC_NONE; 1828*eda14cbcSMatt Macy (void) strlcpy(buf, "-", sizeof (buf)); 1829*eda14cbcSMatt Macy } 1830*eda14cbcSMatt Macy 1831*eda14cbcSMatt Macy zprop_print_one_property(zfs_get_name(zhp), cbp, 1832*eda14cbcSMatt Macy pl->pl_user_prop, buf, sourcetype, source, NULL); 1833*eda14cbcSMatt Macy } else { 1834*eda14cbcSMatt Macy if (nvlist_lookup_nvlist(user_props, 1835*eda14cbcSMatt Macy pl->pl_user_prop, &propval) != 0) { 1836*eda14cbcSMatt Macy if (pl->pl_all) 1837*eda14cbcSMatt Macy continue; 1838*eda14cbcSMatt Macy sourcetype = ZPROP_SRC_NONE; 1839*eda14cbcSMatt Macy strval = "-"; 1840*eda14cbcSMatt Macy } else { 1841*eda14cbcSMatt Macy verify(nvlist_lookup_string(propval, 1842*eda14cbcSMatt Macy ZPROP_VALUE, &strval) == 0); 1843*eda14cbcSMatt Macy verify(nvlist_lookup_string(propval, 1844*eda14cbcSMatt Macy ZPROP_SOURCE, &sourceval) == 0); 1845*eda14cbcSMatt Macy 1846*eda14cbcSMatt Macy if (strcmp(sourceval, 1847*eda14cbcSMatt Macy zfs_get_name(zhp)) == 0) { 1848*eda14cbcSMatt Macy sourcetype = ZPROP_SRC_LOCAL; 1849*eda14cbcSMatt Macy } else if (strcmp(sourceval, 1850*eda14cbcSMatt Macy ZPROP_SOURCE_VAL_RECVD) == 0) { 1851*eda14cbcSMatt Macy sourcetype = ZPROP_SRC_RECEIVED; 1852*eda14cbcSMatt Macy } else { 1853*eda14cbcSMatt Macy sourcetype = ZPROP_SRC_INHERITED; 1854*eda14cbcSMatt Macy (void) strlcpy(source, 1855*eda14cbcSMatt Macy sourceval, sizeof (source)); 1856*eda14cbcSMatt Macy } 1857*eda14cbcSMatt Macy } 1858*eda14cbcSMatt Macy 1859*eda14cbcSMatt Macy if (received && (zfs_prop_get_recvd(zhp, 1860*eda14cbcSMatt Macy pl->pl_user_prop, rbuf, sizeof (rbuf), 1861*eda14cbcSMatt Macy cbp->cb_literal) == 0)) 1862*eda14cbcSMatt Macy recvdval = rbuf; 1863*eda14cbcSMatt Macy 1864*eda14cbcSMatt Macy zprop_print_one_property(zfs_get_name(zhp), cbp, 1865*eda14cbcSMatt Macy pl->pl_user_prop, strval, sourcetype, 1866*eda14cbcSMatt Macy source, recvdval); 1867*eda14cbcSMatt Macy } 1868*eda14cbcSMatt Macy } 1869*eda14cbcSMatt Macy 1870*eda14cbcSMatt Macy return (0); 1871*eda14cbcSMatt Macy } 1872*eda14cbcSMatt Macy 1873*eda14cbcSMatt Macy static int 1874*eda14cbcSMatt Macy zfs_do_get(int argc, char **argv) 1875*eda14cbcSMatt Macy { 1876*eda14cbcSMatt Macy zprop_get_cbdata_t cb = { 0 }; 1877*eda14cbcSMatt Macy int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS; 1878*eda14cbcSMatt Macy int types = ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK; 1879*eda14cbcSMatt Macy char *value, *fields; 1880*eda14cbcSMatt Macy int ret = 0; 1881*eda14cbcSMatt Macy int limit = 0; 1882*eda14cbcSMatt Macy zprop_list_t fake_name = { 0 }; 1883*eda14cbcSMatt Macy 1884*eda14cbcSMatt Macy /* 1885*eda14cbcSMatt Macy * Set up default columns and sources. 1886*eda14cbcSMatt Macy */ 1887*eda14cbcSMatt Macy cb.cb_sources = ZPROP_SRC_ALL; 1888*eda14cbcSMatt Macy cb.cb_columns[0] = GET_COL_NAME; 1889*eda14cbcSMatt Macy cb.cb_columns[1] = GET_COL_PROPERTY; 1890*eda14cbcSMatt Macy cb.cb_columns[2] = GET_COL_VALUE; 1891*eda14cbcSMatt Macy cb.cb_columns[3] = GET_COL_SOURCE; 1892*eda14cbcSMatt Macy cb.cb_type = ZFS_TYPE_DATASET; 1893*eda14cbcSMatt Macy 1894*eda14cbcSMatt Macy /* check options */ 1895*eda14cbcSMatt Macy while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) { 1896*eda14cbcSMatt Macy switch (c) { 1897*eda14cbcSMatt Macy case 'p': 1898*eda14cbcSMatt Macy cb.cb_literal = B_TRUE; 1899*eda14cbcSMatt Macy break; 1900*eda14cbcSMatt Macy case 'd': 1901*eda14cbcSMatt Macy limit = parse_depth(optarg, &flags); 1902*eda14cbcSMatt Macy break; 1903*eda14cbcSMatt Macy case 'r': 1904*eda14cbcSMatt Macy flags |= ZFS_ITER_RECURSE; 1905*eda14cbcSMatt Macy break; 1906*eda14cbcSMatt Macy case 'H': 1907*eda14cbcSMatt Macy cb.cb_scripted = B_TRUE; 1908*eda14cbcSMatt Macy break; 1909*eda14cbcSMatt Macy case ':': 1910*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing argument for " 1911*eda14cbcSMatt Macy "'%c' option\n"), optopt); 1912*eda14cbcSMatt Macy usage(B_FALSE); 1913*eda14cbcSMatt Macy break; 1914*eda14cbcSMatt Macy case 'o': 1915*eda14cbcSMatt Macy /* 1916*eda14cbcSMatt Macy * Process the set of columns to display. We zero out 1917*eda14cbcSMatt Macy * the structure to give us a blank slate. 1918*eda14cbcSMatt Macy */ 1919*eda14cbcSMatt Macy bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 1920*eda14cbcSMatt Macy i = 0; 1921*eda14cbcSMatt Macy while (*optarg != '\0') { 1922*eda14cbcSMatt Macy static char *col_subopts[] = 1923*eda14cbcSMatt Macy { "name", "property", "value", "received", 1924*eda14cbcSMatt Macy "source", "all", NULL }; 1925*eda14cbcSMatt Macy 1926*eda14cbcSMatt Macy if (i == ZFS_GET_NCOLS) { 1927*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too " 1928*eda14cbcSMatt Macy "many fields given to -o " 1929*eda14cbcSMatt Macy "option\n")); 1930*eda14cbcSMatt Macy usage(B_FALSE); 1931*eda14cbcSMatt Macy } 1932*eda14cbcSMatt Macy 1933*eda14cbcSMatt Macy switch (getsubopt(&optarg, col_subopts, 1934*eda14cbcSMatt Macy &value)) { 1935*eda14cbcSMatt Macy case 0: 1936*eda14cbcSMatt Macy cb.cb_columns[i++] = GET_COL_NAME; 1937*eda14cbcSMatt Macy break; 1938*eda14cbcSMatt Macy case 1: 1939*eda14cbcSMatt Macy cb.cb_columns[i++] = GET_COL_PROPERTY; 1940*eda14cbcSMatt Macy break; 1941*eda14cbcSMatt Macy case 2: 1942*eda14cbcSMatt Macy cb.cb_columns[i++] = GET_COL_VALUE; 1943*eda14cbcSMatt Macy break; 1944*eda14cbcSMatt Macy case 3: 1945*eda14cbcSMatt Macy cb.cb_columns[i++] = GET_COL_RECVD; 1946*eda14cbcSMatt Macy flags |= ZFS_ITER_RECVD_PROPS; 1947*eda14cbcSMatt Macy break; 1948*eda14cbcSMatt Macy case 4: 1949*eda14cbcSMatt Macy cb.cb_columns[i++] = GET_COL_SOURCE; 1950*eda14cbcSMatt Macy break; 1951*eda14cbcSMatt Macy case 5: 1952*eda14cbcSMatt Macy if (i > 0) { 1953*eda14cbcSMatt Macy (void) fprintf(stderr, 1954*eda14cbcSMatt Macy gettext("\"all\" conflicts " 1955*eda14cbcSMatt Macy "with specific fields " 1956*eda14cbcSMatt Macy "given to -o option\n")); 1957*eda14cbcSMatt Macy usage(B_FALSE); 1958*eda14cbcSMatt Macy } 1959*eda14cbcSMatt Macy cb.cb_columns[0] = GET_COL_NAME; 1960*eda14cbcSMatt Macy cb.cb_columns[1] = GET_COL_PROPERTY; 1961*eda14cbcSMatt Macy cb.cb_columns[2] = GET_COL_VALUE; 1962*eda14cbcSMatt Macy cb.cb_columns[3] = GET_COL_RECVD; 1963*eda14cbcSMatt Macy cb.cb_columns[4] = GET_COL_SOURCE; 1964*eda14cbcSMatt Macy flags |= ZFS_ITER_RECVD_PROPS; 1965*eda14cbcSMatt Macy i = ZFS_GET_NCOLS; 1966*eda14cbcSMatt Macy break; 1967*eda14cbcSMatt Macy default: 1968*eda14cbcSMatt Macy (void) fprintf(stderr, 1969*eda14cbcSMatt Macy gettext("invalid column name " 1970*eda14cbcSMatt Macy "'%s'\n"), value); 1971*eda14cbcSMatt Macy usage(B_FALSE); 1972*eda14cbcSMatt Macy } 1973*eda14cbcSMatt Macy } 1974*eda14cbcSMatt Macy break; 1975*eda14cbcSMatt Macy 1976*eda14cbcSMatt Macy case 's': 1977*eda14cbcSMatt Macy cb.cb_sources = 0; 1978*eda14cbcSMatt Macy while (*optarg != '\0') { 1979*eda14cbcSMatt Macy static char *source_subopts[] = { 1980*eda14cbcSMatt Macy "local", "default", "inherited", 1981*eda14cbcSMatt Macy "received", "temporary", "none", 1982*eda14cbcSMatt Macy NULL }; 1983*eda14cbcSMatt Macy 1984*eda14cbcSMatt Macy switch (getsubopt(&optarg, source_subopts, 1985*eda14cbcSMatt Macy &value)) { 1986*eda14cbcSMatt Macy case 0: 1987*eda14cbcSMatt Macy cb.cb_sources |= ZPROP_SRC_LOCAL; 1988*eda14cbcSMatt Macy break; 1989*eda14cbcSMatt Macy case 1: 1990*eda14cbcSMatt Macy cb.cb_sources |= ZPROP_SRC_DEFAULT; 1991*eda14cbcSMatt Macy break; 1992*eda14cbcSMatt Macy case 2: 1993*eda14cbcSMatt Macy cb.cb_sources |= ZPROP_SRC_INHERITED; 1994*eda14cbcSMatt Macy break; 1995*eda14cbcSMatt Macy case 3: 1996*eda14cbcSMatt Macy cb.cb_sources |= ZPROP_SRC_RECEIVED; 1997*eda14cbcSMatt Macy break; 1998*eda14cbcSMatt Macy case 4: 1999*eda14cbcSMatt Macy cb.cb_sources |= ZPROP_SRC_TEMPORARY; 2000*eda14cbcSMatt Macy break; 2001*eda14cbcSMatt Macy case 5: 2002*eda14cbcSMatt Macy cb.cb_sources |= ZPROP_SRC_NONE; 2003*eda14cbcSMatt Macy break; 2004*eda14cbcSMatt Macy default: 2005*eda14cbcSMatt Macy (void) fprintf(stderr, 2006*eda14cbcSMatt Macy gettext("invalid source " 2007*eda14cbcSMatt Macy "'%s'\n"), value); 2008*eda14cbcSMatt Macy usage(B_FALSE); 2009*eda14cbcSMatt Macy } 2010*eda14cbcSMatt Macy } 2011*eda14cbcSMatt Macy break; 2012*eda14cbcSMatt Macy 2013*eda14cbcSMatt Macy case 't': 2014*eda14cbcSMatt Macy types = 0; 2015*eda14cbcSMatt Macy flags &= ~ZFS_ITER_PROP_LISTSNAPS; 2016*eda14cbcSMatt Macy while (*optarg != '\0') { 2017*eda14cbcSMatt Macy static char *type_subopts[] = { "filesystem", 2018*eda14cbcSMatt Macy "volume", "snapshot", "snap", "bookmark", 2019*eda14cbcSMatt Macy "all", NULL }; 2020*eda14cbcSMatt Macy 2021*eda14cbcSMatt Macy switch (getsubopt(&optarg, type_subopts, 2022*eda14cbcSMatt Macy &value)) { 2023*eda14cbcSMatt Macy case 0: 2024*eda14cbcSMatt Macy types |= ZFS_TYPE_FILESYSTEM; 2025*eda14cbcSMatt Macy break; 2026*eda14cbcSMatt Macy case 1: 2027*eda14cbcSMatt Macy types |= ZFS_TYPE_VOLUME; 2028*eda14cbcSMatt Macy break; 2029*eda14cbcSMatt Macy case 2: 2030*eda14cbcSMatt Macy case 3: 2031*eda14cbcSMatt Macy types |= ZFS_TYPE_SNAPSHOT; 2032*eda14cbcSMatt Macy break; 2033*eda14cbcSMatt Macy case 4: 2034*eda14cbcSMatt Macy types |= ZFS_TYPE_BOOKMARK; 2035*eda14cbcSMatt Macy break; 2036*eda14cbcSMatt Macy case 5: 2037*eda14cbcSMatt Macy types = ZFS_TYPE_DATASET | 2038*eda14cbcSMatt Macy ZFS_TYPE_BOOKMARK; 2039*eda14cbcSMatt Macy break; 2040*eda14cbcSMatt Macy 2041*eda14cbcSMatt Macy default: 2042*eda14cbcSMatt Macy (void) fprintf(stderr, 2043*eda14cbcSMatt Macy gettext("invalid type '%s'\n"), 2044*eda14cbcSMatt Macy value); 2045*eda14cbcSMatt Macy usage(B_FALSE); 2046*eda14cbcSMatt Macy } 2047*eda14cbcSMatt Macy } 2048*eda14cbcSMatt Macy break; 2049*eda14cbcSMatt Macy 2050*eda14cbcSMatt Macy case '?': 2051*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2052*eda14cbcSMatt Macy optopt); 2053*eda14cbcSMatt Macy usage(B_FALSE); 2054*eda14cbcSMatt Macy } 2055*eda14cbcSMatt Macy } 2056*eda14cbcSMatt Macy 2057*eda14cbcSMatt Macy argc -= optind; 2058*eda14cbcSMatt Macy argv += optind; 2059*eda14cbcSMatt Macy 2060*eda14cbcSMatt Macy if (argc < 1) { 2061*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing property " 2062*eda14cbcSMatt Macy "argument\n")); 2063*eda14cbcSMatt Macy usage(B_FALSE); 2064*eda14cbcSMatt Macy } 2065*eda14cbcSMatt Macy 2066*eda14cbcSMatt Macy fields = argv[0]; 2067*eda14cbcSMatt Macy 2068*eda14cbcSMatt Macy /* 2069*eda14cbcSMatt Macy * Handle users who want to get all snapshots or bookmarks 2070*eda14cbcSMatt Macy * of a dataset (ex. 'zfs get -t snapshot refer <dataset>'). 2071*eda14cbcSMatt Macy */ 2072*eda14cbcSMatt Macy if ((types == ZFS_TYPE_SNAPSHOT || types == ZFS_TYPE_BOOKMARK) && 2073*eda14cbcSMatt Macy argc > 1 && (flags & ZFS_ITER_RECURSE) == 0 && limit == 0) { 2074*eda14cbcSMatt Macy flags |= (ZFS_ITER_DEPTH_LIMIT | ZFS_ITER_RECURSE); 2075*eda14cbcSMatt Macy limit = 1; 2076*eda14cbcSMatt Macy } 2077*eda14cbcSMatt Macy 2078*eda14cbcSMatt Macy if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET) 2079*eda14cbcSMatt Macy != 0) 2080*eda14cbcSMatt Macy usage(B_FALSE); 2081*eda14cbcSMatt Macy 2082*eda14cbcSMatt Macy argc--; 2083*eda14cbcSMatt Macy argv++; 2084*eda14cbcSMatt Macy 2085*eda14cbcSMatt Macy /* 2086*eda14cbcSMatt Macy * As part of zfs_expand_proplist(), we keep track of the maximum column 2087*eda14cbcSMatt Macy * width for each property. For the 'NAME' (and 'SOURCE') columns, we 2088*eda14cbcSMatt Macy * need to know the maximum name length. However, the user likely did 2089*eda14cbcSMatt Macy * not specify 'name' as one of the properties to fetch, so we need to 2090*eda14cbcSMatt Macy * make sure we always include at least this property for 2091*eda14cbcSMatt Macy * print_get_headers() to work properly. 2092*eda14cbcSMatt Macy */ 2093*eda14cbcSMatt Macy if (cb.cb_proplist != NULL) { 2094*eda14cbcSMatt Macy fake_name.pl_prop = ZFS_PROP_NAME; 2095*eda14cbcSMatt Macy fake_name.pl_width = strlen(gettext("NAME")); 2096*eda14cbcSMatt Macy fake_name.pl_next = cb.cb_proplist; 2097*eda14cbcSMatt Macy cb.cb_proplist = &fake_name; 2098*eda14cbcSMatt Macy } 2099*eda14cbcSMatt Macy 2100*eda14cbcSMatt Macy cb.cb_first = B_TRUE; 2101*eda14cbcSMatt Macy 2102*eda14cbcSMatt Macy /* run for each object */ 2103*eda14cbcSMatt Macy ret = zfs_for_each(argc, argv, flags, types, NULL, 2104*eda14cbcSMatt Macy &cb.cb_proplist, limit, get_callback, &cb); 2105*eda14cbcSMatt Macy 2106*eda14cbcSMatt Macy if (cb.cb_proplist == &fake_name) 2107*eda14cbcSMatt Macy zprop_free_list(fake_name.pl_next); 2108*eda14cbcSMatt Macy else 2109*eda14cbcSMatt Macy zprop_free_list(cb.cb_proplist); 2110*eda14cbcSMatt Macy 2111*eda14cbcSMatt Macy return (ret); 2112*eda14cbcSMatt Macy } 2113*eda14cbcSMatt Macy 2114*eda14cbcSMatt Macy /* 2115*eda14cbcSMatt Macy * inherit [-rS] <property> <fs|vol> ... 2116*eda14cbcSMatt Macy * 2117*eda14cbcSMatt Macy * -r Recurse over all children 2118*eda14cbcSMatt Macy * -S Revert to received value, if any 2119*eda14cbcSMatt Macy * 2120*eda14cbcSMatt Macy * For each dataset specified on the command line, inherit the given property 2121*eda14cbcSMatt Macy * from its parent. Inheriting a property at the pool level will cause it to 2122*eda14cbcSMatt Macy * use the default value. The '-r' flag will recurse over all children, and is 2123*eda14cbcSMatt Macy * useful for setting a property on a hierarchy-wide basis, regardless of any 2124*eda14cbcSMatt Macy * local modifications for each dataset. 2125*eda14cbcSMatt Macy */ 2126*eda14cbcSMatt Macy 2127*eda14cbcSMatt Macy typedef struct inherit_cbdata { 2128*eda14cbcSMatt Macy const char *cb_propname; 2129*eda14cbcSMatt Macy boolean_t cb_received; 2130*eda14cbcSMatt Macy } inherit_cbdata_t; 2131*eda14cbcSMatt Macy 2132*eda14cbcSMatt Macy static int 2133*eda14cbcSMatt Macy inherit_recurse_cb(zfs_handle_t *zhp, void *data) 2134*eda14cbcSMatt Macy { 2135*eda14cbcSMatt Macy inherit_cbdata_t *cb = data; 2136*eda14cbcSMatt Macy zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname); 2137*eda14cbcSMatt Macy 2138*eda14cbcSMatt Macy /* 2139*eda14cbcSMatt Macy * If we're doing it recursively, then ignore properties that 2140*eda14cbcSMatt Macy * are not valid for this type of dataset. 2141*eda14cbcSMatt Macy */ 2142*eda14cbcSMatt Macy if (prop != ZPROP_INVAL && 2143*eda14cbcSMatt Macy !zfs_prop_valid_for_type(prop, zfs_get_type(zhp), B_FALSE)) 2144*eda14cbcSMatt Macy return (0); 2145*eda14cbcSMatt Macy 2146*eda14cbcSMatt Macy return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0); 2147*eda14cbcSMatt Macy } 2148*eda14cbcSMatt Macy 2149*eda14cbcSMatt Macy static int 2150*eda14cbcSMatt Macy inherit_cb(zfs_handle_t *zhp, void *data) 2151*eda14cbcSMatt Macy { 2152*eda14cbcSMatt Macy inherit_cbdata_t *cb = data; 2153*eda14cbcSMatt Macy 2154*eda14cbcSMatt Macy return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0); 2155*eda14cbcSMatt Macy } 2156*eda14cbcSMatt Macy 2157*eda14cbcSMatt Macy static int 2158*eda14cbcSMatt Macy zfs_do_inherit(int argc, char **argv) 2159*eda14cbcSMatt Macy { 2160*eda14cbcSMatt Macy int c; 2161*eda14cbcSMatt Macy zfs_prop_t prop; 2162*eda14cbcSMatt Macy inherit_cbdata_t cb = { 0 }; 2163*eda14cbcSMatt Macy char *propname; 2164*eda14cbcSMatt Macy int ret = 0; 2165*eda14cbcSMatt Macy int flags = 0; 2166*eda14cbcSMatt Macy boolean_t received = B_FALSE; 2167*eda14cbcSMatt Macy 2168*eda14cbcSMatt Macy /* check options */ 2169*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "rS")) != -1) { 2170*eda14cbcSMatt Macy switch (c) { 2171*eda14cbcSMatt Macy case 'r': 2172*eda14cbcSMatt Macy flags |= ZFS_ITER_RECURSE; 2173*eda14cbcSMatt Macy break; 2174*eda14cbcSMatt Macy case 'S': 2175*eda14cbcSMatt Macy received = B_TRUE; 2176*eda14cbcSMatt Macy break; 2177*eda14cbcSMatt Macy case '?': 2178*eda14cbcSMatt Macy default: 2179*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2180*eda14cbcSMatt Macy optopt); 2181*eda14cbcSMatt Macy usage(B_FALSE); 2182*eda14cbcSMatt Macy } 2183*eda14cbcSMatt Macy } 2184*eda14cbcSMatt Macy 2185*eda14cbcSMatt Macy argc -= optind; 2186*eda14cbcSMatt Macy argv += optind; 2187*eda14cbcSMatt Macy 2188*eda14cbcSMatt Macy /* check number of arguments */ 2189*eda14cbcSMatt Macy if (argc < 1) { 2190*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing property argument\n")); 2191*eda14cbcSMatt Macy usage(B_FALSE); 2192*eda14cbcSMatt Macy } 2193*eda14cbcSMatt Macy if (argc < 2) { 2194*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing dataset argument\n")); 2195*eda14cbcSMatt Macy usage(B_FALSE); 2196*eda14cbcSMatt Macy } 2197*eda14cbcSMatt Macy 2198*eda14cbcSMatt Macy propname = argv[0]; 2199*eda14cbcSMatt Macy argc--; 2200*eda14cbcSMatt Macy argv++; 2201*eda14cbcSMatt Macy 2202*eda14cbcSMatt Macy if ((prop = zfs_name_to_prop(propname)) != ZPROP_INVAL) { 2203*eda14cbcSMatt Macy if (zfs_prop_readonly(prop)) { 2204*eda14cbcSMatt Macy (void) fprintf(stderr, gettext( 2205*eda14cbcSMatt Macy "%s property is read-only\n"), 2206*eda14cbcSMatt Macy propname); 2207*eda14cbcSMatt Macy return (1); 2208*eda14cbcSMatt Macy } 2209*eda14cbcSMatt Macy if (!zfs_prop_inheritable(prop) && !received) { 2210*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("'%s' property cannot " 2211*eda14cbcSMatt Macy "be inherited\n"), propname); 2212*eda14cbcSMatt Macy if (prop == ZFS_PROP_QUOTA || 2213*eda14cbcSMatt Macy prop == ZFS_PROP_RESERVATION || 2214*eda14cbcSMatt Macy prop == ZFS_PROP_REFQUOTA || 2215*eda14cbcSMatt Macy prop == ZFS_PROP_REFRESERVATION) { 2216*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("use 'zfs set " 2217*eda14cbcSMatt Macy "%s=none' to clear\n"), propname); 2218*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("use 'zfs " 2219*eda14cbcSMatt Macy "inherit -S %s' to revert to received " 2220*eda14cbcSMatt Macy "value\n"), propname); 2221*eda14cbcSMatt Macy } 2222*eda14cbcSMatt Macy return (1); 2223*eda14cbcSMatt Macy } 2224*eda14cbcSMatt Macy if (received && (prop == ZFS_PROP_VOLSIZE || 2225*eda14cbcSMatt Macy prop == ZFS_PROP_VERSION)) { 2226*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("'%s' property cannot " 2227*eda14cbcSMatt Macy "be reverted to a received value\n"), propname); 2228*eda14cbcSMatt Macy return (1); 2229*eda14cbcSMatt Macy } 2230*eda14cbcSMatt Macy } else if (!zfs_prop_user(propname)) { 2231*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid property '%s'\n"), 2232*eda14cbcSMatt Macy propname); 2233*eda14cbcSMatt Macy usage(B_FALSE); 2234*eda14cbcSMatt Macy } 2235*eda14cbcSMatt Macy 2236*eda14cbcSMatt Macy cb.cb_propname = propname; 2237*eda14cbcSMatt Macy cb.cb_received = received; 2238*eda14cbcSMatt Macy 2239*eda14cbcSMatt Macy if (flags & ZFS_ITER_RECURSE) { 2240*eda14cbcSMatt Macy ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET, 2241*eda14cbcSMatt Macy NULL, NULL, 0, inherit_recurse_cb, &cb); 2242*eda14cbcSMatt Macy } else { 2243*eda14cbcSMatt Macy ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET, 2244*eda14cbcSMatt Macy NULL, NULL, 0, inherit_cb, &cb); 2245*eda14cbcSMatt Macy } 2246*eda14cbcSMatt Macy 2247*eda14cbcSMatt Macy return (ret); 2248*eda14cbcSMatt Macy } 2249*eda14cbcSMatt Macy 2250*eda14cbcSMatt Macy typedef struct upgrade_cbdata { 2251*eda14cbcSMatt Macy uint64_t cb_numupgraded; 2252*eda14cbcSMatt Macy uint64_t cb_numsamegraded; 2253*eda14cbcSMatt Macy uint64_t cb_numfailed; 2254*eda14cbcSMatt Macy uint64_t cb_version; 2255*eda14cbcSMatt Macy boolean_t cb_newer; 2256*eda14cbcSMatt Macy boolean_t cb_foundone; 2257*eda14cbcSMatt Macy char cb_lastfs[ZFS_MAX_DATASET_NAME_LEN]; 2258*eda14cbcSMatt Macy } upgrade_cbdata_t; 2259*eda14cbcSMatt Macy 2260*eda14cbcSMatt Macy static int 2261*eda14cbcSMatt Macy same_pool(zfs_handle_t *zhp, const char *name) 2262*eda14cbcSMatt Macy { 2263*eda14cbcSMatt Macy int len1 = strcspn(name, "/@"); 2264*eda14cbcSMatt Macy const char *zhname = zfs_get_name(zhp); 2265*eda14cbcSMatt Macy int len2 = strcspn(zhname, "/@"); 2266*eda14cbcSMatt Macy 2267*eda14cbcSMatt Macy if (len1 != len2) 2268*eda14cbcSMatt Macy return (B_FALSE); 2269*eda14cbcSMatt Macy return (strncmp(name, zhname, len1) == 0); 2270*eda14cbcSMatt Macy } 2271*eda14cbcSMatt Macy 2272*eda14cbcSMatt Macy static int 2273*eda14cbcSMatt Macy upgrade_list_callback(zfs_handle_t *zhp, void *data) 2274*eda14cbcSMatt Macy { 2275*eda14cbcSMatt Macy upgrade_cbdata_t *cb = data; 2276*eda14cbcSMatt Macy int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 2277*eda14cbcSMatt Macy 2278*eda14cbcSMatt Macy /* list if it's old/new */ 2279*eda14cbcSMatt Macy if ((!cb->cb_newer && version < ZPL_VERSION) || 2280*eda14cbcSMatt Macy (cb->cb_newer && version > ZPL_VERSION)) { 2281*eda14cbcSMatt Macy char *str; 2282*eda14cbcSMatt Macy if (cb->cb_newer) { 2283*eda14cbcSMatt Macy str = gettext("The following filesystems are " 2284*eda14cbcSMatt Macy "formatted using a newer software version and\n" 2285*eda14cbcSMatt Macy "cannot be accessed on the current system.\n\n"); 2286*eda14cbcSMatt Macy } else { 2287*eda14cbcSMatt Macy str = gettext("The following filesystems are " 2288*eda14cbcSMatt Macy "out of date, and can be upgraded. After being\n" 2289*eda14cbcSMatt Macy "upgraded, these filesystems (and any 'zfs send' " 2290*eda14cbcSMatt Macy "streams generated from\n" 2291*eda14cbcSMatt Macy "subsequent snapshots) will no longer be " 2292*eda14cbcSMatt Macy "accessible by older software versions.\n\n"); 2293*eda14cbcSMatt Macy } 2294*eda14cbcSMatt Macy 2295*eda14cbcSMatt Macy if (!cb->cb_foundone) { 2296*eda14cbcSMatt Macy (void) puts(str); 2297*eda14cbcSMatt Macy (void) printf(gettext("VER FILESYSTEM\n")); 2298*eda14cbcSMatt Macy (void) printf(gettext("--- ------------\n")); 2299*eda14cbcSMatt Macy cb->cb_foundone = B_TRUE; 2300*eda14cbcSMatt Macy } 2301*eda14cbcSMatt Macy 2302*eda14cbcSMatt Macy (void) printf("%2u %s\n", version, zfs_get_name(zhp)); 2303*eda14cbcSMatt Macy } 2304*eda14cbcSMatt Macy 2305*eda14cbcSMatt Macy return (0); 2306*eda14cbcSMatt Macy } 2307*eda14cbcSMatt Macy 2308*eda14cbcSMatt Macy static int 2309*eda14cbcSMatt Macy upgrade_set_callback(zfs_handle_t *zhp, void *data) 2310*eda14cbcSMatt Macy { 2311*eda14cbcSMatt Macy upgrade_cbdata_t *cb = data; 2312*eda14cbcSMatt Macy int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 2313*eda14cbcSMatt Macy int needed_spa_version; 2314*eda14cbcSMatt Macy int spa_version; 2315*eda14cbcSMatt Macy 2316*eda14cbcSMatt Macy if (zfs_spa_version(zhp, &spa_version) < 0) 2317*eda14cbcSMatt Macy return (-1); 2318*eda14cbcSMatt Macy 2319*eda14cbcSMatt Macy needed_spa_version = zfs_spa_version_map(cb->cb_version); 2320*eda14cbcSMatt Macy 2321*eda14cbcSMatt Macy if (needed_spa_version < 0) 2322*eda14cbcSMatt Macy return (-1); 2323*eda14cbcSMatt Macy 2324*eda14cbcSMatt Macy if (spa_version < needed_spa_version) { 2325*eda14cbcSMatt Macy /* can't upgrade */ 2326*eda14cbcSMatt Macy (void) printf(gettext("%s: can not be " 2327*eda14cbcSMatt Macy "upgraded; the pool version needs to first " 2328*eda14cbcSMatt Macy "be upgraded\nto version %d\n\n"), 2329*eda14cbcSMatt Macy zfs_get_name(zhp), needed_spa_version); 2330*eda14cbcSMatt Macy cb->cb_numfailed++; 2331*eda14cbcSMatt Macy return (0); 2332*eda14cbcSMatt Macy } 2333*eda14cbcSMatt Macy 2334*eda14cbcSMatt Macy /* upgrade */ 2335*eda14cbcSMatt Macy if (version < cb->cb_version) { 2336*eda14cbcSMatt Macy char verstr[16]; 2337*eda14cbcSMatt Macy (void) snprintf(verstr, sizeof (verstr), 2338*eda14cbcSMatt Macy "%llu", (u_longlong_t)cb->cb_version); 2339*eda14cbcSMatt Macy if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) { 2340*eda14cbcSMatt Macy /* 2341*eda14cbcSMatt Macy * If they did "zfs upgrade -a", then we could 2342*eda14cbcSMatt Macy * be doing ioctls to different pools. We need 2343*eda14cbcSMatt Macy * to log this history once to each pool, and bypass 2344*eda14cbcSMatt Macy * the normal history logging that happens in main(). 2345*eda14cbcSMatt Macy */ 2346*eda14cbcSMatt Macy (void) zpool_log_history(g_zfs, history_str); 2347*eda14cbcSMatt Macy log_history = B_FALSE; 2348*eda14cbcSMatt Macy } 2349*eda14cbcSMatt Macy if (zfs_prop_set(zhp, "version", verstr) == 0) 2350*eda14cbcSMatt Macy cb->cb_numupgraded++; 2351*eda14cbcSMatt Macy else 2352*eda14cbcSMatt Macy cb->cb_numfailed++; 2353*eda14cbcSMatt Macy (void) strcpy(cb->cb_lastfs, zfs_get_name(zhp)); 2354*eda14cbcSMatt Macy } else if (version > cb->cb_version) { 2355*eda14cbcSMatt Macy /* can't downgrade */ 2356*eda14cbcSMatt Macy (void) printf(gettext("%s: can not be downgraded; " 2357*eda14cbcSMatt Macy "it is already at version %u\n"), 2358*eda14cbcSMatt Macy zfs_get_name(zhp), version); 2359*eda14cbcSMatt Macy cb->cb_numfailed++; 2360*eda14cbcSMatt Macy } else { 2361*eda14cbcSMatt Macy cb->cb_numsamegraded++; 2362*eda14cbcSMatt Macy } 2363*eda14cbcSMatt Macy return (0); 2364*eda14cbcSMatt Macy } 2365*eda14cbcSMatt Macy 2366*eda14cbcSMatt Macy /* 2367*eda14cbcSMatt Macy * zfs upgrade 2368*eda14cbcSMatt Macy * zfs upgrade -v 2369*eda14cbcSMatt Macy * zfs upgrade [-r] [-V <version>] <-a | filesystem> 2370*eda14cbcSMatt Macy */ 2371*eda14cbcSMatt Macy static int 2372*eda14cbcSMatt Macy zfs_do_upgrade(int argc, char **argv) 2373*eda14cbcSMatt Macy { 2374*eda14cbcSMatt Macy boolean_t all = B_FALSE; 2375*eda14cbcSMatt Macy boolean_t showversions = B_FALSE; 2376*eda14cbcSMatt Macy int ret = 0; 2377*eda14cbcSMatt Macy upgrade_cbdata_t cb = { 0 }; 2378*eda14cbcSMatt Macy int c; 2379*eda14cbcSMatt Macy int flags = ZFS_ITER_ARGS_CAN_BE_PATHS; 2380*eda14cbcSMatt Macy 2381*eda14cbcSMatt Macy /* check options */ 2382*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "rvV:a")) != -1) { 2383*eda14cbcSMatt Macy switch (c) { 2384*eda14cbcSMatt Macy case 'r': 2385*eda14cbcSMatt Macy flags |= ZFS_ITER_RECURSE; 2386*eda14cbcSMatt Macy break; 2387*eda14cbcSMatt Macy case 'v': 2388*eda14cbcSMatt Macy showversions = B_TRUE; 2389*eda14cbcSMatt Macy break; 2390*eda14cbcSMatt Macy case 'V': 2391*eda14cbcSMatt Macy if (zfs_prop_string_to_index(ZFS_PROP_VERSION, 2392*eda14cbcSMatt Macy optarg, &cb.cb_version) != 0) { 2393*eda14cbcSMatt Macy (void) fprintf(stderr, 2394*eda14cbcSMatt Macy gettext("invalid version %s\n"), optarg); 2395*eda14cbcSMatt Macy usage(B_FALSE); 2396*eda14cbcSMatt Macy } 2397*eda14cbcSMatt Macy break; 2398*eda14cbcSMatt Macy case 'a': 2399*eda14cbcSMatt Macy all = B_TRUE; 2400*eda14cbcSMatt Macy break; 2401*eda14cbcSMatt Macy case '?': 2402*eda14cbcSMatt Macy default: 2403*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2404*eda14cbcSMatt Macy optopt); 2405*eda14cbcSMatt Macy usage(B_FALSE); 2406*eda14cbcSMatt Macy } 2407*eda14cbcSMatt Macy } 2408*eda14cbcSMatt Macy 2409*eda14cbcSMatt Macy argc -= optind; 2410*eda14cbcSMatt Macy argv += optind; 2411*eda14cbcSMatt Macy 2412*eda14cbcSMatt Macy if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version)) 2413*eda14cbcSMatt Macy usage(B_FALSE); 2414*eda14cbcSMatt Macy if (showversions && (flags & ZFS_ITER_RECURSE || all || 2415*eda14cbcSMatt Macy cb.cb_version || argc)) 2416*eda14cbcSMatt Macy usage(B_FALSE); 2417*eda14cbcSMatt Macy if ((all || argc) && (showversions)) 2418*eda14cbcSMatt Macy usage(B_FALSE); 2419*eda14cbcSMatt Macy if (all && argc) 2420*eda14cbcSMatt Macy usage(B_FALSE); 2421*eda14cbcSMatt Macy 2422*eda14cbcSMatt Macy if (showversions) { 2423*eda14cbcSMatt Macy /* Show info on available versions. */ 2424*eda14cbcSMatt Macy (void) printf(gettext("The following filesystem versions are " 2425*eda14cbcSMatt Macy "supported:\n\n")); 2426*eda14cbcSMatt Macy (void) printf(gettext("VER DESCRIPTION\n")); 2427*eda14cbcSMatt Macy (void) printf("--- -----------------------------------------" 2428*eda14cbcSMatt Macy "---------------\n"); 2429*eda14cbcSMatt Macy (void) printf(gettext(" 1 Initial ZFS filesystem version\n")); 2430*eda14cbcSMatt Macy (void) printf(gettext(" 2 Enhanced directory entries\n")); 2431*eda14cbcSMatt Macy (void) printf(gettext(" 3 Case insensitive and filesystem " 2432*eda14cbcSMatt Macy "user identifier (FUID)\n")); 2433*eda14cbcSMatt Macy (void) printf(gettext(" 4 userquota, groupquota " 2434*eda14cbcSMatt Macy "properties\n")); 2435*eda14cbcSMatt Macy (void) printf(gettext(" 5 System attributes\n")); 2436*eda14cbcSMatt Macy (void) printf(gettext("\nFor more information on a particular " 2437*eda14cbcSMatt Macy "version, including supported releases,\n")); 2438*eda14cbcSMatt Macy (void) printf("see the ZFS Administration Guide.\n\n"); 2439*eda14cbcSMatt Macy ret = 0; 2440*eda14cbcSMatt Macy } else if (argc || all) { 2441*eda14cbcSMatt Macy /* Upgrade filesystems */ 2442*eda14cbcSMatt Macy if (cb.cb_version == 0) 2443*eda14cbcSMatt Macy cb.cb_version = ZPL_VERSION; 2444*eda14cbcSMatt Macy ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM, 2445*eda14cbcSMatt Macy NULL, NULL, 0, upgrade_set_callback, &cb); 2446*eda14cbcSMatt Macy (void) printf(gettext("%llu filesystems upgraded\n"), 2447*eda14cbcSMatt Macy (u_longlong_t)cb.cb_numupgraded); 2448*eda14cbcSMatt Macy if (cb.cb_numsamegraded) { 2449*eda14cbcSMatt Macy (void) printf(gettext("%llu filesystems already at " 2450*eda14cbcSMatt Macy "this version\n"), 2451*eda14cbcSMatt Macy (u_longlong_t)cb.cb_numsamegraded); 2452*eda14cbcSMatt Macy } 2453*eda14cbcSMatt Macy if (cb.cb_numfailed != 0) 2454*eda14cbcSMatt Macy ret = 1; 2455*eda14cbcSMatt Macy } else { 2456*eda14cbcSMatt Macy /* List old-version filesystems */ 2457*eda14cbcSMatt Macy boolean_t found; 2458*eda14cbcSMatt Macy (void) printf(gettext("This system is currently running " 2459*eda14cbcSMatt Macy "ZFS filesystem version %llu.\n\n"), ZPL_VERSION); 2460*eda14cbcSMatt Macy 2461*eda14cbcSMatt Macy flags |= ZFS_ITER_RECURSE; 2462*eda14cbcSMatt Macy ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM, 2463*eda14cbcSMatt Macy NULL, NULL, 0, upgrade_list_callback, &cb); 2464*eda14cbcSMatt Macy 2465*eda14cbcSMatt Macy found = cb.cb_foundone; 2466*eda14cbcSMatt Macy cb.cb_foundone = B_FALSE; 2467*eda14cbcSMatt Macy cb.cb_newer = B_TRUE; 2468*eda14cbcSMatt Macy 2469*eda14cbcSMatt Macy ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM, 2470*eda14cbcSMatt Macy NULL, NULL, 0, upgrade_list_callback, &cb); 2471*eda14cbcSMatt Macy 2472*eda14cbcSMatt Macy if (!cb.cb_foundone && !found) { 2473*eda14cbcSMatt Macy (void) printf(gettext("All filesystems are " 2474*eda14cbcSMatt Macy "formatted with the current version.\n")); 2475*eda14cbcSMatt Macy } 2476*eda14cbcSMatt Macy } 2477*eda14cbcSMatt Macy 2478*eda14cbcSMatt Macy return (ret); 2479*eda14cbcSMatt Macy } 2480*eda14cbcSMatt Macy 2481*eda14cbcSMatt Macy /* 2482*eda14cbcSMatt Macy * zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...] 2483*eda14cbcSMatt Macy * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot 2484*eda14cbcSMatt Macy * zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...] 2485*eda14cbcSMatt Macy * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot 2486*eda14cbcSMatt Macy * zfs projectspace [-Hp] [-o field[,...]] [-s field [-s field]...] 2487*eda14cbcSMatt Macy * [-S field [-S field]...] filesystem | snapshot 2488*eda14cbcSMatt Macy * 2489*eda14cbcSMatt Macy * -H Scripted mode; elide headers and separate columns by tabs. 2490*eda14cbcSMatt Macy * -i Translate SID to POSIX ID. 2491*eda14cbcSMatt Macy * -n Print numeric ID instead of user/group name. 2492*eda14cbcSMatt Macy * -o Control which fields to display. 2493*eda14cbcSMatt Macy * -p Use exact (parsable) numeric output. 2494*eda14cbcSMatt Macy * -s Specify sort columns, descending order. 2495*eda14cbcSMatt Macy * -S Specify sort columns, ascending order. 2496*eda14cbcSMatt Macy * -t Control which object types to display. 2497*eda14cbcSMatt Macy * 2498*eda14cbcSMatt Macy * Displays space consumed by, and quotas on, each user in the specified 2499*eda14cbcSMatt Macy * filesystem or snapshot. 2500*eda14cbcSMatt Macy */ 2501*eda14cbcSMatt Macy 2502*eda14cbcSMatt Macy /* us_field_types, us_field_hdr and us_field_names should be kept in sync */ 2503*eda14cbcSMatt Macy enum us_field_types { 2504*eda14cbcSMatt Macy USFIELD_TYPE, 2505*eda14cbcSMatt Macy USFIELD_NAME, 2506*eda14cbcSMatt Macy USFIELD_USED, 2507*eda14cbcSMatt Macy USFIELD_QUOTA, 2508*eda14cbcSMatt Macy USFIELD_OBJUSED, 2509*eda14cbcSMatt Macy USFIELD_OBJQUOTA 2510*eda14cbcSMatt Macy }; 2511*eda14cbcSMatt Macy static char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA", 2512*eda14cbcSMatt Macy "OBJUSED", "OBJQUOTA" }; 2513*eda14cbcSMatt Macy static char *us_field_names[] = { "type", "name", "used", "quota", 2514*eda14cbcSMatt Macy "objused", "objquota" }; 2515*eda14cbcSMatt Macy #define USFIELD_LAST (sizeof (us_field_names) / sizeof (char *)) 2516*eda14cbcSMatt Macy 2517*eda14cbcSMatt Macy #define USTYPE_PSX_GRP (1 << 0) 2518*eda14cbcSMatt Macy #define USTYPE_PSX_USR (1 << 1) 2519*eda14cbcSMatt Macy #define USTYPE_SMB_GRP (1 << 2) 2520*eda14cbcSMatt Macy #define USTYPE_SMB_USR (1 << 3) 2521*eda14cbcSMatt Macy #define USTYPE_PROJ (1 << 4) 2522*eda14cbcSMatt Macy #define USTYPE_ALL \ 2523*eda14cbcSMatt Macy (USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR | \ 2524*eda14cbcSMatt Macy USTYPE_PROJ) 2525*eda14cbcSMatt Macy 2526*eda14cbcSMatt Macy static int us_type_bits[] = { 2527*eda14cbcSMatt Macy USTYPE_PSX_GRP, 2528*eda14cbcSMatt Macy USTYPE_PSX_USR, 2529*eda14cbcSMatt Macy USTYPE_SMB_GRP, 2530*eda14cbcSMatt Macy USTYPE_SMB_USR, 2531*eda14cbcSMatt Macy USTYPE_ALL 2532*eda14cbcSMatt Macy }; 2533*eda14cbcSMatt Macy static char *us_type_names[] = { "posixgroup", "posixuser", "smbgroup", 2534*eda14cbcSMatt Macy "smbuser", "all" }; 2535*eda14cbcSMatt Macy 2536*eda14cbcSMatt Macy typedef struct us_node { 2537*eda14cbcSMatt Macy nvlist_t *usn_nvl; 2538*eda14cbcSMatt Macy uu_avl_node_t usn_avlnode; 2539*eda14cbcSMatt Macy uu_list_node_t usn_listnode; 2540*eda14cbcSMatt Macy } us_node_t; 2541*eda14cbcSMatt Macy 2542*eda14cbcSMatt Macy typedef struct us_cbdata { 2543*eda14cbcSMatt Macy nvlist_t **cb_nvlp; 2544*eda14cbcSMatt Macy uu_avl_pool_t *cb_avl_pool; 2545*eda14cbcSMatt Macy uu_avl_t *cb_avl; 2546*eda14cbcSMatt Macy boolean_t cb_numname; 2547*eda14cbcSMatt Macy boolean_t cb_nicenum; 2548*eda14cbcSMatt Macy boolean_t cb_sid2posix; 2549*eda14cbcSMatt Macy zfs_userquota_prop_t cb_prop; 2550*eda14cbcSMatt Macy zfs_sort_column_t *cb_sortcol; 2551*eda14cbcSMatt Macy size_t cb_width[USFIELD_LAST]; 2552*eda14cbcSMatt Macy } us_cbdata_t; 2553*eda14cbcSMatt Macy 2554*eda14cbcSMatt Macy static boolean_t us_populated = B_FALSE; 2555*eda14cbcSMatt Macy 2556*eda14cbcSMatt Macy typedef struct { 2557*eda14cbcSMatt Macy zfs_sort_column_t *si_sortcol; 2558*eda14cbcSMatt Macy boolean_t si_numname; 2559*eda14cbcSMatt Macy } us_sort_info_t; 2560*eda14cbcSMatt Macy 2561*eda14cbcSMatt Macy static int 2562*eda14cbcSMatt Macy us_field_index(char *field) 2563*eda14cbcSMatt Macy { 2564*eda14cbcSMatt Macy int i; 2565*eda14cbcSMatt Macy 2566*eda14cbcSMatt Macy for (i = 0; i < USFIELD_LAST; i++) { 2567*eda14cbcSMatt Macy if (strcmp(field, us_field_names[i]) == 0) 2568*eda14cbcSMatt Macy return (i); 2569*eda14cbcSMatt Macy } 2570*eda14cbcSMatt Macy 2571*eda14cbcSMatt Macy return (-1); 2572*eda14cbcSMatt Macy } 2573*eda14cbcSMatt Macy 2574*eda14cbcSMatt Macy static int 2575*eda14cbcSMatt Macy us_compare(const void *larg, const void *rarg, void *unused) 2576*eda14cbcSMatt Macy { 2577*eda14cbcSMatt Macy const us_node_t *l = larg; 2578*eda14cbcSMatt Macy const us_node_t *r = rarg; 2579*eda14cbcSMatt Macy us_sort_info_t *si = (us_sort_info_t *)unused; 2580*eda14cbcSMatt Macy zfs_sort_column_t *sortcol = si->si_sortcol; 2581*eda14cbcSMatt Macy boolean_t numname = si->si_numname; 2582*eda14cbcSMatt Macy nvlist_t *lnvl = l->usn_nvl; 2583*eda14cbcSMatt Macy nvlist_t *rnvl = r->usn_nvl; 2584*eda14cbcSMatt Macy int rc = 0; 2585*eda14cbcSMatt Macy boolean_t lvb, rvb; 2586*eda14cbcSMatt Macy 2587*eda14cbcSMatt Macy for (; sortcol != NULL; sortcol = sortcol->sc_next) { 2588*eda14cbcSMatt Macy char *lvstr = ""; 2589*eda14cbcSMatt Macy char *rvstr = ""; 2590*eda14cbcSMatt Macy uint32_t lv32 = 0; 2591*eda14cbcSMatt Macy uint32_t rv32 = 0; 2592*eda14cbcSMatt Macy uint64_t lv64 = 0; 2593*eda14cbcSMatt Macy uint64_t rv64 = 0; 2594*eda14cbcSMatt Macy zfs_prop_t prop = sortcol->sc_prop; 2595*eda14cbcSMatt Macy const char *propname = NULL; 2596*eda14cbcSMatt Macy boolean_t reverse = sortcol->sc_reverse; 2597*eda14cbcSMatt Macy 2598*eda14cbcSMatt Macy switch (prop) { 2599*eda14cbcSMatt Macy case ZFS_PROP_TYPE: 2600*eda14cbcSMatt Macy propname = "type"; 2601*eda14cbcSMatt Macy (void) nvlist_lookup_uint32(lnvl, propname, &lv32); 2602*eda14cbcSMatt Macy (void) nvlist_lookup_uint32(rnvl, propname, &rv32); 2603*eda14cbcSMatt Macy if (rv32 != lv32) 2604*eda14cbcSMatt Macy rc = (rv32 < lv32) ? 1 : -1; 2605*eda14cbcSMatt Macy break; 2606*eda14cbcSMatt Macy case ZFS_PROP_NAME: 2607*eda14cbcSMatt Macy propname = "name"; 2608*eda14cbcSMatt Macy if (numname) { 2609*eda14cbcSMatt Macy compare_nums: 2610*eda14cbcSMatt Macy (void) nvlist_lookup_uint64(lnvl, propname, 2611*eda14cbcSMatt Macy &lv64); 2612*eda14cbcSMatt Macy (void) nvlist_lookup_uint64(rnvl, propname, 2613*eda14cbcSMatt Macy &rv64); 2614*eda14cbcSMatt Macy if (rv64 != lv64) 2615*eda14cbcSMatt Macy rc = (rv64 < lv64) ? 1 : -1; 2616*eda14cbcSMatt Macy } else { 2617*eda14cbcSMatt Macy if ((nvlist_lookup_string(lnvl, propname, 2618*eda14cbcSMatt Macy &lvstr) == ENOENT) || 2619*eda14cbcSMatt Macy (nvlist_lookup_string(rnvl, propname, 2620*eda14cbcSMatt Macy &rvstr) == ENOENT)) { 2621*eda14cbcSMatt Macy goto compare_nums; 2622*eda14cbcSMatt Macy } 2623*eda14cbcSMatt Macy rc = strcmp(lvstr, rvstr); 2624*eda14cbcSMatt Macy } 2625*eda14cbcSMatt Macy break; 2626*eda14cbcSMatt Macy case ZFS_PROP_USED: 2627*eda14cbcSMatt Macy case ZFS_PROP_QUOTA: 2628*eda14cbcSMatt Macy if (!us_populated) 2629*eda14cbcSMatt Macy break; 2630*eda14cbcSMatt Macy if (prop == ZFS_PROP_USED) 2631*eda14cbcSMatt Macy propname = "used"; 2632*eda14cbcSMatt Macy else 2633*eda14cbcSMatt Macy propname = "quota"; 2634*eda14cbcSMatt Macy (void) nvlist_lookup_uint64(lnvl, propname, &lv64); 2635*eda14cbcSMatt Macy (void) nvlist_lookup_uint64(rnvl, propname, &rv64); 2636*eda14cbcSMatt Macy if (rv64 != lv64) 2637*eda14cbcSMatt Macy rc = (rv64 < lv64) ? 1 : -1; 2638*eda14cbcSMatt Macy break; 2639*eda14cbcSMatt Macy 2640*eda14cbcSMatt Macy default: 2641*eda14cbcSMatt Macy break; 2642*eda14cbcSMatt Macy } 2643*eda14cbcSMatt Macy 2644*eda14cbcSMatt Macy if (rc != 0) { 2645*eda14cbcSMatt Macy if (rc < 0) 2646*eda14cbcSMatt Macy return (reverse ? 1 : -1); 2647*eda14cbcSMatt Macy else 2648*eda14cbcSMatt Macy return (reverse ? -1 : 1); 2649*eda14cbcSMatt Macy } 2650*eda14cbcSMatt Macy } 2651*eda14cbcSMatt Macy 2652*eda14cbcSMatt Macy /* 2653*eda14cbcSMatt Macy * If entries still seem to be the same, check if they are of the same 2654*eda14cbcSMatt Macy * type (smbentity is added only if we are doing SID to POSIX ID 2655*eda14cbcSMatt Macy * translation where we can have duplicate type/name combinations). 2656*eda14cbcSMatt Macy */ 2657*eda14cbcSMatt Macy if (nvlist_lookup_boolean_value(lnvl, "smbentity", &lvb) == 0 && 2658*eda14cbcSMatt Macy nvlist_lookup_boolean_value(rnvl, "smbentity", &rvb) == 0 && 2659*eda14cbcSMatt Macy lvb != rvb) 2660*eda14cbcSMatt Macy return (lvb < rvb ? -1 : 1); 2661*eda14cbcSMatt Macy 2662*eda14cbcSMatt Macy return (0); 2663*eda14cbcSMatt Macy } 2664*eda14cbcSMatt Macy 2665*eda14cbcSMatt Macy static boolean_t 2666*eda14cbcSMatt Macy zfs_prop_is_user(unsigned p) 2667*eda14cbcSMatt Macy { 2668*eda14cbcSMatt Macy return (p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA || 2669*eda14cbcSMatt Macy p == ZFS_PROP_USEROBJUSED || p == ZFS_PROP_USEROBJQUOTA); 2670*eda14cbcSMatt Macy } 2671*eda14cbcSMatt Macy 2672*eda14cbcSMatt Macy static boolean_t 2673*eda14cbcSMatt Macy zfs_prop_is_group(unsigned p) 2674*eda14cbcSMatt Macy { 2675*eda14cbcSMatt Macy return (p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA || 2676*eda14cbcSMatt Macy p == ZFS_PROP_GROUPOBJUSED || p == ZFS_PROP_GROUPOBJQUOTA); 2677*eda14cbcSMatt Macy } 2678*eda14cbcSMatt Macy 2679*eda14cbcSMatt Macy static boolean_t 2680*eda14cbcSMatt Macy zfs_prop_is_project(unsigned p) 2681*eda14cbcSMatt Macy { 2682*eda14cbcSMatt Macy return (p == ZFS_PROP_PROJECTUSED || p == ZFS_PROP_PROJECTQUOTA || 2683*eda14cbcSMatt Macy p == ZFS_PROP_PROJECTOBJUSED || p == ZFS_PROP_PROJECTOBJQUOTA); 2684*eda14cbcSMatt Macy } 2685*eda14cbcSMatt Macy 2686*eda14cbcSMatt Macy static inline const char * 2687*eda14cbcSMatt Macy us_type2str(unsigned field_type) 2688*eda14cbcSMatt Macy { 2689*eda14cbcSMatt Macy switch (field_type) { 2690*eda14cbcSMatt Macy case USTYPE_PSX_USR: 2691*eda14cbcSMatt Macy return ("POSIX User"); 2692*eda14cbcSMatt Macy case USTYPE_PSX_GRP: 2693*eda14cbcSMatt Macy return ("POSIX Group"); 2694*eda14cbcSMatt Macy case USTYPE_SMB_USR: 2695*eda14cbcSMatt Macy return ("SMB User"); 2696*eda14cbcSMatt Macy case USTYPE_SMB_GRP: 2697*eda14cbcSMatt Macy return ("SMB Group"); 2698*eda14cbcSMatt Macy case USTYPE_PROJ: 2699*eda14cbcSMatt Macy return ("Project"); 2700*eda14cbcSMatt Macy default: 2701*eda14cbcSMatt Macy return ("Undefined"); 2702*eda14cbcSMatt Macy } 2703*eda14cbcSMatt Macy } 2704*eda14cbcSMatt Macy 2705*eda14cbcSMatt Macy static int 2706*eda14cbcSMatt Macy userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space) 2707*eda14cbcSMatt Macy { 2708*eda14cbcSMatt Macy us_cbdata_t *cb = (us_cbdata_t *)arg; 2709*eda14cbcSMatt Macy zfs_userquota_prop_t prop = cb->cb_prop; 2710*eda14cbcSMatt Macy char *name = NULL; 2711*eda14cbcSMatt Macy char *propname; 2712*eda14cbcSMatt Macy char sizebuf[32]; 2713*eda14cbcSMatt Macy us_node_t *node; 2714*eda14cbcSMatt Macy uu_avl_pool_t *avl_pool = cb->cb_avl_pool; 2715*eda14cbcSMatt Macy uu_avl_t *avl = cb->cb_avl; 2716*eda14cbcSMatt Macy uu_avl_index_t idx; 2717*eda14cbcSMatt Macy nvlist_t *props; 2718*eda14cbcSMatt Macy us_node_t *n; 2719*eda14cbcSMatt Macy zfs_sort_column_t *sortcol = cb->cb_sortcol; 2720*eda14cbcSMatt Macy unsigned type = 0; 2721*eda14cbcSMatt Macy const char *typestr; 2722*eda14cbcSMatt Macy size_t namelen; 2723*eda14cbcSMatt Macy size_t typelen; 2724*eda14cbcSMatt Macy size_t sizelen; 2725*eda14cbcSMatt Macy int typeidx, nameidx, sizeidx; 2726*eda14cbcSMatt Macy us_sort_info_t sortinfo = { sortcol, cb->cb_numname }; 2727*eda14cbcSMatt Macy boolean_t smbentity = B_FALSE; 2728*eda14cbcSMatt Macy 2729*eda14cbcSMatt Macy if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 2730*eda14cbcSMatt Macy nomem(); 2731*eda14cbcSMatt Macy node = safe_malloc(sizeof (us_node_t)); 2732*eda14cbcSMatt Macy uu_avl_node_init(node, &node->usn_avlnode, avl_pool); 2733*eda14cbcSMatt Macy node->usn_nvl = props; 2734*eda14cbcSMatt Macy 2735*eda14cbcSMatt Macy if (domain != NULL && domain[0] != '\0') { 2736*eda14cbcSMatt Macy #ifdef HAVE_IDMAP 2737*eda14cbcSMatt Macy /* SMB */ 2738*eda14cbcSMatt Macy char sid[MAXNAMELEN + 32]; 2739*eda14cbcSMatt Macy uid_t id; 2740*eda14cbcSMatt Macy uint64_t classes; 2741*eda14cbcSMatt Macy int err; 2742*eda14cbcSMatt Macy directory_error_t e; 2743*eda14cbcSMatt Macy 2744*eda14cbcSMatt Macy smbentity = B_TRUE; 2745*eda14cbcSMatt Macy 2746*eda14cbcSMatt Macy (void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid); 2747*eda14cbcSMatt Macy 2748*eda14cbcSMatt Macy if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) { 2749*eda14cbcSMatt Macy type = USTYPE_SMB_GRP; 2750*eda14cbcSMatt Macy err = sid_to_id(sid, B_FALSE, &id); 2751*eda14cbcSMatt Macy } else { 2752*eda14cbcSMatt Macy type = USTYPE_SMB_USR; 2753*eda14cbcSMatt Macy err = sid_to_id(sid, B_TRUE, &id); 2754*eda14cbcSMatt Macy } 2755*eda14cbcSMatt Macy 2756*eda14cbcSMatt Macy if (err == 0) { 2757*eda14cbcSMatt Macy rid = id; 2758*eda14cbcSMatt Macy if (!cb->cb_sid2posix) { 2759*eda14cbcSMatt Macy e = directory_name_from_sid(NULL, sid, &name, 2760*eda14cbcSMatt Macy &classes); 2761*eda14cbcSMatt Macy if (e != NULL) 2762*eda14cbcSMatt Macy directory_error_free(e); 2763*eda14cbcSMatt Macy if (name == NULL) 2764*eda14cbcSMatt Macy name = sid; 2765*eda14cbcSMatt Macy } 2766*eda14cbcSMatt Macy } 2767*eda14cbcSMatt Macy #else 2768*eda14cbcSMatt Macy nvlist_free(props); 2769*eda14cbcSMatt Macy free(node); 2770*eda14cbcSMatt Macy 2771*eda14cbcSMatt Macy return (-1); 2772*eda14cbcSMatt Macy #endif /* HAVE_IDMAP */ 2773*eda14cbcSMatt Macy } 2774*eda14cbcSMatt Macy 2775*eda14cbcSMatt Macy if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') { 2776*eda14cbcSMatt Macy /* POSIX or -i */ 2777*eda14cbcSMatt Macy if (zfs_prop_is_group(prop)) { 2778*eda14cbcSMatt Macy type = USTYPE_PSX_GRP; 2779*eda14cbcSMatt Macy if (!cb->cb_numname) { 2780*eda14cbcSMatt Macy struct group *g; 2781*eda14cbcSMatt Macy 2782*eda14cbcSMatt Macy if ((g = getgrgid(rid)) != NULL) 2783*eda14cbcSMatt Macy name = g->gr_name; 2784*eda14cbcSMatt Macy } 2785*eda14cbcSMatt Macy } else if (zfs_prop_is_user(prop)) { 2786*eda14cbcSMatt Macy type = USTYPE_PSX_USR; 2787*eda14cbcSMatt Macy if (!cb->cb_numname) { 2788*eda14cbcSMatt Macy struct passwd *p; 2789*eda14cbcSMatt Macy 2790*eda14cbcSMatt Macy if ((p = getpwuid(rid)) != NULL) 2791*eda14cbcSMatt Macy name = p->pw_name; 2792*eda14cbcSMatt Macy } 2793*eda14cbcSMatt Macy } else { 2794*eda14cbcSMatt Macy type = USTYPE_PROJ; 2795*eda14cbcSMatt Macy } 2796*eda14cbcSMatt Macy } 2797*eda14cbcSMatt Macy 2798*eda14cbcSMatt Macy /* 2799*eda14cbcSMatt Macy * Make sure that the type/name combination is unique when doing 2800*eda14cbcSMatt Macy * SID to POSIX ID translation (hence changing the type from SMB to 2801*eda14cbcSMatt Macy * POSIX). 2802*eda14cbcSMatt Macy */ 2803*eda14cbcSMatt Macy if (cb->cb_sid2posix && 2804*eda14cbcSMatt Macy nvlist_add_boolean_value(props, "smbentity", smbentity) != 0) 2805*eda14cbcSMatt Macy nomem(); 2806*eda14cbcSMatt Macy 2807*eda14cbcSMatt Macy /* Calculate/update width of TYPE field */ 2808*eda14cbcSMatt Macy typestr = us_type2str(type); 2809*eda14cbcSMatt Macy typelen = strlen(gettext(typestr)); 2810*eda14cbcSMatt Macy typeidx = us_field_index("type"); 2811*eda14cbcSMatt Macy if (typelen > cb->cb_width[typeidx]) 2812*eda14cbcSMatt Macy cb->cb_width[typeidx] = typelen; 2813*eda14cbcSMatt Macy if (nvlist_add_uint32(props, "type", type) != 0) 2814*eda14cbcSMatt Macy nomem(); 2815*eda14cbcSMatt Macy 2816*eda14cbcSMatt Macy /* Calculate/update width of NAME field */ 2817*eda14cbcSMatt Macy if ((cb->cb_numname && cb->cb_sid2posix) || name == NULL) { 2818*eda14cbcSMatt Macy if (nvlist_add_uint64(props, "name", rid) != 0) 2819*eda14cbcSMatt Macy nomem(); 2820*eda14cbcSMatt Macy namelen = snprintf(NULL, 0, "%u", rid); 2821*eda14cbcSMatt Macy } else { 2822*eda14cbcSMatt Macy if (nvlist_add_string(props, "name", name) != 0) 2823*eda14cbcSMatt Macy nomem(); 2824*eda14cbcSMatt Macy namelen = strlen(name); 2825*eda14cbcSMatt Macy } 2826*eda14cbcSMatt Macy nameidx = us_field_index("name"); 2827*eda14cbcSMatt Macy if (nameidx >= 0 && namelen > cb->cb_width[nameidx]) 2828*eda14cbcSMatt Macy cb->cb_width[nameidx] = namelen; 2829*eda14cbcSMatt Macy 2830*eda14cbcSMatt Macy /* 2831*eda14cbcSMatt Macy * Check if this type/name combination is in the list and update it; 2832*eda14cbcSMatt Macy * otherwise add new node to the list. 2833*eda14cbcSMatt Macy */ 2834*eda14cbcSMatt Macy if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) { 2835*eda14cbcSMatt Macy uu_avl_insert(avl, node, idx); 2836*eda14cbcSMatt Macy } else { 2837*eda14cbcSMatt Macy nvlist_free(props); 2838*eda14cbcSMatt Macy free(node); 2839*eda14cbcSMatt Macy node = n; 2840*eda14cbcSMatt Macy props = node->usn_nvl; 2841*eda14cbcSMatt Macy } 2842*eda14cbcSMatt Macy 2843*eda14cbcSMatt Macy /* Calculate/update width of USED/QUOTA fields */ 2844*eda14cbcSMatt Macy if (cb->cb_nicenum) { 2845*eda14cbcSMatt Macy if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED || 2846*eda14cbcSMatt Macy prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA || 2847*eda14cbcSMatt Macy prop == ZFS_PROP_PROJECTUSED || 2848*eda14cbcSMatt Macy prop == ZFS_PROP_PROJECTQUOTA) { 2849*eda14cbcSMatt Macy zfs_nicebytes(space, sizebuf, sizeof (sizebuf)); 2850*eda14cbcSMatt Macy } else { 2851*eda14cbcSMatt Macy zfs_nicenum(space, sizebuf, sizeof (sizebuf)); 2852*eda14cbcSMatt Macy } 2853*eda14cbcSMatt Macy } else { 2854*eda14cbcSMatt Macy (void) snprintf(sizebuf, sizeof (sizebuf), "%llu", 2855*eda14cbcSMatt Macy (u_longlong_t)space); 2856*eda14cbcSMatt Macy } 2857*eda14cbcSMatt Macy sizelen = strlen(sizebuf); 2858*eda14cbcSMatt Macy if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED || 2859*eda14cbcSMatt Macy prop == ZFS_PROP_PROJECTUSED) { 2860*eda14cbcSMatt Macy propname = "used"; 2861*eda14cbcSMatt Macy if (!nvlist_exists(props, "quota")) 2862*eda14cbcSMatt Macy (void) nvlist_add_uint64(props, "quota", 0); 2863*eda14cbcSMatt Macy } else if (prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA || 2864*eda14cbcSMatt Macy prop == ZFS_PROP_PROJECTQUOTA) { 2865*eda14cbcSMatt Macy propname = "quota"; 2866*eda14cbcSMatt Macy if (!nvlist_exists(props, "used")) 2867*eda14cbcSMatt Macy (void) nvlist_add_uint64(props, "used", 0); 2868*eda14cbcSMatt Macy } else if (prop == ZFS_PROP_USEROBJUSED || 2869*eda14cbcSMatt Macy prop == ZFS_PROP_GROUPOBJUSED || prop == ZFS_PROP_PROJECTOBJUSED) { 2870*eda14cbcSMatt Macy propname = "objused"; 2871*eda14cbcSMatt Macy if (!nvlist_exists(props, "objquota")) 2872*eda14cbcSMatt Macy (void) nvlist_add_uint64(props, "objquota", 0); 2873*eda14cbcSMatt Macy } else if (prop == ZFS_PROP_USEROBJQUOTA || 2874*eda14cbcSMatt Macy prop == ZFS_PROP_GROUPOBJQUOTA || 2875*eda14cbcSMatt Macy prop == ZFS_PROP_PROJECTOBJQUOTA) { 2876*eda14cbcSMatt Macy propname = "objquota"; 2877*eda14cbcSMatt Macy if (!nvlist_exists(props, "objused")) 2878*eda14cbcSMatt Macy (void) nvlist_add_uint64(props, "objused", 0); 2879*eda14cbcSMatt Macy } else { 2880*eda14cbcSMatt Macy return (-1); 2881*eda14cbcSMatt Macy } 2882*eda14cbcSMatt Macy sizeidx = us_field_index(propname); 2883*eda14cbcSMatt Macy if (sizeidx >= 0 && sizelen > cb->cb_width[sizeidx]) 2884*eda14cbcSMatt Macy cb->cb_width[sizeidx] = sizelen; 2885*eda14cbcSMatt Macy 2886*eda14cbcSMatt Macy if (nvlist_add_uint64(props, propname, space) != 0) 2887*eda14cbcSMatt Macy nomem(); 2888*eda14cbcSMatt Macy 2889*eda14cbcSMatt Macy return (0); 2890*eda14cbcSMatt Macy } 2891*eda14cbcSMatt Macy 2892*eda14cbcSMatt Macy static void 2893*eda14cbcSMatt Macy print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types, 2894*eda14cbcSMatt Macy size_t *width, us_node_t *node) 2895*eda14cbcSMatt Macy { 2896*eda14cbcSMatt Macy nvlist_t *nvl = node->usn_nvl; 2897*eda14cbcSMatt Macy char valstr[MAXNAMELEN]; 2898*eda14cbcSMatt Macy boolean_t first = B_TRUE; 2899*eda14cbcSMatt Macy int cfield = 0; 2900*eda14cbcSMatt Macy int field; 2901*eda14cbcSMatt Macy uint32_t ustype; 2902*eda14cbcSMatt Macy 2903*eda14cbcSMatt Macy /* Check type */ 2904*eda14cbcSMatt Macy (void) nvlist_lookup_uint32(nvl, "type", &ustype); 2905*eda14cbcSMatt Macy if (!(ustype & types)) 2906*eda14cbcSMatt Macy return; 2907*eda14cbcSMatt Macy 2908*eda14cbcSMatt Macy while ((field = fields[cfield]) != USFIELD_LAST) { 2909*eda14cbcSMatt Macy nvpair_t *nvp = NULL; 2910*eda14cbcSMatt Macy data_type_t type; 2911*eda14cbcSMatt Macy uint32_t val32; 2912*eda14cbcSMatt Macy uint64_t val64; 2913*eda14cbcSMatt Macy char *strval = "-"; 2914*eda14cbcSMatt Macy 2915*eda14cbcSMatt Macy while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 2916*eda14cbcSMatt Macy if (strcmp(nvpair_name(nvp), 2917*eda14cbcSMatt Macy us_field_names[field]) == 0) 2918*eda14cbcSMatt Macy break; 2919*eda14cbcSMatt Macy } 2920*eda14cbcSMatt Macy 2921*eda14cbcSMatt Macy type = nvp == NULL ? DATA_TYPE_UNKNOWN : nvpair_type(nvp); 2922*eda14cbcSMatt Macy switch (type) { 2923*eda14cbcSMatt Macy case DATA_TYPE_UINT32: 2924*eda14cbcSMatt Macy (void) nvpair_value_uint32(nvp, &val32); 2925*eda14cbcSMatt Macy break; 2926*eda14cbcSMatt Macy case DATA_TYPE_UINT64: 2927*eda14cbcSMatt Macy (void) nvpair_value_uint64(nvp, &val64); 2928*eda14cbcSMatt Macy break; 2929*eda14cbcSMatt Macy case DATA_TYPE_STRING: 2930*eda14cbcSMatt Macy (void) nvpair_value_string(nvp, &strval); 2931*eda14cbcSMatt Macy break; 2932*eda14cbcSMatt Macy case DATA_TYPE_UNKNOWN: 2933*eda14cbcSMatt Macy break; 2934*eda14cbcSMatt Macy default: 2935*eda14cbcSMatt Macy (void) fprintf(stderr, "invalid data type\n"); 2936*eda14cbcSMatt Macy } 2937*eda14cbcSMatt Macy 2938*eda14cbcSMatt Macy switch (field) { 2939*eda14cbcSMatt Macy case USFIELD_TYPE: 2940*eda14cbcSMatt Macy if (type == DATA_TYPE_UINT32) 2941*eda14cbcSMatt Macy strval = (char *)us_type2str(val32); 2942*eda14cbcSMatt Macy break; 2943*eda14cbcSMatt Macy case USFIELD_NAME: 2944*eda14cbcSMatt Macy if (type == DATA_TYPE_UINT64) { 2945*eda14cbcSMatt Macy (void) sprintf(valstr, "%llu", 2946*eda14cbcSMatt Macy (u_longlong_t)val64); 2947*eda14cbcSMatt Macy strval = valstr; 2948*eda14cbcSMatt Macy } 2949*eda14cbcSMatt Macy break; 2950*eda14cbcSMatt Macy case USFIELD_USED: 2951*eda14cbcSMatt Macy case USFIELD_QUOTA: 2952*eda14cbcSMatt Macy if (type == DATA_TYPE_UINT64) { 2953*eda14cbcSMatt Macy if (parsable) { 2954*eda14cbcSMatt Macy (void) sprintf(valstr, "%llu", 2955*eda14cbcSMatt Macy (u_longlong_t)val64); 2956*eda14cbcSMatt Macy strval = valstr; 2957*eda14cbcSMatt Macy } else if (field == USFIELD_QUOTA && 2958*eda14cbcSMatt Macy val64 == 0) { 2959*eda14cbcSMatt Macy strval = "none"; 2960*eda14cbcSMatt Macy } else { 2961*eda14cbcSMatt Macy zfs_nicebytes(val64, valstr, 2962*eda14cbcSMatt Macy sizeof (valstr)); 2963*eda14cbcSMatt Macy strval = valstr; 2964*eda14cbcSMatt Macy } 2965*eda14cbcSMatt Macy } 2966*eda14cbcSMatt Macy break; 2967*eda14cbcSMatt Macy case USFIELD_OBJUSED: 2968*eda14cbcSMatt Macy case USFIELD_OBJQUOTA: 2969*eda14cbcSMatt Macy if (type == DATA_TYPE_UINT64) { 2970*eda14cbcSMatt Macy if (parsable) { 2971*eda14cbcSMatt Macy (void) sprintf(valstr, "%llu", 2972*eda14cbcSMatt Macy (u_longlong_t)val64); 2973*eda14cbcSMatt Macy strval = valstr; 2974*eda14cbcSMatt Macy } else if (field == USFIELD_OBJQUOTA && 2975*eda14cbcSMatt Macy val64 == 0) { 2976*eda14cbcSMatt Macy strval = "none"; 2977*eda14cbcSMatt Macy } else { 2978*eda14cbcSMatt Macy zfs_nicenum(val64, valstr, 2979*eda14cbcSMatt Macy sizeof (valstr)); 2980*eda14cbcSMatt Macy strval = valstr; 2981*eda14cbcSMatt Macy } 2982*eda14cbcSMatt Macy } 2983*eda14cbcSMatt Macy break; 2984*eda14cbcSMatt Macy } 2985*eda14cbcSMatt Macy 2986*eda14cbcSMatt Macy if (!first) { 2987*eda14cbcSMatt Macy if (scripted) 2988*eda14cbcSMatt Macy (void) printf("\t"); 2989*eda14cbcSMatt Macy else 2990*eda14cbcSMatt Macy (void) printf(" "); 2991*eda14cbcSMatt Macy } 2992*eda14cbcSMatt Macy if (scripted) 2993*eda14cbcSMatt Macy (void) printf("%s", strval); 2994*eda14cbcSMatt Macy else if (field == USFIELD_TYPE || field == USFIELD_NAME) 2995*eda14cbcSMatt Macy (void) printf("%-*s", (int)width[field], strval); 2996*eda14cbcSMatt Macy else 2997*eda14cbcSMatt Macy (void) printf("%*s", (int)width[field], strval); 2998*eda14cbcSMatt Macy 2999*eda14cbcSMatt Macy first = B_FALSE; 3000*eda14cbcSMatt Macy cfield++; 3001*eda14cbcSMatt Macy } 3002*eda14cbcSMatt Macy 3003*eda14cbcSMatt Macy (void) printf("\n"); 3004*eda14cbcSMatt Macy } 3005*eda14cbcSMatt Macy 3006*eda14cbcSMatt Macy static void 3007*eda14cbcSMatt Macy print_us(boolean_t scripted, boolean_t parsable, int *fields, int types, 3008*eda14cbcSMatt Macy size_t *width, boolean_t rmnode, uu_avl_t *avl) 3009*eda14cbcSMatt Macy { 3010*eda14cbcSMatt Macy us_node_t *node; 3011*eda14cbcSMatt Macy const char *col; 3012*eda14cbcSMatt Macy int cfield = 0; 3013*eda14cbcSMatt Macy int field; 3014*eda14cbcSMatt Macy 3015*eda14cbcSMatt Macy if (!scripted) { 3016*eda14cbcSMatt Macy boolean_t first = B_TRUE; 3017*eda14cbcSMatt Macy 3018*eda14cbcSMatt Macy while ((field = fields[cfield]) != USFIELD_LAST) { 3019*eda14cbcSMatt Macy col = gettext(us_field_hdr[field]); 3020*eda14cbcSMatt Macy if (field == USFIELD_TYPE || field == USFIELD_NAME) { 3021*eda14cbcSMatt Macy (void) printf(first ? "%-*s" : " %-*s", 3022*eda14cbcSMatt Macy (int)width[field], col); 3023*eda14cbcSMatt Macy } else { 3024*eda14cbcSMatt Macy (void) printf(first ? "%*s" : " %*s", 3025*eda14cbcSMatt Macy (int)width[field], col); 3026*eda14cbcSMatt Macy } 3027*eda14cbcSMatt Macy first = B_FALSE; 3028*eda14cbcSMatt Macy cfield++; 3029*eda14cbcSMatt Macy } 3030*eda14cbcSMatt Macy (void) printf("\n"); 3031*eda14cbcSMatt Macy } 3032*eda14cbcSMatt Macy 3033*eda14cbcSMatt Macy for (node = uu_avl_first(avl); node; node = uu_avl_next(avl, node)) { 3034*eda14cbcSMatt Macy print_us_node(scripted, parsable, fields, types, width, node); 3035*eda14cbcSMatt Macy if (rmnode) 3036*eda14cbcSMatt Macy nvlist_free(node->usn_nvl); 3037*eda14cbcSMatt Macy } 3038*eda14cbcSMatt Macy } 3039*eda14cbcSMatt Macy 3040*eda14cbcSMatt Macy static int 3041*eda14cbcSMatt Macy zfs_do_userspace(int argc, char **argv) 3042*eda14cbcSMatt Macy { 3043*eda14cbcSMatt Macy zfs_handle_t *zhp; 3044*eda14cbcSMatt Macy zfs_userquota_prop_t p; 3045*eda14cbcSMatt Macy uu_avl_pool_t *avl_pool; 3046*eda14cbcSMatt Macy uu_avl_t *avl_tree; 3047*eda14cbcSMatt Macy uu_avl_walk_t *walk; 3048*eda14cbcSMatt Macy char *delim; 3049*eda14cbcSMatt Macy char deffields[] = "type,name,used,quota,objused,objquota"; 3050*eda14cbcSMatt Macy char *ofield = NULL; 3051*eda14cbcSMatt Macy char *tfield = NULL; 3052*eda14cbcSMatt Macy int cfield = 0; 3053*eda14cbcSMatt Macy int fields[256]; 3054*eda14cbcSMatt Macy int i; 3055*eda14cbcSMatt Macy boolean_t scripted = B_FALSE; 3056*eda14cbcSMatt Macy boolean_t prtnum = B_FALSE; 3057*eda14cbcSMatt Macy boolean_t parsable = B_FALSE; 3058*eda14cbcSMatt Macy boolean_t sid2posix = B_FALSE; 3059*eda14cbcSMatt Macy int ret = 0; 3060*eda14cbcSMatt Macy int c; 3061*eda14cbcSMatt Macy zfs_sort_column_t *sortcol = NULL; 3062*eda14cbcSMatt Macy int types = USTYPE_PSX_USR | USTYPE_SMB_USR; 3063*eda14cbcSMatt Macy us_cbdata_t cb; 3064*eda14cbcSMatt Macy us_node_t *node; 3065*eda14cbcSMatt Macy us_node_t *rmnode; 3066*eda14cbcSMatt Macy uu_list_pool_t *listpool; 3067*eda14cbcSMatt Macy uu_list_t *list; 3068*eda14cbcSMatt Macy uu_avl_index_t idx = 0; 3069*eda14cbcSMatt Macy uu_list_index_t idx2 = 0; 3070*eda14cbcSMatt Macy 3071*eda14cbcSMatt Macy if (argc < 2) 3072*eda14cbcSMatt Macy usage(B_FALSE); 3073*eda14cbcSMatt Macy 3074*eda14cbcSMatt Macy if (strcmp(argv[0], "groupspace") == 0) { 3075*eda14cbcSMatt Macy /* Toggle default group types */ 3076*eda14cbcSMatt Macy types = USTYPE_PSX_GRP | USTYPE_SMB_GRP; 3077*eda14cbcSMatt Macy } else if (strcmp(argv[0], "projectspace") == 0) { 3078*eda14cbcSMatt Macy types = USTYPE_PROJ; 3079*eda14cbcSMatt Macy prtnum = B_TRUE; 3080*eda14cbcSMatt Macy } 3081*eda14cbcSMatt Macy 3082*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) { 3083*eda14cbcSMatt Macy switch (c) { 3084*eda14cbcSMatt Macy case 'n': 3085*eda14cbcSMatt Macy if (types == USTYPE_PROJ) { 3086*eda14cbcSMatt Macy (void) fprintf(stderr, 3087*eda14cbcSMatt Macy gettext("invalid option 'n'\n")); 3088*eda14cbcSMatt Macy usage(B_FALSE); 3089*eda14cbcSMatt Macy } 3090*eda14cbcSMatt Macy prtnum = B_TRUE; 3091*eda14cbcSMatt Macy break; 3092*eda14cbcSMatt Macy case 'H': 3093*eda14cbcSMatt Macy scripted = B_TRUE; 3094*eda14cbcSMatt Macy break; 3095*eda14cbcSMatt Macy case 'p': 3096*eda14cbcSMatt Macy parsable = B_TRUE; 3097*eda14cbcSMatt Macy break; 3098*eda14cbcSMatt Macy case 'o': 3099*eda14cbcSMatt Macy ofield = optarg; 3100*eda14cbcSMatt Macy break; 3101*eda14cbcSMatt Macy case 's': 3102*eda14cbcSMatt Macy case 'S': 3103*eda14cbcSMatt Macy if (zfs_add_sort_column(&sortcol, optarg, 3104*eda14cbcSMatt Macy c == 's' ? B_FALSE : B_TRUE) != 0) { 3105*eda14cbcSMatt Macy (void) fprintf(stderr, 3106*eda14cbcSMatt Macy gettext("invalid field '%s'\n"), optarg); 3107*eda14cbcSMatt Macy usage(B_FALSE); 3108*eda14cbcSMatt Macy } 3109*eda14cbcSMatt Macy break; 3110*eda14cbcSMatt Macy case 't': 3111*eda14cbcSMatt Macy if (types == USTYPE_PROJ) { 3112*eda14cbcSMatt Macy (void) fprintf(stderr, 3113*eda14cbcSMatt Macy gettext("invalid option 't'\n")); 3114*eda14cbcSMatt Macy usage(B_FALSE); 3115*eda14cbcSMatt Macy } 3116*eda14cbcSMatt Macy tfield = optarg; 3117*eda14cbcSMatt Macy break; 3118*eda14cbcSMatt Macy case 'i': 3119*eda14cbcSMatt Macy if (types == USTYPE_PROJ) { 3120*eda14cbcSMatt Macy (void) fprintf(stderr, 3121*eda14cbcSMatt Macy gettext("invalid option 'i'\n")); 3122*eda14cbcSMatt Macy usage(B_FALSE); 3123*eda14cbcSMatt Macy } 3124*eda14cbcSMatt Macy sid2posix = B_TRUE; 3125*eda14cbcSMatt Macy break; 3126*eda14cbcSMatt Macy case ':': 3127*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing argument for " 3128*eda14cbcSMatt Macy "'%c' option\n"), optopt); 3129*eda14cbcSMatt Macy usage(B_FALSE); 3130*eda14cbcSMatt Macy break; 3131*eda14cbcSMatt Macy case '?': 3132*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3133*eda14cbcSMatt Macy optopt); 3134*eda14cbcSMatt Macy usage(B_FALSE); 3135*eda14cbcSMatt Macy } 3136*eda14cbcSMatt Macy } 3137*eda14cbcSMatt Macy 3138*eda14cbcSMatt Macy argc -= optind; 3139*eda14cbcSMatt Macy argv += optind; 3140*eda14cbcSMatt Macy 3141*eda14cbcSMatt Macy if (argc < 1) { 3142*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing dataset name\n")); 3143*eda14cbcSMatt Macy usage(B_FALSE); 3144*eda14cbcSMatt Macy } 3145*eda14cbcSMatt Macy if (argc > 1) { 3146*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many arguments\n")); 3147*eda14cbcSMatt Macy usage(B_FALSE); 3148*eda14cbcSMatt Macy } 3149*eda14cbcSMatt Macy 3150*eda14cbcSMatt Macy /* Use default output fields if not specified using -o */ 3151*eda14cbcSMatt Macy if (ofield == NULL) 3152*eda14cbcSMatt Macy ofield = deffields; 3153*eda14cbcSMatt Macy do { 3154*eda14cbcSMatt Macy if ((delim = strchr(ofield, ',')) != NULL) 3155*eda14cbcSMatt Macy *delim = '\0'; 3156*eda14cbcSMatt Macy if ((fields[cfield++] = us_field_index(ofield)) == -1) { 3157*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid type '%s' " 3158*eda14cbcSMatt Macy "for -o option\n"), ofield); 3159*eda14cbcSMatt Macy return (-1); 3160*eda14cbcSMatt Macy } 3161*eda14cbcSMatt Macy if (delim != NULL) 3162*eda14cbcSMatt Macy ofield = delim + 1; 3163*eda14cbcSMatt Macy } while (delim != NULL); 3164*eda14cbcSMatt Macy fields[cfield] = USFIELD_LAST; 3165*eda14cbcSMatt Macy 3166*eda14cbcSMatt Macy /* Override output types (-t option) */ 3167*eda14cbcSMatt Macy if (tfield != NULL) { 3168*eda14cbcSMatt Macy types = 0; 3169*eda14cbcSMatt Macy 3170*eda14cbcSMatt Macy do { 3171*eda14cbcSMatt Macy boolean_t found = B_FALSE; 3172*eda14cbcSMatt Macy 3173*eda14cbcSMatt Macy if ((delim = strchr(tfield, ',')) != NULL) 3174*eda14cbcSMatt Macy *delim = '\0'; 3175*eda14cbcSMatt Macy for (i = 0; i < sizeof (us_type_bits) / sizeof (int); 3176*eda14cbcSMatt Macy i++) { 3177*eda14cbcSMatt Macy if (strcmp(tfield, us_type_names[i]) == 0) { 3178*eda14cbcSMatt Macy found = B_TRUE; 3179*eda14cbcSMatt Macy types |= us_type_bits[i]; 3180*eda14cbcSMatt Macy break; 3181*eda14cbcSMatt Macy } 3182*eda14cbcSMatt Macy } 3183*eda14cbcSMatt Macy if (!found) { 3184*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid type " 3185*eda14cbcSMatt Macy "'%s' for -t option\n"), tfield); 3186*eda14cbcSMatt Macy return (-1); 3187*eda14cbcSMatt Macy } 3188*eda14cbcSMatt Macy if (delim != NULL) 3189*eda14cbcSMatt Macy tfield = delim + 1; 3190*eda14cbcSMatt Macy } while (delim != NULL); 3191*eda14cbcSMatt Macy } 3192*eda14cbcSMatt Macy 3193*eda14cbcSMatt Macy if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | 3194*eda14cbcSMatt Macy ZFS_TYPE_SNAPSHOT)) == NULL) 3195*eda14cbcSMatt Macy return (1); 3196*eda14cbcSMatt Macy if (zhp->zfs_head_type != ZFS_TYPE_FILESYSTEM) { 3197*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("operation is only applicable " 3198*eda14cbcSMatt Macy "to filesystems and their snapshots\n")); 3199*eda14cbcSMatt Macy zfs_close(zhp); 3200*eda14cbcSMatt Macy return (1); 3201*eda14cbcSMatt Macy } 3202*eda14cbcSMatt Macy 3203*eda14cbcSMatt Macy if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t), 3204*eda14cbcSMatt Macy offsetof(us_node_t, usn_avlnode), us_compare, UU_DEFAULT)) == NULL) 3205*eda14cbcSMatt Macy nomem(); 3206*eda14cbcSMatt Macy if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) 3207*eda14cbcSMatt Macy nomem(); 3208*eda14cbcSMatt Macy 3209*eda14cbcSMatt Macy /* Always add default sorting columns */ 3210*eda14cbcSMatt Macy (void) zfs_add_sort_column(&sortcol, "type", B_FALSE); 3211*eda14cbcSMatt Macy (void) zfs_add_sort_column(&sortcol, "name", B_FALSE); 3212*eda14cbcSMatt Macy 3213*eda14cbcSMatt Macy cb.cb_sortcol = sortcol; 3214*eda14cbcSMatt Macy cb.cb_numname = prtnum; 3215*eda14cbcSMatt Macy cb.cb_nicenum = !parsable; 3216*eda14cbcSMatt Macy cb.cb_avl_pool = avl_pool; 3217*eda14cbcSMatt Macy cb.cb_avl = avl_tree; 3218*eda14cbcSMatt Macy cb.cb_sid2posix = sid2posix; 3219*eda14cbcSMatt Macy 3220*eda14cbcSMatt Macy for (i = 0; i < USFIELD_LAST; i++) 3221*eda14cbcSMatt Macy cb.cb_width[i] = strlen(gettext(us_field_hdr[i])); 3222*eda14cbcSMatt Macy 3223*eda14cbcSMatt Macy for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) { 3224*eda14cbcSMatt Macy if ((zfs_prop_is_user(p) && 3225*eda14cbcSMatt Macy !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) || 3226*eda14cbcSMatt Macy (zfs_prop_is_group(p) && 3227*eda14cbcSMatt Macy !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))) || 3228*eda14cbcSMatt Macy (zfs_prop_is_project(p) && types != USTYPE_PROJ)) 3229*eda14cbcSMatt Macy continue; 3230*eda14cbcSMatt Macy 3231*eda14cbcSMatt Macy cb.cb_prop = p; 3232*eda14cbcSMatt Macy if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0) { 3233*eda14cbcSMatt Macy zfs_close(zhp); 3234*eda14cbcSMatt Macy return (ret); 3235*eda14cbcSMatt Macy } 3236*eda14cbcSMatt Macy } 3237*eda14cbcSMatt Macy zfs_close(zhp); 3238*eda14cbcSMatt Macy 3239*eda14cbcSMatt Macy /* Sort the list */ 3240*eda14cbcSMatt Macy if ((node = uu_avl_first(avl_tree)) == NULL) 3241*eda14cbcSMatt Macy return (0); 3242*eda14cbcSMatt Macy 3243*eda14cbcSMatt Macy us_populated = B_TRUE; 3244*eda14cbcSMatt Macy 3245*eda14cbcSMatt Macy listpool = uu_list_pool_create("tmplist", sizeof (us_node_t), 3246*eda14cbcSMatt Macy offsetof(us_node_t, usn_listnode), NULL, UU_DEFAULT); 3247*eda14cbcSMatt Macy list = uu_list_create(listpool, NULL, UU_DEFAULT); 3248*eda14cbcSMatt Macy uu_list_node_init(node, &node->usn_listnode, listpool); 3249*eda14cbcSMatt Macy 3250*eda14cbcSMatt Macy while (node != NULL) { 3251*eda14cbcSMatt Macy rmnode = node; 3252*eda14cbcSMatt Macy node = uu_avl_next(avl_tree, node); 3253*eda14cbcSMatt Macy uu_avl_remove(avl_tree, rmnode); 3254*eda14cbcSMatt Macy if (uu_list_find(list, rmnode, NULL, &idx2) == NULL) 3255*eda14cbcSMatt Macy uu_list_insert(list, rmnode, idx2); 3256*eda14cbcSMatt Macy } 3257*eda14cbcSMatt Macy 3258*eda14cbcSMatt Macy for (node = uu_list_first(list); node != NULL; 3259*eda14cbcSMatt Macy node = uu_list_next(list, node)) { 3260*eda14cbcSMatt Macy us_sort_info_t sortinfo = { sortcol, cb.cb_numname }; 3261*eda14cbcSMatt Macy 3262*eda14cbcSMatt Macy if (uu_avl_find(avl_tree, node, &sortinfo, &idx) == NULL) 3263*eda14cbcSMatt Macy uu_avl_insert(avl_tree, node, idx); 3264*eda14cbcSMatt Macy } 3265*eda14cbcSMatt Macy 3266*eda14cbcSMatt Macy uu_list_destroy(list); 3267*eda14cbcSMatt Macy uu_list_pool_destroy(listpool); 3268*eda14cbcSMatt Macy 3269*eda14cbcSMatt Macy /* Print and free node nvlist memory */ 3270*eda14cbcSMatt Macy print_us(scripted, parsable, fields, types, cb.cb_width, B_TRUE, 3271*eda14cbcSMatt Macy cb.cb_avl); 3272*eda14cbcSMatt Macy 3273*eda14cbcSMatt Macy zfs_free_sort_columns(sortcol); 3274*eda14cbcSMatt Macy 3275*eda14cbcSMatt Macy /* Clean up the AVL tree */ 3276*eda14cbcSMatt Macy if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) 3277*eda14cbcSMatt Macy nomem(); 3278*eda14cbcSMatt Macy 3279*eda14cbcSMatt Macy while ((node = uu_avl_walk_next(walk)) != NULL) { 3280*eda14cbcSMatt Macy uu_avl_remove(cb.cb_avl, node); 3281*eda14cbcSMatt Macy free(node); 3282*eda14cbcSMatt Macy } 3283*eda14cbcSMatt Macy 3284*eda14cbcSMatt Macy uu_avl_walk_end(walk); 3285*eda14cbcSMatt Macy uu_avl_destroy(avl_tree); 3286*eda14cbcSMatt Macy uu_avl_pool_destroy(avl_pool); 3287*eda14cbcSMatt Macy 3288*eda14cbcSMatt Macy return (ret); 3289*eda14cbcSMatt Macy } 3290*eda14cbcSMatt Macy 3291*eda14cbcSMatt Macy /* 3292*eda14cbcSMatt Macy * list [-Hp][-r|-d max] [-o property[,...]] [-s property] ... [-S property] 3293*eda14cbcSMatt Macy * [-t type[,...]] [filesystem|volume|snapshot] ... 3294*eda14cbcSMatt Macy * 3295*eda14cbcSMatt Macy * -H Scripted mode; elide headers and separate columns by tabs 3296*eda14cbcSMatt Macy * -p Display values in parsable (literal) format. 3297*eda14cbcSMatt Macy * -r Recurse over all children 3298*eda14cbcSMatt Macy * -d Limit recursion by depth. 3299*eda14cbcSMatt Macy * -o Control which fields to display. 3300*eda14cbcSMatt Macy * -s Specify sort columns, descending order. 3301*eda14cbcSMatt Macy * -S Specify sort columns, ascending order. 3302*eda14cbcSMatt Macy * -t Control which object types to display. 3303*eda14cbcSMatt Macy * 3304*eda14cbcSMatt Macy * When given no arguments, list all filesystems in the system. 3305*eda14cbcSMatt Macy * Otherwise, list the specified datasets, optionally recursing down them if 3306*eda14cbcSMatt Macy * '-r' is specified. 3307*eda14cbcSMatt Macy */ 3308*eda14cbcSMatt Macy typedef struct list_cbdata { 3309*eda14cbcSMatt Macy boolean_t cb_first; 3310*eda14cbcSMatt Macy boolean_t cb_literal; 3311*eda14cbcSMatt Macy boolean_t cb_scripted; 3312*eda14cbcSMatt Macy zprop_list_t *cb_proplist; 3313*eda14cbcSMatt Macy } list_cbdata_t; 3314*eda14cbcSMatt Macy 3315*eda14cbcSMatt Macy /* 3316*eda14cbcSMatt Macy * Given a list of columns to display, output appropriate headers for each one. 3317*eda14cbcSMatt Macy */ 3318*eda14cbcSMatt Macy static void 3319*eda14cbcSMatt Macy print_header(list_cbdata_t *cb) 3320*eda14cbcSMatt Macy { 3321*eda14cbcSMatt Macy zprop_list_t *pl = cb->cb_proplist; 3322*eda14cbcSMatt Macy char headerbuf[ZFS_MAXPROPLEN]; 3323*eda14cbcSMatt Macy const char *header; 3324*eda14cbcSMatt Macy int i; 3325*eda14cbcSMatt Macy boolean_t first = B_TRUE; 3326*eda14cbcSMatt Macy boolean_t right_justify; 3327*eda14cbcSMatt Macy 3328*eda14cbcSMatt Macy for (; pl != NULL; pl = pl->pl_next) { 3329*eda14cbcSMatt Macy if (!first) { 3330*eda14cbcSMatt Macy (void) printf(" "); 3331*eda14cbcSMatt Macy } else { 3332*eda14cbcSMatt Macy first = B_FALSE; 3333*eda14cbcSMatt Macy } 3334*eda14cbcSMatt Macy 3335*eda14cbcSMatt Macy right_justify = B_FALSE; 3336*eda14cbcSMatt Macy if (pl->pl_prop != ZPROP_INVAL) { 3337*eda14cbcSMatt Macy header = zfs_prop_column_name(pl->pl_prop); 3338*eda14cbcSMatt Macy right_justify = zfs_prop_align_right(pl->pl_prop); 3339*eda14cbcSMatt Macy } else { 3340*eda14cbcSMatt Macy for (i = 0; pl->pl_user_prop[i] != '\0'; i++) 3341*eda14cbcSMatt Macy headerbuf[i] = toupper(pl->pl_user_prop[i]); 3342*eda14cbcSMatt Macy headerbuf[i] = '\0'; 3343*eda14cbcSMatt Macy header = headerbuf; 3344*eda14cbcSMatt Macy } 3345*eda14cbcSMatt Macy 3346*eda14cbcSMatt Macy if (pl->pl_next == NULL && !right_justify) 3347*eda14cbcSMatt Macy (void) printf("%s", header); 3348*eda14cbcSMatt Macy else if (right_justify) 3349*eda14cbcSMatt Macy (void) printf("%*s", (int)pl->pl_width, header); 3350*eda14cbcSMatt Macy else 3351*eda14cbcSMatt Macy (void) printf("%-*s", (int)pl->pl_width, header); 3352*eda14cbcSMatt Macy } 3353*eda14cbcSMatt Macy 3354*eda14cbcSMatt Macy (void) printf("\n"); 3355*eda14cbcSMatt Macy } 3356*eda14cbcSMatt Macy 3357*eda14cbcSMatt Macy /* 3358*eda14cbcSMatt Macy * Given a dataset and a list of fields, print out all the properties according 3359*eda14cbcSMatt Macy * to the described layout. 3360*eda14cbcSMatt Macy */ 3361*eda14cbcSMatt Macy static void 3362*eda14cbcSMatt Macy print_dataset(zfs_handle_t *zhp, list_cbdata_t *cb) 3363*eda14cbcSMatt Macy { 3364*eda14cbcSMatt Macy zprop_list_t *pl = cb->cb_proplist; 3365*eda14cbcSMatt Macy boolean_t first = B_TRUE; 3366*eda14cbcSMatt Macy char property[ZFS_MAXPROPLEN]; 3367*eda14cbcSMatt Macy nvlist_t *userprops = zfs_get_user_props(zhp); 3368*eda14cbcSMatt Macy nvlist_t *propval; 3369*eda14cbcSMatt Macy char *propstr; 3370*eda14cbcSMatt Macy boolean_t right_justify; 3371*eda14cbcSMatt Macy 3372*eda14cbcSMatt Macy for (; pl != NULL; pl = pl->pl_next) { 3373*eda14cbcSMatt Macy if (!first) { 3374*eda14cbcSMatt Macy if (cb->cb_scripted) 3375*eda14cbcSMatt Macy (void) printf("\t"); 3376*eda14cbcSMatt Macy else 3377*eda14cbcSMatt Macy (void) printf(" "); 3378*eda14cbcSMatt Macy } else { 3379*eda14cbcSMatt Macy first = B_FALSE; 3380*eda14cbcSMatt Macy } 3381*eda14cbcSMatt Macy 3382*eda14cbcSMatt Macy if (pl->pl_prop == ZFS_PROP_NAME) { 3383*eda14cbcSMatt Macy (void) strlcpy(property, zfs_get_name(zhp), 3384*eda14cbcSMatt Macy sizeof (property)); 3385*eda14cbcSMatt Macy propstr = property; 3386*eda14cbcSMatt Macy right_justify = zfs_prop_align_right(pl->pl_prop); 3387*eda14cbcSMatt Macy } else if (pl->pl_prop != ZPROP_INVAL) { 3388*eda14cbcSMatt Macy if (zfs_prop_get(zhp, pl->pl_prop, property, 3389*eda14cbcSMatt Macy sizeof (property), NULL, NULL, 0, 3390*eda14cbcSMatt Macy cb->cb_literal) != 0) 3391*eda14cbcSMatt Macy propstr = "-"; 3392*eda14cbcSMatt Macy else 3393*eda14cbcSMatt Macy propstr = property; 3394*eda14cbcSMatt Macy right_justify = zfs_prop_align_right(pl->pl_prop); 3395*eda14cbcSMatt Macy } else if (zfs_prop_userquota(pl->pl_user_prop)) { 3396*eda14cbcSMatt Macy if (zfs_prop_get_userquota(zhp, pl->pl_user_prop, 3397*eda14cbcSMatt Macy property, sizeof (property), cb->cb_literal) != 0) 3398*eda14cbcSMatt Macy propstr = "-"; 3399*eda14cbcSMatt Macy else 3400*eda14cbcSMatt Macy propstr = property; 3401*eda14cbcSMatt Macy right_justify = B_TRUE; 3402*eda14cbcSMatt Macy } else if (zfs_prop_written(pl->pl_user_prop)) { 3403*eda14cbcSMatt Macy if (zfs_prop_get_written(zhp, pl->pl_user_prop, 3404*eda14cbcSMatt Macy property, sizeof (property), cb->cb_literal) != 0) 3405*eda14cbcSMatt Macy propstr = "-"; 3406*eda14cbcSMatt Macy else 3407*eda14cbcSMatt Macy propstr = property; 3408*eda14cbcSMatt Macy right_justify = B_TRUE; 3409*eda14cbcSMatt Macy } else { 3410*eda14cbcSMatt Macy if (nvlist_lookup_nvlist(userprops, 3411*eda14cbcSMatt Macy pl->pl_user_prop, &propval) != 0) 3412*eda14cbcSMatt Macy propstr = "-"; 3413*eda14cbcSMatt Macy else 3414*eda14cbcSMatt Macy verify(nvlist_lookup_string(propval, 3415*eda14cbcSMatt Macy ZPROP_VALUE, &propstr) == 0); 3416*eda14cbcSMatt Macy right_justify = B_FALSE; 3417*eda14cbcSMatt Macy } 3418*eda14cbcSMatt Macy 3419*eda14cbcSMatt Macy /* 3420*eda14cbcSMatt Macy * If this is being called in scripted mode, or if this is the 3421*eda14cbcSMatt Macy * last column and it is left-justified, don't include a width 3422*eda14cbcSMatt Macy * format specifier. 3423*eda14cbcSMatt Macy */ 3424*eda14cbcSMatt Macy if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify)) 3425*eda14cbcSMatt Macy (void) printf("%s", propstr); 3426*eda14cbcSMatt Macy else if (right_justify) 3427*eda14cbcSMatt Macy (void) printf("%*s", (int)pl->pl_width, propstr); 3428*eda14cbcSMatt Macy else 3429*eda14cbcSMatt Macy (void) printf("%-*s", (int)pl->pl_width, propstr); 3430*eda14cbcSMatt Macy } 3431*eda14cbcSMatt Macy 3432*eda14cbcSMatt Macy (void) printf("\n"); 3433*eda14cbcSMatt Macy } 3434*eda14cbcSMatt Macy 3435*eda14cbcSMatt Macy /* 3436*eda14cbcSMatt Macy * Generic callback function to list a dataset or snapshot. 3437*eda14cbcSMatt Macy */ 3438*eda14cbcSMatt Macy static int 3439*eda14cbcSMatt Macy list_callback(zfs_handle_t *zhp, void *data) 3440*eda14cbcSMatt Macy { 3441*eda14cbcSMatt Macy list_cbdata_t *cbp = data; 3442*eda14cbcSMatt Macy 3443*eda14cbcSMatt Macy if (cbp->cb_first) { 3444*eda14cbcSMatt Macy if (!cbp->cb_scripted) 3445*eda14cbcSMatt Macy print_header(cbp); 3446*eda14cbcSMatt Macy cbp->cb_first = B_FALSE; 3447*eda14cbcSMatt Macy } 3448*eda14cbcSMatt Macy 3449*eda14cbcSMatt Macy print_dataset(zhp, cbp); 3450*eda14cbcSMatt Macy 3451*eda14cbcSMatt Macy return (0); 3452*eda14cbcSMatt Macy } 3453*eda14cbcSMatt Macy 3454*eda14cbcSMatt Macy static int 3455*eda14cbcSMatt Macy zfs_do_list(int argc, char **argv) 3456*eda14cbcSMatt Macy { 3457*eda14cbcSMatt Macy int c; 3458*eda14cbcSMatt Macy static char default_fields[] = 3459*eda14cbcSMatt Macy "name,used,available,referenced,mountpoint"; 3460*eda14cbcSMatt Macy int types = ZFS_TYPE_DATASET; 3461*eda14cbcSMatt Macy boolean_t types_specified = B_FALSE; 3462*eda14cbcSMatt Macy char *fields = NULL; 3463*eda14cbcSMatt Macy list_cbdata_t cb = { 0 }; 3464*eda14cbcSMatt Macy char *value; 3465*eda14cbcSMatt Macy int limit = 0; 3466*eda14cbcSMatt Macy int ret = 0; 3467*eda14cbcSMatt Macy zfs_sort_column_t *sortcol = NULL; 3468*eda14cbcSMatt Macy int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS; 3469*eda14cbcSMatt Macy 3470*eda14cbcSMatt Macy /* check options */ 3471*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "HS:d:o:prs:t:")) != -1) { 3472*eda14cbcSMatt Macy switch (c) { 3473*eda14cbcSMatt Macy case 'o': 3474*eda14cbcSMatt Macy fields = optarg; 3475*eda14cbcSMatt Macy break; 3476*eda14cbcSMatt Macy case 'p': 3477*eda14cbcSMatt Macy cb.cb_literal = B_TRUE; 3478*eda14cbcSMatt Macy flags |= ZFS_ITER_LITERAL_PROPS; 3479*eda14cbcSMatt Macy break; 3480*eda14cbcSMatt Macy case 'd': 3481*eda14cbcSMatt Macy limit = parse_depth(optarg, &flags); 3482*eda14cbcSMatt Macy break; 3483*eda14cbcSMatt Macy case 'r': 3484*eda14cbcSMatt Macy flags |= ZFS_ITER_RECURSE; 3485*eda14cbcSMatt Macy break; 3486*eda14cbcSMatt Macy case 'H': 3487*eda14cbcSMatt Macy cb.cb_scripted = B_TRUE; 3488*eda14cbcSMatt Macy break; 3489*eda14cbcSMatt Macy case 's': 3490*eda14cbcSMatt Macy if (zfs_add_sort_column(&sortcol, optarg, 3491*eda14cbcSMatt Macy B_FALSE) != 0) { 3492*eda14cbcSMatt Macy (void) fprintf(stderr, 3493*eda14cbcSMatt Macy gettext("invalid property '%s'\n"), optarg); 3494*eda14cbcSMatt Macy usage(B_FALSE); 3495*eda14cbcSMatt Macy } 3496*eda14cbcSMatt Macy break; 3497*eda14cbcSMatt Macy case 'S': 3498*eda14cbcSMatt Macy if (zfs_add_sort_column(&sortcol, optarg, 3499*eda14cbcSMatt Macy B_TRUE) != 0) { 3500*eda14cbcSMatt Macy (void) fprintf(stderr, 3501*eda14cbcSMatt Macy gettext("invalid property '%s'\n"), optarg); 3502*eda14cbcSMatt Macy usage(B_FALSE); 3503*eda14cbcSMatt Macy } 3504*eda14cbcSMatt Macy break; 3505*eda14cbcSMatt Macy case 't': 3506*eda14cbcSMatt Macy types = 0; 3507*eda14cbcSMatt Macy types_specified = B_TRUE; 3508*eda14cbcSMatt Macy flags &= ~ZFS_ITER_PROP_LISTSNAPS; 3509*eda14cbcSMatt Macy while (*optarg != '\0') { 3510*eda14cbcSMatt Macy static char *type_subopts[] = { "filesystem", 3511*eda14cbcSMatt Macy "volume", "snapshot", "snap", "bookmark", 3512*eda14cbcSMatt Macy "all", NULL }; 3513*eda14cbcSMatt Macy 3514*eda14cbcSMatt Macy switch (getsubopt(&optarg, type_subopts, 3515*eda14cbcSMatt Macy &value)) { 3516*eda14cbcSMatt Macy case 0: 3517*eda14cbcSMatt Macy types |= ZFS_TYPE_FILESYSTEM; 3518*eda14cbcSMatt Macy break; 3519*eda14cbcSMatt Macy case 1: 3520*eda14cbcSMatt Macy types |= ZFS_TYPE_VOLUME; 3521*eda14cbcSMatt Macy break; 3522*eda14cbcSMatt Macy case 2: 3523*eda14cbcSMatt Macy case 3: 3524*eda14cbcSMatt Macy types |= ZFS_TYPE_SNAPSHOT; 3525*eda14cbcSMatt Macy break; 3526*eda14cbcSMatt Macy case 4: 3527*eda14cbcSMatt Macy types |= ZFS_TYPE_BOOKMARK; 3528*eda14cbcSMatt Macy break; 3529*eda14cbcSMatt Macy case 5: 3530*eda14cbcSMatt Macy types = ZFS_TYPE_DATASET | 3531*eda14cbcSMatt Macy ZFS_TYPE_BOOKMARK; 3532*eda14cbcSMatt Macy break; 3533*eda14cbcSMatt Macy default: 3534*eda14cbcSMatt Macy (void) fprintf(stderr, 3535*eda14cbcSMatt Macy gettext("invalid type '%s'\n"), 3536*eda14cbcSMatt Macy value); 3537*eda14cbcSMatt Macy usage(B_FALSE); 3538*eda14cbcSMatt Macy } 3539*eda14cbcSMatt Macy } 3540*eda14cbcSMatt Macy break; 3541*eda14cbcSMatt Macy case ':': 3542*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing argument for " 3543*eda14cbcSMatt Macy "'%c' option\n"), optopt); 3544*eda14cbcSMatt Macy usage(B_FALSE); 3545*eda14cbcSMatt Macy break; 3546*eda14cbcSMatt Macy case '?': 3547*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3548*eda14cbcSMatt Macy optopt); 3549*eda14cbcSMatt Macy usage(B_FALSE); 3550*eda14cbcSMatt Macy } 3551*eda14cbcSMatt Macy } 3552*eda14cbcSMatt Macy 3553*eda14cbcSMatt Macy argc -= optind; 3554*eda14cbcSMatt Macy argv += optind; 3555*eda14cbcSMatt Macy 3556*eda14cbcSMatt Macy if (fields == NULL) 3557*eda14cbcSMatt Macy fields = default_fields; 3558*eda14cbcSMatt Macy 3559*eda14cbcSMatt Macy /* 3560*eda14cbcSMatt Macy * If we are only going to list snapshot names and sort by name, 3561*eda14cbcSMatt Macy * then we can use faster version. 3562*eda14cbcSMatt Macy */ 3563*eda14cbcSMatt Macy if (strcmp(fields, "name") == 0 && zfs_sort_only_by_name(sortcol)) 3564*eda14cbcSMatt Macy flags |= ZFS_ITER_SIMPLE; 3565*eda14cbcSMatt Macy 3566*eda14cbcSMatt Macy /* 3567*eda14cbcSMatt Macy * If "-o space" and no types were specified, don't display snapshots. 3568*eda14cbcSMatt Macy */ 3569*eda14cbcSMatt Macy if (strcmp(fields, "space") == 0 && types_specified == B_FALSE) 3570*eda14cbcSMatt Macy types &= ~ZFS_TYPE_SNAPSHOT; 3571*eda14cbcSMatt Macy 3572*eda14cbcSMatt Macy /* 3573*eda14cbcSMatt Macy * Handle users who want to list all snapshots or bookmarks 3574*eda14cbcSMatt Macy * of the current dataset (ex. 'zfs list -t snapshot <dataset>'). 3575*eda14cbcSMatt Macy */ 3576*eda14cbcSMatt Macy if ((types == ZFS_TYPE_SNAPSHOT || types == ZFS_TYPE_BOOKMARK) && 3577*eda14cbcSMatt Macy argc > 0 && (flags & ZFS_ITER_RECURSE) == 0 && limit == 0) { 3578*eda14cbcSMatt Macy flags |= (ZFS_ITER_DEPTH_LIMIT | ZFS_ITER_RECURSE); 3579*eda14cbcSMatt Macy limit = 1; 3580*eda14cbcSMatt Macy } 3581*eda14cbcSMatt Macy 3582*eda14cbcSMatt Macy /* 3583*eda14cbcSMatt Macy * If the user specifies '-o all', the zprop_get_list() doesn't 3584*eda14cbcSMatt Macy * normally include the name of the dataset. For 'zfs list', we always 3585*eda14cbcSMatt Macy * want this property to be first. 3586*eda14cbcSMatt Macy */ 3587*eda14cbcSMatt Macy if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET) 3588*eda14cbcSMatt Macy != 0) 3589*eda14cbcSMatt Macy usage(B_FALSE); 3590*eda14cbcSMatt Macy 3591*eda14cbcSMatt Macy cb.cb_first = B_TRUE; 3592*eda14cbcSMatt Macy 3593*eda14cbcSMatt Macy ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist, 3594*eda14cbcSMatt Macy limit, list_callback, &cb); 3595*eda14cbcSMatt Macy 3596*eda14cbcSMatt Macy zprop_free_list(cb.cb_proplist); 3597*eda14cbcSMatt Macy zfs_free_sort_columns(sortcol); 3598*eda14cbcSMatt Macy 3599*eda14cbcSMatt Macy if (ret == 0 && cb.cb_first && !cb.cb_scripted) 3600*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("no datasets available\n")); 3601*eda14cbcSMatt Macy 3602*eda14cbcSMatt Macy return (ret); 3603*eda14cbcSMatt Macy } 3604*eda14cbcSMatt Macy 3605*eda14cbcSMatt Macy /* 3606*eda14cbcSMatt Macy * zfs rename [-f] <fs | snap | vol> <fs | snap | vol> 3607*eda14cbcSMatt Macy * zfs rename [-f] -p <fs | vol> <fs | vol> 3608*eda14cbcSMatt Macy * zfs rename -r <snap> <snap> 3609*eda14cbcSMatt Macy * 3610*eda14cbcSMatt Macy * Renames the given dataset to another of the same type. 3611*eda14cbcSMatt Macy * 3612*eda14cbcSMatt Macy * The '-p' flag creates all the non-existing ancestors of the target first. 3613*eda14cbcSMatt Macy */ 3614*eda14cbcSMatt Macy /* ARGSUSED */ 3615*eda14cbcSMatt Macy static int 3616*eda14cbcSMatt Macy zfs_do_rename(int argc, char **argv) 3617*eda14cbcSMatt Macy { 3618*eda14cbcSMatt Macy zfs_handle_t *zhp; 3619*eda14cbcSMatt Macy int c; 3620*eda14cbcSMatt Macy int ret = 0; 3621*eda14cbcSMatt Macy boolean_t recurse = B_FALSE; 3622*eda14cbcSMatt Macy boolean_t parents = B_FALSE; 3623*eda14cbcSMatt Macy boolean_t force_unmount = B_FALSE; 3624*eda14cbcSMatt Macy 3625*eda14cbcSMatt Macy /* check options */ 3626*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "prf")) != -1) { 3627*eda14cbcSMatt Macy switch (c) { 3628*eda14cbcSMatt Macy case 'p': 3629*eda14cbcSMatt Macy parents = B_TRUE; 3630*eda14cbcSMatt Macy break; 3631*eda14cbcSMatt Macy case 'r': 3632*eda14cbcSMatt Macy recurse = B_TRUE; 3633*eda14cbcSMatt Macy break; 3634*eda14cbcSMatt Macy case 'f': 3635*eda14cbcSMatt Macy force_unmount = B_TRUE; 3636*eda14cbcSMatt Macy break; 3637*eda14cbcSMatt Macy case '?': 3638*eda14cbcSMatt Macy default: 3639*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3640*eda14cbcSMatt Macy optopt); 3641*eda14cbcSMatt Macy usage(B_FALSE); 3642*eda14cbcSMatt Macy } 3643*eda14cbcSMatt Macy } 3644*eda14cbcSMatt Macy 3645*eda14cbcSMatt Macy argc -= optind; 3646*eda14cbcSMatt Macy argv += optind; 3647*eda14cbcSMatt Macy 3648*eda14cbcSMatt Macy /* check number of arguments */ 3649*eda14cbcSMatt Macy if (argc < 1) { 3650*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing source dataset " 3651*eda14cbcSMatt Macy "argument\n")); 3652*eda14cbcSMatt Macy usage(B_FALSE); 3653*eda14cbcSMatt Macy } 3654*eda14cbcSMatt Macy if (argc < 2) { 3655*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing target dataset " 3656*eda14cbcSMatt Macy "argument\n")); 3657*eda14cbcSMatt Macy usage(B_FALSE); 3658*eda14cbcSMatt Macy } 3659*eda14cbcSMatt Macy if (argc > 2) { 3660*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many arguments\n")); 3661*eda14cbcSMatt Macy usage(B_FALSE); 3662*eda14cbcSMatt Macy } 3663*eda14cbcSMatt Macy 3664*eda14cbcSMatt Macy if (recurse && parents) { 3665*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("-p and -r options are mutually " 3666*eda14cbcSMatt Macy "exclusive\n")); 3667*eda14cbcSMatt Macy usage(B_FALSE); 3668*eda14cbcSMatt Macy } 3669*eda14cbcSMatt Macy 3670*eda14cbcSMatt Macy if (recurse && strchr(argv[0], '@') == 0) { 3671*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("source dataset for recursive " 3672*eda14cbcSMatt Macy "rename must be a snapshot\n")); 3673*eda14cbcSMatt Macy usage(B_FALSE); 3674*eda14cbcSMatt Macy } 3675*eda14cbcSMatt Macy 3676*eda14cbcSMatt Macy if ((zhp = zfs_open(g_zfs, argv[0], parents ? ZFS_TYPE_FILESYSTEM | 3677*eda14cbcSMatt Macy ZFS_TYPE_VOLUME : ZFS_TYPE_DATASET)) == NULL) 3678*eda14cbcSMatt Macy return (1); 3679*eda14cbcSMatt Macy 3680*eda14cbcSMatt Macy /* If we were asked and the name looks good, try to create ancestors. */ 3681*eda14cbcSMatt Macy if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) && 3682*eda14cbcSMatt Macy zfs_create_ancestors(g_zfs, argv[1]) != 0) { 3683*eda14cbcSMatt Macy zfs_close(zhp); 3684*eda14cbcSMatt Macy return (1); 3685*eda14cbcSMatt Macy } 3686*eda14cbcSMatt Macy 3687*eda14cbcSMatt Macy ret = (zfs_rename(zhp, argv[1], recurse, force_unmount) != 0); 3688*eda14cbcSMatt Macy 3689*eda14cbcSMatt Macy zfs_close(zhp); 3690*eda14cbcSMatt Macy return (ret); 3691*eda14cbcSMatt Macy } 3692*eda14cbcSMatt Macy 3693*eda14cbcSMatt Macy /* 3694*eda14cbcSMatt Macy * zfs promote <fs> 3695*eda14cbcSMatt Macy * 3696*eda14cbcSMatt Macy * Promotes the given clone fs to be the parent 3697*eda14cbcSMatt Macy */ 3698*eda14cbcSMatt Macy /* ARGSUSED */ 3699*eda14cbcSMatt Macy static int 3700*eda14cbcSMatt Macy zfs_do_promote(int argc, char **argv) 3701*eda14cbcSMatt Macy { 3702*eda14cbcSMatt Macy zfs_handle_t *zhp; 3703*eda14cbcSMatt Macy int ret = 0; 3704*eda14cbcSMatt Macy 3705*eda14cbcSMatt Macy /* check options */ 3706*eda14cbcSMatt Macy if (argc > 1 && argv[1][0] == '-') { 3707*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3708*eda14cbcSMatt Macy argv[1][1]); 3709*eda14cbcSMatt Macy usage(B_FALSE); 3710*eda14cbcSMatt Macy } 3711*eda14cbcSMatt Macy 3712*eda14cbcSMatt Macy /* check number of arguments */ 3713*eda14cbcSMatt Macy if (argc < 2) { 3714*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing clone filesystem" 3715*eda14cbcSMatt Macy " argument\n")); 3716*eda14cbcSMatt Macy usage(B_FALSE); 3717*eda14cbcSMatt Macy } 3718*eda14cbcSMatt Macy if (argc > 2) { 3719*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many arguments\n")); 3720*eda14cbcSMatt Macy usage(B_FALSE); 3721*eda14cbcSMatt Macy } 3722*eda14cbcSMatt Macy 3723*eda14cbcSMatt Macy zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 3724*eda14cbcSMatt Macy if (zhp == NULL) 3725*eda14cbcSMatt Macy return (1); 3726*eda14cbcSMatt Macy 3727*eda14cbcSMatt Macy ret = (zfs_promote(zhp) != 0); 3728*eda14cbcSMatt Macy 3729*eda14cbcSMatt Macy 3730*eda14cbcSMatt Macy zfs_close(zhp); 3731*eda14cbcSMatt Macy return (ret); 3732*eda14cbcSMatt Macy } 3733*eda14cbcSMatt Macy 3734*eda14cbcSMatt Macy static int 3735*eda14cbcSMatt Macy zfs_do_redact(int argc, char **argv) 3736*eda14cbcSMatt Macy { 3737*eda14cbcSMatt Macy char *snap = NULL; 3738*eda14cbcSMatt Macy char *bookname = NULL; 3739*eda14cbcSMatt Macy char **rsnaps = NULL; 3740*eda14cbcSMatt Macy int numrsnaps = 0; 3741*eda14cbcSMatt Macy argv++; 3742*eda14cbcSMatt Macy argc--; 3743*eda14cbcSMatt Macy if (argc < 3) { 3744*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too few arguments\n")); 3745*eda14cbcSMatt Macy usage(B_FALSE); 3746*eda14cbcSMatt Macy } 3747*eda14cbcSMatt Macy 3748*eda14cbcSMatt Macy snap = argv[0]; 3749*eda14cbcSMatt Macy bookname = argv[1]; 3750*eda14cbcSMatt Macy rsnaps = argv + 2; 3751*eda14cbcSMatt Macy numrsnaps = argc - 2; 3752*eda14cbcSMatt Macy 3753*eda14cbcSMatt Macy nvlist_t *rsnapnv = fnvlist_alloc(); 3754*eda14cbcSMatt Macy 3755*eda14cbcSMatt Macy for (int i = 0; i < numrsnaps; i++) { 3756*eda14cbcSMatt Macy fnvlist_add_boolean(rsnapnv, rsnaps[i]); 3757*eda14cbcSMatt Macy } 3758*eda14cbcSMatt Macy 3759*eda14cbcSMatt Macy int err = lzc_redact(snap, bookname, rsnapnv); 3760*eda14cbcSMatt Macy fnvlist_free(rsnapnv); 3761*eda14cbcSMatt Macy 3762*eda14cbcSMatt Macy switch (err) { 3763*eda14cbcSMatt Macy case 0: 3764*eda14cbcSMatt Macy break; 3765*eda14cbcSMatt Macy case ENOENT: 3766*eda14cbcSMatt Macy (void) fprintf(stderr, 3767*eda14cbcSMatt Macy gettext("provided snapshot %s does not exist\n"), snap); 3768*eda14cbcSMatt Macy break; 3769*eda14cbcSMatt Macy case EEXIST: 3770*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("specified redaction bookmark " 3771*eda14cbcSMatt Macy "(%s) provided already exists\n"), bookname); 3772*eda14cbcSMatt Macy break; 3773*eda14cbcSMatt Macy case ENAMETOOLONG: 3774*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("provided bookmark name cannot " 3775*eda14cbcSMatt Macy "be used, final name would be too long\n")); 3776*eda14cbcSMatt Macy break; 3777*eda14cbcSMatt Macy case E2BIG: 3778*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many redaction snapshots " 3779*eda14cbcSMatt Macy "specified\n")); 3780*eda14cbcSMatt Macy break; 3781*eda14cbcSMatt Macy case EINVAL: 3782*eda14cbcSMatt Macy if (strchr(bookname, '#') != NULL) 3783*eda14cbcSMatt Macy (void) fprintf(stderr, gettext( 3784*eda14cbcSMatt Macy "redaction bookmark name must not contain '#'\n")); 3785*eda14cbcSMatt Macy else 3786*eda14cbcSMatt Macy (void) fprintf(stderr, gettext( 3787*eda14cbcSMatt Macy "redaction snapshot must be descendent of " 3788*eda14cbcSMatt Macy "snapshot being redacted\n")); 3789*eda14cbcSMatt Macy break; 3790*eda14cbcSMatt Macy case EALREADY: 3791*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("attempted to redact redacted " 3792*eda14cbcSMatt Macy "dataset or with respect to redacted dataset\n")); 3793*eda14cbcSMatt Macy break; 3794*eda14cbcSMatt Macy case ENOTSUP: 3795*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("redaction bookmarks feature " 3796*eda14cbcSMatt Macy "not enabled\n")); 3797*eda14cbcSMatt Macy break; 3798*eda14cbcSMatt Macy case EXDEV: 3799*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("potentially invalid redaction " 3800*eda14cbcSMatt Macy "snapshot; full dataset names required\n")); 3801*eda14cbcSMatt Macy break; 3802*eda14cbcSMatt Macy default: 3803*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("internal error: %s\n"), 3804*eda14cbcSMatt Macy strerror(errno)); 3805*eda14cbcSMatt Macy } 3806*eda14cbcSMatt Macy 3807*eda14cbcSMatt Macy return (err); 3808*eda14cbcSMatt Macy } 3809*eda14cbcSMatt Macy 3810*eda14cbcSMatt Macy /* 3811*eda14cbcSMatt Macy * zfs rollback [-rRf] <snapshot> 3812*eda14cbcSMatt Macy * 3813*eda14cbcSMatt Macy * -r Delete any intervening snapshots before doing rollback 3814*eda14cbcSMatt Macy * -R Delete any snapshots and their clones 3815*eda14cbcSMatt Macy * -f ignored for backwards compatibility 3816*eda14cbcSMatt Macy * 3817*eda14cbcSMatt Macy * Given a filesystem, rollback to a specific snapshot, discarding any changes 3818*eda14cbcSMatt Macy * since then and making it the active dataset. If more recent snapshots exist, 3819*eda14cbcSMatt Macy * the command will complain unless the '-r' flag is given. 3820*eda14cbcSMatt Macy */ 3821*eda14cbcSMatt Macy typedef struct rollback_cbdata { 3822*eda14cbcSMatt Macy uint64_t cb_create; 3823*eda14cbcSMatt Macy uint8_t cb_younger_ds_printed; 3824*eda14cbcSMatt Macy boolean_t cb_first; 3825*eda14cbcSMatt Macy int cb_doclones; 3826*eda14cbcSMatt Macy char *cb_target; 3827*eda14cbcSMatt Macy int cb_error; 3828*eda14cbcSMatt Macy boolean_t cb_recurse; 3829*eda14cbcSMatt Macy } rollback_cbdata_t; 3830*eda14cbcSMatt Macy 3831*eda14cbcSMatt Macy static int 3832*eda14cbcSMatt Macy rollback_check_dependent(zfs_handle_t *zhp, void *data) 3833*eda14cbcSMatt Macy { 3834*eda14cbcSMatt Macy rollback_cbdata_t *cbp = data; 3835*eda14cbcSMatt Macy 3836*eda14cbcSMatt Macy if (cbp->cb_first && cbp->cb_recurse) { 3837*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot rollback to " 3838*eda14cbcSMatt Macy "'%s': clones of previous snapshots exist\n"), 3839*eda14cbcSMatt Macy cbp->cb_target); 3840*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("use '-R' to " 3841*eda14cbcSMatt Macy "force deletion of the following clones and " 3842*eda14cbcSMatt Macy "dependents:\n")); 3843*eda14cbcSMatt Macy cbp->cb_first = 0; 3844*eda14cbcSMatt Macy cbp->cb_error = 1; 3845*eda14cbcSMatt Macy } 3846*eda14cbcSMatt Macy 3847*eda14cbcSMatt Macy (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 3848*eda14cbcSMatt Macy 3849*eda14cbcSMatt Macy zfs_close(zhp); 3850*eda14cbcSMatt Macy return (0); 3851*eda14cbcSMatt Macy } 3852*eda14cbcSMatt Macy 3853*eda14cbcSMatt Macy 3854*eda14cbcSMatt Macy /* 3855*eda14cbcSMatt Macy * Report some snapshots/bookmarks more recent than the one specified. 3856*eda14cbcSMatt Macy * Used when '-r' is not specified. We reuse this same callback for the 3857*eda14cbcSMatt Macy * snapshot dependents - if 'cb_dependent' is set, then this is a 3858*eda14cbcSMatt Macy * dependent and we should report it without checking the transaction group. 3859*eda14cbcSMatt Macy */ 3860*eda14cbcSMatt Macy static int 3861*eda14cbcSMatt Macy rollback_check(zfs_handle_t *zhp, void *data) 3862*eda14cbcSMatt Macy { 3863*eda14cbcSMatt Macy rollback_cbdata_t *cbp = data; 3864*eda14cbcSMatt Macy /* 3865*eda14cbcSMatt Macy * Max number of younger snapshots and/or bookmarks to display before 3866*eda14cbcSMatt Macy * we stop the iteration. 3867*eda14cbcSMatt Macy */ 3868*eda14cbcSMatt Macy const uint8_t max_younger = 32; 3869*eda14cbcSMatt Macy 3870*eda14cbcSMatt Macy if (cbp->cb_doclones) { 3871*eda14cbcSMatt Macy zfs_close(zhp); 3872*eda14cbcSMatt Macy return (0); 3873*eda14cbcSMatt Macy } 3874*eda14cbcSMatt Macy 3875*eda14cbcSMatt Macy if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) { 3876*eda14cbcSMatt Macy if (cbp->cb_first && !cbp->cb_recurse) { 3877*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot " 3878*eda14cbcSMatt Macy "rollback to '%s': more recent snapshots " 3879*eda14cbcSMatt Macy "or bookmarks exist\n"), 3880*eda14cbcSMatt Macy cbp->cb_target); 3881*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("use '-r' to " 3882*eda14cbcSMatt Macy "force deletion of the following " 3883*eda14cbcSMatt Macy "snapshots and bookmarks:\n")); 3884*eda14cbcSMatt Macy cbp->cb_first = 0; 3885*eda14cbcSMatt Macy cbp->cb_error = 1; 3886*eda14cbcSMatt Macy } 3887*eda14cbcSMatt Macy 3888*eda14cbcSMatt Macy if (cbp->cb_recurse) { 3889*eda14cbcSMatt Macy if (zfs_iter_dependents(zhp, B_TRUE, 3890*eda14cbcSMatt Macy rollback_check_dependent, cbp) != 0) { 3891*eda14cbcSMatt Macy zfs_close(zhp); 3892*eda14cbcSMatt Macy return (-1); 3893*eda14cbcSMatt Macy } 3894*eda14cbcSMatt Macy } else { 3895*eda14cbcSMatt Macy (void) fprintf(stderr, "%s\n", 3896*eda14cbcSMatt Macy zfs_get_name(zhp)); 3897*eda14cbcSMatt Macy cbp->cb_younger_ds_printed++; 3898*eda14cbcSMatt Macy } 3899*eda14cbcSMatt Macy } 3900*eda14cbcSMatt Macy zfs_close(zhp); 3901*eda14cbcSMatt Macy 3902*eda14cbcSMatt Macy if (cbp->cb_younger_ds_printed == max_younger) { 3903*eda14cbcSMatt Macy /* 3904*eda14cbcSMatt Macy * This non-recursive rollback is going to fail due to the 3905*eda14cbcSMatt Macy * presence of snapshots and/or bookmarks that are younger than 3906*eda14cbcSMatt Macy * the rollback target. 3907*eda14cbcSMatt Macy * We printed some of the offending objects, now we stop 3908*eda14cbcSMatt Macy * zfs_iter_snapshot/bookmark iteration so we can fail fast and 3909*eda14cbcSMatt Macy * avoid iterating over the rest of the younger objects 3910*eda14cbcSMatt Macy */ 3911*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("Output limited to %d " 3912*eda14cbcSMatt Macy "snapshots/bookmarks\n"), max_younger); 3913*eda14cbcSMatt Macy return (-1); 3914*eda14cbcSMatt Macy } 3915*eda14cbcSMatt Macy return (0); 3916*eda14cbcSMatt Macy } 3917*eda14cbcSMatt Macy 3918*eda14cbcSMatt Macy static int 3919*eda14cbcSMatt Macy zfs_do_rollback(int argc, char **argv) 3920*eda14cbcSMatt Macy { 3921*eda14cbcSMatt Macy int ret = 0; 3922*eda14cbcSMatt Macy int c; 3923*eda14cbcSMatt Macy boolean_t force = B_FALSE; 3924*eda14cbcSMatt Macy rollback_cbdata_t cb = { 0 }; 3925*eda14cbcSMatt Macy zfs_handle_t *zhp, *snap; 3926*eda14cbcSMatt Macy char parentname[ZFS_MAX_DATASET_NAME_LEN]; 3927*eda14cbcSMatt Macy char *delim; 3928*eda14cbcSMatt Macy uint64_t min_txg = 0; 3929*eda14cbcSMatt Macy 3930*eda14cbcSMatt Macy /* check options */ 3931*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "rRf")) != -1) { 3932*eda14cbcSMatt Macy switch (c) { 3933*eda14cbcSMatt Macy case 'r': 3934*eda14cbcSMatt Macy cb.cb_recurse = 1; 3935*eda14cbcSMatt Macy break; 3936*eda14cbcSMatt Macy case 'R': 3937*eda14cbcSMatt Macy cb.cb_recurse = 1; 3938*eda14cbcSMatt Macy cb.cb_doclones = 1; 3939*eda14cbcSMatt Macy break; 3940*eda14cbcSMatt Macy case 'f': 3941*eda14cbcSMatt Macy force = B_TRUE; 3942*eda14cbcSMatt Macy break; 3943*eda14cbcSMatt Macy case '?': 3944*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3945*eda14cbcSMatt Macy optopt); 3946*eda14cbcSMatt Macy usage(B_FALSE); 3947*eda14cbcSMatt Macy } 3948*eda14cbcSMatt Macy } 3949*eda14cbcSMatt Macy 3950*eda14cbcSMatt Macy argc -= optind; 3951*eda14cbcSMatt Macy argv += optind; 3952*eda14cbcSMatt Macy 3953*eda14cbcSMatt Macy /* check number of arguments */ 3954*eda14cbcSMatt Macy if (argc < 1) { 3955*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing dataset argument\n")); 3956*eda14cbcSMatt Macy usage(B_FALSE); 3957*eda14cbcSMatt Macy } 3958*eda14cbcSMatt Macy if (argc > 1) { 3959*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many arguments\n")); 3960*eda14cbcSMatt Macy usage(B_FALSE); 3961*eda14cbcSMatt Macy } 3962*eda14cbcSMatt Macy 3963*eda14cbcSMatt Macy /* open the snapshot */ 3964*eda14cbcSMatt Macy if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 3965*eda14cbcSMatt Macy return (1); 3966*eda14cbcSMatt Macy 3967*eda14cbcSMatt Macy /* open the parent dataset */ 3968*eda14cbcSMatt Macy (void) strlcpy(parentname, argv[0], sizeof (parentname)); 3969*eda14cbcSMatt Macy verify((delim = strrchr(parentname, '@')) != NULL); 3970*eda14cbcSMatt Macy *delim = '\0'; 3971*eda14cbcSMatt Macy if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_DATASET)) == NULL) { 3972*eda14cbcSMatt Macy zfs_close(snap); 3973*eda14cbcSMatt Macy return (1); 3974*eda14cbcSMatt Macy } 3975*eda14cbcSMatt Macy 3976*eda14cbcSMatt Macy /* 3977*eda14cbcSMatt Macy * Check for more recent snapshots and/or clones based on the presence 3978*eda14cbcSMatt Macy * of '-r' and '-R'. 3979*eda14cbcSMatt Macy */ 3980*eda14cbcSMatt Macy cb.cb_target = argv[0]; 3981*eda14cbcSMatt Macy cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 3982*eda14cbcSMatt Macy cb.cb_first = B_TRUE; 3983*eda14cbcSMatt Macy cb.cb_error = 0; 3984*eda14cbcSMatt Macy 3985*eda14cbcSMatt Macy if (cb.cb_create > 0) 3986*eda14cbcSMatt Macy min_txg = cb.cb_create; 3987*eda14cbcSMatt Macy 3988*eda14cbcSMatt Macy if ((ret = zfs_iter_snapshots(zhp, B_FALSE, rollback_check, &cb, 3989*eda14cbcSMatt Macy min_txg, 0)) != 0) 3990*eda14cbcSMatt Macy goto out; 3991*eda14cbcSMatt Macy if ((ret = zfs_iter_bookmarks(zhp, rollback_check, &cb)) != 0) 3992*eda14cbcSMatt Macy goto out; 3993*eda14cbcSMatt Macy 3994*eda14cbcSMatt Macy if ((ret = cb.cb_error) != 0) 3995*eda14cbcSMatt Macy goto out; 3996*eda14cbcSMatt Macy 3997*eda14cbcSMatt Macy /* 3998*eda14cbcSMatt Macy * Rollback parent to the given snapshot. 3999*eda14cbcSMatt Macy */ 4000*eda14cbcSMatt Macy ret = zfs_rollback(zhp, snap, force); 4001*eda14cbcSMatt Macy 4002*eda14cbcSMatt Macy out: 4003*eda14cbcSMatt Macy zfs_close(snap); 4004*eda14cbcSMatt Macy zfs_close(zhp); 4005*eda14cbcSMatt Macy 4006*eda14cbcSMatt Macy if (ret == 0) 4007*eda14cbcSMatt Macy return (0); 4008*eda14cbcSMatt Macy else 4009*eda14cbcSMatt Macy return (1); 4010*eda14cbcSMatt Macy } 4011*eda14cbcSMatt Macy 4012*eda14cbcSMatt Macy /* 4013*eda14cbcSMatt Macy * zfs set property=value ... { fs | snap | vol } ... 4014*eda14cbcSMatt Macy * 4015*eda14cbcSMatt Macy * Sets the given properties for all datasets specified on the command line. 4016*eda14cbcSMatt Macy */ 4017*eda14cbcSMatt Macy 4018*eda14cbcSMatt Macy static int 4019*eda14cbcSMatt Macy set_callback(zfs_handle_t *zhp, void *data) 4020*eda14cbcSMatt Macy { 4021*eda14cbcSMatt Macy nvlist_t *props = data; 4022*eda14cbcSMatt Macy 4023*eda14cbcSMatt Macy if (zfs_prop_set_list(zhp, props) != 0) { 4024*eda14cbcSMatt Macy switch (libzfs_errno(g_zfs)) { 4025*eda14cbcSMatt Macy case EZFS_MOUNTFAILED: 4026*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("property may be set " 4027*eda14cbcSMatt Macy "but unable to remount filesystem\n")); 4028*eda14cbcSMatt Macy break; 4029*eda14cbcSMatt Macy case EZFS_SHARENFSFAILED: 4030*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("property may be set " 4031*eda14cbcSMatt Macy "but unable to reshare filesystem\n")); 4032*eda14cbcSMatt Macy break; 4033*eda14cbcSMatt Macy } 4034*eda14cbcSMatt Macy return (1); 4035*eda14cbcSMatt Macy } 4036*eda14cbcSMatt Macy return (0); 4037*eda14cbcSMatt Macy } 4038*eda14cbcSMatt Macy 4039*eda14cbcSMatt Macy static int 4040*eda14cbcSMatt Macy zfs_do_set(int argc, char **argv) 4041*eda14cbcSMatt Macy { 4042*eda14cbcSMatt Macy nvlist_t *props = NULL; 4043*eda14cbcSMatt Macy int ds_start = -1; /* argv idx of first dataset arg */ 4044*eda14cbcSMatt Macy int ret = 0; 4045*eda14cbcSMatt Macy int i; 4046*eda14cbcSMatt Macy 4047*eda14cbcSMatt Macy /* check for options */ 4048*eda14cbcSMatt Macy if (argc > 1 && argv[1][0] == '-') { 4049*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 4050*eda14cbcSMatt Macy argv[1][1]); 4051*eda14cbcSMatt Macy usage(B_FALSE); 4052*eda14cbcSMatt Macy } 4053*eda14cbcSMatt Macy 4054*eda14cbcSMatt Macy /* check number of arguments */ 4055*eda14cbcSMatt Macy if (argc < 2) { 4056*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing arguments\n")); 4057*eda14cbcSMatt Macy usage(B_FALSE); 4058*eda14cbcSMatt Macy } 4059*eda14cbcSMatt Macy if (argc < 3) { 4060*eda14cbcSMatt Macy if (strchr(argv[1], '=') == NULL) { 4061*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing property=value " 4062*eda14cbcSMatt Macy "argument(s)\n")); 4063*eda14cbcSMatt Macy } else { 4064*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing dataset " 4065*eda14cbcSMatt Macy "name(s)\n")); 4066*eda14cbcSMatt Macy } 4067*eda14cbcSMatt Macy usage(B_FALSE); 4068*eda14cbcSMatt Macy } 4069*eda14cbcSMatt Macy 4070*eda14cbcSMatt Macy /* validate argument order: prop=val args followed by dataset args */ 4071*eda14cbcSMatt Macy for (i = 1; i < argc; i++) { 4072*eda14cbcSMatt Macy if (strchr(argv[i], '=') != NULL) { 4073*eda14cbcSMatt Macy if (ds_start > 0) { 4074*eda14cbcSMatt Macy /* out-of-order prop=val argument */ 4075*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid " 4076*eda14cbcSMatt Macy "argument order\n")); 4077*eda14cbcSMatt Macy usage(B_FALSE); 4078*eda14cbcSMatt Macy } 4079*eda14cbcSMatt Macy } else if (ds_start < 0) { 4080*eda14cbcSMatt Macy ds_start = i; 4081*eda14cbcSMatt Macy } 4082*eda14cbcSMatt Macy } 4083*eda14cbcSMatt Macy if (ds_start < 0) { 4084*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing dataset name(s)\n")); 4085*eda14cbcSMatt Macy usage(B_FALSE); 4086*eda14cbcSMatt Macy } 4087*eda14cbcSMatt Macy 4088*eda14cbcSMatt Macy /* Populate a list of property settings */ 4089*eda14cbcSMatt Macy if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 4090*eda14cbcSMatt Macy nomem(); 4091*eda14cbcSMatt Macy for (i = 1; i < ds_start; i++) { 4092*eda14cbcSMatt Macy if (!parseprop(props, argv[i])) { 4093*eda14cbcSMatt Macy ret = -1; 4094*eda14cbcSMatt Macy goto error; 4095*eda14cbcSMatt Macy } 4096*eda14cbcSMatt Macy } 4097*eda14cbcSMatt Macy 4098*eda14cbcSMatt Macy ret = zfs_for_each(argc - ds_start, argv + ds_start, 0, 4099*eda14cbcSMatt Macy ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, props); 4100*eda14cbcSMatt Macy 4101*eda14cbcSMatt Macy error: 4102*eda14cbcSMatt Macy nvlist_free(props); 4103*eda14cbcSMatt Macy return (ret); 4104*eda14cbcSMatt Macy } 4105*eda14cbcSMatt Macy 4106*eda14cbcSMatt Macy typedef struct snap_cbdata { 4107*eda14cbcSMatt Macy nvlist_t *sd_nvl; 4108*eda14cbcSMatt Macy boolean_t sd_recursive; 4109*eda14cbcSMatt Macy const char *sd_snapname; 4110*eda14cbcSMatt Macy } snap_cbdata_t; 4111*eda14cbcSMatt Macy 4112*eda14cbcSMatt Macy static int 4113*eda14cbcSMatt Macy zfs_snapshot_cb(zfs_handle_t *zhp, void *arg) 4114*eda14cbcSMatt Macy { 4115*eda14cbcSMatt Macy snap_cbdata_t *sd = arg; 4116*eda14cbcSMatt Macy char *name; 4117*eda14cbcSMatt Macy int rv = 0; 4118*eda14cbcSMatt Macy int error; 4119*eda14cbcSMatt Macy 4120*eda14cbcSMatt Macy if (sd->sd_recursive && 4121*eda14cbcSMatt Macy zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) != 0) { 4122*eda14cbcSMatt Macy zfs_close(zhp); 4123*eda14cbcSMatt Macy return (0); 4124*eda14cbcSMatt Macy } 4125*eda14cbcSMatt Macy 4126*eda14cbcSMatt Macy error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname); 4127*eda14cbcSMatt Macy if (error == -1) 4128*eda14cbcSMatt Macy nomem(); 4129*eda14cbcSMatt Macy fnvlist_add_boolean(sd->sd_nvl, name); 4130*eda14cbcSMatt Macy free(name); 4131*eda14cbcSMatt Macy 4132*eda14cbcSMatt Macy if (sd->sd_recursive) 4133*eda14cbcSMatt Macy rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd); 4134*eda14cbcSMatt Macy zfs_close(zhp); 4135*eda14cbcSMatt Macy return (rv); 4136*eda14cbcSMatt Macy } 4137*eda14cbcSMatt Macy 4138*eda14cbcSMatt Macy /* 4139*eda14cbcSMatt Macy * zfs snapshot [-r] [-o prop=value] ... <fs@snap> 4140*eda14cbcSMatt Macy * 4141*eda14cbcSMatt Macy * Creates a snapshot with the given name. While functionally equivalent to 4142*eda14cbcSMatt Macy * 'zfs create', it is a separate command to differentiate intent. 4143*eda14cbcSMatt Macy */ 4144*eda14cbcSMatt Macy static int 4145*eda14cbcSMatt Macy zfs_do_snapshot(int argc, char **argv) 4146*eda14cbcSMatt Macy { 4147*eda14cbcSMatt Macy int ret = 0; 4148*eda14cbcSMatt Macy int c; 4149*eda14cbcSMatt Macy nvlist_t *props; 4150*eda14cbcSMatt Macy snap_cbdata_t sd = { 0 }; 4151*eda14cbcSMatt Macy boolean_t multiple_snaps = B_FALSE; 4152*eda14cbcSMatt Macy 4153*eda14cbcSMatt Macy if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 4154*eda14cbcSMatt Macy nomem(); 4155*eda14cbcSMatt Macy if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0) 4156*eda14cbcSMatt Macy nomem(); 4157*eda14cbcSMatt Macy 4158*eda14cbcSMatt Macy /* check options */ 4159*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "ro:")) != -1) { 4160*eda14cbcSMatt Macy switch (c) { 4161*eda14cbcSMatt Macy case 'o': 4162*eda14cbcSMatt Macy if (!parseprop(props, optarg)) { 4163*eda14cbcSMatt Macy nvlist_free(sd.sd_nvl); 4164*eda14cbcSMatt Macy nvlist_free(props); 4165*eda14cbcSMatt Macy return (1); 4166*eda14cbcSMatt Macy } 4167*eda14cbcSMatt Macy break; 4168*eda14cbcSMatt Macy case 'r': 4169*eda14cbcSMatt Macy sd.sd_recursive = B_TRUE; 4170*eda14cbcSMatt Macy multiple_snaps = B_TRUE; 4171*eda14cbcSMatt Macy break; 4172*eda14cbcSMatt Macy case '?': 4173*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 4174*eda14cbcSMatt Macy optopt); 4175*eda14cbcSMatt Macy goto usage; 4176*eda14cbcSMatt Macy } 4177*eda14cbcSMatt Macy } 4178*eda14cbcSMatt Macy 4179*eda14cbcSMatt Macy argc -= optind; 4180*eda14cbcSMatt Macy argv += optind; 4181*eda14cbcSMatt Macy 4182*eda14cbcSMatt Macy /* check number of arguments */ 4183*eda14cbcSMatt Macy if (argc < 1) { 4184*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing snapshot argument\n")); 4185*eda14cbcSMatt Macy goto usage; 4186*eda14cbcSMatt Macy } 4187*eda14cbcSMatt Macy 4188*eda14cbcSMatt Macy if (argc > 1) 4189*eda14cbcSMatt Macy multiple_snaps = B_TRUE; 4190*eda14cbcSMatt Macy for (; argc > 0; argc--, argv++) { 4191*eda14cbcSMatt Macy char *atp; 4192*eda14cbcSMatt Macy zfs_handle_t *zhp; 4193*eda14cbcSMatt Macy 4194*eda14cbcSMatt Macy atp = strchr(argv[0], '@'); 4195*eda14cbcSMatt Macy if (atp == NULL) 4196*eda14cbcSMatt Macy goto usage; 4197*eda14cbcSMatt Macy *atp = '\0'; 4198*eda14cbcSMatt Macy sd.sd_snapname = atp + 1; 4199*eda14cbcSMatt Macy zhp = zfs_open(g_zfs, argv[0], 4200*eda14cbcSMatt Macy ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 4201*eda14cbcSMatt Macy if (zhp == NULL) 4202*eda14cbcSMatt Macy goto usage; 4203*eda14cbcSMatt Macy if (zfs_snapshot_cb(zhp, &sd) != 0) 4204*eda14cbcSMatt Macy goto usage; 4205*eda14cbcSMatt Macy } 4206*eda14cbcSMatt Macy 4207*eda14cbcSMatt Macy ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props); 4208*eda14cbcSMatt Macy nvlist_free(sd.sd_nvl); 4209*eda14cbcSMatt Macy nvlist_free(props); 4210*eda14cbcSMatt Macy if (ret != 0 && multiple_snaps) 4211*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("no snapshots were created\n")); 4212*eda14cbcSMatt Macy return (ret != 0); 4213*eda14cbcSMatt Macy 4214*eda14cbcSMatt Macy usage: 4215*eda14cbcSMatt Macy nvlist_free(sd.sd_nvl); 4216*eda14cbcSMatt Macy nvlist_free(props); 4217*eda14cbcSMatt Macy usage(B_FALSE); 4218*eda14cbcSMatt Macy return (-1); 4219*eda14cbcSMatt Macy } 4220*eda14cbcSMatt Macy 4221*eda14cbcSMatt Macy 4222*eda14cbcSMatt Macy /* 4223*eda14cbcSMatt Macy * Send a backup stream to stdout. 4224*eda14cbcSMatt Macy */ 4225*eda14cbcSMatt Macy static int 4226*eda14cbcSMatt Macy zfs_do_send(int argc, char **argv) 4227*eda14cbcSMatt Macy { 4228*eda14cbcSMatt Macy char *fromname = NULL; 4229*eda14cbcSMatt Macy char *toname = NULL; 4230*eda14cbcSMatt Macy char *resume_token = NULL; 4231*eda14cbcSMatt Macy char *cp; 4232*eda14cbcSMatt Macy zfs_handle_t *zhp; 4233*eda14cbcSMatt Macy sendflags_t flags = { 0 }; 4234*eda14cbcSMatt Macy int c, err; 4235*eda14cbcSMatt Macy nvlist_t *dbgnv = NULL; 4236*eda14cbcSMatt Macy char *redactbook = NULL; 4237*eda14cbcSMatt Macy 4238*eda14cbcSMatt Macy struct option long_options[] = { 4239*eda14cbcSMatt Macy {"replicate", no_argument, NULL, 'R'}, 4240*eda14cbcSMatt Macy {"redact", required_argument, NULL, 'd'}, 4241*eda14cbcSMatt Macy {"props", no_argument, NULL, 'p'}, 4242*eda14cbcSMatt Macy {"parsable", no_argument, NULL, 'P'}, 4243*eda14cbcSMatt Macy {"dedup", no_argument, NULL, 'D'}, 4244*eda14cbcSMatt Macy {"verbose", no_argument, NULL, 'v'}, 4245*eda14cbcSMatt Macy {"dryrun", no_argument, NULL, 'n'}, 4246*eda14cbcSMatt Macy {"large-block", no_argument, NULL, 'L'}, 4247*eda14cbcSMatt Macy {"embed", no_argument, NULL, 'e'}, 4248*eda14cbcSMatt Macy {"resume", required_argument, NULL, 't'}, 4249*eda14cbcSMatt Macy {"compressed", no_argument, NULL, 'c'}, 4250*eda14cbcSMatt Macy {"raw", no_argument, NULL, 'w'}, 4251*eda14cbcSMatt Macy {"backup", no_argument, NULL, 'b'}, 4252*eda14cbcSMatt Macy {"holds", no_argument, NULL, 'h'}, 4253*eda14cbcSMatt Macy {"saved", no_argument, NULL, 'S'}, 4254*eda14cbcSMatt Macy {0, 0, 0, 0} 4255*eda14cbcSMatt Macy }; 4256*eda14cbcSMatt Macy 4257*eda14cbcSMatt Macy /* check options */ 4258*eda14cbcSMatt Macy while ((c = getopt_long(argc, argv, ":i:I:RDpvnPLeht:cwbd:S", 4259*eda14cbcSMatt Macy long_options, NULL)) != -1) { 4260*eda14cbcSMatt Macy switch (c) { 4261*eda14cbcSMatt Macy case 'i': 4262*eda14cbcSMatt Macy if (fromname) 4263*eda14cbcSMatt Macy usage(B_FALSE); 4264*eda14cbcSMatt Macy fromname = optarg; 4265*eda14cbcSMatt Macy break; 4266*eda14cbcSMatt Macy case 'I': 4267*eda14cbcSMatt Macy if (fromname) 4268*eda14cbcSMatt Macy usage(B_FALSE); 4269*eda14cbcSMatt Macy fromname = optarg; 4270*eda14cbcSMatt Macy flags.doall = B_TRUE; 4271*eda14cbcSMatt Macy break; 4272*eda14cbcSMatt Macy case 'R': 4273*eda14cbcSMatt Macy flags.replicate = B_TRUE; 4274*eda14cbcSMatt Macy break; 4275*eda14cbcSMatt Macy case 'd': 4276*eda14cbcSMatt Macy redactbook = optarg; 4277*eda14cbcSMatt Macy break; 4278*eda14cbcSMatt Macy case 'p': 4279*eda14cbcSMatt Macy flags.props = B_TRUE; 4280*eda14cbcSMatt Macy break; 4281*eda14cbcSMatt Macy case 'b': 4282*eda14cbcSMatt Macy flags.backup = B_TRUE; 4283*eda14cbcSMatt Macy break; 4284*eda14cbcSMatt Macy case 'h': 4285*eda14cbcSMatt Macy flags.holds = B_TRUE; 4286*eda14cbcSMatt Macy break; 4287*eda14cbcSMatt Macy case 'P': 4288*eda14cbcSMatt Macy flags.parsable = B_TRUE; 4289*eda14cbcSMatt Macy break; 4290*eda14cbcSMatt Macy case 'v': 4291*eda14cbcSMatt Macy flags.verbosity++; 4292*eda14cbcSMatt Macy flags.progress = B_TRUE; 4293*eda14cbcSMatt Macy break; 4294*eda14cbcSMatt Macy case 'D': 4295*eda14cbcSMatt Macy (void) fprintf(stderr, 4296*eda14cbcSMatt Macy gettext("WARNING: deduplicated send is no " 4297*eda14cbcSMatt Macy "longer supported. A regular,\n" 4298*eda14cbcSMatt Macy "non-deduplicated stream will be generated.\n\n")); 4299*eda14cbcSMatt Macy break; 4300*eda14cbcSMatt Macy case 'n': 4301*eda14cbcSMatt Macy flags.dryrun = B_TRUE; 4302*eda14cbcSMatt Macy break; 4303*eda14cbcSMatt Macy case 'L': 4304*eda14cbcSMatt Macy flags.largeblock = B_TRUE; 4305*eda14cbcSMatt Macy break; 4306*eda14cbcSMatt Macy case 'e': 4307*eda14cbcSMatt Macy flags.embed_data = B_TRUE; 4308*eda14cbcSMatt Macy break; 4309*eda14cbcSMatt Macy case 't': 4310*eda14cbcSMatt Macy resume_token = optarg; 4311*eda14cbcSMatt Macy break; 4312*eda14cbcSMatt Macy case 'c': 4313*eda14cbcSMatt Macy flags.compress = B_TRUE; 4314*eda14cbcSMatt Macy break; 4315*eda14cbcSMatt Macy case 'w': 4316*eda14cbcSMatt Macy flags.raw = B_TRUE; 4317*eda14cbcSMatt Macy flags.compress = B_TRUE; 4318*eda14cbcSMatt Macy flags.embed_data = B_TRUE; 4319*eda14cbcSMatt Macy flags.largeblock = B_TRUE; 4320*eda14cbcSMatt Macy break; 4321*eda14cbcSMatt Macy case 'S': 4322*eda14cbcSMatt Macy flags.saved = B_TRUE; 4323*eda14cbcSMatt Macy break; 4324*eda14cbcSMatt Macy case ':': 4325*eda14cbcSMatt Macy /* 4326*eda14cbcSMatt Macy * If a parameter was not passed, optopt contains the 4327*eda14cbcSMatt Macy * value that would normally lead us into the 4328*eda14cbcSMatt Macy * appropriate case statement. If it's > 256, then this 4329*eda14cbcSMatt Macy * must be a longopt and we should look at argv to get 4330*eda14cbcSMatt Macy * the string. Otherwise it's just the character, so we 4331*eda14cbcSMatt Macy * should use it directly. 4332*eda14cbcSMatt Macy */ 4333*eda14cbcSMatt Macy if (optopt <= UINT8_MAX) { 4334*eda14cbcSMatt Macy (void) fprintf(stderr, 4335*eda14cbcSMatt Macy gettext("missing argument for '%c' " 4336*eda14cbcSMatt Macy "option\n"), optopt); 4337*eda14cbcSMatt Macy } else { 4338*eda14cbcSMatt Macy (void) fprintf(stderr, 4339*eda14cbcSMatt Macy gettext("missing argument for '%s' " 4340*eda14cbcSMatt Macy "option\n"), argv[optind - 1]); 4341*eda14cbcSMatt Macy } 4342*eda14cbcSMatt Macy usage(B_FALSE); 4343*eda14cbcSMatt Macy break; 4344*eda14cbcSMatt Macy case '?': 4345*eda14cbcSMatt Macy /*FALLTHROUGH*/ 4346*eda14cbcSMatt Macy default: 4347*eda14cbcSMatt Macy /* 4348*eda14cbcSMatt Macy * If an invalid flag was passed, optopt contains the 4349*eda14cbcSMatt Macy * character if it was a short flag, or 0 if it was a 4350*eda14cbcSMatt Macy * longopt. 4351*eda14cbcSMatt Macy */ 4352*eda14cbcSMatt Macy if (optopt != 0) { 4353*eda14cbcSMatt Macy (void) fprintf(stderr, 4354*eda14cbcSMatt Macy gettext("invalid option '%c'\n"), optopt); 4355*eda14cbcSMatt Macy } else { 4356*eda14cbcSMatt Macy (void) fprintf(stderr, 4357*eda14cbcSMatt Macy gettext("invalid option '%s'\n"), 4358*eda14cbcSMatt Macy argv[optind - 1]); 4359*eda14cbcSMatt Macy 4360*eda14cbcSMatt Macy } 4361*eda14cbcSMatt Macy usage(B_FALSE); 4362*eda14cbcSMatt Macy } 4363*eda14cbcSMatt Macy } 4364*eda14cbcSMatt Macy 4365*eda14cbcSMatt Macy if (flags.parsable && flags.verbosity == 0) 4366*eda14cbcSMatt Macy flags.verbosity = 1; 4367*eda14cbcSMatt Macy 4368*eda14cbcSMatt Macy argc -= optind; 4369*eda14cbcSMatt Macy argv += optind; 4370*eda14cbcSMatt Macy 4371*eda14cbcSMatt Macy if (resume_token != NULL) { 4372*eda14cbcSMatt Macy if (fromname != NULL || flags.replicate || flags.props || 4373*eda14cbcSMatt Macy flags.backup || flags.holds || 4374*eda14cbcSMatt Macy flags.saved || redactbook != NULL) { 4375*eda14cbcSMatt Macy (void) fprintf(stderr, 4376*eda14cbcSMatt Macy gettext("invalid flags combined with -t\n")); 4377*eda14cbcSMatt Macy usage(B_FALSE); 4378*eda14cbcSMatt Macy } 4379*eda14cbcSMatt Macy if (argc > 0) { 4380*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many arguments\n")); 4381*eda14cbcSMatt Macy usage(B_FALSE); 4382*eda14cbcSMatt Macy } 4383*eda14cbcSMatt Macy } else { 4384*eda14cbcSMatt Macy if (argc < 1) { 4385*eda14cbcSMatt Macy (void) fprintf(stderr, 4386*eda14cbcSMatt Macy gettext("missing snapshot argument\n")); 4387*eda14cbcSMatt Macy usage(B_FALSE); 4388*eda14cbcSMatt Macy } 4389*eda14cbcSMatt Macy if (argc > 1) { 4390*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many arguments\n")); 4391*eda14cbcSMatt Macy usage(B_FALSE); 4392*eda14cbcSMatt Macy } 4393*eda14cbcSMatt Macy } 4394*eda14cbcSMatt Macy 4395*eda14cbcSMatt Macy if (flags.saved) { 4396*eda14cbcSMatt Macy if (fromname != NULL || flags.replicate || flags.props || 4397*eda14cbcSMatt Macy flags.doall || flags.backup || 4398*eda14cbcSMatt Macy flags.holds || flags.largeblock || flags.embed_data || 4399*eda14cbcSMatt Macy flags.compress || flags.raw || redactbook != NULL) { 4400*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("incompatible flags " 4401*eda14cbcSMatt Macy "combined with saved send flag\n")); 4402*eda14cbcSMatt Macy usage(B_FALSE); 4403*eda14cbcSMatt Macy } 4404*eda14cbcSMatt Macy if (strchr(argv[0], '@') != NULL) { 4405*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("saved send must " 4406*eda14cbcSMatt Macy "specify the dataset with partially-received " 4407*eda14cbcSMatt Macy "state\n")); 4408*eda14cbcSMatt Macy usage(B_FALSE); 4409*eda14cbcSMatt Macy } 4410*eda14cbcSMatt Macy } 4411*eda14cbcSMatt Macy 4412*eda14cbcSMatt Macy if (flags.raw && redactbook != NULL) { 4413*eda14cbcSMatt Macy (void) fprintf(stderr, 4414*eda14cbcSMatt Macy gettext("Error: raw sends may not be redacted.\n")); 4415*eda14cbcSMatt Macy return (1); 4416*eda14cbcSMatt Macy } 4417*eda14cbcSMatt Macy 4418*eda14cbcSMatt Macy if (!flags.dryrun && isatty(STDOUT_FILENO)) { 4419*eda14cbcSMatt Macy (void) fprintf(stderr, 4420*eda14cbcSMatt Macy gettext("Error: Stream can not be written to a terminal.\n" 4421*eda14cbcSMatt Macy "You must redirect standard output.\n")); 4422*eda14cbcSMatt Macy return (1); 4423*eda14cbcSMatt Macy } 4424*eda14cbcSMatt Macy 4425*eda14cbcSMatt Macy if (flags.saved) { 4426*eda14cbcSMatt Macy zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET); 4427*eda14cbcSMatt Macy if (zhp == NULL) 4428*eda14cbcSMatt Macy return (1); 4429*eda14cbcSMatt Macy 4430*eda14cbcSMatt Macy err = zfs_send_saved(zhp, &flags, STDOUT_FILENO, 4431*eda14cbcSMatt Macy resume_token); 4432*eda14cbcSMatt Macy zfs_close(zhp); 4433*eda14cbcSMatt Macy return (err != 0); 4434*eda14cbcSMatt Macy } else if (resume_token != NULL) { 4435*eda14cbcSMatt Macy return (zfs_send_resume(g_zfs, &flags, STDOUT_FILENO, 4436*eda14cbcSMatt Macy resume_token)); 4437*eda14cbcSMatt Macy } 4438*eda14cbcSMatt Macy 4439*eda14cbcSMatt Macy /* 4440*eda14cbcSMatt Macy * For everything except -R and -I, use the new, cleaner code path. 4441*eda14cbcSMatt Macy */ 4442*eda14cbcSMatt Macy if (!(flags.replicate || flags.doall)) { 4443*eda14cbcSMatt Macy char frombuf[ZFS_MAX_DATASET_NAME_LEN]; 4444*eda14cbcSMatt Macy 4445*eda14cbcSMatt Macy if (fromname != NULL && (strchr(fromname, '#') == NULL && 4446*eda14cbcSMatt Macy strchr(fromname, '@') == NULL)) { 4447*eda14cbcSMatt Macy /* 4448*eda14cbcSMatt Macy * Neither bookmark or snapshot was specified. Print a 4449*eda14cbcSMatt Macy * warning, and assume snapshot. 4450*eda14cbcSMatt Macy */ 4451*eda14cbcSMatt Macy (void) fprintf(stderr, "Warning: incremental source " 4452*eda14cbcSMatt Macy "didn't specify type, assuming snapshot. Use '@' " 4453*eda14cbcSMatt Macy "or '#' prefix to avoid ambiguity.\n"); 4454*eda14cbcSMatt Macy (void) snprintf(frombuf, sizeof (frombuf), "@%s", 4455*eda14cbcSMatt Macy fromname); 4456*eda14cbcSMatt Macy fromname = frombuf; 4457*eda14cbcSMatt Macy } 4458*eda14cbcSMatt Macy if (fromname != NULL && 4459*eda14cbcSMatt Macy (fromname[0] == '#' || fromname[0] == '@')) { 4460*eda14cbcSMatt Macy /* 4461*eda14cbcSMatt Macy * Incremental source name begins with # or @. 4462*eda14cbcSMatt Macy * Default to same fs as target. 4463*eda14cbcSMatt Macy */ 4464*eda14cbcSMatt Macy char tmpbuf[ZFS_MAX_DATASET_NAME_LEN]; 4465*eda14cbcSMatt Macy (void) strlcpy(tmpbuf, fromname, sizeof (tmpbuf)); 4466*eda14cbcSMatt Macy (void) strlcpy(frombuf, argv[0], sizeof (frombuf)); 4467*eda14cbcSMatt Macy cp = strchr(frombuf, '@'); 4468*eda14cbcSMatt Macy if (cp != NULL) 4469*eda14cbcSMatt Macy *cp = '\0'; 4470*eda14cbcSMatt Macy (void) strlcat(frombuf, tmpbuf, sizeof (frombuf)); 4471*eda14cbcSMatt Macy fromname = frombuf; 4472*eda14cbcSMatt Macy } 4473*eda14cbcSMatt Macy 4474*eda14cbcSMatt Macy zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET); 4475*eda14cbcSMatt Macy if (zhp == NULL) 4476*eda14cbcSMatt Macy return (1); 4477*eda14cbcSMatt Macy err = zfs_send_one(zhp, fromname, STDOUT_FILENO, &flags, 4478*eda14cbcSMatt Macy redactbook); 4479*eda14cbcSMatt Macy zfs_close(zhp); 4480*eda14cbcSMatt Macy return (err != 0); 4481*eda14cbcSMatt Macy } 4482*eda14cbcSMatt Macy 4483*eda14cbcSMatt Macy if (fromname != NULL && strchr(fromname, '#')) { 4484*eda14cbcSMatt Macy (void) fprintf(stderr, 4485*eda14cbcSMatt Macy gettext("Error: multiple snapshots cannot be " 4486*eda14cbcSMatt Macy "sent from a bookmark.\n")); 4487*eda14cbcSMatt Macy return (1); 4488*eda14cbcSMatt Macy } 4489*eda14cbcSMatt Macy 4490*eda14cbcSMatt Macy if (redactbook != NULL) { 4491*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("Error: multiple snapshots " 4492*eda14cbcSMatt Macy "cannot be sent redacted.\n")); 4493*eda14cbcSMatt Macy return (1); 4494*eda14cbcSMatt Macy } 4495*eda14cbcSMatt Macy 4496*eda14cbcSMatt Macy if ((cp = strchr(argv[0], '@')) == NULL) { 4497*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("Error: " 4498*eda14cbcSMatt Macy "Unsupported flag with filesystem or bookmark.\n")); 4499*eda14cbcSMatt Macy return (1); 4500*eda14cbcSMatt Macy } 4501*eda14cbcSMatt Macy *cp = '\0'; 4502*eda14cbcSMatt Macy toname = cp + 1; 4503*eda14cbcSMatt Macy zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 4504*eda14cbcSMatt Macy if (zhp == NULL) 4505*eda14cbcSMatt Macy return (1); 4506*eda14cbcSMatt Macy 4507*eda14cbcSMatt Macy /* 4508*eda14cbcSMatt Macy * If they specified the full path to the snapshot, chop off 4509*eda14cbcSMatt Macy * everything except the short name of the snapshot, but special 4510*eda14cbcSMatt Macy * case if they specify the origin. 4511*eda14cbcSMatt Macy */ 4512*eda14cbcSMatt Macy if (fromname && (cp = strchr(fromname, '@')) != NULL) { 4513*eda14cbcSMatt Macy char origin[ZFS_MAX_DATASET_NAME_LEN]; 4514*eda14cbcSMatt Macy zprop_source_t src; 4515*eda14cbcSMatt Macy 4516*eda14cbcSMatt Macy (void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN, 4517*eda14cbcSMatt Macy origin, sizeof (origin), &src, NULL, 0, B_FALSE); 4518*eda14cbcSMatt Macy 4519*eda14cbcSMatt Macy if (strcmp(origin, fromname) == 0) { 4520*eda14cbcSMatt Macy fromname = NULL; 4521*eda14cbcSMatt Macy flags.fromorigin = B_TRUE; 4522*eda14cbcSMatt Macy } else { 4523*eda14cbcSMatt Macy *cp = '\0'; 4524*eda14cbcSMatt Macy if (cp != fromname && strcmp(argv[0], fromname)) { 4525*eda14cbcSMatt Macy (void) fprintf(stderr, 4526*eda14cbcSMatt Macy gettext("incremental source must be " 4527*eda14cbcSMatt Macy "in same filesystem\n")); 4528*eda14cbcSMatt Macy usage(B_FALSE); 4529*eda14cbcSMatt Macy } 4530*eda14cbcSMatt Macy fromname = cp + 1; 4531*eda14cbcSMatt Macy if (strchr(fromname, '@') || strchr(fromname, '/')) { 4532*eda14cbcSMatt Macy (void) fprintf(stderr, 4533*eda14cbcSMatt Macy gettext("invalid incremental source\n")); 4534*eda14cbcSMatt Macy usage(B_FALSE); 4535*eda14cbcSMatt Macy } 4536*eda14cbcSMatt Macy } 4537*eda14cbcSMatt Macy } 4538*eda14cbcSMatt Macy 4539*eda14cbcSMatt Macy if (flags.replicate && fromname == NULL) 4540*eda14cbcSMatt Macy flags.doall = B_TRUE; 4541*eda14cbcSMatt Macy 4542*eda14cbcSMatt Macy err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO, NULL, 0, 4543*eda14cbcSMatt Macy flags.verbosity >= 3 ? &dbgnv : NULL); 4544*eda14cbcSMatt Macy 4545*eda14cbcSMatt Macy if (flags.verbosity >= 3 && dbgnv != NULL) { 4546*eda14cbcSMatt Macy /* 4547*eda14cbcSMatt Macy * dump_nvlist prints to stdout, but that's been 4548*eda14cbcSMatt Macy * redirected to a file. Make it print to stderr 4549*eda14cbcSMatt Macy * instead. 4550*eda14cbcSMatt Macy */ 4551*eda14cbcSMatt Macy (void) dup2(STDERR_FILENO, STDOUT_FILENO); 4552*eda14cbcSMatt Macy dump_nvlist(dbgnv, 0); 4553*eda14cbcSMatt Macy nvlist_free(dbgnv); 4554*eda14cbcSMatt Macy } 4555*eda14cbcSMatt Macy zfs_close(zhp); 4556*eda14cbcSMatt Macy 4557*eda14cbcSMatt Macy return (err != 0); 4558*eda14cbcSMatt Macy } 4559*eda14cbcSMatt Macy 4560*eda14cbcSMatt Macy /* 4561*eda14cbcSMatt Macy * Restore a backup stream from stdin. 4562*eda14cbcSMatt Macy */ 4563*eda14cbcSMatt Macy static int 4564*eda14cbcSMatt Macy zfs_do_receive(int argc, char **argv) 4565*eda14cbcSMatt Macy { 4566*eda14cbcSMatt Macy int c, err = 0; 4567*eda14cbcSMatt Macy recvflags_t flags = { 0 }; 4568*eda14cbcSMatt Macy boolean_t abort_resumable = B_FALSE; 4569*eda14cbcSMatt Macy nvlist_t *props; 4570*eda14cbcSMatt Macy 4571*eda14cbcSMatt Macy if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 4572*eda14cbcSMatt Macy nomem(); 4573*eda14cbcSMatt Macy 4574*eda14cbcSMatt Macy /* check options */ 4575*eda14cbcSMatt Macy while ((c = getopt(argc, argv, ":o:x:dehMnuvFsA")) != -1) { 4576*eda14cbcSMatt Macy switch (c) { 4577*eda14cbcSMatt Macy case 'o': 4578*eda14cbcSMatt Macy if (!parseprop(props, optarg)) { 4579*eda14cbcSMatt Macy nvlist_free(props); 4580*eda14cbcSMatt Macy usage(B_FALSE); 4581*eda14cbcSMatt Macy } 4582*eda14cbcSMatt Macy break; 4583*eda14cbcSMatt Macy case 'x': 4584*eda14cbcSMatt Macy if (!parsepropname(props, optarg)) { 4585*eda14cbcSMatt Macy nvlist_free(props); 4586*eda14cbcSMatt Macy usage(B_FALSE); 4587*eda14cbcSMatt Macy } 4588*eda14cbcSMatt Macy break; 4589*eda14cbcSMatt Macy case 'd': 4590*eda14cbcSMatt Macy if (flags.istail) { 4591*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option " 4592*eda14cbcSMatt Macy "combination: -d and -e are mutually " 4593*eda14cbcSMatt Macy "exclusive\n")); 4594*eda14cbcSMatt Macy usage(B_FALSE); 4595*eda14cbcSMatt Macy } 4596*eda14cbcSMatt Macy flags.isprefix = B_TRUE; 4597*eda14cbcSMatt Macy break; 4598*eda14cbcSMatt Macy case 'e': 4599*eda14cbcSMatt Macy if (flags.isprefix) { 4600*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option " 4601*eda14cbcSMatt Macy "combination: -d and -e are mutually " 4602*eda14cbcSMatt Macy "exclusive\n")); 4603*eda14cbcSMatt Macy usage(B_FALSE); 4604*eda14cbcSMatt Macy } 4605*eda14cbcSMatt Macy flags.istail = B_TRUE; 4606*eda14cbcSMatt Macy break; 4607*eda14cbcSMatt Macy case 'h': 4608*eda14cbcSMatt Macy flags.skipholds = B_TRUE; 4609*eda14cbcSMatt Macy break; 4610*eda14cbcSMatt Macy case 'M': 4611*eda14cbcSMatt Macy flags.forceunmount = B_TRUE; 4612*eda14cbcSMatt Macy break; 4613*eda14cbcSMatt Macy case 'n': 4614*eda14cbcSMatt Macy flags.dryrun = B_TRUE; 4615*eda14cbcSMatt Macy break; 4616*eda14cbcSMatt Macy case 'u': 4617*eda14cbcSMatt Macy flags.nomount = B_TRUE; 4618*eda14cbcSMatt Macy break; 4619*eda14cbcSMatt Macy case 'v': 4620*eda14cbcSMatt Macy flags.verbose = B_TRUE; 4621*eda14cbcSMatt Macy break; 4622*eda14cbcSMatt Macy case 's': 4623*eda14cbcSMatt Macy flags.resumable = B_TRUE; 4624*eda14cbcSMatt Macy break; 4625*eda14cbcSMatt Macy case 'F': 4626*eda14cbcSMatt Macy flags.force = B_TRUE; 4627*eda14cbcSMatt Macy break; 4628*eda14cbcSMatt Macy case 'A': 4629*eda14cbcSMatt Macy abort_resumable = B_TRUE; 4630*eda14cbcSMatt Macy break; 4631*eda14cbcSMatt Macy case ':': 4632*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing argument for " 4633*eda14cbcSMatt Macy "'%c' option\n"), optopt); 4634*eda14cbcSMatt Macy usage(B_FALSE); 4635*eda14cbcSMatt Macy break; 4636*eda14cbcSMatt Macy case '?': 4637*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 4638*eda14cbcSMatt Macy optopt); 4639*eda14cbcSMatt Macy usage(B_FALSE); 4640*eda14cbcSMatt Macy } 4641*eda14cbcSMatt Macy } 4642*eda14cbcSMatt Macy 4643*eda14cbcSMatt Macy argc -= optind; 4644*eda14cbcSMatt Macy argv += optind; 4645*eda14cbcSMatt Macy 4646*eda14cbcSMatt Macy /* zfs recv -e (use "tail" name) implies -d (remove dataset "head") */ 4647*eda14cbcSMatt Macy if (flags.istail) 4648*eda14cbcSMatt Macy flags.isprefix = B_TRUE; 4649*eda14cbcSMatt Macy 4650*eda14cbcSMatt Macy /* check number of arguments */ 4651*eda14cbcSMatt Macy if (argc < 1) { 4652*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing snapshot argument\n")); 4653*eda14cbcSMatt Macy usage(B_FALSE); 4654*eda14cbcSMatt Macy } 4655*eda14cbcSMatt Macy if (argc > 1) { 4656*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many arguments\n")); 4657*eda14cbcSMatt Macy usage(B_FALSE); 4658*eda14cbcSMatt Macy } 4659*eda14cbcSMatt Macy 4660*eda14cbcSMatt Macy if (abort_resumable) { 4661*eda14cbcSMatt Macy if (flags.isprefix || flags.istail || flags.dryrun || 4662*eda14cbcSMatt Macy flags.resumable || flags.nomount) { 4663*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option\n")); 4664*eda14cbcSMatt Macy usage(B_FALSE); 4665*eda14cbcSMatt Macy } 4666*eda14cbcSMatt Macy 4667*eda14cbcSMatt Macy char namebuf[ZFS_MAX_DATASET_NAME_LEN]; 4668*eda14cbcSMatt Macy (void) snprintf(namebuf, sizeof (namebuf), 4669*eda14cbcSMatt Macy "%s/%%recv", argv[0]); 4670*eda14cbcSMatt Macy 4671*eda14cbcSMatt Macy if (zfs_dataset_exists(g_zfs, namebuf, 4672*eda14cbcSMatt Macy ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) { 4673*eda14cbcSMatt Macy zfs_handle_t *zhp = zfs_open(g_zfs, 4674*eda14cbcSMatt Macy namebuf, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 4675*eda14cbcSMatt Macy if (zhp == NULL) { 4676*eda14cbcSMatt Macy nvlist_free(props); 4677*eda14cbcSMatt Macy return (1); 4678*eda14cbcSMatt Macy } 4679*eda14cbcSMatt Macy err = zfs_destroy(zhp, B_FALSE); 4680*eda14cbcSMatt Macy zfs_close(zhp); 4681*eda14cbcSMatt Macy } else { 4682*eda14cbcSMatt Macy zfs_handle_t *zhp = zfs_open(g_zfs, 4683*eda14cbcSMatt Macy argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 4684*eda14cbcSMatt Macy if (zhp == NULL) 4685*eda14cbcSMatt Macy usage(B_FALSE); 4686*eda14cbcSMatt Macy if (!zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) || 4687*eda14cbcSMatt Macy zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN, 4688*eda14cbcSMatt Macy NULL, 0, NULL, NULL, 0, B_TRUE) == -1) { 4689*eda14cbcSMatt Macy (void) fprintf(stderr, 4690*eda14cbcSMatt Macy gettext("'%s' does not have any " 4691*eda14cbcSMatt Macy "resumable receive state to abort\n"), 4692*eda14cbcSMatt Macy argv[0]); 4693*eda14cbcSMatt Macy nvlist_free(props); 4694*eda14cbcSMatt Macy zfs_close(zhp); 4695*eda14cbcSMatt Macy return (1); 4696*eda14cbcSMatt Macy } 4697*eda14cbcSMatt Macy err = zfs_destroy(zhp, B_FALSE); 4698*eda14cbcSMatt Macy zfs_close(zhp); 4699*eda14cbcSMatt Macy } 4700*eda14cbcSMatt Macy nvlist_free(props); 4701*eda14cbcSMatt Macy return (err != 0); 4702*eda14cbcSMatt Macy } 4703*eda14cbcSMatt Macy 4704*eda14cbcSMatt Macy if (isatty(STDIN_FILENO)) { 4705*eda14cbcSMatt Macy (void) fprintf(stderr, 4706*eda14cbcSMatt Macy gettext("Error: Backup stream can not be read " 4707*eda14cbcSMatt Macy "from a terminal.\n" 4708*eda14cbcSMatt Macy "You must redirect standard input.\n")); 4709*eda14cbcSMatt Macy nvlist_free(props); 4710*eda14cbcSMatt Macy return (1); 4711*eda14cbcSMatt Macy } 4712*eda14cbcSMatt Macy err = zfs_receive(g_zfs, argv[0], props, &flags, STDIN_FILENO, NULL); 4713*eda14cbcSMatt Macy nvlist_free(props); 4714*eda14cbcSMatt Macy 4715*eda14cbcSMatt Macy return (err != 0); 4716*eda14cbcSMatt Macy } 4717*eda14cbcSMatt Macy 4718*eda14cbcSMatt Macy /* 4719*eda14cbcSMatt Macy * allow/unallow stuff 4720*eda14cbcSMatt Macy */ 4721*eda14cbcSMatt Macy /* copied from zfs/sys/dsl_deleg.h */ 4722*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_CREATE "create" 4723*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_DESTROY "destroy" 4724*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_SNAPSHOT "snapshot" 4725*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_ROLLBACK "rollback" 4726*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_CLONE "clone" 4727*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_PROMOTE "promote" 4728*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_RENAME "rename" 4729*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_MOUNT "mount" 4730*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_SHARE "share" 4731*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_SEND "send" 4732*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_RECEIVE "receive" 4733*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_ALLOW "allow" 4734*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_USERPROP "userprop" 4735*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_VSCAN "vscan" /* ??? */ 4736*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_USERQUOTA "userquota" 4737*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_GROUPQUOTA "groupquota" 4738*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_USERUSED "userused" 4739*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_GROUPUSED "groupused" 4740*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_USEROBJQUOTA "userobjquota" 4741*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_GROUPOBJQUOTA "groupobjquota" 4742*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_USEROBJUSED "userobjused" 4743*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_GROUPOBJUSED "groupobjused" 4744*eda14cbcSMatt Macy 4745*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_HOLD "hold" 4746*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_RELEASE "release" 4747*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_DIFF "diff" 4748*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_BOOKMARK "bookmark" 4749*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_LOAD_KEY "load-key" 4750*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_CHANGE_KEY "change-key" 4751*eda14cbcSMatt Macy 4752*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_PROJECTUSED "projectused" 4753*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_PROJECTQUOTA "projectquota" 4754*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_PROJECTOBJUSED "projectobjused" 4755*eda14cbcSMatt Macy #define ZFS_DELEG_PERM_PROJECTOBJQUOTA "projectobjquota" 4756*eda14cbcSMatt Macy 4757*eda14cbcSMatt Macy #define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE 4758*eda14cbcSMatt Macy 4759*eda14cbcSMatt Macy static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = { 4760*eda14cbcSMatt Macy { ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW }, 4761*eda14cbcSMatt Macy { ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE }, 4762*eda14cbcSMatt Macy { ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE }, 4763*eda14cbcSMatt Macy { ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY }, 4764*eda14cbcSMatt Macy { ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF}, 4765*eda14cbcSMatt Macy { ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD }, 4766*eda14cbcSMatt Macy { ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT }, 4767*eda14cbcSMatt Macy { ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE }, 4768*eda14cbcSMatt Macy { ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE }, 4769*eda14cbcSMatt Macy { ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE }, 4770*eda14cbcSMatt Macy { ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME }, 4771*eda14cbcSMatt Macy { ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK }, 4772*eda14cbcSMatt Macy { ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND }, 4773*eda14cbcSMatt Macy { ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE }, 4774*eda14cbcSMatt Macy { ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT }, 4775*eda14cbcSMatt Macy { ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK }, 4776*eda14cbcSMatt Macy { ZFS_DELEG_PERM_LOAD_KEY, ZFS_DELEG_NOTE_LOAD_KEY }, 4777*eda14cbcSMatt Macy { ZFS_DELEG_PERM_CHANGE_KEY, ZFS_DELEG_NOTE_CHANGE_KEY }, 4778*eda14cbcSMatt Macy 4779*eda14cbcSMatt Macy { ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA }, 4780*eda14cbcSMatt Macy { ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED }, 4781*eda14cbcSMatt Macy { ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP }, 4782*eda14cbcSMatt Macy { ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA }, 4783*eda14cbcSMatt Macy { ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED }, 4784*eda14cbcSMatt Macy { ZFS_DELEG_PERM_USEROBJQUOTA, ZFS_DELEG_NOTE_USEROBJQUOTA }, 4785*eda14cbcSMatt Macy { ZFS_DELEG_PERM_USEROBJUSED, ZFS_DELEG_NOTE_USEROBJUSED }, 4786*eda14cbcSMatt Macy { ZFS_DELEG_PERM_GROUPOBJQUOTA, ZFS_DELEG_NOTE_GROUPOBJQUOTA }, 4787*eda14cbcSMatt Macy { ZFS_DELEG_PERM_GROUPOBJUSED, ZFS_DELEG_NOTE_GROUPOBJUSED }, 4788*eda14cbcSMatt Macy { ZFS_DELEG_PERM_PROJECTUSED, ZFS_DELEG_NOTE_PROJECTUSED }, 4789*eda14cbcSMatt Macy { ZFS_DELEG_PERM_PROJECTQUOTA, ZFS_DELEG_NOTE_PROJECTQUOTA }, 4790*eda14cbcSMatt Macy { ZFS_DELEG_PERM_PROJECTOBJUSED, ZFS_DELEG_NOTE_PROJECTOBJUSED }, 4791*eda14cbcSMatt Macy { ZFS_DELEG_PERM_PROJECTOBJQUOTA, ZFS_DELEG_NOTE_PROJECTOBJQUOTA }, 4792*eda14cbcSMatt Macy { NULL, ZFS_DELEG_NOTE_NONE } 4793*eda14cbcSMatt Macy }; 4794*eda14cbcSMatt Macy 4795*eda14cbcSMatt Macy /* permission structure */ 4796*eda14cbcSMatt Macy typedef struct deleg_perm { 4797*eda14cbcSMatt Macy zfs_deleg_who_type_t dp_who_type; 4798*eda14cbcSMatt Macy const char *dp_name; 4799*eda14cbcSMatt Macy boolean_t dp_local; 4800*eda14cbcSMatt Macy boolean_t dp_descend; 4801*eda14cbcSMatt Macy } deleg_perm_t; 4802*eda14cbcSMatt Macy 4803*eda14cbcSMatt Macy /* */ 4804*eda14cbcSMatt Macy typedef struct deleg_perm_node { 4805*eda14cbcSMatt Macy deleg_perm_t dpn_perm; 4806*eda14cbcSMatt Macy 4807*eda14cbcSMatt Macy uu_avl_node_t dpn_avl_node; 4808*eda14cbcSMatt Macy } deleg_perm_node_t; 4809*eda14cbcSMatt Macy 4810*eda14cbcSMatt Macy typedef struct fs_perm fs_perm_t; 4811*eda14cbcSMatt Macy 4812*eda14cbcSMatt Macy /* permissions set */ 4813*eda14cbcSMatt Macy typedef struct who_perm { 4814*eda14cbcSMatt Macy zfs_deleg_who_type_t who_type; 4815*eda14cbcSMatt Macy const char *who_name; /* id */ 4816*eda14cbcSMatt Macy char who_ug_name[256]; /* user/group name */ 4817*eda14cbcSMatt Macy fs_perm_t *who_fsperm; /* uplink */ 4818*eda14cbcSMatt Macy 4819*eda14cbcSMatt Macy uu_avl_t *who_deleg_perm_avl; /* permissions */ 4820*eda14cbcSMatt Macy } who_perm_t; 4821*eda14cbcSMatt Macy 4822*eda14cbcSMatt Macy /* */ 4823*eda14cbcSMatt Macy typedef struct who_perm_node { 4824*eda14cbcSMatt Macy who_perm_t who_perm; 4825*eda14cbcSMatt Macy uu_avl_node_t who_avl_node; 4826*eda14cbcSMatt Macy } who_perm_node_t; 4827*eda14cbcSMatt Macy 4828*eda14cbcSMatt Macy typedef struct fs_perm_set fs_perm_set_t; 4829*eda14cbcSMatt Macy /* fs permissions */ 4830*eda14cbcSMatt Macy struct fs_perm { 4831*eda14cbcSMatt Macy const char *fsp_name; 4832*eda14cbcSMatt Macy 4833*eda14cbcSMatt Macy uu_avl_t *fsp_sc_avl; /* sets,create */ 4834*eda14cbcSMatt Macy uu_avl_t *fsp_uge_avl; /* user,group,everyone */ 4835*eda14cbcSMatt Macy 4836*eda14cbcSMatt Macy fs_perm_set_t *fsp_set; /* uplink */ 4837*eda14cbcSMatt Macy }; 4838*eda14cbcSMatt Macy 4839*eda14cbcSMatt Macy /* */ 4840*eda14cbcSMatt Macy typedef struct fs_perm_node { 4841*eda14cbcSMatt Macy fs_perm_t fspn_fsperm; 4842*eda14cbcSMatt Macy uu_avl_t *fspn_avl; 4843*eda14cbcSMatt Macy 4844*eda14cbcSMatt Macy uu_list_node_t fspn_list_node; 4845*eda14cbcSMatt Macy } fs_perm_node_t; 4846*eda14cbcSMatt Macy 4847*eda14cbcSMatt Macy /* top level structure */ 4848*eda14cbcSMatt Macy struct fs_perm_set { 4849*eda14cbcSMatt Macy uu_list_pool_t *fsps_list_pool; 4850*eda14cbcSMatt Macy uu_list_t *fsps_list; /* list of fs_perms */ 4851*eda14cbcSMatt Macy 4852*eda14cbcSMatt Macy uu_avl_pool_t *fsps_named_set_avl_pool; 4853*eda14cbcSMatt Macy uu_avl_pool_t *fsps_who_perm_avl_pool; 4854*eda14cbcSMatt Macy uu_avl_pool_t *fsps_deleg_perm_avl_pool; 4855*eda14cbcSMatt Macy }; 4856*eda14cbcSMatt Macy 4857*eda14cbcSMatt Macy static inline const char * 4858*eda14cbcSMatt Macy deleg_perm_type(zfs_deleg_note_t note) 4859*eda14cbcSMatt Macy { 4860*eda14cbcSMatt Macy /* subcommands */ 4861*eda14cbcSMatt Macy switch (note) { 4862*eda14cbcSMatt Macy /* SUBCOMMANDS */ 4863*eda14cbcSMatt Macy /* OTHER */ 4864*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_GROUPQUOTA: 4865*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_GROUPUSED: 4866*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_USERPROP: 4867*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_USERQUOTA: 4868*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_USERUSED: 4869*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_USEROBJQUOTA: 4870*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_USEROBJUSED: 4871*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_GROUPOBJQUOTA: 4872*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_GROUPOBJUSED: 4873*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_PROJECTUSED: 4874*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_PROJECTQUOTA: 4875*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_PROJECTOBJUSED: 4876*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_PROJECTOBJQUOTA: 4877*eda14cbcSMatt Macy /* other */ 4878*eda14cbcSMatt Macy return (gettext("other")); 4879*eda14cbcSMatt Macy default: 4880*eda14cbcSMatt Macy return (gettext("subcommand")); 4881*eda14cbcSMatt Macy } 4882*eda14cbcSMatt Macy } 4883*eda14cbcSMatt Macy 4884*eda14cbcSMatt Macy static int 4885*eda14cbcSMatt Macy who_type2weight(zfs_deleg_who_type_t who_type) 4886*eda14cbcSMatt Macy { 4887*eda14cbcSMatt Macy int res; 4888*eda14cbcSMatt Macy switch (who_type) { 4889*eda14cbcSMatt Macy case ZFS_DELEG_NAMED_SET_SETS: 4890*eda14cbcSMatt Macy case ZFS_DELEG_NAMED_SET: 4891*eda14cbcSMatt Macy res = 0; 4892*eda14cbcSMatt Macy break; 4893*eda14cbcSMatt Macy case ZFS_DELEG_CREATE_SETS: 4894*eda14cbcSMatt Macy case ZFS_DELEG_CREATE: 4895*eda14cbcSMatt Macy res = 1; 4896*eda14cbcSMatt Macy break; 4897*eda14cbcSMatt Macy case ZFS_DELEG_USER_SETS: 4898*eda14cbcSMatt Macy case ZFS_DELEG_USER: 4899*eda14cbcSMatt Macy res = 2; 4900*eda14cbcSMatt Macy break; 4901*eda14cbcSMatt Macy case ZFS_DELEG_GROUP_SETS: 4902*eda14cbcSMatt Macy case ZFS_DELEG_GROUP: 4903*eda14cbcSMatt Macy res = 3; 4904*eda14cbcSMatt Macy break; 4905*eda14cbcSMatt Macy case ZFS_DELEG_EVERYONE_SETS: 4906*eda14cbcSMatt Macy case ZFS_DELEG_EVERYONE: 4907*eda14cbcSMatt Macy res = 4; 4908*eda14cbcSMatt Macy break; 4909*eda14cbcSMatt Macy default: 4910*eda14cbcSMatt Macy res = -1; 4911*eda14cbcSMatt Macy } 4912*eda14cbcSMatt Macy 4913*eda14cbcSMatt Macy return (res); 4914*eda14cbcSMatt Macy } 4915*eda14cbcSMatt Macy 4916*eda14cbcSMatt Macy /* ARGSUSED */ 4917*eda14cbcSMatt Macy static int 4918*eda14cbcSMatt Macy who_perm_compare(const void *larg, const void *rarg, void *unused) 4919*eda14cbcSMatt Macy { 4920*eda14cbcSMatt Macy const who_perm_node_t *l = larg; 4921*eda14cbcSMatt Macy const who_perm_node_t *r = rarg; 4922*eda14cbcSMatt Macy zfs_deleg_who_type_t ltype = l->who_perm.who_type; 4923*eda14cbcSMatt Macy zfs_deleg_who_type_t rtype = r->who_perm.who_type; 4924*eda14cbcSMatt Macy int lweight = who_type2weight(ltype); 4925*eda14cbcSMatt Macy int rweight = who_type2weight(rtype); 4926*eda14cbcSMatt Macy int res = lweight - rweight; 4927*eda14cbcSMatt Macy if (res == 0) 4928*eda14cbcSMatt Macy res = strncmp(l->who_perm.who_name, r->who_perm.who_name, 4929*eda14cbcSMatt Macy ZFS_MAX_DELEG_NAME-1); 4930*eda14cbcSMatt Macy 4931*eda14cbcSMatt Macy if (res == 0) 4932*eda14cbcSMatt Macy return (0); 4933*eda14cbcSMatt Macy if (res > 0) 4934*eda14cbcSMatt Macy return (1); 4935*eda14cbcSMatt Macy else 4936*eda14cbcSMatt Macy return (-1); 4937*eda14cbcSMatt Macy } 4938*eda14cbcSMatt Macy 4939*eda14cbcSMatt Macy /* ARGSUSED */ 4940*eda14cbcSMatt Macy static int 4941*eda14cbcSMatt Macy deleg_perm_compare(const void *larg, const void *rarg, void *unused) 4942*eda14cbcSMatt Macy { 4943*eda14cbcSMatt Macy const deleg_perm_node_t *l = larg; 4944*eda14cbcSMatt Macy const deleg_perm_node_t *r = rarg; 4945*eda14cbcSMatt Macy int res = strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name, 4946*eda14cbcSMatt Macy ZFS_MAX_DELEG_NAME-1); 4947*eda14cbcSMatt Macy 4948*eda14cbcSMatt Macy if (res == 0) 4949*eda14cbcSMatt Macy return (0); 4950*eda14cbcSMatt Macy 4951*eda14cbcSMatt Macy if (res > 0) 4952*eda14cbcSMatt Macy return (1); 4953*eda14cbcSMatt Macy else 4954*eda14cbcSMatt Macy return (-1); 4955*eda14cbcSMatt Macy } 4956*eda14cbcSMatt Macy 4957*eda14cbcSMatt Macy static inline void 4958*eda14cbcSMatt Macy fs_perm_set_init(fs_perm_set_t *fspset) 4959*eda14cbcSMatt Macy { 4960*eda14cbcSMatt Macy bzero(fspset, sizeof (fs_perm_set_t)); 4961*eda14cbcSMatt Macy 4962*eda14cbcSMatt Macy if ((fspset->fsps_list_pool = uu_list_pool_create("fsps_list_pool", 4963*eda14cbcSMatt Macy sizeof (fs_perm_node_t), offsetof(fs_perm_node_t, fspn_list_node), 4964*eda14cbcSMatt Macy NULL, UU_DEFAULT)) == NULL) 4965*eda14cbcSMatt Macy nomem(); 4966*eda14cbcSMatt Macy if ((fspset->fsps_list = uu_list_create(fspset->fsps_list_pool, NULL, 4967*eda14cbcSMatt Macy UU_DEFAULT)) == NULL) 4968*eda14cbcSMatt Macy nomem(); 4969*eda14cbcSMatt Macy 4970*eda14cbcSMatt Macy if ((fspset->fsps_named_set_avl_pool = uu_avl_pool_create( 4971*eda14cbcSMatt Macy "named_set_avl_pool", sizeof (who_perm_node_t), offsetof( 4972*eda14cbcSMatt Macy who_perm_node_t, who_avl_node), who_perm_compare, 4973*eda14cbcSMatt Macy UU_DEFAULT)) == NULL) 4974*eda14cbcSMatt Macy nomem(); 4975*eda14cbcSMatt Macy 4976*eda14cbcSMatt Macy if ((fspset->fsps_who_perm_avl_pool = uu_avl_pool_create( 4977*eda14cbcSMatt Macy "who_perm_avl_pool", sizeof (who_perm_node_t), offsetof( 4978*eda14cbcSMatt Macy who_perm_node_t, who_avl_node), who_perm_compare, 4979*eda14cbcSMatt Macy UU_DEFAULT)) == NULL) 4980*eda14cbcSMatt Macy nomem(); 4981*eda14cbcSMatt Macy 4982*eda14cbcSMatt Macy if ((fspset->fsps_deleg_perm_avl_pool = uu_avl_pool_create( 4983*eda14cbcSMatt Macy "deleg_perm_avl_pool", sizeof (deleg_perm_node_t), offsetof( 4984*eda14cbcSMatt Macy deleg_perm_node_t, dpn_avl_node), deleg_perm_compare, UU_DEFAULT)) 4985*eda14cbcSMatt Macy == NULL) 4986*eda14cbcSMatt Macy nomem(); 4987*eda14cbcSMatt Macy } 4988*eda14cbcSMatt Macy 4989*eda14cbcSMatt Macy static inline void fs_perm_fini(fs_perm_t *); 4990*eda14cbcSMatt Macy static inline void who_perm_fini(who_perm_t *); 4991*eda14cbcSMatt Macy 4992*eda14cbcSMatt Macy static inline void 4993*eda14cbcSMatt Macy fs_perm_set_fini(fs_perm_set_t *fspset) 4994*eda14cbcSMatt Macy { 4995*eda14cbcSMatt Macy fs_perm_node_t *node = uu_list_first(fspset->fsps_list); 4996*eda14cbcSMatt Macy 4997*eda14cbcSMatt Macy while (node != NULL) { 4998*eda14cbcSMatt Macy fs_perm_node_t *next_node = 4999*eda14cbcSMatt Macy uu_list_next(fspset->fsps_list, node); 5000*eda14cbcSMatt Macy fs_perm_t *fsperm = &node->fspn_fsperm; 5001*eda14cbcSMatt Macy fs_perm_fini(fsperm); 5002*eda14cbcSMatt Macy uu_list_remove(fspset->fsps_list, node); 5003*eda14cbcSMatt Macy free(node); 5004*eda14cbcSMatt Macy node = next_node; 5005*eda14cbcSMatt Macy } 5006*eda14cbcSMatt Macy 5007*eda14cbcSMatt Macy uu_avl_pool_destroy(fspset->fsps_named_set_avl_pool); 5008*eda14cbcSMatt Macy uu_avl_pool_destroy(fspset->fsps_who_perm_avl_pool); 5009*eda14cbcSMatt Macy uu_avl_pool_destroy(fspset->fsps_deleg_perm_avl_pool); 5010*eda14cbcSMatt Macy } 5011*eda14cbcSMatt Macy 5012*eda14cbcSMatt Macy static inline void 5013*eda14cbcSMatt Macy deleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type, 5014*eda14cbcSMatt Macy const char *name) 5015*eda14cbcSMatt Macy { 5016*eda14cbcSMatt Macy deleg_perm->dp_who_type = type; 5017*eda14cbcSMatt Macy deleg_perm->dp_name = name; 5018*eda14cbcSMatt Macy } 5019*eda14cbcSMatt Macy 5020*eda14cbcSMatt Macy static inline void 5021*eda14cbcSMatt Macy who_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm, 5022*eda14cbcSMatt Macy zfs_deleg_who_type_t type, const char *name) 5023*eda14cbcSMatt Macy { 5024*eda14cbcSMatt Macy uu_avl_pool_t *pool; 5025*eda14cbcSMatt Macy pool = fsperm->fsp_set->fsps_deleg_perm_avl_pool; 5026*eda14cbcSMatt Macy 5027*eda14cbcSMatt Macy bzero(who_perm, sizeof (who_perm_t)); 5028*eda14cbcSMatt Macy 5029*eda14cbcSMatt Macy if ((who_perm->who_deleg_perm_avl = uu_avl_create(pool, NULL, 5030*eda14cbcSMatt Macy UU_DEFAULT)) == NULL) 5031*eda14cbcSMatt Macy nomem(); 5032*eda14cbcSMatt Macy 5033*eda14cbcSMatt Macy who_perm->who_type = type; 5034*eda14cbcSMatt Macy who_perm->who_name = name; 5035*eda14cbcSMatt Macy who_perm->who_fsperm = fsperm; 5036*eda14cbcSMatt Macy } 5037*eda14cbcSMatt Macy 5038*eda14cbcSMatt Macy static inline void 5039*eda14cbcSMatt Macy who_perm_fini(who_perm_t *who_perm) 5040*eda14cbcSMatt Macy { 5041*eda14cbcSMatt Macy deleg_perm_node_t *node = uu_avl_first(who_perm->who_deleg_perm_avl); 5042*eda14cbcSMatt Macy 5043*eda14cbcSMatt Macy while (node != NULL) { 5044*eda14cbcSMatt Macy deleg_perm_node_t *next_node = 5045*eda14cbcSMatt Macy uu_avl_next(who_perm->who_deleg_perm_avl, node); 5046*eda14cbcSMatt Macy 5047*eda14cbcSMatt Macy uu_avl_remove(who_perm->who_deleg_perm_avl, node); 5048*eda14cbcSMatt Macy free(node); 5049*eda14cbcSMatt Macy node = next_node; 5050*eda14cbcSMatt Macy } 5051*eda14cbcSMatt Macy 5052*eda14cbcSMatt Macy uu_avl_destroy(who_perm->who_deleg_perm_avl); 5053*eda14cbcSMatt Macy } 5054*eda14cbcSMatt Macy 5055*eda14cbcSMatt Macy static inline void 5056*eda14cbcSMatt Macy fs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname) 5057*eda14cbcSMatt Macy { 5058*eda14cbcSMatt Macy uu_avl_pool_t *nset_pool = fspset->fsps_named_set_avl_pool; 5059*eda14cbcSMatt Macy uu_avl_pool_t *who_pool = fspset->fsps_who_perm_avl_pool; 5060*eda14cbcSMatt Macy 5061*eda14cbcSMatt Macy bzero(fsperm, sizeof (fs_perm_t)); 5062*eda14cbcSMatt Macy 5063*eda14cbcSMatt Macy if ((fsperm->fsp_sc_avl = uu_avl_create(nset_pool, NULL, UU_DEFAULT)) 5064*eda14cbcSMatt Macy == NULL) 5065*eda14cbcSMatt Macy nomem(); 5066*eda14cbcSMatt Macy 5067*eda14cbcSMatt Macy if ((fsperm->fsp_uge_avl = uu_avl_create(who_pool, NULL, UU_DEFAULT)) 5068*eda14cbcSMatt Macy == NULL) 5069*eda14cbcSMatt Macy nomem(); 5070*eda14cbcSMatt Macy 5071*eda14cbcSMatt Macy fsperm->fsp_set = fspset; 5072*eda14cbcSMatt Macy fsperm->fsp_name = fsname; 5073*eda14cbcSMatt Macy } 5074*eda14cbcSMatt Macy 5075*eda14cbcSMatt Macy static inline void 5076*eda14cbcSMatt Macy fs_perm_fini(fs_perm_t *fsperm) 5077*eda14cbcSMatt Macy { 5078*eda14cbcSMatt Macy who_perm_node_t *node = uu_avl_first(fsperm->fsp_sc_avl); 5079*eda14cbcSMatt Macy while (node != NULL) { 5080*eda14cbcSMatt Macy who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_sc_avl, 5081*eda14cbcSMatt Macy node); 5082*eda14cbcSMatt Macy who_perm_t *who_perm = &node->who_perm; 5083*eda14cbcSMatt Macy who_perm_fini(who_perm); 5084*eda14cbcSMatt Macy uu_avl_remove(fsperm->fsp_sc_avl, node); 5085*eda14cbcSMatt Macy free(node); 5086*eda14cbcSMatt Macy node = next_node; 5087*eda14cbcSMatt Macy } 5088*eda14cbcSMatt Macy 5089*eda14cbcSMatt Macy node = uu_avl_first(fsperm->fsp_uge_avl); 5090*eda14cbcSMatt Macy while (node != NULL) { 5091*eda14cbcSMatt Macy who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_uge_avl, 5092*eda14cbcSMatt Macy node); 5093*eda14cbcSMatt Macy who_perm_t *who_perm = &node->who_perm; 5094*eda14cbcSMatt Macy who_perm_fini(who_perm); 5095*eda14cbcSMatt Macy uu_avl_remove(fsperm->fsp_uge_avl, node); 5096*eda14cbcSMatt Macy free(node); 5097*eda14cbcSMatt Macy node = next_node; 5098*eda14cbcSMatt Macy } 5099*eda14cbcSMatt Macy 5100*eda14cbcSMatt Macy uu_avl_destroy(fsperm->fsp_sc_avl); 5101*eda14cbcSMatt Macy uu_avl_destroy(fsperm->fsp_uge_avl); 5102*eda14cbcSMatt Macy } 5103*eda14cbcSMatt Macy 5104*eda14cbcSMatt Macy static void 5105*eda14cbcSMatt Macy set_deleg_perm_node(uu_avl_t *avl, deleg_perm_node_t *node, 5106*eda14cbcSMatt Macy zfs_deleg_who_type_t who_type, const char *name, char locality) 5107*eda14cbcSMatt Macy { 5108*eda14cbcSMatt Macy uu_avl_index_t idx = 0; 5109*eda14cbcSMatt Macy 5110*eda14cbcSMatt Macy deleg_perm_node_t *found_node = NULL; 5111*eda14cbcSMatt Macy deleg_perm_t *deleg_perm = &node->dpn_perm; 5112*eda14cbcSMatt Macy 5113*eda14cbcSMatt Macy deleg_perm_init(deleg_perm, who_type, name); 5114*eda14cbcSMatt Macy 5115*eda14cbcSMatt Macy if ((found_node = uu_avl_find(avl, node, NULL, &idx)) 5116*eda14cbcSMatt Macy == NULL) 5117*eda14cbcSMatt Macy uu_avl_insert(avl, node, idx); 5118*eda14cbcSMatt Macy else { 5119*eda14cbcSMatt Macy node = found_node; 5120*eda14cbcSMatt Macy deleg_perm = &node->dpn_perm; 5121*eda14cbcSMatt Macy } 5122*eda14cbcSMatt Macy 5123*eda14cbcSMatt Macy 5124*eda14cbcSMatt Macy switch (locality) { 5125*eda14cbcSMatt Macy case ZFS_DELEG_LOCAL: 5126*eda14cbcSMatt Macy deleg_perm->dp_local = B_TRUE; 5127*eda14cbcSMatt Macy break; 5128*eda14cbcSMatt Macy case ZFS_DELEG_DESCENDENT: 5129*eda14cbcSMatt Macy deleg_perm->dp_descend = B_TRUE; 5130*eda14cbcSMatt Macy break; 5131*eda14cbcSMatt Macy case ZFS_DELEG_NA: 5132*eda14cbcSMatt Macy break; 5133*eda14cbcSMatt Macy default: 5134*eda14cbcSMatt Macy assert(B_FALSE); /* invalid locality */ 5135*eda14cbcSMatt Macy } 5136*eda14cbcSMatt Macy } 5137*eda14cbcSMatt Macy 5138*eda14cbcSMatt Macy static inline int 5139*eda14cbcSMatt Macy parse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality) 5140*eda14cbcSMatt Macy { 5141*eda14cbcSMatt Macy nvpair_t *nvp = NULL; 5142*eda14cbcSMatt Macy fs_perm_set_t *fspset = who_perm->who_fsperm->fsp_set; 5143*eda14cbcSMatt Macy uu_avl_t *avl = who_perm->who_deleg_perm_avl; 5144*eda14cbcSMatt Macy zfs_deleg_who_type_t who_type = who_perm->who_type; 5145*eda14cbcSMatt Macy 5146*eda14cbcSMatt Macy while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 5147*eda14cbcSMatt Macy const char *name = nvpair_name(nvp); 5148*eda14cbcSMatt Macy data_type_t type = nvpair_type(nvp); 5149*eda14cbcSMatt Macy uu_avl_pool_t *avl_pool = fspset->fsps_deleg_perm_avl_pool; 5150*eda14cbcSMatt Macy deleg_perm_node_t *node = 5151*eda14cbcSMatt Macy safe_malloc(sizeof (deleg_perm_node_t)); 5152*eda14cbcSMatt Macy 5153*eda14cbcSMatt Macy VERIFY(type == DATA_TYPE_BOOLEAN); 5154*eda14cbcSMatt Macy 5155*eda14cbcSMatt Macy uu_avl_node_init(node, &node->dpn_avl_node, avl_pool); 5156*eda14cbcSMatt Macy set_deleg_perm_node(avl, node, who_type, name, locality); 5157*eda14cbcSMatt Macy } 5158*eda14cbcSMatt Macy 5159*eda14cbcSMatt Macy return (0); 5160*eda14cbcSMatt Macy } 5161*eda14cbcSMatt Macy 5162*eda14cbcSMatt Macy static inline int 5163*eda14cbcSMatt Macy parse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl) 5164*eda14cbcSMatt Macy { 5165*eda14cbcSMatt Macy nvpair_t *nvp = NULL; 5166*eda14cbcSMatt Macy fs_perm_set_t *fspset = fsperm->fsp_set; 5167*eda14cbcSMatt Macy 5168*eda14cbcSMatt Macy while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 5169*eda14cbcSMatt Macy nvlist_t *nvl2 = NULL; 5170*eda14cbcSMatt Macy const char *name = nvpair_name(nvp); 5171*eda14cbcSMatt Macy uu_avl_t *avl = NULL; 5172*eda14cbcSMatt Macy uu_avl_pool_t *avl_pool = NULL; 5173*eda14cbcSMatt Macy zfs_deleg_who_type_t perm_type = name[0]; 5174*eda14cbcSMatt Macy char perm_locality = name[1]; 5175*eda14cbcSMatt Macy const char *perm_name = name + 3; 5176*eda14cbcSMatt Macy who_perm_t *who_perm = NULL; 5177*eda14cbcSMatt Macy 5178*eda14cbcSMatt Macy assert('$' == name[2]); 5179*eda14cbcSMatt Macy 5180*eda14cbcSMatt Macy if (nvpair_value_nvlist(nvp, &nvl2) != 0) 5181*eda14cbcSMatt Macy return (-1); 5182*eda14cbcSMatt Macy 5183*eda14cbcSMatt Macy switch (perm_type) { 5184*eda14cbcSMatt Macy case ZFS_DELEG_CREATE: 5185*eda14cbcSMatt Macy case ZFS_DELEG_CREATE_SETS: 5186*eda14cbcSMatt Macy case ZFS_DELEG_NAMED_SET: 5187*eda14cbcSMatt Macy case ZFS_DELEG_NAMED_SET_SETS: 5188*eda14cbcSMatt Macy avl_pool = fspset->fsps_named_set_avl_pool; 5189*eda14cbcSMatt Macy avl = fsperm->fsp_sc_avl; 5190*eda14cbcSMatt Macy break; 5191*eda14cbcSMatt Macy case ZFS_DELEG_USER: 5192*eda14cbcSMatt Macy case ZFS_DELEG_USER_SETS: 5193*eda14cbcSMatt Macy case ZFS_DELEG_GROUP: 5194*eda14cbcSMatt Macy case ZFS_DELEG_GROUP_SETS: 5195*eda14cbcSMatt Macy case ZFS_DELEG_EVERYONE: 5196*eda14cbcSMatt Macy case ZFS_DELEG_EVERYONE_SETS: 5197*eda14cbcSMatt Macy avl_pool = fspset->fsps_who_perm_avl_pool; 5198*eda14cbcSMatt Macy avl = fsperm->fsp_uge_avl; 5199*eda14cbcSMatt Macy break; 5200*eda14cbcSMatt Macy 5201*eda14cbcSMatt Macy default: 5202*eda14cbcSMatt Macy assert(!"unhandled zfs_deleg_who_type_t"); 5203*eda14cbcSMatt Macy } 5204*eda14cbcSMatt Macy 5205*eda14cbcSMatt Macy who_perm_node_t *found_node = NULL; 5206*eda14cbcSMatt Macy who_perm_node_t *node = safe_malloc( 5207*eda14cbcSMatt Macy sizeof (who_perm_node_t)); 5208*eda14cbcSMatt Macy who_perm = &node->who_perm; 5209*eda14cbcSMatt Macy uu_avl_index_t idx = 0; 5210*eda14cbcSMatt Macy 5211*eda14cbcSMatt Macy uu_avl_node_init(node, &node->who_avl_node, avl_pool); 5212*eda14cbcSMatt Macy who_perm_init(who_perm, fsperm, perm_type, perm_name); 5213*eda14cbcSMatt Macy 5214*eda14cbcSMatt Macy if ((found_node = uu_avl_find(avl, node, NULL, &idx)) 5215*eda14cbcSMatt Macy == NULL) { 5216*eda14cbcSMatt Macy if (avl == fsperm->fsp_uge_avl) { 5217*eda14cbcSMatt Macy uid_t rid = 0; 5218*eda14cbcSMatt Macy struct passwd *p = NULL; 5219*eda14cbcSMatt Macy struct group *g = NULL; 5220*eda14cbcSMatt Macy const char *nice_name = NULL; 5221*eda14cbcSMatt Macy 5222*eda14cbcSMatt Macy switch (perm_type) { 5223*eda14cbcSMatt Macy case ZFS_DELEG_USER_SETS: 5224*eda14cbcSMatt Macy case ZFS_DELEG_USER: 5225*eda14cbcSMatt Macy rid = atoi(perm_name); 5226*eda14cbcSMatt Macy p = getpwuid(rid); 5227*eda14cbcSMatt Macy if (p) 5228*eda14cbcSMatt Macy nice_name = p->pw_name; 5229*eda14cbcSMatt Macy break; 5230*eda14cbcSMatt Macy case ZFS_DELEG_GROUP_SETS: 5231*eda14cbcSMatt Macy case ZFS_DELEG_GROUP: 5232*eda14cbcSMatt Macy rid = atoi(perm_name); 5233*eda14cbcSMatt Macy g = getgrgid(rid); 5234*eda14cbcSMatt Macy if (g) 5235*eda14cbcSMatt Macy nice_name = g->gr_name; 5236*eda14cbcSMatt Macy break; 5237*eda14cbcSMatt Macy 5238*eda14cbcSMatt Macy default: 5239*eda14cbcSMatt Macy break; 5240*eda14cbcSMatt Macy } 5241*eda14cbcSMatt Macy 5242*eda14cbcSMatt Macy if (nice_name != NULL) { 5243*eda14cbcSMatt Macy (void) strlcpy( 5244*eda14cbcSMatt Macy node->who_perm.who_ug_name, 5245*eda14cbcSMatt Macy nice_name, 256); 5246*eda14cbcSMatt Macy } else { 5247*eda14cbcSMatt Macy /* User or group unknown */ 5248*eda14cbcSMatt Macy (void) snprintf( 5249*eda14cbcSMatt Macy node->who_perm.who_ug_name, 5250*eda14cbcSMatt Macy sizeof (node->who_perm.who_ug_name), 5251*eda14cbcSMatt Macy "(unknown: %d)", rid); 5252*eda14cbcSMatt Macy } 5253*eda14cbcSMatt Macy } 5254*eda14cbcSMatt Macy 5255*eda14cbcSMatt Macy uu_avl_insert(avl, node, idx); 5256*eda14cbcSMatt Macy } else { 5257*eda14cbcSMatt Macy node = found_node; 5258*eda14cbcSMatt Macy who_perm = &node->who_perm; 5259*eda14cbcSMatt Macy } 5260*eda14cbcSMatt Macy 5261*eda14cbcSMatt Macy assert(who_perm != NULL); 5262*eda14cbcSMatt Macy (void) parse_who_perm(who_perm, nvl2, perm_locality); 5263*eda14cbcSMatt Macy } 5264*eda14cbcSMatt Macy 5265*eda14cbcSMatt Macy return (0); 5266*eda14cbcSMatt Macy } 5267*eda14cbcSMatt Macy 5268*eda14cbcSMatt Macy static inline int 5269*eda14cbcSMatt Macy parse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl) 5270*eda14cbcSMatt Macy { 5271*eda14cbcSMatt Macy nvpair_t *nvp = NULL; 5272*eda14cbcSMatt Macy uu_avl_index_t idx = 0; 5273*eda14cbcSMatt Macy 5274*eda14cbcSMatt Macy while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 5275*eda14cbcSMatt Macy nvlist_t *nvl2 = NULL; 5276*eda14cbcSMatt Macy const char *fsname = nvpair_name(nvp); 5277*eda14cbcSMatt Macy data_type_t type = nvpair_type(nvp); 5278*eda14cbcSMatt Macy fs_perm_t *fsperm = NULL; 5279*eda14cbcSMatt Macy fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t)); 5280*eda14cbcSMatt Macy if (node == NULL) 5281*eda14cbcSMatt Macy nomem(); 5282*eda14cbcSMatt Macy 5283*eda14cbcSMatt Macy fsperm = &node->fspn_fsperm; 5284*eda14cbcSMatt Macy 5285*eda14cbcSMatt Macy VERIFY(DATA_TYPE_NVLIST == type); 5286*eda14cbcSMatt Macy 5287*eda14cbcSMatt Macy uu_list_node_init(node, &node->fspn_list_node, 5288*eda14cbcSMatt Macy fspset->fsps_list_pool); 5289*eda14cbcSMatt Macy 5290*eda14cbcSMatt Macy idx = uu_list_numnodes(fspset->fsps_list); 5291*eda14cbcSMatt Macy fs_perm_init(fsperm, fspset, fsname); 5292*eda14cbcSMatt Macy 5293*eda14cbcSMatt Macy if (nvpair_value_nvlist(nvp, &nvl2) != 0) 5294*eda14cbcSMatt Macy return (-1); 5295*eda14cbcSMatt Macy 5296*eda14cbcSMatt Macy (void) parse_fs_perm(fsperm, nvl2); 5297*eda14cbcSMatt Macy 5298*eda14cbcSMatt Macy uu_list_insert(fspset->fsps_list, node, idx); 5299*eda14cbcSMatt Macy } 5300*eda14cbcSMatt Macy 5301*eda14cbcSMatt Macy return (0); 5302*eda14cbcSMatt Macy } 5303*eda14cbcSMatt Macy 5304*eda14cbcSMatt Macy static inline const char * 5305*eda14cbcSMatt Macy deleg_perm_comment(zfs_deleg_note_t note) 5306*eda14cbcSMatt Macy { 5307*eda14cbcSMatt Macy const char *str = ""; 5308*eda14cbcSMatt Macy 5309*eda14cbcSMatt Macy /* subcommands */ 5310*eda14cbcSMatt Macy switch (note) { 5311*eda14cbcSMatt Macy /* SUBCOMMANDS */ 5312*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_ALLOW: 5313*eda14cbcSMatt Macy str = gettext("Must also have the permission that is being" 5314*eda14cbcSMatt Macy "\n\t\t\t\tallowed"); 5315*eda14cbcSMatt Macy break; 5316*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_CLONE: 5317*eda14cbcSMatt Macy str = gettext("Must also have the 'create' ability and 'mount'" 5318*eda14cbcSMatt Macy "\n\t\t\t\tability in the origin file system"); 5319*eda14cbcSMatt Macy break; 5320*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_CREATE: 5321*eda14cbcSMatt Macy str = gettext("Must also have the 'mount' ability"); 5322*eda14cbcSMatt Macy break; 5323*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_DESTROY: 5324*eda14cbcSMatt Macy str = gettext("Must also have the 'mount' ability"); 5325*eda14cbcSMatt Macy break; 5326*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_DIFF: 5327*eda14cbcSMatt Macy str = gettext("Allows lookup of paths within a dataset;" 5328*eda14cbcSMatt Macy "\n\t\t\t\tgiven an object number. Ordinary users need this" 5329*eda14cbcSMatt Macy "\n\t\t\t\tin order to use zfs diff"); 5330*eda14cbcSMatt Macy break; 5331*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_HOLD: 5332*eda14cbcSMatt Macy str = gettext("Allows adding a user hold to a snapshot"); 5333*eda14cbcSMatt Macy break; 5334*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_MOUNT: 5335*eda14cbcSMatt Macy str = gettext("Allows mount/umount of ZFS datasets"); 5336*eda14cbcSMatt Macy break; 5337*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_PROMOTE: 5338*eda14cbcSMatt Macy str = gettext("Must also have the 'mount'\n\t\t\t\tand" 5339*eda14cbcSMatt Macy " 'promote' ability in the origin file system"); 5340*eda14cbcSMatt Macy break; 5341*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_RECEIVE: 5342*eda14cbcSMatt Macy str = gettext("Must also have the 'mount' and 'create'" 5343*eda14cbcSMatt Macy " ability"); 5344*eda14cbcSMatt Macy break; 5345*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_RELEASE: 5346*eda14cbcSMatt Macy str = gettext("Allows releasing a user hold which\n\t\t\t\t" 5347*eda14cbcSMatt Macy "might destroy the snapshot"); 5348*eda14cbcSMatt Macy break; 5349*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_RENAME: 5350*eda14cbcSMatt Macy str = gettext("Must also have the 'mount' and 'create'" 5351*eda14cbcSMatt Macy "\n\t\t\t\tability in the new parent"); 5352*eda14cbcSMatt Macy break; 5353*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_ROLLBACK: 5354*eda14cbcSMatt Macy str = gettext(""); 5355*eda14cbcSMatt Macy break; 5356*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_SEND: 5357*eda14cbcSMatt Macy str = gettext(""); 5358*eda14cbcSMatt Macy break; 5359*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_SHARE: 5360*eda14cbcSMatt Macy str = gettext("Allows sharing file systems over NFS or SMB" 5361*eda14cbcSMatt Macy "\n\t\t\t\tprotocols"); 5362*eda14cbcSMatt Macy break; 5363*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_SNAPSHOT: 5364*eda14cbcSMatt Macy str = gettext(""); 5365*eda14cbcSMatt Macy break; 5366*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_LOAD_KEY: 5367*eda14cbcSMatt Macy str = gettext("Allows loading or unloading an encryption key"); 5368*eda14cbcSMatt Macy break; 5369*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_CHANGE_KEY: 5370*eda14cbcSMatt Macy str = gettext("Allows changing or adding an encryption key"); 5371*eda14cbcSMatt Macy break; 5372*eda14cbcSMatt Macy /* 5373*eda14cbcSMatt Macy * case ZFS_DELEG_NOTE_VSCAN: 5374*eda14cbcSMatt Macy * str = gettext(""); 5375*eda14cbcSMatt Macy * break; 5376*eda14cbcSMatt Macy */ 5377*eda14cbcSMatt Macy /* OTHER */ 5378*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_GROUPQUOTA: 5379*eda14cbcSMatt Macy str = gettext("Allows accessing any groupquota@... property"); 5380*eda14cbcSMatt Macy break; 5381*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_GROUPUSED: 5382*eda14cbcSMatt Macy str = gettext("Allows reading any groupused@... property"); 5383*eda14cbcSMatt Macy break; 5384*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_USERPROP: 5385*eda14cbcSMatt Macy str = gettext("Allows changing any user property"); 5386*eda14cbcSMatt Macy break; 5387*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_USERQUOTA: 5388*eda14cbcSMatt Macy str = gettext("Allows accessing any userquota@... property"); 5389*eda14cbcSMatt Macy break; 5390*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_USERUSED: 5391*eda14cbcSMatt Macy str = gettext("Allows reading any userused@... property"); 5392*eda14cbcSMatt Macy break; 5393*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_USEROBJQUOTA: 5394*eda14cbcSMatt Macy str = gettext("Allows accessing any userobjquota@... property"); 5395*eda14cbcSMatt Macy break; 5396*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_GROUPOBJQUOTA: 5397*eda14cbcSMatt Macy str = gettext("Allows accessing any \n\t\t\t\t" 5398*eda14cbcSMatt Macy "groupobjquota@... property"); 5399*eda14cbcSMatt Macy break; 5400*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_GROUPOBJUSED: 5401*eda14cbcSMatt Macy str = gettext("Allows reading any groupobjused@... property"); 5402*eda14cbcSMatt Macy break; 5403*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_USEROBJUSED: 5404*eda14cbcSMatt Macy str = gettext("Allows reading any userobjused@... property"); 5405*eda14cbcSMatt Macy break; 5406*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_PROJECTQUOTA: 5407*eda14cbcSMatt Macy str = gettext("Allows accessing any projectquota@... property"); 5408*eda14cbcSMatt Macy break; 5409*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_PROJECTOBJQUOTA: 5410*eda14cbcSMatt Macy str = gettext("Allows accessing any \n\t\t\t\t" 5411*eda14cbcSMatt Macy "projectobjquota@... property"); 5412*eda14cbcSMatt Macy break; 5413*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_PROJECTUSED: 5414*eda14cbcSMatt Macy str = gettext("Allows reading any projectused@... property"); 5415*eda14cbcSMatt Macy break; 5416*eda14cbcSMatt Macy case ZFS_DELEG_NOTE_PROJECTOBJUSED: 5417*eda14cbcSMatt Macy str = gettext("Allows accessing any \n\t\t\t\t" 5418*eda14cbcSMatt Macy "projectobjused@... property"); 5419*eda14cbcSMatt Macy break; 5420*eda14cbcSMatt Macy /* other */ 5421*eda14cbcSMatt Macy default: 5422*eda14cbcSMatt Macy str = ""; 5423*eda14cbcSMatt Macy } 5424*eda14cbcSMatt Macy 5425*eda14cbcSMatt Macy return (str); 5426*eda14cbcSMatt Macy } 5427*eda14cbcSMatt Macy 5428*eda14cbcSMatt Macy struct allow_opts { 5429*eda14cbcSMatt Macy boolean_t local; 5430*eda14cbcSMatt Macy boolean_t descend; 5431*eda14cbcSMatt Macy boolean_t user; 5432*eda14cbcSMatt Macy boolean_t group; 5433*eda14cbcSMatt Macy boolean_t everyone; 5434*eda14cbcSMatt Macy boolean_t create; 5435*eda14cbcSMatt Macy boolean_t set; 5436*eda14cbcSMatt Macy boolean_t recursive; /* unallow only */ 5437*eda14cbcSMatt Macy boolean_t prt_usage; 5438*eda14cbcSMatt Macy 5439*eda14cbcSMatt Macy boolean_t prt_perms; 5440*eda14cbcSMatt Macy char *who; 5441*eda14cbcSMatt Macy char *perms; 5442*eda14cbcSMatt Macy const char *dataset; 5443*eda14cbcSMatt Macy }; 5444*eda14cbcSMatt Macy 5445*eda14cbcSMatt Macy static inline int 5446*eda14cbcSMatt Macy prop_cmp(const void *a, const void *b) 5447*eda14cbcSMatt Macy { 5448*eda14cbcSMatt Macy const char *str1 = *(const char **)a; 5449*eda14cbcSMatt Macy const char *str2 = *(const char **)b; 5450*eda14cbcSMatt Macy return (strcmp(str1, str2)); 5451*eda14cbcSMatt Macy } 5452*eda14cbcSMatt Macy 5453*eda14cbcSMatt Macy static void 5454*eda14cbcSMatt Macy allow_usage(boolean_t un, boolean_t requested, const char *msg) 5455*eda14cbcSMatt Macy { 5456*eda14cbcSMatt Macy const char *opt_desc[] = { 5457*eda14cbcSMatt Macy "-h", gettext("show this help message and exit"), 5458*eda14cbcSMatt Macy "-l", gettext("set permission locally"), 5459*eda14cbcSMatt Macy "-d", gettext("set permission for descents"), 5460*eda14cbcSMatt Macy "-u", gettext("set permission for user"), 5461*eda14cbcSMatt Macy "-g", gettext("set permission for group"), 5462*eda14cbcSMatt Macy "-e", gettext("set permission for everyone"), 5463*eda14cbcSMatt Macy "-c", gettext("set create time permission"), 5464*eda14cbcSMatt Macy "-s", gettext("define permission set"), 5465*eda14cbcSMatt Macy /* unallow only */ 5466*eda14cbcSMatt Macy "-r", gettext("remove permissions recursively"), 5467*eda14cbcSMatt Macy }; 5468*eda14cbcSMatt Macy size_t unallow_size = sizeof (opt_desc) / sizeof (char *); 5469*eda14cbcSMatt Macy size_t allow_size = unallow_size - 2; 5470*eda14cbcSMatt Macy const char *props[ZFS_NUM_PROPS]; 5471*eda14cbcSMatt Macy int i; 5472*eda14cbcSMatt Macy size_t count = 0; 5473*eda14cbcSMatt Macy FILE *fp = requested ? stdout : stderr; 5474*eda14cbcSMatt Macy zprop_desc_t *pdtbl = zfs_prop_get_table(); 5475*eda14cbcSMatt Macy const char *fmt = gettext("%-16s %-14s\t%s\n"); 5476*eda14cbcSMatt Macy 5477*eda14cbcSMatt Macy (void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW : 5478*eda14cbcSMatt Macy HELP_ALLOW)); 5479*eda14cbcSMatt Macy (void) fprintf(fp, gettext("Options:\n")); 5480*eda14cbcSMatt Macy for (i = 0; i < (un ? unallow_size : allow_size); i += 2) { 5481*eda14cbcSMatt Macy const char *opt = opt_desc[i]; 5482*eda14cbcSMatt Macy const char *optdsc = opt_desc[i + 1]; 5483*eda14cbcSMatt Macy (void) fprintf(fp, gettext(" %-10s %s\n"), opt, optdsc); 5484*eda14cbcSMatt Macy } 5485*eda14cbcSMatt Macy 5486*eda14cbcSMatt Macy (void) fprintf(fp, gettext("\nThe following permissions are " 5487*eda14cbcSMatt Macy "supported:\n\n")); 5488*eda14cbcSMatt Macy (void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"), 5489*eda14cbcSMatt Macy gettext("NOTES")); 5490*eda14cbcSMatt Macy for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) { 5491*eda14cbcSMatt Macy const char *perm_name = zfs_deleg_perm_tbl[i].z_perm; 5492*eda14cbcSMatt Macy zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note; 5493*eda14cbcSMatt Macy const char *perm_type = deleg_perm_type(perm_note); 5494*eda14cbcSMatt Macy const char *perm_comment = deleg_perm_comment(perm_note); 5495*eda14cbcSMatt Macy (void) fprintf(fp, fmt, perm_name, perm_type, perm_comment); 5496*eda14cbcSMatt Macy } 5497*eda14cbcSMatt Macy 5498*eda14cbcSMatt Macy for (i = 0; i < ZFS_NUM_PROPS; i++) { 5499*eda14cbcSMatt Macy zprop_desc_t *pd = &pdtbl[i]; 5500*eda14cbcSMatt Macy if (pd->pd_visible != B_TRUE) 5501*eda14cbcSMatt Macy continue; 5502*eda14cbcSMatt Macy 5503*eda14cbcSMatt Macy if (pd->pd_attr == PROP_READONLY) 5504*eda14cbcSMatt Macy continue; 5505*eda14cbcSMatt Macy 5506*eda14cbcSMatt Macy props[count++] = pd->pd_name; 5507*eda14cbcSMatt Macy } 5508*eda14cbcSMatt Macy props[count] = NULL; 5509*eda14cbcSMatt Macy 5510*eda14cbcSMatt Macy qsort(props, count, sizeof (char *), prop_cmp); 5511*eda14cbcSMatt Macy 5512*eda14cbcSMatt Macy for (i = 0; i < count; i++) 5513*eda14cbcSMatt Macy (void) fprintf(fp, fmt, props[i], gettext("property"), ""); 5514*eda14cbcSMatt Macy 5515*eda14cbcSMatt Macy if (msg != NULL) 5516*eda14cbcSMatt Macy (void) fprintf(fp, gettext("\nzfs: error: %s"), msg); 5517*eda14cbcSMatt Macy 5518*eda14cbcSMatt Macy exit(requested ? 0 : 2); 5519*eda14cbcSMatt Macy } 5520*eda14cbcSMatt Macy 5521*eda14cbcSMatt Macy static inline const char * 5522*eda14cbcSMatt Macy munge_args(int argc, char **argv, boolean_t un, size_t expected_argc, 5523*eda14cbcSMatt Macy char **permsp) 5524*eda14cbcSMatt Macy { 5525*eda14cbcSMatt Macy if (un && argc == expected_argc - 1) 5526*eda14cbcSMatt Macy *permsp = NULL; 5527*eda14cbcSMatt Macy else if (argc == expected_argc) 5528*eda14cbcSMatt Macy *permsp = argv[argc - 2]; 5529*eda14cbcSMatt Macy else 5530*eda14cbcSMatt Macy allow_usage(un, B_FALSE, 5531*eda14cbcSMatt Macy gettext("wrong number of parameters\n")); 5532*eda14cbcSMatt Macy 5533*eda14cbcSMatt Macy return (argv[argc - 1]); 5534*eda14cbcSMatt Macy } 5535*eda14cbcSMatt Macy 5536*eda14cbcSMatt Macy static void 5537*eda14cbcSMatt Macy parse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts) 5538*eda14cbcSMatt Macy { 5539*eda14cbcSMatt Macy int uge_sum = opts->user + opts->group + opts->everyone; 5540*eda14cbcSMatt Macy int csuge_sum = opts->create + opts->set + uge_sum; 5541*eda14cbcSMatt Macy int ldcsuge_sum = csuge_sum + opts->local + opts->descend; 5542*eda14cbcSMatt Macy int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum; 5543*eda14cbcSMatt Macy 5544*eda14cbcSMatt Macy if (uge_sum > 1) 5545*eda14cbcSMatt Macy allow_usage(un, B_FALSE, 5546*eda14cbcSMatt Macy gettext("-u, -g, and -e are mutually exclusive\n")); 5547*eda14cbcSMatt Macy 5548*eda14cbcSMatt Macy if (opts->prt_usage) { 5549*eda14cbcSMatt Macy if (argc == 0 && all_sum == 0) 5550*eda14cbcSMatt Macy allow_usage(un, B_TRUE, NULL); 5551*eda14cbcSMatt Macy else 5552*eda14cbcSMatt Macy usage(B_FALSE); 5553*eda14cbcSMatt Macy } 5554*eda14cbcSMatt Macy 5555*eda14cbcSMatt Macy if (opts->set) { 5556*eda14cbcSMatt Macy if (csuge_sum > 1) 5557*eda14cbcSMatt Macy allow_usage(un, B_FALSE, 5558*eda14cbcSMatt Macy gettext("invalid options combined with -s\n")); 5559*eda14cbcSMatt Macy 5560*eda14cbcSMatt Macy opts->dataset = munge_args(argc, argv, un, 3, &opts->perms); 5561*eda14cbcSMatt Macy if (argv[0][0] != '@') 5562*eda14cbcSMatt Macy allow_usage(un, B_FALSE, 5563*eda14cbcSMatt Macy gettext("invalid set name: missing '@' prefix\n")); 5564*eda14cbcSMatt Macy opts->who = argv[0]; 5565*eda14cbcSMatt Macy } else if (opts->create) { 5566*eda14cbcSMatt Macy if (ldcsuge_sum > 1) 5567*eda14cbcSMatt Macy allow_usage(un, B_FALSE, 5568*eda14cbcSMatt Macy gettext("invalid options combined with -c\n")); 5569*eda14cbcSMatt Macy opts->dataset = munge_args(argc, argv, un, 2, &opts->perms); 5570*eda14cbcSMatt Macy } else if (opts->everyone) { 5571*eda14cbcSMatt Macy if (csuge_sum > 1) 5572*eda14cbcSMatt Macy allow_usage(un, B_FALSE, 5573*eda14cbcSMatt Macy gettext("invalid options combined with -e\n")); 5574*eda14cbcSMatt Macy opts->dataset = munge_args(argc, argv, un, 2, &opts->perms); 5575*eda14cbcSMatt Macy } else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone") 5576*eda14cbcSMatt Macy == 0) { 5577*eda14cbcSMatt Macy opts->everyone = B_TRUE; 5578*eda14cbcSMatt Macy argc--; 5579*eda14cbcSMatt Macy argv++; 5580*eda14cbcSMatt Macy opts->dataset = munge_args(argc, argv, un, 2, &opts->perms); 5581*eda14cbcSMatt Macy } else if (argc == 1 && !un) { 5582*eda14cbcSMatt Macy opts->prt_perms = B_TRUE; 5583*eda14cbcSMatt Macy opts->dataset = argv[argc-1]; 5584*eda14cbcSMatt Macy } else { 5585*eda14cbcSMatt Macy opts->dataset = munge_args(argc, argv, un, 3, &opts->perms); 5586*eda14cbcSMatt Macy opts->who = argv[0]; 5587*eda14cbcSMatt Macy } 5588*eda14cbcSMatt Macy 5589*eda14cbcSMatt Macy if (!opts->local && !opts->descend) { 5590*eda14cbcSMatt Macy opts->local = B_TRUE; 5591*eda14cbcSMatt Macy opts->descend = B_TRUE; 5592*eda14cbcSMatt Macy } 5593*eda14cbcSMatt Macy } 5594*eda14cbcSMatt Macy 5595*eda14cbcSMatt Macy static void 5596*eda14cbcSMatt Macy store_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend, 5597*eda14cbcSMatt Macy const char *who, char *perms, nvlist_t *top_nvl) 5598*eda14cbcSMatt Macy { 5599*eda14cbcSMatt Macy int i; 5600*eda14cbcSMatt Macy char ld[2] = { '\0', '\0' }; 5601*eda14cbcSMatt Macy char who_buf[MAXNAMELEN + 32]; 5602*eda14cbcSMatt Macy char base_type = '\0'; 5603*eda14cbcSMatt Macy char set_type = '\0'; 5604*eda14cbcSMatt Macy nvlist_t *base_nvl = NULL; 5605*eda14cbcSMatt Macy nvlist_t *set_nvl = NULL; 5606*eda14cbcSMatt Macy nvlist_t *nvl; 5607*eda14cbcSMatt Macy 5608*eda14cbcSMatt Macy if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0) 5609*eda14cbcSMatt Macy nomem(); 5610*eda14cbcSMatt Macy if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) != 0) 5611*eda14cbcSMatt Macy nomem(); 5612*eda14cbcSMatt Macy 5613*eda14cbcSMatt Macy switch (type) { 5614*eda14cbcSMatt Macy case ZFS_DELEG_NAMED_SET_SETS: 5615*eda14cbcSMatt Macy case ZFS_DELEG_NAMED_SET: 5616*eda14cbcSMatt Macy set_type = ZFS_DELEG_NAMED_SET_SETS; 5617*eda14cbcSMatt Macy base_type = ZFS_DELEG_NAMED_SET; 5618*eda14cbcSMatt Macy ld[0] = ZFS_DELEG_NA; 5619*eda14cbcSMatt Macy break; 5620*eda14cbcSMatt Macy case ZFS_DELEG_CREATE_SETS: 5621*eda14cbcSMatt Macy case ZFS_DELEG_CREATE: 5622*eda14cbcSMatt Macy set_type = ZFS_DELEG_CREATE_SETS; 5623*eda14cbcSMatt Macy base_type = ZFS_DELEG_CREATE; 5624*eda14cbcSMatt Macy ld[0] = ZFS_DELEG_NA; 5625*eda14cbcSMatt Macy break; 5626*eda14cbcSMatt Macy case ZFS_DELEG_USER_SETS: 5627*eda14cbcSMatt Macy case ZFS_DELEG_USER: 5628*eda14cbcSMatt Macy set_type = ZFS_DELEG_USER_SETS; 5629*eda14cbcSMatt Macy base_type = ZFS_DELEG_USER; 5630*eda14cbcSMatt Macy if (local) 5631*eda14cbcSMatt Macy ld[0] = ZFS_DELEG_LOCAL; 5632*eda14cbcSMatt Macy if (descend) 5633*eda14cbcSMatt Macy ld[1] = ZFS_DELEG_DESCENDENT; 5634*eda14cbcSMatt Macy break; 5635*eda14cbcSMatt Macy case ZFS_DELEG_GROUP_SETS: 5636*eda14cbcSMatt Macy case ZFS_DELEG_GROUP: 5637*eda14cbcSMatt Macy set_type = ZFS_DELEG_GROUP_SETS; 5638*eda14cbcSMatt Macy base_type = ZFS_DELEG_GROUP; 5639*eda14cbcSMatt Macy if (local) 5640*eda14cbcSMatt Macy ld[0] = ZFS_DELEG_LOCAL; 5641*eda14cbcSMatt Macy if (descend) 5642*eda14cbcSMatt Macy ld[1] = ZFS_DELEG_DESCENDENT; 5643*eda14cbcSMatt Macy break; 5644*eda14cbcSMatt Macy case ZFS_DELEG_EVERYONE_SETS: 5645*eda14cbcSMatt Macy case ZFS_DELEG_EVERYONE: 5646*eda14cbcSMatt Macy set_type = ZFS_DELEG_EVERYONE_SETS; 5647*eda14cbcSMatt Macy base_type = ZFS_DELEG_EVERYONE; 5648*eda14cbcSMatt Macy if (local) 5649*eda14cbcSMatt Macy ld[0] = ZFS_DELEG_LOCAL; 5650*eda14cbcSMatt Macy if (descend) 5651*eda14cbcSMatt Macy ld[1] = ZFS_DELEG_DESCENDENT; 5652*eda14cbcSMatt Macy break; 5653*eda14cbcSMatt Macy 5654*eda14cbcSMatt Macy default: 5655*eda14cbcSMatt Macy assert(set_type != '\0' && base_type != '\0'); 5656*eda14cbcSMatt Macy } 5657*eda14cbcSMatt Macy 5658*eda14cbcSMatt Macy if (perms != NULL) { 5659*eda14cbcSMatt Macy char *curr = perms; 5660*eda14cbcSMatt Macy char *end = curr + strlen(perms); 5661*eda14cbcSMatt Macy 5662*eda14cbcSMatt Macy while (curr < end) { 5663*eda14cbcSMatt Macy char *delim = strchr(curr, ','); 5664*eda14cbcSMatt Macy if (delim == NULL) 5665*eda14cbcSMatt Macy delim = end; 5666*eda14cbcSMatt Macy else 5667*eda14cbcSMatt Macy *delim = '\0'; 5668*eda14cbcSMatt Macy 5669*eda14cbcSMatt Macy if (curr[0] == '@') 5670*eda14cbcSMatt Macy nvl = set_nvl; 5671*eda14cbcSMatt Macy else 5672*eda14cbcSMatt Macy nvl = base_nvl; 5673*eda14cbcSMatt Macy 5674*eda14cbcSMatt Macy (void) nvlist_add_boolean(nvl, curr); 5675*eda14cbcSMatt Macy if (delim != end) 5676*eda14cbcSMatt Macy *delim = ','; 5677*eda14cbcSMatt Macy curr = delim + 1; 5678*eda14cbcSMatt Macy } 5679*eda14cbcSMatt Macy 5680*eda14cbcSMatt Macy for (i = 0; i < 2; i++) { 5681*eda14cbcSMatt Macy char locality = ld[i]; 5682*eda14cbcSMatt Macy if (locality == 0) 5683*eda14cbcSMatt Macy continue; 5684*eda14cbcSMatt Macy 5685*eda14cbcSMatt Macy if (!nvlist_empty(base_nvl)) { 5686*eda14cbcSMatt Macy if (who != NULL) 5687*eda14cbcSMatt Macy (void) snprintf(who_buf, 5688*eda14cbcSMatt Macy sizeof (who_buf), "%c%c$%s", 5689*eda14cbcSMatt Macy base_type, locality, who); 5690*eda14cbcSMatt Macy else 5691*eda14cbcSMatt Macy (void) snprintf(who_buf, 5692*eda14cbcSMatt Macy sizeof (who_buf), "%c%c$", 5693*eda14cbcSMatt Macy base_type, locality); 5694*eda14cbcSMatt Macy 5695*eda14cbcSMatt Macy (void) nvlist_add_nvlist(top_nvl, who_buf, 5696*eda14cbcSMatt Macy base_nvl); 5697*eda14cbcSMatt Macy } 5698*eda14cbcSMatt Macy 5699*eda14cbcSMatt Macy 5700*eda14cbcSMatt Macy if (!nvlist_empty(set_nvl)) { 5701*eda14cbcSMatt Macy if (who != NULL) 5702*eda14cbcSMatt Macy (void) snprintf(who_buf, 5703*eda14cbcSMatt Macy sizeof (who_buf), "%c%c$%s", 5704*eda14cbcSMatt Macy set_type, locality, who); 5705*eda14cbcSMatt Macy else 5706*eda14cbcSMatt Macy (void) snprintf(who_buf, 5707*eda14cbcSMatt Macy sizeof (who_buf), "%c%c$", 5708*eda14cbcSMatt Macy set_type, locality); 5709*eda14cbcSMatt Macy 5710*eda14cbcSMatt Macy (void) nvlist_add_nvlist(top_nvl, who_buf, 5711*eda14cbcSMatt Macy set_nvl); 5712*eda14cbcSMatt Macy } 5713*eda14cbcSMatt Macy } 5714*eda14cbcSMatt Macy } else { 5715*eda14cbcSMatt Macy for (i = 0; i < 2; i++) { 5716*eda14cbcSMatt Macy char locality = ld[i]; 5717*eda14cbcSMatt Macy if (locality == 0) 5718*eda14cbcSMatt Macy continue; 5719*eda14cbcSMatt Macy 5720*eda14cbcSMatt Macy if (who != NULL) 5721*eda14cbcSMatt Macy (void) snprintf(who_buf, sizeof (who_buf), 5722*eda14cbcSMatt Macy "%c%c$%s", base_type, locality, who); 5723*eda14cbcSMatt Macy else 5724*eda14cbcSMatt Macy (void) snprintf(who_buf, sizeof (who_buf), 5725*eda14cbcSMatt Macy "%c%c$", base_type, locality); 5726*eda14cbcSMatt Macy (void) nvlist_add_boolean(top_nvl, who_buf); 5727*eda14cbcSMatt Macy 5728*eda14cbcSMatt Macy if (who != NULL) 5729*eda14cbcSMatt Macy (void) snprintf(who_buf, sizeof (who_buf), 5730*eda14cbcSMatt Macy "%c%c$%s", set_type, locality, who); 5731*eda14cbcSMatt Macy else 5732*eda14cbcSMatt Macy (void) snprintf(who_buf, sizeof (who_buf), 5733*eda14cbcSMatt Macy "%c%c$", set_type, locality); 5734*eda14cbcSMatt Macy (void) nvlist_add_boolean(top_nvl, who_buf); 5735*eda14cbcSMatt Macy } 5736*eda14cbcSMatt Macy } 5737*eda14cbcSMatt Macy } 5738*eda14cbcSMatt Macy 5739*eda14cbcSMatt Macy static int 5740*eda14cbcSMatt Macy construct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp) 5741*eda14cbcSMatt Macy { 5742*eda14cbcSMatt Macy if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0) 5743*eda14cbcSMatt Macy nomem(); 5744*eda14cbcSMatt Macy 5745*eda14cbcSMatt Macy if (opts->set) { 5746*eda14cbcSMatt Macy store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local, 5747*eda14cbcSMatt Macy opts->descend, opts->who, opts->perms, *nvlp); 5748*eda14cbcSMatt Macy } else if (opts->create) { 5749*eda14cbcSMatt Macy store_allow_perm(ZFS_DELEG_CREATE, opts->local, 5750*eda14cbcSMatt Macy opts->descend, NULL, opts->perms, *nvlp); 5751*eda14cbcSMatt Macy } else if (opts->everyone) { 5752*eda14cbcSMatt Macy store_allow_perm(ZFS_DELEG_EVERYONE, opts->local, 5753*eda14cbcSMatt Macy opts->descend, NULL, opts->perms, *nvlp); 5754*eda14cbcSMatt Macy } else { 5755*eda14cbcSMatt Macy char *curr = opts->who; 5756*eda14cbcSMatt Macy char *end = curr + strlen(curr); 5757*eda14cbcSMatt Macy 5758*eda14cbcSMatt Macy while (curr < end) { 5759*eda14cbcSMatt Macy const char *who; 5760*eda14cbcSMatt Macy zfs_deleg_who_type_t who_type = ZFS_DELEG_WHO_UNKNOWN; 5761*eda14cbcSMatt Macy char *endch; 5762*eda14cbcSMatt Macy char *delim = strchr(curr, ','); 5763*eda14cbcSMatt Macy char errbuf[256]; 5764*eda14cbcSMatt Macy char id[64]; 5765*eda14cbcSMatt Macy struct passwd *p = NULL; 5766*eda14cbcSMatt Macy struct group *g = NULL; 5767*eda14cbcSMatt Macy 5768*eda14cbcSMatt Macy uid_t rid; 5769*eda14cbcSMatt Macy if (delim == NULL) 5770*eda14cbcSMatt Macy delim = end; 5771*eda14cbcSMatt Macy else 5772*eda14cbcSMatt Macy *delim = '\0'; 5773*eda14cbcSMatt Macy 5774*eda14cbcSMatt Macy rid = (uid_t)strtol(curr, &endch, 0); 5775*eda14cbcSMatt Macy if (opts->user) { 5776*eda14cbcSMatt Macy who_type = ZFS_DELEG_USER; 5777*eda14cbcSMatt Macy if (*endch != '\0') 5778*eda14cbcSMatt Macy p = getpwnam(curr); 5779*eda14cbcSMatt Macy else 5780*eda14cbcSMatt Macy p = getpwuid(rid); 5781*eda14cbcSMatt Macy 5782*eda14cbcSMatt Macy if (p != NULL) 5783*eda14cbcSMatt Macy rid = p->pw_uid; 5784*eda14cbcSMatt Macy else if (*endch != '\0') { 5785*eda14cbcSMatt Macy (void) snprintf(errbuf, 256, gettext( 5786*eda14cbcSMatt Macy "invalid user %s\n"), curr); 5787*eda14cbcSMatt Macy allow_usage(un, B_TRUE, errbuf); 5788*eda14cbcSMatt Macy } 5789*eda14cbcSMatt Macy } else if (opts->group) { 5790*eda14cbcSMatt Macy who_type = ZFS_DELEG_GROUP; 5791*eda14cbcSMatt Macy if (*endch != '\0') 5792*eda14cbcSMatt Macy g = getgrnam(curr); 5793*eda14cbcSMatt Macy else 5794*eda14cbcSMatt Macy g = getgrgid(rid); 5795*eda14cbcSMatt Macy 5796*eda14cbcSMatt Macy if (g != NULL) 5797*eda14cbcSMatt Macy rid = g->gr_gid; 5798*eda14cbcSMatt Macy else if (*endch != '\0') { 5799*eda14cbcSMatt Macy (void) snprintf(errbuf, 256, gettext( 5800*eda14cbcSMatt Macy "invalid group %s\n"), curr); 5801*eda14cbcSMatt Macy allow_usage(un, B_TRUE, errbuf); 5802*eda14cbcSMatt Macy } 5803*eda14cbcSMatt Macy } else { 5804*eda14cbcSMatt Macy if (*endch != '\0') { 5805*eda14cbcSMatt Macy p = getpwnam(curr); 5806*eda14cbcSMatt Macy } else { 5807*eda14cbcSMatt Macy p = getpwuid(rid); 5808*eda14cbcSMatt Macy } 5809*eda14cbcSMatt Macy 5810*eda14cbcSMatt Macy if (p == NULL) { 5811*eda14cbcSMatt Macy if (*endch != '\0') { 5812*eda14cbcSMatt Macy g = getgrnam(curr); 5813*eda14cbcSMatt Macy } else { 5814*eda14cbcSMatt Macy g = getgrgid(rid); 5815*eda14cbcSMatt Macy } 5816*eda14cbcSMatt Macy } 5817*eda14cbcSMatt Macy 5818*eda14cbcSMatt Macy if (p != NULL) { 5819*eda14cbcSMatt Macy who_type = ZFS_DELEG_USER; 5820*eda14cbcSMatt Macy rid = p->pw_uid; 5821*eda14cbcSMatt Macy } else if (g != NULL) { 5822*eda14cbcSMatt Macy who_type = ZFS_DELEG_GROUP; 5823*eda14cbcSMatt Macy rid = g->gr_gid; 5824*eda14cbcSMatt Macy } else { 5825*eda14cbcSMatt Macy (void) snprintf(errbuf, 256, gettext( 5826*eda14cbcSMatt Macy "invalid user/group %s\n"), curr); 5827*eda14cbcSMatt Macy allow_usage(un, B_TRUE, errbuf); 5828*eda14cbcSMatt Macy } 5829*eda14cbcSMatt Macy } 5830*eda14cbcSMatt Macy 5831*eda14cbcSMatt Macy (void) sprintf(id, "%u", rid); 5832*eda14cbcSMatt Macy who = id; 5833*eda14cbcSMatt Macy 5834*eda14cbcSMatt Macy store_allow_perm(who_type, opts->local, 5835*eda14cbcSMatt Macy opts->descend, who, opts->perms, *nvlp); 5836*eda14cbcSMatt Macy curr = delim + 1; 5837*eda14cbcSMatt Macy } 5838*eda14cbcSMatt Macy } 5839*eda14cbcSMatt Macy 5840*eda14cbcSMatt Macy return (0); 5841*eda14cbcSMatt Macy } 5842*eda14cbcSMatt Macy 5843*eda14cbcSMatt Macy static void 5844*eda14cbcSMatt Macy print_set_creat_perms(uu_avl_t *who_avl) 5845*eda14cbcSMatt Macy { 5846*eda14cbcSMatt Macy const char *sc_title[] = { 5847*eda14cbcSMatt Macy gettext("Permission sets:\n"), 5848*eda14cbcSMatt Macy gettext("Create time permissions:\n"), 5849*eda14cbcSMatt Macy NULL 5850*eda14cbcSMatt Macy }; 5851*eda14cbcSMatt Macy who_perm_node_t *who_node = NULL; 5852*eda14cbcSMatt Macy int prev_weight = -1; 5853*eda14cbcSMatt Macy 5854*eda14cbcSMatt Macy for (who_node = uu_avl_first(who_avl); who_node != NULL; 5855*eda14cbcSMatt Macy who_node = uu_avl_next(who_avl, who_node)) { 5856*eda14cbcSMatt Macy uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl; 5857*eda14cbcSMatt Macy zfs_deleg_who_type_t who_type = who_node->who_perm.who_type; 5858*eda14cbcSMatt Macy const char *who_name = who_node->who_perm.who_name; 5859*eda14cbcSMatt Macy int weight = who_type2weight(who_type); 5860*eda14cbcSMatt Macy boolean_t first = B_TRUE; 5861*eda14cbcSMatt Macy deleg_perm_node_t *deleg_node; 5862*eda14cbcSMatt Macy 5863*eda14cbcSMatt Macy if (prev_weight != weight) { 5864*eda14cbcSMatt Macy (void) printf("%s", sc_title[weight]); 5865*eda14cbcSMatt Macy prev_weight = weight; 5866*eda14cbcSMatt Macy } 5867*eda14cbcSMatt Macy 5868*eda14cbcSMatt Macy if (who_name == NULL || strnlen(who_name, 1) == 0) 5869*eda14cbcSMatt Macy (void) printf("\t"); 5870*eda14cbcSMatt Macy else 5871*eda14cbcSMatt Macy (void) printf("\t%s ", who_name); 5872*eda14cbcSMatt Macy 5873*eda14cbcSMatt Macy for (deleg_node = uu_avl_first(avl); deleg_node != NULL; 5874*eda14cbcSMatt Macy deleg_node = uu_avl_next(avl, deleg_node)) { 5875*eda14cbcSMatt Macy if (first) { 5876*eda14cbcSMatt Macy (void) printf("%s", 5877*eda14cbcSMatt Macy deleg_node->dpn_perm.dp_name); 5878*eda14cbcSMatt Macy first = B_FALSE; 5879*eda14cbcSMatt Macy } else 5880*eda14cbcSMatt Macy (void) printf(",%s", 5881*eda14cbcSMatt Macy deleg_node->dpn_perm.dp_name); 5882*eda14cbcSMatt Macy } 5883*eda14cbcSMatt Macy 5884*eda14cbcSMatt Macy (void) printf("\n"); 5885*eda14cbcSMatt Macy } 5886*eda14cbcSMatt Macy } 5887*eda14cbcSMatt Macy 5888*eda14cbcSMatt Macy static void 5889*eda14cbcSMatt Macy print_uge_deleg_perms(uu_avl_t *who_avl, boolean_t local, boolean_t descend, 5890*eda14cbcSMatt Macy const char *title) 5891*eda14cbcSMatt Macy { 5892*eda14cbcSMatt Macy who_perm_node_t *who_node = NULL; 5893*eda14cbcSMatt Macy boolean_t prt_title = B_TRUE; 5894*eda14cbcSMatt Macy uu_avl_walk_t *walk; 5895*eda14cbcSMatt Macy 5896*eda14cbcSMatt Macy if ((walk = uu_avl_walk_start(who_avl, UU_WALK_ROBUST)) == NULL) 5897*eda14cbcSMatt Macy nomem(); 5898*eda14cbcSMatt Macy 5899*eda14cbcSMatt Macy while ((who_node = uu_avl_walk_next(walk)) != NULL) { 5900*eda14cbcSMatt Macy const char *who_name = who_node->who_perm.who_name; 5901*eda14cbcSMatt Macy const char *nice_who_name = who_node->who_perm.who_ug_name; 5902*eda14cbcSMatt Macy uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl; 5903*eda14cbcSMatt Macy zfs_deleg_who_type_t who_type = who_node->who_perm.who_type; 5904*eda14cbcSMatt Macy char delim = ' '; 5905*eda14cbcSMatt Macy deleg_perm_node_t *deleg_node; 5906*eda14cbcSMatt Macy boolean_t prt_who = B_TRUE; 5907*eda14cbcSMatt Macy 5908*eda14cbcSMatt Macy for (deleg_node = uu_avl_first(avl); 5909*eda14cbcSMatt Macy deleg_node != NULL; 5910*eda14cbcSMatt Macy deleg_node = uu_avl_next(avl, deleg_node)) { 5911*eda14cbcSMatt Macy if (local != deleg_node->dpn_perm.dp_local || 5912*eda14cbcSMatt Macy descend != deleg_node->dpn_perm.dp_descend) 5913*eda14cbcSMatt Macy continue; 5914*eda14cbcSMatt Macy 5915*eda14cbcSMatt Macy if (prt_who) { 5916*eda14cbcSMatt Macy const char *who = NULL; 5917*eda14cbcSMatt Macy if (prt_title) { 5918*eda14cbcSMatt Macy prt_title = B_FALSE; 5919*eda14cbcSMatt Macy (void) printf("%s", title); 5920*eda14cbcSMatt Macy } 5921*eda14cbcSMatt Macy 5922*eda14cbcSMatt Macy switch (who_type) { 5923*eda14cbcSMatt Macy case ZFS_DELEG_USER_SETS: 5924*eda14cbcSMatt Macy case ZFS_DELEG_USER: 5925*eda14cbcSMatt Macy who = gettext("user"); 5926*eda14cbcSMatt Macy if (nice_who_name) 5927*eda14cbcSMatt Macy who_name = nice_who_name; 5928*eda14cbcSMatt Macy break; 5929*eda14cbcSMatt Macy case ZFS_DELEG_GROUP_SETS: 5930*eda14cbcSMatt Macy case ZFS_DELEG_GROUP: 5931*eda14cbcSMatt Macy who = gettext("group"); 5932*eda14cbcSMatt Macy if (nice_who_name) 5933*eda14cbcSMatt Macy who_name = nice_who_name; 5934*eda14cbcSMatt Macy break; 5935*eda14cbcSMatt Macy case ZFS_DELEG_EVERYONE_SETS: 5936*eda14cbcSMatt Macy case ZFS_DELEG_EVERYONE: 5937*eda14cbcSMatt Macy who = gettext("everyone"); 5938*eda14cbcSMatt Macy who_name = NULL; 5939*eda14cbcSMatt Macy break; 5940*eda14cbcSMatt Macy 5941*eda14cbcSMatt Macy default: 5942*eda14cbcSMatt Macy assert(who != NULL); 5943*eda14cbcSMatt Macy } 5944*eda14cbcSMatt Macy 5945*eda14cbcSMatt Macy prt_who = B_FALSE; 5946*eda14cbcSMatt Macy if (who_name == NULL) 5947*eda14cbcSMatt Macy (void) printf("\t%s", who); 5948*eda14cbcSMatt Macy else 5949*eda14cbcSMatt Macy (void) printf("\t%s %s", who, who_name); 5950*eda14cbcSMatt Macy } 5951*eda14cbcSMatt Macy 5952*eda14cbcSMatt Macy (void) printf("%c%s", delim, 5953*eda14cbcSMatt Macy deleg_node->dpn_perm.dp_name); 5954*eda14cbcSMatt Macy delim = ','; 5955*eda14cbcSMatt Macy } 5956*eda14cbcSMatt Macy 5957*eda14cbcSMatt Macy if (!prt_who) 5958*eda14cbcSMatt Macy (void) printf("\n"); 5959*eda14cbcSMatt Macy } 5960*eda14cbcSMatt Macy 5961*eda14cbcSMatt Macy uu_avl_walk_end(walk); 5962*eda14cbcSMatt Macy } 5963*eda14cbcSMatt Macy 5964*eda14cbcSMatt Macy static void 5965*eda14cbcSMatt Macy print_fs_perms(fs_perm_set_t *fspset) 5966*eda14cbcSMatt Macy { 5967*eda14cbcSMatt Macy fs_perm_node_t *node = NULL; 5968*eda14cbcSMatt Macy char buf[MAXNAMELEN + 32]; 5969*eda14cbcSMatt Macy const char *dsname = buf; 5970*eda14cbcSMatt Macy 5971*eda14cbcSMatt Macy for (node = uu_list_first(fspset->fsps_list); node != NULL; 5972*eda14cbcSMatt Macy node = uu_list_next(fspset->fsps_list, node)) { 5973*eda14cbcSMatt Macy uu_avl_t *sc_avl = node->fspn_fsperm.fsp_sc_avl; 5974*eda14cbcSMatt Macy uu_avl_t *uge_avl = node->fspn_fsperm.fsp_uge_avl; 5975*eda14cbcSMatt Macy int left = 0; 5976*eda14cbcSMatt Macy 5977*eda14cbcSMatt Macy (void) snprintf(buf, sizeof (buf), 5978*eda14cbcSMatt Macy gettext("---- Permissions on %s "), 5979*eda14cbcSMatt Macy node->fspn_fsperm.fsp_name); 5980*eda14cbcSMatt Macy (void) printf("%s", dsname); 5981*eda14cbcSMatt Macy left = 70 - strlen(buf); 5982*eda14cbcSMatt Macy while (left-- > 0) 5983*eda14cbcSMatt Macy (void) printf("-"); 5984*eda14cbcSMatt Macy (void) printf("\n"); 5985*eda14cbcSMatt Macy 5986*eda14cbcSMatt Macy print_set_creat_perms(sc_avl); 5987*eda14cbcSMatt Macy print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE, 5988*eda14cbcSMatt Macy gettext("Local permissions:\n")); 5989*eda14cbcSMatt Macy print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE, 5990*eda14cbcSMatt Macy gettext("Descendent permissions:\n")); 5991*eda14cbcSMatt Macy print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE, 5992*eda14cbcSMatt Macy gettext("Local+Descendent permissions:\n")); 5993*eda14cbcSMatt Macy } 5994*eda14cbcSMatt Macy } 5995*eda14cbcSMatt Macy 5996*eda14cbcSMatt Macy static fs_perm_set_t fs_perm_set = { NULL, NULL, NULL, NULL }; 5997*eda14cbcSMatt Macy 5998*eda14cbcSMatt Macy struct deleg_perms { 5999*eda14cbcSMatt Macy boolean_t un; 6000*eda14cbcSMatt Macy nvlist_t *nvl; 6001*eda14cbcSMatt Macy }; 6002*eda14cbcSMatt Macy 6003*eda14cbcSMatt Macy static int 6004*eda14cbcSMatt Macy set_deleg_perms(zfs_handle_t *zhp, void *data) 6005*eda14cbcSMatt Macy { 6006*eda14cbcSMatt Macy struct deleg_perms *perms = (struct deleg_perms *)data; 6007*eda14cbcSMatt Macy zfs_type_t zfs_type = zfs_get_type(zhp); 6008*eda14cbcSMatt Macy 6009*eda14cbcSMatt Macy if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME) 6010*eda14cbcSMatt Macy return (0); 6011*eda14cbcSMatt Macy 6012*eda14cbcSMatt Macy return (zfs_set_fsacl(zhp, perms->un, perms->nvl)); 6013*eda14cbcSMatt Macy } 6014*eda14cbcSMatt Macy 6015*eda14cbcSMatt Macy static int 6016*eda14cbcSMatt Macy zfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un) 6017*eda14cbcSMatt Macy { 6018*eda14cbcSMatt Macy zfs_handle_t *zhp; 6019*eda14cbcSMatt Macy nvlist_t *perm_nvl = NULL; 6020*eda14cbcSMatt Macy nvlist_t *update_perm_nvl = NULL; 6021*eda14cbcSMatt Macy int error = 1; 6022*eda14cbcSMatt Macy int c; 6023*eda14cbcSMatt Macy struct allow_opts opts = { 0 }; 6024*eda14cbcSMatt Macy 6025*eda14cbcSMatt Macy const char *optstr = un ? "ldugecsrh" : "ldugecsh"; 6026*eda14cbcSMatt Macy 6027*eda14cbcSMatt Macy /* check opts */ 6028*eda14cbcSMatt Macy while ((c = getopt(argc, argv, optstr)) != -1) { 6029*eda14cbcSMatt Macy switch (c) { 6030*eda14cbcSMatt Macy case 'l': 6031*eda14cbcSMatt Macy opts.local = B_TRUE; 6032*eda14cbcSMatt Macy break; 6033*eda14cbcSMatt Macy case 'd': 6034*eda14cbcSMatt Macy opts.descend = B_TRUE; 6035*eda14cbcSMatt Macy break; 6036*eda14cbcSMatt Macy case 'u': 6037*eda14cbcSMatt Macy opts.user = B_TRUE; 6038*eda14cbcSMatt Macy break; 6039*eda14cbcSMatt Macy case 'g': 6040*eda14cbcSMatt Macy opts.group = B_TRUE; 6041*eda14cbcSMatt Macy break; 6042*eda14cbcSMatt Macy case 'e': 6043*eda14cbcSMatt Macy opts.everyone = B_TRUE; 6044*eda14cbcSMatt Macy break; 6045*eda14cbcSMatt Macy case 's': 6046*eda14cbcSMatt Macy opts.set = B_TRUE; 6047*eda14cbcSMatt Macy break; 6048*eda14cbcSMatt Macy case 'c': 6049*eda14cbcSMatt Macy opts.create = B_TRUE; 6050*eda14cbcSMatt Macy break; 6051*eda14cbcSMatt Macy case 'r': 6052*eda14cbcSMatt Macy opts.recursive = B_TRUE; 6053*eda14cbcSMatt Macy break; 6054*eda14cbcSMatt Macy case ':': 6055*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing argument for " 6056*eda14cbcSMatt Macy "'%c' option\n"), optopt); 6057*eda14cbcSMatt Macy usage(B_FALSE); 6058*eda14cbcSMatt Macy break; 6059*eda14cbcSMatt Macy case 'h': 6060*eda14cbcSMatt Macy opts.prt_usage = B_TRUE; 6061*eda14cbcSMatt Macy break; 6062*eda14cbcSMatt Macy case '?': 6063*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 6064*eda14cbcSMatt Macy optopt); 6065*eda14cbcSMatt Macy usage(B_FALSE); 6066*eda14cbcSMatt Macy } 6067*eda14cbcSMatt Macy } 6068*eda14cbcSMatt Macy 6069*eda14cbcSMatt Macy argc -= optind; 6070*eda14cbcSMatt Macy argv += optind; 6071*eda14cbcSMatt Macy 6072*eda14cbcSMatt Macy /* check arguments */ 6073*eda14cbcSMatt Macy parse_allow_args(argc, argv, un, &opts); 6074*eda14cbcSMatt Macy 6075*eda14cbcSMatt Macy /* try to open the dataset */ 6076*eda14cbcSMatt Macy if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM | 6077*eda14cbcSMatt Macy ZFS_TYPE_VOLUME)) == NULL) { 6078*eda14cbcSMatt Macy (void) fprintf(stderr, "Failed to open dataset: %s\n", 6079*eda14cbcSMatt Macy opts.dataset); 6080*eda14cbcSMatt Macy return (-1); 6081*eda14cbcSMatt Macy } 6082*eda14cbcSMatt Macy 6083*eda14cbcSMatt Macy if (zfs_get_fsacl(zhp, &perm_nvl) != 0) 6084*eda14cbcSMatt Macy goto cleanup2; 6085*eda14cbcSMatt Macy 6086*eda14cbcSMatt Macy fs_perm_set_init(&fs_perm_set); 6087*eda14cbcSMatt Macy if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) { 6088*eda14cbcSMatt Macy (void) fprintf(stderr, "Failed to parse fsacl permissions\n"); 6089*eda14cbcSMatt Macy goto cleanup1; 6090*eda14cbcSMatt Macy } 6091*eda14cbcSMatt Macy 6092*eda14cbcSMatt Macy if (opts.prt_perms) 6093*eda14cbcSMatt Macy print_fs_perms(&fs_perm_set); 6094*eda14cbcSMatt Macy else { 6095*eda14cbcSMatt Macy (void) construct_fsacl_list(un, &opts, &update_perm_nvl); 6096*eda14cbcSMatt Macy if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0) 6097*eda14cbcSMatt Macy goto cleanup0; 6098*eda14cbcSMatt Macy 6099*eda14cbcSMatt Macy if (un && opts.recursive) { 6100*eda14cbcSMatt Macy struct deleg_perms data = { un, update_perm_nvl }; 6101*eda14cbcSMatt Macy if (zfs_iter_filesystems(zhp, set_deleg_perms, 6102*eda14cbcSMatt Macy &data) != 0) 6103*eda14cbcSMatt Macy goto cleanup0; 6104*eda14cbcSMatt Macy } 6105*eda14cbcSMatt Macy } 6106*eda14cbcSMatt Macy 6107*eda14cbcSMatt Macy error = 0; 6108*eda14cbcSMatt Macy 6109*eda14cbcSMatt Macy cleanup0: 6110*eda14cbcSMatt Macy nvlist_free(perm_nvl); 6111*eda14cbcSMatt Macy nvlist_free(update_perm_nvl); 6112*eda14cbcSMatt Macy cleanup1: 6113*eda14cbcSMatt Macy fs_perm_set_fini(&fs_perm_set); 6114*eda14cbcSMatt Macy cleanup2: 6115*eda14cbcSMatt Macy zfs_close(zhp); 6116*eda14cbcSMatt Macy 6117*eda14cbcSMatt Macy return (error); 6118*eda14cbcSMatt Macy } 6119*eda14cbcSMatt Macy 6120*eda14cbcSMatt Macy static int 6121*eda14cbcSMatt Macy zfs_do_allow(int argc, char **argv) 6122*eda14cbcSMatt Macy { 6123*eda14cbcSMatt Macy return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE)); 6124*eda14cbcSMatt Macy } 6125*eda14cbcSMatt Macy 6126*eda14cbcSMatt Macy static int 6127*eda14cbcSMatt Macy zfs_do_unallow(int argc, char **argv) 6128*eda14cbcSMatt Macy { 6129*eda14cbcSMatt Macy return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE)); 6130*eda14cbcSMatt Macy } 6131*eda14cbcSMatt Macy 6132*eda14cbcSMatt Macy static int 6133*eda14cbcSMatt Macy zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding) 6134*eda14cbcSMatt Macy { 6135*eda14cbcSMatt Macy int errors = 0; 6136*eda14cbcSMatt Macy int i; 6137*eda14cbcSMatt Macy const char *tag; 6138*eda14cbcSMatt Macy boolean_t recursive = B_FALSE; 6139*eda14cbcSMatt Macy const char *opts = holding ? "rt" : "r"; 6140*eda14cbcSMatt Macy int c; 6141*eda14cbcSMatt Macy 6142*eda14cbcSMatt Macy /* check options */ 6143*eda14cbcSMatt Macy while ((c = getopt(argc, argv, opts)) != -1) { 6144*eda14cbcSMatt Macy switch (c) { 6145*eda14cbcSMatt Macy case 'r': 6146*eda14cbcSMatt Macy recursive = B_TRUE; 6147*eda14cbcSMatt Macy break; 6148*eda14cbcSMatt Macy case '?': 6149*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 6150*eda14cbcSMatt Macy optopt); 6151*eda14cbcSMatt Macy usage(B_FALSE); 6152*eda14cbcSMatt Macy } 6153*eda14cbcSMatt Macy } 6154*eda14cbcSMatt Macy 6155*eda14cbcSMatt Macy argc -= optind; 6156*eda14cbcSMatt Macy argv += optind; 6157*eda14cbcSMatt Macy 6158*eda14cbcSMatt Macy /* check number of arguments */ 6159*eda14cbcSMatt Macy if (argc < 2) 6160*eda14cbcSMatt Macy usage(B_FALSE); 6161*eda14cbcSMatt Macy 6162*eda14cbcSMatt Macy tag = argv[0]; 6163*eda14cbcSMatt Macy --argc; 6164*eda14cbcSMatt Macy ++argv; 6165*eda14cbcSMatt Macy 6166*eda14cbcSMatt Macy if (holding && tag[0] == '.') { 6167*eda14cbcSMatt Macy /* tags starting with '.' are reserved for libzfs */ 6168*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("tag may not start with '.'\n")); 6169*eda14cbcSMatt Macy usage(B_FALSE); 6170*eda14cbcSMatt Macy } 6171*eda14cbcSMatt Macy 6172*eda14cbcSMatt Macy for (i = 0; i < argc; ++i) { 6173*eda14cbcSMatt Macy zfs_handle_t *zhp; 6174*eda14cbcSMatt Macy char parent[ZFS_MAX_DATASET_NAME_LEN]; 6175*eda14cbcSMatt Macy const char *delim; 6176*eda14cbcSMatt Macy char *path = argv[i]; 6177*eda14cbcSMatt Macy 6178*eda14cbcSMatt Macy delim = strchr(path, '@'); 6179*eda14cbcSMatt Macy if (delim == NULL) { 6180*eda14cbcSMatt Macy (void) fprintf(stderr, 6181*eda14cbcSMatt Macy gettext("'%s' is not a snapshot\n"), path); 6182*eda14cbcSMatt Macy ++errors; 6183*eda14cbcSMatt Macy continue; 6184*eda14cbcSMatt Macy } 6185*eda14cbcSMatt Macy (void) strncpy(parent, path, delim - path); 6186*eda14cbcSMatt Macy parent[delim - path] = '\0'; 6187*eda14cbcSMatt Macy 6188*eda14cbcSMatt Macy zhp = zfs_open(g_zfs, parent, 6189*eda14cbcSMatt Macy ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 6190*eda14cbcSMatt Macy if (zhp == NULL) { 6191*eda14cbcSMatt Macy ++errors; 6192*eda14cbcSMatt Macy continue; 6193*eda14cbcSMatt Macy } 6194*eda14cbcSMatt Macy if (holding) { 6195*eda14cbcSMatt Macy if (zfs_hold(zhp, delim+1, tag, recursive, -1) != 0) 6196*eda14cbcSMatt Macy ++errors; 6197*eda14cbcSMatt Macy } else { 6198*eda14cbcSMatt Macy if (zfs_release(zhp, delim+1, tag, recursive) != 0) 6199*eda14cbcSMatt Macy ++errors; 6200*eda14cbcSMatt Macy } 6201*eda14cbcSMatt Macy zfs_close(zhp); 6202*eda14cbcSMatt Macy } 6203*eda14cbcSMatt Macy 6204*eda14cbcSMatt Macy return (errors != 0); 6205*eda14cbcSMatt Macy } 6206*eda14cbcSMatt Macy 6207*eda14cbcSMatt Macy /* 6208*eda14cbcSMatt Macy * zfs hold [-r] [-t] <tag> <snap> ... 6209*eda14cbcSMatt Macy * 6210*eda14cbcSMatt Macy * -r Recursively hold 6211*eda14cbcSMatt Macy * 6212*eda14cbcSMatt Macy * Apply a user-hold with the given tag to the list of snapshots. 6213*eda14cbcSMatt Macy */ 6214*eda14cbcSMatt Macy static int 6215*eda14cbcSMatt Macy zfs_do_hold(int argc, char **argv) 6216*eda14cbcSMatt Macy { 6217*eda14cbcSMatt Macy return (zfs_do_hold_rele_impl(argc, argv, B_TRUE)); 6218*eda14cbcSMatt Macy } 6219*eda14cbcSMatt Macy 6220*eda14cbcSMatt Macy /* 6221*eda14cbcSMatt Macy * zfs release [-r] <tag> <snap> ... 6222*eda14cbcSMatt Macy * 6223*eda14cbcSMatt Macy * -r Recursively release 6224*eda14cbcSMatt Macy * 6225*eda14cbcSMatt Macy * Release a user-hold with the given tag from the list of snapshots. 6226*eda14cbcSMatt Macy */ 6227*eda14cbcSMatt Macy static int 6228*eda14cbcSMatt Macy zfs_do_release(int argc, char **argv) 6229*eda14cbcSMatt Macy { 6230*eda14cbcSMatt Macy return (zfs_do_hold_rele_impl(argc, argv, B_FALSE)); 6231*eda14cbcSMatt Macy } 6232*eda14cbcSMatt Macy 6233*eda14cbcSMatt Macy typedef struct holds_cbdata { 6234*eda14cbcSMatt Macy boolean_t cb_recursive; 6235*eda14cbcSMatt Macy const char *cb_snapname; 6236*eda14cbcSMatt Macy nvlist_t **cb_nvlp; 6237*eda14cbcSMatt Macy size_t cb_max_namelen; 6238*eda14cbcSMatt Macy size_t cb_max_taglen; 6239*eda14cbcSMatt Macy } holds_cbdata_t; 6240*eda14cbcSMatt Macy 6241*eda14cbcSMatt Macy #define STRFTIME_FMT_STR "%a %b %e %H:%M %Y" 6242*eda14cbcSMatt Macy #define DATETIME_BUF_LEN (32) 6243*eda14cbcSMatt Macy /* 6244*eda14cbcSMatt Macy * 6245*eda14cbcSMatt Macy */ 6246*eda14cbcSMatt Macy static void 6247*eda14cbcSMatt Macy print_holds(boolean_t scripted, int nwidth, int tagwidth, nvlist_t *nvl) 6248*eda14cbcSMatt Macy { 6249*eda14cbcSMatt Macy int i; 6250*eda14cbcSMatt Macy nvpair_t *nvp = NULL; 6251*eda14cbcSMatt Macy char *hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" }; 6252*eda14cbcSMatt Macy const char *col; 6253*eda14cbcSMatt Macy 6254*eda14cbcSMatt Macy if (!scripted) { 6255*eda14cbcSMatt Macy for (i = 0; i < 3; i++) { 6256*eda14cbcSMatt Macy col = gettext(hdr_cols[i]); 6257*eda14cbcSMatt Macy if (i < 2) 6258*eda14cbcSMatt Macy (void) printf("%-*s ", i ? tagwidth : nwidth, 6259*eda14cbcSMatt Macy col); 6260*eda14cbcSMatt Macy else 6261*eda14cbcSMatt Macy (void) printf("%s\n", col); 6262*eda14cbcSMatt Macy } 6263*eda14cbcSMatt Macy } 6264*eda14cbcSMatt Macy 6265*eda14cbcSMatt Macy while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 6266*eda14cbcSMatt Macy char *zname = nvpair_name(nvp); 6267*eda14cbcSMatt Macy nvlist_t *nvl2; 6268*eda14cbcSMatt Macy nvpair_t *nvp2 = NULL; 6269*eda14cbcSMatt Macy (void) nvpair_value_nvlist(nvp, &nvl2); 6270*eda14cbcSMatt Macy while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) { 6271*eda14cbcSMatt Macy char tsbuf[DATETIME_BUF_LEN]; 6272*eda14cbcSMatt Macy char *tagname = nvpair_name(nvp2); 6273*eda14cbcSMatt Macy uint64_t val = 0; 6274*eda14cbcSMatt Macy time_t time; 6275*eda14cbcSMatt Macy struct tm t; 6276*eda14cbcSMatt Macy 6277*eda14cbcSMatt Macy (void) nvpair_value_uint64(nvp2, &val); 6278*eda14cbcSMatt Macy time = (time_t)val; 6279*eda14cbcSMatt Macy (void) localtime_r(&time, &t); 6280*eda14cbcSMatt Macy (void) strftime(tsbuf, DATETIME_BUF_LEN, 6281*eda14cbcSMatt Macy gettext(STRFTIME_FMT_STR), &t); 6282*eda14cbcSMatt Macy 6283*eda14cbcSMatt Macy if (scripted) { 6284*eda14cbcSMatt Macy (void) printf("%s\t%s\t%s\n", zname, 6285*eda14cbcSMatt Macy tagname, tsbuf); 6286*eda14cbcSMatt Macy } else { 6287*eda14cbcSMatt Macy (void) printf("%-*s %-*s %s\n", nwidth, 6288*eda14cbcSMatt Macy zname, tagwidth, tagname, tsbuf); 6289*eda14cbcSMatt Macy } 6290*eda14cbcSMatt Macy } 6291*eda14cbcSMatt Macy } 6292*eda14cbcSMatt Macy } 6293*eda14cbcSMatt Macy 6294*eda14cbcSMatt Macy /* 6295*eda14cbcSMatt Macy * Generic callback function to list a dataset or snapshot. 6296*eda14cbcSMatt Macy */ 6297*eda14cbcSMatt Macy static int 6298*eda14cbcSMatt Macy holds_callback(zfs_handle_t *zhp, void *data) 6299*eda14cbcSMatt Macy { 6300*eda14cbcSMatt Macy holds_cbdata_t *cbp = data; 6301*eda14cbcSMatt Macy nvlist_t *top_nvl = *cbp->cb_nvlp; 6302*eda14cbcSMatt Macy nvlist_t *nvl = NULL; 6303*eda14cbcSMatt Macy nvpair_t *nvp = NULL; 6304*eda14cbcSMatt Macy const char *zname = zfs_get_name(zhp); 6305*eda14cbcSMatt Macy size_t znamelen = strlen(zname); 6306*eda14cbcSMatt Macy 6307*eda14cbcSMatt Macy if (cbp->cb_recursive) { 6308*eda14cbcSMatt Macy const char *snapname; 6309*eda14cbcSMatt Macy char *delim = strchr(zname, '@'); 6310*eda14cbcSMatt Macy if (delim == NULL) 6311*eda14cbcSMatt Macy return (0); 6312*eda14cbcSMatt Macy 6313*eda14cbcSMatt Macy snapname = delim + 1; 6314*eda14cbcSMatt Macy if (strcmp(cbp->cb_snapname, snapname)) 6315*eda14cbcSMatt Macy return (0); 6316*eda14cbcSMatt Macy } 6317*eda14cbcSMatt Macy 6318*eda14cbcSMatt Macy if (zfs_get_holds(zhp, &nvl) != 0) 6319*eda14cbcSMatt Macy return (-1); 6320*eda14cbcSMatt Macy 6321*eda14cbcSMatt Macy if (znamelen > cbp->cb_max_namelen) 6322*eda14cbcSMatt Macy cbp->cb_max_namelen = znamelen; 6323*eda14cbcSMatt Macy 6324*eda14cbcSMatt Macy while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 6325*eda14cbcSMatt Macy const char *tag = nvpair_name(nvp); 6326*eda14cbcSMatt Macy size_t taglen = strlen(tag); 6327*eda14cbcSMatt Macy if (taglen > cbp->cb_max_taglen) 6328*eda14cbcSMatt Macy cbp->cb_max_taglen = taglen; 6329*eda14cbcSMatt Macy } 6330*eda14cbcSMatt Macy 6331*eda14cbcSMatt Macy return (nvlist_add_nvlist(top_nvl, zname, nvl)); 6332*eda14cbcSMatt Macy } 6333*eda14cbcSMatt Macy 6334*eda14cbcSMatt Macy /* 6335*eda14cbcSMatt Macy * zfs holds [-rH] <snap> ... 6336*eda14cbcSMatt Macy * 6337*eda14cbcSMatt Macy * -r Lists holds that are set on the named snapshots recursively. 6338*eda14cbcSMatt Macy * -H Scripted mode; elide headers and separate columns by tabs. 6339*eda14cbcSMatt Macy */ 6340*eda14cbcSMatt Macy static int 6341*eda14cbcSMatt Macy zfs_do_holds(int argc, char **argv) 6342*eda14cbcSMatt Macy { 6343*eda14cbcSMatt Macy int errors = 0; 6344*eda14cbcSMatt Macy int c; 6345*eda14cbcSMatt Macy int i; 6346*eda14cbcSMatt Macy boolean_t scripted = B_FALSE; 6347*eda14cbcSMatt Macy boolean_t recursive = B_FALSE; 6348*eda14cbcSMatt Macy const char *opts = "rH"; 6349*eda14cbcSMatt Macy nvlist_t *nvl; 6350*eda14cbcSMatt Macy 6351*eda14cbcSMatt Macy int types = ZFS_TYPE_SNAPSHOT; 6352*eda14cbcSMatt Macy holds_cbdata_t cb = { 0 }; 6353*eda14cbcSMatt Macy 6354*eda14cbcSMatt Macy int limit = 0; 6355*eda14cbcSMatt Macy int ret = 0; 6356*eda14cbcSMatt Macy int flags = 0; 6357*eda14cbcSMatt Macy 6358*eda14cbcSMatt Macy /* check options */ 6359*eda14cbcSMatt Macy while ((c = getopt(argc, argv, opts)) != -1) { 6360*eda14cbcSMatt Macy switch (c) { 6361*eda14cbcSMatt Macy case 'r': 6362*eda14cbcSMatt Macy recursive = B_TRUE; 6363*eda14cbcSMatt Macy break; 6364*eda14cbcSMatt Macy case 'H': 6365*eda14cbcSMatt Macy scripted = B_TRUE; 6366*eda14cbcSMatt Macy break; 6367*eda14cbcSMatt Macy case '?': 6368*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 6369*eda14cbcSMatt Macy optopt); 6370*eda14cbcSMatt Macy usage(B_FALSE); 6371*eda14cbcSMatt Macy } 6372*eda14cbcSMatt Macy } 6373*eda14cbcSMatt Macy 6374*eda14cbcSMatt Macy if (recursive) { 6375*eda14cbcSMatt Macy types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME; 6376*eda14cbcSMatt Macy flags |= ZFS_ITER_RECURSE; 6377*eda14cbcSMatt Macy } 6378*eda14cbcSMatt Macy 6379*eda14cbcSMatt Macy argc -= optind; 6380*eda14cbcSMatt Macy argv += optind; 6381*eda14cbcSMatt Macy 6382*eda14cbcSMatt Macy /* check number of arguments */ 6383*eda14cbcSMatt Macy if (argc < 1) 6384*eda14cbcSMatt Macy usage(B_FALSE); 6385*eda14cbcSMatt Macy 6386*eda14cbcSMatt Macy if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 6387*eda14cbcSMatt Macy nomem(); 6388*eda14cbcSMatt Macy 6389*eda14cbcSMatt Macy for (i = 0; i < argc; ++i) { 6390*eda14cbcSMatt Macy char *snapshot = argv[i]; 6391*eda14cbcSMatt Macy const char *delim; 6392*eda14cbcSMatt Macy const char *snapname; 6393*eda14cbcSMatt Macy 6394*eda14cbcSMatt Macy delim = strchr(snapshot, '@'); 6395*eda14cbcSMatt Macy if (delim == NULL) { 6396*eda14cbcSMatt Macy (void) fprintf(stderr, 6397*eda14cbcSMatt Macy gettext("'%s' is not a snapshot\n"), snapshot); 6398*eda14cbcSMatt Macy ++errors; 6399*eda14cbcSMatt Macy continue; 6400*eda14cbcSMatt Macy } 6401*eda14cbcSMatt Macy snapname = delim + 1; 6402*eda14cbcSMatt Macy if (recursive) 6403*eda14cbcSMatt Macy snapshot[delim - snapshot] = '\0'; 6404*eda14cbcSMatt Macy 6405*eda14cbcSMatt Macy cb.cb_recursive = recursive; 6406*eda14cbcSMatt Macy cb.cb_snapname = snapname; 6407*eda14cbcSMatt Macy cb.cb_nvlp = &nvl; 6408*eda14cbcSMatt Macy 6409*eda14cbcSMatt Macy /* 6410*eda14cbcSMatt Macy * 1. collect holds data, set format options 6411*eda14cbcSMatt Macy */ 6412*eda14cbcSMatt Macy ret = zfs_for_each(argc, argv, flags, types, NULL, NULL, limit, 6413*eda14cbcSMatt Macy holds_callback, &cb); 6414*eda14cbcSMatt Macy if (ret != 0) 6415*eda14cbcSMatt Macy ++errors; 6416*eda14cbcSMatt Macy } 6417*eda14cbcSMatt Macy 6418*eda14cbcSMatt Macy /* 6419*eda14cbcSMatt Macy * 2. print holds data 6420*eda14cbcSMatt Macy */ 6421*eda14cbcSMatt Macy print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl); 6422*eda14cbcSMatt Macy 6423*eda14cbcSMatt Macy if (nvlist_empty(nvl)) 6424*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("no datasets available\n")); 6425*eda14cbcSMatt Macy 6426*eda14cbcSMatt Macy nvlist_free(nvl); 6427*eda14cbcSMatt Macy 6428*eda14cbcSMatt Macy return (0 != errors); 6429*eda14cbcSMatt Macy } 6430*eda14cbcSMatt Macy 6431*eda14cbcSMatt Macy #define CHECK_SPINNER 30 6432*eda14cbcSMatt Macy #define SPINNER_TIME 3 /* seconds */ 6433*eda14cbcSMatt Macy #define MOUNT_TIME 1 /* seconds */ 6434*eda14cbcSMatt Macy 6435*eda14cbcSMatt Macy typedef struct get_all_state { 6436*eda14cbcSMatt Macy boolean_t ga_verbose; 6437*eda14cbcSMatt Macy get_all_cb_t *ga_cbp; 6438*eda14cbcSMatt Macy } get_all_state_t; 6439*eda14cbcSMatt Macy 6440*eda14cbcSMatt Macy static int 6441*eda14cbcSMatt Macy get_one_dataset(zfs_handle_t *zhp, void *data) 6442*eda14cbcSMatt Macy { 6443*eda14cbcSMatt Macy static char *spin[] = { "-", "\\", "|", "/" }; 6444*eda14cbcSMatt Macy static int spinval = 0; 6445*eda14cbcSMatt Macy static int spincheck = 0; 6446*eda14cbcSMatt Macy static time_t last_spin_time = (time_t)0; 6447*eda14cbcSMatt Macy get_all_state_t *state = data; 6448*eda14cbcSMatt Macy zfs_type_t type = zfs_get_type(zhp); 6449*eda14cbcSMatt Macy 6450*eda14cbcSMatt Macy if (state->ga_verbose) { 6451*eda14cbcSMatt Macy if (--spincheck < 0) { 6452*eda14cbcSMatt Macy time_t now = time(NULL); 6453*eda14cbcSMatt Macy if (last_spin_time + SPINNER_TIME < now) { 6454*eda14cbcSMatt Macy update_progress(spin[spinval++ % 4]); 6455*eda14cbcSMatt Macy last_spin_time = now; 6456*eda14cbcSMatt Macy } 6457*eda14cbcSMatt Macy spincheck = CHECK_SPINNER; 6458*eda14cbcSMatt Macy } 6459*eda14cbcSMatt Macy } 6460*eda14cbcSMatt Macy 6461*eda14cbcSMatt Macy /* 6462*eda14cbcSMatt Macy * Iterate over any nested datasets. 6463*eda14cbcSMatt Macy */ 6464*eda14cbcSMatt Macy if (zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) { 6465*eda14cbcSMatt Macy zfs_close(zhp); 6466*eda14cbcSMatt Macy return (1); 6467*eda14cbcSMatt Macy } 6468*eda14cbcSMatt Macy 6469*eda14cbcSMatt Macy /* 6470*eda14cbcSMatt Macy * Skip any datasets whose type does not match. 6471*eda14cbcSMatt Macy */ 6472*eda14cbcSMatt Macy if ((type & ZFS_TYPE_FILESYSTEM) == 0) { 6473*eda14cbcSMatt Macy zfs_close(zhp); 6474*eda14cbcSMatt Macy return (0); 6475*eda14cbcSMatt Macy } 6476*eda14cbcSMatt Macy libzfs_add_handle(state->ga_cbp, zhp); 6477*eda14cbcSMatt Macy assert(state->ga_cbp->cb_used <= state->ga_cbp->cb_alloc); 6478*eda14cbcSMatt Macy 6479*eda14cbcSMatt Macy return (0); 6480*eda14cbcSMatt Macy } 6481*eda14cbcSMatt Macy 6482*eda14cbcSMatt Macy static void 6483*eda14cbcSMatt Macy get_all_datasets(get_all_cb_t *cbp, boolean_t verbose) 6484*eda14cbcSMatt Macy { 6485*eda14cbcSMatt Macy get_all_state_t state = { 6486*eda14cbcSMatt Macy .ga_verbose = verbose, 6487*eda14cbcSMatt Macy .ga_cbp = cbp 6488*eda14cbcSMatt Macy }; 6489*eda14cbcSMatt Macy 6490*eda14cbcSMatt Macy if (verbose) 6491*eda14cbcSMatt Macy set_progress_header(gettext("Reading ZFS config")); 6492*eda14cbcSMatt Macy (void) zfs_iter_root(g_zfs, get_one_dataset, &state); 6493*eda14cbcSMatt Macy 6494*eda14cbcSMatt Macy if (verbose) 6495*eda14cbcSMatt Macy finish_progress(gettext("done.")); 6496*eda14cbcSMatt Macy } 6497*eda14cbcSMatt Macy 6498*eda14cbcSMatt Macy /* 6499*eda14cbcSMatt Macy * Generic callback for sharing or mounting filesystems. Because the code is so 6500*eda14cbcSMatt Macy * similar, we have a common function with an extra parameter to determine which 6501*eda14cbcSMatt Macy * mode we are using. 6502*eda14cbcSMatt Macy */ 6503*eda14cbcSMatt Macy typedef enum { OP_SHARE, OP_MOUNT } share_mount_op_t; 6504*eda14cbcSMatt Macy 6505*eda14cbcSMatt Macy typedef struct share_mount_state { 6506*eda14cbcSMatt Macy share_mount_op_t sm_op; 6507*eda14cbcSMatt Macy boolean_t sm_verbose; 6508*eda14cbcSMatt Macy int sm_flags; 6509*eda14cbcSMatt Macy char *sm_options; 6510*eda14cbcSMatt Macy char *sm_proto; /* only valid for OP_SHARE */ 6511*eda14cbcSMatt Macy pthread_mutex_t sm_lock; /* protects the remaining fields */ 6512*eda14cbcSMatt Macy uint_t sm_total; /* number of filesystems to process */ 6513*eda14cbcSMatt Macy uint_t sm_done; /* number of filesystems processed */ 6514*eda14cbcSMatt Macy int sm_status; /* -1 if any of the share/mount operations failed */ 6515*eda14cbcSMatt Macy } share_mount_state_t; 6516*eda14cbcSMatt Macy 6517*eda14cbcSMatt Macy /* 6518*eda14cbcSMatt Macy * Share or mount a dataset. 6519*eda14cbcSMatt Macy */ 6520*eda14cbcSMatt Macy static int 6521*eda14cbcSMatt Macy share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol, 6522*eda14cbcSMatt Macy boolean_t explicit, const char *options) 6523*eda14cbcSMatt Macy { 6524*eda14cbcSMatt Macy char mountpoint[ZFS_MAXPROPLEN]; 6525*eda14cbcSMatt Macy char shareopts[ZFS_MAXPROPLEN]; 6526*eda14cbcSMatt Macy char smbshareopts[ZFS_MAXPROPLEN]; 6527*eda14cbcSMatt Macy const char *cmdname = op == OP_SHARE ? "share" : "mount"; 6528*eda14cbcSMatt Macy struct mnttab mnt; 6529*eda14cbcSMatt Macy uint64_t zoned, canmount; 6530*eda14cbcSMatt Macy boolean_t shared_nfs, shared_smb; 6531*eda14cbcSMatt Macy 6532*eda14cbcSMatt Macy assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM); 6533*eda14cbcSMatt Macy 6534*eda14cbcSMatt Macy /* 6535*eda14cbcSMatt Macy * Check to make sure we can mount/share this dataset. If we 6536*eda14cbcSMatt Macy * are in the global zone and the filesystem is exported to a 6537*eda14cbcSMatt Macy * local zone, or if we are in a local zone and the 6538*eda14cbcSMatt Macy * filesystem is not exported, then it is an error. 6539*eda14cbcSMatt Macy */ 6540*eda14cbcSMatt Macy zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 6541*eda14cbcSMatt Macy 6542*eda14cbcSMatt Macy if (zoned && getzoneid() == GLOBAL_ZONEID) { 6543*eda14cbcSMatt Macy if (!explicit) 6544*eda14cbcSMatt Macy return (0); 6545*eda14cbcSMatt Macy 6546*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot %s '%s': " 6547*eda14cbcSMatt Macy "dataset is exported to a local zone\n"), cmdname, 6548*eda14cbcSMatt Macy zfs_get_name(zhp)); 6549*eda14cbcSMatt Macy return (1); 6550*eda14cbcSMatt Macy 6551*eda14cbcSMatt Macy } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 6552*eda14cbcSMatt Macy if (!explicit) 6553*eda14cbcSMatt Macy return (0); 6554*eda14cbcSMatt Macy 6555*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot %s '%s': " 6556*eda14cbcSMatt Macy "permission denied\n"), cmdname, 6557*eda14cbcSMatt Macy zfs_get_name(zhp)); 6558*eda14cbcSMatt Macy return (1); 6559*eda14cbcSMatt Macy } 6560*eda14cbcSMatt Macy 6561*eda14cbcSMatt Macy /* 6562*eda14cbcSMatt Macy * Ignore any filesystems which don't apply to us. This 6563*eda14cbcSMatt Macy * includes those with a legacy mountpoint, or those with 6564*eda14cbcSMatt Macy * legacy share options. 6565*eda14cbcSMatt Macy */ 6566*eda14cbcSMatt Macy verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 6567*eda14cbcSMatt Macy sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); 6568*eda14cbcSMatt Macy verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 6569*eda14cbcSMatt Macy sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 6570*eda14cbcSMatt Macy verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts, 6571*eda14cbcSMatt Macy sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0); 6572*eda14cbcSMatt Macy 6573*eda14cbcSMatt Macy if (op == OP_SHARE && strcmp(shareopts, "off") == 0 && 6574*eda14cbcSMatt Macy strcmp(smbshareopts, "off") == 0) { 6575*eda14cbcSMatt Macy if (!explicit) 6576*eda14cbcSMatt Macy return (0); 6577*eda14cbcSMatt Macy 6578*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot share '%s': " 6579*eda14cbcSMatt Macy "legacy share\n"), zfs_get_name(zhp)); 6580*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("use share(1M) to " 6581*eda14cbcSMatt Macy "share this filesystem, or set " 6582*eda14cbcSMatt Macy "sharenfs property on\n")); 6583*eda14cbcSMatt Macy return (1); 6584*eda14cbcSMatt Macy } 6585*eda14cbcSMatt Macy 6586*eda14cbcSMatt Macy /* 6587*eda14cbcSMatt Macy * We cannot share or mount legacy filesystems. If the 6588*eda14cbcSMatt Macy * shareopts is non-legacy but the mountpoint is legacy, we 6589*eda14cbcSMatt Macy * treat it as a legacy share. 6590*eda14cbcSMatt Macy */ 6591*eda14cbcSMatt Macy if (strcmp(mountpoint, "legacy") == 0) { 6592*eda14cbcSMatt Macy if (!explicit) 6593*eda14cbcSMatt Macy return (0); 6594*eda14cbcSMatt Macy 6595*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot %s '%s': " 6596*eda14cbcSMatt Macy "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 6597*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("use %s(1M) to " 6598*eda14cbcSMatt Macy "%s this filesystem\n"), cmdname, cmdname); 6599*eda14cbcSMatt Macy return (1); 6600*eda14cbcSMatt Macy } 6601*eda14cbcSMatt Macy 6602*eda14cbcSMatt Macy if (strcmp(mountpoint, "none") == 0) { 6603*eda14cbcSMatt Macy if (!explicit) 6604*eda14cbcSMatt Macy return (0); 6605*eda14cbcSMatt Macy 6606*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot %s '%s': no " 6607*eda14cbcSMatt Macy "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 6608*eda14cbcSMatt Macy return (1); 6609*eda14cbcSMatt Macy } 6610*eda14cbcSMatt Macy 6611*eda14cbcSMatt Macy /* 6612*eda14cbcSMatt Macy * canmount explicit outcome 6613*eda14cbcSMatt Macy * on no pass through 6614*eda14cbcSMatt Macy * on yes pass through 6615*eda14cbcSMatt Macy * off no return 0 6616*eda14cbcSMatt Macy * off yes display error, return 1 6617*eda14cbcSMatt Macy * noauto no return 0 6618*eda14cbcSMatt Macy * noauto yes pass through 6619*eda14cbcSMatt Macy */ 6620*eda14cbcSMatt Macy canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); 6621*eda14cbcSMatt Macy if (canmount == ZFS_CANMOUNT_OFF) { 6622*eda14cbcSMatt Macy if (!explicit) 6623*eda14cbcSMatt Macy return (0); 6624*eda14cbcSMatt Macy 6625*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot %s '%s': " 6626*eda14cbcSMatt Macy "'canmount' property is set to 'off'\n"), cmdname, 6627*eda14cbcSMatt Macy zfs_get_name(zhp)); 6628*eda14cbcSMatt Macy return (1); 6629*eda14cbcSMatt Macy } else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) { 6630*eda14cbcSMatt Macy /* 6631*eda14cbcSMatt Macy * When performing a 'zfs mount -a', we skip any mounts for 6632*eda14cbcSMatt Macy * datasets that have 'noauto' set. Sharing a dataset with 6633*eda14cbcSMatt Macy * 'noauto' set is only allowed if it's mounted. 6634*eda14cbcSMatt Macy */ 6635*eda14cbcSMatt Macy if (op == OP_MOUNT) 6636*eda14cbcSMatt Macy return (0); 6637*eda14cbcSMatt Macy if (op == OP_SHARE && !zfs_is_mounted(zhp, NULL)) { 6638*eda14cbcSMatt Macy /* also purge it from existing exports */ 6639*eda14cbcSMatt Macy zfs_unshareall_bypath(zhp, mountpoint); 6640*eda14cbcSMatt Macy return (0); 6641*eda14cbcSMatt Macy } 6642*eda14cbcSMatt Macy } 6643*eda14cbcSMatt Macy 6644*eda14cbcSMatt Macy /* 6645*eda14cbcSMatt Macy * If this filesystem is encrypted and does not have 6646*eda14cbcSMatt Macy * a loaded key, we can not mount it. 6647*eda14cbcSMatt Macy */ 6648*eda14cbcSMatt Macy if ((flags & MS_CRYPT) == 0 && 6649*eda14cbcSMatt Macy zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF && 6650*eda14cbcSMatt Macy zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS) == 6651*eda14cbcSMatt Macy ZFS_KEYSTATUS_UNAVAILABLE) { 6652*eda14cbcSMatt Macy if (!explicit) 6653*eda14cbcSMatt Macy return (0); 6654*eda14cbcSMatt Macy 6655*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot %s '%s': " 6656*eda14cbcSMatt Macy "encryption key not loaded\n"), cmdname, zfs_get_name(zhp)); 6657*eda14cbcSMatt Macy return (1); 6658*eda14cbcSMatt Macy } 6659*eda14cbcSMatt Macy 6660*eda14cbcSMatt Macy /* 6661*eda14cbcSMatt Macy * If this filesystem is inconsistent and has a receive resume 6662*eda14cbcSMatt Macy * token, we can not mount it. 6663*eda14cbcSMatt Macy */ 6664*eda14cbcSMatt Macy if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) && 6665*eda14cbcSMatt Macy zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN, 6666*eda14cbcSMatt Macy NULL, 0, NULL, NULL, 0, B_TRUE) == 0) { 6667*eda14cbcSMatt Macy if (!explicit) 6668*eda14cbcSMatt Macy return (0); 6669*eda14cbcSMatt Macy 6670*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot %s '%s': " 6671*eda14cbcSMatt Macy "Contains partially-completed state from " 6672*eda14cbcSMatt Macy "\"zfs receive -s\", which can be resumed with " 6673*eda14cbcSMatt Macy "\"zfs send -t\"\n"), 6674*eda14cbcSMatt Macy cmdname, zfs_get_name(zhp)); 6675*eda14cbcSMatt Macy return (1); 6676*eda14cbcSMatt Macy } 6677*eda14cbcSMatt Macy 6678*eda14cbcSMatt Macy if (zfs_prop_get_int(zhp, ZFS_PROP_REDACTED) && !(flags & MS_FORCE)) { 6679*eda14cbcSMatt Macy if (!explicit) 6680*eda14cbcSMatt Macy return (0); 6681*eda14cbcSMatt Macy 6682*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot %s '%s': " 6683*eda14cbcSMatt Macy "Dataset is not complete, was created by receiving " 6684*eda14cbcSMatt Macy "a redacted zfs send stream.\n"), cmdname, 6685*eda14cbcSMatt Macy zfs_get_name(zhp)); 6686*eda14cbcSMatt Macy return (1); 6687*eda14cbcSMatt Macy } 6688*eda14cbcSMatt Macy 6689*eda14cbcSMatt Macy /* 6690*eda14cbcSMatt Macy * At this point, we have verified that the mountpoint and/or 6691*eda14cbcSMatt Macy * shareopts are appropriate for auto management. If the 6692*eda14cbcSMatt Macy * filesystem is already mounted or shared, return (failing 6693*eda14cbcSMatt Macy * for explicit requests); otherwise mount or share the 6694*eda14cbcSMatt Macy * filesystem. 6695*eda14cbcSMatt Macy */ 6696*eda14cbcSMatt Macy switch (op) { 6697*eda14cbcSMatt Macy case OP_SHARE: 6698*eda14cbcSMatt Macy 6699*eda14cbcSMatt Macy shared_nfs = zfs_is_shared_nfs(zhp, NULL); 6700*eda14cbcSMatt Macy shared_smb = zfs_is_shared_smb(zhp, NULL); 6701*eda14cbcSMatt Macy 6702*eda14cbcSMatt Macy if ((shared_nfs && shared_smb) || 6703*eda14cbcSMatt Macy (shared_nfs && strcmp(shareopts, "on") == 0 && 6704*eda14cbcSMatt Macy strcmp(smbshareopts, "off") == 0) || 6705*eda14cbcSMatt Macy (shared_smb && strcmp(smbshareopts, "on") == 0 && 6706*eda14cbcSMatt Macy strcmp(shareopts, "off") == 0)) { 6707*eda14cbcSMatt Macy if (!explicit) 6708*eda14cbcSMatt Macy return (0); 6709*eda14cbcSMatt Macy 6710*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot share " 6711*eda14cbcSMatt Macy "'%s': filesystem already shared\n"), 6712*eda14cbcSMatt Macy zfs_get_name(zhp)); 6713*eda14cbcSMatt Macy return (1); 6714*eda14cbcSMatt Macy } 6715*eda14cbcSMatt Macy 6716*eda14cbcSMatt Macy if (!zfs_is_mounted(zhp, NULL) && 6717*eda14cbcSMatt Macy zfs_mount(zhp, NULL, flags) != 0) 6718*eda14cbcSMatt Macy return (1); 6719*eda14cbcSMatt Macy 6720*eda14cbcSMatt Macy if (protocol == NULL) { 6721*eda14cbcSMatt Macy if (zfs_shareall(zhp) != 0) 6722*eda14cbcSMatt Macy return (1); 6723*eda14cbcSMatt Macy } else if (strcmp(protocol, "nfs") == 0) { 6724*eda14cbcSMatt Macy if (zfs_share_nfs(zhp)) 6725*eda14cbcSMatt Macy return (1); 6726*eda14cbcSMatt Macy } else if (strcmp(protocol, "smb") == 0) { 6727*eda14cbcSMatt Macy if (zfs_share_smb(zhp)) 6728*eda14cbcSMatt Macy return (1); 6729*eda14cbcSMatt Macy } else { 6730*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot share " 6731*eda14cbcSMatt Macy "'%s': invalid share type '%s' " 6732*eda14cbcSMatt Macy "specified\n"), 6733*eda14cbcSMatt Macy zfs_get_name(zhp), protocol); 6734*eda14cbcSMatt Macy return (1); 6735*eda14cbcSMatt Macy } 6736*eda14cbcSMatt Macy 6737*eda14cbcSMatt Macy break; 6738*eda14cbcSMatt Macy 6739*eda14cbcSMatt Macy case OP_MOUNT: 6740*eda14cbcSMatt Macy if (options == NULL) 6741*eda14cbcSMatt Macy mnt.mnt_mntopts = ""; 6742*eda14cbcSMatt Macy else 6743*eda14cbcSMatt Macy mnt.mnt_mntopts = (char *)options; 6744*eda14cbcSMatt Macy 6745*eda14cbcSMatt Macy if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 6746*eda14cbcSMatt Macy zfs_is_mounted(zhp, NULL)) { 6747*eda14cbcSMatt Macy if (!explicit) 6748*eda14cbcSMatt Macy return (0); 6749*eda14cbcSMatt Macy 6750*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot mount " 6751*eda14cbcSMatt Macy "'%s': filesystem already mounted\n"), 6752*eda14cbcSMatt Macy zfs_get_name(zhp)); 6753*eda14cbcSMatt Macy return (1); 6754*eda14cbcSMatt Macy } 6755*eda14cbcSMatt Macy 6756*eda14cbcSMatt Macy if (zfs_mount(zhp, options, flags) != 0) 6757*eda14cbcSMatt Macy return (1); 6758*eda14cbcSMatt Macy break; 6759*eda14cbcSMatt Macy } 6760*eda14cbcSMatt Macy 6761*eda14cbcSMatt Macy return (0); 6762*eda14cbcSMatt Macy } 6763*eda14cbcSMatt Macy 6764*eda14cbcSMatt Macy /* 6765*eda14cbcSMatt Macy * Reports progress in the form "(current/total)". Not thread-safe. 6766*eda14cbcSMatt Macy */ 6767*eda14cbcSMatt Macy static void 6768*eda14cbcSMatt Macy report_mount_progress(int current, int total) 6769*eda14cbcSMatt Macy { 6770*eda14cbcSMatt Macy static time_t last_progress_time = 0; 6771*eda14cbcSMatt Macy time_t now = time(NULL); 6772*eda14cbcSMatt Macy char info[32]; 6773*eda14cbcSMatt Macy 6774*eda14cbcSMatt Macy /* report 1..n instead of 0..n-1 */ 6775*eda14cbcSMatt Macy ++current; 6776*eda14cbcSMatt Macy 6777*eda14cbcSMatt Macy /* display header if we're here for the first time */ 6778*eda14cbcSMatt Macy if (current == 1) { 6779*eda14cbcSMatt Macy set_progress_header(gettext("Mounting ZFS filesystems")); 6780*eda14cbcSMatt Macy } else if (current != total && last_progress_time + MOUNT_TIME >= now) { 6781*eda14cbcSMatt Macy /* too soon to report again */ 6782*eda14cbcSMatt Macy return; 6783*eda14cbcSMatt Macy } 6784*eda14cbcSMatt Macy 6785*eda14cbcSMatt Macy last_progress_time = now; 6786*eda14cbcSMatt Macy 6787*eda14cbcSMatt Macy (void) sprintf(info, "(%d/%d)", current, total); 6788*eda14cbcSMatt Macy 6789*eda14cbcSMatt Macy if (current == total) 6790*eda14cbcSMatt Macy finish_progress(info); 6791*eda14cbcSMatt Macy else 6792*eda14cbcSMatt Macy update_progress(info); 6793*eda14cbcSMatt Macy } 6794*eda14cbcSMatt Macy 6795*eda14cbcSMatt Macy /* 6796*eda14cbcSMatt Macy * zfs_foreach_mountpoint() callback that mounts or shares one filesystem and 6797*eda14cbcSMatt Macy * updates the progress meter. 6798*eda14cbcSMatt Macy */ 6799*eda14cbcSMatt Macy static int 6800*eda14cbcSMatt Macy share_mount_one_cb(zfs_handle_t *zhp, void *arg) 6801*eda14cbcSMatt Macy { 6802*eda14cbcSMatt Macy share_mount_state_t *sms = arg; 6803*eda14cbcSMatt Macy int ret; 6804*eda14cbcSMatt Macy 6805*eda14cbcSMatt Macy ret = share_mount_one(zhp, sms->sm_op, sms->sm_flags, sms->sm_proto, 6806*eda14cbcSMatt Macy B_FALSE, sms->sm_options); 6807*eda14cbcSMatt Macy 6808*eda14cbcSMatt Macy pthread_mutex_lock(&sms->sm_lock); 6809*eda14cbcSMatt Macy if (ret != 0) 6810*eda14cbcSMatt Macy sms->sm_status = ret; 6811*eda14cbcSMatt Macy sms->sm_done++; 6812*eda14cbcSMatt Macy if (sms->sm_verbose) 6813*eda14cbcSMatt Macy report_mount_progress(sms->sm_done, sms->sm_total); 6814*eda14cbcSMatt Macy pthread_mutex_unlock(&sms->sm_lock); 6815*eda14cbcSMatt Macy return (ret); 6816*eda14cbcSMatt Macy } 6817*eda14cbcSMatt Macy 6818*eda14cbcSMatt Macy static void 6819*eda14cbcSMatt Macy append_options(char *mntopts, char *newopts) 6820*eda14cbcSMatt Macy { 6821*eda14cbcSMatt Macy int len = strlen(mntopts); 6822*eda14cbcSMatt Macy 6823*eda14cbcSMatt Macy /* original length plus new string to append plus 1 for the comma */ 6824*eda14cbcSMatt Macy if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) { 6825*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("the opts argument for " 6826*eda14cbcSMatt Macy "'%s' option is too long (more than %d chars)\n"), 6827*eda14cbcSMatt Macy "-o", MNT_LINE_MAX); 6828*eda14cbcSMatt Macy usage(B_FALSE); 6829*eda14cbcSMatt Macy } 6830*eda14cbcSMatt Macy 6831*eda14cbcSMatt Macy if (*mntopts) 6832*eda14cbcSMatt Macy mntopts[len++] = ','; 6833*eda14cbcSMatt Macy 6834*eda14cbcSMatt Macy (void) strcpy(&mntopts[len], newopts); 6835*eda14cbcSMatt Macy } 6836*eda14cbcSMatt Macy 6837*eda14cbcSMatt Macy static int 6838*eda14cbcSMatt Macy share_mount(int op, int argc, char **argv) 6839*eda14cbcSMatt Macy { 6840*eda14cbcSMatt Macy int do_all = 0; 6841*eda14cbcSMatt Macy boolean_t verbose = B_FALSE; 6842*eda14cbcSMatt Macy int c, ret = 0; 6843*eda14cbcSMatt Macy char *options = NULL; 6844*eda14cbcSMatt Macy int flags = 0; 6845*eda14cbcSMatt Macy 6846*eda14cbcSMatt Macy /* check options */ 6847*eda14cbcSMatt Macy while ((c = getopt(argc, argv, op == OP_MOUNT ? ":alvo:Of" : "al")) 6848*eda14cbcSMatt Macy != -1) { 6849*eda14cbcSMatt Macy switch (c) { 6850*eda14cbcSMatt Macy case 'a': 6851*eda14cbcSMatt Macy do_all = 1; 6852*eda14cbcSMatt Macy break; 6853*eda14cbcSMatt Macy case 'v': 6854*eda14cbcSMatt Macy verbose = B_TRUE; 6855*eda14cbcSMatt Macy break; 6856*eda14cbcSMatt Macy case 'l': 6857*eda14cbcSMatt Macy flags |= MS_CRYPT; 6858*eda14cbcSMatt Macy break; 6859*eda14cbcSMatt Macy case 'o': 6860*eda14cbcSMatt Macy if (*optarg == '\0') { 6861*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("empty mount " 6862*eda14cbcSMatt Macy "options (-o) specified\n")); 6863*eda14cbcSMatt Macy usage(B_FALSE); 6864*eda14cbcSMatt Macy } 6865*eda14cbcSMatt Macy 6866*eda14cbcSMatt Macy if (options == NULL) 6867*eda14cbcSMatt Macy options = safe_malloc(MNT_LINE_MAX + 1); 6868*eda14cbcSMatt Macy 6869*eda14cbcSMatt Macy /* option validation is done later */ 6870*eda14cbcSMatt Macy append_options(options, optarg); 6871*eda14cbcSMatt Macy break; 6872*eda14cbcSMatt Macy case 'O': 6873*eda14cbcSMatt Macy flags |= MS_OVERLAY; 6874*eda14cbcSMatt Macy break; 6875*eda14cbcSMatt Macy case 'f': 6876*eda14cbcSMatt Macy flags |= MS_FORCE; 6877*eda14cbcSMatt Macy break; 6878*eda14cbcSMatt Macy case ':': 6879*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing argument for " 6880*eda14cbcSMatt Macy "'%c' option\n"), optopt); 6881*eda14cbcSMatt Macy usage(B_FALSE); 6882*eda14cbcSMatt Macy break; 6883*eda14cbcSMatt Macy case '?': 6884*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 6885*eda14cbcSMatt Macy optopt); 6886*eda14cbcSMatt Macy usage(B_FALSE); 6887*eda14cbcSMatt Macy } 6888*eda14cbcSMatt Macy } 6889*eda14cbcSMatt Macy 6890*eda14cbcSMatt Macy argc -= optind; 6891*eda14cbcSMatt Macy argv += optind; 6892*eda14cbcSMatt Macy 6893*eda14cbcSMatt Macy /* check number of arguments */ 6894*eda14cbcSMatt Macy if (do_all) { 6895*eda14cbcSMatt Macy char *protocol = NULL; 6896*eda14cbcSMatt Macy 6897*eda14cbcSMatt Macy if (op == OP_SHARE && argc > 0) { 6898*eda14cbcSMatt Macy if (strcmp(argv[0], "nfs") != 0 && 6899*eda14cbcSMatt Macy strcmp(argv[0], "smb") != 0) { 6900*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("share type " 6901*eda14cbcSMatt Macy "must be 'nfs' or 'smb'\n")); 6902*eda14cbcSMatt Macy usage(B_FALSE); 6903*eda14cbcSMatt Macy } 6904*eda14cbcSMatt Macy protocol = argv[0]; 6905*eda14cbcSMatt Macy argc--; 6906*eda14cbcSMatt Macy argv++; 6907*eda14cbcSMatt Macy } 6908*eda14cbcSMatt Macy 6909*eda14cbcSMatt Macy if (argc != 0) { 6910*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many arguments\n")); 6911*eda14cbcSMatt Macy usage(B_FALSE); 6912*eda14cbcSMatt Macy } 6913*eda14cbcSMatt Macy 6914*eda14cbcSMatt Macy start_progress_timer(); 6915*eda14cbcSMatt Macy get_all_cb_t cb = { 0 }; 6916*eda14cbcSMatt Macy get_all_datasets(&cb, verbose); 6917*eda14cbcSMatt Macy 6918*eda14cbcSMatt Macy if (cb.cb_used == 0) { 6919*eda14cbcSMatt Macy if (options != NULL) 6920*eda14cbcSMatt Macy free(options); 6921*eda14cbcSMatt Macy return (0); 6922*eda14cbcSMatt Macy } 6923*eda14cbcSMatt Macy 6924*eda14cbcSMatt Macy share_mount_state_t share_mount_state = { 0 }; 6925*eda14cbcSMatt Macy share_mount_state.sm_op = op; 6926*eda14cbcSMatt Macy share_mount_state.sm_verbose = verbose; 6927*eda14cbcSMatt Macy share_mount_state.sm_flags = flags; 6928*eda14cbcSMatt Macy share_mount_state.sm_options = options; 6929*eda14cbcSMatt Macy share_mount_state.sm_proto = protocol; 6930*eda14cbcSMatt Macy share_mount_state.sm_total = cb.cb_used; 6931*eda14cbcSMatt Macy pthread_mutex_init(&share_mount_state.sm_lock, NULL); 6932*eda14cbcSMatt Macy 6933*eda14cbcSMatt Macy /* 6934*eda14cbcSMatt Macy * libshare isn't mt-safe, so only do the operation in parallel 6935*eda14cbcSMatt Macy * if we're mounting. Additionally, the key-loading option must 6936*eda14cbcSMatt Macy * be serialized so that we can prompt the user for their keys 6937*eda14cbcSMatt Macy * in a consistent manner. 6938*eda14cbcSMatt Macy */ 6939*eda14cbcSMatt Macy zfs_foreach_mountpoint(g_zfs, cb.cb_handles, cb.cb_used, 6940*eda14cbcSMatt Macy share_mount_one_cb, &share_mount_state, 6941*eda14cbcSMatt Macy op == OP_MOUNT && !(flags & MS_CRYPT)); 6942*eda14cbcSMatt Macy zfs_commit_all_shares(); 6943*eda14cbcSMatt Macy 6944*eda14cbcSMatt Macy ret = share_mount_state.sm_status; 6945*eda14cbcSMatt Macy 6946*eda14cbcSMatt Macy for (int i = 0; i < cb.cb_used; i++) 6947*eda14cbcSMatt Macy zfs_close(cb.cb_handles[i]); 6948*eda14cbcSMatt Macy free(cb.cb_handles); 6949*eda14cbcSMatt Macy } else if (argc == 0) { 6950*eda14cbcSMatt Macy struct mnttab entry; 6951*eda14cbcSMatt Macy 6952*eda14cbcSMatt Macy if ((op == OP_SHARE) || (options != NULL)) { 6953*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing filesystem " 6954*eda14cbcSMatt Macy "argument (specify -a for all)\n")); 6955*eda14cbcSMatt Macy usage(B_FALSE); 6956*eda14cbcSMatt Macy } 6957*eda14cbcSMatt Macy 6958*eda14cbcSMatt Macy /* 6959*eda14cbcSMatt Macy * When mount is given no arguments, go through 6960*eda14cbcSMatt Macy * /proc/self/mounts and display any active ZFS mounts. 6961*eda14cbcSMatt Macy * We hide any snapshots, since they are controlled 6962*eda14cbcSMatt Macy * automatically. 6963*eda14cbcSMatt Macy */ 6964*eda14cbcSMatt Macy 6965*eda14cbcSMatt Macy /* Reopen MNTTAB to prevent reading stale data from open file */ 6966*eda14cbcSMatt Macy if (freopen(MNTTAB, "r", mnttab_file) == NULL) { 6967*eda14cbcSMatt Macy if (options != NULL) 6968*eda14cbcSMatt Macy free(options); 6969*eda14cbcSMatt Macy return (ENOENT); 6970*eda14cbcSMatt Macy } 6971*eda14cbcSMatt Macy 6972*eda14cbcSMatt Macy while (getmntent(mnttab_file, &entry) == 0) { 6973*eda14cbcSMatt Macy if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 6974*eda14cbcSMatt Macy strchr(entry.mnt_special, '@') != NULL) 6975*eda14cbcSMatt Macy continue; 6976*eda14cbcSMatt Macy 6977*eda14cbcSMatt Macy (void) printf("%-30s %s\n", entry.mnt_special, 6978*eda14cbcSMatt Macy entry.mnt_mountp); 6979*eda14cbcSMatt Macy } 6980*eda14cbcSMatt Macy 6981*eda14cbcSMatt Macy } else { 6982*eda14cbcSMatt Macy zfs_handle_t *zhp; 6983*eda14cbcSMatt Macy 6984*eda14cbcSMatt Macy if (argc > 1) { 6985*eda14cbcSMatt Macy (void) fprintf(stderr, 6986*eda14cbcSMatt Macy gettext("too many arguments\n")); 6987*eda14cbcSMatt Macy usage(B_FALSE); 6988*eda14cbcSMatt Macy } 6989*eda14cbcSMatt Macy 6990*eda14cbcSMatt Macy if ((zhp = zfs_open(g_zfs, argv[0], 6991*eda14cbcSMatt Macy ZFS_TYPE_FILESYSTEM)) == NULL) { 6992*eda14cbcSMatt Macy ret = 1; 6993*eda14cbcSMatt Macy } else { 6994*eda14cbcSMatt Macy ret = share_mount_one(zhp, op, flags, NULL, B_TRUE, 6995*eda14cbcSMatt Macy options); 6996*eda14cbcSMatt Macy zfs_commit_all_shares(); 6997*eda14cbcSMatt Macy zfs_close(zhp); 6998*eda14cbcSMatt Macy } 6999*eda14cbcSMatt Macy } 7000*eda14cbcSMatt Macy 7001*eda14cbcSMatt Macy if (options != NULL) 7002*eda14cbcSMatt Macy free(options); 7003*eda14cbcSMatt Macy 7004*eda14cbcSMatt Macy return (ret); 7005*eda14cbcSMatt Macy } 7006*eda14cbcSMatt Macy 7007*eda14cbcSMatt Macy /* 7008*eda14cbcSMatt Macy * zfs mount -a [nfs] 7009*eda14cbcSMatt Macy * zfs mount filesystem 7010*eda14cbcSMatt Macy * 7011*eda14cbcSMatt Macy * Mount all filesystems, or mount the given filesystem. 7012*eda14cbcSMatt Macy */ 7013*eda14cbcSMatt Macy static int 7014*eda14cbcSMatt Macy zfs_do_mount(int argc, char **argv) 7015*eda14cbcSMatt Macy { 7016*eda14cbcSMatt Macy return (share_mount(OP_MOUNT, argc, argv)); 7017*eda14cbcSMatt Macy } 7018*eda14cbcSMatt Macy 7019*eda14cbcSMatt Macy /* 7020*eda14cbcSMatt Macy * zfs share -a [nfs | smb] 7021*eda14cbcSMatt Macy * zfs share filesystem 7022*eda14cbcSMatt Macy * 7023*eda14cbcSMatt Macy * Share all filesystems, or share the given filesystem. 7024*eda14cbcSMatt Macy */ 7025*eda14cbcSMatt Macy static int 7026*eda14cbcSMatt Macy zfs_do_share(int argc, char **argv) 7027*eda14cbcSMatt Macy { 7028*eda14cbcSMatt Macy return (share_mount(OP_SHARE, argc, argv)); 7029*eda14cbcSMatt Macy } 7030*eda14cbcSMatt Macy 7031*eda14cbcSMatt Macy typedef struct unshare_unmount_node { 7032*eda14cbcSMatt Macy zfs_handle_t *un_zhp; 7033*eda14cbcSMatt Macy char *un_mountp; 7034*eda14cbcSMatt Macy uu_avl_node_t un_avlnode; 7035*eda14cbcSMatt Macy } unshare_unmount_node_t; 7036*eda14cbcSMatt Macy 7037*eda14cbcSMatt Macy /* ARGSUSED */ 7038*eda14cbcSMatt Macy static int 7039*eda14cbcSMatt Macy unshare_unmount_compare(const void *larg, const void *rarg, void *unused) 7040*eda14cbcSMatt Macy { 7041*eda14cbcSMatt Macy const unshare_unmount_node_t *l = larg; 7042*eda14cbcSMatt Macy const unshare_unmount_node_t *r = rarg; 7043*eda14cbcSMatt Macy 7044*eda14cbcSMatt Macy return (strcmp(l->un_mountp, r->un_mountp)); 7045*eda14cbcSMatt Macy } 7046*eda14cbcSMatt Macy 7047*eda14cbcSMatt Macy /* 7048*eda14cbcSMatt Macy * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 7049*eda14cbcSMatt Macy * absolute path, find the entry /proc/self/mounts, verify that it's a 7050*eda14cbcSMatt Macy * ZFS filesystem, and unmount it appropriately. 7051*eda14cbcSMatt Macy */ 7052*eda14cbcSMatt Macy static int 7053*eda14cbcSMatt Macy unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) 7054*eda14cbcSMatt Macy { 7055*eda14cbcSMatt Macy zfs_handle_t *zhp; 7056*eda14cbcSMatt Macy int ret = 0; 7057*eda14cbcSMatt Macy struct stat64 statbuf; 7058*eda14cbcSMatt Macy struct extmnttab entry; 7059*eda14cbcSMatt Macy const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount"; 7060*eda14cbcSMatt Macy ino_t path_inode; 7061*eda14cbcSMatt Macy 7062*eda14cbcSMatt Macy /* 7063*eda14cbcSMatt Macy * Search for the given (major,minor) pair in the mount table. 7064*eda14cbcSMatt Macy */ 7065*eda14cbcSMatt Macy 7066*eda14cbcSMatt Macy /* Reopen MNTTAB to prevent reading stale data from open file */ 7067*eda14cbcSMatt Macy if (freopen(MNTTAB, "r", mnttab_file) == NULL) 7068*eda14cbcSMatt Macy return (ENOENT); 7069*eda14cbcSMatt Macy 7070*eda14cbcSMatt Macy if (getextmntent(path, &entry, &statbuf) != 0) { 7071*eda14cbcSMatt Macy if (op == OP_SHARE) { 7072*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot %s '%s': not " 7073*eda14cbcSMatt Macy "currently mounted\n"), cmdname, path); 7074*eda14cbcSMatt Macy return (1); 7075*eda14cbcSMatt Macy } 7076*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("warning: %s not in" 7077*eda14cbcSMatt Macy "/proc/self/mounts\n"), path); 7078*eda14cbcSMatt Macy if ((ret = umount2(path, flags)) != 0) 7079*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("%s: %s\n"), path, 7080*eda14cbcSMatt Macy strerror(errno)); 7081*eda14cbcSMatt Macy return (ret != 0); 7082*eda14cbcSMatt Macy } 7083*eda14cbcSMatt Macy path_inode = statbuf.st_ino; 7084*eda14cbcSMatt Macy 7085*eda14cbcSMatt Macy if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 7086*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 7087*eda14cbcSMatt Macy "filesystem\n"), cmdname, path); 7088*eda14cbcSMatt Macy return (1); 7089*eda14cbcSMatt Macy } 7090*eda14cbcSMatt Macy 7091*eda14cbcSMatt Macy if ((zhp = zfs_open(g_zfs, entry.mnt_special, 7092*eda14cbcSMatt Macy ZFS_TYPE_FILESYSTEM)) == NULL) 7093*eda14cbcSMatt Macy return (1); 7094*eda14cbcSMatt Macy 7095*eda14cbcSMatt Macy ret = 1; 7096*eda14cbcSMatt Macy if (stat64(entry.mnt_mountp, &statbuf) != 0) { 7097*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 7098*eda14cbcSMatt Macy cmdname, path, strerror(errno)); 7099*eda14cbcSMatt Macy goto out; 7100*eda14cbcSMatt Macy } else if (statbuf.st_ino != path_inode) { 7101*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot " 7102*eda14cbcSMatt Macy "%s '%s': not a mountpoint\n"), cmdname, path); 7103*eda14cbcSMatt Macy goto out; 7104*eda14cbcSMatt Macy } 7105*eda14cbcSMatt Macy 7106*eda14cbcSMatt Macy if (op == OP_SHARE) { 7107*eda14cbcSMatt Macy char nfs_mnt_prop[ZFS_MAXPROPLEN]; 7108*eda14cbcSMatt Macy char smbshare_prop[ZFS_MAXPROPLEN]; 7109*eda14cbcSMatt Macy 7110*eda14cbcSMatt Macy verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, nfs_mnt_prop, 7111*eda14cbcSMatt Macy sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0); 7112*eda14cbcSMatt Macy verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshare_prop, 7113*eda14cbcSMatt Macy sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0); 7114*eda14cbcSMatt Macy 7115*eda14cbcSMatt Macy if (strcmp(nfs_mnt_prop, "off") == 0 && 7116*eda14cbcSMatt Macy strcmp(smbshare_prop, "off") == 0) { 7117*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot unshare " 7118*eda14cbcSMatt Macy "'%s': legacy share\n"), path); 7119*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("use exportfs(8) " 7120*eda14cbcSMatt Macy "or smbcontrol(1) to unshare this filesystem\n")); 7121*eda14cbcSMatt Macy } else if (!zfs_is_shared(zhp)) { 7122*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot unshare '%s': " 7123*eda14cbcSMatt Macy "not currently shared\n"), path); 7124*eda14cbcSMatt Macy } else { 7125*eda14cbcSMatt Macy ret = zfs_unshareall_bypath(zhp, path); 7126*eda14cbcSMatt Macy zfs_commit_all_shares(); 7127*eda14cbcSMatt Macy } 7128*eda14cbcSMatt Macy } else { 7129*eda14cbcSMatt Macy char mtpt_prop[ZFS_MAXPROPLEN]; 7130*eda14cbcSMatt Macy 7131*eda14cbcSMatt Macy verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop, 7132*eda14cbcSMatt Macy sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0); 7133*eda14cbcSMatt Macy 7134*eda14cbcSMatt Macy if (is_manual) { 7135*eda14cbcSMatt Macy ret = zfs_unmount(zhp, NULL, flags); 7136*eda14cbcSMatt Macy } else if (strcmp(mtpt_prop, "legacy") == 0) { 7137*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot unmount " 7138*eda14cbcSMatt Macy "'%s': legacy mountpoint\n"), 7139*eda14cbcSMatt Macy zfs_get_name(zhp)); 7140*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("use umount(8) " 7141*eda14cbcSMatt Macy "to unmount this filesystem\n")); 7142*eda14cbcSMatt Macy } else { 7143*eda14cbcSMatt Macy ret = zfs_unmountall(zhp, flags); 7144*eda14cbcSMatt Macy } 7145*eda14cbcSMatt Macy } 7146*eda14cbcSMatt Macy 7147*eda14cbcSMatt Macy out: 7148*eda14cbcSMatt Macy zfs_close(zhp); 7149*eda14cbcSMatt Macy 7150*eda14cbcSMatt Macy return (ret != 0); 7151*eda14cbcSMatt Macy } 7152*eda14cbcSMatt Macy 7153*eda14cbcSMatt Macy /* 7154*eda14cbcSMatt Macy * Generic callback for unsharing or unmounting a filesystem. 7155*eda14cbcSMatt Macy */ 7156*eda14cbcSMatt Macy static int 7157*eda14cbcSMatt Macy unshare_unmount(int op, int argc, char **argv) 7158*eda14cbcSMatt Macy { 7159*eda14cbcSMatt Macy int do_all = 0; 7160*eda14cbcSMatt Macy int flags = 0; 7161*eda14cbcSMatt Macy int ret = 0; 7162*eda14cbcSMatt Macy int c; 7163*eda14cbcSMatt Macy zfs_handle_t *zhp; 7164*eda14cbcSMatt Macy char nfs_mnt_prop[ZFS_MAXPROPLEN]; 7165*eda14cbcSMatt Macy char sharesmb[ZFS_MAXPROPLEN]; 7166*eda14cbcSMatt Macy 7167*eda14cbcSMatt Macy /* check options */ 7168*eda14cbcSMatt Macy while ((c = getopt(argc, argv, op == OP_SHARE ? ":a" : "afu")) != -1) { 7169*eda14cbcSMatt Macy switch (c) { 7170*eda14cbcSMatt Macy case 'a': 7171*eda14cbcSMatt Macy do_all = 1; 7172*eda14cbcSMatt Macy break; 7173*eda14cbcSMatt Macy case 'f': 7174*eda14cbcSMatt Macy flags |= MS_FORCE; 7175*eda14cbcSMatt Macy break; 7176*eda14cbcSMatt Macy case 'u': 7177*eda14cbcSMatt Macy flags |= MS_CRYPT; 7178*eda14cbcSMatt Macy break; 7179*eda14cbcSMatt Macy case ':': 7180*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing argument for " 7181*eda14cbcSMatt Macy "'%c' option\n"), optopt); 7182*eda14cbcSMatt Macy usage(B_FALSE); 7183*eda14cbcSMatt Macy break; 7184*eda14cbcSMatt Macy case '?': 7185*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 7186*eda14cbcSMatt Macy optopt); 7187*eda14cbcSMatt Macy usage(B_FALSE); 7188*eda14cbcSMatt Macy } 7189*eda14cbcSMatt Macy } 7190*eda14cbcSMatt Macy 7191*eda14cbcSMatt Macy argc -= optind; 7192*eda14cbcSMatt Macy argv += optind; 7193*eda14cbcSMatt Macy 7194*eda14cbcSMatt Macy if (do_all) { 7195*eda14cbcSMatt Macy /* 7196*eda14cbcSMatt Macy * We could make use of zfs_for_each() to walk all datasets in 7197*eda14cbcSMatt Macy * the system, but this would be very inefficient, especially 7198*eda14cbcSMatt Macy * since we would have to linearly search /proc/self/mounts for 7199*eda14cbcSMatt Macy * each one. Instead, do one pass through /proc/self/mounts 7200*eda14cbcSMatt Macy * looking for zfs entries and call zfs_unmount() for each one. 7201*eda14cbcSMatt Macy * 7202*eda14cbcSMatt Macy * Things get a little tricky if the administrator has created 7203*eda14cbcSMatt Macy * mountpoints beneath other ZFS filesystems. In this case, we 7204*eda14cbcSMatt Macy * have to unmount the deepest filesystems first. To accomplish 7205*eda14cbcSMatt Macy * this, we place all the mountpoints in an AVL tree sorted by 7206*eda14cbcSMatt Macy * the special type (dataset name), and walk the result in 7207*eda14cbcSMatt Macy * reverse to make sure to get any snapshots first. 7208*eda14cbcSMatt Macy */ 7209*eda14cbcSMatt Macy struct mnttab entry; 7210*eda14cbcSMatt Macy uu_avl_pool_t *pool; 7211*eda14cbcSMatt Macy uu_avl_t *tree = NULL; 7212*eda14cbcSMatt Macy unshare_unmount_node_t *node; 7213*eda14cbcSMatt Macy uu_avl_index_t idx; 7214*eda14cbcSMatt Macy uu_avl_walk_t *walk; 7215*eda14cbcSMatt Macy char *protocol = NULL; 7216*eda14cbcSMatt Macy 7217*eda14cbcSMatt Macy if (op == OP_SHARE && argc > 0) { 7218*eda14cbcSMatt Macy if (strcmp(argv[0], "nfs") != 0 && 7219*eda14cbcSMatt Macy strcmp(argv[0], "smb") != 0) { 7220*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("share type " 7221*eda14cbcSMatt Macy "must be 'nfs' or 'smb'\n")); 7222*eda14cbcSMatt Macy usage(B_FALSE); 7223*eda14cbcSMatt Macy } 7224*eda14cbcSMatt Macy protocol = argv[0]; 7225*eda14cbcSMatt Macy argc--; 7226*eda14cbcSMatt Macy argv++; 7227*eda14cbcSMatt Macy } 7228*eda14cbcSMatt Macy 7229*eda14cbcSMatt Macy if (argc != 0) { 7230*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many arguments\n")); 7231*eda14cbcSMatt Macy usage(B_FALSE); 7232*eda14cbcSMatt Macy } 7233*eda14cbcSMatt Macy 7234*eda14cbcSMatt Macy if (((pool = uu_avl_pool_create("unmount_pool", 7235*eda14cbcSMatt Macy sizeof (unshare_unmount_node_t), 7236*eda14cbcSMatt Macy offsetof(unshare_unmount_node_t, un_avlnode), 7237*eda14cbcSMatt Macy unshare_unmount_compare, UU_DEFAULT)) == NULL) || 7238*eda14cbcSMatt Macy ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL)) 7239*eda14cbcSMatt Macy nomem(); 7240*eda14cbcSMatt Macy 7241*eda14cbcSMatt Macy /* Reopen MNTTAB to prevent reading stale data from open file */ 7242*eda14cbcSMatt Macy if (freopen(MNTTAB, "r", mnttab_file) == NULL) 7243*eda14cbcSMatt Macy return (ENOENT); 7244*eda14cbcSMatt Macy 7245*eda14cbcSMatt Macy while (getmntent(mnttab_file, &entry) == 0) { 7246*eda14cbcSMatt Macy 7247*eda14cbcSMatt Macy /* ignore non-ZFS entries */ 7248*eda14cbcSMatt Macy if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 7249*eda14cbcSMatt Macy continue; 7250*eda14cbcSMatt Macy 7251*eda14cbcSMatt Macy /* ignore snapshots */ 7252*eda14cbcSMatt Macy if (strchr(entry.mnt_special, '@') != NULL) 7253*eda14cbcSMatt Macy continue; 7254*eda14cbcSMatt Macy 7255*eda14cbcSMatt Macy if ((zhp = zfs_open(g_zfs, entry.mnt_special, 7256*eda14cbcSMatt Macy ZFS_TYPE_FILESYSTEM)) == NULL) { 7257*eda14cbcSMatt Macy ret = 1; 7258*eda14cbcSMatt Macy continue; 7259*eda14cbcSMatt Macy } 7260*eda14cbcSMatt Macy 7261*eda14cbcSMatt Macy /* 7262*eda14cbcSMatt Macy * Ignore datasets that are excluded/restricted by 7263*eda14cbcSMatt Macy * parent pool name. 7264*eda14cbcSMatt Macy */ 7265*eda14cbcSMatt Macy if (zpool_skip_pool(zfs_get_pool_name(zhp))) { 7266*eda14cbcSMatt Macy zfs_close(zhp); 7267*eda14cbcSMatt Macy continue; 7268*eda14cbcSMatt Macy } 7269*eda14cbcSMatt Macy 7270*eda14cbcSMatt Macy switch (op) { 7271*eda14cbcSMatt Macy case OP_SHARE: 7272*eda14cbcSMatt Macy verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, 7273*eda14cbcSMatt Macy nfs_mnt_prop, 7274*eda14cbcSMatt Macy sizeof (nfs_mnt_prop), 7275*eda14cbcSMatt Macy NULL, NULL, 0, B_FALSE) == 0); 7276*eda14cbcSMatt Macy if (strcmp(nfs_mnt_prop, "off") != 0) 7277*eda14cbcSMatt Macy break; 7278*eda14cbcSMatt Macy verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, 7279*eda14cbcSMatt Macy nfs_mnt_prop, 7280*eda14cbcSMatt Macy sizeof (nfs_mnt_prop), 7281*eda14cbcSMatt Macy NULL, NULL, 0, B_FALSE) == 0); 7282*eda14cbcSMatt Macy if (strcmp(nfs_mnt_prop, "off") == 0) 7283*eda14cbcSMatt Macy continue; 7284*eda14cbcSMatt Macy break; 7285*eda14cbcSMatt Macy case OP_MOUNT: 7286*eda14cbcSMatt Macy /* Ignore legacy mounts */ 7287*eda14cbcSMatt Macy verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, 7288*eda14cbcSMatt Macy nfs_mnt_prop, 7289*eda14cbcSMatt Macy sizeof (nfs_mnt_prop), 7290*eda14cbcSMatt Macy NULL, NULL, 0, B_FALSE) == 0); 7291*eda14cbcSMatt Macy if (strcmp(nfs_mnt_prop, "legacy") == 0) 7292*eda14cbcSMatt Macy continue; 7293*eda14cbcSMatt Macy /* Ignore canmount=noauto mounts */ 7294*eda14cbcSMatt Macy if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == 7295*eda14cbcSMatt Macy ZFS_CANMOUNT_NOAUTO) 7296*eda14cbcSMatt Macy continue; 7297*eda14cbcSMatt Macy default: 7298*eda14cbcSMatt Macy break; 7299*eda14cbcSMatt Macy } 7300*eda14cbcSMatt Macy 7301*eda14cbcSMatt Macy node = safe_malloc(sizeof (unshare_unmount_node_t)); 7302*eda14cbcSMatt Macy node->un_zhp = zhp; 7303*eda14cbcSMatt Macy node->un_mountp = safe_strdup(entry.mnt_mountp); 7304*eda14cbcSMatt Macy 7305*eda14cbcSMatt Macy uu_avl_node_init(node, &node->un_avlnode, pool); 7306*eda14cbcSMatt Macy 7307*eda14cbcSMatt Macy if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 7308*eda14cbcSMatt Macy uu_avl_insert(tree, node, idx); 7309*eda14cbcSMatt Macy } else { 7310*eda14cbcSMatt Macy zfs_close(node->un_zhp); 7311*eda14cbcSMatt Macy free(node->un_mountp); 7312*eda14cbcSMatt Macy free(node); 7313*eda14cbcSMatt Macy } 7314*eda14cbcSMatt Macy } 7315*eda14cbcSMatt Macy 7316*eda14cbcSMatt Macy /* 7317*eda14cbcSMatt Macy * Walk the AVL tree in reverse, unmounting each filesystem and 7318*eda14cbcSMatt Macy * removing it from the AVL tree in the process. 7319*eda14cbcSMatt Macy */ 7320*eda14cbcSMatt Macy if ((walk = uu_avl_walk_start(tree, 7321*eda14cbcSMatt Macy UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) 7322*eda14cbcSMatt Macy nomem(); 7323*eda14cbcSMatt Macy 7324*eda14cbcSMatt Macy while ((node = uu_avl_walk_next(walk)) != NULL) { 7325*eda14cbcSMatt Macy const char *mntarg = NULL; 7326*eda14cbcSMatt Macy 7327*eda14cbcSMatt Macy uu_avl_remove(tree, node); 7328*eda14cbcSMatt Macy switch (op) { 7329*eda14cbcSMatt Macy case OP_SHARE: 7330*eda14cbcSMatt Macy if (zfs_unshareall_bytype(node->un_zhp, 7331*eda14cbcSMatt Macy node->un_mountp, protocol) != 0) 7332*eda14cbcSMatt Macy ret = 1; 7333*eda14cbcSMatt Macy break; 7334*eda14cbcSMatt Macy 7335*eda14cbcSMatt Macy case OP_MOUNT: 7336*eda14cbcSMatt Macy if (zfs_unmount(node->un_zhp, 7337*eda14cbcSMatt Macy mntarg, flags) != 0) 7338*eda14cbcSMatt Macy ret = 1; 7339*eda14cbcSMatt Macy break; 7340*eda14cbcSMatt Macy } 7341*eda14cbcSMatt Macy 7342*eda14cbcSMatt Macy zfs_close(node->un_zhp); 7343*eda14cbcSMatt Macy free(node->un_mountp); 7344*eda14cbcSMatt Macy free(node); 7345*eda14cbcSMatt Macy } 7346*eda14cbcSMatt Macy 7347*eda14cbcSMatt Macy if (op == OP_SHARE) 7348*eda14cbcSMatt Macy zfs_commit_shares(protocol); 7349*eda14cbcSMatt Macy 7350*eda14cbcSMatt Macy uu_avl_walk_end(walk); 7351*eda14cbcSMatt Macy uu_avl_destroy(tree); 7352*eda14cbcSMatt Macy uu_avl_pool_destroy(pool); 7353*eda14cbcSMatt Macy 7354*eda14cbcSMatt Macy } else { 7355*eda14cbcSMatt Macy if (argc != 1) { 7356*eda14cbcSMatt Macy if (argc == 0) 7357*eda14cbcSMatt Macy (void) fprintf(stderr, 7358*eda14cbcSMatt Macy gettext("missing filesystem argument\n")); 7359*eda14cbcSMatt Macy else 7360*eda14cbcSMatt Macy (void) fprintf(stderr, 7361*eda14cbcSMatt Macy gettext("too many arguments\n")); 7362*eda14cbcSMatt Macy usage(B_FALSE); 7363*eda14cbcSMatt Macy } 7364*eda14cbcSMatt Macy 7365*eda14cbcSMatt Macy /* 7366*eda14cbcSMatt Macy * We have an argument, but it may be a full path or a ZFS 7367*eda14cbcSMatt Macy * filesystem. Pass full paths off to unmount_path() (shared by 7368*eda14cbcSMatt Macy * manual_unmount), otherwise open the filesystem and pass to 7369*eda14cbcSMatt Macy * zfs_unmount(). 7370*eda14cbcSMatt Macy */ 7371*eda14cbcSMatt Macy if (argv[0][0] == '/') 7372*eda14cbcSMatt Macy return (unshare_unmount_path(op, argv[0], 7373*eda14cbcSMatt Macy flags, B_FALSE)); 7374*eda14cbcSMatt Macy 7375*eda14cbcSMatt Macy if ((zhp = zfs_open(g_zfs, argv[0], 7376*eda14cbcSMatt Macy ZFS_TYPE_FILESYSTEM)) == NULL) 7377*eda14cbcSMatt Macy return (1); 7378*eda14cbcSMatt Macy 7379*eda14cbcSMatt Macy verify(zfs_prop_get(zhp, op == OP_SHARE ? 7380*eda14cbcSMatt Macy ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 7381*eda14cbcSMatt Macy nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL, 7382*eda14cbcSMatt Macy NULL, 0, B_FALSE) == 0); 7383*eda14cbcSMatt Macy 7384*eda14cbcSMatt Macy switch (op) { 7385*eda14cbcSMatt Macy case OP_SHARE: 7386*eda14cbcSMatt Macy verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, 7387*eda14cbcSMatt Macy nfs_mnt_prop, 7388*eda14cbcSMatt Macy sizeof (nfs_mnt_prop), 7389*eda14cbcSMatt Macy NULL, NULL, 0, B_FALSE) == 0); 7390*eda14cbcSMatt Macy verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, 7391*eda14cbcSMatt Macy sharesmb, sizeof (sharesmb), NULL, NULL, 7392*eda14cbcSMatt Macy 0, B_FALSE) == 0); 7393*eda14cbcSMatt Macy 7394*eda14cbcSMatt Macy if (strcmp(nfs_mnt_prop, "off") == 0 && 7395*eda14cbcSMatt Macy strcmp(sharesmb, "off") == 0) { 7396*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot " 7397*eda14cbcSMatt Macy "unshare '%s': legacy share\n"), 7398*eda14cbcSMatt Macy zfs_get_name(zhp)); 7399*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("use " 7400*eda14cbcSMatt Macy "unshare(1M) to unshare this " 7401*eda14cbcSMatt Macy "filesystem\n")); 7402*eda14cbcSMatt Macy ret = 1; 7403*eda14cbcSMatt Macy } else if (!zfs_is_shared(zhp)) { 7404*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot " 7405*eda14cbcSMatt Macy "unshare '%s': not currently " 7406*eda14cbcSMatt Macy "shared\n"), zfs_get_name(zhp)); 7407*eda14cbcSMatt Macy ret = 1; 7408*eda14cbcSMatt Macy } else if (zfs_unshareall(zhp) != 0) { 7409*eda14cbcSMatt Macy ret = 1; 7410*eda14cbcSMatt Macy } 7411*eda14cbcSMatt Macy break; 7412*eda14cbcSMatt Macy 7413*eda14cbcSMatt Macy case OP_MOUNT: 7414*eda14cbcSMatt Macy if (strcmp(nfs_mnt_prop, "legacy") == 0) { 7415*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot " 7416*eda14cbcSMatt Macy "unmount '%s': legacy " 7417*eda14cbcSMatt Macy "mountpoint\n"), zfs_get_name(zhp)); 7418*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("use " 7419*eda14cbcSMatt Macy "umount(1M) to unmount this " 7420*eda14cbcSMatt Macy "filesystem\n")); 7421*eda14cbcSMatt Macy ret = 1; 7422*eda14cbcSMatt Macy } else if (!zfs_is_mounted(zhp, NULL)) { 7423*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot " 7424*eda14cbcSMatt Macy "unmount '%s': not currently " 7425*eda14cbcSMatt Macy "mounted\n"), 7426*eda14cbcSMatt Macy zfs_get_name(zhp)); 7427*eda14cbcSMatt Macy ret = 1; 7428*eda14cbcSMatt Macy } else if (zfs_unmountall(zhp, flags) != 0) { 7429*eda14cbcSMatt Macy ret = 1; 7430*eda14cbcSMatt Macy } 7431*eda14cbcSMatt Macy break; 7432*eda14cbcSMatt Macy } 7433*eda14cbcSMatt Macy 7434*eda14cbcSMatt Macy zfs_close(zhp); 7435*eda14cbcSMatt Macy } 7436*eda14cbcSMatt Macy 7437*eda14cbcSMatt Macy return (ret); 7438*eda14cbcSMatt Macy } 7439*eda14cbcSMatt Macy 7440*eda14cbcSMatt Macy /* 7441*eda14cbcSMatt Macy * zfs unmount [-fu] -a 7442*eda14cbcSMatt Macy * zfs unmount [-fu] filesystem 7443*eda14cbcSMatt Macy * 7444*eda14cbcSMatt Macy * Unmount all filesystems, or a specific ZFS filesystem. 7445*eda14cbcSMatt Macy */ 7446*eda14cbcSMatt Macy static int 7447*eda14cbcSMatt Macy zfs_do_unmount(int argc, char **argv) 7448*eda14cbcSMatt Macy { 7449*eda14cbcSMatt Macy return (unshare_unmount(OP_MOUNT, argc, argv)); 7450*eda14cbcSMatt Macy } 7451*eda14cbcSMatt Macy 7452*eda14cbcSMatt Macy /* 7453*eda14cbcSMatt Macy * zfs unshare -a 7454*eda14cbcSMatt Macy * zfs unshare filesystem 7455*eda14cbcSMatt Macy * 7456*eda14cbcSMatt Macy * Unshare all filesystems, or a specific ZFS filesystem. 7457*eda14cbcSMatt Macy */ 7458*eda14cbcSMatt Macy static int 7459*eda14cbcSMatt Macy zfs_do_unshare(int argc, char **argv) 7460*eda14cbcSMatt Macy { 7461*eda14cbcSMatt Macy return (unshare_unmount(OP_SHARE, argc, argv)); 7462*eda14cbcSMatt Macy } 7463*eda14cbcSMatt Macy 7464*eda14cbcSMatt Macy static int 7465*eda14cbcSMatt Macy find_command_idx(char *command, int *idx) 7466*eda14cbcSMatt Macy { 7467*eda14cbcSMatt Macy int i; 7468*eda14cbcSMatt Macy 7469*eda14cbcSMatt Macy for (i = 0; i < NCOMMAND; i++) { 7470*eda14cbcSMatt Macy if (command_table[i].name == NULL) 7471*eda14cbcSMatt Macy continue; 7472*eda14cbcSMatt Macy 7473*eda14cbcSMatt Macy if (strcmp(command, command_table[i].name) == 0) { 7474*eda14cbcSMatt Macy *idx = i; 7475*eda14cbcSMatt Macy return (0); 7476*eda14cbcSMatt Macy } 7477*eda14cbcSMatt Macy } 7478*eda14cbcSMatt Macy return (1); 7479*eda14cbcSMatt Macy } 7480*eda14cbcSMatt Macy 7481*eda14cbcSMatt Macy static int 7482*eda14cbcSMatt Macy zfs_do_diff(int argc, char **argv) 7483*eda14cbcSMatt Macy { 7484*eda14cbcSMatt Macy zfs_handle_t *zhp; 7485*eda14cbcSMatt Macy int flags = 0; 7486*eda14cbcSMatt Macy char *tosnap = NULL; 7487*eda14cbcSMatt Macy char *fromsnap = NULL; 7488*eda14cbcSMatt Macy char *atp, *copy; 7489*eda14cbcSMatt Macy int err = 0; 7490*eda14cbcSMatt Macy int c; 7491*eda14cbcSMatt Macy struct sigaction sa; 7492*eda14cbcSMatt Macy 7493*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "FHt")) != -1) { 7494*eda14cbcSMatt Macy switch (c) { 7495*eda14cbcSMatt Macy case 'F': 7496*eda14cbcSMatt Macy flags |= ZFS_DIFF_CLASSIFY; 7497*eda14cbcSMatt Macy break; 7498*eda14cbcSMatt Macy case 'H': 7499*eda14cbcSMatt Macy flags |= ZFS_DIFF_PARSEABLE; 7500*eda14cbcSMatt Macy break; 7501*eda14cbcSMatt Macy case 't': 7502*eda14cbcSMatt Macy flags |= ZFS_DIFF_TIMESTAMP; 7503*eda14cbcSMatt Macy break; 7504*eda14cbcSMatt Macy default: 7505*eda14cbcSMatt Macy (void) fprintf(stderr, 7506*eda14cbcSMatt Macy gettext("invalid option '%c'\n"), optopt); 7507*eda14cbcSMatt Macy usage(B_FALSE); 7508*eda14cbcSMatt Macy } 7509*eda14cbcSMatt Macy } 7510*eda14cbcSMatt Macy 7511*eda14cbcSMatt Macy argc -= optind; 7512*eda14cbcSMatt Macy argv += optind; 7513*eda14cbcSMatt Macy 7514*eda14cbcSMatt Macy if (argc < 1) { 7515*eda14cbcSMatt Macy (void) fprintf(stderr, 7516*eda14cbcSMatt Macy gettext("must provide at least one snapshot name\n")); 7517*eda14cbcSMatt Macy usage(B_FALSE); 7518*eda14cbcSMatt Macy } 7519*eda14cbcSMatt Macy 7520*eda14cbcSMatt Macy if (argc > 2) { 7521*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many arguments\n")); 7522*eda14cbcSMatt Macy usage(B_FALSE); 7523*eda14cbcSMatt Macy } 7524*eda14cbcSMatt Macy 7525*eda14cbcSMatt Macy fromsnap = argv[0]; 7526*eda14cbcSMatt Macy tosnap = (argc == 2) ? argv[1] : NULL; 7527*eda14cbcSMatt Macy 7528*eda14cbcSMatt Macy copy = NULL; 7529*eda14cbcSMatt Macy if (*fromsnap != '@') 7530*eda14cbcSMatt Macy copy = strdup(fromsnap); 7531*eda14cbcSMatt Macy else if (tosnap) 7532*eda14cbcSMatt Macy copy = strdup(tosnap); 7533*eda14cbcSMatt Macy if (copy == NULL) 7534*eda14cbcSMatt Macy usage(B_FALSE); 7535*eda14cbcSMatt Macy 7536*eda14cbcSMatt Macy if ((atp = strchr(copy, '@')) != NULL) 7537*eda14cbcSMatt Macy *atp = '\0'; 7538*eda14cbcSMatt Macy 7539*eda14cbcSMatt Macy if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL) { 7540*eda14cbcSMatt Macy free(copy); 7541*eda14cbcSMatt Macy return (1); 7542*eda14cbcSMatt Macy } 7543*eda14cbcSMatt Macy free(copy); 7544*eda14cbcSMatt Macy 7545*eda14cbcSMatt Macy /* 7546*eda14cbcSMatt Macy * Ignore SIGPIPE so that the library can give us 7547*eda14cbcSMatt Macy * information on any failure 7548*eda14cbcSMatt Macy */ 7549*eda14cbcSMatt Macy if (sigemptyset(&sa.sa_mask) == -1) { 7550*eda14cbcSMatt Macy err = errno; 7551*eda14cbcSMatt Macy goto out; 7552*eda14cbcSMatt Macy } 7553*eda14cbcSMatt Macy sa.sa_flags = 0; 7554*eda14cbcSMatt Macy sa.sa_handler = SIG_IGN; 7555*eda14cbcSMatt Macy if (sigaction(SIGPIPE, &sa, NULL) == -1) { 7556*eda14cbcSMatt Macy err = errno; 7557*eda14cbcSMatt Macy goto out; 7558*eda14cbcSMatt Macy } 7559*eda14cbcSMatt Macy 7560*eda14cbcSMatt Macy err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags); 7561*eda14cbcSMatt Macy out: 7562*eda14cbcSMatt Macy zfs_close(zhp); 7563*eda14cbcSMatt Macy 7564*eda14cbcSMatt Macy return (err != 0); 7565*eda14cbcSMatt Macy } 7566*eda14cbcSMatt Macy 7567*eda14cbcSMatt Macy /* 7568*eda14cbcSMatt Macy * zfs bookmark <fs@source>|<fs#source> <fs#bookmark> 7569*eda14cbcSMatt Macy * 7570*eda14cbcSMatt Macy * Creates a bookmark with the given name from the source snapshot 7571*eda14cbcSMatt Macy * or creates a copy of an existing source bookmark. 7572*eda14cbcSMatt Macy */ 7573*eda14cbcSMatt Macy static int 7574*eda14cbcSMatt Macy zfs_do_bookmark(int argc, char **argv) 7575*eda14cbcSMatt Macy { 7576*eda14cbcSMatt Macy char *source, *bookname; 7577*eda14cbcSMatt Macy char expbuf[ZFS_MAX_DATASET_NAME_LEN]; 7578*eda14cbcSMatt Macy int source_type; 7579*eda14cbcSMatt Macy nvlist_t *nvl; 7580*eda14cbcSMatt Macy int ret = 0; 7581*eda14cbcSMatt Macy int c; 7582*eda14cbcSMatt Macy 7583*eda14cbcSMatt Macy /* check options */ 7584*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "")) != -1) { 7585*eda14cbcSMatt Macy switch (c) { 7586*eda14cbcSMatt Macy case '?': 7587*eda14cbcSMatt Macy (void) fprintf(stderr, 7588*eda14cbcSMatt Macy gettext("invalid option '%c'\n"), optopt); 7589*eda14cbcSMatt Macy goto usage; 7590*eda14cbcSMatt Macy } 7591*eda14cbcSMatt Macy } 7592*eda14cbcSMatt Macy 7593*eda14cbcSMatt Macy argc -= optind; 7594*eda14cbcSMatt Macy argv += optind; 7595*eda14cbcSMatt Macy 7596*eda14cbcSMatt Macy /* check number of arguments */ 7597*eda14cbcSMatt Macy if (argc < 1) { 7598*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing source argument\n")); 7599*eda14cbcSMatt Macy goto usage; 7600*eda14cbcSMatt Macy } 7601*eda14cbcSMatt Macy if (argc < 2) { 7602*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing bookmark argument\n")); 7603*eda14cbcSMatt Macy goto usage; 7604*eda14cbcSMatt Macy } 7605*eda14cbcSMatt Macy 7606*eda14cbcSMatt Macy source = argv[0]; 7607*eda14cbcSMatt Macy bookname = argv[1]; 7608*eda14cbcSMatt Macy 7609*eda14cbcSMatt Macy if (strchr(source, '@') == NULL && strchr(source, '#') == NULL) { 7610*eda14cbcSMatt Macy (void) fprintf(stderr, 7611*eda14cbcSMatt Macy gettext("invalid source name '%s': " 7612*eda14cbcSMatt Macy "must contain a '@' or '#'\n"), source); 7613*eda14cbcSMatt Macy goto usage; 7614*eda14cbcSMatt Macy } 7615*eda14cbcSMatt Macy if (strchr(bookname, '#') == NULL) { 7616*eda14cbcSMatt Macy (void) fprintf(stderr, 7617*eda14cbcSMatt Macy gettext("invalid bookmark name '%s': " 7618*eda14cbcSMatt Macy "must contain a '#'\n"), bookname); 7619*eda14cbcSMatt Macy goto usage; 7620*eda14cbcSMatt Macy } 7621*eda14cbcSMatt Macy 7622*eda14cbcSMatt Macy /* 7623*eda14cbcSMatt Macy * expand source or bookname to full path: 7624*eda14cbcSMatt Macy * one of them may be specified as short name 7625*eda14cbcSMatt Macy */ 7626*eda14cbcSMatt Macy { 7627*eda14cbcSMatt Macy char **expand; 7628*eda14cbcSMatt Macy char *source_short, *bookname_short; 7629*eda14cbcSMatt Macy source_short = strpbrk(source, "@#"); 7630*eda14cbcSMatt Macy bookname_short = strpbrk(bookname, "#"); 7631*eda14cbcSMatt Macy if (source_short == source && 7632*eda14cbcSMatt Macy bookname_short == bookname) { 7633*eda14cbcSMatt Macy (void) fprintf(stderr, gettext( 7634*eda14cbcSMatt Macy "either source or bookmark must be specified as " 7635*eda14cbcSMatt Macy "full dataset paths")); 7636*eda14cbcSMatt Macy goto usage; 7637*eda14cbcSMatt Macy } else if (source_short != source && 7638*eda14cbcSMatt Macy bookname_short != bookname) { 7639*eda14cbcSMatt Macy expand = NULL; 7640*eda14cbcSMatt Macy } else if (source_short != source) { 7641*eda14cbcSMatt Macy strlcpy(expbuf, source, sizeof (expbuf)); 7642*eda14cbcSMatt Macy expand = &bookname; 7643*eda14cbcSMatt Macy } else if (bookname_short != bookname) { 7644*eda14cbcSMatt Macy strlcpy(expbuf, bookname, sizeof (expbuf)); 7645*eda14cbcSMatt Macy expand = &source; 7646*eda14cbcSMatt Macy } else { 7647*eda14cbcSMatt Macy abort(); 7648*eda14cbcSMatt Macy } 7649*eda14cbcSMatt Macy if (expand != NULL) { 7650*eda14cbcSMatt Macy *strpbrk(expbuf, "@#") = '\0'; /* dataset name in buf */ 7651*eda14cbcSMatt Macy (void) strlcat(expbuf, *expand, sizeof (expbuf)); 7652*eda14cbcSMatt Macy *expand = expbuf; 7653*eda14cbcSMatt Macy } 7654*eda14cbcSMatt Macy } 7655*eda14cbcSMatt Macy 7656*eda14cbcSMatt Macy /* determine source type */ 7657*eda14cbcSMatt Macy switch (*strpbrk(source, "@#")) { 7658*eda14cbcSMatt Macy case '@': source_type = ZFS_TYPE_SNAPSHOT; break; 7659*eda14cbcSMatt Macy case '#': source_type = ZFS_TYPE_BOOKMARK; break; 7660*eda14cbcSMatt Macy default: abort(); 7661*eda14cbcSMatt Macy } 7662*eda14cbcSMatt Macy 7663*eda14cbcSMatt Macy /* test the source exists */ 7664*eda14cbcSMatt Macy zfs_handle_t *zhp; 7665*eda14cbcSMatt Macy zhp = zfs_open(g_zfs, source, source_type); 7666*eda14cbcSMatt Macy if (zhp == NULL) 7667*eda14cbcSMatt Macy goto usage; 7668*eda14cbcSMatt Macy zfs_close(zhp); 7669*eda14cbcSMatt Macy 7670*eda14cbcSMatt Macy nvl = fnvlist_alloc(); 7671*eda14cbcSMatt Macy fnvlist_add_string(nvl, bookname, source); 7672*eda14cbcSMatt Macy ret = lzc_bookmark(nvl, NULL); 7673*eda14cbcSMatt Macy fnvlist_free(nvl); 7674*eda14cbcSMatt Macy 7675*eda14cbcSMatt Macy if (ret != 0) { 7676*eda14cbcSMatt Macy const char *err_msg = NULL; 7677*eda14cbcSMatt Macy char errbuf[1024]; 7678*eda14cbcSMatt Macy 7679*eda14cbcSMatt Macy (void) snprintf(errbuf, sizeof (errbuf), 7680*eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, 7681*eda14cbcSMatt Macy "cannot create bookmark '%s'"), bookname); 7682*eda14cbcSMatt Macy 7683*eda14cbcSMatt Macy switch (ret) { 7684*eda14cbcSMatt Macy case EXDEV: 7685*eda14cbcSMatt Macy err_msg = "bookmark is in a different pool"; 7686*eda14cbcSMatt Macy break; 7687*eda14cbcSMatt Macy case ZFS_ERR_BOOKMARK_SOURCE_NOT_ANCESTOR: 7688*eda14cbcSMatt Macy err_msg = "source is not an ancestor of the " 7689*eda14cbcSMatt Macy "new bookmark's dataset"; 7690*eda14cbcSMatt Macy break; 7691*eda14cbcSMatt Macy case EEXIST: 7692*eda14cbcSMatt Macy err_msg = "bookmark exists"; 7693*eda14cbcSMatt Macy break; 7694*eda14cbcSMatt Macy case EINVAL: 7695*eda14cbcSMatt Macy err_msg = "invalid argument"; 7696*eda14cbcSMatt Macy break; 7697*eda14cbcSMatt Macy case ENOTSUP: 7698*eda14cbcSMatt Macy err_msg = "bookmark feature not enabled"; 7699*eda14cbcSMatt Macy break; 7700*eda14cbcSMatt Macy case ENOSPC: 7701*eda14cbcSMatt Macy err_msg = "out of space"; 7702*eda14cbcSMatt Macy break; 7703*eda14cbcSMatt Macy case ENOENT: 7704*eda14cbcSMatt Macy err_msg = "dataset does not exist"; 7705*eda14cbcSMatt Macy break; 7706*eda14cbcSMatt Macy default: 7707*eda14cbcSMatt Macy (void) zfs_standard_error(g_zfs, ret, errbuf); 7708*eda14cbcSMatt Macy break; 7709*eda14cbcSMatt Macy } 7710*eda14cbcSMatt Macy if (err_msg != NULL) { 7711*eda14cbcSMatt Macy (void) fprintf(stderr, "%s: %s\n", errbuf, 7712*eda14cbcSMatt Macy dgettext(TEXT_DOMAIN, err_msg)); 7713*eda14cbcSMatt Macy } 7714*eda14cbcSMatt Macy } 7715*eda14cbcSMatt Macy 7716*eda14cbcSMatt Macy return (ret != 0); 7717*eda14cbcSMatt Macy 7718*eda14cbcSMatt Macy usage: 7719*eda14cbcSMatt Macy usage(B_FALSE); 7720*eda14cbcSMatt Macy return (-1); 7721*eda14cbcSMatt Macy } 7722*eda14cbcSMatt Macy 7723*eda14cbcSMatt Macy static int 7724*eda14cbcSMatt Macy zfs_do_channel_program(int argc, char **argv) 7725*eda14cbcSMatt Macy { 7726*eda14cbcSMatt Macy int ret, fd, c; 7727*eda14cbcSMatt Macy char *progbuf, *filename, *poolname; 7728*eda14cbcSMatt Macy size_t progsize, progread; 7729*eda14cbcSMatt Macy nvlist_t *outnvl = NULL; 7730*eda14cbcSMatt Macy uint64_t instrlimit = ZCP_DEFAULT_INSTRLIMIT; 7731*eda14cbcSMatt Macy uint64_t memlimit = ZCP_DEFAULT_MEMLIMIT; 7732*eda14cbcSMatt Macy boolean_t sync_flag = B_TRUE, json_output = B_FALSE; 7733*eda14cbcSMatt Macy zpool_handle_t *zhp; 7734*eda14cbcSMatt Macy 7735*eda14cbcSMatt Macy /* check options */ 7736*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "nt:m:j")) != -1) { 7737*eda14cbcSMatt Macy switch (c) { 7738*eda14cbcSMatt Macy case 't': 7739*eda14cbcSMatt Macy case 'm': { 7740*eda14cbcSMatt Macy uint64_t arg; 7741*eda14cbcSMatt Macy char *endp; 7742*eda14cbcSMatt Macy 7743*eda14cbcSMatt Macy errno = 0; 7744*eda14cbcSMatt Macy arg = strtoull(optarg, &endp, 0); 7745*eda14cbcSMatt Macy if (errno != 0 || *endp != '\0') { 7746*eda14cbcSMatt Macy (void) fprintf(stderr, gettext( 7747*eda14cbcSMatt Macy "invalid argument " 7748*eda14cbcSMatt Macy "'%s': expected integer\n"), optarg); 7749*eda14cbcSMatt Macy goto usage; 7750*eda14cbcSMatt Macy } 7751*eda14cbcSMatt Macy 7752*eda14cbcSMatt Macy if (c == 't') { 7753*eda14cbcSMatt Macy instrlimit = arg; 7754*eda14cbcSMatt Macy } else { 7755*eda14cbcSMatt Macy ASSERT3U(c, ==, 'm'); 7756*eda14cbcSMatt Macy memlimit = arg; 7757*eda14cbcSMatt Macy } 7758*eda14cbcSMatt Macy break; 7759*eda14cbcSMatt Macy } 7760*eda14cbcSMatt Macy case 'n': { 7761*eda14cbcSMatt Macy sync_flag = B_FALSE; 7762*eda14cbcSMatt Macy break; 7763*eda14cbcSMatt Macy } 7764*eda14cbcSMatt Macy case 'j': { 7765*eda14cbcSMatt Macy json_output = B_TRUE; 7766*eda14cbcSMatt Macy break; 7767*eda14cbcSMatt Macy } 7768*eda14cbcSMatt Macy case '?': 7769*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 7770*eda14cbcSMatt Macy optopt); 7771*eda14cbcSMatt Macy goto usage; 7772*eda14cbcSMatt Macy } 7773*eda14cbcSMatt Macy } 7774*eda14cbcSMatt Macy 7775*eda14cbcSMatt Macy argc -= optind; 7776*eda14cbcSMatt Macy argv += optind; 7777*eda14cbcSMatt Macy 7778*eda14cbcSMatt Macy if (argc < 2) { 7779*eda14cbcSMatt Macy (void) fprintf(stderr, 7780*eda14cbcSMatt Macy gettext("invalid number of arguments\n")); 7781*eda14cbcSMatt Macy goto usage; 7782*eda14cbcSMatt Macy } 7783*eda14cbcSMatt Macy 7784*eda14cbcSMatt Macy poolname = argv[0]; 7785*eda14cbcSMatt Macy filename = argv[1]; 7786*eda14cbcSMatt Macy if (strcmp(filename, "-") == 0) { 7787*eda14cbcSMatt Macy fd = 0; 7788*eda14cbcSMatt Macy filename = "standard input"; 7789*eda14cbcSMatt Macy } else if ((fd = open(filename, O_RDONLY)) < 0) { 7790*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot open '%s': %s\n"), 7791*eda14cbcSMatt Macy filename, strerror(errno)); 7792*eda14cbcSMatt Macy return (1); 7793*eda14cbcSMatt Macy } 7794*eda14cbcSMatt Macy 7795*eda14cbcSMatt Macy if ((zhp = zpool_open(g_zfs, poolname)) == NULL) { 7796*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot open pool '%s'\n"), 7797*eda14cbcSMatt Macy poolname); 7798*eda14cbcSMatt Macy if (fd != 0) 7799*eda14cbcSMatt Macy (void) close(fd); 7800*eda14cbcSMatt Macy return (1); 7801*eda14cbcSMatt Macy } 7802*eda14cbcSMatt Macy zpool_close(zhp); 7803*eda14cbcSMatt Macy 7804*eda14cbcSMatt Macy /* 7805*eda14cbcSMatt Macy * Read in the channel program, expanding the program buffer as 7806*eda14cbcSMatt Macy * necessary. 7807*eda14cbcSMatt Macy */ 7808*eda14cbcSMatt Macy progread = 0; 7809*eda14cbcSMatt Macy progsize = 1024; 7810*eda14cbcSMatt Macy progbuf = safe_malloc(progsize); 7811*eda14cbcSMatt Macy do { 7812*eda14cbcSMatt Macy ret = read(fd, progbuf + progread, progsize - progread); 7813*eda14cbcSMatt Macy progread += ret; 7814*eda14cbcSMatt Macy if (progread == progsize && ret > 0) { 7815*eda14cbcSMatt Macy progsize *= 2; 7816*eda14cbcSMatt Macy progbuf = safe_realloc(progbuf, progsize); 7817*eda14cbcSMatt Macy } 7818*eda14cbcSMatt Macy } while (ret > 0); 7819*eda14cbcSMatt Macy 7820*eda14cbcSMatt Macy if (fd != 0) 7821*eda14cbcSMatt Macy (void) close(fd); 7822*eda14cbcSMatt Macy if (ret < 0) { 7823*eda14cbcSMatt Macy free(progbuf); 7824*eda14cbcSMatt Macy (void) fprintf(stderr, 7825*eda14cbcSMatt Macy gettext("cannot read '%s': %s\n"), 7826*eda14cbcSMatt Macy filename, strerror(errno)); 7827*eda14cbcSMatt Macy return (1); 7828*eda14cbcSMatt Macy } 7829*eda14cbcSMatt Macy progbuf[progread] = '\0'; 7830*eda14cbcSMatt Macy 7831*eda14cbcSMatt Macy /* 7832*eda14cbcSMatt Macy * Any remaining arguments are passed as arguments to the lua script as 7833*eda14cbcSMatt Macy * a string array: 7834*eda14cbcSMatt Macy * { 7835*eda14cbcSMatt Macy * "argv" -> [ "arg 1", ... "arg n" ], 7836*eda14cbcSMatt Macy * } 7837*eda14cbcSMatt Macy */ 7838*eda14cbcSMatt Macy nvlist_t *argnvl = fnvlist_alloc(); 7839*eda14cbcSMatt Macy fnvlist_add_string_array(argnvl, ZCP_ARG_CLIARGV, argv + 2, argc - 2); 7840*eda14cbcSMatt Macy 7841*eda14cbcSMatt Macy if (sync_flag) { 7842*eda14cbcSMatt Macy ret = lzc_channel_program(poolname, progbuf, 7843*eda14cbcSMatt Macy instrlimit, memlimit, argnvl, &outnvl); 7844*eda14cbcSMatt Macy } else { 7845*eda14cbcSMatt Macy ret = lzc_channel_program_nosync(poolname, progbuf, 7846*eda14cbcSMatt Macy instrlimit, memlimit, argnvl, &outnvl); 7847*eda14cbcSMatt Macy } 7848*eda14cbcSMatt Macy 7849*eda14cbcSMatt Macy if (ret != 0) { 7850*eda14cbcSMatt Macy /* 7851*eda14cbcSMatt Macy * On error, report the error message handed back by lua if one 7852*eda14cbcSMatt Macy * exists. Otherwise, generate an appropriate error message, 7853*eda14cbcSMatt Macy * falling back on strerror() for an unexpected return code. 7854*eda14cbcSMatt Macy */ 7855*eda14cbcSMatt Macy char *errstring = NULL; 7856*eda14cbcSMatt Macy const char *msg = gettext("Channel program execution failed"); 7857*eda14cbcSMatt Macy uint64_t instructions = 0; 7858*eda14cbcSMatt Macy if (outnvl != NULL && nvlist_exists(outnvl, ZCP_RET_ERROR)) { 7859*eda14cbcSMatt Macy (void) nvlist_lookup_string(outnvl, 7860*eda14cbcSMatt Macy ZCP_RET_ERROR, &errstring); 7861*eda14cbcSMatt Macy if (errstring == NULL) 7862*eda14cbcSMatt Macy errstring = strerror(ret); 7863*eda14cbcSMatt Macy if (ret == ETIME) { 7864*eda14cbcSMatt Macy (void) nvlist_lookup_uint64(outnvl, 7865*eda14cbcSMatt Macy ZCP_ARG_INSTRLIMIT, &instructions); 7866*eda14cbcSMatt Macy } 7867*eda14cbcSMatt Macy } else { 7868*eda14cbcSMatt Macy switch (ret) { 7869*eda14cbcSMatt Macy case EINVAL: 7870*eda14cbcSMatt Macy errstring = 7871*eda14cbcSMatt Macy "Invalid instruction or memory limit."; 7872*eda14cbcSMatt Macy break; 7873*eda14cbcSMatt Macy case ENOMEM: 7874*eda14cbcSMatt Macy errstring = "Return value too large."; 7875*eda14cbcSMatt Macy break; 7876*eda14cbcSMatt Macy case ENOSPC: 7877*eda14cbcSMatt Macy errstring = "Memory limit exhausted."; 7878*eda14cbcSMatt Macy break; 7879*eda14cbcSMatt Macy case ETIME: 7880*eda14cbcSMatt Macy errstring = "Timed out."; 7881*eda14cbcSMatt Macy break; 7882*eda14cbcSMatt Macy case EPERM: 7883*eda14cbcSMatt Macy errstring = "Permission denied. Channel " 7884*eda14cbcSMatt Macy "programs must be run as root."; 7885*eda14cbcSMatt Macy break; 7886*eda14cbcSMatt Macy default: 7887*eda14cbcSMatt Macy (void) zfs_standard_error(g_zfs, ret, msg); 7888*eda14cbcSMatt Macy } 7889*eda14cbcSMatt Macy } 7890*eda14cbcSMatt Macy if (errstring != NULL) 7891*eda14cbcSMatt Macy (void) fprintf(stderr, "%s:\n%s\n", msg, errstring); 7892*eda14cbcSMatt Macy 7893*eda14cbcSMatt Macy if (ret == ETIME && instructions != 0) 7894*eda14cbcSMatt Macy (void) fprintf(stderr, 7895*eda14cbcSMatt Macy gettext("%llu Lua instructions\n"), 7896*eda14cbcSMatt Macy (u_longlong_t)instructions); 7897*eda14cbcSMatt Macy } else { 7898*eda14cbcSMatt Macy if (json_output) { 7899*eda14cbcSMatt Macy (void) nvlist_print_json(stdout, outnvl); 7900*eda14cbcSMatt Macy } else if (nvlist_empty(outnvl)) { 7901*eda14cbcSMatt Macy (void) fprintf(stdout, gettext("Channel program fully " 7902*eda14cbcSMatt Macy "executed and did not produce output.\n")); 7903*eda14cbcSMatt Macy } else { 7904*eda14cbcSMatt Macy (void) fprintf(stdout, gettext("Channel program fully " 7905*eda14cbcSMatt Macy "executed and produced output:\n")); 7906*eda14cbcSMatt Macy dump_nvlist(outnvl, 4); 7907*eda14cbcSMatt Macy } 7908*eda14cbcSMatt Macy } 7909*eda14cbcSMatt Macy 7910*eda14cbcSMatt Macy free(progbuf); 7911*eda14cbcSMatt Macy fnvlist_free(outnvl); 7912*eda14cbcSMatt Macy fnvlist_free(argnvl); 7913*eda14cbcSMatt Macy return (ret != 0); 7914*eda14cbcSMatt Macy 7915*eda14cbcSMatt Macy usage: 7916*eda14cbcSMatt Macy usage(B_FALSE); 7917*eda14cbcSMatt Macy return (-1); 7918*eda14cbcSMatt Macy } 7919*eda14cbcSMatt Macy 7920*eda14cbcSMatt Macy 7921*eda14cbcSMatt Macy typedef struct loadkey_cbdata { 7922*eda14cbcSMatt Macy boolean_t cb_loadkey; 7923*eda14cbcSMatt Macy boolean_t cb_recursive; 7924*eda14cbcSMatt Macy boolean_t cb_noop; 7925*eda14cbcSMatt Macy char *cb_keylocation; 7926*eda14cbcSMatt Macy uint64_t cb_numfailed; 7927*eda14cbcSMatt Macy uint64_t cb_numattempted; 7928*eda14cbcSMatt Macy } loadkey_cbdata_t; 7929*eda14cbcSMatt Macy 7930*eda14cbcSMatt Macy static int 7931*eda14cbcSMatt Macy load_key_callback(zfs_handle_t *zhp, void *data) 7932*eda14cbcSMatt Macy { 7933*eda14cbcSMatt Macy int ret; 7934*eda14cbcSMatt Macy boolean_t is_encroot; 7935*eda14cbcSMatt Macy loadkey_cbdata_t *cb = data; 7936*eda14cbcSMatt Macy uint64_t keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS); 7937*eda14cbcSMatt Macy 7938*eda14cbcSMatt Macy /* 7939*eda14cbcSMatt Macy * If we are working recursively, we want to skip loading / unloading 7940*eda14cbcSMatt Macy * keys for non-encryption roots and datasets whose keys are already 7941*eda14cbcSMatt Macy * in the desired end-state. 7942*eda14cbcSMatt Macy */ 7943*eda14cbcSMatt Macy if (cb->cb_recursive) { 7944*eda14cbcSMatt Macy ret = zfs_crypto_get_encryption_root(zhp, &is_encroot, NULL); 7945*eda14cbcSMatt Macy if (ret != 0) 7946*eda14cbcSMatt Macy return (ret); 7947*eda14cbcSMatt Macy if (!is_encroot) 7948*eda14cbcSMatt Macy return (0); 7949*eda14cbcSMatt Macy 7950*eda14cbcSMatt Macy if ((cb->cb_loadkey && keystatus == ZFS_KEYSTATUS_AVAILABLE) || 7951*eda14cbcSMatt Macy (!cb->cb_loadkey && keystatus == ZFS_KEYSTATUS_UNAVAILABLE)) 7952*eda14cbcSMatt Macy return (0); 7953*eda14cbcSMatt Macy } 7954*eda14cbcSMatt Macy 7955*eda14cbcSMatt Macy cb->cb_numattempted++; 7956*eda14cbcSMatt Macy 7957*eda14cbcSMatt Macy if (cb->cb_loadkey) 7958*eda14cbcSMatt Macy ret = zfs_crypto_load_key(zhp, cb->cb_noop, cb->cb_keylocation); 7959*eda14cbcSMatt Macy else 7960*eda14cbcSMatt Macy ret = zfs_crypto_unload_key(zhp); 7961*eda14cbcSMatt Macy 7962*eda14cbcSMatt Macy if (ret != 0) { 7963*eda14cbcSMatt Macy cb->cb_numfailed++; 7964*eda14cbcSMatt Macy return (ret); 7965*eda14cbcSMatt Macy } 7966*eda14cbcSMatt Macy 7967*eda14cbcSMatt Macy return (0); 7968*eda14cbcSMatt Macy } 7969*eda14cbcSMatt Macy 7970*eda14cbcSMatt Macy static int 7971*eda14cbcSMatt Macy load_unload_keys(int argc, char **argv, boolean_t loadkey) 7972*eda14cbcSMatt Macy { 7973*eda14cbcSMatt Macy int c, ret = 0, flags = 0; 7974*eda14cbcSMatt Macy boolean_t do_all = B_FALSE; 7975*eda14cbcSMatt Macy loadkey_cbdata_t cb = { 0 }; 7976*eda14cbcSMatt Macy 7977*eda14cbcSMatt Macy cb.cb_loadkey = loadkey; 7978*eda14cbcSMatt Macy 7979*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "anrL:")) != -1) { 7980*eda14cbcSMatt Macy /* noop and alternate keylocations only apply to zfs load-key */ 7981*eda14cbcSMatt Macy if (loadkey) { 7982*eda14cbcSMatt Macy switch (c) { 7983*eda14cbcSMatt Macy case 'n': 7984*eda14cbcSMatt Macy cb.cb_noop = B_TRUE; 7985*eda14cbcSMatt Macy continue; 7986*eda14cbcSMatt Macy case 'L': 7987*eda14cbcSMatt Macy cb.cb_keylocation = optarg; 7988*eda14cbcSMatt Macy continue; 7989*eda14cbcSMatt Macy default: 7990*eda14cbcSMatt Macy break; 7991*eda14cbcSMatt Macy } 7992*eda14cbcSMatt Macy } 7993*eda14cbcSMatt Macy 7994*eda14cbcSMatt Macy switch (c) { 7995*eda14cbcSMatt Macy case 'a': 7996*eda14cbcSMatt Macy do_all = B_TRUE; 7997*eda14cbcSMatt Macy cb.cb_recursive = B_TRUE; 7998*eda14cbcSMatt Macy break; 7999*eda14cbcSMatt Macy case 'r': 8000*eda14cbcSMatt Macy flags |= ZFS_ITER_RECURSE; 8001*eda14cbcSMatt Macy cb.cb_recursive = B_TRUE; 8002*eda14cbcSMatt Macy break; 8003*eda14cbcSMatt Macy default: 8004*eda14cbcSMatt Macy (void) fprintf(stderr, 8005*eda14cbcSMatt Macy gettext("invalid option '%c'\n"), optopt); 8006*eda14cbcSMatt Macy usage(B_FALSE); 8007*eda14cbcSMatt Macy } 8008*eda14cbcSMatt Macy } 8009*eda14cbcSMatt Macy 8010*eda14cbcSMatt Macy argc -= optind; 8011*eda14cbcSMatt Macy argv += optind; 8012*eda14cbcSMatt Macy 8013*eda14cbcSMatt Macy if (!do_all && argc == 0) { 8014*eda14cbcSMatt Macy (void) fprintf(stderr, 8015*eda14cbcSMatt Macy gettext("Missing dataset argument or -a option\n")); 8016*eda14cbcSMatt Macy usage(B_FALSE); 8017*eda14cbcSMatt Macy } 8018*eda14cbcSMatt Macy 8019*eda14cbcSMatt Macy if (do_all && argc != 0) { 8020*eda14cbcSMatt Macy (void) fprintf(stderr, 8021*eda14cbcSMatt Macy gettext("Cannot specify dataset with -a option\n")); 8022*eda14cbcSMatt Macy usage(B_FALSE); 8023*eda14cbcSMatt Macy } 8024*eda14cbcSMatt Macy 8025*eda14cbcSMatt Macy if (cb.cb_recursive && cb.cb_keylocation != NULL && 8026*eda14cbcSMatt Macy strcmp(cb.cb_keylocation, "prompt") != 0) { 8027*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("alternate keylocation may only " 8028*eda14cbcSMatt Macy "be 'prompt' with -r or -a\n")); 8029*eda14cbcSMatt Macy usage(B_FALSE); 8030*eda14cbcSMatt Macy } 8031*eda14cbcSMatt Macy 8032*eda14cbcSMatt Macy ret = zfs_for_each(argc, argv, flags, 8033*eda14cbcSMatt Macy ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL, 0, 8034*eda14cbcSMatt Macy load_key_callback, &cb); 8035*eda14cbcSMatt Macy 8036*eda14cbcSMatt Macy if (cb.cb_noop || (cb.cb_recursive && cb.cb_numattempted != 0)) { 8037*eda14cbcSMatt Macy (void) printf(gettext("%llu / %llu key(s) successfully %s\n"), 8038*eda14cbcSMatt Macy (u_longlong_t)(cb.cb_numattempted - cb.cb_numfailed), 8039*eda14cbcSMatt Macy (u_longlong_t)cb.cb_numattempted, 8040*eda14cbcSMatt Macy loadkey ? (cb.cb_noop ? "verified" : "loaded") : 8041*eda14cbcSMatt Macy "unloaded"); 8042*eda14cbcSMatt Macy } 8043*eda14cbcSMatt Macy 8044*eda14cbcSMatt Macy if (cb.cb_numfailed != 0) 8045*eda14cbcSMatt Macy ret = -1; 8046*eda14cbcSMatt Macy 8047*eda14cbcSMatt Macy return (ret); 8048*eda14cbcSMatt Macy } 8049*eda14cbcSMatt Macy 8050*eda14cbcSMatt Macy static int 8051*eda14cbcSMatt Macy zfs_do_load_key(int argc, char **argv) 8052*eda14cbcSMatt Macy { 8053*eda14cbcSMatt Macy return (load_unload_keys(argc, argv, B_TRUE)); 8054*eda14cbcSMatt Macy } 8055*eda14cbcSMatt Macy 8056*eda14cbcSMatt Macy 8057*eda14cbcSMatt Macy static int 8058*eda14cbcSMatt Macy zfs_do_unload_key(int argc, char **argv) 8059*eda14cbcSMatt Macy { 8060*eda14cbcSMatt Macy return (load_unload_keys(argc, argv, B_FALSE)); 8061*eda14cbcSMatt Macy } 8062*eda14cbcSMatt Macy 8063*eda14cbcSMatt Macy static int 8064*eda14cbcSMatt Macy zfs_do_change_key(int argc, char **argv) 8065*eda14cbcSMatt Macy { 8066*eda14cbcSMatt Macy int c, ret; 8067*eda14cbcSMatt Macy uint64_t keystatus; 8068*eda14cbcSMatt Macy boolean_t loadkey = B_FALSE, inheritkey = B_FALSE; 8069*eda14cbcSMatt Macy zfs_handle_t *zhp = NULL; 8070*eda14cbcSMatt Macy nvlist_t *props = fnvlist_alloc(); 8071*eda14cbcSMatt Macy 8072*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "lio:")) != -1) { 8073*eda14cbcSMatt Macy switch (c) { 8074*eda14cbcSMatt Macy case 'l': 8075*eda14cbcSMatt Macy loadkey = B_TRUE; 8076*eda14cbcSMatt Macy break; 8077*eda14cbcSMatt Macy case 'i': 8078*eda14cbcSMatt Macy inheritkey = B_TRUE; 8079*eda14cbcSMatt Macy break; 8080*eda14cbcSMatt Macy case 'o': 8081*eda14cbcSMatt Macy if (!parseprop(props, optarg)) { 8082*eda14cbcSMatt Macy nvlist_free(props); 8083*eda14cbcSMatt Macy return (1); 8084*eda14cbcSMatt Macy } 8085*eda14cbcSMatt Macy break; 8086*eda14cbcSMatt Macy default: 8087*eda14cbcSMatt Macy (void) fprintf(stderr, 8088*eda14cbcSMatt Macy gettext("invalid option '%c'\n"), optopt); 8089*eda14cbcSMatt Macy usage(B_FALSE); 8090*eda14cbcSMatt Macy } 8091*eda14cbcSMatt Macy } 8092*eda14cbcSMatt Macy 8093*eda14cbcSMatt Macy if (inheritkey && !nvlist_empty(props)) { 8094*eda14cbcSMatt Macy (void) fprintf(stderr, 8095*eda14cbcSMatt Macy gettext("Properties not allowed for inheriting\n")); 8096*eda14cbcSMatt Macy usage(B_FALSE); 8097*eda14cbcSMatt Macy } 8098*eda14cbcSMatt Macy 8099*eda14cbcSMatt Macy argc -= optind; 8100*eda14cbcSMatt Macy argv += optind; 8101*eda14cbcSMatt Macy 8102*eda14cbcSMatt Macy if (argc < 1) { 8103*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("Missing dataset argument\n")); 8104*eda14cbcSMatt Macy usage(B_FALSE); 8105*eda14cbcSMatt Macy } 8106*eda14cbcSMatt Macy 8107*eda14cbcSMatt Macy if (argc > 1) { 8108*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("Too many arguments\n")); 8109*eda14cbcSMatt Macy usage(B_FALSE); 8110*eda14cbcSMatt Macy } 8111*eda14cbcSMatt Macy 8112*eda14cbcSMatt Macy zhp = zfs_open(g_zfs, argv[argc - 1], 8113*eda14cbcSMatt Macy ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 8114*eda14cbcSMatt Macy if (zhp == NULL) 8115*eda14cbcSMatt Macy usage(B_FALSE); 8116*eda14cbcSMatt Macy 8117*eda14cbcSMatt Macy if (loadkey) { 8118*eda14cbcSMatt Macy keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS); 8119*eda14cbcSMatt Macy if (keystatus != ZFS_KEYSTATUS_AVAILABLE) { 8120*eda14cbcSMatt Macy ret = zfs_crypto_load_key(zhp, B_FALSE, NULL); 8121*eda14cbcSMatt Macy if (ret != 0) { 8122*eda14cbcSMatt Macy nvlist_free(props); 8123*eda14cbcSMatt Macy zfs_close(zhp); 8124*eda14cbcSMatt Macy return (-1); 8125*eda14cbcSMatt Macy } 8126*eda14cbcSMatt Macy } 8127*eda14cbcSMatt Macy 8128*eda14cbcSMatt Macy /* refresh the properties so the new keystatus is visible */ 8129*eda14cbcSMatt Macy zfs_refresh_properties(zhp); 8130*eda14cbcSMatt Macy } 8131*eda14cbcSMatt Macy 8132*eda14cbcSMatt Macy ret = zfs_crypto_rewrap(zhp, props, inheritkey); 8133*eda14cbcSMatt Macy if (ret != 0) { 8134*eda14cbcSMatt Macy nvlist_free(props); 8135*eda14cbcSMatt Macy zfs_close(zhp); 8136*eda14cbcSMatt Macy return (-1); 8137*eda14cbcSMatt Macy } 8138*eda14cbcSMatt Macy 8139*eda14cbcSMatt Macy nvlist_free(props); 8140*eda14cbcSMatt Macy zfs_close(zhp); 8141*eda14cbcSMatt Macy return (0); 8142*eda14cbcSMatt Macy } 8143*eda14cbcSMatt Macy 8144*eda14cbcSMatt Macy /* 8145*eda14cbcSMatt Macy * 1) zfs project [-d|-r] <file|directory ...> 8146*eda14cbcSMatt Macy * List project ID and inherit flag of file(s) or directories. 8147*eda14cbcSMatt Macy * -d: List the directory itself, not its children. 8148*eda14cbcSMatt Macy * -r: List subdirectories recursively. 8149*eda14cbcSMatt Macy * 8150*eda14cbcSMatt Macy * 2) zfs project -C [-k] [-r] <file|directory ...> 8151*eda14cbcSMatt Macy * Clear project inherit flag and/or ID on the file(s) or directories. 8152*eda14cbcSMatt Macy * -k: Keep the project ID unchanged. If not specified, the project ID 8153*eda14cbcSMatt Macy * will be reset as zero. 8154*eda14cbcSMatt Macy * -r: Clear on subdirectories recursively. 8155*eda14cbcSMatt Macy * 8156*eda14cbcSMatt Macy * 3) zfs project -c [-0] [-d|-r] [-p id] <file|directory ...> 8157*eda14cbcSMatt Macy * Check project ID and inherit flag on the file(s) or directories, 8158*eda14cbcSMatt Macy * report the outliers. 8159*eda14cbcSMatt Macy * -0: Print file name followed by a NUL instead of newline. 8160*eda14cbcSMatt Macy * -d: Check the directory itself, not its children. 8161*eda14cbcSMatt Macy * -p: Specify the referenced ID for comparing with the target file(s) 8162*eda14cbcSMatt Macy * or directories' project IDs. If not specified, the target (top) 8163*eda14cbcSMatt Macy * directory's project ID will be used as the referenced one. 8164*eda14cbcSMatt Macy * -r: Check subdirectories recursively. 8165*eda14cbcSMatt Macy * 8166*eda14cbcSMatt Macy * 4) zfs project [-p id] [-r] [-s] <file|directory ...> 8167*eda14cbcSMatt Macy * Set project ID and/or inherit flag on the file(s) or directories. 8168*eda14cbcSMatt Macy * -p: Set the project ID as the given id. 8169*eda14cbcSMatt Macy * -r: Set on subdirectories recursively. If not specify "-p" option, 8170*eda14cbcSMatt Macy * it will use top-level directory's project ID as the given id, 8171*eda14cbcSMatt Macy * then set both project ID and inherit flag on all descendants 8172*eda14cbcSMatt Macy * of the top-level directory. 8173*eda14cbcSMatt Macy * -s: Set project inherit flag. 8174*eda14cbcSMatt Macy */ 8175*eda14cbcSMatt Macy static int 8176*eda14cbcSMatt Macy zfs_do_project(int argc, char **argv) 8177*eda14cbcSMatt Macy { 8178*eda14cbcSMatt Macy zfs_project_control_t zpc = { 8179*eda14cbcSMatt Macy .zpc_expected_projid = ZFS_INVALID_PROJID, 8180*eda14cbcSMatt Macy .zpc_op = ZFS_PROJECT_OP_DEFAULT, 8181*eda14cbcSMatt Macy .zpc_dironly = B_FALSE, 8182*eda14cbcSMatt Macy .zpc_keep_projid = B_FALSE, 8183*eda14cbcSMatt Macy .zpc_newline = B_TRUE, 8184*eda14cbcSMatt Macy .zpc_recursive = B_FALSE, 8185*eda14cbcSMatt Macy .zpc_set_flag = B_FALSE, 8186*eda14cbcSMatt Macy }; 8187*eda14cbcSMatt Macy int ret = 0, c; 8188*eda14cbcSMatt Macy 8189*eda14cbcSMatt Macy if (argc < 2) 8190*eda14cbcSMatt Macy usage(B_FALSE); 8191*eda14cbcSMatt Macy 8192*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "0Ccdkp:rs")) != -1) { 8193*eda14cbcSMatt Macy switch (c) { 8194*eda14cbcSMatt Macy case '0': 8195*eda14cbcSMatt Macy zpc.zpc_newline = B_FALSE; 8196*eda14cbcSMatt Macy break; 8197*eda14cbcSMatt Macy case 'C': 8198*eda14cbcSMatt Macy if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) { 8199*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot " 8200*eda14cbcSMatt Macy "specify '-C' '-c' '-s' together\n")); 8201*eda14cbcSMatt Macy usage(B_FALSE); 8202*eda14cbcSMatt Macy } 8203*eda14cbcSMatt Macy 8204*eda14cbcSMatt Macy zpc.zpc_op = ZFS_PROJECT_OP_CLEAR; 8205*eda14cbcSMatt Macy break; 8206*eda14cbcSMatt Macy case 'c': 8207*eda14cbcSMatt Macy if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) { 8208*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot " 8209*eda14cbcSMatt Macy "specify '-C' '-c' '-s' together\n")); 8210*eda14cbcSMatt Macy usage(B_FALSE); 8211*eda14cbcSMatt Macy } 8212*eda14cbcSMatt Macy 8213*eda14cbcSMatt Macy zpc.zpc_op = ZFS_PROJECT_OP_CHECK; 8214*eda14cbcSMatt Macy break; 8215*eda14cbcSMatt Macy case 'd': 8216*eda14cbcSMatt Macy zpc.zpc_dironly = B_TRUE; 8217*eda14cbcSMatt Macy /* overwrite "-r" option */ 8218*eda14cbcSMatt Macy zpc.zpc_recursive = B_FALSE; 8219*eda14cbcSMatt Macy break; 8220*eda14cbcSMatt Macy case 'k': 8221*eda14cbcSMatt Macy zpc.zpc_keep_projid = B_TRUE; 8222*eda14cbcSMatt Macy break; 8223*eda14cbcSMatt Macy case 'p': { 8224*eda14cbcSMatt Macy char *endptr; 8225*eda14cbcSMatt Macy 8226*eda14cbcSMatt Macy errno = 0; 8227*eda14cbcSMatt Macy zpc.zpc_expected_projid = strtoull(optarg, &endptr, 0); 8228*eda14cbcSMatt Macy if (errno != 0 || *endptr != '\0') { 8229*eda14cbcSMatt Macy (void) fprintf(stderr, 8230*eda14cbcSMatt Macy gettext("project ID must be less than " 8231*eda14cbcSMatt Macy "%u\n"), UINT32_MAX); 8232*eda14cbcSMatt Macy usage(B_FALSE); 8233*eda14cbcSMatt Macy } 8234*eda14cbcSMatt Macy if (zpc.zpc_expected_projid >= UINT32_MAX) { 8235*eda14cbcSMatt Macy (void) fprintf(stderr, 8236*eda14cbcSMatt Macy gettext("invalid project ID\n")); 8237*eda14cbcSMatt Macy usage(B_FALSE); 8238*eda14cbcSMatt Macy } 8239*eda14cbcSMatt Macy break; 8240*eda14cbcSMatt Macy } 8241*eda14cbcSMatt Macy case 'r': 8242*eda14cbcSMatt Macy zpc.zpc_recursive = B_TRUE; 8243*eda14cbcSMatt Macy /* overwrite "-d" option */ 8244*eda14cbcSMatt Macy zpc.zpc_dironly = B_FALSE; 8245*eda14cbcSMatt Macy break; 8246*eda14cbcSMatt Macy case 's': 8247*eda14cbcSMatt Macy if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) { 8248*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("cannot " 8249*eda14cbcSMatt Macy "specify '-C' '-c' '-s' together\n")); 8250*eda14cbcSMatt Macy usage(B_FALSE); 8251*eda14cbcSMatt Macy } 8252*eda14cbcSMatt Macy 8253*eda14cbcSMatt Macy zpc.zpc_set_flag = B_TRUE; 8254*eda14cbcSMatt Macy zpc.zpc_op = ZFS_PROJECT_OP_SET; 8255*eda14cbcSMatt Macy break; 8256*eda14cbcSMatt Macy default: 8257*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 8258*eda14cbcSMatt Macy optopt); 8259*eda14cbcSMatt Macy usage(B_FALSE); 8260*eda14cbcSMatt Macy } 8261*eda14cbcSMatt Macy } 8262*eda14cbcSMatt Macy 8263*eda14cbcSMatt Macy if (zpc.zpc_op == ZFS_PROJECT_OP_DEFAULT) { 8264*eda14cbcSMatt Macy if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID) 8265*eda14cbcSMatt Macy zpc.zpc_op = ZFS_PROJECT_OP_SET; 8266*eda14cbcSMatt Macy else 8267*eda14cbcSMatt Macy zpc.zpc_op = ZFS_PROJECT_OP_LIST; 8268*eda14cbcSMatt Macy } 8269*eda14cbcSMatt Macy 8270*eda14cbcSMatt Macy switch (zpc.zpc_op) { 8271*eda14cbcSMatt Macy case ZFS_PROJECT_OP_LIST: 8272*eda14cbcSMatt Macy if (zpc.zpc_keep_projid) { 8273*eda14cbcSMatt Macy (void) fprintf(stderr, 8274*eda14cbcSMatt Macy gettext("'-k' is only valid together with '-C'\n")); 8275*eda14cbcSMatt Macy usage(B_FALSE); 8276*eda14cbcSMatt Macy } 8277*eda14cbcSMatt Macy if (!zpc.zpc_newline) { 8278*eda14cbcSMatt Macy (void) fprintf(stderr, 8279*eda14cbcSMatt Macy gettext("'-0' is only valid together with '-c'\n")); 8280*eda14cbcSMatt Macy usage(B_FALSE); 8281*eda14cbcSMatt Macy } 8282*eda14cbcSMatt Macy break; 8283*eda14cbcSMatt Macy case ZFS_PROJECT_OP_CHECK: 8284*eda14cbcSMatt Macy if (zpc.zpc_keep_projid) { 8285*eda14cbcSMatt Macy (void) fprintf(stderr, 8286*eda14cbcSMatt Macy gettext("'-k' is only valid together with '-C'\n")); 8287*eda14cbcSMatt Macy usage(B_FALSE); 8288*eda14cbcSMatt Macy } 8289*eda14cbcSMatt Macy break; 8290*eda14cbcSMatt Macy case ZFS_PROJECT_OP_CLEAR: 8291*eda14cbcSMatt Macy if (zpc.zpc_dironly) { 8292*eda14cbcSMatt Macy (void) fprintf(stderr, 8293*eda14cbcSMatt Macy gettext("'-d' is useless together with '-C'\n")); 8294*eda14cbcSMatt Macy usage(B_FALSE); 8295*eda14cbcSMatt Macy } 8296*eda14cbcSMatt Macy if (!zpc.zpc_newline) { 8297*eda14cbcSMatt Macy (void) fprintf(stderr, 8298*eda14cbcSMatt Macy gettext("'-0' is only valid together with '-c'\n")); 8299*eda14cbcSMatt Macy usage(B_FALSE); 8300*eda14cbcSMatt Macy } 8301*eda14cbcSMatt Macy if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID) { 8302*eda14cbcSMatt Macy (void) fprintf(stderr, 8303*eda14cbcSMatt Macy gettext("'-p' is useless together with '-C'\n")); 8304*eda14cbcSMatt Macy usage(B_FALSE); 8305*eda14cbcSMatt Macy } 8306*eda14cbcSMatt Macy break; 8307*eda14cbcSMatt Macy case ZFS_PROJECT_OP_SET: 8308*eda14cbcSMatt Macy if (zpc.zpc_dironly) { 8309*eda14cbcSMatt Macy (void) fprintf(stderr, 8310*eda14cbcSMatt Macy gettext("'-d' is useless for set project ID and/or " 8311*eda14cbcSMatt Macy "inherit flag\n")); 8312*eda14cbcSMatt Macy usage(B_FALSE); 8313*eda14cbcSMatt Macy } 8314*eda14cbcSMatt Macy if (zpc.zpc_keep_projid) { 8315*eda14cbcSMatt Macy (void) fprintf(stderr, 8316*eda14cbcSMatt Macy gettext("'-k' is only valid together with '-C'\n")); 8317*eda14cbcSMatt Macy usage(B_FALSE); 8318*eda14cbcSMatt Macy } 8319*eda14cbcSMatt Macy if (!zpc.zpc_newline) { 8320*eda14cbcSMatt Macy (void) fprintf(stderr, 8321*eda14cbcSMatt Macy gettext("'-0' is only valid together with '-c'\n")); 8322*eda14cbcSMatt Macy usage(B_FALSE); 8323*eda14cbcSMatt Macy } 8324*eda14cbcSMatt Macy break; 8325*eda14cbcSMatt Macy default: 8326*eda14cbcSMatt Macy ASSERT(0); 8327*eda14cbcSMatt Macy break; 8328*eda14cbcSMatt Macy } 8329*eda14cbcSMatt Macy 8330*eda14cbcSMatt Macy argv += optind; 8331*eda14cbcSMatt Macy argc -= optind; 8332*eda14cbcSMatt Macy if (argc == 0) { 8333*eda14cbcSMatt Macy (void) fprintf(stderr, 8334*eda14cbcSMatt Macy gettext("missing file or directory target(s)\n")); 8335*eda14cbcSMatt Macy usage(B_FALSE); 8336*eda14cbcSMatt Macy } 8337*eda14cbcSMatt Macy 8338*eda14cbcSMatt Macy for (int i = 0; i < argc; i++) { 8339*eda14cbcSMatt Macy int err; 8340*eda14cbcSMatt Macy 8341*eda14cbcSMatt Macy err = zfs_project_handle(argv[i], &zpc); 8342*eda14cbcSMatt Macy if (err && !ret) 8343*eda14cbcSMatt Macy ret = err; 8344*eda14cbcSMatt Macy } 8345*eda14cbcSMatt Macy 8346*eda14cbcSMatt Macy return (ret); 8347*eda14cbcSMatt Macy } 8348*eda14cbcSMatt Macy 8349*eda14cbcSMatt Macy static int 8350*eda14cbcSMatt Macy zfs_do_wait(int argc, char **argv) 8351*eda14cbcSMatt Macy { 8352*eda14cbcSMatt Macy boolean_t enabled[ZFS_WAIT_NUM_ACTIVITIES]; 8353*eda14cbcSMatt Macy int error, i; 8354*eda14cbcSMatt Macy char c; 8355*eda14cbcSMatt Macy 8356*eda14cbcSMatt Macy /* By default, wait for all types of activity. */ 8357*eda14cbcSMatt Macy for (i = 0; i < ZFS_WAIT_NUM_ACTIVITIES; i++) 8358*eda14cbcSMatt Macy enabled[i] = B_TRUE; 8359*eda14cbcSMatt Macy 8360*eda14cbcSMatt Macy while ((c = getopt(argc, argv, "t:")) != -1) { 8361*eda14cbcSMatt Macy switch (c) { 8362*eda14cbcSMatt Macy case 't': 8363*eda14cbcSMatt Macy { 8364*eda14cbcSMatt Macy static char *col_subopts[] = { "deleteq", NULL }; 8365*eda14cbcSMatt Macy char *value; 8366*eda14cbcSMatt Macy 8367*eda14cbcSMatt Macy /* Reset activities array */ 8368*eda14cbcSMatt Macy bzero(&enabled, sizeof (enabled)); 8369*eda14cbcSMatt Macy while (*optarg != '\0') { 8370*eda14cbcSMatt Macy int activity = getsubopt(&optarg, col_subopts, 8371*eda14cbcSMatt Macy &value); 8372*eda14cbcSMatt Macy 8373*eda14cbcSMatt Macy if (activity < 0) { 8374*eda14cbcSMatt Macy (void) fprintf(stderr, 8375*eda14cbcSMatt Macy gettext("invalid activity '%s'\n"), 8376*eda14cbcSMatt Macy value); 8377*eda14cbcSMatt Macy usage(B_FALSE); 8378*eda14cbcSMatt Macy } 8379*eda14cbcSMatt Macy 8380*eda14cbcSMatt Macy enabled[activity] = B_TRUE; 8381*eda14cbcSMatt Macy } 8382*eda14cbcSMatt Macy break; 8383*eda14cbcSMatt Macy } 8384*eda14cbcSMatt Macy case '?': 8385*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid option '%c'\n"), 8386*eda14cbcSMatt Macy optopt); 8387*eda14cbcSMatt Macy usage(B_FALSE); 8388*eda14cbcSMatt Macy } 8389*eda14cbcSMatt Macy } 8390*eda14cbcSMatt Macy 8391*eda14cbcSMatt Macy argv += optind; 8392*eda14cbcSMatt Macy argc -= optind; 8393*eda14cbcSMatt Macy if (argc < 1) { 8394*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing 'filesystem' " 8395*eda14cbcSMatt Macy "argument\n")); 8396*eda14cbcSMatt Macy usage(B_FALSE); 8397*eda14cbcSMatt Macy } 8398*eda14cbcSMatt Macy if (argc > 1) { 8399*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many arguments\n")); 8400*eda14cbcSMatt Macy usage(B_FALSE); 8401*eda14cbcSMatt Macy } 8402*eda14cbcSMatt Macy 8403*eda14cbcSMatt Macy zfs_handle_t *zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM); 8404*eda14cbcSMatt Macy if (zhp == NULL) 8405*eda14cbcSMatt Macy return (1); 8406*eda14cbcSMatt Macy 8407*eda14cbcSMatt Macy for (;;) { 8408*eda14cbcSMatt Macy boolean_t missing = B_FALSE; 8409*eda14cbcSMatt Macy boolean_t any_waited = B_FALSE; 8410*eda14cbcSMatt Macy 8411*eda14cbcSMatt Macy for (int i = 0; i < ZFS_WAIT_NUM_ACTIVITIES; i++) { 8412*eda14cbcSMatt Macy boolean_t waited; 8413*eda14cbcSMatt Macy 8414*eda14cbcSMatt Macy if (!enabled[i]) 8415*eda14cbcSMatt Macy continue; 8416*eda14cbcSMatt Macy 8417*eda14cbcSMatt Macy error = zfs_wait_status(zhp, i, &missing, &waited); 8418*eda14cbcSMatt Macy if (error != 0 || missing) 8419*eda14cbcSMatt Macy break; 8420*eda14cbcSMatt Macy 8421*eda14cbcSMatt Macy any_waited = (any_waited || waited); 8422*eda14cbcSMatt Macy } 8423*eda14cbcSMatt Macy 8424*eda14cbcSMatt Macy if (error != 0 || missing || !any_waited) 8425*eda14cbcSMatt Macy break; 8426*eda14cbcSMatt Macy } 8427*eda14cbcSMatt Macy 8428*eda14cbcSMatt Macy zfs_close(zhp); 8429*eda14cbcSMatt Macy 8430*eda14cbcSMatt Macy return (error); 8431*eda14cbcSMatt Macy } 8432*eda14cbcSMatt Macy 8433*eda14cbcSMatt Macy /* 8434*eda14cbcSMatt Macy * Display version message 8435*eda14cbcSMatt Macy */ 8436*eda14cbcSMatt Macy static int 8437*eda14cbcSMatt Macy zfs_do_version(int argc, char **argv) 8438*eda14cbcSMatt Macy { 8439*eda14cbcSMatt Macy if (zfs_version_print() == -1) 8440*eda14cbcSMatt Macy return (1); 8441*eda14cbcSMatt Macy 8442*eda14cbcSMatt Macy return (0); 8443*eda14cbcSMatt Macy } 8444*eda14cbcSMatt Macy 8445*eda14cbcSMatt Macy int 8446*eda14cbcSMatt Macy main(int argc, char **argv) 8447*eda14cbcSMatt Macy { 8448*eda14cbcSMatt Macy int ret = 0; 8449*eda14cbcSMatt Macy int i = 0; 8450*eda14cbcSMatt Macy char *cmdname; 8451*eda14cbcSMatt Macy char **newargv; 8452*eda14cbcSMatt Macy 8453*eda14cbcSMatt Macy (void) setlocale(LC_ALL, ""); 8454*eda14cbcSMatt Macy (void) textdomain(TEXT_DOMAIN); 8455*eda14cbcSMatt Macy 8456*eda14cbcSMatt Macy opterr = 0; 8457*eda14cbcSMatt Macy 8458*eda14cbcSMatt Macy /* 8459*eda14cbcSMatt Macy * Make sure the user has specified some command. 8460*eda14cbcSMatt Macy */ 8461*eda14cbcSMatt Macy if (argc < 2) { 8462*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing command\n")); 8463*eda14cbcSMatt Macy usage(B_FALSE); 8464*eda14cbcSMatt Macy } 8465*eda14cbcSMatt Macy 8466*eda14cbcSMatt Macy cmdname = argv[1]; 8467*eda14cbcSMatt Macy 8468*eda14cbcSMatt Macy /* 8469*eda14cbcSMatt Macy * The 'umount' command is an alias for 'unmount' 8470*eda14cbcSMatt Macy */ 8471*eda14cbcSMatt Macy if (strcmp(cmdname, "umount") == 0) 8472*eda14cbcSMatt Macy cmdname = "unmount"; 8473*eda14cbcSMatt Macy 8474*eda14cbcSMatt Macy /* 8475*eda14cbcSMatt Macy * The 'recv' command is an alias for 'receive' 8476*eda14cbcSMatt Macy */ 8477*eda14cbcSMatt Macy if (strcmp(cmdname, "recv") == 0) 8478*eda14cbcSMatt Macy cmdname = "receive"; 8479*eda14cbcSMatt Macy 8480*eda14cbcSMatt Macy /* 8481*eda14cbcSMatt Macy * The 'snap' command is an alias for 'snapshot' 8482*eda14cbcSMatt Macy */ 8483*eda14cbcSMatt Macy if (strcmp(cmdname, "snap") == 0) 8484*eda14cbcSMatt Macy cmdname = "snapshot"; 8485*eda14cbcSMatt Macy 8486*eda14cbcSMatt Macy /* 8487*eda14cbcSMatt Macy * Special case '-?' 8488*eda14cbcSMatt Macy */ 8489*eda14cbcSMatt Macy if ((strcmp(cmdname, "-?") == 0) || 8490*eda14cbcSMatt Macy (strcmp(cmdname, "--help") == 0)) 8491*eda14cbcSMatt Macy usage(B_TRUE); 8492*eda14cbcSMatt Macy 8493*eda14cbcSMatt Macy /* 8494*eda14cbcSMatt Macy * Special case '-V|--version' 8495*eda14cbcSMatt Macy */ 8496*eda14cbcSMatt Macy if ((strcmp(cmdname, "-V") == 0) || (strcmp(cmdname, "--version") == 0)) 8497*eda14cbcSMatt Macy return (zfs_do_version(argc, argv)); 8498*eda14cbcSMatt Macy 8499*eda14cbcSMatt Macy if ((g_zfs = libzfs_init()) == NULL) { 8500*eda14cbcSMatt Macy (void) fprintf(stderr, "%s\n", libzfs_error_init(errno)); 8501*eda14cbcSMatt Macy return (1); 8502*eda14cbcSMatt Macy } 8503*eda14cbcSMatt Macy 8504*eda14cbcSMatt Macy mnttab_file = g_zfs->libzfs_mnttab; 8505*eda14cbcSMatt Macy 8506*eda14cbcSMatt Macy zfs_save_arguments(argc, argv, history_str, sizeof (history_str)); 8507*eda14cbcSMatt Macy 8508*eda14cbcSMatt Macy libzfs_print_on_error(g_zfs, B_TRUE); 8509*eda14cbcSMatt Macy 8510*eda14cbcSMatt Macy /* 8511*eda14cbcSMatt Macy * Many commands modify input strings for string parsing reasons. 8512*eda14cbcSMatt Macy * We create a copy to protect the original argv. 8513*eda14cbcSMatt Macy */ 8514*eda14cbcSMatt Macy newargv = malloc((argc + 1) * sizeof (newargv[0])); 8515*eda14cbcSMatt Macy for (i = 0; i < argc; i++) 8516*eda14cbcSMatt Macy newargv[i] = strdup(argv[i]); 8517*eda14cbcSMatt Macy newargv[argc] = NULL; 8518*eda14cbcSMatt Macy 8519*eda14cbcSMatt Macy /* 8520*eda14cbcSMatt Macy * Run the appropriate command. 8521*eda14cbcSMatt Macy */ 8522*eda14cbcSMatt Macy libzfs_mnttab_cache(g_zfs, B_TRUE); 8523*eda14cbcSMatt Macy if (find_command_idx(cmdname, &i) == 0) { 8524*eda14cbcSMatt Macy current_command = &command_table[i]; 8525*eda14cbcSMatt Macy ret = command_table[i].func(argc - 1, newargv + 1); 8526*eda14cbcSMatt Macy } else if (strchr(cmdname, '=') != NULL) { 8527*eda14cbcSMatt Macy verify(find_command_idx("set", &i) == 0); 8528*eda14cbcSMatt Macy current_command = &command_table[i]; 8529*eda14cbcSMatt Macy ret = command_table[i].func(argc, newargv); 8530*eda14cbcSMatt Macy } else { 8531*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("unrecognized " 8532*eda14cbcSMatt Macy "command '%s'\n"), cmdname); 8533*eda14cbcSMatt Macy usage(B_FALSE); 8534*eda14cbcSMatt Macy ret = 1; 8535*eda14cbcSMatt Macy } 8536*eda14cbcSMatt Macy 8537*eda14cbcSMatt Macy for (i = 0; i < argc; i++) 8538*eda14cbcSMatt Macy free(newargv[i]); 8539*eda14cbcSMatt Macy free(newargv); 8540*eda14cbcSMatt Macy 8541*eda14cbcSMatt Macy if (ret == 0 && log_history) 8542*eda14cbcSMatt Macy (void) zpool_log_history(g_zfs, history_str); 8543*eda14cbcSMatt Macy 8544*eda14cbcSMatt Macy libzfs_fini(g_zfs); 8545*eda14cbcSMatt Macy 8546*eda14cbcSMatt Macy /* 8547*eda14cbcSMatt Macy * The 'ZFS_ABORT' environment variable causes us to dump core on exit 8548*eda14cbcSMatt Macy * for the purposes of running ::findleaks. 8549*eda14cbcSMatt Macy */ 8550*eda14cbcSMatt Macy if (getenv("ZFS_ABORT") != NULL) { 8551*eda14cbcSMatt Macy (void) printf("dumping core by request\n"); 8552*eda14cbcSMatt Macy abort(); 8553*eda14cbcSMatt Macy } 8554*eda14cbcSMatt Macy 8555*eda14cbcSMatt Macy return (ret); 8556*eda14cbcSMatt Macy } 8557*eda14cbcSMatt Macy 8558*eda14cbcSMatt Macy #ifdef __FreeBSD__ 8559*eda14cbcSMatt Macy #include <sys/jail.h> 8560*eda14cbcSMatt Macy #include <jail.h> 8561*eda14cbcSMatt Macy /* 8562*eda14cbcSMatt Macy * Attach/detach the given dataset to/from the given jail 8563*eda14cbcSMatt Macy */ 8564*eda14cbcSMatt Macy /* ARGSUSED */ 8565*eda14cbcSMatt Macy static int 8566*eda14cbcSMatt Macy zfs_do_jail_impl(int argc, char **argv, boolean_t attach) 8567*eda14cbcSMatt Macy { 8568*eda14cbcSMatt Macy zfs_handle_t *zhp; 8569*eda14cbcSMatt Macy int jailid, ret; 8570*eda14cbcSMatt Macy 8571*eda14cbcSMatt Macy /* check number of arguments */ 8572*eda14cbcSMatt Macy if (argc < 3) { 8573*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("missing argument(s)\n")); 8574*eda14cbcSMatt Macy usage(B_FALSE); 8575*eda14cbcSMatt Macy } 8576*eda14cbcSMatt Macy if (argc > 3) { 8577*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("too many arguments\n")); 8578*eda14cbcSMatt Macy usage(B_FALSE); 8579*eda14cbcSMatt Macy } 8580*eda14cbcSMatt Macy 8581*eda14cbcSMatt Macy jailid = jail_getid(argv[1]); 8582*eda14cbcSMatt Macy if (jailid < 0) { 8583*eda14cbcSMatt Macy (void) fprintf(stderr, gettext("invalid jail id or name\n")); 8584*eda14cbcSMatt Macy usage(B_FALSE); 8585*eda14cbcSMatt Macy } 8586*eda14cbcSMatt Macy 8587*eda14cbcSMatt Macy zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM); 8588*eda14cbcSMatt Macy if (zhp == NULL) 8589*eda14cbcSMatt Macy return (1); 8590*eda14cbcSMatt Macy 8591*eda14cbcSMatt Macy ret = (zfs_jail(zhp, jailid, attach) != 0); 8592*eda14cbcSMatt Macy 8593*eda14cbcSMatt Macy zfs_close(zhp); 8594*eda14cbcSMatt Macy return (ret); 8595*eda14cbcSMatt Macy } 8596*eda14cbcSMatt Macy 8597*eda14cbcSMatt Macy /* 8598*eda14cbcSMatt Macy * zfs jail jailid filesystem 8599*eda14cbcSMatt Macy * 8600*eda14cbcSMatt Macy * Attach the given dataset to the given jail 8601*eda14cbcSMatt Macy */ 8602*eda14cbcSMatt Macy /* ARGSUSED */ 8603*eda14cbcSMatt Macy static int 8604*eda14cbcSMatt Macy zfs_do_jail(int argc, char **argv) 8605*eda14cbcSMatt Macy { 8606*eda14cbcSMatt Macy return (zfs_do_jail_impl(argc, argv, B_TRUE)); 8607*eda14cbcSMatt Macy } 8608*eda14cbcSMatt Macy 8609*eda14cbcSMatt Macy /* 8610*eda14cbcSMatt Macy * zfs unjail jailid filesystem 8611*eda14cbcSMatt Macy * 8612*eda14cbcSMatt Macy * Detach the given dataset from the given jail 8613*eda14cbcSMatt Macy */ 8614*eda14cbcSMatt Macy /* ARGSUSED */ 8615*eda14cbcSMatt Macy static int 8616*eda14cbcSMatt Macy zfs_do_unjail(int argc, char **argv) 8617*eda14cbcSMatt Macy { 8618*eda14cbcSMatt Macy return (zfs_do_jail_impl(argc, argv, B_FALSE)); 8619*eda14cbcSMatt Macy } 8620*eda14cbcSMatt Macy #endif 8621