Path: blob/main/sys/contrib/openzfs/cmd/zfs/zfs_main.c
48287 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/2122/*23* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.24* Copyright (c) 2011, 2020 by Delphix. All rights reserved.25* Copyright 2012 Milan Jurik. All rights reserved.26* Copyright (c) 2012, Joyent, Inc. All rights reserved.27* Copyright (c) 2013 Steven Hartland. All rights reserved.28* Copyright 2016 Igor Kozhukhov <[email protected]>.29* Copyright 2016 Nexenta Systems, Inc.30* Copyright (c) 2019 Datto Inc.31* Copyright (c) 2019, loli10K <[email protected]>32* Copyright 2019 Joyent, Inc.33* Copyright (c) 2019, 2020 by Christian Schwarz. All rights reserved.34*/3536#include <assert.h>37#include <ctype.h>38#include <sys/debug.h>39#include <dirent.h>40#include <errno.h>41#include <getopt.h>42#include <libgen.h>43#include <libintl.h>44#include <libuutil.h>45#include <libnvpair.h>46#include <locale.h>47#include <stddef.h>48#include <stdio.h>49#include <stdlib.h>50#include <string.h>51#include <unistd.h>52#include <fcntl.h>53#include <zone.h>54#include <grp.h>55#include <pwd.h>56#include <umem.h>57#include <pthread.h>58#include <signal.h>59#include <sys/list.h>60#include <sys/mkdev.h>61#include <sys/mntent.h>62#include <sys/mnttab.h>63#include <sys/mount.h>64#include <sys/stat.h>65#include <sys/fs/zfs.h>66#include <sys/systeminfo.h>67#include <sys/types.h>68#include <time.h>69#include <sys/zfs_project.h>7071#include <libzfs.h>72#include <libzfs_core.h>73#include <zfs_prop.h>74#include <zfs_deleg.h>75#include <libzutil.h>76#ifdef HAVE_IDMAP77#include <aclutils.h>78#include <directory.h>79#endif /* HAVE_IDMAP */8081#include "zfs_iter.h"82#include "zfs_util.h"83#include "zfs_comutil.h"84#include "zfs_projectutil.h"8586libzfs_handle_t *g_zfs;8788static char history_str[HIS_MAX_RECORD_LEN];89static boolean_t log_history = B_TRUE;9091static int zfs_do_clone(int argc, char **argv);92static int zfs_do_create(int argc, char **argv);93static int zfs_do_destroy(int argc, char **argv);94static int zfs_do_get(int argc, char **argv);95static int zfs_do_inherit(int argc, char **argv);96static int zfs_do_list(int argc, char **argv);97static int zfs_do_mount(int argc, char **argv);98static int zfs_do_rename(int argc, char **argv);99static int zfs_do_rollback(int argc, char **argv);100static int zfs_do_set(int argc, char **argv);101static int zfs_do_upgrade(int argc, char **argv);102static int zfs_do_snapshot(int argc, char **argv);103static int zfs_do_unmount(int argc, char **argv);104static int zfs_do_share(int argc, char **argv);105static int zfs_do_unshare(int argc, char **argv);106static int zfs_do_send(int argc, char **argv);107static int zfs_do_receive(int argc, char **argv);108static int zfs_do_promote(int argc, char **argv);109static int zfs_do_userspace(int argc, char **argv);110static int zfs_do_allow(int argc, char **argv);111static int zfs_do_unallow(int argc, char **argv);112static int zfs_do_hold(int argc, char **argv);113static int zfs_do_holds(int argc, char **argv);114static int zfs_do_release(int argc, char **argv);115static int zfs_do_diff(int argc, char **argv);116static int zfs_do_bookmark(int argc, char **argv);117static int zfs_do_channel_program(int argc, char **argv);118static int zfs_do_load_key(int argc, char **argv);119static int zfs_do_unload_key(int argc, char **argv);120static int zfs_do_change_key(int argc, char **argv);121static int zfs_do_project(int argc, char **argv);122static int zfs_do_version(int argc, char **argv);123static int zfs_do_redact(int argc, char **argv);124static int zfs_do_rewrite(int argc, char **argv);125static int zfs_do_wait(int argc, char **argv);126127#ifdef __FreeBSD__128static int zfs_do_jail(int argc, char **argv);129static int zfs_do_unjail(int argc, char **argv);130#endif131132#ifdef __linux__133static int zfs_do_zone(int argc, char **argv);134static int zfs_do_unzone(int argc, char **argv);135#endif136137static int zfs_do_help(int argc, char **argv);138139enum zfs_options {140ZFS_OPTION_JSON_NUMS_AS_INT = 1024141};142143/*144* Enable a reasonable set of defaults for libumem debugging on DEBUG builds.145*/146147#ifdef DEBUG148const char *149_umem_debug_init(void)150{151return ("default,verbose"); /* $UMEM_DEBUG setting */152}153154const char *155_umem_logging_init(void)156{157return ("fail,contents"); /* $UMEM_LOGGING setting */158}159#endif160161typedef enum {162HELP_CLONE,163HELP_CREATE,164HELP_DESTROY,165HELP_GET,166HELP_INHERIT,167HELP_UPGRADE,168HELP_LIST,169HELP_MOUNT,170HELP_PROMOTE,171HELP_RECEIVE,172HELP_RENAME,173HELP_ROLLBACK,174HELP_SEND,175HELP_SET,176HELP_SHARE,177HELP_SNAPSHOT,178HELP_UNMOUNT,179HELP_UNSHARE,180HELP_ALLOW,181HELP_UNALLOW,182HELP_USERSPACE,183HELP_GROUPSPACE,184HELP_PROJECTSPACE,185HELP_PROJECT,186HELP_HOLD,187HELP_HOLDS,188HELP_RELEASE,189HELP_DIFF,190HELP_BOOKMARK,191HELP_CHANNEL_PROGRAM,192HELP_LOAD_KEY,193HELP_UNLOAD_KEY,194HELP_CHANGE_KEY,195HELP_VERSION,196HELP_REDACT,197HELP_REWRITE,198HELP_JAIL,199HELP_UNJAIL,200HELP_WAIT,201HELP_ZONE,202HELP_UNZONE,203} zfs_help_t;204205typedef struct zfs_command {206const char *name;207int (*func)(int argc, char **argv);208zfs_help_t usage;209} zfs_command_t;210211/*212* Master command table. Each ZFS command has a name, associated function, and213* usage message. The usage messages need to be internationalized, so we have214* to have a function to return the usage message based on a command index.215*216* These commands are organized according to how they are displayed in the usage217* message. An empty command (one with a NULL name) indicates an empty line in218* the generic usage message.219*/220static zfs_command_t command_table[] = {221{ "version", zfs_do_version, HELP_VERSION },222{ NULL },223{ "create", zfs_do_create, HELP_CREATE },224{ "destroy", zfs_do_destroy, HELP_DESTROY },225{ NULL },226{ "snapshot", zfs_do_snapshot, HELP_SNAPSHOT },227{ "rollback", zfs_do_rollback, HELP_ROLLBACK },228{ "clone", zfs_do_clone, HELP_CLONE },229{ "promote", zfs_do_promote, HELP_PROMOTE },230{ "rename", zfs_do_rename, HELP_RENAME },231{ "bookmark", zfs_do_bookmark, HELP_BOOKMARK },232{ "diff", zfs_do_diff, HELP_DIFF },233{ NULL },234{ "list", zfs_do_list, HELP_LIST },235{ NULL },236{ "set", zfs_do_set, HELP_SET },237{ "get", zfs_do_get, HELP_GET },238{ "inherit", zfs_do_inherit, HELP_INHERIT },239{ "upgrade", zfs_do_upgrade, HELP_UPGRADE },240{ NULL },241{ "userspace", zfs_do_userspace, HELP_USERSPACE },242{ "groupspace", zfs_do_userspace, HELP_GROUPSPACE },243{ "projectspace", zfs_do_userspace, HELP_PROJECTSPACE },244{ NULL },245{ "project", zfs_do_project, HELP_PROJECT },246{ NULL },247{ "mount", zfs_do_mount, HELP_MOUNT },248{ "unmount", zfs_do_unmount, HELP_UNMOUNT },249{ "share", zfs_do_share, HELP_SHARE },250{ "unshare", zfs_do_unshare, HELP_UNSHARE },251{ NULL },252{ "send", zfs_do_send, HELP_SEND },253{ "receive", zfs_do_receive, HELP_RECEIVE },254{ "redact", zfs_do_redact, HELP_REDACT },255{ NULL },256{ "allow", zfs_do_allow, HELP_ALLOW },257{ "unallow", zfs_do_unallow, HELP_UNALLOW },258{ NULL },259{ "hold", zfs_do_hold, HELP_HOLD },260{ "holds", zfs_do_holds, HELP_HOLDS },261{ "release", zfs_do_release, HELP_RELEASE },262{ NULL },263{ "load-key", zfs_do_load_key, HELP_LOAD_KEY },264{ "unload-key", zfs_do_unload_key, HELP_UNLOAD_KEY },265{ "change-key", zfs_do_change_key, HELP_CHANGE_KEY },266{ NULL },267{ "program", zfs_do_channel_program, HELP_CHANNEL_PROGRAM },268{ "rewrite", zfs_do_rewrite, HELP_REWRITE },269{ "wait", zfs_do_wait, HELP_WAIT },270271#ifdef __FreeBSD__272{ NULL },273{ "jail", zfs_do_jail, HELP_JAIL },274{ "unjail", zfs_do_unjail, HELP_UNJAIL },275#endif276277#ifdef __linux__278{ NULL },279{ "zone", zfs_do_zone, HELP_ZONE },280{ "unzone", zfs_do_unzone, HELP_UNZONE },281#endif282};283284#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))285286#define MAX_CMD_LEN 256287288zfs_command_t *current_command;289290static const char *291get_usage(zfs_help_t idx)292{293switch (idx) {294case HELP_CLONE:295return (gettext("\tclone [-p] [-o property=value] ... "296"<snapshot> <filesystem|volume>\n"));297case HELP_CREATE:298return (gettext("\tcreate [-Pnpuv] [-o property=value] ... "299"<filesystem>\n"300"\tcreate [-Pnpsv] [-b blocksize] [-o property=value] ... "301"-V <size> <volume>\n"));302case HELP_DESTROY:303return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"304"\tdestroy [-dnpRrv] "305"<filesystem|volume>@<snap>[%<snap>][,...]\n"306"\tdestroy <filesystem|volume>#<bookmark>\n"));307case HELP_GET:308return (gettext("\tget [-rHp] [-j [--json-int]] [-d max] "309"[-o \"all\" | field[,...]]\n"310"\t [-t type[,...]] [-s source[,...]]\n"311"\t <\"all\" | property[,...]> "312"[filesystem|volume|snapshot|bookmark] ...\n"));313case HELP_INHERIT:314return (gettext("\tinherit [-rS] <property> "315"<filesystem|volume|snapshot> ...\n"));316case HELP_UPGRADE:317return (gettext("\tupgrade [-v]\n"318"\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));319case HELP_LIST:320return (gettext("\tlist [-Hp] [-j [--json-int]] [-r|-d max] "321"[-o property[,...]] [-s property]...\n\t "322"[-S property]... [-t type[,...]] "323"[filesystem|volume|snapshot] ...\n"));324case HELP_MOUNT:325return (gettext("\tmount [-j]\n"326"\tmount [-flvO] [-o opts] <-a|-R filesystem|"327"filesystem>\n"));328case HELP_PROMOTE:329return (gettext("\tpromote <clone-filesystem>\n"));330case HELP_RECEIVE:331return (gettext("\treceive [-vMnsFhu] "332"[-o <property>=<value>] ... [-x <property>] ...\n"333"\t <filesystem|volume|snapshot>\n"334"\treceive [-vMnsFhu] [-o <property>=<value>] ... "335"[-x <property>] ... \n"336"\t [-d | -e] <filesystem>\n"337"\treceive -A <filesystem|volume>\n"));338case HELP_RENAME:339return (gettext("\trename [-f] <filesystem|volume|snapshot> "340"<filesystem|volume|snapshot>\n"341"\trename -p [-f] <filesystem|volume> <filesystem|volume>\n"342"\trename -u [-f] <filesystem> <filesystem>\n"343"\trename -r <snapshot> <snapshot>\n"));344case HELP_ROLLBACK:345return (gettext("\trollback [-rRf] <snapshot>\n"));346case HELP_SEND:347return (gettext("\tsend [-DLPbcehnpsVvw] "348"[-i|-I snapshot]\n"349"\t [-R [-X dataset[,dataset]...]] <snapshot>\n"350"\tsend [-DnVvPLecw] [-i snapshot|bookmark] "351"<filesystem|volume|snapshot>\n"352"\tsend [-DnPpVvLec] [-i bookmark|snapshot] "353"--redact <bookmark> <snapshot>\n"354"\tsend [-nVvPe] -t <receive_resume_token>\n"355"\tsend [-PnVv] --saved filesystem\n"));356case HELP_SET:357return (gettext("\tset [-u] <property=value> ... "358"<filesystem|volume|snapshot> ...\n"));359case HELP_SHARE:360return (gettext("\tshare [-l] <-a [nfs|smb] | filesystem>\n"));361case HELP_SNAPSHOT:362return (gettext("\tsnapshot [-r] [-o property=value] ... "363"<filesystem|volume>@<snap> ...\n"));364case HELP_UNMOUNT:365return (gettext("\tunmount [-fu] "366"<-a | filesystem|mountpoint>\n"));367case HELP_UNSHARE:368return (gettext("\tunshare "369"<-a [nfs|smb] | filesystem|mountpoint>\n"));370case HELP_ALLOW:371return (gettext("\tallow <filesystem|volume>\n"372"\tallow [-ldug] "373"<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n"374"\t <filesystem|volume>\n"375"\tallow [-ld] -e <perm|@setname>[,...] "376"<filesystem|volume>\n"377"\tallow -c <perm|@setname>[,...] <filesystem|volume>\n"378"\tallow -s @setname <perm|@setname>[,...] "379"<filesystem|volume>\n"));380case HELP_UNALLOW:381return (gettext("\tunallow [-rldug] "382"<\"everyone\"|user|group>[,...]\n"383"\t [<perm|@setname>[,...]] <filesystem|volume>\n"384"\tunallow [-rld] -e [<perm|@setname>[,...]] "385"<filesystem|volume>\n"386"\tunallow [-r] -c [<perm|@setname>[,...]] "387"<filesystem|volume>\n"388"\tunallow [-r] -s @setname [<perm|@setname>[,...]] "389"<filesystem|volume>\n"));390case HELP_USERSPACE:391return (gettext("\tuserspace [-Hinp] [-o field[,...]] "392"[-s field] ...\n"393"\t [-S field] ... [-t type[,...]] "394"<filesystem|snapshot|path>\n"));395case HELP_GROUPSPACE:396return (gettext("\tgroupspace [-Hinp] [-o field[,...]] "397"[-s field] ...\n"398"\t [-S field] ... [-t type[,...]] "399"<filesystem|snapshot|path>\n"));400case HELP_PROJECTSPACE:401return (gettext("\tprojectspace [-Hp] [-o field[,...]] "402"[-s field] ... \n"403"\t [-S field] ... <filesystem|snapshot|path>\n"));404case HELP_PROJECT:405return (gettext("\tproject [-d|-r] <directory|file ...>\n"406"\tproject -c [-0] [-d|-r] [-p id] <directory|file ...>\n"407"\tproject -C [-k] [-r] <directory ...>\n"408"\tproject [-p id] [-r] [-s] <directory ...>\n"));409case HELP_HOLD:410return (gettext("\thold [-r] <tag> <snapshot> ...\n"));411case HELP_HOLDS:412return (gettext("\tholds [-rHp] <snapshot> ...\n"));413case HELP_RELEASE:414return (gettext("\trelease [-r] <tag> <snapshot> ...\n"));415case HELP_DIFF:416return (gettext("\tdiff [-FHth] <snapshot> "417"[snapshot|filesystem]\n"));418case HELP_BOOKMARK:419return (gettext("\tbookmark <snapshot|bookmark> "420"<newbookmark>\n"));421case HELP_CHANNEL_PROGRAM:422return (gettext("\tprogram [-jn] [-t <instruction limit>] "423"[-m <memory limit (b)>]\n"424"\t <pool> <program file> [lua args...]\n"));425case HELP_LOAD_KEY:426return (gettext("\tload-key [-rn] [-L <keylocation>] "427"<-a | filesystem|volume>\n"));428case HELP_UNLOAD_KEY:429return (gettext("\tunload-key [-r] "430"<-a | filesystem|volume>\n"));431case HELP_CHANGE_KEY:432return (gettext("\tchange-key [-l] [-o keyformat=<value>]\n"433"\t [-o keylocation=<value>] [-o pbkdf2iters=<value>]\n"434"\t <filesystem|volume>\n"435"\tchange-key -i [-l] <filesystem|volume>\n"));436case HELP_VERSION:437return (gettext("\tversion [-j]\n"));438case HELP_REDACT:439return (gettext("\tredact <snapshot> <bookmark> "440"<redaction_snapshot> ...\n"));441case HELP_REWRITE:442return (gettext("\trewrite [-Prvx] [-o <offset>] [-l <length>] "443"<directory|file ...>\n"));444case HELP_JAIL:445return (gettext("\tjail <jailid|jailname> <filesystem>\n"));446case HELP_UNJAIL:447return (gettext("\tunjail <jailid|jailname> <filesystem>\n"));448case HELP_WAIT:449return (gettext("\twait [-t <activity>] <filesystem>\n"));450case HELP_ZONE:451return (gettext("\tzone <nsfile> <filesystem>\n"));452case HELP_UNZONE:453return (gettext("\tunzone <nsfile> <filesystem>\n"));454default:455__builtin_unreachable();456}457}458459void460nomem(void)461{462(void) fprintf(stderr, gettext("internal error: out of memory\n"));463exit(1);464}465466/*467* Utility function to guarantee malloc() success.468*/469470void *471safe_malloc(size_t size)472{473void *data;474475if ((data = calloc(1, size)) == NULL)476nomem();477478return (data);479}480481static void *482safe_realloc(void *data, size_t size)483{484void *newp;485if ((newp = realloc(data, size)) == NULL) {486free(data);487nomem();488}489490return (newp);491}492493static char *494safe_strdup(const char *str)495{496char *dupstr = strdup(str);497498if (dupstr == NULL)499nomem();500501return (dupstr);502}503504/*505* Callback routine that will print out information for each of506* the properties.507*/508static int509usage_prop_cb(int prop, void *cb)510{511FILE *fp = cb;512513(void) fprintf(fp, "\t%-22s ", zfs_prop_to_name(prop));514515if (zfs_prop_readonly(prop))516(void) fprintf(fp, " NO ");517else518(void) fprintf(fp, "YES ");519520if (zfs_prop_inheritable(prop))521(void) fprintf(fp, " YES ");522else523(void) fprintf(fp, " NO ");524525(void) fprintf(fp, "%s\n", zfs_prop_values(prop) ?: "-");526527return (ZPROP_CONT);528}529530/*531* Display usage message. If we're inside a command, display only the usage for532* that command. Otherwise, iterate over the entire command table and display533* a complete usage message.534*/535static __attribute__((noreturn)) void536usage(boolean_t requested)537{538int i;539boolean_t show_properties = B_FALSE;540FILE *fp = requested ? stdout : stderr;541542if (current_command == NULL) {543544(void) fprintf(fp, gettext("usage: zfs command args ...\n"));545(void) fprintf(fp,546gettext("where 'command' is one of the following:\n\n"));547548for (i = 0; i < NCOMMAND; i++) {549if (command_table[i].name == NULL)550(void) fprintf(fp, "\n");551else552(void) fprintf(fp, "%s",553get_usage(command_table[i].usage));554}555556(void) fprintf(fp, gettext("\nEach dataset is of the form: "557"pool/[dataset/]*dataset[@name]\n"));558} else {559(void) fprintf(fp, gettext("usage:\n"));560(void) fprintf(fp, "%s", get_usage(current_command->usage));561}562563if (current_command != NULL &&564(strcmp(current_command->name, "set") == 0 ||565strcmp(current_command->name, "get") == 0 ||566strcmp(current_command->name, "inherit") == 0 ||567strcmp(current_command->name, "list") == 0))568show_properties = B_TRUE;569570if (show_properties) {571(void) fprintf(fp, "%s",572gettext("\nThe following properties are supported:\n"));573574(void) fprintf(fp, "\n\t%-21s %s %s %s\n\n",575"PROPERTY", "EDIT", "INHERIT", "VALUES");576577/* Iterate over all properties */578(void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE,579ZFS_TYPE_DATASET);580581(void) fprintf(fp, "\t%-22s ", "userused@...");582(void) fprintf(fp, " NO NO <size>\n");583(void) fprintf(fp, "\t%-22s ", "groupused@...");584(void) fprintf(fp, " NO NO <size>\n");585(void) fprintf(fp, "\t%-22s ", "projectused@...");586(void) fprintf(fp, " NO NO <size>\n");587(void) fprintf(fp, "\t%-22s ", "userobjused@...");588(void) fprintf(fp, " NO NO <size>\n");589(void) fprintf(fp, "\t%-22s ", "groupobjused@...");590(void) fprintf(fp, " NO NO <size>\n");591(void) fprintf(fp, "\t%-22s ", "projectobjused@...");592(void) fprintf(fp, " NO NO <size>\n");593(void) fprintf(fp, "\t%-22s ", "userquota@...");594(void) fprintf(fp, "YES NO <size> | none\n");595(void) fprintf(fp, "\t%-22s ", "groupquota@...");596(void) fprintf(fp, "YES NO <size> | none\n");597(void) fprintf(fp, "\t%-22s ", "projectquota@...");598(void) fprintf(fp, "YES NO <size> | none\n");599(void) fprintf(fp, "\t%-22s ", "userobjquota@...");600(void) fprintf(fp, "YES NO <size> | none\n");601(void) fprintf(fp, "\t%-22s ", "groupobjquota@...");602(void) fprintf(fp, "YES NO <size> | none\n");603(void) fprintf(fp, "\t%-22s ", "projectobjquota@...");604(void) fprintf(fp, "YES NO <size> | none\n");605(void) fprintf(fp, "\t%-22s ", "written@<snap>");606(void) fprintf(fp, " NO NO <size>\n");607(void) fprintf(fp, "\t%-22s ", "written#<bookmark>");608(void) fprintf(fp, " NO NO <size>\n");609610(void) fprintf(fp, gettext("\nSizes are specified in bytes "611"with standard units such as K, M, G, etc.\n"));612(void) fprintf(fp, "%s", gettext("\nUser-defined properties "613"can be specified by using a name containing a colon "614"(:).\n"));615(void) fprintf(fp, gettext("\nThe {user|group|project}"616"[obj]{used|quota}@ properties must be appended with\n"617"a user|group|project specifier of one of these forms:\n"618" POSIX name (eg: \"matt\")\n"619" POSIX id (eg: \"126829\")\n"620" SMB name@domain (eg: \"matt@sun\")\n"621" SMB SID (eg: \"S-1-234-567-89\")\n"));622} else {623(void) fprintf(fp,624gettext("\nFor the property list, run: %s\n"),625"zfs set|get");626(void) fprintf(fp,627gettext("\nFor the delegated permission list, run: %s\n"),628"zfs allow|unallow");629(void) fprintf(fp,630gettext("\nFor further help on a command or topic, "631"run: %s\n"), "zfs help [<topic>]");632}633634/*635* See comments at end of main().636*/637if (getenv("ZFS_ABORT") != NULL) {638(void) printf("dumping core by request\n");639abort();640}641642exit(requested ? 0 : 2);643}644645/*646* Take a property=value argument string and add it to the given nvlist.647* Modifies the argument inplace.648*/649static boolean_t650parseprop(nvlist_t *props, char *propname)651{652char *propval;653654if ((propval = strchr(propname, '=')) == NULL) {655(void) fprintf(stderr, gettext("missing "656"'=' for property=value argument\n"));657return (B_FALSE);658}659*propval = '\0';660propval++;661if (nvlist_exists(props, propname)) {662(void) fprintf(stderr, gettext("property '%s' "663"specified multiple times\n"), propname);664return (B_FALSE);665}666if (nvlist_add_string(props, propname, propval) != 0)667nomem();668return (B_TRUE);669}670671/*672* Take a property name argument and add it to the given nvlist.673* Modifies the argument inplace.674*/675static boolean_t676parsepropname(nvlist_t *props, char *propname)677{678if (strchr(propname, '=') != NULL) {679(void) fprintf(stderr, gettext("invalid character "680"'=' in property argument\n"));681return (B_FALSE);682}683if (nvlist_exists(props, propname)) {684(void) fprintf(stderr, gettext("property '%s' "685"specified multiple times\n"), propname);686return (B_FALSE);687}688if (nvlist_add_boolean(props, propname) != 0)689nomem();690return (B_TRUE);691}692693static int694parse_depth(char *opt, int *flags)695{696char *tmp;697int depth;698699depth = (int)strtol(opt, &tmp, 0);700if (*tmp) {701(void) fprintf(stderr,702gettext("%s is not an integer\n"), optarg);703usage(B_FALSE);704}705if (depth < 0) {706(void) fprintf(stderr,707gettext("Depth can not be negative.\n"));708usage(B_FALSE);709}710*flags |= (ZFS_ITER_DEPTH_LIMIT|ZFS_ITER_RECURSE);711return (depth);712}713714#define PROGRESS_DELAY 2 /* seconds */715716static const char *pt_reverse =717"\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";718static time_t pt_begin;719static char *pt_header = NULL;720static boolean_t pt_shown;721722static void723start_progress_timer(void)724{725pt_begin = time(NULL) + PROGRESS_DELAY;726pt_shown = B_FALSE;727}728729static void730set_progress_header(const char *header)731{732assert(pt_header == NULL);733pt_header = safe_strdup(header);734if (pt_shown) {735(void) printf("%s: ", header);736(void) fflush(stdout);737}738}739740static void741update_progress(const char *update)742{743if (!pt_shown && time(NULL) > pt_begin) {744int len = strlen(update);745746(void) printf("%s: %s%*.*s", pt_header, update, len, len,747pt_reverse);748(void) fflush(stdout);749pt_shown = B_TRUE;750} else if (pt_shown) {751int len = strlen(update);752753(void) printf("%s%*.*s", update, len, len, pt_reverse);754(void) fflush(stdout);755}756}757758static void759finish_progress(const char *done)760{761if (pt_shown) {762(void) puts(done);763(void) fflush(stdout);764}765free(pt_header);766pt_header = NULL;767}768769static int770zfs_mount_and_share(libzfs_handle_t *hdl, const char *dataset, zfs_type_t type)771{772zfs_handle_t *zhp = NULL;773int ret = 0;774775zhp = zfs_open(hdl, dataset, type);776if (zhp == NULL)777return (1);778779/*780* Volumes may neither be mounted or shared. Potentially in the781* future filesystems detected on these volumes could be mounted.782*/783if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {784zfs_close(zhp);785return (0);786}787788/*789* Mount and/or share the new filesystem as appropriate. We provide a790* verbose error message to let the user know that their filesystem was791* in fact created, even if we failed to mount or share it.792*793* If the user doesn't want the dataset automatically mounted, then794* skip the mount/share step795*/796if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type, B_FALSE) &&797zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON) {798if (zfs_mount_delegation_check()) {799(void) fprintf(stderr, gettext("filesystem "800"successfully created, but it may only be "801"mounted by root\n"));802ret = 1;803} else if (zfs_mount(zhp, NULL, 0) != 0) {804(void) fprintf(stderr, gettext("filesystem "805"successfully created, but not mounted\n"));806ret = 1;807} else if (zfs_share(zhp, NULL) != 0) {808(void) fprintf(stderr, gettext("filesystem "809"successfully created, but not shared\n"));810ret = 1;811}812zfs_commit_shares(NULL);813}814815zfs_close(zhp);816817return (ret);818}819820/*821* zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>822*823* Given an existing dataset, create a writable copy whose initial contents824* are the same as the source. The newly created dataset maintains a825* dependency on the original; the original cannot be destroyed so long as826* the clone exists.827*828* The '-p' flag creates all the non-existing ancestors of the target first.829*/830static int831zfs_do_clone(int argc, char **argv)832{833zfs_handle_t *zhp = NULL;834boolean_t parents = B_FALSE;835nvlist_t *props;836int ret = 0;837int c;838839if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)840nomem();841842/* check options */843while ((c = getopt(argc, argv, "o:p")) != -1) {844switch (c) {845case 'o':846if (!parseprop(props, optarg)) {847nvlist_free(props);848return (1);849}850break;851case 'p':852parents = B_TRUE;853break;854case '?':855(void) fprintf(stderr, gettext("invalid option '%c'\n"),856optopt);857goto usage;858}859}860861argc -= optind;862argv += optind;863864/* check number of arguments */865if (argc < 1) {866(void) fprintf(stderr, gettext("missing source dataset "867"argument\n"));868goto usage;869}870if (argc < 2) {871(void) fprintf(stderr, gettext("missing target dataset "872"argument\n"));873goto usage;874}875if (argc > 2) {876(void) fprintf(stderr, gettext("too many arguments\n"));877goto usage;878}879880/* open the source dataset */881if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) {882nvlist_free(props);883return (1);884}885886if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM |887ZFS_TYPE_VOLUME)) {888/*889* Now create the ancestors of the target dataset. If the890* target already exists and '-p' option was used we should not891* complain.892*/893if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM |894ZFS_TYPE_VOLUME)) {895zfs_close(zhp);896nvlist_free(props);897return (0);898}899if (zfs_create_ancestors(g_zfs, argv[1]) != 0) {900zfs_close(zhp);901nvlist_free(props);902return (1);903}904}905906/* pass to libzfs */907ret = zfs_clone(zhp, argv[1], props);908909/* create the mountpoint if necessary */910if (ret == 0) {911if (log_history) {912(void) zpool_log_history(g_zfs, history_str);913log_history = B_FALSE;914}915916/*917* Dataset cloned successfully, mount/share failures are918* non-fatal.919*/920(void) zfs_mount_and_share(g_zfs, argv[1], ZFS_TYPE_DATASET);921}922923zfs_close(zhp);924nvlist_free(props);925926return (!!ret);927928usage:929ASSERT0P(zhp);930nvlist_free(props);931usage(B_FALSE);932return (-1);933}934935/*936* Calculate the minimum allocation size based on the top-level vdevs.937*/938static uint64_t939calculate_volblocksize(nvlist_t *config)940{941uint64_t asize = SPA_MINBLOCKSIZE;942nvlist_t *tree, **vdevs;943uint_t nvdevs;944945if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &tree) != 0 ||946nvlist_lookup_nvlist_array(tree, ZPOOL_CONFIG_CHILDREN,947&vdevs, &nvdevs) != 0) {948return (ZVOL_DEFAULT_BLOCKSIZE);949}950951for (int i = 0; i < nvdevs; i++) {952nvlist_t *nv = vdevs[i];953uint64_t ashift, ndata, nparity;954955if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ASHIFT, &ashift) != 0)956continue;957958if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_DRAID_NDATA,959&ndata) == 0) {960/* dRAID minimum allocation width */961asize = MAX(asize, ndata * (1ULL << ashift));962} else if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NPARITY,963&nparity) == 0) {964/* raidz minimum allocation width */965if (nparity == 1)966asize = MAX(asize, 2 * (1ULL << ashift));967else968asize = MAX(asize, 4 * (1ULL << ashift));969} else {970/* mirror or (non-redundant) leaf vdev */971asize = MAX(asize, 1ULL << ashift);972}973}974975return (asize);976}977978/*979* Return a default volblocksize for the pool which always uses more than980* half of the data sectors. This primarily applies to dRAID which always981* writes full stripe widths.982*/983static uint64_t984default_volblocksize(zpool_handle_t *zhp, nvlist_t *props)985{986uint64_t volblocksize, asize = SPA_MINBLOCKSIZE;987988nvlist_t *config = zpool_get_config(zhp, NULL);989990if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_MAX_ALLOC, &asize) != 0)991asize = calculate_volblocksize(config);992993/*994* Calculate the target volblocksize such that more than half995* of the asize is used. The following table is for 4k sectors.996*997* n asize blksz used | n asize blksz used998* -------------------------+---------------------------------999* 1 4,096 8,192 100% | 9 36,864 32,768 88%1000* 2 8,192 8,192 100% | 10 40,960 32,768 80%1001* 3 12,288 8,192 66% | 11 45,056 32,768 72%1002* 4 16,384 16,384 100% | 12 49,152 32,768 66%1003* 5 20,480 16,384 80% | 13 53,248 32,768 61%1004* 6 24,576 16,384 66% | 14 57,344 32,768 57%1005* 7 28,672 16,384 57% | 15 61,440 32,768 53%1006* 8 32,768 32,768 100% | 16 65,536 65,636 100%1007*1008* This is primarily a concern for dRAID which always allocates1009* a full stripe width. For dRAID the default stripe width is1010* n=8 in which case the volblocksize is set to 32k. Ignoring1011* compression there are no unused sectors. This same reasoning1012* applies to raidz[2,3] so target 4 sectors to minimize waste.1013*/1014uint64_t tgt_volblocksize = ZVOL_DEFAULT_BLOCKSIZE;1015while (tgt_volblocksize * 2 <= asize)1016tgt_volblocksize *= 2;10171018const char *prop = zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE);1019if (nvlist_lookup_uint64(props, prop, &volblocksize) == 0) {10201021/* Issue a warning when a non-optimal size is requested. */1022if (volblocksize < ZVOL_DEFAULT_BLOCKSIZE) {1023(void) fprintf(stderr, gettext("Warning: "1024"volblocksize (%llu) is less than the default "1025"minimum block size (%llu).\nTo reduce wasted "1026"space a volblocksize of %llu is recommended.\n"),1027(u_longlong_t)volblocksize,1028(u_longlong_t)ZVOL_DEFAULT_BLOCKSIZE,1029(u_longlong_t)tgt_volblocksize);1030} else if (volblocksize < tgt_volblocksize) {1031(void) fprintf(stderr, gettext("Warning: "1032"volblocksize (%llu) is much less than the "1033"minimum allocation\nunit (%llu), which wastes "1034"at least %llu%% of space. To reduce wasted "1035"space,\nuse a larger volblocksize (%llu is "1036"recommended), fewer dRAID data disks\n"1037"per group, or smaller sector size (ashift).\n"),1038(u_longlong_t)volblocksize, (u_longlong_t)asize,1039(u_longlong_t)((100 * (asize - volblocksize)) /1040asize), (u_longlong_t)tgt_volblocksize);1041}1042} else {1043volblocksize = tgt_volblocksize;1044fnvlist_add_uint64(props, prop, volblocksize);1045}10461047return (volblocksize);1048}10491050/*1051* zfs create [-Pnpv] [-o prop=value] ... fs1052* zfs create [-Pnpsv] [-b blocksize] [-o prop=value] ... -V vol size1053*1054* Create a new dataset. This command can be used to create filesystems1055* and volumes. Snapshot creation is handled by 'zfs snapshot'.1056* For volumes, the user must specify a size to be used.1057*1058* The '-s' flag applies only to volumes, and indicates that we should not try1059* to set the reservation for this volume. By default we set a reservation1060* equal to the size for any volume. For pools with SPA_VERSION >=1061* SPA_VERSION_REFRESERVATION, we set a refreservation instead.1062*1063* The '-p' flag creates all the non-existing ancestors of the target first.1064*1065* The '-n' flag is no-op (dry run) mode. This will perform a user-space sanity1066* check of arguments and properties, but does not check for permissions,1067* available space, etc.1068*1069* The '-u' flag prevents the newly created file system from being mounted.1070*1071* The '-v' flag is for verbose output.1072*1073* The '-P' flag is used for parseable output. It implies '-v'.1074*/1075static int1076zfs_do_create(int argc, char **argv)1077{1078zfs_type_t type = ZFS_TYPE_FILESYSTEM;1079zpool_handle_t *zpool_handle = NULL;1080nvlist_t *real_props = NULL;1081uint64_t volsize = 0;1082int c;1083boolean_t noreserve = B_FALSE;1084boolean_t bflag = B_FALSE;1085boolean_t parents = B_FALSE;1086boolean_t dryrun = B_FALSE;1087boolean_t nomount = B_FALSE;1088boolean_t verbose = B_FALSE;1089boolean_t parseable = B_FALSE;1090int ret = 1;1091nvlist_t *props;1092uint64_t intval;1093const char *strval;10941095if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)1096nomem();10971098/* check options */1099while ((c = getopt(argc, argv, ":PV:b:nso:puv")) != -1) {1100switch (c) {1101case 'V':1102type = ZFS_TYPE_VOLUME;1103if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {1104(void) fprintf(stderr, gettext("bad volume "1105"size '%s': %s\n"), optarg,1106libzfs_error_description(g_zfs));1107goto error;1108}11091110if (nvlist_add_uint64(props,1111zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0)1112nomem();1113volsize = intval;1114break;1115case 'P':1116verbose = B_TRUE;1117parseable = B_TRUE;1118break;1119case 'p':1120parents = B_TRUE;1121break;1122case 'b':1123bflag = B_TRUE;1124if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {1125(void) fprintf(stderr, gettext("bad volume "1126"block size '%s': %s\n"), optarg,1127libzfs_error_description(g_zfs));1128goto error;1129}11301131if (nvlist_add_uint64(props,1132zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),1133intval) != 0)1134nomem();1135break;1136case 'n':1137dryrun = B_TRUE;1138break;1139case 'o':1140if (!parseprop(props, optarg))1141goto error;1142break;1143case 's':1144noreserve = B_TRUE;1145break;1146case 'u':1147nomount = B_TRUE;1148break;1149case 'v':1150verbose = B_TRUE;1151break;1152case ':':1153(void) fprintf(stderr, gettext("missing size "1154"argument\n"));1155goto badusage;1156case '?':1157(void) fprintf(stderr, gettext("invalid option '%c'\n"),1158optopt);1159goto badusage;1160}1161}11621163if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) {1164(void) fprintf(stderr, gettext("'-s' and '-b' can only be "1165"used when creating a volume\n"));1166goto badusage;1167}1168if (nomount && type != ZFS_TYPE_FILESYSTEM) {1169(void) fprintf(stderr, gettext("'-u' can only be "1170"used when creating a filesystem\n"));1171goto badusage;1172}11731174argc -= optind;1175argv += optind;11761177/* check number of arguments */1178if (argc == 0) {1179(void) fprintf(stderr, gettext("missing %s argument\n"),1180zfs_type_to_name(type));1181goto badusage;1182}1183if (argc > 1) {1184(void) fprintf(stderr, gettext("too many arguments\n"));1185goto badusage;1186}11871188if (dryrun || type == ZFS_TYPE_VOLUME) {1189char msg[ZFS_MAX_DATASET_NAME_LEN * 2];1190char *p;11911192if ((p = strchr(argv[0], '/')) != NULL)1193*p = '\0';1194zpool_handle = zpool_open(g_zfs, argv[0]);1195if (p != NULL)1196*p = '/';1197if (zpool_handle == NULL)1198goto error;11991200(void) snprintf(msg, sizeof (msg),1201dryrun ? gettext("cannot verify '%s'") :1202gettext("cannot create '%s'"), argv[0]);1203if (props && (real_props = zfs_valid_proplist(g_zfs, type,1204props, 0, NULL, zpool_handle, B_TRUE, msg)) == NULL) {1205zpool_close(zpool_handle);1206goto error;1207}1208}12091210if (type == ZFS_TYPE_VOLUME) {1211const char *prop = zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE);1212uint64_t volblocksize = default_volblocksize(zpool_handle,1213real_props);12141215if (volblocksize != ZVOL_DEFAULT_BLOCKSIZE &&1216nvlist_lookup_string(props, prop, &strval) != 0) {1217char *tmp;1218if (asprintf(&tmp, "%llu",1219(u_longlong_t)volblocksize) == -1)1220nomem();1221nvlist_add_string(props, prop, tmp);1222free(tmp);1223}12241225/*1226* If volsize is not a multiple of volblocksize, round it1227* up to the nearest multiple of the volblocksize.1228*/1229if (volsize % volblocksize) {1230volsize = P2ROUNDUP_TYPED(volsize, volblocksize,1231uint64_t);12321233if (nvlist_add_uint64(props,1234zfs_prop_to_name(ZFS_PROP_VOLSIZE), volsize) != 0) {1235nvlist_free(props);1236nomem();1237}1238}1239}12401241if (type == ZFS_TYPE_VOLUME && !noreserve) {1242uint64_t spa_version;1243zfs_prop_t resv_prop;12441245spa_version = zpool_get_prop_int(zpool_handle,1246ZPOOL_PROP_VERSION, NULL);1247if (spa_version >= SPA_VERSION_REFRESERVATION)1248resv_prop = ZFS_PROP_REFRESERVATION;1249else1250resv_prop = ZFS_PROP_RESERVATION;12511252volsize = zvol_volsize_to_reservation(zpool_handle, volsize,1253real_props);12541255if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop),1256&strval) != 0) {1257if (nvlist_add_uint64(props,1258zfs_prop_to_name(resv_prop), volsize) != 0) {1259nvlist_free(props);1260nomem();1261}1262}1263}1264if (zpool_handle != NULL) {1265zpool_close(zpool_handle);1266nvlist_free(real_props);1267}12681269if (parents && zfs_name_valid(argv[0], type)) {1270/*1271* Now create the ancestors of target dataset. If the target1272* already exists and '-p' option was used we should not1273* complain.1274*/1275if (zfs_dataset_exists(g_zfs, argv[0], type)) {1276ret = 0;1277goto error;1278}1279if (verbose) {1280(void) printf(parseable ? "create_ancestors\t%s\n" :1281dryrun ? "would create ancestors of %s\n" :1282"create ancestors of %s\n", argv[0]);1283}1284if (!dryrun) {1285if (zfs_create_ancestors(g_zfs, argv[0]) != 0) {1286goto error;1287}1288}1289}12901291if (verbose) {1292nvpair_t *nvp = NULL;1293(void) printf(parseable ? "create\t%s\n" :1294dryrun ? "would create %s\n" : "create %s\n", argv[0]);1295while ((nvp = nvlist_next_nvpair(props, nvp)) != NULL) {1296uint64_t uval;1297const char *sval;12981299switch (nvpair_type(nvp)) {1300case DATA_TYPE_UINT64:1301VERIFY0(nvpair_value_uint64(nvp, &uval));1302(void) printf(parseable ?1303"property\t%s\t%llu\n" : "\t%s=%llu\n",1304nvpair_name(nvp), (u_longlong_t)uval);1305break;1306case DATA_TYPE_STRING:1307VERIFY0(nvpair_value_string(nvp, &sval));1308(void) printf(parseable ?1309"property\t%s\t%s\n" : "\t%s=%s\n",1310nvpair_name(nvp), sval);1311break;1312default:1313(void) fprintf(stderr, "property '%s' "1314"has illegal type %d\n",1315nvpair_name(nvp), nvpair_type(nvp));1316abort();1317}1318}1319}1320if (dryrun) {1321ret = 0;1322goto error;1323}13241325/* pass to libzfs */1326if (zfs_create(g_zfs, argv[0], type, props) != 0)1327goto error;13281329if (log_history) {1330(void) zpool_log_history(g_zfs, history_str);1331log_history = B_FALSE;1332}13331334if (nomount) {1335ret = 0;1336goto error;1337}13381339/* Dataset created successfully, mount/share failures are non-fatal */1340ret = 0;1341(void) zfs_mount_and_share(g_zfs, argv[0], ZFS_TYPE_DATASET);1342error:1343nvlist_free(props);1344return (ret);1345badusage:1346nvlist_free(props);1347usage(B_FALSE);1348return (2);1349}13501351/*1352* zfs destroy [-rRf] <fs, vol>1353* zfs destroy [-rRd] <snap>1354*1355* -r Recursively destroy all children1356* -R Recursively destroy all dependents, including clones1357* -f Force unmounting of any dependents1358* -d If we can't destroy now, mark for deferred destruction1359*1360* Destroys the given dataset. By default, it will unmount any filesystems,1361* and refuse to destroy a dataset that has any dependents. A dependent can1362* either be a child, or a clone of a child.1363*/1364typedef struct destroy_cbdata {1365boolean_t cb_first;1366boolean_t cb_force;1367boolean_t cb_recurse;1368boolean_t cb_error;1369boolean_t cb_doclones;1370zfs_handle_t *cb_target;1371boolean_t cb_defer_destroy;1372boolean_t cb_verbose;1373boolean_t cb_parsable;1374boolean_t cb_dryrun;1375nvlist_t *cb_nvl;1376nvlist_t *cb_batchedsnaps;13771378/* first snap in contiguous run */1379char *cb_firstsnap;1380/* previous snap in contiguous run */1381char *cb_prevsnap;1382int64_t cb_snapused;1383char *cb_snapspec;1384char *cb_bookmark;1385uint64_t cb_snap_count;1386} destroy_cbdata_t;13871388/*1389* Check for any dependents based on the '-r' or '-R' flags.1390*/1391static int1392destroy_check_dependent(zfs_handle_t *zhp, void *data)1393{1394destroy_cbdata_t *cbp = data;1395const char *tname = zfs_get_name(cbp->cb_target);1396const char *name = zfs_get_name(zhp);13971398if (strncmp(tname, name, strlen(tname)) == 0 &&1399(name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) {1400/*1401* This is a direct descendant, not a clone somewhere else in1402* the hierarchy.1403*/1404if (cbp->cb_recurse)1405goto out;14061407if (cbp->cb_first) {1408(void) fprintf(stderr, gettext("cannot destroy '%s': "1409"%s has children\n"),1410zfs_get_name(cbp->cb_target),1411zfs_type_to_name(zfs_get_type(cbp->cb_target)));1412(void) fprintf(stderr, gettext("use '-r' to destroy "1413"the following datasets:\n"));1414cbp->cb_first = B_FALSE;1415cbp->cb_error = B_TRUE;1416}14171418(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));1419} else {1420/*1421* This is a clone. We only want to report this if the '-r'1422* wasn't specified, or the target is a snapshot.1423*/1424if (!cbp->cb_recurse &&1425zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT)1426goto out;14271428if (cbp->cb_first) {1429(void) fprintf(stderr, gettext("cannot destroy '%s': "1430"%s has dependent clones\n"),1431zfs_get_name(cbp->cb_target),1432zfs_type_to_name(zfs_get_type(cbp->cb_target)));1433(void) fprintf(stderr, gettext("use '-R' to destroy "1434"the following datasets:\n"));1435cbp->cb_first = B_FALSE;1436cbp->cb_error = B_TRUE;1437cbp->cb_dryrun = B_TRUE;1438}14391440(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));1441}14421443out:1444zfs_close(zhp);1445return (0);1446}14471448static int1449destroy_batched(destroy_cbdata_t *cb)1450{1451int error = zfs_destroy_snaps_nvl(g_zfs,1452cb->cb_batchedsnaps, B_FALSE);1453fnvlist_free(cb->cb_batchedsnaps);1454cb->cb_batchedsnaps = fnvlist_alloc();1455return (error);1456}14571458static int1459destroy_callback(zfs_handle_t *zhp, void *data)1460{1461destroy_cbdata_t *cb = data;1462const char *name = zfs_get_name(zhp);1463int error;14641465if (cb->cb_verbose) {1466if (cb->cb_parsable) {1467(void) printf("destroy\t%s\n", name);1468} else if (cb->cb_dryrun) {1469(void) printf(gettext("would destroy %s\n"),1470name);1471} else {1472(void) printf(gettext("will destroy %s\n"),1473name);1474}1475}14761477/*1478* Ignore pools (which we've already flagged as an error before getting1479* here).1480*/1481if (strchr(zfs_get_name(zhp), '/') == NULL &&1482zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {1483zfs_close(zhp);1484return (0);1485}1486if (cb->cb_dryrun) {1487zfs_close(zhp);1488return (0);1489}14901491/*1492* We batch up all contiguous snapshots (even of different1493* filesystems) and destroy them with one ioctl. We can't1494* simply do all snap deletions and then all fs deletions,1495* because we must delete a clone before its origin.1496*/1497if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) {1498cb->cb_snap_count++;1499fnvlist_add_boolean(cb->cb_batchedsnaps, name);1500if (cb->cb_snap_count % 10 == 0 && cb->cb_defer_destroy) {1501error = destroy_batched(cb);1502if (error != 0) {1503zfs_close(zhp);1504return (-1);1505}1506}1507} else {1508error = destroy_batched(cb);1509if (error != 0 ||1510zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 ||1511zfs_destroy(zhp, cb->cb_defer_destroy) != 0) {1512zfs_close(zhp);1513/*1514* When performing a recursive destroy we ignore errors1515* so that the recursive destroy could continue1516* destroying past problem datasets1517*/1518if (cb->cb_recurse) {1519cb->cb_error = B_TRUE;1520return (0);1521}1522return (-1);1523}1524}15251526zfs_close(zhp);1527return (0);1528}15291530static int1531destroy_print_cb(zfs_handle_t *zhp, void *arg)1532{1533destroy_cbdata_t *cb = arg;1534const char *name = zfs_get_name(zhp);1535int err = 0;15361537if (nvlist_exists(cb->cb_nvl, name)) {1538if (cb->cb_firstsnap == NULL)1539cb->cb_firstsnap = strdup(name);1540if (cb->cb_prevsnap != NULL)1541free(cb->cb_prevsnap);1542/* this snap continues the current range */1543cb->cb_prevsnap = strdup(name);1544if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL)1545nomem();1546if (cb->cb_verbose) {1547if (cb->cb_parsable) {1548(void) printf("destroy\t%s\n", name);1549} else if (cb->cb_dryrun) {1550(void) printf(gettext("would destroy %s\n"),1551name);1552} else {1553(void) printf(gettext("will destroy %s\n"),1554name);1555}1556}1557} else if (cb->cb_firstsnap != NULL) {1558/* end of this range */1559uint64_t used = 0;1560err = lzc_snaprange_space(cb->cb_firstsnap,1561cb->cb_prevsnap, &used);1562cb->cb_snapused += used;1563free(cb->cb_firstsnap);1564cb->cb_firstsnap = NULL;1565free(cb->cb_prevsnap);1566cb->cb_prevsnap = NULL;1567}1568zfs_close(zhp);1569return (err);1570}15711572static int1573destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb)1574{1575int err;1576assert(cb->cb_firstsnap == NULL);1577assert(cb->cb_prevsnap == NULL);1578err = zfs_iter_snapshots_sorted_v2(fs_zhp, 0, destroy_print_cb, cb, 0,15790);1580if (cb->cb_firstsnap != NULL) {1581uint64_t used = 0;1582if (err == 0) {1583err = lzc_snaprange_space(cb->cb_firstsnap,1584cb->cb_prevsnap, &used);1585}1586cb->cb_snapused += used;1587free(cb->cb_firstsnap);1588cb->cb_firstsnap = NULL;1589free(cb->cb_prevsnap);1590cb->cb_prevsnap = NULL;1591}1592return (err);1593}15941595static int1596snapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg)1597{1598destroy_cbdata_t *cb = arg;1599int err = 0;16001601/* Check for clones. */1602if (!cb->cb_doclones && !cb->cb_defer_destroy) {1603cb->cb_target = zhp;1604cb->cb_first = B_TRUE;1605err = zfs_iter_dependents_v2(zhp, 0, B_TRUE,1606destroy_check_dependent, cb);1607}16081609if (err == 0) {1610if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp)))1611nomem();1612}1613zfs_close(zhp);1614return (err);1615}16161617static int1618gather_snapshots(zfs_handle_t *zhp, void *arg)1619{1620destroy_cbdata_t *cb = arg;1621int err = 0;16221623err = zfs_iter_snapspec_v2(zhp, 0, cb->cb_snapspec,1624snapshot_to_nvl_cb, cb);1625if (err == ENOENT)1626err = 0;1627if (err != 0)1628goto out;16291630if (cb->cb_verbose) {1631err = destroy_print_snapshots(zhp, cb);1632if (err != 0)1633goto out;1634}16351636if (cb->cb_recurse)1637err = zfs_iter_filesystems_v2(zhp, 0, gather_snapshots, cb);16381639out:1640zfs_close(zhp);1641return (err);1642}16431644static int1645destroy_clones(destroy_cbdata_t *cb)1646{1647nvpair_t *pair;1648for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL);1649pair != NULL;1650pair = nvlist_next_nvpair(cb->cb_nvl, pair)) {1651zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair),1652ZFS_TYPE_SNAPSHOT);1653if (zhp != NULL) {1654boolean_t defer = cb->cb_defer_destroy;1655int err;16561657/*1658* We can't defer destroy non-snapshots, so set it to1659* false while destroying the clones.1660*/1661cb->cb_defer_destroy = B_FALSE;1662err = zfs_iter_dependents_v2(zhp, 0, B_FALSE,1663destroy_callback, cb);1664cb->cb_defer_destroy = defer;1665zfs_close(zhp);1666if (err != 0)1667return (err);1668}1669}1670return (0);1671}16721673static int1674zfs_do_destroy(int argc, char **argv)1675{1676destroy_cbdata_t cb = { 0 };1677int rv = 0;1678int err = 0;1679int c;1680zfs_handle_t *zhp = NULL;1681char *at, *pound;1682zfs_type_t type = ZFS_TYPE_DATASET;16831684/* check options */1685while ((c = getopt(argc, argv, "vpndfrR")) != -1) {1686switch (c) {1687case 'v':1688cb.cb_verbose = B_TRUE;1689break;1690case 'p':1691cb.cb_verbose = B_TRUE;1692cb.cb_parsable = B_TRUE;1693break;1694case 'n':1695cb.cb_dryrun = B_TRUE;1696break;1697case 'd':1698cb.cb_defer_destroy = B_TRUE;1699type = ZFS_TYPE_SNAPSHOT;1700break;1701case 'f':1702cb.cb_force = B_TRUE;1703break;1704case 'r':1705cb.cb_recurse = B_TRUE;1706break;1707case 'R':1708cb.cb_recurse = B_TRUE;1709cb.cb_doclones = B_TRUE;1710break;1711case '?':1712default:1713(void) fprintf(stderr, gettext("invalid option '%c'\n"),1714optopt);1715usage(B_FALSE);1716}1717}17181719argc -= optind;1720argv += optind;17211722/* check number of arguments */1723if (argc == 0) {1724(void) fprintf(stderr, gettext("missing dataset argument\n"));1725usage(B_FALSE);1726}1727if (argc > 1) {1728(void) fprintf(stderr, gettext("too many arguments\n"));1729usage(B_FALSE);1730}17311732at = strchr(argv[0], '@');1733pound = strchr(argv[0], '#');1734if (at != NULL) {17351736/* Build the list of snaps to destroy in cb_nvl. */1737cb.cb_nvl = fnvlist_alloc();17381739*at = '\0';1740zhp = zfs_open(g_zfs, argv[0],1741ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);1742if (zhp == NULL) {1743nvlist_free(cb.cb_nvl);1744return (1);1745}17461747cb.cb_snapspec = at + 1;1748if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 ||1749cb.cb_error) {1750rv = 1;1751goto out;1752}17531754if (nvlist_empty(cb.cb_nvl)) {1755(void) fprintf(stderr, gettext("could not find any "1756"snapshots to destroy; check snapshot names.\n"));1757rv = 1;1758goto out;1759}17601761if (cb.cb_verbose) {1762char buf[16];1763zfs_nicebytes(cb.cb_snapused, buf, sizeof (buf));1764if (cb.cb_parsable) {1765(void) printf("reclaim\t%llu\n",1766(u_longlong_t)cb.cb_snapused);1767} else if (cb.cb_dryrun) {1768(void) printf(gettext("would reclaim %s\n"),1769buf);1770} else {1771(void) printf(gettext("will reclaim %s\n"),1772buf);1773}1774}17751776if (!cb.cb_dryrun) {1777if (cb.cb_doclones) {1778cb.cb_batchedsnaps = fnvlist_alloc();1779err = destroy_clones(&cb);1780if (err == 0) {1781err = zfs_destroy_snaps_nvl(g_zfs,1782cb.cb_batchedsnaps, B_FALSE);1783}1784if (err != 0) {1785rv = 1;1786goto out;1787}1788}1789if (err == 0) {1790err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl,1791cb.cb_defer_destroy);1792}1793}17941795if (err != 0)1796rv = 1;1797} else if (pound != NULL) {1798int err;1799nvlist_t *nvl;18001801if (cb.cb_dryrun) {1802(void) fprintf(stderr,1803"dryrun is not supported with bookmark\n");1804return (-1);1805}18061807if (cb.cb_defer_destroy) {1808(void) fprintf(stderr,1809"defer destroy is not supported with bookmark\n");1810return (-1);1811}18121813if (cb.cb_recurse) {1814(void) fprintf(stderr,1815"recursive is not supported with bookmark\n");1816return (-1);1817}18181819/*1820* Unfortunately, zfs_bookmark() doesn't honor the1821* casesensitivity setting. However, we can't simply1822* remove this check, because lzc_destroy_bookmarks()1823* ignores non-existent bookmarks, so this is necessary1824* to get a proper error message.1825*/1826if (!zfs_bookmark_exists(argv[0])) {1827(void) fprintf(stderr, gettext("bookmark '%s' "1828"does not exist.\n"), argv[0]);1829return (1);1830}18311832nvl = fnvlist_alloc();1833fnvlist_add_boolean(nvl, argv[0]);18341835err = lzc_destroy_bookmarks(nvl, NULL);1836if (err != 0) {1837(void) zfs_standard_error(g_zfs, err,1838"cannot destroy bookmark");1839}18401841nvlist_free(nvl);18421843return (err);1844} else {1845/* Open the given dataset */1846if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL)1847return (1);18481849cb.cb_target = zhp;18501851/*1852* Perform an explicit check for pools before going any further.1853*/1854if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL &&1855zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {1856(void) fprintf(stderr, gettext("cannot destroy '%s': "1857"operation does not apply to pools\n"),1858zfs_get_name(zhp));1859(void) fprintf(stderr, gettext("use 'zfs destroy -r "1860"%s' to destroy all datasets in the pool\n"),1861zfs_get_name(zhp));1862(void) fprintf(stderr, gettext("use 'zpool destroy %s' "1863"to destroy the pool itself\n"), zfs_get_name(zhp));1864rv = 1;1865goto out;1866}18671868/*1869* Check for any dependents and/or clones.1870*/1871cb.cb_first = B_TRUE;1872if (!cb.cb_doclones && zfs_iter_dependents_v2(zhp, 0, B_TRUE,1873destroy_check_dependent, &cb) != 0) {1874rv = 1;1875goto out;1876}18771878if (cb.cb_error) {1879rv = 1;1880goto out;1881}1882cb.cb_batchedsnaps = fnvlist_alloc();1883if (zfs_iter_dependents_v2(zhp, 0, B_FALSE, destroy_callback,1884&cb) != 0) {1885rv = 1;1886goto out;1887}18881889/*1890* Do the real thing. The callback will close the1891* handle regardless of whether it succeeds or not.1892*/1893err = destroy_callback(zhp, &cb);1894zhp = NULL;1895if (err == 0) {1896err = zfs_destroy_snaps_nvl(g_zfs,1897cb.cb_batchedsnaps, cb.cb_defer_destroy);1898}1899if (err != 0 || cb.cb_error == B_TRUE)1900rv = 1;1901}19021903out:1904fnvlist_free(cb.cb_batchedsnaps);1905fnvlist_free(cb.cb_nvl);1906if (zhp != NULL)1907zfs_close(zhp);1908return (rv);1909}19101911static boolean_t1912is_recvd_column(zprop_get_cbdata_t *cbp)1913{1914int i;1915zfs_get_column_t col;19161917for (i = 0; i < ZFS_GET_NCOLS &&1918(col = cbp->cb_columns[i]) != GET_COL_NONE; i++)1919if (col == GET_COL_RECVD)1920return (B_TRUE);1921return (B_FALSE);1922}19231924/*1925* Generates an nvlist with output version for every command based on params.1926* Purpose of this is to add a version of JSON output, considering the schema1927* format might be updated for each command in future.1928*1929* Schema:1930*1931* "output_version": {1932* "command": string,1933* "vers_major": integer,1934* "vers_minor": integer,1935* }1936*/1937static nvlist_t *1938zfs_json_schema(int maj_v, int min_v)1939{1940nvlist_t *sch = NULL;1941nvlist_t *ov = NULL;1942char cmd[MAX_CMD_LEN];1943snprintf(cmd, MAX_CMD_LEN, "zfs %s", current_command->name);19441945sch = fnvlist_alloc();1946ov = fnvlist_alloc();1947fnvlist_add_string(ov, "command", cmd);1948fnvlist_add_uint32(ov, "vers_major", maj_v);1949fnvlist_add_uint32(ov, "vers_minor", min_v);1950fnvlist_add_nvlist(sch, "output_version", ov);1951fnvlist_free(ov);1952return (sch);1953}19541955static void1956fill_dataset_info(nvlist_t *list, zfs_handle_t *zhp, boolean_t as_int)1957{1958char createtxg[ZFS_MAXPROPLEN];1959zfs_type_t type = zfs_get_type(zhp);1960nvlist_add_string(list, "name", zfs_get_name(zhp));19611962switch (type) {1963case ZFS_TYPE_FILESYSTEM:1964fnvlist_add_string(list, "type", "FILESYSTEM");1965break;1966case ZFS_TYPE_VOLUME:1967fnvlist_add_string(list, "type", "VOLUME");1968break;1969case ZFS_TYPE_SNAPSHOT:1970fnvlist_add_string(list, "type", "SNAPSHOT");1971break;1972case ZFS_TYPE_POOL:1973fnvlist_add_string(list, "type", "POOL");1974break;1975case ZFS_TYPE_BOOKMARK:1976fnvlist_add_string(list, "type", "BOOKMARK");1977break;1978default:1979fnvlist_add_string(list, "type", "UNKNOWN");1980break;1981}19821983if (type != ZFS_TYPE_POOL)1984fnvlist_add_string(list, "pool", zfs_get_pool_name(zhp));19851986if (as_int) {1987fnvlist_add_uint64(list, "createtxg", zfs_prop_get_int(zhp,1988ZFS_PROP_CREATETXG));1989} else {1990if (zfs_prop_get(zhp, ZFS_PROP_CREATETXG, createtxg,1991sizeof (createtxg), NULL, NULL, 0, B_TRUE) == 0)1992fnvlist_add_string(list, "createtxg", createtxg);1993}19941995if (type == ZFS_TYPE_SNAPSHOT) {1996char *snap = strdup(zfs_get_name(zhp));1997char *ds = strsep(&snap, "@");1998fnvlist_add_string(list, "dataset", ds);1999fnvlist_add_string(list, "snapshot_name", snap);2000free(ds);2001}2002}20032004/*2005* zfs get [-rHp] [-j [--json-int]] [-o all | field[,field]...]2006* [-s source[,source]...]2007* < all | property[,property]... > < fs | snap | vol > ...2008*2009* -r recurse over any child datasets2010* -H scripted mode. Headers are stripped, and fields are separated2011* by tabs instead of spaces.2012* -o Set of fields to display. One of "name,property,value,2013* received,source". Default is "name,property,value,source".2014* "all" is an alias for all five.2015* -s Set of sources to allow. One of2016* "local,default,inherited,received,temporary,none". Default is2017* all six.2018* -p Display values in parsable (literal) format.2019* -j Display output in JSON format.2020* --json-int Display numbers as integers instead of strings.2021*2022* Prints properties for the given datasets. The user can control which2023* columns to display as well as which property types to allow.2024*/20252026/*2027* Invoked to display the properties for a single dataset.2028*/2029static int2030get_callback(zfs_handle_t *zhp, void *data)2031{2032char buf[ZFS_MAXPROPLEN];2033char rbuf[ZFS_MAXPROPLEN];2034zprop_source_t sourcetype;2035char source[ZFS_MAX_DATASET_NAME_LEN];2036zprop_get_cbdata_t *cbp = data;2037nvlist_t *user_props = zfs_get_user_props(zhp);2038zprop_list_t *pl = cbp->cb_proplist;2039nvlist_t *propval;2040nvlist_t *item, *d = NULL, *props = NULL;2041const char *strval;2042const char *sourceval;2043boolean_t received = is_recvd_column(cbp);2044int err = 0;20452046if (cbp->cb_json) {2047d = fnvlist_lookup_nvlist(cbp->cb_jsobj, "datasets");2048if (d == NULL) {2049fprintf(stderr, "datasets obj not found.\n");2050exit(1);2051}2052props = fnvlist_alloc();2053}20542055for (; pl != NULL; pl = pl->pl_next) {2056char *recvdval = NULL;2057/*2058* Skip the special fake placeholder. This will also skip over2059* the name property when 'all' is specified.2060*/2061if (pl->pl_prop == ZFS_PROP_NAME &&2062pl == cbp->cb_proplist)2063continue;20642065if (pl->pl_prop != ZPROP_USERPROP) {2066if (zfs_prop_get(zhp, pl->pl_prop, buf,2067sizeof (buf), &sourcetype, source,2068sizeof (source),2069cbp->cb_literal) != 0) {2070if (pl->pl_all)2071continue;2072if (!zfs_prop_valid_for_type(pl->pl_prop,2073ZFS_TYPE_DATASET, B_FALSE)) {2074(void) fprintf(stderr,2075gettext("No such property '%s'\n"),2076zfs_prop_to_name(pl->pl_prop));2077continue;2078}2079sourcetype = ZPROP_SRC_NONE;2080(void) strlcpy(buf, "-", sizeof (buf));2081}20822083if (received && (zfs_prop_get_recvd(zhp,2084zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf),2085cbp->cb_literal) == 0))2086recvdval = rbuf;20872088err = zprop_collect_property(zfs_get_name(zhp), cbp,2089zfs_prop_to_name(pl->pl_prop),2090buf, sourcetype, source, recvdval, props);2091} else if (zfs_prop_userquota(pl->pl_user_prop)) {2092sourcetype = ZPROP_SRC_LOCAL;20932094if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,2095buf, sizeof (buf), cbp->cb_literal) != 0) {2096sourcetype = ZPROP_SRC_NONE;2097(void) strlcpy(buf, "-", sizeof (buf));2098}20992100err = zprop_collect_property(zfs_get_name(zhp), cbp,2101pl->pl_user_prop, buf, sourcetype, source, NULL,2102props);2103} else if (zfs_prop_written(pl->pl_user_prop)) {2104sourcetype = ZPROP_SRC_LOCAL;21052106if (zfs_prop_get_written(zhp, pl->pl_user_prop,2107buf, sizeof (buf), cbp->cb_literal) != 0) {2108sourcetype = ZPROP_SRC_NONE;2109(void) strlcpy(buf, "-", sizeof (buf));2110}21112112err = zprop_collect_property(zfs_get_name(zhp), cbp,2113pl->pl_user_prop, buf, sourcetype, source, NULL,2114props);2115} else {2116if (nvlist_lookup_nvlist(user_props,2117pl->pl_user_prop, &propval) != 0) {2118if (pl->pl_all)2119continue;2120sourcetype = ZPROP_SRC_NONE;2121strval = "-";2122} else {2123strval = fnvlist_lookup_string(propval,2124ZPROP_VALUE);2125sourceval = fnvlist_lookup_string(propval,2126ZPROP_SOURCE);21272128if (strcmp(sourceval,2129zfs_get_name(zhp)) == 0) {2130sourcetype = ZPROP_SRC_LOCAL;2131} else if (strcmp(sourceval,2132ZPROP_SOURCE_VAL_RECVD) == 0) {2133sourcetype = ZPROP_SRC_RECEIVED;2134} else {2135sourcetype = ZPROP_SRC_INHERITED;2136(void) strlcpy(source,2137sourceval, sizeof (source));2138}2139}21402141if (received && (zfs_prop_get_recvd(zhp,2142pl->pl_user_prop, rbuf, sizeof (rbuf),2143cbp->cb_literal) == 0))2144recvdval = rbuf;21452146err = zprop_collect_property(zfs_get_name(zhp), cbp,2147pl->pl_user_prop, strval, sourcetype,2148source, recvdval, props);2149}2150if (err != 0)2151return (err);2152}21532154if (cbp->cb_json) {2155if (!nvlist_empty(props)) {2156item = fnvlist_alloc();2157fill_dataset_info(item, zhp, cbp->cb_json_as_int);2158fnvlist_add_nvlist(item, "properties", props);2159fnvlist_add_nvlist(d, zfs_get_name(zhp), item);2160fnvlist_free(props);2161fnvlist_free(item);2162} else {2163fnvlist_free(props);2164}2165}21662167return (0);2168}21692170static int2171zfs_do_get(int argc, char **argv)2172{2173zprop_get_cbdata_t cb = { 0 };2174int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS;2175int types = ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK;2176char *fields;2177int ret = 0;2178int limit = 0;2179zprop_list_t fake_name = { 0 };2180nvlist_t *data;21812182/*2183* Set up default columns and sources.2184*/2185cb.cb_sources = ZPROP_SRC_ALL;2186cb.cb_columns[0] = GET_COL_NAME;2187cb.cb_columns[1] = GET_COL_PROPERTY;2188cb.cb_columns[2] = GET_COL_VALUE;2189cb.cb_columns[3] = GET_COL_SOURCE;2190cb.cb_type = ZFS_TYPE_DATASET;21912192struct option long_options[] = {2193{"json", no_argument, NULL, 'j'},2194{"json-int", no_argument, NULL, ZFS_OPTION_JSON_NUMS_AS_INT},2195{0, 0, 0, 0}2196};21972198/* check options */2199while ((c = getopt_long(argc, argv, ":d:o:s:jrt:Hp", long_options,2200NULL)) != -1) {2201switch (c) {2202case 'p':2203cb.cb_literal = B_TRUE;2204break;2205case 'd':2206limit = parse_depth(optarg, &flags);2207break;2208case 'r':2209flags |= ZFS_ITER_RECURSE;2210break;2211case 'H':2212cb.cb_scripted = B_TRUE;2213break;2214case 'j':2215cb.cb_json = B_TRUE;2216cb.cb_jsobj = zfs_json_schema(0, 1);2217data = fnvlist_alloc();2218fnvlist_add_nvlist(cb.cb_jsobj, "datasets", data);2219fnvlist_free(data);2220break;2221case ZFS_OPTION_JSON_NUMS_AS_INT:2222cb.cb_json_as_int = B_TRUE;2223cb.cb_literal = B_TRUE;2224break;2225case ':':2226(void) fprintf(stderr, gettext("missing argument for "2227"'%c' option\n"), optopt);2228usage(B_FALSE);2229break;2230case 'o':2231/*2232* Process the set of columns to display. We zero out2233* the structure to give us a blank slate.2234*/2235memset(&cb.cb_columns, 0, sizeof (cb.cb_columns));22362237i = 0;2238for (char *tok; (tok = strsep(&optarg, ",")); ) {2239static const char *const col_subopts[] =2240{ "name", "property", "value",2241"received", "source", "all" };2242static const zfs_get_column_t col_subopt_col[] =2243{ GET_COL_NAME, GET_COL_PROPERTY, GET_COL_VALUE,2244GET_COL_RECVD, GET_COL_SOURCE };2245static const int col_subopt_flags[] =2246{ 0, 0, 0, ZFS_ITER_RECVD_PROPS, 0 };22472248if (i == ZFS_GET_NCOLS) {2249(void) fprintf(stderr, gettext("too "2250"many fields given to -o "2251"option\n"));2252usage(B_FALSE);2253}22542255for (c = 0; c < ARRAY_SIZE(col_subopts); ++c)2256if (strcmp(tok, col_subopts[c]) == 0)2257goto found;22582259(void) fprintf(stderr,2260gettext("invalid column name '%s'\n"), tok);2261usage(B_FALSE);22622263found:2264if (c >= 5) {2265if (i > 0) {2266(void) fprintf(stderr,2267gettext("\"all\" conflicts "2268"with specific fields "2269"given to -o option\n"));2270usage(B_FALSE);2271}22722273memcpy(cb.cb_columns, col_subopt_col,2274sizeof (col_subopt_col));2275flags |= ZFS_ITER_RECVD_PROPS;2276i = ZFS_GET_NCOLS;2277} else {2278cb.cb_columns[i++] = col_subopt_col[c];2279flags |= col_subopt_flags[c];2280}2281}2282break;22832284case 's':2285cb.cb_sources = 0;22862287for (char *tok; (tok = strsep(&optarg, ",")); ) {2288static const char *const source_opt[] = {2289"local", "default",2290"inherited", "received",2291"temporary", "none" };2292static const int source_flg[] = {2293ZPROP_SRC_LOCAL, ZPROP_SRC_DEFAULT,2294ZPROP_SRC_INHERITED, ZPROP_SRC_RECEIVED,2295ZPROP_SRC_TEMPORARY, ZPROP_SRC_NONE };22962297for (i = 0; i < ARRAY_SIZE(source_opt); ++i)2298if (strcmp(tok, source_opt[i]) == 0) {2299cb.cb_sources |= source_flg[i];2300goto found2;2301}23022303(void) fprintf(stderr,2304gettext("invalid source '%s'\n"), tok);2305usage(B_FALSE);2306found2:;2307}2308break;23092310case 't':2311types = 0;2312flags &= ~ZFS_ITER_PROP_LISTSNAPS;23132314for (char *tok; (tok = strsep(&optarg, ",")); ) {2315static const char *const type_opts[] = {2316"filesystem",2317"fs",2318"volume",2319"vol",2320"snapshot",2321"snap",2322"bookmark",2323"all"2324};2325static const int type_types[] = {2326ZFS_TYPE_FILESYSTEM,2327ZFS_TYPE_FILESYSTEM,2328ZFS_TYPE_VOLUME,2329ZFS_TYPE_VOLUME,2330ZFS_TYPE_SNAPSHOT,2331ZFS_TYPE_SNAPSHOT,2332ZFS_TYPE_BOOKMARK,2333ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK2334};23352336for (i = 0; i < ARRAY_SIZE(type_opts); ++i)2337if (strcmp(tok, type_opts[i]) == 0) {2338types |= type_types[i];2339goto found3;2340}23412342(void) fprintf(stderr,2343gettext("invalid type '%s'\n"), tok);2344usage(B_FALSE);2345found3:;2346}2347break;2348case '?':2349(void) fprintf(stderr, gettext("invalid option '%c'\n"),2350optopt);2351usage(B_FALSE);2352}2353}23542355argc -= optind;2356argv += optind;23572358if (argc < 1) {2359(void) fprintf(stderr, gettext("missing property "2360"argument\n"));2361usage(B_FALSE);2362}23632364if (!cb.cb_json && cb.cb_json_as_int) {2365(void) fprintf(stderr, gettext("'--json-int' only works with"2366" '-j' option\n"));2367usage(B_FALSE);2368}23692370fields = argv[0];23712372/*2373* Handle users who want to get all snapshots or bookmarks2374* of a dataset (ex. 'zfs get -t snapshot refer <dataset>').2375*/2376if ((types == ZFS_TYPE_SNAPSHOT || types == ZFS_TYPE_BOOKMARK) &&2377argc > 1 && (flags & ZFS_ITER_RECURSE) == 0 && limit == 0) {2378flags |= (ZFS_ITER_DEPTH_LIMIT | ZFS_ITER_RECURSE);2379limit = 1;2380}23812382if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)2383!= 0)2384usage(B_FALSE);23852386argc--;2387argv++;23882389/*2390* As part of zfs_expand_proplist(), we keep track of the maximum column2391* width for each property. For the 'NAME' (and 'SOURCE') columns, we2392* need to know the maximum name length. However, the user likely did2393* not specify 'name' as one of the properties to fetch, so we need to2394* make sure we always include at least this property for2395* print_get_headers() to work properly.2396*/2397if (cb.cb_proplist != NULL) {2398fake_name.pl_prop = ZFS_PROP_NAME;2399fake_name.pl_width = strlen(gettext("NAME"));2400fake_name.pl_next = cb.cb_proplist;2401cb.cb_proplist = &fake_name;2402}24032404cb.cb_first = B_TRUE;24052406/* run for each object */2407ret = zfs_for_each(argc, argv, flags, types, NULL,2408&cb.cb_proplist, limit, get_callback, &cb);24092410if (ret == 0 && cb.cb_json)2411zcmd_print_json(cb.cb_jsobj);2412else if (ret != 0 && cb.cb_json)2413nvlist_free(cb.cb_jsobj);24142415if (cb.cb_proplist == &fake_name)2416zprop_free_list(fake_name.pl_next);2417else2418zprop_free_list(cb.cb_proplist);24192420return (ret);2421}24222423/*2424* inherit [-rS] <property> <fs|vol> ...2425*2426* -r Recurse over all children2427* -S Revert to received value, if any2428*2429* For each dataset specified on the command line, inherit the given property2430* from its parent. Inheriting a property at the pool level will cause it to2431* use the default value. The '-r' flag will recurse over all children, and is2432* useful for setting a property on a hierarchy-wide basis, regardless of any2433* local modifications for each dataset.2434*/24352436typedef struct inherit_cbdata {2437const char *cb_propname;2438boolean_t cb_received;2439} inherit_cbdata_t;24402441static int2442inherit_recurse_cb(zfs_handle_t *zhp, void *data)2443{2444inherit_cbdata_t *cb = data;2445zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname);24462447/*2448* If we're doing it recursively, then ignore properties that2449* are not valid for this type of dataset.2450*/2451if (prop != ZPROP_INVAL &&2452!zfs_prop_valid_for_type(prop, zfs_get_type(zhp), B_FALSE))2453return (0);24542455return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);2456}24572458static int2459inherit_cb(zfs_handle_t *zhp, void *data)2460{2461inherit_cbdata_t *cb = data;24622463return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);2464}24652466static int2467zfs_do_inherit(int argc, char **argv)2468{2469int c;2470zfs_prop_t prop;2471inherit_cbdata_t cb = { 0 };2472char *propname;2473int ret = 0;2474int flags = 0;2475boolean_t received = B_FALSE;24762477/* check options */2478while ((c = getopt(argc, argv, "rS")) != -1) {2479switch (c) {2480case 'r':2481flags |= ZFS_ITER_RECURSE;2482break;2483case 'S':2484received = B_TRUE;2485break;2486case '?':2487default:2488(void) fprintf(stderr, gettext("invalid option '%c'\n"),2489optopt);2490usage(B_FALSE);2491}2492}24932494argc -= optind;2495argv += optind;24962497/* check number of arguments */2498if (argc < 1) {2499(void) fprintf(stderr, gettext("missing property argument\n"));2500usage(B_FALSE);2501}2502if (argc < 2) {2503(void) fprintf(stderr, gettext("missing dataset argument\n"));2504usage(B_FALSE);2505}25062507propname = argv[0];2508argc--;2509argv++;25102511if ((prop = zfs_name_to_prop(propname)) != ZPROP_USERPROP) {2512if (zfs_prop_readonly(prop)) {2513(void) fprintf(stderr, gettext(2514"%s property is read-only\n"),2515propname);2516return (1);2517}2518if (!zfs_prop_inheritable(prop) && !received) {2519(void) fprintf(stderr, gettext("'%s' property cannot "2520"be inherited\n"), propname);2521if (prop == ZFS_PROP_QUOTA ||2522prop == ZFS_PROP_RESERVATION ||2523prop == ZFS_PROP_REFQUOTA ||2524prop == ZFS_PROP_REFRESERVATION) {2525(void) fprintf(stderr, gettext("use 'zfs set "2526"%s=none' to clear\n"), propname);2527(void) fprintf(stderr, gettext("use 'zfs "2528"inherit -S %s' to revert to received "2529"value\n"), propname);2530}2531return (1);2532}2533if (received && (prop == ZFS_PROP_VOLSIZE ||2534prop == ZFS_PROP_VERSION)) {2535(void) fprintf(stderr, gettext("'%s' property cannot "2536"be reverted to a received value\n"), propname);2537return (1);2538}2539} else if (!zfs_prop_user(propname)) {2540(void) fprintf(stderr, gettext("invalid property '%s'\n"),2541propname);2542usage(B_FALSE);2543}25442545cb.cb_propname = propname;2546cb.cb_received = received;25472548if (flags & ZFS_ITER_RECURSE) {2549ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,2550NULL, NULL, 0, inherit_recurse_cb, &cb);2551} else {2552ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,2553NULL, NULL, 0, inherit_cb, &cb);2554}25552556return (ret);2557}25582559typedef struct upgrade_cbdata {2560uint64_t cb_numupgraded;2561uint64_t cb_numsamegraded;2562uint64_t cb_numfailed;2563uint64_t cb_version;2564boolean_t cb_newer;2565boolean_t cb_foundone;2566char cb_lastfs[ZFS_MAX_DATASET_NAME_LEN];2567} upgrade_cbdata_t;25682569static int2570same_pool(zfs_handle_t *zhp, const char *name)2571{2572int len1 = strcspn(name, "/@");2573const char *zhname = zfs_get_name(zhp);2574int len2 = strcspn(zhname, "/@");25752576if (len1 != len2)2577return (B_FALSE);2578return (strncmp(name, zhname, len1) == 0);2579}25802581static int2582upgrade_list_callback(zfs_handle_t *zhp, void *data)2583{2584upgrade_cbdata_t *cb = data;2585int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);25862587/* list if it's old/new */2588if ((!cb->cb_newer && version < ZPL_VERSION) ||2589(cb->cb_newer && version > ZPL_VERSION)) {2590char *str;2591if (cb->cb_newer) {2592str = gettext("The following filesystems are "2593"formatted using a newer software version and\n"2594"cannot be accessed on the current system.\n\n");2595} else {2596str = gettext("The following filesystems are "2597"out of date, and can be upgraded. After being\n"2598"upgraded, these filesystems (and any 'zfs send' "2599"streams generated from\n"2600"subsequent snapshots) will no longer be "2601"accessible by older software versions.\n\n");2602}26032604if (!cb->cb_foundone) {2605(void) puts(str);2606(void) printf(gettext("VER FILESYSTEM\n"));2607(void) printf(gettext("--- ------------\n"));2608cb->cb_foundone = B_TRUE;2609}26102611(void) printf("%2u %s\n", version, zfs_get_name(zhp));2612}26132614return (0);2615}26162617static int2618upgrade_set_callback(zfs_handle_t *zhp, void *data)2619{2620upgrade_cbdata_t *cb = data;2621int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);2622int needed_spa_version;2623int spa_version;26242625if (zfs_spa_version(zhp, &spa_version) < 0)2626return (-1);26272628needed_spa_version = zfs_spa_version_map(cb->cb_version);26292630if (needed_spa_version < 0)2631return (-1);26322633if (spa_version < needed_spa_version) {2634/* can't upgrade */2635(void) printf(gettext("%s: can not be "2636"upgraded; the pool version needs to first "2637"be upgraded\nto version %d\n\n"),2638zfs_get_name(zhp), needed_spa_version);2639cb->cb_numfailed++;2640return (0);2641}26422643/* upgrade */2644if (version < cb->cb_version) {2645char verstr[24];2646(void) snprintf(verstr, sizeof (verstr),2647"%llu", (u_longlong_t)cb->cb_version);2648if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {2649/*2650* If they did "zfs upgrade -a", then we could2651* be doing ioctls to different pools. We need2652* to log this history once to each pool, and bypass2653* the normal history logging that happens in main().2654*/2655(void) zpool_log_history(g_zfs, history_str);2656log_history = B_FALSE;2657}2658if (zfs_prop_set(zhp, "version", verstr) == 0)2659cb->cb_numupgraded++;2660else2661cb->cb_numfailed++;2662(void) strlcpy(cb->cb_lastfs, zfs_get_name(zhp),2663sizeof (cb->cb_lastfs));2664} else if (version > cb->cb_version) {2665/* can't downgrade */2666(void) printf(gettext("%s: can not be downgraded; "2667"it is already at version %u\n"),2668zfs_get_name(zhp), version);2669cb->cb_numfailed++;2670} else {2671cb->cb_numsamegraded++;2672}2673return (0);2674}26752676/*2677* zfs upgrade2678* zfs upgrade -v2679* zfs upgrade [-r] [-V <version>] <-a | filesystem>2680*/2681static int2682zfs_do_upgrade(int argc, char **argv)2683{2684boolean_t all = B_FALSE;2685boolean_t showversions = B_FALSE;2686int ret = 0;2687upgrade_cbdata_t cb = { 0 };2688int c;2689int flags = ZFS_ITER_ARGS_CAN_BE_PATHS;26902691/* check options */2692while ((c = getopt(argc, argv, "rvV:a")) != -1) {2693switch (c) {2694case 'r':2695flags |= ZFS_ITER_RECURSE;2696break;2697case 'v':2698showversions = B_TRUE;2699break;2700case 'V':2701if (zfs_prop_string_to_index(ZFS_PROP_VERSION,2702optarg, &cb.cb_version) != 0) {2703(void) fprintf(stderr,2704gettext("invalid version %s\n"), optarg);2705usage(B_FALSE);2706}2707break;2708case 'a':2709all = B_TRUE;2710break;2711case '?':2712default:2713(void) fprintf(stderr, gettext("invalid option '%c'\n"),2714optopt);2715usage(B_FALSE);2716}2717}27182719argc -= optind;2720argv += optind;27212722if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version))2723usage(B_FALSE);2724if (showversions && (flags & ZFS_ITER_RECURSE || all ||2725cb.cb_version || argc))2726usage(B_FALSE);2727if ((all || argc) && (showversions))2728usage(B_FALSE);2729if (all && argc)2730usage(B_FALSE);27312732if (showversions) {2733/* Show info on available versions. */2734(void) printf(gettext("The following filesystem versions are "2735"supported:\n\n"));2736(void) printf(gettext("VER DESCRIPTION\n"));2737(void) printf("--- -----------------------------------------"2738"---------------\n");2739(void) printf(gettext(" 1 Initial ZFS filesystem version\n"));2740(void) printf(gettext(" 2 Enhanced directory entries\n"));2741(void) printf(gettext(" 3 Case insensitive and filesystem "2742"user identifier (FUID)\n"));2743(void) printf(gettext(" 4 userquota, groupquota "2744"properties\n"));2745(void) printf(gettext(" 5 System attributes\n"));2746(void) printf(gettext("\nFor more information on a particular "2747"version, including supported releases,\n"));2748(void) printf("see the ZFS Administration Guide.\n\n");2749ret = 0;2750} else if (argc || all) {2751/* Upgrade filesystems */2752if (cb.cb_version == 0)2753cb.cb_version = ZPL_VERSION;2754ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM,2755NULL, NULL, 0, upgrade_set_callback, &cb);2756(void) printf(gettext("%llu filesystems upgraded\n"),2757(u_longlong_t)cb.cb_numupgraded);2758if (cb.cb_numsamegraded) {2759(void) printf(gettext("%llu filesystems already at "2760"this version\n"),2761(u_longlong_t)cb.cb_numsamegraded);2762}2763if (cb.cb_numfailed != 0)2764ret = 1;2765} else {2766/* List old-version filesystems */2767boolean_t found;2768(void) printf(gettext("This system is currently running "2769"ZFS filesystem version %llu.\n\n"), ZPL_VERSION);27702771flags |= ZFS_ITER_RECURSE;2772ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,2773NULL, NULL, 0, upgrade_list_callback, &cb);27742775found = cb.cb_foundone;2776cb.cb_foundone = B_FALSE;2777cb.cb_newer = B_TRUE;27782779ret |= zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,2780NULL, NULL, 0, upgrade_list_callback, &cb);27812782if (!cb.cb_foundone && !found) {2783(void) printf(gettext("All filesystems are "2784"formatted with the current version.\n"));2785}2786}27872788return (ret);2789}27902791/*2792* zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...]2793* [-S field [-S field]...] [-t type[,...]]2794* filesystem | snapshot | path2795* zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...]2796* [-S field [-S field]...] [-t type[,...]]2797* filesystem | snapshot | path2798* zfs projectspace [-Hp] [-o field[,...]] [-s field [-s field]...]2799* [-S field [-S field]...] filesystem | snapshot | path2800*2801* -H Scripted mode; elide headers and separate columns by tabs.2802* -i Translate SID to POSIX ID.2803* -n Print numeric ID instead of user/group name.2804* -o Control which fields to display.2805* -p Use exact (parsable) numeric output.2806* -s Specify sort columns, descending order.2807* -S Specify sort columns, ascending order.2808* -t Control which object types to display.2809*2810* Displays space consumed by, and quotas on, each user in the specified2811* filesystem or snapshot.2812*/28132814/* us_field_types, us_field_hdr and us_field_names should be kept in sync */2815enum us_field_types {2816USFIELD_TYPE,2817USFIELD_NAME,2818USFIELD_USED,2819USFIELD_QUOTA,2820USFIELD_OBJUSED,2821USFIELD_OBJQUOTA2822};2823static const char *const us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA",2824"OBJUSED", "OBJQUOTA" };2825static const char *const us_field_names[] = { "type", "name", "used", "quota",2826"objused", "objquota" };2827#define USFIELD_LAST (sizeof (us_field_names) / sizeof (char *))28282829#define USTYPE_PSX_GRP (1 << 0)2830#define USTYPE_PSX_USR (1 << 1)2831#define USTYPE_SMB_GRP (1 << 2)2832#define USTYPE_SMB_USR (1 << 3)2833#define USTYPE_PROJ (1 << 4)2834#define USTYPE_ALL \2835(USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR | \2836USTYPE_PROJ)28372838static int us_type_bits[] = {2839USTYPE_PSX_GRP,2840USTYPE_PSX_USR,2841USTYPE_SMB_GRP,2842USTYPE_SMB_USR,2843USTYPE_ALL2844};2845static const char *const us_type_names[] = { "posixgroup", "posixuser",2846"smbgroup", "smbuser", "all" };28472848typedef struct us_node {2849nvlist_t *usn_nvl;2850uu_avl_node_t usn_avlnode;2851uu_list_node_t usn_listnode;2852} us_node_t;28532854typedef struct us_cbdata {2855nvlist_t **cb_nvlp;2856uu_avl_pool_t *cb_avl_pool;2857uu_avl_t *cb_avl;2858boolean_t cb_numname;2859boolean_t cb_nicenum;2860boolean_t cb_sid2posix;2861zfs_userquota_prop_t cb_prop;2862zfs_sort_column_t *cb_sortcol;2863size_t cb_width[USFIELD_LAST];2864} us_cbdata_t;28652866static boolean_t us_populated = B_FALSE;28672868typedef struct {2869zfs_sort_column_t *si_sortcol;2870boolean_t si_numname;2871} us_sort_info_t;28722873static int2874us_field_index(const char *field)2875{2876for (int i = 0; i < USFIELD_LAST; i++) {2877if (strcmp(field, us_field_names[i]) == 0)2878return (i);2879}28802881return (-1);2882}28832884static int2885us_compare(const void *larg, const void *rarg, void *unused)2886{2887const us_node_t *l = larg;2888const us_node_t *r = rarg;2889us_sort_info_t *si = (us_sort_info_t *)unused;2890zfs_sort_column_t *sortcol = si->si_sortcol;2891boolean_t numname = si->si_numname;2892nvlist_t *lnvl = l->usn_nvl;2893nvlist_t *rnvl = r->usn_nvl;2894int rc = 0;2895boolean_t lvb, rvb;28962897for (; sortcol != NULL; sortcol = sortcol->sc_next) {2898const char *lvstr = "";2899const char *rvstr = "";2900uint32_t lv32 = 0;2901uint32_t rv32 = 0;2902uint64_t lv64 = 0;2903uint64_t rv64 = 0;2904zfs_prop_t prop = sortcol->sc_prop;2905const char *propname = NULL;2906boolean_t reverse = sortcol->sc_reverse;29072908switch (prop) {2909case ZFS_PROP_TYPE:2910propname = "type";2911(void) nvlist_lookup_uint32(lnvl, propname, &lv32);2912(void) nvlist_lookup_uint32(rnvl, propname, &rv32);2913if (rv32 != lv32)2914rc = (rv32 < lv32) ? 1 : -1;2915break;2916case ZFS_PROP_NAME:2917propname = "name";2918if (numname) {2919compare_nums:2920(void) nvlist_lookup_uint64(lnvl, propname,2921&lv64);2922(void) nvlist_lookup_uint64(rnvl, propname,2923&rv64);2924if (rv64 != lv64)2925rc = (rv64 < lv64) ? 1 : -1;2926} else {2927if ((nvlist_lookup_string(lnvl, propname,2928&lvstr) == ENOENT) ||2929(nvlist_lookup_string(rnvl, propname,2930&rvstr) == ENOENT)) {2931goto compare_nums;2932}2933rc = strcmp(lvstr, rvstr);2934}2935break;2936case ZFS_PROP_USED:2937case ZFS_PROP_QUOTA:2938if (!us_populated)2939break;2940if (prop == ZFS_PROP_USED)2941propname = "used";2942else2943propname = "quota";2944(void) nvlist_lookup_uint64(lnvl, propname, &lv64);2945(void) nvlist_lookup_uint64(rnvl, propname, &rv64);2946if (rv64 != lv64)2947rc = (rv64 < lv64) ? 1 : -1;2948break;29492950default:2951break;2952}29532954if (rc != 0) {2955if (rc < 0)2956return (reverse ? 1 : -1);2957else2958return (reverse ? -1 : 1);2959}2960}29612962/*2963* If entries still seem to be the same, check if they are of the same2964* type (smbentity is added only if we are doing SID to POSIX ID2965* translation where we can have duplicate type/name combinations).2966*/2967if (nvlist_lookup_boolean_value(lnvl, "smbentity", &lvb) == 0 &&2968nvlist_lookup_boolean_value(rnvl, "smbentity", &rvb) == 0 &&2969lvb != rvb)2970return (lvb < rvb ? -1 : 1);29712972return (0);2973}29742975static boolean_t2976zfs_prop_is_user(unsigned p)2977{2978return (p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA ||2979p == ZFS_PROP_USEROBJUSED || p == ZFS_PROP_USEROBJQUOTA);2980}29812982static boolean_t2983zfs_prop_is_group(unsigned p)2984{2985return (p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA ||2986p == ZFS_PROP_GROUPOBJUSED || p == ZFS_PROP_GROUPOBJQUOTA);2987}29882989static boolean_t2990zfs_prop_is_project(unsigned p)2991{2992return (p == ZFS_PROP_PROJECTUSED || p == ZFS_PROP_PROJECTQUOTA ||2993p == ZFS_PROP_PROJECTOBJUSED || p == ZFS_PROP_PROJECTOBJQUOTA);2994}29952996static inline const char *2997us_type2str(unsigned field_type)2998{2999switch (field_type) {3000case USTYPE_PSX_USR:3001return ("POSIX User");3002case USTYPE_PSX_GRP:3003return ("POSIX Group");3004case USTYPE_SMB_USR:3005return ("SMB User");3006case USTYPE_SMB_GRP:3007return ("SMB Group");3008case USTYPE_PROJ:3009return ("Project");3010default:3011return ("Undefined");3012}3013}30143015static int3016userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space,3017uint64_t default_quota)3018{3019us_cbdata_t *cb = (us_cbdata_t *)arg;3020zfs_userquota_prop_t prop = cb->cb_prop;3021char *name = NULL;3022const char *propname;3023char sizebuf[32];3024us_node_t *node;3025uu_avl_pool_t *avl_pool = cb->cb_avl_pool;3026uu_avl_t *avl = cb->cb_avl;3027uu_avl_index_t idx;3028nvlist_t *props;3029us_node_t *n;3030zfs_sort_column_t *sortcol = cb->cb_sortcol;3031unsigned type = 0;3032const char *typestr;3033size_t namelen;3034size_t typelen;3035size_t sizelen;3036int typeidx, nameidx, sizeidx;3037us_sort_info_t sortinfo = { sortcol, cb->cb_numname };3038boolean_t smbentity = B_FALSE;30393040if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)3041nomem();3042node = safe_malloc(sizeof (us_node_t));3043uu_avl_node_init(node, &node->usn_avlnode, avl_pool);3044node->usn_nvl = props;30453046if (domain != NULL && domain[0] != '\0') {3047#ifdef HAVE_IDMAP3048/* SMB */3049char sid[MAXNAMELEN + 32];3050uid_t id;3051uint64_t classes;3052int err;3053directory_error_t e;30543055smbentity = B_TRUE;30563057(void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid);30583059if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {3060type = USTYPE_SMB_GRP;3061err = sid_to_id(sid, B_FALSE, &id);3062} else {3063type = USTYPE_SMB_USR;3064err = sid_to_id(sid, B_TRUE, &id);3065}30663067if (err == 0) {3068rid = id;3069if (!cb->cb_sid2posix) {3070e = directory_name_from_sid(NULL, sid, &name,3071&classes);3072if (e != NULL)3073directory_error_free(e);3074if (name == NULL)3075name = sid;3076}3077}3078#else3079nvlist_free(props);3080free(node);30813082return (-1);3083#endif /* HAVE_IDMAP */3084}30853086if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') {3087/* POSIX or -i */3088if (zfs_prop_is_group(prop)) {3089type = USTYPE_PSX_GRP;3090if (!cb->cb_numname) {3091struct group *g;30923093if ((g = getgrgid(rid)) != NULL)3094name = g->gr_name;3095}3096} else if (zfs_prop_is_user(prop)) {3097type = USTYPE_PSX_USR;3098if (!cb->cb_numname) {3099struct passwd *p;31003101if ((p = getpwuid(rid)) != NULL)3102name = p->pw_name;3103}3104} else {3105type = USTYPE_PROJ;3106}3107}31083109/*3110* Make sure that the type/name combination is unique when doing3111* SID to POSIX ID translation (hence changing the type from SMB to3112* POSIX).3113*/3114if (cb->cb_sid2posix &&3115nvlist_add_boolean_value(props, "smbentity", smbentity) != 0)3116nomem();31173118/* Calculate/update width of TYPE field */3119typestr = us_type2str(type);3120typelen = strlen(gettext(typestr));3121typeidx = us_field_index("type");3122if (typelen > cb->cb_width[typeidx])3123cb->cb_width[typeidx] = typelen;3124if (nvlist_add_uint32(props, "type", type) != 0)3125nomem();31263127/* Calculate/update width of NAME field */3128if ((cb->cb_numname && cb->cb_sid2posix) || name == NULL) {3129if (nvlist_add_uint64(props, "name", rid) != 0)3130nomem();3131namelen = snprintf(NULL, 0, "%u", rid);3132} else {3133if (nvlist_add_string(props, "name", name) != 0)3134nomem();3135namelen = strlen(name);3136}3137nameidx = us_field_index("name");3138if (nameidx >= 0 && namelen > cb->cb_width[nameidx])3139cb->cb_width[nameidx] = namelen;31403141/*3142* Check if this type/name combination is in the list and update it;3143* otherwise add new node to the list.3144*/3145if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) {3146uu_avl_insert(avl, node, idx);3147} else {3148nvlist_free(props);3149free(node);3150node = n;3151props = node->usn_nvl;3152}31533154/* Calculate/update width of USED/QUOTA fields */3155if (cb->cb_nicenum) {3156if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED ||3157prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA ||3158prop == ZFS_PROP_PROJECTUSED ||3159prop == ZFS_PROP_PROJECTQUOTA) {3160zfs_nicebytes(space, sizebuf, sizeof (sizebuf));3161} else {3162zfs_nicenum(space, sizebuf, sizeof (sizebuf));3163}3164} else {3165(void) snprintf(sizebuf, sizeof (sizebuf), "%llu",3166(u_longlong_t)space);3167}3168sizelen = strlen(sizebuf);3169if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED ||3170prop == ZFS_PROP_PROJECTUSED) {3171propname = "used";3172if (!nvlist_exists(props, "quota"))3173(void) nvlist_add_uint64(props, "quota", default_quota);3174} else if (prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA ||3175prop == ZFS_PROP_PROJECTQUOTA) {3176propname = "quota";3177if (!nvlist_exists(props, "used"))3178(void) nvlist_add_uint64(props, "used", 0);3179} else if (prop == ZFS_PROP_USEROBJUSED ||3180prop == ZFS_PROP_GROUPOBJUSED || prop == ZFS_PROP_PROJECTOBJUSED) {3181propname = "objused";3182if (!nvlist_exists(props, "objquota")) {3183(void) nvlist_add_uint64(props, "objquota",3184default_quota);3185}3186} else if (prop == ZFS_PROP_USEROBJQUOTA ||3187prop == ZFS_PROP_GROUPOBJQUOTA ||3188prop == ZFS_PROP_PROJECTOBJQUOTA) {3189propname = "objquota";3190if (!nvlist_exists(props, "objused"))3191(void) nvlist_add_uint64(props, "objused", 0);3192} else {3193return (-1);3194}3195sizeidx = us_field_index(propname);3196if (sizeidx >= 0 && sizelen > cb->cb_width[sizeidx])3197cb->cb_width[sizeidx] = sizelen;31983199if (nvlist_add_uint64(props, propname, space) != 0)3200nomem();32013202return (0);3203}32043205static void3206print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types,3207size_t *width, us_node_t *node)3208{3209nvlist_t *nvl = node->usn_nvl;3210char valstr[MAXNAMELEN];3211boolean_t first = B_TRUE;3212int cfield = 0;3213int field;3214uint32_t ustype;32153216/* Check type */3217(void) nvlist_lookup_uint32(nvl, "type", &ustype);3218if (!(ustype & types))3219return;32203221while ((field = fields[cfield]) != USFIELD_LAST) {3222nvpair_t *nvp = NULL;3223data_type_t type;3224uint32_t val32 = -1;3225uint64_t val64 = -1;3226const char *strval = "-";32273228while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL)3229if (strcmp(nvpair_name(nvp),3230us_field_names[field]) == 0)3231break;32323233type = nvp == NULL ? DATA_TYPE_UNKNOWN : nvpair_type(nvp);3234switch (type) {3235case DATA_TYPE_UINT32:3236val32 = fnvpair_value_uint32(nvp);3237break;3238case DATA_TYPE_UINT64:3239val64 = fnvpair_value_uint64(nvp);3240break;3241case DATA_TYPE_STRING:3242strval = fnvpair_value_string(nvp);3243break;3244case DATA_TYPE_UNKNOWN:3245break;3246default:3247(void) fprintf(stderr, "invalid data type\n");3248}32493250switch (field) {3251case USFIELD_TYPE:3252if (type == DATA_TYPE_UINT32)3253strval = us_type2str(val32);3254break;3255case USFIELD_NAME:3256if (type == DATA_TYPE_UINT64) {3257(void) sprintf(valstr, "%llu",3258(u_longlong_t)val64);3259strval = valstr;3260}3261break;3262case USFIELD_USED:3263case USFIELD_QUOTA:3264if (type == DATA_TYPE_UINT64) {3265if (parsable) {3266(void) sprintf(valstr, "%llu",3267(u_longlong_t)val64);3268strval = valstr;3269} else if (field == USFIELD_QUOTA &&3270val64 == 0) {3271strval = "none";3272} else {3273zfs_nicebytes(val64, valstr,3274sizeof (valstr));3275strval = valstr;3276}3277}3278break;3279case USFIELD_OBJUSED:3280case USFIELD_OBJQUOTA:3281if (type == DATA_TYPE_UINT64) {3282if (parsable) {3283(void) sprintf(valstr, "%llu",3284(u_longlong_t)val64);3285strval = valstr;3286} else if (field == USFIELD_OBJQUOTA &&3287val64 == 0) {3288strval = "none";3289} else {3290zfs_nicenum(val64, valstr,3291sizeof (valstr));3292strval = valstr;3293}3294}3295break;3296}32973298if (!first) {3299if (scripted)3300(void) putchar('\t');3301else3302(void) fputs(" ", stdout);3303}3304if (scripted)3305(void) fputs(strval, stdout);3306else if (field == USFIELD_TYPE || field == USFIELD_NAME)3307(void) printf("%-*s", (int)width[field], strval);3308else3309(void) printf("%*s", (int)width[field], strval);33103311first = B_FALSE;3312cfield++;3313}33143315(void) putchar('\n');3316}33173318static void3319print_us(boolean_t scripted, boolean_t parsable, int *fields, int types,3320size_t *width, boolean_t rmnode, uu_avl_t *avl)3321{3322us_node_t *node;3323const char *col;3324int cfield = 0;3325int field;33263327if (!scripted) {3328boolean_t first = B_TRUE;33293330while ((field = fields[cfield]) != USFIELD_LAST) {3331col = gettext(us_field_hdr[field]);3332if (field == USFIELD_TYPE || field == USFIELD_NAME) {3333(void) printf(first ? "%-*s" : " %-*s",3334(int)width[field], col);3335} else {3336(void) printf(first ? "%*s" : " %*s",3337(int)width[field], col);3338}3339first = B_FALSE;3340cfield++;3341}3342(void) printf("\n");3343}33443345for (node = uu_avl_first(avl); node; node = uu_avl_next(avl, node)) {3346print_us_node(scripted, parsable, fields, types, width, node);3347if (rmnode)3348nvlist_free(node->usn_nvl);3349}3350}33513352static int3353zfs_do_userspace(int argc, char **argv)3354{3355zfs_handle_t *zhp;3356zfs_userquota_prop_t p;3357uu_avl_pool_t *avl_pool;3358uu_avl_t *avl_tree;3359uu_avl_walk_t *walk;3360char *delim;3361char deffields[] = "type,name,used,quota,objused,objquota";3362char *ofield = NULL;3363char *tfield = NULL;3364int cfield = 0;3365int fields[256];3366int i;3367boolean_t scripted = B_FALSE;3368boolean_t prtnum = B_FALSE;3369boolean_t parsable = B_FALSE;3370boolean_t sid2posix = B_FALSE;3371int ret = 0;3372int c;3373zfs_sort_column_t *sortcol = NULL;3374int types = USTYPE_PSX_USR | USTYPE_SMB_USR;3375us_cbdata_t cb;3376us_node_t *node;3377us_node_t *rmnode;3378uu_list_pool_t *listpool;3379uu_list_t *list;3380uu_avl_index_t idx = 0;3381uu_list_index_t idx2 = 0;33823383if (argc < 2)3384usage(B_FALSE);33853386if (strcmp(argv[0], "groupspace") == 0) {3387/* Toggle default group types */3388types = USTYPE_PSX_GRP | USTYPE_SMB_GRP;3389} else if (strcmp(argv[0], "projectspace") == 0) {3390types = USTYPE_PROJ;3391prtnum = B_TRUE;3392}33933394while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) {3395switch (c) {3396case 'n':3397if (types == USTYPE_PROJ) {3398(void) fprintf(stderr,3399gettext("invalid option 'n'\n"));3400usage(B_FALSE);3401}3402prtnum = B_TRUE;3403break;3404case 'H':3405scripted = B_TRUE;3406break;3407case 'p':3408parsable = B_TRUE;3409break;3410case 'o':3411ofield = optarg;3412break;3413case 's':3414case 'S':3415if (zfs_add_sort_column(&sortcol, optarg,3416c == 's' ? B_FALSE : B_TRUE) != 0) {3417(void) fprintf(stderr,3418gettext("invalid field '%s'\n"), optarg);3419usage(B_FALSE);3420}3421break;3422case 't':3423if (types == USTYPE_PROJ) {3424(void) fprintf(stderr,3425gettext("invalid option 't'\n"));3426usage(B_FALSE);3427}3428tfield = optarg;3429break;3430case 'i':3431if (types == USTYPE_PROJ) {3432(void) fprintf(stderr,3433gettext("invalid option 'i'\n"));3434usage(B_FALSE);3435}3436sid2posix = B_TRUE;3437break;3438case ':':3439(void) fprintf(stderr, gettext("missing argument for "3440"'%c' option\n"), optopt);3441usage(B_FALSE);3442break;3443case '?':3444(void) fprintf(stderr, gettext("invalid option '%c'\n"),3445optopt);3446usage(B_FALSE);3447}3448}34493450argc -= optind;3451argv += optind;34523453if (argc < 1) {3454(void) fprintf(stderr, gettext("missing dataset name\n"));3455usage(B_FALSE);3456}3457if (argc > 1) {3458(void) fprintf(stderr, gettext("too many arguments\n"));3459usage(B_FALSE);3460}34613462/* Use default output fields if not specified using -o */3463if (ofield == NULL)3464ofield = deffields;3465do {3466if ((delim = strchr(ofield, ',')) != NULL)3467*delim = '\0';3468if ((fields[cfield++] = us_field_index(ofield)) == -1) {3469(void) fprintf(stderr, gettext("invalid type '%s' "3470"for -o option\n"), ofield);3471return (-1);3472}3473if (delim != NULL)3474ofield = delim + 1;3475} while (delim != NULL);3476fields[cfield] = USFIELD_LAST;34773478/* Override output types (-t option) */3479if (tfield != NULL) {3480types = 0;34813482do {3483boolean_t found = B_FALSE;34843485if ((delim = strchr(tfield, ',')) != NULL)3486*delim = '\0';3487for (i = 0; i < sizeof (us_type_bits) / sizeof (int);3488i++) {3489if (strcmp(tfield, us_type_names[i]) == 0) {3490found = B_TRUE;3491types |= us_type_bits[i];3492break;3493}3494}3495if (!found) {3496(void) fprintf(stderr, gettext("invalid type "3497"'%s' for -t option\n"), tfield);3498return (-1);3499}3500if (delim != NULL)3501tfield = delim + 1;3502} while (delim != NULL);3503}35043505if ((zhp = zfs_path_to_zhandle(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM |3506ZFS_TYPE_SNAPSHOT)) == NULL)3507return (1);3508if (zfs_get_underlying_type(zhp) != ZFS_TYPE_FILESYSTEM) {3509(void) fprintf(stderr, gettext("operation is only applicable "3510"to filesystems and their snapshots\n"));3511zfs_close(zhp);3512return (1);3513}35143515if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t),3516offsetof(us_node_t, usn_avlnode), us_compare, UU_DEFAULT)) == NULL)3517nomem();3518if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL)3519nomem();35203521/* Always add default sorting columns */3522(void) zfs_add_sort_column(&sortcol, "type", B_FALSE);3523(void) zfs_add_sort_column(&sortcol, "name", B_FALSE);35243525cb.cb_sortcol = sortcol;3526cb.cb_numname = prtnum;3527cb.cb_nicenum = !parsable;3528cb.cb_avl_pool = avl_pool;3529cb.cb_avl = avl_tree;3530cb.cb_sid2posix = sid2posix;35313532for (i = 0; i < USFIELD_LAST; i++)3533cb.cb_width[i] = strlen(gettext(us_field_hdr[i]));35343535for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) {3536if ((zfs_prop_is_user(p) &&3537!(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) ||3538(zfs_prop_is_group(p) &&3539!(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))) ||3540(zfs_prop_is_project(p) && types != USTYPE_PROJ))3541continue;35423543cb.cb_prop = p;3544if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0) {3545zfs_close(zhp);3546return (ret);3547}3548}3549zfs_close(zhp);35503551/* Sort the list */3552if ((node = uu_avl_first(avl_tree)) == NULL)3553return (0);35543555us_populated = B_TRUE;35563557listpool = uu_list_pool_create("tmplist", sizeof (us_node_t),3558offsetof(us_node_t, usn_listnode), NULL, UU_DEFAULT);3559list = uu_list_create(listpool, NULL, UU_DEFAULT);3560uu_list_node_init(node, &node->usn_listnode, listpool);35613562while (node != NULL) {3563rmnode = node;3564node = uu_avl_next(avl_tree, node);3565uu_avl_remove(avl_tree, rmnode);3566if (uu_list_find(list, rmnode, NULL, &idx2) == NULL)3567uu_list_insert(list, rmnode, idx2);3568}35693570for (node = uu_list_first(list); node != NULL;3571node = uu_list_next(list, node)) {3572us_sort_info_t sortinfo = { sortcol, cb.cb_numname };35733574if (uu_avl_find(avl_tree, node, &sortinfo, &idx) == NULL)3575uu_avl_insert(avl_tree, node, idx);3576}35773578uu_list_destroy(list);3579uu_list_pool_destroy(listpool);35803581/* Print and free node nvlist memory */3582print_us(scripted, parsable, fields, types, cb.cb_width, B_TRUE,3583cb.cb_avl);35843585zfs_free_sort_columns(sortcol);35863587/* Clean up the AVL tree */3588if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL)3589nomem();35903591while ((node = uu_avl_walk_next(walk)) != NULL) {3592uu_avl_remove(cb.cb_avl, node);3593free(node);3594}35953596uu_avl_walk_end(walk);3597uu_avl_destroy(avl_tree);3598uu_avl_pool_destroy(avl_pool);35993600return (ret);3601}36023603/*3604* list [-Hp][-r|-d max] [-o property[,...]] [-s property] ... [-S property]3605* [-t type[,...]] [filesystem|volume|snapshot] ...3606*3607* -H Scripted mode; elide headers and separate columns by tabs3608* -p Display values in parsable (literal) format.3609* -r Recurse over all children3610* -d Limit recursion by depth.3611* -o Control which fields to display.3612* -s Specify sort columns, descending order.3613* -S Specify sort columns, ascending order.3614* -t Control which object types to display.3615*3616* When given no arguments, list all filesystems in the system.3617* Otherwise, list the specified datasets, optionally recursing down them if3618* '-r' is specified.3619*/3620typedef struct list_cbdata {3621boolean_t cb_first;3622boolean_t cb_literal;3623boolean_t cb_scripted;3624zprop_list_t *cb_proplist;3625boolean_t cb_json;3626nvlist_t *cb_jsobj;3627boolean_t cb_json_as_int;3628} list_cbdata_t;36293630/*3631* Given a list of columns to display, output appropriate headers for each one.3632*/3633static void3634print_header(list_cbdata_t *cb)3635{3636zprop_list_t *pl = cb->cb_proplist;3637char headerbuf[ZFS_MAXPROPLEN];3638const char *header;3639int i;3640boolean_t first = B_TRUE;3641boolean_t right_justify;36423643color_start(ANSI_BOLD);36443645for (; pl != NULL; pl = pl->pl_next) {3646if (!first) {3647(void) printf(" ");3648} else {3649first = B_FALSE;3650}36513652right_justify = B_FALSE;3653if (pl->pl_prop != ZPROP_USERPROP) {3654header = zfs_prop_column_name(pl->pl_prop);3655right_justify = zfs_prop_align_right(pl->pl_prop);3656} else {3657for (i = 0; pl->pl_user_prop[i] != '\0'; i++)3658headerbuf[i] = toupper(pl->pl_user_prop[i]);3659headerbuf[i] = '\0';3660header = headerbuf;3661}36623663if (pl->pl_next == NULL && !right_justify)3664(void) printf("%s", header);3665else if (right_justify)3666(void) printf("%*s", (int)pl->pl_width, header);3667else3668(void) printf("%-*s", (int)pl->pl_width, header);3669}36703671color_end();36723673(void) printf("\n");3674}36753676/*3677* Decides on the color that the avail value should be printed in.3678* > 80% used = yellow3679* > 90% used = red3680*/3681static const char *3682zfs_list_avail_color(zfs_handle_t *zhp)3683{3684uint64_t used = zfs_prop_get_int(zhp, ZFS_PROP_USED);3685uint64_t avail = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE);3686int percentage = (int)((double)avail / MAX(avail + used, 1) * 100);36873688if (percentage > 20)3689return (NULL);3690else if (percentage > 10)3691return (ANSI_YELLOW);3692else3693return (ANSI_RED);3694}36953696/*3697* Given a dataset and a list of fields, print out all the properties according3698* to the described layout, or return an nvlist containing all the fields, later3699* to be printed out as JSON object.3700*/3701static void3702collect_dataset(zfs_handle_t *zhp, list_cbdata_t *cb)3703{3704zprop_list_t *pl = cb->cb_proplist;3705boolean_t first = B_TRUE;3706char property[ZFS_MAXPROPLEN];3707nvlist_t *userprops = zfs_get_user_props(zhp);3708nvlist_t *propval;3709const char *propstr;3710boolean_t right_justify;3711nvlist_t *item, *d, *props;3712item = d = props = NULL;3713zprop_source_t sourcetype = ZPROP_SRC_NONE;3714char source[ZFS_MAX_DATASET_NAME_LEN];3715if (cb->cb_json) {3716d = fnvlist_lookup_nvlist(cb->cb_jsobj, "datasets");3717if (d == NULL) {3718fprintf(stderr, "datasets obj not found.\n");3719exit(1);3720}3721item = fnvlist_alloc();3722props = fnvlist_alloc();3723fill_dataset_info(item, zhp, cb->cb_json_as_int);3724}37253726for (; pl != NULL; pl = pl->pl_next) {3727if (!cb->cb_json && !first) {3728if (cb->cb_scripted)3729(void) putchar('\t');3730else3731(void) fputs(" ", stdout);3732} else {3733first = B_FALSE;3734}37353736if (pl->pl_prop == ZFS_PROP_NAME) {3737(void) strlcpy(property, zfs_get_name(zhp),3738sizeof (property));3739propstr = property;3740right_justify = zfs_prop_align_right(pl->pl_prop);3741} else if (pl->pl_prop != ZPROP_USERPROP) {3742if (zfs_prop_get(zhp, pl->pl_prop, property,3743sizeof (property), &sourcetype, source,3744sizeof (source), cb->cb_literal) != 0)3745propstr = "-";3746else3747propstr = property;3748right_justify = zfs_prop_align_right(pl->pl_prop);3749} else if (zfs_prop_userquota(pl->pl_user_prop)) {3750sourcetype = ZPROP_SRC_LOCAL;3751if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,3752property, sizeof (property), cb->cb_literal) != 0) {3753sourcetype = ZPROP_SRC_NONE;3754propstr = "-";3755} else {3756propstr = property;3757}3758right_justify = B_TRUE;3759} else if (zfs_prop_written(pl->pl_user_prop)) {3760sourcetype = ZPROP_SRC_LOCAL;3761if (zfs_prop_get_written(zhp, pl->pl_user_prop,3762property, sizeof (property), cb->cb_literal) != 0) {3763sourcetype = ZPROP_SRC_NONE;3764propstr = "-";3765} else {3766propstr = property;3767}3768right_justify = B_TRUE;3769} else {3770if (nvlist_lookup_nvlist(userprops,3771pl->pl_user_prop, &propval) != 0) {3772propstr = "-";3773} else {3774propstr = fnvlist_lookup_string(propval,3775ZPROP_VALUE);3776strlcpy(source,3777fnvlist_lookup_string(propval,3778ZPROP_SOURCE), ZFS_MAX_DATASET_NAME_LEN);3779if (strcmp(source,3780zfs_get_name(zhp)) == 0) {3781sourcetype = ZPROP_SRC_LOCAL;3782} else if (strcmp(source,3783ZPROP_SOURCE_VAL_RECVD) == 0) {3784sourcetype = ZPROP_SRC_RECEIVED;3785} else {3786sourcetype = ZPROP_SRC_INHERITED;3787}3788}3789right_justify = B_FALSE;3790}37913792if (cb->cb_json) {3793if (pl->pl_prop == ZFS_PROP_NAME)3794continue;3795const char *prop_name;3796if (pl->pl_prop != ZPROP_USERPROP)3797prop_name = zfs_prop_to_name(pl->pl_prop);3798else3799prop_name = pl->pl_user_prop;3800if (zprop_nvlist_one_property(3801prop_name, propstr,3802sourcetype, source, NULL, props,3803cb->cb_json_as_int) != 0)3804nomem();3805} else {3806/*3807* zfs_list_avail_color() needs3808* ZFS_PROP_AVAILABLE + USED, so we need another3809* for() search for the USED part when no colors3810* wanted, we can skip the whole thing3811*/3812if (use_color() && pl->pl_prop == ZFS_PROP_AVAILABLE) {3813zprop_list_t *pl2 = cb->cb_proplist;3814for (; pl2 != NULL; pl2 = pl2->pl_next) {3815if (pl2->pl_prop == ZFS_PROP_USED) {3816color_start(3817zfs_list_avail_color(zhp));3818/*3819* found it, no need for more3820* loops3821*/3822break;3823}3824}3825}38263827/*3828* If this is being called in scripted mode, or if3829* this is the last column and it is left-justified,3830* don't include a width format specifier.3831*/3832if (cb->cb_scripted || (pl->pl_next == NULL &&3833!right_justify))3834(void) fputs(propstr, stdout);3835else if (right_justify) {3836(void) printf("%*s", (int)pl->pl_width,3837propstr);3838} else {3839(void) printf("%-*s", (int)pl->pl_width,3840propstr);3841}38423843if (pl->pl_prop == ZFS_PROP_AVAILABLE)3844color_end();3845}3846}3847if (cb->cb_json) {3848fnvlist_add_nvlist(item, "properties", props);3849fnvlist_add_nvlist(d, zfs_get_name(zhp), item);3850fnvlist_free(props);3851fnvlist_free(item);3852} else3853(void) putchar('\n');3854}38553856/*3857* Generic callback function to list a dataset or snapshot.3858*/3859static int3860list_callback(zfs_handle_t *zhp, void *data)3861{3862list_cbdata_t *cbp = data;38633864if (cbp->cb_first) {3865if (!cbp->cb_scripted && !cbp->cb_json)3866print_header(cbp);3867cbp->cb_first = B_FALSE;3868}38693870collect_dataset(zhp, cbp);38713872return (0);3873}38743875static int3876zfs_do_list(int argc, char **argv)3877{3878int c;3879char default_fields[] =3880"name,used,available,referenced,mountpoint";3881int types = ZFS_TYPE_DATASET;3882boolean_t types_specified = B_FALSE;3883char *fields = default_fields;3884list_cbdata_t cb = { 0 };3885int limit = 0;3886int ret = 0;3887zfs_sort_column_t *sortcol = NULL;3888int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS;3889nvlist_t *data = NULL;38903891struct option long_options[] = {3892{"json", no_argument, NULL, 'j'},3893{"json-int", no_argument, NULL, ZFS_OPTION_JSON_NUMS_AS_INT},3894{0, 0, 0, 0}3895};38963897/* check options */3898while ((c = getopt_long(argc, argv, "jHS:d:o:prs:t:", long_options,3899NULL)) != -1) {3900switch (c) {3901case 'o':3902fields = optarg;3903break;3904case 'p':3905cb.cb_literal = B_TRUE;3906flags |= ZFS_ITER_LITERAL_PROPS;3907break;3908case 'd':3909limit = parse_depth(optarg, &flags);3910break;3911case 'r':3912flags |= ZFS_ITER_RECURSE;3913break;3914case 'j':3915cb.cb_json = B_TRUE;3916cb.cb_jsobj = zfs_json_schema(0, 1);3917data = fnvlist_alloc();3918fnvlist_add_nvlist(cb.cb_jsobj, "datasets", data);3919fnvlist_free(data);3920break;3921case ZFS_OPTION_JSON_NUMS_AS_INT:3922cb.cb_json_as_int = B_TRUE;3923cb.cb_literal = B_TRUE;3924break;3925case 'H':3926cb.cb_scripted = B_TRUE;3927break;3928case 's':3929if (zfs_add_sort_column(&sortcol, optarg,3930B_FALSE) != 0) {3931(void) fprintf(stderr,3932gettext("invalid property '%s'\n"), optarg);3933usage(B_FALSE);3934}3935break;3936case 'S':3937if (zfs_add_sort_column(&sortcol, optarg,3938B_TRUE) != 0) {3939(void) fprintf(stderr,3940gettext("invalid property '%s'\n"), optarg);3941usage(B_FALSE);3942}3943break;3944case 't':3945types = 0;3946types_specified = B_TRUE;3947flags &= ~ZFS_ITER_PROP_LISTSNAPS;39483949for (char *tok; (tok = strsep(&optarg, ",")); ) {3950static const char *const type_subopts[] = {3951"filesystem",3952"fs",3953"volume",3954"vol",3955"snapshot",3956"snap",3957"bookmark",3958"all"3959};3960static const int type_types[] = {3961ZFS_TYPE_FILESYSTEM,3962ZFS_TYPE_FILESYSTEM,3963ZFS_TYPE_VOLUME,3964ZFS_TYPE_VOLUME,3965ZFS_TYPE_SNAPSHOT,3966ZFS_TYPE_SNAPSHOT,3967ZFS_TYPE_BOOKMARK,3968ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK3969};39703971for (c = 0; c < ARRAY_SIZE(type_subopts); ++c)3972if (strcmp(tok, type_subopts[c]) == 0) {3973types |= type_types[c];3974goto found3;3975}39763977(void) fprintf(stderr,3978gettext("invalid type '%s'\n"), tok);3979usage(B_FALSE);3980found3:;3981}3982break;3983case ':':3984(void) fprintf(stderr, gettext("missing argument for "3985"'%c' option\n"), optopt);3986usage(B_FALSE);3987break;3988case '?':3989(void) fprintf(stderr, gettext("invalid option '%c'\n"),3990optopt);3991usage(B_FALSE);3992}3993}39943995argc -= optind;3996argv += optind;39973998if (!cb.cb_json && cb.cb_json_as_int) {3999(void) fprintf(stderr, gettext("'--json-int' only works with"4000" '-j' option\n"));4001usage(B_FALSE);4002}40034004/*4005* If "-o space" and no types were specified, don't display snapshots.4006*/4007if (strcmp(fields, "space") == 0 && types_specified == B_FALSE)4008types &= ~ZFS_TYPE_SNAPSHOT;40094010/*4011* Handle users who want to list all snapshots or bookmarks4012* of the current dataset (ex. 'zfs list -t snapshot <dataset>').4013*/4014if ((types == ZFS_TYPE_SNAPSHOT || types == ZFS_TYPE_BOOKMARK) &&4015argc > 0 && (flags & ZFS_ITER_RECURSE) == 0 && limit == 0) {4016flags |= (ZFS_ITER_DEPTH_LIMIT | ZFS_ITER_RECURSE);4017limit = 1;4018}40194020/*4021* If the user specifies '-o all', the zprop_get_list() doesn't4022* normally include the name of the dataset. For 'zfs list', we always4023* want this property to be first.4024*/4025if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)4026!= 0)4027usage(B_FALSE);40284029cb.cb_first = B_TRUE;40304031/*4032* If we are only going to list and sort by properties that are "fast"4033* then we can use "simple" mode and avoid populating the properties4034* nvlist.4035*/4036if (zfs_list_only_by_fast(cb.cb_proplist) &&4037zfs_sort_only_by_fast(sortcol))4038flags |= ZFS_ITER_SIMPLE;40394040ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist,4041limit, list_callback, &cb);40424043if (ret == 0 && cb.cb_json)4044zcmd_print_json(cb.cb_jsobj);4045else if (ret != 0 && cb.cb_json)4046nvlist_free(cb.cb_jsobj);40474048zprop_free_list(cb.cb_proplist);4049zfs_free_sort_columns(sortcol);40504051if (ret == 0 && cb.cb_first && !cb.cb_scripted)4052(void) fprintf(stderr, gettext("no datasets available\n"));40534054return (ret);4055}40564057/*4058* zfs rename [-fu] <fs | snap | vol> <fs | snap | vol>4059* zfs rename [-f] -p <fs | vol> <fs | vol>4060* zfs rename [-u] -r <snap> <snap>4061*4062* Renames the given dataset to another of the same type.4063*4064* The '-p' flag creates all the non-existing ancestors of the target first.4065* The '-u' flag prevents file systems from being remounted during rename.4066*/4067static int4068zfs_do_rename(int argc, char **argv)4069{4070zfs_handle_t *zhp;4071renameflags_t flags = { 0 };4072int c;4073int ret = 0;4074int types;4075boolean_t parents = B_FALSE;40764077/* check options */4078while ((c = getopt(argc, argv, "pruf")) != -1) {4079switch (c) {4080case 'p':4081parents = B_TRUE;4082break;4083case 'r':4084flags.recursive = B_TRUE;4085break;4086case 'u':4087flags.nounmount = B_TRUE;4088break;4089case 'f':4090flags.forceunmount = B_TRUE;4091break;4092case '?':4093default:4094(void) fprintf(stderr, gettext("invalid option '%c'\n"),4095optopt);4096usage(B_FALSE);4097}4098}40994100argc -= optind;4101argv += optind;41024103/* check number of arguments */4104if (argc < 1) {4105(void) fprintf(stderr, gettext("missing source dataset "4106"argument\n"));4107usage(B_FALSE);4108}4109if (argc < 2) {4110(void) fprintf(stderr, gettext("missing target dataset "4111"argument\n"));4112usage(B_FALSE);4113}4114if (argc > 2) {4115(void) fprintf(stderr, gettext("too many arguments\n"));4116usage(B_FALSE);4117}41184119if (flags.recursive && parents) {4120(void) fprintf(stderr, gettext("-p and -r options are mutually "4121"exclusive\n"));4122usage(B_FALSE);4123}41244125if (flags.nounmount && parents) {4126(void) fprintf(stderr, gettext("-u and -p options are mutually "4127"exclusive\n"));4128usage(B_FALSE);4129}41304131if (flags.recursive && strchr(argv[0], '@') == 0) {4132(void) fprintf(stderr, gettext("source dataset for recursive "4133"rename must be a snapshot\n"));4134usage(B_FALSE);4135}41364137if (flags.nounmount)4138types = ZFS_TYPE_FILESYSTEM;4139else if (parents)4140types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;4141else4142types = ZFS_TYPE_DATASET;41434144if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL)4145return (1);41464147/* If we were asked and the name looks good, try to create ancestors. */4148if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) &&4149zfs_create_ancestors(g_zfs, argv[1]) != 0) {4150zfs_close(zhp);4151return (1);4152}41534154ret = (zfs_rename(zhp, argv[1], flags) != 0);41554156zfs_close(zhp);4157return (ret);4158}41594160/*4161* zfs promote <fs>4162*4163* Promotes the given clone fs to be the parent4164*/4165static int4166zfs_do_promote(int argc, char **argv)4167{4168zfs_handle_t *zhp;4169int ret = 0;41704171/* check options */4172if (argc > 1 && argv[1][0] == '-') {4173(void) fprintf(stderr, gettext("invalid option '%c'\n"),4174argv[1][1]);4175usage(B_FALSE);4176}41774178/* check number of arguments */4179if (argc < 2) {4180(void) fprintf(stderr, gettext("missing clone filesystem"4181" argument\n"));4182usage(B_FALSE);4183}4184if (argc > 2) {4185(void) fprintf(stderr, gettext("too many arguments\n"));4186usage(B_FALSE);4187}41884189zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);4190if (zhp == NULL)4191return (1);41924193ret = (zfs_promote(zhp) != 0);419441954196zfs_close(zhp);4197return (ret);4198}41994200static int4201zfs_do_redact(int argc, char **argv)4202{4203char *snap = NULL;4204char *bookname = NULL;4205char **rsnaps = NULL;4206int numrsnaps = 0;4207argv++;4208argc--;4209if (argc < 3) {4210(void) fprintf(stderr, gettext("too few arguments\n"));4211usage(B_FALSE);4212}42134214snap = argv[0];4215bookname = argv[1];4216rsnaps = argv + 2;4217numrsnaps = argc - 2;42184219nvlist_t *rsnapnv = fnvlist_alloc();42204221for (int i = 0; i < numrsnaps; i++) {4222fnvlist_add_boolean(rsnapnv, rsnaps[i]);4223}42244225int err = lzc_redact(snap, bookname, rsnapnv);4226fnvlist_free(rsnapnv);42274228switch (err) {4229case 0:4230break;4231case ENOENT: {4232zfs_handle_t *zhp = zfs_open(g_zfs, snap, ZFS_TYPE_SNAPSHOT);4233if (zhp == NULL) {4234(void) fprintf(stderr, gettext("provided snapshot %s "4235"does not exist\n"), snap);4236} else {4237zfs_close(zhp);4238}4239for (int i = 0; i < numrsnaps; i++) {4240zhp = zfs_open(g_zfs, rsnaps[i], ZFS_TYPE_SNAPSHOT);4241if (zhp == NULL) {4242(void) fprintf(stderr, gettext("provided "4243"snapshot %s does not exist\n"), rsnaps[i]);4244} else {4245zfs_close(zhp);4246}4247}4248break;4249}4250case EEXIST:4251(void) fprintf(stderr, gettext("specified redaction bookmark "4252"(%s) provided already exists\n"), bookname);4253break;4254case ENAMETOOLONG:4255(void) fprintf(stderr, gettext("provided bookmark name cannot "4256"be used, final name would be too long\n"));4257break;4258case E2BIG:4259(void) fprintf(stderr, gettext("too many redaction snapshots "4260"specified\n"));4261break;4262case EINVAL:4263if (strchr(bookname, '#') != NULL)4264(void) fprintf(stderr, gettext(4265"redaction bookmark name must not contain '#'\n"));4266else4267(void) fprintf(stderr, gettext(4268"redaction snapshot must be descendent of "4269"snapshot being redacted\n"));4270break;4271case EALREADY:4272(void) fprintf(stderr, gettext("attempted to redact redacted "4273"dataset or with respect to redacted dataset\n"));4274break;4275case ENOTSUP:4276(void) fprintf(stderr, gettext("redaction bookmarks feature "4277"not enabled\n"));4278break;4279case EXDEV:4280(void) fprintf(stderr, gettext("potentially invalid redaction "4281"snapshot; full dataset names required\n"));4282break;4283case ESRCH:4284(void) fprintf(stderr, gettext("attempted to resume redaction "4285" with a mismatched redaction list\n"));4286break;4287default:4288(void) fprintf(stderr, gettext("internal error: %s\n"),4289strerror(errno));4290}42914292return (err);4293}42944295/*4296* zfs rollback [-rRf] <snapshot>4297*4298* -r Delete any intervening snapshots before doing rollback4299* -R Delete any snapshots and their clones4300* -f ignored for backwards compatibility4301*4302* Given a filesystem, rollback to a specific snapshot, discarding any changes4303* since then and making it the active dataset. If more recent snapshots exist,4304* the command will complain unless the '-r' flag is given.4305*/4306typedef struct rollback_cbdata {4307uint64_t cb_create;4308uint8_t cb_younger_ds_printed;4309boolean_t cb_first;4310int cb_doclones;4311char *cb_target;4312int cb_error;4313boolean_t cb_recurse;4314} rollback_cbdata_t;43154316static int4317rollback_check_dependent(zfs_handle_t *zhp, void *data)4318{4319rollback_cbdata_t *cbp = data;43204321if (cbp->cb_first && cbp->cb_recurse) {4322(void) fprintf(stderr, gettext("cannot rollback to "4323"'%s': clones of previous snapshots exist\n"),4324cbp->cb_target);4325(void) fprintf(stderr, gettext("use '-R' to "4326"force deletion of the following clones and "4327"dependents:\n"));4328cbp->cb_first = 0;4329cbp->cb_error = 1;4330}43314332(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));43334334zfs_close(zhp);4335return (0);4336}433743384339/*4340* Report some snapshots/bookmarks more recent than the one specified.4341* Used when '-r' is not specified. We reuse this same callback for the4342* snapshot dependents - if 'cb_dependent' is set, then this is a4343* dependent and we should report it without checking the transaction group.4344*/4345static int4346rollback_check(zfs_handle_t *zhp, void *data)4347{4348rollback_cbdata_t *cbp = data;4349/*4350* Max number of younger snapshots and/or bookmarks to display before4351* we stop the iteration.4352*/4353const uint8_t max_younger = 32;43544355if (cbp->cb_doclones) {4356zfs_close(zhp);4357return (0);4358}43594360if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {4361if (cbp->cb_first && !cbp->cb_recurse) {4362(void) fprintf(stderr, gettext("cannot "4363"rollback to '%s': more recent snapshots "4364"or bookmarks exist\n"),4365cbp->cb_target);4366(void) fprintf(stderr, gettext("use '-r' to "4367"force deletion of the following "4368"snapshots and bookmarks:\n"));4369cbp->cb_first = 0;4370cbp->cb_error = 1;4371}43724373if (cbp->cb_recurse) {4374if (zfs_iter_dependents_v2(zhp, 0, B_TRUE,4375rollback_check_dependent, cbp) != 0) {4376zfs_close(zhp);4377return (-1);4378}4379} else {4380(void) fprintf(stderr, "%s\n",4381zfs_get_name(zhp));4382cbp->cb_younger_ds_printed++;4383}4384}4385zfs_close(zhp);43864387if (cbp->cb_younger_ds_printed == max_younger) {4388/*4389* This non-recursive rollback is going to fail due to the4390* presence of snapshots and/or bookmarks that are younger than4391* the rollback target.4392* We printed some of the offending objects, now we stop4393* zfs_iter_snapshot/bookmark iteration so we can fail fast and4394* avoid iterating over the rest of the younger objects4395*/4396(void) fprintf(stderr, gettext("Output limited to %d "4397"snapshots/bookmarks\n"), max_younger);4398return (-1);4399}4400return (0);4401}44024403static int4404zfs_do_rollback(int argc, char **argv)4405{4406int ret = 0;4407int c;4408boolean_t force = B_FALSE;4409rollback_cbdata_t cb = { 0 };4410zfs_handle_t *zhp, *snap;4411char parentname[ZFS_MAX_DATASET_NAME_LEN];4412char *delim;4413uint64_t min_txg = 0;44144415/* check options */4416while ((c = getopt(argc, argv, "rRf")) != -1) {4417switch (c) {4418case 'r':4419cb.cb_recurse = 1;4420break;4421case 'R':4422cb.cb_recurse = 1;4423cb.cb_doclones = 1;4424break;4425case 'f':4426force = B_TRUE;4427break;4428case '?':4429(void) fprintf(stderr, gettext("invalid option '%c'\n"),4430optopt);4431usage(B_FALSE);4432}4433}44344435argc -= optind;4436argv += optind;44374438/* check number of arguments */4439if (argc < 1) {4440(void) fprintf(stderr, gettext("missing dataset argument\n"));4441usage(B_FALSE);4442}4443if (argc > 1) {4444(void) fprintf(stderr, gettext("too many arguments\n"));4445usage(B_FALSE);4446}44474448/* open the snapshot */4449if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)4450return (1);44514452/* open the parent dataset */4453(void) strlcpy(parentname, argv[0], sizeof (parentname));4454verify((delim = strrchr(parentname, '@')) != NULL);4455*delim = '\0';4456if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_DATASET)) == NULL) {4457zfs_close(snap);4458return (1);4459}44604461/*4462* Check for more recent snapshots and/or clones based on the presence4463* of '-r' and '-R'.4464*/4465cb.cb_target = argv[0];4466cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);4467cb.cb_first = B_TRUE;4468cb.cb_error = 0;44694470if (cb.cb_create > 0)4471min_txg = cb.cb_create;44724473if ((ret = zfs_iter_snapshots_sorted_v2(zhp, 0, rollback_check, &cb,4474min_txg, 0)) != 0)4475goto out;4476if ((ret = zfs_iter_bookmarks_v2(zhp, 0, rollback_check, &cb)) != 0)4477goto out;44784479if ((ret = cb.cb_error) != 0)4480goto out;44814482/*4483* Rollback parent to the given snapshot.4484*/4485ret = zfs_rollback(zhp, snap, force);44864487out:4488zfs_close(snap);4489zfs_close(zhp);44904491if (ret == 0)4492return (0);4493else4494return (1);4495}44964497/*4498* zfs set property=value ... { fs | snap | vol } ...4499*4500* Sets the given properties for all datasets specified on the command line.4501*/45024503static int4504set_callback(zfs_handle_t *zhp, void *data)4505{4506zprop_set_cbdata_t *cb = data;4507int ret = zfs_prop_set_list_flags(zhp, cb->cb_proplist, cb->cb_flags);45084509if (ret != 0 || libzfs_errno(g_zfs) != EZFS_SUCCESS) {4510switch (libzfs_errno(g_zfs)) {4511case EZFS_MOUNTFAILED:4512(void) fprintf(stderr, gettext("property may be set "4513"but unable to remount filesystem\n"));4514break;4515case EZFS_SHARENFSFAILED:4516(void) fprintf(stderr, gettext("property may be set "4517"but unable to reshare filesystem\n"));4518break;4519}4520}4521return (ret);4522}45234524static int4525zfs_do_set(int argc, char **argv)4526{4527zprop_set_cbdata_t cb = { 0 };4528int ds_start = -1; /* argv idx of first dataset arg */4529int ret = 0;4530int i, c;45314532/* check options */4533while ((c = getopt(argc, argv, "u")) != -1) {4534switch (c) {4535case 'u':4536cb.cb_flags |= ZFS_SET_NOMOUNT;4537break;4538case '?':4539default:4540(void) fprintf(stderr, gettext("invalid option '%c'\n"),4541optopt);4542usage(B_FALSE);4543}4544}45454546argc -= optind;4547argv += optind;45484549/* check number of arguments */4550if (argc < 1) {4551(void) fprintf(stderr, gettext("missing arguments\n"));4552usage(B_FALSE);4553}4554if (argc < 2) {4555if (strchr(argv[0], '=') == NULL) {4556(void) fprintf(stderr, gettext("missing property=value "4557"argument(s)\n"));4558} else {4559(void) fprintf(stderr, gettext("missing dataset "4560"name(s)\n"));4561}4562usage(B_FALSE);4563}45644565/* validate argument order: prop=val args followed by dataset args */4566for (i = 0; i < argc; i++) {4567if (strchr(argv[i], '=') != NULL) {4568if (ds_start > 0) {4569/* out-of-order prop=val argument */4570(void) fprintf(stderr, gettext("invalid "4571"argument order\n"));4572usage(B_FALSE);4573}4574} else if (ds_start < 0) {4575ds_start = i;4576}4577}4578if (ds_start < 0) {4579(void) fprintf(stderr, gettext("missing dataset name(s)\n"));4580usage(B_FALSE);4581}45824583/* Populate a list of property settings */4584if (nvlist_alloc(&cb.cb_proplist, NV_UNIQUE_NAME, 0) != 0)4585nomem();4586for (i = 0; i < ds_start; i++) {4587if (!parseprop(cb.cb_proplist, argv[i])) {4588ret = -1;4589goto error;4590}4591}45924593ret = zfs_for_each(argc - ds_start, argv + ds_start, 0,4594ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, &cb);45954596error:4597nvlist_free(cb.cb_proplist);4598return (ret);4599}46004601typedef struct snap_cbdata {4602nvlist_t *sd_nvl;4603boolean_t sd_recursive;4604const char *sd_snapname;4605} snap_cbdata_t;46064607static int4608zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)4609{4610snap_cbdata_t *sd = arg;4611char *name;4612int rv = 0;4613int error;46144615if (sd->sd_recursive &&4616zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) != 0) {4617zfs_close(zhp);4618return (0);4619}46204621error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname);4622if (error == -1)4623nomem();4624fnvlist_add_boolean(sd->sd_nvl, name);4625free(name);46264627if (sd->sd_recursive)4628rv = zfs_iter_filesystems_v2(zhp, 0, zfs_snapshot_cb, sd);4629zfs_close(zhp);4630return (rv);4631}46324633/*4634* zfs snapshot [-r] [-o prop=value] ... <fs@snap>4635*4636* Creates a snapshot with the given name. While functionally equivalent to4637* 'zfs create', it is a separate command to differentiate intent.4638*/4639static int4640zfs_do_snapshot(int argc, char **argv)4641{4642int ret = 0;4643int c;4644nvlist_t *props;4645snap_cbdata_t sd = { 0 };4646boolean_t multiple_snaps = B_FALSE;46474648if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)4649nomem();4650if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0)4651nomem();46524653/* check options */4654while ((c = getopt(argc, argv, "ro:")) != -1) {4655switch (c) {4656case 'o':4657if (!parseprop(props, optarg)) {4658nvlist_free(sd.sd_nvl);4659nvlist_free(props);4660return (1);4661}4662break;4663case 'r':4664sd.sd_recursive = B_TRUE;4665multiple_snaps = B_TRUE;4666break;4667case '?':4668(void) fprintf(stderr, gettext("invalid option '%c'\n"),4669optopt);4670goto usage;4671}4672}46734674argc -= optind;4675argv += optind;46764677/* check number of arguments */4678if (argc < 1) {4679(void) fprintf(stderr, gettext("missing snapshot argument\n"));4680goto usage;4681}46824683if (argc > 1)4684multiple_snaps = B_TRUE;4685for (; argc > 0; argc--, argv++) {4686char *atp;4687zfs_handle_t *zhp;46884689atp = strchr(argv[0], '@');4690if (atp == NULL)4691goto usage;4692*atp = '\0';4693sd.sd_snapname = atp + 1;4694zhp = zfs_open(g_zfs, argv[0],4695ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);4696if (zhp == NULL)4697goto usage;4698if (zfs_snapshot_cb(zhp, &sd) != 0)4699goto usage;4700}47014702ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props);4703nvlist_free(sd.sd_nvl);4704nvlist_free(props);4705if (ret != 0 && multiple_snaps)4706(void) fprintf(stderr, gettext("no snapshots were created\n"));4707return (ret != 0);47084709usage:4710nvlist_free(sd.sd_nvl);4711nvlist_free(props);4712usage(B_FALSE);4713return (-1);4714}47154716/*4717* Array of prefixes to exclude –4718* a linear search, even if executed for each dataset,4719* is plenty good enough.4720*/4721typedef struct zfs_send_exclude_arg {4722size_t count;4723const char **list;4724} zfs_send_exclude_arg_t;47254726static boolean_t4727zfs_do_send_exclude(zfs_handle_t *zhp, void *context)4728{4729zfs_send_exclude_arg_t *excludes = context;4730const char *name = zfs_get_name(zhp);47314732for (size_t i = 0; i < excludes->count; ++i) {4733size_t len = strlen(excludes->list[i]);4734if (strncmp(name, excludes->list[i], len) == 0 &&4735memchr("/@", name[len], sizeof ("/@")))4736return (B_FALSE);4737}47384739return (B_TRUE);4740}47414742/*4743* Send a backup stream to stdout.4744*/4745static int4746zfs_do_send(int argc, char **argv)4747{4748char *fromname = NULL;4749char *toname = NULL;4750char *resume_token = NULL;4751char *cp;4752zfs_handle_t *zhp;4753sendflags_t flags = { 0 };4754int c, err;4755nvlist_t *dbgnv = NULL;4756char *redactbook = NULL;4757zfs_send_exclude_arg_t excludes = { 0 };47584759struct option long_options[] = {4760{"replicate", no_argument, NULL, 'R'},4761{"skip-missing", no_argument, NULL, 's'},4762{"redact", required_argument, NULL, 'd'},4763{"props", no_argument, NULL, 'p'},4764{"parsable", no_argument, NULL, 'P'},4765{"dedup", no_argument, NULL, 'D'},4766{"proctitle", no_argument, NULL, 'V'},4767{"verbose", no_argument, NULL, 'v'},4768{"dryrun", no_argument, NULL, 'n'},4769{"large-block", no_argument, NULL, 'L'},4770{"embed", no_argument, NULL, 'e'},4771{"resume", required_argument, NULL, 't'},4772{"compressed", no_argument, NULL, 'c'},4773{"raw", no_argument, NULL, 'w'},4774{"backup", no_argument, NULL, 'b'},4775{"holds", no_argument, NULL, 'h'},4776{"saved", no_argument, NULL, 'S'},4777{"exclude", required_argument, NULL, 'X'},4778{0, 0, 0, 0}4779};47804781/* check options */4782while ((c = getopt_long(argc, argv, ":i:I:RsDpVvnPLeht:cwbd:SX:",4783long_options, NULL)) != -1) {4784switch (c) {4785case 'X':4786for (char *ds; (ds = strsep(&optarg, ",")) != NULL; ) {4787if (!zfs_name_valid(ds, ZFS_TYPE_DATASET) ||4788strchr(ds, '/') == NULL) {4789(void) fprintf(stderr, gettext("-X %s: "4790"not a valid non-root dataset name"4791".\n"), ds);4792usage(B_FALSE);4793}4794excludes.list = safe_realloc(excludes.list,4795sizeof (char *) * (excludes.count + 1));4796excludes.list[excludes.count++] = ds;4797}4798break;4799case 'i':4800if (fromname)4801usage(B_FALSE);4802fromname = optarg;4803break;4804case 'I':4805if (fromname)4806usage(B_FALSE);4807fromname = optarg;4808flags.doall = B_TRUE;4809break;4810case 'R':4811flags.replicate = B_TRUE;4812break;4813case 's':4814flags.skipmissing = B_TRUE;4815break;4816case 'd':4817redactbook = optarg;4818break;4819case 'p':4820flags.props = B_TRUE;4821break;4822case 'b':4823flags.backup = B_TRUE;4824break;4825case 'h':4826flags.holds = B_TRUE;4827break;4828case 'P':4829flags.parsable = B_TRUE;4830break;4831case 'V':4832flags.progressastitle = B_TRUE;4833break;4834case 'v':4835flags.verbosity++;4836flags.progress = B_TRUE;4837break;4838case 'D':4839(void) fprintf(stderr,4840gettext("WARNING: deduplicated send is no "4841"longer supported. A regular,\n"4842"non-deduplicated stream will be generated.\n\n"));4843break;4844case 'n':4845flags.dryrun = B_TRUE;4846break;4847case 'L':4848flags.largeblock = B_TRUE;4849break;4850case 'e':4851flags.embed_data = B_TRUE;4852break;4853case 't':4854resume_token = optarg;4855break;4856case 'c':4857flags.compress = B_TRUE;4858break;4859case 'w':4860flags.raw = B_TRUE;4861flags.compress = B_TRUE;4862flags.embed_data = B_TRUE;4863flags.largeblock = B_TRUE;4864break;4865case 'S':4866flags.saved = B_TRUE;4867break;4868case ':':4869/*4870* If a parameter was not passed, optopt contains the4871* value that would normally lead us into the4872* appropriate case statement. If it's > 256, then this4873* must be a longopt and we should look at argv to get4874* the string. Otherwise it's just the character, so we4875* should use it directly.4876*/4877if (optopt <= UINT8_MAX) {4878(void) fprintf(stderr,4879gettext("missing argument for '%c' "4880"option\n"), optopt);4881} else {4882(void) fprintf(stderr,4883gettext("missing argument for '%s' "4884"option\n"), argv[optind - 1]);4885}4886free(excludes.list);4887usage(B_FALSE);4888break;4889case '?':4890default:4891/*4892* If an invalid flag was passed, optopt contains the4893* character if it was a short flag, or 0 if it was a4894* longopt.4895*/4896if (optopt != 0) {4897(void) fprintf(stderr,4898gettext("invalid option '%c'\n"), optopt);4899} else {4900(void) fprintf(stderr,4901gettext("invalid option '%s'\n"),4902argv[optind - 1]);49034904}4905free(excludes.list);4906usage(B_FALSE);4907}4908}49094910if ((flags.parsable || flags.progressastitle) && flags.verbosity == 0)4911flags.verbosity = 1;49124913if (excludes.count > 0 && !flags.replicate) {4914free(excludes.list);4915(void) fprintf(stderr, gettext("Cannot specify "4916"dataset exclusion (-X) on a non-recursive "4917"send.\n"));4918return (1);4919}49204921argc -= optind;4922argv += optind;49234924if (resume_token != NULL) {4925if (fromname != NULL || flags.replicate || flags.props ||4926flags.backup || flags.holds ||4927flags.saved || redactbook != NULL) {4928free(excludes.list);4929(void) fprintf(stderr,4930gettext("invalid flags combined with -t\n"));4931usage(B_FALSE);4932}4933if (argc > 0) {4934free(excludes.list);4935(void) fprintf(stderr, gettext("too many arguments\n"));4936usage(B_FALSE);4937}4938} else {4939if (argc < 1) {4940free(excludes.list);4941(void) fprintf(stderr,4942gettext("missing snapshot argument\n"));4943usage(B_FALSE);4944}4945if (argc > 1) {4946free(excludes.list);4947(void) fprintf(stderr, gettext("too many arguments\n"));4948usage(B_FALSE);4949}4950}49514952if (flags.saved) {4953if (fromname != NULL || flags.replicate || flags.props ||4954flags.doall || flags.backup ||4955flags.holds || flags.largeblock || flags.embed_data ||4956flags.compress || flags.raw || redactbook != NULL) {4957free(excludes.list);49584959(void) fprintf(stderr, gettext("incompatible flags "4960"combined with saved send flag\n"));4961usage(B_FALSE);4962}4963if (strchr(argv[0], '@') != NULL) {4964free(excludes.list);49654966(void) fprintf(stderr, gettext("saved send must "4967"specify the dataset with partially-received "4968"state\n"));4969usage(B_FALSE);4970}4971}49724973if (flags.raw && redactbook != NULL) {4974free(excludes.list);4975(void) fprintf(stderr,4976gettext("Error: raw sends may not be redacted.\n"));4977return (1);4978}49794980if (!flags.dryrun && isatty(STDOUT_FILENO)) {4981free(excludes.list);4982(void) fprintf(stderr,4983gettext("Error: Stream can not be written to a terminal.\n"4984"You must redirect standard output.\n"));4985return (1);4986}49874988if (flags.saved) {4989zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);4990if (zhp == NULL) {4991free(excludes.list);4992return (1);4993}49944995err = zfs_send_saved(zhp, &flags, STDOUT_FILENO,4996resume_token);4997free(excludes.list);4998zfs_close(zhp);4999return (err != 0);5000} else if (resume_token != NULL) {5001free(excludes.list);5002return (zfs_send_resume(g_zfs, &flags, STDOUT_FILENO,5003resume_token));5004}50055006if (flags.skipmissing && !flags.replicate) {5007free(excludes.list);5008(void) fprintf(stderr,5009gettext("skip-missing flag can only be used in "5010"conjunction with replicate\n"));5011usage(B_FALSE);5012}50135014/*5015* For everything except -R and -I, use the new, cleaner code path.5016*/5017if (!(flags.replicate || flags.doall)) {5018char frombuf[ZFS_MAX_DATASET_NAME_LEN];50195020if (fromname != NULL && (strchr(fromname, '#') == NULL &&5021strchr(fromname, '@') == NULL)) {5022/*5023* Neither bookmark or snapshot was specified. Print a5024* warning, and assume snapshot.5025*/5026(void) fprintf(stderr, "Warning: incremental source "5027"didn't specify type, assuming snapshot. Use '@' "5028"or '#' prefix to avoid ambiguity.\n");5029(void) snprintf(frombuf, sizeof (frombuf), "@%s",5030fromname);5031fromname = frombuf;5032}5033if (fromname != NULL &&5034(fromname[0] == '#' || fromname[0] == '@')) {5035/*5036* Incremental source name begins with # or @.5037* Default to same fs as target.5038*/5039char tmpbuf[ZFS_MAX_DATASET_NAME_LEN];5040(void) strlcpy(tmpbuf, fromname, sizeof (tmpbuf));5041(void) strlcpy(frombuf, argv[0], sizeof (frombuf));5042cp = strchr(frombuf, '@');5043if (cp != NULL)5044*cp = '\0';5045(void) strlcat(frombuf, tmpbuf, sizeof (frombuf));5046fromname = frombuf;5047}50485049zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);5050if (zhp == NULL) {5051free(excludes.list);5052return (1);5053}5054err = zfs_send_one(zhp, fromname, STDOUT_FILENO, &flags,5055redactbook);50565057free(excludes.list);5058zfs_close(zhp);5059return (err != 0);5060}50615062if (fromname != NULL && strchr(fromname, '#')) {5063(void) fprintf(stderr,5064gettext("Error: multiple snapshots cannot be "5065"sent from a bookmark.\n"));5066free(excludes.list);5067return (1);5068}50695070if (redactbook != NULL) {5071(void) fprintf(stderr, gettext("Error: multiple snapshots "5072"cannot be sent redacted.\n"));5073free(excludes.list);5074return (1);5075}50765077if ((cp = strchr(argv[0], '@')) == NULL) {5078(void) fprintf(stderr, gettext("Error: "5079"Unsupported flag with filesystem or bookmark.\n"));5080free(excludes.list);5081return (1);5082}5083*cp = '\0';5084toname = cp + 1;5085zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);5086if (zhp == NULL) {5087free(excludes.list);5088return (1);5089}50905091/*5092* If they specified the full path to the snapshot, chop off5093* everything except the short name of the snapshot, but special5094* case if they specify the origin.5095*/5096if (fromname && (cp = strchr(fromname, '@')) != NULL) {5097char origin[ZFS_MAX_DATASET_NAME_LEN];5098zprop_source_t src;50995100(void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN,5101origin, sizeof (origin), &src, NULL, 0, B_FALSE);51025103if (strcmp(origin, fromname) == 0) {5104fromname = NULL;5105flags.fromorigin = B_TRUE;5106} else {5107*cp = '\0';5108if (cp != fromname && strcmp(argv[0], fromname)) {5109zfs_close(zhp);5110free(excludes.list);5111(void) fprintf(stderr,5112gettext("incremental source must be "5113"in same filesystem\n"));5114usage(B_FALSE);5115}5116fromname = cp + 1;5117if (strchr(fromname, '@') || strchr(fromname, '/')) {5118zfs_close(zhp);5119free(excludes.list);5120(void) fprintf(stderr,5121gettext("invalid incremental source\n"));5122usage(B_FALSE);5123}5124}5125}51265127if (flags.replicate && fromname == NULL)5128flags.doall = B_TRUE;51295130err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO,5131excludes.count > 0 ? zfs_do_send_exclude : NULL,5132&excludes, flags.verbosity >= 3 ? &dbgnv : NULL);51335134if (flags.verbosity >= 3 && dbgnv != NULL) {5135/*5136* dump_nvlist prints to stdout, but that's been5137* redirected to a file. Make it print to stderr5138* instead.5139*/5140(void) dup2(STDERR_FILENO, STDOUT_FILENO);5141dump_nvlist(dbgnv, 0);5142nvlist_free(dbgnv);5143}51445145zfs_close(zhp);5146free(excludes.list);5147return (err != 0);5148}51495150/*5151* Restore a backup stream from stdin.5152*/5153static int5154zfs_do_receive(int argc, char **argv)5155{5156int c, err = 0;5157recvflags_t flags = { 0 };5158boolean_t abort_resumable = B_FALSE;5159nvlist_t *props;51605161if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)5162nomem();51635164/* check options */5165while ((c = getopt(argc, argv, ":o:x:dehMnuvFsAc")) != -1) {5166switch (c) {5167case 'o':5168if (!parseprop(props, optarg)) {5169nvlist_free(props);5170usage(B_FALSE);5171}5172break;5173case 'x':5174if (!parsepropname(props, optarg)) {5175nvlist_free(props);5176usage(B_FALSE);5177}5178break;5179case 'd':5180if (flags.istail) {5181(void) fprintf(stderr, gettext("invalid option "5182"combination: -d and -e are mutually "5183"exclusive\n"));5184usage(B_FALSE);5185}5186flags.isprefix = B_TRUE;5187break;5188case 'e':5189if (flags.isprefix) {5190(void) fprintf(stderr, gettext("invalid option "5191"combination: -d and -e are mutually "5192"exclusive\n"));5193usage(B_FALSE);5194}5195flags.istail = B_TRUE;5196break;5197case 'h':5198flags.skipholds = B_TRUE;5199break;5200case 'M':5201flags.forceunmount = B_TRUE;5202break;5203case 'n':5204flags.dryrun = B_TRUE;5205break;5206case 'u':5207flags.nomount = B_TRUE;5208break;5209case 'v':5210flags.verbose = B_TRUE;5211break;5212case 's':5213flags.resumable = B_TRUE;5214break;5215case 'F':5216flags.force = B_TRUE;5217break;5218case 'A':5219abort_resumable = B_TRUE;5220break;5221case 'c':5222flags.heal = B_TRUE;5223break;5224case ':':5225(void) fprintf(stderr, gettext("missing argument for "5226"'%c' option\n"), optopt);5227usage(B_FALSE);5228break;5229case '?':5230(void) fprintf(stderr, gettext("invalid option '%c'\n"),5231optopt);5232usage(B_FALSE);5233}5234}52355236argc -= optind;5237argv += optind;52385239/* zfs recv -e (use "tail" name) implies -d (remove dataset "head") */5240if (flags.istail)5241flags.isprefix = B_TRUE;52425243/* check number of arguments */5244if (argc < 1) {5245(void) fprintf(stderr, gettext("missing snapshot argument\n"));5246usage(B_FALSE);5247}5248if (argc > 1) {5249(void) fprintf(stderr, gettext("too many arguments\n"));5250usage(B_FALSE);5251}52525253if (abort_resumable) {5254if (flags.isprefix || flags.istail || flags.dryrun ||5255flags.resumable || flags.nomount) {5256(void) fprintf(stderr, gettext("invalid option\n"));5257usage(B_FALSE);5258}52595260char namebuf[ZFS_MAX_DATASET_NAME_LEN];5261(void) snprintf(namebuf, sizeof (namebuf),5262"%s/%%recv", argv[0]);52635264if (zfs_dataset_exists(g_zfs, namebuf,5265ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) {5266zfs_handle_t *zhp = zfs_open(g_zfs,5267namebuf, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);5268if (zhp == NULL) {5269nvlist_free(props);5270return (1);5271}5272err = zfs_destroy(zhp, B_FALSE);5273zfs_close(zhp);5274} else {5275zfs_handle_t *zhp = zfs_open(g_zfs,5276argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);5277if (zhp == NULL)5278usage(B_FALSE);5279if (!zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) ||5280zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,5281NULL, 0, NULL, NULL, 0, B_TRUE) == -1) {5282(void) fprintf(stderr,5283gettext("'%s' does not have any "5284"resumable receive state to abort\n"),5285argv[0]);5286nvlist_free(props);5287zfs_close(zhp);5288return (1);5289}5290err = zfs_destroy(zhp, B_FALSE);5291zfs_close(zhp);5292}5293nvlist_free(props);5294return (err != 0);5295}52965297if (isatty(STDIN_FILENO)) {5298(void) fprintf(stderr,5299gettext("Error: Backup stream can not be read "5300"from a terminal.\n"5301"You must redirect standard input.\n"));5302nvlist_free(props);5303return (1);5304}5305err = zfs_receive(g_zfs, argv[0], props, &flags, STDIN_FILENO, NULL);5306nvlist_free(props);53075308return (err != 0);5309}53105311/*5312* allow/unallow stuff5313*/5314/* copied from zfs/sys/dsl_deleg.h */5315#define ZFS_DELEG_PERM_CREATE "create"5316#define ZFS_DELEG_PERM_DESTROY "destroy"5317#define ZFS_DELEG_PERM_SNAPSHOT "snapshot"5318#define ZFS_DELEG_PERM_ROLLBACK "rollback"5319#define ZFS_DELEG_PERM_CLONE "clone"5320#define ZFS_DELEG_PERM_PROMOTE "promote"5321#define ZFS_DELEG_PERM_RENAME "rename"5322#define ZFS_DELEG_PERM_MOUNT "mount"5323#define ZFS_DELEG_PERM_SHARE "share"5324#define ZFS_DELEG_PERM_SEND "send"5325#define ZFS_DELEG_PERM_SEND_RAW "send:raw"5326#define ZFS_DELEG_PERM_RECEIVE "receive"5327#define ZFS_DELEG_PERM_RECEIVE_APPEND "receive:append"5328#define ZFS_DELEG_PERM_ALLOW "allow"5329#define ZFS_DELEG_PERM_USERPROP "userprop"5330#define ZFS_DELEG_PERM_VSCAN "vscan" /* ??? */5331#define ZFS_DELEG_PERM_USERQUOTA "userquota"5332#define ZFS_DELEG_PERM_GROUPQUOTA "groupquota"5333#define ZFS_DELEG_PERM_USERUSED "userused"5334#define ZFS_DELEG_PERM_GROUPUSED "groupused"5335#define ZFS_DELEG_PERM_USEROBJQUOTA "userobjquota"5336#define ZFS_DELEG_PERM_GROUPOBJQUOTA "groupobjquota"5337#define ZFS_DELEG_PERM_USEROBJUSED "userobjused"5338#define ZFS_DELEG_PERM_GROUPOBJUSED "groupobjused"53395340#define ZFS_DELEG_PERM_HOLD "hold"5341#define ZFS_DELEG_PERM_RELEASE "release"5342#define ZFS_DELEG_PERM_DIFF "diff"5343#define ZFS_DELEG_PERM_BOOKMARK "bookmark"5344#define ZFS_DELEG_PERM_LOAD_KEY "load-key"5345#define ZFS_DELEG_PERM_CHANGE_KEY "change-key"53465347#define ZFS_DELEG_PERM_PROJECTUSED "projectused"5348#define ZFS_DELEG_PERM_PROJECTQUOTA "projectquota"5349#define ZFS_DELEG_PERM_PROJECTOBJUSED "projectobjused"5350#define ZFS_DELEG_PERM_PROJECTOBJQUOTA "projectobjquota"53515352#define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE53535354static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = {5355{ ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW },5356{ ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE },5357{ ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE },5358{ ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY },5359{ ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF},5360{ ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD },5361{ ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT },5362{ ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE },5363{ ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE },5364{ ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE },5365{ ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },5366{ ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },5367{ ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND },5368{ ZFS_DELEG_PERM_SEND_RAW, ZFS_DELEG_NOTE_SEND_RAW },5369{ ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },5370{ ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },5371{ ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK },5372{ ZFS_DELEG_PERM_LOAD_KEY, ZFS_DELEG_NOTE_LOAD_KEY },5373{ ZFS_DELEG_PERM_CHANGE_KEY, ZFS_DELEG_NOTE_CHANGE_KEY },53745375{ ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA },5376{ ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED },5377{ ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },5378{ ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA },5379{ ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED },5380{ ZFS_DELEG_PERM_USEROBJQUOTA, ZFS_DELEG_NOTE_USEROBJQUOTA },5381{ ZFS_DELEG_PERM_USEROBJUSED, ZFS_DELEG_NOTE_USEROBJUSED },5382{ ZFS_DELEG_PERM_GROUPOBJQUOTA, ZFS_DELEG_NOTE_GROUPOBJQUOTA },5383{ ZFS_DELEG_PERM_GROUPOBJUSED, ZFS_DELEG_NOTE_GROUPOBJUSED },5384{ ZFS_DELEG_PERM_PROJECTUSED, ZFS_DELEG_NOTE_PROJECTUSED },5385{ ZFS_DELEG_PERM_PROJECTQUOTA, ZFS_DELEG_NOTE_PROJECTQUOTA },5386{ ZFS_DELEG_PERM_PROJECTOBJUSED, ZFS_DELEG_NOTE_PROJECTOBJUSED },5387{ ZFS_DELEG_PERM_PROJECTOBJQUOTA, ZFS_DELEG_NOTE_PROJECTOBJQUOTA },5388{ NULL, ZFS_DELEG_NOTE_NONE }5389};53905391/* permission structure */5392typedef struct deleg_perm {5393zfs_deleg_who_type_t dp_who_type;5394const char *dp_name;5395boolean_t dp_local;5396boolean_t dp_descend;5397} deleg_perm_t;53985399/* */5400typedef struct deleg_perm_node {5401deleg_perm_t dpn_perm;54025403uu_avl_node_t dpn_avl_node;5404} deleg_perm_node_t;54055406typedef struct fs_perm fs_perm_t;54075408/* permissions set */5409typedef struct who_perm {5410zfs_deleg_who_type_t who_type;5411const char *who_name; /* id */5412char who_ug_name[256]; /* user/group name */5413fs_perm_t *who_fsperm; /* uplink */54145415uu_avl_t *who_deleg_perm_avl; /* permissions */5416} who_perm_t;54175418/* */5419typedef struct who_perm_node {5420who_perm_t who_perm;5421uu_avl_node_t who_avl_node;5422} who_perm_node_t;54235424typedef struct fs_perm_set fs_perm_set_t;5425/* fs permissions */5426struct fs_perm {5427const char *fsp_name;54285429uu_avl_t *fsp_sc_avl; /* sets,create */5430uu_avl_t *fsp_uge_avl; /* user,group,everyone */54315432fs_perm_set_t *fsp_set; /* uplink */5433};54345435/* */5436typedef struct fs_perm_node {5437fs_perm_t fspn_fsperm;5438uu_avl_t *fspn_avl;54395440uu_list_node_t fspn_list_node;5441} fs_perm_node_t;54425443/* top level structure */5444struct fs_perm_set {5445uu_list_pool_t *fsps_list_pool;5446uu_list_t *fsps_list; /* list of fs_perms */54475448uu_avl_pool_t *fsps_named_set_avl_pool;5449uu_avl_pool_t *fsps_who_perm_avl_pool;5450uu_avl_pool_t *fsps_deleg_perm_avl_pool;5451};54525453static inline const char *5454deleg_perm_type(zfs_deleg_note_t note)5455{5456/* subcommands */5457switch (note) {5458/* SUBCOMMANDS */5459/* OTHER */5460case ZFS_DELEG_NOTE_GROUPQUOTA:5461case ZFS_DELEG_NOTE_GROUPUSED:5462case ZFS_DELEG_NOTE_USERPROP:5463case ZFS_DELEG_NOTE_USERQUOTA:5464case ZFS_DELEG_NOTE_USERUSED:5465case ZFS_DELEG_NOTE_USEROBJQUOTA:5466case ZFS_DELEG_NOTE_USEROBJUSED:5467case ZFS_DELEG_NOTE_GROUPOBJQUOTA:5468case ZFS_DELEG_NOTE_GROUPOBJUSED:5469case ZFS_DELEG_NOTE_PROJECTUSED:5470case ZFS_DELEG_NOTE_PROJECTQUOTA:5471case ZFS_DELEG_NOTE_PROJECTOBJUSED:5472case ZFS_DELEG_NOTE_PROJECTOBJQUOTA:5473/* other */5474return (gettext("other"));5475default:5476return (gettext("subcommand"));5477}5478}54795480static int5481who_type2weight(zfs_deleg_who_type_t who_type)5482{5483int res;5484switch (who_type) {5485case ZFS_DELEG_NAMED_SET_SETS:5486case ZFS_DELEG_NAMED_SET:5487res = 0;5488break;5489case ZFS_DELEG_CREATE_SETS:5490case ZFS_DELEG_CREATE:5491res = 1;5492break;5493case ZFS_DELEG_USER_SETS:5494case ZFS_DELEG_USER:5495res = 2;5496break;5497case ZFS_DELEG_GROUP_SETS:5498case ZFS_DELEG_GROUP:5499res = 3;5500break;5501case ZFS_DELEG_EVERYONE_SETS:5502case ZFS_DELEG_EVERYONE:5503res = 4;5504break;5505default:5506res = -1;5507}55085509return (res);5510}55115512static int5513who_perm_compare(const void *larg, const void *rarg, void *unused)5514{5515(void) unused;5516const who_perm_node_t *l = larg;5517const who_perm_node_t *r = rarg;5518zfs_deleg_who_type_t ltype = l->who_perm.who_type;5519zfs_deleg_who_type_t rtype = r->who_perm.who_type;5520int lweight = who_type2weight(ltype);5521int rweight = who_type2weight(rtype);5522int res = lweight - rweight;5523if (res == 0)5524res = strncmp(l->who_perm.who_name, r->who_perm.who_name,5525ZFS_MAX_DELEG_NAME-1);55265527if (res == 0)5528return (0);5529if (res > 0)5530return (1);5531else5532return (-1);5533}55345535static int5536deleg_perm_compare(const void *larg, const void *rarg, void *unused)5537{5538(void) unused;5539const deleg_perm_node_t *l = larg;5540const deleg_perm_node_t *r = rarg;5541int res = strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name,5542ZFS_MAX_DELEG_NAME-1);55435544if (res == 0)5545return (0);55465547if (res > 0)5548return (1);5549else5550return (-1);5551}55525553static inline void5554fs_perm_set_init(fs_perm_set_t *fspset)5555{5556memset(fspset, 0, sizeof (fs_perm_set_t));55575558if ((fspset->fsps_list_pool = uu_list_pool_create("fsps_list_pool",5559sizeof (fs_perm_node_t), offsetof(fs_perm_node_t, fspn_list_node),5560NULL, UU_DEFAULT)) == NULL)5561nomem();5562if ((fspset->fsps_list = uu_list_create(fspset->fsps_list_pool, NULL,5563UU_DEFAULT)) == NULL)5564nomem();55655566if ((fspset->fsps_named_set_avl_pool = uu_avl_pool_create(5567"named_set_avl_pool", sizeof (who_perm_node_t), offsetof(5568who_perm_node_t, who_avl_node), who_perm_compare,5569UU_DEFAULT)) == NULL)5570nomem();55715572if ((fspset->fsps_who_perm_avl_pool = uu_avl_pool_create(5573"who_perm_avl_pool", sizeof (who_perm_node_t), offsetof(5574who_perm_node_t, who_avl_node), who_perm_compare,5575UU_DEFAULT)) == NULL)5576nomem();55775578if ((fspset->fsps_deleg_perm_avl_pool = uu_avl_pool_create(5579"deleg_perm_avl_pool", sizeof (deleg_perm_node_t), offsetof(5580deleg_perm_node_t, dpn_avl_node), deleg_perm_compare, UU_DEFAULT))5581== NULL)5582nomem();5583}55845585static inline void fs_perm_fini(fs_perm_t *);5586static inline void who_perm_fini(who_perm_t *);55875588static inline void5589fs_perm_set_fini(fs_perm_set_t *fspset)5590{5591fs_perm_node_t *node = uu_list_first(fspset->fsps_list);55925593while (node != NULL) {5594fs_perm_node_t *next_node =5595uu_list_next(fspset->fsps_list, node);5596fs_perm_t *fsperm = &node->fspn_fsperm;5597fs_perm_fini(fsperm);5598uu_list_remove(fspset->fsps_list, node);5599free(node);5600node = next_node;5601}56025603uu_avl_pool_destroy(fspset->fsps_named_set_avl_pool);5604uu_avl_pool_destroy(fspset->fsps_who_perm_avl_pool);5605uu_avl_pool_destroy(fspset->fsps_deleg_perm_avl_pool);5606}56075608static inline void5609deleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type,5610const char *name)5611{5612deleg_perm->dp_who_type = type;5613deleg_perm->dp_name = name;5614}56155616static inline void5617who_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm,5618zfs_deleg_who_type_t type, const char *name)5619{5620uu_avl_pool_t *pool;5621pool = fsperm->fsp_set->fsps_deleg_perm_avl_pool;56225623memset(who_perm, 0, sizeof (who_perm_t));56245625if ((who_perm->who_deleg_perm_avl = uu_avl_create(pool, NULL,5626UU_DEFAULT)) == NULL)5627nomem();56285629who_perm->who_type = type;5630who_perm->who_name = name;5631who_perm->who_fsperm = fsperm;5632}56335634static inline void5635who_perm_fini(who_perm_t *who_perm)5636{5637deleg_perm_node_t *node = uu_avl_first(who_perm->who_deleg_perm_avl);56385639while (node != NULL) {5640deleg_perm_node_t *next_node =5641uu_avl_next(who_perm->who_deleg_perm_avl, node);56425643uu_avl_remove(who_perm->who_deleg_perm_avl, node);5644free(node);5645node = next_node;5646}56475648uu_avl_destroy(who_perm->who_deleg_perm_avl);5649}56505651static inline void5652fs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname)5653{5654uu_avl_pool_t *nset_pool = fspset->fsps_named_set_avl_pool;5655uu_avl_pool_t *who_pool = fspset->fsps_who_perm_avl_pool;56565657memset(fsperm, 0, sizeof (fs_perm_t));56585659if ((fsperm->fsp_sc_avl = uu_avl_create(nset_pool, NULL, UU_DEFAULT))5660== NULL)5661nomem();56625663if ((fsperm->fsp_uge_avl = uu_avl_create(who_pool, NULL, UU_DEFAULT))5664== NULL)5665nomem();56665667fsperm->fsp_set = fspset;5668fsperm->fsp_name = fsname;5669}56705671static inline void5672fs_perm_fini(fs_perm_t *fsperm)5673{5674who_perm_node_t *node = uu_avl_first(fsperm->fsp_sc_avl);5675while (node != NULL) {5676who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_sc_avl,5677node);5678who_perm_t *who_perm = &node->who_perm;5679who_perm_fini(who_perm);5680uu_avl_remove(fsperm->fsp_sc_avl, node);5681free(node);5682node = next_node;5683}56845685node = uu_avl_first(fsperm->fsp_uge_avl);5686while (node != NULL) {5687who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_uge_avl,5688node);5689who_perm_t *who_perm = &node->who_perm;5690who_perm_fini(who_perm);5691uu_avl_remove(fsperm->fsp_uge_avl, node);5692free(node);5693node = next_node;5694}56955696uu_avl_destroy(fsperm->fsp_sc_avl);5697uu_avl_destroy(fsperm->fsp_uge_avl);5698}56995700static void5701set_deleg_perm_node(uu_avl_t *avl, deleg_perm_node_t *node,5702zfs_deleg_who_type_t who_type, const char *name, char locality)5703{5704uu_avl_index_t idx = 0;57055706deleg_perm_node_t *found_node = NULL;5707deleg_perm_t *deleg_perm = &node->dpn_perm;57085709deleg_perm_init(deleg_perm, who_type, name);57105711if ((found_node = uu_avl_find(avl, node, NULL, &idx))5712== NULL)5713uu_avl_insert(avl, node, idx);5714else {5715node = found_node;5716deleg_perm = &node->dpn_perm;5717}571857195720switch (locality) {5721case ZFS_DELEG_LOCAL:5722deleg_perm->dp_local = B_TRUE;5723break;5724case ZFS_DELEG_DESCENDENT:5725deleg_perm->dp_descend = B_TRUE;5726break;5727case ZFS_DELEG_NA:5728break;5729default:5730assert(B_FALSE); /* invalid locality */5731}5732}57335734static inline int5735parse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality)5736{5737nvpair_t *nvp = NULL;5738fs_perm_set_t *fspset = who_perm->who_fsperm->fsp_set;5739uu_avl_t *avl = who_perm->who_deleg_perm_avl;5740zfs_deleg_who_type_t who_type = who_perm->who_type;57415742while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {5743const char *name = nvpair_name(nvp);5744data_type_t type = nvpair_type(nvp);5745uu_avl_pool_t *avl_pool = fspset->fsps_deleg_perm_avl_pool;5746deleg_perm_node_t *node =5747safe_malloc(sizeof (deleg_perm_node_t));57485749VERIFY(type == DATA_TYPE_BOOLEAN);57505751uu_avl_node_init(node, &node->dpn_avl_node, avl_pool);5752set_deleg_perm_node(avl, node, who_type, name, locality);5753}57545755return (0);5756}57575758static inline int5759parse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl)5760{5761nvpair_t *nvp = NULL;5762fs_perm_set_t *fspset = fsperm->fsp_set;57635764while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {5765nvlist_t *nvl2 = NULL;5766const char *name = nvpair_name(nvp);5767uu_avl_t *avl = NULL;5768uu_avl_pool_t *avl_pool = NULL;5769zfs_deleg_who_type_t perm_type = name[0];5770char perm_locality = name[1];5771const char *perm_name = name + 3;5772who_perm_t *who_perm = NULL;57735774assert('$' == name[2]);57755776if (nvpair_value_nvlist(nvp, &nvl2) != 0)5777return (-1);57785779switch (perm_type) {5780case ZFS_DELEG_CREATE:5781case ZFS_DELEG_CREATE_SETS:5782case ZFS_DELEG_NAMED_SET:5783case ZFS_DELEG_NAMED_SET_SETS:5784avl_pool = fspset->fsps_named_set_avl_pool;5785avl = fsperm->fsp_sc_avl;5786break;5787case ZFS_DELEG_USER:5788case ZFS_DELEG_USER_SETS:5789case ZFS_DELEG_GROUP:5790case ZFS_DELEG_GROUP_SETS:5791case ZFS_DELEG_EVERYONE:5792case ZFS_DELEG_EVERYONE_SETS:5793avl_pool = fspset->fsps_who_perm_avl_pool;5794avl = fsperm->fsp_uge_avl;5795break;57965797default:5798assert(!"unhandled zfs_deleg_who_type_t");5799}58005801who_perm_node_t *found_node = NULL;5802who_perm_node_t *node = safe_malloc(5803sizeof (who_perm_node_t));5804who_perm = &node->who_perm;5805uu_avl_index_t idx = 0;58065807uu_avl_node_init(node, &node->who_avl_node, avl_pool);5808who_perm_init(who_perm, fsperm, perm_type, perm_name);58095810if ((found_node = uu_avl_find(avl, node, NULL, &idx))5811== NULL) {5812if (avl == fsperm->fsp_uge_avl) {5813uid_t rid = 0;5814struct passwd *p = NULL;5815struct group *g = NULL;5816const char *nice_name = NULL;58175818switch (perm_type) {5819case ZFS_DELEG_USER_SETS:5820case ZFS_DELEG_USER:5821rid = atoi(perm_name);5822p = getpwuid(rid);5823if (p)5824nice_name = p->pw_name;5825break;5826case ZFS_DELEG_GROUP_SETS:5827case ZFS_DELEG_GROUP:5828rid = atoi(perm_name);5829g = getgrgid(rid);5830if (g)5831nice_name = g->gr_name;5832break;58335834default:5835break;5836}58375838if (nice_name != NULL) {5839(void) strlcpy(5840node->who_perm.who_ug_name,5841nice_name, 256);5842} else {5843/* User or group unknown */5844(void) snprintf(5845node->who_perm.who_ug_name,5846sizeof (node->who_perm.who_ug_name),5847"(unknown: %d)", rid);5848}5849}58505851uu_avl_insert(avl, node, idx);5852} else {5853node = found_node;5854who_perm = &node->who_perm;5855}58565857assert(who_perm != NULL);5858(void) parse_who_perm(who_perm, nvl2, perm_locality);5859}58605861return (0);5862}58635864static inline int5865parse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl)5866{5867nvpair_t *nvp = NULL;5868uu_avl_index_t idx = 0;58695870while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {5871nvlist_t *nvl2 = NULL;5872const char *fsname = nvpair_name(nvp);5873data_type_t type = nvpair_type(nvp);5874fs_perm_t *fsperm = NULL;5875fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t));58765877fsperm = &node->fspn_fsperm;58785879VERIFY(DATA_TYPE_NVLIST == type);58805881uu_list_node_init(node, &node->fspn_list_node,5882fspset->fsps_list_pool);58835884idx = uu_list_numnodes(fspset->fsps_list);5885fs_perm_init(fsperm, fspset, fsname);58865887if (nvpair_value_nvlist(nvp, &nvl2) != 0)5888return (-1);58895890(void) parse_fs_perm(fsperm, nvl2);58915892uu_list_insert(fspset->fsps_list, node, idx);5893}58945895return (0);5896}58975898static inline const char *5899deleg_perm_comment(zfs_deleg_note_t note)5900{5901const char *str;59025903/* subcommands */5904switch (note) {5905/* SUBCOMMANDS */5906case ZFS_DELEG_NOTE_ALLOW:5907str = gettext("Must also have the permission that is being"5908"\n\t\t\t\tallowed");5909break;5910case ZFS_DELEG_NOTE_CLONE:5911str = gettext("Must also have the 'create' ability and 'mount'"5912"\n\t\t\t\tability in the origin file system");5913break;5914case ZFS_DELEG_NOTE_CREATE:5915str = gettext("Must also have the 'mount' ability");5916break;5917case ZFS_DELEG_NOTE_DESTROY:5918str = gettext("Must also have the 'mount' ability");5919break;5920case ZFS_DELEG_NOTE_DIFF:5921str = gettext("Allows lookup of paths within a dataset;"5922"\n\t\t\t\tgiven an object number. Ordinary users need this"5923"\n\t\t\t\tin order to use zfs diff");5924break;5925case ZFS_DELEG_NOTE_HOLD:5926str = gettext("Allows adding a user hold to a snapshot");5927break;5928case ZFS_DELEG_NOTE_MOUNT:5929str = gettext("Allows mount/umount of ZFS datasets");5930break;5931case ZFS_DELEG_NOTE_PROMOTE:5932str = gettext("Must also have the 'mount'\n\t\t\t\tand"5933" 'promote' ability in the origin file system");5934break;5935case ZFS_DELEG_NOTE_RECEIVE:5936str = gettext("Must also have the 'mount' and 'create'"5937" ability");5938break;5939case ZFS_DELEG_NOTE_RELEASE:5940str = gettext("Allows releasing a user hold which\n\t\t\t\t"5941"might destroy the snapshot");5942break;5943case ZFS_DELEG_NOTE_RENAME:5944str = gettext("Must also have the 'mount' and 'create'"5945"\n\t\t\t\tability in the new parent");5946break;5947case ZFS_DELEG_NOTE_ROLLBACK:5948str = gettext("");5949break;5950case ZFS_DELEG_NOTE_SEND:5951str = gettext("");5952break;5953case ZFS_DELEG_NOTE_SEND_RAW:5954str = gettext("Allow sending ONLY encrypted (raw) replication"5955"\n\t\t\t\tstreams");5956break;5957case ZFS_DELEG_NOTE_SHARE:5958str = gettext("Allows sharing file systems over NFS or SMB"5959"\n\t\t\t\tprotocols");5960break;5961case ZFS_DELEG_NOTE_SNAPSHOT:5962str = gettext("");5963break;5964case ZFS_DELEG_NOTE_LOAD_KEY:5965str = gettext("Allows loading or unloading an encryption key");5966break;5967case ZFS_DELEG_NOTE_CHANGE_KEY:5968str = gettext("Allows changing or adding an encryption key");5969break;5970/*5971* case ZFS_DELEG_NOTE_VSCAN:5972* str = gettext("");5973* break;5974*/5975/* OTHER */5976case ZFS_DELEG_NOTE_GROUPQUOTA:5977str = gettext("Allows accessing any groupquota@... property");5978break;5979case ZFS_DELEG_NOTE_GROUPUSED:5980str = gettext("Allows reading any groupused@... property");5981break;5982case ZFS_DELEG_NOTE_USERPROP:5983str = gettext("Allows changing any user property");5984break;5985case ZFS_DELEG_NOTE_USERQUOTA:5986str = gettext("Allows accessing any userquota@... property");5987break;5988case ZFS_DELEG_NOTE_USERUSED:5989str = gettext("Allows reading any userused@... property");5990break;5991case ZFS_DELEG_NOTE_USEROBJQUOTA:5992str = gettext("Allows accessing any userobjquota@... property");5993break;5994case ZFS_DELEG_NOTE_GROUPOBJQUOTA:5995str = gettext("Allows accessing any \n\t\t\t\t"5996"groupobjquota@... property");5997break;5998case ZFS_DELEG_NOTE_GROUPOBJUSED:5999str = gettext("Allows reading any groupobjused@... property");6000break;6001case ZFS_DELEG_NOTE_USEROBJUSED:6002str = gettext("Allows reading any userobjused@... property");6003break;6004case ZFS_DELEG_NOTE_PROJECTQUOTA:6005str = gettext("Allows accessing any projectquota@... property");6006break;6007case ZFS_DELEG_NOTE_PROJECTOBJQUOTA:6008str = gettext("Allows accessing any \n\t\t\t\t"6009"projectobjquota@... property");6010break;6011case ZFS_DELEG_NOTE_PROJECTUSED:6012str = gettext("Allows reading any projectused@... property");6013break;6014case ZFS_DELEG_NOTE_PROJECTOBJUSED:6015str = gettext("Allows accessing any \n\t\t\t\t"6016"projectobjused@... property");6017break;6018/* other */6019default:6020str = "";6021}60226023return (str);6024}60256026struct allow_opts {6027boolean_t local;6028boolean_t descend;6029boolean_t user;6030boolean_t group;6031boolean_t everyone;6032boolean_t create;6033boolean_t set;6034boolean_t recursive; /* unallow only */6035boolean_t prt_usage;60366037boolean_t prt_perms;6038char *who;6039char *perms;6040const char *dataset;6041};60426043static inline int6044prop_cmp(const void *a, const void *b)6045{6046const char *str1 = *(const char **)a;6047const char *str2 = *(const char **)b;6048return (strcmp(str1, str2));6049}60506051static void6052allow_usage(boolean_t un, boolean_t requested, const char *msg)6053{6054const char *opt_desc[] = {6055"-h", gettext("show this help message and exit"),6056"-l", gettext("set permission locally"),6057"-d", gettext("set permission for descents"),6058"-u", gettext("set permission for user"),6059"-g", gettext("set permission for group"),6060"-e", gettext("set permission for everyone"),6061"-c", gettext("set create time permission"),6062"-s", gettext("define permission set"),6063/* unallow only */6064"-r", gettext("remove permissions recursively"),6065};6066size_t unallow_size = sizeof (opt_desc) / sizeof (char *);6067size_t allow_size = unallow_size - 2;6068const char *props[ZFS_NUM_PROPS];6069int i;6070size_t count = 0;6071FILE *fp = requested ? stdout : stderr;6072zprop_desc_t *pdtbl = zfs_prop_get_table();6073const char *fmt = gettext("%-16s %-14s\t%s\n");60746075(void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW :6076HELP_ALLOW));6077(void) fprintf(fp, gettext("Options:\n"));6078for (i = 0; i < (un ? unallow_size : allow_size); i += 2) {6079const char *opt = opt_desc[i];6080const char *optdsc = opt_desc[i + 1];6081(void) fprintf(fp, gettext(" %-10s %s\n"), opt, optdsc);6082}60836084(void) fprintf(fp, gettext("\nThe following permissions are "6085"supported:\n\n"));6086(void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"),6087gettext("NOTES"));6088for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) {6089const char *perm_name = zfs_deleg_perm_tbl[i].z_perm;6090zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note;6091const char *perm_type = deleg_perm_type(perm_note);6092const char *perm_comment = deleg_perm_comment(perm_note);6093(void) fprintf(fp, fmt, perm_name, perm_type, perm_comment);6094}60956096for (i = 0; i < ZFS_NUM_PROPS; i++) {6097zprop_desc_t *pd = &pdtbl[i];6098if (pd->pd_visible != B_TRUE)6099continue;61006101if (pd->pd_attr == PROP_READONLY)6102continue;61036104props[count++] = pd->pd_name;6105}6106props[count] = NULL;61076108qsort(props, count, sizeof (char *), prop_cmp);61096110for (i = 0; i < count; i++)6111(void) fprintf(fp, fmt, props[i], gettext("property"), "");61126113if (msg != NULL)6114(void) fprintf(fp, gettext("\nzfs: error: %s"), msg);61156116exit(requested ? 0 : 2);6117}61186119static inline const char *6120munge_args(int argc, char **argv, boolean_t un, size_t expected_argc,6121char **permsp)6122{6123if (un && argc == expected_argc - 1)6124*permsp = NULL;6125else if (argc == expected_argc)6126*permsp = argv[argc - 2];6127else6128allow_usage(un, B_FALSE,6129gettext("wrong number of parameters\n"));61306131return (argv[argc - 1]);6132}61336134static void6135parse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts)6136{6137int uge_sum = opts->user + opts->group + opts->everyone;6138int csuge_sum = opts->create + opts->set + uge_sum;6139int ldcsuge_sum = csuge_sum + opts->local + opts->descend;6140int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum;61416142if (uge_sum > 1)6143allow_usage(un, B_FALSE,6144gettext("-u, -g, and -e are mutually exclusive\n"));61456146if (opts->prt_usage) {6147if (argc == 0 && all_sum == 0)6148allow_usage(un, B_TRUE, NULL);6149else6150usage(B_FALSE);6151}61526153if (opts->set) {6154if (csuge_sum > 1)6155allow_usage(un, B_FALSE,6156gettext("invalid options combined with -s\n"));61576158opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);6159if (argv[0][0] != '@')6160allow_usage(un, B_FALSE,6161gettext("invalid set name: missing '@' prefix\n"));6162opts->who = argv[0];6163} else if (opts->create) {6164if (ldcsuge_sum > 1)6165allow_usage(un, B_FALSE,6166gettext("invalid options combined with -c\n"));6167opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);6168} else if (opts->everyone) {6169if (csuge_sum > 1)6170allow_usage(un, B_FALSE,6171gettext("invalid options combined with -e\n"));6172opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);6173} else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone")6174== 0) {6175opts->everyone = B_TRUE;6176argc--;6177argv++;6178opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);6179} else if (argc == 1 && !un) {6180opts->prt_perms = B_TRUE;6181opts->dataset = argv[argc-1];6182} else {6183opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);6184opts->who = argv[0];6185}61866187if (!opts->local && !opts->descend) {6188opts->local = B_TRUE;6189opts->descend = B_TRUE;6190}6191}61926193static void6194store_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend,6195const char *who, char *perms, nvlist_t *top_nvl)6196{6197int i;6198char ld[2] = { '\0', '\0' };6199char who_buf[MAXNAMELEN + 32];6200char base_type = '\0';6201char set_type = '\0';6202nvlist_t *base_nvl = NULL;6203nvlist_t *set_nvl = NULL;6204nvlist_t *nvl;62056206if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0)6207nomem();6208if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) != 0)6209nomem();62106211switch (type) {6212case ZFS_DELEG_NAMED_SET_SETS:6213case ZFS_DELEG_NAMED_SET:6214set_type = ZFS_DELEG_NAMED_SET_SETS;6215base_type = ZFS_DELEG_NAMED_SET;6216ld[0] = ZFS_DELEG_NA;6217break;6218case ZFS_DELEG_CREATE_SETS:6219case ZFS_DELEG_CREATE:6220set_type = ZFS_DELEG_CREATE_SETS;6221base_type = ZFS_DELEG_CREATE;6222ld[0] = ZFS_DELEG_NA;6223break;6224case ZFS_DELEG_USER_SETS:6225case ZFS_DELEG_USER:6226set_type = ZFS_DELEG_USER_SETS;6227base_type = ZFS_DELEG_USER;6228if (local)6229ld[0] = ZFS_DELEG_LOCAL;6230if (descend)6231ld[1] = ZFS_DELEG_DESCENDENT;6232break;6233case ZFS_DELEG_GROUP_SETS:6234case ZFS_DELEG_GROUP:6235set_type = ZFS_DELEG_GROUP_SETS;6236base_type = ZFS_DELEG_GROUP;6237if (local)6238ld[0] = ZFS_DELEG_LOCAL;6239if (descend)6240ld[1] = ZFS_DELEG_DESCENDENT;6241break;6242case ZFS_DELEG_EVERYONE_SETS:6243case ZFS_DELEG_EVERYONE:6244set_type = ZFS_DELEG_EVERYONE_SETS;6245base_type = ZFS_DELEG_EVERYONE;6246if (local)6247ld[0] = ZFS_DELEG_LOCAL;6248if (descend)6249ld[1] = ZFS_DELEG_DESCENDENT;6250break;62516252default:6253assert(set_type != '\0' && base_type != '\0');6254}62556256if (perms != NULL) {6257char *curr = perms;6258char *end = curr + strlen(perms);62596260while (curr < end) {6261char *delim = strchr(curr, ',');6262if (delim == NULL)6263delim = end;6264else6265*delim = '\0';62666267if (curr[0] == '@')6268nvl = set_nvl;6269else6270nvl = base_nvl;62716272(void) nvlist_add_boolean(nvl, curr);6273if (delim != end)6274*delim = ',';6275curr = delim + 1;6276}62776278for (i = 0; i < 2; i++) {6279char locality = ld[i];6280if (locality == 0)6281continue;62826283if (!nvlist_empty(base_nvl)) {6284if (who != NULL)6285(void) snprintf(who_buf,6286sizeof (who_buf), "%c%c$%s",6287base_type, locality, who);6288else6289(void) snprintf(who_buf,6290sizeof (who_buf), "%c%c$",6291base_type, locality);62926293(void) nvlist_add_nvlist(top_nvl, who_buf,6294base_nvl);6295}629662976298if (!nvlist_empty(set_nvl)) {6299if (who != NULL)6300(void) snprintf(who_buf,6301sizeof (who_buf), "%c%c$%s",6302set_type, locality, who);6303else6304(void) snprintf(who_buf,6305sizeof (who_buf), "%c%c$",6306set_type, locality);63076308(void) nvlist_add_nvlist(top_nvl, who_buf,6309set_nvl);6310}6311}6312} else {6313for (i = 0; i < 2; i++) {6314char locality = ld[i];6315if (locality == 0)6316continue;63176318if (who != NULL)6319(void) snprintf(who_buf, sizeof (who_buf),6320"%c%c$%s", base_type, locality, who);6321else6322(void) snprintf(who_buf, sizeof (who_buf),6323"%c%c$", base_type, locality);6324(void) nvlist_add_boolean(top_nvl, who_buf);63256326if (who != NULL)6327(void) snprintf(who_buf, sizeof (who_buf),6328"%c%c$%s", set_type, locality, who);6329else6330(void) snprintf(who_buf, sizeof (who_buf),6331"%c%c$", set_type, locality);6332(void) nvlist_add_boolean(top_nvl, who_buf);6333}6334}6335}63366337static int6338construct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp)6339{6340if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0)6341nomem();63426343if (opts->set) {6344store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local,6345opts->descend, opts->who, opts->perms, *nvlp);6346} else if (opts->create) {6347store_allow_perm(ZFS_DELEG_CREATE, opts->local,6348opts->descend, NULL, opts->perms, *nvlp);6349} else if (opts->everyone) {6350store_allow_perm(ZFS_DELEG_EVERYONE, opts->local,6351opts->descend, NULL, opts->perms, *nvlp);6352} else {6353char *curr = opts->who;6354char *end = curr + strlen(curr);63556356while (curr < end) {6357const char *who;6358zfs_deleg_who_type_t who_type = ZFS_DELEG_WHO_UNKNOWN;6359char *endch;6360char *delim = strchr(curr, ',');6361char errbuf[256];6362char id[64];6363struct passwd *p = NULL;6364struct group *g = NULL;63656366uid_t rid;6367if (delim == NULL)6368delim = end;6369else6370*delim = '\0';63716372rid = (uid_t)strtol(curr, &endch, 0);6373if (opts->user) {6374who_type = ZFS_DELEG_USER;6375if (*endch != '\0')6376p = getpwnam(curr);6377else6378p = getpwuid(rid);63796380if (p != NULL)6381rid = p->pw_uid;6382else if (*endch != '\0') {6383(void) snprintf(errbuf, sizeof (errbuf),6384gettext("invalid user %s\n"), curr);6385allow_usage(un, B_TRUE, errbuf);6386}6387} else if (opts->group) {6388who_type = ZFS_DELEG_GROUP;6389if (*endch != '\0')6390g = getgrnam(curr);6391else6392g = getgrgid(rid);63936394if (g != NULL)6395rid = g->gr_gid;6396else if (*endch != '\0') {6397(void) snprintf(errbuf, sizeof (errbuf),6398gettext("invalid group %s\n"),6399curr);6400allow_usage(un, B_TRUE, errbuf);6401}6402} else {6403if (*endch != '\0') {6404p = getpwnam(curr);6405} else {6406p = getpwuid(rid);6407}64086409if (p == NULL) {6410if (*endch != '\0') {6411g = getgrnam(curr);6412} else {6413g = getgrgid(rid);6414}6415}64166417if (p != NULL) {6418who_type = ZFS_DELEG_USER;6419rid = p->pw_uid;6420} else if (g != NULL) {6421who_type = ZFS_DELEG_GROUP;6422rid = g->gr_gid;6423} else {6424(void) snprintf(errbuf, sizeof (errbuf),6425gettext("invalid user/group %s\n"),6426curr);6427allow_usage(un, B_TRUE, errbuf);6428}6429}64306431(void) sprintf(id, "%u", rid);6432who = id;64336434store_allow_perm(who_type, opts->local,6435opts->descend, who, opts->perms, *nvlp);6436curr = delim + 1;6437}6438}64396440return (0);6441}64426443static void6444print_set_creat_perms(uu_avl_t *who_avl)6445{6446const char *sc_title[] = {6447gettext("Permission sets:\n"),6448gettext("Create time permissions:\n"),6449NULL6450};6451who_perm_node_t *who_node = NULL;6452int prev_weight = -1;64536454for (who_node = uu_avl_first(who_avl); who_node != NULL;6455who_node = uu_avl_next(who_avl, who_node)) {6456uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;6457zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;6458const char *who_name = who_node->who_perm.who_name;6459int weight = who_type2weight(who_type);6460boolean_t first = B_TRUE;6461deleg_perm_node_t *deleg_node;64626463if (prev_weight != weight) {6464(void) printf("%s", sc_title[weight]);6465prev_weight = weight;6466}64676468if (who_name == NULL || strnlen(who_name, 1) == 0)6469(void) printf("\t");6470else6471(void) printf("\t%s ", who_name);64726473for (deleg_node = uu_avl_first(avl); deleg_node != NULL;6474deleg_node = uu_avl_next(avl, deleg_node)) {6475if (first) {6476(void) printf("%s",6477deleg_node->dpn_perm.dp_name);6478first = B_FALSE;6479} else6480(void) printf(",%s",6481deleg_node->dpn_perm.dp_name);6482}64836484(void) printf("\n");6485}6486}64876488static void6489print_uge_deleg_perms(uu_avl_t *who_avl, boolean_t local, boolean_t descend,6490const char *title)6491{6492who_perm_node_t *who_node = NULL;6493boolean_t prt_title = B_TRUE;6494uu_avl_walk_t *walk;64956496if ((walk = uu_avl_walk_start(who_avl, UU_WALK_ROBUST)) == NULL)6497nomem();64986499while ((who_node = uu_avl_walk_next(walk)) != NULL) {6500const char *who_name = who_node->who_perm.who_name;6501const char *nice_who_name = who_node->who_perm.who_ug_name;6502uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;6503zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;6504char delim = ' ';6505deleg_perm_node_t *deleg_node;6506boolean_t prt_who = B_TRUE;65076508for (deleg_node = uu_avl_first(avl);6509deleg_node != NULL;6510deleg_node = uu_avl_next(avl, deleg_node)) {6511if (local != deleg_node->dpn_perm.dp_local ||6512descend != deleg_node->dpn_perm.dp_descend)6513continue;65146515if (prt_who) {6516const char *who = NULL;6517if (prt_title) {6518prt_title = B_FALSE;6519(void) printf("%s", title);6520}65216522switch (who_type) {6523case ZFS_DELEG_USER_SETS:6524case ZFS_DELEG_USER:6525who = gettext("user");6526if (nice_who_name)6527who_name = nice_who_name;6528break;6529case ZFS_DELEG_GROUP_SETS:6530case ZFS_DELEG_GROUP:6531who = gettext("group");6532if (nice_who_name)6533who_name = nice_who_name;6534break;6535case ZFS_DELEG_EVERYONE_SETS:6536case ZFS_DELEG_EVERYONE:6537who = gettext("everyone");6538who_name = NULL;6539break;65406541default:6542assert(who != NULL);6543}65446545prt_who = B_FALSE;6546if (who_name == NULL)6547(void) printf("\t%s", who);6548else6549(void) printf("\t%s %s", who, who_name);6550}65516552(void) printf("%c%s", delim,6553deleg_node->dpn_perm.dp_name);6554delim = ',';6555}65566557if (!prt_who)6558(void) printf("\n");6559}65606561uu_avl_walk_end(walk);6562}65636564static void6565print_fs_perms(fs_perm_set_t *fspset)6566{6567fs_perm_node_t *node = NULL;6568char buf[MAXNAMELEN + 32];6569const char *dsname = buf;65706571for (node = uu_list_first(fspset->fsps_list); node != NULL;6572node = uu_list_next(fspset->fsps_list, node)) {6573uu_avl_t *sc_avl = node->fspn_fsperm.fsp_sc_avl;6574uu_avl_t *uge_avl = node->fspn_fsperm.fsp_uge_avl;6575int left = 0;65766577(void) snprintf(buf, sizeof (buf),6578gettext("---- Permissions on %s "),6579node->fspn_fsperm.fsp_name);6580(void) printf("%s", dsname);6581left = 70 - strlen(buf);6582while (left-- > 0)6583(void) printf("-");6584(void) printf("\n");65856586print_set_creat_perms(sc_avl);6587print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE,6588gettext("Local permissions:\n"));6589print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE,6590gettext("Descendent permissions:\n"));6591print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE,6592gettext("Local+Descendent permissions:\n"));6593}6594}65956596static fs_perm_set_t fs_perm_set = { NULL, NULL, NULL, NULL };65976598struct deleg_perms {6599boolean_t un;6600nvlist_t *nvl;6601};66026603static int6604set_deleg_perms(zfs_handle_t *zhp, void *data)6605{6606struct deleg_perms *perms = (struct deleg_perms *)data;6607zfs_type_t zfs_type = zfs_get_type(zhp);66086609if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME)6610return (0);66116612return (zfs_set_fsacl(zhp, perms->un, perms->nvl));6613}66146615static int6616zfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un)6617{6618zfs_handle_t *zhp;6619nvlist_t *perm_nvl = NULL;6620nvlist_t *update_perm_nvl = NULL;6621int error = 1;6622int c;6623struct allow_opts opts = { 0 };66246625const char *optstr = un ? "ldugecsrh" : "ldugecsh";66266627/* check opts */6628while ((c = getopt(argc, argv, optstr)) != -1) {6629switch (c) {6630case 'l':6631opts.local = B_TRUE;6632break;6633case 'd':6634opts.descend = B_TRUE;6635break;6636case 'u':6637opts.user = B_TRUE;6638break;6639case 'g':6640opts.group = B_TRUE;6641break;6642case 'e':6643opts.everyone = B_TRUE;6644break;6645case 's':6646opts.set = B_TRUE;6647break;6648case 'c':6649opts.create = B_TRUE;6650break;6651case 'r':6652opts.recursive = B_TRUE;6653break;6654case ':':6655(void) fprintf(stderr, gettext("missing argument for "6656"'%c' option\n"), optopt);6657usage(B_FALSE);6658break;6659case 'h':6660opts.prt_usage = B_TRUE;6661break;6662case '?':6663(void) fprintf(stderr, gettext("invalid option '%c'\n"),6664optopt);6665usage(B_FALSE);6666}6667}66686669argc -= optind;6670argv += optind;66716672/* check arguments */6673parse_allow_args(argc, argv, un, &opts);66746675/* try to open the dataset */6676if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM |6677ZFS_TYPE_VOLUME)) == NULL) {6678(void) fprintf(stderr, "Failed to open dataset: %s\n",6679opts.dataset);6680return (-1);6681}66826683if (zfs_get_fsacl(zhp, &perm_nvl) != 0)6684goto cleanup2;66856686fs_perm_set_init(&fs_perm_set);6687if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) {6688(void) fprintf(stderr, "Failed to parse fsacl permissions\n");6689goto cleanup1;6690}66916692if (opts.prt_perms)6693print_fs_perms(&fs_perm_set);6694else {6695(void) construct_fsacl_list(un, &opts, &update_perm_nvl);6696if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0)6697goto cleanup0;66986699if (un && opts.recursive) {6700struct deleg_perms data = { un, update_perm_nvl };6701if (zfs_iter_filesystems_v2(zhp, 0, set_deleg_perms,6702&data) != 0)6703goto cleanup0;6704}6705}67066707error = 0;67086709cleanup0:6710nvlist_free(perm_nvl);6711nvlist_free(update_perm_nvl);6712cleanup1:6713fs_perm_set_fini(&fs_perm_set);6714cleanup2:6715zfs_close(zhp);67166717return (error);6718}67196720static int6721zfs_do_allow(int argc, char **argv)6722{6723return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE));6724}67256726static int6727zfs_do_unallow(int argc, char **argv)6728{6729return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE));6730}67316732static int6733zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)6734{6735int errors = 0;6736int i;6737const char *tag;6738boolean_t recursive = B_FALSE;6739const char *opts = holding ? "rt" : "r";6740int c;67416742/* check options */6743while ((c = getopt(argc, argv, opts)) != -1) {6744switch (c) {6745case 'r':6746recursive = B_TRUE;6747break;6748case '?':6749(void) fprintf(stderr, gettext("invalid option '%c'\n"),6750optopt);6751usage(B_FALSE);6752}6753}67546755argc -= optind;6756argv += optind;67576758/* check number of arguments */6759if (argc < 2)6760usage(B_FALSE);67616762tag = argv[0];6763--argc;6764++argv;67656766if (holding && tag[0] == '.') {6767/* tags starting with '.' are reserved for libzfs */6768(void) fprintf(stderr, gettext("tag may not start with '.'\n"));6769usage(B_FALSE);6770}67716772for (i = 0; i < argc; ++i) {6773zfs_handle_t *zhp;6774char parent[ZFS_MAX_DATASET_NAME_LEN];6775const char *delim;6776char *path = argv[i];67776778delim = strchr(path, '@');6779if (delim == NULL) {6780(void) fprintf(stderr,6781gettext("'%s' is not a snapshot\n"), path);6782++errors;6783continue;6784}6785(void) strlcpy(parent, path, MIN(sizeof (parent),6786delim - path + 1));67876788zhp = zfs_open(g_zfs, parent,6789ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);6790if (zhp == NULL) {6791++errors;6792continue;6793}6794if (holding) {6795if (zfs_hold(zhp, delim+1, tag, recursive, -1) != 0)6796++errors;6797} else {6798if (zfs_release(zhp, delim+1, tag, recursive) != 0)6799++errors;6800}6801zfs_close(zhp);6802}68036804return (errors != 0);6805}68066807/*6808* zfs hold [-r] [-t] <tag> <snap> ...6809*6810* -r Recursively hold6811*6812* Apply a user-hold with the given tag to the list of snapshots.6813*/6814static int6815zfs_do_hold(int argc, char **argv)6816{6817return (zfs_do_hold_rele_impl(argc, argv, B_TRUE));6818}68196820/*6821* zfs release [-r] <tag> <snap> ...6822*6823* -r Recursively release6824*6825* Release a user-hold with the given tag from the list of snapshots.6826*/6827static int6828zfs_do_release(int argc, char **argv)6829{6830return (zfs_do_hold_rele_impl(argc, argv, B_FALSE));6831}68326833typedef struct holds_cbdata {6834boolean_t cb_recursive;6835const char *cb_snapname;6836nvlist_t **cb_nvlp;6837size_t cb_max_namelen;6838size_t cb_max_taglen;6839} holds_cbdata_t;68406841#define STRFTIME_FMT_STR "%a %b %e %H:%M %Y"6842#define DATETIME_BUF_LEN (32)6843/*6844*6845*/6846static void6847print_holds(boolean_t scripted, int nwidth, int tagwidth, nvlist_t *nvl,6848boolean_t parsable)6849{6850int i;6851nvpair_t *nvp = NULL;6852const char *const hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" };6853const char *col;68546855if (!scripted) {6856for (i = 0; i < 3; i++) {6857col = gettext(hdr_cols[i]);6858if (i < 2)6859(void) printf("%-*s ", i ? tagwidth : nwidth,6860col);6861else6862(void) printf("%s\n", col);6863}6864}68656866while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {6867const char *zname = nvpair_name(nvp);6868nvlist_t *nvl2;6869nvpair_t *nvp2 = NULL;6870(void) nvpair_value_nvlist(nvp, &nvl2);6871while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) {6872char tsbuf[DATETIME_BUF_LEN];6873const char *tagname = nvpair_name(nvp2);6874uint64_t val = 0;6875time_t time;6876struct tm t;68776878(void) nvpair_value_uint64(nvp2, &val);6879time = (time_t)val;6880(void) localtime_r(&time, &t);6881(void) strftime(tsbuf, DATETIME_BUF_LEN,6882gettext(STRFTIME_FMT_STR), &t);68836884if (scripted) {6885if (parsable) {6886(void) printf("%s\t%s\t%lld\n", zname,6887tagname, (long long)time);6888} else {6889(void) printf("%s\t%s\t%s\n", zname,6890tagname, tsbuf);6891}6892} else {6893if (parsable) {6894(void) printf("%-*s %-*s %lld\n",6895nwidth, zname, tagwidth,6896tagname, (long long)time);6897} else {6898(void) printf("%-*s %-*s %s\n",6899nwidth, zname, tagwidth,6900tagname, tsbuf);6901}6902}6903}6904}6905}69066907/*6908* Generic callback function to list a dataset or snapshot.6909*/6910static int6911holds_callback(zfs_handle_t *zhp, void *data)6912{6913holds_cbdata_t *cbp = data;6914nvlist_t *top_nvl = *cbp->cb_nvlp;6915nvlist_t *nvl = NULL;6916nvpair_t *nvp = NULL;6917const char *zname = zfs_get_name(zhp);6918size_t znamelen = strlen(zname);69196920if (cbp->cb_recursive) {6921const char *snapname;6922char *delim = strchr(zname, '@');6923if (delim == NULL)6924return (0);69256926snapname = delim + 1;6927if (strcmp(cbp->cb_snapname, snapname))6928return (0);6929}69306931if (zfs_get_holds(zhp, &nvl) != 0)6932return (-1);69336934if (znamelen > cbp->cb_max_namelen)6935cbp->cb_max_namelen = znamelen;69366937while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {6938const char *tag = nvpair_name(nvp);6939size_t taglen = strlen(tag);6940if (taglen > cbp->cb_max_taglen)6941cbp->cb_max_taglen = taglen;6942}69436944return (nvlist_add_nvlist(top_nvl, zname, nvl));6945}69466947/*6948* zfs holds [-rHp] <snap> ...6949*6950* -r Lists holds that are set on the named snapshots recursively.6951* -H Scripted mode; elide headers and separate columns by tabs.6952* -p Display values in parsable (literal) format.6953*/6954static int6955zfs_do_holds(int argc, char **argv)6956{6957int c;6958boolean_t errors = B_FALSE;6959boolean_t scripted = B_FALSE;6960boolean_t recursive = B_FALSE;6961boolean_t parsable = B_FALSE;69626963int types = ZFS_TYPE_SNAPSHOT;6964holds_cbdata_t cb = { 0 };69656966int limit = 0;6967int ret = 0;6968int flags = 0;69696970/* check options */6971while ((c = getopt(argc, argv, "rHp")) != -1) {6972switch (c) {6973case 'r':6974recursive = B_TRUE;6975break;6976case 'H':6977scripted = B_TRUE;6978break;6979case 'p':6980parsable = B_TRUE;6981break;6982case '?':6983(void) fprintf(stderr, gettext("invalid option '%c'\n"),6984optopt);6985usage(B_FALSE);6986}6987}69886989if (recursive) {6990types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;6991flags |= ZFS_ITER_RECURSE;6992}69936994argc -= optind;6995argv += optind;69966997/* check number of arguments */6998if (argc < 1)6999usage(B_FALSE);70007001nvlist_t *nvl = fnvlist_alloc();70027003for (int i = 0; i < argc; ++i) {7004char *snapshot = argv[i];7005const char *delim;7006const char *snapname;70077008delim = strchr(snapshot, '@');7009if (delim == NULL) {7010(void) fprintf(stderr,7011gettext("'%s' is not a snapshot\n"), snapshot);7012errors = B_TRUE;7013continue;7014}7015snapname = delim + 1;7016if (recursive)7017snapshot[delim - snapshot] = '\0';70187019cb.cb_recursive = recursive;7020cb.cb_snapname = snapname;7021cb.cb_nvlp = &nvl;70227023/*7024* 1. collect holds data, set format options7025*/7026ret = zfs_for_each(1, argv + i, flags, types, NULL, NULL, limit,7027holds_callback, &cb);7028if (ret != 0)7029errors = B_TRUE;7030}70317032/*7033* 2. print holds data7034*/7035print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl,7036parsable);70377038if (nvlist_empty(nvl))7039(void) fprintf(stderr, gettext("no datasets available\n"));70407041nvlist_free(nvl);70427043return (errors);7044}70457046#define CHECK_SPINNER 307047#define SPINNER_TIME 3 /* seconds */7048#define MOUNT_TIME 1 /* seconds */70497050typedef struct get_all_state {7051char **ga_datasets;7052int ga_count;7053boolean_t ga_verbose;7054get_all_cb_t *ga_cbp;7055} get_all_state_t;70567057static int7058get_one_dataset(zfs_handle_t *zhp, void *data)7059{7060static const char *const spin[] = { "-", "\\", "|", "/" };7061static int spinval = 0;7062static int spincheck = 0;7063static time_t last_spin_time = (time_t)0;7064get_all_state_t *state = data;7065zfs_type_t type = zfs_get_type(zhp);70667067if (state->ga_verbose) {7068if (--spincheck < 0) {7069time_t now = time(NULL);7070if (last_spin_time + SPINNER_TIME < now) {7071update_progress(spin[spinval++ % 4]);7072last_spin_time = now;7073}7074spincheck = CHECK_SPINNER;7075}7076}70777078/*7079* Iterate over any nested datasets.7080*/7081if (zfs_iter_filesystems_v2(zhp, 0, get_one_dataset, data) != 0) {7082zfs_close(zhp);7083return (1);7084}70857086/*7087* Skip any datasets whose type does not match.7088*/7089if ((type & ZFS_TYPE_FILESYSTEM) == 0) {7090zfs_close(zhp);7091return (0);7092}7093libzfs_add_handle(state->ga_cbp, zhp);7094assert(state->ga_cbp->cb_used <= state->ga_cbp->cb_alloc);70957096return (0);7097}70987099static int7100get_recursive_datasets(zfs_handle_t *zhp, void *data)7101{7102get_all_state_t *state = data;7103int len = strlen(zfs_get_name(zhp));7104for (int i = 0; i < state->ga_count; ++i) {7105if (strcmp(state->ga_datasets[i], zfs_get_name(zhp)) == 0)7106return (get_one_dataset(zhp, data));7107else if ((strncmp(state->ga_datasets[i], zfs_get_name(zhp),7108len) == 0) && state->ga_datasets[i][len] == '/') {7109(void) zfs_iter_filesystems_v2(zhp, 0,7110get_recursive_datasets, data);7111}7112}7113zfs_close(zhp);7114return (0);7115}71167117static void7118get_all_datasets(get_all_state_t *state)7119{7120if (state->ga_verbose)7121set_progress_header(gettext("Reading ZFS config"));7122if (state->ga_datasets == NULL)7123(void) zfs_iter_root(g_zfs, get_one_dataset, state);7124else7125(void) zfs_iter_root(g_zfs, get_recursive_datasets, state);71267127if (state->ga_verbose)7128finish_progress(gettext("done."));7129}71307131/*7132* Generic callback for sharing or mounting filesystems. Because the code is so7133* similar, we have a common function with an extra parameter to determine which7134* mode we are using.7135*/7136typedef enum { OP_SHARE, OP_MOUNT } share_mount_op_t;71377138typedef struct share_mount_state {7139share_mount_op_t sm_op;7140boolean_t sm_verbose;7141int sm_flags;7142char *sm_options;7143enum sa_protocol sm_proto; /* only valid for OP_SHARE */7144pthread_mutex_t sm_lock; /* protects the remaining fields */7145uint_t sm_total; /* number of filesystems to process */7146uint_t sm_done; /* number of filesystems processed */7147int sm_status; /* -1 if any of the share/mount operations failed */7148} share_mount_state_t;71497150/*7151* Share or mount a dataset.7152*/7153static int7154share_mount_one(zfs_handle_t *zhp, int op, int flags, enum sa_protocol protocol,7155boolean_t explicit, const char *options)7156{7157char mountpoint[ZFS_MAXPROPLEN];7158char shareopts[ZFS_MAXPROPLEN];7159char smbshareopts[ZFS_MAXPROPLEN];7160const char *cmdname = op == OP_SHARE ? "share" : "mount";7161struct mnttab mnt;7162uint64_t zoned, canmount;7163boolean_t shared_nfs, shared_smb;71647165assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM);71667167/*7168* Check to make sure we can mount/share this dataset. If we7169* are in the global zone and the filesystem is exported to a7170* local zone, or if we are in a local zone and the7171* filesystem is not exported, then it is an error.7172*/7173zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);71747175if (zoned && getzoneid() == GLOBAL_ZONEID) {7176if (!explicit)7177return (0);71787179(void) fprintf(stderr, gettext("cannot %s '%s': "7180"dataset is exported to a local zone\n"), cmdname,7181zfs_get_name(zhp));7182return (1);71837184} else if (!zoned && getzoneid() != GLOBAL_ZONEID) {7185if (!explicit)7186return (0);71877188(void) fprintf(stderr, gettext("cannot %s '%s': "7189"permission denied\n"), cmdname,7190zfs_get_name(zhp));7191return (1);7192}71937194/*7195* Ignore any filesystems which don't apply to us. This7196* includes those with a legacy mountpoint, or those with7197* legacy share options.7198*/7199verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,7200sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);7201verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,7202sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);7203verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts,7204sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0);72057206if (op == OP_SHARE && strcmp(shareopts, "off") == 0 &&7207strcmp(smbshareopts, "off") == 0) {7208if (!explicit)7209return (0);72107211(void) fprintf(stderr, gettext("cannot share '%s': "7212"legacy share\n"), zfs_get_name(zhp));7213(void) fprintf(stderr, gettext("use exports(5) or "7214"smb.conf(5) to share this filesystem, or set "7215"the sharenfs or sharesmb property\n"));7216return (1);7217}72187219/*7220* We cannot share or mount legacy filesystems. If the7221* shareopts is non-legacy but the mountpoint is legacy, we7222* treat it as a legacy share.7223*/7224if (strcmp(mountpoint, "legacy") == 0) {7225if (!explicit)7226return (0);72277228(void) fprintf(stderr, gettext("cannot %s '%s': "7229"legacy mountpoint\n"), cmdname, zfs_get_name(zhp));7230(void) fprintf(stderr, gettext("use %s(8) to "7231"%s this filesystem\n"), cmdname, cmdname);7232return (1);7233}72347235if (strcmp(mountpoint, "none") == 0) {7236if (!explicit)7237return (0);72387239(void) fprintf(stderr, gettext("cannot %s '%s': no "7240"mountpoint set\n"), cmdname, zfs_get_name(zhp));7241return (1);7242}72437244/*7245* canmount explicit outcome7246* on no pass through7247* on yes pass through7248* off no return 07249* off yes display error, return 17250* noauto no return 07251* noauto yes pass through7252*/7253canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);7254if (canmount == ZFS_CANMOUNT_OFF) {7255if (!explicit)7256return (0);72577258(void) fprintf(stderr, gettext("cannot %s '%s': "7259"'canmount' property is set to 'off'\n"), cmdname,7260zfs_get_name(zhp));7261return (1);7262} else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) {7263/*7264* When performing a 'zfs mount -a', we skip any mounts for7265* datasets that have 'noauto' set. Sharing a dataset with7266* 'noauto' set is only allowed if it's mounted.7267*/7268if (op == OP_MOUNT)7269return (0);7270if (op == OP_SHARE && !zfs_is_mounted(zhp, NULL)) {7271/* also purge it from existing exports */7272zfs_unshare(zhp, mountpoint, NULL);7273return (0);7274}7275}72767277/*7278* If this filesystem is encrypted and does not have7279* a loaded key, we can not mount it.7280*/7281if ((flags & MS_CRYPT) == 0 &&7282zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF &&7283zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS) ==7284ZFS_KEYSTATUS_UNAVAILABLE) {7285if (!explicit)7286return (0);72877288(void) fprintf(stderr, gettext("cannot %s '%s': "7289"encryption key not loaded\n"), cmdname, zfs_get_name(zhp));7290return (1);7291}72927293/*7294* If this filesystem is inconsistent and has a receive resume7295* token, we can not mount it.7296*/7297if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) &&7298zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,7299NULL, 0, NULL, NULL, 0, B_TRUE) == 0) {7300if (!explicit)7301return (0);73027303(void) fprintf(stderr, gettext("cannot %s '%s': "7304"Contains partially-completed state from "7305"\"zfs receive -s\", which can be resumed with "7306"\"zfs send -t\"\n"),7307cmdname, zfs_get_name(zhp));7308return (1);7309}73107311if (zfs_prop_get_int(zhp, ZFS_PROP_REDACTED) && !(flags & MS_FORCE)) {7312if (!explicit)7313return (0);73147315(void) fprintf(stderr, gettext("cannot %s '%s': "7316"Dataset is not complete, was created by receiving "7317"a redacted zfs send stream.\n"), cmdname,7318zfs_get_name(zhp));7319return (1);7320}73217322/*7323* At this point, we have verified that the mountpoint and/or7324* shareopts are appropriate for auto management. If the7325* filesystem is already mounted or shared, return (failing7326* for explicit requests); otherwise mount or share the7327* filesystem.7328*/7329switch (op) {7330case OP_SHARE: {7331enum sa_protocol prot[] = {SA_PROTOCOL_NFS, SA_NO_PROTOCOL};7332shared_nfs = zfs_is_shared(zhp, NULL, prot);7333*prot = SA_PROTOCOL_SMB;7334shared_smb = zfs_is_shared(zhp, NULL, prot);73357336if ((shared_nfs && shared_smb) ||7337(shared_nfs && strcmp(shareopts, "on") == 0 &&7338strcmp(smbshareopts, "off") == 0) ||7339(shared_smb && strcmp(smbshareopts, "on") == 0 &&7340strcmp(shareopts, "off") == 0)) {7341if (!explicit)7342return (0);73437344(void) fprintf(stderr, gettext("cannot share "7345"'%s': filesystem already shared\n"),7346zfs_get_name(zhp));7347return (1);7348}73497350if (!zfs_is_mounted(zhp, NULL) &&7351zfs_mount(zhp, NULL, flags) != 0)7352return (1);73537354*prot = protocol;7355if (zfs_share(zhp, protocol == SA_NO_PROTOCOL ? NULL : prot))7356return (1);73577358}7359break;73607361case OP_MOUNT:7362mnt.mnt_mntopts = (char *)(options ?: "");73637364if (!hasmntopt(&mnt, MNTOPT_REMOUNT) &&7365zfs_is_mounted(zhp, NULL)) {7366if (!explicit)7367return (0);73687369(void) fprintf(stderr, gettext("cannot mount "7370"'%s': filesystem already mounted\n"),7371zfs_get_name(zhp));7372return (1);7373}73747375if (zfs_mount(zhp, options, flags) != 0)7376return (1);7377break;7378}73797380return (0);7381}73827383/*7384* Reports progress in the form "(current/total)". Not thread-safe.7385*/7386static void7387report_mount_progress(int current, int total)7388{7389static time_t last_progress_time = 0;7390time_t now = time(NULL);7391char info[32];73927393/* display header if we're here for the first time */7394if (current == 1) {7395set_progress_header(gettext("Mounting ZFS filesystems"));7396} else if (current != total && last_progress_time + MOUNT_TIME >= now) {7397/* too soon to report again */7398return;7399}74007401last_progress_time = now;74027403(void) sprintf(info, "(%d/%d)", current, total);74047405if (current == total)7406finish_progress(info);7407else7408update_progress(info);7409}74107411/*7412* zfs_foreach_mountpoint() callback that mounts or shares one filesystem and7413* updates the progress meter.7414*/7415static int7416share_mount_one_cb(zfs_handle_t *zhp, void *arg)7417{7418share_mount_state_t *sms = arg;7419int ret;74207421ret = share_mount_one(zhp, sms->sm_op, sms->sm_flags, sms->sm_proto,7422B_FALSE, sms->sm_options);74237424pthread_mutex_lock(&sms->sm_lock);7425if (ret != 0)7426sms->sm_status = ret;7427sms->sm_done++;7428if (sms->sm_verbose)7429report_mount_progress(sms->sm_done, sms->sm_total);7430pthread_mutex_unlock(&sms->sm_lock);7431return (ret);7432}74337434static void7435append_options(char *mntopts, char *newopts)7436{7437int len = strlen(mntopts);74387439/* original length plus new string to append plus 1 for the comma */7440if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) {7441(void) fprintf(stderr, gettext("the opts argument for "7442"'%s' option is too long (more than %d chars)\n"),7443"-o", MNT_LINE_MAX);7444usage(B_FALSE);7445}74467447if (*mntopts)7448mntopts[len++] = ',';74497450(void) strcpy(&mntopts[len], newopts);7451}74527453static enum sa_protocol7454sa_protocol_decode(const char *protocol)7455{7456for (enum sa_protocol i = 0; i < ARRAY_SIZE(sa_protocol_names); ++i)7457if (strcmp(protocol, sa_protocol_names[i]) == 0)7458return (i);74597460(void) fputs(gettext("share type must be one of: "), stderr);7461for (enum sa_protocol i = 0;7462i < ARRAY_SIZE(sa_protocol_names); ++i)7463(void) fprintf(stderr, "%s%s",7464i != 0 ? ", " : "", sa_protocol_names[i]);7465(void) fputc('\n', stderr);7466usage(B_FALSE);7467}74687469static int7470share_mount(int op, int argc, char **argv)7471{7472int do_all = 0;7473int recursive = 0;7474boolean_t verbose = B_FALSE;7475boolean_t json = B_FALSE;7476int c, ret = 0;7477char *options = NULL;7478int flags = 0;7479nvlist_t *jsobj, *data, *item;7480const uint_t mount_nthr = 512;7481uint_t nthr;7482jsobj = data = item = NULL;74837484struct option long_options[] = {7485{"json", no_argument, NULL, 'j'},7486{0, 0, 0, 0}7487};74887489/* check options */7490while ((c = getopt_long(argc, argv,7491op == OP_MOUNT ? ":ajRlvo:Of" : "al",7492op == OP_MOUNT ? long_options : NULL, NULL)) != -1) {7493switch (c) {7494case 'a':7495do_all = 1;7496break;7497case 'R':7498recursive = 1;7499break;7500case 'v':7501verbose = B_TRUE;7502break;7503case 'l':7504flags |= MS_CRYPT;7505break;7506case 'j':7507json = B_TRUE;7508jsobj = zfs_json_schema(0, 1);7509data = fnvlist_alloc();7510break;7511case 'o':7512if (*optarg == '\0') {7513(void) fprintf(stderr, gettext("empty mount "7514"options (-o) specified\n"));7515usage(B_FALSE);7516}75177518if (options == NULL)7519options = safe_malloc(MNT_LINE_MAX + 1);75207521/* option validation is done later */7522append_options(options, optarg);7523break;7524case 'O':7525flags |= MS_OVERLAY;7526break;7527case 'f':7528flags |= MS_FORCE;7529break;7530case ':':7531(void) fprintf(stderr, gettext("missing argument for "7532"'%c' option\n"), optopt);7533usage(B_FALSE);7534break;7535case '?':7536(void) fprintf(stderr, gettext("invalid option '%c'\n"),7537optopt);7538usage(B_FALSE);7539}7540}75417542argc -= optind;7543argv += optind;75447545if (json && argc != 0) {7546(void) fprintf(stderr, gettext("too many arguments\n"));7547usage(B_FALSE);7548}75497550/* check number of arguments */7551if (do_all || recursive) {7552enum sa_protocol protocol = SA_NO_PROTOCOL;75537554if (op == OP_SHARE && argc > 0) {7555protocol = sa_protocol_decode(argv[0]);7556argc--;7557argv++;7558}75597560if (argc != 0 && do_all) {7561(void) fprintf(stderr, gettext("too many arguments\n"));7562usage(B_FALSE);7563}75647565if (argc == 0 && recursive) {7566(void) fprintf(stderr,7567gettext("no dataset provided\n"));7568usage(B_FALSE);7569}75707571start_progress_timer();7572get_all_cb_t cb = { 0 };7573get_all_state_t state = { 0 };7574if (argc == 0) {7575state.ga_datasets = NULL;7576state.ga_count = -1;7577} else {7578zfs_handle_t *zhp;7579for (int i = 0; i < argc; i++) {7580zhp = zfs_open(g_zfs, argv[i],7581ZFS_TYPE_FILESYSTEM);7582if (zhp == NULL)7583usage(B_FALSE);7584zfs_close(zhp);7585}7586state.ga_datasets = argv;7587state.ga_count = argc;7588}7589state.ga_verbose = verbose;7590state.ga_cbp = &cb;7591get_all_datasets(&state);75927593if (cb.cb_used == 0) {7594free(options);7595return (0);7596}75977598share_mount_state_t share_mount_state = { 0 };7599share_mount_state.sm_op = op;7600share_mount_state.sm_verbose = verbose;7601share_mount_state.sm_flags = flags;7602share_mount_state.sm_options = options;7603share_mount_state.sm_proto = protocol;7604share_mount_state.sm_total = cb.cb_used;7605pthread_mutex_init(&share_mount_state.sm_lock, NULL);76067607/* For a 'zfs share -a' operation start with a clean slate. */7608if (op == OP_SHARE)7609zfs_truncate_shares(NULL);76107611/*7612* libshare isn't mt-safe, so only do the operation in parallel7613* if we're mounting. Additionally, the key-loading option must7614* be serialized so that we can prompt the user for their keys7615* in a consistent manner.7616*/7617nthr = op == OP_MOUNT && !(flags & MS_CRYPT) ? mount_nthr : 1;7618zfs_foreach_mountpoint(g_zfs, cb.cb_handles, cb.cb_used,7619share_mount_one_cb, &share_mount_state, nthr);7620zfs_commit_shares(NULL);76217622ret = share_mount_state.sm_status;76237624for (int i = 0; i < cb.cb_used; i++)7625zfs_close(cb.cb_handles[i]);7626free(cb.cb_handles);7627} else if (argc == 0) {7628FILE *mnttab;7629struct mnttab entry;76307631if ((op == OP_SHARE) || (options != NULL)) {7632(void) fprintf(stderr, gettext("missing filesystem "7633"argument (specify -a for all)\n"));7634usage(B_FALSE);7635}76367637/*7638* When mount is given no arguments, go through7639* /proc/self/mounts and display any active ZFS mounts.7640* We hide any snapshots, since they are controlled7641* automatically.7642*/76437644if ((mnttab = fopen(MNTTAB, "re")) == NULL) {7645free(options);7646return (ENOENT);7647}76487649while (getmntent(mnttab, &entry) == 0) {7650if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 ||7651strchr(entry.mnt_special, '@') != NULL)7652continue;7653if (json) {7654item = fnvlist_alloc();7655fnvlist_add_string(item, "filesystem",7656entry.mnt_special);7657fnvlist_add_string(item, "mountpoint",7658entry.mnt_mountp);7659fnvlist_add_nvlist(data, entry.mnt_special,7660item);7661fnvlist_free(item);7662} else {7663(void) printf("%-30s %s\n", entry.mnt_special,7664entry.mnt_mountp);7665}7666}76677668(void) fclose(mnttab);7669if (json) {7670fnvlist_add_nvlist(jsobj, "datasets", data);7671if (nvlist_empty(data))7672fnvlist_free(jsobj);7673else7674zcmd_print_json(jsobj);7675fnvlist_free(data);7676}7677} else {7678zfs_handle_t *zhp;76797680if (argc > 1) {7681(void) fprintf(stderr,7682gettext("too many arguments\n"));7683usage(B_FALSE);7684}76857686if ((zhp = zfs_open(g_zfs, argv[0],7687ZFS_TYPE_FILESYSTEM)) == NULL) {7688ret = 1;7689} else {7690ret = share_mount_one(zhp, op, flags, SA_NO_PROTOCOL,7691B_TRUE, options);7692zfs_commit_shares(NULL);7693zfs_close(zhp);7694}7695}76967697free(options);7698return (ret);7699}77007701/*7702* zfs mount -a7703* zfs mount filesystem7704*7705* Mount all filesystems, or mount the given filesystem.7706*/7707static int7708zfs_do_mount(int argc, char **argv)7709{7710return (share_mount(OP_MOUNT, argc, argv));7711}77127713/*7714* zfs share -a [nfs | smb]7715* zfs share filesystem7716*7717* Share all filesystems, or share the given filesystem.7718*/7719static int7720zfs_do_share(int argc, char **argv)7721{7722return (share_mount(OP_SHARE, argc, argv));7723}77247725typedef struct unshare_unmount_node {7726zfs_handle_t *un_zhp;7727char *un_mountp;7728uu_avl_node_t un_avlnode;7729} unshare_unmount_node_t;77307731static int7732unshare_unmount_compare(const void *larg, const void *rarg, void *unused)7733{7734(void) unused;7735const unshare_unmount_node_t *l = larg;7736const unshare_unmount_node_t *r = rarg;77377738return (strcmp(l->un_mountp, r->un_mountp));7739}77407741/*7742* Convenience routine used by zfs_do_umount() and manual_unmount(). Given an7743* absolute path, find the entry /proc/self/mounts, verify that it's a7744* ZFS filesystem, and unmount it appropriately.7745*/7746static int7747unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)7748{7749zfs_handle_t *zhp;7750int ret = 0;7751struct stat64 statbuf;7752struct extmnttab entry;7753const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount";7754ino_t path_inode;7755char *zfs_mntpnt, *entry_mntpnt;77567757/*7758* Search for the given (major,minor) pair in the mount table.7759*/77607761if (getextmntent(path, &entry, &statbuf) != 0) {7762if (op == OP_SHARE) {7763(void) fprintf(stderr, gettext("cannot %s '%s': not "7764"currently mounted\n"), cmdname, path);7765return (1);7766}7767(void) fprintf(stderr, gettext("warning: %s not in"7768"/proc/self/mounts\n"), path);7769if ((ret = umount2(path, flags)) != 0)7770(void) fprintf(stderr, gettext("%s: %s\n"), path,7771strerror(errno));7772return (ret != 0);7773}7774path_inode = statbuf.st_ino;77757776if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) {7777(void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS "7778"filesystem\n"), cmdname, path);7779return (1);7780}77817782if ((zhp = zfs_open(g_zfs, entry.mnt_special,7783ZFS_TYPE_FILESYSTEM)) == NULL)7784return (1);77857786ret = 1;7787if (stat64(entry.mnt_mountp, &statbuf) != 0) {7788(void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),7789cmdname, path, strerror(errno));7790goto out;7791} else if (statbuf.st_ino != path_inode) {7792(void) fprintf(stderr, gettext("cannot "7793"%s '%s': not a mountpoint\n"), cmdname, path);7794goto out;7795}77967797/*7798* If the filesystem is mounted, check that the mountpoint matches7799* the one in the mnttab entry w.r.t. provided path. If it doesn't,7800* then we should not proceed further.7801*/7802entry_mntpnt = strdup(entry.mnt_mountp);7803if (zfs_is_mounted(zhp, &zfs_mntpnt)) {7804if (strcmp(zfs_mntpnt, entry_mntpnt) != 0) {7805(void) fprintf(stderr, gettext("cannot %s '%s': "7806"not an original mountpoint\n"), cmdname, path);7807free(zfs_mntpnt);7808free(entry_mntpnt);7809goto out;7810}7811free(zfs_mntpnt);7812}7813free(entry_mntpnt);78147815if (op == OP_SHARE) {7816char nfs_mnt_prop[ZFS_MAXPROPLEN];7817char smbshare_prop[ZFS_MAXPROPLEN];78187819verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, nfs_mnt_prop,7820sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0);7821verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshare_prop,7822sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0);78237824if (strcmp(nfs_mnt_prop, "off") == 0 &&7825strcmp(smbshare_prop, "off") == 0) {7826(void) fprintf(stderr, gettext("cannot unshare "7827"'%s': legacy share\n"), path);7828(void) fprintf(stderr, gettext("use exportfs(8) "7829"or smbcontrol(1) to unshare this filesystem\n"));7830} else if (!zfs_is_shared(zhp, NULL, NULL)) {7831(void) fprintf(stderr, gettext("cannot unshare '%s': "7832"not currently shared\n"), path);7833} else {7834ret = zfs_unshare(zhp, path, NULL);7835zfs_commit_shares(NULL);7836}7837} else {7838char mtpt_prop[ZFS_MAXPROPLEN];78397840verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop,7841sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0);78427843if (is_manual) {7844ret = zfs_unmount(zhp, NULL, flags);7845} else if (strcmp(mtpt_prop, "legacy") == 0) {7846(void) fprintf(stderr, gettext("cannot unmount "7847"'%s': legacy mountpoint\n"),7848zfs_get_name(zhp));7849(void) fprintf(stderr, gettext("use umount(8) "7850"to unmount this filesystem\n"));7851} else {7852ret = zfs_unmountall(zhp, flags);7853}7854}78557856out:7857zfs_close(zhp);78587859return (ret != 0);7860}78617862/*7863* Generic callback for unsharing or unmounting a filesystem.7864*/7865static int7866unshare_unmount(int op, int argc, char **argv)7867{7868int do_all = 0;7869int flags = 0;7870int ret = 0;7871int c;7872zfs_handle_t *zhp;7873char nfs_mnt_prop[ZFS_MAXPROPLEN];7874char sharesmb[ZFS_MAXPROPLEN];78757876/* check options */7877while ((c = getopt(argc, argv, op == OP_SHARE ? ":a" : "afu")) != -1) {7878switch (c) {7879case 'a':7880do_all = 1;7881break;7882case 'f':7883flags |= MS_FORCE;7884break;7885case 'u':7886flags |= MS_CRYPT;7887break;7888case ':':7889(void) fprintf(stderr, gettext("missing argument for "7890"'%c' option\n"), optopt);7891usage(B_FALSE);7892break;7893case '?':7894(void) fprintf(stderr, gettext("invalid option '%c'\n"),7895optopt);7896usage(B_FALSE);7897}7898}78997900argc -= optind;7901argv += optind;79027903if (do_all) {7904/*7905* We could make use of zfs_for_each() to walk all datasets in7906* the system, but this would be very inefficient, especially7907* since we would have to linearly search /proc/self/mounts for7908* each one. Instead, do one pass through /proc/self/mounts7909* looking for zfs entries and call zfs_unmount() for each one.7910*7911* Things get a little tricky if the administrator has created7912* mountpoints beneath other ZFS filesystems. In this case, we7913* have to unmount the deepest filesystems first. To accomplish7914* this, we place all the mountpoints in an AVL tree sorted by7915* the special type (dataset name), and walk the result in7916* reverse to make sure to get any snapshots first.7917*/7918FILE *mnttab;7919struct mnttab entry;7920uu_avl_pool_t *pool;7921uu_avl_t *tree = NULL;7922unshare_unmount_node_t *node;7923uu_avl_index_t idx;7924uu_avl_walk_t *walk;7925enum sa_protocol *protocol = NULL,7926single_protocol[] = {SA_NO_PROTOCOL, SA_NO_PROTOCOL};79277928if (op == OP_SHARE && argc > 0) {7929*single_protocol = sa_protocol_decode(argv[0]);7930protocol = single_protocol;7931argc--;7932argv++;7933}79347935if (argc != 0) {7936(void) fprintf(stderr, gettext("too many arguments\n"));7937usage(B_FALSE);7938}79397940if (((pool = uu_avl_pool_create("unmount_pool",7941sizeof (unshare_unmount_node_t),7942offsetof(unshare_unmount_node_t, un_avlnode),7943unshare_unmount_compare, UU_DEFAULT)) == NULL) ||7944((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL))7945nomem();79467947if ((mnttab = fopen(MNTTAB, "re")) == NULL) {7948uu_avl_destroy(tree);7949uu_avl_pool_destroy(pool);7950return (ENOENT);7951}79527953while (getmntent(mnttab, &entry) == 0) {79547955/* ignore non-ZFS entries */7956if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)7957continue;79587959/* ignore snapshots */7960if (strchr(entry.mnt_special, '@') != NULL)7961continue;79627963if ((zhp = zfs_open(g_zfs, entry.mnt_special,7964ZFS_TYPE_FILESYSTEM)) == NULL) {7965ret = 1;7966continue;7967}79687969/*7970* Ignore datasets that are excluded/restricted by7971* parent pool name.7972*/7973if (zpool_skip_pool(zfs_get_pool_name(zhp))) {7974zfs_close(zhp);7975continue;7976}79777978switch (op) {7979case OP_SHARE:7980verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,7981nfs_mnt_prop,7982sizeof (nfs_mnt_prop),7983NULL, NULL, 0, B_FALSE) == 0);7984if (strcmp(nfs_mnt_prop, "off") != 0)7985break;7986verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,7987nfs_mnt_prop,7988sizeof (nfs_mnt_prop),7989NULL, NULL, 0, B_FALSE) == 0);7990if (strcmp(nfs_mnt_prop, "off") == 0)7991continue;7992break;7993case OP_MOUNT:7994/* Ignore legacy mounts */7995verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,7996nfs_mnt_prop,7997sizeof (nfs_mnt_prop),7998NULL, NULL, 0, B_FALSE) == 0);7999if (strcmp(nfs_mnt_prop, "legacy") == 0)8000continue;8001/* Ignore canmount=noauto mounts */8002if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) ==8003ZFS_CANMOUNT_NOAUTO)8004continue;8005break;8006default:8007break;8008}80098010node = safe_malloc(sizeof (unshare_unmount_node_t));8011node->un_zhp = zhp;8012node->un_mountp = safe_strdup(entry.mnt_mountp);80138014uu_avl_node_init(node, &node->un_avlnode, pool);80158016if (uu_avl_find(tree, node, NULL, &idx) == NULL) {8017uu_avl_insert(tree, node, idx);8018} else {8019zfs_close(node->un_zhp);8020free(node->un_mountp);8021free(node);8022}8023}8024(void) fclose(mnttab);80258026/*8027* Walk the AVL tree in reverse, unmounting each filesystem and8028* removing it from the AVL tree in the process.8029*/8030if ((walk = uu_avl_walk_start(tree,8031UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL)8032nomem();80338034while ((node = uu_avl_walk_next(walk)) != NULL) {8035const char *mntarg = NULL;80368037uu_avl_remove(tree, node);8038switch (op) {8039case OP_SHARE:8040if (zfs_unshare(node->un_zhp,8041node->un_mountp, protocol) != 0)8042ret = 1;8043break;80448045case OP_MOUNT:8046if (zfs_unmount(node->un_zhp,8047mntarg, flags) != 0)8048ret = 1;8049break;8050}80518052zfs_close(node->un_zhp);8053free(node->un_mountp);8054free(node);8055}80568057if (op == OP_SHARE)8058zfs_commit_shares(protocol);80598060uu_avl_walk_end(walk);8061uu_avl_destroy(tree);8062uu_avl_pool_destroy(pool);80638064} else {8065if (argc != 1) {8066if (argc == 0)8067(void) fprintf(stderr,8068gettext("missing filesystem argument\n"));8069else8070(void) fprintf(stderr,8071gettext("too many arguments\n"));8072usage(B_FALSE);8073}80748075/*8076* We have an argument, but it may be a full path or a ZFS8077* filesystem. Pass full paths off to unmount_path() (shared by8078* manual_unmount), otherwise open the filesystem and pass to8079* zfs_unmount().8080*/8081if (argv[0][0] == '/')8082return (unshare_unmount_path(op, argv[0],8083flags, B_FALSE));80848085if ((zhp = zfs_open(g_zfs, argv[0],8086ZFS_TYPE_FILESYSTEM)) == NULL)8087return (1);80888089verify(zfs_prop_get(zhp, op == OP_SHARE ?8090ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT,8091nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL,8092NULL, 0, B_FALSE) == 0);80938094switch (op) {8095case OP_SHARE:8096verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,8097nfs_mnt_prop,8098sizeof (nfs_mnt_prop),8099NULL, NULL, 0, B_FALSE) == 0);8100verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,8101sharesmb, sizeof (sharesmb), NULL, NULL,81020, B_FALSE) == 0);81038104if (strcmp(nfs_mnt_prop, "off") == 0 &&8105strcmp(sharesmb, "off") == 0) {8106(void) fprintf(stderr, gettext("cannot "8107"unshare '%s': legacy share\n"),8108zfs_get_name(zhp));8109(void) fprintf(stderr, gettext("use "8110"exports(5) or smb.conf(5) to unshare "8111"this filesystem\n"));8112ret = 1;8113} else if (!zfs_is_shared(zhp, NULL, NULL)) {8114(void) fprintf(stderr, gettext("cannot "8115"unshare '%s': not currently "8116"shared\n"), zfs_get_name(zhp));8117ret = 1;8118} else if (zfs_unshareall(zhp, NULL) != 0) {8119ret = 1;8120}8121break;81228123case OP_MOUNT:8124if (strcmp(nfs_mnt_prop, "legacy") == 0) {8125(void) fprintf(stderr, gettext("cannot "8126"unmount '%s': legacy "8127"mountpoint\n"), zfs_get_name(zhp));8128(void) fprintf(stderr, gettext("use "8129"umount(8) to unmount this "8130"filesystem\n"));8131ret = 1;8132} else if (!zfs_is_mounted(zhp, NULL)) {8133(void) fprintf(stderr, gettext("cannot "8134"unmount '%s': not currently "8135"mounted\n"),8136zfs_get_name(zhp));8137ret = 1;8138} else if (zfs_unmountall(zhp, flags) != 0) {8139ret = 1;8140}8141break;8142}81438144zfs_close(zhp);8145}81468147return (ret);8148}81498150/*8151* zfs unmount [-fu] -a8152* zfs unmount [-fu] filesystem8153*8154* Unmount all filesystems, or a specific ZFS filesystem.8155*/8156static int8157zfs_do_unmount(int argc, char **argv)8158{8159return (unshare_unmount(OP_MOUNT, argc, argv));8160}81618162/*8163* zfs unshare -a8164* zfs unshare filesystem8165*8166* Unshare all filesystems, or a specific ZFS filesystem.8167*/8168static int8169zfs_do_unshare(int argc, char **argv)8170{8171return (unshare_unmount(OP_SHARE, argc, argv));8172}81738174static int8175find_command_idx(const char *command, int *idx)8176{8177int i;81788179for (i = 0; i < NCOMMAND; i++) {8180if (command_table[i].name == NULL)8181continue;81828183if (strcmp(command, command_table[i].name) == 0) {8184*idx = i;8185return (0);8186}8187}8188return (1);8189}81908191static int8192zfs_do_diff(int argc, char **argv)8193{8194zfs_handle_t *zhp;8195int flags = 0;8196char *tosnap = NULL;8197char *fromsnap = NULL;8198char *atp, *copy;8199int err = 0;8200int c;8201struct sigaction sa;82028203while ((c = getopt(argc, argv, "FHth")) != -1) {8204switch (c) {8205case 'F':8206flags |= ZFS_DIFF_CLASSIFY;8207break;8208case 'H':8209flags |= ZFS_DIFF_PARSEABLE;8210break;8211case 't':8212flags |= ZFS_DIFF_TIMESTAMP;8213break;8214case 'h':8215flags |= ZFS_DIFF_NO_MANGLE;8216break;8217default:8218(void) fprintf(stderr,8219gettext("invalid option '%c'\n"), optopt);8220usage(B_FALSE);8221}8222}82238224argc -= optind;8225argv += optind;82268227if (argc < 1) {8228(void) fprintf(stderr,8229gettext("must provide at least one snapshot name\n"));8230usage(B_FALSE);8231}82328233if (argc > 2) {8234(void) fprintf(stderr, gettext("too many arguments\n"));8235usage(B_FALSE);8236}82378238fromsnap = argv[0];8239tosnap = (argc == 2) ? argv[1] : NULL;82408241copy = NULL;8242if (*fromsnap != '@')8243copy = strdup(fromsnap);8244else if (tosnap)8245copy = strdup(tosnap);8246if (copy == NULL)8247usage(B_FALSE);82488249if ((atp = strchr(copy, '@')) != NULL)8250*atp = '\0';82518252if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL) {8253free(copy);8254return (1);8255}8256free(copy);82578258/*8259* Ignore SIGPIPE so that the library can give us8260* information on any failure8261*/8262if (sigemptyset(&sa.sa_mask) == -1) {8263err = errno;8264goto out;8265}8266sa.sa_flags = 0;8267sa.sa_handler = SIG_IGN;8268if (sigaction(SIGPIPE, &sa, NULL) == -1) {8269err = errno;8270goto out;8271}82728273err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags);8274out:8275zfs_close(zhp);82768277return (err != 0);8278}82798280/*8281* zfs bookmark <fs@source>|<fs#source> <fs#bookmark>8282*8283* Creates a bookmark with the given name from the source snapshot8284* or creates a copy of an existing source bookmark.8285*/8286static int8287zfs_do_bookmark(int argc, char **argv)8288{8289char *source, *bookname;8290char expbuf[ZFS_MAX_DATASET_NAME_LEN];8291int source_type;8292nvlist_t *nvl;8293int ret = 0;8294int c;82958296/* check options */8297while ((c = getopt(argc, argv, "")) != -1) {8298switch (c) {8299case '?':8300(void) fprintf(stderr,8301gettext("invalid option '%c'\n"), optopt);8302goto usage;8303}8304}83058306argc -= optind;8307argv += optind;83088309/* check number of arguments */8310if (argc < 1) {8311(void) fprintf(stderr, gettext("missing source argument\n"));8312goto usage;8313}8314if (argc < 2) {8315(void) fprintf(stderr, gettext("missing bookmark argument\n"));8316goto usage;8317}83188319source = argv[0];8320bookname = argv[1];83218322if (strchr(source, '@') == NULL && strchr(source, '#') == NULL) {8323(void) fprintf(stderr,8324gettext("invalid source name '%s': "8325"must contain a '@' or '#'\n"), source);8326goto usage;8327}8328if (strchr(bookname, '#') == NULL) {8329(void) fprintf(stderr,8330gettext("invalid bookmark name '%s': "8331"must contain a '#'\n"), bookname);8332goto usage;8333}83348335/*8336* expand source or bookname to full path:8337* one of them may be specified as short name8338*/8339{8340char **expand;8341char *source_short, *bookname_short;8342source_short = strpbrk(source, "@#");8343bookname_short = strpbrk(bookname, "#");8344if (source_short == source &&8345bookname_short == bookname) {8346(void) fprintf(stderr, gettext(8347"either source or bookmark must be specified as "8348"full dataset paths"));8349goto usage;8350} else if (source_short != source &&8351bookname_short != bookname) {8352expand = NULL;8353} else if (source_short != source) {8354strlcpy(expbuf, source, sizeof (expbuf));8355expand = &bookname;8356} else if (bookname_short != bookname) {8357strlcpy(expbuf, bookname, sizeof (expbuf));8358expand = &source;8359} else {8360abort();8361}8362if (expand != NULL) {8363*strpbrk(expbuf, "@#") = '\0'; /* dataset name in buf */8364(void) strlcat(expbuf, *expand, sizeof (expbuf));8365*expand = expbuf;8366}8367}83688369/* determine source type */8370switch (*strpbrk(source, "@#")) {8371case '@': source_type = ZFS_TYPE_SNAPSHOT; break;8372case '#': source_type = ZFS_TYPE_BOOKMARK; break;8373default: abort();8374}83758376/* test the source exists */8377zfs_handle_t *zhp;8378zhp = zfs_open(g_zfs, source, source_type);8379if (zhp == NULL)8380goto usage;8381zfs_close(zhp);83828383nvl = fnvlist_alloc();8384fnvlist_add_string(nvl, bookname, source);8385ret = lzc_bookmark(nvl, NULL);8386fnvlist_free(nvl);83878388if (ret != 0) {8389const char *err_msg = NULL;8390char errbuf[1024];83918392(void) snprintf(errbuf, sizeof (errbuf),8393dgettext(TEXT_DOMAIN,8394"cannot create bookmark '%s'"), bookname);83958396switch (ret) {8397case EXDEV:8398err_msg = "bookmark is in a different pool";8399break;8400case ZFS_ERR_BOOKMARK_SOURCE_NOT_ANCESTOR:8401err_msg = "source is not an ancestor of the "8402"new bookmark's dataset";8403break;8404case EEXIST:8405err_msg = "bookmark exists";8406break;8407case EINVAL:8408err_msg = "invalid argument";8409break;8410case ENOTSUP:8411err_msg = "bookmark feature not enabled";8412break;8413case ENOSPC:8414err_msg = "out of space";8415break;8416case ENOENT:8417err_msg = "dataset does not exist";8418break;8419default:8420(void) zfs_standard_error(g_zfs, ret, errbuf);8421break;8422}8423if (err_msg != NULL) {8424(void) fprintf(stderr, "%s: %s\n", errbuf,8425dgettext(TEXT_DOMAIN, err_msg));8426}8427}84288429return (ret != 0);84308431usage:8432usage(B_FALSE);8433return (-1);8434}84358436static int8437zfs_do_channel_program(int argc, char **argv)8438{8439int ret, fd, c;8440size_t progsize, progread;8441nvlist_t *outnvl = NULL;8442uint64_t instrlimit = ZCP_DEFAULT_INSTRLIMIT;8443uint64_t memlimit = ZCP_DEFAULT_MEMLIMIT;8444boolean_t sync_flag = B_TRUE, json_output = B_FALSE;8445zpool_handle_t *zhp;84468447struct option long_options[] = {8448{"json", no_argument, NULL, 'j'},8449{0, 0, 0, 0}8450};84518452/* check options */8453while ((c = getopt_long(argc, argv, "nt:m:j", long_options,8454NULL)) != -1) {8455switch (c) {8456case 't':8457case 'm': {8458uint64_t arg;8459char *endp;84608461errno = 0;8462arg = strtoull(optarg, &endp, 0);8463if (errno != 0 || *endp != '\0') {8464(void) fprintf(stderr, gettext(8465"invalid argument "8466"'%s': expected integer\n"), optarg);8467goto usage;8468}84698470if (c == 't') {8471instrlimit = arg;8472} else {8473ASSERT3U(c, ==, 'm');8474memlimit = arg;8475}8476break;8477}8478case 'n': {8479sync_flag = B_FALSE;8480break;8481}8482case 'j': {8483json_output = B_TRUE;8484break;8485}8486case '?':8487(void) fprintf(stderr, gettext("invalid option '%c'\n"),8488optopt);8489goto usage;8490}8491}84928493argc -= optind;8494argv += optind;84958496if (argc < 2) {8497(void) fprintf(stderr,8498gettext("invalid number of arguments\n"));8499goto usage;8500}85018502const char *poolname = argv[0];8503const char *filename = argv[1];8504if (strcmp(filename, "-") == 0) {8505fd = 0;8506filename = "standard input";8507} else if ((fd = open(filename, O_RDONLY)) < 0) {8508(void) fprintf(stderr, gettext("cannot open '%s': %s\n"),8509filename, strerror(errno));8510return (1);8511}85128513if ((zhp = zpool_open(g_zfs, poolname)) == NULL) {8514(void) fprintf(stderr, gettext("cannot open pool '%s'\n"),8515poolname);8516if (fd != 0)8517(void) close(fd);8518return (1);8519}8520zpool_close(zhp);85218522/*8523* Read in the channel program, expanding the program buffer as8524* necessary.8525*/8526progread = 0;8527progsize = 1024;8528char *progbuf = safe_malloc(progsize);8529do {8530ret = read(fd, progbuf + progread, progsize - progread);8531progread += ret;8532if (progread == progsize && ret > 0) {8533progsize *= 2;8534progbuf = safe_realloc(progbuf, progsize);8535}8536} while (ret > 0);85378538if (fd != 0)8539(void) close(fd);8540if (ret < 0) {8541free(progbuf);8542(void) fprintf(stderr,8543gettext("cannot read '%s': %s\n"),8544filename, strerror(errno));8545return (1);8546}8547progbuf[progread] = '\0';85488549/*8550* Any remaining arguments are passed as arguments to the lua script as8551* a string array:8552* {8553* "argv" -> [ "arg 1", ... "arg n" ],8554* }8555*/8556nvlist_t *argnvl = fnvlist_alloc();8557fnvlist_add_string_array(argnvl, ZCP_ARG_CLIARGV,8558(const char **)argv + 2, argc - 2);85598560if (sync_flag) {8561ret = lzc_channel_program(poolname, progbuf,8562instrlimit, memlimit, argnvl, &outnvl);8563} else {8564ret = lzc_channel_program_nosync(poolname, progbuf,8565instrlimit, memlimit, argnvl, &outnvl);8566}85678568if (ret != 0) {8569/*8570* On error, report the error message handed back by lua if one8571* exists. Otherwise, generate an appropriate error message,8572* falling back on strerror() for an unexpected return code.8573*/8574const char *errstring = NULL;8575const char *msg = gettext("Channel program execution failed");8576uint64_t instructions = 0;8577if (outnvl != NULL && nvlist_exists(outnvl, ZCP_RET_ERROR)) {8578const char *es = NULL;8579(void) nvlist_lookup_string(outnvl,8580ZCP_RET_ERROR, &es);8581if (es == NULL)8582errstring = strerror(ret);8583else8584errstring = es;8585if (ret == ETIME) {8586(void) nvlist_lookup_uint64(outnvl,8587ZCP_ARG_INSTRLIMIT, &instructions);8588}8589} else {8590switch (ret) {8591case EINVAL:8592errstring =8593"Invalid instruction or memory limit.";8594break;8595case ENOMEM:8596errstring = "Return value too large.";8597break;8598case ENOSPC:8599errstring = "Memory limit exhausted.";8600break;8601case ETIME:8602errstring = "Timed out.";8603break;8604case EPERM:8605errstring = "Permission denied. Channel "8606"programs must be run as root.";8607break;8608default:8609(void) zfs_standard_error(g_zfs, ret, msg);8610}8611}8612if (errstring != NULL)8613(void) fprintf(stderr, "%s:\n%s\n", msg, errstring);86148615if (ret == ETIME && instructions != 0)8616(void) fprintf(stderr,8617gettext("%llu Lua instructions\n"),8618(u_longlong_t)instructions);8619} else {8620if (json_output) {8621(void) nvlist_print_json(stdout, outnvl);8622} else if (nvlist_empty(outnvl)) {8623(void) fprintf(stdout, gettext("Channel program fully "8624"executed and did not produce output.\n"));8625} else {8626(void) fprintf(stdout, gettext("Channel program fully "8627"executed and produced output:\n"));8628dump_nvlist(outnvl, 4);8629}8630}86318632free(progbuf);8633fnvlist_free(outnvl);8634fnvlist_free(argnvl);8635return (ret != 0);86368637usage:8638usage(B_FALSE);8639return (-1);8640}864186428643typedef struct loadkey_cbdata {8644boolean_t cb_loadkey;8645boolean_t cb_recursive;8646boolean_t cb_noop;8647char *cb_keylocation;8648uint64_t cb_numfailed;8649uint64_t cb_numattempted;8650} loadkey_cbdata_t;86518652static int8653load_key_callback(zfs_handle_t *zhp, void *data)8654{8655int ret;8656boolean_t is_encroot;8657loadkey_cbdata_t *cb = data;8658uint64_t keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);86598660/*8661* If we are working recursively, we want to skip loading / unloading8662* keys for non-encryption roots and datasets whose keys are already8663* in the desired end-state.8664*/8665if (cb->cb_recursive) {8666ret = zfs_crypto_get_encryption_root(zhp, &is_encroot, NULL);8667if (ret != 0)8668return (ret);8669if (!is_encroot)8670return (0);86718672if ((cb->cb_loadkey && keystatus == ZFS_KEYSTATUS_AVAILABLE) ||8673(!cb->cb_loadkey && keystatus == ZFS_KEYSTATUS_UNAVAILABLE))8674return (0);8675}86768677cb->cb_numattempted++;86788679if (cb->cb_loadkey)8680ret = zfs_crypto_load_key(zhp, cb->cb_noop, cb->cb_keylocation);8681else8682ret = zfs_crypto_unload_key(zhp);86838684if (ret != 0) {8685cb->cb_numfailed++;8686return (ret);8687}86888689return (0);8690}86918692static int8693load_unload_keys(int argc, char **argv, boolean_t loadkey)8694{8695int c, ret = 0, flags = 0;8696boolean_t do_all = B_FALSE;8697loadkey_cbdata_t cb = { 0 };86988699cb.cb_loadkey = loadkey;87008701while ((c = getopt(argc, argv, "anrL:")) != -1) {8702/* noop and alternate keylocations only apply to zfs load-key */8703if (loadkey) {8704switch (c) {8705case 'n':8706cb.cb_noop = B_TRUE;8707continue;8708case 'L':8709cb.cb_keylocation = optarg;8710continue;8711default:8712break;8713}8714}87158716switch (c) {8717case 'a':8718do_all = B_TRUE;8719cb.cb_recursive = B_TRUE;8720break;8721case 'r':8722flags |= ZFS_ITER_RECURSE;8723cb.cb_recursive = B_TRUE;8724break;8725default:8726(void) fprintf(stderr,8727gettext("invalid option '%c'\n"), optopt);8728usage(B_FALSE);8729}8730}87318732argc -= optind;8733argv += optind;87348735if (!do_all && argc == 0) {8736(void) fprintf(stderr,8737gettext("Missing dataset argument or -a option\n"));8738usage(B_FALSE);8739}87408741if (do_all && argc != 0) {8742(void) fprintf(stderr,8743gettext("Cannot specify dataset with -a option\n"));8744usage(B_FALSE);8745}87468747if (cb.cb_recursive && cb.cb_keylocation != NULL &&8748strcmp(cb.cb_keylocation, "prompt") != 0) {8749(void) fprintf(stderr, gettext("alternate keylocation may only "8750"be 'prompt' with -r or -a\n"));8751usage(B_FALSE);8752}87538754ret = zfs_for_each(argc, argv, flags,8755ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL, 0,8756load_key_callback, &cb);87578758if (cb.cb_noop || (cb.cb_recursive && cb.cb_numattempted != 0)) {8759(void) printf(gettext("%llu / %llu key(s) successfully %s\n"),8760(u_longlong_t)(cb.cb_numattempted - cb.cb_numfailed),8761(u_longlong_t)cb.cb_numattempted,8762loadkey ? (cb.cb_noop ? "verified" : "loaded") :8763"unloaded");8764}87658766if (cb.cb_numfailed != 0)8767ret = -1;87688769return (ret);8770}87718772static int8773zfs_do_load_key(int argc, char **argv)8774{8775return (load_unload_keys(argc, argv, B_TRUE));8776}877787788779static int8780zfs_do_unload_key(int argc, char **argv)8781{8782return (load_unload_keys(argc, argv, B_FALSE));8783}87848785static int8786zfs_do_change_key(int argc, char **argv)8787{8788int c, ret;8789uint64_t keystatus;8790boolean_t loadkey = B_FALSE, inheritkey = B_FALSE;8791zfs_handle_t *zhp = NULL;8792nvlist_t *props = fnvlist_alloc();87938794while ((c = getopt(argc, argv, "lio:")) != -1) {8795switch (c) {8796case 'l':8797loadkey = B_TRUE;8798break;8799case 'i':8800inheritkey = B_TRUE;8801break;8802case 'o':8803if (!parseprop(props, optarg)) {8804nvlist_free(props);8805return (1);8806}8807break;8808default:8809(void) fprintf(stderr,8810gettext("invalid option '%c'\n"), optopt);8811usage(B_FALSE);8812}8813}88148815if (inheritkey && !nvlist_empty(props)) {8816(void) fprintf(stderr,8817gettext("Properties not allowed for inheriting\n"));8818usage(B_FALSE);8819}88208821argc -= optind;8822argv += optind;88238824if (argc < 1) {8825(void) fprintf(stderr, gettext("Missing dataset argument\n"));8826usage(B_FALSE);8827}88288829if (argc > 1) {8830(void) fprintf(stderr, gettext("Too many arguments\n"));8831usage(B_FALSE);8832}88338834zhp = zfs_open(g_zfs, argv[argc - 1],8835ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);8836if (zhp == NULL)8837usage(B_FALSE);88388839if (loadkey) {8840keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);8841if (keystatus != ZFS_KEYSTATUS_AVAILABLE) {8842ret = zfs_crypto_load_key(zhp, B_FALSE, NULL);8843if (ret != 0) {8844nvlist_free(props);8845zfs_close(zhp);8846return (-1);8847}8848}88498850/* refresh the properties so the new keystatus is visible */8851zfs_refresh_properties(zhp);8852}88538854ret = zfs_crypto_rewrap(zhp, props, inheritkey);8855if (ret != 0) {8856nvlist_free(props);8857zfs_close(zhp);8858return (-1);8859}88608861nvlist_free(props);8862zfs_close(zhp);8863return (0);8864}88658866/*8867* 1) zfs project [-d|-r] <file|directory ...>8868* List project ID and inherit flag of file(s) or directories.8869* -d: List the directory itself, not its children.8870* -r: List subdirectories recursively.8871*8872* 2) zfs project -C [-k] [-r] <file|directory ...>8873* Clear project inherit flag and/or ID on the file(s) or directories.8874* -k: Keep the project ID unchanged. If not specified, the project ID8875* will be reset as zero.8876* -r: Clear on subdirectories recursively.8877*8878* 3) zfs project -c [-0] [-d|-r] [-p id] <file|directory ...>8879* Check project ID and inherit flag on the file(s) or directories,8880* report the outliers.8881* -0: Print file name followed by a NUL instead of newline.8882* -d: Check the directory itself, not its children.8883* -p: Specify the referenced ID for comparing with the target file(s)8884* or directories' project IDs. If not specified, the target (top)8885* directory's project ID will be used as the referenced one.8886* -r: Check subdirectories recursively.8887*8888* 4) zfs project [-p id] [-r] [-s] <file|directory ...>8889* Set project ID and/or inherit flag on the file(s) or directories.8890* -p: Set the project ID as the given id.8891* -r: Set on subdirectories recursively. If not specify "-p" option,8892* it will use top-level directory's project ID as the given id,8893* then set both project ID and inherit flag on all descendants8894* of the top-level directory.8895* -s: Set project inherit flag.8896*/8897static int8898zfs_do_project(int argc, char **argv)8899{8900zfs_project_control_t zpc = {8901.zpc_expected_projid = ZFS_INVALID_PROJID,8902.zpc_op = ZFS_PROJECT_OP_DEFAULT,8903.zpc_dironly = B_FALSE,8904.zpc_keep_projid = B_FALSE,8905.zpc_newline = B_TRUE,8906.zpc_recursive = B_FALSE,8907.zpc_set_flag = B_FALSE,8908};8909int ret = 0, c;89108911if (argc < 2)8912usage(B_FALSE);89138914while ((c = getopt(argc, argv, "0Ccdkp:rs")) != -1) {8915switch (c) {8916case '0':8917zpc.zpc_newline = B_FALSE;8918break;8919case 'C':8920if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {8921(void) fprintf(stderr, gettext("cannot "8922"specify '-C' '-c' '-s' together\n"));8923usage(B_FALSE);8924}89258926zpc.zpc_op = ZFS_PROJECT_OP_CLEAR;8927break;8928case 'c':8929if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {8930(void) fprintf(stderr, gettext("cannot "8931"specify '-C' '-c' '-s' together\n"));8932usage(B_FALSE);8933}89348935zpc.zpc_op = ZFS_PROJECT_OP_CHECK;8936break;8937case 'd':8938zpc.zpc_dironly = B_TRUE;8939/* overwrite "-r" option */8940zpc.zpc_recursive = B_FALSE;8941break;8942case 'k':8943zpc.zpc_keep_projid = B_TRUE;8944break;8945case 'p': {8946char *endptr;89478948errno = 0;8949zpc.zpc_expected_projid = strtoull(optarg, &endptr, 0);8950if (errno != 0 || *endptr != '\0') {8951(void) fprintf(stderr,8952gettext("project ID must be less than "8953"%u\n"), UINT32_MAX);8954usage(B_FALSE);8955}8956if (zpc.zpc_expected_projid >= UINT32_MAX) {8957(void) fprintf(stderr,8958gettext("invalid project ID\n"));8959usage(B_FALSE);8960}8961break;8962}8963case 'r':8964zpc.zpc_recursive = B_TRUE;8965/* overwrite "-d" option */8966zpc.zpc_dironly = B_FALSE;8967break;8968case 's':8969if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {8970(void) fprintf(stderr, gettext("cannot "8971"specify '-C' '-c' '-s' together\n"));8972usage(B_FALSE);8973}89748975zpc.zpc_set_flag = B_TRUE;8976zpc.zpc_op = ZFS_PROJECT_OP_SET;8977break;8978default:8979(void) fprintf(stderr, gettext("invalid option '%c'\n"),8980optopt);8981usage(B_FALSE);8982}8983}89848985if (zpc.zpc_op == ZFS_PROJECT_OP_DEFAULT) {8986if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID)8987zpc.zpc_op = ZFS_PROJECT_OP_SET;8988else8989zpc.zpc_op = ZFS_PROJECT_OP_LIST;8990}89918992switch (zpc.zpc_op) {8993case ZFS_PROJECT_OP_LIST:8994if (zpc.zpc_keep_projid) {8995(void) fprintf(stderr,8996gettext("'-k' is only valid together with '-C'\n"));8997usage(B_FALSE);8998}8999if (!zpc.zpc_newline) {9000(void) fprintf(stderr,9001gettext("'-0' is only valid together with '-c'\n"));9002usage(B_FALSE);9003}9004break;9005case ZFS_PROJECT_OP_CHECK:9006if (zpc.zpc_keep_projid) {9007(void) fprintf(stderr,9008gettext("'-k' is only valid together with '-C'\n"));9009usage(B_FALSE);9010}9011break;9012case ZFS_PROJECT_OP_CLEAR:9013if (zpc.zpc_dironly) {9014(void) fprintf(stderr,9015gettext("'-d' is useless together with '-C'\n"));9016usage(B_FALSE);9017}9018if (!zpc.zpc_newline) {9019(void) fprintf(stderr,9020gettext("'-0' is only valid together with '-c'\n"));9021usage(B_FALSE);9022}9023if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID) {9024(void) fprintf(stderr,9025gettext("'-p' is useless together with '-C'\n"));9026usage(B_FALSE);9027}9028break;9029case ZFS_PROJECT_OP_SET:9030if (zpc.zpc_dironly) {9031(void) fprintf(stderr,9032gettext("'-d' is useless for set project ID and/or "9033"inherit flag\n"));9034usage(B_FALSE);9035}9036if (zpc.zpc_keep_projid) {9037(void) fprintf(stderr,9038gettext("'-k' is only valid together with '-C'\n"));9039usage(B_FALSE);9040}9041if (!zpc.zpc_newline) {9042(void) fprintf(stderr,9043gettext("'-0' is only valid together with '-c'\n"));9044usage(B_FALSE);9045}9046break;9047default:9048ASSERT(0);9049break;9050}90519052argv += optind;9053argc -= optind;9054if (argc == 0) {9055(void) fprintf(stderr,9056gettext("missing file or directory target(s)\n"));9057usage(B_FALSE);9058}90599060for (int i = 0; i < argc; i++) {9061int err;90629063err = zfs_project_handle(argv[i], &zpc);9064if (err && !ret)9065ret = err;9066}90679068return (ret);9069}90709071static int9072zfs_rewrite_file(const char *path, boolean_t verbose, zfs_rewrite_args_t *args)9073{9074int fd, ret = 0;90759076fd = open(path, O_WRONLY);9077if (fd < 0) {9078ret = errno;9079(void) fprintf(stderr, gettext("failed to open %s: %s\n"),9080path, strerror(errno));9081return (ret);9082}90839084if (ioctl(fd, ZFS_IOC_REWRITE, args) < 0) {9085ret = errno;9086(void) fprintf(stderr, gettext("failed to rewrite %s: %s\n"),9087path, strerror(errno));9088} else if (verbose) {9089printf("%s\n", path);9090}90919092close(fd);9093return (ret);9094}90959096static int9097zfs_rewrite_dir(const char *path, boolean_t verbose, boolean_t xdev, dev_t dev,9098zfs_rewrite_args_t *args, nvlist_t *dirs)9099{9100struct dirent *ent;9101DIR *dir;9102int ret = 0, err;91039104dir = opendir(path);9105if (dir == NULL) {9106if (errno == ENOENT)9107return (0);9108ret = errno;9109(void) fprintf(stderr, gettext("failed to opendir %s: %s\n"),9110path, strerror(errno));9111return (ret);9112}91139114size_t plen = strlen(path) + 1;9115while ((ent = readdir(dir)) != NULL) {9116char *fullname;9117struct stat st;91189119if (ent->d_type != DT_REG && ent->d_type != DT_DIR)9120continue;91219122if (strcmp(ent->d_name, ".") == 0 ||9123strcmp(ent->d_name, "..") == 0)9124continue;91259126if (plen + strlen(ent->d_name) >= PATH_MAX) {9127(void) fprintf(stderr, gettext("path too long %s/%s\n"),9128path, ent->d_name);9129ret = ENAMETOOLONG;9130continue;9131}91329133if (asprintf(&fullname, "%s/%s", path, ent->d_name) == -1) {9134(void) fprintf(stderr,9135gettext("failed to allocate memory\n"));9136ret = ENOMEM;9137continue;9138}91399140if (xdev) {9141if (lstat(fullname, &st) < 0) {9142ret = errno;9143(void) fprintf(stderr,9144gettext("failed to stat %s: %s\n"),9145fullname, strerror(errno));9146free(fullname);9147continue;9148}9149if (st.st_dev != dev) {9150free(fullname);9151continue;9152}9153}91549155if (ent->d_type == DT_REG) {9156err = zfs_rewrite_file(fullname, verbose, args);9157if (err)9158ret = err;9159} else { /* DT_DIR */9160fnvlist_add_uint64(dirs, fullname, dev);9161}91629163free(fullname);9164}91659166closedir(dir);9167return (ret);9168}91699170static int9171zfs_rewrite_path(const char *path, boolean_t verbose, boolean_t recurse,9172boolean_t xdev, zfs_rewrite_args_t *args, nvlist_t *dirs)9173{9174struct stat st;9175int ret = 0;91769177if (lstat(path, &st) < 0) {9178ret = errno;9179(void) fprintf(stderr, gettext("failed to stat %s: %s\n"),9180path, strerror(errno));9181return (ret);9182}91839184if (S_ISREG(st.st_mode)) {9185ret = zfs_rewrite_file(path, verbose, args);9186} else if (S_ISDIR(st.st_mode) && recurse) {9187ret = zfs_rewrite_dir(path, verbose, xdev, st.st_dev, args,9188dirs);9189}9190return (ret);9191}91929193static int9194zfs_do_rewrite(int argc, char **argv)9195{9196int ret = 0, err, c;9197boolean_t recurse = B_FALSE, verbose = B_FALSE, xdev = B_FALSE;91989199if (argc < 2)9200usage(B_FALSE);92019202zfs_rewrite_args_t args;9203memset(&args, 0, sizeof (args));92049205while ((c = getopt(argc, argv, "Pl:o:rvx")) != -1) {9206switch (c) {9207case 'P':9208args.flags |= ZFS_REWRITE_PHYSICAL;9209break;9210case 'l':9211args.len = strtoll(optarg, NULL, 0);9212break;9213case 'o':9214args.off = strtoll(optarg, NULL, 0);9215break;9216case 'r':9217recurse = B_TRUE;9218break;9219case 'v':9220verbose = B_TRUE;9221break;9222case 'x':9223xdev = B_TRUE;9224break;9225default:9226(void) fprintf(stderr, gettext("invalid option '%c'\n"),9227optopt);9228usage(B_FALSE);9229}9230}92319232argv += optind;9233argc -= optind;9234if (argc == 0) {9235(void) fprintf(stderr,9236gettext("missing file or directory target(s)\n"));9237usage(B_FALSE);9238}92399240nvlist_t *dirs = fnvlist_alloc();9241for (int i = 0; i < argc; i++) {9242err = zfs_rewrite_path(argv[i], verbose, recurse, xdev, &args,9243dirs);9244if (err)9245ret = err;9246}9247nvpair_t *dir;9248while ((dir = nvlist_next_nvpair(dirs, NULL)) != NULL) {9249err = zfs_rewrite_dir(nvpair_name(dir), verbose, xdev,9250fnvpair_value_uint64(dir), &args, dirs);9251if (err)9252ret = err;9253fnvlist_remove_nvpair(dirs, dir);9254}9255fnvlist_free(dirs);92569257return (ret);9258}92599260static int9261zfs_do_wait(int argc, char **argv)9262{9263boolean_t enabled[ZFS_WAIT_NUM_ACTIVITIES];9264int error = 0, i;9265int c;92669267/* By default, wait for all types of activity. */9268for (i = 0; i < ZFS_WAIT_NUM_ACTIVITIES; i++)9269enabled[i] = B_TRUE;92709271while ((c = getopt(argc, argv, "t:")) != -1) {9272switch (c) {9273case 't':9274/* Reset activities array */9275memset(&enabled, 0, sizeof (enabled));92769277for (char *tok; (tok = strsep(&optarg, ",")); ) {9278static const char *const col_subopts[9279ZFS_WAIT_NUM_ACTIVITIES] = { "deleteq" };92809281for (i = 0; i < ARRAY_SIZE(col_subopts); ++i)9282if (strcmp(tok, col_subopts[i]) == 0) {9283enabled[i] = B_TRUE;9284goto found;9285}92869287(void) fprintf(stderr,9288gettext("invalid activity '%s'\n"), tok);9289usage(B_FALSE);9290found:;9291}9292break;9293case '?':9294(void) fprintf(stderr, gettext("invalid option '%c'\n"),9295optopt);9296usage(B_FALSE);9297}9298}92999300argv += optind;9301argc -= optind;9302if (argc < 1) {9303(void) fprintf(stderr, gettext("missing 'filesystem' "9304"argument\n"));9305usage(B_FALSE);9306}9307if (argc > 1) {9308(void) fprintf(stderr, gettext("too many arguments\n"));9309usage(B_FALSE);9310}93119312zfs_handle_t *zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM);9313if (zhp == NULL)9314return (1);93159316for (;;) {9317boolean_t missing = B_FALSE;9318boolean_t any_waited = B_FALSE;93199320for (int i = 0; i < ZFS_WAIT_NUM_ACTIVITIES; i++) {9321boolean_t waited;93229323if (!enabled[i])9324continue;93259326error = zfs_wait_status(zhp, i, &missing, &waited);9327if (error != 0 || missing)9328break;93299330any_waited = (any_waited || waited);9331}93329333if (error != 0 || missing || !any_waited)9334break;9335}93369337zfs_close(zhp);93389339return (error);9340}93419342/*9343* Display version message9344*/9345static int9346zfs_do_version(int argc, char **argv)9347{9348int c;9349nvlist_t *jsobj = NULL, *zfs_ver = NULL;9350boolean_t json = B_FALSE;93519352struct option long_options[] = {9353{"json", no_argument, NULL, 'j'},9354{0, 0, 0, 0}9355};93569357while ((c = getopt_long(argc, argv, "j", long_options, NULL)) != -1) {9358switch (c) {9359case 'j':9360json = B_TRUE;9361jsobj = zfs_json_schema(0, 1);9362break;9363case '?':9364(void) fprintf(stderr, gettext("invalid option '%c'\n"),9365optopt);9366usage(B_FALSE);9367}9368}93699370argc -= optind;9371if (argc != 0) {9372(void) fprintf(stderr, "too many arguments\n");9373usage(B_FALSE);9374}93759376if (json) {9377zfs_ver = zfs_version_nvlist();9378if (zfs_ver) {9379fnvlist_add_nvlist(jsobj, "zfs_version", zfs_ver);9380zcmd_print_json(jsobj);9381fnvlist_free(zfs_ver);9382return (0);9383} else9384return (-1);9385} else9386return (zfs_version_print() != 0);9387}93889389/* Display documentation */9390static int9391zfs_do_help(int argc, char **argv)9392{9393char page[MAXNAMELEN];9394if (argc < 3 || strcmp(argv[2], "zfs") == 0)9395strcpy(page, "zfs");9396else if (strcmp(argv[2], "concepts") == 0 ||9397strcmp(argv[2], "props") == 0)9398snprintf(page, sizeof (page), "zfs%s", argv[2]);9399else9400snprintf(page, sizeof (page), "zfs-%s", argv[2]);94019402execlp("man", "man", page, NULL);94039404fprintf(stderr, "couldn't run man program: %s", strerror(errno));9405return (-1);9406}94079408int9409main(int argc, char **argv)9410{9411int ret = 0;9412int i = 0;9413const char *cmdname;9414char **newargv;94159416(void) setlocale(LC_ALL, "");9417(void) setlocale(LC_NUMERIC, "C");9418(void) textdomain(TEXT_DOMAIN);94199420opterr = 0;94219422/*9423* Make sure the user has specified some command.9424*/9425if (argc < 2) {9426(void) fprintf(stderr, gettext("missing command\n"));9427usage(B_FALSE);9428}94299430cmdname = argv[1];94319432/*9433* The 'umount' command is an alias for 'unmount'9434*/9435if (strcmp(cmdname, "umount") == 0)9436cmdname = "unmount";94379438/*9439* The 'recv' command is an alias for 'receive'9440*/9441if (strcmp(cmdname, "recv") == 0)9442cmdname = "receive";94439444/*9445* The 'snap' command is an alias for 'snapshot'9446*/9447if (strcmp(cmdname, "snap") == 0)9448cmdname = "snapshot";94499450/*9451* Special case '-?'9452*/9453if ((strcmp(cmdname, "-?") == 0) ||9454(strcmp(cmdname, "--help") == 0))9455usage(B_TRUE);94569457/*9458* Special case '-V|--version'9459*/9460if ((strcmp(cmdname, "-V") == 0) || (strcmp(cmdname, "--version") == 0))9461return (zfs_version_print() != 0);94629463/*9464* Special case 'help'9465*/9466if (strcmp(cmdname, "help") == 0)9467return (zfs_do_help(argc, argv));94689469if ((g_zfs = libzfs_init()) == NULL) {9470(void) fprintf(stderr, "%s\n", libzfs_error_init(errno));9471return (1);9472}94739474zfs_save_arguments(argc, argv, history_str, sizeof (history_str));94759476libzfs_print_on_error(g_zfs, B_TRUE);94779478zfs_setproctitle_init(argc, argv, environ);94799480/*9481* Many commands modify input strings for string parsing reasons.9482* We create a copy to protect the original argv.9483*/9484newargv = safe_malloc((argc + 1) * sizeof (newargv[0]));9485for (i = 0; i < argc; i++)9486newargv[i] = strdup(argv[i]);9487newargv[argc] = NULL;94889489/*9490* Run the appropriate command.9491*/9492libzfs_mnttab_cache(g_zfs, B_TRUE);9493if (find_command_idx(cmdname, &i) == 0) {9494current_command = &command_table[i];9495ret = command_table[i].func(argc - 1, newargv + 1);9496} else if (strchr(cmdname, '=') != NULL) {9497verify(find_command_idx("set", &i) == 0);9498current_command = &command_table[i];9499ret = command_table[i].func(argc, newargv);9500} else {9501(void) fprintf(stderr, gettext("unrecognized "9502"command '%s'\n"), cmdname);9503usage(B_FALSE);9504ret = 1;9505}95069507for (i = 0; i < argc; i++)9508free(newargv[i]);9509free(newargv);95109511if (ret == 0 && log_history)9512(void) zpool_log_history(g_zfs, history_str);95139514libzfs_fini(g_zfs);95159516/*9517* The 'ZFS_ABORT' environment variable causes us to dump core on exit9518* for the purposes of running ::findleaks.9519*/9520if (getenv("ZFS_ABORT") != NULL) {9521(void) printf("dumping core by request\n");9522abort();9523}95249525return (ret);9526}95279528/*9529* zfs zone nsfile filesystem9530*9531* Add or delete the given dataset to/from the namespace.9532*/9533#ifdef __linux__9534static int9535zfs_do_zone_impl(int argc, char **argv, boolean_t attach)9536{9537zfs_handle_t *zhp;9538int ret;95399540if (argc < 3) {9541(void) fprintf(stderr, gettext("missing argument(s)\n"));9542usage(B_FALSE);9543}9544if (argc > 3) {9545(void) fprintf(stderr, gettext("too many arguments\n"));9546usage(B_FALSE);9547}95489549zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM);9550if (zhp == NULL)9551return (1);95529553ret = (zfs_userns(zhp, argv[1], attach) != 0);95549555zfs_close(zhp);9556return (ret);9557}95589559static int9560zfs_do_zone(int argc, char **argv)9561{9562return (zfs_do_zone_impl(argc, argv, B_TRUE));9563}95649565static int9566zfs_do_unzone(int argc, char **argv)9567{9568return (zfs_do_zone_impl(argc, argv, B_FALSE));9569}9570#endif95719572#ifdef __FreeBSD__9573#include <sys/jail.h>9574#include <jail.h>9575/*9576* Attach/detach the given dataset to/from the given jail9577*/9578static int9579zfs_do_jail_impl(int argc, char **argv, boolean_t attach)9580{9581zfs_handle_t *zhp;9582int jailid, ret;95839584/* check number of arguments */9585if (argc < 3) {9586(void) fprintf(stderr, gettext("missing argument(s)\n"));9587usage(B_FALSE);9588}9589if (argc > 3) {9590(void) fprintf(stderr, gettext("too many arguments\n"));9591usage(B_FALSE);9592}95939594jailid = jail_getid(argv[1]);9595if (jailid < 0) {9596(void) fprintf(stderr, gettext("invalid jail id or name\n"));9597usage(B_FALSE);9598}95999600zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM);9601if (zhp == NULL)9602return (1);96039604ret = (zfs_jail(zhp, jailid, attach) != 0);96059606zfs_close(zhp);9607return (ret);9608}96099610/*9611* zfs jail jailid filesystem9612*9613* Attach the given dataset to the given jail9614*/9615static int9616zfs_do_jail(int argc, char **argv)9617{9618return (zfs_do_jail_impl(argc, argv, B_TRUE));9619}96209621/*9622* zfs unjail jailid filesystem9623*9624* Detach the given dataset from the given jail9625*/9626static int9627zfs_do_unjail(int argc, char **argv)9628{9629return (zfs_do_jail_impl(argc, argv, B_FALSE));9630}9631#endif963296339634