aboutsummaryrefslogtreecommitdiff
path: root/cddl/contrib/opensolaris/cmd/zfs
diff options
context:
space:
mode:
Diffstat (limited to 'cddl/contrib/opensolaris/cmd/zfs')
-rw-r--r--cddl/contrib/opensolaris/cmd/zfs/zfs-program.8551
-rw-r--r--cddl/contrib/opensolaris/cmd/zfs/zfs.83973
-rw-r--r--cddl/contrib/opensolaris/cmd/zfs/zfs_iter.c497
-rw-r--r--cddl/contrib/opensolaris/cmd/zfs/zfs_iter.h62
-rw-r--r--cddl/contrib/opensolaris/cmd/zfs/zfs_main.c7592
-rw-r--r--cddl/contrib/opensolaris/cmd/zfs/zfs_util.h42
6 files changed, 0 insertions, 12717 deletions
diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs-program.8 b/cddl/contrib/opensolaris/cmd/zfs/zfs-program.8
deleted file mode 100644
index 76bb97c2d96d..000000000000
--- a/cddl/contrib/opensolaris/cmd/zfs/zfs-program.8
+++ /dev/null
@@ -1,551 +0,0 @@
-.\" This file and its contents are supplied under the terms of the
-.\" Common Development and Distribution License ("CDDL"), version 1.0.
-.\" You may only use this file in accordance with the terms of version
-.\" 1.0 of the CDDL.
-.\"
-.\" A full copy of the text of the CDDL should have accompanied this
-.\" source. A copy of the CDDL is also available via the Internet at
-.\" http://www.illumos.org/license/CDDL.
-.\"
-.\"
-.\" Copyright (c) 2016, 2017 by Delphix. All rights reserved.
-.\" Copyright (c) 2018 Datto Inc.
-.\"
-.Dd April 18, 2020
-.Dt ZFS-PROGRAM 8
-.Os
-.Sh NAME
-.Nm zfs program
-.Nd executes ZFS channel programs
-.Sh SYNOPSIS
-.Cm zfs program
-.Op Fl jn
-.Op Fl t Ar instruction-limit
-.Op Fl m Ar memory-limit
-.Ar pool
-.Ar script
-.\".Op Ar optional arguments to channel program
-.Sh DESCRIPTION
-The ZFS channel program interface allows ZFS administrative operations to be
-run programmatically as a Lua script.
-The entire script is executed atomically, with no other administrative
-operations taking effect concurrently.
-A library of ZFS calls is made available to channel program scripts.
-Channel programs may only be run with root privileges.
-.Pp
-A modified version of the Lua 5.2 interpreter is used to run channel program
-scripts.
-The Lua 5.2 manual can be found at:
-.Bd -centered -offset indent
-.Lk http://www.lua.org/manual/5.2/
-.Ed
-.Pp
-The channel program given by
-.Ar script
-will be run on
-.Ar pool ,
-and any attempts to access or modify other pools will cause an error.
-.Sh OPTIONS
-.Bl -tag -width "-t"
-.It Fl j
-Display channel program output in JSON format.
-When this flag is specified and standard output is empty -
-channel program encountered an error.
-The details of such an error will be printed to standard error in plain text.
-.It Fl n
-Executes a read-only channel program, which runs faster.
-The program cannot change on-disk state by calling functions from the
-zfs.sync submodule.
-The program can be used to gather information such as properties and
-determining if changes would succeed (zfs.check.*).
-Without this flag, all pending changes must be synced to disk before a
-channel program can complete.
-.It Fl t Ar instruction-limit
-Execution time limit, in number of Lua instructions to execute.
-If a channel program executes more than the specified number of instructions,
-it will be stopped and an error will be returned.
-The default limit is 10 million instructions, and it can be set to a maximum of
-100 million instructions.
-.It Fl m Ar memory-limit
-Memory limit, in bytes.
-If a channel program attempts to allocate more memory than the given limit, it
-will be stopped and an error returned.
-The default memory limit is 10 MB, and can be set to a maximum of 100 MB.
-.El
-.Pp
-All remaining argument strings will be passed directly to the Lua script as
-described in the
-.Sx LUA INTERFACE
-section below.
-.Sh LUA INTERFACE
-A channel program can be invoked either from the command line, or via a library
-call to
-.Fn lzc_channel_program .
-.Ss Arguments
-Arguments passed to the channel program are converted to a Lua table.
-If invoked from the command line, extra arguments to the Lua script will be
-accessible as an array stored in the argument table with the key 'argv':
-.Bd -literal -offset indent
-args = ...
-argv = args["argv"]
--- argv == {1="arg1", 2="arg2", ...}
-.Ed
-.Pp
-If invoked from the libZFS interface, an arbitrary argument list can be
-passed to the channel program, which is accessible via the same
-"..." syntax in Lua:
-.Bd -literal -offset indent
-args = ...
--- args == {"foo"="bar", "baz"={...}, ...}
-.Ed
-.Pp
-Note that because Lua arrays are 1-indexed, arrays passed to Lua from the
-libZFS interface will have their indices incremented by 1.
-That is, the element
-in
-.Va arr[0]
-in a C array passed to a channel program will be stored in
-.Va arr[1]
-when accessed from Lua.
-.Ss Return Values
-Lua return statements take the form:
-.Bd -literal -offset indent
-return ret0, ret1, ret2, ...
-.Ed
-.Pp
-Return statements returning multiple values are permitted internally in a
-channel program script, but attempting to return more than one value from the
-top level of the channel program is not permitted and will throw an error.
-However, tables containing multiple values can still be returned.
-If invoked from the command line, a return statement:
-.Bd -literal -offset indent
-a = {foo="bar", baz=2}
-return a
-.Ed
-.Pp
-Will be output formatted as:
-.Bd -literal -offset indent
-Channel program fully executed with return value:
- return:
- baz: 2
- foo: 'bar'
-.Ed
-.Ss Fatal Errors
-If the channel program encounters a fatal error while running, a non-zero exit
-status will be returned.
-If more information about the error is available, a singleton list will be
-returned detailing the error:
-.Bd -literal -offset indent
-error: "error string, including Lua stack trace"
-.Ed
-.Pp
-If a fatal error is returned, the channel program may have not executed at all,
-may have partially executed, or may have fully executed but failed to pass a
-return value back to userland.
-.Pp
-If the channel program exhausts an instruction or memory limit, a fatal error
-will be generated and the program will be stopped, leaving the program partially
-executed.
-No attempt is made to reverse or undo any operations already performed.
-Note that because both the instruction count and amount of memory used by a
-channel program are deterministic when run against the same inputs and
-filesystem state, as long as a channel program has run successfully once, you
-can guarantee that it will finish successfully against a similar size system.
-.Pp
-If a channel program attempts to return too large a value, the program will
-fully execute but exit with a nonzero status code and no return value.
-.Pp
-.Em Note:
-ZFS API functions do not generate Fatal Errors when correctly invoked, they
-return an error code and the channel program continues executing.
-See the
-.Sx ZFS API
-section below for function-specific details on error return codes.
-.Ss Lua to C Value Conversion
-When invoking a channel program via the libZFS interface, it is necessary to
-translate arguments and return values from Lua values to their C equivalents,
-and vice-versa.
-.Pp
-There is a correspondence between nvlist values in C and Lua tables.
-A Lua table which is returned from the channel program will be recursively
-converted to an nvlist, with table values converted to their natural
-equivalents:
-.Bd -literal -offset indent
-string -> string
-number -> int64
-boolean -> boolean_value
-nil -> boolean (no value)
-table -> nvlist
-.Ed
-.Pp
-Likewise, table keys are replaced by string equivalents as follows:
-.Bd -literal -offset indent
-string -> no change
-number -> signed decimal string ("%lld")
-boolean -> "true" | "false"
-.Ed
-.Pp
-Any collision of table key strings (for example, the string "true" and a
-true boolean value) will cause a fatal error.
-.Pp
-Lua numbers are represented internally as signed 64-bit integers.
-.Sh LUA STANDARD LIBRARY
-The following Lua built-in base library functions are available:
-.Bd -literal -offset indent
-assert rawlen
-collectgarbage rawget
-error rawset
-getmetatable select
-ipairs setmetatable
-next tonumber
-pairs tostring
-rawequal type
-.Ed
-.Pp
-All functions in the
-.Em coroutine ,
-.Em string ,
-and
-.Em table
-built-in submodules are also available.
-A complete list and documentation of these modules is available in the Lua
-manual.
-.Pp
-The following functions base library functions have been disabled and are
-not available for use in channel programs:
-.Bd -literal -offset indent
-dofile
-loadfile
-load
-pcall
-print
-xpcall
-.Ed
-.Sh ZFS API
-.Ss Function Arguments
-Each API function takes a fixed set of required positional arguments and
-optional keyword arguments.
-For example, the destroy function takes a single positional string argument
-(the name of the dataset to destroy) and an optional "defer" keyword boolean
-argument.
-When using parentheses to specify the arguments to a Lua function, only
-positional arguments can be used:
-.Bd -literal -offset indent
-zfs.sync.destroy("rpool@snap")
-.Ed
-.Pp
-To use keyword arguments, functions must be called with a single argument that
-is a Lua table containing entries mapping integers to positional arguments and
-strings to keyword arguments:
-.Bd -literal -offset indent
-zfs.sync.destroy({1="rpool@snap", defer=true})
-.Ed
-.Pp
-The Lua language allows curly braces to be used in place of parenthesis as
-syntactic sugar for this calling convention:
-.Bd -literal -offset indent
-zfs.sync.snapshot{"rpool@snap", defer=true}
-.Ed
-.Ss Function Return Values
-If an API function succeeds, it returns 0.
-If it fails, it returns an error code and the channel program continues
-executing.
-API functions do not generate Fatal Errors except in the case of an
-unrecoverable internal file system error.
-.Pp
-In addition to returning an error code, some functions also return extra
-details describing what caused the error.
-This extra description is given as a second return value, and will always be a
-Lua table, or Nil if no error details were returned.
-Different keys will exist in the error details table depending on the function
-and error case.
-Any such function may be called expecting a single return value:
-.Bd -literal -offset indent
-errno = zfs.sync.promote(dataset)
-.Ed
-.Pp
-Or, the error details can be retrieved:
-.Bd -literal -offset indent
-errno, details = zfs.sync.promote(dataset)
-if (errno == EEXIST) then
- assert(details ~= Nil)
- list_of_conflicting_snapshots = details
-end
-.Ed
-.Pp
-The following global aliases for API function error return codes are defined
-for use in channel programs:
-.Bd -literal -offset indent
-EPERM ECHILD ENODEV ENOSPC
-ENOENT EAGAIN ENOTDIR ESPIPE
-ESRCH ENOMEM EISDIR EROFS
-EINTR EACCES EINVAL EMLINK
-EIO EFAULT ENFILE EPIPE
-ENXIO ENOTBLK EMFILE EDOM
-E2BIG EBUSY ENOTTY ERANGE
-ENOEXEC EEXIST ETXTBSY EDQUOT
-EBADF EXDEV EFBIG
-.Ed
-.Ss API Functions
-For detailed descriptions of the exact behavior of any zfs administrative
-operations, see the main
-.Xr zfs 8
-manual page.
-.Bl -tag -width "xx"
-.It Em zfs.debug(msg)
-Record a debug message in the zfs_dbgmsg log.
-A log of these messages can be printed via mdb's "::zfs_dbgmsg" command, or
-can be monitored live by running:
-.Bd -literal -offset indent
- dtrace -n 'zfs-dbgmsg{trace(stringof(arg0))}'
-.Ed
-.Pp
-msg (string)
-.Bd -ragged -compact -offset "xxxx"
-Debug message to be printed.
-.Ed
-.It Em zfs.exists(dataset)
-Returns true if the given dataset exists, or false if it doesn't.
-A fatal error will be thrown if the dataset is not in the target pool.
-That is, in a channel program running on rpool,
-zfs.exists("rpool/nonexistent_fs") returns false, but
-zfs.exists("somepool/fs_that_may_exist") will error.
-.Pp
-dataset (string)
-.Bd -ragged -compact -offset "xxxx"
-Dataset to check for existence.
-Must be in the target pool.
-.Ed
-.It Em zfs.get_prop(dataset, property)
-Returns two values.
-First, a string, number or table containing the property value for the given
-dataset.
-Second, a string containing the source of the property (i.e. the name of the
-dataset in which it was set or nil if it is readonly).
-Throws a Lua error if the dataset is invalid or the property doesn't exist.
-Note that Lua only supports int64 number types whereas ZFS number properties
-are uint64.
-This means very large values (like guid) may wrap around and appear negative.
-.Pp
-dataset (string)
-.Bd -ragged -compact -offset "xxxx"
-Filesystem or snapshot path to retrieve properties from.
-.Ed
-.Pp
-property (string)
-.Bd -ragged -compact -offset "xxxx"
-Name of property to retrieve.
-All filesystem, snapshot and volume properties are supported except
-for 'mounted' and 'iscsioptions.'
-Also supports the 'written@snap' and 'written#bookmark' properties and
-the '<user|group><quota|used>@id' properties, though the id must be in numeric
-form.
-.Ed
-.El
-.Bl -tag -width "xx"
-.It Sy zfs.sync submodule
-The sync submodule contains functions that modify the on-disk state.
-They are executed in "syncing context".
-.Pp
-The available sync submodule functions are as follows:
-.Bl -tag -width "xx"
-.It Em zfs.sync.destroy(dataset, [defer=true|false])
-Destroy the given dataset.
-Returns 0 on successful destroy, or a nonzero error code if the dataset could
-not be destroyed (for example, if the dataset has any active children or
-clones).
-.Pp
-dataset (string)
-.Bd -ragged -compact -offset "xxxx"
-Filesystem or snapshot to be destroyed.
-.Ed
-.Pp
-[optional] defer (boolean)
-.Bd -ragged -compact -offset "xxxx"
-Valid only for destroying snapshots.
-If set to true, and the snapshot has holds or clones, allows the snapshot to be
-marked for deferred deletion rather than failing.
-.Ed
-.It Em zfs.sync.promote(dataset)
-Promote the given clone to a filesystem.
-Returns 0 on successful promotion, or a nonzero error code otherwise.
-If EEXIST is returned, the second return value will be an array of the clone's
-snapshots whose names collide with snapshots of the parent filesystem.
-.Pp
-dataset (string)
-.Bd -ragged -compact -offset "xxxx"
-Clone to be promoted.
-.Ed
-.It Em zfs.sync.rollback(filesystem)
-Rollback to the previous snapshot for a dataset.
-Returns 0 on successful rollback, or a nonzero error code otherwise.
-Rollbacks can be performed on filesystems or zvols, but not on snapshots
-or mounted datasets.
-EBUSY is returned in the case where the filesystem is mounted.
-.Pp
-filesystem (string)
-.Bd -ragged -compact -offset "xxxx"
-Filesystem to rollback.
-.Ed
-.It Em zfs.sync.snapshot(dataset)
-Create a snapshot of a filesystem.
-Returns 0 if the snapshot was successfully created,
-and a nonzero error code otherwise.
-.Pp
-Note: Taking a snapshot will fail on any pool older than legacy version 27.
-To enable taking snapshots from ZCP scripts, the pool must be upgraded.
-.Pp
-dataset (string)
-.Bd -ragged -compact -offset "xxxx"
-Name of snapshot to create.
-.Ed
-.El
-.It Sy zfs.check submodule
-For each function in the zfs.sync submodule, there is a corresponding zfs.check
-function which performs a "dry run" of the same operation.
-Each takes the same arguments as its zfs.sync counterpart and returns 0 if the
-operation would succeed, or a non-zero error code if it would fail, along with
-any other error details.
-That is, each has the same behavior as the corresponding sync function except
-for actually executing the requested change.
-For example,
-.Em zfs.check.destroy("fs")
-returns 0 if
-.Em zfs.sync.destroy("fs")
-would successfully destroy the dataset.
-.Pp
-The available zfs.check functions are:
-.Bl -tag -width "xx"
-.It Em zfs.check.destroy(dataset, [defer=true|false])
-.It Em zfs.check.promote(dataset)
-.It Em zfs.check.rollback(filesystem)
-.It Em zfs.check.snapshot(dataset)
-.El
-.It Sy zfs.list submodule
-The zfs.list submodule provides functions for iterating over datasets and
-properties.
-Rather than returning tables, these functions act as Lua iterators, and are
-generally used as follows:
-.Bd -literal -offset indent
-for child in zfs.list.children("rpool") do
- ...
-end
-.Ed
-.Pp
-The available zfs.list functions are:
-.Bl -tag -width "xx"
-.It Em zfs.list.clones(snapshot)
-Iterate through all clones of the given snapshot.
-.Pp
-snapshot (string)
-.Bd -ragged -compact -offset "xxxx"
-Must be a valid snapshot path in the current pool.
-.Ed
-.It Em zfs.list.snapshots(dataset)
-Iterate through all snapshots of the given dataset.
-Each snapshot is returned as a string containing the full dataset name, e.g.
-"pool/fs@snap".
-.Pp
-dataset (string)
-.Bd -ragged -compact -offset "xxxx"
-Must be a valid filesystem or volume.
-.Ed
-.It Em zfs.list.children(dataset)
-Iterate through all direct children of the given dataset.
-Each child is returned as a string containing the full dataset name, e.g.
-"pool/fs/child".
-.Pp
-dataset (string)
-.Bd -ragged -compact -offset "xxxx"
-Must be a valid filesystem or volume.
-.Ed
-.It Em zfs.list.properties(dataset)
-Iterate through all user properties for the given dataset.
-.Pp
-dataset (string)
-.Bd -ragged -compact -offset "xxxx"
-Must be a valid filesystem, snapshot, or volume.
-.Ed
-.It Em zfs.list.system_properties(dataset)
-Returns an array of strings, the names of the valid system (non-user defined)
-properties for the given dataset.
-Throws a Lua error if the dataset is invalid.
-.Pp
-dataset (string)
-.Bd -ragged -compact -offset "xxxx"
-Must be a valid filesystem, snapshot or volume.
-.Ed
-.El
-.El
-.Sh EXAMPLES
-.Ss Example 1
-The following channel program recursively destroys a filesystem and all its
-snapshots and children in a naive manner.
-Note that this does not involve any error handling or reporting.
-.Bd -literal -offset indent
-function destroy_recursive(root)
- for child in zfs.list.children(root) do
- destroy_recursive(child)
- end
- for snap in zfs.list.snapshots(root) do
- zfs.sync.destroy(snap)
- end
- zfs.sync.destroy(root)
-end
-destroy_recursive("pool/somefs")
-.Ed
-.Ss Example 2
-A more verbose and robust version of the same channel program, which
-properly detects and reports errors, and also takes the dataset to destroy
-as a command line argument, would be as follows:
-.Bd -literal -offset indent
-succeeded = {}
-failed = {}
-
-function destroy_recursive(root)
- for child in zfs.list.children(root) do
- destroy_recursive(child)
- end
- for snap in zfs.list.snapshots(root) do
- err = zfs.sync.destroy(snap)
- if (err ~= 0) then
- failed[snap] = err
- else
- succeeded[snap] = err
- end
- end
- err = zfs.sync.destroy(root)
- if (err ~= 0) then
- failed[root] = err
- else
- succeeded[root] = err
- end
-end
-
-args = ...
-argv = args["argv"]
-
-destroy_recursive(argv[1])
-
-results = {}
-results["succeeded"] = succeeded
-results["failed"] = failed
-return results
-.Ed
-.Ss Example 3
-The following function performs a forced promote operation by attempting to
-promote the given clone and destroying any conflicting snapshots.
-.Bd -literal -offset indent
-function force_promote(ds)
- errno, details = zfs.check.promote(ds)
- if (errno == EEXIST) then
- assert(details ~= Nil)
- for i, snap in ipairs(details) do
- zfs.sync.destroy(ds .. "@" .. snap)
- end
- elseif (errno ~= 0) then
- return errno
- end
- return zfs.sync.promote(ds)
-end
-.Ed
diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs.8 b/cddl/contrib/opensolaris/cmd/zfs/zfs.8
deleted file mode 100644
index 33e0ca4b3040..000000000000
--- a/cddl/contrib/opensolaris/cmd/zfs/zfs.8
+++ /dev/null
@@ -1,3973 +0,0 @@
-'\" te
-.\" Copyright (c) 2013, Martin Matuska <mm@FreeBSD.org>.
-.\" All Rights Reserved.
-.\"
-.\" 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
-.\" or http://www.opensolaris.org/os/licensing.
-.\" 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]
-.\"
-.\" Copyright (c) 2010, Sun Microsystems, Inc. All Rights Reserved.
-.\" Copyright (c) 2011, 2014 by Delphix. All rights reserved.
-.\" Copyright (c) 2011, Pawel Jakub Dawidek <pjd@FreeBSD.org>
-.\" Copyright (c) 2012, Glen Barber <gjb@FreeBSD.org>
-.\" Copyright (c) 2012, Bryan Drewery <bdrewery@FreeBSD.org>
-.\" Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
-.\" Copyright (c) 2013, Steven Hartland <smh@FreeBSD.org>
-.\" Copyright (c) 2016 Nexenta Systems, Inc. All Rights Reserved.
-.\" Copyright (c) 2014, Xin LI <delphij@FreeBSD.org>
-.\" Copyright (c) 2014-2015, The FreeBSD Foundation, All Rights Reserved.
-.\" Copyright 2019 Joyent, Inc.
-.\" Copyright (c) 2018 Datto Inc.
-.\"
-.\" $FreeBSD$
-.\"
-.Dd February 16, 2020
-.Dt ZFS 8
-.Os
-.Sh NAME
-.Nm zfs
-.Nd configures ZFS file systems
-.Sh SYNOPSIS
-.Nm
-.Op Fl \&?
-.Nm
-.Cm create
-.Op Fl pu
-.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ... Ar filesystem
-.Nm
-.Cm create
-.Op Fl ps
-.Op Fl b Ar blocksize
-.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
-.Fl V
-.Ar size volume
-.Nm
-.Cm destroy
-.Op Fl fnpRrv
-.Ar filesystem Ns | Ns Ar volume
-.Nm
-.Cm destroy
-.Op Fl dnpRrv
-.Sm off
-.Ar filesystem Ns | Ns volume
-.Ns @snap
-.Op % Ns Ar snap
-.Op , Ns Ar snap Op % Ns Ar snap
-.Op , Ns ...
-.Sm on
-.Nm
-.Cm destroy
-.Ar filesystem Ns | Ns Ar volume Ns # Ns Ar bookmark
-.Nm
-.Cm snapshot Ns | Ns Cm snap
-.Op Fl r
-.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
-.Ar filesystem@snapname Ns | Ns Ar volume@snapname
-.Ar filesystem@snapname Ns | Ns Ar volume@snapname Ns ...
-.Nm
-.Cm rollback
-.Op Fl rRf
-.Ar snapshot
-.Nm
-.Cm clone
-.Op Fl p
-.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
-.Ar snapshot filesystem Ns | Ns Ar volume
-.Nm
-.Cm promote
-.Ar clone-filesystem
-.Nm
-.Cm rename
-.Op Fl f
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
-.Nm
-.Cm rename
-.Op Fl f
-.Fl p
-.Ar filesystem Ns | Ns Ar volume
-.Ar filesystem Ns | Ns Ar volume
-.Nm
-.Cm rename
-.Fl r
-.Ar snapshot snapshot
-.Nm
-.Cm rename
-.Ar bookmark bookmark
-.Nm
-.Cm rename
-.Fl u
-.Op Fl p
-.Ar filesystem filesystem
-.Nm
-.Cm list
-.Op Fl r Ns | Ns Fl d Ar depth
-.Op Fl Hp
-.Op Fl o Ar property Ns Oo , Ns property Ns Oc Ns ...
-.Op Fl t Ar type Ns Oo , Ns type Ns Oc Ns ...
-.Oo Fl s Ar property Oc Ns ...
-.Oo Fl S Ar property Oc Ns ...
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot | Ns Ar bookmark Ns ...
-.Nm
-.Cm remap
-.Ar filesystem Ns | Ns Ar volume
-.Nm
-.Cm set
-.Ar property Ns = Ns Ar value Oo Ar property Ns = Ns Ar value Oc Ns ...
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns ...
-.Nm
-.Cm get
-.Op Fl r Ns | Ns Fl d Ar depth
-.Op Fl Hp
-.Op Fl o Ar all | field Ns Oo , Ns Ar field Oc Ns ...
-.Op Fl t Ar type Ns Oo Ns , Ar type Oc Ns ...
-.Op Fl s Ar source Ns Oo Ns , Ns Ar source Oc Ns ...
-.Ar all | property Ns Oo Ns , Ns Ar property Oc Ns ...
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns ...
-.Nm
-.Cm inherit
-.Op Fl rS
-.Ar property
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns ...
-.Nm
-.Cm upgrade
-.Op Fl v
-.Nm
-.Cm upgrade
-.Op Fl r
-.Op Fl V Ar version
-.Fl a | Ar filesystem
-.Nm
-.Cm userspace
-.Op Fl Hinp
-.Op Fl o Ar field Ns Oo , Ns Ar field Oc Ns ...
-.Oo Fl s Ar field Oc Ns ...
-.Oo Fl S Ar field Oc Ns ...
-.Op Fl t Ar type Ns Oo Ns , Ns Ar type Oc Ns ...
-.Ar filesystem Ns | Ns Ar snapshot
-.Nm
-.Cm groupspace
-.Op Fl Hinp
-.Op Fl o Ar field Ns Oo , Ns field Oc Ns ...
-.Oo Fl s Ar field Oc Ns ...
-.Oo Fl S Ar field Oc Ns ...
-.Op Fl t Ar type Ns Oo Ns , Ns Ar type Oc Ns ...
-.Ar filesystem Ns | Ns Ar snapshot
-.Nm
-.Cm mount
-.Nm
-.Cm mount
-.Op Fl vO
-.Op Fl o Ar property Ns Oo , Ns Ar property Oc Ns ...
-.Fl a | Ar filesystem
-.Nm
-.Cm unmount Ns | Ns Cm umount
-.Op Fl f
-.Fl a | Ar filesystem Ns | Ns Ar mountpoint
-.Nm
-.Cm share
-.Fl a | Ar filesystem
-.Nm
-.Cm unshare
-.Fl a | Ar filesystem Ns | Ns Ar mountpoint
-.Nm
-.Cm bookmark
-.Ar snapshot
-.Ar bookmark
-.Nm
-.Cm send
-.Op Fl DLPRVcenpv
-.Op Fl i Ar snapshot | Fl I Ar snapshot
-.Ar snapshot
-.Nm
-.Cm send
-.Op Fl LPcenv
-.Op Fl i Ar snapshot Ns | Ns Ar bookmark
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
-.Nm
-.Cm send
-.Op Fl PVenv
-.Fl t Ar receive_resume_token
-.Nm
-.Cm receive Ns | Ns Cm recv
-.Op Fl vnsFMu
-.Op Fl o Sy origin Ns = Ns Ar snapshot
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
-.Nm
-.Cm receive Ns | Ns Cm recv
-.Op Fl vnsFMu
-.Op Fl d | e
-.Op Fl o Sy origin Ns = Ns Ar snapshot
-.Ar filesystem
-.Nm
-.Cm receive Ns | Ns Cm recv
-.Fl A
-.Ar filesystem Ns | Ns Ar volume
-.Nm
-.Cm allow
-.Ar filesystem Ns | Ns Ar volume
-.Nm
-.Cm allow
-.Op Fl ldug
-.Ar user Ns | Ns Ar group Ns Oo Ns , Ns Ar user Ns | Ns Ar group Oc Ns ...
-.Ar perm Ns | Ns Ar @setname Ns
-.Oo Ns , Ns Ar perm Ns | Ns Ar @setname Oc Ns ...
-.Ar filesystem Ns | Ns Ar volume
-.Nm
-.Cm allow
-.Op Fl ld
-.Fl e Ns | Ns Cm everyone
-.Ar perm Ns | Ns Ar @setname Ns Op Ns , Ns Ar perm Ns | Ns Ar @setname Ns
-.Ns ...
-.Ar filesystem Ns | Ns Ar volume
-.Nm
-.Cm allow
-.Fl c
-.Ar perm Ns | Ns Ar @setname Ns Op Ns , Ns Ar perm Ns | Ns Ar @setname Ns
-.Ns ...
-.Ar filesystem Ns | Ns Ar volume
-.Nm
-.Cm allow
-.Fl s
-.Ar @setname
-.Ar perm Ns | Ns Ar @setname Ns Op Ns , Ns Ar perm Ns | Ns Ar @setname Ns
-.Ns ...
-.Ar filesystem Ns | Ns Ar volume
-.Nm
-.Cm unallow
-.Op Fl rldug
-.Ar user Ns | Ns Ar group Ns Oo Ns , Ns Ar user Ns | Ns Ar group Oc Ns ...
-.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns
-.Ns ... Oc
-.Ar filesystem Ns | Ns Ar volume
-.Nm
-.Cm unallow
-.Op Fl rld
-.Fl e Ns | Ns Cm everyone
-.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns
-.Ns ... Oc
-.Ar filesystem Ns | Ns Ar volume
-.Nm
-.Cm unallow
-.Op Fl r
-.Fl c
-.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns
-.Ns ... Oc
-.Ar filesystem Ns | Ns Ar volume
-.Nm
-.Cm unallow
-.Op Fl r
-.Fl s
-.Ar @setname
-.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns
-.Ns ... Oc
-.Ar filesystem Ns | Ns Ar volume
-.Nm
-.Cm hold
-.Op Fl r
-.Ar tag snapshot Ns ...
-.Nm
-.Cm holds
-.Op Fl Hp
-.Op Fl r Ns | Ns Fl d Ar depth
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns
-.Ns ...
-.Nm
-.Cm release
-.Op Fl r
-.Ar tag snapshot Ns ...
-.Nm
-.Cm diff
-.Op Fl FHt
-.Ar snapshot
-.Op Ar snapshot Ns | Ns Ar filesystem
-.Nm
-.Cm program
-.Op Fl jn
-.Op Fl t Ar timeout
-.Op Fl m Ar memory_limit
-.Ar pool script
-.Op Ar arg1 No ...
-.Nm
-.Cm jail
-.Ar jailid Ns | Ns Ar jailname filesystem
-.Nm
-.Cm unjail
-.Ar jailid Ns | Ns Ar jailname filesystem
-.Sh DESCRIPTION
-The
-.Nm
-command configures
-.Tn ZFS
-datasets within a
-.Tn ZFS
-storage pool, as described in
-.Xr zpool 8 .
-A dataset is identified by a unique path within the
-.Tn ZFS
-namespace. For example:
-.Bd -ragged -offset 4n
-.No pool/ Ns Brq filesystem,volume,snapshot
-.Ed
-.Pp
-where the maximum length of a dataset name is
-.Dv MAXNAMELEN
-(256 bytes)
-and the maximum amount of nesting allowed in a path is 50 levels deep.
-.Pp
-A dataset can be one of the following:
-.Bl -hang -width 12n
-.It Sy file system
-A
-.Tn ZFS
-dataset of type
-.Em filesystem
-can be mounted within the standard system namespace and behaves like other file
-systems. While
-.Tn ZFS
-file systems are designed to be
-.Tn POSIX
-compliant, known issues exist that prevent compliance in some cases.
-Applications that depend on standards conformance might fail due to nonstandard
-behavior when checking file system free space.
-.It Sy volume
-A logical volume exported as a raw or block device. This type of dataset should
-only be used under special circumstances. File systems are typically used in
-most environments.
-.It Sy snapshot
-A read-only version of a file system or volume at a given point in time. It is
-specified as
-.Em filesystem@name
-or
-.Em volume@name .
-.El
-.Ss ZFS File System Hierarchy
-A
-.Tn ZFS
-storage pool is a logical collection of devices that provide space for
-datasets. A storage pool is also the root of the
-.Tn ZFS
-file system hierarchy.
-.Pp
-The root of the pool can be accessed as a file system, such as mounting and
-unmounting, taking snapshots, and setting properties. The physical storage
-characteristics, however, are managed by the
-.Xr zpool 8
-command.
-.Pp
-See
-.Xr zpool 8
-for more information on creating and administering pools.
-.Ss Snapshots
-A snapshot is a read-only copy of a file system or volume. Snapshots can be
-created extremely quickly, and initially consume no additional space within the
-pool. As data within the active dataset changes, the snapshot consumes more
-data than would otherwise be shared with the active dataset.
-.Pp
-Snapshots can have arbitrary names. Snapshots of volumes can be cloned or
-rolled back, but cannot be accessed independently.
-.Pp
-File system snapshots can be accessed under the
-.Pa \&.zfs/snapshot
-directory in the root of the file system. Snapshots are automatically mounted
-on demand and may be unmounted at regular intervals. The visibility of the
-.Pa \&.zfs
-directory can be controlled by the
-.Sy snapdir
-property.
-.Ss Clones
-A clone is a writable volume or file system whose initial contents are the same
-as another dataset. As with snapshots, creating a clone is nearly
-instantaneous, and initially consumes no additional space.
-.Pp
-Clones can only be created from a snapshot. When a snapshot is cloned, it
-creates an implicit dependency between the parent and child. Even though the
-clone is created somewhere else in the dataset hierarchy, the original snapshot
-cannot be destroyed as long as a clone exists. The
-.Sy origin
-property exposes this dependency, and the
-.Cm destroy
-command lists any such dependencies, if they exist.
-.Pp
-The clone parent-child dependency relationship can be reversed by using the
-.Cm promote
-subcommand. This causes the "origin" file system to become a clone of the
-specified file system, which makes it possible to destroy the file system that
-the clone was created from.
-.Ss Mount Points
-Creating a
-.Tn ZFS
-file system is a simple operation, so the number of file systems per system is
-likely to be numerous. To cope with this,
-.Tn ZFS
-automatically manages mounting and unmounting file systems without the need to
-edit the
-.Pa /etc/fstab
-file. All automatically managed file systems are mounted by
-.Tn ZFS
-at boot time.
-.Pp
-By default, file systems are mounted under
-.Pa /path ,
-where
-.Ar path
-is the name of the file system in the
-.Tn ZFS
-namespace. Directories are created and destroyed as needed.
-.Pp
-A file system can also have a mount point set in the
-.Sy mountpoint
-property. This directory is created as needed, and
-.Tn ZFS
-automatically mounts the file system when the
-.Qq Nm Cm mount Fl a
-command is invoked (without editing
-.Pa /etc/fstab ) .
-The
-.Sy mountpoint
-property can be inherited, so if
-.Em pool/home
-has a mount point of
-.Pa /home ,
-then
-.Em pool/home/user
-automatically inherits a mount point of
-.Pa /home/user .
-.Pp
-A file system
-.Sy mountpoint
-property of
-.Cm none
-prevents the file system from being mounted.
-.Pp
-If needed,
-.Tn ZFS
-file systems can also be managed with traditional tools
-.Pq Xr mount 8 , Xr umount 8 , Xr fstab 5 .
-If a file system's mount point is set to
-.Cm legacy ,
-.Tn ZFS
-makes no attempt to manage the file system, and the administrator is
-responsible for mounting and unmounting the file system.
-.Ss Jails
-.No A Tn ZFS
-dataset can be attached to a jail by using the
-.Qq Nm Cm jail
-subcommand. You cannot attach a dataset to one jail and the children of the
-same dataset to another jail. You can also not attach the root file system
-of the jail or any dataset which needs to be mounted before the zfs rc script
-is run inside the jail, as it would be attached unmounted until it is
-mounted from the rc script inside the jail. To allow management of the
-dataset from within a jail, the
-.Sy jailed
-property has to be set and the jail needs access to the
-.Pa /dev/zfs
-device. The
-.Sy quota
-property cannot be changed from within a jail. See
-.Xr jail 8
-for information on how to allow mounting
-.Tn ZFS
-datasets from within a jail.
-.Pp
-.No A Tn ZFS
-dataset can be detached from a jail using the
-.Qq Nm Cm unjail
-subcommand.
-.Pp
-After a dataset is attached to a jail and the jailed property is set, a jailed
-file system cannot be mounted outside the jail, since the jail administrator
-might have set the mount point to an unacceptable value.
-.Ss Deduplication
-Deduplication is the process for removing redundant data at the block-level,
-reducing the total amount of data stored. If a file system has the
-.Cm dedup
-property enabled, duplicate data blocks are removed synchronously. The result
-is that only unique data is stored and common components are shared among
-files.
-.Ss Native Properties
-Properties are divided into two types, native properties and user-defined (or
-"user") properties. Native properties either export internal statistics or
-control
-.Tn ZFS
-behavior. In addition, native properties are either editable or read-only. User
-properties have no effect on
-.Tn ZFS
-behavior, but you can use them to annotate datasets in a way that is meaningful
-in your environment. For more information about user properties, see the
-.Qq Sx User Properties
-section, below.
-.Pp
-Every dataset has a set of properties that export statistics about the dataset
-as well as control various behaviors. Properties are inherited from the parent
-unless overridden by the child. Some properties apply only to certain types of
-datasets (file systems, volumes, or snapshots).
-.Pp
-The values of numeric properties can be specified using human-readable suffixes
-(for example,
-.Sy k , KB , M , Gb ,
-and so forth, up to
-.Sy Z
-for zettabyte). The following are all valid (and equal) specifications:
-.Bd -ragged -offset 4n
-1536M, 1.5g, 1.50GB
-.Ed
-.Pp
-The values of non-numeric properties are case sensitive and must be lowercase,
-except for
-.Sy mountpoint , sharenfs , No and Sy sharesmb .
-.Pp
-The following native properties consist of read-only statistics about the
-dataset. These properties can be neither set, nor inherited. Native properties
-apply to all dataset types unless otherwise noted.
-.Bl -tag -width 2n
-.It Sy available
-The amount of space available to the dataset and all its children, assuming
-that there is no other activity in the pool. Because space is shared within a
-pool, availability can be limited by any number of factors, including physical
-pool size, quotas, reservations, or other datasets within the pool.
-.Pp
-This property can also be referred to by its shortened column name,
-.Sy avail .
-.It Sy compressratio
-For non-snapshots, the compression ratio achieved for the
-.Sy used
-space of this dataset, expressed as a multiplier. The
-.Sy used
-property includes descendant datasets, and, for clones, does not include
-the space shared with the origin snapshot. For snapshots, the
-.Sy compressratio
-is the same as the
-.Sy refcompressratio
-property. Compression can be turned on by running:
-.Qq Nm Cm set compression=on Ar dataset
-The default value is
-.Cm off .
-.It Sy createtxg
-The transaction group (txg) in which the dataset was created.
-Bookmarks have the same
-.Sy createtxg
-as the snapshot they are initially tied to.
-This property is suitable for ordering a list of snapshots,
-e.g. for incremental send and receive.
-.It Sy creation
-The time this dataset was created.
-.It Sy clones
-For snapshots, this property is a comma-separated list of filesystems or
-volumes which are clones of this snapshot. The clones'
-.Sy origin
-property is this snapshot. If the
-.Sy clones
-property is not empty, then this snapshot can not be destroyed (even with the
-.Fl r
-or
-.Fl f
-options).
-.It Sy defer_destroy
-This property is
-.Cm on
-if the snapshot has been marked for deferred destroy by using the
-.Qq Nm Cm destroy -d
-command. Otherwise, the property is
-.Cm off .
-.It Sy filesystem_count
-The total number of filesystems and volumes that exist under this location in the
-dataset tree.
-This value is only available when a
-.Sy filesystem_limit
-has
-been set somewhere in the tree under which the dataset resides.
-.It Sy guid
-The 64 bit GUID of this dataset or bookmark which does not change over its
-entire lifetime.
-When a snapshot is sent to another pool, the received snapshot has the same
-GUID.
-Thus, the
-.Sy guid
-is suitable to identify a snapshot across pools.
-.It Sy logicalreferenced
-The amount of space that is
-.Qq logically
-accessible by this dataset.
-See the
-.Sy referenced
-property.
-The logical space ignores the effect of the
-.Sy compression
-and
-.Sy copies
-properties, giving a quantity closer to the amount of data that applications
-see.
-However, it does include space consumed by metadata.
-.Pp
-This property can also be referred to by its shortened column name,
-.Sy lrefer .
-.It Sy logicalused
-The amount of space that is
-.Qq logically
-consumed by this dataset and all its descendents.
-See the
-.Sy used
-property.
-The logical space ignores the effect of the
-.Sy compression
-and
-.Sy copies
-properties, giving a quantity closer to the amount of data that applications
-see.
-.Pp
-This property can also be referred to by its shortened column name,
-.Sy lused .
-.It Sy mounted
-For file systems, indicates whether the file system is currently mounted. This
-property can be either
-.Cm yes
-or
-.Cm no .
-.It Sy origin
-For cloned file systems or volumes, the snapshot from which the clone was
-created. See also the
-.Sy clones
-property.
-.It Sy receive_resume_token
-For filesystems or volumes which have saved partially-completed state from
-.Sy zfs receive -s ,
-this opaque token can be provided to
-.Sy zfs send -t
-to resume and complete the
-.Sy zfs receive .
-.It Sy referenced
-The amount of data that is accessible by this dataset, which may or may not be
-shared with other datasets in the pool. When a snapshot or clone is created, it
-initially references the same amount of space as the file system or snapshot it
-was created from, since its contents are identical.
-.Pp
-This property can also be referred to by its shortened column name,
-.Sy refer .
-.It Sy refcompressratio
-The compression ratio achieved for the
-.Sy referenced
-space of this dataset, expressed as a multiplier. See also the
-.Sy compressratio
-property.
-.It Sy snapshot_count
-The total number of snapshots that exist under this location in the dataset tree.
-This value is only available when a
-.Sy snapshot_limit
-has been set somewhere
-in the tree under which the dataset resides.
-.It Sy type
-The type of dataset:
-.Sy filesystem , volume , No or Sy snapshot .
-.It Sy used
-The amount of space consumed by this dataset and all its descendents. This is
-the value that is checked against this dataset's quota and reservation. The
-space used does not include this dataset's reservation, but does take into
-account the reservations of any descendent datasets. The amount of space that a
-dataset consumes from its parent, as well as the amount of space that are freed
-if this dataset is recursively destroyed, is the greater of its space used and
-its reservation.
-.Pp
-When snapshots (see the
-.Qq Sx Snapshots
-section) are created, their space is
-initially shared between the snapshot and the file system, and possibly with
-previous snapshots. As the file system changes, space that was previously
-shared becomes unique to the snapshot, and counted in the snapshot's space
-used. Additionally, deleting snapshots can increase the amount of space unique
-to (and used by) other snapshots.
-.Pp
-The amount of space used, available, or referenced does not take into account
-pending changes. Pending changes are generally accounted for within a few
-seconds. Committing a change to a disk using
-.Xr fsync 2
-or
-.Sy O_SYNC
-does not necessarily guarantee that the space usage information is updated
-immediately.
-.It Sy usedby*
-The
-.Sy usedby*
-properties decompose the
-.Sy used
-properties into the various reasons that space is used. Specifically,
-.Sy used No =
-.Sy usedbysnapshots + usedbydataset + usedbychildren + usedbyrefreservation .
-These properties are only available for datasets created
-with
-.Tn ZFS
-pool version 13 pools and higher.
-.It Sy usedbysnapshots
-The amount of space consumed by snapshots of this dataset. In particular, it is
-the amount of space that would be freed if all of this dataset's snapshots were
-destroyed. Note that this is not simply the sum of the snapshots'
-.Sy used
-properties because space can be shared by multiple snapshots.
-.It Sy usedbydataset
-The amount of space used by this dataset itself, which would be freed if the
-dataset were destroyed (after first removing any
-.Sy refreservation
-and destroying any necessary snapshots or descendents).
-.It Sy usedbychildren
-The amount of space used by children of this dataset, which would be freed if
-all the dataset's children were destroyed.
-.It Sy usedbyrefreservation
-The amount of space used by a
-.Sy refreservation
-set on this dataset, which would be freed if the
-.Sy refreservation
-was removed.
-.It Sy userused@ Ns Ar user
-The amount of space consumed by the specified user in this dataset. Space is
-charged to the owner of each file, as displayed by
-.Qq Nm ls Fl l .
-The amount of space charged is displayed by
-.Qq Nm du
-and
-.Qq Nm ls Fl s .
-See the
-.Qq Nm Cm userspace
-subcommand for more information.
-.Pp
-Unprivileged users can access only their own space usage. The root user, or a
-user who has been granted the
-.Sy userused
-privilege with
-.Qq Nm Cm allow ,
-can access everyone's usage.
-.Pp
-The
-.Sy userused@ Ns ...
-properties are not displayed by
-.Qq Nm Cm get all .
-The user's name must be appended after the
-.Sy @
-symbol, using one of the following forms:
-.Bl -bullet -offset 2n
-.It
-POSIX name (for example,
-.Em joe )
-.It
-POSIX numeric ID (for example,
-.Em 1001 )
-.El
-.It Sy userrefs
-This property is set to the number of user holds on this snapshot. User holds
-are set by using the
-.Qq Nm Cm hold
-command.
-.It Sy groupused@ Ns Ar group
-The amount of space consumed by the specified group in this dataset. Space is
-charged to the group of each file, as displayed by
-.Nm ls Fl l .
-See the
-.Sy userused@ Ns Ar user
-property for more information.
-.Pp
-Unprivileged users can only access their own groups' space usage. The root
-user, or a user who has been granted the
-.Sy groupused
-privilege with
-.Qq Nm Cm allow ,
-can access all groups' usage.
-.It Sy volblocksize Ns = Ns Ar blocksize
-For volumes, specifies the block size of the volume. The
-.Ar blocksize
-cannot be changed once the volume has been written, so it should be set at
-volume creation time. The default
-.Ar blocksize
-for volumes is 8 Kbytes. Any
-power of 2 from 512 bytes to 128 Kbytes is valid.
-.Pp
-This property can also be referred to by its shortened column name,
-.Sy volblock .
-.It Sy written
-The amount of
-.Sy referenced
-space written to this dataset since the previous snapshot.
-.It Sy written@ Ns Ar snapshot
-The amount of
-.Sy referenced
-space written to this dataset since the specified snapshot. This is the space
-that is referenced by this dataset but was not referenced by the specified
-snapshot.
-.Pp
-The
-.Ar snapshot
-may be specified as a short snapshot name (just the part after the
-.Sy @ ) ,
-in which case it will be interpreted as a snapshot in the same filesystem as
-this dataset. The
-.Ar snapshot
-may be a full snapshot name
-.Pq Em filesystem@snapshot ,
-which for clones may be a snapshot in the origin's filesystem (or the origin of
-the origin's filesystem, etc).
-.El
-.Pp
-The following native properties can be used to change the behavior of a
-.Tn ZFS
-dataset.
-.Bl -tag -width 2n
-.It Xo
-.Sy aclinherit Ns = Ns Cm discard |
-.Cm noallow |
-.Cm restricted |
-.Cm passthrough |
-.Cm passthrough-x
-.Xc
-Controls how
-.Tn ACL
-entries are inherited when files and directories are created. A file system
-with an
-.Sy aclinherit
-property of
-.Cm discard
-does not inherit any
-.Tn ACL
-entries. A file system with an
-.Sy aclinherit
-property value of
-.Cm noallow
-only inherits inheritable
-.Tn ACL
-entries that specify "deny" permissions. The property value
-.Cm restricted
-(the default) removes the
-.Em write_acl
-and
-.Em write_owner
-permissions when the
-.Tn ACL
-entry is inherited. A file system with an
-.Sy aclinherit
-property value of
-.Cm passthrough
-inherits all inheritable
-.Tn ACL
-entries without any modifications made to the
-.Tn ACL
-entries when they are inherited. A file system with an
-.Sy aclinherit
-property value of
-.Cm passthrough-x
-has the same meaning as
-.Cm passthrough ,
-except that the
-.Em owner@ , group@ , No and Em everyone@ Tn ACE Ns s
-inherit the execute permission only if the file creation mode also requests the
-execute bit.
-.Pp
-When the property value is set to
-.Cm passthrough ,
-files are created with a mode determined by the inheritable
-.Tn ACE Ns s.
-If no inheritable
-.Tn ACE Ns s
-exist that affect the mode, then the mode is set in accordance to the requested
-mode from the application.
-.It Sy aclmode Ns = Ns Cm discard | groupmask | passthrough | restricted
-Controls how an
-.Tn ACL
-is modified during
-.Xr chmod 2 .
-A file system with an
-.Sy aclmode
-property of
-.Cm discard
-(the default) deletes all
-.Tn ACL
-entries that do not represent the mode of the file. An
-.Sy aclmode
-property of
-.Cm groupmask
-reduces permissions granted in all
-.Em ALLOW
-entries found in the
-.Tn ACL
-such that they are no greater than the group permissions specified by
-.Xr chmod 2 .
-A file system with an
-.Sy aclmode
-property of
-.Cm passthrough
-indicates that no changes are made to the
-.Tn ACL
-other than creating or updating the necessary
-.Tn ACL
-entries to represent the new mode of the file or directory.
-An
-.Sy aclmode
-property of
-.Cm restricted
-will cause the
-.Xr chmod 2
-operation to return an error when used on any file or directory which has
-a non-trivial
-.Tn ACL
-whose entries can not be represented by a mode.
-.Xr chmod 2
-is required to change the set user ID, set group ID, or sticky bits on a file
-or directory, as they do not have equivalent
-.Tn ACL
-entries.
-In order to use
-.Xr chmod 2
-on a file or directory with a non-trivial
-.Tn ACL
-when
-.Sy aclmode
-is set to
-.Cm restricted ,
-you must first remove all
-.Tn ACL
-entries which do not represent the current mode.
-.It Sy atime Ns = Ns Cm on | off
-Controls whether the access time for files is updated when they are read.
-Turning this property off avoids producing write traffic when reading files and
-can result in significant performance gains, though it might confuse mailers
-and other similar utilities. The default value is
-.Cm on .
-.It Sy canmount Ns = Ns Cm on | off | noauto
-If this property is set to
-.Cm off ,
-the file system cannot be mounted, and is ignored by
-.Qq Nm Cm mount Fl a .
-Setting this property to
-.Cm off
-is similar to setting the
-.Sy mountpoint
-property to
-.Cm none ,
-except that the dataset still has a normal
-.Sy mountpoint
-property, which can be inherited. Setting this property to
-.Cm off
-allows datasets to be used solely as a mechanism to inherit properties. One
-example of setting
-.Sy canmount Ns = Ns Cm off
-is to have two datasets with the same
-.Sy mountpoint ,
-so that the children of both datasets appear in the same directory, but might
-have different inherited characteristics.
-.Pp
-When the
-.Cm noauto
-value is set, a dataset can only be mounted and unmounted explicitly. The
-dataset is not mounted automatically when the dataset is created or imported,
-nor is it mounted by the
-.Qq Nm Cm mount Fl a
-command or unmounted by the
-.Qq Nm Cm umount Fl a
-command.
-.Pp
-This property is not inherited.
-.It Sy checksum Ns = Ns Cm on | off | fletcher2 | fletcher4 | sha256 | noparity | sha512 | skein
-Controls the checksum used to verify data integrity. The default value is
-.Cm on ,
-which automatically selects an appropriate algorithm (currently,
-.Cm fletcher4 ,
-but this may change in future releases). The value
-.Cm off
-disables integrity checking on user data.
-The value
-.Cm noparity
-not only
-disables integrity but also disables maintaining parity for user data. This
-setting is used internally by a dump device residing on a RAID-Z pool and should
-not be used by any other dataset.
-Disabling checksums is
-.Em NOT
-a recommended practice.
-The
-.Sy sha512 ,
-and
-.Sy skein
-checksum algorithms require enabling the appropriate features on the pool.
-Please see
-.Xr zpool-features 7
-for more information on these algorithms.
-.Pp
-Changing this property affects only newly-written data.
-.Pp
-The salted checksum algorithm
-.Pq Cm edonr
-is currently not supported on FreeBSD.
-.It Sy compression Ns = Ns Cm on | off | lzjb | gzip | gzip- Ns Ar N | Cm zle | Cm lz4
-Controls the compression algorithm used for this dataset.
-Setting compression to
-.Cm on
-indicates that the current default compression algorithm should be used.
-The default balances compression and decompression speed, with compression
-ratio and is expected to work well on a wide variety of workloads.
-Unlike all other settings for this property, on does not select a fixed
-compression type.
-As new compression algorithms are added to ZFS and enabled on a pool, the
-default compression algorithm may change.
-The current default compression algorthm is either
-.Cm lzjb
-or, if the
-.Sy lz4_compress
-feature is enabled,
-.Cm lz4 .
-The
-.Cm lzjb
-compression algorithm is optimized for performance while providing decent data
-compression. Setting compression to
-.Cm on
-uses the
-.Cm lzjb
-compression algorithm. The
-.Cm gzip
-compression algorithm uses the same compression as the
-.Xr gzip 1
-command. You can specify the
-.Cm gzip
-level by using the value
-.Cm gzip- Ns Ar N
-where
-.Ar N
-is an integer from 1 (fastest) to 9 (best compression ratio). Currently,
-.Cm gzip
-is equivalent to
-.Cm gzip-6
-(which is also the default for
-.Xr gzip 1 ) .
-The
-.Cm zle
-compression algorithm compresses runs of zeros.
-.Pp
-The
-.Sy lz4
-compression algorithm is a high-performance replacement
-for the
-.Sy lzjb
-algorithm. It features significantly faster
-compression and decompression, as well as a moderately higher
-compression ratio than
-.Sy lzjb ,
-but can only be used on pools with
-the
-.Sy lz4_compress
-feature set to
-.Sy enabled .
-See
-.Xr zpool-features 7
-for details on ZFS feature flags and the
-.Sy lz4_compress
-feature.
-.Pp
-This property can also be referred to by its shortened column name
-.Cm compress .
-Changing this property affects only newly-written data.
-.It Sy copies Ns = Ns Cm 1 | 2 | 3
-Controls the number of copies of data stored for this dataset. These copies are
-in addition to any redundancy provided by the pool, for example, mirroring or
-RAID-Z. The copies are stored on different disks, if possible. The space used
-by multiple copies is charged to the associated file and dataset, changing the
-.Sy used
-property and counting against quotas and reservations.
-.Pp
-Changing this property only affects newly-written data. Therefore, set this
-property at file system creation time by using the
-.Fl o Cm copies= Ns Ar N
-option.
-.It Sy dedup Ns = Ns Cm on | off | verify | sha256 Ns Oo Cm ,verify Oc | Sy sha512 Ns Oo Cm ,verify Oc | Sy skein Ns Oo Cm ,verify Oc
-Configures deduplication for a dataset. The default value is
-.Cm off .
-The default deduplication checksum is
-.Cm sha256
-(this may change in the future).
-When
-.Sy dedup
-is enabled, the checksum defined here overrides the
-.Sy checksum
-property. Setting the value to
-.Cm verify
-has the same effect as the setting
-.Cm sha256,verify .
-.Pp
-If set to
-.Cm verify ,
-.Tn ZFS
-will do a byte-to-byte comparsion in case of two blocks having the same
-signature to make sure the block contents are identical.
-.It Sy devices Ns = Ns Cm on | off
-The
-.Sy devices
-property is currently not supported on
-.Fx .
-.It Sy exec Ns = Ns Cm on | off
-Controls whether processes can be executed from within this file system. The
-default value is
-.Cm on .
-.It Sy mlslabel Ns = Ns Ar label | Cm none
-The
-.Sy mlslabel
-property is currently not supported on
-.Fx .
-.It Sy filesystem_limit Ns = Ns Ar count | Cm none
-Limits the number of filesystems and volumes that can exist under this point in
-the dataset tree.
-The limit is not enforced if the user is allowed to change
-the limit.
-Setting a
-.Sy filesystem_limit
-on a descendent of a filesystem that
-already has a
-.Sy filesystem_limit
-does not override the ancestor's
-.Sy filesystem_limit ,
-but rather imposes an additional limit.
-This feature must be enabled to be used
-.Po see
-.Xr zpool-features 7
-.Pc .
-.It Sy special_small_blocks Ns = Ns Ar size
-This value represents the threshold block size for including small file
-blocks into the special allocation class.
-Blocks smaller than or equal to this value will be assigned to the special
-allocation class while greater blocks will be assigned to the regular class.
-Valid values are zero or a power of two from 512B up to 128K.
-The default size is 0 which means no small file blocks will be allocated in
-the special class.
-.Pp
-Before setting this property, a special class vdev must be added to the
-pool.
-See
-.Xr zpool 8
-for more details on the special allocation class.
-.It Sy mountpoint Ns = Ns Ar path | Cm none | legacy
-Controls the mount point used for this file system.
-See the
-.Qq Sx Mount Points
-section for more information on how this property is used.
-.Pp
-When the
-.Sy mountpoint
-property is changed for a file system, the file system and any children that
-inherit the mount point are unmounted. If the new value is
-.Cm legacy ,
-then they remain unmounted. Otherwise, they are automatically remounted in the
-new location if the property was previously
-.Cm legacy
-or
-.Cm none ,
-or if they were mounted before the property was changed. In addition, any
-shared file systems are unshared and shared in the new location.
-.It Sy nbmand Ns = Ns Cm on | off
-The
-.Sy nbmand
-property is currently not supported on
-.Fx .
-.It Sy primarycache Ns = Ns Cm all | none | metadata
-Controls what is cached in the primary cache (ARC). If this property is set to
-.Cm all ,
-then both user data and metadata is cached. If this property is set to
-.Cm none ,
-then neither user data nor metadata is cached. If this property is set to
-.Cm metadata ,
-then only metadata is cached. The default value is
-.Cm all .
-.It Sy quota Ns = Ns Ar size | Cm none
-Limits the amount of space a dataset and its descendents can consume. This
-property enforces a hard limit on the amount of space used. This includes all
-space consumed by descendents, including file systems and snapshots. Setting a
-quota on a descendent of a dataset that already has a quota does not override
-the ancestor's quota, but rather imposes an additional limit.
-.Pp
-Quotas cannot be set on volumes, as the
-.Sy volsize
-property acts as an implicit quota.
-.It Sy snapshot_limit Ns = Ns Ar count | Cm none
-Limits the number of snapshots that can be created on a dataset and its
-descendents.
-Setting a
-.Sy snapshot_limit
-on a descendent of a dataset that already
-has a
-.Sy snapshot_limit
-does not override the ancestor's
-.Sy snapshot_limit ,
-but
-rather imposes an additional limit.
-The limit is not enforced if the user is
-allowed to change the limit.
-For example, this means that recursive snapshots
-taken from the global zone are counted against each delegated dataset within
-a jail.
-This feature must be enabled to be used
-.Po see
-.Xr zpool-features 7
-.Pc .
-.It Sy userquota@ Ns Ar user Ns = Ns Ar size | Cm none
-Limits the amount of space consumed by the specified user.
-Similar to the
-.Sy refquota
-property, the
-.Sy userquota
-space calculation does not include space that is used by descendent datasets,
-such as snapshots and clones. User space consumption is identified by the
-.Sy userspace@ Ns Ar user
-property.
-.Pp
-Enforcement of user quotas may be delayed by several seconds. This delay means
-that a user might exceed their quota before the system notices that they are
-over quota and begins to refuse additional writes with the
-.Em EDQUOT
-error message. See the
-.Cm userspace
-subcommand for more information.
-.Pp
-Unprivileged users can only access their own groups' space usage. The root
-user, or a user who has been granted the
-.Sy userquota
-privilege with
-.Qq Nm Cm allow ,
-can get and set everyone's quota.
-.Pp
-This property is not available on volumes, on file systems before version 4, or
-on pools before version 15. The
-.Sy userquota@ Ns ...
-properties are not displayed by
-.Qq Nm Cm get all .
-The user's name must be appended after the
-.Sy @
-symbol, using one of the following forms:
-.Bl -bullet -offset 2n
-.It
-POSIX name (for example,
-.Em joe )
-.It
-POSIX numeric ID (for example,
-.Em 1001 )
-.El
-.It Sy groupquota@ Ns Ar group Ns = Ns Ar size | Cm none
-Limits the amount of space consumed by the specified group. Group space
-consumption is identified by the
-.Sy userquota@ Ns Ar user
-property.
-.Pp
-Unprivileged users can access only their own groups' space usage. The root
-user, or a user who has been granted the
-.Sy groupquota
-privilege with
-.Qq Nm Cm allow ,
-can get and set all groups' quotas.
-.It Sy readonly Ns = Ns Cm on | off
-Controls whether this dataset can be modified. The default value is
-.Cm off .
-.It Sy recordsize Ns = Ns Ar size
-Specifies a suggested block size for files in the file system. This property is
-designed solely for use with database workloads that access files in fixed-size
-records.
-.Tn ZFS
-automatically tunes block sizes according to internal algorithms optimized for
-typical access patterns.
-.Pp
-For databases that create very large files but access them in small random
-chunks, these algorithms may be suboptimal. Specifying a
-.Sy recordsize
-greater than or equal to the record size of the database can result in
-significant performance gains. Use of this property for general purpose file
-systems is strongly discouraged, and may adversely affect performance.
-.Pp
-The size specified must be a power of two greater than or equal to 512 and less
-than or equal to 128 Kbytes.
-If the
-.Sy large_blocks
-feature is enabled on the pool, the size may be up to 1 Mbyte.
-See
-.Xr zpool-features 7
-for details on ZFS feature flags.
-.Pp
-Changing the file system's
-.Sy recordsize
-affects only files created afterward; existing files are unaffected.
-.Pp
-This property can also be referred to by its shortened column name,
-.Sy recsize .
-.It Sy redundant_metadata Ns = Ns Cm all | most
-Controls what types of metadata are stored redundantly.
-ZFS stores an extra copy of metadata, so that if a single block is corrupted,
-the amount of user data lost is limited.
-This extra copy is in addition to any redundancy provided at the pool level
-.Pq e.g. by mirroring or RAID-Z ,
-and is in addition to an extra copy specified by the
-.Sy copies
-property
-.Pq up to a total of 3 copies .
-For example if the pool is mirrored,
-.Cm copies Ns = Ns Ar 2 ,
-and
-.Cm redundant_metadata Ns = Ns Ar most ,
-then ZFS
-stores 6 copies of most metadata, and 4 copies of data and some
-metadata.
-.Pp
-When set to
-.Cm all ,
-ZFS stores an extra copy of all metadata.
-If a
-single on-disk block is corrupt, at worst a single block of user data
-.Po which is
-.Cm recordsize
-bytes long
-can be lost.
-.Pc
-.Pp
-When set to
-.Cm most ,
-ZFS stores an extra copy of most types of
-metadata.
-This can improve performance of random writes, because less
-metadata must be written.
-In practice, at worst about 100 blocks
-.Po of
-.Cm recordsize
-bytes each
-.Pc
-of user data can be lost if a single
-on-disk block is corrupt.
-The exact behavior of which metadata blocks
-are stored redundantly may change in future releases.
-.Pp
-The default value is
-.Cm all .
-.It Sy refquota Ns = Ns Ar size | Cm none
-Limits the amount of space a dataset can consume. This property enforces a hard
-limit on the amount of space used. This hard limit does not include space used
-by descendents, including file systems and snapshots.
-.It Sy refreservation Ns = Ns Ar size | Cm none | Cm auto
-The minimum amount of space guaranteed to a dataset, not including its
-descendents. When the amount of space used is below this value, the dataset is
-treated as if it were taking up the amount of space specified by
-.Sy refreservation .
-The
-.Sy refreservation
-reservation is accounted for in the parent datasets' space used, and counts
-against the parent datasets' quotas and reservations.
-.Pp
-If
-.Sy refreservation
-is set, a snapshot is only allowed if there is enough free pool space outside
-of this reservation to accommodate the current number of "referenced" bytes in
-the dataset.
-.Pp
-If
-.Sy refreservation
-is set to
-.Sy auto ,
-a volume is thick provisioned or not sparse.
-.Sy refreservation Ns = Cm auto
-is only supported on volumes.
-See
-.Sy volsize
-in the Native Properties
-section for more information about sparse volumes.
-.Pp
-This property can also be referred to by its shortened column name,
-.Sy refreserv .
-.It Sy reservation Ns = Ns Ar size | Cm none
-The minimum amount of space guaranteed to a dataset and its descendents. When
-the amount of space used is below this value, the dataset is treated as if it
-were taking up the amount of space specified by its reservation. Reservations
-are accounted for in the parent datasets' space used, and count against the
-parent datasets' quotas and reservations.
-.Pp
-This property can also be referred to by its shortened column name,
-.Sy reserv .
-.It Sy secondarycache Ns = Ns Cm all | none | metadata
-Controls what is cached in the secondary cache (L2ARC). If this property is set
-to
-.Cm all ,
-then both user data and metadata is cached. If this property is set to
-.Cm none ,
-then neither user data nor metadata is cached. If this property is set to
-.Cm metadata ,
-then only metadata is cached. The default value is
-.Cm all .
-.It Sy setuid Ns = Ns Cm on | off
-Controls whether the
-.No set- Ns Tn UID
-bit is respected for the file system. The default value is
-.Cm on .
-.It Sy sharesmb Ns = Ns Cm on | off | Ar opts
-The
-.Sy sharesmb
-property currently has no effect on
-.Fx .
-.It Sy sharenfs Ns = Ns Cm on | off | Ar opts
-Controls whether the file system is shared via
-.Tn NFS ,
-and what options are used. A file system with a
-.Sy sharenfs
-property of
-.Cm off
-is managed the traditional way via
-.Xr exports 5 .
-Otherwise, the file system is automatically shared and unshared with the
-.Qq Nm Cm share
-and
-.Qq Nm Cm unshare
-commands. If the property is set to
-.Cm on
-no
-.Tn NFS
-export options are used. Otherwise,
-.Tn NFS
-export options are equivalent to the contents of this property. The export
-options may be comma-separated. See
-.Xr exports 5
-for a list of valid options.
-.Pp
-When the
-.Sy sharenfs
-property is changed for a dataset, the
-.Xr mountd 8
-daemon is reloaded.
-.It Sy logbias Ns = Ns Cm latency | throughput
-Provide a hint to
-.Tn ZFS
-about handling of synchronous requests in this dataset.
-If
-.Sy logbias
-is set to
-.Cm latency
-(the default),
-.Tn ZFS
-will use pool log devices (if configured) to handle the requests at low
-latency. If
-.Sy logbias
-is set to
-.Cm throughput ,
-.Tn ZFS
-will not use configured pool log devices.
-.Tn ZFS
-will instead optimize synchronous operations for global pool throughput and
-efficient use of resources.
-.It Sy snapdir Ns = Ns Cm hidden | visible
-Controls whether the
-.Pa \&.zfs
-directory is hidden or visible in the root of the file system as discussed in
-the
-.Qq Sx Snapshots
-section. The default value is
-.Cm hidden .
-.It Sy sync Ns = Ns Cm standard | always | disabled
-Controls the behavior of synchronous requests (e.g.
-.Xr fsync 2 ,
-O_DSYNC). This property accepts the following values:
-.Bl -tag -offset 4n -width 8n
-.It Sy standard
-This is the POSIX specified behavior of ensuring all synchronous requests are
-written to stable storage and all devices are flushed to ensure data is not
-cached by device controllers (this is the default).
-.It Sy always
-All file system transactions are written and flushed before their system calls
-return. This has a large performance penalty.
-.It Sy disabled
-Disables synchronous requests. File system transactions are only committed to
-stable storage periodically. This option will give the highest performance.
-However, it is very dangerous as
-.Tn ZFS
-would be ignoring the synchronous transaction demands of applications such as
-databases or
-.Tn NFS .
-Administrators should only use this option when the risks are understood.
-.El
-.It Sy volsize Ns = Ns Ar size
-For volumes, specifies the logical size of the volume. By default, creating a
-volume establishes a reservation of equal size. For storage pools with a
-version number of 9 or higher, a
-.Sy refreservation
-is set instead. Any changes to
-.Sy volsize
-are reflected in an equivalent change to the reservation (or
-.Sy refreservation ) .
-The
-.Sy volsize
-can only be set to a multiple of
-.Cm volblocksize ,
-and cannot be zero.
-.Pp
-The reservation is kept equal to the volume's logical size to prevent
-unexpected behavior for consumers. Without the reservation, the volume could
-run out of space, resulting in undefined behavior or data corruption, depending
-on how the volume is used. These effects can also occur when the volume size is
-changed while it is in use (particularly when shrinking the size). Extreme care
-should be used when adjusting the volume size.
-.Pp
-Though not recommended, a "sparse volume" (also known as "thin provisioned")
-can be created by specifying the
-.Fl s
-option to the
-.Qq Nm Cm create Fl V
-command, or by changing the value of the
-.Sy refreservation
-property, or
-.Sy reservation
-property on pool
-.Po
-version 8 or earlier
-.Pc
-after the volume has been created.
-A "sparse volume" is a volume where the value of
-.Sy refreservation
-is less then the size of the volume plus the space required to store its
-metadata.
-Consequently, writes to a sparse volume can fail with
-.Sy ENOSPC
-when the pool is low on space. For a sparse volume, changes to
-.Sy volsize
-are not reflected in the
-.Sy refreservation .
-A volume that is not sparse is said to be "thick provisioned".
-A sparse volume can become thick provisioned by setting
-.Sy refreservation
-to
-.Sy auto .
-.It Sy volmode Ns = Ns Cm default | geom | dev | none
-This property specifies how volumes should be exposed to the OS.
-Setting it to
-.Sy geom
-exposes volumes as
-.Xr geom 4
-providers, providing maximal functionality.
-Setting it to
-.Sy dev
-exposes volumes only as cdev device in devfs.
-Such volumes can be accessed only as raw disk device files, i.e. they
-can not be partitioned, mounted, participate in RAIDs, etc, but they
-are faster, and in some use scenarios with untrusted consumer, such as
-NAS or VM storage, can be more safe.
-Volumes with property set to
-.Sy none
-are not exposed outside ZFS, but can be snapshoted, cloned, replicated, etc,
-that can be suitable for backup purposes.
-Value
-.Sy default
-means that volumes exposition is controlled by system-wide sysctl/tunable
-.Va vfs.zfs.vol.mode ,
-where
-.Sy geom ,
-.Sy dev
-and
-.Sy none
-are encoded as 1, 2 and 3 respectively.
-The default values is
-.Sy geom .
-This property can be changed any time, but so far it is processed only
-during volume creation and pool import.
-.It Sy vscan Ns = Ns Cm off | on
-The
-.Sy vscan
-property is currently not supported on
-.Fx .
-.It Sy xattr Ns = Ns Cm off | on
-The
-.Sy xattr
-property is currently not supported on
-.Fx .
-.It Sy jailed Ns = Ns Cm off | on
-Controls whether the dataset is managed from a jail. See the
-.Qq Sx Jails
-section for more information. The default value is
-.Cm off .
-.El
-.Pp
-The following three properties cannot be changed after the file system is
-created, and therefore, should be set when the file system is created. If the
-properties are not set with the
-.Qq Nm Cm create
-or
-.Nm zpool Cm create
-commands, these properties are inherited from the parent dataset. If the parent
-dataset lacks these properties due to having been created prior to these
-features being supported, the new file system will have the default values for
-these properties.
-.Bl -tag -width 4n
-.It Sy casesensitivity Ns = Ns Cm sensitive | insensitive | mixed
-Indicates whether the file name matching algorithm used by the file system
-should be case-sensitive, case-insensitive, or allow a combination of both
-styles of matching. The default value for the
-.Sy casesensitivity
-property is
-.Cm sensitive .
-Traditionally, UNIX and POSIX file systems have case-sensitive file names.
-.Pp
-The
-.Cm mixed
-value for the
-.Sy casesensitivity
-property indicates that the
-file system can support requests for both case-sensitive and case-insensitive
-matching behavior.
-.It Sy normalization Ns = Ns Cm none | formC | formD | formKC | formKD
-Indicates whether the file system should perform a
-.Sy unicode
-normalization of file names whenever two file names are compared, and which
-normalization algorithm should be used. File names are always stored
-unmodified, names are normalized as part of any comparison process. If this
-property is set to a legal value other than
-.Cm none ,
-and the
-.Sy utf8only
-property was left unspecified, the
-.Sy utf8only
-property is automatically set to
-.Cm on .
-The default value of the
-.Sy normalization
-property is
-.Cm none .
-This property cannot be changed after the file system is created.
-.It Sy utf8only Ns = Ns Cm on | off
-Indicates whether the file system should reject file names that include
-characters that are not present in the
-.Sy UTF-8
-character code set. If this property is explicitly set to
-.Cm off ,
-the normalization property must either not be explicitly set or be set to
-.Cm none .
-The default value for the
-.Sy utf8only
-property is
-.Cm off .
-This property cannot be changed after the file system is created.
-.El
-.Pp
-The
-.Sy casesensitivity , normalization , No and Sy utf8only
-properties are also new permissions that can be assigned to non-privileged
-users by using the
-.Tn ZFS
-delegated administration feature.
-.Ss Temporary Mount Point Properties
-When a file system is mounted, either through
-.Xr mount 8
-for legacy mounts or the
-.Qq Nm Cm mount
-command for normal file systems, its mount options are set according to its
-properties. The correlation between properties and mount options is as follows:
-.Bl -column -offset 4n "PROPERTY" "MOUNT OPTION"
-.It "PROPERTY MOUNT OPTION"
-.It "atime atime/noatime"
-.It "exec exec/noexec"
-.It "readonly ro/rw"
-.It "setuid suid/nosuid"
-.El
-.Pp
-In addition, these options can be set on a per-mount basis using the
-.Fl o
-option, without affecting the property that is stored on disk. The values
-specified on the command line override the values stored in the dataset. These
-properties are reported as "temporary" by the
-.Qq Nm Cm get
-command. If the properties are changed while the dataset is mounted, the new
-setting overrides any temporary settings.
-.Ss User Properties
-In addition to the standard native properties,
-.Tn ZFS
-supports arbitrary user properties. User properties have no effect on
-.Tn ZFS
-behavior, but applications or administrators can use them to annotate datasets
-(file systems, volumes, and snapshots).
-.Pp
-User property names must contain a colon
-.Pq Sy \&:
-character to distinguish them from native properties. They may contain
-lowercase letters, numbers, and the following punctuation characters: colon
-.Pq Sy \&: ,
-dash
-.Pq Sy \&- ,
-period
-.Pq Sy \&.
-and underscore
-.Pq Sy \&_ .
-The expected convention is that the property name is divided into two portions
-such as
-.Em module Ns Sy \&: Ns Em property ,
-but this namespace is not enforced by
-.Tn ZFS .
-User property names can be at most 256 characters, and cannot begin with a dash
-.Pq Sy \&- .
-.Pp
-When making programmatic use of user properties, it is strongly suggested to
-use a reversed
-.Tn DNS
-domain name for the
-.Ar module
-component of property names to reduce the chance that two
-independently-developed packages use the same property name for different
-purposes. Property names beginning with
-.Em com.sun
-are reserved for use by Sun Microsystems.
-.Pp
-The values of user properties are arbitrary strings, are always inherited, and
-are never validated. All of the commands that operate on properties
-.Po
-.Qq Nm Cm list ,
-.Qq Nm Cm get ,
-.Qq Nm Cm set
-and so forth
-.Pc
-can be used to manipulate both native properties and user properties. Use the
-.Qq Nm Cm inherit
-command to clear a user property. If the property is not defined in any parent
-dataset, it is removed entirely. Property values are limited to 1024
-characters.
-.Sh SUBCOMMANDS
-All subcommands that modify state are logged persistently to the pool in their
-original form.
-.Bl -tag -width 2n
-.It Xo
-.Nm
-.Op Fl \&?
-.Xc
-.Pp
-Displays a help message.
-.It Xo
-.Nm
-.Cm create
-.Op Fl pu
-.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
-.Ar filesystem
-.Xc
-.Pp
-Creates a new
-.Tn ZFS
-file system. The file system is automatically mounted according to the
-.Sy mountpoint
-property inherited from the parent.
-.Bl -tag -width indent
-.It Fl p
-Creates all the non-existing parent datasets. Datasets created in this manner
-are automatically mounted according to the
-.Sy mountpoint
-property inherited from their parent. Any property specified on the command
-line using the
-.Fl o
-option is ignored. If the target filesystem already exists, the operation
-completes successfully.
-.It Fl u
-Newly created file system is not mounted.
-.It Fl o Ar property Ns = Ns Ar value
-Sets the specified property as if the command
-.Qq Nm Cm set Ar property Ns = Ns Ar value
-was invoked at the same time the dataset was created. Any editable
-.Tn ZFS
-property can also be set at creation time. Multiple
-.Fl o
-options can be specified. An error results if the same property is specified in
-multiple
-.Fl o
-options.
-.El
-.It Xo
-.Nm
-.Cm create
-.Op Fl ps
-.Op Fl b Ar blocksize
-.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
-.Fl V
-.Ar size volume
-.Xc
-.Pp
-Creates a volume of the given size. The volume is exported as a block device in
-.Pa /dev/zvol/path ,
-where
-.Ar path
-is the name of the volume in the
-.Tn ZFS
-namespace. The size represents the logical size as exported by the device. By
-default, a reservation of equal size is created.
-.Pp
-.Ar size
-is automatically rounded up to the nearest 128 Kbytes to ensure that
-the volume has an integral number of blocks regardless of
-.Ar blocksize .
-.Bl -tag -width indent
-.It Fl p
-Creates all the non-existing parent datasets. Datasets created in this manner
-are automatically mounted according to the
-.Sy mountpoint
-property inherited from their parent. Any property specified on the command
-line using the
-.Fl o
-option is ignored. If the target filesystem already exists, the operation
-completes successfully.
-.It Fl s
-Creates a sparse volume with no reservation. See
-.Sy volsize
-in the
-.Qq Sx Native Properties
-section for more information about sparse volumes.
-.It Fl b Ar blocksize
-Equivalent to
-.Fl o Cm volblocksize Ns = Ns Ar blocksize .
-If this option is specified in conjunction with
-.Fl o Cm volblocksize ,
-the resulting behavior is undefined.
-.It Fl o Ar property Ns = Ns Ar value
-Sets the specified property as if the
-.Qq Nm Cm set Ar property Ns = Ns Ar value
-command was invoked at the same time the dataset was created. Any editable
-.Tn ZFS
-property can also be set at creation time. Multiple
-.Fl o
-options can be specified. An error results if the same property is specified in
-multiple
-.Fl o
-options.
-.El
-.It Xo
-.Nm
-.Cm destroy
-.Op Fl fnpRrv
-.Ar filesystem Ns | Ns Ar volume
-.Xc
-.Pp
-Destroys the given dataset. By default, the command unshares any file systems
-that are currently shared, unmounts any file systems that are currently
-mounted, and refuses to destroy a dataset that has active dependents (children
-or clones).
-.Bl -tag -width indent
-.It Fl r
-Recursively destroy all children.
-.It Fl R
-Recursively destroy all dependents, including cloned file systems outside the
-target hierarchy.
-.It Fl f
-Force an unmount of any file systems using the
-.Qq Nm Cm unmount Fl f
-command. This option has no effect on non-file systems or unmounted file
-systems.
-.It Fl n
-Do a dry-run ("No-op") deletion. No data will be deleted. This is useful in
-conjunction with the
-.Fl v
-or
-.Fl p
-flags to determine what data would be deleted.
-.It Fl p
-Print machine-parsable verbose information about the deleted data.
-.It Fl v
-Print verbose information about the deleted data.
-.El
-.Pp
-Extreme care should be taken when applying either the
-.Fl r
-or the
-.Fl R
-options, as they can destroy large portions of a pool and cause unexpected
-behavior for mounted file systems in use.
-.It Xo
-.Nm
-.Cm destroy
-.Op Fl dnpRrv
-.Sm off
-.Ar snapshot
-.Op % Ns Ar snapname
-.Op , Ns ...
-.Sm on
-.Xc
-.Pp
-The given snapshots are destroyed immediately if and only if the
-.Qq Nm Cm destroy
-command without the
-.Fl d
-option would have destroyed it. Such immediate destruction would occur, for
-example, if the snapshot had no clones and the user-initiated reference count
-were zero.
-.Pp
-If a snapshot does not qualify for immediate destruction, it is marked for
-deferred deletion. In this state, it exists as a usable, visible snapshot until
-both of the preconditions listed above are met, at which point it is destroyed.
-.Pp
-An inclusive range of snapshots may be specified by separating the
-first and last snapshots with a percent sign
-.Pq Sy % .
-The first and/or last snapshots may be left blank, in which case the
-filesystem's oldest or newest snapshot will be implied.
-.Pp
-Multiple snapshots
-(or ranges of snapshots) of the same filesystem or volume may be specified
-in a comma-separated list of snapshots.
-Only the snapshot's short name (the
-part after the
-.Sy @ )
-should be specified when using a range or comma-separated list to identify
-multiple snapshots.
-.Bl -tag -width indent
-.It Fl r
-Destroy (or mark for deferred deletion) all snapshots with this name in
-descendent file systems.
-.It Fl R
-Recursively destroy all clones of these snapshots, including the clones,
-snapshots, and children.
-If this flag is specified, the
-.Fl d
-flag will have no effect.
-.It Fl n
-Do a dry-run ("No-op") deletion. No data will be deleted. This is useful in
-conjunction with the
-.Fl v
-or
-.Fl p
-flags to determine what data would be deleted.
-.It Fl p
-Print machine-parsable verbose information about the deleted data.
-.It Fl v
-Print verbose information about the deleted data.
-.It Fl d
-Defer snapshot deletion.
-.El
-.Pp
-Extreme care should be taken when applying either the
-.Fl r
-or the
-.Fl R
-options, as they can destroy large portions of a pool and cause unexpected
-behavior for mounted file systems in use.
-.It Xo
-.Nm
-.Cm destroy
-.Ar filesystem Ns | Ns Ar volume Ns # Ns Ar bookmark
-.Xc
-.Pp
-The given bookmark is destroyed.
-.It Xo
-.Nm
-.Cm snapshot Ns | Ns Cm snap
-.Op Fl r
-.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
-.Ar filesystem@snapname Ns | Ns volume@snapname
-.Ar filesystem@snapname Ns | Ns volume@snapname Ns ...
-.Xc
-.Pp
-Creates snapshots with the given names. All previous modifications by
-successful system calls to the file system are part of the snapshots.
-Snapshots are taken atomically, so that all snapshots correspond to the same
-moment in time. See the
-.Qq Sx Snapshots
-section for details.
-.Bl -tag -width indent
-.It Fl r
-Recursively create snapshots of all descendent datasets
-.It Fl o Ar property Ns = Ns Ar value
-Sets the specified property; see
-.Qq Nm Cm create
-for details.
-.El
-.It Xo
-.Nm
-.Cm rollback
-.Op Fl rRf
-.Ar snapshot
-.Xc
-.Pp
-Roll back the given dataset to a previous snapshot. When a dataset is rolled
-back, all data that has changed since the snapshot is discarded, and the
-dataset reverts to the state at the time of the snapshot. By default, the
-command refuses to roll back to a snapshot other than the most recent one. In
-order to do so, all intermediate snapshots and bookmarks must be destroyed
-by specifying the
-.Fl r
-option.
-.Pp
-The
-.Fl rR
-options do not recursively destroy the child snapshots of a
-recursive snapshot.
-Only direct snapshots of the specified filesystem
-are destroyed by either of these options.
-To completely roll back a
-recursive snapshot, you must rollback the individual child snapshots.
-.Bl -tag -width indent
-.It Fl r
-Destroy any snapshots and bookmarks more recent than the one specified.
-.It Fl R
-Destroy any more recent snapshots and bookmarks, as well as any clones of those
-snapshots.
-.It Fl f
-Used with the
-.Fl R
-option to force an unmount of any clone file systems that are to be destroyed.
-.El
-.It Xo
-.Nm
-.Cm clone
-.Op Fl p
-.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ...
-.Ar snapshot filesystem Ns | Ns Ar volume
-.Xc
-.Pp
-Creates a clone of the given snapshot. See the
-.Qq Sx Clones
-section for details. The target dataset can be located anywhere in the
-.Tn ZFS
-hierarchy, and is created as the same type as the original.
-.Bl -tag -width indent
-.It Fl p
-Creates all the non-existing parent datasets. Datasets created in this manner
-are automatically mounted according to the
-.Sy mountpoint
-property inherited from their parent. If the target filesystem or volume
-already exists, the operation completes successfully.
-.It Fl o Ar property Ns = Ns Ar value
-Sets the specified property; see
-.Qq Nm Cm create
-for details.
-.El
-.It Xo
-.Nm
-.Cm promote
-.Ar clone-filesystem
-.Xc
-.Pp
-Promotes a clone file system to no longer be dependent on its "origin"
-snapshot. This makes it possible to destroy the file system that the clone was
-created from. The clone parent-child dependency relationship is reversed, so
-that the origin file system becomes a clone of the specified file system.
-.Pp
-The snapshot that was cloned, and any snapshots previous to this snapshot, are
-now owned by the promoted clone. The space they use moves from the origin file
-system to the promoted clone, so enough space must be available to accommodate
-these snapshots. No new space is consumed by this operation, but the space
-accounting is adjusted. The promoted clone must not have any conflicting
-snapshot names of its own. The
-.Cm rename
-subcommand can be used to rename any conflicting snapshots.
-.It Xo
-.Nm
-.Cm rename
-.Op Fl f
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
-.Xc
-.It Xo
-.Nm
-.Cm rename
-.Op Fl f
-.Fl p
-.Ar filesystem Ns | Ns Ar volume
-.Ar filesystem Ns | Ns Ar volume
-.Xc
-.It Xo
-.Nm
-.Cm rename
-.Fl u
-.Op Fl p
-.Ar filesystem filesystem
-.Xc
-.Pp
-Renames the given dataset. The new target can be located anywhere in the
-.Tn ZFS
-hierarchy, with the exception of snapshots. Snapshots can only be renamed
-within the parent file system or volume. When renaming a snapshot, the parent
-file system of the snapshot does not need to be specified as part of the second
-argument. Renamed file systems can inherit new mount points, in which case they
-are unmounted and remounted at the new mount point.
-.Bl -tag -width indent
-.It Fl p
-Creates all the nonexistent parent datasets. Datasets created in this manner
-are automatically mounted according to the
-.Sy mountpoint
-property inherited from their parent.
-.It Fl u
-Do not remount file systems during rename. If a file system's
-.Sy mountpoint
-property is set to
-.Cm legacy
-or
-.Cm none ,
-file system is not unmounted even if this option is not given.
-.It Fl f
-Force unmount any filesystems that need to be unmounted in the process.
-This flag has no effect if used together with the
-.Fl u
-flag.
-.El
-.It Xo
-.Nm
-.Cm rename
-.Fl r
-.Ar snapshot snapshot
-.Xc
-.Pp
-Recursively rename the snapshots of all descendent datasets. Snapshots are the
-only dataset that can be renamed recursively.
-.It Xo
-.Nm
-.Cm rename
-.Ar bookmark bookmark
-.Xc
-.Pp
-Renames the given bookmark.
-Bookmarks can only be renamed within the parent file system or volume.
-When renaming a bookmark, the parent file system or volume of the bookmark
-does not need to be specified as part of the second argument.
-.It Xo
-.Nm
-.Cm list
-.Op Fl r Ns | Ns Fl d Ar depth
-.Op Fl Hp
-.Op Fl o Ar property Ns Oo , Ns Ar property Oc Ns ...
-.Op Fl t Ar type Ns Oo , Ns Ar type Oc Ns ...
-.Oo Fl s Ar property Oc Ns ...
-.Oo Fl S Ar property Oc Ns ...
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns ...
-.Xc
-.Pp
-Lists the property information for the given datasets in tabular form. If
-specified, you can list property information by the absolute pathname or the
-relative pathname. By default, all file systems and volumes are displayed.
-Snapshots are displayed if the
-.Sy listsnaps
-property is
-.Cm on
-(the default is
-.Cm off ) .
-The following fields are displayed,
-.Sy name , used , available , referenced , mountpoint .
-.Bl -tag -width indent
-.It Fl r
-Recursively display any children of the dataset on the command line.
-.It Fl d Ar depth
-Recursively display any children of the dataset, limiting the recursion to
-.Ar depth .
-A depth of
-.Sy 1
-will display only the dataset and its direct children.
-.It Fl H
-Used for scripting mode. Do not print headers and separate fields by a single
-tab instead of arbitrary white space.
-.It Fl p
-Display numbers in parsable (exact) values.
-.It Fl o Ar property Ns Oo , Ns Ar property Oc Ns ...
-A comma-separated list of properties to display. The property must be:
-.Bl -bullet -offset 2n
-.It
-One of the properties described in the
-.Qq Sx Native Properties
-section
-.It
-A user property
-.It
-The value
-.Cm name
-to display the dataset name
-.It
-The value
-.Cm space
-to display space usage properties on file systems and volumes. This is a
-shortcut for specifying
-.Fl o
-.Sy name,avail,used,usedsnap,usedds,usedrefreserv,usedchild
-.Fl t
-.Sy filesystem,volume
-syntax.
-.El
-.It Fl t Ar type Ns Oo , Ns Ar type Oc Ns ...
-A comma-separated list of types to display, where
-.Ar type
-is one of
-.Sy filesystem , snapshot , snap , volume , bookmark , No or Sy all .
-For example, specifying
-.Fl t Cm snapshot
-displays only snapshots.
-.It Fl s Ar property
-A property for sorting the output by column in ascending order based on the
-value of the property. The property must be one of the properties described in
-the
-.Qq Sx Properties
-section, or the special value
-.Cm name
-to sort by the dataset name. Multiple properties can be specified at one time
-using multiple
-.Fl s
-property options. Multiple
-.Fl s
-options are evaluated from left to right in decreasing order of importance.
-.Pp
-The following is a list of sorting criteria:
-.Bl -bullet -offset 2n
-.It
-Numeric types sort in numeric order.
-.It
-String types sort in alphabetical order.
-.It
-Types inappropriate for a row sort that row to the literal bottom, regardless
-of the specified ordering.
-.It
-If no sorting options are specified the existing behavior of
-.Qq Nm Cm list
-is preserved.
-.El
-.It Fl S Ar property
-Same as the
-.Fl s
-option, but sorts by property in descending order.
-.El
-.It Xo
-.Nm
-.Cm set
-.Ar property Ns = Ns Ar value Oo Ar property Ns = Ns Ar value Oc Ns ...
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
-.Xc
-.Pp
-Sets the property or list of properties to the given value(s) for each dataset.
-Only some properties can be edited. See the "Properties" section for more
-information on what properties can be set and acceptable values. Numeric values
-can be specified as exact values, or in a human-readable form with a suffix of
-.Sy B , K , M , G , T , P , E , Z
-(for bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes, exabytes, or
-zettabytes, respectively). User properties can be set on snapshots. For more
-information, see the
-.Qq Sx User Properties
-section.
-.It Xo
-.Nm
-.Cm get
-.Op Fl r Ns | Ns Fl d Ar depth
-.Op Fl Hp
-.Op Fl o Ar all | field Ns Oo , Ns Ar field Oc Ns ...
-.Op Fl t Ar type Ns Oo , Ns Ar type Oc Ns ...
-.Op Fl s Ar source Ns Oo , Ns Ar source Oc Ns ...
-.Ar all | property Ns Oo , Ns Ar property Oc Ns ...
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns | Ns Ar bookmark Ns ...
-.Xc
-.Pp
-Displays properties for the given datasets. If no datasets are specified, then
-the command displays properties for all datasets on the system. For each
-property, the following columns are displayed:
-.Pp
-.Bl -hang -width "property" -offset indent -compact
-.It name
-Dataset name
-.It property
-Property name
-.It value
-Property value
-.It source
-Property source. Can either be local, default, temporary, inherited, received,
-or none
-(\&-).
-.El
-.Pp
-All columns except the
-.Sy RECEIVED
-column are displayed by default. The columns to display can be specified
-by using the
-.Fl o
-option. This command takes a comma-separated list of properties as described in
-the
-.Qq Sx Native Properties
-and
-.Qq Sx User Properties
-sections.
-.Pp
-The special value
-.Cm all
-can be used to display all properties that apply to the given dataset's type
-(filesystem, volume, snapshot, or bookmark).
-.Bl -tag -width indent
-.It Fl r
-Recursively display properties for any children.
-.It Fl d Ar depth
-Recursively display any children of the dataset, limiting the recursion to
-.Ar depth .
-A depth of
-.Sy 1
-will display only the dataset and its direct children.
-.It Fl H
-Display output in a form more easily parsed by scripts. Any headers are
-omitted, and fields are explicitly separated by a single tab instead of an
-arbitrary amount of space.
-.It Fl p
-Display numbers in parsable (exact) values.
-.It Fl o Cm all | Ar field Ns Oo , Ns Ar field Oc Ns ...
-A comma-separated list of columns to display. Supported values are
-.Sy name,property,value,received,source .
-Default values are
-.Sy name,property,value,source .
-The keyword
-.Cm all
-specifies all columns.
-.It Fl t Ar type Ns Oo , Ns Ar type Oc Ns ...
-A comma-separated list of types to display, where
-.Ar type
-is one of
-.Sy filesystem , snapshot , volume , No or Sy all .
-For example, specifying
-.Fl t Cm snapshot
-displays only snapshots.
-.It Fl s Ar source Ns Oo , Ns Ar source Oc Ns ...
-A comma-separated list of sources to display. Those properties coming from a
-source other than those in this list are ignored. Each source must be one of
-the following:
-.Sy local,default,inherited,temporary,received,none .
-The default value is all sources.
-.El
-.It Xo
-.Nm
-.Cm inherit
-.Op Fl rS
-.Ar property
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns ...
-.Xc
-.Pp
-Clears the specified property, causing it to be inherited from an ancestor,
-restored to default if no ancestor has the property set, or with the
-.Fl S
-option reverted to the received value if one exists.
-See the
-.Qq Sx Properties
-section for a listing of default values, and details on which properties can be
-inherited.
-.Bl -tag -width indent
-.It Fl r
-Recursively inherit the given property for all children.
-.It Fl S
-Revert the property to the received value if one exists; otherwise operate as
-if the
-.Fl S
-option was not specified.
-.El
-.It Xo
-.Nm
-.Cm remap
-.Ar filesystem Ns | Ns Ar volume
-.Xc
-.Pp
-Remap the indirect blocks in the given filesystem or volume so that they no
-longer reference blocks on previously removed vdevs and we can eventually
-shrink the size of the indirect mapping objects for the previously removed
-vdevs. Note that remapping all blocks might not be possible and that
-references from snapshots will still exist and cannot be remapped.
-.It Xo
-.Nm
-.Cm upgrade
-.Op Fl v
-.Xc
-.Pp
-Displays a list of file systems that are not the most recent version.
-.Bl -tag -width indent
-.It Fl v
-Displays
-.Tn ZFS
-filesystem versions supported by the current software. The current
-.Tn ZFS
-filesystem version and all previous supported versions are displayed, along
-with an explanation of the features provided with each version.
-.El
-.It Xo
-.Nm
-.Cm upgrade
-.Op Fl r
-.Op Fl V Ar version
-.Fl a | Ar filesystem
-.Xc
-.Pp
-Upgrades file systems to a new on-disk version. Once this is done, the file
-systems will no longer be accessible on systems running older versions of the
-software.
-.Qq Nm Cm send
-streams generated from new snapshots of these file systems cannot be accessed
-on systems running older versions of the software.
-.Pp
-In general, the file system version is independent of the pool version. See
-.Xr zpool 8
-for information on the
-.Nm zpool Cm upgrade
-command.
-.Pp
-In some cases, the file system version and the pool version are interrelated
-and the pool version must be upgraded before the file system version can be
-upgraded.
-.Bl -tag -width indent
-.It Fl r
-Upgrade the specified file system and all descendent file systems.
-.It Fl V Ar version
-Upgrade to the specified
-.Ar version .
-If the
-.Fl V
-flag is not specified, this command upgrades to the most recent version. This
-option can only be used to increase the version number, and only up to the most
-recent version supported by this software.
-.It Fl a
-Upgrade all file systems on all imported pools.
-.It Ar filesystem
-Upgrade the specified file system.
-.El
-.It Xo
-.Nm
-.Cm userspace
-.Op Fl Hinp
-.Op Fl o Ar field Ns Oo , Ns Ar field Oc Ns ...
-.Oo Fl s Ar field Oc Ns ...
-.Oo Fl S Ar field Oc Ns ...
-.Op Fl t Ar type Ns Oo , Ns Ar type Oc Ns ...
-.Ar filesystem Ns | Ns Ar snapshot
-.Xc
-.Pp
-Displays space consumed by, and quotas on, each user in the specified
-filesystem or snapshot. This corresponds to the
-.Sy userused@ Ns Ar user
-and
-.Sy userquota@ Ns Ar user
-properties.
-.Bl -tag -width indent
-.It Fl n
-Print numeric ID instead of user/group name.
-.It Fl H
-Do not print headers, use tab-delimited output.
-.It Fl p
-Use exact (parsable) numeric output.
-.It Fl o Ar field Ns Oo , Ns Ar field Oc Ns ...
-Display only the specified fields from the following set:
-.Sy type,name,used,quota .
-The default is to display all fields.
-.It Fl s Ar field
-Sort output by this field. The
-.Fl s
-and
-.Fl S
-flags may be specified multiple times to sort first by one field, then by
-another. The default is
-.Fl s Cm type Fl s Cm name .
-.It Fl S Ar field
-Sort by this field in reverse order. See
-.Fl s .
-.It Fl t Ar type Ns Oo , Ns Ar type Oc Ns ...
-Print only the specified types from the following set:
-.Sy all,posixuser,smbuser,posixgroup,smbgroup .
-.Pp
-The default is
-.Fl t Cm posixuser,smbuser .
-.Pp
-The default can be changed to include group types.
-.It Fl i
-Translate SID to POSIX ID. This flag currently has no effect on
-.Fx .
-.El
-.It Xo
-.Nm
-.Cm groupspace
-.Op Fl Hinp
-.Op Fl o Ar field Ns Oo , Ns Ar field Oc Ns ...
-.Oo Fl s Ar field Oc Ns ...
-.Oo Fl S Ar field Oc Ns ...
-.Op Fl t Ar type Ns Oo , Ns Ar type Oc Ns ...
-.Ar filesystem Ns | Ns Ar snapshot
-.Xc
-.Pp
-Displays space consumed by, and quotas on, each group in the specified
-filesystem or snapshot. This subcommand is identical to
-.Qq Nm Cm userspace ,
-except that the default types to display are
-.Fl t Sy posixgroup,smbgroup .
-.It Xo
-.Nm
-.Cm mount
-.Xc
-.Pp
-Displays all
-.Tn ZFS
-file systems currently mounted.
-.Bl -tag -width indent
-.It Fl f
-.El
-.It Xo
-.Nm
-.Cm mount
-.Op Fl vO
-.Op Fl o Ar property Ns Oo , Ns Ar property Oc Ns ...
-.Fl a | Ar filesystem
-.Xc
-.Pp
-Mounts
-.Tn ZFS
-file systems.
-.Bl -tag -width indent
-.It Fl v
-Report mount progress.
-.It Fl O
-Perform an overlay mount. Overlay mounts are not supported on
-.Fx .
-.It Fl o Ar property Ns Oo , Ns Ar property Oc Ns ...
-An optional, comma-separated list of mount options to use temporarily for the
-duration of the mount. See the
-.Qq Sx Temporary Mount Point Properties
-section for details.
-.It Fl a
-Mount all available
-.Tn ZFS
-file systems.
-This command may be executed on
-.Fx
-system startup by
-.Pa /etc/rc.d/zfs .
-For more information, see variable
-.Va zfs_enable
-in
-.Xr rc.conf 5 .
-.It Ar filesystem
-Mount the specified filesystem.
-.El
-.It Xo
-.Nm
-.Cm unmount Ns | Ns Cm umount
-.Op Fl f
-.Fl a | Ar filesystem Ns | Ns Ar mountpoint
-.Xc
-.Pp
-Unmounts currently mounted
-.Tn ZFS
-file systems.
-.Bl -tag -width indent
-.It Fl f
-Forcefully unmount the file system, even if it is currently in use.
-.It Fl a
-Unmount all available
-.Tn ZFS
-file systems.
-.It Ar filesystem | mountpoint
-Unmount the specified filesystem. The command can also be given a path to a
-.Tn ZFS
-file system mount point on the system.
-.El
-.It Xo
-.Nm
-.Cm share
-.Fl a | Ar filesystem
-.Xc
-.Pp
-Shares
-.Tn ZFS
-file systems that have the
-.Sy sharenfs
-property set.
-.Bl -tag -width indent
-.It Fl a
-Share all
-.Tn ZFS
-file systems that have the
-.Sy sharenfs
-property set.
-This command may be executed on
-.Fx
-system startup by
-.Pa /etc/rc.d/zfs .
-For more information, see variable
-.Va zfs_enable
-in
-.Xr rc.conf 5 .
-.It Ar filesystem
-Share the specified filesystem according to the
-.Tn sharenfs
-property. File systems are shared when the
-.Tn sharenfs
-property is set.
-.El
-.It Xo
-.Nm
-.Cm unshare
-.Fl a | Ar filesystem Ns | Ns Ar mountpoint
-.Xc
-.Pp
-Unshares
-.Tn ZFS
-file systems that have the
-.Tn sharenfs
-property set.
-.Bl -tag -width indent
-.It Fl a
-Unshares
-.Tn ZFS
-file systems that have the
-.Sy sharenfs
-property set.
-This command may be executed on
-.Fx
-system shutdown by
-.Pa /etc/rc.d/zfs .
-For more information, see variable
-.Va zfs_enable
-in
-.Xr rc.conf 5 .
-.It Ar filesystem | mountpoint
-Unshare the specified filesystem. The command can also be given a path to a
-.Tn ZFS
-file system shared on the system.
-.El
-.It Xo
-.Nm
-.Cm bookmark
-.Ar snapshot
-.Ar bookmark
-.Xc
-.Pp
-Creates a bookmark of the given snapshot.
-Bookmarks mark the point in time
-when the snapshot was created, and can be used as the incremental source for
-a
-.Qq Nm Cm send
-command.
-.Pp
-This feature must be enabled to be used.
-See
-.Xr zpool-features 7
-for details on ZFS feature flags and the
-.Sy bookmark
-feature.
-.It Xo
-.Nm
-.Cm send
-.Op Fl DLPRVcenpv
-.Op Fl i Ar snapshot | Fl I Ar snapshot
-.Ar snapshot
-.Xc
-.Pp
-Creates a stream representation of the last
-.Ar snapshot
-argument (not part of
-.Fl i
-or
-.Fl I )
-which is written to standard output. The output can be redirected to
-a file or to a different system (for example, using
-.Xr ssh 1 ) .
-By default, a full stream is generated.
-.Bl -tag -width indent
-.It Fl i Ar snapshot
-Generate an incremental stream from the first
-.Ar snapshot Pq the incremental source
-to the second
-.Ar snapshot Pq the incremental target .
-The incremental source can be specified as the last component of the
-snapshot name
-.Pq the Em @ No character and following
-and
-it is assumed to be from the same file system as the incremental target.
-.Pp
-If the destination is a clone, the source may be the origin snapshot, which
-must be fully specified (for example,
-.Cm pool/fs@origin ,
-not just
-.Cm @origin ) .
-.It Fl I Ar snapshot
-Generate a stream package that sends all intermediary snapshots from the first
-.Ar snapshot
-to the second
-.Ar snapshot .
-For example,
-.Ic -I @a fs@d
-is similar to
-.Ic -i @a fs@b; -i @b fs@c; -i @c fs@d .
-The incremental
-source may be specified as with the
-.Fl i
-option.
-.It Fl R, -replicate
-Generate a replication stream package, which will replicate the specified
-filesystem, and all descendent file systems, up to the named snapshot. When
-received, all properties, snapshots, descendent file systems, and clones are
-preserved.
-.Pp
-If the
-.Fl i
-or
-.Fl I
-flags are used in conjunction with the
-.Fl R
-flag, an incremental replication stream is generated. The current values of
-properties, and current snapshot and file system names are set when the stream
-is received. If the
-.Fl F
-flag is specified when this stream is received, snapshots and file systems that
-do not exist on the sending side are destroyed.
-.It Fl D, -dedup
-Generate a deduplicated stream. Blocks which would have been sent multiple
-times in the send stream will only be sent once. The receiving system must
-also support this feature to receive a deduplicated stream. This flag can
-be used regardless of the dataset's
-.Sy dedup
-property, but performance will be much better if the filesystem uses a
-dedup-capable checksum (eg.
-.Sy sha256 ) .
-.It Fl L, -large-block
-Generate a stream which may contain blocks larger than 128KB.
-This flag
-has no effect if the
-.Sy large_blocks
-pool feature is disabled, or if the
-.Sy recordsize
-property of this filesystem has never been set above 128KB.
-The receiving system must have the
-.Sy large_blocks
-pool feature enabled as well.
-See
-.Xr zpool-features 7
-for details on ZFS feature flags and the
-.Sy large_blocks
-feature.
-.It Fl e, -embed
-Generate a more compact stream by using WRITE_EMBEDDED records for blocks
-which are stored more compactly on disk by the
-.Sy embedded_data
-pool
-feature.
-This flag has no effect if the
-.Sy embedded_data
-feature is
-disabled.
-The receiving system must have the
-.Sy embedded_data
-feature
-enabled.
-If the
-.Sy lz4_compress
-feature is active on the sending system,
-then the receiving system must have that feature enabled as well.
-See
-.Xr zpool-features 7
-for details on ZFS feature flags and the
-.Sy embedded_data
-feature.
-.It Fl c, -compressed
-Generate a more compact stream by using compressed WRITE records for blocks
-which are compressed on disk and in memory (see the
-.Sy compression
-property for details).
-If the
-.Sy lz4_compress
-feature is active on the sending system, then the receiving system must have that
-feature enabled as well. If the
-.Sy large_blocks
-feature is enabled on the sending system but the
-.Fl L
-option is not supplied in conjunction with
-.Fl c
-then the data will be decompressed before sending so it can be split
-into smaller block sizes.
-.It Fl p, -props
-Include the dataset's properties in the stream. This flag is implicit when
-.Fl R
-is specified. The receiving system must also support this feature.
-.It Fl n, -dryrun
-Do a dry-run ("No-op") send. Do not generate any actual send data. This is
-useful in conjunction with the
-.Fl v
-or
-.Fl P
-flags to determine what data will be sent.
-In this case, the verbose output will be written to
-standard output (contrast with a non-dry-run, where the stream is written
-to standard output and the verbose output goes to standard error).
-.It Fl P, -parsable
-Print machine-parsable verbose information about the stream package generated.
-.It Fl v, -verbose
-Print verbose information about the stream package generated.
-This information includes a per-second report of how much data has been sent.
-.It Fl V
-Set the process title to a per-second report of how much data has been sent.
-.El
-.Pp
-The format of the stream is committed. You will be able to receive your streams
-on future versions of
-.Tn ZFS .
-.It Xo
-.Nm
-.Cm send
-.Op Fl LPcenv
-.Op Fl i Ar snapshot Ns | Ns Ar bookmark
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
-.Xc
-.Pp
-Generate a send stream, which may be of a filesystem, and may be
-incremental from a bookmark.
-If the destination is a filesystem or volume,
-the pool must be read-only, or the filesystem must not be mounted.
-When the
-stream generated from a filesystem or volume is received, the default snapshot
-name will be
-.Pq --head-- .
-.Bl -tag -width indent
-.It Fl i Ar snapshot Ns | Ns Ar bookmark
-Generate an incremental send stream.
-The incremental source must be an earlier
-snapshot in the destination's history.
-It will commonly be an earlier
-snapshot in the destination's filesystem, in which case it can be
-specified as the last component of the name
-.Pq the Em # No or Em @ No character and following .
-.Pp
-If the incremental target is a clone, the incremental source can
-be the origin snapshot, or an earlier snapshot in the origin's filesystem,
-or the origin's origin, etc.
-.It Fl n, -dryrun
-Do a dry-run
-.Pq Qq No-op
-send.
-Do not generate any actual send data.
-This is useful in conjunction with the
-.Fl v
-or
-.Fl P
-flags to determine what data will be sent.
-In this case, the verbose output will be written to standard output
-.Po contrast with a non-dry-run, where the stream is written to standard output
-and the verbose output goes to standard error
-.Pc .
-.It Fl v, -verbose
-Print verbose information about the stream package generated.
-This information includes a per-second report of how much data has been sent.
-.It Fl L, -large-block
-Generate a stream which may contain blocks larger than 128KB.
-This flag
-has no effect if the
-.Sy large_blocks
-pool feature is disabled, or if the
-.Sy recordsize
-property of this filesystem has never been set above 128KB.
-The receiving system must have the
-.Sy large_blocks
-pool feature enabled as well.
-See
-.Xr zpool-features 7
-for details on ZFS feature flags and the
-.Sy large_blocks
-feature.
-.It Fl P, -parsable
-Print machine-parsable verbose information about the stream package generated.
-.It Fl c, -compressed
-Generate a more compact stream by using compressed WRITE records for blocks
-which are compressed on disk and in memory (see the
-.Sy compression
-property for details). If the
-.Sy lz4_compress
-feature is active on the sending system, then the receiving system must have
-that feature enabled as well. If the
-.Sy large_blocks
-feature is enabled on the sending system but the
-.Fl L
-option is not supplied in conjunction with
-.Fl c
-then the data will be decompressed before sending so it can be split
-into smaller block sizes.
-.It Fl e, -embed
-Generate a more compact stream by using WRITE_EMBEDDED records for blocks
-which are stored more compactly on disk by the
-.Sy embedded_data
-pool
-feature.
-This flag has no effect if the
-.Sy embedded_data
-feature is
-disabled.
-The receiving system must have the
-.Sy embedded_data
-feature
-enabled.
-If the
-.Sy lz4_compress
-feature is active on the sending system,
-then the receiving system must have that feature enabled as well.
-See
-.Xr zpool-features 7
-for details on ZFS feature flags and the
-.Sy embedded_data
-feature.
-.El
-.It Xo
-.Nm
-.Cm send
-.Op Fl Penv
-.Fl t
-.Ar receive_resume_token
-.Xc
-Creates a send stream which resumes an interrupted receive. The
-.Ar receive_resume_token
-is the value of this property on the filesystem
-or volume that was being received into. See the documentation for
-.Sy zfs receive -s
-for more details.
-.It Xo
-.Nm
-.Cm receive Ns | Ns Cm recv
-.Op Fl vnsFMu
-.Op Fl o Sy origin Ns = Ns Ar snapshot
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot
-.Xc
-.It Xo
-.Nm
-.Cm receive Ns | Ns Cm recv
-.Op Fl vnsFMu
-.Op Fl d | e
-.Op Fl o Sy origin Ns = Ns Ar snapshot
-.Ar filesystem
-.Xc
-.Pp
-Creates a snapshot whose contents are as specified in the stream provided on
-standard input. If a full stream is received, then a new file system is created
-as well. Streams are created using the
-.Qq Nm Cm send
-subcommand, which by default creates a full stream.
-.Qq Nm Cm recv
-can be used as an alias for
-.Qq Nm Cm receive .
-.Pp
-If an incremental stream is received, then the destination file system must
-already exist, and its most recent snapshot must match the incremental stream's
-source. For
-.Sy zvol Ns s,
-the destination device link is destroyed and recreated, which means the
-.Sy zvol
-cannot be accessed during the
-.Sy receive
-operation.
-.Pp
-When a snapshot replication package stream that is generated by using the
-.Qq Nm Cm send Fl R
-command is received, any snapshots that do not exist on the sending location
-are destroyed by using the
-.Qq Nm Cm destroy Fl d
-command.
-.Pp
-The name of the snapshot (and file system, if a full stream is received) that
-this subcommand creates depends on the argument type and the
-.Fl d
-or
-.Fl e
-option.
-.Pp
-If the argument is a snapshot name, the specified
-.Ar snapshot
-is created. If the argument is a file system or volume name, a snapshot with
-the same name as the sent snapshot is created within the specified
-.Ar filesystem
-or
-.Ar volume .
-If the
-.Fl d
-or
-.Fl e
-option is specified, the snapshot name is determined by appending the sent
-snapshot's name to the specified
-.Ar filesystem .
-If the
-.Fl d
-option is specified, all but the pool name of the sent snapshot path is
-appended (for example,
-.Sy b/c@1
-appended from sent snapshot
-.Sy a/b/c@1 ) ,
-and if the
-.Fl e
-option is specified, only the tail of the sent snapshot path is appended (for
-example,
-.Sy c@1
-appended from sent snapshot
-.Sy a/b/c@1 ) .
-In the case of
-.Fl d ,
-any file systems needed to replicate the path of the sent snapshot are created
-within the specified file system.
-.Bl -tag -width indent
-.It Fl d
-Use the full sent snapshot path without the first element (without pool name)
-to determine the name of the new snapshot as described in the paragraph above.
-.It Fl e
-Use only the last element of the sent snapshot path to determine the name of
-the new snapshot as described in the paragraph above.
-.It Fl u
-File system that is associated with the received stream is not mounted.
-.It Fl v
-Print verbose information about the stream and the time required to perform the
-receive operation.
-.It Fl n
-Do not actually receive the stream. This can be useful in conjunction with the
-.Fl v
-option to verify the name the receive operation would use.
-.It Fl o Sy origin Ns = Ns Ar snapshot
-Forces the stream to be received as a clone of the given snapshot.
-If the stream is a full send stream, this will create the filesystem
-described by the stream as a clone of the specified snapshot. Which
-snapshot was specified will not affect the success or failure of the
-receive, as long as the snapshot does exist. If the stream is an
-incremental send stream, all the normal verification will be performed.
-.It Fl F
-Force a rollback of the file system to the most recent snapshot before
-performing the receive operation. If receiving an incremental replication
-stream (for example, one generated by
-.Qq Nm Cm send Fl R Bro Fl i | Fl I Brc ) ,
-destroy snapshots and file systems that do not exist on the sending side.
-.It Fl M
-Force an unmount of the file system while receiving a snapshot.
-This option is not supported on Linux.
-.It Fl s
-If the receive is interrupted, save the partially received state, rather
-than deleting it. Interruption may be due to premature termination of
-the stream
-.Po e.g. due to network failure or failure of the remote system
-if the stream is being read over a network connection
-.Pc ,
-a checksum error in the stream, termination of the
-.Nm zfs Cm receive
-process, or unclean shutdown of the system.
-.Pp
-The receive can be resumed with a stream generated by
-.Nm zfs Cm send Fl t Ar token ,
-where the
-.Ar token
-is the value of the
-.Sy receive_resume_token
-property of the filesystem or volume which is received into.
-.Pp
-To use this flag, the storage pool must have the
-.Sy extensible_dataset
-feature enabled. See
-.Xr zpool-features 7
-for details on ZFS feature flags.
-.El
-.It Xo
-.Nm
-.Cm receive Ns | Ns Cm recv
-.Fl A
-.Ar filesystem Ns | Ns Ar volume
-.Xc
-Abort an interrupted
-.Nm zfs Cm receive Fl s ,
-deleting its saved partially received state.
-.It Xo
-.Nm
-.Cm allow
-.Ar filesystem Ns | Ns Ar volume
-.Xc
-.Pp
-Displays permissions that have been delegated on the specified filesystem or
-volume. See the other forms of
-.Qq Nm Cm allow
-for more information.
-.It Xo
-.Nm
-.Cm allow
-.Op Fl ldug
-.Ar user Ns | Ns Ar group Ns Oo Ns , Ns Ar user Ns | Ns Ar group Oc Ns ...
-.Ar perm Ns | Ns Ar @setname Ns
-.Oo Ns , Ns Ar perm Ns | Ns Ar @setname Oc Ns ...
-.Ar filesystem Ns | Ns Ar volume
-.Xc
-.It Xo
-.Nm
-.Cm allow
-.Op Fl ld
-.Fl e Ns | Ns Cm everyone
-.Ar perm Ns | Ns Ar @setname Ns Op Ns , Ns Ar perm Ns | Ns Ar @setname Ns
-.Ns ...
-.Ar filesystem Ns | Ns Ar volume
-.Xc
-.Pp
-Delegates
-.Tn ZFS
-administration permission for the file systems to non-privileged users.
-.Bl -tag -width indent
-.It Xo
-.Op Fl ug
-.Ar user Ns | Ns Ar group Ns Oo , Ar user Ns | Ns Ar group Oc Ns ...
-.Xc
-Specifies to whom the permissions are delegated. Multiple entities can be
-specified as a comma-separated list. If neither of the
-.Fl ug
-options are specified, then the argument is interpreted preferentially as the
-keyword
-.Cm everyone ,
-then as a user name, and lastly as a group name. To specify
-a user or group named
-.Qq everyone ,
-use the
-.Fl u
-or
-.Fl g
-options. To specify a group with the same name as a user, use the
-.Fl g
-option.
-.It Op Fl e Ns | Ns Cm everyone
-Specifies that the permissions be delegated to
-.Qq everyone .
-.It Xo
-.Ar perm Ns | Ns Ar @setname Ns Oo , Ns Ar perm Ns | Ns Ar @setname Oc Ns ...
-.Xc
-The permissions to delegate. Multiple permissions
-may be specified as a comma-separated list. Permission names are the same as
-.Tn ZFS
-subcommand and property names. See the property list below. Property set names,
-which begin with an at sign
-.Pq Sy @ ,
-may be specified. See the
-.Fl s
-form below for details.
-.It Xo
-.Op Fl ld
-.Ar filesystem Ns | Ns Ar volume
-.Xc
-Specifies where the permissions are delegated. If neither of the
-.Fl ld
-options are specified, or both are, then the permissions are allowed for the
-file system or volume, and all of its descendents. If only the
-.Fl l
-option is used, then is allowed "locally" only for the specified file system.
-If only the
-.Fl d
-option is used, then is allowed only for the descendent file systems.
-.El
-.Pp
-Permissions are generally the ability to use a
-.Tn ZFS
-subcommand or change a
-.Tn ZFS
-property. The following permissions are available:
-.Bl -column -offset 4n "secondarycache" "subcommand"
-.It NAME Ta TYPE Ta NOTES
-.It allow Ta subcommand Ta Must Xo
-also have the permission that is being allowed
-.Xc
-.It clone Ta subcommand Ta Must Xo
-also have the 'create' ability and 'mount' ability in the origin file system
-.Xc
-.It create Ta subcommand Ta Must also have the 'mount' ability
-.It destroy Ta subcommand Ta Must also have the 'mount' ability
-.It diff Ta subcommand Ta Allows lookup of paths within a dataset given an
-object number, and the ability to create snapshots necessary to 'zfs diff'
-.It hold Ta subcommand Ta Allows adding a user hold to a snapshot
-.It mount Ta subcommand Ta Allows mount/umount of Tn ZFS No datasets
-.It promote Ta subcommand Ta Must Xo
-also have the 'mount' and 'promote' ability in the origin file system
-.Xc
-.It receive Ta subcommand Ta Must also have the 'mount' and 'create' ability
-.It release Ta subcommand Ta Allows Xo
-releasing a user hold which might destroy the snapshot
-.Xc
-.It rename Ta subcommand Ta Must Xo
-also have the 'mount' and 'create' ability in the new parent
-.Xc
-.It rollback Ta subcommand Ta Must also have the 'mount' ability
-.It send Ta subcommand
-.It share Ta subcommand Ta Allows Xo
-sharing file systems over the
-.Tn NFS
-protocol
-.Xc
-.It snapshot Ta subcommand Ta Must also have the 'mount' ability
-.It groupquota Ta other Ta Allows accessing any groupquota@... property
-.It groupused Ta other Ta Allows reading any groupused@... property
-.It userprop Ta other Ta Allows changing any user property
-.It userquota Ta other Ta Allows accessing any userquota@... property
-.It userused Ta other Ta Allows reading any userused@... property
-.It aclinherit Ta property
-.It aclmode Ta property
-.It atime Ta property
-.It canmount Ta property
-.It casesensitivity Ta property
-.It checksum Ta property
-.It compression Ta property
-.It copies Ta property
-.It dedup Ta property
-.It devices Ta property
-.It exec Ta property
-.It filesystem_limit Ta property
-.It logbias Ta property
-.It jailed Ta property
-.It mlslabel Ta property
-.It mountpoint Ta property
-.It nbmand Ta property
-.It normalization Ta property
-.It primarycache Ta property
-.It quota Ta property
-.It readonly Ta property
-.It recordsize Ta property
-.It refquota Ta property
-.It refreservation Ta property
-.It reservation Ta property
-.It secondarycache Ta property
-.It setuid Ta property
-.It sharenfs Ta property
-.It sharesmb Ta property
-.It snapdir Ta property
-.It snapshot_limit Ta property
-.It sync Ta property
-.It utf8only Ta property
-.It version Ta property
-.It volblocksize Ta property
-.It volsize Ta property
-.It vscan Ta property
-.It xattr Ta property
-.El
-.It Xo
-.Nm
-.Cm allow
-.Fl c
-.Ar perm Ns | Ns Ar @setname Ns Op Ns , Ns Ar perm Ns | Ns Ar @setname Ns
-.Ns ...
-.Ar filesystem Ns | Ns Ar volume
-.Xc
-.Pp
-Sets "create time" permissions. These permissions are granted (locally) to the
-creator of any newly-created descendent file system.
-.It Xo
-.Nm
-.Cm allow
-.Fl s
-.Ar @setname
-.Ar perm Ns | Ns Ar @setname Ns Op Ns , Ns Ar perm Ns | Ns Ar @setname Ns
-.Ns ...
-.Ar filesystem Ns | Ns Ar volume
-.Xc
-.Pp
-Defines or adds permissions to a permission set. The set can be used by other
-.Qq Nm Cm allow
-commands for the specified file system and its descendents. Sets are evaluated
-dynamically, so changes to a set are immediately reflected. Permission sets
-follow the same naming restrictions as ZFS file systems, but the name must
-begin with an "at sign"
-.Pq Sy @ ,
-and can be no more than 64 characters long.
-.It Xo
-.Nm
-.Cm unallow
-.Op Fl rldug
-.Ar user Ns | Ns Ar group Ns Oo Ns , Ns Ar user Ns | Ns Ar group Oc Ns ...
-.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns
-.Ns ... Oc
-.Ar filesystem Ns | Ns Ar volume
-.Xc
-.It Xo
-.Nm
-.Cm unallow
-.Op Fl rld
-.Fl e Ns | Ns Cm everyone
-.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns
-.Ns ... Oc
-.Ar filesystem Ns | Ns Ar volume
-.Xc
-.It Xo
-.Nm
-.Cm unallow
-.Op Fl r
-.Fl c
-.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns
-.Ns ... Oc
-.Ar filesystem Ns | Ns Ar volume
-.Xc
-.Pp
-Removes permissions that were granted with the
-.Qq Nm Cm allow
-command. No permissions are explicitly denied, so other permissions granted are
-still in effect. For example, if the permission is granted by an ancestor. If
-no permissions are specified, then all permissions for the specified
-.Ar user , group , No or everyone
-are removed. Specifying
-.Cm everyone
-.Po or using the Fl e
-option
-.Pc only removes the permissions that were granted to everyone ,
-not all permissions for every user and group. See the
-.Qq Nm Cm allow
-command for a description of the
-.Fl ldugec
-options.
-.Bl -tag -width indent
-.It Fl r
-Recursively remove the permissions from this file system and all descendents.
-.El
-.It Xo
-.Nm
-.Cm unallow
-.Op Fl r
-.Fl s
-.Ar @setname
-.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns
-.Ns ... Oc
-.Ar filesystem Ns | Ns Ar volume
-.Xc
-.Pp
-Removes permissions from a permission set. If no permissions are specified,
-then all permissions are removed, thus removing the set entirely.
-.It Xo
-.Nm
-.Cm hold
-.Op Fl r
-.Ar tag snapshot Ns ...
-.Xc
-.Pp
-Adds a single reference, named with the
-.Ar tag
-argument, to the specified snapshot or snapshots. Each snapshot has its own tag
-namespace, and tags must be unique within that space.
-.Pp
-If a hold exists on a snapshot, attempts to destroy that snapshot by using the
-.Qq Nm Cm destroy
-command returns
-.Em EBUSY .
-.Bl -tag -width indent
-.It Fl r
-Specifies that a hold with the given tag is applied recursively to the
-snapshots of all descendent file systems.
-.El
-.It Xo
-.Nm
-.Cm holds
-.Op Fl Hp
-.Op Fl r Ns | Ns Fl d Ar depth
-.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns
-.Ns ...
-.Xc
-.Pp
-Lists all existing user references for the given dataset or datasets.
-.Bl -tag -width indent
-.It Fl H
-Used for scripting mode. Do not print headers and separate fields by a single
-tab instead of arbitrary white space.
-.It Fl p
-Display numbers in parsable (exact) values.
-.It Fl r
-Lists the holds that are set on the descendent snapshots of the named datasets
-or snapshots, in addition to listing the holds on the named snapshots, if any.
-.It Fl d Ar depth
-Recursively display any holds on the named snapshots, or descendent snapshots of
-the named datasets or snapshots, limiting the recursion to
-.Ar depth .
-.El
-.It Xo
-.Nm
-.Cm release
-.Op Fl r
-.Ar tag snapshot Ns ...
-.Xc
-.Pp
-Removes a single reference, named with the
-.Ar tag
-argument, from the specified snapshot or snapshots. The tag must already exist
-for each snapshot.
-.Bl -tag -width indent
-.It Fl r
-Recursively releases a hold with the given tag on the snapshots of all
-descendent file systems.
-.El
-.It Xo
-.Nm
-.Cm diff
-.Op Fl FHt
-.Ar snapshot
-.Op Ar snapshot Ns | Ns Ar filesystem
-.Xc
-.Pp
-Display the difference between a snapshot of a given filesystem and another
-snapshot of that filesystem from a later time or the current contents of the
-filesystem. The first column is a character indicating the type of change,
-the other columns indicate pathname, new pathname
-.Pq in case of rename ,
-change in link count, and optionally file type and/or change time.
-.Pp
-The types of change are:
-.Bl -column -offset 2n indent
-.It \&- Ta path was removed
-.It \&+ Ta path was added
-.It \&M Ta path was modified
-.It \&R Ta path was renamed
-.El
-.Bl -tag -width indent
-.It Fl F
-Display an indication of the type of file, in a manner similar to the
-.Fl F
-option of
-.Xr ls 1 .
-.Bl -column -offset 2n indent
-.It \&B Ta block device
-.It \&C Ta character device
-.It \&F Ta regular file
-.It \&/ Ta directory
-.It \&@ Ta symbolic link
-.It \&= Ta socket
-.It \&> Ta door (not supported on Fx )
-.It \&| Ta named pipe (not supported on Fx )
-.It \&P Ta event port (not supported on Fx )
-.El
-.It Fl H
-Give more parsable tab-separated output, without header lines and without
-arrows.
-.It Fl t
-Display the path's inode change time as the first column of output.
-.El
-.It Xo
-.Nm
-.Cm program
-.Op Fl jn
-.Op Fl t Ar timeout
-.Op Fl m Ar memory_limit
-.Ar pool script
-.Op Ar arg1 No ...
-.Xc
-.Pp
-Executes
-.Ar script
-as a ZFS channel program on
-.Ar pool .
-The ZFS channel
-program interface allows ZFS administrative operations to be run
-programmatically via a Lua script.
-The entire script is executed atomically, with no other administrative
-operations taking effect concurrently.
-A library of ZFS calls is made available to channel program scripts.
-Channel programs may only be run with root privileges.
-.Pp
-For full documentation of the ZFS channel program interface, see the manual
-page for
-.Xr zfs-program 8 .
-.Bl -tag -width indent
-.It Fl j
-Display channel program output in JSON format.
-When this flag is specified and standard output is empty -
-channel program encountered an error.
-The details of such an error will be printed to standard error in plain text.
-.It Fl n
-Executes a read-only channel program, which runs faster.
-The program cannot change on-disk state by calling functions from
-the zfs.sync submodule.
-The program can be used to gather information such as properties and
-determining if changes would succeed (zfs.check.*).
-Without this flag, all pending changes must be synced to disk before
-a channel program can complete.
-.It Fl t Ar timeout
-Execution time limit, in milliseconds.
-If a channel program executes for longer than the provided timeout, it will
-be stopped and an error will be returned.
-The default timeout is 1000 ms, and can be set to a maximum of 10000 ms.
-.It Fl m Ar memory-limit
-Memory limit, in bytes.
-If a channel program attempts to allocate more memory than the given limit,
-it will be stopped and an error returned.
-The default memory limit is 10 MB, and can be set to a maximum of 100 MB.
-.Pp
-All remaining argument strings are passed directly to the channel program as
-arguments.
-See
-.Xr zfs-program 8
-for more information.
-.El
-.It Xo
-.Nm
-.Cm jail
-.Ar jailid filesystem
-.Xc
-.Pp
-Attaches the specified
-.Ar filesystem
-to the jail identified by JID
-.Ar jailid .
-From now on this file system tree can be managed from within a jail if the
-.Sy jailed
-property has been set. To use this functionality, the jail needs the
-.Va allow.mount
-and
-.Va allow.mount.zfs
-parameters set to 1 and the
-.Va enforce_statfs
-parameter set to a value lower than 2.
-.Pp
-See
-.Xr jail 8
-for more information on managing jails and configuring the parameters above.
-.It Xo
-.Nm
-.Cm unjail
-.Ar jailid filesystem
-.Xc
-.Pp
-Detaches the specified
-.Ar filesystem
-from the jail identified by JID
-.Ar jailid .
-.El
-.Sh EXIT STATUS
-The following exit values are returned:
-.Bl -tag -offset 2n -width 2n
-.It 0
-Successful completion.
-.It 1
-An error occurred.
-.It 2
-Invalid command line options were specified.
-.El
-.Sh EXAMPLES
-.Bl -tag -width 0n
-.It Sy Example 1 No Creating a Tn ZFS No File System Hierarchy
-.Pp
-The following commands create a file system named
-.Em pool/home
-and a file system named
-.Em pool/home/bob .
-The mount point
-.Pa /home
-is set for the parent file system, and is automatically inherited by the child
-file system.
-.Bd -literal -offset 2n
-.Li # Ic zfs create pool/home
-.Li # Ic zfs set mountpoint=/home pool/home
-.Li # Ic zfs create pool/home/bob
-.Ed
-.It Sy Example 2 No Creating a Tn ZFS No Snapshot
-.Pp
-The following command creates a snapshot named
-.Sy yesterday .
-This snapshot is mounted on demand in the
-.Pa \&.zfs/snapshot
-directory at the root of the
-.Em pool/home/bob
-file system.
-.Bd -literal -offset 2n
-.Li # Ic zfs snapshot pool/home/bob@yesterday
-.Ed
-.It Sy Example 3 No Creating and Destroying Multiple Snapshots
-.Pp
-The following command creates snapshots named
-.Em yesterday
-of
-.Em pool/home
-and all of its descendent file systems. Each snapshot is mounted on demand in
-the
-.Pa \&.zfs/snapshot
-directory at the root of its file system. The second command destroys the newly
-created snapshots.
-.Bd -literal -offset 2n
-.Li # Ic zfs snapshot -r pool/home@yesterday
-.Li # Ic zfs destroy -r pool/home@yesterday
-.Ed
-.It Sy Example 4 No Disabling and Enabling File System Compression
-.Pp
-The following command disables the
-.Sy compression
-property for all file systems under
-.Em pool/home .
-The next command explicitly enables
-.Sy compression
-for
-.Em pool/home/anne .
-.Bd -literal -offset 2n
-.Li # Ic zfs set compression=off pool/home
-.Li # Ic zfs set compression=on pool/home/anne
-.Ed
-.It Sy Example 5 No Listing Tn ZFS No Datasets
-.Pp
-The following command lists all active file systems and volumes in the system.
-Snapshots are displayed if the
-.Sy listsnaps
-property is
-.Cm on .
-The default is
-.Cm off .
-See
-.Xr zpool 8
-for more information on pool properties.
-.Bd -literal -offset 2n
-.Li # Ic zfs list
- NAME USED AVAIL REFER MOUNTPOINT
- pool 450K 457G 18K /pool
- pool/home 315K 457G 21K /home
- pool/home/anne 18K 457G 18K /home/anne
- pool/home/bob 276K 457G 276K /home/bob
-.Ed
-.It Sy Example 6 No Setting a Quota on a Tn ZFS No File System
-.Pp
-The following command sets a quota of 50 Gbytes for
-.Em pool/home/bob .
-.Bd -literal -offset 2n
-.Li # Ic zfs set quota=50G pool/home/bob
-.Ed
-.It Sy Example 7 No Listing Tn ZFS No Properties
-.Pp
-The following command lists all properties for
-.Em pool/home/bob .
-.Bd -literal -offset 2n
-.Li # Ic zfs get all pool/home/bob
-NAME PROPERTY VALUE SOURCE
-pool/home/bob type filesystem -
-pool/home/bob creation Tue Jul 21 15:53 2009 -
-pool/home/bob used 21K -
-pool/home/bob available 20.0G -
-pool/home/bob referenced 21K -
-pool/home/bob compressratio 1.00x -
-pool/home/bob mounted yes -
-pool/home/bob quota 20G local
-pool/home/bob reservation none default
-pool/home/bob recordsize 128K default
-pool/home/bob mountpoint /home/bob default
-pool/home/bob sharenfs off default
-pool/home/bob checksum on default
-pool/home/bob compression on local
-pool/home/bob atime on default
-pool/home/bob devices on default
-pool/home/bob exec on default
-pool/home/bob filesystem_limit none default
-pool/home/bob setuid on default
-pool/home/bob readonly off default
-pool/home/bob jailed off default
-pool/home/bob snapdir hidden default
-pool/home/bob snapshot_limit none default
-pool/home/bob aclmode discard default
-pool/home/bob aclinherit restricted default
-pool/home/bob canmount on default
-pool/home/bob xattr on default
-pool/home/bob copies 1 default
-pool/home/bob version 5 -
-pool/home/bob utf8only off -
-pool/home/bob normalization none -
-pool/home/bob casesensitivity sensitive -
-pool/home/bob vscan off default
-pool/home/bob nbmand off default
-pool/home/bob sharesmb off default
-pool/home/bob refquota none default
-pool/home/bob refreservation none default
-pool/home/bob primarycache all default
-pool/home/bob secondarycache all default
-pool/home/bob usedbysnapshots 0 -
-pool/home/bob usedbydataset 21K -
-pool/home/bob usedbychildren 0 -
-pool/home/bob usedbyrefreservation 0 -
-pool/home/bob logbias latency default
-pool/home/bob dedup off default
-pool/home/bob mlslabel -
-pool/home/bob sync standard default
-pool/home/bob refcompressratio 1.00x -
-.Ed
-.Pp
-The following command gets a single property value.
-.Bd -literal -offset 2n
-.Li # Ic zfs get -H -o value compression pool/home/bob
-on
-.Ed
-.Pp
-The following command lists all properties with local settings for
-.Em pool/home/bob .
-.Bd -literal -offset 2n
-.Li # Ic zfs get -s local -o name,property,value all pool/home/bob
-NAME PROPERTY VALUE
-pool/home/bob quota 20G
-pool/home/bob compression on
-.Ed
-.It Sy Example 8 No Rolling Back a Tn ZFS No File System
-.Pp
-The following command reverts the contents of
-.Em pool/home/anne
-to the snapshot named
-.Em yesterday ,
-deleting all intermediate snapshots.
-.Bd -literal -offset 2n
-.Li # Ic zfs rollback -r pool/home/anne@yesterday
-.Ed
-.It Sy Example 9 No Creating a Tn ZFS No Clone
-.Pp
-The following command creates a writable file system whose initial contents are
-the same as
-.Em pool/home/bob@yesterday .
-.Bd -literal -offset 2n
-.Li # Ic zfs clone pool/home/bob@yesterday pool/clone
-.Ed
-.It Sy Example 10 No Promoting a Tn ZFS No Clone
-.Pp
-The following commands illustrate how to test out changes to a file system, and
-then replace the original file system with the changed one, using clones, clone
-promotion, and renaming:
-.Bd -literal -offset 2n
-.Li # Ic zfs create pool/project/production
-.Ed
-.Pp
-Populate
-.Pa /pool/project/production
-with data and continue with the following commands:
-.Bd -literal -offset 2n
-.Li # Ic zfs snapshot pool/project/production@today
-.Li # Ic zfs clone pool/project/production@today pool/project/beta
-.Ed
-.Pp
-Now make changes to
-.Pa /pool/project/beta
-and continue with the following commands:
-.Bd -literal -offset 2n
-.Li # Ic zfs promote pool/project/beta
-.Li # Ic zfs rename pool/project/production pool/project/legacy
-.Li # Ic zfs rename pool/project/beta pool/project/production
-.Ed
-.Pp
-Once the legacy version is no longer needed, it can be destroyed.
-.Bd -literal -offset 2n
-.Li # Ic zfs destroy pool/project/legacy
-.Ed
-.It Sy Example 11 No Inheriting Tn ZFS No Properties
-.Pp
-The following command causes
-.Em pool/home/bob
-and
-.Em pool/home/anne
-to inherit the
-.Sy checksum
-property from their parent.
-.Bd -literal -offset 2n
-.Li # Ic zfs inherit checksum pool/home/bob pool/home/anne
-.Ed
-.It Sy Example 12 No Remotely Replicating Tn ZFS No Data
-.Pp
-The following commands send a full stream and then an incremental stream to a
-remote machine, restoring them into
-.Sy poolB/received/fs@a
-and
-.Sy poolB/received/fs@b ,
-respectively.
-.Sy poolB
-must contain the file system
-.Sy poolB/received ,
-and must not initially contain
-.Sy poolB/received/fs .
-.Bd -literal -offset 2n
-.Li # Ic zfs send pool/fs@a | ssh host zfs receive poolB/received/fs@a
-.Li # Ic zfs send -i a pool/fs@b | ssh host zfs receive poolB/received/fs
-.Ed
-.It Xo
-.Sy Example 13
-Using the
-.Qq zfs receive -d
-Option
-.Xc
-.Pp
-The following command sends a full stream of
-.Sy poolA/fsA/fsB@snap
-to a remote machine, receiving it into
-.Sy poolB/received/fsA/fsB@snap .
-The
-.Sy fsA/fsB@snap
-portion of the received snapshot's name is determined from the name of the sent
-snapshot.
-.Sy poolB
-must contain the file system
-.Sy poolB/received .
-If
-.Sy poolB/received/fsA
-does not exist, it is created as an empty file system.
-.Bd -literal -offset 2n
-.Li # Ic zfs send poolA/fsA/fsB@snap | ssh host zfs receive -d poolB/received
-.Ed
-.It Sy Example 14 No Setting User Properties
-.Pp
-The following example sets the user-defined
-.Sy com.example:department
-property for a dataset.
-.Bd -literal -offset 2n
-.Li # Ic zfs set com.example:department=12345 tank/accounting
-.Ed
-.It Sy Example 15 No Performing a Rolling Snapshot
-.Pp
-The following example shows how to maintain a history of snapshots with a
-consistent naming scheme. To keep a week's worth of snapshots, the user
-destroys the oldest snapshot, renames the remaining snapshots, and then creates
-a new snapshot, as follows:
-.Bd -literal -offset 2n
-.Li # Ic zfs destroy -r pool/users@7daysago
-.Li # Ic zfs rename -r pool/users@6daysago @7daysago
-.Li # Ic zfs rename -r pool/users@5daysago @6daysago
-.Li # Ic zfs rename -r pool/users@4daysago @5daysago
-.Li # Ic zfs rename -r pool/users@3daysago @4daysago
-.Li # Ic zfs rename -r pool/users@2daysago @3daysago
-.Li # Ic zfs rename -r pool/users@yesterday @2daysago
-.Li # Ic zfs rename -r pool/users@today @yesterday
-.Li # Ic zfs snapshot -r pool/users@today
-.Ed
-.It Xo
-.Sy Example 16
-Setting
-.Qq sharenfs
-Property Options on a ZFS File System
-.Xc
-.Pp
-The following command shows how to set
-.Sy sharenfs
-property options to enable root access for a specific network on the
-.Em tank/home
-file system. The contents of the
-.Sy sharenfs
-property are valid
-.Xr exports 5
-options.
-.Bd -literal -offset 2n
-.Li # Ic zfs set sharenfs="maproot=root,network 192.168.0.0/24" tank/home
-.Ed
-.Pp
-Another way to write this command with the same result is:
-.Bd -literal -offset 2n
-.Li # Ic set zfs sharenfs="-maproot=root -network 192.168.0.0/24" tank/home
-.Ed
-.It Xo
-.Sy Example 17
-Delegating
-.Tn ZFS
-Administration Permissions on a
-.Tn ZFS
-Dataset
-.Xc
-.Pp
-The following example shows how to set permissions so that user
-.Em cindys
-can create, destroy, mount, and take snapshots on
-.Em tank/cindys .
-The permissions on
-.Em tank/cindys
-are also displayed.
-.Bd -literal -offset 2n
-.Li # Ic zfs allow cindys create,destroy,mount,snapshot tank/cindys
-.Li # Ic zfs allow tank/cindys
----- Permissions on tank/cindys --------------------------------------
-Local+Descendent permissions:
- user cindys create,destroy,mount,snapshot
-.Ed
-.It Sy Example 18 No Delegating Create Time Permissions on a Tn ZFS No Dataset
-.Pp
-The following example shows how to grant anyone in the group
-.Em staff
-to create file systems in
-.Em tank/users .
-This syntax also allows staff members to destroy their own file systems, but
-not destroy anyone else's file system. The permissions on
-.Em tank/users
-are also displayed.
-.Bd -literal -offset 2n
-.Li # Ic zfs allow staff create,mount tank/users
-.Li # Ic zfs allow -c destroy tank/users
-.Li # Ic zfs allow tank/users
----- Permissions on tank/users ---------------------------------------
-Permission sets:
- destroy
-Local+Descendent permissions:
- group staff create,mount
-.Ed
-.It Xo
-.Sy Example 19
-Defining and Granting a Permission Set on a
-.Tn ZFS
-Dataset
-.Xc
-.Pp
-The following example shows how to define and grant a permission set on the
-.Em tank/users
-file system. The permissions on
-.Em tank/users
-are also displayed.
-.Bd -literal -offset 2n
-.Li # Ic zfs allow -s @pset create,destroy,snapshot,mount tank/users
-.Li # Ic zfs allow staff @pset tank/users
-.Li # Ic zfs allow tank/users
----- Permissions on tank/users ---------------------------------------
-Permission sets:
- @pset create,destroy,mount,snapshot
-Local+Descendent permissions:
- group staff @pset
-.Ed
-.It Sy Example 20 No Delegating Property Permissions on a Tn ZFS No Dataset
-.Pp
-The following example shows to grant the ability to set quotas and reservations
-on the
-.Sy users/home
-file system. The permissions on
-.Sy users/home
-are also displayed.
-.Bd -literal -offset 2n
-.Li # Ic zfs allow cindys quota,reservation users/home
-.Li # Ic zfs allow users/home
----- Permissions on users/home ---------------------------------------
-Local+Descendent permissions:
- user cindys quota,reservation
-.Li # Ic su - cindys
-.Li cindys% Ic zfs set quota=10G users/home/marks
-.Li cindys% Ic zfs get quota users/home/marks
-NAME PROPERTY VALUE SOURCE
-users/home/marks quota 10G local
-.Ed
-.It Sy Example 21 No Removing ZFS Delegated Permissions on a Tn ZFS No Dataset
-.Pp
-The following example shows how to remove the snapshot permission from the
-.Em staff
-group on the
-.Em tank/users
-file system. The permissions on
-.Em tank/users
-are also displayed.
-.Bd -literal -offset 2n
-.Li # Ic zfs unallow staff snapshot tank/users
-.Li # Ic zfs allow tank/users
----- Permissions on tank/users ---------------------------------------
-Permission sets:
- @pset create,destroy,mount,snapshot
-Local+Descendent permissions:
- group staff @pset
-.Ed
-.It Sy Example 22 Showing the differences between a snapshot and a ZFS Dataset
-.Pp
-The following example shows how to see what has changed between a prior
-snapshot of a ZFS Dataset and its current state. The
-.Fl F
-option is used to indicate type information for the files affected.
-.Bd -literal -offset 2n
-.Li # Ic zfs diff tank/test@before tank/test
-M / /tank/test/
-M F /tank/test/linked (+1)
-R F /tank/test/oldname -> /tank/test/newname
-- F /tank/test/deleted
-+ F /tank/test/created
-M F /tank/test/modified
-.Ed
-.El
-.Sh SEE ALSO
-.Xr chmod 2 ,
-.Xr fsync 2 ,
-.Xr exports 5 ,
-.Xr fstab 5 ,
-.Xr rc.conf 5 ,
-.Xr jail 8 ,
-.Xr mount 8 ,
-.Xr umount 8 ,
-.Xr zfs-program 8 ,
-.Xr zpool 8
-.Sh HISTORY
-The
-.Nm
-utility first appeared in
-.Fx 7.0 .
-.Sh AUTHORS
-This manual page is a
-.Xr mdoc 7
-reimplementation of the
-.Tn OpenSolaris
-manual page
-.Em zfs(1M) ,
-modified and customized for
-.Fx
-and licensed under the
-Common Development and Distribution License
-.Pq Tn CDDL .
-.Pp
-The
-.Xr mdoc 7
-implementation of this manual page was initially written by
-.An Martin Matuska Aq mm@FreeBSD.org .
diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.c b/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.c
deleted file mode 100644
index a291db083568..000000000000
--- a/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.c
+++ /dev/null
@@ -1,497 +0,0 @@
-/*
- * 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
- * or http://www.opensolaris.org/os/licensing.
- * 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012 Pawel Jakub Dawidek. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2013 by Delphix. All rights reserved.
- */
-
-#include <libintl.h>
-#include <libuutil.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-
-#include <libzfs.h>
-
-#include "zfs_util.h"
-#include "zfs_iter.h"
-
-/*
- * This is a private interface used to gather up all the datasets specified on
- * the command line so that we can iterate over them in order.
- *
- * First, we iterate over all filesystems, gathering them together into an
- * AVL tree. We report errors for any explicitly specified datasets
- * that we couldn't open.
- *
- * When finished, we have an AVL tree of ZFS handles. We go through and execute
- * the provided callback for each one, passing whatever data the user supplied.
- */
-
-typedef struct zfs_node {
- zfs_handle_t *zn_handle;
- uu_avl_node_t zn_avlnode;
-} zfs_node_t;
-
-typedef struct callback_data {
- uu_avl_t *cb_avl;
- int cb_flags;
- zfs_type_t cb_types;
- zfs_sort_column_t *cb_sortcol;
- zprop_list_t **cb_proplist;
- int cb_depth_limit;
- int cb_depth;
- uint8_t cb_props_table[ZFS_NUM_PROPS];
-} callback_data_t;
-
-uu_avl_pool_t *avl_pool;
-
-/*
- * Include snaps if they were requested or if this a zfs list where types
- * were not specified and the "listsnapshots" property is set on this pool.
- */
-static boolean_t
-zfs_include_snapshots(zfs_handle_t *zhp, callback_data_t *cb)
-{
- zpool_handle_t *zph;
-
- if ((cb->cb_flags & ZFS_ITER_PROP_LISTSNAPS) == 0)
- return (cb->cb_types & ZFS_TYPE_SNAPSHOT);
-
- zph = zfs_get_pool_handle(zhp);
- return (zpool_get_prop_int(zph, ZPOOL_PROP_LISTSNAPS, NULL));
-}
-
-/*
- * Called for each dataset. If the object is of an appropriate type,
- * add it to the avl tree and recurse over any children as necessary.
- */
-static int
-zfs_callback(zfs_handle_t *zhp, void *data)
-{
- callback_data_t *cb = data;
- boolean_t should_close = B_TRUE;
- boolean_t include_snaps = zfs_include_snapshots(zhp, cb);
- boolean_t include_bmarks = (cb->cb_types & ZFS_TYPE_BOOKMARK);
-
- if ((zfs_get_type(zhp) & cb->cb_types) ||
- ((zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) && include_snaps)) {
- uu_avl_index_t idx;
- zfs_node_t *node = safe_malloc(sizeof (zfs_node_t));
-
- node->zn_handle = zhp;
- uu_avl_node_init(node, &node->zn_avlnode, avl_pool);
- if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol,
- &idx) == NULL) {
- if (cb->cb_proplist) {
- if ((*cb->cb_proplist) &&
- !(*cb->cb_proplist)->pl_all)
- zfs_prune_proplist(zhp,
- cb->cb_props_table);
-
- if (zfs_expand_proplist(zhp, cb->cb_proplist,
- (cb->cb_flags & ZFS_ITER_RECVD_PROPS),
- (cb->cb_flags & ZFS_ITER_LITERAL_PROPS))
- != 0) {
- free(node);
- return (-1);
- }
- }
- uu_avl_insert(cb->cb_avl, node, idx);
- should_close = B_FALSE;
- } else {
- free(node);
- }
- }
-
- /*
- * Recurse if necessary.
- */
- if (cb->cb_flags & ZFS_ITER_RECURSE &&
- ((cb->cb_flags & ZFS_ITER_DEPTH_LIMIT) == 0 ||
- cb->cb_depth < cb->cb_depth_limit)) {
- cb->cb_depth++;
- if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM)
- (void) zfs_iter_filesystems(zhp, zfs_callback, data);
- if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT |
- ZFS_TYPE_BOOKMARK)) == 0) && include_snaps)
- (void) zfs_iter_snapshots(zhp,
- (cb->cb_flags & ZFS_ITER_SIMPLE) != 0, zfs_callback,
- data, 0, 0);
- if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT |
- ZFS_TYPE_BOOKMARK)) == 0) && include_bmarks)
- (void) zfs_iter_bookmarks(zhp, zfs_callback, data);
- cb->cb_depth--;
- }
-
- if (should_close)
- zfs_close(zhp);
-
- return (0);
-}
-
-int
-zfs_add_sort_column(zfs_sort_column_t **sc, const char *name,
- boolean_t reverse)
-{
- zfs_sort_column_t *col;
- zfs_prop_t prop;
-
- if ((prop = zfs_name_to_prop(name)) == ZPROP_INVAL &&
- !zfs_prop_user(name))
- return (-1);
-
- col = safe_malloc(sizeof (zfs_sort_column_t));
-
- col->sc_prop = prop;
- col->sc_reverse = reverse;
- if (prop == ZPROP_INVAL) {
- col->sc_user_prop = safe_malloc(strlen(name) + 1);
- (void) strcpy(col->sc_user_prop, name);
- }
-
- if (*sc == NULL) {
- col->sc_last = col;
- *sc = col;
- } else {
- (*sc)->sc_last->sc_next = col;
- (*sc)->sc_last = col;
- }
-
- return (0);
-}
-
-void
-zfs_free_sort_columns(zfs_sort_column_t *sc)
-{
- zfs_sort_column_t *col;
-
- while (sc != NULL) {
- col = sc->sc_next;
- free(sc->sc_user_prop);
- free(sc);
- sc = col;
- }
-}
-
-boolean_t
-zfs_sort_only_by_name(const zfs_sort_column_t *sc)
-{
-
- return (sc != NULL && sc->sc_next == NULL &&
- sc->sc_prop == ZFS_PROP_NAME);
-}
-
-/* ARGSUSED */
-static int
-zfs_compare(const void *larg, const void *rarg, void *unused)
-{
- zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
- zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
- const char *lname = zfs_get_name(l);
- const char *rname = zfs_get_name(r);
- char *lat, *rat;
- uint64_t lcreate, rcreate;
- int ret;
-
- lat = (char *)strchr(lname, '@');
- rat = (char *)strchr(rname, '@');
-
- if (lat != NULL)
- *lat = '\0';
- if (rat != NULL)
- *rat = '\0';
-
- ret = strcmp(lname, rname);
- if (ret == 0 && (lat != NULL || rat != NULL)) {
- /*
- * If we're comparing a dataset to one of its snapshots, we
- * always make the full dataset first.
- */
- if (lat == NULL) {
- ret = -1;
- } else if (rat == NULL) {
- ret = 1;
- } else {
- /*
- * If we have two snapshots from the same dataset, then
- * we want to sort them according to creation time. We
- * use the hidden CREATETXG property to get an absolute
- * ordering of snapshots.
- */
- lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
- rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
-
- /*
- * Both lcreate and rcreate being 0 means we don't have
- * properties and we should compare full name.
- */
- if (lcreate == 0 && rcreate == 0)
- ret = strcmp(lat + 1, rat + 1);
- else if (lcreate < rcreate)
- ret = -1;
- else if (lcreate > rcreate)
- ret = 1;
- }
- }
-
- if (lat != NULL)
- *lat = '@';
- if (rat != NULL)
- *rat = '@';
-
- return (ret);
-}
-
-/*
- * Sort datasets by specified columns.
- *
- * o Numeric types sort in ascending order.
- * o String types sort in alphabetical order.
- * o Types inappropriate for a row sort that row to the literal
- * bottom, regardless of the specified ordering.
- *
- * If no sort columns are specified, or two datasets compare equally
- * across all specified columns, they are sorted alphabetically by name
- * with snapshots grouped under their parents.
- */
-static int
-zfs_sort(const void *larg, const void *rarg, void *data)
-{
- zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
- zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
- zfs_sort_column_t *sc = (zfs_sort_column_t *)data;
- zfs_sort_column_t *psc;
-
- for (psc = sc; psc != NULL; psc = psc->sc_next) {
- char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN];
- char *lstr, *rstr;
- uint64_t lnum, rnum;
- boolean_t lvalid, rvalid;
- int ret = 0;
-
- /*
- * We group the checks below the generic code. If 'lstr' and
- * 'rstr' are non-NULL, then we do a string based comparison.
- * Otherwise, we compare 'lnum' and 'rnum'.
- */
- lstr = rstr = NULL;
- if (psc->sc_prop == ZPROP_INVAL) {
- nvlist_t *luser, *ruser;
- nvlist_t *lval, *rval;
-
- luser = zfs_get_user_props(l);
- ruser = zfs_get_user_props(r);
-
- lvalid = (nvlist_lookup_nvlist(luser,
- psc->sc_user_prop, &lval) == 0);
- rvalid = (nvlist_lookup_nvlist(ruser,
- psc->sc_user_prop, &rval) == 0);
-
- if (lvalid)
- verify(nvlist_lookup_string(lval,
- ZPROP_VALUE, &lstr) == 0);
- if (rvalid)
- verify(nvlist_lookup_string(rval,
- ZPROP_VALUE, &rstr) == 0);
- } else if (psc->sc_prop == ZFS_PROP_NAME) {
- lvalid = rvalid = B_TRUE;
-
- (void) strlcpy(lbuf, zfs_get_name(l), sizeof (lbuf));
- (void) strlcpy(rbuf, zfs_get_name(r), sizeof (rbuf));
-
- lstr = lbuf;
- rstr = rbuf;
- } else if (zfs_prop_is_string(psc->sc_prop)) {
- lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf,
- sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0);
- rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf,
- sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0);
-
- lstr = lbuf;
- rstr = rbuf;
- } else {
- lvalid = zfs_prop_valid_for_type(psc->sc_prop,
- zfs_get_type(l));
- rvalid = zfs_prop_valid_for_type(psc->sc_prop,
- zfs_get_type(r));
-
- if (lvalid)
- (void) zfs_prop_get_numeric(l, psc->sc_prop,
- &lnum, NULL, NULL, 0);
- if (rvalid)
- (void) zfs_prop_get_numeric(r, psc->sc_prop,
- &rnum, NULL, NULL, 0);
- }
-
- if (!lvalid && !rvalid)
- continue;
- else if (!lvalid)
- return (1);
- else if (!rvalid)
- return (-1);
-
- if (lstr)
- ret = strcmp(lstr, rstr);
- else if (lnum < rnum)
- ret = -1;
- else if (lnum > rnum)
- ret = 1;
-
- if (ret != 0) {
- if (psc->sc_reverse == B_TRUE)
- ret = (ret < 0) ? 1 : -1;
- return (ret);
- }
- }
-
- return (zfs_compare(larg, rarg, NULL));
-}
-
-int
-zfs_for_each(int argc, char **argv, int flags, zfs_type_t types,
- zfs_sort_column_t *sortcol, zprop_list_t **proplist, int limit,
- zfs_iter_f callback, void *data)
-{
- callback_data_t cb = {0};
- int ret = 0;
- zfs_node_t *node;
- uu_avl_walk_t *walk;
-
- avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t),
- offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT);
-
- if (avl_pool == NULL)
- nomem();
-
- cb.cb_sortcol = sortcol;
- cb.cb_flags = flags;
- cb.cb_proplist = proplist;
- cb.cb_types = types;
- cb.cb_depth_limit = limit;
- /*
- * If cb_proplist is provided then in the zfs_handles created we
- * retain only those properties listed in cb_proplist and sortcol.
- * The rest are pruned. So, the caller should make sure that no other
- * properties other than those listed in cb_proplist/sortcol are
- * accessed.
- *
- * If cb_proplist is NULL then we retain all the properties. We
- * always retain the zoned property, which some other properties
- * need (userquota & friends), and the createtxg property, which
- * we need to sort snapshots.
- */
- if (cb.cb_proplist && *cb.cb_proplist) {
- zprop_list_t *p = *cb.cb_proplist;
-
- while (p) {
- if (p->pl_prop >= ZFS_PROP_TYPE &&
- p->pl_prop < ZFS_NUM_PROPS) {
- cb.cb_props_table[p->pl_prop] = B_TRUE;
- }
- p = p->pl_next;
- }
-
- while (sortcol) {
- if (sortcol->sc_prop >= ZFS_PROP_TYPE &&
- sortcol->sc_prop < ZFS_NUM_PROPS) {
- cb.cb_props_table[sortcol->sc_prop] = B_TRUE;
- }
- sortcol = sortcol->sc_next;
- }
-
- cb.cb_props_table[ZFS_PROP_ZONED] = B_TRUE;
- cb.cb_props_table[ZFS_PROP_CREATETXG] = B_TRUE;
- } else {
- (void) memset(cb.cb_props_table, B_TRUE,
- sizeof (cb.cb_props_table));
- }
-
- if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL)
- nomem();
-
- if (argc == 0) {
- /*
- * If given no arguments, iterate over all datasets.
- */
- cb.cb_flags |= ZFS_ITER_RECURSE;
- ret = zfs_iter_root(g_zfs, zfs_callback, &cb);
- } else {
- int i;
- zfs_handle_t *zhp;
- zfs_type_t argtype;
-
- /*
- * If we're recursive, then we always allow filesystems as
- * arguments. If we also are interested in snapshots or
- * bookmarks, then we can take volumes as well.
- */
- argtype = types;
- if (flags & ZFS_ITER_RECURSE) {
- argtype |= ZFS_TYPE_FILESYSTEM;
- if (types & (ZFS_TYPE_SNAPSHOT | ZFS_TYPE_BOOKMARK))
- argtype |= ZFS_TYPE_VOLUME;
- }
-
- for (i = 0; i < argc; i++) {
- if (flags & ZFS_ITER_ARGS_CAN_BE_PATHS) {
- zhp = zfs_path_to_zhandle(g_zfs, argv[i],
- argtype);
- } else {
- zhp = zfs_open(g_zfs, argv[i], argtype);
- }
- if (zhp != NULL)
- ret |= zfs_callback(zhp, &cb);
- else
- ret = 1;
- }
- }
-
- /*
- * At this point we've got our AVL tree full of zfs handles, so iterate
- * over each one and execute the real user callback.
- */
- for (node = uu_avl_first(cb.cb_avl); node != NULL;
- node = uu_avl_next(cb.cb_avl, node))
- ret |= callback(node->zn_handle, data);
-
- /*
- * Finally, clean up the AVL tree.
- */
- if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL)
- nomem();
-
- while ((node = uu_avl_walk_next(walk)) != NULL) {
- uu_avl_remove(cb.cb_avl, node);
- zfs_close(node->zn_handle);
- free(node);
- }
-
- uu_avl_walk_end(walk);
- uu_avl_destroy(cb.cb_avl);
- uu_avl_pool_destroy(avl_pool);
-
- return (ret);
-}
diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.h b/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.h
deleted file mode 100644
index b89b466ce6fe..000000000000
--- a/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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
- * or http://www.opensolaris.org/os/licensing.
- * 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 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved.
- * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
- */
-
-#ifndef ZFS_ITER_H
-#define ZFS_ITER_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct zfs_sort_column {
- struct zfs_sort_column *sc_next;
- struct zfs_sort_column *sc_last;
- zfs_prop_t sc_prop;
- char *sc_user_prop;
- boolean_t sc_reverse;
-} zfs_sort_column_t;
-
-#define ZFS_ITER_RECURSE (1 << 0)
-#define ZFS_ITER_ARGS_CAN_BE_PATHS (1 << 1)
-#define ZFS_ITER_PROP_LISTSNAPS (1 << 2)
-#define ZFS_ITER_DEPTH_LIMIT (1 << 3)
-#define ZFS_ITER_RECVD_PROPS (1 << 4)
-#define ZFS_ITER_SIMPLE (1 << 5)
-#define ZFS_ITER_LITERAL_PROPS (1 << 6)
-
-int zfs_for_each(int, char **, int options, zfs_type_t,
- zfs_sort_column_t *, zprop_list_t **, int, zfs_iter_f, void *);
-int zfs_add_sort_column(zfs_sort_column_t **, const char *, boolean_t);
-void zfs_free_sort_columns(zfs_sort_column_t *);
-boolean_t zfs_sort_only_by_name(const zfs_sort_column_t *);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* ZFS_ITER_H */
diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c b/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
deleted file mode 100644
index d453ba030488..000000000000
--- a/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c
+++ /dev/null
@@ -1,7592 +0,0 @@
-/*
- * 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
- * or http://www.opensolaris.org/os/licensing.
- * 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, 2016 by Delphix. All rights reserved.
- * Copyright 2012 Milan Jurik. All rights reserved.
- * Copyright (c) 2012, Joyent, Inc. All rights reserved.
- * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved.
- * Copyright (c) 2012 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
- * Copyright (c) 2013 Steven Hartland. All rights reserved.
- * Copyright (c) 2014 Integros [integros.com]
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>.
- * Copyright 2016 Nexenta Systems, Inc.
- * Copyright (c) 2019 Datto Inc.
- */
-
-#include <assert.h>
-#include <ctype.h>
-#include <errno.h>
-#include <getopt.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 <sys/debug.h>
-#include <sys/list.h>
-#include <sys/mntent.h>
-#include <sys/mnttab.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/fs/zfs.h>
-#include <sys/types.h>
-#include <time.h>
-#include <err.h>
-#include <jail.h>
-
-#include <libzfs.h>
-#include <libzfs_core.h>
-#include <zfs_prop.h>
-#include <zfs_deleg.h>
-#include <libuutil.h>
-#ifdef illumos
-#include <aclutils.h>
-#include <directory.h>
-#include <idmap.h>
-#include <libshare.h>
-#endif
-
-#include "zfs_iter.h"
-#include "zfs_util.h"
-#include "zfs_comutil.h"
-
-libzfs_handle_t *g_zfs;
-
-static FILE *mnttab_file;
-static char history_str[HIS_MAX_RECORD_LEN];
-static boolean_t log_history = B_TRUE;
-
-static int zfs_do_clone(int argc, char **argv);
-static int zfs_do_create(int argc, char **argv);
-static int zfs_do_destroy(int argc, char **argv);
-static int zfs_do_get(int argc, char **argv);
-static int zfs_do_inherit(int argc, char **argv);
-static int zfs_do_list(int argc, char **argv);
-static int zfs_do_mount(int argc, char **argv);
-static int zfs_do_rename(int argc, char **argv);
-static int zfs_do_rollback(int argc, char **argv);
-static int zfs_do_set(int argc, char **argv);
-static int zfs_do_upgrade(int argc, char **argv);
-static int zfs_do_snapshot(int argc, char **argv);
-static int zfs_do_unmount(int argc, char **argv);
-static int zfs_do_share(int argc, char **argv);
-static int zfs_do_unshare(int argc, char **argv);
-static int zfs_do_send(int argc, char **argv);
-static int zfs_do_receive(int argc, char **argv);
-static int zfs_do_promote(int argc, char **argv);
-static int zfs_do_userspace(int argc, char **argv);
-static int zfs_do_allow(int argc, char **argv);
-static int zfs_do_unallow(int argc, char **argv);
-static int zfs_do_hold(int argc, char **argv);
-static int zfs_do_holds(int argc, char **argv);
-static int zfs_do_release(int argc, char **argv);
-static int zfs_do_diff(int argc, char **argv);
-static int zfs_do_jail(int argc, char **argv);
-static int zfs_do_unjail(int argc, char **argv);
-static int zfs_do_bookmark(int argc, char **argv);
-static int zfs_do_remap(int argc, char **argv);
-static int zfs_do_channel_program(int argc, char **argv);
-
-/*
- * 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 {
- HELP_CLONE,
- HELP_CREATE,
- HELP_DESTROY,
- HELP_GET,
- HELP_INHERIT,
- HELP_UPGRADE,
- HELP_JAIL,
- HELP_UNJAIL,
- HELP_LIST,
- HELP_MOUNT,
- HELP_PROMOTE,
- HELP_RECEIVE,
- HELP_RENAME,
- HELP_ROLLBACK,
- HELP_SEND,
- HELP_SET,
- HELP_SHARE,
- HELP_SNAPSHOT,
- HELP_UNMOUNT,
- HELP_UNSHARE,
- HELP_ALLOW,
- HELP_UNALLOW,
- HELP_USERSPACE,
- HELP_GROUPSPACE,
- HELP_HOLD,
- HELP_HOLDS,
- HELP_RELEASE,
- HELP_DIFF,
- HELP_REMAP,
- HELP_BOOKMARK,
- HELP_CHANNEL_PROGRAM,
-} zfs_help_t;
-
-typedef struct zfs_command {
- const char *name;
- int (*func)(int argc, char **argv);
- zfs_help_t usage;
-} zfs_command_t;
-
-/*
- * 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[] = {
- { "create", zfs_do_create, HELP_CREATE },
- { "destroy", zfs_do_destroy, HELP_DESTROY },
- { NULL },
- { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT },
- { "rollback", zfs_do_rollback, HELP_ROLLBACK },
- { "clone", zfs_do_clone, HELP_CLONE },
- { "promote", zfs_do_promote, HELP_PROMOTE },
- { "rename", zfs_do_rename, HELP_RENAME },
- { "bookmark", zfs_do_bookmark, HELP_BOOKMARK },
- { "program", zfs_do_channel_program, HELP_CHANNEL_PROGRAM },
- { NULL },
- { "list", zfs_do_list, HELP_LIST },
- { NULL },
- { "set", zfs_do_set, HELP_SET },
- { "get", zfs_do_get, HELP_GET },
- { "inherit", zfs_do_inherit, HELP_INHERIT },
- { "upgrade", zfs_do_upgrade, HELP_UPGRADE },
- { "userspace", zfs_do_userspace, HELP_USERSPACE },
- { "groupspace", zfs_do_userspace, HELP_GROUPSPACE },
- { NULL },
- { "mount", zfs_do_mount, HELP_MOUNT },
- { "unmount", zfs_do_unmount, HELP_UNMOUNT },
- { "share", zfs_do_share, HELP_SHARE },
- { "unshare", zfs_do_unshare, HELP_UNSHARE },
- { NULL },
- { "send", zfs_do_send, HELP_SEND },
- { "receive", zfs_do_receive, HELP_RECEIVE },
- { NULL },
- { "allow", zfs_do_allow, HELP_ALLOW },
- { NULL },
- { "unallow", zfs_do_unallow, HELP_UNALLOW },
- { NULL },
- { "hold", zfs_do_hold, HELP_HOLD },
- { "holds", zfs_do_holds, HELP_HOLDS },
- { "release", zfs_do_release, HELP_RELEASE },
- { "diff", zfs_do_diff, HELP_DIFF },
- { NULL },
- { "jail", zfs_do_jail, HELP_JAIL },
- { "unjail", zfs_do_unjail, HELP_UNJAIL },
- { "remap", zfs_do_remap, HELP_REMAP },
-};
-
-#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))
-
-zfs_command_t *current_command;
-
-static const char *
-get_usage(zfs_help_t idx)
-{
- switch (idx) {
- case HELP_CLONE:
- return (gettext("\tclone [-p] [-o property=value] ... "
- "<snapshot> <filesystem|volume>\n"));
- case HELP_CREATE:
- return (gettext("\tcreate [-pu] [-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"
- "\tdestroy <filesystem|volume>#<bookmark>\n"));
- case HELP_GET:
- return (gettext("\tget [-rHp] [-d max] "
- "[-o \"all\" | field[,...]]\n"
- "\t [-t type[,...]] [-s source[,...]]\n"
- "\t <\"all\" | property[,...]> "
- "[filesystem|volume|snapshot|bookmark] ...\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_JAIL:
- return (gettext("\tjail <jailid|jailname> <filesystem>\n"));
- case HELP_UNJAIL:
- return (gettext("\tunjail <jailid|jailname> <filesystem>\n"));
- case HELP_LIST:
- return (gettext("\tlist [-Hp] [-r|-d max] [-o property[,...]] "
- "[-s property]...\n\t [-S property]... [-t type[,...]] "
- "[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|recv [-vnsFMu] <filesystem|volume|"
- "snapshot>\n"
- "\treceive|recv [-vnsFMu] [-o origin=<snapshot>] [-d | -e] "
- "<filesystem>\n"
- "\treceive|recv -A <filesystem|volume>\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>\n"
- "\trename <bookmark> <bookmark>\n"
- "\trename -u [-p] <filesystem> <filesystem>"));
- case HELP_ROLLBACK:
- return (gettext("\trollback [-rRf] <snapshot>\n"));
- case HELP_SEND:
- return (gettext("\tsend [-DnPpRvLec] [-[iI] snapshot] "
- "<snapshot>\n"
- "\tsend [-LPcenv] [-i snapshot|bookmark] "
- "<filesystem|volume|snapshot>\n"
- "\tsend [-nvPe] -t <receive_resume_token>\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|snap [-r] [-o property=value] ... "
- "<filesystem|volume>@<snap> ...\n"));
- case HELP_UNMOUNT:
- return (gettext("\tunmount|umount [-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 [-Hinp] [-o field[,...]] "
- "[-s field] ...\n"
- "\t [-S field] ... [-t type[,...]] "
- "<filesystem|snapshot>\n"));
- case HELP_GROUPSPACE:
- return (gettext("\tgroupspace [-Hinp] [-o field[,...]] "
- "[-s field] ...\n"
- "\t [-S field] ... [-t type[,...]] "
- "<filesystem|snapshot>\n"));
- case HELP_HOLD:
- return (gettext("\thold [-r] <tag> <snapshot> ...\n"));
- case HELP_HOLDS:
- return (gettext("\tholds [-Hp] [-r|-d depth] "
- "<filesystem|volume|snapshot> ...\n"));
- case HELP_RELEASE:
- return (gettext("\trelease [-r] <tag> <snapshot> ...\n"));
- case HELP_DIFF:
- return (gettext("\tdiff [-FHt] <snapshot> "
- "[snapshot|filesystem]\n"));
- case HELP_REMAP:
- return (gettext("\tremap <filesystem | volume>\n"));
- case HELP_BOOKMARK:
- return (gettext("\tbookmark <snapshot> <bookmark>\n"));
- case HELP_CHANNEL_PROGRAM:
- return (gettext("\tprogram [-jn] [-t <instruction limit>] "
- "[-m <memory limit (b)>] <pool> <program file> "
- "[lua args...]\n"));
- }
-
- abort();
- /* NOTREACHED */
-}
-
-void
-nomem(void)
-{
- (void) fprintf(stderr, gettext("internal error: out of memory\n"));
- exit(1);
-}
-
-/*
- * Utility function to guarantee malloc() success.
- */
-
-void *
-safe_malloc(size_t size)
-{
- void *data;
-
- if ((data = calloc(1, size)) == NULL)
- nomem();
-
- return (data);
-}
-
-void *
-safe_realloc(void *data, size_t size)
-{
- void *newp;
- if ((newp = realloc(data, size)) == NULL) {
- free(data);
- nomem();
- }
-
- return (newp);
-}
-
-static char *
-safe_strdup(char *str)
-{
- char *dupstr = strdup(str);
-
- if (dupstr == NULL)
- nomem();
-
- return (dupstr);
-}
-
-/*
- * Callback routine that will print out information for each of
- * the properties.
- */
-static int
-usage_prop_cb(int prop, void *cb)
-{
- FILE *fp = cb;
-
- (void) fprintf(fp, "\t%-15s ", zfs_prop_to_name(prop));
-
- if (zfs_prop_readonly(prop))
- (void) fprintf(fp, " NO ");
- else
- (void) fprintf(fp, "YES ");
-
- if (zfs_prop_inheritable(prop))
- (void) fprintf(fp, " YES ");
- else
- (void) fprintf(fp, " NO ");
-
- if (zfs_prop_values(prop) == NULL)
- (void) fprintf(fp, "-\n");
- else
- (void) fprintf(fp, "%s\n", zfs_prop_values(prop));
-
- 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
-usage(boolean_t requested)
-{
- int i;
- boolean_t show_properties = B_FALSE;
- FILE *fp = requested ? stdout : stderr;
-
- if (current_command == NULL) {
-
- (void) fprintf(fp, gettext("usage: zfs command args ...\n"));
- (void) fprintf(fp,
- gettext("where 'command' is one of the following:\n\n"));
-
- for (i = 0; i < NCOMMAND; i++) {
- if (command_table[i].name == NULL)
- (void) fprintf(fp, "\n");
- else
- (void) fprintf(fp, "%s",
- get_usage(command_table[i].usage));
- }
-
- (void) fprintf(fp, gettext("\nEach dataset is of the form: "
- "pool/[dataset/]*dataset[@name]\n"));
- } else {
- (void) fprintf(fp, gettext("usage:\n"));
- (void) fprintf(fp, "%s", get_usage(current_command->usage));
- }
-
- if (current_command != NULL &&
- (strcmp(current_command->name, "set") == 0 ||
- strcmp(current_command->name, "get") == 0 ||
- strcmp(current_command->name, "inherit") == 0 ||
- strcmp(current_command->name, "list") == 0))
- show_properties = B_TRUE;
-
- if (show_properties) {
- (void) fprintf(fp,
- gettext("\nThe following properties are supported:\n"));
-
- (void) fprintf(fp, "\n\t%-14s %s %s %s\n\n",
- "PROPERTY", "EDIT", "INHERIT", "VALUES");
-
- /* Iterate over all properties */
- (void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE,
- ZFS_TYPE_DATASET);
-
- (void) fprintf(fp, "\t%-15s ", "userused@...");
- (void) fprintf(fp, " NO NO <size>\n");
- (void) fprintf(fp, "\t%-15s ", "groupused@...");
- (void) fprintf(fp, " NO NO <size>\n");
- (void) fprintf(fp, "\t%-15s ", "userquota@...");
- (void) fprintf(fp, "YES NO <size> | none\n");
- (void) fprintf(fp, "\t%-15s ", "groupquota@...");
- (void) fprintf(fp, "YES NO <size> | none\n");
- (void) fprintf(fp, "\t%-15s ", "written@<snap>");
- (void) fprintf(fp, " NO NO <size>\n");
-
- (void) fprintf(fp, gettext("\nSizes are specified in bytes "
- "with standard units such as K, M, G, etc.\n"));
- (void) fprintf(fp, gettext("\nUser-defined properties can "
- "be specified by using a name containing a colon (:).\n"));
- (void) fprintf(fp, gettext("\nThe {user|group}{used|quota}@ "
- "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 {
- (void) fprintf(fp,
- gettext("\nFor the property list, run: %s\n"),
- "zfs set|get");
- (void) fprintf(fp,
- gettext("\nFor the delegated permission list, run: %s\n"),
- "zfs allow|unallow");
- }
-
- /*
- * See comments at end of main().
- */
- if (getenv("ZFS_ABORT") != NULL) {
- (void) printf("dumping core by request\n");
- abort();
- }
-
- exit(requested ? 0 : 2);
-}
-
-/*
- * Take a property=value argument string and add it to the given nvlist.
- * Modifies the argument inplace.
- */
-static int
-parseprop(nvlist_t *props, char *propname)
-{
- char *propval, *strval;
-
- if ((propval = strchr(propname, '=')) == NULL) {
- (void) fprintf(stderr, gettext("missing "
- "'=' for property=value argument\n"));
- return (-1);
- }
- *propval = '\0';
- propval++;
- if (nvlist_lookup_string(props, propname, &strval) == 0) {
- (void) fprintf(stderr, gettext("property '%s' "
- "specified multiple times\n"), propname);
- return (-1);
- }
- if (nvlist_add_string(props, propname, propval) != 0)
- nomem();
- return (0);
-}
-
-static int
-parse_depth(char *opt, int *flags)
-{
- char *tmp;
- int depth;
-
- depth = (int)strtol(opt, &tmp, 0);
- if (*tmp) {
- (void) fprintf(stderr,
- gettext("%s is not an integer\n"), opt);
- usage(B_FALSE);
- }
- if (depth < 0) {
- (void) fprintf(stderr,
- gettext("Depth can not be negative.\n"));
- usage(B_FALSE);
- }
- *flags |= (ZFS_ITER_DEPTH_LIMIT|ZFS_ITER_RECURSE);
- return (depth);
-}
-
-#define PROGRESS_DELAY 2 /* seconds */
-
-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 time_t pt_begin;
-static char *pt_header = NULL;
-static boolean_t pt_shown;
-
-static void
-start_progress_timer(void)
-{
- pt_begin = time(NULL) + PROGRESS_DELAY;
- pt_shown = B_FALSE;
-}
-
-static void
-set_progress_header(char *header)
-{
- assert(pt_header == NULL);
- pt_header = safe_strdup(header);
- if (pt_shown) {
- (void) printf("%s: ", header);
- (void) fflush(stdout);
- }
-}
-
-static void
-update_progress(char *update)
-{
- if (!pt_shown && time(NULL) > pt_begin) {
- int len = strlen(update);
-
- (void) printf("%s: %s%*.*s", pt_header, update, len, len,
- pt_reverse);
- (void) fflush(stdout);
- pt_shown = B_TRUE;
- } else if (pt_shown) {
- int len = strlen(update);
-
- (void) printf("%s%*.*s", update, len, len, pt_reverse);
- (void) fflush(stdout);
- }
-}
-
-static void
-finish_progress(char *done)
-{
- if (pt_shown) {
- (void) printf("%s\n", done);
- (void) fflush(stdout);
- }
- free(pt_header);
- pt_header = NULL;
-}
-
-/*
- * Check if the dataset is mountable and should be automatically mounted.
- */
-static boolean_t
-should_auto_mount(zfs_handle_t *zhp)
-{
- if (!zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, zfs_get_type(zhp)))
- return (B_FALSE);
- return (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON);
-}
-
-/*
- * 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
-zfs_do_clone(int argc, char **argv)
-{
- zfs_handle_t *zhp = NULL;
- boolean_t parents = B_FALSE;
- nvlist_t *props;
- int ret = 0;
- int c;
-
- if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
- nomem();
-
- /* check options */
- while ((c = getopt(argc, argv, "o:p")) != -1) {
- switch (c) {
- case 'o':
- if (parseprop(props, optarg) != 0)
- return (1);
- break;
- case 'p':
- parents = B_TRUE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- goto usage;
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check number of arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing source dataset "
- "argument\n"));
- goto usage;
- }
- if (argc < 2) {
- (void) fprintf(stderr, gettext("missing target dataset "
- "argument\n"));
- goto usage;
- }
- if (argc > 2) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- goto usage;
- }
-
- /* open the source dataset */
- if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
- return (1);
-
- if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM |
- 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.
- */
- if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM |
- ZFS_TYPE_VOLUME))
- return (0);
- if (zfs_create_ancestors(g_zfs, argv[1]) != 0)
- return (1);
- }
-
- /* pass to libzfs */
- ret = zfs_clone(zhp, argv[1], props);
-
- /* create the mountpoint if necessary */
- if (ret == 0) {
- zfs_handle_t *clone;
-
- clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_DATASET);
- if (clone != NULL) {
- /*
- * If the user doesn't want the dataset
- * automatically mounted, then skip the mount/share
- * step.
- */
- if (should_auto_mount(clone)) {
- if ((ret = zfs_mount(clone, NULL, 0)) != 0) {
- (void) fprintf(stderr, gettext("clone "
- "successfully created, "
- "but not mounted\n"));
- } else if ((ret = zfs_share(clone)) != 0) {
- (void) fprintf(stderr, gettext("clone "
- "successfully created, "
- "but not shared\n"));
- }
- }
- zfs_close(clone);
- }
- }
-
- zfs_close(zhp);
- nvlist_free(props);
-
- return (!!ret);
-
-usage:
- if (zhp)
- zfs_close(zhp);
- nvlist_free(props);
- usage(B_FALSE);
- return (-1);
-}
-
-/*
- * zfs create [-pu] [-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.
- *
- * The '-u' flag prevents mounting of newly created file system.
- */
-static int
-zfs_do_create(int argc, char **argv)
-{
- zfs_type_t type = ZFS_TYPE_FILESYSTEM;
- zfs_handle_t *zhp = NULL;
- uint64_t volsize = 0;
- int c;
- boolean_t noreserve = B_FALSE;
- boolean_t bflag = B_FALSE;
- boolean_t parents = B_FALSE;
- boolean_t nomount = B_FALSE;
- int ret = 1;
- nvlist_t *props;
- uint64_t intval;
-
- if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
- nomem();
-
- /* check options */
- while ((c = getopt(argc, argv, ":V:b:so:pu")) != -1) {
- switch (c) {
- case 'V':
- type = ZFS_TYPE_VOLUME;
- if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
- (void) fprintf(stderr, gettext("bad volume "
- "size '%s': %s\n"), optarg,
- libzfs_error_description(g_zfs));
- goto error;
- }
-
- if (nvlist_add_uint64(props,
- zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0)
- nomem();
- volsize = intval;
- break;
- case 'p':
- parents = B_TRUE;
- break;
- case 'b':
- bflag = B_TRUE;
- if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) {
- (void) fprintf(stderr, gettext("bad volume "
- "block size '%s': %s\n"), optarg,
- libzfs_error_description(g_zfs));
- goto error;
- }
-
- if (nvlist_add_uint64(props,
- zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
- intval) != 0)
- nomem();
- break;
- case 'o':
- if (parseprop(props, optarg) != 0)
- goto error;
- break;
- case 's':
- noreserve = B_TRUE;
- break;
- case 'u':
- nomount = B_TRUE;
- break;
- case ':':
- (void) fprintf(stderr, gettext("missing size "
- "argument\n"));
- goto badusage;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- goto badusage;
- }
- }
-
- if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) {
- (void) fprintf(stderr, gettext("'-s' and '-b' can only be "
- "used when creating a volume\n"));
- goto badusage;
- }
- if (nomount && type != ZFS_TYPE_FILESYSTEM) {
- (void) fprintf(stderr, gettext("'-u' can only be "
- "used when creating a file system\n"));
- goto badusage;
- }
-
- argc -= optind;
- argv += optind;
-
- /* check number of arguments */
- if (argc == 0) {
- (void) fprintf(stderr, gettext("missing %s argument\n"),
- zfs_type_to_name(type));
- goto badusage;
- }
- if (argc > 1) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- goto badusage;
- }
-
- if (type == ZFS_TYPE_VOLUME && !noreserve) {
- zpool_handle_t *zpool_handle;
- nvlist_t *real_props = NULL;
- uint64_t spa_version;
- char *p;
- zfs_prop_t resv_prop;
- char *strval;
- char msg[1024];
-
- if ((p = strchr(argv[0], '/')) != NULL)
- *p = '\0';
- zpool_handle = zpool_open(g_zfs, argv[0]);
- if (p != NULL)
- *p = '/';
- if (zpool_handle == NULL)
- goto error;
- spa_version = zpool_get_prop_int(zpool_handle,
- ZPOOL_PROP_VERSION, NULL);
- if (spa_version >= SPA_VERSION_REFRESERVATION)
- resv_prop = ZFS_PROP_REFRESERVATION;
- else
- resv_prop = ZFS_PROP_RESERVATION;
-
- (void) snprintf(msg, sizeof (msg),
- gettext("cannot create '%s'"), argv[0]);
- if (props && (real_props = zfs_valid_proplist(g_zfs, type,
- props, 0, NULL, zpool_handle, msg)) == NULL) {
- zpool_close(zpool_handle);
- goto error;
- }
- zpool_close(zpool_handle);
-
- volsize = zvol_volsize_to_reservation(volsize, real_props);
- nvlist_free(real_props);
-
- if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop),
- &strval) != 0) {
- if (nvlist_add_uint64(props,
- zfs_prop_to_name(resv_prop), volsize) != 0) {
- nvlist_free(props);
- nomem();
- }
- }
- }
-
- if (parents && zfs_name_valid(argv[0], type)) {
- /*
- * Now create the ancestors of target dataset. If the target
- * already exists and '-p' option was used we should not
- * complain.
- */
- if (zfs_dataset_exists(g_zfs, argv[0], type)) {
- ret = 0;
- goto error;
- }
- if (zfs_create_ancestors(g_zfs, argv[0]) != 0)
- goto error;
- }
-
- /* pass to libzfs */
- if (zfs_create(g_zfs, argv[0], type, props) != 0)
- goto error;
-
- if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
- goto error;
-
- ret = 0;
-
- /*
- * Mount and/or share the new filesystem as appropriate. We provide a
- * 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 the user doesn't want the dataset automatically mounted,
- * then skip the mount/share step altogether.
- */
- if (!nomount && should_auto_mount(zhp)) {
- if (zfs_mount(zhp, NULL, 0) != 0) {
- (void) fprintf(stderr, gettext("filesystem "
- "successfully created, but not mounted\n"));
- ret = 1;
- } else if (zfs_share(zhp) != 0) {
- (void) fprintf(stderr, gettext("filesystem "
- "successfully created, but not shared\n"));
- ret = 1;
- }
- }
-
-error:
- if (zhp)
- zfs_close(zhp);
- nvlist_free(props);
- return (ret);
-badusage:
- nvlist_free(props);
- usage(B_FALSE);
- 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 {
- boolean_t cb_first;
- boolean_t cb_force;
- boolean_t cb_recurse;
- boolean_t cb_error;
- boolean_t cb_doclones;
- zfs_handle_t *cb_target;
- boolean_t cb_defer_destroy;
- boolean_t cb_verbose;
- boolean_t cb_parsable;
- boolean_t cb_dryrun;
- nvlist_t *cb_nvl;
- nvlist_t *cb_batchedsnaps;
-
- /* first snap in contiguous run */
- char *cb_firstsnap;
- /* previous snap in contiguous run */
- char *cb_prevsnap;
- int64_t cb_snapused;
- char *cb_snapspec;
- char *cb_bookmark;
-} destroy_cbdata_t;
-
-/*
- * Check for any dependents based on the '-r' or '-R' flags.
- */
-static int
-destroy_check_dependent(zfs_handle_t *zhp, void *data)
-{
- destroy_cbdata_t *cbp = data;
- const char *tname = zfs_get_name(cbp->cb_target);
- const char *name = zfs_get_name(zhp);
-
- if (strncmp(tname, name, strlen(tname)) == 0 &&
- (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) {
- /*
- * This is a direct descendant, not a clone somewhere else in
- * the hierarchy.
- */
- if (cbp->cb_recurse)
- goto out;
-
- if (cbp->cb_first) {
- (void) fprintf(stderr, gettext("cannot destroy '%s': "
- "%s has children\n"),
- zfs_get_name(cbp->cb_target),
- zfs_type_to_name(zfs_get_type(cbp->cb_target)));
- (void) fprintf(stderr, gettext("use '-r' to destroy "
- "the following datasets:\n"));
- cbp->cb_first = B_FALSE;
- cbp->cb_error = B_TRUE;
- }
-
- (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
- } 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 &&
- zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT)
- goto out;
-
- if (cbp->cb_first) {
- (void) fprintf(stderr, gettext("cannot destroy '%s': "
- "%s has dependent clones\n"),
- zfs_get_name(cbp->cb_target),
- zfs_type_to_name(zfs_get_type(cbp->cb_target)));
- (void) fprintf(stderr, gettext("use '-R' to destroy "
- "the following datasets:\n"));
- cbp->cb_first = B_FALSE;
- cbp->cb_error = B_TRUE;
- cbp->cb_dryrun = B_TRUE;
- }
-
- (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
- }
-
-out:
- zfs_close(zhp);
- return (0);
-}
-
-static int
-destroy_callback(zfs_handle_t *zhp, void *data)
-{
- destroy_cbdata_t *cb = data;
- const char *name = zfs_get_name(zhp);
-
- if (cb->cb_verbose) {
- if (cb->cb_parsable) {
- (void) printf("destroy\t%s\n", name);
- } else if (cb->cb_dryrun) {
- (void) printf(gettext("would destroy %s\n"),
- name);
- } else {
- (void) printf(gettext("will destroy %s\n"),
- name);
- }
- }
-
- /*
- * Ignore pools (which we've already flagged as an error before getting
- * here).
- */
- if (strchr(zfs_get_name(zhp), '/') == NULL &&
- zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
- zfs_close(zhp);
- return (0);
- }
- if (cb->cb_dryrun) {
- zfs_close(zhp);
- return (0);
- }
-
- /*
- * We batch up all contiguous snapshots (even of different
- * filesystems) and destroy them with one ioctl. We can't
- * simply do all snap deletions and then all fs deletions,
- * because we must delete a clone before its origin.
- */
- if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) {
- fnvlist_add_boolean(cb->cb_batchedsnaps, name);
- } else {
- int error = zfs_destroy_snaps_nvl(g_zfs,
- cb->cb_batchedsnaps, B_FALSE);
- fnvlist_free(cb->cb_batchedsnaps);
- cb->cb_batchedsnaps = fnvlist_alloc();
-
- if (error != 0 ||
- zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 ||
- zfs_destroy(zhp, cb->cb_defer_destroy) != 0) {
- zfs_close(zhp);
- return (-1);
- }
- }
-
- zfs_close(zhp);
- return (0);
-}
-
-static int
-destroy_print_cb(zfs_handle_t *zhp, void *arg)
-{
- destroy_cbdata_t *cb = arg;
- const char *name = zfs_get_name(zhp);
- int err = 0;
-
- if (nvlist_exists(cb->cb_nvl, name)) {
- if (cb->cb_firstsnap == NULL)
- cb->cb_firstsnap = strdup(name);
- if (cb->cb_prevsnap != NULL)
- free(cb->cb_prevsnap);
- /* this snap continues the current range */
- cb->cb_prevsnap = strdup(name);
- if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL)
- nomem();
- if (cb->cb_verbose) {
- if (cb->cb_parsable) {
- (void) printf("destroy\t%s\n", name);
- } else if (cb->cb_dryrun) {
- (void) printf(gettext("would destroy %s\n"),
- name);
- } else {
- (void) printf(gettext("will destroy %s\n"),
- name);
- }
- }
- } else if (cb->cb_firstsnap != NULL) {
- /* end of this range */
- uint64_t used = 0;
- err = lzc_snaprange_space(cb->cb_firstsnap,
- cb->cb_prevsnap, &used);
- cb->cb_snapused += used;
- free(cb->cb_firstsnap);
- cb->cb_firstsnap = NULL;
- free(cb->cb_prevsnap);
- cb->cb_prevsnap = NULL;
- }
- zfs_close(zhp);
- return (err);
-}
-
-static int
-destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb)
-{
- int err = 0;
- assert(cb->cb_firstsnap == NULL);
- assert(cb->cb_prevsnap == NULL);
- err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb, 0, 0);
- if (cb->cb_firstsnap != NULL) {
- uint64_t used = 0;
- if (err == 0) {
- err = lzc_snaprange_space(cb->cb_firstsnap,
- cb->cb_prevsnap, &used);
- }
- cb->cb_snapused += used;
- free(cb->cb_firstsnap);
- cb->cb_firstsnap = NULL;
- free(cb->cb_prevsnap);
- cb->cb_prevsnap = NULL;
- }
- return (err);
-}
-
-static int
-snapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg)
-{
- destroy_cbdata_t *cb = arg;
- int err = 0;
-
- /* Check for clones. */
- if (!cb->cb_doclones && !cb->cb_defer_destroy) {
- cb->cb_target = zhp;
- cb->cb_first = B_TRUE;
- err = zfs_iter_dependents(zhp, B_TRUE,
- destroy_check_dependent, cb);
- }
-
- if (err == 0) {
- if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp)))
- nomem();
- }
- zfs_close(zhp);
- return (err);
-}
-
-static int
-gather_snapshots(zfs_handle_t *zhp, void *arg)
-{
- destroy_cbdata_t *cb = arg;
- int err = 0;
-
- err = zfs_iter_snapspec(zhp, cb->cb_snapspec, snapshot_to_nvl_cb, cb);
- if (err == ENOENT)
- err = 0;
- if (err != 0)
- goto out;
-
- if (cb->cb_verbose) {
- err = destroy_print_snapshots(zhp, cb);
- if (err != 0)
- goto out;
- }
-
- if (cb->cb_recurse)
- err = zfs_iter_filesystems(zhp, gather_snapshots, cb);
-
-out:
- zfs_close(zhp);
- return (err);
-}
-
-static int
-destroy_clones(destroy_cbdata_t *cb)
-{
- nvpair_t *pair;
- for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL);
- pair != NULL;
- pair = nvlist_next_nvpair(cb->cb_nvl, pair)) {
- zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair),
- ZFS_TYPE_SNAPSHOT);
- if (zhp != NULL) {
- boolean_t defer = cb->cb_defer_destroy;
- int err = 0;
-
- /*
- * We can't defer destroy non-snapshots, so set it to
- * false while destroying the clones.
- */
- cb->cb_defer_destroy = B_FALSE;
- err = zfs_iter_dependents(zhp, B_FALSE,
- destroy_callback, cb);
- cb->cb_defer_destroy = defer;
- zfs_close(zhp);
- if (err != 0)
- return (err);
- }
- }
- return (0);
-}
-
-static int
-zfs_do_destroy(int argc, char **argv)
-{
- destroy_cbdata_t cb = { 0 };
- int rv = 0;
- int err = 0;
- int c;
- zfs_handle_t *zhp = NULL;
- char *at, *pound;
- zfs_type_t type = ZFS_TYPE_DATASET;
-
- /* check options */
- while ((c = getopt(argc, argv, "vpndfrR")) != -1) {
- switch (c) {
- case 'v':
- cb.cb_verbose = B_TRUE;
- break;
- case 'p':
- cb.cb_verbose = B_TRUE;
- cb.cb_parsable = B_TRUE;
- break;
- case 'n':
- cb.cb_dryrun = B_TRUE;
- break;
- case 'd':
- cb.cb_defer_destroy = B_TRUE;
- type = ZFS_TYPE_SNAPSHOT;
- break;
- case 'f':
- cb.cb_force = B_TRUE;
- break;
- case 'r':
- cb.cb_recurse = B_TRUE;
- break;
- case 'R':
- cb.cb_recurse = B_TRUE;
- cb.cb_doclones = B_TRUE;
- break;
- case '?':
- default:
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check number of arguments */
- if (argc == 0) {
- (void) fprintf(stderr, gettext("missing dataset argument\n"));
- usage(B_FALSE);
- }
- if (argc > 1) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- at = strchr(argv[0], '@');
- pound = strchr(argv[0], '#');
- if (at != NULL) {
-
- /* Build the list of snaps to destroy in cb_nvl. */
- cb.cb_nvl = fnvlist_alloc();
-
- *at = '\0';
- zhp = zfs_open(g_zfs, argv[0],
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- if (zhp == NULL)
- return (1);
-
- cb.cb_snapspec = at + 1;
- if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 ||
- cb.cb_error) {
- rv = 1;
- goto out;
- }
-
- if (nvlist_empty(cb.cb_nvl)) {
- (void) fprintf(stderr, gettext("could not find any "
- "snapshots to destroy; check snapshot names.\n"));
- rv = 1;
- goto out;
- }
-
- if (cb.cb_verbose) {
- char buf[16];
- zfs_nicenum(cb.cb_snapused, buf, sizeof (buf));
- if (cb.cb_parsable) {
- (void) printf("reclaim\t%llu\n",
- cb.cb_snapused);
- } else if (cb.cb_dryrun) {
- (void) printf(gettext("would reclaim %s\n"),
- buf);
- } else {
- (void) printf(gettext("will reclaim %s\n"),
- buf);
- }
- }
-
- if (!cb.cb_dryrun) {
- if (cb.cb_doclones) {
- cb.cb_batchedsnaps = fnvlist_alloc();
- err = destroy_clones(&cb);
- if (err == 0) {
- err = zfs_destroy_snaps_nvl(g_zfs,
- cb.cb_batchedsnaps, B_FALSE);
- }
- if (err != 0) {
- rv = 1;
- goto out;
- }
- }
- if (err == 0) {
- err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl,
- cb.cb_defer_destroy);
- }
- }
-
- if (err != 0)
- rv = 1;
- } else if (pound != NULL) {
- int err;
- nvlist_t *nvl;
-
- if (cb.cb_dryrun) {
- (void) fprintf(stderr,
- "dryrun is not supported with bookmark\n");
- return (-1);
- }
-
- if (cb.cb_defer_destroy) {
- (void) fprintf(stderr,
- "defer destroy is not supported with bookmark\n");
- return (-1);
- }
-
- if (cb.cb_recurse) {
- (void) fprintf(stderr,
- "recursive is not supported with bookmark\n");
- return (-1);
- }
-
- if (!zfs_bookmark_exists(argv[0])) {
- (void) fprintf(stderr, gettext("bookmark '%s' "
- "does not exist.\n"), argv[0]);
- return (1);
- }
-
- nvl = fnvlist_alloc();
- fnvlist_add_boolean(nvl, argv[0]);
-
- err = lzc_destroy_bookmarks(nvl, NULL);
- if (err != 0) {
- (void) zfs_standard_error(g_zfs, err,
- "cannot destroy bookmark");
- }
-
- nvlist_free(cb.cb_nvl);
-
- return (err);
- } else {
- /* Open the given dataset */
- if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL)
- return (1);
-
- cb.cb_target = zhp;
-
- /*
- * Perform an explicit check for pools before going any further.
- */
- if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL &&
- zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
- (void) fprintf(stderr, gettext("cannot destroy '%s': "
- "operation does not apply to pools\n"),
- zfs_get_name(zhp));
- (void) fprintf(stderr, gettext("use 'zfs destroy -r "
- "%s' to destroy all datasets in the pool\n"),
- zfs_get_name(zhp));
- (void) fprintf(stderr, gettext("use 'zpool destroy %s' "
- "to destroy the pool itself\n"), zfs_get_name(zhp));
- rv = 1;
- goto out;
- }
-
- /*
- * Check for any dependents and/or clones.
- */
- cb.cb_first = B_TRUE;
- if (!cb.cb_doclones &&
- zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent,
- &cb) != 0) {
- rv = 1;
- goto out;
- }
-
- if (cb.cb_error) {
- rv = 1;
- goto out;
- }
-
- cb.cb_batchedsnaps = fnvlist_alloc();
- if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback,
- &cb) != 0) {
- rv = 1;
- goto out;
- }
-
- /*
- * Do the real thing. The callback will close the
- * handle regardless of whether it succeeds or not.
- */
- err = destroy_callback(zhp, &cb);
- zhp = NULL;
- if (err == 0) {
- err = zfs_destroy_snaps_nvl(g_zfs,
- cb.cb_batchedsnaps, cb.cb_defer_destroy);
- }
- if (err != 0)
- rv = 1;
- }
-
-out:
- fnvlist_free(cb.cb_batchedsnaps);
- fnvlist_free(cb.cb_nvl);
- if (zhp != NULL)
- zfs_close(zhp);
- return (rv);
-}
-
-static boolean_t
-is_recvd_column(zprop_get_cbdata_t *cbp)
-{
- int i;
- zfs_get_column_t col;
-
- for (i = 0; i < ZFS_GET_NCOLS &&
- (col = cbp->cb_columns[i]) != GET_COL_NONE; i++)
- 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
-get_callback(zfs_handle_t *zhp, void *data)
-{
- char buf[ZFS_MAXPROPLEN];
- char rbuf[ZFS_MAXPROPLEN];
- zprop_source_t sourcetype;
- char source[ZFS_MAX_DATASET_NAME_LEN];
- zprop_get_cbdata_t *cbp = data;
- nvlist_t *user_props = zfs_get_user_props(zhp);
- zprop_list_t *pl = cbp->cb_proplist;
- nvlist_t *propval;
- char *strval;
- char *sourceval;
- boolean_t received = is_recvd_column(cbp);
-
- for (; pl != NULL; pl = pl->pl_next) {
- char *recvdval = NULL;
- /*
- * Skip the special fake placeholder. This will also skip over
- * the name property when 'all' is specified.
- */
- if (pl->pl_prop == ZFS_PROP_NAME &&
- pl == cbp->cb_proplist)
- continue;
-
- if (pl->pl_prop != ZPROP_INVAL) {
- if (zfs_prop_get(zhp, pl->pl_prop, buf,
- sizeof (buf), &sourcetype, source,
- sizeof (source),
- cbp->cb_literal) != 0) {
- if (pl->pl_all)
- continue;
- if (!zfs_prop_valid_for_type(pl->pl_prop,
- ZFS_TYPE_DATASET)) {
- (void) fprintf(stderr,
- gettext("No such property '%s'\n"),
- zfs_prop_to_name(pl->pl_prop));
- continue;
- }
- sourcetype = ZPROP_SRC_NONE;
- (void) strlcpy(buf, "-", sizeof (buf));
- }
-
- if (received && (zfs_prop_get_recvd(zhp,
- zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf),
- cbp->cb_literal) == 0))
- recvdval = rbuf;
-
- zprop_print_one_property(zfs_get_name(zhp), cbp,
- zfs_prop_to_name(pl->pl_prop),
- buf, sourcetype, source, recvdval);
- } else if (zfs_prop_userquota(pl->pl_user_prop)) {
- sourcetype = ZPROP_SRC_LOCAL;
-
- if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
- buf, sizeof (buf), cbp->cb_literal) != 0) {
- sourcetype = ZPROP_SRC_NONE;
- (void) strlcpy(buf, "-", sizeof (buf));
- }
-
- zprop_print_one_property(zfs_get_name(zhp), cbp,
- pl->pl_user_prop, buf, sourcetype, source, NULL);
- } else if (zfs_prop_written(pl->pl_user_prop)) {
- sourcetype = ZPROP_SRC_LOCAL;
-
- if (zfs_prop_get_written(zhp, pl->pl_user_prop,
- buf, sizeof (buf), cbp->cb_literal) != 0) {
- sourcetype = ZPROP_SRC_NONE;
- (void) strlcpy(buf, "-", sizeof (buf));
- }
-
- zprop_print_one_property(zfs_get_name(zhp), cbp,
- pl->pl_user_prop, buf, sourcetype, source, NULL);
- } else {
- if (nvlist_lookup_nvlist(user_props,
- pl->pl_user_prop, &propval) != 0) {
- if (pl->pl_all)
- continue;
- sourcetype = ZPROP_SRC_NONE;
- strval = "-";
- } else {
- verify(nvlist_lookup_string(propval,
- ZPROP_VALUE, &strval) == 0);
- verify(nvlist_lookup_string(propval,
- ZPROP_SOURCE, &sourceval) == 0);
-
- if (strcmp(sourceval,
- zfs_get_name(zhp)) == 0) {
- sourcetype = ZPROP_SRC_LOCAL;
- } else if (strcmp(sourceval,
- ZPROP_SOURCE_VAL_RECVD) == 0) {
- sourcetype = ZPROP_SRC_RECEIVED;
- } else {
- sourcetype = ZPROP_SRC_INHERITED;
- (void) strlcpy(source,
- sourceval, sizeof (source));
- }
- }
-
- if (received && (zfs_prop_get_recvd(zhp,
- pl->pl_user_prop, rbuf, sizeof (rbuf),
- cbp->cb_literal) == 0))
- recvdval = rbuf;
-
- zprop_print_one_property(zfs_get_name(zhp), cbp,
- pl->pl_user_prop, strval, sourcetype,
- source, recvdval);
- }
- }
-
- return (0);
-}
-
-static int
-zfs_do_get(int argc, char **argv)
-{
- zprop_get_cbdata_t cb = { 0 };
- int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
- int types = ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK;
- char *value, *fields;
- int ret = 0;
- int limit = 0;
- zprop_list_t fake_name = { 0 };
-
- /*
- * Set up default columns and sources.
- */
- cb.cb_sources = ZPROP_SRC_ALL;
- cb.cb_columns[0] = GET_COL_NAME;
- cb.cb_columns[1] = GET_COL_PROPERTY;
- cb.cb_columns[2] = GET_COL_VALUE;
- cb.cb_columns[3] = GET_COL_SOURCE;
- cb.cb_type = ZFS_TYPE_DATASET;
-
- /* check options */
- while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) {
- switch (c) {
- case 'p':
- cb.cb_literal = B_TRUE;
- break;
- case 'd':
- limit = parse_depth(optarg, &flags);
- break;
- case 'r':
- flags |= ZFS_ITER_RECURSE;
- break;
- case 'H':
- cb.cb_scripted = B_TRUE;
- break;
- case ':':
- (void) fprintf(stderr, gettext("missing argument for "
- "'%c' option\n"), optopt);
- usage(B_FALSE);
- break;
- case 'o':
- /*
- * Process the set of columns to display. We zero out
- * the structure to give us a blank slate.
- */
- bzero(&cb.cb_columns, sizeof (cb.cb_columns));
- i = 0;
- while (*optarg != '\0') {
- static char *col_subopts[] =
- { "name", "property", "value", "received",
- "source", "all", NULL };
-
- if (i == ZFS_GET_NCOLS) {
- (void) fprintf(stderr, gettext("too "
- "many fields given to -o "
- "option\n"));
- usage(B_FALSE);
- }
-
- switch (getsubopt(&optarg, col_subopts,
- &value)) {
- case 0:
- cb.cb_columns[i++] = GET_COL_NAME;
- break;
- case 1:
- cb.cb_columns[i++] = GET_COL_PROPERTY;
- break;
- case 2:
- cb.cb_columns[i++] = GET_COL_VALUE;
- break;
- case 3:
- cb.cb_columns[i++] = GET_COL_RECVD;
- flags |= ZFS_ITER_RECVD_PROPS;
- break;
- case 4:
- cb.cb_columns[i++] = GET_COL_SOURCE;
- break;
- case 5:
- if (i > 0) {
- (void) fprintf(stderr,
- gettext("\"all\" conflicts "
- "with specific fields "
- "given to -o option\n"));
- usage(B_FALSE);
- }
- cb.cb_columns[0] = GET_COL_NAME;
- cb.cb_columns[1] = GET_COL_PROPERTY;
- cb.cb_columns[2] = GET_COL_VALUE;
- cb.cb_columns[3] = GET_COL_RECVD;
- cb.cb_columns[4] = GET_COL_SOURCE;
- flags |= ZFS_ITER_RECVD_PROPS;
- i = ZFS_GET_NCOLS;
- break;
- default:
- (void) fprintf(stderr,
- gettext("invalid column name "
- "'%s'\n"), suboptarg);
- usage(B_FALSE);
- }
- }
- break;
-
- case 's':
- cb.cb_sources = 0;
- while (*optarg != '\0') {
- static char *source_subopts[] = {
- "local", "default", "inherited",
- "received", "temporary", "none",
- NULL };
-
- switch (getsubopt(&optarg, source_subopts,
- &value)) {
- case 0:
- cb.cb_sources |= ZPROP_SRC_LOCAL;
- break;
- case 1:
- cb.cb_sources |= ZPROP_SRC_DEFAULT;
- break;
- case 2:
- cb.cb_sources |= ZPROP_SRC_INHERITED;
- break;
- case 3:
- cb.cb_sources |= ZPROP_SRC_RECEIVED;
- break;
- case 4:
- cb.cb_sources |= ZPROP_SRC_TEMPORARY;
- break;
- case 5:
- cb.cb_sources |= ZPROP_SRC_NONE;
- break;
- default:
- (void) fprintf(stderr,
- gettext("invalid source "
- "'%s'\n"), suboptarg);
- usage(B_FALSE);
- }
- }
- break;
-
- case 't':
- types = 0;
- flags &= ~ZFS_ITER_PROP_LISTSNAPS;
- while (*optarg != '\0') {
- static char *type_subopts[] = { "filesystem",
- "volume", "snapshot", "bookmark",
- "all", NULL };
-
- switch (getsubopt(&optarg, type_subopts,
- &value)) {
- case 0:
- types |= ZFS_TYPE_FILESYSTEM;
- break;
- case 1:
- types |= ZFS_TYPE_VOLUME;
- break;
- case 2:
- types |= ZFS_TYPE_SNAPSHOT;
- break;
- case 3:
- types |= ZFS_TYPE_BOOKMARK;
- break;
- case 4:
- types = ZFS_TYPE_DATASET |
- ZFS_TYPE_BOOKMARK;
- break;
-
- default:
- (void) fprintf(stderr,
- gettext("invalid type '%s'\n"),
- suboptarg);
- usage(B_FALSE);
- }
- }
- break;
-
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing property "
- "argument\n"));
- usage(B_FALSE);
- }
-
- fields = argv[0];
-
- if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
- != 0)
- usage(B_FALSE);
-
- 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.
- */
- if (cb.cb_proplist != NULL) {
- fake_name.pl_prop = ZFS_PROP_NAME;
- fake_name.pl_width = strlen(gettext("NAME"));
- fake_name.pl_next = cb.cb_proplist;
- cb.cb_proplist = &fake_name;
- }
-
- cb.cb_first = B_TRUE;
-
- /* run for each object */
- ret = zfs_for_each(argc, argv, flags, types, NULL,
- &cb.cb_proplist, limit, get_callback, &cb);
-
- if (cb.cb_proplist == &fake_name)
- zprop_free_list(fake_name.pl_next);
- else
- zprop_free_list(cb.cb_proplist);
-
- 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;
- boolean_t cb_received;
-} inherit_cbdata_t;
-
-static int
-inherit_recurse_cb(zfs_handle_t *zhp, void *data)
-{
- inherit_cbdata_t *cb = data;
- zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname);
-
- /*
- * If we're doing it recursively, then ignore properties that
- * are not valid for this type of dataset.
- */
- if (prop != ZPROP_INVAL &&
- !zfs_prop_valid_for_type(prop, zfs_get_type(zhp)))
- return (0);
-
- return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
-}
-
-static int
-inherit_cb(zfs_handle_t *zhp, void *data)
-{
- inherit_cbdata_t *cb = data;
-
- return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0);
-}
-
-static int
-zfs_do_inherit(int argc, char **argv)
-{
- int c;
- zfs_prop_t prop;
- inherit_cbdata_t cb = { 0 };
- char *propname;
- int ret = 0;
- int flags = 0;
- boolean_t received = B_FALSE;
-
- /* check options */
- while ((c = getopt(argc, argv, "rS")) != -1) {
- switch (c) {
- case 'r':
- flags |= ZFS_ITER_RECURSE;
- break;
- case 'S':
- received = B_TRUE;
- break;
- case '?':
- default:
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check number of arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing property argument\n"));
- usage(B_FALSE);
- }
- if (argc < 2) {
- (void) fprintf(stderr, gettext("missing dataset argument\n"));
- usage(B_FALSE);
- }
-
- propname = argv[0];
- argc--;
- argv++;
-
- if ((prop = zfs_name_to_prop(propname)) != ZPROP_INVAL) {
- if (zfs_prop_readonly(prop)) {
- (void) fprintf(stderr, gettext(
- "%s property is read-only\n"),
- propname);
- return (1);
- }
- if (!zfs_prop_inheritable(prop) && !received) {
- (void) fprintf(stderr, gettext("'%s' property cannot "
- "be inherited\n"), propname);
- if (prop == ZFS_PROP_QUOTA ||
- prop == ZFS_PROP_RESERVATION ||
- prop == ZFS_PROP_REFQUOTA ||
- prop == ZFS_PROP_REFRESERVATION) {
- (void) fprintf(stderr, gettext("use 'zfs set "
- "%s=none' to clear\n"), propname);
- (void) fprintf(stderr, gettext("use 'zfs "
- "inherit -S %s' to revert to received "
- "value\n"), propname);
- }
- return (1);
- }
- if (received && (prop == ZFS_PROP_VOLSIZE ||
- prop == ZFS_PROP_VERSION)) {
- (void) fprintf(stderr, gettext("'%s' property cannot "
- "be reverted to a received value\n"), propname);
- return (1);
- }
- } else if (!zfs_prop_user(propname)) {
- (void) fprintf(stderr, gettext("invalid property '%s'\n"),
- propname);
- usage(B_FALSE);
- }
-
- cb.cb_propname = propname;
- cb.cb_received = received;
-
- if (flags & ZFS_ITER_RECURSE) {
- ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
- NULL, NULL, 0, inherit_recurse_cb, &cb);
- } else {
- ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET,
- NULL, NULL, 0, inherit_cb, &cb);
- }
-
- return (ret);
-}
-
-typedef struct upgrade_cbdata {
- uint64_t cb_numupgraded;
- uint64_t cb_numsamegraded;
- uint64_t cb_numfailed;
- uint64_t cb_version;
- boolean_t cb_newer;
- boolean_t cb_foundone;
- char cb_lastfs[ZFS_MAX_DATASET_NAME_LEN];
-} upgrade_cbdata_t;
-
-static int
-same_pool(zfs_handle_t *zhp, const char *name)
-{
- int len1 = strcspn(name, "/@");
- const char *zhname = zfs_get_name(zhp);
- int len2 = strcspn(zhname, "/@");
-
- if (len1 != len2)
- return (B_FALSE);
- return (strncmp(name, zhname, len1) == 0);
-}
-
-static int
-upgrade_list_callback(zfs_handle_t *zhp, void *data)
-{
- upgrade_cbdata_t *cb = data;
- int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
-
- /* list if it's old/new */
- if ((!cb->cb_newer && version < ZPL_VERSION) ||
- (cb->cb_newer && version > ZPL_VERSION)) {
- char *str;
- if (cb->cb_newer) {
- str = gettext("The following filesystems are "
- "formatted using a newer software version and\n"
- "cannot be accessed on the current system.\n\n");
- } else {
- str = gettext("The following filesystems are "
- "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) {
- (void) puts(str);
- (void) printf(gettext("VER FILESYSTEM\n"));
- (void) printf(gettext("--- ------------\n"));
- cb->cb_foundone = B_TRUE;
- }
-
- (void) printf("%2u %s\n", version, zfs_get_name(zhp));
- }
-
- return (0);
-}
-
-static int
-upgrade_set_callback(zfs_handle_t *zhp, void *data)
-{
- upgrade_cbdata_t *cb = data;
- int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
- int needed_spa_version;
- int spa_version;
-
- if (zfs_spa_version(zhp, &spa_version) < 0)
- return (-1);
-
- needed_spa_version = zfs_spa_version_map(cb->cb_version);
-
- if (needed_spa_version < 0)
- return (-1);
-
- if (spa_version < needed_spa_version) {
- /* can't upgrade */
- (void) printf(gettext("%s: can not be "
- "upgraded; the pool version needs to first "
- "be upgraded\nto version %d\n\n"),
- zfs_get_name(zhp), needed_spa_version);
- cb->cb_numfailed++;
- return (0);
- }
-
- /* upgrade */
- if (version < cb->cb_version) {
- char verstr[16];
- (void) snprintf(verstr, sizeof (verstr),
- "%llu", cb->cb_version);
- if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {
- /*
- * 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().
- */
- (void) zpool_log_history(g_zfs, history_str);
- log_history = B_FALSE;
- }
- if (zfs_prop_set(zhp, "version", verstr) == 0)
- cb->cb_numupgraded++;
- else
- cb->cb_numfailed++;
- (void) strcpy(cb->cb_lastfs, zfs_get_name(zhp));
- } else if (version > cb->cb_version) {
- /* can't downgrade */
- (void) printf(gettext("%s: can not be downgraded; "
- "it is already at version %u\n"),
- zfs_get_name(zhp), version);
- cb->cb_numfailed++;
- } else {
- cb->cb_numsamegraded++;
- }
- return (0);
-}
-
-/*
- * zfs upgrade
- * zfs upgrade -v
- * zfs upgrade [-r] [-V <version>] <-a | filesystem>
- */
-static int
-zfs_do_upgrade(int argc, char **argv)
-{
- boolean_t all = B_FALSE;
- boolean_t showversions = B_FALSE;
- int ret = 0;
- upgrade_cbdata_t cb = { 0 };
- int c;
- int flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
-
- /* check options */
- while ((c = getopt(argc, argv, "rvV:a")) != -1) {
- switch (c) {
- case 'r':
- flags |= ZFS_ITER_RECURSE;
- break;
- case 'v':
- showversions = B_TRUE;
- break;
- case 'V':
- if (zfs_prop_string_to_index(ZFS_PROP_VERSION,
- optarg, &cb.cb_version) != 0) {
- (void) fprintf(stderr,
- gettext("invalid version %s\n"), optarg);
- usage(B_FALSE);
- }
- break;
- case 'a':
- all = B_TRUE;
- break;
- case '?':
- default:
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version))
- usage(B_FALSE);
- if (showversions && (flags & ZFS_ITER_RECURSE || all ||
- cb.cb_version || argc))
- usage(B_FALSE);
- if ((all || argc) && (showversions))
- usage(B_FALSE);
- if (all && argc)
- usage(B_FALSE);
-
- if (showversions) {
- /* Show info on available versions. */
- (void) printf(gettext("The following filesystem versions are "
- "supported:\n\n"));
- (void) printf(gettext("VER DESCRIPTION\n"));
- (void) printf("--- -----------------------------------------"
- "---------------\n");
- (void) printf(gettext(" 1 Initial ZFS filesystem version\n"));
- (void) printf(gettext(" 2 Enhanced directory entries\n"));
- (void) printf(gettext(" 3 Case insensitive and filesystem "
- "user identifier (FUID)\n"));
- (void) printf(gettext(" 4 userquota, groupquota "
- "properties\n"));
- (void) printf(gettext(" 5 System attributes\n"));
- (void) printf(gettext("\nFor more information on a particular "
- "version, including supported releases,\n"));
- (void) printf("see the ZFS Administration Guide.\n\n");
- ret = 0;
- } else if (argc || all) {
- /* Upgrade filesystems */
- if (cb.cb_version == 0)
- cb.cb_version = ZPL_VERSION;
- ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM,
- NULL, NULL, 0, upgrade_set_callback, &cb);
- (void) printf(gettext("%llu filesystems upgraded\n"),
- cb.cb_numupgraded);
- if (cb.cb_numsamegraded) {
- (void) printf(gettext("%llu filesystems already at "
- "this version\n"),
- cb.cb_numsamegraded);
- }
- if (cb.cb_numfailed != 0)
- ret = 1;
- } else {
- /* List old-version filesystems */
- boolean_t found;
- (void) printf(gettext("This system is currently running "
- "ZFS filesystem version %llu.\n\n"), ZPL_VERSION);
-
- flags |= ZFS_ITER_RECURSE;
- ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
- NULL, NULL, 0, upgrade_list_callback, &cb);
-
- found = cb.cb_foundone;
- cb.cb_foundone = B_FALSE;
- cb.cb_newer = B_TRUE;
-
- ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM,
- NULL, NULL, 0, upgrade_list_callback, &cb);
-
- if (!cb.cb_foundone && !found) {
- (void) printf(gettext("All filesystems are "
- "formatted with the current version.\n"));
- }
- }
-
- return (ret);
-}
-
-/*
- * zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
- * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot
- * zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...]
- * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot
- *
- * -H Scripted mode; elide headers and separate columns by tabs.
- * -i Translate SID to POSIX ID.
- * -n Print numeric ID instead of user/group name.
- * -o Control which fields to display.
- * -p Use exact (parsable) numeric output.
- * -s Specify sort columns, descending order.
- * -S Specify sort columns, ascending order.
- * -t Control which object types to display.
- *
- * Displays space consumed by, and quotas on, each user in the specified
- * filesystem or snapshot.
- */
-
-/* us_field_types, us_field_hdr and us_field_names should be kept in sync */
-enum us_field_types {
- USFIELD_TYPE,
- USFIELD_NAME,
- USFIELD_USED,
- USFIELD_QUOTA
-};
-static char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA" };
-static char *us_field_names[] = { "type", "name", "used", "quota" };
-#define USFIELD_LAST (sizeof (us_field_names) / sizeof (char *))
-
-#define USTYPE_PSX_GRP (1 << 0)
-#define USTYPE_PSX_USR (1 << 1)
-#define USTYPE_SMB_GRP (1 << 2)
-#define USTYPE_SMB_USR (1 << 3)
-#define USTYPE_ALL \
- (USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR)
-
-static int us_type_bits[] = {
- USTYPE_PSX_GRP,
- USTYPE_PSX_USR,
- USTYPE_SMB_GRP,
- USTYPE_SMB_USR,
- USTYPE_ALL
-};
-static char *us_type_names[] = { "posixgroup", "posixuser", "smbgroup",
- "smbuser", "all" };
-
-typedef struct us_node {
- nvlist_t *usn_nvl;
- uu_avl_node_t usn_avlnode;
- uu_list_node_t usn_listnode;
-} us_node_t;
-
-typedef struct us_cbdata {
- nvlist_t **cb_nvlp;
- uu_avl_pool_t *cb_avl_pool;
- uu_avl_t *cb_avl;
- boolean_t cb_numname;
- boolean_t cb_nicenum;
- boolean_t cb_sid2posix;
- zfs_userquota_prop_t cb_prop;
- zfs_sort_column_t *cb_sortcol;
- size_t cb_width[USFIELD_LAST];
-} us_cbdata_t;
-
-static boolean_t us_populated = B_FALSE;
-
-typedef struct {
- zfs_sort_column_t *si_sortcol;
- boolean_t si_numname;
-} us_sort_info_t;
-
-static int
-us_field_index(char *field)
-{
- int i;
-
- for (i = 0; i < USFIELD_LAST; i++) {
- if (strcmp(field, us_field_names[i]) == 0)
- return (i);
- }
-
- return (-1);
-}
-
-static int
-us_compare(const void *larg, const void *rarg, void *unused)
-{
- const us_node_t *l = larg;
- const us_node_t *r = rarg;
- us_sort_info_t *si = (us_sort_info_t *)unused;
- zfs_sort_column_t *sortcol = si->si_sortcol;
- boolean_t numname = si->si_numname;
- nvlist_t *lnvl = l->usn_nvl;
- nvlist_t *rnvl = r->usn_nvl;
- int rc = 0;
- boolean_t lvb, rvb;
-
- for (; sortcol != NULL; sortcol = sortcol->sc_next) {
- char *lvstr = "";
- char *rvstr = "";
- uint32_t lv32 = 0;
- uint32_t rv32 = 0;
- uint64_t lv64 = 0;
- uint64_t rv64 = 0;
- zfs_prop_t prop = sortcol->sc_prop;
- const char *propname = NULL;
- boolean_t reverse = sortcol->sc_reverse;
-
- switch (prop) {
- case ZFS_PROP_TYPE:
- propname = "type";
- (void) nvlist_lookup_uint32(lnvl, propname, &lv32);
- (void) nvlist_lookup_uint32(rnvl, propname, &rv32);
- if (rv32 != lv32)
- rc = (rv32 < lv32) ? 1 : -1;
- break;
- case ZFS_PROP_NAME:
- propname = "name";
- if (numname) {
-compare_nums:
- (void) nvlist_lookup_uint64(lnvl, propname,
- &lv64);
- (void) nvlist_lookup_uint64(rnvl, propname,
- &rv64);
- if (rv64 != lv64)
- rc = (rv64 < lv64) ? 1 : -1;
- } else {
- if ((nvlist_lookup_string(lnvl, propname,
- &lvstr) == ENOENT) ||
- (nvlist_lookup_string(rnvl, propname,
- &rvstr) == ENOENT)) {
- goto compare_nums;
- }
- rc = strcmp(lvstr, rvstr);
- }
- break;
- case ZFS_PROP_USED:
- case ZFS_PROP_QUOTA:
- if (!us_populated)
- break;
- if (prop == ZFS_PROP_USED)
- propname = "used";
- else
- propname = "quota";
- (void) nvlist_lookup_uint64(lnvl, propname, &lv64);
- (void) nvlist_lookup_uint64(rnvl, propname, &rv64);
- if (rv64 != lv64)
- rc = (rv64 < lv64) ? 1 : -1;
- break;
-
- default:
- break;
- }
-
- if (rc != 0) {
- if (rc < 0)
- return (reverse ? 1 : -1);
- else
- return (reverse ? -1 : 1);
- }
- }
-
- /*
- * If entries still seem to be the same, check if they are of the same
- * type (smbentity is added only if we are doing SID to POSIX ID
- * translation where we can have duplicate type/name combinations).
- */
- if (nvlist_lookup_boolean_value(lnvl, "smbentity", &lvb) == 0 &&
- nvlist_lookup_boolean_value(rnvl, "smbentity", &rvb) == 0 &&
- lvb != rvb)
- return (lvb < rvb ? -1 : 1);
-
- return (0);
-}
-
-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");
- }
-}
-
-static int
-userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space)
-{
- us_cbdata_t *cb = (us_cbdata_t *)arg;
- zfs_userquota_prop_t prop = cb->cb_prop;
- char *name = NULL;
- char *propname;
- char sizebuf[32];
- us_node_t *node;
- uu_avl_pool_t *avl_pool = cb->cb_avl_pool;
- uu_avl_t *avl = cb->cb_avl;
- uu_avl_index_t idx;
- nvlist_t *props;
- us_node_t *n;
- zfs_sort_column_t *sortcol = cb->cb_sortcol;
- unsigned type = 0;
- const char *typestr;
- size_t namelen;
- size_t typelen;
- size_t sizelen;
- int typeidx, nameidx, sizeidx;
- us_sort_info_t sortinfo = { sortcol, cb->cb_numname };
- boolean_t smbentity = B_FALSE;
-
- if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
- nomem();
- node = safe_malloc(sizeof (us_node_t));
- uu_avl_node_init(node, &node->usn_avlnode, avl_pool);
- node->usn_nvl = props;
-
- if (domain != NULL && domain[0] != '\0') {
- /* SMB */
- char sid[MAXNAMELEN + 32];
- uid_t id;
-#ifdef illumos
- int err;
- int flag = IDMAP_REQ_FLG_USE_CACHE;
-#endif
-
- smbentity = B_TRUE;
-
- (void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid);
-
- if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {
- type = USTYPE_SMB_GRP;
-#ifdef illumos
- err = sid_to_id(sid, B_FALSE, &id);
-#endif
- } else {
- type = USTYPE_SMB_USR;
-#ifdef illumos
- err = sid_to_id(sid, B_TRUE, &id);
-#endif
- }
-
-#ifdef illumos
- if (err == 0) {
- rid = id;
- if (!cb->cb_sid2posix) {
- if (type == USTYPE_SMB_USR) {
- (void) idmap_getwinnamebyuid(rid, flag,
- &name, NULL);
- } else {
- (void) idmap_getwinnamebygid(rid, flag,
- &name, NULL);
- }
- if (name == NULL)
- name = sid;
- }
- }
-#endif
- }
-
- if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') {
- /* POSIX or -i */
- if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) {
- type = USTYPE_PSX_GRP;
- if (!cb->cb_numname) {
- struct group *g;
-
- if ((g = getgrgid(rid)) != NULL)
- name = g->gr_name;
- }
- } else {
- type = USTYPE_PSX_USR;
- if (!cb->cb_numname) {
- struct passwd *p;
-
- if ((p = getpwuid(rid)) != NULL)
- name = p->pw_name;
- }
- }
- }
-
- /*
- * Make sure that the type/name combination is unique when doing
- * SID to POSIX ID translation (hence changing the type from SMB to
- * POSIX).
- */
- if (cb->cb_sid2posix &&
- nvlist_add_boolean_value(props, "smbentity", smbentity) != 0)
- nomem();
-
- /* Calculate/update width of TYPE field */
- typestr = us_type2str(type);
- typelen = strlen(gettext(typestr));
- typeidx = us_field_index("type");
- if (typelen > cb->cb_width[typeidx])
- cb->cb_width[typeidx] = typelen;
- if (nvlist_add_uint32(props, "type", type) != 0)
- nomem();
-
- /* Calculate/update width of NAME field */
- if ((cb->cb_numname && cb->cb_sid2posix) || name == NULL) {
- if (nvlist_add_uint64(props, "name", rid) != 0)
- nomem();
- namelen = snprintf(NULL, 0, "%u", rid);
- } else {
- if (nvlist_add_string(props, "name", name) != 0)
- nomem();
- namelen = strlen(name);
- }
- nameidx = us_field_index("name");
- if (namelen > cb->cb_width[nameidx])
- cb->cb_width[nameidx] = namelen;
-
- /*
- * Check if this type/name combination is in the list and update it;
- * otherwise add new node to the list.
- */
- if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) {
- uu_avl_insert(avl, node, idx);
- } else {
- nvlist_free(props);
- free(node);
- node = n;
- props = node->usn_nvl;
- }
-
- /* Calculate/update width of USED/QUOTA fields */
- if (cb->cb_nicenum)
- zfs_nicenum(space, sizebuf, sizeof (sizebuf));
- else
- (void) snprintf(sizebuf, sizeof (sizebuf), "%llu", space);
- sizelen = strlen(sizebuf);
- if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED) {
- propname = "used";
- if (!nvlist_exists(props, "quota"))
- (void) nvlist_add_uint64(props, "quota", 0);
- } else {
- propname = "quota";
- if (!nvlist_exists(props, "used"))
- (void) nvlist_add_uint64(props, "used", 0);
- }
- sizeidx = us_field_index(propname);
- if (sizelen > cb->cb_width[sizeidx])
- cb->cb_width[sizeidx] = sizelen;
-
- if (nvlist_add_uint64(props, propname, space) != 0)
- nomem();
-
- return (0);
-}
-
-static void
-print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types,
- size_t *width, us_node_t *node)
-{
- nvlist_t *nvl = node->usn_nvl;
- char valstr[MAXNAMELEN];
- boolean_t first = B_TRUE;
- int cfield = 0;
- int field;
- uint32_t ustype;
-
- /* Check type */
- (void) nvlist_lookup_uint32(nvl, "type", &ustype);
- if (!(ustype & types))
- return;
-
- while ((field = fields[cfield]) != USFIELD_LAST) {
- nvpair_t *nvp = NULL;
- data_type_t type;
- uint32_t val32;
- uint64_t val64;
- char *strval = NULL;
-
- while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
- if (strcmp(nvpair_name(nvp),
- us_field_names[field]) == 0)
- break;
- }
-
- type = nvpair_type(nvp);
- switch (type) {
- case DATA_TYPE_UINT32:
- (void) nvpair_value_uint32(nvp, &val32);
- break;
- case DATA_TYPE_UINT64:
- (void) nvpair_value_uint64(nvp, &val64);
- break;
- case DATA_TYPE_STRING:
- (void) nvpair_value_string(nvp, &strval);
- break;
- default:
- (void) fprintf(stderr, "invalid data type\n");
- }
-
- switch (field) {
- case USFIELD_TYPE:
- strval = (char *)us_type2str(val32);
- break;
- case USFIELD_NAME:
- if (type == DATA_TYPE_UINT64) {
- (void) sprintf(valstr, "%llu", val64);
- strval = valstr;
- }
- break;
- case USFIELD_USED:
- case USFIELD_QUOTA:
- if (type == DATA_TYPE_UINT64) {
- if (parsable) {
- (void) sprintf(valstr, "%llu", val64);
- } else {
- zfs_nicenum(val64, valstr,
- sizeof (valstr));
- }
- if (field == USFIELD_QUOTA &&
- strcmp(valstr, "0") == 0)
- strval = "none";
- else
- strval = valstr;
- }
- break;
- }
-
- if (!first) {
- if (scripted)
- (void) printf("\t");
- else
- (void) printf(" ");
- }
- if (scripted)
- (void) printf("%s", strval);
- else if (field == USFIELD_TYPE || field == USFIELD_NAME)
- (void) printf("%-*s", width[field], strval);
- else
- (void) printf("%*s", width[field], strval);
-
- first = B_FALSE;
- cfield++;
- }
-
- (void) printf("\n");
-}
-
-static void
-print_us(boolean_t scripted, boolean_t parsable, int *fields, int types,
- size_t *width, boolean_t rmnode, uu_avl_t *avl)
-{
- us_node_t *node;
- const char *col;
- int cfield = 0;
- int field;
-
- if (!scripted) {
- boolean_t first = B_TRUE;
-
- while ((field = fields[cfield]) != USFIELD_LAST) {
- col = gettext(us_field_hdr[field]);
- if (field == USFIELD_TYPE || field == USFIELD_NAME) {
- (void) printf(first ? "%-*s" : " %-*s",
- width[field], col);
- } else {
- (void) printf(first ? "%*s" : " %*s",
- width[field], col);
- }
- first = B_FALSE;
- cfield++;
- }
- (void) printf("\n");
- }
-
- for (node = uu_avl_first(avl); node; node = uu_avl_next(avl, node)) {
- print_us_node(scripted, parsable, fields, types, width, node);
- if (rmnode)
- nvlist_free(node->usn_nvl);
- }
-}
-
-static int
-zfs_do_userspace(int argc, char **argv)
-{
- zfs_handle_t *zhp;
- zfs_userquota_prop_t p;
-
- uu_avl_pool_t *avl_pool;
- uu_avl_t *avl_tree;
- uu_avl_walk_t *walk;
- char *delim;
- char deffields[] = "type,name,used,quota";
- char *ofield = NULL;
- char *tfield = NULL;
- int cfield = 0;
- int fields[256];
- int i;
- boolean_t scripted = B_FALSE;
- boolean_t prtnum = B_FALSE;
- boolean_t parsable = B_FALSE;
- boolean_t sid2posix = B_FALSE;
- int ret = 0;
- int c;
- zfs_sort_column_t *sortcol = NULL;
- int types = USTYPE_PSX_USR | USTYPE_SMB_USR;
- us_cbdata_t cb;
- us_node_t *node;
- us_node_t *rmnode;
- uu_list_pool_t *listpool;
- uu_list_t *list;
- uu_avl_index_t idx = 0;
- uu_list_index_t idx2 = 0;
-
- if (argc < 2)
- usage(B_FALSE);
-
- if (strcmp(argv[0], "groupspace") == 0)
- /* Toggle default group types */
- types = USTYPE_PSX_GRP | USTYPE_SMB_GRP;
-
- while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) {
- switch (c) {
- case 'n':
- prtnum = B_TRUE;
- break;
- case 'H':
- scripted = B_TRUE;
- break;
- case 'p':
- parsable = B_TRUE;
- break;
- case 'o':
- ofield = optarg;
- break;
- case 's':
- case 'S':
- if (zfs_add_sort_column(&sortcol, optarg,
- c == 's' ? B_FALSE : B_TRUE) != 0) {
- (void) fprintf(stderr,
- gettext("invalid field '%s'\n"), optarg);
- usage(B_FALSE);
- }
- break;
- case 't':
- tfield = optarg;
- break;
- case 'i':
- sid2posix = B_TRUE;
- break;
- case ':':
- (void) fprintf(stderr, gettext("missing argument for "
- "'%c' option\n"), optopt);
- usage(B_FALSE);
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing dataset name\n"));
- usage(B_FALSE);
- }
- if (argc > 1) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- /* Use default output fields if not specified using -o */
- if (ofield == NULL)
- ofield = deffields;
- do {
- if ((delim = strchr(ofield, ',')) != NULL)
- *delim = '\0';
- if ((fields[cfield++] = us_field_index(ofield)) == -1) {
- (void) fprintf(stderr, gettext("invalid type '%s' "
- "for -o option\n"), ofield);
- return (-1);
- }
- if (delim != NULL)
- ofield = delim + 1;
- } while (delim != NULL);
- fields[cfield] = USFIELD_LAST;
-
- /* Override output types (-t option) */
- if (tfield != NULL) {
- types = 0;
-
- do {
- boolean_t found = B_FALSE;
-
- if ((delim = strchr(tfield, ',')) != NULL)
- *delim = '\0';
- for (i = 0; i < sizeof (us_type_bits) / sizeof (int);
- i++) {
- if (strcmp(tfield, us_type_names[i]) == 0) {
- found = B_TRUE;
- types |= us_type_bits[i];
- break;
- }
- }
- if (!found) {
- (void) fprintf(stderr, gettext("invalid type "
- "'%s' for -t option\n"), tfield);
- return (-1);
- }
- if (delim != NULL)
- tfield = delim + 1;
- } while (delim != NULL);
- }
-
- if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
- return (1);
-
- if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t),
- offsetof(us_node_t, usn_avlnode), us_compare, UU_DEFAULT)) == NULL)
- nomem();
- if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL)
- nomem();
-
- /* Always add default sorting columns */
- (void) zfs_add_sort_column(&sortcol, "type", B_FALSE);
- (void) zfs_add_sort_column(&sortcol, "name", B_FALSE);
-
- cb.cb_sortcol = sortcol;
- cb.cb_numname = prtnum;
- cb.cb_nicenum = !parsable;
- cb.cb_avl_pool = avl_pool;
- cb.cb_avl = avl_tree;
- cb.cb_sid2posix = sid2posix;
-
- for (i = 0; i < USFIELD_LAST; i++)
- cb.cb_width[i] = strlen(gettext(us_field_hdr[i]));
-
- for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) {
- if (((p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA) &&
- !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) ||
- ((p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) &&
- !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))))
- continue;
- cb.cb_prop = p;
- if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0)
- return (ret);
- }
-
- /* Sort the list */
- if ((node = uu_avl_first(avl_tree)) == NULL)
- return (0);
-
- us_populated = B_TRUE;
-
- listpool = uu_list_pool_create("tmplist", sizeof (us_node_t),
- offsetof(us_node_t, usn_listnode), NULL, UU_DEFAULT);
- list = uu_list_create(listpool, NULL, UU_DEFAULT);
- uu_list_node_init(node, &node->usn_listnode, listpool);
-
- while (node != NULL) {
- rmnode = node;
- node = uu_avl_next(avl_tree, node);
- uu_avl_remove(avl_tree, rmnode);
- if (uu_list_find(list, rmnode, NULL, &idx2) == NULL)
- uu_list_insert(list, rmnode, idx2);
- }
-
- for (node = uu_list_first(list); node != NULL;
- node = uu_list_next(list, node)) {
- us_sort_info_t sortinfo = { sortcol, cb.cb_numname };
-
- if (uu_avl_find(avl_tree, node, &sortinfo, &idx) == NULL)
- uu_avl_insert(avl_tree, node, idx);
- }
-
- uu_list_destroy(list);
- uu_list_pool_destroy(listpool);
-
- /* Print and free node nvlist memory */
- print_us(scripted, parsable, fields, types, cb.cb_width, B_TRUE,
- cb.cb_avl);
-
- zfs_free_sort_columns(sortcol);
-
- /* Clean up the AVL tree */
- if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL)
- nomem();
-
- while ((node = uu_avl_walk_next(walk)) != NULL) {
- uu_avl_remove(cb.cb_avl, node);
- free(node);
- }
-
- uu_avl_walk_end(walk);
- uu_avl_destroy(avl_tree);
- uu_avl_pool_destroy(avl_pool);
-
- return (ret);
-}
-
-/*
- * list [-Hp][-r|-d max] [-o property[,...]] [-s property] ... [-S property] ...
- * [-t type[,...]] [filesystem|volume|snapshot] ...
- *
- * -H Scripted mode; elide headers and separate columns by tabs.
- * -p Display values in parsable (literal) format.
- * -r Recurse over all children.
- * -d Limit recursion by depth.
- * -o Control which fields to display.
- * -s Specify sort columns, descending order.
- * -S Specify sort columns, ascending order.
- * -t Control which object types to display.
- *
- * When given no arguments, list all filesystems in the system.
- * Otherwise, list the specified datasets, optionally recursing down them if
- * '-r' is specified.
- */
-typedef struct list_cbdata {
- boolean_t cb_first;
- boolean_t cb_literal;
- boolean_t cb_scripted;
- zprop_list_t *cb_proplist;
-} list_cbdata_t;
-
-/*
- * Given a list of columns to display, output appropriate headers for each one.
- */
-static void
-print_header(list_cbdata_t *cb)
-{
- zprop_list_t *pl = cb->cb_proplist;
- char headerbuf[ZFS_MAXPROPLEN];
- const char *header;
- int i;
- boolean_t first = B_TRUE;
- boolean_t right_justify;
-
- for (; pl != NULL; pl = pl->pl_next) {
- if (!first) {
- (void) printf(" ");
- } else {
- first = B_FALSE;
- }
-
- right_justify = B_FALSE;
- if (pl->pl_prop != ZPROP_INVAL) {
- header = zfs_prop_column_name(pl->pl_prop);
- right_justify = zfs_prop_align_right(pl->pl_prop);
- } else {
- for (i = 0; pl->pl_user_prop[i] != '\0'; i++)
- headerbuf[i] = toupper(pl->pl_user_prop[i]);
- headerbuf[i] = '\0';
- header = headerbuf;
- }
-
- if (pl->pl_next == NULL && !right_justify)
- (void) printf("%s", header);
- else if (right_justify)
- (void) printf("%*s", pl->pl_width, header);
- else
- (void) printf("%-*s", pl->pl_width, header);
- }
-
- (void) printf("\n");
-}
-
-/*
- * Given a dataset and a list of fields, print out all the properties according
- * to the described layout.
- */
-static void
-print_dataset(zfs_handle_t *zhp, list_cbdata_t *cb)
-{
- zprop_list_t *pl = cb->cb_proplist;
- boolean_t first = B_TRUE;
- char property[ZFS_MAXPROPLEN];
- nvlist_t *userprops = zfs_get_user_props(zhp);
- nvlist_t *propval;
- char *propstr;
- boolean_t right_justify;
-
- for (; pl != NULL; pl = pl->pl_next) {
- if (!first) {
- if (cb->cb_scripted)
- (void) printf("\t");
- else
- (void) printf(" ");
- } else {
- first = B_FALSE;
- }
-
- if (pl->pl_prop == ZFS_PROP_NAME) {
- (void) strlcpy(property, zfs_get_name(zhp),
- sizeof (property));
- propstr = property;
- right_justify = zfs_prop_align_right(pl->pl_prop);
- } else if (pl->pl_prop != ZPROP_INVAL) {
- if (zfs_prop_get(zhp, pl->pl_prop, property,
- sizeof (property), NULL, NULL, 0,
- cb->cb_literal) != 0)
- propstr = "-";
- else
- propstr = property;
- right_justify = zfs_prop_align_right(pl->pl_prop);
- } else if (zfs_prop_userquota(pl->pl_user_prop)) {
- if (zfs_prop_get_userquota(zhp, pl->pl_user_prop,
- property, sizeof (property), cb->cb_literal) != 0)
- propstr = "-";
- else
- propstr = property;
- right_justify = B_TRUE;
- } else if (zfs_prop_written(pl->pl_user_prop)) {
- if (zfs_prop_get_written(zhp, pl->pl_user_prop,
- property, sizeof (property), cb->cb_literal) != 0)
- propstr = "-";
- else
- propstr = property;
- right_justify = B_TRUE;
- } else {
- if (nvlist_lookup_nvlist(userprops,
- pl->pl_user_prop, &propval) != 0)
- propstr = "-";
- else
- verify(nvlist_lookup_string(propval,
- ZPROP_VALUE, &propstr) == 0);
- right_justify = B_FALSE;
- }
-
- /*
- * 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.
- */
- if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify))
- (void) printf("%s", propstr);
- else if (right_justify)
- (void) printf("%*s", pl->pl_width, propstr);
- else
- (void) printf("%-*s", pl->pl_width, propstr);
- }
-
- (void) printf("\n");
-}
-
-/*
- * Generic callback function to list a dataset or snapshot.
- */
-static int
-list_callback(zfs_handle_t *zhp, void *data)
-{
- list_cbdata_t *cbp = data;
-
- if (cbp->cb_first) {
- if (!cbp->cb_scripted)
- print_header(cbp);
- cbp->cb_first = B_FALSE;
- }
-
- print_dataset(zhp, cbp);
-
- return (0);
-}
-
-static int
-zfs_do_list(int argc, char **argv)
-{
- int c;
- static char default_fields[] =
- "name,used,available,referenced,mountpoint";
- int types = ZFS_TYPE_DATASET;
- boolean_t types_specified = B_FALSE;
- char *fields = NULL;
- list_cbdata_t cb = { 0 };
- char *value;
- int limit = 0;
- int ret = 0;
- zfs_sort_column_t *sortcol = NULL;
- int flags = ZFS_ITER_PROP_LISTSNAPS | ZFS_ITER_ARGS_CAN_BE_PATHS;
-
- /* check options */
- while ((c = getopt(argc, argv, "HS:d:o:prs:t:")) != -1) {
- switch (c) {
- case 'o':
- fields = optarg;
- break;
- case 'p':
- cb.cb_literal = B_TRUE;
- flags |= ZFS_ITER_LITERAL_PROPS;
- break;
- case 'd':
- limit = parse_depth(optarg, &flags);
- break;
- case 'r':
- flags |= ZFS_ITER_RECURSE;
- break;
- case 'H':
- cb.cb_scripted = B_TRUE;
- break;
- case 's':
- if (zfs_add_sort_column(&sortcol, optarg,
- B_FALSE) != 0) {
- (void) fprintf(stderr,
- gettext("invalid property '%s'\n"), optarg);
- usage(B_FALSE);
- }
- break;
- case 'S':
- if (zfs_add_sort_column(&sortcol, optarg,
- B_TRUE) != 0) {
- (void) fprintf(stderr,
- gettext("invalid property '%s'\n"), optarg);
- usage(B_FALSE);
- }
- break;
- case 't':
- types = 0;
- types_specified = B_TRUE;
- flags &= ~ZFS_ITER_PROP_LISTSNAPS;
- while (*optarg != '\0') {
- static char *type_subopts[] = { "filesystem",
- "volume", "snapshot", "snap", "bookmark",
- "all", NULL };
-
- switch (getsubopt(&optarg, type_subopts,
- &value)) {
- case 0:
- types |= ZFS_TYPE_FILESYSTEM;
- break;
- case 1:
- types |= ZFS_TYPE_VOLUME;
- break;
- case 2:
- case 3:
- types |= ZFS_TYPE_SNAPSHOT;
- break;
- case 4:
- types |= ZFS_TYPE_BOOKMARK;
- break;
- case 5:
- types = ZFS_TYPE_DATASET |
- ZFS_TYPE_BOOKMARK;
- break;
- default:
- (void) fprintf(stderr,
- gettext("invalid type '%s'\n"),
- suboptarg);
- usage(B_FALSE);
- }
- }
- break;
- case ':':
- (void) fprintf(stderr, gettext("missing argument for "
- "'%c' option\n"), optopt);
- usage(B_FALSE);
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (fields == NULL)
- fields = default_fields;
-
- /*
- * If we are only going to list snapshot names and sort by name,
- * then we can use faster version.
- */
- if (strcmp(fields, "name") == 0 && zfs_sort_only_by_name(sortcol))
- flags |= ZFS_ITER_SIMPLE;
-
- /*
- * If "-o space" and no types were specified, don't display snapshots.
- */
- if (strcmp(fields, "space") == 0 && types_specified == B_FALSE)
- 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.
- */
- if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET)
- != 0)
- usage(B_FALSE);
-
- cb.cb_first = B_TRUE;
-
- ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist,
- limit, list_callback, &cb);
-
- zprop_free_list(cb.cb_proplist);
- zfs_free_sort_columns(sortcol);
-
- if (ret == 0 && cb.cb_first && !cb.cb_scripted)
- (void) printf(gettext("no datasets available\n"));
-
- return (ret);
-}
-
-/*
- * zfs rename [-f] <fs | snap | vol> <fs | snap | vol>
- * zfs rename [-f] -p <fs | vol> <fs | vol>
- * zfs rename -r <snap> <snap>
- * zfs rename <bmark> <bmark>
- * zfs rename -u [-p] <fs> <fs>
- *
- * 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
-zfs_do_rename(int argc, char **argv)
-{
- zfs_handle_t *zhp;
- renameflags_t flags = { 0 };
- int c;
- int ret = 0;
- int types;
- boolean_t parents = B_FALSE;
- boolean_t bookmarks = B_FALSE;
- char *snapshot = NULL;
-
- /* check options */
- while ((c = getopt(argc, argv, "fpru")) != -1) {
- switch (c) {
- case 'p':
- parents = B_TRUE;
- break;
- case 'r':
- flags.recurse = B_TRUE;
- break;
- case 'u':
- flags.nounmount = B_TRUE;
- break;
- case 'f':
- flags.forceunmount = B_TRUE;
- break;
- case '?':
- default:
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check number of arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing source dataset "
- "argument\n"));
- usage(B_FALSE);
- }
- if (argc < 2) {
- (void) fprintf(stderr, gettext("missing target dataset "
- "argument\n"));
- usage(B_FALSE);
- }
- if (argc > 2) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- if (flags.recurse && parents) {
- (void) fprintf(stderr, gettext("-p and -r options are mutually "
- "exclusive\n"));
- usage(B_FALSE);
- }
-
- if (flags.recurse && strchr(argv[0], '@') == NULL) {
- (void) fprintf(stderr, gettext("source dataset for recursive "
- "rename must be a snapshot\n"));
- usage(B_FALSE);
- }
-
- if (flags.nounmount && parents) {
- (void) fprintf(stderr, gettext("-u and -p options are mutually "
- "exclusive\n"));
- usage(B_FALSE);
- }
-
- if (strchr(argv[0], '#') != NULL)
- bookmarks = B_TRUE;
-
- if (bookmarks && (flags.nounmount || flags.recurse ||
- flags.forceunmount || parents)) {
- (void) fprintf(stderr, gettext("options are not supported "
- "for renaming bookmarks\n"));
- usage(B_FALSE);
- }
-
- if (flags.nounmount)
- types = ZFS_TYPE_FILESYSTEM;
- else if (parents)
- types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
- else if (bookmarks)
- types = ZFS_TYPE_BOOKMARK;
- else
- types = ZFS_TYPE_DATASET;
-
- if (flags.recurse) {
- /*
- * When we do recursive rename we are fine when the given
- * snapshot for the given dataset doesn't exist - it can
- * still exists below.
- */
-
- snapshot = strchr(argv[0], '@');
- assert(snapshot != NULL);
- *snapshot = '\0';
- snapshot++;
- }
-
- if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL)
- return (1);
-
- /* If we were asked and the name looks good, try to create ancestors. */
- if (parents && zfs_name_valid(argv[1], zfs_get_type(zhp)) &&
- zfs_create_ancestors(g_zfs, argv[1]) != 0) {
- zfs_close(zhp);
- return (1);
- }
-
- ret = (zfs_rename(zhp, snapshot, argv[1], flags) != 0);
-
- zfs_close(zhp);
- return (ret);
-}
-
-/*
- * zfs promote <fs>
- *
- * Promotes the given clone fs to be the parent
- */
-/* ARGSUSED */
-static int
-zfs_do_promote(int argc, char **argv)
-{
- zfs_handle_t *zhp;
- int ret = 0;
-
- /* check options */
- if (argc > 1 && argv[1][0] == '-') {
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- argv[1][1]);
- usage(B_FALSE);
- }
-
- /* check number of arguments */
- if (argc < 2) {
- (void) fprintf(stderr, gettext("missing clone filesystem"
- " argument\n"));
- usage(B_FALSE);
- }
- if (argc > 2) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- zhp = zfs_open(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- if (zhp == NULL)
- return (1);
-
- ret = (zfs_promote(zhp) != 0);
-
-
- zfs_close(zhp);
- 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 {
- uint64_t cb_create;
- uint8_t cb_younger_ds_printed;
- boolean_t cb_first;
- int cb_doclones;
- char *cb_target;
- int cb_error;
- boolean_t cb_recurse;
-} rollback_cbdata_t;
-
-static int
-rollback_check_dependent(zfs_handle_t *zhp, void *data)
-{
- rollback_cbdata_t *cbp = data;
-
- if (cbp->cb_first && cbp->cb_recurse) {
- (void) fprintf(stderr, gettext("cannot rollback to "
- "'%s': clones of previous snapshots exist\n"),
- cbp->cb_target);
- (void) fprintf(stderr, gettext("use '-R' to "
- "force deletion of the following clones and "
- "dependents:\n"));
- cbp->cb_first = 0;
- cbp->cb_error = 1;
- }
-
- (void) fprintf(stderr, "%s\n", zfs_get_name(zhp));
-
- zfs_close(zhp);
- return (0);
-}
-
-/*
- * Report some snapshots/bookmarks 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
-rollback_check(zfs_handle_t *zhp, void *data)
-{
- rollback_cbdata_t *cbp = data;
- /*
- * Max number of younger snapshots and/or bookmarks to display before
- * we stop the iteration.
- */
- const uint8_t max_younger = 32;
-
- if (cbp->cb_doclones) {
- zfs_close(zhp);
- return (0);
- }
-
- if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {
- if (cbp->cb_first && !cbp->cb_recurse) {
- (void) fprintf(stderr, gettext("cannot "
- "rollback to '%s': more recent snapshots "
- "or bookmarks exist\n"),
- cbp->cb_target);
- (void) fprintf(stderr, gettext("use '-r' to "
- "force deletion of the following "
- "snapshots and bookmarks:\n"));
- cbp->cb_first = 0;
- cbp->cb_error = 1;
- }
-
- if (cbp->cb_recurse) {
- if (zfs_iter_dependents(zhp, B_TRUE,
- rollback_check_dependent, cbp) != 0) {
- zfs_close(zhp);
- return (-1);
- }
- } else {
- (void) fprintf(stderr, "%s\n",
- zfs_get_name(zhp));
- cbp->cb_younger_ds_printed++;
- }
- }
- zfs_close(zhp);
-
- if (cbp->cb_younger_ds_printed == max_younger) {
- /*
- * This non-recursive rollback is going to fail due to the
- * presence of snapshots and/or bookmarks that are younger than
- * the rollback target.
- * We printed some of the offending objects, now we stop
- * zfs_iter_snapshot/bookmark iteration so we can fail fast and
- * avoid iterating over the rest of the younger objects
- */
- (void) fprintf(stderr, gettext("Output limited to %d "
- "snapshots/bookmarks\n"), max_younger);
- return (-1);
- }
- return (0);
-}
-
-static int
-zfs_do_rollback(int argc, char **argv)
-{
- int ret = 0;
- int c;
- boolean_t force = B_FALSE;
- rollback_cbdata_t cb = { 0 };
- zfs_handle_t *zhp, *snap;
- char parentname[ZFS_MAX_DATASET_NAME_LEN];
- char *delim;
- uint64_t min_txg = 0;
-
- /* check options */
- while ((c = getopt(argc, argv, "rRf")) != -1) {
- switch (c) {
- case 'r':
- cb.cb_recurse = 1;
- break;
- case 'R':
- cb.cb_recurse = 1;
- cb.cb_doclones = 1;
- break;
- case 'f':
- force = B_TRUE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check number of arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing dataset argument\n"));
- usage(B_FALSE);
- }
- if (argc > 1) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- /* open the snapshot */
- if ((snap = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL)
- return (1);
-
- /* open the parent dataset */
- (void) strlcpy(parentname, argv[0], sizeof (parentname));
- verify((delim = strrchr(parentname, '@')) != NULL);
- *delim = '\0';
- if ((zhp = zfs_open(g_zfs, parentname, ZFS_TYPE_DATASET)) == NULL) {
- zfs_close(snap);
- return (1);
- }
-
- /*
- * Check for more recent snapshots and/or clones based on the presence
- * of '-r' and '-R'.
- */
- cb.cb_target = argv[0];
- cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
- cb.cb_first = B_TRUE;
- cb.cb_error = 0;
-
- if (cb.cb_create > 0)
- min_txg = cb.cb_create;
-
- if ((ret = zfs_iter_snapshots(zhp, B_FALSE, rollback_check, &cb,
- min_txg, 0)) != 0)
- goto out;
- if ((ret = zfs_iter_bookmarks(zhp, rollback_check, &cb)) != 0)
- goto out;
-
- if ((ret = cb.cb_error) != 0)
- goto out;
-
- /*
- * Rollback parent to the given snapshot.
- */
- ret = zfs_rollback(zhp, snap, force);
-
-out:
- zfs_close(snap);
- zfs_close(zhp);
-
- if (ret == 0)
- return (0);
- else
- return (1);
-}
-
-/*
- * zfs set property=value ... { fs | snap | vol } ...
- *
- * Sets the given properties for all datasets specified on the command line.
- */
-
-static int
-set_callback(zfs_handle_t *zhp, void *data)
-{
- nvlist_t *props = data;
-
- if (zfs_prop_set_list(zhp, props) != 0) {
- switch (libzfs_errno(g_zfs)) {
- case EZFS_MOUNTFAILED:
- (void) fprintf(stderr, gettext("property may be set "
- "but unable to remount filesystem\n"));
- break;
- case EZFS_SHARENFSFAILED:
- (void) fprintf(stderr, gettext("property may be set "
- "but unable to reshare filesystem\n"));
- break;
- }
- return (1);
- }
- return (0);
-}
-
-static int
-zfs_do_set(int argc, char **argv)
-{
- nvlist_t *props = NULL;
- int ds_start = -1; /* argv idx of first dataset arg */
- int ret = 0;
-
- /* check for options */
- if (argc > 1 && argv[1][0] == '-') {
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- argv[1][1]);
- usage(B_FALSE);
- }
-
- /* check number of arguments */
- if (argc < 2) {
- (void) fprintf(stderr, gettext("missing arguments\n"));
- usage(B_FALSE);
- }
- if (argc < 3) {
- if (strchr(argv[1], '=') == NULL) {
- (void) fprintf(stderr, gettext("missing property=value "
- "argument(s)\n"));
- } else {
- (void) fprintf(stderr, gettext("missing dataset "
- "name(s)\n"));
- }
- usage(B_FALSE);
- }
-
- /* validate argument order: prop=val args followed by dataset args */
- for (int i = 1; i < argc; i++) {
- if (strchr(argv[i], '=') != NULL) {
- if (ds_start > 0) {
- /* out-of-order prop=val argument */
- (void) fprintf(stderr, gettext("invalid "
- "argument order\n"), i);
- usage(B_FALSE);
- }
- } else if (ds_start < 0) {
- ds_start = i;
- }
- }
- if (ds_start < 0) {
- (void) fprintf(stderr, gettext("missing dataset name(s)\n"));
- usage(B_FALSE);
- }
-
- /* Populate a list of property settings */
- if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
- nomem();
- for (int i = 1; i < ds_start; i++) {
- if ((ret = parseprop(props, argv[i])) != 0)
- goto error;
- }
-
- ret = zfs_for_each(argc - ds_start, argv + ds_start, 0,
- ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, props);
-
-error:
- nvlist_free(props);
- return (ret);
-}
-
-typedef struct snap_cbdata {
- nvlist_t *sd_nvl;
- boolean_t sd_recursive;
- const char *sd_snapname;
-} snap_cbdata_t;
-
-static int
-zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
-{
- snap_cbdata_t *sd = arg;
- char *name;
- int rv = 0;
- int error;
-
- if (sd->sd_recursive &&
- zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) != 0) {
- zfs_close(zhp);
- return (0);
- }
-
- error = asprintf(&name, "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
- if (error == -1)
- nomem();
- fnvlist_add_boolean(sd->sd_nvl, name);
- free(name);
-
- if (sd->sd_recursive)
- rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
- zfs_close(zhp);
- 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
-zfs_do_snapshot(int argc, char **argv)
-{
- int ret = 0;
- int c;
- nvlist_t *props;
- snap_cbdata_t sd = { 0 };
- boolean_t multiple_snaps = B_FALSE;
-
- if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
- nomem();
- if (nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) != 0)
- nomem();
-
- /* check options */
- while ((c = getopt(argc, argv, "ro:")) != -1) {
- switch (c) {
- case 'o':
- if (parseprop(props, optarg) != 0)
- return (1);
- break;
- case 'r':
- sd.sd_recursive = B_TRUE;
- multiple_snaps = B_TRUE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- goto usage;
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check number of arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing snapshot argument\n"));
- goto usage;
- }
-
- if (argc > 1)
- multiple_snaps = B_TRUE;
- for (; argc > 0; argc--, argv++) {
- char *atp;
- zfs_handle_t *zhp;
-
- atp = strchr(argv[0], '@');
- if (atp == NULL)
- goto usage;
- *atp = '\0';
- sd.sd_snapname = atp + 1;
- zhp = zfs_open(g_zfs, argv[0],
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- if (zhp == NULL)
- goto usage;
- if (zfs_snapshot_cb(zhp, &sd) != 0)
- goto usage;
- }
-
- ret = zfs_snapshot_nvl(g_zfs, sd.sd_nvl, props);
- nvlist_free(sd.sd_nvl);
- nvlist_free(props);
- if (ret != 0 && multiple_snaps)
- (void) fprintf(stderr, gettext("no snapshots were created\n"));
- return (ret != 0);
-
-usage:
- nvlist_free(sd.sd_nvl);
- nvlist_free(props);
- usage(B_FALSE);
- return (-1);
-}
-
-/*
- * Send a backup stream to stdout.
- */
-static int
-zfs_do_send(int argc, char **argv)
-{
- char *fromname = NULL;
- char *toname = NULL;
- char *resume_token = NULL;
- char *cp;
- zfs_handle_t *zhp;
- sendflags_t flags = { 0 };
- int c, err;
- nvlist_t *dbgnv = NULL;
- boolean_t extraverbose = B_FALSE;
-
- struct option long_options[] = {
- {"replicate", no_argument, NULL, 'R'},
- {"props", no_argument, NULL, 'p'},
- {"parsable", no_argument, NULL, 'P'},
- {"dedup", no_argument, NULL, 'D'},
- {"verbose", no_argument, NULL, 'v'},
- {"dryrun", no_argument, NULL, 'n'},
- {"large-block", no_argument, NULL, 'L'},
- {"embed", no_argument, NULL, 'e'},
- {"resume", required_argument, NULL, 't'},
- {"compressed", no_argument, NULL, 'c'},
- {0, 0, 0, 0}
- };
-
- /* check options */
- while ((c = getopt_long(argc, argv, ":i:I:RbDpVvnPLet:c", long_options,
- NULL)) != -1) {
- switch (c) {
- case 'i':
- if (fromname)
- usage(B_FALSE);
- fromname = optarg;
- break;
- case 'I':
- if (fromname)
- usage(B_FALSE);
- fromname = optarg;
- flags.doall = B_TRUE;
- break;
- case 'R':
- flags.replicate = B_TRUE;
- break;
- case 'p':
- flags.props = B_TRUE;
- break;
- case 'P':
- flags.parsable = B_TRUE;
- flags.verbose = B_TRUE;
- break;
- case 'V':
- flags.progress = B_TRUE;
- flags.progressastitle = B_TRUE;
- break;
- case 'v':
- if (flags.verbose)
- extraverbose = B_TRUE;
- flags.verbose = B_TRUE;
- flags.progress = B_TRUE;
- break;
- case 'D':
- flags.dedup = B_TRUE;
- break;
- case 'n':
- flags.dryrun = B_TRUE;
- break;
- case 'L':
- flags.largeblock = B_TRUE;
- break;
- case 'e':
- flags.embed_data = B_TRUE;
- break;
- case 't':
- resume_token = optarg;
- break;
- case 'c':
- flags.compress = B_TRUE;
- break;
- case ':':
- (void) fprintf(stderr, gettext("missing argument for "
- "'%c' option\n"), optopt);
- usage(B_FALSE);
- break;
- case '?':
- /*FALLTHROUGH*/
- default:
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (resume_token != NULL) {
- if (fromname != NULL || flags.replicate || flags.props ||
- flags.dedup) {
- (void) fprintf(stderr,
- gettext("invalid flags combined with -t\n"));
- usage(B_FALSE);
- }
- if (argc != 0) {
- (void) fprintf(stderr, gettext("no additional "
- "arguments are permitted with -t\n"));
- usage(B_FALSE);
- }
- } else {
- if (argc < 1) {
- (void) fprintf(stderr,
- gettext("missing snapshot argument\n"));
- usage(B_FALSE);
- }
- if (argc > 1) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
- }
-
- if (!flags.dryrun && isatty(STDOUT_FILENO)) {
- (void) fprintf(stderr,
- gettext("Error: Stream can not be written to a terminal.\n"
- "You must redirect standard output.\n"));
- return (1);
- }
-
- if (resume_token != NULL) {
- return (zfs_send_resume(g_zfs, &flags, STDOUT_FILENO,
- resume_token));
- }
-
- /*
- * Special case sending a filesystem, or from a bookmark.
- */
- if (strchr(argv[0], '@') == NULL ||
- (fromname && strchr(fromname, '#') != NULL)) {
- char frombuf[ZFS_MAX_DATASET_NAME_LEN];
-
- if (flags.replicate || flags.doall || flags.props ||
- flags.dedup || (strchr(argv[0], '@') == NULL &&
- (flags.dryrun || flags.verbose || flags.progress))) {
- (void) fprintf(stderr, gettext("Error: "
- "Unsupported flag with filesystem or bookmark.\n"));
- return (1);
- }
-
- zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET);
- if (zhp == NULL)
- return (1);
-
- if (fromname != NULL &&
- (fromname[0] == '#' || fromname[0] == '@')) {
- /*
- * Incremental source name begins with # or @.
- * Default to same fs as target.
- */
- (void) strncpy(frombuf, argv[0], sizeof (frombuf));
- cp = strchr(frombuf, '@');
- if (cp != NULL)
- *cp = '\0';
- (void) strlcat(frombuf, fromname, sizeof (frombuf));
- fromname = frombuf;
- }
- err = zfs_send_one(zhp, fromname, STDOUT_FILENO, flags);
- zfs_close(zhp);
- return (err != 0);
- }
-
- cp = strchr(argv[0], '@');
- *cp = '\0';
- toname = cp + 1;
- zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- if (zhp == NULL)
- 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.
- */
- if (fromname && (cp = strchr(fromname, '@')) != NULL) {
- char origin[ZFS_MAX_DATASET_NAME_LEN];
- zprop_source_t src;
-
- (void) zfs_prop_get(zhp, ZFS_PROP_ORIGIN,
- origin, sizeof (origin), &src, NULL, 0, B_FALSE);
-
- if (strcmp(origin, fromname) == 0) {
- fromname = NULL;
- flags.fromorigin = B_TRUE;
- } else {
- *cp = '\0';
- if (cp != fromname && strcmp(argv[0], fromname)) {
- (void) fprintf(stderr,
- gettext("incremental source must be "
- "in same filesystem\n"));
- usage(B_FALSE);
- }
- fromname = cp + 1;
- if (strchr(fromname, '@') || strchr(fromname, '/')) {
- (void) fprintf(stderr,
- gettext("invalid incremental source\n"));
- usage(B_FALSE);
- }
- }
- }
-
- if (flags.replicate && fromname == NULL)
- flags.doall = B_TRUE;
-
- err = zfs_send(zhp, fromname, toname, &flags, STDOUT_FILENO, NULL, 0,
- extraverbose ? &dbgnv : NULL);
-
- if (extraverbose && dbgnv != NULL) {
- /*
- * dump_nvlist prints to stdout, but that's been
- * redirected to a file. Make it print to stderr
- * instead.
- */
- (void) dup2(STDERR_FILENO, STDOUT_FILENO);
- dump_nvlist(dbgnv, 0);
- nvlist_free(dbgnv);
- }
- zfs_close(zhp);
-
- return (err != 0);
-}
-
-/*
- * Restore a backup stream from stdin.
- */
-static int
-zfs_do_receive(int argc, char **argv)
-{
- int c, err = 0;
- recvflags_t flags = { 0 };
- boolean_t abort_resumable = B_FALSE;
-
- nvlist_t *props;
- nvpair_t *nvp = NULL;
-
- if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
- nomem();
-
- /* check options */
- while ((c = getopt(argc, argv, ":o:denuvMFsA")) != -1) {
- switch (c) {
- case 'o':
- if (parseprop(props, optarg) != 0)
- return (1);
- break;
- case 'd':
- flags.isprefix = B_TRUE;
- break;
- case 'e':
- flags.isprefix = B_TRUE;
- flags.istail = B_TRUE;
- break;
- case 'n':
- flags.dryrun = B_TRUE;
- break;
- case 'u':
- flags.nomount = B_TRUE;
- break;
- case 'v':
- flags.verbose = B_TRUE;
- break;
- case 's':
- flags.resumable = B_TRUE;
- break;
- case 'F':
- flags.force = B_TRUE;
- break;
- case 'M':
- flags.forceunmount = B_TRUE;
- break;
- case 'A':
- abort_resumable = B_TRUE;
- break;
- case ':':
- (void) fprintf(stderr, gettext("missing argument for "
- "'%c' option\n"), optopt);
- usage(B_FALSE);
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check number of arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing snapshot argument\n"));
- usage(B_FALSE);
- }
- if (argc > 1) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- while ((nvp = nvlist_next_nvpair(props, nvp))) {
- if (strcmp(nvpair_name(nvp), "origin") != 0) {
- (void) fprintf(stderr, gettext("invalid option"));
- usage(B_FALSE);
- }
- }
-
- if (abort_resumable) {
- if (flags.isprefix || flags.istail || flags.dryrun ||
- flags.resumable || flags.nomount) {
- (void) fprintf(stderr, gettext("invalid option"));
- usage(B_FALSE);
- }
-
- char namebuf[ZFS_MAX_DATASET_NAME_LEN];
- (void) snprintf(namebuf, sizeof (namebuf),
- "%s/%%recv", argv[0]);
-
- if (zfs_dataset_exists(g_zfs, namebuf,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) {
- zfs_handle_t *zhp = zfs_open(g_zfs,
- namebuf, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- if (zhp == NULL)
- return (1);
- err = zfs_destroy(zhp, B_FALSE);
- } else {
- zfs_handle_t *zhp = zfs_open(g_zfs,
- argv[0], ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- if (zhp == NULL)
- usage(B_FALSE);
- if (!zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) ||
- zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
- NULL, 0, NULL, NULL, 0, B_TRUE) == -1) {
- (void) fprintf(stderr,
- gettext("'%s' does not have any "
- "resumable receive state to abort\n"),
- argv[0]);
- return (1);
- }
- err = zfs_destroy(zhp, B_FALSE);
- }
-
- return (err != 0);
- }
-
- if (isatty(STDIN_FILENO)) {
- (void) fprintf(stderr,
- gettext("Error: Backup stream can not be read "
- "from a terminal.\n"
- "You must redirect standard input.\n"));
- return (1);
- }
- err = zfs_receive(g_zfs, argv[0], props, &flags, STDIN_FILENO, NULL);
-
- return (err != 0);
-}
-
-/*
- * allow/unallow stuff
- */
-/* 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_VSCAN "vscan" /* ??? */
-#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"
-#define ZFS_DELEG_PERM_BOOKMARK "bookmark"
-#define ZFS_DELEG_PERM_REMAP "remap"
-
-#define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE
-
-static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = {
- { ZFS_DELEG_PERM_ALLOW, ZFS_DELEG_NOTE_ALLOW },
- { ZFS_DELEG_PERM_CLONE, ZFS_DELEG_NOTE_CLONE },
- { ZFS_DELEG_PERM_CREATE, ZFS_DELEG_NOTE_CREATE },
- { ZFS_DELEG_PERM_DESTROY, ZFS_DELEG_NOTE_DESTROY },
- { ZFS_DELEG_PERM_DIFF, ZFS_DELEG_NOTE_DIFF},
- { ZFS_DELEG_PERM_HOLD, ZFS_DELEG_NOTE_HOLD },
- { ZFS_DELEG_PERM_MOUNT, ZFS_DELEG_NOTE_MOUNT },
- { ZFS_DELEG_PERM_PROMOTE, ZFS_DELEG_NOTE_PROMOTE },
- { ZFS_DELEG_PERM_RECEIVE, ZFS_DELEG_NOTE_RECEIVE },
- { ZFS_DELEG_PERM_RELEASE, ZFS_DELEG_NOTE_RELEASE },
- { ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },
- { ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },
- { ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND },
- { ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },
- { ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },
- { ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK },
- { ZFS_DELEG_PERM_REMAP, ZFS_DELEG_NOTE_REMAP },
-
- { ZFS_DELEG_PERM_GROUPQUOTA, ZFS_DELEG_NOTE_GROUPQUOTA },
- { ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_NOTE_GROUPUSED },
- { ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP },
- { ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA },
- { ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED },
- { NULL, ZFS_DELEG_NOTE_NONE }
-};
-
-/* permission structure */
-typedef struct deleg_perm {
- zfs_deleg_who_type_t dp_who_type;
- const char *dp_name;
- boolean_t dp_local;
- boolean_t dp_descend;
-} deleg_perm_t;
-
-/* */
-typedef struct deleg_perm_node {
- deleg_perm_t dpn_perm;
-
- uu_avl_node_t dpn_avl_node;
-} deleg_perm_node_t;
-
-typedef struct fs_perm fs_perm_t;
-
-/* permissions set */
-typedef struct who_perm {
- zfs_deleg_who_type_t who_type;
- const char *who_name; /* id */
- char who_ug_name[256]; /* user/group name */
- fs_perm_t *who_fsperm; /* uplink */
-
- uu_avl_t *who_deleg_perm_avl; /* permissions */
-} who_perm_t;
-
-/* */
-typedef struct who_perm_node {
- who_perm_t who_perm;
- uu_avl_node_t who_avl_node;
-} who_perm_node_t;
-
-typedef struct fs_perm_set fs_perm_set_t;
-/* fs permissions */
-struct fs_perm {
- const char *fsp_name;
-
- uu_avl_t *fsp_sc_avl; /* sets,create */
- uu_avl_t *fsp_uge_avl; /* user,group,everyone */
-
- fs_perm_set_t *fsp_set; /* uplink */
-};
-
-/* */
-typedef struct fs_perm_node {
- fs_perm_t fspn_fsperm;
- uu_avl_t *fspn_avl;
-
- uu_list_node_t fspn_list_node;
-} fs_perm_node_t;
-
-/* top level structure */
-struct fs_perm_set {
- uu_list_pool_t *fsps_list_pool;
- uu_list_t *fsps_list; /* list of fs_perms */
-
- uu_avl_pool_t *fsps_named_set_avl_pool;
- uu_avl_pool_t *fsps_who_perm_avl_pool;
- uu_avl_pool_t *fsps_deleg_perm_avl_pool;
-};
-
-static inline const char *
-deleg_perm_type(zfs_deleg_note_t note)
-{
- /* subcommands */
- switch (note) {
- /* SUBCOMMANDS */
- /* OTHER */
- case ZFS_DELEG_NOTE_GROUPQUOTA:
- 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
-who_type2weight(zfs_deleg_who_type_t who_type)
-{
- 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
-who_perm_compare(const void *larg, const void *rarg, void *unused)
-{
- const who_perm_node_t *l = larg;
- const who_perm_node_t *r = rarg;
- zfs_deleg_who_type_t ltype = l->who_perm.who_type;
- zfs_deleg_who_type_t rtype = r->who_perm.who_type;
- int lweight = who_type2weight(ltype);
- int rweight = who_type2weight(rtype);
- int res = lweight - rweight;
- if (res == 0)
- res = strncmp(l->who_perm.who_name, r->who_perm.who_name,
- ZFS_MAX_DELEG_NAME-1);
-
- if (res == 0)
- return (0);
- if (res > 0)
- return (1);
- else
- return (-1);
-}
-
-/* ARGSUSED */
-static int
-deleg_perm_compare(const void *larg, const void *rarg, void *unused)
-{
- const deleg_perm_node_t *l = larg;
- const deleg_perm_node_t *r = rarg;
- int res = strncmp(l->dpn_perm.dp_name, r->dpn_perm.dp_name,
- ZFS_MAX_DELEG_NAME-1);
-
- if (res == 0)
- return (0);
-
- if (res > 0)
- return (1);
- else
- return (-1);
-}
-
-static inline void
-fs_perm_set_init(fs_perm_set_t *fspset)
-{
- bzero(fspset, sizeof (fs_perm_set_t));
-
- if ((fspset->fsps_list_pool = uu_list_pool_create("fsps_list_pool",
- sizeof (fs_perm_node_t), offsetof(fs_perm_node_t, fspn_list_node),
- NULL, UU_DEFAULT)) == NULL)
- nomem();
- if ((fspset->fsps_list = uu_list_create(fspset->fsps_list_pool, NULL,
- UU_DEFAULT)) == NULL)
- nomem();
-
- if ((fspset->fsps_named_set_avl_pool = uu_avl_pool_create(
- "named_set_avl_pool", sizeof (who_perm_node_t), offsetof(
- who_perm_node_t, who_avl_node), who_perm_compare,
- UU_DEFAULT)) == NULL)
- nomem();
-
- if ((fspset->fsps_who_perm_avl_pool = uu_avl_pool_create(
- "who_perm_avl_pool", sizeof (who_perm_node_t), offsetof(
- who_perm_node_t, who_avl_node), who_perm_compare,
- UU_DEFAULT)) == NULL)
- nomem();
-
- if ((fspset->fsps_deleg_perm_avl_pool = uu_avl_pool_create(
- "deleg_perm_avl_pool", sizeof (deleg_perm_node_t), offsetof(
- deleg_perm_node_t, dpn_avl_node), deleg_perm_compare, UU_DEFAULT))
- == NULL)
- nomem();
-}
-
-static inline void fs_perm_fini(fs_perm_t *);
-static inline void who_perm_fini(who_perm_t *);
-
-static inline void
-fs_perm_set_fini(fs_perm_set_t *fspset)
-{
- fs_perm_node_t *node = uu_list_first(fspset->fsps_list);
-
- while (node != NULL) {
- fs_perm_node_t *next_node =
- uu_list_next(fspset->fsps_list, node);
- fs_perm_t *fsperm = &node->fspn_fsperm;
- fs_perm_fini(fsperm);
- uu_list_remove(fspset->fsps_list, node);
- free(node);
- node = next_node;
- }
-
- uu_avl_pool_destroy(fspset->fsps_named_set_avl_pool);
- uu_avl_pool_destroy(fspset->fsps_who_perm_avl_pool);
- uu_avl_pool_destroy(fspset->fsps_deleg_perm_avl_pool);
-}
-
-static inline void
-deleg_perm_init(deleg_perm_t *deleg_perm, zfs_deleg_who_type_t type,
- const char *name)
-{
- deleg_perm->dp_who_type = type;
- deleg_perm->dp_name = name;
-}
-
-static inline void
-who_perm_init(who_perm_t *who_perm, fs_perm_t *fsperm,
- zfs_deleg_who_type_t type, const char *name)
-{
- uu_avl_pool_t *pool;
- pool = fsperm->fsp_set->fsps_deleg_perm_avl_pool;
-
- bzero(who_perm, sizeof (who_perm_t));
-
- if ((who_perm->who_deleg_perm_avl = uu_avl_create(pool, NULL,
- UU_DEFAULT)) == NULL)
- nomem();
-
- who_perm->who_type = type;
- who_perm->who_name = name;
- who_perm->who_fsperm = fsperm;
-}
-
-static inline void
-who_perm_fini(who_perm_t *who_perm)
-{
- deleg_perm_node_t *node = uu_avl_first(who_perm->who_deleg_perm_avl);
-
- while (node != NULL) {
- deleg_perm_node_t *next_node =
- uu_avl_next(who_perm->who_deleg_perm_avl, node);
-
- uu_avl_remove(who_perm->who_deleg_perm_avl, node);
- free(node);
- node = next_node;
- }
-
- uu_avl_destroy(who_perm->who_deleg_perm_avl);
-}
-
-static inline void
-fs_perm_init(fs_perm_t *fsperm, fs_perm_set_t *fspset, const char *fsname)
-{
- uu_avl_pool_t *nset_pool = fspset->fsps_named_set_avl_pool;
- uu_avl_pool_t *who_pool = fspset->fsps_who_perm_avl_pool;
-
- bzero(fsperm, sizeof (fs_perm_t));
-
- if ((fsperm->fsp_sc_avl = uu_avl_create(nset_pool, NULL, UU_DEFAULT))
- == NULL)
- nomem();
-
- if ((fsperm->fsp_uge_avl = uu_avl_create(who_pool, NULL, UU_DEFAULT))
- == NULL)
- nomem();
-
- fsperm->fsp_set = fspset;
- fsperm->fsp_name = fsname;
-}
-
-static inline void
-fs_perm_fini(fs_perm_t *fsperm)
-{
- who_perm_node_t *node = uu_avl_first(fsperm->fsp_sc_avl);
- while (node != NULL) {
- who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_sc_avl,
- node);
- who_perm_t *who_perm = &node->who_perm;
- who_perm_fini(who_perm);
- uu_avl_remove(fsperm->fsp_sc_avl, node);
- free(node);
- node = next_node;
- }
-
- node = uu_avl_first(fsperm->fsp_uge_avl);
- while (node != NULL) {
- who_perm_node_t *next_node = uu_avl_next(fsperm->fsp_uge_avl,
- node);
- who_perm_t *who_perm = &node->who_perm;
- who_perm_fini(who_perm);
- uu_avl_remove(fsperm->fsp_uge_avl, node);
- free(node);
- node = next_node;
- }
-
- uu_avl_destroy(fsperm->fsp_sc_avl);
- uu_avl_destroy(fsperm->fsp_uge_avl);
-}
-
-static void
-set_deleg_perm_node(uu_avl_t *avl, deleg_perm_node_t *node,
- zfs_deleg_who_type_t who_type, const char *name, char locality)
-{
- uu_avl_index_t idx = 0;
-
- deleg_perm_node_t *found_node = NULL;
- deleg_perm_t *deleg_perm = &node->dpn_perm;
-
- deleg_perm_init(deleg_perm, who_type, name);
-
- if ((found_node = uu_avl_find(avl, node, NULL, &idx))
- == NULL)
- uu_avl_insert(avl, node, idx);
- else {
- node = found_node;
- deleg_perm = &node->dpn_perm;
- }
-
-
- switch (locality) {
- case ZFS_DELEG_LOCAL:
- deleg_perm->dp_local = B_TRUE;
- break;
- case ZFS_DELEG_DESCENDENT:
- deleg_perm->dp_descend = B_TRUE;
- break;
- case ZFS_DELEG_NA:
- break;
- default:
- assert(B_FALSE); /* invalid locality */
- }
-}
-
-static inline int
-parse_who_perm(who_perm_t *who_perm, nvlist_t *nvl, char locality)
-{
- nvpair_t *nvp = NULL;
- fs_perm_set_t *fspset = who_perm->who_fsperm->fsp_set;
- uu_avl_t *avl = who_perm->who_deleg_perm_avl;
- zfs_deleg_who_type_t who_type = who_perm->who_type;
-
- while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
- const char *name = nvpair_name(nvp);
- data_type_t type = nvpair_type(nvp);
- uu_avl_pool_t *avl_pool = fspset->fsps_deleg_perm_avl_pool;
- deleg_perm_node_t *node =
- safe_malloc(sizeof (deleg_perm_node_t));
-
- assert(type == DATA_TYPE_BOOLEAN);
-
- uu_avl_node_init(node, &node->dpn_avl_node, avl_pool);
- set_deleg_perm_node(avl, node, who_type, name, locality);
- }
-
- return (0);
-}
-
-static inline int
-parse_fs_perm(fs_perm_t *fsperm, nvlist_t *nvl)
-{
- nvpair_t *nvp = NULL;
- fs_perm_set_t *fspset = fsperm->fsp_set;
-
- while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
- nvlist_t *nvl2 = NULL;
- const char *name = nvpair_name(nvp);
- uu_avl_t *avl = NULL;
- uu_avl_pool_t *avl_pool = NULL;
- zfs_deleg_who_type_t perm_type = name[0];
- char perm_locality = name[1];
- const char *perm_name = name + 3;
- boolean_t is_set = B_TRUE;
- who_perm_t *who_perm = NULL;
-
- assert('$' == name[2]);
-
- if (nvpair_value_nvlist(nvp, &nvl2) != 0)
- 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:
- avl_pool = fspset->fsps_named_set_avl_pool;
- avl = fsperm->fsp_sc_avl;
- 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:
- avl_pool = fspset->fsps_who_perm_avl_pool;
- avl = fsperm->fsp_uge_avl;
- break;
-
- default:
- assert(!"unhandled zfs_deleg_who_type_t");
- }
-
- if (is_set) {
- who_perm_node_t *found_node = NULL;
- who_perm_node_t *node = safe_malloc(
- sizeof (who_perm_node_t));
- who_perm = &node->who_perm;
- uu_avl_index_t idx = 0;
-
- uu_avl_node_init(node, &node->who_avl_node, avl_pool);
- who_perm_init(who_perm, fsperm, perm_type, perm_name);
-
- if ((found_node = uu_avl_find(avl, node, NULL, &idx))
- == NULL) {
- if (avl == fsperm->fsp_uge_avl) {
- uid_t rid = 0;
- struct passwd *p = NULL;
- struct group *g = NULL;
- const char *nice_name = NULL;
-
- switch (perm_type) {
- case ZFS_DELEG_USER_SETS:
- case ZFS_DELEG_USER:
- rid = atoi(perm_name);
- p = getpwuid(rid);
- if (p)
- nice_name = p->pw_name;
- break;
- case ZFS_DELEG_GROUP_SETS:
- case ZFS_DELEG_GROUP:
- rid = atoi(perm_name);
- g = getgrgid(rid);
- if (g)
- nice_name = g->gr_name;
- break;
-
- default:
- break;
- }
-
- if (nice_name != NULL)
- (void) strlcpy(
- node->who_perm.who_ug_name,
- nice_name, 256);
- else {
- /* User or group unknown */
- (void) snprintf(
- node->who_perm.who_ug_name,
- sizeof (
- node->who_perm.who_ug_name),
- "(unknown: %d)", rid);
- }
- }
-
- uu_avl_insert(avl, node, idx);
- } else {
- node = found_node;
- who_perm = &node->who_perm;
- }
- }
-
- (void) parse_who_perm(who_perm, nvl2, perm_locality);
- }
-
- return (0);
-}
-
-static inline int
-parse_fs_perm_set(fs_perm_set_t *fspset, nvlist_t *nvl)
-{
- nvpair_t *nvp = NULL;
- uu_avl_index_t idx = 0;
-
- while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
- nvlist_t *nvl2 = NULL;
- const char *fsname = nvpair_name(nvp);
- data_type_t type = nvpair_type(nvp);
- fs_perm_t *fsperm = NULL;
- fs_perm_node_t *node = safe_malloc(sizeof (fs_perm_node_t));
- if (node == NULL)
- nomem();
-
- fsperm = &node->fspn_fsperm;
-
- assert(DATA_TYPE_NVLIST == type);
-
- uu_list_node_init(node, &node->fspn_list_node,
- fspset->fsps_list_pool);
-
- idx = uu_list_numnodes(fspset->fsps_list);
- fs_perm_init(fsperm, fspset, fsname);
-
- if (nvpair_value_nvlist(nvp, &nvl2) != 0)
- return (-1);
-
- (void) parse_fs_perm(fsperm, nvl2);
-
- uu_list_insert(fspset->fsps_list, node, idx);
- }
-
- return (0);
-}
-
-static inline const char *
-deleg_perm_comment(zfs_deleg_note_t note)
-{
- const char *str = "";
-
- /* subcommands */
- switch (note) {
- /* SUBCOMMANDS */
- case ZFS_DELEG_NOTE_ALLOW:
- str = gettext("Must also have the permission that is being"
- "\n\t\t\t\tallowed");
- break;
- case ZFS_DELEG_NOTE_CLONE:
- str = gettext("Must also have the 'create' ability and 'mount'"
- "\n\t\t\t\tability in the origin file system");
- break;
- case ZFS_DELEG_NOTE_CREATE:
- str = gettext("Must also have the 'mount' ability");
- break;
- case ZFS_DELEG_NOTE_DESTROY:
- str = gettext("Must also have the 'mount' ability");
- break;
- case ZFS_DELEG_NOTE_DIFF:
- str = gettext("Allows lookup of paths within a dataset;"
- "\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:
- str = gettext("Allows adding a user hold to a snapshot");
- break;
- case ZFS_DELEG_NOTE_MOUNT:
- str = gettext("Allows mount/umount of ZFS datasets");
- break;
- case ZFS_DELEG_NOTE_PROMOTE:
- str = gettext("Must also have the 'mount'\n\t\t\t\tand"
- " 'promote' ability in the origin file system");
- break;
- case ZFS_DELEG_NOTE_RECEIVE:
- str = gettext("Must also have the 'mount' and 'create'"
- " ability");
- break;
- case ZFS_DELEG_NOTE_RELEASE:
- str = gettext("Allows releasing a user hold which\n\t\t\t\t"
- "might destroy the snapshot");
- break;
- case ZFS_DELEG_NOTE_RENAME:
- str = gettext("Must also have the 'mount' and 'create'"
- "\n\t\t\t\tability in the new parent");
- break;
- case ZFS_DELEG_NOTE_ROLLBACK:
- str = gettext("");
- break;
- case ZFS_DELEG_NOTE_SEND:
- str = gettext("");
- break;
- case ZFS_DELEG_NOTE_SHARE:
- str = gettext("Allows sharing file systems over NFS or SMB"
- "\n\t\t\t\tprotocols");
- break;
- case ZFS_DELEG_NOTE_SNAPSHOT:
- str = gettext("");
- break;
-/*
- * case ZFS_DELEG_NOTE_VSCAN:
- * str = gettext("");
- * break;
- */
- /* OTHER */
- case ZFS_DELEG_NOTE_GROUPQUOTA:
- str = gettext("Allows accessing any groupquota@... property");
- break;
- case ZFS_DELEG_NOTE_GROUPUSED:
- str = gettext("Allows reading any groupused@... property");
- break;
- case ZFS_DELEG_NOTE_USERPROP:
- str = gettext("Allows changing any user property");
- break;
- case ZFS_DELEG_NOTE_USERQUOTA:
- str = gettext("Allows accessing any userquota@... property");
- break;
- case ZFS_DELEG_NOTE_USERUSED:
- str = gettext("Allows reading any userused@... property");
- break;
- /* other */
- default:
- str = "";
- }
-
- return (str);
-}
-
-struct allow_opts {
- boolean_t local;
- boolean_t descend;
- boolean_t user;
- boolean_t group;
- boolean_t everyone;
- boolean_t create;
- boolean_t set;
- boolean_t recursive; /* unallow only */
- boolean_t prt_usage;
-
- boolean_t prt_perms;
- 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;
- return (strcmp(str1, str2));
-}
-
-static void
-allow_usage(boolean_t un, boolean_t requested, const char *msg)
-{
- const char *opt_desc[] = {
- "-h", gettext("show this help message and exit"),
- "-l", gettext("set permission locally"),
- "-d", gettext("set permission for descents"),
- "-u", gettext("set permission for user"),
- "-g", gettext("set permission for group"),
- "-e", gettext("set permission for everyone"),
- "-c", gettext("set create time permission"),
- "-s", gettext("define permission set"),
- /* unallow only */
- "-r", gettext("remove permissions recursively"),
- };
- size_t unallow_size = sizeof (opt_desc) / sizeof (char *);
- size_t allow_size = unallow_size - 2;
- const char *props[ZFS_NUM_PROPS];
- int i;
- size_t count = 0;
- FILE *fp = requested ? stdout : stderr;
- zprop_desc_t *pdtbl = zfs_prop_get_table();
- const char *fmt = gettext("%-16s %-14s\t%s\n");
-
- (void) fprintf(fp, gettext("Usage: %s\n"), get_usage(un ? HELP_UNALLOW :
- HELP_ALLOW));
- (void) fprintf(fp, gettext("Options:\n"));
- for (i = 0; i < (un ? unallow_size : allow_size); i++) {
- const char *opt = opt_desc[i++];
- const char *optdsc = opt_desc[i];
- (void) fprintf(fp, gettext(" %-10s %s\n"), opt, optdsc);
- }
-
- (void) fprintf(fp, gettext("\nThe following permissions are "
- "supported:\n\n"));
- (void) fprintf(fp, fmt, gettext("NAME"), gettext("TYPE"),
- gettext("NOTES"));
- for (i = 0; i < ZFS_NUM_DELEG_NOTES; i++) {
- const char *perm_name = zfs_deleg_perm_tbl[i].z_perm;
- zfs_deleg_note_t perm_note = zfs_deleg_perm_tbl[i].z_note;
- const char *perm_type = deleg_perm_type(perm_note);
- const char *perm_comment = deleg_perm_comment(perm_note);
- (void) fprintf(fp, fmt, perm_name, perm_type, perm_comment);
- }
-
- for (i = 0; i < ZFS_NUM_PROPS; i++) {
- zprop_desc_t *pd = &pdtbl[i];
- if (pd->pd_visible != B_TRUE)
- continue;
-
- if (pd->pd_attr == PROP_READONLY)
- continue;
-
- props[count++] = pd->pd_name;
- }
- props[count] = NULL;
-
- qsort(props, count, sizeof (char *), prop_cmp);
-
- for (i = 0; i < count; i++)
- (void) fprintf(fp, fmt, props[i], gettext("property"), "");
-
- if (msg != NULL)
- (void) fprintf(fp, gettext("\nzfs: error: %s"), msg);
-
- exit(requested ? 0 : 2);
-}
-
-static inline const char *
-munge_args(int argc, char **argv, boolean_t un, size_t expected_argc,
- char **permsp)
-{
- if (un && argc == expected_argc - 1)
- *permsp = NULL;
- else if (argc == expected_argc)
- *permsp = argv[argc - 2];
- else
- allow_usage(un, B_FALSE,
- gettext("wrong number of parameters\n"));
-
- return (argv[argc - 1]);
-}
-
-static void
-parse_allow_args(int argc, char **argv, boolean_t un, struct allow_opts *opts)
-{
- int uge_sum = opts->user + opts->group + opts->everyone;
- int csuge_sum = opts->create + opts->set + uge_sum;
- int ldcsuge_sum = csuge_sum + opts->local + opts->descend;
- int all_sum = un ? ldcsuge_sum + opts->recursive : ldcsuge_sum;
-
- if (uge_sum > 1)
- allow_usage(un, B_FALSE,
- gettext("-u, -g, and -e are mutually exclusive\n"));
-
- if (opts->prt_usage) {
- if (argc == 0 && all_sum == 0)
- allow_usage(un, B_TRUE, NULL);
- else
- usage(B_FALSE);
- }
-
- if (opts->set) {
- if (csuge_sum > 1)
- allow_usage(un, B_FALSE,
- gettext("invalid options combined with -s\n"));
-
- opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
- if (argv[0][0] != '@')
- allow_usage(un, B_FALSE,
- gettext("invalid set name: missing '@' prefix\n"));
- opts->who = argv[0];
- } else if (opts->create) {
- if (ldcsuge_sum > 1)
- allow_usage(un, B_FALSE,
- gettext("invalid options combined with -c\n"));
- opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
- } else if (opts->everyone) {
- if (csuge_sum > 1)
- allow_usage(un, B_FALSE,
- gettext("invalid options combined with -e\n"));
- opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
- } else if (uge_sum == 0 && argc > 0 && strcmp(argv[0], "everyone")
- == 0) {
- opts->everyone = B_TRUE;
- argc--;
- argv++;
- opts->dataset = munge_args(argc, argv, un, 2, &opts->perms);
- } else if (argc == 1 && !un) {
- opts->prt_perms = B_TRUE;
- opts->dataset = argv[argc-1];
- } else {
- opts->dataset = munge_args(argc, argv, un, 3, &opts->perms);
- opts->who = argv[0];
- }
-
- if (!opts->local && !opts->descend) {
- opts->local = B_TRUE;
- opts->descend = B_TRUE;
- }
-}
-
-static void
-store_allow_perm(zfs_deleg_who_type_t type, boolean_t local, boolean_t descend,
- const char *who, char *perms, nvlist_t *top_nvl)
-{
- int i;
- char ld[2] = { '\0', '\0' };
- char who_buf[MAXNAMELEN + 32];
- char base_type = '\0';
- char set_type = '\0';
- nvlist_t *base_nvl = NULL;
- nvlist_t *set_nvl = NULL;
- nvlist_t *nvl;
-
- if (nvlist_alloc(&base_nvl, NV_UNIQUE_NAME, 0) != 0)
- nomem();
- if (nvlist_alloc(&set_nvl, NV_UNIQUE_NAME, 0) != 0)
- nomem();
-
- switch (type) {
- case ZFS_DELEG_NAMED_SET_SETS:
- case ZFS_DELEG_NAMED_SET:
- set_type = ZFS_DELEG_NAMED_SET_SETS;
- base_type = ZFS_DELEG_NAMED_SET;
- ld[0] = ZFS_DELEG_NA;
- break;
- case ZFS_DELEG_CREATE_SETS:
- case ZFS_DELEG_CREATE:
- set_type = ZFS_DELEG_CREATE_SETS;
- base_type = ZFS_DELEG_CREATE;
- ld[0] = ZFS_DELEG_NA;
- break;
- case ZFS_DELEG_USER_SETS:
- case ZFS_DELEG_USER:
- set_type = ZFS_DELEG_USER_SETS;
- base_type = ZFS_DELEG_USER;
- if (local)
- ld[0] = ZFS_DELEG_LOCAL;
- if (descend)
- ld[1] = ZFS_DELEG_DESCENDENT;
- break;
- case ZFS_DELEG_GROUP_SETS:
- case ZFS_DELEG_GROUP:
- set_type = ZFS_DELEG_GROUP_SETS;
- base_type = ZFS_DELEG_GROUP;
- if (local)
- ld[0] = ZFS_DELEG_LOCAL;
- if (descend)
- ld[1] = ZFS_DELEG_DESCENDENT;
- break;
- case ZFS_DELEG_EVERYONE_SETS:
- case ZFS_DELEG_EVERYONE:
- set_type = ZFS_DELEG_EVERYONE_SETS;
- base_type = ZFS_DELEG_EVERYONE;
- if (local)
- ld[0] = ZFS_DELEG_LOCAL;
- if (descend)
- ld[1] = ZFS_DELEG_DESCENDENT;
- break;
-
- default:
- assert(set_type != '\0' && base_type != '\0');
- }
-
- if (perms != NULL) {
- char *curr = perms;
- char *end = curr + strlen(perms);
-
- while (curr < end) {
- char *delim = strchr(curr, ',');
- if (delim == NULL)
- delim = end;
- else
- *delim = '\0';
-
- if (curr[0] == '@')
- nvl = set_nvl;
- else
- nvl = base_nvl;
-
- (void) nvlist_add_boolean(nvl, curr);
- if (delim != end)
- *delim = ',';
- curr = delim + 1;
- }
-
- for (i = 0; i < 2; i++) {
- char locality = ld[i];
- if (locality == 0)
- continue;
-
- if (!nvlist_empty(base_nvl)) {
- if (who != NULL)
- (void) snprintf(who_buf,
- sizeof (who_buf), "%c%c$%s",
- base_type, locality, who);
- else
- (void) snprintf(who_buf,
- sizeof (who_buf), "%c%c$",
- base_type, locality);
-
- (void) nvlist_add_nvlist(top_nvl, who_buf,
- base_nvl);
- }
-
-
- if (!nvlist_empty(set_nvl)) {
- if (who != NULL)
- (void) snprintf(who_buf,
- sizeof (who_buf), "%c%c$%s",
- set_type, locality, who);
- else
- (void) snprintf(who_buf,
- sizeof (who_buf), "%c%c$",
- set_type, locality);
-
- (void) nvlist_add_nvlist(top_nvl, who_buf,
- set_nvl);
- }
- }
- } else {
- for (i = 0; i < 2; i++) {
- char locality = ld[i];
- if (locality == 0)
- continue;
-
- if (who != NULL)
- (void) snprintf(who_buf, sizeof (who_buf),
- "%c%c$%s", base_type, locality, who);
- else
- (void) snprintf(who_buf, sizeof (who_buf),
- "%c%c$", base_type, locality);
- (void) nvlist_add_boolean(top_nvl, who_buf);
-
- if (who != NULL)
- (void) snprintf(who_buf, sizeof (who_buf),
- "%c%c$%s", set_type, locality, who);
- else
- (void) snprintf(who_buf, sizeof (who_buf),
- "%c%c$", set_type, locality);
- (void) nvlist_add_boolean(top_nvl, who_buf);
- }
- }
-}
-
-static int
-construct_fsacl_list(boolean_t un, struct allow_opts *opts, nvlist_t **nvlp)
-{
- if (nvlist_alloc(nvlp, NV_UNIQUE_NAME, 0) != 0)
- nomem();
-
- if (opts->set) {
- store_allow_perm(ZFS_DELEG_NAMED_SET, opts->local,
- opts->descend, opts->who, opts->perms, *nvlp);
- } else if (opts->create) {
- store_allow_perm(ZFS_DELEG_CREATE, opts->local,
- opts->descend, NULL, opts->perms, *nvlp);
- } else if (opts->everyone) {
- store_allow_perm(ZFS_DELEG_EVERYONE, opts->local,
- opts->descend, NULL, opts->perms, *nvlp);
- } else {
- char *curr = opts->who;
- char *end = curr + strlen(curr);
-
- while (curr < end) {
- const char *who;
- zfs_deleg_who_type_t who_type = ZFS_DELEG_WHO_UNKNOWN;
- char *endch;
- char *delim = strchr(curr, ',');
- char errbuf[256];
- char id[64];
- struct passwd *p = NULL;
- struct group *g = NULL;
-
- uid_t rid;
- if (delim == NULL)
- delim = end;
- else
- *delim = '\0';
-
- rid = (uid_t)strtol(curr, &endch, 0);
- if (opts->user) {
- who_type = ZFS_DELEG_USER;
- if (*endch != '\0')
- p = getpwnam(curr);
- else
- p = getpwuid(rid);
-
- if (p != NULL)
- rid = p->pw_uid;
- else if (*endch != '\0') {
- (void) snprintf(errbuf, 256, gettext(
- "invalid user %s\n"), curr);
- allow_usage(un, B_TRUE, errbuf);
- }
- } else if (opts->group) {
- who_type = ZFS_DELEG_GROUP;
- if (*endch != '\0')
- g = getgrnam(curr);
- else
- g = getgrgid(rid);
-
- if (g != NULL)
- rid = g->gr_gid;
- else if (*endch != '\0') {
- (void) snprintf(errbuf, 256, gettext(
- "invalid group %s\n"), curr);
- allow_usage(un, B_TRUE, errbuf);
- }
- } else {
- if (*endch != '\0') {
- p = getpwnam(curr);
- } else {
- p = getpwuid(rid);
- }
-
- if (p == NULL) {
- if (*endch != '\0') {
- g = getgrnam(curr);
- } else {
- g = getgrgid(rid);
- }
- }
-
- if (p != NULL) {
- who_type = ZFS_DELEG_USER;
- rid = p->pw_uid;
- } else if (g != NULL) {
- who_type = ZFS_DELEG_GROUP;
- rid = g->gr_gid;
- } else {
- (void) snprintf(errbuf, 256, gettext(
- "invalid user/group %s\n"), curr);
- allow_usage(un, B_TRUE, errbuf);
- }
- }
-
- (void) sprintf(id, "%u", rid);
- who = id;
-
- store_allow_perm(who_type, opts->local,
- opts->descend, who, opts->perms, *nvlp);
- curr = delim + 1;
- }
- }
-
- return (0);
-}
-
-static void
-print_set_creat_perms(uu_avl_t *who_avl)
-{
- const char *sc_title[] = {
- gettext("Permission sets:\n"),
- gettext("Create time permissions:\n"),
- NULL
- };
- const char **title_ptr = sc_title;
- who_perm_node_t *who_node = NULL;
- int prev_weight = -1;
-
- for (who_node = uu_avl_first(who_avl); who_node != NULL;
- who_node = uu_avl_next(who_avl, who_node)) {
- uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
- zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
- const char *who_name = who_node->who_perm.who_name;
- int weight = who_type2weight(who_type);
- boolean_t first = B_TRUE;
- deleg_perm_node_t *deleg_node;
-
- if (prev_weight != weight) {
- (void) printf(*title_ptr++);
- prev_weight = weight;
- }
-
- if (who_name == NULL || strnlen(who_name, 1) == 0)
- (void) printf("\t");
- else
- (void) printf("\t%s ", who_name);
-
- for (deleg_node = uu_avl_first(avl); deleg_node != NULL;
- deleg_node = uu_avl_next(avl, deleg_node)) {
- if (first) {
- (void) printf("%s",
- deleg_node->dpn_perm.dp_name);
- first = B_FALSE;
- } else
- (void) printf(",%s",
- deleg_node->dpn_perm.dp_name);
- }
-
- (void) printf("\n");
- }
-}
-
-static void
-print_uge_deleg_perms(uu_avl_t *who_avl, boolean_t local, boolean_t descend,
- const char *title)
-{
- who_perm_node_t *who_node = NULL;
- boolean_t prt_title = B_TRUE;
- uu_avl_walk_t *walk;
-
- if ((walk = uu_avl_walk_start(who_avl, UU_WALK_ROBUST)) == NULL)
- nomem();
-
- while ((who_node = uu_avl_walk_next(walk)) != NULL) {
- const char *who_name = who_node->who_perm.who_name;
- const char *nice_who_name = who_node->who_perm.who_ug_name;
- uu_avl_t *avl = who_node->who_perm.who_deleg_perm_avl;
- zfs_deleg_who_type_t who_type = who_node->who_perm.who_type;
- char delim = ' ';
- deleg_perm_node_t *deleg_node;
- boolean_t prt_who = B_TRUE;
-
- for (deleg_node = uu_avl_first(avl);
- deleg_node != NULL;
- deleg_node = uu_avl_next(avl, deleg_node)) {
- if (local != deleg_node->dpn_perm.dp_local ||
- descend != deleg_node->dpn_perm.dp_descend)
- continue;
-
- if (prt_who) {
- const char *who = NULL;
- if (prt_title) {
- prt_title = B_FALSE;
- (void) printf(title);
- }
-
- switch (who_type) {
- case ZFS_DELEG_USER_SETS:
- case ZFS_DELEG_USER:
- who = gettext("user");
- if (nice_who_name)
- who_name = nice_who_name;
- break;
- case ZFS_DELEG_GROUP_SETS:
- case ZFS_DELEG_GROUP:
- who = gettext("group");
- if (nice_who_name)
- who_name = nice_who_name;
- break;
- case ZFS_DELEG_EVERYONE_SETS:
- case ZFS_DELEG_EVERYONE:
- who = gettext("everyone");
- who_name = NULL;
- break;
-
- default:
- assert(who != NULL);
- }
-
- prt_who = B_FALSE;
- if (who_name == NULL)
- (void) printf("\t%s", who);
- else
- (void) printf("\t%s %s", who, who_name);
- }
-
- (void) printf("%c%s", delim,
- deleg_node->dpn_perm.dp_name);
- delim = ',';
- }
-
- if (!prt_who)
- (void) printf("\n");
- }
-
- uu_avl_walk_end(walk);
-}
-
-static void
-print_fs_perms(fs_perm_set_t *fspset)
-{
- fs_perm_node_t *node = NULL;
- char buf[MAXNAMELEN + 32];
- const char *dsname = buf;
-
- for (node = uu_list_first(fspset->fsps_list); node != NULL;
- node = uu_list_next(fspset->fsps_list, node)) {
- uu_avl_t *sc_avl = node->fspn_fsperm.fsp_sc_avl;
- uu_avl_t *uge_avl = node->fspn_fsperm.fsp_uge_avl;
- int left = 0;
-
- (void) snprintf(buf, sizeof (buf),
- gettext("---- Permissions on %s "),
- node->fspn_fsperm.fsp_name);
- (void) printf(dsname);
- left = 70 - strlen(buf);
- while (left-- > 0)
- (void) printf("-");
- (void) printf("\n");
-
- print_set_creat_perms(sc_avl);
- print_uge_deleg_perms(uge_avl, B_TRUE, B_FALSE,
- gettext("Local permissions:\n"));
- print_uge_deleg_perms(uge_avl, B_FALSE, B_TRUE,
- gettext("Descendent permissions:\n"));
- print_uge_deleg_perms(uge_avl, B_TRUE, B_TRUE,
- gettext("Local+Descendent permissions:\n"));
- }
-}
-
-static fs_perm_set_t fs_perm_set = { NULL, NULL, NULL, NULL };
-
-struct deleg_perms {
- boolean_t un;
- nvlist_t *nvl;
-};
-
-static int
-set_deleg_perms(zfs_handle_t *zhp, void *data)
-{
- struct deleg_perms *perms = (struct deleg_perms *)data;
- zfs_type_t zfs_type = zfs_get_type(zhp);
-
- if (zfs_type != ZFS_TYPE_FILESYSTEM && zfs_type != ZFS_TYPE_VOLUME)
- return (0);
-
- return (zfs_set_fsacl(zhp, perms->un, perms->nvl));
-}
-
-static int
-zfs_do_allow_unallow_impl(int argc, char **argv, boolean_t un)
-{
- zfs_handle_t *zhp;
- nvlist_t *perm_nvl = NULL;
- nvlist_t *update_perm_nvl = NULL;
- int error = 1;
- int c;
- struct allow_opts opts = { 0 };
-
- const char *optstr = un ? "ldugecsrh" : "ldugecsh";
-
- /* check opts */
- while ((c = getopt(argc, argv, optstr)) != -1) {
- switch (c) {
- case 'l':
- opts.local = B_TRUE;
- break;
- case 'd':
- opts.descend = B_TRUE;
- break;
- case 'u':
- opts.user = B_TRUE;
- break;
- case 'g':
- opts.group = B_TRUE;
- break;
- case 'e':
- opts.everyone = B_TRUE;
- break;
- case 's':
- opts.set = B_TRUE;
- break;
- case 'c':
- opts.create = B_TRUE;
- break;
- case 'r':
- opts.recursive = B_TRUE;
- break;
- case ':':
- (void) fprintf(stderr, gettext("missing argument for "
- "'%c' option\n"), optopt);
- usage(B_FALSE);
- break;
- case 'h':
- opts.prt_usage = B_TRUE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check arguments */
- parse_allow_args(argc, argv, un, &opts);
-
- /* try to open the dataset */
- if ((zhp = zfs_open(g_zfs, opts.dataset, ZFS_TYPE_FILESYSTEM |
- ZFS_TYPE_VOLUME)) == NULL) {
- (void) fprintf(stderr, "Failed to open dataset: %s\n",
- opts.dataset);
- return (-1);
- }
-
- if (zfs_get_fsacl(zhp, &perm_nvl) != 0)
- goto cleanup2;
-
- fs_perm_set_init(&fs_perm_set);
- if (parse_fs_perm_set(&fs_perm_set, perm_nvl) != 0) {
- (void) fprintf(stderr, "Failed to parse fsacl permissions\n");
- goto cleanup1;
- }
-
- if (opts.prt_perms)
- print_fs_perms(&fs_perm_set);
- else {
- (void) construct_fsacl_list(un, &opts, &update_perm_nvl);
- if (zfs_set_fsacl(zhp, un, update_perm_nvl) != 0)
- goto cleanup0;
-
- if (un && opts.recursive) {
- struct deleg_perms data = { un, update_perm_nvl };
- if (zfs_iter_filesystems(zhp, set_deleg_perms,
- &data) != 0)
- goto cleanup0;
- }
- }
-
- error = 0;
-
-cleanup0:
- nvlist_free(perm_nvl);
- nvlist_free(update_perm_nvl);
-cleanup1:
- fs_perm_set_fini(&fs_perm_set);
-cleanup2:
- zfs_close(zhp);
-
- return (error);
-}
-
-static int
-zfs_do_allow(int argc, char **argv)
-{
- return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE));
-}
-
-static int
-zfs_do_unallow(int argc, char **argv)
-{
- return (zfs_do_allow_unallow_impl(argc, argv, B_TRUE));
-}
-
-static int
-zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
-{
- int errors = 0;
- int i;
- const char *tag;
- boolean_t recursive = B_FALSE;
- const char *opts = holding ? "rt" : "r";
- int c;
-
- /* check options */
- while ((c = getopt(argc, argv, opts)) != -1) {
- switch (c) {
- case 'r':
- recursive = B_TRUE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check number of arguments */
- if (argc < 2)
- usage(B_FALSE);
-
- tag = argv[0];
- --argc;
- ++argv;
-
- if (holding && tag[0] == '.') {
- /* tags starting with '.' are reserved for libzfs */
- (void) fprintf(stderr, gettext("tag may not start with '.'\n"));
- usage(B_FALSE);
- }
-
- for (i = 0; i < argc; ++i) {
- zfs_handle_t *zhp;
- char parent[ZFS_MAX_DATASET_NAME_LEN];
- const char *delim;
- char *path = argv[i];
-
- delim = strchr(path, '@');
- if (delim == NULL) {
- (void) fprintf(stderr,
- gettext("'%s' is not a snapshot\n"), path);
- ++errors;
- continue;
- }
- (void) strncpy(parent, path, delim - path);
- parent[delim - path] = '\0';
-
- zhp = zfs_open(g_zfs, parent,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- if (zhp == NULL) {
- ++errors;
- continue;
- }
- if (holding) {
- if (zfs_hold(zhp, delim+1, tag, recursive, -1) != 0)
- ++errors;
- } else {
- if (zfs_release(zhp, delim+1, tag, recursive) != 0)
- ++errors;
- }
- zfs_close(zhp);
- }
-
- return (errors != 0);
-}
-
-/*
- * zfs hold [-r] [-t] <tag> <snap> ...
- *
- * -r Recursively hold
- *
- * Apply a user-hold with the given tag to the list of snapshots.
- */
-static int
-zfs_do_hold(int argc, char **argv)
-{
- return (zfs_do_hold_rele_impl(argc, argv, B_TRUE));
-}
-
-/*
- * zfs release [-r] <tag> <snap> ...
- *
- * -r Recursively release
- *
- * Release a user-hold with the given tag from the list of snapshots.
- */
-static int
-zfs_do_release(int argc, char **argv)
-{
- return (zfs_do_hold_rele_impl(argc, argv, B_FALSE));
-}
-
-typedef struct holds_cbdata {
- boolean_t cb_recursive;
- const char *cb_snapname;
- nvlist_t **cb_nvlp;
- size_t cb_max_namelen;
- size_t cb_max_taglen;
-} holds_cbdata_t;
-
-#define STRFTIME_FMT_STR "%a %b %e %k:%M %Y"
-#define DATETIME_BUF_LEN (32)
-/*
- *
- */
-static void
-print_holds(boolean_t scripted, boolean_t literal, size_t nwidth,
- size_t tagwidth, nvlist_t *nvl)
-{
- int i;
- nvpair_t *nvp = NULL;
- char *hdr_cols[] = { "NAME", "TAG", "TIMESTAMP" };
- const char *col;
-
- if (!scripted) {
- for (i = 0; i < 3; i++) {
- col = gettext(hdr_cols[i]);
- if (i < 2)
- (void) printf("%-*s ", i ? tagwidth : nwidth,
- col);
- else
- (void) printf("%s\n", col);
- }
- }
-
- while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
- char *zname = nvpair_name(nvp);
- nvlist_t *nvl2;
- nvpair_t *nvp2 = NULL;
- (void) nvpair_value_nvlist(nvp, &nvl2);
- while ((nvp2 = nvlist_next_nvpair(nvl2, nvp2)) != NULL) {
- char tsbuf[DATETIME_BUF_LEN];
- char *tagname = nvpair_name(nvp2);
- uint64_t val = 0;
- time_t time;
- struct tm t;
-
- (void) nvpair_value_uint64(nvp2, &val);
- if (literal)
- snprintf(tsbuf, DATETIME_BUF_LEN, "%llu", val);
- else {
- time = (time_t)val;
- (void) localtime_r(&time, &t);
- (void) strftime(tsbuf, DATETIME_BUF_LEN,
- gettext(STRFTIME_FMT_STR), &t);
- }
-
- if (scripted) {
- (void) printf("%s\t%s\t%s\n", zname,
- tagname, tsbuf);
- } else {
- (void) printf("%-*s %-*s %s\n", nwidth,
- zname, tagwidth, tagname, tsbuf);
- }
- }
- }
-}
-
-/*
- * Generic callback function to list a dataset or snapshot.
- */
-static int
-holds_callback(zfs_handle_t *zhp, void *data)
-{
- holds_cbdata_t *cbp = data;
- nvlist_t *top_nvl = *cbp->cb_nvlp;
- nvlist_t *nvl = NULL;
- nvpair_t *nvp = NULL;
- const char *zname = zfs_get_name(zhp);
- size_t znamelen = strlen(zname);
-
- if (cbp->cb_recursive && cbp->cb_snapname != NULL) {
- const char *snapname;
- char *delim = strchr(zname, '@');
- if (delim == NULL)
- return (0);
-
- snapname = delim + 1;
- if (strcmp(cbp->cb_snapname, snapname))
- return (0);
- }
-
- if (zfs_get_holds(zhp, &nvl) != 0)
- return (-1);
-
- if (znamelen > cbp->cb_max_namelen)
- cbp->cb_max_namelen = znamelen;
-
- while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
- const char *tag = nvpair_name(nvp);
- size_t taglen = strlen(tag);
- if (taglen > cbp->cb_max_taglen)
- cbp->cb_max_taglen = taglen;
- }
-
- return (nvlist_add_nvlist(top_nvl, zname, nvl));
-}
-
-/*
- * zfs holds [-Hp] [-r | -d max] <dataset|snap> ...
- *
- * -H Suppress header output
- * -p Output literal values
- * -r Recursively search for holds
- * -d max Limit depth of recursive search
- */
-static int
-zfs_do_holds(int argc, char **argv)
-{
- int errors = 0;
- int c;
- int i;
- boolean_t scripted = B_FALSE;
- boolean_t literal = B_FALSE;
- boolean_t recursive = B_FALSE;
- const char *opts = "d:rHp";
- nvlist_t *nvl;
-
- int types = ZFS_TYPE_SNAPSHOT;
- holds_cbdata_t cb = { 0 };
-
- int limit = 0;
- int ret = 0;
- int flags = 0;
-
- /* check options */
- while ((c = getopt(argc, argv, opts)) != -1) {
- switch (c) {
- case 'd':
- limit = parse_depth(optarg, &flags);
- recursive = B_TRUE;
- break;
- case 'r':
- recursive = B_TRUE;
- break;
- case 'H':
- scripted = B_TRUE;
- break;
- case 'p':
- literal = B_TRUE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- if (recursive) {
- types |= ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
- flags |= ZFS_ITER_RECURSE;
- }
-
- argc -= optind;
- argv += optind;
-
- /* check number of arguments */
- if (argc < 1)
- usage(B_FALSE);
-
- if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
- nomem();
-
- for (i = 0; i < argc; ++i) {
- char *snapshot = argv[i];
- const char *delim;
- const char *snapname = NULL;
-
- delim = strchr(snapshot, '@');
- if (delim != NULL) {
- snapname = delim + 1;
- if (recursive)
- snapshot[delim - snapshot] = '\0';
- }
-
- cb.cb_recursive = recursive;
- cb.cb_snapname = snapname;
- cb.cb_nvlp = &nvl;
-
- /*
- * 1. collect holds data, set format options
- */
- ret = zfs_for_each(argc, argv, flags, types, NULL, NULL, limit,
- holds_callback, &cb);
- if (ret != 0)
- ++errors;
- }
-
- /*
- * 2. print holds data
- */
- print_holds(scripted, literal, cb.cb_max_namelen, cb.cb_max_taglen,
- nvl);
-
- if (nvlist_empty(nvl))
- (void) printf(gettext("no datasets available\n"));
-
- nvlist_free(nvl);
-
- return (0 != errors);
-}
-
-#define CHECK_SPINNER 30
-#define SPINNER_TIME 3 /* seconds */
-#define MOUNT_TIME 1 /* seconds */
-
-typedef struct get_all_state {
- boolean_t ga_verbose;
- get_all_cb_t *ga_cbp;
-} get_all_state_t;
-
-static int
-get_one_dataset(zfs_handle_t *zhp, void *data)
-{
- static char *spin[] = { "-", "\\", "|", "/" };
- static int spinval = 0;
- static int spincheck = 0;
- static time_t last_spin_time = (time_t)0;
- get_all_state_t *state = data;
- zfs_type_t type = zfs_get_type(zhp);
-
- if (state->ga_verbose) {
- if (--spincheck < 0) {
- time_t now = time(NULL);
- if (last_spin_time + SPINNER_TIME < now) {
- update_progress(spin[spinval++ % 4]);
- last_spin_time = now;
- }
- spincheck = CHECK_SPINNER;
- }
- }
-
- /*
- * Interate over any nested datasets.
- */
- if (zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) {
- zfs_close(zhp);
- return (1);
- }
-
- /*
- * Skip any datasets whose type does not match.
- */
- if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
- zfs_close(zhp);
- return (0);
- }
- libzfs_add_handle(state->ga_cbp, zhp);
- assert(state->ga_cbp->cb_used <= state->ga_cbp->cb_alloc);
-
- return (0);
-}
-
-static void
-get_all_datasets(get_all_cb_t *cbp, boolean_t verbose)
-{
- get_all_state_t state = {
- .ga_verbose = verbose,
- .ga_cbp = cbp
- };
-
- if (verbose)
- set_progress_header(gettext("Reading ZFS config"));
- (void) zfs_iter_root(g_zfs, get_one_dataset, &state);
-
- if (verbose)
- finish_progress(gettext("done."));
-}
-
-/*
- * 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.
- */
-typedef enum { OP_SHARE, OP_MOUNT } share_mount_op_t;
-
-typedef struct share_mount_state {
- share_mount_op_t sm_op;
- boolean_t sm_verbose;
- int sm_flags;
- char *sm_options;
- char *sm_proto; /* only valid for OP_SHARE */
- pthread_mutex_t sm_lock; /* protects the remaining fields */
- uint_t sm_total; /* number of filesystems to process */
- uint_t sm_done; /* number of filesystems processed */
- int sm_status; /* -1 if any of the share/mount operations failed */
-} share_mount_state_t;
-
-/*
- * Share or mount a dataset.
- */
-static int
-share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
- boolean_t explicit, const char *options)
-{
- char mountpoint[ZFS_MAXPROPLEN];
- char shareopts[ZFS_MAXPROPLEN];
- char smbshareopts[ZFS_MAXPROPLEN];
- const char *cmdname = op == OP_SHARE ? "share" : "mount";
- struct mnttab mnt;
- uint64_t zoned, canmount;
- boolean_t shared_nfs, shared_smb;
-
- assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM);
-
- /*
- * Check to make sure we can mount/share this dataset. If we
- * 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.
- */
- zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
-
- if (zoned && getzoneid() == GLOBAL_ZONEID) {
- if (!explicit)
- return (0);
-
- (void) fprintf(stderr, gettext("cannot %s '%s': "
- "dataset is exported to a local zone\n"), cmdname,
- zfs_get_name(zhp));
- return (1);
-
- } else if (!zoned && getzoneid() != GLOBAL_ZONEID) {
- if (!explicit)
- return (0);
-
- (void) fprintf(stderr, gettext("cannot %s '%s': "
- "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.
- */
- verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
- sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
- verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,
- sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);
- verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts,
- sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0);
-
- if (op == OP_SHARE && strcmp(shareopts, "off") == 0 &&
- strcmp(smbshareopts, "off") == 0) {
- if (!explicit)
- return (0);
-
- (void) fprintf(stderr, gettext("cannot share '%s': "
- "legacy share\n"), zfs_get_name(zhp));
- (void) fprintf(stderr, gettext("to "
- "share this filesystem 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 (strcmp(mountpoint, "legacy") == 0) {
- if (!explicit)
- return (0);
-
- (void) fprintf(stderr, gettext("cannot %s '%s': "
- "legacy mountpoint\n"), cmdname, zfs_get_name(zhp));
- (void) fprintf(stderr, gettext("use %s(8) to "
- "%s this filesystem\n"), cmdname, cmdname);
- return (1);
- }
-
- if (strcmp(mountpoint, "none") == 0) {
- if (!explicit)
- return (0);
-
- (void) fprintf(stderr, gettext("cannot %s '%s': no "
- "mountpoint set\n"), cmdname, zfs_get_name(zhp));
- 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
- */
- canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
- if (canmount == ZFS_CANMOUNT_OFF) {
- if (!explicit)
- return (0);
-
- (void) fprintf(stderr, gettext("cannot %s '%s': "
- "'canmount' property is set to 'off'\n"), cmdname,
- zfs_get_name(zhp));
- return (1);
- } else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) {
- return (0);
- }
-
- /*
- * If this filesystem is inconsistent and has a receive resume
- * token, we can not mount it.
- */
- if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) &&
- zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
- NULL, 0, NULL, NULL, 0, B_TRUE) == 0) {
- if (!explicit)
- return (0);
-
- (void) fprintf(stderr, gettext("cannot %s '%s': "
- "Contains partially-completed state from "
- "\"zfs receive -r\", which can be resumed with "
- "\"zfs send -t\"\n"),
- cmdname, zfs_get_name(zhp));
- return (1);
- }
-
- /*
- * At this point, we have verified that the mountpoint and/or
- * 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:
-
- shared_nfs = zfs_is_shared_nfs(zhp, NULL);
- shared_smb = zfs_is_shared_smb(zhp, NULL);
-
- if ((shared_nfs && shared_smb) ||
- (shared_nfs && strcmp(shareopts, "on") == 0 &&
- strcmp(smbshareopts, "off") == 0) ||
- (shared_smb && strcmp(smbshareopts, "on") == 0 &&
- strcmp(shareopts, "off") == 0)) {
- if (!explicit)
- return (0);
-
- (void) fprintf(stderr, gettext("cannot share "
- "'%s': filesystem already shared\n"),
- zfs_get_name(zhp));
- return (1);
- }
-
- if (!zfs_is_mounted(zhp, NULL) &&
- zfs_mount(zhp, NULL, 0) != 0)
- return (1);
-
- if (protocol == NULL) {
- if (zfs_shareall(zhp) != 0)
- return (1);
- } else if (strcmp(protocol, "nfs") == 0) {
- if (zfs_share_nfs(zhp))
- return (1);
- } else if (strcmp(protocol, "smb") == 0) {
- if (zfs_share_smb(zhp))
- return (1);
- } else {
- (void) fprintf(stderr, gettext("cannot share "
- "'%s': invalid share type '%s' "
- "specified\n"),
- zfs_get_name(zhp), protocol);
- return (1);
- }
-
- break;
-
- case OP_MOUNT:
- if (options == NULL)
- mnt.mnt_mntopts = "";
- else
- mnt.mnt_mntopts = (char *)options;
-
- if (!hasmntopt(&mnt, MNTOPT_REMOUNT) &&
- zfs_is_mounted(zhp, NULL)) {
- if (!explicit)
- return (0);
-
- (void) fprintf(stderr, gettext("cannot mount "
- "'%s': filesystem already mounted\n"),
- zfs_get_name(zhp));
- return (1);
- }
-
- if (zfs_mount(zhp, options, flags) != 0)
- return (1);
- break;
- }
-
- return (0);
-}
-
-/*
- * Reports progress in the form "(current/total)". Not thread-safe.
- */
-static void
-report_mount_progress(int current, int total)
-{
- static time_t last_progress_time = 0;
- time_t now = time(NULL);
- char info[32];
-
- /* display header if we're here for the first time */
- if (current == 1) {
- set_progress_header(gettext("Mounting ZFS filesystems"));
- } else if (current != total && last_progress_time + MOUNT_TIME >= now) {
- /* too soon to report again */
- return;
- }
-
- last_progress_time = now;
-
- (void) sprintf(info, "(%d/%d)", current, total);
-
- if (current == total)
- finish_progress(info);
- else
- update_progress(info);
-}
-
-/*
- * zfs_foreach_mountpoint() callback that mounts or shares on filesystem and
- * updates the progress meter
- */
-static int
-share_mount_one_cb(zfs_handle_t *zhp, void *arg)
-{
- share_mount_state_t *sms = arg;
- int ret;
-
- ret = share_mount_one(zhp, sms->sm_op, sms->sm_flags, sms->sm_proto,
- B_FALSE, sms->sm_options);
-
- pthread_mutex_lock(&sms->sm_lock);
- if (ret != 0)
- sms->sm_status = ret;
- sms->sm_done++;
- if (sms->sm_verbose)
- report_mount_progress(sms->sm_done, sms->sm_total);
- pthread_mutex_unlock(&sms->sm_lock);
- return (ret);
-}
-
-static void
-append_options(char *mntopts, char *newopts)
-{
- int len = strlen(mntopts);
-
- /* original length plus new string to append plus 1 for the comma */
- if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) {
- (void) fprintf(stderr, gettext("the opts argument for "
- "'%c' option is too long (more than %d chars)\n"),
- "-o", MNT_LINE_MAX);
- usage(B_FALSE);
- }
-
- if (*mntopts)
- mntopts[len++] = ',';
-
- (void) strcpy(&mntopts[len], newopts);
-}
-
-static int
-share_mount(int op, int argc, char **argv)
-{
- int do_all = 0;
- boolean_t verbose = B_FALSE;
- int c, ret = 0;
- char *options = NULL;
- int flags = 0;
-
- /* check options */
- while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:O" : "a"))
- != -1) {
- switch (c) {
- case 'a':
- do_all = 1;
- break;
- case 'v':
- verbose = B_TRUE;
- break;
- case 'o':
- if (*optarg == '\0') {
- (void) fprintf(stderr, gettext("empty mount "
- "options (-o) specified\n"));
- usage(B_FALSE);
- }
-
- if (options == NULL)
- options = safe_malloc(MNT_LINE_MAX + 1);
-
- /* option validation is done later */
- append_options(options, optarg);
- break;
-
- case 'O':
- warnx("no overlay mounts support on FreeBSD, ignoring");
- break;
- case ':':
- (void) fprintf(stderr, gettext("missing argument for "
- "'%c' option\n"), optopt);
- usage(B_FALSE);
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check number of arguments */
- if (do_all) {
- char *protocol = NULL;
-
- if (op == OP_SHARE && argc > 0) {
- if (strcmp(argv[0], "nfs") != 0 &&
- strcmp(argv[0], "smb") != 0) {
- (void) fprintf(stderr, gettext("share type "
- "must be 'nfs' or 'smb'\n"));
- usage(B_FALSE);
- }
- protocol = argv[0];
- argc--;
- argv++;
- }
-
- if (argc != 0) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- start_progress_timer();
- get_all_cb_t cb = { 0 };
- get_all_datasets(&cb, verbose);
-
- if (cb.cb_used == 0) {
- if (options != NULL)
- free(options);
- return (0);
- }
-
-#ifdef illumos
- if (op == OP_SHARE) {
- sa_init_selective_arg_t sharearg;
- sharearg.zhandle_arr = cb.cb_handles;
- sharearg.zhandle_len = cb.cb_used;
- if ((ret = zfs_init_libshare_arg(g_zfs,
- SA_INIT_SHARE_API_SELECTIVE, &sharearg)) != SA_OK) {
- (void) fprintf(stderr, gettext(
- "Could not initialize libshare, %d"), ret);
- return (ret);
- }
- }
-#endif
- share_mount_state_t share_mount_state = { 0 };
- share_mount_state.sm_op = op;
- share_mount_state.sm_verbose = verbose;
- share_mount_state.sm_flags = flags;
- share_mount_state.sm_options = options;
- share_mount_state.sm_proto = protocol;
- share_mount_state.sm_total = cb.cb_used;
- pthread_mutex_init(&share_mount_state.sm_lock, NULL);
-
- /*
- * libshare isn't mt-safe, so only do the operation in parallel
- * if we're mounting.
- */
- zfs_foreach_mountpoint(g_zfs, cb.cb_handles, cb.cb_used,
- share_mount_one_cb, &share_mount_state, op == OP_MOUNT);
- ret = share_mount_state.sm_status;
-
- for (int i = 0; i < cb.cb_used; i++)
- zfs_close(cb.cb_handles[i]);
- free(cb.cb_handles);
- } else if (argc == 0) {
- struct mnttab entry;
-
- if ((op == OP_SHARE) || (options != NULL)) {
- (void) fprintf(stderr, gettext("missing filesystem "
- "argument (specify -a for all)\n"));
- usage(B_FALSE);
- }
-
- /*
- * When mount is given no arguments, go through /etc/mnttab and
- * display any active ZFS mounts. We hide any snapshots, since
- * they are controlled automatically.
- */
- rewind(mnttab_file);
- while (getmntent(mnttab_file, &entry) == 0) {
- if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0 ||
- strchr(entry.mnt_special, '@') != NULL)
- continue;
-
- (void) printf("%-30s %s\n", entry.mnt_special,
- entry.mnt_mountp);
- }
-
- } else {
- zfs_handle_t *zhp;
-
- if (argc > 1) {
- (void) fprintf(stderr,
- gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- if ((zhp = zfs_open(g_zfs, argv[0],
- ZFS_TYPE_FILESYSTEM)) == NULL) {
- ret = 1;
- } else {
- ret = share_mount_one(zhp, op, flags, NULL, B_TRUE,
- options);
- zfs_close(zhp);
- }
- }
-
- return (ret);
-}
-
-/*
- * zfs mount -a [nfs]
- * zfs mount filesystem
- *
- * Mount all filesystems, or mount the given filesystem.
- */
-static int
-zfs_do_mount(int argc, char **argv)
-{
- return (share_mount(OP_MOUNT, argc, argv));
-}
-
-/*
- * zfs share -a [nfs | smb]
- * zfs share filesystem
- *
- * Share all filesystems, or share the given filesystem.
- */
-static int
-zfs_do_share(int argc, char **argv)
-{
- return (share_mount(OP_SHARE, argc, argv));
-}
-
-typedef struct unshare_unmount_node {
- zfs_handle_t *un_zhp;
- char *un_mountp;
- uu_avl_node_t un_avlnode;
-} unshare_unmount_node_t;
-
-/* ARGSUSED */
-static int
-unshare_unmount_compare(const void *larg, const void *rarg, void *unused)
-{
- const unshare_unmount_node_t *l = larg;
- const unshare_unmount_node_t *r = rarg;
-
- return (strcmp(l->un_mountp, r->un_mountp));
-}
-
-/*
- * Convenience routine used by zfs_do_umount() and manual_unmount(). Given an
- * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem,
- * and unmount it appropriately.
- */
-static int
-unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
-{
- zfs_handle_t *zhp;
- int ret = 0;
- struct stat64 statbuf;
- struct extmnttab entry;
- const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount";
- ino_t path_inode;
-
- /*
- * Search for the path in /etc/mnttab. Rather than looking for the
- * 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.
- */
- if (stat64(path, &statbuf) != 0) {
- (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
- cmdname, path, strerror(errno));
- return (1);
- }
- path_inode = statbuf.st_ino;
-
- /*
- * Search for the given (major,minor) pair in the mount table.
- */
-#ifdef illumos
- rewind(mnttab_file);
- while ((ret = getextmntent(mnttab_file, &entry, 0)) == 0) {
- if (entry.mnt_major == major(statbuf.st_dev) &&
- entry.mnt_minor == minor(statbuf.st_dev))
- break;
- }
-#else
- {
- struct statfs sfs;
-
- if (statfs(path, &sfs) != 0) {
- (void) fprintf(stderr, "%s: %s\n", path,
- strerror(errno));
- ret = -1;
- }
- statfs2mnttab(&sfs, &entry);
- }
-#endif
- if (ret != 0) {
- if (op == OP_SHARE) {
- (void) fprintf(stderr, gettext("cannot %s '%s': not "
- "currently mounted\n"), cmdname, path);
- return (1);
- }
- (void) fprintf(stderr, gettext("warning: %s not in mnttab\n"),
- path);
- if ((ret = umount2(path, flags)) != 0)
- (void) fprintf(stderr, gettext("%s: %s\n"), path,
- strerror(errno));
- return (ret != 0);
- }
-
- if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) {
- (void) fprintf(stderr, gettext("cannot %s '%s': not a ZFS "
- "filesystem\n"), cmdname, path);
- return (1);
- }
-
- if ((zhp = zfs_open(g_zfs, entry.mnt_special,
- ZFS_TYPE_FILESYSTEM)) == NULL)
- return (1);
-
- ret = 1;
- if (stat64(entry.mnt_mountp, &statbuf) != 0) {
- (void) fprintf(stderr, gettext("cannot %s '%s': %s\n"),
- cmdname, path, strerror(errno));
- goto out;
- } else if (statbuf.st_ino != path_inode) {
- (void) fprintf(stderr, gettext("cannot "
- "%s '%s': not a mountpoint\n"), cmdname, path);
- goto out;
- }
-
- if (op == OP_SHARE) {
- char nfs_mnt_prop[ZFS_MAXPROPLEN];
- char smbshare_prop[ZFS_MAXPROPLEN];
-
- verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, nfs_mnt_prop,
- sizeof (nfs_mnt_prop), NULL, NULL, 0, B_FALSE) == 0);
- verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshare_prop,
- sizeof (smbshare_prop), NULL, NULL, 0, B_FALSE) == 0);
-
- if (strcmp(nfs_mnt_prop, "off") == 0 &&
- strcmp(smbshare_prop, "off") == 0) {
- (void) fprintf(stderr, gettext("cannot unshare "
- "'%s': legacy share\n"), path);
-#ifdef illumos
- (void) fprintf(stderr, gettext("use "
- "unshare(1M) to unshare this filesystem\n"));
-#endif
- } else if (!zfs_is_shared(zhp)) {
- (void) fprintf(stderr, gettext("cannot unshare '%s': "
- "not currently shared\n"), path);
- } else {
- ret = zfs_unshareall_bypath(zhp, path);
- }
- } else {
- char mtpt_prop[ZFS_MAXPROPLEN];
-
- verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mtpt_prop,
- sizeof (mtpt_prop), NULL, NULL, 0, B_FALSE) == 0);
-
- if (is_manual) {
- ret = zfs_unmount(zhp, NULL, flags);
- } else if (strcmp(mtpt_prop, "legacy") == 0) {
- (void) fprintf(stderr, gettext("cannot unmount "
- "'%s': legacy mountpoint\n"),
- zfs_get_name(zhp));
- (void) fprintf(stderr, gettext("use umount(8) "
- "to unmount this filesystem\n"));
- } else {
- ret = zfs_unmountall(zhp, flags);
- }
- }
-
-out:
- zfs_close(zhp);
-
- return (ret != 0);
-}
-
-/*
- * Generic callback for unsharing or unmounting a filesystem.
- */
-static int
-unshare_unmount(int op, int argc, char **argv)
-{
- int do_all = 0;
- int flags = 0;
- int ret = 0;
- int c;
- zfs_handle_t *zhp;
- char nfs_mnt_prop[ZFS_MAXPROPLEN];
- char sharesmb[ZFS_MAXPROPLEN];
-
- /* check options */
- while ((c = getopt(argc, argv, op == OP_SHARE ? "a" : "af")) != -1) {
- switch (c) {
- case 'a':
- do_all = 1;
- break;
- case 'f':
- flags = MS_FORCE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- 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
- * since we would have to linearly search /etc/mnttab for each
- * one. Instead, do one pass through /etc/mnttab looking for
- * 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.
- */
- struct mnttab entry;
- uu_avl_pool_t *pool;
- uu_avl_t *tree = NULL;
- unshare_unmount_node_t *node;
- uu_avl_index_t idx;
- uu_avl_walk_t *walk;
-
- if (argc != 0) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- if (((pool = uu_avl_pool_create("unmount_pool",
- sizeof (unshare_unmount_node_t),
- offsetof(unshare_unmount_node_t, un_avlnode),
- unshare_unmount_compare, UU_DEFAULT)) == NULL) ||
- ((tree = uu_avl_create(pool, NULL, UU_DEFAULT)) == NULL))
- nomem();
-
- rewind(mnttab_file);
- while (getmntent(mnttab_file, &entry) == 0) {
-
- /* ignore non-ZFS entries */
- if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
- continue;
-
- /* ignore snapshots */
- if (strchr(entry.mnt_special, '@') != NULL)
- continue;
-
- if ((zhp = zfs_open(g_zfs, entry.mnt_special,
- ZFS_TYPE_FILESYSTEM)) == NULL) {
- ret = 1;
- continue;
- }
-
- /*
- * Ignore datasets that are excluded/restricted by
- * parent pool name.
- */
- if (zpool_skip_pool(zfs_get_pool_name(zhp))) {
- zfs_close(zhp);
- continue;
- }
-
- switch (op) {
- case OP_SHARE:
- verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
- nfs_mnt_prop,
- sizeof (nfs_mnt_prop),
- NULL, NULL, 0, B_FALSE) == 0);
- if (strcmp(nfs_mnt_prop, "off") != 0)
- break;
- verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
- nfs_mnt_prop,
- sizeof (nfs_mnt_prop),
- NULL, NULL, 0, B_FALSE) == 0);
- if (strcmp(nfs_mnt_prop, "off") == 0)
- continue;
- break;
- case OP_MOUNT:
- /* Ignore legacy mounts */
- verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
- nfs_mnt_prop,
- sizeof (nfs_mnt_prop),
- NULL, NULL, 0, B_FALSE) == 0);
- if (strcmp(nfs_mnt_prop, "legacy") == 0)
- continue;
- /* Ignore canmount=noauto mounts */
- if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) ==
- ZFS_CANMOUNT_NOAUTO)
- continue;
- default:
- break;
- }
-
- node = safe_malloc(sizeof (unshare_unmount_node_t));
- node->un_zhp = zhp;
- node->un_mountp = safe_strdup(entry.mnt_mountp);
-
- uu_avl_node_init(node, &node->un_avlnode, pool);
-
- if (uu_avl_find(tree, node, NULL, &idx) == NULL) {
- uu_avl_insert(tree, node, idx);
- } else {
- zfs_close(node->un_zhp);
- free(node->un_mountp);
- free(node);
- }
- }
-
- /*
- * Walk the AVL tree in reverse, unmounting each filesystem and
- * removing it from the AVL tree in the process.
- */
- if ((walk = uu_avl_walk_start(tree,
- UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL)
- nomem();
-
- while ((node = uu_avl_walk_next(walk)) != NULL) {
- uu_avl_remove(tree, node);
-
- switch (op) {
- case OP_SHARE:
- if (zfs_unshareall_bypath(node->un_zhp,
- node->un_mountp) != 0)
- ret = 1;
- break;
-
- case OP_MOUNT:
- if (zfs_unmount(node->un_zhp,
- node->un_mountp, flags) != 0)
- ret = 1;
- break;
- }
-
- zfs_close(node->un_zhp);
- free(node->un_mountp);
- free(node);
- }
-
- uu_avl_walk_end(walk);
- uu_avl_destroy(tree);
- uu_avl_pool_destroy(pool);
-
- } else {
- if (argc != 1) {
- if (argc == 0)
- (void) fprintf(stderr,
- gettext("missing filesystem argument\n"));
- else
- (void) fprintf(stderr,
- gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- /*
- * 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 (unshare_unmount_path(op, argv[0],
- flags, B_FALSE));
-
- if ((zhp = zfs_open(g_zfs, argv[0],
- ZFS_TYPE_FILESYSTEM)) == NULL)
- return (1);
-
- verify(zfs_prop_get(zhp, op == OP_SHARE ?
- ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT,
- nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL,
- NULL, 0, B_FALSE) == 0);
-
- switch (op) {
- case OP_SHARE:
- verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
- nfs_mnt_prop,
- sizeof (nfs_mnt_prop),
- NULL, NULL, 0, B_FALSE) == 0);
- verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
- sharesmb, sizeof (sharesmb), NULL, NULL,
- 0, B_FALSE) == 0);
-
- if (strcmp(nfs_mnt_prop, "off") == 0 &&
- strcmp(sharesmb, "off") == 0) {
- (void) fprintf(stderr, gettext("cannot "
- "unshare '%s': legacy share\n"),
- zfs_get_name(zhp));
-#ifdef illumos
- (void) fprintf(stderr, gettext("use "
- "unshare(1M) to unshare this "
- "filesystem\n"));
-#endif
- ret = 1;
- } else if (!zfs_is_shared(zhp)) {
- (void) fprintf(stderr, gettext("cannot "
- "unshare '%s': not currently "
- "shared\n"), zfs_get_name(zhp));
- ret = 1;
- } else if (zfs_unshareall(zhp) != 0) {
- ret = 1;
- }
- break;
-
- case OP_MOUNT:
- if (strcmp(nfs_mnt_prop, "legacy") == 0) {
- (void) fprintf(stderr, gettext("cannot "
- "unmount '%s': legacy "
- "mountpoint\n"), zfs_get_name(zhp));
- (void) fprintf(stderr, gettext("use "
- "umount(8) to unmount this "
- "filesystem\n"));
- ret = 1;
- } else if (!zfs_is_mounted(zhp, NULL)) {
- (void) fprintf(stderr, gettext("cannot "
- "unmount '%s': not currently "
- "mounted\n"),
- zfs_get_name(zhp));
- ret = 1;
- } else if (zfs_unmountall(zhp, flags) != 0) {
- ret = 1;
- }
- break;
- }
-
- zfs_close(zhp);
- }
-
- return (ret);
-}
-
-/*
- * zfs unmount -a
- * zfs unmount filesystem
- *
- * Unmount all filesystems, or a specific ZFS filesystem.
- */
-static int
-zfs_do_unmount(int argc, char **argv)
-{
- return (unshare_unmount(OP_MOUNT, argc, argv));
-}
-
-/*
- * zfs unshare -a
- * zfs unshare filesystem
- *
- * Unshare all filesystems, or a specific ZFS filesystem.
- */
-static int
-zfs_do_unshare(int argc, char **argv)
-{
- return (unshare_unmount(OP_SHARE, argc, argv));
-}
-
-/*
- * Attach/detach the given dataset to/from the given jail
- */
-/* ARGSUSED */
-static int
-do_jail(int argc, char **argv, int attach)
-{
- zfs_handle_t *zhp;
- int jailid, ret;
-
- /* check number of arguments */
- if (argc < 3) {
- (void) fprintf(stderr, gettext("missing argument(s)\n"));
- usage(B_FALSE);
- }
- if (argc > 3) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- jailid = jail_getid(argv[1]);
- if (jailid < 0) {
- (void) fprintf(stderr, gettext("invalid jail id or name\n"));
- usage(B_FALSE);
- }
-
- zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM);
- if (zhp == NULL)
- return (1);
-
- ret = (zfs_jail(zhp, jailid, attach) != 0);
-
- zfs_close(zhp);
- return (ret);
-}
-
-/*
- * zfs jail jailid filesystem
- *
- * Attach the given dataset to the given jail
- */
-/* ARGSUSED */
-static int
-zfs_do_jail(int argc, char **argv)
-{
-
- return (do_jail(argc, argv, 1));
-}
-
-/*
- * zfs unjail jailid filesystem
- *
- * Detach the given dataset from the given jail
- */
-/* ARGSUSED */
-static int
-zfs_do_unjail(int argc, char **argv)
-{
-
- return (do_jail(argc, argv, 0));
-}
-
-/*
- * Called when invoked as /etc/fs/zfs/mount. Do the mount if the mountpoint is
- * 'legacy'. Otherwise, complain that use should be using 'zfs mount'.
- */
-static int
-manual_mount(int argc, char **argv)
-{
- zfs_handle_t *zhp;
- char mountpoint[ZFS_MAXPROPLEN];
- char mntopts[MNT_LINE_MAX] = { '\0' };
- int ret = 0;
- int c;
- int flags = 0;
- char *dataset, *path;
-
- /* check options */
- while ((c = getopt(argc, argv, ":mo:O")) != -1) {
- switch (c) {
- case 'o':
- (void) strlcpy(mntopts, optarg, sizeof (mntopts));
- break;
- case 'O':
- flags |= MS_OVERLAY;
- break;
- case 'm':
- flags |= MS_NOMNTTAB;
- break;
- case ':':
- (void) fprintf(stderr, gettext("missing argument for "
- "'%c' option\n"), optopt);
- usage(B_FALSE);
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- (void) fprintf(stderr, gettext("usage: mount [-o opts] "
- "<path>\n"));
- return (2);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check that we only have two arguments */
- if (argc != 2) {
- if (argc == 0)
- (void) fprintf(stderr, gettext("missing dataset "
- "argument\n"));
- else if (argc == 1)
- (void) fprintf(stderr,
- gettext("missing mountpoint argument\n"));
- else
- (void) fprintf(stderr, gettext("too many arguments\n"));
- (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n");
- return (2);
- }
-
- dataset = argv[0];
- path = argv[1];
-
- /* try to open the dataset */
- if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL)
- return (1);
-
- (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
- sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
-
- /* check for legacy mountpoint and complain appropriately */
- ret = 0;
- if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
- if (zmount(dataset, path, flags, MNTTYPE_ZFS,
- NULL, 0, mntopts, sizeof (mntopts)) != 0) {
- (void) fprintf(stderr, gettext("mount failed: %s\n"),
- strerror(errno));
- ret = 1;
- }
- } else {
- (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
- "mounted using 'mount -t zfs'\n"), dataset);
- (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' "
- "instead.\n"), path);
- (void) fprintf(stderr, gettext("If you must use 'mount -t zfs' "
- "or /etc/fstab, use 'zfs set mountpoint=legacy'.\n"));
- (void) fprintf(stderr, gettext("See zfs(8) for more "
- "information.\n"));
- ret = 1;
- }
-
- return (ret);
-}
-
-/*
- * Called when invoked as /etc/fs/zfs/umount. Unlike a manual mount, we allow
- * unmounts of non-legacy filesystems, as this is the dominant administrative
- * interface.
- */
-static int
-manual_unmount(int argc, char **argv)
-{
- int flags = 0;
- int c;
-
- /* check options */
- while ((c = getopt(argc, argv, "f")) != -1) {
- switch (c) {
- case 'f':
- flags = MS_FORCE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- (void) fprintf(stderr, gettext("usage: unmount [-f] "
- "<path>\n"));
- return (2);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check arguments */
- if (argc != 1) {
- if (argc == 0)
- (void) fprintf(stderr, gettext("missing path "
- "argument\n"));
- else
- (void) fprintf(stderr, gettext("too many arguments\n"));
- (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n"));
- return (2);
- }
-
- return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE));
-}
-
-static int
-find_command_idx(char *command, int *idx)
-{
- int i;
-
- for (i = 0; i < NCOMMAND; i++) {
- if (command_table[i].name == NULL)
- continue;
-
- if (strcmp(command, command_table[i].name) == 0) {
- *idx = i;
- return (0);
- }
- }
- return (1);
-}
-
-static int
-zfs_do_diff(int argc, char **argv)
-{
- zfs_handle_t *zhp;
- int flags = 0;
- char *tosnap = NULL;
- char *fromsnap = NULL;
- char *atp, *copy;
- int err = 0;
- int c;
-
- while ((c = getopt(argc, argv, "FHt")) != -1) {
- switch (c) {
- case 'F':
- flags |= ZFS_DIFF_CLASSIFY;
- break;
- case 'H':
- flags |= ZFS_DIFF_PARSEABLE;
- break;
- case 't':
- flags |= ZFS_DIFF_TIMESTAMP;
- break;
- default:
- (void) fprintf(stderr,
- gettext("invalid option '%c'\n"), optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc < 1) {
- (void) fprintf(stderr,
- gettext("must provide at least one snapshot name\n"));
- usage(B_FALSE);
- }
-
- if (argc > 2) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- fromsnap = argv[0];
- tosnap = (argc == 2) ? argv[1] : NULL;
-
- copy = NULL;
- if (*fromsnap != '@')
- copy = strdup(fromsnap);
- else if (tosnap)
- copy = strdup(tosnap);
- if (copy == NULL)
- usage(B_FALSE);
-
- if ((atp = strchr(copy, '@')) != NULL)
- *atp = '\0';
-
- if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL)
- return (1);
-
- free(copy);
-
- /*
- * Ignore SIGPIPE so that the library can give us
- * information on any failure
- */
- (void) sigignore(SIGPIPE);
-
- err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags);
-
- zfs_close(zhp);
-
- return (err != 0);
-}
-
-/*
- * zfs remap <filesystem | volume>
- *
- * Remap the indirect blocks in the given fileystem or volume.
- */
-static int
-zfs_do_remap(int argc, char **argv)
-{
- const char *fsname;
- int err = 0;
- int c;
-
- /* check options */
- while ((c = getopt(argc, argv, "")) != -1) {
- switch (c) {
- case '?':
- (void) fprintf(stderr,
- gettext("invalid option '%c'\n"), optopt);
- usage(B_FALSE);
- }
- }
-
- if (argc != 2) {
- (void) fprintf(stderr, gettext("wrong number of arguments\n"));
- usage(B_FALSE);
- }
-
- fsname = argv[1];
- err = zfs_remap_indirects(g_zfs, fsname);
-
- return (err);
-}
-
-/*
- * zfs bookmark <fs@snap> <fs#bmark>
- *
- * Creates a bookmark with the given name from the given snapshot.
- */
-static int
-zfs_do_bookmark(int argc, char **argv)
-{
- char snapname[ZFS_MAX_DATASET_NAME_LEN];
- zfs_handle_t *zhp;
- nvlist_t *nvl;
- int ret = 0;
- int c;
-
- /* check options */
- while ((c = getopt(argc, argv, "")) != -1) {
- switch (c) {
- case '?':
- (void) fprintf(stderr,
- gettext("invalid option '%c'\n"), optopt);
- goto usage;
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check number of arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing snapshot argument\n"));
- goto usage;
- }
- if (argc < 2) {
- (void) fprintf(stderr, gettext("missing bookmark argument\n"));
- goto usage;
- }
-
- if (strchr(argv[1], '#') == NULL) {
- (void) fprintf(stderr,
- gettext("invalid bookmark name '%s' -- "
- "must contain a '#'\n"), argv[1]);
- goto usage;
- }
-
- if (argv[0][0] == '@') {
- /*
- * Snapshot name begins with @.
- * Default to same fs as bookmark.
- */
- (void) strncpy(snapname, argv[1], sizeof (snapname));
- *strchr(snapname, '#') = '\0';
- (void) strlcat(snapname, argv[0], sizeof (snapname));
- } else {
- (void) strncpy(snapname, argv[0], sizeof (snapname));
- }
- zhp = zfs_open(g_zfs, snapname, ZFS_TYPE_SNAPSHOT);
- if (zhp == NULL)
- goto usage;
- zfs_close(zhp);
-
-
- nvl = fnvlist_alloc();
- fnvlist_add_string(nvl, argv[1], snapname);
- ret = lzc_bookmark(nvl, NULL);
- fnvlist_free(nvl);
-
- if (ret != 0) {
- const char *err_msg = NULL;
- char errbuf[1024];
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot create bookmark '%s'"), argv[1]);
-
- switch (ret) {
- case EXDEV:
- err_msg = "bookmark is in a different pool";
- break;
- case EEXIST:
- err_msg = "bookmark exists";
- break;
- case EINVAL:
- err_msg = "invalid argument";
- break;
- case ENOTSUP:
- err_msg = "bookmark feature not enabled";
- break;
- case ENOSPC:
- err_msg = "out of space";
- break;
- default:
- (void) zfs_standard_error(g_zfs, ret, errbuf);
- break;
- }
- if (err_msg != NULL) {
- (void) fprintf(stderr, "%s: %s\n", errbuf,
- dgettext(TEXT_DOMAIN, err_msg));
- }
- }
-
- return (ret != 0);
-
-usage:
- usage(B_FALSE);
- return (-1);
-}
-
-static int
-zfs_do_channel_program(int argc, char **argv)
-{
- int ret, fd;
- char c;
- char *progbuf, *filename, *poolname;
- size_t progsize, progread;
- nvlist_t *outnvl = NULL;
- uint64_t instrlimit = ZCP_DEFAULT_INSTRLIMIT;
- uint64_t memlimit = ZCP_DEFAULT_MEMLIMIT;
- boolean_t sync_flag = B_TRUE, json_output = B_FALSE;
- zpool_handle_t *zhp;
-
- /* check options */
- while (-1 !=
- (c = getopt(argc, argv, "jnt:(instr-limit)m:(memory-limit)"))) {
- switch (c) {
- case 't':
- case 'm': {
- uint64_t arg;
- char *endp;
-
- errno = 0;
- arg = strtoull(optarg, &endp, 0);
- if (errno != 0 || *endp != '\0') {
- (void) fprintf(stderr, gettext(
- "invalid argument "
- "'%s': expected integer\n"), optarg);
- goto usage;
- }
-
- if (c == 't') {
- if (arg > ZCP_MAX_INSTRLIMIT || arg == 0) {
- (void) fprintf(stderr, gettext(
- "Invalid instruction limit: "
- "%s\n"), optarg);
- return (1);
- } else {
- instrlimit = arg;
- }
- } else {
- ASSERT3U(c, ==, 'm');
- if (arg > ZCP_MAX_MEMLIMIT || arg == 0) {
- (void) fprintf(stderr, gettext(
- "Invalid memory limit: "
- "%s\n"), optarg);
- return (1);
- } else {
- memlimit = arg;
- }
- }
- break;
- }
- case 'n': {
- sync_flag = B_FALSE;
- break;
- }
- case 'j': {
- json_output = B_TRUE;
- break;
- }
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- goto usage;
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc < 2) {
- (void) fprintf(stderr,
- gettext("invalid number of arguments\n"));
- goto usage;
- }
-
- poolname = argv[0];
- filename = argv[1];
- if (strcmp(filename, "-") == 0) {
- fd = 0;
- filename = "standard input";
- } else if ((fd = open(filename, O_RDONLY)) < 0) {
- (void) fprintf(stderr, gettext("cannot open '%s': %s\n"),
- filename, strerror(errno));
- return (1);
- }
-
- if ((zhp = zpool_open(g_zfs, poolname)) == NULL) {
- (void) fprintf(stderr, gettext("cannot open pool '%s'"),
- poolname);
- return (1);
- }
- zpool_close(zhp);
-
- /*
- * Read in the channel program, expanding the program buffer as
- * necessary.
- */
- progread = 0;
- progsize = 1024;
- progbuf = safe_malloc(progsize);
- do {
- ret = read(fd, progbuf + progread, progsize - progread);
- progread += ret;
- if (progread == progsize && ret > 0) {
- progsize *= 2;
- progbuf = safe_realloc(progbuf, progsize);
- }
- } while (ret > 0);
-
- if (fd != 0)
- (void) close(fd);
- if (ret < 0) {
- free(progbuf);
- (void) fprintf(stderr,
- gettext("cannot read '%s': %s\n"),
- filename, strerror(errno));
- return (1);
- }
- progbuf[progread] = '\0';
-
- /*
- * Any remaining arguments are passed as arguments to the lua script as
- * a string array:
- * {
- * "argv" -> [ "arg 1", ... "arg n" ],
- * }
- */
- nvlist_t *argnvl = fnvlist_alloc();
- fnvlist_add_string_array(argnvl, ZCP_ARG_CLIARGV, argv + 2, argc - 2);
-
- if (sync_flag) {
- ret = lzc_channel_program(poolname, progbuf,
- instrlimit, memlimit, argnvl, &outnvl);
- } else {
- ret = lzc_channel_program_nosync(poolname, progbuf,
- instrlimit, memlimit, argnvl, &outnvl);
- }
-
- if (ret != 0) {
- /*
- * On error, report the error message handed back by lua if one
- * exists. Otherwise, generate an appropriate error message,
- * falling back on strerror() for an unexpected return code.
- */
- char *errstring = NULL;
- const char *msg = gettext("Channel program execution failed");
- if (outnvl != NULL && nvlist_exists(outnvl, ZCP_RET_ERROR)) {
- (void) nvlist_lookup_string(outnvl,
- ZCP_RET_ERROR, &errstring);
- if (errstring == NULL)
- errstring = strerror(ret);
- } else {
- switch (ret) {
- case EINVAL:
- errstring =
- "Invalid instruction or memory limit.";
- break;
- case ENOMEM:
- errstring = "Return value too large.";
- break;
- case ENOSPC:
- errstring = "Memory limit exhausted.";
- break;
-#ifdef illumos
- case ETIME:
-#else
- case ETIMEDOUT:
-#endif
- errstring = "Timed out.";
- break;
- case EPERM:
- errstring = "Permission denied. Channel "
- "programs must be run as root.";
- break;
- default:
- (void) zfs_standard_error(g_zfs, ret, msg);
- }
- }
- if (errstring != NULL)
- (void) fprintf(stderr, "%s:\n%s\n", msg, errstring);
- } else {
- if (json_output) {
- (void) nvlist_print_json(stdout, outnvl);
- } else if (nvlist_empty(outnvl)) {
- (void) fprintf(stdout, gettext("Channel program fully "
- "executed and did not produce output.\n"));
- } else {
- (void) fprintf(stdout, gettext("Channel program fully "
- "executed and produced output:\n"));
- dump_nvlist(outnvl, 4);
- }
- }
-
- free(progbuf);
- fnvlist_free(outnvl);
- fnvlist_free(argnvl);
- return (ret != 0);
-
-usage:
- usage(B_FALSE);
- return (-1);
-}
-
-int
-main(int argc, char **argv)
-{
- int ret = 0;
- int i;
- char *progname;
- char *cmdname;
-
- (void) setlocale(LC_ALL, "");
- (void) textdomain(TEXT_DOMAIN);
-
- opterr = 0;
-
- if ((g_zfs = libzfs_init()) == NULL) {
- (void) fprintf(stderr, gettext("internal error: failed to "
- "initialize ZFS library\n"));
- return (1);
- }
-
- zfs_save_arguments(argc, argv, history_str, sizeof (history_str));
-
- libzfs_print_on_error(g_zfs, B_TRUE);
-
- if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) {
- (void) fprintf(stderr, gettext("internal error: unable to "
- "open %s\n"), MNTTAB);
- return (1);
- }
-
- /*
- * This command also doubles as the /etc/fs mount and unmount program.
- * Determine if we should take this behavior based on argv[0].
- */
- progname = basename(argv[0]);
- if (strcmp(progname, "mount") == 0) {
- ret = manual_mount(argc, argv);
- } else if (strcmp(progname, "umount") == 0) {
- ret = manual_unmount(argc, argv);
- } else {
- /*
- * Make sure the user has specified some command.
- */
- if (argc < 2) {
- (void) fprintf(stderr, gettext("missing command\n"));
- usage(B_FALSE);
- }
-
- cmdname = argv[1];
-
- /*
- * The 'umount' command is an alias for 'unmount'
- */
- if (strcmp(cmdname, "umount") == 0)
- cmdname = "unmount";
-
- /*
- * The 'recv' command is an alias for 'receive'
- */
- if (strcmp(cmdname, "recv") == 0)
- cmdname = "receive";
-
- /*
- * The 'snap' command is an alias for 'snapshot'
- */
- if (strcmp(cmdname, "snap") == 0)
- cmdname = "snapshot";
-
- /*
- * Special case '-?'
- */
- if (strcmp(cmdname, "-?") == 0)
- usage(B_TRUE);
-
- /*
- * Run the appropriate command.
- */
- libzfs_mnttab_cache(g_zfs, B_TRUE);
- if (find_command_idx(cmdname, &i) == 0) {
- current_command = &command_table[i];
- ret = command_table[i].func(argc - 1, argv + 1);
- } else if (strchr(cmdname, '=') != NULL) {
- verify(find_command_idx("set", &i) == 0);
- current_command = &command_table[i];
- ret = command_table[i].func(argc, argv);
- } else {
- (void) fprintf(stderr, gettext("unrecognized "
- "command '%s'\n"), cmdname);
- usage(B_FALSE);
- }
- libzfs_mnttab_cache(g_zfs, B_FALSE);
- }
-
- (void) fclose(mnttab_file);
-
- if (ret == 0 && log_history)
- (void) zpool_log_history(g_zfs, history_str);
-
- libzfs_fini(g_zfs);
-
- /*
- * The 'ZFS_ABORT' environment variable causes us to dump core on exit
- * for the purposes of running ::findleaks.
- */
- if (getenv("ZFS_ABORT") != NULL) {
- (void) printf("dumping core by request\n");
- abort();
- }
-
- return (ret);
-}
diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs_util.h b/cddl/contrib/opensolaris/cmd/zfs/zfs_util.h
deleted file mode 100644
index a56af59adb15..000000000000
--- a/cddl/contrib/opensolaris/cmd/zfs/zfs_util.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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
- * or http://www.opensolaris.org/os/licensing.
- * 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 (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- */
-
-#ifndef _ZFS_UTIL_H
-#define _ZFS_UTIL_H
-
-#include <libzfs.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void * safe_malloc(size_t size);
-void nomem(void);
-extern libzfs_handle_t *g_zfs;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ZFS_UTIL_H */