zfs_main.c revision 4445fffbbb1ea25fd0e9ea68b9380dd7a6709025
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2012 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2012 by Delphix. All rights reserved.
* Copyright 2012 Milan Jurik. All rights reserved.
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <libgen.h>
#include <libintl.h>
#include <libuutil.h>
#include <libnvpair.h>
#include <locale.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <fcntl.h>
#include <zone.h>
#include <grp.h>
#include <pwd.h>
#include <signal.h>
#include <time.h>
#include <libzfs.h>
#include <libzfs_core.h>
#include <zfs_prop.h>
#include <zfs_deleg.h>
#include <libuutil.h>
#include <aclutils.h>
#include <directory.h>
#include "zfs_iter.h"
#include "zfs_util.h"
#include "zfs_comutil.h"
static FILE *mnttab_file;
static char history_str[HIS_MAX_RECORD_LEN];
/*
* Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
*/
#ifdef DEBUG
const char *
_umem_debug_init(void)
{
return ("default,verbose"); /* $UMEM_DEBUG setting */
}
const char *
_umem_logging_init(void)
{
return ("fail,contents"); /* $UMEM_LOGGING setting */
}
#endif
typedef enum {
} zfs_help_t;
typedef struct zfs_command {
const char *name;
/*
* Master command table. Each ZFS command has a name, associated function, and
* usage message. The usage messages need to be internationalized, so we have
* to have a function to return the usage message based on a command index.
*
* These commands are organized according to how they are displayed in the usage
* message. An empty command (one with a NULL name) indicates an empty line in
* the generic usage message.
*/
static zfs_command_t command_table[] = {
{ NULL },
{ NULL },
{ NULL },
{ NULL },
{ NULL },
{ NULL },
{ NULL },
{ NULL },
};
static const char *
{
switch (idx) {
case HELP_CLONE:
return (gettext("\tclone [-p] [-o property=value] ... "
"<snapshot> <filesystem|volume>\n"));
case HELP_CREATE:
return (gettext("\tcreate [-p] [-o property=value] ... "
"<filesystem>\n"
"\tcreate [-ps] [-b blocksize] [-o property=value] ... "
"-V <size> <volume>\n"));
case HELP_DESTROY:
return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n"
"\tdestroy [-dnpRrv] "
"<filesystem|volume>@<snap>[%<snap>][,...]\n"));
case HELP_GET:
return (gettext("\tget [-rHp] [-d max] "
"[-o \"all\" | field[,...]] [-t type[,...]] "
"[-s source[,...]]\n"
"\t <\"all\" | property[,...]> "
"[filesystem|volume|snapshot] ...\n"));
case HELP_INHERIT:
return (gettext("\tinherit [-rS] <property> "
"<filesystem|volume|snapshot> ...\n"));
case HELP_UPGRADE:
return (gettext("\tupgrade [-v]\n"
"\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
case HELP_LIST:
return (gettext("\tlist [-rH][-d max] "
"[-o property[,...]] [-t type[,...]] [-s property] ...\n"
"\t [-S property] ... "
"[filesystem|volume|snapshot] ...\n"));
case HELP_MOUNT:
return (gettext("\tmount\n"
"\tmount [-vO] [-o opts] <-a | filesystem>\n"));
case HELP_PROMOTE:
return (gettext("\tpromote <clone-filesystem>\n"));
case HELP_RECEIVE:
return (gettext("\treceive [-vnFu] <filesystem|volume|"
"snapshot>\n"
"\treceive [-vnFu] [-d | -e] <filesystem>\n"));
case HELP_RENAME:
return (gettext("\trename [-f] <filesystem|volume|snapshot> "
"<filesystem|volume|snapshot>\n"
"\trename [-f] -p <filesystem|volume> <filesystem|volume>\n"
"\trename -r <snapshot> <snapshot>"));
case HELP_ROLLBACK:
return (gettext("\trollback [-rRf] <snapshot>\n"));
case HELP_SEND:
return (gettext("\tsend [-DnPpRrv] [-[iI] snapshot] "
"<snapshot>\n"));
case HELP_SET:
return (gettext("\tset <property=value> "
"<filesystem|volume|snapshot> ...\n"));
case HELP_SHARE:
return (gettext("\tshare <-a | filesystem>\n"));
case HELP_SNAPSHOT:
return (gettext("\tsnapshot [-r] [-o property=value] ... "
"<filesystem@snapname|volume@snapname> ...\n"));
case HELP_UNMOUNT:
return (gettext("\tunmount [-f] "
"<-a | filesystem|mountpoint>\n"));
case HELP_UNSHARE:
return (gettext("\tunshare "
"<-a | filesystem|mountpoint>\n"));
case HELP_ALLOW:
return (gettext("\tallow <filesystem|volume>\n"
"\tallow [-ldug] "
"<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n"
"\t <filesystem|volume>\n"
"\tallow [-ld] -e <perm|@setname>[,...] "
"<filesystem|volume>\n"
"\tallow -c <perm|@setname>[,...] <filesystem|volume>\n"
"\tallow -s @setname <perm|@setname>[,...] "
"<filesystem|volume>\n"));
case HELP_UNALLOW:
return (gettext("\tunallow [-rldug] "
"<\"everyone\"|user|group>[,...]\n"
"\t [<perm|@setname>[,...]] <filesystem|volume>\n"
"\tunallow [-rld] -e [<perm|@setname>[,...]] "
"<filesystem|volume>\n"
"\tunallow [-r] -c [<perm|@setname>[,...]] "
"<filesystem|volume>\n"
"\tunallow [-r] -s @setname [<perm|@setname>[,...]] "
"<filesystem|volume>\n"));
case HELP_USERSPACE:
return (gettext("\tuserspace [-hniHp] [-o field[,...]] "
"[-sS field] ... [-t type[,...]]\n"
"\t <filesystem|snapshot>\n"));
case HELP_GROUPSPACE:
return (gettext("\tgroupspace [-hniHpU] [-o field[,...]] "
"[-sS field] ... [-t type[,...]]\n"
"\t <filesystem|snapshot>\n"));
case HELP_HOLD:
return (gettext("\thold [-r] <tag> <snapshot> ...\n"));
case HELP_HOLDS:
return (gettext("\tholds [-r] <snapshot> ...\n"));
case HELP_RELEASE:
return (gettext("\trelease [-r] <tag> <snapshot> ...\n"));
case HELP_DIFF:
return (gettext("\tdiff [-FHt] <snapshot> "
"[snapshot|filesystem]\n"));
}
abort();
/* NOTREACHED */
}
void
nomem(void)
{
exit(1);
}
/*
* Utility function to guarantee malloc() success.
*/
void *
{
void *data;
nomem();
return (data);
}
static char *
safe_strdup(char *str)
{
nomem();
return (dupstr);
}
/*
* Callback routine that will print out information for each of
* the properties.
*/
static int
{
if (zfs_prop_readonly(prop))
else
if (zfs_prop_inheritable(prop))
else
else
return (ZPROP_CONT);
}
/*
* Display usage message. If we're inside a command, display only the usage for
* that command. Otherwise, iterate over the entire command table and display
* a complete usage message.
*/
static void
{
int i;
if (current_command == NULL) {
gettext("where 'command' is one of the following:\n\n"));
for (i = 0; i < NCOMMAND; i++) {
else
}
"pool/[dataset/]*dataset[@name]\n"));
} else {
}
if (current_command != NULL &&
if (show_properties) {
gettext("\nThe following properties are supported:\n"));
"PROPERTY", "EDIT", "INHERIT", "VALUES");
/* Iterate over all properties */
"with standard units such as K, M, G, etc.\n"));
"be specified by using a name containing a colon (:).\n"));
"properties must be appended with\n"
"a user or group specifier of one of these forms:\n"
" POSIX name (eg: \"matt\")\n"
" POSIX id (eg: \"126829\")\n"
" SMB name@domain (eg: \"matt@sun\")\n"
" SMB SID (eg: \"S-1-234-567-89\")\n"));
} else {
gettext("\nFor the property list, run: %s\n"),
"zfs set|get");
gettext("\nFor the delegated permission list, run: %s\n"),
"zfs allow|unallow");
}
/*
* See comments at end of main().
*/
(void) printf("dumping core by request\n");
abort();
}
}
static int
{
"'=' for -o option\n"));
return (-1);
}
*propval = '\0';
propval++;
"specified multiple times\n"), propname);
return (-1);
}
nomem();
return (0);
}
static int
{
char *tmp;
int depth;
if (*tmp) {
}
if (depth < 0) {
gettext("Depth can not be negative.\n"));
}
return (depth);
}
static char *pt_reverse = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
static void
start_progress_timer(void)
{
}
static void
set_progress_header(char *header)
{
if (pt_shown) {
}
}
static void
update_progress(char *update)
{
} else if (pt_shown) {
}
}
static void
finish_progress(char *done)
{
if (pt_shown) {
}
}
/*
* zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
*
* Given an existing dataset, create a writable copy whose initial contents
* are the same as the source. The newly created dataset maintains a
* dependency on the original; the original cannot be destroyed so long as
* the clone exists.
*
* The '-p' flag creates all the non-existing ancestors of the target first.
*/
static int
{
int ret = 0;
int c;
nomem();
/* check options */
switch (c) {
case 'o':
return (1);
break;
case 'p':
break;
case '?':
optopt);
goto usage;
}
}
/* check number of arguments */
if (argc < 1) {
"argument\n"));
goto usage;
}
if (argc < 2) {
"argument\n"));
goto usage;
}
if (argc > 2) {
goto usage;
}
/* open the source dataset */
return (1);
ZFS_TYPE_VOLUME)) {
/*
* Now create the ancestors of the target dataset. If the
* target already exists and '-p' option was used we should not
* complain.
*/
return (0);
return (1);
}
/* pass to libzfs */
/* create the mountpoint if necessary */
if (ret == 0) {
}
}
return (!!ret);
if (zhp)
return (-1);
}
/*
* zfs create [-p] [-o prop=value] ... fs
* zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size
*
* Create a new dataset. This command can be used to create filesystems
* and volumes. Snapshot creation is handled by 'zfs snapshot'.
* For volumes, the user must specify a size to be used.
*
* The '-s' flag applies only to volumes, and indicates that we should not try
* to set the reservation for this volume. By default we set a reservation
* equal to the size for any volume. For pools with SPA_VERSION >=
* SPA_VERSION_REFRESERVATION, we set a refreservation instead.
*
* The '-p' flag creates all the non-existing ancestors of the target first.
*/
static int
{
int c;
int ret = 1;
int canmount = ZFS_CANMOUNT_OFF;
nomem();
/* check options */
switch (c) {
case 'V':
"size '%s': %s\n"), optarg,
goto error;
}
if (nvlist_add_uint64(props,
nomem();
break;
case 'p':
break;
case 'b':
"block size '%s': %s\n"), optarg,
goto error;
}
if (nvlist_add_uint64(props,
intval) != 0)
nomem();
break;
case 'o':
goto error;
break;
case 's':
break;
case ':':
"argument\n"));
goto badusage;
case '?':
optopt);
goto badusage;
}
}
"used when creating a volume\n"));
goto badusage;
}
/* check number of arguments */
if (argc == 0) {
goto badusage;
}
if (argc > 1) {
goto badusage;
}
char *p;
char *strval;
*p = '\0';
if (p != NULL)
*p = '/';
if (zpool_handle == NULL)
goto error;
else
&strval) != 0) {
if (nvlist_add_uint64(props,
nomem();
}
}
}
/*
* Now create the ancestors of target dataset. If the target
* already exists and '-p' option was used we should not
* complain.
*/
ret = 0;
goto error;
}
goto error;
}
/* pass to libzfs */
goto error;
goto error;
ret = 0;
/*
* if the user doesn't want the dataset automatically mounted,
*/
/*
* verbose error message to let the user know that their filesystem was
* in fact created, even if we failed to mount or share it.
*/
if (canmount == ZFS_CANMOUNT_ON) {
"successfully created, but not mounted\n"));
ret = 1;
"successfully created, but not shared\n"));
ret = 1;
}
}
if (zhp)
return (ret);
return (2);
}
/*
* zfs destroy [-rRf] <fs, vol>
* zfs destroy [-rRd] <snap>
*
* -r Recursively destroy all children
* -R Recursively destroy all dependents, including clones
* -f Force unmounting of any dependents
* -d If we can't destroy now, mark for deferred destruction
*
* Destroys the given dataset. By default, it will unmount any filesystems,
* and refuse to destroy a dataset that has any dependents. A dependent can
* either be a child, or a clone of a child.
*/
typedef struct destroy_cbdata {
/* first snap in contiguous run */
char *cb_firstsnap;
/* previous snap in contiguous run */
char *cb_prevsnap;
char *cb_snapspec;
/*
* Check for any dependents based on the '-r' or '-R' flags.
*/
static int
{
/*
* This is a direct descendant, not a clone somewhere else in
* the hierarchy.
*/
if (cbp->cb_recurse)
goto out;
"%s has children\n"),
"the following datasets:\n"));
}
} else {
/*
* This is a clone. We only want to report this if the '-r'
* wasn't specified, or the target is a snapshot.
*/
if (!cbp->cb_recurse &&
goto out;
"%s has dependent clones\n"),
"the following datasets:\n"));
}
}
out:
return (0);
}
static int
{
if (cb->cb_verbose) {
if (cb->cb_parsable) {
name);
} else {
name);
}
}
/*
* Ignore pools (which we've already flagged as an error before getting
* here).
*/
return (0);
}
return (-1);
}
}
return (0);
}
static int
{
int err = 0;
/* this snap continues the current range */
nomem();
if (cb->cb_verbose) {
if (cb->cb_parsable) {
name);
} else {
name);
}
}
/* end of this range */
}
return (err);
}
static int
{
int err = 0;
if (err == 0) {
}
}
return (err);
}
static int
{
int err = 0;
/* Check for clones. */
if (!cb->cb_doclones) {
}
if (err == 0) {
nomem();
}
return (err);
}
static int
{
int err = 0;
err = 0;
if (err != 0)
goto out;
if (cb->cb_verbose) {
if (err != 0)
goto out;
}
if (cb->cb_recurse)
out:
return (err);
}
static int
{
int err = 0;
/*
* We can't defer destroy non-snapshots, so set it to
* false while destroying the clones.
*/
if (err != 0)
return (err);
}
}
return (0);
}
static int
{
destroy_cbdata_t cb = { 0 };
int c;
char *at;
/* check options */
switch (c) {
case 'v':
break;
case 'p':
break;
case 'n':
break;
case 'd':
break;
case 'f':
break;
case 'r':
break;
case 'R':
break;
case '?':
default:
optopt);
}
}
/* check number of arguments */
if (argc == 0) {
}
if (argc > 1) {
}
int err = 0;
/* Build the list of snaps to destroy in cb_nvl. */
nomem();
*at = '\0';
return (1);
return (1);
}
"snapshots to destroy; check snapshot names.\n"));
return (1);
}
if (cb.cb_verbose) {
char buf[16];
if (cb.cb_parsable) {
(void) printf("reclaim\t%llu\n",
buf);
} else {
buf);
}
}
if (cb.cb_doclones)
if (err == 0) {
}
}
if (err != 0)
return (1);
} else {
/* Open the given dataset */
return (1);
/*
* Perform an explicit check for pools before going any further.
*/
"operation does not apply to pools\n"),
zfs_get_name(zhp));
"%s' to destroy all datasets in the pool\n"),
zfs_get_name(zhp));
return (1);
}
/*
*/
if (!cb.cb_doclones &&
&cb) != 0) {
return (1);
}
return (1);
}
&cb) != 0) {
return (1);
}
/*
* Do the real thing. The callback will close the
* handle regardless of whether it succeeds or not.
*/
return (1);
}
return (0);
}
static boolean_t
{
int i;
for (i = 0; i < ZFS_GET_NCOLS &&
if (col == GET_COL_RECVD)
return (B_TRUE);
return (B_FALSE);
}
/*
* zfs get [-rHp] [-o all | field[,field]...] [-s source[,source]...]
* < all | property[,property]... > < fs | snap | vol > ...
*
* -r recurse over any child datasets
* -H scripted mode. Headers are stripped, and fields are separated
* by tabs instead of spaces.
* -o Set of fields to display. One of "name,property,value,
* received,source". Default is "name,property,value,source".
* "all" is an alias for all five.
* -s Set of sources to allow. One of
* "local,default,inherited,received,temporary,none". Default is
* all six.
* -p Display values in parsable (literal) format.
*
* Prints properties for the given datasets. The user can control which
* columns to display as well as which property types to allow.
*/
/*
* Invoked to display the properties for a single dataset.
*/
static int
{
char buf[ZFS_MAXPROPLEN];
char rbuf[ZFS_MAXPROPLEN];
char source[ZFS_MAXNAMELEN];
char *strval;
char *sourceval;
/*
* Skip the special fake placeholder. This will also skip over
* the name property when 'all' is specified.
*/
continue;
sizeof (source),
cbp->cb_literal) != 0) {
continue;
ZFS_TYPE_DATASET)) {
gettext("No such property '%s'\n"),
continue;
}
}
cbp->cb_literal) == 0))
}
}
} else {
continue;
strval = "-";
} else {
ZPROP_VALUE, &strval) == 0);
ZPROP_SOURCE, &sourceval) == 0);
zfs_get_name(zhp)) == 0) {
ZPROP_SOURCE_VAL_RECVD) == 0) {
} else {
}
}
cbp->cb_literal) == 0))
}
}
return (0);
}
static int
{
zprop_get_cbdata_t cb = { 0 };
int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
int types = ZFS_TYPE_DATASET;
int ret = 0;
int limit = 0;
zprop_list_t fake_name = { 0 };
/*
* Set up default columns and sources.
*/
/* check options */
switch (c) {
case 'p':
break;
case 'd':
break;
case 'r':
break;
case 'H':
break;
case ':':
"'%c' option\n"), optopt);
break;
case 'o':
/*
* Process the set of columns to display. We zero out
* the structure to give us a blank slate.
*/
i = 0;
while (*optarg != '\0') {
static char *col_subopts[] =
{ "name", "property", "value", "received",
if (i == ZFS_GET_NCOLS) {
"many fields given to -o "
"option\n"));
}
&value)) {
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
if (i > 0) {
gettext("\"all\" conflicts "
"with specific fields "
"given to -o option\n"));
}
i = ZFS_GET_NCOLS;
break;
default:
gettext("invalid column name "
"'%s'\n"), value);
}
}
break;
case 's':
cb.cb_sources = 0;
while (*optarg != '\0') {
static char *source_subopts[] = {
"local", "default", "inherited",
"received", "temporary", "none",
NULL };
&value)) {
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
default:
gettext("invalid source "
"'%s'\n"), value);
}
}
break;
case 't':
types = 0;
while (*optarg != '\0') {
static char *type_subopts[] = { "filesystem",
&value)) {
case 0:
break;
case 1:
types |= ZFS_TYPE_VOLUME;
break;
case 2:
break;
case 3:
break;
default:
gettext("invalid type '%s'\n"),
value);
}
}
break;
case '?':
optopt);
}
}
if (argc < 1) {
"argument\n"));
}
!= 0)
argc--;
argv++;
/*
* As part of zfs_expand_proplist(), we keep track of the maximum column
* width for each property. For the 'NAME' (and 'SOURCE') columns, we
* need to know the maximum name length. However, the user likely did
* not specify 'name' as one of the properties to fetch, so we need to
* make sure we always include at least this property for
* print_get_headers() to work properly.
*/
}
/* run for each object */
else
return (ret);
}
/*
* inherit [-rS] <property> <fs|vol> ...
*
* -r Recurse over all children
* -S Revert to received value, if any
*
* For each dataset specified on the command line, inherit the given property
* from its parent. Inheriting a property at the pool level will cause it to
* use the default value. The '-r' flag will recurse over all children, and is
* useful for setting a property on a hierarchy-wide basis, regardless of any
* local modifications for each dataset.
*/
typedef struct inherit_cbdata {
const char *cb_propname;
static int
{
/*
* If we're doing it recursively, then ignore properties that
* are not valid for this type of dataset.
*/
if (prop != ZPROP_INVAL &&
return (0);
}
static int
{
}
static int
{
int c;
inherit_cbdata_t cb = { 0 };
char *propname;
int ret = 0;
int flags = 0;
/* check options */
switch (c) {
case 'r':
break;
case 'S':
break;
case '?':
default:
optopt);
}
}
/* check number of arguments */
if (argc < 1) {
}
if (argc < 2) {
}
argc--;
argv++;
if (zfs_prop_readonly(prop)) {
"%s property is read-only\n"),
propname);
return (1);
}
"be inherited\n"), propname);
if (prop == ZFS_PROP_QUOTA ||
prop == ZFS_PROP_RESERVATION ||
prop == ZFS_PROP_REFQUOTA ||
"%s=none' to clear\n"), propname);
return (1);
}
prop == ZFS_PROP_VERSION)) {
"be reverted to a received value\n"), propname);
return (1);
}
} else if (!zfs_prop_user(propname)) {
propname);
}
if (flags & ZFS_ITER_RECURSE) {
} else {
}
return (ret);
}
typedef struct upgrade_cbdata {
char cb_lastfs[ZFS_MAXNAMELEN];
static int
{
return (B_FALSE);
}
static int
{
char *str;
"formatted using a newer software version and\n"
"cannot be accessed on the current system.\n\n");
} else {
"out of date, and can be upgraded. After being\n"
"upgraded, these filesystems (and any 'zfs send' "
"streams generated from\n"
"subsequent snapshots) will no longer be "
"accessible by older software versions.\n\n");
}
if (!cb->cb_foundone) {
}
}
return (0);
}
static int
{
int needed_spa_version;
int spa_version;
return (-1);
if (needed_spa_version < 0)
return (-1);
if (spa_version < needed_spa_version) {
/* can't upgrade */
"upgraded; the pool version needs to first "
"be upgraded\nto version %d\n\n"),
cb->cb_numfailed++;
return (0);
}
/* upgrade */
char verstr[16];
/*
* If they did "zfs upgrade -a", then we could
* be doing ioctls to different pools. We need
* to log this history once to each pool, and bypass
* the normal history logging that happens in main().
*/
}
cb->cb_numupgraded++;
else
cb->cb_numfailed++;
/* can't downgrade */
"it is already at version %u\n"),
cb->cb_numfailed++;
} else {
cb->cb_numsamegraded++;
}
return (0);
}
/*
* zfs upgrade
* zfs upgrade -v
* zfs upgrade [-r] [-V <version>] <-a | filesystem>
*/
static int
{
int ret = 0;
upgrade_cbdata_t cb = { 0 };
char c;
int flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
/* check options */
switch (c) {
case 'r':
break;
case 'v':
break;
case 'V':
}
break;
case 'a':
break;
case '?':
default:
optopt);
}
}
if (showversions) {
/* Show info on available versions. */
"supported:\n\n"));
(void) printf("--- -----------------------------------------"
"---------------\n");
"user identifier (FUID)\n"));
"properties\n"));
"version, including supported releases,\n"));
(void) printf("see the ZFS Administration Guide.\n\n");
ret = 0;
/* Upgrade filesystems */
if (cb.cb_version == 0)
if (cb.cb_numsamegraded) {
"this version\n"),
}
if (cb.cb_numfailed != 0)
ret = 1;
} else {
/* List old-version filesytems */
"ZFS filesystem version %llu.\n\n"), ZPL_VERSION);
"formatted with the current version.\n"));
}
}
return (ret);
}
#define USTYPE_USR_BIT (0)
#define USTYPE_GRP_BIT (1)
#define USTYPE_PSX_BIT (2)
#define USTYPE_SMB_BIT (3)
#define USPROP_USED_BIT (0)
#define USPROP_QUOTA_BIT (1)
typedef struct us_node {
} us_node_t;
typedef struct us_cbdata {
} us_cbdata_t;
typedef struct {
static int
{
int rc = 0;
char *lvstr = "";
char *rvstr = "";
switch (prop) {
case ZFS_PROP_TYPE:
propname = "type";
break;
case ZFS_PROP_NAME:
propname = "name";
if (num_name) {
&lv32);
&rv32);
} else {
&lvstr);
&rvstr);
}
break;
case ZFS_PROP_USED:
case ZFS_PROP_QUOTA:
if (ZFS_PROP_USED == prop)
propname = "used";
else
propname = "quota";
}
if (rc)
if (rc < 0)
else
}
return (rc);
}
static inline const char *
us_type2str(unsigned field_type)
{
switch (field_type) {
case USTYPE_PSX_USR:
return ("POSIX User");
case USTYPE_PSX_GRP:
return ("POSIX Group");
case USTYPE_SMB_USR:
return ("SMB User");
case USTYPE_SMB_GRP:
return ("SMB Group");
default:
return ("Undefined");
}
}
/*
* zfs userspace
*/
static int
{
char *propname;
char namebuf[32];
char sizebuf[32];
us_node_t *n;
unsigned type;
const char *typestr;
/* POSIX */
if (g)
} else {
if (p)
}
} else {
int err = 0;
/* SMB */
} else {
}
if (err == 0) {
if (e != NULL) {
return (NULL);
}
}
}
/*
* if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA)
* ug = "group";
* else
* ug = "user";
*/
propname = "used";
else
propname = "quota";
if (cb->cb_nicenum)
else
return (-1);
}
nomem();
if (cb->cb_numname) {
nomem();
} else {
nomem();
}
} else {
}
if (n == NULL)
else {
node = n;
}
nomem();
return (0);
}
static inline boolean_t
{
unsigned type;
unsigned prop;
switch (p) {
case ZFS_PROP_USERUSED:
type = USTYPE_USR;
prop = USPROP_USED;
break;
case ZFS_PROP_USERQUOTA:
type = USTYPE_USR;
prop = USPROP_QUOTA;
break;
case ZFS_PROP_GROUPUSED:
type = USTYPE_GRP;
prop = USPROP_USED;
break;
case ZFS_PROP_GROUPQUOTA:
type = USTYPE_GRP;
prop = USPROP_QUOTA;
break;
default: /* ALL */
return (B_TRUE);
};
}
#define USFIELD_TYPE (1 << 0)
static int
{
char *delim;
do {
int i;
*delim = '\0';
for (i = 0; i < len; i++)
break;
}
if (!found) {
"for -t option\n"), field);
return (-1);
}
} while (delim);
return (0);
}
"all" };
static unsigned type_bits[] = {
};
static unsigned us_field_bits[] = {
};
static void
{
char valstr[ZFS_MAXNAMELEN];
nomem();
unsigned field = 0;
unsigned width = 0;
int i;
for (i = 0; i < 4; i++) {
field = us_field_bits[i];
break;
}
}
continue;
switch (type) {
case DATA_TYPE_UINT32:
break;
case DATA_TYPE_UINT64:
break;
case DATA_TYPE_STRING:
break;
default:
}
if (!first)
if (scripted)
(void) printf("\t");
else
(void) printf(" ");
switch (field) {
case USFIELD_TYPE:
width = type_width;
break;
case USFIELD_NAME:
if (type == DATA_TYPE_UINT64) {
}
width = name_width;
break;
case USFIELD_USED:
case USFIELD_QUOTA:
if (type == DATA_TYPE_UINT64) {
if (parseable)
else
sizeof (valstr));
}
if (field == USFIELD_USED)
width = used_width;
else {
width = quota_width;
}
break;
}
else {
if (type == DATA_TYPE_STRING)
else
}
}
(void) printf("\n");
}
static void
{
const char *col;
int i;
if (!scripted) {
for (i = 0; i < 4; i++) {
unsigned field = us_field_bits[i];
continue;
col);
else
col);
}
(void) printf("\n");
}
if (rmnode)
}
}
static int
{
char *cmd;
int error = 0;
int c;
unsigned fields = 0;
if (argc < 2)
/* toggle default group types */
/* check options */
switch (c) {
case 'n':
break;
case 'H':
break;
case 'p':
break;
case 'o':
4) != 0)
return (1);
break;
case 's':
B_FALSE) != 0) {
}
break;
case 'S':
B_TRUE) != 0) {
}
break;
case 't':
return (1);
break;
case 'i':
break;
case ':':
"'%c' option\n"), optopt);
break;
case '?':
optopt);
}
}
/* ok, now we have sorted by default colums (type,name) avl tree */
if (sortcol) {
resort_avl = B_TRUE;
break;
}
}
}
if (!fields)
return (1);
nomem();
nomem();
if (sortcol && !resort_avl)
else {
}
for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) {
continue;
if (error)
break;
}
if (resort_avl) {
uu_avl_index_t idx = 0;
uu_list_index_t idx2 = 0;
}
}
NULL)
}
}
/* print & free node`s nvlist memory */
if (sortcol)
/*
* Finally, clean up the AVL tree.
*/
nomem();
}
return (error);
}
/*
* list [-r][-d max] [-H] [-o property[,property]...] [-t type[,type]...]
* [-s property [-s property]...] [-S property [-S property]...]
* <dataset> ...
*
* -r Recurse over all children
* -d Limit recursion by depth.
* -H Scripted mode; elide headers and separate columns by tabs
* -o Control which fields to display.
* -t Control which object types to display.
* -s Specify sort columns, descending order.
* -S Specify sort columns, ascending order.
*
* When given no arguments, lists all filesystems in the system.
* Otherwise, list the specified datasets, optionally recursing down them if
* '-r' is specified.
*/
typedef struct list_cbdata {
/*
* Given a list of columns to display, output appropriate headers for each one.
*/
static void
{
char headerbuf[ZFS_MAXPROPLEN];
const char *header;
int i;
if (!first) {
(void) printf(" ");
} else {
}
} else {
headerbuf[i] = '\0';
}
else if (right_justify)
else
}
(void) printf("\n");
}
/*
* Given a dataset and a list of fields, print out all the properties according
* to the described layout.
*/
static void
{
char property[ZFS_MAXPROPLEN];
char *propstr;
int width;
if (!first) {
if (scripted)
(void) printf("\t");
else
(void) printf(" ");
} else {
}
propstr = "-";
else
propstr = "-";
else
propstr = "-";
else
} else {
propstr = "-";
else
ZPROP_VALUE, &propstr) == 0);
}
/*
* If this is being called in scripted mode, or if this is the
* last column and it is left-justified, don't include a width
* format specifier.
*/
else if (right_justify)
else
}
(void) printf("\n");
}
/*
* Generic callback function to list a dataset or snapshot.
*/
static int
{
if (!cbp->cb_scripted)
}
return (0);
}
static int
{
int c;
static char default_fields[] =
"name,used,available,referenced,mountpoint";
int types = ZFS_TYPE_DATASET;
list_cbdata_t cb = { 0 };
char *value;
int limit = 0;
int ret = 0;
/* check options */
switch (c) {
case 'o':
break;
case 'd':
break;
case 'r':
break;
case 'H':
break;
case 's':
B_FALSE) != 0) {
}
break;
case 'S':
B_TRUE) != 0) {
}
break;
case 't':
types = 0;
while (*optarg != '\0') {
static char *type_subopts[] = { "filesystem",
&value)) {
case 0:
break;
case 1:
types |= ZFS_TYPE_VOLUME;
break;
case 2:
break;
case 3:
break;
default:
gettext("invalid type '%s'\n"),
value);
}
}
break;
case ':':
"'%c' option\n"), optopt);
break;
case '?':
optopt);
}
}
/*
* If "-o space" and no types were specified, don't display snapshots.
*/
types &= ~ZFS_TYPE_SNAPSHOT;
/*
* If the user specifies '-o all', the zprop_get_list() doesn't
* normally include the name of the dataset. For 'zfs list', we always
* want this property to be first.
*/
!= 0)
return (ret);
}
/*
* zfs rename [-f] <fs | snap | vol> <fs | snap | vol>
* zfs rename [-f] -p <fs | vol> <fs | vol>
* zfs rename -r <snap> <snap>
*
* Renames the given dataset to another of the same type.
*
* The '-p' flag creates all the non-existing ancestors of the target first.
*/
/* ARGSUSED */
static int
{
int c;
int ret = 0;
/* check options */
switch (c) {
case 'p':
break;
case 'r':
break;
case 'f':
break;
case '?':
default:
optopt);
}
}
/* check number of arguments */
if (argc < 1) {
"argument\n"));
}
if (argc < 2) {
"argument\n"));
}
if (argc > 2) {
}
"exclusive\n"));
}
"rename must be a snapshot\n"));
}
return (1);
/* If we were asked and the name looks good, try to create ancestors. */
return (1);
}
return (ret);
}
/*
* zfs promote <fs>
*
* Promotes the given clone fs to be the parent
*/
/* ARGSUSED */
static int
{
int ret = 0;
/* check options */
}
/* check number of arguments */
if (argc < 2) {
" argument\n"));
}
if (argc > 2) {
}
return (1);
return (ret);
}
/*
* zfs rollback [-rRf] <snapshot>
*
* -r Delete any intervening snapshots before doing rollback
* -R Delete any snapshots and their clones
* -f ignored for backwards compatability
*
* Given a filesystem, rollback to a specific snapshot, discarding any changes
* since then and making it the active dataset. If more recent snapshots exist,
* the command will complain unless the '-r' flag is given.
*/
typedef struct rollback_cbdata {
int cb_doclones;
char *cb_target;
int cb_error;
/*
* Report any snapshots more recent than the one specified. Used when '-r' is
* not specified. We reuse this same callback for the snapshot dependents - if
* 'cb_dependent' is set, then this is a dependent and we should report it
* without checking the transaction group.
*/
static int
{
if (cbp->cb_doclones) {
return (0);
}
if (!cbp->cb_dependent) {
"rollback to '%s': more recent snapshots "
"exist\n"),
"force deletion of the following "
"snapshots:\n"));
}
if (cbp->cb_recurse) {
rollback_check, cbp) != 0) {
return (-1);
}
} else {
zfs_get_name(zhp));
}
}
} else {
"'%s': clones of previous snapshots exist\n"),
"force deletion of the following clones and "
"dependents:\n"));
}
}
return (0);
}
static int
{
int ret = 0;
int c;
rollback_cbdata_t cb = { 0 };
char parentname[ZFS_MAXNAMELEN];
char *delim;
/* check options */
switch (c) {
case 'r':
break;
case 'R':
break;
case 'f':
break;
case '?':
optopt);
}
}
/* check number of arguments */
if (argc < 1) {
}
if (argc > 1) {
}
/* open the snapshot */
return (1);
/* open the parent dataset */
*delim = '\0';
return (1);
}
/*
* of '-r' and '-R'.
*/
goto out;
goto out;
/*
* Rollback parent to the given snapshot.
*/
out:
if (ret == 0)
return (0);
else
return (1);
}
/*
* zfs set property=value { fs | snap | vol } ...
*
* Sets the given property for all datasets specified on the command line.
*/
typedef struct set_cbdata {
char *cb_propname;
char *cb_value;
} set_cbdata_t;
static int
{
switch (libzfs_errno(g_zfs)) {
case EZFS_MOUNTFAILED:
"but unable to remount filesystem\n"));
break;
case EZFS_SHARENFSFAILED:
"but unable to reshare filesystem\n"));
break;
}
return (1);
}
return (0);
}
static int
{
int ret = 0;
/* check for options */
}
/* check number of arguments */
if (argc < 2) {
"argument\n"));
}
if (argc < 3) {
}
/* validate property=value argument */
"property=value argument\n"));
}
gettext("missing property in property=value argument\n"));
}
return (ret);
}
typedef struct snap_cbdata {
const char *sd_snapname;
static int
{
char *name;
int rv = 0;
int error;
if (error == -1)
nomem();
if (sd->sd_recursive)
return (rv);
}
/*
* zfs snapshot [-r] [-o prop=value] ... <fs@snap>
*
* Creates a snapshot with the given name. While functionally equivalent to
* 'zfs create', it is a separate command to differentiate intent.
*/
static int
{
int ret = 0;
char c;
snap_cbdata_t sd = { 0 };
nomem();
nomem();
/* check options */
switch (c) {
case 'o':
return (1);
break;
case 'r':
break;
case '?':
optopt);
goto usage;
}
}
/* check number of arguments */
if (argc < 1) {
goto usage;
}
if (argc > 1)
char *atp;
goto usage;
*atp = '\0';
goto usage;
goto usage;
}
if (ret != 0 && multiple_snaps)
return (ret != 0);
return (-1);
}
/*
* Send a backup stream to stdout.
*/
static int
{
char *cp;
sendflags_t flags = { 0 };
int c, err;
/* check options */
switch (c) {
case 'i':
if (fromname)
break;
case 'I':
if (fromname)
break;
case 'R':
break;
case 'p':
break;
case 'P':
break;
case 'v':
break;
case 'D':
break;
case 'n':
break;
case ':':
"'%c' option\n"), optopt);
break;
case '?':
optopt);
}
}
/* check number of arguments */
if (argc < 1) {
}
if (argc > 1) {
}
gettext("Error: Stream can not be written to a terminal.\n"
"You must redirect standard output.\n"));
return (1);
}
gettext("argument must be a snapshot\n"));
}
*cp = '\0';
return (1);
/*
* If they specified the full path to the snapshot, chop off
* everything except the short name of the snapshot, but special
* case if they specify the origin.
*/
char origin[ZFS_MAXNAMELEN];
} else {
*cp = '\0';
gettext("incremental source must be "
"in same filesystem\n"));
}
gettext("invalid incremental source\n"));
}
}
}
/*
* dump_nvlist prints to stdout, but that's been
* redirected to a file. Make it print to stderr
* instead.
*/
dump_nvlist(dbgnv, 0);
}
return (err != 0);
}
/*
* zfs receive [-vnFu] [-d | -e] <fs@snap>
*
* Restore a backup stream from stdin.
*/
static int
{
int c, err;
recvflags_t flags = { 0 };
/* check options */
switch (c) {
case 'd':
break;
case 'e':
break;
case 'n':
break;
case 'u':
break;
case 'v':
break;
case 'F':
break;
case ':':
"'%c' option\n"), optopt);
break;
case '?':
optopt);
}
}
/* check number of arguments */
if (argc < 1) {
}
if (argc > 1) {
}
if (isatty(STDIN_FILENO)) {
gettext("Error: Backup stream can not be read "
"from a terminal.\n"
"You must redirect standard input.\n"));
return (1);
}
return (err != 0);
}
/*
*/
/* copied from zfs/sys/dsl_deleg.h */
#define ZFS_DELEG_PERM_CREATE "create"
#define ZFS_DELEG_PERM_DESTROY "destroy"
#define ZFS_DELEG_PERM_SNAPSHOT "snapshot"
#define ZFS_DELEG_PERM_ROLLBACK "rollback"
#define ZFS_DELEG_PERM_CLONE "clone"
#define ZFS_DELEG_PERM_PROMOTE "promote"
#define ZFS_DELEG_PERM_RENAME "rename"
#define ZFS_DELEG_PERM_MOUNT "mount"
#define ZFS_DELEG_PERM_SHARE "share"
#define ZFS_DELEG_PERM_SEND "send"
#define ZFS_DELEG_PERM_RECEIVE "receive"
#define ZFS_DELEG_PERM_ALLOW "allow"
#define ZFS_DELEG_PERM_USERPROP "userprop"
#define ZFS_DELEG_PERM_USERQUOTA "userquota"
#define ZFS_DELEG_PERM_GROUPQUOTA "groupquota"
#define ZFS_DELEG_PERM_USERUSED "userused"
#define ZFS_DELEG_PERM_GROUPUSED "groupused"
#define ZFS_DELEG_PERM_HOLD "hold"
#define ZFS_DELEG_PERM_RELEASE "release"
#define ZFS_DELEG_PERM_DIFF "diff"
static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = {
{ NULL, ZFS_DELEG_NOTE_NONE }
};
/* permission structure */
typedef struct deleg_perm {
const char *dp_name;
} deleg_perm_t;
/* */
typedef struct deleg_perm_node {
/* permissions set */
typedef struct who_perm {
const char *who_name; /* id */
} who_perm_t;
/* */
typedef struct who_perm_node {
typedef struct fs_perm_set fs_perm_set_t;
/* fs permissions */
struct fs_perm {
const char *fsp_name;
};
/* */
typedef struct fs_perm_node {
/* top level structure */
struct fs_perm_set {
};
static inline const char *
{
/* subcommands */
switch (note) {
/* SUBCOMMANDS */
/* OTHER */
case ZFS_DELEG_NOTE_GROUPUSED:
case ZFS_DELEG_NOTE_USERPROP:
case ZFS_DELEG_NOTE_USERQUOTA:
case ZFS_DELEG_NOTE_USERUSED:
/* other */
return (gettext("other"));
default:
return (gettext("subcommand"));
}
}
static int inline
{
int res;
switch (who_type) {
case ZFS_DELEG_NAMED_SET_SETS:
case ZFS_DELEG_NAMED_SET:
res = 0;
break;
case ZFS_DELEG_CREATE_SETS:
case ZFS_DELEG_CREATE:
res = 1;
break;
case ZFS_DELEG_USER_SETS:
case ZFS_DELEG_USER:
res = 2;
break;
case ZFS_DELEG_GROUP_SETS:
case ZFS_DELEG_GROUP:
res = 3;
break;
case ZFS_DELEG_EVERYONE_SETS:
case ZFS_DELEG_EVERYONE:
res = 4;
break;
default:
res = -1;
}
return (res);
}
/* ARGSUSED */
static int
{
const who_perm_node_t *l = larg;
const who_perm_node_t *r = rarg;
if (res == 0)
if (res == 0)
return (0);
if (res > 0)
return (1);
else
return (-1);
}
/* ARGSUSED */
static int
{
const deleg_perm_node_t *l = larg;
const deleg_perm_node_t *r = rarg;
if (res == 0)
return (0);
if (res > 0)
return (1);
else
return (-1);
}
static inline void
{
nomem();
UU_DEFAULT)) == NULL)
nomem();
UU_DEFAULT)) == NULL)
nomem();
UU_DEFAULT)) == NULL)
nomem();
== NULL)
nomem();
}
static inline void fs_perm_fini(fs_perm_t *);
static inline void who_perm_fini(who_perm_t *);
static inline void
{
}
}
static inline void
const char *name)
{
}
static inline void
{
UU_DEFAULT)) == NULL)
nomem();
}
static inline void
{
}
}
static inline void
{
== NULL)
nomem();
== NULL)
nomem();
}
static inline void
{
node);
}
node);
}
}
static void inline
{
uu_avl_index_t idx = 0;
== NULL)
else {
node = found_node;
}
switch (locality) {
case ZFS_DELEG_LOCAL:
break;
case ZFS_DELEG_DESCENDENT:
break;
case ZFS_DELEG_NA:
break;
default:
}
}
static inline int
{
safe_malloc(sizeof (deleg_perm_node_t));
}
return (0);
}
static inline int
{
return (-1);
switch (perm_type) {
case ZFS_DELEG_CREATE:
case ZFS_DELEG_CREATE_SETS:
case ZFS_DELEG_NAMED_SET:
case ZFS_DELEG_NAMED_SET_SETS:
break;
case ZFS_DELEG_USER:
case ZFS_DELEG_USER_SETS:
case ZFS_DELEG_GROUP:
case ZFS_DELEG_GROUP_SETS:
case ZFS_DELEG_EVERYONE:
case ZFS_DELEG_EVERYONE_SETS:
break;
}
if (is_set) {
sizeof (who_perm_node_t));
uu_avl_index_t idx = 0;
== NULL) {
switch (perm_type) {
case ZFS_DELEG_USER_SETS:
case ZFS_DELEG_USER:
if (p)
break;
case ZFS_DELEG_GROUP_SETS:
case ZFS_DELEG_GROUP:
if (g)
break;
}
(void) strlcpy(
nice_name, 256);
}
} else {
node = found_node;
}
}
}
return (0);
}
static inline int
{
uu_avl_index_t idx = 0;
nomem();
return (-1);
}
return (0);
}
static inline const char *
{
const char *str = "";
/* subcommands */
switch (note) {
/* SUBCOMMANDS */
case ZFS_DELEG_NOTE_ALLOW:
"\n\t\t\t\tallowed");
break;
case ZFS_DELEG_NOTE_CLONE:
"\n\t\t\t\tability in the origin file system");
break;
case ZFS_DELEG_NOTE_CREATE:
break;
case ZFS_DELEG_NOTE_DESTROY:
break;
case ZFS_DELEG_NOTE_DIFF:
"\n\t\t\t\tgiven an object number. Ordinary users need this"
"\n\t\t\t\tin order to use zfs diff");
break;
case ZFS_DELEG_NOTE_HOLD:
break;
case ZFS_DELEG_NOTE_MOUNT:
break;
case ZFS_DELEG_NOTE_PROMOTE:
" 'promote' ability in the origin file system");
break;
case ZFS_DELEG_NOTE_RECEIVE:
" ability");
break;
case ZFS_DELEG_NOTE_RELEASE:
"might destroy the snapshot");
break;
case ZFS_DELEG_NOTE_RENAME:
"\n\t\t\t\tability in the new parent");
break;
case ZFS_DELEG_NOTE_ROLLBACK:
break;
case ZFS_DELEG_NOTE_SEND:
break;
case ZFS_DELEG_NOTE_SHARE:
"\n\t\t\t\tprotocols");
break;
case ZFS_DELEG_NOTE_SNAPSHOT:
break;
/*
* case ZFS_DELEG_NOTE_VSCAN:
* str = gettext("");
* break;
*/
/* OTHER */
break;
case ZFS_DELEG_NOTE_GROUPUSED:
break;
case ZFS_DELEG_NOTE_USERPROP:
break;
case ZFS_DELEG_NOTE_USERQUOTA:
break;
case ZFS_DELEG_NOTE_USERUSED:
break;
/* other */
default:
str = "";
}
return (str);
}
struct allow_opts {
char *who;
char *perms;
const char *dataset;
};
static inline int
prop_cmp(const void *a, const void *b)
{
const char *str1 = *(const char **)a;
const char *str2 = *(const char **)b;
}
static void
{
const char *opt_desc[] = {
/* unallow only */
};
const char *props[ZFS_NUM_PROPS];
int i;
HELP_ALLOW));
}
"supported:\n\n"));
gettext("NOTES"));
for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) {
}
for (i = 0; i < ZFS_NUM_PROPS; i++) {
continue;
continue;
}
for (i = 0; i < count; i++)
}
static inline const char *
char **permsp)
{
else if (argc == expected_argc)
else
gettext("wrong number of parameters\n"));
}
static void
{
if (uge_sum > 1)
gettext("-u, -g, and -e are mutually exclusive\n"));
else
if (csuge_sum > 1)
gettext("invalid options combined with -s\n"));
if (argv[0][0] != '@')
gettext("invalid set name: missing '@' prefix\n"));
if (ldcsuge_sum > 1)
gettext("invalid options combined with -c\n"));
if (csuge_sum > 1)
gettext("invalid options combined with -e\n"));
== 0) {
argc--;
argv++;
} else {
}
}
}
static void
{
int i;
char base_type;
char set_type;
nomem();
nomem();
switch (type) {
case ZFS_DELEG_NAMED_SET_SETS:
case ZFS_DELEG_NAMED_SET:
ld[0] = ZFS_DELEG_NA;
break;
case ZFS_DELEG_CREATE_SETS:
case ZFS_DELEG_CREATE:
ld[0] = ZFS_DELEG_NA;
break;
case ZFS_DELEG_USER_SETS:
case ZFS_DELEG_USER:
if (local)
ld[0] = ZFS_DELEG_LOCAL;
if (descend)
break;
case ZFS_DELEG_GROUP_SETS:
case ZFS_DELEG_GROUP:
if (local)
ld[0] = ZFS_DELEG_LOCAL;
if (descend)
break;
case ZFS_DELEG_EVERYONE_SETS:
case ZFS_DELEG_EVERYONE:
if (local)
ld[0] = ZFS_DELEG_LOCAL;
if (descend)
}
else
*delim = '\0';
if (curr[0] == '@')
else
*delim = ',';
}
for (i = 0; i < 2; i++) {
if (locality == 0)
continue;
if (!nvlist_empty(base_nvl)) {
sizeof (who_buf), "%c%c$%s",
else
sizeof (who_buf), "%c%c$",
base_nvl);
}
if (!nvlist_empty(set_nvl)) {
sizeof (who_buf), "%c%c$%s",
else
sizeof (who_buf), "%c%c$",
set_nvl);
}
}
} else {
for (i = 0; i < 2; i++) {
if (locality == 0)
continue;
else
else
}
}
}
static int
{
nomem();
} else {
const char *who;
char *endch;
char errbuf[256];
char id[64];
else
*delim = '\0';
if (*endch != '\0')
else
if (p != NULL)
else {
"invalid user %s"), curr);
}
if (*endch != '\0')
else
if (g != NULL)
else {
"invalid group %s"), curr);
}
} else {
if (*endch != '\0') {
} else {
}
if (p == NULL)
if (*endch != '\0') {
} else {
}
if (p != NULL) {
} else if (g != NULL) {
} else {
}
}
}
}
return (0);
}
static void
{
const char *sc_title[] = {
gettext("Permission sets:\n"),
gettext("Create time permissions:\n"),
};
int prev_weight = -1;
if (prev_weight != weight) {
}
(void) printf("\t");
else
if (first) {
(void) printf("%s",
} else
(void) printf(",%s",
}
(void) printf("\n");
}
}
static void inline
const char *title)
{
nomem();
char delim = ' ';
deleg_node != NULL;
continue;
if (prt_who) {
if (prt_title) {
}
switch (who_type) {
case ZFS_DELEG_USER_SETS:
case ZFS_DELEG_USER:
if (nice_who_name)
break;
case ZFS_DELEG_GROUP_SETS:
case ZFS_DELEG_GROUP:
if (nice_who_name)
break;
case ZFS_DELEG_EVERYONE_SETS:
case ZFS_DELEG_EVERYONE:
}
else
}
delim = ',';
}
if (!prt_who)
(void) printf("\n");
}
}
static void
{
int left = 0;
gettext("---- Permissions on %s "),
while (left-- > 0)
(void) printf("-");
(void) printf("\n");
gettext("Local permissions:\n"));
gettext("Descendent permissions:\n"));
gettext("Local+Descendent permissions:\n"));
}
}
struct deleg_perms {
};
static int
{
return (0);
}
static int
{
int error = 1;
int c;
struct allow_opts opts = { 0 };
/* check opts */
switch (c) {
case 'l':
break;
case 'd':
break;
case 'u':
break;
case 'g':
break;
case 'e':
break;
case 's':
break;
case 'c':
break;
case 'r':
break;
case ':':
"'%c' option\n"), optopt);
break;
case 'h':
break;
case '?':
optopt);
}
}
/* check arguments */
/* try to open the dataset */
ZFS_TYPE_VOLUME)) == NULL) {
return (-1);
}
goto cleanup2;
goto cleanup1;
}
else {
goto cleanup0;
&data) != 0)
goto cleanup0;
}
}
error = 0;
if (update_perm_nvl != NULL)
return (error);
}
/*
* zfs allow [-r] [-t] <tag> <snap> ...
*
* -r Recursively hold
* -t Temporary hold (hidden option)
*
* Apply a user-hold with the given tag to the list of snapshots.
*/
static int
{
}
/*
* zfs unallow [-r] [-t] <tag> <snap> ...
*
* -r Recursively hold
* -t Temporary hold (hidden option)
*
* Apply a user-hold with the given tag to the list of snapshots.
*/
static int
{
}
static int
{
int errors = 0;
int i;
const char *tag;
int c;
/* check options */
switch (c) {
case 'r':
break;
case 't':
break;
case '?':
optopt);
}
}
/* check number of arguments */
if (argc < 2)
--argc;
++argv;
/* tags starting with '.' are reserved for libzfs */
}
for (i = 0; i < argc; ++i) {
char parent[ZFS_MAXNAMELEN];
const char *delim;
++errors;
continue;
}
++errors;
continue;
}
if (holding) {
++errors;
} else {
++errors;
}
}
return (errors != 0);
}
/*
* zfs hold [-r] [-t] <tag> <snap> ...
*
* -r Recursively hold
* -t Temporary hold (hidden option)
*
* Apply a user-hold with the given tag to the list of snapshots.
*/
static int
{
}
/*
* zfs release [-r] <tag> <snap> ...
*
* -r Recursively release
*
* Release a user-hold with the given tag from the list of snapshots.
*/
static int
{
}
typedef struct holds_cbdata {
const char *cb_snapname;
#define STRFTIME_FMT_STR "%a %b %e %k:%M %Y"
#define DATETIME_BUF_LEN (32)
/*
*
*/
static void
{
int i;
const char *col;
if (!scripted) {
for (i = 0; i < 3; i++) {
if (i < 2)
col);
else
}
}
char tsbuf[DATETIME_BUF_LEN];
struct tm t;
(void) localtime_r(&time, &t);
gettext(STRFTIME_FMT_STR), &t);
}
}
}
/*
* Generic callback function to list a dataset or snapshot.
*/
static int
{
if (cbp->cb_recursive) {
const char *snapname;
return (0);
return (0);
}
return (-1);
}
}
/*
* zfs holds [-r] <snap> ...
*
* -r Recursively hold
*/
static int
{
int errors = 0;
int c;
int i;
const char *opts = "rH";
int types = ZFS_TYPE_SNAPSHOT;
holds_cbdata_t cb = { 0 };
int limit = 0;
int ret = 0;
int flags = 0;
/* check options */
switch (c) {
case 'r':
break;
case 'H':
break;
case '?':
optopt);
}
}
if (recursive) {
}
/* check number of arguments */
if (argc < 1)
nomem();
for (i = 0; i < argc; ++i) {
const char *delim;
const char *snapname;
++errors;
continue;
}
if (recursive)
/*
* 1. collect holds data, set format options
*/
holds_callback, &cb);
if (ret != 0)
++errors;
}
/*
* 2. print holds data
*/
if (nvlist_empty(nvl))
return (0 != errors);
}
#define CHECK_SPINNER 30
static int
{
static int spinval = 0;
static int spincheck = 0;
if (cbp->cb_verbose) {
if (--spincheck < 0) {
}
}
}
/*
* Interate over any nested datasets.
*/
return (1);
}
/*
* Skip any datasets whose type does not match.
*/
if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
return (0);
}
return (0);
}
static void
{
get_all_cb_t cb = { 0 };
if (verbose)
if (verbose)
}
/*
* Generic callback for sharing or mounting filesystems. Because the code is so
* similar, we have a common function with an extra parameter to determine which
* mode we are using.
*/
#define OP_SHARE 0x1
#define OP_MOUNT 0x2
/*
* Share or mount a dataset.
*/
static int
{
char mountpoint[ZFS_MAXPROPLEN];
char shareopts[ZFS_MAXPROPLEN];
char smbshareopts[ZFS_MAXPROPLEN];
/*
* are in the global zone and the filesystem is exported to a
* local zone, or if we are in a local zone and the
* filesystem is not exported, then it is an error.
*/
if (!explicit)
return (0);
"dataset is exported to a local zone\n"), cmdname,
zfs_get_name(zhp));
return (1);
if (!explicit)
return (0);
"permission denied\n"), cmdname,
zfs_get_name(zhp));
return (1);
}
/*
* Ignore any filesystems which don't apply to us. This
* includes those with a legacy mountpoint, or those with
* legacy share options.
*/
if (!explicit)
return (0);
"share this filesystem, or set "
"sharenfs property on\n"));
return (1);
}
/*
* We cannot share or mount legacy filesystems. If the
* shareopts is non-legacy but the mountpoint is legacy, we
* treat it as a legacy share.
*/
if (!explicit)
return (0);
return (1);
}
if (!explicit)
return (0);
return (1);
}
/*
* canmount explicit outcome
* on no pass through
* on yes pass through
* off no return 0
* off yes display error, return 1
* noauto no return 0
* noauto yes pass through
*/
if (canmount == ZFS_CANMOUNT_OFF) {
if (!explicit)
return (0);
"'canmount' property is set to 'off'\n"), cmdname,
zfs_get_name(zhp));
return (1);
return (0);
}
/*
* shareopts are appropriate for auto management. If the
* filesystem is already mounted or shared, return (failing
* for explicit requests); otherwise mount or share the
* filesystem.
*/
switch (op) {
case OP_SHARE:
if (shared_nfs && shared_smb ||
if (!explicit)
return (0);
"'%s': filesystem already shared\n"),
zfs_get_name(zhp));
return (1);
}
return (1);
if (zfs_shareall(zhp) != 0)
return (1);
if (zfs_share_nfs(zhp))
return (1);
if (zfs_share_smb(zhp))
return (1);
} else {
"'%s': invalid share type '%s' "
"specified\n"),
return (1);
}
break;
case OP_MOUNT:
else
if (!explicit)
return (0);
"'%s': filesystem already mounted\n"),
zfs_get_name(zhp));
return (1);
}
return (1);
break;
}
return (0);
}
/*
*/
static void
{
static time_t last_progress_time = 0;
char info[32];
/* report 1..n instead of 0..n-1 */
++current;
/* display header if we're here for the first time */
if (current == 1) {
/* too soon to report again */
return;
}
else
}
static void
{
/* original length plus new string to append plus 1 for the comma */
"'%c' option is too long (more than %d chars)\n"),
"-o", MNT_LINE_MAX);
}
if (*mntopts)
}
static int
{
int do_all = 0;
int c, ret = 0;
int flags = 0;
/* check options */
!= -1) {
switch (c) {
case 'a':
do_all = 1;
break;
case 'v':
break;
case 'o':
if (*optarg == '\0') {
"options (-o) specified\n"));
}
/* option validation is done later */
break;
case 'O':
flags |= MS_OVERLAY;
break;
case ':':
"'%c' option\n"), optopt);
break;
case '?':
optopt);
}
}
/* check number of arguments */
if (do_all) {
"must be 'nfs' or 'smb'\n"));
}
argc--;
argv++;
}
if (argc != 0) {
}
if (count == 0)
return (0);
for (i = 0; i < count; i++) {
if (verbose)
ret = 1;
}
} else if (argc == 0) {
"argument (specify -a for all)\n"));
}
/*
* display any active ZFS mounts. We hide any snapshots, since
* they are controlled automatically.
*/
continue;
}
} else {
if (argc > 1) {
gettext("too many arguments\n"));
}
ZFS_TYPE_FILESYSTEM)) == NULL) {
ret = 1;
} else {
options);
}
}
return (ret);
}
/*
* zfs mount -a [nfs]
* zfs mount filesystem
*
* Mount all filesystems, or mount the given filesystem.
*/
static int
{
}
/*
* zfs share -a [nfs | smb]
* zfs share filesystem
*
* Share all filesystems, or share the given filesystem.
*/
static int
{
}
typedef struct unshare_unmount_node {
char *un_mountp;
/* ARGSUSED */
static int
{
const unshare_unmount_node_t *l = larg;
const unshare_unmount_node_t *r = rarg;
}
/*
* Convenience routine used by zfs_do_umount() and manual_unmount(). Given an
* and unmount it appropriately.
*/
static int
{
int ret = 0;
/*
* specific path, which can be fooled by non-standard paths (i.e. ".."
* or "//"), we stat() the path and search for the corresponding
* (major,minor) device pair.
*/
return (1);
}
/*
* Search for the given (major,minor) pair in the mount table.
*/
break;
}
if (ret != 0) {
return (1);
}
path);
return (ret != 0);
}
return (1);
}
ZFS_TYPE_FILESYSTEM)) == NULL)
return (1);
ret = 1;
goto out;
goto out;
}
char nfs_mnt_prop[ZFS_MAXPROPLEN];
char smbshare_prop[ZFS_MAXPROPLEN];
"'%s': legacy share\n"), path);
"unshare(1M) to unshare this filesystem\n"));
} else if (!zfs_is_shared(zhp)) {
"not currently shared\n"), path);
} else {
}
} else {
char mtpt_prop[ZFS_MAXPROPLEN];
if (is_manual) {
"'%s': legacy mountpoint\n"),
zfs_get_name(zhp));
"to unmount this filesystem\n"));
} else {
}
}
out:
return (ret != 0);
}
/*
* Generic callback for unsharing or unmounting a filesystem.
*/
static int
{
int do_all = 0;
int flags = 0;
int ret = 0;
int c;
char nfs_mnt_prop[ZFS_MAXPROPLEN];
char sharesmb[ZFS_MAXPROPLEN];
/* check options */
switch (c) {
case 'a':
do_all = 1;
break;
case 'f':
break;
case '?':
optopt);
}
}
if (do_all) {
/*
* We could make use of zfs_for_each() to walk all datasets in
* the system, but this would be very inefficient, especially
* zfs entries and call zfs_unmount() for each one.
*
* Things get a little tricky if the administrator has created
* mountpoints beneath other ZFS filesystems. In this case, we
* have to unmount the deepest filesystems first. To accomplish
* this, we place all the mountpoints in an AVL tree sorted by
* the special type (dataset name), and walk the result in
* reverse to make sure to get any snapshots first.
*/
if (argc != 0) {
}
sizeof (unshare_unmount_node_t),
nomem();
/* ignore non-ZFS entries */
continue;
/* ignore snapshots */
continue;
ZFS_TYPE_FILESYSTEM)) == NULL) {
ret = 1;
continue;
}
switch (op) {
case OP_SHARE:
sizeof (nfs_mnt_prop),
break;
sizeof (nfs_mnt_prop),
continue;
break;
case OP_MOUNT:
/* Ignore legacy mounts */
sizeof (nfs_mnt_prop),
continue;
/* Ignore canmount=noauto mounts */
continue;
default:
break;
}
} else {
}
}
/*
* Walk the AVL tree in reverse, unmounting each filesystem and
* removing it from the AVL tree in the process.
*/
nomem();
switch (op) {
case OP_SHARE:
ret = 1;
break;
case OP_MOUNT:
ret = 1;
break;
}
}
} else {
if (argc != 1) {
if (argc == 0)
gettext("missing filesystem argument\n"));
else
gettext("too many arguments\n"));
}
/*
* We have an argument, but it may be a full path or a ZFS
* filesystem. Pass full paths off to unmount_path() (shared by
* manual_unmount), otherwise open the filesystem and pass to
* zfs_unmount().
*/
if (argv[0][0] == '/')
ZFS_TYPE_FILESYSTEM)) == NULL)
return (1);
switch (op) {
case OP_SHARE:
sizeof (nfs_mnt_prop),
0, B_FALSE) == 0);
"unshare '%s': legacy share\n"),
zfs_get_name(zhp));
"unshare(1M) to unshare this "
"filesystem\n"));
ret = 1;
} else if (!zfs_is_shared(zhp)) {
"unshare '%s': not currently "
ret = 1;
} else if (zfs_unshareall(zhp) != 0) {
ret = 1;
}
break;
case OP_MOUNT:
"unmount '%s': legacy "
"umount(1M) to unmount this "
"filesystem\n"));
ret = 1;
"unmount '%s': not currently "
"mounted\n"),
zfs_get_name(zhp));
ret = 1;
ret = 1;
}
break;
}
}
return (ret);
}
/*
* zfs unmount -a
* zfs unmount filesystem
*
* Unmount all filesystems, or a specific ZFS filesystem.
*/
static int
{
}
/*
* zfs unshare -a
* zfs unshare filesystem
*
* Unshare all filesystems, or a specific ZFS filesystem.
*/
static int
{
}
/*
* 'legacy'. Otherwise, complain that use should be using 'zfs mount'.
*/
static int
{
char mountpoint[ZFS_MAXPROPLEN];
int ret = 0;
int c;
int flags = 0;
/* check options */
switch (c) {
case 'o':
break;
case 'O':
flags |= MS_OVERLAY;
break;
case 'm':
flags |= MS_NOMNTTAB;
break;
case ':':
"'%c' option\n"), optopt);
break;
case '?':
optopt);
"<path>\n"));
return (2);
}
}
/* check that we only have two arguments */
if (argc != 2) {
if (argc == 0)
"argument\n"));
else if (argc == 1)
gettext("missing mountpoint argument\n"));
else
return (2);
}
/* try to open the dataset */
return (1);
/* check for legacy mountpoint and complain appropriately */
ret = 0;
ret = 1;
}
} else {
"mounted using 'mount -F zfs'\n"), dataset);
"instead.\n"), path);
"information.\n"));
ret = 1;
}
return (ret);
}
/*
* unmounts of non-legacy filesystems, as this is the dominant administrative
* interface.
*/
static int
{
int flags = 0;
int c;
/* check options */
switch (c) {
case 'f':
break;
case '?':
optopt);
"<path>\n"));
return (2);
}
}
/* check arguments */
if (argc != 1) {
if (argc == 0)
"argument\n"));
else
return (2);
}
}
static int
{
int i;
for (i = 0; i < NCOMMAND; i++) {
continue;
*idx = i;
return (0);
}
}
return (1);
}
static int
{
int flags = 0;
int err = 0;
int c;
switch (c) {
case 'F':
break;
case 'H':
break;
case 't':
break;
default:
}
}
if (argc < 1) {
gettext("must provide at least one snapshot name\n"));
}
if (argc > 2) {
}
if (*fromsnap != '@')
else if (tosnap)
*atp = '\0';
return (1);
/*
* Ignore SIGPIPE so that the library can give us
* information on any failure
*/
return (err != 0);
}
int
{
int ret = 0;
int i;
char *progname;
char *cmdname;
(void) textdomain(TEXT_DOMAIN);
opterr = 0;
"initialize ZFS library\n"));
return (1);
}
"open %s\n"), MNTTAB);
return (1);
}
/*
* Determine if we should take this behavior based on argv[0].
*/
} else {
/*
* Make sure the user has specified some command.
*/
if (argc < 2) {
}
/*
* The 'umount' command is an alias for 'unmount'
*/
cmdname = "unmount";
/*
* The 'recv' command is an alias for 'receive'
*/
cmdname = "receive";
/*
* Special case '-?'
*/
/*
* Run the appropriate command.
*/
if (find_command_idx(cmdname, &i) == 0) {
current_command = &command_table[i];
current_command = &command_table[i];
} else {
"command '%s'\n"), cmdname);
}
}
(void) fclose(mnttab_file);
if (ret == 0 && log_history)
/*
* The 'ZFS_ABORT' environment variable causes us to dump core on exit
* for the purposes of running ::findleaks.
*/
(void) printf("dumping core by request\n");
abort();
}
return (ret);
}