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. 24325534Savg * Copyright (c) 2011, 2016 by Delphix. All rights reserved. 25235217Smm * Copyright 2012 Milan Jurik. All rights reserved. 26235222Smm * Copyright (c) 2012, Joyent, Inc. All rights reserved. 27307046Smav * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved. 28235216Smm * Copyright (c) 2012 Martin Matuska <mm@FreeBSD.org>. All rights reserved. 29251646Sdelphij * Copyright (c) 2013 Steven Hartland. All rights reserved. 30296519Smav * Copyright (c) 2014 Integros [integros.com] 31296535Smav * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>. 32307106Smav * Copyright 2016 Nexenta Systems, Inc. 33168404Spjd */ 34168404Spjd 35168404Spjd#include <assert.h> 36168404Spjd#include <ctype.h> 37168404Spjd#include <errno.h> 38321535Smav#include <getopt.h> 39168404Spjd#include <libgen.h> 40168404Spjd#include <libintl.h> 41168404Spjd#include <libuutil.h> 42185029Spjd#include <libnvpair.h> 43168404Spjd#include <locale.h> 44168404Spjd#include <stddef.h> 45168404Spjd#include <stdio.h> 46168404Spjd#include <stdlib.h> 47168404Spjd#include <strings.h> 48168404Spjd#include <unistd.h> 49168404Spjd#include <fcntl.h> 50168404Spjd#include <zone.h> 51209962Smm#include <grp.h> 52209962Smm#include <pwd.h> 53219089Spjd#include <signal.h> 54325534Savg#include <sys/debug.h> 55219089Spjd#include <sys/list.h> 56168404Spjd#include <sys/mntent.h> 57168404Spjd#include <sys/mnttab.h> 58168404Spjd#include <sys/mount.h> 59168404Spjd#include <sys/stat.h> 60209962Smm#include <sys/fs/zfs.h> 61219089Spjd#include <sys/types.h> 62219089Spjd#include <time.h> 63253930Srmh#include <err.h> 64256110Srmh#include <jail.h> 65168404Spjd 66168404Spjd#include <libzfs.h> 67248571Smm#include <libzfs_core.h> 68219089Spjd#include <zfs_prop.h> 69219089Spjd#include <zfs_deleg.h> 70185029Spjd#include <libuutil.h> 71277300Ssmh#ifdef illumos 72219089Spjd#include <aclutils.h> 73219089Spjd#include <directory.h> 74275579Sdelphij#include <idmap.h> 75339117Smav#include <libshare.h> 76219089Spjd#endif 77168404Spjd 78168404Spjd#include "zfs_iter.h" 79168404Spjd#include "zfs_util.h" 80219089Spjd#include "zfs_comutil.h" 81168404Spjd 82168404Spjdlibzfs_handle_t *g_zfs; 83168404Spjd 84168404Spjdstatic FILE *mnttab_file; 85185029Spjdstatic char history_str[HIS_MAX_RECORD_LEN]; 86248571Smmstatic boolean_t log_history = B_TRUE; 87168404Spjd 88168404Spjdstatic int zfs_do_clone(int argc, char **argv); 89168404Spjdstatic int zfs_do_create(int argc, char **argv); 90168404Spjdstatic int zfs_do_destroy(int argc, char **argv); 91168404Spjdstatic int zfs_do_get(int argc, char **argv); 92168404Spjdstatic int zfs_do_inherit(int argc, char **argv); 93168404Spjdstatic int zfs_do_list(int argc, char **argv); 94168404Spjdstatic int zfs_do_mount(int argc, char **argv); 95168404Spjdstatic int zfs_do_rename(int argc, char **argv); 96168404Spjdstatic int zfs_do_rollback(int argc, char **argv); 97168404Spjdstatic int zfs_do_set(int argc, char **argv); 98185029Spjdstatic int zfs_do_upgrade(int argc, char **argv); 99168404Spjdstatic int zfs_do_snapshot(int argc, char **argv); 100168404Spjdstatic int zfs_do_unmount(int argc, char **argv); 101168404Spjdstatic int zfs_do_share(int argc, char **argv); 102168404Spjdstatic int zfs_do_unshare(int argc, char **argv); 103168404Spjdstatic int zfs_do_send(int argc, char **argv); 104168404Spjdstatic int zfs_do_receive(int argc, char **argv); 105168404Spjdstatic int zfs_do_promote(int argc, char **argv); 106209962Smmstatic int zfs_do_userspace(int argc, char **argv); 107219089Spjdstatic int zfs_do_allow(int argc, char **argv); 108219089Spjdstatic int zfs_do_unallow(int argc, char **argv); 109219089Spjdstatic int zfs_do_hold(int argc, char **argv); 110219089Spjdstatic int zfs_do_holds(int argc, char **argv); 111219089Spjdstatic int zfs_do_release(int argc, char **argv); 112219089Spjdstatic int zfs_do_diff(int argc, char **argv); 113168404Spjdstatic int zfs_do_jail(int argc, char **argv); 114168404Spjdstatic int zfs_do_unjail(int argc, char **argv); 115260183Sdelphijstatic int zfs_do_bookmark(int argc, char **argv); 116332525Smavstatic int zfs_do_remap(int argc, char **argv); 117325534Savgstatic int zfs_do_channel_program(int argc, char **argv); 118168404Spjd 119168404Spjd/* 120185029Spjd * Enable a reasonable set of defaults for libumem debugging on DEBUG builds. 121168404Spjd */ 122185029Spjd 123185029Spjd#ifdef DEBUG 124168404Spjdconst char * 125168404Spjd_umem_debug_init(void) 126168404Spjd{ 127168404Spjd return ("default,verbose"); /* $UMEM_DEBUG setting */ 128168404Spjd} 129168404Spjd 130168404Spjdconst char * 131168404Spjd_umem_logging_init(void) 132168404Spjd{ 133168404Spjd return ("fail,contents"); /* $UMEM_LOGGING setting */ 134168404Spjd} 135185029Spjd#endif 136168404Spjd 137168404Spjdtypedef enum { 138168404Spjd HELP_CLONE, 139168404Spjd HELP_CREATE, 140168404Spjd HELP_DESTROY, 141168404Spjd HELP_GET, 142168404Spjd HELP_INHERIT, 143185029Spjd HELP_UPGRADE, 144168404Spjd HELP_JAIL, 145168404Spjd HELP_UNJAIL, 146168404Spjd HELP_LIST, 147168404Spjd HELP_MOUNT, 148168404Spjd HELP_PROMOTE, 149168404Spjd HELP_RECEIVE, 150168404Spjd HELP_RENAME, 151168404Spjd HELP_ROLLBACK, 152168404Spjd HELP_SEND, 153168404Spjd HELP_SET, 154168404Spjd HELP_SHARE, 155168404Spjd HELP_SNAPSHOT, 156168404Spjd HELP_UNMOUNT, 157185029Spjd HELP_UNSHARE, 158185029Spjd HELP_ALLOW, 159209962Smm HELP_UNALLOW, 160209962Smm HELP_USERSPACE, 161219089Spjd HELP_GROUPSPACE, 162219089Spjd HELP_HOLD, 163219089Spjd HELP_HOLDS, 164219089Spjd HELP_RELEASE, 165228103Smm HELP_DIFF, 166332525Smav HELP_REMAP, 167260183Sdelphij HELP_BOOKMARK, 168325534Savg HELP_CHANNEL_PROGRAM, 169168404Spjd} zfs_help_t; 170168404Spjd 171168404Spjdtypedef struct zfs_command { 172168404Spjd const char *name; 173168404Spjd int (*func)(int argc, char **argv); 174168404Spjd zfs_help_t usage; 175168404Spjd} zfs_command_t; 176168404Spjd 177168404Spjd/* 178168404Spjd * Master command table. Each ZFS command has a name, associated function, and 179168404Spjd * usage message. The usage messages need to be internationalized, so we have 180168404Spjd * to have a function to return the usage message based on a command index. 181168404Spjd * 182168404Spjd * These commands are organized according to how they are displayed in the usage 183168404Spjd * message. An empty command (one with a NULL name) indicates an empty line in 184168404Spjd * the generic usage message. 185168404Spjd */ 186168404Spjdstatic zfs_command_t command_table[] = { 187168404Spjd { "create", zfs_do_create, HELP_CREATE }, 188168404Spjd { "destroy", zfs_do_destroy, HELP_DESTROY }, 189168404Spjd { NULL }, 190168404Spjd { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT }, 191168404Spjd { "rollback", zfs_do_rollback, HELP_ROLLBACK }, 192168404Spjd { "clone", zfs_do_clone, HELP_CLONE }, 193168404Spjd { "promote", zfs_do_promote, HELP_PROMOTE }, 194168404Spjd { "rename", zfs_do_rename, HELP_RENAME }, 195260183Sdelphij { "bookmark", zfs_do_bookmark, HELP_BOOKMARK }, 196325534Savg { "program", zfs_do_channel_program, HELP_CHANNEL_PROGRAM }, 197168404Spjd { NULL }, 198168404Spjd { "list", zfs_do_list, HELP_LIST }, 199168404Spjd { NULL }, 200168404Spjd { "set", zfs_do_set, HELP_SET }, 201219089Spjd { "get", zfs_do_get, HELP_GET }, 202168404Spjd { "inherit", zfs_do_inherit, HELP_INHERIT }, 203185029Spjd { "upgrade", zfs_do_upgrade, HELP_UPGRADE }, 204209962Smm { "userspace", zfs_do_userspace, HELP_USERSPACE }, 205209962Smm { "groupspace", zfs_do_userspace, HELP_GROUPSPACE }, 206168404Spjd { NULL }, 207168404Spjd { "mount", zfs_do_mount, HELP_MOUNT }, 208168404Spjd { "unmount", zfs_do_unmount, HELP_UNMOUNT }, 209168404Spjd { "share", zfs_do_share, HELP_SHARE }, 210168404Spjd { "unshare", zfs_do_unshare, HELP_UNSHARE }, 211168404Spjd { NULL }, 212168404Spjd { "send", zfs_do_send, HELP_SEND }, 213168404Spjd { "receive", zfs_do_receive, HELP_RECEIVE }, 214168404Spjd { NULL }, 215219089Spjd { "allow", zfs_do_allow, HELP_ALLOW }, 216185029Spjd { NULL }, 217219089Spjd { "unallow", zfs_do_unallow, HELP_UNALLOW }, 218185029Spjd { NULL }, 219219089Spjd { "hold", zfs_do_hold, HELP_HOLD }, 220219089Spjd { "holds", zfs_do_holds, HELP_HOLDS }, 221219089Spjd { "release", zfs_do_release, HELP_RELEASE }, 222219089Spjd { "diff", zfs_do_diff, HELP_DIFF }, 223219089Spjd { NULL }, 224168404Spjd { "jail", zfs_do_jail, HELP_JAIL }, 225168404Spjd { "unjail", zfs_do_unjail, HELP_UNJAIL }, 226332525Smav { "remap", zfs_do_remap, HELP_REMAP }, 227168404Spjd}; 228168404Spjd 229168404Spjd#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) 230168404Spjd 231168404Spjdzfs_command_t *current_command; 232168404Spjd 233168404Spjdstatic const char * 234168404Spjdget_usage(zfs_help_t idx) 235168404Spjd{ 236168404Spjd switch (idx) { 237168404Spjd case HELP_CLONE: 238185029Spjd return (gettext("\tclone [-p] [-o property=value] ... " 239185029Spjd "<snapshot> <filesystem|volume>\n")); 240168404Spjd case HELP_CREATE: 241234654Spjd return (gettext("\tcreate [-pu] [-o property=value] ... " 242168404Spjd "<filesystem>\n" 243185029Spjd "\tcreate [-ps] [-b blocksize] [-o property=value] ... " 244185029Spjd "-V <size> <volume>\n")); 245168404Spjd case HELP_DESTROY: 246228103Smm return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n" 247228103Smm "\tdestroy [-dnpRrv] " 248260183Sdelphij "<filesystem|volume>@<snap>[%<snap>][,...]\n" 249260183Sdelphij "\tdestroy <filesystem|volume>#<bookmark>\n")); 250168404Spjd case HELP_GET: 251205199Sdelphij return (gettext("\tget [-rHp] [-d max] " 252260183Sdelphij "[-o \"all\" | field[,...]]\n" 253260183Sdelphij "\t [-t type[,...]] [-s source[,...]]\n" 254185029Spjd "\t <\"all\" | property[,...]> " 255321534Smav "[filesystem|volume|snapshot|bookmark] ...\n")); 256168404Spjd case HELP_INHERIT: 257219089Spjd return (gettext("\tinherit [-rS] <property> " 258185029Spjd "<filesystem|volume|snapshot> ...\n")); 259185029Spjd case HELP_UPGRADE: 260185029Spjd return (gettext("\tupgrade [-v]\n" 261185029Spjd "\tupgrade [-r] [-V version] <-a | filesystem ...>\n")); 262168404Spjd case HELP_JAIL: 263240698Sbapt return (gettext("\tjail <jailid|jailname> <filesystem>\n")); 264168404Spjd case HELP_UNJAIL: 265240698Sbapt return (gettext("\tunjail <jailid|jailname> <filesystem>\n")); 266168404Spjd case HELP_LIST: 267259850Sdelphij return (gettext("\tlist [-Hp] [-r|-d max] [-o property[,...]] " 268259850Sdelphij "[-s property]...\n\t [-S property]... [-t type[,...]] " 269185029Spjd "[filesystem|volume|snapshot] ...\n")); 270168404Spjd case HELP_MOUNT: 271168404Spjd return (gettext("\tmount\n" 272185029Spjd "\tmount [-vO] [-o opts] <-a | filesystem>\n")); 273168404Spjd case HELP_PROMOTE: 274185029Spjd return (gettext("\tpromote <clone-filesystem>\n")); 275168404Spjd case HELP_RECEIVE: 276289362Smav return (gettext("\treceive|recv [-vnsFu] <filesystem|volume|" 277286705Smav "snapshot>\n" 278289362Smav "\treceive|recv [-vnsFu] [-o origin=<snapshot>] [-d | -e] " 279289362Smav "<filesystem>\n" 280289362Smav "\treceive|recv -A <filesystem|volume>\n")); 281168404Spjd case HELP_RENAME: 282235216Smm return (gettext("\trename [-f] <filesystem|volume|snapshot> " 283168676Spjd "<filesystem|volume|snapshot>\n" 284260183Sdelphij "\trename [-f] -p <filesystem|volume> <filesystem|volume>\n" 285226705Spjd "\trename -r <snapshot> <snapshot>\n" 286353759Savg "\trename <bookmark> <bookmark>\n" 287226705Spjd "\trename -u [-p] <filesystem> <filesystem>")); 288168404Spjd case HELP_ROLLBACK: 289168404Spjd return (gettext("\trollback [-rRf] <snapshot>\n")); 290168404Spjd case HELP_SEND: 291321535Smav return (gettext("\tsend [-DnPpRvLec] [-[iI] snapshot] " 292260183Sdelphij "<snapshot>\n" 293352598Savg "\tsend [-LPcenv] [-i snapshot|bookmark] " 294289362Smav "<filesystem|volume|snapshot>\n" 295289362Smav "\tsend [-nvPe] -t <receive_resume_token>\n")); 296168404Spjd case HELP_SET: 297289497Smav return (gettext("\tset <property=value> ... " 298185029Spjd "<filesystem|volume|snapshot> ...\n")); 299168404Spjd case HELP_SHARE: 300185029Spjd return (gettext("\tshare <-a | filesystem>\n")); 301168404Spjd case HELP_SNAPSHOT: 302256999Ssmh return (gettext("\tsnapshot|snap [-r] [-o property=value] ... " 303260183Sdelphij "<filesystem|volume>@<snap> ...\n")); 304168404Spjd case HELP_UNMOUNT: 305256999Ssmh return (gettext("\tunmount|umount [-f] " 306185029Spjd "<-a | filesystem|mountpoint>\n")); 307168404Spjd case HELP_UNSHARE: 308219089Spjd return (gettext("\tunshare " 309185029Spjd "<-a | filesystem|mountpoint>\n")); 310185029Spjd case HELP_ALLOW: 311219089Spjd return (gettext("\tallow <filesystem|volume>\n" 312219089Spjd "\tallow [-ldug] " 313185029Spjd "<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n" 314185029Spjd "\t <filesystem|volume>\n" 315185029Spjd "\tallow [-ld] -e <perm|@setname>[,...] " 316185029Spjd "<filesystem|volume>\n" 317185029Spjd "\tallow -c <perm|@setname>[,...] <filesystem|volume>\n" 318185029Spjd "\tallow -s @setname <perm|@setname>[,...] " 319185029Spjd "<filesystem|volume>\n")); 320185029Spjd case HELP_UNALLOW: 321185029Spjd return (gettext("\tunallow [-rldug] " 322185029Spjd "<\"everyone\"|user|group>[,...]\n" 323185029Spjd "\t [<perm|@setname>[,...]] <filesystem|volume>\n" 324185029Spjd "\tunallow [-rld] -e [<perm|@setname>[,...]] " 325185029Spjd "<filesystem|volume>\n" 326185029Spjd "\tunallow [-r] -c [<perm|@setname>[,...]] " 327185029Spjd "<filesystem|volume>\n" 328185029Spjd "\tunallow [-r] -s @setname [<perm|@setname>[,...]] " 329185029Spjd "<filesystem|volume>\n")); 330209962Smm case HELP_USERSPACE: 331240415Smm return (gettext("\tuserspace [-Hinp] [-o field[,...]] " 332260183Sdelphij "[-s field] ...\n" 333260183Sdelphij "\t [-S field] ... [-t type[,...]] " 334259850Sdelphij "<filesystem|snapshot>\n")); 335209962Smm case HELP_GROUPSPACE: 336240415Smm return (gettext("\tgroupspace [-Hinp] [-o field[,...]] " 337260183Sdelphij "[-s field] ...\n" 338260183Sdelphij "\t [-S field] ... [-t type[,...]] " 339259850Sdelphij "<filesystem|snapshot>\n")); 340219089Spjd case HELP_HOLD: 341219089Spjd return (gettext("\thold [-r] <tag> <snapshot> ...\n")); 342219089Spjd case HELP_HOLDS: 343290015Sallanjude return (gettext("\tholds [-Hp] [-r|-d depth] " 344290015Sallanjude "<filesystem|volume|snapshot> ...\n")); 345219089Spjd case HELP_RELEASE: 346219089Spjd return (gettext("\trelease [-r] <tag> <snapshot> ...\n")); 347219089Spjd case HELP_DIFF: 348219089Spjd return (gettext("\tdiff [-FHt] <snapshot> " 349219089Spjd "[snapshot|filesystem]\n")); 350332525Smav case HELP_REMAP: 351332525Smav return (gettext("\tremap <filesystem | volume>\n")); 352260183Sdelphij case HELP_BOOKMARK: 353260183Sdelphij return (gettext("\tbookmark <snapshot> <bookmark>\n")); 354325534Savg case HELP_CHANNEL_PROGRAM: 355329484Smav return (gettext("\tprogram [-n] [-t <instruction limit>] " 356325534Savg "[-m <memory limit (b)>] <pool> <program file> " 357325534Savg "[lua args...]\n")); 358168404Spjd } 359168404Spjd 360168404Spjd abort(); 361168404Spjd /* NOTREACHED */ 362168404Spjd} 363168404Spjd 364219089Spjdvoid 365219089Spjdnomem(void) 366219089Spjd{ 367219089Spjd (void) fprintf(stderr, gettext("internal error: out of memory\n")); 368219089Spjd exit(1); 369219089Spjd} 370219089Spjd 371168404Spjd/* 372168404Spjd * Utility function to guarantee malloc() success. 373168404Spjd */ 374219089Spjd 375168404Spjdvoid * 376168404Spjdsafe_malloc(size_t size) 377168404Spjd{ 378168404Spjd void *data; 379168404Spjd 380219089Spjd if ((data = calloc(1, size)) == NULL) 381219089Spjd nomem(); 382168404Spjd 383168404Spjd return (data); 384168404Spjd} 385168404Spjd 386325534Savgvoid * 387325534Savgsafe_realloc(void *data, size_t size) 388325534Savg{ 389325534Savg void *newp; 390325534Savg if ((newp = realloc(data, size)) == NULL) { 391325534Savg free(data); 392325534Savg nomem(); 393325534Savg } 394325534Savg 395325534Savg return (newp); 396325534Savg} 397325534Savg 398219089Spjdstatic char * 399219089Spjdsafe_strdup(char *str) 400219089Spjd{ 401219089Spjd char *dupstr = strdup(str); 402219089Spjd 403219089Spjd if (dupstr == NULL) 404219089Spjd nomem(); 405219089Spjd 406219089Spjd return (dupstr); 407219089Spjd} 408219089Spjd 409168404Spjd/* 410185029Spjd * Callback routine that will print out information for each of 411168404Spjd * the properties. 412168404Spjd */ 413185029Spjdstatic int 414185029Spjdusage_prop_cb(int prop, void *cb) 415168404Spjd{ 416168404Spjd FILE *fp = cb; 417168404Spjd 418185029Spjd (void) fprintf(fp, "\t%-15s ", zfs_prop_to_name(prop)); 419168404Spjd 420168404Spjd if (zfs_prop_readonly(prop)) 421185029Spjd (void) fprintf(fp, " NO "); 422168404Spjd else 423185029Spjd (void) fprintf(fp, "YES "); 424168404Spjd 425168404Spjd if (zfs_prop_inheritable(prop)) 426168404Spjd (void) fprintf(fp, " YES "); 427168404Spjd else 428168404Spjd (void) fprintf(fp, " NO "); 429168404Spjd 430168404Spjd if (zfs_prop_values(prop) == NULL) 431168404Spjd (void) fprintf(fp, "-\n"); 432168404Spjd else 433168404Spjd (void) fprintf(fp, "%s\n", zfs_prop_values(prop)); 434168404Spjd 435185029Spjd return (ZPROP_CONT); 436168404Spjd} 437168404Spjd 438168404Spjd/* 439168404Spjd * Display usage message. If we're inside a command, display only the usage for 440168404Spjd * that command. Otherwise, iterate over the entire command table and display 441168404Spjd * a complete usage message. 442168404Spjd */ 443168404Spjdstatic void 444168404Spjdusage(boolean_t requested) 445168404Spjd{ 446168404Spjd int i; 447168404Spjd boolean_t show_properties = B_FALSE; 448168404Spjd FILE *fp = requested ? stdout : stderr; 449168404Spjd 450168404Spjd if (current_command == NULL) { 451168404Spjd 452168404Spjd (void) fprintf(fp, gettext("usage: zfs command args ...\n")); 453168404Spjd (void) fprintf(fp, 454168404Spjd gettext("where 'command' is one of the following:\n\n")); 455168404Spjd 456168404Spjd for (i = 0; i < NCOMMAND; i++) { 457168404Spjd if (command_table[i].name == NULL) 458168404Spjd (void) fprintf(fp, "\n"); 459168404Spjd else 460168404Spjd (void) fprintf(fp, "%s", 461168404Spjd get_usage(command_table[i].usage)); 462168404Spjd } 463168404Spjd 464168404Spjd (void) fprintf(fp, gettext("\nEach dataset is of the form: " 465168404Spjd "pool/[dataset/]*dataset[@name]\n")); 466168404Spjd } else { 467168404Spjd (void) fprintf(fp, gettext("usage:\n")); 468168404Spjd (void) fprintf(fp, "%s", get_usage(current_command->usage)); 469168404Spjd } 470168404Spjd 471168404Spjd if (current_command != NULL && 472168404Spjd (strcmp(current_command->name, "set") == 0 || 473168404Spjd strcmp(current_command->name, "get") == 0 || 474168404Spjd strcmp(current_command->name, "inherit") == 0 || 475168404Spjd strcmp(current_command->name, "list") == 0)) 476168404Spjd show_properties = B_TRUE; 477168404Spjd 478168404Spjd if (show_properties) { 479168404Spjd (void) fprintf(fp, 480168404Spjd gettext("\nThe following properties are supported:\n")); 481168404Spjd 482185029Spjd (void) fprintf(fp, "\n\t%-14s %s %s %s\n\n", 483168404Spjd "PROPERTY", "EDIT", "INHERIT", "VALUES"); 484168404Spjd 485168404Spjd /* Iterate over all properties */ 486185029Spjd (void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE, 487185029Spjd ZFS_TYPE_DATASET); 488168404Spjd 489209962Smm (void) fprintf(fp, "\t%-15s ", "userused@..."); 490209962Smm (void) fprintf(fp, " NO NO <size>\n"); 491209962Smm (void) fprintf(fp, "\t%-15s ", "groupused@..."); 492209962Smm (void) fprintf(fp, " NO NO <size>\n"); 493209962Smm (void) fprintf(fp, "\t%-15s ", "userquota@..."); 494209962Smm (void) fprintf(fp, "YES NO <size> | none\n"); 495209962Smm (void) fprintf(fp, "\t%-15s ", "groupquota@..."); 496209962Smm (void) fprintf(fp, "YES NO <size> | none\n"); 497228103Smm (void) fprintf(fp, "\t%-15s ", "written@<snap>"); 498228103Smm (void) fprintf(fp, " NO NO <size>\n"); 499209962Smm 500168404Spjd (void) fprintf(fp, gettext("\nSizes are specified in bytes " 501168404Spjd "with standard units such as K, M, G, etc.\n")); 502185029Spjd (void) fprintf(fp, gettext("\nUser-defined properties can " 503168404Spjd "be specified by using a name containing a colon (:).\n")); 504209962Smm (void) fprintf(fp, gettext("\nThe {user|group}{used|quota}@ " 505209962Smm "properties must be appended with\n" 506209962Smm "a user or group specifier of one of these forms:\n" 507209962Smm " POSIX name (eg: \"matt\")\n" 508209962Smm " POSIX id (eg: \"126829\")\n" 509209962Smm " SMB name@domain (eg: \"matt@sun\")\n" 510209962Smm " SMB SID (eg: \"S-1-234-567-89\")\n")); 511168404Spjd } else { 512168404Spjd (void) fprintf(fp, 513209962Smm gettext("\nFor the property list, run: %s\n"), 514209962Smm "zfs set|get"); 515185029Spjd (void) fprintf(fp, 516209962Smm gettext("\nFor the delegated permission list, run: %s\n"), 517209962Smm "zfs allow|unallow"); 518168404Spjd } 519168404Spjd 520168404Spjd /* 521168404Spjd * See comments at end of main(). 522168404Spjd */ 523168404Spjd if (getenv("ZFS_ABORT") != NULL) { 524168404Spjd (void) printf("dumping core by request\n"); 525168404Spjd abort(); 526168404Spjd } 527168404Spjd 528168404Spjd exit(requested ? 0 : 2); 529168404Spjd} 530168404Spjd 531289497Smav/* 532289497Smav * Take a property=value argument string and add it to the given nvlist. 533289497Smav * Modifies the argument inplace. 534289497Smav */ 535185029Spjdstatic int 536264851Ssmhparseprop(nvlist_t *props, char *propname) 537185029Spjd{ 538185029Spjd char *propval, *strval; 539185029Spjd 540185029Spjd if ((propval = strchr(propname, '=')) == NULL) { 541185029Spjd (void) fprintf(stderr, gettext("missing " 542289497Smav "'=' for property=value argument\n")); 543185029Spjd return (-1); 544185029Spjd } 545185029Spjd *propval = '\0'; 546185029Spjd propval++; 547185029Spjd if (nvlist_lookup_string(props, propname, &strval) == 0) { 548185029Spjd (void) fprintf(stderr, gettext("property '%s' " 549185029Spjd "specified multiple times\n"), propname); 550185029Spjd return (-1); 551185029Spjd } 552219089Spjd if (nvlist_add_string(props, propname, propval) != 0) 553219089Spjd nomem(); 554185029Spjd return (0); 555185029Spjd} 556185029Spjd 557205199Sdelphijstatic int 558205199Sdelphijparse_depth(char *opt, int *flags) 559205199Sdelphij{ 560205199Sdelphij char *tmp; 561205199Sdelphij int depth; 562205199Sdelphij 563205199Sdelphij depth = (int)strtol(opt, &tmp, 0); 564205199Sdelphij if (*tmp) { 565205199Sdelphij (void) fprintf(stderr, 566264851Ssmh gettext("%s is not an integer\n"), opt); 567205199Sdelphij usage(B_FALSE); 568205199Sdelphij } 569205199Sdelphij if (depth < 0) { 570205199Sdelphij (void) fprintf(stderr, 571205199Sdelphij gettext("Depth can not be negative.\n")); 572205199Sdelphij usage(B_FALSE); 573205199Sdelphij } 574205199Sdelphij *flags |= (ZFS_ITER_DEPTH_LIMIT|ZFS_ITER_RECURSE); 575205199Sdelphij return (depth); 576205199Sdelphij} 577205199Sdelphij 578219089Spjd#define PROGRESS_DELAY 2 /* seconds */ 579219089Spjd 580219089Spjdstatic 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"; 581219089Spjdstatic time_t pt_begin; 582219089Spjdstatic char *pt_header = NULL; 583219089Spjdstatic boolean_t pt_shown; 584219089Spjd 585219089Spjdstatic void 586219089Spjdstart_progress_timer(void) 587219089Spjd{ 588219089Spjd pt_begin = time(NULL) + PROGRESS_DELAY; 589219089Spjd pt_shown = B_FALSE; 590219089Spjd} 591219089Spjd 592219089Spjdstatic void 593219089Spjdset_progress_header(char *header) 594219089Spjd{ 595219089Spjd assert(pt_header == NULL); 596219089Spjd pt_header = safe_strdup(header); 597219089Spjd if (pt_shown) { 598219089Spjd (void) printf("%s: ", header); 599219089Spjd (void) fflush(stdout); 600219089Spjd } 601219089Spjd} 602219089Spjd 603219089Spjdstatic void 604219089Spjdupdate_progress(char *update) 605219089Spjd{ 606219089Spjd if (!pt_shown && time(NULL) > pt_begin) { 607219089Spjd int len = strlen(update); 608219089Spjd 609219089Spjd (void) printf("%s: %s%*.*s", pt_header, update, len, len, 610219089Spjd pt_reverse); 611219089Spjd (void) fflush(stdout); 612219089Spjd pt_shown = B_TRUE; 613219089Spjd } else if (pt_shown) { 614219089Spjd int len = strlen(update); 615219089Spjd 616219089Spjd (void) printf("%s%*.*s", update, len, len, pt_reverse); 617219089Spjd (void) fflush(stdout); 618219089Spjd } 619219089Spjd} 620219089Spjd 621219089Spjdstatic void 622219089Spjdfinish_progress(char *done) 623219089Spjd{ 624219089Spjd if (pt_shown) { 625219089Spjd (void) printf("%s\n", done); 626219089Spjd (void) fflush(stdout); 627219089Spjd } 628219089Spjd free(pt_header); 629219089Spjd pt_header = NULL; 630219089Spjd} 631268075Sdelphij 632168404Spjd/* 633284309Savg * Check if the dataset is mountable and should be automatically mounted. 634284309Savg */ 635284309Savgstatic boolean_t 636284309Savgshould_auto_mount(zfs_handle_t *zhp) 637284309Savg{ 638284309Savg if (!zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, zfs_get_type(zhp))) 639284309Savg return (B_FALSE); 640284309Savg return (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON); 641284309Savg} 642284309Savg 643284309Savg/* 644185029Spjd * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol> 645168404Spjd * 646168404Spjd * Given an existing dataset, create a writable copy whose initial contents 647168404Spjd * are the same as the source. The newly created dataset maintains a 648168404Spjd * dependency on the original; the original cannot be destroyed so long as 649168404Spjd * the clone exists. 650185029Spjd * 651185029Spjd * The '-p' flag creates all the non-existing ancestors of the target first. 652168404Spjd */ 653168404Spjdstatic int 654168404Spjdzfs_do_clone(int argc, char **argv) 655168404Spjd{ 656185029Spjd zfs_handle_t *zhp = NULL; 657185029Spjd boolean_t parents = B_FALSE; 658185029Spjd nvlist_t *props; 659231144Smm int ret = 0; 660185029Spjd int c; 661168404Spjd 662219089Spjd if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 663219089Spjd nomem(); 664185029Spjd 665168404Spjd /* check options */ 666185029Spjd while ((c = getopt(argc, argv, "o:p")) != -1) { 667185029Spjd switch (c) { 668185029Spjd case 'o': 669289497Smav if (parseprop(props, optarg) != 0) 670185029Spjd return (1); 671185029Spjd break; 672185029Spjd case 'p': 673185029Spjd parents = B_TRUE; 674185029Spjd break; 675185029Spjd case '?': 676185029Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 677185029Spjd optopt); 678185029Spjd goto usage; 679185029Spjd } 680168404Spjd } 681168404Spjd 682185029Spjd argc -= optind; 683185029Spjd argv += optind; 684185029Spjd 685168404Spjd /* check number of arguments */ 686185029Spjd if (argc < 1) { 687168404Spjd (void) fprintf(stderr, gettext("missing source dataset " 688168404Spjd "argument\n")); 689185029Spjd goto usage; 690168404Spjd } 691185029Spjd if (argc < 2) { 692168404Spjd (void) fprintf(stderr, gettext("missing target dataset " 693168404Spjd "argument\n")); 694185029Spjd goto usage; 695168404Spjd } 696185029Spjd if (argc > 2) { 697168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 698185029Spjd goto usage; 699168404Spjd } 700168404Spjd 701168404Spjd /* open the source dataset */ 702185029Spjd if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 703168404Spjd return (1); 704168404Spjd 705185029Spjd if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM | 706185029Spjd ZFS_TYPE_VOLUME)) { 707185029Spjd /* 708185029Spjd * Now create the ancestors of the target dataset. If the 709185029Spjd * target already exists and '-p' option was used we should not 710185029Spjd * complain. 711185029Spjd */ 712185029Spjd if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | 713185029Spjd ZFS_TYPE_VOLUME)) 714185029Spjd return (0); 715185029Spjd if (zfs_create_ancestors(g_zfs, argv[1]) != 0) 716185029Spjd return (1); 717185029Spjd } 718185029Spjd 719168404Spjd /* pass to libzfs */ 720185029Spjd ret = zfs_clone(zhp, argv[1], props); 721168404Spjd 722168404Spjd /* create the mountpoint if necessary */ 723168404Spjd if (ret == 0) { 724185029Spjd zfs_handle_t *clone; 725185029Spjd 726185029Spjd clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_DATASET); 727168404Spjd if (clone != NULL) { 728284309Savg /* 729284309Savg * If the user doesn't want the dataset 730284309Savg * automatically mounted, then skip the mount/share 731284309Savg * step. 732284309Savg */ 733284309Savg if (should_auto_mount(clone)) { 734284309Savg if ((ret = zfs_mount(clone, NULL, 0)) != 0) { 735284309Savg (void) fprintf(stderr, gettext("clone " 736284309Savg "successfully created, " 737284309Savg "but not mounted\n")); 738284309Savg } else if ((ret = zfs_share(clone)) != 0) { 739284309Savg (void) fprintf(stderr, gettext("clone " 740284309Savg "successfully created, " 741284309Savg "but not shared\n")); 742284309Savg } 743284309Savg } 744168404Spjd zfs_close(clone); 745168404Spjd } 746168404Spjd } 747168404Spjd 748168404Spjd zfs_close(zhp); 749185029Spjd nvlist_free(props); 750168404Spjd 751185029Spjd return (!!ret); 752185029Spjd 753185029Spjdusage: 754185029Spjd if (zhp) 755185029Spjd zfs_close(zhp); 756185029Spjd nvlist_free(props); 757185029Spjd usage(B_FALSE); 758185029Spjd return (-1); 759168404Spjd} 760168404Spjd 761168404Spjd/* 762234654Spjd * zfs create [-pu] [-o prop=value] ... fs 763185029Spjd * zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size 764168404Spjd * 765168404Spjd * Create a new dataset. This command can be used to create filesystems 766168404Spjd * and volumes. Snapshot creation is handled by 'zfs snapshot'. 767168404Spjd * For volumes, the user must specify a size to be used. 768168404Spjd * 769168404Spjd * The '-s' flag applies only to volumes, and indicates that we should not try 770168404Spjd * to set the reservation for this volume. By default we set a reservation 771185029Spjd * equal to the size for any volume. For pools with SPA_VERSION >= 772185029Spjd * SPA_VERSION_REFRESERVATION, we set a refreservation instead. 773185029Spjd * 774185029Spjd * The '-p' flag creates all the non-existing ancestors of the target first. 775234654Spjd * 776234654Spjd * The '-u' flag prevents mounting of newly created file system. 777168404Spjd */ 778168404Spjdstatic int 779168404Spjdzfs_do_create(int argc, char **argv) 780168404Spjd{ 781168404Spjd zfs_type_t type = ZFS_TYPE_FILESYSTEM; 782168404Spjd zfs_handle_t *zhp = NULL; 783296535Smav uint64_t volsize = 0; 784168404Spjd int c; 785168404Spjd boolean_t noreserve = B_FALSE; 786185029Spjd boolean_t bflag = B_FALSE; 787185029Spjd boolean_t parents = B_FALSE; 788234654Spjd boolean_t nomount = B_FALSE; 789168404Spjd int ret = 1; 790185029Spjd nvlist_t *props; 791168404Spjd uint64_t intval; 792168404Spjd 793219089Spjd if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 794219089Spjd nomem(); 795168404Spjd 796168404Spjd /* check options */ 797234654Spjd while ((c = getopt(argc, argv, ":V:b:so:pu")) != -1) { 798168404Spjd switch (c) { 799168404Spjd case 'V': 800168404Spjd type = ZFS_TYPE_VOLUME; 801168404Spjd if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 802168404Spjd (void) fprintf(stderr, gettext("bad volume " 803168404Spjd "size '%s': %s\n"), optarg, 804168404Spjd libzfs_error_description(g_zfs)); 805168404Spjd goto error; 806168404Spjd } 807168404Spjd 808168404Spjd if (nvlist_add_uint64(props, 809219089Spjd zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0) 810219089Spjd nomem(); 811168404Spjd volsize = intval; 812168404Spjd break; 813185029Spjd case 'p': 814185029Spjd parents = B_TRUE; 815185029Spjd break; 816168404Spjd case 'b': 817185029Spjd bflag = B_TRUE; 818168404Spjd if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { 819168404Spjd (void) fprintf(stderr, gettext("bad volume " 820168404Spjd "block size '%s': %s\n"), optarg, 821168404Spjd libzfs_error_description(g_zfs)); 822168404Spjd goto error; 823168404Spjd } 824168404Spjd 825168404Spjd if (nvlist_add_uint64(props, 826168404Spjd zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), 827219089Spjd intval) != 0) 828219089Spjd nomem(); 829168404Spjd break; 830168404Spjd case 'o': 831286705Smav if (parseprop(props, optarg) != 0) 832168404Spjd goto error; 833168404Spjd break; 834168404Spjd case 's': 835168404Spjd noreserve = B_TRUE; 836168404Spjd break; 837234654Spjd case 'u': 838234654Spjd nomount = B_TRUE; 839234654Spjd break; 840168404Spjd case ':': 841168404Spjd (void) fprintf(stderr, gettext("missing size " 842168404Spjd "argument\n")); 843168404Spjd goto badusage; 844168404Spjd case '?': 845168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 846168404Spjd optopt); 847168404Spjd goto badusage; 848168404Spjd } 849168404Spjd } 850168404Spjd 851185029Spjd if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) { 852185029Spjd (void) fprintf(stderr, gettext("'-s' and '-b' can only be " 853185029Spjd "used when creating a volume\n")); 854168404Spjd goto badusage; 855168404Spjd } 856234654Spjd if (nomount && type != ZFS_TYPE_FILESYSTEM) { 857234654Spjd (void) fprintf(stderr, gettext("'-u' can only be " 858234654Spjd "used when creating a file system\n")); 859234654Spjd goto badusage; 860234654Spjd } 861168404Spjd 862168404Spjd argc -= optind; 863168404Spjd argv += optind; 864168404Spjd 865168404Spjd /* check number of arguments */ 866168404Spjd if (argc == 0) { 867168404Spjd (void) fprintf(stderr, gettext("missing %s argument\n"), 868168404Spjd zfs_type_to_name(type)); 869168404Spjd goto badusage; 870168404Spjd } 871168404Spjd if (argc > 1) { 872168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 873168404Spjd goto badusage; 874168404Spjd } 875168404Spjd 876185029Spjd if (type == ZFS_TYPE_VOLUME && !noreserve) { 877185029Spjd zpool_handle_t *zpool_handle; 878296535Smav nvlist_t *real_props = NULL; 879185029Spjd uint64_t spa_version; 880185029Spjd char *p; 881185029Spjd zfs_prop_t resv_prop; 882185029Spjd char *strval; 883289499Smav char msg[1024]; 884185029Spjd 885296535Smav if ((p = strchr(argv[0], '/')) != NULL) 886185029Spjd *p = '\0'; 887185029Spjd zpool_handle = zpool_open(g_zfs, argv[0]); 888185029Spjd if (p != NULL) 889185029Spjd *p = '/'; 890185029Spjd if (zpool_handle == NULL) 891185029Spjd goto error; 892185029Spjd spa_version = zpool_get_prop_int(zpool_handle, 893185029Spjd ZPOOL_PROP_VERSION, NULL); 894185029Spjd if (spa_version >= SPA_VERSION_REFRESERVATION) 895185029Spjd resv_prop = ZFS_PROP_REFRESERVATION; 896185029Spjd else 897185029Spjd resv_prop = ZFS_PROP_RESERVATION; 898185029Spjd 899289499Smav (void) snprintf(msg, sizeof (msg), 900289499Smav gettext("cannot create '%s'"), argv[0]); 901289499Smav if (props && (real_props = zfs_valid_proplist(g_zfs, type, 902289500Smav props, 0, NULL, zpool_handle, msg)) == NULL) { 903289500Smav zpool_close(zpool_handle); 904289499Smav goto error; 905289500Smav } 906289500Smav zpool_close(zpool_handle); 907289499Smav 908289499Smav volsize = zvol_volsize_to_reservation(volsize, real_props); 909289499Smav nvlist_free(real_props); 910289499Smav 911185029Spjd if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop), 912185029Spjd &strval) != 0) { 913185029Spjd if (nvlist_add_uint64(props, 914185029Spjd zfs_prop_to_name(resv_prop), volsize) != 0) { 915185029Spjd nvlist_free(props); 916219089Spjd nomem(); 917185029Spjd } 918168404Spjd } 919168404Spjd } 920168404Spjd 921185029Spjd if (parents && zfs_name_valid(argv[0], type)) { 922185029Spjd /* 923185029Spjd * Now create the ancestors of target dataset. If the target 924185029Spjd * already exists and '-p' option was used we should not 925185029Spjd * complain. 926185029Spjd */ 927185029Spjd if (zfs_dataset_exists(g_zfs, argv[0], type)) { 928185029Spjd ret = 0; 929185029Spjd goto error; 930185029Spjd } 931185029Spjd if (zfs_create_ancestors(g_zfs, argv[0]) != 0) 932185029Spjd goto error; 933185029Spjd } 934185029Spjd 935168404Spjd /* pass to libzfs */ 936168404Spjd if (zfs_create(g_zfs, argv[0], type, props) != 0) 937168404Spjd goto error; 938168404Spjd 939185029Spjd if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL) 940168404Spjd goto error; 941219089Spjd 942219089Spjd ret = 0; 943168404Spjd 944168404Spjd /* 945168404Spjd * Mount and/or share the new filesystem as appropriate. We provide a 946168404Spjd * verbose error message to let the user know that their filesystem was 947168404Spjd * in fact created, even if we failed to mount or share it. 948284309Savg * If the user doesn't want the dataset automatically mounted, 949284309Savg * then skip the mount/share step altogether. 950168404Spjd */ 951284309Savg if (!nomount && should_auto_mount(zhp)) { 952185029Spjd if (zfs_mount(zhp, NULL, 0) != 0) { 953185029Spjd (void) fprintf(stderr, gettext("filesystem " 954185029Spjd "successfully created, but not mounted\n")); 955185029Spjd ret = 1; 956185029Spjd } else if (zfs_share(zhp) != 0) { 957185029Spjd (void) fprintf(stderr, gettext("filesystem " 958185029Spjd "successfully created, but not shared\n")); 959185029Spjd ret = 1; 960185029Spjd } 961168404Spjd } 962168404Spjd 963168404Spjderror: 964168404Spjd if (zhp) 965168404Spjd zfs_close(zhp); 966168404Spjd nvlist_free(props); 967168404Spjd return (ret); 968168404Spjdbadusage: 969168404Spjd nvlist_free(props); 970168404Spjd usage(B_FALSE); 971168404Spjd return (2); 972168404Spjd} 973168404Spjd 974168404Spjd/* 975219089Spjd * zfs destroy [-rRf] <fs, vol> 976219089Spjd * zfs destroy [-rRd] <snap> 977168404Spjd * 978219089Spjd * -r Recursively destroy all children 979219089Spjd * -R Recursively destroy all dependents, including clones 980219089Spjd * -f Force unmounting of any dependents 981219089Spjd * -d If we can't destroy now, mark for deferred destruction 982168404Spjd * 983168404Spjd * Destroys the given dataset. By default, it will unmount any filesystems, 984168404Spjd * and refuse to destroy a dataset that has any dependents. A dependent can 985168404Spjd * either be a child, or a clone of a child. 986168404Spjd */ 987168404Spjdtypedef struct destroy_cbdata { 988168404Spjd boolean_t cb_first; 989228103Smm boolean_t cb_force; 990228103Smm boolean_t cb_recurse; 991228103Smm boolean_t cb_error; 992228103Smm boolean_t cb_doclones; 993168404Spjd zfs_handle_t *cb_target; 994219089Spjd boolean_t cb_defer_destroy; 995228103Smm boolean_t cb_verbose; 996228103Smm boolean_t cb_parsable; 997228103Smm boolean_t cb_dryrun; 998228103Smm nvlist_t *cb_nvl; 999248571Smm nvlist_t *cb_batchedsnaps; 1000228103Smm 1001228103Smm /* first snap in contiguous run */ 1002248571Smm char *cb_firstsnap; 1003228103Smm /* previous snap in contiguous run */ 1004248571Smm char *cb_prevsnap; 1005228103Smm int64_t cb_snapused; 1006228103Smm char *cb_snapspec; 1007260183Sdelphij char *cb_bookmark; 1008168404Spjd} destroy_cbdata_t; 1009168404Spjd 1010168404Spjd/* 1011168404Spjd * Check for any dependents based on the '-r' or '-R' flags. 1012168404Spjd */ 1013168404Spjdstatic int 1014168404Spjddestroy_check_dependent(zfs_handle_t *zhp, void *data) 1015168404Spjd{ 1016168404Spjd destroy_cbdata_t *cbp = data; 1017168404Spjd const char *tname = zfs_get_name(cbp->cb_target); 1018168404Spjd const char *name = zfs_get_name(zhp); 1019168404Spjd 1020168404Spjd if (strncmp(tname, name, strlen(tname)) == 0 && 1021168404Spjd (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { 1022168404Spjd /* 1023168404Spjd * This is a direct descendant, not a clone somewhere else in 1024168404Spjd * the hierarchy. 1025168404Spjd */ 1026168404Spjd if (cbp->cb_recurse) 1027168404Spjd goto out; 1028168404Spjd 1029168404Spjd if (cbp->cb_first) { 1030168404Spjd (void) fprintf(stderr, gettext("cannot destroy '%s': " 1031168404Spjd "%s has children\n"), 1032168404Spjd zfs_get_name(cbp->cb_target), 1033168404Spjd zfs_type_to_name(zfs_get_type(cbp->cb_target))); 1034168404Spjd (void) fprintf(stderr, gettext("use '-r' to destroy " 1035168404Spjd "the following datasets:\n")); 1036168404Spjd cbp->cb_first = B_FALSE; 1037228103Smm cbp->cb_error = B_TRUE; 1038168404Spjd } 1039168404Spjd 1040168404Spjd (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1041168404Spjd } else { 1042168404Spjd /* 1043168404Spjd * This is a clone. We only want to report this if the '-r' 1044168404Spjd * wasn't specified, or the target is a snapshot. 1045168404Spjd */ 1046168404Spjd if (!cbp->cb_recurse && 1047168404Spjd zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) 1048168404Spjd goto out; 1049168404Spjd 1050168404Spjd if (cbp->cb_first) { 1051168404Spjd (void) fprintf(stderr, gettext("cannot destroy '%s': " 1052168404Spjd "%s has dependent clones\n"), 1053168404Spjd zfs_get_name(cbp->cb_target), 1054168404Spjd zfs_type_to_name(zfs_get_type(cbp->cb_target))); 1055168404Spjd (void) fprintf(stderr, gettext("use '-R' to destroy " 1056168404Spjd "the following datasets:\n")); 1057168404Spjd cbp->cb_first = B_FALSE; 1058228103Smm cbp->cb_error = B_TRUE; 1059228103Smm cbp->cb_dryrun = B_TRUE; 1060168404Spjd } 1061168404Spjd 1062168404Spjd (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 1063168404Spjd } 1064168404Spjd 1065168404Spjdout: 1066168404Spjd zfs_close(zhp); 1067168404Spjd return (0); 1068168404Spjd} 1069168404Spjd 1070168404Spjdstatic int 1071168404Spjddestroy_callback(zfs_handle_t *zhp, void *data) 1072168404Spjd{ 1073228103Smm destroy_cbdata_t *cb = data; 1074228103Smm const char *name = zfs_get_name(zhp); 1075168404Spjd 1076228103Smm if (cb->cb_verbose) { 1077228103Smm if (cb->cb_parsable) { 1078228103Smm (void) printf("destroy\t%s\n", name); 1079228103Smm } else if (cb->cb_dryrun) { 1080228103Smm (void) printf(gettext("would destroy %s\n"), 1081228103Smm name); 1082228103Smm } else { 1083228103Smm (void) printf(gettext("will destroy %s\n"), 1084228103Smm name); 1085228103Smm } 1086228103Smm } 1087228103Smm 1088168404Spjd /* 1089168404Spjd * Ignore pools (which we've already flagged as an error before getting 1090219089Spjd * here). 1091168404Spjd */ 1092168404Spjd if (strchr(zfs_get_name(zhp), '/') == NULL && 1093168404Spjd zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 1094168404Spjd zfs_close(zhp); 1095168404Spjd return (0); 1096168404Spjd } 1097248571Smm if (cb->cb_dryrun) { 1098248571Smm zfs_close(zhp); 1099248571Smm return (0); 1100248571Smm } 1101168404Spjd 1102248571Smm /* 1103248571Smm * We batch up all contiguous snapshots (even of different 1104248571Smm * filesystems) and destroy them with one ioctl. We can't 1105248571Smm * simply do all snap deletions and then all fs deletions, 1106248571Smm * because we must delete a clone before its origin. 1107248571Smm */ 1108248571Smm if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { 1109248571Smm fnvlist_add_boolean(cb->cb_batchedsnaps, name); 1110248571Smm } else { 1111248571Smm int error = zfs_destroy_snaps_nvl(g_zfs, 1112248571Smm cb->cb_batchedsnaps, B_FALSE); 1113248571Smm fnvlist_free(cb->cb_batchedsnaps); 1114248571Smm cb->cb_batchedsnaps = fnvlist_alloc(); 1115248571Smm 1116248571Smm if (error != 0 || 1117248571Smm zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 || 1118228103Smm zfs_destroy(zhp, cb->cb_defer_destroy) != 0) { 1119228103Smm zfs_close(zhp); 1120228103Smm return (-1); 1121228103Smm } 1122168404Spjd } 1123168404Spjd 1124168404Spjd zfs_close(zhp); 1125168404Spjd return (0); 1126168404Spjd} 1127168404Spjd 1128168404Spjdstatic int 1129228103Smmdestroy_print_cb(zfs_handle_t *zhp, void *arg) 1130168404Spjd{ 1131228103Smm destroy_cbdata_t *cb = arg; 1132228103Smm const char *name = zfs_get_name(zhp); 1133228103Smm int err = 0; 1134168404Spjd 1135228103Smm if (nvlist_exists(cb->cb_nvl, name)) { 1136228103Smm if (cb->cb_firstsnap == NULL) 1137248571Smm cb->cb_firstsnap = strdup(name); 1138228103Smm if (cb->cb_prevsnap != NULL) 1139248571Smm free(cb->cb_prevsnap); 1140228103Smm /* this snap continues the current range */ 1141248571Smm cb->cb_prevsnap = strdup(name); 1142248571Smm if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL) 1143248571Smm nomem(); 1144228103Smm if (cb->cb_verbose) { 1145228103Smm if (cb->cb_parsable) { 1146228103Smm (void) printf("destroy\t%s\n", name); 1147228103Smm } else if (cb->cb_dryrun) { 1148228103Smm (void) printf(gettext("would destroy %s\n"), 1149228103Smm name); 1150228103Smm } else { 1151228103Smm (void) printf(gettext("will destroy %s\n"), 1152228103Smm name); 1153228103Smm } 1154228103Smm } 1155228103Smm } else if (cb->cb_firstsnap != NULL) { 1156228103Smm /* end of this range */ 1157228103Smm uint64_t used = 0; 1158248571Smm err = lzc_snaprange_space(cb->cb_firstsnap, 1159228103Smm cb->cb_prevsnap, &used); 1160228103Smm cb->cb_snapused += used; 1161248571Smm free(cb->cb_firstsnap); 1162228103Smm cb->cb_firstsnap = NULL; 1163248571Smm free(cb->cb_prevsnap); 1164228103Smm cb->cb_prevsnap = NULL; 1165228103Smm } 1166228103Smm zfs_close(zhp); 1167228103Smm return (err); 1168228103Smm} 1169168404Spjd 1170228103Smmstatic int 1171228103Smmdestroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb) 1172228103Smm{ 1173231144Smm int err = 0; 1174228103Smm assert(cb->cb_firstsnap == NULL); 1175228103Smm assert(cb->cb_prevsnap == NULL); 1176228103Smm err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb); 1177228103Smm if (cb->cb_firstsnap != NULL) { 1178228103Smm uint64_t used = 0; 1179228103Smm if (err == 0) { 1180248571Smm err = lzc_snaprange_space(cb->cb_firstsnap, 1181228103Smm cb->cb_prevsnap, &used); 1182168404Spjd } 1183228103Smm cb->cb_snapused += used; 1184248571Smm free(cb->cb_firstsnap); 1185228103Smm cb->cb_firstsnap = NULL; 1186248571Smm free(cb->cb_prevsnap); 1187228103Smm cb->cb_prevsnap = NULL; 1188168404Spjd } 1189228103Smm return (err); 1190228103Smm} 1191168404Spjd 1192228103Smmstatic int 1193228103Smmsnapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg) 1194228103Smm{ 1195228103Smm destroy_cbdata_t *cb = arg; 1196228103Smm int err = 0; 1197228103Smm 1198228103Smm /* Check for clones. */ 1199238422Smm if (!cb->cb_doclones && !cb->cb_defer_destroy) { 1200228103Smm cb->cb_target = zhp; 1201228103Smm cb->cb_first = B_TRUE; 1202228103Smm err = zfs_iter_dependents(zhp, B_TRUE, 1203228103Smm destroy_check_dependent, cb); 1204228103Smm } 1205228103Smm 1206228103Smm if (err == 0) { 1207228103Smm if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp))) 1208228103Smm nomem(); 1209228103Smm } 1210228103Smm zfs_close(zhp); 1211228103Smm return (err); 1212168404Spjd} 1213168404Spjd 1214168404Spjdstatic int 1215228103Smmgather_snapshots(zfs_handle_t *zhp, void *arg) 1216228103Smm{ 1217228103Smm destroy_cbdata_t *cb = arg; 1218228103Smm int err = 0; 1219228103Smm 1220228103Smm err = zfs_iter_snapspec(zhp, cb->cb_snapspec, snapshot_to_nvl_cb, cb); 1221228103Smm if (err == ENOENT) 1222228103Smm err = 0; 1223228103Smm if (err != 0) 1224228103Smm goto out; 1225228103Smm 1226228103Smm if (cb->cb_verbose) { 1227228103Smm err = destroy_print_snapshots(zhp, cb); 1228228103Smm if (err != 0) 1229228103Smm goto out; 1230228103Smm } 1231228103Smm 1232228103Smm if (cb->cb_recurse) 1233228103Smm err = zfs_iter_filesystems(zhp, gather_snapshots, cb); 1234228103Smm 1235228103Smmout: 1236228103Smm zfs_close(zhp); 1237228103Smm return (err); 1238228103Smm} 1239228103Smm 1240228103Smmstatic int 1241228103Smmdestroy_clones(destroy_cbdata_t *cb) 1242228103Smm{ 1243228103Smm nvpair_t *pair; 1244228103Smm for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL); 1245228103Smm pair != NULL; 1246228103Smm pair = nvlist_next_nvpair(cb->cb_nvl, pair)) { 1247228103Smm zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair), 1248228103Smm ZFS_TYPE_SNAPSHOT); 1249228103Smm if (zhp != NULL) { 1250228103Smm boolean_t defer = cb->cb_defer_destroy; 1251231144Smm int err = 0; 1252228103Smm 1253228103Smm /* 1254228103Smm * We can't defer destroy non-snapshots, so set it to 1255228103Smm * false while destroying the clones. 1256228103Smm */ 1257228103Smm cb->cb_defer_destroy = B_FALSE; 1258228103Smm err = zfs_iter_dependents(zhp, B_FALSE, 1259228103Smm destroy_callback, cb); 1260228103Smm cb->cb_defer_destroy = defer; 1261228103Smm zfs_close(zhp); 1262228103Smm if (err != 0) 1263228103Smm return (err); 1264228103Smm } 1265228103Smm } 1266228103Smm return (0); 1267228103Smm} 1268228103Smm 1269228103Smmstatic int 1270168404Spjdzfs_do_destroy(int argc, char **argv) 1271168404Spjd{ 1272168404Spjd destroy_cbdata_t cb = { 0 }; 1273248571Smm int rv = 0; 1274248571Smm int err = 0; 1275168404Spjd int c; 1276248571Smm zfs_handle_t *zhp = NULL; 1277260183Sdelphij char *at, *pound; 1278219089Spjd zfs_type_t type = ZFS_TYPE_DATASET; 1279168404Spjd 1280168404Spjd /* check options */ 1281228103Smm while ((c = getopt(argc, argv, "vpndfrR")) != -1) { 1282168404Spjd switch (c) { 1283228103Smm case 'v': 1284228103Smm cb.cb_verbose = B_TRUE; 1285228103Smm break; 1286228103Smm case 'p': 1287228103Smm cb.cb_verbose = B_TRUE; 1288228103Smm cb.cb_parsable = B_TRUE; 1289228103Smm break; 1290228103Smm case 'n': 1291228103Smm cb.cb_dryrun = B_TRUE; 1292228103Smm break; 1293219089Spjd case 'd': 1294219089Spjd cb.cb_defer_destroy = B_TRUE; 1295219089Spjd type = ZFS_TYPE_SNAPSHOT; 1296219089Spjd break; 1297168404Spjd case 'f': 1298228103Smm cb.cb_force = B_TRUE; 1299168404Spjd break; 1300168404Spjd case 'r': 1301228103Smm cb.cb_recurse = B_TRUE; 1302168404Spjd break; 1303168404Spjd case 'R': 1304228103Smm cb.cb_recurse = B_TRUE; 1305228103Smm cb.cb_doclones = B_TRUE; 1306168404Spjd break; 1307168404Spjd case '?': 1308168404Spjd default: 1309168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1310168404Spjd optopt); 1311168404Spjd usage(B_FALSE); 1312168404Spjd } 1313168404Spjd } 1314168404Spjd 1315168404Spjd argc -= optind; 1316168404Spjd argv += optind; 1317168404Spjd 1318168404Spjd /* check number of arguments */ 1319168404Spjd if (argc == 0) { 1320228103Smm (void) fprintf(stderr, gettext("missing dataset argument\n")); 1321168404Spjd usage(B_FALSE); 1322168404Spjd } 1323168404Spjd if (argc > 1) { 1324168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 1325168404Spjd usage(B_FALSE); 1326168404Spjd } 1327168404Spjd 1328228103Smm at = strchr(argv[0], '@'); 1329260183Sdelphij pound = strchr(argv[0], '#'); 1330228103Smm if (at != NULL) { 1331168404Spjd 1332228103Smm /* Build the list of snaps to destroy in cb_nvl. */ 1333248571Smm cb.cb_nvl = fnvlist_alloc(); 1334228103Smm 1335228103Smm *at = '\0'; 1336228103Smm zhp = zfs_open(g_zfs, argv[0], 1337228103Smm ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 1338228103Smm if (zhp == NULL) 1339168404Spjd return (1); 1340168404Spjd 1341228103Smm cb.cb_snapspec = at + 1; 1342228103Smm if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 || 1343228103Smm cb.cb_error) { 1344248571Smm rv = 1; 1345248571Smm goto out; 1346228103Smm } 1347219089Spjd 1348228103Smm if (nvlist_empty(cb.cb_nvl)) { 1349228103Smm (void) fprintf(stderr, gettext("could not find any " 1350228103Smm "snapshots to destroy; check snapshot names.\n")); 1351248571Smm rv = 1; 1352248571Smm goto out; 1353228103Smm } 1354228103Smm 1355228103Smm if (cb.cb_verbose) { 1356228103Smm char buf[16]; 1357228103Smm zfs_nicenum(cb.cb_snapused, buf, sizeof (buf)); 1358228103Smm if (cb.cb_parsable) { 1359228103Smm (void) printf("reclaim\t%llu\n", 1360228103Smm cb.cb_snapused); 1361228103Smm } else if (cb.cb_dryrun) { 1362228103Smm (void) printf(gettext("would reclaim %s\n"), 1363228103Smm buf); 1364228103Smm } else { 1365228103Smm (void) printf(gettext("will reclaim %s\n"), 1366228103Smm buf); 1367168404Spjd } 1368168404Spjd } 1369168404Spjd 1370228103Smm if (!cb.cb_dryrun) { 1371248571Smm if (cb.cb_doclones) { 1372248571Smm cb.cb_batchedsnaps = fnvlist_alloc(); 1373228103Smm err = destroy_clones(&cb); 1374248571Smm if (err == 0) { 1375248571Smm err = zfs_destroy_snaps_nvl(g_zfs, 1376248571Smm cb.cb_batchedsnaps, B_FALSE); 1377248571Smm } 1378248571Smm if (err != 0) { 1379248571Smm rv = 1; 1380248571Smm goto out; 1381248571Smm } 1382248571Smm } 1383228103Smm if (err == 0) { 1384248571Smm err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl, 1385228103Smm cb.cb_defer_destroy); 1386228103Smm } 1387168404Spjd } 1388168404Spjd 1389228103Smm if (err != 0) 1390248571Smm rv = 1; 1391260183Sdelphij } else if (pound != NULL) { 1392260183Sdelphij int err; 1393260183Sdelphij nvlist_t *nvl; 1394260183Sdelphij 1395260183Sdelphij if (cb.cb_dryrun) { 1396260183Sdelphij (void) fprintf(stderr, 1397260183Sdelphij "dryrun is not supported with bookmark\n"); 1398260183Sdelphij return (-1); 1399260183Sdelphij } 1400260183Sdelphij 1401260183Sdelphij if (cb.cb_defer_destroy) { 1402260183Sdelphij (void) fprintf(stderr, 1403260183Sdelphij "defer destroy is not supported with bookmark\n"); 1404260183Sdelphij return (-1); 1405260183Sdelphij } 1406260183Sdelphij 1407260183Sdelphij if (cb.cb_recurse) { 1408260183Sdelphij (void) fprintf(stderr, 1409260183Sdelphij "recursive is not supported with bookmark\n"); 1410260183Sdelphij return (-1); 1411260183Sdelphij } 1412260183Sdelphij 1413260183Sdelphij if (!zfs_bookmark_exists(argv[0])) { 1414260183Sdelphij (void) fprintf(stderr, gettext("bookmark '%s' " 1415260183Sdelphij "does not exist.\n"), argv[0]); 1416260183Sdelphij return (1); 1417260183Sdelphij } 1418260183Sdelphij 1419260183Sdelphij nvl = fnvlist_alloc(); 1420260183Sdelphij fnvlist_add_boolean(nvl, argv[0]); 1421260183Sdelphij 1422260183Sdelphij err = lzc_destroy_bookmarks(nvl, NULL); 1423260183Sdelphij if (err != 0) { 1424260183Sdelphij (void) zfs_standard_error(g_zfs, err, 1425260183Sdelphij "cannot destroy bookmark"); 1426260183Sdelphij } 1427260183Sdelphij 1428260183Sdelphij nvlist_free(cb.cb_nvl); 1429260183Sdelphij 1430260183Sdelphij return (err); 1431228103Smm } else { 1432228103Smm /* Open the given dataset */ 1433228103Smm if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL) 1434228103Smm return (1); 1435168404Spjd 1436228103Smm cb.cb_target = zhp; 1437168404Spjd 1438228103Smm /* 1439228103Smm * Perform an explicit check for pools before going any further. 1440228103Smm */ 1441228103Smm if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && 1442228103Smm zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { 1443228103Smm (void) fprintf(stderr, gettext("cannot destroy '%s': " 1444228103Smm "operation does not apply to pools\n"), 1445228103Smm zfs_get_name(zhp)); 1446228103Smm (void) fprintf(stderr, gettext("use 'zfs destroy -r " 1447228103Smm "%s' to destroy all datasets in the pool\n"), 1448228103Smm zfs_get_name(zhp)); 1449228103Smm (void) fprintf(stderr, gettext("use 'zpool destroy %s' " 1450228103Smm "to destroy the pool itself\n"), zfs_get_name(zhp)); 1451248571Smm rv = 1; 1452248571Smm goto out; 1453228103Smm } 1454168404Spjd 1455228103Smm /* 1456228103Smm * Check for any dependents and/or clones. 1457228103Smm */ 1458228103Smm cb.cb_first = B_TRUE; 1459228103Smm if (!cb.cb_doclones && 1460228103Smm zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent, 1461228103Smm &cb) != 0) { 1462248571Smm rv = 1; 1463248571Smm goto out; 1464228103Smm } 1465168404Spjd 1466228103Smm if (cb.cb_error) { 1467248571Smm rv = 1; 1468248571Smm goto out; 1469228103Smm } 1470168404Spjd 1471248571Smm cb.cb_batchedsnaps = fnvlist_alloc(); 1472228103Smm if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback, 1473228103Smm &cb) != 0) { 1474248571Smm rv = 1; 1475248571Smm goto out; 1476228103Smm } 1477185029Spjd 1478228103Smm /* 1479228103Smm * Do the real thing. The callback will close the 1480228103Smm * handle regardless of whether it succeeds or not. 1481228103Smm */ 1482248571Smm err = destroy_callback(zhp, &cb); 1483248571Smm zhp = NULL; 1484248571Smm if (err == 0) { 1485248571Smm err = zfs_destroy_snaps_nvl(g_zfs, 1486248571Smm cb.cb_batchedsnaps, cb.cb_defer_destroy); 1487248571Smm } 1488248571Smm if (err != 0) 1489248571Smm rv = 1; 1490228103Smm } 1491168404Spjd 1492248571Smmout: 1493248571Smm fnvlist_free(cb.cb_batchedsnaps); 1494248571Smm fnvlist_free(cb.cb_nvl); 1495248571Smm if (zhp != NULL) 1496248571Smm zfs_close(zhp); 1497248571Smm return (rv); 1498168404Spjd} 1499168404Spjd 1500219089Spjdstatic boolean_t 1501219089Spjdis_recvd_column(zprop_get_cbdata_t *cbp) 1502219089Spjd{ 1503219089Spjd int i; 1504219089Spjd zfs_get_column_t col; 1505219089Spjd 1506219089Spjd for (i = 0; i < ZFS_GET_NCOLS && 1507219089Spjd (col = cbp->cb_columns[i]) != GET_COL_NONE; i++) 1508219089Spjd if (col == GET_COL_RECVD) 1509219089Spjd return (B_TRUE); 1510219089Spjd return (B_FALSE); 1511219089Spjd} 1512219089Spjd 1513168404Spjd/* 1514219089Spjd * zfs get [-rHp] [-o all | field[,field]...] [-s source[,source]...] 1515219089Spjd * < all | property[,property]... > < fs | snap | vol > ... 1516168404Spjd * 1517168404Spjd * -r recurse over any child datasets 1518168404Spjd * -H scripted mode. Headers are stripped, and fields are separated 1519168404Spjd * by tabs instead of spaces. 1520219089Spjd * -o Set of fields to display. One of "name,property,value, 1521219089Spjd * received,source". Default is "name,property,value,source". 1522219089Spjd * "all" is an alias for all five. 1523168404Spjd * -s Set of sources to allow. One of 1524219089Spjd * "local,default,inherited,received,temporary,none". Default is 1525219089Spjd * all six. 1526168404Spjd * -p Display values in parsable (literal) format. 1527168404Spjd * 1528168404Spjd * Prints properties for the given datasets. The user can control which 1529168404Spjd * columns to display as well as which property types to allow. 1530168404Spjd */ 1531168404Spjd 1532168404Spjd/* 1533168404Spjd * Invoked to display the properties for a single dataset. 1534168404Spjd */ 1535168404Spjdstatic int 1536168404Spjdget_callback(zfs_handle_t *zhp, void *data) 1537168404Spjd{ 1538168404Spjd char buf[ZFS_MAXPROPLEN]; 1539219089Spjd char rbuf[ZFS_MAXPROPLEN]; 1540185029Spjd zprop_source_t sourcetype; 1541307108Smav char source[ZFS_MAX_DATASET_NAME_LEN]; 1542185029Spjd zprop_get_cbdata_t *cbp = data; 1543219089Spjd nvlist_t *user_props = zfs_get_user_props(zhp); 1544185029Spjd zprop_list_t *pl = cbp->cb_proplist; 1545168404Spjd nvlist_t *propval; 1546168404Spjd char *strval; 1547168404Spjd char *sourceval; 1548219089Spjd boolean_t received = is_recvd_column(cbp); 1549168404Spjd 1550168404Spjd for (; pl != NULL; pl = pl->pl_next) { 1551219089Spjd char *recvdval = NULL; 1552168404Spjd /* 1553168404Spjd * Skip the special fake placeholder. This will also skip over 1554168404Spjd * the name property when 'all' is specified. 1555168404Spjd */ 1556168404Spjd if (pl->pl_prop == ZFS_PROP_NAME && 1557168404Spjd pl == cbp->cb_proplist) 1558168404Spjd continue; 1559168404Spjd 1560185029Spjd if (pl->pl_prop != ZPROP_INVAL) { 1561168404Spjd if (zfs_prop_get(zhp, pl->pl_prop, buf, 1562168404Spjd sizeof (buf), &sourcetype, source, 1563168404Spjd sizeof (source), 1564168404Spjd cbp->cb_literal) != 0) { 1565168404Spjd if (pl->pl_all) 1566168404Spjd continue; 1567168404Spjd if (!zfs_prop_valid_for_type(pl->pl_prop, 1568185029Spjd ZFS_TYPE_DATASET)) { 1569168404Spjd (void) fprintf(stderr, 1570168404Spjd gettext("No such property '%s'\n"), 1571168404Spjd zfs_prop_to_name(pl->pl_prop)); 1572168404Spjd continue; 1573168404Spjd } 1574185029Spjd sourcetype = ZPROP_SRC_NONE; 1575168404Spjd (void) strlcpy(buf, "-", sizeof (buf)); 1576168404Spjd } 1577168404Spjd 1578219089Spjd if (received && (zfs_prop_get_recvd(zhp, 1579219089Spjd zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf), 1580219089Spjd cbp->cb_literal) == 0)) 1581219089Spjd recvdval = rbuf; 1582219089Spjd 1583185029Spjd zprop_print_one_property(zfs_get_name(zhp), cbp, 1584168404Spjd zfs_prop_to_name(pl->pl_prop), 1585219089Spjd buf, sourcetype, source, recvdval); 1586209962Smm } else if (zfs_prop_userquota(pl->pl_user_prop)) { 1587209962Smm sourcetype = ZPROP_SRC_LOCAL; 1588209962Smm 1589209962Smm if (zfs_prop_get_userquota(zhp, pl->pl_user_prop, 1590209962Smm buf, sizeof (buf), cbp->cb_literal) != 0) { 1591209962Smm sourcetype = ZPROP_SRC_NONE; 1592209962Smm (void) strlcpy(buf, "-", sizeof (buf)); 1593209962Smm } 1594209962Smm 1595209962Smm zprop_print_one_property(zfs_get_name(zhp), cbp, 1596219089Spjd pl->pl_user_prop, buf, sourcetype, source, NULL); 1597228103Smm } else if (zfs_prop_written(pl->pl_user_prop)) { 1598228103Smm sourcetype = ZPROP_SRC_LOCAL; 1599228103Smm 1600228103Smm if (zfs_prop_get_written(zhp, pl->pl_user_prop, 1601228103Smm buf, sizeof (buf), cbp->cb_literal) != 0) { 1602228103Smm sourcetype = ZPROP_SRC_NONE; 1603228103Smm (void) strlcpy(buf, "-", sizeof (buf)); 1604228103Smm } 1605228103Smm 1606228103Smm zprop_print_one_property(zfs_get_name(zhp), cbp, 1607228103Smm pl->pl_user_prop, buf, sourcetype, source, NULL); 1608168404Spjd } else { 1609219089Spjd if (nvlist_lookup_nvlist(user_props, 1610168404Spjd pl->pl_user_prop, &propval) != 0) { 1611168404Spjd if (pl->pl_all) 1612168404Spjd continue; 1613185029Spjd sourcetype = ZPROP_SRC_NONE; 1614168404Spjd strval = "-"; 1615168404Spjd } else { 1616168404Spjd verify(nvlist_lookup_string(propval, 1617185029Spjd ZPROP_VALUE, &strval) == 0); 1618168404Spjd verify(nvlist_lookup_string(propval, 1619185029Spjd ZPROP_SOURCE, &sourceval) == 0); 1620168404Spjd 1621168404Spjd if (strcmp(sourceval, 1622168404Spjd zfs_get_name(zhp)) == 0) { 1623185029Spjd sourcetype = ZPROP_SRC_LOCAL; 1624219089Spjd } else if (strcmp(sourceval, 1625219089Spjd ZPROP_SOURCE_VAL_RECVD) == 0) { 1626219089Spjd sourcetype = ZPROP_SRC_RECEIVED; 1627168404Spjd } else { 1628185029Spjd sourcetype = ZPROP_SRC_INHERITED; 1629168404Spjd (void) strlcpy(source, 1630168404Spjd sourceval, sizeof (source)); 1631168404Spjd } 1632168404Spjd } 1633168404Spjd 1634219089Spjd if (received && (zfs_prop_get_recvd(zhp, 1635219089Spjd pl->pl_user_prop, rbuf, sizeof (rbuf), 1636219089Spjd cbp->cb_literal) == 0)) 1637219089Spjd recvdval = rbuf; 1638219089Spjd 1639185029Spjd zprop_print_one_property(zfs_get_name(zhp), cbp, 1640168404Spjd pl->pl_user_prop, strval, sourcetype, 1641219089Spjd source, recvdval); 1642168404Spjd } 1643168404Spjd } 1644168404Spjd 1645168404Spjd return (0); 1646168404Spjd} 1647168404Spjd 1648168404Spjdstatic int 1649168404Spjdzfs_do_get(int argc, char **argv) 1650168404Spjd{ 1651185029Spjd zprop_get_cbdata_t cb = { 0 }; 1652223620Smm int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS; 1653321534Smav int types = ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK; 1654168404Spjd char *value, *fields; 1655231144Smm int ret = 0; 1656205199Sdelphij int limit = 0; 1657185029Spjd zprop_list_t fake_name = { 0 }; 1658168404Spjd 1659168404Spjd /* 1660168404Spjd * Set up default columns and sources. 1661168404Spjd */ 1662185029Spjd cb.cb_sources = ZPROP_SRC_ALL; 1663168404Spjd cb.cb_columns[0] = GET_COL_NAME; 1664168404Spjd cb.cb_columns[1] = GET_COL_PROPERTY; 1665168404Spjd cb.cb_columns[2] = GET_COL_VALUE; 1666168404Spjd cb.cb_columns[3] = GET_COL_SOURCE; 1667185029Spjd cb.cb_type = ZFS_TYPE_DATASET; 1668168404Spjd 1669168404Spjd /* check options */ 1670232064Smm while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) { 1671168404Spjd switch (c) { 1672168404Spjd case 'p': 1673168404Spjd cb.cb_literal = B_TRUE; 1674168404Spjd break; 1675205199Sdelphij case 'd': 1676205199Sdelphij limit = parse_depth(optarg, &flags); 1677205199Sdelphij break; 1678168404Spjd case 'r': 1679185029Spjd flags |= ZFS_ITER_RECURSE; 1680168404Spjd break; 1681168404Spjd case 'H': 1682168404Spjd cb.cb_scripted = B_TRUE; 1683168404Spjd break; 1684168404Spjd case ':': 1685168404Spjd (void) fprintf(stderr, gettext("missing argument for " 1686168404Spjd "'%c' option\n"), optopt); 1687168404Spjd usage(B_FALSE); 1688168404Spjd break; 1689168404Spjd case 'o': 1690168404Spjd /* 1691168404Spjd * Process the set of columns to display. We zero out 1692168404Spjd * the structure to give us a blank slate. 1693168404Spjd */ 1694168404Spjd bzero(&cb.cb_columns, sizeof (cb.cb_columns)); 1695168404Spjd i = 0; 1696168404Spjd while (*optarg != '\0') { 1697168404Spjd static char *col_subopts[] = 1698219089Spjd { "name", "property", "value", "received", 1699219089Spjd "source", "all", NULL }; 1700168404Spjd 1701219089Spjd if (i == ZFS_GET_NCOLS) { 1702168404Spjd (void) fprintf(stderr, gettext("too " 1703168404Spjd "many fields given to -o " 1704168404Spjd "option\n")); 1705168404Spjd usage(B_FALSE); 1706168404Spjd } 1707168404Spjd 1708168404Spjd switch (getsubopt(&optarg, col_subopts, 1709168404Spjd &value)) { 1710168404Spjd case 0: 1711168404Spjd cb.cb_columns[i++] = GET_COL_NAME; 1712168404Spjd break; 1713168404Spjd case 1: 1714168404Spjd cb.cb_columns[i++] = GET_COL_PROPERTY; 1715168404Spjd break; 1716168404Spjd case 2: 1717168404Spjd cb.cb_columns[i++] = GET_COL_VALUE; 1718168404Spjd break; 1719168404Spjd case 3: 1720219089Spjd cb.cb_columns[i++] = GET_COL_RECVD; 1721219089Spjd flags |= ZFS_ITER_RECVD_PROPS; 1722219089Spjd break; 1723219089Spjd case 4: 1724168404Spjd cb.cb_columns[i++] = GET_COL_SOURCE; 1725168404Spjd break; 1726219089Spjd case 5: 1727219089Spjd if (i > 0) { 1728219089Spjd (void) fprintf(stderr, 1729219089Spjd gettext("\"all\" conflicts " 1730219089Spjd "with specific fields " 1731219089Spjd "given to -o option\n")); 1732219089Spjd usage(B_FALSE); 1733219089Spjd } 1734219089Spjd cb.cb_columns[0] = GET_COL_NAME; 1735219089Spjd cb.cb_columns[1] = GET_COL_PROPERTY; 1736219089Spjd cb.cb_columns[2] = GET_COL_VALUE; 1737219089Spjd cb.cb_columns[3] = GET_COL_RECVD; 1738219089Spjd cb.cb_columns[4] = GET_COL_SOURCE; 1739219089Spjd flags |= ZFS_ITER_RECVD_PROPS; 1740219089Spjd i = ZFS_GET_NCOLS; 1741219089Spjd break; 1742168404Spjd default: 1743168404Spjd (void) fprintf(stderr, 1744168404Spjd gettext("invalid column name " 1745295844Sdim "'%s'\n"), suboptarg); 1746168404Spjd usage(B_FALSE); 1747168404Spjd } 1748168404Spjd } 1749168404Spjd break; 1750168404Spjd 1751168404Spjd case 's': 1752168404Spjd cb.cb_sources = 0; 1753168404Spjd while (*optarg != '\0') { 1754168404Spjd static char *source_subopts[] = { 1755168404Spjd "local", "default", "inherited", 1756219089Spjd "received", "temporary", "none", 1757219089Spjd NULL }; 1758168404Spjd 1759168404Spjd switch (getsubopt(&optarg, source_subopts, 1760168404Spjd &value)) { 1761168404Spjd case 0: 1762185029Spjd cb.cb_sources |= ZPROP_SRC_LOCAL; 1763168404Spjd break; 1764168404Spjd case 1: 1765185029Spjd cb.cb_sources |= ZPROP_SRC_DEFAULT; 1766168404Spjd break; 1767168404Spjd case 2: 1768185029Spjd cb.cb_sources |= ZPROP_SRC_INHERITED; 1769168404Spjd break; 1770168404Spjd case 3: 1771219089Spjd cb.cb_sources |= ZPROP_SRC_RECEIVED; 1772219089Spjd break; 1773219089Spjd case 4: 1774185029Spjd cb.cb_sources |= ZPROP_SRC_TEMPORARY; 1775168404Spjd break; 1776219089Spjd case 5: 1777185029Spjd cb.cb_sources |= ZPROP_SRC_NONE; 1778168404Spjd break; 1779168404Spjd default: 1780168404Spjd (void) fprintf(stderr, 1781168404Spjd gettext("invalid source " 1782295844Sdim "'%s'\n"), suboptarg); 1783168404Spjd usage(B_FALSE); 1784168404Spjd } 1785168404Spjd } 1786168404Spjd break; 1787168404Spjd 1788232064Smm case 't': 1789232064Smm types = 0; 1790232064Smm flags &= ~ZFS_ITER_PROP_LISTSNAPS; 1791232064Smm while (*optarg != '\0') { 1792232064Smm static char *type_subopts[] = { "filesystem", 1793260183Sdelphij "volume", "snapshot", "bookmark", 1794260183Sdelphij "all", NULL }; 1795232064Smm 1796232064Smm switch (getsubopt(&optarg, type_subopts, 1797232064Smm &value)) { 1798232064Smm case 0: 1799232064Smm types |= ZFS_TYPE_FILESYSTEM; 1800232064Smm break; 1801232064Smm case 1: 1802232064Smm types |= ZFS_TYPE_VOLUME; 1803232064Smm break; 1804232064Smm case 2: 1805232064Smm types |= ZFS_TYPE_SNAPSHOT; 1806232064Smm break; 1807232064Smm case 3: 1808260183Sdelphij types |= ZFS_TYPE_BOOKMARK; 1809232064Smm break; 1810260183Sdelphij case 4: 1811260183Sdelphij types = ZFS_TYPE_DATASET | 1812260183Sdelphij ZFS_TYPE_BOOKMARK; 1813260183Sdelphij break; 1814232064Smm 1815232064Smm default: 1816232064Smm (void) fprintf(stderr, 1817232064Smm gettext("invalid type '%s'\n"), 1818295844Sdim suboptarg); 1819232064Smm usage(B_FALSE); 1820232064Smm } 1821232064Smm } 1822232064Smm break; 1823232064Smm 1824168404Spjd case '?': 1825168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1826168404Spjd optopt); 1827168404Spjd usage(B_FALSE); 1828168404Spjd } 1829168404Spjd } 1830168404Spjd 1831168404Spjd argc -= optind; 1832168404Spjd argv += optind; 1833168404Spjd 1834168404Spjd if (argc < 1) { 1835168404Spjd (void) fprintf(stderr, gettext("missing property " 1836168404Spjd "argument\n")); 1837168404Spjd usage(B_FALSE); 1838168404Spjd } 1839168404Spjd 1840168404Spjd fields = argv[0]; 1841168404Spjd 1842185029Spjd if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET) 1843185029Spjd != 0) 1844168404Spjd usage(B_FALSE); 1845168404Spjd 1846168404Spjd argc--; 1847168404Spjd argv++; 1848168404Spjd 1849168404Spjd /* 1850168404Spjd * As part of zfs_expand_proplist(), we keep track of the maximum column 1851168404Spjd * width for each property. For the 'NAME' (and 'SOURCE') columns, we 1852168404Spjd * need to know the maximum name length. However, the user likely did 1853168404Spjd * not specify 'name' as one of the properties to fetch, so we need to 1854168404Spjd * make sure we always include at least this property for 1855168404Spjd * print_get_headers() to work properly. 1856168404Spjd */ 1857168404Spjd if (cb.cb_proplist != NULL) { 1858168404Spjd fake_name.pl_prop = ZFS_PROP_NAME; 1859168404Spjd fake_name.pl_width = strlen(gettext("NAME")); 1860168404Spjd fake_name.pl_next = cb.cb_proplist; 1861168404Spjd cb.cb_proplist = &fake_name; 1862168404Spjd } 1863168404Spjd 1864168404Spjd cb.cb_first = B_TRUE; 1865168404Spjd 1866168404Spjd /* run for each object */ 1867232064Smm ret = zfs_for_each(argc, argv, flags, types, NULL, 1868205199Sdelphij &cb.cb_proplist, limit, get_callback, &cb); 1869168404Spjd 1870168404Spjd if (cb.cb_proplist == &fake_name) 1871185029Spjd zprop_free_list(fake_name.pl_next); 1872168404Spjd else 1873185029Spjd zprop_free_list(cb.cb_proplist); 1874168404Spjd 1875168404Spjd return (ret); 1876168404Spjd} 1877168404Spjd 1878168404Spjd/* 1879219089Spjd * inherit [-rS] <property> <fs|vol> ... 1880168404Spjd * 1881219089Spjd * -r Recurse over all children 1882219089Spjd * -S Revert to received value, if any 1883168404Spjd * 1884168404Spjd * For each dataset specified on the command line, inherit the given property 1885168404Spjd * from its parent. Inheriting a property at the pool level will cause it to 1886168404Spjd * use the default value. The '-r' flag will recurse over all children, and is 1887168404Spjd * useful for setting a property on a hierarchy-wide basis, regardless of any 1888168404Spjd * local modifications for each dataset. 1889168404Spjd */ 1890168404Spjd 1891219089Spjdtypedef struct inherit_cbdata { 1892219089Spjd const char *cb_propname; 1893219089Spjd boolean_t cb_received; 1894219089Spjd} inherit_cbdata_t; 1895219089Spjd 1896168404Spjdstatic int 1897185029Spjdinherit_recurse_cb(zfs_handle_t *zhp, void *data) 1898168404Spjd{ 1899219089Spjd inherit_cbdata_t *cb = data; 1900219089Spjd zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname); 1901168404Spjd 1902185029Spjd /* 1903185029Spjd * If we're doing it recursively, then ignore properties that 1904185029Spjd * are not valid for this type of dataset. 1905185029Spjd */ 1906185029Spjd if (prop != ZPROP_INVAL && 1907185029Spjd !zfs_prop_valid_for_type(prop, zfs_get_type(zhp))) 1908185029Spjd return (0); 1909185029Spjd 1910219089Spjd return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0); 1911168404Spjd} 1912168404Spjd 1913168404Spjdstatic int 1914185029Spjdinherit_cb(zfs_handle_t *zhp, void *data) 1915185029Spjd{ 1916219089Spjd inherit_cbdata_t *cb = data; 1917185029Spjd 1918219089Spjd return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0); 1919185029Spjd} 1920185029Spjd 1921185029Spjdstatic int 1922168404Spjdzfs_do_inherit(int argc, char **argv) 1923168404Spjd{ 1924168404Spjd int c; 1925168404Spjd zfs_prop_t prop; 1926219089Spjd inherit_cbdata_t cb = { 0 }; 1927185029Spjd char *propname; 1928231144Smm int ret = 0; 1929185029Spjd int flags = 0; 1930219089Spjd boolean_t received = B_FALSE; 1931168404Spjd 1932168404Spjd /* check options */ 1933219089Spjd while ((c = getopt(argc, argv, "rS")) != -1) { 1934168404Spjd switch (c) { 1935168404Spjd case 'r': 1936185029Spjd flags |= ZFS_ITER_RECURSE; 1937168404Spjd break; 1938219089Spjd case 'S': 1939219089Spjd received = B_TRUE; 1940219089Spjd break; 1941168404Spjd case '?': 1942168404Spjd default: 1943168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 1944168404Spjd optopt); 1945168404Spjd usage(B_FALSE); 1946168404Spjd } 1947168404Spjd } 1948168404Spjd 1949168404Spjd argc -= optind; 1950168404Spjd argv += optind; 1951168404Spjd 1952168404Spjd /* check number of arguments */ 1953168404Spjd if (argc < 1) { 1954168404Spjd (void) fprintf(stderr, gettext("missing property argument\n")); 1955168404Spjd usage(B_FALSE); 1956168404Spjd } 1957168404Spjd if (argc < 2) { 1958168404Spjd (void) fprintf(stderr, gettext("missing dataset argument\n")); 1959168404Spjd usage(B_FALSE); 1960168404Spjd } 1961168404Spjd 1962185029Spjd propname = argv[0]; 1963168404Spjd argc--; 1964168404Spjd argv++; 1965168404Spjd 1966185029Spjd if ((prop = zfs_name_to_prop(propname)) != ZPROP_INVAL) { 1967168404Spjd if (zfs_prop_readonly(prop)) { 1968168404Spjd (void) fprintf(stderr, gettext( 1969168404Spjd "%s property is read-only\n"), 1970185029Spjd propname); 1971168404Spjd return (1); 1972168404Spjd } 1973219089Spjd if (!zfs_prop_inheritable(prop) && !received) { 1974168404Spjd (void) fprintf(stderr, gettext("'%s' property cannot " 1975185029Spjd "be inherited\n"), propname); 1976168404Spjd if (prop == ZFS_PROP_QUOTA || 1977185029Spjd prop == ZFS_PROP_RESERVATION || 1978185029Spjd prop == ZFS_PROP_REFQUOTA || 1979287770Sdelphij prop == ZFS_PROP_REFRESERVATION) { 1980168404Spjd (void) fprintf(stderr, gettext("use 'zfs set " 1981185029Spjd "%s=none' to clear\n"), propname); 1982287770Sdelphij (void) fprintf(stderr, gettext("use 'zfs " 1983287770Sdelphij "inherit -S %s' to revert to received " 1984287770Sdelphij "value\n"), propname); 1985287770Sdelphij } 1986168404Spjd return (1); 1987168404Spjd } 1988219089Spjd if (received && (prop == ZFS_PROP_VOLSIZE || 1989219089Spjd prop == ZFS_PROP_VERSION)) { 1990219089Spjd (void) fprintf(stderr, gettext("'%s' property cannot " 1991219089Spjd "be reverted to a received value\n"), propname); 1992219089Spjd return (1); 1993219089Spjd } 1994185029Spjd } else if (!zfs_prop_user(propname)) { 1995185029Spjd (void) fprintf(stderr, gettext("invalid property '%s'\n"), 1996185029Spjd propname); 1997168404Spjd usage(B_FALSE); 1998168404Spjd } 1999168404Spjd 2000219089Spjd cb.cb_propname = propname; 2001219089Spjd cb.cb_received = received; 2002219089Spjd 2003185029Spjd if (flags & ZFS_ITER_RECURSE) { 2004185029Spjd ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET, 2005219089Spjd NULL, NULL, 0, inherit_recurse_cb, &cb); 2006185029Spjd } else { 2007185029Spjd ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET, 2008219089Spjd NULL, NULL, 0, inherit_cb, &cb); 2009185029Spjd } 2010168404Spjd 2011185029Spjd return (ret); 2012185029Spjd} 2013168404Spjd 2014185029Spjdtypedef struct upgrade_cbdata { 2015185029Spjd uint64_t cb_numupgraded; 2016185029Spjd uint64_t cb_numsamegraded; 2017185029Spjd uint64_t cb_numfailed; 2018185029Spjd uint64_t cb_version; 2019185029Spjd boolean_t cb_newer; 2020185029Spjd boolean_t cb_foundone; 2021307108Smav char cb_lastfs[ZFS_MAX_DATASET_NAME_LEN]; 2022185029Spjd} upgrade_cbdata_t; 2023185029Spjd 2024185029Spjdstatic int 2025185029Spjdsame_pool(zfs_handle_t *zhp, const char *name) 2026185029Spjd{ 2027185029Spjd int len1 = strcspn(name, "/@"); 2028185029Spjd const char *zhname = zfs_get_name(zhp); 2029185029Spjd int len2 = strcspn(zhname, "/@"); 2030185029Spjd 2031185029Spjd if (len1 != len2) 2032185029Spjd return (B_FALSE); 2033185029Spjd return (strncmp(name, zhname, len1) == 0); 2034185029Spjd} 2035185029Spjd 2036185029Spjdstatic int 2037185029Spjdupgrade_list_callback(zfs_handle_t *zhp, void *data) 2038185029Spjd{ 2039185029Spjd upgrade_cbdata_t *cb = data; 2040185029Spjd int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 2041185029Spjd 2042185029Spjd /* list if it's old/new */ 2043185029Spjd if ((!cb->cb_newer && version < ZPL_VERSION) || 2044185029Spjd (cb->cb_newer && version > ZPL_VERSION)) { 2045185029Spjd char *str; 2046185029Spjd if (cb->cb_newer) { 2047185029Spjd str = gettext("The following filesystems are " 2048185029Spjd "formatted using a newer software version and\n" 2049185029Spjd "cannot be accessed on the current system.\n\n"); 2050185029Spjd } else { 2051185029Spjd str = gettext("The following filesystems are " 2052185029Spjd "out of date, and can be upgraded. After being\n" 2053185029Spjd "upgraded, these filesystems (and any 'zfs send' " 2054185029Spjd "streams generated from\n" 2055185029Spjd "subsequent snapshots) will no longer be " 2056185029Spjd "accessible by older software versions.\n\n"); 2057185029Spjd } 2058185029Spjd 2059185029Spjd if (!cb->cb_foundone) { 2060185029Spjd (void) puts(str); 2061185029Spjd (void) printf(gettext("VER FILESYSTEM\n")); 2062185029Spjd (void) printf(gettext("--- ------------\n")); 2063185029Spjd cb->cb_foundone = B_TRUE; 2064185029Spjd } 2065185029Spjd 2066185029Spjd (void) printf("%2u %s\n", version, zfs_get_name(zhp)); 2067168404Spjd } 2068168404Spjd 2069185029Spjd return (0); 2070185029Spjd} 2071185029Spjd 2072185029Spjdstatic int 2073185029Spjdupgrade_set_callback(zfs_handle_t *zhp, void *data) 2074185029Spjd{ 2075185029Spjd upgrade_cbdata_t *cb = data; 2076185029Spjd int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); 2077219089Spjd int needed_spa_version; 2078219089Spjd int spa_version; 2079185029Spjd 2080219089Spjd if (zfs_spa_version(zhp, &spa_version) < 0) 2081219089Spjd return (-1); 2082185029Spjd 2083219089Spjd needed_spa_version = zfs_spa_version_map(cb->cb_version); 2084185029Spjd 2085219089Spjd if (needed_spa_version < 0) 2086219089Spjd return (-1); 2087209962Smm 2088219089Spjd if (spa_version < needed_spa_version) { 2089219089Spjd /* can't upgrade */ 2090219089Spjd (void) printf(gettext("%s: can not be " 2091219089Spjd "upgraded; the pool version needs to first " 2092219089Spjd "be upgraded\nto version %d\n\n"), 2093219089Spjd zfs_get_name(zhp), needed_spa_version); 2094219089Spjd cb->cb_numfailed++; 2095219089Spjd return (0); 2096185029Spjd } 2097185029Spjd 2098185029Spjd /* upgrade */ 2099185029Spjd if (version < cb->cb_version) { 2100185029Spjd char verstr[16]; 2101185029Spjd (void) snprintf(verstr, sizeof (verstr), 2102185029Spjd "%llu", cb->cb_version); 2103185029Spjd if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) { 2104185029Spjd /* 2105185029Spjd * If they did "zfs upgrade -a", then we could 2106185029Spjd * be doing ioctls to different pools. We need 2107248571Smm * to log this history once to each pool, and bypass 2108248571Smm * the normal history logging that happens in main(). 2109185029Spjd */ 2110248571Smm (void) zpool_log_history(g_zfs, history_str); 2111248571Smm log_history = B_FALSE; 2112185029Spjd } 2113185029Spjd if (zfs_prop_set(zhp, "version", verstr) == 0) 2114185029Spjd cb->cb_numupgraded++; 2115185029Spjd else 2116185029Spjd cb->cb_numfailed++; 2117185029Spjd (void) strcpy(cb->cb_lastfs, zfs_get_name(zhp)); 2118185029Spjd } else if (version > cb->cb_version) { 2119185029Spjd /* can't downgrade */ 2120185029Spjd (void) printf(gettext("%s: can not be downgraded; " 2121185029Spjd "it is already at version %u\n"), 2122185029Spjd zfs_get_name(zhp), version); 2123185029Spjd cb->cb_numfailed++; 2124185029Spjd } else { 2125185029Spjd cb->cb_numsamegraded++; 2126185029Spjd } 2127185029Spjd return (0); 2128185029Spjd} 2129185029Spjd 2130185029Spjd/* 2131185029Spjd * zfs upgrade 2132185029Spjd * zfs upgrade -v 2133185029Spjd * zfs upgrade [-r] [-V <version>] <-a | filesystem> 2134185029Spjd */ 2135185029Spjdstatic int 2136185029Spjdzfs_do_upgrade(int argc, char **argv) 2137185029Spjd{ 2138185029Spjd boolean_t all = B_FALSE; 2139185029Spjd boolean_t showversions = B_FALSE; 2140231144Smm int ret = 0; 2141185029Spjd upgrade_cbdata_t cb = { 0 }; 2142258362Sjhibbits int c; 2143185029Spjd int flags = ZFS_ITER_ARGS_CAN_BE_PATHS; 2144185029Spjd 2145185029Spjd /* check options */ 2146185029Spjd while ((c = getopt(argc, argv, "rvV:a")) != -1) { 2147185029Spjd switch (c) { 2148185029Spjd case 'r': 2149185029Spjd flags |= ZFS_ITER_RECURSE; 2150185029Spjd break; 2151185029Spjd case 'v': 2152185029Spjd showversions = B_TRUE; 2153185029Spjd break; 2154185029Spjd case 'V': 2155185029Spjd if (zfs_prop_string_to_index(ZFS_PROP_VERSION, 2156185029Spjd optarg, &cb.cb_version) != 0) { 2157185029Spjd (void) fprintf(stderr, 2158185029Spjd gettext("invalid version %s\n"), optarg); 2159185029Spjd usage(B_FALSE); 2160185029Spjd } 2161185029Spjd break; 2162185029Spjd case 'a': 2163185029Spjd all = B_TRUE; 2164185029Spjd break; 2165185029Spjd case '?': 2166185029Spjd default: 2167185029Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2168185029Spjd optopt); 2169185029Spjd usage(B_FALSE); 2170185029Spjd } 2171185029Spjd } 2172185029Spjd 2173185029Spjd argc -= optind; 2174185029Spjd argv += optind; 2175185029Spjd 2176185029Spjd if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version)) 2177185029Spjd usage(B_FALSE); 2178185029Spjd if (showversions && (flags & ZFS_ITER_RECURSE || all || 2179185029Spjd cb.cb_version || argc)) 2180185029Spjd usage(B_FALSE); 2181185029Spjd if ((all || argc) && (showversions)) 2182185029Spjd usage(B_FALSE); 2183185029Spjd if (all && argc) 2184185029Spjd usage(B_FALSE); 2185185029Spjd 2186185029Spjd if (showversions) { 2187185029Spjd /* Show info on available versions. */ 2188185029Spjd (void) printf(gettext("The following filesystem versions are " 2189185029Spjd "supported:\n\n")); 2190185029Spjd (void) printf(gettext("VER DESCRIPTION\n")); 2191185029Spjd (void) printf("--- -----------------------------------------" 2192185029Spjd "---------------\n"); 2193185029Spjd (void) printf(gettext(" 1 Initial ZFS filesystem version\n")); 2194185029Spjd (void) printf(gettext(" 2 Enhanced directory entries\n")); 2195228103Smm (void) printf(gettext(" 3 Case insensitive and filesystem " 2196228103Smm "user identifier (FUID)\n")); 2197209962Smm (void) printf(gettext(" 4 userquota, groupquota " 2198209962Smm "properties\n")); 2199219089Spjd (void) printf(gettext(" 5 System attributes\n")); 2200185029Spjd (void) printf(gettext("\nFor more information on a particular " 2201219089Spjd "version, including supported releases,\n")); 2202219089Spjd (void) printf("see the ZFS Administration Guide.\n\n"); 2203185029Spjd ret = 0; 2204185029Spjd } else if (argc || all) { 2205185029Spjd /* Upgrade filesystems */ 2206185029Spjd if (cb.cb_version == 0) 2207185029Spjd cb.cb_version = ZPL_VERSION; 2208185029Spjd ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM, 2209205199Sdelphij NULL, NULL, 0, upgrade_set_callback, &cb); 2210185029Spjd (void) printf(gettext("%llu filesystems upgraded\n"), 2211185029Spjd cb.cb_numupgraded); 2212185029Spjd if (cb.cb_numsamegraded) { 2213185029Spjd (void) printf(gettext("%llu filesystems already at " 2214185029Spjd "this version\n"), 2215185029Spjd cb.cb_numsamegraded); 2216185029Spjd } 2217185029Spjd if (cb.cb_numfailed != 0) 2218185029Spjd ret = 1; 2219185029Spjd } else { 2220332542Smav /* List old-version filesystems */ 2221185029Spjd boolean_t found; 2222185029Spjd (void) printf(gettext("This system is currently running " 2223185029Spjd "ZFS filesystem version %llu.\n\n"), ZPL_VERSION); 2224185029Spjd 2225185029Spjd flags |= ZFS_ITER_RECURSE; 2226185029Spjd ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM, 2227205199Sdelphij NULL, NULL, 0, upgrade_list_callback, &cb); 2228185029Spjd 2229185029Spjd found = cb.cb_foundone; 2230185029Spjd cb.cb_foundone = B_FALSE; 2231185029Spjd cb.cb_newer = B_TRUE; 2232185029Spjd 2233185029Spjd ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM, 2234205199Sdelphij NULL, NULL, 0, upgrade_list_callback, &cb); 2235185029Spjd 2236185029Spjd if (!cb.cb_foundone && !found) { 2237185029Spjd (void) printf(gettext("All filesystems are " 2238185029Spjd "formatted with the current version.\n")); 2239185029Spjd } 2240185029Spjd } 2241185029Spjd 2242168404Spjd return (ret); 2243168404Spjd} 2244168404Spjd 2245240415Smm/* 2246240415Smm * zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...] 2247240415Smm * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot 2248240415Smm * zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...] 2249240415Smm * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot 2250240415Smm * 2251240415Smm * -H Scripted mode; elide headers and separate columns by tabs. 2252240415Smm * -i Translate SID to POSIX ID. 2253240415Smm * -n Print numeric ID instead of user/group name. 2254240415Smm * -o Control which fields to display. 2255259850Sdelphij * -p Use exact (parsable) numeric output. 2256240415Smm * -s Specify sort columns, descending order. 2257240415Smm * -S Specify sort columns, ascending order. 2258240415Smm * -t Control which object types to display. 2259240415Smm * 2260240415Smm * Displays space consumed by, and quotas on, each user in the specified 2261240415Smm * filesystem or snapshot. 2262240415Smm */ 2263219089Spjd 2264240415Smm/* us_field_types, us_field_hdr and us_field_names should be kept in sync */ 2265240415Smmenum us_field_types { 2266240415Smm USFIELD_TYPE, 2267240415Smm USFIELD_NAME, 2268240415Smm USFIELD_USED, 2269240415Smm USFIELD_QUOTA 2270240415Smm}; 2271240415Smmstatic char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA" }; 2272240415Smmstatic char *us_field_names[] = { "type", "name", "used", "quota" }; 2273240415Smm#define USFIELD_LAST (sizeof (us_field_names) / sizeof (char *)) 2274219089Spjd 2275240415Smm#define USTYPE_PSX_GRP (1 << 0) 2276240415Smm#define USTYPE_PSX_USR (1 << 1) 2277240415Smm#define USTYPE_SMB_GRP (1 << 2) 2278240415Smm#define USTYPE_SMB_USR (1 << 3) 2279240415Smm#define USTYPE_ALL \ 2280240415Smm (USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR) 2281219089Spjd 2282240415Smmstatic int us_type_bits[] = { 2283240415Smm USTYPE_PSX_GRP, 2284240415Smm USTYPE_PSX_USR, 2285240415Smm USTYPE_SMB_GRP, 2286240415Smm USTYPE_SMB_USR, 2287240415Smm USTYPE_ALL 2288240415Smm}; 2289260156Sdelphijstatic char *us_type_names[] = { "posixgroup", "posixuser", "smbgroup", 2290240415Smm "smbuser", "all" }; 2291219089Spjd 2292219089Spjdtypedef struct us_node { 2293219089Spjd nvlist_t *usn_nvl; 2294219089Spjd uu_avl_node_t usn_avlnode; 2295219089Spjd uu_list_node_t usn_listnode; 2296219089Spjd} us_node_t; 2297219089Spjd 2298219089Spjdtypedef struct us_cbdata { 2299240415Smm nvlist_t **cb_nvlp; 2300240415Smm uu_avl_pool_t *cb_avl_pool; 2301240415Smm uu_avl_t *cb_avl; 2302240415Smm boolean_t cb_numname; 2303240415Smm boolean_t cb_nicenum; 2304240415Smm boolean_t cb_sid2posix; 2305240415Smm zfs_userquota_prop_t cb_prop; 2306240415Smm zfs_sort_column_t *cb_sortcol; 2307240415Smm size_t cb_width[USFIELD_LAST]; 2308219089Spjd} us_cbdata_t; 2309219089Spjd 2310240415Smmstatic boolean_t us_populated = B_FALSE; 2311240415Smm 2312219089Spjdtypedef struct { 2313219089Spjd zfs_sort_column_t *si_sortcol; 2314240415Smm boolean_t si_numname; 2315219089Spjd} us_sort_info_t; 2316219089Spjd 2317219089Spjdstatic int 2318240415Smmus_field_index(char *field) 2319240415Smm{ 2320240415Smm int i; 2321240415Smm 2322240415Smm for (i = 0; i < USFIELD_LAST; i++) { 2323240415Smm if (strcmp(field, us_field_names[i]) == 0) 2324240415Smm return (i); 2325240415Smm } 2326240415Smm 2327240415Smm return (-1); 2328240415Smm} 2329240415Smm 2330240415Smmstatic int 2331219089Spjdus_compare(const void *larg, const void *rarg, void *unused) 2332219089Spjd{ 2333219089Spjd const us_node_t *l = larg; 2334219089Spjd const us_node_t *r = rarg; 2335219089Spjd us_sort_info_t *si = (us_sort_info_t *)unused; 2336219089Spjd zfs_sort_column_t *sortcol = si->si_sortcol; 2337240415Smm boolean_t numname = si->si_numname; 2338219089Spjd nvlist_t *lnvl = l->usn_nvl; 2339219089Spjd nvlist_t *rnvl = r->usn_nvl; 2340240415Smm int rc = 0; 2341240415Smm boolean_t lvb, rvb; 2342219089Spjd 2343219089Spjd for (; sortcol != NULL; sortcol = sortcol->sc_next) { 2344219089Spjd char *lvstr = ""; 2345219089Spjd char *rvstr = ""; 2346219089Spjd uint32_t lv32 = 0; 2347219089Spjd uint32_t rv32 = 0; 2348219089Spjd uint64_t lv64 = 0; 2349219089Spjd uint64_t rv64 = 0; 2350219089Spjd zfs_prop_t prop = sortcol->sc_prop; 2351219089Spjd const char *propname = NULL; 2352219089Spjd boolean_t reverse = sortcol->sc_reverse; 2353219089Spjd 2354219089Spjd switch (prop) { 2355219089Spjd case ZFS_PROP_TYPE: 2356219089Spjd propname = "type"; 2357219089Spjd (void) nvlist_lookup_uint32(lnvl, propname, &lv32); 2358219089Spjd (void) nvlist_lookup_uint32(rnvl, propname, &rv32); 2359219089Spjd if (rv32 != lv32) 2360240415Smm rc = (rv32 < lv32) ? 1 : -1; 2361219089Spjd break; 2362219089Spjd case ZFS_PROP_NAME: 2363219089Spjd propname = "name"; 2364240415Smm if (numname) { 2365348725Sallanjudecompare_nums: 2366240415Smm (void) nvlist_lookup_uint64(lnvl, propname, 2367240415Smm &lv64); 2368240415Smm (void) nvlist_lookup_uint64(rnvl, propname, 2369240415Smm &rv64); 2370240415Smm if (rv64 != lv64) 2371240415Smm rc = (rv64 < lv64) ? 1 : -1; 2372219089Spjd } else { 2373348725Sallanjude if ((nvlist_lookup_string(lnvl, propname, 2374348725Sallanjude &lvstr) == ENOENT) || 2375348725Sallanjude (nvlist_lookup_string(rnvl, propname, 2376348725Sallanjude &rvstr) == ENOENT)) { 2377348725Sallanjude goto compare_nums; 2378348725Sallanjude } 2379219089Spjd rc = strcmp(lvstr, rvstr); 2380219089Spjd } 2381219089Spjd break; 2382219089Spjd case ZFS_PROP_USED: 2383219089Spjd case ZFS_PROP_QUOTA: 2384240415Smm if (!us_populated) 2385240415Smm break; 2386240415Smm if (prop == ZFS_PROP_USED) 2387219089Spjd propname = "used"; 2388219089Spjd else 2389219089Spjd propname = "quota"; 2390219089Spjd (void) nvlist_lookup_uint64(lnvl, propname, &lv64); 2391219089Spjd (void) nvlist_lookup_uint64(rnvl, propname, &rv64); 2392219089Spjd if (rv64 != lv64) 2393240415Smm rc = (rv64 < lv64) ? 1 : -1; 2394240415Smm break; 2395296535Smav 2396296535Smav default: 2397296535Smav break; 2398219089Spjd } 2399219089Spjd 2400240415Smm if (rc != 0) { 2401219089Spjd if (rc < 0) 2402219089Spjd return (reverse ? 1 : -1); 2403219089Spjd else 2404219089Spjd return (reverse ? -1 : 1); 2405240415Smm } 2406219089Spjd } 2407219089Spjd 2408240415Smm /* 2409240415Smm * If entries still seem to be the same, check if they are of the same 2410240415Smm * type (smbentity is added only if we are doing SID to POSIX ID 2411240415Smm * translation where we can have duplicate type/name combinations). 2412240415Smm */ 2413240415Smm if (nvlist_lookup_boolean_value(lnvl, "smbentity", &lvb) == 0 && 2414240415Smm nvlist_lookup_boolean_value(rnvl, "smbentity", &rvb) == 0 && 2415240415Smm lvb != rvb) 2416240415Smm return (lvb < rvb ? -1 : 1); 2417240415Smm 2418240415Smm return (0); 2419219089Spjd} 2420219089Spjd 2421219089Spjdstatic inline const char * 2422219089Spjdus_type2str(unsigned field_type) 2423219089Spjd{ 2424219089Spjd switch (field_type) { 2425219089Spjd case USTYPE_PSX_USR: 2426219089Spjd return ("POSIX User"); 2427219089Spjd case USTYPE_PSX_GRP: 2428219089Spjd return ("POSIX Group"); 2429219089Spjd case USTYPE_SMB_USR: 2430219089Spjd return ("SMB User"); 2431219089Spjd case USTYPE_SMB_GRP: 2432219089Spjd return ("SMB Group"); 2433219089Spjd default: 2434219089Spjd return ("Undefined"); 2435219089Spjd } 2436219089Spjd} 2437219089Spjd 2438209962Smmstatic int 2439209962Smmuserspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space) 2440209962Smm{ 2441219089Spjd us_cbdata_t *cb = (us_cbdata_t *)arg; 2442219089Spjd zfs_userquota_prop_t prop = cb->cb_prop; 2443209962Smm char *name = NULL; 2444219089Spjd char *propname; 2445209962Smm char sizebuf[32]; 2446219089Spjd us_node_t *node; 2447219089Spjd uu_avl_pool_t *avl_pool = cb->cb_avl_pool; 2448219089Spjd uu_avl_t *avl = cb->cb_avl; 2449219089Spjd uu_avl_index_t idx; 2450219089Spjd nvlist_t *props; 2451219089Spjd us_node_t *n; 2452219089Spjd zfs_sort_column_t *sortcol = cb->cb_sortcol; 2453296535Smav unsigned type = 0; 2454219089Spjd const char *typestr; 2455219089Spjd size_t namelen; 2456219089Spjd size_t typelen; 2457219089Spjd size_t sizelen; 2458240415Smm int typeidx, nameidx, sizeidx; 2459219089Spjd us_sort_info_t sortinfo = { sortcol, cb->cb_numname }; 2460240415Smm boolean_t smbentity = B_FALSE; 2461209962Smm 2462240415Smm if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 2463240415Smm nomem(); 2464240415Smm node = safe_malloc(sizeof (us_node_t)); 2465240415Smm uu_avl_node_init(node, &node->usn_avlnode, avl_pool); 2466240415Smm node->usn_nvl = props; 2467240415Smm 2468240415Smm if (domain != NULL && domain[0] != '\0') { 2469240415Smm /* SMB */ 2470307108Smav char sid[MAXNAMELEN + 32]; 2471219089Spjd uid_t id; 2472277300Ssmh#ifdef illumos 2473240415Smm int err; 2474275579Sdelphij int flag = IDMAP_REQ_FLG_USE_CACHE; 2475219089Spjd#endif 2476219089Spjd 2477240415Smm smbentity = B_TRUE; 2478240415Smm 2479219089Spjd (void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid); 2480240415Smm 2481219089Spjd if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) { 2482219089Spjd type = USTYPE_SMB_GRP; 2483277300Ssmh#ifdef illumos 2484219089Spjd err = sid_to_id(sid, B_FALSE, &id); 2485219089Spjd#endif 2486219089Spjd } else { 2487219089Spjd type = USTYPE_SMB_USR; 2488277300Ssmh#ifdef illumos 2489219089Spjd err = sid_to_id(sid, B_TRUE, &id); 2490219089Spjd#endif 2491219089Spjd } 2492219089Spjd 2493277300Ssmh#ifdef illumos 2494219089Spjd if (err == 0) { 2495219089Spjd rid = id; 2496240415Smm if (!cb->cb_sid2posix) { 2497275579Sdelphij if (type == USTYPE_SMB_USR) { 2498275579Sdelphij (void) idmap_getwinnamebyuid(rid, flag, 2499275579Sdelphij &name, NULL); 2500275579Sdelphij } else { 2501275579Sdelphij (void) idmap_getwinnamebygid(rid, flag, 2502275579Sdelphij &name, NULL); 2503275579Sdelphij } 2504240415Smm if (name == NULL) 2505240415Smm name = sid; 2506219089Spjd } 2507219089Spjd } 2508219089Spjd#endif 2509209962Smm } 2510209962Smm 2511240415Smm if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') { 2512240415Smm /* POSIX or -i */ 2513240415Smm if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) { 2514240415Smm type = USTYPE_PSX_GRP; 2515240415Smm if (!cb->cb_numname) { 2516240415Smm struct group *g; 2517209962Smm 2518240415Smm if ((g = getgrgid(rid)) != NULL) 2519240415Smm name = g->gr_name; 2520240415Smm } 2521240415Smm } else { 2522240415Smm type = USTYPE_PSX_USR; 2523240415Smm if (!cb->cb_numname) { 2524240415Smm struct passwd *p; 2525209962Smm 2526240415Smm if ((p = getpwuid(rid)) != NULL) 2527240415Smm name = p->pw_name; 2528240415Smm } 2529240415Smm } 2530240415Smm } 2531219089Spjd 2532240415Smm /* 2533240415Smm * Make sure that the type/name combination is unique when doing 2534240415Smm * SID to POSIX ID translation (hence changing the type from SMB to 2535240415Smm * POSIX). 2536240415Smm */ 2537240415Smm if (cb->cb_sid2posix && 2538240415Smm nvlist_add_boolean_value(props, "smbentity", smbentity) != 0) 2539240415Smm nomem(); 2540219089Spjd 2541240415Smm /* Calculate/update width of TYPE field */ 2542240415Smm typestr = us_type2str(type); 2543240415Smm typelen = strlen(gettext(typestr)); 2544240415Smm typeidx = us_field_index("type"); 2545240415Smm if (typelen > cb->cb_width[typeidx]) 2546240415Smm cb->cb_width[typeidx] = typelen; 2547219089Spjd if (nvlist_add_uint32(props, "type", type) != 0) 2548219089Spjd nomem(); 2549209962Smm 2550240415Smm /* Calculate/update width of NAME field */ 2551240415Smm if ((cb->cb_numname && cb->cb_sid2posix) || name == NULL) { 2552240415Smm if (nvlist_add_uint64(props, "name", rid) != 0) 2553219089Spjd nomem(); 2554240415Smm namelen = snprintf(NULL, 0, "%u", rid); 2555219089Spjd } else { 2556219089Spjd if (nvlist_add_string(props, "name", name) != 0) 2557219089Spjd nomem(); 2558219089Spjd namelen = strlen(name); 2559219089Spjd } 2560240415Smm nameidx = us_field_index("name"); 2561240415Smm if (namelen > cb->cb_width[nameidx]) 2562240415Smm cb->cb_width[nameidx] = namelen; 2563219089Spjd 2564240415Smm /* 2565240415Smm * Check if this type/name combination is in the list and update it; 2566240415Smm * otherwise add new node to the list. 2567240415Smm */ 2568240415Smm if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) { 2569240415Smm uu_avl_insert(avl, node, idx); 2570219089Spjd } else { 2571219089Spjd nvlist_free(props); 2572219089Spjd free(node); 2573219089Spjd node = n; 2574219089Spjd props = node->usn_nvl; 2575219089Spjd } 2576219089Spjd 2577240415Smm /* Calculate/update width of USED/QUOTA fields */ 2578240415Smm if (cb->cb_nicenum) 2579240415Smm zfs_nicenum(space, sizebuf, sizeof (sizebuf)); 2580240415Smm else 2581240415Smm (void) snprintf(sizebuf, sizeof (sizebuf), "%llu", space); 2582240415Smm sizelen = strlen(sizebuf); 2583240415Smm if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED) { 2584240415Smm propname = "used"; 2585240415Smm if (!nvlist_exists(props, "quota")) 2586240415Smm (void) nvlist_add_uint64(props, "quota", 0); 2587240415Smm } else { 2588240415Smm propname = "quota"; 2589240415Smm if (!nvlist_exists(props, "used")) 2590240415Smm (void) nvlist_add_uint64(props, "used", 0); 2591240415Smm } 2592240415Smm sizeidx = us_field_index(propname); 2593240415Smm if (sizelen > cb->cb_width[sizeidx]) 2594240415Smm cb->cb_width[sizeidx] = sizelen; 2595240415Smm 2596219089Spjd if (nvlist_add_uint64(props, propname, space) != 0) 2597219089Spjd nomem(); 2598219089Spjd 2599209962Smm return (0); 2600209962Smm} 2601209962Smm 2602219089Spjdstatic void 2603240415Smmprint_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types, 2604240415Smm size_t *width, us_node_t *node) 2605219089Spjd{ 2606219089Spjd nvlist_t *nvl = node->usn_nvl; 2607307108Smav char valstr[MAXNAMELEN]; 2608219089Spjd boolean_t first = B_TRUE; 2609240415Smm int cfield = 0; 2610240415Smm int field; 2611240415Smm uint32_t ustype; 2612219089Spjd 2613240415Smm /* Check type */ 2614240415Smm (void) nvlist_lookup_uint32(nvl, "type", &ustype); 2615240415Smm if (!(ustype & types)) 2616240415Smm return; 2617219089Spjd 2618240415Smm while ((field = fields[cfield]) != USFIELD_LAST) { 2619240415Smm nvpair_t *nvp = NULL; 2620240415Smm data_type_t type; 2621240415Smm uint32_t val32; 2622240415Smm uint64_t val64; 2623219089Spjd char *strval = NULL; 2624240415Smm 2625240415Smm while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 2626240415Smm if (strcmp(nvpair_name(nvp), 2627240415Smm us_field_names[field]) == 0) 2628219089Spjd break; 2629219089Spjd } 2630219089Spjd 2631240415Smm type = nvpair_type(nvp); 2632219089Spjd switch (type) { 2633219089Spjd case DATA_TYPE_UINT32: 2634219089Spjd (void) nvpair_value_uint32(nvp, &val32); 2635219089Spjd break; 2636219089Spjd case DATA_TYPE_UINT64: 2637219089Spjd (void) nvpair_value_uint64(nvp, &val64); 2638219089Spjd break; 2639219089Spjd case DATA_TYPE_STRING: 2640219089Spjd (void) nvpair_value_string(nvp, &strval); 2641219089Spjd break; 2642219089Spjd default: 2643240415Smm (void) fprintf(stderr, "invalid data type\n"); 2644219089Spjd } 2645219089Spjd 2646219089Spjd switch (field) { 2647219089Spjd case USFIELD_TYPE: 2648219089Spjd strval = (char *)us_type2str(val32); 2649219089Spjd break; 2650219089Spjd case USFIELD_NAME: 2651219089Spjd if (type == DATA_TYPE_UINT64) { 2652219089Spjd (void) sprintf(valstr, "%llu", val64); 2653219089Spjd strval = valstr; 2654219089Spjd } 2655219089Spjd break; 2656219089Spjd case USFIELD_USED: 2657219089Spjd case USFIELD_QUOTA: 2658219089Spjd if (type == DATA_TYPE_UINT64) { 2659240415Smm if (parsable) { 2660219089Spjd (void) sprintf(valstr, "%llu", val64); 2661240415Smm } else { 2662219089Spjd zfs_nicenum(val64, valstr, 2663219089Spjd sizeof (valstr)); 2664240415Smm } 2665240415Smm if (field == USFIELD_QUOTA && 2666240415Smm strcmp(valstr, "0") == 0) 2667240415Smm strval = "none"; 2668240415Smm else 2669240415Smm strval = valstr; 2670219089Spjd } 2671219089Spjd break; 2672219089Spjd } 2673219089Spjd 2674240415Smm if (!first) { 2675240415Smm if (scripted) 2676240415Smm (void) printf("\t"); 2677219089Spjd else 2678240415Smm (void) printf(" "); 2679219089Spjd } 2680240415Smm if (scripted) 2681240415Smm (void) printf("%s", strval); 2682240415Smm else if (field == USFIELD_TYPE || field == USFIELD_NAME) 2683240415Smm (void) printf("%-*s", width[field], strval); 2684240415Smm else 2685240415Smm (void) printf("%*s", width[field], strval); 2686219089Spjd 2687219089Spjd first = B_FALSE; 2688240415Smm cfield++; 2689219089Spjd } 2690219089Spjd 2691219089Spjd (void) printf("\n"); 2692219089Spjd} 2693219089Spjd 2694219089Spjdstatic void 2695240415Smmprint_us(boolean_t scripted, boolean_t parsable, int *fields, int types, 2696240415Smm size_t *width, boolean_t rmnode, uu_avl_t *avl) 2697219089Spjd{ 2698219089Spjd us_node_t *node; 2699219089Spjd const char *col; 2700240415Smm int cfield = 0; 2701240415Smm int field; 2702219089Spjd 2703219089Spjd if (!scripted) { 2704219089Spjd boolean_t first = B_TRUE; 2705219089Spjd 2706240415Smm while ((field = fields[cfield]) != USFIELD_LAST) { 2707240415Smm col = gettext(us_field_hdr[field]); 2708240415Smm if (field == USFIELD_TYPE || field == USFIELD_NAME) { 2709240415Smm (void) printf(first ? "%-*s" : " %-*s", 2710240415Smm width[field], col); 2711240415Smm } else { 2712240415Smm (void) printf(first ? "%*s" : " %*s", 2713240415Smm width[field], col); 2714240415Smm } 2715219089Spjd first = B_FALSE; 2716240415Smm cfield++; 2717219089Spjd } 2718219089Spjd (void) printf("\n"); 2719219089Spjd } 2720219089Spjd 2721240415Smm for (node = uu_avl_first(avl); node; node = uu_avl_next(avl, node)) { 2722240415Smm print_us_node(scripted, parsable, fields, types, width, node); 2723219089Spjd if (rmnode) 2724219089Spjd nvlist_free(node->usn_nvl); 2725219089Spjd } 2726219089Spjd} 2727219089Spjd 2728219089Spjdstatic int 2729209962Smmzfs_do_userspace(int argc, char **argv) 2730209962Smm{ 2731209962Smm zfs_handle_t *zhp; 2732209962Smm zfs_userquota_prop_t p; 2733219089Spjd 2734219089Spjd uu_avl_pool_t *avl_pool; 2735219089Spjd uu_avl_t *avl_tree; 2736219089Spjd uu_avl_walk_t *walk; 2737240415Smm char *delim; 2738240415Smm char deffields[] = "type,name,used,quota"; 2739240415Smm char *ofield = NULL; 2740240415Smm char *tfield = NULL; 2741240415Smm int cfield = 0; 2742240415Smm int fields[256]; 2743240415Smm int i; 2744219089Spjd boolean_t scripted = B_FALSE; 2745219089Spjd boolean_t prtnum = B_FALSE; 2746240415Smm boolean_t parsable = B_FALSE; 2747219089Spjd boolean_t sid2posix = B_FALSE; 2748240415Smm int ret = 0; 2749219089Spjd int c; 2750219089Spjd zfs_sort_column_t *sortcol = NULL; 2751240415Smm int types = USTYPE_PSX_USR | USTYPE_SMB_USR; 2752219089Spjd us_cbdata_t cb; 2753219089Spjd us_node_t *node; 2754240415Smm us_node_t *rmnode; 2755240415Smm uu_list_pool_t *listpool; 2756240415Smm uu_list_t *list; 2757240415Smm uu_avl_index_t idx = 0; 2758240415Smm uu_list_index_t idx2 = 0; 2759209962Smm 2760219089Spjd if (argc < 2) 2761219089Spjd usage(B_FALSE); 2762209962Smm 2763240415Smm if (strcmp(argv[0], "groupspace") == 0) 2764240415Smm /* Toggle default group types */ 2765219089Spjd types = USTYPE_PSX_GRP | USTYPE_SMB_GRP; 2766209962Smm 2767219089Spjd while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) { 2768219089Spjd switch (c) { 2769219089Spjd case 'n': 2770219089Spjd prtnum = B_TRUE; 2771219089Spjd break; 2772219089Spjd case 'H': 2773219089Spjd scripted = B_TRUE; 2774219089Spjd break; 2775219089Spjd case 'p': 2776240415Smm parsable = B_TRUE; 2777219089Spjd break; 2778219089Spjd case 'o': 2779240415Smm ofield = optarg; 2780219089Spjd break; 2781219089Spjd case 's': 2782219089Spjd case 'S': 2783219089Spjd if (zfs_add_sort_column(&sortcol, optarg, 2784240415Smm c == 's' ? B_FALSE : B_TRUE) != 0) { 2785219089Spjd (void) fprintf(stderr, 2786240415Smm gettext("invalid field '%s'\n"), optarg); 2787219089Spjd usage(B_FALSE); 2788219089Spjd } 2789219089Spjd break; 2790219089Spjd case 't': 2791240415Smm tfield = optarg; 2792219089Spjd break; 2793219089Spjd case 'i': 2794219089Spjd sid2posix = B_TRUE; 2795219089Spjd break; 2796219089Spjd case ':': 2797219089Spjd (void) fprintf(stderr, gettext("missing argument for " 2798219089Spjd "'%c' option\n"), optopt); 2799219089Spjd usage(B_FALSE); 2800219089Spjd break; 2801219089Spjd case '?': 2802219089Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 2803219089Spjd optopt); 2804219089Spjd usage(B_FALSE); 2805219089Spjd } 2806219089Spjd } 2807219089Spjd 2808219089Spjd argc -= optind; 2809219089Spjd argv += optind; 2810219089Spjd 2811240415Smm if (argc < 1) { 2812240415Smm (void) fprintf(stderr, gettext("missing dataset name\n")); 2813240415Smm usage(B_FALSE); 2814240415Smm } 2815240415Smm if (argc > 1) { 2816240415Smm (void) fprintf(stderr, gettext("too many arguments\n")); 2817240415Smm usage(B_FALSE); 2818240415Smm } 2819240415Smm 2820240415Smm /* Use default output fields if not specified using -o */ 2821240415Smm if (ofield == NULL) 2822240415Smm ofield = deffields; 2823240415Smm do { 2824240415Smm if ((delim = strchr(ofield, ',')) != NULL) 2825240415Smm *delim = '\0'; 2826240415Smm if ((fields[cfield++] = us_field_index(ofield)) == -1) { 2827240415Smm (void) fprintf(stderr, gettext("invalid type '%s' " 2828240415Smm "for -o option\n"), ofield); 2829240415Smm return (-1); 2830240415Smm } 2831240415Smm if (delim != NULL) 2832240415Smm ofield = delim + 1; 2833240415Smm } while (delim != NULL); 2834240415Smm fields[cfield] = USFIELD_LAST; 2835240415Smm 2836240415Smm /* Override output types (-t option) */ 2837240415Smm if (tfield != NULL) { 2838240415Smm types = 0; 2839240415Smm 2840240415Smm do { 2841240415Smm boolean_t found = B_FALSE; 2842240415Smm 2843240415Smm if ((delim = strchr(tfield, ',')) != NULL) 2844240415Smm *delim = '\0'; 2845240415Smm for (i = 0; i < sizeof (us_type_bits) / sizeof (int); 2846240415Smm i++) { 2847240415Smm if (strcmp(tfield, us_type_names[i]) == 0) { 2848240415Smm found = B_TRUE; 2849240415Smm types |= us_type_bits[i]; 2850240415Smm break; 2851240415Smm } 2852219089Spjd } 2853240415Smm if (!found) { 2854240415Smm (void) fprintf(stderr, gettext("invalid type " 2855240415Smm "'%s' for -t option\n"), tfield); 2856240415Smm return (-1); 2857240415Smm } 2858240415Smm if (delim != NULL) 2859240415Smm tfield = delim + 1; 2860240415Smm } while (delim != NULL); 2861219089Spjd } 2862219089Spjd 2863240415Smm if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL) 2864209962Smm return (1); 2865209962Smm 2866219089Spjd if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t), 2867240415Smm offsetof(us_node_t, usn_avlnode), us_compare, UU_DEFAULT)) == NULL) 2868219089Spjd nomem(); 2869219089Spjd if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) 2870219089Spjd nomem(); 2871209962Smm 2872240415Smm /* Always add default sorting columns */ 2873240415Smm (void) zfs_add_sort_column(&sortcol, "type", B_FALSE); 2874240415Smm (void) zfs_add_sort_column(&sortcol, "name", B_FALSE); 2875240415Smm 2876240415Smm cb.cb_sortcol = sortcol; 2877219089Spjd cb.cb_numname = prtnum; 2878240415Smm cb.cb_nicenum = !parsable; 2879219089Spjd cb.cb_avl_pool = avl_pool; 2880219089Spjd cb.cb_avl = avl_tree; 2881219089Spjd cb.cb_sid2posix = sid2posix; 2882219089Spjd 2883240415Smm for (i = 0; i < USFIELD_LAST; i++) 2884240415Smm cb.cb_width[i] = strlen(gettext(us_field_hdr[i])); 2885240415Smm 2886209962Smm for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) { 2887240415Smm if (((p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA) && 2888240415Smm !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) || 2889240415Smm ((p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) && 2890240415Smm !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP)))) 2891219089Spjd continue; 2892219089Spjd cb.cb_prop = p; 2893240415Smm if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0) 2894240415Smm return (ret); 2895209962Smm } 2896219089Spjd 2897240415Smm /* Sort the list */ 2898240415Smm if ((node = uu_avl_first(avl_tree)) == NULL) 2899240415Smm return (0); 2900219089Spjd 2901240415Smm us_populated = B_TRUE; 2902219089Spjd 2903240415Smm listpool = uu_list_pool_create("tmplist", sizeof (us_node_t), 2904240415Smm offsetof(us_node_t, usn_listnode), NULL, UU_DEFAULT); 2905240415Smm list = uu_list_create(listpool, NULL, UU_DEFAULT); 2906240415Smm uu_list_node_init(node, &node->usn_listnode, listpool); 2907219089Spjd 2908240415Smm while (node != NULL) { 2909240415Smm rmnode = node; 2910240415Smm node = uu_avl_next(avl_tree, node); 2911240415Smm uu_avl_remove(avl_tree, rmnode); 2912240415Smm if (uu_list_find(list, rmnode, NULL, &idx2) == NULL) 2913240415Smm uu_list_insert(list, rmnode, idx2); 2914219089Spjd } 2915219089Spjd 2916240415Smm for (node = uu_list_first(list); node != NULL; 2917240415Smm node = uu_list_next(list, node)) { 2918240415Smm us_sort_info_t sortinfo = { sortcol, cb.cb_numname }; 2919219089Spjd 2920240415Smm if (uu_avl_find(avl_tree, node, &sortinfo, &idx) == NULL) 2921240415Smm uu_avl_insert(avl_tree, node, idx); 2922240415Smm } 2923219089Spjd 2924240415Smm uu_list_destroy(list); 2925240415Smm uu_list_pool_destroy(listpool); 2926240415Smm 2927240415Smm /* Print and free node nvlist memory */ 2928240415Smm print_us(scripted, parsable, fields, types, cb.cb_width, B_TRUE, 2929240415Smm cb.cb_avl); 2930240415Smm 2931240415Smm zfs_free_sort_columns(sortcol); 2932240415Smm 2933240415Smm /* Clean up the AVL tree */ 2934219089Spjd if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) 2935219089Spjd nomem(); 2936219089Spjd 2937219089Spjd while ((node = uu_avl_walk_next(walk)) != NULL) { 2938219089Spjd uu_avl_remove(cb.cb_avl, node); 2939219089Spjd free(node); 2940219089Spjd } 2941219089Spjd 2942219089Spjd uu_avl_walk_end(walk); 2943219089Spjd uu_avl_destroy(avl_tree); 2944219089Spjd uu_avl_pool_destroy(avl_pool); 2945219089Spjd 2946240415Smm return (ret); 2947209962Smm} 2948209962Smm 2949209962Smm/* 2950259850Sdelphij * list [-Hp][-r|-d max] [-o property[,...]] [-s property] ... [-S property] ... 2951259850Sdelphij * [-t type[,...]] [filesystem|volume|snapshot] ... 2952168404Spjd * 2953259850Sdelphij * -H Scripted mode; elide headers and separate columns by tabs. 2954259850Sdelphij * -p Display values in parsable (literal) format. 2955259850Sdelphij * -r Recurse over all children. 2956219089Spjd * -d Limit recursion by depth. 2957219089Spjd * -o Control which fields to display. 2958168404Spjd * -s Specify sort columns, descending order. 2959168404Spjd * -S Specify sort columns, ascending order. 2960259850Sdelphij * -t Control which object types to display. 2961168404Spjd * 2962259850Sdelphij * When given no arguments, list all filesystems in the system. 2963168404Spjd * Otherwise, list the specified datasets, optionally recursing down them if 2964168404Spjd * '-r' is specified. 2965168404Spjd */ 2966168404Spjdtypedef struct list_cbdata { 2967168404Spjd boolean_t cb_first; 2968259850Sdelphij boolean_t cb_literal; 2969168404Spjd boolean_t cb_scripted; 2970185029Spjd zprop_list_t *cb_proplist; 2971168404Spjd} list_cbdata_t; 2972168404Spjd 2973168404Spjd/* 2974168404Spjd * Given a list of columns to display, output appropriate headers for each one. 2975168404Spjd */ 2976168404Spjdstatic void 2977259850Sdelphijprint_header(list_cbdata_t *cb) 2978168404Spjd{ 2979259850Sdelphij zprop_list_t *pl = cb->cb_proplist; 2980168404Spjd char headerbuf[ZFS_MAXPROPLEN]; 2981168404Spjd const char *header; 2982168404Spjd int i; 2983168404Spjd boolean_t first = B_TRUE; 2984168404Spjd boolean_t right_justify; 2985168404Spjd 2986168404Spjd for (; pl != NULL; pl = pl->pl_next) { 2987168404Spjd if (!first) { 2988168404Spjd (void) printf(" "); 2989168404Spjd } else { 2990168404Spjd first = B_FALSE; 2991168404Spjd } 2992168404Spjd 2993168404Spjd right_justify = B_FALSE; 2994185029Spjd if (pl->pl_prop != ZPROP_INVAL) { 2995168404Spjd header = zfs_prop_column_name(pl->pl_prop); 2996168404Spjd right_justify = zfs_prop_align_right(pl->pl_prop); 2997168404Spjd } else { 2998168404Spjd for (i = 0; pl->pl_user_prop[i] != '\0'; i++) 2999168404Spjd headerbuf[i] = toupper(pl->pl_user_prop[i]); 3000168404Spjd headerbuf[i] = '\0'; 3001168404Spjd header = headerbuf; 3002168404Spjd } 3003168404Spjd 3004168404Spjd if (pl->pl_next == NULL && !right_justify) 3005168404Spjd (void) printf("%s", header); 3006168404Spjd else if (right_justify) 3007168404Spjd (void) printf("%*s", pl->pl_width, header); 3008168404Spjd else 3009168404Spjd (void) printf("%-*s", pl->pl_width, header); 3010168404Spjd } 3011168404Spjd 3012168404Spjd (void) printf("\n"); 3013168404Spjd} 3014168404Spjd 3015168404Spjd/* 3016168404Spjd * Given a dataset and a list of fields, print out all the properties according 3017168404Spjd * to the described layout. 3018168404Spjd */ 3019168404Spjdstatic void 3020259850Sdelphijprint_dataset(zfs_handle_t *zhp, list_cbdata_t *cb) 3021168404Spjd{ 3022259850Sdelphij zprop_list_t *pl = cb->cb_proplist; 3023168404Spjd boolean_t first = B_TRUE; 3024168404Spjd char property[ZFS_MAXPROPLEN]; 3025168404Spjd nvlist_t *userprops = zfs_get_user_props(zhp); 3026168404Spjd nvlist_t *propval; 3027168404Spjd char *propstr; 3028168404Spjd boolean_t right_justify; 3029168404Spjd 3030168404Spjd for (; pl != NULL; pl = pl->pl_next) { 3031168404Spjd if (!first) { 3032259850Sdelphij if (cb->cb_scripted) 3033168404Spjd (void) printf("\t"); 3034168404Spjd else 3035168404Spjd (void) printf(" "); 3036168404Spjd } else { 3037168404Spjd first = B_FALSE; 3038168404Spjd } 3039168404Spjd 3040230438Spjd if (pl->pl_prop == ZFS_PROP_NAME) { 3041230438Spjd (void) strlcpy(property, zfs_get_name(zhp), 3042307046Smav sizeof (property)); 3043230438Spjd propstr = property; 3044230438Spjd right_justify = zfs_prop_align_right(pl->pl_prop); 3045230438Spjd } else if (pl->pl_prop != ZPROP_INVAL) { 3046168404Spjd if (zfs_prop_get(zhp, pl->pl_prop, property, 3047259850Sdelphij sizeof (property), NULL, NULL, 0, 3048259850Sdelphij cb->cb_literal) != 0) 3049168404Spjd propstr = "-"; 3050168404Spjd else 3051168404Spjd propstr = property; 3052168404Spjd right_justify = zfs_prop_align_right(pl->pl_prop); 3053209962Smm } else if (zfs_prop_userquota(pl->pl_user_prop)) { 3054209962Smm if (zfs_prop_get_userquota(zhp, pl->pl_user_prop, 3055259850Sdelphij property, sizeof (property), cb->cb_literal) != 0) 3056209962Smm propstr = "-"; 3057209962Smm else 3058209962Smm propstr = property; 3059209962Smm right_justify = B_TRUE; 3060228103Smm } else if (zfs_prop_written(pl->pl_user_prop)) { 3061228103Smm if (zfs_prop_get_written(zhp, pl->pl_user_prop, 3062259850Sdelphij property, sizeof (property), cb->cb_literal) != 0) 3063228103Smm propstr = "-"; 3064228103Smm else 3065228103Smm propstr = property; 3066228103Smm right_justify = B_TRUE; 3067168404Spjd } else { 3068168404Spjd if (nvlist_lookup_nvlist(userprops, 3069168404Spjd pl->pl_user_prop, &propval) != 0) 3070168404Spjd propstr = "-"; 3071168404Spjd else 3072168404Spjd verify(nvlist_lookup_string(propval, 3073185029Spjd ZPROP_VALUE, &propstr) == 0); 3074209962Smm right_justify = B_FALSE; 3075168404Spjd } 3076168404Spjd 3077168404Spjd /* 3078168404Spjd * If this is being called in scripted mode, or if this is the 3079168404Spjd * last column and it is left-justified, don't include a width 3080168404Spjd * format specifier. 3081168404Spjd */ 3082259850Sdelphij if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify)) 3083168404Spjd (void) printf("%s", propstr); 3084168404Spjd else if (right_justify) 3085259850Sdelphij (void) printf("%*s", pl->pl_width, propstr); 3086168404Spjd else 3087259850Sdelphij (void) printf("%-*s", pl->pl_width, propstr); 3088168404Spjd } 3089168404Spjd 3090168404Spjd (void) printf("\n"); 3091168404Spjd} 3092168404Spjd 3093168404Spjd/* 3094168404Spjd * Generic callback function to list a dataset or snapshot. 3095168404Spjd */ 3096168404Spjdstatic int 3097168404Spjdlist_callback(zfs_handle_t *zhp, void *data) 3098168404Spjd{ 3099168404Spjd list_cbdata_t *cbp = data; 3100168404Spjd 3101168404Spjd if (cbp->cb_first) { 3102168404Spjd if (!cbp->cb_scripted) 3103259850Sdelphij print_header(cbp); 3104168404Spjd cbp->cb_first = B_FALSE; 3105168404Spjd } 3106168404Spjd 3107259850Sdelphij print_dataset(zhp, cbp); 3108168404Spjd 3109168404Spjd return (0); 3110168404Spjd} 3111168404Spjd 3112168404Spjdstatic int 3113168404Spjdzfs_do_list(int argc, char **argv) 3114168404Spjd{ 3115168404Spjd int c; 3116168404Spjd static char default_fields[] = 3117168404Spjd "name,used,available,referenced,mountpoint"; 3118207627Smm int types = ZFS_TYPE_DATASET; 3119185029Spjd boolean_t types_specified = B_FALSE; 3120168404Spjd char *fields = NULL; 3121168404Spjd list_cbdata_t cb = { 0 }; 3122168404Spjd char *value; 3123205199Sdelphij int limit = 0; 3124231144Smm int ret = 0; 3125168404Spjd zfs_sort_column_t *sortcol = NULL; 3126185029Spjd int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS; 3127168404Spjd 3128168404Spjd /* check options */ 3129259850Sdelphij while ((c = getopt(argc, argv, "HS:d:o:prs:t:")) != -1) { 3130168404Spjd switch (c) { 3131168404Spjd case 'o': 3132168404Spjd fields = optarg; 3133168404Spjd break; 3134259850Sdelphij case 'p': 3135259850Sdelphij cb.cb_literal = B_TRUE; 3136259850Sdelphij flags |= ZFS_ITER_LITERAL_PROPS; 3137259850Sdelphij break; 3138205199Sdelphij case 'd': 3139205199Sdelphij limit = parse_depth(optarg, &flags); 3140205199Sdelphij break; 3141168404Spjd case 'r': 3142185029Spjd flags |= ZFS_ITER_RECURSE; 3143168404Spjd break; 3144168404Spjd case 'H': 3145259850Sdelphij cb.cb_scripted = B_TRUE; 3146168404Spjd break; 3147168404Spjd case 's': 3148168404Spjd if (zfs_add_sort_column(&sortcol, optarg, 3149168404Spjd B_FALSE) != 0) { 3150168404Spjd (void) fprintf(stderr, 3151168404Spjd gettext("invalid property '%s'\n"), optarg); 3152168404Spjd usage(B_FALSE); 3153168404Spjd } 3154168404Spjd break; 3155168404Spjd case 'S': 3156168404Spjd if (zfs_add_sort_column(&sortcol, optarg, 3157168404Spjd B_TRUE) != 0) { 3158168404Spjd (void) fprintf(stderr, 3159168404Spjd gettext("invalid property '%s'\n"), optarg); 3160168404Spjd usage(B_FALSE); 3161168404Spjd } 3162168404Spjd break; 3163168404Spjd case 't': 3164168404Spjd types = 0; 3165185029Spjd types_specified = B_TRUE; 3166185029Spjd flags &= ~ZFS_ITER_PROP_LISTSNAPS; 3167168404Spjd while (*optarg != '\0') { 3168185029Spjd static char *type_subopts[] = { "filesystem", 3169260183Sdelphij "volume", "snapshot", "snap", "bookmark", 3170260183Sdelphij "all", NULL }; 3171185029Spjd 3172168404Spjd switch (getsubopt(&optarg, type_subopts, 3173168404Spjd &value)) { 3174168404Spjd case 0: 3175168404Spjd types |= ZFS_TYPE_FILESYSTEM; 3176168404Spjd break; 3177168404Spjd case 1: 3178168404Spjd types |= ZFS_TYPE_VOLUME; 3179168404Spjd break; 3180168404Spjd case 2: 3181256999Ssmh case 3: 3182168404Spjd types |= ZFS_TYPE_SNAPSHOT; 3183168404Spjd break; 3184256999Ssmh case 4: 3185260183Sdelphij types |= ZFS_TYPE_BOOKMARK; 3186185029Spjd break; 3187260183Sdelphij case 5: 3188260183Sdelphij types = ZFS_TYPE_DATASET | 3189260183Sdelphij ZFS_TYPE_BOOKMARK; 3190260183Sdelphij break; 3191168404Spjd default: 3192168404Spjd (void) fprintf(stderr, 3193168404Spjd gettext("invalid type '%s'\n"), 3194295844Sdim suboptarg); 3195168404Spjd usage(B_FALSE); 3196168404Spjd } 3197168404Spjd } 3198168404Spjd break; 3199168404Spjd case ':': 3200168404Spjd (void) fprintf(stderr, gettext("missing argument for " 3201168404Spjd "'%c' option\n"), optopt); 3202168404Spjd usage(B_FALSE); 3203168404Spjd break; 3204168404Spjd case '?': 3205168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3206168404Spjd optopt); 3207168404Spjd usage(B_FALSE); 3208168404Spjd } 3209168404Spjd } 3210168404Spjd 3211168404Spjd argc -= optind; 3212168404Spjd argv += optind; 3213168404Spjd 3214168404Spjd if (fields == NULL) 3215185029Spjd fields = default_fields; 3216168404Spjd 3217168404Spjd /* 3218230438Spjd * If we are only going to list snapshot names and sort by name, 3219230438Spjd * then we can use faster version. 3220230438Spjd */ 3221230438Spjd if (strcmp(fields, "name") == 0 && zfs_sort_only_by_name(sortcol)) 3222230438Spjd flags |= ZFS_ITER_SIMPLE; 3223230438Spjd 3224230438Spjd /* 3225185029Spjd * If "-o space" and no types were specified, don't display snapshots. 3226185029Spjd */ 3227185029Spjd if (strcmp(fields, "space") == 0 && types_specified == B_FALSE) 3228185029Spjd types &= ~ZFS_TYPE_SNAPSHOT; 3229185029Spjd 3230185029Spjd /* 3231185029Spjd * If the user specifies '-o all', the zprop_get_list() doesn't 3232168404Spjd * normally include the name of the dataset. For 'zfs list', we always 3233168404Spjd * want this property to be first. 3234168404Spjd */ 3235185029Spjd if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET) 3236185029Spjd != 0) 3237168404Spjd usage(B_FALSE); 3238168404Spjd 3239168404Spjd cb.cb_first = B_TRUE; 3240168404Spjd 3241185029Spjd ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist, 3242205199Sdelphij limit, list_callback, &cb); 3243168404Spjd 3244185029Spjd zprop_free_list(cb.cb_proplist); 3245168404Spjd zfs_free_sort_columns(sortcol); 3246168404Spjd 3247185029Spjd if (ret == 0 && cb.cb_first && !cb.cb_scripted) 3248168404Spjd (void) printf(gettext("no datasets available\n")); 3249168404Spjd 3250168404Spjd return (ret); 3251168404Spjd} 3252168404Spjd 3253168404Spjd/* 3254235216Smm * zfs rename [-f] <fs | snap | vol> <fs | snap | vol> 3255235216Smm * zfs rename [-f] -p <fs | vol> <fs | vol> 3256185029Spjd * zfs rename -r <snap> <snap> 3257353759Savg * zfs rename <bmark> <bmark> 3258226705Spjd * zfs rename -u [-p] <fs> <fs> 3259168404Spjd * 3260168404Spjd * Renames the given dataset to another of the same type. 3261185029Spjd * 3262185029Spjd * The '-p' flag creates all the non-existing ancestors of the target first. 3263168404Spjd */ 3264168404Spjd/* ARGSUSED */ 3265168404Spjdstatic int 3266168404Spjdzfs_do_rename(int argc, char **argv) 3267168404Spjd{ 3268168404Spjd zfs_handle_t *zhp; 3269226705Spjd renameflags_t flags = { 0 }; 3270231144Smm int c; 3271231144Smm int ret = 0; 3272231144Smm int types; 3273185029Spjd boolean_t parents = B_FALSE; 3274353759Savg boolean_t bookmarks = B_FALSE; 3275240870Spjd char *snapshot = NULL; 3276168404Spjd 3277168404Spjd /* check options */ 3278235216Smm while ((c = getopt(argc, argv, "fpru")) != -1) { 3279168676Spjd switch (c) { 3280185029Spjd case 'p': 3281185029Spjd parents = B_TRUE; 3282185029Spjd break; 3283168676Spjd case 'r': 3284226705Spjd flags.recurse = B_TRUE; 3285168676Spjd break; 3286226705Spjd case 'u': 3287226705Spjd flags.nounmount = B_TRUE; 3288226705Spjd break; 3289235216Smm case 'f': 3290235216Smm flags.forceunmount = B_TRUE; 3291235216Smm break; 3292168676Spjd case '?': 3293168676Spjd default: 3294168676Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3295168676Spjd optopt); 3296168676Spjd usage(B_FALSE); 3297168676Spjd } 3298168404Spjd } 3299168404Spjd 3300168676Spjd argc -= optind; 3301168676Spjd argv += optind; 3302168676Spjd 3303168404Spjd /* check number of arguments */ 3304168676Spjd if (argc < 1) { 3305168404Spjd (void) fprintf(stderr, gettext("missing source dataset " 3306168404Spjd "argument\n")); 3307168404Spjd usage(B_FALSE); 3308168404Spjd } 3309168676Spjd if (argc < 2) { 3310168404Spjd (void) fprintf(stderr, gettext("missing target dataset " 3311168404Spjd "argument\n")); 3312168404Spjd usage(B_FALSE); 3313168404Spjd } 3314168676Spjd if (argc > 2) { 3315168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 3316168404Spjd usage(B_FALSE); 3317168404Spjd } 3318168404Spjd 3319226705Spjd if (flags.recurse && parents) { 3320185029Spjd (void) fprintf(stderr, gettext("-p and -r options are mutually " 3321185029Spjd "exclusive\n")); 3322185029Spjd usage(B_FALSE); 3323185029Spjd } 3324185029Spjd 3325353759Savg if (flags.recurse && strchr(argv[0], '@') == NULL) { 3326168676Spjd (void) fprintf(stderr, gettext("source dataset for recursive " 3327168676Spjd "rename must be a snapshot\n")); 3328168676Spjd usage(B_FALSE); 3329168676Spjd } 3330168676Spjd 3331226705Spjd if (flags.nounmount && parents) { 3332236013Spjd (void) fprintf(stderr, gettext("-u and -p options are mutually " 3333226705Spjd "exclusive\n")); 3334226705Spjd usage(B_FALSE); 3335226705Spjd } 3336226705Spjd 3337353759Savg if (strchr(argv[0], '#') != NULL) 3338353759Savg bookmarks = B_TRUE; 3339353759Savg 3340353759Savg if (bookmarks && (flags.nounmount || flags.recurse || 3341353759Savg flags.forceunmount || parents)) { 3342353759Savg (void) fprintf(stderr, gettext("options are not supported " 3343353759Savg "for renaming bookmarks\n")); 3344353759Savg usage(B_FALSE); 3345353759Savg } 3346353759Savg 3347226705Spjd if (flags.nounmount) 3348226705Spjd types = ZFS_TYPE_FILESYSTEM; 3349226705Spjd else if (parents) 3350226705Spjd types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME; 3351353759Savg else if (bookmarks) 3352353759Savg types = ZFS_TYPE_BOOKMARK; 3353226705Spjd else 3354226705Spjd types = ZFS_TYPE_DATASET; 3355226705Spjd 3356240870Spjd if (flags.recurse) { 3357240870Spjd /* 3358240870Spjd * When we do recursive rename we are fine when the given 3359240870Spjd * snapshot for the given dataset doesn't exist - it can 3360240870Spjd * still exists below. 3361240870Spjd */ 3362240870Spjd 3363240870Spjd snapshot = strchr(argv[0], '@'); 3364240870Spjd assert(snapshot != NULL); 3365240870Spjd *snapshot = '\0'; 3366240870Spjd snapshot++; 3367240870Spjd } 3368240870Spjd 3369226705Spjd if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) 3370168404Spjd return (1); 3371168404Spjd 3372185029Spjd /* If we were asked and the name looks good, try to create ancestors. */ 3373185029Spjd if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) && 3374185029Spjd zfs_create_ancestors(g_zfs, argv[1]) != 0) { 3375185029Spjd zfs_close(zhp); 3376185029Spjd return (1); 3377185029Spjd } 3378185029Spjd 3379240870Spjd ret = (zfs_rename(zhp, snapshot, argv[1], flags) != 0); 3380168404Spjd 3381168404Spjd zfs_close(zhp); 3382168404Spjd return (ret); 3383168404Spjd} 3384168404Spjd 3385168404Spjd/* 3386168404Spjd * zfs promote <fs> 3387168404Spjd * 3388168404Spjd * Promotes the given clone fs to be the parent 3389168404Spjd */ 3390168404Spjd/* ARGSUSED */ 3391168404Spjdstatic int 3392168404Spjdzfs_do_promote(int argc, char **argv) 3393168404Spjd{ 3394168404Spjd zfs_handle_t *zhp; 3395231144Smm int ret = 0; 3396168404Spjd 3397168404Spjd /* check options */ 3398168404Spjd if (argc > 1 && argv[1][0] == '-') { 3399168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3400168404Spjd argv[1][1]); 3401168404Spjd usage(B_FALSE); 3402168404Spjd } 3403168404Spjd 3404168404Spjd /* check number of arguments */ 3405168404Spjd if (argc < 2) { 3406168404Spjd (void) fprintf(stderr, gettext("missing clone filesystem" 3407168404Spjd " argument\n")); 3408168404Spjd usage(B_FALSE); 3409168404Spjd } 3410168404Spjd if (argc > 2) { 3411168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 3412168404Spjd usage(B_FALSE); 3413168404Spjd } 3414168404Spjd 3415168404Spjd zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 3416168404Spjd if (zhp == NULL) 3417168404Spjd return (1); 3418168404Spjd 3419168404Spjd ret = (zfs_promote(zhp) != 0); 3420168404Spjd 3421168404Spjd 3422168404Spjd zfs_close(zhp); 3423168404Spjd return (ret); 3424168404Spjd} 3425168404Spjd 3426168404Spjd/* 3427185029Spjd * zfs rollback [-rRf] <snapshot> 3428168404Spjd * 3429219089Spjd * -r Delete any intervening snapshots before doing rollback 3430219089Spjd * -R Delete any snapshots and their clones 3431219089Spjd * -f ignored for backwards compatability 3432168404Spjd * 3433168404Spjd * Given a filesystem, rollback to a specific snapshot, discarding any changes 3434168404Spjd * since then and making it the active dataset. If more recent snapshots exist, 3435168404Spjd * the command will complain unless the '-r' flag is given. 3436168404Spjd */ 3437168404Spjdtypedef struct rollback_cbdata { 3438168404Spjd uint64_t cb_create; 3439168404Spjd boolean_t cb_first; 3440168404Spjd int cb_doclones; 3441168404Spjd char *cb_target; 3442168404Spjd int cb_error; 3443168404Spjd boolean_t cb_recurse; 3444168404Spjd} rollback_cbdata_t; 3445168404Spjd 3446260183Sdelphijstatic int 3447260183Sdelphijrollback_check_dependent(zfs_handle_t *zhp, void *data) 3448260183Sdelphij{ 3449260183Sdelphij rollback_cbdata_t *cbp = data; 3450260183Sdelphij 3451260183Sdelphij if (cbp->cb_first && cbp->cb_recurse) { 3452260183Sdelphij (void) fprintf(stderr, gettext("cannot rollback to " 3453260183Sdelphij "'%s': clones of previous snapshots exist\n"), 3454260183Sdelphij cbp->cb_target); 3455260183Sdelphij (void) fprintf(stderr, gettext("use '-R' to " 3456260183Sdelphij "force deletion of the following clones and " 3457260183Sdelphij "dependents:\n")); 3458260183Sdelphij cbp->cb_first = 0; 3459260183Sdelphij cbp->cb_error = 1; 3460260183Sdelphij } 3461260183Sdelphij 3462260183Sdelphij (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); 3463260183Sdelphij 3464260183Sdelphij zfs_close(zhp); 3465260183Sdelphij return (0); 3466260183Sdelphij} 3467268075Sdelphij 3468168404Spjd/* 3469168404Spjd * Report any snapshots more recent than the one specified. Used when '-r' is 3470168404Spjd * not specified. We reuse this same callback for the snapshot dependents - if 3471168404Spjd * 'cb_dependent' is set, then this is a dependent and we should report it 3472168404Spjd * without checking the transaction group. 3473168404Spjd */ 3474168404Spjdstatic int 3475168404Spjdrollback_check(zfs_handle_t *zhp, void *data) 3476168404Spjd{ 3477168404Spjd rollback_cbdata_t *cbp = data; 3478168404Spjd 3479168404Spjd if (cbp->cb_doclones) { 3480168404Spjd zfs_close(zhp); 3481168404Spjd return (0); 3482168404Spjd } 3483168404Spjd 3484260183Sdelphij if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) { 3485260183Sdelphij if (cbp->cb_first && !cbp->cb_recurse) { 3486260183Sdelphij (void) fprintf(stderr, gettext("cannot " 3487260183Sdelphij "rollback to '%s': more recent snapshots " 3488260183Sdelphij "or bookmarks exist\n"), 3489168404Spjd cbp->cb_target); 3490260183Sdelphij (void) fprintf(stderr, gettext("use '-r' to " 3491260183Sdelphij "force deletion of the following " 3492260183Sdelphij "snapshots and bookmarks:\n")); 3493168404Spjd cbp->cb_first = 0; 3494168404Spjd cbp->cb_error = 1; 3495168404Spjd } 3496168404Spjd 3497260183Sdelphij if (cbp->cb_recurse) { 3498260183Sdelphij if (zfs_iter_dependents(zhp, B_TRUE, 3499260183Sdelphij rollback_check_dependent, cbp) != 0) { 3500260183Sdelphij zfs_close(zhp); 3501260183Sdelphij return (-1); 3502260183Sdelphij } 3503260183Sdelphij } else { 3504260183Sdelphij (void) fprintf(stderr, "%s\n", 3505260183Sdelphij zfs_get_name(zhp)); 3506260183Sdelphij } 3507168404Spjd } 3508168404Spjd zfs_close(zhp); 3509168404Spjd return (0); 3510168404Spjd} 3511168404Spjd 3512168404Spjdstatic int 3513168404Spjdzfs_do_rollback(int argc, char **argv) 3514168404Spjd{ 3515231144Smm int ret = 0; 3516168404Spjd int c; 3517185029Spjd boolean_t force = B_FALSE; 3518168404Spjd rollback_cbdata_t cb = { 0 }; 3519168404Spjd zfs_handle_t *zhp, *snap; 3520307108Smav char parentname[ZFS_MAX_DATASET_NAME_LEN]; 3521168404Spjd char *delim; 3522168404Spjd 3523168404Spjd /* check options */ 3524185029Spjd while ((c = getopt(argc, argv, "rRf")) != -1) { 3525168404Spjd switch (c) { 3526168404Spjd case 'r': 3527168404Spjd cb.cb_recurse = 1; 3528168404Spjd break; 3529168404Spjd case 'R': 3530168404Spjd cb.cb_recurse = 1; 3531168404Spjd cb.cb_doclones = 1; 3532168404Spjd break; 3533185029Spjd case 'f': 3534185029Spjd force = B_TRUE; 3535185029Spjd break; 3536168404Spjd case '?': 3537168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3538168404Spjd optopt); 3539168404Spjd usage(B_FALSE); 3540168404Spjd } 3541168404Spjd } 3542168404Spjd 3543168404Spjd argc -= optind; 3544168404Spjd argv += optind; 3545168404Spjd 3546168404Spjd /* check number of arguments */ 3547168404Spjd if (argc < 1) { 3548168404Spjd (void) fprintf(stderr, gettext("missing dataset argument\n")); 3549168404Spjd usage(B_FALSE); 3550168404Spjd } 3551168404Spjd if (argc > 1) { 3552168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 3553168404Spjd usage(B_FALSE); 3554168404Spjd } 3555168404Spjd 3556168404Spjd /* open the snapshot */ 3557168404Spjd if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) 3558168404Spjd return (1); 3559168404Spjd 3560168404Spjd /* open the parent dataset */ 3561168404Spjd (void) strlcpy(parentname, argv[0], sizeof (parentname)); 3562168404Spjd verify((delim = strrchr(parentname, '@')) != NULL); 3563168404Spjd *delim = '\0'; 3564185029Spjd if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_DATASET)) == NULL) { 3565168404Spjd zfs_close(snap); 3566168404Spjd return (1); 3567168404Spjd } 3568168404Spjd 3569168404Spjd /* 3570168404Spjd * Check for more recent snapshots and/or clones based on the presence 3571168404Spjd * of '-r' and '-R'. 3572168404Spjd */ 3573168404Spjd cb.cb_target = argv[0]; 3574168404Spjd cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG); 3575168404Spjd cb.cb_first = B_TRUE; 3576168404Spjd cb.cb_error = 0; 3577260183Sdelphij if ((ret = zfs_iter_snapshots(zhp, B_FALSE, rollback_check, &cb)) != 0) 3578168404Spjd goto out; 3579260183Sdelphij if ((ret = zfs_iter_bookmarks(zhp, rollback_check, &cb)) != 0) 3580260183Sdelphij goto out; 3581168404Spjd 3582168404Spjd if ((ret = cb.cb_error) != 0) 3583168404Spjd goto out; 3584168404Spjd 3585168404Spjd /* 3586168404Spjd * Rollback parent to the given snapshot. 3587168404Spjd */ 3588168404Spjd ret = zfs_rollback(zhp, snap, force); 3589168404Spjd 3590168404Spjdout: 3591168404Spjd zfs_close(snap); 3592168404Spjd zfs_close(zhp); 3593168404Spjd 3594168404Spjd if (ret == 0) 3595168404Spjd return (0); 3596168404Spjd else 3597168404Spjd return (1); 3598168404Spjd} 3599168404Spjd 3600168404Spjd/* 3601289497Smav * zfs set property=value ... { fs | snap | vol } ... 3602168404Spjd * 3603289497Smav * Sets the given properties for all datasets specified on the command line. 3604168404Spjd */ 3605168404Spjd 3606168404Spjdstatic int 3607168404Spjdset_callback(zfs_handle_t *zhp, void *data) 3608168404Spjd{ 3609289497Smav nvlist_t *props = data; 3610168404Spjd 3611289497Smav if (zfs_prop_set_list(zhp, props) != 0) { 3612168404Spjd switch (libzfs_errno(g_zfs)) { 3613168404Spjd case EZFS_MOUNTFAILED: 3614168404Spjd (void) fprintf(stderr, gettext("property may be set " 3615168404Spjd "but unable to remount filesystem\n")); 3616168404Spjd break; 3617168404Spjd case EZFS_SHARENFSFAILED: 3618168404Spjd (void) fprintf(stderr, gettext("property may be set " 3619168404Spjd "but unable to reshare filesystem\n")); 3620168404Spjd break; 3621168404Spjd } 3622168404Spjd return (1); 3623168404Spjd } 3624168404Spjd return (0); 3625168404Spjd} 3626168404Spjd 3627168404Spjdstatic int 3628168404Spjdzfs_do_set(int argc, char **argv) 3629168404Spjd{ 3630289497Smav nvlist_t *props = NULL; 3631289497Smav int ds_start = -1; /* argv idx of first dataset arg */ 3632231144Smm int ret = 0; 3633168404Spjd 3634168404Spjd /* check for options */ 3635168404Spjd if (argc > 1 && argv[1][0] == '-') { 3636168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3637168404Spjd argv[1][1]); 3638168404Spjd usage(B_FALSE); 3639168404Spjd } 3640168404Spjd 3641168404Spjd /* check number of arguments */ 3642168404Spjd if (argc < 2) { 3643289497Smav (void) fprintf(stderr, gettext("missing arguments\n")); 3644168404Spjd usage(B_FALSE); 3645168404Spjd } 3646168404Spjd if (argc < 3) { 3647289497Smav if (strchr(argv[1], '=') == NULL) { 3648289497Smav (void) fprintf(stderr, gettext("missing property=value " 3649289497Smav "argument(s)\n")); 3650289497Smav } else { 3651289497Smav (void) fprintf(stderr, gettext("missing dataset " 3652289497Smav "name(s)\n")); 3653289497Smav } 3654168404Spjd usage(B_FALSE); 3655168404Spjd } 3656168404Spjd 3657289497Smav /* validate argument order: prop=val args followed by dataset args */ 3658289497Smav for (int i = 1; i < argc; i++) { 3659289497Smav if (strchr(argv[i], '=') != NULL) { 3660289497Smav if (ds_start > 0) { 3661289497Smav /* out-of-order prop=val argument */ 3662289497Smav (void) fprintf(stderr, gettext("invalid " 3663289497Smav "argument order\n"), i); 3664289497Smav usage(B_FALSE); 3665289497Smav } 3666289497Smav } else if (ds_start < 0) { 3667289497Smav ds_start = i; 3668289497Smav } 3669289497Smav } 3670289497Smav if (ds_start < 0) { 3671289497Smav (void) fprintf(stderr, gettext("missing dataset name(s)\n")); 3672168404Spjd usage(B_FALSE); 3673168404Spjd } 3674168404Spjd 3675289497Smav /* Populate a list of property settings */ 3676289497Smav if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 3677289497Smav nomem(); 3678289497Smav for (int i = 1; i < ds_start; i++) { 3679289497Smav if ((ret = parseprop(props, argv[i])) != 0) 3680289497Smav goto error; 3681168404Spjd } 3682168404Spjd 3683289497Smav ret = zfs_for_each(argc - ds_start, argv + ds_start, 0, 3684289497Smav ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, props); 3685168404Spjd 3686289497Smaverror: 3687289497Smav nvlist_free(props); 3688168404Spjd return (ret); 3689168404Spjd} 3690168404Spjd 3691248571Smmtypedef struct snap_cbdata { 3692248571Smm nvlist_t *sd_nvl; 3693248571Smm boolean_t sd_recursive; 3694248571Smm const char *sd_snapname; 3695248571Smm} snap_cbdata_t; 3696248571Smm 3697248571Smmstatic int 3698248571Smmzfs_snapshot_cb(zfs_handle_t *zhp, void *arg) 3699248571Smm{ 3700248571Smm snap_cbdata_t *sd = arg; 3701248571Smm char *name; 3702248571Smm int rv = 0; 3703248571Smm int error; 3704248571Smm 3705253819Sdelphij if (sd->sd_recursive && 3706253819Sdelphij zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) != 0) { 3707253819Sdelphij zfs_close(zhp); 3708253819Sdelphij return (0); 3709253819Sdelphij } 3710253819Sdelphij 3711248571Smm error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname); 3712248571Smm if (error == -1) 3713248571Smm nomem(); 3714248571Smm fnvlist_add_boolean(sd->sd_nvl, name); 3715248571Smm free(name); 3716248571Smm 3717248571Smm if (sd->sd_recursive) 3718248571Smm rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd); 3719248571Smm zfs_close(zhp); 3720248571Smm return (rv); 3721248571Smm} 3722248571Smm 3723168404Spjd/* 3724185029Spjd * zfs snapshot [-r] [-o prop=value] ... <fs@snap> 3725168404Spjd * 3726168404Spjd * Creates a snapshot with the given name. While functionally equivalent to 3727185029Spjd * 'zfs create', it is a separate command to differentiate intent. 3728168404Spjd */ 3729168404Spjdstatic int 3730168404Spjdzfs_do_snapshot(int argc, char **argv) 3731168404Spjd{ 3732231144Smm int ret = 0; 3733258362Sjhibbits int c; 3734185029Spjd nvlist_t *props; 3735248571Smm snap_cbdata_t sd = { 0 }; 3736248571Smm boolean_t multiple_snaps = B_FALSE; 3737168404Spjd 3738219089Spjd if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 3739219089Spjd nomem(); 3740248571Smm if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0) 3741248571Smm nomem(); 3742185029Spjd 3743168404Spjd /* check options */ 3744185029Spjd while ((c = getopt(argc, argv, "ro:")) != -1) { 3745168404Spjd switch (c) { 3746185029Spjd case 'o': 3747286705Smav if (parseprop(props, optarg) != 0) 3748185029Spjd return (1); 3749185029Spjd break; 3750168404Spjd case 'r': 3751248571Smm sd.sd_recursive = B_TRUE; 3752248571Smm multiple_snaps = B_TRUE; 3753168404Spjd break; 3754168404Spjd case '?': 3755168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3756168404Spjd optopt); 3757185029Spjd goto usage; 3758168404Spjd } 3759168404Spjd } 3760168404Spjd 3761168404Spjd argc -= optind; 3762168404Spjd argv += optind; 3763168404Spjd 3764168404Spjd /* check number of arguments */ 3765168404Spjd if (argc < 1) { 3766168404Spjd (void) fprintf(stderr, gettext("missing snapshot argument\n")); 3767185029Spjd goto usage; 3768168404Spjd } 3769248571Smm 3770248571Smm if (argc > 1) 3771248571Smm multiple_snaps = B_TRUE; 3772248571Smm for (; argc > 0; argc--, argv++) { 3773248571Smm char *atp; 3774248571Smm zfs_handle_t *zhp; 3775248571Smm 3776248571Smm atp = strchr(argv[0], '@'); 3777248571Smm if (atp == NULL) 3778248571Smm goto usage; 3779248571Smm *atp = '\0'; 3780248571Smm sd.sd_snapname = atp + 1; 3781248571Smm zhp = zfs_open(g_zfs, argv[0], 3782248571Smm ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 3783248571Smm if (zhp == NULL) 3784248571Smm goto usage; 3785248571Smm if (zfs_snapshot_cb(zhp, &sd) != 0) 3786248571Smm goto usage; 3787168404Spjd } 3788168404Spjd 3789248571Smm ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props); 3790248571Smm nvlist_free(sd.sd_nvl); 3791185029Spjd nvlist_free(props); 3792248571Smm if (ret != 0 && multiple_snaps) 3793168404Spjd (void) fprintf(stderr, gettext("no snapshots were created\n")); 3794168404Spjd return (ret != 0); 3795185029Spjd 3796185029Spjdusage: 3797248571Smm nvlist_free(sd.sd_nvl); 3798185029Spjd nvlist_free(props); 3799185029Spjd usage(B_FALSE); 3800185029Spjd return (-1); 3801168404Spjd} 3802168404Spjd 3803168404Spjd/* 3804168404Spjd * Send a backup stream to stdout. 3805168404Spjd */ 3806168404Spjdstatic int 3807168404Spjdzfs_do_send(int argc, char **argv) 3808168404Spjd{ 3809168404Spjd char *fromname = NULL; 3810185029Spjd char *toname = NULL; 3811289362Smav char *resume_token = NULL; 3812168404Spjd char *cp; 3813168404Spjd zfs_handle_t *zhp; 3814219089Spjd sendflags_t flags = { 0 }; 3815168404Spjd int c, err; 3816228103Smm nvlist_t *dbgnv = NULL; 3817219089Spjd boolean_t extraverbose = B_FALSE; 3818168404Spjd 3819321535Smav struct option long_options[] = { 3820321535Smav {"replicate", no_argument, NULL, 'R'}, 3821321535Smav {"props", no_argument, NULL, 'p'}, 3822321535Smav {"parsable", no_argument, NULL, 'P'}, 3823321535Smav {"dedup", no_argument, NULL, 'D'}, 3824321535Smav {"verbose", no_argument, NULL, 'v'}, 3825321535Smav {"dryrun", no_argument, NULL, 'n'}, 3826321535Smav {"large-block", no_argument, NULL, 'L'}, 3827321535Smav {"embed", no_argument, NULL, 'e'}, 3828321535Smav {"resume", required_argument, NULL, 't'}, 3829321535Smav {"compressed", no_argument, NULL, 'c'}, 3830321535Smav {0, 0, 0, 0} 3831321535Smav }; 3832321535Smav 3833168404Spjd /* check options */ 3834346685Smav while ((c = getopt_long(argc, argv, ":i:I:RbDpVvnPLet:c", long_options, 3835321535Smav NULL)) != -1) { 3836168404Spjd switch (c) { 3837168404Spjd case 'i': 3838168404Spjd if (fromname) 3839168404Spjd usage(B_FALSE); 3840168404Spjd fromname = optarg; 3841168404Spjd break; 3842185029Spjd case 'I': 3843185029Spjd if (fromname) 3844185029Spjd usage(B_FALSE); 3845185029Spjd fromname = optarg; 3846219089Spjd flags.doall = B_TRUE; 3847185029Spjd break; 3848185029Spjd case 'R': 3849219089Spjd flags.replicate = B_TRUE; 3850185029Spjd break; 3851219089Spjd case 'p': 3852219089Spjd flags.props = B_TRUE; 3853219089Spjd break; 3854228103Smm case 'P': 3855228103Smm flags.parsable = B_TRUE; 3856228103Smm flags.verbose = B_TRUE; 3857228103Smm break; 3858346685Smav case 'V': 3859346685Smav flags.progress = B_TRUE; 3860346685Smav flags.progressastitle = B_TRUE; 3861346685Smav break; 3862185029Spjd case 'v': 3863219089Spjd if (flags.verbose) 3864219089Spjd extraverbose = B_TRUE; 3865219089Spjd flags.verbose = B_TRUE; 3866235222Smm flags.progress = B_TRUE; 3867185029Spjd break; 3868219089Spjd case 'D': 3869219089Spjd flags.dedup = B_TRUE; 3870219089Spjd break; 3871228103Smm case 'n': 3872228103Smm flags.dryrun = B_TRUE; 3873228103Smm break; 3874274337Sdelphij case 'L': 3875274337Sdelphij flags.largeblock = B_TRUE; 3876274337Sdelphij break; 3877268075Sdelphij case 'e': 3878268075Sdelphij flags.embed_data = B_TRUE; 3879268075Sdelphij break; 3880289362Smav case 't': 3881289362Smav resume_token = optarg; 3882289362Smav break; 3883321535Smav case 'c': 3884321535Smav flags.compress = B_TRUE; 3885321535Smav break; 3886168404Spjd case ':': 3887168404Spjd (void) fprintf(stderr, gettext("missing argument for " 3888168404Spjd "'%c' option\n"), optopt); 3889168404Spjd usage(B_FALSE); 3890168404Spjd break; 3891168404Spjd case '?': 3892321535Smav /*FALLTHROUGH*/ 3893321535Smav default: 3894168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 3895168404Spjd optopt); 3896168404Spjd usage(B_FALSE); 3897168404Spjd } 3898168404Spjd } 3899168404Spjd 3900168404Spjd argc -= optind; 3901168404Spjd argv += optind; 3902168404Spjd 3903289362Smav if (resume_token != NULL) { 3904289362Smav if (fromname != NULL || flags.replicate || flags.props || 3905289362Smav flags.dedup) { 3906289362Smav (void) fprintf(stderr, 3907289362Smav gettext("invalid flags combined with -t\n")); 3908289362Smav usage(B_FALSE); 3909289362Smav } 3910289362Smav if (argc != 0) { 3911289362Smav (void) fprintf(stderr, gettext("no additional " 3912289362Smav "arguments are permitted with -t\n")); 3913289362Smav usage(B_FALSE); 3914289362Smav } 3915289362Smav } else { 3916289362Smav if (argc < 1) { 3917289362Smav (void) fprintf(stderr, 3918289362Smav gettext("missing snapshot argument\n")); 3919289362Smav usage(B_FALSE); 3920289362Smav } 3921289362Smav if (argc > 1) { 3922289362Smav (void) fprintf(stderr, gettext("too many arguments\n")); 3923289362Smav usage(B_FALSE); 3924289362Smav } 3925168404Spjd } 3926168404Spjd 3927228103Smm if (!flags.dryrun && isatty(STDOUT_FILENO)) { 3928168404Spjd (void) fprintf(stderr, 3929168404Spjd gettext("Error: Stream can not be written to a terminal.\n" 3930168404Spjd "You must redirect standard output.\n")); 3931168404Spjd return (1); 3932168404Spjd } 3933168404Spjd 3934289362Smav if (resume_token != NULL) { 3935289362Smav return (zfs_send_resume(g_zfs, &flags, STDOUT_FILENO, 3936289362Smav resume_token)); 3937289362Smav } 3938289362Smav 3939260183Sdelphij /* 3940260183Sdelphij * Special case sending a filesystem, or from a bookmark. 3941260183Sdelphij */ 3942260183Sdelphij if (strchr(argv[0], '@') == NULL || 3943260183Sdelphij (fromname && strchr(fromname, '#') != NULL)) { 3944307108Smav char frombuf[ZFS_MAX_DATASET_NAME_LEN]; 3945260183Sdelphij 3946260183Sdelphij if (flags.replicate || flags.doall || flags.props || 3947352598Savg flags.dedup || (strchr(argv[0], '@') == NULL && 3948352598Savg (flags.dryrun || flags.verbose || flags.progress))) { 3949352598Savg (void) fprintf(stderr, gettext("Error: " 3950260183Sdelphij "Unsupported flag with filesystem or bookmark.\n")); 3951260183Sdelphij return (1); 3952260183Sdelphij } 3953260183Sdelphij 3954260183Sdelphij zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET); 3955260183Sdelphij if (zhp == NULL) 3956260183Sdelphij return (1); 3957260183Sdelphij 3958260183Sdelphij if (fromname != NULL && 3959260183Sdelphij (fromname[0] == '#' || fromname[0] == '@')) { 3960260183Sdelphij /* 3961260183Sdelphij * Incremental source name begins with # or @. 3962260183Sdelphij * Default to same fs as target. 3963260183Sdelphij */ 3964260183Sdelphij (void) strncpy(frombuf, argv[0], sizeof (frombuf)); 3965260183Sdelphij cp = strchr(frombuf, '@'); 3966260183Sdelphij if (cp != NULL) 3967260183Sdelphij *cp = '\0'; 3968260183Sdelphij (void) strlcat(frombuf, fromname, sizeof (frombuf)); 3969260183Sdelphij fromname = frombuf; 3970260183Sdelphij } 3971352598Savg err = zfs_send_one(zhp, fromname, STDOUT_FILENO, flags); 3972260183Sdelphij zfs_close(zhp); 3973260183Sdelphij return (err != 0); 3974260183Sdelphij } 3975260183Sdelphij 3976185029Spjd cp = strchr(argv[0], '@'); 3977185029Spjd *cp = '\0'; 3978185029Spjd toname = cp + 1; 3979185029Spjd zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 3980185029Spjd if (zhp == NULL) 3981168404Spjd return (1); 3982168404Spjd 3983168404Spjd /* 3984168404Spjd * If they specified the full path to the snapshot, chop off 3985185029Spjd * everything except the short name of the snapshot, but special 3986185029Spjd * case if they specify the origin. 3987168404Spjd */ 3988168404Spjd if (fromname && (cp = strchr(fromname, '@')) != NULL) { 3989307108Smav char origin[ZFS_MAX_DATASET_NAME_LEN]; 3990185029Spjd zprop_source_t src; 3991185029Spjd 3992185029Spjd (void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN, 3993185029Spjd origin, sizeof (origin), &src, NULL, 0, B_FALSE); 3994185029Spjd 3995185029Spjd if (strcmp(origin, fromname) == 0) { 3996185029Spjd fromname = NULL; 3997219089Spjd flags.fromorigin = B_TRUE; 3998185029Spjd } else { 3999185029Spjd *cp = '\0'; 4000185029Spjd if (cp != fromname && strcmp(argv[0], fromname)) { 4001185029Spjd (void) fprintf(stderr, 4002185029Spjd gettext("incremental source must be " 4003185029Spjd "in same filesystem\n")); 4004185029Spjd usage(B_FALSE); 4005185029Spjd } 4006185029Spjd fromname = cp + 1; 4007185029Spjd if (strchr(fromname, '@') || strchr(fromname, '/')) { 4008185029Spjd (void) fprintf(stderr, 4009185029Spjd gettext("invalid incremental source\n")); 4010185029Spjd usage(B_FALSE); 4011185029Spjd } 4012168404Spjd } 4013168404Spjd } 4014168404Spjd 4015219089Spjd if (flags.replicate && fromname == NULL) 4016219089Spjd flags.doall = B_TRUE; 4017185029Spjd 4018228103Smm err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO, NULL, 0, 4019219089Spjd extraverbose ? &dbgnv : NULL); 4020219089Spjd 4021228103Smm if (extraverbose && dbgnv != NULL) { 4022219089Spjd /* 4023219089Spjd * dump_nvlist prints to stdout, but that's been 4024219089Spjd * redirected to a file. Make it print to stderr 4025219089Spjd * instead. 4026219089Spjd */ 4027219089Spjd (void) dup2(STDERR_FILENO, STDOUT_FILENO); 4028219089Spjd dump_nvlist(dbgnv, 0); 4029219089Spjd nvlist_free(dbgnv); 4030219089Spjd } 4031168404Spjd zfs_close(zhp); 4032168404Spjd 4033168404Spjd return (err != 0); 4034168404Spjd} 4035168404Spjd 4036168404Spjd/* 4037168404Spjd * Restore a backup stream from stdin. 4038168404Spjd */ 4039168404Spjdstatic int 4040168404Spjdzfs_do_receive(int argc, char **argv) 4041168404Spjd{ 4042296535Smav int c, err = 0; 4043219089Spjd recvflags_t flags = { 0 }; 4044289362Smav boolean_t abort_resumable = B_FALSE; 4045289362Smav 4046286705Smav nvlist_t *props; 4047286705Smav nvpair_t *nvp = NULL; 4048168404Spjd 4049286705Smav if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) 4050286705Smav nomem(); 4051286705Smav 4052168404Spjd /* check options */ 4053289362Smav while ((c = getopt(argc, argv, ":o:denuvFsA")) != -1) { 4054168404Spjd switch (c) { 4055286705Smav case 'o': 4056286705Smav if (parseprop(props, optarg) != 0) 4057286705Smav return (1); 4058286705Smav break; 4059168404Spjd case 'd': 4060185029Spjd flags.isprefix = B_TRUE; 4061168404Spjd break; 4062219089Spjd case 'e': 4063219089Spjd flags.isprefix = B_TRUE; 4064219089Spjd flags.istail = B_TRUE; 4065219089Spjd break; 4066168404Spjd case 'n': 4067185029Spjd flags.dryrun = B_TRUE; 4068168404Spjd break; 4069200516Sdelphij case 'u': 4070200516Sdelphij flags.nomount = B_TRUE; 4071200516Sdelphij break; 4072168404Spjd case 'v': 4073185029Spjd flags.verbose = B_TRUE; 4074168404Spjd break; 4075289362Smav case 's': 4076289362Smav flags.resumable = B_TRUE; 4077289362Smav break; 4078168404Spjd case 'F': 4079185029Spjd flags.force = B_TRUE; 4080168404Spjd break; 4081289362Smav case 'A': 4082289362Smav abort_resumable = B_TRUE; 4083289362Smav break; 4084168404Spjd case ':': 4085168404Spjd (void) fprintf(stderr, gettext("missing argument for " 4086168404Spjd "'%c' option\n"), optopt); 4087168404Spjd usage(B_FALSE); 4088168404Spjd break; 4089168404Spjd case '?': 4090168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 4091168404Spjd optopt); 4092168404Spjd usage(B_FALSE); 4093168404Spjd } 4094168404Spjd } 4095168404Spjd 4096168404Spjd argc -= optind; 4097168404Spjd argv += optind; 4098168404Spjd 4099168404Spjd /* check number of arguments */ 4100168404Spjd if (argc < 1) { 4101168404Spjd (void) fprintf(stderr, gettext("missing snapshot argument\n")); 4102168404Spjd usage(B_FALSE); 4103168404Spjd } 4104168404Spjd if (argc > 1) { 4105168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 4106168404Spjd usage(B_FALSE); 4107168404Spjd } 4108168404Spjd 4109286705Smav while ((nvp = nvlist_next_nvpair(props, nvp))) { 4110286705Smav if (strcmp(nvpair_name(nvp), "origin") != 0) { 4111286705Smav (void) fprintf(stderr, gettext("invalid option")); 4112286705Smav usage(B_FALSE); 4113286705Smav } 4114286705Smav } 4115286705Smav 4116289362Smav if (abort_resumable) { 4117289362Smav if (flags.isprefix || flags.istail || flags.dryrun || 4118289362Smav flags.resumable || flags.nomount) { 4119289362Smav (void) fprintf(stderr, gettext("invalid option")); 4120289362Smav usage(B_FALSE); 4121289362Smav } 4122289362Smav 4123307108Smav char namebuf[ZFS_MAX_DATASET_NAME_LEN]; 4124289362Smav (void) snprintf(namebuf, sizeof (namebuf), 4125289362Smav "%s/%%recv", argv[0]); 4126289362Smav 4127289362Smav if (zfs_dataset_exists(g_zfs, namebuf, 4128289362Smav ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) { 4129289362Smav zfs_handle_t *zhp = zfs_open(g_zfs, 4130289362Smav namebuf, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 4131289362Smav if (zhp == NULL) 4132289362Smav return (1); 4133289362Smav err = zfs_destroy(zhp, B_FALSE); 4134289362Smav } else { 4135289362Smav zfs_handle_t *zhp = zfs_open(g_zfs, 4136289362Smav argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 4137289362Smav if (zhp == NULL) 4138289362Smav usage(B_FALSE); 4139289362Smav if (!zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) || 4140289362Smav zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN, 4141289362Smav NULL, 0, NULL, NULL, 0, B_TRUE) == -1) { 4142289362Smav (void) fprintf(stderr, 4143289362Smav gettext("'%s' does not have any " 4144289362Smav "resumable receive state to abort\n"), 4145289362Smav argv[0]); 4146289362Smav return (1); 4147289362Smav } 4148289362Smav err = zfs_destroy(zhp, B_FALSE); 4149289362Smav } 4150289362Smav 4151289362Smav return (err != 0); 4152289362Smav } 4153289362Smav 4154168404Spjd if (isatty(STDIN_FILENO)) { 4155168404Spjd (void) fprintf(stderr, 4156168404Spjd gettext("Error: Backup stream can not be read " 4157168404Spjd "from a terminal.\n" 4158168404Spjd "You must redirect standard input.\n")); 4159168404Spjd return (1); 4160168404Spjd } 4161286705Smav err = zfs_receive(g_zfs, argv[0], props, &flags, STDIN_FILENO, NULL); 4162168404Spjd 4163185029Spjd return (err != 0); 4164185029Spjd} 4165185029Spjd 4166219089Spjd/* 4167219089Spjd * allow/unallow stuff 4168219089Spjd */ 4169219089Spjd/* copied from zfs/sys/dsl_deleg.h */ 4170219089Spjd#define ZFS_DELEG_PERM_CREATE "create" 4171219089Spjd#define ZFS_DELEG_PERM_DESTROY "destroy" 4172219089Spjd#define ZFS_DELEG_PERM_SNAPSHOT "snapshot" 4173219089Spjd#define ZFS_DELEG_PERM_ROLLBACK "rollback" 4174219089Spjd#define ZFS_DELEG_PERM_CLONE "clone" 4175219089Spjd#define ZFS_DELEG_PERM_PROMOTE "promote" 4176219089Spjd#define ZFS_DELEG_PERM_RENAME "rename" 4177219089Spjd#define ZFS_DELEG_PERM_MOUNT "mount" 4178219089Spjd#define ZFS_DELEG_PERM_SHARE "share" 4179219089Spjd#define ZFS_DELEG_PERM_SEND "send" 4180219089Spjd#define ZFS_DELEG_PERM_RECEIVE "receive" 4181219089Spjd#define ZFS_DELEG_PERM_ALLOW "allow" 4182219089Spjd#define ZFS_DELEG_PERM_USERPROP "userprop" 4183219089Spjd#define ZFS_DELEG_PERM_VSCAN "vscan" /* ??? */ 4184219089Spjd#define ZFS_DELEG_PERM_USERQUOTA "userquota" 4185219089Spjd#define ZFS_DELEG_PERM_GROUPQUOTA "groupquota" 4186219089Spjd#define ZFS_DELEG_PERM_USERUSED "userused" 4187219089Spjd#define ZFS_DELEG_PERM_GROUPUSED "groupused" 4188219089Spjd#define ZFS_DELEG_PERM_HOLD "hold" 4189219089Spjd#define ZFS_DELEG_PERM_RELEASE "release" 4190219089Spjd#define ZFS_DELEG_PERM_DIFF "diff" 4191260183Sdelphij#define ZFS_DELEG_PERM_BOOKMARK "bookmark" 4192332525Smav#define ZFS_DELEG_PERM_REMAP "remap" 4193168404Spjd 4194219089Spjd#define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE 4195219089Spjd 4196219089Spjdstatic zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = { 4197219089Spjd { ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW }, 4198219089Spjd { ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE }, 4199219089Spjd { ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE }, 4200219089Spjd { ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY }, 4201219089Spjd { ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF}, 4202219089Spjd { ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD }, 4203219089Spjd { ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT }, 4204219089Spjd { ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE }, 4205219089Spjd { ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE }, 4206219089Spjd { ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE }, 4207219089Spjd { ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME }, 4208219089Spjd { ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK }, 4209219089Spjd { ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND }, 4210219089Spjd { ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE }, 4211219089Spjd { ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT }, 4212260183Sdelphij { ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK }, 4213332525Smav { ZFS_DELEG_PERM_REMAP, ZFS_DELEG_NOTE_REMAP }, 4214219089Spjd 4215219089Spjd { ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA }, 4216219089Spjd { ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED }, 4217219089Spjd { ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP }, 4218219089Spjd { ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA }, 4219219089Spjd { ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED }, 4220219089Spjd { NULL, ZFS_DELEG_NOTE_NONE } 4221219089Spjd}; 4222219089Spjd 4223219089Spjd/* permission structure */ 4224219089Spjdtypedef struct deleg_perm { 4225219089Spjd zfs_deleg_who_type_t dp_who_type; 4226219089Spjd const char *dp_name; 4227219089Spjd boolean_t dp_local; 4228219089Spjd boolean_t dp_descend; 4229219089Spjd} deleg_perm_t; 4230219089Spjd 4231219089Spjd/* */ 4232219089Spjdtypedef struct deleg_perm_node { 4233219089Spjd deleg_perm_t dpn_perm; 4234219089Spjd 4235219089Spjd uu_avl_node_t dpn_avl_node; 4236219089Spjd} deleg_perm_node_t; 4237219089Spjd 4238219089Spjdtypedef struct fs_perm fs_perm_t; 4239219089Spjd 4240219089Spjd/* permissions set */ 4241219089Spjdtypedef struct who_perm { 4242219089Spjd zfs_deleg_who_type_t who_type; 4243219089Spjd const char *who_name; /* id */ 4244219089Spjd char who_ug_name[256]; /* user/group name */ 4245219089Spjd fs_perm_t *who_fsperm; /* uplink */ 4246219089Spjd 4247219089Spjd uu_avl_t *who_deleg_perm_avl; /* permissions */ 4248219089Spjd} who_perm_t; 4249219089Spjd 4250219089Spjd/* */ 4251219089Spjdtypedef struct who_perm_node { 4252219089Spjd who_perm_t who_perm; 4253219089Spjd uu_avl_node_t who_avl_node; 4254219089Spjd} who_perm_node_t; 4255219089Spjd 4256219089Spjdtypedef struct fs_perm_set fs_perm_set_t; 4257219089Spjd/* fs permissions */ 4258219089Spjdstruct fs_perm { 4259219089Spjd const char *fsp_name; 4260219089Spjd 4261219089Spjd uu_avl_t *fsp_sc_avl; /* sets,create */ 4262219089Spjd uu_avl_t *fsp_uge_avl; /* user,group,everyone */ 4263219089Spjd 4264219089Spjd fs_perm_set_t *fsp_set; /* uplink */ 4265219089Spjd}; 4266219089Spjd 4267219089Spjd/* */ 4268219089Spjdtypedef struct fs_perm_node { 4269219089Spjd fs_perm_t fspn_fsperm; 4270219089Spjd uu_avl_t *fspn_avl; 4271219089Spjd 4272219089Spjd uu_list_node_t fspn_list_node; 4273219089Spjd} fs_perm_node_t; 4274219089Spjd 4275219089Spjd/* top level structure */ 4276219089Spjdstruct fs_perm_set { 4277219089Spjd uu_list_pool_t *fsps_list_pool; 4278219089Spjd uu_list_t *fsps_list; /* list of fs_perms */ 4279219089Spjd 4280219089Spjd uu_avl_pool_t *fsps_named_set_avl_pool; 4281219089Spjd uu_avl_pool_t *fsps_who_perm_avl_pool; 4282219089Spjd uu_avl_pool_t *fsps_deleg_perm_avl_pool; 4283219089Spjd}; 4284219089Spjd 4285219089Spjdstatic inline const char * 4286219089Spjddeleg_perm_type(zfs_deleg_note_t note) 4287219089Spjd{ 4288219089Spjd /* subcommands */ 4289219089Spjd switch (note) { 4290219089Spjd /* SUBCOMMANDS */ 4291219089Spjd /* OTHER */ 4292219089Spjd case ZFS_DELEG_NOTE_GROUPQUOTA: 4293219089Spjd case ZFS_DELEG_NOTE_GROUPUSED: 4294219089Spjd case ZFS_DELEG_NOTE_USERPROP: 4295219089Spjd case ZFS_DELEG_NOTE_USERQUOTA: 4296219089Spjd case ZFS_DELEG_NOTE_USERUSED: 4297219089Spjd /* other */ 4298219089Spjd return (gettext("other")); 4299219089Spjd default: 4300219089Spjd return (gettext("subcommand")); 4301219089Spjd } 4302219089Spjd} 4303219089Spjd 4304296535Smavstatic int 4305219089Spjdwho_type2weight(zfs_deleg_who_type_t who_type) 4306219089Spjd{ 4307219089Spjd int res; 4308219089Spjd switch (who_type) { 4309219089Spjd case ZFS_DELEG_NAMED_SET_SETS: 4310219089Spjd case ZFS_DELEG_NAMED_SET: 4311219089Spjd res = 0; 4312219089Spjd break; 4313219089Spjd case ZFS_DELEG_CREATE_SETS: 4314219089Spjd case ZFS_DELEG_CREATE: 4315219089Spjd res = 1; 4316219089Spjd break; 4317219089Spjd case ZFS_DELEG_USER_SETS: 4318219089Spjd case ZFS_DELEG_USER: 4319219089Spjd res = 2; 4320219089Spjd break; 4321219089Spjd case ZFS_DELEG_GROUP_SETS: 4322219089Spjd case ZFS_DELEG_GROUP: 4323219089Spjd res = 3; 4324219089Spjd break; 4325219089Spjd case ZFS_DELEG_EVERYONE_SETS: 4326219089Spjd case ZFS_DELEG_EVERYONE: 4327219089Spjd res = 4; 4328219089Spjd break; 4329219089Spjd default: 4330219089Spjd res = -1; 4331219089Spjd } 4332219089Spjd 4333219089Spjd return (res); 4334219089Spjd} 4335219089Spjd 4336219089Spjd/* ARGSUSED */ 4337219089Spjdstatic int 4338219089Spjdwho_perm_compare(const void *larg, const void *rarg, void *unused) 4339219089Spjd{ 4340219089Spjd const who_perm_node_t *l = larg; 4341219089Spjd const who_perm_node_t *r = rarg; 4342219089Spjd zfs_deleg_who_type_t ltype = l->who_perm.who_type; 4343219089Spjd zfs_deleg_who_type_t rtype = r->who_perm.who_type; 4344219089Spjd int lweight = who_type2weight(ltype); 4345219089Spjd int rweight = who_type2weight(rtype); 4346219089Spjd int res = lweight - rweight; 4347219089Spjd if (res == 0) 4348219089Spjd res = strncmp(l->who_perm.who_name, r->who_perm.who_name, 4349219089Spjd ZFS_MAX_DELEG_NAME-1); 4350219089Spjd 4351219089Spjd if (res == 0) 4352219089Spjd return (0); 4353219089Spjd if (res > 0) 4354219089Spjd return (1); 4355219089Spjd else 4356219089Spjd return (-1); 4357219089Spjd} 4358219089Spjd 4359219089Spjd/* ARGSUSED */ 4360219089Spjdstatic int 4361219089Spjddeleg_perm_compare(const void *larg, const void *rarg, void *unused) 4362219089Spjd{ 4363219089Spjd const deleg_perm_node_t *l = larg; 4364219089Spjd const deleg_perm_node_t *r = rarg; 4365219089Spjd int res = strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name, 4366219089Spjd ZFS_MAX_DELEG_NAME-1); 4367219089Spjd 4368219089Spjd if (res == 0) 4369219089Spjd return (0); 4370219089Spjd 4371219089Spjd if (res > 0) 4372219089Spjd return (1); 4373219089Spjd else 4374219089Spjd return (-1); 4375219089Spjd} 4376219089Spjd 4377219089Spjdstatic inline void 4378219089Spjdfs_perm_set_init(fs_perm_set_t *fspset) 4379219089Spjd{ 4380219089Spjd bzero(fspset, sizeof (fs_perm_set_t)); 4381219089Spjd 4382219089Spjd if ((fspset->fsps_list_pool = uu_list_pool_create("fsps_list_pool", 4383219089Spjd sizeof (fs_perm_node_t), offsetof(fs_perm_node_t, fspn_list_node), 4384219089Spjd NULL, UU_DEFAULT)) == NULL) 4385219089Spjd nomem(); 4386219089Spjd if ((fspset->fsps_list = uu_list_create(fspset->fsps_list_pool, NULL, 4387219089Spjd UU_DEFAULT)) == NULL) 4388219089Spjd nomem(); 4389219089Spjd 4390219089Spjd if ((fspset->fsps_named_set_avl_pool = uu_avl_pool_create( 4391219089Spjd "named_set_avl_pool", sizeof (who_perm_node_t), offsetof( 4392219089Spjd who_perm_node_t, who_avl_node), who_perm_compare, 4393219089Spjd UU_DEFAULT)) == NULL) 4394219089Spjd nomem(); 4395219089Spjd 4396219089Spjd if ((fspset->fsps_who_perm_avl_pool = uu_avl_pool_create( 4397219089Spjd "who_perm_avl_pool", sizeof (who_perm_node_t), offsetof( 4398219089Spjd who_perm_node_t, who_avl_node), who_perm_compare, 4399219089Spjd UU_DEFAULT)) == NULL) 4400219089Spjd nomem(); 4401219089Spjd 4402219089Spjd if ((fspset->fsps_deleg_perm_avl_pool = uu_avl_pool_create( 4403219089Spjd "deleg_perm_avl_pool", sizeof (deleg_perm_node_t), offsetof( 4404219089Spjd deleg_perm_node_t, dpn_avl_node), deleg_perm_compare, UU_DEFAULT)) 4405219089Spjd == NULL) 4406219089Spjd nomem(); 4407219089Spjd} 4408219089Spjd 4409219089Spjdstatic inline void fs_perm_fini(fs_perm_t *); 4410219089Spjdstatic inline void who_perm_fini(who_perm_t *); 4411219089Spjd 4412219089Spjdstatic inline void 4413219089Spjdfs_perm_set_fini(fs_perm_set_t *fspset) 4414219089Spjd{ 4415219089Spjd fs_perm_node_t *node = uu_list_first(fspset->fsps_list); 4416219089Spjd 4417219089Spjd while (node != NULL) { 4418219089Spjd fs_perm_node_t *next_node = 4419219089Spjd uu_list_next(fspset->fsps_list, node); 4420219089Spjd fs_perm_t *fsperm = &node->fspn_fsperm; 4421219089Spjd fs_perm_fini(fsperm); 4422219089Spjd uu_list_remove(fspset->fsps_list, node); 4423219089Spjd free(node); 4424219089Spjd node = next_node; 4425219089Spjd } 4426219089Spjd 4427219089Spjd uu_avl_pool_destroy(fspset->fsps_named_set_avl_pool); 4428219089Spjd uu_avl_pool_destroy(fspset->fsps_who_perm_avl_pool); 4429219089Spjd uu_avl_pool_destroy(fspset->fsps_deleg_perm_avl_pool); 4430219089Spjd} 4431219089Spjd 4432219089Spjdstatic inline void 4433219089Spjddeleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type, 4434219089Spjd const char *name) 4435219089Spjd{ 4436219089Spjd deleg_perm->dp_who_type = type; 4437219089Spjd deleg_perm->dp_name = name; 4438219089Spjd} 4439219089Spjd 4440219089Spjdstatic inline void 4441219089Spjdwho_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm, 4442219089Spjd zfs_deleg_who_type_t type, const char *name) 4443219089Spjd{ 4444219089Spjd uu_avl_pool_t *pool; 4445219089Spjd pool = fsperm->fsp_set->fsps_deleg_perm_avl_pool; 4446219089Spjd 4447219089Spjd bzero(who_perm, sizeof (who_perm_t)); 4448219089Spjd 4449219089Spjd if ((who_perm->who_deleg_perm_avl = uu_avl_create(pool, NULL, 4450219089Spjd UU_DEFAULT)) == NULL) 4451219089Spjd nomem(); 4452219089Spjd 4453219089Spjd who_perm->who_type = type; 4454219089Spjd who_perm->who_name = name; 4455219089Spjd who_perm->who_fsperm = fsperm; 4456219089Spjd} 4457219089Spjd 4458219089Spjdstatic inline void 4459219089Spjdwho_perm_fini(who_perm_t *who_perm) 4460219089Spjd{ 4461219089Spjd deleg_perm_node_t *node = uu_avl_first(who_perm->who_deleg_perm_avl); 4462219089Spjd 4463219089Spjd while (node != NULL) { 4464219089Spjd deleg_perm_node_t *next_node = 4465219089Spjd uu_avl_next(who_perm->who_deleg_perm_avl, node); 4466219089Spjd 4467219089Spjd uu_avl_remove(who_perm->who_deleg_perm_avl, node); 4468219089Spjd free(node); 4469219089Spjd node = next_node; 4470219089Spjd } 4471219089Spjd 4472219089Spjd uu_avl_destroy(who_perm->who_deleg_perm_avl); 4473219089Spjd} 4474219089Spjd 4475219089Spjdstatic inline void 4476219089Spjdfs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname) 4477219089Spjd{ 4478219089Spjd uu_avl_pool_t *nset_pool = fspset->fsps_named_set_avl_pool; 4479219089Spjd uu_avl_pool_t *who_pool = fspset->fsps_who_perm_avl_pool; 4480219089Spjd 4481219089Spjd bzero(fsperm, sizeof (fs_perm_t)); 4482219089Spjd 4483219089Spjd if ((fsperm->fsp_sc_avl = uu_avl_create(nset_pool, NULL, UU_DEFAULT)) 4484219089Spjd == NULL) 4485219089Spjd nomem(); 4486219089Spjd 4487219089Spjd if ((fsperm->fsp_uge_avl = uu_avl_create(who_pool, NULL, UU_DEFAULT)) 4488219089Spjd == NULL) 4489219089Spjd nomem(); 4490219089Spjd 4491219089Spjd fsperm->fsp_set = fspset; 4492219089Spjd fsperm->fsp_name = fsname; 4493219089Spjd} 4494219089Spjd 4495219089Spjdstatic inline void 4496219089Spjdfs_perm_fini(fs_perm_t *fsperm) 4497219089Spjd{ 4498219089Spjd who_perm_node_t *node = uu_avl_first(fsperm->fsp_sc_avl); 4499219089Spjd while (node != NULL) { 4500219089Spjd who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_sc_avl, 4501219089Spjd node); 4502219089Spjd who_perm_t *who_perm = &node->who_perm; 4503219089Spjd who_perm_fini(who_perm); 4504219089Spjd uu_avl_remove(fsperm->fsp_sc_avl, node); 4505219089Spjd free(node); 4506219089Spjd node = next_node; 4507219089Spjd } 4508219089Spjd 4509219089Spjd node = uu_avl_first(fsperm->fsp_uge_avl); 4510219089Spjd while (node != NULL) { 4511219089Spjd who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_uge_avl, 4512219089Spjd node); 4513219089Spjd who_perm_t *who_perm = &node->who_perm; 4514219089Spjd who_perm_fini(who_perm); 4515219089Spjd uu_avl_remove(fsperm->fsp_uge_avl, node); 4516219089Spjd free(node); 4517219089Spjd node = next_node; 4518219089Spjd } 4519219089Spjd 4520219089Spjd uu_avl_destroy(fsperm->fsp_sc_avl); 4521219089Spjd uu_avl_destroy(fsperm->fsp_uge_avl); 4522219089Spjd} 4523219089Spjd 4524296535Smavstatic void 4525219089Spjdset_deleg_perm_node(uu_avl_t *avl, deleg_perm_node_t *node, 4526219089Spjd zfs_deleg_who_type_t who_type, const char *name, char locality) 4527219089Spjd{ 4528219089Spjd uu_avl_index_t idx = 0; 4529219089Spjd 4530219089Spjd deleg_perm_node_t *found_node = NULL; 4531219089Spjd deleg_perm_t *deleg_perm = &node->dpn_perm; 4532219089Spjd 4533219089Spjd deleg_perm_init(deleg_perm, who_type, name); 4534219089Spjd 4535219089Spjd if ((found_node = uu_avl_find(avl, node, NULL, &idx)) 4536219089Spjd == NULL) 4537219089Spjd uu_avl_insert(avl, node, idx); 4538219089Spjd else { 4539219089Spjd node = found_node; 4540219089Spjd deleg_perm = &node->dpn_perm; 4541219089Spjd } 4542219089Spjd 4543219089Spjd 4544219089Spjd switch (locality) { 4545219089Spjd case ZFS_DELEG_LOCAL: 4546219089Spjd deleg_perm->dp_local = B_TRUE; 4547219089Spjd break; 4548219089Spjd case ZFS_DELEG_DESCENDENT: 4549219089Spjd deleg_perm->dp_descend = B_TRUE; 4550219089Spjd break; 4551219089Spjd case ZFS_DELEG_NA: 4552219089Spjd break; 4553219089Spjd default: 4554219089Spjd assert(B_FALSE); /* invalid locality */ 4555219089Spjd } 4556219089Spjd} 4557219089Spjd 4558219089Spjdstatic inline int 4559219089Spjdparse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality) 4560219089Spjd{ 4561219089Spjd nvpair_t *nvp = NULL; 4562219089Spjd fs_perm_set_t *fspset = who_perm->who_fsperm->fsp_set; 4563219089Spjd uu_avl_t *avl = who_perm->who_deleg_perm_avl; 4564219089Spjd zfs_deleg_who_type_t who_type = who_perm->who_type; 4565219089Spjd 4566219089Spjd while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 4567219089Spjd const char *name = nvpair_name(nvp); 4568219089Spjd data_type_t type = nvpair_type(nvp); 4569219089Spjd uu_avl_pool_t *avl_pool = fspset->fsps_deleg_perm_avl_pool; 4570219089Spjd deleg_perm_node_t *node = 4571219089Spjd safe_malloc(sizeof (deleg_perm_node_t)); 4572219089Spjd 4573219089Spjd assert(type == DATA_TYPE_BOOLEAN); 4574219089Spjd 4575219089Spjd uu_avl_node_init(node, &node->dpn_avl_node, avl_pool); 4576219089Spjd set_deleg_perm_node(avl, node, who_type, name, locality); 4577219089Spjd } 4578219089Spjd 4579219089Spjd return (0); 4580219089Spjd} 4581219089Spjd 4582219089Spjdstatic inline int 4583219089Spjdparse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl) 4584219089Spjd{ 4585219089Spjd nvpair_t *nvp = NULL; 4586219089Spjd fs_perm_set_t *fspset = fsperm->fsp_set; 4587219089Spjd 4588219089Spjd while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 4589219089Spjd nvlist_t *nvl2 = NULL; 4590219089Spjd const char *name = nvpair_name(nvp); 4591219089Spjd uu_avl_t *avl = NULL; 4592296535Smav uu_avl_pool_t *avl_pool = NULL; 4593219089Spjd zfs_deleg_who_type_t perm_type = name[0]; 4594219089Spjd char perm_locality = name[1]; 4595219089Spjd const char *perm_name = name + 3; 4596219089Spjd boolean_t is_set = B_TRUE; 4597219089Spjd who_perm_t *who_perm = NULL; 4598219089Spjd 4599219089Spjd assert('$' == name[2]); 4600219089Spjd 4601219089Spjd if (nvpair_value_nvlist(nvp, &nvl2) != 0) 4602219089Spjd return (-1); 4603219089Spjd 4604219089Spjd switch (perm_type) { 4605219089Spjd case ZFS_DELEG_CREATE: 4606219089Spjd case ZFS_DELEG_CREATE_SETS: 4607219089Spjd case ZFS_DELEG_NAMED_SET: 4608219089Spjd case ZFS_DELEG_NAMED_SET_SETS: 4609219089Spjd avl_pool = fspset->fsps_named_set_avl_pool; 4610219089Spjd avl = fsperm->fsp_sc_avl; 4611219089Spjd break; 4612219089Spjd case ZFS_DELEG_USER: 4613219089Spjd case ZFS_DELEG_USER_SETS: 4614219089Spjd case ZFS_DELEG_GROUP: 4615219089Spjd case ZFS_DELEG_GROUP_SETS: 4616219089Spjd case ZFS_DELEG_EVERYONE: 4617219089Spjd case ZFS_DELEG_EVERYONE_SETS: 4618219089Spjd avl_pool = fspset->fsps_who_perm_avl_pool; 4619219089Spjd avl = fsperm->fsp_uge_avl; 4620219089Spjd break; 4621296535Smav 4622296535Smav default: 4623296535Smav assert(!"unhandled zfs_deleg_who_type_t"); 4624219089Spjd } 4625219089Spjd 4626219089Spjd if (is_set) { 4627219089Spjd who_perm_node_t *found_node = NULL; 4628219089Spjd who_perm_node_t *node = safe_malloc( 4629219089Spjd sizeof (who_perm_node_t)); 4630219089Spjd who_perm = &node->who_perm; 4631219089Spjd uu_avl_index_t idx = 0; 4632219089Spjd 4633219089Spjd uu_avl_node_init(node, &node->who_avl_node, avl_pool); 4634219089Spjd who_perm_init(who_perm, fsperm, perm_type, perm_name); 4635219089Spjd 4636219089Spjd if ((found_node = uu_avl_find(avl, node, NULL, &idx)) 4637219089Spjd == NULL) { 4638219089Spjd if (avl == fsperm->fsp_uge_avl) { 4639219089Spjd uid_t rid = 0; 4640219089Spjd struct passwd *p = NULL; 4641219089Spjd struct group *g = NULL; 4642219089Spjd const char *nice_name = NULL; 4643219089Spjd 4644219089Spjd switch (perm_type) { 4645219089Spjd case ZFS_DELEG_USER_SETS: 4646219089Spjd case ZFS_DELEG_USER: 4647219089Spjd rid = atoi(perm_name); 4648219089Spjd p = getpwuid(rid); 4649219089Spjd if (p) 4650219089Spjd nice_name = p->pw_name; 4651219089Spjd break; 4652219089Spjd case ZFS_DELEG_GROUP_SETS: 4653219089Spjd case ZFS_DELEG_GROUP: 4654219089Spjd rid = atoi(perm_name); 4655219089Spjd g = getgrgid(rid); 4656219089Spjd if (g) 4657219089Spjd nice_name = g->gr_name; 4658219089Spjd break; 4659296535Smav 4660296535Smav default: 4661296535Smav break; 4662219089Spjd } 4663219089Spjd 4664219089Spjd if (nice_name != NULL) 4665219089Spjd (void) strlcpy( 4666219089Spjd node->who_perm.who_ug_name, 4667219089Spjd nice_name, 256); 4668219089Spjd } 4669219089Spjd 4670219089Spjd uu_avl_insert(avl, node, idx); 4671219089Spjd } else { 4672219089Spjd node = found_node; 4673219089Spjd who_perm = &node->who_perm; 4674219089Spjd } 4675219089Spjd } 4676219089Spjd 4677219089Spjd (void) parse_who_perm(who_perm, nvl2, perm_locality); 4678219089Spjd } 4679219089Spjd 4680219089Spjd return (0); 4681219089Spjd} 4682219089Spjd 4683219089Spjdstatic inline int 4684219089Spjdparse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl) 4685219089Spjd{ 4686219089Spjd nvpair_t *nvp = NULL; 4687219089Spjd uu_avl_index_t idx = 0; 4688219089Spjd 4689219089Spjd while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 4690219089Spjd nvlist_t *nvl2 = NULL; 4691219089Spjd const char *fsname = nvpair_name(nvp); 4692219089Spjd data_type_t type = nvpair_type(nvp); 4693219089Spjd fs_perm_t *fsperm = NULL; 4694219089Spjd fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t)); 4695219089Spjd if (node == NULL) 4696219089Spjd nomem(); 4697219089Spjd 4698219089Spjd fsperm = &node->fspn_fsperm; 4699219089Spjd 4700219089Spjd assert(DATA_TYPE_NVLIST == type); 4701219089Spjd 4702219089Spjd uu_list_node_init(node, &node->fspn_list_node, 4703219089Spjd fspset->fsps_list_pool); 4704219089Spjd 4705219089Spjd idx = uu_list_numnodes(fspset->fsps_list); 4706219089Spjd fs_perm_init(fsperm, fspset, fsname); 4707219089Spjd 4708219089Spjd if (nvpair_value_nvlist(nvp, &nvl2) != 0) 4709219089Spjd return (-1); 4710219089Spjd 4711219089Spjd (void) parse_fs_perm(fsperm, nvl2); 4712219089Spjd 4713219089Spjd uu_list_insert(fspset->fsps_list, node, idx); 4714219089Spjd } 4715219089Spjd 4716219089Spjd return (0); 4717219089Spjd} 4718219089Spjd 4719219089Spjdstatic inline const char * 4720219089Spjddeleg_perm_comment(zfs_deleg_note_t note) 4721219089Spjd{ 4722219089Spjd const char *str = ""; 4723219089Spjd 4724219089Spjd /* subcommands */ 4725219089Spjd switch (note) { 4726219089Spjd /* SUBCOMMANDS */ 4727219089Spjd case ZFS_DELEG_NOTE_ALLOW: 4728219089Spjd str = gettext("Must also have the permission that is being" 4729219089Spjd "\n\t\t\t\tallowed"); 4730219089Spjd break; 4731219089Spjd case ZFS_DELEG_NOTE_CLONE: 4732219089Spjd str = gettext("Must also have the 'create' ability and 'mount'" 4733219089Spjd "\n\t\t\t\tability in the origin file system"); 4734219089Spjd break; 4735219089Spjd case ZFS_DELEG_NOTE_CREATE: 4736219089Spjd str = gettext("Must also have the 'mount' ability"); 4737219089Spjd break; 4738219089Spjd case ZFS_DELEG_NOTE_DESTROY: 4739219089Spjd str = gettext("Must also have the 'mount' ability"); 4740219089Spjd break; 4741219089Spjd case ZFS_DELEG_NOTE_DIFF: 4742219089Spjd str = gettext("Allows lookup of paths within a dataset;" 4743219089Spjd "\n\t\t\t\tgiven an object number. Ordinary users need this" 4744219089Spjd "\n\t\t\t\tin order to use zfs diff"); 4745219089Spjd break; 4746219089Spjd case ZFS_DELEG_NOTE_HOLD: 4747219089Spjd str = gettext("Allows adding a user hold to a snapshot"); 4748219089Spjd break; 4749219089Spjd case ZFS_DELEG_NOTE_MOUNT: 4750219089Spjd str = gettext("Allows mount/umount of ZFS datasets"); 4751219089Spjd break; 4752219089Spjd case ZFS_DELEG_NOTE_PROMOTE: 4753219089Spjd str = gettext("Must also have the 'mount'\n\t\t\t\tand" 4754219089Spjd " 'promote' ability in the origin file system"); 4755219089Spjd break; 4756219089Spjd case ZFS_DELEG_NOTE_RECEIVE: 4757219089Spjd str = gettext("Must also have the 'mount' and 'create'" 4758219089Spjd " ability"); 4759219089Spjd break; 4760219089Spjd case ZFS_DELEG_NOTE_RELEASE: 4761219089Spjd str = gettext("Allows releasing a user hold which\n\t\t\t\t" 4762219089Spjd "might destroy the snapshot"); 4763219089Spjd break; 4764219089Spjd case ZFS_DELEG_NOTE_RENAME: 4765219089Spjd str = gettext("Must also have the 'mount' and 'create'" 4766219089Spjd "\n\t\t\t\tability in the new parent"); 4767219089Spjd break; 4768219089Spjd case ZFS_DELEG_NOTE_ROLLBACK: 4769219089Spjd str = gettext(""); 4770219089Spjd break; 4771219089Spjd case ZFS_DELEG_NOTE_SEND: 4772219089Spjd str = gettext(""); 4773219089Spjd break; 4774219089Spjd case ZFS_DELEG_NOTE_SHARE: 4775219089Spjd str = gettext("Allows sharing file systems over NFS or SMB" 4776219089Spjd "\n\t\t\t\tprotocols"); 4777219089Spjd break; 4778219089Spjd case ZFS_DELEG_NOTE_SNAPSHOT: 4779219089Spjd str = gettext(""); 4780219089Spjd break; 4781219089Spjd/* 4782219089Spjd * case ZFS_DELEG_NOTE_VSCAN: 4783219089Spjd * str = gettext(""); 4784219089Spjd * break; 4785219089Spjd */ 4786219089Spjd /* OTHER */ 4787219089Spjd case ZFS_DELEG_NOTE_GROUPQUOTA: 4788219089Spjd str = gettext("Allows accessing any groupquota@... property"); 4789219089Spjd break; 4790219089Spjd case ZFS_DELEG_NOTE_GROUPUSED: 4791219089Spjd str = gettext("Allows reading any groupused@... property"); 4792219089Spjd break; 4793219089Spjd case ZFS_DELEG_NOTE_USERPROP: 4794219089Spjd str = gettext("Allows changing any user property"); 4795219089Spjd break; 4796219089Spjd case ZFS_DELEG_NOTE_USERQUOTA: 4797219089Spjd str = gettext("Allows accessing any userquota@... property"); 4798219089Spjd break; 4799219089Spjd case ZFS_DELEG_NOTE_USERUSED: 4800219089Spjd str = gettext("Allows reading any userused@... property"); 4801219089Spjd break; 4802219089Spjd /* other */ 4803219089Spjd default: 4804219089Spjd str = ""; 4805219089Spjd } 4806219089Spjd 4807219089Spjd return (str); 4808219089Spjd} 4809219089Spjd 4810219089Spjdstruct allow_opts { 4811219089Spjd boolean_t local; 4812219089Spjd boolean_t descend; 4813219089Spjd boolean_t user; 4814219089Spjd boolean_t group; 4815219089Spjd boolean_t everyone; 4816219089Spjd boolean_t create; 4817219089Spjd boolean_t set; 4818219089Spjd boolean_t recursive; /* unallow only */ 4819219089Spjd boolean_t prt_usage; 4820219089Spjd 4821219089Spjd boolean_t prt_perms; 4822219089Spjd char *who; 4823219089Spjd char *perms; 4824219089Spjd const char *dataset; 4825219089Spjd}; 4826219089Spjd 4827219089Spjdstatic inline int 4828219089Spjdprop_cmp(const void *a, const void *b) 4829219089Spjd{ 4830219089Spjd const char *str1 = *(const char **)a; 4831219089Spjd const char *str2 = *(const char **)b; 4832219089Spjd return (strcmp(str1, str2)); 4833219089Spjd} 4834219089Spjd 4835219089Spjdstatic void 4836219089Spjdallow_usage(boolean_t un, boolean_t requested, const char *msg) 4837219089Spjd{ 4838219089Spjd const char *opt_desc[] = { 4839219089Spjd "-h", gettext("show this help message and exit"), 4840219089Spjd "-l", gettext("set permission locally"), 4841219089Spjd "-d", gettext("set permission for descents"), 4842219089Spjd "-u", gettext("set permission for user"), 4843219089Spjd "-g", gettext("set permission for group"), 4844219089Spjd "-e", gettext("set permission for everyone"), 4845219089Spjd "-c", gettext("set create time permission"), 4846219089Spjd "-s", gettext("define permission set"), 4847219089Spjd /* unallow only */ 4848219089Spjd "-r", gettext("remove permissions recursively"), 4849219089Spjd }; 4850219089Spjd size_t unallow_size = sizeof (opt_desc) / sizeof (char *); 4851219089Spjd size_t allow_size = unallow_size - 2; 4852219089Spjd const char *props[ZFS_NUM_PROPS]; 4853219089Spjd int i; 4854219089Spjd size_t count = 0; 4855219089Spjd FILE *fp = requested ? stdout : stderr; 4856219089Spjd zprop_desc_t *pdtbl = zfs_prop_get_table(); 4857219089Spjd const char *fmt = gettext("%-16s %-14s\t%s\n"); 4858219089Spjd 4859219089Spjd (void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW : 4860219089Spjd HELP_ALLOW)); 4861219089Spjd (void) fprintf(fp, gettext("Options:\n")); 4862219089Spjd for (i = 0; i < (un ? unallow_size : allow_size); i++) { 4863219089Spjd const char *opt = opt_desc[i++]; 4864219089Spjd const char *optdsc = opt_desc[i]; 4865219089Spjd (void) fprintf(fp, gettext(" %-10s %s\n"), opt, optdsc); 4866219089Spjd } 4867219089Spjd 4868219089Spjd (void) fprintf(fp, gettext("\nThe following permissions are " 4869219089Spjd "supported:\n\n")); 4870219089Spjd (void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"), 4871219089Spjd gettext("NOTES")); 4872219089Spjd for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) { 4873219089Spjd const char *perm_name = zfs_deleg_perm_tbl[i].z_perm; 4874219089Spjd zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note; 4875219089Spjd const char *perm_type = deleg_perm_type(perm_note); 4876219089Spjd const char *perm_comment = deleg_perm_comment(perm_note); 4877219089Spjd (void) fprintf(fp, fmt, perm_name, perm_type, perm_comment); 4878219089Spjd } 4879219089Spjd 4880219089Spjd for (i = 0; i < ZFS_NUM_PROPS; i++) { 4881219089Spjd zprop_desc_t *pd = &pdtbl[i]; 4882219089Spjd if (pd->pd_visible != B_TRUE) 4883219089Spjd continue; 4884219089Spjd 4885219089Spjd if (pd->pd_attr == PROP_READONLY) 4886219089Spjd continue; 4887219089Spjd 4888219089Spjd props[count++] = pd->pd_name; 4889219089Spjd } 4890219089Spjd props[count] = NULL; 4891219089Spjd 4892219089Spjd qsort(props, count, sizeof (char *), prop_cmp); 4893219089Spjd 4894219089Spjd for (i = 0; i < count; i++) 4895219089Spjd (void) fprintf(fp, fmt, props[i], gettext("property"), ""); 4896219089Spjd 4897219089Spjd if (msg != NULL) 4898219089Spjd (void) fprintf(fp, gettext("\nzfs: error: %s"), msg); 4899219089Spjd 4900219089Spjd exit(requested ? 0 : 2); 4901219089Spjd} 4902219089Spjd 4903219089Spjdstatic inline const char * 4904219089Spjdmunge_args(int argc, char **argv, boolean_t un, size_t expected_argc, 4905219089Spjd char **permsp) 4906219089Spjd{ 4907219089Spjd if (un && argc == expected_argc - 1) 4908219089Spjd *permsp = NULL; 4909219089Spjd else if (argc == expected_argc) 4910219089Spjd *permsp = argv[argc - 2]; 4911219089Spjd else 4912219089Spjd allow_usage(un, B_FALSE, 4913219089Spjd gettext("wrong number of parameters\n")); 4914219089Spjd 4915219089Spjd return (argv[argc - 1]); 4916219089Spjd} 4917219089Spjd 4918219089Spjdstatic void 4919219089Spjdparse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts) 4920219089Spjd{ 4921219089Spjd int uge_sum = opts->user + opts->group + opts->everyone; 4922219089Spjd int csuge_sum = opts->create + opts->set + uge_sum; 4923219089Spjd int ldcsuge_sum = csuge_sum + opts->local + opts->descend; 4924219089Spjd int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum; 4925219089Spjd 4926219089Spjd if (uge_sum > 1) 4927219089Spjd allow_usage(un, B_FALSE, 4928219089Spjd gettext("-u, -g, and -e are mutually exclusive\n")); 4929219089Spjd 4930296535Smav if (opts->prt_usage) { 4931219089Spjd if (argc == 0 && all_sum == 0) 4932219089Spjd allow_usage(un, B_TRUE, NULL); 4933219089Spjd else 4934219089Spjd usage(B_FALSE); 4935296535Smav } 4936219089Spjd 4937219089Spjd if (opts->set) { 4938219089Spjd if (csuge_sum > 1) 4939219089Spjd allow_usage(un, B_FALSE, 4940219089Spjd gettext("invalid options combined with -s\n")); 4941219089Spjd 4942219089Spjd opts->dataset = munge_args(argc, argv, un, 3, &opts->perms); 4943219089Spjd if (argv[0][0] != '@') 4944219089Spjd allow_usage(un, B_FALSE, 4945219089Spjd gettext("invalid set name: missing '@' prefix\n")); 4946219089Spjd opts->who = argv[0]; 4947219089Spjd } else if (opts->create) { 4948219089Spjd if (ldcsuge_sum > 1) 4949219089Spjd allow_usage(un, B_FALSE, 4950219089Spjd gettext("invalid options combined with -c\n")); 4951219089Spjd opts->dataset = munge_args(argc, argv, un, 2, &opts->perms); 4952219089Spjd } else if (opts->everyone) { 4953219089Spjd if (csuge_sum > 1) 4954219089Spjd allow_usage(un, B_FALSE, 4955219089Spjd gettext("invalid options combined with -e\n")); 4956219089Spjd opts->dataset = munge_args(argc, argv, un, 2, &opts->perms); 4957219089Spjd } else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone") 4958219089Spjd == 0) { 4959219089Spjd opts->everyone = B_TRUE; 4960219089Spjd argc--; 4961219089Spjd argv++; 4962219089Spjd opts->dataset = munge_args(argc, argv, un, 2, &opts->perms); 4963230449Smm } else if (argc == 1 && !un) { 4964219089Spjd opts->prt_perms = B_TRUE; 4965219089Spjd opts->dataset = argv[argc-1]; 4966219089Spjd } else { 4967219089Spjd opts->dataset = munge_args(argc, argv, un, 3, &opts->perms); 4968219089Spjd opts->who = argv[0]; 4969219089Spjd } 4970219089Spjd 4971219089Spjd if (!opts->local && !opts->descend) { 4972219089Spjd opts->local = B_TRUE; 4973219089Spjd opts->descend = B_TRUE; 4974219089Spjd } 4975219089Spjd} 4976219089Spjd 4977219089Spjdstatic void 4978219089Spjdstore_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend, 4979219089Spjd const char *who, char *perms, nvlist_t *top_nvl) 4980219089Spjd{ 4981219089Spjd int i; 4982219089Spjd char ld[2] = { '\0', '\0' }; 4983307108Smav char who_buf[MAXNAMELEN + 32]; 4984296535Smav char base_type = '\0'; 4985296535Smav char set_type = '\0'; 4986219089Spjd nvlist_t *base_nvl = NULL; 4987219089Spjd nvlist_t *set_nvl = NULL; 4988219089Spjd nvlist_t *nvl; 4989219089Spjd 4990219089Spjd if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0) 4991219089Spjd nomem(); 4992219089Spjd if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) != 0) 4993219089Spjd nomem(); 4994219089Spjd 4995219089Spjd switch (type) { 4996219089Spjd case ZFS_DELEG_NAMED_SET_SETS: 4997219089Spjd case ZFS_DELEG_NAMED_SET: 4998219089Spjd set_type = ZFS_DELEG_NAMED_SET_SETS; 4999219089Spjd base_type = ZFS_DELEG_NAMED_SET; 5000219089Spjd ld[0] = ZFS_DELEG_NA; 5001219089Spjd break; 5002219089Spjd case ZFS_DELEG_CREATE_SETS: 5003219089Spjd case ZFS_DELEG_CREATE: 5004219089Spjd set_type = ZFS_DELEG_CREATE_SETS; 5005219089Spjd base_type = ZFS_DELEG_CREATE; 5006219089Spjd ld[0] = ZFS_DELEG_NA; 5007219089Spjd break; 5008219089Spjd case ZFS_DELEG_USER_SETS: 5009219089Spjd case ZFS_DELEG_USER: 5010219089Spjd set_type = ZFS_DELEG_USER_SETS; 5011219089Spjd base_type = ZFS_DELEG_USER; 5012219089Spjd if (local) 5013219089Spjd ld[0] = ZFS_DELEG_LOCAL; 5014219089Spjd if (descend) 5015219089Spjd ld[1] = ZFS_DELEG_DESCENDENT; 5016219089Spjd break; 5017219089Spjd case ZFS_DELEG_GROUP_SETS: 5018219089Spjd case ZFS_DELEG_GROUP: 5019219089Spjd set_type = ZFS_DELEG_GROUP_SETS; 5020219089Spjd base_type = ZFS_DELEG_GROUP; 5021219089Spjd if (local) 5022219089Spjd ld[0] = ZFS_DELEG_LOCAL; 5023219089Spjd if (descend) 5024219089Spjd ld[1] = ZFS_DELEG_DESCENDENT; 5025219089Spjd break; 5026219089Spjd case ZFS_DELEG_EVERYONE_SETS: 5027219089Spjd case ZFS_DELEG_EVERYONE: 5028219089Spjd set_type = ZFS_DELEG_EVERYONE_SETS; 5029219089Spjd base_type = ZFS_DELEG_EVERYONE; 5030219089Spjd if (local) 5031219089Spjd ld[0] = ZFS_DELEG_LOCAL; 5032219089Spjd if (descend) 5033219089Spjd ld[1] = ZFS_DELEG_DESCENDENT; 5034296535Smav break; 5035296535Smav 5036296535Smav default: 5037296535Smav assert(set_type != '\0' && base_type != '\0'); 5038219089Spjd } 5039219089Spjd 5040219089Spjd if (perms != NULL) { 5041219089Spjd char *curr = perms; 5042219089Spjd char *end = curr + strlen(perms); 5043219089Spjd 5044219089Spjd while (curr < end) { 5045219089Spjd char *delim = strchr(curr, ','); 5046219089Spjd if (delim == NULL) 5047219089Spjd delim = end; 5048219089Spjd else 5049219089Spjd *delim = '\0'; 5050219089Spjd 5051219089Spjd if (curr[0] == '@') 5052219089Spjd nvl = set_nvl; 5053219089Spjd else 5054219089Spjd nvl = base_nvl; 5055219089Spjd 5056219089Spjd (void) nvlist_add_boolean(nvl, curr); 5057219089Spjd if (delim != end) 5058219089Spjd *delim = ','; 5059219089Spjd curr = delim + 1; 5060219089Spjd } 5061219089Spjd 5062219089Spjd for (i = 0; i < 2; i++) { 5063219089Spjd char locality = ld[i]; 5064219089Spjd if (locality == 0) 5065219089Spjd continue; 5066219089Spjd 5067219089Spjd if (!nvlist_empty(base_nvl)) { 5068219089Spjd if (who != NULL) 5069219089Spjd (void) snprintf(who_buf, 5070219089Spjd sizeof (who_buf), "%c%c$%s", 5071219089Spjd base_type, locality, who); 5072219089Spjd else 5073219089Spjd (void) snprintf(who_buf, 5074219089Spjd sizeof (who_buf), "%c%c$", 5075219089Spjd base_type, locality); 5076219089Spjd 5077219089Spjd (void) nvlist_add_nvlist(top_nvl, who_buf, 5078219089Spjd base_nvl); 5079219089Spjd } 5080219089Spjd 5081219089Spjd 5082219089Spjd if (!nvlist_empty(set_nvl)) { 5083219089Spjd if (who != NULL) 5084219089Spjd (void) snprintf(who_buf, 5085219089Spjd sizeof (who_buf), "%c%c$%s", 5086219089Spjd set_type, locality, who); 5087219089Spjd else 5088219089Spjd (void) snprintf(who_buf, 5089219089Spjd sizeof (who_buf), "%c%c$", 5090219089Spjd set_type, locality); 5091219089Spjd 5092219089Spjd (void) nvlist_add_nvlist(top_nvl, who_buf, 5093219089Spjd set_nvl); 5094219089Spjd } 5095219089Spjd } 5096219089Spjd } else { 5097219089Spjd for (i = 0; i < 2; i++) { 5098219089Spjd char locality = ld[i]; 5099219089Spjd if (locality == 0) 5100219089Spjd continue; 5101219089Spjd 5102219089Spjd if (who != NULL) 5103219089Spjd (void) snprintf(who_buf, sizeof (who_buf), 5104219089Spjd "%c%c$%s", base_type, locality, who); 5105219089Spjd else 5106219089Spjd (void) snprintf(who_buf, sizeof (who_buf), 5107219089Spjd "%c%c$", base_type, locality); 5108219089Spjd (void) nvlist_add_boolean(top_nvl, who_buf); 5109219089Spjd 5110219089Spjd if (who != NULL) 5111219089Spjd (void) snprintf(who_buf, sizeof (who_buf), 5112219089Spjd "%c%c$%s", set_type, locality, who); 5113219089Spjd else 5114219089Spjd (void) snprintf(who_buf, sizeof (who_buf), 5115219089Spjd "%c%c$", set_type, locality); 5116219089Spjd (void) nvlist_add_boolean(top_nvl, who_buf); 5117219089Spjd } 5118219089Spjd } 5119219089Spjd} 5120219089Spjd 5121219089Spjdstatic int 5122219089Spjdconstruct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp) 5123219089Spjd{ 5124219089Spjd if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0) 5125219089Spjd nomem(); 5126219089Spjd 5127219089Spjd if (opts->set) { 5128219089Spjd store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local, 5129219089Spjd opts->descend, opts->who, opts->perms, *nvlp); 5130219089Spjd } else if (opts->create) { 5131219089Spjd store_allow_perm(ZFS_DELEG_CREATE, opts->local, 5132219089Spjd opts->descend, NULL, opts->perms, *nvlp); 5133219089Spjd } else if (opts->everyone) { 5134219089Spjd store_allow_perm(ZFS_DELEG_EVERYONE, opts->local, 5135219089Spjd opts->descend, NULL, opts->perms, *nvlp); 5136219089Spjd } else { 5137219089Spjd char *curr = opts->who; 5138219089Spjd char *end = curr + strlen(curr); 5139219089Spjd 5140219089Spjd while (curr < end) { 5141219089Spjd const char *who; 5142296535Smav zfs_deleg_who_type_t who_type = ZFS_DELEG_WHO_UNKNOWN; 5143219089Spjd char *endch; 5144219089Spjd char *delim = strchr(curr, ','); 5145219089Spjd char errbuf[256]; 5146219089Spjd char id[64]; 5147219089Spjd struct passwd *p = NULL; 5148219089Spjd struct group *g = NULL; 5149219089Spjd 5150219089Spjd uid_t rid; 5151219089Spjd if (delim == NULL) 5152219089Spjd delim = end; 5153219089Spjd else 5154219089Spjd *delim = '\0'; 5155219089Spjd 5156219089Spjd rid = (uid_t)strtol(curr, &endch, 0); 5157219089Spjd if (opts->user) { 5158219089Spjd who_type = ZFS_DELEG_USER; 5159219089Spjd if (*endch != '\0') 5160219089Spjd p = getpwnam(curr); 5161219089Spjd else 5162219089Spjd p = getpwuid(rid); 5163219089Spjd 5164219089Spjd if (p != NULL) 5165219089Spjd rid = p->pw_uid; 5166219089Spjd else { 5167219089Spjd (void) snprintf(errbuf, 256, gettext( 5168219089Spjd "invalid user %s"), curr); 5169219089Spjd allow_usage(un, B_TRUE, errbuf); 5170219089Spjd } 5171219089Spjd } else if (opts->group) { 5172219089Spjd who_type = ZFS_DELEG_GROUP; 5173219089Spjd if (*endch != '\0') 5174219089Spjd g = getgrnam(curr); 5175219089Spjd else 5176219089Spjd g = getgrgid(rid); 5177219089Spjd 5178219089Spjd if (g != NULL) 5179219089Spjd rid = g->gr_gid; 5180219089Spjd else { 5181219089Spjd (void) snprintf(errbuf, 256, gettext( 5182219089Spjd "invalid group %s"), curr); 5183219089Spjd allow_usage(un, B_TRUE, errbuf); 5184219089Spjd } 5185219089Spjd } else { 5186219089Spjd if (*endch != '\0') { 5187219089Spjd p = getpwnam(curr); 5188219089Spjd } else { 5189219089Spjd p = getpwuid(rid); 5190219089Spjd } 5191219089Spjd 5192296535Smav if (p == NULL) { 5193219089Spjd if (*endch != '\0') { 5194219089Spjd g = getgrnam(curr); 5195219089Spjd } else { 5196219089Spjd g = getgrgid(rid); 5197219089Spjd } 5198296535Smav } 5199219089Spjd 5200219089Spjd if (p != NULL) { 5201219089Spjd who_type = ZFS_DELEG_USER; 5202219089Spjd rid = p->pw_uid; 5203219089Spjd } else if (g != NULL) { 5204219089Spjd who_type = ZFS_DELEG_GROUP; 5205219089Spjd rid = g->gr_gid; 5206219089Spjd } else { 5207219089Spjd (void) snprintf(errbuf, 256, gettext( 5208219089Spjd "invalid user/group %s"), curr); 5209219089Spjd allow_usage(un, B_TRUE, errbuf); 5210219089Spjd } 5211219089Spjd } 5212219089Spjd 5213219089Spjd (void) sprintf(id, "%u", rid); 5214219089Spjd who = id; 5215219089Spjd 5216219089Spjd store_allow_perm(who_type, opts->local, 5217219089Spjd opts->descend, who, opts->perms, *nvlp); 5218219089Spjd curr = delim + 1; 5219219089Spjd } 5220219089Spjd } 5221219089Spjd 5222219089Spjd return (0); 5223219089Spjd} 5224219089Spjd 5225219089Spjdstatic void 5226219089Spjdprint_set_creat_perms(uu_avl_t *who_avl) 5227219089Spjd{ 5228219089Spjd const char *sc_title[] = { 5229219089Spjd gettext("Permission sets:\n"), 5230219089Spjd gettext("Create time permissions:\n"), 5231219089Spjd NULL 5232219089Spjd }; 5233219089Spjd const char **title_ptr = sc_title; 5234219089Spjd who_perm_node_t *who_node = NULL; 5235219089Spjd int prev_weight = -1; 5236219089Spjd 5237219089Spjd for (who_node = uu_avl_first(who_avl); who_node != NULL; 5238219089Spjd who_node = uu_avl_next(who_avl, who_node)) { 5239219089Spjd uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl; 5240219089Spjd zfs_deleg_who_type_t who_type = who_node->who_perm.who_type; 5241219089Spjd const char *who_name = who_node->who_perm.who_name; 5242219089Spjd int weight = who_type2weight(who_type); 5243219089Spjd boolean_t first = B_TRUE; 5244219089Spjd deleg_perm_node_t *deleg_node; 5245219089Spjd 5246219089Spjd if (prev_weight != weight) { 5247219089Spjd (void) printf(*title_ptr++); 5248219089Spjd prev_weight = weight; 5249219089Spjd } 5250219089Spjd 5251219089Spjd if (who_name == NULL || strnlen(who_name, 1) == 0) 5252219089Spjd (void) printf("\t"); 5253219089Spjd else 5254219089Spjd (void) printf("\t%s ", who_name); 5255219089Spjd 5256219089Spjd for (deleg_node = uu_avl_first(avl); deleg_node != NULL; 5257219089Spjd deleg_node = uu_avl_next(avl, deleg_node)) { 5258219089Spjd if (first) { 5259219089Spjd (void) printf("%s", 5260219089Spjd deleg_node->dpn_perm.dp_name); 5261219089Spjd first = B_FALSE; 5262219089Spjd } else 5263219089Spjd (void) printf(",%s", 5264219089Spjd deleg_node->dpn_perm.dp_name); 5265219089Spjd } 5266219089Spjd 5267219089Spjd (void) printf("\n"); 5268219089Spjd } 5269219089Spjd} 5270219089Spjd 5271296535Smavstatic void 5272219089Spjdprint_uge_deleg_perms(uu_avl_t *who_avl, boolean_t local, boolean_t descend, 5273219089Spjd const char *title) 5274219089Spjd{ 5275219089Spjd who_perm_node_t *who_node = NULL; 5276219089Spjd boolean_t prt_title = B_TRUE; 5277219089Spjd uu_avl_walk_t *walk; 5278219089Spjd 5279219089Spjd if ((walk = uu_avl_walk_start(who_avl, UU_WALK_ROBUST)) == NULL) 5280219089Spjd nomem(); 5281219089Spjd 5282219089Spjd while ((who_node = uu_avl_walk_next(walk)) != NULL) { 5283219089Spjd const char *who_name = who_node->who_perm.who_name; 5284219089Spjd const char *nice_who_name = who_node->who_perm.who_ug_name; 5285219089Spjd uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl; 5286219089Spjd zfs_deleg_who_type_t who_type = who_node->who_perm.who_type; 5287219089Spjd char delim = ' '; 5288219089Spjd deleg_perm_node_t *deleg_node; 5289219089Spjd boolean_t prt_who = B_TRUE; 5290219089Spjd 5291219089Spjd for (deleg_node = uu_avl_first(avl); 5292219089Spjd deleg_node != NULL; 5293219089Spjd deleg_node = uu_avl_next(avl, deleg_node)) { 5294219089Spjd if (local != deleg_node->dpn_perm.dp_local || 5295219089Spjd descend != deleg_node->dpn_perm.dp_descend) 5296219089Spjd continue; 5297219089Spjd 5298219089Spjd if (prt_who) { 5299219089Spjd const char *who = NULL; 5300219089Spjd if (prt_title) { 5301219089Spjd prt_title = B_FALSE; 5302219089Spjd (void) printf(title); 5303219089Spjd } 5304219089Spjd 5305219089Spjd switch (who_type) { 5306219089Spjd case ZFS_DELEG_USER_SETS: 5307219089Spjd case ZFS_DELEG_USER: 5308219089Spjd who = gettext("user"); 5309219089Spjd if (nice_who_name) 5310219089Spjd who_name = nice_who_name; 5311219089Spjd break; 5312219089Spjd case ZFS_DELEG_GROUP_SETS: 5313219089Spjd case ZFS_DELEG_GROUP: 5314219089Spjd who = gettext("group"); 5315219089Spjd if (nice_who_name) 5316219089Spjd who_name = nice_who_name; 5317219089Spjd break; 5318219089Spjd case ZFS_DELEG_EVERYONE_SETS: 5319219089Spjd case ZFS_DELEG_EVERYONE: 5320219089Spjd who = gettext("everyone"); 5321219089Spjd who_name = NULL; 5322296535Smav break; 5323296535Smav 5324296535Smav default: 5325296535Smav assert(who != NULL); 5326219089Spjd } 5327219089Spjd 5328219089Spjd prt_who = B_FALSE; 5329219089Spjd if (who_name == NULL) 5330219089Spjd (void) printf("\t%s", who); 5331219089Spjd else 5332219089Spjd (void) printf("\t%s %s", who, who_name); 5333219089Spjd } 5334219089Spjd 5335219089Spjd (void) printf("%c%s", delim, 5336219089Spjd deleg_node->dpn_perm.dp_name); 5337219089Spjd delim = ','; 5338219089Spjd } 5339219089Spjd 5340219089Spjd if (!prt_who) 5341219089Spjd (void) printf("\n"); 5342219089Spjd } 5343219089Spjd 5344219089Spjd uu_avl_walk_end(walk); 5345219089Spjd} 5346219089Spjd 5347219089Spjdstatic void 5348219089Spjdprint_fs_perms(fs_perm_set_t *fspset) 5349219089Spjd{ 5350219089Spjd fs_perm_node_t *node = NULL; 5351307108Smav char buf[MAXNAMELEN + 32]; 5352219089Spjd const char *dsname = buf; 5353219089Spjd 5354219089Spjd for (node = uu_list_first(fspset->fsps_list); node != NULL; 5355219089Spjd node = uu_list_next(fspset->fsps_list, node)) { 5356219089Spjd uu_avl_t *sc_avl = node->fspn_fsperm.fsp_sc_avl; 5357219089Spjd uu_avl_t *uge_avl = node->fspn_fsperm.fsp_uge_avl; 5358219089Spjd int left = 0; 5359219089Spjd 5360307108Smav (void) snprintf(buf, sizeof (buf), 5361219089Spjd gettext("---- Permissions on %s "), 5362219089Spjd node->fspn_fsperm.fsp_name); 5363219089Spjd (void) printf(dsname); 5364219089Spjd left = 70 - strlen(buf); 5365219089Spjd while (left-- > 0) 5366219089Spjd (void) printf("-"); 5367219089Spjd (void) printf("\n"); 5368219089Spjd 5369219089Spjd print_set_creat_perms(sc_avl); 5370219089Spjd print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE, 5371219089Spjd gettext("Local permissions:\n")); 5372219089Spjd print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE, 5373219089Spjd gettext("Descendent permissions:\n")); 5374219089Spjd print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE, 5375219089Spjd gettext("Local+Descendent permissions:\n")); 5376219089Spjd } 5377219089Spjd} 5378219089Spjd 5379219089Spjdstatic fs_perm_set_t fs_perm_set = { NULL, NULL, NULL, NULL }; 5380219089Spjd 5381219089Spjdstruct deleg_perms { 5382219089Spjd boolean_t un; 5383219089Spjd nvlist_t *nvl; 5384219089Spjd}; 5385219089Spjd 5386219089Spjdstatic int 5387219089Spjdset_deleg_perms(zfs_handle_t *zhp, void *data) 5388219089Spjd{ 5389219089Spjd struct deleg_perms *perms = (struct deleg_perms *)data; 5390219089Spjd zfs_type_t zfs_type = zfs_get_type(zhp); 5391219089Spjd 5392219089Spjd if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME) 5393219089Spjd return (0); 5394219089Spjd 5395219089Spjd return (zfs_set_fsacl(zhp, perms->un, perms->nvl)); 5396219089Spjd} 5397219089Spjd 5398219089Spjdstatic int 5399219089Spjdzfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un) 5400219089Spjd{ 5401219089Spjd zfs_handle_t *zhp; 5402219089Spjd nvlist_t *perm_nvl = NULL; 5403219089Spjd nvlist_t *update_perm_nvl = NULL; 5404219089Spjd int error = 1; 5405219089Spjd int c; 5406219089Spjd struct allow_opts opts = { 0 }; 5407219089Spjd 5408219089Spjd const char *optstr = un ? "ldugecsrh" : "ldugecsh"; 5409219089Spjd 5410219089Spjd /* check opts */ 5411219089Spjd while ((c = getopt(argc, argv, optstr)) != -1) { 5412219089Spjd switch (c) { 5413219089Spjd case 'l': 5414219089Spjd opts.local = B_TRUE; 5415219089Spjd break; 5416219089Spjd case 'd': 5417219089Spjd opts.descend = B_TRUE; 5418219089Spjd break; 5419219089Spjd case 'u': 5420219089Spjd opts.user = B_TRUE; 5421219089Spjd break; 5422219089Spjd case 'g': 5423219089Spjd opts.group = B_TRUE; 5424219089Spjd break; 5425219089Spjd case 'e': 5426219089Spjd opts.everyone = B_TRUE; 5427219089Spjd break; 5428219089Spjd case 's': 5429219089Spjd opts.set = B_TRUE; 5430219089Spjd break; 5431219089Spjd case 'c': 5432219089Spjd opts.create = B_TRUE; 5433219089Spjd break; 5434219089Spjd case 'r': 5435219089Spjd opts.recursive = B_TRUE; 5436219089Spjd break; 5437219089Spjd case ':': 5438219089Spjd (void) fprintf(stderr, gettext("missing argument for " 5439219089Spjd "'%c' option\n"), optopt); 5440219089Spjd usage(B_FALSE); 5441219089Spjd break; 5442219089Spjd case 'h': 5443219089Spjd opts.prt_usage = B_TRUE; 5444219089Spjd break; 5445219089Spjd case '?': 5446219089Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 5447219089Spjd optopt); 5448219089Spjd usage(B_FALSE); 5449219089Spjd } 5450219089Spjd } 5451219089Spjd 5452219089Spjd argc -= optind; 5453219089Spjd argv += optind; 5454219089Spjd 5455219089Spjd /* check arguments */ 5456219089Spjd parse_allow_args(argc, argv, un, &opts); 5457219089Spjd 5458219089Spjd /* try to open the dataset */ 5459230449Smm if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM | 5460230449Smm ZFS_TYPE_VOLUME)) == NULL) { 5461230449Smm (void) fprintf(stderr, "Failed to open dataset: %s\n", 5462219089Spjd opts.dataset); 5463219089Spjd return (-1); 5464219089Spjd } 5465219089Spjd 5466219089Spjd if (zfs_get_fsacl(zhp, &perm_nvl) != 0) 5467219089Spjd goto cleanup2; 5468219089Spjd 5469219089Spjd fs_perm_set_init(&fs_perm_set); 5470219089Spjd if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) { 5471230449Smm (void) fprintf(stderr, "Failed to parse fsacl permissions\n"); 5472219089Spjd goto cleanup1; 5473219089Spjd } 5474219089Spjd 5475219089Spjd if (opts.prt_perms) 5476219089Spjd print_fs_perms(&fs_perm_set); 5477219089Spjd else { 5478219089Spjd (void) construct_fsacl_list(un, &opts, &update_perm_nvl); 5479219089Spjd if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0) 5480219089Spjd goto cleanup0; 5481219089Spjd 5482219089Spjd if (un && opts.recursive) { 5483219089Spjd struct deleg_perms data = { un, update_perm_nvl }; 5484219089Spjd if (zfs_iter_filesystems(zhp, set_deleg_perms, 5485219089Spjd &data) != 0) 5486219089Spjd goto cleanup0; 5487219089Spjd } 5488219089Spjd } 5489219089Spjd 5490219089Spjd error = 0; 5491219089Spjd 5492219089Spjdcleanup0: 5493219089Spjd nvlist_free(perm_nvl); 5494296528Smav nvlist_free(update_perm_nvl); 5495219089Spjdcleanup1: 5496219089Spjd fs_perm_set_fini(&fs_perm_set); 5497219089Spjdcleanup2: 5498219089Spjd zfs_close(zhp); 5499219089Spjd 5500219089Spjd return (error); 5501219089Spjd} 5502219089Spjd 5503219089Spjdstatic int 5504219089Spjdzfs_do_allow(int argc, char **argv) 5505219089Spjd{ 5506219089Spjd return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE)); 5507219089Spjd} 5508219089Spjd 5509219089Spjdstatic int 5510219089Spjdzfs_do_unallow(int argc, char **argv) 5511219089Spjd{ 5512219089Spjd return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE)); 5513219089Spjd} 5514219089Spjd 5515219089Spjdstatic int 5516219089Spjdzfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding) 5517219089Spjd{ 5518219089Spjd int errors = 0; 5519219089Spjd int i; 5520219089Spjd const char *tag; 5521219089Spjd boolean_t recursive = B_FALSE; 5522219089Spjd const char *opts = holding ? "rt" : "r"; 5523219089Spjd int c; 5524219089Spjd 5525219089Spjd /* check options */ 5526219089Spjd while ((c = getopt(argc, argv, opts)) != -1) { 5527219089Spjd switch (c) { 5528219089Spjd case 'r': 5529219089Spjd recursive = B_TRUE; 5530219089Spjd break; 5531219089Spjd case '?': 5532219089Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 5533219089Spjd optopt); 5534219089Spjd usage(B_FALSE); 5535219089Spjd } 5536219089Spjd } 5537219089Spjd 5538219089Spjd argc -= optind; 5539219089Spjd argv += optind; 5540219089Spjd 5541219089Spjd /* check number of arguments */ 5542219089Spjd if (argc < 2) 5543219089Spjd usage(B_FALSE); 5544219089Spjd 5545219089Spjd tag = argv[0]; 5546219089Spjd --argc; 5547219089Spjd ++argv; 5548219089Spjd 5549219089Spjd if (holding && tag[0] == '.') { 5550219089Spjd /* tags starting with '.' are reserved for libzfs */ 5551219089Spjd (void) fprintf(stderr, gettext("tag may not start with '.'\n")); 5552219089Spjd usage(B_FALSE); 5553219089Spjd } 5554219089Spjd 5555219089Spjd for (i = 0; i < argc; ++i) { 5556219089Spjd zfs_handle_t *zhp; 5557307108Smav char parent[ZFS_MAX_DATASET_NAME_LEN]; 5558219089Spjd const char *delim; 5559219089Spjd char *path = argv[i]; 5560219089Spjd 5561219089Spjd delim = strchr(path, '@'); 5562219089Spjd if (delim == NULL) { 5563219089Spjd (void) fprintf(stderr, 5564219089Spjd gettext("'%s' is not a snapshot\n"), path); 5565219089Spjd ++errors; 5566219089Spjd continue; 5567219089Spjd } 5568219089Spjd (void) strncpy(parent, path, delim - path); 5569219089Spjd parent[delim - path] = '\0'; 5570219089Spjd 5571219089Spjd zhp = zfs_open(g_zfs, parent, 5572219089Spjd ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); 5573219089Spjd if (zhp == NULL) { 5574219089Spjd ++errors; 5575219089Spjd continue; 5576219089Spjd } 5577219089Spjd if (holding) { 5578251646Sdelphij if (zfs_hold(zhp, delim+1, tag, recursive, -1) != 0) 5579219089Spjd ++errors; 5580219089Spjd } else { 5581219089Spjd if (zfs_release(zhp, delim+1, tag, recursive) != 0) 5582219089Spjd ++errors; 5583219089Spjd } 5584219089Spjd zfs_close(zhp); 5585219089Spjd } 5586219089Spjd 5587219089Spjd return (errors != 0); 5588219089Spjd} 5589219089Spjd 5590219089Spjd/* 5591219089Spjd * zfs hold [-r] [-t] <tag> <snap> ... 5592219089Spjd * 5593219089Spjd * -r Recursively hold 5594219089Spjd * 5595219089Spjd * Apply a user-hold with the given tag to the list of snapshots. 5596219089Spjd */ 5597219089Spjdstatic int 5598219089Spjdzfs_do_hold(int argc, char **argv) 5599219089Spjd{ 5600219089Spjd return (zfs_do_hold_rele_impl(argc, argv, B_TRUE)); 5601219089Spjd} 5602219089Spjd 5603219089Spjd/* 5604219089Spjd * zfs release [-r] <tag> <snap> ... 5605219089Spjd * 5606219089Spjd * -r Recursively release 5607219089Spjd * 5608219089Spjd * Release a user-hold with the given tag from the list of snapshots. 5609219089Spjd */ 5610219089Spjdstatic int 5611219089Spjdzfs_do_release(int argc, char **argv) 5612219089Spjd{ 5613219089Spjd return (zfs_do_hold_rele_impl(argc, argv, B_FALSE)); 5614219089Spjd} 5615219089Spjd 5616219089Spjdtypedef struct holds_cbdata { 5617219089Spjd boolean_t cb_recursive; 5618219089Spjd const char *cb_snapname; 5619219089Spjd nvlist_t **cb_nvlp; 5620219089Spjd size_t cb_max_namelen; 5621219089Spjd size_t cb_max_taglen; 5622219089Spjd} holds_cbdata_t; 5623219089Spjd 5624219089Spjd#define STRFTIME_FMT_STR "%a %b %e %k:%M %Y" 5625219089Spjd#define DATETIME_BUF_LEN (32) 5626219089Spjd/* 5627219089Spjd * 5628219089Spjd */ 5629219089Spjdstatic void 5630290015Sallanjudeprint_holds(boolean_t scripted, boolean_t literal, size_t nwidth, 5631290015Sallanjude size_t tagwidth, nvlist_t *nvl) 5632219089Spjd{ 5633219089Spjd int i; 5634219089Spjd nvpair_t *nvp = NULL; 5635219089Spjd char *hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" }; 5636219089Spjd const char *col; 5637219089Spjd 5638219089Spjd if (!scripted) { 5639219089Spjd for (i = 0; i < 3; i++) { 5640219089Spjd col = gettext(hdr_cols[i]); 5641219089Spjd if (i < 2) 5642219089Spjd (void) printf("%-*s ", i ? tagwidth : nwidth, 5643219089Spjd col); 5644219089Spjd else 5645219089Spjd (void) printf("%s\n", col); 5646219089Spjd } 5647219089Spjd } 5648219089Spjd 5649219089Spjd while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 5650219089Spjd char *zname = nvpair_name(nvp); 5651219089Spjd nvlist_t *nvl2; 5652219089Spjd nvpair_t *nvp2 = NULL; 5653219089Spjd (void) nvpair_value_nvlist(nvp, &nvl2); 5654219089Spjd while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) { 5655219089Spjd char tsbuf[DATETIME_BUF_LEN]; 5656219089Spjd char *tagname = nvpair_name(nvp2); 5657219089Spjd uint64_t val = 0; 5658219089Spjd time_t time; 5659219089Spjd struct tm t; 5660219089Spjd 5661219089Spjd (void) nvpair_value_uint64(nvp2, &val); 5662290015Sallanjude if (literal) 5663290015Sallanjude snprintf(tsbuf, DATETIME_BUF_LEN, "%llu", val); 5664290015Sallanjude else { 5665290015Sallanjude time = (time_t)val; 5666290015Sallanjude (void) localtime_r(&time, &t); 5667290015Sallanjude (void) strftime(tsbuf, DATETIME_BUF_LEN, 5668290015Sallanjude gettext(STRFTIME_FMT_STR), &t); 5669290015Sallanjude } 5670219089Spjd 5671329495Smav if (scripted) { 5672329495Smav (void) printf("%s\t%s\t%s\n", zname, 5673329495Smav tagname, tsbuf); 5674329495Smav } else { 5675329495Smav (void) printf("%-*s %-*s %s\n", nwidth, 5676329495Smav zname, tagwidth, tagname, tsbuf); 5677329495Smav } 5678219089Spjd } 5679219089Spjd } 5680219089Spjd} 5681219089Spjd 5682219089Spjd/* 5683219089Spjd * Generic callback function to list a dataset or snapshot. 5684219089Spjd */ 5685219089Spjdstatic int 5686219089Spjdholds_callback(zfs_handle_t *zhp, void *data) 5687219089Spjd{ 5688219089Spjd holds_cbdata_t *cbp = data; 5689219089Spjd nvlist_t *top_nvl = *cbp->cb_nvlp; 5690219089Spjd nvlist_t *nvl = NULL; 5691219089Spjd nvpair_t *nvp = NULL; 5692219089Spjd const char *zname = zfs_get_name(zhp); 5693307108Smav size_t znamelen = strlen(zname); 5694219089Spjd 5695290015Sallanjude if (cbp->cb_recursive && cbp->cb_snapname != NULL) { 5696219089Spjd const char *snapname; 5697219089Spjd char *delim = strchr(zname, '@'); 5698219089Spjd if (delim == NULL) 5699219089Spjd return (0); 5700219089Spjd 5701219089Spjd snapname = delim + 1; 5702219089Spjd if (strcmp(cbp->cb_snapname, snapname)) 5703219089Spjd return (0); 5704219089Spjd } 5705219089Spjd 5706219089Spjd if (zfs_get_holds(zhp, &nvl) != 0) 5707219089Spjd return (-1); 5708219089Spjd 5709219089Spjd if (znamelen > cbp->cb_max_namelen) 5710219089Spjd cbp->cb_max_namelen = znamelen; 5711219089Spjd 5712219089Spjd while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 5713219089Spjd const char *tag = nvpair_name(nvp); 5714307108Smav size_t taglen = strlen(tag); 5715219089Spjd if (taglen > cbp->cb_max_taglen) 5716219089Spjd cbp->cb_max_taglen = taglen; 5717219089Spjd } 5718219089Spjd 5719219089Spjd return (nvlist_add_nvlist(top_nvl, zname, nvl)); 5720219089Spjd} 5721219089Spjd 5722219089Spjd/* 5723290015Sallanjude * zfs holds [-Hp] [-r | -d max] <dataset|snap> ... 5724219089Spjd * 5725290015Sallanjude * -H Suppress header output 5726290015Sallanjude * -p Output literal values 5727290015Sallanjude * -r Recursively search for holds 5728290015Sallanjude * -d max Limit depth of recursive search 5729219089Spjd */ 5730219089Spjdstatic int 5731219089Spjdzfs_do_holds(int argc, char **argv) 5732219089Spjd{ 5733219089Spjd int errors = 0; 5734219089Spjd int c; 5735219089Spjd int i; 5736219089Spjd boolean_t scripted = B_FALSE; 5737290015Sallanjude boolean_t literal = B_FALSE; 5738219089Spjd boolean_t recursive = B_FALSE; 5739290015Sallanjude const char *opts = "d:rHp"; 5740219089Spjd nvlist_t *nvl; 5741219089Spjd 5742219089Spjd int types = ZFS_TYPE_SNAPSHOT; 5743219089Spjd holds_cbdata_t cb = { 0 }; 5744219089Spjd 5745219089Spjd int limit = 0; 5746231144Smm int ret = 0; 5747219089Spjd int flags = 0; 5748219089Spjd 5749219089Spjd /* check options */ 5750219089Spjd while ((c = getopt(argc, argv, opts)) != -1) { 5751219089Spjd switch (c) { 5752290015Sallanjude case 'd': 5753290015Sallanjude limit = parse_depth(optarg, &flags); 5754290015Sallanjude recursive = B_TRUE; 5755290015Sallanjude break; 5756219089Spjd case 'r': 5757219089Spjd recursive = B_TRUE; 5758219089Spjd break; 5759219089Spjd case 'H': 5760219089Spjd scripted = B_TRUE; 5761219089Spjd break; 5762290015Sallanjude case 'p': 5763290015Sallanjude literal = B_TRUE; 5764290015Sallanjude break; 5765219089Spjd case '?': 5766219089Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 5767219089Spjd optopt); 5768219089Spjd usage(B_FALSE); 5769219089Spjd } 5770219089Spjd } 5771219089Spjd 5772219089Spjd if (recursive) { 5773219089Spjd types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME; 5774219089Spjd flags |= ZFS_ITER_RECURSE; 5775219089Spjd } 5776219089Spjd 5777219089Spjd argc -= optind; 5778219089Spjd argv += optind; 5779219089Spjd 5780219089Spjd /* check number of arguments */ 5781219089Spjd if (argc < 1) 5782219089Spjd usage(B_FALSE); 5783219089Spjd 5784219089Spjd if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) 5785219089Spjd nomem(); 5786219089Spjd 5787219089Spjd for (i = 0; i < argc; ++i) { 5788219089Spjd char *snapshot = argv[i]; 5789219089Spjd const char *delim; 5790290015Sallanjude const char *snapname = NULL; 5791219089Spjd 5792219089Spjd delim = strchr(snapshot, '@'); 5793290015Sallanjude if (delim != NULL) { 5794290015Sallanjude snapname = delim + 1; 5795290015Sallanjude if (recursive) 5796290015Sallanjude snapshot[delim - snapshot] = '\0'; 5797219089Spjd } 5798219089Spjd 5799219089Spjd cb.cb_recursive = recursive; 5800219089Spjd cb.cb_snapname = snapname; 5801219089Spjd cb.cb_nvlp = &nvl; 5802219089Spjd 5803219089Spjd /* 5804219089Spjd * 1. collect holds data, set format options 5805219089Spjd */ 5806219089Spjd ret = zfs_for_each(argc, argv, flags, types, NULL, NULL, limit, 5807219089Spjd holds_callback, &cb); 5808219089Spjd if (ret != 0) 5809219089Spjd ++errors; 5810219089Spjd } 5811219089Spjd 5812219089Spjd /* 5813219089Spjd * 2. print holds data 5814219089Spjd */ 5815290015Sallanjude print_holds(scripted, literal, cb.cb_max_namelen, cb.cb_max_taglen, 5816290015Sallanjude nvl); 5817219089Spjd 5818219089Spjd if (nvlist_empty(nvl)) 5819219089Spjd (void) printf(gettext("no datasets available\n")); 5820219089Spjd 5821219089Spjd nvlist_free(nvl); 5822219089Spjd 5823219089Spjd return (0 != errors); 5824219089Spjd} 5825219089Spjd 5826185029Spjd#define CHECK_SPINNER 30 5827185029Spjd#define SPINNER_TIME 3 /* seconds */ 5828346690Smav#define MOUNT_TIME 1 /* seconds */ 5829185029Spjd 5830346690Smavtypedef struct get_all_state { 5831346690Smav boolean_t ga_verbose; 5832346690Smav get_all_cb_t *ga_cbp; 5833346690Smav} get_all_state_t; 5834346690Smav 5835168404Spjdstatic int 5836168404Spjdget_one_dataset(zfs_handle_t *zhp, void *data) 5837168404Spjd{ 5838219089Spjd static char *spin[] = { "-", "\\", "|", "/" }; 5839185029Spjd static int spinval = 0; 5840185029Spjd static int spincheck = 0; 5841185029Spjd static time_t last_spin_time = (time_t)0; 5842346690Smav get_all_state_t *state = data; 5843168404Spjd zfs_type_t type = zfs_get_type(zhp); 5844168404Spjd 5845346690Smav if (state->ga_verbose) { 5846185029Spjd if (--spincheck < 0) { 5847185029Spjd time_t now = time(NULL); 5848185029Spjd if (last_spin_time + SPINNER_TIME < now) { 5849219089Spjd update_progress(spin[spinval++ % 4]); 5850185029Spjd last_spin_time = now; 5851185029Spjd } 5852185029Spjd spincheck = CHECK_SPINNER; 5853185029Spjd } 5854185029Spjd } 5855185029Spjd 5856168404Spjd /* 5857168404Spjd * Interate over any nested datasets. 5858168404Spjd */ 5859219089Spjd if (zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) { 5860168404Spjd zfs_close(zhp); 5861168404Spjd return (1); 5862168404Spjd } 5863168404Spjd 5864168404Spjd /* 5865168404Spjd * Skip any datasets whose type does not match. 5866168404Spjd */ 5867219089Spjd if ((type & ZFS_TYPE_FILESYSTEM) == 0) { 5868168404Spjd zfs_close(zhp); 5869168404Spjd return (0); 5870168404Spjd } 5871346690Smav libzfs_add_handle(state->ga_cbp, zhp); 5872346690Smav assert(state->ga_cbp->cb_used <= state->ga_cbp->cb_alloc); 5873168404Spjd 5874168404Spjd return (0); 5875168404Spjd} 5876168404Spjd 5877168404Spjdstatic void 5878346690Smavget_all_datasets(get_all_cb_t *cbp, boolean_t verbose) 5879168404Spjd{ 5880346690Smav get_all_state_t state = { 5881346690Smav .ga_verbose = verbose, 5882346690Smav .ga_cbp = cbp 5883346690Smav }; 5884168404Spjd 5885219089Spjd if (verbose) 5886219089Spjd set_progress_header(gettext("Reading ZFS config")); 5887346690Smav (void) zfs_iter_root(g_zfs, get_one_dataset, &state); 5888168404Spjd 5889219089Spjd if (verbose) 5890219089Spjd finish_progress(gettext("done.")); 5891168404Spjd} 5892168404Spjd 5893168404Spjd/* 5894168404Spjd * Generic callback for sharing or mounting filesystems. Because the code is so 5895168404Spjd * similar, we have a common function with an extra parameter to determine which 5896168404Spjd * mode we are using. 5897168404Spjd */ 5898346690Smavtypedef enum { OP_SHARE, OP_MOUNT } share_mount_op_t; 5899168404Spjd 5900346690Smavtypedef struct share_mount_state { 5901346690Smav share_mount_op_t sm_op; 5902346690Smav boolean_t sm_verbose; 5903346690Smav int sm_flags; 5904346690Smav char *sm_options; 5905346690Smav char *sm_proto; /* only valid for OP_SHARE */ 5906346690Smav pthread_mutex_t sm_lock; /* protects the remaining fields */ 5907346690Smav uint_t sm_total; /* number of filesystems to process */ 5908346690Smav uint_t sm_done; /* number of filesystems processed */ 5909346690Smav int sm_status; /* -1 if any of the share/mount operations failed */ 5910346690Smav} share_mount_state_t; 5911346690Smav 5912168404Spjd/* 5913168404Spjd * Share or mount a dataset. 5914168404Spjd */ 5915168404Spjdstatic int 5916185029Spjdshare_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol, 5917185029Spjd boolean_t explicit, const char *options) 5918168404Spjd{ 5919168404Spjd char mountpoint[ZFS_MAXPROPLEN]; 5920168404Spjd char shareopts[ZFS_MAXPROPLEN]; 5921185029Spjd char smbshareopts[ZFS_MAXPROPLEN]; 5922168404Spjd const char *cmdname = op == OP_SHARE ? "share" : "mount"; 5923168404Spjd struct mnttab mnt; 5924168404Spjd uint64_t zoned, canmount; 5925185029Spjd boolean_t shared_nfs, shared_smb; 5926168404Spjd 5927219089Spjd assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM); 5928168404Spjd 5929219089Spjd /* 5930219089Spjd * Check to make sure we can mount/share this dataset. If we 5931219089Spjd * are in the global zone and the filesystem is exported to a 5932219089Spjd * local zone, or if we are in a local zone and the 5933219089Spjd * filesystem is not exported, then it is an error. 5934219089Spjd */ 5935219089Spjd zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 5936168404Spjd 5937219089Spjd if (zoned && getzoneid() == GLOBAL_ZONEID) { 5938219089Spjd if (!explicit) 5939219089Spjd return (0); 5940168404Spjd 5941219089Spjd (void) fprintf(stderr, gettext("cannot %s '%s': " 5942219089Spjd "dataset is exported to a local zone\n"), cmdname, 5943219089Spjd zfs_get_name(zhp)); 5944219089Spjd return (1); 5945168404Spjd 5946219089Spjd } else if (!zoned && getzoneid() != GLOBAL_ZONEID) { 5947219089Spjd if (!explicit) 5948219089Spjd return (0); 5949168404Spjd 5950219089Spjd (void) fprintf(stderr, gettext("cannot %s '%s': " 5951219089Spjd "permission denied\n"), cmdname, 5952219089Spjd zfs_get_name(zhp)); 5953219089Spjd return (1); 5954219089Spjd } 5955168404Spjd 5956219089Spjd /* 5957219089Spjd * Ignore any filesystems which don't apply to us. This 5958219089Spjd * includes those with a legacy mountpoint, or those with 5959219089Spjd * legacy share options. 5960219089Spjd */ 5961219089Spjd verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 5962219089Spjd sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0); 5963219089Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts, 5964219089Spjd sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0); 5965219089Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts, 5966219089Spjd sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0); 5967168404Spjd 5968219089Spjd if (op == OP_SHARE && strcmp(shareopts, "off") == 0 && 5969219089Spjd strcmp(smbshareopts, "off") == 0) { 5970219089Spjd if (!explicit) 5971219089Spjd return (0); 5972168404Spjd 5973219089Spjd (void) fprintf(stderr, gettext("cannot share '%s': " 5974219089Spjd "legacy share\n"), zfs_get_name(zhp)); 5975252732Smm (void) fprintf(stderr, gettext("to " 5976252732Smm "share this filesystem set " 5977219089Spjd "sharenfs property on\n")); 5978219089Spjd return (1); 5979219089Spjd } 5980168404Spjd 5981219089Spjd /* 5982219089Spjd * We cannot share or mount legacy filesystems. If the 5983219089Spjd * shareopts is non-legacy but the mountpoint is legacy, we 5984219089Spjd * treat it as a legacy share. 5985219089Spjd */ 5986219089Spjd if (strcmp(mountpoint, "legacy") == 0) { 5987219089Spjd if (!explicit) 5988219089Spjd return (0); 5989168404Spjd 5990219089Spjd (void) fprintf(stderr, gettext("cannot %s '%s': " 5991219089Spjd "legacy mountpoint\n"), cmdname, zfs_get_name(zhp)); 5992252732Smm (void) fprintf(stderr, gettext("use %s(8) to " 5993219089Spjd "%s this filesystem\n"), cmdname, cmdname); 5994219089Spjd return (1); 5995219089Spjd } 5996168404Spjd 5997219089Spjd if (strcmp(mountpoint, "none") == 0) { 5998219089Spjd if (!explicit) 5999219089Spjd return (0); 6000168404Spjd 6001219089Spjd (void) fprintf(stderr, gettext("cannot %s '%s': no " 6002219089Spjd "mountpoint set\n"), cmdname, zfs_get_name(zhp)); 6003219089Spjd return (1); 6004219089Spjd } 6005168404Spjd 6006219089Spjd /* 6007219089Spjd * canmount explicit outcome 6008219089Spjd * on no pass through 6009219089Spjd * on yes pass through 6010219089Spjd * off no return 0 6011219089Spjd * off yes display error, return 1 6012219089Spjd * noauto no return 0 6013219089Spjd * noauto yes pass through 6014219089Spjd */ 6015219089Spjd canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT); 6016219089Spjd if (canmount == ZFS_CANMOUNT_OFF) { 6017219089Spjd if (!explicit) 6018219089Spjd return (0); 6019219089Spjd 6020219089Spjd (void) fprintf(stderr, gettext("cannot %s '%s': " 6021219089Spjd "'canmount' property is set to 'off'\n"), cmdname, 6022219089Spjd zfs_get_name(zhp)); 6023219089Spjd return (1); 6024219089Spjd } else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) { 6025219089Spjd return (0); 6026219089Spjd } 6027219089Spjd 6028219089Spjd /* 6029289362Smav * If this filesystem is inconsistent and has a receive resume 6030289362Smav * token, we can not mount it. 6031289362Smav */ 6032289362Smav if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) && 6033289362Smav zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN, 6034289362Smav NULL, 0, NULL, NULL, 0, B_TRUE) == 0) { 6035289362Smav if (!explicit) 6036289362Smav return (0); 6037289362Smav 6038289362Smav (void) fprintf(stderr, gettext("cannot %s '%s': " 6039289362Smav "Contains partially-completed state from " 6040289362Smav "\"zfs receive -r\", which can be resumed with " 6041289362Smav "\"zfs send -t\"\n"), 6042289362Smav cmdname, zfs_get_name(zhp)); 6043289362Smav return (1); 6044289362Smav } 6045289362Smav 6046289362Smav /* 6047219089Spjd * At this point, we have verified that the mountpoint and/or 6048219089Spjd * shareopts are appropriate for auto management. If the 6049219089Spjd * filesystem is already mounted or shared, return (failing 6050219089Spjd * for explicit requests); otherwise mount or share the 6051219089Spjd * filesystem. 6052219089Spjd */ 6053219089Spjd switch (op) { 6054219089Spjd case OP_SHARE: 6055219089Spjd 6056219089Spjd shared_nfs = zfs_is_shared_nfs(zhp, NULL); 6057219089Spjd shared_smb = zfs_is_shared_smb(zhp, NULL); 6058219089Spjd 6059296535Smav if ((shared_nfs && shared_smb) || 6060219089Spjd (shared_nfs && strcmp(shareopts, "on") == 0 && 6061219089Spjd strcmp(smbshareopts, "off") == 0) || 6062219089Spjd (shared_smb && strcmp(smbshareopts, "on") == 0 && 6063219089Spjd strcmp(shareopts, "off") == 0)) { 6064168404Spjd if (!explicit) 6065168404Spjd return (0); 6066168404Spjd 6067219089Spjd (void) fprintf(stderr, gettext("cannot share " 6068219089Spjd "'%s': filesystem already shared\n"), 6069168404Spjd zfs_get_name(zhp)); 6070168404Spjd return (1); 6071168404Spjd } 6072168404Spjd 6073219089Spjd if (!zfs_is_mounted(zhp, NULL) && 6074219089Spjd zfs_mount(zhp, NULL, 0) != 0) 6075219089Spjd return (1); 6076185029Spjd 6077219089Spjd if (protocol == NULL) { 6078219089Spjd if (zfs_shareall(zhp) != 0) 6079168404Spjd return (1); 6080219089Spjd } else if (strcmp(protocol, "nfs") == 0) { 6081219089Spjd if (zfs_share_nfs(zhp)) 6082168404Spjd return (1); 6083219089Spjd } else if (strcmp(protocol, "smb") == 0) { 6084219089Spjd if (zfs_share_smb(zhp)) 6085168404Spjd return (1); 6086219089Spjd } else { 6087219089Spjd (void) fprintf(stderr, gettext("cannot share " 6088219089Spjd "'%s': invalid share type '%s' " 6089219089Spjd "specified\n"), 6090219089Spjd zfs_get_name(zhp), protocol); 6091219089Spjd return (1); 6092168404Spjd } 6093168404Spjd 6094219089Spjd break; 6095168404Spjd 6096219089Spjd case OP_MOUNT: 6097219089Spjd if (options == NULL) 6098219089Spjd mnt.mnt_mntopts = ""; 6099219089Spjd else 6100219089Spjd mnt.mnt_mntopts = (char *)options; 6101168404Spjd 6102219089Spjd if (!hasmntopt(&mnt, MNTOPT_REMOUNT) && 6103219089Spjd zfs_is_mounted(zhp, NULL)) { 6104168404Spjd if (!explicit) 6105168404Spjd return (0); 6106168404Spjd 6107219089Spjd (void) fprintf(stderr, gettext("cannot mount " 6108219089Spjd "'%s': filesystem already mounted\n"), 6109168404Spjd zfs_get_name(zhp)); 6110168404Spjd return (1); 6111168404Spjd } 6112168404Spjd 6113219089Spjd if (zfs_mount(zhp, options, flags) != 0) 6114168404Spjd return (1); 6115219089Spjd break; 6116168404Spjd } 6117168404Spjd 6118168404Spjd return (0); 6119168404Spjd} 6120168404Spjd 6121185029Spjd/* 6122185029Spjd * Reports progress in the form "(current/total)". Not thread-safe. 6123185029Spjd */ 6124185029Spjdstatic void 6125185029Spjdreport_mount_progress(int current, int total) 6126185029Spjd{ 6127219089Spjd static time_t last_progress_time = 0; 6128185029Spjd time_t now = time(NULL); 6129219089Spjd char info[32]; 6130185029Spjd 6131185029Spjd /* display header if we're here for the first time */ 6132185029Spjd if (current == 1) { 6133219089Spjd set_progress_header(gettext("Mounting ZFS filesystems")); 6134185029Spjd } else if (current != total && last_progress_time + MOUNT_TIME >= now) { 6135185029Spjd /* too soon to report again */ 6136185029Spjd return; 6137185029Spjd } 6138185029Spjd 6139185029Spjd last_progress_time = now; 6140185029Spjd 6141219089Spjd (void) sprintf(info, "(%d/%d)", current, total); 6142185029Spjd 6143219089Spjd if (current == total) 6144219089Spjd finish_progress(info); 6145219089Spjd else 6146219089Spjd update_progress(info); 6147185029Spjd} 6148185029Spjd 6149346690Smav/* 6150346690Smav * zfs_foreach_mountpoint() callback that mounts or shares on filesystem and 6151346690Smav * updates the progress meter 6152346690Smav */ 6153346690Smavstatic int 6154346690Smavshare_mount_one_cb(zfs_handle_t *zhp, void *arg) 6155346690Smav{ 6156346690Smav share_mount_state_t *sms = arg; 6157346690Smav int ret; 6158346690Smav 6159346690Smav ret = share_mount_one(zhp, sms->sm_op, sms->sm_flags, sms->sm_proto, 6160346690Smav B_FALSE, sms->sm_options); 6161346690Smav 6162346690Smav pthread_mutex_lock(&sms->sm_lock); 6163346690Smav if (ret != 0) 6164346690Smav sms->sm_status = ret; 6165346690Smav sms->sm_done++; 6166346690Smav if (sms->sm_verbose) 6167346690Smav report_mount_progress(sms->sm_done, sms->sm_total); 6168346690Smav pthread_mutex_unlock(&sms->sm_lock); 6169346690Smav return (ret); 6170346690Smav} 6171346690Smav 6172185029Spjdstatic void 6173185029Spjdappend_options(char *mntopts, char *newopts) 6174185029Spjd{ 6175185029Spjd int len = strlen(mntopts); 6176185029Spjd 6177185029Spjd /* original length plus new string to append plus 1 for the comma */ 6178185029Spjd if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) { 6179185029Spjd (void) fprintf(stderr, gettext("the opts argument for " 6180185029Spjd "'%c' option is too long (more than %d chars)\n"), 6181185029Spjd "-o", MNT_LINE_MAX); 6182185029Spjd usage(B_FALSE); 6183185029Spjd } 6184185029Spjd 6185185029Spjd if (*mntopts) 6186185029Spjd mntopts[len++] = ','; 6187185029Spjd 6188185029Spjd (void) strcpy(&mntopts[len], newopts); 6189185029Spjd} 6190185029Spjd 6191168404Spjdstatic int 6192168404Spjdshare_mount(int op, int argc, char **argv) 6193168404Spjd{ 6194168404Spjd int do_all = 0; 6195185029Spjd boolean_t verbose = B_FALSE; 6196168404Spjd int c, ret = 0; 6197185029Spjd char *options = NULL; 6198219089Spjd int flags = 0; 6199168404Spjd 6200168404Spjd /* check options */ 6201185029Spjd while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:O" : "a")) 6202168404Spjd != -1) { 6203168404Spjd switch (c) { 6204168404Spjd case 'a': 6205168404Spjd do_all = 1; 6206168404Spjd break; 6207185029Spjd case 'v': 6208185029Spjd verbose = B_TRUE; 6209185029Spjd break; 6210168404Spjd case 'o': 6211185029Spjd if (*optarg == '\0') { 6212185029Spjd (void) fprintf(stderr, gettext("empty mount " 6213185029Spjd "options (-o) specified\n")); 6214185029Spjd usage(B_FALSE); 6215185029Spjd } 6216185029Spjd 6217185029Spjd if (options == NULL) 6218185029Spjd options = safe_malloc(MNT_LINE_MAX + 1); 6219185029Spjd 6220185029Spjd /* option validation is done later */ 6221185029Spjd append_options(options, optarg); 6222168404Spjd break; 6223185029Spjd 6224168404Spjd case 'O': 6225168404Spjd warnx("no overlay mounts support on FreeBSD, ignoring"); 6226168404Spjd break; 6227168404Spjd case ':': 6228168404Spjd (void) fprintf(stderr, gettext("missing argument for " 6229168404Spjd "'%c' option\n"), optopt); 6230168404Spjd usage(B_FALSE); 6231168404Spjd break; 6232168404Spjd case '?': 6233168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 6234168404Spjd optopt); 6235168404Spjd usage(B_FALSE); 6236168404Spjd } 6237168404Spjd } 6238168404Spjd 6239168404Spjd argc -= optind; 6240168404Spjd argv += optind; 6241168404Spjd 6242168404Spjd /* check number of arguments */ 6243168404Spjd if (do_all) { 6244185029Spjd char *protocol = NULL; 6245168404Spjd 6246219089Spjd if (op == OP_SHARE && argc > 0) { 6247219089Spjd if (strcmp(argv[0], "nfs") != 0 && 6248219089Spjd strcmp(argv[0], "smb") != 0) { 6249168404Spjd (void) fprintf(stderr, gettext("share type " 6250219089Spjd "must be 'nfs' or 'smb'\n")); 6251168404Spjd usage(B_FALSE); 6252168404Spjd } 6253185029Spjd protocol = argv[0]; 6254168404Spjd argc--; 6255168404Spjd argv++; 6256168404Spjd } 6257168404Spjd 6258168404Spjd if (argc != 0) { 6259168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 6260168404Spjd usage(B_FALSE); 6261168404Spjd } 6262168404Spjd 6263219089Spjd start_progress_timer(); 6264346690Smav get_all_cb_t cb = { 0 }; 6265346690Smav get_all_datasets(&cb, verbose); 6266168404Spjd 6267346690Smav if (cb.cb_used == 0) { 6268346690Smav if (options != NULL) 6269346690Smav free(options); 6270168404Spjd return (0); 6271346690Smav } 6272168404Spjd 6273339117Smav#ifdef illumos 6274346690Smav if (op == OP_SHARE) { 6275346690Smav sa_init_selective_arg_t sharearg; 6276346690Smav sharearg.zhandle_arr = cb.cb_handles; 6277346690Smav sharearg.zhandle_len = cb.cb_used; 6278346690Smav if ((ret = zfs_init_libshare_arg(g_zfs, 6279346690Smav SA_INIT_SHARE_API_SELECTIVE, &sharearg)) != SA_OK) { 6280346690Smav (void) fprintf(stderr, gettext( 6281346690Smav "Could not initialize libshare, %d"), ret); 6282346690Smav return (ret); 6283346690Smav } 6284339117Smav } 6285339117Smav#endif 6286346690Smav share_mount_state_t share_mount_state = { 0 }; 6287346690Smav share_mount_state.sm_op = op; 6288346690Smav share_mount_state.sm_verbose = verbose; 6289346690Smav share_mount_state.sm_flags = flags; 6290346690Smav share_mount_state.sm_options = options; 6291346690Smav share_mount_state.sm_proto = protocol; 6292346690Smav share_mount_state.sm_total = cb.cb_used; 6293346690Smav pthread_mutex_init(&share_mount_state.sm_lock, NULL); 6294168404Spjd 6295346690Smav /* 6296346690Smav * libshare isn't mt-safe, so only do the operation in parallel 6297346690Smav * if we're mounting. 6298346690Smav */ 6299346690Smav zfs_foreach_mountpoint(g_zfs, cb.cb_handles, cb.cb_used, 6300346690Smav share_mount_one_cb, &share_mount_state, op == OP_MOUNT); 6301346690Smav ret = share_mount_state.sm_status; 6302185029Spjd 6303346690Smav for (int i = 0; i < cb.cb_used; i++) 6304346690Smav zfs_close(cb.cb_handles[i]); 6305346690Smav free(cb.cb_handles); 6306168404Spjd } else if (argc == 0) { 6307219089Spjd struct mnttab entry; 6308168404Spjd 6309185029Spjd if ((op == OP_SHARE) || (options != NULL)) { 6310168404Spjd (void) fprintf(stderr, gettext("missing filesystem " 6311185029Spjd "argument (specify -a for all)\n")); 6312168404Spjd usage(B_FALSE); 6313168404Spjd } 6314168404Spjd 6315168404Spjd /* 6316168404Spjd * When mount is given no arguments, go through /etc/mnttab and 6317168404Spjd * display any active ZFS mounts. We hide any snapshots, since 6318168404Spjd * they are controlled automatically. 6319168404Spjd */ 6320219089Spjd rewind(mnttab_file); 6321219089Spjd while (getmntent(mnttab_file, &entry) == 0) { 6322219089Spjd if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 || 6323219089Spjd strchr(entry.mnt_special, '@') != NULL) 6324168404Spjd continue; 6325168404Spjd 6326219089Spjd (void) printf("%-30s %s\n", entry.mnt_special, 6327219089Spjd entry.mnt_mountp); 6328168404Spjd } 6329168404Spjd 6330168404Spjd } else { 6331168404Spjd zfs_handle_t *zhp; 6332168404Spjd 6333168404Spjd if (argc > 1) { 6334168404Spjd (void) fprintf(stderr, 6335168404Spjd gettext("too many arguments\n")); 6336168404Spjd usage(B_FALSE); 6337168404Spjd } 6338168404Spjd 6339219089Spjd if ((zhp = zfs_open(g_zfs, argv[0], 6340219089Spjd ZFS_TYPE_FILESYSTEM)) == NULL) { 6341168404Spjd ret = 1; 6342168404Spjd } else { 6343185029Spjd ret = share_mount_one(zhp, op, flags, NULL, B_TRUE, 6344168404Spjd options); 6345168404Spjd zfs_close(zhp); 6346168404Spjd } 6347168404Spjd } 6348168404Spjd 6349168404Spjd return (ret); 6350168404Spjd} 6351168404Spjd 6352168404Spjd/* 6353219089Spjd * zfs mount -a [nfs] 6354168404Spjd * zfs mount filesystem 6355168404Spjd * 6356168404Spjd * Mount all filesystems, or mount the given filesystem. 6357168404Spjd */ 6358168404Spjdstatic int 6359168404Spjdzfs_do_mount(int argc, char **argv) 6360168404Spjd{ 6361168404Spjd return (share_mount(OP_MOUNT, argc, argv)); 6362168404Spjd} 6363168404Spjd 6364168404Spjd/* 6365219089Spjd * zfs share -a [nfs | smb] 6366168404Spjd * zfs share filesystem 6367168404Spjd * 6368168404Spjd * Share all filesystems, or share the given filesystem. 6369168404Spjd */ 6370168404Spjdstatic int 6371168404Spjdzfs_do_share(int argc, char **argv) 6372168404Spjd{ 6373168404Spjd return (share_mount(OP_SHARE, argc, argv)); 6374168404Spjd} 6375168404Spjd 6376168404Spjdtypedef struct unshare_unmount_node { 6377168404Spjd zfs_handle_t *un_zhp; 6378168404Spjd char *un_mountp; 6379168404Spjd uu_avl_node_t un_avlnode; 6380168404Spjd} unshare_unmount_node_t; 6381168404Spjd 6382168404Spjd/* ARGSUSED */ 6383168404Spjdstatic int 6384168404Spjdunshare_unmount_compare(const void *larg, const void *rarg, void *unused) 6385168404Spjd{ 6386168404Spjd const unshare_unmount_node_t *l = larg; 6387168404Spjd const unshare_unmount_node_t *r = rarg; 6388168404Spjd 6389168404Spjd return (strcmp(l->un_mountp, r->un_mountp)); 6390168404Spjd} 6391168404Spjd 6392168404Spjd/* 6393168404Spjd * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an 6394168404Spjd * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem, 6395168404Spjd * and unmount it appropriately. 6396168404Spjd */ 6397168404Spjdstatic int 6398168404Spjdunshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) 6399168404Spjd{ 6400168404Spjd zfs_handle_t *zhp; 6401231144Smm int ret = 0; 6402185029Spjd struct stat64 statbuf; 6403219089Spjd struct extmnttab entry; 6404168404Spjd const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount"; 6405185029Spjd ino_t path_inode; 6406168404Spjd 6407168404Spjd /* 6408185029Spjd * Search for the path in /etc/mnttab. Rather than looking for the 6409185029Spjd * specific path, which can be fooled by non-standard paths (i.e. ".." 6410185029Spjd * or "//"), we stat() the path and search for the corresponding 6411185029Spjd * (major,minor) device pair. 6412185029Spjd */ 6413185029Spjd if (stat64(path, &statbuf) != 0) { 6414185029Spjd (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 6415185029Spjd cmdname, path, strerror(errno)); 6416185029Spjd return (1); 6417185029Spjd } 6418185029Spjd path_inode = statbuf.st_ino; 6419185029Spjd 6420185029Spjd /* 6421168404Spjd * Search for the given (major,minor) pair in the mount table. 6422168404Spjd */ 6423277300Ssmh#ifdef illumos 6424168404Spjd rewind(mnttab_file); 6425219089Spjd while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) { 6426219089Spjd if (entry.mnt_major == major(statbuf.st_dev) && 6427219089Spjd entry.mnt_minor == minor(statbuf.st_dev)) 6428219089Spjd break; 6429219089Spjd } 6430219089Spjd#else 6431219089Spjd { 6432219089Spjd struct statfs sfs; 6433219089Spjd 6434219089Spjd if (statfs(path, &sfs) != 0) { 6435219089Spjd (void) fprintf(stderr, "%s: %s\n", path, 6436219089Spjd strerror(errno)); 6437219089Spjd ret = -1; 6438219089Spjd } 6439219089Spjd statfs2mnttab(&sfs, &entry); 6440219089Spjd } 6441219089Spjd#endif 6442219089Spjd if (ret != 0) { 6443185029Spjd if (op == OP_SHARE) { 6444185029Spjd (void) fprintf(stderr, gettext("cannot %s '%s': not " 6445185029Spjd "currently mounted\n"), cmdname, path); 6446185029Spjd return (1); 6447185029Spjd } 6448185029Spjd (void) fprintf(stderr, gettext("warning: %s not in mnttab\n"), 6449185029Spjd path); 6450185029Spjd if ((ret = umount2(path, flags)) != 0) 6451185029Spjd (void) fprintf(stderr, gettext("%s: %s\n"), path, 6452185029Spjd strerror(errno)); 6453185029Spjd return (ret != 0); 6454168404Spjd } 6455168404Spjd 6456168404Spjd if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) { 6457168404Spjd (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS " 6458168404Spjd "filesystem\n"), cmdname, path); 6459168404Spjd return (1); 6460168404Spjd } 6461168404Spjd 6462168404Spjd if ((zhp = zfs_open(g_zfs, entry.mnt_special, 6463168404Spjd ZFS_TYPE_FILESYSTEM)) == NULL) 6464168404Spjd return (1); 6465168404Spjd 6466185029Spjd ret = 1; 6467185029Spjd if (stat64(entry.mnt_mountp, &statbuf) != 0) { 6468185029Spjd (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"), 6469185029Spjd cmdname, path, strerror(errno)); 6470185029Spjd goto out; 6471185029Spjd } else if (statbuf.st_ino != path_inode) { 6472185029Spjd (void) fprintf(stderr, gettext("cannot " 6473185029Spjd "%s '%s': not a mountpoint\n"), cmdname, path); 6474185029Spjd goto out; 6475185029Spjd } 6476168404Spjd 6477168404Spjd if (op == OP_SHARE) { 6478185029Spjd char nfs_mnt_prop[ZFS_MAXPROPLEN]; 6479185029Spjd char smbshare_prop[ZFS_MAXPROPLEN]; 6480185029Spjd 6481185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, nfs_mnt_prop, 6482185029Spjd sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0); 6483185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshare_prop, 6484185029Spjd sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0); 6485185029Spjd 6486185029Spjd if (strcmp(nfs_mnt_prop, "off") == 0 && 6487185029Spjd strcmp(smbshare_prop, "off") == 0) { 6488168404Spjd (void) fprintf(stderr, gettext("cannot unshare " 6489168404Spjd "'%s': legacy share\n"), path); 6490252732Smm#ifdef illumos 6491168404Spjd (void) fprintf(stderr, gettext("use " 6492168404Spjd "unshare(1M) to unshare this filesystem\n")); 6493252732Smm#endif 6494185029Spjd } else if (!zfs_is_shared(zhp)) { 6495168404Spjd (void) fprintf(stderr, gettext("cannot unshare '%s': " 6496168404Spjd "not currently shared\n"), path); 6497168404Spjd } else { 6498185029Spjd ret = zfs_unshareall_bypath(zhp, path); 6499168404Spjd } 6500168404Spjd } else { 6501185029Spjd char mtpt_prop[ZFS_MAXPROPLEN]; 6502185029Spjd 6503185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop, 6504185029Spjd sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0); 6505185029Spjd 6506168404Spjd if (is_manual) { 6507168404Spjd ret = zfs_unmount(zhp, NULL, flags); 6508185029Spjd } else if (strcmp(mtpt_prop, "legacy") == 0) { 6509168404Spjd (void) fprintf(stderr, gettext("cannot unmount " 6510168404Spjd "'%s': legacy mountpoint\n"), 6511168404Spjd zfs_get_name(zhp)); 6512252732Smm (void) fprintf(stderr, gettext("use umount(8) " 6513168404Spjd "to unmount this filesystem\n")); 6514168404Spjd } else { 6515168404Spjd ret = zfs_unmountall(zhp, flags); 6516168404Spjd } 6517168404Spjd } 6518168404Spjd 6519185029Spjdout: 6520168404Spjd zfs_close(zhp); 6521168404Spjd 6522168404Spjd return (ret != 0); 6523168404Spjd} 6524168404Spjd 6525168404Spjd/* 6526168404Spjd * Generic callback for unsharing or unmounting a filesystem. 6527168404Spjd */ 6528168404Spjdstatic int 6529168404Spjdunshare_unmount(int op, int argc, char **argv) 6530168404Spjd{ 6531168404Spjd int do_all = 0; 6532168404Spjd int flags = 0; 6533168404Spjd int ret = 0; 6534219089Spjd int c; 6535168404Spjd zfs_handle_t *zhp; 6536219089Spjd char nfs_mnt_prop[ZFS_MAXPROPLEN]; 6537185029Spjd char sharesmb[ZFS_MAXPROPLEN]; 6538168404Spjd 6539168404Spjd /* check options */ 6540168404Spjd while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) { 6541168404Spjd switch (c) { 6542168404Spjd case 'a': 6543168404Spjd do_all = 1; 6544168404Spjd break; 6545168404Spjd case 'f': 6546168404Spjd flags = MS_FORCE; 6547168404Spjd break; 6548168404Spjd case '?': 6549168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 6550168404Spjd optopt); 6551168404Spjd usage(B_FALSE); 6552168404Spjd } 6553168404Spjd } 6554168404Spjd 6555168404Spjd argc -= optind; 6556168404Spjd argv += optind; 6557168404Spjd 6558168404Spjd if (do_all) { 6559168404Spjd /* 6560168404Spjd * We could make use of zfs_for_each() to walk all datasets in 6561168404Spjd * the system, but this would be very inefficient, especially 6562168404Spjd * since we would have to linearly search /etc/mnttab for each 6563168404Spjd * one. Instead, do one pass through /etc/mnttab looking for 6564168404Spjd * zfs entries and call zfs_unmount() for each one. 6565168404Spjd * 6566168404Spjd * Things get a little tricky if the administrator has created 6567168404Spjd * mountpoints beneath other ZFS filesystems. In this case, we 6568168404Spjd * have to unmount the deepest filesystems first. To accomplish 6569168404Spjd * this, we place all the mountpoints in an AVL tree sorted by 6570168404Spjd * the special type (dataset name), and walk the result in 6571168404Spjd * reverse to make sure to get any snapshots first. 6572168404Spjd */ 6573219089Spjd struct mnttab entry; 6574168404Spjd uu_avl_pool_t *pool; 6575296535Smav uu_avl_t *tree = NULL; 6576168404Spjd unshare_unmount_node_t *node; 6577168404Spjd uu_avl_index_t idx; 6578168404Spjd uu_avl_walk_t *walk; 6579168404Spjd 6580168404Spjd if (argc != 0) { 6581168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 6582168404Spjd usage(B_FALSE); 6583168404Spjd } 6584168404Spjd 6585219089Spjd if (((pool = uu_avl_pool_create("unmount_pool", 6586168404Spjd sizeof (unshare_unmount_node_t), 6587168404Spjd offsetof(unshare_unmount_node_t, un_avlnode), 6588219089Spjd unshare_unmount_compare, UU_DEFAULT)) == NULL) || 6589219089Spjd ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL)) 6590219089Spjd nomem(); 6591168404Spjd 6592219089Spjd rewind(mnttab_file); 6593219089Spjd while (getmntent(mnttab_file, &entry) == 0) { 6594168404Spjd 6595168404Spjd /* ignore non-ZFS entries */ 6596219089Spjd if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) 6597168404Spjd continue; 6598168404Spjd 6599168404Spjd /* ignore snapshots */ 6600219089Spjd if (strchr(entry.mnt_special, '@') != NULL) 6601168404Spjd continue; 6602168404Spjd 6603219089Spjd if ((zhp = zfs_open(g_zfs, entry.mnt_special, 6604168404Spjd ZFS_TYPE_FILESYSTEM)) == NULL) { 6605168404Spjd ret = 1; 6606168404Spjd continue; 6607168404Spjd } 6608168404Spjd 6609307106Smav /* 6610307106Smav * Ignore datasets that are excluded/restricted by 6611307106Smav * parent pool name. 6612307106Smav */ 6613307106Smav if (zpool_skip_pool(zfs_get_pool_name(zhp))) { 6614307106Smav zfs_close(zhp); 6615307106Smav continue; 6616307106Smav } 6617307106Smav 6618185029Spjd switch (op) { 6619185029Spjd case OP_SHARE: 6620185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, 6621219089Spjd nfs_mnt_prop, 6622219089Spjd sizeof (nfs_mnt_prop), 6623185029Spjd NULL, NULL, 0, B_FALSE) == 0); 6624219089Spjd if (strcmp(nfs_mnt_prop, "off") != 0) 6625185029Spjd break; 6626185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, 6627219089Spjd nfs_mnt_prop, 6628219089Spjd sizeof (nfs_mnt_prop), 6629185029Spjd NULL, NULL, 0, B_FALSE) == 0); 6630219089Spjd if (strcmp(nfs_mnt_prop, "off") == 0) 6631185029Spjd continue; 6632185029Spjd break; 6633185029Spjd case OP_MOUNT: 6634185029Spjd /* Ignore legacy mounts */ 6635185029Spjd verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, 6636219089Spjd nfs_mnt_prop, 6637219089Spjd sizeof (nfs_mnt_prop), 6638185029Spjd NULL, NULL, 0, B_FALSE) == 0); 6639219089Spjd if (strcmp(nfs_mnt_prop, "legacy") == 0) 6640185029Spjd continue; 6641185029Spjd /* Ignore canmount=noauto mounts */ 6642185029Spjd if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == 6643185029Spjd ZFS_CANMOUNT_NOAUTO) 6644185029Spjd continue; 6645185029Spjd default: 6646185029Spjd break; 6647168404Spjd } 6648168404Spjd 6649168404Spjd node = safe_malloc(sizeof (unshare_unmount_node_t)); 6650168404Spjd node->un_zhp = zhp; 6651219089Spjd node->un_mountp = safe_strdup(entry.mnt_mountp); 6652168404Spjd 6653168404Spjd uu_avl_node_init(node, &node->un_avlnode, pool); 6654168404Spjd 6655168404Spjd if (uu_avl_find(tree, node, NULL, &idx) == NULL) { 6656168404Spjd uu_avl_insert(tree, node, idx); 6657168404Spjd } else { 6658168404Spjd zfs_close(node->un_zhp); 6659168404Spjd free(node->un_mountp); 6660168404Spjd free(node); 6661168404Spjd } 6662168404Spjd } 6663168404Spjd 6664168404Spjd /* 6665168404Spjd * Walk the AVL tree in reverse, unmounting each filesystem and 6666168404Spjd * removing it from the AVL tree in the process. 6667168404Spjd */ 6668168404Spjd if ((walk = uu_avl_walk_start(tree, 6669219089Spjd UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL) 6670219089Spjd nomem(); 6671168404Spjd 6672168404Spjd while ((node = uu_avl_walk_next(walk)) != NULL) { 6673168404Spjd uu_avl_remove(tree, node); 6674168404Spjd 6675168404Spjd switch (op) { 6676168404Spjd case OP_SHARE: 6677185029Spjd if (zfs_unshareall_bypath(node->un_zhp, 6678168404Spjd node->un_mountp) != 0) 6679168404Spjd ret = 1; 6680168404Spjd break; 6681168404Spjd 6682168404Spjd case OP_MOUNT: 6683168404Spjd if (zfs_unmount(node->un_zhp, 6684168404Spjd node->un_mountp, flags) != 0) 6685168404Spjd ret = 1; 6686168404Spjd break; 6687168404Spjd } 6688168404Spjd 6689168404Spjd zfs_close(node->un_zhp); 6690168404Spjd free(node->un_mountp); 6691168404Spjd free(node); 6692168404Spjd } 6693168404Spjd 6694168404Spjd uu_avl_walk_end(walk); 6695168404Spjd uu_avl_destroy(tree); 6696168404Spjd uu_avl_pool_destroy(pool); 6697168404Spjd 6698168404Spjd } else { 6699168404Spjd if (argc != 1) { 6700168404Spjd if (argc == 0) 6701168404Spjd (void) fprintf(stderr, 6702168404Spjd gettext("missing filesystem argument\n")); 6703168404Spjd else 6704168404Spjd (void) fprintf(stderr, 6705168404Spjd gettext("too many arguments\n")); 6706168404Spjd usage(B_FALSE); 6707168404Spjd } 6708168404Spjd 6709168404Spjd /* 6710168404Spjd * We have an argument, but it may be a full path or a ZFS 6711168404Spjd * filesystem. Pass full paths off to unmount_path() (shared by 6712168404Spjd * manual_unmount), otherwise open the filesystem and pass to 6713168404Spjd * zfs_unmount(). 6714168404Spjd */ 6715168404Spjd if (argv[0][0] == '/') 6716168404Spjd return (unshare_unmount_path(op, argv[0], 6717168404Spjd flags, B_FALSE)); 6718168404Spjd 6719219089Spjd if ((zhp = zfs_open(g_zfs, argv[0], 6720219089Spjd ZFS_TYPE_FILESYSTEM)) == NULL) 6721168404Spjd return (1); 6722168404Spjd 6723219089Spjd verify(zfs_prop_get(zhp, op == OP_SHARE ? 6724219089Spjd ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT, 6725219089Spjd nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL, 6726219089Spjd NULL, 0, B_FALSE) == 0); 6727168404Spjd 6728219089Spjd switch (op) { 6729219089Spjd case OP_SHARE: 6730219089Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, 6731219089Spjd nfs_mnt_prop, 6732219089Spjd sizeof (nfs_mnt_prop), 6733185029Spjd NULL, NULL, 0, B_FALSE) == 0); 6734219089Spjd verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, 6735219089Spjd sharesmb, sizeof (sharesmb), NULL, NULL, 6736219089Spjd 0, B_FALSE) == 0); 6737168404Spjd 6738219089Spjd if (strcmp(nfs_mnt_prop, "off") == 0 && 6739219089Spjd strcmp(sharesmb, "off") == 0) { 6740219089Spjd (void) fprintf(stderr, gettext("cannot " 6741219089Spjd "unshare '%s': legacy share\n"), 6742168404Spjd zfs_get_name(zhp)); 6743252732Smm#ifdef illumos 6744219089Spjd (void) fprintf(stderr, gettext("use " 6745219089Spjd "unshare(1M) to unshare this " 6746219089Spjd "filesystem\n")); 6747252732Smm#endif 6748168404Spjd ret = 1; 6749219089Spjd } else if (!zfs_is_shared(zhp)) { 6750168404Spjd (void) fprintf(stderr, gettext("cannot " 6751219089Spjd "unshare '%s': not currently " 6752219089Spjd "shared\n"), zfs_get_name(zhp)); 6753219089Spjd ret = 1; 6754219089Spjd } else if (zfs_unshareall(zhp) != 0) { 6755219089Spjd ret = 1; 6756219089Spjd } 6757219089Spjd break; 6758219089Spjd 6759219089Spjd case OP_MOUNT: 6760219089Spjd if (strcmp(nfs_mnt_prop, "legacy") == 0) { 6761219089Spjd (void) fprintf(stderr, gettext("cannot " 6762219089Spjd "unmount '%s': legacy " 6763219089Spjd "mountpoint\n"), zfs_get_name(zhp)); 6764219089Spjd (void) fprintf(stderr, gettext("use " 6765252732Smm "umount(8) to unmount this " 6766219089Spjd "filesystem\n")); 6767219089Spjd ret = 1; 6768219089Spjd } else if (!zfs_is_mounted(zhp, NULL)) { 6769219089Spjd (void) fprintf(stderr, gettext("cannot " 6770219089Spjd "unmount '%s': not currently " 6771219089Spjd "mounted\n"), 6772168404Spjd zfs_get_name(zhp)); 6773168404Spjd ret = 1; 6774219089Spjd } else if (zfs_unmountall(zhp, flags) != 0) { 6775168404Spjd ret = 1; 6776168404Spjd } 6777219089Spjd break; 6778168404Spjd } 6779168404Spjd 6780168404Spjd zfs_close(zhp); 6781168404Spjd } 6782168404Spjd 6783168404Spjd return (ret); 6784168404Spjd} 6785168404Spjd 6786168404Spjd/* 6787168404Spjd * zfs unmount -a 6788168404Spjd * zfs unmount filesystem 6789168404Spjd * 6790168404Spjd * Unmount all filesystems, or a specific ZFS filesystem. 6791168404Spjd */ 6792168404Spjdstatic int 6793168404Spjdzfs_do_unmount(int argc, char **argv) 6794168404Spjd{ 6795168404Spjd return (unshare_unmount(OP_MOUNT, argc, argv)); 6796168404Spjd} 6797168404Spjd 6798168404Spjd/* 6799168404Spjd * zfs unshare -a 6800168404Spjd * zfs unshare filesystem 6801168404Spjd * 6802168404Spjd * Unshare all filesystems, or a specific ZFS filesystem. 6803168404Spjd */ 6804168404Spjdstatic int 6805168404Spjdzfs_do_unshare(int argc, char **argv) 6806168404Spjd{ 6807168404Spjd return (unshare_unmount(OP_SHARE, argc, argv)); 6808168404Spjd} 6809168404Spjd 6810168404Spjd/* 6811168404Spjd * Attach/detach the given dataset to/from the given jail 6812168404Spjd */ 6813168404Spjd/* ARGSUSED */ 6814168404Spjdstatic int 6815168404Spjddo_jail(int argc, char **argv, int attach) 6816168404Spjd{ 6817168404Spjd zfs_handle_t *zhp; 6818168404Spjd int jailid, ret; 6819168404Spjd 6820168404Spjd /* check number of arguments */ 6821168404Spjd if (argc < 3) { 6822168404Spjd (void) fprintf(stderr, gettext("missing argument(s)\n")); 6823168404Spjd usage(B_FALSE); 6824168404Spjd } 6825168404Spjd if (argc > 3) { 6826168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 6827168404Spjd usage(B_FALSE); 6828168404Spjd } 6829168404Spjd 6830240696Sbapt jailid = jail_getid(argv[1]); 6831240696Sbapt if (jailid < 0) { 6832240696Sbapt (void) fprintf(stderr, gettext("invalid jail id or name\n")); 6833168404Spjd usage(B_FALSE); 6834168404Spjd } 6835168404Spjd 6836168404Spjd zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM); 6837168404Spjd if (zhp == NULL) 6838168404Spjd return (1); 6839168404Spjd 6840168404Spjd ret = (zfs_jail(zhp, jailid, attach) != 0); 6841168404Spjd 6842168404Spjd zfs_close(zhp); 6843168404Spjd return (ret); 6844168404Spjd} 6845168404Spjd 6846168404Spjd/* 6847168404Spjd * zfs jail jailid filesystem 6848168404Spjd * 6849168404Spjd * Attach the given dataset to the given jail 6850168404Spjd */ 6851168404Spjd/* ARGSUSED */ 6852168404Spjdstatic int 6853168404Spjdzfs_do_jail(int argc, char **argv) 6854168404Spjd{ 6855168404Spjd 6856168404Spjd return (do_jail(argc, argv, 1)); 6857168404Spjd} 6858168404Spjd 6859168404Spjd/* 6860168404Spjd * zfs unjail jailid filesystem 6861168404Spjd * 6862168404Spjd * Detach the given dataset from the given jail 6863168404Spjd */ 6864168404Spjd/* ARGSUSED */ 6865168404Spjdstatic int 6866168404Spjdzfs_do_unjail(int argc, char **argv) 6867168404Spjd{ 6868168404Spjd 6869168404Spjd return (do_jail(argc, argv, 0)); 6870168404Spjd} 6871168404Spjd 6872168404Spjd/* 6873168404Spjd * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is 6874168404Spjd * 'legacy'. Otherwise, complain that use should be using 'zfs mount'. 6875168404Spjd */ 6876168404Spjdstatic int 6877168404Spjdmanual_mount(int argc, char **argv) 6878168404Spjd{ 6879168404Spjd zfs_handle_t *zhp; 6880168404Spjd char mountpoint[ZFS_MAXPROPLEN]; 6881168404Spjd char mntopts[MNT_LINE_MAX] = { '\0' }; 6882231144Smm int ret = 0; 6883168404Spjd int c; 6884168404Spjd int flags = 0; 6885168404Spjd char *dataset, *path; 6886168404Spjd 6887168404Spjd /* check options */ 6888168404Spjd while ((c = getopt(argc, argv, ":mo:O")) != -1) { 6889168404Spjd switch (c) { 6890168404Spjd case 'o': 6891168404Spjd (void) strlcpy(mntopts, optarg, sizeof (mntopts)); 6892168404Spjd break; 6893168404Spjd case 'O': 6894168404Spjd flags |= MS_OVERLAY; 6895168404Spjd break; 6896168404Spjd case 'm': 6897168404Spjd flags |= MS_NOMNTTAB; 6898168404Spjd break; 6899168404Spjd case ':': 6900168404Spjd (void) fprintf(stderr, gettext("missing argument for " 6901168404Spjd "'%c' option\n"), optopt); 6902168404Spjd usage(B_FALSE); 6903168404Spjd break; 6904168404Spjd case '?': 6905168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 6906168404Spjd optopt); 6907168404Spjd (void) fprintf(stderr, gettext("usage: mount [-o opts] " 6908168404Spjd "<path>\n")); 6909168404Spjd return (2); 6910168404Spjd } 6911168404Spjd } 6912168404Spjd 6913168404Spjd argc -= optind; 6914168404Spjd argv += optind; 6915168404Spjd 6916168404Spjd /* check that we only have two arguments */ 6917168404Spjd if (argc != 2) { 6918168404Spjd if (argc == 0) 6919168404Spjd (void) fprintf(stderr, gettext("missing dataset " 6920168404Spjd "argument\n")); 6921168404Spjd else if (argc == 1) 6922168404Spjd (void) fprintf(stderr, 6923168404Spjd gettext("missing mountpoint argument\n")); 6924168404Spjd else 6925168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 6926168404Spjd (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n"); 6927168404Spjd return (2); 6928168404Spjd } 6929168404Spjd 6930168404Spjd dataset = argv[0]; 6931168404Spjd path = argv[1]; 6932168404Spjd 6933168404Spjd /* try to open the dataset */ 6934168404Spjd if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) 6935168404Spjd return (1); 6936168404Spjd 6937168404Spjd (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 6938168404Spjd sizeof (mountpoint), NULL, NULL, 0, B_FALSE); 6939168404Spjd 6940168404Spjd /* check for legacy mountpoint and complain appropriately */ 6941168404Spjd ret = 0; 6942168404Spjd if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 6943168404Spjd if (zmount(dataset, path, flags, MNTTYPE_ZFS, 6944168404Spjd NULL, 0, mntopts, sizeof (mntopts)) != 0) { 6945168404Spjd (void) fprintf(stderr, gettext("mount failed: %s\n"), 6946168404Spjd strerror(errno)); 6947168404Spjd ret = 1; 6948168404Spjd } 6949168404Spjd } else { 6950168404Spjd (void) fprintf(stderr, gettext("filesystem '%s' cannot be " 6951252732Smm "mounted using 'mount -t zfs'\n"), dataset); 6952168404Spjd (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' " 6953168404Spjd "instead.\n"), path); 6954252732Smm (void) fprintf(stderr, gettext("If you must use 'mount -t zfs' " 6955252732Smm "or /etc/fstab, use 'zfs set mountpoint=legacy'.\n")); 6956252732Smm (void) fprintf(stderr, gettext("See zfs(8) for more " 6957168404Spjd "information.\n")); 6958168404Spjd ret = 1; 6959168404Spjd } 6960168404Spjd 6961168404Spjd return (ret); 6962168404Spjd} 6963168404Spjd 6964168404Spjd/* 6965168404Spjd * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow 6966168404Spjd * unmounts of non-legacy filesystems, as this is the dominant administrative 6967168404Spjd * interface. 6968168404Spjd */ 6969168404Spjdstatic int 6970168404Spjdmanual_unmount(int argc, char **argv) 6971168404Spjd{ 6972168404Spjd int flags = 0; 6973168404Spjd int c; 6974168404Spjd 6975168404Spjd /* check options */ 6976168404Spjd while ((c = getopt(argc, argv, "f")) != -1) { 6977168404Spjd switch (c) { 6978168404Spjd case 'f': 6979168404Spjd flags = MS_FORCE; 6980168404Spjd break; 6981168404Spjd case '?': 6982168404Spjd (void) fprintf(stderr, gettext("invalid option '%c'\n"), 6983168404Spjd optopt); 6984168404Spjd (void) fprintf(stderr, gettext("usage: unmount [-f] " 6985168404Spjd "<path>\n")); 6986168404Spjd return (2); 6987168404Spjd } 6988168404Spjd } 6989168404Spjd 6990168404Spjd argc -= optind; 6991168404Spjd argv += optind; 6992168404Spjd 6993168404Spjd /* check arguments */ 6994168404Spjd if (argc != 1) { 6995168404Spjd if (argc == 0) 6996168404Spjd (void) fprintf(stderr, gettext("missing path " 6997168404Spjd "argument\n")); 6998168404Spjd else 6999168404Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 7000168404Spjd (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n")); 7001168404Spjd return (2); 7002168404Spjd } 7003168404Spjd 7004168404Spjd return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE)); 7005168404Spjd} 7006168404Spjd 7007168404Spjdstatic int 7008185029Spjdfind_command_idx(char *command, int *idx) 7009185029Spjd{ 7010185029Spjd int i; 7011185029Spjd 7012185029Spjd for (i = 0; i < NCOMMAND; i++) { 7013185029Spjd if (command_table[i].name == NULL) 7014185029Spjd continue; 7015185029Spjd 7016185029Spjd if (strcmp(command, command_table[i].name) == 0) { 7017185029Spjd *idx = i; 7018185029Spjd return (0); 7019185029Spjd } 7020185029Spjd } 7021185029Spjd return (1); 7022185029Spjd} 7023185029Spjd 7024219089Spjdstatic int 7025219089Spjdzfs_do_diff(int argc, char **argv) 7026219089Spjd{ 7027219089Spjd zfs_handle_t *zhp; 7028219089Spjd int flags = 0; 7029219089Spjd char *tosnap = NULL; 7030219089Spjd char *fromsnap = NULL; 7031219089Spjd char *atp, *copy; 7032231144Smm int err = 0; 7033219089Spjd int c; 7034219089Spjd 7035219089Spjd while ((c = getopt(argc, argv, "FHt")) != -1) { 7036219089Spjd switch (c) { 7037219089Spjd case 'F': 7038219089Spjd flags |= ZFS_DIFF_CLASSIFY; 7039219089Spjd break; 7040219089Spjd case 'H': 7041219089Spjd flags |= ZFS_DIFF_PARSEABLE; 7042219089Spjd break; 7043219089Spjd case 't': 7044219089Spjd flags |= ZFS_DIFF_TIMESTAMP; 7045219089Spjd break; 7046219089Spjd default: 7047219089Spjd (void) fprintf(stderr, 7048219089Spjd gettext("invalid option '%c'\n"), optopt); 7049219089Spjd usage(B_FALSE); 7050219089Spjd } 7051219089Spjd } 7052219089Spjd 7053219089Spjd argc -= optind; 7054219089Spjd argv += optind; 7055219089Spjd 7056219089Spjd if (argc < 1) { 7057219089Spjd (void) fprintf(stderr, 7058332525Smav gettext("must provide at least one snapshot name\n")); 7059219089Spjd usage(B_FALSE); 7060219089Spjd } 7061219089Spjd 7062219089Spjd if (argc > 2) { 7063219089Spjd (void) fprintf(stderr, gettext("too many arguments\n")); 7064219089Spjd usage(B_FALSE); 7065219089Spjd } 7066219089Spjd 7067219089Spjd fromsnap = argv[0]; 7068219089Spjd tosnap = (argc == 2) ? argv[1] : NULL; 7069219089Spjd 7070219089Spjd copy = NULL; 7071219089Spjd if (*fromsnap != '@') 7072219089Spjd copy = strdup(fromsnap); 7073219089Spjd else if (tosnap) 7074219089Spjd copy = strdup(tosnap); 7075219089Spjd if (copy == NULL) 7076219089Spjd usage(B_FALSE); 7077219089Spjd 7078296535Smav if ((atp = strchr(copy, '@')) != NULL) 7079219089Spjd *atp = '\0'; 7080219089Spjd 7081219089Spjd if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL) 7082219089Spjd return (1); 7083219089Spjd 7084219089Spjd free(copy); 7085219089Spjd 7086219089Spjd /* 7087219089Spjd * Ignore SIGPIPE so that the library can give us 7088219089Spjd * information on any failure 7089219089Spjd */ 7090219089Spjd (void) sigignore(SIGPIPE); 7091219089Spjd 7092219089Spjd err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags); 7093219089Spjd 7094219089Spjd zfs_close(zhp); 7095219089Spjd 7096219089Spjd return (err != 0); 7097219089Spjd} 7098219089Spjd 7099339119Smav/* 7100339119Smav * zfs remap <filesystem | volume> 7101339119Smav * 7102339119Smav * Remap the indirect blocks in the given fileystem or volume. 7103339119Smav */ 7104332525Smavstatic int 7105332525Smavzfs_do_remap(int argc, char **argv) 7106332525Smav{ 7107332525Smav const char *fsname; 7108332525Smav int err = 0; 7109339119Smav int c; 7110339119Smav 7111339119Smav /* check options */ 7112339119Smav while ((c = getopt(argc, argv, "")) != -1) { 7113339119Smav switch (c) { 7114339119Smav case '?': 7115339119Smav (void) fprintf(stderr, 7116339119Smav gettext("invalid option '%c'\n"), optopt); 7117339119Smav usage(B_FALSE); 7118339119Smav } 7119339119Smav } 7120339119Smav 7121332525Smav if (argc != 2) { 7122332525Smav (void) fprintf(stderr, gettext("wrong number of arguments\n")); 7123332525Smav usage(B_FALSE); 7124332525Smav } 7125332525Smav 7126332525Smav fsname = argv[1]; 7127332525Smav err = zfs_remap_indirects(g_zfs, fsname); 7128332525Smav 7129332525Smav return (err); 7130332525Smav} 7131332525Smav 7132260183Sdelphij/* 7133260183Sdelphij * zfs bookmark <fs@snap> <fs#bmark> 7134260183Sdelphij * 7135260183Sdelphij * Creates a bookmark with the given name from the given snapshot. 7136260183Sdelphij */ 7137260183Sdelphijstatic int 7138260183Sdelphijzfs_do_bookmark(int argc, char **argv) 7139260183Sdelphij{ 7140307108Smav char snapname[ZFS_MAX_DATASET_NAME_LEN]; 7141260183Sdelphij zfs_handle_t *zhp; 7142260183Sdelphij nvlist_t *nvl; 7143260183Sdelphij int ret = 0; 7144260183Sdelphij int c; 7145260183Sdelphij 7146260183Sdelphij /* check options */ 7147260183Sdelphij while ((c = getopt(argc, argv, "")) != -1) { 7148260183Sdelphij switch (c) { 7149260183Sdelphij case '?': 7150260183Sdelphij (void) fprintf(stderr, 7151260183Sdelphij gettext("invalid option '%c'\n"), optopt); 7152260183Sdelphij goto usage; 7153260183Sdelphij } 7154260183Sdelphij } 7155260183Sdelphij 7156260183Sdelphij argc -= optind; 7157260183Sdelphij argv += optind; 7158260183Sdelphij 7159260183Sdelphij /* check number of arguments */ 7160260183Sdelphij if (argc < 1) { 7161260183Sdelphij (void) fprintf(stderr, gettext("missing snapshot argument\n")); 7162260183Sdelphij goto usage; 7163260183Sdelphij } 7164260183Sdelphij if (argc < 2) { 7165260183Sdelphij (void) fprintf(stderr, gettext("missing bookmark argument\n")); 7166260183Sdelphij goto usage; 7167260183Sdelphij } 7168260183Sdelphij 7169260183Sdelphij if (strchr(argv[1], '#') == NULL) { 7170260183Sdelphij (void) fprintf(stderr, 7171260183Sdelphij gettext("invalid bookmark name '%s' -- " 7172260183Sdelphij "must contain a '#'\n"), argv[1]); 7173260183Sdelphij goto usage; 7174260183Sdelphij } 7175260183Sdelphij 7176260183Sdelphij if (argv[0][0] == '@') { 7177260183Sdelphij /* 7178260183Sdelphij * Snapshot name begins with @. 7179260183Sdelphij * Default to same fs as bookmark. 7180260183Sdelphij */ 7181260183Sdelphij (void) strncpy(snapname, argv[1], sizeof (snapname)); 7182260183Sdelphij *strchr(snapname, '#') = '\0'; 7183260183Sdelphij (void) strlcat(snapname, argv[0], sizeof (snapname)); 7184260183Sdelphij } else { 7185260183Sdelphij (void) strncpy(snapname, argv[0], sizeof (snapname)); 7186260183Sdelphij } 7187260183Sdelphij zhp = zfs_open(g_zfs, snapname, ZFS_TYPE_SNAPSHOT); 7188260183Sdelphij if (zhp == NULL) 7189260183Sdelphij goto usage; 7190260183Sdelphij zfs_close(zhp); 7191260183Sdelphij 7192260183Sdelphij 7193260183Sdelphij nvl = fnvlist_alloc(); 7194260183Sdelphij fnvlist_add_string(nvl, argv[1], snapname); 7195260183Sdelphij ret = lzc_bookmark(nvl, NULL); 7196260183Sdelphij fnvlist_free(nvl); 7197260183Sdelphij 7198260183Sdelphij if (ret != 0) { 7199260183Sdelphij const char *err_msg; 7200260183Sdelphij char errbuf[1024]; 7201260183Sdelphij 7202260183Sdelphij (void) snprintf(errbuf, sizeof (errbuf), 7203260183Sdelphij dgettext(TEXT_DOMAIN, 7204260183Sdelphij "cannot create bookmark '%s'"), argv[1]); 7205260183Sdelphij 7206260183Sdelphij switch (ret) { 7207260183Sdelphij case EXDEV: 7208260183Sdelphij err_msg = "bookmark is in a different pool"; 7209260183Sdelphij break; 7210260183Sdelphij case EEXIST: 7211260183Sdelphij err_msg = "bookmark exists"; 7212260183Sdelphij break; 7213260183Sdelphij case EINVAL: 7214260183Sdelphij err_msg = "invalid argument"; 7215260183Sdelphij break; 7216260183Sdelphij case ENOTSUP: 7217260183Sdelphij err_msg = "bookmark feature not enabled"; 7218260183Sdelphij break; 7219268473Sdelphij case ENOSPC: 7220268473Sdelphij err_msg = "out of space"; 7221268473Sdelphij break; 7222260183Sdelphij default: 7223260183Sdelphij err_msg = "unknown error"; 7224260183Sdelphij break; 7225260183Sdelphij } 7226260183Sdelphij (void) fprintf(stderr, "%s: %s\n", errbuf, 7227260183Sdelphij dgettext(TEXT_DOMAIN, err_msg)); 7228260183Sdelphij } 7229260183Sdelphij 7230268473Sdelphij return (ret != 0); 7231260183Sdelphij 7232260183Sdelphijusage: 7233260183Sdelphij usage(B_FALSE); 7234260183Sdelphij return (-1); 7235260183Sdelphij} 7236260183Sdelphij 7237325534Savgstatic int 7238325534Savgzfs_do_channel_program(int argc, char **argv) 7239325534Savg{ 7240325534Savg int ret, fd; 7241325534Savg char c; 7242325534Savg char *progbuf, *filename, *poolname; 7243325534Savg size_t progsize, progread; 7244325534Savg nvlist_t *outnvl; 7245325534Savg uint64_t instrlimit = ZCP_DEFAULT_INSTRLIMIT; 7246325534Savg uint64_t memlimit = ZCP_DEFAULT_MEMLIMIT; 7247329484Smav boolean_t sync_flag = B_TRUE; 7248325534Savg zpool_handle_t *zhp; 7249325534Savg 7250325534Savg /* check options */ 7251325534Savg while (-1 != 7252329484Smav (c = getopt(argc, argv, "nt:(instr-limit)m:(memory-limit)"))) { 7253325534Savg switch (c) { 7254325534Savg case 't': 7255325534Savg case 'm': { 7256325534Savg uint64_t arg; 7257325534Savg char *endp; 7258325534Savg 7259325534Savg errno = 0; 7260325534Savg arg = strtoull(optarg, &endp, 0); 7261325534Savg if (errno != 0 || *endp != '\0') { 7262325534Savg (void) fprintf(stderr, gettext( 7263325534Savg "invalid argument " 7264325534Savg "'%s': expected integer\n"), optarg); 7265325534Savg goto usage; 7266325534Savg } 7267325534Savg 7268325534Savg if (c == 't') { 7269325534Savg if (arg > ZCP_MAX_INSTRLIMIT || arg == 0) { 7270325534Savg (void) fprintf(stderr, gettext( 7271325534Savg "Invalid instruction limit: " 7272325534Savg "%s\n"), optarg); 7273325534Savg return (1); 7274325534Savg } else { 7275325534Savg instrlimit = arg; 7276325534Savg } 7277325534Savg } else { 7278325534Savg ASSERT3U(c, ==, 'm'); 7279325534Savg if (arg > ZCP_MAX_MEMLIMIT || arg == 0) { 7280325534Savg (void) fprintf(stderr, gettext( 7281325534Savg "Invalid memory limit: " 7282325534Savg "%s\n"), optarg); 7283325534Savg return (1); 7284325534Savg } else { 7285325534Savg memlimit = arg; 7286325534Savg } 7287325534Savg } 7288325534Savg break; 7289325534Savg } 7290329484Smav case 'n': { 7291329484Smav sync_flag = B_FALSE; 7292329484Smav break; 7293329484Smav } 7294325534Savg case '?': 7295325534Savg (void) fprintf(stderr, gettext("invalid option '%c'\n"), 7296325534Savg optopt); 7297325534Savg goto usage; 7298325534Savg } 7299325534Savg } 7300325534Savg 7301325534Savg argc -= optind; 7302325534Savg argv += optind; 7303325534Savg 7304325534Savg if (argc < 2) { 7305325534Savg (void) fprintf(stderr, 7306325534Savg gettext("invalid number of arguments\n")); 7307325534Savg goto usage; 7308325534Savg } 7309325534Savg 7310325534Savg poolname = argv[0]; 7311325534Savg filename = argv[1]; 7312325534Savg if (strcmp(filename, "-") == 0) { 7313325534Savg fd = 0; 7314325534Savg filename = "standard input"; 7315325534Savg } else if ((fd = open(filename, O_RDONLY)) < 0) { 7316325534Savg (void) fprintf(stderr, gettext("cannot open '%s': %s\n"), 7317325534Savg filename, strerror(errno)); 7318325534Savg return (1); 7319325534Savg } 7320325534Savg 7321325534Savg if ((zhp = zpool_open(g_zfs, poolname)) == NULL) { 7322325534Savg (void) fprintf(stderr, gettext("cannot open pool '%s'"), 7323325534Savg poolname); 7324325534Savg return (1); 7325325534Savg } 7326325534Savg zpool_close(zhp); 7327325534Savg 7328325534Savg /* 7329325534Savg * Read in the channel program, expanding the program buffer as 7330325534Savg * necessary. 7331325534Savg */ 7332325534Savg progread = 0; 7333325534Savg progsize = 1024; 7334325534Savg progbuf = safe_malloc(progsize); 7335325534Savg do { 7336325534Savg ret = read(fd, progbuf + progread, progsize - progread); 7337325534Savg progread += ret; 7338325534Savg if (progread == progsize && ret > 0) { 7339325534Savg progsize *= 2; 7340325534Savg progbuf = safe_realloc(progbuf, progsize); 7341325534Savg } 7342325534Savg } while (ret > 0); 7343325534Savg 7344325534Savg if (fd != 0) 7345325534Savg (void) close(fd); 7346325534Savg if (ret < 0) { 7347325534Savg free(progbuf); 7348325534Savg (void) fprintf(stderr, 7349325534Savg gettext("cannot read '%s': %s\n"), 7350325534Savg filename, strerror(errno)); 7351325534Savg return (1); 7352325534Savg } 7353325534Savg progbuf[progread] = '\0'; 7354325534Savg 7355325534Savg /* 7356325534Savg * Any remaining arguments are passed as arguments to the lua script as 7357325534Savg * a string array: 7358325534Savg * { 7359325534Savg * "argv" -> [ "arg 1", ... "arg n" ], 7360325534Savg * } 7361325534Savg */ 7362325534Savg nvlist_t *argnvl = fnvlist_alloc(); 7363325534Savg fnvlist_add_string_array(argnvl, ZCP_ARG_CLIARGV, argv + 2, argc - 2); 7364325534Savg 7365329484Smav if (sync_flag) { 7366329484Smav ret = lzc_channel_program(poolname, progbuf, 7367329484Smav instrlimit, memlimit, argnvl, &outnvl); 7368329484Smav } else { 7369329484Smav ret = lzc_channel_program_nosync(poolname, progbuf, 7370329484Smav instrlimit, memlimit, argnvl, &outnvl); 7371329484Smav } 7372325534Savg 7373325534Savg if (ret != 0) { 7374325534Savg /* 7375325534Savg * On error, report the error message handed back by lua if one 7376325534Savg * exists. Otherwise, generate an appropriate error message, 7377325534Savg * falling back on strerror() for an unexpected return code. 7378325534Savg */ 7379325534Savg char *errstring = NULL; 7380325534Savg if (nvlist_exists(outnvl, ZCP_RET_ERROR)) { 7381325534Savg (void) nvlist_lookup_string(outnvl, 7382325534Savg ZCP_RET_ERROR, &errstring); 7383325534Savg if (errstring == NULL) 7384325534Savg errstring = strerror(ret); 7385325534Savg } else { 7386325534Savg switch (ret) { 7387325534Savg case EINVAL: 7388325534Savg errstring = 7389325534Savg "Invalid instruction or memory limit."; 7390325534Savg break; 7391325534Savg case ENOMEM: 7392325534Savg errstring = "Return value too large."; 7393325534Savg break; 7394325534Savg case ENOSPC: 7395325534Savg errstring = "Memory limit exhausted."; 7396325534Savg break; 7397325534Savg#ifdef illumos 7398325534Savg case ETIME: 7399325534Savg#else 7400325534Savg case ETIMEDOUT: 7401325534Savg#endif 7402325534Savg errstring = "Timed out."; 7403325534Savg break; 7404325534Savg case EPERM: 7405325534Savg errstring = "Permission denied. Channel " 7406325534Savg "programs must be run as root."; 7407325534Savg break; 7408325534Savg default: 7409325534Savg errstring = strerror(ret); 7410325534Savg } 7411325534Savg } 7412325534Savg (void) fprintf(stderr, 7413325534Savg gettext("Channel program execution failed:\n%s\n"), 7414325534Savg errstring); 7415325534Savg } else { 7416325534Savg (void) printf("Channel program fully executed "); 7417325534Savg if (nvlist_empty(outnvl)) { 7418325534Savg (void) printf("with no return value.\n"); 7419325534Savg } else { 7420325534Savg (void) printf("with return value:\n"); 7421325534Savg dump_nvlist(outnvl, 4); 7422325534Savg } 7423325534Savg } 7424325534Savg 7425325534Savg free(progbuf); 7426325534Savg fnvlist_free(outnvl); 7427325534Savg fnvlist_free(argnvl); 7428325534Savg return (ret != 0); 7429325534Savg 7430325534Savgusage: 7431325534Savg usage(B_FALSE); 7432325534Savg return (-1); 7433325534Savg} 7434325534Savg 7435168404Spjdint 7436168404Spjdmain(int argc, char **argv) 7437168404Spjd{ 7438231144Smm int ret = 0; 7439168404Spjd int i; 7440168404Spjd char *progname; 7441168404Spjd char *cmdname; 7442168404Spjd 7443168404Spjd (void) setlocale(LC_ALL, ""); 7444168404Spjd (void) textdomain(TEXT_DOMAIN); 7445168404Spjd 7446168404Spjd opterr = 0; 7447168404Spjd 7448168404Spjd if ((g_zfs = libzfs_init()) == NULL) { 7449168404Spjd (void) fprintf(stderr, gettext("internal error: failed to " 7450168404Spjd "initialize ZFS library\n")); 7451168404Spjd return (1); 7452168404Spjd } 7453168404Spjd 7454248571Smm zfs_save_arguments(argc, argv, history_str, sizeof (history_str)); 7455185029Spjd 7456168404Spjd libzfs_print_on_error(g_zfs, B_TRUE); 7457168404Spjd 7458168404Spjd if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) { 7459168404Spjd (void) fprintf(stderr, gettext("internal error: unable to " 7460168404Spjd "open %s\n"), MNTTAB); 7461168404Spjd return (1); 7462168404Spjd } 7463168404Spjd 7464168404Spjd /* 7465168404Spjd * This command also doubles as the /etc/fs mount and unmount program. 7466168404Spjd * Determine if we should take this behavior based on argv[0]. 7467168404Spjd */ 7468168404Spjd progname = basename(argv[0]); 7469168404Spjd if (strcmp(progname, "mount") == 0) { 7470168404Spjd ret = manual_mount(argc, argv); 7471168404Spjd } else if (strcmp(progname, "umount") == 0) { 7472168404Spjd ret = manual_unmount(argc, argv); 7473168404Spjd } else { 7474168404Spjd /* 7475168404Spjd * Make sure the user has specified some command. 7476168404Spjd */ 7477168404Spjd if (argc < 2) { 7478168404Spjd (void) fprintf(stderr, gettext("missing command\n")); 7479168404Spjd usage(B_FALSE); 7480168404Spjd } 7481168404Spjd 7482168404Spjd cmdname = argv[1]; 7483168404Spjd 7484168404Spjd /* 7485168404Spjd * The 'umount' command is an alias for 'unmount' 7486168404Spjd */ 7487168404Spjd if (strcmp(cmdname, "umount") == 0) 7488168404Spjd cmdname = "unmount"; 7489168404Spjd 7490168404Spjd /* 7491168404Spjd * The 'recv' command is an alias for 'receive' 7492168404Spjd */ 7493168404Spjd if (strcmp(cmdname, "recv") == 0) 7494168404Spjd cmdname = "receive"; 7495168404Spjd 7496168404Spjd /* 7497256999Ssmh * The 'snap' command is an alias for 'snapshot' 7498256999Ssmh */ 7499256999Ssmh if (strcmp(cmdname, "snap") == 0) 7500256999Ssmh cmdname = "snapshot"; 7501256999Ssmh 7502256999Ssmh /* 7503168404Spjd * Special case '-?' 7504168404Spjd */ 7505168404Spjd if (strcmp(cmdname, "-?") == 0) 7506168404Spjd usage(B_TRUE); 7507168404Spjd 7508168404Spjd /* 7509168404Spjd * Run the appropriate command. 7510168404Spjd */ 7511209962Smm libzfs_mnttab_cache(g_zfs, B_TRUE); 7512185029Spjd if (find_command_idx(cmdname, &i) == 0) { 7513185029Spjd current_command = &command_table[i]; 7514185029Spjd ret = command_table[i].func(argc - 1, argv + 1); 7515185029Spjd } else if (strchr(cmdname, '=') != NULL) { 7516185029Spjd verify(find_command_idx("set", &i) == 0); 7517185029Spjd current_command = &command_table[i]; 7518185029Spjd ret = command_table[i].func(argc, argv); 7519185029Spjd } else { 7520168404Spjd (void) fprintf(stderr, gettext("unrecognized " 7521168404Spjd "command '%s'\n"), cmdname); 7522168404Spjd usage(B_FALSE); 7523168404Spjd } 7524209962Smm libzfs_mnttab_cache(g_zfs, B_FALSE); 7525168404Spjd } 7526168404Spjd 7527168404Spjd (void) fclose(mnttab_file); 7528168404Spjd 7529248571Smm if (ret == 0 && log_history) 7530248571Smm (void) zpool_log_history(g_zfs, history_str); 7531248571Smm 7532168404Spjd libzfs_fini(g_zfs); 7533168404Spjd 7534168404Spjd /* 7535168404Spjd * The 'ZFS_ABORT' environment variable causes us to dump core on exit 7536168404Spjd * for the purposes of running ::findleaks. 7537168404Spjd */ 7538168404Spjd if (getenv("ZFS_ABORT") != NULL) { 7539168404Spjd (void) printf("dumping core by request\n"); 7540168404Spjd abort(); 7541168404Spjd } 7542168404Spjd 7543168404Spjd return (ret); 7544168404Spjd} 7545