1168404Spjd/* 2168404Spjd * CDDL HEADER START 3168404Spjd * 4168404Spjd * The contents of this file are subject to the terms of the 5168404Spjd * Common Development and Distribution License (the "License"). 6168404Spjd * You may not use this file except in compliance with the License. 7168404Spjd * 8168404Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9168404Spjd * or http://www.opensolaris.org/os/licensing. 10168404Spjd * See the License for the specific language governing permissions 11168404Spjd * and limitations under the License. 12168404Spjd * 13168404Spjd * When distributing Covered Code, include this CDDL HEADER in each 14168404Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15168404Spjd * If applicable, add the following below this CDDL HEADER, with the 16168404Spjd * fields enclosed by brackets "[]" replaced with your own identifying 17168404Spjd * information: Portions Copyright [yyyy] [name of copyright owner] 18168404Spjd * 19168404Spjd * CDDL HEADER END 20168404Spjd */ 21168404Spjd 22168404Spjd/* 23219089Spjd * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. 24290760Smav * Copyright (c) 2011, 2015 by Delphix. All rights reserved. 25235217Smm * Copyright 2012 Milan Jurik. All rights reserved. 26235222Smm * Copyright (c) 2012, Joyent, Inc. All rights reserved. 27230438Spjd * Copyright (c) 2011-2012 Pawel Jakub Dawidek <pawel@dawidek.net>. 28230402Smm * All rights reserved. 29235216Smm * Copyright (c) 2012 Martin Matuska <mm@FreeBSD.org>. All rights reserved. 30251646Sdelphij * Copyright (c) 2013 Steven Hartland. All rights reserved. 31263405Sdelphij * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 32168404Spjd */ 33168404Spjd 34168404Spjd#include <assert.h> 35168404Spjd#include <ctype.h> 36168404Spjd#include <errno.h> 37168404Spjd#include <libgen.h> 38168404Spjd#include <libintl.h> 39168404Spjd#include <libuutil.h> 40185029Spjd#include <libnvpair.h> 41168404Spjd#include <locale.h> 42168404Spjd#include <stddef.h> 43168404Spjd#include <stdio.h> 44168404Spjd#include <stdlib.h> 45168404Spjd#include <strings.h> 46168404Spjd#include <unistd.h> 47168404Spjd#include <fcntl.h> 48168404Spjd#include <zone.h> 49209962Smm#include <grp.h> 50209962Smm#include <pwd.h> 51219089Spjd#include <signal.h> 52219089Spjd#include <sys/list.h> 53168404Spjd#include <sys/mntent.h> 54168404Spjd#include <sys/mnttab.h> 55168404Spjd#include <sys/mount.h> 56168404Spjd#include <sys/stat.h> 57209962Smm#include <sys/fs/zfs.h> 58219089Spjd#include <sys/types.h> 59219089Spjd#include <time.h> 60253930Srmh#include <err.h> 61256110Srmh#include <jail.h> 62168404Spjd 63168404Spjd#include <libzfs.h> 64248571Smm#include <libzfs_core.h> 65219089Spjd#include <zfs_prop.h> 66219089Spjd#include <zfs_deleg.h> 67185029Spjd#include <libuutil.h> 68219089Spjd#ifdef sun 69219089Spjd#include <aclutils.h> 70219089Spjd#include <directory.h> 71277552Sdelphij#include <idmap.h> 72219089Spjd#endif 73168404Spjd 74168404Spjd#include "zfs_iter.h" 75168404Spjd#include "zfs_util.h" 76219089Spjd#include "zfs_comutil.h" 77168404Spjd 78168404Spjdlibzfs_handle_t *g_zfs; 79168404Spjd 80168404Spjdstatic FILE *mnttab_file; 81185029Spjdstatic char history_str[HIS_MAX_RECORD_LEN]; 82248571Smmstatic boolean_t log_history = B_TRUE; 83168404Spjd 84168404Spjdstatic int zfs_do_clone(int argc, char **argv); 85168404Spjdstatic int zfs_do_create(int argc, char **argv); 86168404Spjdstatic int zfs_do_destroy(int argc, char **argv); 87168404Spjdstatic int zfs_do_get(int argc, char **argv); 88168404Spjdstatic int zfs_do_inherit(int argc, char **argv); 89168404Spjdstatic int zfs_do_list(int argc, char **argv); 90168404Spjdstatic int zfs_do_mount(int argc, char **argv); 91168404Spjdstatic int zfs_do_rename(int argc, char **argv); 92168404Spjdstatic int zfs_do_rollback(int argc, char **argv); 93168404Spjdstatic int zfs_do_set(int argc, char **argv); 94185029Spjdstatic int zfs_do_upgrade(int argc, char **argv); 95168404Spjdstatic int zfs_do_snapshot(int argc, char **argv); 96168404Spjdstatic int zfs_do_unmount(int argc, char **argv); 97168404Spjdstatic int zfs_do_share(int argc, char **argv); 98168404Spjdstatic int zfs_do_unshare(int argc, char **argv); 99168404Spjdstatic int zfs_do_send(int argc, char **argv); 100168404Spjdstatic int zfs_do_receive(int argc, char **argv); 101168404Spjdstatic int zfs_do_promote(int argc, char **argv); 102209962Smmstatic int zfs_do_userspace(int argc, char **argv); 103219089Spjdstatic int zfs_do_allow(int argc, char **argv); 104219089Spjdstatic int zfs_do_unallow(int argc, char **argv); 105219089Spjdstatic int zfs_do_hold(int argc, char **argv); 106219089Spjdstatic int zfs_do_holds(int argc, char **argv); 107219089Spjdstatic int zfs_do_release(int argc, char **argv); 108219089Spjdstatic int zfs_do_diff(int argc, char **argv); 109168404Spjdstatic int zfs_do_jail(int argc, char **argv); 110168404Spjdstatic int zfs_do_unjail(int argc, char **argv); 111263407Sdelphijstatic int zfs_do_bookmark(int argc, char **argv); 112168404Spjd 113168404Spjd/* 114185029Spjd * Enable a reasonable set of defaults for libumem debugging on DEBUG builds. 115168404Spjd */ 116185029Spjd 117185029Spjd#ifdef DEBUG 118168404Spjdconst char * 119168404Spjd_umem_debug_init(void) 120168404Spjd{ 121168404Spjd return ("default,verbose"); /* $UMEM_DEBUG setting */ 122168404Spjd} 123168404Spjd 124168404Spjdconst char * 125168404Spjd_umem_logging_init(void) 126168404Spjd{ 127168404Spjd return ("fail,contents"); /* $UMEM_LOGGING setting */ 128168404Spjd} 129185029Spjd#endif 130168404Spjd 131168404Spjdtypedef enum { 132168404Spjd HELP_CLONE, 133168404Spjd HELP_CREATE, 134168404Spjd HELP_DESTROY, 135168404Spjd HELP_GET, 136168404Spjd HELP_INHERIT, 137185029Spjd HELP_UPGRADE, 138168404Spjd HELP_JAIL, 139168404Spjd HELP_UNJAIL, 140168404Spjd HELP_LIST, 141168404Spjd HELP_MOUNT, 142168404Spjd HELP_PROMOTE, 143168404Spjd HELP_RECEIVE, 144168404Spjd HELP_RENAME, 145168404Spjd HELP_ROLLBACK, 146168404Spjd HELP_SEND, 147168404Spjd HELP_SET, 148168404Spjd HELP_SHARE, 149168404Spjd HELP_SNAPSHOT, 150168404Spjd HELP_UNMOUNT, 151185029Spjd HELP_UNSHARE, 152185029Spjd HELP_ALLOW, 153209962Smm HELP_UNALLOW, 154209962Smm HELP_USERSPACE, 155219089Spjd HELP_GROUPSPACE, 156219089Spjd HELP_HOLD, 157219089Spjd HELP_HOLDS, 158219089Spjd HELP_RELEASE, 159228103Smm HELP_DIFF, 160263407Sdelphij HELP_BOOKMARK, 161168404Spjd} zfs_help_t; 162168404Spjd 163168404Spjdtypedef struct zfs_command { 164168404Spjd const char *name; 165168404Spjd int (*func)(int argc, char **argv); 166168404Spjd zfs_help_t usage; 167168404Spjd} zfs_command_t; 168168404Spjd 169168404Spjd/* 170168404Spjd * Master command table. Each ZFS command has a name, associated function, and 171168404Spjd * usage message. The usage messages need to be internationalized, so we have 172168404Spjd * to have a function to return the usage message based on a command index. 173168404Spjd * 174168404Spjd * These commands are organized according to how they are displayed in the usage 175168404Spjd * message. An empty command (one with a NULL name) indicates an empty line in 176168404Spjd * the generic usage message. 177168404Spjd */ 178168404Spjdstatic zfs_command_t command_table[] = { 179168404Spjd { "create", zfs_do_create, HELP_CREATE }, 180168404Spjd { "destroy", zfs_do_destroy, HELP_DESTROY }, 181168404Spjd { NULL }, 182168404Spjd { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT }, 183168404Spjd { "rollback", zfs_do_rollback, HELP_ROLLBACK }, 184168404Spjd { "clone", zfs_do_clone, HELP_CLONE }, 185168404Spjd { "promote", zfs_do_promote, HELP_PROMOTE }, 186168404Spjd { "rename", zfs_do_rename, HELP_RENAME }, 187263407Sdelphij { "bookmark", zfs_do_bookmark, HELP_BOOKMARK }, 188168404Spjd { NULL }, 189168404Spjd { "list", zfs_do_list, HELP_LIST }, 190168404Spjd { NULL }, 191168404Spjd { "set", zfs_do_set, HELP_SET }, 192219089Spjd { "get", zfs_do_get, HELP_GET }, 193168404Spjd { "inherit", zfs_do_inherit, HELP_INHERIT }, 194185029Spjd { "upgrade", zfs_do_upgrade, HELP_UPGRADE }, 195209962Smm { "userspace", zfs_do_userspace, HELP_USERSPACE }, 196209962Smm { "groupspace", zfs_do_userspace, HELP_GROUPSPACE }, 197168404Spjd { NULL }, 198168404Spjd { "mount", zfs_do_mount, HELP_MOUNT }, 199168404Spjd { "unmount", zfs_do_unmount, HELP_UNMOUNT }, 200168404Spjd { "share", zfs_do_share, HELP_SHARE }, 201168404Spjd { "unshare", zfs_do_unshare, HELP_UNSHARE }, 202168404Spjd { NULL }, 203168404Spjd { "send", zfs_do_send, HELP_SEND }, 204168404Spjd { "receive", zfs_do_receive, HELP_RECEIVE }, 205168404Spjd { NULL }, 206219089Spjd { "allow", zfs_do_allow, HELP_ALLOW }, 207185029Spjd { NULL }, 208219089Spjd { "unallow", zfs_do_unallow, HELP_UNALLOW }, 209185029Spjd { NULL }, 210219089Spjd { "hold", zfs_do_hold, HELP_HOLD }, 211219089Spjd { "holds", zfs_do_holds, HELP_HOLDS }, 212219089Spjd { "release", zfs_do_release, HELP_RELEASE }, 213219089Spjd { "diff", zfs_do_diff, HELP_DIFF }, 214219089Spjd { NULL }, 215168404Spjd { "jail", zfs_do_jail, HELP_JAIL }, 216168404Spjd { "unjail", zfs_do_unjail, HELP_UNJAIL }, 217168404Spjd}; 218168404Spjd 219168404Spjd#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) 220168404Spjd 221168404Spjdzfs_command_t *current_command; 222168404Spjd 223168404Spjdstatic const char * 224168404Spjdget_usage(zfs_help_t idx) 225168404Spjd{ 226168404Spjd switch (idx) { 227168404Spjd case HELP_CLONE: 228185029Spjd return (gettext("\tclone [-p] [-o property=value] ... " 229185029Spjd "<snapshot> <filesystem|volume>\n")); 230168404Spjd case HELP_CREATE: 231234654Spjd return (gettext("\tcreate [-pu] [-o property=value] ... " 232168404Spjd "<filesystem>\n" 233185029Spjd "\tcreate [-ps] [-b blocksize] [-o property=value] ... " 234185029Spjd "-V <size> <volume>\n")); 235168404Spjd case HELP_DESTROY: 236228103Smm return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n" 237228103Smm "\tdestroy [-dnpRrv] " 238263407Sdelphij "<filesystem|volume>@<snap>[%<snap>][,...]\n" 239263407Sdelphij "\tdestroy <filesystem|volume>#<bookmark>\n")); 240168404Spjd case HELP_GET: 241205199Sdelphij return (gettext("\tget [-rHp] [-d max] " 242263407Sdelphij "[-o \"all\" | field[,...]]\n" 243263407Sdelphij "\t [-t type[,...]] [-s source[,...]]\n" 244185029Spjd "\t <\"all\" | property[,...]> " 245168404Spjd "[filesystem|volume|snapshot] ...\n")); 246168404Spjd case HELP_INHERIT: 247219089Spjd return (gettext("\tinherit [-rS] <property> " 248185029Spjd "<filesystem|volume|snapshot> ...\n")); 249185029Spjd case HELP_UPGRADE: 250185029Spjd return (gettext("\tupgrade [-v]\n" 251185029Spjd "\tupgrade [-r] [-V version] <-a | filesystem ...>\n")); 252168404Spjd case HELP_JAIL: 253240698Sbapt return (gettext("\tjail <jailid|jailname> <filesystem>\n")); 254168404Spjd case HELP_UNJAIL: 255240698Sbapt return (gettext("\tunjail <jailid|jailname> <filesystem>\n")); 256168404Spjd case HELP_LIST: 257263405Sdelphij return (gettext("\tlist [-Hp] [-r|-d max] [-o property[,...]] " 258263405Sdelphij "[-s property]...\n\t [-S property]... [-t type[,...]] " 259185029Spjd "[filesystem|volume|snapshot] ...\n")); 260168404Spjd case HELP_MOUNT: 261168404Spjd return (gettext("\tmount\n" 262185029Spjd "\tmount [-vO] [-o opts] <-a | filesystem>\n")); 263168404Spjd case HELP_PROMOTE: 264185029Spjd return (gettext("\tpromote <clone-filesystem>\n")); 265168404Spjd case HELP_RECEIVE: 266290756Smav return (gettext("\treceive|recv [-vnsFu] <filesystem|volume|" 267288571Smav "snapshot>\n" 268290756Smav "\treceive|recv [-vnsFu] [-o origin=<snapshot>] [-d | -e] " 269290756Smav "<filesystem>\n" 270290756Smav "\treceive|recv -A <filesystem|volume>\n")); 271168404Spjd case HELP_RENAME: 272235216Smm return (gettext("\trename [-f] <filesystem|volume|snapshot> " 273168676Spjd "<filesystem|volume|snapshot>\n" 274263407Sdelphij "\trename [-f] -p <filesystem|volume> <filesystem|volume>\n" 275226705Spjd "\trename -r <snapshot> <snapshot>\n" 276226705Spjd "\trename -u [-p] <filesystem> <filesystem>")); 277168404Spjd case HELP_ROLLBACK: 278168404Spjd return (gettext("\trollback [-rRf] <snapshot>\n")); 279168404Spjd case HELP_SEND: 280276081Sdelphij return (gettext("\tsend [-DnPpRvLe] [-[iI] snapshot] " 281263407Sdelphij "<snapshot>\n" 282276081Sdelphij "\tsend [-Le] [-i snapshot|bookmark] " 283290756Smav "<filesystem|volume|snapshot>\n" 284290756Smav "\tsend [-nvPe] -t <receive_resume_token>\n")); 285168404Spjd case HELP_SET: 286290758Smav return (gettext("\tset <property=value> ... " 287185029Spjd "<filesystem|volume|snapshot> ...\n")); 288168404Spjd case HELP_SHARE: 289185029Spjd return (gettext("\tshare <-a | filesystem>\n")); 290168404Spjd case HELP_SNAPSHOT: 291263403Sdelphij return (gettext("\tsnapshot|snap [-r] [-o property=value] ... " 292263407Sdelphij "<filesystem|volume>@<snap> ...\n")); 293168404Spjd case HELP_UNMOUNT: 294263403Sdelphij return (gettext("\tunmount|umount [-f] " 295185029Spjd "<-a | filesystem|mountpoint>\n")); 296168404Spjd case HELP_UNSHARE: 297219089Spjd return (gettext("\tunshare " 298185029Spjd "<-a | filesystem|mountpoint>\n")); 299185029Spjd case HELP_ALLOW: 300219089Spjd return (gettext("\tallow <filesystem|volume>\n" 301219089Spjd "\tallow [-ldug] " 302185029Spjd "<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n" 303185029Spjd "\t <filesystem|volume>\n" 304185029Spjd "\tallow [-ld] -e <perm|@setname>[,...] " 305185029Spjd "<filesystem|volume>\n" 306185029Spjd "\tallow -c <perm|@setname>[,...] <filesystem|volume>\n" 307185029Spjd "\tallow -s @setname <perm|@setname>[,...] " 308185029Spjd "<filesystem|volume>\n")); 309185029Spjd case HELP_UNALLOW: 310185029Spjd return (gettext("\tunallow [-rldug] " 311185029Spjd "<\"everyone\"|user|group>[,...]\n" 312185029Spjd "\t [<perm|@setname>[,...]] <filesystem|volume>\n" 313185029Spjd "\tunallow [-rld] -e [<perm|@setname>[,...]] " 314185029Spjd "<filesystem|volume>\n" 315185029Spjd "\tunallow [-r] -c [<perm|@setname>[,...]] " 316185029Spjd "<filesystem|volume>\n" 317185029Spjd "\tunallow [-r] -s @setname [<perm|@setname>[,...]] " 318185029Spjd "<filesystem|volume>\n")); 319209962Smm case HELP_USERSPACE: 320240415Smm return (gettext("\tuserspace [-Hinp] [-o field[,...]] " 321263407Sdelphij "[-s field] ...\n" 322263407Sdelphij "\t [-S field] ... [-t type[,...]] " 323263405Sdelphij "<filesystem|snapshot>\n")); 324209962Smm case HELP_GROUPSPACE: 325240415Smm return (gettext("\tgroupspace [-Hinp] [-o field[,...]] " 326263407Sdelphij "[-s field] ...\n" 327263407Sdelphij "\t [-S field] ... [-t type[,...]] " 328263405Sdelphij "<filesystem|snapshot>\n")); 329219089Spjd case HELP_HOLD: 330219089Spjd return (gettext("\thold [-r] <tag> <snapshot> ...\n")); 331219089Spjd case HELP_HOLDS: 332219089Spjd return (gettext("\tholds [-r] <snapshot> ...\n")); 333219089Spjd case HELP_RELEASE: 334219089Spjd return (gettext("\trelease [-r] <tag> <snapshot> ...\n")); 335219089Spjd case HELP_DIFF: 336219089Spjd return (gettext("\tdiff [-FHt] <snapshot> " 337219089Spjd "[snapshot|filesystem]\n")); 338263407Sdelphij case HELP_BOOKMARK: 339263407Sdelphij return (gettext("\tbookmark <snapshot> <bookmark>\n")); 340168404Spjd } 341168404Spjd 342168404Spjd abort(); 343168404Spjd /* NOTREACHED */ 344168404Spjd} 345168404Spjd 346219089Spjdvoid 347219089Spjdnomem(void) 348219089Spjd{ 349219089Spjd (void) fprintf(stderr, gettext("internal error: out of memory\n")); 350219089Spjd exit(1); 351219089Spjd} 352219089Spjd 353168404Spjd/* 354168404Spjd * Utility function to guarantee malloc() success. 355168404Spjd */ 356219089Spjd 357168404Spjdvoid * 358168404Spjdsafe_malloc(size_t size) 359168404Spjd{ 360168404Spjd void *data; 361168404Spjd 362219089Spjd if ((data = calloc(1, size)) == NULL) 363219089Spjd nomem(); 364168404Spjd 365168404Spjd return (data); 366168404Spjd} 367168404Spjd 368219089Spjdstatic char * 369219089Spjdsafe_strdup(char *str) 370219089Spjd{ 371219089Spjd char *dupstr = strdup(str); 372219089Spjd 373219089Spjd if (dupstr == NULL) 374219089Spjd nomem(); 375219089Spjd 376219089Spjd return (dupstr); 377219089Spjd} 378219089Spjd 379168404Spjd/* 380185029Spjd * Callback routine that will print out information for each of 381168404Spjd * the properties. 382168404Spjd */ 383185029Spjdstatic int 384185029Spjdusage_prop_cb(int prop, void *cb) 385168404Spjd{ 386168404Spjd FILE *fp = cb; 387168404Spjd 388185029Spjd (void) fprintf(fp, "\t%-15s ", zfs_prop_to_name(prop)); 389168404Spjd 390168404Spjd if (zfs_prop_readonly(prop)) 391185029Spjd (void) fprintf(fp, " NO "); 392168404Spjd else 393185029Spjd (void) fprintf(fp, "YES "); 394168404Spjd 395168404Spjd if (zfs_prop_inheritable(prop)) 396168404Spjd (void) fprintf(fp, " YES "); 397168404Spjd else 398168404Spjd (void) fprintf(fp, " NO "); 399168404Spjd 400168404Spjd if (zfs_prop_values(prop) == NULL) 401168404Spjd (void) fprintf(fp, "-\n"); 402168404Spjd else 403168404Spjd (void) fprintf(fp, "%s\n", zfs_prop_values(prop)); 404168404Spjd 405185029Spjd return (ZPROP_CONT); 406168404Spjd} 407168404Spjd 408168404Spjd/* 409168404Spjd * Display usage message. If we're inside a command, display only the usage for 410168404Spjd * that command. Otherwise, iterate over the entire command table and display 411168404Spjd * a complete usage message. 412168404Spjd */ 413168404Spjdstatic void 414168404Spjdusage(boolean_t requested) 415168404Spjd{ 416168404Spjd int i; 417168404Spjd boolean_t show_properties = B_FALSE; 418168404Spjd FILE *fp = requested ? stdout : stderr; 419168404Spjd 420168404Spjd if (current_command == NULL) { 421168404Spjd 422168404Spjd (void) fprintf(fp, gettext("usage: zfs command args ...\n")); 423168404Spjd (void) fprintf(fp, 424168404Spjd gettext("where 'command' is one of the following:\n\n")); 425168404Spjd 426168404Spjd for (i = 0; i < NCOMMAND; i++) { 427168404Spjd if (command_table[i].name == NULL) 428168404Spjd (void) fprintf(fp, "\n"); 429168404Spjd else 430168404Spjd (void) fprintf(fp, "%s", 431168404Spjd get_usage(command_table[i].usage)); 432168404Spjd } 433168404Spjd 434168404Spjd (void) fprintf(fp, gettext("\nEach dataset is of the form: " 435168404Spjd "pool/[dataset/]*dataset[@name]\n")); 436168404Spjd } else { 437168404Spjd (void) fprintf(fp, gettext("usage:\n")); 438168404Spjd (void) fprintf(fp, "%s", get_usage(current_command->usage)); 439168404Spjd } 440168404Spjd 441168404Spjd if (current_command != NULL && 442168404Spjd (strcmp(current_command->name, "set") == 0 || 443168404Spjd strcmp(current_command->name, "get") == 0 || 444168404Spjd strcmp(current_command->name, "inherit") == 0 || 445168404Spjd strcmp(current_command->name, "list") == 0)) 446168404Spjd show_properties = B_TRUE; 447168404Spjd 448168404Spjd if (show_properties) { 449168404Spjd (void) fprintf(fp, 450168404Spjd gettext("\nThe following properties are supported:\n")); 451168404Spjd 452185029Spjd (void) fprintf(fp, "\n\t%-14s %s %s %s\n\n", 453168404Spjd "PROPERTY", "EDIT", "INHERIT", "VALUES"); 454168404Spjd 455168404Spjd /* Iterate over all properties */ 456185029Spjd (void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE, 457185029Spjd ZFS_TYPE_DATASET); 458168404Spjd 459209962Smm (void) fprintf(fp, "\t%-15s ", "userused@..."); 460209962Smm (void) fprintf(fp, " NO NO <size>\n"); 461209962Smm (void) fprintf(fp, "\t%-15s ", "groupused@..."); 462209962Smm (void) fprintf(fp, " NO NO <size>\n"); 463209962Smm (void) fprintf(fp, "\t%-15s ", "userquota@..."); 464209962Smm (void) fprintf(fp, "YES NO <size> | none\n"); 465209962Smm (void) fprintf(fp, "\t%-15s ", "groupquota@..."); 466209962Smm (void) fprintf(fp, "YES NO <size> | none\n"); 467228103Smm (void) fprintf(fp, "\t%-15s ", "written@<snap>"); 468228103Smm (void) fprintf(fp, " NO NO <size>\n"); 469209962Smm 470168404Spjd (void) fprintf(fp, gettext("\nSizes are specified in bytes " 471168404Spjd "with standard units such as K, M, G, etc.\n")); 472185029Spjd (void) fprintf(fp, gettext("\nUser-defined properties can " 473168404Spjd "be specified by using a name containing a colon (:).\n")); 474209962Smm (void) fprintf(fp, gettext("\nThe {user|group}{used|quota}@ " 475209962Smm "properties must be appended with\n" 476209962Smm "a user or group specifier of one of these forms:\n" 477209962Smm " POSIX name (eg: \"matt\")\n" 478209962Smm " POSIX id (eg: \"126829\")\n" 479209962Smm " SMB name@domain (eg: \"matt@sun\")\n" 480209962Smm " SMB SID (eg: \"S-1-234-567-89\")\n")); 481168404Spjd } else { 482168404Spjd (void) fprintf(fp, 483209962Smm gettext("\nFor the property list, run: %s\n"), 484209962Smm "zfs set|get"); 485185029Spjd (void) fprintf(fp, 486209962Smm gettext("\nFor the delegated permission list, run: %s\n"), 487209962Smm "zfs allow|unallow"); 488168404Spjd } 489168404Spjd 490168404Spjd /* 491168404Spjd * See comments at end of main(). 492168404Spjd */ 493168404Spjd if (getenv("ZFS_ABORT") != NULL) { 494168404Spjd (void) printf("dumping core by request\n"); 495168404Spjd abort(); 496168404Spjd } 497168404Spjd 498168404Spjd exit(requested ? 0 : 2); 499168404Spjd} 500168404Spjd 501290758Smav/* 502290758Smav * Take a property=value argument string and add it to the given nvlist. 503290758Smav * Modifies the argument inplace. 504290758Smav */ 505185029Spjdstatic int 506265650Ssmhparseprop(nvlist_t *props, char *propname) 507185029Spjd{ 508185029Spjd char *propval, *strval; 509185029Spjd 510185029Spjd if ((propval = strchr(propname, '=')) == NULL) { 511185029Spjd (void) fprintf(stderr, gettext("missing " 512290758Smav "'=' for property=value argument\n")); 513185029Spjd return (-1); 514185029Spjd } 515185029Spjd *propval = '\0'; 516185029Spjd propval++; 517185029Spjd if (nvlist_lookup_string(props, propname, &strval) == 0) { 518185029Spjd (void) fprintf(stderr, gettext("property '%s' " 519185029Spjd "specified multiple times\n"), propname); 520185029Spjd return (-1); 521185029Spjd } 522219089Spjd if (nvlist_add_string(props, propname, propval) != 0) 523219089Spjd nomem(); 524185029Spjd return (0); 525185029Spjd} 526185029Spjd 527205199Sdelphijstatic int 528205199Sdelphijparse_depth(char *opt, int *flags) 529205199Sdelphij{ 530205199Sdelphij char *tmp; 531205199Sdelphij int depth; 532205199Sdelphij 533205199Sdelphij depth = (int)strtol(opt, &tmp, 0); 534205199Sdelphij if (*tmp) { 535205199Sdelphij (void) fprintf(stderr, 536265650Ssmh gettext("%s is not an integer\n"), opt); 537205199Sdelphij usage(B_FALSE); 538205199Sdelphij } 539205199Sdelphij if (depth < 0) { 540205199Sdelphij (void) fprintf(stderr, 541205199Sdelphij gettext("Depth can not be negative.\n")); 542205199Sdelphij usage(B_FALSE); 543205199Sdelphij } 544205199Sdelphij *flags |= (ZFS_ITER_DEPTH_LIMIT|ZFS_ITER_RECURSE); 545205199Sdelphij return (depth); 546205199Sdelphij} 547205199Sdelphij 548219089Spjd#define PROGRESS_DELAY 2 /* seconds */ 549219089Spjd 550219089Spjdstatic 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"; 551219089Spjdstatic time_t pt_begin; 552219089Spjdstatic char *pt_header = NULL; 553219089Spjdstatic boolean_t pt_shown; 554219089Spjd 555219089Spjdstatic void 556219089Spjdstart_progress_timer(void) 557219089Spjd{ 558219089Spjd pt_begin = time(NULL) + PROGRESS_DELAY; 559219089Spjd pt_shown = B_FALSE; 560219089Spjd} 561219089Spjd 562219089Spjdstatic void 563219089Spjdset_progress_header(char *header) 564219089Spjd{ 565219089Spjd assert(pt_header == NULL); 566219089Spjd pt_header = safe_strdup(header); 567219089Spjd if (pt_shown) { 568219089Spjd (void) printf("%s: ", header); 569219089Spjd (void) fflush(stdout); 570219089Spjd } 571219089Spjd} 572219089Spjd 573219089Spjdstatic void 574219089Spjdupdate_progress(char *update) 575219089Spjd{ 576219089Spjd if (!pt_shown && time(NULL) > pt_begin) { 577219089Spjd int len = strlen(update); 578219089Spjd 579219089Spjd (void) printf("%s: %s%*.*s", pt_header, update, len, len, 580219089Spjd pt_reverse); 581219089Spjd (void) fflush(stdout); 582219089Spjd pt_shown = B_TRUE; 583219089Spjd } else if (pt_shown) { 584219089Spjd int len = strlen(update); 585219089Spjd 586219089Spjd (void) printf("%s%*.*s", update, len, len, pt_reverse); 587219089Spjd (void) fflush(stdout); 588219089Spjd } 589219089Spjd} 590219089Spjd 591219089Spjdstatic void 592219089Spjdfinish_progress(char *done) 593219089Spjd{ 594219089Spjd if (pt_shown) { 595219089Spjd (void) printf("%s\n", done); 596219089Spjd (void) fflush(stdout); 597219089Spjd } 598219089Spjd free(pt_header); 599219089Spjd pt_header = NULL; 600219089Spjd} 601268649Sdelphij 602168404Spjd/* 603284754Savg * Check if the dataset is mountable and should be automatically mounted. 604284754Savg */ 605284754Savgstatic boolean_t 606284754Savgshould_auto_mount(zfs_handle_t *zhp) 607284754Savg{ 608284754Savg if (!zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, zfs_get_type(zhp))) 609284754Savg return (B_FALSE); 610284754Savg return (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON); 611284754Savg} 612284754Savg 613284754Savg/* 614185029Spjd * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol> 615168404Spjd * 616168404Spjd * Given an existing dataset, create a writable copy whose initial contents 617168404Spjd * are the same as the source. The newly created dataset maintains a 618168404Spjd * dependency on the original; the original cannot be destroyed so long as 619168404Spjd * the clone exists. 620185029Spjd * 621185029Spjd * The '-p' flag creates all the non-existing ancestors of the target first. 622168404Spjd */ 623168404Spjdstatic int 624168404Spjdzfs_do_clone(int argc, char **argv) 625168404Spjd{ 626185029Spjd zfs_handle_t *zhp = NULL; 627185029Spjd boolean_t parents = B_FALSE; 628185029Spjd nvlist_t *props; 629231144Smm int ret = 0; 630185029Spjd int c; 631168404Spjd 632219089Spjd if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 633219089Spjd nomem(); 634185029Spjd 635168404Spjd /* check options */ 636185029Spjd while ((c = getopt(argc, argv, "o:p")) != -1) { 637185029Spjd switch (c) { 638185029Spjd case 'o': 639290758Smav if (parseprop(props, optarg) != 0) 640185029Spjd return (1); 641185029Spjd break; 642185029Spjd case 'p': 643185029Spjd parents = B_TRUE; 644185029Spjd break; 645185029Spjd case '?': 646185029Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 647185029Spjd optopt); 648185029Spjd goto usage; 649185029Spjd } 650168404Spjd } 651168404Spjd 652185029Spjd argc -= optind; 653185029Spjd argv += optind; 654185029Spjd 655168404Spjd /* check number of arguments */ 656185029Spjd if (argc < 1) { 657168404Spjd (void) fprintf(stderr, gettext("missing source dataset " 658168404Spjd "argument\n")); 659185029Spjd goto usage; 660168404Spjd } 661185029Spjd if (argc < 2) { 662168404Spjd (void) fprintf(stderr, gettext("missing target dataset " 663168404Spjd "argument\n")); 664185029Spjd goto usage; 665168404Spjd } 666185029Spjd if (argc > 2) { 667168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 668185029Spjd goto usage; 669168404Spjd } 670168404Spjd 671168404Spjd /* open the source dataset */ 672185029Spjd if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 673168404Spjd return (1); 674168404Spjd 675185029Spjd if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM | 676185029Spjd ZFS_TYPE_VOLUME)) { 677185029Spjd /* 678185029Spjd * Now create the ancestors of the target dataset. If the 679185029Spjd * target already exists and '-p' option was used we should not 680185029Spjd * complain. 681185029Spjd */ 682185029Spjd if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | 683185029Spjd ZFS_TYPE_VOLUME)) 684185029Spjd return (0); 685185029Spjd if (zfs_create_ancestors(g_zfs, argv[1]) != 0) 686185029Spjd return (1); 687185029Spjd } 688185029Spjd 689168404Spjd /* pass to libzfs */ 690185029Spjd ret = zfs_clone(zhp, argv[1], props); 691168404Spjd 692168404Spjd /* create the mountpoint if necessary */ 693168404Spjd if (ret == 0) { 694185029Spjd zfs_handle_t *clone; 695185029Spjd 696185029Spjd clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_DATASET); 697168404Spjd if (clone != NULL) { 698284754Savg /* 699284754Savg * If the user doesn't want the dataset 700284754Savg * automatically mounted, then skip the mount/share 701284754Savg * step. 702284754Savg */ 703284754Savg if (should_auto_mount(clone)) { 704284754Savg if ((ret = zfs_mount(clone, NULL, 0)) != 0) { 705284754Savg (void) fprintf(stderr, gettext("clone " 706284754Savg "successfully created, " 707284754Savg "but not mounted\n")); 708284754Savg } else if ((ret = zfs_share(clone)) != 0) { 709284754Savg (void) fprintf(stderr, gettext("clone " 710284754Savg "successfully created, " 711284754Savg "but not shared\n")); 712284754Savg } 713284754Savg } 714168404Spjd zfs_close(clone); 715168404Spjd } 716168404Spjd } 717168404Spjd 718168404Spjd zfs_close(zhp); 719185029Spjd nvlist_free(props); 720168404Spjd 721185029Spjd return (!!ret); 722185029Spjd 723185029Spjdusage: 724185029Spjd if (zhp) 725185029Spjd zfs_close(zhp); 726185029Spjd nvlist_free(props); 727185029Spjd usage(B_FALSE); 728185029Spjd return (-1); 729168404Spjd} 730168404Spjd 731168404Spjd/* 732234654Spjd * zfs create [-pu] [-o prop=value] ... fs 733185029Spjd * zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size 734168404Spjd * 735168404Spjd * Create a new dataset. This command can be used to create filesystems 736168404Spjd * and volumes. Snapshot creation is handled by 'zfs snapshot'. 737168404Spjd * For volumes, the user must specify a size to be used. 738168404Spjd * 739168404Spjd * The '-s' flag applies only to volumes, and indicates that we should not try 740168404Spjd * to set the reservation for this volume. By default we set a reservation 741185029Spjd * equal to the size for any volume. For pools with SPA_VERSION >= 742185029Spjd * SPA_VERSION_REFRESERVATION, we set a refreservation instead. 743185029Spjd * 744185029Spjd * The '-p' flag creates all the non-existing ancestors of the target first. 745234654Spjd * 746234654Spjd * The '-u' flag prevents mounting of newly created file system. 747168404Spjd */ 748168404Spjdstatic int 749168404Spjdzfs_do_create(int argc, char **argv) 750168404Spjd{ 751168404Spjd zfs_type_t type = ZFS_TYPE_FILESYSTEM; 752168404Spjd zfs_handle_t *zhp = NULL; 753168404Spjd uint64_t volsize; 754168404Spjd int c; 755168404Spjd boolean_t noreserve = B_FALSE; 756185029Spjd boolean_t bflag = B_FALSE; 757185029Spjd boolean_t parents = B_FALSE; 758234654Spjd boolean_t nomount = B_FALSE; 759168404Spjd int ret = 1; 760185029Spjd nvlist_t *props; 761168404Spjd uint64_t intval; 762168404Spjd 763219089Spjd if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 764219089Spjd nomem(); 765168404Spjd 766168404Spjd /* check options */ 767234654Spjd while ((c = getopt(argc, argv, ":V:b:so:pu")) != -1) { 768168404Spjd switch (c) { 769168404Spjd case 'V': 770168404Spjd type = ZFS_TYPE_VOLUME; 771168404Spjd if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 772168404Spjd (void) fprintf(stderr, gettext("bad volume " 773168404Spjd "size '%s': %s\n"), optarg, 774168404Spjd libzfs_error_description(g_zfs)); 775168404Spjd goto error; 776168404Spjd } 777168404Spjd 778168404Spjd if (nvlist_add_uint64(props, 779219089Spjd zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0) 780219089Spjd nomem(); 781168404Spjd volsize = intval; 782168404Spjd break; 783185029Spjd case 'p': 784185029Spjd parents = B_TRUE; 785185029Spjd break; 786168404Spjd case 'b': 787185029Spjd bflag = B_TRUE; 788168404Spjd if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 789168404Spjd (void) fprintf(stderr, gettext("bad volume " 790168404Spjd "block size '%s': %s\n"), optarg, 791168404Spjd libzfs_error_description(g_zfs)); 792168404Spjd goto error; 793168404Spjd } 794168404Spjd 795168404Spjd if (nvlist_add_uint64(props, 796168404Spjd zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 797219089Spjd intval) != 0) 798219089Spjd nomem(); 799168404Spjd break; 800168404Spjd case 'o': 801288571Smav if (parseprop(props, optarg) != 0) 802168404Spjd goto error; 803168404Spjd break; 804168404Spjd case 's': 805168404Spjd noreserve = B_TRUE; 806168404Spjd break; 807234654Spjd case 'u': 808234654Spjd nomount = B_TRUE; 809234654Spjd break; 810168404Spjd case ':': 811168404Spjd (void) fprintf(stderr, gettext("missing size " 812168404Spjd "argument\n")); 813168404Spjd goto badusage; 814168404Spjd case '?': 815168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 816168404Spjd optopt); 817168404Spjd goto badusage; 818168404Spjd } 819168404Spjd } 820168404Spjd 821185029Spjd if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) { 822185029Spjd (void) fprintf(stderr, gettext("'-s' and '-b' can only be " 823185029Spjd "used when creating a volume\n")); 824168404Spjd goto badusage; 825168404Spjd } 826234654Spjd if (nomount && type != ZFS_TYPE_FILESYSTEM) { 827234654Spjd (void) fprintf(stderr, gettext("'-u' can only be " 828234654Spjd "used when creating a file system\n")); 829234654Spjd goto badusage; 830234654Spjd } 831168404Spjd 832168404Spjd argc -= optind; 833168404Spjd argv += optind; 834168404Spjd 835168404Spjd /* check number of arguments */ 836168404Spjd if (argc == 0) { 837168404Spjd (void) fprintf(stderr, gettext("missing %s argument\n"), 838168404Spjd zfs_type_to_name(type)); 839168404Spjd goto badusage; 840168404Spjd } 841168404Spjd if (argc > 1) { 842168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 843168404Spjd goto badusage; 844168404Spjd } 845168404Spjd 846185029Spjd if (type == ZFS_TYPE_VOLUME && !noreserve) { 847185029Spjd zpool_handle_t *zpool_handle; 848290759Smav nvlist_t *real_props; 849185029Spjd uint64_t spa_version; 850185029Spjd char *p; 851185029Spjd zfs_prop_t resv_prop; 852185029Spjd char *strval; 853290759Smav char msg[1024]; 854185029Spjd 855185029Spjd if (p = strchr(argv[0], '/')) 856185029Spjd *p = '\0'; 857185029Spjd zpool_handle = zpool_open(g_zfs, argv[0]); 858185029Spjd if (p != NULL) 859185029Spjd *p = '/'; 860185029Spjd if (zpool_handle == NULL) 861185029Spjd goto error; 862185029Spjd spa_version = zpool_get_prop_int(zpool_handle, 863185029Spjd ZPOOL_PROP_VERSION, NULL); 864185029Spjd if (spa_version >= SPA_VERSION_REFRESERVATION) 865185029Spjd resv_prop = ZFS_PROP_REFRESERVATION; 866185029Spjd else 867185029Spjd resv_prop = ZFS_PROP_RESERVATION; 868185029Spjd 869290759Smav (void) snprintf(msg, sizeof (msg), 870290759Smav gettext("cannot create '%s'"), argv[0]); 871290759Smav if (props && (real_props = zfs_valid_proplist(g_zfs, type, 872290760Smav props, 0, NULL, zpool_handle, msg)) == NULL) { 873290760Smav zpool_close(zpool_handle); 874290759Smav goto error; 875290760Smav } 876290760Smav zpool_close(zpool_handle); 877290759Smav 878290759Smav volsize = zvol_volsize_to_reservation(volsize, real_props); 879290759Smav nvlist_free(real_props); 880290759Smav 881185029Spjd if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop), 882185029Spjd &strval) != 0) { 883185029Spjd if (nvlist_add_uint64(props, 884185029Spjd zfs_prop_to_name(resv_prop), volsize) != 0) { 885185029Spjd nvlist_free(props); 886219089Spjd nomem(); 887185029Spjd } 888168404Spjd } 889168404Spjd } 890168404Spjd 891185029Spjd if (parents && zfs_name_valid(argv[0], type)) { 892185029Spjd /* 893185029Spjd * Now create the ancestors of target dataset. If the target 894185029Spjd * already exists and '-p' option was used we should not 895185029Spjd * complain. 896185029Spjd */ 897185029Spjd if (zfs_dataset_exists(g_zfs, argv[0], type)) { 898185029Spjd ret = 0; 899185029Spjd goto error; 900185029Spjd } 901185029Spjd if (zfs_create_ancestors(g_zfs, argv[0]) != 0) 902185029Spjd goto error; 903185029Spjd } 904185029Spjd 905168404Spjd /* pass to libzfs */ 906168404Spjd if (zfs_create(g_zfs, argv[0], type, props) != 0) 907168404Spjd goto error; 908168404Spjd 909185029Spjd if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL) 910168404Spjd goto error; 911219089Spjd 912219089Spjd ret = 0; 913168404Spjd 914168404Spjd /* 915168404Spjd * Mount and/or share the new filesystem as appropriate. We provide a 916168404Spjd * verbose error message to let the user know that their filesystem was 917168404Spjd * in fact created, even if we failed to mount or share it. 918284754Savg * If the user doesn't want the dataset automatically mounted, 919284754Savg * then skip the mount/share step altogether. 920168404Spjd */ 921284754Savg if (!nomount && should_auto_mount(zhp)) { 922185029Spjd if (zfs_mount(zhp, NULL, 0) != 0) { 923185029Spjd (void) fprintf(stderr, gettext("filesystem " 924185029Spjd "successfully created, but not mounted\n")); 925185029Spjd ret = 1; 926185029Spjd } else if (zfs_share(zhp) != 0) { 927185029Spjd (void) fprintf(stderr, gettext("filesystem " 928185029Spjd "successfully created, but not shared\n")); 929185029Spjd ret = 1; 930185029Spjd } 931168404Spjd } 932168404Spjd 933168404Spjderror: 934168404Spjd if (zhp) 935168404Spjd zfs_close(zhp); 936168404Spjd nvlist_free(props); 937168404Spjd return (ret); 938168404Spjdbadusage: 939168404Spjd nvlist_free(props); 940168404Spjd usage(B_FALSE); 941168404Spjd return (2); 942168404Spjd} 943168404Spjd 944168404Spjd/* 945219089Spjd * zfs destroy [-rRf] <fs, vol> 946219089Spjd * zfs destroy [-rRd] <snap> 947168404Spjd * 948219089Spjd * -r Recursively destroy all children 949219089Spjd * -R Recursively destroy all dependents, including clones 950219089Spjd * -f Force unmounting of any dependents 951219089Spjd * -d If we can't destroy now, mark for deferred destruction 952168404Spjd * 953168404Spjd * Destroys the given dataset. By default, it will unmount any filesystems, 954168404Spjd * and refuse to destroy a dataset that has any dependents. A dependent can 955168404Spjd * either be a child, or a clone of a child. 956168404Spjd */ 957168404Spjdtypedef struct destroy_cbdata { 958168404Spjd boolean_t cb_first; 959228103Smm boolean_t cb_force; 960228103Smm boolean_t cb_recurse; 961228103Smm boolean_t cb_error; 962228103Smm boolean_t cb_doclones; 963168404Spjd zfs_handle_t *cb_target; 964219089Spjd boolean_t cb_defer_destroy; 965228103Smm boolean_t cb_verbose; 966228103Smm boolean_t cb_parsable; 967228103Smm boolean_t cb_dryrun; 968228103Smm nvlist_t *cb_nvl; 969248571Smm nvlist_t *cb_batchedsnaps; 970228103Smm 971228103Smm /* first snap in contiguous run */ 972248571Smm char *cb_firstsnap; 973228103Smm /* previous snap in contiguous run */ 974248571Smm char *cb_prevsnap; 975228103Smm int64_t cb_snapused; 976228103Smm char *cb_snapspec; 977263407Sdelphij char *cb_bookmark; 978168404Spjd} destroy_cbdata_t; 979168404Spjd 980168404Spjd/* 981168404Spjd * Check for any dependents based on the '-r' or '-R' flags. 982168404Spjd */ 983168404Spjdstatic int 984168404Spjddestroy_check_dependent(zfs_handle_t *zhp, void *data) 985168404Spjd{ 986168404Spjd destroy_cbdata_t *cbp = data; 987168404Spjd const char *tname = zfs_get_name(cbp->cb_target); 988168404Spjd const char *name = zfs_get_name(zhp); 989168404Spjd 990168404Spjd if (strncmp(tname, name, strlen(tname)) == 0 && 991168404Spjd (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { 992168404Spjd /* 993168404Spjd * This is a direct descendant, not a clone somewhere else in 994168404Spjd * the hierarchy. 995168404Spjd */ 996168404Spjd if (cbp->cb_recurse) 997168404Spjd goto out; 998168404Spjd 999168404Spjd if (cbp->cb_first) { 1000168404Spjd (void) fprintf(stderr, gettext("cannot destroy '%s': " 1001168404Spjd "%s has children\n"), 1002168404Spjd zfs_get_name(cbp->cb_target), 1003168404Spjd zfs_type_to_name(zfs_get_type(cbp->cb_target))); 1004168404Spjd (void) fprintf(stderr, gettext("use '-r' to destroy " 1005168404Spjd "the following datasets:\n")); 1006168404Spjd cbp->cb_first = B_FALSE; 1007228103Smm cbp->cb_error = B_TRUE; 1008168404Spjd } 1009168404Spjd 1010168404Spjd (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1011168404Spjd } else { 1012168404Spjd /* 1013168404Spjd * This is a clone. We only want to report this if the '-r' 1014168404Spjd * wasn't specified, or the target is a snapshot. 1015168404Spjd */ 1016168404Spjd if (!cbp->cb_recurse && 1017168404Spjd zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) 1018168404Spjd goto out; 1019168404Spjd 1020168404Spjd if (cbp->cb_first) { 1021168404Spjd (void) fprintf(stderr, gettext("cannot destroy '%s': " 1022168404Spjd "%s has dependent clones\n"), 1023168404Spjd zfs_get_name(cbp->cb_target), 1024168404Spjd zfs_type_to_name(zfs_get_type(cbp->cb_target))); 1025168404Spjd (void) fprintf(stderr, gettext("use '-R' to destroy " 1026168404Spjd "the following datasets:\n")); 1027168404Spjd cbp->cb_first = B_FALSE; 1028228103Smm cbp->cb_error = B_TRUE; 1029228103Smm cbp->cb_dryrun = B_TRUE; 1030168404Spjd } 1031168404Spjd 1032168404Spjd (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1033168404Spjd } 1034168404Spjd 1035168404Spjdout: 1036168404Spjd zfs_close(zhp); 1037168404Spjd return (0); 1038168404Spjd} 1039168404Spjd 1040168404Spjdstatic int 1041168404Spjddestroy_callback(zfs_handle_t *zhp, void *data) 1042168404Spjd{ 1043228103Smm destroy_cbdata_t *cb = data; 1044228103Smm const char *name = zfs_get_name(zhp); 1045168404Spjd 1046228103Smm if (cb->cb_verbose) { 1047228103Smm if (cb->cb_parsable) { 1048228103Smm (void) printf("destroy\t%s\n", name); 1049228103Smm } else if (cb->cb_dryrun) { 1050228103Smm (void) printf(gettext("would destroy %s\n"), 1051228103Smm name); 1052228103Smm } else { 1053228103Smm (void) printf(gettext("will destroy %s\n"), 1054228103Smm name); 1055228103Smm } 1056228103Smm } 1057228103Smm 1058168404Spjd /* 1059168404Spjd * Ignore pools (which we've already flagged as an error before getting 1060219089Spjd * here). 1061168404Spjd */ 1062168404Spjd if (strchr(zfs_get_name(zhp), '/') == NULL && 1063168404Spjd zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 1064168404Spjd zfs_close(zhp); 1065168404Spjd return (0); 1066168404Spjd } 1067248571Smm if (cb->cb_dryrun) { 1068248571Smm zfs_close(zhp); 1069248571Smm return (0); 1070248571Smm } 1071168404Spjd 1072248571Smm /* 1073248571Smm * We batch up all contiguous snapshots (even of different 1074248571Smm * filesystems) and destroy them with one ioctl. We can't 1075248571Smm * simply do all snap deletions and then all fs deletions, 1076248571Smm * because we must delete a clone before its origin. 1077248571Smm */ 1078248571Smm if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { 1079248571Smm fnvlist_add_boolean(cb->cb_batchedsnaps, name); 1080248571Smm } else { 1081248571Smm int error = zfs_destroy_snaps_nvl(g_zfs, 1082248571Smm cb->cb_batchedsnaps, B_FALSE); 1083248571Smm fnvlist_free(cb->cb_batchedsnaps); 1084248571Smm cb->cb_batchedsnaps = fnvlist_alloc(); 1085248571Smm 1086248571Smm if (error != 0 || 1087248571Smm zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 || 1088228103Smm zfs_destroy(zhp, cb->cb_defer_destroy) != 0) { 1089228103Smm zfs_close(zhp); 1090228103Smm return (-1); 1091228103Smm } 1092168404Spjd } 1093168404Spjd 1094168404Spjd zfs_close(zhp); 1095168404Spjd return (0); 1096168404Spjd} 1097168404Spjd 1098168404Spjdstatic int 1099228103Smmdestroy_print_cb(zfs_handle_t *zhp, void *arg) 1100168404Spjd{ 1101228103Smm destroy_cbdata_t *cb = arg; 1102228103Smm const char *name = zfs_get_name(zhp); 1103228103Smm int err = 0; 1104168404Spjd 1105228103Smm if (nvlist_exists(cb->cb_nvl, name)) { 1106228103Smm if (cb->cb_firstsnap == NULL) 1107248571Smm cb->cb_firstsnap = strdup(name); 1108228103Smm if (cb->cb_prevsnap != NULL) 1109248571Smm free(cb->cb_prevsnap); 1110228103Smm /* this snap continues the current range */ 1111248571Smm cb->cb_prevsnap = strdup(name); 1112248571Smm if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL) 1113248571Smm nomem(); 1114228103Smm if (cb->cb_verbose) { 1115228103Smm if (cb->cb_parsable) { 1116228103Smm (void) printf("destroy\t%s\n", name); 1117228103Smm } else if (cb->cb_dryrun) { 1118228103Smm (void) printf(gettext("would destroy %s\n"), 1119228103Smm name); 1120228103Smm } else { 1121228103Smm (void) printf(gettext("will destroy %s\n"), 1122228103Smm name); 1123228103Smm } 1124228103Smm } 1125228103Smm } else if (cb->cb_firstsnap != NULL) { 1126228103Smm /* end of this range */ 1127228103Smm uint64_t used = 0; 1128248571Smm err = lzc_snaprange_space(cb->cb_firstsnap, 1129228103Smm cb->cb_prevsnap, &used); 1130228103Smm cb->cb_snapused += used; 1131248571Smm free(cb->cb_firstsnap); 1132228103Smm cb->cb_firstsnap = NULL; 1133248571Smm free(cb->cb_prevsnap); 1134228103Smm cb->cb_prevsnap = NULL; 1135228103Smm } 1136228103Smm zfs_close(zhp); 1137228103Smm return (err); 1138228103Smm} 1139168404Spjd 1140228103Smmstatic int 1141228103Smmdestroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb) 1142228103Smm{ 1143231144Smm int err = 0; 1144228103Smm assert(cb->cb_firstsnap == NULL); 1145228103Smm assert(cb->cb_prevsnap == NULL); 1146228103Smm err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb); 1147228103Smm if (cb->cb_firstsnap != NULL) { 1148228103Smm uint64_t used = 0; 1149228103Smm if (err == 0) { 1150248571Smm err = lzc_snaprange_space(cb->cb_firstsnap, 1151228103Smm cb->cb_prevsnap, &used); 1152168404Spjd } 1153228103Smm cb->cb_snapused += used; 1154248571Smm free(cb->cb_firstsnap); 1155228103Smm cb->cb_firstsnap = NULL; 1156248571Smm free(cb->cb_prevsnap); 1157228103Smm cb->cb_prevsnap = NULL; 1158168404Spjd } 1159228103Smm return (err); 1160228103Smm} 1161168404Spjd 1162228103Smmstatic int 1163228103Smmsnapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg) 1164228103Smm{ 1165228103Smm destroy_cbdata_t *cb = arg; 1166228103Smm int err = 0; 1167228103Smm 1168228103Smm /* Check for clones. */ 1169238422Smm if (!cb->cb_doclones && !cb->cb_defer_destroy) { 1170228103Smm cb->cb_target = zhp; 1171228103Smm cb->cb_first = B_TRUE; 1172228103Smm err = zfs_iter_dependents(zhp, B_TRUE, 1173228103Smm destroy_check_dependent, cb); 1174228103Smm } 1175228103Smm 1176228103Smm if (err == 0) { 1177228103Smm if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp))) 1178228103Smm nomem(); 1179228103Smm } 1180228103Smm zfs_close(zhp); 1181228103Smm return (err); 1182168404Spjd} 1183168404Spjd 1184168404Spjdstatic int 1185228103Smmgather_snapshots(zfs_handle_t *zhp, void *arg) 1186228103Smm{ 1187228103Smm destroy_cbdata_t *cb = arg; 1188228103Smm int err = 0; 1189228103Smm 1190228103Smm err = zfs_iter_snapspec(zhp, cb->cb_snapspec, snapshot_to_nvl_cb, cb); 1191228103Smm if (err == ENOENT) 1192228103Smm err = 0; 1193228103Smm if (err != 0) 1194228103Smm goto out; 1195228103Smm 1196228103Smm if (cb->cb_verbose) { 1197228103Smm err = destroy_print_snapshots(zhp, cb); 1198228103Smm if (err != 0) 1199228103Smm goto out; 1200228103Smm } 1201228103Smm 1202228103Smm if (cb->cb_recurse) 1203228103Smm err = zfs_iter_filesystems(zhp, gather_snapshots, cb); 1204228103Smm 1205228103Smmout: 1206228103Smm zfs_close(zhp); 1207228103Smm return (err); 1208228103Smm} 1209228103Smm 1210228103Smmstatic int 1211228103Smmdestroy_clones(destroy_cbdata_t *cb) 1212228103Smm{ 1213228103Smm nvpair_t *pair; 1214228103Smm for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL); 1215228103Smm pair != NULL; 1216228103Smm pair = nvlist_next_nvpair(cb->cb_nvl, pair)) { 1217228103Smm zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair), 1218228103Smm ZFS_TYPE_SNAPSHOT); 1219228103Smm if (zhp != NULL) { 1220228103Smm boolean_t defer = cb->cb_defer_destroy; 1221231144Smm int err = 0; 1222228103Smm 1223228103Smm /* 1224228103Smm * We can't defer destroy non-snapshots, so set it to 1225228103Smm * false while destroying the clones. 1226228103Smm */ 1227228103Smm cb->cb_defer_destroy = B_FALSE; 1228228103Smm err = zfs_iter_dependents(zhp, B_FALSE, 1229228103Smm destroy_callback, cb); 1230228103Smm cb->cb_defer_destroy = defer; 1231228103Smm zfs_close(zhp); 1232228103Smm if (err != 0) 1233228103Smm return (err); 1234228103Smm } 1235228103Smm } 1236228103Smm return (0); 1237228103Smm} 1238228103Smm 1239228103Smmstatic int 1240168404Spjdzfs_do_destroy(int argc, char **argv) 1241168404Spjd{ 1242168404Spjd destroy_cbdata_t cb = { 0 }; 1243248571Smm int rv = 0; 1244248571Smm int err = 0; 1245168404Spjd int c; 1246248571Smm zfs_handle_t *zhp = NULL; 1247263407Sdelphij char *at, *pound; 1248219089Spjd zfs_type_t type = ZFS_TYPE_DATASET; 1249168404Spjd 1250168404Spjd /* check options */ 1251228103Smm while ((c = getopt(argc, argv, "vpndfrR")) != -1) { 1252168404Spjd switch (c) { 1253228103Smm case 'v': 1254228103Smm cb.cb_verbose = B_TRUE; 1255228103Smm break; 1256228103Smm case 'p': 1257228103Smm cb.cb_verbose = B_TRUE; 1258228103Smm cb.cb_parsable = B_TRUE; 1259228103Smm break; 1260228103Smm case 'n': 1261228103Smm cb.cb_dryrun = B_TRUE; 1262228103Smm break; 1263219089Spjd case 'd': 1264219089Spjd cb.cb_defer_destroy = B_TRUE; 1265219089Spjd type = ZFS_TYPE_SNAPSHOT; 1266219089Spjd break; 1267168404Spjd case 'f': 1268228103Smm cb.cb_force = B_TRUE; 1269168404Spjd break; 1270168404Spjd case 'r': 1271228103Smm cb.cb_recurse = B_TRUE; 1272168404Spjd break; 1273168404Spjd case 'R': 1274228103Smm cb.cb_recurse = B_TRUE; 1275228103Smm cb.cb_doclones = B_TRUE; 1276168404Spjd break; 1277168404Spjd case '?': 1278168404Spjd default: 1279168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1280168404Spjd optopt); 1281168404Spjd usage(B_FALSE); 1282168404Spjd } 1283168404Spjd } 1284168404Spjd 1285168404Spjd argc -= optind; 1286168404Spjd argv += optind; 1287168404Spjd 1288168404Spjd /* check number of arguments */ 1289168404Spjd if (argc == 0) { 1290228103Smm (void) fprintf(stderr, gettext("missing dataset argument\n")); 1291168404Spjd usage(B_FALSE); 1292168404Spjd } 1293168404Spjd if (argc > 1) { 1294168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 1295168404Spjd usage(B_FALSE); 1296168404Spjd } 1297168404Spjd 1298228103Smm at = strchr(argv[0], '@'); 1299263407Sdelphij pound = strchr(argv[0], '#'); 1300228103Smm if (at != NULL) { 1301168404Spjd 1302228103Smm /* Build the list of snaps to destroy in cb_nvl. */ 1303248571Smm cb.cb_nvl = fnvlist_alloc(); 1304228103Smm 1305228103Smm *at = '\0'; 1306228103Smm zhp = zfs_open(g_zfs, argv[0], 1307228103Smm ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 1308228103Smm if (zhp == NULL) 1309168404Spjd return (1); 1310168404Spjd 1311228103Smm cb.cb_snapspec = at + 1; 1312228103Smm if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 || 1313228103Smm cb.cb_error) { 1314248571Smm rv = 1; 1315248571Smm goto out; 1316228103Smm } 1317219089Spjd 1318228103Smm if (nvlist_empty(cb.cb_nvl)) { 1319228103Smm (void) fprintf(stderr, gettext("could not find any " 1320228103Smm "snapshots to destroy; check snapshot names.\n")); 1321248571Smm rv = 1; 1322248571Smm goto out; 1323228103Smm } 1324228103Smm 1325228103Smm if (cb.cb_verbose) { 1326228103Smm char buf[16]; 1327228103Smm zfs_nicenum(cb.cb_snapused, buf, sizeof (buf)); 1328228103Smm if (cb.cb_parsable) { 1329228103Smm (void) printf("reclaim\t%llu\n", 1330228103Smm cb.cb_snapused); 1331228103Smm } else if (cb.cb_dryrun) { 1332228103Smm (void) printf(gettext("would reclaim %s\n"), 1333228103Smm buf); 1334228103Smm } else { 1335228103Smm (void) printf(gettext("will reclaim %s\n"), 1336228103Smm buf); 1337168404Spjd } 1338168404Spjd } 1339168404Spjd 1340228103Smm if (!cb.cb_dryrun) { 1341248571Smm if (cb.cb_doclones) { 1342248571Smm cb.cb_batchedsnaps = fnvlist_alloc(); 1343228103Smm err = destroy_clones(&cb); 1344248571Smm if (err == 0) { 1345248571Smm err = zfs_destroy_snaps_nvl(g_zfs, 1346248571Smm cb.cb_batchedsnaps, B_FALSE); 1347248571Smm } 1348248571Smm if (err != 0) { 1349248571Smm rv = 1; 1350248571Smm goto out; 1351248571Smm } 1352248571Smm } 1353228103Smm if (err == 0) { 1354248571Smm err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl, 1355228103Smm cb.cb_defer_destroy); 1356228103Smm } 1357168404Spjd } 1358168404Spjd 1359228103Smm if (err != 0) 1360248571Smm rv = 1; 1361263407Sdelphij } else if (pound != NULL) { 1362263407Sdelphij int err; 1363263407Sdelphij nvlist_t *nvl; 1364263407Sdelphij 1365263407Sdelphij if (cb.cb_dryrun) { 1366263407Sdelphij (void) fprintf(stderr, 1367263407Sdelphij "dryrun is not supported with bookmark\n"); 1368263407Sdelphij return (-1); 1369263407Sdelphij } 1370263407Sdelphij 1371263407Sdelphij if (cb.cb_defer_destroy) { 1372263407Sdelphij (void) fprintf(stderr, 1373263407Sdelphij "defer destroy is not supported with bookmark\n"); 1374263407Sdelphij return (-1); 1375263407Sdelphij } 1376263407Sdelphij 1377263407Sdelphij if (cb.cb_recurse) { 1378263407Sdelphij (void) fprintf(stderr, 1379263407Sdelphij "recursive is not supported with bookmark\n"); 1380263407Sdelphij return (-1); 1381263407Sdelphij } 1382263407Sdelphij 1383263407Sdelphij if (!zfs_bookmark_exists(argv[0])) { 1384263407Sdelphij (void) fprintf(stderr, gettext("bookmark '%s' " 1385263407Sdelphij "does not exist.\n"), argv[0]); 1386263407Sdelphij return (1); 1387263407Sdelphij } 1388263407Sdelphij 1389263407Sdelphij nvl = fnvlist_alloc(); 1390263407Sdelphij fnvlist_add_boolean(nvl, argv[0]); 1391263407Sdelphij 1392263407Sdelphij err = lzc_destroy_bookmarks(nvl, NULL); 1393263407Sdelphij if (err != 0) { 1394263407Sdelphij (void) zfs_standard_error(g_zfs, err, 1395263407Sdelphij "cannot destroy bookmark"); 1396263407Sdelphij } 1397263407Sdelphij 1398263407Sdelphij nvlist_free(cb.cb_nvl); 1399263407Sdelphij 1400263407Sdelphij return (err); 1401228103Smm } else { 1402228103Smm /* Open the given dataset */ 1403228103Smm if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL) 1404228103Smm return (1); 1405168404Spjd 1406228103Smm cb.cb_target = zhp; 1407168404Spjd 1408228103Smm /* 1409228103Smm * Perform an explicit check for pools before going any further. 1410228103Smm */ 1411228103Smm if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 1412228103Smm zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 1413228103Smm (void) fprintf(stderr, gettext("cannot destroy '%s': " 1414228103Smm "operation does not apply to pools\n"), 1415228103Smm zfs_get_name(zhp)); 1416228103Smm (void) fprintf(stderr, gettext("use 'zfs destroy -r " 1417228103Smm "%s' to destroy all datasets in the pool\n"), 1418228103Smm zfs_get_name(zhp)); 1419228103Smm (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 1420228103Smm "to destroy the pool itself\n"), zfs_get_name(zhp)); 1421248571Smm rv = 1; 1422248571Smm goto out; 1423228103Smm } 1424168404Spjd 1425228103Smm /* 1426228103Smm * Check for any dependents and/or clones. 1427228103Smm */ 1428228103Smm cb.cb_first = B_TRUE; 1429228103Smm if (!cb.cb_doclones && 1430228103Smm zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent, 1431228103Smm &cb) != 0) { 1432248571Smm rv = 1; 1433248571Smm goto out; 1434228103Smm } 1435168404Spjd 1436228103Smm if (cb.cb_error) { 1437248571Smm rv = 1; 1438248571Smm goto out; 1439228103Smm } 1440168404Spjd 1441248571Smm cb.cb_batchedsnaps = fnvlist_alloc(); 1442228103Smm if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback, 1443228103Smm &cb) != 0) { 1444248571Smm rv = 1; 1445248571Smm goto out; 1446228103Smm } 1447185029Spjd 1448228103Smm /* 1449228103Smm * Do the real thing. The callback will close the 1450228103Smm * handle regardless of whether it succeeds or not. 1451228103Smm */ 1452248571Smm err = destroy_callback(zhp, &cb); 1453248571Smm zhp = NULL; 1454248571Smm if (err == 0) { 1455248571Smm err = zfs_destroy_snaps_nvl(g_zfs, 1456248571Smm cb.cb_batchedsnaps, cb.cb_defer_destroy); 1457248571Smm } 1458248571Smm if (err != 0) 1459248571Smm rv = 1; 1460228103Smm } 1461168404Spjd 1462248571Smmout: 1463248571Smm fnvlist_free(cb.cb_batchedsnaps); 1464248571Smm fnvlist_free(cb.cb_nvl); 1465248571Smm if (zhp != NULL) 1466248571Smm zfs_close(zhp); 1467248571Smm return (rv); 1468168404Spjd} 1469168404Spjd 1470219089Spjdstatic boolean_t 1471219089Spjdis_recvd_column(zprop_get_cbdata_t *cbp) 1472219089Spjd{ 1473219089Spjd int i; 1474219089Spjd zfs_get_column_t col; 1475219089Spjd 1476219089Spjd for (i = 0; i < ZFS_GET_NCOLS && 1477219089Spjd (col = cbp->cb_columns[i]) != GET_COL_NONE; i++) 1478219089Spjd if (col == GET_COL_RECVD) 1479219089Spjd return (B_TRUE); 1480219089Spjd return (B_FALSE); 1481219089Spjd} 1482219089Spjd 1483168404Spjd/* 1484219089Spjd * zfs get [-rHp] [-o all | field[,field]...] [-s source[,source]...] 1485219089Spjd * < all | property[,property]... > < fs | snap | vol > ... 1486168404Spjd * 1487168404Spjd * -r recurse over any child datasets 1488168404Spjd * -H scripted mode. Headers are stripped, and fields are separated 1489168404Spjd * by tabs instead of spaces. 1490219089Spjd * -o Set of fields to display. One of "name,property,value, 1491219089Spjd * received,source". Default is "name,property,value,source". 1492219089Spjd * "all" is an alias for all five. 1493168404Spjd * -s Set of sources to allow. One of 1494219089Spjd * "local,default,inherited,received,temporary,none". Default is 1495219089Spjd * all six. 1496168404Spjd * -p Display values in parsable (literal) format. 1497168404Spjd * 1498168404Spjd * Prints properties for the given datasets. The user can control which 1499168404Spjd * columns to display as well as which property types to allow. 1500168404Spjd */ 1501168404Spjd 1502168404Spjd/* 1503168404Spjd * Invoked to display the properties for a single dataset. 1504168404Spjd */ 1505168404Spjdstatic int 1506168404Spjdget_callback(zfs_handle_t *zhp, void *data) 1507168404Spjd{ 1508168404Spjd char buf[ZFS_MAXPROPLEN]; 1509219089Spjd char rbuf[ZFS_MAXPROPLEN]; 1510185029Spjd zprop_source_t sourcetype; 1511168404Spjd char source[ZFS_MAXNAMELEN]; 1512185029Spjd zprop_get_cbdata_t *cbp = data; 1513219089Spjd nvlist_t *user_props = zfs_get_user_props(zhp); 1514185029Spjd zprop_list_t *pl = cbp->cb_proplist; 1515168404Spjd nvlist_t *propval; 1516168404Spjd char *strval; 1517168404Spjd char *sourceval; 1518219089Spjd boolean_t received = is_recvd_column(cbp); 1519168404Spjd 1520168404Spjd for (; pl != NULL; pl = pl->pl_next) { 1521219089Spjd char *recvdval = NULL; 1522168404Spjd /* 1523168404Spjd * Skip the special fake placeholder. This will also skip over 1524168404Spjd * the name property when 'all' is specified. 1525168404Spjd */ 1526168404Spjd if (pl->pl_prop == ZFS_PROP_NAME && 1527168404Spjd pl == cbp->cb_proplist) 1528168404Spjd continue; 1529168404Spjd 1530185029Spjd if (pl->pl_prop != ZPROP_INVAL) { 1531168404Spjd if (zfs_prop_get(zhp, pl->pl_prop, buf, 1532168404Spjd sizeof (buf), &sourcetype, source, 1533168404Spjd sizeof (source), 1534168404Spjd cbp->cb_literal) != 0) { 1535168404Spjd if (pl->pl_all) 1536168404Spjd continue; 1537168404Spjd if (!zfs_prop_valid_for_type(pl->pl_prop, 1538185029Spjd ZFS_TYPE_DATASET)) { 1539168404Spjd (void) fprintf(stderr, 1540168404Spjd gettext("No such property '%s'\n"), 1541168404Spjd zfs_prop_to_name(pl->pl_prop)); 1542168404Spjd continue; 1543168404Spjd } 1544185029Spjd sourcetype = ZPROP_SRC_NONE; 1545168404Spjd (void) strlcpy(buf, "-", sizeof (buf)); 1546168404Spjd } 1547168404Spjd 1548219089Spjd if (received && (zfs_prop_get_recvd(zhp, 1549219089Spjd zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf), 1550219089Spjd cbp->cb_literal) == 0)) 1551219089Spjd recvdval = rbuf; 1552219089Spjd 1553185029Spjd zprop_print_one_property(zfs_get_name(zhp), cbp, 1554168404Spjd zfs_prop_to_name(pl->pl_prop), 1555219089Spjd buf, sourcetype, source, recvdval); 1556209962Smm } else if (zfs_prop_userquota(pl->pl_user_prop)) { 1557209962Smm sourcetype = ZPROP_SRC_LOCAL; 1558209962Smm 1559209962Smm if (zfs_prop_get_userquota(zhp, pl->pl_user_prop, 1560209962Smm buf, sizeof (buf), cbp->cb_literal) != 0) { 1561209962Smm sourcetype = ZPROP_SRC_NONE; 1562209962Smm (void) strlcpy(buf, "-", sizeof (buf)); 1563209962Smm } 1564209962Smm 1565209962Smm zprop_print_one_property(zfs_get_name(zhp), cbp, 1566219089Spjd pl->pl_user_prop, buf, sourcetype, source, NULL); 1567228103Smm } else if (zfs_prop_written(pl->pl_user_prop)) { 1568228103Smm sourcetype = ZPROP_SRC_LOCAL; 1569228103Smm 1570228103Smm if (zfs_prop_get_written(zhp, pl->pl_user_prop, 1571228103Smm buf, sizeof (buf), cbp->cb_literal) != 0) { 1572228103Smm sourcetype = ZPROP_SRC_NONE; 1573228103Smm (void) strlcpy(buf, "-", sizeof (buf)); 1574228103Smm } 1575228103Smm 1576228103Smm zprop_print_one_property(zfs_get_name(zhp), cbp, 1577228103Smm pl->pl_user_prop, buf, sourcetype, source, NULL); 1578168404Spjd } else { 1579219089Spjd if (nvlist_lookup_nvlist(user_props, 1580168404Spjd pl->pl_user_prop, &propval) != 0) { 1581168404Spjd if (pl->pl_all) 1582168404Spjd continue; 1583185029Spjd sourcetype = ZPROP_SRC_NONE; 1584168404Spjd strval = "-"; 1585168404Spjd } else { 1586168404Spjd verify(nvlist_lookup_string(propval, 1587185029Spjd ZPROP_VALUE, &strval) == 0); 1588168404Spjd verify(nvlist_lookup_string(propval, 1589185029Spjd ZPROP_SOURCE, &sourceval) == 0); 1590168404Spjd 1591168404Spjd if (strcmp(sourceval, 1592168404Spjd zfs_get_name(zhp)) == 0) { 1593185029Spjd sourcetype = ZPROP_SRC_LOCAL; 1594219089Spjd } else if (strcmp(sourceval, 1595219089Spjd ZPROP_SOURCE_VAL_RECVD) == 0) { 1596219089Spjd sourcetype = ZPROP_SRC_RECEIVED; 1597168404Spjd } else { 1598185029Spjd sourcetype = ZPROP_SRC_INHERITED; 1599168404Spjd (void) strlcpy(source, 1600168404Spjd sourceval, sizeof (source)); 1601168404Spjd } 1602168404Spjd } 1603168404Spjd 1604219089Spjd if (received && (zfs_prop_get_recvd(zhp, 1605219089Spjd pl->pl_user_prop, rbuf, sizeof (rbuf), 1606219089Spjd cbp->cb_literal) == 0)) 1607219089Spjd recvdval = rbuf; 1608219089Spjd 1609185029Spjd zprop_print_one_property(zfs_get_name(zhp), cbp, 1610168404Spjd pl->pl_user_prop, strval, sourcetype, 1611219089Spjd source, recvdval); 1612168404Spjd } 1613168404Spjd } 1614168404Spjd 1615168404Spjd return (0); 1616168404Spjd} 1617168404Spjd 1618168404Spjdstatic int 1619168404Spjdzfs_do_get(int argc, char **argv) 1620168404Spjd{ 1621185029Spjd zprop_get_cbdata_t cb = { 0 }; 1622223620Smm int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS; 1623232064Smm int types = ZFS_TYPE_DATASET; 1624168404Spjd char *value, *fields; 1625231144Smm int ret = 0; 1626205199Sdelphij int limit = 0; 1627185029Spjd zprop_list_t fake_name = { 0 }; 1628168404Spjd 1629168404Spjd /* 1630168404Spjd * Set up default columns and sources. 1631168404Spjd */ 1632185029Spjd cb.cb_sources = ZPROP_SRC_ALL; 1633168404Spjd cb.cb_columns[0] = GET_COL_NAME; 1634168404Spjd cb.cb_columns[1] = GET_COL_PROPERTY; 1635168404Spjd cb.cb_columns[2] = GET_COL_VALUE; 1636168404Spjd cb.cb_columns[3] = GET_COL_SOURCE; 1637185029Spjd cb.cb_type = ZFS_TYPE_DATASET; 1638168404Spjd 1639168404Spjd /* check options */ 1640232064Smm while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) { 1641168404Spjd switch (c) { 1642168404Spjd case 'p': 1643168404Spjd cb.cb_literal = B_TRUE; 1644168404Spjd break; 1645205199Sdelphij case 'd': 1646205199Sdelphij limit = parse_depth(optarg, &flags); 1647205199Sdelphij break; 1648168404Spjd case 'r': 1649185029Spjd flags |= ZFS_ITER_RECURSE; 1650168404Spjd break; 1651168404Spjd case 'H': 1652168404Spjd cb.cb_scripted = B_TRUE; 1653168404Spjd break; 1654168404Spjd case ':': 1655168404Spjd (void) fprintf(stderr, gettext("missing argument for " 1656168404Spjd "'%c' option\n"), optopt); 1657168404Spjd usage(B_FALSE); 1658168404Spjd break; 1659168404Spjd case 'o': 1660168404Spjd /* 1661168404Spjd * Process the set of columns to display. We zero out 1662168404Spjd * the structure to give us a blank slate. 1663168404Spjd */ 1664168404Spjd bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 1665168404Spjd i = 0; 1666168404Spjd while (*optarg != '\0') { 1667168404Spjd static char *col_subopts[] = 1668219089Spjd { "name", "property", "value", "received", 1669219089Spjd "source", "all", NULL }; 1670168404Spjd 1671219089Spjd if (i == ZFS_GET_NCOLS) { 1672168404Spjd (void) fprintf(stderr, gettext("too " 1673168404Spjd "many fields given to -o " 1674168404Spjd "option\n")); 1675168404Spjd usage(B_FALSE); 1676168404Spjd } 1677168404Spjd 1678168404Spjd switch (getsubopt(&optarg, col_subopts, 1679168404Spjd &value)) { 1680168404Spjd case 0: 1681168404Spjd cb.cb_columns[i++] = GET_COL_NAME; 1682168404Spjd break; 1683168404Spjd case 1: 1684168404Spjd cb.cb_columns[i++] = GET_COL_PROPERTY; 1685168404Spjd break; 1686168404Spjd case 2: 1687168404Spjd cb.cb_columns[i++] = GET_COL_VALUE; 1688168404Spjd break; 1689168404Spjd case 3: 1690219089Spjd cb.cb_columns[i++] = GET_COL_RECVD; 1691219089Spjd flags |= ZFS_ITER_RECVD_PROPS; 1692219089Spjd break; 1693219089Spjd case 4: 1694168404Spjd cb.cb_columns[i++] = GET_COL_SOURCE; 1695168404Spjd break; 1696219089Spjd case 5: 1697219089Spjd if (i > 0) { 1698219089Spjd (void) fprintf(stderr, 1699219089Spjd gettext("\"all\" conflicts " 1700219089Spjd "with specific fields " 1701219089Spjd "given to -o option\n")); 1702219089Spjd usage(B_FALSE); 1703219089Spjd } 1704219089Spjd cb.cb_columns[0] = GET_COL_NAME; 1705219089Spjd cb.cb_columns[1] = GET_COL_PROPERTY; 1706219089Spjd cb.cb_columns[2] = GET_COL_VALUE; 1707219089Spjd cb.cb_columns[3] = GET_COL_RECVD; 1708219089Spjd cb.cb_columns[4] = GET_COL_SOURCE; 1709219089Spjd flags |= ZFS_ITER_RECVD_PROPS; 1710219089Spjd i = ZFS_GET_NCOLS; 1711219089Spjd break; 1712168404Spjd default: 1713168404Spjd (void) fprintf(stderr, 1714168404Spjd gettext("invalid column name " 1715168404Spjd "'%s'\n"), value); 1716168404Spjd usage(B_FALSE); 1717168404Spjd } 1718168404Spjd } 1719168404Spjd break; 1720168404Spjd 1721168404Spjd case 's': 1722168404Spjd cb.cb_sources = 0; 1723168404Spjd while (*optarg != '\0') { 1724168404Spjd static char *source_subopts[] = { 1725168404Spjd "local", "default", "inherited", 1726219089Spjd "received", "temporary", "none", 1727219089Spjd NULL }; 1728168404Spjd 1729168404Spjd switch (getsubopt(&optarg, source_subopts, 1730168404Spjd &value)) { 1731168404Spjd case 0: 1732185029Spjd cb.cb_sources |= ZPROP_SRC_LOCAL; 1733168404Spjd break; 1734168404Spjd case 1: 1735185029Spjd cb.cb_sources |= ZPROP_SRC_DEFAULT; 1736168404Spjd break; 1737168404Spjd case 2: 1738185029Spjd cb.cb_sources |= ZPROP_SRC_INHERITED; 1739168404Spjd break; 1740168404Spjd case 3: 1741219089Spjd cb.cb_sources |= ZPROP_SRC_RECEIVED; 1742219089Spjd break; 1743219089Spjd case 4: 1744185029Spjd cb.cb_sources |= ZPROP_SRC_TEMPORARY; 1745168404Spjd break; 1746219089Spjd case 5: 1747185029Spjd cb.cb_sources |= ZPROP_SRC_NONE; 1748168404Spjd break; 1749168404Spjd default: 1750168404Spjd (void) fprintf(stderr, 1751168404Spjd gettext("invalid source " 1752168404Spjd "'%s'\n"), value); 1753168404Spjd usage(B_FALSE); 1754168404Spjd } 1755168404Spjd } 1756168404Spjd break; 1757168404Spjd 1758232064Smm case 't': 1759232064Smm types = 0; 1760232064Smm flags &= ~ZFS_ITER_PROP_LISTSNAPS; 1761232064Smm while (*optarg != '\0') { 1762232064Smm static char *type_subopts[] = { "filesystem", 1763263407Sdelphij "volume", "snapshot", "bookmark", 1764263407Sdelphij "all", NULL }; 1765232064Smm 1766232064Smm switch (getsubopt(&optarg, type_subopts, 1767232064Smm &value)) { 1768232064Smm case 0: 1769232064Smm types |= ZFS_TYPE_FILESYSTEM; 1770232064Smm break; 1771232064Smm case 1: 1772232064Smm types |= ZFS_TYPE_VOLUME; 1773232064Smm break; 1774232064Smm case 2: 1775232064Smm types |= ZFS_TYPE_SNAPSHOT; 1776232064Smm break; 1777232064Smm case 3: 1778263407Sdelphij types |= ZFS_TYPE_BOOKMARK; 1779232064Smm break; 1780263407Sdelphij case 4: 1781263407Sdelphij types = ZFS_TYPE_DATASET | 1782263407Sdelphij ZFS_TYPE_BOOKMARK; 1783263407Sdelphij break; 1784232064Smm 1785232064Smm default: 1786232064Smm (void) fprintf(stderr, 1787232064Smm gettext("invalid type '%s'\n"), 1788232064Smm value); 1789232064Smm usage(B_FALSE); 1790232064Smm } 1791232064Smm } 1792232064Smm break; 1793232064Smm 1794168404Spjd case '?': 1795168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1796168404Spjd optopt); 1797168404Spjd usage(B_FALSE); 1798168404Spjd } 1799168404Spjd } 1800168404Spjd 1801168404Spjd argc -= optind; 1802168404Spjd argv += optind; 1803168404Spjd 1804168404Spjd if (argc < 1) { 1805168404Spjd (void) fprintf(stderr, gettext("missing property " 1806168404Spjd "argument\n")); 1807168404Spjd usage(B_FALSE); 1808168404Spjd } 1809168404Spjd 1810168404Spjd fields = argv[0]; 1811168404Spjd 1812185029Spjd if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET) 1813185029Spjd != 0) 1814168404Spjd usage(B_FALSE); 1815168404Spjd 1816168404Spjd argc--; 1817168404Spjd argv++; 1818168404Spjd 1819168404Spjd /* 1820168404Spjd * As part of zfs_expand_proplist(), we keep track of the maximum column 1821168404Spjd * width for each property. For the 'NAME' (and 'SOURCE') columns, we 1822168404Spjd * need to know the maximum name length. However, the user likely did 1823168404Spjd * not specify 'name' as one of the properties to fetch, so we need to 1824168404Spjd * make sure we always include at least this property for 1825168404Spjd * print_get_headers() to work properly. 1826168404Spjd */ 1827168404Spjd if (cb.cb_proplist != NULL) { 1828168404Spjd fake_name.pl_prop = ZFS_PROP_NAME; 1829168404Spjd fake_name.pl_width = strlen(gettext("NAME")); 1830168404Spjd fake_name.pl_next = cb.cb_proplist; 1831168404Spjd cb.cb_proplist = &fake_name; 1832168404Spjd } 1833168404Spjd 1834168404Spjd cb.cb_first = B_TRUE; 1835168404Spjd 1836168404Spjd /* run for each object */ 1837232064Smm ret = zfs_for_each(argc, argv, flags, types, NULL, 1838205199Sdelphij &cb.cb_proplist, limit, get_callback, &cb); 1839168404Spjd 1840168404Spjd if (cb.cb_proplist == &fake_name) 1841185029Spjd zprop_free_list(fake_name.pl_next); 1842168404Spjd else 1843185029Spjd zprop_free_list(cb.cb_proplist); 1844168404Spjd 1845168404Spjd return (ret); 1846168404Spjd} 1847168404Spjd 1848168404Spjd/* 1849219089Spjd * inherit [-rS] <property> <fs|vol> ... 1850168404Spjd * 1851219089Spjd * -r Recurse over all children 1852219089Spjd * -S Revert to received value, if any 1853168404Spjd * 1854168404Spjd * For each dataset specified on the command line, inherit the given property 1855168404Spjd * from its parent. Inheriting a property at the pool level will cause it to 1856168404Spjd * use the default value. The '-r' flag will recurse over all children, and is 1857168404Spjd * useful for setting a property on a hierarchy-wide basis, regardless of any 1858168404Spjd * local modifications for each dataset. 1859168404Spjd */ 1860168404Spjd 1861219089Spjdtypedef struct inherit_cbdata { 1862219089Spjd const char *cb_propname; 1863219089Spjd boolean_t cb_received; 1864219089Spjd} inherit_cbdata_t; 1865219089Spjd 1866168404Spjdstatic int 1867185029Spjdinherit_recurse_cb(zfs_handle_t *zhp, void *data) 1868168404Spjd{ 1869219089Spjd inherit_cbdata_t *cb = data; 1870219089Spjd zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname); 1871168404Spjd 1872185029Spjd /* 1873185029Spjd * If we're doing it recursively, then ignore properties that 1874185029Spjd * are not valid for this type of dataset. 1875185029Spjd */ 1876185029Spjd if (prop != ZPROP_INVAL && 1877185029Spjd !zfs_prop_valid_for_type(prop, zfs_get_type(zhp))) 1878185029Spjd return (0); 1879185029Spjd 1880219089Spjd return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0); 1881168404Spjd} 1882168404Spjd 1883168404Spjdstatic int 1884185029Spjdinherit_cb(zfs_handle_t *zhp, void *data) 1885185029Spjd{ 1886219089Spjd inherit_cbdata_t *cb = data; 1887185029Spjd 1888219089Spjd return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0); 1889185029Spjd} 1890185029Spjd 1891185029Spjdstatic int 1892168404Spjdzfs_do_inherit(int argc, char **argv) 1893168404Spjd{ 1894168404Spjd int c; 1895168404Spjd zfs_prop_t prop; 1896219089Spjd inherit_cbdata_t cb = { 0 }; 1897185029Spjd char *propname; 1898231144Smm int ret = 0; 1899185029Spjd int flags = 0; 1900219089Spjd boolean_t received = B_FALSE; 1901168404Spjd 1902168404Spjd /* check options */ 1903219089Spjd while ((c = getopt(argc, argv, "rS")) != -1) { 1904168404Spjd switch (c) { 1905168404Spjd case 'r': 1906185029Spjd flags |= ZFS_ITER_RECURSE; 1907168404Spjd break; 1908219089Spjd case 'S': 1909219089Spjd received = B_TRUE; 1910219089Spjd break; 1911168404Spjd case '?': 1912168404Spjd default: 1913168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1914168404Spjd optopt); 1915168404Spjd usage(B_FALSE); 1916168404Spjd } 1917168404Spjd } 1918168404Spjd 1919168404Spjd argc -= optind; 1920168404Spjd argv += optind; 1921168404Spjd 1922168404Spjd /* check number of arguments */ 1923168404Spjd if (argc < 1) { 1924168404Spjd (void) fprintf(stderr, gettext("missing property argument\n")); 1925168404Spjd usage(B_FALSE); 1926168404Spjd } 1927168404Spjd if (argc < 2) { 1928168404Spjd (void) fprintf(stderr, gettext("missing dataset argument\n")); 1929168404Spjd usage(B_FALSE); 1930168404Spjd } 1931168404Spjd 1932185029Spjd propname = argv[0]; 1933168404Spjd argc--; 1934168404Spjd argv++; 1935168404Spjd 1936185029Spjd if ((prop = zfs_name_to_prop(propname)) != ZPROP_INVAL) { 1937168404Spjd if (zfs_prop_readonly(prop)) { 1938168404Spjd (void) fprintf(stderr, gettext( 1939168404Spjd "%s property is read-only\n"), 1940185029Spjd propname); 1941168404Spjd return (1); 1942168404Spjd } 1943219089Spjd if (!zfs_prop_inheritable(prop) && !received) { 1944168404Spjd (void) fprintf(stderr, gettext("'%s' property cannot " 1945185029Spjd "be inherited\n"), propname); 1946168404Spjd if (prop == ZFS_PROP_QUOTA || 1947185029Spjd prop == ZFS_PROP_RESERVATION || 1948185029Spjd prop == ZFS_PROP_REFQUOTA || 1949288346Sdelphij prop == ZFS_PROP_REFRESERVATION) { 1950168404Spjd (void) fprintf(stderr, gettext("use 'zfs set " 1951185029Spjd "%s=none' to clear\n"), propname); 1952288346Sdelphij (void) fprintf(stderr, gettext("use 'zfs " 1953288346Sdelphij "inherit -S %s' to revert to received " 1954288346Sdelphij "value\n"), propname); 1955288346Sdelphij } 1956168404Spjd return (1); 1957168404Spjd } 1958219089Spjd if (received && (prop == ZFS_PROP_VOLSIZE || 1959219089Spjd prop == ZFS_PROP_VERSION)) { 1960219089Spjd (void) fprintf(stderr, gettext("'%s' property cannot " 1961219089Spjd "be reverted to a received value\n"), propname); 1962219089Spjd return (1); 1963219089Spjd } 1964185029Spjd } else if (!zfs_prop_user(propname)) { 1965185029Spjd (void) fprintf(stderr, gettext("invalid property '%s'\n"), 1966185029Spjd propname); 1967168404Spjd usage(B_FALSE); 1968168404Spjd } 1969168404Spjd 1970219089Spjd cb.cb_propname = propname; 1971219089Spjd cb.cb_received = received; 1972219089Spjd 1973185029Spjd if (flags & ZFS_ITER_RECURSE) { 1974185029Spjd ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET, 1975219089Spjd NULL, NULL, 0, inherit_recurse_cb, &cb); 1976185029Spjd } else { 1977185029Spjd ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET, 1978219089Spjd NULL, NULL, 0, inherit_cb, &cb); 1979185029Spjd } 1980168404Spjd 1981185029Spjd return (ret); 1982185029Spjd} 1983168404Spjd 1984185029Spjdtypedef struct upgrade_cbdata { 1985185029Spjd uint64_t cb_numupgraded; 1986185029Spjd uint64_t cb_numsamegraded; 1987185029Spjd uint64_t cb_numfailed; 1988185029Spjd uint64_t cb_version; 1989185029Spjd boolean_t cb_newer; 1990185029Spjd boolean_t cb_foundone; 1991185029Spjd char cb_lastfs[ZFS_MAXNAMELEN]; 1992185029Spjd} upgrade_cbdata_t; 1993185029Spjd 1994185029Spjdstatic int 1995185029Spjdsame_pool(zfs_handle_t *zhp, const char *name) 1996185029Spjd{ 1997185029Spjd int len1 = strcspn(name, "/@"); 1998185029Spjd const char *zhname = zfs_get_name(zhp); 1999185029Spjd int len2 = strcspn(zhname, "/@"); 2000185029Spjd 2001185029Spjd if (len1 != len2) 2002185029Spjd return (B_FALSE); 2003185029Spjd return (strncmp(name, zhname, len1) == 0); 2004185029Spjd} 2005185029Spjd 2006185029Spjdstatic int 2007185029Spjdupgrade_list_callback(zfs_handle_t *zhp, void *data) 2008185029Spjd{ 2009185029Spjd upgrade_cbdata_t *cb = data; 2010185029Spjd int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 2011185029Spjd 2012185029Spjd /* list if it's old/new */ 2013185029Spjd if ((!cb->cb_newer && version < ZPL_VERSION) || 2014185029Spjd (cb->cb_newer && version > ZPL_VERSION)) { 2015185029Spjd char *str; 2016185029Spjd if (cb->cb_newer) { 2017185029Spjd str = gettext("The following filesystems are " 2018185029Spjd "formatted using a newer software version and\n" 2019185029Spjd "cannot be accessed on the current system.\n\n"); 2020185029Spjd } else { 2021185029Spjd str = gettext("The following filesystems are " 2022185029Spjd "out of date, and can be upgraded. After being\n" 2023185029Spjd "upgraded, these filesystems (and any 'zfs send' " 2024185029Spjd "streams generated from\n" 2025185029Spjd "subsequent snapshots) will no longer be " 2026185029Spjd "accessible by older software versions.\n\n"); 2027185029Spjd } 2028185029Spjd 2029185029Spjd if (!cb->cb_foundone) { 2030185029Spjd (void) puts(str); 2031185029Spjd (void) printf(gettext("VER FILESYSTEM\n")); 2032185029Spjd (void) printf(gettext("--- ------------\n")); 2033185029Spjd cb->cb_foundone = B_TRUE; 2034185029Spjd } 2035185029Spjd 2036185029Spjd (void) printf("%2u %s\n", version, zfs_get_name(zhp)); 2037168404Spjd } 2038168404Spjd 2039185029Spjd return (0); 2040185029Spjd} 2041185029Spjd 2042185029Spjdstatic int 2043185029Spjdupgrade_set_callback(zfs_handle_t *zhp, void *data) 2044185029Spjd{ 2045185029Spjd upgrade_cbdata_t *cb = data; 2046185029Spjd int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 2047219089Spjd int needed_spa_version; 2048219089Spjd int spa_version; 2049185029Spjd 2050219089Spjd if (zfs_spa_version(zhp, &spa_version) < 0) 2051219089Spjd return (-1); 2052185029Spjd 2053219089Spjd needed_spa_version = zfs_spa_version_map(cb->cb_version); 2054185029Spjd 2055219089Spjd if (needed_spa_version < 0) 2056219089Spjd return (-1); 2057209962Smm 2058219089Spjd if (spa_version < needed_spa_version) { 2059219089Spjd /* can't upgrade */ 2060219089Spjd (void) printf(gettext("%s: can not be " 2061219089Spjd "upgraded; the pool version needs to first " 2062219089Spjd "be upgraded\nto version %d\n\n"), 2063219089Spjd zfs_get_name(zhp), needed_spa_version); 2064219089Spjd cb->cb_numfailed++; 2065219089Spjd return (0); 2066185029Spjd } 2067185029Spjd 2068185029Spjd /* upgrade */ 2069185029Spjd if (version < cb->cb_version) { 2070185029Spjd char verstr[16]; 2071185029Spjd (void) snprintf(verstr, sizeof (verstr), 2072185029Spjd "%llu", cb->cb_version); 2073185029Spjd if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) { 2074185029Spjd /* 2075185029Spjd * If they did "zfs upgrade -a", then we could 2076185029Spjd * be doing ioctls to different pools. We need 2077248571Smm * to log this history once to each pool, and bypass 2078248571Smm * the normal history logging that happens in main(). 2079185029Spjd */ 2080248571Smm (void) zpool_log_history(g_zfs, history_str); 2081248571Smm log_history = B_FALSE; 2082185029Spjd } 2083185029Spjd if (zfs_prop_set(zhp, "version", verstr) == 0) 2084185029Spjd cb->cb_numupgraded++; 2085185029Spjd else 2086185029Spjd cb->cb_numfailed++; 2087185029Spjd (void) strcpy(cb->cb_lastfs, zfs_get_name(zhp)); 2088185029Spjd } else if (version > cb->cb_version) { 2089185029Spjd /* can't downgrade */ 2090185029Spjd (void) printf(gettext("%s: can not be downgraded; " 2091185029Spjd "it is already at version %u\n"), 2092185029Spjd zfs_get_name(zhp), version); 2093185029Spjd cb->cb_numfailed++; 2094185029Spjd } else { 2095185029Spjd cb->cb_numsamegraded++; 2096185029Spjd } 2097185029Spjd return (0); 2098185029Spjd} 2099185029Spjd 2100185029Spjd/* 2101185029Spjd * zfs upgrade 2102185029Spjd * zfs upgrade -v 2103185029Spjd * zfs upgrade [-r] [-V <version>] <-a | filesystem> 2104185029Spjd */ 2105185029Spjdstatic int 2106185029Spjdzfs_do_upgrade(int argc, char **argv) 2107185029Spjd{ 2108185029Spjd boolean_t all = B_FALSE; 2109185029Spjd boolean_t showversions = B_FALSE; 2110231144Smm int ret = 0; 2111185029Spjd upgrade_cbdata_t cb = { 0 }; 2112260671Sjhibbits int c; 2113185029Spjd int flags = ZFS_ITER_ARGS_CAN_BE_PATHS; 2114185029Spjd 2115185029Spjd /* check options */ 2116185029Spjd while ((c = getopt(argc, argv, "rvV:a")) != -1) { 2117185029Spjd switch (c) { 2118185029Spjd case 'r': 2119185029Spjd flags |= ZFS_ITER_RECURSE; 2120185029Spjd break; 2121185029Spjd case 'v': 2122185029Spjd showversions = B_TRUE; 2123185029Spjd break; 2124185029Spjd case 'V': 2125185029Spjd if (zfs_prop_string_to_index(ZFS_PROP_VERSION, 2126185029Spjd optarg, &cb.cb_version) != 0) { 2127185029Spjd (void) fprintf(stderr, 2128185029Spjd gettext("invalid version %s\n"), optarg); 2129185029Spjd usage(B_FALSE); 2130185029Spjd } 2131185029Spjd break; 2132185029Spjd case 'a': 2133185029Spjd all = B_TRUE; 2134185029Spjd break; 2135185029Spjd case '?': 2136185029Spjd default: 2137185029Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2138185029Spjd optopt); 2139185029Spjd usage(B_FALSE); 2140185029Spjd } 2141185029Spjd } 2142185029Spjd 2143185029Spjd argc -= optind; 2144185029Spjd argv += optind; 2145185029Spjd 2146185029Spjd if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version)) 2147185029Spjd usage(B_FALSE); 2148185029Spjd if (showversions && (flags & ZFS_ITER_RECURSE || all || 2149185029Spjd cb.cb_version || argc)) 2150185029Spjd usage(B_FALSE); 2151185029Spjd if ((all || argc) && (showversions)) 2152185029Spjd usage(B_FALSE); 2153185029Spjd if (all && argc) 2154185029Spjd usage(B_FALSE); 2155185029Spjd 2156185029Spjd if (showversions) { 2157185029Spjd /* Show info on available versions. */ 2158185029Spjd (void) printf(gettext("The following filesystem versions are " 2159185029Spjd "supported:\n\n")); 2160185029Spjd (void) printf(gettext("VER DESCRIPTION\n")); 2161185029Spjd (void) printf("--- -----------------------------------------" 2162185029Spjd "---------------\n"); 2163185029Spjd (void) printf(gettext(" 1 Initial ZFS filesystem version\n")); 2164185029Spjd (void) printf(gettext(" 2 Enhanced directory entries\n")); 2165228103Smm (void) printf(gettext(" 3 Case insensitive and filesystem " 2166228103Smm "user identifier (FUID)\n")); 2167209962Smm (void) printf(gettext(" 4 userquota, groupquota " 2168209962Smm "properties\n")); 2169219089Spjd (void) printf(gettext(" 5 System attributes\n")); 2170185029Spjd (void) printf(gettext("\nFor more information on a particular " 2171219089Spjd "version, including supported releases,\n")); 2172219089Spjd (void) printf("see the ZFS Administration Guide.\n\n"); 2173185029Spjd ret = 0; 2174185029Spjd } else if (argc || all) { 2175185029Spjd /* Upgrade filesystems */ 2176185029Spjd if (cb.cb_version == 0) 2177185029Spjd cb.cb_version = ZPL_VERSION; 2178185029Spjd ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM, 2179205199Sdelphij NULL, NULL, 0, upgrade_set_callback, &cb); 2180185029Spjd (void) printf(gettext("%llu filesystems upgraded\n"), 2181185029Spjd cb.cb_numupgraded); 2182185029Spjd if (cb.cb_numsamegraded) { 2183185029Spjd (void) printf(gettext("%llu filesystems already at " 2184185029Spjd "this version\n"), 2185185029Spjd cb.cb_numsamegraded); 2186185029Spjd } 2187185029Spjd if (cb.cb_numfailed != 0) 2188185029Spjd ret = 1; 2189185029Spjd } else { 2190185029Spjd /* List old-version filesytems */ 2191185029Spjd boolean_t found; 2192185029Spjd (void) printf(gettext("This system is currently running " 2193185029Spjd "ZFS filesystem version %llu.\n\n"), ZPL_VERSION); 2194185029Spjd 2195185029Spjd flags |= ZFS_ITER_RECURSE; 2196185029Spjd ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM, 2197205199Sdelphij NULL, NULL, 0, upgrade_list_callback, &cb); 2198185029Spjd 2199185029Spjd found = cb.cb_foundone; 2200185029Spjd cb.cb_foundone = B_FALSE; 2201185029Spjd cb.cb_newer = B_TRUE; 2202185029Spjd 2203185029Spjd ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM, 2204205199Sdelphij NULL, NULL, 0, upgrade_list_callback, &cb); 2205185029Spjd 2206185029Spjd if (!cb.cb_foundone && !found) { 2207185029Spjd (void) printf(gettext("All filesystems are " 2208185029Spjd "formatted with the current version.\n")); 2209185029Spjd } 2210185029Spjd } 2211185029Spjd 2212168404Spjd return (ret); 2213168404Spjd} 2214168404Spjd 2215240415Smm/* 2216240415Smm * zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...] 2217240415Smm * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot 2218240415Smm * zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...] 2219240415Smm * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot 2220240415Smm * 2221240415Smm * -H Scripted mode; elide headers and separate columns by tabs. 2222240415Smm * -i Translate SID to POSIX ID. 2223240415Smm * -n Print numeric ID instead of user/group name. 2224240415Smm * -o Control which fields to display. 2225263405Sdelphij * -p Use exact (parsable) numeric output. 2226240415Smm * -s Specify sort columns, descending order. 2227240415Smm * -S Specify sort columns, ascending order. 2228240415Smm * -t Control which object types to display. 2229240415Smm * 2230240415Smm * Displays space consumed by, and quotas on, each user in the specified 2231240415Smm * filesystem or snapshot. 2232240415Smm */ 2233219089Spjd 2234240415Smm/* us_field_types, us_field_hdr and us_field_names should be kept in sync */ 2235240415Smmenum us_field_types { 2236240415Smm USFIELD_TYPE, 2237240415Smm USFIELD_NAME, 2238240415Smm USFIELD_USED, 2239240415Smm USFIELD_QUOTA 2240240415Smm}; 2241240415Smmstatic char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA" }; 2242240415Smmstatic char *us_field_names[] = { "type", "name", "used", "quota" }; 2243240415Smm#define USFIELD_LAST (sizeof (us_field_names) / sizeof (char *)) 2244219089Spjd 2245240415Smm#define USTYPE_PSX_GRP (1 << 0) 2246240415Smm#define USTYPE_PSX_USR (1 << 1) 2247240415Smm#define USTYPE_SMB_GRP (1 << 2) 2248240415Smm#define USTYPE_SMB_USR (1 << 3) 2249240415Smm#define USTYPE_ALL \ 2250240415Smm (USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR) 2251219089Spjd 2252240415Smmstatic int us_type_bits[] = { 2253240415Smm USTYPE_PSX_GRP, 2254240415Smm USTYPE_PSX_USR, 2255240415Smm USTYPE_SMB_GRP, 2256240415Smm USTYPE_SMB_USR, 2257240415Smm USTYPE_ALL 2258240415Smm}; 2259269005Sdelphijstatic char *us_type_names[] = { "posixgroup", "posixuser", "smbgroup", 2260240415Smm "smbuser", "all" }; 2261219089Spjd 2262219089Spjdtypedef struct us_node { 2263219089Spjd nvlist_t *usn_nvl; 2264219089Spjd uu_avl_node_t usn_avlnode; 2265219089Spjd uu_list_node_t usn_listnode; 2266219089Spjd} us_node_t; 2267219089Spjd 2268219089Spjdtypedef struct us_cbdata { 2269240415Smm nvlist_t **cb_nvlp; 2270240415Smm uu_avl_pool_t *cb_avl_pool; 2271240415Smm uu_avl_t *cb_avl; 2272240415Smm boolean_t cb_numname; 2273240415Smm boolean_t cb_nicenum; 2274240415Smm boolean_t cb_sid2posix; 2275240415Smm zfs_userquota_prop_t cb_prop; 2276240415Smm zfs_sort_column_t *cb_sortcol; 2277240415Smm size_t cb_width[USFIELD_LAST]; 2278219089Spjd} us_cbdata_t; 2279219089Spjd 2280240415Smmstatic boolean_t us_populated = B_FALSE; 2281240415Smm 2282219089Spjdtypedef struct { 2283219089Spjd zfs_sort_column_t *si_sortcol; 2284240415Smm boolean_t si_numname; 2285219089Spjd} us_sort_info_t; 2286219089Spjd 2287219089Spjdstatic int 2288240415Smmus_field_index(char *field) 2289240415Smm{ 2290240415Smm int i; 2291240415Smm 2292240415Smm for (i = 0; i < USFIELD_LAST; i++) { 2293240415Smm if (strcmp(field, us_field_names[i]) == 0) 2294240415Smm return (i); 2295240415Smm } 2296240415Smm 2297240415Smm return (-1); 2298240415Smm} 2299240415Smm 2300240415Smmstatic int 2301219089Spjdus_compare(const void *larg, const void *rarg, void *unused) 2302219089Spjd{ 2303219089Spjd const us_node_t *l = larg; 2304219089Spjd const us_node_t *r = rarg; 2305219089Spjd us_sort_info_t *si = (us_sort_info_t *)unused; 2306219089Spjd zfs_sort_column_t *sortcol = si->si_sortcol; 2307240415Smm boolean_t numname = si->si_numname; 2308219089Spjd nvlist_t *lnvl = l->usn_nvl; 2309219089Spjd nvlist_t *rnvl = r->usn_nvl; 2310240415Smm int rc = 0; 2311240415Smm boolean_t lvb, rvb; 2312219089Spjd 2313219089Spjd for (; sortcol != NULL; sortcol = sortcol->sc_next) { 2314219089Spjd char *lvstr = ""; 2315219089Spjd char *rvstr = ""; 2316219089Spjd uint32_t lv32 = 0; 2317219089Spjd uint32_t rv32 = 0; 2318219089Spjd uint64_t lv64 = 0; 2319219089Spjd uint64_t rv64 = 0; 2320219089Spjd zfs_prop_t prop = sortcol->sc_prop; 2321219089Spjd const char *propname = NULL; 2322219089Spjd boolean_t reverse = sortcol->sc_reverse; 2323219089Spjd 2324219089Spjd switch (prop) { 2325219089Spjd case ZFS_PROP_TYPE: 2326219089Spjd propname = "type"; 2327219089Spjd (void) nvlist_lookup_uint32(lnvl, propname, &lv32); 2328219089Spjd (void) nvlist_lookup_uint32(rnvl, propname, &rv32); 2329219089Spjd if (rv32 != lv32) 2330240415Smm rc = (rv32 < lv32) ? 1 : -1; 2331219089Spjd break; 2332219089Spjd case ZFS_PROP_NAME: 2333219089Spjd propname = "name"; 2334240415Smm if (numname) { 2335240415Smm (void) nvlist_lookup_uint64(lnvl, propname, 2336240415Smm &lv64); 2337240415Smm (void) nvlist_lookup_uint64(rnvl, propname, 2338240415Smm &rv64); 2339240415Smm if (rv64 != lv64) 2340240415Smm rc = (rv64 < lv64) ? 1 : -1; 2341219089Spjd } else { 2342219089Spjd (void) nvlist_lookup_string(lnvl, propname, 2343219089Spjd &lvstr); 2344219089Spjd (void) nvlist_lookup_string(rnvl, propname, 2345219089Spjd &rvstr); 2346219089Spjd rc = strcmp(lvstr, rvstr); 2347219089Spjd } 2348219089Spjd break; 2349219089Spjd case ZFS_PROP_USED: 2350219089Spjd case ZFS_PROP_QUOTA: 2351240415Smm if (!us_populated) 2352240415Smm break; 2353240415Smm if (prop == ZFS_PROP_USED) 2354219089Spjd propname = "used"; 2355219089Spjd else 2356219089Spjd propname = "quota"; 2357219089Spjd (void) nvlist_lookup_uint64(lnvl, propname, &lv64); 2358219089Spjd (void) nvlist_lookup_uint64(rnvl, propname, &rv64); 2359219089Spjd if (rv64 != lv64) 2360240415Smm rc = (rv64 < lv64) ? 1 : -1; 2361240415Smm break; 2362219089Spjd } 2363219089Spjd 2364240415Smm if (rc != 0) { 2365219089Spjd if (rc < 0) 2366219089Spjd return (reverse ? 1 : -1); 2367219089Spjd else 2368219089Spjd return (reverse ? -1 : 1); 2369240415Smm } 2370219089Spjd } 2371219089Spjd 2372240415Smm /* 2373240415Smm * If entries still seem to be the same, check if they are of the same 2374240415Smm * type (smbentity is added only if we are doing SID to POSIX ID 2375240415Smm * translation where we can have duplicate type/name combinations). 2376240415Smm */ 2377240415Smm if (nvlist_lookup_boolean_value(lnvl, "smbentity", &lvb) == 0 && 2378240415Smm nvlist_lookup_boolean_value(rnvl, "smbentity", &rvb) == 0 && 2379240415Smm lvb != rvb) 2380240415Smm return (lvb < rvb ? -1 : 1); 2381240415Smm 2382240415Smm return (0); 2383219089Spjd} 2384219089Spjd 2385219089Spjdstatic inline const char * 2386219089Spjdus_type2str(unsigned field_type) 2387219089Spjd{ 2388219089Spjd switch (field_type) { 2389219089Spjd case USTYPE_PSX_USR: 2390219089Spjd return ("POSIX User"); 2391219089Spjd case USTYPE_PSX_GRP: 2392219089Spjd return ("POSIX Group"); 2393219089Spjd case USTYPE_SMB_USR: 2394219089Spjd return ("SMB User"); 2395219089Spjd case USTYPE_SMB_GRP: 2396219089Spjd return ("SMB Group"); 2397219089Spjd default: 2398219089Spjd return ("Undefined"); 2399219089Spjd } 2400219089Spjd} 2401219089Spjd 2402209962Smmstatic int 2403209962Smmuserspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space) 2404209962Smm{ 2405219089Spjd us_cbdata_t *cb = (us_cbdata_t *)arg; 2406219089Spjd zfs_userquota_prop_t prop = cb->cb_prop; 2407209962Smm char *name = NULL; 2408219089Spjd char *propname; 2409209962Smm char sizebuf[32]; 2410219089Spjd us_node_t *node; 2411219089Spjd uu_avl_pool_t *avl_pool = cb->cb_avl_pool; 2412219089Spjd uu_avl_t *avl = cb->cb_avl; 2413219089Spjd uu_avl_index_t idx; 2414219089Spjd nvlist_t *props; 2415219089Spjd us_node_t *n; 2416219089Spjd zfs_sort_column_t *sortcol = cb->cb_sortcol; 2417219089Spjd unsigned type; 2418219089Spjd const char *typestr; 2419219089Spjd size_t namelen; 2420219089Spjd size_t typelen; 2421219089Spjd size_t sizelen; 2422240415Smm int typeidx, nameidx, sizeidx; 2423219089Spjd us_sort_info_t sortinfo = { sortcol, cb->cb_numname }; 2424240415Smm boolean_t smbentity = B_FALSE; 2425209962Smm 2426240415Smm if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 2427240415Smm nomem(); 2428240415Smm node = safe_malloc(sizeof (us_node_t)); 2429240415Smm uu_avl_node_init(node, &node->usn_avlnode, avl_pool); 2430240415Smm node->usn_nvl = props; 2431240415Smm 2432240415Smm if (domain != NULL && domain[0] != '\0') { 2433240415Smm /* SMB */ 2434240415Smm char sid[ZFS_MAXNAMELEN + 32]; 2435219089Spjd uid_t id; 2436219089Spjd#ifdef sun 2437240415Smm int err; 2438277552Sdelphij int flag = IDMAP_REQ_FLG_USE_CACHE; 2439219089Spjd#endif 2440219089Spjd 2441240415Smm smbentity = B_TRUE; 2442240415Smm 2443219089Spjd (void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid); 2444240415Smm 2445219089Spjd if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) { 2446219089Spjd type = USTYPE_SMB_GRP; 2447219089Spjd#ifdef sun 2448219089Spjd err = sid_to_id(sid, B_FALSE, &id); 2449219089Spjd#endif 2450219089Spjd } else { 2451219089Spjd type = USTYPE_SMB_USR; 2452219089Spjd#ifdef sun 2453219089Spjd err = sid_to_id(sid, B_TRUE, &id); 2454219089Spjd#endif 2455219089Spjd } 2456219089Spjd 2457219089Spjd#ifdef sun 2458219089Spjd if (err == 0) { 2459219089Spjd rid = id; 2460240415Smm if (!cb->cb_sid2posix) { 2461277552Sdelphij if (type == USTYPE_SMB_USR) { 2462277552Sdelphij (void) idmap_getwinnamebyuid(rid, flag, 2463277552Sdelphij &name, NULL); 2464277552Sdelphij } else { 2465277552Sdelphij (void) idmap_getwinnamebygid(rid, flag, 2466277552Sdelphij &name, NULL); 2467277552Sdelphij } 2468240415Smm if (name == NULL) 2469240415Smm name = sid; 2470219089Spjd } 2471219089Spjd } 2472219089Spjd#endif 2473209962Smm } 2474209962Smm 2475240415Smm if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') { 2476240415Smm /* POSIX or -i */ 2477240415Smm if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) { 2478240415Smm type = USTYPE_PSX_GRP; 2479240415Smm if (!cb->cb_numname) { 2480240415Smm struct group *g; 2481209962Smm 2482240415Smm if ((g = getgrgid(rid)) != NULL) 2483240415Smm name = g->gr_name; 2484240415Smm } 2485240415Smm } else { 2486240415Smm type = USTYPE_PSX_USR; 2487240415Smm if (!cb->cb_numname) { 2488240415Smm struct passwd *p; 2489209962Smm 2490240415Smm if ((p = getpwuid(rid)) != NULL) 2491240415Smm name = p->pw_name; 2492240415Smm } 2493240415Smm } 2494240415Smm } 2495219089Spjd 2496240415Smm /* 2497240415Smm * Make sure that the type/name combination is unique when doing 2498240415Smm * SID to POSIX ID translation (hence changing the type from SMB to 2499240415Smm * POSIX). 2500240415Smm */ 2501240415Smm if (cb->cb_sid2posix && 2502240415Smm nvlist_add_boolean_value(props, "smbentity", smbentity) != 0) 2503240415Smm nomem(); 2504219089Spjd 2505240415Smm /* Calculate/update width of TYPE field */ 2506240415Smm typestr = us_type2str(type); 2507240415Smm typelen = strlen(gettext(typestr)); 2508240415Smm typeidx = us_field_index("type"); 2509240415Smm if (typelen > cb->cb_width[typeidx]) 2510240415Smm cb->cb_width[typeidx] = typelen; 2511219089Spjd if (nvlist_add_uint32(props, "type", type) != 0) 2512219089Spjd nomem(); 2513209962Smm 2514240415Smm /* Calculate/update width of NAME field */ 2515240415Smm if ((cb->cb_numname && cb->cb_sid2posix) || name == NULL) { 2516240415Smm if (nvlist_add_uint64(props, "name", rid) != 0) 2517219089Spjd nomem(); 2518240415Smm namelen = snprintf(NULL, 0, "%u", rid); 2519219089Spjd } else { 2520219089Spjd if (nvlist_add_string(props, "name", name) != 0) 2521219089Spjd nomem(); 2522219089Spjd namelen = strlen(name); 2523219089Spjd } 2524240415Smm nameidx = us_field_index("name"); 2525240415Smm if (namelen > cb->cb_width[nameidx]) 2526240415Smm cb->cb_width[nameidx] = namelen; 2527219089Spjd 2528240415Smm /* 2529240415Smm * Check if this type/name combination is in the list and update it; 2530240415Smm * otherwise add new node to the list. 2531240415Smm */ 2532240415Smm if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) { 2533240415Smm uu_avl_insert(avl, node, idx); 2534219089Spjd } else { 2535219089Spjd nvlist_free(props); 2536219089Spjd free(node); 2537219089Spjd node = n; 2538219089Spjd props = node->usn_nvl; 2539219089Spjd } 2540219089Spjd 2541240415Smm /* Calculate/update width of USED/QUOTA fields */ 2542240415Smm if (cb->cb_nicenum) 2543240415Smm zfs_nicenum(space, sizebuf, sizeof (sizebuf)); 2544240415Smm else 2545240415Smm (void) snprintf(sizebuf, sizeof (sizebuf), "%llu", space); 2546240415Smm sizelen = strlen(sizebuf); 2547240415Smm if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED) { 2548240415Smm propname = "used"; 2549240415Smm if (!nvlist_exists(props, "quota")) 2550240415Smm (void) nvlist_add_uint64(props, "quota", 0); 2551240415Smm } else { 2552240415Smm propname = "quota"; 2553240415Smm if (!nvlist_exists(props, "used")) 2554240415Smm (void) nvlist_add_uint64(props, "used", 0); 2555240415Smm } 2556240415Smm sizeidx = us_field_index(propname); 2557240415Smm if (sizelen > cb->cb_width[sizeidx]) 2558240415Smm cb->cb_width[sizeidx] = sizelen; 2559240415Smm 2560219089Spjd if (nvlist_add_uint64(props, propname, space) != 0) 2561219089Spjd nomem(); 2562219089Spjd 2563209962Smm return (0); 2564209962Smm} 2565209962Smm 2566219089Spjdstatic void 2567240415Smmprint_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types, 2568240415Smm size_t *width, us_node_t *node) 2569219089Spjd{ 2570219089Spjd nvlist_t *nvl = node->usn_nvl; 2571219089Spjd char valstr[ZFS_MAXNAMELEN]; 2572219089Spjd boolean_t first = B_TRUE; 2573240415Smm int cfield = 0; 2574240415Smm int field; 2575240415Smm uint32_t ustype; 2576219089Spjd 2577240415Smm /* Check type */ 2578240415Smm (void) nvlist_lookup_uint32(nvl, "type", &ustype); 2579240415Smm if (!(ustype & types)) 2580240415Smm return; 2581219089Spjd 2582240415Smm while ((field = fields[cfield]) != USFIELD_LAST) { 2583240415Smm nvpair_t *nvp = NULL; 2584240415Smm data_type_t type; 2585240415Smm uint32_t val32; 2586240415Smm uint64_t val64; 2587219089Spjd char *strval = NULL; 2588240415Smm 2589240415Smm while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 2590240415Smm if (strcmp(nvpair_name(nvp), 2591240415Smm us_field_names[field]) == 0) 2592219089Spjd break; 2593219089Spjd } 2594219089Spjd 2595240415Smm type = nvpair_type(nvp); 2596219089Spjd switch (type) { 2597219089Spjd case DATA_TYPE_UINT32: 2598219089Spjd (void) nvpair_value_uint32(nvp, &val32); 2599219089Spjd break; 2600219089Spjd case DATA_TYPE_UINT64: 2601219089Spjd (void) nvpair_value_uint64(nvp, &val64); 2602219089Spjd break; 2603219089Spjd case DATA_TYPE_STRING: 2604219089Spjd (void) nvpair_value_string(nvp, &strval); 2605219089Spjd break; 2606219089Spjd default: 2607240415Smm (void) fprintf(stderr, "invalid data type\n"); 2608219089Spjd } 2609219089Spjd 2610219089Spjd switch (field) { 2611219089Spjd case USFIELD_TYPE: 2612219089Spjd strval = (char *)us_type2str(val32); 2613219089Spjd break; 2614219089Spjd case USFIELD_NAME: 2615219089Spjd if (type == DATA_TYPE_UINT64) { 2616219089Spjd (void) sprintf(valstr, "%llu", val64); 2617219089Spjd strval = valstr; 2618219089Spjd } 2619219089Spjd break; 2620219089Spjd case USFIELD_USED: 2621219089Spjd case USFIELD_QUOTA: 2622219089Spjd if (type == DATA_TYPE_UINT64) { 2623240415Smm if (parsable) { 2624219089Spjd (void) sprintf(valstr, "%llu", val64); 2625240415Smm } else { 2626219089Spjd zfs_nicenum(val64, valstr, 2627219089Spjd sizeof (valstr)); 2628240415Smm } 2629240415Smm if (field == USFIELD_QUOTA && 2630240415Smm strcmp(valstr, "0") == 0) 2631240415Smm strval = "none"; 2632240415Smm else 2633240415Smm strval = valstr; 2634219089Spjd } 2635219089Spjd break; 2636219089Spjd } 2637219089Spjd 2638240415Smm if (!first) { 2639240415Smm if (scripted) 2640240415Smm (void) printf("\t"); 2641219089Spjd else 2642240415Smm (void) printf(" "); 2643219089Spjd } 2644240415Smm if (scripted) 2645240415Smm (void) printf("%s", strval); 2646240415Smm else if (field == USFIELD_TYPE || field == USFIELD_NAME) 2647240415Smm (void) printf("%-*s", width[field], strval); 2648240415Smm else 2649240415Smm (void) printf("%*s", width[field], strval); 2650219089Spjd 2651219089Spjd first = B_FALSE; 2652240415Smm cfield++; 2653219089Spjd } 2654219089Spjd 2655219089Spjd (void) printf("\n"); 2656219089Spjd} 2657219089Spjd 2658219089Spjdstatic void 2659240415Smmprint_us(boolean_t scripted, boolean_t parsable, int *fields, int types, 2660240415Smm size_t *width, boolean_t rmnode, uu_avl_t *avl) 2661219089Spjd{ 2662219089Spjd us_node_t *node; 2663219089Spjd const char *col; 2664240415Smm int cfield = 0; 2665240415Smm int field; 2666219089Spjd 2667219089Spjd if (!scripted) { 2668219089Spjd boolean_t first = B_TRUE; 2669219089Spjd 2670240415Smm while ((field = fields[cfield]) != USFIELD_LAST) { 2671240415Smm col = gettext(us_field_hdr[field]); 2672240415Smm if (field == USFIELD_TYPE || field == USFIELD_NAME) { 2673240415Smm (void) printf(first ? "%-*s" : " %-*s", 2674240415Smm width[field], col); 2675240415Smm } else { 2676240415Smm (void) printf(first ? "%*s" : " %*s", 2677240415Smm width[field], col); 2678240415Smm } 2679219089Spjd first = B_FALSE; 2680240415Smm cfield++; 2681219089Spjd } 2682219089Spjd (void) printf("\n"); 2683219089Spjd } 2684219089Spjd 2685240415Smm for (node = uu_avl_first(avl); node; node = uu_avl_next(avl, node)) { 2686240415Smm print_us_node(scripted, parsable, fields, types, width, node); 2687219089Spjd if (rmnode) 2688219089Spjd nvlist_free(node->usn_nvl); 2689219089Spjd } 2690219089Spjd} 2691219089Spjd 2692219089Spjdstatic int 2693209962Smmzfs_do_userspace(int argc, char **argv) 2694209962Smm{ 2695209962Smm zfs_handle_t *zhp; 2696209962Smm zfs_userquota_prop_t p; 2697219089Spjd 2698219089Spjd uu_avl_pool_t *avl_pool; 2699219089Spjd uu_avl_t *avl_tree; 2700219089Spjd uu_avl_walk_t *walk; 2701240415Smm char *delim; 2702240415Smm char deffields[] = "type,name,used,quota"; 2703240415Smm char *ofield = NULL; 2704240415Smm char *tfield = NULL; 2705240415Smm int cfield = 0; 2706240415Smm int fields[256]; 2707240415Smm int i; 2708219089Spjd boolean_t scripted = B_FALSE; 2709219089Spjd boolean_t prtnum = B_FALSE; 2710240415Smm boolean_t parsable = B_FALSE; 2711219089Spjd boolean_t sid2posix = B_FALSE; 2712240415Smm int ret = 0; 2713219089Spjd int c; 2714219089Spjd zfs_sort_column_t *sortcol = NULL; 2715240415Smm int types = USTYPE_PSX_USR | USTYPE_SMB_USR; 2716219089Spjd us_cbdata_t cb; 2717219089Spjd us_node_t *node; 2718240415Smm us_node_t *rmnode; 2719240415Smm uu_list_pool_t *listpool; 2720240415Smm uu_list_t *list; 2721240415Smm uu_avl_index_t idx = 0; 2722240415Smm uu_list_index_t idx2 = 0; 2723209962Smm 2724219089Spjd if (argc < 2) 2725219089Spjd usage(B_FALSE); 2726209962Smm 2727240415Smm if (strcmp(argv[0], "groupspace") == 0) 2728240415Smm /* Toggle default group types */ 2729219089Spjd types = USTYPE_PSX_GRP | USTYPE_SMB_GRP; 2730209962Smm 2731219089Spjd while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) { 2732219089Spjd switch (c) { 2733219089Spjd case 'n': 2734219089Spjd prtnum = B_TRUE; 2735219089Spjd break; 2736219089Spjd case 'H': 2737219089Spjd scripted = B_TRUE; 2738219089Spjd break; 2739219089Spjd case 'p': 2740240415Smm parsable = B_TRUE; 2741219089Spjd break; 2742219089Spjd case 'o': 2743240415Smm ofield = optarg; 2744219089Spjd break; 2745219089Spjd case 's': 2746219089Spjd case 'S': 2747219089Spjd if (zfs_add_sort_column(&sortcol, optarg, 2748240415Smm c == 's' ? B_FALSE : B_TRUE) != 0) { 2749219089Spjd (void) fprintf(stderr, 2750240415Smm gettext("invalid field '%s'\n"), optarg); 2751219089Spjd usage(B_FALSE); 2752219089Spjd } 2753219089Spjd break; 2754219089Spjd case 't': 2755240415Smm tfield = optarg; 2756219089Spjd break; 2757219089Spjd case 'i': 2758219089Spjd sid2posix = B_TRUE; 2759219089Spjd break; 2760219089Spjd case ':': 2761219089Spjd (void) fprintf(stderr, gettext("missing argument for " 2762219089Spjd "'%c' option\n"), optopt); 2763219089Spjd usage(B_FALSE); 2764219089Spjd break; 2765219089Spjd case '?': 2766219089Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2767219089Spjd optopt); 2768219089Spjd usage(B_FALSE); 2769219089Spjd } 2770219089Spjd } 2771219089Spjd 2772219089Spjd argc -= optind; 2773219089Spjd argv += optind; 2774219089Spjd 2775240415Smm if (argc < 1) { 2776240415Smm (void) fprintf(stderr, gettext("missing dataset name\n")); 2777240415Smm usage(B_FALSE); 2778240415Smm } 2779240415Smm if (argc > 1) { 2780240415Smm (void) fprintf(stderr, gettext("too many arguments\n")); 2781240415Smm usage(B_FALSE); 2782240415Smm } 2783240415Smm 2784240415Smm /* Use default output fields if not specified using -o */ 2785240415Smm if (ofield == NULL) 2786240415Smm ofield = deffields; 2787240415Smm do { 2788240415Smm if ((delim = strchr(ofield, ',')) != NULL) 2789240415Smm *delim = '\0'; 2790240415Smm if ((fields[cfield++] = us_field_index(ofield)) == -1) { 2791240415Smm (void) fprintf(stderr, gettext("invalid type '%s' " 2792240415Smm "for -o option\n"), ofield); 2793240415Smm return (-1); 2794240415Smm } 2795240415Smm if (delim != NULL) 2796240415Smm ofield = delim + 1; 2797240415Smm } while (delim != NULL); 2798240415Smm fields[cfield] = USFIELD_LAST; 2799240415Smm 2800240415Smm /* Override output types (-t option) */ 2801240415Smm if (tfield != NULL) { 2802240415Smm types = 0; 2803240415Smm 2804240415Smm do { 2805240415Smm boolean_t found = B_FALSE; 2806240415Smm 2807240415Smm if ((delim = strchr(tfield, ',')) != NULL) 2808240415Smm *delim = '\0'; 2809240415Smm for (i = 0; i < sizeof (us_type_bits) / sizeof (int); 2810240415Smm i++) { 2811240415Smm if (strcmp(tfield, us_type_names[i]) == 0) { 2812240415Smm found = B_TRUE; 2813240415Smm types |= us_type_bits[i]; 2814240415Smm break; 2815240415Smm } 2816219089Spjd } 2817240415Smm if (!found) { 2818240415Smm (void) fprintf(stderr, gettext("invalid type " 2819240415Smm "'%s' for -t option\n"), tfield); 2820240415Smm return (-1); 2821240415Smm } 2822240415Smm if (delim != NULL) 2823240415Smm tfield = delim + 1; 2824240415Smm } while (delim != NULL); 2825219089Spjd } 2826219089Spjd 2827240415Smm if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL) 2828209962Smm return (1); 2829209962Smm 2830219089Spjd if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t), 2831240415Smm offsetof(us_node_t, usn_avlnode), us_compare, UU_DEFAULT)) == NULL) 2832219089Spjd nomem(); 2833219089Spjd if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) 2834219089Spjd nomem(); 2835209962Smm 2836240415Smm /* Always add default sorting columns */ 2837240415Smm (void) zfs_add_sort_column(&sortcol, "type", B_FALSE); 2838240415Smm (void) zfs_add_sort_column(&sortcol, "name", B_FALSE); 2839240415Smm 2840240415Smm cb.cb_sortcol = sortcol; 2841219089Spjd cb.cb_numname = prtnum; 2842240415Smm cb.cb_nicenum = !parsable; 2843219089Spjd cb.cb_avl_pool = avl_pool; 2844219089Spjd cb.cb_avl = avl_tree; 2845219089Spjd cb.cb_sid2posix = sid2posix; 2846219089Spjd 2847240415Smm for (i = 0; i < USFIELD_LAST; i++) 2848240415Smm cb.cb_width[i] = strlen(gettext(us_field_hdr[i])); 2849240415Smm 2850209962Smm for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) { 2851240415Smm if (((p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA) && 2852240415Smm !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) || 2853240415Smm ((p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) && 2854240415Smm !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP)))) 2855219089Spjd continue; 2856219089Spjd cb.cb_prop = p; 2857240415Smm if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0) 2858240415Smm return (ret); 2859209962Smm } 2860219089Spjd 2861240415Smm /* Sort the list */ 2862240415Smm if ((node = uu_avl_first(avl_tree)) == NULL) 2863240415Smm return (0); 2864219089Spjd 2865240415Smm us_populated = B_TRUE; 2866219089Spjd 2867240415Smm listpool = uu_list_pool_create("tmplist", sizeof (us_node_t), 2868240415Smm offsetof(us_node_t, usn_listnode), NULL, UU_DEFAULT); 2869240415Smm list = uu_list_create(listpool, NULL, UU_DEFAULT); 2870240415Smm uu_list_node_init(node, &node->usn_listnode, listpool); 2871219089Spjd 2872240415Smm while (node != NULL) { 2873240415Smm rmnode = node; 2874240415Smm node = uu_avl_next(avl_tree, node); 2875240415Smm uu_avl_remove(avl_tree, rmnode); 2876240415Smm if (uu_list_find(list, rmnode, NULL, &idx2) == NULL) 2877240415Smm uu_list_insert(list, rmnode, idx2); 2878219089Spjd } 2879219089Spjd 2880240415Smm for (node = uu_list_first(list); node != NULL; 2881240415Smm node = uu_list_next(list, node)) { 2882240415Smm us_sort_info_t sortinfo = { sortcol, cb.cb_numname }; 2883219089Spjd 2884240415Smm if (uu_avl_find(avl_tree, node, &sortinfo, &idx) == NULL) 2885240415Smm uu_avl_insert(avl_tree, node, idx); 2886240415Smm } 2887219089Spjd 2888240415Smm uu_list_destroy(list); 2889240415Smm uu_list_pool_destroy(listpool); 2890240415Smm 2891240415Smm /* Print and free node nvlist memory */ 2892240415Smm print_us(scripted, parsable, fields, types, cb.cb_width, B_TRUE, 2893240415Smm cb.cb_avl); 2894240415Smm 2895240415Smm zfs_free_sort_columns(sortcol); 2896240415Smm 2897240415Smm /* Clean up the AVL tree */ 2898219089Spjd if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) 2899219089Spjd nomem(); 2900219089Spjd 2901219089Spjd while ((node = uu_avl_walk_next(walk)) != NULL) { 2902219089Spjd uu_avl_remove(cb.cb_avl, node); 2903219089Spjd free(node); 2904219089Spjd } 2905219089Spjd 2906219089Spjd uu_avl_walk_end(walk); 2907219089Spjd uu_avl_destroy(avl_tree); 2908219089Spjd uu_avl_pool_destroy(avl_pool); 2909219089Spjd 2910240415Smm return (ret); 2911209962Smm} 2912209962Smm 2913209962Smm/* 2914263405Sdelphij * list [-Hp][-r|-d max] [-o property[,...]] [-s property] ... [-S property] ... 2915263405Sdelphij * [-t type[,...]] [filesystem|volume|snapshot] ... 2916168404Spjd * 2917263405Sdelphij * -H Scripted mode; elide headers and separate columns by tabs. 2918263405Sdelphij * -p Display values in parsable (literal) format. 2919263405Sdelphij * -r Recurse over all children. 2920219089Spjd * -d Limit recursion by depth. 2921219089Spjd * -o Control which fields to display. 2922168404Spjd * -s Specify sort columns, descending order. 2923168404Spjd * -S Specify sort columns, ascending order. 2924263405Sdelphij * -t Control which object types to display. 2925168404Spjd * 2926263405Sdelphij * When given no arguments, list all filesystems in the system. 2927168404Spjd * Otherwise, list the specified datasets, optionally recursing down them if 2928168404Spjd * '-r' is specified. 2929168404Spjd */ 2930168404Spjdtypedef struct list_cbdata { 2931168404Spjd boolean_t cb_first; 2932263405Sdelphij boolean_t cb_literal; 2933168404Spjd boolean_t cb_scripted; 2934185029Spjd zprop_list_t *cb_proplist; 2935168404Spjd} list_cbdata_t; 2936168404Spjd 2937168404Spjd/* 2938168404Spjd * Given a list of columns to display, output appropriate headers for each one. 2939168404Spjd */ 2940168404Spjdstatic void 2941263405Sdelphijprint_header(list_cbdata_t *cb) 2942168404Spjd{ 2943263405Sdelphij zprop_list_t *pl = cb->cb_proplist; 2944168404Spjd char headerbuf[ZFS_MAXPROPLEN]; 2945168404Spjd const char *header; 2946168404Spjd int i; 2947168404Spjd boolean_t first = B_TRUE; 2948168404Spjd boolean_t right_justify; 2949168404Spjd 2950168404Spjd for (; pl != NULL; pl = pl->pl_next) { 2951168404Spjd if (!first) { 2952168404Spjd (void) printf(" "); 2953168404Spjd } else { 2954168404Spjd first = B_FALSE; 2955168404Spjd } 2956168404Spjd 2957168404Spjd right_justify = B_FALSE; 2958185029Spjd if (pl->pl_prop != ZPROP_INVAL) { 2959168404Spjd header = zfs_prop_column_name(pl->pl_prop); 2960168404Spjd right_justify = zfs_prop_align_right(pl->pl_prop); 2961168404Spjd } else { 2962168404Spjd for (i = 0; pl->pl_user_prop[i] != '\0'; i++) 2963168404Spjd headerbuf[i] = toupper(pl->pl_user_prop[i]); 2964168404Spjd headerbuf[i] = '\0'; 2965168404Spjd header = headerbuf; 2966168404Spjd } 2967168404Spjd 2968168404Spjd if (pl->pl_next == NULL && !right_justify) 2969168404Spjd (void) printf("%s", header); 2970168404Spjd else if (right_justify) 2971168404Spjd (void) printf("%*s", pl->pl_width, header); 2972168404Spjd else 2973168404Spjd (void) printf("%-*s", pl->pl_width, header); 2974168404Spjd } 2975168404Spjd 2976168404Spjd (void) printf("\n"); 2977168404Spjd} 2978168404Spjd 2979168404Spjd/* 2980168404Spjd * Given a dataset and a list of fields, print out all the properties according 2981168404Spjd * to the described layout. 2982168404Spjd */ 2983168404Spjdstatic void 2984263405Sdelphijprint_dataset(zfs_handle_t *zhp, list_cbdata_t *cb) 2985168404Spjd{ 2986263405Sdelphij zprop_list_t *pl = cb->cb_proplist; 2987168404Spjd boolean_t first = B_TRUE; 2988168404Spjd char property[ZFS_MAXPROPLEN]; 2989168404Spjd nvlist_t *userprops = zfs_get_user_props(zhp); 2990168404Spjd nvlist_t *propval; 2991168404Spjd char *propstr; 2992168404Spjd boolean_t right_justify; 2993168404Spjd 2994168404Spjd for (; pl != NULL; pl = pl->pl_next) { 2995168404Spjd if (!first) { 2996263405Sdelphij if (cb->cb_scripted) 2997168404Spjd (void) printf("\t"); 2998168404Spjd else 2999168404Spjd (void) printf(" "); 3000168404Spjd } else { 3001168404Spjd first = B_FALSE; 3002168404Spjd } 3003168404Spjd 3004230438Spjd if (pl->pl_prop == ZFS_PROP_NAME) { 3005230438Spjd (void) strlcpy(property, zfs_get_name(zhp), 3006230438Spjd sizeof(property)); 3007230438Spjd propstr = property; 3008230438Spjd right_justify = zfs_prop_align_right(pl->pl_prop); 3009230438Spjd } else if (pl->pl_prop != ZPROP_INVAL) { 3010168404Spjd if (zfs_prop_get(zhp, pl->pl_prop, property, 3011263405Sdelphij sizeof (property), NULL, NULL, 0, 3012263405Sdelphij cb->cb_literal) != 0) 3013168404Spjd propstr = "-"; 3014168404Spjd else 3015168404Spjd propstr = property; 3016168404Spjd right_justify = zfs_prop_align_right(pl->pl_prop); 3017209962Smm } else if (zfs_prop_userquota(pl->pl_user_prop)) { 3018209962Smm if (zfs_prop_get_userquota(zhp, pl->pl_user_prop, 3019263405Sdelphij property, sizeof (property), cb->cb_literal) != 0) 3020209962Smm propstr = "-"; 3021209962Smm else 3022209962Smm propstr = property; 3023209962Smm right_justify = B_TRUE; 3024228103Smm } else if (zfs_prop_written(pl->pl_user_prop)) { 3025228103Smm if (zfs_prop_get_written(zhp, pl->pl_user_prop, 3026263405Sdelphij property, sizeof (property), cb->cb_literal) != 0) 3027228103Smm propstr = "-"; 3028228103Smm else 3029228103Smm propstr = property; 3030228103Smm right_justify = B_TRUE; 3031168404Spjd } else { 3032168404Spjd if (nvlist_lookup_nvlist(userprops, 3033168404Spjd pl->pl_user_prop, &propval) != 0) 3034168404Spjd propstr = "-"; 3035168404Spjd else 3036168404Spjd verify(nvlist_lookup_string(propval, 3037185029Spjd ZPROP_VALUE, &propstr) == 0); 3038209962Smm right_justify = B_FALSE; 3039168404Spjd } 3040168404Spjd 3041168404Spjd /* 3042168404Spjd * If this is being called in scripted mode, or if this is the 3043168404Spjd * last column and it is left-justified, don't include a width 3044168404Spjd * format specifier. 3045168404Spjd */ 3046263405Sdelphij if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify)) 3047168404Spjd (void) printf("%s", propstr); 3048168404Spjd else if (right_justify) 3049263405Sdelphij (void) printf("%*s", pl->pl_width, propstr); 3050168404Spjd else 3051263405Sdelphij (void) printf("%-*s", pl->pl_width, propstr); 3052168404Spjd } 3053168404Spjd 3054168404Spjd (void) printf("\n"); 3055168404Spjd} 3056168404Spjd 3057168404Spjd/* 3058168404Spjd * Generic callback function to list a dataset or snapshot. 3059168404Spjd */ 3060168404Spjdstatic int 3061168404Spjdlist_callback(zfs_handle_t *zhp, void *data) 3062168404Spjd{ 3063168404Spjd list_cbdata_t *cbp = data; 3064168404Spjd 3065168404Spjd if (cbp->cb_first) { 3066168404Spjd if (!cbp->cb_scripted) 3067263405Sdelphij print_header(cbp); 3068168404Spjd cbp->cb_first = B_FALSE; 3069168404Spjd } 3070168404Spjd 3071263405Sdelphij print_dataset(zhp, cbp); 3072168404Spjd 3073168404Spjd return (0); 3074168404Spjd} 3075168404Spjd 3076168404Spjdstatic int 3077168404Spjdzfs_do_list(int argc, char **argv) 3078168404Spjd{ 3079168404Spjd int c; 3080168404Spjd static char default_fields[] = 3081168404Spjd "name,used,available,referenced,mountpoint"; 3082207627Smm int types = ZFS_TYPE_DATASET; 3083185029Spjd boolean_t types_specified = B_FALSE; 3084168404Spjd char *fields = NULL; 3085168404Spjd list_cbdata_t cb = { 0 }; 3086168404Spjd char *value; 3087205199Sdelphij int limit = 0; 3088231144Smm int ret = 0; 3089168404Spjd zfs_sort_column_t *sortcol = NULL; 3090185029Spjd int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS; 3091168404Spjd 3092168404Spjd /* check options */ 3093263405Sdelphij while ((c = getopt(argc, argv, "HS:d:o:prs:t:")) != -1) { 3094168404Spjd switch (c) { 3095168404Spjd case 'o': 3096168404Spjd fields = optarg; 3097168404Spjd break; 3098263405Sdelphij case 'p': 3099263405Sdelphij cb.cb_literal = B_TRUE; 3100263405Sdelphij flags |= ZFS_ITER_LITERAL_PROPS; 3101263405Sdelphij break; 3102205199Sdelphij case 'd': 3103205199Sdelphij limit = parse_depth(optarg, &flags); 3104205199Sdelphij break; 3105168404Spjd case 'r': 3106185029Spjd flags |= ZFS_ITER_RECURSE; 3107168404Spjd break; 3108168404Spjd case 'H': 3109263405Sdelphij cb.cb_scripted = B_TRUE; 3110168404Spjd break; 3111168404Spjd case 's': 3112168404Spjd if (zfs_add_sort_column(&sortcol, optarg, 3113168404Spjd B_FALSE) != 0) { 3114168404Spjd (void) fprintf(stderr, 3115168404Spjd gettext("invalid property '%s'\n"), optarg); 3116168404Spjd usage(B_FALSE); 3117168404Spjd } 3118168404Spjd break; 3119168404Spjd case 'S': 3120168404Spjd if (zfs_add_sort_column(&sortcol, optarg, 3121168404Spjd B_TRUE) != 0) { 3122168404Spjd (void) fprintf(stderr, 3123168404Spjd gettext("invalid property '%s'\n"), optarg); 3124168404Spjd usage(B_FALSE); 3125168404Spjd } 3126168404Spjd break; 3127168404Spjd case 't': 3128168404Spjd types = 0; 3129185029Spjd types_specified = B_TRUE; 3130185029Spjd flags &= ~ZFS_ITER_PROP_LISTSNAPS; 3131168404Spjd while (*optarg != '\0') { 3132185029Spjd static char *type_subopts[] = { "filesystem", 3133263407Sdelphij "volume", "snapshot", "snap", "bookmark", 3134263407Sdelphij "all", NULL }; 3135185029Spjd 3136168404Spjd switch (getsubopt(&optarg, type_subopts, 3137168404Spjd &value)) { 3138168404Spjd case 0: 3139168404Spjd types |= ZFS_TYPE_FILESYSTEM; 3140168404Spjd break; 3141168404Spjd case 1: 3142168404Spjd types |= ZFS_TYPE_VOLUME; 3143168404Spjd break; 3144168404Spjd case 2: 3145263403Sdelphij case 3: 3146168404Spjd types |= ZFS_TYPE_SNAPSHOT; 3147168404Spjd break; 3148263403Sdelphij case 4: 3149263407Sdelphij types |= ZFS_TYPE_BOOKMARK; 3150185029Spjd break; 3151263407Sdelphij case 5: 3152263407Sdelphij types = ZFS_TYPE_DATASET | 3153263407Sdelphij ZFS_TYPE_BOOKMARK; 3154263407Sdelphij break; 3155168404Spjd default: 3156168404Spjd (void) fprintf(stderr, 3157168404Spjd gettext("invalid type '%s'\n"), 3158168404Spjd value); 3159168404Spjd usage(B_FALSE); 3160168404Spjd } 3161168404Spjd } 3162168404Spjd break; 3163168404Spjd case ':': 3164168404Spjd (void) fprintf(stderr, gettext("missing argument for " 3165168404Spjd "'%c' option\n"), optopt); 3166168404Spjd usage(B_FALSE); 3167168404Spjd break; 3168168404Spjd case '?': 3169168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3170168404Spjd optopt); 3171168404Spjd usage(B_FALSE); 3172168404Spjd } 3173168404Spjd } 3174168404Spjd 3175168404Spjd argc -= optind; 3176168404Spjd argv += optind; 3177168404Spjd 3178168404Spjd if (fields == NULL) 3179185029Spjd fields = default_fields; 3180168404Spjd 3181168404Spjd /* 3182230438Spjd * If we are only going to list snapshot names and sort by name, 3183230438Spjd * then we can use faster version. 3184230438Spjd */ 3185230438Spjd if (strcmp(fields, "name") == 0 && zfs_sort_only_by_name(sortcol)) 3186230438Spjd flags |= ZFS_ITER_SIMPLE; 3187230438Spjd 3188230438Spjd /* 3189185029Spjd * If "-o space" and no types were specified, don't display snapshots. 3190185029Spjd */ 3191185029Spjd if (strcmp(fields, "space") == 0 && types_specified == B_FALSE) 3192185029Spjd types &= ~ZFS_TYPE_SNAPSHOT; 3193185029Spjd 3194185029Spjd /* 3195185029Spjd * If the user specifies '-o all', the zprop_get_list() doesn't 3196168404Spjd * normally include the name of the dataset. For 'zfs list', we always 3197168404Spjd * want this property to be first. 3198168404Spjd */ 3199185029Spjd if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET) 3200185029Spjd != 0) 3201168404Spjd usage(B_FALSE); 3202168404Spjd 3203168404Spjd cb.cb_first = B_TRUE; 3204168404Spjd 3205185029Spjd ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist, 3206205199Sdelphij limit, list_callback, &cb); 3207168404Spjd 3208185029Spjd zprop_free_list(cb.cb_proplist); 3209168404Spjd zfs_free_sort_columns(sortcol); 3210168404Spjd 3211185029Spjd if (ret == 0 && cb.cb_first && !cb.cb_scripted) 3212168404Spjd (void) printf(gettext("no datasets available\n")); 3213168404Spjd 3214168404Spjd return (ret); 3215168404Spjd} 3216168404Spjd 3217168404Spjd/* 3218235216Smm * zfs rename [-f] <fs | snap | vol> <fs | snap | vol> 3219235216Smm * zfs rename [-f] -p <fs | vol> <fs | vol> 3220185029Spjd * zfs rename -r <snap> <snap> 3221226705Spjd * zfs rename -u [-p] <fs> <fs> 3222168404Spjd * 3223168404Spjd * Renames the given dataset to another of the same type. 3224185029Spjd * 3225185029Spjd * The '-p' flag creates all the non-existing ancestors of the target first. 3226168404Spjd */ 3227168404Spjd/* ARGSUSED */ 3228168404Spjdstatic int 3229168404Spjdzfs_do_rename(int argc, char **argv) 3230168404Spjd{ 3231168404Spjd zfs_handle_t *zhp; 3232226705Spjd renameflags_t flags = { 0 }; 3233231144Smm int c; 3234231144Smm int ret = 0; 3235231144Smm int types; 3236185029Spjd boolean_t parents = B_FALSE; 3237240870Spjd char *snapshot = NULL; 3238168404Spjd 3239168404Spjd /* check options */ 3240235216Smm while ((c = getopt(argc, argv, "fpru")) != -1) { 3241168676Spjd switch (c) { 3242185029Spjd case 'p': 3243185029Spjd parents = B_TRUE; 3244185029Spjd break; 3245168676Spjd case 'r': 3246226705Spjd flags.recurse = B_TRUE; 3247168676Spjd break; 3248226705Spjd case 'u': 3249226705Spjd flags.nounmount = B_TRUE; 3250226705Spjd break; 3251235216Smm case 'f': 3252235216Smm flags.forceunmount = B_TRUE; 3253235216Smm break; 3254168676Spjd case '?': 3255168676Spjd default: 3256168676Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3257168676Spjd optopt); 3258168676Spjd usage(B_FALSE); 3259168676Spjd } 3260168404Spjd } 3261168404Spjd 3262168676Spjd argc -= optind; 3263168676Spjd argv += optind; 3264168676Spjd 3265168404Spjd /* check number of arguments */ 3266168676Spjd if (argc < 1) { 3267168404Spjd (void) fprintf(stderr, gettext("missing source dataset " 3268168404Spjd "argument\n")); 3269168404Spjd usage(B_FALSE); 3270168404Spjd } 3271168676Spjd if (argc < 2) { 3272168404Spjd (void) fprintf(stderr, gettext("missing target dataset " 3273168404Spjd "argument\n")); 3274168404Spjd usage(B_FALSE); 3275168404Spjd } 3276168676Spjd if (argc > 2) { 3277168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 3278168404Spjd usage(B_FALSE); 3279168404Spjd } 3280168404Spjd 3281226705Spjd if (flags.recurse && parents) { 3282185029Spjd (void) fprintf(stderr, gettext("-p and -r options are mutually " 3283185029Spjd "exclusive\n")); 3284185029Spjd usage(B_FALSE); 3285185029Spjd } 3286185029Spjd 3287226705Spjd if (flags.recurse && strchr(argv[0], '@') == 0) { 3288168676Spjd (void) fprintf(stderr, gettext("source dataset for recursive " 3289168676Spjd "rename must be a snapshot\n")); 3290168676Spjd usage(B_FALSE); 3291168676Spjd } 3292168676Spjd 3293226705Spjd if (flags.nounmount && parents) { 3294236013Spjd (void) fprintf(stderr, gettext("-u and -p options are mutually " 3295226705Spjd "exclusive\n")); 3296226705Spjd usage(B_FALSE); 3297226705Spjd } 3298226705Spjd 3299226705Spjd if (flags.nounmount) 3300226705Spjd types = ZFS_TYPE_FILESYSTEM; 3301226705Spjd else if (parents) 3302226705Spjd types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME; 3303226705Spjd else 3304226705Spjd types = ZFS_TYPE_DATASET; 3305226705Spjd 3306240870Spjd if (flags.recurse) { 3307240870Spjd /* 3308240870Spjd * When we do recursive rename we are fine when the given 3309240870Spjd * snapshot for the given dataset doesn't exist - it can 3310240870Spjd * still exists below. 3311240870Spjd */ 3312240870Spjd 3313240870Spjd snapshot = strchr(argv[0], '@'); 3314240870Spjd assert(snapshot != NULL); 3315240870Spjd *snapshot = '\0'; 3316240870Spjd snapshot++; 3317240870Spjd } 3318240870Spjd 3319226705Spjd if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) 3320168404Spjd return (1); 3321168404Spjd 3322185029Spjd /* If we were asked and the name looks good, try to create ancestors. */ 3323185029Spjd if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) && 3324185029Spjd zfs_create_ancestors(g_zfs, argv[1]) != 0) { 3325185029Spjd zfs_close(zhp); 3326185029Spjd return (1); 3327185029Spjd } 3328185029Spjd 3329240870Spjd ret = (zfs_rename(zhp, snapshot, argv[1], flags) != 0); 3330168404Spjd 3331168404Spjd zfs_close(zhp); 3332168404Spjd return (ret); 3333168404Spjd} 3334168404Spjd 3335168404Spjd/* 3336168404Spjd * zfs promote <fs> 3337168404Spjd * 3338168404Spjd * Promotes the given clone fs to be the parent 3339168404Spjd */ 3340168404Spjd/* ARGSUSED */ 3341168404Spjdstatic int 3342168404Spjdzfs_do_promote(int argc, char **argv) 3343168404Spjd{ 3344168404Spjd zfs_handle_t *zhp; 3345231144Smm int ret = 0; 3346168404Spjd 3347168404Spjd /* check options */ 3348168404Spjd if (argc > 1 && argv[1][0] == '-') { 3349168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3350168404Spjd argv[1][1]); 3351168404Spjd usage(B_FALSE); 3352168404Spjd } 3353168404Spjd 3354168404Spjd /* check number of arguments */ 3355168404Spjd if (argc < 2) { 3356168404Spjd (void) fprintf(stderr, gettext("missing clone filesystem" 3357168404Spjd " argument\n")); 3358168404Spjd usage(B_FALSE); 3359168404Spjd } 3360168404Spjd if (argc > 2) { 3361168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 3362168404Spjd usage(B_FALSE); 3363168404Spjd } 3364168404Spjd 3365168404Spjd zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 3366168404Spjd if (zhp == NULL) 3367168404Spjd return (1); 3368168404Spjd 3369168404Spjd ret = (zfs_promote(zhp) != 0); 3370168404Spjd 3371168404Spjd 3372168404Spjd zfs_close(zhp); 3373168404Spjd return (ret); 3374168404Spjd} 3375168404Spjd 3376168404Spjd/* 3377185029Spjd * zfs rollback [-rRf] <snapshot> 3378168404Spjd * 3379219089Spjd * -r Delete any intervening snapshots before doing rollback 3380219089Spjd * -R Delete any snapshots and their clones 3381219089Spjd * -f ignored for backwards compatability 3382168404Spjd * 3383168404Spjd * Given a filesystem, rollback to a specific snapshot, discarding any changes 3384168404Spjd * since then and making it the active dataset. If more recent snapshots exist, 3385168404Spjd * the command will complain unless the '-r' flag is given. 3386168404Spjd */ 3387168404Spjdtypedef struct rollback_cbdata { 3388168404Spjd uint64_t cb_create; 3389168404Spjd boolean_t cb_first; 3390168404Spjd int cb_doclones; 3391168404Spjd char *cb_target; 3392168404Spjd int cb_error; 3393168404Spjd boolean_t cb_recurse; 3394168404Spjd} rollback_cbdata_t; 3395168404Spjd 3396263407Sdelphijstatic int 3397263407Sdelphijrollback_check_dependent(zfs_handle_t *zhp, void *data) 3398263407Sdelphij{ 3399263407Sdelphij rollback_cbdata_t *cbp = data; 3400263407Sdelphij 3401263407Sdelphij if (cbp->cb_first && cbp->cb_recurse) { 3402263407Sdelphij (void) fprintf(stderr, gettext("cannot rollback to " 3403263407Sdelphij "'%s': clones of previous snapshots exist\n"), 3404263407Sdelphij cbp->cb_target); 3405263407Sdelphij (void) fprintf(stderr, gettext("use '-R' to " 3406263407Sdelphij "force deletion of the following clones and " 3407263407Sdelphij "dependents:\n")); 3408263407Sdelphij cbp->cb_first = 0; 3409263407Sdelphij cbp->cb_error = 1; 3410263407Sdelphij } 3411263407Sdelphij 3412263407Sdelphij (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 3413263407Sdelphij 3414263407Sdelphij zfs_close(zhp); 3415263407Sdelphij return (0); 3416263407Sdelphij} 3417268649Sdelphij 3418168404Spjd/* 3419168404Spjd * Report any snapshots more recent than the one specified. Used when '-r' is 3420168404Spjd * not specified. We reuse this same callback for the snapshot dependents - if 3421168404Spjd * 'cb_dependent' is set, then this is a dependent and we should report it 3422168404Spjd * without checking the transaction group. 3423168404Spjd */ 3424168404Spjdstatic int 3425168404Spjdrollback_check(zfs_handle_t *zhp, void *data) 3426168404Spjd{ 3427168404Spjd rollback_cbdata_t *cbp = data; 3428168404Spjd 3429168404Spjd if (cbp->cb_doclones) { 3430168404Spjd zfs_close(zhp); 3431168404Spjd return (0); 3432168404Spjd } 3433168404Spjd 3434263407Sdelphij if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) { 3435263407Sdelphij if (cbp->cb_first && !cbp->cb_recurse) { 3436263407Sdelphij (void) fprintf(stderr, gettext("cannot " 3437263407Sdelphij "rollback to '%s': more recent snapshots " 3438263407Sdelphij "or bookmarks exist\n"), 3439168404Spjd cbp->cb_target); 3440263407Sdelphij (void) fprintf(stderr, gettext("use '-r' to " 3441263407Sdelphij "force deletion of the following " 3442263407Sdelphij "snapshots and bookmarks:\n")); 3443168404Spjd cbp->cb_first = 0; 3444168404Spjd cbp->cb_error = 1; 3445168404Spjd } 3446168404Spjd 3447263407Sdelphij if (cbp->cb_recurse) { 3448263407Sdelphij if (zfs_iter_dependents(zhp, B_TRUE, 3449263407Sdelphij rollback_check_dependent, cbp) != 0) { 3450263407Sdelphij zfs_close(zhp); 3451263407Sdelphij return (-1); 3452263407Sdelphij } 3453263407Sdelphij } else { 3454263407Sdelphij (void) fprintf(stderr, "%s\n", 3455263407Sdelphij zfs_get_name(zhp)); 3456263407Sdelphij } 3457168404Spjd } 3458168404Spjd zfs_close(zhp); 3459168404Spjd return (0); 3460168404Spjd} 3461168404Spjd 3462168404Spjdstatic int 3463168404Spjdzfs_do_rollback(int argc, char **argv) 3464168404Spjd{ 3465231144Smm int ret = 0; 3466168404Spjd int c; 3467185029Spjd boolean_t force = B_FALSE; 3468168404Spjd rollback_cbdata_t cb = { 0 }; 3469168404Spjd zfs_handle_t *zhp, *snap; 3470168404Spjd char parentname[ZFS_MAXNAMELEN]; 3471168404Spjd char *delim; 3472168404Spjd 3473168404Spjd /* check options */ 3474185029Spjd while ((c = getopt(argc, argv, "rRf")) != -1) { 3475168404Spjd switch (c) { 3476168404Spjd case 'r': 3477168404Spjd cb.cb_recurse = 1; 3478168404Spjd break; 3479168404Spjd case 'R': 3480168404Spjd cb.cb_recurse = 1; 3481168404Spjd cb.cb_doclones = 1; 3482168404Spjd break; 3483185029Spjd case 'f': 3484185029Spjd force = B_TRUE; 3485185029Spjd break; 3486168404Spjd case '?': 3487168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3488168404Spjd optopt); 3489168404Spjd usage(B_FALSE); 3490168404Spjd } 3491168404Spjd } 3492168404Spjd 3493168404Spjd argc -= optind; 3494168404Spjd argv += optind; 3495168404Spjd 3496168404Spjd /* check number of arguments */ 3497168404Spjd if (argc < 1) { 3498168404Spjd (void) fprintf(stderr, gettext("missing dataset argument\n")); 3499168404Spjd usage(B_FALSE); 3500168404Spjd } 3501168404Spjd if (argc > 1) { 3502168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 3503168404Spjd usage(B_FALSE); 3504168404Spjd } 3505168404Spjd 3506168404Spjd /* open the snapshot */ 3507168404Spjd if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 3508168404Spjd return (1); 3509168404Spjd 3510168404Spjd /* open the parent dataset */ 3511168404Spjd (void) strlcpy(parentname, argv[0], sizeof (parentname)); 3512168404Spjd verify((delim = strrchr(parentname, '@')) != NULL); 3513168404Spjd *delim = '\0'; 3514185029Spjd if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_DATASET)) == NULL) { 3515168404Spjd zfs_close(snap); 3516168404Spjd return (1); 3517168404Spjd } 3518168404Spjd 3519168404Spjd /* 3520168404Spjd * Check for more recent snapshots and/or clones based on the presence 3521168404Spjd * of '-r' and '-R'. 3522168404Spjd */ 3523168404Spjd cb.cb_target = argv[0]; 3524168404Spjd cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 3525168404Spjd cb.cb_first = B_TRUE; 3526168404Spjd cb.cb_error = 0; 3527263407Sdelphij if ((ret = zfs_iter_snapshots(zhp, B_FALSE, rollback_check, &cb)) != 0) 3528168404Spjd goto out; 3529263407Sdelphij if ((ret = zfs_iter_bookmarks(zhp, rollback_check, &cb)) != 0) 3530263407Sdelphij goto out; 3531168404Spjd 3532168404Spjd if ((ret = cb.cb_error) != 0) 3533168404Spjd goto out; 3534168404Spjd 3535168404Spjd /* 3536168404Spjd * Rollback parent to the given snapshot. 3537168404Spjd */ 3538168404Spjd ret = zfs_rollback(zhp, snap, force); 3539168404Spjd 3540168404Spjdout: 3541168404Spjd zfs_close(snap); 3542168404Spjd zfs_close(zhp); 3543168404Spjd 3544168404Spjd if (ret == 0) 3545168404Spjd return (0); 3546168404Spjd else 3547168404Spjd return (1); 3548168404Spjd} 3549168404Spjd 3550168404Spjd/* 3551290758Smav * zfs set property=value ... { fs | snap | vol } ... 3552168404Spjd * 3553290758Smav * Sets the given properties for all datasets specified on the command line. 3554168404Spjd */ 3555168404Spjd 3556168404Spjdstatic int 3557168404Spjdset_callback(zfs_handle_t *zhp, void *data) 3558168404Spjd{ 3559290758Smav nvlist_t *props = data; 3560168404Spjd 3561290758Smav if (zfs_prop_set_list(zhp, props) != 0) { 3562168404Spjd switch (libzfs_errno(g_zfs)) { 3563168404Spjd case EZFS_MOUNTFAILED: 3564168404Spjd (void) fprintf(stderr, gettext("property may be set " 3565168404Spjd "but unable to remount filesystem\n")); 3566168404Spjd break; 3567168404Spjd case EZFS_SHARENFSFAILED: 3568168404Spjd (void) fprintf(stderr, gettext("property may be set " 3569168404Spjd "but unable to reshare filesystem\n")); 3570168404Spjd break; 3571168404Spjd } 3572168404Spjd return (1); 3573168404Spjd } 3574168404Spjd return (0); 3575168404Spjd} 3576168404Spjd 3577168404Spjdstatic int 3578168404Spjdzfs_do_set(int argc, char **argv) 3579168404Spjd{ 3580290758Smav nvlist_t *props = NULL; 3581290758Smav int ds_start = -1; /* argv idx of first dataset arg */ 3582290767Smav int i, ret = 0; 3583168404Spjd 3584168404Spjd /* check for options */ 3585168404Spjd if (argc > 1 && argv[1][0] == '-') { 3586168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3587168404Spjd argv[1][1]); 3588168404Spjd usage(B_FALSE); 3589168404Spjd } 3590168404Spjd 3591168404Spjd /* check number of arguments */ 3592168404Spjd if (argc < 2) { 3593290758Smav (void) fprintf(stderr, gettext("missing arguments\n")); 3594168404Spjd usage(B_FALSE); 3595168404Spjd } 3596168404Spjd if (argc < 3) { 3597290758Smav if (strchr(argv[1], '=') == NULL) { 3598290758Smav (void) fprintf(stderr, gettext("missing property=value " 3599290758Smav "argument(s)\n")); 3600290758Smav } else { 3601290758Smav (void) fprintf(stderr, gettext("missing dataset " 3602290758Smav "name(s)\n")); 3603290758Smav } 3604168404Spjd usage(B_FALSE); 3605168404Spjd } 3606168404Spjd 3607290758Smav /* validate argument order: prop=val args followed by dataset args */ 3608290767Smav for (i = 1; i < argc; i++) { 3609290758Smav if (strchr(argv[i], '=') != NULL) { 3610290758Smav if (ds_start > 0) { 3611290758Smav /* out-of-order prop=val argument */ 3612290758Smav (void) fprintf(stderr, gettext("invalid " 3613290758Smav "argument order\n"), i); 3614290758Smav usage(B_FALSE); 3615290758Smav } 3616290758Smav } else if (ds_start < 0) { 3617290758Smav ds_start = i; 3618290758Smav } 3619290758Smav } 3620290758Smav if (ds_start < 0) { 3621290758Smav (void) fprintf(stderr, gettext("missing dataset name(s)\n")); 3622168404Spjd usage(B_FALSE); 3623168404Spjd } 3624168404Spjd 3625290758Smav /* Populate a list of property settings */ 3626290758Smav if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 3627290758Smav nomem(); 3628290767Smav for (i = 1; i < ds_start; i++) { 3629290758Smav if ((ret = parseprop(props, argv[i])) != 0) 3630290758Smav goto error; 3631168404Spjd } 3632168404Spjd 3633290758Smav ret = zfs_for_each(argc - ds_start, argv + ds_start, 0, 3634290758Smav ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, props); 3635168404Spjd 3636290758Smaverror: 3637290758Smav nvlist_free(props); 3638168404Spjd return (ret); 3639168404Spjd} 3640168404Spjd 3641248571Smmtypedef struct snap_cbdata { 3642248571Smm nvlist_t *sd_nvl; 3643248571Smm boolean_t sd_recursive; 3644248571Smm const char *sd_snapname; 3645248571Smm} snap_cbdata_t; 3646248571Smm 3647248571Smmstatic int 3648248571Smmzfs_snapshot_cb(zfs_handle_t *zhp, void *arg) 3649248571Smm{ 3650248571Smm snap_cbdata_t *sd = arg; 3651248571Smm char *name; 3652248571Smm int rv = 0; 3653248571Smm int error; 3654248571Smm 3655253819Sdelphij if (sd->sd_recursive && 3656253819Sdelphij zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) != 0) { 3657253819Sdelphij zfs_close(zhp); 3658253819Sdelphij return (0); 3659253819Sdelphij } 3660253819Sdelphij 3661248571Smm error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname); 3662248571Smm if (error == -1) 3663248571Smm nomem(); 3664248571Smm fnvlist_add_boolean(sd->sd_nvl, name); 3665248571Smm free(name); 3666248571Smm 3667248571Smm if (sd->sd_recursive) 3668248571Smm rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd); 3669248571Smm zfs_close(zhp); 3670248571Smm return (rv); 3671248571Smm} 3672248571Smm 3673168404Spjd/* 3674185029Spjd * zfs snapshot [-r] [-o prop=value] ... <fs@snap> 3675168404Spjd * 3676168404Spjd * Creates a snapshot with the given name. While functionally equivalent to 3677185029Spjd * 'zfs create', it is a separate command to differentiate intent. 3678168404Spjd */ 3679168404Spjdstatic int 3680168404Spjdzfs_do_snapshot(int argc, char **argv) 3681168404Spjd{ 3682231144Smm int ret = 0; 3683260671Sjhibbits int c; 3684185029Spjd nvlist_t *props; 3685248571Smm snap_cbdata_t sd = { 0 }; 3686248571Smm boolean_t multiple_snaps = B_FALSE; 3687168404Spjd 3688219089Spjd if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 3689219089Spjd nomem(); 3690248571Smm if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0) 3691248571Smm nomem(); 3692185029Spjd 3693168404Spjd /* check options */ 3694185029Spjd while ((c = getopt(argc, argv, "ro:")) != -1) { 3695168404Spjd switch (c) { 3696185029Spjd case 'o': 3697288571Smav if (parseprop(props, optarg) != 0) 3698185029Spjd return (1); 3699185029Spjd break; 3700168404Spjd case 'r': 3701248571Smm sd.sd_recursive = B_TRUE; 3702248571Smm multiple_snaps = B_TRUE; 3703168404Spjd break; 3704168404Spjd case '?': 3705168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3706168404Spjd optopt); 3707185029Spjd goto usage; 3708168404Spjd } 3709168404Spjd } 3710168404Spjd 3711168404Spjd argc -= optind; 3712168404Spjd argv += optind; 3713168404Spjd 3714168404Spjd /* check number of arguments */ 3715168404Spjd if (argc < 1) { 3716168404Spjd (void) fprintf(stderr, gettext("missing snapshot argument\n")); 3717185029Spjd goto usage; 3718168404Spjd } 3719248571Smm 3720248571Smm if (argc > 1) 3721248571Smm multiple_snaps = B_TRUE; 3722248571Smm for (; argc > 0; argc--, argv++) { 3723248571Smm char *atp; 3724248571Smm zfs_handle_t *zhp; 3725248571Smm 3726248571Smm atp = strchr(argv[0], '@'); 3727248571Smm if (atp == NULL) 3728248571Smm goto usage; 3729248571Smm *atp = '\0'; 3730248571Smm sd.sd_snapname = atp + 1; 3731248571Smm zhp = zfs_open(g_zfs, argv[0], 3732248571Smm ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 3733248571Smm if (zhp == NULL) 3734248571Smm goto usage; 3735248571Smm if (zfs_snapshot_cb(zhp, &sd) != 0) 3736248571Smm goto usage; 3737168404Spjd } 3738168404Spjd 3739248571Smm ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props); 3740248571Smm nvlist_free(sd.sd_nvl); 3741185029Spjd nvlist_free(props); 3742248571Smm if (ret != 0 && multiple_snaps) 3743168404Spjd (void) fprintf(stderr, gettext("no snapshots were created\n")); 3744168404Spjd return (ret != 0); 3745185029Spjd 3746185029Spjdusage: 3747248571Smm nvlist_free(sd.sd_nvl); 3748185029Spjd nvlist_free(props); 3749185029Spjd usage(B_FALSE); 3750185029Spjd return (-1); 3751168404Spjd} 3752168404Spjd 3753168404Spjd/* 3754168404Spjd * Send a backup stream to stdout. 3755168404Spjd */ 3756168404Spjdstatic int 3757168404Spjdzfs_do_send(int argc, char **argv) 3758168404Spjd{ 3759168404Spjd char *fromname = NULL; 3760185029Spjd char *toname = NULL; 3761290756Smav char *resume_token = NULL; 3762168404Spjd char *cp; 3763168404Spjd zfs_handle_t *zhp; 3764219089Spjd sendflags_t flags = { 0 }; 3765168404Spjd int c, err; 3766228103Smm nvlist_t *dbgnv = NULL; 3767219089Spjd boolean_t extraverbose = B_FALSE; 3768168404Spjd 3769168404Spjd /* check options */ 3770290756Smav while ((c = getopt(argc, argv, ":i:I:RDpvnPLet:")) != -1) { 3771168404Spjd switch (c) { 3772168404Spjd case 'i': 3773168404Spjd if (fromname) 3774168404Spjd usage(B_FALSE); 3775168404Spjd fromname = optarg; 3776168404Spjd break; 3777185029Spjd case 'I': 3778185029Spjd if (fromname) 3779185029Spjd usage(B_FALSE); 3780185029Spjd fromname = optarg; 3781219089Spjd flags.doall = B_TRUE; 3782185029Spjd break; 3783185029Spjd case 'R': 3784219089Spjd flags.replicate = B_TRUE; 3785185029Spjd break; 3786219089Spjd case 'p': 3787219089Spjd flags.props = B_TRUE; 3788219089Spjd break; 3789228103Smm case 'P': 3790228103Smm flags.parsable = B_TRUE; 3791228103Smm flags.verbose = B_TRUE; 3792228103Smm break; 3793185029Spjd case 'v': 3794219089Spjd if (flags.verbose) 3795219089Spjd extraverbose = B_TRUE; 3796219089Spjd flags.verbose = B_TRUE; 3797235222Smm flags.progress = B_TRUE; 3798185029Spjd break; 3799219089Spjd case 'D': 3800219089Spjd flags.dedup = B_TRUE; 3801219089Spjd break; 3802228103Smm case 'n': 3803228103Smm flags.dryrun = B_TRUE; 3804228103Smm break; 3805276081Sdelphij case 'L': 3806276081Sdelphij flags.largeblock = B_TRUE; 3807276081Sdelphij break; 3808268649Sdelphij case 'e': 3809268649Sdelphij flags.embed_data = B_TRUE; 3810268649Sdelphij break; 3811290756Smav case 't': 3812290756Smav resume_token = optarg; 3813290756Smav break; 3814168404Spjd case ':': 3815168404Spjd (void) fprintf(stderr, gettext("missing argument for " 3816168404Spjd "'%c' option\n"), optopt); 3817168404Spjd usage(B_FALSE); 3818168404Spjd break; 3819168404Spjd case '?': 3820168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3821168404Spjd optopt); 3822168404Spjd usage(B_FALSE); 3823168404Spjd } 3824168404Spjd } 3825168404Spjd 3826168404Spjd argc -= optind; 3827168404Spjd argv += optind; 3828168404Spjd 3829290756Smav if (resume_token != NULL) { 3830290756Smav if (fromname != NULL || flags.replicate || flags.props || 3831290756Smav flags.dedup) { 3832290756Smav (void) fprintf(stderr, 3833290756Smav gettext("invalid flags combined with -t\n")); 3834290756Smav usage(B_FALSE); 3835290756Smav } 3836290756Smav if (argc != 0) { 3837290756Smav (void) fprintf(stderr, gettext("no additional " 3838290756Smav "arguments are permitted with -t\n")); 3839290756Smav usage(B_FALSE); 3840290756Smav } 3841290756Smav } else { 3842290756Smav if (argc < 1) { 3843290756Smav (void) fprintf(stderr, 3844290756Smav gettext("missing snapshot argument\n")); 3845290756Smav usage(B_FALSE); 3846290756Smav } 3847290756Smav if (argc > 1) { 3848290756Smav (void) fprintf(stderr, gettext("too many arguments\n")); 3849290756Smav usage(B_FALSE); 3850290756Smav } 3851168404Spjd } 3852168404Spjd 3853228103Smm if (!flags.dryrun && isatty(STDOUT_FILENO)) { 3854168404Spjd (void) fprintf(stderr, 3855168404Spjd gettext("Error: Stream can not be written to a terminal.\n" 3856168404Spjd "You must redirect standard output.\n")); 3857168404Spjd return (1); 3858168404Spjd } 3859168404Spjd 3860290756Smav if (resume_token != NULL) { 3861290756Smav return (zfs_send_resume(g_zfs, &flags, STDOUT_FILENO, 3862290756Smav resume_token)); 3863290756Smav } 3864290756Smav 3865263407Sdelphij /* 3866263407Sdelphij * Special case sending a filesystem, or from a bookmark. 3867263407Sdelphij */ 3868263407Sdelphij if (strchr(argv[0], '@') == NULL || 3869263407Sdelphij (fromname && strchr(fromname, '#') != NULL)) { 3870263407Sdelphij char frombuf[ZFS_MAXNAMELEN]; 3871268649Sdelphij enum lzc_send_flags lzc_flags = 0; 3872263407Sdelphij 3873263407Sdelphij if (flags.replicate || flags.doall || flags.props || 3874263407Sdelphij flags.dedup || flags.dryrun || flags.verbose || 3875263407Sdelphij flags.progress) { 3876263407Sdelphij (void) fprintf(stderr, 3877263407Sdelphij gettext("Error: " 3878263407Sdelphij "Unsupported flag with filesystem or bookmark.\n")); 3879263407Sdelphij return (1); 3880263407Sdelphij } 3881263407Sdelphij 3882263407Sdelphij zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET); 3883263407Sdelphij if (zhp == NULL) 3884263407Sdelphij return (1); 3885263407Sdelphij 3886276081Sdelphij if (flags.largeblock) 3887276081Sdelphij lzc_flags |= LZC_SEND_FLAG_LARGE_BLOCK; 3888268649Sdelphij if (flags.embed_data) 3889268649Sdelphij lzc_flags |= LZC_SEND_FLAG_EMBED_DATA; 3890268649Sdelphij 3891263407Sdelphij if (fromname != NULL && 3892263407Sdelphij (fromname[0] == '#' || fromname[0] == '@')) { 3893263407Sdelphij /* 3894263407Sdelphij * Incremental source name begins with # or @. 3895263407Sdelphij * Default to same fs as target. 3896263407Sdelphij */ 3897263407Sdelphij (void) strncpy(frombuf, argv[0], sizeof (frombuf)); 3898263407Sdelphij cp = strchr(frombuf, '@'); 3899263407Sdelphij if (cp != NULL) 3900263407Sdelphij *cp = '\0'; 3901263407Sdelphij (void) strlcat(frombuf, fromname, sizeof (frombuf)); 3902263407Sdelphij fromname = frombuf; 3903263407Sdelphij } 3904268649Sdelphij err = zfs_send_one(zhp, fromname, STDOUT_FILENO, lzc_flags); 3905263407Sdelphij zfs_close(zhp); 3906263407Sdelphij return (err != 0); 3907263407Sdelphij } 3908263407Sdelphij 3909185029Spjd cp = strchr(argv[0], '@'); 3910185029Spjd *cp = '\0'; 3911185029Spjd toname = cp + 1; 3912185029Spjd zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 3913185029Spjd if (zhp == NULL) 3914168404Spjd return (1); 3915168404Spjd 3916168404Spjd /* 3917168404Spjd * If they specified the full path to the snapshot, chop off 3918185029Spjd * everything except the short name of the snapshot, but special 3919185029Spjd * case if they specify the origin. 3920168404Spjd */ 3921168404Spjd if (fromname && (cp = strchr(fromname, '@')) != NULL) { 3922185029Spjd char origin[ZFS_MAXNAMELEN]; 3923185029Spjd zprop_source_t src; 3924185029Spjd 3925185029Spjd (void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN, 3926185029Spjd origin, sizeof (origin), &src, NULL, 0, B_FALSE); 3927185029Spjd 3928185029Spjd if (strcmp(origin, fromname) == 0) { 3929185029Spjd fromname = NULL; 3930219089Spjd flags.fromorigin = B_TRUE; 3931185029Spjd } else { 3932185029Spjd *cp = '\0'; 3933185029Spjd if (cp != fromname && strcmp(argv[0], fromname)) { 3934185029Spjd (void) fprintf(stderr, 3935185029Spjd gettext("incremental source must be " 3936185029Spjd "in same filesystem\n")); 3937185029Spjd usage(B_FALSE); 3938185029Spjd } 3939185029Spjd fromname = cp + 1; 3940185029Spjd if (strchr(fromname, '@') || strchr(fromname, '/')) { 3941185029Spjd (void) fprintf(stderr, 3942185029Spjd gettext("invalid incremental source\n")); 3943185029Spjd usage(B_FALSE); 3944185029Spjd } 3945168404Spjd } 3946168404Spjd } 3947168404Spjd 3948219089Spjd if (flags.replicate && fromname == NULL) 3949219089Spjd flags.doall = B_TRUE; 3950185029Spjd 3951228103Smm err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO, NULL, 0, 3952219089Spjd extraverbose ? &dbgnv : NULL); 3953219089Spjd 3954228103Smm if (extraverbose && dbgnv != NULL) { 3955219089Spjd /* 3956219089Spjd * dump_nvlist prints to stdout, but that's been 3957219089Spjd * redirected to a file. Make it print to stderr 3958219089Spjd * instead. 3959219089Spjd */ 3960219089Spjd (void) dup2(STDERR_FILENO, STDOUT_FILENO); 3961219089Spjd dump_nvlist(dbgnv, 0); 3962219089Spjd nvlist_free(dbgnv); 3963219089Spjd } 3964168404Spjd zfs_close(zhp); 3965168404Spjd 3966168404Spjd return (err != 0); 3967168404Spjd} 3968168404Spjd 3969168404Spjd/* 3970168404Spjd * Restore a backup stream from stdin. 3971168404Spjd */ 3972168404Spjdstatic int 3973168404Spjdzfs_do_receive(int argc, char **argv) 3974168404Spjd{ 3975168404Spjd int c, err; 3976219089Spjd recvflags_t flags = { 0 }; 3977290756Smav boolean_t abort_resumable = B_FALSE; 3978290756Smav 3979288571Smav nvlist_t *props; 3980288571Smav nvpair_t *nvp = NULL; 3981168404Spjd 3982288571Smav if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 3983288571Smav nomem(); 3984288571Smav 3985168404Spjd /* check options */ 3986290756Smav while ((c = getopt(argc, argv, ":o:denuvFsA")) != -1) { 3987168404Spjd switch (c) { 3988288571Smav case 'o': 3989288571Smav if (parseprop(props, optarg) != 0) 3990288571Smav return (1); 3991288571Smav break; 3992168404Spjd case 'd': 3993185029Spjd flags.isprefix = B_TRUE; 3994168404Spjd break; 3995219089Spjd case 'e': 3996219089Spjd flags.isprefix = B_TRUE; 3997219089Spjd flags.istail = B_TRUE; 3998219089Spjd break; 3999168404Spjd case 'n': 4000185029Spjd flags.dryrun = B_TRUE; 4001168404Spjd break; 4002200516Sdelphij case 'u': 4003200516Sdelphij flags.nomount = B_TRUE; 4004200516Sdelphij break; 4005168404Spjd case 'v': 4006185029Spjd flags.verbose = B_TRUE; 4007168404Spjd break; 4008290756Smav case 's': 4009290756Smav flags.resumable = B_TRUE; 4010290756Smav break; 4011168404Spjd case 'F': 4012185029Spjd flags.force = B_TRUE; 4013168404Spjd break; 4014290756Smav case 'A': 4015290756Smav abort_resumable = B_TRUE; 4016290756Smav break; 4017168404Spjd case ':': 4018168404Spjd (void) fprintf(stderr, gettext("missing argument for " 4019168404Spjd "'%c' option\n"), optopt); 4020168404Spjd usage(B_FALSE); 4021168404Spjd break; 4022168404Spjd case '?': 4023168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 4024168404Spjd optopt); 4025168404Spjd usage(B_FALSE); 4026168404Spjd } 4027168404Spjd } 4028168404Spjd 4029168404Spjd argc -= optind; 4030168404Spjd argv += optind; 4031168404Spjd 4032168404Spjd /* check number of arguments */ 4033168404Spjd if (argc < 1) { 4034168404Spjd (void) fprintf(stderr, gettext("missing snapshot argument\n")); 4035168404Spjd usage(B_FALSE); 4036168404Spjd } 4037168404Spjd if (argc > 1) { 4038168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 4039168404Spjd usage(B_FALSE); 4040168404Spjd } 4041168404Spjd 4042288571Smav while ((nvp = nvlist_next_nvpair(props, nvp))) { 4043288571Smav if (strcmp(nvpair_name(nvp), "origin") != 0) { 4044288571Smav (void) fprintf(stderr, gettext("invalid option")); 4045288571Smav usage(B_FALSE); 4046288571Smav } 4047288571Smav } 4048288571Smav 4049290756Smav if (abort_resumable) { 4050290756Smav if (flags.isprefix || flags.istail || flags.dryrun || 4051290756Smav flags.resumable || flags.nomount) { 4052290756Smav (void) fprintf(stderr, gettext("invalid option")); 4053290756Smav usage(B_FALSE); 4054290756Smav } 4055290756Smav 4056290756Smav char namebuf[ZFS_MAXNAMELEN]; 4057290756Smav (void) snprintf(namebuf, sizeof (namebuf), 4058290756Smav "%s/%%recv", argv[0]); 4059290756Smav 4060290756Smav if (zfs_dataset_exists(g_zfs, namebuf, 4061290756Smav ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) { 4062290756Smav zfs_handle_t *zhp = zfs_open(g_zfs, 4063290756Smav namebuf, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 4064290756Smav if (zhp == NULL) 4065290756Smav return (1); 4066290756Smav err = zfs_destroy(zhp, B_FALSE); 4067290756Smav } else { 4068290756Smav zfs_handle_t *zhp = zfs_open(g_zfs, 4069290756Smav argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 4070290756Smav if (zhp == NULL) 4071290756Smav usage(B_FALSE); 4072290756Smav if (!zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) || 4073290756Smav zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN, 4074290756Smav NULL, 0, NULL, NULL, 0, B_TRUE) == -1) { 4075290756Smav (void) fprintf(stderr, 4076290756Smav gettext("'%s' does not have any " 4077290756Smav "resumable receive state to abort\n"), 4078290756Smav argv[0]); 4079290756Smav return (1); 4080290756Smav } 4081290756Smav err = zfs_destroy(zhp, B_FALSE); 4082290756Smav } 4083290756Smav 4084290756Smav return (err != 0); 4085290756Smav } 4086290756Smav 4087168404Spjd if (isatty(STDIN_FILENO)) { 4088168404Spjd (void) fprintf(stderr, 4089168404Spjd gettext("Error: Backup stream can not be read " 4090168404Spjd "from a terminal.\n" 4091168404Spjd "You must redirect standard input.\n")); 4092168404Spjd return (1); 4093168404Spjd } 4094288571Smav err = zfs_receive(g_zfs, argv[0], props, &flags, STDIN_FILENO, NULL); 4095168404Spjd 4096185029Spjd return (err != 0); 4097185029Spjd} 4098185029Spjd 4099219089Spjd/* 4100219089Spjd * allow/unallow stuff 4101219089Spjd */ 4102219089Spjd/* copied from zfs/sys/dsl_deleg.h */ 4103219089Spjd#define ZFS_DELEG_PERM_CREATE "create" 4104219089Spjd#define ZFS_DELEG_PERM_DESTROY "destroy" 4105219089Spjd#define ZFS_DELEG_PERM_SNAPSHOT "snapshot" 4106219089Spjd#define ZFS_DELEG_PERM_ROLLBACK "rollback" 4107219089Spjd#define ZFS_DELEG_PERM_CLONE "clone" 4108219089Spjd#define ZFS_DELEG_PERM_PROMOTE "promote" 4109219089Spjd#define ZFS_DELEG_PERM_RENAME "rename" 4110219089Spjd#define ZFS_DELEG_PERM_MOUNT "mount" 4111219089Spjd#define ZFS_DELEG_PERM_SHARE "share" 4112219089Spjd#define ZFS_DELEG_PERM_SEND "send" 4113219089Spjd#define ZFS_DELEG_PERM_RECEIVE "receive" 4114219089Spjd#define ZFS_DELEG_PERM_ALLOW "allow" 4115219089Spjd#define ZFS_DELEG_PERM_USERPROP "userprop" 4116219089Spjd#define ZFS_DELEG_PERM_VSCAN "vscan" /* ??? */ 4117219089Spjd#define ZFS_DELEG_PERM_USERQUOTA "userquota" 4118219089Spjd#define ZFS_DELEG_PERM_GROUPQUOTA "groupquota" 4119219089Spjd#define ZFS_DELEG_PERM_USERUSED "userused" 4120219089Spjd#define ZFS_DELEG_PERM_GROUPUSED "groupused" 4121219089Spjd#define ZFS_DELEG_PERM_HOLD "hold" 4122219089Spjd#define ZFS_DELEG_PERM_RELEASE "release" 4123219089Spjd#define ZFS_DELEG_PERM_DIFF "diff" 4124263407Sdelphij#define ZFS_DELEG_PERM_BOOKMARK "bookmark" 4125168404Spjd 4126219089Spjd#define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE 4127219089Spjd 4128219089Spjdstatic zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = { 4129219089Spjd { ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW }, 4130219089Spjd { ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE }, 4131219089Spjd { ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE }, 4132219089Spjd { ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY }, 4133219089Spjd { ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF}, 4134219089Spjd { ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD }, 4135219089Spjd { ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT }, 4136219089Spjd { ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE }, 4137219089Spjd { ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE }, 4138219089Spjd { ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE }, 4139219089Spjd { ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME }, 4140219089Spjd { ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK }, 4141219089Spjd { ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND }, 4142219089Spjd { ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE }, 4143219089Spjd { ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT }, 4144263407Sdelphij { ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK }, 4145219089Spjd 4146219089Spjd { ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA }, 4147219089Spjd { ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED }, 4148219089Spjd { ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP }, 4149219089Spjd { ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA }, 4150219089Spjd { ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED }, 4151219089Spjd { NULL, ZFS_DELEG_NOTE_NONE } 4152219089Spjd}; 4153219089Spjd 4154219089Spjd/* permission structure */ 4155219089Spjdtypedef struct deleg_perm { 4156219089Spjd zfs_deleg_who_type_t dp_who_type; 4157219089Spjd const char *dp_name; 4158219089Spjd boolean_t dp_local; 4159219089Spjd boolean_t dp_descend; 4160219089Spjd} deleg_perm_t; 4161219089Spjd 4162219089Spjd/* */ 4163219089Spjdtypedef struct deleg_perm_node { 4164219089Spjd deleg_perm_t dpn_perm; 4165219089Spjd 4166219089Spjd uu_avl_node_t dpn_avl_node; 4167219089Spjd} deleg_perm_node_t; 4168219089Spjd 4169219089Spjdtypedef struct fs_perm fs_perm_t; 4170219089Spjd 4171219089Spjd/* permissions set */ 4172219089Spjdtypedef struct who_perm { 4173219089Spjd zfs_deleg_who_type_t who_type; 4174219089Spjd const char *who_name; /* id */ 4175219089Spjd char who_ug_name[256]; /* user/group name */ 4176219089Spjd fs_perm_t *who_fsperm; /* uplink */ 4177219089Spjd 4178219089Spjd uu_avl_t *who_deleg_perm_avl; /* permissions */ 4179219089Spjd} who_perm_t; 4180219089Spjd 4181219089Spjd/* */ 4182219089Spjdtypedef struct who_perm_node { 4183219089Spjd who_perm_t who_perm; 4184219089Spjd uu_avl_node_t who_avl_node; 4185219089Spjd} who_perm_node_t; 4186219089Spjd 4187219089Spjdtypedef struct fs_perm_set fs_perm_set_t; 4188219089Spjd/* fs permissions */ 4189219089Spjdstruct fs_perm { 4190219089Spjd const char *fsp_name; 4191219089Spjd 4192219089Spjd uu_avl_t *fsp_sc_avl; /* sets,create */ 4193219089Spjd uu_avl_t *fsp_uge_avl; /* user,group,everyone */ 4194219089Spjd 4195219089Spjd fs_perm_set_t *fsp_set; /* uplink */ 4196219089Spjd}; 4197219089Spjd 4198219089Spjd/* */ 4199219089Spjdtypedef struct fs_perm_node { 4200219089Spjd fs_perm_t fspn_fsperm; 4201219089Spjd uu_avl_t *fspn_avl; 4202219089Spjd 4203219089Spjd uu_list_node_t fspn_list_node; 4204219089Spjd} fs_perm_node_t; 4205219089Spjd 4206219089Spjd/* top level structure */ 4207219089Spjdstruct fs_perm_set { 4208219089Spjd uu_list_pool_t *fsps_list_pool; 4209219089Spjd uu_list_t *fsps_list; /* list of fs_perms */ 4210219089Spjd 4211219089Spjd uu_avl_pool_t *fsps_named_set_avl_pool; 4212219089Spjd uu_avl_pool_t *fsps_who_perm_avl_pool; 4213219089Spjd uu_avl_pool_t *fsps_deleg_perm_avl_pool; 4214219089Spjd}; 4215219089Spjd 4216219089Spjdstatic inline const char * 4217219089Spjddeleg_perm_type(zfs_deleg_note_t note) 4218219089Spjd{ 4219219089Spjd /* subcommands */ 4220219089Spjd switch (note) { 4221219089Spjd /* SUBCOMMANDS */ 4222219089Spjd /* OTHER */ 4223219089Spjd case ZFS_DELEG_NOTE_GROUPQUOTA: 4224219089Spjd case ZFS_DELEG_NOTE_GROUPUSED: 4225219089Spjd case ZFS_DELEG_NOTE_USERPROP: 4226219089Spjd case ZFS_DELEG_NOTE_USERQUOTA: 4227219089Spjd case ZFS_DELEG_NOTE_USERUSED: 4228219089Spjd /* other */ 4229219089Spjd return (gettext("other")); 4230219089Spjd default: 4231219089Spjd return (gettext("subcommand")); 4232219089Spjd } 4233219089Spjd} 4234219089Spjd 4235219089Spjdstatic int inline 4236219089Spjdwho_type2weight(zfs_deleg_who_type_t who_type) 4237219089Spjd{ 4238219089Spjd int res; 4239219089Spjd switch (who_type) { 4240219089Spjd case ZFS_DELEG_NAMED_SET_SETS: 4241219089Spjd case ZFS_DELEG_NAMED_SET: 4242219089Spjd res = 0; 4243219089Spjd break; 4244219089Spjd case ZFS_DELEG_CREATE_SETS: 4245219089Spjd case ZFS_DELEG_CREATE: 4246219089Spjd res = 1; 4247219089Spjd break; 4248219089Spjd case ZFS_DELEG_USER_SETS: 4249219089Spjd case ZFS_DELEG_USER: 4250219089Spjd res = 2; 4251219089Spjd break; 4252219089Spjd case ZFS_DELEG_GROUP_SETS: 4253219089Spjd case ZFS_DELEG_GROUP: 4254219089Spjd res = 3; 4255219089Spjd break; 4256219089Spjd case ZFS_DELEG_EVERYONE_SETS: 4257219089Spjd case ZFS_DELEG_EVERYONE: 4258219089Spjd res = 4; 4259219089Spjd break; 4260219089Spjd default: 4261219089Spjd res = -1; 4262219089Spjd } 4263219089Spjd 4264219089Spjd return (res); 4265219089Spjd} 4266219089Spjd 4267219089Spjd/* ARGSUSED */ 4268219089Spjdstatic int 4269219089Spjdwho_perm_compare(const void *larg, const void *rarg, void *unused) 4270219089Spjd{ 4271219089Spjd const who_perm_node_t *l = larg; 4272219089Spjd const who_perm_node_t *r = rarg; 4273219089Spjd zfs_deleg_who_type_t ltype = l->who_perm.who_type; 4274219089Spjd zfs_deleg_who_type_t rtype = r->who_perm.who_type; 4275219089Spjd int lweight = who_type2weight(ltype); 4276219089Spjd int rweight = who_type2weight(rtype); 4277219089Spjd int res = lweight - rweight; 4278219089Spjd if (res == 0) 4279219089Spjd res = strncmp(l->who_perm.who_name, r->who_perm.who_name, 4280219089Spjd ZFS_MAX_DELEG_NAME-1); 4281219089Spjd 4282219089Spjd if (res == 0) 4283219089Spjd return (0); 4284219089Spjd if (res > 0) 4285219089Spjd return (1); 4286219089Spjd else 4287219089Spjd return (-1); 4288219089Spjd} 4289219089Spjd 4290219089Spjd/* ARGSUSED */ 4291219089Spjdstatic int 4292219089Spjddeleg_perm_compare(const void *larg, const void *rarg, void *unused) 4293219089Spjd{ 4294219089Spjd const deleg_perm_node_t *l = larg; 4295219089Spjd const deleg_perm_node_t *r = rarg; 4296219089Spjd int res = strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name, 4297219089Spjd ZFS_MAX_DELEG_NAME-1); 4298219089Spjd 4299219089Spjd if (res == 0) 4300219089Spjd return (0); 4301219089Spjd 4302219089Spjd if (res > 0) 4303219089Spjd return (1); 4304219089Spjd else 4305219089Spjd return (-1); 4306219089Spjd} 4307219089Spjd 4308219089Spjdstatic inline void 4309219089Spjdfs_perm_set_init(fs_perm_set_t *fspset) 4310219089Spjd{ 4311219089Spjd bzero(fspset, sizeof (fs_perm_set_t)); 4312219089Spjd 4313219089Spjd if ((fspset->fsps_list_pool = uu_list_pool_create("fsps_list_pool", 4314219089Spjd sizeof (fs_perm_node_t), offsetof(fs_perm_node_t, fspn_list_node), 4315219089Spjd NULL, UU_DEFAULT)) == NULL) 4316219089Spjd nomem(); 4317219089Spjd if ((fspset->fsps_list = uu_list_create(fspset->fsps_list_pool, NULL, 4318219089Spjd UU_DEFAULT)) == NULL) 4319219089Spjd nomem(); 4320219089Spjd 4321219089Spjd if ((fspset->fsps_named_set_avl_pool = uu_avl_pool_create( 4322219089Spjd "named_set_avl_pool", sizeof (who_perm_node_t), offsetof( 4323219089Spjd who_perm_node_t, who_avl_node), who_perm_compare, 4324219089Spjd UU_DEFAULT)) == NULL) 4325219089Spjd nomem(); 4326219089Spjd 4327219089Spjd if ((fspset->fsps_who_perm_avl_pool = uu_avl_pool_create( 4328219089Spjd "who_perm_avl_pool", sizeof (who_perm_node_t), offsetof( 4329219089Spjd who_perm_node_t, who_avl_node), who_perm_compare, 4330219089Spjd UU_DEFAULT)) == NULL) 4331219089Spjd nomem(); 4332219089Spjd 4333219089Spjd if ((fspset->fsps_deleg_perm_avl_pool = uu_avl_pool_create( 4334219089Spjd "deleg_perm_avl_pool", sizeof (deleg_perm_node_t), offsetof( 4335219089Spjd deleg_perm_node_t, dpn_avl_node), deleg_perm_compare, UU_DEFAULT)) 4336219089Spjd == NULL) 4337219089Spjd nomem(); 4338219089Spjd} 4339219089Spjd 4340219089Spjdstatic inline void fs_perm_fini(fs_perm_t *); 4341219089Spjdstatic inline void who_perm_fini(who_perm_t *); 4342219089Spjd 4343219089Spjdstatic inline void 4344219089Spjdfs_perm_set_fini(fs_perm_set_t *fspset) 4345219089Spjd{ 4346219089Spjd fs_perm_node_t *node = uu_list_first(fspset->fsps_list); 4347219089Spjd 4348219089Spjd while (node != NULL) { 4349219089Spjd fs_perm_node_t *next_node = 4350219089Spjd uu_list_next(fspset->fsps_list, node); 4351219089Spjd fs_perm_t *fsperm = &node->fspn_fsperm; 4352219089Spjd fs_perm_fini(fsperm); 4353219089Spjd uu_list_remove(fspset->fsps_list, node); 4354219089Spjd free(node); 4355219089Spjd node = next_node; 4356219089Spjd } 4357219089Spjd 4358219089Spjd uu_avl_pool_destroy(fspset->fsps_named_set_avl_pool); 4359219089Spjd uu_avl_pool_destroy(fspset->fsps_who_perm_avl_pool); 4360219089Spjd uu_avl_pool_destroy(fspset->fsps_deleg_perm_avl_pool); 4361219089Spjd} 4362219089Spjd 4363219089Spjdstatic inline void 4364219089Spjddeleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type, 4365219089Spjd const char *name) 4366219089Spjd{ 4367219089Spjd deleg_perm->dp_who_type = type; 4368219089Spjd deleg_perm->dp_name = name; 4369219089Spjd} 4370219089Spjd 4371219089Spjdstatic inline void 4372219089Spjdwho_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm, 4373219089Spjd zfs_deleg_who_type_t type, const char *name) 4374219089Spjd{ 4375219089Spjd uu_avl_pool_t *pool; 4376219089Spjd pool = fsperm->fsp_set->fsps_deleg_perm_avl_pool; 4377219089Spjd 4378219089Spjd bzero(who_perm, sizeof (who_perm_t)); 4379219089Spjd 4380219089Spjd if ((who_perm->who_deleg_perm_avl = uu_avl_create(pool, NULL, 4381219089Spjd UU_DEFAULT)) == NULL) 4382219089Spjd nomem(); 4383219089Spjd 4384219089Spjd who_perm->who_type = type; 4385219089Spjd who_perm->who_name = name; 4386219089Spjd who_perm->who_fsperm = fsperm; 4387219089Spjd} 4388219089Spjd 4389219089Spjdstatic inline void 4390219089Spjdwho_perm_fini(who_perm_t *who_perm) 4391219089Spjd{ 4392219089Spjd deleg_perm_node_t *node = uu_avl_first(who_perm->who_deleg_perm_avl); 4393219089Spjd 4394219089Spjd while (node != NULL) { 4395219089Spjd deleg_perm_node_t *next_node = 4396219089Spjd uu_avl_next(who_perm->who_deleg_perm_avl, node); 4397219089Spjd 4398219089Spjd uu_avl_remove(who_perm->who_deleg_perm_avl, node); 4399219089Spjd free(node); 4400219089Spjd node = next_node; 4401219089Spjd } 4402219089Spjd 4403219089Spjd uu_avl_destroy(who_perm->who_deleg_perm_avl); 4404219089Spjd} 4405219089Spjd 4406219089Spjdstatic inline void 4407219089Spjdfs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname) 4408219089Spjd{ 4409219089Spjd uu_avl_pool_t *nset_pool = fspset->fsps_named_set_avl_pool; 4410219089Spjd uu_avl_pool_t *who_pool = fspset->fsps_who_perm_avl_pool; 4411219089Spjd 4412219089Spjd bzero(fsperm, sizeof (fs_perm_t)); 4413219089Spjd 4414219089Spjd if ((fsperm->fsp_sc_avl = uu_avl_create(nset_pool, NULL, UU_DEFAULT)) 4415219089Spjd == NULL) 4416219089Spjd nomem(); 4417219089Spjd 4418219089Spjd if ((fsperm->fsp_uge_avl = uu_avl_create(who_pool, NULL, UU_DEFAULT)) 4419219089Spjd == NULL) 4420219089Spjd nomem(); 4421219089Spjd 4422219089Spjd fsperm->fsp_set = fspset; 4423219089Spjd fsperm->fsp_name = fsname; 4424219089Spjd} 4425219089Spjd 4426219089Spjdstatic inline void 4427219089Spjdfs_perm_fini(fs_perm_t *fsperm) 4428219089Spjd{ 4429219089Spjd who_perm_node_t *node = uu_avl_first(fsperm->fsp_sc_avl); 4430219089Spjd while (node != NULL) { 4431219089Spjd who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_sc_avl, 4432219089Spjd node); 4433219089Spjd who_perm_t *who_perm = &node->who_perm; 4434219089Spjd who_perm_fini(who_perm); 4435219089Spjd uu_avl_remove(fsperm->fsp_sc_avl, node); 4436219089Spjd free(node); 4437219089Spjd node = next_node; 4438219089Spjd } 4439219089Spjd 4440219089Spjd node = uu_avl_first(fsperm->fsp_uge_avl); 4441219089Spjd while (node != NULL) { 4442219089Spjd who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_uge_avl, 4443219089Spjd node); 4444219089Spjd who_perm_t *who_perm = &node->who_perm; 4445219089Spjd who_perm_fini(who_perm); 4446219089Spjd uu_avl_remove(fsperm->fsp_uge_avl, node); 4447219089Spjd free(node); 4448219089Spjd node = next_node; 4449219089Spjd } 4450219089Spjd 4451219089Spjd uu_avl_destroy(fsperm->fsp_sc_avl); 4452219089Spjd uu_avl_destroy(fsperm->fsp_uge_avl); 4453219089Spjd} 4454219089Spjd 4455219089Spjdstatic void inline 4456219089Spjdset_deleg_perm_node(uu_avl_t *avl, deleg_perm_node_t *node, 4457219089Spjd zfs_deleg_who_type_t who_type, const char *name, char locality) 4458219089Spjd{ 4459219089Spjd uu_avl_index_t idx = 0; 4460219089Spjd 4461219089Spjd deleg_perm_node_t *found_node = NULL; 4462219089Spjd deleg_perm_t *deleg_perm = &node->dpn_perm; 4463219089Spjd 4464219089Spjd deleg_perm_init(deleg_perm, who_type, name); 4465219089Spjd 4466219089Spjd if ((found_node = uu_avl_find(avl, node, NULL, &idx)) 4467219089Spjd == NULL) 4468219089Spjd uu_avl_insert(avl, node, idx); 4469219089Spjd else { 4470219089Spjd node = found_node; 4471219089Spjd deleg_perm = &node->dpn_perm; 4472219089Spjd } 4473219089Spjd 4474219089Spjd 4475219089Spjd switch (locality) { 4476219089Spjd case ZFS_DELEG_LOCAL: 4477219089Spjd deleg_perm->dp_local = B_TRUE; 4478219089Spjd break; 4479219089Spjd case ZFS_DELEG_DESCENDENT: 4480219089Spjd deleg_perm->dp_descend = B_TRUE; 4481219089Spjd break; 4482219089Spjd case ZFS_DELEG_NA: 4483219089Spjd break; 4484219089Spjd default: 4485219089Spjd assert(B_FALSE); /* invalid locality */ 4486219089Spjd } 4487219089Spjd} 4488219089Spjd 4489219089Spjdstatic inline int 4490219089Spjdparse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality) 4491219089Spjd{ 4492219089Spjd nvpair_t *nvp = NULL; 4493219089Spjd fs_perm_set_t *fspset = who_perm->who_fsperm->fsp_set; 4494219089Spjd uu_avl_t *avl = who_perm->who_deleg_perm_avl; 4495219089Spjd zfs_deleg_who_type_t who_type = who_perm->who_type; 4496219089Spjd 4497219089Spjd while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 4498219089Spjd const char *name = nvpair_name(nvp); 4499219089Spjd data_type_t type = nvpair_type(nvp); 4500219089Spjd uu_avl_pool_t *avl_pool = fspset->fsps_deleg_perm_avl_pool; 4501219089Spjd deleg_perm_node_t *node = 4502219089Spjd safe_malloc(sizeof (deleg_perm_node_t)); 4503219089Spjd 4504219089Spjd assert(type == DATA_TYPE_BOOLEAN); 4505219089Spjd 4506219089Spjd uu_avl_node_init(node, &node->dpn_avl_node, avl_pool); 4507219089Spjd set_deleg_perm_node(avl, node, who_type, name, locality); 4508219089Spjd } 4509219089Spjd 4510219089Spjd return (0); 4511219089Spjd} 4512219089Spjd 4513219089Spjdstatic inline int 4514219089Spjdparse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl) 4515219089Spjd{ 4516219089Spjd nvpair_t *nvp = NULL; 4517219089Spjd fs_perm_set_t *fspset = fsperm->fsp_set; 4518219089Spjd 4519219089Spjd while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 4520219089Spjd nvlist_t *nvl2 = NULL; 4521219089Spjd const char *name = nvpair_name(nvp); 4522219089Spjd uu_avl_t *avl = NULL; 4523219089Spjd uu_avl_pool_t *avl_pool; 4524219089Spjd zfs_deleg_who_type_t perm_type = name[0]; 4525219089Spjd char perm_locality = name[1]; 4526219089Spjd const char *perm_name = name + 3; 4527219089Spjd boolean_t is_set = B_TRUE; 4528219089Spjd who_perm_t *who_perm = NULL; 4529219089Spjd 4530219089Spjd assert('$' == name[2]); 4531219089Spjd 4532219089Spjd if (nvpair_value_nvlist(nvp, &nvl2) != 0) 4533219089Spjd return (-1); 4534219089Spjd 4535219089Spjd switch (perm_type) { 4536219089Spjd case ZFS_DELEG_CREATE: 4537219089Spjd case ZFS_DELEG_CREATE_SETS: 4538219089Spjd case ZFS_DELEG_NAMED_SET: 4539219089Spjd case ZFS_DELEG_NAMED_SET_SETS: 4540219089Spjd avl_pool = fspset->fsps_named_set_avl_pool; 4541219089Spjd avl = fsperm->fsp_sc_avl; 4542219089Spjd break; 4543219089Spjd case ZFS_DELEG_USER: 4544219089Spjd case ZFS_DELEG_USER_SETS: 4545219089Spjd case ZFS_DELEG_GROUP: 4546219089Spjd case ZFS_DELEG_GROUP_SETS: 4547219089Spjd case ZFS_DELEG_EVERYONE: 4548219089Spjd case ZFS_DELEG_EVERYONE_SETS: 4549219089Spjd avl_pool = fspset->fsps_who_perm_avl_pool; 4550219089Spjd avl = fsperm->fsp_uge_avl; 4551219089Spjd break; 4552219089Spjd } 4553219089Spjd 4554219089Spjd if (is_set) { 4555219089Spjd who_perm_node_t *found_node = NULL; 4556219089Spjd who_perm_node_t *node = safe_malloc( 4557219089Spjd sizeof (who_perm_node_t)); 4558219089Spjd who_perm = &node->who_perm; 4559219089Spjd uu_avl_index_t idx = 0; 4560219089Spjd 4561219089Spjd uu_avl_node_init(node, &node->who_avl_node, avl_pool); 4562219089Spjd who_perm_init(who_perm, fsperm, perm_type, perm_name); 4563219089Spjd 4564219089Spjd if ((found_node = uu_avl_find(avl, node, NULL, &idx)) 4565219089Spjd == NULL) { 4566219089Spjd if (avl == fsperm->fsp_uge_avl) { 4567219089Spjd uid_t rid = 0; 4568219089Spjd struct passwd *p = NULL; 4569219089Spjd struct group *g = NULL; 4570219089Spjd const char *nice_name = NULL; 4571219089Spjd 4572219089Spjd switch (perm_type) { 4573219089Spjd case ZFS_DELEG_USER_SETS: 4574219089Spjd case ZFS_DELEG_USER: 4575219089Spjd rid = atoi(perm_name); 4576219089Spjd p = getpwuid(rid); 4577219089Spjd if (p) 4578219089Spjd nice_name = p->pw_name; 4579219089Spjd break; 4580219089Spjd case ZFS_DELEG_GROUP_SETS: 4581219089Spjd case ZFS_DELEG_GROUP: 4582219089Spjd rid = atoi(perm_name); 4583219089Spjd g = getgrgid(rid); 4584219089Spjd if (g) 4585219089Spjd nice_name = g->gr_name; 4586219089Spjd break; 4587219089Spjd } 4588219089Spjd 4589219089Spjd if (nice_name != NULL) 4590219089Spjd (void) strlcpy( 4591219089Spjd node->who_perm.who_ug_name, 4592219089Spjd nice_name, 256); 4593219089Spjd } 4594219089Spjd 4595219089Spjd uu_avl_insert(avl, node, idx); 4596219089Spjd } else { 4597219089Spjd node = found_node; 4598219089Spjd who_perm = &node->who_perm; 4599219089Spjd } 4600219089Spjd } 4601219089Spjd 4602219089Spjd (void) parse_who_perm(who_perm, nvl2, perm_locality); 4603219089Spjd } 4604219089Spjd 4605219089Spjd return (0); 4606219089Spjd} 4607219089Spjd 4608219089Spjdstatic inline int 4609219089Spjdparse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl) 4610219089Spjd{ 4611219089Spjd nvpair_t *nvp = NULL; 4612219089Spjd uu_avl_index_t idx = 0; 4613219089Spjd 4614219089Spjd while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 4615219089Spjd nvlist_t *nvl2 = NULL; 4616219089Spjd const char *fsname = nvpair_name(nvp); 4617219089Spjd data_type_t type = nvpair_type(nvp); 4618219089Spjd fs_perm_t *fsperm = NULL; 4619219089Spjd fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t)); 4620219089Spjd if (node == NULL) 4621219089Spjd nomem(); 4622219089Spjd 4623219089Spjd fsperm = &node->fspn_fsperm; 4624219089Spjd 4625219089Spjd assert(DATA_TYPE_NVLIST == type); 4626219089Spjd 4627219089Spjd uu_list_node_init(node, &node->fspn_list_node, 4628219089Spjd fspset->fsps_list_pool); 4629219089Spjd 4630219089Spjd idx = uu_list_numnodes(fspset->fsps_list); 4631219089Spjd fs_perm_init(fsperm, fspset, fsname); 4632219089Spjd 4633219089Spjd if (nvpair_value_nvlist(nvp, &nvl2) != 0) 4634219089Spjd return (-1); 4635219089Spjd 4636219089Spjd (void) parse_fs_perm(fsperm, nvl2); 4637219089Spjd 4638219089Spjd uu_list_insert(fspset->fsps_list, node, idx); 4639219089Spjd } 4640219089Spjd 4641219089Spjd return (0); 4642219089Spjd} 4643219089Spjd 4644219089Spjdstatic inline const char * 4645219089Spjddeleg_perm_comment(zfs_deleg_note_t note) 4646219089Spjd{ 4647219089Spjd const char *str = ""; 4648219089Spjd 4649219089Spjd /* subcommands */ 4650219089Spjd switch (note) { 4651219089Spjd /* SUBCOMMANDS */ 4652219089Spjd case ZFS_DELEG_NOTE_ALLOW: 4653219089Spjd str = gettext("Must also have the permission that is being" 4654219089Spjd "\n\t\t\t\tallowed"); 4655219089Spjd break; 4656219089Spjd case ZFS_DELEG_NOTE_CLONE: 4657219089Spjd str = gettext("Must also have the 'create' ability and 'mount'" 4658219089Spjd "\n\t\t\t\tability in the origin file system"); 4659219089Spjd break; 4660219089Spjd case ZFS_DELEG_NOTE_CREATE: 4661219089Spjd str = gettext("Must also have the 'mount' ability"); 4662219089Spjd break; 4663219089Spjd case ZFS_DELEG_NOTE_DESTROY: 4664219089Spjd str = gettext("Must also have the 'mount' ability"); 4665219089Spjd break; 4666219089Spjd case ZFS_DELEG_NOTE_DIFF: 4667219089Spjd str = gettext("Allows lookup of paths within a dataset;" 4668219089Spjd "\n\t\t\t\tgiven an object number. Ordinary users need this" 4669219089Spjd "\n\t\t\t\tin order to use zfs diff"); 4670219089Spjd break; 4671219089Spjd case ZFS_DELEG_NOTE_HOLD: 4672219089Spjd str = gettext("Allows adding a user hold to a snapshot"); 4673219089Spjd break; 4674219089Spjd case ZFS_DELEG_NOTE_MOUNT: 4675219089Spjd str = gettext("Allows mount/umount of ZFS datasets"); 4676219089Spjd break; 4677219089Spjd case ZFS_DELEG_NOTE_PROMOTE: 4678219089Spjd str = gettext("Must also have the 'mount'\n\t\t\t\tand" 4679219089Spjd " 'promote' ability in the origin file system"); 4680219089Spjd break; 4681219089Spjd case ZFS_DELEG_NOTE_RECEIVE: 4682219089Spjd str = gettext("Must also have the 'mount' and 'create'" 4683219089Spjd " ability"); 4684219089Spjd break; 4685219089Spjd case ZFS_DELEG_NOTE_RELEASE: 4686219089Spjd str = gettext("Allows releasing a user hold which\n\t\t\t\t" 4687219089Spjd "might destroy the snapshot"); 4688219089Spjd break; 4689219089Spjd case ZFS_DELEG_NOTE_RENAME: 4690219089Spjd str = gettext("Must also have the 'mount' and 'create'" 4691219089Spjd "\n\t\t\t\tability in the new parent"); 4692219089Spjd break; 4693219089Spjd case ZFS_DELEG_NOTE_ROLLBACK: 4694219089Spjd str = gettext(""); 4695219089Spjd break; 4696219089Spjd case ZFS_DELEG_NOTE_SEND: 4697219089Spjd str = gettext(""); 4698219089Spjd break; 4699219089Spjd case ZFS_DELEG_NOTE_SHARE: 4700219089Spjd str = gettext("Allows sharing file systems over NFS or SMB" 4701219089Spjd "\n\t\t\t\tprotocols"); 4702219089Spjd break; 4703219089Spjd case ZFS_DELEG_NOTE_SNAPSHOT: 4704219089Spjd str = gettext(""); 4705219089Spjd break; 4706219089Spjd/* 4707219089Spjd * case ZFS_DELEG_NOTE_VSCAN: 4708219089Spjd * str = gettext(""); 4709219089Spjd * break; 4710219089Spjd */ 4711219089Spjd /* OTHER */ 4712219089Spjd case ZFS_DELEG_NOTE_GROUPQUOTA: 4713219089Spjd str = gettext("Allows accessing any groupquota@... property"); 4714219089Spjd break; 4715219089Spjd case ZFS_DELEG_NOTE_GROUPUSED: 4716219089Spjd str = gettext("Allows reading any groupused@... property"); 4717219089Spjd break; 4718219089Spjd case ZFS_DELEG_NOTE_USERPROP: 4719219089Spjd str = gettext("Allows changing any user property"); 4720219089Spjd break; 4721219089Spjd case ZFS_DELEG_NOTE_USERQUOTA: 4722219089Spjd str = gettext("Allows accessing any userquota@... property"); 4723219089Spjd break; 4724219089Spjd case ZFS_DELEG_NOTE_USERUSED: 4725219089Spjd str = gettext("Allows reading any userused@... property"); 4726219089Spjd break; 4727219089Spjd /* other */ 4728219089Spjd default: 4729219089Spjd str = ""; 4730219089Spjd } 4731219089Spjd 4732219089Spjd return (str); 4733219089Spjd} 4734219089Spjd 4735219089Spjdstruct allow_opts { 4736219089Spjd boolean_t local; 4737219089Spjd boolean_t descend; 4738219089Spjd boolean_t user; 4739219089Spjd boolean_t group; 4740219089Spjd boolean_t everyone; 4741219089Spjd boolean_t create; 4742219089Spjd boolean_t set; 4743219089Spjd boolean_t recursive; /* unallow only */ 4744219089Spjd boolean_t prt_usage; 4745219089Spjd 4746219089Spjd boolean_t prt_perms; 4747219089Spjd char *who; 4748219089Spjd char *perms; 4749219089Spjd const char *dataset; 4750219089Spjd}; 4751219089Spjd 4752219089Spjdstatic inline int 4753219089Spjdprop_cmp(const void *a, const void *b) 4754219089Spjd{ 4755219089Spjd const char *str1 = *(const char **)a; 4756219089Spjd const char *str2 = *(const char **)b; 4757219089Spjd return (strcmp(str1, str2)); 4758219089Spjd} 4759219089Spjd 4760219089Spjdstatic void 4761219089Spjdallow_usage(boolean_t un, boolean_t requested, const char *msg) 4762219089Spjd{ 4763219089Spjd const char *opt_desc[] = { 4764219089Spjd "-h", gettext("show this help message and exit"), 4765219089Spjd "-l", gettext("set permission locally"), 4766219089Spjd "-d", gettext("set permission for descents"), 4767219089Spjd "-u", gettext("set permission for user"), 4768219089Spjd "-g", gettext("set permission for group"), 4769219089Spjd "-e", gettext("set permission for everyone"), 4770219089Spjd "-c", gettext("set create time permission"), 4771219089Spjd "-s", gettext("define permission set"), 4772219089Spjd /* unallow only */ 4773219089Spjd "-r", gettext("remove permissions recursively"), 4774219089Spjd }; 4775219089Spjd size_t unallow_size = sizeof (opt_desc) / sizeof (char *); 4776219089Spjd size_t allow_size = unallow_size - 2; 4777219089Spjd const char *props[ZFS_NUM_PROPS]; 4778219089Spjd int i; 4779219089Spjd size_t count = 0; 4780219089Spjd FILE *fp = requested ? stdout : stderr; 4781219089Spjd zprop_desc_t *pdtbl = zfs_prop_get_table(); 4782219089Spjd const char *fmt = gettext("%-16s %-14s\t%s\n"); 4783219089Spjd 4784219089Spjd (void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW : 4785219089Spjd HELP_ALLOW)); 4786219089Spjd (void) fprintf(fp, gettext("Options:\n")); 4787219089Spjd for (i = 0; i < (un ? unallow_size : allow_size); i++) { 4788219089Spjd const char *opt = opt_desc[i++]; 4789219089Spjd const char *optdsc = opt_desc[i]; 4790219089Spjd (void) fprintf(fp, gettext(" %-10s %s\n"), opt, optdsc); 4791219089Spjd } 4792219089Spjd 4793219089Spjd (void) fprintf(fp, gettext("\nThe following permissions are " 4794219089Spjd "supported:\n\n")); 4795219089Spjd (void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"), 4796219089Spjd gettext("NOTES")); 4797219089Spjd for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) { 4798219089Spjd const char *perm_name = zfs_deleg_perm_tbl[i].z_perm; 4799219089Spjd zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note; 4800219089Spjd const char *perm_type = deleg_perm_type(perm_note); 4801219089Spjd const char *perm_comment = deleg_perm_comment(perm_note); 4802219089Spjd (void) fprintf(fp, fmt, perm_name, perm_type, perm_comment); 4803219089Spjd } 4804219089Spjd 4805219089Spjd for (i = 0; i < ZFS_NUM_PROPS; i++) { 4806219089Spjd zprop_desc_t *pd = &pdtbl[i]; 4807219089Spjd if (pd->pd_visible != B_TRUE) 4808219089Spjd continue; 4809219089Spjd 4810219089Spjd if (pd->pd_attr == PROP_READONLY) 4811219089Spjd continue; 4812219089Spjd 4813219089Spjd props[count++] = pd->pd_name; 4814219089Spjd } 4815219089Spjd props[count] = NULL; 4816219089Spjd 4817219089Spjd qsort(props, count, sizeof (char *), prop_cmp); 4818219089Spjd 4819219089Spjd for (i = 0; i < count; i++) 4820219089Spjd (void) fprintf(fp, fmt, props[i], gettext("property"), ""); 4821219089Spjd 4822219089Spjd if (msg != NULL) 4823219089Spjd (void) fprintf(fp, gettext("\nzfs: error: %s"), msg); 4824219089Spjd 4825219089Spjd exit(requested ? 0 : 2); 4826219089Spjd} 4827219089Spjd 4828219089Spjdstatic inline const char * 4829219089Spjdmunge_args(int argc, char **argv, boolean_t un, size_t expected_argc, 4830219089Spjd char **permsp) 4831219089Spjd{ 4832219089Spjd if (un && argc == expected_argc - 1) 4833219089Spjd *permsp = NULL; 4834219089Spjd else if (argc == expected_argc) 4835219089Spjd *permsp = argv[argc - 2]; 4836219089Spjd else 4837219089Spjd allow_usage(un, B_FALSE, 4838219089Spjd gettext("wrong number of parameters\n")); 4839219089Spjd 4840219089Spjd return (argv[argc - 1]); 4841219089Spjd} 4842219089Spjd 4843219089Spjdstatic void 4844219089Spjdparse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts) 4845219089Spjd{ 4846219089Spjd int uge_sum = opts->user + opts->group + opts->everyone; 4847219089Spjd int csuge_sum = opts->create + opts->set + uge_sum; 4848219089Spjd int ldcsuge_sum = csuge_sum + opts->local + opts->descend; 4849219089Spjd int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum; 4850219089Spjd 4851219089Spjd if (uge_sum > 1) 4852219089Spjd allow_usage(un, B_FALSE, 4853219089Spjd gettext("-u, -g, and -e are mutually exclusive\n")); 4854219089Spjd 4855219089Spjd if (opts->prt_usage) 4856219089Spjd if (argc == 0 && all_sum == 0) 4857219089Spjd allow_usage(un, B_TRUE, NULL); 4858219089Spjd else 4859219089Spjd usage(B_FALSE); 4860219089Spjd 4861219089Spjd if (opts->set) { 4862219089Spjd if (csuge_sum > 1) 4863219089Spjd allow_usage(un, B_FALSE, 4864219089Spjd gettext("invalid options combined with -s\n")); 4865219089Spjd 4866219089Spjd opts->dataset = munge_args(argc, argv, un, 3, &opts->perms); 4867219089Spjd if (argv[0][0] != '@') 4868219089Spjd allow_usage(un, B_FALSE, 4869219089Spjd gettext("invalid set name: missing '@' prefix\n")); 4870219089Spjd opts->who = argv[0]; 4871219089Spjd } else if (opts->create) { 4872219089Spjd if (ldcsuge_sum > 1) 4873219089Spjd allow_usage(un, B_FALSE, 4874219089Spjd gettext("invalid options combined with -c\n")); 4875219089Spjd opts->dataset = munge_args(argc, argv, un, 2, &opts->perms); 4876219089Spjd } else if (opts->everyone) { 4877219089Spjd if (csuge_sum > 1) 4878219089Spjd allow_usage(un, B_FALSE, 4879219089Spjd gettext("invalid options combined with -e\n")); 4880219089Spjd opts->dataset = munge_args(argc, argv, un, 2, &opts->perms); 4881219089Spjd } else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone") 4882219089Spjd == 0) { 4883219089Spjd opts->everyone = B_TRUE; 4884219089Spjd argc--; 4885219089Spjd argv++; 4886219089Spjd opts->dataset = munge_args(argc, argv, un, 2, &opts->perms); 4887230449Smm } else if (argc == 1 && !un) { 4888219089Spjd opts->prt_perms = B_TRUE; 4889219089Spjd opts->dataset = argv[argc-1]; 4890219089Spjd } else { 4891219089Spjd opts->dataset = munge_args(argc, argv, un, 3, &opts->perms); 4892219089Spjd opts->who = argv[0]; 4893219089Spjd } 4894219089Spjd 4895219089Spjd if (!opts->local && !opts->descend) { 4896219089Spjd opts->local = B_TRUE; 4897219089Spjd opts->descend = B_TRUE; 4898219089Spjd } 4899219089Spjd} 4900219089Spjd 4901219089Spjdstatic void 4902219089Spjdstore_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend, 4903219089Spjd const char *who, char *perms, nvlist_t *top_nvl) 4904219089Spjd{ 4905219089Spjd int i; 4906219089Spjd char ld[2] = { '\0', '\0' }; 4907219089Spjd char who_buf[ZFS_MAXNAMELEN+32]; 4908219089Spjd char base_type; 4909219089Spjd char set_type; 4910219089Spjd nvlist_t *base_nvl = NULL; 4911219089Spjd nvlist_t *set_nvl = NULL; 4912219089Spjd nvlist_t *nvl; 4913219089Spjd 4914219089Spjd if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0) 4915219089Spjd nomem(); 4916219089Spjd if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) != 0) 4917219089Spjd nomem(); 4918219089Spjd 4919219089Spjd switch (type) { 4920219089Spjd case ZFS_DELEG_NAMED_SET_SETS: 4921219089Spjd case ZFS_DELEG_NAMED_SET: 4922219089Spjd set_type = ZFS_DELEG_NAMED_SET_SETS; 4923219089Spjd base_type = ZFS_DELEG_NAMED_SET; 4924219089Spjd ld[0] = ZFS_DELEG_NA; 4925219089Spjd break; 4926219089Spjd case ZFS_DELEG_CREATE_SETS: 4927219089Spjd case ZFS_DELEG_CREATE: 4928219089Spjd set_type = ZFS_DELEG_CREATE_SETS; 4929219089Spjd base_type = ZFS_DELEG_CREATE; 4930219089Spjd ld[0] = ZFS_DELEG_NA; 4931219089Spjd break; 4932219089Spjd case ZFS_DELEG_USER_SETS: 4933219089Spjd case ZFS_DELEG_USER: 4934219089Spjd set_type = ZFS_DELEG_USER_SETS; 4935219089Spjd base_type = ZFS_DELEG_USER; 4936219089Spjd if (local) 4937219089Spjd ld[0] = ZFS_DELEG_LOCAL; 4938219089Spjd if (descend) 4939219089Spjd ld[1] = ZFS_DELEG_DESCENDENT; 4940219089Spjd break; 4941219089Spjd case ZFS_DELEG_GROUP_SETS: 4942219089Spjd case ZFS_DELEG_GROUP: 4943219089Spjd set_type = ZFS_DELEG_GROUP_SETS; 4944219089Spjd base_type = ZFS_DELEG_GROUP; 4945219089Spjd if (local) 4946219089Spjd ld[0] = ZFS_DELEG_LOCAL; 4947219089Spjd if (descend) 4948219089Spjd ld[1] = ZFS_DELEG_DESCENDENT; 4949219089Spjd break; 4950219089Spjd case ZFS_DELEG_EVERYONE_SETS: 4951219089Spjd case ZFS_DELEG_EVERYONE: 4952219089Spjd set_type = ZFS_DELEG_EVERYONE_SETS; 4953219089Spjd base_type = ZFS_DELEG_EVERYONE; 4954219089Spjd if (local) 4955219089Spjd ld[0] = ZFS_DELEG_LOCAL; 4956219089Spjd if (descend) 4957219089Spjd ld[1] = ZFS_DELEG_DESCENDENT; 4958219089Spjd } 4959219089Spjd 4960219089Spjd if (perms != NULL) { 4961219089Spjd char *curr = perms; 4962219089Spjd char *end = curr + strlen(perms); 4963219089Spjd 4964219089Spjd while (curr < end) { 4965219089Spjd char *delim = strchr(curr, ','); 4966219089Spjd if (delim == NULL) 4967219089Spjd delim = end; 4968219089Spjd else 4969219089Spjd *delim = '\0'; 4970219089Spjd 4971219089Spjd if (curr[0] == '@') 4972219089Spjd nvl = set_nvl; 4973219089Spjd else 4974219089Spjd nvl = base_nvl; 4975219089Spjd 4976219089Spjd (void) nvlist_add_boolean(nvl, curr); 4977219089Spjd if (delim != end) 4978219089Spjd *delim = ','; 4979219089Spjd curr = delim + 1; 4980219089Spjd } 4981219089Spjd 4982219089Spjd for (i = 0; i < 2; i++) { 4983219089Spjd char locality = ld[i]; 4984219089Spjd if (locality == 0) 4985219089Spjd continue; 4986219089Spjd 4987219089Spjd if (!nvlist_empty(base_nvl)) { 4988219089Spjd if (who != NULL) 4989219089Spjd (void) snprintf(who_buf, 4990219089Spjd sizeof (who_buf), "%c%c$%s", 4991219089Spjd base_type, locality, who); 4992219089Spjd else 4993219089Spjd (void) snprintf(who_buf, 4994219089Spjd sizeof (who_buf), "%c%c$", 4995219089Spjd base_type, locality); 4996219089Spjd 4997219089Spjd (void) nvlist_add_nvlist(top_nvl, who_buf, 4998219089Spjd base_nvl); 4999219089Spjd } 5000219089Spjd 5001219089Spjd 5002219089Spjd if (!nvlist_empty(set_nvl)) { 5003219089Spjd if (who != NULL) 5004219089Spjd (void) snprintf(who_buf, 5005219089Spjd sizeof (who_buf), "%c%c$%s", 5006219089Spjd set_type, locality, who); 5007219089Spjd else 5008219089Spjd (void) snprintf(who_buf, 5009219089Spjd sizeof (who_buf), "%c%c$", 5010219089Spjd set_type, locality); 5011219089Spjd 5012219089Spjd (void) nvlist_add_nvlist(top_nvl, who_buf, 5013219089Spjd set_nvl); 5014219089Spjd } 5015219089Spjd } 5016219089Spjd } else { 5017219089Spjd for (i = 0; i < 2; i++) { 5018219089Spjd char locality = ld[i]; 5019219089Spjd if (locality == 0) 5020219089Spjd continue; 5021219089Spjd 5022219089Spjd if (who != NULL) 5023219089Spjd (void) snprintf(who_buf, sizeof (who_buf), 5024219089Spjd "%c%c$%s", base_type, locality, who); 5025219089Spjd else 5026219089Spjd (void) snprintf(who_buf, sizeof (who_buf), 5027219089Spjd "%c%c$", base_type, locality); 5028219089Spjd (void) nvlist_add_boolean(top_nvl, who_buf); 5029219089Spjd 5030219089Spjd if (who != NULL) 5031219089Spjd (void) snprintf(who_buf, sizeof (who_buf), 5032219089Spjd "%c%c$%s", set_type, locality, who); 5033219089Spjd else 5034219089Spjd (void) snprintf(who_buf, sizeof (who_buf), 5035219089Spjd "%c%c$", set_type, locality); 5036219089Spjd (void) nvlist_add_boolean(top_nvl, who_buf); 5037219089Spjd } 5038219089Spjd } 5039219089Spjd} 5040219089Spjd 5041219089Spjdstatic int 5042219089Spjdconstruct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp) 5043219089Spjd{ 5044219089Spjd if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0) 5045219089Spjd nomem(); 5046219089Spjd 5047219089Spjd if (opts->set) { 5048219089Spjd store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local, 5049219089Spjd opts->descend, opts->who, opts->perms, *nvlp); 5050219089Spjd } else if (opts->create) { 5051219089Spjd store_allow_perm(ZFS_DELEG_CREATE, opts->local, 5052219089Spjd opts->descend, NULL, opts->perms, *nvlp); 5053219089Spjd } else if (opts->everyone) { 5054219089Spjd store_allow_perm(ZFS_DELEG_EVERYONE, opts->local, 5055219089Spjd opts->descend, NULL, opts->perms, *nvlp); 5056219089Spjd } else { 5057219089Spjd char *curr = opts->who; 5058219089Spjd char *end = curr + strlen(curr); 5059219089Spjd 5060219089Spjd while (curr < end) { 5061219089Spjd const char *who; 5062219089Spjd zfs_deleg_who_type_t who_type; 5063219089Spjd char *endch; 5064219089Spjd char *delim = strchr(curr, ','); 5065219089Spjd char errbuf[256]; 5066219089Spjd char id[64]; 5067219089Spjd struct passwd *p = NULL; 5068219089Spjd struct group *g = NULL; 5069219089Spjd 5070219089Spjd uid_t rid; 5071219089Spjd if (delim == NULL) 5072219089Spjd delim = end; 5073219089Spjd else 5074219089Spjd *delim = '\0'; 5075219089Spjd 5076219089Spjd rid = (uid_t)strtol(curr, &endch, 0); 5077219089Spjd if (opts->user) { 5078219089Spjd who_type = ZFS_DELEG_USER; 5079219089Spjd if (*endch != '\0') 5080219089Spjd p = getpwnam(curr); 5081219089Spjd else 5082219089Spjd p = getpwuid(rid); 5083219089Spjd 5084219089Spjd if (p != NULL) 5085219089Spjd rid = p->pw_uid; 5086219089Spjd else { 5087219089Spjd (void) snprintf(errbuf, 256, gettext( 5088219089Spjd "invalid user %s"), curr); 5089219089Spjd allow_usage(un, B_TRUE, errbuf); 5090219089Spjd } 5091219089Spjd } else if (opts->group) { 5092219089Spjd who_type = ZFS_DELEG_GROUP; 5093219089Spjd if (*endch != '\0') 5094219089Spjd g = getgrnam(curr); 5095219089Spjd else 5096219089Spjd g = getgrgid(rid); 5097219089Spjd 5098219089Spjd if (g != NULL) 5099219089Spjd rid = g->gr_gid; 5100219089Spjd else { 5101219089Spjd (void) snprintf(errbuf, 256, gettext( 5102219089Spjd "invalid group %s"), curr); 5103219089Spjd allow_usage(un, B_TRUE, errbuf); 5104219089Spjd } 5105219089Spjd } else { 5106219089Spjd if (*endch != '\0') { 5107219089Spjd p = getpwnam(curr); 5108219089Spjd } else { 5109219089Spjd p = getpwuid(rid); 5110219089Spjd } 5111219089Spjd 5112219089Spjd if (p == NULL) 5113219089Spjd if (*endch != '\0') { 5114219089Spjd g = getgrnam(curr); 5115219089Spjd } else { 5116219089Spjd g = getgrgid(rid); 5117219089Spjd } 5118219089Spjd 5119219089Spjd if (p != NULL) { 5120219089Spjd who_type = ZFS_DELEG_USER; 5121219089Spjd rid = p->pw_uid; 5122219089Spjd } else if (g != NULL) { 5123219089Spjd who_type = ZFS_DELEG_GROUP; 5124219089Spjd rid = g->gr_gid; 5125219089Spjd } else { 5126219089Spjd (void) snprintf(errbuf, 256, gettext( 5127219089Spjd "invalid user/group %s"), curr); 5128219089Spjd allow_usage(un, B_TRUE, errbuf); 5129219089Spjd } 5130219089Spjd } 5131219089Spjd 5132219089Spjd (void) sprintf(id, "%u", rid); 5133219089Spjd who = id; 5134219089Spjd 5135219089Spjd store_allow_perm(who_type, opts->local, 5136219089Spjd opts->descend, who, opts->perms, *nvlp); 5137219089Spjd curr = delim + 1; 5138219089Spjd } 5139219089Spjd } 5140219089Spjd 5141219089Spjd return (0); 5142219089Spjd} 5143219089Spjd 5144219089Spjdstatic void 5145219089Spjdprint_set_creat_perms(uu_avl_t *who_avl) 5146219089Spjd{ 5147219089Spjd const char *sc_title[] = { 5148219089Spjd gettext("Permission sets:\n"), 5149219089Spjd gettext("Create time permissions:\n"), 5150219089Spjd NULL 5151219089Spjd }; 5152219089Spjd const char **title_ptr = sc_title; 5153219089Spjd who_perm_node_t *who_node = NULL; 5154219089Spjd int prev_weight = -1; 5155219089Spjd 5156219089Spjd for (who_node = uu_avl_first(who_avl); who_node != NULL; 5157219089Spjd who_node = uu_avl_next(who_avl, who_node)) { 5158219089Spjd uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl; 5159219089Spjd zfs_deleg_who_type_t who_type = who_node->who_perm.who_type; 5160219089Spjd const char *who_name = who_node->who_perm.who_name; 5161219089Spjd int weight = who_type2weight(who_type); 5162219089Spjd boolean_t first = B_TRUE; 5163219089Spjd deleg_perm_node_t *deleg_node; 5164219089Spjd 5165219089Spjd if (prev_weight != weight) { 5166219089Spjd (void) printf(*title_ptr++); 5167219089Spjd prev_weight = weight; 5168219089Spjd } 5169219089Spjd 5170219089Spjd if (who_name == NULL || strnlen(who_name, 1) == 0) 5171219089Spjd (void) printf("\t"); 5172219089Spjd else 5173219089Spjd (void) printf("\t%s ", who_name); 5174219089Spjd 5175219089Spjd for (deleg_node = uu_avl_first(avl); deleg_node != NULL; 5176219089Spjd deleg_node = uu_avl_next(avl, deleg_node)) { 5177219089Spjd if (first) { 5178219089Spjd (void) printf("%s", 5179219089Spjd deleg_node->dpn_perm.dp_name); 5180219089Spjd first = B_FALSE; 5181219089Spjd } else 5182219089Spjd (void) printf(",%s", 5183219089Spjd deleg_node->dpn_perm.dp_name); 5184219089Spjd } 5185219089Spjd 5186219089Spjd (void) printf("\n"); 5187219089Spjd } 5188219089Spjd} 5189219089Spjd 5190219089Spjdstatic void inline 5191219089Spjdprint_uge_deleg_perms(uu_avl_t *who_avl, boolean_t local, boolean_t descend, 5192219089Spjd const char *title) 5193219089Spjd{ 5194219089Spjd who_perm_node_t *who_node = NULL; 5195219089Spjd boolean_t prt_title = B_TRUE; 5196219089Spjd uu_avl_walk_t *walk; 5197219089Spjd 5198219089Spjd if ((walk = uu_avl_walk_start(who_avl, UU_WALK_ROBUST)) == NULL) 5199219089Spjd nomem(); 5200219089Spjd 5201219089Spjd while ((who_node = uu_avl_walk_next(walk)) != NULL) { 5202219089Spjd const char *who_name = who_node->who_perm.who_name; 5203219089Spjd const char *nice_who_name = who_node->who_perm.who_ug_name; 5204219089Spjd uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl; 5205219089Spjd zfs_deleg_who_type_t who_type = who_node->who_perm.who_type; 5206219089Spjd char delim = ' '; 5207219089Spjd deleg_perm_node_t *deleg_node; 5208219089Spjd boolean_t prt_who = B_TRUE; 5209219089Spjd 5210219089Spjd for (deleg_node = uu_avl_first(avl); 5211219089Spjd deleg_node != NULL; 5212219089Spjd deleg_node = uu_avl_next(avl, deleg_node)) { 5213219089Spjd if (local != deleg_node->dpn_perm.dp_local || 5214219089Spjd descend != deleg_node->dpn_perm.dp_descend) 5215219089Spjd continue; 5216219089Spjd 5217219089Spjd if (prt_who) { 5218219089Spjd const char *who = NULL; 5219219089Spjd if (prt_title) { 5220219089Spjd prt_title = B_FALSE; 5221219089Spjd (void) printf(title); 5222219089Spjd } 5223219089Spjd 5224219089Spjd switch (who_type) { 5225219089Spjd case ZFS_DELEG_USER_SETS: 5226219089Spjd case ZFS_DELEG_USER: 5227219089Spjd who = gettext("user"); 5228219089Spjd if (nice_who_name) 5229219089Spjd who_name = nice_who_name; 5230219089Spjd break; 5231219089Spjd case ZFS_DELEG_GROUP_SETS: 5232219089Spjd case ZFS_DELEG_GROUP: 5233219089Spjd who = gettext("group"); 5234219089Spjd if (nice_who_name) 5235219089Spjd who_name = nice_who_name; 5236219089Spjd break; 5237219089Spjd case ZFS_DELEG_EVERYONE_SETS: 5238219089Spjd case ZFS_DELEG_EVERYONE: 5239219089Spjd who = gettext("everyone"); 5240219089Spjd who_name = NULL; 5241219089Spjd } 5242219089Spjd 5243219089Spjd prt_who = B_FALSE; 5244219089Spjd if (who_name == NULL) 5245219089Spjd (void) printf("\t%s", who); 5246219089Spjd else 5247219089Spjd (void) printf("\t%s %s", who, who_name); 5248219089Spjd } 5249219089Spjd 5250219089Spjd (void) printf("%c%s", delim, 5251219089Spjd deleg_node->dpn_perm.dp_name); 5252219089Spjd delim = ','; 5253219089Spjd } 5254219089Spjd 5255219089Spjd if (!prt_who) 5256219089Spjd (void) printf("\n"); 5257219089Spjd } 5258219089Spjd 5259219089Spjd uu_avl_walk_end(walk); 5260219089Spjd} 5261219089Spjd 5262219089Spjdstatic void 5263219089Spjdprint_fs_perms(fs_perm_set_t *fspset) 5264219089Spjd{ 5265219089Spjd fs_perm_node_t *node = NULL; 5266219089Spjd char buf[ZFS_MAXNAMELEN+32]; 5267219089Spjd const char *dsname = buf; 5268219089Spjd 5269219089Spjd for (node = uu_list_first(fspset->fsps_list); node != NULL; 5270219089Spjd node = uu_list_next(fspset->fsps_list, node)) { 5271219089Spjd uu_avl_t *sc_avl = node->fspn_fsperm.fsp_sc_avl; 5272219089Spjd uu_avl_t *uge_avl = node->fspn_fsperm.fsp_uge_avl; 5273219089Spjd int left = 0; 5274219089Spjd 5275219089Spjd (void) snprintf(buf, ZFS_MAXNAMELEN+32, 5276219089Spjd gettext("---- Permissions on %s "), 5277219089Spjd node->fspn_fsperm.fsp_name); 5278219089Spjd (void) printf(dsname); 5279219089Spjd left = 70 - strlen(buf); 5280219089Spjd while (left-- > 0) 5281219089Spjd (void) printf("-"); 5282219089Spjd (void) printf("\n"); 5283219089Spjd 5284219089Spjd print_set_creat_perms(sc_avl); 5285219089Spjd print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE, 5286219089Spjd gettext("Local permissions:\n")); 5287219089Spjd print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE, 5288219089Spjd gettext("Descendent permissions:\n")); 5289219089Spjd print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE, 5290219089Spjd gettext("Local+Descendent permissions:\n")); 5291219089Spjd } 5292219089Spjd} 5293219089Spjd 5294219089Spjdstatic fs_perm_set_t fs_perm_set = { NULL, NULL, NULL, NULL }; 5295219089Spjd 5296219089Spjdstruct deleg_perms { 5297219089Spjd boolean_t un; 5298219089Spjd nvlist_t *nvl; 5299219089Spjd}; 5300219089Spjd 5301219089Spjdstatic int 5302219089Spjdset_deleg_perms(zfs_handle_t *zhp, void *data) 5303219089Spjd{ 5304219089Spjd struct deleg_perms *perms = (struct deleg_perms *)data; 5305219089Spjd zfs_type_t zfs_type = zfs_get_type(zhp); 5306219089Spjd 5307219089Spjd if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME) 5308219089Spjd return (0); 5309219089Spjd 5310219089Spjd return (zfs_set_fsacl(zhp, perms->un, perms->nvl)); 5311219089Spjd} 5312219089Spjd 5313219089Spjdstatic int 5314219089Spjdzfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un) 5315219089Spjd{ 5316219089Spjd zfs_handle_t *zhp; 5317219089Spjd nvlist_t *perm_nvl = NULL; 5318219089Spjd nvlist_t *update_perm_nvl = NULL; 5319219089Spjd int error = 1; 5320219089Spjd int c; 5321219089Spjd struct allow_opts opts = { 0 }; 5322219089Spjd 5323219089Spjd const char *optstr = un ? "ldugecsrh" : "ldugecsh"; 5324219089Spjd 5325219089Spjd /* check opts */ 5326219089Spjd while ((c = getopt(argc, argv, optstr)) != -1) { 5327219089Spjd switch (c) { 5328219089Spjd case 'l': 5329219089Spjd opts.local = B_TRUE; 5330219089Spjd break; 5331219089Spjd case 'd': 5332219089Spjd opts.descend = B_TRUE; 5333219089Spjd break; 5334219089Spjd case 'u': 5335219089Spjd opts.user = B_TRUE; 5336219089Spjd break; 5337219089Spjd case 'g': 5338219089Spjd opts.group = B_TRUE; 5339219089Spjd break; 5340219089Spjd case 'e': 5341219089Spjd opts.everyone = B_TRUE; 5342219089Spjd break; 5343219089Spjd case 's': 5344219089Spjd opts.set = B_TRUE; 5345219089Spjd break; 5346219089Spjd case 'c': 5347219089Spjd opts.create = B_TRUE; 5348219089Spjd break; 5349219089Spjd case 'r': 5350219089Spjd opts.recursive = B_TRUE; 5351219089Spjd break; 5352219089Spjd case ':': 5353219089Spjd (void) fprintf(stderr, gettext("missing argument for " 5354219089Spjd "'%c' option\n"), optopt); 5355219089Spjd usage(B_FALSE); 5356219089Spjd break; 5357219089Spjd case 'h': 5358219089Spjd opts.prt_usage = B_TRUE; 5359219089Spjd break; 5360219089Spjd case '?': 5361219089Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 5362219089Spjd optopt); 5363219089Spjd usage(B_FALSE); 5364219089Spjd } 5365219089Spjd } 5366219089Spjd 5367219089Spjd argc -= optind; 5368219089Spjd argv += optind; 5369219089Spjd 5370219089Spjd /* check arguments */ 5371219089Spjd parse_allow_args(argc, argv, un, &opts); 5372219089Spjd 5373219089Spjd /* try to open the dataset */ 5374230449Smm if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM | 5375230449Smm ZFS_TYPE_VOLUME)) == NULL) { 5376230449Smm (void) fprintf(stderr, "Failed to open dataset: %s\n", 5377219089Spjd opts.dataset); 5378219089Spjd return (-1); 5379219089Spjd } 5380219089Spjd 5381219089Spjd if (zfs_get_fsacl(zhp, &perm_nvl) != 0) 5382219089Spjd goto cleanup2; 5383219089Spjd 5384219089Spjd fs_perm_set_init(&fs_perm_set); 5385219089Spjd if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) { 5386230449Smm (void) fprintf(stderr, "Failed to parse fsacl permissions\n"); 5387219089Spjd goto cleanup1; 5388219089Spjd } 5389219089Spjd 5390219089Spjd if (opts.prt_perms) 5391219089Spjd print_fs_perms(&fs_perm_set); 5392219089Spjd else { 5393219089Spjd (void) construct_fsacl_list(un, &opts, &update_perm_nvl); 5394219089Spjd if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0) 5395219089Spjd goto cleanup0; 5396219089Spjd 5397219089Spjd if (un && opts.recursive) { 5398219089Spjd struct deleg_perms data = { un, update_perm_nvl }; 5399219089Spjd if (zfs_iter_filesystems(zhp, set_deleg_perms, 5400219089Spjd &data) != 0) 5401219089Spjd goto cleanup0; 5402219089Spjd } 5403219089Spjd } 5404219089Spjd 5405219089Spjd error = 0; 5406219089Spjd 5407219089Spjdcleanup0: 5408219089Spjd nvlist_free(perm_nvl); 5409219089Spjd if (update_perm_nvl != NULL) 5410219089Spjd nvlist_free(update_perm_nvl); 5411219089Spjdcleanup1: 5412219089Spjd fs_perm_set_fini(&fs_perm_set); 5413219089Spjdcleanup2: 5414219089Spjd zfs_close(zhp); 5415219089Spjd 5416219089Spjd return (error); 5417219089Spjd} 5418219089Spjd 5419219089Spjdstatic int 5420219089Spjdzfs_do_allow(int argc, char **argv) 5421219089Spjd{ 5422219089Spjd return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE)); 5423219089Spjd} 5424219089Spjd 5425219089Spjdstatic int 5426219089Spjdzfs_do_unallow(int argc, char **argv) 5427219089Spjd{ 5428219089Spjd return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE)); 5429219089Spjd} 5430219089Spjd 5431219089Spjdstatic int 5432219089Spjdzfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding) 5433219089Spjd{ 5434219089Spjd int errors = 0; 5435219089Spjd int i; 5436219089Spjd const char *tag; 5437219089Spjd boolean_t recursive = B_FALSE; 5438219089Spjd const char *opts = holding ? "rt" : "r"; 5439219089Spjd int c; 5440219089Spjd 5441219089Spjd /* check options */ 5442219089Spjd while ((c = getopt(argc, argv, opts)) != -1) { 5443219089Spjd switch (c) { 5444219089Spjd case 'r': 5445219089Spjd recursive = B_TRUE; 5446219089Spjd break; 5447219089Spjd case '?': 5448219089Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 5449219089Spjd optopt); 5450219089Spjd usage(B_FALSE); 5451219089Spjd } 5452219089Spjd } 5453219089Spjd 5454219089Spjd argc -= optind; 5455219089Spjd argv += optind; 5456219089Spjd 5457219089Spjd /* check number of arguments */ 5458219089Spjd if (argc < 2) 5459219089Spjd usage(B_FALSE); 5460219089Spjd 5461219089Spjd tag = argv[0]; 5462219089Spjd --argc; 5463219089Spjd ++argv; 5464219089Spjd 5465219089Spjd if (holding && tag[0] == '.') { 5466219089Spjd /* tags starting with '.' are reserved for libzfs */ 5467219089Spjd (void) fprintf(stderr, gettext("tag may not start with '.'\n")); 5468219089Spjd usage(B_FALSE); 5469219089Spjd } 5470219089Spjd 5471219089Spjd for (i = 0; i < argc; ++i) { 5472219089Spjd zfs_handle_t *zhp; 5473219089Spjd char parent[ZFS_MAXNAMELEN]; 5474219089Spjd const char *delim; 5475219089Spjd char *path = argv[i]; 5476219089Spjd 5477219089Spjd delim = strchr(path, '@'); 5478219089Spjd if (delim == NULL) { 5479219089Spjd (void) fprintf(stderr, 5480219089Spjd gettext("'%s' is not a snapshot\n"), path); 5481219089Spjd ++errors; 5482219089Spjd continue; 5483219089Spjd } 5484219089Spjd (void) strncpy(parent, path, delim - path); 5485219089Spjd parent[delim - path] = '\0'; 5486219089Spjd 5487219089Spjd zhp = zfs_open(g_zfs, parent, 5488219089Spjd ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 5489219089Spjd if (zhp == NULL) { 5490219089Spjd ++errors; 5491219089Spjd continue; 5492219089Spjd } 5493219089Spjd if (holding) { 5494251646Sdelphij if (zfs_hold(zhp, delim+1, tag, recursive, -1) != 0) 5495219089Spjd ++errors; 5496219089Spjd } else { 5497219089Spjd if (zfs_release(zhp, delim+1, tag, recursive) != 0) 5498219089Spjd ++errors; 5499219089Spjd } 5500219089Spjd zfs_close(zhp); 5501219089Spjd } 5502219089Spjd 5503219089Spjd return (errors != 0); 5504219089Spjd} 5505219089Spjd 5506219089Spjd/* 5507219089Spjd * zfs hold [-r] [-t] <tag> <snap> ... 5508219089Spjd * 5509219089Spjd * -r Recursively hold 5510219089Spjd * 5511219089Spjd * Apply a user-hold with the given tag to the list of snapshots. 5512219089Spjd */ 5513219089Spjdstatic int 5514219089Spjdzfs_do_hold(int argc, char **argv) 5515219089Spjd{ 5516219089Spjd return (zfs_do_hold_rele_impl(argc, argv, B_TRUE)); 5517219089Spjd} 5518219089Spjd 5519219089Spjd/* 5520219089Spjd * zfs release [-r] <tag> <snap> ... 5521219089Spjd * 5522219089Spjd * -r Recursively release 5523219089Spjd * 5524219089Spjd * Release a user-hold with the given tag from the list of snapshots. 5525219089Spjd */ 5526219089Spjdstatic int 5527219089Spjdzfs_do_release(int argc, char **argv) 5528219089Spjd{ 5529219089Spjd return (zfs_do_hold_rele_impl(argc, argv, B_FALSE)); 5530219089Spjd} 5531219089Spjd 5532219089Spjdtypedef struct holds_cbdata { 5533219089Spjd boolean_t cb_recursive; 5534219089Spjd const char *cb_snapname; 5535219089Spjd nvlist_t **cb_nvlp; 5536219089Spjd size_t cb_max_namelen; 5537219089Spjd size_t cb_max_taglen; 5538219089Spjd} holds_cbdata_t; 5539219089Spjd 5540219089Spjd#define STRFTIME_FMT_STR "%a %b %e %k:%M %Y" 5541219089Spjd#define DATETIME_BUF_LEN (32) 5542219089Spjd/* 5543219089Spjd * 5544219089Spjd */ 5545219089Spjdstatic void 5546219089Spjdprint_holds(boolean_t scripted, size_t nwidth, size_t tagwidth, nvlist_t *nvl) 5547219089Spjd{ 5548219089Spjd int i; 5549219089Spjd nvpair_t *nvp = NULL; 5550219089Spjd char *hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" }; 5551219089Spjd const char *col; 5552219089Spjd 5553219089Spjd if (!scripted) { 5554219089Spjd for (i = 0; i < 3; i++) { 5555219089Spjd col = gettext(hdr_cols[i]); 5556219089Spjd if (i < 2) 5557219089Spjd (void) printf("%-*s ", i ? tagwidth : nwidth, 5558219089Spjd col); 5559219089Spjd else 5560219089Spjd (void) printf("%s\n", col); 5561219089Spjd } 5562219089Spjd } 5563219089Spjd 5564219089Spjd while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 5565219089Spjd char *zname = nvpair_name(nvp); 5566219089Spjd nvlist_t *nvl2; 5567219089Spjd nvpair_t *nvp2 = NULL; 5568219089Spjd (void) nvpair_value_nvlist(nvp, &nvl2); 5569219089Spjd while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) { 5570219089Spjd char tsbuf[DATETIME_BUF_LEN]; 5571219089Spjd char *tagname = nvpair_name(nvp2); 5572219089Spjd uint64_t val = 0; 5573219089Spjd time_t time; 5574219089Spjd struct tm t; 5575219089Spjd char sep = scripted ? '\t' : ' '; 5576219089Spjd size_t sepnum = scripted ? 1 : 2; 5577219089Spjd 5578219089Spjd (void) nvpair_value_uint64(nvp2, &val); 5579219089Spjd time = (time_t)val; 5580219089Spjd (void) localtime_r(&time, &t); 5581219089Spjd (void) strftime(tsbuf, DATETIME_BUF_LEN, 5582219089Spjd gettext(STRFTIME_FMT_STR), &t); 5583219089Spjd 5584219089Spjd (void) printf("%-*s%*c%-*s%*c%s\n", nwidth, zname, 5585219089Spjd sepnum, sep, tagwidth, tagname, sepnum, sep, tsbuf); 5586219089Spjd } 5587219089Spjd } 5588219089Spjd} 5589219089Spjd 5590219089Spjd/* 5591219089Spjd * Generic callback function to list a dataset or snapshot. 5592219089Spjd */ 5593219089Spjdstatic int 5594219089Spjdholds_callback(zfs_handle_t *zhp, void *data) 5595219089Spjd{ 5596219089Spjd holds_cbdata_t *cbp = data; 5597219089Spjd nvlist_t *top_nvl = *cbp->cb_nvlp; 5598219089Spjd nvlist_t *nvl = NULL; 5599219089Spjd nvpair_t *nvp = NULL; 5600219089Spjd const char *zname = zfs_get_name(zhp); 5601219089Spjd size_t znamelen = strnlen(zname, ZFS_MAXNAMELEN); 5602219089Spjd 5603219089Spjd if (cbp->cb_recursive) { 5604219089Spjd const char *snapname; 5605219089Spjd char *delim = strchr(zname, '@'); 5606219089Spjd if (delim == NULL) 5607219089Spjd return (0); 5608219089Spjd 5609219089Spjd snapname = delim + 1; 5610219089Spjd if (strcmp(cbp->cb_snapname, snapname)) 5611219089Spjd return (0); 5612219089Spjd } 5613219089Spjd 5614219089Spjd if (zfs_get_holds(zhp, &nvl) != 0) 5615219089Spjd return (-1); 5616219089Spjd 5617219089Spjd if (znamelen > cbp->cb_max_namelen) 5618219089Spjd cbp->cb_max_namelen = znamelen; 5619219089Spjd 5620219089Spjd while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 5621219089Spjd const char *tag = nvpair_name(nvp); 5622219089Spjd size_t taglen = strnlen(tag, MAXNAMELEN); 5623219089Spjd if (taglen > cbp->cb_max_taglen) 5624219089Spjd cbp->cb_max_taglen = taglen; 5625219089Spjd } 5626219089Spjd 5627219089Spjd return (nvlist_add_nvlist(top_nvl, zname, nvl)); 5628219089Spjd} 5629219089Spjd 5630219089Spjd/* 5631219089Spjd * zfs holds [-r] <snap> ... 5632219089Spjd * 5633219089Spjd * -r Recursively hold 5634219089Spjd */ 5635219089Spjdstatic int 5636219089Spjdzfs_do_holds(int argc, char **argv) 5637219089Spjd{ 5638219089Spjd int errors = 0; 5639219089Spjd int c; 5640219089Spjd int i; 5641219089Spjd boolean_t scripted = B_FALSE; 5642219089Spjd boolean_t recursive = B_FALSE; 5643219089Spjd const char *opts = "rH"; 5644219089Spjd nvlist_t *nvl; 5645219089Spjd 5646219089Spjd int types = ZFS_TYPE_SNAPSHOT; 5647219089Spjd holds_cbdata_t cb = { 0 }; 5648219089Spjd 5649219089Spjd int limit = 0; 5650231144Smm int ret = 0; 5651219089Spjd int flags = 0; 5652219089Spjd 5653219089Spjd /* check options */ 5654219089Spjd while ((c = getopt(argc, argv, opts)) != -1) { 5655219089Spjd switch (c) { 5656219089Spjd case 'r': 5657219089Spjd recursive = B_TRUE; 5658219089Spjd break; 5659219089Spjd case 'H': 5660219089Spjd scripted = B_TRUE; 5661219089Spjd break; 5662219089Spjd case '?': 5663219089Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 5664219089Spjd optopt); 5665219089Spjd usage(B_FALSE); 5666219089Spjd } 5667219089Spjd } 5668219089Spjd 5669219089Spjd if (recursive) { 5670219089Spjd types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME; 5671219089Spjd flags |= ZFS_ITER_RECURSE; 5672219089Spjd } 5673219089Spjd 5674219089Spjd argc -= optind; 5675219089Spjd argv += optind; 5676219089Spjd 5677219089Spjd /* check number of arguments */ 5678219089Spjd if (argc < 1) 5679219089Spjd usage(B_FALSE); 5680219089Spjd 5681219089Spjd if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 5682219089Spjd nomem(); 5683219089Spjd 5684219089Spjd for (i = 0; i < argc; ++i) { 5685219089Spjd char *snapshot = argv[i]; 5686219089Spjd const char *delim; 5687219089Spjd const char *snapname; 5688219089Spjd 5689219089Spjd delim = strchr(snapshot, '@'); 5690219089Spjd if (delim == NULL) { 5691219089Spjd (void) fprintf(stderr, 5692219089Spjd gettext("'%s' is not a snapshot\n"), snapshot); 5693219089Spjd ++errors; 5694219089Spjd continue; 5695219089Spjd } 5696219089Spjd snapname = delim + 1; 5697219089Spjd if (recursive) 5698219089Spjd snapshot[delim - snapshot] = '\0'; 5699219089Spjd 5700219089Spjd cb.cb_recursive = recursive; 5701219089Spjd cb.cb_snapname = snapname; 5702219089Spjd cb.cb_nvlp = &nvl; 5703219089Spjd 5704219089Spjd /* 5705219089Spjd * 1. collect holds data, set format options 5706219089Spjd */ 5707219089Spjd ret = zfs_for_each(argc, argv, flags, types, NULL, NULL, limit, 5708219089Spjd holds_callback, &cb); 5709219089Spjd if (ret != 0) 5710219089Spjd ++errors; 5711219089Spjd } 5712219089Spjd 5713219089Spjd /* 5714219089Spjd * 2. print holds data 5715219089Spjd */ 5716219089Spjd print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl); 5717219089Spjd 5718219089Spjd if (nvlist_empty(nvl)) 5719219089Spjd (void) printf(gettext("no datasets available\n")); 5720219089Spjd 5721219089Spjd nvlist_free(nvl); 5722219089Spjd 5723219089Spjd return (0 != errors); 5724219089Spjd} 5725219089Spjd 5726185029Spjd#define CHECK_SPINNER 30 5727185029Spjd#define SPINNER_TIME 3 /* seconds */ 5728185029Spjd#define MOUNT_TIME 5 /* seconds */ 5729185029Spjd 5730168404Spjdstatic int 5731168404Spjdget_one_dataset(zfs_handle_t *zhp, void *data) 5732168404Spjd{ 5733219089Spjd static char *spin[] = { "-", "\\", "|", "/" }; 5734185029Spjd static int spinval = 0; 5735185029Spjd static int spincheck = 0; 5736185029Spjd static time_t last_spin_time = (time_t)0; 5737219089Spjd get_all_cb_t *cbp = data; 5738168404Spjd zfs_type_t type = zfs_get_type(zhp); 5739168404Spjd 5740185029Spjd if (cbp->cb_verbose) { 5741185029Spjd if (--spincheck < 0) { 5742185029Spjd time_t now = time(NULL); 5743185029Spjd if (last_spin_time + SPINNER_TIME < now) { 5744219089Spjd update_progress(spin[spinval++ % 4]); 5745185029Spjd last_spin_time = now; 5746185029Spjd } 5747185029Spjd spincheck = CHECK_SPINNER; 5748185029Spjd } 5749185029Spjd } 5750185029Spjd 5751168404Spjd /* 5752168404Spjd * Interate over any nested datasets. 5753168404Spjd */ 5754219089Spjd if (zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) { 5755168404Spjd zfs_close(zhp); 5756168404Spjd return (1); 5757168404Spjd } 5758168404Spjd 5759168404Spjd /* 5760168404Spjd * Skip any datasets whose type does not match. 5761168404Spjd */ 5762219089Spjd if ((type & ZFS_TYPE_FILESYSTEM) == 0) { 5763168404Spjd zfs_close(zhp); 5764168404Spjd return (0); 5765168404Spjd } 5766219089Spjd libzfs_add_handle(cbp, zhp); 5767219089Spjd assert(cbp->cb_used <= cbp->cb_alloc); 5768168404Spjd 5769168404Spjd return (0); 5770168404Spjd} 5771168404Spjd 5772168404Spjdstatic void 5773219089Spjdget_all_datasets(zfs_handle_t ***dslist, size_t *count, boolean_t verbose) 5774168404Spjd{ 5775219089Spjd get_all_cb_t cb = { 0 }; 5776185029Spjd cb.cb_verbose = verbose; 5777219089Spjd cb.cb_getone = get_one_dataset; 5778168404Spjd 5779219089Spjd if (verbose) 5780219089Spjd set_progress_header(gettext("Reading ZFS config")); 5781168404Spjd (void) zfs_iter_root(g_zfs, get_one_dataset, &cb); 5782168404Spjd 5783168404Spjd *dslist = cb.cb_handles; 5784168404Spjd *count = cb.cb_used; 5785185029Spjd 5786219089Spjd if (verbose) 5787219089Spjd finish_progress(gettext("done.")); 5788168404Spjd} 5789168404Spjd 5790168404Spjd/* 5791168404Spjd * Generic callback for sharing or mounting filesystems. Because the code is so 5792168404Spjd * similar, we have a common function with an extra parameter to determine which 5793168404Spjd * mode we are using. 5794168404Spjd */ 5795168404Spjd#define OP_SHARE 0x1 5796168404Spjd#define OP_MOUNT 0x2 5797168404Spjd 5798168404Spjd/* 5799168404Spjd * Share or mount a dataset. 5800168404Spjd */ 5801168404Spjdstatic int 5802185029Spjdshare_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol, 5803185029Spjd boolean_t explicit, const char *options) 5804168404Spjd{ 5805168404Spjd char mountpoint[ZFS_MAXPROPLEN]; 5806168404Spjd char shareopts[ZFS_MAXPROPLEN]; 5807185029Spjd char smbshareopts[ZFS_MAXPROPLEN]; 5808168404Spjd const char *cmdname = op == OP_SHARE ? "share" : "mount"; 5809168404Spjd struct mnttab mnt; 5810168404Spjd uint64_t zoned, canmount; 5811185029Spjd boolean_t shared_nfs, shared_smb; 5812168404Spjd 5813219089Spjd assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM); 5814168404Spjd 5815219089Spjd /* 5816219089Spjd * Check to make sure we can mount/share this dataset. If we 5817219089Spjd * are in the global zone and the filesystem is exported to a 5818219089Spjd * local zone, or if we are in a local zone and the 5819219089Spjd * filesystem is not exported, then it is an error. 5820219089Spjd */ 5821219089Spjd zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 5822168404Spjd 5823219089Spjd if (zoned && getzoneid() == GLOBAL_ZONEID) { 5824219089Spjd if (!explicit) 5825219089Spjd return (0); 5826168404Spjd 5827219089Spjd (void) fprintf(stderr, gettext("cannot %s '%s': " 5828219089Spjd "dataset is exported to a local zone\n"), cmdname, 5829219089Spjd zfs_get_name(zhp)); 5830219089Spjd return (1); 5831168404Spjd 5832219089Spjd } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 5833219089Spjd if (!explicit) 5834219089Spjd return (0); 5835168404Spjd 5836219089Spjd (void) fprintf(stderr, gettext("cannot %s '%s': " 5837219089Spjd "permission denied\n"), cmdname, 5838219089Spjd zfs_get_name(zhp)); 5839219089Spjd return (1); 5840219089Spjd } 5841168404Spjd 5842219089Spjd /* 5843219089Spjd * Ignore any filesystems which don't apply to us. This 5844219089Spjd * includes those with a legacy mountpoint, or those with 5845219089Spjd * legacy share options. 5846219089Spjd */ 5847219089Spjd verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 5848219089Spjd sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); 5849219089Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 5850219089Spjd sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 5851219089Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts, 5852219089Spjd sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0); 5853168404Spjd 5854219089Spjd if (op == OP_SHARE && strcmp(shareopts, "off") == 0 && 5855219089Spjd strcmp(smbshareopts, "off") == 0) { 5856219089Spjd if (!explicit) 5857219089Spjd return (0); 5858168404Spjd 5859219089Spjd (void) fprintf(stderr, gettext("cannot share '%s': " 5860219089Spjd "legacy share\n"), zfs_get_name(zhp)); 5861252732Smm (void) fprintf(stderr, gettext("to " 5862252732Smm "share this filesystem set " 5863219089Spjd "sharenfs property on\n")); 5864219089Spjd return (1); 5865219089Spjd } 5866168404Spjd 5867219089Spjd /* 5868219089Spjd * We cannot share or mount legacy filesystems. If the 5869219089Spjd * shareopts is non-legacy but the mountpoint is legacy, we 5870219089Spjd * treat it as a legacy share. 5871219089Spjd */ 5872219089Spjd if (strcmp(mountpoint, "legacy") == 0) { 5873219089Spjd if (!explicit) 5874219089Spjd return (0); 5875168404Spjd 5876219089Spjd (void) fprintf(stderr, gettext("cannot %s '%s': " 5877219089Spjd "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 5878252732Smm (void) fprintf(stderr, gettext("use %s(8) to " 5879219089Spjd "%s this filesystem\n"), cmdname, cmdname); 5880219089Spjd return (1); 5881219089Spjd } 5882168404Spjd 5883219089Spjd if (strcmp(mountpoint, "none") == 0) { 5884219089Spjd if (!explicit) 5885219089Spjd return (0); 5886168404Spjd 5887219089Spjd (void) fprintf(stderr, gettext("cannot %s '%s': no " 5888219089Spjd "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 5889219089Spjd return (1); 5890219089Spjd } 5891168404Spjd 5892219089Spjd /* 5893219089Spjd * canmount explicit outcome 5894219089Spjd * on no pass through 5895219089Spjd * on yes pass through 5896219089Spjd * off no return 0 5897219089Spjd * off yes display error, return 1 5898219089Spjd * noauto no return 0 5899219089Spjd * noauto yes pass through 5900219089Spjd */ 5901219089Spjd canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); 5902219089Spjd if (canmount == ZFS_CANMOUNT_OFF) { 5903219089Spjd if (!explicit) 5904219089Spjd return (0); 5905219089Spjd 5906219089Spjd (void) fprintf(stderr, gettext("cannot %s '%s': " 5907219089Spjd "'canmount' property is set to 'off'\n"), cmdname, 5908219089Spjd zfs_get_name(zhp)); 5909219089Spjd return (1); 5910219089Spjd } else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) { 5911219089Spjd return (0); 5912219089Spjd } 5913219089Spjd 5914219089Spjd /* 5915290756Smav * If this filesystem is inconsistent and has a receive resume 5916290756Smav * token, we can not mount it. 5917290756Smav */ 5918290756Smav if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) && 5919290756Smav zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN, 5920290756Smav NULL, 0, NULL, NULL, 0, B_TRUE) == 0) { 5921290756Smav if (!explicit) 5922290756Smav return (0); 5923290756Smav 5924290756Smav (void) fprintf(stderr, gettext("cannot %s '%s': " 5925290756Smav "Contains partially-completed state from " 5926290756Smav "\"zfs receive -r\", which can be resumed with " 5927290756Smav "\"zfs send -t\"\n"), 5928290756Smav cmdname, zfs_get_name(zhp)); 5929290756Smav return (1); 5930290756Smav } 5931290756Smav 5932290756Smav /* 5933219089Spjd * At this point, we have verified that the mountpoint and/or 5934219089Spjd * shareopts are appropriate for auto management. If the 5935219089Spjd * filesystem is already mounted or shared, return (failing 5936219089Spjd * for explicit requests); otherwise mount or share the 5937219089Spjd * filesystem. 5938219089Spjd */ 5939219089Spjd switch (op) { 5940219089Spjd case OP_SHARE: 5941219089Spjd 5942219089Spjd shared_nfs = zfs_is_shared_nfs(zhp, NULL); 5943219089Spjd shared_smb = zfs_is_shared_smb(zhp, NULL); 5944219089Spjd 5945219089Spjd if (shared_nfs && shared_smb || 5946219089Spjd (shared_nfs && strcmp(shareopts, "on") == 0 && 5947219089Spjd strcmp(smbshareopts, "off") == 0) || 5948219089Spjd (shared_smb && strcmp(smbshareopts, "on") == 0 && 5949219089Spjd strcmp(shareopts, "off") == 0)) { 5950168404Spjd if (!explicit) 5951168404Spjd return (0); 5952168404Spjd 5953219089Spjd (void) fprintf(stderr, gettext("cannot share " 5954219089Spjd "'%s': filesystem already shared\n"), 5955168404Spjd zfs_get_name(zhp)); 5956168404Spjd return (1); 5957168404Spjd } 5958168404Spjd 5959219089Spjd if (!zfs_is_mounted(zhp, NULL) && 5960219089Spjd zfs_mount(zhp, NULL, 0) != 0) 5961219089Spjd return (1); 5962185029Spjd 5963219089Spjd if (protocol == NULL) { 5964219089Spjd if (zfs_shareall(zhp) != 0) 5965168404Spjd return (1); 5966219089Spjd } else if (strcmp(protocol, "nfs") == 0) { 5967219089Spjd if (zfs_share_nfs(zhp)) 5968168404Spjd return (1); 5969219089Spjd } else if (strcmp(protocol, "smb") == 0) { 5970219089Spjd if (zfs_share_smb(zhp)) 5971168404Spjd return (1); 5972219089Spjd } else { 5973219089Spjd (void) fprintf(stderr, gettext("cannot share " 5974219089Spjd "'%s': invalid share type '%s' " 5975219089Spjd "specified\n"), 5976219089Spjd zfs_get_name(zhp), protocol); 5977219089Spjd return (1); 5978168404Spjd } 5979168404Spjd 5980219089Spjd break; 5981168404Spjd 5982219089Spjd case OP_MOUNT: 5983219089Spjd if (options == NULL) 5984219089Spjd mnt.mnt_mntopts = ""; 5985219089Spjd else 5986219089Spjd mnt.mnt_mntopts = (char *)options; 5987168404Spjd 5988219089Spjd if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 5989219089Spjd zfs_is_mounted(zhp, NULL)) { 5990168404Spjd if (!explicit) 5991168404Spjd return (0); 5992168404Spjd 5993219089Spjd (void) fprintf(stderr, gettext("cannot mount " 5994219089Spjd "'%s': filesystem already mounted\n"), 5995168404Spjd zfs_get_name(zhp)); 5996168404Spjd return (1); 5997168404Spjd } 5998168404Spjd 5999219089Spjd if (zfs_mount(zhp, options, flags) != 0) 6000168404Spjd return (1); 6001219089Spjd break; 6002168404Spjd } 6003168404Spjd 6004168404Spjd return (0); 6005168404Spjd} 6006168404Spjd 6007185029Spjd/* 6008185029Spjd * Reports progress in the form "(current/total)". Not thread-safe. 6009185029Spjd */ 6010185029Spjdstatic void 6011185029Spjdreport_mount_progress(int current, int total) 6012185029Spjd{ 6013219089Spjd static time_t last_progress_time = 0; 6014185029Spjd time_t now = time(NULL); 6015219089Spjd char info[32]; 6016185029Spjd 6017185029Spjd /* report 1..n instead of 0..n-1 */ 6018185029Spjd ++current; 6019185029Spjd 6020185029Spjd /* display header if we're here for the first time */ 6021185029Spjd if (current == 1) { 6022219089Spjd set_progress_header(gettext("Mounting ZFS filesystems")); 6023185029Spjd } else if (current != total && last_progress_time + MOUNT_TIME >= now) { 6024185029Spjd /* too soon to report again */ 6025185029Spjd return; 6026185029Spjd } 6027185029Spjd 6028185029Spjd last_progress_time = now; 6029185029Spjd 6030219089Spjd (void) sprintf(info, "(%d/%d)", current, total); 6031185029Spjd 6032219089Spjd if (current == total) 6033219089Spjd finish_progress(info); 6034219089Spjd else 6035219089Spjd update_progress(info); 6036185029Spjd} 6037185029Spjd 6038185029Spjdstatic void 6039185029Spjdappend_options(char *mntopts, char *newopts) 6040185029Spjd{ 6041185029Spjd int len = strlen(mntopts); 6042185029Spjd 6043185029Spjd /* original length plus new string to append plus 1 for the comma */ 6044185029Spjd if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) { 6045185029Spjd (void) fprintf(stderr, gettext("the opts argument for " 6046185029Spjd "'%c' option is too long (more than %d chars)\n"), 6047185029Spjd "-o", MNT_LINE_MAX); 6048185029Spjd usage(B_FALSE); 6049185029Spjd } 6050185029Spjd 6051185029Spjd if (*mntopts) 6052185029Spjd mntopts[len++] = ','; 6053185029Spjd 6054185029Spjd (void) strcpy(&mntopts[len], newopts); 6055185029Spjd} 6056185029Spjd 6057168404Spjdstatic int 6058168404Spjdshare_mount(int op, int argc, char **argv) 6059168404Spjd{ 6060168404Spjd int do_all = 0; 6061185029Spjd boolean_t verbose = B_FALSE; 6062168404Spjd int c, ret = 0; 6063185029Spjd char *options = NULL; 6064219089Spjd int flags = 0; 6065168404Spjd 6066168404Spjd /* check options */ 6067185029Spjd while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:O" : "a")) 6068168404Spjd != -1) { 6069168404Spjd switch (c) { 6070168404Spjd case 'a': 6071168404Spjd do_all = 1; 6072168404Spjd break; 6073185029Spjd case 'v': 6074185029Spjd verbose = B_TRUE; 6075185029Spjd break; 6076168404Spjd case 'o': 6077185029Spjd if (*optarg == '\0') { 6078185029Spjd (void) fprintf(stderr, gettext("empty mount " 6079185029Spjd "options (-o) specified\n")); 6080185029Spjd usage(B_FALSE); 6081185029Spjd } 6082185029Spjd 6083185029Spjd if (options == NULL) 6084185029Spjd options = safe_malloc(MNT_LINE_MAX + 1); 6085185029Spjd 6086185029Spjd /* option validation is done later */ 6087185029Spjd append_options(options, optarg); 6088168404Spjd break; 6089185029Spjd 6090168404Spjd case 'O': 6091168404Spjd warnx("no overlay mounts support on FreeBSD, ignoring"); 6092168404Spjd break; 6093168404Spjd case ':': 6094168404Spjd (void) fprintf(stderr, gettext("missing argument for " 6095168404Spjd "'%c' option\n"), optopt); 6096168404Spjd usage(B_FALSE); 6097168404Spjd break; 6098168404Spjd case '?': 6099168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 6100168404Spjd optopt); 6101168404Spjd usage(B_FALSE); 6102168404Spjd } 6103168404Spjd } 6104168404Spjd 6105168404Spjd argc -= optind; 6106168404Spjd argv += optind; 6107168404Spjd 6108168404Spjd /* check number of arguments */ 6109168404Spjd if (do_all) { 6110168404Spjd zfs_handle_t **dslist = NULL; 6111168404Spjd size_t i, count = 0; 6112185029Spjd char *protocol = NULL; 6113168404Spjd 6114219089Spjd if (op == OP_SHARE && argc > 0) { 6115219089Spjd if (strcmp(argv[0], "nfs") != 0 && 6116219089Spjd strcmp(argv[0], "smb") != 0) { 6117168404Spjd (void) fprintf(stderr, gettext("share type " 6118219089Spjd "must be 'nfs' or 'smb'\n")); 6119168404Spjd usage(B_FALSE); 6120168404Spjd } 6121185029Spjd protocol = argv[0]; 6122168404Spjd argc--; 6123168404Spjd argv++; 6124168404Spjd } 6125168404Spjd 6126168404Spjd if (argc != 0) { 6127168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 6128168404Spjd usage(B_FALSE); 6129168404Spjd } 6130168404Spjd 6131219089Spjd start_progress_timer(); 6132219089Spjd get_all_datasets(&dslist, &count, verbose); 6133168404Spjd 6134168404Spjd if (count == 0) 6135168404Spjd return (0); 6136168404Spjd 6137219089Spjd qsort(dslist, count, sizeof (void *), libzfs_dataset_cmp); 6138168404Spjd 6139168404Spjd for (i = 0; i < count; i++) { 6140185029Spjd if (verbose) 6141185029Spjd report_mount_progress(i, count); 6142185029Spjd 6143185029Spjd if (share_mount_one(dslist[i], op, flags, protocol, 6144185029Spjd B_FALSE, options) != 0) 6145168404Spjd ret = 1; 6146168404Spjd zfs_close(dslist[i]); 6147168404Spjd } 6148168404Spjd 6149168404Spjd free(dslist); 6150168404Spjd } else if (argc == 0) { 6151219089Spjd struct mnttab entry; 6152168404Spjd 6153185029Spjd if ((op == OP_SHARE) || (options != NULL)) { 6154168404Spjd (void) fprintf(stderr, gettext("missing filesystem " 6155185029Spjd "argument (specify -a for all)\n")); 6156168404Spjd usage(B_FALSE); 6157168404Spjd } 6158168404Spjd 6159168404Spjd /* 6160168404Spjd * When mount is given no arguments, go through /etc/mnttab and 6161168404Spjd * display any active ZFS mounts. We hide any snapshots, since 6162168404Spjd * they are controlled automatically. 6163168404Spjd */ 6164219089Spjd rewind(mnttab_file); 6165219089Spjd while (getmntent(mnttab_file, &entry) == 0) { 6166219089Spjd if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 6167219089Spjd strchr(entry.mnt_special, '@') != NULL) 6168168404Spjd continue; 6169168404Spjd 6170219089Spjd (void) printf("%-30s %s\n", entry.mnt_special, 6171219089Spjd entry.mnt_mountp); 6172168404Spjd } 6173168404Spjd 6174168404Spjd } else { 6175168404Spjd zfs_handle_t *zhp; 6176168404Spjd 6177168404Spjd if (argc > 1) { 6178168404Spjd (void) fprintf(stderr, 6179168404Spjd gettext("too many arguments\n")); 6180168404Spjd usage(B_FALSE); 6181168404Spjd } 6182168404Spjd 6183219089Spjd if ((zhp = zfs_open(g_zfs, argv[0], 6184219089Spjd ZFS_TYPE_FILESYSTEM)) == NULL) { 6185168404Spjd ret = 1; 6186168404Spjd } else { 6187185029Spjd ret = share_mount_one(zhp, op, flags, NULL, B_TRUE, 6188168404Spjd options); 6189168404Spjd zfs_close(zhp); 6190168404Spjd } 6191168404Spjd } 6192168404Spjd 6193168404Spjd return (ret); 6194168404Spjd} 6195168404Spjd 6196168404Spjd/* 6197219089Spjd * zfs mount -a [nfs] 6198168404Spjd * zfs mount filesystem 6199168404Spjd * 6200168404Spjd * Mount all filesystems, or mount the given filesystem. 6201168404Spjd */ 6202168404Spjdstatic int 6203168404Spjdzfs_do_mount(int argc, char **argv) 6204168404Spjd{ 6205168404Spjd return (share_mount(OP_MOUNT, argc, argv)); 6206168404Spjd} 6207168404Spjd 6208168404Spjd/* 6209219089Spjd * zfs share -a [nfs | smb] 6210168404Spjd * zfs share filesystem 6211168404Spjd * 6212168404Spjd * Share all filesystems, or share the given filesystem. 6213168404Spjd */ 6214168404Spjdstatic int 6215168404Spjdzfs_do_share(int argc, char **argv) 6216168404Spjd{ 6217168404Spjd return (share_mount(OP_SHARE, argc, argv)); 6218168404Spjd} 6219168404Spjd 6220168404Spjdtypedef struct unshare_unmount_node { 6221168404Spjd zfs_handle_t *un_zhp; 6222168404Spjd char *un_mountp; 6223168404Spjd uu_avl_node_t un_avlnode; 6224168404Spjd} unshare_unmount_node_t; 6225168404Spjd 6226168404Spjd/* ARGSUSED */ 6227168404Spjdstatic int 6228168404Spjdunshare_unmount_compare(const void *larg, const void *rarg, void *unused) 6229168404Spjd{ 6230168404Spjd const unshare_unmount_node_t *l = larg; 6231168404Spjd const unshare_unmount_node_t *r = rarg; 6232168404Spjd 6233168404Spjd return (strcmp(l->un_mountp, r->un_mountp)); 6234168404Spjd} 6235168404Spjd 6236168404Spjd/* 6237168404Spjd * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 6238168404Spjd * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 6239168404Spjd * and unmount it appropriately. 6240168404Spjd */ 6241168404Spjdstatic int 6242168404Spjdunshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) 6243168404Spjd{ 6244168404Spjd zfs_handle_t *zhp; 6245231144Smm int ret = 0; 6246185029Spjd struct stat64 statbuf; 6247219089Spjd struct extmnttab entry; 6248168404Spjd const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount"; 6249185029Spjd ino_t path_inode; 6250168404Spjd 6251168404Spjd /* 6252185029Spjd * Search for the path in /etc/mnttab. Rather than looking for the 6253185029Spjd * specific path, which can be fooled by non-standard paths (i.e. ".." 6254185029Spjd * or "//"), we stat() the path and search for the corresponding 6255185029Spjd * (major,minor) device pair. 6256185029Spjd */ 6257185029Spjd if (stat64(path, &statbuf) != 0) { 6258185029Spjd (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 6259185029Spjd cmdname, path, strerror(errno)); 6260185029Spjd return (1); 6261185029Spjd } 6262185029Spjd path_inode = statbuf.st_ino; 6263185029Spjd 6264185029Spjd /* 6265168404Spjd * Search for the given (major,minor) pair in the mount table. 6266168404Spjd */ 6267219089Spjd#ifdef sun 6268168404Spjd rewind(mnttab_file); 6269219089Spjd while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 6270219089Spjd if (entry.mnt_major == major(statbuf.st_dev) && 6271219089Spjd entry.mnt_minor == minor(statbuf.st_dev)) 6272219089Spjd break; 6273219089Spjd } 6274219089Spjd#else 6275219089Spjd { 6276219089Spjd struct statfs sfs; 6277219089Spjd 6278219089Spjd if (statfs(path, &sfs) != 0) { 6279219089Spjd (void) fprintf(stderr, "%s: %s\n", path, 6280219089Spjd strerror(errno)); 6281219089Spjd ret = -1; 6282219089Spjd } 6283219089Spjd statfs2mnttab(&sfs, &entry); 6284219089Spjd } 6285219089Spjd#endif 6286219089Spjd if (ret != 0) { 6287185029Spjd if (op == OP_SHARE) { 6288185029Spjd (void) fprintf(stderr, gettext("cannot %s '%s': not " 6289185029Spjd "currently mounted\n"), cmdname, path); 6290185029Spjd return (1); 6291185029Spjd } 6292185029Spjd (void) fprintf(stderr, gettext("warning: %s not in mnttab\n"), 6293185029Spjd path); 6294185029Spjd if ((ret = umount2(path, flags)) != 0) 6295185029Spjd (void) fprintf(stderr, gettext("%s: %s\n"), path, 6296185029Spjd strerror(errno)); 6297185029Spjd return (ret != 0); 6298168404Spjd } 6299168404Spjd 6300168404Spjd if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 6301168404Spjd (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 6302168404Spjd "filesystem\n"), cmdname, path); 6303168404Spjd return (1); 6304168404Spjd } 6305168404Spjd 6306168404Spjd if ((zhp = zfs_open(g_zfs, entry.mnt_special, 6307168404Spjd ZFS_TYPE_FILESYSTEM)) == NULL) 6308168404Spjd return (1); 6309168404Spjd 6310185029Spjd ret = 1; 6311185029Spjd if (stat64(entry.mnt_mountp, &statbuf) != 0) { 6312185029Spjd (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 6313185029Spjd cmdname, path, strerror(errno)); 6314185029Spjd goto out; 6315185029Spjd } else if (statbuf.st_ino != path_inode) { 6316185029Spjd (void) fprintf(stderr, gettext("cannot " 6317185029Spjd "%s '%s': not a mountpoint\n"), cmdname, path); 6318185029Spjd goto out; 6319185029Spjd } 6320168404Spjd 6321168404Spjd if (op == OP_SHARE) { 6322185029Spjd char nfs_mnt_prop[ZFS_MAXPROPLEN]; 6323185029Spjd char smbshare_prop[ZFS_MAXPROPLEN]; 6324185029Spjd 6325185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, nfs_mnt_prop, 6326185029Spjd sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0); 6327185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshare_prop, 6328185029Spjd sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0); 6329185029Spjd 6330185029Spjd if (strcmp(nfs_mnt_prop, "off") == 0 && 6331185029Spjd strcmp(smbshare_prop, "off") == 0) { 6332168404Spjd (void) fprintf(stderr, gettext("cannot unshare " 6333168404Spjd "'%s': legacy share\n"), path); 6334252732Smm#ifdef illumos 6335168404Spjd (void) fprintf(stderr, gettext("use " 6336168404Spjd "unshare(1M) to unshare this filesystem\n")); 6337252732Smm#endif 6338185029Spjd } else if (!zfs_is_shared(zhp)) { 6339168404Spjd (void) fprintf(stderr, gettext("cannot unshare '%s': " 6340168404Spjd "not currently shared\n"), path); 6341168404Spjd } else { 6342185029Spjd ret = zfs_unshareall_bypath(zhp, path); 6343168404Spjd } 6344168404Spjd } else { 6345185029Spjd char mtpt_prop[ZFS_MAXPROPLEN]; 6346185029Spjd 6347185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop, 6348185029Spjd sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0); 6349185029Spjd 6350168404Spjd if (is_manual) { 6351168404Spjd ret = zfs_unmount(zhp, NULL, flags); 6352185029Spjd } else if (strcmp(mtpt_prop, "legacy") == 0) { 6353168404Spjd (void) fprintf(stderr, gettext("cannot unmount " 6354168404Spjd "'%s': legacy mountpoint\n"), 6355168404Spjd zfs_get_name(zhp)); 6356252732Smm (void) fprintf(stderr, gettext("use umount(8) " 6357168404Spjd "to unmount this filesystem\n")); 6358168404Spjd } else { 6359168404Spjd ret = zfs_unmountall(zhp, flags); 6360168404Spjd } 6361168404Spjd } 6362168404Spjd 6363185029Spjdout: 6364168404Spjd zfs_close(zhp); 6365168404Spjd 6366168404Spjd return (ret != 0); 6367168404Spjd} 6368168404Spjd 6369168404Spjd/* 6370168404Spjd * Generic callback for unsharing or unmounting a filesystem. 6371168404Spjd */ 6372168404Spjdstatic int 6373168404Spjdunshare_unmount(int op, int argc, char **argv) 6374168404Spjd{ 6375168404Spjd int do_all = 0; 6376168404Spjd int flags = 0; 6377168404Spjd int ret = 0; 6378219089Spjd int c; 6379168404Spjd zfs_handle_t *zhp; 6380219089Spjd char nfs_mnt_prop[ZFS_MAXPROPLEN]; 6381185029Spjd char sharesmb[ZFS_MAXPROPLEN]; 6382168404Spjd 6383168404Spjd /* check options */ 6384168404Spjd while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) { 6385168404Spjd switch (c) { 6386168404Spjd case 'a': 6387168404Spjd do_all = 1; 6388168404Spjd break; 6389168404Spjd case 'f': 6390168404Spjd flags = MS_FORCE; 6391168404Spjd break; 6392168404Spjd case '?': 6393168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 6394168404Spjd optopt); 6395168404Spjd usage(B_FALSE); 6396168404Spjd } 6397168404Spjd } 6398168404Spjd 6399168404Spjd argc -= optind; 6400168404Spjd argv += optind; 6401168404Spjd 6402168404Spjd if (do_all) { 6403168404Spjd /* 6404168404Spjd * We could make use of zfs_for_each() to walk all datasets in 6405168404Spjd * the system, but this would be very inefficient, especially 6406168404Spjd * since we would have to linearly search /etc/mnttab for each 6407168404Spjd * one. Instead, do one pass through /etc/mnttab looking for 6408168404Spjd * zfs entries and call zfs_unmount() for each one. 6409168404Spjd * 6410168404Spjd * Things get a little tricky if the administrator has created 6411168404Spjd * mountpoints beneath other ZFS filesystems. In this case, we 6412168404Spjd * have to unmount the deepest filesystems first. To accomplish 6413168404Spjd * this, we place all the mountpoints in an AVL tree sorted by 6414168404Spjd * the special type (dataset name), and walk the result in 6415168404Spjd * reverse to make sure to get any snapshots first. 6416168404Spjd */ 6417219089Spjd struct mnttab entry; 6418168404Spjd uu_avl_pool_t *pool; 6419168404Spjd uu_avl_t *tree; 6420168404Spjd unshare_unmount_node_t *node; 6421168404Spjd uu_avl_index_t idx; 6422168404Spjd uu_avl_walk_t *walk; 6423168404Spjd 6424168404Spjd if (argc != 0) { 6425168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 6426168404Spjd usage(B_FALSE); 6427168404Spjd } 6428168404Spjd 6429219089Spjd if (((pool = uu_avl_pool_create("unmount_pool", 6430168404Spjd sizeof (unshare_unmount_node_t), 6431168404Spjd offsetof(unshare_unmount_node_t, un_avlnode), 6432219089Spjd unshare_unmount_compare, UU_DEFAULT)) == NULL) || 6433219089Spjd ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL)) 6434219089Spjd nomem(); 6435168404Spjd 6436219089Spjd rewind(mnttab_file); 6437219089Spjd while (getmntent(mnttab_file, &entry) == 0) { 6438168404Spjd 6439168404Spjd /* ignore non-ZFS entries */ 6440219089Spjd if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 6441168404Spjd continue; 6442168404Spjd 6443168404Spjd /* ignore snapshots */ 6444219089Spjd if (strchr(entry.mnt_special, '@') != NULL) 6445168404Spjd continue; 6446168404Spjd 6447219089Spjd if ((zhp = zfs_open(g_zfs, entry.mnt_special, 6448168404Spjd ZFS_TYPE_FILESYSTEM)) == NULL) { 6449168404Spjd ret = 1; 6450168404Spjd continue; 6451168404Spjd } 6452168404Spjd 6453185029Spjd switch (op) { 6454185029Spjd case OP_SHARE: 6455185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, 6456219089Spjd nfs_mnt_prop, 6457219089Spjd sizeof (nfs_mnt_prop), 6458185029Spjd NULL, NULL, 0, B_FALSE) == 0); 6459219089Spjd if (strcmp(nfs_mnt_prop, "off") != 0) 6460185029Spjd break; 6461185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, 6462219089Spjd nfs_mnt_prop, 6463219089Spjd sizeof (nfs_mnt_prop), 6464185029Spjd NULL, NULL, 0, B_FALSE) == 0); 6465219089Spjd if (strcmp(nfs_mnt_prop, "off") == 0) 6466185029Spjd continue; 6467185029Spjd break; 6468185029Spjd case OP_MOUNT: 6469185029Spjd /* Ignore legacy mounts */ 6470185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, 6471219089Spjd nfs_mnt_prop, 6472219089Spjd sizeof (nfs_mnt_prop), 6473185029Spjd NULL, NULL, 0, B_FALSE) == 0); 6474219089Spjd if (strcmp(nfs_mnt_prop, "legacy") == 0) 6475185029Spjd continue; 6476185029Spjd /* Ignore canmount=noauto mounts */ 6477185029Spjd if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == 6478185029Spjd ZFS_CANMOUNT_NOAUTO) 6479185029Spjd continue; 6480185029Spjd default: 6481185029Spjd break; 6482168404Spjd } 6483168404Spjd 6484168404Spjd node = safe_malloc(sizeof (unshare_unmount_node_t)); 6485168404Spjd node->un_zhp = zhp; 6486219089Spjd node->un_mountp = safe_strdup(entry.mnt_mountp); 6487168404Spjd 6488168404Spjd uu_avl_node_init(node, &node->un_avlnode, pool); 6489168404Spjd 6490168404Spjd if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 6491168404Spjd uu_avl_insert(tree, node, idx); 6492168404Spjd } else { 6493168404Spjd zfs_close(node->un_zhp); 6494168404Spjd free(node->un_mountp); 6495168404Spjd free(node); 6496168404Spjd } 6497168404Spjd } 6498168404Spjd 6499168404Spjd /* 6500168404Spjd * Walk the AVL tree in reverse, unmounting each filesystem and 6501168404Spjd * removing it from the AVL tree in the process. 6502168404Spjd */ 6503168404Spjd if ((walk = uu_avl_walk_start(tree, 6504219089Spjd UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) 6505219089Spjd nomem(); 6506168404Spjd 6507168404Spjd while ((node = uu_avl_walk_next(walk)) != NULL) { 6508168404Spjd uu_avl_remove(tree, node); 6509168404Spjd 6510168404Spjd switch (op) { 6511168404Spjd case OP_SHARE: 6512185029Spjd if (zfs_unshareall_bypath(node->un_zhp, 6513168404Spjd node->un_mountp) != 0) 6514168404Spjd ret = 1; 6515168404Spjd break; 6516168404Spjd 6517168404Spjd case OP_MOUNT: 6518168404Spjd if (zfs_unmount(node->un_zhp, 6519168404Spjd node->un_mountp, flags) != 0) 6520168404Spjd ret = 1; 6521168404Spjd break; 6522168404Spjd } 6523168404Spjd 6524168404Spjd zfs_close(node->un_zhp); 6525168404Spjd free(node->un_mountp); 6526168404Spjd free(node); 6527168404Spjd } 6528168404Spjd 6529168404Spjd uu_avl_walk_end(walk); 6530168404Spjd uu_avl_destroy(tree); 6531168404Spjd uu_avl_pool_destroy(pool); 6532168404Spjd 6533168404Spjd } else { 6534168404Spjd if (argc != 1) { 6535168404Spjd if (argc == 0) 6536168404Spjd (void) fprintf(stderr, 6537168404Spjd gettext("missing filesystem argument\n")); 6538168404Spjd else 6539168404Spjd (void) fprintf(stderr, 6540168404Spjd gettext("too many arguments\n")); 6541168404Spjd usage(B_FALSE); 6542168404Spjd } 6543168404Spjd 6544168404Spjd /* 6545168404Spjd * We have an argument, but it may be a full path or a ZFS 6546168404Spjd * filesystem. Pass full paths off to unmount_path() (shared by 6547168404Spjd * manual_unmount), otherwise open the filesystem and pass to 6548168404Spjd * zfs_unmount(). 6549168404Spjd */ 6550168404Spjd if (argv[0][0] == '/') 6551168404Spjd return (unshare_unmount_path(op, argv[0], 6552168404Spjd flags, B_FALSE)); 6553168404Spjd 6554219089Spjd if ((zhp = zfs_open(g_zfs, argv[0], 6555219089Spjd ZFS_TYPE_FILESYSTEM)) == NULL) 6556168404Spjd return (1); 6557168404Spjd 6558219089Spjd verify(zfs_prop_get(zhp, op == OP_SHARE ? 6559219089Spjd ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 6560219089Spjd nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL, 6561219089Spjd NULL, 0, B_FALSE) == 0); 6562168404Spjd 6563219089Spjd switch (op) { 6564219089Spjd case OP_SHARE: 6565219089Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, 6566219089Spjd nfs_mnt_prop, 6567219089Spjd sizeof (nfs_mnt_prop), 6568185029Spjd NULL, NULL, 0, B_FALSE) == 0); 6569219089Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, 6570219089Spjd sharesmb, sizeof (sharesmb), NULL, NULL, 6571219089Spjd 0, B_FALSE) == 0); 6572168404Spjd 6573219089Spjd if (strcmp(nfs_mnt_prop, "off") == 0 && 6574219089Spjd strcmp(sharesmb, "off") == 0) { 6575219089Spjd (void) fprintf(stderr, gettext("cannot " 6576219089Spjd "unshare '%s': legacy share\n"), 6577168404Spjd zfs_get_name(zhp)); 6578252732Smm#ifdef illumos 6579219089Spjd (void) fprintf(stderr, gettext("use " 6580219089Spjd "unshare(1M) to unshare this " 6581219089Spjd "filesystem\n")); 6582252732Smm#endif 6583168404Spjd ret = 1; 6584219089Spjd } else if (!zfs_is_shared(zhp)) { 6585168404Spjd (void) fprintf(stderr, gettext("cannot " 6586219089Spjd "unshare '%s': not currently " 6587219089Spjd "shared\n"), zfs_get_name(zhp)); 6588219089Spjd ret = 1; 6589219089Spjd } else if (zfs_unshareall(zhp) != 0) { 6590219089Spjd ret = 1; 6591219089Spjd } 6592219089Spjd break; 6593219089Spjd 6594219089Spjd case OP_MOUNT: 6595219089Spjd if (strcmp(nfs_mnt_prop, "legacy") == 0) { 6596219089Spjd (void) fprintf(stderr, gettext("cannot " 6597219089Spjd "unmount '%s': legacy " 6598219089Spjd "mountpoint\n"), zfs_get_name(zhp)); 6599219089Spjd (void) fprintf(stderr, gettext("use " 6600252732Smm "umount(8) to unmount this " 6601219089Spjd "filesystem\n")); 6602219089Spjd ret = 1; 6603219089Spjd } else if (!zfs_is_mounted(zhp, NULL)) { 6604219089Spjd (void) fprintf(stderr, gettext("cannot " 6605219089Spjd "unmount '%s': not currently " 6606219089Spjd "mounted\n"), 6607168404Spjd zfs_get_name(zhp)); 6608168404Spjd ret = 1; 6609219089Spjd } else if (zfs_unmountall(zhp, flags) != 0) { 6610168404Spjd ret = 1; 6611168404Spjd } 6612219089Spjd break; 6613168404Spjd } 6614168404Spjd 6615168404Spjd zfs_close(zhp); 6616168404Spjd } 6617168404Spjd 6618168404Spjd return (ret); 6619168404Spjd} 6620168404Spjd 6621168404Spjd/* 6622168404Spjd * zfs unmount -a 6623168404Spjd * zfs unmount filesystem 6624168404Spjd * 6625168404Spjd * Unmount all filesystems, or a specific ZFS filesystem. 6626168404Spjd */ 6627168404Spjdstatic int 6628168404Spjdzfs_do_unmount(int argc, char **argv) 6629168404Spjd{ 6630168404Spjd return (unshare_unmount(OP_MOUNT, argc, argv)); 6631168404Spjd} 6632168404Spjd 6633168404Spjd/* 6634168404Spjd * zfs unshare -a 6635168404Spjd * zfs unshare filesystem 6636168404Spjd * 6637168404Spjd * Unshare all filesystems, or a specific ZFS filesystem. 6638168404Spjd */ 6639168404Spjdstatic int 6640168404Spjdzfs_do_unshare(int argc, char **argv) 6641168404Spjd{ 6642168404Spjd return (unshare_unmount(OP_SHARE, argc, argv)); 6643168404Spjd} 6644168404Spjd 6645168404Spjd/* 6646168404Spjd * Attach/detach the given dataset to/from the given jail 6647168404Spjd */ 6648168404Spjd/* ARGSUSED */ 6649168404Spjdstatic int 6650168404Spjddo_jail(int argc, char **argv, int attach) 6651168404Spjd{ 6652168404Spjd zfs_handle_t *zhp; 6653168404Spjd int jailid, ret; 6654168404Spjd 6655168404Spjd /* check number of arguments */ 6656168404Spjd if (argc < 3) { 6657168404Spjd (void) fprintf(stderr, gettext("missing argument(s)\n")); 6658168404Spjd usage(B_FALSE); 6659168404Spjd } 6660168404Spjd if (argc > 3) { 6661168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 6662168404Spjd usage(B_FALSE); 6663168404Spjd } 6664168404Spjd 6665240696Sbapt jailid = jail_getid(argv[1]); 6666240696Sbapt if (jailid < 0) { 6667240696Sbapt (void) fprintf(stderr, gettext("invalid jail id or name\n")); 6668168404Spjd usage(B_FALSE); 6669168404Spjd } 6670168404Spjd 6671168404Spjd zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM); 6672168404Spjd if (zhp == NULL) 6673168404Spjd return (1); 6674168404Spjd 6675168404Spjd ret = (zfs_jail(zhp, jailid, attach) != 0); 6676168404Spjd 6677168404Spjd zfs_close(zhp); 6678168404Spjd return (ret); 6679168404Spjd} 6680168404Spjd 6681168404Spjd/* 6682168404Spjd * zfs jail jailid filesystem 6683168404Spjd * 6684168404Spjd * Attach the given dataset to the given jail 6685168404Spjd */ 6686168404Spjd/* ARGSUSED */ 6687168404Spjdstatic int 6688168404Spjdzfs_do_jail(int argc, char **argv) 6689168404Spjd{ 6690168404Spjd 6691168404Spjd return (do_jail(argc, argv, 1)); 6692168404Spjd} 6693168404Spjd 6694168404Spjd/* 6695168404Spjd * zfs unjail jailid filesystem 6696168404Spjd * 6697168404Spjd * Detach the given dataset from the given jail 6698168404Spjd */ 6699168404Spjd/* ARGSUSED */ 6700168404Spjdstatic int 6701168404Spjdzfs_do_unjail(int argc, char **argv) 6702168404Spjd{ 6703168404Spjd 6704168404Spjd return (do_jail(argc, argv, 0)); 6705168404Spjd} 6706168404Spjd 6707168404Spjd/* 6708168404Spjd * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 6709168404Spjd * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 6710168404Spjd */ 6711168404Spjdstatic int 6712168404Spjdmanual_mount(int argc, char **argv) 6713168404Spjd{ 6714168404Spjd zfs_handle_t *zhp; 6715168404Spjd char mountpoint[ZFS_MAXPROPLEN]; 6716168404Spjd char mntopts[MNT_LINE_MAX] = { '\0' }; 6717231144Smm int ret = 0; 6718168404Spjd int c; 6719168404Spjd int flags = 0; 6720168404Spjd char *dataset, *path; 6721168404Spjd 6722168404Spjd /* check options */ 6723168404Spjd while ((c = getopt(argc, argv, ":mo:O")) != -1) { 6724168404Spjd switch (c) { 6725168404Spjd case 'o': 6726168404Spjd (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 6727168404Spjd break; 6728168404Spjd case 'O': 6729168404Spjd flags |= MS_OVERLAY; 6730168404Spjd break; 6731168404Spjd case 'm': 6732168404Spjd flags |= MS_NOMNTTAB; 6733168404Spjd break; 6734168404Spjd case ':': 6735168404Spjd (void) fprintf(stderr, gettext("missing argument for " 6736168404Spjd "'%c' option\n"), optopt); 6737168404Spjd usage(B_FALSE); 6738168404Spjd break; 6739168404Spjd case '?': 6740168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 6741168404Spjd optopt); 6742168404Spjd (void) fprintf(stderr, gettext("usage: mount [-o opts] " 6743168404Spjd "<path>\n")); 6744168404Spjd return (2); 6745168404Spjd } 6746168404Spjd } 6747168404Spjd 6748168404Spjd argc -= optind; 6749168404Spjd argv += optind; 6750168404Spjd 6751168404Spjd /* check that we only have two arguments */ 6752168404Spjd if (argc != 2) { 6753168404Spjd if (argc == 0) 6754168404Spjd (void) fprintf(stderr, gettext("missing dataset " 6755168404Spjd "argument\n")); 6756168404Spjd else if (argc == 1) 6757168404Spjd (void) fprintf(stderr, 6758168404Spjd gettext("missing mountpoint argument\n")); 6759168404Spjd else 6760168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 6761168404Spjd (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 6762168404Spjd return (2); 6763168404Spjd } 6764168404Spjd 6765168404Spjd dataset = argv[0]; 6766168404Spjd path = argv[1]; 6767168404Spjd 6768168404Spjd /* try to open the dataset */ 6769168404Spjd if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 6770168404Spjd return (1); 6771168404Spjd 6772168404Spjd (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 6773168404Spjd sizeof (mountpoint), NULL, NULL, 0, B_FALSE); 6774168404Spjd 6775168404Spjd /* check for legacy mountpoint and complain appropriately */ 6776168404Spjd ret = 0; 6777168404Spjd if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 6778168404Spjd if (zmount(dataset, path, flags, MNTTYPE_ZFS, 6779168404Spjd NULL, 0, mntopts, sizeof (mntopts)) != 0) { 6780168404Spjd (void) fprintf(stderr, gettext("mount failed: %s\n"), 6781168404Spjd strerror(errno)); 6782168404Spjd ret = 1; 6783168404Spjd } 6784168404Spjd } else { 6785168404Spjd (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 6786252732Smm "mounted using 'mount -t zfs'\n"), dataset); 6787168404Spjd (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 6788168404Spjd "instead.\n"), path); 6789252732Smm (void) fprintf(stderr, gettext("If you must use 'mount -t zfs' " 6790252732Smm "or /etc/fstab, use 'zfs set mountpoint=legacy'.\n")); 6791252732Smm (void) fprintf(stderr, gettext("See zfs(8) for more " 6792168404Spjd "information.\n")); 6793168404Spjd ret = 1; 6794168404Spjd } 6795168404Spjd 6796168404Spjd return (ret); 6797168404Spjd} 6798168404Spjd 6799168404Spjd/* 6800168404Spjd * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 6801168404Spjd * unmounts of non-legacy filesystems, as this is the dominant administrative 6802168404Spjd * interface. 6803168404Spjd */ 6804168404Spjdstatic int 6805168404Spjdmanual_unmount(int argc, char **argv) 6806168404Spjd{ 6807168404Spjd int flags = 0; 6808168404Spjd int c; 6809168404Spjd 6810168404Spjd /* check options */ 6811168404Spjd while ((c = getopt(argc, argv, "f")) != -1) { 6812168404Spjd switch (c) { 6813168404Spjd case 'f': 6814168404Spjd flags = MS_FORCE; 6815168404Spjd break; 6816168404Spjd case '?': 6817168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 6818168404Spjd optopt); 6819168404Spjd (void) fprintf(stderr, gettext("usage: unmount [-f] " 6820168404Spjd "<path>\n")); 6821168404Spjd return (2); 6822168404Spjd } 6823168404Spjd } 6824168404Spjd 6825168404Spjd argc -= optind; 6826168404Spjd argv += optind; 6827168404Spjd 6828168404Spjd /* check arguments */ 6829168404Spjd if (argc != 1) { 6830168404Spjd if (argc == 0) 6831168404Spjd (void) fprintf(stderr, gettext("missing path " 6832168404Spjd "argument\n")); 6833168404Spjd else 6834168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 6835168404Spjd (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 6836168404Spjd return (2); 6837168404Spjd } 6838168404Spjd 6839168404Spjd return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE)); 6840168404Spjd} 6841168404Spjd 6842168404Spjdstatic int 6843185029Spjdfind_command_idx(char *command, int *idx) 6844185029Spjd{ 6845185029Spjd int i; 6846185029Spjd 6847185029Spjd for (i = 0; i < NCOMMAND; i++) { 6848185029Spjd if (command_table[i].name == NULL) 6849185029Spjd continue; 6850185029Spjd 6851185029Spjd if (strcmp(command, command_table[i].name) == 0) { 6852185029Spjd *idx = i; 6853185029Spjd return (0); 6854185029Spjd } 6855185029Spjd } 6856185029Spjd return (1); 6857185029Spjd} 6858185029Spjd 6859219089Spjdstatic int 6860219089Spjdzfs_do_diff(int argc, char **argv) 6861219089Spjd{ 6862219089Spjd zfs_handle_t *zhp; 6863219089Spjd int flags = 0; 6864219089Spjd char *tosnap = NULL; 6865219089Spjd char *fromsnap = NULL; 6866219089Spjd char *atp, *copy; 6867231144Smm int err = 0; 6868219089Spjd int c; 6869219089Spjd 6870219089Spjd while ((c = getopt(argc, argv, "FHt")) != -1) { 6871219089Spjd switch (c) { 6872219089Spjd case 'F': 6873219089Spjd flags |= ZFS_DIFF_CLASSIFY; 6874219089Spjd break; 6875219089Spjd case 'H': 6876219089Spjd flags |= ZFS_DIFF_PARSEABLE; 6877219089Spjd break; 6878219089Spjd case 't': 6879219089Spjd flags |= ZFS_DIFF_TIMESTAMP; 6880219089Spjd break; 6881219089Spjd default: 6882219089Spjd (void) fprintf(stderr, 6883219089Spjd gettext("invalid option '%c'\n"), optopt); 6884219089Spjd usage(B_FALSE); 6885219089Spjd } 6886219089Spjd } 6887219089Spjd 6888219089Spjd argc -= optind; 6889219089Spjd argv += optind; 6890219089Spjd 6891219089Spjd if (argc < 1) { 6892219089Spjd (void) fprintf(stderr, 6893219089Spjd gettext("must provide at least one snapshot name\n")); 6894219089Spjd usage(B_FALSE); 6895219089Spjd } 6896219089Spjd 6897219089Spjd if (argc > 2) { 6898219089Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 6899219089Spjd usage(B_FALSE); 6900219089Spjd } 6901219089Spjd 6902219089Spjd fromsnap = argv[0]; 6903219089Spjd tosnap = (argc == 2) ? argv[1] : NULL; 6904219089Spjd 6905219089Spjd copy = NULL; 6906219089Spjd if (*fromsnap != '@') 6907219089Spjd copy = strdup(fromsnap); 6908219089Spjd else if (tosnap) 6909219089Spjd copy = strdup(tosnap); 6910219089Spjd if (copy == NULL) 6911219089Spjd usage(B_FALSE); 6912219089Spjd 6913219089Spjd if (atp = strchr(copy, '@')) 6914219089Spjd *atp = '\0'; 6915219089Spjd 6916219089Spjd if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL) 6917219089Spjd return (1); 6918219089Spjd 6919219089Spjd free(copy); 6920219089Spjd 6921219089Spjd /* 6922219089Spjd * Ignore SIGPIPE so that the library can give us 6923219089Spjd * information on any failure 6924219089Spjd */ 6925219089Spjd (void) sigignore(SIGPIPE); 6926219089Spjd 6927219089Spjd err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags); 6928219089Spjd 6929219089Spjd zfs_close(zhp); 6930219089Spjd 6931219089Spjd return (err != 0); 6932219089Spjd} 6933219089Spjd 6934263407Sdelphij/* 6935263407Sdelphij * zfs bookmark <fs@snap> <fs#bmark> 6936263407Sdelphij * 6937263407Sdelphij * Creates a bookmark with the given name from the given snapshot. 6938263407Sdelphij */ 6939263407Sdelphijstatic int 6940263407Sdelphijzfs_do_bookmark(int argc, char **argv) 6941263407Sdelphij{ 6942263407Sdelphij char snapname[ZFS_MAXNAMELEN]; 6943263407Sdelphij zfs_handle_t *zhp; 6944263407Sdelphij nvlist_t *nvl; 6945263407Sdelphij int ret = 0; 6946263407Sdelphij int c; 6947263407Sdelphij 6948263407Sdelphij /* check options */ 6949263407Sdelphij while ((c = getopt(argc, argv, "")) != -1) { 6950263407Sdelphij switch (c) { 6951263407Sdelphij case '?': 6952263407Sdelphij (void) fprintf(stderr, 6953263407Sdelphij gettext("invalid option '%c'\n"), optopt); 6954263407Sdelphij goto usage; 6955263407Sdelphij } 6956263407Sdelphij } 6957263407Sdelphij 6958263407Sdelphij argc -= optind; 6959263407Sdelphij argv += optind; 6960263407Sdelphij 6961263407Sdelphij /* check number of arguments */ 6962263407Sdelphij if (argc < 1) { 6963263407Sdelphij (void) fprintf(stderr, gettext("missing snapshot argument\n")); 6964263407Sdelphij goto usage; 6965263407Sdelphij } 6966263407Sdelphij if (argc < 2) { 6967263407Sdelphij (void) fprintf(stderr, gettext("missing bookmark argument\n")); 6968263407Sdelphij goto usage; 6969263407Sdelphij } 6970263407Sdelphij 6971263407Sdelphij if (strchr(argv[1], '#') == NULL) { 6972263407Sdelphij (void) fprintf(stderr, 6973263407Sdelphij gettext("invalid bookmark name '%s' -- " 6974263407Sdelphij "must contain a '#'\n"), argv[1]); 6975263407Sdelphij goto usage; 6976263407Sdelphij } 6977263407Sdelphij 6978263407Sdelphij if (argv[0][0] == '@') { 6979263407Sdelphij /* 6980263407Sdelphij * Snapshot name begins with @. 6981263407Sdelphij * Default to same fs as bookmark. 6982263407Sdelphij */ 6983263407Sdelphij (void) strncpy(snapname, argv[1], sizeof (snapname)); 6984263407Sdelphij *strchr(snapname, '#') = '\0'; 6985263407Sdelphij (void) strlcat(snapname, argv[0], sizeof (snapname)); 6986263407Sdelphij } else { 6987263407Sdelphij (void) strncpy(snapname, argv[0], sizeof (snapname)); 6988263407Sdelphij } 6989263407Sdelphij zhp = zfs_open(g_zfs, snapname, ZFS_TYPE_SNAPSHOT); 6990263407Sdelphij if (zhp == NULL) 6991263407Sdelphij goto usage; 6992263407Sdelphij zfs_close(zhp); 6993263407Sdelphij 6994263407Sdelphij 6995263407Sdelphij nvl = fnvlist_alloc(); 6996263407Sdelphij fnvlist_add_string(nvl, argv[1], snapname); 6997263407Sdelphij ret = lzc_bookmark(nvl, NULL); 6998263407Sdelphij fnvlist_free(nvl); 6999263407Sdelphij 7000263407Sdelphij if (ret != 0) { 7001263407Sdelphij const char *err_msg; 7002263407Sdelphij char errbuf[1024]; 7003263407Sdelphij 7004263407Sdelphij (void) snprintf(errbuf, sizeof (errbuf), 7005263407Sdelphij dgettext(TEXT_DOMAIN, 7006263407Sdelphij "cannot create bookmark '%s'"), argv[1]); 7007263407Sdelphij 7008263407Sdelphij switch (ret) { 7009263407Sdelphij case EXDEV: 7010263407Sdelphij err_msg = "bookmark is in a different pool"; 7011263407Sdelphij break; 7012263407Sdelphij case EEXIST: 7013263407Sdelphij err_msg = "bookmark exists"; 7014263407Sdelphij break; 7015263407Sdelphij case EINVAL: 7016263407Sdelphij err_msg = "invalid argument"; 7017263407Sdelphij break; 7018263407Sdelphij case ENOTSUP: 7019263407Sdelphij err_msg = "bookmark feature not enabled"; 7020263407Sdelphij break; 7021269006Sdelphij case ENOSPC: 7022269006Sdelphij err_msg = "out of space"; 7023269006Sdelphij break; 7024263407Sdelphij default: 7025263407Sdelphij err_msg = "unknown error"; 7026263407Sdelphij break; 7027263407Sdelphij } 7028263407Sdelphij (void) fprintf(stderr, "%s: %s\n", errbuf, 7029263407Sdelphij dgettext(TEXT_DOMAIN, err_msg)); 7030263407Sdelphij } 7031263407Sdelphij 7032269006Sdelphij return (ret != 0); 7033263407Sdelphij 7034263407Sdelphijusage: 7035263407Sdelphij usage(B_FALSE); 7036263407Sdelphij return (-1); 7037263407Sdelphij} 7038263407Sdelphij 7039168404Spjdint 7040168404Spjdmain(int argc, char **argv) 7041168404Spjd{ 7042231144Smm int ret = 0; 7043168404Spjd int i; 7044168404Spjd char *progname; 7045168404Spjd char *cmdname; 7046168404Spjd 7047168404Spjd (void) setlocale(LC_ALL, ""); 7048168404Spjd (void) textdomain(TEXT_DOMAIN); 7049168404Spjd 7050168404Spjd opterr = 0; 7051168404Spjd 7052168404Spjd if ((g_zfs = libzfs_init()) == NULL) { 7053168404Spjd (void) fprintf(stderr, gettext("internal error: failed to " 7054168404Spjd "initialize ZFS library\n")); 7055168404Spjd return (1); 7056168404Spjd } 7057168404Spjd 7058248571Smm zfs_save_arguments(argc, argv, history_str, sizeof (history_str)); 7059185029Spjd 7060168404Spjd libzfs_print_on_error(g_zfs, B_TRUE); 7061168404Spjd 7062168404Spjd if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 7063168404Spjd (void) fprintf(stderr, gettext("internal error: unable to " 7064168404Spjd "open %s\n"), MNTTAB); 7065168404Spjd return (1); 7066168404Spjd } 7067168404Spjd 7068168404Spjd /* 7069168404Spjd * This command also doubles as the /etc/fs mount and unmount program. 7070168404Spjd * Determine if we should take this behavior based on argv[0]. 7071168404Spjd */ 7072168404Spjd progname = basename(argv[0]); 7073168404Spjd if (strcmp(progname, "mount") == 0) { 7074168404Spjd ret = manual_mount(argc, argv); 7075168404Spjd } else if (strcmp(progname, "umount") == 0) { 7076168404Spjd ret = manual_unmount(argc, argv); 7077168404Spjd } else { 7078168404Spjd /* 7079168404Spjd * Make sure the user has specified some command. 7080168404Spjd */ 7081168404Spjd if (argc < 2) { 7082168404Spjd (void) fprintf(stderr, gettext("missing command\n")); 7083168404Spjd usage(B_FALSE); 7084168404Spjd } 7085168404Spjd 7086168404Spjd cmdname = argv[1]; 7087168404Spjd 7088168404Spjd /* 7089168404Spjd * The 'umount' command is an alias for 'unmount' 7090168404Spjd */ 7091168404Spjd if (strcmp(cmdname, "umount") == 0) 7092168404Spjd cmdname = "unmount"; 7093168404Spjd 7094168404Spjd /* 7095168404Spjd * The 'recv' command is an alias for 'receive' 7096168404Spjd */ 7097168404Spjd if (strcmp(cmdname, "recv") == 0) 7098168404Spjd cmdname = "receive"; 7099168404Spjd 7100168404Spjd /* 7101263403Sdelphij * The 'snap' command is an alias for 'snapshot' 7102263403Sdelphij */ 7103263403Sdelphij if (strcmp(cmdname, "snap") == 0) 7104263403Sdelphij cmdname = "snapshot"; 7105263403Sdelphij 7106263403Sdelphij /* 7107168404Spjd * Special case '-?' 7108168404Spjd */ 7109168404Spjd if (strcmp(cmdname, "-?") == 0) 7110168404Spjd usage(B_TRUE); 7111168404Spjd 7112168404Spjd /* 7113168404Spjd * Run the appropriate command. 7114168404Spjd */ 7115209962Smm libzfs_mnttab_cache(g_zfs, B_TRUE); 7116185029Spjd if (find_command_idx(cmdname, &i) == 0) { 7117185029Spjd current_command = &command_table[i]; 7118185029Spjd ret = command_table[i].func(argc - 1, argv + 1); 7119185029Spjd } else if (strchr(cmdname, '=') != NULL) { 7120185029Spjd verify(find_command_idx("set", &i) == 0); 7121185029Spjd current_command = &command_table[i]; 7122185029Spjd ret = command_table[i].func(argc, argv); 7123185029Spjd } else { 7124168404Spjd (void) fprintf(stderr, gettext("unrecognized " 7125168404Spjd "command '%s'\n"), cmdname); 7126168404Spjd usage(B_FALSE); 7127168404Spjd } 7128209962Smm libzfs_mnttab_cache(g_zfs, B_FALSE); 7129168404Spjd } 7130168404Spjd 7131168404Spjd (void) fclose(mnttab_file); 7132168404Spjd 7133248571Smm if (ret == 0 && log_history) 7134248571Smm (void) zpool_log_history(g_zfs, history_str); 7135248571Smm 7136168404Spjd libzfs_fini(g_zfs); 7137168404Spjd 7138168404Spjd /* 7139168404Spjd * The 'ZFS_ABORT' environment variable causes us to dump core on exit 7140168404Spjd * for the purposes of running ::findleaks. 7141168404Spjd */ 7142168404Spjd if (getenv("ZFS_ABORT") != NULL) { 7143168404Spjd (void) printf("dumping core by request\n"); 7144168404Spjd abort(); 7145168404Spjd } 7146168404Spjd 7147168404Spjd return (ret); 7148168404Spjd} 7149