aboutsummaryrefslogtreecommitdiff
path: root/cddl/contrib
diff options
context:
space:
mode:
authorMatt Macy <mmacy@FreeBSD.org>2020-08-25 02:21:27 +0000
committerMatt Macy <mmacy@FreeBSD.org>2020-08-25 02:21:27 +0000
commit9e5787d2284e187abb5b654d924394a65772e004 (patch)
tree2ebf833af6b1953d4a683e2da830fe87bf3435e1 /cddl/contrib
parent22df1ffd812f0395cdb7c0b1edae1f67b991562a (diff)
downloadsrc-9e5787d2284e187abb5b654d924394a65772e004.tar.gz
src-9e5787d2284e187abb5b654d924394a65772e004.zip
Merge OpenZFS support in to HEAD.
The primary benefit is maintaining a completely shared code base with the community allowing FreeBSD to receive new features sooner and with less effort. I would advise against doing 'zpool upgrade' or creating indispensable pools using new features until this change has had a month+ to soak. Work on merging FreeBSD support in to what was at the time "ZFS on Linux" began in August 2018. I first publicly proposed transitioning FreeBSD to (new) OpenZFS on December 18th, 2018. FreeBSD support in OpenZFS was finally completed in December 2019. A CFT for downstreaming OpenZFS support in to FreeBSD was first issued on July 8th. All issues that were reported have been addressed or, for a couple of less critical matters there are pull requests in progress with OpenZFS. iXsystems has tested and dogfooded extensively internally. The TrueNAS 12 release is based on OpenZFS with some additional features that have not yet made it upstream. Improvements include: project quotas, encrypted datasets, allocation classes, vectorized raidz, vectorized checksums, various command line improvements, zstd compression. Thanks to those who have helped along the way: Ryan Moeller, Allan Jude, Zack Welch, and many others. Sponsored by: iXsystems, Inc. Differential Revision: https://reviews.freebsd.org/D25872
Notes
Notes: svn path=/head/; revision=364746
Diffstat (limited to 'cddl/contrib')
-rw-r--r--cddl/contrib/opensolaris/cmd/lockstat/sym.c6
-rw-r--r--cddl/contrib/opensolaris/cmd/zdb/zdb.8414
-rw-r--r--cddl/contrib/opensolaris/cmd/zdb/zdb.c5749
-rw-r--r--cddl/contrib/opensolaris/cmd/zdb/zdb.h33
-rw-r--r--cddl/contrib/opensolaris/cmd/zdb/zdb_il.c424
-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
-rw-r--r--cddl/contrib/opensolaris/cmd/zhack/zhack.c535
-rw-r--r--cddl/contrib/opensolaris/cmd/zinject/translate.c492
-rw-r--r--cddl/contrib/opensolaris/cmd/zinject/zinject.c1093
-rw-r--r--cddl/contrib/opensolaris/cmd/zinject/zinject.h70
-rw-r--r--cddl/contrib/opensolaris/cmd/zlook/zlook.c411
-rw-r--r--cddl/contrib/opensolaris/cmd/zpool/zpool-features.7674
-rw-r--r--cddl/contrib/opensolaris/cmd/zpool/zpool.82485
-rw-r--r--cddl/contrib/opensolaris/cmd/zpool/zpool_iter.c255
-rw-r--r--cddl/contrib/opensolaris/cmd/zpool/zpool_main.c6742
-rw-r--r--cddl/contrib/opensolaris/cmd/zpool/zpool_util.c86
-rw-r--r--cddl/contrib/opensolaris/cmd/zpool/zpool_util.h73
-rw-r--r--cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c1729
-rw-r--r--cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.176
-rw-r--r--cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.c644
-rw-r--r--cddl/contrib/opensolaris/cmd/ztest/ztest.c7135
-rw-r--r--cddl/contrib/opensolaris/lib/libdtrace/common/drti.c1
-rw-r--r--cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c1
-rw-r--r--cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c1
-rw-r--r--cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.c11
-rw-r--r--cddl/contrib/opensolaris/lib/libnvpair/libnvpair.c1286
-rw-r--r--cddl/contrib/opensolaris/lib/libnvpair/libnvpair.h196
-rw-r--r--cddl/contrib/opensolaris/lib/libnvpair/nvpair_alloc_system.c59
-rw-r--r--cddl/contrib/opensolaris/lib/libnvpair/nvpair_json.c406
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/libuutil.h391
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/libuutil_common.h35
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/libuutil_impl.h181
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/uu_alloc.c135
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/uu_avl.c570
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/uu_dprintf.c128
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/uu_ident.c122
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/uu_list.c718
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/uu_misc.c277
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/uu_open.c70
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/uu_pname.c205
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/uu_string.c56
-rw-r--r--cddl/contrib/opensolaris/lib/libuutil/common/uu_strtoint.c300
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h894
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_changelist.c736
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c121
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.h44
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_config.c469
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c5284
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_diff.c834
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_fru.c452
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h228
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c1929
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c546
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c1734
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c4669
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c3924
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_status.c511
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c1661
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c1234
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h114
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core_compat.c189
-rw-r--r--cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core_compat.h47
-rw-r--r--cddl/contrib/opensolaris/lib/libzpool/common/kernel.c1238
-rw-r--r--cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h838
-rw-r--r--cddl/contrib/opensolaris/lib/libzpool/common/taskq.c353
-rw-r--r--cddl/contrib/opensolaris/lib/libzpool/common/util.c196
-rw-r--r--cddl/contrib/opensolaris/lib/libzpool/common/zfs.d36
-rw-r--r--cddl/contrib/opensolaris/tools/ctf/cvt/util.c1
73 files changed, 18 insertions, 75256 deletions
diff --git a/cddl/contrib/opensolaris/cmd/lockstat/sym.c b/cddl/contrib/opensolaris/cmd/lockstat/sym.c
index f2987a028e74..b5366c566857 100644
--- a/cddl/contrib/opensolaris/cmd/lockstat/sym.c
+++ b/cddl/contrib/opensolaris/cmd/lockstat/sym.c
@@ -54,6 +54,7 @@
#endif
#include <sys/cpuvar.h>
+
typedef struct syment {
uintptr_t addr;
char *name;
@@ -72,6 +73,11 @@ static char maxsymname[64];
#endif
#endif
+#define __sElfN(x) typedef __CONCAT(__CONCAT(__CONCAT(Elf,__ELF_WORD_SIZE),_),x) x
+__sElfN(Sym);
+__sElfN(Shdr);
+#define elf_getshdr __elfN(getshdr)
+
static void
add_symbol(char *name, uintptr_t addr, size_t size)
{
diff --git a/cddl/contrib/opensolaris/cmd/zdb/zdb.8 b/cddl/contrib/opensolaris/cmd/zdb/zdb.8
deleted file mode 100644
index e60c56c7c199..000000000000
--- a/cddl/contrib/opensolaris/cmd/zdb/zdb.8
+++ /dev/null
@@ -1,414 +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 2012, Richard Lowe.
-.\" Copyright (c) 2012, 2018 by Delphix. All rights reserved.
-.\" Copyright 2017 Nexenta Systems, Inc.
-.\"
-.Dd February 25, 2020
-.Dt ZDB 8
-.Os
-.Sh NAME
-.Nm zdb
-.Nd display zpool debugging and consistency information
-.Sh SYNOPSIS
-.Nm
-.Op Fl AbcdDFGhikLMPsvX
-.Op Fl e Oo Fl V Oc Op Fl p Ar path ...
-.Op Fl I Ar inflight I/Os
-.Oo Fl o Ar var Ns = Ns Ar value Oc Ns ...
-.Op Fl t Ar txg
-.Op Fl U Ar cache
-.Op Fl x Ar dumpdir
-.Op Ar poolname Op Ar object ...
-.Nm
-.Op Fl AdiPv
-.Op Fl e Oo Fl V Oc Op Fl p Ar path ...
-.Op Fl U Ar cache
-.Ar dataset Op Ar object ...
-.Nm
-.Fl C
-.Op Fl A
-.Op Fl U Ar cache
-.Nm
-.Fl E
-.Op Fl A
-.Ar word0 Ns \&: Ns Ar word1 Ns :...: Ns Ar word15
-.Nm
-.Fl l
-.Op Fl Aqu
-.Ar device
-.Nm
-.Fl m
-.Op Fl AFLPX
-.Op Fl e Oo Fl V Oc Op Fl p Ar path ...
-.Op Fl t Ar txg
-.Op Fl U Ar cache
-.Ar poolname Op Ar vdev Op Ar metaslab ...
-.Nm
-.Fl O
-.Ar dataset path
-.Nm
-.Fl R
-.Op Fl A
-.Op Fl e Oo Fl V Oc Op Fl p Ar path ...
-.Op Fl U Ar cache
-.Ar poolname vdev Ns \&: Ns Ar offset Ns \&: Ns Ar size Ns Op : Ns Ar flags
-.Nm
-.Fl S
-.Op Fl AP
-.Op Fl e Oo Fl V Oc Op Fl p Ar path ...
-.Op Fl U Ar cache
-.Ar poolname
-.Sh DESCRIPTION
-The
-.Nm
-utility displays information about a ZFS pool useful for debugging and performs
-some amount of consistency checking.
-It is a not a general purpose tool and options
-.Pq and facilities
-may change.
-This is neither a
-.Xr fsck 8
-nor an
-.Xr fsdb 8
-utility.
-.Pp
-The output of this command in general reflects the on-disk structure of a ZFS
-pool, and is inherently unstable.
-The precise output of most invocations is not documented, a knowledge of ZFS
-internals is assumed.
-.Pp
-If the
-.Ar dataset
-argument does not contain any
-.Qq Sy /
-or
-.Qq Sy @
-characters, it is interpreted as a pool name.
-The root dataset can be specified as
-.Ar pool Ns /
-.Pq pool name followed by a slash .
-.Pp
-When operating on an imported and active pool it is possible, though unlikely,
-that zdb may interpret inconsistent pool data and behave erratically.
-.Sh OPTIONS
-Display options:
-.Bl -tag -width Ds
-.It Fl b
-Display statistics regarding the number, size
-.Pq logical, physical and allocated
-and deduplication of blocks.
-.It Fl c
-Verify the checksum of all metadata blocks while printing block statistics
-.Po see
-.Fl b
-.Pc .
-.Pp
-If specified multiple times, verify the checksums of all blocks.
-.It Fl C
-Display information about the configuration.
-If specified with no other options, instead display information about the cache
-file
-.Pq Pa /boot/zfs/zpool.cache .
-To specify the cache file to display, see
-.Fl U .
-.Pp
-If specified multiple times, and a pool name is also specified display both the
-cached configuration and the on-disk configuration.
-If specified multiple times with
-.Fl e
-also display the configuration that would be used were the pool to be imported.
-.It Fl d
-Display information about datasets.
-Specified once, displays basic dataset information: ID, create transaction,
-size, and object count.
-.Pp
-If specified multiple times provides greater and greater verbosity.
-.Pp
-If object IDs are specified, display information about those specific objects
-only.
-.It Fl D
-Display deduplication statistics, including the deduplication ratio
-.Pq Sy dedup ,
-compression ratio
-.Pq Sy compress ,
-inflation due to the zfs copies property
-.Pq Sy copies ,
-and an overall effective ratio
-.Pq Sy dedup No * Sy compress No / Sy copies .
-.It Fl DD
-Display a histogram of deduplication statistics, showing the allocated
-.Pq physically present on disk
-and referenced
-.Pq logically referenced in the pool
-block counts and sizes by reference count.
-.It Fl DDD
-Display the statistics independently for each deduplication table.
-.It Fl DDDD
-Dump the contents of the deduplication tables describing duplicate blocks.
-.It Fl DDDDD
-Also dump the contents of the deduplication tables describing unique blocks.
-.It Fl E Ar word0 Ns \&: Ns Ar word1 Ns :...: Ns Ar word15
-Decode and display block from an embedded block pointer specified by the
-.Ar word
-arguments.
-.It Fl h
-Display pool history similar to
-.Nm zpool Cm history ,
-but include internal changes, transaction, and dataset information.
-.It Fl i
-Display information about intent log
-.Pq ZIL
-entries relating to each dataset.
-If specified multiple times, display counts of each intent log transaction type.
-.It Fl k
-Examine the checkpointed state of the pool.
-Note, the on disk format of the pool is not reverted to the checkpointed state.
-.It Fl l Ar device
-Read the vdev labels from the specified device.
-.Nm Fl l
-will return 0 if valid label was found, 1 if error occurred, and 2 if no valid
-labels were found.
-.Pp
-If the
-.Fl q
-option is also specified, don't print the labels.
-.Pp
-If the
-.Fl u
-option is also specified, also display the uberblocks on this device.
-.It Fl L
-Disable leak detection and the loading of space maps.
-By default,
-.Nm
-verifies that all non-free blocks are referenced, which can be very expensive.
-.It Fl m
-Display the offset, spacemap, and free space of each metaslab.
-.It Fl mm
-Also display information about the on-disk free space histogram associated with
-each metaslab.
-.It Fl mmm
-Display the maximum contiguous free space, the in-core free space histogram, and
-the percentage of free space in each space map.
-.It Fl mmmm
-Display every spacemap record.
-.It Fl M
-Display the offset, spacemap, and free space of each metaslab.
-.It Fl MM
-Also display information about the maximum contiguous free space and the
-percentage of free space in each space map.
-.It Fl MMM
-Display every spacemap record.
-.It Fl O Ar dataset path
-Look up the specified
-.Ar path
-inside of the
-.Ar dataset
-and display its metadata and indirect blocks.
-Specified
-.Ar path
-must be relative to the root of
-.Ar dataset .
-This option can be combined with
-.Fl v
-for increasing verbosity.
-.It Xo
-.Fl R Ar poolname vdev Ns \&: Ns Ar offset Ns \&: Ns Ar size Ns Op : Ns Ar flags
-.Xc
-Read and display a block from the specified device.
-By default the block is displayed as a hex dump, but see the description of the
-.Sy r
-flag, below.
-.Pp
-The block is specified in terms of a colon-separated tuple
-.Ar vdev
-.Pq an integer vdev identifier
-.Ar offset
-.Pq the offset within the vdev
-.Ar size
-.Pq the size of the block to read
-and, optionally,
-.Ar flags
-.Pq a set of flags, described below .
-.Pp
-.Bl -tag -compact -width "b offset"
-.It Sy b Ar offset
-Print block pointer
-.It Sy d
-Decompress the block
-.It Sy e
-Byte swap the block
-.It Sy g
-Dump gang block header
-.It Sy i
-Dump indirect block
-.It Sy r
-Dump raw uninterpreted block data
-.El
-.It Fl s
-Report statistics on
-.Nm zdb
-I/O.
-Display operation counts, bandwidth, and error counts of I/O to the pool from
-.Nm .
-.It Fl S
-Simulate the effects of deduplication, constructing a DDT and then display
-that DDT as with
-.Fl DD .
-.It Fl u
-Display the current uberblock.
-.El
-.Pp
-Other options:
-.Bl -tag -width Ds
-.It Fl A
-Do not abort should any assertion fail.
-.It Fl AA
-Enable panic recovery, certain errors which would otherwise be fatal are
-demoted to warnings.
-.It Fl AAA
-Do not abort if asserts fail and also enable panic recovery.
-.It Fl e Op Fl p Ar path ...
-Operate on an exported pool, not present in
-.Pa /boot/zfs/zpool.cache .
-The
-.Fl p
-flag specifies the path under which devices are to be searched.
-.It Fl x Ar dumpdir
-All blocks accessed will be copied to files in the specified directory.
-The blocks will be placed in sparse files whose name is the same as
-that of the file or device read.
-.Nm
-can be then run on the generated files.
-Note that the
-.Fl bbc
-flags are sufficient to access
-.Pq and thus copy
-all metadata on the pool.
-.It Fl F
-Attempt to make an unreadable pool readable by trying progressively older
-transactions.
-.It Fl G
-Dump the contents of the zfs_dbgmsg buffer before exiting
-.Nm .
-zfs_dbgmsg is a buffer used by ZFS to dump advanced debug information.
-.It Fl I Ar inflight I/Os
-Limit the number of outstanding checksum I/Os to the specified value.
-The default value is 200.
-This option affects the performance of the
-.Fl c
-option.
-.It Fl o Ar var Ns = Ns Ar value ...
-Set the given global libzpool variable to the provided value.
-The value must be an unsigned 32-bit integer.
-Currently only little-endian systems are supported to avoid accidentally setting
-the high 32 bits of 64-bit variables.
-.It Fl P
-Print numbers in an unscaled form more amenable to parsing, eg. 1000000 rather
-than 1M.
-.It Fl t Ar transaction
-Specify the highest transaction to use when searching for uberblocks.
-See also the
-.Fl u
-and
-.Fl l
-options for a means to see the available uberblocks and their associated
-transaction numbers.
-.It Fl U Ar cachefile
-Use a cache file other than
-.Pa /boot/zfs/zpool.cache .
-.It Fl v
-Enable verbosity.
-Specify multiple times for increased verbosity.
-.It Fl V
-Attempt verbatim import.
-This mimics the behavior of the kernel when loading a pool from a cachefile.
-Only usable with
-.Fl e .
-.It Fl X
-Attempt
-.Qq extreme
-transaction rewind, that is attempt the same recovery as
-.Fl F
-but read transactions otherwise deemed too old.
-.El
-.Pp
-Specifying a display option more than once enables verbosity for only that
-option, with more occurrences enabling more verbosity.
-.Pp
-If no options are specified, all information about the named pool will be
-displayed at default verbosity.
-.Sh EXAMPLES
-.Bl -tag -width Ds
-.It Xo
-.Sy Example 1
-Display the configuration of imported pool
-.Pa rpool
-.Xc
-.Bd -literal
-# zdb -C rpool
-
-MOS Configuration:
- version: 28
- name: 'rpool'
- ...
-.Ed
-.It Xo
-.Sy Example 2
-Display basic dataset information about
-.Pa rpool
-.Xc
-.Bd -literal
-# zdb -d rpool
-Dataset mos [META], ID 0, cr_txg 4, 26.9M, 1051 objects
-Dataset rpool/swap [ZVOL], ID 59, cr_txg 356, 486M, 2 objects
- ...
-.Ed
-.It Xo
-.Sy Example 3
-Display basic information about object 0 in
-.Pa rpool/export/home
-.Xc
-.Bd -literal
-# zdb -d rpool/export/home 0
-Dataset rpool/export/home [ZPL], ID 137, cr_txg 1546, 32K, 8 objects
-
- Object lvl iblk dblk dsize lsize %full type
- 0 7 16K 16K 15.0K 16K 25.00 DMU dnode
-.Ed
-.It Xo
-.Sy Example 4
-Display the predicted effect of enabling deduplication on
-.Pa rpool
-.Xc
-.Bd -literal
-# zdb -S rpool
-Simulated DDT histogram:
-
-bucket allocated referenced
-______ ______________________________ ______________________________
-refcnt blocks LSIZE PSIZE DSIZE blocks LSIZE PSIZE DSIZE
------- ------ ----- ----- ----- ------ ----- ----- -----
- 1 694K 27.1G 15.0G 15.0G 694K 27.1G 15.0G 15.0G
- 2 35.0K 1.33G 699M 699M 74.7K 2.79G 1.45G 1.45G
- ...
-dedup = 1.11, compress = 1.80, copies = 1.00, dedup * compress / copies = 2.00
-.Ed
-.El
-.Sh SEE ALSO
-.Xr zfs 8 ,
-.Xr zpool 8
-.Sh HISTORY
-The
-.Nm
-utility first appeared in
-.Fx 7.0 .
diff --git a/cddl/contrib/opensolaris/cmd/zdb/zdb.c b/cddl/contrib/opensolaris/cmd/zdb/zdb.c
deleted file mode 100644
index d51ddc68908c..000000000000
--- a/cddl/contrib/opensolaris/cmd/zdb/zdb.c
+++ /dev/null
@@ -1,5749 +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, 2018 by Delphix. All rights reserved.
- * Copyright (c) 2014 Integros [integros.com]
- * Copyright 2017 Nexenta Systems, Inc.
- * Copyright (c) 2017, 2018 Lawrence Livermore National Security, LLC.
- * Copyright 2017 RackTop Systems.
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdio_ext.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <sys/zfs_context.h>
-#include <sys/spa.h>
-#include <sys/spa_impl.h>
-#include <sys/dmu.h>
-#include <sys/zap.h>
-#include <sys/fs/zfs.h>
-#include <sys/zfs_znode.h>
-#include <sys/zfs_sa.h>
-#include <sys/sa.h>
-#include <sys/sa_impl.h>
-#include <sys/vdev.h>
-#include <sys/vdev_impl.h>
-#include <sys/metaslab_impl.h>
-#include <sys/dmu_objset.h>
-#include <sys/dsl_dir.h>
-#include <sys/dsl_dataset.h>
-#include <sys/dsl_pool.h>
-#include <sys/dbuf.h>
-#include <sys/zil.h>
-#include <sys/zil_impl.h>
-#include <sys/stat.h>
-#include <sys/resource.h>
-#include <sys/dmu_traverse.h>
-#include <sys/zio_checksum.h>
-#include <sys/zio_compress.h>
-#include <sys/zfs_fuid.h>
-#include <sys/arc.h>
-#include <sys/ddt.h>
-#include <sys/zfeature.h>
-#include <sys/abd.h>
-#include <sys/blkptr.h>
-#include <sys/dsl_scan.h>
-#include <zfs_comutil.h>
-#include <libcmdutils.h>
-#undef verify
-#include <libzfs.h>
-
-#include "zdb.h"
-
-#define ZDB_COMPRESS_NAME(idx) ((idx) < ZIO_COMPRESS_FUNCTIONS ? \
- zio_compress_table[(idx)].ci_name : "UNKNOWN")
-#define ZDB_CHECKSUM_NAME(idx) ((idx) < ZIO_CHECKSUM_FUNCTIONS ? \
- zio_checksum_table[(idx)].ci_name : "UNKNOWN")
-#define ZDB_OT_NAME(idx) ((idx) < DMU_OT_NUMTYPES ? \
- dmu_ot[(idx)].ot_name : DMU_OT_IS_VALID(idx) ? \
- dmu_ot_byteswap[DMU_OT_BYTESWAP(idx)].ob_name : "UNKNOWN")
-#define ZDB_OT_TYPE(idx) ((idx) < DMU_OT_NUMTYPES ? (idx) : \
- (idx) == DMU_OTN_ZAP_DATA || (idx) == DMU_OTN_ZAP_METADATA ? \
- DMU_OT_ZAP_OTHER : \
- (idx) == DMU_OTN_UINT64_DATA || (idx) == DMU_OTN_UINT64_METADATA ? \
- DMU_OT_UINT64_OTHER : DMU_OT_NUMTYPES)
-
-#ifndef lint
-extern int reference_tracking_enable;
-extern boolean_t zfs_recover;
-extern uint64_t zfs_arc_max, zfs_arc_meta_limit;
-extern int zfs_vdev_async_read_max_active;
-extern boolean_t spa_load_verify_dryrun;
-extern int aok;
-#else
-int reference_tracking_enable;
-boolean_t zfs_recover;
-uint64_t zfs_arc_max, zfs_arc_meta_limit;
-int zfs_vdev_async_read_max_active;
-boolean_t spa_load_verify_dryrun;
-int aok;
-#endif
-
-static const char cmdname[] = "zdb";
-uint8_t dump_opt[256];
-
-typedef void object_viewer_t(objset_t *, uint64_t, void *data, size_t size);
-
-static uint64_t *zopt_object = NULL;
-static unsigned zopt_objects = 0;
-static libzfs_handle_t *g_zfs;
-static uint64_t max_inflight = 1000;
-static int leaked_objects = 0;
-
-static void snprintf_blkptr_compact(char *, size_t, const blkptr_t *);
-static void mos_obj_refd(uint64_t);
-
-/*
- * These libumem hooks provide a reasonable set of defaults for the allocator's
- * debugging facilities.
- */
-const char *
-_umem_debug_init()
-{
- return ("default,verbose"); /* $UMEM_DEBUG setting */
-}
-
-const char *
-_umem_logging_init(void)
-{
- return ("fail,contents"); /* $UMEM_LOGGING setting */
-}
-
-static void
-usage(void)
-{
- (void) fprintf(stderr,
- "Usage:\t%s [-AbcdDFGhikLMPsvX] [-e [-V] [-p <path> ...]] "
- "[-I <inflight I/Os>]\n"
- "\t\t[-o <var>=<value>]... [-t <txg>] [-U <cache>] [-x <dumpdir>]\n"
- "\t\t[<poolname> [<object> ...]]\n"
- "\t%s [-AdiPv] [-e [-V] [-p <path> ...]] [-U <cache>] <dataset> "
- "[<object> ...]\n"
- "\t%s -C [-A] [-U <cache>]\n"
- "\t%s -l [-Aqu] <device>\n"
- "\t%s -m [-AFLPX] [-e [-V] [-p <path> ...]] [-t <txg>] "
- "[-U <cache>]\n\t\t<poolname> [<vdev> [<metaslab> ...]]\n"
- "\t%s -O <dataset> <path>\n"
- "\t%s -R [-A] [-e [-V] [-p <path> ...]] [-U <cache>]\n"
- "\t\t<poolname> <vdev>:<offset>:<size>[:<flags>]\n"
- "\t%s -E [-A] word0:word1:...:word15\n"
- "\t%s -S [-AP] [-e [-V] [-p <path> ...]] [-U <cache>] "
- "<poolname>\n\n",
- cmdname, cmdname, cmdname, cmdname, cmdname, cmdname, cmdname,
- cmdname, cmdname);
-
- (void) fprintf(stderr, " Dataset name must include at least one "
- "separator character '/' or '@'\n");
- (void) fprintf(stderr, " If dataset name is specified, only that "
- "dataset is dumped\n");
- (void) fprintf(stderr, " If object numbers are specified, only "
- "those objects are dumped\n\n");
- (void) fprintf(stderr, " Options to control amount of output:\n");
- (void) fprintf(stderr, " -b block statistics\n");
- (void) fprintf(stderr, " -c checksum all metadata (twice for "
- "all data) blocks\n");
- (void) fprintf(stderr, " -C config (or cachefile if alone)\n");
- (void) fprintf(stderr, " -d dataset(s)\n");
- (void) fprintf(stderr, " -D dedup statistics\n");
- (void) fprintf(stderr, " -E decode and display block from an "
- "embedded block pointer\n");
- (void) fprintf(stderr, " -h pool history\n");
- (void) fprintf(stderr, " -i intent logs\n");
- (void) fprintf(stderr, " -l read label contents\n");
- (void) fprintf(stderr, " -k examine the checkpointed state "
- "of the pool\n");
- (void) fprintf(stderr, " -L disable leak tracking (do not "
- "load spacemaps)\n");
- (void) fprintf(stderr, " -m metaslabs\n");
- (void) fprintf(stderr, " -M metaslab groups\n");
- (void) fprintf(stderr, " -O perform object lookups by path\n");
- (void) fprintf(stderr, " -R read and display block from a "
- "device\n");
- (void) fprintf(stderr, " -s report stats on zdb's I/O\n");
- (void) fprintf(stderr, " -S simulate dedup to measure effect\n");
- (void) fprintf(stderr, " -v verbose (applies to all "
- "others)\n\n");
- (void) fprintf(stderr, " Below options are intended for use "
- "with other options:\n");
- (void) fprintf(stderr, " -A ignore assertions (-A), enable "
- "panic recovery (-AA) or both (-AAA)\n");
- (void) fprintf(stderr, " -e pool is exported/destroyed/"
- "has altroot/not in a cachefile\n");
- (void) fprintf(stderr, " -F attempt automatic rewind within "
- "safe range of transaction groups\n");
- (void) fprintf(stderr, " -G dump zfs_dbgmsg buffer before "
- "exiting\n");
- (void) fprintf(stderr, " -I <number of inflight I/Os> -- "
- "specify the maximum number of "
- "checksumming I/Os [default is 200]\n");
- (void) fprintf(stderr, " -o <variable>=<value> set global "
- "variable to an unsigned 32-bit integer value\n");
- (void) fprintf(stderr, " -p <path> -- use one or more with "
- "-e to specify path to vdev dir\n");
- (void) fprintf(stderr, " -P print numbers in parseable form\n");
- (void) fprintf(stderr, " -q don't print label contents\n");
- (void) fprintf(stderr, " -t <txg> -- highest txg to use when "
- "searching for uberblocks\n");
- (void) fprintf(stderr, " -u uberblock\n");
- (void) fprintf(stderr, " -U <cachefile_path> -- use alternate "
- "cachefile\n");
- (void) fprintf(stderr, " -V do verbatim import\n");
- (void) fprintf(stderr, " -x <dumpdir> -- "
- "dump all read blocks into specified directory\n");
- (void) fprintf(stderr, " -X attempt extreme rewind (does not "
- "work with dataset)\n\n");
- (void) fprintf(stderr, "Specify an option more than once (e.g. -bb) "
- "to make only that option verbose\n");
- (void) fprintf(stderr, "Default is to dump everything non-verbosely\n");
- exit(1);
-}
-
-static void
-dump_debug_buffer()
-{
- if (dump_opt['G']) {
- (void) printf("\n");
- zfs_dbgmsg_print("zdb");
- }
-}
-
-/*
- * Called for usage errors that are discovered after a call to spa_open(),
- * dmu_bonus_hold(), or pool_match(). abort() is called for other errors.
- */
-
-static void
-fatal(const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- (void) fprintf(stderr, "%s: ", cmdname);
- (void) vfprintf(stderr, fmt, ap);
- va_end(ap);
- (void) fprintf(stderr, "\n");
-
- dump_debug_buffer();
-
- exit(1);
-}
-
-/* ARGSUSED */
-static void
-dump_packed_nvlist(objset_t *os, uint64_t object, void *data, size_t size)
-{
- nvlist_t *nv;
- size_t nvsize = *(uint64_t *)data;
- char *packed = umem_alloc(nvsize, UMEM_NOFAIL);
-
- VERIFY(0 == dmu_read(os, object, 0, nvsize, packed, DMU_READ_PREFETCH));
-
- VERIFY(nvlist_unpack(packed, nvsize, &nv, 0) == 0);
-
- umem_free(packed, nvsize);
-
- dump_nvlist(nv, 8);
-
- nvlist_free(nv);
-}
-
-/* ARGSUSED */
-static void
-dump_history_offsets(objset_t *os, uint64_t object, void *data, size_t size)
-{
- spa_history_phys_t *shp = data;
-
- if (shp == NULL)
- return;
-
- (void) printf("\t\tpool_create_len = %llu\n",
- (u_longlong_t)shp->sh_pool_create_len);
- (void) printf("\t\tphys_max_off = %llu\n",
- (u_longlong_t)shp->sh_phys_max_off);
- (void) printf("\t\tbof = %llu\n",
- (u_longlong_t)shp->sh_bof);
- (void) printf("\t\teof = %llu\n",
- (u_longlong_t)shp->sh_eof);
- (void) printf("\t\trecords_lost = %llu\n",
- (u_longlong_t)shp->sh_records_lost);
-}
-
-static void
-zdb_nicenum(uint64_t num, char *buf, size_t buflen)
-{
- if (dump_opt['P'])
- (void) snprintf(buf, buflen, "%llu", (longlong_t)num);
- else
- nicenum(num, buf, sizeof (buf));
-}
-
-static const char histo_stars[] = "****************************************";
-static const uint64_t histo_width = sizeof (histo_stars) - 1;
-
-static void
-dump_histogram(const uint64_t *histo, int size, int offset)
-{
- int i;
- int minidx = size - 1;
- int maxidx = 0;
- uint64_t max = 0;
-
- for (i = 0; i < size; i++) {
- if (histo[i] > max)
- max = histo[i];
- if (histo[i] > 0 && i > maxidx)
- maxidx = i;
- if (histo[i] > 0 && i < minidx)
- minidx = i;
- }
-
- if (max < histo_width)
- max = histo_width;
-
- for (i = minidx; i <= maxidx; i++) {
- (void) printf("\t\t\t%3u: %6llu %s\n",
- i + offset, (u_longlong_t)histo[i],
- &histo_stars[(max - histo[i]) * histo_width / max]);
- }
-}
-
-static void
-dump_zap_stats(objset_t *os, uint64_t object)
-{
- int error;
- zap_stats_t zs;
-
- error = zap_get_stats(os, object, &zs);
- if (error)
- return;
-
- if (zs.zs_ptrtbl_len == 0) {
- ASSERT(zs.zs_num_blocks == 1);
- (void) printf("\tmicrozap: %llu bytes, %llu entries\n",
- (u_longlong_t)zs.zs_blocksize,
- (u_longlong_t)zs.zs_num_entries);
- return;
- }
-
- (void) printf("\tFat ZAP stats:\n");
-
- (void) printf("\t\tPointer table:\n");
- (void) printf("\t\t\t%llu elements\n",
- (u_longlong_t)zs.zs_ptrtbl_len);
- (void) printf("\t\t\tzt_blk: %llu\n",
- (u_longlong_t)zs.zs_ptrtbl_zt_blk);
- (void) printf("\t\t\tzt_numblks: %llu\n",
- (u_longlong_t)zs.zs_ptrtbl_zt_numblks);
- (void) printf("\t\t\tzt_shift: %llu\n",
- (u_longlong_t)zs.zs_ptrtbl_zt_shift);
- (void) printf("\t\t\tzt_blks_copied: %llu\n",
- (u_longlong_t)zs.zs_ptrtbl_blks_copied);
- (void) printf("\t\t\tzt_nextblk: %llu\n",
- (u_longlong_t)zs.zs_ptrtbl_nextblk);
-
- (void) printf("\t\tZAP entries: %llu\n",
- (u_longlong_t)zs.zs_num_entries);
- (void) printf("\t\tLeaf blocks: %llu\n",
- (u_longlong_t)zs.zs_num_leafs);
- (void) printf("\t\tTotal blocks: %llu\n",
- (u_longlong_t)zs.zs_num_blocks);
- (void) printf("\t\tzap_block_type: 0x%llx\n",
- (u_longlong_t)zs.zs_block_type);
- (void) printf("\t\tzap_magic: 0x%llx\n",
- (u_longlong_t)zs.zs_magic);
- (void) printf("\t\tzap_salt: 0x%llx\n",
- (u_longlong_t)zs.zs_salt);
-
- (void) printf("\t\tLeafs with 2^n pointers:\n");
- dump_histogram(zs.zs_leafs_with_2n_pointers, ZAP_HISTOGRAM_SIZE, 0);
-
- (void) printf("\t\tBlocks with n*5 entries:\n");
- dump_histogram(zs.zs_blocks_with_n5_entries, ZAP_HISTOGRAM_SIZE, 0);
-
- (void) printf("\t\tBlocks n/10 full:\n");
- dump_histogram(zs.zs_blocks_n_tenths_full, ZAP_HISTOGRAM_SIZE, 0);
-
- (void) printf("\t\tEntries with n chunks:\n");
- dump_histogram(zs.zs_entries_using_n_chunks, ZAP_HISTOGRAM_SIZE, 0);
-
- (void) printf("\t\tBuckets with n entries:\n");
- dump_histogram(zs.zs_buckets_with_n_entries, ZAP_HISTOGRAM_SIZE, 0);
-}
-
-/*ARGSUSED*/
-static void
-dump_none(objset_t *os, uint64_t object, void *data, size_t size)
-{
-}
-
-/*ARGSUSED*/
-static void
-dump_unknown(objset_t *os, uint64_t object, void *data, size_t size)
-{
- (void) printf("\tUNKNOWN OBJECT TYPE\n");
-}
-
-/*ARGSUSED*/
-static void
-dump_uint8(objset_t *os, uint64_t object, void *data, size_t size)
-{
-}
-
-/*ARGSUSED*/
-static void
-dump_uint64(objset_t *os, uint64_t object, void *data, size_t size)
-{
-}
-
-/*ARGSUSED*/
-static void
-dump_zap(objset_t *os, uint64_t object, void *data, size_t size)
-{
- zap_cursor_t zc;
- zap_attribute_t attr;
- void *prop;
- unsigned i;
-
- dump_zap_stats(os, object);
- (void) printf("\n");
-
- for (zap_cursor_init(&zc, os, object);
- zap_cursor_retrieve(&zc, &attr) == 0;
- zap_cursor_advance(&zc)) {
- (void) printf("\t\t%s = ", attr.za_name);
- if (attr.za_num_integers == 0) {
- (void) printf("\n");
- continue;
- }
- prop = umem_zalloc(attr.za_num_integers *
- attr.za_integer_length, UMEM_NOFAIL);
- (void) zap_lookup(os, object, attr.za_name,
- attr.za_integer_length, attr.za_num_integers, prop);
- if (attr.za_integer_length == 1) {
- (void) printf("%s", (char *)prop);
- } else {
- for (i = 0; i < attr.za_num_integers; i++) {
- switch (attr.za_integer_length) {
- case 2:
- (void) printf("%u ",
- ((uint16_t *)prop)[i]);
- break;
- case 4:
- (void) printf("%u ",
- ((uint32_t *)prop)[i]);
- break;
- case 8:
- (void) printf("%lld ",
- (u_longlong_t)((int64_t *)prop)[i]);
- break;
- }
- }
- }
- (void) printf("\n");
- umem_free(prop, attr.za_num_integers * attr.za_integer_length);
- }
- zap_cursor_fini(&zc);
-}
-
-static void
-dump_bpobj(objset_t *os, uint64_t object, void *data, size_t size)
-{
- bpobj_phys_t *bpop = data;
- char bytes[32], comp[32], uncomp[32];
-
- /* make sure the output won't get truncated */
- CTASSERT(sizeof (bytes) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (comp) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (uncomp) >= NN_NUMBUF_SZ);
-
- if (bpop == NULL)
- return;
-
- zdb_nicenum(bpop->bpo_bytes, bytes, sizeof (bytes));
- zdb_nicenum(bpop->bpo_comp, comp, sizeof (comp));
- zdb_nicenum(bpop->bpo_uncomp, uncomp, sizeof (uncomp));
-
- (void) printf("\t\tnum_blkptrs = %llu\n",
- (u_longlong_t)bpop->bpo_num_blkptrs);
- (void) printf("\t\tbytes = %s\n", bytes);
- if (size >= BPOBJ_SIZE_V1) {
- (void) printf("\t\tcomp = %s\n", comp);
- (void) printf("\t\tuncomp = %s\n", uncomp);
- }
- if (size >= sizeof (*bpop)) {
- (void) printf("\t\tsubobjs = %llu\n",
- (u_longlong_t)bpop->bpo_subobjs);
- (void) printf("\t\tnum_subobjs = %llu\n",
- (u_longlong_t)bpop->bpo_num_subobjs);
- }
-
- if (dump_opt['d'] < 5)
- return;
-
- for (uint64_t i = 0; i < bpop->bpo_num_blkptrs; i++) {
- char blkbuf[BP_SPRINTF_LEN];
- blkptr_t bp;
-
- int err = dmu_read(os, object,
- i * sizeof (bp), sizeof (bp), &bp, 0);
- if (err != 0) {
- (void) printf("got error %u from dmu_read\n", err);
- break;
- }
- snprintf_blkptr_compact(blkbuf, sizeof (blkbuf), &bp);
- (void) printf("\t%s\n", blkbuf);
- }
-}
-
-/* ARGSUSED */
-static void
-dump_bpobj_subobjs(objset_t *os, uint64_t object, void *data, size_t size)
-{
- dmu_object_info_t doi;
-
- VERIFY0(dmu_object_info(os, object, &doi));
- uint64_t *subobjs = kmem_alloc(doi.doi_max_offset, KM_SLEEP);
-
- int err = dmu_read(os, object, 0, doi.doi_max_offset, subobjs, 0);
- if (err != 0) {
- (void) printf("got error %u from dmu_read\n", err);
- kmem_free(subobjs, doi.doi_max_offset);
- return;
- }
-
- int64_t last_nonzero = -1;
- for (uint64_t i = 0; i < doi.doi_max_offset / 8; i++) {
- if (subobjs[i] != 0)
- last_nonzero = i;
- }
-
- for (int64_t i = 0; i <= last_nonzero; i++) {
- (void) printf("\t%llu\n", (longlong_t)subobjs[i]);
- }
- kmem_free(subobjs, doi.doi_max_offset);
-}
-
-/*ARGSUSED*/
-static void
-dump_ddt_zap(objset_t *os, uint64_t object, void *data, size_t size)
-{
- dump_zap_stats(os, object);
- /* contents are printed elsewhere, properly decoded */
-}
-
-/*ARGSUSED*/
-static void
-dump_sa_attrs(objset_t *os, uint64_t object, void *data, size_t size)
-{
- zap_cursor_t zc;
- zap_attribute_t attr;
-
- dump_zap_stats(os, object);
- (void) printf("\n");
-
- for (zap_cursor_init(&zc, os, object);
- zap_cursor_retrieve(&zc, &attr) == 0;
- zap_cursor_advance(&zc)) {
- (void) printf("\t\t%s = ", attr.za_name);
- if (attr.za_num_integers == 0) {
- (void) printf("\n");
- continue;
- }
- (void) printf(" %llx : [%d:%d:%d]\n",
- (u_longlong_t)attr.za_first_integer,
- (int)ATTR_LENGTH(attr.za_first_integer),
- (int)ATTR_BSWAP(attr.za_first_integer),
- (int)ATTR_NUM(attr.za_first_integer));
- }
- zap_cursor_fini(&zc);
-}
-
-/*ARGSUSED*/
-static void
-dump_sa_layouts(objset_t *os, uint64_t object, void *data, size_t size)
-{
- zap_cursor_t zc;
- zap_attribute_t attr;
- uint16_t *layout_attrs;
- unsigned i;
-
- dump_zap_stats(os, object);
- (void) printf("\n");
-
- for (zap_cursor_init(&zc, os, object);
- zap_cursor_retrieve(&zc, &attr) == 0;
- zap_cursor_advance(&zc)) {
- (void) printf("\t\t%s = [", attr.za_name);
- if (attr.za_num_integers == 0) {
- (void) printf("\n");
- continue;
- }
-
- VERIFY(attr.za_integer_length == 2);
- layout_attrs = umem_zalloc(attr.za_num_integers *
- attr.za_integer_length, UMEM_NOFAIL);
-
- VERIFY(zap_lookup(os, object, attr.za_name,
- attr.za_integer_length,
- attr.za_num_integers, layout_attrs) == 0);
-
- for (i = 0; i != attr.za_num_integers; i++)
- (void) printf(" %d ", (int)layout_attrs[i]);
- (void) printf("]\n");
- umem_free(layout_attrs,
- attr.za_num_integers * attr.za_integer_length);
- }
- zap_cursor_fini(&zc);
-}
-
-/*ARGSUSED*/
-static void
-dump_zpldir(objset_t *os, uint64_t object, void *data, size_t size)
-{
- zap_cursor_t zc;
- zap_attribute_t attr;
- const char *typenames[] = {
- /* 0 */ "not specified",
- /* 1 */ "FIFO",
- /* 2 */ "Character Device",
- /* 3 */ "3 (invalid)",
- /* 4 */ "Directory",
- /* 5 */ "5 (invalid)",
- /* 6 */ "Block Device",
- /* 7 */ "7 (invalid)",
- /* 8 */ "Regular File",
- /* 9 */ "9 (invalid)",
- /* 10 */ "Symbolic Link",
- /* 11 */ "11 (invalid)",
- /* 12 */ "Socket",
- /* 13 */ "Door",
- /* 14 */ "Event Port",
- /* 15 */ "15 (invalid)",
- };
-
- dump_zap_stats(os, object);
- (void) printf("\n");
-
- for (zap_cursor_init(&zc, os, object);
- zap_cursor_retrieve(&zc, &attr) == 0;
- zap_cursor_advance(&zc)) {
- (void) printf("\t\t%s = %lld (type: %s)\n",
- attr.za_name, ZFS_DIRENT_OBJ(attr.za_first_integer),
- typenames[ZFS_DIRENT_TYPE(attr.za_first_integer)]);
- }
- zap_cursor_fini(&zc);
-}
-
-static int
-get_dtl_refcount(vdev_t *vd)
-{
- int refcount = 0;
-
- if (vd->vdev_ops->vdev_op_leaf) {
- space_map_t *sm = vd->vdev_dtl_sm;
-
- if (sm != NULL &&
- sm->sm_dbuf->db_size == sizeof (space_map_phys_t))
- return (1);
- return (0);
- }
-
- for (unsigned c = 0; c < vd->vdev_children; c++)
- refcount += get_dtl_refcount(vd->vdev_child[c]);
- return (refcount);
-}
-
-static int
-get_metaslab_refcount(vdev_t *vd)
-{
- int refcount = 0;
-
- if (vd->vdev_top == vd) {
- for (uint64_t m = 0; m < vd->vdev_ms_count; m++) {
- space_map_t *sm = vd->vdev_ms[m]->ms_sm;
-
- if (sm != NULL &&
- sm->sm_dbuf->db_size == sizeof (space_map_phys_t))
- refcount++;
- }
- }
- for (unsigned c = 0; c < vd->vdev_children; c++)
- refcount += get_metaslab_refcount(vd->vdev_child[c]);
-
- return (refcount);
-}
-
-static int
-get_obsolete_refcount(vdev_t *vd)
-{
- int refcount = 0;
-
- uint64_t obsolete_sm_obj = vdev_obsolete_sm_object(vd);
- if (vd->vdev_top == vd && obsolete_sm_obj != 0) {
- dmu_object_info_t doi;
- VERIFY0(dmu_object_info(vd->vdev_spa->spa_meta_objset,
- obsolete_sm_obj, &doi));
- if (doi.doi_bonus_size == sizeof (space_map_phys_t)) {
- refcount++;
- }
- } else {
- ASSERT3P(vd->vdev_obsolete_sm, ==, NULL);
- ASSERT3U(obsolete_sm_obj, ==, 0);
- }
- for (unsigned c = 0; c < vd->vdev_children; c++) {
- refcount += get_obsolete_refcount(vd->vdev_child[c]);
- }
-
- return (refcount);
-}
-
-static int
-get_prev_obsolete_spacemap_refcount(spa_t *spa)
-{
- uint64_t prev_obj =
- spa->spa_condensing_indirect_phys.scip_prev_obsolete_sm_object;
- if (prev_obj != 0) {
- dmu_object_info_t doi;
- VERIFY0(dmu_object_info(spa->spa_meta_objset, prev_obj, &doi));
- if (doi.doi_bonus_size == sizeof (space_map_phys_t)) {
- return (1);
- }
- }
- return (0);
-}
-
-static int
-get_checkpoint_refcount(vdev_t *vd)
-{
- int refcount = 0;
-
- if (vd->vdev_top == vd && vd->vdev_top_zap != 0 &&
- zap_contains(spa_meta_objset(vd->vdev_spa),
- vd->vdev_top_zap, VDEV_TOP_ZAP_POOL_CHECKPOINT_SM) == 0)
- refcount++;
-
- for (uint64_t c = 0; c < vd->vdev_children; c++)
- refcount += get_checkpoint_refcount(vd->vdev_child[c]);
-
- return (refcount);
-}
-
-static int
-verify_spacemap_refcounts(spa_t *spa)
-{
- uint64_t expected_refcount = 0;
- uint64_t actual_refcount;
-
- (void) feature_get_refcount(spa,
- &spa_feature_table[SPA_FEATURE_SPACEMAP_HISTOGRAM],
- &expected_refcount);
- actual_refcount = get_dtl_refcount(spa->spa_root_vdev);
- actual_refcount += get_metaslab_refcount(spa->spa_root_vdev);
- actual_refcount += get_obsolete_refcount(spa->spa_root_vdev);
- actual_refcount += get_prev_obsolete_spacemap_refcount(spa);
- actual_refcount += get_checkpoint_refcount(spa->spa_root_vdev);
-
- if (expected_refcount != actual_refcount) {
- (void) printf("space map refcount mismatch: expected %lld != "
- "actual %lld\n",
- (longlong_t)expected_refcount,
- (longlong_t)actual_refcount);
- return (2);
- }
- return (0);
-}
-
-static void
-dump_spacemap(objset_t *os, space_map_t *sm)
-{
- char *ddata[] = { "ALLOC", "FREE", "CONDENSE", "INVALID",
- "INVALID", "INVALID", "INVALID", "INVALID" };
-
- if (sm == NULL)
- return;
-
- (void) printf("space map object %llu:\n",
- (longlong_t)sm->sm_object);
- (void) printf(" smp_length = 0x%llx\n",
- (longlong_t)sm->sm_phys->smp_length);
- (void) printf(" smp_alloc = 0x%llx\n",
- (longlong_t)sm->sm_phys->smp_alloc);
-
- if (dump_opt['d'] < 6 && dump_opt['m'] < 4)
- return;
-
- /*
- * Print out the freelist entries in both encoded and decoded form.
- */
- uint8_t mapshift = sm->sm_shift;
- int64_t alloc = 0;
- uint64_t word, entry_id = 0;
- for (uint64_t offset = 0; offset < space_map_length(sm);
- offset += sizeof (word)) {
-
- VERIFY0(dmu_read(os, space_map_object(sm), offset,
- sizeof (word), &word, DMU_READ_PREFETCH));
-
- if (sm_entry_is_debug(word)) {
- (void) printf("\t [%6llu] %s: txg %llu pass %llu\n",
- (u_longlong_t)entry_id,
- ddata[SM_DEBUG_ACTION_DECODE(word)],
- (u_longlong_t)SM_DEBUG_TXG_DECODE(word),
- (u_longlong_t)SM_DEBUG_SYNCPASS_DECODE(word));
- entry_id++;
- continue;
- }
-
- uint8_t words;
- char entry_type;
- uint64_t entry_off, entry_run, entry_vdev = SM_NO_VDEVID;
-
- if (sm_entry_is_single_word(word)) {
- entry_type = (SM_TYPE_DECODE(word) == SM_ALLOC) ?
- 'A' : 'F';
- entry_off = (SM_OFFSET_DECODE(word) << mapshift) +
- sm->sm_start;
- entry_run = SM_RUN_DECODE(word) << mapshift;
- words = 1;
- } else {
- /* it is a two-word entry so we read another word */
- ASSERT(sm_entry_is_double_word(word));
-
- uint64_t extra_word;
- offset += sizeof (extra_word);
- VERIFY0(dmu_read(os, space_map_object(sm), offset,
- sizeof (extra_word), &extra_word,
- DMU_READ_PREFETCH));
-
- ASSERT3U(offset, <=, space_map_length(sm));
-
- entry_run = SM2_RUN_DECODE(word) << mapshift;
- entry_vdev = SM2_VDEV_DECODE(word);
- entry_type = (SM2_TYPE_DECODE(extra_word) == SM_ALLOC) ?
- 'A' : 'F';
- entry_off = (SM2_OFFSET_DECODE(extra_word) <<
- mapshift) + sm->sm_start;
- words = 2;
- }
-
- (void) printf("\t [%6llu] %c range:"
- " %010llx-%010llx size: %06llx vdev: %06llu words: %u\n",
- (u_longlong_t)entry_id,
- entry_type, (u_longlong_t)entry_off,
- (u_longlong_t)(entry_off + entry_run),
- (u_longlong_t)entry_run,
- (u_longlong_t)entry_vdev, words);
-
- if (entry_type == 'A')
- alloc += entry_run;
- else
- alloc -= entry_run;
- entry_id++;
- }
- if (alloc != space_map_allocated(sm)) {
- (void) printf("space_map_object alloc (%lld) INCONSISTENT "
- "with space map summary (%lld)\n",
- (longlong_t)space_map_allocated(sm), (longlong_t)alloc);
- }
-}
-
-static void
-dump_metaslab_stats(metaslab_t *msp)
-{
- char maxbuf[32];
- range_tree_t *rt = msp->ms_allocatable;
- avl_tree_t *t = &msp->ms_allocatable_by_size;
- int free_pct = range_tree_space(rt) * 100 / msp->ms_size;
-
- /* max sure nicenum has enough space */
- CTASSERT(sizeof (maxbuf) >= NN_NUMBUF_SZ);
-
- zdb_nicenum(metaslab_block_maxsize(msp), maxbuf, sizeof (maxbuf));
-
- (void) printf("\t %25s %10lu %7s %6s %4s %4d%%\n",
- "segments", avl_numnodes(t), "maxsize", maxbuf,
- "freepct", free_pct);
- (void) printf("\tIn-memory histogram:\n");
- dump_histogram(rt->rt_histogram, RANGE_TREE_HISTOGRAM_SIZE, 0);
-}
-
-static void
-dump_metaslab(metaslab_t *msp)
-{
- vdev_t *vd = msp->ms_group->mg_vd;
- spa_t *spa = vd->vdev_spa;
- space_map_t *sm = msp->ms_sm;
- char freebuf[32];
-
- zdb_nicenum(msp->ms_size - space_map_allocated(sm), freebuf,
- sizeof (freebuf));
-
- (void) printf(
- "\tmetaslab %6llu offset %12llx spacemap %6llu free %5s\n",
- (u_longlong_t)msp->ms_id, (u_longlong_t)msp->ms_start,
- (u_longlong_t)space_map_object(sm), freebuf);
-
- if (dump_opt['m'] > 2 && !dump_opt['L']) {
- mutex_enter(&msp->ms_lock);
- VERIFY0(metaslab_load(msp));
- range_tree_stat_verify(msp->ms_allocatable);
- dump_metaslab_stats(msp);
- metaslab_unload(msp);
- mutex_exit(&msp->ms_lock);
- }
-
- if (dump_opt['m'] > 1 && sm != NULL &&
- spa_feature_is_active(spa, SPA_FEATURE_SPACEMAP_HISTOGRAM)) {
- /*
- * The space map histogram represents free space in chunks
- * of sm_shift (i.e. bucket 0 refers to 2^sm_shift).
- */
- (void) printf("\tOn-disk histogram:\t\tfragmentation %llu\n",
- (u_longlong_t)msp->ms_fragmentation);
- dump_histogram(sm->sm_phys->smp_histogram,
- SPACE_MAP_HISTOGRAM_SIZE, sm->sm_shift);
- }
-
- ASSERT(msp->ms_size == (1ULL << vd->vdev_ms_shift));
- dump_spacemap(spa->spa_meta_objset, msp->ms_sm);
-}
-
-static void
-print_vdev_metaslab_header(vdev_t *vd)
-{
- vdev_alloc_bias_t alloc_bias = vd->vdev_alloc_bias;
- const char *bias_str;
-
- bias_str = (alloc_bias == VDEV_BIAS_LOG || vd->vdev_islog) ?
- VDEV_ALLOC_BIAS_LOG :
- (alloc_bias == VDEV_BIAS_SPECIAL) ? VDEV_ALLOC_BIAS_SPECIAL :
- (alloc_bias == VDEV_BIAS_DEDUP) ? VDEV_ALLOC_BIAS_DEDUP :
- vd->vdev_islog ? "log" : "";
-
- (void) printf("\tvdev %10llu %s\n"
- "\t%-10s%5llu %-19s %-15s %-12s\n",
- (u_longlong_t)vd->vdev_id, bias_str,
- "metaslabs", (u_longlong_t)vd->vdev_ms_count,
- "offset", "spacemap", "free");
- (void) printf("\t%15s %19s %15s %12s\n",
- "---------------", "-------------------",
- "---------------", "------------");
-}
-
-static void
-dump_metaslab_groups(spa_t *spa)
-{
- vdev_t *rvd = spa->spa_root_vdev;
- metaslab_class_t *mc = spa_normal_class(spa);
- uint64_t fragmentation;
-
- metaslab_class_histogram_verify(mc);
-
- for (unsigned c = 0; c < rvd->vdev_children; c++) {
- vdev_t *tvd = rvd->vdev_child[c];
- metaslab_group_t *mg = tvd->vdev_mg;
-
- if (mg == NULL || mg->mg_class != mc)
- continue;
-
- metaslab_group_histogram_verify(mg);
- mg->mg_fragmentation = metaslab_group_fragmentation(mg);
-
- (void) printf("\tvdev %10llu\t\tmetaslabs%5llu\t\t"
- "fragmentation",
- (u_longlong_t)tvd->vdev_id,
- (u_longlong_t)tvd->vdev_ms_count);
- if (mg->mg_fragmentation == ZFS_FRAG_INVALID) {
- (void) printf("%3s\n", "-");
- } else {
- (void) printf("%3llu%%\n",
- (u_longlong_t)mg->mg_fragmentation);
- }
- dump_histogram(mg->mg_histogram, RANGE_TREE_HISTOGRAM_SIZE, 0);
- }
-
- (void) printf("\tpool %s\tfragmentation", spa_name(spa));
- fragmentation = metaslab_class_fragmentation(mc);
- if (fragmentation == ZFS_FRAG_INVALID)
- (void) printf("\t%3s\n", "-");
- else
- (void) printf("\t%3llu%%\n", (u_longlong_t)fragmentation);
- dump_histogram(mc->mc_histogram, RANGE_TREE_HISTOGRAM_SIZE, 0);
-}
-
-static void
-print_vdev_indirect(vdev_t *vd)
-{
- vdev_indirect_config_t *vic = &vd->vdev_indirect_config;
- vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping;
- vdev_indirect_births_t *vib = vd->vdev_indirect_births;
-
- if (vim == NULL) {
- ASSERT3P(vib, ==, NULL);
- return;
- }
-
- ASSERT3U(vdev_indirect_mapping_object(vim), ==,
- vic->vic_mapping_object);
- ASSERT3U(vdev_indirect_births_object(vib), ==,
- vic->vic_births_object);
-
- (void) printf("indirect births obj %llu:\n",
- (longlong_t)vic->vic_births_object);
- (void) printf(" vib_count = %llu\n",
- (longlong_t)vdev_indirect_births_count(vib));
- for (uint64_t i = 0; i < vdev_indirect_births_count(vib); i++) {
- vdev_indirect_birth_entry_phys_t *cur_vibe =
- &vib->vib_entries[i];
- (void) printf("\toffset %llx -> txg %llu\n",
- (longlong_t)cur_vibe->vibe_offset,
- (longlong_t)cur_vibe->vibe_phys_birth_txg);
- }
- (void) printf("\n");
-
- (void) printf("indirect mapping obj %llu:\n",
- (longlong_t)vic->vic_mapping_object);
- (void) printf(" vim_max_offset = 0x%llx\n",
- (longlong_t)vdev_indirect_mapping_max_offset(vim));
- (void) printf(" vim_bytes_mapped = 0x%llx\n",
- (longlong_t)vdev_indirect_mapping_bytes_mapped(vim));
- (void) printf(" vim_count = %llu\n",
- (longlong_t)vdev_indirect_mapping_num_entries(vim));
-
- if (dump_opt['d'] <= 5 && dump_opt['m'] <= 3)
- return;
-
- uint32_t *counts = vdev_indirect_mapping_load_obsolete_counts(vim);
-
- for (uint64_t i = 0; i < vdev_indirect_mapping_num_entries(vim); i++) {
- vdev_indirect_mapping_entry_phys_t *vimep =
- &vim->vim_entries[i];
- (void) printf("\t<%llx:%llx:%llx> -> "
- "<%llx:%llx:%llx> (%x obsolete)\n",
- (longlong_t)vd->vdev_id,
- (longlong_t)DVA_MAPPING_GET_SRC_OFFSET(vimep),
- (longlong_t)DVA_GET_ASIZE(&vimep->vimep_dst),
- (longlong_t)DVA_GET_VDEV(&vimep->vimep_dst),
- (longlong_t)DVA_GET_OFFSET(&vimep->vimep_dst),
- (longlong_t)DVA_GET_ASIZE(&vimep->vimep_dst),
- counts[i]);
- }
- (void) printf("\n");
-
- uint64_t obsolete_sm_object = vdev_obsolete_sm_object(vd);
- if (obsolete_sm_object != 0) {
- objset_t *mos = vd->vdev_spa->spa_meta_objset;
- (void) printf("obsolete space map object %llu:\n",
- (u_longlong_t)obsolete_sm_object);
- ASSERT(vd->vdev_obsolete_sm != NULL);
- ASSERT3U(space_map_object(vd->vdev_obsolete_sm), ==,
- obsolete_sm_object);
- dump_spacemap(mos, vd->vdev_obsolete_sm);
- (void) printf("\n");
- }
-}
-
-static void
-dump_metaslabs(spa_t *spa)
-{
- vdev_t *vd, *rvd = spa->spa_root_vdev;
- uint64_t m, c = 0, children = rvd->vdev_children;
-
- (void) printf("\nMetaslabs:\n");
-
- if (!dump_opt['d'] && zopt_objects > 0) {
- c = zopt_object[0];
-
- if (c >= children)
- (void) fatal("bad vdev id: %llu", (u_longlong_t)c);
-
- if (zopt_objects > 1) {
- vd = rvd->vdev_child[c];
- print_vdev_metaslab_header(vd);
-
- for (m = 1; m < zopt_objects; m++) {
- if (zopt_object[m] < vd->vdev_ms_count)
- dump_metaslab(
- vd->vdev_ms[zopt_object[m]]);
- else
- (void) fprintf(stderr, "bad metaslab "
- "number %llu\n",
- (u_longlong_t)zopt_object[m]);
- }
- (void) printf("\n");
- return;
- }
- children = c + 1;
- }
- for (; c < children; c++) {
- vd = rvd->vdev_child[c];
- print_vdev_metaslab_header(vd);
-
- print_vdev_indirect(vd);
-
- for (m = 0; m < vd->vdev_ms_count; m++)
- dump_metaslab(vd->vdev_ms[m]);
- (void) printf("\n");
- }
-}
-
-static void
-dump_dde(const ddt_t *ddt, const ddt_entry_t *dde, uint64_t index)
-{
- const ddt_phys_t *ddp = dde->dde_phys;
- const ddt_key_t *ddk = &dde->dde_key;
- const char *types[4] = { "ditto", "single", "double", "triple" };
- char blkbuf[BP_SPRINTF_LEN];
- blkptr_t blk;
-
- for (int p = 0; p < DDT_PHYS_TYPES; p++, ddp++) {
- if (ddp->ddp_phys_birth == 0)
- continue;
- ddt_bp_create(ddt->ddt_checksum, ddk, ddp, &blk);
- snprintf_blkptr(blkbuf, sizeof (blkbuf), &blk);
- (void) printf("index %llx refcnt %llu %s %s\n",
- (u_longlong_t)index, (u_longlong_t)ddp->ddp_refcnt,
- types[p], blkbuf);
- }
-}
-
-static void
-dump_dedup_ratio(const ddt_stat_t *dds)
-{
- double rL, rP, rD, D, dedup, compress, copies;
-
- if (dds->dds_blocks == 0)
- return;
-
- rL = (double)dds->dds_ref_lsize;
- rP = (double)dds->dds_ref_psize;
- rD = (double)dds->dds_ref_dsize;
- D = (double)dds->dds_dsize;
-
- dedup = rD / D;
- compress = rL / rP;
- copies = rD / rP;
-
- (void) printf("dedup = %.2f, compress = %.2f, copies = %.2f, "
- "dedup * compress / copies = %.2f\n\n",
- dedup, compress, copies, dedup * compress / copies);
-}
-
-static void
-dump_ddt(ddt_t *ddt, enum ddt_type type, enum ddt_class class)
-{
- char name[DDT_NAMELEN];
- ddt_entry_t dde;
- uint64_t walk = 0;
- dmu_object_info_t doi;
- uint64_t count, dspace, mspace;
- int error;
-
- error = ddt_object_info(ddt, type, class, &doi);
-
- if (error == ENOENT)
- return;
- ASSERT(error == 0);
-
- error = ddt_object_count(ddt, type, class, &count);
- ASSERT(error == 0);
- if (count == 0)
- return;
-
- dspace = doi.doi_physical_blocks_512 << 9;
- mspace = doi.doi_fill_count * doi.doi_data_block_size;
-
- ddt_object_name(ddt, type, class, name);
-
- (void) printf("%s: %llu entries, size %llu on disk, %llu in core\n",
- name,
- (u_longlong_t)count,
- (u_longlong_t)(dspace / count),
- (u_longlong_t)(mspace / count));
-
- if (dump_opt['D'] < 3)
- return;
-
- zpool_dump_ddt(NULL, &ddt->ddt_histogram[type][class]);
-
- if (dump_opt['D'] < 4)
- return;
-
- if (dump_opt['D'] < 5 && class == DDT_CLASS_UNIQUE)
- return;
-
- (void) printf("%s contents:\n\n", name);
-
- while ((error = ddt_object_walk(ddt, type, class, &walk, &dde)) == 0)
- dump_dde(ddt, &dde, walk);
-
- ASSERT3U(error, ==, ENOENT);
-
- (void) printf("\n");
-}
-
-static void
-dump_all_ddts(spa_t *spa)
-{
- ddt_histogram_t ddh_total;
- ddt_stat_t dds_total;
-
- bzero(&ddh_total, sizeof (ddh_total));
- bzero(&dds_total, sizeof (dds_total));
-
- for (enum zio_checksum c = 0; c < ZIO_CHECKSUM_FUNCTIONS; c++) {
- ddt_t *ddt = spa->spa_ddt[c];
- for (enum ddt_type type = 0; type < DDT_TYPES; type++) {
- for (enum ddt_class class = 0; class < DDT_CLASSES;
- class++) {
- dump_ddt(ddt, type, class);
- }
- }
- }
-
- ddt_get_dedup_stats(spa, &dds_total);
-
- if (dds_total.dds_blocks == 0) {
- (void) printf("All DDTs are empty\n");
- return;
- }
-
- (void) printf("\n");
-
- if (dump_opt['D'] > 1) {
- (void) printf("DDT histogram (aggregated over all DDTs):\n");
- ddt_get_dedup_histogram(spa, &ddh_total);
- zpool_dump_ddt(&dds_total, &ddh_total);
- }
-
- dump_dedup_ratio(&dds_total);
-}
-
-static void
-dump_dtl_seg(void *arg, uint64_t start, uint64_t size)
-{
- char *prefix = arg;
-
- (void) printf("%s [%llu,%llu) length %llu\n",
- prefix,
- (u_longlong_t)start,
- (u_longlong_t)(start + size),
- (u_longlong_t)(size));
-}
-
-static void
-dump_dtl(vdev_t *vd, int indent)
-{
- spa_t *spa = vd->vdev_spa;
- boolean_t required;
- const char *name[DTL_TYPES] = { "missing", "partial", "scrub",
- "outage" };
- char prefix[256];
-
- spa_vdev_state_enter(spa, SCL_NONE);
- required = vdev_dtl_required(vd);
- (void) spa_vdev_state_exit(spa, NULL, 0);
-
- if (indent == 0)
- (void) printf("\nDirty time logs:\n\n");
-
- (void) printf("\t%*s%s [%s]\n", indent, "",
- vd->vdev_path ? vd->vdev_path :
- vd->vdev_parent ? vd->vdev_ops->vdev_op_type : spa_name(spa),
- required ? "DTL-required" : "DTL-expendable");
-
- for (int t = 0; t < DTL_TYPES; t++) {
- range_tree_t *rt = vd->vdev_dtl[t];
- if (range_tree_space(rt) == 0)
- continue;
- (void) snprintf(prefix, sizeof (prefix), "\t%*s%s",
- indent + 2, "", name[t]);
- range_tree_walk(rt, dump_dtl_seg, prefix);
- if (dump_opt['d'] > 5 && vd->vdev_children == 0)
- dump_spacemap(spa->spa_meta_objset, vd->vdev_dtl_sm);
- }
-
- for (unsigned c = 0; c < vd->vdev_children; c++)
- dump_dtl(vd->vdev_child[c], indent + 4);
-}
-
-/* from spa_history.c: spa_history_create_obj() */
-#define HIS_BUF_LEN_DEF (128 << 10)
-#define HIS_BUF_LEN_MAX (1 << 30)
-
-static void
-dump_history(spa_t *spa)
-{
- nvlist_t **events = NULL;
- char *buf = NULL;
- uint64_t bufsize = HIS_BUF_LEN_DEF;
- uint64_t resid, len, off = 0;
- uint_t num = 0;
- int error;
- time_t tsec;
- struct tm t;
- char tbuf[30];
- char internalstr[MAXPATHLEN];
-
- if ((buf = malloc(bufsize)) == NULL)
- (void) fprintf(stderr, "Unable to read history: "
- "out of memory\n");
- do {
- len = bufsize;
-
- if ((error = spa_history_get(spa, &off, &len, buf)) != 0) {
- (void) fprintf(stderr, "Unable to read history: "
- "error %d\n", error);
- return;
- }
-
- if (zpool_history_unpack(buf, len, &resid, &events, &num) != 0)
- break;
- off -= resid;
-
- /*
- * If the history block is too big, double the buffer
- * size and try again.
- */
- if (resid == len) {
- free(buf);
- buf = NULL;
-
- bufsize <<= 1;
- if ((bufsize >= HIS_BUF_LEN_MAX) ||
- ((buf = malloc(bufsize)) == NULL)) {
- (void) fprintf(stderr, "Unable to read history: "
- "out of memory\n");
- return;
- }
- }
- } while (len != 0);
- free(buf);
-
- (void) printf("\nHistory:\n");
- for (unsigned i = 0; i < num; i++) {
- uint64_t time, txg, ievent;
- char *cmd, *intstr;
- boolean_t printed = B_FALSE;
-
- if (nvlist_lookup_uint64(events[i], ZPOOL_HIST_TIME,
- &time) != 0)
- goto next;
- if (nvlist_lookup_string(events[i], ZPOOL_HIST_CMD,
- &cmd) != 0) {
- if (nvlist_lookup_uint64(events[i],
- ZPOOL_HIST_INT_EVENT, &ievent) != 0)
- goto next;
- verify(nvlist_lookup_uint64(events[i],
- ZPOOL_HIST_TXG, &txg) == 0);
- verify(nvlist_lookup_string(events[i],
- ZPOOL_HIST_INT_STR, &intstr) == 0);
- if (ievent >= ZFS_NUM_LEGACY_HISTORY_EVENTS)
- goto next;
-
- (void) snprintf(internalstr,
- sizeof (internalstr),
- "[internal %s txg:%ju] %s",
- zfs_history_event_names[ievent], (uintmax_t)txg,
- intstr);
- cmd = internalstr;
- }
- tsec = time;
- (void) localtime_r(&tsec, &t);
- (void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t);
- (void) printf("%s %s\n", tbuf, cmd);
- printed = B_TRUE;
-
-next:
- if (dump_opt['h'] > 1) {
- if (!printed)
- (void) printf("unrecognized record:\n");
- dump_nvlist(events[i], 2);
- }
- }
-}
-
-/*ARGSUSED*/
-static void
-dump_dnode(objset_t *os, uint64_t object, void *data, size_t size)
-{
-}
-
-static uint64_t
-blkid2offset(const dnode_phys_t *dnp, const blkptr_t *bp,
- const zbookmark_phys_t *zb)
-{
- if (dnp == NULL) {
- ASSERT(zb->zb_level < 0);
- if (zb->zb_object == 0)
- return (zb->zb_blkid);
- return (zb->zb_blkid * BP_GET_LSIZE(bp));
- }
-
- ASSERT(zb->zb_level >= 0);
-
- return ((zb->zb_blkid <<
- (zb->zb_level * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT))) *
- dnp->dn_datablkszsec << SPA_MINBLOCKSHIFT);
-}
-
-static void
-snprintf_blkptr_compact(char *blkbuf, size_t buflen, const blkptr_t *bp)
-{
- const dva_t *dva = bp->blk_dva;
- int ndvas = dump_opt['d'] > 5 ? BP_GET_NDVAS(bp) : 1;
-
- if (dump_opt['b'] >= 6) {
- snprintf_blkptr(blkbuf, buflen, bp);
- return;
- }
-
- if (BP_IS_EMBEDDED(bp)) {
- (void) sprintf(blkbuf,
- "EMBEDDED et=%u %llxL/%llxP B=%llu",
- (int)BPE_GET_ETYPE(bp),
- (u_longlong_t)BPE_GET_LSIZE(bp),
- (u_longlong_t)BPE_GET_PSIZE(bp),
- (u_longlong_t)bp->blk_birth);
- return;
- }
-
- blkbuf[0] = '\0';
- for (int i = 0; i < ndvas; i++)
- (void) snprintf(blkbuf + strlen(blkbuf),
- buflen - strlen(blkbuf), "%llu:%llx:%llx ",
- (u_longlong_t)DVA_GET_VDEV(&dva[i]),
- (u_longlong_t)DVA_GET_OFFSET(&dva[i]),
- (u_longlong_t)DVA_GET_ASIZE(&dva[i]));
-
- if (BP_IS_HOLE(bp)) {
- (void) snprintf(blkbuf + strlen(blkbuf),
- buflen - strlen(blkbuf),
- "%llxL B=%llu",
- (u_longlong_t)BP_GET_LSIZE(bp),
- (u_longlong_t)bp->blk_birth);
- } else {
- (void) snprintf(blkbuf + strlen(blkbuf),
- buflen - strlen(blkbuf),
- "%llxL/%llxP F=%llu B=%llu/%llu",
- (u_longlong_t)BP_GET_LSIZE(bp),
- (u_longlong_t)BP_GET_PSIZE(bp),
- (u_longlong_t)BP_GET_FILL(bp),
- (u_longlong_t)bp->blk_birth,
- (u_longlong_t)BP_PHYSICAL_BIRTH(bp));
- }
-}
-
-static void
-print_indirect(blkptr_t *bp, const zbookmark_phys_t *zb,
- const dnode_phys_t *dnp)
-{
- char blkbuf[BP_SPRINTF_LEN];
- int l;
-
- if (!BP_IS_EMBEDDED(bp)) {
- ASSERT3U(BP_GET_TYPE(bp), ==, dnp->dn_type);
- ASSERT3U(BP_GET_LEVEL(bp), ==, zb->zb_level);
- }
-
- (void) printf("%16llx ", (u_longlong_t)blkid2offset(dnp, bp, zb));
-
- ASSERT(zb->zb_level >= 0);
-
- for (l = dnp->dn_nlevels - 1; l >= -1; l--) {
- if (l == zb->zb_level) {
- (void) printf("L%llx", (u_longlong_t)zb->zb_level);
- } else {
- (void) printf(" ");
- }
- }
-
- snprintf_blkptr_compact(blkbuf, sizeof (blkbuf), bp);
- (void) printf("%s\n", blkbuf);
-}
-
-static int
-visit_indirect(spa_t *spa, const dnode_phys_t *dnp,
- blkptr_t *bp, const zbookmark_phys_t *zb)
-{
- int err = 0;
-
- if (bp->blk_birth == 0)
- return (0);
-
- print_indirect(bp, zb, dnp);
-
- if (BP_GET_LEVEL(bp) > 0 && !BP_IS_HOLE(bp)) {
- arc_flags_t flags = ARC_FLAG_WAIT;
- int i;
- blkptr_t *cbp;
- int epb = BP_GET_LSIZE(bp) >> SPA_BLKPTRSHIFT;
- arc_buf_t *buf;
- uint64_t fill = 0;
-
- err = arc_read(NULL, spa, bp, arc_getbuf_func, &buf,
- ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb);
- if (err)
- return (err);
- ASSERT(buf->b_data);
-
- /* recursively visit blocks below this */
- cbp = buf->b_data;
- for (i = 0; i < epb; i++, cbp++) {
- zbookmark_phys_t czb;
-
- SET_BOOKMARK(&czb, zb->zb_objset, zb->zb_object,
- zb->zb_level - 1,
- zb->zb_blkid * epb + i);
- err = visit_indirect(spa, dnp, cbp, &czb);
- if (err)
- break;
- fill += BP_GET_FILL(cbp);
- }
- if (!err)
- ASSERT3U(fill, ==, BP_GET_FILL(bp));
- arc_buf_destroy(buf, &buf);
- }
-
- return (err);
-}
-
-/*ARGSUSED*/
-static void
-dump_indirect(dnode_t *dn)
-{
- dnode_phys_t *dnp = dn->dn_phys;
- int j;
- zbookmark_phys_t czb;
-
- (void) printf("Indirect blocks:\n");
-
- SET_BOOKMARK(&czb, dmu_objset_id(dn->dn_objset),
- dn->dn_object, dnp->dn_nlevels - 1, 0);
- for (j = 0; j < dnp->dn_nblkptr; j++) {
- czb.zb_blkid = j;
- (void) visit_indirect(dmu_objset_spa(dn->dn_objset), dnp,
- &dnp->dn_blkptr[j], &czb);
- }
-
- (void) printf("\n");
-}
-
-/*ARGSUSED*/
-static void
-dump_dsl_dir(objset_t *os, uint64_t object, void *data, size_t size)
-{
- dsl_dir_phys_t *dd = data;
- time_t crtime;
- char nice[32];
-
- /* make sure nicenum has enough space */
- CTASSERT(sizeof (nice) >= NN_NUMBUF_SZ);
-
- if (dd == NULL)
- return;
-
- ASSERT3U(size, >=, sizeof (dsl_dir_phys_t));
-
- crtime = dd->dd_creation_time;
- (void) printf("\t\tcreation_time = %s", ctime(&crtime));
- (void) printf("\t\thead_dataset_obj = %llu\n",
- (u_longlong_t)dd->dd_head_dataset_obj);
- (void) printf("\t\tparent_dir_obj = %llu\n",
- (u_longlong_t)dd->dd_parent_obj);
- (void) printf("\t\torigin_obj = %llu\n",
- (u_longlong_t)dd->dd_origin_obj);
- (void) printf("\t\tchild_dir_zapobj = %llu\n",
- (u_longlong_t)dd->dd_child_dir_zapobj);
- zdb_nicenum(dd->dd_used_bytes, nice, sizeof (nice));
- (void) printf("\t\tused_bytes = %s\n", nice);
- zdb_nicenum(dd->dd_compressed_bytes, nice, sizeof (nice));
- (void) printf("\t\tcompressed_bytes = %s\n", nice);
- zdb_nicenum(dd->dd_uncompressed_bytes, nice, sizeof (nice));
- (void) printf("\t\tuncompressed_bytes = %s\n", nice);
- zdb_nicenum(dd->dd_quota, nice, sizeof (nice));
- (void) printf("\t\tquota = %s\n", nice);
- zdb_nicenum(dd->dd_reserved, nice, sizeof (nice));
- (void) printf("\t\treserved = %s\n", nice);
- (void) printf("\t\tprops_zapobj = %llu\n",
- (u_longlong_t)dd->dd_props_zapobj);
- (void) printf("\t\tdeleg_zapobj = %llu\n",
- (u_longlong_t)dd->dd_deleg_zapobj);
- (void) printf("\t\tflags = %llx\n",
- (u_longlong_t)dd->dd_flags);
-
-#define DO(which) \
- zdb_nicenum(dd->dd_used_breakdown[DD_USED_ ## which], nice, \
- sizeof (nice)); \
- (void) printf("\t\tused_breakdown[" #which "] = %s\n", nice)
- DO(HEAD);
- DO(SNAP);
- DO(CHILD);
- DO(CHILD_RSRV);
- DO(REFRSRV);
-#undef DO
- (void) printf("\t\tclones = %llu\n",
- (u_longlong_t)dd->dd_clones);
-}
-
-/*ARGSUSED*/
-static void
-dump_dsl_dataset(objset_t *os, uint64_t object, void *data, size_t size)
-{
- dsl_dataset_phys_t *ds = data;
- time_t crtime;
- char used[32], compressed[32], uncompressed[32], unique[32];
- char blkbuf[BP_SPRINTF_LEN];
-
- /* make sure nicenum has enough space */
- CTASSERT(sizeof (used) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (compressed) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (uncompressed) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (unique) >= NN_NUMBUF_SZ);
-
- if (ds == NULL)
- return;
-
- ASSERT(size == sizeof (*ds));
- crtime = ds->ds_creation_time;
- zdb_nicenum(ds->ds_referenced_bytes, used, sizeof (used));
- zdb_nicenum(ds->ds_compressed_bytes, compressed, sizeof (compressed));
- zdb_nicenum(ds->ds_uncompressed_bytes, uncompressed,
- sizeof (uncompressed));
- zdb_nicenum(ds->ds_unique_bytes, unique, sizeof (unique));
- snprintf_blkptr(blkbuf, sizeof (blkbuf), &ds->ds_bp);
-
- (void) printf("\t\tdir_obj = %llu\n",
- (u_longlong_t)ds->ds_dir_obj);
- (void) printf("\t\tprev_snap_obj = %llu\n",
- (u_longlong_t)ds->ds_prev_snap_obj);
- (void) printf("\t\tprev_snap_txg = %llu\n",
- (u_longlong_t)ds->ds_prev_snap_txg);
- (void) printf("\t\tnext_snap_obj = %llu\n",
- (u_longlong_t)ds->ds_next_snap_obj);
- (void) printf("\t\tsnapnames_zapobj = %llu\n",
- (u_longlong_t)ds->ds_snapnames_zapobj);
- (void) printf("\t\tnum_children = %llu\n",
- (u_longlong_t)ds->ds_num_children);
- (void) printf("\t\tuserrefs_obj = %llu\n",
- (u_longlong_t)ds->ds_userrefs_obj);
- (void) printf("\t\tcreation_time = %s", ctime(&crtime));
- (void) printf("\t\tcreation_txg = %llu\n",
- (u_longlong_t)ds->ds_creation_txg);
- (void) printf("\t\tdeadlist_obj = %llu\n",
- (u_longlong_t)ds->ds_deadlist_obj);
- (void) printf("\t\tused_bytes = %s\n", used);
- (void) printf("\t\tcompressed_bytes = %s\n", compressed);
- (void) printf("\t\tuncompressed_bytes = %s\n", uncompressed);
- (void) printf("\t\tunique = %s\n", unique);
- (void) printf("\t\tfsid_guid = %llu\n",
- (u_longlong_t)ds->ds_fsid_guid);
- (void) printf("\t\tguid = %llu\n",
- (u_longlong_t)ds->ds_guid);
- (void) printf("\t\tflags = %llx\n",
- (u_longlong_t)ds->ds_flags);
- (void) printf("\t\tnext_clones_obj = %llu\n",
- (u_longlong_t)ds->ds_next_clones_obj);
- (void) printf("\t\tprops_obj = %llu\n",
- (u_longlong_t)ds->ds_props_obj);
- (void) printf("\t\tbp = %s\n", blkbuf);
-}
-
-/* ARGSUSED */
-static int
-dump_bptree_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx)
-{
- char blkbuf[BP_SPRINTF_LEN];
-
- if (bp->blk_birth != 0) {
- snprintf_blkptr(blkbuf, sizeof (blkbuf), bp);
- (void) printf("\t%s\n", blkbuf);
- }
- return (0);
-}
-
-static void
-dump_bptree(objset_t *os, uint64_t obj, const char *name)
-{
- char bytes[32];
- bptree_phys_t *bt;
- dmu_buf_t *db;
-
- /* make sure nicenum has enough space */
- CTASSERT(sizeof (bytes) >= NN_NUMBUF_SZ);
-
- if (dump_opt['d'] < 3)
- return;
-
- VERIFY3U(0, ==, dmu_bonus_hold(os, obj, FTAG, &db));
- bt = db->db_data;
- zdb_nicenum(bt->bt_bytes, bytes, sizeof (bytes));
- (void) printf("\n %s: %llu datasets, %s\n",
- name, (unsigned long long)(bt->bt_end - bt->bt_begin), bytes);
- dmu_buf_rele(db, FTAG);
-
- if (dump_opt['d'] < 5)
- return;
-
- (void) printf("\n");
-
- (void) bptree_iterate(os, obj, B_FALSE, dump_bptree_cb, NULL, NULL);
-}
-
-/* ARGSUSED */
-static int
-dump_bpobj_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx)
-{
- char blkbuf[BP_SPRINTF_LEN];
-
- ASSERT(bp->blk_birth != 0);
- snprintf_blkptr_compact(blkbuf, sizeof (blkbuf), bp);
- (void) printf("\t%s\n", blkbuf);
- return (0);
-}
-
-static void
-dump_full_bpobj(bpobj_t *bpo, const char *name, int indent)
-{
- char bytes[32];
- char comp[32];
- char uncomp[32];
-
- /* make sure nicenum has enough space */
- CTASSERT(sizeof (bytes) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (comp) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (uncomp) >= NN_NUMBUF_SZ);
-
- if (dump_opt['d'] < 3)
- return;
-
- zdb_nicenum(bpo->bpo_phys->bpo_bytes, bytes, sizeof (bytes));
- if (bpo->bpo_havesubobj && bpo->bpo_phys->bpo_subobjs != 0) {
- zdb_nicenum(bpo->bpo_phys->bpo_comp, comp, sizeof (comp));
- zdb_nicenum(bpo->bpo_phys->bpo_uncomp, uncomp, sizeof (uncomp));
- (void) printf(" %*s: object %llu, %llu local blkptrs, "
- "%llu subobjs in object %llu, %s (%s/%s comp)\n",
- indent * 8, name,
- (u_longlong_t)bpo->bpo_object,
- (u_longlong_t)bpo->bpo_phys->bpo_num_blkptrs,
- (u_longlong_t)bpo->bpo_phys->bpo_num_subobjs,
- (u_longlong_t)bpo->bpo_phys->bpo_subobjs,
- bytes, comp, uncomp);
-
- for (uint64_t i = 0; i < bpo->bpo_phys->bpo_num_subobjs; i++) {
- uint64_t subobj;
- bpobj_t subbpo;
- int error;
- VERIFY0(dmu_read(bpo->bpo_os,
- bpo->bpo_phys->bpo_subobjs,
- i * sizeof (subobj), sizeof (subobj), &subobj, 0));
- error = bpobj_open(&subbpo, bpo->bpo_os, subobj);
- if (error != 0) {
- (void) printf("ERROR %u while trying to open "
- "subobj id %llu\n",
- error, (u_longlong_t)subobj);
- continue;
- }
- dump_full_bpobj(&subbpo, "subobj", indent + 1);
- bpobj_close(&subbpo);
- }
- } else {
- (void) printf(" %*s: object %llu, %llu blkptrs, %s\n",
- indent * 8, name,
- (u_longlong_t)bpo->bpo_object,
- (u_longlong_t)bpo->bpo_phys->bpo_num_blkptrs,
- bytes);
- }
-
- if (dump_opt['d'] < 5)
- return;
-
-
- if (indent == 0) {
- (void) bpobj_iterate_nofree(bpo, dump_bpobj_cb, NULL, NULL);
- (void) printf("\n");
- }
-}
-
-static void
-bpobj_count_refd(bpobj_t *bpo)
-{
- mos_obj_refd(bpo->bpo_object);
-
- if (bpo->bpo_havesubobj && bpo->bpo_phys->bpo_subobjs != 0) {
- mos_obj_refd(bpo->bpo_phys->bpo_subobjs);
- for (uint64_t i = 0; i < bpo->bpo_phys->bpo_num_subobjs; i++) {
- uint64_t subobj;
- bpobj_t subbpo;
- int error;
- VERIFY0(dmu_read(bpo->bpo_os,
- bpo->bpo_phys->bpo_subobjs,
- i * sizeof (subobj), sizeof (subobj), &subobj, 0));
- error = bpobj_open(&subbpo, bpo->bpo_os, subobj);
- if (error != 0) {
- (void) printf("ERROR %u while trying to open "
- "subobj id %llu\n",
- error, (u_longlong_t)subobj);
- continue;
- }
- bpobj_count_refd(&subbpo);
- bpobj_close(&subbpo);
- }
- }
-}
-
-static void
-dump_deadlist(dsl_deadlist_t *dl)
-{
- dsl_deadlist_entry_t *dle;
- uint64_t unused;
- char bytes[32];
- char comp[32];
- char uncomp[32];
- uint64_t empty_bpobj =
- dmu_objset_spa(dl->dl_os)->spa_dsl_pool->dp_empty_bpobj;
-
- /* force the tree to be loaded */
- dsl_deadlist_space_range(dl, 0, UINT64_MAX, &unused, &unused, &unused);
-
- if (dl->dl_oldfmt) {
- if (dl->dl_bpobj.bpo_object != empty_bpobj)
- bpobj_count_refd(&dl->dl_bpobj);
- } else {
- mos_obj_refd(dl->dl_object);
- for (dle = avl_first(&dl->dl_tree); dle;
- dle = AVL_NEXT(&dl->dl_tree, dle)) {
- if (dle->dle_bpobj.bpo_object != empty_bpobj)
- bpobj_count_refd(&dle->dle_bpobj);
- }
- }
-
- /* make sure nicenum has enough space */
- CTASSERT(sizeof (bytes) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (comp) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (uncomp) >= NN_NUMBUF_SZ);
-
- if (dump_opt['d'] < 3)
- return;
-
- if (dl->dl_oldfmt) {
- dump_full_bpobj(&dl->dl_bpobj, "old-format deadlist", 0);
- return;
- }
-
- zdb_nicenum(dl->dl_phys->dl_used, bytes, sizeof (bytes));
- zdb_nicenum(dl->dl_phys->dl_comp, comp, sizeof (comp));
- zdb_nicenum(dl->dl_phys->dl_uncomp, uncomp, sizeof (uncomp));
- (void) printf("\n Deadlist: %s (%s/%s comp)\n",
- bytes, comp, uncomp);
-
- if (dump_opt['d'] < 4)
- return;
-
- (void) printf("\n");
-
- for (dle = avl_first(&dl->dl_tree); dle;
- dle = AVL_NEXT(&dl->dl_tree, dle)) {
- if (dump_opt['d'] >= 5) {
- char buf[128];
- (void) snprintf(buf, sizeof (buf),
- "mintxg %llu -> obj %llu",
- (longlong_t)dle->dle_mintxg,
- (longlong_t)dle->dle_bpobj.bpo_object);
- dump_full_bpobj(&dle->dle_bpobj, buf, 0);
- } else {
- (void) printf("mintxg %llu -> obj %llu\n",
- (longlong_t)dle->dle_mintxg,
- (longlong_t)dle->dle_bpobj.bpo_object);
- }
- }
-}
-
-static avl_tree_t idx_tree;
-static avl_tree_t domain_tree;
-static boolean_t fuid_table_loaded;
-static objset_t *sa_os = NULL;
-static sa_attr_type_t *sa_attr_table = NULL;
-
-static int
-open_objset(const char *path, dmu_objset_type_t type, void *tag, objset_t **osp)
-{
- int err;
- uint64_t sa_attrs = 0;
- uint64_t version = 0;
-
- VERIFY3P(sa_os, ==, NULL);
- err = dmu_objset_own(path, type, B_TRUE, tag, osp);
- if (err != 0) {
- (void) fprintf(stderr, "failed to own dataset '%s': %s\n", path,
- strerror(err));
- return (err);
- }
-
- if (dmu_objset_type(*osp) == DMU_OST_ZFS) {
- (void) zap_lookup(*osp, MASTER_NODE_OBJ, ZPL_VERSION_STR,
- 8, 1, &version);
- if (version >= ZPL_VERSION_SA) {
- (void) zap_lookup(*osp, MASTER_NODE_OBJ, ZFS_SA_ATTRS,
- 8, 1, &sa_attrs);
- }
- err = sa_setup(*osp, sa_attrs, zfs_attr_table, ZPL_END,
- &sa_attr_table);
- if (err != 0) {
- (void) fprintf(stderr, "sa_setup failed: %s\n",
- strerror(err));
- dmu_objset_disown(*osp, tag);
- *osp = NULL;
- }
- }
- sa_os = *osp;
-
- return (0);
-}
-
-static void
-close_objset(objset_t *os, void *tag)
-{
- VERIFY3P(os, ==, sa_os);
- if (os->os_sa != NULL)
- sa_tear_down(os);
- dmu_objset_disown(os, tag);
- sa_attr_table = NULL;
- sa_os = NULL;
-}
-
-static void
-fuid_table_destroy()
-{
- if (fuid_table_loaded) {
- zfs_fuid_table_destroy(&idx_tree, &domain_tree);
- fuid_table_loaded = B_FALSE;
- }
-}
-
-/*
- * print uid or gid information.
- * For normal POSIX id just the id is printed in decimal format.
- * For CIFS files with FUID the fuid is printed in hex followed by
- * the domain-rid string.
- */
-static void
-print_idstr(uint64_t id, const char *id_type)
-{
- if (FUID_INDEX(id)) {
- char *domain;
-
- domain = zfs_fuid_idx_domain(&idx_tree, FUID_INDEX(id));
- (void) printf("\t%s %llx [%s-%d]\n", id_type,
- (u_longlong_t)id, domain, (int)FUID_RID(id));
- } else {
- (void) printf("\t%s %llu\n", id_type, (u_longlong_t)id);
- }
-
-}
-
-static void
-dump_uidgid(objset_t *os, uint64_t uid, uint64_t gid)
-{
- uint32_t uid_idx, gid_idx;
-
- uid_idx = FUID_INDEX(uid);
- gid_idx = FUID_INDEX(gid);
-
- /* Load domain table, if not already loaded */
- if (!fuid_table_loaded && (uid_idx || gid_idx)) {
- uint64_t fuid_obj;
-
- /* first find the fuid object. It lives in the master node */
- VERIFY(zap_lookup(os, MASTER_NODE_OBJ, ZFS_FUID_TABLES,
- 8, 1, &fuid_obj) == 0);
- zfs_fuid_avl_tree_create(&idx_tree, &domain_tree);
- (void) zfs_fuid_table_load(os, fuid_obj,
- &idx_tree, &domain_tree);
- fuid_table_loaded = B_TRUE;
- }
-
- print_idstr(uid, "uid");
- print_idstr(gid, "gid");
-}
-
-/*ARGSUSED*/
-static void
-dump_znode(objset_t *os, uint64_t object, void *data, size_t size)
-{
- char path[MAXPATHLEN * 2]; /* allow for xattr and failure prefix */
- sa_handle_t *hdl;
- uint64_t xattr, rdev, gen;
- uint64_t uid, gid, mode, fsize, parent, links;
- uint64_t pflags;
- uint64_t acctm[2], modtm[2], chgtm[2], crtm[2];
- time_t z_crtime, z_atime, z_mtime, z_ctime;
- sa_bulk_attr_t bulk[12];
- int idx = 0;
- int error;
-
- VERIFY3P(os, ==, sa_os);
- if (sa_handle_get(os, object, NULL, SA_HDL_PRIVATE, &hdl)) {
- (void) printf("Failed to get handle for SA znode\n");
- return;
- }
-
- SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_UID], NULL, &uid, 8);
- SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_GID], NULL, &gid, 8);
- SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_LINKS], NULL,
- &links, 8);
- SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_GEN], NULL, &gen, 8);
- SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_MODE], NULL,
- &mode, 8);
- SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_PARENT],
- NULL, &parent, 8);
- SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_SIZE], NULL,
- &fsize, 8);
- SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_ATIME], NULL,
- acctm, 16);
- SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_MTIME], NULL,
- modtm, 16);
- SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_CRTIME], NULL,
- crtm, 16);
- SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_CTIME], NULL,
- chgtm, 16);
- SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_FLAGS], NULL,
- &pflags, 8);
-
- if (sa_bulk_lookup(hdl, bulk, idx)) {
- (void) sa_handle_destroy(hdl);
- return;
- }
-
- z_crtime = (time_t)crtm[0];
- z_atime = (time_t)acctm[0];
- z_mtime = (time_t)modtm[0];
- z_ctime = (time_t)chgtm[0];
-
- if (dump_opt['d'] > 4) {
- error = zfs_obj_to_path(os, object, path, sizeof (path));
- if (error == ESTALE) {
- (void) snprintf(path, sizeof (path), "on delete queue");
- } else if (error != 0) {
- leaked_objects++;
- (void) snprintf(path, sizeof (path),
- "path not found, possibly leaked");
- }
- (void) printf("\tpath %s\n", path);
- }
- dump_uidgid(os, uid, gid);
- (void) printf("\tatime %s", ctime(&z_atime));
- (void) printf("\tmtime %s", ctime(&z_mtime));
- (void) printf("\tctime %s", ctime(&z_ctime));
- (void) printf("\tcrtime %s", ctime(&z_crtime));
- (void) printf("\tgen %llu\n", (u_longlong_t)gen);
- (void) printf("\tmode %llo\n", (u_longlong_t)mode);
- (void) printf("\tsize %llu\n", (u_longlong_t)fsize);
- (void) printf("\tparent %llu\n", (u_longlong_t)parent);
- (void) printf("\tlinks %llu\n", (u_longlong_t)links);
- (void) printf("\tpflags %llx\n", (u_longlong_t)pflags);
- if (sa_lookup(hdl, sa_attr_table[ZPL_XATTR], &xattr,
- sizeof (uint64_t)) == 0)
- (void) printf("\txattr %llu\n", (u_longlong_t)xattr);
- if (sa_lookup(hdl, sa_attr_table[ZPL_RDEV], &rdev,
- sizeof (uint64_t)) == 0)
- (void) printf("\trdev 0x%016llx\n", (u_longlong_t)rdev);
- sa_handle_destroy(hdl);
-}
-
-/*ARGSUSED*/
-static void
-dump_acl(objset_t *os, uint64_t object, void *data, size_t size)
-{
-}
-
-/*ARGSUSED*/
-static void
-dump_dmu_objset(objset_t *os, uint64_t object, void *data, size_t size)
-{
-}
-
-static object_viewer_t *object_viewer[DMU_OT_NUMTYPES + 1] = {
- dump_none, /* unallocated */
- dump_zap, /* object directory */
- dump_uint64, /* object array */
- dump_none, /* packed nvlist */
- dump_packed_nvlist, /* packed nvlist size */
- dump_none, /* bpobj */
- dump_bpobj, /* bpobj header */
- dump_none, /* SPA space map header */
- dump_none, /* SPA space map */
- dump_none, /* ZIL intent log */
- dump_dnode, /* DMU dnode */
- dump_dmu_objset, /* DMU objset */
- dump_dsl_dir, /* DSL directory */
- dump_zap, /* DSL directory child map */
- dump_zap, /* DSL dataset snap map */
- dump_zap, /* DSL props */
- dump_dsl_dataset, /* DSL dataset */
- dump_znode, /* ZFS znode */
- dump_acl, /* ZFS V0 ACL */
- dump_uint8, /* ZFS plain file */
- dump_zpldir, /* ZFS directory */
- dump_zap, /* ZFS master node */
- dump_zap, /* ZFS delete queue */
- dump_uint8, /* zvol object */
- dump_zap, /* zvol prop */
- dump_uint8, /* other uint8[] */
- dump_uint64, /* other uint64[] */
- dump_zap, /* other ZAP */
- dump_zap, /* persistent error log */
- dump_uint8, /* SPA history */
- dump_history_offsets, /* SPA history offsets */
- dump_zap, /* Pool properties */
- dump_zap, /* DSL permissions */
- dump_acl, /* ZFS ACL */
- dump_uint8, /* ZFS SYSACL */
- dump_none, /* FUID nvlist */
- dump_packed_nvlist, /* FUID nvlist size */
- dump_zap, /* DSL dataset next clones */
- dump_zap, /* DSL scrub queue */
- dump_zap, /* ZFS user/group used */
- dump_zap, /* ZFS user/group quota */
- dump_zap, /* snapshot refcount tags */
- dump_ddt_zap, /* DDT ZAP object */
- dump_zap, /* DDT statistics */
- dump_znode, /* SA object */
- dump_zap, /* SA Master Node */
- dump_sa_attrs, /* SA attribute registration */
- dump_sa_layouts, /* SA attribute layouts */
- dump_zap, /* DSL scrub translations */
- dump_none, /* fake dedup BP */
- dump_zap, /* deadlist */
- dump_none, /* deadlist hdr */
- dump_zap, /* dsl clones */
- dump_bpobj_subobjs, /* bpobj subobjs */
- dump_unknown, /* Unknown type, must be last */
-};
-
-static void
-dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header,
- uint64_t *dnode_slots_used)
-{
- dmu_buf_t *db = NULL;
- dmu_object_info_t doi;
- dnode_t *dn;
- void *bonus = NULL;
- size_t bsize = 0;
- char iblk[32], dblk[32], lsize[32], asize[32], fill[32], dnsize[32];
- char bonus_size[32];
- char aux[50];
- int error;
-
- /* make sure nicenum has enough space */
- CTASSERT(sizeof (iblk) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (dblk) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (lsize) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (asize) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (bonus_size) >= NN_NUMBUF_SZ);
-
- if (*print_header) {
- (void) printf("\n%10s %3s %5s %5s %5s %6s %5s %6s %s\n",
- "Object", "lvl", "iblk", "dblk", "dsize", "dnsize",
- "lsize", "%full", "type");
- *print_header = 0;
- }
-
- if (object == 0) {
- dn = DMU_META_DNODE(os);
- } else {
- error = dmu_bonus_hold(os, object, FTAG, &db);
- if (error)
- fatal("dmu_bonus_hold(%llu) failed, errno %u",
- object, error);
- bonus = db->db_data;
- bsize = db->db_size;
- dn = DB_DNODE((dmu_buf_impl_t *)db);
- }
- dmu_object_info_from_dnode(dn, &doi);
-
- if (dnode_slots_used != NULL)
- *dnode_slots_used = doi.doi_dnodesize / DNODE_MIN_SIZE;
-
- zdb_nicenum(doi.doi_metadata_block_size, iblk, sizeof (iblk));
- zdb_nicenum(doi.doi_data_block_size, dblk, sizeof (dblk));
- zdb_nicenum(doi.doi_max_offset, lsize, sizeof (lsize));
- zdb_nicenum(doi.doi_physical_blocks_512 << 9, asize, sizeof (asize));
- zdb_nicenum(doi.doi_bonus_size, bonus_size, sizeof (bonus_size));
- zdb_nicenum(doi.doi_dnodesize, dnsize, sizeof (dnsize));
- (void) sprintf(fill, "%6.2f", 100.0 * doi.doi_fill_count *
- doi.doi_data_block_size / (object == 0 ? DNODES_PER_BLOCK : 1) /
- doi.doi_max_offset);
-
- aux[0] = '\0';
-
- if (doi.doi_checksum != ZIO_CHECKSUM_INHERIT || verbosity >= 6) {
- (void) snprintf(aux + strlen(aux), sizeof (aux), " (K=%s)",
- ZDB_CHECKSUM_NAME(doi.doi_checksum));
- }
-
- if (doi.doi_compress != ZIO_COMPRESS_INHERIT || verbosity >= 6) {
- (void) snprintf(aux + strlen(aux), sizeof (aux), " (Z=%s)",
- ZDB_COMPRESS_NAME(doi.doi_compress));
- }
-
- (void) printf("%10" PRIu64
- " %3u %5s %5s %5s %5s %5s %6s %s%s\n",
- object, doi.doi_indirection, iblk, dblk,
- asize, dnsize, lsize, fill, ZDB_OT_NAME(doi.doi_type), aux);
-
- if (doi.doi_bonus_type != DMU_OT_NONE && verbosity > 3) {
- (void) printf("%10s %3s %5s %5s %5s %5s %5s %6s %s\n",
- "", "", "", "", "", "", bonus_size, "bonus",
- ZDB_OT_NAME(doi.doi_bonus_type));
- }
-
- if (verbosity >= 4) {
- (void) printf("\tdnode flags: %s%s%s\n",
- (dn->dn_phys->dn_flags & DNODE_FLAG_USED_BYTES) ?
- "USED_BYTES " : "",
- (dn->dn_phys->dn_flags & DNODE_FLAG_USERUSED_ACCOUNTED) ?
- "USERUSED_ACCOUNTED " : "",
- (dn->dn_phys->dn_flags & DNODE_FLAG_SPILL_BLKPTR) ?
- "SPILL_BLKPTR" : "");
- (void) printf("\tdnode maxblkid: %llu\n",
- (longlong_t)dn->dn_phys->dn_maxblkid);
-
- object_viewer[ZDB_OT_TYPE(doi.doi_bonus_type)](os, object,
- bonus, bsize);
- object_viewer[ZDB_OT_TYPE(doi.doi_type)](os, object, NULL, 0);
- *print_header = 1;
- }
-
- if (verbosity >= 5)
- dump_indirect(dn);
-
- if (verbosity >= 5) {
- /*
- * Report the list of segments that comprise the object.
- */
- uint64_t start = 0;
- uint64_t end;
- uint64_t blkfill = 1;
- int minlvl = 1;
-
- if (dn->dn_type == DMU_OT_DNODE) {
- minlvl = 0;
- blkfill = DNODES_PER_BLOCK;
- }
-
- for (;;) {
- char segsize[32];
- /* make sure nicenum has enough space */
- CTASSERT(sizeof (segsize) >= NN_NUMBUF_SZ);
- error = dnode_next_offset(dn,
- 0, &start, minlvl, blkfill, 0);
- if (error)
- break;
- end = start;
- error = dnode_next_offset(dn,
- DNODE_FIND_HOLE, &end, minlvl, blkfill, 0);
- zdb_nicenum(end - start, segsize, sizeof (segsize));
- (void) printf("\t\tsegment [%016llx, %016llx)"
- " size %5s\n", (u_longlong_t)start,
- (u_longlong_t)end, segsize);
- if (error)
- break;
- start = end;
- }
- }
-
- if (db != NULL)
- dmu_buf_rele(db, FTAG);
-}
-
-static void
-count_dir_mos_objects(dsl_dir_t *dd)
-{
- mos_obj_refd(dd->dd_object);
- mos_obj_refd(dsl_dir_phys(dd)->dd_child_dir_zapobj);
- mos_obj_refd(dsl_dir_phys(dd)->dd_deleg_zapobj);
- mos_obj_refd(dsl_dir_phys(dd)->dd_props_zapobj);
- mos_obj_refd(dsl_dir_phys(dd)->dd_clones);
-}
-
-static void
-count_ds_mos_objects(dsl_dataset_t *ds)
-{
- mos_obj_refd(ds->ds_object);
- mos_obj_refd(dsl_dataset_phys(ds)->ds_next_clones_obj);
- mos_obj_refd(dsl_dataset_phys(ds)->ds_props_obj);
- mos_obj_refd(dsl_dataset_phys(ds)->ds_userrefs_obj);
- mos_obj_refd(dsl_dataset_phys(ds)->ds_snapnames_zapobj);
-
- if (!dsl_dataset_is_snapshot(ds)) {
- count_dir_mos_objects(ds->ds_dir);
- }
-}
-
-static const char *objset_types[DMU_OST_NUMTYPES] = {
- "NONE", "META", "ZPL", "ZVOL", "OTHER", "ANY" };
-
-static void
-dump_dir(objset_t *os)
-{
- dmu_objset_stats_t dds;
- uint64_t object, object_count;
- uint64_t refdbytes, usedobjs, scratch;
- char numbuf[32];
- char blkbuf[BP_SPRINTF_LEN + 20];
- char osname[ZFS_MAX_DATASET_NAME_LEN];
- const char *type = "UNKNOWN";
- int verbosity = dump_opt['d'];
- int print_header = 1;
- unsigned i;
- int error;
- uint64_t total_slots_used = 0;
- uint64_t max_slot_used = 0;
- uint64_t dnode_slots;
-
- /* make sure nicenum has enough space */
- CTASSERT(sizeof (numbuf) >= NN_NUMBUF_SZ);
-
- dsl_pool_config_enter(dmu_objset_pool(os), FTAG);
- dmu_objset_fast_stat(os, &dds);
- dsl_pool_config_exit(dmu_objset_pool(os), FTAG);
-
- if (dds.dds_type < DMU_OST_NUMTYPES)
- type = objset_types[dds.dds_type];
-
- if (dds.dds_type == DMU_OST_META) {
- dds.dds_creation_txg = TXG_INITIAL;
- usedobjs = BP_GET_FILL(os->os_rootbp);
- refdbytes = dsl_dir_phys(os->os_spa->spa_dsl_pool->dp_mos_dir)->
- dd_used_bytes;
- } else {
- dmu_objset_space(os, &refdbytes, &scratch, &usedobjs, &scratch);
- }
-
- ASSERT3U(usedobjs, ==, BP_GET_FILL(os->os_rootbp));
-
- zdb_nicenum(refdbytes, numbuf, sizeof (numbuf));
-
- if (verbosity >= 4) {
- (void) snprintf(blkbuf, sizeof (blkbuf), ", rootbp ");
- (void) snprintf_blkptr(blkbuf + strlen(blkbuf),
- sizeof (blkbuf) - strlen(blkbuf), os->os_rootbp);
- } else {
- blkbuf[0] = '\0';
- }
-
- dmu_objset_name(os, osname);
-
- (void) printf("Dataset %s [%s], ID %llu, cr_txg %llu, "
- "%s, %llu objects%s%s\n",
- osname, type, (u_longlong_t)dmu_objset_id(os),
- (u_longlong_t)dds.dds_creation_txg,
- numbuf, (u_longlong_t)usedobjs, blkbuf,
- (dds.dds_inconsistent) ? " (inconsistent)" : "");
-
- if (zopt_objects != 0) {
- for (i = 0; i < zopt_objects; i++)
- dump_object(os, zopt_object[i], verbosity,
- &print_header, NULL);
- (void) printf("\n");
- return;
- }
-
- if (dump_opt['i'] != 0 || verbosity >= 2)
- dump_intent_log(dmu_objset_zil(os));
-
- if (dmu_objset_ds(os) != NULL) {
- dsl_dataset_t *ds = dmu_objset_ds(os);
- dump_deadlist(&ds->ds_deadlist);
-
- if (dsl_dataset_remap_deadlist_exists(ds)) {
- (void) printf("ds_remap_deadlist:\n");
- dump_deadlist(&ds->ds_remap_deadlist);
- }
- count_ds_mos_objects(ds);
- }
-
- if (verbosity < 2)
- return;
-
- if (BP_IS_HOLE(os->os_rootbp))
- return;
-
- dump_object(os, 0, verbosity, &print_header, NULL);
- object_count = 0;
- if (DMU_USERUSED_DNODE(os) != NULL &&
- DMU_USERUSED_DNODE(os)->dn_type != 0) {
- dump_object(os, DMU_USERUSED_OBJECT, verbosity, &print_header,
- NULL);
- dump_object(os, DMU_GROUPUSED_OBJECT, verbosity, &print_header,
- NULL);
- }
-
- object = 0;
- while ((error = dmu_object_next(os, &object, B_FALSE, 0)) == 0) {
- dump_object(os, object, verbosity, &print_header, &dnode_slots);
- object_count++;
- total_slots_used += dnode_slots;
- max_slot_used = object + dnode_slots - 1;
- }
-
- (void) printf("\n");
-
- (void) printf(" Dnode slots:\n");
- (void) printf("\tTotal used: %10llu\n",
- (u_longlong_t)total_slots_used);
- (void) printf("\tMax used: %10llu\n",
- (u_longlong_t)max_slot_used);
- (void) printf("\tPercent empty: %10lf\n",
- (double)(max_slot_used - total_slots_used)*100 /
- (double)max_slot_used);
-
- (void) printf("\n");
-
- if (error != ESRCH) {
- (void) fprintf(stderr, "dmu_object_next() = %d\n", error);
- abort();
- }
-
- ASSERT3U(object_count, ==, usedobjs);
-
- if (leaked_objects != 0) {
- (void) printf("%d potentially leaked objects detected\n",
- leaked_objects);
- leaked_objects = 0;
- }
-}
-
-static void
-dump_uberblock(uberblock_t *ub, const char *header, const char *footer)
-{
- time_t timestamp = ub->ub_timestamp;
-
- (void) printf("%s", header ? header : "");
- (void) printf("\tmagic = %016llx\n", (u_longlong_t)ub->ub_magic);
- (void) printf("\tversion = %llu\n", (u_longlong_t)ub->ub_version);
- (void) printf("\ttxg = %llu\n", (u_longlong_t)ub->ub_txg);
- (void) printf("\tguid_sum = %llu\n", (u_longlong_t)ub->ub_guid_sum);
- (void) printf("\ttimestamp = %llu UTC = %s",
- (u_longlong_t)ub->ub_timestamp, asctime(localtime(&timestamp)));
-
- (void) printf("\tmmp_magic = %016llx\n",
- (u_longlong_t)ub->ub_mmp_magic);
- if (MMP_VALID(ub)) {
- (void) printf("\tmmp_delay = %0llu\n",
- (u_longlong_t)ub->ub_mmp_delay);
- if (MMP_SEQ_VALID(ub))
- (void) printf("\tmmp_seq = %u\n",
- (unsigned int) MMP_SEQ(ub));
- if (MMP_FAIL_INT_VALID(ub))
- (void) printf("\tmmp_fail = %u\n",
- (unsigned int) MMP_FAIL_INT(ub));
- if (MMP_INTERVAL_VALID(ub))
- (void) printf("\tmmp_write = %u\n",
- (unsigned int) MMP_INTERVAL(ub));
- /* After MMP_* to make summarize_uberblock_mmp cleaner */
- (void) printf("\tmmp_valid = %x\n",
- (unsigned int) ub->ub_mmp_config & 0xFF);
- }
-
- if (dump_opt['u'] >= 3) {
- char blkbuf[BP_SPRINTF_LEN];
- snprintf_blkptr(blkbuf, sizeof (blkbuf), &ub->ub_rootbp);
- (void) printf("\trootbp = %s\n", blkbuf);
- }
- (void) printf("\tcheckpoint_txg = %llu\n",
- (u_longlong_t)ub->ub_checkpoint_txg);
- (void) printf("%s", footer ? footer : "");
-}
-
-static void
-dump_config(spa_t *spa)
-{
- dmu_buf_t *db;
- size_t nvsize = 0;
- int error = 0;
-
-
- error = dmu_bonus_hold(spa->spa_meta_objset,
- spa->spa_config_object, FTAG, &db);
-
- if (error == 0) {
- nvsize = *(uint64_t *)db->db_data;
- dmu_buf_rele(db, FTAG);
-
- (void) printf("\nMOS Configuration:\n");
- dump_packed_nvlist(spa->spa_meta_objset,
- spa->spa_config_object, (void *)&nvsize, 1);
- } else {
- (void) fprintf(stderr, "dmu_bonus_hold(%llu) failed, errno %d",
- (u_longlong_t)spa->spa_config_object, error);
- }
-}
-
-static void
-dump_cachefile(const char *cachefile)
-{
- int fd;
- struct stat64 statbuf;
- char *buf;
- nvlist_t *config;
-
- if ((fd = open64(cachefile, O_RDONLY)) < 0) {
- (void) fprintf(stderr, "cannot open '%s': %s\n", cachefile,
- strerror(errno));
- exit(1);
- }
-
- if (fstat64(fd, &statbuf) != 0) {
- (void) fprintf(stderr, "failed to stat '%s': %s\n", cachefile,
- strerror(errno));
- exit(1);
- }
-
- if ((buf = malloc(statbuf.st_size)) == NULL) {
- (void) fprintf(stderr, "failed to allocate %llu bytes\n",
- (u_longlong_t)statbuf.st_size);
- exit(1);
- }
-
- if (read(fd, buf, statbuf.st_size) != statbuf.st_size) {
- (void) fprintf(stderr, "failed to read %llu bytes\n",
- (u_longlong_t)statbuf.st_size);
- exit(1);
- }
-
- (void) close(fd);
-
- if (nvlist_unpack(buf, statbuf.st_size, &config, 0) != 0) {
- (void) fprintf(stderr, "failed to unpack nvlist\n");
- exit(1);
- }
-
- free(buf);
-
- dump_nvlist(config, 0);
-
- nvlist_free(config);
-}
-
-#define ZDB_MAX_UB_HEADER_SIZE 32
-
-static void
-dump_label_uberblocks(vdev_label_t *lbl, uint64_t ashift)
-{
- vdev_t vd;
- vdev_t *vdp = &vd;
- char header[ZDB_MAX_UB_HEADER_SIZE];
-
- vd.vdev_ashift = ashift;
- vdp->vdev_top = vdp;
-
- for (int i = 0; i < VDEV_UBERBLOCK_COUNT(vdp); i++) {
- uint64_t uoff = VDEV_UBERBLOCK_OFFSET(vdp, i);
- uberblock_t *ub = (void *)((char *)lbl + uoff);
-
- if (uberblock_verify(ub))
- continue;
-
- if ((dump_opt['u'] < 4) &&
- (ub->ub_mmp_magic == MMP_MAGIC) && ub->ub_mmp_delay &&
- (i >= VDEV_UBERBLOCK_COUNT(&vd) - MMP_BLOCKS_PER_LABEL))
- continue;
-
- (void) snprintf(header, ZDB_MAX_UB_HEADER_SIZE,
- "Uberblock[%d]\n", i);
- dump_uberblock(ub, header, "");
- }
-}
-
-static char curpath[PATH_MAX];
-
-/*
- * Iterate through the path components, recursively passing
- * current one's obj and remaining path until we find the obj
- * for the last one.
- */
-static int
-dump_path_impl(objset_t *os, uint64_t obj, char *name)
-{
- int err;
- int header = 1;
- uint64_t child_obj;
- char *s;
- dmu_buf_t *db;
- dmu_object_info_t doi;
-
- if ((s = strchr(name, '/')) != NULL)
- *s = '\0';
- err = zap_lookup(os, obj, name, 8, 1, &child_obj);
-
- (void) strlcat(curpath, name, sizeof (curpath));
-
- if (err != 0) {
- (void) fprintf(stderr, "failed to lookup %s: %s\n",
- curpath, strerror(err));
- return (err);
- }
-
- child_obj = ZFS_DIRENT_OBJ(child_obj);
- err = sa_buf_hold(os, child_obj, FTAG, &db);
- if (err != 0) {
- (void) fprintf(stderr,
- "failed to get SA dbuf for obj %llu: %s\n",
- (u_longlong_t)child_obj, strerror(err));
- return (EINVAL);
- }
- dmu_object_info_from_db(db, &doi);
- sa_buf_rele(db, FTAG);
-
- if (doi.doi_bonus_type != DMU_OT_SA &&
- doi.doi_bonus_type != DMU_OT_ZNODE) {
- (void) fprintf(stderr, "invalid bonus type %d for obj %llu\n",
- doi.doi_bonus_type, (u_longlong_t)child_obj);
- return (EINVAL);
- }
-
- if (dump_opt['v'] > 6) {
- (void) printf("obj=%llu %s type=%d bonustype=%d\n",
- (u_longlong_t)child_obj, curpath, doi.doi_type,
- doi.doi_bonus_type);
- }
-
- (void) strlcat(curpath, "/", sizeof (curpath));
-
- switch (doi.doi_type) {
- case DMU_OT_DIRECTORY_CONTENTS:
- if (s != NULL && *(s + 1) != '\0')
- return (dump_path_impl(os, child_obj, s + 1));
- /*FALLTHROUGH*/
- case DMU_OT_PLAIN_FILE_CONTENTS:
- dump_object(os, child_obj, dump_opt['v'], &header, NULL);
- return (0);
- default:
- (void) fprintf(stderr, "object %llu has non-file/directory "
- "type %d\n", (u_longlong_t)obj, doi.doi_type);
- break;
- }
-
- return (EINVAL);
-}
-
-/*
- * Dump the blocks for the object specified by path inside the dataset.
- */
-static int
-dump_path(char *ds, char *path)
-{
- int err;
- objset_t *os;
- uint64_t root_obj;
-
- err = open_objset(ds, DMU_OST_ZFS, FTAG, &os);
- if (err != 0)
- return (err);
-
- err = zap_lookup(os, MASTER_NODE_OBJ, ZFS_ROOT_OBJ, 8, 1, &root_obj);
- if (err != 0) {
- (void) fprintf(stderr, "can't lookup root znode: %s\n",
- strerror(err));
- dmu_objset_disown(os, FTAG);
- return (EINVAL);
- }
-
- (void) snprintf(curpath, sizeof (curpath), "dataset=%s path=/", ds);
-
- err = dump_path_impl(os, root_obj, path);
-
- close_objset(os, FTAG);
- return (err);
-}
-
-static int
-dump_label(const char *dev)
-{
- int fd;
- vdev_label_t label;
- char path[MAXPATHLEN];
- char *buf = label.vl_vdev_phys.vp_nvlist;
- size_t buflen = sizeof (label.vl_vdev_phys.vp_nvlist);
- struct stat64 statbuf;
- uint64_t psize, ashift;
- boolean_t label_found = B_FALSE;
-
- (void) strlcpy(path, dev, sizeof (path));
- if (dev[0] == '/') {
- if (strncmp(dev, ZFS_DISK_ROOTD,
- strlen(ZFS_DISK_ROOTD)) == 0) {
- (void) snprintf(path, sizeof (path), "%s%s",
- ZFS_RDISK_ROOTD, dev + strlen(ZFS_DISK_ROOTD));
- }
- } else if (stat64(path, &statbuf) != 0) {
- char *s;
-
- (void) snprintf(path, sizeof (path), "%s%s", ZFS_RDISK_ROOTD,
- dev);
- if (((s = strrchr(dev, 's')) == NULL &&
- (s = strchr(dev, 'p')) == NULL) ||
- !isdigit(*(s + 1)))
- (void) strlcat(path, "s0", sizeof (path));
- }
-
- if ((fd = open64(path, O_RDONLY)) < 0) {
- (void) fprintf(stderr, "cannot open '%s': %s\n", path,
- strerror(errno));
- exit(1);
- }
-
- if (fstat64(fd, &statbuf) != 0) {
- (void) fprintf(stderr, "failed to stat '%s': %s\n", path,
- strerror(errno));
- (void) close(fd);
- exit(1);
- }
-
- if (S_ISBLK(statbuf.st_mode)) {
- (void) fprintf(stderr,
- "cannot use '%s': character device required\n", path);
- (void) close(fd);
- exit(1);
- }
-
- psize = statbuf.st_size;
- psize = P2ALIGN(psize, (uint64_t)sizeof (vdev_label_t));
-
- for (int l = 0; l < VDEV_LABELS; l++) {
- nvlist_t *config = NULL;
-
- if (!dump_opt['q']) {
- (void) printf("------------------------------------\n");
- (void) printf("LABEL %d\n", l);
- (void) printf("------------------------------------\n");
- }
-
- if (pread64(fd, &label, sizeof (label),
- vdev_label_offset(psize, l, 0)) != sizeof (label)) {
- if (!dump_opt['q'])
- (void) printf("failed to read label %d\n", l);
- continue;
- }
-
- if (nvlist_unpack(buf, buflen, &config, 0) != 0) {
- if (!dump_opt['q'])
- (void) printf("failed to unpack label %d\n", l);
- ashift = SPA_MINBLOCKSHIFT;
- } else {
- nvlist_t *vdev_tree = NULL;
-
- if (!dump_opt['q'])
- dump_nvlist(config, 4);
- if ((nvlist_lookup_nvlist(config,
- ZPOOL_CONFIG_VDEV_TREE, &vdev_tree) != 0) ||
- (nvlist_lookup_uint64(vdev_tree,
- ZPOOL_CONFIG_ASHIFT, &ashift) != 0))
- ashift = SPA_MINBLOCKSHIFT;
- nvlist_free(config);
- label_found = B_TRUE;
- }
- if (dump_opt['u'])
- dump_label_uberblocks(&label, ashift);
- }
-
- (void) close(fd);
-
- return (label_found ? 0 : 2);
-}
-
-static uint64_t dataset_feature_count[SPA_FEATURES];
-static uint64_t remap_deadlist_count = 0;
-
-/*ARGSUSED*/
-static int
-dump_one_dir(const char *dsname, void *arg)
-{
- int error;
- objset_t *os;
-
- error = open_objset(dsname, DMU_OST_ANY, FTAG, &os);
- if (error != 0)
- return (0);
-
- for (spa_feature_t f = 0; f < SPA_FEATURES; f++) {
- if (!dmu_objset_ds(os)->ds_feature_inuse[f])
- continue;
- ASSERT(spa_feature_table[f].fi_flags &
- ZFEATURE_FLAG_PER_DATASET);
- dataset_feature_count[f]++;
- }
-
- if (dsl_dataset_remap_deadlist_exists(dmu_objset_ds(os))) {
- remap_deadlist_count++;
- }
-
- dump_dir(os);
- close_objset(os, FTAG);
- fuid_table_destroy();
- return (0);
-}
-
-/*
- * Block statistics.
- */
-#define PSIZE_HISTO_SIZE (SPA_OLD_MAXBLOCKSIZE / SPA_MINBLOCKSIZE + 2)
-typedef struct zdb_blkstats {
- uint64_t zb_asize;
- uint64_t zb_lsize;
- uint64_t zb_psize;
- uint64_t zb_count;
- uint64_t zb_gangs;
- uint64_t zb_ditto_samevdev;
- uint64_t zb_ditto_same_ms;
- uint64_t zb_psize_histogram[PSIZE_HISTO_SIZE];
-} zdb_blkstats_t;
-
-/*
- * Extended object types to report deferred frees and dedup auto-ditto blocks.
- */
-#define ZDB_OT_DEFERRED (DMU_OT_NUMTYPES + 0)
-#define ZDB_OT_DITTO (DMU_OT_NUMTYPES + 1)
-#define ZDB_OT_OTHER (DMU_OT_NUMTYPES + 2)
-#define ZDB_OT_TOTAL (DMU_OT_NUMTYPES + 3)
-
-static const char *zdb_ot_extname[] = {
- "deferred free",
- "dedup ditto",
- "other",
- "Total",
-};
-
-#define ZB_TOTAL DN_MAX_LEVELS
-
-typedef struct zdb_cb {
- zdb_blkstats_t zcb_type[ZB_TOTAL + 1][ZDB_OT_TOTAL + 1];
- uint64_t zcb_removing_size;
- uint64_t zcb_checkpoint_size;
- uint64_t zcb_dedup_asize;
- uint64_t zcb_dedup_blocks;
- uint64_t zcb_embedded_blocks[NUM_BP_EMBEDDED_TYPES];
- uint64_t zcb_embedded_histogram[NUM_BP_EMBEDDED_TYPES]
- [BPE_PAYLOAD_SIZE];
- uint64_t zcb_start;
- hrtime_t zcb_lastprint;
- uint64_t zcb_totalasize;
- uint64_t zcb_errors[256];
- int zcb_readfails;
- int zcb_haderrors;
- spa_t *zcb_spa;
- uint32_t **zcb_vd_obsolete_counts;
-} zdb_cb_t;
-
-/* test if two DVA offsets from same vdev are within the same metaslab */
-static boolean_t
-same_metaslab(spa_t *spa, uint64_t vdev, uint64_t off1, uint64_t off2)
-{
- vdev_t *vd = vdev_lookup_top(spa, vdev);
- uint64_t ms_shift = vd->vdev_ms_shift;
-
- return ((off1 >> ms_shift) == (off2 >> ms_shift));
-}
-
-static void
-zdb_count_block(zdb_cb_t *zcb, zilog_t *zilog, const blkptr_t *bp,
- dmu_object_type_t type)
-{
- uint64_t refcnt = 0;
-
- ASSERT(type < ZDB_OT_TOTAL);
-
- if (zilog && zil_bp_tree_add(zilog, bp) != 0)
- return;
-
- spa_config_enter(zcb->zcb_spa, SCL_CONFIG, FTAG, RW_READER);
-
- for (int i = 0; i < 4; i++) {
- int l = (i < 2) ? BP_GET_LEVEL(bp) : ZB_TOTAL;
- int t = (i & 1) ? type : ZDB_OT_TOTAL;
- int equal;
- zdb_blkstats_t *zb = &zcb->zcb_type[l][t];
-
- zb->zb_asize += BP_GET_ASIZE(bp);
- zb->zb_lsize += BP_GET_LSIZE(bp);
- zb->zb_psize += BP_GET_PSIZE(bp);
- zb->zb_count++;
-
- /*
- * The histogram is only big enough to record blocks up to
- * SPA_OLD_MAXBLOCKSIZE; larger blocks go into the last,
- * "other", bucket.
- */
- unsigned idx = BP_GET_PSIZE(bp) >> SPA_MINBLOCKSHIFT;
- idx = MIN(idx, SPA_OLD_MAXBLOCKSIZE / SPA_MINBLOCKSIZE + 1);
- zb->zb_psize_histogram[idx]++;
-
- zb->zb_gangs += BP_COUNT_GANG(bp);
-
- switch (BP_GET_NDVAS(bp)) {
- case 2:
- if (DVA_GET_VDEV(&bp->blk_dva[0]) ==
- DVA_GET_VDEV(&bp->blk_dva[1])) {
- zb->zb_ditto_samevdev++;
-
- if (same_metaslab(zcb->zcb_spa,
- DVA_GET_VDEV(&bp->blk_dva[0]),
- DVA_GET_OFFSET(&bp->blk_dva[0]),
- DVA_GET_OFFSET(&bp->blk_dva[1])))
- zb->zb_ditto_same_ms++;
- }
- break;
- case 3:
- equal = (DVA_GET_VDEV(&bp->blk_dva[0]) ==
- DVA_GET_VDEV(&bp->blk_dva[1])) +
- (DVA_GET_VDEV(&bp->blk_dva[0]) ==
- DVA_GET_VDEV(&bp->blk_dva[2])) +
- (DVA_GET_VDEV(&bp->blk_dva[1]) ==
- DVA_GET_VDEV(&bp->blk_dva[2]));
- if (equal != 0) {
- zb->zb_ditto_samevdev++;
-
- if (DVA_GET_VDEV(&bp->blk_dva[0]) ==
- DVA_GET_VDEV(&bp->blk_dva[1]) &&
- same_metaslab(zcb->zcb_spa,
- DVA_GET_VDEV(&bp->blk_dva[0]),
- DVA_GET_OFFSET(&bp->blk_dva[0]),
- DVA_GET_OFFSET(&bp->blk_dva[1])))
- zb->zb_ditto_same_ms++;
- else if (DVA_GET_VDEV(&bp->blk_dva[0]) ==
- DVA_GET_VDEV(&bp->blk_dva[2]) &&
- same_metaslab(zcb->zcb_spa,
- DVA_GET_VDEV(&bp->blk_dva[0]),
- DVA_GET_OFFSET(&bp->blk_dva[0]),
- DVA_GET_OFFSET(&bp->blk_dva[2])))
- zb->zb_ditto_same_ms++;
- else if (DVA_GET_VDEV(&bp->blk_dva[1]) ==
- DVA_GET_VDEV(&bp->blk_dva[2]) &&
- same_metaslab(zcb->zcb_spa,
- DVA_GET_VDEV(&bp->blk_dva[1]),
- DVA_GET_OFFSET(&bp->blk_dva[1]),
- DVA_GET_OFFSET(&bp->blk_dva[2])))
- zb->zb_ditto_same_ms++;
- }
- break;
- }
- }
-
- spa_config_exit(zcb->zcb_spa, SCL_CONFIG, FTAG);
-
- if (BP_IS_EMBEDDED(bp)) {
- zcb->zcb_embedded_blocks[BPE_GET_ETYPE(bp)]++;
- zcb->zcb_embedded_histogram[BPE_GET_ETYPE(bp)]
- [BPE_GET_PSIZE(bp)]++;
- return;
- }
-
- if (dump_opt['L'])
- return;
-
- if (BP_GET_DEDUP(bp)) {
- ddt_t *ddt;
- ddt_entry_t *dde;
-
- ddt = ddt_select(zcb->zcb_spa, bp);
- ddt_enter(ddt);
- dde = ddt_lookup(ddt, bp, B_FALSE);
-
- if (dde == NULL) {
- refcnt = 0;
- } else {
- ddt_phys_t *ddp = ddt_phys_select(dde, bp);
- ddt_phys_decref(ddp);
- refcnt = ddp->ddp_refcnt;
- if (ddt_phys_total_refcnt(dde) == 0)
- ddt_remove(ddt, dde);
- }
- ddt_exit(ddt);
- }
-
- VERIFY3U(zio_wait(zio_claim(NULL, zcb->zcb_spa,
- refcnt ? 0 : spa_min_claim_txg(zcb->zcb_spa),
- bp, NULL, NULL, ZIO_FLAG_CANFAIL)), ==, 0);
-}
-
-/* ARGSUSED */
-static void
-zdb_blkptr_done(zio_t *zio)
-{
- spa_t *spa = zio->io_spa;
- blkptr_t *bp = zio->io_bp;
- int ioerr = zio->io_error;
- zdb_cb_t *zcb = zio->io_private;
- zbookmark_phys_t *zb = &zio->io_bookmark;
-
- abd_free(zio->io_abd);
-
- mutex_enter(&spa->spa_scrub_lock);
- spa->spa_scrub_inflight--;
- spa->spa_load_verify_ios--;
- cv_broadcast(&spa->spa_scrub_io_cv);
-
- if (ioerr && !(zio->io_flags & ZIO_FLAG_SPECULATIVE)) {
- char blkbuf[BP_SPRINTF_LEN];
-
- zcb->zcb_haderrors = 1;
- zcb->zcb_errors[ioerr]++;
-
- if (dump_opt['b'] >= 2)
- snprintf_blkptr(blkbuf, sizeof (blkbuf), bp);
- else
- blkbuf[0] = '\0';
-
- (void) printf("zdb_blkptr_cb: "
- "Got error %d reading "
- "<%llu, %llu, %lld, %llx> %s -- skipping\n",
- ioerr,
- (u_longlong_t)zb->zb_objset,
- (u_longlong_t)zb->zb_object,
- (u_longlong_t)zb->zb_level,
- (u_longlong_t)zb->zb_blkid,
- blkbuf);
- }
- mutex_exit(&spa->spa_scrub_lock);
-}
-
-/* ARGSUSED */
-static int
-zdb_blkptr_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
- const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg)
-{
- zdb_cb_t *zcb = arg;
- dmu_object_type_t type;
- boolean_t is_metadata;
-
- if (bp == NULL)
- return (0);
-
- if (dump_opt['b'] >= 5 && bp->blk_birth > 0) {
- char blkbuf[BP_SPRINTF_LEN];
- snprintf_blkptr(blkbuf, sizeof (blkbuf), bp);
- (void) printf("objset %llu object %llu "
- "level %lld offset 0x%llx %s\n",
- (u_longlong_t)zb->zb_objset,
- (u_longlong_t)zb->zb_object,
- (longlong_t)zb->zb_level,
- (u_longlong_t)blkid2offset(dnp, bp, zb),
- blkbuf);
- }
-
- if (BP_IS_HOLE(bp))
- return (0);
-
- type = BP_GET_TYPE(bp);
-
- zdb_count_block(zcb, zilog, bp,
- (type & DMU_OT_NEWTYPE) ? ZDB_OT_OTHER : type);
-
- is_metadata = (BP_GET_LEVEL(bp) != 0 || DMU_OT_IS_METADATA(type));
-
- if (!BP_IS_EMBEDDED(bp) &&
- (dump_opt['c'] > 1 || (dump_opt['c'] && is_metadata))) {
- size_t size = BP_GET_PSIZE(bp);
- abd_t *abd = abd_alloc(size, B_FALSE);
- int flags = ZIO_FLAG_CANFAIL | ZIO_FLAG_SCRUB | ZIO_FLAG_RAW;
-
- /* If it's an intent log block, failure is expected. */
- if (zb->zb_level == ZB_ZIL_LEVEL)
- flags |= ZIO_FLAG_SPECULATIVE;
-
- mutex_enter(&spa->spa_scrub_lock);
- while (spa->spa_load_verify_ios > max_inflight)
- cv_wait(&spa->spa_scrub_io_cv, &spa->spa_scrub_lock);
- spa->spa_scrub_inflight++;
- spa->spa_load_verify_ios++;
- mutex_exit(&spa->spa_scrub_lock);
-
- zio_nowait(zio_read(NULL, spa, bp, abd, size,
- zdb_blkptr_done, zcb, ZIO_PRIORITY_ASYNC_READ, flags, zb));
- }
-
- zcb->zcb_readfails = 0;
-
- /* only call gethrtime() every 100 blocks */
- static int iters;
- if (++iters > 100)
- iters = 0;
- else
- return (0);
-
- if (dump_opt['b'] < 5 && gethrtime() > zcb->zcb_lastprint + NANOSEC) {
- uint64_t now = gethrtime();
- char buf[10];
- uint64_t bytes = zcb->zcb_type[ZB_TOTAL][ZDB_OT_TOTAL].zb_asize;
- int kb_per_sec =
- 1 + bytes / (1 + ((now - zcb->zcb_start) / 1000 / 1000));
- int sec_remaining =
- (zcb->zcb_totalasize - bytes) / 1024 / kb_per_sec;
-
- /* make sure nicenum has enough space */
- CTASSERT(sizeof (buf) >= NN_NUMBUF_SZ);
-
- zfs_nicenum(bytes, buf, sizeof (buf));
- (void) fprintf(stderr,
- "\r%5s completed (%4dMB/s) "
- "estimated time remaining: %uhr %02umin %02usec ",
- buf, kb_per_sec / 1024,
- sec_remaining / 60 / 60,
- sec_remaining / 60 % 60,
- sec_remaining % 60);
-
- zcb->zcb_lastprint = now;
- }
-
- return (0);
-}
-
-static void
-zdb_leak(void *arg, uint64_t start, uint64_t size)
-{
- vdev_t *vd = arg;
-
- (void) printf("leaked space: vdev %llu, offset 0x%llx, size %llu\n",
- (u_longlong_t)vd->vdev_id, (u_longlong_t)start, (u_longlong_t)size);
-}
-
-static metaslab_ops_t zdb_metaslab_ops = {
- NULL /* alloc */
-};
-
-static void
-zdb_ddt_leak_init(spa_t *spa, zdb_cb_t *zcb)
-{
- ddt_bookmark_t ddb;
- ddt_entry_t dde;
- int error;
-
- ASSERT(!dump_opt['L']);
-
- bzero(&ddb, sizeof (ddb));
- while ((error = ddt_walk(spa, &ddb, &dde)) == 0) {
- blkptr_t blk;
- ddt_phys_t *ddp = dde.dde_phys;
-
- if (ddb.ddb_class == DDT_CLASS_UNIQUE)
- return;
-
- ASSERT(ddt_phys_total_refcnt(&dde) > 1);
-
- for (int p = 0; p < DDT_PHYS_TYPES; p++, ddp++) {
- if (ddp->ddp_phys_birth == 0)
- continue;
- ddt_bp_create(ddb.ddb_checksum,
- &dde.dde_key, ddp, &blk);
- if (p == DDT_PHYS_DITTO) {
- zdb_count_block(zcb, NULL, &blk, ZDB_OT_DITTO);
- } else {
- zcb->zcb_dedup_asize +=
- BP_GET_ASIZE(&blk) * (ddp->ddp_refcnt - 1);
- zcb->zcb_dedup_blocks++;
- }
- }
- ddt_t *ddt = spa->spa_ddt[ddb.ddb_checksum];
- ddt_enter(ddt);
- VERIFY(ddt_lookup(ddt, &blk, B_TRUE) != NULL);
- ddt_exit(ddt);
- }
-
- ASSERT(error == ENOENT);
-}
-
-/* ARGSUSED */
-static void
-claim_segment_impl_cb(uint64_t inner_offset, vdev_t *vd, uint64_t offset,
- uint64_t size, void *arg)
-{
- /*
- * This callback was called through a remap from
- * a device being removed. Therefore, the vdev that
- * this callback is applied to is a concrete
- * vdev.
- */
- ASSERT(vdev_is_concrete(vd));
-
- VERIFY0(metaslab_claim_impl(vd, offset, size,
- spa_min_claim_txg(vd->vdev_spa)));
-}
-
-static void
-claim_segment_cb(void *arg, uint64_t offset, uint64_t size)
-{
- vdev_t *vd = arg;
-
- vdev_indirect_ops.vdev_op_remap(vd, offset, size,
- claim_segment_impl_cb, NULL);
-}
-
-/*
- * After accounting for all allocated blocks that are directly referenced,
- * we might have missed a reference to a block from a partially complete
- * (and thus unused) indirect mapping object. We perform a secondary pass
- * through the metaslabs we have already mapped and claim the destination
- * blocks.
- */
-static void
-zdb_claim_removing(spa_t *spa, zdb_cb_t *zcb)
-{
- if (dump_opt['L'])
- return;
-
- if (spa->spa_vdev_removal == NULL)
- return;
-
- spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER);
-
- spa_vdev_removal_t *svr = spa->spa_vdev_removal;
- vdev_t *vd = vdev_lookup_top(spa, svr->svr_vdev_id);
- vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping;
-
- for (uint64_t msi = 0; msi < vd->vdev_ms_count; msi++) {
- metaslab_t *msp = vd->vdev_ms[msi];
-
- if (msp->ms_start >= vdev_indirect_mapping_max_offset(vim))
- break;
-
- ASSERT0(range_tree_space(svr->svr_allocd_segs));
-
- if (msp->ms_sm != NULL) {
- VERIFY0(space_map_load(msp->ms_sm,
- svr->svr_allocd_segs, SM_ALLOC));
-
- /*
- * Clear everything past what has been synced unless
- * it's past the spacemap, because we have not allocated
- * mappings for it yet.
- */
- uint64_t vim_max_offset =
- vdev_indirect_mapping_max_offset(vim);
- uint64_t sm_end = msp->ms_sm->sm_start +
- msp->ms_sm->sm_size;
- if (sm_end > vim_max_offset)
- range_tree_clear(svr->svr_allocd_segs,
- vim_max_offset, sm_end - vim_max_offset);
- }
-
- zcb->zcb_removing_size +=
- range_tree_space(svr->svr_allocd_segs);
- range_tree_vacate(svr->svr_allocd_segs, claim_segment_cb, vd);
- }
-
- spa_config_exit(spa, SCL_CONFIG, FTAG);
-}
-
-/* ARGSUSED */
-static int
-increment_indirect_mapping_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx)
-{
- zdb_cb_t *zcb = arg;
- spa_t *spa = zcb->zcb_spa;
- vdev_t *vd;
- const dva_t *dva = &bp->blk_dva[0];
-
- ASSERT(!dump_opt['L']);
- ASSERT3U(BP_GET_NDVAS(bp), ==, 1);
-
- spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
- vd = vdev_lookup_top(zcb->zcb_spa, DVA_GET_VDEV(dva));
- ASSERT3P(vd, !=, NULL);
- spa_config_exit(spa, SCL_VDEV, FTAG);
-
- ASSERT(vd->vdev_indirect_config.vic_mapping_object != 0);
- ASSERT3P(zcb->zcb_vd_obsolete_counts[vd->vdev_id], !=, NULL);
-
- vdev_indirect_mapping_increment_obsolete_count(
- vd->vdev_indirect_mapping,
- DVA_GET_OFFSET(dva), DVA_GET_ASIZE(dva),
- zcb->zcb_vd_obsolete_counts[vd->vdev_id]);
-
- return (0);
-}
-
-static uint32_t *
-zdb_load_obsolete_counts(vdev_t *vd)
-{
- vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping;
- spa_t *spa = vd->vdev_spa;
- spa_condensing_indirect_phys_t *scip =
- &spa->spa_condensing_indirect_phys;
- uint32_t *counts;
-
- EQUIV(vdev_obsolete_sm_object(vd) != 0, vd->vdev_obsolete_sm != NULL);
- counts = vdev_indirect_mapping_load_obsolete_counts(vim);
- if (vd->vdev_obsolete_sm != NULL) {
- vdev_indirect_mapping_load_obsolete_spacemap(vim, counts,
- vd->vdev_obsolete_sm);
- }
- if (scip->scip_vdev == vd->vdev_id &&
- scip->scip_prev_obsolete_sm_object != 0) {
- space_map_t *prev_obsolete_sm = NULL;
- VERIFY0(space_map_open(&prev_obsolete_sm, spa->spa_meta_objset,
- scip->scip_prev_obsolete_sm_object, 0, vd->vdev_asize, 0));
- vdev_indirect_mapping_load_obsolete_spacemap(vim, counts,
- prev_obsolete_sm);
- space_map_close(prev_obsolete_sm);
- }
- return (counts);
-}
-
-typedef struct checkpoint_sm_exclude_entry_arg {
- vdev_t *cseea_vd;
- uint64_t cseea_checkpoint_size;
-} checkpoint_sm_exclude_entry_arg_t;
-
-static int
-checkpoint_sm_exclude_entry_cb(space_map_entry_t *sme, void *arg)
-{
- checkpoint_sm_exclude_entry_arg_t *cseea = arg;
- vdev_t *vd = cseea->cseea_vd;
- metaslab_t *ms = vd->vdev_ms[sme->sme_offset >> vd->vdev_ms_shift];
- uint64_t end = sme->sme_offset + sme->sme_run;
-
- ASSERT(sme->sme_type == SM_FREE);
-
- /*
- * Since the vdev_checkpoint_sm exists in the vdev level
- * and the ms_sm space maps exist in the metaslab level,
- * an entry in the checkpoint space map could theoretically
- * cross the boundaries of the metaslab that it belongs.
- *
- * In reality, because of the way that we populate and
- * manipulate the checkpoint's space maps currently,
- * there shouldn't be any entries that cross metaslabs.
- * Hence the assertion below.
- *
- * That said, there is no fundamental requirement that
- * the checkpoint's space map entries should not cross
- * metaslab boundaries. So if needed we could add code
- * that handles metaslab-crossing segments in the future.
- */
- VERIFY3U(sme->sme_offset, >=, ms->ms_start);
- VERIFY3U(end, <=, ms->ms_start + ms->ms_size);
-
- /*
- * By removing the entry from the allocated segments we
- * also verify that the entry is there to begin with.
- */
- mutex_enter(&ms->ms_lock);
- range_tree_remove(ms->ms_allocatable, sme->sme_offset, sme->sme_run);
- mutex_exit(&ms->ms_lock);
-
- cseea->cseea_checkpoint_size += sme->sme_run;
- return (0);
-}
-
-static void
-zdb_leak_init_vdev_exclude_checkpoint(vdev_t *vd, zdb_cb_t *zcb)
-{
- spa_t *spa = vd->vdev_spa;
- space_map_t *checkpoint_sm = NULL;
- uint64_t checkpoint_sm_obj;
-
- /*
- * If there is no vdev_top_zap, we are in a pool whose
- * version predates the pool checkpoint feature.
- */
- if (vd->vdev_top_zap == 0)
- return;
-
- /*
- * If there is no reference of the vdev_checkpoint_sm in
- * the vdev_top_zap, then one of the following scenarios
- * is true:
- *
- * 1] There is no checkpoint
- * 2] There is a checkpoint, but no checkpointed blocks
- * have been freed yet
- * 3] The current vdev is indirect
- *
- * In these cases we return immediately.
- */
- if (zap_contains(spa_meta_objset(spa), vd->vdev_top_zap,
- VDEV_TOP_ZAP_POOL_CHECKPOINT_SM) != 0)
- return;
-
- VERIFY0(zap_lookup(spa_meta_objset(spa), vd->vdev_top_zap,
- VDEV_TOP_ZAP_POOL_CHECKPOINT_SM, sizeof (uint64_t), 1,
- &checkpoint_sm_obj));
-
- checkpoint_sm_exclude_entry_arg_t cseea;
- cseea.cseea_vd = vd;
- cseea.cseea_checkpoint_size = 0;
-
- VERIFY0(space_map_open(&checkpoint_sm, spa_meta_objset(spa),
- checkpoint_sm_obj, 0, vd->vdev_asize, vd->vdev_ashift));
-
- VERIFY0(space_map_iterate(checkpoint_sm,
- space_map_length(checkpoint_sm),
- checkpoint_sm_exclude_entry_cb, &cseea));
- space_map_close(checkpoint_sm);
-
- zcb->zcb_checkpoint_size += cseea.cseea_checkpoint_size;
-}
-
-static void
-zdb_leak_init_exclude_checkpoint(spa_t *spa, zdb_cb_t *zcb)
-{
- ASSERT(!dump_opt['L']);
-
- vdev_t *rvd = spa->spa_root_vdev;
- for (uint64_t c = 0; c < rvd->vdev_children; c++) {
- ASSERT3U(c, ==, rvd->vdev_child[c]->vdev_id);
- zdb_leak_init_vdev_exclude_checkpoint(rvd->vdev_child[c], zcb);
- }
-}
-
-static void
-load_concrete_ms_allocatable_trees(spa_t *spa, maptype_t maptype)
-{
- vdev_t *rvd = spa->spa_root_vdev;
- for (uint64_t i = 0; i < rvd->vdev_children; i++) {
- vdev_t *vd = rvd->vdev_child[i];
-
- ASSERT3U(i, ==, vd->vdev_id);
-
- if (vd->vdev_ops == &vdev_indirect_ops)
- continue;
-
- for (uint64_t m = 0; m < vd->vdev_ms_count; m++) {
- metaslab_t *msp = vd->vdev_ms[m];
-
- (void) fprintf(stderr,
- "\rloading concrete vdev %llu, "
- "metaslab %llu of %llu ...",
- (longlong_t)vd->vdev_id,
- (longlong_t)msp->ms_id,
- (longlong_t)vd->vdev_ms_count);
-
- mutex_enter(&msp->ms_lock);
- metaslab_unload(msp);
-
- /*
- * We don't want to spend the CPU manipulating the
- * size-ordered tree, so clear the range_tree ops.
- */
- msp->ms_allocatable->rt_ops = NULL;
-
- if (msp->ms_sm != NULL) {
- VERIFY0(space_map_load(msp->ms_sm,
- msp->ms_allocatable, maptype));
- }
- if (!msp->ms_loaded)
- msp->ms_loaded = B_TRUE;
- mutex_exit(&msp->ms_lock);
- }
- }
-}
-
-/*
- * vm_idxp is an in-out parameter which (for indirect vdevs) is the
- * index in vim_entries that has the first entry in this metaslab.
- * On return, it will be set to the first entry after this metaslab.
- */
-static void
-load_indirect_ms_allocatable_tree(vdev_t *vd, metaslab_t *msp,
- uint64_t *vim_idxp)
-{
- vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping;
-
- mutex_enter(&msp->ms_lock);
- metaslab_unload(msp);
-
- /*
- * We don't want to spend the CPU manipulating the
- * size-ordered tree, so clear the range_tree ops.
- */
- msp->ms_allocatable->rt_ops = NULL;
-
- for (; *vim_idxp < vdev_indirect_mapping_num_entries(vim);
- (*vim_idxp)++) {
- vdev_indirect_mapping_entry_phys_t *vimep =
- &vim->vim_entries[*vim_idxp];
- uint64_t ent_offset = DVA_MAPPING_GET_SRC_OFFSET(vimep);
- uint64_t ent_len = DVA_GET_ASIZE(&vimep->vimep_dst);
- ASSERT3U(ent_offset, >=, msp->ms_start);
- if (ent_offset >= msp->ms_start + msp->ms_size)
- break;
-
- /*
- * Mappings do not cross metaslab boundaries,
- * because we create them by walking the metaslabs.
- */
- ASSERT3U(ent_offset + ent_len, <=,
- msp->ms_start + msp->ms_size);
- range_tree_add(msp->ms_allocatable, ent_offset, ent_len);
- }
-
- if (!msp->ms_loaded)
- msp->ms_loaded = B_TRUE;
- mutex_exit(&msp->ms_lock);
-}
-
-static void
-zdb_leak_init_prepare_indirect_vdevs(spa_t *spa, zdb_cb_t *zcb)
-{
- ASSERT(!dump_opt['L']);
-
- vdev_t *rvd = spa->spa_root_vdev;
- for (uint64_t c = 0; c < rvd->vdev_children; c++) {
- vdev_t *vd = rvd->vdev_child[c];
-
- ASSERT3U(c, ==, vd->vdev_id);
-
- if (vd->vdev_ops != &vdev_indirect_ops)
- continue;
-
- /*
- * Note: we don't check for mapping leaks on
- * removing vdevs because their ms_allocatable's
- * are used to look for leaks in allocated space.
- */
- zcb->zcb_vd_obsolete_counts[c] = zdb_load_obsolete_counts(vd);
-
- /*
- * Normally, indirect vdevs don't have any
- * metaslabs. We want to set them up for
- * zio_claim().
- */
- VERIFY0(vdev_metaslab_init(vd, 0));
-
- vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping;
- uint64_t vim_idx = 0;
- for (uint64_t m = 0; m < vd->vdev_ms_count; m++) {
-
- (void) fprintf(stderr,
- "\rloading indirect vdev %llu, "
- "metaslab %llu of %llu ...",
- (longlong_t)vd->vdev_id,
- (longlong_t)vd->vdev_ms[m]->ms_id,
- (longlong_t)vd->vdev_ms_count);
-
- load_indirect_ms_allocatable_tree(vd, vd->vdev_ms[m],
- &vim_idx);
- }
- ASSERT3U(vim_idx, ==, vdev_indirect_mapping_num_entries(vim));
- }
-}
-
-static void
-zdb_leak_init(spa_t *spa, zdb_cb_t *zcb)
-{
- zcb->zcb_spa = spa;
-
- if (dump_opt['L'])
- return;
-
- dsl_pool_t *dp = spa->spa_dsl_pool;
- vdev_t *rvd = spa->spa_root_vdev;
-
- /*
- * We are going to be changing the meaning of the metaslab's
- * ms_allocatable. Ensure that the allocator doesn't try to
- * use the tree.
- */
- spa->spa_normal_class->mc_ops = &zdb_metaslab_ops;
- spa->spa_log_class->mc_ops = &zdb_metaslab_ops;
-
- zcb->zcb_vd_obsolete_counts =
- umem_zalloc(rvd->vdev_children * sizeof (uint32_t *),
- UMEM_NOFAIL);
-
- /*
- * For leak detection, we overload the ms_allocatable trees
- * to contain allocated segments instead of free segments.
- * As a result, we can't use the normal metaslab_load/unload
- * interfaces.
- */
- zdb_leak_init_prepare_indirect_vdevs(spa, zcb);
- load_concrete_ms_allocatable_trees(spa, SM_ALLOC);
-
- /*
- * On load_concrete_ms_allocatable_trees() we loaded all the
- * allocated entries from the ms_sm to the ms_allocatable for
- * each metaslab. If the pool has a checkpoint or is in the
- * middle of discarding a checkpoint, some of these blocks
- * may have been freed but their ms_sm may not have been
- * updated because they are referenced by the checkpoint. In
- * order to avoid false-positives during leak-detection, we
- * go through the vdev's checkpoint space map and exclude all
- * its entries from their relevant ms_allocatable.
- *
- * We also aggregate the space held by the checkpoint and add
- * it to zcb_checkpoint_size.
- *
- * Note that at this point we are also verifying that all the
- * entries on the checkpoint_sm are marked as allocated in
- * the ms_sm of their relevant metaslab.
- * [see comment in checkpoint_sm_exclude_entry_cb()]
- */
- zdb_leak_init_exclude_checkpoint(spa, zcb);
- ASSERT3U(zcb->zcb_checkpoint_size, ==, spa_get_checkpoint_space(spa));
-
- /* for cleaner progress output */
- (void) fprintf(stderr, "\n");
-
- if (bpobj_is_open(&dp->dp_obsolete_bpobj)) {
- ASSERT(spa_feature_is_enabled(spa,
- SPA_FEATURE_DEVICE_REMOVAL));
- (void) bpobj_iterate_nofree(&dp->dp_obsolete_bpobj,
- increment_indirect_mapping_cb, zcb, NULL);
- }
-
- spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER);
- zdb_ddt_leak_init(spa, zcb);
- spa_config_exit(spa, SCL_CONFIG, FTAG);
-}
-
-static boolean_t
-zdb_check_for_obsolete_leaks(vdev_t *vd, zdb_cb_t *zcb)
-{
- boolean_t leaks = B_FALSE;
- vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping;
- uint64_t total_leaked = 0;
-
- ASSERT(vim != NULL);
-
- for (uint64_t i = 0; i < vdev_indirect_mapping_num_entries(vim); i++) {
- vdev_indirect_mapping_entry_phys_t *vimep =
- &vim->vim_entries[i];
- uint64_t obsolete_bytes = 0;
- uint64_t offset = DVA_MAPPING_GET_SRC_OFFSET(vimep);
- metaslab_t *msp = vd->vdev_ms[offset >> vd->vdev_ms_shift];
-
- /*
- * This is not very efficient but it's easy to
- * verify correctness.
- */
- for (uint64_t inner_offset = 0;
- inner_offset < DVA_GET_ASIZE(&vimep->vimep_dst);
- inner_offset += 1 << vd->vdev_ashift) {
- if (range_tree_contains(msp->ms_allocatable,
- offset + inner_offset, 1 << vd->vdev_ashift)) {
- obsolete_bytes += 1 << vd->vdev_ashift;
- }
- }
-
- int64_t bytes_leaked = obsolete_bytes -
- zcb->zcb_vd_obsolete_counts[vd->vdev_id][i];
- ASSERT3U(DVA_GET_ASIZE(&vimep->vimep_dst), >=,
- zcb->zcb_vd_obsolete_counts[vd->vdev_id][i]);
- if (bytes_leaked != 0 &&
- (vdev_obsolete_counts_are_precise(vd) ||
- dump_opt['d'] >= 5)) {
- (void) printf("obsolete indirect mapping count "
- "mismatch on %llu:%llx:%llx : %llx bytes leaked\n",
- (u_longlong_t)vd->vdev_id,
- (u_longlong_t)DVA_MAPPING_GET_SRC_OFFSET(vimep),
- (u_longlong_t)DVA_GET_ASIZE(&vimep->vimep_dst),
- (u_longlong_t)bytes_leaked);
- }
- total_leaked += ABS(bytes_leaked);
- }
-
- if (!vdev_obsolete_counts_are_precise(vd) && total_leaked > 0) {
- int pct_leaked = total_leaked * 100 /
- vdev_indirect_mapping_bytes_mapped(vim);
- (void) printf("cannot verify obsolete indirect mapping "
- "counts of vdev %llu because precise feature was not "
- "enabled when it was removed: %d%% (%llx bytes) of mapping"
- "unreferenced\n",
- (u_longlong_t)vd->vdev_id, pct_leaked,
- (u_longlong_t)total_leaked);
- } else if (total_leaked > 0) {
- (void) printf("obsolete indirect mapping count mismatch "
- "for vdev %llu -- %llx total bytes mismatched\n",
- (u_longlong_t)vd->vdev_id,
- (u_longlong_t)total_leaked);
- leaks |= B_TRUE;
- }
-
- vdev_indirect_mapping_free_obsolete_counts(vim,
- zcb->zcb_vd_obsolete_counts[vd->vdev_id]);
- zcb->zcb_vd_obsolete_counts[vd->vdev_id] = NULL;
-
- return (leaks);
-}
-
-static boolean_t
-zdb_leak_fini(spa_t *spa, zdb_cb_t *zcb)
-{
- if (dump_opt['L'])
- return (B_FALSE);
-
- boolean_t leaks = B_FALSE;
-
- vdev_t *rvd = spa->spa_root_vdev;
- for (unsigned c = 0; c < rvd->vdev_children; c++) {
- vdev_t *vd = rvd->vdev_child[c];
-#if DEBUG
- metaslab_group_t *mg = vd->vdev_mg;
-#endif
-
- if (zcb->zcb_vd_obsolete_counts[c] != NULL) {
- leaks |= zdb_check_for_obsolete_leaks(vd, zcb);
- }
-
- for (uint64_t m = 0; m < vd->vdev_ms_count; m++) {
- metaslab_t *msp = vd->vdev_ms[m];
- ASSERT3P(mg, ==, msp->ms_group);
-
- /*
- * ms_allocatable has been overloaded
- * to contain allocated segments. Now that
- * we finished traversing all blocks, any
- * block that remains in the ms_allocatable
- * represents an allocated block that we
- * did not claim during the traversal.
- * Claimed blocks would have been removed
- * from the ms_allocatable. For indirect
- * vdevs, space remaining in the tree
- * represents parts of the mapping that are
- * not referenced, which is not a bug.
- */
- if (vd->vdev_ops == &vdev_indirect_ops) {
- range_tree_vacate(msp->ms_allocatable,
- NULL, NULL);
- } else {
- range_tree_vacate(msp->ms_allocatable,
- zdb_leak, vd);
- }
-
- if (msp->ms_loaded) {
- msp->ms_loaded = B_FALSE;
- }
- }
-
- }
-
- umem_free(zcb->zcb_vd_obsolete_counts,
- rvd->vdev_children * sizeof (uint32_t *));
- zcb->zcb_vd_obsolete_counts = NULL;
-
- return (leaks);
-}
-
-/* ARGSUSED */
-static int
-count_block_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx)
-{
- zdb_cb_t *zcb = arg;
-
- if (dump_opt['b'] >= 5) {
- char blkbuf[BP_SPRINTF_LEN];
- snprintf_blkptr(blkbuf, sizeof (blkbuf), bp);
- (void) printf("[%s] %s\n",
- "deferred free", blkbuf);
- }
- zdb_count_block(zcb, NULL, bp, ZDB_OT_DEFERRED);
- return (0);
-}
-
-static int
-dump_block_stats(spa_t *spa)
-{
- zdb_cb_t zcb;
- zdb_blkstats_t *zb, *tzb;
- uint64_t norm_alloc, norm_space, total_alloc, total_found;
- int flags = TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA | TRAVERSE_HARD;
- boolean_t leaks = B_FALSE;
- int err;
-
- bzero(&zcb, sizeof (zcb));
- (void) printf("\nTraversing all blocks %s%s%s%s%s...\n\n",
- (dump_opt['c'] || !dump_opt['L']) ? "to verify " : "",
- (dump_opt['c'] == 1) ? "metadata " : "",
- dump_opt['c'] ? "checksums " : "",
- (dump_opt['c'] && !dump_opt['L']) ? "and verify " : "",
- !dump_opt['L'] ? "nothing leaked " : "");
-
- /*
- * When leak detection is enabled we load all space maps as SM_ALLOC
- * maps, then traverse the pool claiming each block we discover. If
- * the pool is perfectly consistent, the segment trees will be empty
- * when we're done. Anything left over is a leak; any block we can't
- * claim (because it's not part of any space map) is a double
- * allocation, reference to a freed block, or an unclaimed log block.
- *
- * When leak detection is disabled (-L option) we still traverse the
- * pool claiming each block we discover, but we skip opening any space
- * maps.
- */
- bzero(&zcb, sizeof (zdb_cb_t));
- zdb_leak_init(spa, &zcb);
-
- /*
- * If there's a deferred-free bplist, process that first.
- */
- (void) bpobj_iterate_nofree(&spa->spa_deferred_bpobj,
- count_block_cb, &zcb, NULL);
-
- if (spa_version(spa) >= SPA_VERSION_DEADLISTS) {
- (void) bpobj_iterate_nofree(&spa->spa_dsl_pool->dp_free_bpobj,
- count_block_cb, &zcb, NULL);
- }
-
- zdb_claim_removing(spa, &zcb);
-
- if (spa_feature_is_active(spa, SPA_FEATURE_ASYNC_DESTROY)) {
- VERIFY3U(0, ==, bptree_iterate(spa->spa_meta_objset,
- spa->spa_dsl_pool->dp_bptree_obj, B_FALSE, count_block_cb,
- &zcb, NULL));
- }
-
- if (dump_opt['c'] > 1)
- flags |= TRAVERSE_PREFETCH_DATA;
-
- zcb.zcb_totalasize = metaslab_class_get_alloc(spa_normal_class(spa));
- zcb.zcb_totalasize += metaslab_class_get_alloc(spa_special_class(spa));
- zcb.zcb_totalasize += metaslab_class_get_alloc(spa_dedup_class(spa));
- zcb.zcb_start = zcb.zcb_lastprint = gethrtime();
- err = traverse_pool(spa, 0, flags, zdb_blkptr_cb, &zcb);
-
- /*
- * If we've traversed the data blocks then we need to wait for those
- * I/Os to complete. We leverage "The Godfather" zio to wait on
- * all async I/Os to complete.
- */
- if (dump_opt['c']) {
- for (int i = 0; i < max_ncpus; i++) {
- (void) zio_wait(spa->spa_async_zio_root[i]);
- spa->spa_async_zio_root[i] = zio_root(spa, NULL, NULL,
- ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE |
- ZIO_FLAG_GODFATHER);
- }
- }
-
- /*
- * Done after zio_wait() since zcb_haderrors is modified in
- * zdb_blkptr_done()
- */
- zcb.zcb_haderrors |= err;
-
- if (zcb.zcb_haderrors) {
- (void) printf("\nError counts:\n\n");
- (void) printf("\t%5s %s\n", "errno", "count");
- for (int e = 0; e < 256; e++) {
- if (zcb.zcb_errors[e] != 0) {
- (void) printf("\t%5d %llu\n",
- e, (u_longlong_t)zcb.zcb_errors[e]);
- }
- }
- }
-
- /*
- * Report any leaked segments.
- */
- leaks |= zdb_leak_fini(spa, &zcb);
-
- tzb = &zcb.zcb_type[ZB_TOTAL][ZDB_OT_TOTAL];
-
- norm_alloc = metaslab_class_get_alloc(spa_normal_class(spa));
- norm_space = metaslab_class_get_space(spa_normal_class(spa));
-
- total_alloc = norm_alloc +
- metaslab_class_get_alloc(spa_log_class(spa)) +
- metaslab_class_get_alloc(spa_special_class(spa)) +
- metaslab_class_get_alloc(spa_dedup_class(spa));
- total_found = tzb->zb_asize - zcb.zcb_dedup_asize +
- zcb.zcb_removing_size + zcb.zcb_checkpoint_size;
-
- if (total_found == total_alloc && !dump_opt['L']) {
- (void) printf("\n\tNo leaks (block sum matches space"
- " maps exactly)\n");
- } else if (!dump_opt['L']) {
- (void) printf("block traversal size %llu != alloc %llu "
- "(%s %lld)\n",
- (u_longlong_t)total_found,
- (u_longlong_t)total_alloc,
- (dump_opt['L']) ? "unreachable" : "leaked",
- (longlong_t)(total_alloc - total_found));
- leaks = B_TRUE;
- }
-
- if (tzb->zb_count == 0)
- return (2);
-
- (void) printf("\n");
- (void) printf("\t%-16s %14llu\n", "bp count:",
- (u_longlong_t)tzb->zb_count);
- (void) printf("\t%-16s %14llu\n", "ganged count:",
- (longlong_t)tzb->zb_gangs);
- (void) printf("\t%-16s %14llu avg: %6llu\n", "bp logical:",
- (u_longlong_t)tzb->zb_lsize,
- (u_longlong_t)(tzb->zb_lsize / tzb->zb_count));
- (void) printf("\t%-16s %14llu avg: %6llu compression: %6.2f\n",
- "bp physical:", (u_longlong_t)tzb->zb_psize,
- (u_longlong_t)(tzb->zb_psize / tzb->zb_count),
- (double)tzb->zb_lsize / tzb->zb_psize);
- (void) printf("\t%-16s %14llu avg: %6llu compression: %6.2f\n",
- "bp allocated:", (u_longlong_t)tzb->zb_asize,
- (u_longlong_t)(tzb->zb_asize / tzb->zb_count),
- (double)tzb->zb_lsize / tzb->zb_asize);
- (void) printf("\t%-16s %14llu ref>1: %6llu deduplication: %6.2f\n",
- "bp deduped:", (u_longlong_t)zcb.zcb_dedup_asize,
- (u_longlong_t)zcb.zcb_dedup_blocks,
- (double)zcb.zcb_dedup_asize / tzb->zb_asize + 1.0);
- (void) printf("\t%-16s %14llu used: %5.2f%%\n", "Normal class:",
- (u_longlong_t)norm_alloc, 100.0 * norm_alloc / norm_space);
-
- if (spa_special_class(spa)->mc_rotor != NULL) {
- uint64_t alloc = metaslab_class_get_alloc(
- spa_special_class(spa));
- uint64_t space = metaslab_class_get_space(
- spa_special_class(spa));
-
- (void) printf("\t%-16s %14llu used: %5.2f%%\n",
- "Special class", (u_longlong_t)alloc,
- 100.0 * alloc / space);
- }
-
- if (spa_dedup_class(spa)->mc_rotor != NULL) {
- uint64_t alloc = metaslab_class_get_alloc(
- spa_dedup_class(spa));
- uint64_t space = metaslab_class_get_space(
- spa_dedup_class(spa));
-
- (void) printf("\t%-16s %14llu used: %5.2f%%\n",
- "Dedup class", (u_longlong_t)alloc,
- 100.0 * alloc / space);
- }
-
- for (bp_embedded_type_t i = 0; i < NUM_BP_EMBEDDED_TYPES; i++) {
- if (zcb.zcb_embedded_blocks[i] == 0)
- continue;
- (void) printf("\n");
- (void) printf("\tadditional, non-pointer bps of type %u: "
- "%10llu\n",
- i, (u_longlong_t)zcb.zcb_embedded_blocks[i]);
-
- if (dump_opt['b'] >= 3) {
- (void) printf("\t number of (compressed) bytes: "
- "number of bps\n");
- dump_histogram(zcb.zcb_embedded_histogram[i],
- sizeof (zcb.zcb_embedded_histogram[i]) /
- sizeof (zcb.zcb_embedded_histogram[i][0]), 0);
- }
- }
-
- if (tzb->zb_ditto_samevdev != 0) {
- (void) printf("\tDittoed blocks on same vdev: %llu\n",
- (longlong_t)tzb->zb_ditto_samevdev);
- }
- if (tzb->zb_ditto_same_ms != 0) {
- (void) printf("\tDittoed blocks in same metaslab: %llu\n",
- (longlong_t)tzb->zb_ditto_same_ms);
- }
-
- for (uint64_t v = 0; v < spa->spa_root_vdev->vdev_children; v++) {
- vdev_t *vd = spa->spa_root_vdev->vdev_child[v];
- vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping;
-
- if (vim == NULL) {
- continue;
- }
-
- char mem[32];
- zdb_nicenum(vdev_indirect_mapping_num_entries(vim),
- mem, vdev_indirect_mapping_size(vim));
-
- (void) printf("\tindirect vdev id %llu has %llu segments "
- "(%s in memory)\n",
- (longlong_t)vd->vdev_id,
- (longlong_t)vdev_indirect_mapping_num_entries(vim), mem);
- }
-
- if (dump_opt['b'] >= 2) {
- int l, t, level;
- (void) printf("\nBlocks\tLSIZE\tPSIZE\tASIZE"
- "\t avg\t comp\t%%Total\tType\n");
-
- for (t = 0; t <= ZDB_OT_TOTAL; t++) {
- char csize[32], lsize[32], psize[32], asize[32];
- char avg[32], gang[32];
- const char *typename;
-
- /* make sure nicenum has enough space */
- CTASSERT(sizeof (csize) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (lsize) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (psize) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (asize) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (avg) >= NN_NUMBUF_SZ);
- CTASSERT(sizeof (gang) >= NN_NUMBUF_SZ);
-
- if (t < DMU_OT_NUMTYPES)
- typename = dmu_ot[t].ot_name;
- else
- typename = zdb_ot_extname[t - DMU_OT_NUMTYPES];
-
- if (zcb.zcb_type[ZB_TOTAL][t].zb_asize == 0) {
- (void) printf("%6s\t%5s\t%5s\t%5s"
- "\t%5s\t%5s\t%6s\t%s\n",
- "-",
- "-",
- "-",
- "-",
- "-",
- "-",
- "-",
- typename);
- continue;
- }
-
- for (l = ZB_TOTAL - 1; l >= -1; l--) {
- level = (l == -1 ? ZB_TOTAL : l);
- zb = &zcb.zcb_type[level][t];
-
- if (zb->zb_asize == 0)
- continue;
-
- if (dump_opt['b'] < 3 && level != ZB_TOTAL)
- continue;
-
- if (level == 0 && zb->zb_asize ==
- zcb.zcb_type[ZB_TOTAL][t].zb_asize)
- continue;
-
- zdb_nicenum(zb->zb_count, csize,
- sizeof (csize));
- zdb_nicenum(zb->zb_lsize, lsize,
- sizeof (lsize));
- zdb_nicenum(zb->zb_psize, psize,
- sizeof (psize));
- zdb_nicenum(zb->zb_asize, asize,
- sizeof (asize));
- zdb_nicenum(zb->zb_asize / zb->zb_count, avg,
- sizeof (avg));
- zdb_nicenum(zb->zb_gangs, gang, sizeof (gang));
-
- (void) printf("%6s\t%5s\t%5s\t%5s\t%5s"
- "\t%5.2f\t%6.2f\t",
- csize, lsize, psize, asize, avg,
- (double)zb->zb_lsize / zb->zb_psize,
- 100.0 * zb->zb_asize / tzb->zb_asize);
-
- if (level == ZB_TOTAL)
- (void) printf("%s\n", typename);
- else
- (void) printf(" L%d %s\n",
- level, typename);
-
- if (dump_opt['b'] >= 3 && zb->zb_gangs > 0) {
- (void) printf("\t number of ganged "
- "blocks: %s\n", gang);
- }
-
- if (dump_opt['b'] >= 4) {
- (void) printf("psize "
- "(in 512-byte sectors): "
- "number of blocks\n");
- dump_histogram(zb->zb_psize_histogram,
- PSIZE_HISTO_SIZE, 0);
- }
- }
- }
- }
-
- (void) printf("\n");
-
- if (leaks)
- return (2);
-
- if (zcb.zcb_haderrors)
- return (3);
-
- return (0);
-}
-
-typedef struct zdb_ddt_entry {
- ddt_key_t zdde_key;
- uint64_t zdde_ref_blocks;
- uint64_t zdde_ref_lsize;
- uint64_t zdde_ref_psize;
- uint64_t zdde_ref_dsize;
- avl_node_t zdde_node;
-} zdb_ddt_entry_t;
-
-/* ARGSUSED */
-static int
-zdb_ddt_add_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
- const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg)
-{
- avl_tree_t *t = arg;
- avl_index_t where;
- zdb_ddt_entry_t *zdde, zdde_search;
-
- if (bp == NULL || BP_IS_HOLE(bp) || BP_IS_EMBEDDED(bp))
- return (0);
-
- if (dump_opt['S'] > 1 && zb->zb_level == ZB_ROOT_LEVEL) {
- (void) printf("traversing objset %llu, %llu objects, "
- "%lu blocks so far\n",
- (u_longlong_t)zb->zb_objset,
- (u_longlong_t)BP_GET_FILL(bp),
- avl_numnodes(t));
- }
-
- if (BP_IS_HOLE(bp) || BP_GET_CHECKSUM(bp) == ZIO_CHECKSUM_OFF ||
- BP_GET_LEVEL(bp) > 0 || DMU_OT_IS_METADATA(BP_GET_TYPE(bp)))
- return (0);
-
- ddt_key_fill(&zdde_search.zdde_key, bp);
-
- zdde = avl_find(t, &zdde_search, &where);
-
- if (zdde == NULL) {
- zdde = umem_zalloc(sizeof (*zdde), UMEM_NOFAIL);
- zdde->zdde_key = zdde_search.zdde_key;
- avl_insert(t, zdde, where);
- }
-
- zdde->zdde_ref_blocks += 1;
- zdde->zdde_ref_lsize += BP_GET_LSIZE(bp);
- zdde->zdde_ref_psize += BP_GET_PSIZE(bp);
- zdde->zdde_ref_dsize += bp_get_dsize_sync(spa, bp);
-
- return (0);
-}
-
-static void
-dump_simulated_ddt(spa_t *spa)
-{
- avl_tree_t t;
- void *cookie = NULL;
- zdb_ddt_entry_t *zdde;
- ddt_histogram_t ddh_total;
- ddt_stat_t dds_total;
-
- bzero(&ddh_total, sizeof (ddh_total));
- bzero(&dds_total, sizeof (dds_total));
- avl_create(&t, ddt_entry_compare,
- sizeof (zdb_ddt_entry_t), offsetof(zdb_ddt_entry_t, zdde_node));
-
- spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER);
-
- (void) traverse_pool(spa, 0, TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA,
- zdb_ddt_add_cb, &t);
-
- spa_config_exit(spa, SCL_CONFIG, FTAG);
-
- while ((zdde = avl_destroy_nodes(&t, &cookie)) != NULL) {
- ddt_stat_t dds;
- uint64_t refcnt = zdde->zdde_ref_blocks;
- ASSERT(refcnt != 0);
-
- dds.dds_blocks = zdde->zdde_ref_blocks / refcnt;
- dds.dds_lsize = zdde->zdde_ref_lsize / refcnt;
- dds.dds_psize = zdde->zdde_ref_psize / refcnt;
- dds.dds_dsize = zdde->zdde_ref_dsize / refcnt;
-
- dds.dds_ref_blocks = zdde->zdde_ref_blocks;
- dds.dds_ref_lsize = zdde->zdde_ref_lsize;
- dds.dds_ref_psize = zdde->zdde_ref_psize;
- dds.dds_ref_dsize = zdde->zdde_ref_dsize;
-
- ddt_stat_add(&ddh_total.ddh_stat[highbit64(refcnt) - 1],
- &dds, 0);
-
- umem_free(zdde, sizeof (*zdde));
- }
-
- avl_destroy(&t);
-
- ddt_histogram_stat(&dds_total, &ddh_total);
-
- (void) printf("Simulated DDT histogram:\n");
-
- zpool_dump_ddt(&dds_total, &ddh_total);
-
- dump_dedup_ratio(&dds_total);
-}
-
-static int
-verify_device_removal_feature_counts(spa_t *spa)
-{
- uint64_t dr_feature_refcount = 0;
- uint64_t oc_feature_refcount = 0;
- uint64_t indirect_vdev_count = 0;
- uint64_t precise_vdev_count = 0;
- uint64_t obsolete_counts_object_count = 0;
- uint64_t obsolete_sm_count = 0;
- uint64_t obsolete_counts_count = 0;
- uint64_t scip_count = 0;
- uint64_t obsolete_bpobj_count = 0;
- int ret = 0;
-
- spa_condensing_indirect_phys_t *scip =
- &spa->spa_condensing_indirect_phys;
- if (scip->scip_next_mapping_object != 0) {
- vdev_t *vd = spa->spa_root_vdev->vdev_child[scip->scip_vdev];
- ASSERT(scip->scip_prev_obsolete_sm_object != 0);
- ASSERT3P(vd->vdev_ops, ==, &vdev_indirect_ops);
-
- (void) printf("Condensing indirect vdev %llu: new mapping "
- "object %llu, prev obsolete sm %llu\n",
- (u_longlong_t)scip->scip_vdev,
- (u_longlong_t)scip->scip_next_mapping_object,
- (u_longlong_t)scip->scip_prev_obsolete_sm_object);
- if (scip->scip_prev_obsolete_sm_object != 0) {
- space_map_t *prev_obsolete_sm = NULL;
- VERIFY0(space_map_open(&prev_obsolete_sm,
- spa->spa_meta_objset,
- scip->scip_prev_obsolete_sm_object,
- 0, vd->vdev_asize, 0));
- dump_spacemap(spa->spa_meta_objset, prev_obsolete_sm);
- (void) printf("\n");
- space_map_close(prev_obsolete_sm);
- }
-
- scip_count += 2;
- }
-
- for (uint64_t i = 0; i < spa->spa_root_vdev->vdev_children; i++) {
- vdev_t *vd = spa->spa_root_vdev->vdev_child[i];
- vdev_indirect_config_t *vic = &vd->vdev_indirect_config;
-
- if (vic->vic_mapping_object != 0) {
- ASSERT(vd->vdev_ops == &vdev_indirect_ops ||
- vd->vdev_removing);
- indirect_vdev_count++;
-
- if (vd->vdev_indirect_mapping->vim_havecounts) {
- obsolete_counts_count++;
- }
- }
- if (vdev_obsolete_counts_are_precise(vd)) {
- ASSERT(vic->vic_mapping_object != 0);
- precise_vdev_count++;
- }
- if (vdev_obsolete_sm_object(vd) != 0) {
- ASSERT(vic->vic_mapping_object != 0);
- obsolete_sm_count++;
- }
- }
-
- (void) feature_get_refcount(spa,
- &spa_feature_table[SPA_FEATURE_DEVICE_REMOVAL],
- &dr_feature_refcount);
- (void) feature_get_refcount(spa,
- &spa_feature_table[SPA_FEATURE_OBSOLETE_COUNTS],
- &oc_feature_refcount);
-
- if (dr_feature_refcount != indirect_vdev_count) {
- ret = 1;
- (void) printf("Number of indirect vdevs (%llu) " \
- "does not match feature count (%llu)\n",
- (u_longlong_t)indirect_vdev_count,
- (u_longlong_t)dr_feature_refcount);
- } else {
- (void) printf("Verified device_removal feature refcount " \
- "of %llu is correct\n",
- (u_longlong_t)dr_feature_refcount);
- }
-
- if (zap_contains(spa_meta_objset(spa), DMU_POOL_DIRECTORY_OBJECT,
- DMU_POOL_OBSOLETE_BPOBJ) == 0) {
- obsolete_bpobj_count++;
- }
-
-
- obsolete_counts_object_count = precise_vdev_count;
- obsolete_counts_object_count += obsolete_sm_count;
- obsolete_counts_object_count += obsolete_counts_count;
- obsolete_counts_object_count += scip_count;
- obsolete_counts_object_count += obsolete_bpobj_count;
- obsolete_counts_object_count += remap_deadlist_count;
-
- if (oc_feature_refcount != obsolete_counts_object_count) {
- ret = 1;
- (void) printf("Number of obsolete counts objects (%llu) " \
- "does not match feature count (%llu)\n",
- (u_longlong_t)obsolete_counts_object_count,
- (u_longlong_t)oc_feature_refcount);
- (void) printf("pv:%llu os:%llu oc:%llu sc:%llu "
- "ob:%llu rd:%llu\n",
- (u_longlong_t)precise_vdev_count,
- (u_longlong_t)obsolete_sm_count,
- (u_longlong_t)obsolete_counts_count,
- (u_longlong_t)scip_count,
- (u_longlong_t)obsolete_bpobj_count,
- (u_longlong_t)remap_deadlist_count);
- } else {
- (void) printf("Verified indirect_refcount feature refcount " \
- "of %llu is correct\n",
- (u_longlong_t)oc_feature_refcount);
- }
- return (ret);
-}
-
-static void
-zdb_set_skip_mmp(char *target)
-{
- spa_t *spa;
-
- /*
- * Disable the activity check to allow examination of
- * active pools.
- */
- mutex_enter(&spa_namespace_lock);
- if ((spa = spa_lookup(target)) != NULL) {
- spa->spa_import_flags |= ZFS_IMPORT_SKIP_MMP;
- }
- mutex_exit(&spa_namespace_lock);
-}
-
-#define BOGUS_SUFFIX "_CHECKPOINTED_UNIVERSE"
-/*
- * Import the checkpointed state of the pool specified by the target
- * parameter as readonly. The function also accepts a pool config
- * as an optional parameter, else it attempts to infer the config by
- * the name of the target pool.
- *
- * Note that the checkpointed state's pool name will be the name of
- * the original pool with the above suffix appened to it. In addition,
- * if the target is not a pool name (e.g. a path to a dataset) then
- * the new_path parameter is populated with the updated path to
- * reflect the fact that we are looking into the checkpointed state.
- *
- * The function returns a newly-allocated copy of the name of the
- * pool containing the checkpointed state. When this copy is no
- * longer needed it should be freed with free(3C). Same thing
- * applies to the new_path parameter if allocated.
- */
-static char *
-import_checkpointed_state(char *target, nvlist_t *cfg, char **new_path)
-{
- int error = 0;
- char *poolname, *bogus_name;
-
- /* If the target is not a pool, the extract the pool name */
- char *path_start = strchr(target, '/');
- if (path_start != NULL) {
- size_t poolname_len = path_start - target;
- poolname = strndup(target, poolname_len);
- } else {
- poolname = target;
- }
-
- if (cfg == NULL) {
- zdb_set_skip_mmp(poolname);
- error = spa_get_stats(poolname, &cfg, NULL, 0);
- if (error != 0) {
- fatal("Tried to read config of pool \"%s\" but "
- "spa_get_stats() failed with error %d\n",
- poolname, error);
- }
- }
-
- (void) asprintf(&bogus_name, "%s%s", poolname, BOGUS_SUFFIX);
- fnvlist_add_string(cfg, ZPOOL_CONFIG_POOL_NAME, bogus_name);
-
- error = spa_import(bogus_name, cfg, NULL,
- ZFS_IMPORT_MISSING_LOG | ZFS_IMPORT_CHECKPOINT |
- ZFS_IMPORT_SKIP_MMP);
- if (error != 0) {
- fatal("Tried to import pool \"%s\" but spa_import() failed "
- "with error %d\n", bogus_name, error);
- }
-
- if (new_path != NULL && path_start != NULL)
- (void) asprintf(new_path, "%s%s", bogus_name, path_start);
-
- if (target != poolname)
- free(poolname);
-
- return (bogus_name);
-}
-
-typedef struct verify_checkpoint_sm_entry_cb_arg {
- vdev_t *vcsec_vd;
-
- /* the following fields are only used for printing progress */
- uint64_t vcsec_entryid;
- uint64_t vcsec_num_entries;
-} verify_checkpoint_sm_entry_cb_arg_t;
-
-#define ENTRIES_PER_PROGRESS_UPDATE 10000
-
-static int
-verify_checkpoint_sm_entry_cb(space_map_entry_t *sme, void *arg)
-{
- verify_checkpoint_sm_entry_cb_arg_t *vcsec = arg;
- vdev_t *vd = vcsec->vcsec_vd;
- metaslab_t *ms = vd->vdev_ms[sme->sme_offset >> vd->vdev_ms_shift];
- uint64_t end = sme->sme_offset + sme->sme_run;
-
- ASSERT(sme->sme_type == SM_FREE);
-
- if ((vcsec->vcsec_entryid % ENTRIES_PER_PROGRESS_UPDATE) == 0) {
- (void) fprintf(stderr,
- "\rverifying vdev %llu, space map entry %llu of %llu ...",
- (longlong_t)vd->vdev_id,
- (longlong_t)vcsec->vcsec_entryid,
- (longlong_t)vcsec->vcsec_num_entries);
- }
- vcsec->vcsec_entryid++;
-
- /*
- * See comment in checkpoint_sm_exclude_entry_cb()
- */
- VERIFY3U(sme->sme_offset, >=, ms->ms_start);
- VERIFY3U(end, <=, ms->ms_start + ms->ms_size);
-
- /*
- * The entries in the vdev_checkpoint_sm should be marked as
- * allocated in the checkpointed state of the pool, therefore
- * their respective ms_allocateable trees should not contain them.
- */
- mutex_enter(&ms->ms_lock);
- range_tree_verify_not_present(ms->ms_allocatable,
- sme->sme_offset, sme->sme_run);
- mutex_exit(&ms->ms_lock);
-
- return (0);
-}
-
-/*
- * Verify that all segments in the vdev_checkpoint_sm are allocated
- * according to the checkpoint's ms_sm (i.e. are not in the checkpoint's
- * ms_allocatable).
- *
- * Do so by comparing the checkpoint space maps (vdev_checkpoint_sm) of
- * each vdev in the current state of the pool to the metaslab space maps
- * (ms_sm) of the checkpointed state of the pool.
- *
- * Note that the function changes the state of the ms_allocatable
- * trees of the current spa_t. The entries of these ms_allocatable
- * trees are cleared out and then repopulated from with the free
- * entries of their respective ms_sm space maps.
- */
-static void
-verify_checkpoint_vdev_spacemaps(spa_t *checkpoint, spa_t *current)
-{
- vdev_t *ckpoint_rvd = checkpoint->spa_root_vdev;
- vdev_t *current_rvd = current->spa_root_vdev;
-
- load_concrete_ms_allocatable_trees(checkpoint, SM_FREE);
-
- for (uint64_t c = 0; c < ckpoint_rvd->vdev_children; c++) {
- vdev_t *ckpoint_vd = ckpoint_rvd->vdev_child[c];
- vdev_t *current_vd = current_rvd->vdev_child[c];
-
- space_map_t *checkpoint_sm = NULL;
- uint64_t checkpoint_sm_obj;
-
- if (ckpoint_vd->vdev_ops == &vdev_indirect_ops) {
- /*
- * Since we don't allow device removal in a pool
- * that has a checkpoint, we expect that all removed
- * vdevs were removed from the pool before the
- * checkpoint.
- */
- ASSERT3P(current_vd->vdev_ops, ==, &vdev_indirect_ops);
- continue;
- }
-
- /*
- * If the checkpoint space map doesn't exist, then nothing
- * here is checkpointed so there's nothing to verify.
- */
- if (current_vd->vdev_top_zap == 0 ||
- zap_contains(spa_meta_objset(current),
- current_vd->vdev_top_zap,
- VDEV_TOP_ZAP_POOL_CHECKPOINT_SM) != 0)
- continue;
-
- VERIFY0(zap_lookup(spa_meta_objset(current),
- current_vd->vdev_top_zap, VDEV_TOP_ZAP_POOL_CHECKPOINT_SM,
- sizeof (uint64_t), 1, &checkpoint_sm_obj));
-
- VERIFY0(space_map_open(&checkpoint_sm, spa_meta_objset(current),
- checkpoint_sm_obj, 0, current_vd->vdev_asize,
- current_vd->vdev_ashift));
-
- verify_checkpoint_sm_entry_cb_arg_t vcsec;
- vcsec.vcsec_vd = ckpoint_vd;
- vcsec.vcsec_entryid = 0;
- vcsec.vcsec_num_entries =
- space_map_length(checkpoint_sm) / sizeof (uint64_t);
- VERIFY0(space_map_iterate(checkpoint_sm,
- space_map_length(checkpoint_sm),
- verify_checkpoint_sm_entry_cb, &vcsec));
- dump_spacemap(current->spa_meta_objset, checkpoint_sm);
- space_map_close(checkpoint_sm);
- }
-
- /*
- * If we've added vdevs since we took the checkpoint, ensure
- * that their checkpoint space maps are empty.
- */
- if (ckpoint_rvd->vdev_children < current_rvd->vdev_children) {
- for (uint64_t c = ckpoint_rvd->vdev_children;
- c < current_rvd->vdev_children; c++) {
- vdev_t *current_vd = current_rvd->vdev_child[c];
- ASSERT3P(current_vd->vdev_checkpoint_sm, ==, NULL);
- }
- }
-
- /* for cleaner progress output */
- (void) fprintf(stderr, "\n");
-}
-
-/*
- * Verifies that all space that's allocated in the checkpoint is
- * still allocated in the current version, by checking that everything
- * in checkpoint's ms_allocatable (which is actually allocated, not
- * allocatable/free) is not present in current's ms_allocatable.
- *
- * Note that the function changes the state of the ms_allocatable
- * trees of both spas when called. The entries of all ms_allocatable
- * trees are cleared out and then repopulated from their respective
- * ms_sm space maps. In the checkpointed state we load the allocated
- * entries, and in the current state we load the free entries.
- */
-static void
-verify_checkpoint_ms_spacemaps(spa_t *checkpoint, spa_t *current)
-{
- vdev_t *ckpoint_rvd = checkpoint->spa_root_vdev;
- vdev_t *current_rvd = current->spa_root_vdev;
-
- load_concrete_ms_allocatable_trees(checkpoint, SM_ALLOC);
- load_concrete_ms_allocatable_trees(current, SM_FREE);
-
- for (uint64_t i = 0; i < ckpoint_rvd->vdev_children; i++) {
- vdev_t *ckpoint_vd = ckpoint_rvd->vdev_child[i];
- vdev_t *current_vd = current_rvd->vdev_child[i];
-
- if (ckpoint_vd->vdev_ops == &vdev_indirect_ops) {
- /*
- * See comment in verify_checkpoint_vdev_spacemaps()
- */
- ASSERT3P(current_vd->vdev_ops, ==, &vdev_indirect_ops);
- continue;
- }
-
- for (uint64_t m = 0; m < ckpoint_vd->vdev_ms_count; m++) {
- metaslab_t *ckpoint_msp = ckpoint_vd->vdev_ms[m];
- metaslab_t *current_msp = current_vd->vdev_ms[m];
-
- (void) fprintf(stderr,
- "\rverifying vdev %llu of %llu, "
- "metaslab %llu of %llu ...",
- (longlong_t)current_vd->vdev_id,
- (longlong_t)current_rvd->vdev_children,
- (longlong_t)current_vd->vdev_ms[m]->ms_id,
- (longlong_t)current_vd->vdev_ms_count);
-
- /*
- * We walk through the ms_allocatable trees that
- * are loaded with the allocated blocks from the
- * ms_sm spacemaps of the checkpoint. For each
- * one of these ranges we ensure that none of them
- * exists in the ms_allocatable trees of the
- * current state which are loaded with the ranges
- * that are currently free.
- *
- * This way we ensure that none of the blocks that
- * are part of the checkpoint were freed by mistake.
- */
- range_tree_walk(ckpoint_msp->ms_allocatable,
- (range_tree_func_t *)range_tree_verify_not_present,
- current_msp->ms_allocatable);
- }
- }
-
- /* for cleaner progress output */
- (void) fprintf(stderr, "\n");
-}
-
-static void
-verify_checkpoint_blocks(spa_t *spa)
-{
- ASSERT(!dump_opt['L']);
-
- spa_t *checkpoint_spa;
- char *checkpoint_pool;
- nvlist_t *config = NULL;
- int error = 0;
-
- /*
- * We import the checkpointed state of the pool (under a different
- * name) so we can do verification on it against the current state
- * of the pool.
- */
- checkpoint_pool = import_checkpointed_state(spa->spa_name, config,
- NULL);
- ASSERT(strcmp(spa->spa_name, checkpoint_pool) != 0);
-
- error = spa_open(checkpoint_pool, &checkpoint_spa, FTAG);
- if (error != 0) {
- fatal("Tried to open pool \"%s\" but spa_open() failed with "
- "error %d\n", checkpoint_pool, error);
- }
-
- /*
- * Ensure that ranges in the checkpoint space maps of each vdev
- * are allocated according to the checkpointed state's metaslab
- * space maps.
- */
- verify_checkpoint_vdev_spacemaps(checkpoint_spa, spa);
-
- /*
- * Ensure that allocated ranges in the checkpoint's metaslab
- * space maps remain allocated in the metaslab space maps of
- * the current state.
- */
- verify_checkpoint_ms_spacemaps(checkpoint_spa, spa);
-
- /*
- * Once we are done, we get rid of the checkpointed state.
- */
- spa_close(checkpoint_spa, FTAG);
- free(checkpoint_pool);
-}
-
-static void
-dump_leftover_checkpoint_blocks(spa_t *spa)
-{
- vdev_t *rvd = spa->spa_root_vdev;
-
- for (uint64_t i = 0; i < rvd->vdev_children; i++) {
- vdev_t *vd = rvd->vdev_child[i];
-
- space_map_t *checkpoint_sm = NULL;
- uint64_t checkpoint_sm_obj;
-
- if (vd->vdev_top_zap == 0)
- continue;
-
- if (zap_contains(spa_meta_objset(spa), vd->vdev_top_zap,
- VDEV_TOP_ZAP_POOL_CHECKPOINT_SM) != 0)
- continue;
-
- VERIFY0(zap_lookup(spa_meta_objset(spa), vd->vdev_top_zap,
- VDEV_TOP_ZAP_POOL_CHECKPOINT_SM,
- sizeof (uint64_t), 1, &checkpoint_sm_obj));
-
- VERIFY0(space_map_open(&checkpoint_sm, spa_meta_objset(spa),
- checkpoint_sm_obj, 0, vd->vdev_asize, vd->vdev_ashift));
- dump_spacemap(spa->spa_meta_objset, checkpoint_sm);
- space_map_close(checkpoint_sm);
- }
-}
-
-static int
-verify_checkpoint(spa_t *spa)
-{
- uberblock_t checkpoint;
- int error;
-
- if (!spa_feature_is_active(spa, SPA_FEATURE_POOL_CHECKPOINT))
- return (0);
-
- error = zap_lookup(spa->spa_meta_objset, DMU_POOL_DIRECTORY_OBJECT,
- DMU_POOL_ZPOOL_CHECKPOINT, sizeof (uint64_t),
- sizeof (uberblock_t) / sizeof (uint64_t), &checkpoint);
-
- if (error == ENOENT && !dump_opt['L']) {
- /*
- * If the feature is active but the uberblock is missing
- * then we must be in the middle of discarding the
- * checkpoint.
- */
- (void) printf("\nPartially discarded checkpoint "
- "state found:\n");
- dump_leftover_checkpoint_blocks(spa);
- return (0);
- } else if (error != 0) {
- (void) printf("lookup error %d when looking for "
- "checkpointed uberblock in MOS\n", error);
- return (error);
- }
- dump_uberblock(&checkpoint, "\nCheckpointed uberblock found:\n", "\n");
-
- if (checkpoint.ub_checkpoint_txg == 0) {
- (void) printf("\nub_checkpoint_txg not set in checkpointed "
- "uberblock\n");
- error = 3;
- }
-
- if (error == 0 && !dump_opt['L'])
- verify_checkpoint_blocks(spa);
-
- return (error);
-}
-
-/* ARGSUSED */
-static void
-mos_leaks_cb(void *arg, uint64_t start, uint64_t size)
-{
- for (uint64_t i = start; i < size; i++) {
- (void) printf("MOS object %llu referenced but not allocated\n",
- (u_longlong_t)i);
- }
-}
-
-static range_tree_t *mos_refd_objs;
-
-static void
-mos_obj_refd(uint64_t obj)
-{
- if (obj != 0 && mos_refd_objs != NULL)
- range_tree_add(mos_refd_objs, obj, 1);
-}
-
-static void
-mos_leak_vdev(vdev_t *vd)
-{
- mos_obj_refd(vd->vdev_dtl_object);
- mos_obj_refd(vd->vdev_ms_array);
- mos_obj_refd(vd->vdev_top_zap);
- mos_obj_refd(vd->vdev_indirect_config.vic_births_object);
- mos_obj_refd(vd->vdev_indirect_config.vic_mapping_object);
- mos_obj_refd(vd->vdev_leaf_zap);
- if (vd->vdev_checkpoint_sm != NULL)
- mos_obj_refd(vd->vdev_checkpoint_sm->sm_object);
- if (vd->vdev_indirect_mapping != NULL) {
- mos_obj_refd(vd->vdev_indirect_mapping->
- vim_phys->vimp_counts_object);
- }
- if (vd->vdev_obsolete_sm != NULL)
- mos_obj_refd(vd->vdev_obsolete_sm->sm_object);
-
- for (uint64_t m = 0; m < vd->vdev_ms_count; m++) {
- metaslab_t *ms = vd->vdev_ms[m];
- mos_obj_refd(space_map_object(ms->ms_sm));
- }
-
- for (uint64_t c = 0; c < vd->vdev_children; c++) {
- mos_leak_vdev(vd->vdev_child[c]);
- }
-}
-
-static int
-dump_mos_leaks(spa_t *spa)
-{
- int rv = 0;
- objset_t *mos = spa->spa_meta_objset;
- dsl_pool_t *dp = spa->spa_dsl_pool;
-
- /* Visit and mark all referenced objects in the MOS */
-
- mos_obj_refd(DMU_POOL_DIRECTORY_OBJECT);
- mos_obj_refd(spa->spa_pool_props_object);
- mos_obj_refd(spa->spa_config_object);
- mos_obj_refd(spa->spa_ddt_stat_object);
- mos_obj_refd(spa->spa_feat_desc_obj);
- mos_obj_refd(spa->spa_feat_enabled_txg_obj);
- mos_obj_refd(spa->spa_feat_for_read_obj);
- mos_obj_refd(spa->spa_feat_for_write_obj);
- mos_obj_refd(spa->spa_history);
- mos_obj_refd(spa->spa_errlog_last);
- mos_obj_refd(spa->spa_errlog_scrub);
- mos_obj_refd(spa->spa_all_vdev_zaps);
- mos_obj_refd(spa->spa_dsl_pool->dp_bptree_obj);
- mos_obj_refd(spa->spa_dsl_pool->dp_tmp_userrefs_obj);
- mos_obj_refd(spa->spa_dsl_pool->dp_scan->scn_phys.scn_queue_obj);
- bpobj_count_refd(&spa->spa_deferred_bpobj);
- mos_obj_refd(dp->dp_empty_bpobj);
- bpobj_count_refd(&dp->dp_obsolete_bpobj);
- bpobj_count_refd(&dp->dp_free_bpobj);
- mos_obj_refd(spa->spa_l2cache.sav_object);
- mos_obj_refd(spa->spa_spares.sav_object);
-
- mos_obj_refd(spa->spa_condensing_indirect_phys.
- scip_next_mapping_object);
- mos_obj_refd(spa->spa_condensing_indirect_phys.
- scip_prev_obsolete_sm_object);
- if (spa->spa_condensing_indirect_phys.scip_next_mapping_object != 0) {
- vdev_indirect_mapping_t *vim =
- vdev_indirect_mapping_open(mos,
- spa->spa_condensing_indirect_phys.scip_next_mapping_object);
- mos_obj_refd(vim->vim_phys->vimp_counts_object);
- vdev_indirect_mapping_close(vim);
- }
-
- if (dp->dp_origin_snap != NULL) {
- dsl_dataset_t *ds;
-
- dsl_pool_config_enter(dp, FTAG);
- VERIFY0(dsl_dataset_hold_obj(dp,
- dsl_dataset_phys(dp->dp_origin_snap)->ds_next_snap_obj,
- FTAG, &ds));
- count_ds_mos_objects(ds);
- dump_deadlist(&ds->ds_deadlist);
- dsl_dataset_rele(ds, FTAG);
- dsl_pool_config_exit(dp, FTAG);
-
- count_ds_mos_objects(dp->dp_origin_snap);
- dump_deadlist(&dp->dp_origin_snap->ds_deadlist);
- }
- count_dir_mos_objects(dp->dp_mos_dir);
- if (dp->dp_free_dir != NULL)
- count_dir_mos_objects(dp->dp_free_dir);
- if (dp->dp_leak_dir != NULL)
- count_dir_mos_objects(dp->dp_leak_dir);
-
- mos_leak_vdev(spa->spa_root_vdev);
-
- for (uint64_t class = 0; class < DDT_CLASSES; class++) {
- for (uint64_t type = 0; type < DDT_TYPES; type++) {
- for (uint64_t cksum = 0;
- cksum < ZIO_CHECKSUM_FUNCTIONS; cksum++) {
- ddt_t *ddt = spa->spa_ddt[cksum];
- mos_obj_refd(ddt->ddt_object[type][class]);
- }
- }
- }
-
- /*
- * Visit all allocated objects and make sure they are referenced.
- */
- uint64_t object = 0;
- while (dmu_object_next(mos, &object, B_FALSE, 0) == 0) {
- if (range_tree_contains(mos_refd_objs, object, 1)) {
- range_tree_remove(mos_refd_objs, object, 1);
- } else {
- dmu_object_info_t doi;
- const char *name;
- dmu_object_info(mos, object, &doi);
- if (doi.doi_type & DMU_OT_NEWTYPE) {
- dmu_object_byteswap_t bswap =
- DMU_OT_BYTESWAP(doi.doi_type);
- name = dmu_ot_byteswap[bswap].ob_name;
- } else {
- name = dmu_ot[doi.doi_type].ot_name;
- }
-
- (void) printf("MOS object %llu (%s) leaked\n",
- (u_longlong_t)object, name);
- rv = 2;
- }
- }
- (void) range_tree_walk(mos_refd_objs, mos_leaks_cb, NULL);
- if (!range_tree_is_empty(mos_refd_objs))
- rv = 2;
- range_tree_vacate(mos_refd_objs, NULL, NULL);
- range_tree_destroy(mos_refd_objs);
- return (rv);
-}
-
-static void
-dump_zpool(spa_t *spa)
-{
- dsl_pool_t *dp = spa_get_dsl(spa);
- int rc = 0;
-
- if (dump_opt['S']) {
- dump_simulated_ddt(spa);
- return;
- }
-
- if (!dump_opt['e'] && dump_opt['C'] > 1) {
- (void) printf("\nCached configuration:\n");
- dump_nvlist(spa->spa_config, 8);
- }
-
- if (dump_opt['C'])
- dump_config(spa);
-
- if (dump_opt['u'])
- dump_uberblock(&spa->spa_uberblock, "\nUberblock:\n", "\n");
-
- if (dump_opt['D'])
- dump_all_ddts(spa);
-
- if (dump_opt['d'] > 2 || dump_opt['m'])
- dump_metaslabs(spa);
- if (dump_opt['M'])
- dump_metaslab_groups(spa);
-
- if (dump_opt['d'] || dump_opt['i']) {
- mos_refd_objs = range_tree_create(NULL, NULL);
- dump_dir(dp->dp_meta_objset);
-
- if (dump_opt['d'] >= 3) {
- dsl_pool_t *dp = spa->spa_dsl_pool;
- dump_full_bpobj(&spa->spa_deferred_bpobj,
- "Deferred frees", 0);
- if (spa_version(spa) >= SPA_VERSION_DEADLISTS) {
- dump_full_bpobj(&dp->dp_free_bpobj,
- "Pool snapshot frees", 0);
- }
- if (bpobj_is_open(&dp->dp_obsolete_bpobj)) {
- ASSERT(spa_feature_is_enabled(spa,
- SPA_FEATURE_DEVICE_REMOVAL));
- dump_full_bpobj(&dp->dp_obsolete_bpobj,
- "Pool obsolete blocks", 0);
- }
-
- if (spa_feature_is_active(spa,
- SPA_FEATURE_ASYNC_DESTROY)) {
- dump_bptree(spa->spa_meta_objset,
- dp->dp_bptree_obj,
- "Pool dataset frees");
- }
- dump_dtl(spa->spa_root_vdev, 0);
- }
- (void) dmu_objset_find(spa_name(spa), dump_one_dir,
- NULL, DS_FIND_SNAPSHOTS | DS_FIND_CHILDREN);
-
- if (rc == 0 && !dump_opt['L'])
- rc = dump_mos_leaks(spa);
-
- for (spa_feature_t f = 0; f < SPA_FEATURES; f++) {
- uint64_t refcount;
-
- if (!(spa_feature_table[f].fi_flags &
- ZFEATURE_FLAG_PER_DATASET)) {
- ASSERT0(dataset_feature_count[f]);
- continue;
- }
- (void) feature_get_refcount(spa,
- &spa_feature_table[f], &refcount);
- if (dataset_feature_count[f] != refcount) {
- (void) printf("%s feature refcount mismatch: "
- "%lld datasets != %lld refcount\n",
- spa_feature_table[f].fi_uname,
- (longlong_t)dataset_feature_count[f],
- (longlong_t)refcount);
- rc = 2;
- } else {
- (void) printf("Verified %s feature refcount "
- "of %llu is correct\n",
- spa_feature_table[f].fi_uname,
- (longlong_t)refcount);
- }
- }
-
- if (rc == 0) {
- rc = verify_device_removal_feature_counts(spa);
- }
- }
-
- if (rc == 0 && (dump_opt['b'] || dump_opt['c']))
- rc = dump_block_stats(spa);
-
- if (rc == 0)
- rc = verify_spacemap_refcounts(spa);
-
- if (dump_opt['s'])
- show_pool_stats(spa);
-
- if (dump_opt['h'])
- dump_history(spa);
-
- if (rc == 0)
- rc = verify_checkpoint(spa);
-
- if (rc != 0) {
- dump_debug_buffer();
- exit(rc);
- }
-}
-
-#define ZDB_FLAG_CHECKSUM 0x0001
-#define ZDB_FLAG_DECOMPRESS 0x0002
-#define ZDB_FLAG_BSWAP 0x0004
-#define ZDB_FLAG_GBH 0x0008
-#define ZDB_FLAG_INDIRECT 0x0010
-#define ZDB_FLAG_PHYS 0x0020
-#define ZDB_FLAG_RAW 0x0040
-#define ZDB_FLAG_PRINT_BLKPTR 0x0080
-
-static int flagbits[256];
-
-static void
-zdb_print_blkptr(blkptr_t *bp, int flags)
-{
- char blkbuf[BP_SPRINTF_LEN];
-
- if (flags & ZDB_FLAG_BSWAP)
- byteswap_uint64_array((void *)bp, sizeof (blkptr_t));
-
- snprintf_blkptr(blkbuf, sizeof (blkbuf), bp);
- (void) printf("%s\n", blkbuf);
-}
-
-static void
-zdb_dump_indirect(blkptr_t *bp, int nbps, int flags)
-{
- int i;
-
- for (i = 0; i < nbps; i++)
- zdb_print_blkptr(&bp[i], flags);
-}
-
-static void
-zdb_dump_gbh(void *buf, int flags)
-{
- zdb_dump_indirect((blkptr_t *)buf, SPA_GBH_NBLKPTRS, flags);
-}
-
-static void
-zdb_dump_block_raw(void *buf, uint64_t size, int flags)
-{
- if (flags & ZDB_FLAG_BSWAP)
- byteswap_uint64_array(buf, size);
- (void) write(1, buf, size);
-}
-
-static void
-zdb_dump_block(char *label, void *buf, uint64_t size, int flags)
-{
- uint64_t *d = (uint64_t *)buf;
- unsigned nwords = size / sizeof (uint64_t);
- int do_bswap = !!(flags & ZDB_FLAG_BSWAP);
- unsigned i, j;
- const char *hdr;
- char *c;
-
-
- if (do_bswap)
- hdr = " 7 6 5 4 3 2 1 0 f e d c b a 9 8";
- else
- hdr = " 0 1 2 3 4 5 6 7 8 9 a b c d e f";
-
- (void) printf("\n%s\n%6s %s 0123456789abcdef\n", label, "", hdr);
-
- for (i = 0; i < nwords; i += 2) {
- (void) printf("%06llx: %016llx %016llx ",
- (u_longlong_t)(i * sizeof (uint64_t)),
- (u_longlong_t)(do_bswap ? BSWAP_64(d[i]) : d[i]),
- (u_longlong_t)(do_bswap ? BSWAP_64(d[i + 1]) : d[i + 1]));
-
- c = (char *)&d[i];
- for (j = 0; j < 2 * sizeof (uint64_t); j++)
- (void) printf("%c", isprint(c[j]) ? c[j] : '.');
- (void) printf("\n");
- }
-}
-
-/*
- * There are two acceptable formats:
- * leaf_name - For example: c1t0d0 or /tmp/ztest.0a
- * child[.child]* - For example: 0.1.1
- *
- * The second form can be used to specify arbitrary vdevs anywhere
- * in the heirarchy. For example, in a pool with a mirror of
- * RAID-Zs, you can specify either RAID-Z vdev with 0.0 or 0.1 .
- */
-static vdev_t *
-zdb_vdev_lookup(vdev_t *vdev, const char *path)
-{
- char *s, *p, *q;
- unsigned i;
-
- if (vdev == NULL)
- return (NULL);
-
- /* First, assume the x.x.x.x format */
- i = strtoul(path, &s, 10);
- if (s == path || (s && *s != '.' && *s != '\0'))
- goto name;
- if (i >= vdev->vdev_children)
- return (NULL);
-
- vdev = vdev->vdev_child[i];
- if (*s == '\0')
- return (vdev);
- return (zdb_vdev_lookup(vdev, s+1));
-
-name:
- for (i = 0; i < vdev->vdev_children; i++) {
- vdev_t *vc = vdev->vdev_child[i];
-
- if (vc->vdev_path == NULL) {
- vc = zdb_vdev_lookup(vc, path);
- if (vc == NULL)
- continue;
- else
- return (vc);
- }
-
- p = strrchr(vc->vdev_path, '/');
- p = p ? p + 1 : vc->vdev_path;
- q = &vc->vdev_path[strlen(vc->vdev_path) - 2];
-
- if (strcmp(vc->vdev_path, path) == 0)
- return (vc);
- if (strcmp(p, path) == 0)
- return (vc);
- if (strcmp(q, "s0") == 0 && strncmp(p, path, q - p) == 0)
- return (vc);
- }
-
- return (NULL);
-}
-
-/* ARGSUSED */
-static int
-random_get_pseudo_bytes_cb(void *buf, size_t len, void *unused)
-{
- return (random_get_pseudo_bytes(buf, len));
-}
-
-/*
- * Read a block from a pool and print it out. The syntax of the
- * block descriptor is:
- *
- * pool:vdev_specifier:offset:size[:flags]
- *
- * pool - The name of the pool you wish to read from
- * vdev_specifier - Which vdev (see comment for zdb_vdev_lookup)
- * offset - offset, in hex, in bytes
- * size - Amount of data to read, in hex, in bytes
- * flags - A string of characters specifying options
- * b: Decode a blkptr at given offset within block
- * *c: Calculate and display checksums
- * d: Decompress data before dumping
- * e: Byteswap data before dumping
- * g: Display data as a gang block header
- * i: Display as an indirect block
- * p: Do I/O to physical offset
- * r: Dump raw data to stdout
- *
- * * = not yet implemented
- */
-static void
-zdb_read_block(char *thing, spa_t *spa)
-{
- blkptr_t blk, *bp = &blk;
- dva_t *dva = bp->blk_dva;
- int flags = 0;
- uint64_t offset = 0, size = 0, psize = 0, lsize = 0, blkptr_offset = 0;
- zio_t *zio;
- vdev_t *vd;
- abd_t *pabd;
- void *lbuf, *buf;
- const char *s, *vdev;
- char *p, *dup, *flagstr;
- int i, error;
-
- dup = strdup(thing);
- s = strtok(dup, ":");
- vdev = s ? s : "";
- s = strtok(NULL, ":");
- offset = strtoull(s ? s : "", NULL, 16);
- s = strtok(NULL, ":");
- size = strtoull(s ? s : "", NULL, 16);
- s = strtok(NULL, ":");
- if (s)
- flagstr = strdup(s);
- else
- flagstr = strdup("");
-
- s = NULL;
- if (size == 0)
- s = "size must not be zero";
- if (!IS_P2ALIGNED(size, DEV_BSIZE))
- s = "size must be a multiple of sector size";
- if (!IS_P2ALIGNED(offset, DEV_BSIZE))
- s = "offset must be a multiple of sector size";
- if (s) {
- (void) printf("Invalid block specifier: %s - %s\n", thing, s);
- free(flagstr);
- free(dup);
- return;
- }
-
- for (s = strtok(flagstr, ":"); s; s = strtok(NULL, ":")) {
- for (i = 0; flagstr[i]; i++) {
- int bit = flagbits[(uchar_t)flagstr[i]];
-
- if (bit == 0) {
- (void) printf("***Invalid flag: %c\n",
- flagstr[i]);
- continue;
- }
- flags |= bit;
-
- /* If it's not something with an argument, keep going */
- if ((bit & (ZDB_FLAG_CHECKSUM |
- ZDB_FLAG_PRINT_BLKPTR)) == 0)
- continue;
-
- p = &flagstr[i + 1];
- if (bit == ZDB_FLAG_PRINT_BLKPTR)
- blkptr_offset = strtoull(p, &p, 16);
- if (*p != ':' && *p != '\0') {
- (void) printf("***Invalid flag arg: '%s'\n", s);
- free(flagstr);
- free(dup);
- return;
- }
- i += p - &flagstr[i + 1]; /* skip over the number */
- }
- }
- free(flagstr);
-
- vd = zdb_vdev_lookup(spa->spa_root_vdev, vdev);
- if (vd == NULL) {
- (void) printf("***Invalid vdev: %s\n", vdev);
- free(dup);
- return;
- } else {
- if (vd->vdev_path)
- (void) fprintf(stderr, "Found vdev: %s\n",
- vd->vdev_path);
- else
- (void) fprintf(stderr, "Found vdev type: %s\n",
- vd->vdev_ops->vdev_op_type);
- }
-
- psize = size;
- lsize = size;
-
- pabd = abd_alloc_linear(SPA_MAXBLOCKSIZE, B_FALSE);
- lbuf = umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL);
-
- BP_ZERO(bp);
-
- DVA_SET_VDEV(&dva[0], vd->vdev_id);
- DVA_SET_OFFSET(&dva[0], offset);
- DVA_SET_GANG(&dva[0], !!(flags & ZDB_FLAG_GBH));
- DVA_SET_ASIZE(&dva[0], vdev_psize_to_asize(vd, psize));
-
- BP_SET_BIRTH(bp, TXG_INITIAL, TXG_INITIAL);
-
- BP_SET_LSIZE(bp, lsize);
- BP_SET_PSIZE(bp, psize);
- BP_SET_COMPRESS(bp, ZIO_COMPRESS_OFF);
- BP_SET_CHECKSUM(bp, ZIO_CHECKSUM_OFF);
- BP_SET_TYPE(bp, DMU_OT_NONE);
- BP_SET_LEVEL(bp, 0);
- BP_SET_DEDUP(bp, 0);
- BP_SET_BYTEORDER(bp, ZFS_HOST_BYTEORDER);
-
- spa_config_enter(spa, SCL_STATE, FTAG, RW_READER);
- zio = zio_root(spa, NULL, NULL, 0);
-
- if (vd == vd->vdev_top) {
- /*
- * Treat this as a normal block read.
- */
- zio_nowait(zio_read(zio, spa, bp, pabd, psize, NULL, NULL,
- ZIO_PRIORITY_SYNC_READ,
- ZIO_FLAG_CANFAIL | ZIO_FLAG_RAW, NULL));
- } else {
- /*
- * Treat this as a vdev child I/O.
- */
- zio_nowait(zio_vdev_child_io(zio, bp, vd, offset, pabd,
- psize, ZIO_TYPE_READ, ZIO_PRIORITY_SYNC_READ,
- ZIO_FLAG_DONT_CACHE | ZIO_FLAG_DONT_QUEUE |
- ZIO_FLAG_DONT_PROPAGATE | ZIO_FLAG_DONT_RETRY |
- ZIO_FLAG_CANFAIL | ZIO_FLAG_RAW | ZIO_FLAG_OPTIONAL,
- NULL, NULL));
- }
-
- error = zio_wait(zio);
- spa_config_exit(spa, SCL_STATE, FTAG);
-
- if (error) {
- (void) printf("Read of %s failed, error: %d\n", thing, error);
- goto out;
- }
-
- if (flags & ZDB_FLAG_DECOMPRESS) {
- /*
- * We don't know how the data was compressed, so just try
- * every decompress function at every inflated blocksize.
- */
- enum zio_compress c;
- void *pbuf2 = umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL);
- void *lbuf2 = umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL);
-
- abd_copy_to_buf(pbuf2, pabd, psize);
-
- VERIFY0(abd_iterate_func(pabd, psize, SPA_MAXBLOCKSIZE - psize,
- random_get_pseudo_bytes_cb, NULL));
-
- VERIFY0(random_get_pseudo_bytes((uint8_t *)pbuf2 + psize,
- SPA_MAXBLOCKSIZE - psize));
-
- for (lsize = SPA_MAXBLOCKSIZE; lsize > psize;
- lsize -= SPA_MINBLOCKSIZE) {
- for (c = 0; c < ZIO_COMPRESS_FUNCTIONS; c++) {
- if (zio_decompress_data(c, pabd,
- lbuf, psize, lsize) == 0 &&
- zio_decompress_data_buf(c, pbuf2,
- lbuf2, psize, lsize) == 0 &&
- bcmp(lbuf, lbuf2, lsize) == 0)
- break;
- }
- if (c != ZIO_COMPRESS_FUNCTIONS)
- break;
- lsize -= SPA_MINBLOCKSIZE;
- }
-
- umem_free(pbuf2, SPA_MAXBLOCKSIZE);
- umem_free(lbuf2, SPA_MAXBLOCKSIZE);
-
- if (lsize <= psize) {
- (void) printf("Decompress of %s failed\n", thing);
- goto out;
- }
- buf = lbuf;
- size = lsize;
- } else {
- buf = abd_to_buf(pabd);
- size = psize;
- }
-
- if (flags & ZDB_FLAG_PRINT_BLKPTR)
- zdb_print_blkptr((blkptr_t *)(void *)
- ((uintptr_t)buf + (uintptr_t)blkptr_offset), flags);
- else if (flags & ZDB_FLAG_RAW)
- zdb_dump_block_raw(buf, size, flags);
- else if (flags & ZDB_FLAG_INDIRECT)
- zdb_dump_indirect((blkptr_t *)buf, size / sizeof (blkptr_t),
- flags);
- else if (flags & ZDB_FLAG_GBH)
- zdb_dump_gbh(buf, flags);
- else
- zdb_dump_block(thing, buf, size, flags);
-
-out:
- abd_free(pabd);
- umem_free(lbuf, SPA_MAXBLOCKSIZE);
- free(dup);
-}
-
-static void
-zdb_embedded_block(char *thing)
-{
- blkptr_t bp;
- unsigned long long *words = (void *)&bp;
- char *buf;
- int err;
-
- bzero(&bp, sizeof (bp));
- err = sscanf(thing, "%llx:%llx:%llx:%llx:%llx:%llx:%llx:%llx:"
- "%llx:%llx:%llx:%llx:%llx:%llx:%llx:%llx",
- words + 0, words + 1, words + 2, words + 3,
- words + 4, words + 5, words + 6, words + 7,
- words + 8, words + 9, words + 10, words + 11,
- words + 12, words + 13, words + 14, words + 15);
- if (err != 16) {
- (void) fprintf(stderr, "invalid input format\n");
- exit(1);
- }
- ASSERT3U(BPE_GET_LSIZE(&bp), <=, SPA_MAXBLOCKSIZE);
- buf = malloc(SPA_MAXBLOCKSIZE);
- if (buf == NULL) {
- (void) fprintf(stderr, "out of memory\n");
- exit(1);
- }
- err = decode_embedded_bp(&bp, buf, BPE_GET_LSIZE(&bp));
- if (err != 0) {
- (void) fprintf(stderr, "decode failed: %u\n", err);
- free(buf);
- exit(1);
- }
- zdb_dump_block_raw(buf, BPE_GET_LSIZE(&bp), 0);
- free(buf);
-}
-
-int
-main(int argc, char **argv)
-{
- int c;
- struct rlimit rl = { 1024, 1024 };
- spa_t *spa = NULL;
- objset_t *os = NULL;
- int dump_all = 1;
- int verbose = 0;
- int error = 0;
- char **searchdirs = NULL;
- int nsearch = 0;
- char *target, *target_pool;
- nvlist_t *policy = NULL;
- uint64_t max_txg = UINT64_MAX;
- int flags = ZFS_IMPORT_MISSING_LOG;
- int rewind = ZPOOL_NEVER_REWIND;
- char *spa_config_path_env;
- boolean_t target_is_spa = B_TRUE;
- nvlist_t *cfg = NULL;
-
- (void) setrlimit(RLIMIT_NOFILE, &rl);
- (void) enable_extended_FILE_stdio(-1, -1);
-
- dprintf_setup(&argc, argv);
-
- /*
- * If there is an environment variable SPA_CONFIG_PATH it overrides
- * default spa_config_path setting. If -U flag is specified it will
- * override this environment variable settings once again.
- */
- spa_config_path_env = getenv("SPA_CONFIG_PATH");
- if (spa_config_path_env != NULL)
- spa_config_path = spa_config_path_env;
-
- while ((c = getopt(argc, argv,
- "AbcCdDeEFGhiI:klLmMo:Op:PqRsSt:uU:vVx:X")) != -1) {
- switch (c) {
- case 'b':
- case 'c':
- case 'C':
- case 'd':
- case 'D':
- case 'E':
- case 'G':
- case 'h':
- case 'i':
- case 'l':
- case 'm':
- case 'M':
- case 'O':
- case 'R':
- case 's':
- case 'S':
- case 'u':
- dump_opt[c]++;
- dump_all = 0;
- break;
- case 'A':
- case 'e':
- case 'F':
- case 'k':
- case 'L':
- case 'P':
- case 'q':
- case 'X':
- dump_opt[c]++;
- break;
- /* NB: Sort single match options below. */
- case 'I':
- max_inflight = strtoull(optarg, NULL, 0);
- if (max_inflight == 0) {
- (void) fprintf(stderr, "maximum number "
- "of inflight I/Os must be greater "
- "than 0\n");
- usage();
- }
- break;
- case 'o':
- error = set_global_var(optarg);
- if (error != 0)
- usage();
- break;
- case 'p':
- if (searchdirs == NULL) {
- searchdirs = umem_alloc(sizeof (char *),
- UMEM_NOFAIL);
- } else {
- char **tmp = umem_alloc((nsearch + 1) *
- sizeof (char *), UMEM_NOFAIL);
- bcopy(searchdirs, tmp, nsearch *
- sizeof (char *));
- umem_free(searchdirs,
- nsearch * sizeof (char *));
- searchdirs = tmp;
- }
- searchdirs[nsearch++] = optarg;
- break;
- case 't':
- max_txg = strtoull(optarg, NULL, 0);
- if (max_txg < TXG_INITIAL) {
- (void) fprintf(stderr, "incorrect txg "
- "specified: %s\n", optarg);
- usage();
- }
- break;
- case 'U':
- spa_config_path = optarg;
- if (spa_config_path[0] != '/') {
- (void) fprintf(stderr,
- "cachefile must be an absolute path "
- "(i.e. start with a slash)\n");
- usage();
- }
- break;
- case 'v':
- verbose++;
- break;
- case 'V':
- flags = ZFS_IMPORT_VERBATIM;
- break;
- case 'x':
- vn_dumpdir = optarg;
- break;
- default:
- usage();
- break;
- }
- }
-
- if (!dump_opt['e'] && searchdirs != NULL) {
- (void) fprintf(stderr, "-p option requires use of -e\n");
- usage();
- }
-
- /*
- * ZDB does not typically re-read blocks; therefore limit the ARC
- * to 256 MB, which can be used entirely for metadata.
- */
- zfs_arc_max = zfs_arc_meta_limit = 256 * 1024 * 1024;
-
- /*
- * "zdb -c" uses checksum-verifying scrub i/os which are async reads.
- * "zdb -b" uses traversal prefetch which uses async reads.
- * For good performance, let several of them be active at once.
- */
- zfs_vdev_async_read_max_active = 10;
-
- /*
- * Disable reference tracking for better performance.
- */
- reference_tracking_enable = B_FALSE;
-
- /*
- * Do not fail spa_load when spa_load_verify fails. This is needed
- * to load non-idle pools.
- */
- spa_load_verify_dryrun = B_TRUE;
-
- kernel_init(FREAD);
- g_zfs = libzfs_init();
- if (g_zfs == NULL)
- fatal("Fail to initialize zfs");
-
- if (dump_all)
- verbose = MAX(verbose, 1);
-
- for (c = 0; c < 256; c++) {
- if (dump_all && strchr("AeEFklLOPRSX", c) == NULL)
- dump_opt[c] = 1;
- if (dump_opt[c])
- dump_opt[c] += verbose;
- }
-
- aok = (dump_opt['A'] == 1) || (dump_opt['A'] > 2);
- zfs_recover = (dump_opt['A'] > 1);
-
- argc -= optind;
- argv += optind;
-
- if (argc < 2 && dump_opt['R'])
- usage();
-
- if (dump_opt['E']) {
- if (argc != 1)
- usage();
- zdb_embedded_block(argv[0]);
- return (0);
- }
-
- if (argc < 1) {
- if (!dump_opt['e'] && dump_opt['C']) {
- dump_cachefile(spa_config_path);
- return (0);
- }
- usage();
- }
-
- if (dump_opt['l'])
- return (dump_label(argv[0]));
-
- if (dump_opt['O']) {
- if (argc != 2)
- usage();
- dump_opt['v'] = verbose + 3;
- return (dump_path(argv[0], argv[1]));
- }
-
- if (dump_opt['X'] || dump_opt['F'])
- rewind = ZPOOL_DO_REWIND |
- (dump_opt['X'] ? ZPOOL_EXTREME_REWIND : 0);
-
- if (nvlist_alloc(&policy, NV_UNIQUE_NAME_TYPE, 0) != 0 ||
- nvlist_add_uint64(policy, ZPOOL_LOAD_REQUEST_TXG, max_txg) != 0 ||
- nvlist_add_uint32(policy, ZPOOL_LOAD_REWIND_POLICY, rewind) != 0)
- fatal("internal error: %s", strerror(ENOMEM));
-
- error = 0;
- target = argv[0];
-
- if (strpbrk(target, "/@") != NULL) {
- size_t targetlen;
-
- target_pool = strdup(target);
- *strpbrk(target_pool, "/@") = '\0';
-
- target_is_spa = B_FALSE;
- targetlen = strlen(target);
- if (targetlen && target[targetlen - 1] == '/')
- target[targetlen - 1] = '\0';
- } else {
- target_pool = target;
- }
-
- if (dump_opt['e']) {
- importargs_t args = { 0 };
-
- args.paths = nsearch;
- args.path = searchdirs;
- args.can_be_active = B_TRUE;
-
- error = zpool_tryimport(g_zfs, target_pool, &cfg, &args);
-
- if (error == 0) {
-
- if (nvlist_add_nvlist(cfg,
- ZPOOL_LOAD_POLICY, policy) != 0) {
- fatal("can't open '%s': %s",
- target, strerror(ENOMEM));
- }
-
- if (dump_opt['C'] > 1) {
- (void) printf("\nConfiguration for import:\n");
- dump_nvlist(cfg, 8);
- }
-
- /*
- * Disable the activity check to allow examination of
- * active pools.
- */
- error = spa_import(target_pool, cfg, NULL,
- flags | ZFS_IMPORT_SKIP_MMP);
- }
- }
-
- char *checkpoint_pool = NULL;
- char *checkpoint_target = NULL;
- if (dump_opt['k']) {
- checkpoint_pool = import_checkpointed_state(target, cfg,
- &checkpoint_target);
-
- if (checkpoint_target != NULL)
- target = checkpoint_target;
-
- }
-
- if (error == 0) {
- if (dump_opt['k'] && (target_is_spa || dump_opt['R'])) {
- ASSERT(checkpoint_pool != NULL);
- ASSERT(checkpoint_target == NULL);
-
- error = spa_open(checkpoint_pool, &spa, FTAG);
- if (error != 0) {
- fatal("Tried to open pool \"%s\" but "
- "spa_open() failed with error %d\n",
- checkpoint_pool, error);
- }
-
- } else if (target_is_spa || dump_opt['R']) {
- zdb_set_skip_mmp(target);
- error = spa_open_rewind(target, &spa, FTAG, policy,
- NULL);
- if (error) {
- /*
- * If we're missing the log device then
- * try opening the pool after clearing the
- * log state.
- */
- mutex_enter(&spa_namespace_lock);
- if ((spa = spa_lookup(target)) != NULL &&
- spa->spa_log_state == SPA_LOG_MISSING) {
- spa->spa_log_state = SPA_LOG_CLEAR;
- error = 0;
- }
- mutex_exit(&spa_namespace_lock);
-
- if (!error) {
- error = spa_open_rewind(target, &spa,
- FTAG, policy, NULL);
- }
- }
- } else {
- zdb_set_skip_mmp(target);
- error = open_objset(target, DMU_OST_ANY, FTAG, &os);
- }
- }
- nvlist_free(policy);
-
- if (error)
- fatal("can't open '%s': %s", target, strerror(error));
-
- argv++;
- argc--;
- if (!dump_opt['R']) {
- if (argc > 0) {
- zopt_objects = argc;
- zopt_object = calloc(zopt_objects, sizeof (uint64_t));
- for (unsigned i = 0; i < zopt_objects; i++) {
- errno = 0;
- zopt_object[i] = strtoull(argv[i], NULL, 0);
- if (zopt_object[i] == 0 && errno != 0)
- fatal("bad number %s: %s",
- argv[i], strerror(errno));
- }
- }
- if (os != NULL) {
- dump_dir(os);
- } else if (zopt_objects > 0 && !dump_opt['m']) {
- dump_dir(spa->spa_meta_objset);
- } else {
- dump_zpool(spa);
- }
- } else {
- flagbits['b'] = ZDB_FLAG_PRINT_BLKPTR;
- flagbits['c'] = ZDB_FLAG_CHECKSUM;
- flagbits['d'] = ZDB_FLAG_DECOMPRESS;
- flagbits['e'] = ZDB_FLAG_BSWAP;
- flagbits['g'] = ZDB_FLAG_GBH;
- flagbits['i'] = ZDB_FLAG_INDIRECT;
- flagbits['p'] = ZDB_FLAG_PHYS;
- flagbits['r'] = ZDB_FLAG_RAW;
-
- for (int i = 0; i < argc; i++)
- zdb_read_block(argv[i], spa);
- }
-
- if (dump_opt['k']) {
- free(checkpoint_pool);
- if (!target_is_spa)
- free(checkpoint_target);
- }
-
- if (os != NULL)
- close_objset(os, FTAG);
- else
- spa_close(spa, FTAG);
-
- fuid_table_destroy();
-
- dump_debug_buffer();
-
- libzfs_fini(g_zfs);
- kernel_fini();
-
- return (error);
-}
diff --git a/cddl/contrib/opensolaris/cmd/zdb/zdb.h b/cddl/contrib/opensolaris/cmd/zdb/zdb.h
deleted file mode 100644
index 49579811efbb..000000000000
--- a/cddl/contrib/opensolaris/cmd/zdb/zdb.h
+++ /dev/null
@@ -1,33 +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 2017 Spectra Logic Corp Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-
-#ifndef _ZDB_H
-#define _ZDB_H
-
-void dump_intent_log(zilog_t *);
-extern uint8_t dump_opt[256];
-
-#endif /* _ZDB_H */
diff --git a/cddl/contrib/opensolaris/cmd/zdb/zdb_il.c b/cddl/contrib/opensolaris/cmd/zdb/zdb_il.c
deleted file mode 100644
index 9f3f23f82da1..000000000000
--- a/cddl/contrib/opensolaris/cmd/zdb/zdb_il.c
+++ /dev/null
@@ -1,424 +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) 2013, 2017 by Delphix. All rights reserved.
- */
-
-/*
- * Print intent log header and statistics.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <sys/zfs_context.h>
-#include <sys/spa.h>
-#include <sys/dmu.h>
-#include <sys/stat.h>
-#include <sys/resource.h>
-#include <sys/zil.h>
-#include <sys/zil_impl.h>
-#include <sys/spa_impl.h>
-#include <sys/abd.h>
-
-#include "zdb.h"
-
-extern uint8_t dump_opt[256];
-
-static char tab_prefix[4] = "\t\t\t";
-
-static void
-print_log_bp(const blkptr_t *bp, const char *prefix)
-{
- char blkbuf[BP_SPRINTF_LEN];
-
- snprintf_blkptr(blkbuf, sizeof (blkbuf), bp);
- (void) printf("%s%s\n", prefix, blkbuf);
-}
-
-/* ARGSUSED */
-static void
-zil_prt_rec_create(zilog_t *zilog, int txtype, void *arg)
-{
- lr_create_t *lr = arg;
- time_t crtime = lr->lr_crtime[0];
- char *name, *link;
- lr_attr_t *lrattr;
-
- name = (char *)(lr + 1);
-
- if (lr->lr_common.lrc_txtype == TX_CREATE_ATTR ||
- lr->lr_common.lrc_txtype == TX_MKDIR_ATTR) {
- lrattr = (lr_attr_t *)(lr + 1);
- name += ZIL_XVAT_SIZE(lrattr->lr_attr_masksize);
- }
-
- if (txtype == TX_SYMLINK) {
- link = name + strlen(name) + 1;
- (void) printf("%s%s -> %s\n", tab_prefix, name, link);
- } else if (txtype != TX_MKXATTR) {
- (void) printf("%s%s\n", tab_prefix, name);
- }
-
- (void) printf("%s%s", tab_prefix, ctime(&crtime));
- (void) printf("%sdoid %" PRIu64 ", foid %" PRIu64 ", slots %" PRIu64
- ", mode %" PRIo64 "\n",
- tab_prefix, lr->lr_doid,
- (uint64_t)LR_FOID_GET_OBJ(lr->lr_foid),
- (uint64_t)LR_FOID_GET_SLOTS(lr->lr_foid),
- lr->lr_mode);
- (void) printf("%suid %" PRIu64 ", gid %" PRIu64 ", gen %" PRIu64
- ", rdev %#" PRIx64 "\n",
- tab_prefix, lr->lr_uid, lr->lr_gid, lr->lr_gen, lr->lr_rdev);
-}
-
-/* ARGSUSED */
-static void
-zil_prt_rec_remove(zilog_t *zilog, int txtype, void *arg)
-{
- lr_remove_t *lr = arg;
-
- (void) printf("%sdoid %llu, name %s\n", tab_prefix,
- (u_longlong_t)lr->lr_doid, (char *)(lr + 1));
-}
-
-/* ARGSUSED */
-static void
-zil_prt_rec_link(zilog_t *zilog, int txtype, void *arg)
-{
- lr_link_t *lr = arg;
-
- (void) printf("%sdoid %llu, link_obj %llu, name %s\n", tab_prefix,
- (u_longlong_t)lr->lr_doid, (u_longlong_t)lr->lr_link_obj,
- (char *)(lr + 1));
-}
-
-/* ARGSUSED */
-static void
-zil_prt_rec_rename(zilog_t *zilog, int txtype, void *arg)
-{
- lr_rename_t *lr = arg;
- char *snm = (char *)(lr + 1);
- char *tnm = snm + strlen(snm) + 1;
-
- (void) printf("%ssdoid %llu, tdoid %llu\n", tab_prefix,
- (u_longlong_t)lr->lr_sdoid, (u_longlong_t)lr->lr_tdoid);
- (void) printf("%ssrc %s tgt %s\n", tab_prefix, snm, tnm);
-}
-
-/* ARGSUSED */
-static int
-zil_prt_rec_write_cb(void *data, size_t len, void *unused)
-{
- char *cdata = data;
- for (size_t i = 0; i < len; i++) {
- if (isprint(*cdata))
- (void) printf("%c ", *cdata);
- else
- (void) printf("%2X", *cdata);
- cdata++;
- }
- return (0);
-}
-
-/* ARGSUSED */
-static void
-zil_prt_rec_write(zilog_t *zilog, int txtype, void *arg)
-{
- lr_write_t *lr = arg;
- abd_t *data;
- blkptr_t *bp = &lr->lr_blkptr;
- zbookmark_phys_t zb;
- int verbose = MAX(dump_opt['d'], dump_opt['i']);
- int error;
-
- (void) printf("%sfoid %llu, offset %llx, length %llx\n", tab_prefix,
- (u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_offset,
- (u_longlong_t)lr->lr_length);
-
- if (txtype == TX_WRITE2 || verbose < 5)
- return;
-
- if (lr->lr_common.lrc_reclen == sizeof (lr_write_t)) {
- (void) printf("%shas blkptr, %s\n", tab_prefix,
- !BP_IS_HOLE(bp) &&
- bp->blk_birth >= spa_min_claim_txg(zilog->zl_spa) ?
- "will claim" : "won't claim");
- print_log_bp(bp, tab_prefix);
-
- if (BP_IS_HOLE(bp)) {
- (void) printf("\t\t\tLSIZE 0x%llx\n",
- (u_longlong_t)BP_GET_LSIZE(bp));
- (void) printf("%s<hole>\n", tab_prefix);
- return;
- }
- if (bp->blk_birth < zilog->zl_header->zh_claim_txg) {
- (void) printf("%s<block already committed>\n",
- tab_prefix);
- return;
- }
-
- SET_BOOKMARK(&zb, dmu_objset_id(zilog->zl_os),
- lr->lr_foid, ZB_ZIL_LEVEL,
- lr->lr_offset / BP_GET_LSIZE(bp));
-
- data = abd_alloc(BP_GET_LSIZE(bp), B_FALSE);
- error = zio_wait(zio_read(NULL, zilog->zl_spa,
- bp, data, BP_GET_LSIZE(bp), NULL, NULL,
- ZIO_PRIORITY_SYNC_READ, ZIO_FLAG_CANFAIL, &zb));
- if (error)
- goto out;
- } else {
- /* data is stored after the end of the lr_write record */
- data = abd_alloc(lr->lr_length, B_FALSE);
- abd_copy_from_buf(data, lr + 1, lr->lr_length);
- }
-
- (void) printf("%s", tab_prefix);
- (void) abd_iterate_func(data,
- 0, MIN(lr->lr_length, (verbose < 6 ? 20 : SPA_MAXBLOCKSIZE)),
- zil_prt_rec_write_cb, NULL);
- (void) printf("\n");
-
-out:
- abd_free(data);
-}
-
-/* ARGSUSED */
-static void
-zil_prt_rec_truncate(zilog_t *zilog, int txtype, void *arg)
-{
- lr_truncate_t *lr = arg;
-
- (void) printf("%sfoid %llu, offset 0x%llx, length 0x%llx\n", tab_prefix,
- (u_longlong_t)lr->lr_foid, (longlong_t)lr->lr_offset,
- (u_longlong_t)lr->lr_length);
-}
-
-/* ARGSUSED */
-static void
-zil_prt_rec_setattr(zilog_t *zilog, int txtype, void *arg)
-{
- lr_setattr_t *lr = arg;
- time_t atime = (time_t)lr->lr_atime[0];
- time_t mtime = (time_t)lr->lr_mtime[0];
-
- (void) printf("%sfoid %llu, mask 0x%llx\n", tab_prefix,
- (u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_mask);
-
- if (lr->lr_mask & AT_MODE) {
- (void) printf("%sAT_MODE %llo\n", tab_prefix,
- (longlong_t)lr->lr_mode);
- }
-
- if (lr->lr_mask & AT_UID) {
- (void) printf("%sAT_UID %llu\n", tab_prefix,
- (u_longlong_t)lr->lr_uid);
- }
-
- if (lr->lr_mask & AT_GID) {
- (void) printf("%sAT_GID %llu\n", tab_prefix,
- (u_longlong_t)lr->lr_gid);
- }
-
- if (lr->lr_mask & AT_SIZE) {
- (void) printf("%sAT_SIZE %llu\n", tab_prefix,
- (u_longlong_t)lr->lr_size);
- }
-
- if (lr->lr_mask & AT_ATIME) {
- (void) printf("%sAT_ATIME %llu.%09llu %s", tab_prefix,
- (u_longlong_t)lr->lr_atime[0],
- (u_longlong_t)lr->lr_atime[1],
- ctime(&atime));
- }
-
- if (lr->lr_mask & AT_MTIME) {
- (void) printf("%sAT_MTIME %llu.%09llu %s", tab_prefix,
- (u_longlong_t)lr->lr_mtime[0],
- (u_longlong_t)lr->lr_mtime[1],
- ctime(&mtime));
- }
-}
-
-/* ARGSUSED */
-static void
-zil_prt_rec_acl(zilog_t *zilog, int txtype, void *arg)
-{
- lr_acl_t *lr = arg;
-
- (void) printf("%sfoid %llu, aclcnt %llu\n", tab_prefix,
- (u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_aclcnt);
-}
-
-typedef void (*zil_prt_rec_func_t)(zilog_t *, int, void *);
-typedef struct zil_rec_info {
- zil_prt_rec_func_t zri_print;
- const char *zri_name;
- uint64_t zri_count;
-} zil_rec_info_t;
-
-static zil_rec_info_t zil_rec_info[TX_MAX_TYPE] = {
- {.zri_print = NULL, .zri_name = "Total "},
- {.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE "},
- {.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR "},
- {.zri_print = zil_prt_rec_create, .zri_name = "TX_MKXATTR "},
- {.zri_print = zil_prt_rec_create, .zri_name = "TX_SYMLINK "},
- {.zri_print = zil_prt_rec_remove, .zri_name = "TX_REMOVE "},
- {.zri_print = zil_prt_rec_remove, .zri_name = "TX_RMDIR "},
- {.zri_print = zil_prt_rec_link, .zri_name = "TX_LINK "},
- {.zri_print = zil_prt_rec_rename, .zri_name = "TX_RENAME "},
- {.zri_print = zil_prt_rec_write, .zri_name = "TX_WRITE "},
- {.zri_print = zil_prt_rec_truncate, .zri_name = "TX_TRUNCATE "},
- {.zri_print = zil_prt_rec_setattr, .zri_name = "TX_SETATTR "},
- {.zri_print = zil_prt_rec_acl, .zri_name = "TX_ACL_V0 "},
- {.zri_print = zil_prt_rec_acl, .zri_name = "TX_ACL_ACL "},
- {.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE_ACL "},
- {.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE_ATTR "},
- {.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE_ACL_ATTR "},
- {.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR_ACL "},
- {.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR_ATTR "},
- {.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR_ACL_ATTR "},
- {.zri_print = zil_prt_rec_write, .zri_name = "TX_WRITE2 "},
-};
-
-/* ARGSUSED */
-static int
-print_log_record(zilog_t *zilog, lr_t *lr, void *arg, uint64_t claim_txg)
-{
- int txtype;
- int verbose = MAX(dump_opt['d'], dump_opt['i']);
-
- /* reduce size of txtype to strip off TX_CI bit */
- txtype = lr->lrc_txtype;
-
- ASSERT(txtype != 0 && (uint_t)txtype < TX_MAX_TYPE);
- ASSERT(lr->lrc_txg);
-
- (void) printf("\t\t%s%s len %6llu, txg %llu, seq %llu\n",
- (lr->lrc_txtype & TX_CI) ? "CI-" : "",
- zil_rec_info[txtype].zri_name,
- (u_longlong_t)lr->lrc_reclen,
- (u_longlong_t)lr->lrc_txg,
- (u_longlong_t)lr->lrc_seq);
-
- if (txtype && verbose >= 3)
- zil_rec_info[txtype].zri_print(zilog, txtype, lr);
-
- zil_rec_info[txtype].zri_count++;
- zil_rec_info[0].zri_count++;
-
- return (0);
-}
-
-/* ARGSUSED */
-static int
-print_log_block(zilog_t *zilog, blkptr_t *bp, void *arg, uint64_t claim_txg)
-{
- char blkbuf[BP_SPRINTF_LEN + 10];
- int verbose = MAX(dump_opt['d'], dump_opt['i']);
- const char *claim;
-
- if (verbose <= 3)
- return (0);
-
- if (verbose >= 5) {
- (void) strcpy(blkbuf, ", ");
- snprintf_blkptr(blkbuf + strlen(blkbuf),
- sizeof (blkbuf) - strlen(blkbuf), bp);
- } else {
- blkbuf[0] = '\0';
- }
-
- if (claim_txg != 0)
- claim = "already claimed";
- else if (bp->blk_birth >= spa_min_claim_txg(zilog->zl_spa))
- claim = "will claim";
- else
- claim = "won't claim";
-
- (void) printf("\tBlock seqno %llu, %s%s\n",
- (u_longlong_t)bp->blk_cksum.zc_word[ZIL_ZC_SEQ], claim, blkbuf);
-
- return (0);
-}
-
-static void
-print_log_stats(int verbose)
-{
- unsigned i, w, p10;
-
- if (verbose > 3)
- (void) printf("\n");
-
- if (zil_rec_info[0].zri_count == 0)
- return;
-
- for (w = 1, p10 = 10; zil_rec_info[0].zri_count >= p10; p10 *= 10)
- w++;
-
- for (i = 0; i < TX_MAX_TYPE; i++)
- if (zil_rec_info[i].zri_count || verbose >= 3)
- (void) printf("\t\t%s %*llu\n",
- zil_rec_info[i].zri_name, w,
- (u_longlong_t)zil_rec_info[i].zri_count);
- (void) printf("\n");
-}
-
-/* ARGSUSED */
-void
-dump_intent_log(zilog_t *zilog)
-{
- const zil_header_t *zh = zilog->zl_header;
- int verbose = MAX(dump_opt['d'], dump_opt['i']);
- int i;
-
- if (BP_IS_HOLE(&zh->zh_log) || verbose < 1)
- return;
-
- (void) printf("\n ZIL header: claim_txg %llu, "
- "claim_blk_seq %llu, claim_lr_seq %llu",
- (u_longlong_t)zh->zh_claim_txg,
- (u_longlong_t)zh->zh_claim_blk_seq,
- (u_longlong_t)zh->zh_claim_lr_seq);
- (void) printf(" replay_seq %llu, flags 0x%llx\n",
- (u_longlong_t)zh->zh_replay_seq, (u_longlong_t)zh->zh_flags);
-
- for (i = 0; i < TX_MAX_TYPE; i++)
- zil_rec_info[i].zri_count = 0;
-
- /* see comment in zil_claim() or zil_check_log_chain() */
- if (zilog->zl_spa->spa_uberblock.ub_checkpoint_txg != 0 &&
- zh->zh_claim_txg == 0)
- return;
-
- if (verbose >= 2) {
- (void) printf("\n");
- (void) zil_parse(zilog, print_log_block, print_log_record, NULL,
- zh->zh_claim_txg);
- print_log_stats(verbose);
- }
-}
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 */
diff --git a/cddl/contrib/opensolaris/cmd/zhack/zhack.c b/cddl/contrib/opensolaris/cmd/zhack/zhack.c
deleted file mode 100644
index 20a0c60e6a18..000000000000
--- a/cddl/contrib/opensolaris/cmd/zhack/zhack.c
+++ /dev/null
@@ -1,535 +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) 2011, 2015 by Delphix. All rights reserved.
- * Copyright (c) 2013 Steven Hartland. All rights reserved.
- */
-
-/*
- * zhack is a debugging tool that can write changes to ZFS pool using libzpool
- * for testing purposes. Altering pools with zhack is unsupported and may
- * result in corrupted pools.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <sys/zfs_context.h>
-#include <sys/spa.h>
-#include <sys/spa_impl.h>
-#include <sys/dmu.h>
-#include <sys/zap.h>
-#include <sys/zfs_znode.h>
-#include <sys/dsl_synctask.h>
-#include <sys/vdev.h>
-#include <sys/fs/zfs.h>
-#include <sys/dmu_objset.h>
-#include <sys/dsl_pool.h>
-#include <sys/zio_checksum.h>
-#include <sys/zio_compress.h>
-#include <sys/zfeature.h>
-#include <sys/dmu_tx.h>
-#undef verify
-#include <libzfs.h>
-
-extern boolean_t zfeature_checks_disable;
-
-const char cmdname[] = "zhack";
-libzfs_handle_t *g_zfs;
-static importargs_t g_importargs;
-static char *g_pool;
-static boolean_t g_readonly;
-
-static void
-usage(void)
-{
- (void) fprintf(stderr,
- "Usage: %s [-c cachefile] [-d dir] <subcommand> <args> ...\n"
- "where <subcommand> <args> is one of the following:\n"
- "\n", cmdname);
-
- (void) fprintf(stderr,
- " feature stat <pool>\n"
- " print information about enabled features\n"
- " feature enable [-d desc] <pool> <feature>\n"
- " add a new enabled feature to the pool\n"
- " -d <desc> sets the feature's description\n"
- " feature ref [-md] <pool> <feature>\n"
- " change the refcount on the given feature\n"
- " -d decrease instead of increase the refcount\n"
- " -m add the feature to the label if increasing refcount\n"
- "\n"
- " <feature> : should be a feature guid\n");
- exit(1);
-}
-
-
-static void
-fatal(spa_t *spa, void *tag, const char *fmt, ...)
-{
- va_list ap;
-
- if (spa != NULL) {
- spa_close(spa, tag);
- (void) spa_export(g_pool, NULL, B_TRUE, B_FALSE);
- }
-
- va_start(ap, fmt);
- (void) fprintf(stderr, "%s: ", cmdname);
- (void) vfprintf(stderr, fmt, ap);
- va_end(ap);
- (void) fprintf(stderr, "\n");
-
- exit(1);
-}
-
-/* ARGSUSED */
-static int
-space_delta_cb(dmu_object_type_t bonustype, void *data,
- uint64_t *userp, uint64_t *groupp)
-{
- /*
- * Is it a valid type of object to track?
- */
- if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
- return (ENOENT);
- (void) fprintf(stderr, "modifying object that needs user accounting");
- abort();
- /* NOTREACHED */
-}
-
-/*
- * Target is the dataset whose pool we want to open.
- */
-static void
-zhack_import(char *target, boolean_t readonly)
-{
- nvlist_t *config;
- nvlist_t *props;
- int error;
-
- kernel_init(readonly ? FREAD : (FREAD | FWRITE));
- g_zfs = libzfs_init();
- ASSERT(g_zfs != NULL);
-
- dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
-
- g_readonly = readonly;
- g_importargs.unique = B_TRUE;
- g_importargs.can_be_active = readonly;
- g_pool = strdup(target);
-
- error = zpool_tryimport(g_zfs, target, &config, &g_importargs);
- if (error)
- fatal(NULL, FTAG, "cannot import '%s': %s", target,
- libzfs_error_description(g_zfs));
-
- props = NULL;
- if (readonly) {
- VERIFY(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_uint64(props,
- zpool_prop_to_name(ZPOOL_PROP_READONLY), 1) == 0);
- }
-
- zfeature_checks_disable = B_TRUE;
- error = spa_import(target, config, props,
- (readonly ? ZFS_IMPORT_SKIP_MMP : ZFS_IMPORT_NORMAL));
- zfeature_checks_disable = B_FALSE;
- if (error == EEXIST)
- error = 0;
-
- if (error)
- fatal(NULL, FTAG, "can't import '%s': %s", target,
- strerror(error));
-}
-
-static void
-zhack_spa_open(char *target, boolean_t readonly, void *tag, spa_t **spa)
-{
- int err;
-
- zhack_import(target, readonly);
-
- zfeature_checks_disable = B_TRUE;
- err = spa_open(target, spa, tag);
- zfeature_checks_disable = B_FALSE;
-
- if (err != 0)
- fatal(*spa, FTAG, "cannot open '%s': %s", target,