zfs_main.c revision 07ba041951d9c8107e1fde609b578608d2f08e24
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <assert.h>
#include <errno.h>
#include <libgen.h>
#include <libintl.h>
#include <libuutil.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 "zfs_iter.h"
static FILE *mnttab_file;
/*
* These libumem hooks provide a reasonable set of defaults for the allocator's
* debugging facilities.
*/
const char *
{
return ("default,verbose"); /* $UMEM_DEBUG setting */
}
const char *
_umem_logging_init(void)
{
return ("fail,contents"); /* $UMEM_LOGGING setting */
}
typedef struct zfs_command {
const char *name;
const char *usage;
/*
* Master command table. Each ZFS command has a name, associated function, and
* usage message. 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. A command with a NULL
* usage message indicates an alias for an existing command, and is not
* displayed in the general usage message.
*/
static zfs_command_t command_table[] = {
{ "create", zfs_do_create,
"\tcreate <filesystem>\n"
"\tcreate [-s] [-b blocksize] -V <size> <volume>\n" },
{ "destroy", zfs_do_destroy,
"\tdestroy [-rRf] <filesystem|volume|snapshot>\n" },
{ NULL },
{ "snapshot", zfs_do_snapshot,
"\tsnapshot <filesystem@name|volume@name>\n" },
{ "rollback", zfs_do_rollback,
"\trollback [-rRf] <snapshot>\n" },
{ "clone", zfs_do_clone,
"\tclone <snapshot> <filesystem|volume>\n" },
{ "rename", zfs_do_rename,
"\trename <filesystems|volume|snapshot> "
"<filesystem|volume|snapshot>\n" },
{ NULL },
{ "list", zfs_do_list,
"\tlist [-rH] [-o property[,property]...] [-t type[,type]...]\n"
"\t [filesystem|volume|snapshot] ...\n" },
{ NULL },
{ "set", zfs_do_set,
"\tset <property=value> <filesystem|volume> ...\n" },
{ "get", zfs_do_get,
"\tget [-rHp] [-o field[,field]...] [-s source[,source]...]\n"
"\t <all | property[,property]...> "
"<filesystem|volume|snapshot> ...\n" },
{ "inherit", zfs_do_inherit,
"\tinherit [-r] <property> <filesystem|volume> ...\n" },
{ NULL },
{ "mount", zfs_do_mount,
"\tmount\n"
"\tmount [-o opts] [-O] -a\n"
"\tmount [-o opts] [-O] <filesystem>\n" },
{ NULL },
{ "unmount", zfs_do_unmount,
"\tunmount [-f] -a\n"
"\tunmount [-f] <filesystem|mountpoint>\n" },
{ NULL },
{ "share", zfs_do_share,
"\tshare -a\n"
"\tshare <filesystem>\n" },
{ NULL },
{ "unshare", zfs_do_unshare,
"\tunshare [-f] -a\n"
"\tunshare [-f] <filesystem|mountpoint>\n" },
{ NULL },
{ "backup", zfs_do_backup,
"\tbackup [-i <snapshot>] <snapshot>\n" },
{ "restore", zfs_do_restore,
"\trestore [-vn] <filesystem|volume|snapshot>\n"
"\trestore [-vn] -d <filesystem>\n" },
};
/*
* Utility function to guarantee malloc() success.
*/
void *
{
void *data;
exit(1);
}
return (data);
}
/*
* 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;
int show_properties = FALSE;
if (current_command == NULL) {
gettext("where 'command' is one of the following:\n\n"));
for (i = 0; i < NCOMMAND; i++) {
else
command_table[i].usage);
}
"pool/[dataset/]*dataset[@name]\n"));
} else {
}
if (current_command == NULL ||
if (show_properties) {
gettext("\nThe following properties are supported:\n"));
"PROPERTY", "EDIT", "INHERIT", "VALUES");
for (i = 0; i < ZFS_NPROP_VISIBLE; i++) {
if (zfs_prop_readonly(i))
else
if (zfs_prop_inheritable(i))
else
if (zfs_prop_values(i) == NULL)
else
}
"with standard units such as K, M, G, etc.\n"));
}
}
/*
* zfs clone <fs, snap, vol> fs
*
* 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.
*/
static int
{
int ret;
/* check options */
}
/* check number of arguments */
if (argc < 2) {
"argument\n"));
}
if (argc < 3) {
"argument\n"));
}
if (argc > 3) {
}
/* open the source dataset */
return (1);
/* pass to libzfs */
/* create the mountpoint if necessary */
if (ret == 0) {
}
}
return (ret == 0 ? 0 : 1);
}
/*
* zfs create fs
* zfs create [-s] -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.
*/
static int
{
int c;
int ret;
/* check options */
switch (c) {
case 'V':
break;
case 'b':
break;
case 's':
break;
case ':':
"argument\n"));
break;
case '?':
optopt);
}
}
"creating a volume\n"));
}
/* check number of arguments */
if (argc == 0) {
}
if (argc > 1) {
}
/* pass to libzfs */
return (1);
return (1);
/*
* Volume handling. By default, we try to create a reservation of equal
* size for the volume. If we can't do this, then destroy the dataset
* and report an error.
*/
"volume without a matching reservation\n"));
(void) zfs_destroy(zhp);
return (1);
}
}
/*
* 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;
}
return (ret);
}
/*
* 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_first;
int cb_force;
int cb_recurse;
int cb_error;
int cb_needforce;
int cb_doclones;
/*
* 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
{
destroy_cbdata_t cb = { 0 };
int c;
/* 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) {
}
/* 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)
return (1);
}
/*
* Do the real thing.
*/
return (0);
return (1);
}
/*
* 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.
*/
typedef struct get_cbdata {
int cb_scripted;
int cb_sources;
int cb_literal;
int cb_columns[4];
int cb_nprop;
int cb_isall;
} get_cbdata_t;
#define GET_COL_NAME 1
#define GET_COL_PROPERTY 2
#define GET_COL_VALUE 3
#define GET_COL_SOURCE 4
/*
* Display a single line of output, according to the settings in the callback
* structure.
*/
static void
{
int i;
int width;
const char *str;
char buf[128];
/*
* Ignore those source types that the user has chosen to ignore.
*/
return;
for (i = 0; i < 4; i++) {
switch (cbp->cb_columns[i]) {
case GET_COL_NAME:
width = 15;
break;
case GET_COL_PROPERTY:
width = 13;
break;
case GET_COL_VALUE:
width = 25;
break;
case GET_COL_SOURCE:
width = 15;
switch (sourcetype) {
case ZFS_SRC_NONE:
str = "-";
break;
case ZFS_SRC_DEFAULT:
str = "default";
break;
case ZFS_SRC_LOCAL:
str = "local";
break;
case ZFS_SRC_TEMPORARY:
str = "temporary";
break;
case ZFS_SRC_INHERITED:
"inherited from %s", source);
break;
}
break;
default:
continue;
}
else if (cbp->cb_scripted)
else
}
(void) printf("\n");
}
/*
* Invoked to display the properties for a single dataset.
*/
static int
{
char buf[ZFS_MAXPROPLEN];
char source[ZFS_MAXNAMELEN];
int i;
cbp->cb_literal) != 0) {
continue;
}
}
return (0);
}
static int
{
get_cbdata_t cb = { 0 };
int recurse = 0;
int c;
int i;
int ret;
/*
* 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"));
}
/*
* If the user specifies 'all', the behavior of 'zfs get' is slightly
* different, because we don't show properties which don't apply to the
* given dataset.
*/
"'%s'\n"), badopt);
else
"specified\n"));
}
argc--;
argv++;
/* check for at least one dataset name */
if (argc < 1) {
}
/*
* Print out any headers
*/
if (!cb.cb_scripted) {
int i;
for (i = 0; i < 4; i++) {
switch (cb.cb_columns[i]) {
case GET_COL_NAME:
break;
case GET_COL_PROPERTY:
break;
case GET_COL_VALUE:
break;
case GET_COL_SOURCE:
break;
}
}
(void) printf("\n");
}
/* run for each object */
get_callback, &cb));
}
/*
* 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
{
}
static int
{
int recurse = 0;
int c;
char *propname;
/* check options */
switch (c) {
case 'r':
break;
case '?':
default:
optopt);
}
}
/* check number of arguments */
if (argc < 1) {
}
if (argc < 2) {
}
/*
* Get and validate the property before iterating over the datasets. We
* do this now so as to avoid printing out an error message for each and
* every dataset.
*/
propname);
}
if (zfs_prop_readonly(prop)) {
propname);
return (1);
}
if (!zfs_prop_inheritable(prop)) {
"inherited\n"), propname);
"clear\n"), propname);
return (1);
}
inherit_callback, (void *)prop));
}
/*
* list [-rH] [-o property[,property]...] [-t type[,type]...] <dataset> ...
*
* -r Recurse over all children
* -H Scripted mode; elide headers and separate colums by tabs
* -o Control which fields to display.
* -t Control which object types to display.
*
* 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 {
int cb_first;
int cb_scripted;
int cb_fieldcount;
/*
* Given a list of columns to display, output appropriate headers for each one.
*/
static void
{
int i;
for (i = 0; i < count; i++) {
if (i != 0)
(void) printf(" ");
if (i == count - 1)
else /* LINTED - format specifier */
zfs_prop_column_name(fields[i]));
}
(void) printf("\n");
}
/*
* Given a dataset and a list of fields, print out all the properties according
* to the described layout.
*/
static void
{
int i;
char property[ZFS_MAXPROPLEN];
for (i = 0; i < count; i++) {
if (i != 0) {
if (scripted)
(void) printf("\t");
else
(void) printf(" ");
}
/*
* 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 /* LINTED - format specifier */
property);
}
(void) printf("\n");
}
/*
* Generic callback function to list a dataset or snapshot.
*/
static int
{
if (!cbp->cb_scripted)
}
cbp->cb_scripted);
return (0);
}
static int
{
int c;
int recurse = 0;
static char default_fields[] =
"name,used,available,referenced,mountpoint";
int types = ZFS_TYPE_ANY;
char *basic_fields = default_fields;
list_cbdata_t cb = { 0 };
char *value;
int ret;
char *badopt;
int alloffset;
/* check options */
switch (c) {
case 'o':
break;
case 'r':
break;
case 'H':
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 zfs_get_proplist() doesn't
* normally include the name of the dataset. For 'zfs list', we always
* want this property to be first.
*/
alloffset = 1;
} else {
alloffset = 0;
}
"'%s'\n"), badopt);
else
"specified\n"));
}
return (ret);
}
/*
* zfs rename <fs | snap | vol> <fs | snap | vol>
*
* Renames the given dataset to another of the same type.
*/
/* ARGSUSED */
static int
{
int ret = 1;
/* check options */
}
/* check number of arguments */
if (argc < 2) {
"argument\n"));
}
if (argc < 3) {
"argument\n"));
}
if (argc > 3) {
}
return (1);
goto error;
ret = 0;
return (ret);
}
/*
* zfs rollback [-rfR] <snapshot>
*
* -r Delete any intervening snapshots before doing rollback
* -R Delete any snapshots and their clones
* -f Force unmount filesystems, even if they are in use.
*
* 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_first;
int cb_force;
int cb_doclones;
char *cb_target;
int cb_error;
int cb_recurse;
int cb_dependent;
/*
* 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) {
cbp);
} else {
zfs_get_name(zhp));
}
}
} else {
"'%s': clones of previous snapshots exist\n"),
"force deletion of the following clones and "
"dependents:\n"));
}
}
return (0);
}
/*
* Unmount any filesystems or snapshots that will need to be destroyed as part
* of the rollback process.
*/
static int
{
if (!cbp->cb_dependent) {
}
}
return (0);
}
/*
* Destroy any more recent snapshots. We invoke this callback on any dependents
* of the snapshot first. If the 'cb_dependent' member is non-zero, then this
* is a dependent and we should just destroy it without checking the transaction
* group.
*/
static int
{
if (!cbp->cb_dependent) {
if (zfs_destroy(zhp) != 0)
}
} else if (zfs_destroy(zhp) != 0) {
}
return (0);
}
static int
{
int ret;
int c;
rollback_cbdata_t cb = { 0 };
int was_mounted;
char parentname[ZFS_MAXNAMELEN];
char *delim;
/* check options */
switch (c) {
case 'f':
break;
case 'r':
break;
case 'R':
break;
case '?':
optopt);
}
}
/* check number of arguments */
if (argc < 1) {
}
if (argc > 1) {
}
/* open the snapshot */
return (1);
*delim = '\0';
return (1);
}
/* See if this dataset is mounted */
/*
* of '-r' and '-R'.
*/
goto out;
/*
* Unmount any snapshots as well as the dataset itself.
*/
goto out;
goto out;
/*
* Now that we have verified that the snapshot is the latest, rollback
* to the given snapshot.
*/
/*
* We only want to re-mount the filesystem if it was mounted in the
* first place.
*/
if (was_mounted)
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
{
int ret = 1;
/* don't allow setting of properties for snapshots */
"'%s': snapshot properties cannot be modified\n"),
return (1);
}
/*
* If we're changing the volsize, and the volsize and reservation are
* the same, then change the reservation as well.
*/
/*
* Warn about raising the volume size greater than the amount of
* available space.
*/
"%s property for '%s': volume size exceeds "
"amount of available space\n"),
return (1);
}
"reservation must remain equal\n"));
return (1);
}
}
/*
* Do not allow the reservation to be set above the volume size. We do
* this here instead of inside libzfs because libzfs violates this rule
* internally.
*/
value = 0;
else
"for '%s': size is greater than current "
zfs_get_name(zhp));
return (-1);
}
}
return (1);
ret = 0;
return (ret);
}
static int
{
/* 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"));
}
gettext("missing value in property=value argument\n"));
}
/* get the property type */
}
/*
* Validate that the value is appropriate for this property. We do this
* once now so we don't generate multiple errors each time we try to
* apply it to a dataset.
*/
return (1);
}
/*
* zfs snapshot <fs@snap>
*
* Creates a snapshot with the given name. While functionally equivalent to
* 'zfs create', it is a separate command to diffferentiate intent.
*/
static int
{
/* check options */
}
/* check number of arguments */
if (argc < 2) {
}
if (argc > 2) {
}
}
/*
* zfs backup [-i <fs@snap>] <fs@snap>
*
* Send a backup stream to stdout.
*/
static int
{
int c, err;
/* check options */
switch (c) {
case 'i':
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: Backup stream can not be written "
"to a terminal.\n"
"You must redirect standard output.\n"));
return (1);
}
if (fromname) {
return (1);
}
return (1);
if (zhp_from)
return (err != 0);
}
/*
* zfs restore <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 ':':
"'%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);
}
/*
* 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
typedef struct share_mount_cbdata {
int cb_type;
int cb_explicit;
int cb_flags;
const char *cb_options;
/*
* Share or mount the filesystem.
*/
static int
{
char mountpoint[ZFS_MAXPROPLEN];
char shareopts[ZFS_MAXPROPLEN];
else
/*
* 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 (!cbp->cb_explicit)
return (0);
return (1);
if (!cbp->cb_explicit)
return (0);
return (1);
}
/*
* Inore any filesystems which don't apply to us. This includes those
* with a legacy mountpoint, or those with legacy share options.
*/
if (!cbp->cb_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 (!cbp->cb_explicit)
return (0);
return (1);
}
if (!cbp->cb_explicit)
return (0);
return (1);
}
/*
* are appropriate for auto management. Determine if the filesystem is
* currently mounted or shared, and abort if this is an explicit
* request.
*/
case OP_SHARE:
if (cbp->cb_explicit) {
"'%s': filesystem already shared\n"),
zfs_get_name(zhp));
return (1);
} else {
return (0);
}
}
break;
case OP_MOUNT:
if (cbp->cb_explicit) {
"'%s': filesystem already mounted\n"),
zfs_get_name(zhp));
return (1);
} else {
return (0);
}
}
break;
}
/*
* Mount and optionally share the filesystem.
*/
case OP_SHARE:
{
return (1);
return (1);
}
break;
case OP_MOUNT:
return (1);
break;
}
return (0);
}
static int
{
int do_all = 0;
int c, ret;
share_mount_cbdata_t cb = { 0 };
/* check options */
!= -1) {
switch (c) {
case 'a':
do_all = 1;
break;
case 'o':
break;
case 'O':
break;
case ':':
"'%c' option\n"), optopt);
break;
case '?':
optopt);
}
}
/* check number of arguments */
if (do_all) {
if (argc != 0) {
}
} else if (argc == 0) {
"argument\n"));
}
/*
* display any active ZFS mounts. We hide any snapshots, since
* they are controlled automatically.
*/
continue;
}
ret = 0;
} else {
if (argc > 1) {
gettext("too many arguments\n"));
}
ret = 1;
else {
}
}
return (ret);
}
/*
* zfs mount -a
* zfs mount filesystem
*
* Mount all filesystems, or mount the given filesystem.
*/
static int
{
}
/*
* zfs share -a
* 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 property[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);
}
return (1);
"'%s': legacy share\n"), path);
"unshare(1M) to unshare this filesystem\n"));
ret = 1;
"not currently shared\n"), path);
ret = 1;
} else {
}
} else {
"'%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 c;
char property[ZFS_MAXPROPLEN];
/* check options */
switch (c) {
case 'a':
do_all = 1;
break;
case 'f':
break;
case '?':
optopt);
}
}
/* ensure correct number of arguments */
if (do_all) {
if (argc != 0) {
}
} else if (argc != 1) {
if (argc == 0)
gettext("missing filesystem argument\n"));
else
gettext("too many arguments\n"));
}
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.
*/
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;
}
0, FALSE) == 0);
/* Ignore legacy mounts and shares */
continue;
}
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 (type) {
case OP_SHARE:
ret = 1;
break;
case OP_MOUNT:
ret = 1;
break;
}
}
} else {
/*
* 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] == '/')
return (1);
switch (type) {
case OP_SHARE:
" to unshare this filesystem\n"));
ret = 1;
"'%s': not currently shared\n"),
zfs_get_name(zhp));
ret = 1;
} else if (zfs_unshareall(zhp) != 0) {
ret = 1;
}
break;
case OP_MOUNT:
"'%s': legacy mountpoint\n"),
zfs_get_name(zhp));
"to unmount this filesystem\n"));
ret = 1;
"'%s': not currently mounted\n"),
zfs_get_name(zhp));
ret = 1;
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 ':':
"'%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
do_volcheck(int isinit)
{
}
int
{
int ret;
int i;
char *progname;
char *cmdname;
(void) textdomain(TEXT_DOMAIN);
opterr = 0;
"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";
/*
* Special case '-?'
*/
/*
* 'volinit' and 'volfini' do not appear in the usage message,
* so we have to special case them here.
*/
return (do_volcheck(TRUE));
return (do_volcheck(FALSE));
/*
* Run the appropriate command.
*/
for (i = 0; i < NCOMMAND; i++) {
continue;
current_command = &command_table[i];
break;
}
}
if (i == NCOMMAND) {
"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);
}