zfs_main.c revision a9b821a05317e0a13944933cac8976e203c08991
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#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 <libzfs.h>
#include <libuutil.h>
#include "zfs_iter.h"
#include "zfs_util.h"
static FILE *mnttab_file;
static char history_str[HIS_MAX_RECORD_LEN];
/*
* These libumem hooks provide a reasonable set of defaults for the allocator's
* debugging facilities.
*/
const char *
_umem_debug_init(void)
{
return ("default,verbose"); /* $UMEM_DEBUG setting */
}
const char *
_umem_logging_init(void)
{
return ("fail,contents"); /* $UMEM_LOGGING setting */
}
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 },
};
static const char *
{
switch (idx) {
case HELP_CLONE:
return (gettext("\tclone [-p] <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 [-rRf] "
"<filesystem|volume|snapshot>\n"));
case HELP_GET:
return (gettext("\tget [-rHp] [-o field[,...]] "
"[-s source[,...]]\n"
"\t <\"all\" | property[,...]> "
"[filesystem|volume|snapshot] ...\n"));
case HELP_INHERIT:
return (gettext("\tinherit [-r] <property> "
"<filesystem|volume> ...\n"));
case HELP_UPGRADE:
return (gettext("\tupgrade [-v]\n"
"\tupgrade [-r] [-V version] <-a | filesystem ...>\n"));
case HELP_LIST:
return (gettext("\tlist [-rH] [-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 [-vnF] <filesystem|volume|"
"snapshot>\n"
"\treceive [-vnF] -d <filesystem>\n"));
case HELP_RENAME:
return (gettext("\trename <filesystem|volume|snapshot> "
"<filesystem|volume|snapshot>\n"
"\trename -p <filesystem|volume> <filesystem|volume>\n"
"\trename -r <snapshot> <snapshot>"));
case HELP_ROLLBACK:
return (gettext("\trollback [-rR] <snapshot>\n"));
case HELP_SEND:
return (gettext("\tsend [-R] [-[iI] snapshot] <snapshot>\n"));
case HELP_SET:
return (gettext("\tset <property=value> "
"<filesystem|volume> ...\n"));
case HELP_SHARE:
return (gettext("\tshare <-a | filesystem>\n"));
case HELP_SNAPSHOT:
return (gettext("\tsnapshot [-r] "
"<filesystem@snapname|volume@snapname>\n"));
case HELP_UNMOUNT:
return (gettext("\tunmount [-f] "
"<-a | filesystem|mountpoint>\n"));
case HELP_UNSHARE:
return (gettext("\tunshare [-f] "
"<-a | filesystem|mountpoint>\n"));
case HELP_ALLOW:
return (gettext("\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"));
}
abort();
/* NOTREACHED */
}
/*
* Utility function to guarantee malloc() success.
*/
void *
{
void *data;
exit(1);
}
return (data);
}
/*
* Callback routine that will print out information for each of
* the properties.
*/
static int
{
if (prop == ZFS_PROP_CASE)
else 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"));
} else {
/*
* TRANSLATION NOTE:
* "zfs set|get" must not be localised this is the
* command name and arguments.
*/
gettext("\nFor the property list, run: zfs set|get\n"));
}
/*
* See comments at end of main().
*/
(void) printf("dumping core by request\n");
abort();
}
}
/*
* zfs clone [-p] <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;
int c;
/* check options */
switch (c) {
case 'p':
break;
case '?':
optopt);
}
}
/* check number of arguments */
if (argc < 1) {
"argument\n"));
}
if (argc < 2) {
"argument\n"));
}
if (argc > 2) {
}
/* 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 == 0 ? 0 : 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;
char *propname;
char *strval;
"out of memory\n"));
return (1);
}
/* check options */
switch (c) {
case 'V':
"size '%s': %s\n"), optarg,
goto error;
}
if (nvlist_add_uint64(props,
intval) != 0) {
"error: out of memory\n"));
goto error;
}
break;
case 'p':
break;
case 'b':
"block size '%s': %s\n"), optarg,
goto error;
}
if (nvlist_add_uint64(props,
intval) != 0) {
"error: out of memory\n"));
goto error;
}
break;
case 'o':
"'=' for -o option\n"));
goto error;
}
*propval = '\0';
propval++;
&strval) == 0) {
"specified multiple times\n"), propname);
goto error;
}
"error: out of memory\n"));
goto error;
}
break;
case 's':
break;
case ':':
"argument\n"));
goto badusage;
break;
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;
*p = '\0';
if (p != NULL)
*p = '/';
if (zpool_handle == NULL)
goto error;
else
&strval) != 0) {
if (nvlist_add_uint64(props,
"error: out of memory\n"));
return (1);
}
}
}
/*
* 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;
/*
* verbose error message to let the user know that their filesystem was
* in fact created, even if we failed to mount or share it.
*/
"created, but not mounted\n"));
ret = 1;
"created, but not shared\n"));
ret = 1;
} else {
ret = 0;
}
if (zhp)
return (ret);
return (2);
}
/*
* zfs destroy [-rf] <fs, snap, vol>
*
* -r Recursively destroy all children
* -R Recursively destroy all dependents, including clones
* -f Force unmounting of any dependents
*
* 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 {
int cb_force;
int cb_recurse;
int cb_error;
int cb_needforce;
int cb_doclones;
char *cb_snapname;
/*
* 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
{
/*
* Ignore pools (which we've already flagged as an error before getting
* here.
*/
return (0);
}
/*
* Bail out on the first error.
*/
zfs_destroy(zhp) != 0) {
return (-1);
}
return (0);
}
static int
{
char thissnap[MAXPATHLEN];
int rv;
if (szhp) {
/*
* Destroy any clones of this snapshot
*/
cbp) != 0) {
if (closezhp)
return (-1);
}
}
if (closezhp)
return (rv);
}
static int
{
destroy_cbdata_t cb = { 0 };
int c;
char *cp;
/* check options */
switch (c) {
case 'f':
break;
case 'r':
break;
case 'R':
break;
case '?':
default:
optopt);
}
}
/* check number of arguments */
if (argc == 0) {
}
if (argc > 1) {
}
/*
* If we are doing recursive destroy of a snapshot, then the
* named snapshot may not exist. Go straight to libzfs.
*/
int ret;
*cp = '\0';
return (1);
*cp = '@';
cp++;
if (cb.cb_doclones) {
return (1);
}
}
if (ret) {
gettext("no snapshots destroyed\n"));
}
return (ret != 0);
}
/* 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);
}
/*
* Do the real thing. The callback will close the handle regardless of
* whether it succeeds or not.
*/
return (1);
return (0);
}
/*
* zfs get [-rHp] [-o 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,source".
* Default is all four.
* -s Set of sources to allow. One of
* "local,default,inherited,temporary,none". Default is all
* five.
* -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 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;
}
}
} else {
continue;
strval = "-";
} else {
ZPROP_VALUE, &strval) == 0);
ZPROP_SOURCE, &sourceval) == 0);
zfs_get_name(zhp)) == 0) {
} else {
}
}
source);
}
}
return (0);
}
static int
{
zprop_get_cbdata_t cb = { 0 };
int i, c;
int ret;
zprop_list_t fake_name = { 0 };
/*
* Set up default columns and sources.
*/
/* check options */
switch (c) {
case 'p':
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", "source",
NULL };
if (i == 4) {
"many fields given to -o "
"option\n"));
}
&value)) {
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
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",
&value)) {
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
default:
gettext("invalid source "
"'%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 [-r] <property> <fs|vol> ...
*
* -r Recurse over all children
*
* 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.
*/
static int
{
int ret;
return (ret != 0);
}
static int
{
int c;
char *propname;
int ret;
/* check options */
switch (c) {
case 'r':
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);
}
if (!zfs_prop_inheritable(prop)) {
"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);
}
} else if (!zfs_prop_user(propname)) {
propname);
}
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
{
char pool_name[MAXPATHLEN];
int spa_version;
char *p;
return (-1);
*p = '\0';
return (-1);
if (spa_version < SPA_VERSION_FUID) {
/* can't upgrade */
"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.
*/
}
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;
upgrade_cbdata_t cb = { 0 };
char c;
/* 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");
"unique identifer (FUID)\n"));
"version, including supported releases, see:\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);
}
/*
* list [-rH] [-o property[,property]...] [-t type[,type]...]
* [-s property [-s property]...] [-S property [-S property]...]
* <dataset> ...
*
* -r Recurse over all children
* -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
} 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;
char *basic_fields = default_fields;
list_cbdata_t cb = { 0 };
char *value;
int ret;
/* check options */
switch (c) {
case 'o':
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') {
&value)) {
case 0:
break;
case 1:
types |= ZFS_TYPE_VOLUME;
break;
case 2:
break;
default:
gettext("invalid type '%s'\n"),
value);
}
}
break;
case ':':
"'%c' option\n"), optopt);
break;
case '?':
optopt);
}
}
/*
* 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 <fs | snap | vol> <fs | snap | vol>
* zfs rename -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;
/* check options */
switch (c) {
case 'p':
break;
case 'r':
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;
/* check options */
}
/* check number of arguments */
if (argc < 2) {
" argument\n"));
}
if (argc > 2) {
}
return (1);
return (ret);
}
/*
* zfs rollback [-rR] <snapshot>
*
* -r Delete any intervening snapshots before doing rollback
* -R Delete any snapshots and their clones
*
* 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;
int c;
rollback_cbdata_t cb = { 0 };
char parentname[ZFS_MAXNAMELEN];
char *delim;
/* check options */
switch (c) {
case 'r':
break;
case 'R':
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;
/* 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);
}
/*
* zfs snapshot [-r] <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;
char c;
/* check options */
switch (c) {
case 'r':
break;
case '?':
optopt);
}
}
/* check number of arguments */
if (argc < 1) {
}
if (argc > 1) {
}
return (ret != 0);
}
/*
* zfs send [-v] -R [-i|-I <@snap>] <fs@snap>
* zfs send [-v] [-i|-I <@snap>] <fs@snap>
*
* Send a backup stream to stdout.
*/
static int
{
char *cp;
int c, err;
/* check options */
switch (c) {
case 'i':
if (fromname)
break;
case 'I':
if (fromname)
break;
case 'R':
break;
case 'v':
break;
case ':':
"'%c' option\n"), optopt);
break;
case '?':
optopt);
}
}
/* check number of arguments */
if (argc < 1) {
}
if (argc > 1) {
}
if (isatty(STDOUT_FILENO)) {
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];
fromorigin = B_TRUE;
} else {
*cp = '\0';
gettext("incremental source must be "
"in same filesystem\n"));
}
gettext("invalid incremental source\n"));
}
}
}
return (err != 0);
}
/*
* zfs receive [-dnvF] <fs@snap>
*
* Restore a backup stream from stdin.
*/
static int
{
int c, err;
/* check options */
switch (c) {
case 'd':
break;
case 'n':
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);
}
typedef struct allow_cb {
int a_permcnt;
} allow_cb_t;
static void
{
if (permnode)
(void) printf(",");
else
(void) printf("\n");
}
}
/*
* function to print actual permission when tree has >0 nodes.
*/
static void
{
while (item) {
if (avl_numnodes(ptree)) {
/*
* Avoid an extra space being printed
* for "everyone" which is keyed with a null
* string
*/
(void) printf(" ");
}
}
}
#define LINES "-------------------------------------------------------------\n"
static int
zfs_print_allows(char *ds)
{
allow_cb_t allowcb = { 0 };
char banner[MAXPATHLEN];
if (ds[0] == '-')
" permissions\n"));
return (1);
}
return (1);
return (1);
}
"Local+Descendent permissions on (%s)",
}
return (0);
}
#define ALLOWOPTIONS "ldcsu:g:e"
#define UNALLOWOPTIONS "ldcsu:g:er"
/*
* permissions.
* Returns 1 - If permissions should be displayed.
* Returns -1 - on failure
*/
int
{
int c;
switch (c) {
case 'l':
if (who_type == ZFS_DELEG_CREATE ||
break;
case 'd':
if (who_type == ZFS_DELEG_CREATE ||
break;
case 'r':
break;
case 'c':
if (who_type != ZFS_DELEG_WHO_UNKNOWN)
if (deleg_type)
break;
case 's':
if (who_type != ZFS_DELEG_WHO_UNKNOWN)
if (deleg_type)
break;
case 'u':
if (who_type != ZFS_DELEG_WHO_UNKNOWN)
break;
case 'g':
if (who_type != ZFS_DELEG_WHO_UNKNOWN)
break;
case 'e':
if (who_type != ZFS_DELEG_WHO_UNKNOWN)
break;
default:
break;
}
}
if (deleg_type == 0)
/*
* Only print permissions if no options were processed
*/
if (optind == 1)
return (1);
else
}
/*
* initialize variables for zfs_build_perms based on number
* of arguments.
* 3 arguments ==> zfs [un]allow joe perm,perm,perm <dataset> or
* zfs [un]allow -s @set1 perm,perm <dataset>
* 2 arguments ==> zfs [un]allow -c perm,perm <dataset> or
* zfs [un]allow -u|-g <name> perm <dataset> or
* zfs [un]allow -e perm,perm <dataset>
* zfs unallow joe <dataset>
* zfs unallow -s @set1 <dataset>
* 1 argument ==> zfs [un]allow -e <dataset> or
* zfs [un]allow -c <dataset>
*/
switch (*argc) {
case 3:
/*
* for do_allow case make sure who have a know who type
* and its not a permission set.
*/
*argc -= 2;
*argv += 2;
} else if (who_type != ZFS_DELEG_WHO_UNKNOWN &&
break;
case 2:
} else {
(who_type == ZFS_DELEG_WHO_UNKNOWN ||
else if (who_type == ZFS_DELEG_WHO_UNKNOWN ||
else if (who_type != ZFS_DELEG_NAMED_SET)
}
(*argc)--;
(*argv)++;
}
break;
case 1:
break;
default:
}
gettext("Can't set or remove 'allow' permissions "
"on snapshots.\n"));
return (-1);
}
return (-1);
return (-1);
}
return (0);
}
static int
{
char *ds;
int unused;
int ret;
return (1);
if (ret == 1)
return (zfs_print_allows(argv[0]));
return (1);
return (1);
}
return (0);
}
static int
{
int error;
if (error) {
}
return (error);
}
static int
{
char *ds;
int error;
return (1);
if (zperms)
return (error);
}
typedef struct get_all_cbdata {
#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.
*/
if (type == ZFS_TYPE_FILESYSTEM &&
return (1);
}
/*
* Skip any datasets whose type does not match.
*/
return (0);
}
else
if (cbp->cb_handles) {
}
}
return (0);
}
static void
{
get_all_cbdata_t cb = { 0 };
if (verbose) {
}
if (verbose) {
}
}
static int
dataset_cmp(const void *a, const void *b)
{
char mounta[MAXPATHLEN];
char mountb[MAXPATHLEN];
if (gota)
return (-1);
if (gotb)
return (1);
}
/*
* 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];
if (type == ZFS_TYPE_FILESYSTEM) {
/*
* 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\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);
}
if (!canmount) {
if (!explicit)
return (0);
"'canmount' property is set to 'off'\n"), cmdname,
zfs_get_name(zhp));
return (1);
}
/*
* 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;
}
} else {
/*
* Ignore any volumes that aren't shared.
*/
if (!explicit)
return (0);
"'shareiscsi' property not set\n"),
zfs_get_name(zhp));
"property or use iscsitadm(1M) to share this "
"volume\n"));
return (1);
}
if (zfs_is_shared_iscsi(zhp)) {
if (!explicit)
return (0);
"'%s': volume already shared\n"),
zfs_get_name(zhp));
return (1);
}
if (zfs_share_iscsi(zhp) != 0)
return (1);
}
return (0);
}
/*
*/
static void
{
static int len;
static char *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\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
static time_t last_progress_time;
/* report 1..n instead of 0..n-1 */
++current;
/* display header if we're here for the first time */
if (current == 1) {
len = 0;
/* too soon to report again */
return;
}
/* back up to prepare for overwriting */
if (len)
/* We put a newline at the end if this is the last one. */
}
static int
{
int do_all = 0;
int c, ret = 0;
/* check options */
!= -1) {
switch (c) {
case 'a':
do_all = 1;
break;
case 'v':
break;
case 'o':
break;
}
"'%c' option is too long (more than %d chars)\n"),
break;
case 'O':
flags |= MS_OVERLAY;
break;
case ':':
"'%c' option\n"), optopt);
break;
case '?':
optopt);
}
}
/* check number of arguments */
if (do_all) {
} else if (argc > 0) {
} else {
"must be 'nfs', 'smb' or 'iscsi'\n"));
}
argc--;
argv++;
} else {
}
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 {
types |= ZFS_TYPE_VOLUME;
if (argc > 1) {
gettext("too many arguments\n"));
}
ret = 1;
} else {
options);
}
}
return (ret);
}
/*
* zfs mount -a [nfs | iscsi]
* zfs mount filesystem
*
* Mount all filesystems, or mount the given filesystem.
*/
static int
{
}
/*
* zfs share -a [nfs | iscsi | 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;
char nfs_mnt_prop[ZFS_MAXPROPLEN];
char smbshare_prop[ZFS_MAXPROPLEN];
/*
* 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);
}
return (1);
}
ZFS_TYPE_FILESYSTEM)) == NULL)
return (1);
"'%s': legacy share\n"), path);
"unshare(1M) to unshare this filesystem\n"));
ret = 1;
} else if (!zfs_is_shared(zhp)) {
"not currently shared\n"), path);
ret = 1;
} else {
}
} else {
if (is_manual) {
"'%s': legacy mountpoint\n"),
zfs_get_name(zhp));
"to unmount this filesystem\n"));
ret = 1;
} else {
}
}
return (ret != 0);
}
/*
* Generic callback for unsharing or unmounting a filesystem.
*/
static int
{
int do_all = 0;
int flags = 0;
int ret = 0;
int types, c;
char nfsiscsi_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),
UU_DEFAULT)) == NULL) {
"out of memory\n"));
exit(1);
}
"out of memory\n"));
exit(1);
}
/* ignore non-ZFS entries */
continue;
/* ignore snapshots */
continue;
ZFS_TYPE_FILESYSTEM)) == NULL) {
ret = 1;
continue;
}
switch (op) {
case OP_SHARE:
sizeof (nfsiscsi_mnt_prop),
break;
sizeof (nfsiscsi_mnt_prop),
continue;
break;
case OP_MOUNT:
/* Ignore legacy mounts */
sizeof (nfsiscsi_mnt_prop),
continue;
default:
break;
}
NULL) {
" out of memory\n"));
exit(1);
}
} else {
}
}
/*
* Walk the AVL tree in reverse, unmounting each filesystem and
* removing it from the AVL tree in the process.
*/
gettext("internal error: out of memory"));
exit(1);
}
switch (op) {
case OP_SHARE:
ret = 1;
break;
case OP_MOUNT:
ret = 1;
break;
}
}
/*
* Finally, unshare any volumes shared via iSCSI.
*/
B_FALSE);
if (count != 0) {
for (i = 0; i < count; i++) {
if (zfs_unshare_iscsi(dslist[i]) != 0)
ret = 1;
}
}
}
} 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] == '/')
types |= ZFS_TYPE_VOLUME;
return (1);
switch (op) {
case OP_SHARE:
sizeof (nfsiscsi_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;
}
} else {
nfsiscsi_mnt_prop, sizeof (nfsiscsi_mnt_prop),
"'%s': 'shareiscsi' property not set\n"),
zfs_get_name(zhp));
"'shareiscsi' property or use "
"iscsitadm(1M) to share this volume\n"));
ret = 1;
} else if (!zfs_is_shared_iscsi(zhp)) {
"unshare '%s': not currently shared\n"),
zfs_get_name(zhp));
ret = 1;
} else if (zfs_unshare_iscsi(zhp) != 0) {
ret = 1;
}
}
}
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;
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
{
if (isinit)
return (zpool_create_zvol_links(zhp));
else
return (zpool_remove_zvol_links(zhp));
}
/*
* links, depending on the value of 'isinit'.
*/
static int
{
}
static int
{
int i;
for (i = 0; i < NCOMMAND; i++) {
continue;
*idx = i;
return (0);
}
}
return (1);
}
int
{
int ret;
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 '-?'
*/
/*
* 'volinit' and 'volfini' do not appear in the usage message,
* so we have to special case them here.
*/
return (do_volcheck(B_TRUE));
return (do_volcheck(B_FALSE));
/*
* 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);
/*
* 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);
}