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. 24230449Smm * Copyright 2012 Nexenta Systems, Inc. All rights reserved. 25235216Smm * Copyright (c) 2012 by Delphix. All rights reserved. 26235217Smm * Copyright 2012 Milan Jurik. All rights reserved. 27235222Smm * Copyright (c) 2012, Joyent, Inc. All rights reserved. 28230438Spjd * Copyright (c) 2011-2012 Pawel Jakub Dawidek <pawel@dawidek.net>. 29230402Smm * All rights reserved. 30235216Smm * Copyright (c) 2012 Martin Matuska <mm@FreeBSD.org>. All rights reserved. 31251646Sdelphij * Copyright (c) 2013 Steven Hartland. 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> 71219089Spjd#endif 72168404Spjd 73168404Spjd#include "zfs_iter.h" 74168404Spjd#include "zfs_util.h" 75219089Spjd#include "zfs_comutil.h" 76168404Spjd 77168404Spjdlibzfs_handle_t *g_zfs; 78168404Spjd 79168404Spjdstatic FILE *mnttab_file; 80185029Spjdstatic char history_str[HIS_MAX_RECORD_LEN]; 81248571Smmstatic boolean_t log_history = B_TRUE; 82168404Spjd 83168404Spjdstatic int zfs_do_clone(int argc, char **argv); 84168404Spjdstatic int zfs_do_create(int argc, char **argv); 85168404Spjdstatic int zfs_do_destroy(int argc, char **argv); 86168404Spjdstatic int zfs_do_get(int argc, char **argv); 87168404Spjdstatic int zfs_do_inherit(int argc, char **argv); 88168404Spjdstatic int zfs_do_list(int argc, char **argv); 89168404Spjdstatic int zfs_do_mount(int argc, char **argv); 90168404Spjdstatic int zfs_do_rename(int argc, char **argv); 91168404Spjdstatic int zfs_do_rollback(int argc, char **argv); 92168404Spjdstatic int zfs_do_set(int argc, char **argv); 93185029Spjdstatic int zfs_do_upgrade(int argc, char **argv); 94168404Spjdstatic int zfs_do_snapshot(int argc, char **argv); 95168404Spjdstatic int zfs_do_unmount(int argc, char **argv); 96168404Spjdstatic int zfs_do_share(int argc, char **argv); 97168404Spjdstatic int zfs_do_unshare(int argc, char **argv); 98168404Spjdstatic int zfs_do_send(int argc, char **argv); 99168404Spjdstatic int zfs_do_receive(int argc, char **argv); 100168404Spjdstatic int zfs_do_promote(int argc, char **argv); 101209962Smmstatic int zfs_do_userspace(int argc, char **argv); 102219089Spjdstatic int zfs_do_allow(int argc, char **argv); 103219089Spjdstatic int zfs_do_unallow(int argc, char **argv); 104219089Spjdstatic int zfs_do_hold(int argc, char **argv); 105219089Spjdstatic int zfs_do_holds(int argc, char **argv); 106219089Spjdstatic int zfs_do_release(int argc, char **argv); 107219089Spjdstatic int zfs_do_diff(int argc, char **argv); 108168404Spjdstatic int zfs_do_jail(int argc, char **argv); 109168404Spjdstatic int zfs_do_unjail(int argc, char **argv); 110168404Spjd 111168404Spjd/* 112185029Spjd * Enable a reasonable set of defaults for libumem debugging on DEBUG builds. 113168404Spjd */ 114185029Spjd 115185029Spjd#ifdef DEBUG 116168404Spjdconst char * 117168404Spjd_umem_debug_init(void) 118168404Spjd{ 119168404Spjd return ("default,verbose"); /* $UMEM_DEBUG setting */ 120168404Spjd} 121168404Spjd 122168404Spjdconst char * 123168404Spjd_umem_logging_init(void) 124168404Spjd{ 125168404Spjd return ("fail,contents"); /* $UMEM_LOGGING setting */ 126168404Spjd} 127185029Spjd#endif 128168404Spjd 129168404Spjdtypedef enum { 130168404Spjd HELP_CLONE, 131168404Spjd HELP_CREATE, 132168404Spjd HELP_DESTROY, 133168404Spjd HELP_GET, 134168404Spjd HELP_INHERIT, 135185029Spjd HELP_UPGRADE, 136168404Spjd HELP_JAIL, 137168404Spjd HELP_UNJAIL, 138168404Spjd HELP_LIST, 139168404Spjd HELP_MOUNT, 140168404Spjd HELP_PROMOTE, 141168404Spjd HELP_RECEIVE, 142168404Spjd HELP_RENAME, 143168404Spjd HELP_ROLLBACK, 144168404Spjd HELP_SEND, 145168404Spjd HELP_SET, 146168404Spjd HELP_SHARE, 147168404Spjd HELP_SNAPSHOT, 148168404Spjd HELP_UNMOUNT, 149185029Spjd HELP_UNSHARE, 150185029Spjd HELP_ALLOW, 151209962Smm HELP_UNALLOW, 152209962Smm HELP_USERSPACE, 153219089Spjd HELP_GROUPSPACE, 154219089Spjd HELP_HOLD, 155219089Spjd HELP_HOLDS, 156219089Spjd HELP_RELEASE, 157228103Smm HELP_DIFF, 158168404Spjd} zfs_help_t; 159168404Spjd 160168404Spjdtypedef struct zfs_command { 161168404Spjd const char *name; 162168404Spjd int (*func)(int argc, char **argv); 163168404Spjd zfs_help_t usage; 164168404Spjd} zfs_command_t; 165168404Spjd 166168404Spjd/* 167168404Spjd * Master command table. Each ZFS command has a name, associated function, and 168168404Spjd * usage message. The usage messages need to be internationalized, so we have 169168404Spjd * to have a function to return the usage message based on a command index. 170168404Spjd * 171168404Spjd * These commands are organized according to how they are displayed in the usage 172168404Spjd * message. An empty command (one with a NULL name) indicates an empty line in 173168404Spjd * the generic usage message. 174168404Spjd */ 175168404Spjdstatic zfs_command_t command_table[] = { 176168404Spjd { "create", zfs_do_create, HELP_CREATE }, 177168404Spjd { "destroy", zfs_do_destroy, HELP_DESTROY }, 178168404Spjd { NULL }, 179168404Spjd { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT }, 180168404Spjd { "rollback", zfs_do_rollback, HELP_ROLLBACK }, 181168404Spjd { "clone", zfs_do_clone, HELP_CLONE }, 182168404Spjd { "promote", zfs_do_promote, HELP_PROMOTE }, 183168404Spjd { "rename", zfs_do_rename, HELP_RENAME }, 184168404Spjd { NULL }, 185168404Spjd { "list", zfs_do_list, HELP_LIST }, 186168404Spjd { NULL }, 187168404Spjd { "set", zfs_do_set, HELP_SET }, 188219089Spjd { "get", zfs_do_get, HELP_GET }, 189168404Spjd { "inherit", zfs_do_inherit, HELP_INHERIT }, 190185029Spjd { "upgrade", zfs_do_upgrade, HELP_UPGRADE }, 191209962Smm { "userspace", zfs_do_userspace, HELP_USERSPACE }, 192209962Smm { "groupspace", zfs_do_userspace, HELP_GROUPSPACE }, 193168404Spjd { NULL }, 194168404Spjd { "mount", zfs_do_mount, HELP_MOUNT }, 195168404Spjd { "unmount", zfs_do_unmount, HELP_UNMOUNT }, 196168404Spjd { "share", zfs_do_share, HELP_SHARE }, 197168404Spjd { "unshare", zfs_do_unshare, HELP_UNSHARE }, 198168404Spjd { NULL }, 199168404Spjd { "send", zfs_do_send, HELP_SEND }, 200168404Spjd { "receive", zfs_do_receive, HELP_RECEIVE }, 201168404Spjd { NULL }, 202219089Spjd { "allow", zfs_do_allow, HELP_ALLOW }, 203185029Spjd { NULL }, 204219089Spjd { "unallow", zfs_do_unallow, HELP_UNALLOW }, 205185029Spjd { NULL }, 206219089Spjd { "hold", zfs_do_hold, HELP_HOLD }, 207219089Spjd { "holds", zfs_do_holds, HELP_HOLDS }, 208219089Spjd { "release", zfs_do_release, HELP_RELEASE }, 209219089Spjd { "diff", zfs_do_diff, HELP_DIFF }, 210219089Spjd { NULL }, 211168404Spjd { "jail", zfs_do_jail, HELP_JAIL }, 212168404Spjd { "unjail", zfs_do_unjail, HELP_UNJAIL }, 213168404Spjd}; 214168404Spjd 215168404Spjd#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) 216168404Spjd 217168404Spjdzfs_command_t *current_command; 218168404Spjd 219168404Spjdstatic const char * 220168404Spjdget_usage(zfs_help_t idx) 221168404Spjd{ 222168404Spjd switch (idx) { 223168404Spjd case HELP_CLONE: 224185029Spjd return (gettext("\tclone [-p] [-o property=value] ... " 225185029Spjd "<snapshot> <filesystem|volume>\n")); 226168404Spjd case HELP_CREATE: 227234654Spjd return (gettext("\tcreate [-pu] [-o property=value] ... " 228168404Spjd "<filesystem>\n" 229185029Spjd "\tcreate [-ps] [-b blocksize] [-o property=value] ... " 230185029Spjd "-V <size> <volume>\n")); 231168404Spjd case HELP_DESTROY: 232228103Smm return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n" 233228103Smm "\tdestroy [-dnpRrv] " 234228103Smm "<snapshot>[%<snapname>][,...]\n")); 235168404Spjd case HELP_GET: 236205199Sdelphij return (gettext("\tget [-rHp] [-d max] " 237232064Smm "[-o \"all\" | field[,...]] [-t type[,...]] " 238232064Smm "[-s source[,...]]\n" 239185029Spjd "\t <\"all\" | property[,...]> " 240168404Spjd "[filesystem|volume|snapshot] ...\n")); 241168404Spjd case HELP_INHERIT: 242219089Spjd return (gettext("\tinherit [-rS] <property> " 243185029Spjd "<filesystem|volume|snapshot> ...\n")); 244185029Spjd case HELP_UPGRADE: 245185029Spjd return (gettext("\tupgrade [-v]\n" 246185029Spjd "\tupgrade [-r] [-V version] <-a | filesystem ...>\n")); 247168404Spjd case HELP_JAIL: 248240698Sbapt return (gettext("\tjail <jailid|jailname> <filesystem>\n")); 249168404Spjd case HELP_UNJAIL: 250240698Sbapt return (gettext("\tunjail <jailid|jailname> <filesystem>\n")); 251168404Spjd case HELP_LIST: 252205199Sdelphij return (gettext("\tlist [-rH][-d max] " 253205199Sdelphij "[-o property[,...]] [-t type[,...]] [-s property] ...\n" 254185029Spjd "\t [-S property] ... " 255185029Spjd "[filesystem|volume|snapshot] ...\n")); 256168404Spjd case HELP_MOUNT: 257168404Spjd return (gettext("\tmount\n" 258185029Spjd "\tmount [-vO] [-o opts] <-a | filesystem>\n")); 259168404Spjd case HELP_PROMOTE: 260185029Spjd return (gettext("\tpromote <clone-filesystem>\n")); 261168404Spjd case HELP_RECEIVE: 262219089Spjd return (gettext("\treceive [-vnFu] <filesystem|volume|" 263168404Spjd "snapshot>\n" 264219089Spjd "\treceive [-vnFu] [-d | -e] <filesystem>\n")); 265168404Spjd case HELP_RENAME: 266235216Smm return (gettext("\trename [-f] <filesystem|volume|snapshot> " 267168676Spjd "<filesystem|volume|snapshot>\n" 268235216Smm "\trename [-f] -p <filesystem|volume> " 269235216Smm "<filesystem|volume>\n" 270226705Spjd "\trename -r <snapshot> <snapshot>\n" 271226705Spjd "\trename -u [-p] <filesystem> <filesystem>")); 272168404Spjd case HELP_ROLLBACK: 273168404Spjd return (gettext("\trollback [-rRf] <snapshot>\n")); 274168404Spjd case HELP_SEND: 275240955Smm return (gettext("\tsend [-DnPpRv] " 276228020Smm "[-i snapshot | -I snapshot] <snapshot>\n")); 277168404Spjd case HELP_SET: 278168404Spjd return (gettext("\tset <property=value> " 279185029Spjd "<filesystem|volume|snapshot> ...\n")); 280168404Spjd case HELP_SHARE: 281185029Spjd return (gettext("\tshare <-a | filesystem>\n")); 282168404Spjd case HELP_SNAPSHOT: 283185029Spjd return (gettext("\tsnapshot [-r] [-o property=value] ... " 284248571Smm "<filesystem@snapname|volume@snapname> ...\n")); 285168404Spjd case HELP_UNMOUNT: 286185029Spjd return (gettext("\tunmount [-f] " 287185029Spjd "<-a | filesystem|mountpoint>\n")); 288168404Spjd case HELP_UNSHARE: 289219089Spjd return (gettext("\tunshare " 290185029Spjd "<-a | filesystem|mountpoint>\n")); 291185029Spjd case HELP_ALLOW: 292219089Spjd return (gettext("\tallow <filesystem|volume>\n" 293219089Spjd "\tallow [-ldug] " 294185029Spjd "<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n" 295185029Spjd "\t <filesystem|volume>\n" 296185029Spjd "\tallow [-ld] -e <perm|@setname>[,...] " 297185029Spjd "<filesystem|volume>\n" 298185029Spjd "\tallow -c <perm|@setname>[,...] <filesystem|volume>\n" 299185029Spjd "\tallow -s @setname <perm|@setname>[,...] " 300185029Spjd "<filesystem|volume>\n")); 301185029Spjd case HELP_UNALLOW: 302185029Spjd return (gettext("\tunallow [-rldug] " 303185029Spjd "<\"everyone\"|user|group>[,...]\n" 304185029Spjd "\t [<perm|@setname>[,...]] <filesystem|volume>\n" 305185029Spjd "\tunallow [-rld] -e [<perm|@setname>[,...]] " 306185029Spjd "<filesystem|volume>\n" 307185029Spjd "\tunallow [-r] -c [<perm|@setname>[,...]] " 308185029Spjd "<filesystem|volume>\n" 309185029Spjd "\tunallow [-r] -s @setname [<perm|@setname>[,...]] " 310185029Spjd "<filesystem|volume>\n")); 311209962Smm case HELP_USERSPACE: 312240415Smm return (gettext("\tuserspace [-Hinp] [-o field[,...]] " 313240415Smm "[-s field] ...\n\t[-S field] ... " 314240415Smm "[-t type[,...]] <filesystem|snapshot>\n")); 315209962Smm case HELP_GROUPSPACE: 316240415Smm return (gettext("\tgroupspace [-Hinp] [-o field[,...]] " 317240415Smm "[-s field] ...\n\t[-S field] ... " 318240415Smm "[-t type[,...]] <filesystem|snapshot>\n")); 319219089Spjd case HELP_HOLD: 320219089Spjd return (gettext("\thold [-r] <tag> <snapshot> ...\n")); 321219089Spjd case HELP_HOLDS: 322219089Spjd return (gettext("\tholds [-r] <snapshot> ...\n")); 323219089Spjd case HELP_RELEASE: 324219089Spjd return (gettext("\trelease [-r] <tag> <snapshot> ...\n")); 325219089Spjd case HELP_DIFF: 326219089Spjd return (gettext("\tdiff [-FHt] <snapshot> " 327219089Spjd "[snapshot|filesystem]\n")); 328168404Spjd } 329168404Spjd 330168404Spjd abort(); 331168404Spjd /* NOTREACHED */ 332168404Spjd} 333168404Spjd 334219089Spjdvoid 335219089Spjdnomem(void) 336219089Spjd{ 337219089Spjd (void) fprintf(stderr, gettext("internal error: out of memory\n")); 338219089Spjd exit(1); 339219089Spjd} 340219089Spjd 341168404Spjd/* 342168404Spjd * Utility function to guarantee malloc() success. 343168404Spjd */ 344219089Spjd 345168404Spjdvoid * 346168404Spjdsafe_malloc(size_t size) 347168404Spjd{ 348168404Spjd void *data; 349168404Spjd 350219089Spjd if ((data = calloc(1, size)) == NULL) 351219089Spjd nomem(); 352168404Spjd 353168404Spjd return (data); 354168404Spjd} 355168404Spjd 356219089Spjdstatic char * 357219089Spjdsafe_strdup(char *str) 358219089Spjd{ 359219089Spjd char *dupstr = strdup(str); 360219089Spjd 361219089Spjd if (dupstr == NULL) 362219089Spjd nomem(); 363219089Spjd 364219089Spjd return (dupstr); 365219089Spjd} 366219089Spjd 367168404Spjd/* 368185029Spjd * Callback routine that will print out information for each of 369168404Spjd * the properties. 370168404Spjd */ 371185029Spjdstatic int 372185029Spjdusage_prop_cb(int prop, void *cb) 373168404Spjd{ 374168404Spjd FILE *fp = cb; 375168404Spjd 376185029Spjd (void) fprintf(fp, "\t%-15s ", zfs_prop_to_name(prop)); 377168404Spjd 378168404Spjd if (zfs_prop_readonly(prop)) 379185029Spjd (void) fprintf(fp, " NO "); 380168404Spjd else 381185029Spjd (void) fprintf(fp, "YES "); 382168404Spjd 383168404Spjd if (zfs_prop_inheritable(prop)) 384168404Spjd (void) fprintf(fp, " YES "); 385168404Spjd else 386168404Spjd (void) fprintf(fp, " NO "); 387168404Spjd 388168404Spjd if (zfs_prop_values(prop) == NULL) 389168404Spjd (void) fprintf(fp, "-\n"); 390168404Spjd else 391168404Spjd (void) fprintf(fp, "%s\n", zfs_prop_values(prop)); 392168404Spjd 393185029Spjd return (ZPROP_CONT); 394168404Spjd} 395168404Spjd 396168404Spjd/* 397168404Spjd * Display usage message. If we're inside a command, display only the usage for 398168404Spjd * that command. Otherwise, iterate over the entire command table and display 399168404Spjd * a complete usage message. 400168404Spjd */ 401168404Spjdstatic void 402168404Spjdusage(boolean_t requested) 403168404Spjd{ 404168404Spjd int i; 405168404Spjd boolean_t show_properties = B_FALSE; 406168404Spjd FILE *fp = requested ? stdout : stderr; 407168404Spjd 408168404Spjd if (current_command == NULL) { 409168404Spjd 410168404Spjd (void) fprintf(fp, gettext("usage: zfs command args ...\n")); 411168404Spjd (void) fprintf(fp, 412168404Spjd gettext("where 'command' is one of the following:\n\n")); 413168404Spjd 414168404Spjd for (i = 0; i < NCOMMAND; i++) { 415168404Spjd if (command_table[i].name == NULL) 416168404Spjd (void) fprintf(fp, "\n"); 417168404Spjd else 418168404Spjd (void) fprintf(fp, "%s", 419168404Spjd get_usage(command_table[i].usage)); 420168404Spjd } 421168404Spjd 422168404Spjd (void) fprintf(fp, gettext("\nEach dataset is of the form: " 423168404Spjd "pool/[dataset/]*dataset[@name]\n")); 424168404Spjd } else { 425168404Spjd (void) fprintf(fp, gettext("usage:\n")); 426168404Spjd (void) fprintf(fp, "%s", get_usage(current_command->usage)); 427168404Spjd } 428168404Spjd 429168404Spjd if (current_command != NULL && 430168404Spjd (strcmp(current_command->name, "set") == 0 || 431168404Spjd strcmp(current_command->name, "get") == 0 || 432168404Spjd strcmp(current_command->name, "inherit") == 0 || 433168404Spjd strcmp(current_command->name, "list") == 0)) 434168404Spjd show_properties = B_TRUE; 435168404Spjd 436168404Spjd if (show_properties) { 437168404Spjd (void) fprintf(fp, 438168404Spjd gettext("\nThe following properties are supported:\n")); 439168404Spjd 440185029Spjd (void) fprintf(fp, "\n\t%-14s %s %s %s\n\n", 441168404Spjd "PROPERTY", "EDIT", "INHERIT", "VALUES"); 442168404Spjd 443168404Spjd /* Iterate over all properties */ 444185029Spjd (void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE, 445185029Spjd ZFS_TYPE_DATASET); 446168404Spjd 447209962Smm (void) fprintf(fp, "\t%-15s ", "userused@..."); 448209962Smm (void) fprintf(fp, " NO NO <size>\n"); 449209962Smm (void) fprintf(fp, "\t%-15s ", "groupused@..."); 450209962Smm (void) fprintf(fp, " NO NO <size>\n"); 451209962Smm (void) fprintf(fp, "\t%-15s ", "userquota@..."); 452209962Smm (void) fprintf(fp, "YES NO <size> | none\n"); 453209962Smm (void) fprintf(fp, "\t%-15s ", "groupquota@..."); 454209962Smm (void) fprintf(fp, "YES NO <size> | none\n"); 455228103Smm (void) fprintf(fp, "\t%-15s ", "written@<snap>"); 456228103Smm (void) fprintf(fp, " NO NO <size>\n"); 457209962Smm 458168404Spjd (void) fprintf(fp, gettext("\nSizes are specified in bytes " 459168404Spjd "with standard units such as K, M, G, etc.\n")); 460185029Spjd (void) fprintf(fp, gettext("\nUser-defined properties can " 461168404Spjd "be specified by using a name containing a colon (:).\n")); 462209962Smm (void) fprintf(fp, gettext("\nThe {user|group}{used|quota}@ " 463209962Smm "properties must be appended with\n" 464209962Smm "a user or group specifier of one of these forms:\n" 465209962Smm " POSIX name (eg: \"matt\")\n" 466209962Smm " POSIX id (eg: \"126829\")\n" 467209962Smm " SMB name@domain (eg: \"matt@sun\")\n" 468209962Smm " SMB SID (eg: \"S-1-234-567-89\")\n")); 469168404Spjd } else { 470168404Spjd (void) fprintf(fp, 471209962Smm gettext("\nFor the property list, run: %s\n"), 472209962Smm "zfs set|get"); 473185029Spjd (void) fprintf(fp, 474209962Smm gettext("\nFor the delegated permission list, run: %s\n"), 475209962Smm "zfs allow|unallow"); 476168404Spjd } 477168404Spjd 478168404Spjd /* 479168404Spjd * See comments at end of main(). 480168404Spjd */ 481168404Spjd if (getenv("ZFS_ABORT") != NULL) { 482168404Spjd (void) printf("dumping core by request\n"); 483168404Spjd abort(); 484168404Spjd } 485168404Spjd 486168404Spjd exit(requested ? 0 : 2); 487168404Spjd} 488168404Spjd 489185029Spjdstatic int 490185029Spjdparseprop(nvlist_t *props) 491185029Spjd{ 492185029Spjd char *propname = optarg; 493185029Spjd char *propval, *strval; 494185029Spjd 495185029Spjd if ((propval = strchr(propname, '=')) == NULL) { 496185029Spjd (void) fprintf(stderr, gettext("missing " 497185029Spjd "'=' for -o option\n")); 498185029Spjd return (-1); 499185029Spjd } 500185029Spjd *propval = '\0'; 501185029Spjd propval++; 502185029Spjd if (nvlist_lookup_string(props, propname, &strval) == 0) { 503185029Spjd (void) fprintf(stderr, gettext("property '%s' " 504185029Spjd "specified multiple times\n"), propname); 505185029Spjd return (-1); 506185029Spjd } 507219089Spjd if (nvlist_add_string(props, propname, propval) != 0) 508219089Spjd nomem(); 509185029Spjd return (0); 510185029Spjd} 511185029Spjd 512205199Sdelphijstatic int 513205199Sdelphijparse_depth(char *opt, int *flags) 514205199Sdelphij{ 515205199Sdelphij char *tmp; 516205199Sdelphij int depth; 517205199Sdelphij 518205199Sdelphij depth = (int)strtol(opt, &tmp, 0); 519205199Sdelphij if (*tmp) { 520205199Sdelphij (void) fprintf(stderr, 521205199Sdelphij gettext("%s is not an integer\n"), optarg); 522205199Sdelphij usage(B_FALSE); 523205199Sdelphij } 524205199Sdelphij if (depth < 0) { 525205199Sdelphij (void) fprintf(stderr, 526205199Sdelphij gettext("Depth can not be negative.\n")); 527205199Sdelphij usage(B_FALSE); 528205199Sdelphij } 529205199Sdelphij *flags |= (ZFS_ITER_DEPTH_LIMIT|ZFS_ITER_RECURSE); 530205199Sdelphij return (depth); 531205199Sdelphij} 532205199Sdelphij 533219089Spjd#define PROGRESS_DELAY 2 /* seconds */ 534219089Spjd 535219089Spjdstatic 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"; 536219089Spjdstatic time_t pt_begin; 537219089Spjdstatic char *pt_header = NULL; 538219089Spjdstatic boolean_t pt_shown; 539219089Spjd 540219089Spjdstatic void 541219089Spjdstart_progress_timer(void) 542219089Spjd{ 543219089Spjd pt_begin = time(NULL) + PROGRESS_DELAY; 544219089Spjd pt_shown = B_FALSE; 545219089Spjd} 546219089Spjd 547219089Spjdstatic void 548219089Spjdset_progress_header(char *header) 549219089Spjd{ 550219089Spjd assert(pt_header == NULL); 551219089Spjd pt_header = safe_strdup(header); 552219089Spjd if (pt_shown) { 553219089Spjd (void) printf("%s: ", header); 554219089Spjd (void) fflush(stdout); 555219089Spjd } 556219089Spjd} 557219089Spjd 558219089Spjdstatic void 559219089Spjdupdate_progress(char *update) 560219089Spjd{ 561219089Spjd if (!pt_shown && time(NULL) > pt_begin) { 562219089Spjd int len = strlen(update); 563219089Spjd 564219089Spjd (void) printf("%s: %s%*.*s", pt_header, update, len, len, 565219089Spjd pt_reverse); 566219089Spjd (void) fflush(stdout); 567219089Spjd pt_shown = B_TRUE; 568219089Spjd } else if (pt_shown) { 569219089Spjd int len = strlen(update); 570219089Spjd 571219089Spjd (void) printf("%s%*.*s", update, len, len, pt_reverse); 572219089Spjd (void) fflush(stdout); 573219089Spjd } 574219089Spjd} 575219089Spjd 576219089Spjdstatic void 577219089Spjdfinish_progress(char *done) 578219089Spjd{ 579219089Spjd if (pt_shown) { 580219089Spjd (void) printf("%s\n", done); 581219089Spjd (void) fflush(stdout); 582219089Spjd } 583219089Spjd free(pt_header); 584219089Spjd pt_header = NULL; 585219089Spjd} 586168404Spjd/* 587185029Spjd * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol> 588168404Spjd * 589168404Spjd * Given an existing dataset, create a writable copy whose initial contents 590168404Spjd * are the same as the source. The newly created dataset maintains a 591168404Spjd * dependency on the original; the original cannot be destroyed so long as 592168404Spjd * the clone exists. 593185029Spjd * 594185029Spjd * The '-p' flag creates all the non-existing ancestors of the target first. 595168404Spjd */ 596168404Spjdstatic int 597168404Spjdzfs_do_clone(int argc, char **argv) 598168404Spjd{ 599185029Spjd zfs_handle_t *zhp = NULL; 600185029Spjd boolean_t parents = B_FALSE; 601185029Spjd nvlist_t *props; 602231144Smm int ret = 0; 603185029Spjd int c; 604168404Spjd 605219089Spjd if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 606219089Spjd nomem(); 607185029Spjd 608168404Spjd /* check options */ 609185029Spjd while ((c = getopt(argc, argv, "o:p")) != -1) { 610185029Spjd switch (c) { 611185029Spjd case 'o': 612185029Spjd if (parseprop(props)) 613185029Spjd return (1); 614185029Spjd break; 615185029Spjd case 'p': 616185029Spjd parents = B_TRUE; 617185029Spjd break; 618185029Spjd case '?': 619185029Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 620185029Spjd optopt); 621185029Spjd goto usage; 622185029Spjd } 623168404Spjd } 624168404Spjd 625185029Spjd argc -= optind; 626185029Spjd argv += optind; 627185029Spjd 628168404Spjd /* check number of arguments */ 629185029Spjd if (argc < 1) { 630168404Spjd (void) fprintf(stderr, gettext("missing source dataset " 631168404Spjd "argument\n")); 632185029Spjd goto usage; 633168404Spjd } 634185029Spjd if (argc < 2) { 635168404Spjd (void) fprintf(stderr, gettext("missing target dataset " 636168404Spjd "argument\n")); 637185029Spjd goto usage; 638168404Spjd } 639185029Spjd if (argc > 2) { 640168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 641185029Spjd goto usage; 642168404Spjd } 643168404Spjd 644168404Spjd /* open the source dataset */ 645185029Spjd if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 646168404Spjd return (1); 647168404Spjd 648185029Spjd if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM | 649185029Spjd ZFS_TYPE_VOLUME)) { 650185029Spjd /* 651185029Spjd * Now create the ancestors of the target dataset. If the 652185029Spjd * target already exists and '-p' option was used we should not 653185029Spjd * complain. 654185029Spjd */ 655185029Spjd if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | 656185029Spjd ZFS_TYPE_VOLUME)) 657185029Spjd return (0); 658185029Spjd if (zfs_create_ancestors(g_zfs, argv[1]) != 0) 659185029Spjd return (1); 660185029Spjd } 661185029Spjd 662168404Spjd /* pass to libzfs */ 663185029Spjd ret = zfs_clone(zhp, argv[1], props); 664168404Spjd 665168404Spjd /* create the mountpoint if necessary */ 666168404Spjd if (ret == 0) { 667185029Spjd zfs_handle_t *clone; 668185029Spjd 669185029Spjd clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_DATASET); 670168404Spjd if (clone != NULL) { 671219089Spjd if (zfs_get_type(clone) != ZFS_TYPE_VOLUME) 672219089Spjd if ((ret = zfs_mount(clone, NULL, 0)) == 0) 673219089Spjd ret = zfs_share(clone); 674168404Spjd zfs_close(clone); 675168404Spjd } 676168404Spjd } 677168404Spjd 678168404Spjd zfs_close(zhp); 679185029Spjd nvlist_free(props); 680168404Spjd 681185029Spjd return (!!ret); 682185029Spjd 683185029Spjdusage: 684185029Spjd if (zhp) 685185029Spjd zfs_close(zhp); 686185029Spjd nvlist_free(props); 687185029Spjd usage(B_FALSE); 688185029Spjd return (-1); 689168404Spjd} 690168404Spjd 691168404Spjd/* 692234654Spjd * zfs create [-pu] [-o prop=value] ... fs 693185029Spjd * zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size 694168404Spjd * 695168404Spjd * Create a new dataset. This command can be used to create filesystems 696168404Spjd * and volumes. Snapshot creation is handled by 'zfs snapshot'. 697168404Spjd * For volumes, the user must specify a size to be used. 698168404Spjd * 699168404Spjd * The '-s' flag applies only to volumes, and indicates that we should not try 700168404Spjd * to set the reservation for this volume. By default we set a reservation 701185029Spjd * equal to the size for any volume. For pools with SPA_VERSION >= 702185029Spjd * SPA_VERSION_REFRESERVATION, we set a refreservation instead. 703185029Spjd * 704185029Spjd * The '-p' flag creates all the non-existing ancestors of the target first. 705234654Spjd * 706234654Spjd * The '-u' flag prevents mounting of newly created file system. 707168404Spjd */ 708168404Spjdstatic int 709168404Spjdzfs_do_create(int argc, char **argv) 710168404Spjd{ 711168404Spjd zfs_type_t type = ZFS_TYPE_FILESYSTEM; 712168404Spjd zfs_handle_t *zhp = NULL; 713168404Spjd uint64_t volsize; 714168404Spjd int c; 715168404Spjd boolean_t noreserve = B_FALSE; 716185029Spjd boolean_t bflag = B_FALSE; 717185029Spjd boolean_t parents = B_FALSE; 718234654Spjd boolean_t nomount = B_FALSE; 719168404Spjd int ret = 1; 720185029Spjd nvlist_t *props; 721168404Spjd uint64_t intval; 722219089Spjd int canmount = ZFS_CANMOUNT_OFF; 723168404Spjd 724219089Spjd if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 725219089Spjd nomem(); 726168404Spjd 727168404Spjd /* check options */ 728234654Spjd while ((c = getopt(argc, argv, ":V:b:so:pu")) != -1) { 729168404Spjd switch (c) { 730168404Spjd case 'V': 731168404Spjd type = ZFS_TYPE_VOLUME; 732168404Spjd if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 733168404Spjd (void) fprintf(stderr, gettext("bad volume " 734168404Spjd "size '%s': %s\n"), optarg, 735168404Spjd libzfs_error_description(g_zfs)); 736168404Spjd goto error; 737168404Spjd } 738168404Spjd 739168404Spjd if (nvlist_add_uint64(props, 740219089Spjd zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0) 741219089Spjd nomem(); 742168404Spjd volsize = intval; 743168404Spjd break; 744185029Spjd case 'p': 745185029Spjd parents = B_TRUE; 746185029Spjd break; 747168404Spjd case 'b': 748185029Spjd bflag = B_TRUE; 749168404Spjd if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 750168404Spjd (void) fprintf(stderr, gettext("bad volume " 751168404Spjd "block size '%s': %s\n"), optarg, 752168404Spjd libzfs_error_description(g_zfs)); 753168404Spjd goto error; 754168404Spjd } 755168404Spjd 756168404Spjd if (nvlist_add_uint64(props, 757168404Spjd zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 758219089Spjd intval) != 0) 759219089Spjd nomem(); 760168404Spjd break; 761168404Spjd case 'o': 762185029Spjd if (parseprop(props)) 763168404Spjd goto error; 764168404Spjd break; 765168404Spjd case 's': 766168404Spjd noreserve = B_TRUE; 767168404Spjd break; 768234654Spjd case 'u': 769234654Spjd nomount = B_TRUE; 770234654Spjd break; 771168404Spjd case ':': 772168404Spjd (void) fprintf(stderr, gettext("missing size " 773168404Spjd "argument\n")); 774168404Spjd goto badusage; 775168404Spjd case '?': 776168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 777168404Spjd optopt); 778168404Spjd goto badusage; 779168404Spjd } 780168404Spjd } 781168404Spjd 782185029Spjd if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) { 783185029Spjd (void) fprintf(stderr, gettext("'-s' and '-b' can only be " 784185029Spjd "used when creating a volume\n")); 785168404Spjd goto badusage; 786168404Spjd } 787234654Spjd if (nomount && type != ZFS_TYPE_FILESYSTEM) { 788234654Spjd (void) fprintf(stderr, gettext("'-u' can only be " 789234654Spjd "used when creating a file system\n")); 790234654Spjd goto badusage; 791234654Spjd } 792168404Spjd 793168404Spjd argc -= optind; 794168404Spjd argv += optind; 795168404Spjd 796168404Spjd /* check number of arguments */ 797168404Spjd if (argc == 0) { 798168404Spjd (void) fprintf(stderr, gettext("missing %s argument\n"), 799168404Spjd zfs_type_to_name(type)); 800168404Spjd goto badusage; 801168404Spjd } 802168404Spjd if (argc > 1) { 803168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 804168404Spjd goto badusage; 805168404Spjd } 806168404Spjd 807185029Spjd if (type == ZFS_TYPE_VOLUME && !noreserve) { 808185029Spjd zpool_handle_t *zpool_handle; 809185029Spjd uint64_t spa_version; 810185029Spjd char *p; 811185029Spjd zfs_prop_t resv_prop; 812185029Spjd char *strval; 813185029Spjd 814185029Spjd if (p = strchr(argv[0], '/')) 815185029Spjd *p = '\0'; 816185029Spjd zpool_handle = zpool_open(g_zfs, argv[0]); 817185029Spjd if (p != NULL) 818185029Spjd *p = '/'; 819185029Spjd if (zpool_handle == NULL) 820185029Spjd goto error; 821185029Spjd spa_version = zpool_get_prop_int(zpool_handle, 822185029Spjd ZPOOL_PROP_VERSION, NULL); 823185029Spjd zpool_close(zpool_handle); 824185029Spjd if (spa_version >= SPA_VERSION_REFRESERVATION) 825185029Spjd resv_prop = ZFS_PROP_REFRESERVATION; 826185029Spjd else 827185029Spjd resv_prop = ZFS_PROP_RESERVATION; 828219089Spjd volsize = zvol_volsize_to_reservation(volsize, props); 829185029Spjd 830185029Spjd if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop), 831185029Spjd &strval) != 0) { 832185029Spjd if (nvlist_add_uint64(props, 833185029Spjd zfs_prop_to_name(resv_prop), volsize) != 0) { 834185029Spjd nvlist_free(props); 835219089Spjd nomem(); 836185029Spjd } 837168404Spjd } 838168404Spjd } 839168404Spjd 840185029Spjd if (parents && zfs_name_valid(argv[0], type)) { 841185029Spjd /* 842185029Spjd * Now create the ancestors of target dataset. If the target 843185029Spjd * already exists and '-p' option was used we should not 844185029Spjd * complain. 845185029Spjd */ 846185029Spjd if (zfs_dataset_exists(g_zfs, argv[0], type)) { 847185029Spjd ret = 0; 848185029Spjd goto error; 849185029Spjd } 850185029Spjd if (zfs_create_ancestors(g_zfs, argv[0]) != 0) 851185029Spjd goto error; 852185029Spjd } 853185029Spjd 854168404Spjd /* pass to libzfs */ 855168404Spjd if (zfs_create(g_zfs, argv[0], type, props) != 0) 856168404Spjd goto error; 857168404Spjd 858185029Spjd if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL) 859168404Spjd goto error; 860219089Spjd 861219089Spjd ret = 0; 862185029Spjd /* 863185029Spjd * if the user doesn't want the dataset automatically mounted, 864185029Spjd * then skip the mount/share step 865185029Spjd */ 866219089Spjd if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type)) 867219089Spjd canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); 868168404Spjd 869168404Spjd /* 870168404Spjd * Mount and/or share the new filesystem as appropriate. We provide a 871168404Spjd * verbose error message to let the user know that their filesystem was 872168404Spjd * in fact created, even if we failed to mount or share it. 873168404Spjd */ 874234654Spjd if (!nomount && canmount == ZFS_CANMOUNT_ON) { 875185029Spjd if (zfs_mount(zhp, NULL, 0) != 0) { 876185029Spjd (void) fprintf(stderr, gettext("filesystem " 877185029Spjd "successfully created, but not mounted\n")); 878185029Spjd ret = 1; 879185029Spjd } else if (zfs_share(zhp) != 0) { 880185029Spjd (void) fprintf(stderr, gettext("filesystem " 881185029Spjd "successfully created, but not shared\n")); 882185029Spjd ret = 1; 883185029Spjd } 884168404Spjd } 885168404Spjd 886168404Spjderror: 887168404Spjd if (zhp) 888168404Spjd zfs_close(zhp); 889168404Spjd nvlist_free(props); 890168404Spjd return (ret); 891168404Spjdbadusage: 892168404Spjd nvlist_free(props); 893168404Spjd usage(B_FALSE); 894168404Spjd return (2); 895168404Spjd} 896168404Spjd 897168404Spjd/* 898219089Spjd * zfs destroy [-rRf] <fs, vol> 899219089Spjd * zfs destroy [-rRd] <snap> 900168404Spjd * 901219089Spjd * -r Recursively destroy all children 902219089Spjd * -R Recursively destroy all dependents, including clones 903219089Spjd * -f Force unmounting of any dependents 904219089Spjd * -d If we can't destroy now, mark for deferred destruction 905168404Spjd * 906168404Spjd * Destroys the given dataset. By default, it will unmount any filesystems, 907168404Spjd * and refuse to destroy a dataset that has any dependents. A dependent can 908168404Spjd * either be a child, or a clone of a child. 909168404Spjd */ 910168404Spjdtypedef struct destroy_cbdata { 911168404Spjd boolean_t cb_first; 912228103Smm boolean_t cb_force; 913228103Smm boolean_t cb_recurse; 914228103Smm boolean_t cb_error; 915228103Smm boolean_t cb_doclones; 916168404Spjd zfs_handle_t *cb_target; 917219089Spjd boolean_t cb_defer_destroy; 918228103Smm boolean_t cb_verbose; 919228103Smm boolean_t cb_parsable; 920228103Smm boolean_t cb_dryrun; 921228103Smm nvlist_t *cb_nvl; 922248571Smm nvlist_t *cb_batchedsnaps; 923228103Smm 924228103Smm /* first snap in contiguous run */ 925248571Smm char *cb_firstsnap; 926228103Smm /* previous snap in contiguous run */ 927248571Smm char *cb_prevsnap; 928228103Smm int64_t cb_snapused; 929228103Smm char *cb_snapspec; 930168404Spjd} destroy_cbdata_t; 931168404Spjd 932168404Spjd/* 933168404Spjd * Check for any dependents based on the '-r' or '-R' flags. 934168404Spjd */ 935168404Spjdstatic int 936168404Spjddestroy_check_dependent(zfs_handle_t *zhp, void *data) 937168404Spjd{ 938168404Spjd destroy_cbdata_t *cbp = data; 939168404Spjd const char *tname = zfs_get_name(cbp->cb_target); 940168404Spjd const char *name = zfs_get_name(zhp); 941168404Spjd 942168404Spjd if (strncmp(tname, name, strlen(tname)) == 0 && 943168404Spjd (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { 944168404Spjd /* 945168404Spjd * This is a direct descendant, not a clone somewhere else in 946168404Spjd * the hierarchy. 947168404Spjd */ 948168404Spjd if (cbp->cb_recurse) 949168404Spjd goto out; 950168404Spjd 951168404Spjd if (cbp->cb_first) { 952168404Spjd (void) fprintf(stderr, gettext("cannot destroy '%s': " 953168404Spjd "%s has children\n"), 954168404Spjd zfs_get_name(cbp->cb_target), 955168404Spjd zfs_type_to_name(zfs_get_type(cbp->cb_target))); 956168404Spjd (void) fprintf(stderr, gettext("use '-r' to destroy " 957168404Spjd "the following datasets:\n")); 958168404Spjd cbp->cb_first = B_FALSE; 959228103Smm cbp->cb_error = B_TRUE; 960168404Spjd } 961168404Spjd 962168404Spjd (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 963168404Spjd } else { 964168404Spjd /* 965168404Spjd * This is a clone. We only want to report this if the '-r' 966168404Spjd * wasn't specified, or the target is a snapshot. 967168404Spjd */ 968168404Spjd if (!cbp->cb_recurse && 969168404Spjd zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) 970168404Spjd goto out; 971168404Spjd 972168404Spjd if (cbp->cb_first) { 973168404Spjd (void) fprintf(stderr, gettext("cannot destroy '%s': " 974168404Spjd "%s has dependent clones\n"), 975168404Spjd zfs_get_name(cbp->cb_target), 976168404Spjd zfs_type_to_name(zfs_get_type(cbp->cb_target))); 977168404Spjd (void) fprintf(stderr, gettext("use '-R' to destroy " 978168404Spjd "the following datasets:\n")); 979168404Spjd cbp->cb_first = B_FALSE; 980228103Smm cbp->cb_error = B_TRUE; 981228103Smm cbp->cb_dryrun = B_TRUE; 982168404Spjd } 983168404Spjd 984168404Spjd (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 985168404Spjd } 986168404Spjd 987168404Spjdout: 988168404Spjd zfs_close(zhp); 989168404Spjd return (0); 990168404Spjd} 991168404Spjd 992168404Spjdstatic int 993168404Spjddestroy_callback(zfs_handle_t *zhp, void *data) 994168404Spjd{ 995228103Smm destroy_cbdata_t *cb = data; 996228103Smm const char *name = zfs_get_name(zhp); 997168404Spjd 998228103Smm if (cb->cb_verbose) { 999228103Smm if (cb->cb_parsable) { 1000228103Smm (void) printf("destroy\t%s\n", name); 1001228103Smm } else if (cb->cb_dryrun) { 1002228103Smm (void) printf(gettext("would destroy %s\n"), 1003228103Smm name); 1004228103Smm } else { 1005228103Smm (void) printf(gettext("will destroy %s\n"), 1006228103Smm name); 1007228103Smm } 1008228103Smm } 1009228103Smm 1010168404Spjd /* 1011168404Spjd * Ignore pools (which we've already flagged as an error before getting 1012219089Spjd * here). 1013168404Spjd */ 1014168404Spjd if (strchr(zfs_get_name(zhp), '/') == NULL && 1015168404Spjd zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 1016168404Spjd zfs_close(zhp); 1017168404Spjd return (0); 1018168404Spjd } 1019248571Smm if (cb->cb_dryrun) { 1020248571Smm zfs_close(zhp); 1021248571Smm return (0); 1022248571Smm } 1023168404Spjd 1024248571Smm /* 1025248571Smm * We batch up all contiguous snapshots (even of different 1026248571Smm * filesystems) and destroy them with one ioctl. We can't 1027248571Smm * simply do all snap deletions and then all fs deletions, 1028248571Smm * because we must delete a clone before its origin. 1029248571Smm */ 1030248571Smm if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { 1031248571Smm fnvlist_add_boolean(cb->cb_batchedsnaps, name); 1032248571Smm } else { 1033248571Smm int error = zfs_destroy_snaps_nvl(g_zfs, 1034248571Smm cb->cb_batchedsnaps, B_FALSE); 1035248571Smm fnvlist_free(cb->cb_batchedsnaps); 1036248571Smm cb->cb_batchedsnaps = fnvlist_alloc(); 1037248571Smm 1038248571Smm if (error != 0 || 1039248571Smm zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 || 1040228103Smm zfs_destroy(zhp, cb->cb_defer_destroy) != 0) { 1041228103Smm zfs_close(zhp); 1042228103Smm return (-1); 1043228103Smm } 1044168404Spjd } 1045168404Spjd 1046168404Spjd zfs_close(zhp); 1047168404Spjd return (0); 1048168404Spjd} 1049168404Spjd 1050168404Spjdstatic int 1051228103Smmdestroy_print_cb(zfs_handle_t *zhp, void *arg) 1052168404Spjd{ 1053228103Smm destroy_cbdata_t *cb = arg; 1054228103Smm const char *name = zfs_get_name(zhp); 1055228103Smm int err = 0; 1056168404Spjd 1057228103Smm if (nvlist_exists(cb->cb_nvl, name)) { 1058228103Smm if (cb->cb_firstsnap == NULL) 1059248571Smm cb->cb_firstsnap = strdup(name); 1060228103Smm if (cb->cb_prevsnap != NULL) 1061248571Smm free(cb->cb_prevsnap); 1062228103Smm /* this snap continues the current range */ 1063248571Smm cb->cb_prevsnap = strdup(name); 1064248571Smm if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL) 1065248571Smm nomem(); 1066228103Smm if (cb->cb_verbose) { 1067228103Smm if (cb->cb_parsable) { 1068228103Smm (void) printf("destroy\t%s\n", name); 1069228103Smm } else if (cb->cb_dryrun) { 1070228103Smm (void) printf(gettext("would destroy %s\n"), 1071228103Smm name); 1072228103Smm } else { 1073228103Smm (void) printf(gettext("will destroy %s\n"), 1074228103Smm name); 1075228103Smm } 1076228103Smm } 1077228103Smm } else if (cb->cb_firstsnap != NULL) { 1078228103Smm /* end of this range */ 1079228103Smm uint64_t used = 0; 1080248571Smm err = lzc_snaprange_space(cb->cb_firstsnap, 1081228103Smm cb->cb_prevsnap, &used); 1082228103Smm cb->cb_snapused += used; 1083248571Smm free(cb->cb_firstsnap); 1084228103Smm cb->cb_firstsnap = NULL; 1085248571Smm free(cb->cb_prevsnap); 1086228103Smm cb->cb_prevsnap = NULL; 1087228103Smm } 1088228103Smm zfs_close(zhp); 1089228103Smm return (err); 1090228103Smm} 1091168404Spjd 1092228103Smmstatic int 1093228103Smmdestroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb) 1094228103Smm{ 1095231144Smm int err = 0; 1096228103Smm assert(cb->cb_firstsnap == NULL); 1097228103Smm assert(cb->cb_prevsnap == NULL); 1098228103Smm err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb); 1099228103Smm if (cb->cb_firstsnap != NULL) { 1100228103Smm uint64_t used = 0; 1101228103Smm if (err == 0) { 1102248571Smm err = lzc_snaprange_space(cb->cb_firstsnap, 1103228103Smm cb->cb_prevsnap, &used); 1104168404Spjd } 1105228103Smm cb->cb_snapused += used; 1106248571Smm free(cb->cb_firstsnap); 1107228103Smm cb->cb_firstsnap = NULL; 1108248571Smm free(cb->cb_prevsnap); 1109228103Smm cb->cb_prevsnap = NULL; 1110168404Spjd } 1111228103Smm return (err); 1112228103Smm} 1113168404Spjd 1114228103Smmstatic int 1115228103Smmsnapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg) 1116228103Smm{ 1117228103Smm destroy_cbdata_t *cb = arg; 1118228103Smm int err = 0; 1119228103Smm 1120228103Smm /* Check for clones. */ 1121238422Smm if (!cb->cb_doclones && !cb->cb_defer_destroy) { 1122228103Smm cb->cb_target = zhp; 1123228103Smm cb->cb_first = B_TRUE; 1124228103Smm err = zfs_iter_dependents(zhp, B_TRUE, 1125228103Smm destroy_check_dependent, cb); 1126228103Smm } 1127228103Smm 1128228103Smm if (err == 0) { 1129228103Smm if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp))) 1130228103Smm nomem(); 1131228103Smm } 1132228103Smm zfs_close(zhp); 1133228103Smm return (err); 1134168404Spjd} 1135168404Spjd 1136168404Spjdstatic int 1137228103Smmgather_snapshots(zfs_handle_t *zhp, void *arg) 1138228103Smm{ 1139228103Smm destroy_cbdata_t *cb = arg; 1140228103Smm int err = 0; 1141228103Smm 1142228103Smm err = zfs_iter_snapspec(zhp, cb->cb_snapspec, snapshot_to_nvl_cb, cb); 1143228103Smm if (err == ENOENT) 1144228103Smm err = 0; 1145228103Smm if (err != 0) 1146228103Smm goto out; 1147228103Smm 1148228103Smm if (cb->cb_verbose) { 1149228103Smm err = destroy_print_snapshots(zhp, cb); 1150228103Smm if (err != 0) 1151228103Smm goto out; 1152228103Smm } 1153228103Smm 1154228103Smm if (cb->cb_recurse) 1155228103Smm err = zfs_iter_filesystems(zhp, gather_snapshots, cb); 1156228103Smm 1157228103Smmout: 1158228103Smm zfs_close(zhp); 1159228103Smm return (err); 1160228103Smm} 1161228103Smm 1162228103Smmstatic int 1163228103Smmdestroy_clones(destroy_cbdata_t *cb) 1164228103Smm{ 1165228103Smm nvpair_t *pair; 1166228103Smm for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL); 1167228103Smm pair != NULL; 1168228103Smm pair = nvlist_next_nvpair(cb->cb_nvl, pair)) { 1169228103Smm zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair), 1170228103Smm ZFS_TYPE_SNAPSHOT); 1171228103Smm if (zhp != NULL) { 1172228103Smm boolean_t defer = cb->cb_defer_destroy; 1173231144Smm int err = 0; 1174228103Smm 1175228103Smm /* 1176228103Smm * We can't defer destroy non-snapshots, so set it to 1177228103Smm * false while destroying the clones. 1178228103Smm */ 1179228103Smm cb->cb_defer_destroy = B_FALSE; 1180228103Smm err = zfs_iter_dependents(zhp, B_FALSE, 1181228103Smm destroy_callback, cb); 1182228103Smm cb->cb_defer_destroy = defer; 1183228103Smm zfs_close(zhp); 1184228103Smm if (err != 0) 1185228103Smm return (err); 1186228103Smm } 1187228103Smm } 1188228103Smm return (0); 1189228103Smm} 1190228103Smm 1191228103Smmstatic int 1192168404Spjdzfs_do_destroy(int argc, char **argv) 1193168404Spjd{ 1194168404Spjd destroy_cbdata_t cb = { 0 }; 1195248571Smm int rv = 0; 1196248571Smm int err = 0; 1197168404Spjd int c; 1198248571Smm zfs_handle_t *zhp = NULL; 1199228103Smm char *at; 1200219089Spjd zfs_type_t type = ZFS_TYPE_DATASET; 1201168404Spjd 1202168404Spjd /* check options */ 1203228103Smm while ((c = getopt(argc, argv, "vpndfrR")) != -1) { 1204168404Spjd switch (c) { 1205228103Smm case 'v': 1206228103Smm cb.cb_verbose = B_TRUE; 1207228103Smm break; 1208228103Smm case 'p': 1209228103Smm cb.cb_verbose = B_TRUE; 1210228103Smm cb.cb_parsable = B_TRUE; 1211228103Smm break; 1212228103Smm case 'n': 1213228103Smm cb.cb_dryrun = B_TRUE; 1214228103Smm break; 1215219089Spjd case 'd': 1216219089Spjd cb.cb_defer_destroy = B_TRUE; 1217219089Spjd type = ZFS_TYPE_SNAPSHOT; 1218219089Spjd break; 1219168404Spjd case 'f': 1220228103Smm cb.cb_force = B_TRUE; 1221168404Spjd break; 1222168404Spjd case 'r': 1223228103Smm cb.cb_recurse = B_TRUE; 1224168404Spjd break; 1225168404Spjd case 'R': 1226228103Smm cb.cb_recurse = B_TRUE; 1227228103Smm cb.cb_doclones = B_TRUE; 1228168404Spjd break; 1229168404Spjd case '?': 1230168404Spjd default: 1231168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1232168404Spjd optopt); 1233168404Spjd usage(B_FALSE); 1234168404Spjd } 1235168404Spjd } 1236168404Spjd 1237168404Spjd argc -= optind; 1238168404Spjd argv += optind; 1239168404Spjd 1240168404Spjd /* check number of arguments */ 1241168404Spjd if (argc == 0) { 1242228103Smm (void) fprintf(stderr, gettext("missing dataset argument\n")); 1243168404Spjd usage(B_FALSE); 1244168404Spjd } 1245168404Spjd if (argc > 1) { 1246168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 1247168404Spjd usage(B_FALSE); 1248168404Spjd } 1249168404Spjd 1250228103Smm at = strchr(argv[0], '@'); 1251228103Smm if (at != NULL) { 1252168404Spjd 1253228103Smm /* Build the list of snaps to destroy in cb_nvl. */ 1254248571Smm cb.cb_nvl = fnvlist_alloc(); 1255228103Smm 1256228103Smm *at = '\0'; 1257228103Smm zhp = zfs_open(g_zfs, argv[0], 1258228103Smm ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 1259228103Smm if (zhp == NULL) 1260168404Spjd return (1); 1261168404Spjd 1262228103Smm cb.cb_snapspec = at + 1; 1263228103Smm if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 || 1264228103Smm cb.cb_error) { 1265248571Smm rv = 1; 1266248571Smm goto out; 1267228103Smm } 1268219089Spjd 1269228103Smm if (nvlist_empty(cb.cb_nvl)) { 1270228103Smm (void) fprintf(stderr, gettext("could not find any " 1271228103Smm "snapshots to destroy; check snapshot names.\n")); 1272248571Smm rv = 1; 1273248571Smm goto out; 1274228103Smm } 1275228103Smm 1276228103Smm if (cb.cb_verbose) { 1277228103Smm char buf[16]; 1278228103Smm zfs_nicenum(cb.cb_snapused, buf, sizeof (buf)); 1279228103Smm if (cb.cb_parsable) { 1280228103Smm (void) printf("reclaim\t%llu\n", 1281228103Smm cb.cb_snapused); 1282228103Smm } else if (cb.cb_dryrun) { 1283228103Smm (void) printf(gettext("would reclaim %s\n"), 1284228103Smm buf); 1285228103Smm } else { 1286228103Smm (void) printf(gettext("will reclaim %s\n"), 1287228103Smm buf); 1288168404Spjd } 1289168404Spjd } 1290168404Spjd 1291228103Smm if (!cb.cb_dryrun) { 1292248571Smm if (cb.cb_doclones) { 1293248571Smm cb.cb_batchedsnaps = fnvlist_alloc(); 1294228103Smm err = destroy_clones(&cb); 1295248571Smm if (err == 0) { 1296248571Smm err = zfs_destroy_snaps_nvl(g_zfs, 1297248571Smm cb.cb_batchedsnaps, B_FALSE); 1298248571Smm } 1299248571Smm if (err != 0) { 1300248571Smm rv = 1; 1301248571Smm goto out; 1302248571Smm } 1303248571Smm } 1304228103Smm if (err == 0) { 1305248571Smm err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl, 1306228103Smm cb.cb_defer_destroy); 1307228103Smm } 1308168404Spjd } 1309168404Spjd 1310228103Smm if (err != 0) 1311248571Smm rv = 1; 1312228103Smm } else { 1313228103Smm /* Open the given dataset */ 1314228103Smm if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL) 1315228103Smm return (1); 1316168404Spjd 1317228103Smm cb.cb_target = zhp; 1318168404Spjd 1319228103Smm /* 1320228103Smm * Perform an explicit check for pools before going any further. 1321228103Smm */ 1322228103Smm if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 1323228103Smm zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 1324228103Smm (void) fprintf(stderr, gettext("cannot destroy '%s': " 1325228103Smm "operation does not apply to pools\n"), 1326228103Smm zfs_get_name(zhp)); 1327228103Smm (void) fprintf(stderr, gettext("use 'zfs destroy -r " 1328228103Smm "%s' to destroy all datasets in the pool\n"), 1329228103Smm zfs_get_name(zhp)); 1330228103Smm (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 1331228103Smm "to destroy the pool itself\n"), zfs_get_name(zhp)); 1332248571Smm rv = 1; 1333248571Smm goto out; 1334228103Smm } 1335168404Spjd 1336228103Smm /* 1337228103Smm * Check for any dependents and/or clones. 1338228103Smm */ 1339228103Smm cb.cb_first = B_TRUE; 1340228103Smm if (!cb.cb_doclones && 1341228103Smm zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent, 1342228103Smm &cb) != 0) { 1343248571Smm rv = 1; 1344248571Smm goto out; 1345228103Smm } 1346168404Spjd 1347228103Smm if (cb.cb_error) { 1348248571Smm rv = 1; 1349248571Smm goto out; 1350228103Smm } 1351168404Spjd 1352248571Smm cb.cb_batchedsnaps = fnvlist_alloc(); 1353228103Smm if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback, 1354228103Smm &cb) != 0) { 1355248571Smm rv = 1; 1356248571Smm goto out; 1357228103Smm } 1358185029Spjd 1359228103Smm /* 1360228103Smm * Do the real thing. The callback will close the 1361228103Smm * handle regardless of whether it succeeds or not. 1362228103Smm */ 1363248571Smm err = destroy_callback(zhp, &cb); 1364248571Smm zhp = NULL; 1365248571Smm if (err == 0) { 1366248571Smm err = zfs_destroy_snaps_nvl(g_zfs, 1367248571Smm cb.cb_batchedsnaps, cb.cb_defer_destroy); 1368248571Smm } 1369248571Smm if (err != 0) 1370248571Smm rv = 1; 1371228103Smm } 1372168404Spjd 1373248571Smmout: 1374248571Smm fnvlist_free(cb.cb_batchedsnaps); 1375248571Smm fnvlist_free(cb.cb_nvl); 1376248571Smm if (zhp != NULL) 1377248571Smm zfs_close(zhp); 1378248571Smm return (rv); 1379168404Spjd} 1380168404Spjd 1381219089Spjdstatic boolean_t 1382219089Spjdis_recvd_column(zprop_get_cbdata_t *cbp) 1383219089Spjd{ 1384219089Spjd int i; 1385219089Spjd zfs_get_column_t col; 1386219089Spjd 1387219089Spjd for (i = 0; i < ZFS_GET_NCOLS && 1388219089Spjd (col = cbp->cb_columns[i]) != GET_COL_NONE; i++) 1389219089Spjd if (col == GET_COL_RECVD) 1390219089Spjd return (B_TRUE); 1391219089Spjd return (B_FALSE); 1392219089Spjd} 1393219089Spjd 1394168404Spjd/* 1395219089Spjd * zfs get [-rHp] [-o all | field[,field]...] [-s source[,source]...] 1396219089Spjd * < all | property[,property]... > < fs | snap | vol > ... 1397168404Spjd * 1398168404Spjd * -r recurse over any child datasets 1399168404Spjd * -H scripted mode. Headers are stripped, and fields are separated 1400168404Spjd * by tabs instead of spaces. 1401219089Spjd * -o Set of fields to display. One of "name,property,value, 1402219089Spjd * received,source". Default is "name,property,value,source". 1403219089Spjd * "all" is an alias for all five. 1404168404Spjd * -s Set of sources to allow. One of 1405219089Spjd * "local,default,inherited,received,temporary,none". Default is 1406219089Spjd * all six. 1407168404Spjd * -p Display values in parsable (literal) format. 1408168404Spjd * 1409168404Spjd * Prints properties for the given datasets. The user can control which 1410168404Spjd * columns to display as well as which property types to allow. 1411168404Spjd */ 1412168404Spjd 1413168404Spjd/* 1414168404Spjd * Invoked to display the properties for a single dataset. 1415168404Spjd */ 1416168404Spjdstatic int 1417168404Spjdget_callback(zfs_handle_t *zhp, void *data) 1418168404Spjd{ 1419168404Spjd char buf[ZFS_MAXPROPLEN]; 1420219089Spjd char rbuf[ZFS_MAXPROPLEN]; 1421185029Spjd zprop_source_t sourcetype; 1422168404Spjd char source[ZFS_MAXNAMELEN]; 1423185029Spjd zprop_get_cbdata_t *cbp = data; 1424219089Spjd nvlist_t *user_props = zfs_get_user_props(zhp); 1425185029Spjd zprop_list_t *pl = cbp->cb_proplist; 1426168404Spjd nvlist_t *propval; 1427168404Spjd char *strval; 1428168404Spjd char *sourceval; 1429219089Spjd boolean_t received = is_recvd_column(cbp); 1430168404Spjd 1431168404Spjd for (; pl != NULL; pl = pl->pl_next) { 1432219089Spjd char *recvdval = NULL; 1433168404Spjd /* 1434168404Spjd * Skip the special fake placeholder. This will also skip over 1435168404Spjd * the name property when 'all' is specified. 1436168404Spjd */ 1437168404Spjd if (pl->pl_prop == ZFS_PROP_NAME && 1438168404Spjd pl == cbp->cb_proplist) 1439168404Spjd continue; 1440168404Spjd 1441185029Spjd if (pl->pl_prop != ZPROP_INVAL) { 1442168404Spjd if (zfs_prop_get(zhp, pl->pl_prop, buf, 1443168404Spjd sizeof (buf), &sourcetype, source, 1444168404Spjd sizeof (source), 1445168404Spjd cbp->cb_literal) != 0) { 1446168404Spjd if (pl->pl_all) 1447168404Spjd continue; 1448168404Spjd if (!zfs_prop_valid_for_type(pl->pl_prop, 1449185029Spjd ZFS_TYPE_DATASET)) { 1450168404Spjd (void) fprintf(stderr, 1451168404Spjd gettext("No such property '%s'\n"), 1452168404Spjd zfs_prop_to_name(pl->pl_prop)); 1453168404Spjd continue; 1454168404Spjd } 1455185029Spjd sourcetype = ZPROP_SRC_NONE; 1456168404Spjd (void) strlcpy(buf, "-", sizeof (buf)); 1457168404Spjd } 1458168404Spjd 1459219089Spjd if (received && (zfs_prop_get_recvd(zhp, 1460219089Spjd zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf), 1461219089Spjd cbp->cb_literal) == 0)) 1462219089Spjd recvdval = rbuf; 1463219089Spjd 1464185029Spjd zprop_print_one_property(zfs_get_name(zhp), cbp, 1465168404Spjd zfs_prop_to_name(pl->pl_prop), 1466219089Spjd buf, sourcetype, source, recvdval); 1467209962Smm } else if (zfs_prop_userquota(pl->pl_user_prop)) { 1468209962Smm sourcetype = ZPROP_SRC_LOCAL; 1469209962Smm 1470209962Smm if (zfs_prop_get_userquota(zhp, pl->pl_user_prop, 1471209962Smm buf, sizeof (buf), cbp->cb_literal) != 0) { 1472209962Smm sourcetype = ZPROP_SRC_NONE; 1473209962Smm (void) strlcpy(buf, "-", sizeof (buf)); 1474209962Smm } 1475209962Smm 1476209962Smm zprop_print_one_property(zfs_get_name(zhp), cbp, 1477219089Spjd pl->pl_user_prop, buf, sourcetype, source, NULL); 1478228103Smm } else if (zfs_prop_written(pl->pl_user_prop)) { 1479228103Smm sourcetype = ZPROP_SRC_LOCAL; 1480228103Smm 1481228103Smm if (zfs_prop_get_written(zhp, pl->pl_user_prop, 1482228103Smm buf, sizeof (buf), cbp->cb_literal) != 0) { 1483228103Smm sourcetype = ZPROP_SRC_NONE; 1484228103Smm (void) strlcpy(buf, "-", sizeof (buf)); 1485228103Smm } 1486228103Smm 1487228103Smm zprop_print_one_property(zfs_get_name(zhp), cbp, 1488228103Smm pl->pl_user_prop, buf, sourcetype, source, NULL); 1489168404Spjd } else { 1490219089Spjd if (nvlist_lookup_nvlist(user_props, 1491168404Spjd pl->pl_user_prop, &propval) != 0) { 1492168404Spjd if (pl->pl_all) 1493168404Spjd continue; 1494185029Spjd sourcetype = ZPROP_SRC_NONE; 1495168404Spjd strval = "-"; 1496168404Spjd } else { 1497168404Spjd verify(nvlist_lookup_string(propval, 1498185029Spjd ZPROP_VALUE, &strval) == 0); 1499168404Spjd verify(nvlist_lookup_string(propval, 1500185029Spjd ZPROP_SOURCE, &sourceval) == 0); 1501168404Spjd 1502168404Spjd if (strcmp(sourceval, 1503168404Spjd zfs_get_name(zhp)) == 0) { 1504185029Spjd sourcetype = ZPROP_SRC_LOCAL; 1505219089Spjd } else if (strcmp(sourceval, 1506219089Spjd ZPROP_SOURCE_VAL_RECVD) == 0) { 1507219089Spjd sourcetype = ZPROP_SRC_RECEIVED; 1508168404Spjd } else { 1509185029Spjd sourcetype = ZPROP_SRC_INHERITED; 1510168404Spjd (void) strlcpy(source, 1511168404Spjd sourceval, sizeof (source)); 1512168404Spjd } 1513168404Spjd } 1514168404Spjd 1515219089Spjd if (received && (zfs_prop_get_recvd(zhp, 1516219089Spjd pl->pl_user_prop, rbuf, sizeof (rbuf), 1517219089Spjd cbp->cb_literal) == 0)) 1518219089Spjd recvdval = rbuf; 1519219089Spjd 1520185029Spjd zprop_print_one_property(zfs_get_name(zhp), cbp, 1521168404Spjd pl->pl_user_prop, strval, sourcetype, 1522219089Spjd source, recvdval); 1523168404Spjd } 1524168404Spjd } 1525168404Spjd 1526168404Spjd return (0); 1527168404Spjd} 1528168404Spjd 1529168404Spjdstatic int 1530168404Spjdzfs_do_get(int argc, char **argv) 1531168404Spjd{ 1532185029Spjd zprop_get_cbdata_t cb = { 0 }; 1533223620Smm int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS; 1534232064Smm int types = ZFS_TYPE_DATASET; 1535168404Spjd char *value, *fields; 1536231144Smm int ret = 0; 1537205199Sdelphij int limit = 0; 1538185029Spjd zprop_list_t fake_name = { 0 }; 1539168404Spjd 1540168404Spjd /* 1541168404Spjd * Set up default columns and sources. 1542168404Spjd */ 1543185029Spjd cb.cb_sources = ZPROP_SRC_ALL; 1544168404Spjd cb.cb_columns[0] = GET_COL_NAME; 1545168404Spjd cb.cb_columns[1] = GET_COL_PROPERTY; 1546168404Spjd cb.cb_columns[2] = GET_COL_VALUE; 1547168404Spjd cb.cb_columns[3] = GET_COL_SOURCE; 1548185029Spjd cb.cb_type = ZFS_TYPE_DATASET; 1549168404Spjd 1550168404Spjd /* check options */ 1551232064Smm while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) { 1552168404Spjd switch (c) { 1553168404Spjd case 'p': 1554168404Spjd cb.cb_literal = B_TRUE; 1555168404Spjd break; 1556205199Sdelphij case 'd': 1557205199Sdelphij limit = parse_depth(optarg, &flags); 1558205199Sdelphij break; 1559168404Spjd case 'r': 1560185029Spjd flags |= ZFS_ITER_RECURSE; 1561168404Spjd break; 1562168404Spjd case 'H': 1563168404Spjd cb.cb_scripted = B_TRUE; 1564168404Spjd break; 1565168404Spjd case ':': 1566168404Spjd (void) fprintf(stderr, gettext("missing argument for " 1567168404Spjd "'%c' option\n"), optopt); 1568168404Spjd usage(B_FALSE); 1569168404Spjd break; 1570168404Spjd case 'o': 1571168404Spjd /* 1572168404Spjd * Process the set of columns to display. We zero out 1573168404Spjd * the structure to give us a blank slate. 1574168404Spjd */ 1575168404Spjd bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 1576168404Spjd i = 0; 1577168404Spjd while (*optarg != '\0') { 1578168404Spjd static char *col_subopts[] = 1579219089Spjd { "name", "property", "value", "received", 1580219089Spjd "source", "all", NULL }; 1581168404Spjd 1582219089Spjd if (i == ZFS_GET_NCOLS) { 1583168404Spjd (void) fprintf(stderr, gettext("too " 1584168404Spjd "many fields given to -o " 1585168404Spjd "option\n")); 1586168404Spjd usage(B_FALSE); 1587168404Spjd } 1588168404Spjd 1589168404Spjd switch (getsubopt(&optarg, col_subopts, 1590168404Spjd &value)) { 1591168404Spjd case 0: 1592168404Spjd cb.cb_columns[i++] = GET_COL_NAME; 1593168404Spjd break; 1594168404Spjd case 1: 1595168404Spjd cb.cb_columns[i++] = GET_COL_PROPERTY; 1596168404Spjd break; 1597168404Spjd case 2: 1598168404Spjd cb.cb_columns[i++] = GET_COL_VALUE; 1599168404Spjd break; 1600168404Spjd case 3: 1601219089Spjd cb.cb_columns[i++] = GET_COL_RECVD; 1602219089Spjd flags |= ZFS_ITER_RECVD_PROPS; 1603219089Spjd break; 1604219089Spjd case 4: 1605168404Spjd cb.cb_columns[i++] = GET_COL_SOURCE; 1606168404Spjd break; 1607219089Spjd case 5: 1608219089Spjd if (i > 0) { 1609219089Spjd (void) fprintf(stderr, 1610219089Spjd gettext("\"all\" conflicts " 1611219089Spjd "with specific fields " 1612219089Spjd "given to -o option\n")); 1613219089Spjd usage(B_FALSE); 1614219089Spjd } 1615219089Spjd cb.cb_columns[0] = GET_COL_NAME; 1616219089Spjd cb.cb_columns[1] = GET_COL_PROPERTY; 1617219089Spjd cb.cb_columns[2] = GET_COL_VALUE; 1618219089Spjd cb.cb_columns[3] = GET_COL_RECVD; 1619219089Spjd cb.cb_columns[4] = GET_COL_SOURCE; 1620219089Spjd flags |= ZFS_ITER_RECVD_PROPS; 1621219089Spjd i = ZFS_GET_NCOLS; 1622219089Spjd break; 1623168404Spjd default: 1624168404Spjd (void) fprintf(stderr, 1625168404Spjd gettext("invalid column name " 1626168404Spjd "'%s'\n"), value); 1627168404Spjd usage(B_FALSE); 1628168404Spjd } 1629168404Spjd } 1630168404Spjd break; 1631168404Spjd 1632168404Spjd case 's': 1633168404Spjd cb.cb_sources = 0; 1634168404Spjd while (*optarg != '\0') { 1635168404Spjd static char *source_subopts[] = { 1636168404Spjd "local", "default", "inherited", 1637219089Spjd "received", "temporary", "none", 1638219089Spjd NULL }; 1639168404Spjd 1640168404Spjd switch (getsubopt(&optarg, source_subopts, 1641168404Spjd &value)) { 1642168404Spjd case 0: 1643185029Spjd cb.cb_sources |= ZPROP_SRC_LOCAL; 1644168404Spjd break; 1645168404Spjd case 1: 1646185029Spjd cb.cb_sources |= ZPROP_SRC_DEFAULT; 1647168404Spjd break; 1648168404Spjd case 2: 1649185029Spjd cb.cb_sources |= ZPROP_SRC_INHERITED; 1650168404Spjd break; 1651168404Spjd case 3: 1652219089Spjd cb.cb_sources |= ZPROP_SRC_RECEIVED; 1653219089Spjd break; 1654219089Spjd case 4: 1655185029Spjd cb.cb_sources |= ZPROP_SRC_TEMPORARY; 1656168404Spjd break; 1657219089Spjd case 5: 1658185029Spjd cb.cb_sources |= ZPROP_SRC_NONE; 1659168404Spjd break; 1660168404Spjd default: 1661168404Spjd (void) fprintf(stderr, 1662168404Spjd gettext("invalid source " 1663168404Spjd "'%s'\n"), value); 1664168404Spjd usage(B_FALSE); 1665168404Spjd } 1666168404Spjd } 1667168404Spjd break; 1668168404Spjd 1669232064Smm case 't': 1670232064Smm types = 0; 1671232064Smm flags &= ~ZFS_ITER_PROP_LISTSNAPS; 1672232064Smm while (*optarg != '\0') { 1673232064Smm static char *type_subopts[] = { "filesystem", 1674232064Smm "volume", "snapshot", "all", NULL }; 1675232064Smm 1676232064Smm switch (getsubopt(&optarg, type_subopts, 1677232064Smm &value)) { 1678232064Smm case 0: 1679232064Smm types |= ZFS_TYPE_FILESYSTEM; 1680232064Smm break; 1681232064Smm case 1: 1682232064Smm types |= ZFS_TYPE_VOLUME; 1683232064Smm break; 1684232064Smm case 2: 1685232064Smm types |= ZFS_TYPE_SNAPSHOT; 1686232064Smm break; 1687232064Smm case 3: 1688232064Smm types = ZFS_TYPE_DATASET; 1689232064Smm break; 1690232064Smm 1691232064Smm default: 1692232064Smm (void) fprintf(stderr, 1693232064Smm gettext("invalid type '%s'\n"), 1694232064Smm value); 1695232064Smm usage(B_FALSE); 1696232064Smm } 1697232064Smm } 1698232064Smm break; 1699232064Smm 1700168404Spjd case '?': 1701168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1702168404Spjd optopt); 1703168404Spjd usage(B_FALSE); 1704168404Spjd } 1705168404Spjd } 1706168404Spjd 1707168404Spjd argc -= optind; 1708168404Spjd argv += optind; 1709168404Spjd 1710168404Spjd if (argc < 1) { 1711168404Spjd (void) fprintf(stderr, gettext("missing property " 1712168404Spjd "argument\n")); 1713168404Spjd usage(B_FALSE); 1714168404Spjd } 1715168404Spjd 1716168404Spjd fields = argv[0]; 1717168404Spjd 1718185029Spjd if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET) 1719185029Spjd != 0) 1720168404Spjd usage(B_FALSE); 1721168404Spjd 1722168404Spjd argc--; 1723168404Spjd argv++; 1724168404Spjd 1725168404Spjd /* 1726168404Spjd * As part of zfs_expand_proplist(), we keep track of the maximum column 1727168404Spjd * width for each property. For the 'NAME' (and 'SOURCE') columns, we 1728168404Spjd * need to know the maximum name length. However, the user likely did 1729168404Spjd * not specify 'name' as one of the properties to fetch, so we need to 1730168404Spjd * make sure we always include at least this property for 1731168404Spjd * print_get_headers() to work properly. 1732168404Spjd */ 1733168404Spjd if (cb.cb_proplist != NULL) { 1734168404Spjd fake_name.pl_prop = ZFS_PROP_NAME; 1735168404Spjd fake_name.pl_width = strlen(gettext("NAME")); 1736168404Spjd fake_name.pl_next = cb.cb_proplist; 1737168404Spjd cb.cb_proplist = &fake_name; 1738168404Spjd } 1739168404Spjd 1740168404Spjd cb.cb_first = B_TRUE; 1741168404Spjd 1742168404Spjd /* run for each object */ 1743232064Smm ret = zfs_for_each(argc, argv, flags, types, NULL, 1744205199Sdelphij &cb.cb_proplist, limit, get_callback, &cb); 1745168404Spjd 1746168404Spjd if (cb.cb_proplist == &fake_name) 1747185029Spjd zprop_free_list(fake_name.pl_next); 1748168404Spjd else 1749185029Spjd zprop_free_list(cb.cb_proplist); 1750168404Spjd 1751168404Spjd return (ret); 1752168404Spjd} 1753168404Spjd 1754168404Spjd/* 1755219089Spjd * inherit [-rS] <property> <fs|vol> ... 1756168404Spjd * 1757219089Spjd * -r Recurse over all children 1758219089Spjd * -S Revert to received value, if any 1759168404Spjd * 1760168404Spjd * For each dataset specified on the command line, inherit the given property 1761168404Spjd * from its parent. Inheriting a property at the pool level will cause it to 1762168404Spjd * use the default value. The '-r' flag will recurse over all children, and is 1763168404Spjd * useful for setting a property on a hierarchy-wide basis, regardless of any 1764168404Spjd * local modifications for each dataset. 1765168404Spjd */ 1766168404Spjd 1767219089Spjdtypedef struct inherit_cbdata { 1768219089Spjd const char *cb_propname; 1769219089Spjd boolean_t cb_received; 1770219089Spjd} inherit_cbdata_t; 1771219089Spjd 1772168404Spjdstatic int 1773185029Spjdinherit_recurse_cb(zfs_handle_t *zhp, void *data) 1774168404Spjd{ 1775219089Spjd inherit_cbdata_t *cb = data; 1776219089Spjd zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname); 1777168404Spjd 1778185029Spjd /* 1779185029Spjd * If we're doing it recursively, then ignore properties that 1780185029Spjd * are not valid for this type of dataset. 1781185029Spjd */ 1782185029Spjd if (prop != ZPROP_INVAL && 1783185029Spjd !zfs_prop_valid_for_type(prop, zfs_get_type(zhp))) 1784185029Spjd return (0); 1785185029Spjd 1786219089Spjd return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0); 1787168404Spjd} 1788168404Spjd 1789168404Spjdstatic int 1790185029Spjdinherit_cb(zfs_handle_t *zhp, void *data) 1791185029Spjd{ 1792219089Spjd inherit_cbdata_t *cb = data; 1793185029Spjd 1794219089Spjd return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0); 1795185029Spjd} 1796185029Spjd 1797185029Spjdstatic int 1798168404Spjdzfs_do_inherit(int argc, char **argv) 1799168404Spjd{ 1800168404Spjd int c; 1801168404Spjd zfs_prop_t prop; 1802219089Spjd inherit_cbdata_t cb = { 0 }; 1803185029Spjd char *propname; 1804231144Smm int ret = 0; 1805185029Spjd int flags = 0; 1806219089Spjd boolean_t received = B_FALSE; 1807168404Spjd 1808168404Spjd /* check options */ 1809219089Spjd while ((c = getopt(argc, argv, "rS")) != -1) { 1810168404Spjd switch (c) { 1811168404Spjd case 'r': 1812185029Spjd flags |= ZFS_ITER_RECURSE; 1813168404Spjd break; 1814219089Spjd case 'S': 1815219089Spjd received = B_TRUE; 1816219089Spjd break; 1817168404Spjd case '?': 1818168404Spjd default: 1819168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1820168404Spjd optopt); 1821168404Spjd usage(B_FALSE); 1822168404Spjd } 1823168404Spjd } 1824168404Spjd 1825168404Spjd argc -= optind; 1826168404Spjd argv += optind; 1827168404Spjd 1828168404Spjd /* check number of arguments */ 1829168404Spjd if (argc < 1) { 1830168404Spjd (void) fprintf(stderr, gettext("missing property argument\n")); 1831168404Spjd usage(B_FALSE); 1832168404Spjd } 1833168404Spjd if (argc < 2) { 1834168404Spjd (void) fprintf(stderr, gettext("missing dataset argument\n")); 1835168404Spjd usage(B_FALSE); 1836168404Spjd } 1837168404Spjd 1838185029Spjd propname = argv[0]; 1839168404Spjd argc--; 1840168404Spjd argv++; 1841168404Spjd 1842185029Spjd if ((prop = zfs_name_to_prop(propname)) != ZPROP_INVAL) { 1843168404Spjd if (zfs_prop_readonly(prop)) { 1844168404Spjd (void) fprintf(stderr, gettext( 1845168404Spjd "%s property is read-only\n"), 1846185029Spjd propname); 1847168404Spjd return (1); 1848168404Spjd } 1849219089Spjd if (!zfs_prop_inheritable(prop) && !received) { 1850168404Spjd (void) fprintf(stderr, gettext("'%s' property cannot " 1851185029Spjd "be inherited\n"), propname); 1852168404Spjd if (prop == ZFS_PROP_QUOTA || 1853185029Spjd prop == ZFS_PROP_RESERVATION || 1854185029Spjd prop == ZFS_PROP_REFQUOTA || 1855185029Spjd prop == ZFS_PROP_REFRESERVATION) 1856168404Spjd (void) fprintf(stderr, gettext("use 'zfs set " 1857185029Spjd "%s=none' to clear\n"), propname); 1858168404Spjd return (1); 1859168404Spjd } 1860219089Spjd if (received && (prop == ZFS_PROP_VOLSIZE || 1861219089Spjd prop == ZFS_PROP_VERSION)) { 1862219089Spjd (void) fprintf(stderr, gettext("'%s' property cannot " 1863219089Spjd "be reverted to a received value\n"), propname); 1864219089Spjd return (1); 1865219089Spjd } 1866185029Spjd } else if (!zfs_prop_user(propname)) { 1867185029Spjd (void) fprintf(stderr, gettext("invalid property '%s'\n"), 1868185029Spjd propname); 1869168404Spjd usage(B_FALSE); 1870168404Spjd } 1871168404Spjd 1872219089Spjd cb.cb_propname = propname; 1873219089Spjd cb.cb_received = received; 1874219089Spjd 1875185029Spjd if (flags & ZFS_ITER_RECURSE) { 1876185029Spjd ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET, 1877219089Spjd NULL, NULL, 0, inherit_recurse_cb, &cb); 1878185029Spjd } else { 1879185029Spjd ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET, 1880219089Spjd NULL, NULL, 0, inherit_cb, &cb); 1881185029Spjd } 1882168404Spjd 1883185029Spjd return (ret); 1884185029Spjd} 1885168404Spjd 1886185029Spjdtypedef struct upgrade_cbdata { 1887185029Spjd uint64_t cb_numupgraded; 1888185029Spjd uint64_t cb_numsamegraded; 1889185029Spjd uint64_t cb_numfailed; 1890185029Spjd uint64_t cb_version; 1891185029Spjd boolean_t cb_newer; 1892185029Spjd boolean_t cb_foundone; 1893185029Spjd char cb_lastfs[ZFS_MAXNAMELEN]; 1894185029Spjd} upgrade_cbdata_t; 1895185029Spjd 1896185029Spjdstatic int 1897185029Spjdsame_pool(zfs_handle_t *zhp, const char *name) 1898185029Spjd{ 1899185029Spjd int len1 = strcspn(name, "/@"); 1900185029Spjd const char *zhname = zfs_get_name(zhp); 1901185029Spjd int len2 = strcspn(zhname, "/@"); 1902185029Spjd 1903185029Spjd if (len1 != len2) 1904185029Spjd return (B_FALSE); 1905185029Spjd return (strncmp(name, zhname, len1) == 0); 1906185029Spjd} 1907185029Spjd 1908185029Spjdstatic int 1909185029Spjdupgrade_list_callback(zfs_handle_t *zhp, void *data) 1910185029Spjd{ 1911185029Spjd upgrade_cbdata_t *cb = data; 1912185029Spjd int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 1913185029Spjd 1914185029Spjd /* list if it's old/new */ 1915185029Spjd if ((!cb->cb_newer && version < ZPL_VERSION) || 1916185029Spjd (cb->cb_newer && version > ZPL_VERSION)) { 1917185029Spjd char *str; 1918185029Spjd if (cb->cb_newer) { 1919185029Spjd str = gettext("The following filesystems are " 1920185029Spjd "formatted using a newer software version and\n" 1921185029Spjd "cannot be accessed on the current system.\n\n"); 1922185029Spjd } else { 1923185029Spjd str = gettext("The following filesystems are " 1924185029Spjd "out of date, and can be upgraded. After being\n" 1925185029Spjd "upgraded, these filesystems (and any 'zfs send' " 1926185029Spjd "streams generated from\n" 1927185029Spjd "subsequent snapshots) will no longer be " 1928185029Spjd "accessible by older software versions.\n\n"); 1929185029Spjd } 1930185029Spjd 1931185029Spjd if (!cb->cb_foundone) { 1932185029Spjd (void) puts(str); 1933185029Spjd (void) printf(gettext("VER FILESYSTEM\n")); 1934185029Spjd (void) printf(gettext("--- ------------\n")); 1935185029Spjd cb->cb_foundone = B_TRUE; 1936185029Spjd } 1937185029Spjd 1938185029Spjd (void) printf("%2u %s\n", version, zfs_get_name(zhp)); 1939168404Spjd } 1940168404Spjd 1941185029Spjd return (0); 1942185029Spjd} 1943185029Spjd 1944185029Spjdstatic int 1945185029Spjdupgrade_set_callback(zfs_handle_t *zhp, void *data) 1946185029Spjd{ 1947185029Spjd upgrade_cbdata_t *cb = data; 1948185029Spjd int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 1949219089Spjd int needed_spa_version; 1950219089Spjd int spa_version; 1951185029Spjd 1952219089Spjd if (zfs_spa_version(zhp, &spa_version) < 0) 1953219089Spjd return (-1); 1954185029Spjd 1955219089Spjd needed_spa_version = zfs_spa_version_map(cb->cb_version); 1956185029Spjd 1957219089Spjd if (needed_spa_version < 0) 1958219089Spjd return (-1); 1959209962Smm 1960219089Spjd if (spa_version < needed_spa_version) { 1961219089Spjd /* can't upgrade */ 1962219089Spjd (void) printf(gettext("%s: can not be " 1963219089Spjd "upgraded; the pool version needs to first " 1964219089Spjd "be upgraded\nto version %d\n\n"), 1965219089Spjd zfs_get_name(zhp), needed_spa_version); 1966219089Spjd cb->cb_numfailed++; 1967219089Spjd return (0); 1968185029Spjd } 1969185029Spjd 1970185029Spjd /* upgrade */ 1971185029Spjd if (version < cb->cb_version) { 1972185029Spjd char verstr[16]; 1973185029Spjd (void) snprintf(verstr, sizeof (verstr), 1974185029Spjd "%llu", cb->cb_version); 1975185029Spjd if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) { 1976185029Spjd /* 1977185029Spjd * If they did "zfs upgrade -a", then we could 1978185029Spjd * be doing ioctls to different pools. We need 1979248571Smm * to log this history once to each pool, and bypass 1980248571Smm * the normal history logging that happens in main(). 1981185029Spjd */ 1982248571Smm (void) zpool_log_history(g_zfs, history_str); 1983248571Smm log_history = B_FALSE; 1984185029Spjd } 1985185029Spjd if (zfs_prop_set(zhp, "version", verstr) == 0) 1986185029Spjd cb->cb_numupgraded++; 1987185029Spjd else 1988185029Spjd cb->cb_numfailed++; 1989185029Spjd (void) strcpy(cb->cb_lastfs, zfs_get_name(zhp)); 1990185029Spjd } else if (version > cb->cb_version) { 1991185029Spjd /* can't downgrade */ 1992185029Spjd (void) printf(gettext("%s: can not be downgraded; " 1993185029Spjd "it is already at version %u\n"), 1994185029Spjd zfs_get_name(zhp), version); 1995185029Spjd cb->cb_numfailed++; 1996185029Spjd } else { 1997185029Spjd cb->cb_numsamegraded++; 1998185029Spjd } 1999185029Spjd return (0); 2000185029Spjd} 2001185029Spjd 2002185029Spjd/* 2003185029Spjd * zfs upgrade 2004185029Spjd * zfs upgrade -v 2005185029Spjd * zfs upgrade [-r] [-V <version>] <-a | filesystem> 2006185029Spjd */ 2007185029Spjdstatic int 2008185029Spjdzfs_do_upgrade(int argc, char **argv) 2009185029Spjd{ 2010185029Spjd boolean_t all = B_FALSE; 2011185029Spjd boolean_t showversions = B_FALSE; 2012231144Smm int ret = 0; 2013185029Spjd upgrade_cbdata_t cb = { 0 }; 2014185029Spjd char c; 2015185029Spjd int flags = ZFS_ITER_ARGS_CAN_BE_PATHS; 2016185029Spjd 2017185029Spjd /* check options */ 2018185029Spjd while ((c = getopt(argc, argv, "rvV:a")) != -1) { 2019185029Spjd switch (c) { 2020185029Spjd case 'r': 2021185029Spjd flags |= ZFS_ITER_RECURSE; 2022185029Spjd break; 2023185029Spjd case 'v': 2024185029Spjd showversions = B_TRUE; 2025185029Spjd break; 2026185029Spjd case 'V': 2027185029Spjd if (zfs_prop_string_to_index(ZFS_PROP_VERSION, 2028185029Spjd optarg, &cb.cb_version) != 0) { 2029185029Spjd (void) fprintf(stderr, 2030185029Spjd gettext("invalid version %s\n"), optarg); 2031185029Spjd usage(B_FALSE); 2032185029Spjd } 2033185029Spjd break; 2034185029Spjd case 'a': 2035185029Spjd all = B_TRUE; 2036185029Spjd break; 2037185029Spjd case '?': 2038185029Spjd default: 2039185029Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2040185029Spjd optopt); 2041185029Spjd usage(B_FALSE); 2042185029Spjd } 2043185029Spjd } 2044185029Spjd 2045185029Spjd argc -= optind; 2046185029Spjd argv += optind; 2047185029Spjd 2048185029Spjd if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version)) 2049185029Spjd usage(B_FALSE); 2050185029Spjd if (showversions && (flags & ZFS_ITER_RECURSE || all || 2051185029Spjd cb.cb_version || argc)) 2052185029Spjd usage(B_FALSE); 2053185029Spjd if ((all || argc) && (showversions)) 2054185029Spjd usage(B_FALSE); 2055185029Spjd if (all && argc) 2056185029Spjd usage(B_FALSE); 2057185029Spjd 2058185029Spjd if (showversions) { 2059185029Spjd /* Show info on available versions. */ 2060185029Spjd (void) printf(gettext("The following filesystem versions are " 2061185029Spjd "supported:\n\n")); 2062185029Spjd (void) printf(gettext("VER DESCRIPTION\n")); 2063185029Spjd (void) printf("--- -----------------------------------------" 2064185029Spjd "---------------\n"); 2065185029Spjd (void) printf(gettext(" 1 Initial ZFS filesystem version\n")); 2066185029Spjd (void) printf(gettext(" 2 Enhanced directory entries\n")); 2067228103Smm (void) printf(gettext(" 3 Case insensitive and filesystem " 2068228103Smm "user identifier (FUID)\n")); 2069209962Smm (void) printf(gettext(" 4 userquota, groupquota " 2070209962Smm "properties\n")); 2071219089Spjd (void) printf(gettext(" 5 System attributes\n")); 2072185029Spjd (void) printf(gettext("\nFor more information on a particular " 2073219089Spjd "version, including supported releases,\n")); 2074219089Spjd (void) printf("see the ZFS Administration Guide.\n\n"); 2075185029Spjd ret = 0; 2076185029Spjd } else if (argc || all) { 2077185029Spjd /* Upgrade filesystems */ 2078185029Spjd if (cb.cb_version == 0) 2079185029Spjd cb.cb_version = ZPL_VERSION; 2080185029Spjd ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM, 2081205199Sdelphij NULL, NULL, 0, upgrade_set_callback, &cb); 2082185029Spjd (void) printf(gettext("%llu filesystems upgraded\n"), 2083185029Spjd cb.cb_numupgraded); 2084185029Spjd if (cb.cb_numsamegraded) { 2085185029Spjd (void) printf(gettext("%llu filesystems already at " 2086185029Spjd "this version\n"), 2087185029Spjd cb.cb_numsamegraded); 2088185029Spjd } 2089185029Spjd if (cb.cb_numfailed != 0) 2090185029Spjd ret = 1; 2091185029Spjd } else { 2092185029Spjd /* List old-version filesytems */ 2093185029Spjd boolean_t found; 2094185029Spjd (void) printf(gettext("This system is currently running " 2095185029Spjd "ZFS filesystem version %llu.\n\n"), ZPL_VERSION); 2096185029Spjd 2097185029Spjd flags |= ZFS_ITER_RECURSE; 2098185029Spjd ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM, 2099205199Sdelphij NULL, NULL, 0, upgrade_list_callback, &cb); 2100185029Spjd 2101185029Spjd found = cb.cb_foundone; 2102185029Spjd cb.cb_foundone = B_FALSE; 2103185029Spjd cb.cb_newer = B_TRUE; 2104185029Spjd 2105185029Spjd ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM, 2106205199Sdelphij NULL, NULL, 0, upgrade_list_callback, &cb); 2107185029Spjd 2108185029Spjd if (!cb.cb_foundone && !found) { 2109185029Spjd (void) printf(gettext("All filesystems are " 2110185029Spjd "formatted with the current version.\n")); 2111185029Spjd } 2112185029Spjd } 2113185029Spjd 2114168404Spjd return (ret); 2115168404Spjd} 2116168404Spjd 2117240415Smm/* 2118240415Smm * zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...] 2119240415Smm * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot 2120240415Smm * zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...] 2121240415Smm * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot 2122240415Smm * 2123240415Smm * -H Scripted mode; elide headers and separate columns by tabs. 2124240415Smm * -i Translate SID to POSIX ID. 2125240415Smm * -n Print numeric ID instead of user/group name. 2126240415Smm * -o Control which fields to display. 2127240415Smm * -p Use exact (parseable) numeric output. 2128240415Smm * -s Specify sort columns, descending order. 2129240415Smm * -S Specify sort columns, ascending order. 2130240415Smm * -t Control which object types to display. 2131240415Smm * 2132240415Smm * Displays space consumed by, and quotas on, each user in the specified 2133240415Smm * filesystem or snapshot. 2134240415Smm */ 2135219089Spjd 2136240415Smm/* us_field_types, us_field_hdr and us_field_names should be kept in sync */ 2137240415Smmenum us_field_types { 2138240415Smm USFIELD_TYPE, 2139240415Smm USFIELD_NAME, 2140240415Smm USFIELD_USED, 2141240415Smm USFIELD_QUOTA 2142240415Smm}; 2143240415Smmstatic char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA" }; 2144240415Smmstatic char *us_field_names[] = { "type", "name", "used", "quota" }; 2145240415Smm#define USFIELD_LAST (sizeof (us_field_names) / sizeof (char *)) 2146219089Spjd 2147240415Smm#define USTYPE_PSX_GRP (1 << 0) 2148240415Smm#define USTYPE_PSX_USR (1 << 1) 2149240415Smm#define USTYPE_SMB_GRP (1 << 2) 2150240415Smm#define USTYPE_SMB_USR (1 << 3) 2151240415Smm#define USTYPE_ALL \ 2152240415Smm (USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR) 2153219089Spjd 2154240415Smmstatic int us_type_bits[] = { 2155240415Smm USTYPE_PSX_GRP, 2156240415Smm USTYPE_PSX_USR, 2157240415Smm USTYPE_SMB_GRP, 2158240415Smm USTYPE_SMB_USR, 2159240415Smm USTYPE_ALL 2160240415Smm}; 2161240415Smmstatic char *us_type_names[] = { "posixgroup", "posxiuser", "smbgroup", 2162240415Smm "smbuser", "all" }; 2163219089Spjd 2164219089Spjdtypedef struct us_node { 2165219089Spjd nvlist_t *usn_nvl; 2166219089Spjd uu_avl_node_t usn_avlnode; 2167219089Spjd uu_list_node_t usn_listnode; 2168219089Spjd} us_node_t; 2169219089Spjd 2170219089Spjdtypedef struct us_cbdata { 2171240415Smm nvlist_t **cb_nvlp; 2172240415Smm uu_avl_pool_t *cb_avl_pool; 2173240415Smm uu_avl_t *cb_avl; 2174240415Smm boolean_t cb_numname; 2175240415Smm boolean_t cb_nicenum; 2176240415Smm boolean_t cb_sid2posix; 2177240415Smm zfs_userquota_prop_t cb_prop; 2178240415Smm zfs_sort_column_t *cb_sortcol; 2179240415Smm size_t cb_width[USFIELD_LAST]; 2180219089Spjd} us_cbdata_t; 2181219089Spjd 2182240415Smmstatic boolean_t us_populated = B_FALSE; 2183240415Smm 2184219089Spjdtypedef struct { 2185219089Spjd zfs_sort_column_t *si_sortcol; 2186240415Smm boolean_t si_numname; 2187219089Spjd} us_sort_info_t; 2188219089Spjd 2189219089Spjdstatic int 2190240415Smmus_field_index(char *field) 2191240415Smm{ 2192240415Smm int i; 2193240415Smm 2194240415Smm for (i = 0; i < USFIELD_LAST; i++) { 2195240415Smm if (strcmp(field, us_field_names[i]) == 0) 2196240415Smm return (i); 2197240415Smm } 2198240415Smm 2199240415Smm return (-1); 2200240415Smm} 2201240415Smm 2202240415Smmstatic int 2203219089Spjdus_compare(const void *larg, const void *rarg, void *unused) 2204219089Spjd{ 2205219089Spjd const us_node_t *l = larg; 2206219089Spjd const us_node_t *r = rarg; 2207219089Spjd us_sort_info_t *si = (us_sort_info_t *)unused; 2208219089Spjd zfs_sort_column_t *sortcol = si->si_sortcol; 2209240415Smm boolean_t numname = si->si_numname; 2210219089Spjd nvlist_t *lnvl = l->usn_nvl; 2211219089Spjd nvlist_t *rnvl = r->usn_nvl; 2212240415Smm int rc = 0; 2213240415Smm boolean_t lvb, rvb; 2214219089Spjd 2215219089Spjd for (; sortcol != NULL; sortcol = sortcol->sc_next) { 2216219089Spjd char *lvstr = ""; 2217219089Spjd char *rvstr = ""; 2218219089Spjd uint32_t lv32 = 0; 2219219089Spjd uint32_t rv32 = 0; 2220219089Spjd uint64_t lv64 = 0; 2221219089Spjd uint64_t rv64 = 0; 2222219089Spjd zfs_prop_t prop = sortcol->sc_prop; 2223219089Spjd const char *propname = NULL; 2224219089Spjd boolean_t reverse = sortcol->sc_reverse; 2225219089Spjd 2226219089Spjd switch (prop) { 2227219089Spjd case ZFS_PROP_TYPE: 2228219089Spjd propname = "type"; 2229219089Spjd (void) nvlist_lookup_uint32(lnvl, propname, &lv32); 2230219089Spjd (void) nvlist_lookup_uint32(rnvl, propname, &rv32); 2231219089Spjd if (rv32 != lv32) 2232240415Smm rc = (rv32 < lv32) ? 1 : -1; 2233219089Spjd break; 2234219089Spjd case ZFS_PROP_NAME: 2235219089Spjd propname = "name"; 2236240415Smm if (numname) { 2237240415Smm (void) nvlist_lookup_uint64(lnvl, propname, 2238240415Smm &lv64); 2239240415Smm (void) nvlist_lookup_uint64(rnvl, propname, 2240240415Smm &rv64); 2241240415Smm if (rv64 != lv64) 2242240415Smm rc = (rv64 < lv64) ? 1 : -1; 2243219089Spjd } else { 2244219089Spjd (void) nvlist_lookup_string(lnvl, propname, 2245219089Spjd &lvstr); 2246219089Spjd (void) nvlist_lookup_string(rnvl, propname, 2247219089Spjd &rvstr); 2248219089Spjd rc = strcmp(lvstr, rvstr); 2249219089Spjd } 2250219089Spjd break; 2251219089Spjd case ZFS_PROP_USED: 2252219089Spjd case ZFS_PROP_QUOTA: 2253240415Smm if (!us_populated) 2254240415Smm break; 2255240415Smm if (prop == ZFS_PROP_USED) 2256219089Spjd propname = "used"; 2257219089Spjd else 2258219089Spjd propname = "quota"; 2259219089Spjd (void) nvlist_lookup_uint64(lnvl, propname, &lv64); 2260219089Spjd (void) nvlist_lookup_uint64(rnvl, propname, &rv64); 2261219089Spjd if (rv64 != lv64) 2262240415Smm rc = (rv64 < lv64) ? 1 : -1; 2263240415Smm break; 2264219089Spjd } 2265219089Spjd 2266240415Smm if (rc != 0) { 2267219089Spjd if (rc < 0) 2268219089Spjd return (reverse ? 1 : -1); 2269219089Spjd else 2270219089Spjd return (reverse ? -1 : 1); 2271240415Smm } 2272219089Spjd } 2273219089Spjd 2274240415Smm /* 2275240415Smm * If entries still seem to be the same, check if they are of the same 2276240415Smm * type (smbentity is added only if we are doing SID to POSIX ID 2277240415Smm * translation where we can have duplicate type/name combinations). 2278240415Smm */ 2279240415Smm if (nvlist_lookup_boolean_value(lnvl, "smbentity", &lvb) == 0 && 2280240415Smm nvlist_lookup_boolean_value(rnvl, "smbentity", &rvb) == 0 && 2281240415Smm lvb != rvb) 2282240415Smm return (lvb < rvb ? -1 : 1); 2283240415Smm 2284240415Smm return (0); 2285219089Spjd} 2286219089Spjd 2287219089Spjdstatic inline const char * 2288219089Spjdus_type2str(unsigned field_type) 2289219089Spjd{ 2290219089Spjd switch (field_type) { 2291219089Spjd case USTYPE_PSX_USR: 2292219089Spjd return ("POSIX User"); 2293219089Spjd case USTYPE_PSX_GRP: 2294219089Spjd return ("POSIX Group"); 2295219089Spjd case USTYPE_SMB_USR: 2296219089Spjd return ("SMB User"); 2297219089Spjd case USTYPE_SMB_GRP: 2298219089Spjd return ("SMB Group"); 2299219089Spjd default: 2300219089Spjd return ("Undefined"); 2301219089Spjd } 2302219089Spjd} 2303219089Spjd 2304209962Smmstatic int 2305209962Smmuserspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space) 2306209962Smm{ 2307219089Spjd us_cbdata_t *cb = (us_cbdata_t *)arg; 2308219089Spjd zfs_userquota_prop_t prop = cb->cb_prop; 2309209962Smm char *name = NULL; 2310219089Spjd char *propname; 2311209962Smm char sizebuf[32]; 2312219089Spjd us_node_t *node; 2313219089Spjd uu_avl_pool_t *avl_pool = cb->cb_avl_pool; 2314219089Spjd uu_avl_t *avl = cb->cb_avl; 2315219089Spjd uu_avl_index_t idx; 2316219089Spjd nvlist_t *props; 2317219089Spjd us_node_t *n; 2318219089Spjd zfs_sort_column_t *sortcol = cb->cb_sortcol; 2319219089Spjd unsigned type; 2320219089Spjd const char *typestr; 2321219089Spjd size_t namelen; 2322219089Spjd size_t typelen; 2323219089Spjd size_t sizelen; 2324240415Smm int typeidx, nameidx, sizeidx; 2325219089Spjd us_sort_info_t sortinfo = { sortcol, cb->cb_numname }; 2326240415Smm boolean_t smbentity = B_FALSE; 2327209962Smm 2328240415Smm if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 2329240415Smm nomem(); 2330240415Smm node = safe_malloc(sizeof (us_node_t)); 2331240415Smm uu_avl_node_init(node, &node->usn_avlnode, avl_pool); 2332240415Smm node->usn_nvl = props; 2333240415Smm 2334240415Smm if (domain != NULL && domain[0] != '\0') { 2335240415Smm /* SMB */ 2336240415Smm char sid[ZFS_MAXNAMELEN + 32]; 2337219089Spjd uid_t id; 2338219089Spjd uint64_t classes; 2339219089Spjd#ifdef sun 2340240415Smm int err; 2341219089Spjd directory_error_t e; 2342219089Spjd#endif 2343219089Spjd 2344240415Smm smbentity = B_TRUE; 2345240415Smm 2346219089Spjd (void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid); 2347240415Smm 2348219089Spjd if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) { 2349219089Spjd type = USTYPE_SMB_GRP; 2350219089Spjd#ifdef sun 2351219089Spjd err = sid_to_id(sid, B_FALSE, &id); 2352219089Spjd#endif 2353219089Spjd } else { 2354219089Spjd type = USTYPE_SMB_USR; 2355219089Spjd#ifdef sun 2356219089Spjd err = sid_to_id(sid, B_TRUE, &id); 2357219089Spjd#endif 2358219089Spjd } 2359219089Spjd 2360219089Spjd#ifdef sun 2361219089Spjd if (err == 0) { 2362219089Spjd rid = id; 2363240415Smm if (!cb->cb_sid2posix) { 2364240415Smm e = directory_name_from_sid(NULL, sid, &name, 2365240415Smm &classes); 2366240415Smm if (e != NULL) 2367240415Smm directory_error_free(e); 2368240415Smm if (name == NULL) 2369240415Smm name = sid; 2370219089Spjd } 2371219089Spjd } 2372219089Spjd#endif 2373209962Smm } 2374209962Smm 2375240415Smm if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') { 2376240415Smm /* POSIX or -i */ 2377240415Smm if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) { 2378240415Smm type = USTYPE_PSX_GRP; 2379240415Smm if (!cb->cb_numname) { 2380240415Smm struct group *g; 2381209962Smm 2382240415Smm if ((g = getgrgid(rid)) != NULL) 2383240415Smm name = g->gr_name; 2384240415Smm } 2385240415Smm } else { 2386240415Smm type = USTYPE_PSX_USR; 2387240415Smm if (!cb->cb_numname) { 2388240415Smm struct passwd *p; 2389209962Smm 2390240415Smm if ((p = getpwuid(rid)) != NULL) 2391240415Smm name = p->pw_name; 2392240415Smm } 2393240415Smm } 2394240415Smm } 2395219089Spjd 2396240415Smm /* 2397240415Smm * Make sure that the type/name combination is unique when doing 2398240415Smm * SID to POSIX ID translation (hence changing the type from SMB to 2399240415Smm * POSIX). 2400240415Smm */ 2401240415Smm if (cb->cb_sid2posix && 2402240415Smm nvlist_add_boolean_value(props, "smbentity", smbentity) != 0) 2403240415Smm nomem(); 2404219089Spjd 2405240415Smm /* Calculate/update width of TYPE field */ 2406240415Smm typestr = us_type2str(type); 2407240415Smm typelen = strlen(gettext(typestr)); 2408240415Smm typeidx = us_field_index("type"); 2409240415Smm if (typelen > cb->cb_width[typeidx]) 2410240415Smm cb->cb_width[typeidx] = typelen; 2411219089Spjd if (nvlist_add_uint32(props, "type", type) != 0) 2412219089Spjd nomem(); 2413209962Smm 2414240415Smm /* Calculate/update width of NAME field */ 2415240415Smm if ((cb->cb_numname && cb->cb_sid2posix) || name == NULL) { 2416240415Smm if (nvlist_add_uint64(props, "name", rid) != 0) 2417219089Spjd nomem(); 2418240415Smm namelen = snprintf(NULL, 0, "%u", rid); 2419219089Spjd } else { 2420219089Spjd if (nvlist_add_string(props, "name", name) != 0) 2421219089Spjd nomem(); 2422219089Spjd namelen = strlen(name); 2423219089Spjd } 2424240415Smm nameidx = us_field_index("name"); 2425240415Smm if (namelen > cb->cb_width[nameidx]) 2426240415Smm cb->cb_width[nameidx] = namelen; 2427219089Spjd 2428240415Smm /* 2429240415Smm * Check if this type/name combination is in the list and update it; 2430240415Smm * otherwise add new node to the list. 2431240415Smm */ 2432240415Smm if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) { 2433240415Smm uu_avl_insert(avl, node, idx); 2434219089Spjd } else { 2435219089Spjd nvlist_free(props); 2436219089Spjd free(node); 2437219089Spjd node = n; 2438219089Spjd props = node->usn_nvl; 2439219089Spjd } 2440219089Spjd 2441240415Smm /* Calculate/update width of USED/QUOTA fields */ 2442240415Smm if (cb->cb_nicenum) 2443240415Smm zfs_nicenum(space, sizebuf, sizeof (sizebuf)); 2444240415Smm else 2445240415Smm (void) snprintf(sizebuf, sizeof (sizebuf), "%llu", space); 2446240415Smm sizelen = strlen(sizebuf); 2447240415Smm if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED) { 2448240415Smm propname = "used"; 2449240415Smm if (!nvlist_exists(props, "quota")) 2450240415Smm (void) nvlist_add_uint64(props, "quota", 0); 2451240415Smm } else { 2452240415Smm propname = "quota"; 2453240415Smm if (!nvlist_exists(props, "used")) 2454240415Smm (void) nvlist_add_uint64(props, "used", 0); 2455240415Smm } 2456240415Smm sizeidx = us_field_index(propname); 2457240415Smm if (sizelen > cb->cb_width[sizeidx]) 2458240415Smm cb->cb_width[sizeidx] = sizelen; 2459240415Smm 2460219089Spjd if (nvlist_add_uint64(props, propname, space) != 0) 2461219089Spjd nomem(); 2462219089Spjd 2463209962Smm return (0); 2464209962Smm} 2465209962Smm 2466219089Spjdstatic void 2467240415Smmprint_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types, 2468240415Smm size_t *width, us_node_t *node) 2469219089Spjd{ 2470219089Spjd nvlist_t *nvl = node->usn_nvl; 2471219089Spjd char valstr[ZFS_MAXNAMELEN]; 2472219089Spjd boolean_t first = B_TRUE; 2473240415Smm int cfield = 0; 2474240415Smm int field; 2475240415Smm uint32_t ustype; 2476219089Spjd 2477240415Smm /* Check type */ 2478240415Smm (void) nvlist_lookup_uint32(nvl, "type", &ustype); 2479240415Smm if (!(ustype & types)) 2480240415Smm return; 2481219089Spjd 2482240415Smm while ((field = fields[cfield]) != USFIELD_LAST) { 2483240415Smm nvpair_t *nvp = NULL; 2484240415Smm data_type_t type; 2485240415Smm uint32_t val32; 2486240415Smm uint64_t val64; 2487219089Spjd char *strval = NULL; 2488240415Smm 2489240415Smm while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 2490240415Smm if (strcmp(nvpair_name(nvp), 2491240415Smm us_field_names[field]) == 0) 2492219089Spjd break; 2493219089Spjd } 2494219089Spjd 2495240415Smm type = nvpair_type(nvp); 2496219089Spjd switch (type) { 2497219089Spjd case DATA_TYPE_UINT32: 2498219089Spjd (void) nvpair_value_uint32(nvp, &val32); 2499219089Spjd break; 2500219089Spjd case DATA_TYPE_UINT64: 2501219089Spjd (void) nvpair_value_uint64(nvp, &val64); 2502219089Spjd break; 2503219089Spjd case DATA_TYPE_STRING: 2504219089Spjd (void) nvpair_value_string(nvp, &strval); 2505219089Spjd break; 2506219089Spjd default: 2507240415Smm (void) fprintf(stderr, "invalid data type\n"); 2508219089Spjd } 2509219089Spjd 2510219089Spjd switch (field) { 2511219089Spjd case USFIELD_TYPE: 2512219089Spjd strval = (char *)us_type2str(val32); 2513219089Spjd break; 2514219089Spjd case USFIELD_NAME: 2515219089Spjd if (type == DATA_TYPE_UINT64) { 2516219089Spjd (void) sprintf(valstr, "%llu", val64); 2517219089Spjd strval = valstr; 2518219089Spjd } 2519219089Spjd break; 2520219089Spjd case USFIELD_USED: 2521219089Spjd case USFIELD_QUOTA: 2522219089Spjd if (type == DATA_TYPE_UINT64) { 2523240415Smm if (parsable) { 2524219089Spjd (void) sprintf(valstr, "%llu", val64); 2525240415Smm } else { 2526219089Spjd zfs_nicenum(val64, valstr, 2527219089Spjd sizeof (valstr)); 2528240415Smm } 2529240415Smm if (field == USFIELD_QUOTA && 2530240415Smm strcmp(valstr, "0") == 0) 2531240415Smm strval = "none"; 2532240415Smm else 2533240415Smm strval = valstr; 2534219089Spjd } 2535219089Spjd break; 2536219089Spjd } 2537219089Spjd 2538240415Smm if (!first) { 2539240415Smm if (scripted) 2540240415Smm (void) printf("\t"); 2541219089Spjd else 2542240415Smm (void) printf(" "); 2543219089Spjd } 2544240415Smm if (scripted) 2545240415Smm (void) printf("%s", strval); 2546240415Smm else if (field == USFIELD_TYPE || field == USFIELD_NAME) 2547240415Smm (void) printf("%-*s", width[field], strval); 2548240415Smm else 2549240415Smm (void) printf("%*s", width[field], strval); 2550219089Spjd 2551219089Spjd first = B_FALSE; 2552240415Smm cfield++; 2553219089Spjd } 2554219089Spjd 2555219089Spjd (void) printf("\n"); 2556219089Spjd} 2557219089Spjd 2558219089Spjdstatic void 2559240415Smmprint_us(boolean_t scripted, boolean_t parsable, int *fields, int types, 2560240415Smm size_t *width, boolean_t rmnode, uu_avl_t *avl) 2561219089Spjd{ 2562219089Spjd us_node_t *node; 2563219089Spjd const char *col; 2564240415Smm int cfield = 0; 2565240415Smm int field; 2566219089Spjd 2567219089Spjd if (!scripted) { 2568219089Spjd boolean_t first = B_TRUE; 2569219089Spjd 2570240415Smm while ((field = fields[cfield]) != USFIELD_LAST) { 2571240415Smm col = gettext(us_field_hdr[field]); 2572240415Smm if (field == USFIELD_TYPE || field == USFIELD_NAME) { 2573240415Smm (void) printf(first ? "%-*s" : " %-*s", 2574240415Smm width[field], col); 2575240415Smm } else { 2576240415Smm (void) printf(first ? "%*s" : " %*s", 2577240415Smm width[field], col); 2578240415Smm } 2579219089Spjd first = B_FALSE; 2580240415Smm cfield++; 2581219089Spjd } 2582219089Spjd (void) printf("\n"); 2583219089Spjd } 2584219089Spjd 2585240415Smm for (node = uu_avl_first(avl); node; node = uu_avl_next(avl, node)) { 2586240415Smm print_us_node(scripted, parsable, fields, types, width, node); 2587219089Spjd if (rmnode) 2588219089Spjd nvlist_free(node->usn_nvl); 2589219089Spjd } 2590219089Spjd} 2591219089Spjd 2592219089Spjdstatic int 2593209962Smmzfs_do_userspace(int argc, char **argv) 2594209962Smm{ 2595209962Smm zfs_handle_t *zhp; 2596209962Smm zfs_userquota_prop_t p; 2597219089Spjd 2598219089Spjd uu_avl_pool_t *avl_pool; 2599219089Spjd uu_avl_t *avl_tree; 2600219089Spjd uu_avl_walk_t *walk; 2601240415Smm char *delim; 2602240415Smm char deffields[] = "type,name,used,quota"; 2603240415Smm char *ofield = NULL; 2604240415Smm char *tfield = NULL; 2605240415Smm int cfield = 0; 2606240415Smm int fields[256]; 2607240415Smm int i; 2608219089Spjd boolean_t scripted = B_FALSE; 2609219089Spjd boolean_t prtnum = B_FALSE; 2610240415Smm boolean_t parsable = B_FALSE; 2611219089Spjd boolean_t sid2posix = B_FALSE; 2612240415Smm int ret = 0; 2613219089Spjd int c; 2614219089Spjd zfs_sort_column_t *sortcol = NULL; 2615240415Smm int types = USTYPE_PSX_USR | USTYPE_SMB_USR; 2616219089Spjd us_cbdata_t cb; 2617219089Spjd us_node_t *node; 2618240415Smm us_node_t *rmnode; 2619240415Smm uu_list_pool_t *listpool; 2620240415Smm uu_list_t *list; 2621240415Smm uu_avl_index_t idx = 0; 2622240415Smm uu_list_index_t idx2 = 0; 2623209962Smm 2624219089Spjd if (argc < 2) 2625219089Spjd usage(B_FALSE); 2626209962Smm 2627240415Smm if (strcmp(argv[0], "groupspace") == 0) 2628240415Smm /* Toggle default group types */ 2629219089Spjd types = USTYPE_PSX_GRP | USTYPE_SMB_GRP; 2630209962Smm 2631219089Spjd while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) { 2632219089Spjd switch (c) { 2633219089Spjd case 'n': 2634219089Spjd prtnum = B_TRUE; 2635219089Spjd break; 2636219089Spjd case 'H': 2637219089Spjd scripted = B_TRUE; 2638219089Spjd break; 2639219089Spjd case 'p': 2640240415Smm parsable = B_TRUE; 2641219089Spjd break; 2642219089Spjd case 'o': 2643240415Smm ofield = optarg; 2644219089Spjd break; 2645219089Spjd case 's': 2646219089Spjd case 'S': 2647219089Spjd if (zfs_add_sort_column(&sortcol, optarg, 2648240415Smm c == 's' ? B_FALSE : B_TRUE) != 0) { 2649219089Spjd (void) fprintf(stderr, 2650240415Smm gettext("invalid field '%s'\n"), optarg); 2651219089Spjd usage(B_FALSE); 2652219089Spjd } 2653219089Spjd break; 2654219089Spjd case 't': 2655240415Smm tfield = optarg; 2656219089Spjd break; 2657219089Spjd case 'i': 2658219089Spjd sid2posix = B_TRUE; 2659219089Spjd break; 2660219089Spjd case ':': 2661219089Spjd (void) fprintf(stderr, gettext("missing argument for " 2662219089Spjd "'%c' option\n"), optopt); 2663219089Spjd usage(B_FALSE); 2664219089Spjd break; 2665219089Spjd case '?': 2666219089Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2667219089Spjd optopt); 2668219089Spjd usage(B_FALSE); 2669219089Spjd } 2670219089Spjd } 2671219089Spjd 2672219089Spjd argc -= optind; 2673219089Spjd argv += optind; 2674219089Spjd 2675240415Smm if (argc < 1) { 2676240415Smm (void) fprintf(stderr, gettext("missing dataset name\n")); 2677240415Smm usage(B_FALSE); 2678240415Smm } 2679240415Smm if (argc > 1) { 2680240415Smm (void) fprintf(stderr, gettext("too many arguments\n")); 2681240415Smm usage(B_FALSE); 2682240415Smm } 2683240415Smm 2684240415Smm /* Use default output fields if not specified using -o */ 2685240415Smm if (ofield == NULL) 2686240415Smm ofield = deffields; 2687240415Smm do { 2688240415Smm if ((delim = strchr(ofield, ',')) != NULL) 2689240415Smm *delim = '\0'; 2690240415Smm if ((fields[cfield++] = us_field_index(ofield)) == -1) { 2691240415Smm (void) fprintf(stderr, gettext("invalid type '%s' " 2692240415Smm "for -o option\n"), ofield); 2693240415Smm return (-1); 2694240415Smm } 2695240415Smm if (delim != NULL) 2696240415Smm ofield = delim + 1; 2697240415Smm } while (delim != NULL); 2698240415Smm fields[cfield] = USFIELD_LAST; 2699240415Smm 2700240415Smm /* Override output types (-t option) */ 2701240415Smm if (tfield != NULL) { 2702240415Smm types = 0; 2703240415Smm 2704240415Smm do { 2705240415Smm boolean_t found = B_FALSE; 2706240415Smm 2707240415Smm if ((delim = strchr(tfield, ',')) != NULL) 2708240415Smm *delim = '\0'; 2709240415Smm for (i = 0; i < sizeof (us_type_bits) / sizeof (int); 2710240415Smm i++) { 2711240415Smm if (strcmp(tfield, us_type_names[i]) == 0) { 2712240415Smm found = B_TRUE; 2713240415Smm types |= us_type_bits[i]; 2714240415Smm break; 2715240415Smm } 2716219089Spjd } 2717240415Smm if (!found) { 2718240415Smm (void) fprintf(stderr, gettext("invalid type " 2719240415Smm "'%s' for -t option\n"), tfield); 2720240415Smm return (-1); 2721240415Smm } 2722240415Smm if (delim != NULL) 2723240415Smm tfield = delim + 1; 2724240415Smm } while (delim != NULL); 2725219089Spjd } 2726219089Spjd 2727240415Smm if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL) 2728209962Smm return (1); 2729209962Smm 2730219089Spjd if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t), 2731240415Smm offsetof(us_node_t, usn_avlnode), us_compare, UU_DEFAULT)) == NULL) 2732219089Spjd nomem(); 2733219089Spjd if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) 2734219089Spjd nomem(); 2735209962Smm 2736240415Smm /* Always add default sorting columns */ 2737240415Smm (void) zfs_add_sort_column(&sortcol, "type", B_FALSE); 2738240415Smm (void) zfs_add_sort_column(&sortcol, "name", B_FALSE); 2739240415Smm 2740240415Smm cb.cb_sortcol = sortcol; 2741219089Spjd cb.cb_numname = prtnum; 2742240415Smm cb.cb_nicenum = !parsable; 2743219089Spjd cb.cb_avl_pool = avl_pool; 2744219089Spjd cb.cb_avl = avl_tree; 2745219089Spjd cb.cb_sid2posix = sid2posix; 2746219089Spjd 2747240415Smm for (i = 0; i < USFIELD_LAST; i++) 2748240415Smm cb.cb_width[i] = strlen(gettext(us_field_hdr[i])); 2749240415Smm 2750209962Smm for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) { 2751240415Smm if (((p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA) && 2752240415Smm !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) || 2753240415Smm ((p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) && 2754240415Smm !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP)))) 2755219089Spjd continue; 2756219089Spjd cb.cb_prop = p; 2757240415Smm if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0) 2758240415Smm return (ret); 2759209962Smm } 2760219089Spjd 2761240415Smm /* Sort the list */ 2762240415Smm if ((node = uu_avl_first(avl_tree)) == NULL) 2763240415Smm return (0); 2764219089Spjd 2765240415Smm us_populated = B_TRUE; 2766219089Spjd 2767240415Smm listpool = uu_list_pool_create("tmplist", sizeof (us_node_t), 2768240415Smm offsetof(us_node_t, usn_listnode), NULL, UU_DEFAULT); 2769240415Smm list = uu_list_create(listpool, NULL, UU_DEFAULT); 2770240415Smm uu_list_node_init(node, &node->usn_listnode, listpool); 2771219089Spjd 2772240415Smm while (node != NULL) { 2773240415Smm rmnode = node; 2774240415Smm node = uu_avl_next(avl_tree, node); 2775240415Smm uu_avl_remove(avl_tree, rmnode); 2776240415Smm if (uu_list_find(list, rmnode, NULL, &idx2) == NULL) 2777240415Smm uu_list_insert(list, rmnode, idx2); 2778219089Spjd } 2779219089Spjd 2780240415Smm for (node = uu_list_first(list); node != NULL; 2781240415Smm node = uu_list_next(list, node)) { 2782240415Smm us_sort_info_t sortinfo = { sortcol, cb.cb_numname }; 2783219089Spjd 2784240415Smm if (uu_avl_find(avl_tree, node, &sortinfo, &idx) == NULL) 2785240415Smm uu_avl_insert(avl_tree, node, idx); 2786240415Smm } 2787219089Spjd 2788240415Smm uu_list_destroy(list); 2789240415Smm uu_list_pool_destroy(listpool); 2790240415Smm 2791240415Smm /* Print and free node nvlist memory */ 2792240415Smm print_us(scripted, parsable, fields, types, cb.cb_width, B_TRUE, 2793240415Smm cb.cb_avl); 2794240415Smm 2795240415Smm zfs_free_sort_columns(sortcol); 2796240415Smm 2797240415Smm /* Clean up the AVL tree */ 2798219089Spjd if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) 2799219089Spjd nomem(); 2800219089Spjd 2801219089Spjd while ((node = uu_avl_walk_next(walk)) != NULL) { 2802219089Spjd uu_avl_remove(cb.cb_avl, node); 2803219089Spjd free(node); 2804219089Spjd } 2805219089Spjd 2806219089Spjd uu_avl_walk_end(walk); 2807219089Spjd uu_avl_destroy(avl_tree); 2808219089Spjd uu_avl_pool_destroy(avl_pool); 2809219089Spjd 2810240415Smm return (ret); 2811209962Smm} 2812209962Smm 2813209962Smm/* 2814205199Sdelphij * list [-r][-d max] [-H] [-o property[,property]...] [-t type[,type]...] 2815168404Spjd * [-s property [-s property]...] [-S property [-S property]...] 2816168404Spjd * <dataset> ... 2817168404Spjd * 2818219089Spjd * -r Recurse over all children 2819219089Spjd * -d Limit recursion by depth. 2820219089Spjd * -H Scripted mode; elide headers and separate columns by tabs 2821219089Spjd * -o Control which fields to display. 2822219089Spjd * -t Control which object types to display. 2823168404Spjd * -s Specify sort columns, descending order. 2824168404Spjd * -S Specify sort columns, ascending order. 2825168404Spjd * 2826168404Spjd * When given no arguments, lists all filesystems in the system. 2827168404Spjd * Otherwise, list the specified datasets, optionally recursing down them if 2828168404Spjd * '-r' is specified. 2829168404Spjd */ 2830168404Spjdtypedef struct list_cbdata { 2831168404Spjd boolean_t cb_first; 2832168404Spjd boolean_t cb_scripted; 2833185029Spjd zprop_list_t *cb_proplist; 2834168404Spjd} list_cbdata_t; 2835168404Spjd 2836168404Spjd/* 2837168404Spjd * Given a list of columns to display, output appropriate headers for each one. 2838168404Spjd */ 2839168404Spjdstatic void 2840185029Spjdprint_header(zprop_list_t *pl) 2841168404Spjd{ 2842168404Spjd char headerbuf[ZFS_MAXPROPLEN]; 2843168404Spjd const char *header; 2844168404Spjd int i; 2845168404Spjd boolean_t first = B_TRUE; 2846168404Spjd boolean_t right_justify; 2847168404Spjd 2848168404Spjd for (; pl != NULL; pl = pl->pl_next) { 2849168404Spjd if (!first) { 2850168404Spjd (void) printf(" "); 2851168404Spjd } else { 2852168404Spjd first = B_FALSE; 2853168404Spjd } 2854168404Spjd 2855168404Spjd right_justify = B_FALSE; 2856185029Spjd if (pl->pl_prop != ZPROP_INVAL) { 2857168404Spjd header = zfs_prop_column_name(pl->pl_prop); 2858168404Spjd right_justify = zfs_prop_align_right(pl->pl_prop); 2859168404Spjd } else { 2860168404Spjd for (i = 0; pl->pl_user_prop[i] != '\0'; i++) 2861168404Spjd headerbuf[i] = toupper(pl->pl_user_prop[i]); 2862168404Spjd headerbuf[i] = '\0'; 2863168404Spjd header = headerbuf; 2864168404Spjd } 2865168404Spjd 2866168404Spjd if (pl->pl_next == NULL && !right_justify) 2867168404Spjd (void) printf("%s", header); 2868168404Spjd else if (right_justify) 2869168404Spjd (void) printf("%*s", pl->pl_width, header); 2870168404Spjd else 2871168404Spjd (void) printf("%-*s", pl->pl_width, header); 2872168404Spjd } 2873168404Spjd 2874168404Spjd (void) printf("\n"); 2875168404Spjd} 2876168404Spjd 2877168404Spjd/* 2878168404Spjd * Given a dataset and a list of fields, print out all the properties according 2879168404Spjd * to the described layout. 2880168404Spjd */ 2881168404Spjdstatic void 2882185029Spjdprint_dataset(zfs_handle_t *zhp, zprop_list_t *pl, boolean_t scripted) 2883168404Spjd{ 2884168404Spjd boolean_t first = B_TRUE; 2885168404Spjd char property[ZFS_MAXPROPLEN]; 2886168404Spjd nvlist_t *userprops = zfs_get_user_props(zhp); 2887168404Spjd nvlist_t *propval; 2888168404Spjd char *propstr; 2889168404Spjd boolean_t right_justify; 2890168404Spjd int width; 2891168404Spjd 2892168404Spjd for (; pl != NULL; pl = pl->pl_next) { 2893168404Spjd if (!first) { 2894168404Spjd if (scripted) 2895168404Spjd (void) printf("\t"); 2896168404Spjd else 2897168404Spjd (void) printf(" "); 2898168404Spjd } else { 2899168404Spjd first = B_FALSE; 2900168404Spjd } 2901168404Spjd 2902230438Spjd if (pl->pl_prop == ZFS_PROP_NAME) { 2903230438Spjd (void) strlcpy(property, zfs_get_name(zhp), 2904230438Spjd sizeof(property)); 2905230438Spjd propstr = property; 2906230438Spjd right_justify = zfs_prop_align_right(pl->pl_prop); 2907230438Spjd } else if (pl->pl_prop != ZPROP_INVAL) { 2908168404Spjd if (zfs_prop_get(zhp, pl->pl_prop, property, 2909168404Spjd sizeof (property), NULL, NULL, 0, B_FALSE) != 0) 2910168404Spjd propstr = "-"; 2911168404Spjd else 2912168404Spjd propstr = property; 2913168404Spjd 2914168404Spjd right_justify = zfs_prop_align_right(pl->pl_prop); 2915209962Smm } else if (zfs_prop_userquota(pl->pl_user_prop)) { 2916209962Smm if (zfs_prop_get_userquota(zhp, pl->pl_user_prop, 2917209962Smm property, sizeof (property), B_FALSE) != 0) 2918209962Smm propstr = "-"; 2919209962Smm else 2920209962Smm propstr = property; 2921209962Smm right_justify = B_TRUE; 2922228103Smm } else if (zfs_prop_written(pl->pl_user_prop)) { 2923228103Smm if (zfs_prop_get_written(zhp, pl->pl_user_prop, 2924228103Smm property, sizeof (property), B_FALSE) != 0) 2925228103Smm propstr = "-"; 2926228103Smm else 2927228103Smm propstr = property; 2928228103Smm right_justify = B_TRUE; 2929168404Spjd } else { 2930168404Spjd if (nvlist_lookup_nvlist(userprops, 2931168404Spjd pl->pl_user_prop, &propval) != 0) 2932168404Spjd propstr = "-"; 2933168404Spjd else 2934168404Spjd verify(nvlist_lookup_string(propval, 2935185029Spjd ZPROP_VALUE, &propstr) == 0); 2936209962Smm right_justify = B_FALSE; 2937168404Spjd } 2938168404Spjd 2939168404Spjd width = pl->pl_width; 2940168404Spjd 2941168404Spjd /* 2942168404Spjd * If this is being called in scripted mode, or if this is the 2943168404Spjd * last column and it is left-justified, don't include a width 2944168404Spjd * format specifier. 2945168404Spjd */ 2946168404Spjd if (scripted || (pl->pl_next == NULL && !right_justify)) 2947168404Spjd (void) printf("%s", propstr); 2948168404Spjd else if (right_justify) 2949168404Spjd (void) printf("%*s", width, propstr); 2950168404Spjd else 2951168404Spjd (void) printf("%-*s", width, propstr); 2952168404Spjd } 2953168404Spjd 2954168404Spjd (void) printf("\n"); 2955168404Spjd} 2956168404Spjd 2957168404Spjd/* 2958168404Spjd * Generic callback function to list a dataset or snapshot. 2959168404Spjd */ 2960168404Spjdstatic int 2961168404Spjdlist_callback(zfs_handle_t *zhp, void *data) 2962168404Spjd{ 2963168404Spjd list_cbdata_t *cbp = data; 2964168404Spjd 2965168404Spjd if (cbp->cb_first) { 2966168404Spjd if (!cbp->cb_scripted) 2967168404Spjd print_header(cbp->cb_proplist); 2968168404Spjd cbp->cb_first = B_FALSE; 2969168404Spjd } 2970168404Spjd 2971168404Spjd print_dataset(zhp, cbp->cb_proplist, cbp->cb_scripted); 2972168404Spjd 2973168404Spjd return (0); 2974168404Spjd} 2975168404Spjd 2976168404Spjdstatic int 2977168404Spjdzfs_do_list(int argc, char **argv) 2978168404Spjd{ 2979168404Spjd int c; 2980168404Spjd boolean_t scripted = B_FALSE; 2981168404Spjd static char default_fields[] = 2982168404Spjd "name,used,available,referenced,mountpoint"; 2983207627Smm int types = ZFS_TYPE_DATASET; 2984185029Spjd boolean_t types_specified = B_FALSE; 2985168404Spjd char *fields = NULL; 2986168404Spjd list_cbdata_t cb = { 0 }; 2987168404Spjd char *value; 2988205199Sdelphij int limit = 0; 2989231144Smm int ret = 0; 2990168404Spjd zfs_sort_column_t *sortcol = NULL; 2991185029Spjd int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS; 2992168404Spjd 2993168404Spjd /* check options */ 2994205199Sdelphij while ((c = getopt(argc, argv, ":d:o:rt:Hs:S:")) != -1) { 2995168404Spjd switch (c) { 2996168404Spjd case 'o': 2997168404Spjd fields = optarg; 2998168404Spjd break; 2999205199Sdelphij case 'd': 3000205199Sdelphij limit = parse_depth(optarg, &flags); 3001205199Sdelphij break; 3002168404Spjd case 'r': 3003185029Spjd flags |= ZFS_ITER_RECURSE; 3004168404Spjd break; 3005168404Spjd case 'H': 3006168404Spjd scripted = B_TRUE; 3007168404Spjd break; 3008168404Spjd case 's': 3009168404Spjd if (zfs_add_sort_column(&sortcol, optarg, 3010168404Spjd B_FALSE) != 0) { 3011168404Spjd (void) fprintf(stderr, 3012168404Spjd gettext("invalid property '%s'\n"), optarg); 3013168404Spjd usage(B_FALSE); 3014168404Spjd } 3015168404Spjd break; 3016168404Spjd case 'S': 3017168404Spjd if (zfs_add_sort_column(&sortcol, optarg, 3018168404Spjd B_TRUE) != 0) { 3019168404Spjd (void) fprintf(stderr, 3020168404Spjd gettext("invalid property '%s'\n"), optarg); 3021168404Spjd usage(B_FALSE); 3022168404Spjd } 3023168404Spjd break; 3024168404Spjd case 't': 3025168404Spjd types = 0; 3026185029Spjd types_specified = B_TRUE; 3027185029Spjd flags &= ~ZFS_ITER_PROP_LISTSNAPS; 3028168404Spjd while (*optarg != '\0') { 3029185029Spjd static char *type_subopts[] = { "filesystem", 3030185029Spjd "volume", "snapshot", "all", NULL }; 3031185029Spjd 3032168404Spjd switch (getsubopt(&optarg, type_subopts, 3033168404Spjd &value)) { 3034168404Spjd case 0: 3035168404Spjd types |= ZFS_TYPE_FILESYSTEM; 3036168404Spjd break; 3037168404Spjd case 1: 3038168404Spjd types |= ZFS_TYPE_VOLUME; 3039168404Spjd break; 3040168404Spjd case 2: 3041168404Spjd types |= ZFS_TYPE_SNAPSHOT; 3042168404Spjd break; 3043185029Spjd case 3: 3044185029Spjd types = ZFS_TYPE_DATASET; 3045185029Spjd break; 3046185029Spjd 3047168404Spjd default: 3048168404Spjd (void) fprintf(stderr, 3049168404Spjd gettext("invalid type '%s'\n"), 3050168404Spjd value); 3051168404Spjd usage(B_FALSE); 3052168404Spjd } 3053168404Spjd } 3054168404Spjd break; 3055168404Spjd case ':': 3056168404Spjd (void) fprintf(stderr, gettext("missing argument for " 3057168404Spjd "'%c' option\n"), optopt); 3058168404Spjd usage(B_FALSE); 3059168404Spjd break; 3060168404Spjd case '?': 3061168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3062168404Spjd optopt); 3063168404Spjd usage(B_FALSE); 3064168404Spjd } 3065168404Spjd } 3066168404Spjd 3067168404Spjd argc -= optind; 3068168404Spjd argv += optind; 3069168404Spjd 3070168404Spjd if (fields == NULL) 3071185029Spjd fields = default_fields; 3072168404Spjd 3073168404Spjd /* 3074230438Spjd * If we are only going to list snapshot names and sort by name, 3075230438Spjd * then we can use faster version. 3076230438Spjd */ 3077230438Spjd if (strcmp(fields, "name") == 0 && zfs_sort_only_by_name(sortcol)) 3078230438Spjd flags |= ZFS_ITER_SIMPLE; 3079230438Spjd 3080230438Spjd /* 3081185029Spjd * If "-o space" and no types were specified, don't display snapshots. 3082185029Spjd */ 3083185029Spjd if (strcmp(fields, "space") == 0 && types_specified == B_FALSE) 3084185029Spjd types &= ~ZFS_TYPE_SNAPSHOT; 3085185029Spjd 3086185029Spjd /* 3087185029Spjd * If the user specifies '-o all', the zprop_get_list() doesn't 3088168404Spjd * normally include the name of the dataset. For 'zfs list', we always 3089168404Spjd * want this property to be first. 3090168404Spjd */ 3091185029Spjd if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET) 3092185029Spjd != 0) 3093168404Spjd usage(B_FALSE); 3094168404Spjd 3095168404Spjd cb.cb_scripted = scripted; 3096168404Spjd cb.cb_first = B_TRUE; 3097168404Spjd 3098185029Spjd ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist, 3099205199Sdelphij limit, list_callback, &cb); 3100168404Spjd 3101185029Spjd zprop_free_list(cb.cb_proplist); 3102168404Spjd zfs_free_sort_columns(sortcol); 3103168404Spjd 3104185029Spjd if (ret == 0 && cb.cb_first && !cb.cb_scripted) 3105168404Spjd (void) printf(gettext("no datasets available\n")); 3106168404Spjd 3107168404Spjd return (ret); 3108168404Spjd} 3109168404Spjd 3110168404Spjd/* 3111235216Smm * zfs rename [-f] <fs | snap | vol> <fs | snap | vol> 3112235216Smm * zfs rename [-f] -p <fs | vol> <fs | vol> 3113185029Spjd * zfs rename -r <snap> <snap> 3114226705Spjd * zfs rename -u [-p] <fs> <fs> 3115168404Spjd * 3116168404Spjd * Renames the given dataset to another of the same type. 3117185029Spjd * 3118185029Spjd * The '-p' flag creates all the non-existing ancestors of the target first. 3119168404Spjd */ 3120168404Spjd/* ARGSUSED */ 3121168404Spjdstatic int 3122168404Spjdzfs_do_rename(int argc, char **argv) 3123168404Spjd{ 3124168404Spjd zfs_handle_t *zhp; 3125226705Spjd renameflags_t flags = { 0 }; 3126231144Smm int c; 3127231144Smm int ret = 0; 3128231144Smm int types; 3129185029Spjd boolean_t parents = B_FALSE; 3130240870Spjd char *snapshot = NULL; 3131168404Spjd 3132168404Spjd /* check options */ 3133235216Smm while ((c = getopt(argc, argv, "fpru")) != -1) { 3134168676Spjd switch (c) { 3135185029Spjd case 'p': 3136185029Spjd parents = B_TRUE; 3137185029Spjd break; 3138168676Spjd case 'r': 3139226705Spjd flags.recurse = B_TRUE; 3140168676Spjd break; 3141226705Spjd case 'u': 3142226705Spjd flags.nounmount = B_TRUE; 3143226705Spjd break; 3144235216Smm case 'f': 3145235216Smm flags.forceunmount = B_TRUE; 3146235216Smm break; 3147168676Spjd case '?': 3148168676Spjd default: 3149168676Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3150168676Spjd optopt); 3151168676Spjd usage(B_FALSE); 3152168676Spjd } 3153168404Spjd } 3154168404Spjd 3155168676Spjd argc -= optind; 3156168676Spjd argv += optind; 3157168676Spjd 3158168404Spjd /* check number of arguments */ 3159168676Spjd if (argc < 1) { 3160168404Spjd (void) fprintf(stderr, gettext("missing source dataset " 3161168404Spjd "argument\n")); 3162168404Spjd usage(B_FALSE); 3163168404Spjd } 3164168676Spjd if (argc < 2) { 3165168404Spjd (void) fprintf(stderr, gettext("missing target dataset " 3166168404Spjd "argument\n")); 3167168404Spjd usage(B_FALSE); 3168168404Spjd } 3169168676Spjd if (argc > 2) { 3170168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 3171168404Spjd usage(B_FALSE); 3172168404Spjd } 3173168404Spjd 3174226705Spjd if (flags.recurse && parents) { 3175185029Spjd (void) fprintf(stderr, gettext("-p and -r options are mutually " 3176185029Spjd "exclusive\n")); 3177185029Spjd usage(B_FALSE); 3178185029Spjd } 3179185029Spjd 3180226705Spjd if (flags.recurse && strchr(argv[0], '@') == 0) { 3181168676Spjd (void) fprintf(stderr, gettext("source dataset for recursive " 3182168676Spjd "rename must be a snapshot\n")); 3183168676Spjd usage(B_FALSE); 3184168676Spjd } 3185168676Spjd 3186226705Spjd if (flags.nounmount && parents) { 3187236013Spjd (void) fprintf(stderr, gettext("-u and -p options are mutually " 3188226705Spjd "exclusive\n")); 3189226705Spjd usage(B_FALSE); 3190226705Spjd } 3191226705Spjd 3192226705Spjd if (flags.nounmount) 3193226705Spjd types = ZFS_TYPE_FILESYSTEM; 3194226705Spjd else if (parents) 3195226705Spjd types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME; 3196226705Spjd else 3197226705Spjd types = ZFS_TYPE_DATASET; 3198226705Spjd 3199240870Spjd if (flags.recurse) { 3200240870Spjd /* 3201240870Spjd * When we do recursive rename we are fine when the given 3202240870Spjd * snapshot for the given dataset doesn't exist - it can 3203240870Spjd * still exists below. 3204240870Spjd */ 3205240870Spjd 3206240870Spjd snapshot = strchr(argv[0], '@'); 3207240870Spjd assert(snapshot != NULL); 3208240870Spjd *snapshot = '\0'; 3209240870Spjd snapshot++; 3210240870Spjd } 3211240870Spjd 3212226705Spjd if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) 3213168404Spjd return (1); 3214168404Spjd 3215185029Spjd /* If we were asked and the name looks good, try to create ancestors. */ 3216185029Spjd if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) && 3217185029Spjd zfs_create_ancestors(g_zfs, argv[1]) != 0) { 3218185029Spjd zfs_close(zhp); 3219185029Spjd return (1); 3220185029Spjd } 3221185029Spjd 3222240870Spjd ret = (zfs_rename(zhp, snapshot, argv[1], flags) != 0); 3223168404Spjd 3224168404Spjd zfs_close(zhp); 3225168404Spjd return (ret); 3226168404Spjd} 3227168404Spjd 3228168404Spjd/* 3229168404Spjd * zfs promote <fs> 3230168404Spjd * 3231168404Spjd * Promotes the given clone fs to be the parent 3232168404Spjd */ 3233168404Spjd/* ARGSUSED */ 3234168404Spjdstatic int 3235168404Spjdzfs_do_promote(int argc, char **argv) 3236168404Spjd{ 3237168404Spjd zfs_handle_t *zhp; 3238231144Smm int ret = 0; 3239168404Spjd 3240168404Spjd /* check options */ 3241168404Spjd if (argc > 1 && argv[1][0] == '-') { 3242168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3243168404Spjd argv[1][1]); 3244168404Spjd usage(B_FALSE); 3245168404Spjd } 3246168404Spjd 3247168404Spjd /* check number of arguments */ 3248168404Spjd if (argc < 2) { 3249168404Spjd (void) fprintf(stderr, gettext("missing clone filesystem" 3250168404Spjd " argument\n")); 3251168404Spjd usage(B_FALSE); 3252168404Spjd } 3253168404Spjd if (argc > 2) { 3254168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 3255168404Spjd usage(B_FALSE); 3256168404Spjd } 3257168404Spjd 3258168404Spjd zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 3259168404Spjd if (zhp == NULL) 3260168404Spjd return (1); 3261168404Spjd 3262168404Spjd ret = (zfs_promote(zhp) != 0); 3263168404Spjd 3264168404Spjd 3265168404Spjd zfs_close(zhp); 3266168404Spjd return (ret); 3267168404Spjd} 3268168404Spjd 3269168404Spjd/* 3270185029Spjd * zfs rollback [-rRf] <snapshot> 3271168404Spjd * 3272219089Spjd * -r Delete any intervening snapshots before doing rollback 3273219089Spjd * -R Delete any snapshots and their clones 3274219089Spjd * -f ignored for backwards compatability 3275168404Spjd * 3276168404Spjd * Given a filesystem, rollback to a specific snapshot, discarding any changes 3277168404Spjd * since then and making it the active dataset. If more recent snapshots exist, 3278168404Spjd * the command will complain unless the '-r' flag is given. 3279168404Spjd */ 3280168404Spjdtypedef struct rollback_cbdata { 3281168404Spjd uint64_t cb_create; 3282168404Spjd boolean_t cb_first; 3283168404Spjd int cb_doclones; 3284168404Spjd char *cb_target; 3285168404Spjd int cb_error; 3286168404Spjd boolean_t cb_recurse; 3287168404Spjd boolean_t cb_dependent; 3288168404Spjd} rollback_cbdata_t; 3289168404Spjd 3290168404Spjd/* 3291168404Spjd * Report any snapshots more recent than the one specified. Used when '-r' is 3292168404Spjd * not specified. We reuse this same callback for the snapshot dependents - if 3293168404Spjd * 'cb_dependent' is set, then this is a dependent and we should report it 3294168404Spjd * without checking the transaction group. 3295168404Spjd */ 3296168404Spjdstatic int 3297168404Spjdrollback_check(zfs_handle_t *zhp, void *data) 3298168404Spjd{ 3299168404Spjd rollback_cbdata_t *cbp = data; 3300168404Spjd 3301168404Spjd if (cbp->cb_doclones) { 3302168404Spjd zfs_close(zhp); 3303168404Spjd return (0); 3304168404Spjd } 3305168404Spjd 3306168404Spjd if (!cbp->cb_dependent) { 3307168404Spjd if (strcmp(zfs_get_name(zhp), cbp->cb_target) != 0 && 3308168404Spjd zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && 3309168404Spjd zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > 3310168404Spjd cbp->cb_create) { 3311168404Spjd 3312168404Spjd if (cbp->cb_first && !cbp->cb_recurse) { 3313168404Spjd (void) fprintf(stderr, gettext("cannot " 3314168404Spjd "rollback to '%s': more recent snapshots " 3315168404Spjd "exist\n"), 3316168404Spjd cbp->cb_target); 3317168404Spjd (void) fprintf(stderr, gettext("use '-r' to " 3318168404Spjd "force deletion of the following " 3319168404Spjd "snapshots:\n")); 3320168404Spjd cbp->cb_first = 0; 3321168404Spjd cbp->cb_error = 1; 3322168404Spjd } 3323168404Spjd 3324168404Spjd if (cbp->cb_recurse) { 3325168404Spjd cbp->cb_dependent = B_TRUE; 3326168404Spjd if (zfs_iter_dependents(zhp, B_TRUE, 3327168404Spjd rollback_check, cbp) != 0) { 3328168404Spjd zfs_close(zhp); 3329168404Spjd return (-1); 3330168404Spjd } 3331168404Spjd cbp->cb_dependent = B_FALSE; 3332168404Spjd } else { 3333168404Spjd (void) fprintf(stderr, "%s\n", 3334168404Spjd zfs_get_name(zhp)); 3335168404Spjd } 3336168404Spjd } 3337168404Spjd } else { 3338168404Spjd if (cbp->cb_first && cbp->cb_recurse) { 3339168404Spjd (void) fprintf(stderr, gettext("cannot rollback to " 3340168404Spjd "'%s': clones of previous snapshots exist\n"), 3341168404Spjd cbp->cb_target); 3342168404Spjd (void) fprintf(stderr, gettext("use '-R' to " 3343168404Spjd "force deletion of the following clones and " 3344168404Spjd "dependents:\n")); 3345168404Spjd cbp->cb_first = 0; 3346168404Spjd cbp->cb_error = 1; 3347168404Spjd } 3348168404Spjd 3349168404Spjd (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 3350168404Spjd } 3351168404Spjd 3352168404Spjd zfs_close(zhp); 3353168404Spjd return (0); 3354168404Spjd} 3355168404Spjd 3356168404Spjdstatic int 3357168404Spjdzfs_do_rollback(int argc, char **argv) 3358168404Spjd{ 3359231144Smm int ret = 0; 3360168404Spjd int c; 3361185029Spjd boolean_t force = B_FALSE; 3362168404Spjd rollback_cbdata_t cb = { 0 }; 3363168404Spjd zfs_handle_t *zhp, *snap; 3364168404Spjd char parentname[ZFS_MAXNAMELEN]; 3365168404Spjd char *delim; 3366168404Spjd 3367168404Spjd /* check options */ 3368185029Spjd while ((c = getopt(argc, argv, "rRf")) != -1) { 3369168404Spjd switch (c) { 3370168404Spjd case 'r': 3371168404Spjd cb.cb_recurse = 1; 3372168404Spjd break; 3373168404Spjd case 'R': 3374168404Spjd cb.cb_recurse = 1; 3375168404Spjd cb.cb_doclones = 1; 3376168404Spjd break; 3377185029Spjd case 'f': 3378185029Spjd force = B_TRUE; 3379185029Spjd break; 3380168404Spjd case '?': 3381168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3382168404Spjd optopt); 3383168404Spjd usage(B_FALSE); 3384168404Spjd } 3385168404Spjd } 3386168404Spjd 3387168404Spjd argc -= optind; 3388168404Spjd argv += optind; 3389168404Spjd 3390168404Spjd /* check number of arguments */ 3391168404Spjd if (argc < 1) { 3392168404Spjd (void) fprintf(stderr, gettext("missing dataset argument\n")); 3393168404Spjd usage(B_FALSE); 3394168404Spjd } 3395168404Spjd if (argc > 1) { 3396168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 3397168404Spjd usage(B_FALSE); 3398168404Spjd } 3399168404Spjd 3400168404Spjd /* open the snapshot */ 3401168404Spjd if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 3402168404Spjd return (1); 3403168404Spjd 3404168404Spjd /* open the parent dataset */ 3405168404Spjd (void) strlcpy(parentname, argv[0], sizeof (parentname)); 3406168404Spjd verify((delim = strrchr(parentname, '@')) != NULL); 3407168404Spjd *delim = '\0'; 3408185029Spjd if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_DATASET)) == NULL) { 3409168404Spjd zfs_close(snap); 3410168404Spjd return (1); 3411168404Spjd } 3412168404Spjd 3413168404Spjd /* 3414168404Spjd * Check for more recent snapshots and/or clones based on the presence 3415168404Spjd * of '-r' and '-R'. 3416168404Spjd */ 3417168404Spjd cb.cb_target = argv[0]; 3418168404Spjd cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 3419168404Spjd cb.cb_first = B_TRUE; 3420168404Spjd cb.cb_error = 0; 3421168404Spjd if ((ret = zfs_iter_children(zhp, rollback_check, &cb)) != 0) 3422168404Spjd goto out; 3423168404Spjd 3424168404Spjd if ((ret = cb.cb_error) != 0) 3425168404Spjd goto out; 3426168404Spjd 3427168404Spjd /* 3428168404Spjd * Rollback parent to the given snapshot. 3429168404Spjd */ 3430168404Spjd ret = zfs_rollback(zhp, snap, force); 3431168404Spjd 3432168404Spjdout: 3433168404Spjd zfs_close(snap); 3434168404Spjd zfs_close(zhp); 3435168404Spjd 3436168404Spjd if (ret == 0) 3437168404Spjd return (0); 3438168404Spjd else 3439168404Spjd return (1); 3440168404Spjd} 3441168404Spjd 3442168404Spjd/* 3443168404Spjd * zfs set property=value { fs | snap | vol } ... 3444168404Spjd * 3445168404Spjd * Sets the given property for all datasets specified on the command line. 3446168404Spjd */ 3447168404Spjdtypedef struct set_cbdata { 3448168404Spjd char *cb_propname; 3449168404Spjd char *cb_value; 3450168404Spjd} set_cbdata_t; 3451168404Spjd 3452168404Spjdstatic int 3453168404Spjdset_callback(zfs_handle_t *zhp, void *data) 3454168404Spjd{ 3455168404Spjd set_cbdata_t *cbp = data; 3456168404Spjd 3457168404Spjd if (zfs_prop_set(zhp, cbp->cb_propname, cbp->cb_value) != 0) { 3458168404Spjd switch (libzfs_errno(g_zfs)) { 3459168404Spjd case EZFS_MOUNTFAILED: 3460168404Spjd (void) fprintf(stderr, gettext("property may be set " 3461168404Spjd "but unable to remount filesystem\n")); 3462168404Spjd break; 3463168404Spjd case EZFS_SHARENFSFAILED: 3464168404Spjd (void) fprintf(stderr, gettext("property may be set " 3465168404Spjd "but unable to reshare filesystem\n")); 3466168404Spjd break; 3467168404Spjd } 3468168404Spjd return (1); 3469168404Spjd } 3470168404Spjd return (0); 3471168404Spjd} 3472168404Spjd 3473168404Spjdstatic int 3474168404Spjdzfs_do_set(int argc, char **argv) 3475168404Spjd{ 3476168404Spjd set_cbdata_t cb; 3477231144Smm int ret = 0; 3478168404Spjd 3479168404Spjd /* check for options */ 3480168404Spjd if (argc > 1 && argv[1][0] == '-') { 3481168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3482168404Spjd argv[1][1]); 3483168404Spjd usage(B_FALSE); 3484168404Spjd } 3485168404Spjd 3486168404Spjd /* check number of arguments */ 3487168404Spjd if (argc < 2) { 3488168404Spjd (void) fprintf(stderr, gettext("missing property=value " 3489168404Spjd "argument\n")); 3490168404Spjd usage(B_FALSE); 3491168404Spjd } 3492168404Spjd if (argc < 3) { 3493168404Spjd (void) fprintf(stderr, gettext("missing dataset name\n")); 3494168404Spjd usage(B_FALSE); 3495168404Spjd } 3496168404Spjd 3497168404Spjd /* validate property=value argument */ 3498168404Spjd cb.cb_propname = argv[1]; 3499185029Spjd if (((cb.cb_value = strchr(cb.cb_propname, '=')) == NULL) || 3500185029Spjd (cb.cb_value[1] == '\0')) { 3501168404Spjd (void) fprintf(stderr, gettext("missing value in " 3502168404Spjd "property=value argument\n")); 3503168404Spjd usage(B_FALSE); 3504168404Spjd } 3505168404Spjd 3506168404Spjd *cb.cb_value = '\0'; 3507168404Spjd cb.cb_value++; 3508168404Spjd 3509168404Spjd if (*cb.cb_propname == '\0') { 3510168404Spjd (void) fprintf(stderr, 3511168404Spjd gettext("missing property in property=value argument\n")); 3512168404Spjd usage(B_FALSE); 3513168404Spjd } 3514168404Spjd 3515209962Smm ret = zfs_for_each(argc - 2, argv + 2, 0, 3516205199Sdelphij ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, &cb); 3517168404Spjd 3518168404Spjd return (ret); 3519168404Spjd} 3520168404Spjd 3521248571Smmtypedef struct snap_cbdata { 3522248571Smm nvlist_t *sd_nvl; 3523248571Smm boolean_t sd_recursive; 3524248571Smm const char *sd_snapname; 3525248571Smm} snap_cbdata_t; 3526248571Smm 3527248571Smmstatic int 3528248571Smmzfs_snapshot_cb(zfs_handle_t *zhp, void *arg) 3529248571Smm{ 3530248571Smm snap_cbdata_t *sd = arg; 3531248571Smm char *name; 3532248571Smm int rv = 0; 3533248571Smm int error; 3534248571Smm 3535253819Sdelphij if (sd->sd_recursive && 3536253819Sdelphij zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) != 0) { 3537253819Sdelphij zfs_close(zhp); 3538253819Sdelphij return (0); 3539253819Sdelphij } 3540253819Sdelphij 3541248571Smm error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname); 3542248571Smm if (error == -1) 3543248571Smm nomem(); 3544248571Smm fnvlist_add_boolean(sd->sd_nvl, name); 3545248571Smm free(name); 3546248571Smm 3547248571Smm if (sd->sd_recursive) 3548248571Smm rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd); 3549248571Smm zfs_close(zhp); 3550248571Smm return (rv); 3551248571Smm} 3552248571Smm 3553168404Spjd/* 3554185029Spjd * zfs snapshot [-r] [-o prop=value] ... <fs@snap> 3555168404Spjd * 3556168404Spjd * Creates a snapshot with the given name. While functionally equivalent to 3557185029Spjd * 'zfs create', it is a separate command to differentiate intent. 3558168404Spjd */ 3559168404Spjdstatic int 3560168404Spjdzfs_do_snapshot(int argc, char **argv) 3561168404Spjd{ 3562231144Smm int ret = 0; 3563168404Spjd char c; 3564185029Spjd nvlist_t *props; 3565248571Smm snap_cbdata_t sd = { 0 }; 3566248571Smm boolean_t multiple_snaps = B_FALSE; 3567168404Spjd 3568219089Spjd if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 3569219089Spjd nomem(); 3570248571Smm if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0) 3571248571Smm nomem(); 3572185029Spjd 3573168404Spjd /* check options */ 3574185029Spjd while ((c = getopt(argc, argv, "ro:")) != -1) { 3575168404Spjd switch (c) { 3576185029Spjd case 'o': 3577185029Spjd if (parseprop(props)) 3578185029Spjd return (1); 3579185029Spjd break; 3580168404Spjd case 'r': 3581248571Smm sd.sd_recursive = B_TRUE; 3582248571Smm multiple_snaps = B_TRUE; 3583168404Spjd break; 3584168404Spjd case '?': 3585168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3586168404Spjd optopt); 3587185029Spjd goto usage; 3588168404Spjd } 3589168404Spjd } 3590168404Spjd 3591168404Spjd argc -= optind; 3592168404Spjd argv += optind; 3593168404Spjd 3594168404Spjd /* check number of arguments */ 3595168404Spjd if (argc < 1) { 3596168404Spjd (void) fprintf(stderr, gettext("missing snapshot argument\n")); 3597185029Spjd goto usage; 3598168404Spjd } 3599248571Smm 3600248571Smm if (argc > 1) 3601248571Smm multiple_snaps = B_TRUE; 3602248571Smm for (; argc > 0; argc--, argv++) { 3603248571Smm char *atp; 3604248571Smm zfs_handle_t *zhp; 3605248571Smm 3606248571Smm atp = strchr(argv[0], '@'); 3607248571Smm if (atp == NULL) 3608248571Smm goto usage; 3609248571Smm *atp = '\0'; 3610248571Smm sd.sd_snapname = atp + 1; 3611248571Smm zhp = zfs_open(g_zfs, argv[0], 3612248571Smm ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 3613248571Smm if (zhp == NULL) 3614248571Smm goto usage; 3615248571Smm if (zfs_snapshot_cb(zhp, &sd) != 0) 3616248571Smm goto usage; 3617168404Spjd } 3618168404Spjd 3619248571Smm ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props); 3620248571Smm nvlist_free(sd.sd_nvl); 3621185029Spjd nvlist_free(props); 3622248571Smm if (ret != 0 && multiple_snaps) 3623168404Spjd (void) fprintf(stderr, gettext("no snapshots were created\n")); 3624168404Spjd return (ret != 0); 3625185029Spjd 3626185029Spjdusage: 3627248571Smm nvlist_free(sd.sd_nvl); 3628185029Spjd nvlist_free(props); 3629185029Spjd usage(B_FALSE); 3630185029Spjd return (-1); 3631168404Spjd} 3632168404Spjd 3633168404Spjd/* 3634168404Spjd * Send a backup stream to stdout. 3635168404Spjd */ 3636168404Spjdstatic int 3637168404Spjdzfs_do_send(int argc, char **argv) 3638168404Spjd{ 3639168404Spjd char *fromname = NULL; 3640185029Spjd char *toname = NULL; 3641168404Spjd char *cp; 3642168404Spjd zfs_handle_t *zhp; 3643219089Spjd sendflags_t flags = { 0 }; 3644168404Spjd int c, err; 3645228103Smm nvlist_t *dbgnv = NULL; 3646219089Spjd boolean_t extraverbose = B_FALSE; 3647168404Spjd 3648168404Spjd /* check options */ 3649228103Smm while ((c = getopt(argc, argv, ":i:I:RDpvnP")) != -1) { 3650168404Spjd switch (c) { 3651168404Spjd case 'i': 3652168404Spjd if (fromname) 3653168404Spjd usage(B_FALSE); 3654168404Spjd fromname = optarg; 3655168404Spjd break; 3656185029Spjd case 'I': 3657185029Spjd if (fromname) 3658185029Spjd usage(B_FALSE); 3659185029Spjd fromname = optarg; 3660219089Spjd flags.doall = B_TRUE; 3661185029Spjd break; 3662185029Spjd case 'R': 3663219089Spjd flags.replicate = B_TRUE; 3664185029Spjd break; 3665219089Spjd case 'p': 3666219089Spjd flags.props = B_TRUE; 3667219089Spjd break; 3668228103Smm case 'P': 3669228103Smm flags.parsable = B_TRUE; 3670228103Smm flags.verbose = B_TRUE; 3671228103Smm break; 3672185029Spjd case 'v': 3673219089Spjd if (flags.verbose) 3674219089Spjd extraverbose = B_TRUE; 3675219089Spjd flags.verbose = B_TRUE; 3676235222Smm flags.progress = B_TRUE; 3677185029Spjd break; 3678219089Spjd case 'D': 3679219089Spjd flags.dedup = B_TRUE; 3680219089Spjd break; 3681228103Smm case 'n': 3682228103Smm flags.dryrun = B_TRUE; 3683228103Smm break; 3684168404Spjd case ':': 3685168404Spjd (void) fprintf(stderr, gettext("missing argument for " 3686168404Spjd "'%c' option\n"), optopt); 3687168404Spjd usage(B_FALSE); 3688168404Spjd break; 3689168404Spjd case '?': 3690168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3691168404Spjd optopt); 3692168404Spjd usage(B_FALSE); 3693168404Spjd } 3694168404Spjd } 3695168404Spjd 3696168404Spjd argc -= optind; 3697168404Spjd argv += optind; 3698168404Spjd 3699168404Spjd /* check number of arguments */ 3700168404Spjd if (argc < 1) { 3701168404Spjd (void) fprintf(stderr, gettext("missing snapshot argument\n")); 3702168404Spjd usage(B_FALSE); 3703168404Spjd } 3704168404Spjd if (argc > 1) { 3705168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 3706168404Spjd usage(B_FALSE); 3707168404Spjd } 3708168404Spjd 3709228103Smm if (!flags.dryrun && isatty(STDOUT_FILENO)) { 3710168404Spjd (void) fprintf(stderr, 3711168404Spjd gettext("Error: Stream can not be written to a terminal.\n" 3712168404Spjd "You must redirect standard output.\n")); 3713168404Spjd return (1); 3714168404Spjd } 3715168404Spjd 3716185029Spjd cp = strchr(argv[0], '@'); 3717185029Spjd if (cp == NULL) { 3718185029Spjd (void) fprintf(stderr, 3719185029Spjd gettext("argument must be a snapshot\n")); 3720185029Spjd usage(B_FALSE); 3721185029Spjd } 3722185029Spjd *cp = '\0'; 3723185029Spjd toname = cp + 1; 3724185029Spjd zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 3725185029Spjd if (zhp == NULL) 3726168404Spjd return (1); 3727168404Spjd 3728168404Spjd /* 3729168404Spjd * If they specified the full path to the snapshot, chop off 3730185029Spjd * everything except the short name of the snapshot, but special 3731185029Spjd * case if they specify the origin. 3732168404Spjd */ 3733168404Spjd if (fromname && (cp = strchr(fromname, '@')) != NULL) { 3734185029Spjd char origin[ZFS_MAXNAMELEN]; 3735185029Spjd zprop_source_t src; 3736185029Spjd 3737185029Spjd (void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN, 3738185029Spjd origin, sizeof (origin), &src, NULL, 0, B_FALSE); 3739185029Spjd 3740185029Spjd if (strcmp(origin, fromname) == 0) { 3741185029Spjd fromname = NULL; 3742219089Spjd flags.fromorigin = B_TRUE; 3743185029Spjd } else { 3744185029Spjd *cp = '\0'; 3745185029Spjd if (cp != fromname && strcmp(argv[0], fromname)) { 3746185029Spjd (void) fprintf(stderr, 3747185029Spjd gettext("incremental source must be " 3748185029Spjd "in same filesystem\n")); 3749185029Spjd usage(B_FALSE); 3750185029Spjd } 3751185029Spjd fromname = cp + 1; 3752185029Spjd if (strchr(fromname, '@') || strchr(fromname, '/')) { 3753185029Spjd (void) fprintf(stderr, 3754185029Spjd gettext("invalid incremental source\n")); 3755185029Spjd usage(B_FALSE); 3756185029Spjd } 3757168404Spjd } 3758168404Spjd } 3759168404Spjd 3760219089Spjd if (flags.replicate && fromname == NULL) 3761219089Spjd flags.doall = B_TRUE; 3762185029Spjd 3763228103Smm err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO, NULL, 0, 3764219089Spjd extraverbose ? &dbgnv : NULL); 3765219089Spjd 3766228103Smm if (extraverbose && dbgnv != NULL) { 3767219089Spjd /* 3768219089Spjd * dump_nvlist prints to stdout, but that's been 3769219089Spjd * redirected to a file. Make it print to stderr 3770219089Spjd * instead. 3771219089Spjd */ 3772219089Spjd (void) dup2(STDERR_FILENO, STDOUT_FILENO); 3773219089Spjd dump_nvlist(dbgnv, 0); 3774219089Spjd nvlist_free(dbgnv); 3775219089Spjd } 3776168404Spjd zfs_close(zhp); 3777168404Spjd 3778168404Spjd return (err != 0); 3779168404Spjd} 3780168404Spjd 3781168404Spjd/* 3782219089Spjd * zfs receive [-vnFu] [-d | -e] <fs@snap> 3783168404Spjd * 3784168404Spjd * Restore a backup stream from stdin. 3785168404Spjd */ 3786168404Spjdstatic int 3787168404Spjdzfs_do_receive(int argc, char **argv) 3788168404Spjd{ 3789168404Spjd int c, err; 3790219089Spjd recvflags_t flags = { 0 }; 3791168404Spjd 3792168404Spjd /* check options */ 3793219089Spjd while ((c = getopt(argc, argv, ":denuvF")) != -1) { 3794168404Spjd switch (c) { 3795168404Spjd case 'd': 3796185029Spjd flags.isprefix = B_TRUE; 3797168404Spjd break; 3798219089Spjd case 'e': 3799219089Spjd flags.isprefix = B_TRUE; 3800219089Spjd flags.istail = B_TRUE; 3801219089Spjd break; 3802168404Spjd case 'n': 3803185029Spjd flags.dryrun = B_TRUE; 3804168404Spjd break; 3805200516Sdelphij case 'u': 3806200516Sdelphij flags.nomount = B_TRUE; 3807200516Sdelphij break; 3808168404Spjd case 'v': 3809185029Spjd flags.verbose = B_TRUE; 3810168404Spjd break; 3811168404Spjd case 'F': 3812185029Spjd flags.force = B_TRUE; 3813168404Spjd 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 3829168404Spjd /* check number of arguments */ 3830168404Spjd if (argc < 1) { 3831168404Spjd (void) fprintf(stderr, gettext("missing snapshot argument\n")); 3832168404Spjd usage(B_FALSE); 3833168404Spjd } 3834168404Spjd if (argc > 1) { 3835168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 3836168404Spjd usage(B_FALSE); 3837168404Spjd } 3838168404Spjd 3839168404Spjd if (isatty(STDIN_FILENO)) { 3840168404Spjd (void) fprintf(stderr, 3841168404Spjd gettext("Error: Backup stream can not be read " 3842168404Spjd "from a terminal.\n" 3843168404Spjd "You must redirect standard input.\n")); 3844168404Spjd return (1); 3845168404Spjd } 3846168404Spjd 3847228103Smm err = zfs_receive(g_zfs, argv[0], &flags, STDIN_FILENO, NULL); 3848168404Spjd 3849185029Spjd return (err != 0); 3850185029Spjd} 3851185029Spjd 3852219089Spjd/* 3853219089Spjd * allow/unallow stuff 3854219089Spjd */ 3855219089Spjd/* copied from zfs/sys/dsl_deleg.h */ 3856219089Spjd#define ZFS_DELEG_PERM_CREATE "create" 3857219089Spjd#define ZFS_DELEG_PERM_DESTROY "destroy" 3858219089Spjd#define ZFS_DELEG_PERM_SNAPSHOT "snapshot" 3859219089Spjd#define ZFS_DELEG_PERM_ROLLBACK "rollback" 3860219089Spjd#define ZFS_DELEG_PERM_CLONE "clone" 3861219089Spjd#define ZFS_DELEG_PERM_PROMOTE "promote" 3862219089Spjd#define ZFS_DELEG_PERM_RENAME "rename" 3863219089Spjd#define ZFS_DELEG_PERM_MOUNT "mount" 3864219089Spjd#define ZFS_DELEG_PERM_SHARE "share" 3865219089Spjd#define ZFS_DELEG_PERM_SEND "send" 3866219089Spjd#define ZFS_DELEG_PERM_RECEIVE "receive" 3867219089Spjd#define ZFS_DELEG_PERM_ALLOW "allow" 3868219089Spjd#define ZFS_DELEG_PERM_USERPROP "userprop" 3869219089Spjd#define ZFS_DELEG_PERM_VSCAN "vscan" /* ??? */ 3870219089Spjd#define ZFS_DELEG_PERM_USERQUOTA "userquota" 3871219089Spjd#define ZFS_DELEG_PERM_GROUPQUOTA "groupquota" 3872219089Spjd#define ZFS_DELEG_PERM_USERUSED "userused" 3873219089Spjd#define ZFS_DELEG_PERM_GROUPUSED "groupused" 3874219089Spjd#define ZFS_DELEG_PERM_HOLD "hold" 3875219089Spjd#define ZFS_DELEG_PERM_RELEASE "release" 3876219089Spjd#define ZFS_DELEG_PERM_DIFF "diff" 3877168404Spjd 3878219089Spjd#define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE 3879219089Spjd 3880219089Spjdstatic zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = { 3881219089Spjd { ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW }, 3882219089Spjd { ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE }, 3883219089Spjd { ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE }, 3884219089Spjd { ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY }, 3885219089Spjd { ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF}, 3886219089Spjd { ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD }, 3887219089Spjd { ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT }, 3888219089Spjd { ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE }, 3889219089Spjd { ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE }, 3890219089Spjd { ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE }, 3891219089Spjd { ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME }, 3892219089Spjd { ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK }, 3893219089Spjd { ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND }, 3894219089Spjd { ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE }, 3895219089Spjd { ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT }, 3896219089Spjd 3897219089Spjd { ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA }, 3898219089Spjd { ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED }, 3899219089Spjd { ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP }, 3900219089Spjd { ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA }, 3901219089Spjd { ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED }, 3902219089Spjd { NULL, ZFS_DELEG_NOTE_NONE } 3903219089Spjd}; 3904219089Spjd 3905219089Spjd/* permission structure */ 3906219089Spjdtypedef struct deleg_perm { 3907219089Spjd zfs_deleg_who_type_t dp_who_type; 3908219089Spjd const char *dp_name; 3909219089Spjd boolean_t dp_local; 3910219089Spjd boolean_t dp_descend; 3911219089Spjd} deleg_perm_t; 3912219089Spjd 3913219089Spjd/* */ 3914219089Spjdtypedef struct deleg_perm_node { 3915219089Spjd deleg_perm_t dpn_perm; 3916219089Spjd 3917219089Spjd uu_avl_node_t dpn_avl_node; 3918219089Spjd} deleg_perm_node_t; 3919219089Spjd 3920219089Spjdtypedef struct fs_perm fs_perm_t; 3921219089Spjd 3922219089Spjd/* permissions set */ 3923219089Spjdtypedef struct who_perm { 3924219089Spjd zfs_deleg_who_type_t who_type; 3925219089Spjd const char *who_name; /* id */ 3926219089Spjd char who_ug_name[256]; /* user/group name */ 3927219089Spjd fs_perm_t *who_fsperm; /* uplink */ 3928219089Spjd 3929219089Spjd uu_avl_t *who_deleg_perm_avl; /* permissions */ 3930219089Spjd} who_perm_t; 3931219089Spjd 3932219089Spjd/* */ 3933219089Spjdtypedef struct who_perm_node { 3934219089Spjd who_perm_t who_perm; 3935219089Spjd uu_avl_node_t who_avl_node; 3936219089Spjd} who_perm_node_t; 3937219089Spjd 3938219089Spjdtypedef struct fs_perm_set fs_perm_set_t; 3939219089Spjd/* fs permissions */ 3940219089Spjdstruct fs_perm { 3941219089Spjd const char *fsp_name; 3942219089Spjd 3943219089Spjd uu_avl_t *fsp_sc_avl; /* sets,create */ 3944219089Spjd uu_avl_t *fsp_uge_avl; /* user,group,everyone */ 3945219089Spjd 3946219089Spjd fs_perm_set_t *fsp_set; /* uplink */ 3947219089Spjd}; 3948219089Spjd 3949219089Spjd/* */ 3950219089Spjdtypedef struct fs_perm_node { 3951219089Spjd fs_perm_t fspn_fsperm; 3952219089Spjd uu_avl_t *fspn_avl; 3953219089Spjd 3954219089Spjd uu_list_node_t fspn_list_node; 3955219089Spjd} fs_perm_node_t; 3956219089Spjd 3957219089Spjd/* top level structure */ 3958219089Spjdstruct fs_perm_set { 3959219089Spjd uu_list_pool_t *fsps_list_pool; 3960219089Spjd uu_list_t *fsps_list; /* list of fs_perms */ 3961219089Spjd 3962219089Spjd uu_avl_pool_t *fsps_named_set_avl_pool; 3963219089Spjd uu_avl_pool_t *fsps_who_perm_avl_pool; 3964219089Spjd uu_avl_pool_t *fsps_deleg_perm_avl_pool; 3965219089Spjd}; 3966219089Spjd 3967219089Spjdstatic inline const char * 3968219089Spjddeleg_perm_type(zfs_deleg_note_t note) 3969219089Spjd{ 3970219089Spjd /* subcommands */ 3971219089Spjd switch (note) { 3972219089Spjd /* SUBCOMMANDS */ 3973219089Spjd /* OTHER */ 3974219089Spjd case ZFS_DELEG_NOTE_GROUPQUOTA: 3975219089Spjd case ZFS_DELEG_NOTE_GROUPUSED: 3976219089Spjd case ZFS_DELEG_NOTE_USERPROP: 3977219089Spjd case ZFS_DELEG_NOTE_USERQUOTA: 3978219089Spjd case ZFS_DELEG_NOTE_USERUSED: 3979219089Spjd /* other */ 3980219089Spjd return (gettext("other")); 3981219089Spjd default: 3982219089Spjd return (gettext("subcommand")); 3983219089Spjd } 3984219089Spjd} 3985219089Spjd 3986219089Spjdstatic int inline 3987219089Spjdwho_type2weight(zfs_deleg_who_type_t who_type) 3988219089Spjd{ 3989219089Spjd int res; 3990219089Spjd switch (who_type) { 3991219089Spjd case ZFS_DELEG_NAMED_SET_SETS: 3992219089Spjd case ZFS_DELEG_NAMED_SET: 3993219089Spjd res = 0; 3994219089Spjd break; 3995219089Spjd case ZFS_DELEG_CREATE_SETS: 3996219089Spjd case ZFS_DELEG_CREATE: 3997219089Spjd res = 1; 3998219089Spjd break; 3999219089Spjd case ZFS_DELEG_USER_SETS: 4000219089Spjd case ZFS_DELEG_USER: 4001219089Spjd res = 2; 4002219089Spjd break; 4003219089Spjd case ZFS_DELEG_GROUP_SETS: 4004219089Spjd case ZFS_DELEG_GROUP: 4005219089Spjd res = 3; 4006219089Spjd break; 4007219089Spjd case ZFS_DELEG_EVERYONE_SETS: 4008219089Spjd case ZFS_DELEG_EVERYONE: 4009219089Spjd res = 4; 4010219089Spjd break; 4011219089Spjd default: 4012219089Spjd res = -1; 4013219089Spjd } 4014219089Spjd 4015219089Spjd return (res); 4016219089Spjd} 4017219089Spjd 4018219089Spjd/* ARGSUSED */ 4019219089Spjdstatic int 4020219089Spjdwho_perm_compare(const void *larg, const void *rarg, void *unused) 4021219089Spjd{ 4022219089Spjd const who_perm_node_t *l = larg; 4023219089Spjd const who_perm_node_t *r = rarg; 4024219089Spjd zfs_deleg_who_type_t ltype = l->who_perm.who_type; 4025219089Spjd zfs_deleg_who_type_t rtype = r->who_perm.who_type; 4026219089Spjd int lweight = who_type2weight(ltype); 4027219089Spjd int rweight = who_type2weight(rtype); 4028219089Spjd int res = lweight - rweight; 4029219089Spjd if (res == 0) 4030219089Spjd res = strncmp(l->who_perm.who_name, r->who_perm.who_name, 4031219089Spjd ZFS_MAX_DELEG_NAME-1); 4032219089Spjd 4033219089Spjd if (res == 0) 4034219089Spjd return (0); 4035219089Spjd if (res > 0) 4036219089Spjd return (1); 4037219089Spjd else 4038219089Spjd return (-1); 4039219089Spjd} 4040219089Spjd 4041219089Spjd/* ARGSUSED */ 4042219089Spjdstatic int 4043219089Spjddeleg_perm_compare(const void *larg, const void *rarg, void *unused) 4044219089Spjd{ 4045219089Spjd const deleg_perm_node_t *l = larg; 4046219089Spjd const deleg_perm_node_t *r = rarg; 4047219089Spjd int res = strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name, 4048219089Spjd ZFS_MAX_DELEG_NAME-1); 4049219089Spjd 4050219089Spjd if (res == 0) 4051219089Spjd return (0); 4052219089Spjd 4053219089Spjd if (res > 0) 4054219089Spjd return (1); 4055219089Spjd else 4056219089Spjd return (-1); 4057219089Spjd} 4058219089Spjd 4059219089Spjdstatic inline void 4060219089Spjdfs_perm_set_init(fs_perm_set_t *fspset) 4061219089Spjd{ 4062219089Spjd bzero(fspset, sizeof (fs_perm_set_t)); 4063219089Spjd 4064219089Spjd if ((fspset->fsps_list_pool = uu_list_pool_create("fsps_list_pool", 4065219089Spjd sizeof (fs_perm_node_t), offsetof(fs_perm_node_t, fspn_list_node), 4066219089Spjd NULL, UU_DEFAULT)) == NULL) 4067219089Spjd nomem(); 4068219089Spjd if ((fspset->fsps_list = uu_list_create(fspset->fsps_list_pool, NULL, 4069219089Spjd UU_DEFAULT)) == NULL) 4070219089Spjd nomem(); 4071219089Spjd 4072219089Spjd if ((fspset->fsps_named_set_avl_pool = uu_avl_pool_create( 4073219089Spjd "named_set_avl_pool", sizeof (who_perm_node_t), offsetof( 4074219089Spjd who_perm_node_t, who_avl_node), who_perm_compare, 4075219089Spjd UU_DEFAULT)) == NULL) 4076219089Spjd nomem(); 4077219089Spjd 4078219089Spjd if ((fspset->fsps_who_perm_avl_pool = uu_avl_pool_create( 4079219089Spjd "who_perm_avl_pool", sizeof (who_perm_node_t), offsetof( 4080219089Spjd who_perm_node_t, who_avl_node), who_perm_compare, 4081219089Spjd UU_DEFAULT)) == NULL) 4082219089Spjd nomem(); 4083219089Spjd 4084219089Spjd if ((fspset->fsps_deleg_perm_avl_pool = uu_avl_pool_create( 4085219089Spjd "deleg_perm_avl_pool", sizeof (deleg_perm_node_t), offsetof( 4086219089Spjd deleg_perm_node_t, dpn_avl_node), deleg_perm_compare, UU_DEFAULT)) 4087219089Spjd == NULL) 4088219089Spjd nomem(); 4089219089Spjd} 4090219089Spjd 4091219089Spjdstatic inline void fs_perm_fini(fs_perm_t *); 4092219089Spjdstatic inline void who_perm_fini(who_perm_t *); 4093219089Spjd 4094219089Spjdstatic inline void 4095219089Spjdfs_perm_set_fini(fs_perm_set_t *fspset) 4096219089Spjd{ 4097219089Spjd fs_perm_node_t *node = uu_list_first(fspset->fsps_list); 4098219089Spjd 4099219089Spjd while (node != NULL) { 4100219089Spjd fs_perm_node_t *next_node = 4101219089Spjd uu_list_next(fspset->fsps_list, node); 4102219089Spjd fs_perm_t *fsperm = &node->fspn_fsperm; 4103219089Spjd fs_perm_fini(fsperm); 4104219089Spjd uu_list_remove(fspset->fsps_list, node); 4105219089Spjd free(node); 4106219089Spjd node = next_node; 4107219089Spjd } 4108219089Spjd 4109219089Spjd uu_avl_pool_destroy(fspset->fsps_named_set_avl_pool); 4110219089Spjd uu_avl_pool_destroy(fspset->fsps_who_perm_avl_pool); 4111219089Spjd uu_avl_pool_destroy(fspset->fsps_deleg_perm_avl_pool); 4112219089Spjd} 4113219089Spjd 4114219089Spjdstatic inline void 4115219089Spjddeleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type, 4116219089Spjd const char *name) 4117219089Spjd{ 4118219089Spjd deleg_perm->dp_who_type = type; 4119219089Spjd deleg_perm->dp_name = name; 4120219089Spjd} 4121219089Spjd 4122219089Spjdstatic inline void 4123219089Spjdwho_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm, 4124219089Spjd zfs_deleg_who_type_t type, const char *name) 4125219089Spjd{ 4126219089Spjd uu_avl_pool_t *pool; 4127219089Spjd pool = fsperm->fsp_set->fsps_deleg_perm_avl_pool; 4128219089Spjd 4129219089Spjd bzero(who_perm, sizeof (who_perm_t)); 4130219089Spjd 4131219089Spjd if ((who_perm->who_deleg_perm_avl = uu_avl_create(pool, NULL, 4132219089Spjd UU_DEFAULT)) == NULL) 4133219089Spjd nomem(); 4134219089Spjd 4135219089Spjd who_perm->who_type = type; 4136219089Spjd who_perm->who_name = name; 4137219089Spjd who_perm->who_fsperm = fsperm; 4138219089Spjd} 4139219089Spjd 4140219089Spjdstatic inline void 4141219089Spjdwho_perm_fini(who_perm_t *who_perm) 4142219089Spjd{ 4143219089Spjd deleg_perm_node_t *node = uu_avl_first(who_perm->who_deleg_perm_avl); 4144219089Spjd 4145219089Spjd while (node != NULL) { 4146219089Spjd deleg_perm_node_t *next_node = 4147219089Spjd uu_avl_next(who_perm->who_deleg_perm_avl, node); 4148219089Spjd 4149219089Spjd uu_avl_remove(who_perm->who_deleg_perm_avl, node); 4150219089Spjd free(node); 4151219089Spjd node = next_node; 4152219089Spjd } 4153219089Spjd 4154219089Spjd uu_avl_destroy(who_perm->who_deleg_perm_avl); 4155219089Spjd} 4156219089Spjd 4157219089Spjdstatic inline void 4158219089Spjdfs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname) 4159219089Spjd{ 4160219089Spjd uu_avl_pool_t *nset_pool = fspset->fsps_named_set_avl_pool; 4161219089Spjd uu_avl_pool_t *who_pool = fspset->fsps_who_perm_avl_pool; 4162219089Spjd 4163219089Spjd bzero(fsperm, sizeof (fs_perm_t)); 4164219089Spjd 4165219089Spjd if ((fsperm->fsp_sc_avl = uu_avl_create(nset_pool, NULL, UU_DEFAULT)) 4166219089Spjd == NULL) 4167219089Spjd nomem(); 4168219089Spjd 4169219089Spjd if ((fsperm->fsp_uge_avl = uu_avl_create(who_pool, NULL, UU_DEFAULT)) 4170219089Spjd == NULL) 4171219089Spjd nomem(); 4172219089Spjd 4173219089Spjd fsperm->fsp_set = fspset; 4174219089Spjd fsperm->fsp_name = fsname; 4175219089Spjd} 4176219089Spjd 4177219089Spjdstatic inline void 4178219089Spjdfs_perm_fini(fs_perm_t *fsperm) 4179219089Spjd{ 4180219089Spjd who_perm_node_t *node = uu_avl_first(fsperm->fsp_sc_avl); 4181219089Spjd while (node != NULL) { 4182219089Spjd who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_sc_avl, 4183219089Spjd node); 4184219089Spjd who_perm_t *who_perm = &node->who_perm; 4185219089Spjd who_perm_fini(who_perm); 4186219089Spjd uu_avl_remove(fsperm->fsp_sc_avl, node); 4187219089Spjd free(node); 4188219089Spjd node = next_node; 4189219089Spjd } 4190219089Spjd 4191219089Spjd node = uu_avl_first(fsperm->fsp_uge_avl); 4192219089Spjd while (node != NULL) { 4193219089Spjd who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_uge_avl, 4194219089Spjd node); 4195219089Spjd who_perm_t *who_perm = &node->who_perm; 4196219089Spjd who_perm_fini(who_perm); 4197219089Spjd uu_avl_remove(fsperm->fsp_uge_avl, node); 4198219089Spjd free(node); 4199219089Spjd node = next_node; 4200219089Spjd } 4201219089Spjd 4202219089Spjd uu_avl_destroy(fsperm->fsp_sc_avl); 4203219089Spjd uu_avl_destroy(fsperm->fsp_uge_avl); 4204219089Spjd} 4205219089Spjd 4206219089Spjdstatic void inline 4207219089Spjdset_deleg_perm_node(uu_avl_t *avl, deleg_perm_node_t *node, 4208219089Spjd zfs_deleg_who_type_t who_type, const char *name, char locality) 4209219089Spjd{ 4210219089Spjd uu_avl_index_t idx = 0; 4211219089Spjd 4212219089Spjd deleg_perm_node_t *found_node = NULL; 4213219089Spjd deleg_perm_t *deleg_perm = &node->dpn_perm; 4214219089Spjd 4215219089Spjd deleg_perm_init(deleg_perm, who_type, name); 4216219089Spjd 4217219089Spjd if ((found_node = uu_avl_find(avl, node, NULL, &idx)) 4218219089Spjd == NULL) 4219219089Spjd uu_avl_insert(avl, node, idx); 4220219089Spjd else { 4221219089Spjd node = found_node; 4222219089Spjd deleg_perm = &node->dpn_perm; 4223219089Spjd } 4224219089Spjd 4225219089Spjd 4226219089Spjd switch (locality) { 4227219089Spjd case ZFS_DELEG_LOCAL: 4228219089Spjd deleg_perm->dp_local = B_TRUE; 4229219089Spjd break; 4230219089Spjd case ZFS_DELEG_DESCENDENT: 4231219089Spjd deleg_perm->dp_descend = B_TRUE; 4232219089Spjd break; 4233219089Spjd case ZFS_DELEG_NA: 4234219089Spjd break; 4235219089Spjd default: 4236219089Spjd assert(B_FALSE); /* invalid locality */ 4237219089Spjd } 4238219089Spjd} 4239219089Spjd 4240219089Spjdstatic inline int 4241219089Spjdparse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality) 4242219089Spjd{ 4243219089Spjd nvpair_t *nvp = NULL; 4244219089Spjd fs_perm_set_t *fspset = who_perm->who_fsperm->fsp_set; 4245219089Spjd uu_avl_t *avl = who_perm->who_deleg_perm_avl; 4246219089Spjd zfs_deleg_who_type_t who_type = who_perm->who_type; 4247219089Spjd 4248219089Spjd while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 4249219089Spjd const char *name = nvpair_name(nvp); 4250219089Spjd data_type_t type = nvpair_type(nvp); 4251219089Spjd uu_avl_pool_t *avl_pool = fspset->fsps_deleg_perm_avl_pool; 4252219089Spjd deleg_perm_node_t *node = 4253219089Spjd safe_malloc(sizeof (deleg_perm_node_t)); 4254219089Spjd 4255219089Spjd assert(type == DATA_TYPE_BOOLEAN); 4256219089Spjd 4257219089Spjd uu_avl_node_init(node, &node->dpn_avl_node, avl_pool); 4258219089Spjd set_deleg_perm_node(avl, node, who_type, name, locality); 4259219089Spjd } 4260219089Spjd 4261219089Spjd return (0); 4262219089Spjd} 4263219089Spjd 4264219089Spjdstatic inline int 4265219089Spjdparse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl) 4266219089Spjd{ 4267219089Spjd nvpair_t *nvp = NULL; 4268219089Spjd fs_perm_set_t *fspset = fsperm->fsp_set; 4269219089Spjd 4270219089Spjd while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 4271219089Spjd nvlist_t *nvl2 = NULL; 4272219089Spjd const char *name = nvpair_name(nvp); 4273219089Spjd uu_avl_t *avl = NULL; 4274219089Spjd uu_avl_pool_t *avl_pool; 4275219089Spjd zfs_deleg_who_type_t perm_type = name[0]; 4276219089Spjd char perm_locality = name[1]; 4277219089Spjd const char *perm_name = name + 3; 4278219089Spjd boolean_t is_set = B_TRUE; 4279219089Spjd who_perm_t *who_perm = NULL; 4280219089Spjd 4281219089Spjd assert('$' == name[2]); 4282219089Spjd 4283219089Spjd if (nvpair_value_nvlist(nvp, &nvl2) != 0) 4284219089Spjd return (-1); 4285219089Spjd 4286219089Spjd switch (perm_type) { 4287219089Spjd case ZFS_DELEG_CREATE: 4288219089Spjd case ZFS_DELEG_CREATE_SETS: 4289219089Spjd case ZFS_DELEG_NAMED_SET: 4290219089Spjd case ZFS_DELEG_NAMED_SET_SETS: 4291219089Spjd avl_pool = fspset->fsps_named_set_avl_pool; 4292219089Spjd avl = fsperm->fsp_sc_avl; 4293219089Spjd break; 4294219089Spjd case ZFS_DELEG_USER: 4295219089Spjd case ZFS_DELEG_USER_SETS: 4296219089Spjd case ZFS_DELEG_GROUP: 4297219089Spjd case ZFS_DELEG_GROUP_SETS: 4298219089Spjd case ZFS_DELEG_EVERYONE: 4299219089Spjd case ZFS_DELEG_EVERYONE_SETS: 4300219089Spjd avl_pool = fspset->fsps_who_perm_avl_pool; 4301219089Spjd avl = fsperm->fsp_uge_avl; 4302219089Spjd break; 4303219089Spjd } 4304219089Spjd 4305219089Spjd if (is_set) { 4306219089Spjd who_perm_node_t *found_node = NULL; 4307219089Spjd who_perm_node_t *node = safe_malloc( 4308219089Spjd sizeof (who_perm_node_t)); 4309219089Spjd who_perm = &node->who_perm; 4310219089Spjd uu_avl_index_t idx = 0; 4311219089Spjd 4312219089Spjd uu_avl_node_init(node, &node->who_avl_node, avl_pool); 4313219089Spjd who_perm_init(who_perm, fsperm, perm_type, perm_name); 4314219089Spjd 4315219089Spjd if ((found_node = uu_avl_find(avl, node, NULL, &idx)) 4316219089Spjd == NULL) { 4317219089Spjd if (avl == fsperm->fsp_uge_avl) { 4318219089Spjd uid_t rid = 0; 4319219089Spjd struct passwd *p = NULL; 4320219089Spjd struct group *g = NULL; 4321219089Spjd const char *nice_name = NULL; 4322219089Spjd 4323219089Spjd switch (perm_type) { 4324219089Spjd case ZFS_DELEG_USER_SETS: 4325219089Spjd case ZFS_DELEG_USER: 4326219089Spjd rid = atoi(perm_name); 4327219089Spjd p = getpwuid(rid); 4328219089Spjd if (p) 4329219089Spjd nice_name = p->pw_name; 4330219089Spjd break; 4331219089Spjd case ZFS_DELEG_GROUP_SETS: 4332219089Spjd case ZFS_DELEG_GROUP: 4333219089Spjd rid = atoi(perm_name); 4334219089Spjd g = getgrgid(rid); 4335219089Spjd if (g) 4336219089Spjd nice_name = g->gr_name; 4337219089Spjd break; 4338219089Spjd } 4339219089Spjd 4340219089Spjd if (nice_name != NULL) 4341219089Spjd (void) strlcpy( 4342219089Spjd node->who_perm.who_ug_name, 4343219089Spjd nice_name, 256); 4344219089Spjd } 4345219089Spjd 4346219089Spjd uu_avl_insert(avl, node, idx); 4347219089Spjd } else { 4348219089Spjd node = found_node; 4349219089Spjd who_perm = &node->who_perm; 4350219089Spjd } 4351219089Spjd } 4352219089Spjd 4353219089Spjd (void) parse_who_perm(who_perm, nvl2, perm_locality); 4354219089Spjd } 4355219089Spjd 4356219089Spjd return (0); 4357219089Spjd} 4358219089Spjd 4359219089Spjdstatic inline int 4360219089Spjdparse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl) 4361219089Spjd{ 4362219089Spjd nvpair_t *nvp = NULL; 4363219089Spjd uu_avl_index_t idx = 0; 4364219089Spjd 4365219089Spjd while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 4366219089Spjd nvlist_t *nvl2 = NULL; 4367219089Spjd const char *fsname = nvpair_name(nvp); 4368219089Spjd data_type_t type = nvpair_type(nvp); 4369219089Spjd fs_perm_t *fsperm = NULL; 4370219089Spjd fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t)); 4371219089Spjd if (node == NULL) 4372219089Spjd nomem(); 4373219089Spjd 4374219089Spjd fsperm = &node->fspn_fsperm; 4375219089Spjd 4376219089Spjd assert(DATA_TYPE_NVLIST == type); 4377219089Spjd 4378219089Spjd uu_list_node_init(node, &node->fspn_list_node, 4379219089Spjd fspset->fsps_list_pool); 4380219089Spjd 4381219089Spjd idx = uu_list_numnodes(fspset->fsps_list); 4382219089Spjd fs_perm_init(fsperm, fspset, fsname); 4383219089Spjd 4384219089Spjd if (nvpair_value_nvlist(nvp, &nvl2) != 0) 4385219089Spjd return (-1); 4386219089Spjd 4387219089Spjd (void) parse_fs_perm(fsperm, nvl2); 4388219089Spjd 4389219089Spjd uu_list_insert(fspset->fsps_list, node, idx); 4390219089Spjd } 4391219089Spjd 4392219089Spjd return (0); 4393219089Spjd} 4394219089Spjd 4395219089Spjdstatic inline const char * 4396219089Spjddeleg_perm_comment(zfs_deleg_note_t note) 4397219089Spjd{ 4398219089Spjd const char *str = ""; 4399219089Spjd 4400219089Spjd /* subcommands */ 4401219089Spjd switch (note) { 4402219089Spjd /* SUBCOMMANDS */ 4403219089Spjd case ZFS_DELEG_NOTE_ALLOW: 4404219089Spjd str = gettext("Must also have the permission that is being" 4405219089Spjd "\n\t\t\t\tallowed"); 4406219089Spjd break; 4407219089Spjd case ZFS_DELEG_NOTE_CLONE: 4408219089Spjd str = gettext("Must also have the 'create' ability and 'mount'" 4409219089Spjd "\n\t\t\t\tability in the origin file system"); 4410219089Spjd break; 4411219089Spjd case ZFS_DELEG_NOTE_CREATE: 4412219089Spjd str = gettext("Must also have the 'mount' ability"); 4413219089Spjd break; 4414219089Spjd case ZFS_DELEG_NOTE_DESTROY: 4415219089Spjd str = gettext("Must also have the 'mount' ability"); 4416219089Spjd break; 4417219089Spjd case ZFS_DELEG_NOTE_DIFF: 4418219089Spjd str = gettext("Allows lookup of paths within a dataset;" 4419219089Spjd "\n\t\t\t\tgiven an object number. Ordinary users need this" 4420219089Spjd "\n\t\t\t\tin order to use zfs diff"); 4421219089Spjd break; 4422219089Spjd case ZFS_DELEG_NOTE_HOLD: 4423219089Spjd str = gettext("Allows adding a user hold to a snapshot"); 4424219089Spjd break; 4425219089Spjd case ZFS_DELEG_NOTE_MOUNT: 4426219089Spjd str = gettext("Allows mount/umount of ZFS datasets"); 4427219089Spjd break; 4428219089Spjd case ZFS_DELEG_NOTE_PROMOTE: 4429219089Spjd str = gettext("Must also have the 'mount'\n\t\t\t\tand" 4430219089Spjd " 'promote' ability in the origin file system"); 4431219089Spjd break; 4432219089Spjd case ZFS_DELEG_NOTE_RECEIVE: 4433219089Spjd str = gettext("Must also have the 'mount' and 'create'" 4434219089Spjd " ability"); 4435219089Spjd break; 4436219089Spjd case ZFS_DELEG_NOTE_RELEASE: 4437219089Spjd str = gettext("Allows releasing a user hold which\n\t\t\t\t" 4438219089Spjd "might destroy the snapshot"); 4439219089Spjd break; 4440219089Spjd case ZFS_DELEG_NOTE_RENAME: 4441219089Spjd str = gettext("Must also have the 'mount' and 'create'" 4442219089Spjd "\n\t\t\t\tability in the new parent"); 4443219089Spjd break; 4444219089Spjd case ZFS_DELEG_NOTE_ROLLBACK: 4445219089Spjd str = gettext(""); 4446219089Spjd break; 4447219089Spjd case ZFS_DELEG_NOTE_SEND: 4448219089Spjd str = gettext(""); 4449219089Spjd break; 4450219089Spjd case ZFS_DELEG_NOTE_SHARE: 4451219089Spjd str = gettext("Allows sharing file systems over NFS or SMB" 4452219089Spjd "\n\t\t\t\tprotocols"); 4453219089Spjd break; 4454219089Spjd case ZFS_DELEG_NOTE_SNAPSHOT: 4455219089Spjd str = gettext(""); 4456219089Spjd break; 4457219089Spjd/* 4458219089Spjd * case ZFS_DELEG_NOTE_VSCAN: 4459219089Spjd * str = gettext(""); 4460219089Spjd * break; 4461219089Spjd */ 4462219089Spjd /* OTHER */ 4463219089Spjd case ZFS_DELEG_NOTE_GROUPQUOTA: 4464219089Spjd str = gettext("Allows accessing any groupquota@... property"); 4465219089Spjd break; 4466219089Spjd case ZFS_DELEG_NOTE_GROUPUSED: 4467219089Spjd str = gettext("Allows reading any groupused@... property"); 4468219089Spjd break; 4469219089Spjd case ZFS_DELEG_NOTE_USERPROP: 4470219089Spjd str = gettext("Allows changing any user property"); 4471219089Spjd break; 4472219089Spjd case ZFS_DELEG_NOTE_USERQUOTA: 4473219089Spjd str = gettext("Allows accessing any userquota@... property"); 4474219089Spjd break; 4475219089Spjd case ZFS_DELEG_NOTE_USERUSED: 4476219089Spjd str = gettext("Allows reading any userused@... property"); 4477219089Spjd break; 4478219089Spjd /* other */ 4479219089Spjd default: 4480219089Spjd str = ""; 4481219089Spjd } 4482219089Spjd 4483219089Spjd return (str); 4484219089Spjd} 4485219089Spjd 4486219089Spjdstruct allow_opts { 4487219089Spjd boolean_t local; 4488219089Spjd boolean_t descend; 4489219089Spjd boolean_t user; 4490219089Spjd boolean_t group; 4491219089Spjd boolean_t everyone; 4492219089Spjd boolean_t create; 4493219089Spjd boolean_t set; 4494219089Spjd boolean_t recursive; /* unallow only */ 4495219089Spjd boolean_t prt_usage; 4496219089Spjd 4497219089Spjd boolean_t prt_perms; 4498219089Spjd char *who; 4499219089Spjd char *perms; 4500219089Spjd const char *dataset; 4501219089Spjd}; 4502219089Spjd 4503219089Spjdstatic inline int 4504219089Spjdprop_cmp(const void *a, const void *b) 4505219089Spjd{ 4506219089Spjd const char *str1 = *(const char **)a; 4507219089Spjd const char *str2 = *(const char **)b; 4508219089Spjd return (strcmp(str1, str2)); 4509219089Spjd} 4510219089Spjd 4511219089Spjdstatic void 4512219089Spjdallow_usage(boolean_t un, boolean_t requested, const char *msg) 4513219089Spjd{ 4514219089Spjd const char *opt_desc[] = { 4515219089Spjd "-h", gettext("show this help message and exit"), 4516219089Spjd "-l", gettext("set permission locally"), 4517219089Spjd "-d", gettext("set permission for descents"), 4518219089Spjd "-u", gettext("set permission for user"), 4519219089Spjd "-g", gettext("set permission for group"), 4520219089Spjd "-e", gettext("set permission for everyone"), 4521219089Spjd "-c", gettext("set create time permission"), 4522219089Spjd "-s", gettext("define permission set"), 4523219089Spjd /* unallow only */ 4524219089Spjd "-r", gettext("remove permissions recursively"), 4525219089Spjd }; 4526219089Spjd size_t unallow_size = sizeof (opt_desc) / sizeof (char *); 4527219089Spjd size_t allow_size = unallow_size - 2; 4528219089Spjd const char *props[ZFS_NUM_PROPS]; 4529219089Spjd int i; 4530219089Spjd size_t count = 0; 4531219089Spjd FILE *fp = requested ? stdout : stderr; 4532219089Spjd zprop_desc_t *pdtbl = zfs_prop_get_table(); 4533219089Spjd const char *fmt = gettext("%-16s %-14s\t%s\n"); 4534219089Spjd 4535219089Spjd (void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW : 4536219089Spjd HELP_ALLOW)); 4537219089Spjd (void) fprintf(fp, gettext("Options:\n")); 4538219089Spjd for (i = 0; i < (un ? unallow_size : allow_size); i++) { 4539219089Spjd const char *opt = opt_desc[i++]; 4540219089Spjd const char *optdsc = opt_desc[i]; 4541219089Spjd (void) fprintf(fp, gettext(" %-10s %s\n"), opt, optdsc); 4542219089Spjd } 4543219089Spjd 4544219089Spjd (void) fprintf(fp, gettext("\nThe following permissions are " 4545219089Spjd "supported:\n\n")); 4546219089Spjd (void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"), 4547219089Spjd gettext("NOTES")); 4548219089Spjd for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) { 4549219089Spjd const char *perm_name = zfs_deleg_perm_tbl[i].z_perm; 4550219089Spjd zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note; 4551219089Spjd const char *perm_type = deleg_perm_type(perm_note); 4552219089Spjd const char *perm_comment = deleg_perm_comment(perm_note); 4553219089Spjd (void) fprintf(fp, fmt, perm_name, perm_type, perm_comment); 4554219089Spjd } 4555219089Spjd 4556219089Spjd for (i = 0; i < ZFS_NUM_PROPS; i++) { 4557219089Spjd zprop_desc_t *pd = &pdtbl[i]; 4558219089Spjd if (pd->pd_visible != B_TRUE) 4559219089Spjd continue; 4560219089Spjd 4561219089Spjd if (pd->pd_attr == PROP_READONLY) 4562219089Spjd continue; 4563219089Spjd 4564219089Spjd props[count++] = pd->pd_name; 4565219089Spjd } 4566219089Spjd props[count] = NULL; 4567219089Spjd 4568219089Spjd qsort(props, count, sizeof (char *), prop_cmp); 4569219089Spjd 4570219089Spjd for (i = 0; i < count; i++) 4571219089Spjd (void) fprintf(fp, fmt, props[i], gettext("property"), ""); 4572219089Spjd 4573219089Spjd if (msg != NULL) 4574219089Spjd (void) fprintf(fp, gettext("\nzfs: error: %s"), msg); 4575219089Spjd 4576219089Spjd exit(requested ? 0 : 2); 4577219089Spjd} 4578219089Spjd 4579219089Spjdstatic inline const char * 4580219089Spjdmunge_args(int argc, char **argv, boolean_t un, size_t expected_argc, 4581219089Spjd char **permsp) 4582219089Spjd{ 4583219089Spjd if (un && argc == expected_argc - 1) 4584219089Spjd *permsp = NULL; 4585219089Spjd else if (argc == expected_argc) 4586219089Spjd *permsp = argv[argc - 2]; 4587219089Spjd else 4588219089Spjd allow_usage(un, B_FALSE, 4589219089Spjd gettext("wrong number of parameters\n")); 4590219089Spjd 4591219089Spjd return (argv[argc - 1]); 4592219089Spjd} 4593219089Spjd 4594219089Spjdstatic void 4595219089Spjdparse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts) 4596219089Spjd{ 4597219089Spjd int uge_sum = opts->user + opts->group + opts->everyone; 4598219089Spjd int csuge_sum = opts->create + opts->set + uge_sum; 4599219089Spjd int ldcsuge_sum = csuge_sum + opts->local + opts->descend; 4600219089Spjd int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum; 4601219089Spjd 4602219089Spjd if (uge_sum > 1) 4603219089Spjd allow_usage(un, B_FALSE, 4604219089Spjd gettext("-u, -g, and -e are mutually exclusive\n")); 4605219089Spjd 4606219089Spjd if (opts->prt_usage) 4607219089Spjd if (argc == 0 && all_sum == 0) 4608219089Spjd allow_usage(un, B_TRUE, NULL); 4609219089Spjd else 4610219089Spjd usage(B_FALSE); 4611219089Spjd 4612219089Spjd if (opts->set) { 4613219089Spjd if (csuge_sum > 1) 4614219089Spjd allow_usage(un, B_FALSE, 4615219089Spjd gettext("invalid options combined with -s\n")); 4616219089Spjd 4617219089Spjd opts->dataset = munge_args(argc, argv, un, 3, &opts->perms); 4618219089Spjd if (argv[0][0] != '@') 4619219089Spjd allow_usage(un, B_FALSE, 4620219089Spjd gettext("invalid set name: missing '@' prefix\n")); 4621219089Spjd opts->who = argv[0]; 4622219089Spjd } else if (opts->create) { 4623219089Spjd if (ldcsuge_sum > 1) 4624219089Spjd allow_usage(un, B_FALSE, 4625219089Spjd gettext("invalid options combined with -c\n")); 4626219089Spjd opts->dataset = munge_args(argc, argv, un, 2, &opts->perms); 4627219089Spjd } else if (opts->everyone) { 4628219089Spjd if (csuge_sum > 1) 4629219089Spjd allow_usage(un, B_FALSE, 4630219089Spjd gettext("invalid options combined with -e\n")); 4631219089Spjd opts->dataset = munge_args(argc, argv, un, 2, &opts->perms); 4632219089Spjd } else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone") 4633219089Spjd == 0) { 4634219089Spjd opts->everyone = B_TRUE; 4635219089Spjd argc--; 4636219089Spjd argv++; 4637219089Spjd opts->dataset = munge_args(argc, argv, un, 2, &opts->perms); 4638230449Smm } else if (argc == 1 && !un) { 4639219089Spjd opts->prt_perms = B_TRUE; 4640219089Spjd opts->dataset = argv[argc-1]; 4641219089Spjd } else { 4642219089Spjd opts->dataset = munge_args(argc, argv, un, 3, &opts->perms); 4643219089Spjd opts->who = argv[0]; 4644219089Spjd } 4645219089Spjd 4646219089Spjd if (!opts->local && !opts->descend) { 4647219089Spjd opts->local = B_TRUE; 4648219089Spjd opts->descend = B_TRUE; 4649219089Spjd } 4650219089Spjd} 4651219089Spjd 4652219089Spjdstatic void 4653219089Spjdstore_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend, 4654219089Spjd const char *who, char *perms, nvlist_t *top_nvl) 4655219089Spjd{ 4656219089Spjd int i; 4657219089Spjd char ld[2] = { '\0', '\0' }; 4658219089Spjd char who_buf[ZFS_MAXNAMELEN+32]; 4659219089Spjd char base_type; 4660219089Spjd char set_type; 4661219089Spjd nvlist_t *base_nvl = NULL; 4662219089Spjd nvlist_t *set_nvl = NULL; 4663219089Spjd nvlist_t *nvl; 4664219089Spjd 4665219089Spjd if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0) 4666219089Spjd nomem(); 4667219089Spjd if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) != 0) 4668219089Spjd nomem(); 4669219089Spjd 4670219089Spjd switch (type) { 4671219089Spjd case ZFS_DELEG_NAMED_SET_SETS: 4672219089Spjd case ZFS_DELEG_NAMED_SET: 4673219089Spjd set_type = ZFS_DELEG_NAMED_SET_SETS; 4674219089Spjd base_type = ZFS_DELEG_NAMED_SET; 4675219089Spjd ld[0] = ZFS_DELEG_NA; 4676219089Spjd break; 4677219089Spjd case ZFS_DELEG_CREATE_SETS: 4678219089Spjd case ZFS_DELEG_CREATE: 4679219089Spjd set_type = ZFS_DELEG_CREATE_SETS; 4680219089Spjd base_type = ZFS_DELEG_CREATE; 4681219089Spjd ld[0] = ZFS_DELEG_NA; 4682219089Spjd break; 4683219089Spjd case ZFS_DELEG_USER_SETS: 4684219089Spjd case ZFS_DELEG_USER: 4685219089Spjd set_type = ZFS_DELEG_USER_SETS; 4686219089Spjd base_type = ZFS_DELEG_USER; 4687219089Spjd if (local) 4688219089Spjd ld[0] = ZFS_DELEG_LOCAL; 4689219089Spjd if (descend) 4690219089Spjd ld[1] = ZFS_DELEG_DESCENDENT; 4691219089Spjd break; 4692219089Spjd case ZFS_DELEG_GROUP_SETS: 4693219089Spjd case ZFS_DELEG_GROUP: 4694219089Spjd set_type = ZFS_DELEG_GROUP_SETS; 4695219089Spjd base_type = ZFS_DELEG_GROUP; 4696219089Spjd if (local) 4697219089Spjd ld[0] = ZFS_DELEG_LOCAL; 4698219089Spjd if (descend) 4699219089Spjd ld[1] = ZFS_DELEG_DESCENDENT; 4700219089Spjd break; 4701219089Spjd case ZFS_DELEG_EVERYONE_SETS: 4702219089Spjd case ZFS_DELEG_EVERYONE: 4703219089Spjd set_type = ZFS_DELEG_EVERYONE_SETS; 4704219089Spjd base_type = ZFS_DELEG_EVERYONE; 4705219089Spjd if (local) 4706219089Spjd ld[0] = ZFS_DELEG_LOCAL; 4707219089Spjd if (descend) 4708219089Spjd ld[1] = ZFS_DELEG_DESCENDENT; 4709219089Spjd } 4710219089Spjd 4711219089Spjd if (perms != NULL) { 4712219089Spjd char *curr = perms; 4713219089Spjd char *end = curr + strlen(perms); 4714219089Spjd 4715219089Spjd while (curr < end) { 4716219089Spjd char *delim = strchr(curr, ','); 4717219089Spjd if (delim == NULL) 4718219089Spjd delim = end; 4719219089Spjd else 4720219089Spjd *delim = '\0'; 4721219089Spjd 4722219089Spjd if (curr[0] == '@') 4723219089Spjd nvl = set_nvl; 4724219089Spjd else 4725219089Spjd nvl = base_nvl; 4726219089Spjd 4727219089Spjd (void) nvlist_add_boolean(nvl, curr); 4728219089Spjd if (delim != end) 4729219089Spjd *delim = ','; 4730219089Spjd curr = delim + 1; 4731219089Spjd } 4732219089Spjd 4733219089Spjd for (i = 0; i < 2; i++) { 4734219089Spjd char locality = ld[i]; 4735219089Spjd if (locality == 0) 4736219089Spjd continue; 4737219089Spjd 4738219089Spjd if (!nvlist_empty(base_nvl)) { 4739219089Spjd if (who != NULL) 4740219089Spjd (void) snprintf(who_buf, 4741219089Spjd sizeof (who_buf), "%c%c$%s", 4742219089Spjd base_type, locality, who); 4743219089Spjd else 4744219089Spjd (void) snprintf(who_buf, 4745219089Spjd sizeof (who_buf), "%c%c$", 4746219089Spjd base_type, locality); 4747219089Spjd 4748219089Spjd (void) nvlist_add_nvlist(top_nvl, who_buf, 4749219089Spjd base_nvl); 4750219089Spjd } 4751219089Spjd 4752219089Spjd 4753219089Spjd if (!nvlist_empty(set_nvl)) { 4754219089Spjd if (who != NULL) 4755219089Spjd (void) snprintf(who_buf, 4756219089Spjd sizeof (who_buf), "%c%c$%s", 4757219089Spjd set_type, locality, who); 4758219089Spjd else 4759219089Spjd (void) snprintf(who_buf, 4760219089Spjd sizeof (who_buf), "%c%c$", 4761219089Spjd set_type, locality); 4762219089Spjd 4763219089Spjd (void) nvlist_add_nvlist(top_nvl, who_buf, 4764219089Spjd set_nvl); 4765219089Spjd } 4766219089Spjd } 4767219089Spjd } else { 4768219089Spjd for (i = 0; i < 2; i++) { 4769219089Spjd char locality = ld[i]; 4770219089Spjd if (locality == 0) 4771219089Spjd continue; 4772219089Spjd 4773219089Spjd if (who != NULL) 4774219089Spjd (void) snprintf(who_buf, sizeof (who_buf), 4775219089Spjd "%c%c$%s", base_type, locality, who); 4776219089Spjd else 4777219089Spjd (void) snprintf(who_buf, sizeof (who_buf), 4778219089Spjd "%c%c$", base_type, locality); 4779219089Spjd (void) nvlist_add_boolean(top_nvl, who_buf); 4780219089Spjd 4781219089Spjd if (who != NULL) 4782219089Spjd (void) snprintf(who_buf, sizeof (who_buf), 4783219089Spjd "%c%c$%s", set_type, locality, who); 4784219089Spjd else 4785219089Spjd (void) snprintf(who_buf, sizeof (who_buf), 4786219089Spjd "%c%c$", set_type, locality); 4787219089Spjd (void) nvlist_add_boolean(top_nvl, who_buf); 4788219089Spjd } 4789219089Spjd } 4790219089Spjd} 4791219089Spjd 4792219089Spjdstatic int 4793219089Spjdconstruct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp) 4794219089Spjd{ 4795219089Spjd if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0) 4796219089Spjd nomem(); 4797219089Spjd 4798219089Spjd if (opts->set) { 4799219089Spjd store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local, 4800219089Spjd opts->descend, opts->who, opts->perms, *nvlp); 4801219089Spjd } else if (opts->create) { 4802219089Spjd store_allow_perm(ZFS_DELEG_CREATE, opts->local, 4803219089Spjd opts->descend, NULL, opts->perms, *nvlp); 4804219089Spjd } else if (opts->everyone) { 4805219089Spjd store_allow_perm(ZFS_DELEG_EVERYONE, opts->local, 4806219089Spjd opts->descend, NULL, opts->perms, *nvlp); 4807219089Spjd } else { 4808219089Spjd char *curr = opts->who; 4809219089Spjd char *end = curr + strlen(curr); 4810219089Spjd 4811219089Spjd while (curr < end) { 4812219089Spjd const char *who; 4813219089Spjd zfs_deleg_who_type_t who_type; 4814219089Spjd char *endch; 4815219089Spjd char *delim = strchr(curr, ','); 4816219089Spjd char errbuf[256]; 4817219089Spjd char id[64]; 4818219089Spjd struct passwd *p = NULL; 4819219089Spjd struct group *g = NULL; 4820219089Spjd 4821219089Spjd uid_t rid; 4822219089Spjd if (delim == NULL) 4823219089Spjd delim = end; 4824219089Spjd else 4825219089Spjd *delim = '\0'; 4826219089Spjd 4827219089Spjd rid = (uid_t)strtol(curr, &endch, 0); 4828219089Spjd if (opts->user) { 4829219089Spjd who_type = ZFS_DELEG_USER; 4830219089Spjd if (*endch != '\0') 4831219089Spjd p = getpwnam(curr); 4832219089Spjd else 4833219089Spjd p = getpwuid(rid); 4834219089Spjd 4835219089Spjd if (p != NULL) 4836219089Spjd rid = p->pw_uid; 4837219089Spjd else { 4838219089Spjd (void) snprintf(errbuf, 256, gettext( 4839219089Spjd "invalid user %s"), curr); 4840219089Spjd allow_usage(un, B_TRUE, errbuf); 4841219089Spjd } 4842219089Spjd } else if (opts->group) { 4843219089Spjd who_type = ZFS_DELEG_GROUP; 4844219089Spjd if (*endch != '\0') 4845219089Spjd g = getgrnam(curr); 4846219089Spjd else 4847219089Spjd g = getgrgid(rid); 4848219089Spjd 4849219089Spjd if (g != NULL) 4850219089Spjd rid = g->gr_gid; 4851219089Spjd else { 4852219089Spjd (void) snprintf(errbuf, 256, gettext( 4853219089Spjd "invalid group %s"), curr); 4854219089Spjd allow_usage(un, B_TRUE, errbuf); 4855219089Spjd } 4856219089Spjd } else { 4857219089Spjd if (*endch != '\0') { 4858219089Spjd p = getpwnam(curr); 4859219089Spjd } else { 4860219089Spjd p = getpwuid(rid); 4861219089Spjd } 4862219089Spjd 4863219089Spjd if (p == NULL) 4864219089Spjd if (*endch != '\0') { 4865219089Spjd g = getgrnam(curr); 4866219089Spjd } else { 4867219089Spjd g = getgrgid(rid); 4868219089Spjd } 4869219089Spjd 4870219089Spjd if (p != NULL) { 4871219089Spjd who_type = ZFS_DELEG_USER; 4872219089Spjd rid = p->pw_uid; 4873219089Spjd } else if (g != NULL) { 4874219089Spjd who_type = ZFS_DELEG_GROUP; 4875219089Spjd rid = g->gr_gid; 4876219089Spjd } else { 4877219089Spjd (void) snprintf(errbuf, 256, gettext( 4878219089Spjd "invalid user/group %s"), curr); 4879219089Spjd allow_usage(un, B_TRUE, errbuf); 4880219089Spjd } 4881219089Spjd } 4882219089Spjd 4883219089Spjd (void) sprintf(id, "%u", rid); 4884219089Spjd who = id; 4885219089Spjd 4886219089Spjd store_allow_perm(who_type, opts->local, 4887219089Spjd opts->descend, who, opts->perms, *nvlp); 4888219089Spjd curr = delim + 1; 4889219089Spjd } 4890219089Spjd } 4891219089Spjd 4892219089Spjd return (0); 4893219089Spjd} 4894219089Spjd 4895219089Spjdstatic void 4896219089Spjdprint_set_creat_perms(uu_avl_t *who_avl) 4897219089Spjd{ 4898219089Spjd const char *sc_title[] = { 4899219089Spjd gettext("Permission sets:\n"), 4900219089Spjd gettext("Create time permissions:\n"), 4901219089Spjd NULL 4902219089Spjd }; 4903219089Spjd const char **title_ptr = sc_title; 4904219089Spjd who_perm_node_t *who_node = NULL; 4905219089Spjd int prev_weight = -1; 4906219089Spjd 4907219089Spjd for (who_node = uu_avl_first(who_avl); who_node != NULL; 4908219089Spjd who_node = uu_avl_next(who_avl, who_node)) { 4909219089Spjd uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl; 4910219089Spjd zfs_deleg_who_type_t who_type = who_node->who_perm.who_type; 4911219089Spjd const char *who_name = who_node->who_perm.who_name; 4912219089Spjd int weight = who_type2weight(who_type); 4913219089Spjd boolean_t first = B_TRUE; 4914219089Spjd deleg_perm_node_t *deleg_node; 4915219089Spjd 4916219089Spjd if (prev_weight != weight) { 4917219089Spjd (void) printf(*title_ptr++); 4918219089Spjd prev_weight = weight; 4919219089Spjd } 4920219089Spjd 4921219089Spjd if (who_name == NULL || strnlen(who_name, 1) == 0) 4922219089Spjd (void) printf("\t"); 4923219089Spjd else 4924219089Spjd (void) printf("\t%s ", who_name); 4925219089Spjd 4926219089Spjd for (deleg_node = uu_avl_first(avl); deleg_node != NULL; 4927219089Spjd deleg_node = uu_avl_next(avl, deleg_node)) { 4928219089Spjd if (first) { 4929219089Spjd (void) printf("%s", 4930219089Spjd deleg_node->dpn_perm.dp_name); 4931219089Spjd first = B_FALSE; 4932219089Spjd } else 4933219089Spjd (void) printf(",%s", 4934219089Spjd deleg_node->dpn_perm.dp_name); 4935219089Spjd } 4936219089Spjd 4937219089Spjd (void) printf("\n"); 4938219089Spjd } 4939219089Spjd} 4940219089Spjd 4941219089Spjdstatic void inline 4942219089Spjdprint_uge_deleg_perms(uu_avl_t *who_avl, boolean_t local, boolean_t descend, 4943219089Spjd const char *title) 4944219089Spjd{ 4945219089Spjd who_perm_node_t *who_node = NULL; 4946219089Spjd boolean_t prt_title = B_TRUE; 4947219089Spjd uu_avl_walk_t *walk; 4948219089Spjd 4949219089Spjd if ((walk = uu_avl_walk_start(who_avl, UU_WALK_ROBUST)) == NULL) 4950219089Spjd nomem(); 4951219089Spjd 4952219089Spjd while ((who_node = uu_avl_walk_next(walk)) != NULL) { 4953219089Spjd const char *who_name = who_node->who_perm.who_name; 4954219089Spjd const char *nice_who_name = who_node->who_perm.who_ug_name; 4955219089Spjd uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl; 4956219089Spjd zfs_deleg_who_type_t who_type = who_node->who_perm.who_type; 4957219089Spjd char delim = ' '; 4958219089Spjd deleg_perm_node_t *deleg_node; 4959219089Spjd boolean_t prt_who = B_TRUE; 4960219089Spjd 4961219089Spjd for (deleg_node = uu_avl_first(avl); 4962219089Spjd deleg_node != NULL; 4963219089Spjd deleg_node = uu_avl_next(avl, deleg_node)) { 4964219089Spjd if (local != deleg_node->dpn_perm.dp_local || 4965219089Spjd descend != deleg_node->dpn_perm.dp_descend) 4966219089Spjd continue; 4967219089Spjd 4968219089Spjd if (prt_who) { 4969219089Spjd const char *who = NULL; 4970219089Spjd if (prt_title) { 4971219089Spjd prt_title = B_FALSE; 4972219089Spjd (void) printf(title); 4973219089Spjd } 4974219089Spjd 4975219089Spjd switch (who_type) { 4976219089Spjd case ZFS_DELEG_USER_SETS: 4977219089Spjd case ZFS_DELEG_USER: 4978219089Spjd who = gettext("user"); 4979219089Spjd if (nice_who_name) 4980219089Spjd who_name = nice_who_name; 4981219089Spjd break; 4982219089Spjd case ZFS_DELEG_GROUP_SETS: 4983219089Spjd case ZFS_DELEG_GROUP: 4984219089Spjd who = gettext("group"); 4985219089Spjd if (nice_who_name) 4986219089Spjd who_name = nice_who_name; 4987219089Spjd break; 4988219089Spjd case ZFS_DELEG_EVERYONE_SETS: 4989219089Spjd case ZFS_DELEG_EVERYONE: 4990219089Spjd who = gettext("everyone"); 4991219089Spjd who_name = NULL; 4992219089Spjd } 4993219089Spjd 4994219089Spjd prt_who = B_FALSE; 4995219089Spjd if (who_name == NULL) 4996219089Spjd (void) printf("\t%s", who); 4997219089Spjd else 4998219089Spjd (void) printf("\t%s %s", who, who_name); 4999219089Spjd } 5000219089Spjd 5001219089Spjd (void) printf("%c%s", delim, 5002219089Spjd deleg_node->dpn_perm.dp_name); 5003219089Spjd delim = ','; 5004219089Spjd } 5005219089Spjd 5006219089Spjd if (!prt_who) 5007219089Spjd (void) printf("\n"); 5008219089Spjd } 5009219089Spjd 5010219089Spjd uu_avl_walk_end(walk); 5011219089Spjd} 5012219089Spjd 5013219089Spjdstatic void 5014219089Spjdprint_fs_perms(fs_perm_set_t *fspset) 5015219089Spjd{ 5016219089Spjd fs_perm_node_t *node = NULL; 5017219089Spjd char buf[ZFS_MAXNAMELEN+32]; 5018219089Spjd const char *dsname = buf; 5019219089Spjd 5020219089Spjd for (node = uu_list_first(fspset->fsps_list); node != NULL; 5021219089Spjd node = uu_list_next(fspset->fsps_list, node)) { 5022219089Spjd uu_avl_t *sc_avl = node->fspn_fsperm.fsp_sc_avl; 5023219089Spjd uu_avl_t *uge_avl = node->fspn_fsperm.fsp_uge_avl; 5024219089Spjd int left = 0; 5025219089Spjd 5026219089Spjd (void) snprintf(buf, ZFS_MAXNAMELEN+32, 5027219089Spjd gettext("---- Permissions on %s "), 5028219089Spjd node->fspn_fsperm.fsp_name); 5029219089Spjd (void) printf(dsname); 5030219089Spjd left = 70 - strlen(buf); 5031219089Spjd while (left-- > 0) 5032219089Spjd (void) printf("-"); 5033219089Spjd (void) printf("\n"); 5034219089Spjd 5035219089Spjd print_set_creat_perms(sc_avl); 5036219089Spjd print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE, 5037219089Spjd gettext("Local permissions:\n")); 5038219089Spjd print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE, 5039219089Spjd gettext("Descendent permissions:\n")); 5040219089Spjd print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE, 5041219089Spjd gettext("Local+Descendent permissions:\n")); 5042219089Spjd } 5043219089Spjd} 5044219089Spjd 5045219089Spjdstatic fs_perm_set_t fs_perm_set = { NULL, NULL, NULL, NULL }; 5046219089Spjd 5047219089Spjdstruct deleg_perms { 5048219089Spjd boolean_t un; 5049219089Spjd nvlist_t *nvl; 5050219089Spjd}; 5051219089Spjd 5052219089Spjdstatic int 5053219089Spjdset_deleg_perms(zfs_handle_t *zhp, void *data) 5054219089Spjd{ 5055219089Spjd struct deleg_perms *perms = (struct deleg_perms *)data; 5056219089Spjd zfs_type_t zfs_type = zfs_get_type(zhp); 5057219089Spjd 5058219089Spjd if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME) 5059219089Spjd return (0); 5060219089Spjd 5061219089Spjd return (zfs_set_fsacl(zhp, perms->un, perms->nvl)); 5062219089Spjd} 5063219089Spjd 5064219089Spjdstatic int 5065219089Spjdzfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un) 5066219089Spjd{ 5067219089Spjd zfs_handle_t *zhp; 5068219089Spjd nvlist_t *perm_nvl = NULL; 5069219089Spjd nvlist_t *update_perm_nvl = NULL; 5070219089Spjd int error = 1; 5071219089Spjd int c; 5072219089Spjd struct allow_opts opts = { 0 }; 5073219089Spjd 5074219089Spjd const char *optstr = un ? "ldugecsrh" : "ldugecsh"; 5075219089Spjd 5076219089Spjd /* check opts */ 5077219089Spjd while ((c = getopt(argc, argv, optstr)) != -1) { 5078219089Spjd switch (c) { 5079219089Spjd case 'l': 5080219089Spjd opts.local = B_TRUE; 5081219089Spjd break; 5082219089Spjd case 'd': 5083219089Spjd opts.descend = B_TRUE; 5084219089Spjd break; 5085219089Spjd case 'u': 5086219089Spjd opts.user = B_TRUE; 5087219089Spjd break; 5088219089Spjd case 'g': 5089219089Spjd opts.group = B_TRUE; 5090219089Spjd break; 5091219089Spjd case 'e': 5092219089Spjd opts.everyone = B_TRUE; 5093219089Spjd break; 5094219089Spjd case 's': 5095219089Spjd opts.set = B_TRUE; 5096219089Spjd break; 5097219089Spjd case 'c': 5098219089Spjd opts.create = B_TRUE; 5099219089Spjd break; 5100219089Spjd case 'r': 5101219089Spjd opts.recursive = B_TRUE; 5102219089Spjd break; 5103219089Spjd case ':': 5104219089Spjd (void) fprintf(stderr, gettext("missing argument for " 5105219089Spjd "'%c' option\n"), optopt); 5106219089Spjd usage(B_FALSE); 5107219089Spjd break; 5108219089Spjd case 'h': 5109219089Spjd opts.prt_usage = B_TRUE; 5110219089Spjd break; 5111219089Spjd case '?': 5112219089Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 5113219089Spjd optopt); 5114219089Spjd usage(B_FALSE); 5115219089Spjd } 5116219089Spjd } 5117219089Spjd 5118219089Spjd argc -= optind; 5119219089Spjd argv += optind; 5120219089Spjd 5121219089Spjd /* check arguments */ 5122219089Spjd parse_allow_args(argc, argv, un, &opts); 5123219089Spjd 5124219089Spjd /* try to open the dataset */ 5125230449Smm if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM | 5126230449Smm ZFS_TYPE_VOLUME)) == NULL) { 5127230449Smm (void) fprintf(stderr, "Failed to open dataset: %s\n", 5128219089Spjd opts.dataset); 5129219089Spjd return (-1); 5130219089Spjd } 5131219089Spjd 5132219089Spjd if (zfs_get_fsacl(zhp, &perm_nvl) != 0) 5133219089Spjd goto cleanup2; 5134219089Spjd 5135219089Spjd fs_perm_set_init(&fs_perm_set); 5136219089Spjd if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) { 5137230449Smm (void) fprintf(stderr, "Failed to parse fsacl permissions\n"); 5138219089Spjd goto cleanup1; 5139219089Spjd } 5140219089Spjd 5141219089Spjd if (opts.prt_perms) 5142219089Spjd print_fs_perms(&fs_perm_set); 5143219089Spjd else { 5144219089Spjd (void) construct_fsacl_list(un, &opts, &update_perm_nvl); 5145219089Spjd if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0) 5146219089Spjd goto cleanup0; 5147219089Spjd 5148219089Spjd if (un && opts.recursive) { 5149219089Spjd struct deleg_perms data = { un, update_perm_nvl }; 5150219089Spjd if (zfs_iter_filesystems(zhp, set_deleg_perms, 5151219089Spjd &data) != 0) 5152219089Spjd goto cleanup0; 5153219089Spjd } 5154219089Spjd } 5155219089Spjd 5156219089Spjd error = 0; 5157219089Spjd 5158219089Spjdcleanup0: 5159219089Spjd nvlist_free(perm_nvl); 5160219089Spjd if (update_perm_nvl != NULL) 5161219089Spjd nvlist_free(update_perm_nvl); 5162219089Spjdcleanup1: 5163219089Spjd fs_perm_set_fini(&fs_perm_set); 5164219089Spjdcleanup2: 5165219089Spjd zfs_close(zhp); 5166219089Spjd 5167219089Spjd return (error); 5168219089Spjd} 5169219089Spjd 5170219089Spjdstatic int 5171219089Spjdzfs_do_allow(int argc, char **argv) 5172219089Spjd{ 5173219089Spjd return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE)); 5174219089Spjd} 5175219089Spjd 5176219089Spjdstatic int 5177219089Spjdzfs_do_unallow(int argc, char **argv) 5178219089Spjd{ 5179219089Spjd return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE)); 5180219089Spjd} 5181219089Spjd 5182219089Spjdstatic int 5183219089Spjdzfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding) 5184219089Spjd{ 5185219089Spjd int errors = 0; 5186219089Spjd int i; 5187219089Spjd const char *tag; 5188219089Spjd boolean_t recursive = B_FALSE; 5189219089Spjd const char *opts = holding ? "rt" : "r"; 5190219089Spjd int c; 5191219089Spjd 5192219089Spjd /* check options */ 5193219089Spjd while ((c = getopt(argc, argv, opts)) != -1) { 5194219089Spjd switch (c) { 5195219089Spjd case 'r': 5196219089Spjd recursive = B_TRUE; 5197219089Spjd break; 5198219089Spjd case '?': 5199219089Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 5200219089Spjd optopt); 5201219089Spjd usage(B_FALSE); 5202219089Spjd } 5203219089Spjd } 5204219089Spjd 5205219089Spjd argc -= optind; 5206219089Spjd argv += optind; 5207219089Spjd 5208219089Spjd /* check number of arguments */ 5209219089Spjd if (argc < 2) 5210219089Spjd usage(B_FALSE); 5211219089Spjd 5212219089Spjd tag = argv[0]; 5213219089Spjd --argc; 5214219089Spjd ++argv; 5215219089Spjd 5216219089Spjd if (holding && tag[0] == '.') { 5217219089Spjd /* tags starting with '.' are reserved for libzfs */ 5218219089Spjd (void) fprintf(stderr, gettext("tag may not start with '.'\n")); 5219219089Spjd usage(B_FALSE); 5220219089Spjd } 5221219089Spjd 5222219089Spjd for (i = 0; i < argc; ++i) { 5223219089Spjd zfs_handle_t *zhp; 5224219089Spjd char parent[ZFS_MAXNAMELEN]; 5225219089Spjd const char *delim; 5226219089Spjd char *path = argv[i]; 5227219089Spjd 5228219089Spjd delim = strchr(path, '@'); 5229219089Spjd if (delim == NULL) { 5230219089Spjd (void) fprintf(stderr, 5231219089Spjd gettext("'%s' is not a snapshot\n"), path); 5232219089Spjd ++errors; 5233219089Spjd continue; 5234219089Spjd } 5235219089Spjd (void) strncpy(parent, path, delim - path); 5236219089Spjd parent[delim - path] = '\0'; 5237219089Spjd 5238219089Spjd zhp = zfs_open(g_zfs, parent, 5239219089Spjd ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 5240219089Spjd if (zhp == NULL) { 5241219089Spjd ++errors; 5242219089Spjd continue; 5243219089Spjd } 5244219089Spjd if (holding) { 5245251646Sdelphij if (zfs_hold(zhp, delim+1, tag, recursive, -1) != 0) 5246219089Spjd ++errors; 5247219089Spjd } else { 5248219089Spjd if (zfs_release(zhp, delim+1, tag, recursive) != 0) 5249219089Spjd ++errors; 5250219089Spjd } 5251219089Spjd zfs_close(zhp); 5252219089Spjd } 5253219089Spjd 5254219089Spjd return (errors != 0); 5255219089Spjd} 5256219089Spjd 5257219089Spjd/* 5258219089Spjd * zfs hold [-r] [-t] <tag> <snap> ... 5259219089Spjd * 5260219089Spjd * -r Recursively hold 5261219089Spjd * 5262219089Spjd * Apply a user-hold with the given tag to the list of snapshots. 5263219089Spjd */ 5264219089Spjdstatic int 5265219089Spjdzfs_do_hold(int argc, char **argv) 5266219089Spjd{ 5267219089Spjd return (zfs_do_hold_rele_impl(argc, argv, B_TRUE)); 5268219089Spjd} 5269219089Spjd 5270219089Spjd/* 5271219089Spjd * zfs release [-r] <tag> <snap> ... 5272219089Spjd * 5273219089Spjd * -r Recursively release 5274219089Spjd * 5275219089Spjd * Release a user-hold with the given tag from the list of snapshots. 5276219089Spjd */ 5277219089Spjdstatic int 5278219089Spjdzfs_do_release(int argc, char **argv) 5279219089Spjd{ 5280219089Spjd return (zfs_do_hold_rele_impl(argc, argv, B_FALSE)); 5281219089Spjd} 5282219089Spjd 5283219089Spjdtypedef struct holds_cbdata { 5284219089Spjd boolean_t cb_recursive; 5285219089Spjd const char *cb_snapname; 5286219089Spjd nvlist_t **cb_nvlp; 5287219089Spjd size_t cb_max_namelen; 5288219089Spjd size_t cb_max_taglen; 5289219089Spjd} holds_cbdata_t; 5290219089Spjd 5291219089Spjd#define STRFTIME_FMT_STR "%a %b %e %k:%M %Y" 5292219089Spjd#define DATETIME_BUF_LEN (32) 5293219089Spjd/* 5294219089Spjd * 5295219089Spjd */ 5296219089Spjdstatic void 5297219089Spjdprint_holds(boolean_t scripted, size_t nwidth, size_t tagwidth, nvlist_t *nvl) 5298219089Spjd{ 5299219089Spjd int i; 5300219089Spjd nvpair_t *nvp = NULL; 5301219089Spjd char *hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" }; 5302219089Spjd const char *col; 5303219089Spjd 5304219089Spjd if (!scripted) { 5305219089Spjd for (i = 0; i < 3; i++) { 5306219089Spjd col = gettext(hdr_cols[i]); 5307219089Spjd if (i < 2) 5308219089Spjd (void) printf("%-*s ", i ? tagwidth : nwidth, 5309219089Spjd col); 5310219089Spjd else 5311219089Spjd (void) printf("%s\n", col); 5312219089Spjd } 5313219089Spjd } 5314219089Spjd 5315219089Spjd while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 5316219089Spjd char *zname = nvpair_name(nvp); 5317219089Spjd nvlist_t *nvl2; 5318219089Spjd nvpair_t *nvp2 = NULL; 5319219089Spjd (void) nvpair_value_nvlist(nvp, &nvl2); 5320219089Spjd while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) { 5321219089Spjd char tsbuf[DATETIME_BUF_LEN]; 5322219089Spjd char *tagname = nvpair_name(nvp2); 5323219089Spjd uint64_t val = 0; 5324219089Spjd time_t time; 5325219089Spjd struct tm t; 5326219089Spjd char sep = scripted ? '\t' : ' '; 5327219089Spjd size_t sepnum = scripted ? 1 : 2; 5328219089Spjd 5329219089Spjd (void) nvpair_value_uint64(nvp2, &val); 5330219089Spjd time = (time_t)val; 5331219089Spjd (void) localtime_r(&time, &t); 5332219089Spjd (void) strftime(tsbuf, DATETIME_BUF_LEN, 5333219089Spjd gettext(STRFTIME_FMT_STR), &t); 5334219089Spjd 5335219089Spjd (void) printf("%-*s%*c%-*s%*c%s\n", nwidth, zname, 5336219089Spjd sepnum, sep, tagwidth, tagname, sepnum, sep, tsbuf); 5337219089Spjd } 5338219089Spjd } 5339219089Spjd} 5340219089Spjd 5341219089Spjd/* 5342219089Spjd * Generic callback function to list a dataset or snapshot. 5343219089Spjd */ 5344219089Spjdstatic int 5345219089Spjdholds_callback(zfs_handle_t *zhp, void *data) 5346219089Spjd{ 5347219089Spjd holds_cbdata_t *cbp = data; 5348219089Spjd nvlist_t *top_nvl = *cbp->cb_nvlp; 5349219089Spjd nvlist_t *nvl = NULL; 5350219089Spjd nvpair_t *nvp = NULL; 5351219089Spjd const char *zname = zfs_get_name(zhp); 5352219089Spjd size_t znamelen = strnlen(zname, ZFS_MAXNAMELEN); 5353219089Spjd 5354219089Spjd if (cbp->cb_recursive) { 5355219089Spjd const char *snapname; 5356219089Spjd char *delim = strchr(zname, '@'); 5357219089Spjd if (delim == NULL) 5358219089Spjd return (0); 5359219089Spjd 5360219089Spjd snapname = delim + 1; 5361219089Spjd if (strcmp(cbp->cb_snapname, snapname)) 5362219089Spjd return (0); 5363219089Spjd } 5364219089Spjd 5365219089Spjd if (zfs_get_holds(zhp, &nvl) != 0) 5366219089Spjd return (-1); 5367219089Spjd 5368219089Spjd if (znamelen > cbp->cb_max_namelen) 5369219089Spjd cbp->cb_max_namelen = znamelen; 5370219089Spjd 5371219089Spjd while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 5372219089Spjd const char *tag = nvpair_name(nvp); 5373219089Spjd size_t taglen = strnlen(tag, MAXNAMELEN); 5374219089Spjd if (taglen > cbp->cb_max_taglen) 5375219089Spjd cbp->cb_max_taglen = taglen; 5376219089Spjd } 5377219089Spjd 5378219089Spjd return (nvlist_add_nvlist(top_nvl, zname, nvl)); 5379219089Spjd} 5380219089Spjd 5381219089Spjd/* 5382219089Spjd * zfs holds [-r] <snap> ... 5383219089Spjd * 5384219089Spjd * -r Recursively hold 5385219089Spjd */ 5386219089Spjdstatic int 5387219089Spjdzfs_do_holds(int argc, char **argv) 5388219089Spjd{ 5389219089Spjd int errors = 0; 5390219089Spjd int c; 5391219089Spjd int i; 5392219089Spjd boolean_t scripted = B_FALSE; 5393219089Spjd boolean_t recursive = B_FALSE; 5394219089Spjd const char *opts = "rH"; 5395219089Spjd nvlist_t *nvl; 5396219089Spjd 5397219089Spjd int types = ZFS_TYPE_SNAPSHOT; 5398219089Spjd holds_cbdata_t cb = { 0 }; 5399219089Spjd 5400219089Spjd int limit = 0; 5401231144Smm int ret = 0; 5402219089Spjd int flags = 0; 5403219089Spjd 5404219089Spjd /* check options */ 5405219089Spjd while ((c = getopt(argc, argv, opts)) != -1) { 5406219089Spjd switch (c) { 5407219089Spjd case 'r': 5408219089Spjd recursive = B_TRUE; 5409219089Spjd break; 5410219089Spjd case 'H': 5411219089Spjd scripted = B_TRUE; 5412219089Spjd break; 5413219089Spjd case '?': 5414219089Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 5415219089Spjd optopt); 5416219089Spjd usage(B_FALSE); 5417219089Spjd } 5418219089Spjd } 5419219089Spjd 5420219089Spjd if (recursive) { 5421219089Spjd types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME; 5422219089Spjd flags |= ZFS_ITER_RECURSE; 5423219089Spjd } 5424219089Spjd 5425219089Spjd argc -= optind; 5426219089Spjd argv += optind; 5427219089Spjd 5428219089Spjd /* check number of arguments */ 5429219089Spjd if (argc < 1) 5430219089Spjd usage(B_FALSE); 5431219089Spjd 5432219089Spjd if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 5433219089Spjd nomem(); 5434219089Spjd 5435219089Spjd for (i = 0; i < argc; ++i) { 5436219089Spjd char *snapshot = argv[i]; 5437219089Spjd const char *delim; 5438219089Spjd const char *snapname; 5439219089Spjd 5440219089Spjd delim = strchr(snapshot, '@'); 5441219089Spjd if (delim == NULL) { 5442219089Spjd (void) fprintf(stderr, 5443219089Spjd gettext("'%s' is not a snapshot\n"), snapshot); 5444219089Spjd ++errors; 5445219089Spjd continue; 5446219089Spjd } 5447219089Spjd snapname = delim + 1; 5448219089Spjd if (recursive) 5449219089Spjd snapshot[delim - snapshot] = '\0'; 5450219089Spjd 5451219089Spjd cb.cb_recursive = recursive; 5452219089Spjd cb.cb_snapname = snapname; 5453219089Spjd cb.cb_nvlp = &nvl; 5454219089Spjd 5455219089Spjd /* 5456219089Spjd * 1. collect holds data, set format options 5457219089Spjd */ 5458219089Spjd ret = zfs_for_each(argc, argv, flags, types, NULL, NULL, limit, 5459219089Spjd holds_callback, &cb); 5460219089Spjd if (ret != 0) 5461219089Spjd ++errors; 5462219089Spjd } 5463219089Spjd 5464219089Spjd /* 5465219089Spjd * 2. print holds data 5466219089Spjd */ 5467219089Spjd print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl); 5468219089Spjd 5469219089Spjd if (nvlist_empty(nvl)) 5470219089Spjd (void) printf(gettext("no datasets available\n")); 5471219089Spjd 5472219089Spjd nvlist_free(nvl); 5473219089Spjd 5474219089Spjd return (0 != errors); 5475219089Spjd} 5476219089Spjd 5477185029Spjd#define CHECK_SPINNER 30 5478185029Spjd#define SPINNER_TIME 3 /* seconds */ 5479185029Spjd#define MOUNT_TIME 5 /* seconds */ 5480185029Spjd 5481168404Spjdstatic int 5482168404Spjdget_one_dataset(zfs_handle_t *zhp, void *data) 5483168404Spjd{ 5484219089Spjd static char *spin[] = { "-", "\\", "|", "/" }; 5485185029Spjd static int spinval = 0; 5486185029Spjd static int spincheck = 0; 5487185029Spjd static time_t last_spin_time = (time_t)0; 5488219089Spjd get_all_cb_t *cbp = data; 5489168404Spjd zfs_type_t type = zfs_get_type(zhp); 5490168404Spjd 5491185029Spjd if (cbp->cb_verbose) { 5492185029Spjd if (--spincheck < 0) { 5493185029Spjd time_t now = time(NULL); 5494185029Spjd if (last_spin_time + SPINNER_TIME < now) { 5495219089Spjd update_progress(spin[spinval++ % 4]); 5496185029Spjd last_spin_time = now; 5497185029Spjd } 5498185029Spjd spincheck = CHECK_SPINNER; 5499185029Spjd } 5500185029Spjd } 5501185029Spjd 5502168404Spjd /* 5503168404Spjd * Interate over any nested datasets. 5504168404Spjd */ 5505219089Spjd if (zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) { 5506168404Spjd zfs_close(zhp); 5507168404Spjd return (1); 5508168404Spjd } 5509168404Spjd 5510168404Spjd /* 5511168404Spjd * Skip any datasets whose type does not match. 5512168404Spjd */ 5513219089Spjd if ((type & ZFS_TYPE_FILESYSTEM) == 0) { 5514168404Spjd zfs_close(zhp); 5515168404Spjd return (0); 5516168404Spjd } 5517219089Spjd libzfs_add_handle(cbp, zhp); 5518219089Spjd assert(cbp->cb_used <= cbp->cb_alloc); 5519168404Spjd 5520168404Spjd return (0); 5521168404Spjd} 5522168404Spjd 5523168404Spjdstatic void 5524219089Spjdget_all_datasets(zfs_handle_t ***dslist, size_t *count, boolean_t verbose) 5525168404Spjd{ 5526219089Spjd get_all_cb_t cb = { 0 }; 5527185029Spjd cb.cb_verbose = verbose; 5528219089Spjd cb.cb_getone = get_one_dataset; 5529168404Spjd 5530219089Spjd if (verbose) 5531219089Spjd set_progress_header(gettext("Reading ZFS config")); 5532168404Spjd (void) zfs_iter_root(g_zfs, get_one_dataset, &cb); 5533168404Spjd 5534168404Spjd *dslist = cb.cb_handles; 5535168404Spjd *count = cb.cb_used; 5536185029Spjd 5537219089Spjd if (verbose) 5538219089Spjd finish_progress(gettext("done.")); 5539168404Spjd} 5540168404Spjd 5541168404Spjd/* 5542168404Spjd * Generic callback for sharing or mounting filesystems. Because the code is so 5543168404Spjd * similar, we have a common function with an extra parameter to determine which 5544168404Spjd * mode we are using. 5545168404Spjd */ 5546168404Spjd#define OP_SHARE 0x1 5547168404Spjd#define OP_MOUNT 0x2 5548168404Spjd 5549168404Spjd/* 5550168404Spjd * Share or mount a dataset. 5551168404Spjd */ 5552168404Spjdstatic int 5553185029Spjdshare_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol, 5554185029Spjd boolean_t explicit, const char *options) 5555168404Spjd{ 5556168404Spjd char mountpoint[ZFS_MAXPROPLEN]; 5557168404Spjd char shareopts[ZFS_MAXPROPLEN]; 5558185029Spjd char smbshareopts[ZFS_MAXPROPLEN]; 5559168404Spjd const char *cmdname = op == OP_SHARE ? "share" : "mount"; 5560168404Spjd struct mnttab mnt; 5561168404Spjd uint64_t zoned, canmount; 5562185029Spjd boolean_t shared_nfs, shared_smb; 5563168404Spjd 5564219089Spjd assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM); 5565168404Spjd 5566219089Spjd /* 5567219089Spjd * Check to make sure we can mount/share this dataset. If we 5568219089Spjd * are in the global zone and the filesystem is exported to a 5569219089Spjd * local zone, or if we are in a local zone and the 5570219089Spjd * filesystem is not exported, then it is an error. 5571219089Spjd */ 5572219089Spjd zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 5573168404Spjd 5574219089Spjd if (zoned && getzoneid() == GLOBAL_ZONEID) { 5575219089Spjd if (!explicit) 5576219089Spjd return (0); 5577168404Spjd 5578219089Spjd (void) fprintf(stderr, gettext("cannot %s '%s': " 5579219089Spjd "dataset is exported to a local zone\n"), cmdname, 5580219089Spjd zfs_get_name(zhp)); 5581219089Spjd return (1); 5582168404Spjd 5583219089Spjd } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 5584219089Spjd if (!explicit) 5585219089Spjd return (0); 5586168404Spjd 5587219089Spjd (void) fprintf(stderr, gettext("cannot %s '%s': " 5588219089Spjd "permission denied\n"), cmdname, 5589219089Spjd zfs_get_name(zhp)); 5590219089Spjd return (1); 5591219089Spjd } 5592168404Spjd 5593219089Spjd /* 5594219089Spjd * Ignore any filesystems which don't apply to us. This 5595219089Spjd * includes those with a legacy mountpoint, or those with 5596219089Spjd * legacy share options. 5597219089Spjd */ 5598219089Spjd verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 5599219089Spjd sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); 5600219089Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 5601219089Spjd sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 5602219089Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts, 5603219089Spjd sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0); 5604168404Spjd 5605219089Spjd if (op == OP_SHARE && strcmp(shareopts, "off") == 0 && 5606219089Spjd strcmp(smbshareopts, "off") == 0) { 5607219089Spjd if (!explicit) 5608219089Spjd return (0); 5609168404Spjd 5610219089Spjd (void) fprintf(stderr, gettext("cannot share '%s': " 5611219089Spjd "legacy share\n"), zfs_get_name(zhp)); 5612252732Smm (void) fprintf(stderr, gettext("to " 5613252732Smm "share this filesystem set " 5614219089Spjd "sharenfs property on\n")); 5615219089Spjd return (1); 5616219089Spjd } 5617168404Spjd 5618219089Spjd /* 5619219089Spjd * We cannot share or mount legacy filesystems. If the 5620219089Spjd * shareopts is non-legacy but the mountpoint is legacy, we 5621219089Spjd * treat it as a legacy share. 5622219089Spjd */ 5623219089Spjd if (strcmp(mountpoint, "legacy") == 0) { 5624219089Spjd if (!explicit) 5625219089Spjd return (0); 5626168404Spjd 5627219089Spjd (void) fprintf(stderr, gettext("cannot %s '%s': " 5628219089Spjd "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 5629252732Smm (void) fprintf(stderr, gettext("use %s(8) to " 5630219089Spjd "%s this filesystem\n"), cmdname, cmdname); 5631219089Spjd return (1); 5632219089Spjd } 5633168404Spjd 5634219089Spjd if (strcmp(mountpoint, "none") == 0) { 5635219089Spjd if (!explicit) 5636219089Spjd return (0); 5637168404Spjd 5638219089Spjd (void) fprintf(stderr, gettext("cannot %s '%s': no " 5639219089Spjd "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 5640219089Spjd return (1); 5641219089Spjd } 5642168404Spjd 5643219089Spjd /* 5644219089Spjd * canmount explicit outcome 5645219089Spjd * on no pass through 5646219089Spjd * on yes pass through 5647219089Spjd * off no return 0 5648219089Spjd * off yes display error, return 1 5649219089Spjd * noauto no return 0 5650219089Spjd * noauto yes pass through 5651219089Spjd */ 5652219089Spjd canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); 5653219089Spjd if (canmount == ZFS_CANMOUNT_OFF) { 5654219089Spjd if (!explicit) 5655219089Spjd return (0); 5656219089Spjd 5657219089Spjd (void) fprintf(stderr, gettext("cannot %s '%s': " 5658219089Spjd "'canmount' property is set to 'off'\n"), cmdname, 5659219089Spjd zfs_get_name(zhp)); 5660219089Spjd return (1); 5661219089Spjd } else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) { 5662219089Spjd return (0); 5663219089Spjd } 5664219089Spjd 5665219089Spjd /* 5666219089Spjd * At this point, we have verified that the mountpoint and/or 5667219089Spjd * shareopts are appropriate for auto management. If the 5668219089Spjd * filesystem is already mounted or shared, return (failing 5669219089Spjd * for explicit requests); otherwise mount or share the 5670219089Spjd * filesystem. 5671219089Spjd */ 5672219089Spjd switch (op) { 5673219089Spjd case OP_SHARE: 5674219089Spjd 5675219089Spjd shared_nfs = zfs_is_shared_nfs(zhp, NULL); 5676219089Spjd shared_smb = zfs_is_shared_smb(zhp, NULL); 5677219089Spjd 5678219089Spjd if (shared_nfs && shared_smb || 5679219089Spjd (shared_nfs && strcmp(shareopts, "on") == 0 && 5680219089Spjd strcmp(smbshareopts, "off") == 0) || 5681219089Spjd (shared_smb && strcmp(smbshareopts, "on") == 0 && 5682219089Spjd strcmp(shareopts, "off") == 0)) { 5683168404Spjd if (!explicit) 5684168404Spjd return (0); 5685168404Spjd 5686219089Spjd (void) fprintf(stderr, gettext("cannot share " 5687219089Spjd "'%s': filesystem already shared\n"), 5688168404Spjd zfs_get_name(zhp)); 5689168404Spjd return (1); 5690168404Spjd } 5691168404Spjd 5692219089Spjd if (!zfs_is_mounted(zhp, NULL) && 5693219089Spjd zfs_mount(zhp, NULL, 0) != 0) 5694219089Spjd return (1); 5695185029Spjd 5696219089Spjd if (protocol == NULL) { 5697219089Spjd if (zfs_shareall(zhp) != 0) 5698168404Spjd return (1); 5699219089Spjd } else if (strcmp(protocol, "nfs") == 0) { 5700219089Spjd if (zfs_share_nfs(zhp)) 5701168404Spjd return (1); 5702219089Spjd } else if (strcmp(protocol, "smb") == 0) { 5703219089Spjd if (zfs_share_smb(zhp)) 5704168404Spjd return (1); 5705219089Spjd } else { 5706219089Spjd (void) fprintf(stderr, gettext("cannot share " 5707219089Spjd "'%s': invalid share type '%s' " 5708219089Spjd "specified\n"), 5709219089Spjd zfs_get_name(zhp), protocol); 5710219089Spjd return (1); 5711168404Spjd } 5712168404Spjd 5713219089Spjd break; 5714168404Spjd 5715219089Spjd case OP_MOUNT: 5716219089Spjd if (options == NULL) 5717219089Spjd mnt.mnt_mntopts = ""; 5718219089Spjd else 5719219089Spjd mnt.mnt_mntopts = (char *)options; 5720168404Spjd 5721219089Spjd if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 5722219089Spjd zfs_is_mounted(zhp, NULL)) { 5723168404Spjd if (!explicit) 5724168404Spjd return (0); 5725168404Spjd 5726219089Spjd (void) fprintf(stderr, gettext("cannot mount " 5727219089Spjd "'%s': filesystem already mounted\n"), 5728168404Spjd zfs_get_name(zhp)); 5729168404Spjd return (1); 5730168404Spjd } 5731168404Spjd 5732219089Spjd if (zfs_mount(zhp, options, flags) != 0) 5733168404Spjd return (1); 5734219089Spjd break; 5735168404Spjd } 5736168404Spjd 5737168404Spjd return (0); 5738168404Spjd} 5739168404Spjd 5740185029Spjd/* 5741185029Spjd * Reports progress in the form "(current/total)". Not thread-safe. 5742185029Spjd */ 5743185029Spjdstatic void 5744185029Spjdreport_mount_progress(int current, int total) 5745185029Spjd{ 5746219089Spjd static time_t last_progress_time = 0; 5747185029Spjd time_t now = time(NULL); 5748219089Spjd char info[32]; 5749185029Spjd 5750185029Spjd /* report 1..n instead of 0..n-1 */ 5751185029Spjd ++current; 5752185029Spjd 5753185029Spjd /* display header if we're here for the first time */ 5754185029Spjd if (current == 1) { 5755219089Spjd set_progress_header(gettext("Mounting ZFS filesystems")); 5756185029Spjd } else if (current != total && last_progress_time + MOUNT_TIME >= now) { 5757185029Spjd /* too soon to report again */ 5758185029Spjd return; 5759185029Spjd } 5760185029Spjd 5761185029Spjd last_progress_time = now; 5762185029Spjd 5763219089Spjd (void) sprintf(info, "(%d/%d)", current, total); 5764185029Spjd 5765219089Spjd if (current == total) 5766219089Spjd finish_progress(info); 5767219089Spjd else 5768219089Spjd update_progress(info); 5769185029Spjd} 5770185029Spjd 5771185029Spjdstatic void 5772185029Spjdappend_options(char *mntopts, char *newopts) 5773185029Spjd{ 5774185029Spjd int len = strlen(mntopts); 5775185029Spjd 5776185029Spjd /* original length plus new string to append plus 1 for the comma */ 5777185029Spjd if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) { 5778185029Spjd (void) fprintf(stderr, gettext("the opts argument for " 5779185029Spjd "'%c' option is too long (more than %d chars)\n"), 5780185029Spjd "-o", MNT_LINE_MAX); 5781185029Spjd usage(B_FALSE); 5782185029Spjd } 5783185029Spjd 5784185029Spjd if (*mntopts) 5785185029Spjd mntopts[len++] = ','; 5786185029Spjd 5787185029Spjd (void) strcpy(&mntopts[len], newopts); 5788185029Spjd} 5789185029Spjd 5790168404Spjdstatic int 5791168404Spjdshare_mount(int op, int argc, char **argv) 5792168404Spjd{ 5793168404Spjd int do_all = 0; 5794185029Spjd boolean_t verbose = B_FALSE; 5795168404Spjd int c, ret = 0; 5796185029Spjd char *options = NULL; 5797219089Spjd int flags = 0; 5798168404Spjd 5799168404Spjd /* check options */ 5800185029Spjd while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:O" : "a")) 5801168404Spjd != -1) { 5802168404Spjd switch (c) { 5803168404Spjd case 'a': 5804168404Spjd do_all = 1; 5805168404Spjd break; 5806185029Spjd case 'v': 5807185029Spjd verbose = B_TRUE; 5808185029Spjd break; 5809168404Spjd case 'o': 5810185029Spjd if (*optarg == '\0') { 5811185029Spjd (void) fprintf(stderr, gettext("empty mount " 5812185029Spjd "options (-o) specified\n")); 5813185029Spjd usage(B_FALSE); 5814185029Spjd } 5815185029Spjd 5816185029Spjd if (options == NULL) 5817185029Spjd options = safe_malloc(MNT_LINE_MAX + 1); 5818185029Spjd 5819185029Spjd /* option validation is done later */ 5820185029Spjd append_options(options, optarg); 5821168404Spjd break; 5822185029Spjd 5823168404Spjd case 'O': 5824168404Spjd warnx("no overlay mounts support on FreeBSD, ignoring"); 5825168404Spjd break; 5826168404Spjd case ':': 5827168404Spjd (void) fprintf(stderr, gettext("missing argument for " 5828168404Spjd "'%c' option\n"), optopt); 5829168404Spjd usage(B_FALSE); 5830168404Spjd break; 5831168404Spjd case '?': 5832168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 5833168404Spjd optopt); 5834168404Spjd usage(B_FALSE); 5835168404Spjd } 5836168404Spjd } 5837168404Spjd 5838168404Spjd argc -= optind; 5839168404Spjd argv += optind; 5840168404Spjd 5841168404Spjd /* check number of arguments */ 5842168404Spjd if (do_all) { 5843168404Spjd zfs_handle_t **dslist = NULL; 5844168404Spjd size_t i, count = 0; 5845185029Spjd char *protocol = NULL; 5846168404Spjd 5847219089Spjd if (op == OP_SHARE && argc > 0) { 5848219089Spjd if (strcmp(argv[0], "nfs") != 0 && 5849219089Spjd strcmp(argv[0], "smb") != 0) { 5850168404Spjd (void) fprintf(stderr, gettext("share type " 5851219089Spjd "must be 'nfs' or 'smb'\n")); 5852168404Spjd usage(B_FALSE); 5853168404Spjd } 5854185029Spjd protocol = argv[0]; 5855168404Spjd argc--; 5856168404Spjd argv++; 5857168404Spjd } 5858168404Spjd 5859168404Spjd if (argc != 0) { 5860168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 5861168404Spjd usage(B_FALSE); 5862168404Spjd } 5863168404Spjd 5864219089Spjd start_progress_timer(); 5865219089Spjd get_all_datasets(&dslist, &count, verbose); 5866168404Spjd 5867168404Spjd if (count == 0) 5868168404Spjd return (0); 5869168404Spjd 5870219089Spjd qsort(dslist, count, sizeof (void *), libzfs_dataset_cmp); 5871168404Spjd 5872168404Spjd for (i = 0; i < count; i++) { 5873185029Spjd if (verbose) 5874185029Spjd report_mount_progress(i, count); 5875185029Spjd 5876185029Spjd if (share_mount_one(dslist[i], op, flags, protocol, 5877185029Spjd B_FALSE, options) != 0) 5878168404Spjd ret = 1; 5879168404Spjd zfs_close(dslist[i]); 5880168404Spjd } 5881168404Spjd 5882168404Spjd free(dslist); 5883168404Spjd } else if (argc == 0) { 5884219089Spjd struct mnttab entry; 5885168404Spjd 5886185029Spjd if ((op == OP_SHARE) || (options != NULL)) { 5887168404Spjd (void) fprintf(stderr, gettext("missing filesystem " 5888185029Spjd "argument (specify -a for all)\n")); 5889168404Spjd usage(B_FALSE); 5890168404Spjd } 5891168404Spjd 5892168404Spjd /* 5893168404Spjd * When mount is given no arguments, go through /etc/mnttab and 5894168404Spjd * display any active ZFS mounts. We hide any snapshots, since 5895168404Spjd * they are controlled automatically. 5896168404Spjd */ 5897219089Spjd rewind(mnttab_file); 5898219089Spjd while (getmntent(mnttab_file, &entry) == 0) { 5899219089Spjd if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 5900219089Spjd strchr(entry.mnt_special, '@') != NULL) 5901168404Spjd continue; 5902168404Spjd 5903219089Spjd (void) printf("%-30s %s\n", entry.mnt_special, 5904219089Spjd entry.mnt_mountp); 5905168404Spjd } 5906168404Spjd 5907168404Spjd } else { 5908168404Spjd zfs_handle_t *zhp; 5909168404Spjd 5910168404Spjd if (argc > 1) { 5911168404Spjd (void) fprintf(stderr, 5912168404Spjd gettext("too many arguments\n")); 5913168404Spjd usage(B_FALSE); 5914168404Spjd } 5915168404Spjd 5916219089Spjd if ((zhp = zfs_open(g_zfs, argv[0], 5917219089Spjd ZFS_TYPE_FILESYSTEM)) == NULL) { 5918168404Spjd ret = 1; 5919168404Spjd } else { 5920185029Spjd ret = share_mount_one(zhp, op, flags, NULL, B_TRUE, 5921168404Spjd options); 5922168404Spjd zfs_close(zhp); 5923168404Spjd } 5924168404Spjd } 5925168404Spjd 5926168404Spjd return (ret); 5927168404Spjd} 5928168404Spjd 5929168404Spjd/* 5930219089Spjd * zfs mount -a [nfs] 5931168404Spjd * zfs mount filesystem 5932168404Spjd * 5933168404Spjd * Mount all filesystems, or mount the given filesystem. 5934168404Spjd */ 5935168404Spjdstatic int 5936168404Spjdzfs_do_mount(int argc, char **argv) 5937168404Spjd{ 5938168404Spjd return (share_mount(OP_MOUNT, argc, argv)); 5939168404Spjd} 5940168404Spjd 5941168404Spjd/* 5942219089Spjd * zfs share -a [nfs | smb] 5943168404Spjd * zfs share filesystem 5944168404Spjd * 5945168404Spjd * Share all filesystems, or share the given filesystem. 5946168404Spjd */ 5947168404Spjdstatic int 5948168404Spjdzfs_do_share(int argc, char **argv) 5949168404Spjd{ 5950168404Spjd return (share_mount(OP_SHARE, argc, argv)); 5951168404Spjd} 5952168404Spjd 5953168404Spjdtypedef struct unshare_unmount_node { 5954168404Spjd zfs_handle_t *un_zhp; 5955168404Spjd char *un_mountp; 5956168404Spjd uu_avl_node_t un_avlnode; 5957168404Spjd} unshare_unmount_node_t; 5958168404Spjd 5959168404Spjd/* ARGSUSED */ 5960168404Spjdstatic int 5961168404Spjdunshare_unmount_compare(const void *larg, const void *rarg, void *unused) 5962168404Spjd{ 5963168404Spjd const unshare_unmount_node_t *l = larg; 5964168404Spjd const unshare_unmount_node_t *r = rarg; 5965168404Spjd 5966168404Spjd return (strcmp(l->un_mountp, r->un_mountp)); 5967168404Spjd} 5968168404Spjd 5969168404Spjd/* 5970168404Spjd * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 5971168404Spjd * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 5972168404Spjd * and unmount it appropriately. 5973168404Spjd */ 5974168404Spjdstatic int 5975168404Spjdunshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) 5976168404Spjd{ 5977168404Spjd zfs_handle_t *zhp; 5978231144Smm int ret = 0; 5979185029Spjd struct stat64 statbuf; 5980219089Spjd struct extmnttab entry; 5981168404Spjd const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount"; 5982185029Spjd ino_t path_inode; 5983168404Spjd 5984168404Spjd /* 5985185029Spjd * Search for the path in /etc/mnttab. Rather than looking for the 5986185029Spjd * specific path, which can be fooled by non-standard paths (i.e. ".." 5987185029Spjd * or "//"), we stat() the path and search for the corresponding 5988185029Spjd * (major,minor) device pair. 5989185029Spjd */ 5990185029Spjd if (stat64(path, &statbuf) != 0) { 5991185029Spjd (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 5992185029Spjd cmdname, path, strerror(errno)); 5993185029Spjd return (1); 5994185029Spjd } 5995185029Spjd path_inode = statbuf.st_ino; 5996185029Spjd 5997185029Spjd /* 5998168404Spjd * Search for the given (major,minor) pair in the mount table. 5999168404Spjd */ 6000219089Spjd#ifdef sun 6001168404Spjd rewind(mnttab_file); 6002219089Spjd while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 6003219089Spjd if (entry.mnt_major == major(statbuf.st_dev) && 6004219089Spjd entry.mnt_minor == minor(statbuf.st_dev)) 6005219089Spjd break; 6006219089Spjd } 6007219089Spjd#else 6008219089Spjd { 6009219089Spjd struct statfs sfs; 6010219089Spjd 6011219089Spjd if (statfs(path, &sfs) != 0) { 6012219089Spjd (void) fprintf(stderr, "%s: %s\n", path, 6013219089Spjd strerror(errno)); 6014219089Spjd ret = -1; 6015219089Spjd } 6016219089Spjd statfs2mnttab(&sfs, &entry); 6017219089Spjd } 6018219089Spjd#endif 6019219089Spjd if (ret != 0) { 6020185029Spjd if (op == OP_SHARE) { 6021185029Spjd (void) fprintf(stderr, gettext("cannot %s '%s': not " 6022185029Spjd "currently mounted\n"), cmdname, path); 6023185029Spjd return (1); 6024185029Spjd } 6025185029Spjd (void) fprintf(stderr, gettext("warning: %s not in mnttab\n"), 6026185029Spjd path); 6027185029Spjd if ((ret = umount2(path, flags)) != 0) 6028185029Spjd (void) fprintf(stderr, gettext("%s: %s\n"), path, 6029185029Spjd strerror(errno)); 6030185029Spjd return (ret != 0); 6031168404Spjd } 6032168404Spjd 6033168404Spjd if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 6034168404Spjd (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 6035168404Spjd "filesystem\n"), cmdname, path); 6036168404Spjd return (1); 6037168404Spjd } 6038168404Spjd 6039168404Spjd if ((zhp = zfs_open(g_zfs, entry.mnt_special, 6040168404Spjd ZFS_TYPE_FILESYSTEM)) == NULL) 6041168404Spjd return (1); 6042168404Spjd 6043185029Spjd ret = 1; 6044185029Spjd if (stat64(entry.mnt_mountp, &statbuf) != 0) { 6045185029Spjd (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 6046185029Spjd cmdname, path, strerror(errno)); 6047185029Spjd goto out; 6048185029Spjd } else if (statbuf.st_ino != path_inode) { 6049185029Spjd (void) fprintf(stderr, gettext("cannot " 6050185029Spjd "%s '%s': not a mountpoint\n"), cmdname, path); 6051185029Spjd goto out; 6052185029Spjd } 6053168404Spjd 6054168404Spjd if (op == OP_SHARE) { 6055185029Spjd char nfs_mnt_prop[ZFS_MAXPROPLEN]; 6056185029Spjd char smbshare_prop[ZFS_MAXPROPLEN]; 6057185029Spjd 6058185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, nfs_mnt_prop, 6059185029Spjd sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0); 6060185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshare_prop, 6061185029Spjd sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0); 6062185029Spjd 6063185029Spjd if (strcmp(nfs_mnt_prop, "off") == 0 && 6064185029Spjd strcmp(smbshare_prop, "off") == 0) { 6065168404Spjd (void) fprintf(stderr, gettext("cannot unshare " 6066168404Spjd "'%s': legacy share\n"), path); 6067252732Smm#ifdef illumos 6068168404Spjd (void) fprintf(stderr, gettext("use " 6069168404Spjd "unshare(1M) to unshare this filesystem\n")); 6070252732Smm#endif 6071185029Spjd } else if (!zfs_is_shared(zhp)) { 6072168404Spjd (void) fprintf(stderr, gettext("cannot unshare '%s': " 6073168404Spjd "not currently shared\n"), path); 6074168404Spjd } else { 6075185029Spjd ret = zfs_unshareall_bypath(zhp, path); 6076168404Spjd } 6077168404Spjd } else { 6078185029Spjd char mtpt_prop[ZFS_MAXPROPLEN]; 6079185029Spjd 6080185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop, 6081185029Spjd sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0); 6082185029Spjd 6083168404Spjd if (is_manual) { 6084168404Spjd ret = zfs_unmount(zhp, NULL, flags); 6085185029Spjd } else if (strcmp(mtpt_prop, "legacy") == 0) { 6086168404Spjd (void) fprintf(stderr, gettext("cannot unmount " 6087168404Spjd "'%s': legacy mountpoint\n"), 6088168404Spjd zfs_get_name(zhp)); 6089252732Smm (void) fprintf(stderr, gettext("use umount(8) " 6090168404Spjd "to unmount this filesystem\n")); 6091168404Spjd } else { 6092168404Spjd ret = zfs_unmountall(zhp, flags); 6093168404Spjd } 6094168404Spjd } 6095168404Spjd 6096185029Spjdout: 6097168404Spjd zfs_close(zhp); 6098168404Spjd 6099168404Spjd return (ret != 0); 6100168404Spjd} 6101168404Spjd 6102168404Spjd/* 6103168404Spjd * Generic callback for unsharing or unmounting a filesystem. 6104168404Spjd */ 6105168404Spjdstatic int 6106168404Spjdunshare_unmount(int op, int argc, char **argv) 6107168404Spjd{ 6108168404Spjd int do_all = 0; 6109168404Spjd int flags = 0; 6110168404Spjd int ret = 0; 6111219089Spjd int c; 6112168404Spjd zfs_handle_t *zhp; 6113219089Spjd char nfs_mnt_prop[ZFS_MAXPROPLEN]; 6114185029Spjd char sharesmb[ZFS_MAXPROPLEN]; 6115168404Spjd 6116168404Spjd /* check options */ 6117168404Spjd while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) { 6118168404Spjd switch (c) { 6119168404Spjd case 'a': 6120168404Spjd do_all = 1; 6121168404Spjd break; 6122168404Spjd case 'f': 6123168404Spjd flags = MS_FORCE; 6124168404Spjd break; 6125168404Spjd case '?': 6126168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 6127168404Spjd optopt); 6128168404Spjd usage(B_FALSE); 6129168404Spjd } 6130168404Spjd } 6131168404Spjd 6132168404Spjd argc -= optind; 6133168404Spjd argv += optind; 6134168404Spjd 6135168404Spjd if (do_all) { 6136168404Spjd /* 6137168404Spjd * We could make use of zfs_for_each() to walk all datasets in 6138168404Spjd * the system, but this would be very inefficient, especially 6139168404Spjd * since we would have to linearly search /etc/mnttab for each 6140168404Spjd * one. Instead, do one pass through /etc/mnttab looking for 6141168404Spjd * zfs entries and call zfs_unmount() for each one. 6142168404Spjd * 6143168404Spjd * Things get a little tricky if the administrator has created 6144168404Spjd * mountpoints beneath other ZFS filesystems. In this case, we 6145168404Spjd * have to unmount the deepest filesystems first. To accomplish 6146168404Spjd * this, we place all the mountpoints in an AVL tree sorted by 6147168404Spjd * the special type (dataset name), and walk the result in 6148168404Spjd * reverse to make sure to get any snapshots first. 6149168404Spjd */ 6150219089Spjd struct mnttab entry; 6151168404Spjd uu_avl_pool_t *pool; 6152168404Spjd uu_avl_t *tree; 6153168404Spjd unshare_unmount_node_t *node; 6154168404Spjd uu_avl_index_t idx; 6155168404Spjd uu_avl_walk_t *walk; 6156168404Spjd 6157168404Spjd if (argc != 0) { 6158168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 6159168404Spjd usage(B_FALSE); 6160168404Spjd } 6161168404Spjd 6162219089Spjd if (((pool = uu_avl_pool_create("unmount_pool", 6163168404Spjd sizeof (unshare_unmount_node_t), 6164168404Spjd offsetof(unshare_unmount_node_t, un_avlnode), 6165219089Spjd unshare_unmount_compare, UU_DEFAULT)) == NULL) || 6166219089Spjd ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL)) 6167219089Spjd nomem(); 6168168404Spjd 6169219089Spjd rewind(mnttab_file); 6170219089Spjd while (getmntent(mnttab_file, &entry) == 0) { 6171168404Spjd 6172168404Spjd /* ignore non-ZFS entries */ 6173219089Spjd if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 6174168404Spjd continue; 6175168404Spjd 6176168404Spjd /* ignore snapshots */ 6177219089Spjd if (strchr(entry.mnt_special, '@') != NULL) 6178168404Spjd continue; 6179168404Spjd 6180219089Spjd if ((zhp = zfs_open(g_zfs, entry.mnt_special, 6181168404Spjd ZFS_TYPE_FILESYSTEM)) == NULL) { 6182168404Spjd ret = 1; 6183168404Spjd continue; 6184168404Spjd } 6185168404Spjd 6186185029Spjd switch (op) { 6187185029Spjd case OP_SHARE: 6188185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, 6189219089Spjd nfs_mnt_prop, 6190219089Spjd sizeof (nfs_mnt_prop), 6191185029Spjd NULL, NULL, 0, B_FALSE) == 0); 6192219089Spjd if (strcmp(nfs_mnt_prop, "off") != 0) 6193185029Spjd break; 6194185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, 6195219089Spjd nfs_mnt_prop, 6196219089Spjd sizeof (nfs_mnt_prop), 6197185029Spjd NULL, NULL, 0, B_FALSE) == 0); 6198219089Spjd if (strcmp(nfs_mnt_prop, "off") == 0) 6199185029Spjd continue; 6200185029Spjd break; 6201185029Spjd case OP_MOUNT: 6202185029Spjd /* Ignore legacy mounts */ 6203185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, 6204219089Spjd nfs_mnt_prop, 6205219089Spjd sizeof (nfs_mnt_prop), 6206185029Spjd NULL, NULL, 0, B_FALSE) == 0); 6207219089Spjd if (strcmp(nfs_mnt_prop, "legacy") == 0) 6208185029Spjd continue; 6209185029Spjd /* Ignore canmount=noauto mounts */ 6210185029Spjd if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == 6211185029Spjd ZFS_CANMOUNT_NOAUTO) 6212185029Spjd continue; 6213185029Spjd default: 6214185029Spjd break; 6215168404Spjd } 6216168404Spjd 6217168404Spjd node = safe_malloc(sizeof (unshare_unmount_node_t)); 6218168404Spjd node->un_zhp = zhp; 6219219089Spjd node->un_mountp = safe_strdup(entry.mnt_mountp); 6220168404Spjd 6221168404Spjd uu_avl_node_init(node, &node->un_avlnode, pool); 6222168404Spjd 6223168404Spjd if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 6224168404Spjd uu_avl_insert(tree, node, idx); 6225168404Spjd } else { 6226168404Spjd zfs_close(node->un_zhp); 6227168404Spjd free(node->un_mountp); 6228168404Spjd free(node); 6229168404Spjd } 6230168404Spjd } 6231168404Spjd 6232168404Spjd /* 6233168404Spjd * Walk the AVL tree in reverse, unmounting each filesystem and 6234168404Spjd * removing it from the AVL tree in the process. 6235168404Spjd */ 6236168404Spjd if ((walk = uu_avl_walk_start(tree, 6237219089Spjd UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) 6238219089Spjd nomem(); 6239168404Spjd 6240168404Spjd while ((node = uu_avl_walk_next(walk)) != NULL) { 6241168404Spjd uu_avl_remove(tree, node); 6242168404Spjd 6243168404Spjd switch (op) { 6244168404Spjd case OP_SHARE: 6245185029Spjd if (zfs_unshareall_bypath(node->un_zhp, 6246168404Spjd node->un_mountp) != 0) 6247168404Spjd ret = 1; 6248168404Spjd break; 6249168404Spjd 6250168404Spjd case OP_MOUNT: 6251168404Spjd if (zfs_unmount(node->un_zhp, 6252168404Spjd node->un_mountp, flags) != 0) 6253168404Spjd ret = 1; 6254168404Spjd break; 6255168404Spjd } 6256168404Spjd 6257168404Spjd zfs_close(node->un_zhp); 6258168404Spjd free(node->un_mountp); 6259168404Spjd free(node); 6260168404Spjd } 6261168404Spjd 6262168404Spjd uu_avl_walk_end(walk); 6263168404Spjd uu_avl_destroy(tree); 6264168404Spjd uu_avl_pool_destroy(pool); 6265168404Spjd 6266168404Spjd } else { 6267168404Spjd if (argc != 1) { 6268168404Spjd if (argc == 0) 6269168404Spjd (void) fprintf(stderr, 6270168404Spjd gettext("missing filesystem argument\n")); 6271168404Spjd else 6272168404Spjd (void) fprintf(stderr, 6273168404Spjd gettext("too many arguments\n")); 6274168404Spjd usage(B_FALSE); 6275168404Spjd } 6276168404Spjd 6277168404Spjd /* 6278168404Spjd * We have an argument, but it may be a full path or a ZFS 6279168404Spjd * filesystem. Pass full paths off to unmount_path() (shared by 6280168404Spjd * manual_unmount), otherwise open the filesystem and pass to 6281168404Spjd * zfs_unmount(). 6282168404Spjd */ 6283168404Spjd if (argv[0][0] == '/') 6284168404Spjd return (unshare_unmount_path(op, argv[0], 6285168404Spjd flags, B_FALSE)); 6286168404Spjd 6287219089Spjd if ((zhp = zfs_open(g_zfs, argv[0], 6288219089Spjd ZFS_TYPE_FILESYSTEM)) == NULL) 6289168404Spjd return (1); 6290168404Spjd 6291219089Spjd verify(zfs_prop_get(zhp, op == OP_SHARE ? 6292219089Spjd ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 6293219089Spjd nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL, 6294219089Spjd NULL, 0, B_FALSE) == 0); 6295168404Spjd 6296219089Spjd switch (op) { 6297219089Spjd case OP_SHARE: 6298219089Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, 6299219089Spjd nfs_mnt_prop, 6300219089Spjd sizeof (nfs_mnt_prop), 6301185029Spjd NULL, NULL, 0, B_FALSE) == 0); 6302219089Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, 6303219089Spjd sharesmb, sizeof (sharesmb), NULL, NULL, 6304219089Spjd 0, B_FALSE) == 0); 6305168404Spjd 6306219089Spjd if (strcmp(nfs_mnt_prop, "off") == 0 && 6307219089Spjd strcmp(sharesmb, "off") == 0) { 6308219089Spjd (void) fprintf(stderr, gettext("cannot " 6309219089Spjd "unshare '%s': legacy share\n"), 6310168404Spjd zfs_get_name(zhp)); 6311252732Smm#ifdef illumos 6312219089Spjd (void) fprintf(stderr, gettext("use " 6313219089Spjd "unshare(1M) to unshare this " 6314219089Spjd "filesystem\n")); 6315252732Smm#endif 6316168404Spjd ret = 1; 6317219089Spjd } else if (!zfs_is_shared(zhp)) { 6318168404Spjd (void) fprintf(stderr, gettext("cannot " 6319219089Spjd "unshare '%s': not currently " 6320219089Spjd "shared\n"), zfs_get_name(zhp)); 6321219089Spjd ret = 1; 6322219089Spjd } else if (zfs_unshareall(zhp) != 0) { 6323219089Spjd ret = 1; 6324219089Spjd } 6325219089Spjd break; 6326219089Spjd 6327219089Spjd case OP_MOUNT: 6328219089Spjd if (strcmp(nfs_mnt_prop, "legacy") == 0) { 6329219089Spjd (void) fprintf(stderr, gettext("cannot " 6330219089Spjd "unmount '%s': legacy " 6331219089Spjd "mountpoint\n"), zfs_get_name(zhp)); 6332219089Spjd (void) fprintf(stderr, gettext("use " 6333252732Smm "umount(8) to unmount this " 6334219089Spjd "filesystem\n")); 6335219089Spjd ret = 1; 6336219089Spjd } else if (!zfs_is_mounted(zhp, NULL)) { 6337219089Spjd (void) fprintf(stderr, gettext("cannot " 6338219089Spjd "unmount '%s': not currently " 6339219089Spjd "mounted\n"), 6340168404Spjd zfs_get_name(zhp)); 6341168404Spjd ret = 1; 6342219089Spjd } else if (zfs_unmountall(zhp, flags) != 0) { 6343168404Spjd ret = 1; 6344168404Spjd } 6345219089Spjd break; 6346168404Spjd } 6347168404Spjd 6348168404Spjd zfs_close(zhp); 6349168404Spjd } 6350168404Spjd 6351168404Spjd return (ret); 6352168404Spjd} 6353168404Spjd 6354168404Spjd/* 6355168404Spjd * zfs unmount -a 6356168404Spjd * zfs unmount filesystem 6357168404Spjd * 6358168404Spjd * Unmount all filesystems, or a specific ZFS filesystem. 6359168404Spjd */ 6360168404Spjdstatic int 6361168404Spjdzfs_do_unmount(int argc, char **argv) 6362168404Spjd{ 6363168404Spjd return (unshare_unmount(OP_MOUNT, argc, argv)); 6364168404Spjd} 6365168404Spjd 6366168404Spjd/* 6367168404Spjd * zfs unshare -a 6368168404Spjd * zfs unshare filesystem 6369168404Spjd * 6370168404Spjd * Unshare all filesystems, or a specific ZFS filesystem. 6371168404Spjd */ 6372168404Spjdstatic int 6373168404Spjdzfs_do_unshare(int argc, char **argv) 6374168404Spjd{ 6375168404Spjd return (unshare_unmount(OP_SHARE, argc, argv)); 6376168404Spjd} 6377168404Spjd 6378168404Spjd/* 6379168404Spjd * Attach/detach the given dataset to/from the given jail 6380168404Spjd */ 6381168404Spjd/* ARGSUSED */ 6382168404Spjdstatic int 6383168404Spjddo_jail(int argc, char **argv, int attach) 6384168404Spjd{ 6385168404Spjd zfs_handle_t *zhp; 6386168404Spjd int jailid, ret; 6387168404Spjd 6388168404Spjd /* check number of arguments */ 6389168404Spjd if (argc < 3) { 6390168404Spjd (void) fprintf(stderr, gettext("missing argument(s)\n")); 6391168404Spjd usage(B_FALSE); 6392168404Spjd } 6393168404Spjd if (argc > 3) { 6394168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 6395168404Spjd usage(B_FALSE); 6396168404Spjd } 6397168404Spjd 6398240696Sbapt jailid = jail_getid(argv[1]); 6399240696Sbapt if (jailid < 0) { 6400240696Sbapt (void) fprintf(stderr, gettext("invalid jail id or name\n")); 6401168404Spjd usage(B_FALSE); 6402168404Spjd } 6403168404Spjd 6404168404Spjd zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM); 6405168404Spjd if (zhp == NULL) 6406168404Spjd return (1); 6407168404Spjd 6408168404Spjd ret = (zfs_jail(zhp, jailid, attach) != 0); 6409168404Spjd 6410168404Spjd zfs_close(zhp); 6411168404Spjd return (ret); 6412168404Spjd} 6413168404Spjd 6414168404Spjd/* 6415168404Spjd * zfs jail jailid filesystem 6416168404Spjd * 6417168404Spjd * Attach the given dataset to the given jail 6418168404Spjd */ 6419168404Spjd/* ARGSUSED */ 6420168404Spjdstatic int 6421168404Spjdzfs_do_jail(int argc, char **argv) 6422168404Spjd{ 6423168404Spjd 6424168404Spjd return (do_jail(argc, argv, 1)); 6425168404Spjd} 6426168404Spjd 6427168404Spjd/* 6428168404Spjd * zfs unjail jailid filesystem 6429168404Spjd * 6430168404Spjd * Detach the given dataset from the given jail 6431168404Spjd */ 6432168404Spjd/* ARGSUSED */ 6433168404Spjdstatic int 6434168404Spjdzfs_do_unjail(int argc, char **argv) 6435168404Spjd{ 6436168404Spjd 6437168404Spjd return (do_jail(argc, argv, 0)); 6438168404Spjd} 6439168404Spjd 6440168404Spjd/* 6441168404Spjd * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 6442168404Spjd * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 6443168404Spjd */ 6444168404Spjdstatic int 6445168404Spjdmanual_mount(int argc, char **argv) 6446168404Spjd{ 6447168404Spjd zfs_handle_t *zhp; 6448168404Spjd char mountpoint[ZFS_MAXPROPLEN]; 6449168404Spjd char mntopts[MNT_LINE_MAX] = { '\0' }; 6450231144Smm int ret = 0; 6451168404Spjd int c; 6452168404Spjd int flags = 0; 6453168404Spjd char *dataset, *path; 6454168404Spjd 6455168404Spjd /* check options */ 6456168404Spjd while ((c = getopt(argc, argv, ":mo:O")) != -1) { 6457168404Spjd switch (c) { 6458168404Spjd case 'o': 6459168404Spjd (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 6460168404Spjd break; 6461168404Spjd case 'O': 6462168404Spjd flags |= MS_OVERLAY; 6463168404Spjd break; 6464168404Spjd case 'm': 6465168404Spjd flags |= MS_NOMNTTAB; 6466168404Spjd break; 6467168404Spjd case ':': 6468168404Spjd (void) fprintf(stderr, gettext("missing argument for " 6469168404Spjd "'%c' option\n"), optopt); 6470168404Spjd usage(B_FALSE); 6471168404Spjd break; 6472168404Spjd case '?': 6473168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 6474168404Spjd optopt); 6475168404Spjd (void) fprintf(stderr, gettext("usage: mount [-o opts] " 6476168404Spjd "<path>\n")); 6477168404Spjd return (2); 6478168404Spjd } 6479168404Spjd } 6480168404Spjd 6481168404Spjd argc -= optind; 6482168404Spjd argv += optind; 6483168404Spjd 6484168404Spjd /* check that we only have two arguments */ 6485168404Spjd if (argc != 2) { 6486168404Spjd if (argc == 0) 6487168404Spjd (void) fprintf(stderr, gettext("missing dataset " 6488168404Spjd "argument\n")); 6489168404Spjd else if (argc == 1) 6490168404Spjd (void) fprintf(stderr, 6491168404Spjd gettext("missing mountpoint argument\n")); 6492168404Spjd else 6493168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 6494168404Spjd (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 6495168404Spjd return (2); 6496168404Spjd } 6497168404Spjd 6498168404Spjd dataset = argv[0]; 6499168404Spjd path = argv[1]; 6500168404Spjd 6501168404Spjd /* try to open the dataset */ 6502168404Spjd if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 6503168404Spjd return (1); 6504168404Spjd 6505168404Spjd (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 6506168404Spjd sizeof (mountpoint), NULL, NULL, 0, B_FALSE); 6507168404Spjd 6508168404Spjd /* check for legacy mountpoint and complain appropriately */ 6509168404Spjd ret = 0; 6510168404Spjd if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 6511168404Spjd if (zmount(dataset, path, flags, MNTTYPE_ZFS, 6512168404Spjd NULL, 0, mntopts, sizeof (mntopts)) != 0) { 6513168404Spjd (void) fprintf(stderr, gettext("mount failed: %s\n"), 6514168404Spjd strerror(errno)); 6515168404Spjd ret = 1; 6516168404Spjd } 6517168404Spjd } else { 6518168404Spjd (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 6519252732Smm "mounted using 'mount -t zfs'\n"), dataset); 6520168404Spjd (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 6521168404Spjd "instead.\n"), path); 6522252732Smm (void) fprintf(stderr, gettext("If you must use 'mount -t zfs' " 6523252732Smm "or /etc/fstab, use 'zfs set mountpoint=legacy'.\n")); 6524252732Smm (void) fprintf(stderr, gettext("See zfs(8) for more " 6525168404Spjd "information.\n")); 6526168404Spjd ret = 1; 6527168404Spjd } 6528168404Spjd 6529168404Spjd return (ret); 6530168404Spjd} 6531168404Spjd 6532168404Spjd/* 6533168404Spjd * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 6534168404Spjd * unmounts of non-legacy filesystems, as this is the dominant administrative 6535168404Spjd * interface. 6536168404Spjd */ 6537168404Spjdstatic int 6538168404Spjdmanual_unmount(int argc, char **argv) 6539168404Spjd{ 6540168404Spjd int flags = 0; 6541168404Spjd int c; 6542168404Spjd 6543168404Spjd /* check options */ 6544168404Spjd while ((c = getopt(argc, argv, "f")) != -1) { 6545168404Spjd switch (c) { 6546168404Spjd case 'f': 6547168404Spjd flags = MS_FORCE; 6548168404Spjd break; 6549168404Spjd case '?': 6550168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 6551168404Spjd optopt); 6552168404Spjd (void) fprintf(stderr, gettext("usage: unmount [-f] " 6553168404Spjd "<path>\n")); 6554168404Spjd return (2); 6555168404Spjd } 6556168404Spjd } 6557168404Spjd 6558168404Spjd argc -= optind; 6559168404Spjd argv += optind; 6560168404Spjd 6561168404Spjd /* check arguments */ 6562168404Spjd if (argc != 1) { 6563168404Spjd if (argc == 0) 6564168404Spjd (void) fprintf(stderr, gettext("missing path " 6565168404Spjd "argument\n")); 6566168404Spjd else 6567168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 6568168404Spjd (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 6569168404Spjd return (2); 6570168404Spjd } 6571168404Spjd 6572168404Spjd return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE)); 6573168404Spjd} 6574168404Spjd 6575168404Spjdstatic int 6576185029Spjdfind_command_idx(char *command, int *idx) 6577185029Spjd{ 6578185029Spjd int i; 6579185029Spjd 6580185029Spjd for (i = 0; i < NCOMMAND; i++) { 6581185029Spjd if (command_table[i].name == NULL) 6582185029Spjd continue; 6583185029Spjd 6584185029Spjd if (strcmp(command, command_table[i].name) == 0) { 6585185029Spjd *idx = i; 6586185029Spjd return (0); 6587185029Spjd } 6588185029Spjd } 6589185029Spjd return (1); 6590185029Spjd} 6591185029Spjd 6592219089Spjdstatic int 6593219089Spjdzfs_do_diff(int argc, char **argv) 6594219089Spjd{ 6595219089Spjd zfs_handle_t *zhp; 6596219089Spjd int flags = 0; 6597219089Spjd char *tosnap = NULL; 6598219089Spjd char *fromsnap = NULL; 6599219089Spjd char *atp, *copy; 6600231144Smm int err = 0; 6601219089Spjd int c; 6602219089Spjd 6603219089Spjd while ((c = getopt(argc, argv, "FHt")) != -1) { 6604219089Spjd switch (c) { 6605219089Spjd case 'F': 6606219089Spjd flags |= ZFS_DIFF_CLASSIFY; 6607219089Spjd break; 6608219089Spjd case 'H': 6609219089Spjd flags |= ZFS_DIFF_PARSEABLE; 6610219089Spjd break; 6611219089Spjd case 't': 6612219089Spjd flags |= ZFS_DIFF_TIMESTAMP; 6613219089Spjd break; 6614219089Spjd default: 6615219089Spjd (void) fprintf(stderr, 6616219089Spjd gettext("invalid option '%c'\n"), optopt); 6617219089Spjd usage(B_FALSE); 6618219089Spjd } 6619219089Spjd } 6620219089Spjd 6621219089Spjd argc -= optind; 6622219089Spjd argv += optind; 6623219089Spjd 6624219089Spjd if (argc < 1) { 6625219089Spjd (void) fprintf(stderr, 6626219089Spjd gettext("must provide at least one snapshot name\n")); 6627219089Spjd usage(B_FALSE); 6628219089Spjd } 6629219089Spjd 6630219089Spjd if (argc > 2) { 6631219089Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 6632219089Spjd usage(B_FALSE); 6633219089Spjd } 6634219089Spjd 6635219089Spjd fromsnap = argv[0]; 6636219089Spjd tosnap = (argc == 2) ? argv[1] : NULL; 6637219089Spjd 6638219089Spjd copy = NULL; 6639219089Spjd if (*fromsnap != '@') 6640219089Spjd copy = strdup(fromsnap); 6641219089Spjd else if (tosnap) 6642219089Spjd copy = strdup(tosnap); 6643219089Spjd if (copy == NULL) 6644219089Spjd usage(B_FALSE); 6645219089Spjd 6646219089Spjd if (atp = strchr(copy, '@')) 6647219089Spjd *atp = '\0'; 6648219089Spjd 6649219089Spjd if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL) 6650219089Spjd return (1); 6651219089Spjd 6652219089Spjd free(copy); 6653219089Spjd 6654219089Spjd /* 6655219089Spjd * Ignore SIGPIPE so that the library can give us 6656219089Spjd * information on any failure 6657219089Spjd */ 6658219089Spjd (void) sigignore(SIGPIPE); 6659219089Spjd 6660219089Spjd err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags); 6661219089Spjd 6662219089Spjd zfs_close(zhp); 6663219089Spjd 6664219089Spjd return (err != 0); 6665219089Spjd} 6666219089Spjd 6667168404Spjdint 6668168404Spjdmain(int argc, char **argv) 6669168404Spjd{ 6670231144Smm int ret = 0; 6671168404Spjd int i; 6672168404Spjd char *progname; 6673168404Spjd char *cmdname; 6674168404Spjd 6675168404Spjd (void) setlocale(LC_ALL, ""); 6676168404Spjd (void) textdomain(TEXT_DOMAIN); 6677168404Spjd 6678168404Spjd opterr = 0; 6679168404Spjd 6680168404Spjd if ((g_zfs = libzfs_init()) == NULL) { 6681168404Spjd (void) fprintf(stderr, gettext("internal error: failed to " 6682168404Spjd "initialize ZFS library\n")); 6683168404Spjd return (1); 6684168404Spjd } 6685168404Spjd 6686248571Smm zfs_save_arguments(argc, argv, history_str, sizeof (history_str)); 6687185029Spjd 6688168404Spjd libzfs_print_on_error(g_zfs, B_TRUE); 6689168404Spjd 6690168404Spjd if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 6691168404Spjd (void) fprintf(stderr, gettext("internal error: unable to " 6692168404Spjd "open %s\n"), MNTTAB); 6693168404Spjd return (1); 6694168404Spjd } 6695168404Spjd 6696168404Spjd /* 6697168404Spjd * This command also doubles as the /etc/fs mount and unmount program. 6698168404Spjd * Determine if we should take this behavior based on argv[0]. 6699168404Spjd */ 6700168404Spjd progname = basename(argv[0]); 6701168404Spjd if (strcmp(progname, "mount") == 0) { 6702168404Spjd ret = manual_mount(argc, argv); 6703168404Spjd } else if (strcmp(progname, "umount") == 0) { 6704168404Spjd ret = manual_unmount(argc, argv); 6705168404Spjd } else { 6706168404Spjd /* 6707168404Spjd * Make sure the user has specified some command. 6708168404Spjd */ 6709168404Spjd if (argc < 2) { 6710168404Spjd (void) fprintf(stderr, gettext("missing command\n")); 6711168404Spjd usage(B_FALSE); 6712168404Spjd } 6713168404Spjd 6714168404Spjd cmdname = argv[1]; 6715168404Spjd 6716168404Spjd /* 6717168404Spjd * The 'umount' command is an alias for 'unmount' 6718168404Spjd */ 6719168404Spjd if (strcmp(cmdname, "umount") == 0) 6720168404Spjd cmdname = "unmount"; 6721168404Spjd 6722168404Spjd /* 6723168404Spjd * The 'recv' command is an alias for 'receive' 6724168404Spjd */ 6725168404Spjd if (strcmp(cmdname, "recv") == 0) 6726168404Spjd cmdname = "receive"; 6727168404Spjd 6728168404Spjd /* 6729168404Spjd * Special case '-?' 6730168404Spjd */ 6731168404Spjd if (strcmp(cmdname, "-?") == 0) 6732168404Spjd usage(B_TRUE); 6733168404Spjd 6734168404Spjd /* 6735168404Spjd * Run the appropriate command. 6736168404Spjd */ 6737209962Smm libzfs_mnttab_cache(g_zfs, B_TRUE); 6738185029Spjd if (find_command_idx(cmdname, &i) == 0) { 6739185029Spjd current_command = &command_table[i]; 6740185029Spjd ret = command_table[i].func(argc - 1, argv + 1); 6741185029Spjd } else if (strchr(cmdname, '=') != NULL) { 6742185029Spjd verify(find_command_idx("set", &i) == 0); 6743185029Spjd current_command = &command_table[i]; 6744185029Spjd ret = command_table[i].func(argc, argv); 6745185029Spjd } else { 6746168404Spjd (void) fprintf(stderr, gettext("unrecognized " 6747168404Spjd "command '%s'\n"), cmdname); 6748168404Spjd usage(B_FALSE); 6749168404Spjd } 6750209962Smm libzfs_mnttab_cache(g_zfs, B_FALSE); 6751168404Spjd } 6752168404Spjd 6753168404Spjd (void) fclose(mnttab_file); 6754168404Spjd 6755248571Smm if (ret == 0 && log_history) 6756248571Smm (void) zpool_log_history(g_zfs, history_str); 6757248571Smm 6758168404Spjd libzfs_fini(g_zfs); 6759168404Spjd 6760168404Spjd /* 6761168404Spjd * The 'ZFS_ABORT' environment variable causes us to dump core on exit 6762168404Spjd * for the purposes of running ::findleaks. 6763168404Spjd */ 6764168404Spjd if (getenv("ZFS_ABORT") != NULL) { 6765168404Spjd (void) printf("dumping core by request\n"); 6766168404Spjd abort(); 6767168404Spjd } 6768168404Spjd 6769168404Spjd return (ret); 6770168404Spjd} 6771