Path: blob/main/sys/contrib/openzfs/cmd/zfs/zfs_main.c
108884 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 <libnvpair.h>45#include <locale.h>46#include <stddef.h>47#include <stdio.h>48#include <stdlib.h>49#include <string.h>50#include <unistd.h>51#include <fcntl.h>52#include <zone.h>53#include <grp.h>54#include <pwd.h>55#include <umem.h>56#include <pthread.h>57#include <signal.h>58#include <sys/list.h>59#include <sys/mkdev.h>60#include <sys/mntent.h>61#include <sys/mnttab.h>62#include <sys/mount.h>63#include <sys/stat.h>64#include <sys/fs/zfs.h>65#include <sys/systeminfo.h>66#include <sys/types.h>67#include <time.h>68#include <sys/zfs_project.h>6970#include <libzfs.h>71#include <libzfs_core.h>72#include <zfs_prop.h>73#include <zfs_deleg.h>74#include <libzutil.h>75#ifdef HAVE_IDMAP76#include <aclutils.h>77#include <directory.h>78#endif /* HAVE_IDMAP */7980#include "zfs_iter.h"81#include "zfs_util.h"82#include "zfs_comutil.h"83#include "zfs_projectutil.h"8485libzfs_handle_t *g_zfs;8687static char history_str[HIS_MAX_RECORD_LEN];88static boolean_t log_history = B_TRUE;8990static int zfs_do_clone(int argc, char **argv);91static int zfs_do_create(int argc, char **argv);92static int zfs_do_destroy(int argc, char **argv);93static int zfs_do_get(int argc, char **argv);94static int zfs_do_inherit(int argc, char **argv);95static int zfs_do_list(int argc, char **argv);96static int zfs_do_mount(int argc, char **argv);97static int zfs_do_rename(int argc, char **argv);98static int zfs_do_rollback(int argc, char **argv);99static int zfs_do_set(int argc, char **argv);100static int zfs_do_upgrade(int argc, char **argv);101static int zfs_do_snapshot(int argc, char **argv);102static int zfs_do_unmount(int argc, char **argv);103static int zfs_do_share(int argc, char **argv);104static int zfs_do_unshare(int argc, char **argv);105static int zfs_do_send(int argc, char **argv);106static int zfs_do_receive(int argc, char **argv);107static int zfs_do_promote(int argc, char **argv);108static int zfs_do_userspace(int argc, char **argv);109static int zfs_do_allow(int argc, char **argv);110static int zfs_do_unallow(int argc, char **argv);111static int zfs_do_hold(int argc, char **argv);112static int zfs_do_holds(int argc, char **argv);113static int zfs_do_release(int argc, char **argv);114static int zfs_do_diff(int argc, char **argv);115static int zfs_do_bookmark(int argc, char **argv);116static int zfs_do_channel_program(int argc, char **argv);117static int zfs_do_load_key(int argc, char **argv);118static int zfs_do_unload_key(int argc, char **argv);119static int zfs_do_change_key(int argc, char **argv);120static int zfs_do_project(int argc, char **argv);121static int zfs_do_version(int argc, char **argv);122static int zfs_do_redact(int argc, char **argv);123static int zfs_do_rewrite(int argc, char **argv);124static int zfs_do_wait(int argc, char **argv);125126#ifdef __FreeBSD__127static int zfs_do_jail(int argc, char **argv);128static int zfs_do_unjail(int argc, char **argv);129#endif130131#ifdef __linux__132static int zfs_do_zone(int argc, char **argv);133static int zfs_do_unzone(int argc, char **argv);134#endif135136static int zfs_do_help(int argc, char **argv);137138enum zfs_options {139ZFS_OPTION_JSON_NUMS_AS_INT = 1024140};141142/*143* Enable a reasonable set of defaults for libumem debugging on DEBUG builds.144*/145146#ifdef DEBUG147const char *148_umem_debug_init(void)149{150return ("default,verbose"); /* $UMEM_DEBUG setting */151}152153const char *154_umem_logging_init(void)155{156return ("fail,contents"); /* $UMEM_LOGGING setting */157}158#endif159160typedef enum {161HELP_CLONE,162HELP_CREATE,163HELP_DESTROY,164HELP_GET,165HELP_INHERIT,166HELP_UPGRADE,167HELP_LIST,168HELP_MOUNT,169HELP_PROMOTE,170HELP_RECEIVE,171HELP_RENAME,172HELP_ROLLBACK,173HELP_SEND,174HELP_SET,175HELP_SHARE,176HELP_SNAPSHOT,177HELP_UNMOUNT,178HELP_UNSHARE,179HELP_ALLOW,180HELP_UNALLOW,181HELP_USERSPACE,182HELP_GROUPSPACE,183HELP_PROJECTSPACE,184HELP_PROJECT,185HELP_HOLD,186HELP_HOLDS,187HELP_RELEASE,188HELP_DIFF,189HELP_BOOKMARK,190HELP_CHANNEL_PROGRAM,191HELP_LOAD_KEY,192HELP_UNLOAD_KEY,193HELP_CHANGE_KEY,194HELP_VERSION,195HELP_REDACT,196HELP_REWRITE,197HELP_JAIL,198HELP_UNJAIL,199HELP_WAIT,200HELP_ZONE,201HELP_UNZONE,202} zfs_help_t;203204typedef struct zfs_command {205const char *name;206int (*func)(int argc, char **argv);207zfs_help_t usage;208} zfs_command_t;209210/*211* Master command table. Each ZFS command has a name, associated function, and212* usage message. The usage messages need to be internationalized, so we have213* to have a function to return the usage message based on a command index.214*215* These commands are organized according to how they are displayed in the usage216* message. An empty command (one with a NULL name) indicates an empty line in217* the generic usage message.218*/219static zfs_command_t command_table[] = {220{ "version", zfs_do_version, HELP_VERSION },221{ NULL },222{ "create", zfs_do_create, HELP_CREATE },223{ "destroy", zfs_do_destroy, HELP_DESTROY },224{ NULL },225{ "snapshot", zfs_do_snapshot, HELP_SNAPSHOT },226{ "rollback", zfs_do_rollback, HELP_ROLLBACK },227{ "clone", zfs_do_clone, HELP_CLONE },228{ "promote", zfs_do_promote, HELP_PROMOTE },229{ "rename", zfs_do_rename, HELP_RENAME },230{ "bookmark", zfs_do_bookmark, HELP_BOOKMARK },231{ "diff", zfs_do_diff, HELP_DIFF },232{ NULL },233{ "list", zfs_do_list, HELP_LIST },234{ NULL },235{ "set", zfs_do_set, HELP_SET },236{ "get", zfs_do_get, HELP_GET },237{ "inherit", zfs_do_inherit, HELP_INHERIT },238{ "upgrade", zfs_do_upgrade, HELP_UPGRADE },239{ NULL },240{ "userspace", zfs_do_userspace, HELP_USERSPACE },241{ "groupspace", zfs_do_userspace, HELP_GROUPSPACE },242{ "projectspace", zfs_do_userspace, HELP_PROJECTSPACE },243{ NULL },244{ "project", zfs_do_project, HELP_PROJECT },245{ NULL },246{ "mount", zfs_do_mount, HELP_MOUNT },247{ "unmount", zfs_do_unmount, HELP_UNMOUNT },248{ "share", zfs_do_share, HELP_SHARE },249{ "unshare", zfs_do_unshare, HELP_UNSHARE },250{ NULL },251{ "send", zfs_do_send, HELP_SEND },252{ "receive", zfs_do_receive, HELP_RECEIVE },253{ "redact", zfs_do_redact, HELP_REDACT },254{ NULL },255{ "allow", zfs_do_allow, HELP_ALLOW },256{ "unallow", zfs_do_unallow, HELP_UNALLOW },257{ NULL },258{ "hold", zfs_do_hold, HELP_HOLD },259{ "holds", zfs_do_holds, HELP_HOLDS },260{ "release", zfs_do_release, HELP_RELEASE },261{ NULL },262{ "load-key", zfs_do_load_key, HELP_LOAD_KEY },263{ "unload-key", zfs_do_unload_key, HELP_UNLOAD_KEY },264{ "change-key", zfs_do_change_key, HELP_CHANGE_KEY },265{ NULL },266{ "program", zfs_do_channel_program, HELP_CHANNEL_PROGRAM },267{ "rewrite", zfs_do_rewrite, HELP_REWRITE },268{ "wait", zfs_do_wait, HELP_WAIT },269270#ifdef __FreeBSD__271{ NULL },272{ "jail", zfs_do_jail, HELP_JAIL },273{ "unjail", zfs_do_unjail, HELP_UNJAIL },274#endif275276#ifdef __linux__277{ NULL },278{ "zone", zfs_do_zone, HELP_ZONE },279{ "unzone", zfs_do_unzone, HELP_UNZONE },280#endif281};282283#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))284285#define MAX_CMD_LEN 256286287zfs_command_t *current_command;288289static const char *290get_usage(zfs_help_t idx)291{292switch (idx) {293case HELP_CLONE:294return (gettext("\tclone [-p] [-o property=value] ... "295"<snapshot> <filesystem|volume>\n"));296case HELP_CREATE:297return (gettext("\tcreate [-Pnpuv] [-o property=value] ... "298"<filesystem>\n"299"\tcreate [-Pnpsv] [-b blocksize] [-o property=value] ... "300"-V <size> <volume>\n"));301case HELP_DESTROY:302return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"303"\tdestroy [-dnpRrv] "304"<filesystem|volume>@<snap>[%<snap>][,...]\n"305"\tdestroy <filesystem|volume>#<bookmark>\n"));306case HELP_GET:307return (gettext("\tget [-rHp] [-j [--json-int]] [-d max] "308"[-o \"all\" | field[,...]]\n"309"\t [-t type[,...]] [-s source[,...]]\n"310"\t <\"all\" | property[,...]> "311"[filesystem|volume|snapshot|bookmark] ...\n"));312case HELP_INHERIT:313return (gettext("\tinherit [-rS] <property> "314"<filesystem|volume|snapshot> ...\n"));315case HELP_UPGRADE:316return (gettext("\tupgrade [-v]\n"317"\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));318case HELP_LIST:319return (gettext("\tlist [-Hp] [-j [--json-int]] [-r|-d max] "320"[-o property[,...]] [-s property]...\n\t "321"[-S property]... [-t type[,...]] "322"[filesystem|volume|snapshot] ...\n"));323case HELP_MOUNT:324return (gettext("\tmount [-j]\n"325"\tmount [-flvO] [-o opts] <-a|-R filesystem|"326"filesystem>\n"));327case HELP_PROMOTE:328return (gettext("\tpromote <clone-filesystem>\n"));329case HELP_RECEIVE:330return (gettext("\treceive [-vMnsFhu] "331"[-o <property>=<value>] ... [-x <property>] ...\n"332"\t <filesystem|volume|snapshot>\n"333"\treceive [-vMnsFhu] [-o <property>=<value>] ... "334"[-x <property>] ... \n"335"\t [-d | -e] <filesystem>\n"336"\treceive -A <filesystem|volume>\n"));337case HELP_RENAME:338return (gettext("\trename [-f] <filesystem|volume|snapshot> "339"<filesystem|volume|snapshot>\n"340"\trename -p [-f] <filesystem|volume> <filesystem|volume>\n"341"\trename -u [-f] <filesystem> <filesystem>\n"342"\trename -r <snapshot> <snapshot>\n"));343case HELP_ROLLBACK:344return (gettext("\trollback [-rRf] <snapshot>\n"));345case HELP_SEND:346return (gettext("\tsend [-DLPbcehnpsVvw] "347"[-i|-I snapshot]\n"348"\t [-R [-X dataset[,dataset]...]] <snapshot>\n"349"\tsend [-DnVvPLecw] [-i snapshot|bookmark] "350"<filesystem|volume|snapshot>\n"351"\tsend [-DnPpVvLec] [-i bookmark|snapshot] "352"--redact <bookmark> <snapshot>\n"353"\tsend [-nVvPe] -t <receive_resume_token>\n"354"\tsend [-PnVv] --saved filesystem\n"));355case HELP_SET:356return (gettext("\tset [-u] <property=value> ... "357"<filesystem|volume|snapshot> ...\n"));358case HELP_SHARE:359return (gettext("\tshare [-l] <-a [nfs|smb] | filesystem>\n"));360case HELP_SNAPSHOT:361return (gettext("\tsnapshot [-r] [-o property=value] ... "362"<filesystem|volume>@<snap> ...\n"));363case HELP_UNMOUNT:364return (gettext("\tunmount [-fu] "365"<-a | filesystem|mountpoint>\n"));366case HELP_UNSHARE:367return (gettext("\tunshare "368"<-a [nfs|smb] | filesystem|mountpoint>\n"));369case HELP_ALLOW:370return (gettext("\tallow <filesystem|volume>\n"371"\tallow [-ldug] "372"<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n"373"\t <filesystem|volume>\n"374"\tallow [-ld] -e <perm|@setname>[,...] "375"<filesystem|volume>\n"376"\tallow -c <perm|@setname>[,...] <filesystem|volume>\n"377"\tallow -s @setname <perm|@setname>[,...] "378"<filesystem|volume>\n"));379case HELP_UNALLOW:380return (gettext("\tunallow [-rldug] "381"<\"everyone\"|user|group>[,...]\n"382"\t [<perm|@setname>[,...]] <filesystem|volume>\n"383"\tunallow [-rld] -e [<perm|@setname>[,...]] "384"<filesystem|volume>\n"385"\tunallow [-r] -c [<perm|@setname>[,...]] "386"<filesystem|volume>\n"387"\tunallow [-r] -s @setname [<perm|@setname>[,...]] "388"<filesystem|volume>\n"));389case HELP_USERSPACE:390return (gettext("\tuserspace [-Hinp] [-o field[,...]] "391"[-s field] ...\n"392"\t [-S field] ... [-t type[,...]] "393"<filesystem|snapshot|path>\n"));394case HELP_GROUPSPACE:395return (gettext("\tgroupspace [-Hinp] [-o field[,...]] "396"[-s field] ...\n"397"\t [-S field] ... [-t type[,...]] "398"<filesystem|snapshot|path>\n"));399case HELP_PROJECTSPACE:400return (gettext("\tprojectspace [-Hp] [-o field[,...]] "401"[-s field] ... \n"402"\t [-S field] ... <filesystem|snapshot|path>\n"));403case HELP_PROJECT:404return (gettext("\tproject [-d|-r] <directory|file ...>\n"405"\tproject -c [-0] [-d|-r] [-p id] <directory|file ...>\n"406"\tproject -C [-k] [-r] <directory ...>\n"407"\tproject [-p id] [-r] [-s] <directory ...>\n"));408case HELP_HOLD:409return (gettext("\thold [-r] <tag> <snapshot> ...\n"));410case HELP_HOLDS:411return (gettext("\tholds [-rHp] <snapshot> ...\n"));412case HELP_RELEASE:413return (gettext("\trelease [-r] <tag> <snapshot> ...\n"));414case HELP_DIFF:415return (gettext("\tdiff [-FHth] <snapshot> "416"[snapshot|filesystem]\n"));417case HELP_BOOKMARK:418return (gettext("\tbookmark <snapshot|bookmark> "419"<newbookmark>\n"));420case HELP_CHANNEL_PROGRAM:421return (gettext("\tprogram [-jn] [-t <instruction limit>] "422"[-m <memory limit (b)>]\n"423"\t <pool> <program file> [lua args...]\n"));424case HELP_LOAD_KEY:425return (gettext("\tload-key [-rn] [-L <keylocation>] "426"<-a | filesystem|volume>\n"));427case HELP_UNLOAD_KEY:428return (gettext("\tunload-key [-r] "429"<-a | filesystem|volume>\n"));430case HELP_CHANGE_KEY:431return (gettext("\tchange-key [-l] [-o keyformat=<value>]\n"432"\t [-o keylocation=<value>] [-o pbkdf2iters=<value>]\n"433"\t <filesystem|volume>\n"434"\tchange-key -i [-l] <filesystem|volume>\n"));435case HELP_VERSION:436return (gettext("\tversion [-j]\n"));437case HELP_REDACT:438return (gettext("\tredact <snapshot> <bookmark> "439"<redaction_snapshot> ...\n"));440case HELP_REWRITE:441return (gettext("\trewrite [-Prvx] [-o <offset>] [-l <length>] "442"<directory|file ...>\n"));443case HELP_JAIL:444return (gettext("\tjail <jailid|jailname> <filesystem>\n"));445case HELP_UNJAIL:446return (gettext("\tunjail <jailid|jailname> <filesystem>\n"));447case HELP_WAIT:448return (gettext("\twait [-t <activity>] <filesystem>\n"));449case HELP_ZONE:450return (gettext("\tzone <nsfile> <filesystem>\n"));451case HELP_UNZONE:452return (gettext("\tunzone <nsfile> <filesystem>\n"));453default:454__builtin_unreachable();455}456}457458void459nomem(void)460{461(void) fprintf(stderr, gettext("internal error: out of memory\n"));462exit(1);463}464465/*466* Utility function to guarantee malloc() success.467*/468469void *470safe_malloc(size_t size)471{472void *data;473474if ((data = calloc(1, size)) == NULL)475nomem();476477return (data);478}479480static void *481safe_realloc(void *data, size_t size)482{483void *newp;484if ((newp = realloc(data, size)) == NULL) {485free(data);486nomem();487}488489return (newp);490}491492static char *493safe_strdup(const char *str)494{495char *dupstr = strdup(str);496497if (dupstr == NULL)498nomem();499500return (dupstr);501}502503/*504* Callback routine that will print out information for each of505* the properties.506*/507static int508usage_prop_cb(int prop, void *cb)509{510FILE *fp = cb;511512(void) fprintf(fp, "\t%-22s ", zfs_prop_to_name(prop));513514if (zfs_prop_readonly(prop))515(void) fprintf(fp, " NO ");516else517(void) fprintf(fp, "YES ");518519if (zfs_prop_inheritable(prop))520(void) fprintf(fp, " YES ");521else522(void) fprintf(fp, " NO ");523524(void) fprintf(fp, "%s\n", zfs_prop_values(prop) ?: "-");525526return (ZPROP_CONT);527}528529/*530* Display usage message. If we're inside a command, display only the usage for531* that command. Otherwise, iterate over the entire command table and display532* a complete usage message.533*/534static __attribute__((noreturn)) void535usage(boolean_t requested)536{537int i;538boolean_t show_properties = B_FALSE;539FILE *fp = requested ? stdout : stderr;540541if (current_command == NULL) {542543(void) fprintf(fp, gettext("usage: zfs command args ...\n"));544(void) fprintf(fp,545gettext("where 'command' is one of the following:\n\n"));546547for (i = 0; i < NCOMMAND; i++) {548if (command_table[i].name == NULL)549(void) fprintf(fp, "\n");550else551(void) fprintf(fp, "%s",552get_usage(command_table[i].usage));553}554555(void) fprintf(fp, gettext("\nEach dataset is of the form: "556"pool/[dataset/]*dataset[@name]\n"));557} else {558(void) fprintf(fp, gettext("usage:\n"));559(void) fprintf(fp, "%s", get_usage(current_command->usage));560}561562if (current_command != NULL &&563(strcmp(current_command->name, "set") == 0 ||564strcmp(current_command->name, "get") == 0 ||565strcmp(current_command->name, "inherit") == 0 ||566strcmp(current_command->name, "list") == 0))567show_properties = B_TRUE;568569if (show_properties) {570(void) fprintf(fp, "%s",571gettext("\nThe following properties are supported:\n"));572573(void) fprintf(fp, "\n\t%-21s %s %s %s\n\n",574"PROPERTY", "EDIT", "INHERIT", "VALUES");575576/* Iterate over all properties */577(void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE,578ZFS_TYPE_DATASET);579580(void) fprintf(fp, "\t%-22s ", "userused@...");581(void) fprintf(fp, " NO NO <size>\n");582(void) fprintf(fp, "\t%-22s ", "groupused@...");583(void) fprintf(fp, " NO NO <size>\n");584(void) fprintf(fp, "\t%-22s ", "projectused@...");585(void) fprintf(fp, " NO NO <size>\n");586(void) fprintf(fp, "\t%-22s ", "userobjused@...");587(void) fprintf(fp, " NO NO <size>\n");588(void) fprintf(fp, "\t%-22s ", "groupobjused@...");589(void) fprintf(fp, " NO NO <size>\n");590(void) fprintf(fp, "\t%-22s ", "projectobjused@...");591(void) fprintf(fp, " NO NO <size>\n");592(void) fprintf(fp, "\t%-22s ", "userquota@...");593(void) fprintf(fp, "YES NO <size> | none\n");594(void) fprintf(fp, "\t%-22s ", "groupquota@...");595(void) fprintf(fp, "YES NO <size> | none\n");596(void) fprintf(fp, "\t%-22s ", "projectquota@...");597(void) fprintf(fp, "YES NO <size> | none\n");598(void) fprintf(fp, "\t%-22s ", "userobjquota@...");599(void) fprintf(fp, "YES NO <size> | none\n");600(void) fprintf(fp, "\t%-22s ", "groupobjquota@...");601(void) fprintf(fp, "YES NO <size> | none\n");602(void) fprintf(fp, "\t%-22s ", "projectobjquota@...");603(void) fprintf(fp, "YES NO <size> | none\n");604(void) fprintf(fp, "\t%-22s ", "written@<snap>");605(void) fprintf(fp, " NO NO <size>\n");606(void) fprintf(fp, "\t%-22s ", "written#<bookmark>");607(void) fprintf(fp, " NO NO <size>\n");608609(void) fprintf(fp, gettext("\nSizes are specified in bytes "610"with standard units such as K, M, G, etc.\n"));611(void) fprintf(fp, "%s", gettext("\nUser-defined properties "612"can be specified by using a name containing a colon "613"(:).\n"));614(void) fprintf(fp, gettext("\nThe {user|group|project}"615"[obj]{used|quota}@ properties must be appended with\n"616"a user|group|project specifier of one of these forms:\n"617" POSIX name (eg: \"matt\")\n"618" POSIX id (eg: \"126829\")\n"619" SMB name@domain (eg: \"matt@sun\")\n"620" SMB SID (eg: \"S-1-234-567-89\")\n"));621} else {622(void) fprintf(fp,623gettext("\nFor the property list, run: %s\n"),624"zfs set|get");625(void) fprintf(fp,626gettext("\nFor the delegated permission list, run: %s\n"),627"zfs allow|unallow");628(void) fprintf(fp,629gettext("\nFor further help on a command or topic, "630"run: %s\n"), "zfs help [<topic>]");631}632633/*634* See comments at end of main().635*/636if (getenv("ZFS_ABORT") != NULL) {637(void) printf("dumping core by request\n");638abort();639}640641exit(requested ? 0 : 2);642}643644/*645* Take a property=value argument string and add it to the given nvlist.646* Modifies the argument inplace.647*/648static boolean_t649parseprop(nvlist_t *props, char *propname)650{651char *propval;652653if ((propval = strchr(propname, '=')) == NULL) {654(void) fprintf(stderr, gettext("missing "655"'=' for property=value argument\n"));656return (B_FALSE);657}658*propval = '\0';659propval++;660if (nvlist_exists(props, propname)) {661(void) fprintf(stderr, gettext("property '%s' "662"specified multiple times\n"), propname);663return (B_FALSE);664}665if (nvlist_add_string(props, propname, propval) != 0)666nomem();667return (B_TRUE);668}669670/*671* Take a property name argument and add it to the given nvlist.672* Modifies the argument inplace.673*/674static boolean_t675parsepropname(nvlist_t *props, char *propname)676{677if (strchr(propname, '=') != NULL) {678(void) fprintf(stderr, gettext("invalid character "679"'=' in property argument\n"));680return (B_FALSE);681}682if (nvlist_exists(props, propname)) {683(void) fprintf(stderr, gettext("property '%s' "684"specified multiple times\n"), propname);685return (B_FALSE);686}687if (nvlist_add_boolean(props, propname) != 0)688nomem();689return (B_TRUE);690}691692static int693parse_depth(char *opt, int *flags)694{695char *tmp;696int depth;697698depth = (int)strtol(opt, &tmp, 0);699if (*tmp) {700(void) fprintf(stderr,701gettext("%s is not an integer\n"), optarg);702usage(B_FALSE);703}704if (depth < 0) {705(void) fprintf(stderr,706gettext("Depth can not be negative.\n"));707usage(B_FALSE);708}709*flags |= (ZFS_ITER_DEPTH_LIMIT|ZFS_ITER_RECURSE);710return (depth);711}712713#define PROGRESS_DELAY 2 /* seconds */714715static const char *pt_reverse =716"\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";717static time_t pt_begin;718static char *pt_header = NULL;719static boolean_t pt_shown;720721static void722start_progress_timer(void)723{724pt_begin = time(NULL) + PROGRESS_DELAY;725pt_shown = B_FALSE;726}727728static void729set_progress_header(const char *header)730{731assert(pt_header == NULL);732pt_header = safe_strdup(header);733if (pt_shown) {734(void) printf("%s: ", header);735(void) fflush(stdout);736}737}738739static void740update_progress(const char *update)741{742if (!pt_shown && time(NULL) > pt_begin) {743int len = strlen(update);744745(void) printf("%s: %s%*.*s", pt_header, update, len, len,746pt_reverse);747(void) fflush(stdout);748pt_shown = B_TRUE;749} else if (pt_shown) {750int len = strlen(update);751752(void) printf("%s%*.*s", update, len, len, pt_reverse);753(void) fflush(stdout);754}755}756757static void758finish_progress(const char *done)759{760if (pt_shown) {761(void) puts(done);762(void) fflush(stdout);763}764free(pt_header);765pt_header = NULL;766}767768static int769zfs_mount_and_share(libzfs_handle_t *hdl, const char *dataset, zfs_type_t type)770{771zfs_handle_t *zhp = NULL;772int ret = 0;773774zhp = zfs_open(hdl, dataset, type);775if (zhp == NULL)776return (1);777778/*779* Volumes may neither be mounted or shared. Potentially in the780* future filesystems detected on these volumes could be mounted.781*/782if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {783zfs_close(zhp);784return (0);785}786787/*788* Mount and/or share the new filesystem as appropriate. We provide a789* verbose error message to let the user know that their filesystem was790* in fact created, even if we failed to mount or share it.791*792* If the user doesn't want the dataset automatically mounted, then793* skip the mount/share step794*/795if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type, B_FALSE) &&796zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON) {797if (zfs_mount_delegation_check()) {798(void) fprintf(stderr, gettext("filesystem "799"successfully created, but it may only be "800"mounted by root\n"));801ret = 1;802} else if (zfs_mount(zhp, NULL, 0) != 0) {803(void) fprintf(stderr, gettext("filesystem "804"successfully created, but not mounted\n"));805ret = 1;806} else if (zfs_share(zhp, NULL) != 0) {807(void) fprintf(stderr, gettext("filesystem "808"successfully created, but not shared\n"));809ret = 1;810}811zfs_commit_shares(NULL);812}813814zfs_close(zhp);815816return (ret);817}818819/*820* zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>821*822* Given an existing dataset, create a writable copy whose initial contents823* are the same as the source. The newly created dataset maintains a824* dependency on the original; the original cannot be destroyed so long as825* the clone exists.826*827* The '-p' flag creates all the non-existing ancestors of the target first.828*/829static int830zfs_do_clone(int argc, char **argv)831{832zfs_handle_t *zhp = NULL;833boolean_t parents = B_FALSE;834nvlist_t *props;835int ret = 0;836int c;837838if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)839nomem();840841/* check options */842while ((c = getopt(argc, argv, "o:p")) != -1) {843switch (c) {844case 'o':845if (!parseprop(props, optarg)) {846nvlist_free(props);847return (1);848}849break;850case 'p':851parents = B_TRUE;852break;853case '?':854(void) fprintf(stderr, gettext("invalid option '%c'\n"),855optopt);856goto usage;857}858}859860argc -= optind;861argv += optind;862863/* check number of arguments */864if (argc < 1) {865(void) fprintf(stderr, gettext("missing source dataset "866"argument\n"));867goto usage;868}869if (argc < 2) {870(void) fprintf(stderr, gettext("missing target dataset "871"argument\n"));872goto usage;873}874if (argc > 2) {875(void) fprintf(stderr, gettext("too many arguments\n"));876goto usage;877}878879/* open the source dataset */880if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) {881nvlist_free(props);882return (1);883}884885if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM |886ZFS_TYPE_VOLUME)) {887/*888* Now create the ancestors of the target dataset. If the889* target already exists and '-p' option was used we should not890* complain.891*/892if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM |893ZFS_TYPE_VOLUME)) {894zfs_close(zhp);895nvlist_free(props);896return (0);897}898if (zfs_create_ancestors(g_zfs, argv[1]) != 0) {899zfs_close(zhp);900nvlist_free(props);901return (1);902}903}904905/* pass to libzfs */906ret = zfs_clone(zhp, argv[1], props);907908/* create the mountpoint if necessary */909if (ret == 0) {910if (log_history) {911(void) zpool_log_history(g_zfs, history_str);912log_history = B_FALSE;913}914915/*916* Dataset cloned successfully, mount/share failures are917* non-fatal.918*/919(void) zfs_mount_and_share(g_zfs, argv[1], ZFS_TYPE_DATASET);920}921922zfs_close(zhp);923nvlist_free(props);924925return (!!ret);926927usage:928ASSERT0P(zhp);929nvlist_free(props);930usage(B_FALSE);931return (-1);932}933934/*935* Calculate the minimum allocation size based on the top-level vdevs.936*/937static uint64_t938calculate_volblocksize(nvlist_t *config)939{940uint64_t asize = SPA_MINBLOCKSIZE;941nvlist_t *tree, **vdevs;942uint_t nvdevs;943944if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &tree) != 0 ||945nvlist_lookup_nvlist_array(tree, ZPOOL_CONFIG_CHILDREN,946&vdevs, &nvdevs) != 0) {947return (ZVOL_DEFAULT_BLOCKSIZE);948}949950for (int i = 0; i < nvdevs; i++) {951nvlist_t *nv = vdevs[i];952uint64_t ashift, ndata, nparity;953954if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ASHIFT, &ashift) != 0)955continue;956957if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_DRAID_NDATA,958&ndata) == 0) {959/* dRAID minimum allocation width */960asize = MAX(asize, ndata * (1ULL << ashift));961} else if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NPARITY,962&nparity) == 0) {963/* raidz minimum allocation width */964if (nparity == 1)965asize = MAX(asize, 2 * (1ULL << ashift));966else967asize = MAX(asize, 4 * (1ULL << ashift));968} else {969/* mirror or (non-redundant) leaf vdev */970asize = MAX(asize, 1ULL << ashift);971}972}973974return (asize);975}976977/*978* Return a default volblocksize for the pool which always uses more than979* half of the data sectors. This primarily applies to dRAID which always980* writes full stripe widths.981*/982static uint64_t983default_volblocksize(zpool_handle_t *zhp, nvlist_t *props)984{985uint64_t volblocksize, asize = SPA_MINBLOCKSIZE;986987nvlist_t *config = zpool_get_config(zhp, NULL);988989if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_MAX_ALLOC, &asize) != 0)990asize = calculate_volblocksize(config);991992/*993* Calculate the target volblocksize such that more than half994* of the asize is used. The following table is for 4k sectors.995*996* n asize blksz used | n asize blksz used997* -------------------------+---------------------------------998* 1 4,096 8,192 100% | 9 36,864 32,768 88%999* 2 8,192 8,192 100% | 10 40,960 32,768 80%1000* 3 12,288 8,192 66% | 11 45,056 32,768 72%1001* 4 16,384 16,384 100% | 12 49,152 32,768 66%1002* 5 20,480 16,384 80% | 13 53,248 32,768 61%1003* 6 24,576 16,384 66% | 14 57,344 32,768 57%1004* 7 28,672 16,384 57% | 15 61,440 32,768 53%1005* 8 32,768 32,768 100% | 16 65,536 65,636 100%1006*1007* This is primarily a concern for dRAID which always allocates1008* a full stripe width. For dRAID the default stripe width is1009* n=8 in which case the volblocksize is set to 32k. Ignoring1010* compression there are no unused sectors. This same reasoning1011* applies to raidz[2,3] so target 4 sectors to minimize waste.1012*/1013uint64_t tgt_volblocksize = ZVOL_DEFAULT_BLOCKSIZE;1014while (tgt_volblocksize * 2 <= asize)1015tgt_volblocksize *= 2;10161017const char *prop = zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE);1018if (nvlist_lookup_uint64(props, prop, &volblocksize) == 0) {10191020/* Issue a warning when a non-optimal size is requested. */1021if (volblocksize < ZVOL_DEFAULT_BLOCKSIZE) {1022(void) fprintf(stderr, gettext("Warning: "1023"volblocksize (%llu) is less than the default "1024"minimum block size (%llu).\nTo reduce wasted "1025"space a volblocksize of %llu is recommended.\n"),1026(u_longlong_t)volblocksize,1027(u_longlong_t)ZVOL_DEFAULT_BLOCKSIZE,1028(u_longlong_t)tgt_volblocksize);1029} else if (volblocksize < tgt_volblocksize) {1030(void) fprintf(stderr, gettext("Warning: "1031"volblocksize (%llu) is much less than the "1032"minimum allocation\nunit (%llu), which wastes "1033"at least %llu%% of space. To reduce wasted "1034"space,\nuse a larger volblocksize (%llu is "1035"recommended), fewer dRAID data disks\n"1036"per group, or smaller sector size (ashift).\n"),1037(u_longlong_t)volblocksize, (u_longlong_t)asize,1038(u_longlong_t)((100 * (asize - volblocksize)) /1039asize), (u_longlong_t)tgt_volblocksize);1040}1041} else {1042volblocksize = tgt_volblocksize;1043fnvlist_add_uint64(props, prop, volblocksize);1044}10451046return (volblocksize);1047}10481049/*1050* zfs create [-Pnpv] [-o prop=value] ... fs1051* zfs create [-Pnpsv] [-b blocksize] [-o prop=value] ... -V vol size1052*1053* Create a new dataset. This command can be used to create filesystems1054* and volumes. Snapshot creation is handled by 'zfs snapshot'.1055* For volumes, the user must specify a size to be used.1056*1057* The '-s' flag applies only to volumes, and indicates that we should not try1058* to set the reservation for this volume. By default we set a reservation1059* equal to the size for any volume. For pools with SPA_VERSION >=1060* SPA_VERSION_REFRESERVATION, we set a refreservation instead.1061*1062* The '-p' flag creates all the non-existing ancestors of the target first.1063*1064* The '-n' flag is no-op (dry run) mode. This will perform a user-space sanity1065* check of arguments and properties, but does not check for permissions,1066* available space, etc.1067*1068* The '-u' flag prevents the newly created file system from being mounted.1069*1070* The '-v' flag is for verbose output.1071*1072* The '-P' flag is used for parseable output. It implies '-v'.1073*/1074static int1075zfs_do_create(int argc, char **argv)1076{1077zfs_type_t type = ZFS_TYPE_FILESYSTEM;1078zpool_handle_t *zpool_handle = NULL;1079nvlist_t *real_props = NULL;1080uint64_t volsize = 0;1081int c;1082boolean_t noreserve = B_FALSE;1083boolean_t bflag = B_FALSE;1084boolean_t parents = B_FALSE;1085boolean_t dryrun = B_FALSE;1086boolean_t nomount = B_FALSE;1087boolean_t verbose = B_FALSE;1088boolean_t parseable = B_FALSE;1089int ret = 1;1090nvlist_t *props;1091uint64_t intval;1092const char *strval;10931094if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)1095nomem();10961097/* check options */1098while ((c = getopt(argc, argv, ":PV:b:nso:puv")) != -1) {1099switch (c) {1100case 'V':1101type = ZFS_TYPE_VOLUME;1102if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {1103(void) fprintf(stderr, gettext("bad volume "1104"size '%s': %s\n"), optarg,1105libzfs_error_description(g_zfs));1106goto error;1107}11081109if (nvlist_add_uint64(props,1110zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0)1111nomem();1112volsize = intval;1113break;1114case 'P':1115verbose = B_TRUE;1116parseable = B_TRUE;1117break;1118case 'p':1119parents = B_TRUE;1120break;1121case 'b':1122bflag = B_TRUE;1123if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {1124(void) fprintf(stderr, gettext("bad volume "1125"block size '%s': %s\n"), optarg,1126libzfs_error_description(g_zfs));1127goto error;1128}11291130if (nvlist_add_uint64(props,1131zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),1132intval) != 0)1133nomem();1134break;1135case 'n':1136dryrun = B_TRUE;1137break;1138case 'o':1139if (!parseprop(props, optarg))1140goto error;1141break;1142case 's':1143noreserve = B_TRUE;1144break;1145case 'u':1146nomount = B_TRUE;1147break;1148case 'v':1149verbose = B_TRUE;1150break;1151case ':':1152(void) fprintf(stderr, gettext("missing size "1153"argument\n"));1154goto badusage;1155case '?':1156(void) fprintf(stderr, gettext("invalid option '%c'\n"),1157optopt);1158goto badusage;1159}1160}11611162if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) {1163(void) fprintf(stderr, gettext("'-s' and '-b' can only be "1164"used when creating a volume\n"));1165goto badusage;1166}1167if (nomount && type != ZFS_TYPE_FILESYSTEM) {1168(void) fprintf(stderr, gettext("'-u' can only be "1169"used when creating a filesystem\n"));1170goto badusage;1171}11721173argc -= optind;1174argv += optind;11751176/* check number of arguments */1177if (argc == 0) {1178(void) fprintf(stderr, gettext("missing %s argument\n"),1179zfs_type_to_name(type));1180goto badusage;1181}1182if (argc > 1) {1183(void) fprintf(stderr, gettext("too many arguments\n"));1184goto badusage;1185}11861187if (dryrun || type == ZFS_TYPE_VOLUME) {1188char msg[ZFS_MAX_DATASET_NAME_LEN * 2];1189char *p;11901191if ((p = strchr(argv[0], '/')) != NULL)1192*p = '\0';1193zpool_handle = zpool_open(g_zfs, argv[0]);1194if (p != NULL)1195*p = '/';1196if (zpool_handle == NULL)1197goto error;11981199(void) snprintf(msg, sizeof (msg),1200dryrun ? gettext("cannot verify '%s'") :1201gettext("cannot create '%s'"), argv[0]);1202if (props && (real_props = zfs_valid_proplist(g_zfs, type,1203props, 0, NULL, zpool_handle, B_TRUE, msg)) == NULL) {1204zpool_close(zpool_handle);1205goto error;1206}1207}12081209if (type == ZFS_TYPE_VOLUME) {1210const char *prop = zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE);1211uint64_t volblocksize = default_volblocksize(zpool_handle,1212real_props);12131214if (volblocksize != ZVOL_DEFAULT_BLOCKSIZE &&1215nvlist_lookup_string(props, prop, &strval) != 0) {1216char *tmp;1217if (asprintf(&tmp, "%llu",1218(u_longlong_t)volblocksize) == -1)1219nomem();1220nvlist_add_string(props, prop, tmp);1221free(tmp);1222}12231224/*1225* If volsize is not a multiple of volblocksize, round it1226* up to the nearest multiple of the volblocksize.1227*/1228if (volsize % volblocksize) {1229volsize = P2ROUNDUP_TYPED(volsize, volblocksize,1230uint64_t);12311232if (nvlist_add_uint64(props,1233zfs_prop_to_name(ZFS_PROP_VOLSIZE), volsize) != 0) {1234nvlist_free(props);1235nomem();1236}1237}1238}12391240if (type == ZFS_TYPE_VOLUME && !noreserve) {1241uint64_t spa_version;1242zfs_prop_t resv_prop;12431244spa_version = zpool_get_prop_int(zpool_handle,1245ZPOOL_PROP_VERSION, NULL);1246if (spa_version >= SPA_VERSION_REFRESERVATION)1247resv_prop = ZFS_PROP_REFRESERVATION;1248else1249resv_prop = ZFS_PROP_RESERVATION;12501251volsize = zvol_volsize_to_reservation(zpool_handle, volsize,1252real_props);12531254if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop),1255&strval) != 0) {1256if (nvlist_add_uint64(props,1257zfs_prop_to_name(resv_prop), volsize) != 0) {1258nvlist_free(props);1259nomem();1260}1261}1262}1263if (zpool_handle != NULL) {1264zpool_close(zpool_handle);1265nvlist_free(real_props);1266}12671268if (parents && zfs_name_valid(argv[0], type)) {1269/*1270* Now create the ancestors of target dataset. If the target1271* already exists and '-p' option was used we should not1272* complain.1273*/1274if (zfs_dataset_exists(g_zfs, argv[0], type)) {1275ret = 0;1276goto error;1277}1278if (verbose) {1279(void) printf(parseable ? "create_ancestors\t%s\n" :1280dryrun ? "would create ancestors of %s\n" :1281"create ancestors of %s\n", argv[0]);1282}1283if (!dryrun) {1284if (zfs_create_ancestors(g_zfs, argv[0]) != 0) {1285goto error;1286}1287}1288}12891290if (verbose) {1291nvpair_t *nvp = NULL;1292(void) printf(parseable ? "create\t%s\n" :1293dryrun ? "would create %s\n" : "create %s\n", argv[0]);1294while ((nvp = nvlist_next_nvpair(props, nvp)) != NULL) {1295uint64_t uval;1296const char *sval;12971298switch (nvpair_type(nvp)) {1299case DATA_TYPE_UINT64:1300VERIFY0(nvpair_value_uint64(nvp, &uval));1301(void) printf(parseable ?1302"property\t%s\t%llu\n" : "\t%s=%llu\n",1303nvpair_name(nvp), (u_longlong_t)uval);1304break;1305case DATA_TYPE_STRING:1306VERIFY0(nvpair_value_string(nvp, &sval));1307(void) printf(parseable ?1308"property\t%s\t%s\n" : "\t%s=%s\n",1309nvpair_name(nvp), sval);1310break;1311default:1312(void) fprintf(stderr, "property '%s' "1313"has illegal type %d\n",1314nvpair_name(nvp), nvpair_type(nvp));1315abort();1316}1317}1318}1319if (dryrun) {1320ret = 0;1321goto error;1322}13231324/* pass to libzfs */1325if (zfs_create(g_zfs, argv[0], type, props) != 0)1326goto error;13271328if (log_history) {1329(void) zpool_log_history(g_zfs, history_str);1330log_history = B_FALSE;1331}13321333if (nomount) {1334ret = 0;1335goto error;1336}13371338/* Dataset created successfully, mount/share failures are non-fatal */1339ret = 0;1340(void) zfs_mount_and_share(g_zfs, argv[0], ZFS_TYPE_DATASET);1341error:1342nvlist_free(props);1343return (ret);1344badusage:1345nvlist_free(props);1346usage(B_FALSE);1347return (2);1348}13491350/*1351* zfs destroy [-rRf] <fs, vol>1352* zfs destroy [-rRd] <snap>1353*1354* -r Recursively destroy all children1355* -R Recursively destroy all dependents, including clones1356* -f Force unmounting of any dependents1357* -d If we can't destroy now, mark for deferred destruction1358*1359* Destroys the given dataset. By default, it will unmount any filesystems,1360* and refuse to destroy a dataset that has any dependents. A dependent can1361* either be a child, or a clone of a child.1362*/1363typedef struct destroy_cbdata {1364boolean_t cb_first;1365boolean_t cb_force;1366boolean_t cb_recurse;1367boolean_t cb_error;1368boolean_t cb_doclones;1369zfs_handle_t *cb_target;1370boolean_t cb_defer_destroy;1371boolean_t cb_verbose;1372boolean_t cb_parsable;1373boolean_t cb_dryrun;1374nvlist_t *cb_nvl;1375nvlist_t *cb_batchedsnaps;13761377/* first snap in contiguous run */1378char *cb_firstsnap;1379/* previous snap in contiguous run */1380char *cb_prevsnap;1381int64_t cb_snapused;1382char *cb_snapspec;1383char *cb_bookmark;1384uint64_t cb_snap_count;1385} destroy_cbdata_t;13861387/*1388* Check for any dependents based on the '-r' or '-R' flags.1389*/1390static int1391destroy_check_dependent(zfs_handle_t *zhp, void *data)1392{1393destroy_cbdata_t *cbp = data;1394const char *tname = zfs_get_name(cbp->cb_target);1395const char *name = zfs_get_name(zhp);13961397if (strncmp(tname, name, strlen(tname)) == 0 &&1398(name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) {1399/*1400* This is a direct descendant, not a clone somewhere else in1401* the hierarchy.1402*/1403if (cbp->cb_recurse)1404goto out;14051406if (cbp->cb_first) {1407(void) fprintf(stderr, gettext("cannot destroy '%s': "1408"%s has children\n"),1409zfs_get_name(cbp->cb_target),1410zfs_type_to_name(zfs_get_type(cbp->cb_target)));1411(void) fprintf(stderr, gettext("use '-r' to destroy "1412"the following datasets:\n"));1413cbp->cb_first = B_FALSE;1414cbp->cb_error = B_TRUE;1415}14161417(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));1418} else {1419/*1420* This is a clone. We only want to report this if the '-r'1421* wasn't specified, or the target is a snapshot.1422*/1423if (!cbp->cb_recurse &&1424zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT)1425goto out;14261427if (cbp->cb_first) {1428(void) fprintf(stderr, gettext("cannot destroy '%s': "1429"%s has dependent clones\n"),1430zfs_get_name(cbp->cb_target),1431zfs_type_to_name(zfs_get_type(cbp->cb_target)));1432(void) fprintf(stderr, gettext("use '-R' to destroy "1433"the following datasets:\n"));1434cbp->cb_first = B_FALSE;1435cbp->cb_error = B_TRUE;1436cbp->cb_dryrun = B_TRUE;1437}14381439(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));1440}14411442out:1443zfs_close(zhp);1444return (0);1445}14461447static int1448destroy_batched(destroy_cbdata_t *cb)1449{1450int error = zfs_destroy_snaps_nvl(g_zfs,1451cb->cb_batchedsnaps, B_FALSE);1452fnvlist_free(cb->cb_batchedsnaps);1453cb->cb_batchedsnaps = fnvlist_alloc();1454return (error);1455}14561457static int1458destroy_callback(zfs_handle_t *zhp, void *data)1459{1460destroy_cbdata_t *cb = data;1461const char *name = zfs_get_name(zhp);1462int error;14631464if (cb->cb_verbose) {1465if (cb->cb_parsable) {1466(void) printf("destroy\t%s\n", name);1467} else if (cb->cb_dryrun) {1468(void) printf(gettext("would destroy %s\n"),1469name);1470} else {1471(void) printf(gettext("will destroy %s\n"),1472name);1473}1474}14751476/*1477* Ignore pools (which we've already flagged as an error before getting1478* here).1479*/1480if (strchr(zfs_get_name(zhp), '/') == NULL &&1481zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {1482zfs_close(zhp);1483return (0);1484}1485if (cb->cb_dryrun) {1486zfs_close(zhp);1487return (0);1488}14891490/*1491* We batch up all contiguous snapshots (even of different1492* filesystems) and destroy them with one ioctl. We can't1493* simply do all snap deletions and then all fs deletions,1494* because we must delete a clone before its origin.1495*/1496if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) {1497cb->cb_snap_count++;1498fnvlist_add_boolean(cb->cb_batchedsnaps, name);1499if (cb->cb_snap_count % 10 == 0 && cb->cb_defer_destroy) {1500error = destroy_batched(cb);1501if (error != 0) {1502zfs_close(zhp);1503return (-1);1504}1505}1506} else {1507error = destroy_batched(cb);1508if (error != 0 ||1509zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 ||1510zfs_destroy(zhp, cb->cb_defer_destroy) != 0) {1511zfs_close(zhp);1512/*1513* When performing a recursive destroy we ignore errors1514* so that the recursive destroy could continue1515* destroying past problem datasets1516*/1517if (cb->cb_recurse) {1518cb->cb_error = B_TRUE;1519return (0);1520}1521return (-1);1522}1523}15241525zfs_close(zhp);1526return (0);1527}15281529static int1530destroy_print_cb(zfs_handle_t *zhp, void *arg)1531{1532destroy_cbdata_t *cb = arg;1533const char *name = zfs_get_name(zhp);1534int err = 0;15351536if (nvlist_exists(cb->cb_nvl, name)) {1537if (cb->cb_firstsnap == NULL)1538cb->cb_firstsnap = strdup(name);1539if (cb->cb_prevsnap != NULL)1540free(cb->cb_prevsnap);1541/* this snap continues the current range */1542cb->cb_prevsnap = strdup(name);1543if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL)1544nomem();1545if (cb->cb_verbose) {1546if (cb->cb_parsable) {1547(void) printf("destroy\t%s\n", name);1548} else if (cb->cb_dryrun) {1549(void) printf(gettext("would destroy %s\n"),1550name);1551} else {1552(void) printf(gettext("will destroy %s\n"),1553name);1554}1555}1556} else if (cb->cb_firstsnap != NULL) {1557/* end of this range */1558uint64_t used = 0;1559err = lzc_snaprange_space(cb->cb_firstsnap,1560cb->cb_prevsnap, &used);1561cb->cb_snapused += used;1562free(cb->cb_firstsnap);1563cb->cb_firstsnap = NULL;1564free(cb->cb_prevsnap);1565cb->cb_prevsnap = NULL;1566}1567zfs_close(zhp);1568return (err);1569}15701571static int1572destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb)1573{1574int err;1575assert(cb->cb_firstsnap == NULL);1576assert(cb->cb_prevsnap == NULL);1577err = zfs_iter_snapshots_sorted_v2(fs_zhp, 0, destroy_print_cb, cb, 0,15780);1579if (cb->cb_firstsnap != NULL) {1580uint64_t used = 0;1581if (err == 0) {1582err = lzc_snaprange_space(cb->cb_firstsnap,1583cb->cb_prevsnap, &used);1584}1585cb->cb_snapused += used;1586free(cb->cb_firstsnap);1587cb->cb_firstsnap = NULL;1588free(cb->cb_prevsnap);1589cb->cb_prevsnap = NULL;1590}1591return (err);1592}15931594static int1595snapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg)1596{1597destroy_cbdata_t *cb = arg;1598int err = 0;15991600/* Check for clones. */1601if (!cb->cb_doclones && !cb->cb_defer_destroy) {1602cb->cb_target = zhp;1603cb->cb_first = B_TRUE;1604err = zfs_iter_dependents_v2(zhp, 0, B_TRUE,1605destroy_check_dependent, cb);1606}16071608if (err == 0) {1609if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp)))1610nomem();1611}1612zfs_close(zhp);1613return (err);1614}16151616static int1617gather_snapshots(zfs_handle_t *zhp, void *arg)1618{1619destroy_cbdata_t *cb = arg;1620int err = 0;16211622err = zfs_iter_snapspec_v2(zhp, 0, cb->cb_snapspec,1623snapshot_to_nvl_cb, cb);1624if (err == ENOENT)1625err = 0;1626if (err != 0)1627goto out;16281629if (cb->cb_verbose) {1630err = destroy_print_snapshots(zhp, cb);1631if (err != 0)1632goto out;1633}16341635if (cb->cb_recurse)1636err = zfs_iter_filesystems_v2(zhp, 0, gather_snapshots, cb);16371638out:1639zfs_close(zhp);1640return (err);1641}16421643static int1644destroy_clones(destroy_cbdata_t *cb)1645{1646nvpair_t *pair;1647for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL);1648pair != NULL;1649pair = nvlist_next_nvpair(cb->cb_nvl, pair)) {1650zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair),1651ZFS_TYPE_SNAPSHOT);1652if (zhp != NULL) {1653boolean_t defer = cb->cb_defer_destroy;1654int err;16551656/*1657* We can't defer destroy non-snapshots, so set it to1658* false while destroying the clones.1659*/1660cb->cb_defer_destroy = B_FALSE;1661err = zfs_iter_dependents_v2(zhp, 0, B_FALSE,1662destroy_callback, cb);1663cb->cb_defer_destroy = defer;1664zfs_close(zhp);1665if (err != 0)1666return (err);1667}1668}1669return (0);1670}16711672static int1673zfs_do_destroy(int argc, char **argv)1674{1675destroy_cbdata_t cb = { 0 };1676int rv = 0;1677int err = 0;1678int c;1679zfs_handle_t *zhp = NULL;1680char *at, *pound;1681zfs_type_t type = ZFS_TYPE_DATASET;16821683/* check options */1684while ((c = getopt(argc, argv, "vpndfrR")) != -1) {1685switch (c) {1686case 'v':1687cb.cb_verbose = B_TRUE;1688break;1689case 'p':1690cb.cb_verbose = B_TRUE;1691cb.cb_parsable = B_TRUE;1692break;1693case 'n':1694cb.cb_dryrun = B_TRUE;1695break;1696case 'd':1697cb.cb_defer_destroy = B_TRUE;1698type = ZFS_TYPE_SNAPSHOT;1699break;1700case 'f':1701cb.cb_force = B_TRUE;1702break;1703case 'r':1704cb.cb_recurse = B_TRUE;1705break;1706case 'R':1707cb.cb_recurse = B_TRUE;1708cb.cb_doclones = B_TRUE;1709break;1710case '?':1711default:1712(void) fprintf(stderr, gettext("invalid option '%c'\n"),1713optopt);1714usage(B_FALSE);1715}1716}17171718argc -= optind;1719argv += optind;17201721/* check number of arguments */1722if (argc == 0) {1723(void) fprintf(stderr, gettext("missing dataset argument\n"));1724usage(B_FALSE);1725}1726if (argc > 1) {1727(void) fprintf(stderr, gettext("too many arguments\n"));1728usage(B_FALSE);1729}17301731at = strchr(argv[0], '@');1732pound = strchr(argv[0], '#');1733if (at != NULL) {17341735/* Build the list of snaps to destroy in cb_nvl. */1736cb.cb_nvl = fnvlist_alloc();17371738*at = '\0';1739zhp = zfs_open(g_zfs, argv[0],1740ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);1741if (zhp == NULL) {1742nvlist_free(cb.cb_nvl);1743return (1);1744}17451746cb.cb_snapspec = at + 1;1747if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 ||1748cb.cb_error) {1749rv = 1;1750goto out;1751}17521753if (nvlist_empty(cb.cb_nvl)) {1754(void) fprintf(stderr, gettext("could not find any "1755"snapshots to destroy; check snapshot names.\n"));1756rv = 1;1757goto out;1758}17591760if (cb.cb_verbose) {1761char buf[16];1762zfs_nicebytes(cb.cb_snapused, buf, sizeof (buf));1763if (cb.cb_parsable) {1764(void) printf("reclaim\t%llu\n",1765(u_longlong_t)cb.cb_snapused);1766} else if (cb.cb_dryrun) {1767(void) printf(gettext("would reclaim %s\n"),1768buf);1769} else {1770(void) printf(gettext("will reclaim %s\n"),1771buf);1772}1773}17741775if (!cb.cb_dryrun) {1776if (cb.cb_doclones) {1777cb.cb_batchedsnaps = fnvlist_alloc();1778err = destroy_clones(&cb);1779if (err == 0) {1780err = zfs_destroy_snaps_nvl(g_zfs,1781cb.cb_batchedsnaps, B_FALSE);1782}1783if (err != 0) {1784rv = 1;1785goto out;1786}1787}1788if (err == 0) {1789err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl,1790cb.cb_defer_destroy);1791}1792}17931794if (err != 0)1795rv = 1;1796} else if (pound != NULL) {1797int err;1798nvlist_t *nvl;17991800if (cb.cb_dryrun) {1801(void) fprintf(stderr,1802"dryrun is not supported with bookmark\n");1803return (-1);1804}18051806if (cb.cb_defer_destroy) {1807(void) fprintf(stderr,1808"defer destroy is not supported with bookmark\n");1809return (-1);1810}18111812if (cb.cb_recurse) {1813(void) fprintf(stderr,1814"recursive is not supported with bookmark\n");1815return (-1);1816}18171818/*1819* Unfortunately, zfs_bookmark() doesn't honor the1820* casesensitivity setting. However, we can't simply1821* remove this check, because lzc_destroy_bookmarks()1822* ignores non-existent bookmarks, so this is necessary1823* to get a proper error message.1824*/1825if (!zfs_bookmark_exists(argv[0])) {1826(void) fprintf(stderr, gettext("bookmark '%s' "1827"does not exist.\n"), argv[0]);1828return (1);1829}18301831nvl = fnvlist_alloc();1832fnvlist_add_boolean(nvl, argv[0]);18331834err = lzc_destroy_bookmarks(nvl, NULL);1835if (err != 0) {1836(void) zfs_standard_error(g_zfs, err,1837"cannot destroy bookmark");1838}18391840nvlist_free(nvl);18411842return (err);1843} else {1844/* Open the given dataset */1845if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL)1846return (1);18471848cb.cb_target = zhp;18491850/*1851* Perform an explicit check for pools before going any further.1852*/1853if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL &&1854zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {1855(void) fprintf(stderr, gettext("cannot destroy '%s': "1856"operation does not apply to pools\n"),1857zfs_get_name(zhp));1858(void) fprintf(stderr, gettext("use 'zfs destroy -r "1859"%s' to destroy all datasets in the pool\n"),1860zfs_get_name(zhp));1861(void) fprintf(stderr, gettext("use 'zpool destroy %s' "1862"to destroy the pool itself\n"), zfs_get_name(zhp));1863rv = 1;1864goto out;1865}18661867/*1868* Check for any dependents and/or clones.1869*/1870cb.cb_first = B_TRUE;1871if (!cb.cb_doclones && zfs_iter_dependents_v2(zhp, 0, B_TRUE,1872destroy_check_dependent, &cb) != 0) {1873rv = 1;1874goto out;1875}18761877if (cb.cb_error) {1878rv = 1;1879goto out;1880}1881cb.cb_batchedsnaps = fnvlist_alloc();1882if (zfs_iter_dependents_v2(zhp, 0, B_FALSE, destroy_callback,1883&cb) != 0) {1884rv = 1;1885goto out;1886}18871888/*1889* Do the real thing. The callback will close the1890* handle regardless of whether it succeeds or not.1891*/1892err = destroy_callback(zhp, &cb);1893zhp = NULL;1894if (err == 0) {1895err = zfs_destroy_snaps_nvl(g_zfs,1896cb.cb_batchedsnaps, cb.cb_defer_destroy);1897}1898if (err != 0 || cb.cb_error == B_TRUE)1899rv = 1;1900}19011902out:1903fnvlist_free(cb.cb_batchedsnaps);1904fnvlist_free(cb.cb_nvl);1905if (zhp != NULL)1906zfs_close(zhp);1907return (rv);1908}19091910static boolean_t1911is_recvd_column(zprop_get_cbdata_t *cbp)1912{1913int i;1914zfs_get_column_t col;19151916for (i = 0; i < ZFS_GET_NCOLS &&1917(col = cbp->cb_columns[i]) != GET_COL_NONE; i++)1918if (col == GET_COL_RECVD)1919return (B_TRUE);1920return (B_FALSE);1921}19221923/*1924* Generates an nvlist with output version for every command based on params.1925* Purpose of this is to add a version of JSON output, considering the schema1926* format might be updated for each command in future.1927*1928* Schema:1929*1930* "output_version": {1931* "command": string,1932* "vers_major": integer,1933* "vers_minor": integer,1934* }1935*/1936static nvlist_t *1937zfs_json_schema(int maj_v, int min_v)1938{1939nvlist_t *sch = NULL;1940nvlist_t *ov = NULL;1941char cmd[MAX_CMD_LEN];1942snprintf(cmd, MAX_CMD_LEN, "zfs %s", current_command->name);19431944sch = fnvlist_alloc();1945ov = fnvlist_alloc();1946fnvlist_add_string(ov, "command", cmd);1947fnvlist_add_uint32(ov, "vers_major", maj_v);1948fnvlist_add_uint32(ov, "vers_minor", min_v);1949fnvlist_add_nvlist(sch, "output_version", ov);1950fnvlist_free(ov);1951return (sch);1952}19531954static void1955fill_dataset_info(nvlist_t *list, zfs_handle_t *zhp, boolean_t as_int)1956{1957char createtxg[ZFS_MAXPROPLEN];1958zfs_type_t type = zfs_get_type(zhp);1959nvlist_add_string(list, "name", zfs_get_name(zhp));19601961switch (type) {1962case ZFS_TYPE_FILESYSTEM:1963fnvlist_add_string(list, "type", "FILESYSTEM");1964break;1965case ZFS_TYPE_VOLUME:1966fnvlist_add_string(list, "type", "VOLUME");1967break;1968case ZFS_TYPE_SNAPSHOT:1969fnvlist_add_string(list, "type", "SNAPSHOT");1970break;1971case ZFS_TYPE_POOL:1972fnvlist_add_string(list, "type", "POOL");1973break;1974case ZFS_TYPE_BOOKMARK:1975fnvlist_add_string(list, "type", "BOOKMARK");1976break;1977default:1978fnvlist_add_string(list, "type", "UNKNOWN");1979break;1980}19811982if (type != ZFS_TYPE_POOL)1983fnvlist_add_string(list, "pool", zfs_get_pool_name(zhp));19841985if (as_int) {1986fnvlist_add_uint64(list, "createtxg", zfs_prop_get_int(zhp,1987ZFS_PROP_CREATETXG));1988} else {1989if (zfs_prop_get(zhp, ZFS_PROP_CREATETXG, createtxg,1990sizeof (createtxg), NULL, NULL, 0, B_TRUE) == 0)1991fnvlist_add_string(list, "createtxg", createtxg);1992}19931994if (type == ZFS_TYPE_SNAPSHOT) {1995char *snap = strdup(zfs_get_name(zhp));1996char *ds = strsep(&snap, "@");1997fnvlist_add_string(list, "dataset", ds);1998fnvlist_add_string(list, "snapshot_name", snap);1999free(ds);2000}2001}20022003/*2004* zfs get [-rHp] [-j [--json-int]] [-o all | field[,field]...]2005* [-s source[,source]...]2006* < all | property[,property]... > < fs | snap | vol > ...2007*2008* -r recurse over any child datasets2009* -H scripted mode. Headers are stripped, and fields are separated2010* by tabs instead of spaces.2011* -o Set of fields to display. One of "name,property,value,2012* received,source". Default is "name,property,value,source".2013* "all" is an alias for all five.2014* -s Set of sources to allow. One of2015* "local,default,inherited,received,temporary,none". Default is2016* all six.2017* -p Display values in parsable (literal) format.2018* -j Display output in JSON format.2019* --json-int Display numbers as integers instead of strings.2020*2021* Prints properties for the given datasets. The user can control which2022* columns to display as well as which property types to allow.2023*/20242025/*2026* Invoked to display the properties for a single dataset.2027*/2028static int2029get_callback(zfs_handle_t *zhp, void *data)2030{2031char buf[ZFS_MAXPROPLEN];2032char rbuf[ZFS_MAXPROPLEN];2033zprop_source_t sourcetype;2034char source[ZFS_MAX_DATASET_NAME_LEN];2035zprop_get_cbdata_t *cbp = data;2036nvlist_t *user_props = zfs_get_user_props(zhp);2037zprop_list_t *pl = cbp->cb_proplist;2038nvlist_t *propval;2039nvlist_t *item, *d = NULL, *props = NULL;2040const char *strval;2041const char *sourceval;2042boolean_t received = is_recvd_column(cbp);2043int err = 0;20442045if (cbp->cb_json) {2046d = fnvlist_lookup_nvlist(cbp->cb_jsobj, "datasets");2047if (d == NULL) {2048fprintf(stderr, "datasets obj not found.\n");2049exit(1);2050}2051props = fnvlist_alloc();2052}20532054for (; pl != NULL; pl = pl->pl_next) {2055char *recvdval = NULL;2056/*2057* Skip the special fake placeholder. This will also skip over2058* the name property when 'all' is specified.2059*/2060if (pl->pl_prop == ZFS_PROP_NAME &&2061pl == cbp->cb_proplist)2062continue;20632064if (pl->pl_prop != ZPROP_USERPROP) {2065if (zfs_prop_get(zhp, pl->pl_prop, buf,2066sizeof (buf), &sourcetype, source,2067sizeof (source),2068cbp->cb_literal) != 0) {2069if (pl->pl_all)2070continue;2071if (!zfs_prop_valid_for_type(pl->pl_prop,2072ZFS_TYPE_DATASET, B_FALSE)) {2073(void) fprintf(stderr,2074gettext("No such property '%s'\n"),2075zfs_prop_to_name(pl->pl_prop));2076continue;2077}2078sourcetype = ZPROP_SRC_NONE;2079(void) strlcpy(buf, "-", sizeof (buf));2080}20812082if (received && (zfs_prop_get_recvd(zhp,2083zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf),2084cbp->cb_literal) == 0))2085recvdval = rbuf;20862087err = zprop_collect_property(zfs_get_name(zhp), cbp,2088zfs_prop_to_name(pl->pl_prop),2089buf, sourcetype, source, recvdval, props);2090} else if (zfs_prop_userquota(pl->pl_user_prop)) {2091sourcetype = ZPROP_SRC_LOCAL;20922093if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,2094buf, sizeof (buf), cbp->cb_literal) != 0) {2095sourcetype = ZPROP_SRC_NONE;2096(void) strlcpy(buf, "-", sizeof (buf));2097}20982099err = zprop_collect_property(zfs_get_name(zhp), cbp,2100pl->pl_user_prop, buf, sourcetype, source, NULL,2101props);2102} else if (zfs_prop_written(pl->pl_user_prop)) {2103sourcetype = ZPROP_SRC_LOCAL;21042105if (zfs_prop_get_written(zhp, pl->pl_user_prop,2106buf, sizeof (buf), cbp->cb_literal) != 0) {2107sourcetype = ZPROP_SRC_NONE;2108(void) strlcpy(buf, "-", sizeof (buf));2109}21102111err = zprop_collect_property(zfs_get_name(zhp), cbp,2112pl->pl_user_prop, buf, sourcetype, source, NULL,2113props);2114} else {2115if (nvlist_lookup_nvlist(user_props,2116pl->pl_user_prop, &propval) != 0) {2117if (pl->pl_all)2118continue;2119sourcetype = ZPROP_SRC_NONE;2120strval = "-";2121} else {2122strval = fnvlist_lookup_string(propval,2123ZPROP_VALUE);2124sourceval = fnvlist_lookup_string(propval,2125ZPROP_SOURCE);21262127if (strcmp(sourceval,2128zfs_get_name(zhp)) == 0) {2129sourcetype = ZPROP_SRC_LOCAL;2130} else if (strcmp(sourceval,2131ZPROP_SOURCE_VAL_RECVD) == 0) {2132sourcetype = ZPROP_SRC_RECEIVED;2133} else {2134sourcetype = ZPROP_SRC_INHERITED;2135(void) strlcpy(source,2136sourceval, sizeof (source));2137}2138}21392140if (received && (zfs_prop_get_recvd(zhp,2141pl->pl_user_prop, rbuf, sizeof (rbuf),2142cbp->cb_literal) == 0))2143recvdval = rbuf;21442145err = zprop_collect_property(zfs_get_name(zhp), cbp,2146pl->pl_user_prop, strval, sourcetype,2147source, recvdval, props);2148}2149if (err != 0)2150return (err);2151}21522153if (cbp->cb_json) {2154if (!nvlist_empty(props)) {2155item = fnvlist_alloc();2156fill_dataset_info(item, zhp, cbp->cb_json_as_int);2157fnvlist_add_nvlist(item, "properties", props);2158fnvlist_add_nvlist(d, zfs_get_name(zhp), item);2159fnvlist_free(props);2160fnvlist_free(item);2161} else {2162fnvlist_free(props);2163}2164}21652166return (0);2167}21682169static int2170zfs_do_get(int argc, char **argv)2171{2172zprop_get_cbdata_t cb = { 0 };2173int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS;2174int types = ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK;2175char *fields;2176int ret = 0;2177int limit = 0;2178zprop_list_t fake_name = { 0 };2179nvlist_t *data;21802181/*2182* Set up default columns and sources.2183*/2184cb.cb_sources = ZPROP_SRC_ALL;2185cb.cb_columns[0] = GET_COL_NAME;2186cb.cb_columns[1] = GET_COL_PROPERTY;2187cb.cb_columns[2] = GET_COL_VALUE;2188cb.cb_columns[3] = GET_COL_SOURCE;2189cb.cb_type = ZFS_TYPE_DATASET;21902191struct option long_options[] = {2192{"json", no_argument, NULL, 'j'},2193{"json-int", no_argument, NULL, ZFS_OPTION_JSON_NUMS_AS_INT},2194{0, 0, 0, 0}2195};21962197/* check options */2198while ((c = getopt_long(argc, argv, ":d:o:s:jrt:Hp", long_options,2199NULL)) != -1) {2200switch (c) {2201case 'p':2202cb.cb_literal = B_TRUE;2203break;2204case 'd':2205limit = parse_depth(optarg, &flags);2206break;2207case 'r':2208flags |= ZFS_ITER_RECURSE;2209break;2210case 'H':2211cb.cb_scripted = B_TRUE;2212break;2213case 'j':2214cb.cb_json = B_TRUE;2215cb.cb_jsobj = zfs_json_schema(0, 1);2216data = fnvlist_alloc();2217fnvlist_add_nvlist(cb.cb_jsobj, "datasets", data);2218fnvlist_free(data);2219break;2220case ZFS_OPTION_JSON_NUMS_AS_INT:2221cb.cb_json_as_int = B_TRUE;2222cb.cb_literal = B_TRUE;2223break;2224case ':':2225(void) fprintf(stderr, gettext("missing argument for "2226"'%c' option\n"), optopt);2227usage(B_FALSE);2228break;2229case 'o':2230/*2231* Process the set of columns to display. We zero out2232* the structure to give us a blank slate.2233*/2234memset(&cb.cb_columns, 0, sizeof (cb.cb_columns));22352236i = 0;2237for (char *tok; (tok = strsep(&optarg, ",")); ) {2238static const char *const col_subopts[] =2239{ "name", "property", "value",2240"received", "source", "all" };2241static const zfs_get_column_t col_subopt_col[] =2242{ GET_COL_NAME, GET_COL_PROPERTY, GET_COL_VALUE,2243GET_COL_RECVD, GET_COL_SOURCE };2244static const int col_subopt_flags[] =2245{ 0, 0, 0, ZFS_ITER_RECVD_PROPS, 0 };22462247if (i == ZFS_GET_NCOLS) {2248(void) fprintf(stderr, gettext("too "2249"many fields given to -o "2250"option\n"));2251usage(B_FALSE);2252}22532254for (c = 0; c < ARRAY_SIZE(col_subopts); ++c)2255if (strcmp(tok, col_subopts[c]) == 0)2256goto found;22572258(void) fprintf(stderr,2259gettext("invalid column name '%s'\n"), tok);2260usage(B_FALSE);22612262found:2263if (c >= 5) {2264if (i > 0) {2265(void) fprintf(stderr,2266gettext("\"all\" conflicts "2267"with specific fields "2268"given to -o option\n"));2269usage(B_FALSE);2270}22712272memcpy(cb.cb_columns, col_subopt_col,2273sizeof (col_subopt_col));2274flags |= ZFS_ITER_RECVD_PROPS;2275i = ZFS_GET_NCOLS;2276} else {2277cb.cb_columns[i++] = col_subopt_col[c];2278flags |= col_subopt_flags[c];2279}2280}2281break;22822283case 's':2284cb.cb_sources = 0;22852286for (char *tok; (tok = strsep(&optarg, ",")); ) {2287static const char *const source_opt[] = {2288"local", "default",2289"inherited", "received",2290"temporary", "none" };2291static const int source_flg[] = {2292ZPROP_SRC_LOCAL, ZPROP_SRC_DEFAULT,2293ZPROP_SRC_INHERITED, ZPROP_SRC_RECEIVED,2294ZPROP_SRC_TEMPORARY, ZPROP_SRC_NONE };22952296for (i = 0; i < ARRAY_SIZE(source_opt); ++i)2297if (strcmp(tok, source_opt[i]) == 0) {2298cb.cb_sources |= source_flg[i];2299goto found2;2300}23012302(void) fprintf(stderr,2303gettext("invalid source '%s'\n"), tok);2304usage(B_FALSE);2305found2:;2306}2307break;23082309case 't':2310types = 0;2311flags &= ~ZFS_ITER_PROP_LISTSNAPS;23122313for (char *tok; (tok = strsep(&optarg, ",")); ) {2314static const char *const type_opts[] = {2315"filesystem",2316"fs",2317"volume",2318"vol",2319"snapshot",2320"snap",2321"bookmark",2322"all"2323};2324static const int type_types[] = {2325ZFS_TYPE_FILESYSTEM,2326ZFS_TYPE_FILESYSTEM,2327ZFS_TYPE_VOLUME,2328ZFS_TYPE_VOLUME,2329ZFS_TYPE_SNAPSHOT,2330ZFS_TYPE_SNAPSHOT,2331ZFS_TYPE_BOOKMARK,2332ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK2333};23342335for (i = 0; i < ARRAY_SIZE(type_opts); ++i)2336if (strcmp(tok, type_opts[i]) == 0) {2337types |= type_types[i];2338goto found3;2339}23402341(void) fprintf(stderr,2342gettext("invalid type '%s'\n"), tok);2343usage(B_FALSE);2344found3:;2345}2346break;2347case '?':2348(void) fprintf(stderr, gettext("invalid option '%c'\n"),2349optopt);2350usage(B_FALSE);2351}2352}23532354argc -= optind;2355argv += optind;23562357if (argc < 1) {2358(void) fprintf(stderr, gettext("missing property "2359"argument\n"));2360usage(B_FALSE);2361}23622363if (!cb.cb_json && cb.cb_json_as_int) {2364(void) fprintf(stderr, gettext("'--json-int' only works with"2365" '-j' option\n"));2366usage(B_FALSE);2367}23682369fields = argv[0];23702371/*2372* Handle users who want to get all snapshots or bookmarks2373* of a dataset (ex. 'zfs get -t snapshot refer <dataset>').2374*/2375if ((types == ZFS_TYPE_SNAPSHOT || types == ZFS_TYPE_BOOKMARK) &&2376argc > 1 && (flags & ZFS_ITER_RECURSE) == 0 && limit == 0) {2377flags |= (ZFS_ITER_DEPTH_LIMIT | ZFS_ITER_RECURSE);2378limit = 1;2379}23802381if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)2382!= 0)2383usage(B_FALSE);23842385argc--;2386argv++;23872388/*2389* As part of zfs_expand_proplist(), we keep track of the maximum column2390* width for each property. For the 'NAME' (and 'SOURCE') columns, we2391* need to know the maximum name length. However, the user likely did2392* not specify 'name' as one of the properties to fetch, so we need to2393* make sure we always include at least this property for2394* print_get_headers() to work properly.2395*/2396if (cb.cb_proplist != NULL) {2397fake_name.pl_prop = ZFS_PROP_NAME;2398fake_name.pl_width = strlen(gettext("NAME"));2399fake_name.pl_next = cb.cb_proplist;2400cb.cb_proplist = &fake_name;2401}24022403cb.cb_first = B_TRUE;24042405/* run for each object */2406ret = zfs_for_each(argc, argv, flags, types, NULL,2407&cb.cb_proplist, limit, get_callback, &cb);24082409if (ret == 0 && cb.cb_json)2410zcmd_print_json(cb.cb_jsobj);2411else if (ret != 0 && cb.cb_json)2412nvlist_free(cb.cb_jsobj);24132414if (cb.cb_proplist == &fake_name)2415zprop_free_list(fake_name.pl_next);2416else2417zprop_free_list(cb.cb_proplist);24182419return (ret);2420}24212422/*2423* inherit [-rS] <property> <fs|vol> ...2424*2425* -r Recurse over all children2426* -S Revert to received value, if any2427*2428* For each dataset specified on the command line, inherit the given property2429* from its parent. Inheriting a property at the pool level will cause it to2430* use the default value. The '-r' flag will recurse over all children, and is2431* useful for setting a property on a hierarchy-wide basis, regardless of any2432* local modifications for each dataset.2433*/24342435typedef struct inherit_cbdata {2436const char *cb_propname;2437boolean_t cb_received;2438} inherit_cbdata_t;24392440static int2441inherit_recurse_cb(zfs_handle_t *zhp, void *data)2442{2443inherit_cbdata_t *cb = data;2444zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname);24452446/*2447* If we're doing it recursively, then ignore properties that2448* are not valid for this type of dataset.2449*/2450if (prop != ZPROP_INVAL &&2451!zfs_prop_valid_for_type(prop, zfs_get_type(zhp), B_FALSE))2452return (0);24532454return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);2455}24562457static int2458inherit_cb(zfs_handle_t *zhp, void *data)2459{2460inherit_cbdata_t *cb = data;24612462return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);2463}24642465static int2466zfs_do_inherit(int argc, char **argv)2467{2468int c;2469zfs_prop_t prop;2470inherit_cbdata_t cb = { 0 };2471char *propname;2472int ret = 0;2473int flags = 0;2474boolean_t received = B_FALSE;24752476/* check options */2477while ((c = getopt(argc, argv, "rS")) != -1) {2478switch (c) {2479case 'r':2480flags |= ZFS_ITER_RECURSE;2481break;2482case 'S':2483received = B_TRUE;2484break;2485case '?':2486default:2487(void) fprintf(stderr, gettext("invalid option '%c'\n"),2488optopt);2489usage(B_FALSE);2490}2491}24922493argc -= optind;2494argv += optind;24952496/* check number of arguments */2497if (argc < 1) {2498(void) fprintf(stderr, gettext("missing property argument\n"));2499usage(B_FALSE);2500}2501if (argc < 2) {2502(void) fprintf(stderr, gettext("missing dataset argument\n"));2503usage(B_FALSE);2504}25052506propname = argv[0];2507argc--;2508argv++;25092510if ((prop = zfs_name_to_prop(propname)) != ZPROP_USERPROP) {2511if (zfs_prop_readonly(prop)) {2512(void) fprintf(stderr, gettext(2513"%s property is read-only\n"),2514propname);2515return (1);2516}2517if (!zfs_prop_inheritable(prop) && !received) {2518(void) fprintf(stderr, gettext("'%s' property cannot "2519"be inherited\n"), propname);2520if (prop == ZFS_PROP_QUOTA ||2521prop == ZFS_PROP_RESERVATION ||2522prop == ZFS_PROP_REFQUOTA ||2523prop == ZFS_PROP_REFRESERVATION) {2524(void) fprintf(stderr, gettext("use 'zfs set "2525"%s=none' to clear\n"), propname);2526(void) fprintf(stderr, gettext("use 'zfs "2527"inherit -S %s' to revert to received "2528"value\n"), propname);2529}2530return (1);2531}2532if (received && (prop == ZFS_PROP_VOLSIZE ||2533prop == ZFS_PROP_VERSION)) {2534(void) fprintf(stderr, gettext("'%s' property cannot "2535"be reverted to a received value\n"), propname);2536return (1);2537}2538} else if (!zfs_prop_user(propname)) {2539(void) fprintf(stderr, gettext("invalid property '%s'\n"),2540propname);2541usage(B_FALSE);2542}25432544cb.cb_propname = propname;2545cb.cb_received = received;25462547if (flags & ZFS_ITER_RECURSE) {2548ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,2549NULL, NULL, 0, inherit_recurse_cb, &cb);2550} else {2551ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,2552NULL, NULL, 0, inherit_cb, &cb);2553}25542555return (ret);2556}25572558typedef struct upgrade_cbdata {2559uint64_t cb_numupgraded;2560uint64_t cb_numsamegraded;2561uint64_t cb_numfailed;2562uint64_t cb_version;2563boolean_t cb_newer;2564boolean_t cb_foundone;2565char cb_lastfs[ZFS_MAX_DATASET_NAME_LEN];2566} upgrade_cbdata_t;25672568static int2569same_pool(zfs_handle_t *zhp, const char *name)2570{2571int len1 = strcspn(name, "/@");2572const char *zhname = zfs_get_name(zhp);2573int len2 = strcspn(zhname, "/@");25742575if (len1 != len2)2576return (B_FALSE);2577return (strncmp(name, zhname, len1) == 0);2578}25792580static int2581upgrade_list_callback(zfs_handle_t *zhp, void *data)2582{2583upgrade_cbdata_t *cb = data;2584int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);25852586/* list if it's old/new */2587if ((!cb->cb_newer && version < ZPL_VERSION) ||2588(cb->cb_newer && version > ZPL_VERSION)) {2589char *str;2590if (cb->cb_newer) {2591str = gettext("The following filesystems are "2592"formatted using a newer software version and\n"2593"cannot be accessed on the current system.\n\n");2594} else {2595str = gettext("The following filesystems are "2596"out of date, and can be upgraded. After being\n"2597"upgraded, these filesystems (and any 'zfs send' "2598"streams generated from\n"2599"subsequent snapshots) will no longer be "2600"accessible by older software versions.\n\n");2601}26022603if (!cb->cb_foundone) {2604(void) puts(str);2605(void) printf(gettext("VER FILESYSTEM\n"));2606(void) printf(gettext("--- ------------\n"));2607cb->cb_foundone = B_TRUE;2608}26092610(void) printf("%2u %s\n", version, zfs_get_name(zhp));2611}26122613return (0);2614}26152616static int2617upgrade_set_callback(zfs_handle_t *zhp, void *data)2618{2619upgrade_cbdata_t *cb = data;2620int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);2621int needed_spa_version;2622int spa_version;26232624if (zfs_spa_version(zhp, &spa_version) < 0)2625return (-1);26262627needed_spa_version = zfs_spa_version_map(cb->cb_version);26282629if (needed_spa_version < 0)2630return (-1);26312632if (spa_version < needed_spa_version) {2633/* can't upgrade */2634(void) printf(gettext("%s: can not be "2635"upgraded; the pool version needs to first "2636"be upgraded\nto version %d\n\n"),2637zfs_get_name(zhp), needed_spa_version);2638cb->cb_numfailed++;2639return (0);2640}26412642/* upgrade */2643if (version < cb->cb_version) {2644char verstr[24];2645(void) snprintf(verstr, sizeof (verstr),2646"%llu", (u_longlong_t)cb->cb_version);2647if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {2648/*2649* If they did "zfs upgrade -a", then we could2650* be doing ioctls to different pools. We need2651* to log this history once to each pool, and bypass2652* the normal history logging that happens in main().2653*/2654(void) zpool_log_history(g_zfs, history_str);2655log_history = B_FALSE;2656}2657if (zfs_prop_set(zhp, "version", verstr) == 0)2658cb->cb_numupgraded++;2659else2660cb->cb_numfailed++;2661(void) strlcpy(cb->cb_lastfs, zfs_get_name(zhp),2662sizeof (cb->cb_lastfs));2663} else if (version > cb->cb_version) {2664/* can't downgrade */2665(void) printf(gettext("%s: can not be downgraded; "2666"it is already at version %u\n"),2667zfs_get_name(zhp), version);2668cb->cb_numfailed++;2669} else {2670cb->cb_numsamegraded++;2671}2672return (0);2673}26742675/*2676* zfs upgrade2677* zfs upgrade -v2678* zfs upgrade [-r] [-V <version>] <-a | filesystem>2679*/2680static int2681zfs_do_upgrade(int argc, char **argv)2682{2683boolean_t all = B_FALSE;2684boolean_t showversions = B_FALSE;2685int ret = 0;2686upgrade_cbdata_t cb = { 0 };2687int c;2688int flags = ZFS_ITER_ARGS_CAN_BE_PATHS;26892690/* check options */2691while ((c = getopt(argc, argv, "rvV:a")) != -1) {2692switch (c) {2693case 'r':2694flags |= ZFS_ITER_RECURSE;2695break;2696case 'v':2697showversions = B_TRUE;2698break;2699case 'V':2700if (zfs_prop_string_to_index(ZFS_PROP_VERSION,2701optarg, &cb.cb_version) != 0) {2702(void) fprintf(stderr,2703gettext("invalid version %s\n"), optarg);2704usage(B_FALSE);2705}2706break;2707case 'a':2708all = B_TRUE;2709break;2710case '?':2711default:2712(void) fprintf(stderr, gettext("invalid option '%c'\n"),2713optopt);2714usage(B_FALSE);2715}2716}27172718argc -= optind;2719argv += optind;27202721if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version))2722usage(B_FALSE);2723if (showversions && (flags & ZFS_ITER_RECURSE || all ||2724cb.cb_version || argc))2725usage(B_FALSE);2726if ((all || argc) && (showversions))2727usage(B_FALSE);2728if (all && argc)2729usage(B_FALSE);27302731if (showversions) {2732/* Show info on available versions. */2733(void) printf(gettext("The following filesystem versions are "2734"supported:\n\n"));2735(void) printf(gettext("VER DESCRIPTION\n"));2736(void) printf("--- -----------------------------------------"2737"---------------\n");2738(void) printf(gettext(" 1 Initial ZFS filesystem version\n"));2739(void) printf(gettext(" 2 Enhanced directory entries\n"));2740(void) printf(gettext(" 3 Case insensitive and filesystem "2741"user identifier (FUID)\n"));2742(void) printf(gettext(" 4 userquota, groupquota "2743"properties\n"));2744(void) printf(gettext(" 5 System attributes\n"));2745(void) printf(gettext("\nFor more information on a particular "2746"version, including supported releases,\n"));2747(void) printf("see the ZFS Administration Guide.\n\n");2748ret = 0;2749} else if (argc || all) {2750/* Upgrade filesystems */2751if (cb.cb_version == 0)2752cb.cb_version = ZPL_VERSION;2753ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM,2754NULL, NULL, 0, upgrade_set_callback, &cb);2755(void) printf(gettext("%llu filesystems upgraded\n"),2756(u_longlong_t)cb.cb_numupgraded);2757if (cb.cb_numsamegraded) {2758(void) printf(gettext("%llu filesystems already at "2759"this version\n"),2760(u_longlong_t)cb.cb_numsamegraded);2761}2762if (cb.cb_numfailed != 0)2763ret = 1;2764} else {2765/* List old-version filesystems */2766boolean_t found;2767(void) printf(gettext("This system is currently running "2768"ZFS filesystem version %llu.\n\n"), ZPL_VERSION);27692770flags |= ZFS_ITER_RECURSE;2771ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,2772NULL, NULL, 0, upgrade_list_callback, &cb);27732774found = cb.cb_foundone;2775cb.cb_foundone = B_FALSE;2776cb.cb_newer = B_TRUE;27772778ret |= zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,2779NULL, NULL, 0, upgrade_list_callback, &cb);27802781if (!cb.cb_foundone && !found) {2782(void) printf(gettext("All filesystems are "2783"formatted with the current version.\n"));2784}2785}27862787return (ret);2788}27892790/*2791* zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...]2792* [-S field [-S field]...] [-t type[,...]]2793* filesystem | snapshot | path2794* zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...]2795* [-S field [-S field]...] [-t type[,...]]2796* filesystem | snapshot | path2797* zfs projectspace [-Hp] [-o field[,...]] [-s field [-s field]...]2798* [-S field [-S field]...] filesystem | snapshot | path2799*2800* -H Scripted mode; elide headers and separate columns by tabs.2801* -i Translate SID to POSIX ID.2802* -n Print numeric ID instead of user/group name.2803* -o Control which fields to display.2804* -p Use exact (parsable) numeric output.2805* -s Specify sort columns, descending order.2806* -S Specify sort columns, ascending order.2807* -t Control which object types to display.2808*2809* Displays space consumed by, and quotas on, each user in the specified2810* filesystem or snapshot.2811*/28122813/* us_field_types, us_field_hdr and us_field_names should be kept in sync */2814enum us_field_types {2815USFIELD_TYPE,2816USFIELD_NAME,2817USFIELD_USED,2818USFIELD_QUOTA,2819USFIELD_OBJUSED,2820USFIELD_OBJQUOTA2821};2822static const char *const us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA",2823"OBJUSED", "OBJQUOTA" };2824static const char *const us_field_names[] = { "type", "name", "used", "quota",2825"objused", "objquota" };2826#define USFIELD_LAST (sizeof (us_field_names) / sizeof (char *))28272828#define USTYPE_PSX_GRP (1 << 0)2829#define USTYPE_PSX_USR (1 << 1)2830#define USTYPE_SMB_GRP (1 << 2)2831#define USTYPE_SMB_USR (1 << 3)2832#define USTYPE_PROJ (1 << 4)2833#define USTYPE_ALL \2834(USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR | \2835USTYPE_PROJ)28362837static int us_type_bits[] = {2838USTYPE_PSX_GRP,2839USTYPE_PSX_USR,2840USTYPE_SMB_GRP,2841USTYPE_SMB_USR,2842USTYPE_ALL2843};2844static const char *const us_type_names[] = { "posixgroup", "posixuser",2845"smbgroup", "smbuser", "all" };28462847typedef struct us_cbdata us_cbdata_t;2848typedef struct us_node {2849nvlist_t *usn_nvl;2850us_cbdata_t *usn_cbdata;2851avl_node_t usn_avlnode;2852list_node_t usn_listnode;2853} us_node_t;28542855struct us_cbdata {2856nvlist_t **cb_nvlp;2857avl_tree_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};28652866static boolean_t us_populated = B_FALSE;28672868static int2869us_field_index(const char *field)2870{2871for (int i = 0; i < USFIELD_LAST; i++) {2872if (strcmp(field, us_field_names[i]) == 0)2873return (i);2874}28752876return (-1);2877}28782879static int2880us_compare(const void *larg, const void *rarg)2881{2882const us_node_t *l = larg;2883const us_node_t *r = rarg;2884zfs_sort_column_t *sortcol = l->usn_cbdata->cb_sortcol;2885boolean_t numname = l->usn_cbdata->cb_numname;2886nvlist_t *lnvl = l->usn_nvl;2887nvlist_t *rnvl = r->usn_nvl;2888int rc = 0;2889boolean_t lvb, rvb;28902891for (; sortcol != NULL; sortcol = sortcol->sc_next) {2892const char *lvstr = "";2893const char *rvstr = "";2894uint32_t lv32 = 0;2895uint32_t rv32 = 0;2896uint64_t lv64 = 0;2897uint64_t rv64 = 0;2898zfs_prop_t prop = sortcol->sc_prop;2899const char *propname = NULL;2900boolean_t reverse = sortcol->sc_reverse;29012902switch (prop) {2903case ZFS_PROP_TYPE:2904propname = "type";2905(void) nvlist_lookup_uint32(lnvl, propname, &lv32);2906(void) nvlist_lookup_uint32(rnvl, propname, &rv32);2907if (rv32 != lv32)2908rc = (rv32 < lv32) ? 1 : -1;2909break;2910case ZFS_PROP_NAME:2911propname = "name";2912if (numname) {2913compare_nums:2914(void) nvlist_lookup_uint64(lnvl, propname,2915&lv64);2916(void) nvlist_lookup_uint64(rnvl, propname,2917&rv64);2918if (rv64 != lv64)2919rc = (rv64 < lv64) ? 1 : -1;2920} else {2921if ((nvlist_lookup_string(lnvl, propname,2922&lvstr) == ENOENT) ||2923(nvlist_lookup_string(rnvl, propname,2924&rvstr) == ENOENT)) {2925goto compare_nums;2926}2927rc = strcmp(lvstr, rvstr);2928}2929break;2930case ZFS_PROP_USED:2931case ZFS_PROP_QUOTA:2932if (!us_populated)2933break;2934if (prop == ZFS_PROP_USED)2935propname = "used";2936else2937propname = "quota";2938(void) nvlist_lookup_uint64(lnvl, propname, &lv64);2939(void) nvlist_lookup_uint64(rnvl, propname, &rv64);2940if (rv64 != lv64)2941rc = (rv64 < lv64) ? 1 : -1;2942break;29432944default:2945break;2946}29472948if (rc != 0) {2949if (rc < 0)2950return (reverse ? 1 : -1);2951else2952return (reverse ? -1 : 1);2953}2954}29552956/*2957* If entries still seem to be the same, check if they are of the same2958* type (smbentity is added only if we are doing SID to POSIX ID2959* translation where we can have duplicate type/name combinations).2960*/2961if (nvlist_lookup_boolean_value(lnvl, "smbentity", &lvb) == 0 &&2962nvlist_lookup_boolean_value(rnvl, "smbentity", &rvb) == 0 &&2963lvb != rvb)2964return (lvb < rvb ? -1 : 1);29652966return (0);2967}29682969static boolean_t2970zfs_prop_is_user(unsigned p)2971{2972return (p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA ||2973p == ZFS_PROP_USEROBJUSED || p == ZFS_PROP_USEROBJQUOTA);2974}29752976static boolean_t2977zfs_prop_is_group(unsigned p)2978{2979return (p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA ||2980p == ZFS_PROP_GROUPOBJUSED || p == ZFS_PROP_GROUPOBJQUOTA);2981}29822983static boolean_t2984zfs_prop_is_project(unsigned p)2985{2986return (p == ZFS_PROP_PROJECTUSED || p == ZFS_PROP_PROJECTQUOTA ||2987p == ZFS_PROP_PROJECTOBJUSED || p == ZFS_PROP_PROJECTOBJQUOTA);2988}29892990static inline const char *2991us_type2str(unsigned field_type)2992{2993switch (field_type) {2994case USTYPE_PSX_USR:2995return ("POSIX User");2996case USTYPE_PSX_GRP:2997return ("POSIX Group");2998case USTYPE_SMB_USR:2999return ("SMB User");3000case USTYPE_SMB_GRP:3001return ("SMB Group");3002case USTYPE_PROJ:3003return ("Project");3004default:3005return ("Undefined");3006}3007}30083009static int3010userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space,3011uint64_t default_quota)3012{3013us_cbdata_t *cb = (us_cbdata_t *)arg;3014zfs_userquota_prop_t prop = cb->cb_prop;3015char *name = NULL;3016const char *propname;3017char sizebuf[32];3018us_node_t *node;3019avl_tree_t *avl = &cb->cb_avl;3020avl_index_t idx;3021nvlist_t *props;3022us_node_t *n;3023unsigned type = 0;3024const char *typestr;3025size_t namelen;3026size_t typelen;3027size_t sizelen;3028int typeidx, nameidx, sizeidx;3029boolean_t smbentity = B_FALSE;30303031if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)3032nomem();3033node = safe_malloc(sizeof (us_node_t));3034node->usn_cbdata = cb;3035node->usn_nvl = props;30363037if (domain != NULL && domain[0] != '\0') {3038#ifdef HAVE_IDMAP3039/* SMB */3040char sid[MAXNAMELEN + 32];3041uid_t id;3042uint64_t classes;3043int err;3044directory_error_t e;30453046smbentity = B_TRUE;30473048(void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid);30493050if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {3051type = USTYPE_SMB_GRP;3052err = sid_to_id(sid, B_FALSE, &id);3053} else {3054type = USTYPE_SMB_USR;3055err = sid_to_id(sid, B_TRUE, &id);3056}30573058if (err == 0) {3059rid = id;3060if (!cb->cb_sid2posix) {3061e = directory_name_from_sid(NULL, sid, &name,3062&classes);3063if (e != NULL)3064directory_error_free(e);3065if (name == NULL)3066name = sid;3067}3068}3069#else3070nvlist_free(props);3071free(node);30723073return (-1);3074#endif /* HAVE_IDMAP */3075}30763077if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') {3078/* POSIX or -i */3079if (zfs_prop_is_group(prop)) {3080type = USTYPE_PSX_GRP;3081if (!cb->cb_numname) {3082struct group *g;30833084if ((g = getgrgid(rid)) != NULL)3085name = g->gr_name;3086}3087} else if (zfs_prop_is_user(prop)) {3088type = USTYPE_PSX_USR;3089if (!cb->cb_numname) {3090struct passwd *p;30913092if ((p = getpwuid(rid)) != NULL)3093name = p->pw_name;3094}3095} else {3096type = USTYPE_PROJ;3097}3098}30993100/*3101* Make sure that the type/name combination is unique when doing3102* SID to POSIX ID translation (hence changing the type from SMB to3103* POSIX).3104*/3105if (cb->cb_sid2posix &&3106nvlist_add_boolean_value(props, "smbentity", smbentity) != 0)3107nomem();31083109/* Calculate/update width of TYPE field */3110typestr = us_type2str(type);3111typelen = strlen(gettext(typestr));3112typeidx = us_field_index("type");3113if (typelen > cb->cb_width[typeidx])3114cb->cb_width[typeidx] = typelen;3115if (nvlist_add_uint32(props, "type", type) != 0)3116nomem();31173118/* Calculate/update width of NAME field */3119if ((cb->cb_numname && cb->cb_sid2posix) || name == NULL) {3120if (nvlist_add_uint64(props, "name", rid) != 0)3121nomem();3122namelen = snprintf(NULL, 0, "%u", rid);3123} else {3124if (nvlist_add_string(props, "name", name) != 0)3125nomem();3126namelen = strlen(name);3127}3128nameidx = us_field_index("name");3129if (nameidx >= 0 && namelen > cb->cb_width[nameidx])3130cb->cb_width[nameidx] = namelen;31313132/*3133* Check if this type/name combination is in the list and update it;3134* otherwise add new node to the list.3135*/3136if ((n = avl_find(avl, node, &idx)) == NULL) {3137avl_insert(avl, node, idx);3138} else {3139nvlist_free(props);3140free(node);3141node = n;3142props = node->usn_nvl;3143}31443145/* Calculate/update width of USED/QUOTA fields */3146if (cb->cb_nicenum) {3147if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED ||3148prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA ||3149prop == ZFS_PROP_PROJECTUSED ||3150prop == ZFS_PROP_PROJECTQUOTA) {3151zfs_nicebytes(space, sizebuf, sizeof (sizebuf));3152} else {3153zfs_nicenum(space, sizebuf, sizeof (sizebuf));3154}3155} else {3156(void) snprintf(sizebuf, sizeof (sizebuf), "%llu",3157(u_longlong_t)space);3158}3159sizelen = strlen(sizebuf);3160if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED ||3161prop == ZFS_PROP_PROJECTUSED) {3162propname = "used";3163if (!nvlist_exists(props, "quota"))3164(void) nvlist_add_uint64(props, "quota", default_quota);3165} else if (prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA ||3166prop == ZFS_PROP_PROJECTQUOTA) {3167propname = "quota";3168if (!nvlist_exists(props, "used"))3169(void) nvlist_add_uint64(props, "used", 0);3170} else if (prop == ZFS_PROP_USEROBJUSED ||3171prop == ZFS_PROP_GROUPOBJUSED || prop == ZFS_PROP_PROJECTOBJUSED) {3172propname = "objused";3173if (!nvlist_exists(props, "objquota")) {3174(void) nvlist_add_uint64(props, "objquota",3175default_quota);3176}3177} else if (prop == ZFS_PROP_USEROBJQUOTA ||3178prop == ZFS_PROP_GROUPOBJQUOTA ||3179prop == ZFS_PROP_PROJECTOBJQUOTA) {3180propname = "objquota";3181if (!nvlist_exists(props, "objused"))3182(void) nvlist_add_uint64(props, "objused", 0);3183} else {3184return (-1);3185}3186sizeidx = us_field_index(propname);3187if (sizeidx >= 0 && sizelen > cb->cb_width[sizeidx])3188cb->cb_width[sizeidx] = sizelen;31893190if (nvlist_add_uint64(props, propname, space) != 0)3191nomem();31923193return (0);3194}31953196static void3197print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types,3198size_t *width, us_node_t *node)3199{3200nvlist_t *nvl = node->usn_nvl;3201char valstr[MAXNAMELEN];3202boolean_t first = B_TRUE;3203int cfield = 0;3204int field;3205uint32_t ustype;32063207/* Check type */3208(void) nvlist_lookup_uint32(nvl, "type", &ustype);3209if (!(ustype & types))3210return;32113212while ((field = fields[cfield]) != USFIELD_LAST) {3213nvpair_t *nvp = NULL;3214data_type_t type;3215uint32_t val32 = -1;3216uint64_t val64 = -1;3217const char *strval = "-";32183219while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL)3220if (strcmp(nvpair_name(nvp),3221us_field_names[field]) == 0)3222break;32233224type = nvp == NULL ? DATA_TYPE_UNKNOWN : nvpair_type(nvp);3225switch (type) {3226case DATA_TYPE_UINT32:3227val32 = fnvpair_value_uint32(nvp);3228break;3229case DATA_TYPE_UINT64:3230val64 = fnvpair_value_uint64(nvp);3231break;3232case DATA_TYPE_STRING:3233strval = fnvpair_value_string(nvp);3234break;3235case DATA_TYPE_UNKNOWN:3236break;3237default:3238(void) fprintf(stderr, "invalid data type\n");3239}32403241switch (field) {3242case USFIELD_TYPE:3243if (type == DATA_TYPE_UINT32)3244strval = us_type2str(val32);3245break;3246case USFIELD_NAME:3247if (type == DATA_TYPE_UINT64) {3248(void) sprintf(valstr, "%llu",3249(u_longlong_t)val64);3250strval = valstr;3251}3252break;3253case USFIELD_USED:3254case USFIELD_QUOTA:3255if (type == DATA_TYPE_UINT64) {3256if (parsable) {3257(void) sprintf(valstr, "%llu",3258(u_longlong_t)val64);3259strval = valstr;3260} else if (field == USFIELD_QUOTA &&3261val64 == 0) {3262strval = "none";3263} else {3264zfs_nicebytes(val64, valstr,3265sizeof (valstr));3266strval = valstr;3267}3268}3269break;3270case USFIELD_OBJUSED:3271case USFIELD_OBJQUOTA:3272if (type == DATA_TYPE_UINT64) {3273if (parsable) {3274(void) sprintf(valstr, "%llu",3275(u_longlong_t)val64);3276strval = valstr;3277} else if (field == USFIELD_OBJQUOTA &&3278val64 == 0) {3279strval = "none";3280} else {3281zfs_nicenum(val64, valstr,3282sizeof (valstr));3283strval = valstr;3284}3285}3286break;3287}32883289if (!first) {3290if (scripted)3291(void) putchar('\t');3292else3293(void) fputs(" ", stdout);3294}3295if (scripted)3296(void) fputs(strval, stdout);3297else if (field == USFIELD_TYPE || field == USFIELD_NAME)3298(void) printf("%-*s", (int)width[field], strval);3299else3300(void) printf("%*s", (int)width[field], strval);33013302first = B_FALSE;3303cfield++;3304}33053306(void) putchar('\n');3307}33083309static void3310print_us(boolean_t scripted, boolean_t parsable, int *fields, int types,3311size_t *width, boolean_t rmnode, avl_tree_t *avl)3312{3313us_node_t *node;3314const char *col;3315int cfield = 0;3316int field;33173318if (!scripted) {3319boolean_t first = B_TRUE;33203321while ((field = fields[cfield]) != USFIELD_LAST) {3322col = gettext(us_field_hdr[field]);3323if (field == USFIELD_TYPE || field == USFIELD_NAME) {3324(void) printf(first ? "%-*s" : " %-*s",3325(int)width[field], col);3326} else {3327(void) printf(first ? "%*s" : " %*s",3328(int)width[field], col);3329}3330first = B_FALSE;3331cfield++;3332}3333(void) printf("\n");3334}33353336for (node = avl_first(avl); node; node = AVL_NEXT(avl, node)) {3337print_us_node(scripted, parsable, fields, types, width, node);3338if (rmnode)3339nvlist_free(node->usn_nvl);3340}3341}33423343static int3344zfs_do_userspace(int argc, char **argv)3345{3346zfs_handle_t *zhp;3347zfs_userquota_prop_t p;3348char *delim;3349char deffields[] = "type,name,used,quota,objused,objquota";3350char *ofield = NULL;3351char *tfield = NULL;3352int cfield = 0;3353int fields[256];3354int i;3355boolean_t scripted = B_FALSE;3356boolean_t prtnum = B_FALSE;3357boolean_t parsable = B_FALSE;3358boolean_t sid2posix = B_FALSE;3359int ret = 0;3360int c;3361zfs_sort_column_t *sortcol = NULL;3362int types = USTYPE_PSX_USR | USTYPE_SMB_USR;3363us_cbdata_t cb;3364us_node_t *node;3365us_node_t *rmnode;3366list_t list;3367avl_index_t idx = 0;33683369if (argc < 2)3370usage(B_FALSE);33713372if (strcmp(argv[0], "groupspace") == 0) {3373/* Toggle default group types */3374types = USTYPE_PSX_GRP | USTYPE_SMB_GRP;3375} else if (strcmp(argv[0], "projectspace") == 0) {3376types = USTYPE_PROJ;3377prtnum = B_TRUE;3378}33793380while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) {3381switch (c) {3382case 'n':3383if (types == USTYPE_PROJ) {3384(void) fprintf(stderr,3385gettext("invalid option 'n'\n"));3386usage(B_FALSE);3387}3388prtnum = B_TRUE;3389break;3390case 'H':3391scripted = B_TRUE;3392break;3393case 'p':3394parsable = B_TRUE;3395break;3396case 'o':3397ofield = optarg;3398break;3399case 's':3400case 'S':3401if (zfs_add_sort_column(&sortcol, optarg,3402c == 's' ? B_FALSE : B_TRUE) != 0) {3403(void) fprintf(stderr,3404gettext("invalid field '%s'\n"), optarg);3405usage(B_FALSE);3406}3407break;3408case 't':3409if (types == USTYPE_PROJ) {3410(void) fprintf(stderr,3411gettext("invalid option 't'\n"));3412usage(B_FALSE);3413}3414tfield = optarg;3415break;3416case 'i':3417if (types == USTYPE_PROJ) {3418(void) fprintf(stderr,3419gettext("invalid option 'i'\n"));3420usage(B_FALSE);3421}3422sid2posix = B_TRUE;3423break;3424case ':':3425(void) fprintf(stderr, gettext("missing argument for "3426"'%c' option\n"), optopt);3427usage(B_FALSE);3428break;3429case '?':3430(void) fprintf(stderr, gettext("invalid option '%c'\n"),3431optopt);3432usage(B_FALSE);3433}3434}34353436argc -= optind;3437argv += optind;34383439if (argc < 1) {3440(void) fprintf(stderr, gettext("missing dataset name\n"));3441usage(B_FALSE);3442}3443if (argc > 1) {3444(void) fprintf(stderr, gettext("too many arguments\n"));3445usage(B_FALSE);3446}34473448/* Use default output fields if not specified using -o */3449if (ofield == NULL)3450ofield = deffields;3451do {3452if ((delim = strchr(ofield, ',')) != NULL)3453*delim = '\0';3454if ((fields[cfield++] = us_field_index(ofield)) == -1) {3455(void) fprintf(stderr, gettext("invalid type '%s' "3456"for -o option\n"), ofield);3457return (-1);3458}3459if (delim != NULL)3460ofield = delim + 1;3461} while (delim != NULL);3462fields[cfield] = USFIELD_LAST;34633464/* Override output types (-t option) */3465if (tfield != NULL) {3466types = 0;34673468do {3469boolean_t found = B_FALSE;34703471if ((delim = strchr(tfield, ',')) != NULL)3472*delim = '\0';3473for (i = 0; i < sizeof (us_type_bits) / sizeof (int);3474i++) {3475if (strcmp(tfield, us_type_names[i]) == 0) {3476found = B_TRUE;3477types |= us_type_bits[i];3478break;3479}3480}3481if (!found) {3482(void) fprintf(stderr, gettext("invalid type "3483"'%s' for -t option\n"), tfield);3484return (-1);3485}3486if (delim != NULL)3487tfield = delim + 1;3488} while (delim != NULL);3489}34903491if ((zhp = zfs_path_to_zhandle(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM |3492ZFS_TYPE_SNAPSHOT)) == NULL)3493return (1);3494if (zfs_get_underlying_type(zhp) != ZFS_TYPE_FILESYSTEM) {3495(void) fprintf(stderr, gettext("operation is only applicable "3496"to filesystems and their snapshots\n"));3497zfs_close(zhp);3498return (1);3499}35003501/* Always add default sorting columns */3502(void) zfs_add_sort_column(&sortcol, "type", B_FALSE);3503(void) zfs_add_sort_column(&sortcol, "name", B_FALSE);35043505cb.cb_sortcol = sortcol;3506cb.cb_numname = prtnum;3507cb.cb_nicenum = !parsable;3508cb.cb_sid2posix = sid2posix;35093510avl_create(&cb.cb_avl, us_compare,3511sizeof (us_node_t), offsetof(us_node_t, usn_avlnode));351235133514for (i = 0; i < USFIELD_LAST; i++)3515cb.cb_width[i] = strlen(gettext(us_field_hdr[i]));35163517for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) {3518if ((zfs_prop_is_user(p) &&3519!(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) ||3520(zfs_prop_is_group(p) &&3521!(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))) ||3522(zfs_prop_is_project(p) && types != USTYPE_PROJ))3523continue;35243525cb.cb_prop = p;3526if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0) {3527zfs_close(zhp);3528avl_destroy(&cb.cb_avl);3529return (ret);3530}3531}3532zfs_close(zhp);35333534/* Sort the list */3535if ((node = avl_first(&cb.cb_avl)) == NULL) {3536avl_destroy(&cb.cb_avl);3537return (0);3538}35393540us_populated = B_TRUE;35413542list_create(&list, sizeof (us_node_t),3543offsetof(us_node_t, usn_listnode));3544list_link_init(&node->usn_listnode);35453546while (node != NULL) {3547rmnode = node;3548node = AVL_NEXT(&cb.cb_avl, node);3549avl_remove(&cb.cb_avl, rmnode);3550list_insert_head(&list, rmnode);3551}35523553for (node = list_head(&list); node != NULL;3554node = list_next(&list, node)) {3555if (avl_find(&cb.cb_avl, node, &idx) == NULL)3556avl_insert(&cb.cb_avl, node, idx);3557}35583559while ((node = list_remove_head(&list)) != NULL) { }3560list_destroy(&list);35613562/* Print and free node nvlist memory */3563print_us(scripted, parsable, fields, types, cb.cb_width, B_TRUE,3564&cb.cb_avl);35653566zfs_free_sort_columns(sortcol);35673568/* Clean up the AVL tree */3569void *cookie = NULL;3570while ((node = avl_destroy_nodes(&cb.cb_avl, &cookie)) != NULL) {3571free(node);3572}3573avl_destroy(&cb.cb_avl);35743575return (ret);3576}35773578/*3579* list [-Hp][-r|-d max] [-o property[,...]] [-s property] ... [-S property]3580* [-t type[,...]] [filesystem|volume|snapshot] ...3581*3582* -H Scripted mode; elide headers and separate columns by tabs3583* -p Display values in parsable (literal) format.3584* -r Recurse over all children3585* -d Limit recursion by depth.3586* -o Control which fields to display.3587* -s Specify sort columns, descending order.3588* -S Specify sort columns, ascending order.3589* -t Control which object types to display.3590*3591* When given no arguments, list all filesystems in the system.3592* Otherwise, list the specified datasets, optionally recursing down them if3593* '-r' is specified.3594*/3595typedef struct list_cbdata {3596boolean_t cb_first;3597boolean_t cb_literal;3598boolean_t cb_scripted;3599zprop_list_t *cb_proplist;3600boolean_t cb_json;3601nvlist_t *cb_jsobj;3602boolean_t cb_json_as_int;3603} list_cbdata_t;36043605/*3606* Given a list of columns to display, output appropriate headers for each one.3607*/3608static void3609print_header(list_cbdata_t *cb)3610{3611zprop_list_t *pl = cb->cb_proplist;3612char headerbuf[ZFS_MAXPROPLEN];3613const char *header;3614int i;3615boolean_t first = B_TRUE;3616boolean_t right_justify;36173618color_start(ANSI_BOLD);36193620for (; pl != NULL; pl = pl->pl_next) {3621if (!first) {3622(void) printf(" ");3623} else {3624first = B_FALSE;3625}36263627right_justify = B_FALSE;3628if (pl->pl_prop != ZPROP_USERPROP) {3629header = zfs_prop_column_name(pl->pl_prop);3630right_justify = zfs_prop_align_right(pl->pl_prop);3631} else {3632for (i = 0; pl->pl_user_prop[i] != '\0'; i++)3633headerbuf[i] = toupper(pl->pl_user_prop[i]);3634headerbuf[i] = '\0';3635header = headerbuf;3636}36373638if (pl->pl_next == NULL && !right_justify)3639(void) printf("%s", header);3640else if (right_justify)3641(void) printf("%*s", (int)pl->pl_width, header);3642else3643(void) printf("%-*s", (int)pl->pl_width, header);3644}36453646color_end();36473648(void) printf("\n");3649}36503651/*3652* Decides on the color that the avail value should be printed in.3653* > 80% used = yellow3654* > 90% used = red3655*/3656static const char *3657zfs_list_avail_color(zfs_handle_t *zhp)3658{3659uint64_t used = zfs_prop_get_int(zhp, ZFS_PROP_USED);3660uint64_t avail = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE);3661int percentage = (int)((double)avail / MAX(avail + used, 1) * 100);36623663if (percentage > 20)3664return (NULL);3665else if (percentage > 10)3666return (ANSI_YELLOW);3667else3668return (ANSI_RED);3669}36703671/*3672* Given a dataset and a list of fields, print out all the properties according3673* to the described layout, or return an nvlist containing all the fields, later3674* to be printed out as JSON object.3675*/3676static void3677collect_dataset(zfs_handle_t *zhp, list_cbdata_t *cb)3678{3679zprop_list_t *pl = cb->cb_proplist;3680boolean_t first = B_TRUE;3681char property[ZFS_MAXPROPLEN];3682nvlist_t *userprops = zfs_get_user_props(zhp);3683nvlist_t *propval;3684const char *propstr;3685boolean_t right_justify;3686nvlist_t *item, *d, *props;3687item = d = props = NULL;3688zprop_source_t sourcetype = ZPROP_SRC_NONE;3689char source[ZFS_MAX_DATASET_NAME_LEN];3690if (cb->cb_json) {3691d = fnvlist_lookup_nvlist(cb->cb_jsobj, "datasets");3692if (d == NULL) {3693fprintf(stderr, "datasets obj not found.\n");3694exit(1);3695}3696item = fnvlist_alloc();3697props = fnvlist_alloc();3698fill_dataset_info(item, zhp, cb->cb_json_as_int);3699}37003701for (; pl != NULL; pl = pl->pl_next) {3702if (!cb->cb_json && !first) {3703if (cb->cb_scripted)3704(void) putchar('\t');3705else3706(void) fputs(" ", stdout);3707} else {3708first = B_FALSE;3709}37103711if (pl->pl_prop == ZFS_PROP_NAME) {3712(void) strlcpy(property, zfs_get_name(zhp),3713sizeof (property));3714propstr = property;3715right_justify = zfs_prop_align_right(pl->pl_prop);3716} else if (pl->pl_prop != ZPROP_USERPROP) {3717if (zfs_prop_get(zhp, pl->pl_prop, property,3718sizeof (property), &sourcetype, source,3719sizeof (source), cb->cb_literal) != 0)3720propstr = "-";3721else3722propstr = property;3723right_justify = zfs_prop_align_right(pl->pl_prop);3724} else if (zfs_prop_userquota(pl->pl_user_prop)) {3725sourcetype = ZPROP_SRC_LOCAL;3726if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,3727property, sizeof (property), cb->cb_literal) != 0) {3728sourcetype = ZPROP_SRC_NONE;3729propstr = "-";3730} else {3731propstr = property;3732}3733right_justify = B_TRUE;3734} else if (zfs_prop_written(pl->pl_user_prop)) {3735sourcetype = ZPROP_SRC_LOCAL;3736if (zfs_prop_get_written(zhp, pl->pl_user_prop,3737property, sizeof (property), cb->cb_literal) != 0) {3738sourcetype = ZPROP_SRC_NONE;3739propstr = "-";3740} else {3741propstr = property;3742}3743right_justify = B_TRUE;3744} else {3745if (nvlist_lookup_nvlist(userprops,3746pl->pl_user_prop, &propval) != 0) {3747propstr = "-";3748} else {3749propstr = fnvlist_lookup_string(propval,3750ZPROP_VALUE);3751strlcpy(source,3752fnvlist_lookup_string(propval,3753ZPROP_SOURCE), ZFS_MAX_DATASET_NAME_LEN);3754if (strcmp(source,3755zfs_get_name(zhp)) == 0) {3756sourcetype = ZPROP_SRC_LOCAL;3757} else if (strcmp(source,3758ZPROP_SOURCE_VAL_RECVD) == 0) {3759sourcetype = ZPROP_SRC_RECEIVED;3760} else {3761sourcetype = ZPROP_SRC_INHERITED;3762}3763}3764right_justify = B_FALSE;3765}37663767if (cb->cb_json) {3768if (pl->pl_prop == ZFS_PROP_NAME)3769continue;3770const char *prop_name;3771if (pl->pl_prop != ZPROP_USERPROP)3772prop_name = zfs_prop_to_name(pl->pl_prop);3773else3774prop_name = pl->pl_user_prop;3775if (zprop_nvlist_one_property(3776prop_name, propstr,3777sourcetype, source, NULL, props,3778cb->cb_json_as_int) != 0)3779nomem();3780} else {3781/*3782* zfs_list_avail_color() needs3783* ZFS_PROP_AVAILABLE + USED, so we need another3784* for() search for the USED part when no colors3785* wanted, we can skip the whole thing3786*/3787if (use_color() && pl->pl_prop == ZFS_PROP_AVAILABLE) {3788zprop_list_t *pl2 = cb->cb_proplist;3789for (; pl2 != NULL; pl2 = pl2->pl_next) {3790if (pl2->pl_prop == ZFS_PROP_USED) {3791color_start(3792zfs_list_avail_color(zhp));3793/*3794* found it, no need for more3795* loops3796*/3797break;3798}3799}3800}38013802/*3803* If this is being called in scripted mode, or if3804* this is the last column and it is left-justified,3805* don't include a width format specifier.3806*/3807if (cb->cb_scripted || (pl->pl_next == NULL &&3808!right_justify))3809(void) fputs(propstr, stdout);3810else if (right_justify) {3811(void) printf("%*s", (int)pl->pl_width,3812propstr);3813} else {3814(void) printf("%-*s", (int)pl->pl_width,3815propstr);3816}38173818if (pl->pl_prop == ZFS_PROP_AVAILABLE)3819color_end();3820}3821}3822if (cb->cb_json) {3823fnvlist_add_nvlist(item, "properties", props);3824fnvlist_add_nvlist(d, zfs_get_name(zhp), item);3825fnvlist_free(props);3826fnvlist_free(item);3827} else3828(void) putchar('\n');3829}38303831/*3832* Generic callback function to list a dataset or snapshot.3833*/3834static int3835list_callback(zfs_handle_t *zhp, void *data)3836{3837list_cbdata_t *cbp = data;38383839if (cbp->cb_first) {3840if (!cbp->cb_scripted && !cbp->cb_json)3841print_header(cbp);3842cbp->cb_first = B_FALSE;3843}38443845collect_dataset(zhp, cbp);38463847return (0);3848}38493850static int3851zfs_do_list(int argc, char **argv)3852{3853int c;3854char default_fields[] =3855"name,used,available,referenced,mountpoint";3856int types = ZFS_TYPE_DATASET;3857boolean_t types_specified = B_FALSE;3858char *fields = default_fields;3859list_cbdata_t cb = { 0 };3860int limit = 0;3861int ret = 0;3862zfs_sort_column_t *sortcol = NULL;3863int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS;3864nvlist_t *data = NULL;38653866struct option long_options[] = {3867{"json", no_argument, NULL, 'j'},3868{"json-int", no_argument, NULL, ZFS_OPTION_JSON_NUMS_AS_INT},3869{0, 0, 0, 0}3870};38713872/* check options */3873while ((c = getopt_long(argc, argv, "jHS:d:o:prs:t:", long_options,3874NULL)) != -1) {3875switch (c) {3876case 'o':3877fields = optarg;3878break;3879case 'p':3880cb.cb_literal = B_TRUE;3881flags |= ZFS_ITER_LITERAL_PROPS;3882break;3883case 'd':3884limit = parse_depth(optarg, &flags);3885break;3886case 'r':3887flags |= ZFS_ITER_RECURSE;3888break;3889case 'j':3890cb.cb_json = B_TRUE;3891cb.cb_jsobj = zfs_json_schema(0, 1);3892data = fnvlist_alloc();3893fnvlist_add_nvlist(cb.cb_jsobj, "datasets", data);3894fnvlist_free(data);3895break;3896case ZFS_OPTION_JSON_NUMS_AS_INT:3897cb.cb_json_as_int = B_TRUE;3898cb.cb_literal = B_TRUE;3899break;3900case 'H':3901cb.cb_scripted = B_TRUE;3902break;3903case 's':3904if (zfs_add_sort_column(&sortcol, optarg,3905B_FALSE) != 0) {3906(void) fprintf(stderr,3907gettext("invalid property '%s'\n"), optarg);3908usage(B_FALSE);3909}3910break;3911case 'S':3912if (zfs_add_sort_column(&sortcol, optarg,3913B_TRUE) != 0) {3914(void) fprintf(stderr,3915gettext("invalid property '%s'\n"), optarg);3916usage(B_FALSE);3917}3918break;3919case 't':3920types = 0;3921types_specified = B_TRUE;3922flags &= ~ZFS_ITER_PROP_LISTSNAPS;39233924for (char *tok; (tok = strsep(&optarg, ",")); ) {3925static const char *const type_subopts[] = {3926"filesystem",3927"fs",3928"volume",3929"vol",3930"snapshot",3931"snap",3932"bookmark",3933"all"3934};3935static const int type_types[] = {3936ZFS_TYPE_FILESYSTEM,3937ZFS_TYPE_FILESYSTEM,3938ZFS_TYPE_VOLUME,3939ZFS_TYPE_VOLUME,3940ZFS_TYPE_SNAPSHOT,3941ZFS_TYPE_SNAPSHOT,3942ZFS_TYPE_BOOKMARK,3943ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK3944};39453946for (c = 0; c < ARRAY_SIZE(type_subopts); ++c)3947if (strcmp(tok, type_subopts[c]) == 0) {3948types |= type_types[c];3949goto found3;3950}39513952(void) fprintf(stderr,3953gettext("invalid type '%s'\n"), tok);3954usage(B_FALSE);3955found3:;3956}3957break;3958case ':':3959(void) fprintf(stderr, gettext("missing argument for "3960"'%c' option\n"), optopt);3961usage(B_FALSE);3962break;3963case '?':3964(void) fprintf(stderr, gettext("invalid option '%c'\n"),3965optopt);3966usage(B_FALSE);3967}3968}39693970argc -= optind;3971argv += optind;39723973if (!cb.cb_json && cb.cb_json_as_int) {3974(void) fprintf(stderr, gettext("'--json-int' only works with"3975" '-j' option\n"));3976usage(B_FALSE);3977}39783979/*3980* If "-o space" and no types were specified, don't display snapshots.3981*/3982if (strcmp(fields, "space") == 0 && types_specified == B_FALSE)3983types &= ~ZFS_TYPE_SNAPSHOT;39843985/*3986* Handle users who want to list all snapshots or bookmarks3987* of the current dataset (ex. 'zfs list -t snapshot <dataset>').3988*/3989if ((types == ZFS_TYPE_SNAPSHOT || types == ZFS_TYPE_BOOKMARK) &&3990argc > 0 && (flags & ZFS_ITER_RECURSE) == 0 && limit == 0) {3991flags |= (ZFS_ITER_DEPTH_LIMIT | ZFS_ITER_RECURSE);3992limit = 1;3993}39943995/*3996* If the user specifies '-o all', the zprop_get_list() doesn't3997* normally include the name of the dataset. For 'zfs list', we always3998* want this property to be first.3999*/4000if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)4001!= 0)4002usage(B_FALSE);40034004cb.cb_first = B_TRUE;40054006/*4007* If we are only going to list and sort by properties that are "fast"4008* then we can use "simple" mode and avoid populating the properties4009* nvlist.4010*/4011if (zfs_list_only_by_fast(cb.cb_proplist) &&4012zfs_sort_only_by_fast(sortcol))4013flags |= ZFS_ITER_SIMPLE;40144015ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist,4016limit, list_callback, &cb);40174018if (ret == 0 && cb.cb_json)4019zcmd_print_json(cb.cb_jsobj);4020else if (ret != 0 && cb.cb_json)4021nvlist_free(cb.cb_jsobj);40224023zprop_free_list(cb.cb_proplist);4024zfs_free_sort_columns(sortcol);40254026if (ret == 0 && cb.cb_first && !cb.cb_scripted)4027(void) fprintf(stderr, gettext("no datasets available\n"));40284029return (ret);4030}40314032/*4033* zfs rename [-fu] <fs | snap | vol> <fs | snap | vol>4034* zfs rename [-f] -p <fs | vol> <fs | vol>4035* zfs rename [-u] -r <snap> <snap>4036*4037* Renames the given dataset to another of the same type.4038*4039* The '-p' flag creates all the non-existing ancestors of the target first.4040* The '-u' flag prevents file systems from being remounted during rename.4041*/4042static int4043zfs_do_rename(int argc, char **argv)4044{4045zfs_handle_t *zhp;4046renameflags_t flags = { 0 };4047int c;4048int ret = 0;4049int types;4050boolean_t parents = B_FALSE;40514052/* check options */4053while ((c = getopt(argc, argv, "pruf")) != -1) {4054switch (c) {4055case 'p':4056parents = B_TRUE;4057break;4058case 'r':4059flags.recursive = B_TRUE;4060break;4061case 'u':4062flags.nounmount = B_TRUE;4063break;4064case 'f':4065flags.forceunmount = B_TRUE;4066break;4067case '?':4068default:4069(void) fprintf(stderr, gettext("invalid option '%c'\n"),4070optopt);4071usage(B_FALSE);4072}4073}40744075argc -= optind;4076argv += optind;40774078/* check number of arguments */4079if (argc < 1) {4080(void) fprintf(stderr, gettext("missing source dataset "4081"argument\n"));4082usage(B_FALSE);4083}4084if (argc < 2) {4085(void) fprintf(stderr, gettext("missing target dataset "4086"argument\n"));4087usage(B_FALSE);4088}4089if (argc > 2) {4090(void) fprintf(stderr, gettext("too many arguments\n"));4091usage(B_FALSE);4092}40934094if (flags.recursive && parents) {4095(void) fprintf(stderr, gettext("-p and -r options are mutually "4096"exclusive\n"));4097usage(B_FALSE);4098}40994100if (flags.nounmount && parents) {4101(void) fprintf(stderr, gettext("-u and -p options are mutually "4102"exclusive\n"));4103usage(B_FALSE);4104}41054106if (flags.recursive && strchr(argv[0], '@') == 0) {4107(void) fprintf(stderr, gettext("source dataset for recursive "4108"rename must be a snapshot\n"));4109usage(B_FALSE);4110}41114112if (flags.nounmount)4113types = ZFS_TYPE_FILESYSTEM;4114else if (parents)4115types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;4116else4117types = ZFS_TYPE_DATASET;41184119if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL)4120return (1);41214122/* If we were asked and the name looks good, try to create ancestors. */4123if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) &&4124zfs_create_ancestors(g_zfs, argv[1]) != 0) {4125zfs_close(zhp);4126return (1);4127}41284129ret = (zfs_rename(zhp, argv[1], flags) != 0);41304131zfs_close(zhp);4132return (ret);4133}41344135/*4136* zfs promote <fs>4137*4138* Promotes the given clone fs to be the parent4139*/4140static int4141zfs_do_promote(int argc, char **argv)4142{4143zfs_handle_t *zhp;4144int ret = 0;41454146/* check options */4147if (argc > 1 && argv[1][0] == '-') {4148(void) fprintf(stderr, gettext("invalid option '%c'\n"),4149argv[1][1]);4150usage(B_FALSE);4151}41524153/* check number of arguments */4154if (argc < 2) {4155(void) fprintf(stderr, gettext("missing clone filesystem"4156" argument\n"));4157usage(B_FALSE);4158}4159if (argc > 2) {4160(void) fprintf(stderr, gettext("too many arguments\n"));4161usage(B_FALSE);4162}41634164zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);4165if (zhp == NULL)4166return (1);41674168ret = (zfs_promote(zhp) != 0);416941704171zfs_close(zhp);4172return (ret);4173}41744175static int4176zfs_do_redact(int argc, char **argv)4177{4178char *snap = NULL;4179char *bookname = NULL;4180char **rsnaps = NULL;4181int numrsnaps = 0;4182argv++;4183argc--;4184if (argc < 3) {4185(void) fprintf(stderr, gettext("too few arguments\n"));4186usage(B_FALSE);4187}41884189snap = argv[0];4190bookname = argv[1];4191rsnaps = argv + 2;4192numrsnaps = argc - 2;41934194nvlist_t *rsnapnv = fnvlist_alloc();41954196for (int i = 0; i < numrsnaps; i++) {4197fnvlist_add_boolean(rsnapnv, rsnaps[i]);4198}41994200int err = lzc_redact(snap, bookname, rsnapnv);4201fnvlist_free(rsnapnv);42024203switch (err) {4204case 0:4205break;4206case ENOENT: {4207zfs_handle_t *zhp = zfs_open(g_zfs, snap, ZFS_TYPE_SNAPSHOT);4208if (zhp == NULL) {4209(void) fprintf(stderr, gettext("provided snapshot %s "4210"does not exist\n"), snap);4211} else {4212zfs_close(zhp);4213}4214for (int i = 0; i < numrsnaps; i++) {4215zhp = zfs_open(g_zfs, rsnaps[i], ZFS_TYPE_SNAPSHOT);4216if (zhp == NULL) {4217(void) fprintf(stderr, gettext("provided "4218"snapshot %s does not exist\n"), rsnaps[i]);4219} else {4220zfs_close(zhp);4221}4222}4223break;4224}4225case EEXIST:4226(void) fprintf(stderr, gettext("specified redaction bookmark "4227"(%s) provided already exists\n"), bookname);4228break;4229case ENAMETOOLONG:4230(void) fprintf(stderr, gettext("provided bookmark name cannot "4231"be used, final name would be too long\n"));4232break;4233case E2BIG:4234(void) fprintf(stderr, gettext("too many redaction snapshots "4235"specified\n"));4236break;4237case EINVAL:4238if (strchr(bookname, '#') != NULL)4239(void) fprintf(stderr, gettext(4240"redaction bookmark name must not contain '#'\n"));4241else4242(void) fprintf(stderr, gettext(4243"redaction snapshot must be descendent of "4244"snapshot being redacted\n"));4245break;4246case EALREADY:4247(void) fprintf(stderr, gettext("attempted to redact redacted "4248"dataset or with respect to redacted dataset\n"));4249break;4250case ENOTSUP:4251(void) fprintf(stderr, gettext("redaction bookmarks feature "4252"not enabled\n"));4253break;4254case EXDEV:4255(void) fprintf(stderr, gettext("potentially invalid redaction "4256"snapshot; full dataset names required\n"));4257break;4258case ESRCH:4259(void) fprintf(stderr, gettext("attempted to resume redaction "4260" with a mismatched redaction list\n"));4261break;4262default:4263(void) fprintf(stderr, gettext("internal error: %s\n"),4264strerror(errno));4265}42664267return (err);4268}42694270/*4271* zfs rollback [-rRf] <snapshot>4272*4273* -r Delete any intervening snapshots before doing rollback4274* -R Delete any snapshots and their clones4275* -f ignored for backwards compatibility4276*4277* Given a filesystem, rollback to a specific snapshot, discarding any changes4278* since then and making it the active dataset. If more recent snapshots exist,4279* the command will complain unless the '-r' flag is given.4280*/4281typedef struct rollback_cbdata {4282uint64_t cb_create;4283uint8_t cb_younger_ds_printed;4284boolean_t cb_first;4285int cb_doclones;4286char *cb_target;4287int cb_error;4288boolean_t cb_recurse;4289} rollback_cbdata_t;42904291static int4292rollback_check_dependent(zfs_handle_t *zhp, void *data)4293{4294rollback_cbdata_t *cbp = data;42954296if (cbp->cb_first && cbp->cb_recurse) {4297(void) fprintf(stderr, gettext("cannot rollback to "4298"'%s': clones of previous snapshots exist\n"),4299cbp->cb_target);4300(void) fprintf(stderr, gettext("use '-R' to "4301"force deletion of the following clones and "4302"dependents:\n"));4303cbp->cb_first = 0;4304cbp->cb_error = 1;4305}43064307(void) fprintf(stderr, "%s\n", zfs_get_name(zhp));43084309zfs_close(zhp);4310return (0);4311}431243134314/*4315* Report some snapshots/bookmarks more recent than the one specified.4316* Used when '-r' is not specified. We reuse this same callback for the4317* snapshot dependents - if 'cb_dependent' is set, then this is a4318* dependent and we should report it without checking the transaction group.4319*/4320static int4321rollback_check(zfs_handle_t *zhp, void *data)4322{4323rollback_cbdata_t *cbp = data;4324/*4325* Max number of younger snapshots and/or bookmarks to display before4326* we stop the iteration.4327*/4328const uint8_t max_younger = 32;43294330if (cbp->cb_doclones) {4331zfs_close(zhp);4332return (0);4333}43344335if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {4336if (cbp->cb_first && !cbp->cb_recurse) {4337(void) fprintf(stderr, gettext("cannot "4338"rollback to '%s': more recent snapshots "4339"or bookmarks exist\n"),4340cbp->cb_target);4341(void) fprintf(stderr, gettext("use '-r' to "4342"force deletion of the following "4343"snapshots and bookmarks:\n"));4344cbp->cb_first = 0;4345cbp->cb_error = 1;4346}43474348if (cbp->cb_recurse) {4349if (zfs_iter_dependents_v2(zhp, 0, B_TRUE,4350rollback_check_dependent, cbp) != 0) {4351zfs_close(zhp);4352return (-1);4353}4354} else {4355(void) fprintf(stderr, "%s\n",4356zfs_get_name(zhp));4357cbp->cb_younger_ds_printed++;4358}4359}4360zfs_close(zhp);43614362if (cbp->cb_younger_ds_printed == max_younger) {4363/*4364* This non-recursive rollback is going to fail due to the4365* presence of snapshots and/or bookmarks that are younger than4366* the rollback target.4367* We printed some of the offending objects, now we stop4368* zfs_iter_snapshot/bookmark iteration so we can fail fast and4369* avoid iterating over the rest of the younger objects4370*/4371(void) fprintf(stderr, gettext("Output limited to %d "4372"snapshots/bookmarks\n"), max_younger);4373return (-1);4374}4375return (0);4376}43774378static int4379zfs_do_rollback(int argc, char **argv)4380{4381int ret = 0;4382int c;4383boolean_t force = B_FALSE;4384rollback_cbdata_t cb = { 0 };4385zfs_handle_t *zhp, *snap;4386char parentname[ZFS_MAX_DATASET_NAME_LEN];4387char *delim;4388uint64_t min_txg = 0;43894390/* check options */4391while ((c = getopt(argc, argv, "rRf")) != -1) {4392switch (c) {4393case 'r':4394cb.cb_recurse = 1;4395break;4396case 'R':4397cb.cb_recurse = 1;4398cb.cb_doclones = 1;4399break;4400case 'f':4401force = B_TRUE;4402break;4403case '?':4404(void) fprintf(stderr, gettext("invalid option '%c'\n"),4405optopt);4406usage(B_FALSE);4407}4408}44094410argc -= optind;4411argv += optind;44124413/* check number of arguments */4414if (argc < 1) {4415(void) fprintf(stderr, gettext("missing dataset argument\n"));4416usage(B_FALSE);4417}4418if (argc > 1) {4419(void) fprintf(stderr, gettext("too many arguments\n"));4420usage(B_FALSE);4421}44224423/* open the snapshot */4424if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)4425return (1);44264427/* open the parent dataset */4428(void) strlcpy(parentname, argv[0], sizeof (parentname));4429verify((delim = strrchr(parentname, '@')) != NULL);4430*delim = '\0';4431if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_DATASET)) == NULL) {4432zfs_close(snap);4433return (1);4434}44354436/*4437* Check for more recent snapshots and/or clones based on the presence4438* of '-r' and '-R'.4439*/4440cb.cb_target = argv[0];4441cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);4442cb.cb_first = B_TRUE;4443cb.cb_error = 0;44444445if (cb.cb_create > 0)4446min_txg = cb.cb_create;44474448if ((ret = zfs_iter_snapshots_sorted_v2(zhp, 0, rollback_check, &cb,4449min_txg, 0)) != 0)4450goto out;4451if ((ret = zfs_iter_bookmarks_v2(zhp, 0, rollback_check, &cb)) != 0)4452goto out;44534454if ((ret = cb.cb_error) != 0)4455goto out;44564457/*4458* Rollback parent to the given snapshot.4459*/4460ret = zfs_rollback(zhp, snap, force);44614462out:4463zfs_close(snap);4464zfs_close(zhp);44654466if (ret == 0)4467return (0);4468else4469return (1);4470}44714472/*4473* zfs set property=value ... { fs | snap | vol } ...4474*4475* Sets the given properties for all datasets specified on the command line.4476*/44774478static int4479set_callback(zfs_handle_t *zhp, void *data)4480{4481zprop_set_cbdata_t *cb = data;4482int ret = zfs_prop_set_list_flags(zhp, cb->cb_proplist, cb->cb_flags);44834484if (ret != 0 || libzfs_errno(g_zfs) != EZFS_SUCCESS) {4485switch (libzfs_errno(g_zfs)) {4486case EZFS_MOUNTFAILED:4487(void) fprintf(stderr, gettext("property may be set "4488"but unable to remount filesystem\n"));4489break;4490case EZFS_SHARENFSFAILED:4491(void) fprintf(stderr, gettext("property may be set "4492"but unable to reshare filesystem\n"));4493break;4494}4495}4496return (ret);4497}44984499static int4500zfs_do_set(int argc, char **argv)4501{4502zprop_set_cbdata_t cb = { 0 };4503int ds_start = -1; /* argv idx of first dataset arg */4504int ret = 0;4505int i, c;45064507/* check options */4508while ((c = getopt(argc, argv, "u")) != -1) {4509switch (c) {4510case 'u':4511cb.cb_flags |= ZFS_SET_NOMOUNT;4512break;4513case '?':4514default:4515(void) fprintf(stderr, gettext("invalid option '%c'\n"),4516optopt);4517usage(B_FALSE);4518}4519}45204521argc -= optind;4522argv += optind;45234524/* check number of arguments */4525if (argc < 1) {4526(void) fprintf(stderr, gettext("missing arguments\n"));4527usage(B_FALSE);4528}4529if (argc < 2) {4530if (strchr(argv[0], '=') == NULL) {4531(void) fprintf(stderr, gettext("missing property=value "4532"argument(s)\n"));4533} else {4534(void) fprintf(stderr, gettext("missing dataset "4535"name(s)\n"));4536}4537usage(B_FALSE);4538}45394540/* validate argument order: prop=val args followed by dataset args */4541for (i = 0; i < argc; i++) {4542if (strchr(argv[i], '=') != NULL) {4543if (ds_start > 0) {4544/* out-of-order prop=val argument */4545(void) fprintf(stderr, gettext("invalid "4546"argument order\n"));4547usage(B_FALSE);4548}4549} else if (ds_start < 0) {4550ds_start = i;4551}4552}4553if (ds_start < 0) {4554(void) fprintf(stderr, gettext("missing dataset name(s)\n"));4555usage(B_FALSE);4556}45574558/* Populate a list of property settings */4559if (nvlist_alloc(&cb.cb_proplist, NV_UNIQUE_NAME, 0) != 0)4560nomem();4561for (i = 0; i < ds_start; i++) {4562if (!parseprop(cb.cb_proplist, argv[i])) {4563ret = -1;4564goto error;4565}4566}45674568ret = zfs_for_each(argc - ds_start, argv + ds_start, 0,4569ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, &cb);45704571error:4572nvlist_free(cb.cb_proplist);4573return (ret);4574}45754576typedef struct snap_cbdata {4577nvlist_t *sd_nvl;4578boolean_t sd_recursive;4579const char *sd_snapname;4580} snap_cbdata_t;45814582static int4583zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)4584{4585snap_cbdata_t *sd = arg;4586char *name;4587int rv = 0;4588int error;45894590if (sd->sd_recursive &&4591zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) != 0) {4592zfs_close(zhp);4593return (0);4594}45954596error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname);4597if (error == -1)4598nomem();4599fnvlist_add_boolean(sd->sd_nvl, name);4600free(name);46014602if (sd->sd_recursive)4603rv = zfs_iter_filesystems_v2(zhp, 0, zfs_snapshot_cb, sd);4604zfs_close(zhp);4605return (rv);4606}46074608/*4609* zfs snapshot [-r] [-o prop=value] ... <fs@snap>4610*4611* Creates a snapshot with the given name. While functionally equivalent to4612* 'zfs create', it is a separate command to differentiate intent.4613*/4614static int4615zfs_do_snapshot(int argc, char **argv)4616{4617int ret = 0;4618int c;4619nvlist_t *props;4620snap_cbdata_t sd = { 0 };4621boolean_t multiple_snaps = B_FALSE;46224623if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)4624nomem();4625if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0)4626nomem();46274628/* check options */4629while ((c = getopt(argc, argv, "ro:")) != -1) {4630switch (c) {4631case 'o':4632if (!parseprop(props, optarg)) {4633nvlist_free(sd.sd_nvl);4634nvlist_free(props);4635return (1);4636}4637break;4638case 'r':4639sd.sd_recursive = B_TRUE;4640multiple_snaps = B_TRUE;4641break;4642case '?':4643(void) fprintf(stderr, gettext("invalid option '%c'\n"),4644optopt);4645goto usage;4646}4647}46484649argc -= optind;4650argv += optind;46514652/* check number of arguments */4653if (argc < 1) {4654(void) fprintf(stderr, gettext("missing snapshot argument\n"));4655goto usage;4656}46574658if (argc > 1)4659multiple_snaps = B_TRUE;4660for (; argc > 0; argc--, argv++) {4661char *atp;4662zfs_handle_t *zhp;46634664atp = strchr(argv[0], '@');4665if (atp == NULL)4666goto usage;4667*atp = '\0';4668sd.sd_snapname = atp + 1;4669zhp = zfs_open(g_zfs, argv[0],4670ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);4671if (zhp == NULL)4672goto usage;4673if (zfs_snapshot_cb(zhp, &sd) != 0)4674goto usage;4675}46764677ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props);4678nvlist_free(sd.sd_nvl);4679nvlist_free(props);4680if (ret != 0 && multiple_snaps)4681(void) fprintf(stderr, gettext("no snapshots were created\n"));4682return (ret != 0);46834684usage:4685nvlist_free(sd.sd_nvl);4686nvlist_free(props);4687usage(B_FALSE);4688return (-1);4689}46904691/*4692* Array of prefixes to exclude –4693* a linear search, even if executed for each dataset,4694* is plenty good enough.4695*/4696typedef struct zfs_send_exclude_arg {4697size_t count;4698const char **list;4699} zfs_send_exclude_arg_t;47004701static boolean_t4702zfs_do_send_exclude(zfs_handle_t *zhp, void *context)4703{4704zfs_send_exclude_arg_t *excludes = context;4705const char *name = zfs_get_name(zhp);47064707for (size_t i = 0; i < excludes->count; ++i) {4708size_t len = strlen(excludes->list[i]);4709if (strncmp(name, excludes->list[i], len) == 0 &&4710memchr("/@", name[len], sizeof ("/@")))4711return (B_FALSE);4712}47134714return (B_TRUE);4715}47164717/*4718* Send a backup stream to stdout.4719*/4720static int4721zfs_do_send(int argc, char **argv)4722{4723char *fromname = NULL;4724char *toname = NULL;4725char *resume_token = NULL;4726char *cp;4727zfs_handle_t *zhp;4728sendflags_t flags = { 0 };4729int c, err;4730nvlist_t *dbgnv = NULL;4731char *redactbook = NULL;4732zfs_send_exclude_arg_t excludes = { 0 };47334734struct option long_options[] = {4735{"replicate", no_argument, NULL, 'R'},4736{"skip-missing", no_argument, NULL, 's'},4737{"redact", required_argument, NULL, 'd'},4738{"props", no_argument, NULL, 'p'},4739{"parsable", no_argument, NULL, 'P'},4740{"dedup", no_argument, NULL, 'D'},4741{"proctitle", no_argument, NULL, 'V'},4742{"verbose", no_argument, NULL, 'v'},4743{"dryrun", no_argument, NULL, 'n'},4744{"large-block", no_argument, NULL, 'L'},4745{"embed", no_argument, NULL, 'e'},4746{"resume", required_argument, NULL, 't'},4747{"compressed", no_argument, NULL, 'c'},4748{"raw", no_argument, NULL, 'w'},4749{"backup", no_argument, NULL, 'b'},4750{"holds", no_argument, NULL, 'h'},4751{"saved", no_argument, NULL, 'S'},4752{"exclude", required_argument, NULL, 'X'},4753{0, 0, 0, 0}4754};47554756/* check options */4757while ((c = getopt_long(argc, argv, ":i:I:RsDpVvnPLeht:cwbd:SX:",4758long_options, NULL)) != -1) {4759switch (c) {4760case 'X':4761for (char *ds; (ds = strsep(&optarg, ",")) != NULL; ) {4762if (!zfs_name_valid(ds, ZFS_TYPE_DATASET) ||4763strchr(ds, '/') == NULL) {4764(void) fprintf(stderr, gettext("-X %s: "4765"not a valid non-root dataset name"4766".\n"), ds);4767usage(B_FALSE);4768}4769excludes.list = safe_realloc(excludes.list,4770sizeof (char *) * (excludes.count + 1));4771excludes.list[excludes.count++] = ds;4772}4773break;4774case 'i':4775if (fromname)4776usage(B_FALSE);4777fromname = optarg;4778break;4779case 'I':4780if (fromname)4781usage(B_FALSE);4782fromname = optarg;4783flags.doall = B_TRUE;4784break;4785case 'R':4786flags.replicate = B_TRUE;4787break;4788case 's':4789flags.skipmissing = B_TRUE;4790break;4791case 'd':4792redactbook = optarg;4793break;4794case 'p':4795flags.props = B_TRUE;4796break;4797case 'b':4798flags.backup = B_TRUE;4799break;4800case 'h':4801flags.holds = B_TRUE;4802break;4803case 'P':4804flags.parsable = B_TRUE;4805break;4806case 'V':4807flags.progressastitle = B_TRUE;4808break;4809case 'v':4810flags.verbosity++;4811flags.progress = B_TRUE;4812break;4813case 'D':4814(void) fprintf(stderr,4815gettext("WARNING: deduplicated send is no "4816"longer supported. A regular,\n"4817"non-deduplicated stream will be generated.\n\n"));4818break;4819case 'n':4820flags.dryrun = B_TRUE;4821break;4822case 'L':4823flags.largeblock = B_TRUE;4824break;4825case 'e':4826flags.embed_data = B_TRUE;4827break;4828case 't':4829resume_token = optarg;4830break;4831case 'c':4832flags.compress = B_TRUE;4833break;4834case 'w':4835flags.raw = B_TRUE;4836flags.compress = B_TRUE;4837flags.embed_data = B_TRUE;4838flags.largeblock = B_TRUE;4839break;4840case 'S':4841flags.saved = B_TRUE;4842break;4843case ':':4844/*4845* If a parameter was not passed, optopt contains the4846* value that would normally lead us into the4847* appropriate case statement. If it's > 256, then this4848* must be a longopt and we should look at argv to get4849* the string. Otherwise it's just the character, so we4850* should use it directly.4851*/4852if (optopt <= UINT8_MAX) {4853(void) fprintf(stderr,4854gettext("missing argument for '%c' "4855"option\n"), optopt);4856} else {4857(void) fprintf(stderr,4858gettext("missing argument for '%s' "4859"option\n"), argv[optind - 1]);4860}4861free(excludes.list);4862usage(B_FALSE);4863break;4864case '?':4865default:4866/*4867* If an invalid flag was passed, optopt contains the4868* character if it was a short flag, or 0 if it was a4869* longopt.4870*/4871if (optopt != 0) {4872(void) fprintf(stderr,4873gettext("invalid option '%c'\n"), optopt);4874} else {4875(void) fprintf(stderr,4876gettext("invalid option '%s'\n"),4877argv[optind - 1]);48784879}4880free(excludes.list);4881usage(B_FALSE);4882}4883}48844885if ((flags.parsable || flags.progressastitle) && flags.verbosity == 0)4886flags.verbosity = 1;48874888if (excludes.count > 0 && !flags.replicate) {4889free(excludes.list);4890(void) fprintf(stderr, gettext("Cannot specify "4891"dataset exclusion (-X) on a non-recursive "4892"send.\n"));4893return (1);4894}48954896argc -= optind;4897argv += optind;48984899if (resume_token != NULL) {4900if (fromname != NULL || flags.replicate || flags.props ||4901flags.backup || flags.holds ||4902flags.saved || redactbook != NULL) {4903free(excludes.list);4904(void) fprintf(stderr,4905gettext("invalid flags combined with -t\n"));4906usage(B_FALSE);4907}4908if (argc > 0) {4909free(excludes.list);4910(void) fprintf(stderr, gettext("too many arguments\n"));4911usage(B_FALSE);4912}4913} else {4914if (argc < 1) {4915free(excludes.list);4916(void) fprintf(stderr,4917gettext("missing snapshot argument\n"));4918usage(B_FALSE);4919}4920if (argc > 1) {4921free(excludes.list);4922(void) fprintf(stderr, gettext("too many arguments\n"));4923usage(B_FALSE);4924}4925}49264927if (flags.saved) {4928if (fromname != NULL || flags.replicate || flags.props ||4929flags.doall || flags.backup ||4930flags.holds || flags.largeblock || flags.embed_data ||4931flags.compress || flags.raw || redactbook != NULL) {4932free(excludes.list);49334934(void) fprintf(stderr, gettext("incompatible flags "4935"combined with saved send flag\n"));4936usage(B_FALSE);4937}4938if (strchr(argv[0], '@') != NULL) {4939free(excludes.list);49404941(void) fprintf(stderr, gettext("saved send must "4942"specify the dataset with partially-received "4943"state\n"));4944usage(B_FALSE);4945}4946}49474948if (flags.raw && redactbook != NULL) {4949free(excludes.list);4950(void) fprintf(stderr,4951gettext("Error: raw sends may not be redacted.\n"));4952return (1);4953}49544955if (!flags.dryrun && isatty(STDOUT_FILENO)) {4956free(excludes.list);4957(void) fprintf(stderr,4958gettext("Error: Stream can not be written to a terminal.\n"4959"You must redirect standard output.\n"));4960return (1);4961}49624963if (flags.saved) {4964zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);4965if (zhp == NULL) {4966free(excludes.list);4967return (1);4968}49694970err = zfs_send_saved(zhp, &flags, STDOUT_FILENO,4971resume_token);4972free(excludes.list);4973zfs_close(zhp);4974return (err != 0);4975} else if (resume_token != NULL) {4976free(excludes.list);4977return (zfs_send_resume(g_zfs, &flags, STDOUT_FILENO,4978resume_token));4979}49804981if (flags.skipmissing && !flags.replicate) {4982free(excludes.list);4983(void) fprintf(stderr,4984gettext("skip-missing flag can only be used in "4985"conjunction with replicate\n"));4986usage(B_FALSE);4987}49884989/*4990* For everything except -R and -I, use the new, cleaner code path.4991*/4992if (!(flags.replicate || flags.doall)) {4993char frombuf[ZFS_MAX_DATASET_NAME_LEN];49944995if (fromname != NULL && (strchr(fromname, '#') == NULL &&4996strchr(fromname, '@') == NULL)) {4997/*4998* Neither bookmark or snapshot was specified. Print a4999* warning, and assume snapshot.5000*/5001(void) fprintf(stderr, "Warning: incremental source "5002"didn't specify type, assuming snapshot. Use '@' "5003"or '#' prefix to avoid ambiguity.\n");5004(void) snprintf(frombuf, sizeof (frombuf), "@%s",5005fromname);5006fromname = frombuf;5007}5008if (fromname != NULL &&5009(fromname[0] == '#' || fromname[0] == '@')) {5010/*5011* Incremental source name begins with # or @.5012* Default to same fs as target.5013*/5014char tmpbuf[ZFS_MAX_DATASET_NAME_LEN];5015(void) strlcpy(tmpbuf, fromname, sizeof (tmpbuf));5016(void) strlcpy(frombuf, argv[0], sizeof (frombuf));5017cp = strchr(frombuf, '@');5018if (cp != NULL)5019*cp = '\0';5020(void) strlcat(frombuf, tmpbuf, sizeof (frombuf));5021fromname = frombuf;5022}50235024zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);5025if (zhp == NULL) {5026free(excludes.list);5027return (1);5028}5029err = zfs_send_one(zhp, fromname, STDOUT_FILENO, &flags,5030redactbook);50315032free(excludes.list);5033zfs_close(zhp);5034return (err != 0);5035}50365037if (fromname != NULL && strchr(fromname, '#')) {5038(void) fprintf(stderr,5039gettext("Error: multiple snapshots cannot be "5040"sent from a bookmark.\n"));5041free(excludes.list);5042return (1);5043}50445045if (redactbook != NULL) {5046(void) fprintf(stderr, gettext("Error: multiple snapshots "5047"cannot be sent redacted.\n"));5048free(excludes.list);5049return (1);5050}50515052if ((cp = strchr(argv[0], '@')) == NULL) {5053(void) fprintf(stderr, gettext("Error: "5054"Unsupported flag with filesystem or bookmark.\n"));5055free(excludes.list);5056return (1);5057}5058*cp = '\0';5059toname = cp + 1;5060zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);5061if (zhp == NULL) {5062free(excludes.list);5063return (1);5064}50655066/*5067* If they specified the full path to the snapshot, chop off5068* everything except the short name of the snapshot, but special5069* case if they specify the origin.5070*/5071if (fromname && (cp = strchr(fromname, '@')) != NULL) {5072char origin[ZFS_MAX_DATASET_NAME_LEN];5073zprop_source_t src;50745075(void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN,5076origin, sizeof (origin), &src, NULL, 0, B_FALSE);50775078if (strcmp(origin, fromname) == 0) {5079fromname = NULL;5080flags.fromorigin = B_TRUE;5081} else {5082*cp = '\0';5083if (cp != fromname && strcmp(argv[0], fromname)) {5084zfs_close(zhp);5085free(excludes.list);5086(void) fprintf(stderr,5087gettext("incremental source must be "5088"in same filesystem\n"));5089usage(B_FALSE);5090}5091fromname = cp + 1;5092if (strchr(fromname, '@') || strchr(fromname, '/')) {5093zfs_close(zhp);5094free(excludes.list);5095(void) fprintf(stderr,5096gettext("invalid incremental source\n"));5097usage(B_FALSE);5098}5099}5100}51015102if (flags.replicate && fromname == NULL)5103flags.doall = B_TRUE;51045105err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO,5106excludes.count > 0 ? zfs_do_send_exclude : NULL,5107&excludes, flags.verbosity >= 3 ? &dbgnv : NULL);51085109if (flags.verbosity >= 3 && dbgnv != NULL) {5110/*5111* dump_nvlist prints to stdout, but that's been5112* redirected to a file. Make it print to stderr5113* instead.5114*/5115(void) dup2(STDERR_FILENO, STDOUT_FILENO);5116dump_nvlist(dbgnv, 0);5117nvlist_free(dbgnv);5118}51195120zfs_close(zhp);5121free(excludes.list);5122return (err != 0);5123}51245125/*5126* Restore a backup stream from stdin.5127*/5128static int5129zfs_do_receive(int argc, char **argv)5130{5131int c, err = 0;5132recvflags_t flags = { 0 };5133boolean_t abort_resumable = B_FALSE;5134nvlist_t *props;51355136if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)5137nomem();51385139/* check options */5140while ((c = getopt(argc, argv, ":o:x:dehMnuvFsAc")) != -1) {5141switch (c) {5142case 'o':5143if (!parseprop(props, optarg)) {5144nvlist_free(props);5145usage(B_FALSE);5146}5147break;5148case 'x':5149if (!parsepropname(props, optarg)) {5150nvlist_free(props);5151usage(B_FALSE);5152}5153break;5154case 'd':5155if (flags.istail) {5156(void) fprintf(stderr, gettext("invalid option "5157"combination: -d and -e are mutually "5158"exclusive\n"));5159usage(B_FALSE);5160}5161flags.isprefix = B_TRUE;5162break;5163case 'e':5164if (flags.isprefix) {5165(void) fprintf(stderr, gettext("invalid option "5166"combination: -d and -e are mutually "5167"exclusive\n"));5168usage(B_FALSE);5169}5170flags.istail = B_TRUE;5171break;5172case 'h':5173flags.skipholds = B_TRUE;5174break;5175case 'M':5176flags.forceunmount = B_TRUE;5177break;5178case 'n':5179flags.dryrun = B_TRUE;5180break;5181case 'u':5182flags.nomount = B_TRUE;5183break;5184case 'v':5185flags.verbose = B_TRUE;5186break;5187case 's':5188flags.resumable = B_TRUE;5189break;5190case 'F':5191flags.force = B_TRUE;5192break;5193case 'A':5194abort_resumable = B_TRUE;5195break;5196case 'c':5197flags.heal = B_TRUE;5198break;5199case ':':5200(void) fprintf(stderr, gettext("missing argument for "5201"'%c' option\n"), optopt);5202usage(B_FALSE);5203break;5204case '?':5205(void) fprintf(stderr, gettext("invalid option '%c'\n"),5206optopt);5207usage(B_FALSE);5208}5209}52105211argc -= optind;5212argv += optind;52135214/* zfs recv -e (use "tail" name) implies -d (remove dataset "head") */5215if (flags.istail)5216flags.isprefix = B_TRUE;52175218/* check number of arguments */5219if (argc < 1) {5220(void) fprintf(stderr, gettext("missing snapshot argument\n"));5221usage(B_FALSE);5222}5223if (argc > 1) {5224(void) fprintf(stderr, gettext("too many arguments\n"));5225usage(B_FALSE);5226}52275228if (abort_resumable) {5229if (flags.isprefix || flags.istail || flags.dryrun ||5230flags.resumable || flags.nomount) {5231(void) fprintf(stderr, gettext("invalid option\n"));5232usage(B_FALSE);5233}52345235char namebuf[ZFS_MAX_DATASET_NAME_LEN];5236(void) snprintf(namebuf, sizeof (namebuf),5237"%s/%%recv", argv[0]);52385239if (zfs_dataset_exists(g_zfs, namebuf,5240ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) {5241zfs_handle_t *zhp = zfs_open(g_zfs,5242namebuf, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);5243if (zhp == NULL) {5244nvlist_free(props);5245return (1);5246}5247err = zfs_destroy(zhp, B_FALSE);5248zfs_close(zhp);5249} else {5250zfs_handle_t *zhp = zfs_open(g_zfs,5251argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);5252if (zhp == NULL)5253usage(B_FALSE);5254if (!zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) ||5255zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,5256NULL, 0, NULL, NULL, 0, B_TRUE) == -1) {5257(void) fprintf(stderr,5258gettext("'%s' does not have any "5259"resumable receive state to abort\n"),5260argv[0]);5261nvlist_free(props);5262zfs_close(zhp);5263return (1);5264}5265err = zfs_destroy(zhp, B_FALSE);5266zfs_close(zhp);5267}5268nvlist_free(props);5269return (err != 0);5270}52715272if (isatty(STDIN_FILENO)) {5273(void) fprintf(stderr,5274gettext("Error: Backup stream can not be read "5275"from a terminal.\n"5276"You must redirect standard input.\n"));5277nvlist_free(props);5278return (1);5279}5280err = zfs_receive(g_zfs, argv[0], props, &flags, STDIN_FILENO, NULL);5281nvlist_free(props);52825283return (err != 0);5284}52855286/*5287* allow/unallow stuff5288*/5289/* copied from zfs/sys/dsl_deleg.h */5290#define ZFS_DELEG_PERM_CREATE "create"5291#define ZFS_DELEG_PERM_DESTROY "destroy"5292#define ZFS_DELEG_PERM_SNAPSHOT "snapshot"5293#define ZFS_DELEG_PERM_ROLLBACK "rollback"5294#define ZFS_DELEG_PERM_CLONE "clone"5295#define ZFS_DELEG_PERM_PROMOTE "promote"5296#define ZFS_DELEG_PERM_RENAME "rename"5297#define ZFS_DELEG_PERM_MOUNT "mount"5298#define ZFS_DELEG_PERM_SHARE "share"5299#define ZFS_DELEG_PERM_SEND "send"5300#define ZFS_DELEG_PERM_SEND_RAW "send:raw"5301#define ZFS_DELEG_PERM_RECEIVE "receive"5302#define ZFS_DELEG_PERM_RECEIVE_APPEND "receive:append"5303#define ZFS_DELEG_PERM_ALLOW "allow"5304#define ZFS_DELEG_PERM_USERPROP "userprop"5305#define ZFS_DELEG_PERM_VSCAN "vscan" /* ??? */5306#define ZFS_DELEG_PERM_USERQUOTA "userquota"5307#define ZFS_DELEG_PERM_GROUPQUOTA "groupquota"5308#define ZFS_DELEG_PERM_USERUSED "userused"5309#define ZFS_DELEG_PERM_GROUPUSED "groupused"5310#define ZFS_DELEG_PERM_USEROBJQUOTA "userobjquota"5311#define ZFS_DELEG_PERM_GROUPOBJQUOTA "groupobjquota"5312#define ZFS_DELEG_PERM_USEROBJUSED "userobjused"5313#define ZFS_DELEG_PERM_GROUPOBJUSED "groupobjused"53145315#define ZFS_DELEG_PERM_HOLD "hold"5316#define ZFS_DELEG_PERM_RELEASE "release"5317#define ZFS_DELEG_PERM_DIFF "diff"5318#define ZFS_DELEG_PERM_BOOKMARK "bookmark"5319#define ZFS_DELEG_PERM_LOAD_KEY "load-key"5320#define ZFS_DELEG_PERM_CHANGE_KEY "change-key"53215322#define ZFS_DELEG_PERM_PROJECTUSED "projectused"5323#define ZFS_DELEG_PERM_PROJECTQUOTA "projectquota"5324#define ZFS_DELEG_PERM_PROJECTOBJUSED "projectobjused"5325#define ZFS_DELEG_PERM_PROJECTOBJQUOTA "projectobjquota"53265327#define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE53285329static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = {5330{ ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW },5331{ ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE },5332{ ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE },5333{ ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY },5334{ ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF},5335{ ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD },5336{ ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT },5337{ ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE },5338{ ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE },5339{ ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE },5340{ ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },5341{ ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },5342{ ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND },5343{ ZFS_DELEG_PERM_SEND_RAW, ZFS_DELEG_NOTE_SEND_RAW },5344{ ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },5345{ ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },5346{ ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK },5347{ ZFS_DELEG_PERM_LOAD_KEY, ZFS_DELEG_NOTE_LOAD_KEY },5348{ ZFS_DELEG_PERM_CHANGE_KEY, ZFS_DELEG_NOTE_CHANGE_KEY },53495350{ ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA },5351{ ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED },5352{ ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },5353{ ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA },5354{ ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED },5355{ ZFS_DELEG_PERM_USEROBJQUOTA, ZFS_DELEG_NOTE_USEROBJQUOTA },5356{ ZFS_DELEG_PERM_USEROBJUSED, ZFS_DELEG_NOTE_USEROBJUSED },5357{ ZFS_DELEG_PERM_GROUPOBJQUOTA, ZFS_DELEG_NOTE_GROUPOBJQUOTA },5358{ ZFS_DELEG_PERM_GROUPOBJUSED, ZFS_DELEG_NOTE_GROUPOBJUSED },5359{ ZFS_DELEG_PERM_PROJECTUSED, ZFS_DELEG_NOTE_PROJECTUSED },5360{ ZFS_DELEG_PERM_PROJECTQUOTA, ZFS_DELEG_NOTE_PROJECTQUOTA },5361{ ZFS_DELEG_PERM_PROJECTOBJUSED, ZFS_DELEG_NOTE_PROJECTOBJUSED },5362{ ZFS_DELEG_PERM_PROJECTOBJQUOTA, ZFS_DELEG_NOTE_PROJECTOBJQUOTA },5363{ NULL, ZFS_DELEG_NOTE_NONE }5364};53655366/* permission structure */5367typedef struct deleg_perm {5368zfs_deleg_who_type_t dp_who_type;5369const char *dp_name;5370boolean_t dp_local;5371boolean_t dp_descend;5372} deleg_perm_t;53735374/* */5375typedef struct deleg_perm_node {5376deleg_perm_t dpn_perm;53775378avl_node_t dpn_avl_node;5379} deleg_perm_node_t;53805381typedef struct fs_perm fs_perm_t;53825383/* permissions set */5384typedef struct who_perm {5385zfs_deleg_who_type_t who_type;5386const char *who_name; /* id */5387char who_ug_name[256]; /* user/group name */5388fs_perm_t *who_fsperm; /* uplink */53895390avl_tree_t who_deleg_perm_avl; /* permissions */5391} who_perm_t;53925393/* */5394typedef struct who_perm_node {5395who_perm_t who_perm;5396avl_node_t who_avl_node;5397} who_perm_node_t;53985399typedef struct fs_perm_set fs_perm_set_t;5400/* fs permissions */5401struct fs_perm {5402const char *fsp_name;54035404avl_tree_t fsp_sc_avl; /* sets,create */5405avl_tree_t fsp_uge_avl; /* user,group,everyone */54065407fs_perm_set_t *fsp_set; /* uplink */5408};54095410/* */5411typedef struct fs_perm_node {5412fs_perm_t fspn_fsperm;5413avl_tree_t fspn_avl;54145415list_node_t fspn_list_node;5416} fs_perm_node_t;54175418/* top level structure */5419struct fs_perm_set {5420list_t fsps_list; /* list of fs_perms */5421};54225423static inline const char *5424deleg_perm_type(zfs_deleg_note_t note)5425{5426/* subcommands */5427switch (note) {5428/* SUBCOMMANDS */5429/* OTHER */5430case ZFS_DELEG_NOTE_GROUPQUOTA:5431case ZFS_DELEG_NOTE_GROUPUSED:5432case ZFS_DELEG_NOTE_USERPROP:5433case ZFS_DELEG_NOTE_USERQUOTA:5434case ZFS_DELEG_NOTE_USERUSED:5435case ZFS_DELEG_NOTE_USEROBJQUOTA:5436case ZFS_DELEG_NOTE_USEROBJUSED:5437case ZFS_DELEG_NOTE_GROUPOBJQUOTA:5438case ZFS_DELEG_NOTE_GROUPOBJUSED:5439case ZFS_DELEG_NOTE_PROJECTUSED:5440case ZFS_DELEG_NOTE_PROJECTQUOTA:5441case ZFS_DELEG_NOTE_PROJECTOBJUSED:5442case ZFS_DELEG_NOTE_PROJECTOBJQUOTA:5443/* other */5444return (gettext("other"));5445default:5446return (gettext("subcommand"));5447}5448}54495450static int5451who_type2weight(zfs_deleg_who_type_t who_type)5452{5453int res;5454switch (who_type) {5455case ZFS_DELEG_NAMED_SET_SETS:5456case ZFS_DELEG_NAMED_SET:5457res = 0;5458break;5459case ZFS_DELEG_CREATE_SETS:5460case ZFS_DELEG_CREATE:5461res = 1;5462break;5463case ZFS_DELEG_USER_SETS:5464case ZFS_DELEG_USER:5465res = 2;5466break;5467case ZFS_DELEG_GROUP_SETS:5468case ZFS_DELEG_GROUP:5469res = 3;5470break;5471case ZFS_DELEG_EVERYONE_SETS:5472case ZFS_DELEG_EVERYONE:5473res = 4;5474break;5475default:5476res = -1;5477}54785479return (res);5480}54815482static int5483who_perm_compare(const void *larg, const void *rarg)5484{5485const who_perm_node_t *l = larg;5486const who_perm_node_t *r = rarg;5487zfs_deleg_who_type_t ltype = l->who_perm.who_type;5488zfs_deleg_who_type_t rtype = r->who_perm.who_type;5489int lweight = who_type2weight(ltype);5490int rweight = who_type2weight(rtype);5491int res = lweight - rweight;5492if (res == 0)5493res = strncmp(l->who_perm.who_name, r->who_perm.who_name,5494ZFS_MAX_DELEG_NAME-1);5495return (TREE_ISIGN(res));5496}54975498static int5499deleg_perm_compare(const void *larg, const void *rarg)5500{5501const deleg_perm_node_t *l = larg;5502const deleg_perm_node_t *r = rarg;5503return (TREE_ISIGN(strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name,5504ZFS_MAX_DELEG_NAME-1)));5505}55065507static inline void5508fs_perm_set_init(fs_perm_set_t *fspset)5509{5510memset(fspset, 0, sizeof (fs_perm_set_t));5511list_create(&fspset->fsps_list, sizeof (fs_perm_node_t),5512offsetof(fs_perm_node_t, fspn_list_node));5513}55145515static inline void fs_perm_fini(fs_perm_t *);5516static inline void who_perm_fini(who_perm_t *);55175518static inline void5519fs_perm_set_fini(fs_perm_set_t *fspset)5520{5521fs_perm_node_t *node;5522while ((node = list_remove_head(&fspset->fsps_list)) != NULL) {5523fs_perm_t *fsperm = &node->fspn_fsperm;5524fs_perm_fini(fsperm);5525free(node);5526}5527list_destroy(&fspset->fsps_list);5528}55295530static inline void5531deleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type,5532const char *name)5533{5534deleg_perm->dp_who_type = type;5535deleg_perm->dp_name = name;5536}55375538static inline void5539who_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm,5540zfs_deleg_who_type_t type, const char *name)5541{5542memset(who_perm, 0, sizeof (who_perm_t));55435544avl_create(&who_perm->who_deleg_perm_avl, deleg_perm_compare,5545sizeof (deleg_perm_node_t),5546offsetof(deleg_perm_node_t, dpn_avl_node));55475548who_perm->who_type = type;5549who_perm->who_name = name;5550who_perm->who_fsperm = fsperm;5551}55525553static inline void5554who_perm_fini(who_perm_t *who_perm)5555{5556deleg_perm_node_t *node;5557void *cookie = NULL;55585559while ((node = avl_destroy_nodes(&who_perm->who_deleg_perm_avl,5560&cookie)) != NULL) {5561free(node);5562}55635564avl_destroy(&who_perm->who_deleg_perm_avl);5565}55665567static inline void5568fs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname)5569{5570memset(fsperm, 0, sizeof (fs_perm_t));55715572avl_create(&fsperm->fsp_sc_avl, who_perm_compare,5573sizeof (who_perm_node_t), offsetof(who_perm_node_t, who_avl_node));5574avl_create(&fsperm->fsp_uge_avl, who_perm_compare,5575sizeof (who_perm_node_t), offsetof(who_perm_node_t, who_avl_node));55765577fsperm->fsp_set = fspset;5578fsperm->fsp_name = fsname;5579}55805581static inline void5582fs_perm_fini(fs_perm_t *fsperm)5583{5584who_perm_node_t *node;5585void *cookie = NULL;55865587while ((node = avl_destroy_nodes(&fsperm->fsp_sc_avl,5588&cookie)) != NULL) {5589who_perm_t *who_perm = &node->who_perm;5590who_perm_fini(who_perm);5591free(node);5592}55935594cookie = NULL;5595while ((node = avl_destroy_nodes(&fsperm->fsp_uge_avl,5596&cookie)) != NULL) {5597who_perm_t *who_perm = &node->who_perm;5598who_perm_fini(who_perm);5599free(node);5600}56015602avl_destroy(&fsperm->fsp_sc_avl);5603avl_destroy(&fsperm->fsp_uge_avl);5604}56055606static void5607set_deleg_perm_node(avl_tree_t *avl, deleg_perm_node_t *node,5608zfs_deleg_who_type_t who_type, const char *name, char locality)5609{5610avl_index_t idx = 0;56115612deleg_perm_node_t *found_node = NULL;5613deleg_perm_t *deleg_perm = &node->dpn_perm;56145615deleg_perm_init(deleg_perm, who_type, name);56165617if ((found_node = avl_find(avl, node, &idx)) == NULL)5618avl_insert(avl, node, idx);5619else {5620node = found_node;5621deleg_perm = &node->dpn_perm;5622}562356245625switch (locality) {5626case ZFS_DELEG_LOCAL:5627deleg_perm->dp_local = B_TRUE;5628break;5629case ZFS_DELEG_DESCENDENT:5630deleg_perm->dp_descend = B_TRUE;5631break;5632case ZFS_DELEG_NA:5633break;5634default:5635assert(B_FALSE); /* invalid locality */5636}5637}56385639static inline int5640parse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality)5641{5642nvpair_t *nvp = NULL;5643avl_tree_t *avl = &who_perm->who_deleg_perm_avl;5644zfs_deleg_who_type_t who_type = who_perm->who_type;56455646while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {5647const char *name = nvpair_name(nvp);5648data_type_t type = nvpair_type(nvp);5649deleg_perm_node_t *node =5650safe_malloc(sizeof (deleg_perm_node_t));56515652VERIFY(type == DATA_TYPE_BOOLEAN);56535654set_deleg_perm_node(avl, node, who_type, name, locality);5655}56565657return (0);5658}56595660static inline int5661parse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl)5662{5663nvpair_t *nvp = NULL;56645665while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {5666nvlist_t *nvl2 = NULL;5667const char *name = nvpair_name(nvp);5668avl_tree_t *avl = NULL;5669zfs_deleg_who_type_t perm_type = name[0];5670char perm_locality = name[1];5671const char *perm_name = name + 3;5672who_perm_t *who_perm = NULL;56735674assert('$' == name[2]);56755676if (nvpair_value_nvlist(nvp, &nvl2) != 0)5677return (-1);56785679switch (perm_type) {5680case ZFS_DELEG_CREATE:5681case ZFS_DELEG_CREATE_SETS:5682case ZFS_DELEG_NAMED_SET:5683case ZFS_DELEG_NAMED_SET_SETS:5684avl = &fsperm->fsp_sc_avl;5685break;5686case ZFS_DELEG_USER:5687case ZFS_DELEG_USER_SETS:5688case ZFS_DELEG_GROUP:5689case ZFS_DELEG_GROUP_SETS:5690case ZFS_DELEG_EVERYONE:5691case ZFS_DELEG_EVERYONE_SETS:5692avl = &fsperm->fsp_uge_avl;5693break;56945695default:5696assert(!"unhandled zfs_deleg_who_type_t");5697}56985699who_perm_node_t *found_node = NULL;5700who_perm_node_t *node = safe_malloc(5701sizeof (who_perm_node_t));5702who_perm = &node->who_perm;5703avl_index_t idx = 0;57045705who_perm_init(who_perm, fsperm, perm_type, perm_name);57065707if ((found_node = avl_find(avl, node, &idx)) == NULL) {5708if (avl == &fsperm->fsp_uge_avl) {5709uid_t rid = 0;5710struct passwd *p = NULL;5711struct group *g = NULL;5712const char *nice_name = NULL;57135714switch (perm_type) {5715case ZFS_DELEG_USER_SETS:5716case ZFS_DELEG_USER:5717rid = atoi(perm_name);5718p = getpwuid(rid);5719if (p)5720nice_name = p->pw_name;5721break;5722case ZFS_DELEG_GROUP_SETS:5723case ZFS_DELEG_GROUP:5724rid = atoi(perm_name);5725g = getgrgid(rid);5726if (g)5727nice_name = g->gr_name;5728break;57295730default:5731break;5732}57335734if (nice_name != NULL) {5735(void) strlcpy(5736node->who_perm.who_ug_name,5737nice_name, 256);5738} else {5739/* User or group unknown */5740(void) snprintf(5741node->who_perm.who_ug_name,5742sizeof (node->who_perm.who_ug_name),5743"(unknown: %d)", rid);5744}5745}57465747avl_insert(avl, node, idx);5748} else {5749node = found_node;5750who_perm = &node->who_perm;5751}57525753assert(who_perm != NULL);5754(void) parse_who_perm(who_perm, nvl2, perm_locality);5755}57565757return (0);5758}57595760static inline int5761parse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl)5762{5763nvpair_t *nvp = NULL;57645765while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {5766nvlist_t *nvl2 = NULL;5767const char *fsname = nvpair_name(nvp);5768data_type_t type = nvpair_type(nvp);5769fs_perm_t *fsperm = NULL;5770fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t));57715772fsperm = &node->fspn_fsperm;57735774VERIFY(DATA_TYPE_NVLIST == type);57755776fs_perm_init(fsperm, fspset, fsname);57775778if (nvpair_value_nvlist(nvp, &nvl2) != 0)5779return (-1);57805781(void) parse_fs_perm(fsperm, nvl2);57825783list_insert_tail(&fspset->fsps_list, node);5784}57855786return (0);5787}57885789static inline const char *5790deleg_perm_comment(zfs_deleg_note_t note)5791{5792const char *str;57935794/* subcommands */5795switch (note) {5796/* SUBCOMMANDS */5797case ZFS_DELEG_NOTE_ALLOW:5798str = gettext("Must also have the permission that is being"5799"\n\t\t\t\tallowed");5800break;5801case ZFS_DELEG_NOTE_CLONE:5802str = gettext("Must also have the 'create' ability and 'mount'"5803"\n\t\t\t\tability in the origin file system");5804break;5805case ZFS_DELEG_NOTE_CREATE:5806str = gettext("Must also have the 'mount' ability");5807break;5808case ZFS_DELEG_NOTE_DESTROY:5809str = gettext("Must also have the 'mount' ability");5810break;5811case ZFS_DELEG_NOTE_DIFF:5812str = gettext("Allows lookup of paths within a dataset;"5813"\n\t\t\t\tgiven an object number. Ordinary users need this"5814"\n\t\t\t\tin order to use zfs diff");5815break;5816case ZFS_DELEG_NOTE_HOLD:5817str = gettext("Allows adding a user hold to a snapshot");5818break;5819case ZFS_DELEG_NOTE_MOUNT:5820str = gettext("Allows mount/umount of ZFS datasets");5821break;5822case ZFS_DELEG_NOTE_PROMOTE:5823str = gettext("Must also have the 'mount'\n\t\t\t\tand"5824" 'promote' ability in the origin file system");5825break;5826case ZFS_DELEG_NOTE_RECEIVE:5827str = gettext("Must also have the 'mount' and 'create'"5828" ability");5829break;5830case ZFS_DELEG_NOTE_RELEASE:5831str = gettext("Allows releasing a user hold which\n\t\t\t\t"5832"might destroy the snapshot");5833break;5834case ZFS_DELEG_NOTE_RENAME:5835str = gettext("Must also have the 'mount' and 'create'"5836"\n\t\t\t\tability in the new parent");5837break;5838case ZFS_DELEG_NOTE_ROLLBACK:5839str = gettext("");5840break;5841case ZFS_DELEG_NOTE_SEND:5842str = gettext("");5843break;5844case ZFS_DELEG_NOTE_SEND_RAW:5845str = gettext("Allow sending ONLY encrypted (raw) replication"5846"\n\t\t\t\tstreams");5847break;5848case ZFS_DELEG_NOTE_SHARE:5849str = gettext("Allows sharing file systems over NFS or SMB"5850"\n\t\t\t\tprotocols");5851break;5852case ZFS_DELEG_NOTE_SNAPSHOT:5853str = gettext("");5854break;5855case ZFS_DELEG_NOTE_LOAD_KEY:5856str = gettext("Allows loading or unloading an encryption key");5857break;5858case ZFS_DELEG_NOTE_CHANGE_KEY:5859str = gettext("Allows changing or adding an encryption key");5860break;5861/*5862* case ZFS_DELEG_NOTE_VSCAN:5863* str = gettext("");5864* break;5865*/5866/* OTHER */5867case ZFS_DELEG_NOTE_GROUPQUOTA:5868str = gettext("Allows accessing any groupquota@... property");5869break;5870case ZFS_DELEG_NOTE_GROUPUSED:5871str = gettext("Allows reading any groupused@... property");5872break;5873case ZFS_DELEG_NOTE_USERPROP:5874str = gettext("Allows changing any user property");5875break;5876case ZFS_DELEG_NOTE_USERQUOTA:5877str = gettext("Allows accessing any userquota@... property");5878break;5879case ZFS_DELEG_NOTE_USERUSED:5880str = gettext("Allows reading any userused@... property");5881break;5882case ZFS_DELEG_NOTE_USEROBJQUOTA:5883str = gettext("Allows accessing any userobjquota@... property");5884break;5885case ZFS_DELEG_NOTE_GROUPOBJQUOTA:5886str = gettext("Allows accessing any \n\t\t\t\t"5887"groupobjquota@... property");5888break;5889case ZFS_DELEG_NOTE_GROUPOBJUSED:5890str = gettext("Allows reading any groupobjused@... property");5891break;5892case ZFS_DELEG_NOTE_USEROBJUSED:5893str = gettext("Allows reading any userobjused@... property");5894break;5895case ZFS_DELEG_NOTE_PROJECTQUOTA:5896str = gettext("Allows accessing any projectquota@... property");5897break;5898case ZFS_DELEG_NOTE_PROJECTOBJQUOTA:5899str = gettext("Allows accessing any \n\t\t\t\t"5900"projectobjquota@... property");5901break;5902case ZFS_DELEG_NOTE_PROJECTUSED:5903str = gettext("Allows reading any projectused@... property");5904break;5905case ZFS_DELEG_NOTE_PROJECTOBJUSED:5906str = gettext("Allows accessing any \n\t\t\t\t"5907"projectobjused@... property");5908break;5909/* other */5910default:5911str = "";5912}59135914return (str);5915}59165917struct allow_opts {5918boolean_t local;5919boolean_t descend;5920boolean_t user;5921boolean_t group;5922boolean_t everyone;5923boolean_t create;5924boolean_t set;5925boolean_t recursive; /* unallow only */5926boolean_t prt_usage;59275928boolean_t prt_perms;5929char *who;5930char *perms;5931const char *dataset;5932};59335934static inline int5935prop_cmp(const void *a, const void *b)5936{5937const char *str1 = *(const char **)a;5938const char *str2 = *(const char **)b;5939return (strcmp(str1, str2));5940}59415942static void5943allow_usage(boolean_t un, boolean_t requested, const char *msg)5944{5945const char *opt_desc[] = {5946"-h", gettext("show this help message and exit"),5947"-l", gettext("set permission locally"),5948"-d", gettext("set permission for descents"),5949"-u", gettext("set permission for user"),5950"-g", gettext("set permission for group"),5951"-e", gettext("set permission for everyone"),5952"-c", gettext("set create time permission"),5953"-s", gettext("define permission set"),5954/* unallow only */5955"-r", gettext("remove permissions recursively"),5956};5957size_t unallow_size = sizeof (opt_desc) / sizeof (char *);5958size_t allow_size = unallow_size - 2;5959const char *props[ZFS_NUM_PROPS];5960int i;5961size_t count = 0;5962FILE *fp = requested ? stdout : stderr;5963zprop_desc_t *pdtbl = zfs_prop_get_table();5964const char *fmt = gettext("%-16s %-14s\t%s\n");59655966(void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW :5967HELP_ALLOW));5968(void) fprintf(fp, gettext("Options:\n"));5969for (i = 0; i < (un ? unallow_size : allow_size); i += 2) {5970const char *opt = opt_desc[i];5971const char *optdsc = opt_desc[i + 1];5972(void) fprintf(fp, gettext(" %-10s %s\n"), opt, optdsc);5973}59745975(void) fprintf(fp, gettext("\nThe following permissions are "5976"supported:\n\n"));5977(void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"),5978gettext("NOTES"));5979for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) {5980const char *perm_name = zfs_deleg_perm_tbl[i].z_perm;5981zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note;5982const char *perm_type = deleg_perm_type(perm_note);5983const char *perm_comment = deleg_perm_comment(perm_note);5984(void) fprintf(fp, fmt, perm_name, perm_type, perm_comment);5985}59865987for (i = 0; i < ZFS_NUM_PROPS; i++) {5988zprop_desc_t *pd = &pdtbl[i];5989if (pd->pd_visible != B_TRUE)5990continue;59915992if (pd->pd_attr == PROP_READONLY)5993continue;59945995props[count++] = pd->pd_name;5996}5997props[count] = NULL;59985999qsort(props, count, sizeof (char *), prop_cmp);60006001for (i = 0; i < count; i++)6002(void) fprintf(fp, fmt, props[i], gettext("property"), "");60036004if (msg != NULL)6005(void) fprintf(fp, gettext("\nzfs: error: %s"), msg);60066007exit(requested ? 0 : 2);6008}60096010static inline const char *6011munge_args(int argc, char **argv, boolean_t un, size_t expected_argc,6012char **permsp)6013{6014if (un && argc == expected_argc - 1)6015*permsp = NULL;6016else if (argc == expected_argc)6017*permsp = argv[argc - 2];6018else6019allow_usage(un, B_FALSE,6020gettext("wrong number of parameters\n"));60216022return (argv[argc - 1]);6023}60246025static void6026parse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts)6027{6028int uge_sum = opts->user + opts->group + opts->everyone;6029int csuge_sum = opts->create + opts->set + uge_sum;6030int ldcsuge_sum = csuge_sum + opts->local + opts->descend;6031int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum;60326033if (uge_sum > 1)6034allow_usage(un, B_FALSE,6035gettext("-u, -g, and -e are mutually exclusive\n"));60366037if (opts->prt_usage) {6038if (argc == 0 && all_sum == 0)6039allow_usage(un, B_TRUE, NULL);6040else6041usage(B_FALSE);6042}60436044if (opts->set) {6045if (csuge_sum > 1)6046allow_usage(un, B_FALSE,6047gettext("invalid options combined with -s\n"));60486049opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);6050if (argv[0][0] != '@')6051allow_usage(un, B_FALSE,6052gettext("invalid set name: missing '@' prefix\n"));6053opts->who = argv[0];6054} else if (opts->create) {6055if (ldcsuge_sum > 1)6056allow_usage(un, B_FALSE,6057gettext("invalid options combined with -c\n"));6058opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);6059} else if (opts->everyone) {6060if (csuge_sum > 1)6061allow_usage(un, B_FALSE,6062gettext("invalid options combined with -e\n"));6063opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);6064} else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone")6065== 0) {6066opts->everyone = B_TRUE;6067argc--;6068argv++;6069opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);6070} else if (argc == 1 && !un) {6071opts->prt_perms = B_TRUE;6072opts->dataset = argv[argc-1];6073} else {6074opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);6075opts->who = argv[0];6076}60776078if (!opts->local && !opts->descend) {6079opts->local = B_TRUE;6080opts->descend = B_TRUE;6081}6082}60836084static void6085store_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend,6086const char *who, char *perms, nvlist_t *top_nvl)6087{6088int i;6089char ld[2] = { '\0', '\0' };6090char who_buf[MAXNAMELEN + 32];6091char base_type = '\0';6092char set_type = '\0';6093nvlist_t *base_nvl = NULL;6094nvlist_t *set_nvl = NULL;6095nvlist_t *nvl;60966097if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0)6098nomem();6099if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) != 0)6100nomem();61016102switch (type) {6103case ZFS_DELEG_NAMED_SET_SETS:6104case ZFS_DELEG_NAMED_SET:6105set_type = ZFS_DELEG_NAMED_SET_SETS;6106base_type = ZFS_DELEG_NAMED_SET;6107ld[0] = ZFS_DELEG_NA;6108break;6109case ZFS_DELEG_CREATE_SETS:6110case ZFS_DELEG_CREATE:6111set_type = ZFS_DELEG_CREATE_SETS;6112base_type = ZFS_DELEG_CREATE;6113ld[0] = ZFS_DELEG_NA;6114break;6115case ZFS_DELEG_USER_SETS:6116case ZFS_DELEG_USER:6117set_type = ZFS_DELEG_USER_SETS;6118base_type = ZFS_DELEG_USER;6119if (local)6120ld[0] = ZFS_DELEG_LOCAL;6121if (descend)6122ld[1] = ZFS_DELEG_DESCENDENT;6123break;6124case ZFS_DELEG_GROUP_SETS:6125case ZFS_DELEG_GROUP:6126set_type = ZFS_DELEG_GROUP_SETS;6127base_type = ZFS_DELEG_GROUP;6128if (local)6129ld[0] = ZFS_DELEG_LOCAL;6130if (descend)6131ld[1] = ZFS_DELEG_DESCENDENT;6132break;6133case ZFS_DELEG_EVERYONE_SETS:6134case ZFS_DELEG_EVERYONE:6135set_type = ZFS_DELEG_EVERYONE_SETS;6136base_type = ZFS_DELEG_EVERYONE;6137if (local)6138ld[0] = ZFS_DELEG_LOCAL;6139if (descend)6140ld[1] = ZFS_DELEG_DESCENDENT;6141break;61426143default:6144assert(set_type != '\0' && base_type != '\0');6145}61466147if (perms != NULL) {6148char *curr = perms;6149char *end = curr + strlen(perms);61506151while (curr < end) {6152char *delim = strchr(curr, ',');6153if (delim == NULL)6154delim = end;6155else6156*delim = '\0';61576158if (curr[0] == '@')6159nvl = set_nvl;6160else6161nvl = base_nvl;61626163(void) nvlist_add_boolean(nvl, curr);6164if (delim != end)6165*delim = ',';6166curr = delim + 1;6167}61686169for (i = 0; i < 2; i++) {6170char locality = ld[i];6171if (locality == 0)6172continue;61736174if (!nvlist_empty(base_nvl)) {6175if (who != NULL)6176(void) snprintf(who_buf,6177sizeof (who_buf), "%c%c$%s",6178base_type, locality, who);6179else6180(void) snprintf(who_buf,6181sizeof (who_buf), "%c%c$",6182base_type, locality);61836184(void) nvlist_add_nvlist(top_nvl, who_buf,6185base_nvl);6186}618761886189if (!nvlist_empty(set_nvl)) {6190if (who != NULL)6191(void) snprintf(who_buf,6192sizeof (who_buf), "%c%c$%s",6193set_type, locality, who);6194else6195(void) snprintf(who_buf,6196sizeof (who_buf), "%c%c$",6197set_type, locality);61986199(void) nvlist_add_nvlist(top_nvl, who_buf,6200set_nvl);6201}6202}6203} else {6204for (i = 0; i < 2; i++) {6205char locality = ld[i];6206if (locality == 0)6207continue;62086209if (who != NULL)6210(void) snprintf(who_buf, sizeof (who_buf),6211"%c%c$%s", base_type, locality, who);6212else6213(void) snprintf(who_buf, sizeof (who_buf),6214"%c%c$", base_type, locality);6215(void) nvlist_add_boolean(top_nvl, who_buf);62166217if (who != NULL)6218(void) snprintf(who_buf, sizeof (who_buf),6219"%c%c$%s", set_type, locality, who);6220else6221(void) snprintf(who_buf, sizeof (who_buf),6222"%c%c$", set_type, locality);6223(void) nvlist_add_boolean(top_nvl, who_buf);6224}6225}6226}62276228static int6229construct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp)6230{6231if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0)6232nomem();62336234if (opts->set) {6235store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local,6236opts->descend, opts->who, opts->perms, *nvlp);6237} else if (opts->create) {6238store_allow_perm(ZFS_DELEG_CREATE, opts->local,6239opts->descend, NULL, opts->perms, *nvlp);6240} else if (opts->everyone) {6241store_allow_perm(ZFS_DELEG_EVERYONE, opts->local,6242opts->descend, NULL, opts->perms, *nvlp);6243} else {6244char *curr = opts->who;6245char *end = curr + strlen(curr);62466247while (curr < end) {6248const char *who;6249zfs_deleg_who_type_t who_type = ZFS_DELEG_WHO_UNKNOWN;6250char *endch;6251char *delim = strchr(curr, ',');6252char errbuf[256];6253char id[64];6254struct passwd *p = NULL;6255struct group *g = NULL;62566257uid_t rid;6258if (delim == NULL)6259delim = end;6260else6261*delim = '\0';62626263rid = (uid_t)strtol(curr, &endch, 0);6264if (opts->user) {6265who_type = ZFS_DELEG_USER;6266if (*endch != '\0')6267p = getpwnam(curr);6268else6269p = getpwuid(rid);62706271if (p != NULL)6272rid = p->pw_uid;6273else if (*endch != '\0') {6274(void) snprintf(errbuf, sizeof (errbuf),6275gettext("invalid user %s\n"), curr);6276allow_usage(un, B_TRUE, errbuf);6277}6278} else if (opts->group) {6279who_type = ZFS_DELEG_GROUP;6280if (*endch != '\0')6281g = getgrnam(curr);6282else6283g = getgrgid(rid);62846285if (g != NULL)6286rid = g->gr_gid;6287else if (*endch != '\0') {6288(void) snprintf(errbuf, sizeof (errbuf),6289gettext("invalid group %s\n"),6290curr);6291allow_usage(un, B_TRUE, errbuf);6292}6293} else {6294if (*endch != '\0') {6295p = getpwnam(curr);6296} else {6297p = getpwuid(rid);6298}62996300if (p == NULL) {6301if (*endch != '\0') {6302g = getgrnam(curr);6303} else {6304g = getgrgid(rid);6305}6306}63076308if (p != NULL) {6309who_type = ZFS_DELEG_USER;6310rid = p->pw_uid;6311} else if (g != NULL) {6312who_type = ZFS_DELEG_GROUP;6313rid = g->gr_gid;6314} else {6315(void) snprintf(errbuf, sizeof (errbuf),6316gettext("invalid user/group %s\n"),6317curr);6318allow_usage(un, B_TRUE, errbuf);6319}6320}63216322(void) sprintf(id, "%u", rid);6323who = id;63246325store_allow_perm(who_type, opts->local,6326opts->descend, who, opts->perms, *nvlp);6327curr = delim + 1;6328}6329}63306331return (0);6332}63336334static void6335print_set_creat_perms(avl_tree_t *who_avl)6336{6337const char *sc_title[] = {6338gettext("Permission sets:\n"),6339gettext("Create time permissions:\n"),6340NULL6341};6342who_perm_node_t *who_node = NULL;6343int prev_weight = -1;63446345for (who_node = avl_first(who_avl); who_node != NULL;6346who_node = AVL_NEXT(who_avl, who_node)) {6347avl_tree_t *avl = &who_node->who_perm.who_deleg_perm_avl;6348zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;6349const char *who_name = who_node->who_perm.who_name;6350int weight = who_type2weight(who_type);6351boolean_t first = B_TRUE;6352deleg_perm_node_t *deleg_node;63536354if (prev_weight != weight) {6355(void) printf("%s", sc_title[weight]);6356prev_weight = weight;6357}63586359if (who_name == NULL || strnlen(who_name, 1) == 0)6360(void) printf("\t");6361else6362(void) printf("\t%s ", who_name);63636364for (deleg_node = avl_first(avl); deleg_node != NULL;6365deleg_node = AVL_NEXT(avl, deleg_node)) {6366if (first) {6367(void) printf("%s",6368deleg_node->dpn_perm.dp_name);6369first = B_FALSE;6370} else6371(void) printf(",%s",6372deleg_node->dpn_perm.dp_name);6373}63746375(void) printf("\n");6376}6377}63786379static void6380print_uge_deleg_perms(avl_tree_t *who_avl, boolean_t local, boolean_t descend,6381const char *title)6382{6383who_perm_node_t *who_node = NULL;6384boolean_t prt_title = B_TRUE;63856386for (who_node = avl_first(who_avl); who_node != NULL;6387who_node = AVL_NEXT(who_avl, who_node)) {6388const char *who_name = who_node->who_perm.who_name;6389const char *nice_who_name = who_node->who_perm.who_ug_name;6390avl_tree_t *avl = &who_node->who_perm.who_deleg_perm_avl;6391zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;6392char delim = ' ';6393deleg_perm_node_t *deleg_node;6394boolean_t prt_who = B_TRUE;63956396for (deleg_node = avl_first(avl); deleg_node != NULL;6397deleg_node = AVL_NEXT(avl, deleg_node)) {6398if (local != deleg_node->dpn_perm.dp_local ||6399descend != deleg_node->dpn_perm.dp_descend)6400continue;64016402if (prt_who) {6403const char *who = NULL;6404if (prt_title) {6405prt_title = B_FALSE;6406(void) printf("%s", title);6407}64086409switch (who_type) {6410case ZFS_DELEG_USER_SETS:6411case ZFS_DELEG_USER:6412who = gettext("user");6413if (nice_who_name)6414who_name = nice_who_name;6415break;6416case ZFS_DELEG_GROUP_SETS:6417case ZFS_DELEG_GROUP:6418who = gettext("group");6419if (nice_who_name)6420who_name = nice_who_name;6421break;6422case ZFS_DELEG_EVERYONE_SETS:6423case ZFS_DELEG_EVERYONE:6424who = gettext("everyone");6425who_name = NULL;6426break;64276428default:6429assert(who != NULL);6430}64316432prt_who = B_FALSE;6433if (who_name == NULL)6434(void) printf("\t%s", who);6435else6436(void) printf("\t%s %s", who, who_name);6437}64386439(void) printf("%c%s", delim,6440deleg_node->dpn_perm.dp_name);6441delim = ',';6442}64436444if (!prt_who)6445(void) printf("\n");6446}6447}64486449static void6450print_fs_perms(fs_perm_set_t *fspset)6451{6452fs_perm_node_t *node = NULL;6453char buf[MAXNAMELEN + 32];6454const char *dsname = buf;64556456for (node = list_head(&fspset->fsps_list); node != NULL;6457node = list_next(&fspset->fsps_list, node)) {6458avl_tree_t *sc_avl = &node->fspn_fsperm.fsp_sc_avl;6459avl_tree_t *uge_avl = &node->fspn_fsperm.fsp_uge_avl;6460int left = 0;64616462(void) snprintf(buf, sizeof (buf),6463gettext("---- Permissions on %s "),6464node->fspn_fsperm.fsp_name);6465(void) printf("%s", dsname);6466left = 70 - strlen(buf);6467while (left-- > 0)6468(void) printf("-");6469(void) printf("\n");64706471print_set_creat_perms(sc_avl);6472print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE,6473gettext("Local permissions:\n"));6474print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE,6475gettext("Descendent permissions:\n"));6476print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE,6477gettext("Local+Descendent permissions:\n"));6478}6479}64806481static fs_perm_set_t fs_perm_set = {};64826483struct deleg_perms {6484boolean_t un;6485nvlist_t *nvl;6486};64876488static int6489set_deleg_perms(zfs_handle_t *zhp, void *data)6490{6491struct deleg_perms *perms = (struct deleg_perms *)data;6492zfs_type_t zfs_type = zfs_get_type(zhp);64936494if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME)6495return (0);64966497return (zfs_set_fsacl(zhp, perms->un, perms->nvl));6498}64996500static int6501zfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un)6502{6503zfs_handle_t *zhp;6504nvlist_t *perm_nvl = NULL;6505nvlist_t *update_perm_nvl = NULL;6506int error = 1;6507int c;6508struct allow_opts opts = { 0 };65096510const char *optstr = un ? "ldugecsrh" : "ldugecsh";65116512/* check opts */6513while ((c = getopt(argc, argv, optstr)) != -1) {6514switch (c) {6515case 'l':6516opts.local = B_TRUE;6517break;6518case 'd':6519opts.descend = B_TRUE;6520break;6521case 'u':6522opts.user = B_TRUE;6523break;6524case 'g':6525opts.group = B_TRUE;6526break;6527case 'e':6528opts.everyone = B_TRUE;6529break;6530case 's':6531opts.set = B_TRUE;6532break;6533case 'c':6534opts.create = B_TRUE;6535break;6536case 'r':6537opts.recursive = B_TRUE;6538break;6539case ':':6540(void) fprintf(stderr, gettext("missing argument for "6541"'%c' option\n"), optopt);6542usage(B_FALSE);6543break;6544case 'h':6545opts.prt_usage = B_TRUE;6546break;6547case '?':6548(void) fprintf(stderr, gettext("invalid option '%c'\n"),6549optopt);6550usage(B_FALSE);6551}6552}65536554argc -= optind;6555argv += optind;65566557/* check arguments */6558parse_allow_args(argc, argv, un, &opts);65596560/* try to open the dataset */6561if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM |6562ZFS_TYPE_VOLUME)) == NULL) {6563(void) fprintf(stderr, "Failed to open dataset: %s\n",6564opts.dataset);6565return (-1);6566}65676568if (zfs_get_fsacl(zhp, &perm_nvl) != 0)6569goto cleanup2;65706571fs_perm_set_init(&fs_perm_set);6572if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) {6573(void) fprintf(stderr, "Failed to parse fsacl permissions\n");6574goto cleanup1;6575}65766577if (opts.prt_perms)6578print_fs_perms(&fs_perm_set);6579else {6580(void) construct_fsacl_list(un, &opts, &update_perm_nvl);6581if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0)6582goto cleanup0;65836584if (un && opts.recursive) {6585struct deleg_perms data = { un, update_perm_nvl };6586if (zfs_iter_filesystems_v2(zhp, 0, set_deleg_perms,6587&data) != 0)6588goto cleanup0;6589}6590}65916592error = 0;65936594cleanup0:6595nvlist_free(perm_nvl);6596nvlist_free(update_perm_nvl);6597cleanup1:6598fs_perm_set_fini(&fs_perm_set);6599cleanup2:6600zfs_close(zhp);66016602return (error);6603}66046605static int6606zfs_do_allow(int argc, char **argv)6607{6608return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE));6609}66106611static int6612zfs_do_unallow(int argc, char **argv)6613{6614return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE));6615}66166617static int6618zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)6619{6620int errors = 0;6621int i;6622const char *tag;6623boolean_t recursive = B_FALSE;6624const char *opts = holding ? "rt" : "r";6625int c;66266627/* check options */6628while ((c = getopt(argc, argv, opts)) != -1) {6629switch (c) {6630case 'r':6631recursive = B_TRUE;6632break;6633case '?':6634(void) fprintf(stderr, gettext("invalid option '%c'\n"),6635optopt);6636usage(B_FALSE);6637}6638}66396640argc -= optind;6641argv += optind;66426643/* check number of arguments */6644if (argc < 2)6645usage(B_FALSE);66466647tag = argv[0];6648--argc;6649++argv;66506651if (holding && tag[0] == '.') {6652/* tags starting with '.' are reserved for libzfs */6653(void) fprintf(stderr, gettext("tag may not start with '.'\n"));6654usage(B_FALSE);6655}66566657for (i = 0; i < argc; ++i) {6658zfs_handle_t *zhp;6659char parent[ZFS_MAX_DATASET_NAME_LEN];6660const char *delim;6661char *path = argv[i];66626663delim = strchr(path, '@');6664if (delim == NULL) {6665(void) fprintf(stderr,6666gettext("'%s' is not a snapshot\n"), path);6667++errors;6668continue;6669}6670(void) strlcpy(parent, path, MIN(sizeof (parent),6671delim - path + 1));66726673zhp = zfs_open(g_zfs, parent,6674ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);6675if (zhp == NULL) {6676++errors;6677continue;6678}6679if (holding) {6680if (zfs_hold(zhp, delim+1, tag, recursive, -1) != 0)6681++errors;6682} else {6683if (zfs_release(zhp, delim+1, tag, recursive) != 0)6684++errors;6685}6686zfs_close(zhp);6687}66886689return (errors != 0);6690}66916692/*6693* zfs hold [-r] [-t] <tag> <snap> ...6694*6695* -r Recursively hold6696*6697* Apply a user-hold with the given tag to the list of snapshots.6698*/6699static int6700zfs_do_hold(int argc, char **argv)6701{6702return (zfs_do_hold_rele_impl(argc, argv, B_TRUE));6703}67046705/*6706* zfs release [-r] <tag> <snap> ...6707*6708* -r Recursively release6709*6710* Release a user-hold with the given tag from the list of snapshots.6711*/6712static int6713zfs_do_release(int argc, char **argv)6714{6715return (zfs_do_hold_rele_impl(argc, argv, B_FALSE));6716}67176718typedef struct holds_cbdata {6719boolean_t cb_recursive;6720const char *cb_snapname;6721nvlist_t **cb_nvlp;6722size_t cb_max_namelen;6723size_t cb_max_taglen;6724} holds_cbdata_t;67256726#define STRFTIME_FMT_STR "%a %b %e %H:%M %Y"6727#define DATETIME_BUF_LEN (32)6728/*6729*6730*/6731static void6732print_holds(boolean_t scripted, int nwidth, int tagwidth, nvlist_t *nvl,6733boolean_t parsable)6734{6735int i;6736nvpair_t *nvp = NULL;6737const char *const hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" };6738const char *col;67396740if (!scripted) {6741for (i = 0; i < 3; i++) {6742col = gettext(hdr_cols[i]);6743if (i < 2)6744(void) printf("%-*s ", i ? tagwidth : nwidth,6745col);6746else6747(void) printf("%s\n", col);6748}6749}67506751while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {6752const char *zname = nvpair_name(nvp);6753nvlist_t *nvl2;6754nvpair_t *nvp2 = NULL;6755(void) nvpair_value_nvlist(nvp, &nvl2);6756while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) {6757char tsbuf[DATETIME_BUF_LEN];6758const char *tagname = nvpair_name(nvp2);6759uint64_t val = 0;6760time_t time;6761struct tm t;67626763(void) nvpair_value_uint64(nvp2, &val);6764time = (time_t)val;6765(void) localtime_r(&time, &t);6766(void) strftime(tsbuf, DATETIME_BUF_LEN,6767gettext(STRFTIME_FMT_STR), &t);67686769if (scripted) {6770if (parsable) {6771(void) printf("%s\t%s\t%lld\n", zname,6772tagname, (long long)time);6773} else {6774(void) printf("%s\t%s\t%s\n", zname,6775tagname, tsbuf);6776}6777} else {6778if (parsable) {6779(void) printf("%-*s %-*s %lld\n",6780nwidth, zname, tagwidth,6781tagname, (long long)time);6782} else {6783(void) printf("%-*s %-*s %s\n",6784nwidth, zname, tagwidth,6785tagname, tsbuf);6786}6787}6788}6789}6790}67916792/*6793* Generic callback function to list a dataset or snapshot.6794*/6795static int6796holds_callback(zfs_handle_t *zhp, void *data)6797{6798holds_cbdata_t *cbp = data;6799nvlist_t *top_nvl = *cbp->cb_nvlp;6800nvlist_t *nvl = NULL;6801nvpair_t *nvp = NULL;6802const char *zname = zfs_get_name(zhp);6803size_t znamelen = strlen(zname);68046805if (cbp->cb_recursive) {6806const char *snapname;6807char *delim = strchr(zname, '@');6808if (delim == NULL)6809return (0);68106811snapname = delim + 1;6812if (strcmp(cbp->cb_snapname, snapname))6813return (0);6814}68156816if (zfs_get_holds(zhp, &nvl) != 0)6817return (-1);68186819if (znamelen > cbp->cb_max_namelen)6820cbp->cb_max_namelen = znamelen;68216822while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {6823const char *tag = nvpair_name(nvp);6824size_t taglen = strlen(tag);6825if (taglen > cbp->cb_max_taglen)6826cbp->cb_max_taglen = taglen;6827}68286829return (nvlist_add_nvlist(top_nvl, zname, nvl));6830}68316832/*6833* zfs holds [-rHp] <snap> ...6834*6835* -r Lists holds that are set on the named snapshots recursively.6836* -H Scripted mode; elide headers and separate columns by tabs.6837* -p Display values in parsable (literal) format.6838*/6839static int6840zfs_do_holds(int argc, char **argv)6841{6842int c;6843boolean_t errors = B_FALSE;6844boolean_t scripted = B_FALSE;6845boolean_t recursive = B_FALSE;6846boolean_t parsable = B_FALSE;68476848int types = ZFS_TYPE_SNAPSHOT;6849holds_cbdata_t cb = { 0 };68506851int limit = 0;6852int ret = 0;6853int flags = 0;68546855/* check options */6856while ((c = getopt(argc, argv, "rHp")) != -1) {6857switch (c) {6858case 'r':6859recursive = B_TRUE;6860break;6861case 'H':6862scripted = B_TRUE;6863break;6864case 'p':6865parsable = B_TRUE;6866break;6867case '?':6868(void) fprintf(stderr, gettext("invalid option '%c'\n"),6869optopt);6870usage(B_FALSE);6871}6872}68736874if (recursive) {6875types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;6876flags |= ZFS_ITER_RECURSE;6877}68786879argc -= optind;6880argv += optind;68816882/* check number of arguments */6883if (argc < 1)6884usage(B_FALSE);68856886nvlist_t *nvl = fnvlist_alloc();68876888for (int i = 0; i < argc; ++i) {6889char *snapshot = argv[i];6890const char *delim;6891const char *snapname;68926893delim = strchr(snapshot, '@');6894if (delim == NULL) {6895(void) fprintf(stderr,6896gettext("'%s' is not a snapshot\n"), snapshot);6897errors = B_TRUE;6898continue;6899}6900snapname = delim + 1;6901if (recursive)6902snapshot[delim - snapshot] = '\0';69036904cb.cb_recursive = recursive;6905cb.cb_snapname = snapname;6906cb.cb_nvlp = &nvl;69076908/*6909* 1. collect holds data, set format options6910*/6911ret = zfs_for_each(1, argv + i, flags, types, NULL, NULL, limit,6912holds_callback, &cb);6913if (ret != 0)6914errors = B_TRUE;6915}69166917/*6918* 2. print holds data6919*/6920print_holds(scripted, cb.cb_max_namelen, cb.cb_max_taglen, nvl,6921parsable);69226923if (nvlist_empty(nvl))6924(void) fprintf(stderr, gettext("no datasets available\n"));69256926nvlist_free(nvl);69276928return (errors);6929}69306931#define CHECK_SPINNER 306932#define SPINNER_TIME 3 /* seconds */6933#define MOUNT_TIME 1 /* seconds */69346935typedef struct get_all_state {6936char **ga_datasets;6937int ga_count;6938boolean_t ga_verbose;6939get_all_cb_t *ga_cbp;6940} get_all_state_t;69416942static int6943get_one_dataset(zfs_handle_t *zhp, void *data)6944{6945static const char *const spin[] = { "-", "\\", "|", "/" };6946static int spinval = 0;6947static int spincheck = 0;6948static time_t last_spin_time = (time_t)0;6949get_all_state_t *state = data;6950zfs_type_t type = zfs_get_type(zhp);69516952if (state->ga_verbose) {6953if (--spincheck < 0) {6954time_t now = time(NULL);6955if (last_spin_time + SPINNER_TIME < now) {6956update_progress(spin[spinval++ % 4]);6957last_spin_time = now;6958}6959spincheck = CHECK_SPINNER;6960}6961}69626963/*6964* Iterate over any nested datasets.6965*/6966if (zfs_iter_filesystems_v2(zhp, 0, get_one_dataset, data) != 0) {6967zfs_close(zhp);6968return (1);6969}69706971/*6972* Skip any datasets whose type does not match.6973*/6974if ((type & ZFS_TYPE_FILESYSTEM) == 0) {6975zfs_close(zhp);6976return (0);6977}6978libzfs_add_handle(state->ga_cbp, zhp);6979assert(state->ga_cbp->cb_used <= state->ga_cbp->cb_alloc);69806981return (0);6982}69836984static int6985get_recursive_datasets(zfs_handle_t *zhp, void *data)6986{6987get_all_state_t *state = data;6988int len = strlen(zfs_get_name(zhp));6989for (int i = 0; i < state->ga_count; ++i) {6990if (strcmp(state->ga_datasets[i], zfs_get_name(zhp)) == 0)6991return (get_one_dataset(zhp, data));6992else if ((strncmp(state->ga_datasets[i], zfs_get_name(zhp),6993len) == 0) && state->ga_datasets[i][len] == '/') {6994(void) zfs_iter_filesystems_v2(zhp, 0,6995get_recursive_datasets, data);6996}6997}6998zfs_close(zhp);6999return (0);7000}70017002static void7003get_all_datasets(get_all_state_t *state)7004{7005if (state->ga_verbose)7006set_progress_header(gettext("Reading ZFS config"));7007if (state->ga_datasets == NULL)7008(void) zfs_iter_root(g_zfs, get_one_dataset, state);7009else7010(void) zfs_iter_root(g_zfs, get_recursive_datasets, state);70117012if (state->ga_verbose)7013finish_progress(gettext("done."));7014}70157016/*7017* Generic callback for sharing or mounting filesystems. Because the code is so7018* similar, we have a common function with an extra parameter to determine which7019* mode we are using.7020*/7021typedef enum { OP_SHARE, OP_MOUNT } share_mount_op_t;70227023typedef struct share_mount_state {7024share_mount_op_t sm_op;7025boolean_t sm_verbose;7026int sm_flags;7027char *sm_options;7028enum sa_protocol sm_proto; /* only valid for OP_SHARE */7029pthread_mutex_t sm_lock; /* protects the remaining fields */7030uint_t sm_total; /* number of filesystems to process */7031uint_t sm_done; /* number of filesystems processed */7032int sm_status; /* -1 if any of the share/mount operations failed */7033} share_mount_state_t;70347035/*7036* Share or mount a dataset.7037*/7038static int7039share_mount_one(zfs_handle_t *zhp, int op, int flags, enum sa_protocol protocol,7040boolean_t explicit, const char *options)7041{7042char mountpoint[ZFS_MAXPROPLEN];7043char shareopts[ZFS_MAXPROPLEN];7044char smbshareopts[ZFS_MAXPROPLEN];7045const char *cmdname = op == OP_SHARE ? "share" : "mount";7046struct mnttab mnt;7047uint64_t zoned, canmount;7048boolean_t shared_nfs, shared_smb;70497050assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM);70517052/*7053* Check to make sure we can mount/share this dataset. If we7054* are in the global zone and the filesystem is exported to a7055* local zone, or if we are in a local zone and the7056* filesystem is not exported, then it is an error.7057*/7058zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);70597060if (zoned && getzoneid() == GLOBAL_ZONEID) {7061if (!explicit)7062return (0);70637064(void) fprintf(stderr, gettext("cannot %s '%s': "7065"dataset is exported to a local zone\n"), cmdname,7066zfs_get_name(zhp));7067return (1);70687069} else if (!zoned && getzoneid() != GLOBAL_ZONEID) {7070if (!explicit)7071return (0);70727073(void) fprintf(stderr, gettext("cannot %s '%s': "7074"permission denied\n"), cmdname,7075zfs_get_name(zhp));7076return (1);7077}70787079/*7080* Ignore any filesystems which don't apply to us. This7081* includes those with a legacy mountpoint, or those with7082* legacy share options.7083*/7084verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,7085sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);7086verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,7087sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);7088verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts,7089sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0);70907091if (op == OP_SHARE && strcmp(shareopts, "off") == 0 &&7092strcmp(smbshareopts, "off") == 0) {7093if (!explicit)7094return (0);70957096(void) fprintf(stderr, gettext("cannot share '%s': "7097"legacy share\n"), zfs_get_name(zhp));7098(void) fprintf(stderr, gettext("use exports(5) or "7099"smb.conf(5) to share this filesystem, or set "7100"the sharenfs or sharesmb property\n"));7101return (1);7102}71037104/*7105* We cannot share or mount legacy filesystems. If the7106* shareopts is non-legacy but the mountpoint is legacy, we7107* treat it as a legacy share.7108*/7109if (strcmp(mountpoint, "legacy") == 0) {7110if (!explicit)7111return (0);71127113(void) fprintf(stderr, gettext("cannot %s '%s': "7114"legacy mountpoint\n"), cmdname, zfs_get_name(zhp));7115(void) fprintf(stderr, gettext("use %s(8) to "7116"%s this filesystem\n"), cmdname, cmdname);7117return (1);7118}71197120if (strcmp(mountpoint, "none") == 0) {7121if (!explicit)7122return (0);71237124(void) fprintf(stderr, gettext("cannot %s '%s': no "7125"mountpoint set\n"), cmdname, zfs_get_name(zhp));7126return (1);7127}71287129/*7130* canmount explicit outcome7131* on no pass through7132* on yes pass through7133* off no return 07134* off yes display error, return 17135* noauto no return 07136* noauto yes pass through7137*/7138canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);7139if (canmount == ZFS_CANMOUNT_OFF) {7140if (!explicit)7141return (0);71427143(void) fprintf(stderr, gettext("cannot %s '%s': "7144"'canmount' property is set to 'off'\n"), cmdname,7145zfs_get_name(zhp));7146return (1);7147} else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) {7148/*7149* When performing a 'zfs mount -a', we skip any mounts for7150* datasets that have 'noauto' set. Sharing a dataset with7151* 'noauto' set is only allowed if it's mounted.7152*/7153if (op == OP_MOUNT)7154return (0);7155if (op == OP_SHARE && !zfs_is_mounted(zhp, NULL)) {7156/* also purge it from existing exports */7157zfs_unshare(zhp, mountpoint, NULL);7158return (0);7159}7160}71617162/*7163* If this filesystem is encrypted and does not have7164* a loaded key, we can not mount it.7165*/7166if ((flags & MS_CRYPT) == 0 &&7167zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) != ZIO_CRYPT_OFF &&7168zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS) ==7169ZFS_KEYSTATUS_UNAVAILABLE) {7170if (!explicit)7171return (0);71727173(void) fprintf(stderr, gettext("cannot %s '%s': "7174"encryption key not loaded\n"), cmdname, zfs_get_name(zhp));7175return (1);7176}71777178/*7179* If this filesystem is inconsistent and has a receive resume7180* token, we can not mount it.7181*/7182if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) &&7183zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,7184NULL, 0, NULL, NULL, 0, B_TRUE) == 0) {7185if (!explicit)7186return (0);71877188(void) fprintf(stderr, gettext("cannot %s '%s': "7189"Contains partially-completed state from "7190"\"zfs receive -s\", which can be resumed with "7191"\"zfs send -t\"\n"),7192cmdname, zfs_get_name(zhp));7193return (1);7194}71957196if (zfs_prop_get_int(zhp, ZFS_PROP_REDACTED) && !(flags & MS_FORCE)) {7197if (!explicit)7198return (0);71997200(void) fprintf(stderr, gettext("cannot %s '%s': "7201"Dataset is not complete, was created by receiving "7202"a redacted zfs send stream.\n"), cmdname,7203zfs_get_name(zhp));7204return (1);7205}72067207/*7208* At this point, we have verified that the mountpoint and/or7209* shareopts are appropriate for auto management. If the7210* filesystem is already mounted or shared, return (failing7211* for explicit requests); otherwise mount or share the7212* filesystem.7213*/7214switch (op) {7215case OP_SHARE: {7216enum sa_protocol prot[] = {SA_PROTOCOL_NFS, SA_NO_PROTOCOL};7217shared_nfs = zfs_is_shared(zhp, NULL, prot);7218*prot = SA_PROTOCOL_SMB;7219shared_smb = zfs_is_shared(zhp, NULL, prot);72207221if ((shared_nfs && shared_smb) ||7222(shared_nfs && strcmp(shareopts, "on") == 0 &&7223strcmp(smbshareopts, "off") == 0) ||7224(shared_smb && strcmp(smbshareopts, "on") == 0 &&7225strcmp(shareopts, "off") == 0)) {7226if (!explicit)7227return (0);72287229(void) fprintf(stderr, gettext("cannot share "7230"'%s': filesystem already shared\n"),7231zfs_get_name(zhp));7232return (1);7233}72347235if (!zfs_is_mounted(zhp, NULL) &&7236zfs_mount(zhp, NULL, flags) != 0)7237return (1);72387239*prot = protocol;7240if (zfs_share(zhp, protocol == SA_NO_PROTOCOL ? NULL : prot))7241return (1);72427243}7244break;72457246case OP_MOUNT:7247mnt.mnt_mntopts = (char *)(options ?: "");72487249if (!hasmntopt(&mnt, MNTOPT_REMOUNT) &&7250zfs_is_mounted(zhp, NULL)) {7251if (!explicit)7252return (0);72537254(void) fprintf(stderr, gettext("cannot mount "7255"'%s': filesystem already mounted\n"),7256zfs_get_name(zhp));7257return (1);7258}72597260if (zfs_mount(zhp, options, flags) != 0)7261return (1);7262break;7263}72647265return (0);7266}72677268/*7269* Reports progress in the form "(current/total)". Not thread-safe.7270*/7271static void7272report_mount_progress(int current, int total)7273{7274static time_t last_progress_time = 0;7275time_t now = time(NULL);7276char info[32];72777278/* display header if we're here for the first time */7279if (current == 1) {7280set_progress_header(gettext("Mounting ZFS filesystems"));7281} else if (current != total && last_progress_time + MOUNT_TIME >= now) {7282/* too soon to report again */7283return;7284}72857286last_progress_time = now;72877288(void) sprintf(info, "(%d/%d)", current, total);72897290if (current == total)7291finish_progress(info);7292else7293update_progress(info);7294}72957296/*7297* zfs_foreach_mountpoint() callback that mounts or shares one filesystem and7298* updates the progress meter.7299*/7300static int7301share_mount_one_cb(zfs_handle_t *zhp, void *arg)7302{7303share_mount_state_t *sms = arg;7304int ret;73057306ret = share_mount_one(zhp, sms->sm_op, sms->sm_flags, sms->sm_proto,7307B_FALSE, sms->sm_options);73087309pthread_mutex_lock(&sms->sm_lock);7310if (ret != 0)7311sms->sm_status = ret;7312sms->sm_done++;7313if (sms->sm_verbose)7314report_mount_progress(sms->sm_done, sms->sm_total);7315pthread_mutex_unlock(&sms->sm_lock);7316return (ret);7317}73187319static void7320append_options(char *mntopts, char *newopts)7321{7322int len = strlen(mntopts);73237324/* original length plus new string to append plus 1 for the comma */7325if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) {7326(void) fprintf(stderr, gettext("the opts argument for "7327"'%s' option is too long (more than %d chars)\n"),7328"-o", MNT_LINE_MAX);7329usage(B_FALSE);7330}73317332if (*mntopts)7333mntopts[len++] = ',';73347335(void) strcpy(&mntopts[len], newopts);7336}73377338static enum sa_protocol7339sa_protocol_decode(const char *protocol)7340{7341for (enum sa_protocol i = 0; i < ARRAY_SIZE(sa_protocol_names); ++i)7342if (strcmp(protocol, sa_protocol_names[i]) == 0)7343return (i);73447345(void) fputs(gettext("share type must be one of: "), stderr);7346for (enum sa_protocol i = 0;7347i < ARRAY_SIZE(sa_protocol_names); ++i)7348(void) fprintf(stderr, "%s%s",7349i != 0 ? ", " : "", sa_protocol_names[i]);7350(void) fputc('\n', stderr);7351usage(B_FALSE);7352}73537354static int7355share_mount(int op, int argc, char **argv)7356{7357int do_all = 0;7358int recursive = 0;7359boolean_t verbose = B_FALSE;7360boolean_t json = B_FALSE;7361int c, ret = 0;7362char *options = NULL;7363int flags = 0;7364nvlist_t *jsobj, *data, *item;7365const uint_t mount_nthr = 512;7366uint_t nthr;7367jsobj = data = item = NULL;73687369struct option long_options[] = {7370{"json", no_argument, NULL, 'j'},7371{0, 0, 0, 0}7372};73737374/* check options */7375while ((c = getopt_long(argc, argv,7376op == OP_MOUNT ? ":ajRlvo:Of" : "al",7377op == OP_MOUNT ? long_options : NULL, NULL)) != -1) {7378switch (c) {7379case 'a':7380do_all = 1;7381break;7382case 'R':7383recursive = 1;7384break;7385case 'v':7386verbose = B_TRUE;7387break;7388case 'l':7389flags |= MS_CRYPT;7390break;7391case 'j':7392json = B_TRUE;7393jsobj = zfs_json_schema(0, 1);7394data = fnvlist_alloc();7395break;7396case 'o':7397if (*optarg == '\0') {7398(void) fprintf(stderr, gettext("empty mount "7399"options (-o) specified\n"));7400usage(B_FALSE);7401}74027403if (options == NULL)7404options = safe_malloc(MNT_LINE_MAX + 1);74057406/* option validation is done later */7407append_options(options, optarg);7408break;7409case 'O':7410flags |= MS_OVERLAY;7411break;7412case 'f':7413flags |= MS_FORCE;7414break;7415case ':':7416(void) fprintf(stderr, gettext("missing argument for "7417"'%c' option\n"), optopt);7418usage(B_FALSE);7419break;7420case '?':7421(void) fprintf(stderr, gettext("invalid option '%c'\n"),7422optopt);7423usage(B_FALSE);7424}7425}74267427argc -= optind;7428argv += optind;74297430if (json && argc != 0) {7431(void) fprintf(stderr, gettext("too many arguments\n"));7432usage(B_FALSE);7433}74347435/* check number of arguments */7436if (do_all || recursive) {7437enum sa_protocol protocol = SA_NO_PROTOCOL;74387439if (op == OP_SHARE && argc > 0) {7440protocol = sa_protocol_decode(argv[0]);7441argc--;7442argv++;7443}74447445if (argc != 0 && do_all) {7446(void) fprintf(stderr, gettext("too many arguments\n"));7447usage(B_FALSE);7448}74497450if (argc == 0 && recursive) {7451(void) fprintf(stderr,7452gettext("no dataset provided\n"));7453usage(B_FALSE);7454}74557456start_progress_timer();7457get_all_cb_t cb = { 0 };7458get_all_state_t state = { 0 };7459if (argc == 0) {7460state.ga_datasets = NULL;7461state.ga_count = -1;7462} else {7463zfs_handle_t *zhp;7464for (int i = 0; i < argc; i++) {7465zhp = zfs_open(g_zfs, argv[i],7466ZFS_TYPE_FILESYSTEM);7467if (zhp == NULL)7468usage(B_FALSE);7469zfs_close(zhp);7470}7471state.ga_datasets = argv;7472state.ga_count = argc;7473}7474state.ga_verbose = verbose;7475state.ga_cbp = &cb;7476get_all_datasets(&state);74777478if (cb.cb_used == 0) {7479free(options);7480return (0);7481}74827483share_mount_state_t share_mount_state = { 0 };7484share_mount_state.sm_op = op;7485share_mount_state.sm_verbose = verbose;7486share_mount_state.sm_flags = flags;7487share_mount_state.sm_options = options;7488share_mount_state.sm_proto = protocol;7489share_mount_state.sm_total = cb.cb_used;7490pthread_mutex_init(&share_mount_state.sm_lock, NULL);74917492/* For a 'zfs share -a' operation start with a clean slate. */7493if (op == OP_SHARE)7494zfs_truncate_shares(NULL);74957496/*7497* libshare isn't mt-safe, so only do the operation in parallel7498* if we're mounting. Additionally, the key-loading option must7499* be serialized so that we can prompt the user for their keys7500* in a consistent manner.7501*/7502nthr = op == OP_MOUNT && !(flags & MS_CRYPT) ? mount_nthr : 1;7503zfs_foreach_mountpoint(g_zfs, cb.cb_handles, cb.cb_used,7504share_mount_one_cb, &share_mount_state, nthr);7505zfs_commit_shares(NULL);75067507ret = share_mount_state.sm_status;75087509for (int i = 0; i < cb.cb_used; i++)7510zfs_close(cb.cb_handles[i]);7511free(cb.cb_handles);7512} else if (argc == 0) {7513FILE *mnttab;7514struct mnttab entry;75157516if ((op == OP_SHARE) || (options != NULL)) {7517(void) fprintf(stderr, gettext("missing filesystem "7518"argument (specify -a for all)\n"));7519usage(B_FALSE);7520}75217522/*7523* When mount is given no arguments, go through7524* /proc/self/mounts and display any active ZFS mounts.7525* We hide any snapshots, since they are controlled7526* automatically.7527*/75287529if ((mnttab = fopen(MNTTAB, "re")) == NULL) {7530free(options);7531return (ENOENT);7532}75337534while (getmntent(mnttab, &entry) == 0) {7535if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 ||7536strchr(entry.mnt_special, '@') != NULL)7537continue;7538if (json) {7539item = fnvlist_alloc();7540fnvlist_add_string(item, "filesystem",7541entry.mnt_special);7542fnvlist_add_string(item, "mountpoint",7543entry.mnt_mountp);7544fnvlist_add_nvlist(data, entry.mnt_special,7545item);7546fnvlist_free(item);7547} else {7548(void) printf("%-30s %s\n", entry.mnt_special,7549entry.mnt_mountp);7550}7551}75527553(void) fclose(mnttab);7554if (json) {7555fnvlist_add_nvlist(jsobj, "datasets", data);7556if (nvlist_empty(data))7557fnvlist_free(jsobj);7558else7559zcmd_print_json(jsobj);7560fnvlist_free(data);7561}7562} else {7563zfs_handle_t *zhp;75647565if (argc > 1) {7566(void) fprintf(stderr,7567gettext("too many arguments\n"));7568usage(B_FALSE);7569}75707571if ((zhp = zfs_open(g_zfs, argv[0],7572ZFS_TYPE_FILESYSTEM)) == NULL) {7573ret = 1;7574} else {7575ret = share_mount_one(zhp, op, flags, SA_NO_PROTOCOL,7576B_TRUE, options);7577zfs_commit_shares(NULL);7578zfs_close(zhp);7579}7580}75817582free(options);7583return (ret);7584}75857586/*7587* zfs mount -a7588* zfs mount filesystem7589*7590* Mount all filesystems, or mount the given filesystem.7591*/7592static int7593zfs_do_mount(int argc, char **argv)7594{7595return (share_mount(OP_MOUNT, argc, argv));7596}75977598/*7599* zfs share -a [nfs | smb]7600* zfs share filesystem7601*7602* Share all filesystems, or share the given filesystem.7603*/7604static int7605zfs_do_share(int argc, char **argv)7606{7607return (share_mount(OP_SHARE, argc, argv));7608}76097610typedef struct unshare_unmount_node {7611zfs_handle_t *un_zhp;7612char *un_mountp;7613avl_node_t un_avlnode;7614} unshare_unmount_node_t;76157616static int7617unshare_unmount_compare(const void *larg, const void *rarg)7618{7619const unshare_unmount_node_t *l = larg;7620const unshare_unmount_node_t *r = rarg;76217622return (TREE_ISIGN(strcmp(l->un_mountp, r->un_mountp)));7623}76247625/*7626* Convenience routine used by zfs_do_umount() and manual_unmount(). Given an7627* absolute path, find the entry /proc/self/mounts, verify that it's a7628* ZFS filesystem, and unmount it appropriately.7629*/7630static int7631unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)7632{7633zfs_handle_t *zhp;7634int ret = 0;7635struct stat64 statbuf;7636struct extmnttab entry;7637const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount";7638ino_t path_inode;7639char *zfs_mntpnt, *entry_mntpnt;76407641/*7642* Search for the given (major,minor) pair in the mount table.7643*/76447645if (getextmntent(path, &entry, &statbuf) != 0) {7646if (op == OP_SHARE) {7647(void) fprintf(stderr, gettext("cannot %s '%s': not "7648"currently mounted\n"), cmdname, path);7649return (1);7650}7651(void) fprintf(stderr, gettext("warning: %s not in"7652"/proc/self/mounts\n"), path);7653if ((ret = umount2(path, flags)) != 0)7654(void) fprintf(stderr, gettext("%s: %s\n"), path,7655strerror(errno));7656return (ret != 0);7657}7658path_inode = statbuf.st_ino;76597660if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) {7661(void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS "7662"filesystem\n"), cmdname, path);7663return (1);7664}76657666if ((zhp = zfs_open(g_zfs, entry.mnt_special,7667ZFS_TYPE_FILESYSTEM)) == NULL)7668return (1);76697670ret = 1;7671if (stat64(entry.mnt_mountp, &statbuf) != 0) {7672(void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),7673cmdname, path, strerror(errno));7674goto out;7675} else if (statbuf.st_ino != path_inode) {7676(void) fprintf(stderr, gettext("cannot "7677"%s '%s': not a mountpoint\n"), cmdname, path);7678goto out;7679}76807681/*7682* If the filesystem is mounted, check that the mountpoint matches7683* the one in the mnttab entry w.r.t. provided path. If it doesn't,7684* then we should not proceed further.7685*/7686entry_mntpnt = strdup(entry.mnt_mountp);7687if (zfs_is_mounted(zhp, &zfs_mntpnt)) {7688if (strcmp(zfs_mntpnt, entry_mntpnt) != 0) {7689(void) fprintf(stderr, gettext("cannot %s '%s': "7690"not an original mountpoint\n"), cmdname, path);7691free(zfs_mntpnt);7692free(entry_mntpnt);7693goto out;7694}7695free(zfs_mntpnt);7696}7697free(entry_mntpnt);76987699if (op == OP_SHARE) {7700char nfs_mnt_prop[ZFS_MAXPROPLEN];7701char smbshare_prop[ZFS_MAXPROPLEN];77027703verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, nfs_mnt_prop,7704sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0);7705verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshare_prop,7706sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0);77077708if (strcmp(nfs_mnt_prop, "off") == 0 &&7709strcmp(smbshare_prop, "off") == 0) {7710(void) fprintf(stderr, gettext("cannot unshare "7711"'%s': legacy share\n"), path);7712(void) fprintf(stderr, gettext("use exportfs(8) "7713"or smbcontrol(1) to unshare this filesystem\n"));7714} else if (!zfs_is_shared(zhp, NULL, NULL)) {7715(void) fprintf(stderr, gettext("cannot unshare '%s': "7716"not currently shared\n"), path);7717} else {7718ret = zfs_unshare(zhp, path, NULL);7719zfs_commit_shares(NULL);7720}7721} else {7722char mtpt_prop[ZFS_MAXPROPLEN];77237724verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop,7725sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0);77267727if (is_manual) {7728ret = zfs_unmount(zhp, NULL, flags);7729} else if (strcmp(mtpt_prop, "legacy") == 0) {7730(void) fprintf(stderr, gettext("cannot unmount "7731"'%s': legacy mountpoint\n"),7732zfs_get_name(zhp));7733(void) fprintf(stderr, gettext("use umount(8) "7734"to unmount this filesystem\n"));7735} else {7736ret = zfs_unmountall(zhp, flags);7737}7738}77397740out:7741zfs_close(zhp);77427743return (ret != 0);7744}77457746/*7747* Generic callback for unsharing or unmounting a filesystem.7748*/7749static int7750unshare_unmount(int op, int argc, char **argv)7751{7752int do_all = 0;7753int flags = 0;7754int ret = 0;7755int c;7756zfs_handle_t *zhp;7757char nfs_mnt_prop[ZFS_MAXPROPLEN];7758char sharesmb[ZFS_MAXPROPLEN];77597760/* check options */7761while ((c = getopt(argc, argv, op == OP_SHARE ? ":a" : "afu")) != -1) {7762switch (c) {7763case 'a':7764do_all = 1;7765break;7766case 'f':7767flags |= MS_FORCE;7768break;7769case 'u':7770flags |= MS_CRYPT;7771break;7772case ':':7773(void) fprintf(stderr, gettext("missing argument for "7774"'%c' option\n"), optopt);7775usage(B_FALSE);7776break;7777case '?':7778(void) fprintf(stderr, gettext("invalid option '%c'\n"),7779optopt);7780usage(B_FALSE);7781}7782}77837784argc -= optind;7785argv += optind;77867787if (do_all) {7788/*7789* We could make use of zfs_for_each() to walk all datasets in7790* the system, but this would be very inefficient, especially7791* since we would have to linearly search /proc/self/mounts for7792* each one. Instead, do one pass through /proc/self/mounts7793* looking for zfs entries and call zfs_unmount() for each one.7794*7795* Things get a little tricky if the administrator has created7796* mountpoints beneath other ZFS filesystems. In this case, we7797* have to unmount the deepest filesystems first. To accomplish7798* this, we place all the mountpoints in an AVL tree sorted by7799* the special type (dataset name), and walk the result in7800* reverse to make sure to get any snapshots first.7801*/7802FILE *mnttab;7803struct mnttab entry;7804avl_tree_t tree;7805unshare_unmount_node_t *node;7806avl_index_t idx;7807enum sa_protocol *protocol = NULL,7808single_protocol[] = {SA_NO_PROTOCOL, SA_NO_PROTOCOL};78097810if (op == OP_SHARE && argc > 0) {7811*single_protocol = sa_protocol_decode(argv[0]);7812protocol = single_protocol;7813argc--;7814argv++;7815}78167817if (argc != 0) {7818(void) fprintf(stderr, gettext("too many arguments\n"));7819usage(B_FALSE);7820}78217822avl_create(&tree, unshare_unmount_compare,7823sizeof (unshare_unmount_node_t),7824offsetof(unshare_unmount_node_t, un_avlnode));78257826if ((mnttab = fopen(MNTTAB, "re")) == NULL) {7827avl_destroy(&tree);7828return (ENOENT);7829}78307831while (getmntent(mnttab, &entry) == 0) {78327833/* ignore non-ZFS entries */7834if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)7835continue;78367837/* ignore snapshots */7838if (strchr(entry.mnt_special, '@') != NULL)7839continue;78407841if ((zhp = zfs_open(g_zfs, entry.mnt_special,7842ZFS_TYPE_FILESYSTEM)) == NULL) {7843ret = 1;7844continue;7845}78467847/*7848* Ignore datasets that are excluded/restricted by7849* parent pool name.7850*/7851if (zpool_skip_pool(zfs_get_pool_name(zhp))) {7852zfs_close(zhp);7853continue;7854}78557856switch (op) {7857case OP_SHARE:7858verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,7859nfs_mnt_prop,7860sizeof (nfs_mnt_prop),7861NULL, NULL, 0, B_FALSE) == 0);7862if (strcmp(nfs_mnt_prop, "off") != 0)7863break;7864verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,7865nfs_mnt_prop,7866sizeof (nfs_mnt_prop),7867NULL, NULL, 0, B_FALSE) == 0);7868if (strcmp(nfs_mnt_prop, "off") == 0)7869continue;7870break;7871case OP_MOUNT:7872/* Ignore legacy mounts */7873verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,7874nfs_mnt_prop,7875sizeof (nfs_mnt_prop),7876NULL, NULL, 0, B_FALSE) == 0);7877if (strcmp(nfs_mnt_prop, "legacy") == 0)7878continue;7879/* Ignore canmount=noauto mounts */7880if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) ==7881ZFS_CANMOUNT_NOAUTO)7882continue;7883break;7884default:7885break;7886}78877888node = safe_malloc(sizeof (unshare_unmount_node_t));7889node->un_zhp = zhp;7890node->un_mountp = safe_strdup(entry.mnt_mountp);78917892if (avl_find(&tree, node, &idx) == NULL) {7893avl_insert(&tree, node, idx);7894} else {7895zfs_close(node->un_zhp);7896free(node->un_mountp);7897free(node);7898}7899}7900(void) fclose(mnttab);79017902/*7903* Walk the AVL tree in reverse, unmounting each filesystem and7904* removing it from the AVL tree in the process.7905*/7906while ((node = avl_last(&tree)) != NULL) {7907const char *mntarg = NULL;79087909avl_remove(&tree, node);7910switch (op) {7911case OP_SHARE:7912if (zfs_unshare(node->un_zhp,7913node->un_mountp, protocol) != 0)7914ret = 1;7915break;79167917case OP_MOUNT:7918if (zfs_unmount(node->un_zhp,7919mntarg, flags) != 0)7920ret = 1;7921break;7922}79237924zfs_close(node->un_zhp);7925free(node->un_mountp);7926free(node);7927}79287929if (op == OP_SHARE)7930zfs_commit_shares(protocol);79317932avl_destroy(&tree);79337934} else {7935if (argc != 1) {7936if (argc == 0)7937(void) fprintf(stderr,7938gettext("missing filesystem argument\n"));7939else7940(void) fprintf(stderr,7941gettext("too many arguments\n"));7942usage(B_FALSE);7943}79447945/*7946* We have an argument, but it may be a full path or a ZFS7947* filesystem. Pass full paths off to unmount_path() (shared by7948* manual_unmount), otherwise open the filesystem and pass to7949* zfs_unmount().7950*/7951if (argv[0][0] == '/')7952return (unshare_unmount_path(op, argv[0],7953flags, B_FALSE));79547955if ((zhp = zfs_open(g_zfs, argv[0],7956ZFS_TYPE_FILESYSTEM)) == NULL)7957return (1);79587959verify(zfs_prop_get(zhp, op == OP_SHARE ?7960ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT,7961nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL,7962NULL, 0, B_FALSE) == 0);79637964switch (op) {7965case OP_SHARE:7966verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,7967nfs_mnt_prop,7968sizeof (nfs_mnt_prop),7969NULL, NULL, 0, B_FALSE) == 0);7970verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,7971sharesmb, sizeof (sharesmb), NULL, NULL,79720, B_FALSE) == 0);79737974if (strcmp(nfs_mnt_prop, "off") == 0 &&7975strcmp(sharesmb, "off") == 0) {7976(void) fprintf(stderr, gettext("cannot "7977"unshare '%s': legacy share\n"),7978zfs_get_name(zhp));7979(void) fprintf(stderr, gettext("use "7980"exports(5) or smb.conf(5) to unshare "7981"this filesystem\n"));7982ret = 1;7983} else if (!zfs_is_shared(zhp, NULL, NULL)) {7984(void) fprintf(stderr, gettext("cannot "7985"unshare '%s': not currently "7986"shared\n"), zfs_get_name(zhp));7987ret = 1;7988} else if (zfs_unshareall(zhp, NULL) != 0) {7989ret = 1;7990}7991break;79927993case OP_MOUNT:7994if (strcmp(nfs_mnt_prop, "legacy") == 0) {7995(void) fprintf(stderr, gettext("cannot "7996"unmount '%s': legacy "7997"mountpoint\n"), zfs_get_name(zhp));7998(void) fprintf(stderr, gettext("use "7999"umount(8) to unmount this "8000"filesystem\n"));8001ret = 1;8002} else if (!zfs_is_mounted(zhp, NULL)) {8003(void) fprintf(stderr, gettext("cannot "8004"unmount '%s': not currently "8005"mounted\n"),8006zfs_get_name(zhp));8007ret = 1;8008} else if (zfs_unmountall(zhp, flags) != 0) {8009ret = 1;8010}8011break;8012}80138014zfs_close(zhp);8015}80168017return (ret);8018}80198020/*8021* zfs unmount [-fu] -a8022* zfs unmount [-fu] filesystem8023*8024* Unmount all filesystems, or a specific ZFS filesystem.8025*/8026static int8027zfs_do_unmount(int argc, char **argv)8028{8029return (unshare_unmount(OP_MOUNT, argc, argv));8030}80318032/*8033* zfs unshare -a8034* zfs unshare filesystem8035*8036* Unshare all filesystems, or a specific ZFS filesystem.8037*/8038static int8039zfs_do_unshare(int argc, char **argv)8040{8041return (unshare_unmount(OP_SHARE, argc, argv));8042}80438044static int8045find_command_idx(const char *command, int *idx)8046{8047int i;80488049for (i = 0; i < NCOMMAND; i++) {8050if (command_table[i].name == NULL)8051continue;80528053if (strcmp(command, command_table[i].name) == 0) {8054*idx = i;8055return (0);8056}8057}8058return (1);8059}80608061static int8062zfs_do_diff(int argc, char **argv)8063{8064zfs_handle_t *zhp;8065int flags = 0;8066char *tosnap = NULL;8067char *fromsnap = NULL;8068char *atp, *copy;8069int err = 0;8070int c;8071struct sigaction sa;80728073while ((c = getopt(argc, argv, "FHth")) != -1) {8074switch (c) {8075case 'F':8076flags |= ZFS_DIFF_CLASSIFY;8077break;8078case 'H':8079flags |= ZFS_DIFF_PARSEABLE;8080break;8081case 't':8082flags |= ZFS_DIFF_TIMESTAMP;8083break;8084case 'h':8085flags |= ZFS_DIFF_NO_MANGLE;8086break;8087default:8088(void) fprintf(stderr,8089gettext("invalid option '%c'\n"), optopt);8090usage(B_FALSE);8091}8092}80938094argc -= optind;8095argv += optind;80968097if (argc < 1) {8098(void) fprintf(stderr,8099gettext("must provide at least one snapshot name\n"));8100usage(B_FALSE);8101}81028103if (argc > 2) {8104(void) fprintf(stderr, gettext("too many arguments\n"));8105usage(B_FALSE);8106}81078108fromsnap = argv[0];8109tosnap = (argc == 2) ? argv[1] : NULL;81108111copy = NULL;8112if (*fromsnap != '@')8113copy = strdup(fromsnap);8114else if (tosnap)8115copy = strdup(tosnap);8116if (copy == NULL)8117usage(B_FALSE);81188119if ((atp = strchr(copy, '@')) != NULL)8120*atp = '\0';81218122if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL) {8123free(copy);8124return (1);8125}8126free(copy);81278128/*8129* Ignore SIGPIPE so that the library can give us8130* information on any failure8131*/8132if (sigemptyset(&sa.sa_mask) == -1) {8133err = errno;8134goto out;8135}8136sa.sa_flags = 0;8137sa.sa_handler = SIG_IGN;8138if (sigaction(SIGPIPE, &sa, NULL) == -1) {8139err = errno;8140goto out;8141}81428143err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags);8144out:8145zfs_close(zhp);81468147return (err != 0);8148}81498150/*8151* zfs bookmark <fs@source>|<fs#source> <fs#bookmark>8152*8153* Creates a bookmark with the given name from the source snapshot8154* or creates a copy of an existing source bookmark.8155*/8156static int8157zfs_do_bookmark(int argc, char **argv)8158{8159char *source, *bookname;8160char expbuf[ZFS_MAX_DATASET_NAME_LEN];8161int source_type;8162nvlist_t *nvl;8163int ret = 0;8164int c;81658166/* check options */8167while ((c = getopt(argc, argv, "")) != -1) {8168switch (c) {8169case '?':8170(void) fprintf(stderr,8171gettext("invalid option '%c'\n"), optopt);8172goto usage;8173}8174}81758176argc -= optind;8177argv += optind;81788179/* check number of arguments */8180if (argc < 1) {8181(void) fprintf(stderr, gettext("missing source argument\n"));8182goto usage;8183}8184if (argc < 2) {8185(void) fprintf(stderr, gettext("missing bookmark argument\n"));8186goto usage;8187}81888189source = argv[0];8190bookname = argv[1];81918192if (strchr(source, '@') == NULL && strchr(source, '#') == NULL) {8193(void) fprintf(stderr,8194gettext("invalid source name '%s': "8195"must contain a '@' or '#'\n"), source);8196goto usage;8197}8198if (strchr(bookname, '#') == NULL) {8199(void) fprintf(stderr,8200gettext("invalid bookmark name '%s': "8201"must contain a '#'\n"), bookname);8202goto usage;8203}82048205/*8206* expand source or bookname to full path:8207* one of them may be specified as short name8208*/8209{8210char **expand;8211char *source_short, *bookname_short;8212source_short = strpbrk(source, "@#");8213bookname_short = strpbrk(bookname, "#");8214if (source_short == source &&8215bookname_short == bookname) {8216(void) fprintf(stderr, gettext(8217"either source or bookmark must be specified as "8218"full dataset paths"));8219goto usage;8220} else if (source_short != source &&8221bookname_short != bookname) {8222expand = NULL;8223} else if (source_short != source) {8224strlcpy(expbuf, source, sizeof (expbuf));8225expand = &bookname;8226} else if (bookname_short != bookname) {8227strlcpy(expbuf, bookname, sizeof (expbuf));8228expand = &source;8229} else {8230abort();8231}8232if (expand != NULL) {8233*strpbrk(expbuf, "@#") = '\0'; /* dataset name in buf */8234(void) strlcat(expbuf, *expand, sizeof (expbuf));8235*expand = expbuf;8236}8237}82388239/* determine source type */8240switch (*strpbrk(source, "@#")) {8241case '@': source_type = ZFS_TYPE_SNAPSHOT; break;8242case '#': source_type = ZFS_TYPE_BOOKMARK; break;8243default: abort();8244}82458246/* test the source exists */8247zfs_handle_t *zhp;8248zhp = zfs_open(g_zfs, source, source_type);8249if (zhp == NULL)8250goto usage;8251zfs_close(zhp);82528253nvl = fnvlist_alloc();8254fnvlist_add_string(nvl, bookname, source);8255ret = lzc_bookmark(nvl, NULL);8256fnvlist_free(nvl);82578258if (ret != 0) {8259const char *err_msg = NULL;8260char errbuf[1024];82618262(void) snprintf(errbuf, sizeof (errbuf),8263dgettext(TEXT_DOMAIN,8264"cannot create bookmark '%s'"), bookname);82658266switch (ret) {8267case EXDEV:8268err_msg = "bookmark is in a different pool";8269break;8270case ZFS_ERR_BOOKMARK_SOURCE_NOT_ANCESTOR:8271err_msg = "source is not an ancestor of the "8272"new bookmark's dataset";8273break;8274case EEXIST:8275err_msg = "bookmark exists";8276break;8277case EINVAL:8278err_msg = "invalid argument";8279break;8280case ENOTSUP:8281err_msg = "bookmark feature not enabled";8282break;8283case ENOSPC:8284err_msg = "out of space";8285break;8286case ENOENT:8287err_msg = "dataset does not exist";8288break;8289default:8290(void) zfs_standard_error(g_zfs, ret, errbuf);8291break;8292}8293if (err_msg != NULL) {8294(void) fprintf(stderr, "%s: %s\n", errbuf,8295dgettext(TEXT_DOMAIN, err_msg));8296}8297}82988299return (ret != 0);83008301usage:8302usage(B_FALSE);8303return (-1);8304}83058306static int8307zfs_do_channel_program(int argc, char **argv)8308{8309int ret, fd, c;8310size_t progsize, progread;8311nvlist_t *outnvl = NULL;8312uint64_t instrlimit = ZCP_DEFAULT_INSTRLIMIT;8313uint64_t memlimit = ZCP_DEFAULT_MEMLIMIT;8314boolean_t sync_flag = B_TRUE, json_output = B_FALSE;8315zpool_handle_t *zhp;83168317struct option long_options[] = {8318{"json", no_argument, NULL, 'j'},8319{0, 0, 0, 0}8320};83218322/* check options */8323while ((c = getopt_long(argc, argv, "nt:m:j", long_options,8324NULL)) != -1) {8325switch (c) {8326case 't':8327case 'm': {8328uint64_t arg;8329char *endp;83308331errno = 0;8332arg = strtoull(optarg, &endp, 0);8333if (errno != 0 || *endp != '\0') {8334(void) fprintf(stderr, gettext(8335"invalid argument "8336"'%s': expected integer\n"), optarg);8337goto usage;8338}83398340if (c == 't') {8341instrlimit = arg;8342} else {8343ASSERT3U(c, ==, 'm');8344memlimit = arg;8345}8346break;8347}8348case 'n': {8349sync_flag = B_FALSE;8350break;8351}8352case 'j': {8353json_output = B_TRUE;8354break;8355}8356case '?':8357(void) fprintf(stderr, gettext("invalid option '%c'\n"),8358optopt);8359goto usage;8360}8361}83628363argc -= optind;8364argv += optind;83658366if (argc < 2) {8367(void) fprintf(stderr,8368gettext("invalid number of arguments\n"));8369goto usage;8370}83718372const char *poolname = argv[0];8373const char *filename = argv[1];8374if (strcmp(filename, "-") == 0) {8375fd = 0;8376filename = "standard input";8377} else if ((fd = open(filename, O_RDONLY)) < 0) {8378(void) fprintf(stderr, gettext("cannot open '%s': %s\n"),8379filename, strerror(errno));8380return (1);8381}83828383if ((zhp = zpool_open(g_zfs, poolname)) == NULL) {8384(void) fprintf(stderr, gettext("cannot open pool '%s'\n"),8385poolname);8386if (fd != 0)8387(void) close(fd);8388return (1);8389}8390zpool_close(zhp);83918392/*8393* Read in the channel program, expanding the program buffer as8394* necessary.8395*/8396progread = 0;8397progsize = 1024;8398char *progbuf = safe_malloc(progsize);8399do {8400ret = read(fd, progbuf + progread, progsize - progread);8401progread += ret;8402if (progread == progsize && ret > 0) {8403progsize *= 2;8404progbuf = safe_realloc(progbuf, progsize);8405}8406} while (ret > 0);84078408if (fd != 0)8409(void) close(fd);8410if (ret < 0) {8411free(progbuf);8412(void) fprintf(stderr,8413gettext("cannot read '%s': %s\n"),8414filename, strerror(errno));8415return (1);8416}8417progbuf[progread] = '\0';84188419/*8420* Any remaining arguments are passed as arguments to the lua script as8421* a string array:8422* {8423* "argv" -> [ "arg 1", ... "arg n" ],8424* }8425*/8426nvlist_t *argnvl = fnvlist_alloc();8427fnvlist_add_string_array(argnvl, ZCP_ARG_CLIARGV,8428(const char **)argv + 2, argc - 2);84298430if (sync_flag) {8431ret = lzc_channel_program(poolname, progbuf,8432instrlimit, memlimit, argnvl, &outnvl);8433} else {8434ret = lzc_channel_program_nosync(poolname, progbuf,8435instrlimit, memlimit, argnvl, &outnvl);8436}84378438if (ret != 0) {8439/*8440* On error, report the error message handed back by lua if one8441* exists. Otherwise, generate an appropriate error message,8442* falling back on strerror() for an unexpected return code.8443*/8444const char *errstring = NULL;8445const char *msg = gettext("Channel program execution failed");8446uint64_t instructions = 0;8447if (outnvl != NULL && nvlist_exists(outnvl, ZCP_RET_ERROR)) {8448const char *es = NULL;8449(void) nvlist_lookup_string(outnvl,8450ZCP_RET_ERROR, &es);8451if (es == NULL)8452errstring = strerror(ret);8453else8454errstring = es;8455if (ret == ETIME) {8456(void) nvlist_lookup_uint64(outnvl,8457ZCP_ARG_INSTRLIMIT, &instructions);8458}8459} else {8460switch (ret) {8461case EINVAL:8462errstring =8463"Invalid instruction or memory limit.";8464break;8465case ENOMEM:8466errstring = "Return value too large.";8467break;8468case ENOSPC:8469errstring = "Memory limit exhausted.";8470break;8471case ETIME:8472errstring = "Timed out.";8473break;8474case EPERM:8475errstring = "Permission denied. Channel "8476"programs must be run as root.";8477break;8478default:8479(void) zfs_standard_error(g_zfs, ret, msg);8480}8481}8482if (errstring != NULL)8483(void) fprintf(stderr, "%s:\n%s\n", msg, errstring);84848485if (ret == ETIME && instructions != 0)8486(void) fprintf(stderr,8487gettext("%llu Lua instructions\n"),8488(u_longlong_t)instructions);8489} else {8490if (json_output) {8491(void) nvlist_print_json(stdout, outnvl);8492} else if (nvlist_empty(outnvl)) {8493(void) fprintf(stdout, gettext("Channel program fully "8494"executed and did not produce output.\n"));8495} else {8496(void) fprintf(stdout, gettext("Channel program fully "8497"executed and produced output:\n"));8498dump_nvlist(outnvl, 4);8499}8500}85018502free(progbuf);8503fnvlist_free(outnvl);8504fnvlist_free(argnvl);8505return (ret != 0);85068507usage:8508usage(B_FALSE);8509return (-1);8510}851185128513typedef struct loadkey_cbdata {8514boolean_t cb_loadkey;8515boolean_t cb_recursive;8516boolean_t cb_noop;8517char *cb_keylocation;8518uint64_t cb_numfailed;8519uint64_t cb_numattempted;8520} loadkey_cbdata_t;85218522static int8523load_key_callback(zfs_handle_t *zhp, void *data)8524{8525int ret;8526boolean_t is_encroot;8527loadkey_cbdata_t *cb = data;8528uint64_t keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);85298530/*8531* If we are working recursively, we want to skip loading / unloading8532* keys for non-encryption roots and datasets whose keys are already8533* in the desired end-state.8534*/8535if (cb->cb_recursive) {8536ret = zfs_crypto_get_encryption_root(zhp, &is_encroot, NULL);8537if (ret != 0)8538return (ret);8539if (!is_encroot)8540return (0);85418542if ((cb->cb_loadkey && keystatus == ZFS_KEYSTATUS_AVAILABLE) ||8543(!cb->cb_loadkey && keystatus == ZFS_KEYSTATUS_UNAVAILABLE))8544return (0);8545}85468547cb->cb_numattempted++;85488549if (cb->cb_loadkey)8550ret = zfs_crypto_load_key(zhp, cb->cb_noop, cb->cb_keylocation);8551else8552ret = zfs_crypto_unload_key(zhp);85538554if (ret != 0) {8555cb->cb_numfailed++;8556return (ret);8557}85588559return (0);8560}85618562static int8563load_unload_keys(int argc, char **argv, boolean_t loadkey)8564{8565int c, ret = 0, flags = 0;8566boolean_t do_all = B_FALSE;8567loadkey_cbdata_t cb = { 0 };85688569cb.cb_loadkey = loadkey;85708571while ((c = getopt(argc, argv, "anrL:")) != -1) {8572/* noop and alternate keylocations only apply to zfs load-key */8573if (loadkey) {8574switch (c) {8575case 'n':8576cb.cb_noop = B_TRUE;8577continue;8578case 'L':8579cb.cb_keylocation = optarg;8580continue;8581default:8582break;8583}8584}85858586switch (c) {8587case 'a':8588do_all = B_TRUE;8589cb.cb_recursive = B_TRUE;8590break;8591case 'r':8592flags |= ZFS_ITER_RECURSE;8593cb.cb_recursive = B_TRUE;8594break;8595default:8596(void) fprintf(stderr,8597gettext("invalid option '%c'\n"), optopt);8598usage(B_FALSE);8599}8600}86018602argc -= optind;8603argv += optind;86048605if (!do_all && argc == 0) {8606(void) fprintf(stderr,8607gettext("Missing dataset argument or -a option\n"));8608usage(B_FALSE);8609}86108611if (do_all && argc != 0) {8612(void) fprintf(stderr,8613gettext("Cannot specify dataset with -a option\n"));8614usage(B_FALSE);8615}86168617if (cb.cb_recursive && cb.cb_keylocation != NULL &&8618strcmp(cb.cb_keylocation, "prompt") != 0) {8619(void) fprintf(stderr, gettext("alternate keylocation may only "8620"be 'prompt' with -r or -a\n"));8621usage(B_FALSE);8622}86238624ret = zfs_for_each(argc, argv, flags,8625ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, NULL, NULL, 0,8626load_key_callback, &cb);86278628if (cb.cb_noop || (cb.cb_recursive && cb.cb_numattempted != 0)) {8629(void) printf(gettext("%llu / %llu key(s) successfully %s\n"),8630(u_longlong_t)(cb.cb_numattempted - cb.cb_numfailed),8631(u_longlong_t)cb.cb_numattempted,8632loadkey ? (cb.cb_noop ? "verified" : "loaded") :8633"unloaded");8634}86358636if (cb.cb_numfailed != 0)8637ret = -1;86388639return (ret);8640}86418642static int8643zfs_do_load_key(int argc, char **argv)8644{8645return (load_unload_keys(argc, argv, B_TRUE));8646}864786488649static int8650zfs_do_unload_key(int argc, char **argv)8651{8652return (load_unload_keys(argc, argv, B_FALSE));8653}86548655static int8656zfs_do_change_key(int argc, char **argv)8657{8658int c, ret;8659uint64_t keystatus;8660boolean_t loadkey = B_FALSE, inheritkey = B_FALSE;8661zfs_handle_t *zhp = NULL;8662nvlist_t *props = fnvlist_alloc();86638664while ((c = getopt(argc, argv, "lio:")) != -1) {8665switch (c) {8666case 'l':8667loadkey = B_TRUE;8668break;8669case 'i':8670inheritkey = B_TRUE;8671break;8672case 'o':8673if (!parseprop(props, optarg)) {8674nvlist_free(props);8675return (1);8676}8677break;8678default:8679(void) fprintf(stderr,8680gettext("invalid option '%c'\n"), optopt);8681usage(B_FALSE);8682}8683}86848685if (inheritkey && !nvlist_empty(props)) {8686(void) fprintf(stderr,8687gettext("Properties not allowed for inheriting\n"));8688usage(B_FALSE);8689}86908691argc -= optind;8692argv += optind;86938694if (argc < 1) {8695(void) fprintf(stderr, gettext("Missing dataset argument\n"));8696usage(B_FALSE);8697}86988699if (argc > 1) {8700(void) fprintf(stderr, gettext("Too many arguments\n"));8701usage(B_FALSE);8702}87038704zhp = zfs_open(g_zfs, argv[argc - 1],8705ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);8706if (zhp == NULL)8707usage(B_FALSE);87088709if (loadkey) {8710keystatus = zfs_prop_get_int(zhp, ZFS_PROP_KEYSTATUS);8711if (keystatus != ZFS_KEYSTATUS_AVAILABLE) {8712ret = zfs_crypto_load_key(zhp, B_FALSE, NULL);8713if (ret != 0) {8714nvlist_free(props);8715zfs_close(zhp);8716return (-1);8717}8718}87198720/* refresh the properties so the new keystatus is visible */8721zfs_refresh_properties(zhp);8722}87238724ret = zfs_crypto_rewrap(zhp, props, inheritkey);8725if (ret != 0) {8726nvlist_free(props);8727zfs_close(zhp);8728return (-1);8729}87308731nvlist_free(props);8732zfs_close(zhp);8733return (0);8734}87358736/*8737* 1) zfs project [-d|-r] <file|directory ...>8738* List project ID and inherit flag of file(s) or directories.8739* -d: List the directory itself, not its children.8740* -r: List subdirectories recursively.8741*8742* 2) zfs project -C [-k] [-r] <file|directory ...>8743* Clear project inherit flag and/or ID on the file(s) or directories.8744* -k: Keep the project ID unchanged. If not specified, the project ID8745* will be reset as zero.8746* -r: Clear on subdirectories recursively.8747*8748* 3) zfs project -c [-0] [-d|-r] [-p id] <file|directory ...>8749* Check project ID and inherit flag on the file(s) or directories,8750* report the outliers.8751* -0: Print file name followed by a NUL instead of newline.8752* -d: Check the directory itself, not its children.8753* -p: Specify the referenced ID for comparing with the target file(s)8754* or directories' project IDs. If not specified, the target (top)8755* directory's project ID will be used as the referenced one.8756* -r: Check subdirectories recursively.8757*8758* 4) zfs project [-p id] [-r] [-s] <file|directory ...>8759* Set project ID and/or inherit flag on the file(s) or directories.8760* -p: Set the project ID as the given id.8761* -r: Set on subdirectories recursively. If not specify "-p" option,8762* it will use top-level directory's project ID as the given id,8763* then set both project ID and inherit flag on all descendants8764* of the top-level directory.8765* -s: Set project inherit flag.8766*/8767static int8768zfs_do_project(int argc, char **argv)8769{8770zfs_project_control_t zpc = {8771.zpc_expected_projid = ZFS_INVALID_PROJID,8772.zpc_op = ZFS_PROJECT_OP_DEFAULT,8773.zpc_dironly = B_FALSE,8774.zpc_keep_projid = B_FALSE,8775.zpc_newline = B_TRUE,8776.zpc_recursive = B_FALSE,8777.zpc_set_flag = B_FALSE,8778};8779int ret = 0, c;87808781if (argc < 2)8782usage(B_FALSE);87838784while ((c = getopt(argc, argv, "0Ccdkp:rs")) != -1) {8785switch (c) {8786case '0':8787zpc.zpc_newline = B_FALSE;8788break;8789case 'C':8790if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {8791(void) fprintf(stderr, gettext("cannot "8792"specify '-C' '-c' '-s' together\n"));8793usage(B_FALSE);8794}87958796zpc.zpc_op = ZFS_PROJECT_OP_CLEAR;8797break;8798case 'c':8799if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {8800(void) fprintf(stderr, gettext("cannot "8801"specify '-C' '-c' '-s' together\n"));8802usage(B_FALSE);8803}88048805zpc.zpc_op = ZFS_PROJECT_OP_CHECK;8806break;8807case 'd':8808zpc.zpc_dironly = B_TRUE;8809/* overwrite "-r" option */8810zpc.zpc_recursive = B_FALSE;8811break;8812case 'k':8813zpc.zpc_keep_projid = B_TRUE;8814break;8815case 'p': {8816char *endptr;88178818errno = 0;8819zpc.zpc_expected_projid = strtoull(optarg, &endptr, 0);8820if (errno != 0 || *endptr != '\0') {8821(void) fprintf(stderr,8822gettext("project ID must be less than "8823"%u\n"), UINT32_MAX);8824usage(B_FALSE);8825}8826if (zpc.zpc_expected_projid >= UINT32_MAX) {8827(void) fprintf(stderr,8828gettext("invalid project ID\n"));8829usage(B_FALSE);8830}8831break;8832}8833case 'r':8834zpc.zpc_recursive = B_TRUE;8835/* overwrite "-d" option */8836zpc.zpc_dironly = B_FALSE;8837break;8838case 's':8839if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) {8840(void) fprintf(stderr, gettext("cannot "8841"specify '-C' '-c' '-s' together\n"));8842usage(B_FALSE);8843}88448845zpc.zpc_set_flag = B_TRUE;8846zpc.zpc_op = ZFS_PROJECT_OP_SET;8847break;8848default:8849(void) fprintf(stderr, gettext("invalid option '%c'\n"),8850optopt);8851usage(B_FALSE);8852}8853}88548855if (zpc.zpc_op == ZFS_PROJECT_OP_DEFAULT) {8856if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID)8857zpc.zpc_op = ZFS_PROJECT_OP_SET;8858else8859zpc.zpc_op = ZFS_PROJECT_OP_LIST;8860}88618862switch (zpc.zpc_op) {8863case ZFS_PROJECT_OP_LIST:8864if (zpc.zpc_keep_projid) {8865(void) fprintf(stderr,8866gettext("'-k' is only valid together with '-C'\n"));8867usage(B_FALSE);8868}8869if (!zpc.zpc_newline) {8870(void) fprintf(stderr,8871gettext("'-0' is only valid together with '-c'\n"));8872usage(B_FALSE);8873}8874break;8875case ZFS_PROJECT_OP_CHECK:8876if (zpc.zpc_keep_projid) {8877(void) fprintf(stderr,8878gettext("'-k' is only valid together with '-C'\n"));8879usage(B_FALSE);8880}8881break;8882case ZFS_PROJECT_OP_CLEAR:8883if (zpc.zpc_dironly) {8884(void) fprintf(stderr,8885gettext("'-d' is useless together with '-C'\n"));8886usage(B_FALSE);8887}8888if (!zpc.zpc_newline) {8889(void) fprintf(stderr,8890gettext("'-0' is only valid together with '-c'\n"));8891usage(B_FALSE);8892}8893if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID) {8894(void) fprintf(stderr,8895gettext("'-p' is useless together with '-C'\n"));8896usage(B_FALSE);8897}8898break;8899case ZFS_PROJECT_OP_SET:8900if (zpc.zpc_dironly) {8901(void) fprintf(stderr,8902gettext("'-d' is useless for set project ID and/or "8903"inherit flag\n"));8904usage(B_FALSE);8905}8906if (zpc.zpc_keep_projid) {8907(void) fprintf(stderr,8908gettext("'-k' is only valid together with '-C'\n"));8909usage(B_FALSE);8910}8911if (!zpc.zpc_newline) {8912(void) fprintf(stderr,8913gettext("'-0' is only valid together with '-c'\n"));8914usage(B_FALSE);8915}8916break;8917default:8918ASSERT(0);8919break;8920}89218922argv += optind;8923argc -= optind;8924if (argc == 0) {8925(void) fprintf(stderr,8926gettext("missing file or directory target(s)\n"));8927usage(B_FALSE);8928}89298930for (int i = 0; i < argc; i++) {8931int err;89328933err = zfs_project_handle(argv[i], &zpc);8934if (err && !ret)8935ret = err;8936}89378938return (ret);8939}89408941static int8942zfs_rewrite_file(const char *path, boolean_t verbose, zfs_rewrite_args_t *args)8943{8944int fd, ret = 0;89458946fd = open(path, O_WRONLY);8947if (fd < 0) {8948ret = errno;8949(void) fprintf(stderr, gettext("failed to open %s: %s\n"),8950path, strerror(errno));8951return (ret);8952}89538954if (ioctl(fd, ZFS_IOC_REWRITE, args) < 0) {8955ret = errno;8956(void) fprintf(stderr, gettext("failed to rewrite %s: %s\n"),8957path, strerror(errno));8958} else if (verbose) {8959printf("%s\n", path);8960}89618962close(fd);8963return (ret);8964}89658966static int8967zfs_rewrite_dir(const char *path, boolean_t verbose, boolean_t xdev, dev_t dev,8968zfs_rewrite_args_t *args, nvlist_t *dirs)8969{8970struct dirent *ent;8971DIR *dir;8972int ret = 0, err;89738974dir = opendir(path);8975if (dir == NULL) {8976if (errno == ENOENT)8977return (0);8978ret = errno;8979(void) fprintf(stderr, gettext("failed to opendir %s: %s\n"),8980path, strerror(errno));8981return (ret);8982}89838984size_t plen = strlen(path) + 1;8985while ((ent = readdir(dir)) != NULL) {8986char *fullname;8987struct stat st;89888989if (ent->d_type != DT_REG && ent->d_type != DT_DIR)8990continue;89918992if (strcmp(ent->d_name, ".") == 0 ||8993strcmp(ent->d_name, "..") == 0)8994continue;89958996if (plen + strlen(ent->d_name) >= PATH_MAX) {8997(void) fprintf(stderr, gettext("path too long %s/%s\n"),8998path, ent->d_name);8999ret = ENAMETOOLONG;9000continue;9001}90029003if (asprintf(&fullname, "%s/%s", path, ent->d_name) == -1) {9004(void) fprintf(stderr,9005gettext("failed to allocate memory\n"));9006ret = ENOMEM;9007continue;9008}90099010if (xdev) {9011if (lstat(fullname, &st) < 0) {9012ret = errno;9013(void) fprintf(stderr,9014gettext("failed to stat %s: %s\n"),9015fullname, strerror(errno));9016free(fullname);9017continue;9018}9019if (st.st_dev != dev) {9020free(fullname);9021continue;9022}9023}90249025if (ent->d_type == DT_REG) {9026err = zfs_rewrite_file(fullname, verbose, args);9027if (err)9028ret = err;9029} else { /* DT_DIR */9030fnvlist_add_uint64(dirs, fullname, dev);9031}90329033free(fullname);9034}90359036closedir(dir);9037return (ret);9038}90399040static int9041zfs_rewrite_path(const char *path, boolean_t verbose, boolean_t recurse,9042boolean_t xdev, zfs_rewrite_args_t *args, nvlist_t *dirs)9043{9044struct stat st;9045int ret = 0;90469047if (lstat(path, &st) < 0) {9048ret = errno;9049(void) fprintf(stderr, gettext("failed to stat %s: %s\n"),9050path, strerror(errno));9051return (ret);9052}90539054if (S_ISREG(st.st_mode)) {9055ret = zfs_rewrite_file(path, verbose, args);9056} else if (S_ISDIR(st.st_mode) && recurse) {9057ret = zfs_rewrite_dir(path, verbose, xdev, st.st_dev, args,9058dirs);9059}9060return (ret);9061}90629063static int9064zfs_do_rewrite(int argc, char **argv)9065{9066int ret = 0, err, c;9067boolean_t recurse = B_FALSE, verbose = B_FALSE, xdev = B_FALSE;90689069if (argc < 2)9070usage(B_FALSE);90719072zfs_rewrite_args_t args;9073memset(&args, 0, sizeof (args));90749075while ((c = getopt(argc, argv, "Pl:o:rvx")) != -1) {9076switch (c) {9077case 'P':9078args.flags |= ZFS_REWRITE_PHYSICAL;9079break;9080case 'l':9081args.len = strtoll(optarg, NULL, 0);9082break;9083case 'o':9084args.off = strtoll(optarg, NULL, 0);9085break;9086case 'r':9087recurse = B_TRUE;9088break;9089case 'v':9090verbose = B_TRUE;9091break;9092case 'x':9093xdev = B_TRUE;9094break;9095default:9096(void) fprintf(stderr, gettext("invalid option '%c'\n"),9097optopt);9098usage(B_FALSE);9099}9100}91019102argv += optind;9103argc -= optind;9104if (argc == 0) {9105(void) fprintf(stderr,9106gettext("missing file or directory target(s)\n"));9107usage(B_FALSE);9108}91099110nvlist_t *dirs = fnvlist_alloc();9111for (int i = 0; i < argc; i++) {9112err = zfs_rewrite_path(argv[i], verbose, recurse, xdev, &args,9113dirs);9114if (err)9115ret = err;9116}9117nvpair_t *dir;9118while ((dir = nvlist_next_nvpair(dirs, NULL)) != NULL) {9119err = zfs_rewrite_dir(nvpair_name(dir), verbose, xdev,9120fnvpair_value_uint64(dir), &args, dirs);9121if (err)9122ret = err;9123fnvlist_remove_nvpair(dirs, dir);9124}9125fnvlist_free(dirs);91269127return (ret);9128}91299130static int9131zfs_do_wait(int argc, char **argv)9132{9133boolean_t enabled[ZFS_WAIT_NUM_ACTIVITIES];9134int error = 0, i;9135int c;91369137/* By default, wait for all types of activity. */9138for (i = 0; i < ZFS_WAIT_NUM_ACTIVITIES; i++)9139enabled[i] = B_TRUE;91409141while ((c = getopt(argc, argv, "t:")) != -1) {9142switch (c) {9143case 't':9144/* Reset activities array */9145memset(&enabled, 0, sizeof (enabled));91469147for (char *tok; (tok = strsep(&optarg, ",")); ) {9148static const char *const col_subopts[9149ZFS_WAIT_NUM_ACTIVITIES] = { "deleteq" };91509151for (i = 0; i < ARRAY_SIZE(col_subopts); ++i)9152if (strcmp(tok, col_subopts[i]) == 0) {9153enabled[i] = B_TRUE;9154goto found;9155}91569157(void) fprintf(stderr,9158gettext("invalid activity '%s'\n"), tok);9159usage(B_FALSE);9160found:;9161}9162break;9163case '?':9164(void) fprintf(stderr, gettext("invalid option '%c'\n"),9165optopt);9166usage(B_FALSE);9167}9168}91699170argv += optind;9171argc -= optind;9172if (argc < 1) {9173(void) fprintf(stderr, gettext("missing 'filesystem' "9174"argument\n"));9175usage(B_FALSE);9176}9177if (argc > 1) {9178(void) fprintf(stderr, gettext("too many arguments\n"));9179usage(B_FALSE);9180}91819182zfs_handle_t *zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM);9183if (zhp == NULL)9184return (1);91859186for (;;) {9187boolean_t missing = B_FALSE;9188boolean_t any_waited = B_FALSE;91899190for (int i = 0; i < ZFS_WAIT_NUM_ACTIVITIES; i++) {9191boolean_t waited;91929193if (!enabled[i])9194continue;91959196error = zfs_wait_status(zhp, i, &missing, &waited);9197if (error != 0 || missing)9198break;91999200any_waited = (any_waited || waited);9201}92029203if (error != 0 || missing || !any_waited)9204break;9205}92069207zfs_close(zhp);92089209return (error);9210}92119212/*9213* Display version message9214*/9215static int9216zfs_do_version(int argc, char **argv)9217{9218int c;9219nvlist_t *jsobj = NULL, *zfs_ver = NULL;9220boolean_t json = B_FALSE;92219222struct option long_options[] = {9223{"json", no_argument, NULL, 'j'},9224{0, 0, 0, 0}9225};92269227while ((c = getopt_long(argc, argv, "j", long_options, NULL)) != -1) {9228switch (c) {9229case 'j':9230json = B_TRUE;9231jsobj = zfs_json_schema(0, 1);9232break;9233case '?':9234(void) fprintf(stderr, gettext("invalid option '%c'\n"),9235optopt);9236usage(B_FALSE);9237}9238}92399240argc -= optind;9241if (argc != 0) {9242(void) fprintf(stderr, "too many arguments\n");9243usage(B_FALSE);9244}92459246if (json) {9247zfs_ver = zfs_version_nvlist();9248if (zfs_ver) {9249fnvlist_add_nvlist(jsobj, "zfs_version", zfs_ver);9250zcmd_print_json(jsobj);9251fnvlist_free(zfs_ver);9252return (0);9253} else9254return (-1);9255} else9256return (zfs_version_print() != 0);9257}92589259/* Display documentation */9260static int9261zfs_do_help(int argc, char **argv)9262{9263char page[MAXNAMELEN];9264if (argc < 3 || strcmp(argv[2], "zfs") == 0)9265strcpy(page, "zfs");9266else if (strcmp(argv[2], "concepts") == 0 ||9267strcmp(argv[2], "props") == 0)9268snprintf(page, sizeof (page), "zfs%s", argv[2]);9269else9270snprintf(page, sizeof (page), "zfs-%s", argv[2]);92719272execlp("man", "man", page, NULL);92739274fprintf(stderr, "couldn't run man program: %s", strerror(errno));9275return (-1);9276}92779278int9279main(int argc, char **argv)9280{9281int ret = 0;9282int i = 0;9283const char *cmdname;9284char **newargv;92859286(void) setlocale(LC_ALL, "");9287(void) setlocale(LC_NUMERIC, "C");9288(void) textdomain(TEXT_DOMAIN);92899290opterr = 0;92919292/*9293* Make sure the user has specified some command.9294*/9295if (argc < 2) {9296(void) fprintf(stderr, gettext("missing command\n"));9297usage(B_FALSE);9298}92999300cmdname = argv[1];93019302/*9303* The 'umount' command is an alias for 'unmount'9304*/9305if (strcmp(cmdname, "umount") == 0)9306cmdname = "unmount";93079308/*9309* The 'recv' command is an alias for 'receive'9310*/9311if (strcmp(cmdname, "recv") == 0)9312cmdname = "receive";93139314/*9315* The 'snap' command is an alias for 'snapshot'9316*/9317if (strcmp(cmdname, "snap") == 0)9318cmdname = "snapshot";93199320/*9321* Special case '-?'9322*/9323if ((strcmp(cmdname, "-?") == 0) ||9324(strcmp(cmdname, "--help") == 0))9325usage(B_TRUE);93269327/*9328* Special case '-V|--version'9329*/9330if ((strcmp(cmdname, "-V") == 0) || (strcmp(cmdname, "--version") == 0))9331return (zfs_version_print() != 0);93329333/*9334* Special case 'help'9335*/9336if (strcmp(cmdname, "help") == 0)9337return (zfs_do_help(argc, argv));93389339if ((g_zfs = libzfs_init()) == NULL) {9340(void) fprintf(stderr, "%s\n", libzfs_error_init(errno));9341return (1);9342}93439344zfs_save_arguments(argc, argv, history_str, sizeof (history_str));93459346libzfs_print_on_error(g_zfs, B_TRUE);93479348zfs_setproctitle_init(argc, argv, environ);93499350/*9351* Many commands modify input strings for string parsing reasons.9352* We create a copy to protect the original argv.9353*/9354newargv = safe_malloc((argc + 1) * sizeof (newargv[0]));9355for (i = 0; i < argc; i++)9356newargv[i] = strdup(argv[i]);9357newargv[argc] = NULL;93589359/*9360* Run the appropriate command.9361*/9362libzfs_mnttab_cache(g_zfs, B_TRUE);9363if (find_command_idx(cmdname, &i) == 0) {9364current_command = &command_table[i];9365ret = command_table[i].func(argc - 1, newargv + 1);9366} else if (strchr(cmdname, '=') != NULL) {9367verify(find_command_idx("set", &i) == 0);9368current_command = &command_table[i];9369ret = command_table[i].func(argc, newargv);9370} else {9371(void) fprintf(stderr, gettext("unrecognized "9372"command '%s'\n"), cmdname);9373usage(B_FALSE);9374ret = 1;9375}93769377for (i = 0; i < argc; i++)9378free(newargv[i]);9379free(newargv);93809381if (ret == 0 && log_history)9382(void) zpool_log_history(g_zfs, history_str);93839384libzfs_fini(g_zfs);93859386/*9387* The 'ZFS_ABORT' environment variable causes us to dump core on exit9388* for the purposes of running ::findleaks.9389*/9390if (getenv("ZFS_ABORT") != NULL) {9391(void) printf("dumping core by request\n");9392abort();9393}93949395return (ret);9396}93979398/*9399* zfs zone nsfile filesystem9400*9401* Add or delete the given dataset to/from the namespace.9402*/9403#ifdef __linux__9404static int9405zfs_do_zone_impl(int argc, char **argv, boolean_t attach)9406{9407zfs_handle_t *zhp;9408int ret;94099410if (argc < 3) {9411(void) fprintf(stderr, gettext("missing argument(s)\n"));9412usage(B_FALSE);9413}9414if (argc > 3) {9415(void) fprintf(stderr, gettext("too many arguments\n"));9416usage(B_FALSE);9417}94189419zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM);9420if (zhp == NULL)9421return (1);94229423ret = (zfs_userns(zhp, argv[1], attach) != 0);94249425zfs_close(zhp);9426return (ret);9427}94289429static int9430zfs_do_zone(int argc, char **argv)9431{9432return (zfs_do_zone_impl(argc, argv, B_TRUE));9433}94349435static int9436zfs_do_unzone(int argc, char **argv)9437{9438return (zfs_do_zone_impl(argc, argv, B_FALSE));9439}9440#endif94419442#ifdef __FreeBSD__9443#include <sys/jail.h>9444#include <jail.h>9445/*9446* Attach/detach the given dataset to/from the given jail9447*/9448static int9449zfs_do_jail_impl(int argc, char **argv, boolean_t attach)9450{9451zfs_handle_t *zhp;9452int jailid, ret;94539454/* check number of arguments */9455if (argc < 3) {9456(void) fprintf(stderr, gettext("missing argument(s)\n"));9457usage(B_FALSE);9458}9459if (argc > 3) {9460(void) fprintf(stderr, gettext("too many arguments\n"));9461usage(B_FALSE);9462}94639464jailid = jail_getid(argv[1]);9465if (jailid < 0) {9466(void) fprintf(stderr, gettext("invalid jail id or name\n"));9467usage(B_FALSE);9468}94699470zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM);9471if (zhp == NULL)9472return (1);94739474ret = (zfs_jail(zhp, jailid, attach) != 0);94759476zfs_close(zhp);9477return (ret);9478}94799480/*9481* zfs jail jailid filesystem9482*9483* Attach the given dataset to the given jail9484*/9485static int9486zfs_do_jail(int argc, char **argv)9487{9488return (zfs_do_jail_impl(argc, argv, B_TRUE));9489}94909491/*9492* zfs unjail jailid filesystem9493*9494* Detach the given dataset from the given jail9495*/9496static int9497zfs_do_unjail(int argc, char **argv)9498{9499return (zfs_do_jail_impl(argc, argv, B_FALSE));9500}9501#endif950295039504