summaryrefslogtreecommitdiff
path: root/cddl
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
parent22df1ffd812f0395cdb7c0b1edae1f67b991562a (diff)
downloadsrc-test-9e5787d2284e187abb5b654d924394a65772e004.tar.gz
src-test-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')
-rw-r--r--cddl/compat/opensolaris/include/fcntl.h2
-rw-r--r--cddl/compat/opensolaris/include/mnttab.h35
-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
-rw-r--r--cddl/lib/Makefile17
-rw-r--r--cddl/lib/drti/Makefile9
-rw-r--r--cddl/lib/libavl/Makefile11
-rw-r--r--cddl/lib/libctf/Makefile10
-rw-r--r--cddl/lib/libdtrace/Makefile10
-rw-r--r--cddl/lib/libicp/Makefile101
-rw-r--r--cddl/lib/libicp_rescue/Makefile99
-rw-r--r--cddl/lib/libnvpair/Makefile44
-rw-r--r--cddl/lib/libspl/Makefile56
-rw-r--r--cddl/lib/libtpool/Makefile27
-rw-r--r--cddl/lib/libuutil/Makefile22
-rw-r--r--cddl/lib/libzfs/Makefile146
-rw-r--r--cddl/lib/libzfs_core/Makefile39
-rw-r--r--cddl/lib/libzpool/Makefile264
-rw-r--r--cddl/lib/libzpool/Makefile.depend1
-rw-r--r--cddl/lib/libzutil/Makefile42
-rw-r--r--cddl/sbin/zfs/Makefile86
-rw-r--r--cddl/sbin/zpool/Makefile90
-rw-r--r--cddl/usr.bin/Makefile5
-rw-r--r--cddl/usr.bin/ctfconvert/Makefile9
-rw-r--r--cddl/usr.bin/ctfconvert/Makefile.depend1
-rw-r--r--cddl/usr.bin/ctfdump/Makefile8
-rw-r--r--cddl/usr.bin/ctfmerge/Makefile10
-rw-r--r--cddl/usr.bin/zinject/Makefile34
-rw-r--r--cddl/usr.bin/zlook/Makefile12
-rw-r--r--cddl/usr.bin/zstream/Makefile32
-rw-r--r--cddl/usr.bin/zstreamdump/Makefile22
-rw-r--r--cddl/usr.bin/ztest/Makefile39
-rw-r--r--cddl/usr.sbin/dtrace/Makefile10
-rw-r--r--cddl/usr.sbin/lockstat/Makefile9
-rw-r--r--cddl/usr.sbin/plockstat/Makefile8
-rw-r--r--cddl/usr.sbin/zdb/Makefile34
-rw-r--r--cddl/usr.sbin/zfsd/Makefile.common29
-rw-r--r--cddl/usr.sbin/zfsd/callout.cc1
-rw-r--r--cddl/usr.sbin/zfsd/case_file.cc15
-rw-r--r--cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc1
-rw-r--r--cddl/usr.sbin/zfsd/vdev.cc1
-rw-r--r--cddl/usr.sbin/zfsd/vdev_iterator.cc1
-rw-r--r--cddl/usr.sbin/zfsd/zfsd.cc2
-rw-r--r--cddl/usr.sbin/zfsd/zfsd_event.cc7
-rw-r--r--cddl/usr.sbin/zfsd/zfsd_exception.cc1
-rw-r--r--cddl/usr.sbin/zfsd/zpool_list.cc1
-rw-r--r--cddl/usr.sbin/zhack/Makefile28
118 files changed, 1079 insertions, 75626 deletions
diff --git a/cddl/compat/opensolaris/include/fcntl.h b/cddl/compat/opensolaris/include/fcntl.h
index 548918aaab3a9..b962bb4855eed 100644
--- a/cddl/compat/opensolaris/include/fcntl.h
+++ b/cddl/compat/opensolaris/include/fcntl.h
@@ -32,7 +32,9 @@
#include_next <fcntl.h>
+#ifndef open64
#define open64(...) open(__VA_ARGS__)
+#endif
#define openat64(...) openat(__VA_ARGS__)
#endif
diff --git a/cddl/compat/opensolaris/include/mnttab.h b/cddl/compat/opensolaris/include/mnttab.h
deleted file mode 100644
index 227196a4017f9..0000000000000
--- a/cddl/compat/opensolaris/include/mnttab.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* $FreeBSD$ */
-
-#ifndef _OPENSOLARIS_MNTTAB_H_
-#define _OPENSOLARIS_MNTTAB_H_
-
-#include <sys/param.h>
-#include <sys/mount.h>
-
-#include <stdio.h>
-#include <paths.h>
-
-#define MNTTAB _PATH_DEVZERO
-#define MNT_LINE_MAX 1024
-
-#define MS_OVERLAY 0x0
-#define MS_NOMNTTAB 0x0
-#define MS_RDONLY 0x1
-
-#define umount2(p, f) unmount(p, f)
-
-struct mnttab {
- char *mnt_special;
- char *mnt_mountp;
- char *mnt_fstype;
- char *mnt_mntopts;
-};
-#define extmnttab mnttab
-
-int getmntany(FILE *fd, struct mnttab *mgetp, struct mnttab *mrefp);
-int getmntent(FILE *fp, struct mnttab *mp);
-char *hasmntopt(struct mnttab *mnt, char *opt);
-
-void statfs2mnttab(struct statfs *sfs, struct mnttab *mp);
-
-#endif /* !_OPENSOLARIS_MNTTAB_H_ */
diff --git a/cddl/contrib/opensolaris/cmd/lockstat/sym.c b/cddl/contrib/opensolaris/cmd/lockstat/sym.c
index f2987a028e748..b5366c5668570 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 e60c56c7c199f..0000000000000
--- 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 d51ddc68908cf..0000000000000
--- 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 49579811efbb2..0000000000000
--- 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 9f3f23f82da1c..0000000000000
--- 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 76bb97c2d96d5..0000000000000
--- 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 33e0ca4b30404..0000000000000
--- 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 a291db0835688..0000000000000
--- 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 b89b466ce6fef..0000000000000
--- 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 d453ba0304882..0000000000000
--- 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 a56af59adb154..0000000000000
--- 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 20a0c60e6a188..0000000000000
--- 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,
- strerror(err));
- if (spa_version(*spa) < SPA_VERSION_FEATURES) {
- fatal(*spa, FTAG, "'%s' has version %d, features not enabled",
- target, (int)spa_version(*spa));
- }
-}
-
-static void
-dump_obj(objset_t *os, uint64_t obj, const char *name)
-{
- zap_cursor_t zc;
- zap_attribute_t za;
-
- (void) printf("%s_obj:\n", name);
-
- for (zap_cursor_init(&zc, os, obj);
- zap_cursor_retrieve(&zc, &za) == 0;
- zap_cursor_advance(&zc)) {
- if (za.za_integer_length == 8) {
- ASSERT(za.za_num_integers == 1);
- (void) printf("\t%s = %llu\n",
- za.za_name, (u_longlong_t)za.za_first_integer);
- } else {
- ASSERT(za.za_integer_length == 1);
- char val[1024];
- VERIFY(zap_lookup(os, obj, za.za_name,
- 1, sizeof (val), val) == 0);
- (void) printf("\t%s = %s\n", za.za_name, val);
- }
- }
- zap_cursor_fini(&zc);
-}
-
-static void
-dump_mos(spa_t *spa)
-{
- nvlist_t *nv = spa->spa_label_features;
-
- (void) printf("label config:\n");
- for (nvpair_t *pair = nvlist_next_nvpair(nv, NULL);
- pair != NULL;
- pair = nvlist_next_nvpair(nv, pair)) {
- (void) printf("\t%s\n", nvpair_name(pair));
- }
-}
-
-static void
-zhack_do_feature_stat(int argc, char **argv)
-{
- spa_t *spa;
- objset_t *os;
- char *target;
-
- argc--;
- argv++;
-
- if (argc < 1) {
- (void) fprintf(stderr, "error: missing pool name\n");
- usage();
- }
- target = argv[0];
-
- zhack_spa_open(target, B_TRUE, FTAG, &spa);
- os = spa->spa_meta_objset;
-
- dump_obj(os, spa->spa_feat_for_read_obj, "for_read");
- dump_obj(os, spa->spa_feat_for_write_obj, "for_write");
- dump_obj(os, spa->spa_feat_desc_obj, "descriptions");
- if (spa_feature_is_active(spa, SPA_FEATURE_ENABLED_TXG)) {
- dump_obj(os, spa->spa_feat_enabled_txg_obj, "enabled_txg");
- }
- dump_mos(spa);
-
- spa_close(spa, FTAG);
-}
-
-static void
-zhack_feature_enable_sync(void *arg, dmu_tx_t *tx)
-{
- spa_t *spa = dmu_tx_pool(tx)->dp_spa;
- zfeature_info_t *feature = arg;
-
- feature_enable_sync(spa, feature, tx);
-
- spa_history_log_internal(spa, "zhack enable feature", tx,
- "guid=%s flags=%x",
- feature->fi_guid, feature->fi_flags);
-}
-
-static void
-zhack_do_feature_enable(int argc, char **argv)
-{
- char c;
- char *desc, *target;
- spa_t *spa;
- objset_t *mos;
- zfeature_info_t feature;
- spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
-
- /*
- * Features are not added to the pool's label until their refcounts
- * are incremented, so fi_mos can just be left as false for now.
- */
- desc = NULL;
- feature.fi_uname = "zhack";
- feature.fi_flags = 0;
- feature.fi_depends = nodeps;
- feature.fi_feature = SPA_FEATURE_NONE;
-
- optind = 1;
- while ((c = getopt(argc, argv, "rmd:")) != -1) {
- switch (c) {
- case 'r':
- feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
- break;
- case 'd':
- desc = strdup(optarg);
- break;
- default:
- usage();
- break;
- }
- }
-
- if (desc == NULL)
- desc = strdup("zhack injected");
- feature.fi_desc = desc;
-
- argc -= optind;
- argv += optind;
-
- if (argc < 2) {
- (void) fprintf(stderr, "error: missing feature or pool name\n");
- usage();
- }
- target = argv[0];
- feature.fi_guid = argv[1];
-
- if (!zfeature_is_valid_guid(feature.fi_guid))
- fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
-
- zhack_spa_open(target, B_FALSE, FTAG, &spa);
- mos = spa->spa_meta_objset;
-
- if (zfeature_is_supported(feature.fi_guid))
- fatal(spa, FTAG, "'%s' is a real feature, will not enable");
- if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
- fatal(spa, FTAG, "feature already enabled: %s",
- feature.fi_guid);
-
- VERIFY0(dsl_sync_task(spa_name(spa), NULL,
- zhack_feature_enable_sync, &feature, 5, ZFS_SPACE_CHECK_NORMAL));
-
- spa_close(spa, FTAG);
-
- free(desc);
-}
-
-static void
-feature_incr_sync(void *arg, dmu_tx_t *tx)
-{
- spa_t *spa = dmu_tx_pool(tx)->dp_spa;
- zfeature_info_t *feature = arg;
- uint64_t refcount;
-
- VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
- feature_sync(spa, feature, refcount + 1, tx);
- spa_history_log_internal(spa, "zhack feature incr", tx,
- "name=%s", feature->fi_guid);
-}
-
-static void
-feature_decr_sync(void *arg, dmu_tx_t *tx)
-{
- spa_t *spa = dmu_tx_pool(tx)->dp_spa;
- zfeature_info_t *feature = arg;
- uint64_t refcount;
-
- VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
- feature_sync(spa, feature, refcount - 1, tx);
- spa_history_log_internal(spa, "zhack feature decr", tx,
- "name=%s", feature->fi_guid);
-}
-
-static void
-zhack_do_feature_ref(int argc, char **argv)
-{
- char c;
- char *target;
- boolean_t decr = B_FALSE;
- spa_t *spa;
- objset_t *mos;
- zfeature_info_t feature;
- spa_feature_t nodeps[] = { SPA_FEATURE_NONE };
-
- /*
- * fi_desc does not matter here because it was written to disk
- * when the feature was enabled, but we need to properly set the
- * feature for read or write based on the information we read off
- * disk later.
- */
- feature.fi_uname = "zhack";
- feature.fi_flags = 0;
- feature.fi_desc = NULL;
- feature.fi_depends = nodeps;
- feature.fi_feature = SPA_FEATURE_NONE;
-
- optind = 1;
- while ((c = getopt(argc, argv, "md")) != -1) {
- switch (c) {
- case 'm':
- feature.fi_flags |= ZFEATURE_FLAG_MOS;
- break;
- case 'd':
- decr = B_TRUE;
- break;
- default:
- usage();
- break;
- }
- }
- argc -= optind;
- argv += optind;
-
- if (argc < 2) {
- (void) fprintf(stderr, "error: missing feature or pool name\n");
- usage();
- }
- target = argv[0];
- feature.fi_guid = argv[1];
-
- if (!zfeature_is_valid_guid(feature.fi_guid))
- fatal(NULL, FTAG, "invalid feature guid: %s", feature.fi_guid);
-
- zhack_spa_open(target, B_FALSE, FTAG, &spa);
- mos = spa->spa_meta_objset;
-
- if (zfeature_is_supported(feature.fi_guid)) {
- fatal(spa, FTAG,
- "'%s' is a real feature, will not change refcount");
- }
-
- if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,
- feature.fi_guid)) {
- feature.fi_flags &= ~ZFEATURE_FLAG_READONLY_COMPAT;
- } else if (0 == zap_contains(mos, spa->spa_feat_for_write_obj,
- feature.fi_guid)) {
- feature.fi_flags |= ZFEATURE_FLAG_READONLY_COMPAT;
- } else {
- fatal(spa, FTAG, "feature is not enabled: %s", feature.fi_guid);
- }
-
- if (decr) {
- uint64_t count;
- if (feature_get_refcount_from_disk(spa, &feature,
- &count) == 0 && count != 0) {
- fatal(spa, FTAG, "feature refcount already 0: %s",
- feature.fi_guid);
- }
- }
-
- VERIFY0(dsl_sync_task(spa_name(spa), NULL,
- decr ? feature_decr_sync : feature_incr_sync, &feature,
- 5, ZFS_SPACE_CHECK_NORMAL));
-
- spa_close(spa, FTAG);
-}
-
-static int
-zhack_do_feature(int argc, char **argv)
-{
- char *subcommand;
-
- argc--;
- argv++;
- if (argc == 0) {
- (void) fprintf(stderr,
- "error: no feature operation specified\n");
- usage();
- }
-
- subcommand = argv[0];
- if (strcmp(subcommand, "stat") == 0) {
- zhack_do_feature_stat(argc, argv);
- } else if (strcmp(subcommand, "enable") == 0) {
- zhack_do_feature_enable(argc, argv);
- } else if (strcmp(subcommand, "ref") == 0) {
- zhack_do_feature_ref(argc, argv);
- } else {
- (void) fprintf(stderr, "error: unknown subcommand: %s\n",
- subcommand);
- usage();
- }
-
- return (0);
-}
-
-#define MAX_NUM_PATHS 1024
-
-int
-main(int argc, char **argv)
-{
- extern void zfs_prop_init(void);
-
- char *path[MAX_NUM_PATHS];
- const char *subcommand;
- int rv = 0;
- char c;
-
- g_importargs.path = path;
-
- dprintf_setup(&argc, argv);
- zfs_prop_init();
-
- while ((c = getopt(argc, argv, "c:d:")) != -1) {
- switch (c) {
- case 'c':
- g_importargs.cachefile = optarg;
- break;
- case 'd':
- assert(g_importargs.paths < MAX_NUM_PATHS);
- g_importargs.path[g_importargs.paths++] = optarg;
- break;
- default:
- usage();
- break;
- }
- }
-
- argc -= optind;
- argv += optind;
- optind = 1;
-
- if (argc == 0) {
- (void) fprintf(stderr, "error: no command specified\n");
- usage();
- }
-
- subcommand = argv[0];
-
- if (strcmp(subcommand, "feature") == 0) {
- rv = zhack_do_feature(argc, argv);
- } else {
- (void) fprintf(stderr, "error: unknown subcommand: %s\n",
- subcommand);
- usage();
- }
-
- if (!g_readonly && spa_export(g_pool, NULL, B_TRUE, B_FALSE) != 0) {
- fatal(NULL, FTAG, "pool export failed; "
- "changes may not be committed to disk\n");
- }
-
- libzfs_fini(g_zfs);
- kernel_fini();
-
- return (rv);
-}
diff --git a/cddl/contrib/opensolaris/cmd/zinject/translate.c b/cddl/contrib/opensolaris/cmd/zinject/translate.c
deleted file mode 100644
index 99a3d0ca4ff36..0000000000000
--- a/cddl/contrib/opensolaris/cmd/zinject/translate.c
+++ /dev/null
@@ -1,492 +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) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012, 2020 by Delphix. All rights reserved.
- */
-
-#include <libzfs.h>
-
-#include <sys/zfs_context.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <sys/file.h>
-#include <sys/mntent.h>
-#include <sys/mnttab.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-
-#include <sys/dmu.h>
-#include <sys/dmu_objset.h>
-#include <sys/dnode.h>
-#include <sys/vdev_impl.h>
-
-#include "zinject.h"
-
-extern void kernel_init(int);
-extern void kernel_fini(void);
-
-static int debug;
-
-static void
-ziprintf(const char *fmt, ...)
-{
- va_list ap;
-
- if (!debug)
- return;
-
- va_start(ap, fmt);
- (void) vprintf(fmt, ap);
- va_end(ap);
-}
-
-static void
-compress_slashes(const char *src, char *dest)
-{
- while (*src != '\0') {
- *dest = *src++;
- while (*dest == '/' && *src == '/')
- ++src;
- ++dest;
- }
- *dest = '\0';
-}
-
-/*
- * Given a full path to a file, translate into a dataset name and a relative
- * path within the dataset. 'dataset' must be at least MAXNAMELEN characters,
- * and 'relpath' must be at least MAXPATHLEN characters. We also pass a stat64
- * buffer, which we need later to get the object ID.
- */
-static int
-parse_pathname(const char *inpath, char *dataset, char *relpath,
- struct stat64 *statbuf)
-{
- struct statfs sfs;
- const char *rel;
- char fullpath[MAXPATHLEN];
-
- compress_slashes(inpath, fullpath);
-
- if (fullpath[0] != '/') {
- (void) fprintf(stderr, "invalid object '%s': must be full "
- "path\n", fullpath);
- usage();
- return (-1);
- }
-
- if (strlen(fullpath) >= MAXPATHLEN) {
- (void) fprintf(stderr, "invalid object; pathname too long\n");
- return (-1);
- }
-
- if (stat64(fullpath, statbuf) != 0) {
- (void) fprintf(stderr, "cannot open '%s': %s\n",
- fullpath, strerror(errno));
- return (-1);
- }
-
- if (statfs(fullpath, &sfs) == -1) {
- (void) fprintf(stderr, "cannot find mountpoint for '%s': %s\n",
- fullpath, strerror(errno));
- return (-1);
- }
-
- if (strcmp(sfs.f_fstypename, MNTTYPE_ZFS) != 0) {
- (void) fprintf(stderr, "invalid path '%s': not a ZFS "
- "filesystem\n", fullpath);
- return (-1);
- }
-
- if (strncmp(fullpath, sfs.f_mntonname, strlen(sfs.f_mntonname)) != 0) {
- (void) fprintf(stderr, "invalid path '%s': mountpoint "
- "doesn't match path\n", fullpath);
- return (-1);
- }
-
- (void) strcpy(dataset, sfs.f_mntfromname);
-
- rel = fullpath + strlen(sfs.f_mntonname);
- if (rel[0] == '/')
- rel++;
- (void) strcpy(relpath, rel);
-
- return (0);
-}
-
-/*
- * Convert from a (dataset, path) pair into a (objset, object) pair. Note that
- * we grab the object number from the inode number, since looking this up via
- * libzpool is a real pain.
- */
-/* ARGSUSED */
-static int
-object_from_path(const char *dataset, const char *path, struct stat64 *statbuf,
- zinject_record_t *record)
-{
- objset_t *os;
- int err;
-
- /*
- * Before doing any libzpool operations, call sync() to ensure that the
- * on-disk state is consistent with the in-core state.
- */
- sync();
-
- err = dmu_objset_own(dataset, DMU_OST_ZFS, B_TRUE, FTAG, &os);
- if (err != 0) {
- (void) fprintf(stderr, "cannot open dataset '%s': %s\n",
- dataset, strerror(err));
- return (-1);
- }
-
- record->zi_objset = dmu_objset_id(os);
- record->zi_object = statbuf->st_ino;
-
- dmu_objset_disown(os, FTAG);
-
- return (0);
-}
-
-/*
- * Calculate the real range based on the type, level, and range given.
- */
-static int
-calculate_range(const char *dataset, err_type_t type, int level, char *range,
- zinject_record_t *record)
-{
- objset_t *os = NULL;
- dnode_t *dn = NULL;
- int err;
- int ret = -1;
-
- /*
- * Determine the numeric range from the string.
- */
- if (range == NULL) {
- /*
- * If range is unspecified, set the range to [0,-1], which
- * indicates that the whole object should be treated as an
- * error.
- */
- record->zi_start = 0;
- record->zi_end = -1ULL;
- } else {
- char *end;
-
- /* XXX add support for suffixes */
- record->zi_start = strtoull(range, &end, 10);
-
-
- if (*end == '\0')
- record->zi_end = record->zi_start + 1;
- else if (*end == ',')
- record->zi_end = strtoull(end + 1, &end, 10);
-
- if (*end != '\0') {
- (void) fprintf(stderr, "invalid range '%s': must be "
- "a numeric range of the form 'start[,end]'\n",
- range);
- goto out;
- }
- }
-
- switch (type) {
- case TYPE_DATA:
- break;
-
- case TYPE_DNODE:
- /*
- * If this is a request to inject faults into the dnode, then we
- * must translate the current (objset,object) pair into an
- * offset within the metadnode for the objset. Specifying any
- * kind of range with type 'dnode' is illegal.
- */
- if (range != NULL) {
- (void) fprintf(stderr, "range cannot be specified when "
- "type is 'dnode'\n");
- goto out;
- }
-
- record->zi_start = record->zi_object * sizeof (dnode_phys_t);
- record->zi_end = record->zi_start + sizeof (dnode_phys_t);
- record->zi_object = 0;
- break;
- }
-
- /*
- * Get the dnode associated with object, so we can calculate the block
- * size.
- */
- if ((err = dmu_objset_own(dataset, DMU_OST_ANY,
- B_TRUE, FTAG, &os)) != 0) {
- (void) fprintf(stderr, "cannot open dataset '%s': %s\n",
- dataset, strerror(err));
- goto out;
- }
-
- if (record->zi_object == 0) {
- dn = DMU_META_DNODE(os);
- } else {
- err = dnode_hold(os, record->zi_object, FTAG, &dn);
- if (err != 0) {
- (void) fprintf(stderr, "failed to hold dnode "
- "for object %llu\n",
- (u_longlong_t)record->zi_object);
- goto out;
- }
- }
-
-
- ziprintf("data shift: %d\n", (int)dn->dn_datablkshift);
- ziprintf(" ind shift: %d\n", (int)dn->dn_indblkshift);
-
- /*
- * Translate range into block IDs.
- */
- if (record->zi_start != 0 || record->zi_end != -1ULL) {
- record->zi_start >>= dn->dn_datablkshift;
- record->zi_end >>= dn->dn_datablkshift;
- }
-
- /*
- * Check level, and then translate level 0 blkids into ranges
- * appropriate for level of indirection.
- */
- record->zi_level = level;
- if (level > 0) {
- ziprintf("level 0 blkid range: [%llu, %llu]\n",
- record->zi_start, record->zi_end);
-
- if (level >= dn->dn_nlevels) {
- (void) fprintf(stderr, "level %d exceeds max level "
- "of object (%d)\n", level, dn->dn_nlevels - 1);
- goto out;
- }
-
- if (record->zi_start != 0 || record->zi_end != 0) {
- int shift = dn->dn_indblkshift - SPA_BLKPTRSHIFT;
-
- for (; level > 0; level--) {
- record->zi_start >>= shift;
- record->zi_end >>= shift;
- }
- }
- }
-
- ret = 0;
-out:
- if (dn) {
- if (dn != DMU_META_DNODE(os))
- dnode_rele(dn, FTAG);
- }
- if (os)
- dmu_objset_disown(os, FTAG);
-
- return (ret);
-}
-
-int
-translate_record(err_type_t type, const char *object, const char *range,
- int level, zinject_record_t *record, char *poolname, char *dataset)
-{
- char path[MAXPATHLEN];
- char *slash;
- struct stat64 statbuf;
- int ret = -1;
-
- kernel_init(FREAD);
-
- debug = (getenv("ZINJECT_DEBUG") != NULL);
-
- ziprintf("translating: %s\n", object);
-
- if (MOS_TYPE(type)) {
- /*
- * MOS objects are treated specially.
- */
- switch (type) {
- case TYPE_MOS:
- record->zi_type = 0;
- break;
- case TYPE_MOSDIR:
- record->zi_type = DMU_OT_OBJECT_DIRECTORY;
- break;
- case TYPE_METASLAB:
- record->zi_type = DMU_OT_OBJECT_ARRAY;
- break;
- case TYPE_CONFIG:
- record->zi_type = DMU_OT_PACKED_NVLIST;
- break;
- case TYPE_BPOBJ:
- record->zi_type = DMU_OT_BPOBJ;
- break;
- case TYPE_SPACEMAP:
- record->zi_type = DMU_OT_SPACE_MAP;
- break;
- case TYPE_ERRLOG:
- record->zi_type = DMU_OT_ERROR_LOG;
- break;
- }
-
- dataset[0] = '\0';
- (void) strcpy(poolname, object);
- return (0);
- }
-
- /*
- * Convert a full path into a (dataset, file) pair.
- */
- if (parse_pathname(object, dataset, path, &statbuf) != 0)
- goto err;
-
- ziprintf(" dataset: %s\n", dataset);
- ziprintf(" path: %s\n", path);
-
- /*
- * Convert (dataset, file) into (objset, object)
- */
- if (object_from_path(dataset, path, &statbuf, record) != 0)
- goto err;
-
- ziprintf("raw objset: %llu\n", record->zi_objset);
- ziprintf("raw object: %llu\n", record->zi_object);
-
- /*
- * For the given object, calculate the real (type, level, range)
- */
- if (calculate_range(dataset, type, level, (char *)range, record) != 0)
- goto err;
-
- ziprintf(" objset: %llu\n", record->zi_objset);
- ziprintf(" object: %llu\n", record->zi_object);
- if (record->zi_start == 0 &&
- record->zi_end == -1ULL)
- ziprintf(" range: all\n");
- else
- ziprintf(" range: [%llu, %llu]\n", record->zi_start,
- record->zi_end);
-
- /*
- * Copy the pool name
- */
- (void) strcpy(poolname, dataset);
- if ((slash = strchr(poolname, '/')) != NULL)
- *slash = '\0';
-
- ret = 0;
-
-err:
- kernel_fini();
- return (ret);
-}
-
-int
-translate_raw(const char *str, zinject_record_t *record)
-{
- /*
- * A raw bookmark of the form objset:object:level:blkid, where each
- * number is a hexidecimal value.
- */
- if (sscanf(str, "%llx:%llx:%x:%llx", (u_longlong_t *)&record->zi_objset,
- (u_longlong_t *)&record->zi_object, &record->zi_level,
- (u_longlong_t *)&record->zi_start) != 4) {
- (void) fprintf(stderr, "bad raw spec '%s': must be of the form "
- "'objset:object:level:blkid'\n", str);
- return (-1);
- }
-
- record->zi_end = record->zi_start;
-
- return (0);
-}
-
-int
-translate_device(const char *pool, const char *device, err_type_t label_type,
- zinject_record_t *record)
-{
- char *end;
- zpool_handle_t *zhp;
- nvlist_t *tgt;
- boolean_t isspare, iscache;
-
- /*
- * Given a device name or GUID, create an appropriate injection record
- * with zi_guid set.
- */
- if ((zhp = zpool_open(g_zfs, pool)) == NULL)
- return (-1);
-
- record->zi_guid = strtoull(device, &end, 16);
- if (record->zi_guid == 0 || *end != '\0') {
- tgt = zpool_find_vdev(zhp, device, &isspare, &iscache, NULL);
-
- if (tgt == NULL) {
- (void) fprintf(stderr, "cannot find device '%s' in "
- "pool '%s'\n", device, pool);
- return (-1);
- }
-
- verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID,
- &record->zi_guid) == 0);
- }
-
- /*
- * Device faults can take on three different forms:
- * 1). delayed or hanging I/O
- * 2). zfs label faults
- * 3). generic disk faults
- */
- if (record->zi_timer != 0) {
- record->zi_cmd = ZINJECT_DELAY_IO;
- } else if (label_type != TYPE_INVAL) {
- record->zi_cmd = ZINJECT_LABEL_FAULT;
- } else {
- record->zi_cmd = ZINJECT_DEVICE_FAULT;
- }
-
- switch (label_type) {
- case TYPE_LABEL_UBERBLOCK:
- record->zi_start = offsetof(vdev_label_t, vl_uberblock[0]);
- record->zi_end = record->zi_start + VDEV_UBERBLOCK_RING - 1;
- break;
- case TYPE_LABEL_NVLIST:
- record->zi_start = offsetof(vdev_label_t, vl_vdev_phys);
- record->zi_end = record->zi_start + VDEV_PHYS_SIZE - 1;
- break;
- case TYPE_LABEL_PAD1:
- record->zi_start = offsetof(vdev_label_t, vl_pad1);
- record->zi_end = record->zi_start + VDEV_PAD_SIZE - 1;
- break;
- case TYPE_LABEL_PAD2:
- record->zi_start = offsetof(vdev_label_t, vl_be);
- record->zi_end = record->zi_start + VDEV_PAD_SIZE - 1;
- break;
- }
- return (0);
-}
diff --git a/cddl/contrib/opensolaris/cmd/zinject/zinject.c b/cddl/contrib/opensolaris/cmd/zinject/zinject.c
deleted file mode 100644
index bf42bc4838301..0000000000000
--- a/cddl/contrib/opensolaris/cmd/zinject/zinject.c
+++ /dev/null
@@ -1,1093 +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, 2015 by Delphix. All rights reserved.
- */
-
-/*
- * ZFS Fault Injector
- *
- * This userland component takes a set of options and uses libzpool to translate
- * from a user-visible object type and name to an internal representation.
- * There are two basic types of faults: device faults and data faults.
- *
- *
- * DEVICE FAULTS
- *
- * Errors can be injected into a particular vdev using the '-d' option. This
- * option takes a path or vdev GUID to uniquely identify the device within a
- * pool. There are two types of errors that can be injected, EIO and ENXIO,
- * that can be controlled through the '-e' option. The default is ENXIO. For
- * EIO failures, any attempt to read data from the device will return EIO, but
- * subsequent attempt to reopen the device will succeed. For ENXIO failures,
- * any attempt to read from the device will return EIO, but any attempt to
- * reopen the device will also return ENXIO.
- * For label faults, the -L option must be specified. This allows faults
- * to be injected into either the nvlist, uberblock, pad1, or pad2 region
- * of all the labels for the specified device.
- *
- * This form of the command looks like:
- *
- * zinject -d device [-e errno] [-L <uber | nvlist | pad1 | pad2>] pool
- *
- *
- * DATA FAULTS
- *
- * We begin with a tuple of the form:
- *
- * <type,level,range,object>
- *
- * type A string describing the type of data to target. Each type
- * implicitly describes how to interpret 'object'. Currently,
- * the following values are supported:
- *
- * data User data for a file
- * dnode Dnode for a file or directory
- *
- * The following MOS objects are special. Instead of injecting
- * errors on a particular object or blkid, we inject errors across
- * all objects of the given type.
- *
- * mos Any data in the MOS
- * mosdir object directory
- * config pool configuration
- * bpobj blkptr list
- * spacemap spacemap
- * metaslab metaslab
- * errlog persistent error log
- *
- * level Object level. Defaults to '0', not applicable to all types. If
- * a range is given, this corresponds to the indirect block
- * corresponding to the specific range.
- *
- * range A numerical range [start,end) within the object. Defaults to
- * the full size of the file.
- *
- * object A string describing the logical location of the object. For
- * files and directories (currently the only supported types),
- * this is the path of the object on disk.
- *
- * This is translated, via libzpool, into the following internal representation:
- *
- * <type,objset,object,level,range>
- *
- * These types should be self-explanatory. This tuple is then passed to the
- * kernel via a special ioctl() to initiate fault injection for the given
- * object. Note that 'type' is not strictly necessary for fault injection, but
- * is used when translating existing faults into a human-readable string.
- *
- *
- * The command itself takes one of the forms:
- *
- * zinject
- * zinject <-a | -u pool>
- * zinject -c <id|all>
- * zinject [-q] <-t type> [-f freq] [-u] [-a] [-m] [-e errno] [-l level]
- * [-r range] <object>
- * zinject [-f freq] [-a] [-m] [-u] -b objset:object:level:start:end pool
- *
- * With no arguments, the command prints all currently registered injection
- * handlers, with their numeric identifiers.
- *
- * The '-c' option will clear the given handler, or all handlers if 'all' is
- * specified.
- *
- * The '-e' option takes a string describing the errno to simulate. This must
- * be either 'io' or 'checksum'. In most cases this will result in the same
- * behavior, but RAID-Z will produce a different set of ereports for this
- * situation.
- *
- * The '-a', '-u', and '-m' flags toggle internal flush behavior. If '-a' is
- * specified, then the ARC cache is flushed appropriately. If '-u' is
- * specified, then the underlying SPA is unloaded. Either of these flags can be
- * specified independently of any other handlers. The '-m' flag automatically
- * does an unmount and remount of the underlying dataset to aid in flushing the
- * cache.
- *
- * The '-f' flag controls the frequency of errors injected, expressed as a
- * integer percentage between 1 and 100. The default is 100.
- *
- * The this form is responsible for actually injecting the handler into the
- * framework. It takes the arguments described above, translates them to the
- * internal tuple using libzpool, and then issues an ioctl() to register the
- * handler.
- *
- * The final form can target a specific bookmark, regardless of whether a
- * human-readable interface has been designed. It allows developers to specify
- * a particular block by number.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <unistd.h>
-
-#include <sys/fs/zfs.h>
-#include <sys/param.h>
-#include <sys/mount.h>
-
-#include <libzfs.h>
-#include <libzfs_compat.h>
-
-#undef verify /* both libzfs.h and zfs_context.h want to define this */
-
-#include "zinject.h"
-
-libzfs_handle_t *g_zfs;
-int zfs_fd;
-
-#ifndef ECKSUM
-#define ECKSUM EBADE
-#endif
-
-static const char *errtable[TYPE_INVAL] = {
- "data",
- "dnode",
- "mos",
- "mosdir",
- "metaslab",
- "config",
- "bpobj",
- "spacemap",
- "errlog",
- "uber",
- "nvlist",
- "pad1",
- "pad2"
-};
-
-static err_type_t
-name_to_type(const char *arg)
-{
- int i;
- for (i = 0; i < TYPE_INVAL; i++)
- if (strcmp(errtable[i], arg) == 0)
- return (i);
-
- return (TYPE_INVAL);
-}
-
-static const char *
-type_to_name(uint64_t type)
-{
- switch (type) {
- case DMU_OT_OBJECT_DIRECTORY:
- return ("mosdir");
- case DMU_OT_OBJECT_ARRAY:
- return ("metaslab");
- case DMU_OT_PACKED_NVLIST:
- return ("config");
- case DMU_OT_BPOBJ:
- return ("bpobj");
- case DMU_OT_SPACE_MAP:
- return ("spacemap");
- case DMU_OT_ERROR_LOG:
- return ("errlog");
- default:
- return ("-");
- }
-}
-
-
-/*
- * Print usage message.
- */
-void
-usage(void)
-{
- (void) printf(
- "usage:\n"
- "\n"
- "\tzinject\n"
- "\n"
- "\t\tList all active injection records.\n"
- "\n"
- "\tzinject -c <id|all>\n"
- "\n"
- "\t\tClear the particular record (if given a numeric ID), or\n"
- "\t\tall records if 'all' is specificed.\n"
- "\n"
- "\tzinject -p <function name> pool\n"
- "\n"
- "\t\tInject a panic fault at the specified function. Only \n"
- "\t\tfunctions which call spa_vdev_config_exit(), or \n"
- "\t\tspa_vdev_exit() will trigger a panic.\n"
- "\n"
- "\tzinject -d device [-e errno] [-L <nvlist|uber|pad1|pad2>] [-F]\n"
- "\t [-T <read|write|free|claim|all> pool\n"
- "\n"
- "\t\tInject a fault into a particular device or the device's\n"
- "\t\tlabel. Label injection can either be 'nvlist', 'uber',\n "
- "\t\t'pad1', or 'pad2'.\n"
- "\t\t'errno' can be 'nxio' (the default), 'io', or 'dtl'.\n"
- "\n"
- "\tzinject -d device -A <degrade|fault> pool\n"
- "\n"
- "\t\tPerform a specific action on a particular device\n"
- "\n"
- "\tzinject -d device -D latency:lanes pool\n"
- "\n"
- "\t\tAdd an artificial delay to IO requests on a particular\n"
- "\t\tdevice, such that the requests take a minimum of 'latency'\n"
- "\t\tmilliseconds to complete. Each delay has an associated\n"
- "\t\tnumber of 'lanes' which defines the number of concurrent\n"
- "\t\tIO requests that can be processed.\n"
- "\n"
- "\t\tFor example, with a single lane delay of 10 ms (-D 10:1),\n"
- "\t\tthe device will only be able to service a single IO request\n"
- "\t\tat a time with each request taking 10 ms to complete. So,\n"
- "\t\tif only a single request is submitted every 10 ms, the\n"
- "\t\taverage latency will be 10 ms; but if more than one request\n"
- "\t\tis submitted every 10 ms, the average latency will be more\n"
- "\t\tthan 10 ms.\n"
- "\n"
- "\t\tSimilarly, if a delay of 10 ms is specified to have two\n"
- "\t\tlanes (-D 10:2), then the device will be able to service\n"
- "\t\ttwo requests at a time, each with a minimum latency of\n"
- "\t\t10 ms. So, if two requests are submitted every 10 ms, then\n"
- "\t\tthe average latency will be 10 ms; but if more than two\n"
- "\t\trequests are submitted every 10 ms, the average latency\n"
- "\t\twill be more than 10 ms.\n"
- "\n"
- "\t\tAlso note, these delays are additive. So two invocations\n"
- "\t\tof '-D 10:1', is roughly equivalent to a single invocation\n"
- "\t\tof '-D 10:2'. This also means, one can specify multiple\n"
- "\t\tlanes with differing target latencies. For example, an\n"
- "\t\tinvocation of '-D 10:1' followed by '-D 25:2' will\n"
- "\t\tcreate 3 lanes on the device; one lane with a latency\n"
- "\t\tof 10 ms and two lanes with a 25 ms latency.\n"
- "\n"
- "\tzinject -I [-s <seconds> | -g <txgs>] pool\n"
- "\n"
- "\t\tCause the pool to stop writing blocks yet not\n"
- "\t\treport errors for a duration. Simulates buggy hardware\n"
- "\t\tthat fails to honor cache flush requests.\n"
- "\t\tDefault duration is 30 seconds. The machine is panicked\n"
- "\t\tat the end of the duration.\n"
- "\n"
- "\tzinject -b objset:object:level:blkid pool\n"
- "\n"
- "\t\tInject an error into pool 'pool' with the numeric bookmark\n"
- "\t\tspecified by the remaining tuple. Each number is in\n"
- "\t\thexidecimal, and only one block can be specified.\n"
- "\n"
- "\tzinject [-q] <-t type> [-e errno] [-l level] [-r range]\n"
- "\t [-a] [-m] [-u] [-f freq] <object>\n"
- "\n"
- "\t\tInject an error into the object specified by the '-t' option\n"
- "\t\tand the object descriptor. The 'object' parameter is\n"
- "\t\tinterperted depending on the '-t' option.\n"
- "\n"
- "\t\t-q\tQuiet mode. Only print out the handler number added.\n"
- "\t\t-e\tInject a specific error. Must be either 'io' or\n"
- "\t\t\t'checksum'. Default is 'io'.\n"
- "\t\t-l\tInject error at a particular block level. Default is "
- "0.\n"
- "\t\t-m\tAutomatically remount underlying filesystem.\n"
- "\t\t-r\tInject error over a particular logical range of an\n"
- "\t\t\tobject. Will be translated to the appropriate blkid\n"
- "\t\t\trange according to the object's properties.\n"
- "\t\t-a\tFlush the ARC cache. Can be specified without any\n"
- "\t\t\tassociated object.\n"
- "\t\t-u\tUnload the associated pool. Can be specified with only\n"
- "\t\t\ta pool object.\n"
- "\t\t-f\tOnly inject errors a fraction of the time. Expressed as\n"
- "\t\t\ta percentage between 1 and 100.\n"
- "\n"
- "\t-t data\t\tInject an error into the plain file contents of a\n"
- "\t\t\tfile. The object must be specified as a complete path\n"
- "\t\t\tto a file on a ZFS filesystem.\n"
- "\n"
- "\t-t dnode\tInject an error into the metadnode in the block\n"
- "\t\t\tcorresponding to the dnode for a file or directory. The\n"
- "\t\t\t'-r' option is incompatible with this mode. The object\n"
- "\t\t\tis specified as a complete path to a file or directory\n"
- "\t\t\ton a ZFS filesystem.\n"
- "\n"
- "\t-t <mos>\tInject errors into the MOS for objects of the given\n"
- "\t\t\ttype. Valid types are: mos, mosdir, config, bpobj,\n"
- "\t\t\tspacemap, metaslab, errlog. The only valid <object> is\n"
- "\t\t\tthe poolname.\n");
-}
-
-static int
-iter_handlers(int (*func)(int, const char *, zinject_record_t *, void *),
- void *data)
-{
- zfs_cmd_t zc = { 0 };
- int ret;
-
- while (ioctl(zfs_fd, ZFS_IOC_INJECT_LIST_NEXT, &zc) == 0)
- if ((ret = func((int)zc.zc_guid, zc.zc_name,
- &zc.zc_inject_record, data)) != 0)
- return (ret);
-
- if (errno != ENOENT) {
- (void) fprintf(stderr, "Unable to list handlers: %s\n",
- strerror(errno));
- return (-1);
- }
-
- return (0);
-}
-
-static int
-print_data_handler(int id, const char *pool, zinject_record_t *record,
- void *data)
-{
- int *count = data;
-
- if (record->zi_guid != 0 || record->zi_func[0] != '\0')
- return (0);
-
- if (*count == 0) {
- (void) printf("%3s %-15s %-6s %-6s %-8s %3s %-15s\n",
- "ID", "POOL", "OBJSET", "OBJECT", "TYPE", "LVL", "RANGE");
- (void) printf("--- --------------- ------ "
- "------ -------- --- ---------------\n");
- }
-
- *count += 1;
-
- (void) printf("%3d %-15s %-6llu %-6llu %-8s %3d ", id, pool,
- (u_longlong_t)record->zi_objset, (u_longlong_t)record->zi_object,
- type_to_name(record->zi_type), record->zi_level);
-
- if (record->zi_start == 0 &&
- record->zi_end == -1ULL)
- (void) printf("all\n");
- else
- (void) printf("[%llu, %llu]\n", (u_longlong_t)record->zi_start,
- (u_longlong_t)record->zi_end);
-
- return (0);
-}
-
-static int
-print_device_handler(int id, const char *pool, zinject_record_t *record,
- void *data)
-{
- int *count = data;
-
- if (record->zi_guid == 0 || record->zi_func[0] != '\0')
- return (0);
-
- if (record->zi_cmd == ZINJECT_DELAY_IO)
- return (0);
-
- if (*count == 0) {
- (void) printf("%3s %-15s %s\n", "ID", "POOL", "GUID");
- (void) printf("--- --------------- ----------------\n");
- }
-
- *count += 1;
-
- (void) printf("%3d %-15s %llx\n", id, pool,
- (u_longlong_t)record->zi_guid);
-
- return (0);
-}
-
-static int
-print_delay_handler(int id, const char *pool, zinject_record_t *record,
- void *data)
-{
- int *count = data;
-
- if (record->zi_guid == 0 || record->zi_func[0] != '\0')
- return (0);
-
- if (record->zi_cmd != ZINJECT_DELAY_IO)
- return (0);
-
- if (*count == 0) {
- (void) printf("%3s %-15s %-15s %-15s %s\n",
- "ID", "POOL", "DELAY (ms)", "LANES", "GUID");
- (void) printf("--- --------------- --------------- "
- "--------------- ----------------\n");
- }
-
- *count += 1;
-
- (void) printf("%3d %-15s %-15llu %-15llu %llx\n", id, pool,
- (u_longlong_t)NSEC2MSEC(record->zi_timer),
- (u_longlong_t)record->zi_nlanes,
- (u_longlong_t)record->zi_guid);
-
- return (0);
-}
-
-static int
-print_panic_handler(int id, const char *pool, zinject_record_t *record,
- void *data)
-{
- int *count = data;
-
- if (record->zi_func[0] == '\0')
- return (0);
-
- if (*count == 0) {
- (void) printf("%3s %-15s %s\n", "ID", "POOL", "FUNCTION");
- (void) printf("--- --------------- ----------------\n");
- }
-
- *count += 1;
-
- (void) printf("%3d %-15s %s\n", id, pool, record->zi_func);
-
- return (0);
-}
-
-/*
- * Print all registered error handlers. Returns the number of handlers
- * registered.
- */
-static int
-print_all_handlers(void)
-{
- int count = 0, total = 0;
-
- (void) iter_handlers(print_device_handler, &count);
- if (count > 0) {
- total += count;
- (void) printf("\n");
- count = 0;
- }
-
- (void) iter_handlers(print_delay_handler, &count);
- if (count > 0) {
- total += count;
- (void) printf("\n");
- count = 0;
- }
-
- (void) iter_handlers(print_data_handler, &count);
- if (count > 0) {
- total += count;
- (void) printf("\n");
- count = 0;
- }
-
- (void) iter_handlers(print_panic_handler, &count);
-
- return (count + total);
-}
-
-/* ARGSUSED */
-static int
-cancel_one_handler(int id, const char *pool, zinject_record_t *record,
- void *data)
-{
- zfs_cmd_t zc = { 0 };
-
- zc.zc_guid = (uint64_t)id;
-
- if (ioctl(zfs_fd, ZFS_IOC_CLEAR_FAULT, &zc) != 0) {
- (void) fprintf(stderr, "failed to remove handler %d: %s\n",
- id, strerror(errno));
- return (1);
- }
-
- return (0);
-}
-
-/*
- * Remove all fault injection handlers.
- */
-static int
-cancel_all_handlers(void)
-{
- int ret = iter_handlers(cancel_one_handler, NULL);
-
- if (ret == 0)
- (void) printf("removed all registered handlers\n");
-
- return (ret);
-}
-
-/*
- * Remove a specific fault injection handler.
- */
-static int
-cancel_handler(int id)
-{
- zfs_cmd_t zc = { 0 };
-
- zc.zc_guid = (uint64_t)id;
-
- if (ioctl(zfs_fd, ZFS_IOC_CLEAR_FAULT, &zc) != 0) {
- (void) fprintf(stderr, "failed to remove handler %d: %s\n",
- id, strerror(errno));
- return (1);
- }
-
- (void) printf("removed handler %d\n", id);
-
- return (0);
-}
-
-/*
- * Register a new fault injection handler.
- */
-static int
-register_handler(const char *pool, int flags, zinject_record_t *record,
- int quiet)
-{
- zfs_cmd_t zc = { 0 };
-
- (void) strcpy(zc.zc_name, pool);
- zc.zc_inject_record = *record;
- zc.zc_guid = flags;
-
- if (ioctl(zfs_fd, ZFS_IOC_INJECT_FAULT, &zc) != 0) {
- (void) fprintf(stderr, "failed to add handler: %s\n",
- strerror(errno));
- return (1);
- }
-
- if (flags & ZINJECT_NULL)
- return (0);
-
- if (quiet) {
- (void) printf("%llu\n", (u_longlong_t)zc.zc_guid);
- } else {
- (void) printf("Added handler %llu with the following "
- "properties:\n", (u_longlong_t)zc.zc_guid);
- (void) printf(" pool: %s\n", pool);
- if (record->zi_guid) {
- (void) printf(" vdev: %llx\n",
- (u_longlong_t)record->zi_guid);
- } else if (record->zi_func[0] != '\0') {
- (void) printf(" panic function: %s\n",
- record->zi_func);
- } else if (record->zi_duration > 0) {
- (void) printf(" time: %lld seconds\n",
- (u_longlong_t)record->zi_duration);
- } else if (record->zi_duration < 0) {
- (void) printf(" txgs: %lld \n",
- (u_longlong_t)-record->zi_duration);
- } else {
- (void) printf("objset: %llu\n",
- (u_longlong_t)record->zi_objset);
- (void) printf("object: %llu\n",
- (u_longlong_t)record->zi_object);
- (void) printf(" type: %llu\n",
- (u_longlong_t)record->zi_type);
- (void) printf(" level: %d\n", record->zi_level);
- if (record->zi_start == 0 &&
- record->zi_end == -1ULL)
- (void) printf(" range: all\n");
- else
- (void) printf(" range: [%llu, %llu)\n",
- (u_longlong_t)record->zi_start,
- (u_longlong_t)record->zi_end);
- }
- }
-
- return (0);
-}
-
-int
-perform_action(const char *pool, zinject_record_t *record, int cmd)
-{
- zfs_cmd_t zc = { 0 };
-
- ASSERT(cmd == VDEV_STATE_DEGRADED || cmd == VDEV_STATE_FAULTED);
- (void) strlcpy(zc.zc_name, pool, sizeof (zc.zc_name));
- zc.zc_guid = record->zi_guid;
- zc.zc_cookie = cmd;
-
- if (ioctl(zfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
- return (0);
-
- return (1);
-}
-
-static int
-parse_delay(char *str, uint64_t *delay, uint64_t *nlanes)
-{
- unsigned long scan_delay;
- unsigned long scan_nlanes;
-
- if (sscanf(str, "%lu:%lu", &scan_delay, &scan_nlanes) != 2)
- return (1);
-
- /*
- * We explicitly disallow a delay of zero here, because we key
- * off this value being non-zero in translate_device(), to
- * determine if the fault is a ZINJECT_DELAY_IO fault or not.
- */
- if (scan_delay == 0)
- return (1);
-
- /*
- * The units for the CLI delay parameter is milliseconds, but
- * the data passed to the kernel is interpreted as nanoseconds.
- * Thus we scale the milliseconds to nanoseconds here, and this
- * nanosecond value is used to pass the delay to the kernel.
- */
- *delay = MSEC2NSEC(scan_delay);
- *nlanes = scan_nlanes;
-
- return (0);
-}
-
-int
-main(int argc, char **argv)
-{
- int c;
- char *range = NULL;
- char *cancel = NULL;
- char *end;
- char *raw = NULL;
- char *device = NULL;
- int level = 0;
- int quiet = 0;
- int error = 0;
- int domount = 0;
- int io_type = ZIO_TYPES;
- int action = VDEV_STATE_UNKNOWN;
- err_type_t type = TYPE_INVAL;
- err_type_t label = TYPE_INVAL;
- zinject_record_t record = { 0 };
- char pool[MAXNAMELEN];
- char dataset[MAXNAMELEN];
- zfs_handle_t *zhp;
- int nowrites = 0;
- int dur_txg = 0;
- int dur_secs = 0;
- int ret;
- int flags = 0;
-
- if ((g_zfs = libzfs_init()) == NULL) {
- (void) fprintf(stderr, "internal error: failed to "
- "initialize ZFS library\n");
- return (1);
- }
-
- libzfs_print_on_error(g_zfs, B_TRUE);
-
- if ((zfs_fd = open(ZFS_DEV, O_RDWR)) < 0) {
- (void) fprintf(stderr, "failed to open ZFS device\n");
- return (1);
- }
-
- if (argc == 1) {
- /*
- * No arguments. Print the available handlers. If there are no
- * available handlers, direct the user to '-h' for help
- * information.
- */
- if (print_all_handlers() == 0) {
- (void) printf("No handlers registered.\n");
- (void) printf("Run 'zinject -h' for usage "
- "information.\n");
- }
-
- return (0);
- }
-
- while ((c = getopt(argc, argv,
- ":aA:b:d:D:f:Fg:qhIc:t:T:l:mr:s:e:uL:p:")) != -1) {
- switch (c) {
- case 'a':
- flags |= ZINJECT_FLUSH_ARC;
- break;
- case 'A':
- if (strcasecmp(optarg, "degrade") == 0) {
- action = VDEV_STATE_DEGRADED;
- } else if (strcasecmp(optarg, "fault") == 0) {
- action = VDEV_STATE_FAULTED;
- } else {
- (void) fprintf(stderr, "invalid action '%s': "
- "must be 'degrade' or 'fault'\n", optarg);
- usage();
- return (1);
- }
- break;
- case 'b':
- raw = optarg;
- break;
- case 'c':
- cancel = optarg;
- break;
- case 'd':
- device = optarg;
- break;
- case 'D':
- ret = parse_delay(optarg, &record.zi_timer,
- &record.zi_nlanes);
- if (ret != 0) {
- (void) fprintf(stderr, "invalid i/o delay "
- "value: '%s'\n", optarg);
- usage();
- return (1);
- }
- break;
- case 'e':
- if (strcasecmp(optarg, "io") == 0) {
- error = EIO;
- } else if (strcasecmp(optarg, "checksum") == 0) {
- error = ECKSUM;
- } else if (strcasecmp(optarg, "nxio") == 0) {
- error = ENXIO;
- } else if (strcasecmp(optarg, "dtl") == 0) {
- error = ECHILD;
- } else {
- (void) fprintf(stderr, "invalid error type "
- "'%s': must be 'io', 'checksum' or "
- "'nxio'\n", optarg);
- usage();
- return (1);
- }
- break;
- case 'f':
- record.zi_freq = atoi(optarg);
- if (record.zi_freq < 1 || record.zi_freq > 100) {
- (void) fprintf(stderr, "frequency range must "
- "be in the range (0, 100]\n");
- return (1);
- }
- break;
- case 'F':
- record.zi_failfast = B_TRUE;
- break;
- case 'g':
- dur_txg = 1;
- record.zi_duration = (int)strtol(optarg, &end, 10);
- if (record.zi_duration <= 0 || *end != '\0') {
- (void) fprintf(stderr, "invalid duration '%s': "
- "must be a positive integer\n", optarg);
- usage();
- return (1);
- }
- /* store duration of txgs as its negative */
- record.zi_duration *= -1;
- break;
- case 'h':
- usage();
- return (0);
- case 'I':
- /* default duration, if one hasn't yet been defined */
- nowrites = 1;
- if (dur_secs == 0 && dur_txg == 0)
- record.zi_duration = 30;
- break;
- case 'l':
- level = (int)strtol(optarg, &end, 10);
- if (*end != '\0') {
- (void) fprintf(stderr, "invalid level '%s': "
- "must be an integer\n", optarg);
- usage();
- return (1);
- }
- break;
- case 'm':
- domount = 1;
- break;
- case 'p':
- (void) strlcpy(record.zi_func, optarg,
- sizeof (record.zi_func));
- record.zi_cmd = ZINJECT_PANIC;
- break;
- case 'q':
- quiet = 1;
- break;
- case 'r':
- range = optarg;
- break;
- case 's':
- dur_secs = 1;
- record.zi_duration = (int)strtol(optarg, &end, 10);
- if (record.zi_duration <= 0 || *end != '\0') {
- (void) fprintf(stderr, "invalid duration '%s': "
- "must be a positive integer\n", optarg);
- usage();
- return (1);
- }
- break;
- case 'T':
- if (strcasecmp(optarg, "read") == 0) {
- io_type = ZIO_TYPE_READ;
- } else if (strcasecmp(optarg, "write") == 0) {
- io_type = ZIO_TYPE_WRITE;
- } else if (strcasecmp(optarg, "free") == 0) {
- io_type = ZIO_TYPE_FREE;
- } else if (strcasecmp(optarg, "claim") == 0) {
- io_type = ZIO_TYPE_CLAIM;
- } else if (strcasecmp(optarg, "all") == 0) {
- io_type = ZIO_TYPES;
- } else {
- (void) fprintf(stderr, "invalid I/O type "
- "'%s': must be 'read', 'write', 'free', "
- "'claim' or 'all'\n", optarg);
- usage();
- return (1);
- }
- break;
- case 't':
- if ((type = name_to_type(optarg)) == TYPE_INVAL &&
- !MOS_TYPE(type)) {
- (void) fprintf(stderr, "invalid type '%s'\n",
- optarg);
- usage();
- return (1);
- }
- break;
- case 'u':
- flags |= ZINJECT_UNLOAD_SPA;
- break;
- case 'L':
- if ((label = name_to_type(optarg)) == TYPE_INVAL &&
- !LABEL_TYPE(type)) {
- (void) fprintf(stderr, "invalid label type "
- "'%s'\n", optarg);
- usage();
- return (1);
- }
- break;
- case ':':
- (void) fprintf(stderr, "option -%c requires an "
- "operand\n", optopt);
- usage();
- return (1);
- case '?':
- (void) fprintf(stderr, "invalid option '%c'\n",
- optopt);
- usage();
- return (2);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (record.zi_duration != 0)
- record.zi_cmd = ZINJECT_IGNORED_WRITES;
-
- if (cancel != NULL) {
- /*
- * '-c' is invalid with any other options.
- */
- if (raw != NULL || range != NULL || type != TYPE_INVAL ||
- level != 0 || record.zi_cmd != ZINJECT_UNINITIALIZED) {
- (void) fprintf(stderr, "cancel (-c) incompatible with "
- "any other options\n");
- usage();
- return (2);
- }
- if (argc != 0) {
- (void) fprintf(stderr, "extraneous argument to '-c'\n");
- usage();
- return (2);
- }
-
- if (strcmp(cancel, "all") == 0) {
- return (cancel_all_handlers());
- } else {
- int id = (int)strtol(cancel, &end, 10);
- if (*end != '\0') {
- (void) fprintf(stderr, "invalid handle id '%s':"
- " must be an integer or 'all'\n", cancel);
- usage();
- return (1);
- }
- return (cancel_handler(id));
- }
- }
-
- if (device != NULL) {
- /*
- * Device (-d) injection uses a completely different mechanism
- * for doing injection, so handle it separately here.
- */
- if (raw != NULL || range != NULL || type != TYPE_INVAL ||
- level != 0 || record.zi_cmd != ZINJECT_UNINITIALIZED) {
- (void) fprintf(stderr, "device (-d) incompatible with "
- "data error injection\n");
- usage();
- return (2);
- }
-
- if (argc != 1) {
- (void) fprintf(stderr, "device (-d) injection requires "
- "a single pool name\n");
- usage();
- return (2);
- }
-
- (void) strcpy(pool, argv[0]);
- dataset[0] = '\0';
-
- if (error == ECKSUM) {
- (void) fprintf(stderr, "device error type must be "
- "'io' or 'nxio'\n");
- return (1);
- }
-
- record.zi_iotype = io_type;
- if (translate_device(pool, device, label, &record) != 0)
- return (1);
- if (!error)
- error = ENXIO;
-
- if (action != VDEV_STATE_UNKNOWN)
- return (perform_action(pool, &record, action));
-
- } else if (raw != NULL) {
- if (range != NULL || type != TYPE_INVAL || level != 0 ||
- record.zi_cmd != ZINJECT_UNINITIALIZED) {
- (void) fprintf(stderr, "raw (-b) format with "
- "any other options\n");
- usage();
- return (2);
- }
-
- if (argc != 1) {
- (void) fprintf(stderr, "raw (-b) format expects a "
- "single pool name\n");
- usage();
- return (2);
- }
-
- (void) strcpy(pool, argv[0]);
- dataset[0] = '\0';
-
- if (error == ENXIO) {
- (void) fprintf(stderr, "data error type must be "
- "'checksum' or 'io'\n");
- return (1);
- }
-
- record.zi_cmd = ZINJECT_DATA_FAULT;
- if (translate_raw(raw, &record) != 0)
- return (1);
- if (!error)
- error = EIO;
- } else if (record.zi_cmd == ZINJECT_PANIC) {
- if (raw != NULL || range != NULL || type != TYPE_INVAL ||
- level != 0 || device != NULL) {
- (void) fprintf(stderr, "panic (-p) incompatible with "
- "other options\n");
- usage();
- return (2);
- }
-
- if (argc < 1 || argc > 2) {
- (void) fprintf(stderr, "panic (-p) injection requires "
- "a single pool name and an optional id\n");
- usage();
- return (2);
- }
-
- (void) strcpy(pool, argv[0]);
- if (argv[1] != NULL)
- record.zi_type = atoi(argv[1]);
- dataset[0] = '\0';
- } else if (record.zi_cmd == ZINJECT_IGNORED_WRITES) {
- if (nowrites == 0) {
- (void) fprintf(stderr, "-s or -g meaningless "
- "without -I (ignore writes)\n");
- usage();
- return (2);
- } else if (dur_secs && dur_txg) {
- (void) fprintf(stderr, "choose a duration either "
- "in seconds (-s) or a number of txgs (-g) "
- "but not both\n");
- usage();
- return (2);
- } else if (argc != 1) {
- (void) fprintf(stderr, "ignore writes (-I) "
- "injection requires a single pool name\n");
- usage();
- return (2);
- }
-
- (void) strcpy(pool, argv[0]);
- dataset[0] = '\0';
- } else if (type == TYPE_INVAL) {
- if (flags == 0) {
- (void) fprintf(stderr, "at least one of '-b', '-d', "
- "'-t', '-a', '-p', '-I' or '-u' "
- "must be specified\n");
- usage();
- return (2);
- }
-
- if (argc == 1 && (flags & ZINJECT_UNLOAD_SPA)) {
- (void) strcpy(pool, argv[0]);
- dataset[0] = '\0';
- } else if (argc != 0) {
- (void) fprintf(stderr, "extraneous argument for "
- "'-f'\n");
- usage();
- return (2);
- }
-
- flags |= ZINJECT_NULL;
- } else {
- if (argc != 1) {
- (void) fprintf(stderr, "missing object\n");
- usage();
- return (2);
- }
-
- if (error == ENXIO) {
- (void) fprintf(stderr, "data error type must be "
- "'checksum' or 'io'\n");
- return (1);
- }
-
- record.zi_cmd = ZINJECT_DATA_FAULT;
- if (translate_record(type, argv[0], range, level, &record, pool,
- dataset) != 0)
- return (1);
- if (!error)
- error = EIO;
- }
-
- /*
- * If this is pool-wide metadata, unmount everything. The ioctl() will
- * unload the pool, so that we trigger spa-wide reopen of metadata next
- * time we access the pool.
- */
- if (dataset[0] != '\0' && domount) {
- if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET)) == NULL)
- return (1);
-
- if (zfs_unmount(zhp, NULL, 0) != 0)
- return (1);
- }
-
- record.zi_error = error;
-
- ret = register_handler(pool, flags, &record, quiet);
-
- if (dataset[0] != '\0' && domount)
- ret = (zfs_mount(zhp, NULL, 0) != 0);
-
- libzfs_fini(g_zfs);
-
- return (ret);
-}
diff --git a/cddl/contrib/opensolaris/cmd/zinject/zinject.h b/cddl/contrib/opensolaris/cmd/zinject/zinject.h
deleted file mode 100644
index 46fdcad8b31f9..0000000000000
--- a/cddl/contrib/opensolaris/cmd/zinject/zinject.h
+++ /dev/null
@@ -1,70 +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 _ZINJECT_H
-#define _ZINJECT_H
-
-#include <sys/zfs_ioctl.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef enum {
- TYPE_DATA, /* plain file contents */
- TYPE_DNODE, /* metadnode contents */
- TYPE_MOS, /* all MOS data */
- TYPE_MOSDIR, /* MOS object directory */
- TYPE_METASLAB, /* metaslab objects */
- TYPE_CONFIG, /* MOS config */
- TYPE_BPOBJ, /* block pointer list */
- TYPE_SPACEMAP, /* space map objects */
- TYPE_ERRLOG, /* persistent error log */
- TYPE_LABEL_UBERBLOCK, /* label specific uberblock */
- TYPE_LABEL_NVLIST, /* label specific nvlist */
- TYPE_LABEL_PAD1, /* label specific 8K pad1 area */
- TYPE_LABEL_PAD2, /* label specific 8K pad2 area */
- TYPE_INVAL
-} err_type_t;
-
-#define MOS_TYPE(t) \
- ((t) >= TYPE_MOS && (t) < TYPE_LABEL_UBERBLOCK)
-
-#define LABEL_TYPE(t) \
- ((t) >= TYPE_LABEL_UBERBLOCK && (t) < TYPE_INVAL)
-
-int translate_record(err_type_t type, const char *object, const char *range,
- int level, zinject_record_t *record, char *poolname, char *dataset);
-int translate_raw(const char *raw, zinject_record_t *record);
-int translate_device(const char *pool, const char *device,
- err_type_t label_type, zinject_record_t *record);
-void usage(void);
-
-extern libzfs_handle_t *g_zfs;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ZINJECT_H */
diff --git a/cddl/contrib/opensolaris/cmd/zlook/zlook.c b/cddl/contrib/opensolaris/cmd/zlook/zlook.c
deleted file mode 100644
index 29a6559f90230..0000000000000
--- a/cddl/contrib/opensolaris/cmd/zlook/zlook.c
+++ /dev/null
@@ -1,411 +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) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- */
-
-/*
- * This is a test program that uses ioctls to the ZFS Unit Test driver
- * to perform readdirs or lookups using flags not normally available
- * to user-land programs. This allows testing of the flags'
- * behavior outside of a complicated consumer, such as the SMB driver.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <stropts.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/dirent.h>
-#include <sys/attr.h>
-#include <stddef.h>
-#include <fcntl.h>
-#include <string.h>
-#include <time.h>
-
-#define _KERNEL
-
-#include <sys/fs/zut.h>
-#include <sys/extdirent.h>
-
-#undef _KERNEL
-
-#define MAXBUF (64 * 1024)
-#define BIGBUF 4096
-#define LILBUF (sizeof (dirent_t))
-
-#define DIRENT_NAMELEN(reclen) \
- ((reclen) - (offsetof(dirent_t, d_name[0])))
-
-static void
-usage(char *pnam)
-{
- (void) fprintf(stderr, "Usage:\n %s -l [-is] dir-to-look-in "
- "file-in-dir [xfile-on-file]\n", pnam);
- (void) fprintf(stderr, " %s -i [-ls] dir-to-look-in "
- "file-in-dir [xfile-on-file]\n", pnam);
- (void) fprintf(stderr, " %s -s [-il] dir-to-look-in "
- "file-in-dir [xfile-on-file]\n", pnam);
- (void) fprintf(stderr, "\t Perform a lookup\n");
- (void) fprintf(stderr, "\t -l == lookup\n");
- (void) fprintf(stderr, "\t -i == request FIGNORECASE\n");
- (void) fprintf(stderr, "\t -s == request stat(2) and xvattr info\n");
- (void) fprintf(stderr, " %s -r [-ea] [-b buffer-size-in-bytes] "
- "dir-to-look-in [file-in-dir]\n", pnam);
- (void) fprintf(stderr, " %s -e [-ra] [-b buffer-size-in-bytes] "
- "dir-to-look-in [file-in-dir]\n", pnam);
- (void) fprintf(stderr, " %s -a [-re] [-b buffer-size-in-bytes] "
- "dir-to-look-in [file-in-dir]\n", pnam);
- (void) fprintf(stderr, "\t Perform a readdir\n");
- (void) fprintf(stderr, "\t -r == readdir\n");
- (void) fprintf(stderr, "\t -e == request extended entries\n");
- (void) fprintf(stderr, "\t -a == request access filtering\n");
- (void) fprintf(stderr, "\t -b == buffer size (default 4K)\n");
- (void) fprintf(stderr, " %s -A path\n", pnam);
- (void) fprintf(stderr, "\t Look up _PC_ACCESS_FILTERING "
- "for path with pathconf(2)\n");
- (void) fprintf(stderr, " %s -E path\n", pnam);
- (void) fprintf(stderr, "\t Look up _PC_SATTR_EXISTS "
- "for path with pathconf(2)\n");
- (void) fprintf(stderr, " %s -S path\n", pnam);
- (void) fprintf(stderr, "\t Look up _PC_SATTR_EXISTS "
- "for path with pathconf(2)\n");
- exit(EINVAL);
-}
-
-static void
-print_extd_entries(zut_readdir_t *r)
-{
- struct edirent *eodp;
- char *bufstart;
-
- eodp = (edirent_t *)(uintptr_t)r->zr_buf;
- bufstart = (char *)eodp;
- while ((char *)eodp < bufstart + r->zr_bytes) {
- char *blanks = " ";
- int i = 0;
- while (i < EDIRENT_NAMELEN(eodp->ed_reclen)) {
- if (!eodp->ed_name[i])
- break;
- (void) printf("%c", eodp->ed_name[i++]);
- }
- if (i < 16)
- (void) printf("%.*s", 16 - i, blanks);
- (void) printf("\t%x\n", eodp->ed_eflags);
- eodp = (edirent_t *)((intptr_t)eodp + eodp->ed_reclen);
- }
-}
-
-static void
-print_entries(zut_readdir_t *r)
-{
- dirent64_t *dp;
- char *bufstart;
-
- dp = (dirent64_t *)(intptr_t)r->zr_buf;
- bufstart = (char *)dp;
- while ((char *)dp < bufstart + r->zr_bytes) {
- int i = 0;
- while (i < DIRENT_NAMELEN(dp->d_reclen)) {
- if (!dp->d_name[i])
- break;
- (void) printf("%c", dp->d_name[i++]);
- }
- (void) printf("\n");
- dp = (dirent64_t *)((intptr_t)dp + dp->d_reclen);
- }
-}
-
-static void
-print_stats(struct stat64 *sb)
-{
- char timebuf[512];
-
- (void) printf("st_mode\t\t\t%04lo\n", (unsigned long)sb->st_mode);
- (void) printf("st_ino\t\t\t%llu\n", (unsigned long long)sb->st_ino);
- (void) printf("st_nlink\t\t%lu\n", (unsigned long)sb->st_nlink);
- (void) printf("st_uid\t\t\t%d\n", sb->st_uid);
- (void) printf("st_gid\t\t\t%d\n", sb->st_gid);
- (void) printf("st_size\t\t\t%lld\n", (long long)sb->st_size);
- (void) printf("st_blksize\t\t%ld\n", (long)sb->st_blksize);
- (void) printf("st_blocks\t\t%lld\n", (long long)sb->st_blocks);
-
- timebuf[0] = 0;
- if (ctime_r(&sb->st_atime, timebuf, 512)) {
- (void) printf("st_atime\t\t");
- (void) printf("%s", timebuf);
- }
- timebuf[0] = 0;
- if (ctime_r(&sb->st_mtime, timebuf, 512)) {
- (void) printf("st_mtime\t\t");
- (void) printf("%s", timebuf);
- }
- timebuf[0] = 0;
- if (ctime_r(&sb->st_ctime, timebuf, 512)) {
- (void) printf("st_ctime\t\t");
- (void) printf("%s", timebuf);
- }
-}
-
-static void
-print_xvs(uint64_t xvs)
-{
- uint_t bits;
- int idx = 0;
-
- if (xvs == 0)
- return;
-
- (void) printf("-------------------\n");
- (void) printf("Attribute bit(s) set:\n");
- (void) printf("-------------------\n");
-
- bits = xvs & ((1 << F_ATTR_ALL) - 1);
- while (bits) {
- uint_t rest = bits >> 1;
- if (bits & 1) {
- (void) printf("%s", attr_to_name((f_attr_t)idx));
- if (rest)
- (void) printf(", ");
- }
- idx++;
- bits = rest;
- }
- (void) printf("\n");
-}
-
-int
-main(int argc, char **argv)
-{
- zut_lookup_t lk = {0};
- zut_readdir_t rd = {0};
- boolean_t checking = B_FALSE;
- boolean_t looking = B_FALSE;
- boolean_t reading = B_FALSE;
- boolean_t bflag = B_FALSE;
- long rddir_bufsize = BIGBUF;
- int error = 0;
- int check;
- int fd;
- int c;
-
- while ((c = getopt(argc, argv, "lisaerb:ASE")) != -1) {
- switch (c) {
- case 'l':
- looking = B_TRUE;
- break;
- case 'i':
- lk.zl_reqflags |= ZUT_IGNORECASE;
- looking = B_TRUE;
- break;
- case 's':
- lk.zl_reqflags |= ZUT_GETSTAT;
- looking = B_TRUE;
- break;
- case 'a':
- rd.zr_reqflags |= ZUT_ACCFILTER;
- reading = B_TRUE;
- break;
- case 'e':
- rd.zr_reqflags |= ZUT_EXTRDDIR;
- reading = B_TRUE;
- break;
- case 'r':
- reading = B_TRUE;
- break;
- case 'b':
- reading = B_TRUE;
- bflag = B_TRUE;
- rddir_bufsize = strtol(optarg, NULL, 0);
- break;
- case 'A':
- checking = B_TRUE;
- check = _PC_ACCESS_FILTERING;
- break;
- case 'S':
- checking = B_TRUE;
- check = _PC_SATTR_ENABLED;
- break;
- case 'E':
- checking = B_TRUE;
- check = _PC_SATTR_EXISTS;
- break;
- case '?':
- default:
- usage(argv[0]); /* no return */
- }
- }
-
- if ((checking && looking) || (checking && reading) ||
- (looking && reading) || (!reading && bflag) ||
- (!checking && !reading && !looking))
- usage(argv[0]); /* no return */
-
- if (rddir_bufsize < LILBUF || rddir_bufsize > MAXBUF) {
- (void) fprintf(stderr, "Sorry, buffer size "
- "must be >= %d and less than or equal to %d bytes.\n",
- (int)LILBUF, MAXBUF);
- exit(EINVAL);
- }
-
- if (checking) {
- char pathbuf[MAXPATHLEN];
- long result;
-
- if (argc - optind < 1)
- usage(argv[0]); /* no return */
- (void) strlcpy(pathbuf, argv[optind], MAXPATHLEN);
- result = pathconf(pathbuf, check);
- (void) printf("pathconf(2) check for %s\n", pathbuf);
- switch (check) {
- case _PC_SATTR_ENABLED:
- (void) printf("System attributes ");
- if (result != 0)
- (void) printf("Enabled\n");
- else
- (void) printf("Not enabled\n");
- break;
- case _PC_SATTR_EXISTS:
- (void) printf("System attributes ");
- if (result != 0)
- (void) printf("Exist\n");
- else
- (void) printf("Do not exist\n");
- break;
- case _PC_ACCESS_FILTERING:
- (void) printf("Access filtering ");
- if (result != 0)
- (void) printf("Available\n");
- else
- (void) printf("Not available\n");
- break;
- }
- return (result);
- }
-
- if ((fd = open(ZUT_DEV, O_RDONLY)) < 0) {
- perror(ZUT_DEV);
- return (ENXIO);
- }
-
- if (reading) {
- char *buf;
-
- if (argc - optind < 1)
- usage(argv[0]); /* no return */
-
- (void) strlcpy(rd.zr_dir, argv[optind], MAXPATHLEN);
- if (argc - optind > 1) {
- (void) strlcpy(rd.zr_file, argv[optind + 1],
- MAXNAMELEN);
- rd.zr_reqflags |= ZUT_XATTR;
- }
-
- if ((buf = malloc(rddir_bufsize)) == NULL) {
- error = errno;
- perror("malloc");
- (void) close(fd);
- return (error);
- }
-
- rd.zr_buf = (uint64_t)(uintptr_t)buf;
- rd.zr_buflen = rddir_bufsize;
-
- while (!rd.zr_eof) {
- int ierr;
-
- if ((ierr = ioctl(fd, ZUT_IOC_READDIR, &rd)) != 0) {
- (void) fprintf(stderr,
- "IOCTL error: %s (%d)\n",
- strerror(ierr), ierr);
- free(buf);
- (void) close(fd);
- return (ierr);
- }
- if (rd.zr_retcode) {
- (void) fprintf(stderr,
- "readdir result: %s (%d)\n",
- strerror(rd.zr_retcode), rd.zr_retcode);
- free(buf);
- (void) close(fd);
- return (rd.zr_retcode);
- }
- if (rd.zr_reqflags & ZUT_EXTRDDIR)
- print_extd_entries(&rd);
- else
- print_entries(&rd);
- }
- free(buf);
- } else {
- int ierr;
-
- if (argc - optind < 2)
- usage(argv[0]); /* no return */
-
- (void) strlcpy(lk.zl_dir, argv[optind], MAXPATHLEN);
- (void) strlcpy(lk.zl_file, argv[optind + 1], MAXNAMELEN);
- if (argc - optind > 2) {
- (void) strlcpy(lk.zl_xfile,
- argv[optind + 2], MAXNAMELEN);
- lk.zl_reqflags |= ZUT_XATTR;
- }
-
- if ((ierr = ioctl(fd, ZUT_IOC_LOOKUP, &lk)) != 0) {
- (void) fprintf(stderr,
- "IOCTL error: %s (%d)\n",
- strerror(ierr), ierr);
- (void) close(fd);
- return (ierr);
- }
-
- (void) printf("\nLookup of ");
- if (lk.zl_reqflags & ZUT_XATTR) {
- (void) printf("extended attribute \"%s\" of ",
- lk.zl_xfile);
- }
- (void) printf("file \"%s\" ", lk.zl_file);
- (void) printf("in directory \"%s\" ", lk.zl_dir);
- if (lk.zl_retcode) {
- (void) printf("failed: %s (%d)\n",
- strerror(lk.zl_retcode), lk.zl_retcode);
- (void) close(fd);
- return (lk.zl_retcode);
- }
-
- (void) printf("succeeded.\n");
- if (lk.zl_reqflags & ZUT_IGNORECASE) {
- (void) printf("----------------------------\n");
- (void) printf("dirent flags: 0x%0x\n", lk.zl_deflags);
- (void) printf("real name: %s\n", lk.zl_real);
- }
- if (lk.zl_reqflags & ZUT_GETSTAT) {
- (void) printf("----------------------------\n");
- print_stats(&lk.zl_statbuf);
- print_xvs(lk.zl_xvattrs);
- }
- }
-
- (void) close(fd);
- return (0);
-}
diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool-features.7 b/cddl/contrib/opensolaris/cmd/zpool/zpool-features.7
deleted file mode 100644
index 980d4da0e31b6..0000000000000
--- a/cddl/contrib/opensolaris/cmd/zpool/zpool-features.7
+++ /dev/null
@@ -1,674 +0,0 @@
-'\" te
-.\" Copyright (c) 2012, 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) 2012, 2017 by Delphix. All rights reserved.
-.\" Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
-.\" Copyright (c) 2013, Joyent, Inc. All rights reserved.
-.\"
-.\" $FreeBSD$
-.\"
-.Dd August 16, 2019
-.Dt ZPOOL-FEATURES 7
-.Os
-.Sh NAME
-.Nm zpool-features
-.Nd ZFS pool feature descriptions
-.Sh DESCRIPTION
-ZFS pool on\-disk format versions are specified via "features" which replace
-the old on\-disk format numbers (the last supported on\-disk format number is
-28).
-To enable a feature on a pool use the
-.Cm upgrade
-subcommand of the
-.Xr zpool 8
-command, or set the
-.Sy feature@feature_name
-property to
-.Ar enabled .
-.Pp
-The pool format does not affect file system version compatibility or the ability
-to send file systems between pools.
-.Pp
-Since most features can be enabled independently of each other the on\-disk
-format of the pool is specified by the set of all features marked as
-.Sy active
-on the pool.
-If the pool was created by another software version this set may
-include unsupported features.
-.Ss Identifying features
-Every feature has a guid of the form
-.Sy com.example:feature_name .
-The reverse DNS name ensures that the feature's guid is unique across all ZFS
-implementations.
-When unsupported features are encountered on a pool they will
-be identified by their guids.
-Refer to the documentation for the ZFS implementation that created the pool
-for information about those features.
-.Pp
-Each supported feature also has a short name.
-By convention a feature's short name is the portion of its guid which follows
-the ':' (e.g.
-.Sy com.example:feature_name
-would have the short name
-.Sy feature_name ),
-however a feature's short name may differ across ZFS implementations if
-following the convention would result in name conflicts.
-.Ss Feature states
-Features can be in one of three states:
-.Bl -tag -width "XXXXXXXX"
-.It Sy active
-This feature's on\-disk format changes are in effect on the pool.
-Support for this feature is required to import the pool in read\-write mode.
-If this feature is not read-only compatible, support is also required to
-import the pool in read\-only mode (see "Read\-only compatibility").
-.It Sy enabled
-An administrator has marked this feature as enabled on the pool, but the
-feature's on\-disk format changes have not been made yet.
-The pool can still be imported by software that does not support this feature,
-but changes may be made to the on\-disk format at any time which will move
-the feature to the
-.Sy active
-state.
-Some features may support returning to the
-.Sy enabled
-state after becoming
-.Sy active .
-See feature\-specific documentation for details.
-.It Sy disabled
-This feature's on\-disk format changes have not been made and will not be made
-unless an administrator moves the feature to the
-.Sy enabled
-state.
-Features cannot be disabled once they have been enabled.
-.El
-.Pp
-The state of supported features is exposed through pool properties of the form
-.Sy feature@short_name .
-.Ss Read\-only compatibility
-Some features may make on\-disk format changes that do not interfere with other
-software's ability to read from the pool.
-These features are referred to as "read\-only compatible".
-If all unsupported features on a pool are read\-only compatible, the pool can
-be imported in read\-only mode by setting the
-.Sy readonly
-property during import (see
-.Xr zpool 8
-for details on importing pools).
-.Ss Unsupported features
-For each unsupported feature enabled on an imported pool a pool property
-named
-.Sy unsupported@feature_guid
-will indicate why the import was allowed despite the unsupported feature.
-Possible values for this property are:
-.Bl -tag -width "XXXXXXXX"
-.It Sy inactive
-The feature is in the
-.Sy enabled
-state and therefore the pool's on\-disk format is still compatible with
-software that does not support this feature.
-.It Sy readonly
-The feature is read\-only compatible and the pool has been imported in
-read\-only mode.
-.El
-.Ss Feature dependencies
-Some features depend on other features being enabled in order to function
-properly.
-Enabling a feature will automatically enable any features it depends on.
-.Sh FEATURES
-The following features are supported on this system:
-.Bl -tag -width "XXXXXXXX"
-.It Sy async_destroy
-.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:async_destroy"
-.It GUID Ta com.delphix:async_destroy
-.It READ\-ONLY COMPATIBLE Ta yes
-.It DEPENDENCIES Ta none
-.El
-.Pp
-Destroying a file system requires traversing all of its data in order to
-return its used space to the pool.
-Without
-.Sy async_destroy
-the file system is not fully removed until all space has been reclaimed.
-If the destroy operation is interrupted by a reboot or power outage the next
-attempt to open the pool will need to complete the destroy operation
-synchronously.
-.Pp
-When
-.Sy async_destroy
-is enabled the file system's data will be reclaimed by a background process,
-allowing the destroy operation to complete without traversing the entire file
-system.
-The background process is able to resume interrupted destroys after the pool
-has been opened, eliminating the need to finish interrupted destroys as part
-of the open operation.
-The amount of space remaining to be reclaimed by the background process is
-available through the
-.Sy freeing
-property.
-.Pp
-This feature is only
-.Sy active
-while
-.Sy freeing
-is non\-zero.
-.It Sy empty_bpobj
-.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:empty_bpobj"
-.It GUID Ta com.delphix:empty_bpobj
-.It READ\-ONLY COMPATIBLE Ta yes
-.It DEPENDENCIES Ta none
-.El
-.Pp
-This feature increases the performance of creating and using a large number
-of snapshots of a single filesystem or volume, and also reduces the disk
-space required.
-.Pp
-When there are many snapshots, each snapshot uses many Block Pointer Objects
-.Pq bpobj's
-to track blocks associated with that snapshot.
-However, in common use cases, most of these bpobj's are empty.
-This feature allows us to create each bpobj on-demand, thus eliminating the
-empty bpobjs.
-.Pp
-This feature is
-.Sy active
-while there are any filesystems, volumes, or snapshots which were created
-after enabling this feature.
-.It Sy filesystem_limits
-.Bl -column "READ\-ONLY COMPATIBLE" "com.joyent:filesystem_limits"
-.It GUID Ta com.joyent:filesystem_limits
-.It READ\-ONLY COMPATIBLE Ta yes
-.It DEPENDENCIES Ta extensible_dataset
-.El
-.Pp
-This feature enables filesystem and snapshot limits.
-These limits can be used
-to control how many filesystems and/or snapshots can be created at the point in
-the tree on which the limits are set.
-.Pp
-This feature is
-.Sy active
-once either of the limit properties has been
-set on a dataset.
-Once activated the feature is never deactivated.
-.It Sy lz4_compress
-.Bl -column "READ\-ONLY COMPATIBLE" "org.illumos:lz4_compress"
-.It GUID Ta org.illumos:lz4_compress
-.It READ\-ONLY COMPATIBLE Ta no
-.It DEPENDENCIES Ta none
-.El
-.Pp
-.Sy lz4
-is a high-performance real-time compression algorithm that
-features significantly faster compression and decompression as well as a
-higher compression ratio than the older
-.Sy lzjb
-compression.
-Typically,
-.Sy lz4
-compression is approximately 50% faster on
-compressible data and 200% faster on incompressible data than
-.Sy lzjb .
-It is also approximately 80% faster on decompression, while
-giving approximately 10% better compression ratio.
-.Pp
-When the
-.Sy lz4_compress
-feature is set to
-.Sy enabled ,
-the
-administrator can turn on
-.Sy lz4
-compression on any dataset on the
-pool using the
-.Xr zfs 8
-command.
-Also, all newly written metadata
-will be compressed with
-.Sy lz4
-algorithm.
-Since this feature is not read-only compatible, this
-operation will render the pool unimportable on systems without support
-for the
-.Sy lz4_compress
-feature.
-Booting off of
-.Sy lz4
--compressed root pools is supported.
-.Pp
-This feature becomes
-.Sy active
-as soon as it is enabled and will
-never return to being
-.Sy enabled .
-.It Sy multi_vdev_crash_dump
-.Bl -column "READ\-ONLY COMPATIBLE" "com.joyent:multi_vdev_crash_dump"
-.It GUID Ta com.joyent:multi_vdev_crash_dump
-.It READ\-ONLY COMPATIBLE Ta no
-.It DEPENDENCIES Ta none
-.El
-.Pp
-This feature allows a dump device to be configured with a pool comprised
-of multiple vdevs.
-Those vdevs may be arranged in any mirrored or raidz
-configuration.
-.\" TODO: this is not yet supported on FreeBSD.
-.\" .Pp
-.\" When the
-.\" .Sy multi_vdev_crash_dump
-.\" feature is set to
-.\" .Sy enabled ,
-.\" the administrator can use the
-.\" .Xr dumpon 8
-.\" command to configure a
-.\" dump device on a pool comprised of multiple vdevs.
-.It Sy spacemap_histogram
-.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:spacemap_histogram"
-.It GUID Ta com.delphix:spacemap_histogram
-.It READ\-ONLY COMPATIBLE Ta yes
-.It DEPENDENCIES Ta none
-.El
-.Pp
-This feature allows ZFS to maintain more information about how free space
-is organized within the pool.
-If this feature is
-.Sy enabled ,
-ZFS will
-set this feature to
-.Sy active
-when a new space map object is created or
-an existing space map is upgraded to the new format.
-Once the feature is
-.Sy active ,
-it will remain in that state until the pool is destroyed.
-.It Sy extensible_dataset
-.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:extensible_dataset"
-.It GUID Ta com.delphix:extensible_dataset
-.It READ\-ONLY COMPATIBLE Ta no
-.It DEPENDENCIES Ta none
-.El
-.Pp
-This feature allows more flexible use of internal ZFS data structures,
-and exists for other features to depend on.
-.Pp
-This feature will be
-.Sy active
-when the first dependent feature uses it,
-and will be returned to the
-.Sy enabled
-state when all datasets that use
-this feature are destroyed.
-.It Sy bookmarks
-.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:bookmarks"
-.It GUID Ta com.delphix:bookmarks
-.It READ\-ONLY COMPATIBLE Ta yes
-.It DEPENDENCIES Ta extensible_dataset
-.El
-.Pp
-This feature enables use of the
-.Nm zfs
-.Cm bookmark
-subcommand.
-.Pp
-This feature is
-.Sy active
-while any bookmarks exist in the pool.
-All bookmarks in the pool can be listed by running
-.Nm zfs
-.Cm list
-.Fl t No bookmark Fl r Ar poolname .
-.It Sy enabled_txg
-.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:enabled_txg"
-.It GUID Ta com.delphix:enabled_txg
-.It READ\-ONLY COMPATIBLE Ta yes
-.It DEPENDENCIES Ta none
-.El
-.Pp
-Once this feature is enabled ZFS records the transaction group number
-in which new features are enabled.
-This has no user-visible impact,
-but other features may depend on this feature.
-.Pp
-This feature becomes
-.Sy active
-as soon as it is enabled and will
-never return to being
-.Sy enabled .
-.It Sy hole_birth
-.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:hole_birth"
-.It GUID Ta com.delphix:hole_birth
-.It READ\-ONLY COMPATIBLE Ta no
-.It DEPENDENCIES Ta enabled_txg
-.El
-.Pp
-This feature improves performance of incremental sends
-.Pq Dq zfs send -i
-and receives for objects with many holes.
-The most common case of
-hole-filled objects is zvols.
-.Pp
-An incremental send stream from snapshot
-.Sy A
-to snapshot
-.Sy B
-contains information about every block that changed between
-.Sy A
-and
-.Sy B .
-Blocks which did not change between those snapshots can be
-identified and omitted from the stream using a piece of metadata called
-the 'block birth time', but birth times are not recorded for holes
-.Pq blocks filled only with zeroes .
-Since holes created after
-.Sy A
-cannot be
-distinguished from holes created before
-.Sy A ,
-information about every
-hole in the entire filesystem or zvol is included in the send stream.
-.Pp
-For workloads where holes are rare this is not a problem.
-However, when
-incrementally replicating filesystems or zvols with many holes
-.Pq for example a zvol formatted with another filesystem
-a lot of time will
-be spent sending and receiving unnecessary information about holes that
-already exist on the receiving side.
-.Pp
-Once the
-.Sy hole_birth
-feature has been enabled the block birth times
-of all new holes will be recorded.
-Incremental sends between snapshots
-created after this feature is enabled will use this new metadata to avoid
-sending information about holes that already exist on the receiving side.
-.Pp
-This feature becomes
-.Sy active
-as soon as it is enabled and will
-never return to being
-.Sy enabled .
-.It Sy embedded_data
-.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:embedded_data"
-.It GUID Ta com.delphix:embedded_data
-.It READ\-ONLY COMPATIBLE Ta no
-.It DEPENDENCIES Ta none
-.El
-.Pp
-This feature improves the performance and compression ratio of
-highly-compressible blocks.
-Blocks whose contents can compress to 112 bytes
-or smaller can take advantage of this feature.
-.Pp
-When this feature is enabled, the contents of highly-compressible blocks are
-stored in the block "pointer" itself
-.Po a misnomer in this case, as it contains
-the compressed data, rather than a pointer to its location on disk
-.Pc .
-Thus
-the space of the block
-.Pq one sector, typically 512 bytes or 4KB
-is saved,
-and no additional i/o is needed to read and write the data block.
-.Pp
-This feature becomes
-.Sy active
-as soon as it is enabled and will
-never return to being
-.Sy enabled .
-.It Sy zpool_checkpoint
-.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:zpool_checkpoint"
-.It GUID Ta com.delphix:zpool_checkpoint
-.It READ\-ONLY COMPATIBLE Ta yes
-.It DEPENDENCIES Ta none
-.El
-.Pp
-This feature enables the "zpool checkpoint" subcommand that can
-checkpoint the state of the pool at the time it was issued and later
-rewind back to it or discard it.
-.Pp
-This feature becomes
-.Sy active
-when the "zpool checkpoint" command is used to checkpoint the pool.
-The feature will only return back to being
-.Sy enabled
-when the pool is rewound or the checkpoint has been discarded.
-.It Sy device_removal
-.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:device_removal"
-.It GUID Ta com.delphix:device_removal
-.It READ\-ONLY COMPATIBLE Ta no
-.It DEPENDENCIES Ta none
-.El
-.Pp
-This feature enables the "zpool remove" subcommand to remove top-level
-vdevs, evacuating them to reduce the total size of the pool.
-.Pp
-This feature becomes
-.Sy active
-when the "zpool remove" command is used
-on a top-level vdev, and will never return to being
-.Sy enabled .
-.It Sy obsolete_counts
-.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:obsolete_counts"
-.It GUID Ta com.delphix:obsolete_counts
-.It READ\-ONLY COMPATIBLE Ta yes
-.It DEPENDENCIES Ta device_removal
-.El
-.Pp
-This feature is an enhancement of device_removal, which will over time
-reduce the memory used to track removed devices. When indirect blocks
-are freed or remapped, we note that their part of the indirect mapping
-is "obsolete", i.e. no longer needed. See also the "zfs remap"
-subcommand in
-.Xr zfs 8 .
-
-This feature becomes
-.Sy active
-when the "zpool remove" command is
-used on a top-level vdev, and will never return to being
-.Sy enabled .
-.It Sy spacemap_v2
-.Bl -column "READ\-ONLY COMPATIBLE" "com.delphix:spacemap_v2"
-.It GUID Ta com.delphix:spacemap_v2
-.It READ\-ONLY COMPATIBLE Ta yes
-.It DEPENDENCIES Ta none
-.El
-.Pp
-This feature enables the use of the new space map encoding which
-consists of two words (instead of one) whenever it is advantageous.
-The new encoding allows space maps to represent large regions of
-space more efficiently on-disk while also increasing their maximum
-addressable offset.
-.Pp
-This feature becomes
-.Sy active
-as soon as it is enabled and will
-never return to being
-.Sy enabled .
-.It Sy large_blocks
-.Bl -column "READ\-ONLY COMPATIBLE" "org.open-zfs:large_block"
-.It GUID Ta org.open-zfs:large_block
-.It READ\-ONLY COMPATIBLE Ta no
-.It DEPENDENCIES Ta extensible_dataset
-.El
-.Pp
-The
-.Sy large_block
-feature allows the record size on a dataset to be
-set larger than 128KB.
-.Pp
-This feature becomes
-.Sy active
-once a
-.Sy recordsize
-property has been set larger than 128KB, and will return to being
-.Sy enabled
-once all filesystems that have ever had their recordsize larger than 128KB
-are destroyed.
-.Pp
-Booting from datasets that use the
-.Sy large_block
-feature is supported by the
-.Fx
-boot loader.
-.It Sy large_dnode
-.Bl -column "READ\-ONLY COMPATIBLE" "org.zfsonlinux:large_dnode"
-.It GUID Ta org.zfsonlinux:large_dnode
-.It READ\-ONLY COMPATIBLE Ta no
-.It DEPENDENCIES Ta extensible_dataset
-.El
-.Pp
-The
-.Sy large_dnode
-feature allows the size of dnodes in a dataset to be set larger than 512B.
-.Pp
-This feature becomes
-.Sy active
-once a dataset contains an object with a dnode larger than 512B,
-which occurs as a result of setting the
-.Sy dnodesize
-dataset property to a value other than
-.Sy legacy .
-The feature will return to being
-.Sy enabled
-once all filesystems that have ever contained a dnode larger than 512B are
-destroyed.
-Large dnodes allow more data to be stored in the bonus buffer, thus potentially
-improving performance by avoiding the use of spill blocks.
-.It Sy sha512
-.Bl -column "READ\-ONLY COMPATIBLE" "org.illumos:sha512"
-.It GUID Ta org.illumos:sha512
-.It READ\-ONLY COMPATIBLE Ta no
-.It DEPENDENCIES Ta extensible_dataset
-.El
-.Pp
-The
-.Sy sha512
-feature enables the use of the SHA-512/256 truncated hash algorithm
-.Pq FIPS 180-4
-for checksum and dedup.
-The native 64-bit arithmetic of SHA-512 provides an approximate 50%
-performance boost over SHA-256 on 64-bit hardware and is thus a good
-minimum-change replacement candidate for systems where hash performance is
-important, but these systems cannot for whatever reason utilize the faster
-.Sy skein
-algorithms.
-.Pp
-When the
-.Sy sha512
-feature is set to
-.Sy enabled ,
-the administrator can turn on the
-.Sy sha512
-checksum on any dataset using the
-.Dl # zfs set checksum=sha512 Ar dataset
-command.
-This feature becomes
-.Sy active
-once a
-.Sy checksum
-property has been set to
-.Sy sha512 ,
-and will return to being
-.Sy enabled
-once all filesystems that have ever had their checksum set to
-.Sy sha512
-are destroyed.
-.It Sy skein
-.Bl -column "READ\-ONLY COMPATIBLE" "org.illumos:skein"
-.It GUID Ta org.illumos:skein
-.It READ\-ONLY COMPATIBLE Ta no
-.It DEPENDENCIES Ta extensible_dataset
-.El
-.Pp
-The
-.Sy skein
-feature enables the use of the Skein hash algorithm for checksum and dedup.
-Skein is a high-performance secure hash algorithm that was a finalist in the
-NIST SHA-3 competition.
-It provides a very high security margin and high performance on 64-bit hardware
-.Pq 80% faster than SHA-256 .
-This implementation also utilizes the new salted checksumming functionality in
-ZFS, which means that the checksum is pre-seeded with a secret 256-bit random
-key
-.Pq stored on the pool
-before being fed the data block to be checksummed.
-Thus the produced checksums are unique to a given pool, preventing hash
-collision attacks on systems with dedup.
-.Pp
-When the
-.Sy skein
-feature is set to
-.Sy enabled ,
-the administrator can turn on the
-.Sy skein
-checksum on any dataset using the
-.Dl # zfs set checksum=skein Ar dataset
-command.
-This feature becomes
-.Sy active
-once a
-.Sy checksum
-property has been set to
-.Sy skein ,
-and will return to being
-.Sy enabled
-once all filesystems that have ever had their checksum set to
-.Sy skein
-are destroyed.
-Booting off of pools using
-.Sy skein
-is supported.
-.It Sy allocation_classes
-.Bl -column "READ\-ONLY COMPATIBLE" "com.intel:allocation_classes"
-.It GUID Ta com.intel:allocation_classes
-.It READ\-ONLY COMPATIBLE Ta yes
-.It DEPENDENCIES Ta none
-.El
-.Pp
-This feature enables support for separate allocation classes.
-.Pp
-This feature becomes
-.Sy active
-when a dedicated allocation class vdev
-(dedup or special) is created with
-.Dq zpool create
-or
-.Dq zpool add .
-With device removal, it can be returned to the
-.Sy enabled
-state if all the top-level vdevs from an allocation class are removed.
-.El
-.Sh SEE ALSO
-.Xr zpool 8
-.Sh AUTHORS
-This manual page is a
-.Xr mdoc 7
-reimplementation of the
-.Tn illumos
-manual page
-.Em zpool-features(5) ,
-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/zpool/zpool.8 b/cddl/contrib/opensolaris/cmd/zpool/zpool.8
deleted file mode 100644
index f5caffb95d799..0000000000000
--- a/cddl/contrib/opensolaris/cmd/zpool/zpool.8
+++ /dev/null
@@ -1,2485 +0,0 @@
-'\" te
-.\" Copyright (c) 2012, Martin Matuska <mm@FreeBSD.org>.
-.\" Copyright (c) 2013-2014, Xin Li <delphij@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, Justin T. Gibbs <gibbs@FreeBSD.org>
-.\" Copyright (c) 2012, Glen Barber <gjb@FreeBSD.org>
-.\" Copyright (c) 2012, 2017 by Delphix. All Rights Reserved.
-.\" Copyright 2017 Nexenta Systems, Inc.
-.\" Copyright (c) 2017 Datto Inc.
-.\" Copyright (c) 2017 George Melikov. All Rights Reserved.
-.\" Copyright 2019 Joyent, Inc.
-.\"
-.\" $FreeBSD$
-.\"
-.Dd February 25, 2020
-.Dt ZPOOL 8
-.Os
-.Sh NAME
-.Nm zpool
-.Nd configures ZFS storage pools
-.Sh SYNOPSIS
-.Nm
-.Op Fl \&?
-.Nm
-.Cm add
-.Op Fl fgLnP
-.Ar pool vdev ...
-.Nm
-.Cm attach
-.Op Fl f
-.Ar pool device new_device
-.Nm
-.Cm checkpoint
-.Op Fl d, -discard
-.Ar pool
-.Nm
-.Cm clear
-.Op Fl F Op Fl n
-.Ar pool
-.Op Ar device
-.Nm
-.Cm create
-.Op Fl fnd
-.Op Fl o Ar property Ns = Ns Ar value
-.Ar ...
-.Op Fl O Ar file-system-property Ns = Ns Ar value
-.Ar ...
-.Op Fl m Ar mountpoint
-.Op Fl R Ar root
-.Op Fl t Ar tempname
-.Ar pool vdev ...
-.Nm
-.Cm destroy
-.Op Fl f
-.Ar pool
-.Nm
-.Cm detach
-.Ar pool device
-.Nm
-.Cm export
-.Op Fl f
-.Ar pool ...
-.Nm
-.Cm get
-.Op Fl Hp
-.Op Fl o Ar field Ns Op , Ns Ar ...
-.Ar all | property Ns Op , Ns Ar ...
-.Ar pool ...
-.Nm
-.Cm history
-.Op Fl il
-.Op Ar pool
-.Ar ...
-.Nm
-.Cm import
-.Op Fl d Ar dir | Fl c Ar cachefile
-.Op Fl D
-.Nm
-.Cm import
-.Op Fl o Ar mntopts
-.Op Fl o Ar property Ns = Ns Ar value
-.Ar ...
-.Op Fl -rewind-to-checkpoint
-.Op Fl d Ar dir | Fl c Ar cachefile
-.Op Fl D
-.Op Fl f
-.Op Fl m
-.Op Fl N
-.Op Fl R Ar root
-.Op Fl F Op Fl n
-.Fl a
-.Nm
-.Cm import
-.Op Fl o Ar mntopts
-.Op Fl o Ar property Ns = Ns Ar value
-.Ar ...
-.Op Fl -rewind-to-checkpoint
-.Op Fl d Ar dir | Fl c Ar cachefile
-.Op Fl D
-.Op Fl f
-.Op Fl m
-.Op Fl N
-.Op Fl R Ar root
-.Op Fl t
-.Op Fl F Op Fl n
-.Ar pool | id
-.Op Ar newpool
-.Nm
-.Cm initialize
-.Op Fl cs
-.Ar pool
-.Op Ar device Ns ...
-.Nm
-.Cm iostat
-.Op Fl v
-.Op Fl T Cm d Ns | Ns Cm u
-.Op Fl gLP
-.Op Ar pool
-.Ar ...
-.Op Ar interval Op Ar count
-.Nm
-.Cm labelclear
-.Op Fl f
-.Ar device
-.Nm
-.Cm list
-.Op Fl HgLpPv
-.Op Fl o Ar property Ns Op , Ns Ar ...
-.Op Fl T Cm d Ns | Ns Cm u
-.Op Ar pool
-.Ar ...
-.Op Ar interval Op Ar count
-.Nm
-.Cm offline
-.Op Fl t
-.Ar pool device ...
-.Nm
-.Cm online
-.Op Fl e
-.Ar pool device ...
-.Nm
-.Cm reguid
-.Ar pool
-.Nm
-.Cm remove
-.Op Fl np
-.Ar pool device ...
-.Nm
-.Cm remove
-.Fl s
-.Ar pool
-.Nm
-.Cm reopen
-.Ar pool
-.Nm
-.Cm replace
-.Op Fl f
-.Ar pool device
-.Op Ar new_device
-.Nm
-.Cm scrub
-.Op Fl s | Fl p
-.Ar pool ...
-.Nm
-.Cm set
-.Ar property Ns = Ns Ar value pool
-.Nm
-.Cm split
-.Op Fl gLnP
-.Op Fl R Ar altroot
-.Op Fl o Ar mntopts
-.Op Fl o Ar property Ns = Ns Ar value
-.Ar pool newpool
-.Op Ar device ...
-.Nm
-.Cm status
-.Op Fl DgLPvx
-.Op Fl T Cm d Ns | Ns Cm u
-.Op Ar pool
-.Ar ...
-.Op Ar interval Op Ar count
-.Nm
-.Cm sync
-.Oo Ar pool Oc Ns ...
-.Nm
-.Cm upgrade
-.Op Fl v
-.Nm
-.Cm upgrade
-.Op Fl V Ar version
-.Fl a | Ar pool ...
-.Sh DESCRIPTION
-The
-.Nm
-command configures
-.Tn ZFS
-storage pools. A storage pool is a collection of devices that provides physical
-storage and data replication for
-.Tn ZFS
-datasets.
-.Pp
-All datasets within a storage pool share the same space. See
-.Xr zfs 8
-for information on managing datasets.
-.Ss Virtual Devices (vdevs)
-A
-.Qq virtual device
-.Pq No vdev
-describes a single device or a collection of devices organized according to
-certain performance and fault characteristics. The following virtual devices
-are supported:
-.Bl -tag -width "XXXXXX"
-.It Sy disk
-A block device, typically located under
-.Pa /dev .
-.Tn ZFS
-can use individual slices or partitions, though the recommended mode of
-operation is to use whole disks. A disk can be specified by a full path to the
-device or the
-.Xr geom 4
-provider name. When given a whole disk,
-.Tn ZFS
-automatically labels the disk, if necessary.
-.It Sy file
-A regular file. The use of files as a backing store is strongly discouraged. It
-is designed primarily for experimental purposes, as the fault tolerance of a
-file is only as good the file system of which it is a part. A file must be
-specified by a full path.
-.It Sy mirror
-A mirror of two or more devices. Data is replicated in an identical fashion
-across all components of a mirror. A mirror with
-.Em N
-disks of size
-.Em X
-can hold
-.Em X
-bytes and can withstand
-.Pq Em N-1
-devices failing before data integrity is compromised.
-.It Sy raidz
-(or
-.Sy raidz1 raidz2 raidz3 ) .
-A variation on
-.Sy RAID-5
-that allows for better distribution of parity and eliminates the
-.Qq Sy RAID-5
-write hole (in which data and parity become inconsistent after a power loss).
-Data and parity is striped across all disks within a
-.No raidz
-group.
-.Pp
-A
-.No raidz
-group can have single-, double- , or triple parity, meaning that the
-.No raidz
-group can sustain one, two, or three failures, respectively, without
-losing any data. The
-.Sy raidz1 No vdev
-type specifies a single-parity
-.No raidz
-group; the
-.Sy raidz2 No vdev
-type specifies a double-parity
-.No raidz
-group; and the
-.Sy raidz3 No vdev
-type specifies a triple-parity
-.No raidz
-group. The
-.Sy raidz No vdev
-type is an alias for
-.Sy raidz1 .
-.Pp
-A
-.No raidz
-group with
-.Em N
-disks of size
-.Em X
-with
-.Em P
-parity disks can hold approximately
-.Sm off
-.Pq Em N-P
-*X
-.Sm on
-bytes and can withstand
-.Em P
-device(s) failing before data integrity is compromised. The minimum number of
-devices in a
-.No raidz
-group is one more than the number of parity disks. The
-recommended number is between 3 and 9 to help increase performance.
-.It Sy spare
-A special
-.No pseudo- Ns No vdev
-which keeps track of available hot spares for a pool.
-For more information, see the
-.Qq Sx Hot Spares
-section.
-.It Sy log
-A separate-intent log device. If more than one log device is specified, then
-writes are load-balanced between devices. Log devices can be mirrored. However,
-.No raidz
-.No vdev
-types are not supported for the intent log. For more information,
-see the
-.Qq Sx Intent Log
-section.
-.It Sy dedup
-A device dedicated solely for allocating dedup data.
-The redundancy of this device should match the redundancy of the other normal
-devices in the pool.
-If more than one dedup device is specified, then allocations are load-balanced
-between devices.
-.It Sy special
-A device dedicated solely for allocating various kinds of internal metadata,
-and optionally small file data.
-The redundancy of this device should match the redundancy of the other normal
-devices in the pool.
-If more than one special device is specified, then allocations are
-load-balanced between devices.
-.Pp
-For more information on special allocations, see the
-.Sx Special Allocation Class
-section.
-.It Sy cache
-A device used to cache storage pool data.
-A cache device cannot be configured as a mirror or raidz group.
-For more information, see the
-.Qq Sx Cache Devices
-section.
-.El
-.Pp
-Virtual devices cannot be nested, so a mirror or
-.No raidz
-virtual device can only
-contain files or disks. Mirrors of mirrors (or other combinations) are not
-allowed.
-.Pp
-A pool can have any number of virtual devices at the top of the configuration
-(known as
-.Qq root
-.No vdev Ns s).
-Data is dynamically distributed across all top-level devices to balance data
-among devices. As new virtual devices are added,
-.Tn ZFS
-automatically places data on the newly available devices.
-.Pp
-Virtual devices are specified one at a time on the command line, separated by
-whitespace. The keywords
-.Qq mirror
-and
-.Qq raidz
-are used to distinguish where a group ends and another begins. For example, the
-following creates two root
-.No vdev Ns s,
-each a mirror of two disks:
-.Bd -literal -offset 2n
-.Li # Ic zpool create mypool mirror da0 da1 mirror da2 da3
-.Ed
-.Ss Device Failure and Recovery
-.Tn ZFS
-supports a rich set of mechanisms for handling device failure and data
-corruption. All metadata and data is checksummed, and
-.Tn ZFS
-automatically repairs bad data from a good copy when corruption is detected.
-.Pp
-In order to take advantage of these features, a pool must make use of some form
-of redundancy, using either mirrored or
-.No raidz
-groups. While
-.Tn ZFS
-supports running in a non-redundant configuration, where each root
-.No vdev
-is simply a disk or file, this is strongly discouraged. A single case of bit
-corruption can render some or all of your data unavailable.
-.Pp
-A pool's health status is described by one of three states: online, degraded,
-or faulted. An online pool has all devices operating normally. A degraded pool
-is one in which one or more devices have failed, but the data is still
-available due to a redundant configuration. A faulted pool has corrupted
-metadata, or one or more faulted devices, and insufficient replicas to continue
-functioning.
-.Pp
-The health of the top-level
-.No vdev ,
-such as mirror or
-.No raidz
-device, is
-potentially impacted by the state of its associated
-.No vdev Ns s,
-or component devices. A top-level
-.No vdev
-or component device is in one of the following states:
-.Bl -tag -width "DEGRADED"
-.It Sy DEGRADED
-One or more top-level
-.No vdev Ns s
-is in the degraded state because one or more
-component devices are offline. Sufficient replicas exist to continue
-functioning.
-.Pp
-One or more component devices is in the degraded or faulted state, but
-sufficient replicas exist to continue functioning. The underlying conditions
-are as follows:
-.Bl -bullet -offset 2n
-.It
-The number of checksum errors exceeds acceptable levels and the device is
-degraded as an indication that something may be wrong.
-.Tn ZFS
-continues to use the device as necessary.
-.It
-The number of
-.Tn I/O
-errors exceeds acceptable levels. The device could not be
-marked as faulted because there are insufficient replicas to continue
-functioning.
-.El
-.It Sy FAULTED
-One or more top-level
-.No vdev Ns s
-is in the faulted state because one or more
-component devices are offline. Insufficient replicas exist to continue
-functioning.
-.Pp
-One or more component devices is in the faulted state, and insufficient
-replicas exist to continue functioning. The underlying conditions are as
-follows:
-.Bl -bullet -offset 2n
-.It
-The device could be opened, but the contents did not match expected values.
-.It
-The number of
-.Tn I/O
-errors exceeds acceptable levels and the device is faulted to
-prevent further use of the device.
-.El
-.It Sy OFFLINE
-The device was explicitly taken offline by the
-.Qq Nm Cm offline
-command.
-.It Sy ONLINE
-The device is online and functioning.
-.It Sy REMOVED
-The device was physically removed while the system was running. Device removal
-detection is hardware-dependent and may not be supported on all platforms.
-.It Sy UNAVAIL
-The device could not be opened. If a pool is imported when a device was
-unavailable, then the device will be identified by a unique identifier instead
-of its path since the path was never correct in the first place.
-.El
-.Pp
-If a device is removed and later reattached to the system,
-.Tn ZFS
-attempts to put the device online automatically. Device attach detection is
-hardware-dependent and might not be supported on all platforms.
-.Ss Hot Spares
-.Tn ZFS
-allows devices to be associated with pools as
-.Qq hot spares .
-These devices are not actively used in the pool, but when an active device
-fails, it is automatically replaced by a hot spare. To create a pool with hot
-spares, specify a
-.Qq spare
-.No vdev
-with any number of devices. For example,
-.Bd -literal -offset 2n
-.Li # Ic zpool create pool mirror da0 da1 spare da2 da3
-.Ed
-.Pp
-Spares can be shared across multiple pools, and can be added with the
-.Qq Nm Cm add
-command and removed with the
-.Qq Nm Cm remove
-command. Once a spare replacement is initiated, a new "spare"
-.No vdev
-is created
-within the configuration that will remain there until the original device is
-replaced. At this point, the hot spare becomes available again if another
-device fails.
-.Pp
-If a pool has a shared spare that is currently being used, the pool can not be
-exported since other pools may use this shared spare, which may lead to
-potential data corruption.
-.Pp
-Shared spares add some risk.
-If the pools are imported on different hosts, and both pools suffer a device
-failure at the same time, both could attempt to use the spare at the same time.
-This may not be detected, resulting in data corruption.
-.Pp
-An in-progress spare replacement can be cancelled by detaching the hot spare.
-If the original faulted device is detached, then the hot spare assumes its
-place in the configuration, and is removed from the spare list of all active
-pools.
-.Pp
-Spares cannot replace log devices.
-.Pp
-This feature requires a userland helper.
-FreeBSD provides
-.Xr zfsd 8
-for this purpose.
-It must be manually enabled by adding
-.Va zfsd_enable="YES"
-to
-.Pa /etc/rc.conf .
-.Ss Intent Log
-The
-.Tn ZFS
-Intent Log
-.Pq Tn ZIL
-satisfies
-.Tn POSIX
-requirements for synchronous transactions. For instance, databases often
-require their transactions to be on stable storage devices when returning from
-a system call.
-.Tn NFS
-and other applications can also use
-.Xr fsync 2
-to ensure data stability. By default, the intent log is allocated from blocks
-within the main pool. However, it might be possible to get better performance
-using separate intent log devices such as
-.Tn NVRAM
-or a dedicated disk. For example:
-.Bd -literal -offset 2n
-.Li # Ic zpool create pool da0 da1 log da2
-.Ed
-.Pp
-Multiple log devices can also be specified, and they can be mirrored. See the
-.Sx EXAMPLES
-section for an example of mirroring multiple log devices.
-.Pp
-Log devices can be added, replaced, attached, detached, imported and exported
-as part of the larger pool.
-Mirrored devices can be removed by specifying the top-level mirror vdev.
-.Ss Cache devices
-Devices can be added to a storage pool as "cache devices." These devices
-provide an additional layer of caching between main memory and disk. For
-read-heavy workloads, where the working set size is much larger than what can
-be cached in main memory, using cache devices allow much more of this working
-set to be served from low latency media. Using cache devices provides the
-greatest performance improvement for random read-workloads of mostly static
-content.
-.Pp
-To create a pool with cache devices, specify a "cache"
-.No vdev
-with any number of devices. For example:
-.Bd -literal -offset 2n
-.Li # Ic zpool create pool da0 da1 cache da2 da3
-.Ed
-.Pp
-Cache devices cannot be mirrored or part of a
-.No raidz
-configuration. If a read
-error is encountered on a cache device, that read
-.Tn I/O
-is reissued to the original storage pool device, which might be part of a
-mirrored or
-.No raidz
-configuration.
-.Pp
-The content of the cache devices is considered volatile, as is the case with
-other system caches.
-.Ss Pool checkpoint
-Before starting critical procedures that include destructive actions (e.g
-.Nm zfs Cm destroy
-), an administrator can checkpoint the pool's state and in the case of a
-mistake or failure, rewind the entire pool back to the checkpoint.
-Otherwise, the checkpoint can be discarded when the procedure has completed
-successfully.
-.Pp
-A pool checkpoint can be thought of as a pool-wide snapshot and should be used
-with care as it contains every part of the pool's state, from properties to vdev
-configuration.
-Thus, while a pool has a checkpoint certain operations are not allowed.
-Specifically, vdev removal/attach/detach, mirror splitting, and
-changing the pool's guid.
-Adding a new vdev is supported but in the case of a rewind it will have to be
-added again.
-Finally, users of this feature should keep in mind that scrubs in a pool that
-has a checkpoint do not repair checkpointed data.
-.Pp
-To create a checkpoint for a pool:
-.Bd -literal
-# zpool checkpoint pool
-.Ed
-.Pp
-To later rewind to its checkpointed state, you need to first export it and
-then rewind it during import:
-.Bd -literal
-# zpool export pool
-# zpool import --rewind-to-checkpoint pool
-.Ed
-.Pp
-To discard the checkpoint from a pool:
-.Bd -literal
-# zpool checkpoint -d pool
-.Ed
-.Pp
-Dataset reservations (controlled by the
-.Nm reservation
-or
-.Nm refreservation
-zfs properties) may be unenforceable while a checkpoint exists, because the
-checkpoint is allowed to consume the dataset's reservation.
-Finally, data that is part of the checkpoint but has been freed in the
-current state of the pool won't be scanned during a scrub.
-.Ss Special Allocation Class
-The allocations in the special class are dedicated to specific block types.
-By default this includes all metadata, the indirect blocks of user data, and
-any dedup data.
-The class can also be provisioned to accept a limited percentage of small file
-data blocks.
-.Pp
-A pool must always have at least one general (non-specified) vdev before
-other devices can be assigned to the special class.
-If the special class becomes full, then allocations intended for it will spill
-back into the normal class.
-.Pp
-Dedup data can be excluded from the special class by setting the
-.Sy vfs.zfs.ddt_data_is_special
-sysctl to false (0).
-.Pp
-Inclusion of small file blocks in the special class is opt-in.
-Each dataset can control the size of small file blocks allowed in the special
-class by setting the
-.Sy special_small_blocks
-dataset property.
-It defaults to zero so you must opt-in by setting it to a non-zero value.
-See
-.Xr zfs 1M
-for more info on setting this property.
-.Ss Properties
-Each pool has several properties associated with it. Some properties are
-read-only statistics while others are configurable and change the behavior of
-the pool. The following are read-only properties:
-.Bl -tag -width "dedupratio"
-.It Sy allocated
-Amount of storage space used within the pool.
-.It Sy capacity
-Percentage of pool space used. This property can also be referred to by its
-shortened column name, "cap".
-.It Sy dedupratio
-The deduplication ratio specified for a pool, expressed as a multiplier.
-For example, a
-.Sy dedupratio
-value of 1.76 indicates that 1.76 units of data were stored but only 1 unit of disk space was actually consumed. See
-.Xr zfs 8
-for a description of the deduplication feature.
-.It Sy expandsize
-Amount of uninitialized space within the pool or device that can be used to
-increase the total capacity of the pool.
-Uninitialized space consists of
-any space on an EFI labeled vdev which has not been brought online
-.Pq i.e. zpool online -e .
-This space occurs when a LUN is dynamically expanded.
-.It Sy fragmentation
-The amount of fragmentation in the pool.
-.It Sy free
-Number of blocks within the pool that are not allocated.
-.It Sy freeing
-After a file system or snapshot is destroyed, the space it was using is
-returned to the pool asynchronously.
-.Sy freeing
-is the amount of space remaining to be reclaimed.
-Over time
-.Sy freeing
-will decrease while
-.Sy free
-increases.
-.It Sy guid
-A unique identifier for the pool.
-.It Sy health
-The current health of the pool. Health can be
-.Qq Sy ONLINE ,
-.Qq Sy DEGRADED ,
-.Qq Sy FAULTED ,
-.Qq Sy OFFLINE ,
-.Qq Sy REMOVED ,
-or
-.Qq Sy UNAVAIL .
-.It Sy size
-Total size of the storage pool.
-.It Sy unsupported@ Ns Ar feature_guid
-Information about unsupported features that are enabled on the pool.
-See
-.Xr zpool-features 7
-for details.
-.El
-.Pp
-The space usage properties report actual physical space available to the
-storage pool. The physical space can be different from the total amount of
-space that any contained datasets can actually use. The amount of space used in
-a
-.No raidz
-configuration depends on the characteristics of the data being written.
-In addition,
-.Tn ZFS
-reserves some space for internal accounting that the
-.Xr zfs 8
-command takes into account, but the
-.Xr zpool 8
-command does not. For non-full pools of a reasonable size, these effects should
-be invisible. For small pools, or pools that are close to being completely
-full, these discrepancies may become more noticeable.
-.Pp
-The following property can be set at creation time and import time:
-.Bl -tag -width 2n
-.It Sy altroot
-Alternate root directory. If set, this directory is prepended to any mount
-points within the pool. This can be used when examining an unknown pool where
-the mount points cannot be trusted, or in an alternate boot environment, where
-the typical paths are not valid.
-.Sy altroot
-is not a persistent property. It is valid only while the system is up.
-Setting
-.Sy altroot
-defaults to using
-.Cm cachefile=none ,
-though this may be overridden using an explicit setting.
-.El
-.Pp
-The following property can only be set at import time:
-.Bl -tag -width 2n
-.It Sy readonly Ns = Ns Cm on No | Cm off
-If set to
-.Cm on ,
-pool will be imported in read-only mode with the following restrictions:
-.Bl -bullet -offset 2n
-.It
-Synchronous data in the intent log will not be accessible
-.It
-Properties of the pool can not be changed
-.It
-Datasets of this pool can only be mounted read-only
-.It
-To write to a read-only pool, a export and import of the pool is required.
-.El
-.Pp
-This property can also be referred to by its shortened column name,
-.Sy rdonly .
-.El
-.Pp
-The following properties can be set at creation time and import time, and later
-changed with the
-.Ic zpool set
-command:
-.Bl -tag -width 2n
-.It Sy autoexpand Ns = Ns Cm on No | Cm off
-Controls automatic pool expansion when the underlying LUN is grown. If set to
-.Qq Cm on ,
-the pool will be resized according to the size of the expanded
-device. If the device is part of a mirror or
-.No raidz
-then all devices within that
-.No mirror/ Ns No raidz
-group must be expanded before the new space is made available to
-the pool. The default behavior is
-.Qq off .
-This property can also be referred to by its shortened column name,
-.Sy expand .
-.It Sy autoreplace Ns = Ns Cm on No | Cm off
-Controls automatic device replacement. If set to
-.Qq Cm off ,
-device replacement must be initiated by the administrator by using the
-.Qq Nm Cm replace
-command. If set to
-.Qq Cm on ,
-any new device, found in the same
-physical location as a device that previously belonged to the pool, is
-automatically formatted and replaced. The default behavior is
-.Qq Cm off .
-This property can also be referred to by its shortened column name, "replace".
-.It Sy bootfs Ns = Ns Ar pool Ns / Ns Ar dataset
-Identifies the default bootable dataset for the root pool. This property is
-expected to be set mainly by the installation and upgrade programs.
-.It Sy cachefile Ns = Ns Ar path No | Cm none
-Controls the location of where the pool configuration is cached. Discovering
-all pools on system startup requires a cached copy of the configuration data
-that is stored on the root file system. All pools in this cache are
-automatically imported when the system boots. Some environments, such as
-install and clustering, need to cache this information in a different location
-so that pools are not automatically imported. Setting this property caches the
-pool configuration in a different location that can later be imported with
-.Qq Nm Cm import Fl c .
-Setting it to the special value
-.Qq Cm none
-creates a temporary pool that is never cached, and the special value
-.Cm ''
-(empty string) uses the default location.
-.It Sy comment Ns = Ns Ar text
-A text string consisting of printable ASCII characters that will be stored
-such that it is available even if the pool becomes faulted.
-An administrator can provide additional information about a pool using this
-property.
-.It Sy dedupditto Ns = Ns Ar number
-Threshold for the number of block ditto copies. If the reference count for a
-deduplicated block increases above this number, a new ditto copy of this block
-is automatically stored. Default setting is
-.Cm 0
-which causes no ditto copies to be created for deduplicated blocks.
-The miniumum legal nonzero setting is 100.
-.It Sy delegation Ns = Ns Cm on No | Cm off
-Controls whether a non-privileged user is granted access based on the dataset
-permissions defined on the dataset. See
-.Xr zfs 8
-for more information on
-.Tn ZFS
-delegated administration.
-.It Sy failmode Ns = Ns Cm wait No | Cm continue No | Cm panic
-Controls the system behavior in the event of catastrophic pool failure. This
-condition is typically a result of a loss of connectivity to the underlying
-storage device(s) or a failure of all devices within the pool. The behavior of
-such an event is determined as follows:
-.Bl -tag -width indent
-.It Sy wait
-Blocks all
-.Tn I/O
-access until the device connectivity is recovered and the errors are cleared.
-This is the default behavior.
-.It Sy continue
-Returns
-.Em EIO
-to any new write
-.Tn I/O
-requests but allows reads to any of the remaining healthy devices. Any write
-requests that have yet to be committed to disk would be blocked.
-.It Sy panic
-Prints out a message to the console and generates a system crash dump.
-.El
-.It Sy feature@ Ns Ar feature_name Ns = Ns Sy enabled
-The value of this property is the current state of
-.Ar feature_name .
-The only valid value when setting this property is
-.Sy enabled
-which moves
-.Ar feature_name
-to the enabled state.
-See
-.Xr zpool-features 7
-for details on feature states.
-.It Sy listsnapshots Ns = Ns Cm on No | Cm off
-Controls whether information about snapshots associated with this pool is
-output when
-.Qq Nm zfs Cm list
-is run without the
-.Fl t
-option. The default value is
-.Cm off .
-This property can also be referred to by its shortened name,
-.Sy listsnaps .
-.It Sy multihost Ns = Ns Sy on No | Sy off
-Controls whether a pool activity check should be performed during
-.Nm zpool Cm import .
-When a pool is determined to be active it cannot be imported, even with the
-.Fl f
-option.
-This property is intended to be used in failover configurations
-where multiple hosts have access to a pool on shared storage.
-.Pp
-Multihost provides protection on import only.
-It does not protect against an
-individual device being used in multiple pools, regardless of the type of vdev.
-See the discussion under
-.Sy zpool create.
-.Pp
-When this property is on, periodic writes to storage occur to show the pool is
-in use.
-See
-.Sy vfs.zfs.multihost_interval
-sysctl.
-In order to enable this property each host must set a unique hostid.
-The default value is
-.Sy off .
-.It Sy version Ns = Ns Ar version
-The current on-disk version of the pool. This can be increased, but never
-decreased. The preferred method of updating pools is with the
-.Qq Nm Cm upgrade
-command, though this property can be used when a specific version is needed
-for backwards compatibility.
-Once feature flags is enabled on a pool this property will no longer have a
-value.
-.El
-.Sh SUBCOMMANDS
-All subcommands that modify state are logged persistently to the pool in their
-original form.
-.Pp
-The
-.Nm
-command provides subcommands to create and destroy storage pools, add capacity
-to storage pools, and provide information about the storage pools. The following
-subcommands are supported:
-.Bl -tag -width 2n
-.It Xo
-.Nm
-.Op Fl \&?
-.Xc
-.Pp
-Displays a help message.
-.It Xo
-.Nm
-.Cm add
-.Op Fl fgLnP
-.Ar pool vdev ...
-.Xc
-.Pp
-Adds the specified virtual devices to the given pool. The
-.No vdev
-specification is described in the
-.Qq Sx Virtual Devices
-section. The behavior of the
-.Fl f
-option, and the device checks performed are described in the
-.Qq Nm Cm create
-subcommand.
-.Bl -tag -width indent
-.It Fl f
-Forces use of
-.Ar vdev ,
-even if they appear in use or specify a conflicting replication level.
-Not all devices can be overridden in this manner.
-.It Fl g
-Display
-.Ar vdev ,
-GUIDs instead of the normal device names.
-These GUIDs can be used in place of
-device names for the zpool detach/offline/remove/replace commands.
-.It Fl L
-Display real paths for
-.Ar vdev Ns s
-resolving all symbolic links.
-This can be used to look up the current block
-device name regardless of the /dev/disk/ path used to open it.
-.It Fl n
-Displays the configuration that would be used without actually adding the
-.Ar vdev Ns s.
-The actual pool creation can still fail due to insufficient privileges or
-device sharing.
-.It Fl P
-Display real paths for
-.Ar vdev Ns s
-instead of only the last component of the path.
-This can be used in conjunction with the
-.Fl L
-flag.
-.El
-.It Xo
-.Nm
-.Cm attach
-.Op Fl f
-.Ar pool device new_device
-.Xc
-.Pp
-Attaches
-.Ar new_device
-to an existing
-.Sy zpool
-device. The existing device cannot be part of a
-.No raidz
-configuration. If
-.Ar device
-is not currently part of a mirrored configuration,
-.Ar device
-automatically transforms into a two-way mirror of
-.Ar device No and Ar new_device .
-If
-.Ar device
-is part of a two-way mirror, attaching
-.Ar new_device
-creates a three-way mirror, and so on. In either case,
-.Ar new_device
-begins to resilver immediately.
-.Bl -tag -width indent
-.It Fl f
-Forces use of
-.Ar new_device ,
-even if its appears to be in use. Not all devices can be overridden in this
-manner.
-.El
-.It Xo
-.Nm
-.Cm checkpoint
-.Op Fl d, -discard
-.Ar pool
-.Xc
-Checkpoints the current state of
-.Ar pool
-, which can be later restored by
-.Nm zpool Cm import --rewind-to-checkpoint .
-The existence of a checkpoint in a pool prohibits the following
-.Nm zpool
-commands:
-.Cm remove ,
-.Cm attach ,
-.Cm detach ,
-.Cm split ,
-and
-.Cm reguid .
-In addition, it may break reservation boundaries if the pool lacks free
-space.
-The
-.Nm zpool Cm status
-command indicates the existence of a checkpoint or the progress of discarding a
-checkpoint from a pool.
-The
-.Nm zpool Cm list
-command reports how much space the checkpoint takes from the pool.
-.Bl -tag -width Ds
-.It Fl d, -discard
-Discards an existing checkpoint from
-.Ar pool .
-.El
-.It Xo
-.Nm
-.Cm clear
-.Op Fl F Op Fl n
-.Ar pool
-.Op Ar device
-.Xc
-.Pp
-Clears device errors in a pool.
-If no arguments are specified, all device errors within the pool are cleared.
-If one or more devices is specified, only those errors associated with the
-specified device or devices are cleared.
-If multihost is enabled, and the pool has been suspended, this will not
-resume I/O.
-While the pool was suspended, it may have been imported on
-another host, and resuming I/O could result in pool damage.
-.Bl -tag -width indent
-.It Fl F
-Initiates recovery mode for an unopenable pool. Attempts to discard the last
-few transactions in the pool to return it to an openable state. Not all damaged
-pools can be recovered by using this option. If successful, the data from the
-discarded transactions is irretrievably lost.
-.It Fl n
-Used in combination with the
-.Fl F
-flag. Check whether discarding transactions would make the pool openable, but
-do not actually discard any transactions.
-.El
-.It Xo
-.Nm
-.Cm create
-.Op Fl fnd
-.Op Fl o Ar property Ns = Ns Ar value
-.Ar ...
-.Op Fl O Ar file-system-property Ns = Ns Ar value
-.Ar ...
-.Op Fl m Ar mountpoint
-.Op Fl R Ar root
-.Op Fl t Ar tempname
-.Ar pool vdev ...
-.Xc
-.Pp
-Creates a new storage pool containing the virtual devices specified on the
-command line. The pool name must begin with a letter, and can only contain
-alphanumeric characters as well as underscore ("_"), dash ("-"), and period
-("."). The pool names "mirror", "raidz", "spare" and "log" are reserved, as are
-names beginning with the pattern "c[0-9]". The
-.No vdev
-specification is described in the
-.Qq Sx Virtual Devices
-section.
-.Pp
-The command attempts to verify that each device specified is accessible and not
-currently in use by another subsystem.
-However this check is not robust enough
-to detect simultaneous attempts to use a new device in different pools, even if
-.Sy multihost
-is
-.Sy enabled.
-The
-administrator must ensure that simultaneous invocations of any combination of
-.Sy zpool replace ,
-.Sy zpool create ,
-.Sy zpool add ,
-or
-.Sy zpool labelclear ,
-do not refer to the same device.
-Using the same device in two pools will
-result in pool corruption.
-.Pp
-There are some uses, such as being currently mounted, or specified as the
-dedicated dump device, that prevents a device from ever being used by ZFS.
-Other uses, such as having a preexisting UFS file system, can be overridden
-with the
-.Fl f
-option.
-.Pp
-The command also checks that the replication strategy for the pool is
-consistent. An attempt to combine redundant and non-redundant storage in a
-single pool, or to mix disks and files, results in an error unless
-.Fl f
-is specified. The use of differently sized devices within a single
-.No raidz
-or mirror group is also flagged as an error unless
-.Fl f
-is specified.
-.Pp
-Unless the
-.Fl R
-option is specified, the default mount point is
-.Qq Pa /pool .
-The mount point must not exist or must be empty, or else the
-root dataset cannot be mounted. This can be overridden with the
-.Fl m
-option.
-.Pp
-By default all supported features are enabled on the new pool unless the
-.Fl d
-option is specified.
-.Bl -tag -width indent
-.It Fl f
-Forces use of
-.Ar vdev Ns s,
-even if they appear in use or specify a conflicting replication level.
-Not all devices can be overridden in this manner.
-.It Fl n
-Displays the configuration that would be used without actually creating the
-pool. The actual pool creation can still fail due to insufficient privileges or
-device sharing.
-.It Fl d
-Do not enable any features on the new pool.
-Individual features can be enabled by setting their corresponding properties
-to
-.Sy enabled
-with the
-.Fl o
-option.
-See
-.Xr zpool-features 7
-for details about feature properties.
-.It Xo
-.Fl o Ar property Ns = Ns Ar value
-.Op Fl o Ar property Ns = Ns Ar value
-.Ar ...
-.Xc
-Sets the given pool properties. See the
-.Qq Sx Properties
-section for a list of valid properties that can be set.
-.It Xo
-.Fl O
-.Ar file-system-property Ns = Ns Ar value
-.Op Fl O Ar file-system-property Ns = Ns Ar value
-.Ar ...
-.Xc
-Sets the given file system properties in the root file system of the pool. See
-.Xr zfs 8 Properties
-for a list of valid properties that
-can be set.
-.It Fl R Ar root
-Equivalent to
-.Qq Fl o Cm cachefile=none,altroot= Ns Pa root
-.It Fl m Ar mountpoint
-Sets the mount point for the root dataset. The default mount point is
-.Qq Pa /pool
-or
-.Qq Cm altroot Ns Pa /pool
-if
-.Sy altroot
-is specified. The mount point must be an absolute path,
-.Qq Cm legacy ,
-or
-.Qq Cm none .
-For more information on dataset mount points, see
-.Xr zfs 8 .
-.It Fl t Ar tempname
-Sets the in-core pool name to
-.Pa tempname
-while the on-disk name will be the name specified as the pool name
-.Pa pool .
-This will set the default
-.Sy cachefile
-property to
-.Sy none .
-This is intended to handle name space collisions when creating pools
-for other systems, such as virtual machines or physical machines
-whose pools live on network block devices.
-.El
-.It Xo
-.Nm
-.Cm destroy
-.Op Fl f
-.Ar pool
-.Xc
-.Pp
-Destroys the given pool, freeing up any devices for other use. This command
-tries to unmount any active datasets before destroying the pool.
-.Bl -tag -width indent
-.It Fl f
-Forces any active datasets contained within the pool to be unmounted.
-.El
-.It Xo
-.Nm
-.Cm detach
-.Ar pool device
-.Xc
-.Pp
-Detaches
-.Ar device
-from a mirror. The operation is refused if there are no other valid replicas
-of the data.
-.It Xo
-.Nm
-.Cm export
-.Op Fl f
-.Ar pool ...
-.Xc
-.Pp
-Exports the given pools from the system. All devices are marked as exported,
-but are still considered in use by other subsystems. The devices can be moved
-between systems (even those of different endianness) and imported as long as a
-sufficient number of devices are present.
-.Pp
-Before exporting the pool, all datasets within the pool are unmounted. A pool
-can not be exported if it has a shared spare that is currently being used.
-.Pp
-For pools to be portable, you must give the
-.Nm
-command whole disks, not just slices, so that
-.Tn ZFS
-can label the disks with portable
-.Sy EFI
-labels. Otherwise, disk drivers on platforms of different endianness will not
-recognize the disks.
-.Bl -tag -width indent
-.It Fl f
-Forcefully unmount all datasets, using the
-.Qq Nm unmount Fl f
-command.
-.Pp
-This command will forcefully export the pool even if it has a shared spare that
-is currently being used. This may lead to potential data corruption.
-.El
-.It Xo
-.Nm
-.Cm get
-.Op Fl Hp
-.Op Fl o Ar field Ns Op , Ns Ar ...
-.Ar all | property Ns Op , Ns Ar ...
-.Ar pool ...
-.Xc
-.Pp
-Retrieves the given list of properties (or all properties if
-.Qq Cm all
-is used) for the specified storage pool(s). These properties are displayed with
-the following fields:
-.Bl -column -offset indent "property"
-.It name Ta Name of storage pool
-.It property Ta Property name
-.It value Ta Property value
-.It source Ta Property source, either 'default' or 'local'.
-.El
-.Pp
-See the
-.Qq Sx Properties
-section for more information on the available pool properties.
-.It Fl H
-Scripted mode. Do not display headers, and separate fields by a single tab
-instead of arbitrary space.
-.It Fl p
-Display numbers in parsable (exact) values.
-.It Fl o Ar field
-A comma-separated list of columns to display.
-.Sy name Ns , Ns
-.Sy property Ns , Ns
-.Sy value Ns , Ns
-.Sy source
-is the default value.
-.It Xo
-.Nm
-.Cm history
-.Op Fl il
-.Op Ar pool
-.Ar ...
-.Xc
-.Pp
-Displays the command history of the specified pools or all pools if no pool is
-specified.
-.Bl -tag -width indent
-.It Fl i
-Displays internally logged
-.Tn ZFS
-events in addition to user initiated events.
-.It Fl l
-Displays log records in long format, which in addition to standard format
-includes, the user name, the hostname, and the zone in which the operation was
-performed.
-.El
-.It Xo
-.Nm
-.Cm import
-.Op Fl d Ar dir | Fl c Ar cachefile
-.Op Fl D
-.Xc
-.Pp
-Lists pools available to import. If the
-.Fl d
-option is not specified, this command searches for devices in
-.Qq Pa /dev .
-The
-.Fl d
-option can be specified multiple times, and all directories are searched. If
-the device appears to be part of an exported pool, this command displays a
-summary of the pool with the name of the pool, a numeric identifier, as well as
-the
-.No vdev
-layout and current health of the device for each device or file.
-Destroyed pools, pools that were previously destroyed with the
-.Qq Nm Cm destroy
-command, are not listed unless the
-.Fl D
-option is specified.
-.Pp
-The numeric identifier is unique, and can be used instead of the pool name when
-multiple exported pools of the same name are available.
-.Bl -tag -width indent
-.It Fl c Ar cachefile
-Reads configuration from the given
-.Ar cachefile
-that was created with the
-.Qq Sy cachefile
-pool property. This
-.Ar cachefile
-is used instead of searching for devices.
-.It Fl d Ar dir
-Searches for devices or files in
-.Ar dir .
-The
-.Fl d
-option can be specified multiple times.
-.It Fl D
-Lists destroyed pools only.
-.El
-.It Xo
-.Nm
-.Cm import
-.Op Fl o Ar mntopts
-.Op Fl o Ar property Ns = Ns Ar value
-.Ar ...
-.Op Fl d Ar dir | Fl c Ar cachefile
-.Op Fl D
-.Op Fl f
-.Op Fl m
-.Op Fl N
-.Op Fl R Ar root
-.Op Fl F Op Fl n
-.Fl a
-.Xc
-.Pp
-Imports all pools found in the search directories. Identical to the previous
-command, except that all pools with a sufficient number of devices available
-are imported. Destroyed pools, pools that were previously destroyed with the
-.Qq Nm Cm destroy
-command, will not be imported unless the
-.Fl D
-option is specified.
-.Bl -tag -width indent
-.It Fl o Ar mntopts
-Comma-separated list of mount options to use when mounting datasets within the
-pool. See
-.Xr zfs 8
-for a description of dataset properties and mount options.
-.It Fl o Ar property Ns = Ns Ar value
-Sets the specified property on the imported pool. See the
-.Qq Sx Properties
-section for more information on the available pool properties.
-.It Fl c Ar cachefile
-Reads configuration from the given
-.Ar cachefile
-that was created with the
-.Qq Sy cachefile
-pool property. This
-.Ar cachefile
-is used instead of searching for devices.
-.It Fl d Ar dir
-Searches for devices or files in
-.Ar dir .
-The
-.Fl d
-option can be specified multiple times. This option is incompatible with the
-.Fl c
-option.
-.It Fl D
-Imports destroyed pools only. The
-.Fl f
-option is also required.
-.It Fl f
-Forces import, even if the pool appears to be potentially active.
-.It Fl m
-Allows a pool to import when there is a missing log device. Recent transactions
-can be lost because the log device will be discarded.
-.It Fl N
-Import the pool without mounting any file systems.
-.It Fl R Ar root
-Sets the
-.Qq Sy cachefile
-property to
-.Qq Cm none
-and the
-.Qq Sy altroot
-property to
-.Qq Ar root
-.It Fl F
-Recovery mode for a non-importable pool. Attempt to return the pool to an
-importable state by discarding the last few transactions. Not all damaged pools
-can be recovered by using this option. If successful, the data from the
-discarded transactions is irretrievably lost. This option is ignored if the
-pool is importable or already imported.
-.It Fl n
-Used with the
-.Fl F
-recovery option. Determines whether a non-importable pool can be made
-importable again, but does not actually perform the pool recovery. For more
-details about pool recovery mode, see the
-.Fl F
-option, above.
-.It Fl a
-Searches for and imports all pools found.
-.El
-.It Xo
-.Nm
-.Cm import
-.Op Fl o Ar mntopts
-.Op Fl o Ar property Ns = Ns Ar value
-.Ar ...
-.Op Fl d Ar dir | Fl c Ar cachefile
-.Op Fl D
-.Op Fl f
-.Op Fl m
-.Op Fl N
-.Op Fl R Ar root
-.Op Fl t
-.Op Fl F Op Fl n
-.Ar pool | id
-.Op Ar newpool
-.Xc
-.Pp
-Imports a specific pool. A pool can be identified by its name or the numeric
-identifier. If
-.Ar newpool
-is specified, the pool is imported using the name
-.Ar newpool .
-Otherwise, it is imported with the same name as its exported name.
-.Pp
-If a device is removed from a system without running
-.Qq Nm Cm export
-first, the device appears as potentially active. It cannot be determined if
-this was a failed export, or whether the device is really in use from another
-host. To import a pool in this state, the
-.Fl f
-option is required.
-.Bl -tag -width indent
-.It Fl o Ar mntopts
-Comma-separated list of mount options to use when mounting datasets within the
-pool. See
-.Xr zfs 8
-for a description of dataset properties and mount options.
-.It Fl o Ar property Ns = Ns Ar value
-Sets the specified property on the imported pool. See the
-.Qq Sx Properties
-section for more information on the available pool properties.
-.It Fl c Ar cachefile
-Reads configuration from the given
-.Ar cachefile
-that was created with the
-.Qq Sy cachefile
-pool property. This
-.Ar cachefile
-is used instead of searching for devices.
-.It Fl d Ar dir
-Searches for devices or files in
-.Ar dir .
-The
-.Fl d
-option can be specified multiple times. This option is incompatible with the
-.Fl c
-option.
-.It Fl D
-Imports destroyed pools only. The
-.Fl f
-option is also required.
-.It Fl f
-Forces import, even if the pool appears to be potentially active.
-.It Fl m
-Allows a pool to import when there is a missing log device. Recent transactions
-can be lost because the log device will be discarded.
-.It Fl N
-Import the pool without mounting any file systems.
-.It Fl R Ar root
-Equivalent to
-.Qq Fl o Cm cachefile=none,altroot= Ns Pa root
-.It Fl t
-Used with
-.Ar newpool .
-Specifies that
-.Ar newpool
-is temporary.
-Temporary pool names last until export.
-Ensures that the original pool name will be used in all label updates and
-therefore is retained upon export.
-Will also set
-.Sy cachefile
-property to
-.Sy none
-when not explicitly specified.
-.It Fl F
-Recovery mode for a non-importable pool. Attempt to return the pool to an
-importable state by discarding the last few transactions. Not all damaged pools
-can be recovered by using this option. If successful, the data from the
-discarded transactions is irretrievably lost. This option is ignored if the
-pool is importable or already imported.
-.It Fl n
-Used with the
-.Fl F
-recovery option. Determines whether a non-importable pool can be made
-importable again, but does not actually perform the pool recovery. For more
-details about pool recovery mode, see the
-.Fl F
-option, above.
-.It Fl -rewind-to-checkpoint
-Rewinds pool to the checkpointed state.
-Once the pool is imported with this flag there is no way to undo the rewind.
-All changes and data that were written after the checkpoint are lost!
-The only exception is when the
-.Sy readonly
-mounting option is enabled.
-In this case, the checkpointed state of the pool is opened and an
-administrator can see how the pool would look like if they were
-to fully rewind.
-.El
-.It Xo
-.Nm
-.Cm initialize
-.Op Fl cs
-.Ar pool
-.Op Ar device Ns ...
-.Xc
-Begins initializing by writing to all unallocated regions on the specified
-devices, or all eligible devices in the pool if no individual devices are
-specified.
-Only leaf data or log devices may be initialized.
-.Bl -tag -width Ds
-.It Fl c, -cancel
-Cancel initializing on the specified devices, or all eligible devices if none
-are specified.
-If one or more target devices are invalid or are not currently being
-initialized, the command will fail and no cancellation will occur on any device.
-.It Fl s -suspend
-Suspend initializing on the specified devices, or all eligible devices if none
-are specified.
-If one or more target devices are invalid or are not currently being
-initialized, the command will fail and no suspension will occur on any device.
-Initializing can then be resumed by running
-.Nm zpool Cm initialize
-with no flags on the relevant target devices.
-.El
-.It Xo
-.Nm
-.Cm iostat
-.Op Fl T Cm d Ns | Ns Cm u
-.Op Fl gLPv
-.Op Ar pool
-.Ar ...
-.Op Ar interval Op Ar count
-.Xc
-.Pp
-Displays
-.Tn I/O
-statistics for the given pools. When given an interval, the statistics are
-printed every
-.Ar interval
-seconds until
-.Sy Ctrl-C
-is pressed. If no
-.Ar pools
-are specified, statistics for every pool in the system is shown. If
-.Ar count
-is specified, the command exits after
-.Ar count
-reports are printed.
-.Bl -tag -width indent
-.It Fl T Cm d Ns | Ns Cm u
-Print a timestamp.
-.Pp
-Use modifier
-.Cm d
-for standard date format. See
-.Xr date 1 .
-Use modifier
-.Cm u
-for unixtime
-.Pq equals Qq Ic date +%s .
-.It Fl g
-Display vdev GUIDs instead of the normal device names.
-These GUIDs can be used in place of device names for the zpool
-detach/offline/remove/replace commands.
-.It Fl L
-Display real paths for vdevs resolving all symbolic links.
-This can be used to look up the current block device name regardless of the
-.Pa /dev/disk/
-path used to open it.
-.It Fl P
-Display full paths for vdevs instead of only the last component of
-the path.
-This can be used in conjunction with the
-.Fl L
-flag.
-.It Fl v
-Verbose statistics.
-Reports usage statistics for individual vdevs within the
-pool, in addition to the pool-wide statistics.
-.El
-.It Xo
-.Nm
-.Cm labelclear
-.Op Fl f
-.Ar device
-.Xc
-.Pp
-Removes
-.Tn ZFS
-label information from the specified
-.Ar device .
-The
-.Ar device
-must not be part of an active pool configuration.
-.Bl -tag -width indent
-.It Fl f
-Treat exported or foreign devices as inactive.
-.El
-.It Xo
-.Nm
-.Cm list
-.Op Fl HgLpPv
-.Op Fl o Ar property Ns Op , Ns Ar ...
-.Op Fl T Cm d Ns | Ns Cm u
-.Op Ar pool
-.Ar ...
-.Op Ar interval Op Ar count
-.Xc
-.Pp
-Lists the given pools along with a health status and space usage. If no
-.Ar pools
-are specified, all pools in the system are listed.
-.Pp
-When given an interval, the output is printed every
-.Ar interval
-seconds until
-.Sy Ctrl-C
-is pressed. If
-.Ar count
-is specified, the command exits after
-.Ar count
-reports are printed.
-.Bl -tag -width indent
-.It Fl T Cm d Ns | Ns Cm u
-Print a timestamp.
-.Pp
-Use modifier
-.Cm d
-for standard date format. See
-.Xr date 1 .
-Use modifier
-.Cm u
-for unixtime
-.Pq equals Qq Ic date +%s .
-.It Fl g
-Display vdev GUIDs instead of the normal device names.
-These GUIDs can be used in place of device names for the zpool
-detach/offline/remove/replace commands.
-.It Fl H
-Scripted mode. Do not display headers, and separate fields by a single tab
-instead of arbitrary space.
-.It Fl L
-Display real paths for vdevs resolving all symbolic links.
-This can be used to look up the current block device name regardless of the
-/dev/disk/ path used to open it.
-.It Fl p
-Display numbers in parsable
-.Pq exact
-values.
-.It Fl P
-Display full paths for vdevs instead of only the last component of
-the path.
-This can be used in conjunction with the
-.Fl L
-flag.
-.It Fl v
-Verbose statistics. Reports usage statistics for individual
-.Em vdevs
-within
-the pool, in addition to the pool-wide statistics.
-.It Fl o Ar property Ns Op , Ns Ar ...
-Comma-separated list of properties to display. See the
-.Qq Sx Properties
-section for a list of valid properties. The default list is
-.Sy name ,
-.Sy size ,
-.Sy allocated ,
-.Sy free ,
-.Sy checkpoint ,
-.Sy expandsize ,
-.Sy fragmentation ,
-.Sy capacity ,
-.Sy dedupratio ,
-.Sy health ,
-.Sy altroot .
-.It Fl T Cm d Ns | Ns Cm u
-Print a timestamp.
-.Pp
-Use modifier
-.Cm d
-for standard date format. See
-.Xr date 1 .
-Use modifier
-.Cm u
-for unixtime
-.Pq equals Qq Ic date +%s .
-.El
-.It Xo
-.Nm
-.Cm offline
-.Op Fl t
-.Ar pool device ...
-.Xc
-.Pp
-Takes the specified physical device offline. While the
-.Ar device
-is offline, no attempt is made to read or write to the device.
-.Bl -tag -width indent
-.It Fl t
-Temporary. Upon reboot, the specified physical device reverts to its previous
-state.
-.El
-.It Xo
-.Nm
-.Cm online
-.Op Fl e
-.Ar pool device ...
-.Xc
-.Pp
-Brings the specified physical device online.
-.Pp
-This command is not applicable to spares or cache devices.
-.Bl -tag -width indent
-.It Fl e
-Expand the device to use all available space. If the device is part of a mirror
-or
-.No raidz
-then all devices must be expanded before the new space will become
-available to the pool.
-.El
-.It Xo
-.Nm
-.Cm reguid
-.Ar pool
-.Xc
-.Pp
-Generates a new unique identifier for the pool. You must ensure that all
-devices in this pool are online and healthy before performing this action.
-.It Xo
-.Nm
-.Cm remove
-.Op Fl np
-.Ar pool device ...
-.Xc
-.Pp
-Removes the specified device from the pool.
-This command currently only supports removing hot spares, cache, log
-devices and mirrored top-level vdevs (mirror of leaf devices); but not raidz.
-.Pp
-Removing a top-level vdev reduces the total amount of space in the storage pool.
-The specified device will be evacuated by copying all allocated space from it to
-the other devices in the pool.
-In this case, the
-.Nm zpool Cm remove
-command initiates the removal and returns, while the evacuation continues in
-the background.
-The removal progress can be monitored with
-.Nm zpool Cm status.
-This feature must be enabled to be used, see
-.Xr zpool-features 7
-.Pp
-A mirrored top-level device (log or data) can be removed by specifying the
-top-level mirror for the same.
-Non-log devices or data devices that are part of a mirrored configuration can
-be removed using the
-.Qq Nm Cm detach
-command.
-.Bl -tag -width Ds
-.It Fl n
-Do not actually perform the removal ("no-op").
-Instead, print the estimated amount of memory that will be used by the
-mapping table after the removal completes.
-This is nonzero only for top-level vdevs.
-.El
-.Bl -tag -width Ds
-.It Fl p
-Used in conjunction with the
-.Fl n
-flag, displays numbers as parsable (exact) values.
-.El
-.It Xo
-.Nm
-.Cm remove
-.Fl s
-.Ar pool
-.Xc
-.Pp
-Stops and cancels an in-progress removal of a top-level vdev.
-.It Xo
-.Nm
-.Cm reopen
-.Ar pool
-.Xc
-.Pp
-Reopen all the vdevs associated with the pool.
-.It Xo
-.Nm
-.Cm replace
-.Op Fl f
-.Ar pool device
-.Op Ar new_device
-.Xc
-.Pp
-Replaces
-.Ar old_device
-with
-.Ar new_device .
-This is equivalent to attaching
-.Ar new_device ,
-waiting for it to resilver, and then detaching
-.Ar old_device .
-.Pp
-The size of
-.Ar new_device
-must be greater than or equal to the minimum size
-of all the devices in a mirror or
-.No raidz
-configuration.
-.Pp
-.Ar new_device
-is required if the pool is not redundant. If
-.Ar new_device
-is not specified, it defaults to
-.Ar old_device .
-This form of replacement is useful after an existing disk has failed and has
-been physically replaced. In this case, the new disk may have the same
-.Pa /dev
-path as the old device, even though it is actually a different disk.
-.Tn ZFS
-recognizes this.
-.Bl -tag -width indent
-.It Fl f
-Forces use of
-.Ar new_device ,
-even if its appears to be in use. Not all devices can be overridden in this
-manner.
-.El
-.It Xo
-.Nm
-.Cm scrub
-.Op Fl s | Fl p
-.Ar pool ...
-.Xc
-.Pp
-Begins a scrub or resumes a paused scrub.
-The scrub examines all data in the specified pools to verify that it checksums
-correctly.
-For replicated
-.Pq mirror or raidz
-devices, ZFS automatically repairs any damage discovered during the scrub.
-The
-.Nm zpool Cm status
-command reports the progress of the scrub and summarizes the results of the
-scrub upon completion.
-.Pp
-Scrubbing and resilvering are very similar operations.
-The difference is that resilvering only examines data that ZFS knows to be out
-of date
-.Po
-for example, when attaching a new device to a mirror or replacing an existing
-device
-.Pc ,
-whereas scrubbing examines all data to discover silent errors due to hardware
-faults or disk failure.
-.Pp
-Because scrubbing and resilvering are I/O-intensive operations, ZFS only allows
-one at a time.
-If a scrub is paused, the
-.Nm zpool Cm scrub
-resumes it.
-If a resilver is in progress, ZFS does not allow a scrub to be started until the
-resilver completes.
-.Bl -tag -width Ds
-.It Fl s
-Stop scrubbing.
-.El
-.Bl -tag -width Ds
-.It Fl p
-Pause scrubbing.
-Scrub pause state and progress are periodically synced to disk.
-If the system is restarted or pool is exported during a paused scrub,
-even after import, scrub will remain paused until it is resumed.
-Once resumed the scrub will pick up from the place where it was last
-checkpointed to disk.
-To resume a paused scrub issue
-.Nm zpool Cm scrub
-again.
-.El
-.It Xo
-.Nm
-.Cm set
-.Ar property Ns = Ns Ar value pool
-.Xc
-.Pp
-Sets the given property on the specified pool. See the
-.Qq Sx Properties
-section for more information on what properties can be set and acceptable
-values.
-.It Xo
-.Nm
-.Cm split
-.Op Fl gLnP
-.Op Fl R Ar altroot
-.Op Fl o Ar mntopts
-.Op Fl o Ar property Ns = Ns Ar value
-.Ar pool newpool
-.Op Ar device ...
-.Xc
-.Pp
-Splits off one disk from each mirrored top-level
-.No vdev
-in a pool and creates a new pool from the split-off disks. The original pool
-must be made up of one or more mirrors and must not be in the process of
-resilvering. The
-.Cm split
-subcommand chooses the last device in each mirror
-.No vdev
-unless overridden by a device specification on the command line.
-.Pp
-When using a
-.Ar device
-argument,
-.Cm split
-includes the specified device(s) in a new pool and, should any devices remain
-unspecified, assigns the last device in each mirror
-.No vdev
-to that pool, as it does normally. If you are uncertain about the outcome of a
-.Cm split
-command, use the
-.Fl n
-("dry-run") option to ensure your command will have the effect you intend.
-.Bl -tag -width indent
-.It Fl R Ar altroot
-Automatically import the newly created pool after splitting, using the
-specified
-.Ar altroot
-parameter for the new pool's alternate root. See the
-.Sy altroot
-description in the
-.Qq Sx Properties
-section, above.
-.It Fl g
-Display vdev GUIDs instead of the normal device names.
-These GUIDs can be used in place of device names for the zpool
-detach/offline/remove/replace commands.
-.It Fl L
-Display real paths for vdevs resolving all symbolic links.
-This can be used to look up the current block device name regardless of the
-.Pa /dev/disk/
-path used to open it.
-.It Fl n
-Displays the configuration that would be created without actually splitting the
-pool. The actual pool split could still fail due to insufficient privileges or
-device status.
-.It Fl o Ar mntopts
-Comma-separated list of mount options to use when mounting datasets within the
-pool. See
-.Xr zfs 8
-for a description of dataset properties and mount options. Valid only in
-conjunction with the
-.Fl R
-option.
-.It Fl o Ar property Ns = Ns Ar value
-Sets the specified property on the new pool. See the
-.Qq Sx Properties
-section, above, for more information on the available pool properties.
-.It Fl P
-Display full paths for vdevs instead of only the last component of
-the path.
-This can be used in conjunction with the
-.Fl L
-flag.
-.El
-.It Xo
-.Nm
-.Cm status
-.Op Fl DgLPvx
-.Op Fl T Cm d Ns | Ns Cm u
-.Op Ar pool
-.Ar ...
-.Op Ar interval Op Ar count
-.Xc
-.Pp
-Displays the detailed health status for the given pools. If no
-.Ar pool
-is specified, then the status of each pool in the system is displayed. For more
-information on pool and device health, see the
-.Qq Sx Device Failure and Recovery
-section.
-.Pp
-When given an interval, the output is printed every
-.Ar interval
-seconds until
-.Sy Ctrl-C
-is pressed. If
-.Ar count
-is specified, the command exits after
-.Ar count
-reports are printed.
-.Pp
-If a scrub or resilver is in progress, this command reports the percentage
-done and the estimated time to completion. Both of these are only approximate,
-because the amount of data in the pool and the other workloads on the system
-can change.
-.Bl -tag -width indent
-.It Fl D
-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 g
-Display vdev GUIDs instead of the normal device names.
-These GUIDs can be used in place of device names for the zpool
-detach/offline/remove/replace commands.
-.It Fl L
-Display real paths for vdevs resolving all symbolic links.
-This can be used to look up the current block device name regardless of the
-.Pa /dev/disk/
-path used to open it.
-.It Fl P
-Display full paths for vdevs instead of only the last component of
-the path.
-This can be used in conjunction with the
-.Fl L
-flag.
-.It Fl T Cm d Ns | Ns Cm u
-Print a timestamp.
-.Pp
-Use modifier
-.Cm d
-for standard date format. See
-.Xr date 1 .
-Use modifier
-.Cm u
-for unixtime
-.Pq equals Qq Ic date +%s .
-.It Fl v
-Displays verbose data error information, printing out a complete list of all
-data errors since the last complete pool scrub.
-.It Fl x
-Only display status for pools that are exhibiting errors or are otherwise
-unavailable.
-Warnings about pools not using the latest on-disk format, having non-native
-block size or disabled features will not be included.
-.El
-.It Xo
-.Nm
-.Cm sync
-.Oo Ar pool Oc Ns ...
-.Xc
-Forces all in-core dirty data to be written to the primary pool storage and
-not the ZIL.
-It will also update administrative information including quota reporting.
-Without arguments,
-.Nm zpool Cm sync
-will sync all pools on the system.
-Otherwise, it will only sync the specified
-.Ar pool .
-.It Xo
-.Nm
-.Cm upgrade
-.Op Fl v
-.Xc
-.Pp
-Displays pools which do not have all supported features enabled and pools
-formatted using a legacy
-.Tn ZFS
-version number.
-These pools can continue to be used, but some features may not be available.
-Use
-.Nm Cm upgrade Fl a
-to enable all features on all pools.
-.Bl -tag -width indent
-.It Fl v
-Displays legacy
-.Tn ZFS
-versions supported by the current software.
-See
-.Xr zpool-features 7
-for a description of feature flags features supported by the current software.
-.El
-.It Xo
-.Nm
-.Cm upgrade
-.Op Fl V Ar version
-.Fl a | Ar pool ...
-.Xc
-.Pp
-Enables all supported features on the given pool.
-Once this is done, the pool will no longer be accessible on systems that do
-not support feature flags.
-See
-.Xr zpool-features 7
-for details on compatibility with systems that support feature flags, but do
-not support all features enabled on the pool.
-.Bl -tag -width indent
-.It Fl a
-Enables all supported features on all pools.
-.It Fl V Ar version
-Upgrade to the specified legacy version. If the
-.Fl V
-flag is specified, no features will be enabled on the pool.
-This option can only be used to increase version number up to the last
-supported legacy version number.
-.El
-.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 ENVIRONMENT VARIABLES
-.Bl -tag -width "ZPOOL_VDEV_NAME_FOLLOW_LINKS"
-.It Ev ZPOOL_VDEV_NAME_GUID
-Cause
-.Nm zpool
-subcommands to output vdev guids by default.
-This behavior is identical to the
-.Nm zpool status -g
-command line option.
-.It Ev ZPOOL_VDEV_NAME_FOLLOW_LINKS
-Cause
-.Nm zpool
-subcommands to follow links for vdev names by default.
-This behavior is identical to the
-.Nm zpool status -L
-command line option.
-.It Ev ZPOOL_VDEV_NAME_PATH
-Cause
-.Nm zpool
-subcommands to output full vdev path names by default.
-This behavior is identical to the
-.Nm zpool status -P
-command line option.
-.El
-.Sh EXAMPLES
-.Bl -tag -width 0n
-.It Sy Example 1 No Creating a RAID-Z Storage Pool
-.Pp
-The following command creates a pool with a single
-.No raidz
-root
-.No vdev
-that consists of six disks.
-.Bd -literal -offset 2n
-.Li # Ic zpool create tank raidz da0 da1 da2 da3 da4 da5
-.Ed
-.It Sy Example 2 No Creating a Mirrored Storage Pool
-.Pp
-The following command creates a pool with two mirrors, where each mirror
-contains two disks.
-.Bd -literal -offset 2n
-.Li # Ic zpool create tank mirror da0 da1 mirror da2 da3
-.Ed
-.It Sy Example 3 No Creating a Tn ZFS No Storage Pool by Using Partitions
-.Pp
-The following command creates an unmirrored pool using two GPT partitions.
-.Bd -literal -offset 2n
-.Li # Ic zpool create tank da0p3 da1p3
-.Ed
-.It Sy Example 4 No Creating a Tn ZFS No Storage Pool by Using Files
-.Pp
-The following command creates an unmirrored pool using files. While not
-recommended, a pool based on files can be useful for experimental purposes.
-.Bd -literal -offset 2n
-.Li # Ic zpool create tank /path/to/file/a /path/to/file/b
-.Ed
-.It Sy Example 5 No Adding a Mirror to a Tn ZFS No Storage Pool
-.Pp
-The following command adds two mirrored disks to the pool
-.Em tank ,
-assuming the pool is already made up of two-way mirrors. The additional space
-is immediately available to any datasets within the pool.
-.Bd -literal -offset 2n
-.Li # Ic zpool add tank mirror da2 da3
-.Ed
-.It Sy Example 6 No Listing Available Tn ZFS No Storage Pools
-.Pp
-The following command lists all available pools on the system.
-.Bd -literal -offset 2n
-.Li # Ic zpool list
-NAME SIZE ALLOC FREE FRAG EXPANDSZ CAP DEDUP HEALTH ALTROOT
-pool 2.70T 473G 2.24T 33% - 17% 1.00x ONLINE -
-test 1.98G 89.5K 1.98G 48% - 0% 1.00x ONLINE -
-.Ed
-.It Sy Example 7 No Listing All Properties for a Pool
-.Pp
-The following command lists all the properties for a pool.
-.Bd -literal -offset 2n
-.Li # Ic zpool get all pool
-pool size 2.70T -
-pool capacity 17% -
-pool altroot - default
-pool health ONLINE -
-pool guid 2501120270416322443 default
-pool version 28 default
-pool bootfs pool/root local
-pool delegation on default
-pool autoreplace off default
-pool cachefile - default
-pool failmode wait default
-pool listsnapshots off default
-pool autoexpand off default
-pool dedupditto 0 default
-pool dedupratio 1.00x -
-pool free 2.24T -
-pool allocated 473G -
-pool readonly off -
-.Ed
-.It Sy Example 8 No Destroying a Tn ZFS No Storage Pool
-.Pp
-The following command destroys the pool
-.Qq Em tank
-and any datasets contained within.
-.Bd -literal -offset 2n
-.Li # Ic zpool destroy -f tank
-.Ed
-.It Sy Example 9 No Exporting a Tn ZFS No Storage Pool
-.Pp
-The following command exports the devices in pool
-.Em tank
-so that they can be relocated or later imported.
-.Bd -literal -offset 2n
-.Li # Ic zpool export tank
-.Ed
-.It Sy Example 10 No Importing a Tn ZFS No Storage Pool
-.Pp
-The following command displays available pools, and then imports the pool
-.Qq Em tank
-for use on the system.
-.Pp
-The results from this command are similar to the following:
-.Bd -literal -offset 2n
-.Li # Ic zpool import
-
- pool: tank
- id: 15451357997522795478
- state: ONLINE
-action: The pool can be imported using its name or numeric identifier.
-config:
-
- tank ONLINE
- mirror ONLINE
- da0 ONLINE
- da1 ONLINE
-.Ed
-.It Xo
-.Sy Example 11
-Upgrading All
-.Tn ZFS
-Storage Pools to the Current Version
-.Xc
-.Pp
-The following command upgrades all
-.Tn ZFS
-Storage pools to the current version of
-the software.
-.Bd -literal -offset 2n
-.Li # Ic zpool upgrade -a
-This system is currently running ZFS pool version 28.
-.Ed
-.It Sy Example 12 No Managing Hot Spares
-.Pp
-The following command creates a new pool with an available hot spare:
-.Bd -literal -offset 2n
-.Li # Ic zpool create tank mirror da0 da1 spare da2
-.Ed
-.Pp
-If one of the disks were to fail, the pool would be reduced to the degraded
-state. The failed device can be replaced using the following command:
-.Bd -literal -offset 2n
-.Li # Ic zpool replace tank da0 da2
-.Ed
-.Pp
-Once the data has been resilvered, the spare is automatically removed and is
-made available should another device fails. The hot spare can be permanently
-removed from the pool using the following command:
-.Bd -literal -offset 2n
-.Li # Ic zpool remove tank da2
-.Ed
-.It Xo
-.Sy Example 13
-Creating a
-.Tn ZFS
-Pool with Mirrored Separate Intent Logs
-.Xc
-.Pp
-The following command creates a
-.Tn ZFS
-storage pool consisting of two, two-way
-mirrors and mirrored log devices:
-.Bd -literal -offset 2n
-.Li # Ic zpool create pool mirror da0 da1 mirror da2 da3 log mirror da4 da5
-.Ed
-.It Sy Example 14 No Adding Cache Devices to a Tn ZFS No Pool
-.Pp
-The following command adds two disks for use as cache devices to a
-.Tn ZFS
-storage pool:
-.Bd -literal -offset 2n
-.Li # Ic zpool add pool cache da2 da3
-.Ed
-.Pp
-Once added, the cache devices gradually fill with content from main memory.
-Depending on the size of your cache devices, it could take over an hour for
-them to fill. Capacity and reads can be monitored using the
-.Cm iostat
-subcommand as follows:
-.Bd -literal -offset 2n
-.Li # Ic zpool iostat -v pool 5
-.Ed
-.It Xo
-.Sy Example 15
-Displaying expanded space on a device
-.Xc
-.Pp
-The following command dipslays the detailed information for the
-.Em data
-pool.
-This pool is comprised of a single
-.Em raidz
-vdev where one of its
-devices increased its capacity by 10GB.
-In this example, the pool will not
-be able to utilized this extra capacity until all the devices under the
-.Em raidz
-vdev have been expanded.
-.Bd -literal -offset 2n
-.Li # Ic zpool list -v data
-NAME SIZE ALLOC FREE FRAG EXPANDSZ CAP DEDUP HEALTH ALTROOT
-data 23.9G 14.6G 9.30G 48% - 61% 1.00x ONLINE -
- raidz1 23.9G 14.6G 9.30G 48% -
- ada0 - - - - -
- ada1 - - - - 10G
- ada2 - - - - -
-.Ed
-.It Xo
-.Sy Example 16
-Removing a Mirrored top-level (Log or Data) Device
-.Xc
-.Pp
-The following commands remove the mirrored log device
-.Sy mirror-2
-and mirrored top-level data device
-.Sy mirror-1 .
-.Pp
-Given this configuration:
-.Bd -literal -offset 2n
- pool: tank
- state: ONLINE
- scrub: none requested
- config:
-
- NAME STATE READ WRITE CKSUM
- tank ONLINE 0 0 0
- mirror-0 ONLINE 0 0 0
- da0 ONLINE 0 0 0
- da1 ONLINE 0 0 0
- mirror-1 ONLINE 0 0 0
- da2 ONLINE 0 0 0
- da3 ONLINE 0 0 0
- logs
- mirror-2 ONLINE 0 0 0
- da4 ONLINE 0 0 0
- da5 ONLINE 0 0 0
-.Ed
-.Pp
-The command to remove the mirrored log
-.Em mirror-2
-is:
-.Bd -literal -offset 2n
-.Li # Ic zpool remove tank mirror-2
-.Ed
-.Pp
-The command to remove the mirrored data
-.Em mirror-1
-is:
-.Bd -literal -offset 2n
-.Li # Ic zpool remove tank mirror-1
-.Ed
-.It Xo
-.Sy Example 17
-Recovering a Faulted
-.Tn ZFS
-Pool
-.Xc
-.Pp
-If a pool is faulted but recoverable, a message indicating this state is
-provided by
-.Qq Nm Cm status
-if the pool was cached (see the
-.Fl c Ar cachefile
-argument above), or as part of the error output from a failed
-.Qq Nm Cm import
-of the pool.
-.Pp
-Recover a cached pool with the
-.Qq Nm Cm clear
-command:
-.Bd -literal -offset 2n
-.Li # Ic zpool clear -F data
-Pool data returned to its state as of Tue Sep 08 13:23:35 2009.
-Discarded approximately 29 seconds of transactions.
-.Ed
-.Pp
-If the pool configuration was not cached, use
-.Qq Nm Cm import
-with the recovery mode flag:
-.Bd -literal -offset 2n
-.Li # Ic zpool import -F data
-Pool data returned to its state as of Tue Sep 08 13:23:35 2009.
-Discarded approximately 29 seconds of transactions.
-.Ed
-.El
-.Sh SEE ALSO
-.Xr zpool-features 7 ,
-.Xr zfs 8 ,
-.Xr zfsd 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 zpool(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/zpool/zpool_iter.c b/cddl/contrib/opensolaris/cmd/zpool/zpool_iter.c
deleted file mode 100644
index 2f7de933ed419..0000000000000
--- a/cddl/contrib/opensolaris/cmd/zpool/zpool_iter.c
+++ /dev/null
@@ -1,255 +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 2007 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-/*
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>.
- */
-
-#include <solaris.h>
-#include <libintl.h>
-#include <libuutil.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-
-#include <libzfs.h>
-
-#include "zpool_util.h"
-
-/*
- * Private interface for iterating over pools specified on the command line.
- * Most consumers will call for_each_pool, but in order to support iostat, we
- * allow fined grained control through the zpool_list_t interface.
- */
-
-typedef struct zpool_node {
- zpool_handle_t *zn_handle;
- uu_avl_node_t zn_avlnode;
- int zn_mark;
-} zpool_node_t;
-
-struct zpool_list {
- boolean_t zl_findall;
- uu_avl_t *zl_avl;
- uu_avl_pool_t *zl_pool;
- zprop_list_t **zl_proplist;
-};
-
-/* ARGSUSED */
-static int
-zpool_compare(const void *larg, const void *rarg, void *unused)
-{
- zpool_handle_t *l = ((zpool_node_t *)larg)->zn_handle;
- zpool_handle_t *r = ((zpool_node_t *)rarg)->zn_handle;
- const char *lname = zpool_get_name(l);
- const char *rname = zpool_get_name(r);
-
- return (strcmp(lname, rname));
-}
-
-/*
- * Callback function for pool_list_get(). Adds the given pool to the AVL tree
- * of known pools.
- */
-static int
-add_pool(zpool_handle_t *zhp, void *data)
-{
- zpool_list_t *zlp = data;
- zpool_node_t *node = safe_malloc(sizeof (zpool_node_t));
- uu_avl_index_t idx;
-
- node->zn_handle = zhp;
- uu_avl_node_init(node, &node->zn_avlnode, zlp->zl_pool);
- if (uu_avl_find(zlp->zl_avl, node, NULL, &idx) == NULL) {
- if (zlp->zl_proplist &&
- zpool_expand_proplist(zhp, zlp->zl_proplist) != 0) {
- zpool_close(zhp);
- free(node);
- return (-1);
- }
- uu_avl_insert(zlp->zl_avl, node, idx);
- } else {
- zpool_close(zhp);
- free(node);
- return (-1);
- }
-
- return (0);
-}
-
-/*
- * Create a list of pools based on the given arguments. If we're given no
- * arguments, then iterate over all pools in the system and add them to the AVL
- * tree. Otherwise, add only those pool explicitly specified on the command
- * line.
- */
-zpool_list_t *
-pool_list_get(int argc, char **argv, zprop_list_t **proplist, int *err)
-{
- zpool_list_t *zlp;
-
- zlp = safe_malloc(sizeof (zpool_list_t));
-
- zlp->zl_pool = uu_avl_pool_create("zfs_pool", sizeof (zpool_node_t),
- offsetof(zpool_node_t, zn_avlnode), zpool_compare, UU_DEFAULT);
-
- if (zlp->zl_pool == NULL)
- zpool_no_memory();
-
- if ((zlp->zl_avl = uu_avl_create(zlp->zl_pool, NULL,
- UU_DEFAULT)) == NULL)
- zpool_no_memory();
-
- zlp->zl_proplist = proplist;
-
- if (argc == 0) {
- (void) zpool_iter(g_zfs, add_pool, zlp);
- zlp->zl_findall = B_TRUE;
- } else {
- int i;
-
- for (i = 0; i < argc; i++) {
- zpool_handle_t *zhp;
-
- if ((zhp = zpool_open_canfail(g_zfs, argv[i])) !=
- NULL) {
- if (add_pool(zhp, zlp) != 0)
- *err = B_TRUE;
- } else {
- *err = B_TRUE;
- }
- }
- }
-
- return (zlp);
-}
-
-/*
- * Search for any new pools, adding them to the list. We only add pools when no
- * options were given on the command line. Otherwise, we keep the list fixed as
- * those that were explicitly specified.
- */
-void
-pool_list_update(zpool_list_t *zlp)
-{
- if (zlp->zl_findall)
- (void) zpool_iter(g_zfs, add_pool, zlp);
-}
-
-/*
- * Iterate over all pools in the list, executing the callback for each
- */
-int
-pool_list_iter(zpool_list_t *zlp, int unavail, zpool_iter_f func,
- void *data)
-{
- zpool_node_t *node, *next_node;
- int ret = 0;
-
- for (node = uu_avl_first(zlp->zl_avl); node != NULL; node = next_node) {
- next_node = uu_avl_next(zlp->zl_avl, node);
- if (zpool_get_state(node->zn_handle) != POOL_STATE_UNAVAIL ||
- unavail)
- ret |= func(node->zn_handle, data);
- }
-
- return (ret);
-}
-
-/*
- * Remove the given pool from the list. When running iostat, we want to remove
- * those pools that no longer exist.
- */
-void
-pool_list_remove(zpool_list_t *zlp, zpool_handle_t *zhp)
-{
- zpool_node_t search, *node;
-
- search.zn_handle = zhp;
- if ((node = uu_avl_find(zlp->zl_avl, &search, NULL, NULL)) != NULL) {
- uu_avl_remove(zlp->zl_avl, node);
- zpool_close(node->zn_handle);
- free(node);
- }
-}
-
-/*
- * Free all the handles associated with this list.
- */
-void
-pool_list_free(zpool_list_t *zlp)
-{
- uu_avl_walk_t *walk;
- zpool_node_t *node;
-
- if ((walk = uu_avl_walk_start(zlp->zl_avl, UU_WALK_ROBUST)) == NULL) {
- (void) fprintf(stderr,
- gettext("internal error: out of memory"));
- exit(1);
- }
-
- while ((node = uu_avl_walk_next(walk)) != NULL) {
- uu_avl_remove(zlp->zl_avl, node);
- zpool_close(node->zn_handle);
- free(node);
- }
-
- uu_avl_walk_end(walk);
- uu_avl_destroy(zlp->zl_avl);
- uu_avl_pool_destroy(zlp->zl_pool);
-
- free(zlp);
-}
-
-/*
- * Returns the number of elements in the pool list.
- */
-int
-pool_list_count(zpool_list_t *zlp)
-{
- return (uu_avl_numnodes(zlp->zl_avl));
-}
-
-/*
- * High level function which iterates over all pools given on the command line,
- * using the pool_list_* interfaces.
- */
-int
-for_each_pool(int argc, char **argv, boolean_t unavail,
- zprop_list_t **proplist, zpool_iter_f func, void *data)
-{
- zpool_list_t *list;
- int ret = 0;
-
- if ((list = pool_list_get(argc, argv, proplist, &ret)) == NULL)
- return (1);
-
- if (pool_list_iter(list, unavail, func, data) != 0)
- ret = 1;
-
- pool_list_free(list);
-
- return (ret);
-}
diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c b/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c
deleted file mode 100644
index 35c3db7893df4..0000000000000
--- a/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c
+++ /dev/null
@@ -1,6742 +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) 2012 by Frederik Wessels. All rights reserved.
- * Copyright (c) 2012 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
- * Copyright (c) 2013 by Prasad Joshi (sTec). All rights reserved.
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>.
- * Copyright 2016 Nexenta Systems, Inc.
- * Copyright (c) 2017 Datto Inc.
- * Copyright (c) 2017, Intel Corporation.
- */
-
-#include <solaris.h>
-#include <assert.h>
-#include <ctype.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <libgen.h>
-#include <libintl.h>
-#include <libuutil.h>
-#include <locale.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <unistd.h>
-#include <priv.h>
-#include <pwd.h>
-#include <zone.h>
-#include <sys/time.h>
-#include <zfs_prop.h>
-#include <sys/fs/zfs.h>
-#include <sys/stat.h>
-#include <sys/debug.h>
-
-#include <libzfs.h>
-
-#include "zpool_util.h"
-#include "zfs_comutil.h"
-#include "zfeature_common.h"
-
-#include "statcommon.h"
-
-libzfs_handle_t *g_zfs;
-
-static int zpool_do_create(int, char **);
-static int zpool_do_destroy(int, char **);
-
-static int zpool_do_add(int, char **);
-static int zpool_do_remove(int, char **);
-static int zpool_do_labelclear(int, char **);
-
-static int zpool_do_checkpoint(int, char **);
-
-static int zpool_do_list(int, char **);
-static int zpool_do_iostat(int, char **);
-static int zpool_do_status(int, char **);
-
-static int zpool_do_online(int, char **);
-static int zpool_do_offline(int, char **);
-static int zpool_do_clear(int, char **);
-static int zpool_do_reopen(int, char **);
-
-static int zpool_do_reguid(int, char **);
-
-static int zpool_do_attach(int, char **);
-static int zpool_do_detach(int, char **);
-static int zpool_do_replace(int, char **);
-static int zpool_do_split(int, char **);
-
-static int zpool_do_initialize(int, char **);
-static int zpool_do_scrub(int, char **);
-
-static int zpool_do_import(int, char **);
-static int zpool_do_export(int, char **);
-
-static int zpool_do_upgrade(int, char **);
-
-static int zpool_do_history(int, char **);
-
-static int zpool_do_get(int, char **);
-static int zpool_do_set(int, char **);
-
-static int zpool_do_sync(int, char **);
-
-/*
- * These libumem hooks provide a reasonable set of defaults for the allocator's
- * debugging facilities.
- */
-
-#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_ADD,
- HELP_ATTACH,
- HELP_CLEAR,
- HELP_CREATE,
- HELP_CHECKPOINT,
- HELP_DESTROY,
- HELP_DETACH,
- HELP_EXPORT,
- HELP_HISTORY,
- HELP_IMPORT,
- HELP_IOSTAT,
- HELP_LABELCLEAR,
- HELP_LIST,
- HELP_OFFLINE,
- HELP_ONLINE,
- HELP_REPLACE,
- HELP_REMOVE,
- HELP_INITIALIZE,
- HELP_SCRUB,
- HELP_STATUS,
- HELP_UPGRADE,
- HELP_GET,
- HELP_SET,
- HELP_SPLIT,
- HELP_SYNC,
- HELP_REGUID,
- HELP_REOPEN
-} zpool_help_t;
-
-
-typedef struct zpool_command {
- const char *name;
- int (*func)(int, char **);
- zpool_help_t usage;
-} zpool_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 zpool_command_t command_table[] = {
- { "create", zpool_do_create, HELP_CREATE },
- { "destroy", zpool_do_destroy, HELP_DESTROY },
- { NULL },
- { "add", zpool_do_add, HELP_ADD },
- { "remove", zpool_do_remove, HELP_REMOVE },
- { NULL },
- { "labelclear", zpool_do_labelclear, HELP_LABELCLEAR },
- { NULL },
- { "checkpoint", zpool_do_checkpoint, HELP_CHECKPOINT },
- { NULL },
- { "list", zpool_do_list, HELP_LIST },
- { "iostat", zpool_do_iostat, HELP_IOSTAT },
- { "status", zpool_do_status, HELP_STATUS },
- { NULL },
- { "online", zpool_do_online, HELP_ONLINE },
- { "offline", zpool_do_offline, HELP_OFFLINE },
- { "clear", zpool_do_clear, HELP_CLEAR },
- { "reopen", zpool_do_reopen, HELP_REOPEN },
- { NULL },
- { "attach", zpool_do_attach, HELP_ATTACH },
- { "detach", zpool_do_detach, HELP_DETACH },
- { "replace", zpool_do_replace, HELP_REPLACE },
- { "split", zpool_do_split, HELP_SPLIT },
- { NULL },
- { "initialize", zpool_do_initialize, HELP_INITIALIZE },
- { "scrub", zpool_do_scrub, HELP_SCRUB },
- { NULL },
- { "import", zpool_do_import, HELP_IMPORT },
- { "export", zpool_do_export, HELP_EXPORT },
- { "upgrade", zpool_do_upgrade, HELP_UPGRADE },
- { "reguid", zpool_do_reguid, HELP_REGUID },
- { NULL },
- { "history", zpool_do_history, HELP_HISTORY },
- { "get", zpool_do_get, HELP_GET },
- { "set", zpool_do_set, HELP_SET },
- { "sync", zpool_do_sync, HELP_SYNC },
-};
-
-#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0]))
-
-#define VDEV_ALLOC_CLASS_LOGS "logs"
-
-static zpool_command_t *current_command;
-static char history_str[HIS_MAX_RECORD_LEN];
-static boolean_t log_history = B_TRUE;
-static uint_t timestamp_fmt = NODATE;
-
-static const char *
-get_usage(zpool_help_t idx)
-{
- switch (idx) {
- case HELP_ADD:
- return (gettext("\tadd [-fgLnP] <pool> <vdev> ...\n"));
- case HELP_ATTACH:
- return (gettext("\tattach [-f] <pool> <device> "
- "<new-device>\n"));
- case HELP_CLEAR:
- return (gettext("\tclear [-nF] <pool> [device]\n"));
- case HELP_CREATE:
- return (gettext("\tcreate [-fnd] [-B] "
- "[-o property=value] ... \n"
- "\t [-O file-system-property=value] ...\n"
- "\t [-m mountpoint] [-R root] [-t tempname] "
- "<pool> <vdev> ...\n"));
- case HELP_CHECKPOINT:
- return (gettext("\tcheckpoint [--discard] <pool> ...\n"));
- case HELP_DESTROY:
- return (gettext("\tdestroy [-f] <pool>\n"));
- case HELP_DETACH:
- return (gettext("\tdetach <pool> <device>\n"));
- case HELP_EXPORT:
- return (gettext("\texport [-f] <pool> ...\n"));
- case HELP_HISTORY:
- return (gettext("\thistory [-il] [<pool>] ...\n"));
- case HELP_IMPORT:
- return (gettext("\timport [-d dir] [-D]\n"
- "\timport [-o mntopts] [-o property=value] ... \n"
- "\t [-d dir | -c cachefile] [-D] [-f] [-m] [-N] "
- "[-R root] [-F [-n]] -a\n"
- "\timport [-o mntopts] [-o property=value] ... \n"
- "\t [-d dir | -c cachefile] [-D] [-f] [-m] [-N] "
- "[-R root] [-F [-n]] [-t]\n"
- "\t [--rewind-to-checkpoint] <pool | id> [newpool]\n"));
- case HELP_IOSTAT:
- return (gettext("\tiostat [-gLPv] [-T d|u] [pool] ... "
- "[interval [count]]\n"));
- case HELP_LABELCLEAR:
- return (gettext("\tlabelclear [-f] <vdev>\n"));
- case HELP_LIST:
- return (gettext("\tlist [-gHLpPv] [-o property[,...]] "
- "[-T d|u] [pool] ... [interval [count]]\n"));
- case HELP_OFFLINE:
- return (gettext("\toffline [-t] <pool> <device> ...\n"));
- case HELP_ONLINE:
- return (gettext("\tonline [-e] <pool> <device> ...\n"));
- case HELP_REPLACE:
- return (gettext("\treplace [-f] <pool> <device> "
- "[new-device]\n"));
- case HELP_REMOVE:
- return (gettext("\tremove [-nps] <pool> <device> ...\n"));
- case HELP_REOPEN:
- return (gettext("\treopen <pool>\n"));
- case HELP_INITIALIZE:
- return (gettext("\tinitialize [-cs] <pool> [<device> ...]\n"));
- case HELP_SCRUB:
- return (gettext("\tscrub [-s | -p] <pool> ...\n"));
- case HELP_STATUS:
- return (gettext("\tstatus [-DgLPvx] [-T d|u] [pool] ... "
- "[interval [count]]\n"));
- case HELP_UPGRADE:
- return (gettext("\tupgrade [-v]\n"
- "\tupgrade [-V version] <-a | pool ...>\n"));
- case HELP_GET:
- return (gettext("\tget [-Hp] [-o \"all\" | field[,...]] "
- "<\"all\" | property[,...]> <pool> ...\n"));
- case HELP_SET:
- return (gettext("\tset <property=value> <pool> \n"));
- case HELP_SPLIT:
- return (gettext("\tsplit [-gLnP] [-R altroot] [-o mntopts]\n"
- "\t [-o property=value] <pool> <newpool> "
- "[<device> ...]\n"));
- case HELP_REGUID:
- return (gettext("\treguid <pool>\n"));
- case HELP_SYNC:
- return (gettext("\tsync [pool] ...\n"));
- }
-
- abort();
- /* NOTREACHED */
-}
-
-
-/*
- * Callback routine that will print out a pool property value.
- */
-static int
-print_prop_cb(int prop, void *cb)
-{
- FILE *fp = cb;
-
- (void) fprintf(fp, "\t%-19s ", zpool_prop_to_name(prop));
-
- if (zpool_prop_readonly(prop))
- (void) fprintf(fp, " NO ");
- else
- (void) fprintf(fp, " YES ");
-
- if (zpool_prop_values(prop) == NULL)
- (void) fprintf(fp, "-\n");
- else
- (void) fprintf(fp, "%s\n", zpool_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.
- */
-void
-usage(boolean_t requested)
-{
- FILE *fp = requested ? stdout : stderr;
-
- if (current_command == NULL) {
- int i;
-
- (void) fprintf(fp, gettext("usage: zpool 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));
- }
- } 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, "list") == 0))) {
-
- (void) fprintf(fp,
- gettext("\nthe following properties are supported:\n"));
-
- (void) fprintf(fp, "\n\t%-19s %s %s\n\n",
- "PROPERTY", "EDIT", "VALUES");
-
- /* Iterate over all properties */
- (void) zprop_iter(print_prop_cb, fp, B_FALSE, B_TRUE,
- ZFS_TYPE_POOL);
-
- (void) fprintf(fp, "\t%-19s ", "feature@...");
- (void) fprintf(fp, "YES disabled | enabled | active\n");
-
- (void) fprintf(fp, gettext("\nThe feature@ properties must be "
- "appended with a feature name.\nSee zpool-features(7).\n"));
- }
-
- /*
- * See comments at end of main().
- */
- if (getenv("ZFS_ABORT") != NULL) {
- (void) printf("dumping core by request\n");
- abort();
- }
-
- exit(requested ? 0 : 2);
-}
-
-/*
- * print a pool vdev config for dry runs
- */
-static void
-print_vdev_tree(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int indent,
- const char *match, int name_flags)
-{
- nvlist_t **child;
- uint_t c, children;
- char *vname;
- boolean_t printed = B_FALSE;
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0) {
- if (name != NULL)
- (void) printf("\t%*s%s\n", indent, "", name);
- return;
- }
-
- for (c = 0; c < children; c++) {
- uint64_t is_log = B_FALSE;
- char *class = "";
-
- (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
- &is_log);
- if (is_log)
- class = VDEV_ALLOC_BIAS_LOG;
- (void) nvlist_lookup_string(child[c],
- ZPOOL_CONFIG_ALLOCATION_BIAS, &class);
- if (strcmp(match, class) != 0)
- continue;
-
- if (!printed && name != NULL) {
- (void) printf("\t%*s%s\n", indent, "", name);
- printed = B_TRUE;
- }
- vname = zpool_vdev_name(g_zfs, zhp, child[c], name_flags);
- print_vdev_tree(zhp, vname, child[c], indent + 2, "",
- name_flags);
- free(vname);
- }
-}
-
-static boolean_t
-prop_list_contains_feature(nvlist_t *proplist)
-{
- nvpair_t *nvp;
- for (nvp = nvlist_next_nvpair(proplist, NULL); NULL != nvp;
- nvp = nvlist_next_nvpair(proplist, nvp)) {
- if (zpool_prop_feature(nvpair_name(nvp)))
- return (B_TRUE);
- }
- return (B_FALSE);
-}
-
-/*
- * Add a property pair (name, string-value) into a property nvlist.
- */
-static int
-add_prop_list(const char *propname, char *propval, nvlist_t **props,
- boolean_t poolprop)
-{
- zpool_prop_t prop = ZPROP_INVAL;
- zfs_prop_t fprop;
- nvlist_t *proplist;
- const char *normnm;
- char *strval;
-
- if (*props == NULL &&
- nvlist_alloc(props, NV_UNIQUE_NAME, 0) != 0) {
- (void) fprintf(stderr,
- gettext("internal error: out of memory\n"));
- return (1);
- }
-
- proplist = *props;
-
- if (poolprop) {
- const char *vname = zpool_prop_to_name(ZPOOL_PROP_VERSION);
-
- if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL &&
- !zpool_prop_feature(propname)) {
- (void) fprintf(stderr, gettext("property '%s' is "
- "not a valid pool property\n"), propname);
- return (2);
- }
-
- /*
- * feature@ properties and version should not be specified
- * at the same time.
- */
- if ((prop == ZPOOL_PROP_INVAL && zpool_prop_feature(propname) &&
- nvlist_exists(proplist, vname)) ||
- (prop == ZPOOL_PROP_VERSION &&
- prop_list_contains_feature(proplist))) {
- (void) fprintf(stderr, gettext("'feature@' and "
- "'version' properties cannot be specified "
- "together\n"));
- return (2);
- }
-
-
- if (zpool_prop_feature(propname))
- normnm = propname;
- else
- normnm = zpool_prop_to_name(prop);
- } else {
- if ((fprop = zfs_name_to_prop(propname)) != ZPROP_INVAL) {
- normnm = zfs_prop_to_name(fprop);
- } else {
- normnm = propname;
- }
- }
-
- if (nvlist_lookup_string(proplist, normnm, &strval) == 0 &&
- prop != ZPOOL_PROP_CACHEFILE) {
- (void) fprintf(stderr, gettext("property '%s' "
- "specified multiple times\n"), propname);
- return (2);
- }
-
- if (nvlist_add_string(proplist, normnm, propval) != 0) {
- (void) fprintf(stderr, gettext("internal "
- "error: out of memory\n"));
- return (1);
- }
-
- return (0);
-}
-
-/*
- * Set a default property pair (name, string-value) in a property nvlist
- */
-static int
-add_prop_list_default(const char *propname, char *propval, nvlist_t **props,
- boolean_t poolprop)
-{
- char *pval;
-
- if (nvlist_lookup_string(*props, propname, &pval) == 0)
- return (0);
-
- return (add_prop_list(propname, propval, props, poolprop));
-}
-
-/*
- * zpool add [-fgLnP] [-o property=value] <pool> <vdev> ...
- *
- * -f Force addition of devices, even if they appear in use
- * -g Display guid for individual vdev name.
- * -L Follow links when resolving vdev path name.
- * -n Do not add the devices, but display the resulting layout if
- * they were to be added.
- * -P Display full path for vdev name.
- *
- * Adds the given vdevs to 'pool'. As with create, the bulk of this work is
- * handled by get_vdev_spec(), which constructs the nvlist needed to pass to
- * libzfs.
- */
-int
-zpool_do_add(int argc, char **argv)
-{
- boolean_t force = B_FALSE;
- boolean_t dryrun = B_FALSE;
- int name_flags = 0;
- int c;
- nvlist_t *nvroot;
- char *poolname;
- zpool_boot_label_t boot_type;
- uint64_t boot_size;
- int ret;
- zpool_handle_t *zhp;
- nvlist_t *config;
-
- /* check options */
- while ((c = getopt(argc, argv, "fgLnP")) != -1) {
- switch (c) {
- case 'f':
- force = B_TRUE;
- break;
- case 'g':
- name_flags |= VDEV_NAME_GUID;
- break;
- case 'L':
- name_flags |= VDEV_NAME_FOLLOW_LINKS;
- break;
- case 'n':
- dryrun = B_TRUE;
- break;
- case 'P':
- name_flags |= VDEV_NAME_PATH;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* get pool name and check number of arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing pool name argument\n"));
- usage(B_FALSE);
- }
- if (argc < 2) {
- (void) fprintf(stderr, gettext("missing vdev specification\n"));
- usage(B_FALSE);
- }
-
- poolname = argv[0];
-
- argc--;
- argv++;
-
- if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
- return (1);
-
- if ((config = zpool_get_config(zhp, NULL)) == NULL) {
- (void) fprintf(stderr, gettext("pool '%s' is unavailable\n"),
- poolname);
- zpool_close(zhp);
- return (1);
- }
-
- if (zpool_is_bootable(zhp))
- boot_type = ZPOOL_COPY_BOOT_LABEL;
- else
- boot_type = ZPOOL_NO_BOOT_LABEL;
-
- /* pass off to get_vdev_spec for processing */
- boot_size = zpool_get_prop_int(zhp, ZPOOL_PROP_BOOTSIZE, NULL);
- nvroot = make_root_vdev(zhp, force, !force, B_FALSE, dryrun,
- boot_type, boot_size, argc, argv);
- if (nvroot == NULL) {
- zpool_close(zhp);
- return (1);
- }
-
- if (dryrun) {
- nvlist_t *poolnvroot;
-
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &poolnvroot) == 0);
-
- (void) printf(gettext("would update '%s' to the following "
- "configuration:\n"), zpool_get_name(zhp));
-
- /* print original main pool and new tree */
- print_vdev_tree(zhp, poolname, poolnvroot, 0, "",
- name_flags | VDEV_NAME_TYPE_ID);
- print_vdev_tree(zhp, NULL, nvroot, 0, "", name_flags);
-
- /* print other classes: 'dedup', 'special', and 'log' */
- print_vdev_tree(zhp, "dedup", poolnvroot, 0,
- VDEV_ALLOC_BIAS_DEDUP, name_flags);
- print_vdev_tree(zhp, NULL, nvroot, 0, VDEV_ALLOC_BIAS_DEDUP,
- name_flags);
-
- print_vdev_tree(zhp, "special", poolnvroot, 0,
- VDEV_ALLOC_BIAS_SPECIAL, name_flags);
- print_vdev_tree(zhp, NULL, nvroot, 0, VDEV_ALLOC_BIAS_SPECIAL,
- name_flags);
-
- print_vdev_tree(zhp, "logs", poolnvroot, 0, VDEV_ALLOC_BIAS_LOG,
- name_flags);
- print_vdev_tree(zhp, NULL, nvroot, 0, VDEV_ALLOC_BIAS_LOG,
- name_flags);
-
- ret = 0;
- } else {
- ret = (zpool_add(zhp, nvroot) != 0);
- }
-
- nvlist_free(nvroot);
- zpool_close(zhp);
-
- return (ret);
-}
-
-/*
- * zpool remove <pool> <vdev> ...
- *
- * Removes the given vdev from the pool.
- */
-int
-zpool_do_remove(int argc, char **argv)
-{
- char *poolname;
- int i, ret = 0;
- zpool_handle_t *zhp;
- boolean_t stop = B_FALSE;
- boolean_t noop = B_FALSE;
- boolean_t parsable = B_FALSE;
- char c;
-
- /* check options */
- while ((c = getopt(argc, argv, "nps")) != -1) {
- switch (c) {
- case 'n':
- noop = B_TRUE;
- break;
- case 'p':
- parsable = B_TRUE;
- break;
- case 's':
- stop = B_TRUE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* get pool name and check number of arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing pool name argument\n"));
- usage(B_FALSE);
- }
-
- poolname = argv[0];
-
- if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
- return (1);
-
- if (stop && noop) {
- (void) fprintf(stderr, gettext("stop request ignored\n"));
- return (0);
- }
-
- if (stop) {
- if (argc > 1) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
- if (zpool_vdev_remove_cancel(zhp) != 0)
- ret = 1;
- } else {
- if (argc < 2) {
- (void) fprintf(stderr, gettext("missing device\n"));
- usage(B_FALSE);
- }
-
- for (i = 1; i < argc; i++) {
- if (noop) {
- uint64_t size;
-
- if (zpool_vdev_indirect_size(zhp, argv[i],
- &size) != 0) {
- ret = 1;
- break;
- }
- if (parsable) {
- (void) printf("%s %llu\n",
- argv[i], size);
- } else {
- char valstr[32];
- zfs_nicenum(size, valstr,
- sizeof (valstr));
- (void) printf("Memory that will be "
- "used after removing %s: %s\n",
- argv[i], valstr);
- }
- } else {
- if (zpool_vdev_remove(zhp, argv[i]) != 0)
- ret = 1;
- }
- }
- }
-
- return (ret);
-}
-
-/*
- * zpool labelclear [-f] <vdev>
- *
- * -f Force clearing the label for the vdevs which are members of
- * the exported or foreign pools.
- *
- * Verifies that the vdev is not active and zeros out the label information
- * on the device.
- */
-int
-zpool_do_labelclear(int argc, char **argv)
-{
- char vdev[MAXPATHLEN];
- char *name = NULL;
- struct stat st;
- int c, fd, ret = 0;
- nvlist_t *config;
- pool_state_t state;
- boolean_t inuse = B_FALSE;
- boolean_t force = B_FALSE;
-
- /* check options */
- while ((c = getopt(argc, argv, "f")) != -1) {
- switch (c) {
- case 'f':
- force = B_TRUE;
- break;
- default:
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* get vdev name */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing vdev name\n"));
- usage(B_FALSE);
- }
- if (argc > 1) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- /*
- * Check if we were given absolute path and use it as is.
- * Otherwise if the provided vdev name doesn't point to a file,
- * try prepending dsk path and appending s0.
- */
- (void) strlcpy(vdev, argv[0], sizeof (vdev));
- if (vdev[0] != '/' && stat(vdev, &st) != 0) {
- char *s;
-
- (void) snprintf(vdev, sizeof (vdev), "%s/%s",
-#ifdef illumos
- ZFS_DISK_ROOT, argv[0]);
- if ((s = strrchr(argv[0], 's')) == NULL ||
- !isdigit(*(s + 1)))
- (void) strlcat(vdev, "s0", sizeof (vdev));
-#else
- "/dev", argv[0]);
-#endif
- if (stat(vdev, &st) != 0) {
- (void) fprintf(stderr, gettext(
- "failed to find device %s, try specifying absolute "
- "path instead\n"), argv[0]);
- return (1);
- }
- }
-
- if ((fd = open(vdev, O_RDWR)) < 0) {
- (void) fprintf(stderr, gettext("failed to open %s: %s\n"),
- vdev, strerror(errno));
- return (1);
- }
-
- if (zpool_read_label(fd, &config) != 0) {
- (void) fprintf(stderr,
- gettext("failed to read label from %s\n"), vdev);
- return (1);
- }
- nvlist_free(config);
-
- ret = zpool_in_use(g_zfs, fd, &state, &name, &inuse);
- if (ret != 0) {
- (void) fprintf(stderr,
- gettext("failed to check state for %s\n"), vdev);
- return (1);
- }
-
- if (!inuse)
- goto wipe_label;
-
- switch (state) {
- default:
- case POOL_STATE_ACTIVE:
- case POOL_STATE_SPARE:
- case POOL_STATE_L2CACHE:
- (void) fprintf(stderr, gettext(
- "%s is a member (%s) of pool \"%s\"\n"),
- vdev, zpool_pool_state_to_name(state), name);
- ret = 1;
- goto errout;
-
- case POOL_STATE_EXPORTED:
- if (force)
- break;
- (void) fprintf(stderr, gettext(
- "use '-f' to override the following error:\n"
- "%s is a member of exported pool \"%s\"\n"),
- vdev, name);
- ret = 1;
- goto errout;
-
- case POOL_STATE_POTENTIALLY_ACTIVE:
- if (force)
- break;
- (void) fprintf(stderr, gettext(
- "use '-f' to override the following error:\n"
- "%s is a member of potentially active pool \"%s\"\n"),
- vdev, name);
- ret = 1;
- goto errout;
-
- case POOL_STATE_DESTROYED:
- /* inuse should never be set for a destroyed pool */
- assert(0);
- break;
- }
-
-wipe_label:
- ret = zpool_clear_label(fd);
- if (ret != 0) {
- (void) fprintf(stderr,
- gettext("failed to clear label for %s\n"), vdev);
- }
-
-errout:
- free(name);
- (void) close(fd);
-
- return (ret);
-}
-
-/*
- * zpool create [-fnd] [-B] [-o property=value] ...
- * [-O file-system-property=value] ...
- * [-R root] [-m mountpoint] [-t tempname] <pool> <dev> ...
- *
- * -B Create boot partition.
- * -f Force creation, even if devices appear in use
- * -n Do not create the pool, but display the resulting layout if it
- * were to be created.
- * -R Create a pool under an alternate root
- * -m Set default mountpoint for the root dataset. By default it's
- * '/<pool>'
- * -t Use the temporary name until the pool is exported.
- * -o Set property=value.
- * -d Don't automatically enable all supported pool features
- * (individual features can be enabled with -o).
- * -O Set fsproperty=value in the pool's root file system
- *
- * Creates the named pool according to the given vdev specification. The
- * bulk of the vdev processing is done in get_vdev_spec() in zpool_vdev.c. Once
- * we get the nvlist back from get_vdev_spec(), we either print out the contents
- * (if '-n' was specified), or pass it to libzfs to do the creation.
- */
-
-#define SYSTEM256 (256 * 1024 * 1024)
-int
-zpool_do_create(int argc, char **argv)
-{
- boolean_t force = B_FALSE;
- boolean_t dryrun = B_FALSE;
- boolean_t enable_all_pool_feat = B_TRUE;
- zpool_boot_label_t boot_type = ZPOOL_NO_BOOT_LABEL;
- uint64_t boot_size = 0;
- int c;
- nvlist_t *nvroot = NULL;
- char *poolname;
- char *tname = NULL;
- int ret = 1;
- char *altroot = NULL;
- char *mountpoint = NULL;
- nvlist_t *fsprops = NULL;
- nvlist_t *props = NULL;
- char *propval;
-
- /* check options */
- while ((c = getopt(argc, argv, ":fndBR:m:o:O:t:")) != -1) {
- switch (c) {
- case 'f':
- force = B_TRUE;
- break;
- case 'n':
- dryrun = B_TRUE;
- break;
- case 'd':
- enable_all_pool_feat = B_FALSE;
- break;
- case 'B':
-#ifdef illumos
- /*
- * We should create the system partition.
- * Also make sure the size is set.
- */
- boot_type = ZPOOL_CREATE_BOOT_LABEL;
- if (boot_size == 0)
- boot_size = SYSTEM256;
- break;
-#else
- (void) fprintf(stderr,
- gettext("option '%c' is not supported\n"),
- optopt);
- goto badusage;
-#endif
- case 'R':
- altroot = optarg;
- if (add_prop_list(zpool_prop_to_name(
- ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE))
- goto errout;
- if (add_prop_list_default(zpool_prop_to_name(
- ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE))
- goto errout;
- break;
- case 'm':
- /* Equivalent to -O mountpoint=optarg */
- mountpoint = optarg;
- break;
- case 'o':
- if ((propval = strchr(optarg, '=')) == NULL) {
- (void) fprintf(stderr, gettext("missing "
- "'=' for -o option\n"));
- goto errout;
- }
- *propval = '\0';
- propval++;
-
- if (add_prop_list(optarg, propval, &props, B_TRUE))
- goto errout;
-
- /*
- * Get bootsize value for make_root_vdev().
- */
- if (zpool_name_to_prop(optarg) == ZPOOL_PROP_BOOTSIZE) {
- if (zfs_nicestrtonum(g_zfs, propval,
- &boot_size) < 0 || boot_size == 0) {
- (void) fprintf(stderr,
- gettext("bad boot partition size "
- "'%s': %s\n"), propval,
- libzfs_error_description(g_zfs));
- goto errout;
- }
- }
-
- /*
- * If the user is creating a pool that doesn't support
- * feature flags, don't enable any features.
- */
- if (zpool_name_to_prop(optarg) == ZPOOL_PROP_VERSION) {
- char *end;
- u_longlong_t ver;
-
- ver = strtoull(propval, &end, 10);
- if (*end == '\0' &&
- ver < SPA_VERSION_FEATURES) {
- enable_all_pool_feat = B_FALSE;
- }
- }
- if (zpool_name_to_prop(optarg) == ZPOOL_PROP_ALTROOT)
- altroot = propval;
- break;
- case 'O':
- if ((propval = strchr(optarg, '=')) == NULL) {
- (void) fprintf(stderr, gettext("missing "
- "'=' for -O option\n"));
- goto errout;
- }
- *propval = '\0';
- propval++;
-
- /*
- * Mountpoints are checked and then added later.
- * Uniquely among properties, they can be specified
- * more than once, to avoid conflict with -m.
- */
- if (0 == strcmp(optarg,
- zfs_prop_to_name(ZFS_PROP_MOUNTPOINT))) {
- mountpoint = propval;
- } else if (add_prop_list(optarg, propval, &fsprops,
- B_FALSE)) {
- goto errout;
- }
- break;
- case 't':
- /*
- * Sanity check temporary pool name.
- */
- if (strchr(optarg, '/') != NULL) {
- (void) fprintf(stderr, gettext("cannot create "
- "'%s': invalid character '/' in temporary "
- "name\n"), optarg);
- (void) fprintf(stderr, gettext("use 'zfs "
- "create' to create a dataset\n"));
- goto errout;
- }
-
- if (add_prop_list(zpool_prop_to_name(
- ZPOOL_PROP_TNAME), optarg, &props, B_TRUE))
- goto errout;
- if (add_prop_list_default(zpool_prop_to_name(
- ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE))
- goto errout;
- tname = optarg;
- break;
- case ':':
- (void) fprintf(stderr, gettext("missing argument for "
- "'%c' option\n"), optopt);
- goto badusage;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- goto badusage;
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* get pool name and check number of arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing pool name argument\n"));
- goto badusage;
- }
- if (argc < 2) {
- (void) fprintf(stderr, gettext("missing vdev specification\n"));
- goto badusage;
- }
-
- poolname = argv[0];
-
- /*
- * As a special case, check for use of '/' in the name, and direct the
- * user to use 'zfs create' instead.
- */
- if (strchr(poolname, '/') != NULL) {
- (void) fprintf(stderr, gettext("cannot create '%s': invalid "
- "character '/' in pool name\n"), poolname);
- (void) fprintf(stderr, gettext("use 'zfs create' to "
- "create a dataset\n"));
- goto errout;
- }
-
- /*
- * Make sure the bootsize is set when ZPOOL_CREATE_BOOT_LABEL is used,
- * and not set otherwise.
- */
- if (boot_type == ZPOOL_CREATE_BOOT_LABEL) {
- const char *propname;
- char *strptr, *buf = NULL;
- int rv;
-
- propname = zpool_prop_to_name(ZPOOL_PROP_BOOTSIZE);
- if (nvlist_lookup_string(props, propname, &strptr) != 0) {
- (void) asprintf(&buf, "%" PRIu64, boot_size);
- if (buf == NULL) {
- (void) fprintf(stderr,
- gettext("internal error: out of memory\n"));
- goto errout;
- }
- rv = add_prop_list(propname, buf, &props, B_TRUE);
- free(buf);
- if (rv != 0)
- goto errout;
- }
- } else {
- const char *propname;
- char *strptr;
-
- propname = zpool_prop_to_name(ZPOOL_PROP_BOOTSIZE);
- if (nvlist_lookup_string(props, propname, &strptr) == 0) {
- (void) fprintf(stderr, gettext("error: setting boot "
- "partition size requires option '-B'\n"));
- goto errout;
- }
- }
-
- /* pass off to get_vdev_spec for bulk processing */
- nvroot = make_root_vdev(NULL, force, !force, B_FALSE, dryrun,
- boot_type, boot_size, argc - 1, argv + 1);
- if (nvroot == NULL)
- goto errout;
-
- /* make_root_vdev() allows 0 toplevel children if there are spares */
- if (!zfs_allocatable_devs(nvroot)) {
- (void) fprintf(stderr, gettext("invalid vdev "
- "specification: at least one toplevel vdev must be "
- "specified\n"));
- goto errout;
- }
-
- if (altroot != NULL && altroot[0] != '/') {
- (void) fprintf(stderr, gettext("invalid alternate root '%s': "
- "must be an absolute path\n"), altroot);
- goto errout;
- }
-
- /*
- * Check the validity of the mountpoint and direct the user to use the
- * '-m' mountpoint option if it looks like its in use.
- * Ignore the checks if the '-f' option is given.
- */
- if (!force && (mountpoint == NULL ||
- (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 &&
- strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0))) {
- char buf[MAXPATHLEN];
- DIR *dirp;
-
- if (mountpoint && mountpoint[0] != '/') {
- (void) fprintf(stderr, gettext("invalid mountpoint "
- "'%s': must be an absolute path, 'legacy', or "
- "'none'\n"), mountpoint);
- goto errout;
- }
-
- if (mountpoint == NULL) {
- if (altroot != NULL)
- (void) snprintf(buf, sizeof (buf), "%s/%s",
- altroot, poolname);
- else
- (void) snprintf(buf, sizeof (buf), "/%s",
- poolname);
- } else {
- if (altroot != NULL)
- (void) snprintf(buf, sizeof (buf), "%s%s",
- altroot, mountpoint);
- else
- (void) snprintf(buf, sizeof (buf), "%s",
- mountpoint);
- }
-
- if ((dirp = opendir(buf)) == NULL && errno != ENOENT) {
- (void) fprintf(stderr, gettext("mountpoint '%s' : "
- "%s\n"), buf, strerror(errno));
- (void) fprintf(stderr, gettext("use '-m' "
- "option to provide a different default\n"));
- goto errout;
- } else if (dirp) {
- int count = 0;
-
- while (count < 3 && readdir(dirp) != NULL)
- count++;
- (void) closedir(dirp);
-
- if (count > 2) {
- (void) fprintf(stderr, gettext("mountpoint "
- "'%s' exists and is not empty\n"), buf);
- (void) fprintf(stderr, gettext("use '-m' "
- "option to provide a "
- "different default\n"));
- goto errout;
- }
- }
- }
-
- /*
- * Now that the mountpoint's validity has been checked, ensure that
- * the property is set appropriately prior to creating the pool.
- */
- if (mountpoint != NULL) {
- ret = add_prop_list(zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
- mountpoint, &fsprops, B_FALSE);
- if (ret != 0)
- goto errout;
- }
-
- ret = 1;
- if (dryrun) {
- /*
- * For a dry run invocation, print out a basic message and run
- * through all the vdevs in the list and print out in an
- * appropriate hierarchy.
- */
- (void) printf(gettext("would create '%s' with the "
- "following layout:\n\n"), poolname);
-
- print_vdev_tree(NULL, poolname, nvroot, 0, "", 0);
- print_vdev_tree(NULL, "dedup", nvroot, 0,
- VDEV_ALLOC_BIAS_DEDUP, 0);
- print_vdev_tree(NULL, "special", nvroot, 0,
- VDEV_ALLOC_BIAS_SPECIAL, 0);
- print_vdev_tree(NULL, "logs", nvroot, 0,
- VDEV_ALLOC_BIAS_LOG, 0);
-
- ret = 0;
- } else {
- /*
- * Hand off to libzfs.
- */
- if (enable_all_pool_feat) {
- spa_feature_t i;
- for (i = 0; i < SPA_FEATURES; i++) {
- char propname[MAXPATHLEN];
- zfeature_info_t *feat = &spa_feature_table[i];
-
- (void) snprintf(propname, sizeof (propname),
- "feature@%s", feat->fi_uname);
-
- /*
- * Skip feature if user specified it manually
- * on the command line.
- */
- if (nvlist_exists(props, propname))
- continue;
-
- ret = add_prop_list(propname,
- ZFS_FEATURE_ENABLED, &props, B_TRUE);
- if (ret != 0)
- goto errout;
- }
- }
-
- ret = 1;
- if (zpool_create(g_zfs, poolname,
- nvroot, props, fsprops) == 0) {
- zfs_handle_t *pool = zfs_open(g_zfs,
- tname ? tname : poolname, ZFS_TYPE_FILESYSTEM);
- if (pool != NULL) {
- if (zfs_mount(pool, NULL, 0) == 0)
- ret = zfs_shareall(pool);
- zfs_close(pool);
- }
- } else if (libzfs_errno(g_zfs) == EZFS_INVALIDNAME) {
- (void) fprintf(stderr, gettext("pool name may have "
- "been omitted\n"));
- }
- }
-
-errout:
- nvlist_free(nvroot);
- nvlist_free(fsprops);
- nvlist_free(props);
- return (ret);
-badusage:
- nvlist_free(fsprops);
- nvlist_free(props);
- usage(B_FALSE);
- return (2);
-}
-
-/*
- * zpool destroy <pool>
- *
- * -f Forcefully unmount any datasets
- *
- * Destroy the given pool. Automatically unmounts any datasets in the pool.
- */
-int
-zpool_do_destroy(int argc, char **argv)
-{
- boolean_t force = B_FALSE;
- int c;
- char *pool;
- zpool_handle_t *zhp;
- int ret;
-
- /* check options */
- while ((c = getopt(argc, argv, "f")) != -1) {
- switch (c) {
- case 'f':
- force = B_TRUE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing pool argument\n"));
- usage(B_FALSE);
- }
- if (argc > 1) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- pool = argv[0];
-
- if ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL) {
- /*
- * As a special case, check for use of '/' in the name, and
- * direct the user to use 'zfs destroy' instead.
- */
- if (strchr(pool, '/') != NULL)
- (void) fprintf(stderr, gettext("use 'zfs destroy' to "
- "destroy a dataset\n"));
- return (1);
- }
-
- if (zpool_disable_datasets(zhp, force) != 0) {
- (void) fprintf(stderr, gettext("could not destroy '%s': "
- "could not unmount datasets\n"), zpool_get_name(zhp));
- return (1);
- }
-
- /* The history must be logged as part of the export */
- log_history = B_FALSE;
-
- ret = (zpool_destroy(zhp, history_str) != 0);
-
- zpool_close(zhp);
-
- return (ret);
-}
-
-/*
- * zpool export [-f] <pool> ...
- *
- * -f Forcefully unmount datasets
- *
- * Export the given pools. By default, the command will attempt to cleanly
- * unmount any active datasets within the pool. If the '-f' flag is specified,
- * then the datasets will be forcefully unmounted.
- */
-int
-zpool_do_export(int argc, char **argv)
-{
- boolean_t force = B_FALSE;
- boolean_t hardforce = B_FALSE;
- int c;
- zpool_handle_t *zhp;
- int ret;
- int i;
-
- /* check options */
- while ((c = getopt(argc, argv, "fF")) != -1) {
- switch (c) {
- case 'f':
- force = B_TRUE;
- break;
- case 'F':
- hardforce = B_TRUE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* check arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing pool argument\n"));
- usage(B_FALSE);
- }
-
- ret = 0;
- for (i = 0; i < argc; i++) {
- if ((zhp = zpool_open_canfail(g_zfs, argv[i])) == NULL) {
- ret = 1;
- continue;
- }
-
- if (zpool_disable_datasets(zhp, force) != 0) {
- ret = 1;
- zpool_close(zhp);
- continue;
- }
-
- /* The history must be logged as part of the export */
- log_history = B_FALSE;
-
- if (hardforce) {
- if (zpool_export_force(zhp, history_str) != 0)
- ret = 1;
- } else if (zpool_export(zhp, force, history_str) != 0) {
- ret = 1;
- }
-
- zpool_close(zhp);
- }
-
- return (ret);
-}
-
-/*
- * Given a vdev configuration, determine the maximum width needed for the device
- * name column.
- */
-static int
-max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max,
- int name_flags)
-{
- char *name;
- nvlist_t **child;
- uint_t c, children;
- int ret;
-
- name = zpool_vdev_name(g_zfs, zhp, nv, name_flags | VDEV_NAME_TYPE_ID);
- if (strlen(name) + depth > max)
- max = strlen(name) + depth;
-
- free(name);
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
- &child, &children) == 0) {
- for (c = 0; c < children; c++)
- if ((ret = max_width(zhp, child[c], depth + 2,
- max, name_flags)) > max)
- max = ret;
- }
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
- &child, &children) == 0) {
- for (c = 0; c < children; c++)
- if ((ret = max_width(zhp, child[c], depth + 2,
- max, name_flags)) > max)
- max = ret;
- }
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) == 0) {
- for (c = 0; c < children; c++)
- if ((ret = max_width(zhp, child[c], depth + 2,
- max, name_flags)) > max)
- max = ret;
- }
-
- return (max);
-}
-
-typedef struct spare_cbdata {
- uint64_t cb_guid;
- zpool_handle_t *cb_zhp;
-} spare_cbdata_t;
-
-static boolean_t
-find_vdev(nvlist_t *nv, uint64_t search)
-{
- uint64_t guid;
- nvlist_t **child;
- uint_t c, children;
-
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0 &&
- search == guid)
- return (B_TRUE);
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) == 0) {
- for (c = 0; c < children; c++)
- if (find_vdev(child[c], search))
- return (B_TRUE);
- }
-
- return (B_FALSE);
-}
-
-static int
-find_spare(zpool_handle_t *zhp, void *data)
-{
- spare_cbdata_t *cbp = data;
- nvlist_t *config, *nvroot;
-
- config = zpool_get_config(zhp, NULL);
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
-
- if (find_vdev(nvroot, cbp->cb_guid)) {
- cbp->cb_zhp = zhp;
- return (1);
- }
-
- zpool_close(zhp);
- return (0);
-}
-
-typedef struct status_cbdata {
- int cb_count;
- int cb_name_flags;
- int cb_namewidth;
- boolean_t cb_allpools;
- boolean_t cb_verbose;
- boolean_t cb_explain;
- boolean_t cb_first;
- boolean_t cb_dedup_stats;
- boolean_t cb_print_status;
-} status_cbdata_t;
-
-/*
- * Print out configuration state as requested by status_callback.
- */
-static void
-print_status_config(zpool_handle_t *zhp, status_cbdata_t *cb, const char *name,
- nvlist_t *nv, int depth, boolean_t isspare)
-{
- nvlist_t **child;
- uint_t c, vsc, children;
- pool_scan_stat_t *ps = NULL;
- vdev_stat_t *vs;
- char rbuf[6], wbuf[6], cbuf[6];
- char *vname;
- uint64_t notpresent;
- uint64_t ashift;
- spare_cbdata_t spare_cb;
- const char *state;
- char *type;
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0)
- children = 0;
-
- verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS,
- (uint64_t **)&vs, &vsc) == 0);
-
- verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
-
- if (strcmp(type, VDEV_TYPE_INDIRECT) == 0)
- return;
-
- state = zpool_state_to_name(vs->vs_state, vs->vs_aux);
- if (isspare) {
- /*
- * For hot spares, we use the terms 'INUSE' and 'AVAILABLE' for
- * online drives.
- */
- if (vs->vs_aux == VDEV_AUX_SPARED)
- state = "INUSE";
- else if (vs->vs_state == VDEV_STATE_HEALTHY)
- state = "AVAIL";
- }
-
- (void) printf("\t%*s%-*s %-8s", depth, "", cb->cb_namewidth - depth,
- name, state);
-
- if (!isspare) {
- zfs_nicenum(vs->vs_read_errors, rbuf, sizeof (rbuf));
- zfs_nicenum(vs->vs_write_errors, wbuf, sizeof (wbuf));
- zfs_nicenum(vs->vs_checksum_errors, cbuf, sizeof (cbuf));
- (void) printf(" %5s %5s %5s", rbuf, wbuf, cbuf);
- }
-
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT,
- &notpresent) == 0 ||
- vs->vs_state <= VDEV_STATE_CANT_OPEN) {
- char *path;
- if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0)
- (void) printf(" was %s", path);
- } else if (vs->vs_aux != 0) {
- (void) printf(" ");
-
- switch (vs->vs_aux) {
- case VDEV_AUX_OPEN_FAILED:
- (void) printf(gettext("cannot open"));
- break;
-
- case VDEV_AUX_BAD_GUID_SUM:
- (void) printf(gettext("missing device"));
- break;
-
- case VDEV_AUX_NO_REPLICAS:
- (void) printf(gettext("insufficient replicas"));
- break;
-
- case VDEV_AUX_VERSION_NEWER:
- (void) printf(gettext("newer version"));
- break;
-
- case VDEV_AUX_UNSUP_FEAT:
- (void) printf(gettext("unsupported feature(s)"));
- break;
-
- case VDEV_AUX_ASHIFT_TOO_BIG:
- (void) printf(gettext("unsupported minimum blocksize"));
- break;
-
- case VDEV_AUX_SPARED:
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID,
- &spare_cb.cb_guid) == 0);
- if (zpool_iter(g_zfs, find_spare, &spare_cb) == 1) {
- if (strcmp(zpool_get_name(spare_cb.cb_zhp),
- zpool_get_name(zhp)) == 0)
- (void) printf(gettext("currently in "
- "use"));
- else
- (void) printf(gettext("in use by "
- "pool '%s'"),
- zpool_get_name(spare_cb.cb_zhp));
- zpool_close(spare_cb.cb_zhp);
- } else {
- (void) printf(gettext("currently in use"));
- }
- break;
-
- case VDEV_AUX_ERR_EXCEEDED:
- (void) printf(gettext("too many errors"));
- break;
-
- case VDEV_AUX_IO_FAILURE:
- (void) printf(gettext("experienced I/O failures"));
- break;
-
- case VDEV_AUX_BAD_LOG:
- (void) printf(gettext("bad intent log"));
- break;
-
- case VDEV_AUX_EXTERNAL:
- (void) printf(gettext("external device fault"));
- break;
-
- case VDEV_AUX_SPLIT_POOL:
- (void) printf(gettext("split into new pool"));
- break;
-
- case VDEV_AUX_ACTIVE:
- (void) printf(gettext("currently in use"));
- break;
-
- case VDEV_AUX_CHILDREN_OFFLINE:
- (void) printf(gettext("all children offline"));
- break;
-
- default:
- (void) printf(gettext("corrupted data"));
- break;
- }
- } else if (children == 0 && !isspare &&
- VDEV_STAT_VALID(vs_physical_ashift, vsc) &&
- vs->vs_configured_ashift < vs->vs_physical_ashift) {
- (void) printf(
- gettext(" block size: %dB configured, %dB native"),
- 1 << vs->vs_configured_ashift, 1 << vs->vs_physical_ashift);
- }
-
- (void) nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_SCAN_STATS,
- (uint64_t **)&ps, &c);
-
- if (ps != NULL && ps->pss_state == DSS_SCANNING &&
- vs->vs_scan_processed != 0 && children == 0) {
- (void) printf(gettext(" (%s)"),
- (ps->pss_func == POOL_SCAN_RESILVER) ?
- "resilvering" : "repairing");
- }
-
- if ((vs->vs_initialize_state == VDEV_INITIALIZE_ACTIVE ||
- vs->vs_initialize_state == VDEV_INITIALIZE_SUSPENDED ||
- vs->vs_initialize_state == VDEV_INITIALIZE_COMPLETE) &&
- !vs->vs_scan_removing) {
- char zbuf[1024];
- char tbuf[256];
- struct tm zaction_ts;
-
- time_t t = vs->vs_initialize_action_time;
- int initialize_pct = 100;
- if (vs->vs_initialize_state != VDEV_INITIALIZE_COMPLETE) {
- initialize_pct = (vs->vs_initialize_bytes_done * 100 /
- (vs->vs_initialize_bytes_est + 1));
- }
-
- (void) localtime_r(&t, &zaction_ts);
- (void) strftime(tbuf, sizeof (tbuf), "%c", &zaction_ts);
-
- switch (vs->vs_initialize_state) {
- case VDEV_INITIALIZE_SUSPENDED:
- (void) snprintf(zbuf, sizeof (zbuf),
- ", suspended, started at %s", tbuf);
- break;
- case VDEV_INITIALIZE_ACTIVE:
- (void) snprintf(zbuf, sizeof (zbuf),
- ", started at %s", tbuf);
- break;
- case VDEV_INITIALIZE_COMPLETE:
- (void) snprintf(zbuf, sizeof (zbuf),
- ", completed at %s", tbuf);
- break;
- }
-
- (void) printf(gettext(" (%d%% initialized%s)"),
- initialize_pct, zbuf);
- }
-
- (void) printf("\n");
-
- for (c = 0; c < children; c++) {
- uint64_t islog = B_FALSE, ishole = B_FALSE;
-
- /* Don't print logs or holes here */
- (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
- &islog);
- (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_HOLE,
- &ishole);
- if (islog || ishole)
- continue;
- /* Only print normal classes here */
- if (nvlist_exists(child[c], ZPOOL_CONFIG_ALLOCATION_BIAS))
- continue;
-
- vname = zpool_vdev_name(g_zfs, zhp, child[c],
- cb->cb_name_flags | VDEV_NAME_TYPE_ID);
- print_status_config(zhp, cb, vname, child[c], depth + 2,
- isspare);
- free(vname);
- }
-}
-
-/*
- * Print the configuration of an exported pool. Iterate over all vdevs in the
- * pool, printing out the name and status for each one.
- */
-static void
-print_import_config(status_cbdata_t *cb, const char *name, nvlist_t *nv,
- int depth)
-{
- nvlist_t **child;
- uint_t c, children;
- vdev_stat_t *vs;
- char *type, *vname;
-
- verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
- if (strcmp(type, VDEV_TYPE_MISSING) == 0 ||
- strcmp(type, VDEV_TYPE_HOLE) == 0)
- return;
-
- verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS,
- (uint64_t **)&vs, &c) == 0);
-
- (void) printf("\t%*s%-*s", depth, "", cb->cb_namewidth - depth, name);
- (void) printf(" %s", zpool_state_to_name(vs->vs_state, vs->vs_aux));
-
- if (vs->vs_aux != 0) {
- (void) printf(" ");
-
- switch (vs->vs_aux) {
- case VDEV_AUX_OPEN_FAILED:
- (void) printf(gettext("cannot open"));
- break;
-
- case VDEV_AUX_BAD_GUID_SUM:
- (void) printf(gettext("missing device"));
- break;
-
- case VDEV_AUX_NO_REPLICAS:
- (void) printf(gettext("insufficient replicas"));
- break;
-
- case VDEV_AUX_VERSION_NEWER:
- (void) printf(gettext("newer version"));
- break;
-
- case VDEV_AUX_UNSUP_FEAT:
- (void) printf(gettext("unsupported feature(s)"));
- break;
-
- case VDEV_AUX_ERR_EXCEEDED:
- (void) printf(gettext("too many errors"));
- break;
-
- case VDEV_AUX_ACTIVE:
- (void) printf(gettext("currently in use"));
- break;
-
- case VDEV_AUX_CHILDREN_OFFLINE:
- (void) printf(gettext("all children offline"));
- break;
-
- default:
- (void) printf(gettext("corrupted data"));
- break;
- }
- }
- (void) printf("\n");
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0)
- return;
-
- for (c = 0; c < children; c++) {
- uint64_t is_log = B_FALSE;
-
- (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
- &is_log);
- if (is_log)
- continue;
- if (nvlist_exists(child[c], ZPOOL_CONFIG_ALLOCATION_BIAS))
- continue;
-
- vname = zpool_vdev_name(g_zfs, NULL, child[c],
- cb->cb_name_flags | VDEV_NAME_TYPE_ID);
- print_import_config(cb, vname, child[c], depth + 2);
- free(vname);
- }
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
- &child, &children) == 0) {
- (void) printf(gettext("\tcache\n"));
- for (c = 0; c < children; c++) {
- vname = zpool_vdev_name(g_zfs, NULL, child[c],
- cb->cb_name_flags);
- (void) printf("\t %s\n", vname);
- free(vname);
- }
- }
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
- &child, &children) == 0) {
- (void) printf(gettext("\tspares\n"));
- for (c = 0; c < children; c++) {
- vname = zpool_vdev_name(g_zfs, NULL, child[c],
- cb->cb_name_flags);
- (void) printf("\t %s\n", vname);
- free(vname);
- }
- }
-}
-
-/*
- * Print specialized class vdevs.
- *
- * These are recorded as top level vdevs in the main pool child array
- * but with "is_log" set to 1 or an "alloc_bias" string. We use either
- * print_status_config() or print_import_config() to print the top level
- * class vdevs then any of their children (eg mirrored slogs) are printed
- * recursively - which works because only the top level vdev is marked.
- */
-static void
-print_class_vdevs(zpool_handle_t *zhp, status_cbdata_t *cb, nvlist_t *nv,
- const char *class)
-{
- uint_t c, children;
- nvlist_t **child;
- boolean_t printed = B_FALSE;
-
- assert(zhp != NULL || !cb->cb_verbose);
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
- &children) != 0)
- return;
-
- for (c = 0; c < children; c++) {
- uint64_t is_log = B_FALSE;
- char *bias = NULL;
- char *type = NULL;
-
- (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
- &is_log);
-
- if (is_log) {
- bias = VDEV_ALLOC_CLASS_LOGS;
- } else {
- (void) nvlist_lookup_string(child[c],
- ZPOOL_CONFIG_ALLOCATION_BIAS, &bias);
- (void) nvlist_lookup_string(child[c],
- ZPOOL_CONFIG_TYPE, &type);
- }
-
- if (bias == NULL || strcmp(bias, class) != 0)
- continue;
- if (!is_log && strcmp(type, VDEV_TYPE_INDIRECT) == 0)
- continue;
-
- if (!printed) {
- (void) printf("\t%s\t\n", gettext(class));
- printed = B_TRUE;
- }
-
- char *name = zpool_vdev_name(g_zfs, zhp, child[c],
- cb->cb_name_flags | VDEV_NAME_TYPE_ID);
- if (cb->cb_print_status)
- print_status_config(zhp, cb, name, child[c], 2,
- B_FALSE);
- else
- print_import_config(cb, name, child[c], 2);
- free(name);
- }
-}
-
-/*
- * Display the status for the given pool.
- */
-static void
-show_import(nvlist_t *config)
-{
- uint64_t pool_state;
- vdev_stat_t *vs;
- char *name;
- uint64_t guid;
- uint64_t hostid = 0;
- char *msgid;
- char *hostname = "unknown";
- nvlist_t *nvroot, *nvinfo;
- int reason;
- const char *health;
- uint_t vsc;
- char *comment;
- status_cbdata_t cb = { 0 };
-
- verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
- &name) == 0);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
- &guid) == 0);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
- &pool_state) == 0);
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
-
- verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS,
- (uint64_t **)&vs, &vsc) == 0);
- health = zpool_state_to_name(vs->vs_state, vs->vs_aux);
-
- reason = zpool_import_status(config, &msgid);
-
- (void) printf(gettext(" pool: %s\n"), name);
- (void) printf(gettext(" id: %llu\n"), (u_longlong_t)guid);
- (void) printf(gettext(" state: %s"), health);
- if (pool_state == POOL_STATE_DESTROYED)
- (void) printf(gettext(" (DESTROYED)"));
- (void) printf("\n");
-
- switch (reason) {
- case ZPOOL_STATUS_MISSING_DEV_R:
- case ZPOOL_STATUS_MISSING_DEV_NR:
- case ZPOOL_STATUS_BAD_GUID_SUM:
- (void) printf(gettext(" status: One or more devices are "
- "missing from the system.\n"));
- break;
-
- case ZPOOL_STATUS_CORRUPT_LABEL_R:
- case ZPOOL_STATUS_CORRUPT_LABEL_NR:
- (void) printf(gettext(" status: One or more devices contains "
- "corrupted data.\n"));
- break;
-
- case ZPOOL_STATUS_CORRUPT_DATA:
- (void) printf(
- gettext(" status: The pool data is corrupted.\n"));
- break;
-
- case ZPOOL_STATUS_OFFLINE_DEV:
- (void) printf(gettext(" status: One or more devices "
- "are offlined.\n"));
- break;
-
- case ZPOOL_STATUS_CORRUPT_POOL:
- (void) printf(gettext(" status: The pool metadata is "
- "corrupted.\n"));
- break;
-
- case ZPOOL_STATUS_VERSION_OLDER:
- (void) printf(gettext(" status: The pool is formatted using a "
- "legacy on-disk version.\n"));
- break;
-
- case ZPOOL_STATUS_VERSION_NEWER:
- (void) printf(gettext(" status: The pool is formatted using an "
- "incompatible version.\n"));
- break;
-
- case ZPOOL_STATUS_FEAT_DISABLED:
- (void) printf(gettext(" status: Some supported features are "
- "not enabled on the pool.\n"));
- break;
-
- case ZPOOL_STATUS_UNSUP_FEAT_READ:
- (void) printf(gettext("status: The pool uses the following "
- "feature(s) not supported on this system:\n"));
- zpool_print_unsup_feat(config);
- break;
-
- case ZPOOL_STATUS_UNSUP_FEAT_WRITE:
- (void) printf(gettext("status: The pool can only be accessed "
- "in read-only mode on this system. It\n\tcannot be "
- "accessed in read-write mode because it uses the "
- "following\n\tfeature(s) not supported on this system:\n"));
- zpool_print_unsup_feat(config);
- break;
-
- case ZPOOL_STATUS_HOSTID_ACTIVE:
- (void) printf(gettext(" status: The pool is currently "
- "imported by another system.\n"));
- break;
-
- case ZPOOL_STATUS_HOSTID_REQUIRED:
- (void) printf(gettext(" status: The pool has the "
- "multihost property on. It cannot\n\tbe safely imported "
- "when the system hostid is not set.\n"));
- break;
-
- case ZPOOL_STATUS_HOSTID_MISMATCH:
- (void) printf(gettext(" status: The pool was last accessed by "
- "another system.\n"));
- break;
-
- case ZPOOL_STATUS_FAULTED_DEV_R:
- case ZPOOL_STATUS_FAULTED_DEV_NR:
- (void) printf(gettext(" status: One or more devices are "
- "faulted.\n"));
- break;
-
- case ZPOOL_STATUS_BAD_LOG:
- (void) printf(gettext(" status: An intent log record cannot be "
- "read.\n"));
- break;
-
- case ZPOOL_STATUS_RESILVERING:
- (void) printf(gettext(" status: One or more devices were being "
- "resilvered.\n"));
- break;
-
- case ZPOOL_STATUS_NON_NATIVE_ASHIFT:
- (void) printf(gettext("status: One or more devices were "
- "configured to use a non-native block size.\n"
- "\tExpect reduced performance.\n"));
- break;
-
- default:
- /*
- * No other status can be seen when importing pools.
- */
- assert(reason == ZPOOL_STATUS_OK);
- }
-
- /*
- * Print out an action according to the overall state of the pool.
- */
- if (vs->vs_state == VDEV_STATE_HEALTHY) {
- if (reason == ZPOOL_STATUS_VERSION_OLDER ||
- reason == ZPOOL_STATUS_FEAT_DISABLED) {
- (void) printf(gettext(" action: The pool can be "
- "imported using its name or numeric identifier, "
- "though\n\tsome features will not be available "
- "without an explicit 'zpool upgrade'.\n"));
- } else if (reason == ZPOOL_STATUS_HOSTID_MISMATCH) {
- (void) printf(gettext(" action: The pool can be "
- "imported using its name or numeric "
- "identifier and\n\tthe '-f' flag.\n"));
- } else {
- (void) printf(gettext(" action: The pool can be "
- "imported using its name or numeric "
- "identifier.\n"));
- }
- } else if (vs->vs_state == VDEV_STATE_DEGRADED) {
- (void) printf(gettext(" action: The pool can be imported "
- "despite missing or damaged devices. The\n\tfault "
- "tolerance of the pool may be compromised if imported.\n"));
- } else {
- switch (reason) {
- case ZPOOL_STATUS_VERSION_NEWER:
- (void) printf(gettext(" action: The pool cannot be "
- "imported. Access the pool on a system running "
- "newer\n\tsoftware, or recreate the pool from "
- "backup.\n"));
- break;
- case ZPOOL_STATUS_UNSUP_FEAT_READ:
- (void) printf(gettext("action: The pool cannot be "
- "imported. Access the pool on a system that "
- "supports\n\tthe required feature(s), or recreate "
- "the pool from backup.\n"));
- break;
- case ZPOOL_STATUS_UNSUP_FEAT_WRITE:
- (void) printf(gettext("action: The pool cannot be "
- "imported in read-write mode. Import the pool "
- "with\n"
- "\t\"-o readonly=on\", access the pool on a system "
- "that supports the\n\trequired feature(s), or "
- "recreate the pool from backup.\n"));
- break;
- case ZPOOL_STATUS_MISSING_DEV_R:
- case ZPOOL_STATUS_MISSING_DEV_NR:
- case ZPOOL_STATUS_BAD_GUID_SUM:
- (void) printf(gettext(" action: The pool cannot be "
- "imported. Attach the missing\n\tdevices and try "
- "again.\n"));
- break;
- case ZPOOL_STATUS_HOSTID_ACTIVE:
- VERIFY0(nvlist_lookup_nvlist(config,
- ZPOOL_CONFIG_LOAD_INFO, &nvinfo));
-
- if (nvlist_exists(nvinfo, ZPOOL_CONFIG_MMP_HOSTNAME))
- hostname = fnvlist_lookup_string(nvinfo,
- ZPOOL_CONFIG_MMP_HOSTNAME);
-
- if (nvlist_exists(nvinfo, ZPOOL_CONFIG_MMP_HOSTID))
- hostid = fnvlist_lookup_uint64(nvinfo,
- ZPOOL_CONFIG_MMP_HOSTID);
-
- (void) printf(gettext(" action: The pool must be "
- "exported from %s (hostid=%lx)\n\tbefore it "
- "can be safely imported.\n"), hostname,
- (unsigned long) hostid);
- break;
- case ZPOOL_STATUS_HOSTID_REQUIRED:
- (void) printf(gettext(" action: Check the SMF "
- "svc:/system/hostid service.\n"));
- break;
- default:
- (void) printf(gettext(" action: The pool cannot be "
- "imported due to damaged devices or data.\n"));
- }
- }
-
- /* Print the comment attached to the pool. */
- if (nvlist_lookup_string(config, ZPOOL_CONFIG_COMMENT, &comment) == 0)
- (void) printf(gettext("comment: %s\n"), comment);
-
- /*
- * If the state is "closed" or "can't open", and the aux state
- * is "corrupt data":
- */
- if (((vs->vs_state == VDEV_STATE_CLOSED) ||
- (vs->vs_state == VDEV_STATE_CANT_OPEN)) &&
- (vs->vs_aux == VDEV_AUX_CORRUPT_DATA)) {
- if (pool_state == POOL_STATE_DESTROYED)
- (void) printf(gettext("\tThe pool was destroyed, "
- "but can be imported using the '-Df' flags.\n"));
- else if (pool_state != POOL_STATE_EXPORTED)
- (void) printf(gettext("\tThe pool may be active on "
- "another system, but can be imported using\n\t"
- "the '-f' flag.\n"));
- }
-
- if (msgid != NULL)
- (void) printf(gettext(" see: http://illumos.org/msg/%s\n"),
- msgid);
-
- (void) printf(gettext(" config:\n\n"));
-
- cb.cb_namewidth = max_width(NULL, nvroot, 0, 0, 0);
- if (cb.cb_namewidth < 10)
- cb.cb_namewidth = 10;
-
- print_import_config(&cb, name, nvroot, 0);
-
- print_class_vdevs(NULL, &cb, nvroot, VDEV_ALLOC_BIAS_DEDUP);
- print_class_vdevs(NULL, &cb, nvroot, VDEV_ALLOC_BIAS_SPECIAL);
- print_class_vdevs(NULL, &cb, nvroot, VDEV_ALLOC_CLASS_LOGS);
-
- if (reason == ZPOOL_STATUS_BAD_GUID_SUM) {
- (void) printf(gettext("\n\tAdditional devices are known to "
- "be part of this pool, though their\n\texact "
- "configuration cannot be determined.\n"));
- }
-}
-
-static boolean_t
-zfs_force_import_required(nvlist_t *config)
-{
- uint64_t state;
- uint64_t hostid = 0;
- nvlist_t *nvinfo;
-
- state = fnvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE);
- (void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID, &hostid);
-
- if (state != POOL_STATE_EXPORTED && hostid != get_system_hostid())
- return (B_TRUE);
-
- nvinfo = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO);
- if (nvlist_exists(nvinfo, ZPOOL_CONFIG_MMP_STATE)) {
- mmp_state_t mmp_state = fnvlist_lookup_uint64(nvinfo,
- ZPOOL_CONFIG_MMP_STATE);
-
- if (mmp_state != MMP_STATE_INACTIVE)
- return (B_TRUE);
- }
-
- return (B_FALSE);
-}
-
-/*
- * Perform the import for the given configuration. This passes the heavy
- * lifting off to zpool_import_props(), and then mounts the datasets contained
- * within the pool.
- */
-static int
-do_import(nvlist_t *config, const char *newname, const char *mntopts,
- nvlist_t *props, int flags)
-{
- zpool_handle_t *zhp;
- char *name;
- uint64_t version;
-
- name = fnvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME);
- version = fnvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION);
-
- if (!SPA_VERSION_IS_SUPPORTED(version)) {
- (void) fprintf(stderr, gettext("cannot import '%s': pool "
- "is formatted using an unsupported ZFS version\n"), name);
- return (1);
- } else if (zfs_force_import_required(config) &&
- !(flags & ZFS_IMPORT_ANY_HOST)) {
- mmp_state_t mmp_state = MMP_STATE_INACTIVE;
- nvlist_t *nvinfo;
-
- nvinfo = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO);
- if (nvlist_exists(nvinfo, ZPOOL_CONFIG_MMP_STATE))
- mmp_state = fnvlist_lookup_uint64(nvinfo,
- ZPOOL_CONFIG_MMP_STATE);
-
- if (mmp_state == MMP_STATE_ACTIVE) {
- char *hostname = "<unknown>";
- uint64_t hostid = 0;
-
- if (nvlist_exists(nvinfo, ZPOOL_CONFIG_MMP_HOSTNAME))
- hostname = fnvlist_lookup_string(nvinfo,
- ZPOOL_CONFIG_MMP_HOSTNAME);
-
- if (nvlist_exists(nvinfo, ZPOOL_CONFIG_MMP_HOSTID))
- hostid = fnvlist_lookup_uint64(nvinfo,
- ZPOOL_CONFIG_MMP_HOSTID);
-
- (void) fprintf(stderr, gettext("cannot import '%s': "
- "pool is imported on %s (hostid: "
- "0x%lx)\nExport the pool on the other system, "
- "then run 'zpool import'.\n"),
- name, hostname, (unsigned long) hostid);
- } else if (mmp_state == MMP_STATE_NO_HOSTID) {
- (void) fprintf(stderr, gettext("Cannot import '%s': "
- "pool has the multihost property on and the\n"
- "system's hostid is not set.\n"), name);
- } else {
- char *hostname = "<unknown>";
- uint64_t timestamp = 0;
- uint64_t hostid = 0;
-
- if (nvlist_exists(config, ZPOOL_CONFIG_HOSTNAME))
- hostname = fnvlist_lookup_string(config,
- ZPOOL_CONFIG_HOSTNAME);
-
- if (nvlist_exists(config, ZPOOL_CONFIG_TIMESTAMP))
- timestamp = fnvlist_lookup_uint64(config,
- ZPOOL_CONFIG_TIMESTAMP);
-
- if (nvlist_exists(config, ZPOOL_CONFIG_HOSTID))
- hostid = fnvlist_lookup_uint64(config,
- ZPOOL_CONFIG_HOSTID);
-
- (void) fprintf(stderr, gettext("cannot import '%s': "
- "pool was previously in use from another system.\n"
- "Last accessed by %s (hostid=%lx) at %s"
- "The pool can be imported, use 'zpool import -f' "
- "to import the pool.\n"), name, hostname,
- (unsigned long)hostid, ctime((time_t *)&timestamp));
-
- }
-
- return (1);
- }
-
- if (zpool_import_props(g_zfs, config, newname, props, flags) != 0)
- return (1);
-
- if (newname != NULL)
- name = (char *)newname;
-
- if ((zhp = zpool_open_canfail(g_zfs, name)) == NULL)
- return (1);
-
- if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL &&
- !(flags & ZFS_IMPORT_ONLY) &&
- zpool_enable_datasets(zhp, mntopts, 0) != 0) {
- zpool_close(zhp);
- return (1);
- }
-
- zpool_close(zhp);
- return (0);
-}
-
-/*
- * zpool checkpoint <pool>
- * checkpoint --discard <pool>
- *
- * -d Discard the checkpoint from a checkpointed
- * --discard pool.
- *
- * Checkpoints the specified pool, by taking a "snapshot" of its
- * current state. A pool can only have one checkpoint at a time.
- */
-int
-zpool_do_checkpoint(int argc, char **argv)
-{
- boolean_t discard;
- char *pool;
- zpool_handle_t *zhp;
- int c, err;
-
- struct option long_options[] = {
- {"discard", no_argument, NULL, 'd'},
- {0, 0, 0, 0}
- };
-
- discard = B_FALSE;
- while ((c = getopt_long(argc, argv, ":d", long_options, NULL)) != -1) {
- switch (c) {
- case 'd':
- discard = B_TRUE;
- 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 pool argument\n"));
- usage(B_FALSE);
- }
-
- if (argc > 1) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- pool = argv[0];
-
- if ((zhp = zpool_open(g_zfs, pool)) == NULL) {
- /* As a special case, check for use of '/' in the name */
- if (strchr(pool, '/') != NULL)
- (void) fprintf(stderr, gettext("'zpool checkpoint' "
- "doesn't work on datasets. To save the state "
- "of a dataset from a specific point in time "
- "please use 'zfs snapshot'\n"));
- return (1);
- }
-
- if (discard)
- err = (zpool_discard_checkpoint(zhp) != 0);
- else
- err = (zpool_checkpoint(zhp) != 0);
-
- zpool_close(zhp);
-
- return (err);
-}
-
-#define CHECKPOINT_OPT 1024
-
-/*
- * zpool import [-d dir] [-D]
- * import [-o mntopts] [-o prop=value] ... [-R root] [-D]
- * [-d dir | -c cachefile] [-f] -a
- * import [-o mntopts] [-o prop=value] ... [-R root] [-D]
- * [-d dir | -c cachefile] [-f] [-n] [-F] [-t]
- * <pool | id> [newpool]
- *
- * -c Read pool information from a cachefile instead of searching
- * devices.
- *
- * -d Scan in a specific directory, other than /dev/dsk. More than
- * one directory can be specified using multiple '-d' options.
- *
- * -D Scan for previously destroyed pools or import all or only
- * specified destroyed pools.
- *
- * -R Temporarily import the pool, with all mountpoints relative to
- * the given root. The pool will remain exported when the machine
- * is rebooted.
- *
- * -V Import even in the presence of faulted vdevs. This is an
- * intentionally undocumented option for testing purposes, and
- * treats the pool configuration as complete, leaving any bad
- * vdevs in the FAULTED state. In other words, it does verbatim
- * import.
- *
- * -f Force import, even if it appears that the pool is active.
- *
- * -F Attempt rewind if necessary.
- *
- * -n See if rewind would work, but don't actually rewind.
- *
- * -N Import the pool but don't mount datasets.
- *
- * -t Use newpool as a temporary pool name instead of renaming
- * the pool.
- *
- * -T Specify a starting txg to use for import. This option is
- * intentionally undocumented option for testing purposes.
- *
- * -a Import all pools found.
- *
- * -o Set property=value and/or temporary mount options (without '=').
- *
- * --rewind-to-checkpoint
- * Import the pool and revert back to the checkpoint.
- *
- * The import command scans for pools to import, and import pools based on pool
- * name and GUID. The pool can also be renamed as part of the import process.
- */
-int
-zpool_do_import(int argc, char **argv)
-{
- char **searchdirs = NULL;
- int nsearch = 0;
- int c;
- int err = 0;
- nvlist_t *pools = NULL;
- boolean_t do_all = B_FALSE;
- boolean_t do_destroyed = B_FALSE;
- char *mntopts = NULL;
- nvpair_t *elem;
- nvlist_t *config;
- uint64_t searchguid = 0;
- char *searchname = NULL;
- char *propval;
- nvlist_t *found_config;
- nvlist_t *policy = NULL;
- nvlist_t *props = NULL;
- boolean_t first;
- int flags = ZFS_IMPORT_NORMAL;
- uint32_t rewind_policy = ZPOOL_NO_REWIND;
- boolean_t dryrun = B_FALSE;
- boolean_t do_rewind = B_FALSE;
- boolean_t xtreme_rewind = B_FALSE;
- uint64_t pool_state, txg = -1ULL;
- char *cachefile = NULL;
- importargs_t idata = { 0 };
- char *endptr;
-
-
- struct option long_options[] = {
- {"rewind-to-checkpoint", no_argument, NULL, CHECKPOINT_OPT},
- {0, 0, 0, 0}
- };
-
- /* check options */
- while ((c = getopt_long(argc, argv, ":aCc:d:DEfFmnNo:rR:tT:VX",
- long_options, NULL)) != -1) {
- switch (c) {
- case 'a':
- do_all = B_TRUE;
- break;
- case 'c':
- cachefile = optarg;
- break;
- case 'd':
- if (searchdirs == NULL) {
- searchdirs = safe_malloc(sizeof (char *));
- } else {
- char **tmp = safe_malloc((nsearch + 1) *
- sizeof (char *));
- bcopy(searchdirs, tmp, nsearch *
- sizeof (char *));
- free(searchdirs);
- searchdirs = tmp;
- }
- searchdirs[nsearch++] = optarg;
- break;
- case 'D':
- do_destroyed = B_TRUE;
- break;
- case 'f':
- flags |= ZFS_IMPORT_ANY_HOST;
- break;
- case 'F':
- do_rewind = B_TRUE;
- break;
- case 'm':
- flags |= ZFS_IMPORT_MISSING_LOG;
- break;
- case 'n':
- dryrun = B_TRUE;
- break;
- case 'N':
- flags |= ZFS_IMPORT_ONLY;
- break;
- case 'o':
- if ((propval = strchr(optarg, '=')) != NULL) {
- *propval = '\0';
- propval++;
- if (add_prop_list(optarg, propval,
- &props, B_TRUE))
- goto error;
- } else {
- mntopts = optarg;
- }
- break;
- case 'R':
- if (add_prop_list(zpool_prop_to_name(
- ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE))
- goto error;
- if (add_prop_list_default(zpool_prop_to_name(
- ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE))
- goto error;
- break;
- case 't':
- flags |= ZFS_IMPORT_TEMP_NAME;
- if (add_prop_list_default(zpool_prop_to_name(
- ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE))
- goto error;
- break;
- case 'T':
- errno = 0;
- txg = strtoull(optarg, &endptr, 0);
- if (errno != 0 || *endptr != '\0') {
- (void) fprintf(stderr,
- gettext("invalid txg value\n"));
- usage(B_FALSE);
- }
- rewind_policy = ZPOOL_DO_REWIND | ZPOOL_EXTREME_REWIND;
- break;
- case 'V':
- flags |= ZFS_IMPORT_VERBATIM;
- break;
- case 'X':
- xtreme_rewind = B_TRUE;
- break;
- case CHECKPOINT_OPT:
- flags |= ZFS_IMPORT_CHECKPOINT;
- 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 (cachefile && nsearch != 0) {
- (void) fprintf(stderr, gettext("-c is incompatible with -d\n"));
- usage(B_FALSE);
- }
-
- if ((dryrun || xtreme_rewind) && !do_rewind) {
- (void) fprintf(stderr,
- gettext("-n or -X only meaningful with -F\n"));
- usage(B_FALSE);
- }
- if (dryrun)
- rewind_policy = ZPOOL_TRY_REWIND;
- else if (do_rewind)
- rewind_policy = ZPOOL_DO_REWIND;
- if (xtreme_rewind)
- rewind_policy |= ZPOOL_EXTREME_REWIND;
-
- /* In the future, we can capture further policy and include it here */
- if (nvlist_alloc(&policy, NV_UNIQUE_NAME, 0) != 0 ||
- nvlist_add_uint64(policy, ZPOOL_LOAD_REQUEST_TXG, txg) != 0 ||
- nvlist_add_uint32(policy, ZPOOL_LOAD_REWIND_POLICY,
- rewind_policy) != 0)
- goto error;
-
- if (searchdirs == NULL) {
- searchdirs = safe_malloc(sizeof (char *));
- searchdirs[0] = "/dev";
- nsearch = 1;
- }
-
- /* check argument count */
- if (do_all) {
- if (argc != 0) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
- } else {
- if (argc > 2) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- /*
- * Check for the SYS_CONFIG privilege. We do this explicitly
- * here because otherwise any attempt to discover pools will
- * silently fail.
- */
- if (argc == 0 && !priv_ineffect(PRIV_SYS_CONFIG)) {
- (void) fprintf(stderr, gettext("cannot "
- "discover pools: permission denied\n"));
- free(searchdirs);
- nvlist_free(policy);
- return (1);
- }
- }
-
- /*
- * Depending on the arguments given, we do one of the following:
- *
- * <none> Iterate through all pools and display information about
- * each one.
- *
- * -a Iterate through all pools and try to import each one.
- *
- * <id> Find the pool that corresponds to the given GUID/pool
- * name and import that one.
- *
- * -D Above options applies only to destroyed pools.
- */
- if (argc != 0) {
- char *endptr;
-
- errno = 0;
- searchguid = strtoull(argv[0], &endptr, 10);
- if (errno != 0 || *endptr != '\0') {
- searchname = argv[0];
- searchguid = 0;
- }
- found_config = NULL;
-
- /*
- * User specified a name or guid. Ensure it's unique.
- */
- idata.unique = B_TRUE;
- }
-
-
- idata.path = searchdirs;
- idata.paths = nsearch;
- idata.poolname = searchname;
- idata.guid = searchguid;
- idata.cachefile = cachefile;
- idata.policy = policy;
-
- pools = zpool_search_import(g_zfs, &idata);
-
- if (pools != NULL && idata.exists &&
- (argc == 1 || strcmp(argv[0], argv[1]) == 0)) {
- (void) fprintf(stderr, gettext("cannot import '%s': "
- "a pool with that name already exists\n"),
- argv[0]);
- (void) fprintf(stderr, gettext("use the form 'zpool import "
- "[-t] <pool | id> <newpool>' to give it a new temporary "
- "or permanent name\n"));
- err = 1;
- } else if (pools == NULL && idata.exists) {
- (void) fprintf(stderr, gettext("cannot import '%s': "
- "a pool with that name is already created/imported,\n"),
- argv[0]);
- (void) fprintf(stderr, gettext("and no additional pools "
- "with that name were found\n"));
- err = 1;
- } else if (pools == NULL) {
- if (argc != 0) {
- (void) fprintf(stderr, gettext("cannot import '%s': "
- "no such pool available\n"), argv[0]);
- }
- err = 1;
- }
-
- if (err == 1) {
- free(searchdirs);
- nvlist_free(policy);
- return (1);
- }
-
- /*
- * At this point we have a list of import candidate configs. Even if
- * we were searching by pool name or guid, we still need to
- * post-process the list to deal with pool state and possible
- * duplicate names.
- */
- err = 0;
- elem = NULL;
- first = B_TRUE;
- while ((elem = nvlist_next_nvpair(pools, elem)) != NULL) {
-
- verify(nvpair_value_nvlist(elem, &config) == 0);
-
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
- &pool_state) == 0);
- if (!do_destroyed && pool_state == POOL_STATE_DESTROYED)
- continue;
- if (do_destroyed && pool_state != POOL_STATE_DESTROYED)
- continue;
-
- verify(nvlist_add_nvlist(config, ZPOOL_LOAD_POLICY,
- policy) == 0);
-
- if (argc == 0) {
- if (first)
- first = B_FALSE;
- else if (!do_all)
- (void) printf("\n");
-
- if (do_all) {
- err |= do_import(config, NULL, mntopts,
- props, flags);
- } else {
- show_import(config);
- }
- } else if (searchname != NULL) {
- char *name;
-
- /*
- * We are searching for a pool based on name.
- */
- verify(nvlist_lookup_string(config,
- ZPOOL_CONFIG_POOL_NAME, &name) == 0);
-
- if (strcmp(name, searchname) == 0) {
- if (found_config != NULL) {
- (void) fprintf(stderr, gettext(
- "cannot import '%s': more than "
- "one matching pool\n"), searchname);
- (void) fprintf(stderr, gettext(
- "import by numeric ID instead\n"));
- err = B_TRUE;
- }
- found_config = config;
- }
- } else {
- uint64_t guid;
-
- /*
- * Search for a pool by guid.
- */
- verify(nvlist_lookup_uint64(config,
- ZPOOL_CONFIG_POOL_GUID, &guid) == 0);
-
- if (guid == searchguid)
- found_config = config;
- }
- }
-
- /*
- * If we were searching for a specific pool, verify that we found a
- * pool, and then do the import.
- */
- if (argc != 0 && err == 0) {
- if (found_config == NULL) {
- (void) fprintf(stderr, gettext("cannot import '%s': "
- "no such pool available\n"), argv[0]);
- err = B_TRUE;
- } else {
- err |= do_import(found_config, argc == 1 ? NULL :
- argv[1], mntopts, props, flags);
- }
- }
-
- /*
- * If we were just looking for pools, report an error if none were
- * found.
- */
- if (argc == 0 && first)
- (void) fprintf(stderr,
- gettext("no pools available to import\n"));
-
-error:
- nvlist_free(props);
- nvlist_free(pools);
- nvlist_free(policy);
- free(searchdirs);
-
- return (err ? 1 : 0);
-}
-
-/*
- * zpool sync [-f] [pool] ...
- *
- * -f (undocumented) force uberblock (and config including zpool cache file)
- * update.
- *
- * Sync the specified pool(s).
- * Without arguments "zpool sync" will sync all pools.
- * This command initiates TXG sync(s) and will return after the TXG(s) commit.
- *
- */
-static int
-zpool_do_sync(int argc, char **argv)
-{
- int ret;
- boolean_t force = B_FALSE;
-
- /* check options */
- while ((ret = getopt(argc, argv, "f")) != -1) {
- switch (ret) {
- case 'f':
- force = B_TRUE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* if argc == 0 we will execute zpool_sync_one on all pools */
- ret = for_each_pool(argc, argv, B_FALSE, NULL, zpool_sync_one, &force);
-
- return (ret);
-}
-
-typedef struct iostat_cbdata {
- boolean_t cb_verbose;
- int cb_name_flags;
- int cb_namewidth;
- int cb_iteration;
- boolean_t cb_scripted;
- zpool_list_t *cb_list;
-} iostat_cbdata_t;
-
-static void
-print_iostat_separator(iostat_cbdata_t *cb)
-{
- int i = 0;
-
- for (i = 0; i < cb->cb_namewidth; i++)
- (void) printf("-");
- (void) printf(" ----- ----- ----- ----- ----- -----\n");
-}
-
-static void
-print_iostat_header(iostat_cbdata_t *cb)
-{
- (void) printf("%*s capacity operations bandwidth\n",
- cb->cb_namewidth, "");
- (void) printf("%-*s alloc free read write read write\n",
- cb->cb_namewidth, "pool");
- print_iostat_separator(cb);
-}
-
-/*
- * Display a single statistic.
- */
-static void
-print_one_stat(uint64_t value)
-{
- char buf[64];
-
- zfs_nicenum(value, buf, sizeof (buf));
- (void) printf(" %5s", buf);
-}
-
-static const char *class_name[] = {
- VDEV_ALLOC_BIAS_DEDUP,
- VDEV_ALLOC_BIAS_SPECIAL,
- VDEV_ALLOC_CLASS_LOGS
-};
-
-/*
- * Print out all the statistics for the given vdev. This can either be the
- * toplevel configuration, or called recursively. If 'name' is NULL, then this
- * is a verbose output, and we don't want to display the toplevel pool stats.
- *
- * Returns the number of stat lines printed.
- */
-static unsigned int
-print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv,
- nvlist_t *newnv, iostat_cbdata_t *cb, int depth)
-{
- nvlist_t **oldchild, **newchild;
- uint_t c, children;
- vdev_stat_t *oldvs, *newvs;
- vdev_stat_t zerovs = { 0 };
- char *vname;
- int ret = 0;
- uint64_t tdelta;
- double scale;
-
- if (strcmp(name, VDEV_TYPE_INDIRECT) == 0)
- return (ret);
-
- if (oldnv != NULL) {
- verify(nvlist_lookup_uint64_array(oldnv,
- ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&oldvs, &c) == 0);
- } else {
- oldvs = &zerovs;
- }
-
- verify(nvlist_lookup_uint64_array(newnv, ZPOOL_CONFIG_VDEV_STATS,
- (uint64_t **)&newvs, &c) == 0);
-
- if (strlen(name) + depth > cb->cb_namewidth)
- (void) printf("%*s%s", depth, "", name);
- else
- (void) printf("%*s%s%*s", depth, "", name,
- (int)(cb->cb_namewidth - strlen(name) - depth), "");
-
- tdelta = newvs->vs_timestamp - oldvs->vs_timestamp;
-
- if (tdelta == 0)
- scale = 1.0;
- else
- scale = (double)NANOSEC / tdelta;
-
- /* only toplevel vdevs have capacity stats */
- if (newvs->vs_space == 0) {
- (void) printf(" - -");
- } else {
- print_one_stat(newvs->vs_alloc);
- print_one_stat(newvs->vs_space - newvs->vs_alloc);
- }
-
- print_one_stat((uint64_t)(scale * (newvs->vs_ops[ZIO_TYPE_READ] -
- oldvs->vs_ops[ZIO_TYPE_READ])));
-
- print_one_stat((uint64_t)(scale * (newvs->vs_ops[ZIO_TYPE_WRITE] -
- oldvs->vs_ops[ZIO_TYPE_WRITE])));
-
- print_one_stat((uint64_t)(scale * (newvs->vs_bytes[ZIO_TYPE_READ] -
- oldvs->vs_bytes[ZIO_TYPE_READ])));
-
- print_one_stat((uint64_t)(scale * (newvs->vs_bytes[ZIO_TYPE_WRITE] -
- oldvs->vs_bytes[ZIO_TYPE_WRITE])));
-
- (void) printf("\n");
-
- if (!cb->cb_verbose)
- return (ret);
-
- if (nvlist_lookup_nvlist_array(newnv, ZPOOL_CONFIG_CHILDREN,
- &newchild, &children) != 0)
- return (ret);
-
- if (oldnv && nvlist_lookup_nvlist_array(oldnv, ZPOOL_CONFIG_CHILDREN,
- &oldchild, &c) != 0)
- return (ret);
-
- /*
- * print normal top-level devices
- */
- for (c = 0; c < children; c++) {
- uint64_t ishole = B_FALSE, islog = B_FALSE;
-
- (void) nvlist_lookup_uint64(newchild[c], ZPOOL_CONFIG_IS_HOLE,
- &ishole);
-
- (void) nvlist_lookup_uint64(newchild[c], ZPOOL_CONFIG_IS_LOG,
- &islog);
-
- if (ishole || islog)
- continue;
-
- if (nvlist_exists(newchild[c], ZPOOL_CONFIG_ALLOCATION_BIAS))
- continue;
-
- vname = zpool_vdev_name(g_zfs, zhp, newchild[c],
- cb->cb_name_flags);
- print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL,
- newchild[c], cb, depth + 2);
- free(vname);
- }
-
- /*
- * print all other top-level devices
- */
- for (uint_t n = 0; n < 3; n++) {
- for (c = 0; c < children; c++) {
- uint64_t islog = B_FALSE;
- char *bias = NULL;
- char *type = NULL;
-
- (void) nvlist_lookup_uint64(newchild[c],
- ZPOOL_CONFIG_IS_LOG, &islog);
- if (islog) {
- bias = VDEV_ALLOC_CLASS_LOGS;
- } else {
- (void) nvlist_lookup_string(newchild[c],
- ZPOOL_CONFIG_ALLOCATION_BIAS, &bias);
- (void) nvlist_lookup_string(newchild[c],
- ZPOOL_CONFIG_TYPE, &type);
- }
- if (bias == NULL || strcmp(bias, class_name[n]) != 0)
- continue;
- if (!islog && strcmp(type, VDEV_TYPE_INDIRECT) == 0)
- continue;
-
- vname = zpool_vdev_name(g_zfs, zhp, newchild[c],
- cb->cb_name_flags);
- ret += print_vdev_stats(zhp, vname, oldnv ?
- oldchild[c] : NULL, newchild[c], cb, depth + 2);
- free(vname);
- }
-
- }
-
- /*
- * Include level 2 ARC devices in iostat output
- */
- if (nvlist_lookup_nvlist_array(newnv, ZPOOL_CONFIG_L2CACHE,
- &newchild, &children) != 0)
- return (ret);
-
- if (oldnv && nvlist_lookup_nvlist_array(oldnv, ZPOOL_CONFIG_L2CACHE,
- &oldchild, &c) != 0)
- return (ret);
-
- if (children > 0) {
- (void) printf("%-*s - - - - - "
- "-\n", cb->cb_namewidth, "cache");
- for (c = 0; c < children; c++) {
- vname = zpool_vdev_name(g_zfs, zhp, newchild[c],
- cb->cb_name_flags);
- print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL,
- newchild[c], cb, depth + 2);
- free(vname);
- }
- }
-
- return (ret);
-}
-
-static int
-refresh_iostat(zpool_handle_t *zhp, void *data)
-{
- iostat_cbdata_t *cb = data;
- boolean_t missing;
-
- /*
- * If the pool has disappeared, remove it from the list and continue.
- */
- if (zpool_refresh_stats(zhp, &missing) != 0)
- return (-1);
-
- if (missing)
- pool_list_remove(cb->cb_list, zhp);
-
- return (0);
-}
-
-/*
- * Callback to print out the iostats for the given pool.
- */
-int
-print_iostat(zpool_handle_t *zhp, void *data)
-{
- iostat_cbdata_t *cb = data;
- nvlist_t *oldconfig, *newconfig;
- nvlist_t *oldnvroot, *newnvroot;
-
- newconfig = zpool_get_config(zhp, &oldconfig);
-
- if (cb->cb_iteration == 1)
- oldconfig = NULL;
-
- verify(nvlist_lookup_nvlist(newconfig, ZPOOL_CONFIG_VDEV_TREE,
- &newnvroot) == 0);
-
- if (oldconfig == NULL)
- oldnvroot = NULL;
- else
- verify(nvlist_lookup_nvlist(oldconfig, ZPOOL_CONFIG_VDEV_TREE,
- &oldnvroot) == 0);
-
- /*
- * Print out the statistics for the pool.
- */
- print_vdev_stats(zhp, zpool_get_name(zhp), oldnvroot, newnvroot, cb, 0);
-
- if (cb->cb_verbose)
- print_iostat_separator(cb);
-
- return (0);
-}
-
-int
-get_namewidth(zpool_handle_t *zhp, void *data)
-{
- iostat_cbdata_t *cb = data;
- nvlist_t *config, *nvroot;
-
- if ((config = zpool_get_config(zhp, NULL)) != NULL) {
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
- if (!cb->cb_verbose)
- cb->cb_namewidth = strlen(zpool_get_name(zhp));
- else
- cb->cb_namewidth = max_width(zhp, nvroot, 0,
- cb->cb_namewidth, cb->cb_name_flags);
- }
-
- /*
- * The width must fall into the range [10,38]. The upper limit is the
- * maximum we can have and still fit in 80 columns.
- */
- if (cb->cb_namewidth < 10)
- cb->cb_namewidth = 10;
- if (cb->cb_namewidth > 38)
- cb->cb_namewidth = 38;
-
- return (0);
-}
-
-/*
- * Parse the input string, get the 'interval' and 'count' value if there is one.
- */
-static void
-get_interval_count(int *argcp, char **argv, unsigned long *iv,
- unsigned long *cnt)
-{
- unsigned long interval = 0, count = 0;
- int argc = *argcp, errno;
-
- /*
- * Determine if the last argument is an integer or a pool name
- */
- if (argc > 0 && isdigit(argv[argc - 1][0])) {
- char *end;
-
- errno = 0;
- interval = strtoul(argv[argc - 1], &end, 10);
-
- if (*end == '\0' && errno == 0) {
- if (interval == 0) {
- (void) fprintf(stderr, gettext("interval "
- "cannot be zero\n"));
- usage(B_FALSE);
- }
- /*
- * Ignore the last parameter
- */
- argc--;
- } else {
- /*
- * If this is not a valid number, just plow on. The
- * user will get a more informative error message later
- * on.
- */
- interval = 0;
- }
- }
-
- /*
- * If the last argument is also an integer, then we have both a count
- * and an interval.
- */
- if (argc > 0 && isdigit(argv[argc - 1][0])) {
- char *end;
-
- errno = 0;
- count = interval;
- interval = strtoul(argv[argc - 1], &end, 10);
-
- if (*end == '\0' && errno == 0) {
- if (interval == 0) {
- (void) fprintf(stderr, gettext("interval "
- "cannot be zero\n"));
- usage(B_FALSE);
- }
-
- /*
- * Ignore the last parameter
- */
- argc--;
- } else {
- interval = 0;
- }
- }
-
- *iv = interval;
- *cnt = count;
- *argcp = argc;
-}
-
-static void
-get_timestamp_arg(char c)
-{
- if (c == 'u')
- timestamp_fmt = UDATE;
- else if (c == 'd')
- timestamp_fmt = DDATE;
- else
- usage(B_FALSE);
-}
-
-/*
- * zpool iostat [-gLPv] [-T d|u] [pool] ... [interval [count]]
- *
- * -g Display guid for individual vdev name.
- * -L Follow links when resolving vdev path name.
- * -P Display full path for vdev name.
- * -v Display statistics for individual vdevs
- * -T Display a timestamp in date(1) or Unix format
- *
- * This command can be tricky because we want to be able to deal with pool
- * creation/destruction as well as vdev configuration changes. The bulk of this
- * processing is handled by the pool_list_* routines in zpool_iter.c. We rely
- * on pool_list_update() to detect the addition of new pools. Configuration
- * changes are all handled within libzfs.
- */
-int
-zpool_do_iostat(int argc, char **argv)
-{
- int c;
- int ret;
- int npools;
- unsigned long interval = 0, count = 0;
- zpool_list_t *list;
- boolean_t verbose = B_FALSE;
- boolean_t guid = B_FALSE;
- boolean_t follow_links = B_FALSE;
- boolean_t full_name = B_FALSE;
- iostat_cbdata_t cb = { 0 };
-
- /* check options */
- while ((c = getopt(argc, argv, "gLPT:v")) != -1) {
- switch (c) {
- case 'g':
- guid = B_TRUE;
- break;
- case 'L':
- follow_links = B_TRUE;
- break;
- case 'P':
- full_name = B_TRUE;
- break;
- case 'T':
- get_timestamp_arg(*optarg);
- break;
- case 'v':
- verbose = B_TRUE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- get_interval_count(&argc, argv, &interval, &count);
-
- /*
- * Construct the list of all interesting pools.
- */
- ret = 0;
- if ((list = pool_list_get(argc, argv, NULL, &ret)) == NULL)
- return (1);
-
- if (pool_list_count(list) == 0 && argc != 0) {
- pool_list_free(list);
- return (1);
- }
-
- if (pool_list_count(list) == 0 && interval == 0) {
- pool_list_free(list);
- (void) fprintf(stderr, gettext("no pools available\n"));
- return (1);
- }
-
- /*
- * Enter the main iostat loop.
- */
- cb.cb_list = list;
- cb.cb_verbose = verbose;
- if (guid)
- cb.cb_name_flags |= VDEV_NAME_GUID;
- if (follow_links)
- cb.cb_name_flags |= VDEV_NAME_FOLLOW_LINKS;
- if (full_name)
- cb.cb_name_flags |= VDEV_NAME_PATH;
- cb.cb_iteration = 0;
- cb.cb_namewidth = 0;
-
- for (;;) {
- pool_list_update(list);
-
- if ((npools = pool_list_count(list)) == 0)
- break;
-
- /*
- * Refresh all statistics. This is done as an explicit step
- * before calculating the maximum name width, so that any
- * configuration changes are properly accounted for.
- */
- (void) pool_list_iter(list, B_FALSE, refresh_iostat, &cb);
-
- /*
- * Iterate over all pools to determine the maximum width
- * for the pool / device name column across all pools.
- */
- cb.cb_namewidth = 0;
- (void) pool_list_iter(list, B_FALSE, get_namewidth, &cb);
-
- if (timestamp_fmt != NODATE)
- print_timestamp(timestamp_fmt);
-
- /*
- * If it's the first time, or verbose mode, print the header.
- */
- if (++cb.cb_iteration == 1 || verbose)
- print_iostat_header(&cb);
-
- (void) pool_list_iter(list, B_FALSE, print_iostat, &cb);
-
- /*
- * If there's more than one pool, and we're not in verbose mode
- * (which prints a separator for us), then print a separator.
- */
- if (npools > 1 && !verbose)
- print_iostat_separator(&cb);
-
- if (verbose)
- (void) printf("\n");
-
- /*
- * Flush the output so that redirection to a file isn't buffered
- * indefinitely.
- */
- (void) fflush(stdout);
-
- if (interval == 0)
- break;
-
- if (count != 0 && --count == 0)
- break;
-
- (void) sleep(interval);
- }
-
- pool_list_free(list);
-
- return (ret);
-}
-
-typedef struct list_cbdata {
- boolean_t cb_verbose;
- int cb_name_flags;
- int cb_namewidth;
- boolean_t cb_scripted;
- zprop_list_t *cb_proplist;
- boolean_t cb_literal;
-} 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[ZPOOL_MAXPROPLEN];
- const char *header;
- boolean_t first = B_TRUE;
- boolean_t right_justify;
- size_t width = 0;
-
- for (; pl != NULL; pl = pl->pl_next) {
- width = pl->pl_width;
- if (first && cb->cb_verbose) {
- /*
- * Reset the width to accommodate the verbose listing
- * of devices.
- */
- width = cb->cb_namewidth;
- }
-
- if (!first)
- (void) printf(" ");
- else
- first = B_FALSE;
-
- right_justify = B_FALSE;
- if (pl->pl_prop != ZPROP_INVAL) {
- header = zpool_prop_column_name(pl->pl_prop);
- right_justify = zpool_prop_align_right(pl->pl_prop);
- } else {
- int i;
-
- 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", width, header);
- else
- (void) printf("%-*s", width, header);
-
- }
-
- (void) printf("\n");
-}
-
-/*
- * Given a pool and a list of properties, print out all the properties according
- * to the described layout. Used by zpool_do_list().
- */
-static void
-print_pool(zpool_handle_t *zhp, list_cbdata_t *cb)
-{
- zprop_list_t *pl = cb->cb_proplist;
- boolean_t first = B_TRUE;
- char property[ZPOOL_MAXPROPLEN];
- char *propstr;
- boolean_t right_justify;
- size_t width;
-
- for (; pl != NULL; pl = pl->pl_next) {
-
- width = pl->pl_width;
- if (first && cb->cb_verbose) {
- /*
- * Reset the width to accommodate the verbose listing
- * of devices.
- */
- width = cb->cb_namewidth;
- }
-
- if (!first) {
- if (cb->cb_scripted)
- (void) printf("\t");
- else
- (void) printf(" ");
- } else {
- first = B_FALSE;
- }
-
- right_justify = B_FALSE;
- if (pl->pl_prop != ZPROP_INVAL) {
- if (zpool_get_prop(zhp, pl->pl_prop, property,
- sizeof (property), NULL, cb->cb_literal) != 0)
- propstr = "-";
- else
- propstr = property;
-
- right_justify = zpool_prop_align_right(pl->pl_prop);
- } else if ((zpool_prop_feature(pl->pl_user_prop) ||
- zpool_prop_unsupported(pl->pl_user_prop)) &&
- zpool_prop_get_feature(zhp, pl->pl_user_prop, property,
- sizeof (property)) == 0) {
- propstr = property;
- } else {
- propstr = "-";
- }
-
-
- /*
- * 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", width, propstr);
- else
- (void) printf("%-*s", width, propstr);
- }
-
- (void) printf("\n");
-}
-
-static void
-print_one_column(zpool_prop_t prop, uint64_t value, boolean_t scripted,
- boolean_t valid)
-{
- char propval[64];
- boolean_t fixed;
- size_t width = zprop_width(prop, &fixed, ZFS_TYPE_POOL);
-
- switch (prop) {
- case ZPOOL_PROP_EXPANDSZ:
- case ZPOOL_PROP_CHECKPOINT:
- if (value == 0)
- (void) strlcpy(propval, "-", sizeof (propval));
- else
- zfs_nicenum(value, propval, sizeof (propval));
- break;
- case ZPOOL_PROP_FRAGMENTATION:
- if (value == ZFS_FRAG_INVALID) {
- (void) strlcpy(propval, "-", sizeof (propval));
- } else {
- (void) snprintf(propval, sizeof (propval), "%llu%%",
- value);
- }
- break;
- case ZPOOL_PROP_CAPACITY:
- (void) snprintf(propval, sizeof (propval),
- value < 1000 ? "%1.2f%%" : value < 10000 ?
- "%2.1f%%" : "%3.0f%%", value / 100.0);
- break;
- default:
- zfs_nicenum(value, propval, sizeof (propval));
- }
-
- if (!valid)
- (void) strlcpy(propval, "-", sizeof (propval));
-
- if (scripted)
- (void) printf("\t%s", propval);
- else
- (void) printf(" %*s", width, propval);
-}
-
-/*
- * print static default line per vdev
- */
-void
-print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv,
- list_cbdata_t *cb, int depth)
-{
- nvlist_t **child;
- vdev_stat_t *vs;
- uint_t c, children;
- char *vname;
- boolean_t scripted = cb->cb_scripted;
- uint64_t islog = B_FALSE;
- char *dashes = "%-*s - - - - - -\n";
-
- verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS,
- (uint64_t **)&vs, &c) == 0);
-
- if (name != NULL) {
- boolean_t toplevel = (vs->vs_space != 0);
- uint64_t cap;
-
- if (strcmp(name, VDEV_TYPE_INDIRECT) == 0)
- return;
-
- if (scripted)
- (void) printf("\t%s", name);
- else if (strlen(name) + depth > cb->cb_namewidth)
- (void) printf("%*s%s", depth, "", name);
- else
- (void) printf("%*s%s%*s", depth, "", name,
- (int)(cb->cb_namewidth - strlen(name) - depth), "");
-
- /*
- * Print the properties for the individual vdevs. Some
- * properties are only applicable to toplevel vdevs. The
- * 'toplevel' boolean value is passed to the print_one_column()
- * to indicate that the value is valid.
- */
- print_one_column(ZPOOL_PROP_SIZE, vs->vs_space, scripted,
- toplevel);
- print_one_column(ZPOOL_PROP_ALLOCATED, vs->vs_alloc, scripted,
- toplevel);
- print_one_column(ZPOOL_PROP_FREE, vs->vs_space - vs->vs_alloc,
- scripted, toplevel);
- print_one_column(ZPOOL_PROP_CHECKPOINT,
- vs->vs_checkpoint_space, scripted, toplevel);
- print_one_column(ZPOOL_PROP_EXPANDSZ, vs->vs_esize, scripted,
- B_TRUE);
- print_one_column(ZPOOL_PROP_FRAGMENTATION,
- vs->vs_fragmentation, scripted,
- (vs->vs_fragmentation != ZFS_FRAG_INVALID && toplevel));
- cap = (vs->vs_space == 0) ? 0 :
- (vs->vs_alloc * 10000 / vs->vs_space);
- print_one_column(ZPOOL_PROP_CAPACITY, cap, scripted, toplevel);
- (void) printf("\n");
- }
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0)
- return;
-
- /* list the normal vdevs first */
- for (c = 0; c < children; c++) {
- uint64_t ishole = B_FALSE;
-
- if (nvlist_lookup_uint64(child[c],
- ZPOOL_CONFIG_IS_HOLE, &ishole) == 0 && ishole)
- continue;
-
- if (nvlist_lookup_uint64(child[c],
- ZPOOL_CONFIG_IS_LOG, &islog) == 0 && islog)
- continue;
-
- if (nvlist_exists(child[c], ZPOOL_CONFIG_ALLOCATION_BIAS))
- continue;
-
- vname = zpool_vdev_name(g_zfs, zhp, child[c],
- cb->cb_name_flags);
- print_list_stats(zhp, vname, child[c], cb, depth + 2);
- free(vname);
- }
-
- /* list the classes: 'logs', 'dedup', and 'special' */
- for (uint_t n = 0; n < 3; n++) {
- boolean_t printed = B_FALSE;
-
- for (c = 0; c < children; c++) {
- char *bias = NULL;
- char *type = NULL;
-
- if (nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
- &islog) == 0 && islog) {
- bias = VDEV_ALLOC_CLASS_LOGS;
- } else {
- (void) nvlist_lookup_string(child[c],
- ZPOOL_CONFIG_ALLOCATION_BIAS, &bias);
- (void) nvlist_lookup_string(child[c],
- ZPOOL_CONFIG_TYPE, &type);
- }
- if (bias == NULL || strcmp(bias, class_name[n]) != 0)
- continue;
- if (!islog && strcmp(type, VDEV_TYPE_INDIRECT) == 0)
- continue;
-
- if (!printed) {
- /* LINTED E_SEC_PRINTF_VAR_FMT */
- (void) printf(dashes, cb->cb_namewidth,
- class_name[n]);
- printed = B_TRUE;
- }
- vname = zpool_vdev_name(g_zfs, zhp, child[c],
- cb->cb_name_flags);
- print_list_stats(zhp, vname, child[c], cb, depth + 2);
- free(vname);
- }
- }
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
- &child, &children) == 0 && children > 0) {
- /* LINTED E_SEC_PRINTF_VAR_FMT */
- (void) printf(dashes, cb->cb_namewidth, "cache");
- for (c = 0; c < children; c++) {
- vname = zpool_vdev_name(g_zfs, zhp, child[c],
- cb->cb_name_flags);
- print_list_stats(zhp, vname, child[c], cb, depth + 2);
- free(vname);
- }
- }
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, &child,
- &children) == 0 && children > 0) {
- /* LINTED E_SEC_PRINTF_VAR_FMT */
- (void) printf(dashes, cb->cb_namewidth, "spare");
- for (c = 0; c < children; c++) {
- vname = zpool_vdev_name(g_zfs, zhp, child[c],
- cb->cb_name_flags);
- print_list_stats(zhp, vname, child[c], cb, depth + 2);
- free(vname);
- }
- }
-}
-
-/*
- * Generic callback function to list a pool.
- */
-int
-list_callback(zpool_handle_t *zhp, void *data)
-{
- list_cbdata_t *cbp = data;
- nvlist_t *config;
- nvlist_t *nvroot;
-
- config = zpool_get_config(zhp, NULL);
-
- if (cbp->cb_verbose) {
- config = zpool_get_config(zhp, NULL);
-
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
- }
-
- if (cbp->cb_verbose)
- cbp->cb_namewidth = max_width(zhp, nvroot, 0, 0,
- cbp->cb_name_flags);
-
- print_pool(zhp, cbp);
-
- if (cbp->cb_verbose)
- print_list_stats(zhp, NULL, nvroot, cbp, 0);
-
- return (0);
-}
-
-/*
- * zpool list [-gHLP] [-o prop[,prop]*] [-T d|u] [pool] ... [interval [count]]
- *
- * -g Display guid for individual vdev name.
- * -H Scripted mode. Don't display headers, and separate properties
- * by a single tab.
- * -L Follow links when resolving vdev path name.
- * -o List of properties to display. Defaults to
- * "name,size,allocated,free,expandsize,fragmentation,capacity,"
- * "dedupratio,health,altroot"
- * -p Diplay values in parsable (exact) format.
- * -P Display full path for vdev name.
- * -T Display a timestamp in date(1) or Unix format
- *
- * List all pools in the system, whether or not they're healthy. Output space
- * statistics for each one, as well as health status summary.
- */
-int
-zpool_do_list(int argc, char **argv)
-{
- int c;
- int ret;
- list_cbdata_t cb = { 0 };
- static char default_props[] =
- "name,size,allocated,free,checkpoint,expandsize,fragmentation,"
- "capacity,dedupratio,health,altroot";
- char *props = default_props;
- unsigned long interval = 0, count = 0;
- zpool_list_t *list;
- boolean_t first = B_TRUE;
-
- /* check options */
- while ((c = getopt(argc, argv, ":gHLo:pPT:v")) != -1) {
- switch (c) {
- case 'g':
- cb.cb_name_flags |= VDEV_NAME_GUID;
- break;
- case 'H':
- cb.cb_scripted = B_TRUE;
- break;
- case 'L':
- cb.cb_name_flags |= VDEV_NAME_FOLLOW_LINKS;
- break;
- case 'o':
- props = optarg;
- break;
- case 'P':
- cb.cb_name_flags |= VDEV_NAME_PATH;
- break;
- case 'p':
- cb.cb_literal = B_TRUE;
- break;
- case 'T':
- get_timestamp_arg(*optarg);
- break;
- case 'v':
- cb.cb_verbose = B_TRUE;
- cb.cb_namewidth = 8; /* 8 until precalc is avail */
- 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;
-
- get_interval_count(&argc, argv, &interval, &count);
-
- if (zprop_get_list(g_zfs, props, &cb.cb_proplist, ZFS_TYPE_POOL) != 0)
- usage(B_FALSE);
-
- for (;;) {
- if ((list = pool_list_get(argc, argv, &cb.cb_proplist,
- &ret)) == NULL)
- return (1);
-
- if (pool_list_count(list) == 0)
- break;
-
- cb.cb_namewidth = 0;
- (void) pool_list_iter(list, B_FALSE, get_namewidth, &cb);
-
- if (timestamp_fmt != NODATE)
- print_timestamp(timestamp_fmt);
-
- if (!cb.cb_scripted && (first || cb.cb_verbose)) {
- print_header(&cb);
- first = B_FALSE;
- }
- ret = pool_list_iter(list, B_TRUE, list_callback, &cb);
-
- if (interval == 0)
- break;
-
- if (count != 0 && --count == 0)
- break;
-
- pool_list_free(list);
- (void) sleep(interval);
- }
-
- if (argc == 0 && !cb.cb_scripted && pool_list_count(list) == 0) {
- (void) printf(gettext("no pools available\n"));
- ret = 0;
- }
-
- pool_list_free(list);
- zprop_free_list(cb.cb_proplist);
- return (ret);
-}
-
-static int
-zpool_do_attach_or_replace(int argc, char **argv, int replacing)
-{
- boolean_t force = B_FALSE;
- int c;
- nvlist_t *nvroot;
- char *poolname, *old_disk, *new_disk;
- zpool_handle_t *zhp;
- zpool_boot_label_t boot_type;
- uint64_t boot_size;
- int ret;
-
- /* check options */
- while ((c = getopt(argc, argv, "f")) != -1) {
- switch (c) {
- case 'f':
- force = B_TRUE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* get pool name and check number of arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing pool name argument\n"));
- usage(B_FALSE);
- }
-
- poolname = argv[0];
-
- if (argc < 2) {
- (void) fprintf(stderr,
- gettext("missing <device> specification\n"));
- usage(B_FALSE);
- }
-
- old_disk = argv[1];
-
- if (argc < 3) {
- if (!replacing) {
- (void) fprintf(stderr,
- gettext("missing <new_device> specification\n"));
- usage(B_FALSE);
- }
- new_disk = old_disk;
- argc -= 1;
- argv += 1;
- } else {
- new_disk = argv[2];
- argc -= 2;
- argv += 2;
- }
-
- if (argc > 1) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
- return (1);
-
- if (zpool_get_config(zhp, NULL) == NULL) {
- (void) fprintf(stderr, gettext("pool '%s' is unavailable\n"),
- poolname);
- zpool_close(zhp);
- return (1);
- }
-
- if (zpool_is_bootable(zhp))
- boot_type = ZPOOL_COPY_BOOT_LABEL;
- else
- boot_type = ZPOOL_NO_BOOT_LABEL;
-
- boot_size = zpool_get_prop_int(zhp, ZPOOL_PROP_BOOTSIZE, NULL);
- nvroot = make_root_vdev(zhp, force, B_FALSE, replacing, B_FALSE,
- boot_type, boot_size, argc, argv);
- if (nvroot == NULL) {
- zpool_close(zhp);
- return (1);
- }
-
- ret = zpool_vdev_attach(zhp, old_disk, new_disk, nvroot, replacing);
-
- nvlist_free(nvroot);
- zpool_close(zhp);
-
- return (ret);
-}
-
-/*
- * zpool replace [-f] <pool> <device> <new_device>
- *
- * -f Force attach, even if <new_device> appears to be in use.
- *
- * Replace <device> with <new_device>.
- */
-/* ARGSUSED */
-int
-zpool_do_replace(int argc, char **argv)
-{
- return (zpool_do_attach_or_replace(argc, argv, B_TRUE));
-}
-
-/*
- * zpool attach [-f] <pool> <device> <new_device>
- *
- * -f Force attach, even if <new_device> appears to be in use.
- *
- * Attach <new_device> to the mirror containing <device>. If <device> is not
- * part of a mirror, then <device> will be transformed into a mirror of
- * <device> and <new_device>. In either case, <new_device> will begin life
- * with a DTL of [0, now], and will immediately begin to resilver itself.
- */
-int
-zpool_do_attach(int argc, char **argv)
-{
- return (zpool_do_attach_or_replace(argc, argv, B_FALSE));
-}
-
-/*
- * zpool detach [-f] <pool> <device>
- *
- * -f Force detach of <device>, even if DTLs argue against it
- * (not supported yet)
- *
- * Detach a device from a mirror. The operation will be refused if <device>
- * is the last device in the mirror, or if the DTLs indicate that this device
- * has the only valid copy of some data.
- */
-/* ARGSUSED */
-int
-zpool_do_detach(int argc, char **argv)
-{
- int c;
- char *poolname, *path;
- zpool_handle_t *zhp;
- int ret;
-
- /* check options */
- while ((c = getopt(argc, argv, "f")) != -1) {
- switch (c) {
- case 'f':
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* get pool name and check number of arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing pool name argument\n"));
- usage(B_FALSE);
- }
-
- if (argc < 2) {
- (void) fprintf(stderr,
- gettext("missing <device> specification\n"));
- usage(B_FALSE);
- }
-
- poolname = argv[0];
- path = argv[1];
-
- if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
- return (1);
-
- ret = zpool_vdev_detach(zhp, path);
-
- zpool_close(zhp);
-
- return (ret);
-}
-
-/*
- * zpool split [-gLnP] [-o prop=val] ...
- * [-o mntopt] ...
- * [-R altroot] <pool> <newpool> [<device> ...]
- *
- * -g Display guid for individual vdev name.
- * -L Follow links when resolving vdev path name.
- * -n Do not split the pool, but display the resulting layout if
- * it were to be split.
- * -o Set property=value, or set mount options.
- * -P Display full path for vdev name.
- * -R Mount the split-off pool under an alternate root.
- *
- * Splits the named pool and gives it the new pool name. Devices to be split
- * off may be listed, provided that no more than one device is specified
- * per top-level vdev mirror. The newly split pool is left in an exported
- * state unless -R is specified.
- *
- * Restrictions: the top-level of the pool pool must only be made up of
- * mirrors; all devices in the pool must be healthy; no device may be
- * undergoing a resilvering operation.
- */
-int
-zpool_do_split(int argc, char **argv)
-{
- char *srcpool, *newpool, *propval;
- char *mntopts = NULL;
- splitflags_t flags;
- int c, ret = 0;
- zpool_handle_t *zhp;
- nvlist_t *config, *props = NULL;
-
- flags.dryrun = B_FALSE;
- flags.import = B_FALSE;
- flags.name_flags = 0;
-
- /* check options */
- while ((c = getopt(argc, argv, ":gLR:no:P")) != -1) {
- switch (c) {
- case 'g':
- flags.name_flags |= VDEV_NAME_GUID;
- break;
- case 'L':
- flags.name_flags |= VDEV_NAME_FOLLOW_LINKS;
- break;
- case 'R':
- flags.import = B_TRUE;
- if (add_prop_list(
- zpool_prop_to_name(ZPOOL_PROP_ALTROOT), optarg,
- &props, B_TRUE) != 0) {
- nvlist_free(props);
- usage(B_FALSE);
- }
- break;
- case 'n':
- flags.dryrun = B_TRUE;
- break;
- case 'o':
- if ((propval = strchr(optarg, '=')) != NULL) {
- *propval = '\0';
- propval++;
- if (add_prop_list(optarg, propval,
- &props, B_TRUE) != 0) {
- nvlist_free(props);
- usage(B_FALSE);
- }
- } else {
- mntopts = optarg;
- }
- break;
- case 'P':
- flags.name_flags |= VDEV_NAME_PATH;
- 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);
- break;
- }
- }
-
- if (!flags.import && mntopts != NULL) {
- (void) fprintf(stderr, gettext("setting mntopts is only "
- "valid when importing the pool\n"));
- usage(B_FALSE);
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc < 1) {
- (void) fprintf(stderr, gettext("Missing pool name\n"));
- usage(B_FALSE);
- }
- if (argc < 2) {
- (void) fprintf(stderr, gettext("Missing new pool name\n"));
- usage(B_FALSE);
- }
-
- srcpool = argv[0];
- newpool = argv[1];
-
- argc -= 2;
- argv += 2;
-
- if ((zhp = zpool_open(g_zfs, srcpool)) == NULL)
- return (1);
-
- config = split_mirror_vdev(zhp, newpool, props, flags, argc, argv);
- if (config == NULL) {
- ret = 1;
- } else {
- if (flags.dryrun) {
- (void) printf(gettext("would create '%s' with the "
- "following layout:\n\n"), newpool);
- print_vdev_tree(NULL, newpool, config, 0, "",
- flags.name_flags);
- }
- nvlist_free(config);
- }
-
- zpool_close(zhp);
-
- if (ret != 0 || flags.dryrun || !flags.import)
- return (ret);
-
- /*
- * The split was successful. Now we need to open the new
- * pool and import it.
- */
- if ((zhp = zpool_open_canfail(g_zfs, newpool)) == NULL)
- return (1);
- if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL &&
- zpool_enable_datasets(zhp, mntopts, 0) != 0) {
- ret = 1;
- (void) fprintf(stderr, gettext("Split was successful, but "
- "the datasets could not all be mounted\n"));
- (void) fprintf(stderr, gettext("Try doing '%s' with a "
- "different altroot\n"), "zpool import");
- }
- zpool_close(zhp);
-
- return (ret);
-}
-
-
-
-/*
- * zpool online <pool> <device> ...
- */
-int
-zpool_do_online(int argc, char **argv)
-{
- int c, i;
- char *poolname;
- zpool_handle_t *zhp;
- int ret = 0;
- vdev_state_t newstate;
- int flags = 0;
-
- /* check options */
- while ((c = getopt(argc, argv, "et")) != -1) {
- switch (c) {
- case 'e':
- flags |= ZFS_ONLINE_EXPAND;
- break;
- case 't':
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* get pool name and check number of arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing pool name\n"));
- usage(B_FALSE);
- }
- if (argc < 2) {
- (void) fprintf(stderr, gettext("missing device name\n"));
- usage(B_FALSE);
- }
-
- poolname = argv[0];
-
- if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
- return (1);
-
- for (i = 1; i < argc; i++) {
- if (zpool_vdev_online(zhp, argv[i], flags, &newstate) == 0) {
- if (newstate != VDEV_STATE_HEALTHY) {
- (void) printf(gettext("warning: device '%s' "
- "onlined, but remains in faulted state\n"),
- argv[i]);
- if (newstate == VDEV_STATE_FAULTED)
- (void) printf(gettext("use 'zpool "
- "clear' to restore a faulted "
- "device\n"));
- else
- (void) printf(gettext("use 'zpool "
- "replace' to replace devices "
- "that are no longer present\n"));
- }
- } else {
- ret = 1;
- }
- }
-
- zpool_close(zhp);
-
- return (ret);
-}
-
-/*
- * zpool offline [-ft] <pool> <device> ...
- *
- * -f Force the device into the offline state, even if doing
- * so would appear to compromise pool availability.
- * (not supported yet)
- *
- * -t Only take the device off-line temporarily. The offline
- * state will not be persistent across reboots.
- */
-/* ARGSUSED */
-int
-zpool_do_offline(int argc, char **argv)
-{
- int c, i;
- char *poolname;
- zpool_handle_t *zhp;
- int ret = 0;
- boolean_t istmp = B_FALSE;
-
- /* check options */
- while ((c = getopt(argc, argv, "ft")) != -1) {
- switch (c) {
- case 't':
- istmp = B_TRUE;
- break;
- case 'f':
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* get pool name and check number of arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing pool name\n"));
- usage(B_FALSE);
- }
- if (argc < 2) {
- (void) fprintf(stderr, gettext("missing device name\n"));
- usage(B_FALSE);
- }
-
- poolname = argv[0];
-
- if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
- return (1);
-
- for (i = 1; i < argc; i++) {
- if (zpool_vdev_offline(zhp, argv[i], istmp) != 0)
- ret = 1;
- }
-
- zpool_close(zhp);
-
- return (ret);
-}
-
-/*
- * zpool clear <pool> [device]
- *
- * Clear all errors associated with a pool or a particular device.
- */
-int
-zpool_do_clear(int argc, char **argv)
-{
- int c;
- int ret = 0;
- boolean_t dryrun = B_FALSE;
- boolean_t do_rewind = B_FALSE;
- boolean_t xtreme_rewind = B_FALSE;
- uint32_t rewind_policy = ZPOOL_NO_REWIND;
- nvlist_t *policy = NULL;
- zpool_handle_t *zhp;
- char *pool, *device;
-
- /* check options */
- while ((c = getopt(argc, argv, "FnX")) != -1) {
- switch (c) {
- case 'F':
- do_rewind = B_TRUE;
- break;
- case 'n':
- dryrun = B_TRUE;
- break;
- case 'X':
- xtreme_rewind = B_TRUE;
- 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 pool name\n"));
- usage(B_FALSE);
- }
-
- if (argc > 2) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- if ((dryrun || xtreme_rewind) && !do_rewind) {
- (void) fprintf(stderr,
- gettext("-n or -X only meaningful with -F\n"));
- usage(B_FALSE);
- }
- if (dryrun)
- rewind_policy = ZPOOL_TRY_REWIND;
- else if (do_rewind)
- rewind_policy = ZPOOL_DO_REWIND;
- if (xtreme_rewind)
- rewind_policy |= ZPOOL_EXTREME_REWIND;
-
- /* In future, further rewind policy choices can be passed along here */
- if (nvlist_alloc(&policy, NV_UNIQUE_NAME, 0) != 0 ||
- nvlist_add_uint32(policy, ZPOOL_LOAD_REWIND_POLICY,
- rewind_policy) != 0) {
- return (1);
- }
-
- pool = argv[0];
- device = argc == 2 ? argv[1] : NULL;
-
- if ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL) {
- nvlist_free(policy);
- return (1);
- }
-
- if (zpool_clear(zhp, device, policy) != 0)
- ret = 1;
-
- zpool_close(zhp);
-
- nvlist_free(policy);
-
- return (ret);
-}
-
-/*
- * zpool reguid <pool>
- */
-int
-zpool_do_reguid(int argc, char **argv)
-{
- int c;
- char *poolname;
- zpool_handle_t *zhp;
- int ret = 0;
-
- /* check options */
- while ((c = getopt(argc, argv, "")) != -1) {
- switch (c) {
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- /* get pool name and check number of arguments */
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing pool name\n"));
- usage(B_FALSE);
- }
-
- if (argc > 1) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- poolname = argv[0];
- if ((zhp = zpool_open(g_zfs, poolname)) == NULL)
- return (1);
-
- ret = zpool_reguid(zhp);
-
- zpool_close(zhp);
- return (ret);
-}
-
-
-/*
- * zpool reopen <pool>
- *
- * Reopen the pool so that the kernel can update the sizes of all vdevs.
- */
-int
-zpool_do_reopen(int argc, char **argv)
-{
- int c;
- int ret = 0;
- zpool_handle_t *zhp;
- char *pool;
-
- /* check options */
- while ((c = getopt(argc, argv, "")) != -1) {
- switch (c) {
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc--;
- argv++;
-
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing pool name\n"));
- usage(B_FALSE);
- }
-
- if (argc > 1) {
- (void) fprintf(stderr, gettext("too many arguments\n"));
- usage(B_FALSE);
- }
-
- pool = argv[0];
- if ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL)
- return (1);
-
- ret = zpool_reopen(zhp);
- zpool_close(zhp);
- return (ret);
-}
-
-typedef struct scrub_cbdata {
- int cb_type;
- int cb_argc;
- char **cb_argv;
- pool_scrub_cmd_t cb_scrub_cmd;
-} scrub_cbdata_t;
-
-static boolean_t
-zpool_has_checkpoint(zpool_handle_t *zhp)
-{
- nvlist_t *config, *nvroot;
-
- config = zpool_get_config(zhp, NULL);
-
- if (config != NULL) {
- pool_checkpoint_stat_t *pcs = NULL;
- uint_t c;
-
- nvroot = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE);
- (void) nvlist_lookup_uint64_array(nvroot,
- ZPOOL_CONFIG_CHECKPOINT_STATS, (uint64_t **)&pcs, &c);
-
- if (pcs == NULL || pcs->pcs_state == CS_NONE)
- return (B_FALSE);
-
- assert(pcs->pcs_state == CS_CHECKPOINT_EXISTS ||
- pcs->pcs_state == CS_CHECKPOINT_DISCARDING);
- return (B_TRUE);
- }
-
- return (B_FALSE);
-}
-
-int
-scrub_callback(zpool_handle_t *zhp, void *data)
-{
- scrub_cbdata_t *cb = data;
- int err;
-
- /*
- * Ignore faulted pools.
- */
- if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
- (void) fprintf(stderr, gettext("cannot scrub '%s': pool is "
- "currently unavailable\n"), zpool_get_name(zhp));
- return (1);
- }
-
- err = zpool_scan(zhp, cb->cb_type, cb->cb_scrub_cmd);
-
- if (err == 0 && zpool_has_checkpoint(zhp) &&
- cb->cb_type == POOL_SCAN_SCRUB) {
- (void) printf(gettext("warning: will not scrub state that "
- "belongs to the checkpoint of pool '%s'\n"),
- zpool_get_name(zhp));
- }
-
- return (err != 0);
-}
-
-/*
- * zpool scrub [-s | -p] <pool> ...
- *
- * -s Stop. Stops any in-progress scrub.
- * -p Pause. Pause in-progress scrub.
- */
-int
-zpool_do_scrub(int argc, char **argv)
-{
- int c;
- scrub_cbdata_t cb;
-
- cb.cb_type = POOL_SCAN_SCRUB;
- cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
-
- /* check options */
- while ((c = getopt(argc, argv, "sp")) != -1) {
- switch (c) {
- case 's':
- cb.cb_type = POOL_SCAN_NONE;
- break;
- case 'p':
- cb.cb_scrub_cmd = POOL_SCRUB_PAUSE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- if (cb.cb_type == POOL_SCAN_NONE &&
- cb.cb_scrub_cmd == POOL_SCRUB_PAUSE) {
- (void) fprintf(stderr, gettext("invalid option combination: "
- "-s and -p are mutually exclusive\n"));
- usage(B_FALSE);
- }
-
- cb.cb_argc = argc;
- cb.cb_argv = argv;
- argc -= optind;
- argv += optind;
-
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing pool name argument\n"));
- usage(B_FALSE);
- }
-
- return (for_each_pool(argc, argv, B_TRUE, NULL, scrub_callback, &cb));
-}
-
-static void
-zpool_collect_leaves(zpool_handle_t *zhp, nvlist_t *nvroot, nvlist_t *res)
-{
- uint_t children = 0;
- nvlist_t **child;
- uint_t i;
-
- (void) nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
- &child, &children);
-
- if (children == 0) {
- char *path = zpool_vdev_name(g_zfs, zhp, nvroot, B_FALSE);
- fnvlist_add_boolean(res, path);
- free(path);
- return;
- }
-
- for (i = 0; i < children; i++) {
- zpool_collect_leaves(zhp, child[i], res);
- }
-}
-
-/*
- * zpool initialize [-cs] <pool> [<vdev> ...]
- * Initialize all unused blocks in the specified vdevs, or all vdevs in the pool
- * if none specified.
- *
- * -c Cancel. Ends active initializing.
- * -s Suspend. Initializing can then be restarted with no flags.
- */
-int
-zpool_do_initialize(int argc, char **argv)
-{
- int c;
- char *poolname;
- zpool_handle_t *zhp;
- nvlist_t *vdevs;
- int err = 0;
-
- struct option long_options[] = {
- {"cancel", no_argument, NULL, 'c'},
- {"suspend", no_argument, NULL, 's'},
- {0, 0, 0, 0}
- };
-
- pool_initialize_func_t cmd_type = POOL_INITIALIZE_DO;
- while ((c = getopt_long(argc, argv, "cs", long_options, NULL)) != -1) {
- switch (c) {
- case 'c':
- if (cmd_type != POOL_INITIALIZE_DO) {
- (void) fprintf(stderr, gettext("-c cannot be "
- "combined with other options\n"));
- usage(B_FALSE);
- }
- cmd_type = POOL_INITIALIZE_CANCEL;
- break;
- case 's':
- if (cmd_type != POOL_INITIALIZE_DO) {
- (void) fprintf(stderr, gettext("-s cannot be "
- "combined with other options\n"));
- usage(B_FALSE);
- }
- cmd_type = POOL_INITIALIZE_SUSPEND;
- break;
- case '?':
- if (optopt != 0) {
- (void) fprintf(stderr,
- gettext("invalid option '%c'\n"), optopt);
- } else {
- (void) fprintf(stderr,
- gettext("invalid option '%s'\n"),
- argv[optind - 1]);
- }
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- if (argc < 1) {
- (void) fprintf(stderr, gettext("missing pool name argument\n"));
- usage(B_FALSE);
- return (-1);
- }
-
- poolname = argv[0];
- zhp = zpool_open(g_zfs, poolname);
- if (zhp == NULL)
- return (-1);
-
- vdevs = fnvlist_alloc();
- if (argc == 1) {
- /* no individual leaf vdevs specified, so add them all */
- nvlist_t *config = zpool_get_config(zhp, NULL);
- nvlist_t *nvroot = fnvlist_lookup_nvlist(config,
- ZPOOL_CONFIG_VDEV_TREE);
- zpool_collect_leaves(zhp, nvroot, vdevs);
- } else {
- int i;
- for (i = 1; i < argc; i++) {
- fnvlist_add_boolean(vdevs, argv[i]);
- }
- }
-
- err = zpool_initialize(zhp, cmd_type, vdevs);
-
- fnvlist_free(vdevs);
- zpool_close(zhp);
-
- return (err);
-}
-
-/*
- * Print out detailed scrub status.
- */
-static void
-print_scan_status(pool_scan_stat_t *ps)
-{
- time_t start, end, pause;
- uint64_t total_secs_left;
- uint64_t elapsed, secs_left, mins_left, hours_left, days_left;
- uint64_t pass_scanned, scanned, pass_issued, issued, total;
- uint_t scan_rate, issue_rate;
- double fraction_done;
- char processed_buf[7], scanned_buf[7], issued_buf[7], total_buf[7];
- char srate_buf[7], irate_buf[7];
-
- (void) printf(gettext(" scan: "));
-
- /* If there's never been a scan, there's not much to say. */
- if (ps == NULL || ps->pss_func == POOL_SCAN_NONE ||
- ps->pss_func >= POOL_SCAN_FUNCS) {
- (void) printf(gettext("none requested\n"));
- return;
- }
-
- start = ps->pss_start_time;
- end = ps->pss_end_time;
- pause = ps->pss_pass_scrub_pause;
-
- zfs_nicenum(ps->pss_processed, processed_buf, sizeof (processed_buf));
-
- assert(ps->pss_func == POOL_SCAN_SCRUB ||
- ps->pss_func == POOL_SCAN_RESILVER);
-
- /* Scan is finished or canceled. */
- if (ps->pss_state == DSS_FINISHED) {
- total_secs_left = end - start;
- days_left = total_secs_left / 60 / 60 / 24;
- hours_left = (total_secs_left / 60 / 60) % 24;
- mins_left = (total_secs_left / 60) % 60;
- secs_left = (total_secs_left % 60);
-
- if (ps->pss_func == POOL_SCAN_SCRUB) {
- (void) printf(gettext("scrub repaired %s "
- "in %llu days %02llu:%02llu:%02llu "
- "with %llu errors on %s"), processed_buf,
- (u_longlong_t)days_left, (u_longlong_t)hours_left,
- (u_longlong_t)mins_left, (u_longlong_t)secs_left,
- (u_longlong_t)ps->pss_errors, ctime(&end));
- } else if (ps->pss_func == POOL_SCAN_RESILVER) {
- (void) printf(gettext("resilvered %s "
- "in %llu days %02llu:%02llu:%02llu "
- "with %llu errors on %s"), processed_buf,
- (u_longlong_t)days_left, (u_longlong_t)hours_left,
- (u_longlong_t)mins_left, (u_longlong_t)secs_left,
- (u_longlong_t)ps->pss_errors, ctime(&end));
-
- }
-
- return;
- } else if (ps->pss_state == DSS_CANCELED) {
- if (ps->pss_func == POOL_SCAN_SCRUB) {
- (void) printf(gettext("scrub canceled on %s"),
- ctime(&end));
- } else if (ps->pss_func == POOL_SCAN_RESILVER) {
- (void) printf(gettext("resilver canceled on %s"),
- ctime(&end));
- }
- return;
- }
-
- assert(ps->pss_state == DSS_SCANNING);
-
- /* Scan is in progress. Resilvers can't be paused. */
- if (ps->pss_func == POOL_SCAN_SCRUB) {
- if (pause == 0) {
- (void) printf(gettext("scrub in progress since %s"),
- ctime(&start));
- } else {
- (void) printf(gettext("scrub paused since %s"),
- ctime(&pause));
- (void) printf(gettext("\tscrub started on %s"),
- ctime(&start));
- }
- } else if (ps->pss_func == POOL_SCAN_RESILVER) {
- (void) printf(gettext("resilver in progress since %s"),
- ctime(&start));
- }
-
- scanned = ps->pss_examined;
- pass_scanned = ps->pss_pass_exam;
- issued = ps->pss_issued;
- pass_issued = ps->pss_pass_issued;
- total = ps->pss_to_examine;
-
- /* we are only done with a block once we have issued the IO for it */
- fraction_done = (double)issued / total;
-
- /* elapsed time for this pass, rounding up to 1 if it's 0 */
- elapsed = time(NULL) - ps->pss_pass_start;
- elapsed -= ps->pss_pass_scrub_spent_paused;
- elapsed = (elapsed != 0) ? elapsed : 1;
-
- scan_rate = pass_scanned / elapsed;
- issue_rate = pass_issued / elapsed;
- total_secs_left = (issue_rate != 0) ?
- ((total - issued) / issue_rate) : UINT64_MAX;
-
- days_left = total_secs_left / 60 / 60 / 24;
- hours_left = (total_secs_left / 60 / 60) % 24;
- mins_left = (total_secs_left / 60) % 60;
- secs_left = (total_secs_left % 60);
-
- /* format all of the numbers we will be reporting */
- zfs_nicenum(scanned, scanned_buf, sizeof (scanned_buf));
- zfs_nicenum(issued, issued_buf, sizeof (issued_buf));
- zfs_nicenum(total, total_buf, sizeof (total_buf));
- zfs_nicenum(scan_rate, srate_buf, sizeof (srate_buf));
- zfs_nicenum(issue_rate, irate_buf, sizeof (irate_buf));
-
- /* doo not print estimated time if we have a paused scrub */
- if (pause == 0) {
- (void) printf(gettext("\t%s scanned at %s/s, "
- "%s issued at %s/s, %s total\n"),
- scanned_buf, srate_buf, issued_buf, irate_buf, total_buf);
- } else {
- (void) printf(gettext("\t%s scanned, %s issued, %s total\n"),
- scanned_buf, issued_buf, total_buf);
- }
-
- if (ps->pss_func == POOL_SCAN_RESILVER) {
- (void) printf(gettext("\t%s resilvered, %.2f%% done"),
- processed_buf, 100 * fraction_done);
- } else if (ps->pss_func == POOL_SCAN_SCRUB) {
- (void) printf(gettext("\t%s repaired, %.2f%% done"),
- processed_buf, 100 * fraction_done);
- }
-
- if (pause == 0) {
- if (issue_rate >= 10 * 1024 * 1024) {
- (void) printf(gettext(", %llu days "
- "%02llu:%02llu:%02llu to go\n"),
- (u_longlong_t)days_left, (u_longlong_t)hours_left,
- (u_longlong_t)mins_left, (u_longlong_t)secs_left);
- } else {
- (void) printf(gettext(", no estimated "
- "completion time\n"));
- }
- } else {
- (void) printf(gettext("\n"));
- }
-}
-
-/*
- * As we don't scrub checkpointed blocks, we want to warn the
- * user that we skipped scanning some blocks if a checkpoint exists
- * or existed at any time during the scan.
- */
-static void
-print_checkpoint_scan_warning(pool_scan_stat_t *ps, pool_checkpoint_stat_t *pcs)
-{
- if (ps == NULL || pcs == NULL)
- return;
-
- if (pcs->pcs_state == CS_NONE ||
- pcs->pcs_state == CS_CHECKPOINT_DISCARDING)
- return;
-
- assert(pcs->pcs_state == CS_CHECKPOINT_EXISTS);
-
- if (ps->pss_state == DSS_NONE)
- return;
-
- if ((ps->pss_state == DSS_FINISHED || ps->pss_state == DSS_CANCELED) &&
- ps->pss_end_time < pcs->pcs_start_time)
- return;
-
- if (ps->pss_state == DSS_FINISHED || ps->pss_state == DSS_CANCELED) {
- (void) printf(gettext(" scan warning: skipped blocks "
- "that are only referenced by the checkpoint.\n"));
- } else {
- assert(ps->pss_state == DSS_SCANNING);
- (void) printf(gettext(" scan warning: skipping blocks "
- "that are only referenced by the checkpoint.\n"));
- }
-}
-
-/*
- * Print out detailed removal status.
- */
-static void
-print_removal_status(zpool_handle_t *zhp, pool_removal_stat_t *prs)
-{
- char copied_buf[7], examined_buf[7], total_buf[7], rate_buf[7];
- time_t start, end;
- nvlist_t *config, *nvroot;
- nvlist_t **child;
- uint_t children;
- char *vdev_name;
-
- if (prs == NULL || prs->prs_state == DSS_NONE)
- return;
-
- /*
- * Determine name of vdev.
- */
- config = zpool_get_config(zhp, NULL);
- nvroot = fnvlist_lookup_nvlist(config,
- ZPOOL_CONFIG_VDEV_TREE);
- verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
- &child, &children) == 0);
- assert(prs->prs_removing_vdev < children);
- vdev_name = zpool_vdev_name(g_zfs, zhp,
- child[prs->prs_removing_vdev], B_TRUE);
-
- (void) printf(gettext("remove: "));
-
- start = prs->prs_start_time;
- end = prs->prs_end_time;
- zfs_nicenum(prs->prs_copied, copied_buf, sizeof (copied_buf));
-
- /*
- * Removal is finished or canceled.
- */
- if (prs->prs_state == DSS_FINISHED) {
- uint64_t minutes_taken = (end - start) / 60;
-
- (void) printf(gettext("Removal of vdev %llu copied %s "
- "in %lluh%um, completed on %s"),
- (longlong_t)prs->prs_removing_vdev,
- copied_buf,
- (u_longlong_t)(minutes_taken / 60),
- (uint_t)(minutes_taken % 60),
- ctime((time_t *)&end));
- } else if (prs->prs_state == DSS_CANCELED) {
- (void) printf(gettext("Removal of %s canceled on %s"),
- vdev_name, ctime(&end));
- } else {
- uint64_t copied, total, elapsed, mins_left, hours_left;
- double fraction_done;
- uint_t rate;
-
- assert(prs->prs_state == DSS_SCANNING);
-
- /*
- * Removal is in progress.
- */
- (void) printf(gettext(
- "Evacuation of %s in progress since %s"),
- vdev_name, ctime(&start));
-
- copied = prs->prs_copied > 0 ? prs->prs_copied : 1;
- total = prs->prs_to_copy;
- fraction_done = (double)copied / total;
-
- /* elapsed time for this pass */
- elapsed = time(NULL) - prs->prs_start_time;
- elapsed = elapsed > 0 ? elapsed : 1;
- rate = copied / elapsed;
- rate = rate > 0 ? rate : 1;
- mins_left = ((total - copied) / rate) / 60;
- hours_left = mins_left / 60;
-
- zfs_nicenum(copied, examined_buf, sizeof (examined_buf));
- zfs_nicenum(total, total_buf, sizeof (total_buf));
- zfs_nicenum(rate, rate_buf, sizeof (rate_buf));
-
- /*
- * do not print estimated time if hours_left is more than
- * 30 days
- */
- (void) printf(gettext(" %s copied out of %s at %s/s, "
- "%.2f%% done"),
- examined_buf, total_buf, rate_buf, 100 * fraction_done);
- if (hours_left < (30 * 24)) {
- (void) printf(gettext(", %lluh%um to go\n"),
- (u_longlong_t)hours_left, (uint_t)(mins_left % 60));
- } else {
- (void) printf(gettext(
- ", (copy is slow, no estimated time)\n"));
- }
- }
-
- if (prs->prs_mapping_memory > 0) {
- char mem_buf[7];
- zfs_nicenum(prs->prs_mapping_memory, mem_buf, sizeof (mem_buf));
- (void) printf(gettext(" %s memory used for "
- "removed device mappings\n"),
- mem_buf);
- }
-}
-
-static void
-print_checkpoint_status(pool_checkpoint_stat_t *pcs)
-{
- time_t start;
- char space_buf[7];
-
- if (pcs == NULL || pcs->pcs_state == CS_NONE)
- return;
-
- (void) printf(gettext("checkpoint: "));
-
- start = pcs->pcs_start_time;
- zfs_nicenum(pcs->pcs_space, space_buf, sizeof (space_buf));
-
- if (pcs->pcs_state == CS_CHECKPOINT_EXISTS) {
- char *date = ctime(&start);
-
- /*
- * ctime() adds a newline at the end of the generated
- * string, thus the weird format specifier and the
- * strlen() call used to chop it off from the output.
- */
- (void) printf(gettext("created %.*s, consumes %s\n"),
- strlen(date) - 1, date, space_buf);
- return;
- }
-
- assert(pcs->pcs_state == CS_CHECKPOINT_DISCARDING);
-
- (void) printf(gettext("discarding, %s remaining.\n"),
- space_buf);
-}
-
-static void
-print_error_log(zpool_handle_t *zhp)
-{
- nvlist_t *nverrlist = NULL;
- nvpair_t *elem;
- char *pathname;
- size_t len = MAXPATHLEN * 2;
-
- if (zpool_get_errlog(zhp, &nverrlist) != 0) {
- (void) printf("errors: List of errors unavailable "
- "(insufficient privileges)\n");
- return;
- }
-
- (void) printf("errors: Permanent errors have been "
- "detected in the following files:\n\n");
-
- pathname = safe_malloc(len);
- elem = NULL;
- while ((elem = nvlist_next_nvpair(nverrlist, elem)) != NULL) {
- nvlist_t *nv;
- uint64_t dsobj, obj;
-
- verify(nvpair_value_nvlist(elem, &nv) == 0);
- verify(nvlist_lookup_uint64(nv, ZPOOL_ERR_DATASET,
- &dsobj) == 0);
- verify(nvlist_lookup_uint64(nv, ZPOOL_ERR_OBJECT,
- &obj) == 0);
- zpool_obj_to_path(zhp, dsobj, obj, pathname, len);
- (void) printf("%7s %s\n", "", pathname);
- }
- free(pathname);
- nvlist_free(nverrlist);
-}
-
-static void
-print_spares(zpool_handle_t *zhp, status_cbdata_t *cb, nvlist_t **spares,
- uint_t nspares)
-{
- uint_t i;
- char *name;
-
- if (nspares == 0)
- return;
-
- (void) printf(gettext("\tspares\n"));
-
- for (i = 0; i < nspares; i++) {
- name = zpool_vdev_name(g_zfs, zhp, spares[i],
- cb->cb_name_flags);
- print_status_config(zhp, cb, name, spares[i], 2, B_TRUE);
- free(name);
- }
-}
-
-static void
-print_l2cache(zpool_handle_t *zhp, status_cbdata_t *cb, nvlist_t **l2cache,
- uint_t nl2cache)
-{
- uint_t i;
- char *name;
-
- if (nl2cache == 0)
- return;
-
- (void) printf(gettext("\tcache\n"));
-
- for (i = 0; i < nl2cache; i++) {
- name = zpool_vdev_name(g_zfs, zhp, l2cache[i],
- cb->cb_name_flags);
- print_status_config(zhp, cb, name, l2cache[i], 2, B_FALSE);
- free(name);
- }
-}
-
-static void
-print_dedup_stats(nvlist_t *config)
-{
- ddt_histogram_t *ddh;
- ddt_stat_t *dds;
- ddt_object_t *ddo;
- uint_t c;
-
- /*
- * If the pool was faulted then we may not have been able to
- * obtain the config. Otherwise, if we have anything in the dedup
- * table continue processing the stats.
- */
- if (nvlist_lookup_uint64_array(config, ZPOOL_CONFIG_DDT_OBJ_STATS,
- (uint64_t **)&ddo, &c) != 0)
- return;
-
- (void) printf("\n");
- (void) printf(gettext(" dedup: "));
- if (ddo->ddo_count == 0) {
- (void) printf(gettext("no DDT entries\n"));
- return;
- }
-
- (void) printf("DDT entries %llu, size %llu on disk, %llu in core\n",
- (u_longlong_t)ddo->ddo_count,
- (u_longlong_t)ddo->ddo_dspace,
- (u_longlong_t)ddo->ddo_mspace);
-
- verify(nvlist_lookup_uint64_array(config, ZPOOL_CONFIG_DDT_STATS,
- (uint64_t **)&dds, &c) == 0);
- verify(nvlist_lookup_uint64_array(config, ZPOOL_CONFIG_DDT_HISTOGRAM,
- (uint64_t **)&ddh, &c) == 0);
- zpool_dump_ddt(dds, ddh);
-}
-
-/*
- * Display a summary of pool status. Displays a summary such as:
- *
- * pool: tank
- * status: DEGRADED
- * reason: One or more devices ...
- * see: http://illumos.org/msg/ZFS-xxxx-01
- * config:
- * mirror DEGRADED
- * c1t0d0 OK
- * c2t0d0 UNAVAIL
- *
- * When given the '-v' option, we print out the complete config. If the '-e'
- * option is specified, then we print out error rate information as well.
- */
-int
-status_callback(zpool_handle_t *zhp, void *data)
-{
- status_cbdata_t *cbp = data;
- nvlist_t *config, *nvroot;
- char *msgid;
- int reason;
- const char *health;
- uint_t c;
- vdev_stat_t *vs;
-
- config = zpool_get_config(zhp, NULL);
- reason = zpool_get_status(zhp, &msgid);
-
- cbp->cb_count++;
-
- /*
- * If we were given 'zpool status -x', only report those pools with
- * problems.
- */
- if (cbp->cb_explain &&
- (reason == ZPOOL_STATUS_OK ||
- reason == ZPOOL_STATUS_VERSION_OLDER ||
- reason == ZPOOL_STATUS_NON_NATIVE_ASHIFT ||
- reason == ZPOOL_STATUS_FEAT_DISABLED)) {
- if (!cbp->cb_allpools) {
- (void) printf(gettext("pool '%s' is healthy\n"),
- zpool_get_name(zhp));
- if (cbp->cb_first)
- cbp->cb_first = B_FALSE;
- }
- return (0);
- }
-
- if (cbp->cb_first)
- cbp->cb_first = B_FALSE;
- else
- (void) printf("\n");
-
- nvroot = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE);
- verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS,
- (uint64_t **)&vs, &c) == 0);
- health = zpool_state_to_name(vs->vs_state, vs->vs_aux);
-
- (void) printf(gettext(" pool: %s\n"), zpool_get_name(zhp));
- (void) printf(gettext(" state: %s\n"), health);
-
- switch (reason) {
- case ZPOOL_STATUS_MISSING_DEV_R:
- (void) printf(gettext("status: One or more devices could not "
- "be opened. Sufficient replicas exist for\n\tthe pool to "
- "continue functioning in a degraded state.\n"));
- (void) printf(gettext("action: Attach the missing device and "
- "online it using 'zpool online'.\n"));
- break;
-
- case ZPOOL_STATUS_MISSING_DEV_NR:
- (void) printf(gettext("status: One or more devices could not "
- "be opened. There are insufficient\n\treplicas for the "
- "pool to continue functioning.\n"));
- (void) printf(gettext("action: Attach the missing device and "
- "online it using 'zpool online'.\n"));
- break;
-
- case ZPOOL_STATUS_CORRUPT_LABEL_R:
- (void) printf(gettext("status: One or more devices could not "
- "be used because the label is missing or\n\tinvalid. "
- "Sufficient replicas exist for the pool to continue\n\t"
- "functioning in a degraded state.\n"));
- (void) printf(gettext("action: Replace the device using "
- "'zpool replace'.\n"));
- break;
-
- case ZPOOL_STATUS_CORRUPT_LABEL_NR:
- (void) printf(gettext("status: One or more devices could not "
- "be used because the label is missing \n\tor invalid. "
- "There are insufficient replicas for the pool to "
- "continue\n\tfunctioning.\n"));
- zpool_explain_recover(zpool_get_handle(zhp),
- zpool_get_name(zhp), reason, config);
- break;
-
- case ZPOOL_STATUS_FAILING_DEV:
- (void) printf(gettext("status: One or more devices has "
- "experienced an unrecoverable error. An\n\tattempt was "
- "made to correct the error. Applications are "
- "unaffected.\n"));
- (void) printf(gettext("action: Determine if the device needs "
- "to be replaced, and clear the errors\n\tusing "
- "'zpool clear' or replace the device with 'zpool "
- "replace'.\n"));
- break;
-
- case ZPOOL_STATUS_OFFLINE_DEV:
- (void) printf(gettext("status: One or more devices has "
- "been taken offline by the administrator.\n\tSufficient "
- "replicas exist for the pool to continue functioning in "
- "a\n\tdegraded state.\n"));
- (void) printf(gettext("action: Online the device using "
- "'zpool online' or replace the device with\n\t'zpool "
- "replace'.\n"));
- break;
-
- case ZPOOL_STATUS_REMOVED_DEV:
- (void) printf(gettext("status: One or more devices has "
- "been removed by the administrator.\n\tSufficient "
- "replicas exist for the pool to continue functioning in "
- "a\n\tdegraded state.\n"));
- (void) printf(gettext("action: Online the device using "
- "'zpool online' or replace the device with\n\t'zpool "
- "replace'.\n"));
- break;
-
- case ZPOOL_STATUS_RESILVERING:
- (void) printf(gettext("status: One or more devices is "
- "currently being resilvered. The pool will\n\tcontinue "
- "to function, possibly in a degraded state.\n"));
- (void) printf(gettext("action: Wait for the resilver to "
- "complete.\n"));
- break;
-
- case ZPOOL_STATUS_CORRUPT_DATA:
- (void) printf(gettext("status: One or more devices has "
- "experienced an error resulting in data\n\tcorruption. "
- "Applications may be affected.\n"));
- (void) printf(gettext("action: Restore the file in question "
- "if possible. Otherwise restore the\n\tentire pool from "
- "backup.\n"));
- break;
-
- case ZPOOL_STATUS_CORRUPT_POOL:
- (void) printf(gettext("status: The pool metadata is corrupted "
- "and the pool cannot be opened.\n"));
- zpool_explain_recover(zpool_get_handle(zhp),
- zpool_get_name(zhp), reason, config);
- break;
-
- case ZPOOL_STATUS_VERSION_OLDER:
- (void) printf(gettext("status: The pool is formatted using a "
- "legacy on-disk format. The pool can\n\tstill be used, "
- "but some features are unavailable.\n"));
- (void) printf(gettext("action: Upgrade the pool using 'zpool "
- "upgrade'. Once this is done, the\n\tpool will no longer "
- "be accessible on software that does not support feature\n"
- "\tflags.\n"));
- break;
-
- case ZPOOL_STATUS_VERSION_NEWER:
- (void) printf(gettext("status: The pool has been upgraded to a "
- "newer, incompatible on-disk version.\n\tThe pool cannot "
- "be accessed on this system.\n"));
- (void) printf(gettext("action: Access the pool from a system "
- "running more recent software, or\n\trestore the pool from "
- "backup.\n"));
- break;
-
- case ZPOOL_STATUS_FEAT_DISABLED:
- (void) printf(gettext("status: Some supported features are not "
- "enabled on the pool. The pool can\n\tstill be used, but "
- "some features are unavailable.\n"));
- (void) printf(gettext("action: Enable all features using "
- "'zpool upgrade'. Once this is done,\n\tthe pool may no "
- "longer be accessible by software that does not support\n\t"
- "the features. See zpool-features(7) for details.\n"));
- break;
-
- case ZPOOL_STATUS_UNSUP_FEAT_READ:
- (void) printf(gettext("status: The pool cannot be accessed on "
- "this system because it uses the\n\tfollowing feature(s) "
- "not supported on this system:\n"));
- zpool_print_unsup_feat(config);
- (void) printf("\n");
- (void) printf(gettext("action: Access the pool from a system "
- "that supports the required feature(s),\n\tor restore the "
- "pool from backup.\n"));
- break;
-
- case ZPOOL_STATUS_UNSUP_FEAT_WRITE:
- (void) printf(gettext("status: The pool can only be accessed "
- "in read-only mode on this system. It\n\tcannot be "
- "accessed in read-write mode because it uses the "
- "following\n\tfeature(s) not supported on this system:\n"));
- zpool_print_unsup_feat(config);
- (void) printf("\n");
- (void) printf(gettext("action: The pool cannot be accessed in "
- "read-write mode. Import the pool with\n"
- "\t\"-o readonly=on\", access the pool from a system that "
- "supports the\n\trequired feature(s), or restore the "
- "pool from backup.\n"));
- break;
-
- case ZPOOL_STATUS_FAULTED_DEV_R:
- (void) printf(gettext("status: One or more devices are "
- "faulted in response to persistent errors.\n\tSufficient "
- "replicas exist for the pool to continue functioning "
- "in a\n\tdegraded state.\n"));
- (void) printf(gettext("action: Replace the faulted device, "
- "or use 'zpool clear' to mark the device\n\trepaired.\n"));
- break;
-
- case ZPOOL_STATUS_FAULTED_DEV_NR:
- (void) printf(gettext("status: One or more devices are "
- "faulted in response to persistent errors. There are "
- "insufficient replicas for the pool to\n\tcontinue "
- "functioning.\n"));
- (void) printf(gettext("action: Destroy and re-create the pool "
- "from a backup source. Manually marking the device\n"
- "\trepaired using 'zpool clear' may allow some data "
- "to be recovered.\n"));
- break;
-
- case ZPOOL_STATUS_IO_FAILURE_MMP:
- (void) printf(gettext("status: The pool is suspended because "
- "multihost writes failed or were delayed;\n\tanother "
- "system could import the pool undetected.\n"));
- (void) printf(gettext("action: Make sure the pool's devices "
- "are connected, then reboot your system and\n\timport the "
- "pool.\n"));
- break;
-
- case ZPOOL_STATUS_IO_FAILURE_WAIT:
- case ZPOOL_STATUS_IO_FAILURE_CONTINUE:
- (void) printf(gettext("status: One or more devices are "
- "faulted in response to IO failures.\n"));
- (void) printf(gettext("action: Make sure the affected devices "
- "are connected, then run 'zpool clear'.\n"));
- break;
-
- case ZPOOL_STATUS_BAD_LOG:
- (void) printf(gettext("status: An intent log record "
- "could not be read.\n"
- "\tWaiting for adminstrator intervention to fix the "
- "faulted pool.\n"));
- (void) printf(gettext("action: Either restore the affected "
- "device(s) and run 'zpool online',\n"
- "\tor ignore the intent log records by running "
- "'zpool clear'.\n"));
- break;
-
- case ZPOOL_STATUS_NON_NATIVE_ASHIFT:
- (void) printf(gettext("status: One or more devices are "
- "configured to use a non-native block size.\n"
- "\tExpect reduced performance.\n"));
- (void) printf(gettext("action: Replace affected devices with "
- "devices that support the\n\tconfigured block size, or "
- "migrate data to a properly configured\n\tpool.\n"));
- break;
-
- default:
- /*
- * The remaining errors can't actually be generated, yet.
- */
- assert(reason == ZPOOL_STATUS_OK);
- }
-
- if (msgid != NULL)
- (void) printf(gettext(" see: http://illumos.org/msg/%s\n"),
- msgid);
-
- if (config != NULL) {
- uint64_t nerr;
- nvlist_t **spares, **l2cache;
- uint_t nspares, nl2cache;
- pool_checkpoint_stat_t *pcs = NULL;
- pool_scan_stat_t *ps = NULL;
- pool_removal_stat_t *prs = NULL;
-
- (void) nvlist_lookup_uint64_array(nvroot,
- ZPOOL_CONFIG_CHECKPOINT_STATS, (uint64_t **)&pcs, &c);
- (void) nvlist_lookup_uint64_array(nvroot,
- ZPOOL_CONFIG_SCAN_STATS, (uint64_t **)&ps, &c);
- (void) nvlist_lookup_uint64_array(nvroot,
- ZPOOL_CONFIG_REMOVAL_STATS, (uint64_t **)&prs, &c);
-
- print_scan_status(ps);
- print_checkpoint_scan_warning(ps, pcs);
- print_removal_status(zhp, prs);
- print_checkpoint_status(pcs);
-
- cbp->cb_namewidth = max_width(zhp, nvroot, 0, 0,
- cbp->cb_name_flags);
- if (cbp->cb_namewidth < 10)
- cbp->cb_namewidth = 10;
-
- (void) printf(gettext("config:\n\n"));
- (void) printf(gettext("\t%-*s %-8s %5s %5s %5s\n"),
- cbp->cb_namewidth, "NAME", "STATE", "READ", "WRITE",
- "CKSUM");
-
- print_status_config(zhp, cbp, zpool_get_name(zhp), nvroot, 0,
- B_FALSE);
-
- print_class_vdevs(zhp, cbp, nvroot, VDEV_ALLOC_BIAS_DEDUP);
- print_class_vdevs(zhp, cbp, nvroot, VDEV_ALLOC_BIAS_SPECIAL);
- print_class_vdevs(zhp, cbp, nvroot, VDEV_ALLOC_CLASS_LOGS);
-
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
- &l2cache, &nl2cache) == 0)
- print_l2cache(zhp, cbp, l2cache, nl2cache);
-
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- &spares, &nspares) == 0)
- print_spares(zhp, cbp, spares, nspares);
-
- if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRCOUNT,
- &nerr) == 0) {
- nvlist_t *nverrlist = NULL;
-
- /*
- * If the approximate error count is small, get a
- * precise count by fetching the entire log and
- * uniquifying the results.
- */
- if (nerr > 0 && nerr < 100 && !cbp->cb_verbose &&
- zpool_get_errlog(zhp, &nverrlist) == 0) {
- nvpair_t *elem;
-
- elem = NULL;
- nerr = 0;
- while ((elem = nvlist_next_nvpair(nverrlist,
- elem)) != NULL) {
- nerr++;
- }
- }
- nvlist_free(nverrlist);
-
- (void) printf("\n");
-
- if (nerr == 0)
- (void) printf(gettext("errors: No known data "
- "errors\n"));
- else if (!cbp->cb_verbose)
- (void) printf(gettext("errors: %llu data "
- "errors, use '-v' for a list\n"),
- (u_longlong_t)nerr);
- else
- print_error_log(zhp);
- }
-
- if (cbp->cb_dedup_stats)
- print_dedup_stats(config);
- } else {
- (void) printf(gettext("config: The configuration cannot be "
- "determined.\n"));
- }
-
- return (0);
-}
-
-/*
- * zpool status [-gLPvx] [-T d|u] [pool] ... [interval [count]]
- *
- * -g Display guid for individual vdev name.
- * -L Follow links when resolving vdev path name.
- * -P Display full path for vdev name.
- * -v Display complete error logs
- * -x Display only pools with potential problems
- * -D Display dedup status (undocumented)
- * -T Display a timestamp in date(1) or Unix format
- *
- * Describes the health status of all pools or some subset.
- */
-int
-zpool_do_status(int argc, char **argv)
-{
- int c;
- int ret;
- unsigned long interval = 0, count = 0;
- status_cbdata_t cb = { 0 };
-
- /* check options */
- while ((c = getopt(argc, argv, "gLPvxDT:")) != -1) {
- switch (c) {
- case 'g':
- cb.cb_name_flags |= VDEV_NAME_GUID;
- break;
- case 'L':
- cb.cb_name_flags |= VDEV_NAME_FOLLOW_LINKS;
- break;
- case 'P':
- cb.cb_name_flags |= VDEV_NAME_PATH;
- break;
- case 'v':
- cb.cb_verbose = B_TRUE;
- break;
- case 'x':
- cb.cb_explain = B_TRUE;
- break;
- case 'D':
- cb.cb_dedup_stats = B_TRUE;
- break;
- case 'T':
- get_timestamp_arg(*optarg);
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
-
- argc -= optind;
- argv += optind;
-
- get_interval_count(&argc, argv, &interval, &count);
-
- if (argc == 0)
- cb.cb_allpools = B_TRUE;
-
- cb.cb_first = B_TRUE;
- cb.cb_print_status = B_TRUE;
-
- for (;;) {
- if (timestamp_fmt != NODATE)
- print_timestamp(timestamp_fmt);
-
- ret = for_each_pool(argc, argv, B_TRUE, NULL,
- status_callback, &cb);
-
- if (argc == 0 && cb.cb_count == 0)
- (void) printf(gettext("no pools available\n"));
- else if (cb.cb_explain && cb.cb_first && cb.cb_allpools)
- (void) printf(gettext("all pools are healthy\n"));
-
- if (ret != 0)
- return (ret);
-
- if (interval == 0)
- break;
-
- if (count != 0 && --count == 0)
- break;
-
- (void) sleep(interval);
- }
-
- return (0);
-}
-
-typedef struct upgrade_cbdata {
- boolean_t cb_first;
- boolean_t cb_unavail;
- char cb_poolname[ZFS_MAX_DATASET_NAME_LEN];
- int cb_argc;
- uint64_t cb_version;
- char **cb_argv;
-} upgrade_cbdata_t;
-
-#ifdef __FreeBSD__
-static int
-is_root_pool(zpool_handle_t *zhp)
-{
- static struct statfs sfs;
- static char *poolname = NULL;
- static boolean_t stated = B_FALSE;
- char *slash;
-
- if (!stated) {
- stated = B_TRUE;
- if (statfs("/", &sfs) == -1) {
- (void) fprintf(stderr,
- "Unable to stat root file system: %s.\n",
- strerror(errno));
- return (0);
- }
- if (strcmp(sfs.f_fstypename, "zfs") != 0)
- return (0);
- poolname = sfs.f_mntfromname;
- if ((slash = strchr(poolname, '/')) != NULL)
- *slash = '\0';
- }
- return (poolname != NULL && strcmp(poolname, zpool_get_name(zhp)) == 0);
-}
-
-static void
-root_pool_upgrade_check(zpool_handle_t *zhp, char *poolname, int size)
-{
-
- if (poolname[0] == '\0' && is_root_pool(zhp))
- (void) strlcpy(poolname, zpool_get_name(zhp), size);
-}
-#endif /* FreeBSD */
-
-static int
-upgrade_version(zpool_handle_t *zhp, uint64_t version)
-{
- int ret;
- nvlist_t *config;
- uint64_t oldversion;
-
- config = zpool_get_config(zhp, NULL);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
- &oldversion) == 0);
-
- assert(SPA_VERSION_IS_SUPPORTED(oldversion));
- assert(oldversion < version);
-
- ret = zpool_upgrade(zhp, version);
- if (ret != 0)
- return (ret);
-
- if (version >= SPA_VERSION_FEATURES) {
- (void) printf(gettext("Successfully upgraded "
- "'%s' from version %llu to feature flags.\n"),
- zpool_get_name(zhp), oldversion);
- } else {
- (void) printf(gettext("Successfully upgraded "
- "'%s' from version %llu to version %llu.\n"),
- zpool_get_name(zhp), oldversion, version);
- }
-
- return (0);
-}
-
-static int
-upgrade_enable_all(zpool_handle_t *zhp, int *countp)
-{
- int i, ret, count;
- boolean_t firstff = B_TRUE;
- nvlist_t *enabled = zpool_get_features(zhp);
-
- count = 0;
- for (i = 0; i < SPA_FEATURES; i++) {
- const char *fname = spa_feature_table[i].fi_uname;
- const char *fguid = spa_feature_table[i].fi_guid;
- if (!nvlist_exists(enabled, fguid)) {
- char *propname;
- verify(-1 != asprintf(&propname, "feature@%s", fname));
- ret = zpool_set_prop(zhp, propname,
- ZFS_FEATURE_ENABLED);
- if (ret != 0) {
- free(propname);
- return (ret);
- }
- count++;
-
- if (firstff) {
- (void) printf(gettext("Enabled the "
- "following features on '%s':\n"),
- zpool_get_name(zhp));
- firstff = B_FALSE;
- }
- (void) printf(gettext(" %s\n"), fname);
- free(propname);
- }
- }
-
- if (countp != NULL)
- *countp = count;
- return (0);
-}
-
-static int
-upgrade_cb(zpool_handle_t *zhp, void *arg)
-{
- upgrade_cbdata_t *cbp = arg;
- nvlist_t *config;
- uint64_t version;
- boolean_t printnl = B_FALSE;
- int ret;
-
- if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
- (void) fprintf(stderr, gettext("cannot upgrade '%s': pool is "
- "currently unavailable.\n\n"), zpool_get_name(zhp));
- cbp->cb_unavail = B_TRUE;
- /* Allow iteration to continue. */
- return (0);
- }
-
- config = zpool_get_config(zhp, NULL);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
- &version) == 0);
-
- assert(SPA_VERSION_IS_SUPPORTED(version));
-
- if (version < cbp->cb_version) {
- cbp->cb_first = B_FALSE;
- ret = upgrade_version(zhp, cbp->cb_version);
- if (ret != 0)
- return (ret);
-#ifdef __FreeBSD__
- root_pool_upgrade_check(zhp, cbp->cb_poolname,
- sizeof(cbp->cb_poolname));
-#endif /* __FreeBSD__ */
- printnl = B_TRUE;
-
-#ifdef illumos
- /*
- * If they did "zpool 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;
-#endif
- }
-
- if (cbp->cb_version >= SPA_VERSION_FEATURES) {
- int count;
- ret = upgrade_enable_all(zhp, &count);
- if (ret != 0)
- return (ret);
-
- if (count > 0) {
- cbp->cb_first = B_FALSE;
- printnl = B_TRUE;
-#ifdef __FreeBSD__
- root_pool_upgrade_check(zhp, cbp->cb_poolname,
- sizeof(cbp->cb_poolname));
-#endif /* __FreeBSD__ */
- /*
- * If they did "zpool 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 (printnl) {
- (void) printf(gettext("\n"));
- }
-
- return (0);
-}
-
-static int
-upgrade_list_unavail(zpool_handle_t *zhp, void *arg)
-{
- upgrade_cbdata_t *cbp = arg;
-
- if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
- if (cbp->cb_first) {
- (void) fprintf(stderr, gettext("The following pools "
- "are unavailable and cannot be upgraded as this "
- "time.\n\n"));
- (void) fprintf(stderr, gettext("POOL\n"));
- (void) fprintf(stderr, gettext("------------\n"));
- cbp->cb_first = B_FALSE;
- }
- (void) printf(gettext("%s\n"), zpool_get_name(zhp));
- cbp->cb_unavail = B_TRUE;
- }
- return (0);
-}
-
-static int
-upgrade_list_older_cb(zpool_handle_t *zhp, void *arg)
-{
- upgrade_cbdata_t *cbp = arg;
- nvlist_t *config;
- uint64_t version;
-
- if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
- /*
- * This will have been reported by upgrade_list_unavail so
- * just allow iteration to continue.
- */
- cbp->cb_unavail = B_TRUE;
- return (0);
- }
-
- config = zpool_get_config(zhp, NULL);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
- &version) == 0);
-
- assert(SPA_VERSION_IS_SUPPORTED(version));
-
- if (version < SPA_VERSION_FEATURES) {
- if (cbp->cb_first) {
- (void) printf(gettext("The following pools are "
- "formatted with legacy version numbers and can\n"
- "be upgraded to use feature flags. After "
- "being upgraded, these pools\nwill no "
- "longer be accessible by software that does not "
- "support feature\nflags.\n\n"));
- (void) printf(gettext("VER POOL\n"));
- (void) printf(gettext("--- ------------\n"));
- cbp->cb_first = B_FALSE;
- }
-
- (void) printf("%2llu %s\n", (u_longlong_t)version,
- zpool_get_name(zhp));
- }
-
- return (0);
-}
-
-static int
-upgrade_list_disabled_cb(zpool_handle_t *zhp, void *arg)
-{
- upgrade_cbdata_t *cbp = arg;
- nvlist_t *config;
- uint64_t version;
-
- if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
- /*
- * This will have been reported by upgrade_list_unavail so
- * just allow iteration to continue.
- */
- cbp->cb_unavail = B_TRUE;
- return (0);
- }
-
- config = zpool_get_config(zhp, NULL);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
- &version) == 0);
-
- if (version >= SPA_VERSION_FEATURES) {
- int i;
- boolean_t poolfirst = B_TRUE;
- nvlist_t *enabled = zpool_get_features(zhp);
-
- for (i = 0; i < SPA_FEATURES; i++) {
- const char *fguid = spa_feature_table[i].fi_guid;
- const char *fname = spa_feature_table[i].fi_uname;
- if (!nvlist_exists(enabled, fguid)) {
- if (cbp->cb_first) {
- (void) printf(gettext("\nSome "
- "supported features are not "
- "enabled on the following pools. "
- "Once a\nfeature is enabled the "
- "pool may become incompatible with "
- "software\nthat does not support "
- "the feature. See "
- "zpool-features(7) for "
- "details.\n\n"));
- (void) printf(gettext("POOL "
- "FEATURE\n"));
- (void) printf(gettext("------"
- "---------\n"));
- cbp->cb_first = B_FALSE;
- }
-
- if (poolfirst) {
- (void) printf(gettext("%s\n"),
- zpool_get_name(zhp));
- poolfirst = B_FALSE;
- }
-
- (void) printf(gettext(" %s\n"), fname);
- }
- }
- }
-
- return (0);
-}
-
-/* ARGSUSED */
-static int
-upgrade_one(zpool_handle_t *zhp, void *data)
-{
- boolean_t printnl = B_FALSE;
- upgrade_cbdata_t *cbp = data;
- uint64_t cur_version;
- int ret;
-
- if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
- (void) fprintf(stderr, gettext("cannot upgrade '%s': pool is "
- "is currently unavailable.\n\n"), zpool_get_name(zhp));
- cbp->cb_unavail = B_TRUE;
- return (1);
- }
-
- if (strcmp("log", zpool_get_name(zhp)) == 0) {
- (void) printf(gettext("'log' is now a reserved word\n"
- "Pool 'log' must be renamed using export and import"
- " to upgrade.\n\n"));
- return (1);
- }
-
- cur_version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL);
- if (cur_version > cbp->cb_version) {
- (void) printf(gettext("Pool '%s' is already formatted "
- "using more current version '%llu'.\n\n"),
- zpool_get_name(zhp), cur_version);
- return (0);
- }
-
- if (cbp->cb_version != SPA_VERSION && cur_version == cbp->cb_version) {
- (void) printf(gettext("Pool '%s' is already formatted "
- "using version %llu.\n\n"), zpool_get_name(zhp),
- cbp->cb_version);
- return (0);
- }
-
- if (cur_version != cbp->cb_version) {
- printnl = B_TRUE;
- ret = upgrade_version(zhp, cbp->cb_version);
- if (ret != 0)
- return (ret);
-#ifdef __FreeBSD__
- root_pool_upgrade_check(zhp, cbp->cb_poolname,
- sizeof(cbp->cb_poolname));
-#endif /* __FreeBSD__ */
- }
-
- if (cbp->cb_version >= SPA_VERSION_FEATURES) {
- int count = 0;
- ret = upgrade_enable_all(zhp, &count);
- if (ret != 0)
- return (ret);
-
- if (count != 0) {
- printnl = B_TRUE;
-#ifdef __FreeBSD__
- root_pool_upgrade_check(zhp, cbp->cb_poolname,
- sizeof(cbp->cb_poolname));
-#endif /* __FreeBSD __*/
- } else if (cur_version == SPA_VERSION) {
- (void) printf(gettext("Pool '%s' already has all "
- "supported features enabled.\n\n"),
- zpool_get_name(zhp));
- }
- }
-
- if (printnl) {
- (void) printf(gettext("\n"));
- }
-
- return (0);
-}
-
-/*
- * zpool upgrade
- * zpool upgrade -v
- * zpool upgrade [-V version] <-a | pool ...>
- *
- * With no arguments, display downrev'd ZFS pool available for upgrade.
- * Individual pools can be upgraded by specifying the pool, and '-a' will
- * upgrade all pools.
- */
-int
-zpool_do_upgrade(int argc, char **argv)
-{
- int c;
- upgrade_cbdata_t cb = { 0 };
- int ret = 0;
- boolean_t showversions = B_FALSE;
- boolean_t upgradeall = B_FALSE;
- char *end;
-
-
- /* check options */
- while ((c = getopt(argc, argv, ":avV:")) != -1) {
- switch (c) {
- case 'a':
- upgradeall = B_TRUE;
- break;
- case 'v':
- showversions = B_TRUE;
- break;
- case 'V':
- cb.cb_version = strtoll(optarg, &end, 10);
- if (*end != '\0' ||
- !SPA_VERSION_IS_SUPPORTED(cb.cb_version)) {
- (void) fprintf(stderr,
- gettext("invalid version '%s'\n"), optarg);
- 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);
- }
- }
-
- cb.cb_argc = argc;
- cb.cb_argv = argv;
- argc -= optind;
- argv += optind;
-
- if (cb.cb_version == 0) {
- cb.cb_version = SPA_VERSION;
- } else if (!upgradeall && argc == 0) {
- (void) fprintf(stderr, gettext("-V option is "
- "incompatible with other arguments\n"));
- usage(B_FALSE);
- }
-
- if (showversions) {
- if (upgradeall || argc != 0) {
- (void) fprintf(stderr, gettext("-v option is "
- "incompatible with other arguments\n"));
- usage(B_FALSE);
- }
- } else if (upgradeall) {
- if (argc != 0) {
- (void) fprintf(stderr, gettext("-a option should not "
- "be used along with a pool name\n"));
- usage(B_FALSE);
- }
- }
-
- (void) printf(gettext("This system supports ZFS pool feature "
- "flags.\n\n"));
- if (showversions) {
- int i;
-
- (void) printf(gettext("The following features are "
- "supported:\n\n"));
- (void) printf(gettext("FEAT DESCRIPTION\n"));
- (void) printf("----------------------------------------------"
- "---------------\n");
- for (i = 0; i < SPA_FEATURES; i++) {
- zfeature_info_t *fi = &spa_feature_table[i];
- const char *ro =
- (fi->fi_flags & ZFEATURE_FLAG_READONLY_COMPAT) ?
- " (read-only compatible)" : "";
-
- (void) printf("%-37s%s\n", fi->fi_uname, ro);
- (void) printf(" %s\n", fi->fi_desc);
- }
- (void) printf("\n");
-
- (void) printf(gettext("The following legacy versions are also "
- "supported:\n\n"));
- (void) printf(gettext("VER DESCRIPTION\n"));
- (void) printf("--- -----------------------------------------"
- "---------------\n");
- (void) printf(gettext(" 1 Initial ZFS version\n"));
- (void) printf(gettext(" 2 Ditto blocks "
- "(replicated metadata)\n"));
- (void) printf(gettext(" 3 Hot spares and double parity "
- "RAID-Z\n"));
- (void) printf(gettext(" 4 zpool history\n"));
- (void) printf(gettext(" 5 Compression using the gzip "
- "algorithm\n"));
- (void) printf(gettext(" 6 bootfs pool property\n"));
- (void) printf(gettext(" 7 Separate intent log devices\n"));
- (void) printf(gettext(" 8 Delegated administration\n"));
- (void) printf(gettext(" 9 refquota and refreservation "
- "properties\n"));
- (void) printf(gettext(" 10 Cache devices\n"));
- (void) printf(gettext(" 11 Improved scrub performance\n"));
- (void) printf(gettext(" 12 Snapshot properties\n"));
- (void) printf(gettext(" 13 snapused property\n"));
- (void) printf(gettext(" 14 passthrough-x aclinherit\n"));
- (void) printf(gettext(" 15 user/group space accounting\n"));
- (void) printf(gettext(" 16 stmf property support\n"));
- (void) printf(gettext(" 17 Triple-parity RAID-Z\n"));
- (void) printf(gettext(" 18 Snapshot user holds\n"));
- (void) printf(gettext(" 19 Log device removal\n"));
- (void) printf(gettext(" 20 Compression using zle "
- "(zero-length encoding)\n"));
- (void) printf(gettext(" 21 Deduplication\n"));
- (void) printf(gettext(" 22 Received properties\n"));
- (void) printf(gettext(" 23 Slim ZIL\n"));
- (void) printf(gettext(" 24 System attributes\n"));
- (void) printf(gettext(" 25 Improved scrub stats\n"));
- (void) printf(gettext(" 26 Improved snapshot deletion "
- "performance\n"));
- (void) printf(gettext(" 27 Improved snapshot creation "
- "performance\n"));
- (void) printf(gettext(" 28 Multiple vdev replacements\n"));
- (void) printf(gettext("\nFor more information on a particular "
- "version, including supported releases,\n"));
- (void) printf(gettext("see the ZFS Administration Guide.\n\n"));
- } else if (argc == 0 && upgradeall) {
- cb.cb_first = B_TRUE;
- ret = zpool_iter(g_zfs, upgrade_cb, &cb);
- if (ret == 0 && cb.cb_first) {
- if (cb.cb_version == SPA_VERSION) {
- (void) printf(gettext("All %spools are already "
- "formatted using feature flags.\n\n"),
- cb.cb_unavail ? gettext("available ") : "");
- (void) printf(gettext("Every %sfeature flags "
- "pool already has all supported features "
- "enabled.\n"),
- cb.cb_unavail ? gettext("available ") : "");
- } else {
- (void) printf(gettext("All pools are already "
- "formatted with version %llu or higher.\n"),
- cb.cb_version);
- }
- }
- } else if (argc == 0) {
- cb.cb_first = B_TRUE;
- ret = zpool_iter(g_zfs, upgrade_list_unavail, &cb);
- assert(ret == 0);
-
- if (!cb.cb_first) {
- (void) fprintf(stderr, "\n");
- }
-
- cb.cb_first = B_TRUE;
- ret = zpool_iter(g_zfs, upgrade_list_older_cb, &cb);
- assert(ret == 0);
-
- if (cb.cb_first) {
- (void) printf(gettext("All %spools are formatted using "
- "feature flags.\n\n"), cb.cb_unavail ?
- gettext("available ") : "");
- } else {
- (void) printf(gettext("\nUse 'zpool upgrade -v' "
- "for a list of available legacy versions.\n"));
- }
-
- cb.cb_first = B_TRUE;
- ret = zpool_iter(g_zfs, upgrade_list_disabled_cb, &cb);
- assert(ret == 0);
-
- if (cb.cb_first) {
- (void) printf(gettext("Every %sfeature flags pool has "
- "all supported features enabled.\n"),
- cb.cb_unavail ? gettext("available ") : "");
- } else {
- (void) printf(gettext("\n"));
- }
- } else {
- ret = for_each_pool(argc, argv, B_TRUE, NULL,
- upgrade_one, &cb);
- }
-
- if (cb.cb_poolname[0] != '\0') {
- (void) printf(
- "If you boot from pool '%s', don't forget to update boot code.\n"
- "Assuming you use GPT partitioning and da0 is your boot disk\n"
- "the following command will do it:\n"
- "\n"
- "\tgpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 da0\n\n",
- cb.cb_poolname);
- }
-
- return (ret);
-}
-
-typedef struct hist_cbdata {
- boolean_t first;
- boolean_t longfmt;
- boolean_t internal;
-} hist_cbdata_t;
-
-static void
-print_history_records(nvlist_t *nvhis, hist_cbdata_t *cb)
-{
- nvlist_t **records;
- uint_t numrecords;
- int i;
-
- verify(nvlist_lookup_nvlist_array(nvhis, ZPOOL_HIST_RECORD,
- &records, &numrecords) == 0);
- for (i = 0; i < numrecords; i++) {
- nvlist_t *rec = records[i];
- char tbuf[30] = "";
-
- if (nvlist_exists(rec, ZPOOL_HIST_TIME)) {
- time_t tsec;
- struct tm t;
-
- tsec = fnvlist_lookup_uint64(records[i],
- ZPOOL_HIST_TIME);
- (void) localtime_r(&tsec, &t);
- (void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t);
- }
-
- if (nvlist_exists(rec, ZPOOL_HIST_CMD)) {
- (void) printf("%s %s", tbuf,
- fnvlist_lookup_string(rec, ZPOOL_HIST_CMD));
- } else if (nvlist_exists(rec, ZPOOL_HIST_INT_EVENT)) {
- int ievent =
- fnvlist_lookup_uint64(rec, ZPOOL_HIST_INT_EVENT);
- if (!cb->internal)
- continue;
- if (ievent >= ZFS_NUM_LEGACY_HISTORY_EVENTS) {
- (void) printf("%s unrecognized record:\n",
- tbuf);
- dump_nvlist(rec, 4);
- continue;
- }
- (void) printf("%s [internal %s txg:%lld] %s", tbuf,
- zfs_history_event_names[ievent],
- fnvlist_lookup_uint64(rec, ZPOOL_HIST_TXG),
- fnvlist_lookup_string(rec, ZPOOL_HIST_INT_STR));
- } else if (nvlist_exists(rec, ZPOOL_HIST_INT_NAME)) {
- if (!cb->internal)
- continue;
- (void) printf("%s [txg:%lld] %s", tbuf,
- fnvlist_lookup_uint64(rec, ZPOOL_HIST_TXG),
- fnvlist_lookup_string(rec, ZPOOL_HIST_INT_NAME));
- if (nvlist_exists(rec, ZPOOL_HIST_DSNAME)) {
- (void) printf(" %s (%llu)",
- fnvlist_lookup_string(rec,
- ZPOOL_HIST_DSNAME),
- fnvlist_lookup_uint64(rec,
- ZPOOL_HIST_DSID));
- }
- (void) printf(" %s", fnvlist_lookup_string(rec,
- ZPOOL_HIST_INT_STR));
- } else if (nvlist_exists(rec, ZPOOL_HIST_IOCTL)) {
- if (!cb->internal)
- continue;
- (void) printf("%s ioctl %s\n", tbuf,
- fnvlist_lookup_string(rec, ZPOOL_HIST_IOCTL));
- if (nvlist_exists(rec, ZPOOL_HIST_INPUT_NVL)) {
- (void) printf(" input:\n");
- dump_nvlist(fnvlist_lookup_nvlist(rec,
- ZPOOL_HIST_INPUT_NVL), 8);
- }
- if (nvlist_exists(rec, ZPOOL_HIST_OUTPUT_NVL)) {
- (void) printf(" output:\n");
- dump_nvlist(fnvlist_lookup_nvlist(rec,
- ZPOOL_HIST_OUTPUT_NVL), 8);
- }
- if (nvlist_exists(rec, ZPOOL_HIST_ERRNO)) {
- (void) printf(" errno: %lld\n",
- fnvlist_lookup_int64(rec,
- ZPOOL_HIST_ERRNO));
- }
- } else {
- if (!cb->internal)
- continue;
- (void) printf("%s unrecognized record:\n", tbuf);
- dump_nvlist(rec, 4);
- }
-
- if (!cb->longfmt) {
- (void) printf("\n");
- continue;
- }
- (void) printf(" [");
- if (nvlist_exists(rec, ZPOOL_HIST_WHO)) {
- uid_t who = fnvlist_lookup_uint64(rec, ZPOOL_HIST_WHO);
- struct passwd *pwd = getpwuid(who);
- (void) printf("user %d ", (int)who);
- if (pwd != NULL)
- (void) printf("(%s) ", pwd->pw_name);
- }
- if (nvlist_exists(rec, ZPOOL_HIST_HOST)) {
- (void) printf("on %s",
- fnvlist_lookup_string(rec, ZPOOL_HIST_HOST));
- }
- if (nvlist_exists(rec, ZPOOL_HIST_ZONE)) {
- (void) printf(":%s",
- fnvlist_lookup_string(rec, ZPOOL_HIST_ZONE));
- }
- (void) printf("]");
- (void) printf("\n");
- }
-}
-
-/*
- * Print out the command history for a specific pool.
- */
-static int
-get_history_one(zpool_handle_t *zhp, void *data)
-{
- nvlist_t *nvhis;
- int ret;
- hist_cbdata_t *cb = (hist_cbdata_t *)data;
- uint64_t off = 0;
- boolean_t eof = B_FALSE;
-
- cb->first = B_FALSE;
-
- (void) printf(gettext("History for '%s':\n"), zpool_get_name(zhp));
-
- while (!eof) {
- if ((ret = zpool_get_history(zhp, &nvhis, &off, &eof)) != 0)
- return (ret);
-
- print_history_records(nvhis, cb);
- nvlist_free(nvhis);
- }
- (void) printf("\n");
-
- return (ret);
-}
-
-/*
- * zpool history <pool>
- *
- * Displays the history of commands that modified pools.
- */
-int
-zpool_do_history(int argc, char **argv)
-{
- hist_cbdata_t cbdata = { 0 };
- int ret;
- int c;
-
- cbdata.first = B_TRUE;
- /* check options */
- while ((c = getopt(argc, argv, "li")) != -1) {
- switch (c) {
- case 'l':
- cbdata.longfmt = B_TRUE;
- break;
- case 'i':
- cbdata.internal = B_TRUE;
- break;
- case '?':
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- optopt);
- usage(B_FALSE);
- }
- }
- argc -= optind;
- argv += optind;
-
- ret = for_each_pool(argc, argv, B_FALSE, NULL, get_history_one,
- &cbdata);
-
- if (argc == 0 && cbdata.first == B_TRUE) {
- (void) printf(gettext("no pools available\n"));
- return (0);
- }
-
- return (ret);
-}
-
-static int
-get_callback(zpool_handle_t *zhp, void *data)
-{
- zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data;
- char value[MAXNAMELEN];
- zprop_source_t srctype;
- zprop_list_t *pl;
-
- for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) {
-
- /*
- * Skip the special fake placeholder. This will also skip
- * over the name property when 'all' is specified.
- */
- if (pl->pl_prop == ZPOOL_PROP_NAME &&
- pl == cbp->cb_proplist)
- continue;
-
- if (pl->pl_prop == ZPROP_INVAL &&
- (zpool_prop_feature(pl->pl_user_prop) ||
- zpool_prop_unsupported(pl->pl_user_prop))) {
- srctype = ZPROP_SRC_LOCAL;
-
- if (zpool_prop_get_feature(zhp, pl->pl_user_prop,
- value, sizeof (value)) == 0) {
- zprop_print_one_property(zpool_get_name(zhp),
- cbp, pl->pl_user_prop, value, srctype,
- NULL, NULL);
- }
- } else {
- if (zpool_get_prop(zhp, pl->pl_prop, value,
- sizeof (value), &srctype, cbp->cb_literal) != 0)
- continue;
-
- zprop_print_one_property(zpool_get_name(zhp), cbp,
- zpool_prop_to_name(pl->pl_prop), value, srctype,
- NULL, NULL);
- }
- }
- return (0);
-}
-
-/*
- * zpool get [-Hp] [-o "all" | field[,...]] <"all" | property[,...]> <pool> ...
- *
- * -H Scripted mode. Don't display headers, and separate properties
- * by a single tab.
- * -o List of columns to display. Defaults to
- * "name,property,value,source".
- * -p Diplay values in parsable (exact) format.
- *
- * Get properties of pools in the system. Output space statistics
- * for each one as well as other attributes.
- */
-int
-zpool_do_get(int argc, char **argv)
-{
- zprop_get_cbdata_t cb = { 0 };
- zprop_list_t fake_name = { 0 };
- int ret;
- int c, i;
- char *value;
-
- cb.cb_first = B_TRUE;
-
- /*
- * 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_POOL;
-
- /* check options */
- while ((c = getopt(argc, argv, ":Hpo:")) != -1) {
- switch (c) {
- case 'p':
- cb.cb_literal = B_TRUE;
- break;
- case 'H':
- cb.cb_scripted = B_TRUE;
- break;
- case 'o':
- bzero(&cb.cb_columns, sizeof (cb.cb_columns));
- i = 0;
- while (*optarg != '\0') {
- static char *col_subopts[] =
- { "name", "property", "value", "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_SOURCE;
- break;
- case 4:
- 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_SOURCE;
- i = ZFS_GET_NCOLS;
- break;
- default:
- (void) fprintf(stderr,
- gettext("invalid column name "
- "'%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);
- }
-
- if (zprop_get_list(g_zfs, argv[0], &cb.cb_proplist,
- ZFS_TYPE_POOL) != 0)
- usage(B_FALSE);
-
- argc--;
- argv++;
-
- if (cb.cb_proplist != NULL) {
- fake_name.pl_prop = ZPOOL_PROP_NAME;
- fake_name.pl_width = strlen(gettext("NAME"));
- fake_name.pl_next = cb.cb_proplist;
- cb.cb_proplist = &fake_name;
- }
-
- ret = for_each_pool(argc, argv, B_TRUE, &cb.cb_proplist,
- 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);
-}
-
-typedef struct set_cbdata {
- char *cb_propname;
- char *cb_value;
- boolean_t cb_any_successful;
-} set_cbdata_t;
-
-int
-set_callback(zpool_handle_t *zhp, void *data)
-{
- int error;
- set_cbdata_t *cb = (set_cbdata_t *)data;
-
- error = zpool_set_prop(zhp, cb->cb_propname, cb->cb_value);
-
- if (!error)
- cb->cb_any_successful = B_TRUE;
-
- return (error);
-}
-
-int
-zpool_do_set(int argc, char **argv)
-{
- set_cbdata_t cb = { 0 };
- int error;
-
- if (argc > 1 && argv[1][0] == '-') {
- (void) fprintf(stderr, gettext("invalid option '%c'\n"),
- argv[1][1]);
- usage(B_FALSE);
- }
-
- if (argc < 2) {
- (void) fprintf(stderr, gettext("missing property=value "
- "argument\n"));
- usage(B_FALSE);
- }
-
- if (argc < 3) {
- (void) fprintf(stderr, gettext("missing pool name\n"));
- usage(B_FALSE);
- }
-
- if (argc > 3) {
- (void) fprintf(stderr, gettext("too many pool names\n"));
- usage(B_FALSE);
- }
-
- cb.cb_propname = argv[1];
- cb.cb_value = strchr(cb.cb_propname, '=');
- if (cb.cb_value == NULL) {
- (void) fprintf(stderr, gettext("missing value in "
- "property=value argument\n"));
- usage(B_FALSE);
- }
-
- *(cb.cb_value) = '\0';
- cb.cb_value++;
-
- error = for_each_pool(argc - 2, argv + 2, B_TRUE, NULL,
- set_callback, &cb);
-
- return (error);
-}
-
-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);
-}
-
-int
-main(int argc, char **argv)
-{
- int ret = 0;
- int i;
- char *cmdname;
-
- (void) setlocale(LC_ALL, "");
- (void) textdomain(TEXT_DOMAIN);
-
- if ((g_zfs = libzfs_init()) == NULL) {
- (void) fprintf(stderr, gettext("internal error: failed to "
- "initialize ZFS library\n"));
- return (1);
- }
-
- libzfs_print_on_error(g_zfs, B_TRUE);
-
- opterr = 0;
-
- /*
- * Make sure the user has specified some command.
- */
- if (argc < 2) {
- (void) fprintf(stderr, gettext("missing command\n"));
- usage(B_FALSE);
- }
-
- cmdname = argv[1];
-
- /*
- * Special case '-?'
- */
- if (strcmp(cmdname, "-?") == 0)
- usage(B_TRUE);
-
- zfs_save_arguments(argc, argv, history_str, sizeof (history_str));
-
- /*
- * Run the appropriate command.
- */
- 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, '=')) {
- verify(find_command_idx("set", &i) == 0);
- current_command = &command_table[i];
- ret = command_table[i].func(argc, argv);
- } else if (strcmp(cmdname, "freeze") == 0 && argc == 3) {
- /*
- * 'freeze' is a vile debugging abomination, so we treat
- * it as such.
- */
- zfs_cmd_t zc = { 0 };
- (void) strlcpy(zc.zc_name, argv[2], sizeof (zc.zc_name));
- return (!!zfs_ioctl(g_zfs, ZFS_IOC_POOL_FREEZE, &zc));
- } else {
- (void) fprintf(stderr, gettext("unrecognized "
- "command '%s'\n"), cmdname);
- usage(B_FALSE);
- }
-
- 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/zpool/zpool_util.c b/cddl/contrib/opensolaris/cmd/zpool/zpool_util.c
deleted file mode 100644
index c7a002efb17cf..0000000000000
--- a/cddl/contrib/opensolaris/cmd/zpool/zpool_util.c
+++ /dev/null
@@ -1,86 +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.
- */
-
-#include <errno.h>
-#include <libgen.h>
-#include <libintl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-
-#include "zpool_util.h"
-
-/*
- * Utility function to guarantee malloc() success.
- */
-void *
-safe_malloc(size_t size)
-{
- void *data;
-
- if ((data = calloc(1, size)) == NULL) {
- (void) fprintf(stderr, "internal error: out of memory\n");
- exit(1);
- }
-
- return (data);
-}
-
-/*
- * Display an out of memory error message and abort the current program.
- */
-void
-zpool_no_memory(void)
-{
- assert(errno == ENOMEM);
- (void) fprintf(stderr,
- gettext("internal error: out of memory\n"));
- exit(1);
-}
-
-/*
- * Return the number of logs in supplied nvlist
- */
-uint_t
-num_logs(nvlist_t *nv)
-{
- uint_t nlogs = 0;
- uint_t c, children;
- nvlist_t **child;
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0)
- return (0);
-
- for (c = 0; c < children; c++) {
- uint64_t is_log = B_FALSE;
-
- (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
- &is_log);
- if (is_log)
- nlogs++;
- }
- return (nlogs);
-}
diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool_util.h b/cddl/contrib/opensolaris/cmd/zpool/zpool_util.h
deleted file mode 100644
index 118029a228667..0000000000000
--- a/cddl/contrib/opensolaris/cmd/zpool/zpool_util.h
+++ /dev/null
@@ -1,73 +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 ZPOOL_UTIL_H
-#define ZPOOL_UTIL_H
-
-#include <libnvpair.h>
-#include <libzfs.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Basic utility functions
- */
-void *safe_malloc(size_t);
-void zpool_no_memory(void);
-uint_t num_logs(nvlist_t *nv);
-
-/*
- * Virtual device functions
- */
-
-nvlist_t *make_root_vdev(zpool_handle_t *zhp, int force, int check_rep,
- boolean_t replacing, boolean_t dryrun, zpool_boot_label_t boot_type,
- uint64_t boot_size, int argc, char **argv);
-nvlist_t *split_mirror_vdev(zpool_handle_t *zhp, char *newname,
- nvlist_t *props, splitflags_t flags, int argc, char **argv);
-
-/*
- * Pool list functions
- */
-int for_each_pool(int, char **, boolean_t unavail, zprop_list_t **,
- zpool_iter_f, void *);
-
-typedef struct zpool_list zpool_list_t;
-
-zpool_list_t *pool_list_get(int, char **, zprop_list_t **, int *);
-void pool_list_update(zpool_list_t *);
-int pool_list_iter(zpool_list_t *, int unavail, zpool_iter_f, void *);
-void pool_list_free(zpool_list_t *);
-int pool_list_count(zpool_list_t *);
-void pool_list_remove(zpool_list_t *, zpool_handle_t *);
-
-extern libzfs_handle_t *g_zfs;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* ZPOOL_UTIL_H */
diff --git a/cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c b/cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c
deleted file mode 100644
index 43d66d2263e05..0000000000000
--- a/cddl/contrib/opensolaris/cmd/zpool/zpool_vdev.c
+++ /dev/null
@@ -1,1729 +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) 2013, 2018 by Delphix. All rights reserved.
- * Copyright (c) 2016, 2017 Intel Corporation.
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>.
- */
-
-/*
- * Functions to convert between a list of vdevs and an nvlist representing the
- * configuration. Each entry in the list can be one of:
- *
- * Device vdevs
- * disk=(path=..., devid=...)
- * file=(path=...)
- *
- * Group vdevs
- * raidz[1|2]=(...)
- * mirror=(...)
- *
- * Hot spares
- *
- * While the underlying implementation supports it, group vdevs cannot contain
- * other group vdevs. All userland verification of devices is contained within
- * this file. If successful, the nvlist returned can be passed directly to the
- * kernel; we've done as much verification as possible in userland.
- *
- * Hot spares are a special case, and passed down as an array of disk vdevs, at
- * the same level as the root of the vdev tree.
- *
- * The only function exported by this file is 'make_root_vdev'. The
- * function performs several passes:
- *
- * 1. Construct the vdev specification. Performs syntax validation and
- * makes sure each device is valid.
- * 2. Check for devices in use. Using libdiskmgt, makes sure that no
- * devices are also in use. Some can be overridden using the 'force'
- * flag, others cannot.
- * 3. Check for replication errors if the 'force' flag is not specified.
- * validates that the replication level is consistent across the
- * entire pool.
- * 4. Call libzfs to label any whole disks with an EFI label.
- */
-
-#include <assert.h>
-#include <devid.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <libintl.h>
-#include <libnvpair.h>
-#include <limits.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <paths.h>
-#include <sys/stat.h>
-#include <sys/disk.h>
-#include <sys/mntent.h>
-#include <libgeom.h>
-
-#include "zpool_util.h"
-
-#define BACKUP_SLICE "s2"
-
-/*
- * For any given vdev specification, we can have multiple errors. The
- * vdev_error() function keeps track of whether we have seen an error yet, and
- * prints out a header if its the first error we've seen.
- */
-boolean_t error_seen;
-boolean_t is_force;
-
-/*PRINTFLIKE1*/
-static void
-vdev_error(const char *fmt, ...)
-{
- va_list ap;
-
- if (!error_seen) {
- (void) fprintf(stderr, gettext("invalid vdev specification\n"));
- if (!is_force)
- (void) fprintf(stderr, gettext("use '-f' to override "
- "the following errors:\n"));
- else
- (void) fprintf(stderr, gettext("the following errors "
- "must be manually repaired:\n"));
- error_seen = B_TRUE;
- }
-
- va_start(ap, fmt);
- (void) vfprintf(stderr, fmt, ap);
- va_end(ap);
-}
-
-#ifdef illumos
-static void
-libdiskmgt_error(int error)
-{
- /*
- * ENXIO/ENODEV is a valid error message if the device doesn't live in
- * /dev/dsk. Don't bother printing an error message in this case.
- */
- if (error == ENXIO || error == ENODEV)
- return;
-
- (void) fprintf(stderr, gettext("warning: device in use checking "
- "failed: %s\n"), strerror(error));
-}
-
-/*
- * Validate a device, passing the bulk of the work off to libdiskmgt.
- */
-static int
-check_slice(const char *path, int force, boolean_t wholedisk, boolean_t isspare)
-{
- char *msg;
- int error = 0;
- dm_who_type_t who;
-
- if (force)
- who = DM_WHO_ZPOOL_FORCE;
- else if (isspare)
- who = DM_WHO_ZPOOL_SPARE;
- else
- who = DM_WHO_ZPOOL;
-
- if (dm_inuse((char *)path, &msg, who, &error) || error) {
- if (error != 0) {
- libdiskmgt_error(error);
- return (0);
- } else {
- vdev_error("%s", msg);
- free(msg);
- return (-1);
- }
- }
-
- /*
- * If we're given a whole disk, ignore overlapping slices since we're
- * about to label it anyway.
- */
- error = 0;
- if (!wholedisk && !force &&
- (dm_isoverlapping((char *)path, &msg, &error) || error)) {
- if (error == 0) {
- /* dm_isoverlapping returned -1 */
- vdev_error(gettext("%s overlaps with %s\n"), path, msg);
- free(msg);
- return (-1);
- } else if (error != ENODEV) {
- /* libdiskmgt's devcache only handles physical drives */
- libdiskmgt_error(error);
- return (0);
- }
- }
-
- return (0);
-}
-
-
-/*
- * Validate a whole disk. Iterate over all slices on the disk and make sure
- * that none is in use by calling check_slice().
- */
-static int
-check_disk(const char *name, dm_descriptor_t disk, int force, int isspare)
-{
- dm_descriptor_t *drive, *media, *slice;
- int err = 0;
- int i;
- int ret;
-
- /*
- * Get the drive associated with this disk. This should never fail,
- * because we already have an alias handle open for the device.
- */
- if ((drive = dm_get_associated_descriptors(disk, DM_DRIVE,
- &err)) == NULL || *drive == NULL) {
- if (err)
- libdiskmgt_error(err);
- return (0);
- }
-
- if ((media = dm_get_associated_descriptors(*drive, DM_MEDIA,
- &err)) == NULL) {
- dm_free_descriptors(drive);
- if (err)
- libdiskmgt_error(err);
- return (0);
- }
-
- dm_free_descriptors(drive);
-
- /*
- * It is possible that the user has specified a removable media drive,
- * and the media is not present.
- */
- if (*media == NULL) {
- dm_free_descriptors(media);
- vdev_error(gettext("'%s' has no media in drive\n"), name);
- return (-1);
- }
-
- if ((slice = dm_get_associated_descriptors(*media, DM_SLICE,
- &err)) == NULL) {
- dm_free_descriptors(media);
- if (err)
- libdiskmgt_error(err);
- return (0);
- }
-
- dm_free_descriptors(media);
-
- ret = 0;
-
- /*
- * Iterate over all slices and report any errors. We don't care about
- * overlapping slices because we are using the whole disk.
- */
- for (i = 0; slice[i] != NULL; i++) {
- char *name = dm_get_name(slice[i], &err);
-
- if (check_slice(name, force, B_TRUE, isspare) != 0)
- ret = -1;
-
- dm_free_name(name);
- }
-
- dm_free_descriptors(slice);
- return (ret);
-}
-
-/*
- * Validate a device.
- */
-static int
-check_device(const char *path, boolean_t force, boolean_t isspare)
-{
- dm_descriptor_t desc;
- int err;
- char *dev;
-
- /*
- * For whole disks, libdiskmgt does not include the leading dev path.
- */
- dev = strrchr(path, '/');
- assert(dev != NULL);
- dev++;
- if ((desc = dm_get_descriptor_by_name(DM_ALIAS, dev, &err)) != NULL) {
- err = check_disk(path, desc, force, isspare);
- dm_free_descriptor(desc);
- return (err);
- }
-
- return (check_slice(path, force, B_FALSE, isspare));
-}
-#endif /* illumos */
-
-/*
- * Check that a file is valid. All we can do in this case is check that it's
- * not in use by another pool, and not in use by swap.
- */
-static int
-check_file(const char *file, boolean_t force, boolean_t isspare)
-{
- char *name;
- int fd;
- int ret = 0;
- int err;
- pool_state_t state;
- boolean_t inuse;
-
-#ifdef illumos
- if (dm_inuse_swap(file, &err)) {
- if (err)
- libdiskmgt_error(err);
- else
- vdev_error(gettext("%s is currently used by swap. "
- "Please see swap(1M).\n"), file);
- return (-1);
- }
-#endif
-
- if ((fd = open(file, O_RDONLY)) < 0)
- return (0);
-
- if (zpool_in_use(g_zfs, fd, &state, &name, &inuse) == 0 && inuse) {
- const char *desc;
-
- switch (state) {
- case POOL_STATE_ACTIVE:
- desc = gettext("active");
- break;
-
- case POOL_STATE_EXPORTED:
- desc = gettext("exported");
- break;
-
- case POOL_STATE_POTENTIALLY_ACTIVE:
- desc = gettext("potentially active");
- break;
-
- default:
- desc = gettext("unknown");
- break;
- }
-
- /*
- * Allow hot spares to be shared between pools.
- */
- if (state == POOL_STATE_SPARE && isspare)
- return (0);
-
- if (state == POOL_STATE_ACTIVE ||
- state == POOL_STATE_SPARE || !force) {
- switch (state) {
- case POOL_STATE_SPARE:
- vdev_error(gettext("%s is reserved as a hot "
- "spare for pool %s\n"), file, name);
- break;
- default:
- vdev_error(gettext("%s is part of %s pool "
- "'%s'\n"), file, desc, name);
- break;
- }
- ret = -1;
- }
-
- free(name);
- }
-
- (void) close(fd);
- return (ret);
-}
-
-static int
-check_device(const char *name, boolean_t force, boolean_t isspare)
-{
- char path[MAXPATHLEN];
-
- if (strncmp(name, _PATH_DEV, sizeof(_PATH_DEV) - 1) != 0)
- snprintf(path, sizeof(path), "%s%s", _PATH_DEV, name);
- else
- strlcpy(path, name, sizeof(path));
-
- return (check_file(path, force, isspare));
-}
-
-/*
- * By "whole disk" we mean an entire physical disk (something we can
- * label, toggle the write cache on, etc.) as opposed to the full
- * capacity of a pseudo-device such as lofi or did. We act as if we
- * are labeling the disk, which should be a pretty good test of whether
- * it's a viable device or not. Returns B_TRUE if it is and B_FALSE if
- * it isn't.
- */
-static boolean_t
-is_whole_disk(const char *arg)
-{
-#ifdef illumos
- struct dk_gpt *label;
- int fd;
- char path[MAXPATHLEN];
-
- (void) snprintf(path, sizeof (path), "%s%s%s",
- ZFS_RDISK_ROOT, strrchr(arg, '/'), BACKUP_SLICE);
- if ((fd = open(path, O_RDWR | O_NDELAY)) < 0)
- return (B_FALSE);
- if (efi_alloc_and_init(fd, EFI_NUMPAR, &label) != 0) {
- (void) close(fd);
- return (B_FALSE);
- }
- efi_free(label);
- (void) close(fd);
- return (B_TRUE);
-#else
- int fd;
-
- fd = g_open(arg, 0);
- if (fd >= 0) {
- g_close(fd);
- return (B_TRUE);
- }
- return (B_FALSE);
-#endif
-}
-
-/*
- * Create a leaf vdev. Determine if this is a file or a device. If it's a
- * device, fill in the device id to make a complete nvlist. Valid forms for a
- * leaf vdev are:
- *
- * /dev/dsk/xxx Complete disk path
- * /xxx Full path to file
- * xxx Shorthand for /dev/dsk/xxx
- */
-static nvlist_t *
-make_leaf_vdev(const char *arg, uint64_t is_log)
-{
- char path[MAXPATHLEN];
- struct stat64 statbuf;
- nvlist_t *vdev = NULL;
- char *type = NULL;
- boolean_t wholedisk = B_FALSE;
-
- /*
- * Determine what type of vdev this is, and put the full path into
- * 'path'. We detect whether this is a device of file afterwards by
- * checking the st_mode of the file.
- */
- if (arg[0] == '/') {
- /*
- * Complete device or file path. Exact type is determined by
- * examining the file descriptor afterwards.
- */
- wholedisk = is_whole_disk(arg);
- if (!wholedisk && (stat64(arg, &statbuf) != 0)) {
- (void) fprintf(stderr,
- gettext("cannot open '%s': %s\n"),
- arg, strerror(errno));
- return (NULL);
- }
-
- (void) strlcpy(path, arg, sizeof (path));
- } else {
- /*
- * This may be a short path for a device, or it could be total
- * gibberish. Check to see if it's a known device in
- * /dev/dsk/. As part of this check, see if we've been given a
- * an entire disk (minus the slice number).
- */
- if (strncmp(arg, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
- strlcpy(path, arg, sizeof (path));
- else
- snprintf(path, sizeof (path), "%s%s", _PATH_DEV, arg);
- wholedisk = is_whole_disk(path);
- if (!wholedisk && (stat64(path, &statbuf) != 0)) {
- /*
- * If we got ENOENT, then the user gave us
- * gibberish, so try to direct them with a
- * reasonable error message. Otherwise,
- * regurgitate strerror() since it's the best we
- * can do.
- */
- if (errno == ENOENT) {
- (void) fprintf(stderr,
- gettext("cannot open '%s': no such "
- "GEOM provider\n"), arg);
- (void) fprintf(stderr,
- gettext("must be a full path or "
- "shorthand device name\n"));
- return (NULL);
- } else {
- (void) fprintf(stderr,
- gettext("cannot open '%s': %s\n"),
- path, strerror(errno));
- return (NULL);
- }
- }
- }
-
-#ifdef __FreeBSD__
- if (S_ISCHR(statbuf.st_mode)) {
- statbuf.st_mode &= ~S_IFCHR;
- statbuf.st_mode |= S_IFBLK;
- wholedisk = B_FALSE;
- }
-#endif
-
- /*
- * Determine whether this is a device or a file.
- */
- if (wholedisk || S_ISBLK(statbuf.st_mode)) {
- type = VDEV_TYPE_DISK;
- } else if (S_ISREG(statbuf.st_mode)) {
- type = VDEV_TYPE_FILE;
- } else {
- (void) fprintf(stderr, gettext("cannot use '%s': must be a "
- "GEOM provider or regular file\n"), path);
- return (NULL);
- }
-
- /*
- * Finally, we have the complete device or file, and we know that it is
- * acceptable to use. Construct the nvlist to describe this vdev. All
- * vdevs have a 'path' element, and devices also have a 'devid' element.
- */
- verify(nvlist_alloc(&vdev, NV_UNIQUE_NAME, 0) == 0);
- verify(nvlist_add_string(vdev, ZPOOL_CONFIG_PATH, path) == 0);
- verify(nvlist_add_string(vdev, ZPOOL_CONFIG_TYPE, type) == 0);
- verify(nvlist_add_uint64(vdev, ZPOOL_CONFIG_IS_LOG, is_log) == 0);
- if (is_log)
- verify(nvlist_add_string(vdev, ZPOOL_CONFIG_ALLOCATION_BIAS,
- VDEV_ALLOC_BIAS_LOG) == 0);
- if (strcmp(type, VDEV_TYPE_DISK) == 0)
- verify(nvlist_add_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK,
- (uint64_t)wholedisk) == 0);
-
-#ifdef have_devid
- /*
- * For a whole disk, defer getting its devid until after labeling it.
- */
- if (S_ISBLK(statbuf.st_mode) && !wholedisk) {
- /*
- * Get the devid for the device.
- */
- int fd;
- ddi_devid_t devid;
- char *minor = NULL, *devid_str = NULL;
-
- if ((fd = open(path, O_RDONLY)) < 0) {
- (void) fprintf(stderr, gettext("cannot open '%s': "
- "%s\n"), path, strerror(errno));
- nvlist_free(vdev);
- return (NULL);
- }
-
- if (devid_get(fd, &devid) == 0) {
- if (devid_get_minor_name(fd, &minor) == 0 &&
- (devid_str = devid_str_encode(devid, minor)) !=
- NULL) {
- verify(nvlist_add_string(vdev,
- ZPOOL_CONFIG_DEVID, devid_str) == 0);
- }
- if (devid_str != NULL)
- devid_str_free(devid_str);
- if (minor != NULL)
- devid_str_free(minor);
- devid_free(devid);
- }
-
- (void) close(fd);
- }
-#endif
-
- return (vdev);
-}
-
-/*
- * Go through and verify the replication level of the pool is consistent.
- * Performs the following checks:
- *
- * For the new spec, verifies that devices in mirrors and raidz are the
- * same size.
- *
- * If the current configuration already has inconsistent replication
- * levels, ignore any other potential problems in the new spec.
- *
- * Otherwise, make sure that the current spec (if there is one) and the new
- * spec have consistent replication levels.
- *
- * If there is no current spec (create), make sure new spec has at least
- * one general purpose vdev.
- */
-typedef struct replication_level {
- char *zprl_type;
- uint64_t zprl_children;
- uint64_t zprl_parity;
-} replication_level_t;
-
-#define ZPOOL_FUZZ (16 * 1024 * 1024)
-
-static boolean_t
-is_raidz_mirror(replication_level_t *a, replication_level_t *b,
- replication_level_t **raidz, replication_level_t **mirror)
-{
- if (strcmp(a->zprl_type, "raidz") == 0 &&
- strcmp(b->zprl_type, "mirror") == 0) {
- *raidz = a;
- *mirror = b;
- return (B_TRUE);
- }
- return (B_FALSE);
-}
-
-/*
- * Given a list of toplevel vdevs, return the current replication level. If
- * the config is inconsistent, then NULL is returned. If 'fatal' is set, then
- * an error message will be displayed for each self-inconsistent vdev.
- */
-static replication_level_t *
-get_replication(nvlist_t *nvroot, boolean_t fatal)
-{
- nvlist_t **top;
- uint_t t, toplevels;
- nvlist_t **child;
- uint_t c, children;
- nvlist_t *nv;
- char *type;
- replication_level_t lastrep = {0};
- replication_level_t rep;
- replication_level_t *ret;
- replication_level_t *raidz, *mirror;
- boolean_t dontreport;
-
- ret = safe_malloc(sizeof (replication_level_t));
-
- verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
- &top, &toplevels) == 0);
-
- for (t = 0; t < toplevels; t++) {
- uint64_t is_log = B_FALSE;
-
- nv = top[t];
-
- /*
- * For separate logs we ignore the top level vdev replication
- * constraints.
- */
- (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_LOG, &is_log);
- if (is_log)
- continue;
-
- verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE,
- &type) == 0);
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0) {
- /*
- * This is a 'file' or 'disk' vdev.
- */
- rep.zprl_type = type;
- rep.zprl_children = 1;
- rep.zprl_parity = 0;
- } else {
- uint64_t vdev_size;
-
- /*
- * This is a mirror or RAID-Z vdev. Go through and make
- * sure the contents are all the same (files vs. disks),
- * keeping track of the number of elements in the
- * process.
- *
- * We also check that the size of each vdev (if it can
- * be determined) is the same.
- */
- rep.zprl_type = type;
- rep.zprl_children = 0;
-
- if (strcmp(type, VDEV_TYPE_RAIDZ) == 0) {
- verify(nvlist_lookup_uint64(nv,
- ZPOOL_CONFIG_NPARITY,
- &rep.zprl_parity) == 0);
- assert(rep.zprl_parity != 0);
- } else {
- rep.zprl_parity = 0;
- }
-
- /*
- * The 'dontreport' variable indicates that we've
- * already reported an error for this spec, so don't
- * bother doing it again.
- */
- type = NULL;
- dontreport = 0;
- vdev_size = -1ULL;
- for (c = 0; c < children; c++) {
- boolean_t is_replacing, is_spare;
- nvlist_t *cnv = child[c];
- char *path;
- struct stat64 statbuf;
- uint64_t size = -1ULL;
- char *childtype;
- int fd, err;
-
- rep.zprl_children++;
-
- verify(nvlist_lookup_string(cnv,
- ZPOOL_CONFIG_TYPE, &childtype) == 0);
-
- /*
- * If this is a replacing or spare vdev, then
- * get the real first child of the vdev.
- */
- is_replacing = strcmp(childtype,
- VDEV_TYPE_REPLACING) == 0;
- is_spare = strcmp(childtype,
- VDEV_TYPE_SPARE) == 0;
- if (is_replacing || is_spare) {
- nvlist_t **rchild;
- uint_t rchildren;
-
- verify(nvlist_lookup_nvlist_array(cnv,
- ZPOOL_CONFIG_CHILDREN, &rchild,
- &rchildren) == 0);
- assert((is_replacing && rchildren == 2)
- || (is_spare && rchildren >= 2));
- cnv = rchild[0];
-
- verify(nvlist_lookup_string(cnv,
- ZPOOL_CONFIG_TYPE,
- &childtype) == 0);
- if (strcmp(childtype,
- VDEV_TYPE_SPARE) == 0) {
- /* We have a replacing vdev with
- * a spare child. Get the first
- * real child of the spare
- */
- verify(
- nvlist_lookup_nvlist_array(
- cnv,
- ZPOOL_CONFIG_CHILDREN,
- &rchild,
- &rchildren) == 0);
- assert(rchildren >= 2);
- cnv = rchild[0];
- }
- }
-
- verify(nvlist_lookup_string(cnv,
- ZPOOL_CONFIG_PATH, &path) == 0);
-
- /*
- * If we have a raidz/mirror that combines disks
- * with files, report it as an error.
- */
- if (!dontreport && type != NULL &&
- strcmp(type, childtype) != 0) {
- if (ret != NULL)
- free(ret);
- ret = NULL;
- if (fatal)
- vdev_error(gettext(
- "mismatched replication "
- "level: %s contains both "
- "files and devices\n"),
- rep.zprl_type);
- else
- return (NULL);
- dontreport = B_TRUE;
- }
-
- /*
- * According to stat(2), the value of 'st_size'
- * is undefined for block devices and character
- * devices. But there is no effective way to
- * determine the real size in userland.
- *
- * Instead, we'll take advantage of an
- * implementation detail of spec_size(). If the
- * device is currently open, then we (should)
- * return a valid size.
- *
- * If we still don't get a valid size (indicated
- * by a size of 0 or MAXOFFSET_T), then ignore
- * this device altogether.
- */
- if ((fd = open(path, O_RDONLY)) >= 0) {
- err = fstat64(fd, &statbuf);
- (void) close(fd);
- } else {
- err = stat64(path, &statbuf);
- }
-
- if (err != 0 ||
- statbuf.st_size == 0 ||
- statbuf.st_size == MAXOFFSET_T)
- continue;
-
- size = statbuf.st_size;
-
- /*
- * Also make sure that devices and
- * slices have a consistent size. If
- * they differ by a significant amount
- * (~16MB) then report an error.
- */
- if (!dontreport &&
- (vdev_size != -1ULL &&
- (labs(size - vdev_size) >
- ZPOOL_FUZZ))) {
- if (ret != NULL)
- free(ret);
- ret = NULL;
- if (fatal)
- vdev_error(gettext(
- "%s contains devices of "
- "different sizes\n"),
- rep.zprl_type);
- else
- return (NULL);
- dontreport = B_TRUE;
- }
-
- type = childtype;
- vdev_size = size;
- }
- }
-
- /*
- * At this point, we have the replication of the last toplevel
- * vdev in 'rep'. Compare it to 'lastrep' to see if it is
- * different.
- */
- if (lastrep.zprl_type != NULL) {
- if (is_raidz_mirror(&lastrep, &rep, &raidz, &mirror) ||
- is_raidz_mirror(&rep, &lastrep, &raidz, &mirror)) {
- /*
- * Accepted raidz and mirror when they can
- * handle the same number of disk failures.
- */
- if (raidz->zprl_parity !=
- mirror->zprl_children - 1) {
- if (ret != NULL)
- free(ret);
- ret = NULL;
- if (fatal)
- vdev_error(gettext(
- "mismatched replication "
- "level: "
- "%s and %s vdevs with "
- "different redundancy, "
- "%llu vs. %llu (%llu-way) "
- "are present\n"),
- raidz->zprl_type,
- mirror->zprl_type,
- raidz->zprl_parity,
- mirror->zprl_children - 1,
- mirror->zprl_children);
- else
- return (NULL);
- }
- } else if (strcmp(lastrep.zprl_type, rep.zprl_type) !=
- 0) {
- if (ret != NULL)
- free(ret);
- ret = NULL;
- if (fatal)
- vdev_error(gettext(
- "mismatched replication level: "
- "both %s and %s vdevs are "
- "present\n"),
- lastrep.zprl_type, rep.zprl_type);
- else
- return (NULL);
- } else if (lastrep.zprl_parity != rep.zprl_parity) {
- if (ret)
- free(ret);
- ret = NULL;
- if (fatal)
- vdev_error(gettext(
- "mismatched replication level: "
- "both %llu and %llu device parity "
- "%s vdevs are present\n"),
- lastrep.zprl_parity,
- rep.zprl_parity,
- rep.zprl_type);
- else
- return (NULL);
- } else if (lastrep.zprl_children != rep.zprl_children) {
- if (ret)
- free(ret);
- ret = NULL;
- if (fatal)
- vdev_error(gettext(
- "mismatched replication level: "
- "both %llu-way and %llu-way %s "
- "vdevs are present\n"),
- lastrep.zprl_children,
- rep.zprl_children,
- rep.zprl_type);
- else
- return (NULL);
- }
- }
- lastrep = rep;
- }
-
- if (ret != NULL)
- *ret = rep;
-
- return (ret);
-}
-
-/*
- * Check the replication level of the vdev spec against the current pool. Calls
- * get_replication() to make sure the new spec is self-consistent. If the pool
- * has a consistent replication level, then we ignore any errors. Otherwise,
- * report any difference between the two.
- */
-static int
-check_replication(nvlist_t *config, nvlist_t *newroot)
-{
- nvlist_t **child;
- uint_t children;
- replication_level_t *current = NULL, *new;
- replication_level_t *raidz, *mirror;
- int ret;
-
- /*
- * If we have a current pool configuration, check to see if it's
- * self-consistent. If not, simply return success.
- */
- if (config != NULL) {
- nvlist_t *nvroot;
-
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
- if ((current = get_replication(nvroot, B_FALSE)) == NULL)
- return (0);
- }
- /*
- * for spares there may be no children, and therefore no
- * replication level to check
- */
- if ((nvlist_lookup_nvlist_array(newroot, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0) || (children == 0)) {
- free(current);
- return (0);
- }
-
- /*
- * If all we have is logs then there's no replication level to check.
- */
- if (num_logs(newroot) == children) {
- free(current);
- return (0);
- }
-
- /*
- * Get the replication level of the new vdev spec, reporting any
- * inconsistencies found.
- */
- if ((new = get_replication(newroot, B_TRUE)) == NULL) {
- free(current);
- return (-1);
- }
-
- /*
- * Check to see if the new vdev spec matches the replication level of
- * the current pool.
- */
- ret = 0;
- if (current != NULL) {
- if (is_raidz_mirror(current, new, &raidz, &mirror) ||
- is_raidz_mirror(new, current, &raidz, &mirror)) {
- if (raidz->zprl_parity != mirror->zprl_children - 1) {
- vdev_error(gettext(
- "mismatched replication level: pool and "
- "new vdev with different redundancy, %s "
- "and %s vdevs, %llu vs. %llu (%llu-way)\n"),
- raidz->zprl_type,
- mirror->zprl_type,
- raidz->zprl_parity,
- mirror->zprl_children - 1,
- mirror->zprl_children);
- ret = -1;
- }
- } else if (strcmp(current->zprl_type, new->zprl_type) != 0) {
- vdev_error(gettext(
- "mismatched replication level: pool uses %s "
- "and new vdev is %s\n"),
- current->zprl_type, new->zprl_type);
- ret = -1;
- } else if (current->zprl_parity != new->zprl_parity) {
- vdev_error(gettext(
- "mismatched replication level: pool uses %llu "
- "device parity and new vdev uses %llu\n"),
- current->zprl_parity, new->zprl_parity);
- ret = -1;
- } else if (current->zprl_children != new->zprl_children) {
- vdev_error(gettext(
- "mismatched replication level: pool uses %llu-way "
- "%s and new vdev uses %llu-way %s\n"),
- current->zprl_children, current->zprl_type,
- new->zprl_children, new->zprl_type);
- ret = -1;
- }
- }
-
- free(new);
- if (current != NULL)
- free(current);
-
- return (ret);
-}
-
-#ifdef illumos
-/*
- * Go through and find any whole disks in the vdev specification, labelling them
- * as appropriate. When constructing the vdev spec, we were unable to open this
- * device in order to provide a devid. Now that we have labelled the disk and
- * know the pool slice is valid, we can construct the devid now.
- *
- * If the disk was already labeled with an EFI label, we will have gotten the
- * devid already (because we were able to open the whole disk). Otherwise, we
- * need to get the devid after we label the disk.
- */
-static int
-make_disks(zpool_handle_t *zhp, nvlist_t *nv, zpool_boot_label_t boot_type,
- uint64_t boot_size)
-{
- nvlist_t **child;
- uint_t c, children;
- char *type, *path, *diskname;
- char buf[MAXPATHLEN];
- uint64_t wholedisk;
- int fd;
- int ret;
- int slice;
- ddi_devid_t devid;
- char *minor = NULL, *devid_str = NULL;
-
- verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0) {
-
- if (strcmp(type, VDEV_TYPE_DISK) != 0)
- return (0);
-
- /*
- * We have a disk device. Get the path to the device
- * and see if it's a whole disk by appending the backup
- * slice and stat()ing the device.
- */
- verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0);
-
- diskname = strrchr(path, '/');
- assert(diskname != NULL);
- diskname++;
-
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
- &wholedisk) != 0 || !wholedisk) {
- /*
- * This is not whole disk, return error if
- * boot partition creation was requested
- */
- if (boot_type == ZPOOL_CREATE_BOOT_LABEL) {
- (void) fprintf(stderr,
- gettext("creating boot partition is only "
- "supported on whole disk vdevs: %s\n"),
- diskname);
- return (-1);
- }
- return (0);
- }
-
- ret = zpool_label_disk(g_zfs, zhp, diskname, boot_type,
- boot_size, &slice);
- if (ret == -1)
- return (ret);
-
- /*
- * Fill in the devid, now that we've labeled the disk.
- */
- (void) snprintf(buf, sizeof (buf), "%ss%d", path, slice);
- if ((fd = open(buf, O_RDONLY)) < 0) {
- (void) fprintf(stderr,
- gettext("cannot open '%s': %s\n"),
- buf, strerror(errno));
- return (-1);
- }
-
- if (devid_get(fd, &devid) == 0) {
- if (devid_get_minor_name(fd, &minor) == 0 &&
- (devid_str = devid_str_encode(devid, minor)) !=
- NULL) {
- verify(nvlist_add_string(nv,
- ZPOOL_CONFIG_DEVID, devid_str) == 0);
- }
- if (devid_str != NULL)
- devid_str_free(devid_str);
- if (minor != NULL)
- devid_str_free(minor);
- devid_free(devid);
- }
-
- /*
- * Update the path to refer to the pool slice. The presence of
- * the 'whole_disk' field indicates to the CLI that we should
- * chop off the slice number when displaying the device in
- * future output.
- */
- verify(nvlist_add_string(nv, ZPOOL_CONFIG_PATH, buf) == 0);
-
- (void) close(fd);
-
- return (0);
- }
-
- /* illumos kernel does not support booting from multi-vdev pools. */
- if ((boot_type == ZPOOL_CREATE_BOOT_LABEL)) {
- if ((strcmp(type, VDEV_TYPE_ROOT) == 0) && children > 1) {
- (void) fprintf(stderr, gettext("boot pool "
- "can not have more than one vdev\n"));
- return (-1);
- }
- }
-
- for (c = 0; c < children; c++) {
- ret = make_disks(zhp, child[c], boot_type, boot_size);
- if (ret != 0)
- return (ret);
- }
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
- &child, &children) == 0)
- for (c = 0; c < children; c++) {
- ret = make_disks(zhp, child[c], boot_type, boot_size);
- if (ret != 0)
- return (ret);
- }
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
- &child, &children) == 0)
- for (c = 0; c < children; c++) {
- ret = make_disks(zhp, child[c], boot_type, boot_size);
- if (ret != 0)
- return (ret);
- }
-
- return (0);
-}
-#endif /* illumos */
-
-/*
- * Determine if the given path is a hot spare within the given configuration.
- */
-static boolean_t
-is_spare(nvlist_t *config, const char *path)
-{
- int fd;
- pool_state_t state;
- char *name = NULL;
- nvlist_t *label;
- uint64_t guid, spareguid;
- nvlist_t *nvroot;
- nvlist_t **spares;
- uint_t i, nspares;
- boolean_t inuse;
-
- if ((fd = open(path, O_RDONLY)) < 0)
- return (B_FALSE);
-
- if (zpool_in_use(g_zfs, fd, &state, &name, &inuse) != 0 ||
- !inuse ||
- state != POOL_STATE_SPARE ||
- zpool_read_label(fd, &label) != 0) {
- free(name);
- (void) close(fd);
- return (B_FALSE);
- }
- free(name);
- (void) close(fd);
-
- verify(nvlist_lookup_uint64(label, ZPOOL_CONFIG_GUID, &guid) == 0);
- nvlist_free(label);
-
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- &spares, &nspares) == 0) {
- for (i = 0; i < nspares; i++) {
- verify(nvlist_lookup_uint64(spares[i],
- ZPOOL_CONFIG_GUID, &spareguid) == 0);
- if (spareguid == guid)
- return (B_TRUE);
- }
- }
-
- return (B_FALSE);
-}
-
-/*
- * Go through and find any devices that are in use. We rely on libdiskmgt for
- * the majority of this task.
- */
-static boolean_t
-is_device_in_use(nvlist_t *config, nvlist_t *nv, boolean_t force,
- boolean_t replacing, boolean_t isspare)
-{
- nvlist_t **child;
- uint_t c, children;
- char *type, *path;
- int ret = 0;
- char buf[MAXPATHLEN];
- uint64_t wholedisk;
- boolean_t anyinuse = B_FALSE;
-
- verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0);
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0) {
-
- verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0);
-
- /*
- * As a generic check, we look to see if this is a replace of a
- * hot spare within the same pool. If so, we allow it
- * regardless of what libdiskmgt or zpool_in_use() says.
- */
- if (replacing) {
-#ifdef illumos
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
- &wholedisk) == 0 && wholedisk)
- (void) snprintf(buf, sizeof (buf), "%ss0",
- path);
- else
-#endif
- (void) strlcpy(buf, path, sizeof (buf));
-
- if (is_spare(config, buf))
- return (B_FALSE);
- }
-
- if (strcmp(type, VDEV_TYPE_DISK) == 0)
- ret = check_device(path, force, isspare);
- else if (strcmp(type, VDEV_TYPE_FILE) == 0)
- ret = check_file(path, force, isspare);
-
- return (ret != 0);
- }
-
- for (c = 0; c < children; c++)
- if (is_device_in_use(config, child[c], force, replacing,
- B_FALSE))
- anyinuse = B_TRUE;
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
- &child, &children) == 0)
- for (c = 0; c < children; c++)
- if (is_device_in_use(config, child[c], force, replacing,
- B_TRUE))
- anyinuse = B_TRUE;
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
- &child, &children) == 0)
- for (c = 0; c < children; c++)
- if (is_device_in_use(config, child[c], force, replacing,
- B_FALSE))
- anyinuse = B_TRUE;
-
- return (anyinuse);
-}
-
-static const char *
-is_grouping(const char *type, int *mindev, int *maxdev)
-{
- if (strncmp(type, "raidz", 5) == 0) {
- const char *p = type + 5;
- char *end;
- long nparity;
-
- if (*p == '\0') {
- nparity = 1;
- } else if (*p == '0') {
- return (NULL); /* no zero prefixes allowed */
- } else {
- errno = 0;
- nparity = strtol(p, &end, 10);
- if (errno != 0 || nparity < 1 || nparity >= 255 ||
- *end != '\0')
- return (NULL);
- }
-
- if (mindev != NULL)
- *mindev = nparity + 1;
- if (maxdev != NULL)
- *maxdev = 255;
- return (VDEV_TYPE_RAIDZ);
- }
-
- if (maxdev != NULL)
- *maxdev = INT_MAX;
-
- if (strcmp(type, "mirror") == 0) {
- if (mindev != NULL)
- *mindev = 2;
- return (VDEV_TYPE_MIRROR);
- }
-
- if (strcmp(type, "spare") == 0) {
- if (mindev != NULL)
- *mindev = 1;
- return (VDEV_TYPE_SPARE);
- }
-
- if (strcmp(type, "log") == 0) {
- if (mindev != NULL)
- *mindev = 1;
- return (VDEV_TYPE_LOG);
- }
-
- if (strcmp(type, VDEV_ALLOC_BIAS_SPECIAL) == 0 ||
- strcmp(type, VDEV_ALLOC_BIAS_DEDUP) == 0) {
- if (mindev != NULL)
- *mindev = 1;
- return (type);
- }
-
- if (strcmp(type, "cache") == 0) {
- if (mindev != NULL)
- *mindev = 1;
- return (VDEV_TYPE_L2CACHE);
- }
-
- return (NULL);
-}
-
-/*
- * Construct a syntactically valid vdev specification,
- * and ensure that all devices and files exist and can be opened.
- * Note: we don't bother freeing anything in the error paths
- * because the program is just going to exit anyway.
- */
-nvlist_t *
-construct_spec(int argc, char **argv)
-{
- nvlist_t *nvroot, *nv, **top, **spares, **l2cache;
- int t, toplevels, mindev, maxdev, nspares, nlogs, nl2cache;
- const char *type;
- uint64_t is_log, is_special, is_dedup;
- boolean_t seen_logs;
-
- top = NULL;
- toplevels = 0;
- spares = NULL;
- l2cache = NULL;
- nspares = 0;
- nlogs = 0;
- nl2cache = 0;
- is_log = is_special = is_dedup = B_FALSE;
- seen_logs = B_FALSE;
-
- while (argc > 0) {
- nv = NULL;
-
- /*
- * If it's a mirror or raidz, the subsequent arguments are
- * its leaves -- until we encounter the next mirror or raidz.
- */
- if ((type = is_grouping(argv[0], &mindev, &maxdev)) != NULL) {
- nvlist_t **child = NULL;
- int c, children = 0;
-
- if (strcmp(type, VDEV_TYPE_SPARE) == 0) {
- if (spares != NULL) {
- (void) fprintf(stderr,
- gettext("invalid vdev "
- "specification: 'spare' can be "
- "specified only once\n"));
- return (NULL);
- }
- is_log = is_special = is_dedup = B_FALSE;
- }
-
- if (strcmp(type, VDEV_TYPE_LOG) == 0) {
- if (seen_logs) {
- (void) fprintf(stderr,
- gettext("invalid vdev "
- "specification: 'log' can be "
- "specified only once\n"));
- return (NULL);
- }
- seen_logs = B_TRUE;
- is_log = B_TRUE;
- is_special = B_FALSE;
- is_dedup = B_FALSE;
- argc--;
- argv++;
- /*
- * A log is not a real grouping device.
- * We just set is_log and continue.
- */
- continue;
- }
-
- if (strcmp(type, VDEV_ALLOC_BIAS_SPECIAL) == 0) {
- is_special = B_TRUE;
- is_log = B_FALSE;
- is_dedup = B_FALSE;
- argc--;
- argv++;
- continue;
- }
-
- if (strcmp(type, VDEV_ALLOC_BIAS_DEDUP) == 0) {
- is_dedup = B_TRUE;
- is_log = B_FALSE;
- is_special = B_FALSE;
- argc--;
- argv++;
- continue;
- }
-
- if (strcmp(type, VDEV_TYPE_L2CACHE) == 0) {
- if (l2cache != NULL) {
- (void) fprintf(stderr,
- gettext("invalid vdev "
- "specification: 'cache' can be "
- "specified only once\n"));
- return (NULL);
- }
- is_log = is_special = is_dedup = B_FALSE;
- }
-
- if (is_log || is_special || is_dedup) {
- if (strcmp(type, VDEV_TYPE_MIRROR) != 0) {
- (void) fprintf(stderr,
- gettext("invalid vdev "
- "specification: unsupported '%s' "
- "device: %s\n"), is_log ? "log" :
- "special", type);
- return (NULL);
- }
- nlogs++;
- }
-
- for (c = 1; c < argc; c++) {
- if (is_grouping(argv[c], NULL, NULL) != NULL)
- break;
- children++;
- child = realloc(child,
- children * sizeof (nvlist_t *));
- if (child == NULL)
- zpool_no_memory();
- if ((nv = make_leaf_vdev(argv[c], B_FALSE))
- == NULL)
- return (NULL);
- child[children - 1] = nv;
- }
-
- if (children < mindev) {
- (void) fprintf(stderr, gettext("invalid vdev "
- "specification: %s requires at least %d "
- "devices\n"), argv[0], mindev);
- return (NULL);
- }
-
- if (children > maxdev) {
- (void) fprintf(stderr, gettext("invalid vdev "
- "specification: %s supports no more than "
- "%d devices\n"), argv[0], maxdev);
- return (NULL);
- }
-
- argc -= c;
- argv += c;
-
- if (strcmp(type, VDEV_TYPE_SPARE) == 0) {
- spares = child;
- nspares = children;
- continue;
- } else if (strcmp(type, VDEV_TYPE_L2CACHE) == 0) {
- l2cache = child;
- nl2cache = children;
- continue;
- } else {
- /* create a top-level vdev with children */
- verify(nvlist_alloc(&nv, NV_UNIQUE_NAME,
- 0) == 0);
- verify(nvlist_add_string(nv, ZPOOL_CONFIG_TYPE,
- type) == 0);
- verify(nvlist_add_uint64(nv,
- ZPOOL_CONFIG_IS_LOG, is_log) == 0);
- if (is_log)
- verify(nvlist_add_string(nv,
- ZPOOL_CONFIG_ALLOCATION_BIAS,
- VDEV_ALLOC_BIAS_LOG) == 0);
- if (is_special) {
- verify(nvlist_add_string(nv,
- ZPOOL_CONFIG_ALLOCATION_BIAS,
- VDEV_ALLOC_BIAS_SPECIAL) == 0);
- }
- if (is_dedup) {
- verify(nvlist_add_string(nv,
- ZPOOL_CONFIG_ALLOCATION_BIAS,
- VDEV_ALLOC_BIAS_DEDUP) == 0);
- }
- if (strcmp(type, VDEV_TYPE_RAIDZ) == 0) {
- verify(nvlist_add_uint64(nv,
- ZPOOL_CONFIG_NPARITY,
- mindev - 1) == 0);
- }
- verify(nvlist_add_nvlist_array(nv,
- ZPOOL_CONFIG_CHILDREN, child,
- children) == 0);
-
- for (c = 0; c < children; c++)
- nvlist_free(child[c]);
- free(child);
- }
- } else {
- /*
- * We have a device. Pass off to make_leaf_vdev() to
- * construct the appropriate nvlist describing the vdev.
- */
- if ((nv = make_leaf_vdev(argv[0], is_log)) == NULL)
- return (NULL);
- if (is_log)
- nlogs++;
- if (is_special) {
- verify(nvlist_add_string(nv,
- ZPOOL_CONFIG_ALLOCATION_BIAS,
- VDEV_ALLOC_BIAS_SPECIAL) == 0);
- }
- if (is_dedup) {
- verify(nvlist_add_string(nv,
- ZPOOL_CONFIG_ALLOCATION_BIAS,
- VDEV_ALLOC_BIAS_DEDUP) == 0);
- }
- argc--;
- argv++;
- }
-
- toplevels++;
- top = realloc(top, toplevels * sizeof (nvlist_t *));
- if (top == NULL)
- zpool_no_memory();
- top[toplevels - 1] = nv;
- }
-
- if (toplevels == 0 && nspares == 0 && nl2cache == 0) {
- (void) fprintf(stderr, gettext("invalid vdev "
- "specification: at least one toplevel vdev must be "
- "specified\n"));
- return (NULL);
- }
-
- if (seen_logs && nlogs == 0) {
- (void) fprintf(stderr, gettext("invalid vdev specification: "
- "log requires at least 1 device\n"));
- return (NULL);
- }
-
- /*
- * Finally, create nvroot and add all top-level vdevs to it.
- */
- verify(nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) == 0);
- verify(nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE,
- VDEV_TYPE_ROOT) == 0);
- verify(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
- top, toplevels) == 0);
- if (nspares != 0)
- verify(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- spares, nspares) == 0);
- if (nl2cache != 0)
- verify(nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
- l2cache, nl2cache) == 0);
-
- for (t = 0; t < toplevels; t++)
- nvlist_free(top[t]);
- for (t = 0; t < nspares; t++)
- nvlist_free(spares[t]);
- for (t = 0; t < nl2cache; t++)
- nvlist_free(l2cache[t]);
- if (spares)
- free(spares);
- if (l2cache)
- free(l2cache);
- free(top);
-
- return (nvroot);
-}
-
-nvlist_t *
-split_mirror_vdev(zpool_handle_t *zhp, char *newname, nvlist_t *props,
- splitflags_t flags, int argc, char **argv)
-{
- nvlist_t *newroot = NULL, **child;
- uint_t c, children;
-#ifdef illumos
- zpool_boot_label_t boot_type;
-#endif
-
- if (argc > 0) {
- if ((newroot = construct_spec(argc, argv)) == NULL) {
- (void) fprintf(stderr, gettext("Unable to build a "
- "pool from the specified devices\n"));
- return (NULL);
- }
-
-#ifdef illumos
- if (zpool_is_bootable(zhp))
- boot_type = ZPOOL_COPY_BOOT_LABEL;
- else
- boot_type = ZPOOL_NO_BOOT_LABEL;
-
- if (!flags.dryrun &&
- make_disks(zhp, newroot, boot_type, 0) != 0) {
- nvlist_free(newroot);
- return (NULL);
- }
-#endif
-
- /* avoid any tricks in the spec */
- verify(nvlist_lookup_nvlist_array(newroot,
- ZPOOL_CONFIG_CHILDREN, &child, &children) == 0);
- for (c = 0; c < children; c++) {
- char *path;
- const char *type;
- int min, max;
-
- verify(nvlist_lookup_string(child[c],
- ZPOOL_CONFIG_PATH, &path) == 0);
- if ((type = is_grouping(path, &min, &max)) != NULL) {
- (void) fprintf(stderr, gettext("Cannot use "
- "'%s' as a device for splitting\n"), type);
- nvlist_free(newroot);
- return (NULL);
- }
- }
- }
-
- if (zpool_vdev_split(zhp, newname, &newroot, props, flags) != 0) {
- nvlist_free(newroot);
- return (NULL);
- }
-
- return (newroot);
-}
-
-static int
-num_normal_vdevs(nvlist_t *nvroot)
-{
- nvlist_t **top;
- uint_t t, toplevels, normal = 0;
-
- verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
- &top, &toplevels) == 0);
-
- for (t = 0; t < toplevels; t++) {
- uint64_t log = B_FALSE;
-
- (void) nvlist_lookup_uint64(top[t], ZPOOL_CONFIG_IS_LOG, &log);
- if (log)
- continue;
- if (nvlist_exists(top[t], ZPOOL_CONFIG_ALLOCATION_BIAS))
- continue;
-
- normal++;
- }
-
- return (normal);
-}
-
-/*
- * Get and validate the contents of the given vdev specification. This ensures
- * that the nvlist returned is well-formed, that all the devices exist, and that
- * they are not currently in use by any other known consumer. The 'poolconfig'
- * parameter is the current configuration of the pool when adding devices
- * existing pool, and is used to perform additional checks, such as changing the
- * replication level of the pool. It can be 'NULL' to indicate that this is a
- * new pool. The 'force' flag controls whether devices should be forcefully
- * added, even if they appear in use.
- */
-nvlist_t *
-make_root_vdev(zpool_handle_t *zhp, int force, int check_rep,
- boolean_t replacing, boolean_t dryrun, zpool_boot_label_t boot_type,
- uint64_t boot_size, int argc, char **argv)
-{
- nvlist_t *newroot;
- nvlist_t *poolconfig = NULL;
- is_force = force;
-
- /*
- * Construct the vdev specification. If this is successful, we know
- * that we have a valid specification, and that all devices can be
- * opened.
- */
- if ((newroot = construct_spec(argc, argv)) == NULL)
- return (NULL);
-
- if (zhp && ((poolconfig = zpool_get_config(zhp, NULL)) == NULL))
- return (NULL);
-
- /*
- * Validate each device to make sure that its not shared with another
- * subsystem. We do this even if 'force' is set, because there are some
- * uses (such as a dedicated dump device) that even '-f' cannot
- * override.
- */
- if (is_device_in_use(poolconfig, newroot, force, replacing, B_FALSE)) {
- nvlist_free(newroot);
- return (NULL);
- }
-
- /*
- * Check the replication level of the given vdevs and report any errors
- * found. We include the existing pool spec, if any, as we need to
- * catch changes against the existing replication level.
- */
- if (check_rep && check_replication(poolconfig, newroot) != 0) {
- nvlist_free(newroot);
- return (NULL);
- }
-
-#ifdef illumos
- /*
- * On pool create the new vdev spec must have one normal vdev.
- */
- if (poolconfig == NULL && num_normal_vdevs(newroot) == 0) {
- vdev_error(gettext("at least one general top-level vdev must "
- "be specified\n"));
- nvlist_free(newroot);
- return (NULL);
- }
-
- /*
- * Run through the vdev specification and label any whole disks found.
- */
- if (!dryrun && make_disks(zhp, newroot, boot_type, boot_size) != 0) {
- nvlist_free(newroot);
- return (NULL);
- }
-#endif
-
- return (newroot);
-}
diff --git a/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.1 b/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.1
deleted file mode 100644
index 3e30502833133..0000000000000
--- a/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.1
+++ /dev/null
@@ -1,76 +0,0 @@
-'\" te
-.\" Copyright (c) 2011, 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) 2009, Sun Microsystems, Inc. All Rights Reserved.
-.\" Copyright (c) 2013, Delphix. All Rights Reserved.
-.\"
-.\" $FreeBSD$
-.\"
-.Dd February 25, 2020
-.Dt ZSTREAMDUMP 8
-.Os
-.Sh NAME
-.Nm zstreamdump
-.Nd filter data in zfs send stream
-.Sh SYNOPSIS
-.Nm
-.Op Fl C
-.Op Fl d
-.Op Fl v
-.Sh DESCRIPTION
-The
-.Nm
-utility reads from the output of the
-.Qq Nm zfs Cm send
-command, then displays headers and some statistics from that output. See
-.Xr zfs 8 .
-.Pp
-The following options are supported:
-.Bl -tag -width indent
-.It Fl C
-Suppress the validation of checksums.
-.It Fl d
-Dump contents of blocks modified, implies verbose.
-.It Fl v
-Verbose. Dump all headers, not only begin and end headers.
-.El
-.Sh SEE ALSO
-.Xr zfs 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 zstreamdump(1M) ,
-modified and customized for
-.Fx
-and licensed under the
-.Tn 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/zstreamdump/zstreamdump.c b/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.c
deleted file mode 100644
index 51c4c8e0e6494..0000000000000
--- a/cddl/contrib/opensolaris/cmd/zstreamdump/zstreamdump.c
+++ /dev/null
@@ -1,644 +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 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/*
- * Copyright (c) 2014 Integros [integros.com]
- * Copyright (c) 2013, 2015 by Delphix. All rights reserved.
- */
-
-#include <ctype.h>
-#include <libnvpair.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <unistd.h>
-#include <stddef.h>
-
-#include <sys/dmu.h>
-#include <sys/zfs_ioctl.h>
-#include <sys/zio.h>
-#include <zfs_fletcher.h>
-
-/*
- * If dump mode is enabled, the number of bytes to print per line
- */
-#define BYTES_PER_LINE 16
-/*
- * If dump mode is enabled, the number of bytes to group together, separated
- * by newlines or spaces
- */
-#define DUMP_GROUPING 4
-
-uint64_t total_write_size = 0;
-uint64_t total_stream_len = 0;
-FILE *send_stream = 0;
-boolean_t do_byteswap = B_FALSE;
-boolean_t do_cksum = B_TRUE;
-
-static void
-usage(void)
-{
- (void) fprintf(stderr, "usage: zstreamdump [-v] [-C] [-d] < file\n");
- (void) fprintf(stderr, "\t -v -- verbose\n");
- (void) fprintf(stderr, "\t -C -- suppress checksum verification\n");
- (void) fprintf(stderr, "\t -d -- dump contents of blocks modified, "
- "implies verbose\n");
- exit(1);
-}
-
-static void *
-safe_malloc(size_t size)
-{
- void *rv = malloc(size);
- if (rv == NULL) {
- (void) fprintf(stderr, "ERROR; failed to allocate %zu bytes\n",
- size);
- abort();
- }
- return (rv);
-}
-
-/*
- * ssread - send stream read.
- *
- * Read while computing incremental checksum
- */
-static size_t
-ssread(void *buf, size_t len, zio_cksum_t *cksum)
-{
- size_t outlen;
-
- if ((outlen = fread(buf, len, 1, send_stream)) == 0)
- return (0);
-
- if (do_cksum) {
- if (do_byteswap)
- fletcher_4_incremental_byteswap(buf, len, cksum);
- else
- fletcher_4_incremental_native(buf, len, cksum);
- }
- total_stream_len += len;
- return (outlen);
-}
-
-static size_t
-read_hdr(dmu_replay_record_t *drr, zio_cksum_t *cksum)
-{
- ASSERT3U(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum),
- ==, sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t));
- size_t r = ssread(drr, sizeof (*drr) - sizeof (zio_cksum_t), cksum);
- if (r == 0)
- return (0);
- zio_cksum_t saved_cksum = *cksum;
- r = ssread(&drr->drr_u.drr_checksum.drr_checksum,
- sizeof (zio_cksum_t), cksum);
- if (r == 0)
- return (0);
- if (!ZIO_CHECKSUM_IS_ZERO(&drr->drr_u.drr_checksum.drr_checksum) &&
- !ZIO_CHECKSUM_EQUAL(saved_cksum,
- drr->drr_u.drr_checksum.drr_checksum)) {
- fprintf(stderr, "invalid checksum\n");
- (void) printf("Incorrect checksum in record header.\n");
- (void) printf("Expected checksum = %llx/%llx/%llx/%llx\n",
- saved_cksum.zc_word[0],
- saved_cksum.zc_word[1],
- saved_cksum.zc_word[2],
- saved_cksum.zc_word[3]);
- return (0);
- }
- return (sizeof (*drr));
-}
-
-/*
- * Print part of a block in ASCII characters
- */
-static void
-print_ascii_block(char *subbuf, int length)
-{
- int i;
-
- for (i = 0; i < length; i++) {
- char char_print = isprint(subbuf[i]) ? subbuf[i] : '.';
- if (i != 0 && i % DUMP_GROUPING == 0) {
- (void) printf(" ");
- }
- (void) printf("%c", char_print);
- }
- (void) printf("\n");
-}
-
-/*
- * print_block - Dump the contents of a modified block to STDOUT
- *
- * Assume that buf has capacity evenly divisible by BYTES_PER_LINE
- */
-static void
-print_block(char *buf, int length)
-{
- int i;
- /*
- * Start printing ASCII characters at a constant offset, after
- * the hex prints. Leave 3 characters per byte on a line (2 digit
- * hex number plus 1 space) plus spaces between characters and
- * groupings.
- */
- int ascii_start = BYTES_PER_LINE * 3 +
- BYTES_PER_LINE / DUMP_GROUPING + 2;
-
- for (i = 0; i < length; i += BYTES_PER_LINE) {
- int j;
- int this_line_length = MIN(BYTES_PER_LINE, length - i);
- int print_offset = 0;
-
- for (j = 0; j < this_line_length; j++) {
- int buf_offset = i + j;
-
- /*
- * Separate every DUMP_GROUPING bytes by a space.
- */
- if (buf_offset % DUMP_GROUPING == 0) {
- print_offset += printf(" ");
- }
-
- /*
- * Print the two-digit hex value for this byte.
- */
- unsigned char hex_print = buf[buf_offset];
- print_offset += printf("%02x ", hex_print);
- }
-
- (void) printf("%*s", ascii_start - print_offset, " ");
-
- print_ascii_block(buf + i, this_line_length);
- }
-}
-
-int
-main(int argc, char *argv[])
-{
- char *buf = safe_malloc(SPA_MAXBLOCKSIZE);
- uint64_t drr_record_count[DRR_NUMTYPES] = { 0 };
- uint64_t total_records = 0;
- dmu_replay_record_t thedrr;
- dmu_replay_record_t *drr = &thedrr;
- struct drr_begin *drrb = &thedrr.drr_u.drr_begin;
- struct drr_end *drre = &thedrr.drr_u.drr_end;
- struct drr_object *drro = &thedrr.drr_u.drr_object;
- struct drr_freeobjects *drrfo = &thedrr.drr_u.drr_freeobjects;
- struct drr_write *drrw = &thedrr.drr_u.drr_write;
- struct drr_write_byref *drrwbr = &thedrr.drr_u.drr_write_byref;
- struct drr_free *drrf = &thedrr.drr_u.drr_free;
- struct drr_spill *drrs = &thedrr.drr_u.drr_spill;
- struct drr_write_embedded *drrwe = &thedrr.drr_u.drr_write_embedded;
- struct drr_checksum *drrc = &thedrr.drr_u.drr_checksum;
- char c;
- boolean_t verbose = B_FALSE;
- boolean_t very_verbose = B_FALSE;
- boolean_t first = B_TRUE;
- /*
- * dump flag controls whether the contents of any modified data blocks
- * are printed to the console during processing of the stream. Warning:
- * for large streams, this can obviously lead to massive prints.
- */
- boolean_t dump = B_FALSE;
- int err;
- zio_cksum_t zc = { 0 };
- zio_cksum_t pcksum = { 0 };
-
- while ((c = getopt(argc, argv, ":vCd")) != -1) {
- switch (c) {
- case 'C':
- do_cksum = B_FALSE;
- break;
- case 'v':
- if (verbose)
- very_verbose = B_TRUE;
- verbose = B_TRUE;
- break;
- case 'd':
- dump = B_TRUE;
- verbose = B_TRUE;
- very_verbose = B_TRUE;
- break;
- case ':':
- (void) fprintf(stderr,
- "missing argument for '%c' option\n", optopt);
- usage();
- break;
- case '?':
- (void) fprintf(stderr, "invalid option '%c'\n",
- optopt);
- usage();
- break;
- }
- }
-
- if (isatty(STDIN_FILENO)) {
- (void) fprintf(stderr,
- "Error: Backup stream can not be read "
- "from a terminal.\n"
- "You must redirect standard input.\n");
- exit(1);
- }
-
- send_stream = stdin;
- pcksum = zc;
- while (read_hdr(drr, &zc)) {
-
- /*
- * If this is the first DMU record being processed, check for
- * the magic bytes and figure out the endian-ness based on them.
- */
- if (first) {
- if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
- do_byteswap = B_TRUE;
- if (do_cksum) {
- ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0);
- /*
- * recalculate header checksum now
- * that we know it needs to be
- * byteswapped.
- */
- fletcher_4_incremental_byteswap(drr,
- sizeof (dmu_replay_record_t), &zc);
- }
- } else if (drrb->drr_magic != DMU_BACKUP_MAGIC) {
- (void) fprintf(stderr, "Invalid stream "
- "(bad magic number)\n");
- exit(1);
- }
- first = B_FALSE;
- }
- if (do_byteswap) {
- drr->drr_type = BSWAP_32(drr->drr_type);
- drr->drr_payloadlen =
- BSWAP_32(drr->drr_payloadlen);
- }
-
- /*
- * At this point, the leading fields of the replay record
- * (drr_type and drr_payloadlen) have been byte-swapped if
- * necessary, but the rest of the data structure (the
- * union of type-specific structures) is still in its
- * original state.
- */
- if (drr->drr_type >= DRR_NUMTYPES) {
- (void) printf("INVALID record found: type 0x%x\n",
- drr->drr_type);
- (void) printf("Aborting.\n");
- exit(1);
- }
-
- drr_record_count[drr->drr_type]++;
- total_records++;
-
- switch (drr->drr_type) {
- case DRR_BEGIN:
- if (do_byteswap) {
- drrb->drr_magic = BSWAP_64(drrb->drr_magic);
- drrb->drr_versioninfo =
- BSWAP_64(drrb->drr_versioninfo);
- drrb->drr_creation_time =
- BSWAP_64(drrb->drr_creation_time);
- drrb->drr_type = BSWAP_32(drrb->drr_type);
- drrb->drr_flags = BSWAP_32(drrb->drr_flags);
- drrb->drr_toguid = BSWAP_64(drrb->drr_toguid);
- drrb->drr_fromguid =
- BSWAP_64(drrb->drr_fromguid);
- }
-
- (void) printf("BEGIN record\n");
- (void) printf("\thdrtype = %lld\n",
- DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo));
- (void) printf("\tfeatures = %llx\n",
- DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo));
- (void) printf("\tmagic = %llx\n",
- (u_longlong_t)drrb->drr_magic);
- (void) printf("\tcreation_time = %llx\n",
- (u_longlong_t)drrb->drr_creation_time);
- (void) printf("\ttype = %u\n", drrb->drr_type);
- (void) printf("\tflags = 0x%x\n", drrb->drr_flags);
- (void) printf("\ttoguid = %llx\n",
- (u_longlong_t)drrb->drr_toguid);
- (void) printf("\tfromguid = %llx\n",
- (u_longlong_t)drrb->drr_fromguid);
- (void) printf("\ttoname = %s\n", drrb->drr_toname);
- if (verbose)
- (void) printf("\n");
-
- if (drr->drr_payloadlen != 0) {
- nvlist_t *nv;
- int sz = drr->drr_payloadlen;
-
- if (sz > SPA_MAXBLOCKSIZE) {
- free(buf);
- buf = safe_malloc(sz);
- }
- (void) ssread(buf, sz, &zc);
- if (ferror(send_stream))
- perror("fread");
- err = nvlist_unpack(buf, sz, &nv, 0);
- if (err)
- perror(strerror(err));
- nvlist_print(stdout, nv);
- nvlist_free(nv);
- }
- break;
-
- case DRR_END:
- if (do_byteswap) {
- drre->drr_checksum.zc_word[0] =
- BSWAP_64(drre->drr_checksum.zc_word[0]);
- drre->drr_checksum.zc_word[1] =
- BSWAP_64(drre->drr_checksum.zc_word[1]);
- drre->drr_checksum.zc_word[2] =
- BSWAP_64(drre->drr_checksum.zc_word[2]);
- drre->drr_checksum.zc_word[3] =
- BSWAP_64(drre->drr_checksum.zc_word[3]);
- }
- /*
- * We compare against the *previous* checksum
- * value, because the stored checksum is of
- * everything before the DRR_END record.
- */
- if (do_cksum && !ZIO_CHECKSUM_EQUAL(drre->drr_checksum,
- pcksum)) {
- (void) printf("Expected checksum differs from "
- "checksum in stream.\n");
- (void) printf("Expected checksum = "
- "%llx/%llx/%llx/%llx\n",
- pcksum.zc_word[0],
- pcksum.zc_word[1],
- pcksum.zc_word[2],
- pcksum.zc_word[3]);
- }
- (void) printf("END checksum = %llx/%llx/%llx/%llx\n",
- drre->drr_checksum.zc_word[0],
- drre->drr_checksum.zc_word[1],
- drre->drr_checksum.zc_word[2],
- drre->drr_checksum.zc_word[3]);
-
- ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0);
- break;
-
- case DRR_OBJECT:
- if (do_byteswap) {
- drro->drr_object = BSWAP_64(drro->drr_object);
- drro->drr_type = BSWAP_32(drro->drr_type);
- drro->drr_bonustype =
- BSWAP_32(drro->drr_bonustype);
- drro->drr_blksz = BSWAP_32(drro->drr_blksz);
- drro->drr_bonuslen =
- BSWAP_32(drro->drr_bonuslen);
- drro->drr_toguid = BSWAP_64(drro->drr_toguid);
- }
- if (verbose) {
- (void) printf("OBJECT object = %" PRIu64
- " type = %u bonustype = %u blksz = %u"
- " bonuslen = %u dn_slots = %u\n",
- drro->drr_object,
- drro->drr_type,
- drro->drr_bonustype,
- drro->drr_blksz,
- drro->drr_bonuslen,
- drro->drr_dn_slots);
- }
- if (drro->drr_bonuslen > 0) {
- (void) ssread(buf,
- P2ROUNDUP(drro->drr_bonuslen, 8), &zc);
- if (dump) {
- print_block(buf,
- P2ROUNDUP(drro->drr_bonuslen, 8));
- }
- }
- break;
-
- case DRR_FREEOBJECTS:
- if (do_byteswap) {
- drrfo->drr_firstobj =
- BSWAP_64(drrfo->drr_firstobj);
- drrfo->drr_numobjs =
- BSWAP_64(drrfo->drr_numobjs);
- drrfo->drr_toguid = BSWAP_64(drrfo->drr_toguid);
- }
- if (verbose) {
- (void) printf("FREEOBJECTS firstobj = %llu "
- "numobjs = %llu\n",
- (u_longlong_t)drrfo->drr_firstobj,
- (u_longlong_t)drrfo->drr_numobjs);
- }
- break;
-
- case DRR_WRITE:
- if (do_byteswap) {
- drrw->drr_object = BSWAP_64(drrw->drr_object);
- drrw->drr_type = BSWAP_32(drrw->drr_type);
- drrw->drr_offset = BSWAP_64(drrw->drr_offset);
- drrw->drr_logical_size =
- BSWAP_64(drrw->drr_logical_size);
- drrw->drr_toguid = BSWAP_64(drrw->drr_toguid);
- drrw->drr_key.ddk_prop =
- BSWAP_64(drrw->drr_key.ddk_prop);
- drrw->drr_compressed_size =
- BSWAP_64(drrw->drr_compressed_size);
- }
-
- uint64_t payload_size = DRR_WRITE_PAYLOAD_SIZE(drrw);
-
- /*
- * If this is verbose and/or dump output,
- * print info on the modified block
- */
- if (verbose) {
- (void) printf("WRITE object = %llu type = %u "
- "checksum type = %u compression type = %u\n"
- " offset = %llu logical_size = %llu "
- "compressed_size = %llu "
- "payload_size = %llu "
- "props = %llx\n",
- (u_longlong_t)drrw->drr_object,
- drrw->drr_type,
- drrw->drr_checksumtype,
- drrw->drr_compressiontype,
- (u_longlong_t)drrw->drr_offset,
- (u_longlong_t)drrw->drr_logical_size,
- (u_longlong_t)drrw->drr_compressed_size,
- (u_longlong_t)payload_size,
- (u_longlong_t)drrw->drr_key.ddk_prop);
- }
-
- /*
- * Read the contents of the block in from STDIN to buf
- */
- (void) ssread(buf, payload_size, &zc);
- /*
- * If in dump mode
- */
- if (dump) {
- print_block(buf, payload_size);
- }
- total_write_size += payload_size;
- break;
-
- case DRR_WRITE_BYREF:
- if (do_byteswap) {
- drrwbr->drr_object =
- BSWAP_64(drrwbr->drr_object);
- drrwbr->drr_offset =
- BSWAP_64(drrwbr->drr_offset);
- drrwbr->drr_length =
- BSWAP_64(drrwbr->drr_length);
- drrwbr->drr_toguid =
- BSWAP_64(drrwbr->drr_toguid);
- drrwbr->drr_refguid =
- BSWAP_64(drrwbr->drr_refguid);
- drrwbr->drr_refobject =
- BSWAP_64(drrwbr->drr_refobject);
- drrwbr->drr_refoffset =
- BSWAP_64(drrwbr->drr_refoffset);
- drrwbr->drr_key.ddk_prop =
- BSWAP_64(drrwbr->drr_key.ddk_prop);
- }
- if (verbose) {
- (void) printf("WRITE_BYREF object = %llu "
- "checksum type = %u props = %llx\n"
- " offset = %llu length = %llu\n"
- "toguid = %llx refguid = %llx\n"
- " refobject = %llu refoffset = %llu\n",
- (u_longlong_t)drrwbr->drr_object,
- drrwbr->drr_checksumtype,
- (u_longlong_t)drrwbr->drr_key.ddk_prop,
- (u_longlong_t)drrwbr->drr_offset,
- (u_longlong_t)drrwbr->drr_length,
- (u_longlong_t)drrwbr->drr_toguid,
- (u_longlong_t)drrwbr->drr_refguid,
- (u_longlong_t)drrwbr->drr_refobject,
- (u_longlong_t)drrwbr->drr_refoffset);
- }
- break;
-
- case DRR_FREE:
- if (do_byteswap) {
- drrf->drr_object = BSWAP_64(drrf->drr_object);
- drrf->drr_offset = BSWAP_64(drrf->drr_offset);
- drrf->drr_length = BSWAP_64(drrf->drr_length);
- }
- if (verbose) {
- (void) printf("FREE object = %llu "
- "offset = %llu length = %lld\n",
- (u_longlong_t)drrf->drr_object,
- (u_longlong_t)drrf->drr_offset,
- (longlong_t)drrf->drr_length);
- }
- break;
- case DRR_SPILL:
- if (do_byteswap) {
- drrs->drr_object = BSWAP_64(drrs->drr_object);
- drrs->drr_length = BSWAP_64(drrs->drr_length);
- }
- if (verbose) {
- (void) printf("SPILL block for object = %llu "
- "length = %llu\n", drrs->drr_object,
- drrs->drr_length);
- }
- (void) ssread(buf, drrs->drr_length, &zc);
- if (dump) {
- print_block(buf, drrs->drr_length);
- }
- break;
- case DRR_WRITE_EMBEDDED:
- if (do_byteswap) {
- drrwe->drr_object =
- BSWAP_64(drrwe->drr_object);
- drrwe->drr_offset =
- BSWAP_64(drrwe->drr_offset);
- drrwe->drr_length =
- BSWAP_64(drrwe->drr_length);
- drrwe->drr_toguid =
- BSWAP_64(drrwe->drr_toguid);
- drrwe->drr_lsize =
- BSWAP_32(drrwe->drr_lsize);
- drrwe->drr_psize =
- BSWAP_32(drrwe->drr_psize);
- }
- if (verbose) {
- (void) printf("WRITE_EMBEDDED object = %llu "
- "offset = %llu length = %llu\n"
- " toguid = %llx comp = %u etype = %u "
- "lsize = %u psize = %u\n",
- (u_longlong_t)drrwe->drr_object,
- (u_longlong_t)drrwe->drr_offset,
- (u_longlong_t)drrwe->drr_length,
- (u_longlong_t)drrwe->drr_toguid,
- drrwe->drr_compression,
- drrwe->drr_etype,
- drrwe->drr_lsize,
- drrwe->drr_psize);
- }
- (void) ssread(buf,
- P2ROUNDUP(drrwe->drr_psize, 8), &zc);
- break;
- }
- if (drr->drr_type != DRR_BEGIN && very_verbose) {
- (void) printf(" checksum = %llx/%llx/%llx/%llx\n",
- (longlong_t)drrc->drr_checksum.zc_word[0],
- (longlong_t)drrc->drr_checksum.zc_word[1],
- (longlong_t)drrc->drr_checksum.zc_word[2],
- (longlong_t)drrc->drr_checksum.zc_word[3]);
- }
- pcksum = zc;
- }
- free(buf);
-
- /* Print final summary */
-
- (void) printf("SUMMARY:\n");
- (void) printf("\tTotal DRR_BEGIN records = %lld\n",
- (u_longlong_t)drr_record_count[DRR_BEGIN]);
- (void) printf("\tTotal DRR_END records = %lld\n",
- (u_longlong_t)drr_record_count[DRR_END]);
- (void) printf("\tTotal DRR_OBJECT records = %lld\n",
- (u_longlong_t)drr_record_count[DRR_OBJECT]);
- (void) printf("\tTotal DRR_FREEOBJECTS records = %lld\n",
- (u_longlong_t)drr_record_count[DRR_FREEOBJECTS]);
- (void) printf("\tTotal DRR_WRITE records = %lld\n",
- (u_longlong_t)drr_record_count[DRR_WRITE]);
- (void) printf("\tTotal DRR_WRITE_BYREF records = %lld\n",
- (u_longlong_t)drr_record_count[DRR_WRITE_BYREF]);
- (void) printf("\tTotal DRR_WRITE_EMBEDDED records = %lld\n",
- (u_longlong_t)drr_record_count[DRR_WRITE_EMBEDDED]);
- (void) printf("\tTotal DRR_FREE records = %lld\n",
- (u_longlong_t)drr_record_count[DRR_FREE]);
- (void) printf("\tTotal DRR_SPILL records = %lld\n",
- (u_longlong_t)drr_record_count[DRR_SPILL]);
- (void) printf("\tTotal records = %lld\n",
- (u_longlong_t)total_records);
- (void) printf("\tTotal write size = %lld (0x%llx)\n",
- (u_longlong_t)total_write_size, (u_longlong_t)total_write_size);
- (void) printf("\tTotal stream length = %lld (0x%llx)\n",
- (u_longlong_t)total_stream_len, (u_longlong_t)total_stream_len);
- return (0);
-}
diff --git a/cddl/contrib/opensolaris/cmd/ztest/ztest.c b/cddl/contrib/opensolaris/cmd/ztest/ztest.c
deleted file mode 100644
index 65a4858b95d97..0000000000000
--- a/cddl/contrib/opensolaris/cmd/ztest/ztest.c
+++ /dev/null
@@ -1,7135 +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 2011 Nexenta Systems, Inc. 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 2017 Joyent, Inc.
- * Copyright (c) 2017, Intel Corporation.
- * Copyright 2017 RackTop Systems.
- */
-
-/*
- * The objective of this program is to provide a DMU/ZAP/SPA stress test
- * that runs entirely in userland, is easy to use, and easy to extend.
- *
- * The overall design of the ztest program is as follows:
- *
- * (1) For each major functional area (e.g. adding vdevs to a pool,
- * creating and destroying datasets, reading and writing objects, etc)
- * we have a simple routine to test that functionality. These
- * individual routines do not have to do anything "stressful".
- *
- * (2) We turn these simple functionality tests into a stress test by
- * running them all in parallel, with as many threads as desired,
- * and spread across as many datasets, objects, and vdevs as desired.
- *
- * (3) While all this is happening, we inject faults into the pool to
- * verify that self-healing data really works.
- *
- * (4) Every time we open a dataset, we change its checksum and compression
- * functions. Thus even individual objects vary from block to block
- * in which checksum they use and whether they're compressed.
- *
- * (5) To verify that we never lose on-disk consistency after a crash,
- * we run the entire test in a child of the main process.
- * At random times, the child self-immolates with a SIGKILL.
- * This is the software equivalent of pulling the power cord.
- * The parent then runs the test again, using the existing
- * storage pool, as many times as desired. If backwards compatibility
- * testing is enabled ztest will sometimes run the "older" version
- * of ztest after a SIGKILL.
- *
- * (6) To verify that we don't have future leaks or temporal incursions,
- * many of the functional tests record the transaction group number
- * as part of their data. When reading old data, they verify that
- * the transaction group number is less than the current, open txg.
- * If you add a new test, please do this if applicable.
- *
- * When run with no arguments, ztest runs for about five minutes and
- * produces no output if successful. To get a little bit of information,
- * specify -V. To get more information, specify -VV, and so on.
- *
- * To turn this into an overnight stress test, use -T to specify run time.
- *
- * You can ask more more vdevs [-v], datasets [-d], or threads [-t]
- * to increase the pool capacity, fanout, and overall stress level.
- *
- * Use the -k option to set the desired frequency of kills.
- *
- * When ztest invokes itself it passes all relevant information through a
- * temporary file which is mmap-ed in the child process. This allows shared
- * memory to survive the exec syscall. The ztest_shared_hdr_t struct is always
- * stored at offset 0 of this file and contains information on the size and
- * number of shared structures in the file. The information stored in this file
- * must remain backwards compatible with older versions of ztest so that
- * ztest can invoke them during backwards compatibility testing (-B).
- */
-
-#include <sys/zfs_context.h>
-#include <sys/spa.h>
-#include <sys/dmu.h>
-#include <sys/txg.h>
-#include <sys/dbuf.h>
-#include <sys/zap.h>
-#include <sys/dmu_objset.h>
-#include <sys/poll.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/wait.h>
-#include <sys/mman.h>
-#include <sys/resource.h>
-#include <sys/zio.h>
-#include <sys/zil.h>
-#include <sys/zil_impl.h>
-#include <sys/vdev_impl.h>
-#include <sys/vdev_file.h>
-#include <sys/vdev_initialize.h>
-#include <sys/spa_impl.h>
-#include <sys/metaslab_impl.h>
-#include <sys/dsl_prop.h>
-#include <sys/dsl_dataset.h>
-#include <sys/dsl_destroy.h>
-#include <sys/dsl_scan.h>
-#include <sys/zio_checksum.h>
-#include <sys/refcount.h>
-#include <sys/zfeature.h>
-#include <sys/dsl_userhold.h>
-#include <sys/abd.h>
-#include <stdio.h>
-#include <stdio_ext.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <signal.h>
-#include <umem.h>
-#include <dlfcn.h>
-#include <ctype.h>
-#include <math.h>
-#include <errno.h>
-#include <sys/fs/zfs.h>
-#include <libnvpair.h>
-#include <libzfs.h>
-#include <libcmdutils.h>
-
-static int ztest_fd_data = -1;
-static int ztest_fd_rand = -1;
-
-typedef struct ztest_shared_hdr {
- uint64_t zh_hdr_size;
- uint64_t zh_opts_size;
- uint64_t zh_size;
- uint64_t zh_stats_size;
- uint64_t zh_stats_count;
- uint64_t zh_ds_size;
- uint64_t zh_ds_count;
-} ztest_shared_hdr_t;
-
-static ztest_shared_hdr_t *ztest_shared_hdr;
-
-enum ztest_class_state {
- ZTEST_VDEV_CLASS_OFF,
- ZTEST_VDEV_CLASS_ON,
- ZTEST_VDEV_CLASS_RND
-};
-
-typedef struct ztest_shared_opts {
- char zo_pool[ZFS_MAX_DATASET_NAME_LEN];
- char zo_dir[ZFS_MAX_DATASET_NAME_LEN];
- char zo_alt_ztest[MAXNAMELEN];
- char zo_alt_libpath[MAXNAMELEN];
- uint64_t zo_vdevs;
- uint64_t zo_vdevtime;
- size_t zo_vdev_size;
- int zo_ashift;
- int zo_mirrors;
- int zo_raidz;
- int zo_raidz_parity;
- int zo_datasets;
- int zo_threads;
- uint64_t zo_passtime;
- uint64_t zo_killrate;
- int zo_verbose;
- int zo_init;
- uint64_t zo_time;
- uint64_t zo_maxloops;
- uint64_t zo_metaslab_force_ganging;
- int zo_mmp_test;
- int zo_special_vdevs;
-} ztest_shared_opts_t;
-
-static const ztest_shared_opts_t ztest_opts_defaults = {
- .zo_pool = { 'z', 't', 'e', 's', 't', '\0' },
- .zo_dir = { '/', 't', 'm', 'p', '\0' },
- .zo_alt_ztest = { '\0' },
- .zo_alt_libpath = { '\0' },
- .zo_vdevs = 5,
- .zo_ashift = SPA_MINBLOCKSHIFT,
- .zo_mirrors = 2,
- .zo_raidz = 4,
- .zo_raidz_parity = 1,
- .zo_vdev_size = SPA_MINDEVSIZE * 4, /* 256m default size */
- .zo_datasets = 7,
- .zo_threads = 23,
- .zo_passtime = 60, /* 60 seconds */
- .zo_killrate = 70, /* 70% kill rate */
- .zo_verbose = 0,
- .zo_mmp_test = 0,
- .zo_init = 1,
- .zo_time = 300, /* 5 minutes */
- .zo_maxloops = 50, /* max loops during spa_freeze() */
- .zo_metaslab_force_ganging = 32 << 10,
- .zo_special_vdevs = ZTEST_VDEV_CLASS_RND,
-};
-
-extern uint64_t metaslab_force_ganging;
-extern uint64_t metaslab_df_alloc_threshold;
-extern uint64_t zfs_deadman_synctime_ms;
-extern int metaslab_preload_limit;
-extern boolean_t zfs_compressed_arc_enabled;
-extern boolean_t zfs_abd_scatter_enabled;
-extern int dmu_object_alloc_chunk_shift;
-extern boolean_t zfs_force_some_double_word_sm_entries;
-extern unsigned long zfs_reconstruct_indirect_damage_fraction;
-
-static ztest_shared_opts_t *ztest_shared_opts;
-static ztest_shared_opts_t ztest_opts;
-
-typedef struct ztest_shared_ds {
- uint64_t zd_seq;
-} ztest_shared_ds_t;
-
-static ztest_shared_ds_t *ztest_shared_ds;
-#define ZTEST_GET_SHARED_DS(d) (&ztest_shared_ds[d])
-
-#define BT_MAGIC 0x123456789abcdefULL
-#define MAXFAULTS() \
- (MAX(zs->zs_mirrors, 1) * (ztest_opts.zo_raidz_parity + 1) - 1)
-
-enum ztest_io_type {
- ZTEST_IO_WRITE_TAG,
- ZTEST_IO_WRITE_PATTERN,
- ZTEST_IO_WRITE_ZEROES,
- ZTEST_IO_TRUNCATE,
- ZTEST_IO_SETATTR,
- ZTEST_IO_REWRITE,
- ZTEST_IO_TYPES
-};
-
-typedef struct ztest_block_tag {
- uint64_t bt_magic;
- uint64_t bt_objset;
- uint64_t bt_object;
- uint64_t bt_dnodesize;
- uint64_t bt_offset;
- uint64_t bt_gen;
- uint64_t bt_txg;
- uint64_t bt_crtxg;
-} ztest_block_tag_t;
-
-typedef struct bufwad {
- uint64_t bw_index;
- uint64_t bw_txg;
- uint64_t bw_data;
-} bufwad_t;
-
-/*
- * It would be better to use a rangelock_t per object. Unfortunately
- * the rangelock_t is not a drop-in replacement for rl_t, because we
- * still need to map from object ID to rangelock_t.
- */
-typedef enum {
- RL_READER,
- RL_WRITER,
- RL_APPEND
-} rl_type_t;
-
-typedef struct rll {
- void *rll_writer;
- int rll_readers;
- kmutex_t rll_lock;
- kcondvar_t rll_cv;
-} rll_t;
-
-typedef struct rl {
- uint64_t rl_object;
- uint64_t rl_offset;
- uint64_t rl_size;
- rll_t *rl_lock;
-} rl_t;
-
-#define ZTEST_RANGE_LOCKS 64
-#define ZTEST_OBJECT_LOCKS 64
-
-/*
- * Object descriptor. Used as a template for object lookup/create/remove.
- */
-typedef struct ztest_od {
- uint64_t od_dir;
- uint64_t od_object;
- dmu_object_type_t od_type;
- dmu_object_type_t od_crtype;
- uint64_t od_blocksize;
- uint64_t od_crblocksize;
- uint64_t od_crdnodesize;
- uint64_t od_gen;
- uint64_t od_crgen;
- char od_name[ZFS_MAX_DATASET_NAME_LEN];
-} ztest_od_t;
-
-/*
- * Per-dataset state.
- */
-typedef struct ztest_ds {
- ztest_shared_ds_t *zd_shared;
- objset_t *zd_os;
- krwlock_t zd_zilog_lock;
- zilog_t *zd_zilog;
- ztest_od_t *zd_od; /* debugging aid */
- char zd_name[ZFS_MAX_DATASET_NAME_LEN];
- kmutex_t zd_dirobj_lock;
- rll_t zd_object_lock[ZTEST_OBJECT_LOCKS];
- rll_t zd_range_lock[ZTEST_RANGE_LOCKS];
-} ztest_ds_t;
-
-/*
- * Per-iteration state.
- */
-typedef void ztest_func_t(ztest_ds_t *zd, uint64_t id);
-
-typedef struct ztest_info {
- ztest_func_t *zi_func; /* test function */
- uint64_t zi_iters; /* iterations per execution */
- uint64_t *zi_interval; /* execute every <interval> seconds */
-} ztest_info_t;
-
-typedef struct ztest_shared_callstate {
- uint64_t zc_count; /* per-pass count */
- uint64_t zc_time; /* per-pass time */
- uint64_t zc_next; /* next time to call this function */
-} ztest_shared_callstate_t;
-
-static ztest_shared_callstate_t *ztest_shared_callstate;
-#define ZTEST_GET_SHARED_CALLSTATE(c) (&ztest_shared_callstate[c])
-
-/*
- * Note: these aren't static because we want dladdr() to work.
- */
-ztest_func_t ztest_dmu_read_write;
-ztest_func_t ztest_dmu_write_parallel;
-ztest_func_t ztest_dmu_object_alloc_free;
-ztest_func_t ztest_dmu_object_next_chunk;
-ztest_func_t ztest_dmu_commit_callbacks;
-ztest_func_t ztest_zap;
-ztest_func_t ztest_zap_parallel;
-ztest_func_t ztest_zil_commit;
-ztest_func_t ztest_zil_remount;
-ztest_func_t ztest_dmu_read_write_zcopy;
-ztest_func_t ztest_dmu_objset_create_destroy;
-ztest_func_t ztest_dmu_prealloc;
-ztest_func_t ztest_fzap;
-ztest_func_t ztest_dmu_snapshot_create_destroy;
-ztest_func_t ztest_dsl_prop_get_set;
-ztest_func_t ztest_spa_prop_get_set;
-ztest_func_t ztest_spa_create_destroy;
-ztest_func_t ztest_fault_inject;
-ztest_func_t ztest_ddt_repair;
-ztest_func_t ztest_dmu_snapshot_hold;
-ztest_func_t ztest_mmp_enable_disable;
-ztest_func_t ztest_scrub;
-ztest_func_t ztest_dsl_dataset_promote_busy;
-ztest_func_t ztest_vdev_attach_detach;
-ztest_func_t ztest_vdev_LUN_growth;
-ztest_func_t ztest_vdev_add_remove;
-ztest_func_t ztest_vdev_class_add;
-ztest_func_t ztest_vdev_aux_add_remove;
-ztest_func_t ztest_split_pool;
-ztest_func_t ztest_reguid;
-ztest_func_t ztest_spa_upgrade;
-ztest_func_t ztest_device_removal;
-ztest_func_t ztest_remap_blocks;
-ztest_func_t ztest_spa_checkpoint_create_discard;
-ztest_func_t ztest_initialize;
-ztest_func_t ztest_verify_dnode_bt;
-
-uint64_t zopt_always = 0ULL * NANOSEC; /* all the time */
-uint64_t zopt_incessant = 1ULL * NANOSEC / 10; /* every 1/10 second */
-uint64_t zopt_often = 1ULL * NANOSEC; /* every second */
-uint64_t zopt_sometimes = 10ULL * NANOSEC; /* every 10 seconds */
-uint64_t zopt_rarely = 60ULL * NANOSEC; /* every 60 seconds */
-
-ztest_info_t ztest_info[] = {
- { ztest_dmu_read_write, 1, &zopt_always },
- { ztest_dmu_write_parallel, 10, &zopt_always },
- { ztest_dmu_object_alloc_free, 1, &zopt_always },
- { ztest_dmu_object_next_chunk, 1, &zopt_sometimes },
- { ztest_dmu_commit_callbacks, 1, &zopt_always },
- { ztest_zap, 30, &zopt_always },
- { ztest_zap_parallel, 100, &zopt_always },
- { ztest_split_pool, 1, &zopt_always },
- { ztest_zil_commit, 1, &zopt_incessant },
- { ztest_zil_remount, 1, &zopt_sometimes },
- { ztest_dmu_read_write_zcopy, 1, &zopt_often },
- { ztest_dmu_objset_create_destroy, 1, &zopt_often },
- { ztest_dsl_prop_get_set, 1, &zopt_often },
- { ztest_spa_prop_get_set, 1, &zopt_sometimes },
-#if 0
- { ztest_dmu_prealloc, 1, &zopt_sometimes },
-#endif
- { ztest_fzap, 1, &zopt_sometimes },
- { ztest_dmu_snapshot_create_destroy, 1, &zopt_sometimes },
- { ztest_spa_create_destroy, 1, &zopt_sometimes },
- { ztest_fault_inject, 1, &zopt_incessant },
- { ztest_ddt_repair, 1, &zopt_sometimes },
- { ztest_dmu_snapshot_hold, 1, &zopt_sometimes },
- { ztest_mmp_enable_disable, 1, &zopt_sometimes },
- { ztest_reguid, 1, &zopt_rarely },
- { ztest_scrub, 1, &zopt_often },
- { ztest_spa_upgrade, 1, &zopt_rarely },
- { ztest_dsl_dataset_promote_busy, 1, &zopt_rarely },
- { ztest_vdev_attach_detach, 1, &zopt_incessant },
- { ztest_vdev_LUN_growth, 1, &zopt_rarely },
- { ztest_vdev_add_remove, 1,
- &ztest_opts.zo_vdevtime },
- { ztest_vdev_class_add, 1,
- &ztest_opts.zo_vdevtime },
- { ztest_vdev_aux_add_remove, 1,
- &ztest_opts.zo_vdevtime },
- { ztest_device_removal, 1, &zopt_sometimes },
- { ztest_remap_blocks, 1, &zopt_sometimes },
- { ztest_spa_checkpoint_create_discard, 1, &zopt_rarely },
- { ztest_initialize, 1, &zopt_sometimes },
- { ztest_verify_dnode_bt, 1, &zopt_sometimes }
-};
-
-#define ZTEST_FUNCS (sizeof (ztest_info) / sizeof (ztest_info_t))
-
-/*
- * The following struct is used to hold a list of uncalled commit callbacks.
- * The callbacks are ordered by txg number.
- */
-typedef struct ztest_cb_list {
- kmutex_t zcl_callbacks_lock;
- list_t zcl_callbacks;
-} ztest_cb_list_t;
-
-/*
- * Stuff we need to share writably between parent and child.
- */
-typedef struct ztest_shared {
- boolean_t zs_do_init;
- hrtime_t zs_proc_start;
- hrtime_t zs_proc_stop;
- hrtime_t zs_thread_start;
- hrtime_t zs_thread_stop;
- hrtime_t zs_thread_kill;
- uint64_t zs_enospc_count;
- uint64_t zs_vdev_next_leaf;
- uint64_t zs_vdev_aux;
- uint64_t zs_alloc;
- uint64_t zs_space;
- uint64_t zs_splits;
- uint64_t zs_mirrors;
- uint64_t zs_metaslab_sz;
- uint64_t zs_metaslab_df_alloc_threshold;
- uint64_t zs_guid;
-} ztest_shared_t;
-
-#define ID_PARALLEL -1ULL
-
-static char ztest_dev_template[] = "%s/%s.%llua";
-static char ztest_aux_template[] = "%s/%s.%s.%llu";
-ztest_shared_t *ztest_shared;
-
-static spa_t *ztest_spa = NULL;
-static ztest_ds_t *ztest_ds;
-
-static kmutex_t ztest_vdev_lock;
-static boolean_t ztest_device_removal_active = B_FALSE;
-static kmutex_t ztest_checkpoint_lock;
-
-/*
- * The ztest_name_lock protects the pool and dataset namespace used by
- * the individual tests. To modify the namespace, consumers must grab
- * this lock as writer. Grabbing the lock as reader will ensure that the
- * namespace does not change while the lock is held.
- */
-static krwlock_t ztest_name_lock;
-
-static boolean_t ztest_dump_core = B_TRUE;
-static boolean_t ztest_exiting;
-
-/* Global commit callback list */
-static ztest_cb_list_t zcl;
-
-enum ztest_object {
- ZTEST_META_DNODE = 0,
- ZTEST_DIROBJ,
- ZTEST_OBJECTS
-};
-
-static void usage(boolean_t) __NORETURN;
-
-/*
- * 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 */
-}
-
-#define FATAL_MSG_SZ 1024
-
-char *fatal_msg;
-
-static void
-fatal(int do_perror, char *message, ...)
-{
- va_list args;
- int save_errno = errno;
- char buf[FATAL_MSG_SZ];
-
- (void) fflush(stdout);
-
- va_start(args, message);
- (void) sprintf(buf, "ztest: ");
- /* LINTED */
- (void) vsprintf(buf + strlen(buf), message, args);
- va_end(args);
- if (do_perror) {
- (void) snprintf(buf + strlen(buf), FATAL_MSG_SZ - strlen(buf),
- ": %s", strerror(save_errno));
- }
- (void) fprintf(stderr, "%s\n", buf);
- fatal_msg = buf; /* to ease debugging */
- if (ztest_dump_core)
- abort();
- exit(3);
-}
-
-static int
-str2shift(const char *buf)
-{
- const char *ends = "BKMGTPEZ";
- int i;
-
- if (buf[0] == '\0')
- return (0);
- for (i = 0; i < strlen(ends); i++) {
- if (toupper(buf[0]) == ends[i])
- break;
- }
- if (i == strlen(ends)) {
- (void) fprintf(stderr, "ztest: invalid bytes suffix: %s\n",
- buf);
- usage(B_FALSE);
- }
- if (buf[1] == '\0' || (toupper(buf[1]) == 'B' && buf[2] == '\0')) {
- return (10*i);
- }
- (void) fprintf(stderr, "ztest: invalid bytes suffix: %s\n", buf);
- usage(B_FALSE);
- /* NOTREACHED */
-}
-
-static uint64_t
-nicenumtoull(const char *buf)
-{
- char *end;
- uint64_t val;
-
- val = strtoull(buf, &end, 0);
- if (end == buf) {
- (void) fprintf(stderr, "ztest: bad numeric value: %s\n", buf);
- usage(B_FALSE);
- } else if (end[0] == '.') {
- double fval = strtod(buf, &end);
- fval *= pow(2, str2shift(end));
- if (fval > UINT64_MAX) {
- (void) fprintf(stderr, "ztest: value too large: %s\n",
- buf);
- usage(B_FALSE);
- }
- val = (uint64_t)fval;
- } else {
- int shift = str2shift(end);
- if (shift >= 64 || (val << shift) >> shift != val) {
- (void) fprintf(stderr, "ztest: value too large: %s\n",
- buf);
- usage(B_FALSE);
- }
- val <<= shift;
- }
- return (val);
-}
-
-static void
-usage(boolean_t requested)
-{
- const ztest_shared_opts_t *zo = &ztest_opts_defaults;
-
- char nice_vdev_size[NN_NUMBUF_SZ];
- char nice_force_ganging[NN_NUMBUF_SZ];
- FILE *fp = requested ? stdout : stderr;
-
- nicenum(zo->zo_vdev_size, nice_vdev_size, sizeof (nice_vdev_size));
- nicenum(zo->zo_metaslab_force_ganging, nice_force_ganging,
- sizeof (nice_force_ganging));
-
- (void) fprintf(fp, "Usage: %s\n"
- "\t[-v vdevs (default: %llu)]\n"
- "\t[-s size_of_each_vdev (default: %s)]\n"
- "\t[-a alignment_shift (default: %d)] use 0 for random\n"
- "\t[-m mirror_copies (default: %d)]\n"
- "\t[-r raidz_disks (default: %d)]\n"
- "\t[-R raidz_parity (default: %d)]\n"
- "\t[-d datasets (default: %d)]\n"
- "\t[-t threads (default: %d)]\n"
- "\t[-g gang_block_threshold (default: %s)]\n"
- "\t[-i init_count (default: %d)] initialize pool i times\n"
- "\t[-k kill_percentage (default: %llu%%)]\n"
- "\t[-p pool_name (default: %s)]\n"
- "\t[-f dir (default: %s)] file directory for vdev files\n"
- "\t[-M] Multi-host simulate pool imported on remote host\n"
- "\t[-V] verbose (use multiple times for ever more blather)\n"
- "\t[-E] use existing pool instead of creating new one\n"
- "\t[-T time (default: %llu sec)] total run time\n"
- "\t[-F freezeloops (default: %llu)] max loops in spa_freeze()\n"
- "\t[-P passtime (default: %llu sec)] time per pass\n"
- "\t[-B alt_ztest (default: <none>)] alternate ztest path\n"
- "\t[-C vdev class state (default: random)] special=on|off|random\n"
- "\t[-o variable=value] ... set global variable to an unsigned\n"
- "\t 32-bit integer value\n"
- "\t[-h] (print help)\n"
- "",
- zo->zo_pool,
- (u_longlong_t)zo->zo_vdevs, /* -v */
- nice_vdev_size, /* -s */
- zo->zo_ashift, /* -a */
- zo->zo_mirrors, /* -m */
- zo->zo_raidz, /* -r */
- zo->zo_raidz_parity, /* -R */
- zo->zo_datasets, /* -d */
- zo->zo_threads, /* -t */
- nice_force_ganging, /* -g */
- zo->zo_init, /* -i */
- (u_longlong_t)zo->zo_killrate, /* -k */
- zo->zo_pool, /* -p */
- zo->zo_dir, /* -f */
- (u_longlong_t)zo->zo_time, /* -T */
- (u_longlong_t)zo->zo_maxloops, /* -F */
- (u_longlong_t)zo->zo_passtime);
- exit(requested ? 0 : 1);
-}
-
-
-static void
-ztest_parse_name_value(const char *input, ztest_shared_opts_t *zo)
-{
- char name[32];
- char *value;
- int state = ZTEST_VDEV_CLASS_RND;
-
- (void) strlcpy(name, input, sizeof (name));
-
- value = strchr(name, '=');
- if (value == NULL) {
- (void) fprintf(stderr, "missing value in property=value "
- "'-C' argument (%s)\n", input);
- usage(B_FALSE);
- }
- *(value) = '\0';
- value++;
-
- if (strcmp(value, "on") == 0) {
- state = ZTEST_VDEV_CLASS_ON;
- } else if (strcmp(value, "off") == 0) {
- state = ZTEST_VDEV_CLASS_OFF;
- } else if (strcmp(value, "random") == 0) {
- state = ZTEST_VDEV_CLASS_RND;
- } else {
- (void) fprintf(stderr, "invalid property value '%s'\n", value);
- usage(B_FALSE);
- }
-
- if (strcmp(name, "special") == 0) {
- zo->zo_special_vdevs = state;
- } else {
- (void) fprintf(stderr, "invalid property name '%s'\n", name);
- usage(B_FALSE);
- }
- if (zo->zo_verbose >= 3)
- (void) printf("%s vdev state is '%s'\n", name, value);
-}
-
-static void
-process_options(int argc, char **argv)
-{
- char *path;
- ztest_shared_opts_t *zo = &ztest_opts;
-
- int opt;
- uint64_t value;
- char altdir[MAXNAMELEN] = { 0 };
-
- bcopy(&ztest_opts_defaults, zo, sizeof (*zo));
-
- while ((opt = getopt(argc, argv,
- "v:s:a:m:r:R:d:t:g:i:k:p:f:MVET:P:hF:B:C:o:")) != EOF) {
- value = 0;
- switch (opt) {
- case 'v':
- case 's':
- case 'a':
- case 'm':
- case 'r':
- case 'R':
- case 'd':
- case 't':
- case 'g':
- case 'i':
- case 'k':
- case 'T':
- case 'P':
- case 'F':
- value = nicenumtoull(optarg);
- }
- switch (opt) {
- case 'v':
- zo->zo_vdevs = value;
- break;
- case 's':
- zo->zo_vdev_size = MAX(SPA_MINDEVSIZE, value);
- break;
- case 'a':
- zo->zo_ashift = value;
- break;
- case 'm':
- zo->zo_mirrors = value;
- break;
- case 'r':
- zo->zo_raidz = MAX(1, value);
- break;
- case 'R':
- zo->zo_raidz_parity = MIN(MAX(value, 1), 3);
- break;
- case 'd':
- zo->zo_datasets = MAX(1, value);
- break;
- case 't':
- zo->zo_threads = MAX(1, value);
- break;
- case 'g':
- zo->zo_metaslab_force_ganging =
- MAX(SPA_MINBLOCKSIZE << 1, value);
- break;
- case 'i':
- zo->zo_init = value;
- break;
- case 'k':
- zo->zo_killrate = value;
- break;
- case 'p':
- (void) strlcpy(zo->zo_pool, optarg,
- sizeof (zo->zo_pool));
- break;
- case 'f':
- path = realpath(optarg, NULL);
- if (path == NULL) {
- (void) fprintf(stderr, "error: %s: %s\n",
- optarg, strerror(errno));
- usage(B_FALSE);
- } else {
- (void) strlcpy(zo->zo_dir, path,
- sizeof (zo->zo_dir));
- }
- break;
- case 'M':
- zo->zo_mmp_test = 1;
- break;
- case 'V':
- zo->zo_verbose++;
- break;
- case 'E':
- zo->zo_init = 0;
- break;
- case 'T':
- zo->zo_time = value;
- break;
- case 'P':
- zo->zo_passtime = MAX(1, value);
- break;
- case 'F':
- zo->zo_maxloops = MAX(1, value);
- break;
- case 'B':
- (void) strlcpy(altdir, optarg, sizeof (altdir));
- break;
- case 'C':
- ztest_parse_name_value(optarg, zo);
- break;
- case 'o':
- if (set_global_var(optarg) != 0)
- usage(B_FALSE);
- break;
- case 'h':
- usage(B_TRUE);
- break;
- case '?':
- default:
- usage(B_FALSE);
- break;
- }
- }
-
- zo->zo_raidz_parity = MIN(zo->zo_raidz_parity, zo->zo_raidz - 1);
-
- zo->zo_vdevtime =
- (zo->zo_vdevs > 0 ? zo->zo_time * NANOSEC / zo->zo_vdevs :
- UINT64_MAX >> 2);
-
- if (strlen(altdir) > 0) {
- char *cmd;
- char *realaltdir;
- char *bin;
- char *ztest;
- char *isa;
- int isalen;
-
- cmd = umem_alloc(MAXPATHLEN, UMEM_NOFAIL);
- realaltdir = umem_alloc(MAXPATHLEN, UMEM_NOFAIL);
-
- VERIFY(NULL != realpath(getexecname(), cmd));
- if (0 != access(altdir, F_OK)) {
- ztest_dump_core = B_FALSE;
- fatal(B_TRUE, "invalid alternate ztest path: %s",
- altdir);
- }
- VERIFY(NULL != realpath(altdir, realaltdir));
-
- /*
- * 'cmd' should be of the form "<anything>/usr/bin/<isa>/ztest".
- * We want to extract <isa> to determine if we should use
- * 32 or 64 bit binaries.
- */
- bin = strstr(cmd, "/usr/bin/");
- ztest = strstr(bin, "/ztest");
- isa = bin + 9;
- isalen = ztest - isa;
- (void) snprintf(zo->zo_alt_ztest, sizeof (zo->zo_alt_ztest),
- "%s/usr/bin/%.*s/ztest", realaltdir, isalen, isa);
- (void) snprintf(zo->zo_alt_libpath, sizeof (zo->zo_alt_libpath),
- "%s/usr/lib/%.*s", realaltdir, isalen, isa);
-
- if (0 != access(zo->zo_alt_ztest, X_OK)) {
- ztest_dump_core = B_FALSE;
- fatal(B_TRUE, "invalid alternate ztest: %s",
- zo->zo_alt_ztest);
- } else if (0 != access(zo->zo_alt_libpath, X_OK)) {
- ztest_dump_core = B_FALSE;
- fatal(B_TRUE, "invalid alternate lib directory %s",
- zo->zo_alt_libpath);
- }
-
- umem_free(cmd, MAXPATHLEN);
- umem_free(realaltdir, MAXPATHLEN);
- }
-}
-
-static void
-ztest_kill(ztest_shared_t *zs)
-{
- zs->zs_alloc = metaslab_class_get_alloc(spa_normal_class(ztest_spa));
- zs->zs_space = metaslab_class_get_space(spa_normal_class(ztest_spa));
-
- /*
- * Before we kill off ztest, make sure that the config is updated.
- * See comment above spa_write_cachefile().
- */
- mutex_enter(&spa_namespace_lock);
- spa_write_cachefile(ztest_spa, B_FALSE, B_FALSE);
- mutex_exit(&spa_namespace_lock);
-
- zfs_dbgmsg_print(FTAG);
- (void) kill(getpid(), SIGKILL);
-}
-
-static uint64_t
-ztest_random(uint64_t range)
-{
- uint64_t r;
-
- ASSERT3S(ztest_fd_rand, >=, 0);
-
- if (range == 0)
- return (0);
-
- if (read(ztest_fd_rand, &r, sizeof (r)) != sizeof (r))
- fatal(1, "short read from /dev/urandom");
-
- return (r % range);
-}
-
-/* ARGSUSED */
-static void
-ztest_record_enospc(const char *s)
-{
- ztest_shared->zs_enospc_count++;
-}
-
-static uint64_t
-ztest_get_ashift(void)
-{
- if (ztest_opts.zo_ashift == 0)
- return (SPA_MINBLOCKSHIFT + ztest_random(5));
- return (ztest_opts.zo_ashift);
-}
-
-static nvlist_t *
-make_vdev_file(char *path, char *aux, char *pool, size_t size, uint64_t ashift)
-{
- char pathbuf[MAXPATHLEN];
- uint64_t vdev;
- nvlist_t *file;
-
- if (ashift == 0)
- ashift = ztest_get_ashift();
-
- if (path == NULL) {
- path = pathbuf;
-
- if (aux != NULL) {
- vdev = ztest_shared->zs_vdev_aux;
- (void) snprintf(path, sizeof (pathbuf),
- ztest_aux_template, ztest_opts.zo_dir,
- pool == NULL ? ztest_opts.zo_pool : pool,
- aux, vdev);
- } else {
- vdev = ztest_shared->zs_vdev_next_leaf++;
- (void) snprintf(path, sizeof (pathbuf),
- ztest_dev_template, ztest_opts.zo_dir,
- pool == NULL ? ztest_opts.zo_pool : pool, vdev);
- }
- }
-
- if (size != 0) {
- int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666);
- if (fd == -1)
- fatal(1, "can't open %s", path);
- if (ftruncate(fd, size) != 0)
- fatal(1, "can't ftruncate %s", path);
- (void) close(fd);
- }
-
- VERIFY(nvlist_alloc(&file, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_TYPE, VDEV_TYPE_FILE) == 0);
- VERIFY(nvlist_add_string(file, ZPOOL_CONFIG_PATH, path) == 0);
- VERIFY(nvlist_add_uint64(file, ZPOOL_CONFIG_ASHIFT, ashift) == 0);
-
- return (file);
-}
-
-static nvlist_t *
-make_vdev_raidz(char *path, char *aux, char *pool, size_t size,
- uint64_t ashift, int r)
-{
- nvlist_t *raidz, **child;
- int c;
-
- if (r < 2)
- return (make_vdev_file(path, aux, pool, size, ashift));
- child = umem_alloc(r * sizeof (nvlist_t *), UMEM_NOFAIL);
-
- for (c = 0; c < r; c++)
- child[c] = make_vdev_file(path, aux, pool, size, ashift);
-
- VERIFY(nvlist_alloc(&raidz, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_string(raidz, ZPOOL_CONFIG_TYPE,
- VDEV_TYPE_RAIDZ) == 0);
- VERIFY(nvlist_add_uint64(raidz, ZPOOL_CONFIG_NPARITY,
- ztest_opts.zo_raidz_parity) == 0);
- VERIFY(nvlist_add_nvlist_array(raidz, ZPOOL_CONFIG_CHILDREN,
- child, r) == 0);
-
- for (c = 0; c < r; c++)
- nvlist_free(child[c]);
-
- umem_free(child, r * sizeof (nvlist_t *));
-
- return (raidz);
-}
-
-static nvlist_t *
-make_vdev_mirror(char *path, char *aux, char *pool, size_t size,
- uint64_t ashift, int r, int m)
-{
- nvlist_t *mirror, **child;
- int c;
-
- if (m < 1)
- return (make_vdev_raidz(path, aux, pool, size, ashift, r));
-
- child = umem_alloc(m * sizeof (nvlist_t *), UMEM_NOFAIL);
-
- for (c = 0; c < m; c++)
- child[c] = make_vdev_raidz(path, aux, pool, size, ashift, r);
-
- VERIFY(nvlist_alloc(&mirror, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_string(mirror, ZPOOL_CONFIG_TYPE,
- VDEV_TYPE_MIRROR) == 0);
- VERIFY(nvlist_add_nvlist_array(mirror, ZPOOL_CONFIG_CHILDREN,
- child, m) == 0);
-
- for (c = 0; c < m; c++)
- nvlist_free(child[c]);
-
- umem_free(child, m * sizeof (nvlist_t *));
-
- return (mirror);
-}
-
-static nvlist_t *
-make_vdev_root(char *path, char *aux, char *pool, size_t size, uint64_t ashift,
- const char *class, int r, int m, int t)
-{
- nvlist_t *root, **child;
- int c;
- boolean_t log;
-
- ASSERT(t > 0);
-
- log = (class != NULL && strcmp(class, "log") == 0);
-
- child = umem_alloc(t * sizeof (nvlist_t *), UMEM_NOFAIL);
-
- for (c = 0; c < t; c++) {
- child[c] = make_vdev_mirror(path, aux, pool, size, ashift,
- r, m);
- VERIFY(nvlist_add_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
- log) == 0);
-
- if (class != NULL && class[0] != '\0') {
- ASSERT(m > 1 || log); /* expecting a mirror */
- VERIFY(nvlist_add_string(child[c],
- ZPOOL_CONFIG_ALLOCATION_BIAS, class) == 0);
- }
- }
-
- VERIFY(nvlist_alloc(&root, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_string(root, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) == 0);
- VERIFY(nvlist_add_nvlist_array(root, aux ? aux : ZPOOL_CONFIG_CHILDREN,
- child, t) == 0);
-
- for (c = 0; c < t; c++)
- nvlist_free(child[c]);
-
- umem_free(child, t * sizeof (nvlist_t *));
-
- return (root);
-}
-
-/*
- * Find a random spa version. Returns back a random spa version in the
- * range [initial_version, SPA_VERSION_FEATURES].
- */
-static uint64_t
-ztest_random_spa_version(uint64_t initial_version)
-{
- uint64_t version = initial_version;
-
- if (version <= SPA_VERSION_BEFORE_FEATURES) {
- version = version +
- ztest_random(SPA_VERSION_BEFORE_FEATURES - version + 1);
- }
-
- if (version > SPA_VERSION_BEFORE_FEATURES)
- version = SPA_VERSION_FEATURES;
-
- ASSERT(SPA_VERSION_IS_SUPPORTED(version));
- return (version);
-}
-
-static int
-ztest_random_blocksize(void)
-{
- uint64_t block_shift;
-
- ASSERT(ztest_spa->spa_max_ashift != 0);
-
- /*
- * Choose a block size >= the ashift.
- * If the SPA supports new MAXBLOCKSIZE, test up to 1MB blocks.
- */
- int maxbs = SPA_OLD_MAXBLOCKSHIFT;
- if (spa_maxblocksize(ztest_spa) == SPA_MAXBLOCKSIZE)
- maxbs = 20;
- block_shift = ztest_random(maxbs - ztest_spa->spa_max_ashift + 1);
- return (1 << (SPA_MINBLOCKSHIFT + block_shift));
-}
-
-static int
-ztest_random_dnodesize(void)
-{
- int slots;
- int max_slots = spa_maxdnodesize(ztest_spa) >> DNODE_SHIFT;
-
- if (max_slots == DNODE_MIN_SLOTS)
- return (DNODE_MIN_SIZE);
-
- /*
- * Weight the random distribution more heavily toward smaller
- * dnode sizes since that is more likely to reflect real-world
- * usage.
- */
- ASSERT3U(max_slots, >, 4);
- switch (ztest_random(10)) {
- case 0:
- slots = 5 + ztest_random(max_slots - 4);
- break;
- case 1 ... 4:
- slots = 2 + ztest_random(3);
- break;
- default:
- slots = 1;
- break;
- }
-
- return (slots << DNODE_SHIFT);
-}
-
-static int
-ztest_random_ibshift(void)
-{
- return (DN_MIN_INDBLKSHIFT +
- ztest_random(DN_MAX_INDBLKSHIFT - DN_MIN_INDBLKSHIFT + 1));
-}
-
-static uint64_t
-ztest_random_vdev_top(spa_t *spa, boolean_t log_ok)
-{
- uint64_t top;
- vdev_t *rvd = spa->spa_root_vdev;
- vdev_t *tvd;
-
- ASSERT(spa_config_held(spa, SCL_ALL, RW_READER) != 0);
-
- do {
- top = ztest_random(rvd->vdev_children);
- tvd = rvd->vdev_child[top];
- } while (!vdev_is_concrete(tvd) || (tvd->vdev_islog && !log_ok) ||
- tvd->vdev_mg == NULL || tvd->vdev_mg->mg_class == NULL);
-
- return (top);
-}
-
-static uint64_t
-ztest_random_dsl_prop(zfs_prop_t prop)
-{
- uint64_t value;
-
- do {
- value = zfs_prop_random_value(prop, ztest_random(-1ULL));
- } while (prop == ZFS_PROP_CHECKSUM && value == ZIO_CHECKSUM_OFF);
-
- return (value);
-}
-
-static int
-ztest_dsl_prop_set_uint64(char *osname, zfs_prop_t prop, uint64_t value,
- boolean_t inherit)
-{
- const char *propname = zfs_prop_to_name(prop);
- const char *valname;
- char setpoint[MAXPATHLEN];
- uint64_t curval;
- int error;
-
- error = dsl_prop_set_int(osname, propname,
- (inherit ? ZPROP_SRC_NONE : ZPROP_SRC_LOCAL), value);
-
- if (error == ENOSPC) {
- ztest_record_enospc(FTAG);
- return (error);
- }
- ASSERT0(error);
-
- VERIFY0(dsl_prop_get_integer(osname, propname, &curval, setpoint));
-
- if (ztest_opts.zo_verbose >= 6) {
- VERIFY(zfs_prop_index_to_string(prop, curval, &valname) == 0);
- (void) printf("%s %s = %s at '%s'\n",
- osname, propname, valname, setpoint);
- }
-
- return (error);
-}
-
-static int
-ztest_spa_prop_set_uint64(zpool_prop_t prop, uint64_t value)
-{
- spa_t *spa = ztest_spa;
- nvlist_t *props = NULL;
- int error;
-
- VERIFY(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_uint64(props, zpool_prop_to_name(prop), value) == 0);
-
- error = spa_prop_set(spa, props);
-
- nvlist_free(props);
-
- if (error == ENOSPC) {
- ztest_record_enospc(FTAG);
- return (error);
- }
- ASSERT0(error);
-
- return (error);
-}
-
-static void
-ztest_rll_init(rll_t *rll)
-{
- rll->rll_writer = NULL;
- rll->rll_readers = 0;
- mutex_init(&rll->rll_lock, NULL, USYNC_THREAD, NULL);
- cv_init(&rll->rll_cv, NULL, USYNC_THREAD, NULL);
-}
-
-static void
-ztest_rll_destroy(rll_t *rll)
-{
- ASSERT(rll->rll_writer == NULL);
- ASSERT(rll->rll_readers == 0);
- mutex_destroy(&rll->rll_lock);
- cv_destroy(&rll->rll_cv);
-}
-
-static void
-ztest_rll_lock(rll_t *rll, rl_type_t type)
-{
- mutex_enter(&rll->rll_lock);
-
- if (type == RL_READER) {
- while (rll->rll_writer != NULL)
- cv_wait(&rll->rll_cv, &rll->rll_lock);
- rll->rll_readers++;
- } else {
- while (rll->rll_writer != NULL || rll->rll_readers)
- cv_wait(&rll->rll_cv, &rll->rll_lock);
- rll->rll_writer = curthread;
- }
-
- mutex_exit(&rll->rll_lock);
-}
-
-static void
-ztest_rll_unlock(rll_t *rll)
-{
- mutex_enter(&rll->rll_lock);
-
- if (rll->rll_writer) {
- ASSERT(rll->rll_readers == 0);
- rll->rll_writer = NULL;
- } else {
- ASSERT(rll->rll_readers != 0);
- ASSERT(rll->rll_writer == NULL);
- rll->rll_readers--;
- }
-
- if (rll->rll_writer == NULL && rll->rll_readers == 0)
- cv_broadcast(&rll->rll_cv);
-
- mutex_exit(&rll->rll_lock);
-}
-
-static void
-ztest_object_lock(ztest_ds_t *zd, uint64_t object, rl_type_t type)
-{
- rll_t *rll = &zd->zd_object_lock[object & (ZTEST_OBJECT_LOCKS - 1)];
-
- ztest_rll_lock(rll, type);
-}
-
-static void
-ztest_object_unlock(ztest_ds_t *zd, uint64_t object)
-{
- rll_t *rll = &zd->zd_object_lock[object & (ZTEST_OBJECT_LOCKS - 1)];
-
- ztest_rll_unlock(rll);
-}
-
-static rl_t *
-ztest_range_lock(ztest_ds_t *zd, uint64_t object, uint64_t offset,
- uint64_t size, rl_type_t type)
-{
- uint64_t hash = object ^ (offset % (ZTEST_RANGE_LOCKS + 1));
- rll_t *rll = &zd->zd_range_lock[hash & (ZTEST_RANGE_LOCKS - 1)];
- rl_t *rl;
-
- rl = umem_alloc(sizeof (*rl), UMEM_NOFAIL);
- rl->rl_object = object;
- rl->rl_offset = offset;
- rl->rl_size = size;
- rl->rl_lock = rll;
-
- ztest_rll_lock(rll, type);
-
- return (rl);
-}
-
-static void
-ztest_range_unlock(rl_t *rl)
-{
- rll_t *rll = rl->rl_lock;
-
- ztest_rll_unlock(rll);
-
- umem_free(rl, sizeof (*rl));
-}
-
-static void
-ztest_zd_init(ztest_ds_t *zd, ztest_shared_ds_t *szd, objset_t *os)
-{
- zd->zd_os = os;
- zd->zd_zilog = dmu_objset_zil(os);
- zd->zd_shared = szd;
- dmu_objset_name(os, zd->zd_name);
-
- if (zd->zd_shared != NULL)
- zd->zd_shared->zd_seq = 0;
-
- rw_init(&zd->zd_zilog_lock, NULL, USYNC_THREAD, NULL);
- mutex_init(&zd->zd_dirobj_lock, NULL, USYNC_THREAD, NULL);
-
- for (int l = 0; l < ZTEST_OBJECT_LOCKS; l++)
- ztest_rll_init(&zd->zd_object_lock[l]);
-
- for (int l = 0; l < ZTEST_RANGE_LOCKS; l++)
- ztest_rll_init(&zd->zd_range_lock[l]);
-}
-
-static void
-ztest_zd_fini(ztest_ds_t *zd)
-{
- mutex_destroy(&zd->zd_dirobj_lock);
-
- for (int l = 0; l < ZTEST_OBJECT_LOCKS; l++)
- ztest_rll_destroy(&zd->zd_object_lock[l]);
-
- for (int l = 0; l < ZTEST_RANGE_LOCKS; l++)
- ztest_rll_destroy(&zd->zd_range_lock[l]);
-}
-
-#define TXG_MIGHTWAIT (ztest_random(10) == 0 ? TXG_NOWAIT : TXG_WAIT)
-
-static uint64_t
-ztest_tx_assign(dmu_tx_t *tx, uint64_t txg_how, const char *tag)
-{
- uint64_t txg;
- int error;
-
- /*
- * Attempt to assign tx to some transaction group.
- */
- error = dmu_tx_assign(tx, txg_how);
- if (error) {
- if (error == ERESTART) {
- ASSERT(txg_how == TXG_NOWAIT);
- dmu_tx_wait(tx);
- } else {
- ASSERT3U(error, ==, ENOSPC);
- ztest_record_enospc(tag);
- }
- dmu_tx_abort(tx);
- return (0);
- }
- txg = dmu_tx_get_txg(tx);
- ASSERT(txg != 0);
- return (txg);
-}
-
-static void
-ztest_pattern_set(void *buf, uint64_t size, uint64_t value)
-{
- uint64_t *ip = buf;
- uint64_t *ip_end = (uint64_t *)((uintptr_t)buf + (uintptr_t)size);
-
- while (ip < ip_end)
- *ip++ = value;
-}
-
-static boolean_t
-ztest_pattern_match(void *buf, uint64_t size, uint64_t value)
-{
- uint64_t *ip = buf;
- uint64_t *ip_end = (uint64_t *)((uintptr_t)buf + (uintptr_t)size);
- uint64_t diff = 0;
-
- while (ip < ip_end)
- diff |= (value - *ip++);
-
- return (diff == 0);
-}
-
-static void
-ztest_bt_generate(ztest_block_tag_t *bt, objset_t *os, uint64_t object,
- uint64_t dnodesize, uint64_t offset, uint64_t gen, uint64_t txg,
- uint64_t crtxg)
-{
- bt->bt_magic = BT_MAGIC;
- bt->bt_objset = dmu_objset_id(os);
- bt->bt_object = object;
- bt->bt_dnodesize = dnodesize;
- bt->bt_offset = offset;
- bt->bt_gen = gen;
- bt->bt_txg = txg;
- bt->bt_crtxg = crtxg;
-}
-
-static void
-ztest_bt_verify(ztest_block_tag_t *bt, objset_t *os, uint64_t object,
- uint64_t dnodesize, uint64_t offset, uint64_t gen, uint64_t txg,
- uint64_t crtxg)
-{
- ASSERT3U(bt->bt_magic, ==, BT_MAGIC);
- ASSERT3U(bt->bt_objset, ==, dmu_objset_id(os));
- ASSERT3U(bt->bt_object, ==, object);
- ASSERT3U(bt->bt_dnodesize, ==, dnodesize);
- ASSERT3U(bt->bt_offset, ==, offset);
- ASSERT3U(bt->bt_gen, <=, gen);
- ASSERT3U(bt->bt_txg, <=, txg);
- ASSERT3U(bt->bt_crtxg, ==, crtxg);
-}
-
-static ztest_block_tag_t *
-ztest_bt_bonus(dmu_buf_t *db)
-{
- dmu_object_info_t doi;
- ztest_block_tag_t *bt;
-
- dmu_object_info_from_db(db, &doi);
- ASSERT3U(doi.doi_bonus_size, <=, db->db_size);
- ASSERT3U(doi.doi_bonus_size, >=, sizeof (*bt));
- bt = (void *)((char *)db->db_data + doi.doi_bonus_size - sizeof (*bt));
-
- return (bt);
-}
-
-/*
- * Generate a token to fill up unused bonus buffer space. Try to make
- * it unique to the object, generation, and offset to verify that data
- * is not getting overwritten by data from other dnodes.
- */
-#define ZTEST_BONUS_FILL_TOKEN(obj, ds, gen, offset) \
- (((ds) << 48) | ((gen) << 32) | ((obj) << 8) | (offset))
-
-/*
- * Fill up the unused bonus buffer region before the block tag with a
- * verifiable pattern. Filling the whole bonus area with non-zero data
- * helps ensure that all dnode traversal code properly skips the
- * interior regions of large dnodes.
- */
-void
-ztest_fill_unused_bonus(dmu_buf_t *db, void *end, uint64_t obj,
- objset_t *os, uint64_t gen)
-{
- uint64_t *bonusp;
-
- ASSERT(IS_P2ALIGNED((char *)end - (char *)db->db_data, 8));
-
- for (bonusp = db->db_data; bonusp < (uint64_t *)end; bonusp++) {
- uint64_t token = ZTEST_BONUS_FILL_TOKEN(obj, dmu_objset_id(os),
- gen, bonusp - (uint64_t *)db->db_data);
- *bonusp = token;
- }
-}
-
-/*
- * Verify that the unused area of a bonus buffer is filled with the
- * expected tokens.
- */
-void
-ztest_verify_unused_bonus(dmu_buf_t *db, void *end, uint64_t obj,
- objset_t *os, uint64_t gen)
-{
- uint64_t *bonusp;
-
- for (bonusp = db->db_data; bonusp < (uint64_t *)end; bonusp++) {
- uint64_t token = ZTEST_BONUS_FILL_TOKEN(obj, dmu_objset_id(os),
- gen, bonusp - (uint64_t *)db->db_data);
- VERIFY3U(*bonusp, ==, token);
- }
-}
-
-/*
- * ZIL logging ops
- */
-
-#define lrz_type lr_mode
-#define lrz_blocksize lr_uid
-#define lrz_ibshift lr_gid
-#define lrz_bonustype lr_rdev
-#define lrz_dnodesize lr_crtime[1]
-
-static void
-ztest_log_create(ztest_ds_t *zd, dmu_tx_t *tx, lr_create_t *lr)
-{
- char *name = (void *)(lr + 1); /* name follows lr */
- size_t namesize = strlen(name) + 1;
- itx_t *itx;
-
- if (zil_replaying(zd->zd_zilog, tx))
- return;
-
- itx = zil_itx_create(TX_CREATE, sizeof (*lr) + namesize);
- bcopy(&lr->lr_common + 1, &itx->itx_lr + 1,
- sizeof (*lr) + namesize - sizeof (lr_t));
-
- zil_itx_assign(zd->zd_zilog, itx, tx);
-}
-
-static void
-ztest_log_remove(ztest_ds_t *zd, dmu_tx_t *tx, lr_remove_t *lr, uint64_t object)
-{
- char *name = (void *)(lr + 1); /* name follows lr */
- size_t namesize = strlen(name) + 1;
- itx_t *itx;
-
- if (zil_replaying(zd->zd_zilog, tx))
- return;
-
- itx = zil_itx_create(TX_REMOVE, sizeof (*lr) + namesize);
- bcopy(&lr->lr_common + 1, &itx->itx_lr + 1,
- sizeof (*lr) + namesize - sizeof (lr_t));
-
- itx->itx_oid = object;
- zil_itx_assign(zd->zd_zilog, itx, tx);
-}
-
-static void
-ztest_log_write(ztest_ds_t *zd, dmu_tx_t *tx, lr_write_t *lr)
-{
- itx_t *itx;
- itx_wr_state_t write_state = ztest_random(WR_NUM_STATES);
-
- if (zil_replaying(zd->zd_zilog, tx))
- return;
-
- if (lr->lr_length > zil_max_log_data(zd->zd_zilog))
- write_state = WR_INDIRECT;
-
- itx = zil_itx_create(TX_WRITE,
- sizeof (*lr) + (write_state == WR_COPIED ? lr->lr_length : 0));
-
- if (write_state == WR_COPIED &&
- dmu_read(zd->zd_os, lr->lr_foid, lr->lr_offset, lr->lr_length,
- ((lr_write_t *)&itx->itx_lr) + 1, DMU_READ_NO_PREFETCH) != 0) {
- zil_itx_destroy(itx);
- itx = zil_itx_create(TX_WRITE, sizeof (*lr));
- write_state = WR_NEED_COPY;
- }
- itx->itx_private = zd;
- itx->itx_wr_state = write_state;
- itx->itx_sync = (ztest_random(8) == 0);
-
- bcopy(&lr->lr_common + 1, &itx->itx_lr + 1,
- sizeof (*lr) - sizeof (lr_t));
-
- zil_itx_assign(zd->zd_zilog, itx, tx);
-}
-
-static void
-ztest_log_truncate(ztest_ds_t *zd, dmu_tx_t *tx, lr_truncate_t *lr)
-{
- itx_t *itx;
-
- if (zil_replaying(zd->zd_zilog, tx))
- return;
-
- itx = zil_itx_create(TX_TRUNCATE, sizeof (*lr));
- bcopy(&lr->lr_common + 1, &itx->itx_lr + 1,
- sizeof (*lr) - sizeof (lr_t));
-
- itx->itx_sync = B_FALSE;
- zil_itx_assign(zd->zd_zilog, itx, tx);
-}
-
-static void
-ztest_log_setattr(ztest_ds_t *zd, dmu_tx_t *tx, lr_setattr_t *lr)
-{
- itx_t *itx;
-
- if (zil_replaying(zd->zd_zilog, tx))
- return;
-
- itx = zil_itx_create(TX_SETATTR, sizeof (*lr));
- bcopy(&lr->lr_common + 1, &itx->itx_lr + 1,
- sizeof (*lr) - sizeof (lr_t));
-
- itx->itx_sync = B_FALSE;
- zil_itx_assign(zd->zd_zilog, itx, tx);
-}
-
-/*
- * ZIL replay ops
- */
-static int
-ztest_replay_create(void *arg1, void *arg2, boolean_t byteswap)
-{
- ztest_ds_t *zd = arg1;
- lr_create_t *lr = arg2;
- char *name = (void *)(lr + 1); /* name follows lr */
- objset_t *os = zd->zd_os;
- ztest_block_tag_t *bbt;
- dmu_buf_t *db;
- dmu_tx_t *tx;
- uint64_t txg;
- int error = 0;
- int bonuslen;
-
- if (byteswap)
- byteswap_uint64_array(lr, sizeof (*lr));
-
- ASSERT(lr->lr_doid == ZTEST_DIROBJ);
- ASSERT(name[0] != '\0');
-
- tx = dmu_tx_create(os);
-
- dmu_tx_hold_zap(tx, lr->lr_doid, B_TRUE, name);
-
- if (lr->lrz_type == DMU_OT_ZAP_OTHER) {
- dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, B_TRUE, NULL);
- } else {
- dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT);
- }
-
- txg = ztest_tx_assign(tx, TXG_WAIT, FTAG);
- if (txg == 0)
- return (ENOSPC);
-
- ASSERT(dmu_objset_zil(os)->zl_replay == !!lr->lr_foid);
- bonuslen = DN_BONUS_SIZE(lr->lrz_dnodesize);
-
- if (lr->lrz_type == DMU_OT_ZAP_OTHER) {
- if (lr->lr_foid == 0) {
- lr->lr_foid = zap_create_dnsize(os,
- lr->lrz_type, lr->lrz_bonustype,
- bonuslen, lr->lrz_dnodesize, tx);
- } else {
- error = zap_create_claim_dnsize(os, lr->lr_foid,
- lr->lrz_type, lr->lrz_bonustype,
- bonuslen, lr->lrz_dnodesize, tx);
- }
- } else {
- if (lr->lr_foid == 0) {
- lr->lr_foid = dmu_object_alloc_dnsize(os,
- lr->lrz_type, 0, lr->lrz_bonustype,
- bonuslen, lr->lrz_dnodesize, tx);
- } else {
- error = dmu_object_claim_dnsize(os, lr->lr_foid,
- lr->lrz_type, 0, lr->lrz_bonustype,
- bonuslen, lr->lrz_dnodesize, tx);
- }
- }
-
- if (error) {
- ASSERT3U(error, ==, EEXIST);
- ASSERT(zd->zd_zilog->zl_replay);
- dmu_tx_commit(tx);
- return (error);
- }
-
- ASSERT(lr->lr_foid != 0);
-
- if (lr->lrz_type != DMU_OT_ZAP_OTHER)
- VERIFY3U(0, ==, dmu_object_set_blocksize(os, lr->lr_foid,
- lr->lrz_blocksize, lr->lrz_ibshift, tx));
-
- VERIFY3U(0, ==, dmu_bonus_hold(os, lr->lr_foid, FTAG, &db));
- bbt = ztest_bt_bonus(db);
- dmu_buf_will_dirty(db, tx);
- ztest_bt_generate(bbt, os, lr->lr_foid, lr->lrz_dnodesize, -1ULL,
- lr->lr_gen, txg, txg);
- ztest_fill_unused_bonus(db, bbt, lr->lr_foid, os, lr->lr_gen);
- dmu_buf_rele(db, FTAG);
-
- VERIFY3U(0, ==, zap_add(os, lr->lr_doid, name, sizeof (uint64_t), 1,
- &lr->lr_foid, tx));
-
- (void) ztest_log_create(zd, tx, lr);
-
- dmu_tx_commit(tx);
-
- return (0);
-}
-
-static int
-ztest_replay_remove(void *arg1, void *arg2, boolean_t byteswap)
-{
- ztest_ds_t *zd = arg1;
- lr_remove_t *lr = arg2;
- char *name = (void *)(lr + 1); /* name follows lr */
- objset_t *os = zd->zd_os;
- dmu_object_info_t doi;
- dmu_tx_t *tx;
- uint64_t object, txg;
-
- if (byteswap)
- byteswap_uint64_array(lr, sizeof (*lr));
-
- ASSERT(lr->lr_doid == ZTEST_DIROBJ);
- ASSERT(name[0] != '\0');
-
- VERIFY3U(0, ==,
- zap_lookup(os, lr->lr_doid, name, sizeof (object), 1, &object));
- ASSERT(object != 0);
-
- ztest_object_lock(zd, object, RL_WRITER);
-
- VERIFY3U(0, ==, dmu_object_info(os, object, &doi));
-
- tx = dmu_tx_create(os);
-
- dmu_tx_hold_zap(tx, lr->lr_doid, B_FALSE, name);
- dmu_tx_hold_free(tx, object, 0, DMU_OBJECT_END);
-
- txg = ztest_tx_assign(tx, TXG_WAIT, FTAG);
- if (txg == 0) {
- ztest_object_unlock(zd, object);
- return (ENOSPC);
- }
-
- if (doi.doi_type == DMU_OT_ZAP_OTHER) {
- VERIFY3U(0, ==, zap_destroy(os, object, tx));
- } else {
- VERIFY3U(0, ==, dmu_object_free(os, object, tx));
- }
-
- VERIFY3U(0, ==, zap_remove(os, lr->lr_doid, name, tx));
-
- (void) ztest_log_remove(zd, tx, lr, object);
-
- dmu_tx_commit(tx);
-
- ztest_object_unlock(zd, object);
-
- return (0);
-}
-
-static int
-ztest_replay_write(void *arg1, void *arg2, boolean_t byteswap)
-{
- ztest_ds_t *zd = arg1;
- lr_write_t *lr = arg2;
- objset_t *os = zd->zd_os;
- void *data = lr + 1; /* data follows lr */
- uint64_t offset, length;
- ztest_block_tag_t *bt = data;
- ztest_block_tag_t *bbt;
- uint64_t gen, txg, lrtxg, crtxg;
- dmu_object_info_t doi;
- dmu_tx_t *tx;
- dmu_buf_t *db;
- arc_buf_t *abuf = NULL;
- rl_t *rl;
-
- if (byteswap)
- byteswap_uint64_array(lr, sizeof (*lr));
-
- offset = lr->lr_offset;
- length = lr->lr_length;
-
- /* If it's a dmu_sync() block, write the whole block */
- if (lr->lr_common.lrc_reclen == sizeof (lr_write_t)) {
- uint64_t blocksize = BP_GET_LSIZE(&lr->lr_blkptr);
- if (length < blocksize) {
- offset -= offset % blocksize;
- length = blocksize;
- }
- }
-
- if (bt->bt_magic == BSWAP_64(BT_MAGIC))
- byteswap_uint64_array(bt, sizeof (*bt));
-
- if (bt->bt_magic != BT_MAGIC)
- bt = NULL;
-
- ztest_object_lock(zd, lr->lr_foid, RL_READER);
- rl = ztest_range_lock(zd, lr->lr_foid, offset, length, RL_WRITER);
-
- VERIFY3U(0, ==, dmu_bonus_hold(os, lr->lr_foid, FTAG, &db));
-
- dmu_object_info_from_db(db, &doi);
-
- bbt = ztest_bt_bonus(db);
- ASSERT3U(bbt->bt_magic, ==, BT_MAGIC);
- gen = bbt->bt_gen;
- crtxg = bbt->bt_crtxg;
- lrtxg = lr->lr_common.lrc_txg;
-
- tx = dmu_tx_create(os);
-
- dmu_tx_hold_write(tx, lr->lr_foid, offset, length);
-
- if (ztest_random(8) == 0 && length == doi.doi_data_block_size &&
- P2PHASE(offset, length) == 0)
- abuf = dmu_request_arcbuf(db, length);
-
- txg = ztest_tx_assign(tx, TXG_WAIT, FTAG);
- if (txg == 0) {
- if (abuf != NULL)
- dmu_return_arcbuf(abuf);
- dmu_buf_rele(db, FTAG);
- ztest_range_unlock(rl);
- ztest_object_unlock(zd, lr->lr_foid);
- return (ENOSPC);
- }
-
- if (bt != NULL) {
- /*
- * Usually, verify the old data before writing new data --
- * but not always, because we also want to verify correct
- * behavior when the data was not recently read into cache.
- */
- ASSERT(offset % doi.doi_data_block_size == 0);
- if (ztest_random(4) != 0) {
- int prefetch = ztest_random(2) ?
- DMU_READ_PREFETCH : DMU_READ_NO_PREFETCH;
- ztest_block_tag_t rbt;
-
- VERIFY(dmu_read(os, lr->lr_foid, offset,
- sizeof (rbt), &rbt, prefetch) == 0);
- if (rbt.bt_magic == BT_MAGIC) {
- ztest_bt_verify(&rbt, os, lr->lr_foid, 0,
- offset, gen, txg, crtxg);
- }
- }
-
- /*
- * Writes can appear to be newer than the bonus buffer because
- * the ztest_get_data() callback does a dmu_read() of the
- * open-context data, which may be different than the data
- * as it was when the write was generated.
- */
- if (zd->zd_zilog->zl_replay) {
- ztest_bt_verify(bt, os, lr->lr_foid, 0, offset,
- MAX(gen, bt->bt_gen), MAX(txg, lrtxg),
- bt->bt_crtxg);
- }
-
- /*
- * Set the bt's gen/txg to the bonus buffer's gen/txg
- * so that all of the usual ASSERTs will work.
- */
- ztest_bt_generate(bt, os, lr->lr_foid, 0, offset, gen, txg,
- crtxg);
- }
-
- if (abuf == NULL) {
- dmu_write(os, lr->lr_foid, offset, length, data, tx);
- } else {
- bcopy(data, abuf->b_data, length);
- dmu_assign_arcbuf(db, offset, abuf, tx);
- }
-
- (void) ztest_log_write(zd, tx, lr);
-
- dmu_buf_rele(db, FTAG);
-
- dmu_tx_commit(tx);
-
- ztest_range_unlock(rl);
- ztest_object_unlock(zd, lr->lr_foid);
-
- return (0);
-}
-
-static int
-ztest_replay_truncate(void *arg1, void *arg2, boolean_t byteswap)
-{
- ztest_ds_t *zd = arg1;
- lr_truncate_t *lr = arg2;
- objset_t *os = zd->zd_os;
- dmu_tx_t *tx;
- uint64_t txg;
- rl_t *rl;
-
- if (byteswap)
- byteswap_uint64_array(lr, sizeof (*lr));
-
- ztest_object_lock(zd, lr->lr_foid, RL_READER);
- rl = ztest_range_lock(zd, lr->lr_foid, lr->lr_offset, lr->lr_length,
- RL_WRITER);
-
- tx = dmu_tx_create(os);
-
- dmu_tx_hold_free(tx, lr->lr_foid, lr->lr_offset, lr->lr_length);
-
- txg = ztest_tx_assign(tx, TXG_WAIT, FTAG);
- if (txg == 0) {
- ztest_range_unlock(rl);
- ztest_object_unlock(zd, lr->lr_foid);
- return (ENOSPC);
- }
-
- VERIFY(dmu_free_range(os, lr->lr_foid, lr->lr_offset,
- lr->lr_length, tx) == 0);
-
- (void) ztest_log_truncate(zd, tx, lr);
-
- dmu_tx_commit(tx);
-
- ztest_range_unlock(rl);
- ztest_object_unlock(zd, lr->lr_foid);
-
- return (0);
-}
-
-static int
-ztest_replay_setattr(void *arg1, void *arg2, boolean_t byteswap)
-{
- ztest_ds_t *zd = arg1;
- lr_setattr_t *lr = arg2;
- objset_t *os = zd->zd_os;
- dmu_tx_t *tx;
- dmu_buf_t *db;
- ztest_block_tag_t *bbt;
- uint64_t txg, lrtxg, crtxg, dnodesize;
-
- if (byteswap)
- byteswap_uint64_array(lr, sizeof (*lr));
-
- ztest_object_lock(zd, lr->lr_foid, RL_WRITER);
-
- VERIFY3U(0, ==, dmu_bonus_hold(os, lr->lr_foid, FTAG, &db));
-
- tx = dmu_tx_create(os);
- dmu_tx_hold_bonus(tx, lr->lr_foid);
-
- txg = ztest_tx_assign(tx, TXG_WAIT, FTAG);
- if (txg == 0) {
- dmu_buf_rele(db, FTAG);
- ztest_object_unlock(zd, lr->lr_foid);
- return (ENOSPC);
- }
-
- bbt = ztest_bt_bonus(db);
- ASSERT3U(bbt->bt_magic, ==, BT_MAGIC);
- crtxg = bbt->bt_crtxg;
- lrtxg = lr->lr_common.lrc_txg;
- dnodesize = bbt->bt_dnodesize;
-
- if (zd->zd_zilog->zl_replay) {
- ASSERT(lr->lr_size != 0);
- ASSERT(lr->lr_mode != 0);
- ASSERT(lrtxg != 0);
- } else {
- /*
- * Randomly change the size and increment the generation.
- */
- lr->lr_size = (ztest_random(db->db_size / sizeof (*bbt)) + 1) *
- sizeof (*bbt);
- lr->lr_mode = bbt->bt_gen + 1;
- ASSERT(lrtxg == 0);
- }
-
- /*
- * Verify that the current bonus buffer is not newer than our txg.
- */
- ztest_bt_verify(bbt, os, lr->lr_foid, dnodesize, -1ULL, lr->lr_mode,
- MAX(txg, lrtxg), crtxg);
-
- dmu_buf_will_dirty(db, tx);
-
- ASSERT3U(lr->lr_size, >=, sizeof (*bbt));
- ASSERT3U(lr->lr_size, <=, db->db_size);
- VERIFY0(dmu_set_bonus(db, lr->lr_size, tx));
- bbt = ztest_bt_bonus(db);
-
- ztest_bt_generate(bbt, os, lr->lr_foid, dnodesize, -1ULL, lr->lr_mode,
- txg, crtxg);
- ztest_fill_unused_bonus(db, bbt, lr->lr_foid, os, bbt->bt_gen);
-
- dmu_buf_rele(db, FTAG);
-
- (void) ztest_log_setattr(zd, tx, lr);
-
- dmu_tx_commit(tx);
-
- ztest_object_unlock(zd, lr->lr_foid);
-
- return (0);
-}
-
-zil_replay_func_t *ztest_replay_vector[TX_MAX_TYPE] = {
- NULL, /* 0 no such transaction type */
- ztest_replay_create, /* TX_CREATE */
- NULL, /* TX_MKDIR */
- NULL, /* TX_MKXATTR */
- NULL, /* TX_SYMLINK */
- ztest_replay_remove, /* TX_REMOVE */
- NULL, /* TX_RMDIR */
- NULL, /* TX_LINK */
- NULL, /* TX_RENAME */
- ztest_replay_write, /* TX_WRITE */
- ztest_replay_truncate, /* TX_TRUNCATE */
- ztest_replay_setattr, /* TX_SETATTR */
- NULL, /* TX_ACL */
- NULL, /* TX_CREATE_ACL */
- NULL, /* TX_CREATE_ATTR */
- NULL, /* TX_CREATE_ACL_ATTR */
- NULL, /* TX_MKDIR_ACL */
- NULL, /* TX_MKDIR_ATTR */
- NULL, /* TX_MKDIR_ACL_ATTR */
- NULL, /* TX_WRITE2 */
-};
-
-/*
- * ZIL get_data callbacks
- */
-
-/* ARGSUSED */
-static void
-ztest_get_done(zgd_t *zgd, int error)
-{
- ztest_ds_t *zd = zgd->zgd_private;
- uint64_t object = ((rl_t *)zgd->zgd_lr)->rl_object;
-
- if (zgd->zgd_db)
- dmu_buf_rele(zgd->zgd_db, zgd);
-
- ztest_range_unlock((rl_t *)zgd->zgd_lr);
- ztest_object_unlock(zd, object);
-
- umem_free(zgd, sizeof (*zgd));
-}
-
-static int
-ztest_get_data(void *arg, lr_write_t *lr, char *buf, struct lwb *lwb,
- zio_t *zio)
-{
- ztest_ds_t *zd = arg;
- objset_t *os = zd->zd_os;
- uint64_t object = lr->lr_foid;
- uint64_t offset = lr->lr_offset;
- uint64_t size = lr->lr_length;
- uint64_t txg = lr->lr_common.lrc_txg;
- uint64_t crtxg;
- dmu_object_info_t doi;
- dmu_buf_t *db;
- zgd_t *zgd;
- int error;
-
- ASSERT3P(lwb, !=, NULL);
- ASSERT3P(zio, !=, NULL);
- ASSERT3U(size, !=, 0);
-
- ztest_object_lock(zd, object, RL_READER);
- error = dmu_bonus_hold(os, object, FTAG, &db);
- if (error) {
- ztest_object_unlock(zd, object);
- return (error);
- }
-
- crtxg = ztest_bt_bonus(db)->bt_crtxg;
-
- if (crtxg == 0 || crtxg > txg) {
- dmu_buf_rele(db, FTAG);
- ztest_object_unlock(zd, object);
- return (ENOENT);
- }
-
- dmu_object_info_from_db(db, &doi);
- dmu_buf_rele(db, FTAG);
- db = NULL;
-
- zgd = umem_zalloc(sizeof (*zgd), UMEM_NOFAIL);
- zgd->zgd_lwb = lwb;
- zgd->zgd_private = zd;
-
- if (buf != NULL) { /* immediate write */
- zgd->zgd_lr = (struct locked_range *)ztest_range_lock(zd,
- object, offset, size, RL_READER);
-
- error = dmu_read(os, object, offset, size, buf,
- DMU_READ_NO_PREFETCH);
- ASSERT(error == 0);
- } else {
- size = doi.doi_data_block_size;
- if (ISP2(size)) {
- offset = P2ALIGN(offset, size);
- } else {
- ASSERT(offset < size);
- offset = 0;
- }
-
- zgd->zgd_lr = (struct locked_range *)ztest_range_lock(zd,
- object, offset, size, RL_READER);
-
- error = dmu_buf_hold(os, object, offset, zgd, &db,
- DMU_READ_NO_PREFETCH);
-
- if (error == 0) {
- blkptr_t *bp = &lr->lr_blkptr;
-
- zgd->zgd_db = db;
- zgd->zgd_bp = bp;
-
- ASSERT(db->db_offset == offset);
- ASSERT(db->db_size == size);
-
- error = dmu_sync(zio, lr->lr_common.lrc_txg,
- ztest_get_done, zgd);
-
- if (error == 0)
- return (0);
- }
- }
-
- ztest_get_done(zgd, error);
-
- return (error);
-}
-
-static void *
-ztest_lr_alloc(size_t lrsize, char *name)
-{
- char *lr;
- size_t namesize = name ? strlen(name) + 1 : 0;
-
- lr = umem_zalloc(lrsize + namesize, UMEM_NOFAIL);
-
- if (name)
- bcopy(name, lr + lrsize, namesize);
-
- return (lr);
-}
-
-void
-ztest_lr_free(void *lr, size_t lrsize, char *name)
-{
- size_t namesize = name ? strlen(name) + 1 : 0;
-
- umem_free(lr, lrsize + namesize);
-}
-
-/*
- * Lookup a bunch of objects. Returns the number of objects not found.
- */
-static int
-ztest_lookup(ztest_ds_t *zd, ztest_od_t *od, int count)
-{
- int missing = 0;
- int error;
-
- ASSERT(MUTEX_HELD(&zd->zd_dirobj_lock));
-
- for (int i = 0; i < count; i++, od++) {
- od->od_object = 0;
- error = zap_lookup(zd->zd_os, od->od_dir, od->od_name,
- sizeof (uint64_t), 1, &od->od_object);
- if (error) {
- ASSERT(error == ENOENT);
- ASSERT(od->od_object == 0);
- missing++;
- } else {
- dmu_buf_t *db;
- ztest_block_tag_t *bbt;
- dmu_object_info_t doi;
-
- ASSERT(od->od_object != 0);
- ASSERT(missing == 0); /* there should be no gaps */
-
- ztest_object_lock(zd, od->od_object, RL_READER);
- VERIFY3U(0, ==, dmu_bonus_hold(zd->zd_os,
- od->od_object, FTAG, &db));
- dmu_object_info_from_db(db, &doi);
- bbt = ztest_bt_bonus(db);
- ASSERT3U(bbt->bt_magic, ==, BT_MAGIC);
- od->od_type = doi.doi_type;
- od->od_blocksize = doi.doi_data_block_size;
- od->od_gen = bbt->bt_gen;
- dmu_buf_rele(db, FTAG);
- ztest_object_unlock(zd, od->od_object);
- }
- }
-
- return (missing);
-}
-
-static int
-ztest_create(ztest_ds_t *zd, ztest_od_t *od, int count)
-{
- int missing = 0;
-
- ASSERT(MUTEX_HELD(&zd->zd_dirobj_lock));
-
- for (int i = 0; i < count; i++, od++) {
- if (missing) {
- od->od_object = 0;
- missing++;
- continue;
- }
-
- lr_create_t *lr = ztest_lr_alloc(sizeof (*lr), od->od_name);
-
- lr->lr_doid = od->od_dir;
- lr->lr_foid = 0; /* 0 to allocate, > 0 to claim */
- lr->lrz_type = od->od_crtype;
- lr->lrz_blocksize = od->od_crblocksize;
- lr->lrz_ibshift = ztest_random_ibshift();
- lr->lrz_bonustype = DMU_OT_UINT64_OTHER;
- lr->lrz_dnodesize = od->od_crdnodesize;
- lr->lr_gen = od->od_crgen;
- lr->lr_crtime[0] = time(NULL);
-
- if (ztest_replay_create(zd, lr, B_FALSE) != 0) {
- ASSERT(missing == 0);
- od->od_object = 0;
- missing++;
- } else {
- od->od_object = lr->lr_foid;
- od->od_type = od->od_crtype;
- od->od_blocksize = od->od_crblocksize;
- od->od_gen = od->od_crgen;
- ASSERT(od->od_object != 0);
- }
-
- ztest_lr_free(lr, sizeof (*lr), od->od_name);
- }
-
- return (missing);
-}
-
-static int
-ztest_remove(ztest_ds_t *zd, ztest_od_t *od, int count)
-{
- int missing = 0;
- int error;
-
- ASSERT(MUTEX_HELD(&zd->zd_dirobj_lock));
-
- od += count - 1;
-
- for (int i = count - 1; i >= 0; i--, od--) {
- if (missing) {
- missing++;
- continue;
- }
-
- /*
- * No object was found.
- */
- if (od->od_object == 0)
- continue;
-
- lr_remove_t *lr = ztest_lr_alloc(sizeof (*lr), od->od_name);
-
- lr->lr_doid = od->od_dir;
-
- if ((error = ztest_replay_remove(zd, lr, B_FALSE)) != 0) {
- ASSERT3U(error, ==, ENOSPC);
- missing++;
- } else {
- od->od_object = 0;
- }
- ztest_lr_free(lr, sizeof (*lr), od->od_name);
- }
-
- return (missing);
-}
-
-static int
-ztest_write(ztest_ds_t *zd, uint64_t object, uint64_t offset, uint64_t size,
- void *data)
-{
- lr_write_t *lr;
- int error;
-
- lr = ztest_lr_alloc(sizeof (*lr) + size, NULL);
-
- lr->lr_foid = object;
- lr->lr_offset = offset;
- lr->lr_length = size;
- lr->lr_blkoff = 0;
- BP_ZERO(&lr->lr_blkptr);
-
- bcopy(data, lr + 1, size);
-
- error = ztest_replay_write(zd, lr, B_FALSE);
-
- ztest_lr_free(lr, sizeof (*lr) + size, NULL);
-
- return (error);
-}
-
-static int
-ztest_truncate(ztest_ds_t *zd, uint64_t object, uint64_t offset, uint64_t size)
-{
- lr_truncate_t *lr;
- int error;
-
- lr = ztest_lr_alloc(sizeof (*lr), NULL);
-
- lr->lr_foid = object;
- lr->lr_offset = offset;
- lr->lr_length = size;
-
- error = ztest_replay_truncate(zd, lr, B_FALSE);
-
- ztest_lr_free(lr, sizeof (*lr), NULL);
-
- return (error);
-}
-
-static int
-ztest_setattr(ztest_ds_t *zd, uint64_t object)
-{
- lr_setattr_t *lr;
- int error;
-
- lr = ztest_lr_alloc(sizeof (*lr), NULL);
-
- lr->lr_foid = object;
- lr->lr_size = 0;
- lr->lr_mode = 0;
-
- error = ztest_replay_setattr(zd, lr, B_FALSE);
-
- ztest_lr_free(lr, sizeof (*lr), NULL);
-
- return (error);
-}
-
-static void
-ztest_prealloc(ztest_ds_t *zd, uint64_t object, uint64_t offset, uint64_t size)
-{
- objset_t *os = zd->zd_os;
- dmu_tx_t *tx;
- uint64_t txg;
- rl_t *rl;
-
- txg_wait_synced(dmu_objset_pool(os), 0);
-
- ztest_object_lock(zd, object, RL_READER);
- rl = ztest_range_lock(zd, object, offset, size, RL_WRITER);
-
- tx = dmu_tx_create(os);
-
- dmu_tx_hold_write(tx, object, offset, size);
-
- txg = ztest_tx_assign(tx, TXG_WAIT, FTAG);
-
- if (txg != 0) {
- dmu_prealloc(os, object, offset, size, tx);
- dmu_tx_commit(tx);
- txg_wait_synced(dmu_objset_pool(os), txg);
- } else {
- (void) dmu_free_long_range(os, object, offset, size);
- }
-
- ztest_range_unlock(rl);
- ztest_object_unlock(zd, object);
-}
-
-static void
-ztest_io(ztest_ds_t *zd, uint64_t object, uint64_t offset)
-{
- int err;
- ztest_block_tag_t wbt;
- dmu_object_info_t doi;
- enum ztest_io_type io_type;
- uint64_t blocksize;
- void *data;
-
- VERIFY(dmu_object_info(zd->zd_os, object, &doi) == 0);
- blocksize = doi.doi_data_block_size;
- data = umem_alloc(blocksize, UMEM_NOFAIL);
-
- /*
- * Pick an i/o type at random, biased toward writing block tags.
- */
- io_type = ztest_random(ZTEST_IO_TYPES);
- if (ztest_random(2) == 0)
- io_type = ZTEST_IO_WRITE_TAG;
-
- rw_enter(&zd->zd_zilog_lock, RW_READER);
-
- switch (io_type) {
-
- case ZTEST_IO_WRITE_TAG:
- ztest_bt_generate(&wbt, zd->zd_os, object, doi.doi_dnodesize,
- offset, 0, 0, 0);
- (void) ztest_write(zd, object, offset, sizeof (wbt), &wbt);
- break;
-
- case ZTEST_IO_WRITE_PATTERN:
- (void) memset(data, 'a' + (object + offset) % 5, blocksize);
- if (ztest_random(2) == 0) {
- /*
- * Induce fletcher2 collisions to ensure that
- * zio_ddt_collision() detects and resolves them
- * when using fletcher2-verify for deduplication.
- */
- ((uint64_t *)data)[0] ^= 1ULL << 63;
- ((uint64_t *)data)[4] ^= 1ULL << 63;
- }
- (void) ztest_write(zd, object, offset, blocksize, data);
- break;
-
- case ZTEST_IO_WRITE_ZEROES:
- bzero(data, blocksize);
- (void) ztest_write(zd, object, offset, blocksize, data);
- break;
-
- case ZTEST_IO_TRUNCATE:
- (void) ztest_truncate(zd, object, offset, blocksize);
- break;
-
- case ZTEST_IO_SETATTR:
- (void) ztest_setattr(zd, object);
- break;
-
- case ZTEST_IO_REWRITE:
- rw_enter(&ztest_name_lock, RW_READER);
- err = ztest_dsl_prop_set_uint64(zd->zd_name,
- ZFS_PROP_CHECKSUM, spa_dedup_checksum(ztest_spa),
- B_FALSE);
- VERIFY(err == 0 || err == ENOSPC);
- err = ztest_dsl_prop_set_uint64(zd->zd_name,
- ZFS_PROP_COMPRESSION,
- ztest_random_dsl_prop(ZFS_PROP_COMPRESSION),
- B_FALSE);
- VERIFY(err == 0 || err == ENOSPC);
- rw_exit(&ztest_name_lock);
-
- VERIFY0(dmu_read(zd->zd_os, object, offset, blocksize, data,
- DMU_READ_NO_PREFETCH));
-
- (void) ztest_write(zd, object, offset, blocksize, data);
- break;
- }
-
- rw_exit(&zd->zd_zilog_lock);
-
- umem_free(data, blocksize);
-}
-
-/*
- * Initialize an object description template.
- */
-static void
-ztest_od_init(ztest_od_t *od, uint64_t id, char *tag, uint64_t index,
- dmu_object_type_t type, uint64_t blocksize, uint64_t dnodesize,
- uint64_t gen)
-{
- od->od_dir = ZTEST_DIROBJ;
- od->od_object = 0;
-
- od->od_crtype = type;
- od->od_crblocksize = blocksize ? blocksize : ztest_random_blocksize();
- od->od_crdnodesize = dnodesize ? dnodesize : ztest_random_dnodesize();
- od->od_crgen = gen;
-
- od->od_type = DMU_OT_NONE;
- od->od_blocksize = 0;
- od->od_gen = 0;
-
- (void) snprintf(od->od_name, sizeof (od->od_name), "%s(%lld)[%llu]",
- tag, (int64_t)id, index);
-}
-
-/*
- * Lookup or create the objects for a test using the od template.
- * If the objects do not all exist, or if 'remove' is specified,
- * remove any existing objects and create new ones. Otherwise,
- * use the existing objects.
- */
-static int
-ztest_object_init(ztest_ds_t *zd, ztest_od_t *od, size_t size, boolean_t remove)
-{
- int count = size / sizeof (*od);
- int rv = 0;
-
- mutex_enter(&zd->zd_dirobj_lock);
- if ((ztest_lookup(zd, od, count) != 0 || remove) &&
- (ztest_remove(zd, od, count) != 0 ||
- ztest_create(zd, od, count) != 0))
- rv = -1;
- zd->zd_od = od;
- mutex_exit(&zd->zd_dirobj_lock);
-
- return (rv);
-}
-
-/* ARGSUSED */
-void
-ztest_zil_commit(ztest_ds_t *zd, uint64_t id)
-{
- zilog_t *zilog = zd->zd_zilog;
-
- rw_enter(&zd->zd_zilog_lock, RW_READER);
-
- zil_commit(zilog, ztest_random(ZTEST_OBJECTS));
-
- /*
- * Remember the committed values in zd, which is in parent/child
- * shared memory. If we die, the next iteration of ztest_run()
- * will verify that the log really does contain this record.
- */
- mutex_enter(&zilog->zl_lock);
- ASSERT(zd->zd_shared != NULL);
- ASSERT3U(zd->zd_shared->zd_seq, <=, zilog->zl_commit_lr_seq);
- zd->zd_shared->zd_seq = zilog->zl_commit_lr_seq;
- mutex_exit(&zilog->zl_lock);
-
- rw_exit(&zd->zd_zilog_lock);
-}
-
-/*
- * This function is designed to simulate the operations that occur during a
- * mount/unmount operation. We hold the dataset across these operations in an
- * attempt to expose any implicit assumptions about ZIL management.
- */
-/* ARGSUSED */
-void
-ztest_zil_remount(ztest_ds_t *zd, uint64_t id)
-{
- objset_t *os = zd->zd_os;
-
- /*
- * We grab the zd_dirobj_lock to ensure that no other thread is
- * updating the zil (i.e. adding in-memory log records) and the
- * zd_zilog_lock to block any I/O.
- */
- mutex_enter(&zd->zd_dirobj_lock);
- rw_enter(&zd->zd_zilog_lock, RW_WRITER);
-
- /* zfsvfs_teardown() */
- zil_close(zd->zd_zilog);
-
- /* zfsvfs_setup() */
- VERIFY(zil_open(os, ztest_get_data) == zd->zd_zilog);
- zil_replay(os, zd, ztest_replay_vector);
-
- rw_exit(&zd->zd_zilog_lock);
- mutex_exit(&zd->zd_dirobj_lock);
-}
-
-/*
- * Verify that we can't destroy an active pool, create an existing pool,
- * or create a pool with a bad vdev spec.
- */
-/* ARGSUSED */
-void
-ztest_spa_create_destroy(ztest_ds_t *zd, uint64_t id)
-{
- ztest_shared_opts_t *zo = &ztest_opts;
- spa_t *spa;
- nvlist_t *nvroot;
-
- if (zo->zo_mmp_test)
- return;
-
- /*
- * Attempt to create using a bad file.
- */
- nvroot = make_vdev_root("/dev/bogus", NULL, NULL, 0, 0, NULL, 0, 0, 1);
- VERIFY3U(ENOENT, ==,
- spa_create("ztest_bad_file", nvroot, NULL, NULL));
- nvlist_free(nvroot);
-
- /*
- * Attempt to create using a bad mirror.
- */
- nvroot = make_vdev_root("/dev/bogus", NULL, NULL, 0, 0, NULL, 0, 2, 1);
- VERIFY3U(ENOENT, ==,
- spa_create("ztest_bad_mirror", nvroot, NULL, NULL));
- nvlist_free(nvroot);
-
- /*
- * Attempt to create an existing pool. It shouldn't matter
- * what's in the nvroot; we should fail with EEXIST.
- */
- rw_enter(&ztest_name_lock, RW_READER);
- nvroot = make_vdev_root("/dev/bogus", NULL, NULL, 0, 0, NULL, 0, 0, 1);
- VERIFY3U(EEXIST, ==, spa_create(zo->zo_pool, nvroot, NULL, NULL));
- nvlist_free(nvroot);
- VERIFY3U(0, ==, spa_open(zo->zo_pool, &spa, FTAG));
- VERIFY3U(EBUSY, ==, spa_destroy(zo->zo_pool));
- spa_close(spa, FTAG);
-
- rw_exit(&ztest_name_lock);
-}
-
-/*
- * Start and then stop the MMP threads to ensure the startup and shutdown code
- * works properly. Actual protection and property-related code tested via ZTS.
- */
-/* ARGSUSED */
-void
-ztest_mmp_enable_disable(ztest_ds_t *zd, uint64_t id)
-{
- ztest_shared_opts_t *zo = &ztest_opts;
- spa_t *spa = ztest_spa;
-
- if (zo->zo_mmp_test)
- return;
-
- /*
- * Since enabling MMP involves setting a property, it could not be done
- * while the pool is suspended.
- */
- if (spa_suspended(spa))
- return;
-
- spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER);
- mutex_enter(&spa->spa_props_lock);
-
- zfs_multihost_fail_intervals = 0;
-
- if (!spa_multihost(spa)) {
- spa->spa_multihost = B_TRUE;
- mmp_thread_start(spa);
- }
-
- mutex_exit(&spa->spa_props_lock);
- spa_config_exit(spa, SCL_CONFIG, FTAG);
-
- txg_wait_synced(spa_get_dsl(spa), 0);
- mmp_signal_all_threads();
- txg_wait_synced(spa_get_dsl(spa), 0);
-
- spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER);
- mutex_enter(&spa->spa_props_lock);
-
- if (spa_multihost(spa)) {
- mmp_thread_stop(spa);
- spa->spa_multihost = B_FALSE;
- }
-
- mutex_exit(&spa->spa_props_lock);
- spa_config_exit(spa, SCL_CONFIG, FTAG);
-}
-
-/* ARGSUSED */
-void
-ztest_spa_upgrade(ztest_ds_t *zd, uint64_t id)
-{
- spa_t *spa;
- uint64_t initial_version = SPA_VERSION_INITIAL;
- uint64_t version, newversion;
- nvlist_t *nvroot, *props;
- char *name;
-
- if (ztest_opts.zo_mmp_test)
- return;
-
- mutex_enter(&ztest_vdev_lock);
- name = kmem_asprintf("%s_upgrade", ztest_opts.zo_pool);
-
- /*
- * Clean up from previous runs.
- */
- (void) spa_destroy(name);
-
- nvroot = make_vdev_root(NULL, NULL, name, ztest_opts.zo_vdev_size, 0,
- NULL, ztest_opts.zo_raidz, ztest_opts.zo_mirrors, 1);
-
- /*
- * If we're configuring a RAIDZ device then make sure that the
- * the initial version is capable of supporting that feature.
- */
- switch (ztest_opts.zo_raidz_parity) {
- case 0:
- case 1:
- initial_version = SPA_VERSION_INITIAL;
- break;
- case 2:
- initial_version = SPA_VERSION_RAIDZ2;
- break;
- case 3:
- initial_version = SPA_VERSION_RAIDZ3;
- break;
- }
-
- /*
- * Create a pool with a spa version that can be upgraded. Pick
- * a value between initial_version and SPA_VERSION_BEFORE_FEATURES.
- */
- do {
- version = ztest_random_spa_version(initial_version);
- } while (version > SPA_VERSION_BEFORE_FEATURES);
-
- props = fnvlist_alloc();
- fnvlist_add_uint64(props,
- zpool_prop_to_name(ZPOOL_PROP_VERSION), version);
- VERIFY0(spa_create(name, nvroot, props, NULL));
- fnvlist_free(nvroot);
- fnvlist_free(props);
-
- VERIFY0(spa_open(name, &spa, FTAG));
- VERIFY3U(spa_version(spa), ==, version);
- newversion = ztest_random_spa_version(version + 1);
-
- if (ztest_opts.zo_verbose >= 4) {
- (void) printf("upgrading spa version from %llu to %llu\n",
- (u_longlong_t)version, (u_longlong_t)newversion);
- }
-
- spa_upgrade(spa, newversion);
- VERIFY3U(spa_version(spa), >, version);
- VERIFY3U(spa_version(spa), ==, fnvlist_lookup_uint64(spa->spa_config,
- zpool_prop_to_name(ZPOOL_PROP_VERSION)));
- spa_close(spa, FTAG);
-
- strfree(name);
- mutex_exit(&ztest_vdev_lock);
-}
-
-static void
-ztest_spa_checkpoint(spa_t *spa)
-{
- ASSERT(MUTEX_HELD(&ztest_checkpoint_lock));
-
- int error = spa_checkpoint(spa->spa_name);
-
- switch (error) {
- case 0:
- case ZFS_ERR_DEVRM_IN_PROGRESS:
- case ZFS_ERR_DISCARDING_CHECKPOINT:
- case ZFS_ERR_CHECKPOINT_EXISTS:
- break;
- case ENOSPC:
- ztest_record_enospc(FTAG);
- break;
- default:
- fatal(0, "spa_checkpoint(%s) = %d", spa->spa_name, error);
- }
-}
-
-static void
-ztest_spa_discard_checkpoint(spa_t *spa)
-{
- ASSERT(MUTEX_HELD(&ztest_checkpoint_lock));
-
- int error = spa_checkpoint_discard(spa->spa_name);
-
- switch (error) {
- case 0:
- case ZFS_ERR_DISCARDING_CHECKPOINT:
- case ZFS_ERR_NO_CHECKPOINT:
- break;
- default:
- fatal(0, "spa_discard_checkpoint(%s) = %d",
- spa->spa_name, error);
- }
-
-}
-
-/* ARGSUSED */
-void
-ztest_spa_checkpoint_create_discard(ztest_ds_t *zd, uint64_t id)
-{
- spa_t *spa = ztest_spa;
-
- mutex_enter(&ztest_checkpoint_lock);
- if (ztest_random(2) == 0) {
- ztest_spa_checkpoint(spa);
- } else {
- ztest_spa_discard_checkpoint(spa);
- }
- mutex_exit(&ztest_checkpoint_lock);
-}
-
-
-static vdev_t *
-vdev_lookup_by_path(vdev_t *vd, const char *path)
-{
- vdev_t *mvd;
-
- if (vd->vdev_path != NULL && strcmp(path, vd->vdev_path) == 0)
- return (vd);
-
- for (int c = 0; c < vd->vdev_children; c++)
- if ((mvd = vdev_lookup_by_path(vd->vdev_child[c], path)) !=
- NULL)
- return (mvd);
-
- return (NULL);
-}
-
-/*
- * Find the first available hole which can be used as a top-level.
- */
-int
-find_vdev_hole(spa_t *spa)
-{
- vdev_t *rvd = spa->spa_root_vdev;
- int c;
-
- ASSERT(spa_config_held(spa, SCL_VDEV, RW_READER) == SCL_VDEV);
-
- for (c = 0; c < rvd->vdev_children; c++) {
- vdev_t *cvd = rvd->vdev_child[c];
-
- if (cvd->vdev_ishole)
- break;
- }
- return (c);
-}
-
-/*
- * Verify that vdev_add() works as expected.
- */
-/* ARGSUSED */
-void
-ztest_vdev_add_remove(ztest_ds_t *zd, uint64_t id)
-{
- ztest_shared_t *zs = ztest_shared;
- spa_t *spa = ztest_spa;
- uint64_t leaves;
- uint64_t guid;
- nvlist_t *nvroot;
- int error;
-
- if (ztest_opts.zo_mmp_test)
- return;
-
- mutex_enter(&ztest_vdev_lock);
- leaves = MAX(zs->zs_mirrors + zs->zs_splits, 1) * ztest_opts.zo_raidz;
-
- spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
-
- ztest_shared->zs_vdev_next_leaf = find_vdev_hole(spa) * leaves;
-
- /*
- * If we have slogs then remove them 1/4 of the time.
- */
- if (spa_has_slogs(spa) && ztest_random(4) == 0) {
- metaslab_group_t *mg;
-
- /*
- * find the first real slog in log allocation class
- */
- mg = spa_log_class(spa)->mc_rotor;
- while (!mg->mg_vd->vdev_islog)
- mg = mg->mg_next;
-
- guid = mg->mg_vd->vdev_guid;
-
- spa_config_exit(spa, SCL_VDEV, FTAG);
-
- /*
- * We have to grab the zs_name_lock as writer to
- * prevent a race between removing a slog (dmu_objset_find)
- * and destroying a dataset. Removing the slog will
- * grab a reference on the dataset which may cause
- * dmu_objset_destroy() to fail with EBUSY thus
- * leaving the dataset in an inconsistent state.
- */
- rw_enter(&ztest_name_lock, RW_WRITER);
- error = spa_vdev_remove(spa, guid, B_FALSE);
- rw_exit(&ztest_name_lock);
-
- switch (error) {
- case 0:
- case EEXIST:
- case ZFS_ERR_CHECKPOINT_EXISTS:
- case ZFS_ERR_DISCARDING_CHECKPOINT:
- break;
- default:
- fatal(0, "spa_vdev_remove() = %d", error);
- }
- } else {
- spa_config_exit(spa, SCL_VDEV, FTAG);
-
- /*
- * Make 1/4 of the devices be log devices
- */
- nvroot = make_vdev_root(NULL, NULL, NULL,
- ztest_opts.zo_vdev_size, 0, (ztest_random(4) == 0) ?
- "log" : NULL, ztest_opts.zo_raidz, zs->zs_mirrors, 1);
-
- error = spa_vdev_add(spa, nvroot);
- nvlist_free(nvroot);
-
- switch (error) {
- case 0:
- break;
- case ENOSPC:
- ztest_record_enospc("spa_vdev_add");
- break;
- default:
- fatal(0, "spa_vdev_add() = %d", error);
- }
- }
-
- mutex_exit(&ztest_vdev_lock);
-}
-
-/* ARGSUSED */
-void
-ztest_vdev_class_add(ztest_ds_t *zd, uint64_t id)
-{
- ztest_shared_t *zs = ztest_shared;
- spa_t *spa = ztest_spa;
- uint64_t leaves;
- nvlist_t *nvroot;
- const char *class = (ztest_random(2) == 0) ?
- VDEV_ALLOC_BIAS_SPECIAL : VDEV_ALLOC_BIAS_DEDUP;
- int error;
-
- /*
- * By default add a special vdev 50% of the time
- */
- if ((ztest_opts.zo_special_vdevs == ZTEST_VDEV_CLASS_OFF) ||
- (ztest_opts.zo_special_vdevs == ZTEST_VDEV_CLASS_RND &&
- ztest_random(2) == 0)) {
- return;
- }
-
- mutex_enter(&ztest_vdev_lock);
-
- /* Only test with mirrors */
- if (zs->zs_mirrors < 2) {
- mutex_exit(&ztest_vdev_lock);
- return;
- }
-
- /* requires feature@allocation_classes */
- if (!spa_feature_is_enabled(spa, SPA_FEATURE_ALLOCATION_CLASSES)) {
- mutex_exit(&ztest_vdev_lock);
- return;
- }
-
- leaves = MAX(zs->zs_mirrors + zs->zs_splits, 1) * ztest_opts.zo_raidz;
-
- spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
- ztest_shared->zs_vdev_next_leaf = find_vdev_hole(spa) * leaves;
- spa_config_exit(spa, SCL_VDEV, FTAG);
-
- nvroot = make_vdev_root(NULL, NULL, NULL, ztest_opts.zo_vdev_size, 0,
- class, ztest_opts.zo_raidz, zs->zs_mirrors, 1);
-
- error = spa_vdev_add(spa, nvroot);
- nvlist_free(nvroot);
-
- if (error == ENOSPC)
- ztest_record_enospc("spa_vdev_add");
- else if (error != 0)
- fatal(0, "spa_vdev_add() = %d", error);
-
- /*
- * 50% of the time allow small blocks in the special class
- */
- if (error == 0 &&
- spa_special_class(spa)->mc_groups == 1 && ztest_random(2) == 0) {
- if (ztest_opts.zo_verbose >= 3)
- (void) printf("Enabling special VDEV small blocks\n");
- (void) ztest_dsl_prop_set_uint64(zd->zd_name,
- ZFS_PROP_SPECIAL_SMALL_BLOCKS, 32768, B_FALSE);
- }
-
- mutex_exit(&ztest_vdev_lock);
-
- if (ztest_opts.zo_verbose >= 3) {
- metaslab_class_t *mc;
-
- if (strcmp(class, VDEV_ALLOC_BIAS_SPECIAL) == 0)
- mc = spa_special_class(spa);
- else
- mc = spa_dedup_class(spa);
- (void) printf("Added a %s mirrored vdev (of %d)\n",
- class, (int)mc->mc_groups);
- }
-}
-
-/*
- * Verify that adding/removing aux devices (l2arc, hot spare) works as expected.
- */
-/* ARGSUSED */
-void
-ztest_vdev_aux_add_remove(ztest_ds_t *zd, uint64_t id)
-{
- ztest_shared_t *zs = ztest_shared;
- spa_t *spa = ztest_spa;
- vdev_t *rvd = spa->spa_root_vdev;
- spa_aux_vdev_t *sav;
- char *aux;
- uint64_t guid = 0;
- int error;
-
- if (ztest_opts.zo_mmp_test)
- return;
-
- if (ztest_random(2) == 0) {
- sav = &spa->spa_spares;
- aux = ZPOOL_CONFIG_SPARES;
- } else {
- sav = &spa->spa_l2cache;
- aux = ZPOOL_CONFIG_L2CACHE;
- }
-
- mutex_enter(&ztest_vdev_lock);
-
- spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
-
- if (sav->sav_count != 0 && ztest_random(4) == 0) {
- /*
- * Pick a random device to remove.
- */
- guid = sav->sav_vdevs[ztest_random(sav->sav_count)]->vdev_guid;
- } else {
- /*
- * Find an unused device we can add.
- */
- zs->zs_vdev_aux = 0;
- for (;;) {
- char path[MAXPATHLEN];
- int c;
- (void) snprintf(path, sizeof (path), ztest_aux_template,
- ztest_opts.zo_dir, ztest_opts.zo_pool, aux,
- zs->zs_vdev_aux);
- for (c = 0; c < sav->sav_count; c++)
- if (strcmp(sav->sav_vdevs[c]->vdev_path,
- path) == 0)
- break;
- if (c == sav->sav_count &&
- vdev_lookup_by_path(rvd, path) == NULL)
- break;
- zs->zs_vdev_aux++;
- }
- }
-
- spa_config_exit(spa, SCL_VDEV, FTAG);
-
- if (guid == 0) {
- /*
- * Add a new device.
- */
- nvlist_t *nvroot = make_vdev_root(NULL, aux, NULL,
- (ztest_opts.zo_vdev_size * 5) / 4, 0, NULL, 0, 0, 1);
- error = spa_vdev_add(spa, nvroot);
-
- switch (error) {
- case 0:
- break;
- default:
- fatal(0, "spa_vdev_add(%p) = %d", nvroot, error);
- }
- nvlist_free(nvroot);
- } else {
- /*
- * Remove an existing device. Sometimes, dirty its
- * vdev state first to make sure we handle removal
- * of devices that have pending state changes.
- */
- if (ztest_random(2) == 0)
- (void) vdev_online(spa, guid, 0, NULL);
-
- error = spa_vdev_remove(spa, guid, B_FALSE);
-
- switch (error) {
- case 0:
- case EBUSY:
- case ZFS_ERR_CHECKPOINT_EXISTS:
- case ZFS_ERR_DISCARDING_CHECKPOINT:
- break;
- default:
- fatal(0, "spa_vdev_remove(%llu) = %d", guid, error);
- }
- }
-
- mutex_exit(&ztest_vdev_lock);
-}
-
-/*
- * split a pool if it has mirror tlvdevs
- */
-/* ARGSUSED */
-void
-ztest_split_pool(ztest_ds_t *zd, uint64_t id)
-{
- ztest_shared_t *zs = ztest_shared;
- spa_t *spa = ztest_spa;
- vdev_t *rvd = spa->spa_root_vdev;
- nvlist_t *tree, **child, *config, *split, **schild;
- uint_t c, children, schildren = 0, lastlogid = 0;
- int error = 0;
-
- if (ztest_opts.zo_mmp_test)
- return;
-
- mutex_enter(&ztest_vdev_lock);
-
- /* ensure we have a useable config; mirrors of raidz aren't supported */
- if (zs->zs_mirrors < 3 || ztest_opts.zo_raidz > 1) {
- mutex_exit(&ztest_vdev_lock);
- return;
- }
-
- /* clean up the old pool, if any */
- (void) spa_destroy("splitp");
-
- spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
-
- /* generate a config from the existing config */
- mutex_enter(&spa->spa_props_lock);
- VERIFY(nvlist_lookup_nvlist(spa->spa_config, ZPOOL_CONFIG_VDEV_TREE,
- &tree) == 0);
- mutex_exit(&spa->spa_props_lock);
-
- VERIFY(nvlist_lookup_nvlist_array(tree, ZPOOL_CONFIG_CHILDREN, &child,
- &children) == 0);
-
- schild = malloc(rvd->vdev_children * sizeof (nvlist_t *));
- for (c = 0; c < children; c++) {
- vdev_t *tvd = rvd->vdev_child[c];
- nvlist_t **mchild;
- uint_t mchildren;
-
- if (tvd->vdev_islog || tvd->vdev_ops == &vdev_hole_ops) {
- VERIFY(nvlist_alloc(&schild[schildren], NV_UNIQUE_NAME,
- 0) == 0);
- VERIFY(nvlist_add_string(schild[schildren],
- ZPOOL_CONFIG_TYPE, VDEV_TYPE_HOLE) == 0);
- VERIFY(nvlist_add_uint64(schild[schildren],
- ZPOOL_CONFIG_IS_HOLE, 1) == 0);
- if (lastlogid == 0)
- lastlogid = schildren;
- ++schildren;
- continue;
- }
- lastlogid = 0;
- VERIFY(nvlist_lookup_nvlist_array(child[c],
- ZPOOL_CONFIG_CHILDREN, &mchild, &mchildren) == 0);
- VERIFY(nvlist_dup(mchild[0], &schild[schildren++], 0) == 0);
- }
-
- /* OK, create a config that can be used to split */
- VERIFY(nvlist_alloc(&split, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_string(split, ZPOOL_CONFIG_TYPE,
- VDEV_TYPE_ROOT) == 0);
- VERIFY(nvlist_add_nvlist_array(split, ZPOOL_CONFIG_CHILDREN, schild,
- lastlogid != 0 ? lastlogid : schildren) == 0);
-
- VERIFY(nvlist_alloc(&config, NV_UNIQUE_NAME, 0) == 0);
- VERIFY(nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, split) == 0);
-
- for (c = 0; c < schildren; c++)
- nvlist_free(schild[c]);
- free(schild);
- nvlist_free(split);
-
- spa_config_exit(spa, SCL_VDEV, FTAG);
-
- rw_enter(&ztest_name_lock, RW_WRITER);
- error = spa_vdev_split_mirror(spa, "splitp", config, NULL, B_FALSE);
- rw_exit(&ztest_name_lock);
-
- nvlist_free(config);
-
- if (error == 0) {
- (void) printf("successful split - results:\n");
- mutex_enter(&spa_namespace_lock);
- show_pool_stats(spa);
- show_pool_stats(spa_lookup("splitp"));
- mutex_exit(&spa_namespace_lock);
- ++zs->zs_splits;
- --zs->zs_mirrors;
- }
- mutex_exit(&ztest_vdev_lock);
-}
-
-/*
- * Verify that we can attach and detach devices.
- */
-/* ARGSUSED */
-void
-ztest_vdev_attach_detach(ztest_ds_t *zd, uint64_t id)
-{
- ztest_shared_t *zs = ztest_shared;
- spa_t *spa = ztest_spa;
- spa_aux_vdev_t *sav = &spa->spa_spares;
- vdev_t *rvd = spa->spa_root_vdev;
- vdev_t *oldvd, *newvd, *pvd;
- nvlist_t *root;
- uint64_t leaves;
- uint64_t leaf, top;
- uint64_t ashift = ztest_get_ashift();
- uint64_t oldguid, pguid;
- uint64_t oldsize, newsize;
- char oldpath[MAXPATHLEN], newpath[MAXPATHLEN];
- int replacing;
- int oldvd_has_siblings = B_FALSE;
- int newvd_is_spare = B_FALSE;
- int oldvd_is_log;
- int error, expected_error;
-
- if (ztest_opts.zo_mmp_test)
- return;
-
- mutex_enter(&ztest_vdev_lock);
- leaves = MAX(zs->zs_mirrors, 1) * ztest_opts.zo_raidz;
-
- spa_config_enter(spa, SCL_ALL, FTAG, RW_WRITER);
-
- /*
- * If a vdev is in the process of being removed, its removal may
- * finish while we are in progress, leading to an unexpected error
- * value. Don't bother trying to attach while we are in the middle
- * of removal.
- */
- if (ztest_device_removal_active) {
- spa_config_exit(spa, SCL_ALL, FTAG);
- mutex_exit(&ztest_vdev_lock);
- return;
- }
-
- /*
- * Decide whether to do an attach or a replace.
- */
- replacing = ztest_random(2);
-
- /*
- * Pick a random top-level vdev.
- */
- top = ztest_random_vdev_top(spa, B_TRUE);
-
- /*
- * Pick a random leaf within it.
- */
- leaf = ztest_random(leaves);
-
- /*
- * Locate this vdev.
- */
- oldvd = rvd->vdev_child[top];
-
- /* pick a child from the mirror */
- if (zs->zs_mirrors >= 1) {
- ASSERT(oldvd->vdev_ops == &vdev_mirror_ops);
- ASSERT(oldvd->vdev_children >= zs->zs_mirrors);
- oldvd = oldvd->vdev_child[leaf / ztest_opts.zo_raidz];
- }
-
- /* pick a child out of the raidz group */
- if (ztest_opts.zo_raidz > 1) {
- ASSERT(oldvd->vdev_ops == &vdev_raidz_ops);
- ASSERT(oldvd->vdev_children == ztest_opts.zo_raidz);
- oldvd = oldvd->vdev_child[leaf % ztest_opts.zo_raidz];
- }
-
- /*
- * If we're already doing an attach or replace, oldvd may be a
- * mirror vdev -- in which case, pick a random child.
- */
- while (oldvd->vdev_children != 0) {
- oldvd_has_siblings = B_TRUE;
- ASSERT(oldvd->vdev_children >= 2);
- oldvd = oldvd->vdev_child[ztest_random(oldvd->vdev_children)];
- }
-
- oldguid = oldvd->vdev_guid;
- oldsize = vdev_get_min_asize(oldvd);
- oldvd_is_log = oldvd->vdev_top->vdev_islog;
- (void) strcpy(oldpath, oldvd->vdev_path);
- pvd = oldvd->vdev_parent;
- pguid = pvd->vdev_guid;
-
- /*
- * If oldvd has siblings, then half of the time, detach it.
- */
- if (oldvd_has_siblings && ztest_random(2) == 0) {
- spa_config_exit(spa, SCL_ALL, FTAG);
- error = spa_vdev_detach(spa, oldguid, pguid, B_FALSE);
- if (error != 0 && error != ENODEV && error != EBUSY &&
- error != ENOTSUP && error != ZFS_ERR_CHECKPOINT_EXISTS &&
- error != ZFS_ERR_DISCARDING_CHECKPOINT)
- fatal(0, "detach (%s) returned %d", oldpath, error);
- mutex_exit(&ztest_vdev_lock);
- return;
- }
-
- /*
- * For the new vdev, choose with equal probability between the two
- * standard paths (ending in either 'a' or 'b') or a random hot spare.
- */
- if (sav->sav_count != 0 && ztest_random(3) == 0) {
- newvd = sav->sav_vdevs[ztest_random(sav->sav_count)];
- newvd_is_spare = B_TRUE;
- (void) strcpy(newpath, newvd->vdev_path);
- } else {
- (void) snprintf(newpath, sizeof (newpath), ztest_dev_template,
- ztest_opts.zo_dir, ztest_opts.zo_pool,
- top * leaves + leaf);
- if (ztest_random(2) == 0)
- newpath[strlen(newpath) - 1] = 'b';
- newvd = vdev_lookup_by_path(rvd, newpath);
- }
-
- if (newvd) {
- /*
- * Reopen to ensure the vdev's asize field isn't stale.
- */
- vdev_reopen(newvd);
- newsize = vdev_get_min_asize(newvd);
- } else {
- /*
- * Make newsize a little bigger or smaller than oldsize.
- * If it's smaller, the attach should fail.
- * If it's larger, and we're doing a replace,
- * we should get dynamic LUN growth when we're done.
- */
- newsize = 10 * oldsize / (9 + ztest_random(3));
- }
-
- /*
- * If pvd is not a mirror or root, the attach should fail with ENOTSUP,
- * unless it's a replace; in that case any non-replacing parent is OK.
- *
- * If newvd is already part of the pool, it should fail with EBUSY.
- *
- * If newvd is too small, it should fail with EOVERFLOW.
- */
- if (pvd->vdev_ops != &vdev_mirror_ops &&
- pvd->vdev_ops != &vdev_root_ops && (!replacing ||
- pvd->vdev_ops == &vdev_replacing_ops ||
- pvd->vdev_ops == &vdev_spare_ops))
- expected_error = ENOTSUP;
- else if (newvd_is_spare && (!replacing || oldvd_is_log))
- expected_error = ENOTSUP;
- else if (newvd == oldvd)
- expected_error = replacing ? 0 : EBUSY;
- else if (vdev_lookup_by_path(rvd, newpath) != NULL)
- expected_error = EBUSY;
- else if (newsize < oldsize)
- expected_error = EOVERFLOW;
- else if (ashift > oldvd->vdev_top->vdev_ashift)
- expected_error = EDOM;
- else
- expected_error = 0;
-
- spa_config_exit(spa, SCL_ALL, FTAG);
-
- /*
- * Build the nvlist describing newpath.
- */
- root = make_vdev_root(newpath, NULL, NULL, newvd == NULL ? newsize : 0,
- ashift, NULL, 0, 0, 1);
-
- error = spa_vdev_attach(spa, oldguid, root, replacing);
-
- nvlist_free(root);
-
- /*
- * If our parent was the replacing vdev, but the replace completed,
- * then instead of failing with ENOTSUP we may either succeed,
- * fail with ENODEV, or fail with EOVERFLOW.
- */
- if (expected_error == ENOTSUP &&
- (error == 0 || error == ENODEV || error == EOVERFLOW))
- expected_error = error;
-
- /*
- * If someone grew the LUN, the replacement may be too small.
- */
- if (error == EOVERFLOW || error == EBUSY)
- expected_error = error;
-
- if (error == ZFS_ERR_CHECKPOINT_EXISTS ||
- error == ZFS_ERR_DISCARDING_CHECKPOINT)
- expected_error = error;
-
- /* XXX workaround 6690467 */
- if (error != expected_error && expected_error != EBUSY) {
- fatal(0, "attach (%s %llu, %s %llu, %d) "
- "returned %d, expected %d",
- oldpath, oldsize, newpath,
- newsize, replacing, error, expected_error);
- }
-
- mutex_exit(&ztest_vdev_lock);
-}
-
-/* ARGSUSED */
-void
-ztest_device_removal(ztest_ds_t *zd, uint64_t id)
-{
- spa_t *spa = ztest_spa;
- vdev_t *vd;
- uint64_t guid;
- int error;
-
- mutex_enter(&ztest_vdev_lock);
-
- if (ztest_device_removal_active) {
- mutex_exit(&ztest_vdev_lock);
- return;
- }
-
- /*
- * Remove a random top-level vdev and wait for removal to finish.
- */
- spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
- vd = vdev_lookup_top(spa, ztest_random_vdev_top(spa, B_FALSE));
- guid = vd->vdev_guid;
- spa_config_exit(spa, SCL_VDEV, FTAG);
-
- error = spa_vdev_remove(spa, guid, B_FALSE);
- if (error == 0) {
- ztest_device_removal_active = B_TRUE;
- mutex_exit(&ztest_vdev_lock);
-
- while (spa->spa_vdev_removal != NULL)
- txg_wait_synced(spa_get_dsl(spa), 0);
- } else {
- mutex_exit(&ztest_vdev_lock);
- return;
- }
-
- /*
- * The pool needs to be scrubbed after completing device removal.
- * Failure to do so may result in checksum errors due to the
- * strategy employed by ztest_fault_inject() when selecting which
- * offset are redundant and can be damaged.
- */
- error = spa_scan(spa, POOL_SCAN_SCRUB);
- if (error == 0) {
- while (dsl_scan_scrubbing(spa_get_dsl(spa)))
- txg_wait_synced(spa_get_dsl(spa), 0);
- }
-
- mutex_enter(&ztest_vdev_lock);
- ztest_device_removal_active = B_FALSE;
- mutex_exit(&ztest_vdev_lock);
-}
-
-/*
- * Callback function which expands the physical size of the vdev.
- */
-vdev_t *
-grow_vdev(vdev_t *vd, void *arg)
-{
- spa_t *spa = vd->vdev_spa;
- size_t *newsize = arg;
- size_t fsize;
- int fd;
-
- ASSERT(spa_config_held(spa, SCL_STATE, RW_READER) == SCL_STATE);
- ASSERT(vd->vdev_ops->vdev_op_leaf);
-
- if ((fd = open(vd->vdev_path, O_RDWR)) == -1)
- return (vd);
-
- fsize = lseek(fd, 0, SEEK_END);
- (void) ftruncate(fd, *newsize);
-
- if (ztest_opts.zo_verbose >= 6) {
- (void) printf("%s grew from %lu to %lu bytes\n",
- vd->vdev_path, (ulong_t)fsize, (ulong_t)*newsize);
- }
- (void) close(fd);
- return (NULL);
-}
-
-/*
- * Callback function which expands a given vdev by calling vdev_online().
- */
-/* ARGSUSED */
-vdev_t *
-online_vdev(vdev_t *vd, void *arg)
-{
- spa_t *spa = vd->vdev_spa;
- vdev_t *tvd = vd->vdev_top;
- uint64_t guid = vd->vdev_guid;
- uint64_t generation = spa->spa_config_generation + 1;
- vdev_state_t newstate = VDEV_STATE_UNKNOWN;
- int error;
-
- ASSERT(spa_config_held(spa, SCL_STATE, RW_READER) == SCL_STATE);
- ASSERT(vd->vdev_ops->vdev_op_leaf);
-
- /* Calling vdev_online will initialize the new metaslabs */
- spa_config_exit(spa, SCL_STATE, spa);
- error = vdev_online(spa, guid, ZFS_ONLINE_EXPAND, &newstate);
- spa_config_enter(spa, SCL_STATE, spa, RW_READER);
-
- /*
- * If vdev_online returned an error or the underlying vdev_open
- * failed then we abort the expand. The only way to know that
- * vdev_open fails is by checking the returned newstate.
- */
- if (error || newstate != VDEV_STATE_HEALTHY) {
- if (ztest_opts.zo_verbose >= 5) {
- (void) printf("Unable to expand vdev, state %llu, "
- "error %d\n", (u_longlong_t)newstate, error);
- }
- return (vd);
- }
- ASSERT3U(newstate, ==, VDEV_STATE_HEALTHY);
-
- /*
- * Since we dropped the lock we need to ensure that we're
- * still talking to the original vdev. It's possible this
- * vdev may have been detached/replaced while we were
- * trying to online it.
- */
- if (generation != spa->spa_config_generation) {
- if (ztest_opts.zo_verbose >= 5) {
- (void) printf("vdev configuration has changed, "
- "guid %llu, state %llu, expected gen %llu, "
- "got gen %llu\n",
- (u_longlong_t)guid,
- (u_longlong_t)tvd->vdev_state,
- (u_longlong_t)generation,
- (u_longlong_t)spa->spa_config_generation);
- }
- return (vd);
- }
- return (NULL);
-}
-
-/*
- * Traverse the vdev tree calling the supplied function.
- * We continue to walk the tree until we either have walked all
- * children or we receive a non-NULL return from the callback.
- * If a NULL callback is passed, then we just return back the first
- * leaf vdev we encounter.
- */
-vdev_t *
-vdev_walk_tree(vdev_t *vd, vdev_t *(*func)(vdev_t *, void *), void *arg)
-{
- if (vd->vdev_ops->vdev_op_leaf) {
- if (func == NULL)
- return (vd);
- else
- return (func(vd, arg));
- }
-
- for (uint_t c = 0; c < vd->vdev_children; c++) {
- vdev_t *cvd = vd->vdev_child[c];
- if ((cvd = vdev_walk_tree(cvd, func, arg)) != NULL)
- return (cvd);
- }
- return (NULL);
-}
-
-/*
- * Verify that dynamic LUN growth works as expected.
- */
-/* ARGSUSED */
-void
-ztest_vdev_LUN_growth(ztest_ds_t *zd, uint64_t id)
-{
- spa_t *spa = ztest_spa;
- vdev_t *vd, *tvd;
- metaslab_class_t *mc;
- metaslab_group_t *mg;
- size_t psize, newsize;
- uint64_t top;
- uint64_t old_class_space, new_class_space, old_ms_count, new_ms_count;
-
- mutex_enter(&ztest_checkpoint_lock);
- mutex_enter(&ztest_vdev_lock);
- spa_config_enter(spa, SCL_STATE, spa, RW_READER);
-
- /*
- * If there is a vdev removal in progress, it could complete while
- * we are running, in which case we would not be able to verify
- * that the metaslab_class space increased (because it decreases
- * when the device removal completes).
- */
- if (ztest_device_removal_active) {
- spa_config_exit(spa, SCL_STATE, spa);
- mutex_exit(&ztest_vdev_lock);
- mutex_exit(&ztest_checkpoint_lock);
- return;
- }
-
- top = ztest_random_vdev_top(spa, B_TRUE);
-
- tvd = spa->spa_root_vdev->vdev_child[top];
- mg = tvd->vdev_mg;
- mc = mg->mg_class;
- old_ms_count = tvd->vdev_ms_count;
- old_class_space = metaslab_class_get_space(mc);
-
- /*
- * Determine the size of the first leaf vdev associated with
- * our top-level device.
- */
- vd = vdev_walk_tree(tvd, NULL, NULL);
- ASSERT3P(vd, !=, NULL);
- ASSERT(vd->vdev_ops->vdev_op_leaf);
-
- psize = vd->vdev_psize;
-
- /*
- * We only try to expand the vdev if it's healthy, less than 4x its
- * original size, and it has a valid psize.
- */
- if (tvd->vdev_state != VDEV_STATE_HEALTHY ||
- psize == 0 || psize >= 4 * ztest_opts.zo_vdev_size) {
- spa_config_exit(spa, SCL_STATE, spa);
- mutex_exit(&ztest_vdev_lock);
- mutex_exit(&ztest_checkpoint_lock);
- return;
- }
- ASSERT(psize > 0);
- newsize = psize + MAX(psize / 8, SPA_MAXBLOCKSIZE);
- ASSERT3U(newsize, >, psize);
-
- if (ztest_opts.zo_verbose >= 6) {
- (void) printf("Expanding LUN %s from %lu to %lu\n",
- vd->vdev_path, (ulong_t)psize, (ulong_t)newsize);
- }
-
- /*
- * Growing the vdev is a two step process:
- * 1). expand the physical size (i.e. relabel)
- * 2). online the vdev to create the new metaslabs
- */
- if (vdev_walk_tree(tvd, grow_vdev, &newsize) != NULL ||
- vdev_walk_tree(tvd, online_vdev, NULL) != NULL ||
- tvd->vdev_state != VDEV_STATE_HEALTHY) {
- if (ztest_opts.zo_verbose >= 5) {
- (void) printf("Could not expand LUN because "
- "the vdev configuration changed.\n");
- }
- spa_config_exit(spa, SCL_STATE, spa);
- mutex_exit(&ztest_vdev_lock);
- mutex_exit(&ztest_checkpoint_lock);
- return;
- }
-
- spa_config_exit(spa, SCL_STATE, spa);
-
- /*
- * Expanding the LUN will update the config asynchronously,
- * thus we must wait for the async thread to complete any
- * pending tasks before proceeding.
- */
- for (;;) {
- boolean_t done;
- mutex_enter(&spa->spa_async_lock);
- done = (spa->spa_async_thread == NULL && !spa->spa_async_tasks);
- mutex_exit(&spa->spa_async_lock);
- if (done)
- break;
- txg_wait_synced(spa_get_dsl(spa), 0);
- (void) poll(NULL, 0, 100);
- }
-
- spa_config_enter(spa, SCL_STATE, spa, RW_READER);
-
- tvd = spa->spa_root_vdev->vdev_child[top];
- new_ms_count = tvd->vdev_ms_count;
- new_class_space = metaslab_class_get_space(mc);
-
- if (tvd->vdev_mg != mg || mg->mg_class != mc) {
- if (ztest_opts.zo_verbose >= 5) {
- (void) printf("Could not verify LUN expansion due to "
- "intervening vdev offline or remove.\n");
- }
- spa_config_exit(spa, SCL_STATE, spa);
- mutex_exit(&ztest_vdev_lock);
- mutex_exit(&ztest_checkpoint_lock);
- return;
- }
-
- /*
- * Make sure we were able to grow the vdev.
- */
- if (new_ms_count <= old_ms_count) {
- fatal(0, "LUN expansion failed: ms_count %llu < %llu\n",
- old_ms_count, new_ms_count);
- }
-
- /*
- * Make sure we were able to grow the pool.
- */
- if (new_class_space <= old_class_space) {
- fatal(0, "LUN expansion failed: class_space %llu < %llu\n",
- old_class_space, new_class_space);
- }
-
- if (ztest_opts.zo_verbose >= 5) {
- char oldnumbuf[NN_NUMBUF_SZ], newnumbuf[NN_NUMBUF_SZ];
-
- nicenum(old_class_space, oldnumbuf, sizeof (oldnumbuf));
- nicenum(new_class_space, newnumbuf, sizeof (newnumbuf));
- (void) printf("%s grew from %s to %s\n",
- spa->spa_name, oldnumbuf, newnumbuf);
- }
-
- spa_config_exit(spa, SCL_STATE, spa);
- mutex_exit(&ztest_vdev_lock);
- mutex_exit(&ztest_checkpoint_lock);
-}
-
-/*
- * Verify that dmu_objset_{create,destroy,open,close} work as expected.
- */
-/* ARGSUSED */
-static void
-ztest_objset_create_cb(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx)
-{
- /*
- * Create the objects common to all ztest datasets.
- */
- VERIFY(zap_create_claim(os, ZTEST_DIROBJ,
- DMU_OT_ZAP_OTHER, DMU_OT_NONE, 0, tx) == 0);
-}
-
-static int
-ztest_dataset_create(char *dsname)
-{
- uint64_t zilset = ztest_random(100);
- int err = dmu_objset_create(dsname, DMU_OST_OTHER, 0,
- ztest_objset_create_cb, NULL);
-
- if (err || zilset < 80)
- return (err);
-
- if (ztest_opts.zo_verbose >= 6)
- (void) printf("Setting dataset %s to sync always\n", dsname);
- return (ztest_dsl_prop_set_uint64(dsname, ZFS_PROP_SYNC,
- ZFS_SYNC_ALWAYS, B_FALSE));
-}
-
-/* ARGSUSED */
-static int
-ztest_objset_destroy_cb(const char *name, void *arg)
-{
- objset_t *os;
- dmu_object_info_t doi;
- int error;
-
- /*
- * Verify that the dataset contains a directory object.
- */
- VERIFY0(dmu_objset_own(name, DMU_OST_OTHER, B_TRUE, FTAG, &os));
- error = dmu_object_info(os, ZTEST_DIROBJ, &doi);
- if (error != ENOENT) {
- /* We could have crashed in the middle of destroying it */
- ASSERT0(error);
- ASSERT3U(doi.doi_type, ==, DMU_OT_ZAP_OTHER);
- ASSERT3S(doi.doi_physical_blocks_512, >=, 0);
- }
- dmu_objset_disown(os, FTAG);
-
- /*
- * Destroy the dataset.
- */
- if (strchr(name, '@') != NULL) {
- VERIFY0(dsl_destroy_snapshot(name, B_FALSE));
- } else {
- VERIFY0(dsl_destroy_head(name));
- }
- return (0);
-}
-
-static boolean_t
-ztest_snapshot_create(char *osname, uint64_t id)
-{
- char snapname[ZFS_MAX_DATASET_NAME_LEN];
- int error;
-
- (void) snprintf(snapname, sizeof (snapname), "%llu", (u_longlong_t)id);
-
- error = dmu_objset_snapshot_one(osname, snapname);
- if (error == ENOSPC) {
- ztest_record_enospc(FTAG);
- return (B_FALSE);
- }
- if (error != 0 && error != EEXIST) {
- fatal(0, "ztest_snapshot_create(%s@%s) = %d", osname,
- snapname, error);
- }
- return (B_TRUE);
-}
-
-static boolean_t
-ztest_snapshot_destroy(char *osname, uint64_t id)
-{
- char snapname[ZFS_MAX_DATASET_NAME_LEN];
- int error;
-
- (void) snprintf(snapname, sizeof (snapname), "%s@%llu", osname,
- (u_longlong_t)id);
-
- error = dsl_destroy_snapshot(snapname, B_FALSE);
- if (error != 0 && error != ENOENT)
- fatal(0, "ztest_snapshot_destroy(%s) = %d", snapname, error);
- return (B_TRUE);
-}
-
-/* ARGSUSED */
-void
-ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id)
-{
- ztest_ds_t zdtmp;
- int iters;
- int error;
- objset_t *os, *os2;
- char name[ZFS_MAX_DATASET_NAME_LEN];
- zilog_t *zilog;
-
- rw_enter(&ztest_name_lock, RW_READER);
-
- (void) snprintf(name, sizeof (name), "%s/temp_%llu",
- ztest_opts.zo_pool, (u_longlong_t)id);
-
- /*
- * If this dataset exists from a previous run, process its replay log
- * half of the time. If we don't replay it, then dmu_objset_destroy()
- * (invoked from ztest_objset_destroy_cb()) should just throw it away.
- */
- if (ztest_random(2) == 0 &&
- dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, FTAG, &os) == 0) {
- ztest_zd_init(&zdtmp, NULL, os);
- zil_replay(os, &zdtmp, ztest_replay_vector);
- ztest_zd_fini(&zdtmp);
- dmu_objset_disown(os, FTAG);
- }
-
- /*
- * There may be an old instance of the dataset we're about to
- * create lying around from a previous run. If so, destroy it
- * and all of its snapshots.
- */
- (void) dmu_objset_find(name, ztest_objset_destroy_cb, NULL,
- DS_FIND_CHILDREN | DS_FIND_SNAPSHOTS);
-
- /*
- * Verify that the destroyed dataset is no longer in the namespace.
- */
- VERIFY3U(ENOENT, ==, dmu_objset_own(name, DMU_OST_OTHER, B_TRUE,
- FTAG, &os));
-
- /*
- * Verify that we can create a new dataset.
- */
- error = ztest_dataset_create(name);
- if (error) {
- if (error == ENOSPC) {
- ztest_record_enospc(FTAG);
- rw_exit(&ztest_name_lock);
- return;
- }
- fatal(0, "dmu_objset_create(%s) = %d", name, error);
- }
-
- VERIFY0(dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, FTAG, &os));
-
- ztest_zd_init(&zdtmp, NULL, os);
-
- /*
- * Open the intent log for it.
- */
- zilog = zil_open(os, ztest_get_data);
-
- /*
- * Put some objects in there, do a little I/O to them,
- * and randomly take a couple of snapshots along the way.
- */
- iters = ztest_random(5);
- for (int i = 0; i < iters; i++) {
- ztest_dmu_object_alloc_free(&zdtmp, id);
- if (ztest_random(iters) == 0)
- (void) ztest_snapshot_create(name, i);
- }
-
- /*
- * Verify that we cannot create an existing dataset.
- */
- VERIFY3U(EEXIST, ==,
- dmu_objset_create(name, DMU_OST_OTHER, 0, NULL, NULL));
-
- /*
- * Verify that we can hold an objset that is also owned.
- */
- VERIFY3U(0, ==, dmu_objset_hold(name, FTAG, &os2));
- dmu_objset_rele(os2, FTAG);
-
- /*
- * Verify that we cannot own an objset that is already owned.
- */
- VERIFY3U(EBUSY, ==,
- dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, FTAG, &os2));
-
- zil_close(zilog);
- dmu_objset_disown(os, FTAG);
- ztest_zd_fini(&zdtmp);
-
- rw_exit(&ztest_name_lock);
-}
-
-/*
- * Verify that dmu_snapshot_{create,destroy,open,close} work as expected.
- */
-void
-ztest_dmu_snapshot_create_destroy(ztest_ds_t *zd, uint64_t id)
-{
- rw_enter(&ztest_name_lock, RW_READER);
- (void) ztest_snapshot_destroy(zd->zd_name, id);
- (void) ztest_snapshot_create(zd->zd_name, id);
- rw_exit(&ztest_name_lock);
-}
-
-/*
- * Cleanup non-standard snapshots and clones.
- */
-void
-ztest_dsl_dataset_cleanup(char *osname, uint64_t id)
-{
- char snap1name[ZFS_MAX_DATASET_NAME_LEN];
- char clone1name[ZFS_MAX_DATASET_NAME_LEN];
- char snap2name[ZFS_MAX_DATASET_NAME_LEN];
- char clone2name[ZFS_MAX_DATASET_NAME_LEN];
- char snap3name[ZFS_MAX_DATASET_NAME_LEN];
- int error;
-
- (void) snprintf(snap1name, sizeof (snap1name),
- "%s@s1_%llu", osname, id);
- (void) snprintf(clone1name, sizeof (clone1name),
- "%s/c1_%llu", osname, id);
- (void) snprintf(snap2name, sizeof (snap2name),
- "%s@s2_%llu", clone1name, id);
- (void) snprintf(clone2name, sizeof (clone2name),
- "%s/c2_%llu", osname, id);
- (void) snprintf(snap3name, sizeof (snap3name),
- "%s@s3_%llu", clone1name, id);
-
- error = dsl_destroy_head(clone2name);
- if (error && error != ENOENT)
- fatal(0, "dsl_destroy_head(%s) = %d", clone2name, error);
- error = dsl_destroy_snapshot(snap3name, B_FALSE);
- if (error && error != ENOENT)
- fatal(0, "dsl_destroy_snapshot(%s) = %d", snap3name, error);
- error = dsl_destroy_snapshot(snap2name, B_FALSE);
- if (error && error != ENOENT)
- fatal(0, "dsl_destroy_snapshot(%s) = %d", snap2name, error);
- error = dsl_destroy_head(clone1name);
- if (error && error != ENOENT)
- fatal(0, "dsl_destroy_head(%s) = %d", clone1name, error);
- error = dsl_destroy_snapshot(snap1name, B_FALSE);
- if (error && error != ENOENT)
- fatal(0, "dsl_destroy_snapshot(%s) = %d", snap1name, error);
-}
-
-/*
- * Verify dsl_dataset_promote handles EBUSY
- */
-void
-ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id)
-{
- objset_t *os;
- char snap1name[ZFS_MAX_DATASET_NAME_LEN];
- char clone1name[ZFS_MAX_DATASET_NAME_LEN];
- char snap2name[ZFS_MAX_DATASET_NAME_LEN];
- char clone2name[ZFS_MAX_DATASET_NAME_LEN];
- char snap3name[ZFS_MAX_DATASET_NAME_LEN];
- char *osname = zd->zd_name;
- int error;
-
- rw_enter(&ztest_name_lock, RW_READER);
-
- ztest_dsl_dataset_cleanup(osname, id);
-
- (void) snprintf(snap1name, sizeof (snap1name),
- "%s@s1_%llu", osname, id);
- (void) snprintf(clone1name, sizeof (clone1name),
- "%s/c1_%llu", osname, id);
- (void) snprintf(snap2name, sizeof (snap2name),
- "%s@s2_%llu", clone1name, id);
- (void) snprintf(clone2name, sizeof (clone2name),
- "%s/c2_%llu", osname, id);
- (void) snprintf(snap3name, sizeof (snap3name),
- "%s@s3_%llu", clone1name, id);
-
- error = dmu_objset_snapshot_one(osname, strchr(snap1name, '@') + 1);
- if (error && error != EEXIST) {
- if (error == ENOSPC) {
- ztest_record_enospc(FTAG);
- goto out;
- }
- fatal(0, "dmu_take_snapshot(%s) = %d", snap1name, error);
- }
-
- error = dmu_objset_clone(clone1name, snap1name);
- if (error) {
- if (error == ENOSPC) {
- ztest_record_enospc(FTAG);
- goto out;
- }
- fatal(0, "dmu_objset_create(%s) = %d", clone1name, error);
- }
-
- error = dmu_objset_snapshot_one(clone1name, strchr(snap2name, '@') + 1);
- if (error && error != EEXIST) {
- if (error == ENOSPC) {
- ztest_record_enospc(FTAG);
- goto out;
- }
- fatal(0, "dmu_open_snapshot(%s) = %d", snap2name, error);
- }
-
- error = dmu_objset_snapshot_one(clone1name, strchr(snap3name, '@') + 1);
- if (error && error != EEXIST) {
- if (error == ENOSPC) {
- ztest_record_enospc(FTAG);
- goto out;
- }
- fatal(0, "dmu_open_snapshot(%s) = %d", snap3name, error);
- }
-
- error = dmu_objset_clone(clone2name, snap3name);
- if (error) {
- if (error == ENOSPC) {
- ztest_record_enospc(FTAG);
- goto out;
- }
- fatal(0, "dmu_objset_create(%s) = %d", clone2name, error);
- }
-
- error = dmu_objset_own(snap2name, DMU_OST_ANY, B_TRUE, FTAG, &os);
- if (error)
- fatal(0, "dmu_objset_own(%s) = %d", snap2name, error);
- error = dsl_dataset_promote(clone2name, NULL);
- if (error == ENOSPC) {
- dmu_objset_disown(os, FTAG);
- ztest_record_enospc(FTAG);
- goto out;
- }
- if (error != EBUSY)
- fatal(0, "dsl_dataset_promote(%s), %d, not EBUSY", clone2name,
- error);
- dmu_objset_disown(os, FTAG);
-
-out:
- ztest_dsl_dataset_cleanup(osname, id);
-
- rw_exit(&ztest_name_lock);
-}
-
-/*
- * Verify that dmu_object_{alloc,free} work as expected.
- */
-void
-ztest_dmu_object_alloc_free(ztest_ds_t *zd, uint64_t id)
-{
- ztest_od_t od[4];
- int batchsize = sizeof (od) / sizeof (od[0]);
-
- for (int b = 0; b < batchsize; b++) {
- ztest_od_init(&od[b], id, FTAG, b, DMU_OT_UINT64_OTHER,
- 0, 0, 0);
- }
-
- /*
- * Destroy the previous batch of objects, create a new batch,
- * and do some I/O on the new objects.
- */
- if (ztest_object_init(zd, od, sizeof (od), B_TRUE) != 0)
- return;
-
- while (ztest_random(4 * batchsize) != 0)
- ztest_io(zd, od[ztest_random(batchsize)].od_object,
- ztest_random(ZTEST_RANGE_LOCKS) << SPA_MAXBLOCKSHIFT);
-}
-
-/*
- * Rewind the global allocator to verify object allocation backfilling.
- */
-void
-ztest_dmu_object_next_chunk(ztest_ds_t *zd, uint64_t id)
-{
- objset_t *os = zd->zd_os;
- int dnodes_per_chunk = 1 << dmu_object_alloc_chunk_shift;
- uint64_t object;
-
- /*
- * Rewind the global allocator randomly back to a lower object number
- * to force backfilling and reclamation of recently freed dnodes.
- */
- mutex_enter(&os->os_obj_lock);
- object = ztest_random(os->os_obj_next_chunk);
- os->os_obj_next_chunk = P2ALIGN(object, dnodes_per_chunk);
- mutex_exit(&os->os_obj_lock);
-}
-
-/*
- * Verify that dmu_{read,write} work as expected.
- */
-void
-ztest_dmu_read_write(ztest_ds_t *zd, uint64_t id)
-{
- objset_t *os = zd->zd_os;
- ztest_od_t od[2];
- dmu_tx_t *tx;
- int i, freeit, error;
- uint64_t n, s, txg;
- bufwad_t *packbuf, *bigbuf, *pack, *bigH, *bigT;
- uint64_t packobj, packoff, packsize, bigobj, bigoff, bigsize;
- uint64_t chunksize = (1000 + ztest_random(1000)) * sizeof (uint64_t);
- uint64_t regions = 997;
- uint64_t stride = 123456789ULL;
- uint64_t width = 40;
- int free_percent = 5;
-
- /*
- * This test uses two objects, packobj and bigobj, that are always
- * updated together (i.e. in the same tx) so that their contents are
- * in sync and can be compared. Their contents relate to each other
- * in a simple way: packobj is a dense array of 'bufwad' structures,
- * while bigobj is a sparse array of the same bufwads. Specifically,
- * for any index n, there are three bufwads that should be identical:
- *
- * packobj, at offset n * sizeof (bufwad_t)
- * bigobj, at the head of the nth chunk
- * bigobj, at the tail of the nth chunk
- *
- * The chunk size is arbitrary. It doesn't have to be a power of two,
- * and it doesn't have any relation to the object blocksize.
- * The only requirement is that it can hold at least two bufwads.
- *
- * Normally, we write the bufwad to each of these locations.
- * However, free_percent of the time we instead write zeroes to
- * packobj and perform a dmu_free_range() on bigobj. By comparing
- * bigobj to packobj, we can verify that the DMU is correctly
- * tracking which parts of an object are allocated and free,
- * and that the contents of the allocated blocks are correct.
- */
-
- /*
- * Read the directory info. If it's the first time, set things up.
- */
- ztest_od_init(&od[0], id, FTAG, 0, DMU_OT_UINT64_OTHER, 0, 0,
- chunksize);
- ztest_od_init(&od[1], id, FTAG, 1, DMU_OT_UINT64_OTHER, 0, 0,
- chunksize);
-
- if (ztest_object_init(zd, od, sizeof (od), B_FALSE) != 0)
- return;
-
- bigobj = od[0].od_object;
- packobj = od[1].od_object;
- chunksize = od[0].od_gen;
- ASSERT(chunksize == od[1].od_gen);
-
- /*
- * Prefetch a random chunk of the big object.
- * Our aim here is to get some async reads in flight
- * for blocks that we may free below; the DMU should
- * handle this race correctly.
- */
- n = ztest_random(regions) * stride + ztest_random(width);
- s = 1 + ztest_random(2 * width - 1);
- dmu_prefetch(os, bigobj, 0, n * chunksize, s * chunksize,
- ZIO_PRIORITY_SYNC_READ);
-
- /*
- * Pick a random index and compute the offsets into packobj and bigobj.
- */
- n = ztest_random(regions) * stride + ztest_random(width);
- s = 1 + ztest_random(width - 1);
-
- packoff = n * sizeof (bufwad_t);
- packsize = s * sizeof (bufwad_t);
-
- bigoff = n * chunksize;
- bigsize = s * chunksize;
-
- packbuf = umem_alloc(packsize, UMEM_NOFAIL);
- bigbuf = umem_alloc(bigsize, UMEM_NOFAIL);
-
- /*
- * free_percent of the time, free a range of bigobj rather than
- * overwriting it.
- */
- freeit = (ztest_random(100) < free_percent);
-
- /*
- * Read the current contents of our objects.
- */
- error = dmu_read(os, packobj, packoff, packsize, packbuf,
- DMU_READ_PREFETCH);
- ASSERT0(error);
- error = dmu_read(os, bigobj, bigoff, bigsize, bigbuf,
- DMU_READ_PREFETCH);
- ASSERT0(error);
-
- /*
- * Get a tx for the mods to both packobj and bigobj.
- */
- tx = dmu_tx_create(os);
-
- dmu_tx_hold_write(tx, packobj, packoff, packsize);
-
- if (freeit)
- dmu_tx_hold_free(tx, bigobj, bigoff, bigsize);
- else
- dmu_tx_hold_write(tx, bigobj, bigoff, bigsize);
-
- /* This accounts for setting the checksum/compression. */
- dmu_tx_hold_bonus(tx, bigobj);
-
- txg = ztest_tx_assign(tx, TXG_MIGHTWAIT, FTAG);
- if (txg == 0) {
- umem_free(packbuf, packsize);
- umem_free(bigbuf, bigsize);
- return;
- }
-
- enum zio_checksum cksum;
- do {
- cksum = (enum zio_checksum)
- ztest_random_dsl_prop(ZFS_PROP_CHECKSUM);
- } while (cksum >= ZIO_CHECKSUM_LEGACY_FUNCTIONS);
- dmu_object_set_checksum(os, bigobj, cksum, tx);
-
- enum zio_compress comp;
- do {
- comp = (enum zio_compress)
- ztest_random_dsl_prop(ZFS_PROP_COMPRESSION);
- } while (comp >= ZIO_COMPRESS_LEGACY_FUNCTIONS);
- dmu_object_set_compress(os, bigobj, comp, tx);
-
- /*
- * For each index from n to n + s, verify that the existing bufwad
- * in packobj matches the bufwads at the head and tail of the
- * corresponding chunk in bigobj. Then update all three bufwads
- * with the new values we want to write out.
- */
- for (i = 0; i < s; i++) {
- /* LINTED */
- pack = (bufwad_t *)((char *)packbuf + i * sizeof (bufwad_t));
- /* LINTED */
- bigH = (bufwad_t *)((char *)bigbuf + i * chunksize);
- /* LINTED */
- bigT = (bufwad_t *)((char *)bigH + chunksize) - 1;
-
- ASSERT((uintptr_t)bigH - (uintptr_t)bigbuf < bigsize);
- ASSERT((uintptr_t)bigT - (uintptr_t)bigbuf < bigsize);
-
- if (pack->bw_txg > txg)
- fatal(0, "future leak: got %llx, open txg is %llx",
- pack->bw_txg, txg);
-
- if (pack->bw_data != 0 && pack->bw_index != n + i)
- fatal(0, "wrong index: got %llx, wanted %llx+%llx",
- pack->bw_index, n, i);
-
- if (bcmp(pack, bigH, sizeof (bufwad_t)) != 0)
- fatal(0, "pack/bigH mismatch in %p/%p", pack, bigH);
-
- if (bcmp(pack, bigT, sizeof (bufwad_t)) != 0)
- fatal(0, "pack/bigT mismatch in %p/%p", pack, bigT);
-
- if (freeit) {
- bzero(pack, sizeof (bufwad_t));
- } else {
- pack->bw_index = n + i;
- pack->bw_txg = txg;
- pack->bw_data = 1 + ztest_random(-2ULL);
- }
- *bigH = *pack;
- *bigT = *pack;
- }
-
- /*
- * We've verified all the old bufwads, and made new ones.
- * Now write them out.
- */
- dmu_write(os, packobj, packoff, packsize, packbuf, tx);
-
- if (freeit) {
- if (ztest_opts.zo_verbose >= 7) {
- (void) printf("freeing offset %llx size %llx"
- " txg %llx\n",
- (u_longlong_t)bigoff,
- (u_longlong_t)bigsize,
- (u_longlong_t)txg);
- }
- VERIFY(0 == dmu_free_range(os, bigobj, bigoff, bigsize, tx));
- } else {
- if (ztest_opts.zo_verbose >= 7) {
- (void) printf("writing offset %llx size %llx"
- " txg %llx\n",
- (u_longlong_t)bigoff,
- (u_longlong_t)bigsize,
- (u_longlong_t)txg);
- }
- dmu_write(os, bigobj, bigoff, bigsize, bigbuf, tx);
- }
-
- dmu_tx_commit(tx);
-
- /*
- * Sanity check the stuff we just wrote.
- */
- {
- void *packcheck = umem_alloc(packsize, UMEM_NOFAIL);
- void *bigcheck = umem_alloc(bigsize, UMEM_NOFAIL);
-
- VERIFY(0 == dmu_read(os, packobj, packoff,
- packsize, packcheck, DMU_READ_PREFETCH));
- VERIFY(0 == dmu_read(os, bigobj, bigoff,
- bigsize, bigcheck, DMU_READ_PREFETCH));
-
- ASSERT(bcmp(packbuf, packcheck, packsize) == 0);
- ASSERT(bcmp(bigbuf, bigcheck, bigsize) == 0);
-
- umem_free(packcheck, packsize);
- umem_free(bigcheck, bigsize);
- }
-
- umem_free(packbuf, packsize);
- umem_free(bigbuf, bigsize);
-}
-
-void
-compare_and_update_pbbufs(uint64_t s, bufwad_t *packbuf, bufwad_t *bigbuf,
- uint64_t bigsize, uint64_t n, uint64_t chunksize, uint64_t txg)
-{
- uint64_t i;
- bufwad_t *pack;
- bufwad_t *bigH;
- bufwad_t *bigT;
-
- /*
- * For each index from n to n + s, verify that the existing bufwad
- * in packobj matches the bufwads at the head and tail of the
- * corresponding chunk in bigobj. Then update all three bufwads
- * with the new values we want to write out.
- */
- for (i = 0; i < s; i++) {
- /* LINTED */
- pack = (bufwad_t *)((char *)packbuf + i * sizeof (bufwad_t));
- /* LINTED */
- bigH = (bufwad_t *)((char *)bigbuf + i * chunksize);
- /* LINTED */
- bigT = (bufwad_t *)((char *)bigH + chunksize) - 1;
-
- ASSERT((uintptr_t)bigH - (uintptr_t)bigbuf < bigsize);
- ASSERT((uintptr_t)bigT - (uintptr_t)bigbuf < bigsize);
-
- if (pack->bw_txg > txg)
- fatal(0, "future leak: got %llx, open txg is %llx",
- pack->bw_txg, txg);
-
- if (pack->bw_data != 0 && pack->bw_index != n + i)
- fatal(0, "wrong index: got %llx, wanted %llx+%llx",
- pack->bw_index, n, i);
-
- if (bcmp(pack, bigH, sizeof (bufwad_t)) != 0)
- fatal(0, "pack/bigH mismatch in %p/%p", pack, bigH);
-
- if (bcmp(pack, bigT, sizeof (bufwad_t)) != 0)
- fatal(0, "pack/bigT mismatch in %p/%p", pack, bigT);
-
- pack->bw_index = n + i;
- pack->bw_txg = txg;
- pack->bw_data = 1 + ztest_random(-2ULL);
-
- *bigH = *pack;
- *bigT = *pack;
- }
-}
-
-void
-ztest_dmu_read_write_zcopy(ztest_ds_t *zd, uint64_t id)
-{
- objset_t *os = zd->zd_os;
- ztest_od_t od[2];
- dmu_tx_t *tx;
- uint64_t i;
- int error;
- uint64_t n, s, txg;
- bufwad_t *packbuf, *bigbuf;
- uint64_t packobj, packoff, packsize, bigobj, bigoff, bigsize;
- uint64_t blocksize = ztest_random_blocksize();
- uint64_t chunksize = blocksize;
- uint64_t regions = 997;
- uint64_t stride = 123456789ULL;
- uint64_t width = 9;
- dmu_buf_t *bonus_db;
- arc_buf_t **bigbuf_arcbufs;
- dmu_object_info_t doi;
-
- /*
- * This test uses two objects, packobj and bigobj, that are always
- * updated together (i.e. in the same tx) so that their contents are
- * in sync and can be compared. Their contents relate to each other
- * in a simple way: packobj is a dense array of 'bufwad' structures,
- * while bigobj is a sparse array of the same bufwads. Specifically,
- * for any index n, there are three bufwads that should be identical:
- *
- * packobj, at offset n * sizeof (bufwad_t)
- * bigobj, at the head of the nth chunk
- * bigobj, at the tail of the nth chunk
- *
- * The chunk size is set equal to bigobj block size so that
- * dmu_assign_arcbuf() can be tested for object updates.
- */
-
- /*
- * Read the directory info. If it's the first time, set things up.
- */
- ztest_od_init(&od[0], id, FTAG, 0, DMU_OT_UINT64_OTHER, blocksize,
- 0, 0);
- ztest_od_init(&od[1], id, FTAG, 1, DMU_OT_UINT64_OTHER, 0, 0,
- chunksize);
-
- if (ztest_object_init(zd, od, sizeof (od), B_FALSE) != 0)
- return;
-
- bigobj = od[0].od_object;
- packobj = od[1].od_object;
- blocksize = od[0].od_blocksize;
- chunksize = blocksize;
- ASSERT(chunksize == od[1].od_gen);
-
- VERIFY(dmu_object_info(os, bigobj, &doi) == 0);
- VERIFY(ISP2(doi.doi_data_block_size));
- VERIFY(chunksize == doi.doi_data_block_size);
- VERIFY(chunksize >= 2 * sizeof (bufwad_t));
-
- /*
- * Pick a random index and compute the offsets into packobj and bigobj.
- */
- n = ztest_random(regions) * stride + ztest_random(width);
- s = 1 + ztest_random(width - 1);
-
- packoff = n * sizeof (bufwad_t);
- packsize = s * sizeof (bufwad_t);
-
- bigoff = n * chunksize;
- bigsize = s * chunksize;
-
- packbuf = umem_zalloc(packsize, UMEM_NOFAIL);
- bigbuf = umem_zalloc(bigsize, UMEM_NOFAIL);
-
- VERIFY3U(0, ==, dmu_bonus_hold(os, bigobj, FTAG, &bonus_db));
-
- bigbuf_arcbufs = umem_zalloc(2 * s * sizeof (arc_buf_t *), UMEM_NOFAIL);
-
- /*
- * Iteration 0 test zcopy for DB_UNCACHED dbufs.
- * Iteration 1 test zcopy to already referenced dbufs.
- * Iteration 2 test zcopy to dirty dbuf in the same txg.
- * Iteration 3 test zcopy to dbuf dirty in previous txg.
- * Iteration 4 test zcopy when dbuf is no longer dirty.
- * Iteration 5 test zcopy when it can't be done.
- * Iteration 6 one more zcopy write.
- */
- for (i = 0; i < 7; i++) {
- uint64_t j;
- uint64_t off;
-
- /*
- * In iteration 5 (i == 5) use arcbufs
- * that don't match bigobj blksz to test
- * dmu_assign_arcbuf() when it can't directly
- * assign an arcbuf to a dbuf.
- */
- for (j = 0; j < s; j++) {
- if (i != 5) {
- bigbuf_arcbufs[j] =
- dmu_request_arcbuf(bonus_db, chunksize);
- } else {
- bigbuf_arcbufs[2 * j] =
- dmu_request_arcbuf(bonus_db, chunksize / 2);
- bigbuf_arcbufs[2 * j + 1] =
- dmu_request_arcbuf(bonus_db, chunksize / 2);
- }
- }
-
- /*
- * Get a tx for the mods to both packobj and bigobj.
- */
- tx = dmu_tx_create(os);
-
- dmu_tx_hold_write(tx, packobj, packoff, packsize);
- dmu_tx_hold_write(tx, bigobj, bigoff, bigsize);
-
- txg = ztest_tx_assign(tx, TXG_MIGHTWAIT, FTAG);
- if (txg == 0) {
- umem_free(packbuf, packsize);
- umem_free(bigbuf, bigsize);
- for (j = 0; j < s; j++) {
- if (i != 5) {
- dmu_return_arcbuf(bigbuf_arcbufs[j]);
- } else {
- dmu_return_arcbuf(
- bigbuf_arcbufs[2 * j]);
- dmu_return_arcbuf(
- bigbuf_arcbufs[2 * j + 1]);
- }
- }
- umem_free(bigbuf_arcbufs, 2 * s * sizeof (arc_buf_t *));
- dmu_buf_rele(bonus_db, FTAG);
- return;
- }
-
- /*
- * 50% of the time don't read objects in the 1st iteration to
- * test dmu_assign_arcbuf() for the case when there're no
- * existing dbufs for the specified offsets.
- */
- if (i != 0 || ztest_random(2) != 0) {
- error = dmu_read(os, packobj, packoff,
- packsize, packbuf, DMU_READ_PREFETCH);
- ASSERT0(error);
- error = dmu_read(os, bigobj, bigoff, bigsize,
- bigbuf, DMU_READ_PREFETCH);
- ASSERT0(error);
- }
- compare_and_update_pbbufs(s, packbuf, bigbuf, bigsize,
- n, chunksize, txg);
-
- /*
- * We've verified all the old bufwads, and made new ones.
- * Now write them out.
- */
- dmu_write(os, packobj, packoff, packsize, packbuf, tx);
- if (ztest_opts.zo_verbose >= 7) {
- (void) printf("writing offset %llx size %llx"
- " txg %llx\n",
- (u_longlong_t)bigoff,
- (u_longlong_t)bigsize,
- (u_longlong_t)txg);
- }
- for (off = bigoff, j = 0; j < s; j++, off += chunksize) {
- dmu_buf_t *dbt;
- if (i != 5) {
- bcopy((caddr_t)bigbuf + (off - bigoff),
- bigbuf_arcbufs[j]->b_data, chunksize);
- } else {
- bcopy((caddr_t)bigbuf + (off - bigoff),
- bigbuf_arcbufs[2 * j]->b_data,
- chunksize / 2);
- bcopy((caddr_t)bigbuf + (off - bigoff) +
- chunksize / 2,
- bigbuf_arcbufs[2 * j + 1]->b_data,
- chunksize / 2);
- }
-
- if (i == 1) {
- VERIFY(dmu_buf_hold(os, bigobj, off,
- FTAG, &dbt, DMU_READ_NO_PREFETCH) == 0);
- }
- if (i != 5) {
- dmu_assign_arcbuf(bonus_db, off,
- bigbuf_arcbufs[j], tx);
- } else {
- dmu_assign_arcbuf(bonus_db, off,
- bigbuf_arcbufs[2 * j], tx);
- dmu_assign_arcbuf(bonus_db,
- off + chunksize / 2,
- bigbuf_arcbufs[2 * j + 1], tx);
- }
- if (i == 1) {
- dmu_buf_rele(dbt, FTAG);
- }
- }
- dmu_tx_commit(tx);
-
- /*
- * Sanity check the stuff we just wrote.
- */
- {
- void *packcheck = umem_alloc(packsize, UMEM_NOFAIL);
- void *bigcheck = umem_alloc(bigsize, UMEM_NOFAIL);
-
- VERIFY(0 == dmu_read(os, packobj, packoff,
- packsize, packcheck, DMU_READ_PREFETCH));
- VERIFY(0 == dmu_read(os, bigobj, bigoff,
- bigsize, bigcheck, DMU_READ_PREFETCH));
-
- ASSERT(bcmp(packbuf, packcheck, packsize) == 0);
- ASSERT(bcmp(bigbuf, bigcheck, bigsize) == 0);
-
- umem_free(packcheck, packsize);
- umem_free(bigcheck, bigsize);
- }
- if (i == 2) {
- txg_wait_open(dmu_objset_pool(os), 0);
- } else if (i == 3) {
- txg_wait_synced(dmu_objset_pool(os), 0);
- }
- }
-
- dmu_buf_rele(bonus_db, FTAG);
- umem_free(packbuf, packsize);
- umem_free(bigbuf, bigsize);
- umem_free(bigbuf_arcbufs, 2 * s * sizeof (arc_buf_t *));
-}
-
-/* ARGSUSED */
-void
-ztest_dmu_write_parallel(ztest_ds_t *zd, uint64_t id)
-{
- ztest_od_t od[1];
- uint64_t offset = (1ULL << (ztest_random(20) + 43)) +
- (ztest_random(ZTEST_RANGE_LOCKS) << SPA_MAXBLOCKSHIFT);
-
- /*
- * Have multiple threads write to large offsets in an object
- * to verify that parallel writes to an object -- even to the
- * same blocks within the object -- doesn't cause any trouble.
- */
- ztest_od_init(&od[0], ID_PARALLEL, FTAG, 0, DMU_OT_UINT64_OTHER,
- 0, 0, 0);
-
- if (ztest_object_init(zd, od, sizeof (od), B_FALSE) != 0)
- return;
-
- while (ztest_random(10) != 0)
- ztest_io(zd, od[0].od_object, offset);
-}
-
-void
-ztest_dmu_prealloc(ztest_ds_t *zd, uint64_t id)
-{
- ztest_od_t od[1];
- uint64_t offset = (1ULL << (ztest_random(4) + SPA_MAXBLOCKSHIFT)) +
- (ztest_random(ZTEST_RANGE_LOCKS) << SPA_MAXBLOCKSHIFT);
- uint64_t count = ztest_random(20) + 1;
- uint64_t blocksize = ztest_random_blocksize();
- void *data;
-
- ztest_od_init(&od[0], id, FTAG, 0, DMU_OT_UINT64_OTHER, blocksize,
- 0, 0);
-
- if (ztest_object_init(zd, od, sizeof (od), !ztest_random(2)) != 0)
- return;
-
- if (ztest_truncate(zd, od[0].od_object, offset, count * blocksize) != 0)
- return;
-
- ztest_prealloc(zd, od[0].od_object, offset, count * blocksize);
-
- data = umem_zalloc(blocksize, UMEM_NOFAIL);
-
- while (ztest_random(count) != 0) {
- uint64_t randoff = offset + (ztest_random(count) * blocksize);
- if (ztest_write(zd, od[0].od_object, randoff, blocksize,
- data) != 0)
- break;
- while (ztest_random(4) != 0)
- ztest_io(zd, od[0].od_object, randoff);
- }
-
- umem_free(data, blocksize);
-}
-
-/*
- * Verify that zap_{create,destroy,add,remove,update} work as expected.
- */
-#define ZTEST_ZAP_MIN_INTS 1
-#define ZTEST_ZAP_MAX_INTS 4
-#define ZTEST_ZAP_MAX_PROPS 1000
-
-void
-ztest_zap(ztest_ds_t *zd, uint64_t id)
-{
- objset_t *os = zd->zd_os;
- ztest_od_t od[1];
- uint64_t object;
- uint64_t txg, last_txg;
- uint64_t value[ZTEST_ZAP_MAX_INTS];
- uint64_t zl_ints, zl_intsize, prop;
- int i, ints;
- dmu_tx_t *tx;
- char propname[100], txgname[100];
- int error;
- char *hc[2] = { "s.acl.h", ".s.open.h.hyLZlg" };
-
- ztest_od_init(&od[0], id, FTAG, 0, DMU_OT_ZAP_OTHER, 0, 0, 0);
-
- if (ztest_object_init(zd, od, sizeof (od), !ztest_random(2)) != 0)
- return;
-
- object = od[0].od_object;
-
- /*
- * Generate a known hash collision, and verify that
- * we can lookup and remove both entries.
- */
- tx = dmu_tx_create(os);
- dmu_tx_hold_zap(tx, object, B_TRUE, NULL);
- txg = ztest_tx_assign(tx, TXG_MIGHTWAIT, FTAG);
- if (txg == 0)
- return;
- for (i = 0; i < 2; i++) {
- value[i] = i;
- VERIFY3U(0, ==, zap_add(os, object, hc[i], sizeof (uint64_t),
- 1, &value[i], tx));
- }
- for (i = 0; i < 2; i++) {
- VERIFY3U(EEXIST, ==, zap_add(os, object, hc[i],
- sizeof (uint64_t), 1, &value[i], tx));
- VERIFY3U(0, ==,
- zap_length(os, object, hc[i], &zl_intsize, &zl_ints));
- ASSERT3U(zl_intsize, ==, sizeof (uint64_t));
- ASSERT3U(zl_ints, ==, 1);
- }
- for (i = 0; i < 2; i++) {
- VERIFY3U(0, ==, zap_remove(os, object, hc[i], tx));
- }
- dmu_tx_commit(tx);
-
- /*
- * Generate a buch of random entries.
- */
- ints = MAX(ZTEST_ZAP_MIN_INTS, object % ZTEST_ZAP_MAX_INTS);
-
- prop = ztest_random(ZTEST_ZAP_MAX_PROPS);
- (void) sprintf(propname, "prop_%llu", (u_longlong_t)prop);
- (void) sprintf(txgname, "txg_%llu", (u_longlong_t)prop);
- bzero(value, sizeof (value));
- last_txg = 0;
-
- /*
- * If these zap entries already exist, validate their contents.
- */
- error = zap_length(os, object, txgname, &zl_intsize, &zl_ints);
- if (error == 0) {
- ASSERT3U(zl_intsize, ==, sizeof (uint64_t));
- ASSERT3U(zl_ints, ==, 1);
-
- VERIFY(zap_lookup(os, object, txgname, zl_intsize,
- zl_ints, &last_txg) == 0);
-
- VERIFY(zap_length(os, object, propname, &zl_intsize,
- &zl_ints) == 0);
-
- ASSERT3U(zl_intsize, ==, sizeof (uint64_t));
- ASSERT3U(zl_ints, ==, ints);
-
- VERIFY(zap_lookup(os, object, propname, zl_intsize,
- zl_ints, value) == 0);
-
- for (i = 0; i < ints; i++) {
- ASSERT3U(value[i], ==, last_txg + object + i);
- }
- } else {
- ASSERT3U(error, ==, ENOENT);
- }
-
- /*
- * Atomically update two entries in our zap object.
- * The first is named txg_%llu, and contains the txg
- * in which the property was last updated. The second
- * is named prop_%llu, and the nth element of its value
- * should be txg + object + n.
- */
- tx = dmu_tx_create(os);
- dmu_tx_hold_zap(tx, object, B_TRUE, NULL);
- txg = ztest_tx_assign(tx, TXG_MIGHTWAIT, FTAG);
- if (txg == 0)
- return;
-
- if (last_txg > txg)
- fatal(0, "zap future leak: old %llu new %llu", last_txg, txg);
-
- for (i = 0; i < ints; i++)
- value[i] = txg + object + i;
-
- VERIFY3U(0, ==, zap_update(os, object, txgname, sizeof (uint64_t),
- 1, &txg, tx));
- VERIFY3U(0, ==, zap_update(os, object, propname, sizeof (uint64_t),
- ints, value, tx));
-
- dmu_tx_commit(tx);
-
- /*
- * Remove a random pair of entries.
- */
- prop = ztest_random(ZTEST_ZAP_MAX_PROPS);
- (void) sprintf(propname, "prop_%llu", (u_longlong_t)prop);
- (void) sprintf(txgname, "txg_%llu", (u_longlong_t)prop);
-
- error = zap_length(os, object, txgname, &zl_intsize, &zl_ints);
-
- if (error == ENOENT)
- return;
-
- ASSERT0(error);
-
- tx = dmu_tx_create(os);
- dmu_tx_hold_zap(tx, object, B_TRUE, NULL);
- txg = ztest_tx_assign(tx, TXG_MIGHTWAIT, FTAG);
- if (txg == 0)
- return;
- VERIFY3U(0, ==, zap_remove(os, object, txgname, tx));
- VERIFY3U(0, ==, zap_remove(os, object, propname, tx));
- dmu_tx_commit(tx);
-}
-
-/*
- * Testcase to test the upgrading of a microzap to fatzap.
- */
-void
-ztest_fzap(ztest_ds_t *zd, uint64_t id)
-{
- objset_t *os = zd->zd_os;
- ztest_od_t od[1];
- uint64_t object, txg;
-
- ztest_od_init(&od[0], id, FTAG, 0, DMU_OT_ZAP_OTHER, 0, 0, 0);
-
- if (ztest_object_init(zd, od, sizeof (od), !ztest_random(2)) != 0)
- return;
-
- object = od[0].od_object;
-
- /*
- * Add entries to this ZAP and make sure it spills over
- * and gets upgraded to a fatzap. Also, since we are adding
- * 2050 entries we should see ptrtbl growth and leaf-block split.
- */
- for (int i = 0; i < 2050; i++) {
- char name[ZFS_MAX_DATASET_NAME_LEN];
- uint64_t value = i;
- dmu_tx_t *tx;
- int error;
-
- (void) snprintf(name, sizeof (name), "fzap-%llu-%llu",
- id, value);
-
- tx = dmu_tx_create(os);
- dmu_tx_hold_zap(tx, object, B_TRUE, name);
- txg = ztest_tx_assign(tx, TXG_MIGHTWAIT, FTAG);
- if (txg == 0)
- return;
- error = zap_add(os, object, name, sizeof (uint64_t), 1,
- &value, tx);
- ASSERT(error == 0 || error == EEXIST);
- dmu_tx_commit(tx);
- }
-}
-
-/* ARGSUSED */
-void
-ztest_zap_parallel(ztest_ds_t *zd, uint64_t id)
-{
- objset_t *os = zd->zd_os;
- ztest_od_t od[1];
- uint64_t txg, object, count, wsize, wc, zl_wsize, zl_wc;
- dmu_tx_t *tx;
- int i, namelen, error;
- int micro = ztest_random(2);
- char name[20], string_value[20];
- void *data;
-
- ztest_od_init(&od[0], ID_PARALLEL, FTAG, micro, DMU_OT_ZAP_OTHER,
- 0, 0, 0);
-
- if (ztest_object_init(zd, od, sizeof (od), B_FALSE) != 0)
- return;
-
- object = od[0].od_object;
-
- /*
- * Generate a random name of the form 'xxx.....' where each
- * x is a random printable character and the dots are dots.
- * There are 94 such characters, and the name length goes from
- * 6 to 20, so there are 94^3 * 15 = 12,458,760 possible names.
- */
- namelen = ztest_random(sizeof (name) - 5) + 5 + 1;
-
- for (i = 0; i < 3; i++)
- name[i] = '!' + ztest_random('~' - '!' + 1);
- for (; i < namelen - 1; i++)
- name[i] = '.';
- name[i] = '\0';
-
- if ((namelen & 1) || micro) {
- wsize = sizeof (txg);
- wc = 1;
- data = &txg;
- } else {
- wsize = 1;
- wc = namelen;
- data = string_value;
- }
-
- count = -1ULL;
- VERIFY0(zap_count(os, object, &count));
- ASSERT(count != -1ULL);
-
- /*
- * Select an operation: length, lookup, add, update, remove.
- */
- i = ztest_random(5);
-
- if (i >= 2) {
- tx = dmu_tx_create(os);
- dmu_tx_hold_zap(tx, object, B_TRUE, NULL);
- txg = ztest_tx_assign(tx, TXG_MIGHTWAIT, FTAG);
- if (txg == 0)
- return;
- bcopy(name, string_value, namelen);
- } else {
- tx = NULL;
- txg = 0;
- bzero(string_value, namelen);
- }
-
- switch (i) {
-
- case 0:
- error = zap_length(os, object, name, &zl_wsize, &zl_wc);
- if (error == 0) {
- ASSERT3U(wsize, ==, zl_wsize);
- ASSERT3U(wc, ==, zl_wc);
- } else {
- ASSERT3U(error, ==, ENOENT);
- }
- break;
-
- case 1:
- error = zap_lookup(os, object, name, wsize, wc, data);
- if (error == 0) {
- if (data == string_value &&
- bcmp(name, data, namelen) != 0)
- fatal(0, "name '%s' != val '%s' len %d",
- name, data, namelen);
- } else {
- ASSERT3U(error, ==, ENOENT);
- }
- break;
-
- case 2:
- error = zap_add(os, object, name, wsize, wc, data, tx);
- ASSERT(error == 0 || error == EEXIST);
- break;
-
- case 3:
- VERIFY(zap_update(os, object, name, wsize, wc, data, tx) == 0);
- break;
-
- case 4:
- error = zap_remove(os, object, name, tx);
- ASSERT(error == 0 || error == ENOENT);
- break;
- }
-
- if (tx != NULL)
- dmu_tx_commit(tx);
-}
-
-/*
- * Commit callback data.
- */
-typedef struct ztest_cb_data {
- list_node_t zcd_node;
- uint64_t zcd_txg;
- int zcd_expected_err;
- boolean_t zcd_added;
- boolean_t zcd_called;
- spa_t *zcd_spa;
-} ztest_cb_data_t;
-
-/* This is the actual commit callback function */
-static void
-ztest_commit_callback(void *arg, int error)
-{
- ztest_cb_data_t *data = arg;
- uint64_t synced_txg;
-
- VERIFY(data != NULL);
- VERIFY3S(data->zcd_expected_err, ==, error);
- VERIFY(!data->zcd_called);
-
- synced_txg = spa_last_synced_txg(data->zcd_spa);
- if (data->zcd_txg > synced_txg)
- fatal(0, "commit callback of txg %" PRIu64 " called prematurely"
- ", last synced txg = %" PRIu64 "\n", data->zcd_txg,
- synced_txg);
-
- data->zcd_called = B_TRUE;
-
- if (error == ECANCELED) {
- ASSERT0(data->zcd_txg);
- ASSERT(!data->zcd_added);
-
- /*
- * The private callback data should be destroyed here, but
- * since we are going to check the zcd_called field after
- * dmu_tx_abort(), we will destroy it there.
- */
- return;
- }
-
- /* Was this callback added to the global callback list? */
- if (!data->zcd_added)
- goto out;
-
- ASSERT3U(data->zcd_txg, !=, 0);
-
- /* Remove our callback from the list */
- mutex_enter(&zcl.zcl_callbacks_lock);
- list_remove(&zcl.zcl_callbacks, data);
- mutex_exit(&zcl.zcl_callbacks_lock);
-
-out:
- umem_free(data, sizeof (ztest_cb_data_t));
-}
-
-/* Allocate and initialize callback data structure */
-static ztest_cb_data_t *
-ztest_create_cb_data(objset_t *os, uint64_t txg)
-{
- ztest_cb_data_t *cb_data;
-
- cb_data = umem_zalloc(sizeof (ztest_cb_data_t), UMEM_NOFAIL);
-
- cb_data->zcd_txg = txg;
- cb_data->zcd_spa = dmu_objset_spa(os);
-
- return (cb_data);
-}
-
-/*
- * If a number of txgs equal to this threshold have been created after a commit
- * callback has been registered but not called, then we assume there is an
- * implementation bug.
- */
-#define ZTEST_COMMIT_CALLBACK_THRESH (TXG_CONCURRENT_STATES + 2)
-
-/*
- * Commit callback test.
- */
-void
-ztest_dmu_commit_callbacks(ztest_ds_t *zd, uint64_t id)
-{
- objset_t *os = zd->zd_os;
- ztest_od_t od[1];
- dmu_tx_t *tx;
- ztest_cb_data_t *cb_data[3], *tmp_cb;
- uint64_t old_txg, txg;
- int i, error;
-
- ztest_od_init(&od[0], id, FTAG, 0, DMU_OT_UINT64_OTHER, 0, 0, 0);
-
- if (ztest_object_init(zd, od, sizeof (od), B_FALSE) != 0)
- return;
-
- tx = dmu_tx_create(os);
-
- cb_data[0] = ztest_create_cb_data(os, 0);
- dmu_tx_callback_register(tx, ztest_commit_callback, cb_data[0]);
-
- dmu_tx_hold_write(tx, od[0].od_object, 0, sizeof (uint64_t));
-
- /* Every once in a while, abort the transaction on purpose */
- if (ztest_random(100) == 0)
- error = -1;
-
- if (!error)
- error = dmu_tx_assign(tx, TXG_NOWAIT);
-
- txg = error ? 0 : dmu_tx_get_txg(tx);
-
- cb_data[0]->zcd_txg = txg;
- cb_data[1] = ztest_create_cb_data(os, txg);
- dmu_tx_callback_register(tx, ztest_commit_callback, cb_data[1]);
-
- if (error) {
- /*
- * It's not a strict requirement to call the registered
- * callbacks from inside dmu_tx_abort(), but that's what
- * it's supposed to happen in the current implementation
- * so we will check for that.
- */
- for (i = 0; i < 2; i++) {
- cb_data[i]->zcd_expected_err = ECANCELED;
- VERIFY(!cb_data[i]->zcd_called);
- }
-
- dmu_tx_abort(tx);
-
- for (i = 0; i < 2; i++) {
- VERIFY(cb_data[i]->zcd_called);
- umem_free(cb_data[i], sizeof (ztest_cb_data_t));
- }
-
- return;
- }
-
- cb_data[2] = ztest_create_cb_data(os, txg);
- dmu_tx_callback_register(tx, ztest_commit_callback, cb_data[2]);
-
- /*
- * Read existing data to make sure there isn't a future leak.
- */
- VERIFY(0 == dmu_read(os, od[0].od_object, 0, sizeof (uint64_t),
- &old_txg, DMU_READ_PREFETCH));
-
- if (old_txg > txg)
- fatal(0, "future leak: got %" PRIu64 ", open txg is %" PRIu64,
- old_txg, txg);
-
- dmu_write(os, od[0].od_object, 0, sizeof (uint64_t), &txg, tx);
-
- mutex_enter(&zcl.zcl_callbacks_lock);
-
- /*
- * Since commit callbacks don't have any ordering requirement and since
- * it is theoretically possible for a commit callback to be called
- * after an arbitrary amount of time has elapsed since its txg has been
- * synced, it is difficult to reliably determine whether a commit
- * callback hasn't been called due to high load or due to a flawed
- * implementation.
- *
- * In practice, we will assume that if after a certain number of txgs a
- * commit callback hasn't been called, then most likely there's an
- * implementation bug..
- */
- tmp_cb = list_head(&zcl.zcl_callbacks);
- if (tmp_cb != NULL &&
- (txg - ZTEST_COMMIT_CALLBACK_THRESH) > tmp_cb->zcd_txg) {
- fatal(0, "Commit callback threshold exceeded, oldest txg: %"
- PRIu64 ", open txg: %" PRIu64 "\n", tmp_cb->zcd_txg, txg);
- }
-
- /*
- * Let's find the place to insert our callbacks.
- *
- * Even though the list is ordered by txg, it is possible for the
- * insertion point to not be the end because our txg may already be
- * quiescing at this point and other callbacks in the open txg
- * (from other objsets) may have sneaked in.
- */
- tmp_cb = list_tail(&zcl.zcl_callbacks);
- while (tmp_cb != NULL && tmp_cb->zcd_txg > txg)
- tmp_cb = list_prev(&zcl.zcl_callbacks, tmp_cb);
-
- /* Add the 3 callbacks to the list */
- for (i = 0; i < 3; i++) {
- if (tmp_cb == NULL)
- list_insert_head(&zcl.zcl_callbacks, cb_data[i]);
- else
- list_insert_after(&zcl.zcl_callbacks, tmp_cb,
- cb_data[i]);
-
- cb_data[i]->zcd_added = B_TRUE;
- VERIFY(!cb_data[i]->zcd_called);
-
- tmp_cb = cb_data[i];
- }
-
- mutex_exit(&zcl.zcl_callbacks_lock);
-
- dmu_tx_commit(tx);
-}
-
-/*
- * Visit each object in the dataset. Verify that its properties
- * are consistent what was stored in the block tag when it was created,
- * and that its unused bonus buffer space has not been overwritten.
- */
-void
-ztest_verify_dnode_bt(ztest_ds_t *zd, uint64_t id)
-{
- objset_t *os = zd->zd_os;
- uint64_t obj;
- int err = 0;
-
- for (obj = 0; err == 0; err = dmu_object_next(os, &obj, FALSE, 0)) {
- ztest_block_tag_t *bt = NULL;
- dmu_object_info_t doi;
- dmu_buf_t *db;
-
- if (dmu_bonus_hold(os, obj, FTAG, &db) != 0)
- continue;
-
- dmu_object_info_from_db(db, &doi);
- if (doi.doi_bonus_size >= sizeof (*bt))
- bt = ztest_bt_bonus(db);
-
- if (bt && bt->bt_magic == BT_MAGIC) {
- ztest_bt_verify(bt, os, obj, doi.doi_dnodesize,
- bt->bt_offset, bt->bt_gen, bt->bt_txg,
- bt->bt_crtxg);
- ztest_verify_unused_bonus(db, bt, obj, os, bt->bt_gen);
- }
-
- dmu_buf_rele(db, FTAG);
- }
-}
-
-/* ARGSUSED */
-void
-ztest_dsl_prop_get_set(ztest_ds_t *zd, uint64_t id)
-{
- zfs_prop_t proplist[] = {
- ZFS_PROP_CHECKSUM,
- ZFS_PROP_COMPRESSION,
- ZFS_PROP_COPIES,
- ZFS_PROP_DEDUP
- };
-
- rw_enter(&ztest_name_lock, RW_READER);
-
- for (int p = 0; p < sizeof (proplist) / sizeof (proplist[0]); p++)
- (void) ztest_dsl_prop_set_uint64(zd->zd_name, proplist[p],
- ztest_random_dsl_prop(proplist[p]), (int)ztest_random(2));
-
- rw_exit(&ztest_name_lock);
-}
-
-/* ARGSUSED */
-void
-ztest_remap_blocks(ztest_ds_t *zd, uint64_t id)
-{
- rw_enter(&ztest_name_lock, RW_READER);
-
- int error = dmu_objset_remap_indirects(zd->zd_name);
- if (error == ENOSPC)
- error = 0;
- ASSERT0(error);
-
- rw_exit(&ztest_name_lock);
-}
-
-/* ARGSUSED */
-void
-ztest_spa_prop_get_set(ztest_ds_t *zd, uint64_t id)
-{
- nvlist_t *props = NULL;
-
- rw_enter(&ztest_name_lock, RW_READER);
-
- (void) ztest_spa_prop_set_uint64(ZPOOL_PROP_DEDUPDITTO,
- ZIO_DEDUPDITTO_MIN + ztest_random(ZIO_DEDUPDITTO_MIN));
-
- VERIFY0(spa_prop_get(ztest_spa, &props));
-
- if (ztest_opts.zo_verbose >= 6)
- dump_nvlist(props, 4);
-
- nvlist_free(props);
-
- rw_exit(&ztest_name_lock);
-}
-
-static int
-user_release_one(const char *snapname, const char *holdname)
-{
- nvlist_t *snaps, *holds;
- int error;
-
- snaps = fnvlist_alloc();
- holds = fnvlist_alloc();
- fnvlist_add_boolean(holds, holdname);
- fnvlist_add_nvlist(snaps, snapname, holds);
- fnvlist_free(holds);
- error = dsl_dataset_user_release(snaps, NULL);
- fnvlist_free(snaps);
- return (error);
-}
-
-/*
- * Test snapshot hold/release and deferred destroy.
- */
-void
-ztest_dmu_snapshot_hold(ztest_ds_t *zd, uint64_t id)
-{
- int error;
- objset_t *os = zd->zd_os;
- objset_t *origin;
- char snapname[100];
- char fullname[100];
- char clonename[100];
- char tag[100];
- char osname[ZFS_MAX_DATASET_NAME_LEN];
- nvlist_t *holds;
-
- rw_enter(&ztest_name_lock, RW_READER);
-
- dmu_objset_name(os, osname);
-
- (void) snprintf(snapname, sizeof (snapname), "sh1_%llu", id);
- (void) snprintf(fullname, sizeof (fullname), "%s@%s", osname, snapname);
- (void) snprintf(clonename, sizeof (clonename),
- "%s/ch1_%llu", osname, id);
- (void) snprintf(tag, sizeof (tag), "tag_%llu", id);
-
- /*
- * Clean up from any previous run.
- */
- error = dsl_destroy_head(clonename);
- if (error != ENOENT)
- ASSERT0(error);
- error = user_release_one(fullname, tag);
- if (error != ESRCH && error != ENOENT)
- ASSERT0(error);
- error = dsl_destroy_snapshot(fullname, B_FALSE);
- if (error != ENOENT)
- ASSERT0(error);
-
- /*
- * Create snapshot, clone it, mark snap for deferred destroy,
- * destroy clone, verify snap was also destroyed.
- */
- error = dmu_objset_snapshot_one(osname, snapname);
- if (error) {
- if (error == ENOSPC) {
- ztest_record_enospc("dmu_objset_snapshot");
- goto out;
- }
- fatal(0, "dmu_objset_snapshot(%s) = %d", fullname, error);
- }
-
- error = dmu_objset_clone(clonename, fullname);
- if (error) {
- if (error == ENOSPC) {
- ztest_record_enospc("dmu_objset_clone");
- goto out;
- }
- fatal(0, "dmu_objset_clone(%s) = %d", clonename, error);
- }
-
- error = dsl_destroy_snapshot(fullname, B_TRUE);
- if (error) {
- fatal(0, "dsl_destroy_snapshot(%s, B_TRUE) = %d",
- fullname, error);
- }
-
- error = dsl_destroy_head(clonename);
- if (error)
- fatal(0, "dsl_destroy_head(%s) = %d", clonename, error);
-
- error = dmu_objset_hold(fullname, FTAG, &origin);
- if (error != ENOENT)
- fatal(0, "dmu_objset_hold(%s) = %d", fullname, error);
-
- /*
- * Create snapshot, add temporary hold, verify that we can't
- * destroy a held snapshot, mark for deferred destroy,
- * release hold, verify snapshot was destroyed.
- */
- error = dmu_objset_snapshot_one(osname, snapname);
- if (error) {
- if (error == ENOSPC) {
- ztest_record_enospc("dmu_objset_snapshot");
- goto out;
- }
- fatal(0, "dmu_objset_snapshot(%s) = %d", fullname, error);
- }
-
- holds = fnvlist_alloc();
- fnvlist_add_string(holds, fullname, tag);
- error = dsl_dataset_user_hold(holds, 0, NULL);
- fnvlist_free(holds);
-
- if (error == ENOSPC) {
- ztest_record_enospc("dsl_dataset_user_hold");
- goto out;
- } else if (error) {
- fatal(0, "dsl_dataset_user_hold(%s, %s) = %u",
- fullname, tag, error);
- }
-
- error = dsl_destroy_snapshot(fullname, B_FALSE);
- if (error != EBUSY) {
- fatal(0, "dsl_destroy_snapshot(%s, B_FALSE) = %d",
- fullname, error);
- }
-
- error = dsl_destroy_snapshot(fullname, B_TRUE);
- if (error) {
- fatal(0, "dsl_destroy_snapshot(%s, B_TRUE) = %d",
- fullname, error);
- }
-
- error = user_release_one(fullname, tag);
- if (error)
- fatal(0, "user_release_one(%s, %s) = %d", fullname, tag, error);
-
- VERIFY3U(dmu_objset_hold(fullname, FTAG, &origin), ==, ENOENT);
-
-out:
- rw_exit(&ztest_name_lock);
-}
-
-/*
- * Inject random faults into the on-disk data.
- */
-/* ARGSUSED */
-void
-ztest_fault_inject(ztest_ds_t *zd, uint64_t id)
-{
- ztest_shared_t *zs = ztest_shared;
- spa_t *spa = ztest_spa;
- int fd;
- uint64_t offset;
- uint64_t leaves;
- uint64_t bad = 0x1990c0ffeedecadeULL;
- uint64_t top, leaf;
- char path0[MAXPATHLEN];
- char pathrand[MAXPATHLEN];
- size_t fsize;
- int bshift = SPA_MAXBLOCKSHIFT + 2;
- int iters = 1000;
- int maxfaults;
- int mirror_save;
- vdev_t *vd0 = NULL;
- uint64_t guid0 = 0;
- boolean_t islog = B_FALSE;
-
- mutex_enter(&ztest_vdev_lock);
-
- /*
- * Device removal is in progress, fault injection must be disabled
- * until it completes and the pool is scrubbed. The fault injection
- * strategy for damaging blocks does not take in to account evacuated
- * blocks which may have already been damaged.
- */
- if (ztest_device_removal_active) {
- mutex_exit(&ztest_vdev_lock);
- return;
- }
-
- maxfaults = MAXFAULTS();
- leaves = MAX(zs->zs_mirrors, 1) * ztest_opts.zo_raidz;
- mirror_save = zs->zs_mirrors;
- mutex_exit(&ztest_vdev_lock);
-
- ASSERT(leaves >= 1);
-
- /*
- * Grab the name lock as reader. There are some operations
- * which don't like to have their vdevs changed while
- * they are in progress (i.e. spa_change_guid). Those
- * operations will have grabbed the name lock as writer.
- */
- rw_enter(&ztest_name_lock, RW_READER);
-
- /*
- * We need SCL_STATE here because we're going to look at vd0->vdev_tsd.
- */
- spa_config_enter(spa, SCL_STATE, FTAG, RW_READER);
-
- if (ztest_random(2) == 0) {
- /*
- * Inject errors on a normal data device or slog device.
- */
- top = ztest_random_vdev_top(spa, B_TRUE);
- leaf = ztest_random(leaves) + zs->zs_splits;
-
- /*
- * Generate paths to the first leaf in this top-level vdev,
- * and to the random leaf we selected. We'll induce transient
- * write failures and random online/offline activity on leaf 0,
- * and we'll write random garbage to the randomly chosen leaf.
- */
- (void) snprintf(path0, sizeof (path0), ztest_dev_template,
- ztest_opts.zo_dir, ztest_opts.zo_pool,
- top * leaves + zs->zs_splits);
- (void) snprintf(pathrand, sizeof (pathrand), ztest_dev_template,
- ztest_opts.zo_dir, ztest_opts.zo_pool,
- top * leaves + leaf);
-
- vd0 = vdev_lookup_by_path(spa->spa_root_vdev, path0);
- if (vd0 != NULL && vd0->vdev_top->vdev_islog)
- islog = B_TRUE;
-
- /*
- * If the top-level vdev needs to be resilvered
- * then we only allow faults on the device that is
- * resilvering.
- */
- if (vd0 != NULL && maxfaults != 1 &&
- (!vdev_resilver_needed(vd0->vdev_top, NULL, NULL) ||
- vd0->vdev_resilver_txg != 0)) {
- /*
- * Make vd0 explicitly claim to be unreadable,
- * or unwriteable, or reach behind its back
- * and close the underlying fd. We can do this if
- * maxfaults == 0 because we'll fail and reexecute,
- * and we can do it if maxfaults >= 2 because we'll
- * have enough redundancy. If maxfaults == 1, the
- * combination of this with injection of random data
- * corruption below exceeds the pool's fault tolerance.
- */
- vdev_file_t *vf = vd0->vdev_tsd;
-
- zfs_dbgmsg("injecting fault to vdev %llu; maxfaults=%d",
- (long long)vd0->vdev_id, (int)maxfaults);
-
- if (vf != NULL && ztest_random(3) == 0) {
- (void) close(vf->vf_vnode->v_fd);
- vf->vf_vnode->v_fd = -1;
- } else if (ztest_random(2) == 0) {
- vd0->vdev_cant_read = B_TRUE;
- } else {
- vd0->vdev_cant_write = B_TRUE;
- }
- guid0 = vd0->vdev_guid;
- }
- } else {
- /*
- * Inject errors on an l2cache device.
- */
- spa_aux_vdev_t *sav = &spa->spa_l2cache;
-
- if (sav->sav_count == 0) {
- spa_config_exit(spa, SCL_STATE, FTAG);
- rw_exit(&ztest_name_lock);
- return;
- }
- vd0 = sav->sav_vdevs[ztest_random(sav->sav_count)];
- guid0 = vd0->vdev_guid;
- (void) strcpy(path0, vd0->vdev_path);
- (void) strcpy(pathrand, vd0->vdev_path);
-
- leaf = 0;
- leaves = 1;
- maxfaults = INT_MAX; /* no limit on cache devices */
- }
-
- spa_config_exit(spa, SCL_STATE, FTAG);
- rw_exit(&ztest_name_lock);
-
- /*
- * If we can tolerate two or more faults, or we're dealing
- * with a slog, randomly online/offline vd0.
- */
- if ((maxfaults >= 2 || islog) && guid0 != 0) {
- if (ztest_random(10) < 6) {
- int flags = (ztest_random(2) == 0 ?
- ZFS_OFFLINE_TEMPORARY : 0);
-
- /*
- * We have to grab the zs_name_lock as writer to
- * prevent a race between offlining a slog and
- * destroying a dataset. Offlining the slog will
- * grab a reference on the dataset which may cause
- * dmu_objset_destroy() to fail with EBUSY thus
- * leaving the dataset in an inconsistent state.
- */
- if (islog)
- rw_enter(&ztest_name_lock, RW_WRITER);
-
- VERIFY(vdev_offline(spa, guid0, flags) != EBUSY);
-
- if (islog)
- rw_exit(&ztest_name_lock);
- } else {
- /*
- * Ideally we would like to be able to randomly
- * call vdev_[on|off]line without holding locks
- * to force unpredictable failures but the side
- * effects of vdev_[on|off]line prevent us from
- * doing so. We grab the ztest_vdev_lock here to
- * prevent a race between injection testing and
- * aux_vdev removal.
- */
- mutex_enter(&ztest_vdev_lock);
- (void) vdev_online(spa, guid0, 0, NULL);
- mutex_exit(&ztest_vdev_lock);
- }
- }
-
- if (maxfaults == 0)
- return;
-
- /*
- * We have at least single-fault tolerance, so inject data corruption.
- */
- fd = open(pathrand, O_RDWR);
-
- if (fd == -1) /* we hit a gap in the device namespace */
- return;
-
- fsize = lseek(fd, 0, SEEK_END);
-
- while (--iters != 0) {
- /*
- * The offset must be chosen carefully to ensure that
- * we do not inject a given logical block with errors
- * on two different leaf devices, because ZFS can not
- * tolerate that (if maxfaults==1).
- *
- * We divide each leaf into chunks of size
- * (# leaves * SPA_MAXBLOCKSIZE * 4). Within each chunk
- * there is a series of ranges to which we can inject errors.
- * Each range can accept errors on only a single leaf vdev.
- * The error injection ranges are separated by ranges
- * which we will not inject errors on any device (DMZs).
- * Each DMZ must be large enough such that a single block
- * can not straddle it, so that a single block can not be
- * a target in two different injection ranges (on different
- * leaf vdevs).
- *
- * For example, with 3 leaves, each chunk looks like:
- * 0 to 32M: injection range for leaf 0
- * 32M to 64M: DMZ - no injection allowed
- * 64M to 96M: injection range for leaf 1
- * 96M to 128M: DMZ - no injection allowed
- * 128M to 160M: injection range for leaf 2
- * 160M to 192M: DMZ - no injection allowed
- */
- offset = ztest_random(fsize / (leaves << bshift)) *
- (leaves << bshift) + (leaf << bshift) +
- (ztest_random(1ULL << (bshift - 1)) & -8ULL);
-
- /*
- * Only allow damage to the labels at one end of the vdev.
- *
- * If all labels are damaged, the device will be totally
- * inaccessible, which will result in loss of data,
- * because we also damage (parts of) the other side of
- * the mirror/raidz.
- *
- * Additionally, we will always have both an even and an
- * odd label, so that we can handle crashes in the
- * middle of vdev_config_sync().
- */
- if ((leaf & 1) == 0 && offset < VDEV_LABEL_START_SIZE)
- continue;
-
- /*
- * The two end labels are stored at the "end" of the disk, but
- * the end of the disk (vdev_psize) is aligned to
- * sizeof (vdev_label_t).
- */
- uint64_t psize = P2ALIGN(fsize, sizeof (vdev_label_t));
- if ((leaf & 1) == 1 &&
- offset + sizeof (bad) > psize - VDEV_LABEL_END_SIZE)
- continue;
-
- mutex_enter(&ztest_vdev_lock);
- if (mirror_save != zs->zs_mirrors) {
- mutex_exit(&ztest_vdev_lock);
- (void) close(fd);
- return;
- }
-
- if (pwrite(fd, &bad, sizeof (bad), offset) != sizeof (bad))
- fatal(1, "can't inject bad word at 0x%llx in %s",
- offset, pathrand);
-
- mutex_exit(&ztest_vdev_lock);
-
- if (ztest_opts.zo_verbose >= 7)
- (void) printf("injected bad word into %s,"
- " offset 0x%llx\n", pathrand, (u_longlong_t)offset);
- }
-
- (void) close(fd);
-}
-
-/*
- * Verify that DDT repair works as expected.
- */
-void
-ztest_ddt_repair(ztest_ds_t *zd, uint64_t id)
-{
- ztest_shared_t *zs = ztest_shared;
- spa_t *spa = ztest_spa;
- objset_t *os = zd->zd_os;
- ztest_od_t od[1];
- uint64_t object, blocksize, txg, pattern, psize;
- enum zio_checksum checksum = spa_dedup_checksum(spa);
- dmu_buf_t *db;
- dmu_tx_t *tx;
- abd_t *abd;
- blkptr_t blk;
- int copies = 2 * ZIO_DEDUPDITTO_MIN;
-
- blocksize = ztest_random_blocksize();
- blocksize = MIN(blocksize, 2048); /* because we write so many */
-
- ztest_od_init(&od[0], id, FTAG, 0, DMU_OT_UINT64_OTHER, blocksize,
- 0, 0);
-
- if (ztest_object_init(zd, od, sizeof (od), B_FALSE) != 0)
- return;
-
- /*
- * Take the name lock as writer to prevent anyone else from changing
- * the pool and dataset properies we need to maintain during this test.
- */
- rw_enter(&ztest_name_lock, RW_WRITER);
-
- if (ztest_dsl_prop_set_uint64(zd->zd_name, ZFS_PROP_DEDUP, checksum,
- B_FALSE) != 0 ||
- ztest_dsl_prop_set_uint64(zd->zd_name, ZFS_PROP_COPIES, 1,
- B_FALSE) != 0) {
- rw_exit(&ztest_name_lock);
- return;
- }
-
- dmu_objset_stats_t dds;
- dsl_pool_config_enter(dmu_objset_pool(os), FTAG);
- dmu_objset_fast_stat(os, &dds);
- dsl_pool_config_exit(dmu_objset_pool(os), FTAG);
-
- object = od[0].od_object;
- blocksize = od[0].od_blocksize;
- pattern = zs->zs_guid ^ dds.dds_guid;
-
- ASSERT(object != 0);
-
- tx = dmu_tx_create(os);
- dmu_tx_hold_write(tx, object, 0, copies * blocksize);
- txg = ztest_tx_assign(tx, TXG_WAIT, FTAG);
- if (txg == 0) {
- rw_exit(&ztest_name_lock);
- return;
- }
-
- /*
- * Write all the copies of our block.
- */
- for (int i = 0; i < copies; i++) {
- uint64_t offset = i * blocksize;
- int error = dmu_buf_hold(os, object, offset, FTAG, &db,
- DMU_READ_NO_PREFETCH);
- if (error != 0) {
- fatal(B_FALSE, "dmu_buf_hold(%p, %llu, %llu) = %u",
- os, (long long)object, (long long) offset, error);
- }
- ASSERT(db->db_offset == offset);
- ASSERT(db->db_size == blocksize);
- ASSERT(ztest_pattern_match(db->db_data, db->db_size, pattern) ||
- ztest_pattern_match(db->db_data, db->db_size, 0ULL));
- dmu_buf_will_fill(db, tx);
- ztest_pattern_set(db->db_data, db->db_size, pattern);
- dmu_buf_rele(db, FTAG);
- }
-
- dmu_tx_commit(tx);
- txg_wait_synced(spa_get_dsl(spa), txg);
-
- /*
- * Find out what block we got.
- */
- VERIFY0(dmu_buf_hold(os, object, 0, FTAG, &db,
- DMU_READ_NO_PREFETCH));
- blk = *((dmu_buf_impl_t *)db)->db_blkptr;
- dmu_buf_rele(db, FTAG);
-
- /*
- * Damage the block. Dedup-ditto will save us when we read it later.
- */
- psize = BP_GET_PSIZE(&blk);
- abd = abd_alloc_linear(psize, B_TRUE);
- ztest_pattern_set(abd_to_buf(abd), psize, ~pattern);
-
- (void) zio_wait(zio_rewrite(NULL, spa, 0, &blk,
- abd, psize, NULL, NULL, ZIO_PRIORITY_SYNC_WRITE,
- ZIO_FLAG_CANFAIL | ZIO_FLAG_INDUCE_DAMAGE, NULL));
-
- abd_free(abd);
-
- rw_exit(&ztest_name_lock);
-}
-
-/*
- * Scrub the pool.
- */
-/* ARGSUSED */
-void
-ztest_scrub(ztest_ds_t *zd, uint64_t id)
-{
- spa_t *spa = ztest_spa;
-
- /*
- * Scrub in progress by device removal.
- */
- if (ztest_device_removal_active)
- return;
-
- (void) spa_scan(spa, POOL_SCAN_SCRUB);
- (void) poll(NULL, 0, 100); /* wait a moment, then force a restart */
- (void) spa_scan(spa, POOL_SCAN_SCRUB);
-}
-
-/*
- * Change the guid for the pool.
- */
-/* ARGSUSED */
-void
-ztest_reguid(ztest_ds_t *zd, uint64_t id)
-{
- spa_t *spa = ztest_spa;
- uint64_t orig, load;
- int error;
-
- if (ztest_opts.zo_mmp_test)
- return;
-
- orig = spa_guid(spa);
- load = spa_load_guid(spa);
-
- rw_enter(&ztest_name_lock, RW_WRITER);
- error = spa_change_guid(spa);
- rw_exit(&ztest_name_lock);
-
- if (error != 0)
- return;
-
- if (ztest_opts.zo_verbose >= 4) {
- (void) printf("Changed guid old %llu -> %llu\n",
- (u_longlong_t)orig, (u_longlong_t)spa_guid(spa));
- }
-
- VERIFY3U(orig, !=, spa_guid(spa));
- VERIFY3U(load, ==, spa_load_guid(spa));
-}
-
-static vdev_t *
-ztest_random_concrete_vdev_leaf(vdev_t *vd)
-{
- if (vd == NULL)
- return (NULL);
-
- if (vd->vdev_children == 0)
- return (vd);
-
- vdev_t *eligible[vd->vdev_children];
- int eligible_idx = 0, i;
- for (i = 0; i < vd->vdev_children; i++) {
- vdev_t *cvd = vd->vdev_child[i];
- if (cvd->vdev_top->vdev_removing)
- continue;
- if (cvd->vdev_children > 0 ||
- (vdev_is_concrete(cvd) && !cvd->vdev_detached)) {
- eligible[eligible_idx++] = cvd;
- }
- }
- VERIFY(eligible_idx > 0);
-
- uint64_t child_no = ztest_random(eligible_idx);
- return (ztest_random_concrete_vdev_leaf(eligible[child_no]));
-}
-
-/* ARGSUSED */
-void
-ztest_initialize(ztest_ds_t *zd, uint64_t id)
-{
- spa_t *spa = ztest_spa;
- int error = 0;
-
- mutex_enter(&ztest_vdev_lock);
-
- spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
-
- /* Random leaf vdev */
- vdev_t *rand_vd = ztest_random_concrete_vdev_leaf(spa->spa_root_vdev);
- if (rand_vd == NULL) {
- spa_config_exit(spa, SCL_VDEV, FTAG);
- mutex_exit(&ztest_vdev_lock);
- return;
- }
-
- /*
- * The random vdev we've selected may change as soon as we
- * drop the spa_config_lock. We create local copies of things
- * we're interested in.
- */
- uint64_t guid = rand_vd->vdev_guid;
- char *path = strdup(rand_vd->vdev_path);
- boolean_t active = rand_vd->vdev_initialize_thread != NULL;
-
- zfs_dbgmsg("vd %p, guid %llu", rand_vd, guid);
- spa_config_exit(spa, SCL_VDEV, FTAG);
-
- uint64_t cmd = ztest_random(POOL_INITIALIZE_FUNCS);
- error = spa_vdev_initialize(spa, guid, cmd);
- switch (cmd) {
- case POOL_INITIALIZE_CANCEL:
- if (ztest_opts.zo_verbose >= 4) {
- (void) printf("Cancel initialize %s", path);
- if (!active)
- (void) printf(" failed (no initialize active)");
- (void) printf("\n");
- }
- break;
- case POOL_INITIALIZE_DO:
- if (ztest_opts.zo_verbose >= 4) {
- (void) printf("Start initialize %s", path);
- if (active && error == 0)
- (void) printf(" failed (already active)");
- else if (error != 0)
- (void) printf(" failed (error %d)", error);
- (void) printf("\n");
- }
- break;
- case POOL_INITIALIZE_SUSPEND:
- if (ztest_opts.zo_verbose >= 4) {
- (void) printf("Suspend initialize %s", path);
- if (!active)
- (void) printf(" failed (no initialize active)");
- (void) printf("\n");
- }
- break;
- }
- free(path);
- mutex_exit(&ztest_vdev_lock);
-}
-
-/*
- * Verify pool integrity by running zdb.
- */
-static void
-ztest_run_zdb(char *pool)
-{
- int status;
- char zdb[MAXPATHLEN + MAXNAMELEN + 20];
- char zbuf[1024];
- char *bin;
- char *ztest;
- char *isa;
- int isalen;
- FILE *fp;
-
- strlcpy(zdb, "/usr/bin/ztest", sizeof(zdb));
-
- /* zdb lives in /usr/sbin, while ztest lives in /usr/bin */
- bin = strstr(zdb, "/usr/bin/");
- ztest = strstr(bin, "/ztest");
- isa = bin + 8;
- isalen = ztest - isa;
- isa = strdup(isa);
- /* LINTED */
- (void) sprintf(bin,
- "/usr/sbin%.*s/zdb -bcc%s%s -G -d -U %s "
- "-o zfs_reconstruct_indirect_combinations_max=65536 %s",
- isalen,
- isa,
- ztest_opts.zo_verbose >= 3 ? "s" : "",
- ztest_opts.zo_verbose >= 4 ? "v" : "",
- spa_config_path,
- pool);
- free(isa);
-
- if (ztest_opts.zo_verbose >= 5)
- (void) printf("Executing %s\n", strstr(zdb, "zdb "));
-
- fp = popen(zdb, "r");
- assert(fp != NULL);
-
- while (fgets(zbuf, sizeof (zbuf), fp) != NULL)
- if (ztest_opts.zo_verbose >= 3)
- (void) printf("%s", zbuf);
-
- status = pclose(fp);
-
- if (status == 0)
- return;
-
- ztest_dump_core = 0;
- if (WIFEXITED(status))
- fatal(0, "'%s' exit code %d", zdb, WEXITSTATUS(status));
- else
- fatal(0, "'%s' died with signal %d", zdb, WTERMSIG(status));
-}
-
-static void
-ztest_walk_pool_directory(char *header)
-{
- spa_t *spa = NULL;
-
- if (ztest_opts.zo_verbose >= 6)
- (void) printf("%s\n", header);
-
- mutex_enter(&spa_namespace_lock);
- while ((spa = spa_next(spa)) != NULL)
- if (ztest_opts.zo_verbose >= 6)
- (void) printf("\t%s\n", spa_name(spa));
- mutex_exit(&spa_namespace_lock);
-}
-
-static void
-ztest_spa_import_export(char *oldname, char *newname)
-{
- nvlist_t *config, *newconfig;
- uint64_t pool_guid;
- spa_t *spa;
- int error;
-
- if (ztest_opts.zo_verbose >= 4) {
- (void) printf("import/export: old = %s, new = %s\n",
- oldname, newname);
- }
-
- /*
- * Clean up from previous runs.
- */
- (void) spa_destroy(newname);
-
- /*
- * Get the pool's configuration and guid.
- */
- VERIFY3U(0, ==, spa_open(oldname, &spa, FTAG));
-
- /*
- * Kick off a scrub to tickle scrub/export races.
- */
- if (ztest_random(2) == 0)
- (void) spa_scan(spa, POOL_SCAN_SCRUB);
-
- pool_guid = spa_guid(spa);
- spa_close(spa, FTAG);
-
- ztest_walk_pool_directory("pools before export");
-
- /*
- * Export it.
- */
- VERIFY3U(0, ==, spa_export(oldname, &config, B_FALSE, B_FALSE));
-
- ztest_walk_pool_directory("pools after export");
-
- /*
- * Try to import it.
- */
- newconfig = spa_tryimport(config);
- ASSERT(newconfig != NULL);
- nvlist_free(newconfig);
-
- /*
- * Import it under the new name.
- */
- error = spa_import(newname, config, NULL, 0);
- if (error != 0) {
- dump_nvlist(config, 0);
- fatal(B_FALSE, "couldn't import pool %s as %s: error %u",
- oldname, newname, error);
- }
-
- ztest_walk_pool_directory("pools after import");
-
- /*
- * Try to import it again -- should fail with EEXIST.
- */
- VERIFY3U(EEXIST, ==, spa_import(newname, config, NULL, 0));
-
- /*
- * Try to import it under a different name -- should fail with EEXIST.
- */
- VERIFY3U(EEXIST, ==, spa_import(oldname, config, NULL, 0));
-
- /*
- * Verify that the pool is no longer visible under the old name.
- */
- VERIFY3U(ENOENT, ==, spa_open(oldname, &spa, FTAG));
-
- /*
- * Verify that we can open and close the pool using the new name.
- */
- VERIFY3U(0, ==, spa_open(newname, &spa, FTAG));
- ASSERT(pool_guid == spa_guid(spa));
- spa_close(spa, FTAG);
-
- nvlist_free(config);
-}
-
-static void
-ztest_resume(spa_t *spa)
-{
- if (spa_suspended(spa) && ztest_opts.zo_verbose >= 6)
- (void) printf("resuming from suspended state\n");
- spa_vdev_state_enter(spa, SCL_NONE);
- vdev_clear(spa, NULL);
- (void) spa_vdev_state_exit(spa, NULL, 0);
- (void) zio_resume(spa);
-}
-
-static void *
-ztest_resume_thread(void *arg)
-{
- spa_t *spa = arg;
-
- while (!ztest_exiting) {
- if (spa_suspended(spa))
- ztest_resume(spa);
- (void) poll(NULL, 0, 100);
-
- /*
- * Periodically change the zfs_compressed_arc_enabled setting.
- */
- if (ztest_random(10) == 0)
- zfs_compressed_arc_enabled = ztest_random(2);
-
- /*
- * Periodically change the zfs_abd_scatter_enabled setting.
- */
- if (ztest_random(10) == 0)
- zfs_abd_scatter_enabled = ztest_random(2);
- }
- return (NULL);
-}
-
-static void *
-ztest_deadman_thread(void *arg)
-{
- ztest_shared_t *zs = arg;
- spa_t *spa = ztest_spa;
- hrtime_t delta, total = 0;
-
- for (;;) {
- delta = zs->zs_thread_stop - zs->zs_thread_start +
- MSEC2NSEC(zfs_deadman_synctime_ms);
-
- (void) poll(NULL, 0, (int)NSEC2MSEC(delta));
-
- /*
- * If the pool is suspended then fail immediately. Otherwise,
- * check to see if the pool is making any progress. If
- * vdev_deadman() discovers that there hasn't been any recent
- * I/Os then it will end up aborting the tests.
- */
- if (spa_suspended(spa) || spa->spa_root_vdev == NULL) {
- fatal(0, "aborting test after %llu seconds because "
- "pool has transitioned to a suspended state.",
- zfs_deadman_synctime_ms / 1000);
- return (NULL);
- }
- vdev_deadman(spa->spa_root_vdev);
-
- total += zfs_deadman_synctime_ms/1000;
- (void) printf("ztest has been running for %lld seconds\n",
- total);
- }
-}
-
-static void
-ztest_execute(int test, ztest_info_t *zi, uint64_t id)
-{
- ztest_ds_t *zd = &ztest_ds[id % ztest_opts.zo_datasets];
- ztest_shared_callstate_t *zc = ZTEST_GET_SHARED_CALLSTATE(test);
- hrtime_t functime = gethrtime();
-
- for (int i = 0; i < zi->zi_iters; i++)
- zi->zi_func(zd, id);
-
- functime = gethrtime() - functime;
-
- atomic_add_64(&zc->zc_count, 1);
- atomic_add_64(&zc->zc_time, functime);
-
- if (ztest_opts.zo_verbose >= 4) {
- Dl_info dli;
- (void) dladdr((void *)zi->zi_func, &dli);
- (void) printf("%6.2f sec in %s\n",
- (double)functime / NANOSEC, dli.dli_sname);
- }
-}
-
-static void *
-ztest_thread(void *arg)
-{
- int rand;
- uint64_t id = (uintptr_t)arg;
- ztest_shared_t *zs = ztest_shared;
- uint64_t call_next;
- hrtime_t now;
- ztest_info_t *zi;
- ztest_shared_callstate_t *zc;
-
- while ((now = gethrtime()) < zs->zs_thread_stop) {
- /*
- * See if it's time to force a crash.
- */
- if (now > zs->zs_thread_kill)
- ztest_kill(zs);
-
- /*
- * If we're getting ENOSPC with some regularity, stop.
- */
- if (zs->zs_enospc_count > 10)
- break;
-
- /*
- * Pick a random function to execute.
- */
- rand = ztest_random(ZTEST_FUNCS);
- zi = &ztest_info[rand];
- zc = ZTEST_GET_SHARED_CALLSTATE(rand);
- call_next = zc->zc_next;
-
- if (now >= call_next &&
- atomic_cas_64(&zc->zc_next, call_next, call_next +
- ztest_random(2 * zi->zi_interval[0] + 1)) == call_next) {
- ztest_execute(rand, zi, id);
- }
- }
-
- return (NULL);
-}
-
-static void
-ztest_dataset_name(char *dsname, char *pool, int d)
-{
- (void) snprintf(dsname, ZFS_MAX_DATASET_NAME_LEN, "%s/ds_%d", pool, d);
-}
-
-static void
-ztest_dataset_destroy(int d)
-{
- char name[ZFS_MAX_DATASET_NAME_LEN];
-
- ztest_dataset_name(name, ztest_opts.zo_pool, d);
-
- if (ztest_opts.zo_verbose >= 3)
- (void) printf("Destroying %s to free up space\n", name);
-
- /*
- * Cleanup any non-standard clones and snapshots. In general,
- * ztest thread t operates on dataset (t % zopt_datasets),
- * so there may be more than one thing to clean up.
- */
- for (int t = d; t < ztest_opts.zo_threads;
- t += ztest_opts.zo_datasets) {
- ztest_dsl_dataset_cleanup(name, t);
- }
-
- (void) dmu_objset_find(name, ztest_objset_destroy_cb, NULL,
- DS_FIND_SNAPSHOTS | DS_FIND_CHILDREN);
-}
-
-static void
-ztest_dataset_dirobj_verify(ztest_ds_t *zd)
-{
- uint64_t usedobjs, dirobjs, scratch;
-
- /*
- * ZTEST_DIROBJ is the object directory for the entire dataset.
- * Therefore, the number of objects in use should equal the
- * number of ZTEST_DIROBJ entries, +1 for ZTEST_DIROBJ itself.
- * If not, we have an object leak.
- *
- * Note that we can only check this in ztest_dataset_open(),
- * when the open-context and syncing-context values agree.
- * That's because zap_count() returns the open-context value,
- * while dmu_objset_space() returns the rootbp fill count.
- */
- VERIFY3U(0, ==, zap_count(zd->zd_os, ZTEST_DIROBJ, &dirobjs));
- dmu_objset_space(zd->zd_os, &scratch, &scratch, &usedobjs, &scratch);
- ASSERT3U(dirobjs + 1, ==, usedobjs);
-}
-
-static int
-ztest_dataset_open(int d)
-{
- ztest_ds_t *zd = &ztest_ds[d];
- uint64_t committed_seq = ZTEST_GET_SHARED_DS(d)->zd_seq;
- objset_t *os;
- zilog_t *zilog;
- char name[ZFS_MAX_DATASET_NAME_LEN];
- int error;
-
- ztest_dataset_name(name, ztest_opts.zo_pool, d);
-
- rw_enter(&ztest_name_lock, RW_READER);
-
- error = ztest_dataset_create(name);
- if (error == ENOSPC) {
- rw_exit(&ztest_name_lock);
- ztest_record_enospc(FTAG);
- return (error);
- }
- ASSERT(error == 0 || error == EEXIST);
-
- VERIFY0(dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, zd, &os));
- rw_exit(&ztest_name_lock);
-
- ztest_zd_init(zd, ZTEST_GET_SHARED_DS(d), os);
-
- zilog = zd->zd_zilog;
-
- if (zilog->zl_header->zh_claim_lr_seq != 0 &&
- zilog->zl_header->zh_claim_lr_seq < committed_seq)
- fatal(0, "missing log records: claimed %llu < committed %llu",
- zilog->zl_header->zh_claim_lr_seq, committed_seq);
-
- ztest_dataset_dirobj_verify(zd);
-
- zil_replay(os, zd, ztest_replay_vector);
-
- ztest_dataset_dirobj_verify(zd);
-
- if (ztest_opts.zo_verbose >= 6)
- (void) printf("%s replay %llu blocks, %llu records, seq %llu\n",
- zd->zd_name,
- (u_longlong_t)zilog->zl_parse_blk_count,
- (u_longlong_t)zilog->zl_parse_lr_count,
- (u_longlong_t)zilog->zl_replaying_seq);
-
- zilog = zil_open(os, ztest_get_data);
-
- if (zilog->zl_replaying_seq != 0 &&
- zilog->zl_replaying_seq < committed_seq)
- fatal(0, "missing log records: replayed %llu < committed %llu",
- zilog->zl_replaying_seq, committed_seq);
-
- return (0);
-}
-
-static void
-ztest_dataset_close(int d)
-{
- ztest_ds_t *zd = &ztest_ds[d];
-
- zil_close(zd->zd_zilog);
- dmu_objset_disown(zd->zd_os, zd);
-
- ztest_zd_fini(zd);
-}
-
-/*
- * Kick off threads to run tests on all datasets in parallel.
- */
-static void
-ztest_run(ztest_shared_t *zs)
-{
- thread_t *tid;
- spa_t *spa;
- objset_t *os;
- thread_t resume_tid;
- int error;
-
- ztest_exiting = B_FALSE;
-
- /*
- * Initialize parent/child shared state.
- */
- mutex_init(&ztest_checkpoint_lock, NULL, USYNC_THREAD, NULL);
- mutex_init(&ztest_vdev_lock, NULL, USYNC_THREAD, NULL);
- rw_init(&ztest_name_lock, NULL, USYNC_THREAD, NULL);
-
- zs->zs_thread_start = gethrtime();
- zs->zs_thread_stop =
- zs->zs_thread_start + ztest_opts.zo_passtime * NANOSEC;
- zs->zs_thread_stop = MIN(zs->zs_thread_stop, zs->zs_proc_stop);
- zs->zs_thread_kill = zs->zs_thread_stop;
- if (ztest_random(100) < ztest_opts.zo_killrate) {
- zs->zs_thread_kill -=
- ztest_random(ztest_opts.zo_passtime * NANOSEC);
- }
-
- mutex_init(&zcl.zcl_callbacks_lock, NULL, USYNC_THREAD, NULL);
-
- list_create(&zcl.zcl_callbacks, sizeof (ztest_cb_data_t),
- offsetof(ztest_cb_data_t, zcd_node));
-
- /*
- * Open our pool.
- */
- kernel_init(FREAD | FWRITE);
- VERIFY0(spa_open(ztest_opts.zo_pool, &spa, FTAG));
- metaslab_preload_limit = ztest_random(20) + 1;
- ztest_spa = spa;
-
- dmu_objset_stats_t dds;
- VERIFY0(dmu_objset_own(ztest_opts.zo_pool,
- DMU_OST_ANY, B_TRUE, FTAG, &os));
- dsl_pool_config_enter(dmu_objset_pool(os), FTAG);
- dmu_objset_fast_stat(os, &dds);
- dsl_pool_config_exit(dmu_objset_pool(os), FTAG);
- zs->zs_guid = dds.dds_guid;
- dmu_objset_disown(os, FTAG);
-
- spa->spa_dedup_ditto = 2 * ZIO_DEDUPDITTO_MIN;
-
- /*
- * We don't expect the pool to suspend unless maxfaults == 0,
- * in which case ztest_fault_inject() temporarily takes away
- * the only valid replica.
- */
- if (MAXFAULTS() == 0)
- spa->spa_failmode = ZIO_FAILURE_MODE_WAIT;
- else
- spa->spa_failmode = ZIO_FAILURE_MODE_PANIC;
-
- /*
- * Create a thread to periodically resume suspended I/O.
- */
- VERIFY(thr_create(0, 0, ztest_resume_thread, spa, THR_BOUND,
- &resume_tid) == 0);
-
- /*
- * Create a deadman thread to abort() if we hang.
- */
- VERIFY(thr_create(0, 0, ztest_deadman_thread, zs, THR_BOUND,
- NULL) == 0);
-
- /*
- * Verify that we can safely inquire about any object,
- * whether it's allocated or not. To make it interesting,
- * we probe a 5-wide window around each power of two.
- * This hits all edge cases, including zero and the max.
- */
- for (int t = 0; t < 64; t++) {
- for (int d = -5; d <= 5; d++) {
- error = dmu_object_info(spa->spa_meta_objset,
- (1ULL << t) + d, NULL);
- ASSERT(error == 0 || error == ENOENT ||
- error == EINVAL);
- }
- }
-
- /*
- * If we got any ENOSPC errors on the previous run, destroy something.
- */
- if (zs->zs_enospc_count != 0) {
- int d = ztest_random(ztest_opts.zo_datasets);
- ztest_dataset_destroy(d);
- }
- zs->zs_enospc_count = 0;
-
- tid = umem_zalloc(ztest_opts.zo_threads * sizeof (thread_t),
- UMEM_NOFAIL);
-
- if (ztest_opts.zo_verbose >= 4)
- (void) printf("starting main threads...\n");
-
- /*
- * Kick off all the tests that run in parallel.
- */
- for (int t = 0; t < ztest_opts.zo_threads; t++) {
- if (t < ztest_opts.zo_datasets &&
- ztest_dataset_open(t) != 0)
- return;
- VERIFY(thr_create(0, 0, ztest_thread, (void *)(uintptr_t)t,
- THR_BOUND, &tid[t]) == 0);
- }
-
- /*
- * Wait for all of the tests to complete. We go in reverse order
- * so we don't close datasets while threads are still using them.
- */
- for (int t = ztest_opts.zo_threads - 1; t >= 0; t--) {
- VERIFY(thr_join(tid[t], NULL, NULL) == 0);
- if (t < ztest_opts.zo_datasets)
- ztest_dataset_close(t);
- }
-
- txg_wait_synced(spa_get_dsl(spa), 0);
-
- zs->zs_alloc = metaslab_class_get_alloc(spa_normal_class(spa));
- zs->zs_space = metaslab_class_get_space(spa_normal_class(spa));
- zfs_dbgmsg_print(FTAG);
-
- umem_free(tid, ztest_opts.zo_threads * sizeof (thread_t));
-
- /* Kill the resume thread */
- ztest_exiting = B_TRUE;
- VERIFY(thr_join(resume_tid, NULL, NULL) == 0);
- ztest_resume(spa);
-
- /*
- * Right before closing the pool, kick off a bunch of async I/O;
- * spa_close() should wait for it to complete.
- */
- for (uint64_t object = 1; object < 50; object++) {
- dmu_prefetch(spa->spa_meta_objset, object, 0, 0, 1ULL << 20,
- ZIO_PRIORITY_SYNC_READ);
- }
-
- spa_close(spa, FTAG);
-
- /*
- * Verify that we can loop over all pools.
- */
- mutex_enter(&spa_namespace_lock);
- for (spa = spa_next(NULL); spa != NULL; spa = spa_next(spa))
- if (ztest_opts.zo_verbose > 3)
- (void) printf("spa_next: found %s\n", spa_name(spa));
- mutex_exit(&spa_namespace_lock);
-
- /*
- * Verify that we can export the pool and reimport it under a
- * different name.
- */
- if ((ztest_random(2) == 0) && !ztest_opts.zo_mmp_test) {
- char name[ZFS_MAX_DATASET_NAME_LEN];
- (void) snprintf(name, sizeof (name), "%s_import",
- ztest_opts.zo_pool);
- ztest_spa_import_export(ztest_opts.zo_pool, name);
- ztest_spa_import_export(name, ztest_opts.zo_pool);
- }
-
- kernel_fini();
-
- list_destroy(&zcl.zcl_callbacks);
-
- mutex_destroy(&zcl.zcl_callbacks_lock);
-
- rw_destroy(&ztest_name_lock);
- mutex_destroy(&ztest_vdev_lock);
- mutex_destroy(&ztest_checkpoint_lock);
-}
-
-static void
-ztest_freeze(void)
-{
- ztest_ds_t *zd = &ztest_ds[0];
- spa_t *spa;
- int numloops = 0;
-
- if (ztest_opts.zo_verbose >= 3)
- (void) printf("testing spa_freeze()...\n");
-
- kernel_init(FREAD | FWRITE);
- VERIFY3U(0, ==, spa_open(ztest_opts.zo_pool, &spa, FTAG));
- VERIFY3U(0, ==, ztest_dataset_open(0));
- ztest_spa = spa;
-
- /*
- * Force the first log block to be transactionally allocated.
- * We have to do this before we freeze the pool -- otherwise
- * the log chain won't be anchored.
- */
- while (BP_IS_HOLE(&zd->zd_zilog->zl_header->zh_log)) {
- ztest_dmu_object_alloc_free(zd, 0);
- zil_commit(zd->zd_zilog, 0);
- }
-
- txg_wait_synced(spa_get_dsl(spa), 0);
-
- /*
- * Freeze the pool. This stops spa_sync() from doing anything,
- * so that the only way to record changes from now on is the ZIL.
- */
- spa_freeze(spa);
-
- /*
- * Because it is hard to predict how much space a write will actually
- * require beforehand, we leave ourselves some fudge space to write over
- * capacity.
- */
- uint64_t capacity = metaslab_class_get_space(spa_normal_class(spa)) / 2;
-
- /*
- * Run tests that generate log records but don't alter the pool config
- * or depend on DSL sync tasks (snapshots, objset create/destroy, etc).
- * We do a txg_wait_synced() after each iteration to force the txg
- * to increase well beyond the last synced value in the uberblock.
- * The ZIL should be OK with that.
- *
- * Run a random number of times less than zo_maxloops and ensure we do
- * not run out of space on the pool.
- */
- while (ztest_random(10) != 0 &&
- numloops++ < ztest_opts.zo_maxloops &&
- metaslab_class_get_alloc(spa_normal_class(spa)) < capacity) {
- ztest_od_t od;
- ztest_od_init(&od, 0, FTAG, 0, DMU_OT_UINT64_OTHER, 0, 0, 0);
- VERIFY0(ztest_object_init(zd, &od, sizeof (od), B_FALSE));
- ztest_io(zd, od.od_object,
- ztest_random(ZTEST_RANGE_LOCKS) << SPA_MAXBLOCKSHIFT);
- txg_wait_synced(spa_get_dsl(spa), 0);
- }
-
- /*
- * Commit all of the changes we just generated.
- */
- zil_commit(zd->zd_zilog, 0);
- txg_wait_synced(spa_get_dsl(spa), 0);
-
- /*
- * Close our dataset and close the pool.
- */
- ztest_dataset_close(0);
- spa_close(spa, FTAG);
- kernel_fini();
-
- /*
- * Open and close the pool and dataset to induce log replay.
- */
- kernel_init(FREAD | FWRITE);
- VERIFY3U(0, ==, spa_open(ztest_opts.zo_pool, &spa, FTAG));
- ASSERT(spa_freeze_txg(spa) == UINT64_MAX);
- VERIFY3U(0, ==, ztest_dataset_open(0));
- ztest_dataset_close(0);
-
- ztest_spa = spa;
- txg_wait_synced(spa_get_dsl(spa), 0);
- ztest_reguid(NULL, 0);
-
- spa_close(spa, FTAG);
- kernel_fini();
-}
-
-void
-print_time(hrtime_t t, char *timebuf)
-{
- hrtime_t s = t / NANOSEC;
- hrtime_t m = s / 60;
- hrtime_t h = m / 60;
- hrtime_t d = h / 24;
-
- s -= m * 60;
- m -= h * 60;
- h -= d * 24;
-
- timebuf[0] = '\0';
-
- if (d)
- (void) sprintf(timebuf,
- "%llud%02lluh%02llum%02llus", d, h, m, s);
- else if (h)
- (void) sprintf(timebuf, "%lluh%02llum%02llus", h, m, s);
- else if (m)
- (void) sprintf(timebuf, "%llum%02llus", m, s);
- else
- (void) sprintf(timebuf, "%llus", s);
-}
-
-static nvlist_t *
-make_random_props()
-{
- nvlist_t *props;
-
- VERIFY(nvlist_alloc(&props, NV_UNIQUE_NAME, 0) == 0);
-
- if (ztest_random(2) == 0)
- return (props);
- VERIFY(nvlist_add_uint64(props, "autoreplace", 1) == 0);
-
- return (props);
-}
-
-/*
- * Import a storage pool with the given name.
- */
-static void
-ztest_import(ztest_shared_t *zs)
-{
- libzfs_handle_t *hdl;
- importargs_t args = { 0 };
- spa_t *spa;
- nvlist_t *cfg = NULL;
- int nsearch = 1;
- char *searchdirs[nsearch];
- char *name = ztest_opts.zo_pool;
- int flags = ZFS_IMPORT_MISSING_LOG;
- int error;
-
- mutex_init(&ztest_vdev_lock, NULL, MUTEX_DEFAULT, NULL);
- rw_init(&ztest_name_lock, NULL, USYNC_THREAD, NULL);
-
- kernel_init(FREAD | FWRITE);
- hdl = libzfs_init();
-
- searchdirs[0] = ztest_opts.zo_dir;
- args.paths = nsearch;
- args.path = searchdirs;
- args.can_be_active = B_FALSE;
-
- error = zpool_tryimport(hdl, name, &cfg, &args);
- if (error)
- (void) fatal(0, "No pools found\n");
-
- VERIFY0(spa_import(name, cfg, NULL, flags));
- VERIFY0(spa_open(name, &spa, FTAG));
- zs->zs_metaslab_sz =
- 1ULL << spa->spa_root_vdev->vdev_child[0]->vdev_ms_shift;
- spa_close(spa, FTAG);
-
- libzfs_fini(hdl);
- kernel_fini();
-
- if (!ztest_opts.zo_mmp_test) {
- ztest_run_zdb(ztest_opts.zo_pool);
- ztest_freeze();
- ztest_run_zdb(ztest_opts.zo_pool);
- }
-
- rw_destroy(&ztest_name_lock);
- mutex_destroy(&ztest_vdev_lock);
-}
-
-/*
- * Create a storage pool with the given name and initial vdev size.
- * Then test spa_freeze() functionality.
- */
-static void
-ztest_init(ztest_shared_t *zs)
-{
- spa_t *spa;
- nvlist_t *nvroot, *props;
-
- mutex_init(&ztest_vdev_lock, NULL, USYNC_THREAD, NULL);
- mutex_init(&ztest_checkpoint_lock, NULL, USYNC_THREAD, NULL);
- rw_init(&ztest_name_lock, NULL, USYNC_THREAD, NULL);
-
- kernel_init(FREAD | FWRITE);
-
- /*
- * Create the storage pool.
- */
- (void) spa_destroy(ztest_opts.zo_pool);
- ztest_shared->zs_vdev_next_leaf = 0;
- zs->zs_splits = 0;
- zs->zs_mirrors = ztest_opts.zo_mirrors;
- nvroot = make_vdev_root(NULL, NULL, NULL, ztest_opts.zo_vdev_size, 0,
- NULL, ztest_opts.zo_raidz, zs->zs_mirrors, 1);
- props = make_random_props();
- for (int i = 0; i < SPA_FEATURES; i++) {
- char buf[1024];
- (void) snprintf(buf, sizeof (buf), "feature@%s",
- spa_feature_table[i].fi_uname);
- VERIFY3U(0, ==, nvlist_add_uint64(props, buf, 0));
- }
- VERIFY3U(0, ==, spa_create(ztest_opts.zo_pool, nvroot, props, NULL));
- nvlist_free(nvroot);
- nvlist_free(props);
-
- VERIFY3U(0, ==, spa_open(ztest_opts.zo_pool, &spa, FTAG));
- zs->zs_metaslab_sz =
- 1ULL << spa->spa_root_vdev->vdev_child[0]->vdev_ms_shift;
-
- spa_close(spa, FTAG);
-
- kernel_fini();
-
- if (!ztest_opts.zo_mmp_test) {
- ztest_run_zdb(ztest_opts.zo_pool);
- ztest_freeze();
- ztest_run_zdb(ztest_opts.zo_pool);
- }
-
- rw_destroy(&ztest_name_lock);
- mutex_destroy(&ztest_vdev_lock);
- mutex_destroy(&ztest_checkpoint_lock);
-}
-
-static void
-setup_data_fd(void)
-{
- static char ztest_name_data[] = "/tmp/ztest.data.XXXXXX";
-
- ztest_fd_data = mkstemp(ztest_name_data);
- ASSERT3S(ztest_fd_data, >=, 0);
- (void) unlink(ztest_name_data);
-}
-
-
-static int
-shared_data_size(ztest_shared_hdr_t *hdr)
-{
- int size;
-
- size = hdr->zh_hdr_size;
- size += hdr->zh_opts_size;
- size += hdr->zh_size;
- size += hdr->zh_stats_size * hdr->zh_stats_count;
- size += hdr->zh_ds_size * hdr->zh_ds_count;
-
- return (size);
-}
-
-static void
-setup_hdr(void)
-{
- int size;
- ztest_shared_hdr_t *hdr;
-
- hdr = (void *)mmap(0, P2ROUNDUP(sizeof (*hdr), getpagesize()),
- PROT_READ | PROT_WRITE, MAP_SHARED, ztest_fd_data, 0);
- ASSERT(hdr != MAP_FAILED);
-
- VERIFY3U(0, ==, ftruncate(ztest_fd_data, sizeof (ztest_shared_hdr_t)));
-
- hdr->zh_hdr_size = sizeof (ztest_shared_hdr_t);
- hdr->zh_opts_size = sizeof (ztest_shared_opts_t);
- hdr->zh_size = sizeof (ztest_shared_t);
- hdr->zh_stats_size = sizeof (ztest_shared_callstate_t);
- hdr->zh_stats_count = ZTEST_FUNCS;
- hdr->zh_ds_size = sizeof (ztest_shared_ds_t);
- hdr->zh_ds_count = ztest_opts.zo_datasets;
-
- size = shared_data_size(hdr);
- VERIFY3U(0, ==, ftruncate(ztest_fd_data, size));
-
- (void) munmap((caddr_t)hdr, P2ROUNDUP(sizeof (*hdr), getpagesize()));
-}
-
-static void
-setup_data(void)
-{
- int size, offset;
- ztest_shared_hdr_t *hdr;
- uint8_t *buf;
-
- hdr = (void *)mmap(0, P2ROUNDUP(sizeof (*hdr), getpagesize()),
- PROT_READ, MAP_SHARED, ztest_fd_data, 0);
- ASSERT(hdr != MAP_FAILED);
-
- size = shared_data_size(hdr);
-
- (void) munmap((caddr_t)hdr, P2ROUNDUP(sizeof (*hdr), getpagesize()));
- hdr = ztest_shared_hdr = (void *)mmap(0, P2ROUNDUP(size, getpagesize()),
- PROT_READ | PROT_WRITE, MAP_SHARED, ztest_fd_data, 0);
- ASSERT(hdr != MAP_FAILED);
- buf = (uint8_t *)hdr;
-
- offset = hdr->zh_hdr_size;
- ztest_shared_opts = (void *)&buf[offset];
- offset += hdr->zh_opts_size;
- ztest_shared = (void *)&buf[offset];
- offset += hdr->zh_size;
- ztest_shared_callstate = (void *)&buf[offset];
- offset += hdr->zh_stats_size * hdr->zh_stats_count;
- ztest_shared_ds = (void *)&buf[offset];
-}
-
-static boolean_t
-exec_child(char *cmd, char *libpath, boolean_t ignorekill, int *statusp)
-{
- pid_t pid;
- int status;
- char *cmdbuf = NULL;
-
- pid = fork();
-
- if (cmd == NULL) {
- cmdbuf = umem_alloc(MAXPATHLEN, UMEM_NOFAIL);
- (void) strlcpy(cmdbuf, getexecname(), MAXPATHLEN);
- cmd = cmdbuf;
- }
-
- if (pid == -1)
- fatal(1, "fork failed");
-
- if (pid == 0) { /* child */
- char *emptyargv[2] = { cmd, NULL };
- char fd_data_str[12];
-
- struct rlimit rl = { 1024, 1024 };
- (void) setrlimit(RLIMIT_NOFILE, &rl);
-
- (void) close(ztest_fd_rand);
- VERIFY3U(11, >=,
- snprintf(fd_data_str, 12, "%d", ztest_fd_data));
- VERIFY0(setenv("ZTEST_FD_DATA", fd_data_str, 1));
-
- (void) enable_extended_FILE_stdio(-1, -1);
- if (libpath != NULL)
- VERIFY(0 == setenv("LD_LIBRARY_PATH", libpath, 1));
-#ifdef illumos
- (void) execv(cmd, emptyargv);
-#else
- (void) execvp(cmd, emptyargv);
-#endif
- ztest_dump_core = B_FALSE;
- fatal(B_TRUE, "exec failed: %s", cmd);
- }
-
- if (cmdbuf != NULL) {
- umem_free(cmdbuf, MAXPATHLEN);
- cmd = NULL;
- }
-
- while (waitpid(pid, &status, 0) != pid)
- continue;
- if (statusp != NULL)
- *statusp = status;
-
- if (WIFEXITED(status)) {
- if (WEXITSTATUS(status) != 0) {
- (void) fprintf(stderr, "child exited with code %d\n",
- WEXITSTATUS(status));
- exit(2);
- }
- return (B_FALSE);
- } else if (WIFSIGNALED(status)) {
- if (!ignorekill || WTERMSIG(status) != SIGKILL) {
- (void) fprintf(stderr, "child died with signal %d\n",
- WTERMSIG(status));
- exit(3);
- }
- return (B_TRUE);
- } else {
- (void) fprintf(stderr, "something strange happened to child\n");
- exit(4);
- /* NOTREACHED */
- }
-}
-
-static void
-ztest_run_init(void)
-{
- ztest_shared_t *zs = ztest_shared;
-
- /*
- * Blow away any existing copy of zpool.cache
- */
- (void) remove(spa_config_path);
-
- if (ztest_opts.zo_init == 0) {
- if (ztest_opts.zo_verbose >= 1)
- (void) printf("Importing pool %s\n",
- ztest_opts.zo_pool);
- ztest_import(zs);
- return;
- }
-
- /*
- * Create and initialize our storage pool.
- */
- for (int i = 1; i <= ztest_opts.zo_init; i++) {
- bzero(zs, sizeof (ztest_shared_t));
- if (ztest_opts.zo_verbose >= 3 &&
- ztest_opts.zo_init != 1) {
- (void) printf("ztest_init(), pass %d\n", i);
- }
- ztest_init(zs);
- }
-}
-
-int
-main(int argc, char **argv)
-{
- int kills = 0;
- int iters = 0;
- int older = 0;
- int newer = 0;
- ztest_shared_t *zs;
- ztest_info_t *zi;
- ztest_shared_callstate_t *zc;
- char timebuf[100];
- char numbuf[NN_NUMBUF_SZ];
- char *cmd;
- boolean_t hasalt;
- char *fd_data_str = getenv("ZTEST_FD_DATA");
-
- (void) setvbuf(stdout, NULL, _IOLBF, 0);
-
- dprintf_setup(&argc, argv);
- zfs_deadman_synctime_ms = 300000;
- /*
- * As two-word space map entries may not come up often (especially
- * if pool and vdev sizes are small) we want to force at least some
- * of them so the feature get tested.
- */
- zfs_force_some_double_word_sm_entries = B_TRUE;
-
- /*
- * Verify that even extensively damaged split blocks with many
- * segments can be reconstructed in a reasonable amount of time
- * when reconstruction is known to be possible.
- */
- zfs_reconstruct_indirect_damage_fraction = 4;
-
- ztest_fd_rand = open("/dev/urandom", O_RDONLY);
- ASSERT3S(ztest_fd_rand, >=, 0);
-
- if (!fd_data_str) {
- process_options(argc, argv);
-
- setup_data_fd();
- setup_hdr();
- setup_data();
- bcopy(&ztest_opts, ztest_shared_opts,
- sizeof (*ztest_shared_opts));
- } else {
- ztest_fd_data = atoi(fd_data_str);
- setup_data();
- bcopy(ztest_shared_opts, &ztest_opts, sizeof (ztest_opts));
- }
- ASSERT3U(ztest_opts.zo_datasets, ==, ztest_shared_hdr->zh_ds_count);
-
- /* Override location of zpool.cache */
- VERIFY3U(asprintf((char **)&spa_config_path, "%s/zpool.cache",
- ztest_opts.zo_dir), !=, -1);
-
- ztest_ds = umem_alloc(ztest_opts.zo_datasets * sizeof (ztest_ds_t),
- UMEM_NOFAIL);
- zs = ztest_shared;
-
- if (fd_data_str) {
- metaslab_force_ganging = ztest_opts.zo_metaslab_force_ganging;
- metaslab_df_alloc_threshold =
- zs->zs_metaslab_df_alloc_threshold;
-
- if (zs->zs_do_init)
- ztest_run_init();
- else
- ztest_run(zs);
- exit(0);
- }
-
- hasalt = (strlen(ztest_opts.zo_alt_ztest) != 0);
-
- if (ztest_opts.zo_verbose >= 1) {
- (void) printf("%llu vdevs, %d datasets, %d threads,"
- " %llu seconds...\n",
- (u_longlong_t)ztest_opts.zo_vdevs,
- ztest_opts.zo_datasets,
- ztest_opts.zo_threads,
- (u_longlong_t)ztest_opts.zo_time);
- }
-
- cmd = umem_alloc(MAXNAMELEN, UMEM_NOFAIL);
- (void) strlcpy(cmd, getexecname(), MAXNAMELEN);
-
- zs->zs_do_init = B_TRUE;
- if (strlen(ztest_opts.zo_alt_ztest) != 0) {
- if (ztest_opts.zo_verbose >= 1) {
- (void) printf("Executing older ztest for "
- "initialization: %s\n", ztest_opts.zo_alt_ztest);
- }
- VERIFY(!exec_child(ztest_opts.zo_alt_ztest,
- ztest_opts.zo_alt_libpath, B_FALSE, NULL));
- } else {
- VERIFY(!exec_child(NULL, NULL, B_FALSE, NULL));
- }
- zs->zs_do_init = B_FALSE;
-
- zs->zs_proc_start = gethrtime();
- zs->zs_proc_stop = zs->zs_proc_start + ztest_opts.zo_time * NANOSEC;
-
- for (int f = 0; f < ZTEST_FUNCS; f++) {
- zi = &ztest_info[f];
- zc = ZTEST_GET_SHARED_CALLSTATE(f);
- if (zs->zs_proc_start + zi->zi_interval[0] > zs->zs_proc_stop)
- zc->zc_next = UINT64_MAX;
- else
- zc->zc_next = zs->zs_proc_start +
- ztest_random(2 * zi->zi_interval[0] + 1);
- }
-
- /*
- * Run the tests in a loop. These tests include fault injection
- * to verify that self-healing data works, and forced crashes
- * to verify that we never lose on-disk consistency.
- */
- while (gethrtime() < zs->zs_proc_stop) {
- int status;
- boolean_t killed;
-
- /*
- * Initialize the workload counters for each function.
- */
- for (int f = 0; f < ZTEST_FUNCS; f++) {
- zc = ZTEST_GET_SHARED_CALLSTATE(f);
- zc->zc_count = 0;
- zc->zc_time = 0;
- }
-
- /* Set the allocation switch size */
- zs->zs_metaslab_df_alloc_threshold =
- ztest_random(zs->zs_metaslab_sz / 4) + 1;
-
- if (!hasalt || ztest_random(2) == 0) {
- if (hasalt && ztest_opts.zo_verbose >= 1) {
- (void) printf("Executing newer ztest: %s\n",
- cmd);
- }
- newer++;
- killed = exec_child(cmd, NULL, B_TRUE, &status);
- } else {
- if (hasalt && ztest_opts.zo_verbose >= 1) {
- (void) printf("Executing older ztest: %s\n",
- ztest_opts.zo_alt_ztest);
- }
- older++;
- killed = exec_child(ztest_opts.zo_alt_ztest,
- ztest_opts.zo_alt_libpath, B_TRUE, &status);
- }
-
- if (killed)
- kills++;
- iters++;
-
- if (ztest_opts.zo_verbose >= 1) {
- hrtime_t now = gethrtime();
-
- now = MIN(now, zs->zs_proc_stop);
- print_time(zs->zs_proc_stop - now, timebuf);
- nicenum(zs->zs_space, numbuf, sizeof (numbuf));
-
- (void) printf("Pass %3d, %8s, %3llu ENOSPC, "
- "%4.1f%% of %5s used, %3.0f%% done, %8s to go\n",
- iters,
- WIFEXITED(status) ? "Complete" : "SIGKILL",
- (u_longlong_t)zs->zs_enospc_count,
- 100.0 * zs->zs_alloc / zs->zs_space,
- numbuf,
- 100.0 * (now - zs->zs_proc_start) /
- (ztest_opts.zo_time * NANOSEC), timebuf);
- }
-
- if (ztest_opts.zo_verbose >= 2) {
- (void) printf("\nWorkload summary:\n\n");
- (void) printf("%7s %9s %s\n",
- "Calls", "Time", "Function");
- (void) printf("%7s %9s %s\n",
- "-----", "----", "--------");
- for (int f = 0; f < ZTEST_FUNCS; f++) {
- Dl_info dli;
-
- zi = &ztest_info[f];
- zc = ZTEST_GET_SHARED_CALLSTATE(f);
- print_time(zc->zc_time, timebuf);
- (void) dladdr((void *)zi->zi_func, &dli);
- (void) printf("%7llu %9s %s\n",
- (u_longlong_t)zc->zc_count, timebuf,
- dli.dli_sname);
- }
- (void) printf("\n");
- }
-
- if (!ztest_opts.zo_mmp_test)
- ztest_run_zdb(ztest_opts.zo_pool);
- }
-
- if (ztest_opts.zo_verbose >= 1) {
- if (hasalt) {
- (void) printf("%d runs of older ztest: %s\n", older,
- ztest_opts.zo_alt_ztest);
- (void) printf("%d runs of newer ztest: %s\n", newer,
- cmd);
- }
- (void) printf("%d killed, %d completed, %.0f%% kill rate\n",
- kills, iters - kills, (100.0 * kills) / MAX(1, iters));
- }
-
- umem_free(cmd, MAXNAMELEN);
-
- return (0);
-}
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/drti.c b/cddl/contrib/opensolaris/lib/libdtrace/common/drti.c
index 836eeccb8274a..a66661fd9ab87 100644
--- a/cddl/contrib/opensolaris/lib/libdtrace/common/drti.c
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/drti.c
@@ -24,6 +24,7 @@
* Use is subject to license terms.
*/
+#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <dlfcn.h>
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c
index 05f2785e6600f..8f32890057f02 100644
--- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_link.c
@@ -31,6 +31,7 @@
#include <assert.h>
#include <elf.h>
+#include <sys/types.h>
#include <fcntl.h>
#include <gelf.h>
#include <limits.h>
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c
index 0a3a10a769547..97da0c3a5ac28 100644
--- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_print.c
@@ -77,7 +77,6 @@
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <arpa/nameser.h>
#include <dt_module.h>
#include <dt_printf.h>
diff --git a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.c b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.c
index f7b4684b01d0f..57a7db4ad0fd5 100644
--- a/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.c
+++ b/cddl/contrib/opensolaris/lib/libdtrace/common/dt_printf.c
@@ -44,12 +44,19 @@
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <arpa/nameser.h>
-
+#include <sys/byteorder.h>
#include <dt_printf.h>
#include <dt_string.h>
#include <dt_impl.h>
+#ifndef NS_IN6ADDRSZ
+#define NS_IN6ADDRSZ 16
+#endif
+
+#ifndef NS_INADDRSZ
+#define NS_INADDRSZ 4
+#endif
+
/*ARGSUSED*/
static int
pfcheck_addr(dt_pfargv_t *pfv, dt_pfargd_t *pfd, dt_node_t *dnp)
diff --git a/cddl/contrib/opensolaris/lib/libnvpair/libnvpair.c b/cddl/contrib/opensolaris/lib/libnvpair/libnvpair.c
deleted file mode 100644
index c6fbfe97a9af5..0000000000000
--- a/cddl/contrib/opensolaris/lib/libnvpair/libnvpair.c
+++ /dev/null
@@ -1,1286 +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) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2012 by Delphix. All rights reserved.
- */
-
-#include <solaris.h>
-#include <inttypes.h>
-#include <unistd.h>
-#include <string.h>
-#include <libintl.h>
-#include <stdarg.h>
-#include "libnvpair.h"
-
-/*
- * libnvpair - A tools library for manipulating <name, value> pairs.
- *
- * This library provides routines packing an unpacking nv pairs
- * for transporting data across process boundaries, transporting
- * between kernel and userland, and possibly saving onto disk files.
- */
-
-/*
- * Print control structure.
- */
-
-#define DEFINEOP(opname, vtype) \
- struct { \
- int (*op)(struct nvlist_prtctl *, void *, nvlist_t *, \
- const char *, vtype); \
- void *arg; \
- } opname
-
-#define DEFINEARROP(opname, vtype) \
- struct { \
- int (*op)(struct nvlist_prtctl *, void *, nvlist_t *, \
- const char *, vtype, uint_t); \
- void *arg; \
- } opname
-
-struct nvlist_printops {
- DEFINEOP(print_boolean, int);
- DEFINEOP(print_boolean_value, boolean_t);
- DEFINEOP(print_byte, uchar_t);
- DEFINEOP(print_int8, int8_t);
- DEFINEOP(print_uint8, uint8_t);
- DEFINEOP(print_int16, int16_t);
- DEFINEOP(print_uint16, uint16_t);
- DEFINEOP(print_int32, int32_t);
- DEFINEOP(print_uint32, uint32_t);
- DEFINEOP(print_int64, int64_t);
- DEFINEOP(print_uint64, uint64_t);
- DEFINEOP(print_double, double);
- DEFINEOP(print_string, char *);
- DEFINEOP(print_hrtime, hrtime_t);
- DEFINEOP(print_nvlist, nvlist_t *);
- DEFINEARROP(print_boolean_array, boolean_t *);
- DEFINEARROP(print_byte_array, uchar_t *);
- DEFINEARROP(print_int8_array, int8_t *);
- DEFINEARROP(print_uint8_array, uint8_t *);
- DEFINEARROP(print_int16_array, int16_t *);
- DEFINEARROP(print_uint16_array, uint16_t *);
- DEFINEARROP(print_int32_array, int32_t *);
- DEFINEARROP(print_uint32_array, uint32_t *);
- DEFINEARROP(print_int64_array, int64_t *);
- DEFINEARROP(print_uint64_array, uint64_t *);
- DEFINEARROP(print_string_array, char **);
- DEFINEARROP(print_nvlist_array, nvlist_t **);
-};
-
-struct nvlist_prtctl {
- FILE *nvprt_fp; /* output destination */
- enum nvlist_indent_mode nvprt_indent_mode; /* see above */
- int nvprt_indent; /* absolute indent, or tab depth */
- int nvprt_indentinc; /* indent or tab increment */
- const char *nvprt_nmfmt; /* member name format, max one %s */
- const char *nvprt_eomfmt; /* after member format, e.g. "\n" */
- const char *nvprt_btwnarrfmt; /* between array members */
- int nvprt_btwnarrfmt_nl; /* nvprt_eoamfmt includes newline? */
- struct nvlist_printops *nvprt_dfltops;
- struct nvlist_printops *nvprt_custops;
-};
-
-#define DFLTPRTOP(pctl, type) \
- ((pctl)->nvprt_dfltops->print_##type.op)
-
-#define DFLTPRTOPARG(pctl, type) \
- ((pctl)->nvprt_dfltops->print_##type.arg)
-
-#define CUSTPRTOP(pctl, type) \
- ((pctl)->nvprt_custops->print_##type.op)
-
-#define CUSTPRTOPARG(pctl, type) \
- ((pctl)->nvprt_custops->print_##type.arg)
-
-#define RENDER(pctl, type, nvl, name, val) \
- { \
- int done = 0; \
- if ((pctl)->nvprt_custops && CUSTPRTOP(pctl, type)) { \
- done = CUSTPRTOP(pctl, type)(pctl, \
- CUSTPRTOPARG(pctl, type), nvl, name, val); \
- } \
- if (!done) { \
- (void) DFLTPRTOP(pctl, type)(pctl, \
- DFLTPRTOPARG(pctl, type), nvl, name, val); \
- } \
- (void) fprintf(pctl->nvprt_fp, pctl->nvprt_eomfmt); \
- }
-
-#define ARENDER(pctl, type, nvl, name, arrp, count) \
- { \
- int done = 0; \
- if ((pctl)->nvprt_custops && CUSTPRTOP(pctl, type)) { \
- done = CUSTPRTOP(pctl, type)(pctl, \
- CUSTPRTOPARG(pctl, type), nvl, name, arrp, count); \
- } \
- if (!done) { \
- (void) DFLTPRTOP(pctl, type)(pctl, \
- DFLTPRTOPARG(pctl, type), nvl, name, arrp, count); \
- } \
- (void) fprintf(pctl->nvprt_fp, pctl->nvprt_eomfmt); \
- }
-
-static void nvlist_print_with_indent(nvlist_t *, nvlist_prtctl_t);
-
-/*
- * ======================================================================
- * | |
- * | Indentation |
- * | |
- * ======================================================================
- */
-
-static void
-indent(nvlist_prtctl_t pctl, int onemore)
-{
- int depth;
-
- switch (pctl->nvprt_indent_mode) {
- case NVLIST_INDENT_ABS:
- (void) fprintf(pctl->nvprt_fp, "%*s",
- pctl->nvprt_indent + onemore * pctl->nvprt_indentinc, "");
- break;
-
- case NVLIST_INDENT_TABBED:
- depth = pctl->nvprt_indent + onemore;
- while (depth-- > 0)
- (void) fprintf(pctl->nvprt_fp, "\t");
- }
-}
-
-/*
- * ======================================================================
- * | |
- * | Default nvlist member rendering functions. |
- * | |
- * ======================================================================
- */
-
-/*
- * Generate functions to print single-valued nvlist members.
- *
- * type_and_variant - suffix to form function name
- * vtype - C type for the member value
- * ptype - C type to cast value to for printing
- * vfmt - format string for pair value, e.g "%d" or "0x%llx"
- */
-
-#define NVLIST_PRTFUNC(type_and_variant, vtype, ptype, vfmt) \
-static int \
-nvprint_##type_and_variant(nvlist_prtctl_t pctl, void *private, \
- nvlist_t *nvl, const char *name, vtype value) \
-{ \
- FILE *fp = pctl->nvprt_fp; \
- NOTE(ARGUNUSED(private)) \
- NOTE(ARGUNUSED(nvl)) \
- indent(pctl, 1); \
- (void) fprintf(fp, pctl->nvprt_nmfmt, name); \
- (void) fprintf(fp, vfmt, (ptype)value); \
- return (1); \
-}
-
-NVLIST_PRTFUNC(boolean, int, int, "%d")
-NVLIST_PRTFUNC(boolean_value, boolean_t, int, "%d")
-NVLIST_PRTFUNC(byte, uchar_t, uchar_t, "0x%2.2x")
-NVLIST_PRTFUNC(int8, int8_t, int, "%d")
-NVLIST_PRTFUNC(uint8, uint8_t, uint8_t, "0x%x")
-NVLIST_PRTFUNC(int16, int16_t, int16_t, "%d")
-NVLIST_PRTFUNC(uint16, uint16_t, uint16_t, "0x%x")
-NVLIST_PRTFUNC(int32, int32_t, int32_t, "%d")
-NVLIST_PRTFUNC(uint32, uint32_t, uint32_t, "0x%x")
-NVLIST_PRTFUNC(int64, int64_t, longlong_t, "%lld")
-NVLIST_PRTFUNC(uint64, uint64_t, u_longlong_t, "0x%llx")
-NVLIST_PRTFUNC(double, double, double, "0x%f")
-NVLIST_PRTFUNC(string, char *, char *, "%s")
-NVLIST_PRTFUNC(hrtime, hrtime_t, hrtime_t, "0x%llx")
-
-/*
- * Generate functions to print array-valued nvlist members.
- */
-
-#define NVLIST_ARRPRTFUNC(type_and_variant, vtype, ptype, vfmt) \
-static int \
-nvaprint_##type_and_variant(nvlist_prtctl_t pctl, void *private, \
- nvlist_t *nvl, const char *name, vtype *valuep, uint_t count) \
-{ \
- FILE *fp = pctl->nvprt_fp; \
- uint_t i; \
- NOTE(ARGUNUSED(private)) \
- NOTE(ARGUNUSED(nvl)) \
- for (i = 0; i < count; i++) { \
- if (i == 0 || pctl->nvprt_btwnarrfmt_nl) { \
- indent(pctl, 1); \
- (void) fprintf(fp, pctl->nvprt_nmfmt, name); \
- if (pctl->nvprt_btwnarrfmt_nl) \
- (void) fprintf(fp, "[%d]: ", i); \
- } \
- if (i != 0) \
- (void) fprintf(fp, pctl->nvprt_btwnarrfmt); \
- (void) fprintf(fp, vfmt, (ptype)valuep[i]); \
- } \
- return (1); \
-}
-
-NVLIST_ARRPRTFUNC(boolean_array, boolean_t, boolean_t, "%d")
-NVLIST_ARRPRTFUNC(byte_array, uchar_t, uchar_t, "0x%2.2x")
-NVLIST_ARRPRTFUNC(int8_array, int8_t, int8_t, "%d")
-NVLIST_ARRPRTFUNC(uint8_array, uint8_t, uint8_t, "0x%x")
-NVLIST_ARRPRTFUNC(int16_array, int16_t, int16_t, "%d")
-NVLIST_ARRPRTFUNC(uint16_array, uint16_t, uint16_t, "0x%x")
-NVLIST_ARRPRTFUNC(int32_array, int32_t, int32_t, "%d")
-NVLIST_ARRPRTFUNC(uint32_array, uint32_t, uint32_t, "0x%x")
-NVLIST_ARRPRTFUNC(int64_array, int64_t, longlong_t, "%lld")
-NVLIST_ARRPRTFUNC(uint64_array, uint64_t, u_longlong_t, "0x%llx")
-NVLIST_ARRPRTFUNC(string_array, char *, char *, "%s")
-
-/*ARGSUSED*/
-static int
-nvprint_nvlist(nvlist_prtctl_t pctl, void *private,
- nvlist_t *nvl, const char *name, nvlist_t *value)
-{
- FILE *fp = pctl->nvprt_fp;
-
- indent(pctl, 1);
- (void) fprintf(fp, "%s = (embedded nvlist)\n", name);
-
- pctl->nvprt_indent += pctl->nvprt_indentinc;
- nvlist_print_with_indent(value, pctl);
- pctl->nvprt_indent -= pctl->nvprt_indentinc;
-
- indent(pctl, 1);
- (void) fprintf(fp, "(end %s)\n", name);
-
- return (1);
-}
-
-/*ARGSUSED*/
-static int
-nvaprint_nvlist_array(nvlist_prtctl_t pctl, void *private,
- nvlist_t *nvl, const char *name, nvlist_t **valuep, uint_t count)
-{
- FILE *fp = pctl->nvprt_fp;
- uint_t i;
-
- indent(pctl, 1);
- (void) fprintf(fp, "%s = (array of embedded nvlists)\n", name);
-
- for (i = 0; i < count; i++) {
- indent(pctl, 1);
- (void) fprintf(fp, "(start %s[%d])\n", name, i);
-
- pctl->nvprt_indent += pctl->nvprt_indentinc;
- nvlist_print_with_indent(valuep[i], pctl);
- pctl->nvprt_indent -= pctl->nvprt_indentinc;
-
- indent(pctl, 1);
- (void) fprintf(fp, "(end %s[%d])\n", name, i);
- }
-
- return (1);
-}
-
-/*
- * ======================================================================
- * | |
- * | Interfaces that allow control over formatting. |
- * | |
- * ======================================================================
- */
-
-void
-nvlist_prtctl_setdest(nvlist_prtctl_t pctl, FILE *fp)
-{
- pctl->nvprt_fp = fp;
-}
-
-FILE *
-nvlist_prtctl_getdest(nvlist_prtctl_t pctl)
-{
- return (pctl->nvprt_fp);
-}
-
-
-void
-nvlist_prtctl_setindent(nvlist_prtctl_t pctl, enum nvlist_indent_mode mode,
- int start, int inc)
-{
- if (mode < NVLIST_INDENT_ABS || mode > NVLIST_INDENT_TABBED)
- mode = NVLIST_INDENT_TABBED;
-
- if (start < 0)
- start = 0;
-
- if (inc < 0)
- inc = 1;
-
- pctl->nvprt_indent_mode = mode;
- pctl->nvprt_indent = start;
- pctl->nvprt_indentinc = inc;
-}
-
-void
-nvlist_prtctl_doindent(nvlist_prtctl_t pctl, int onemore)
-{
- indent(pctl, onemore);
-}
-
-
-void
-nvlist_prtctl_setfmt(nvlist_prtctl_t pctl, enum nvlist_prtctl_fmt which,
- const char *fmt)
-{
- switch (which) {
- case NVLIST_FMT_MEMBER_NAME:
- if (fmt == NULL)
- fmt = "%s = ";
- pctl->nvprt_nmfmt = fmt;
- break;
-
- case NVLIST_FMT_MEMBER_POSTAMBLE:
- if (fmt == NULL)
- fmt = "\n";
- pctl->nvprt_eomfmt = fmt;
- break;
-
- case NVLIST_FMT_BTWN_ARRAY:
- if (fmt == NULL) {
- pctl->nvprt_btwnarrfmt = " ";
- pctl->nvprt_btwnarrfmt_nl = 0;
- } else {
- pctl->nvprt_btwnarrfmt = fmt;
- pctl->nvprt_btwnarrfmt_nl = (strstr(fmt, "\n") != NULL);
- }
- break;
-
- default:
- break;
- }
-}
-
-
-void
-nvlist_prtctl_dofmt(nvlist_prtctl_t pctl, enum nvlist_prtctl_fmt which, ...)
-{
- FILE *fp = pctl->nvprt_fp;
- va_list ap;
- char *name;
-
- va_start(ap, which);
-
- switch (which) {
- case NVLIST_FMT_MEMBER_NAME:
- name = va_arg(ap, char *);
- (void) fprintf(fp, pctl->nvprt_nmfmt, name);
- break;
-
- case NVLIST_FMT_MEMBER_POSTAMBLE:
- (void) fprintf(fp, pctl->nvprt_eomfmt);
- break;
-
- case NVLIST_FMT_BTWN_ARRAY:
- (void) fprintf(fp, pctl->nvprt_btwnarrfmt); \
- break;
-
- default:
- break;
- }
-
- va_end(ap);
-}
-
-/*
- * ======================================================================
- * | |
- * | Interfaces to allow appointment of replacement rendering functions.|
- * | |
- * ======================================================================
- */
-
-#define NVLIST_PRINTCTL_REPLACE(type, vtype) \
-void \
-nvlist_prtctlop_##type(nvlist_prtctl_t pctl, \
- int (*func)(nvlist_prtctl_t, void *, nvlist_t *, const char *, vtype), \
- void *private) \
-{ \
- CUSTPRTOP(pctl, type) = func; \
- CUSTPRTOPARG(pctl, type) = private; \
-}
-
-NVLIST_PRINTCTL_REPLACE(boolean, int)
-NVLIST_PRINTCTL_REPLACE(boolean_value, boolean_t)
-NVLIST_PRINTCTL_REPLACE(byte, uchar_t)
-NVLIST_PRINTCTL_REPLACE(int8, int8_t)
-NVLIST_PRINTCTL_REPLACE(uint8, uint8_t)
-NVLIST_PRINTCTL_REPLACE(int16, int16_t)
-NVLIST_PRINTCTL_REPLACE(uint16, uint16_t)
-NVLIST_PRINTCTL_REPLACE(int32, int32_t)
-NVLIST_PRINTCTL_REPLACE(uint32, uint32_t)
-NVLIST_PRINTCTL_REPLACE(int64, int64_t)
-NVLIST_PRINTCTL_REPLACE(uint64, uint64_t)
-NVLIST_PRINTCTL_REPLACE(double, double)
-NVLIST_PRINTCTL_REPLACE(string, char *)
-NVLIST_PRINTCTL_REPLACE(hrtime, hrtime_t)
-NVLIST_PRINTCTL_REPLACE(nvlist, nvlist_t *)
-
-#define NVLIST_PRINTCTL_AREPLACE(type, vtype) \
-void \
-nvlist_prtctlop_##type(nvlist_prtctl_t pctl, \
- int (*func)(nvlist_prtctl_t, void *, nvlist_t *, const char *, vtype, \
- uint_t), void *private) \
-{ \
- CUSTPRTOP(pctl, type) = func; \
- CUSTPRTOPARG(pctl, type) = private; \
-}
-
-NVLIST_PRINTCTL_AREPLACE(boolean_array, boolean_t *)
-NVLIST_PRINTCTL_AREPLACE(byte_array, uchar_t *)
-NVLIST_PRINTCTL_AREPLACE(int8_array, int8_t *)
-NVLIST_PRINTCTL_AREPLACE(uint8_array, uint8_t *)
-NVLIST_PRINTCTL_AREPLACE(int16_array, int16_t *)
-NVLIST_PRINTCTL_AREPLACE(uint16_array, uint16_t *)
-NVLIST_PRINTCTL_AREPLACE(int32_array, int32_t *)
-NVLIST_PRINTCTL_AREPLACE(uint32_array, uint32_t *)
-NVLIST_PRINTCTL_AREPLACE(int64_array, int64_t *)
-NVLIST_PRINTCTL_AREPLACE(uint64_array, uint64_t *)
-NVLIST_PRINTCTL_AREPLACE(string_array, char **)
-NVLIST_PRINTCTL_AREPLACE(nvlist_array, nvlist_t **)
-
-/*
- * ======================================================================
- * | |
- * | Interfaces to manage nvlist_prtctl_t cookies. |
- * | |
- * ======================================================================
- */
-
-
-static const struct nvlist_printops defprtops = {
- { nvprint_boolean, NULL },
- { nvprint_boolean_value, NULL },
- { nvprint_byte, NULL },
- { nvprint_int8, NULL },
- { nvprint_uint8, NULL },
- { nvprint_int16, NULL },
- { nvprint_uint16, NULL },
- { nvprint_int32, NULL },
- { nvprint_uint32, NULL },
- { nvprint_int64, NULL },
- { nvprint_uint64, NULL },
- { nvprint_double, NULL },
- { nvprint_string, NULL },
- { nvprint_hrtime, NULL },
- { nvprint_nvlist, NULL },
- { nvaprint_boolean_array, NULL },
- { nvaprint_byte_array, NULL },
- { nvaprint_int8_array, NULL },
- { nvaprint_uint8_array, NULL },
- { nvaprint_int16_array, NULL },
- { nvaprint_uint16_array, NULL },
- { nvaprint_int32_array, NULL },
- { nvaprint_uint32_array, NULL },
- { nvaprint_int64_array, NULL },
- { nvaprint_uint64_array, NULL },
- { nvaprint_string_array, NULL },
- { nvaprint_nvlist_array, NULL },
-};
-
-static void
-prtctl_defaults(FILE *fp, struct nvlist_prtctl *pctl,
- struct nvlist_printops *ops)
-{
- pctl->nvprt_fp = fp;
- pctl->nvprt_indent_mode = NVLIST_INDENT_TABBED;
- pctl->nvprt_indent = 0;
- pctl->nvprt_indentinc = 1;
- pctl->nvprt_nmfmt = "%s = ";
- pctl->nvprt_eomfmt = "\n";
- pctl->nvprt_btwnarrfmt = " ";
- pctl->nvprt_btwnarrfmt_nl = 0;
-
- pctl->nvprt_dfltops = (struct nvlist_printops *)&defprtops;
- pctl->nvprt_custops = ops;
-}
-
-nvlist_prtctl_t
-nvlist_prtctl_alloc(void)
-{
- struct nvlist_prtctl *pctl;
- struct nvlist_printops *ops;
-
- if ((pctl = malloc(sizeof (*pctl))) == NULL)
- return (NULL);
-
- if ((ops = calloc(1, sizeof (*ops))) == NULL) {
- free(pctl);
- return (NULL);
- }
-
- prtctl_defaults(stdout, pctl, ops);
-
- return (pctl);
-}
-
-void
-nvlist_prtctl_free(nvlist_prtctl_t pctl)
-{
- if (pctl != NULL) {
- free(pctl->nvprt_custops);
- free(pctl);
- }
-}
-
-/*
- * ======================================================================
- * | |
- * | Top-level print request interfaces. |
- * | |
- * ======================================================================
- */
-
-/*
- * nvlist_print - Prints elements in an event buffer
- */
-static void
-nvlist_print_with_indent(nvlist_t *nvl, nvlist_prtctl_t pctl)
-{
- FILE *fp = pctl->nvprt_fp;
- char *name;
- uint_t nelem;
- nvpair_t *nvp;
-
- if (nvl == NULL)
- return;
-
- indent(pctl, 0);
- (void) fprintf(fp, "nvlist version: %d\n", NVL_VERSION(nvl));
-
- nvp = nvlist_next_nvpair(nvl, NULL);
-
- while (nvp) {
- data_type_t type = nvpair_type(nvp);
-
- name = nvpair_name(nvp);
- nelem = 0;
-
- switch (type) {
- case DATA_TYPE_BOOLEAN: {
- RENDER(pctl, boolean, nvl, name, 1);
- break;
- }
- case DATA_TYPE_BOOLEAN_VALUE: {
- boolean_t val;
- (void) nvpair_value_boolean_value(nvp, &val);
- RENDER(pctl, boolean_value, nvl, name, val);
- break;
- }
- case DATA_TYPE_BYTE: {
- uchar_t val;
- (void) nvpair_value_byte(nvp, &val);
- RENDER(pctl, byte, nvl, name, val);
- break;
- }
- case DATA_TYPE_INT8: {
- int8_t val;
- (void) nvpair_value_int8(nvp, &val);
- RENDER(pctl, int8, nvl, name, val);
- break;
- }
- case DATA_TYPE_UINT8: {
- uint8_t val;
- (void) nvpair_value_uint8(nvp, &val);
- RENDER(pctl, uint8, nvl, name, val);
- break;
- }
- case DATA_TYPE_INT16: {
- int16_t val;
- (void) nvpair_value_int16(nvp, &val);
- RENDER(pctl, int16, nvl, name, val);
- break;
- }
- case DATA_TYPE_UINT16: {
- uint16_t val;
- (void) nvpair_value_uint16(nvp, &val);
- RENDER(pctl, uint16, nvl, name, val);
- break;
- }
- case DATA_TYPE_INT32: {
- int32_t val;
- (void) nvpair_value_int32(nvp, &val);
- RENDER(pctl, int32, nvl, name, val);
- break;
- }
- case DATA_TYPE_UINT32: {
- uint32_t val;
- (void) nvpair_value_uint32(nvp, &val);
- RENDER(pctl, uint32, nvl, name, val);
- break;
- }
- case DATA_TYPE_INT64: {
- int64_t val;
- (void) nvpair_value_int64(nvp, &val);
- RENDER(pctl, int64, nvl, name, val);
- break;
- }
- case DATA_TYPE_UINT64: {
- uint64_t val;
- (void) nvpair_value_uint64(nvp, &val);
- RENDER(pctl, uint64, nvl, name, val);
- break;
- }
- case DATA_TYPE_DOUBLE: {
- double val;
- (void) nvpair_value_double(nvp, &val);
- RENDER(pctl, double, nvl, name, val);
- break;
- }
- case DATA_TYPE_STRING: {
- char *val;
- (void) nvpair_value_string(nvp, &val);
- RENDER(pctl, string, nvl, name, val);
- break;
- }
- case DATA_TYPE_BOOLEAN_ARRAY: {
- boolean_t *val;
- (void) nvpair_value_boolean_array(nvp, &val, &nelem);
- ARENDER(pctl, boolean_array, nvl, name, val, nelem);
- break;
- }
- case DATA_TYPE_BYTE_ARRAY: {
- uchar_t *val;
- (void) nvpair_value_byte_array(nvp, &val, &nelem);
- ARENDER(pctl, byte_array, nvl, name, val, nelem);
- break;
- }
- case DATA_TYPE_INT8_ARRAY: {
- int8_t *val;
- (void) nvpair_value_int8_array(nvp, &val, &nelem);
- ARENDER(pctl, int8_array, nvl, name, val, nelem);
- break;
- }
- case DATA_TYPE_UINT8_ARRAY: {
- uint8_t *val;
- (void) nvpair_value_uint8_array(nvp, &val, &nelem);
- ARENDER(pctl, uint8_array, nvl, name, val, nelem);
- break;
- }
- case DATA_TYPE_INT16_ARRAY: {
- int16_t *val;
- (void) nvpair_value_int16_array(nvp, &val, &nelem);
- ARENDER(pctl, int16_array, nvl, name, val, nelem);
- break;
- }
- case DATA_TYPE_UINT16_ARRAY: {
- uint16_t *val;
- (void) nvpair_value_uint16_array(nvp, &val, &nelem);
- ARENDER(pctl, uint16_array, nvl, name, val, nelem);
- break;
- }
- case DATA_TYPE_INT32_ARRAY: {
- int32_t *val;
- (void) nvpair_value_int32_array(nvp, &val, &nelem);
- ARENDER(pctl, int32_array, nvl, name, val, nelem);
- break;
- }
- case DATA_TYPE_UINT32_ARRAY: {
- uint32_t *val;
- (void) nvpair_value_uint32_array(nvp, &val, &nelem);
- ARENDER(pctl, uint32_array, nvl, name, val, nelem);
- break;
- }
- case DATA_TYPE_INT64_ARRAY: {
- int64_t *val;
- (void) nvpair_value_int64_array(nvp, &val, &nelem);
- ARENDER(pctl, int64_array, nvl, name, val, nelem);
- break;
- }
- case DATA_TYPE_UINT64_ARRAY: {
- uint64_t *val;
- (void) nvpair_value_uint64_array(nvp, &val, &nelem);
- ARENDER(pctl, uint64_array, nvl, name, val, nelem);
- break;
- }
- case DATA_TYPE_STRING_ARRAY: {
- char **val;
- (void) nvpair_value_string_array(nvp, &val, &nelem);
- ARENDER(pctl, string_array, nvl, name, val, nelem);
- break;
- }
- case DATA_TYPE_HRTIME: {
- hrtime_t val;
- (void) nvpair_value_hrtime(nvp, &val);
- RENDER(pctl, hrtime, nvl, name, val);
- break;
- }
- case DATA_TYPE_NVLIST: {
- nvlist_t *val;
- (void) nvpair_value_nvlist(nvp, &val);
- RENDER(pctl, nvlist, nvl, name, val);
- break;
- }
- case DATA_TYPE_NVLIST_ARRAY: {
- nvlist_t **val;
- (void) nvpair_value_nvlist_array(nvp, &val, &nelem);
- ARENDER(pctl, nvlist_array, nvl, name, val, nelem);
- break;
- }
- default:
- (void) fprintf(fp, " unknown data type (%d)", type);
- break;
- }
- nvp = nvlist_next_nvpair(nvl, nvp);
- }
-}
-
-void
-nvlist_print(FILE *fp, nvlist_t *nvl)
-{
- struct nvlist_prtctl pc;
-
- prtctl_defaults(fp, &pc, NULL);
- nvlist_print_with_indent(nvl, &pc);
-}
-
-void
-nvlist_prt(nvlist_t *nvl, nvlist_prtctl_t pctl)
-{
- nvlist_print_with_indent(nvl, pctl);
-}
-
-#define NVP(elem, type, vtype, ptype, format) { \
- vtype value; \
-\
- (void) nvpair_value_##type(elem, &value); \
- (void) printf("%*s%s: " format "\n", indent, "", \
- nvpair_name(elem), (ptype)value); \
-}
-
-#define NVPA(elem, type, vtype, ptype, format) { \
- uint_t i, count; \
- vtype *value; \
-\
- (void) nvpair_value_##type(elem, &value, &count); \
- for (i = 0; i < count; i++) { \
- (void) printf("%*s%s[%d]: " format "\n", indent, "", \
- nvpair_name(elem), i, (ptype)value[i]); \
- } \
-}
-
-/*
- * Similar to nvlist_print() but handles arrays slightly differently.
- */
-void
-dump_nvlist(nvlist_t *list, int indent)
-{
- nvpair_t *elem = NULL;
- boolean_t bool_value;
- boolean_t *bool_array_value;
- nvlist_t *nvlist_value;
- nvlist_t **nvlist_array_value;
- uint_t i, count;
-
- if (list == NULL) {
- return;
- }
-
- while ((elem = nvlist_next_nvpair(list, elem)) != NULL) {
- switch (nvpair_type(elem)) {
- case DATA_TYPE_BOOLEAN:
- (void) printf("%*s%s\n", indent, "", nvpair_name(elem));
- break;
-
- case DATA_TYPE_BOOLEAN_VALUE:
- (void) nvpair_value_boolean_value(elem, &bool_value);
- (void) printf("%*s%s: %s\n", indent, "",
- nvpair_name(elem), bool_value ? "true" : "false");
- break;
-
- case DATA_TYPE_BYTE:
- NVP(elem, byte, uchar_t, int, "%u");
- break;
-
- case DATA_TYPE_INT8:
- NVP(elem, int8, int8_t, int, "%d");
- break;
-
- case DATA_TYPE_UINT8:
- NVP(elem, uint8, uint8_t, int, "%u");
- break;
-
- case DATA_TYPE_INT16:
- NVP(elem, int16, int16_t, int, "%d");
- break;
-
- case DATA_TYPE_UINT16:
- NVP(elem, uint16, uint16_t, int, "%u");
- break;
-
- case DATA_TYPE_INT32:
- NVP(elem, int32, int32_t, long, "%ld");
- break;
-
- case DATA_TYPE_UINT32:
- NVP(elem, uint32, uint32_t, ulong_t, "%lu");
- break;
-
- case DATA_TYPE_INT64:
- NVP(elem, int64, int64_t, longlong_t, "%lld");
- break;
-
- case DATA_TYPE_UINT64:
- NVP(elem, uint64, uint64_t, u_longlong_t, "%llu");
- break;
-
- case DATA_TYPE_STRING:
- NVP(elem, string, char *, char *, "'%s'");
- break;
-
- case DATA_TYPE_BOOLEAN_ARRAY:
- (void) nvpair_value_boolean_array(elem,
- &bool_array_value, &count);
- for (i = 0; i < count; i++) {
- (void) printf("%*s%s[%d]: %s\n", indent, "",
- nvpair_name(elem), i,
- bool_array_value[i] ? "true" : "false");
- }
- break;
-
- case DATA_TYPE_BYTE_ARRAY:
- NVPA(elem, byte_array, uchar_t, int, "%u");
- break;
-
- case DATA_TYPE_INT8_ARRAY:
- NVPA(elem, int8_array, int8_t, int, "%d");
- break;
-
- case DATA_TYPE_UINT8_ARRAY:
- NVPA(elem, uint8_array, uint8_t, int, "%u");
- break;
-
- case DATA_TYPE_INT16_ARRAY:
- NVPA(elem, int16_array, int16_t, int, "%d");
- break;
-
- case DATA_TYPE_UINT16_ARRAY:
- NVPA(elem, uint16_array, uint16_t, int, "%u");
- break;
-
- case DATA_TYPE_INT32_ARRAY:
- NVPA(elem, int32_array, int32_t, long, "%ld");
- break;
-
- case DATA_TYPE_UINT32_ARRAY:
- NVPA(elem, uint32_array, uint32_t, ulong_t, "%lu");
- break;
-
- case DATA_TYPE_INT64_ARRAY:
- NVPA(elem, int64_array, int64_t, longlong_t, "%lld");
- break;
-
- case DATA_TYPE_UINT64_ARRAY:
- NVPA(elem, uint64_array, uint64_t, u_longlong_t,
- "%llu");
- break;
-
- case DATA_TYPE_STRING_ARRAY:
- NVPA(elem, string_array, char *, char *, "'%s'");
- break;
-
- case DATA_TYPE_NVLIST:
- (void) nvpair_value_nvlist(elem, &nvlist_value);
- (void) printf("%*s%s:\n", indent, "",
- nvpair_name(elem));
- dump_nvlist(nvlist_value, indent + 4);
- break;
-
- case DATA_TYPE_NVLIST_ARRAY:
- (void) nvpair_value_nvlist_array(elem,
- &nvlist_array_value, &count);
- for (i = 0; i < count; i++) {
- (void) printf("%*s%s[%u]:\n", indent, "",
- nvpair_name(elem), i);
- dump_nvlist(nvlist_array_value[i], indent + 4);
- }
- break;
-
- default:
- (void) printf(dgettext(TEXT_DOMAIN, "bad config type "
- "%d for %s\n"), nvpair_type(elem),
- nvpair_name(elem));
- }
- }
-}
-
-/*
- * ======================================================================
- * | |
- * | Misc private interface. |
- * | |
- * ======================================================================
- */
-
-/*
- * Determine if string 'value' matches 'nvp' value. The 'value' string is
- * converted, depending on the type of 'nvp', prior to match. For numeric
- * types, a radix independent sscanf conversion of 'value' is used. If 'nvp'
- * is an array type, 'ai' is the index into the array against which we are
- * checking for match. If nvp is of DATA_TYPE_STRING*, the caller can pass
- * in a regex_t compilation of value in 'value_regex' to trigger regular
- * expression string match instead of simple strcmp().
- *
- * Return 1 on match, 0 on no-match, and -1 on error. If the error is
- * related to value syntax error and 'ep' is non-NULL, *ep will point into
- * the 'value' string at the location where the error exists.
- *
- * NOTE: It may be possible to move the non-regex_t version of this into
- * common code used by library/kernel/boot.
- */
-int
-nvpair_value_match_regex(nvpair_t *nvp, int ai,
- char *value, regex_t *value_regex, char **ep)
-{
- char *evalue;
- uint_t a_len;
- int sr;
-
- if (ep)
- *ep = NULL;
-
- if ((nvp == NULL) || (value == NULL))
- return (-1); /* error fail match - invalid args */
-
- /* make sure array and index combination make sense */
- if ((nvpair_type_is_array(nvp) && (ai < 0)) ||
- (!nvpair_type_is_array(nvp) && (ai >= 0)))
- return (-1); /* error fail match - bad index */
-
- /* non-string values should be single 'chunk' */
- if ((nvpair_type(nvp) != DATA_TYPE_STRING) &&
- (nvpair_type(nvp) != DATA_TYPE_STRING_ARRAY)) {
- value += strspn(value, " \t");
- evalue = value + strcspn(value, " \t");
- if (*evalue) {
- if (ep)
- *ep = evalue;
- return (-1); /* error fail match - syntax */
- }
- }
-
- sr = EOF;
- switch (nvpair_type(nvp)) {
- case DATA_TYPE_STRING: {
- char *val;
-
- /* check string value for match */
- if (nvpair_value_string(nvp, &val) == 0) {
- if (value_regex) {
- if (regexec(value_regex, val,
- (size_t)0, NULL, 0) == 0)
- return (1); /* match */
- } else {
- if (strcmp(value, val) == 0)
- return (1); /* match */
- }
- }
- break;
- }
- case DATA_TYPE_STRING_ARRAY: {
- char **val_array;
-
- /* check indexed string value of array for match */
- if ((nvpair_value_string_array(nvp, &val_array, &a_len) == 0) &&
- (ai < a_len)) {
- if (value_regex) {
- if (regexec(value_regex, val_array[ai],
- (size_t)0, NULL, 0) == 0)
- return (1);
- } else {
- if (strcmp(value, val_array[ai]) == 0)
- return (1);
- }
- }
- break;
- }
- case DATA_TYPE_BYTE: {
- uchar_t val, val_arg;
-
- /* scanf uchar_t from value and check for match */
- sr = sscanf(value, "%c", &val_arg);
- if ((sr == 1) && (nvpair_value_byte(nvp, &val) == 0) &&
- (val == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_BYTE_ARRAY: {
- uchar_t *val_array, val_arg;
-
-
- /* check indexed value of array for match */
- sr = sscanf(value, "%c", &val_arg);
- if ((sr == 1) &&
- (nvpair_value_byte_array(nvp, &val_array, &a_len) == 0) &&
- (ai < a_len) &&
- (val_array[ai] == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_INT8: {
- int8_t val, val_arg;
-
- /* scanf int8_t from value and check for match */
- sr = sscanf(value, "%"SCNi8, &val_arg);
- if ((sr == 1) &&
- (nvpair_value_int8(nvp, &val) == 0) &&
- (val == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_INT8_ARRAY: {
- int8_t *val_array, val_arg;
-
- /* check indexed value of array for match */
- sr = sscanf(value, "%"SCNi8, &val_arg);
- if ((sr == 1) &&
- (nvpair_value_int8_array(nvp, &val_array, &a_len) == 0) &&
- (ai < a_len) &&
- (val_array[ai] == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_UINT8: {
- uint8_t val, val_arg;
-
- /* scanf uint8_t from value and check for match */
- sr = sscanf(value, "%"SCNi8, (int8_t *)&val_arg);
- if ((sr == 1) &&
- (nvpair_value_uint8(nvp, &val) == 0) &&
- (val == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_UINT8_ARRAY: {
- uint8_t *val_array, val_arg;
-
- /* check indexed value of array for match */
- sr = sscanf(value, "%"SCNi8, (int8_t *)&val_arg);
- if ((sr == 1) &&
- (nvpair_value_uint8_array(nvp, &val_array, &a_len) == 0) &&
- (ai < a_len) &&
- (val_array[ai] == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_INT16: {
- int16_t val, val_arg;
-
- /* scanf int16_t from value and check for match */
- sr = sscanf(value, "%"SCNi16, &val_arg);
- if ((sr == 1) &&
- (nvpair_value_int16(nvp, &val) == 0) &&
- (val == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_INT16_ARRAY: {
- int16_t *val_array, val_arg;
-
- /* check indexed value of array for match */
- sr = sscanf(value, "%"SCNi16, &val_arg);
- if ((sr == 1) &&
- (nvpair_value_int16_array(nvp, &val_array, &a_len) == 0) &&
- (ai < a_len) &&
- (val_array[ai] == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_UINT16: {
- uint16_t val, val_arg;
-
- /* scanf uint16_t from value and check for match */
- sr = sscanf(value, "%"SCNi16, (int16_t *)&val_arg);
- if ((sr == 1) &&
- (nvpair_value_uint16(nvp, &val) == 0) &&
- (val == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_UINT16_ARRAY: {
- uint16_t *val_array, val_arg;
-
- /* check indexed value of array for match */
- sr = sscanf(value, "%"SCNi16, (int16_t *)&val_arg);
- if ((sr == 1) &&
- (nvpair_value_uint16_array(nvp, &val_array, &a_len) == 0) &&
- (ai < a_len) &&
- (val_array[ai] == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_INT32: {
- int32_t val, val_arg;
-
- /* scanf int32_t from value and check for match */
- sr = sscanf(value, "%"SCNi32, &val_arg);
- if ((sr == 1) &&
- (nvpair_value_int32(nvp, &val) == 0) &&
- (val == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_INT32_ARRAY: {
- int32_t *val_array, val_arg;
-
- /* check indexed value of array for match */
- sr = sscanf(value, "%"SCNi32, &val_arg);
- if ((sr == 1) &&
- (nvpair_value_int32_array(nvp, &val_array, &a_len) == 0) &&
- (ai < a_len) &&
- (val_array[ai] == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_UINT32: {
- uint32_t val, val_arg;
-
- /* scanf uint32_t from value and check for match */
- sr = sscanf(value, "%"SCNi32, (int32_t *)&val_arg);
- if ((sr == 1) &&
- (nvpair_value_uint32(nvp, &val) == 0) &&
- (val == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_UINT32_ARRAY: {
- uint32_t *val_array, val_arg;
-
- /* check indexed value of array for match */
- sr = sscanf(value, "%"SCNi32, (int32_t *)&val_arg);
- if ((sr == 1) &&
- (nvpair_value_uint32_array(nvp, &val_array, &a_len) == 0) &&
- (ai < a_len) &&
- (val_array[ai] == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_INT64: {
- int64_t val, val_arg;
-
- /* scanf int64_t from value and check for match */
- sr = sscanf(value, "%"SCNi64, &val_arg);
- if ((sr == 1) &&
- (nvpair_value_int64(nvp, &val) == 0) &&
- (val == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_INT64_ARRAY: {
- int64_t *val_array, val_arg;
-
- /* check indexed value of array for match */
- sr = sscanf(value, "%"SCNi64, &val_arg);
- if ((sr == 1) &&
- (nvpair_value_int64_array(nvp, &val_array, &a_len) == 0) &&
- (ai < a_len) &&
- (val_array[ai] == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_UINT64: {
- uint64_t val_arg, val;
-
- /* scanf uint64_t from value and check for match */
- sr = sscanf(value, "%"SCNi64, (int64_t *)&val_arg);
- if ((sr == 1) &&
- (nvpair_value_uint64(nvp, &val) == 0) &&
- (val == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_UINT64_ARRAY: {
- uint64_t *val_array, val_arg;
-
- /* check indexed value of array for match */
- sr = sscanf(value, "%"SCNi64, (int64_t *)&val_arg);
- if ((sr == 1) &&
- (nvpair_value_uint64_array(nvp, &val_array, &a_len) == 0) &&
- (ai < a_len) &&
- (val_array[ai] == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_BOOLEAN_VALUE: {
- int32_t val_arg;
- boolean_t val;
-
- /* scanf boolean_t from value and check for match */
- sr = sscanf(value, "%"SCNi32, &val_arg);
- if ((sr == 1) &&
- (nvpair_value_boolean_value(nvp, &val) == 0) &&
- (val == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_BOOLEAN_ARRAY: {
- boolean_t *val_array;
- int32_t val_arg;
-
- /* check indexed value of array for match */
- sr = sscanf(value, "%"SCNi32, &val_arg);
- if ((sr == 1) &&
- (nvpair_value_boolean_array(nvp,
- &val_array, &a_len) == 0) &&
- (ai < a_len) &&
- (val_array[ai] == val_arg))
- return (1);
- break;
- }
- case DATA_TYPE_HRTIME:
- case DATA_TYPE_NVLIST:
- case DATA_TYPE_NVLIST_ARRAY:
- case DATA_TYPE_BOOLEAN:
- case DATA_TYPE_DOUBLE:
- case DATA_TYPE_UNKNOWN:
- default:
- /*
- * unknown/unsupported data type
- */
- return (-1); /* error fail match */
- }
-
- /*
- * check to see if sscanf failed conversion, return approximate
- * pointer to problem
- */
- if (sr != 1) {
- if (ep)
- *ep = value;
- return (-1); /* error fail match - syntax */
- }
-
- return (0); /* fail match */
-}
-
-int
-nvpair_value_match(nvpair_t *nvp, int ai, char *value, char **ep)
-{
- return (nvpair_value_match_regex(nvp, ai, value, NULL, ep));
-}
diff --git a/cddl/contrib/opensolaris/lib/libnvpair/libnvpair.h b/cddl/contrib/opensolaris/lib/libnvpair/libnvpair.h
deleted file mode 100644
index b05669e506ba2..0000000000000
--- a/cddl/contrib/opensolaris/lib/libnvpair/libnvpair.h
+++ /dev/null
@@ -1,196 +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) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
- */
-
-#ifndef _LIBNVPAIR_H
-#define _LIBNVPAIR_H
-
-#include <sys/nvpair.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <regex.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * All interfaces described in this file are private to Solaris, and
- * are subject to change at any time and without notice. The public
- * nvlist/nvpair interfaces, as documented in manpage sections 3NVPAIR,
- * are all imported from <sys/nvpair.h> included above.
- */
-
-extern int nvpair_value_match(nvpair_t *, int, char *, char **);
-extern int nvpair_value_match_regex(nvpair_t *, int, char *, regex_t *,
- char **);
-
-extern void nvlist_print(FILE *, nvlist_t *);
-extern int nvlist_print_json(FILE *, nvlist_t *);
-extern void dump_nvlist(nvlist_t *, int);
-
-/*
- * Private nvlist printing interface that allows the caller some control
- * over output rendering (as opposed to nvlist_print and dump_nvlist).
- *
- * Obtain an opaque nvlist_prtctl_t cookie using nvlist_prtctl_alloc
- * (NULL on failure); on return the cookie is set up for default formatting
- * and rendering. Quote the cookie in subsequent customisation functions and
- * then pass the cookie to nvlist_prt to render the nvlist. Finally,
- * use nvlist_prtctl_free to release the cookie.
- *
- * For all nvlist_lookup_xxx and nvlist_lookup_xxx_array functions
- * we have a corresponding brace of functions that appoint replacement
- * rendering functions:
- *
- * extern void nvlist_prtctl_xxx(nvlist_prtctl_t,
- * void (*)(nvlist_prtctl_t ctl, void *private, const char *name,
- * xxxtype value))
- *
- * and
- *
- * extern void nvlist_prtctl_xxx_array(nvlist_prtctl_t,
- * void (*)(nvlist_prtctl_t ctl, void *private, const char *name,
- * xxxtype value, uint_t count))
- *
- * where xxxtype is the C datatype corresponding to xxx, eg int8_t for "int8"
- * and char * for "string". The function that is appointed to render the
- * specified datatype receives as arguments the cookie, the nvlist
- * member name, the value of that member (or a pointer for array function),
- * and (for array rendering functions) a count of the number of elements.
- */
-
-typedef struct nvlist_prtctl *nvlist_prtctl_t; /* opaque */
-
-enum nvlist_indent_mode {
- NVLIST_INDENT_ABS, /* Absolute indentation */
- NVLIST_INDENT_TABBED /* Indent with tabstops */
-};
-
-extern nvlist_prtctl_t nvlist_prtctl_alloc(void);
-extern void nvlist_prtctl_free(nvlist_prtctl_t);
-extern void nvlist_prt(nvlist_t *, nvlist_prtctl_t);
-
-/* Output stream */
-extern void nvlist_prtctl_setdest(nvlist_prtctl_t, FILE *);
-extern FILE *nvlist_prtctl_getdest(nvlist_prtctl_t);
-
-/* Indentation mode, start indent, indent increment; default tabbed/0/1 */
-extern void nvlist_prtctl_setindent(nvlist_prtctl_t, enum nvlist_indent_mode,
- int, int);
-extern void nvlist_prtctl_doindent(nvlist_prtctl_t, int);
-
-enum nvlist_prtctl_fmt {
- NVLIST_FMT_MEMBER_NAME, /* name fmt; default "%s = " */
- NVLIST_FMT_MEMBER_POSTAMBLE, /* after nvlist member; default "\n" */
- NVLIST_FMT_BTWN_ARRAY /* between array members; default " " */
-};
-
-extern void nvlist_prtctl_setfmt(nvlist_prtctl_t, enum nvlist_prtctl_fmt,
- const char *);
-extern void nvlist_prtctl_dofmt(nvlist_prtctl_t, enum nvlist_prtctl_fmt, ...);
-
-/*
- * Function prototypes for interfaces that appoint a new rendering function
- * for single-valued nvlist members.
- *
- * A replacement function receives arguments as follows:
- *
- * nvlist_prtctl_t Print control structure; do not change preferences
- * for this object from a print callback function.
- *
- * void * The function-private cookie argument registered
- * when the replacement function was appointed.
- *
- * nvlist_t * The full nvlist that is being processed. The
- * rendering function is called to render a single
- * member (name and value passed as below) but it may
- * want to reference or incorporate other aspects of
- * the full nvlist.
- *
- * const char * Member name to render
- *
- * valtype Value of the member to render
- *
- * The function must return non-zero if it has rendered output for this
- * member, or 0 if it wants to default to standard rendering for this
- * one member.
- */
-
-#define NVLIST_PRINTCTL_SVDECL(funcname, valtype) \
- extern void funcname(nvlist_prtctl_t, \
- int (*)(nvlist_prtctl_t, void *, nvlist_t *, const char *, valtype), \
- void *)
-
-NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_boolean, int);
-NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_boolean_value, boolean_t);
-NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_byte, uchar_t);
-NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_int8, int8_t);
-NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_uint8, uint8_t);
-NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_int16, int16_t);
-NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_uint16, uint16_t);
-NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_int32, int32_t);
-NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_uint32, uint32_t);
-NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_int64, int64_t);
-NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_uint64, uint64_t);
-NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_double, double);
-NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_string, char *);
-NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_hrtime, hrtime_t);
-NVLIST_PRINTCTL_SVDECL(nvlist_prtctlop_nvlist, nvlist_t *);
-
-#undef NVLIST_PRINTCTL_SVDECL /* was just for "clarity" above */
-
-/*
- * Function prototypes for interfaces that appoint a new rendering function
- * for array-valued nvlist members.
- *
- * One additional argument is taken: uint_t for the number of array elements
- *
- * Return values as above.
- */
-#define NVLIST_PRINTCTL_AVDECL(funcname, vtype) \
- extern void funcname(nvlist_prtctl_t, \
- int (*)(nvlist_prtctl_t, void *, nvlist_t *, const char *, vtype, uint_t), \
- void *)
-
-NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_boolean_array, boolean_t *);
-NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_byte_array, uchar_t *);
-NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_int8_array, int8_t *);
-NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_uint8_array, uint8_t *);
-NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_int16_array, int16_t *);
-NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_uint16_array, uint16_t *);
-NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_int32_array, int32_t *);
-NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_uint32_array, uint32_t *);
-NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_int64_array, int64_t *);
-NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_uint64_array, uint64_t *);
-NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_string_array, char **);
-NVLIST_PRINTCTL_AVDECL(nvlist_prtctlop_nvlist_array, nvlist_t **);
-
-#undef NVLIST_PRINTCTL_AVDECL /* was just for "clarity" above */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBNVPAIR_H */
diff --git a/cddl/contrib/opensolaris/lib/libnvpair/nvpair_alloc_system.c b/cddl/contrib/opensolaris/lib/libnvpair/nvpair_alloc_system.c
deleted file mode 100644
index 1aefc1004daf1..0000000000000
--- a/cddl/contrib/opensolaris/lib/libnvpair/nvpair_alloc_system.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * 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 2004 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#include <sys/nvpair.h>
-#include <stdlib.h>
-
-/*ARGSUSED*/
-static void *
-nv_alloc_sys(nv_alloc_t *nva, size_t size)
-{
- return (malloc(size));
-}
-
-/*ARGSUSED*/
-static void
-nv_free_sys(nv_alloc_t *nva, void *buf, size_t size)
-{
- free(buf);
-}
-
-const nv_alloc_ops_t system_ops_def = {
- NULL, /* nv_ao_init() */
- NULL, /* nv_ao_fini() */
- nv_alloc_sys, /* nv_ao_alloc() */
- nv_free_sys, /* nv_ao_free() */
- NULL /* nv_ao_reset() */
-};
-
-nv_alloc_t nv_alloc_nosleep_def = {
- &system_ops_def,
- NULL
-};
-
-nv_alloc_t *nv_alloc_nosleep = &nv_alloc_nosleep_def;
diff --git a/cddl/contrib/opensolaris/lib/libnvpair/nvpair_json.c b/cddl/contrib/opensolaris/lib/libnvpair/nvpair_json.c
deleted file mode 100644
index b687a2f5761ab..0000000000000
--- a/cddl/contrib/opensolaris/lib/libnvpair/nvpair_json.c
+++ /dev/null
@@ -1,406 +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) 2014, Joyent, Inc.
- * Copyright (c) 2017 by Delphix. All rights reserved.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <wchar.h>
-#include <sys/debug.h>
-
-#include "libnvpair.h"
-
-#define FPRINTF(fp, ...) \
- do { \
- if (fprintf(fp, __VA_ARGS__) < 0) \
- return (-1); \
- } while (0)
-
-/*
- * When formatting a string for JSON output we must escape certain characters,
- * as described in RFC4627. This applies to both member names and
- * DATA_TYPE_STRING values.
- *
- * This function will only operate correctly if the following conditions are
- * met:
- *
- * 1. The input String is encoded in the current locale.
- *
- * 2. The current locale includes the Basic Multilingual Plane (plane 0)
- * as defined in the Unicode standard.
- *
- * The output will be entirely 7-bit ASCII (as a subset of UTF-8) with all
- * representable Unicode characters included in their escaped numeric form.
- */
-static int
-nvlist_print_json_string(FILE *fp, const char *input)
-{
- mbstate_t mbr;
- wchar_t c;
- size_t sz;
-
- bzero(&mbr, sizeof (mbr));
-
- FPRINTF(fp, "\"");
- while ((sz = mbrtowc(&c, input, MB_CUR_MAX, &mbr)) > 0) {
- switch (c) {
- case '"':
- FPRINTF(fp, "\\\"");
- break;
- case '\n':
- FPRINTF(fp, "\\n");
- break;
- case '\r':
- FPRINTF(fp, "\\r");
- break;
- case '\\':
- FPRINTF(fp, "\\\\");
- break;
- case '\f':
- FPRINTF(fp, "\\f");
- break;
- case '\t':
- FPRINTF(fp, "\\t");
- break;
- case '\b':
- FPRINTF(fp, "\\b");
- break;
- default:
- if ((c >= 0x00 && c <= 0x1f) ||
- (c > 0x7f && c <= 0xffff)) {
- /*
- * Render both Control Characters and Unicode
- * characters in the Basic Multilingual Plane
- * as JSON-escaped multibyte characters.
- */
- FPRINTF(fp, "\\u%04x", (int)(0xffff & c));
- } else if (c >= 0x20 && c <= 0x7f) {
- /*
- * Render other 7-bit ASCII characters directly
- * and drop other, unrepresentable characters.
- */
- FPRINTF(fp, "%c", (int)(0xff & c));
- }
- break;
- }
- input += sz;
- }
-
- if (sz == (size_t)-1 || sz == (size_t)-2) {
- /*
- * We last read an invalid multibyte character sequence,
- * so return an error.
- */
- return (-1);
- }
-
- FPRINTF(fp, "\"");
- return (0);
-}
-
-/*
- * Dump a JSON-formatted representation of an nvlist to the provided FILE *.
- * This routine does not output any new-lines or additional whitespace other
- * than that contained in strings, nor does it call fflush(3C).
- */
-int
-nvlist_print_json(FILE *fp, nvlist_t *nvl)
-{
- nvpair_t *curr;
- boolean_t first = B_TRUE;
-
- FPRINTF(fp, "{");
-
- for (curr = nvlist_next_nvpair(nvl, NULL); curr;
- curr = nvlist_next_nvpair(nvl, curr)) {
- data_type_t type = nvpair_type(curr);
-
- if (!first)
- FPRINTF(fp, ",");
- else
- first = B_FALSE;
-
- if (nvlist_print_json_string(fp, nvpair_name(curr)) == -1)
- return (-1);
- FPRINTF(fp, ":");
-
- switch (type) {
- case DATA_TYPE_STRING: {
- char *string = fnvpair_value_string(curr);
- if (nvlist_print_json_string(fp, string) == -1)
- return (-1);
- break;
- }
-
- case DATA_TYPE_BOOLEAN: {
- FPRINTF(fp, "true");
- break;
- }
-
- case DATA_TYPE_BOOLEAN_VALUE: {
- FPRINTF(fp, "%s", fnvpair_value_boolean_value(curr) ==
- B_TRUE ? "true" : "false");
- break;
- }
-
- case DATA_TYPE_BYTE: {
- FPRINTF(fp, "%hhu", fnvpair_value_byte(curr));
- break;
- }
-
- case DATA_TYPE_INT8: {
- FPRINTF(fp, "%hhd", fnvpair_value_int8(curr));
- break;
- }
-
- case DATA_TYPE_UINT8: {
- FPRINTF(fp, "%hhu", fnvpair_value_uint8_t(curr));
- break;
- }
-
- case DATA_TYPE_INT16: {
- FPRINTF(fp, "%hd", fnvpair_value_int16(curr));
- break;
- }
-
- case DATA_TYPE_UINT16: {
- FPRINTF(fp, "%hu", fnvpair_value_uint16(curr));
- break;
- }
-
- case DATA_TYPE_INT32: {
- FPRINTF(fp, "%d", fnvpair_value_int32(curr));
- break;
- }
-
- case DATA_TYPE_UINT32: {
- FPRINTF(fp, "%u", fnvpair_value_uint32(curr));
- break;
- }
-
- case DATA_TYPE_INT64: {
- FPRINTF(fp, "%lld",
- (long long)fnvpair_value_int64(curr));
- break;
- }
-
- case DATA_TYPE_UINT64: {
- FPRINTF(fp, "%llu",
- (unsigned long long)fnvpair_value_uint64(curr));
- break;
- }
-
- case DATA_TYPE_HRTIME: {
- hrtime_t val;
- VERIFY0(nvpair_value_hrtime(curr, &val));
- FPRINTF(fp, "%llu", (unsigned long long)val);
- break;
- }
-
- case DATA_TYPE_DOUBLE: {
- double val;
- VERIFY0(nvpair_value_double(curr, &val));
- FPRINTF(fp, "%f", val);
- break;
- }
-
- case DATA_TYPE_NVLIST: {
- if (nvlist_print_json(fp,
- fnvpair_value_nvlist(curr)) == -1)
- return (-1);
- break;
- }
-
- case DATA_TYPE_STRING_ARRAY: {
- char **val;
- uint_t valsz, i;
- VERIFY0(nvpair_value_string_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
- for (i = 0; i < valsz; i++) {
- if (i > 0)
- FPRINTF(fp, ",");
- if (nvlist_print_json_string(fp, val[i]) == -1)
- return (-1);
- }
- FPRINTF(fp, "]");
- break;
- }
-
- case DATA_TYPE_NVLIST_ARRAY: {
- nvlist_t **val;
- uint_t valsz, i;
- VERIFY0(nvpair_value_nvlist_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
- for (i = 0; i < valsz; i++) {
- if (i > 0)
- FPRINTF(fp, ",");
- if (nvlist_print_json(fp, val[i]) == -1)
- return (-1);
- }
- FPRINTF(fp, "]");
- break;
- }
-
- case DATA_TYPE_BOOLEAN_ARRAY: {
- boolean_t *val;
- uint_t valsz, i;
- VERIFY0(nvpair_value_boolean_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
- for (i = 0; i < valsz; i++) {
- if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, val[i] == B_TRUE ?
- "true" : "false");
- }
- FPRINTF(fp, "]");
- break;
- }
-
- case DATA_TYPE_BYTE_ARRAY: {
- uchar_t *val;
- uint_t valsz, i;
- VERIFY0(nvpair_value_byte_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
- for (i = 0; i < valsz; i++) {
- if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%hhu", val[i]);
- }
- FPRINTF(fp, "]");
- break;
- }
-
- case DATA_TYPE_UINT8_ARRAY: {
- uint8_t *val;
- uint_t valsz, i;
- VERIFY0(nvpair_value_uint8_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
- for (i = 0; i < valsz; i++) {
- if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%hhu", val[i]);
- }
- FPRINTF(fp, "]");
- break;
- }
-
- case DATA_TYPE_INT8_ARRAY: {
- int8_t *val;
- uint_t valsz, i;
- VERIFY0(nvpair_value_int8_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
- for (i = 0; i < valsz; i++) {
- if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%hhd", val[i]);
- }
- FPRINTF(fp, "]");
- break;
- }
-
- case DATA_TYPE_UINT16_ARRAY: {
- uint16_t *val;
- uint_t valsz, i;
- VERIFY0(nvpair_value_uint16_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
- for (i = 0; i < valsz; i++) {
- if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%hu", val[i]);
- }
- FPRINTF(fp, "]");
- break;
- }
-
- case DATA_TYPE_INT16_ARRAY: {
- int16_t *val;
- uint_t valsz, i;
- VERIFY0(nvpair_value_int16_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
- for (i = 0; i < valsz; i++) {
- if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%hd", val[i]);
- }
- FPRINTF(fp, "]");
- break;
- }
-
- case DATA_TYPE_UINT32_ARRAY: {
- uint32_t *val;
- uint_t valsz, i;
- VERIFY0(nvpair_value_uint32_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
- for (i = 0; i < valsz; i++) {
- if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%u", val[i]);
- }
- FPRINTF(fp, "]");
- break;
- }
-
- case DATA_TYPE_INT32_ARRAY: {
- int32_t *val;
- uint_t valsz, i;
- VERIFY0(nvpair_value_int32_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
- for (i = 0; i < valsz; i++) {
- if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%d", val[i]);
- }
- FPRINTF(fp, "]");
- break;
- }
-
- case DATA_TYPE_UINT64_ARRAY: {
- uint64_t *val;
- uint_t valsz, i;
- VERIFY0(nvpair_value_uint64_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
- for (i = 0; i < valsz; i++) {
- if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%llu",
- (unsigned long long)val[i]);
- }
- FPRINTF(fp, "]");
- break;
- }
-
- case DATA_TYPE_INT64_ARRAY: {
- int64_t *val;
- uint_t valsz, i;
- VERIFY0(nvpair_value_int64_array(curr, &val, &valsz));
- FPRINTF(fp, "[");
- for (i = 0; i < valsz; i++) {
- if (i > 0)
- FPRINTF(fp, ",");
- FPRINTF(fp, "%lld", (long long)val[i]);
- }
- FPRINTF(fp, "]");
- break;
- }
-
- case DATA_TYPE_UNKNOWN:
- case DATA_TYPE_DONTCARE:
- return (-1);
- }
-
- }
-
- FPRINTF(fp, "}");
- return (0);
-}
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/libuutil.h b/cddl/contrib/opensolaris/lib/libuutil/common/libuutil.h
deleted file mode 100644
index 7a5f8a8570c63..0000000000000
--- a/cddl/contrib/opensolaris/lib/libuutil/common/libuutil.h
+++ /dev/null
@@ -1,391 +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) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
- */
-
-#ifndef _LIBUUTIL_H
-#define _LIBUUTIL_H
-
-#include <solaris.h>
-#include <sys/types.h>
-#include <stdarg.h>
-#include <stdio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Standard flags codes.
- */
-#define UU_DEFAULT 0
-
-/*
- * Standard error codes.
- */
-#define UU_ERROR_NONE 0 /* no error */
-#define UU_ERROR_INVALID_ARGUMENT 1 /* invalid argument */
-#define UU_ERROR_UNKNOWN_FLAG 2 /* passed flag invalid */
-#define UU_ERROR_NO_MEMORY 3 /* out of memory */
-#define UU_ERROR_CALLBACK_FAILED 4 /* callback-initiated error */
-#define UU_ERROR_NOT_SUPPORTED 5 /* operation not supported */
-#define UU_ERROR_EMPTY 6 /* no value provided */
-#define UU_ERROR_UNDERFLOW 7 /* value is too small */
-#define UU_ERROR_OVERFLOW 8 /* value is too value */
-#define UU_ERROR_INVALID_CHAR 9 /* value contains unexpected char */
-#define UU_ERROR_INVALID_DIGIT 10 /* value contains digit not in base */
-
-#define UU_ERROR_SYSTEM 99 /* underlying system error */
-#define UU_ERROR_UNKNOWN 100 /* error status not known */
-
-/*
- * Standard program exit codes.
- */
-#define UU_EXIT_OK (*(uu_exit_ok()))
-#define UU_EXIT_FATAL (*(uu_exit_fatal()))
-#define UU_EXIT_USAGE (*(uu_exit_usage()))
-
-/*
- * Exit status profiles.
- */
-#define UU_PROFILE_DEFAULT 0
-#define UU_PROFILE_LAUNCHER 1
-
-/*
- * Error reporting functions.
- */
-uint32_t uu_error(void);
-const char *uu_strerror(uint32_t);
-
-/*
- * Program notification functions.
- */
-extern void uu_alt_exit(int);
-extern const char *uu_setpname(char *);
-extern const char *uu_getpname(void);
-/*PRINTFLIKE1*/
-extern void uu_warn(const char *, ...);
-extern void uu_vwarn(const char *, va_list);
-/*PRINTFLIKE1*/
-extern void uu_die(const char *, ...) __NORETURN;
-extern void uu_vdie(const char *, va_list) __NORETURN;
-/*PRINTFLIKE2*/
-extern void uu_xdie(int, const char *, ...) __NORETURN;
-extern void uu_vxdie(int, const char *, va_list) __NORETURN;
-
-/*
- * Exit status functions (not to be used directly)
- */
-extern int *uu_exit_ok(void);
-extern int *uu_exit_fatal(void);
-extern int *uu_exit_usage(void);
-
-/*
- * string->number conversions
- */
-extern int uu_strtoint(const char *, void *, size_t, int, int64_t, int64_t);
-extern int uu_strtouint(const char *, void *, size_t, int, uint64_t, uint64_t);
-
-/*
- * Debug print facility functions.
- */
-typedef struct uu_dprintf uu_dprintf_t;
-
-typedef enum {
- UU_DPRINTF_SILENT,
- UU_DPRINTF_FATAL,
- UU_DPRINTF_WARNING,
- UU_DPRINTF_NOTICE,
- UU_DPRINTF_INFO,
- UU_DPRINTF_DEBUG
-} uu_dprintf_severity_t;
-
-extern uu_dprintf_t *uu_dprintf_create(const char *, uu_dprintf_severity_t,
- uint_t);
-/*PRINTFLIKE3*/
-extern void uu_dprintf(uu_dprintf_t *, uu_dprintf_severity_t,
- const char *, ...);
-extern void uu_dprintf_destroy(uu_dprintf_t *);
-extern const char *uu_dprintf_getname(uu_dprintf_t *);
-
-/*
- * Identifier test flags and function.
- */
-#define UU_NAME_DOMAIN 0x1 /* allow SUNW, or com.sun, prefix */
-#define UU_NAME_PATH 0x2 /* allow '/'-delimited paths */
-
-int uu_check_name(const char *, uint_t);
-
-/*
- * File creation functions.
- */
-extern int uu_open_tmp(const char *dir, uint_t uflags);
-
-/*
- * Convenience functions.
- */
-#define UU_NELEM(a) (sizeof (a) / sizeof ((a)[0]))
-
-/*PRINTFLIKE1*/
-extern char *uu_msprintf(const char *format, ...);
-extern void *uu_zalloc(size_t);
-extern char *uu_strdup(const char *);
-extern void uu_free(void *);
-
-extern boolean_t uu_strcaseeq(const char *a, const char *b);
-extern boolean_t uu_streq(const char *a, const char *b);
-extern char *uu_strndup(const char *s, size_t n);
-extern boolean_t uu_strbw(const char *a, const char *b);
-extern void *uu_memdup(const void *buf, size_t sz);
-extern void uu_dump(FILE *out, const char *prefix, const void *buf, size_t len);
-
-/*
- * Comparison function type definition.
- * Developers should be careful in their use of the _private argument. If you
- * break interface guarantees, you get undefined behavior.
- */
-typedef int uu_compare_fn_t(const void *__left, const void *__right,
- void *__private);
-
-/*
- * Walk variant flags.
- * A data structure need not provide support for all variants and
- * combinations. Refer to the appropriate documentation.
- */
-#define UU_WALK_ROBUST 0x00000001 /* walk can survive removes */
-#define UU_WALK_REVERSE 0x00000002 /* reverse walk order */
-
-#define UU_WALK_PREORDER 0x00000010 /* walk tree in pre-order */
-#define UU_WALK_POSTORDER 0x00000020 /* walk tree in post-order */
-
-/*
- * Walk callback function return codes.
- */
-#define UU_WALK_ERROR -1
-#define UU_WALK_NEXT 0
-#define UU_WALK_DONE 1
-
-/*
- * Walk callback function type definition.
- */
-typedef int uu_walk_fn_t(void *_elem, void *_private);
-
-/*
- * lists: opaque structures
- */
-typedef struct uu_list_pool uu_list_pool_t;
-typedef struct uu_list uu_list_t;
-
-typedef struct uu_list_node {
- uintptr_t uln_opaque[2];
-} uu_list_node_t;
-
-typedef struct uu_list_walk uu_list_walk_t;
-
-typedef uintptr_t uu_list_index_t;
-
-/*
- * lists: interface
- *
- * basic usage:
- * typedef struct foo {
- * ...
- * uu_list_node_t foo_node;
- * ...
- * } foo_t;
- *
- * static int
- * foo_compare(void *l_arg, void *r_arg, void *private)
- * {
- * foo_t *l = l_arg;
- * foo_t *r = r_arg;
- *
- * if (... l greater than r ...)
- * return (1);
- * if (... l less than r ...)
- * return (-1);
- * return (0);
- * }
- *
- * ...
- * // at initialization time
- * foo_pool = uu_list_pool_create("foo_pool",
- * sizeof (foo_t), offsetof(foo_t, foo_node), foo_compare,
- * debugging? 0 : UU_AVL_POOL_DEBUG);
- * ...
- */
-uu_list_pool_t *uu_list_pool_create(const char *, size_t, size_t,
- uu_compare_fn_t *, uint32_t);
-#define UU_LIST_POOL_DEBUG 0x00000001
-
-void uu_list_pool_destroy(uu_list_pool_t *);
-
-/*
- * usage:
- *
- * foo_t *a;
- * a = malloc(sizeof(*a));
- * uu_list_node_init(a, &a->foo_list, pool);
- * ...
- * uu_list_node_fini(a, &a->foo_list, pool);
- * free(a);
- */
-void uu_list_node_init(void *, uu_list_node_t *, uu_list_pool_t *);
-void uu_list_node_fini(void *, uu_list_node_t *, uu_list_pool_t *);
-
-uu_list_t *uu_list_create(uu_list_pool_t *, void *_parent, uint32_t);
-#define UU_LIST_DEBUG 0x00000001
-#define UU_LIST_SORTED 0x00000002 /* list is sorted */
-
-void uu_list_destroy(uu_list_t *); /* list must be empty */
-
-size_t uu_list_numnodes(uu_list_t *);
-
-void *uu_list_first(uu_list_t *);
-void *uu_list_last(uu_list_t *);
-
-void *uu_list_next(uu_list_t *, void *);
-void *uu_list_prev(uu_list_t *, void *);
-
-int uu_list_walk(uu_list_t *, uu_walk_fn_t *, void *, uint32_t);
-
-uu_list_walk_t *uu_list_walk_start(uu_list_t *, uint32_t);
-void *uu_list_walk_next(uu_list_walk_t *);
-void uu_list_walk_end(uu_list_walk_t *);
-
-void *uu_list_find(uu_list_t *, void *, void *, uu_list_index_t *);
-void uu_list_insert(uu_list_t *, void *, uu_list_index_t);
-
-void *uu_list_nearest_next(uu_list_t *, uu_list_index_t);
-void *uu_list_nearest_prev(uu_list_t *, uu_list_index_t);
-
-void *uu_list_teardown(uu_list_t *, void **);
-
-void uu_list_remove(uu_list_t *, void *);
-
-/*
- * lists: interfaces for non-sorted lists only
- */
-int uu_list_insert_before(uu_list_t *, void *_target, void *_elem);
-int uu_list_insert_after(uu_list_t *, void *_target, void *_elem);
-
-/*
- * avl trees: opaque structures
- */
-typedef struct uu_avl_pool uu_avl_pool_t;
-typedef struct uu_avl uu_avl_t;
-
-typedef struct uu_avl_node {
-#ifdef _LP64
- uintptr_t uan_opaque[3];
-#else
- uintptr_t uan_opaque[4];
-#endif
-} uu_avl_node_t;
-
-typedef struct uu_avl_walk uu_avl_walk_t;
-
-typedef uintptr_t uu_avl_index_t;
-
-/*
- * avl trees: interface
- *
- * basic usage:
- * typedef struct foo {
- * ...
- * uu_avl_node_t foo_node;
- * ...
- * } foo_t;
- *
- * static int
- * foo_compare(void *l_arg, void *r_arg, void *private)
- * {
- * foo_t *l = l_arg;
- * foo_t *r = r_arg;
- *
- * if (... l greater than r ...)
- * return (1);
- * if (... l less than r ...)
- * return (-1);
- * return (0);
- * }
- *
- * ...
- * // at initialization time
- * foo_pool = uu_avl_pool_create("foo_pool",
- * sizeof (foo_t), offsetof(foo_t, foo_node), foo_compare,
- * debugging? 0 : UU_AVL_POOL_DEBUG);
- * ...
- */
-uu_avl_pool_t *uu_avl_pool_create(const char *, size_t, size_t,
- uu_compare_fn_t *, uint32_t);
-#define UU_AVL_POOL_DEBUG 0x00000001
-
-void uu_avl_pool_destroy(uu_avl_pool_t *);
-
-/*
- * usage:
- *
- * foo_t *a;
- * a = malloc(sizeof(*a));
- * uu_avl_node_init(a, &a->foo_avl, pool);
- * ...
- * uu_avl_node_fini(a, &a->foo_avl, pool);
- * free(a);
- */
-void uu_avl_node_init(void *, uu_avl_node_t *, uu_avl_pool_t *);
-void uu_avl_node_fini(void *, uu_avl_node_t *, uu_avl_pool_t *);
-
-uu_avl_t *uu_avl_create(uu_avl_pool_t *, void *_parent, uint32_t);
-#define UU_AVL_DEBUG 0x00000001
-
-void uu_avl_destroy(uu_avl_t *); /* list must be empty */
-
-size_t uu_avl_numnodes(uu_avl_t *);
-
-void *uu_avl_first(uu_avl_t *);
-void *uu_avl_last(uu_avl_t *);
-
-void *uu_avl_next(uu_avl_t *, void *);
-void *uu_avl_prev(uu_avl_t *, void *);
-
-int uu_avl_walk(uu_avl_t *, uu_walk_fn_t *, void *, uint32_t);
-
-uu_avl_walk_t *uu_avl_walk_start(uu_avl_t *, uint32_t);
-void *uu_avl_walk_next(uu_avl_walk_t *);
-void uu_avl_walk_end(uu_avl_walk_t *);
-
-void *uu_avl_find(uu_avl_t *, void *, void *, uu_avl_index_t *);
-void uu_avl_insert(uu_avl_t *, void *, uu_avl_index_t);
-
-void *uu_avl_nearest_next(uu_avl_t *, uu_avl_index_t);
-void *uu_avl_nearest_prev(uu_avl_t *, uu_avl_index_t);
-
-void *uu_avl_teardown(uu_avl_t *, void **);
-
-void uu_avl_remove(uu_avl_t *, void *);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBUUTIL_H */
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/libuutil_common.h b/cddl/contrib/opensolaris/lib/libuutil/common/libuutil_common.h
deleted file mode 100644
index 9ebaaedfd2379..0000000000000
--- a/cddl/contrib/opensolaris/lib/libuutil/common/libuutil_common.h
+++ /dev/null
@@ -1,35 +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 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef _LIBUUTIL_COMMON_H
-#define _LIBUUTIL_COMMON_H
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#include <libuutil.h>
-#include <libuutil_impl.h>
-
-#endif /* _LIBUUTIL_COMMON_H */
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/libuutil_impl.h b/cddl/contrib/opensolaris/lib/libuutil/common/libuutil_impl.h
deleted file mode 100644
index 9466e59745819..0000000000000
--- a/cddl/contrib/opensolaris/lib/libuutil/common/libuutil_impl.h
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * 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 2005 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#ifndef _LIBUUTIL_IMPL_H
-#define _LIBUUTIL_IMPL_H
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#include <libuutil.h>
-#include <pthread.h>
-
-#include <sys/avl_impl.h>
-#include <sys/byteorder.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-void uu_set_error(uint_t);
-#pragma rarely_called(uu_set_error)
-
-/*PRINTFLIKE1*/
-void uu_panic(const char *format, ...);
-#pragma rarely_called(uu_panic)
-
-struct uu_dprintf {
- char *uud_name;
- uu_dprintf_severity_t uud_severity;
- uint_t uud_flags;
-};
-
-/*
- * For debugging purposes, libuutil keeps around linked lists of all uu_lists
- * and uu_avls, along with pointers to their parents. These can cause false
- * negatives when looking for memory leaks, so we encode the pointers by
- * storing them with swapped endianness; this is not perfect, but it's about
- * the best we can do without wasting a lot of space.
- */
-#ifdef _LP64
-#define UU_PTR_ENCODE(ptr) BSWAP_64((uintptr_t)(void *)(ptr))
-#else
-#define UU_PTR_ENCODE(ptr) BSWAP_32((uintptr_t)(void *)(ptr))
-#endif
-
-#define UU_PTR_DECODE(ptr) ((void *)UU_PTR_ENCODE(ptr))
-
-/*
- * uu_list structures
- */
-typedef struct uu_list_node_impl {
- struct uu_list_node_impl *uln_next;
- struct uu_list_node_impl *uln_prev;
-} uu_list_node_impl_t;
-
-struct uu_list_walk {
- uu_list_walk_t *ulw_next;
- uu_list_walk_t *ulw_prev;
-
- uu_list_t *ulw_list;
- int8_t ulw_dir;
- uint8_t ulw_robust;
- uu_list_node_impl_t *ulw_next_result;
-};
-
-struct uu_list {
- uintptr_t ul_next_enc;
- uintptr_t ul_prev_enc;
-
- uu_list_pool_t *ul_pool;
- uintptr_t ul_parent_enc; /* encoded parent pointer */
- size_t ul_offset;
- size_t ul_numnodes;
- uint8_t ul_debug;
- uint8_t ul_sorted;
- uint8_t ul_index; /* mark for uu_list_index_ts */
-
- uu_list_node_impl_t ul_null_node;
- uu_list_walk_t ul_null_walk; /* for robust walkers */
-};
-
-#define UU_LIST_PTR(ptr) ((uu_list_t *)UU_PTR_DECODE(ptr))
-
-#define UU_LIST_POOL_MAXNAME 64
-
-struct uu_list_pool {
- uu_list_pool_t *ulp_next;
- uu_list_pool_t *ulp_prev;
-
- char ulp_name[UU_LIST_POOL_MAXNAME];
- size_t ulp_nodeoffset;
- size_t ulp_objsize;
- uu_compare_fn_t *ulp_cmp;
- uint8_t ulp_debug;
- uint8_t ulp_last_index;
- pthread_mutex_t ulp_lock; /* protects null_list */
- uu_list_t ulp_null_list;
-};
-
-/*
- * uu_avl structures
- */
-typedef struct avl_node uu_avl_node_impl_t;
-
-struct uu_avl_walk {
- uu_avl_walk_t *uaw_next;
- uu_avl_walk_t *uaw_prev;
-
- uu_avl_t *uaw_avl;
- void *uaw_next_result;
- int8_t uaw_dir;
- uint8_t uaw_robust;
-};
-
-struct uu_avl {
- uintptr_t ua_next_enc;
- uintptr_t ua_prev_enc;
-
- uu_avl_pool_t *ua_pool;
- uintptr_t ua_parent_enc;
- uint8_t ua_debug;
- uint8_t ua_index; /* mark for uu_avl_index_ts */
-
- struct avl_tree ua_tree;
- uu_avl_walk_t ua_null_walk;
-};
-
-#define UU_AVL_PTR(x) ((uu_avl_t *)UU_PTR_DECODE(x))
-
-#define UU_AVL_POOL_MAXNAME 64
-
-struct uu_avl_pool {
- uu_avl_pool_t *uap_next;
- uu_avl_pool_t *uap_prev;
-
- char uap_name[UU_AVL_POOL_MAXNAME];
- size_t uap_nodeoffset;
- size_t uap_objsize;
- uu_compare_fn_t *uap_cmp;
- uint8_t uap_debug;
- uint8_t uap_last_index;
- pthread_mutex_t uap_lock; /* protects null_avl */
- uu_avl_t uap_null_avl;
-};
-
-/*
- * atfork() handlers
- */
-void uu_avl_lockup(void);
-void uu_avl_release(void);
-
-void uu_list_lockup(void);
-void uu_list_release(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBUUTIL_IMPL_H */
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/uu_alloc.c b/cddl/contrib/opensolaris/lib/libuutil/common/uu_alloc.c
deleted file mode 100644
index 2bef759d525ec..0000000000000
--- a/cddl/contrib/opensolaris/lib/libuutil/common/uu_alloc.c
+++ /dev/null
@@ -1,135 +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) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
- */
-
-#include "libuutil_common.h"
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-void *
-uu_zalloc(size_t n)
-{
- void *p = malloc(n);
-
- if (p == NULL) {
- uu_set_error(UU_ERROR_SYSTEM);
- return (NULL);
- }
-
- (void) memset(p, 0, n);
-
- return (p);
-}
-
-void
-uu_free(void *p)
-{
- free(p);
-}
-
-char *
-uu_strdup(const char *str)
-{
- char *buf = NULL;
-
- if (str != NULL) {
- size_t sz;
-
- sz = strlen(str) + 1;
- buf = uu_zalloc(sz);
- if (buf != NULL)
- (void) memcpy(buf, str, sz);
- }
- return (buf);
-}
-
-/*
- * Duplicate up to n bytes of a string. Kind of sort of like
- * strdup(strlcpy(s, n)).
- */
-char *
-uu_strndup(const char *s, size_t n)
-{
- size_t len;
- char *p;
-
- len = strnlen(s, n);
- p = uu_zalloc(len + 1);
- if (p == NULL)
- return (NULL);
-
- if (len > 0)
- (void) memcpy(p, s, len);
- p[len] = '\0';
-
- return (p);
-}
-
-/*
- * Duplicate a block of memory. Combines malloc with memcpy, much as
- * strdup combines malloc, strlen, and strcpy.
- */
-void *
-uu_memdup(const void *buf, size_t sz)
-{
- void *p;
-
- p = uu_zalloc(sz);
- if (p == NULL)
- return (NULL);
- (void) memcpy(p, buf, sz);
- return (p);
-}
-
-char *
-uu_msprintf(const char *format, ...)
-{
- va_list args;
- char attic[1];
- uint_t M, m;
- char *b;
-
- va_start(args, format);
- M = vsnprintf(attic, 1, format, args);
- va_end(args);
-
- for (;;) {
- m = M;
- if ((b = uu_zalloc(m + 1)) == NULL)
- return (NULL);
-
- va_start(args, format);
- M = vsnprintf(b, m + 1, format, args);
- va_end(args);
-
- if (M == m)
- break; /* sizes match */
-
- uu_free(b);
- }
-
- return (b);
-}
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/uu_avl.c b/cddl/contrib/opensolaris/lib/libuutil/common/uu_avl.c
deleted file mode 100644
index 5e78ececeec93..0000000000000
--- a/cddl/contrib/opensolaris/lib/libuutil/common/uu_avl.c
+++ /dev/null
@@ -1,570 +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 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#include "libuutil_common.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/avl.h>
-
-static uu_avl_pool_t uu_null_apool = { &uu_null_apool, &uu_null_apool };
-static pthread_mutex_t uu_apool_list_lock = PTHREAD_MUTEX_INITIALIZER;
-
-/*
- * The index mark change on every insert and delete, to catch stale
- * references.
- *
- * We leave the low bit alone, since the avl code uses it.
- */
-#define INDEX_MAX (sizeof (uintptr_t) - 2)
-#define INDEX_NEXT(m) (((m) == INDEX_MAX)? 2 : ((m) + 2) & INDEX_MAX)
-
-#define INDEX_DECODE(i) ((i) & ~INDEX_MAX)
-#define INDEX_ENCODE(p, n) (((n) & ~INDEX_MAX) | (p)->ua_index)
-#define INDEX_VALID(p, i) (((i) & INDEX_MAX) == (p)->ua_index)
-#define INDEX_CHECK(i) (((i) & INDEX_MAX) != 0)
-
-/*
- * When an element is inactive (not in a tree), we keep a marked pointer to
- * its containing pool in its first word, and a NULL pointer in its second.
- *
- * On insert, we use these to verify that it comes from the correct pool.
- */
-#define NODE_ARRAY(p, n) ((uintptr_t *)((uintptr_t)(n) + \
- (pp)->uap_nodeoffset))
-
-#define POOL_TO_MARKER(pp) (((uintptr_t)(pp) | 1))
-
-#define DEAD_MARKER 0xc4
-
-uu_avl_pool_t *
-uu_avl_pool_create(const char *name, size_t objsize, size_t nodeoffset,
- uu_compare_fn_t *compare_func, uint32_t flags)
-{
- uu_avl_pool_t *pp, *next, *prev;
-
- if (name == NULL ||
- uu_check_name(name, UU_NAME_DOMAIN) == -1 ||
- nodeoffset + sizeof (uu_avl_node_t) > objsize ||
- compare_func == NULL) {
- uu_set_error(UU_ERROR_INVALID_ARGUMENT);
- return (NULL);
- }
-
- if (flags & ~UU_AVL_POOL_DEBUG) {
- uu_set_error(UU_ERROR_UNKNOWN_FLAG);
- return (NULL);
- }
-
- pp = uu_zalloc(sizeof (uu_avl_pool_t));
- if (pp == NULL) {
- uu_set_error(UU_ERROR_NO_MEMORY);
- return (NULL);
- }
-
- (void) strlcpy(pp->uap_name, name, sizeof (pp->uap_name));
- pp->uap_nodeoffset = nodeoffset;
- pp->uap_objsize = objsize;
- pp->uap_cmp = compare_func;
- if (flags & UU_AVL_POOL_DEBUG)
- pp->uap_debug = 1;
- pp->uap_last_index = 0;
-
- (void) pthread_mutex_init(&pp->uap_lock, NULL);
-
- pp->uap_null_avl.ua_next_enc = UU_PTR_ENCODE(&pp->uap_null_avl);
- pp->uap_null_avl.ua_prev_enc = UU_PTR_ENCODE(&pp->uap_null_avl);
-
- (void) pthread_mutex_lock(&uu_apool_list_lock);
- pp->uap_next = next = &uu_null_apool;
- pp->uap_prev = prev = next->uap_prev;
- next->uap_prev = pp;
- prev->uap_next = pp;
- (void) pthread_mutex_unlock(&uu_apool_list_lock);
-
- return (pp);
-}
-
-void
-uu_avl_pool_destroy(uu_avl_pool_t *pp)
-{
- if (pp->uap_debug) {
- if (pp->uap_null_avl.ua_next_enc !=
- UU_PTR_ENCODE(&pp->uap_null_avl) ||
- pp->uap_null_avl.ua_prev_enc !=
- UU_PTR_ENCODE(&pp->uap_null_avl)) {
- uu_panic("uu_avl_pool_destroy: Pool \"%.*s\" (%p) has "
- "outstanding avls, or is corrupt.\n",
- (int)sizeof (pp->uap_name), pp->uap_name,
- (void *)pp);
- }
- }
- (void) pthread_mutex_lock(&uu_apool_list_lock);
- pp->uap_next->uap_prev = pp->uap_prev;
- pp->uap_prev->uap_next = pp->uap_next;
- (void) pthread_mutex_unlock(&uu_apool_list_lock);
- (void) pthread_mutex_destroy(&pp->uap_lock);
- pp->uap_prev = NULL;
- pp->uap_next = NULL;
- uu_free(pp);
-}
-
-void
-uu_avl_node_init(void *base, uu_avl_node_t *np, uu_avl_pool_t *pp)
-{
- uintptr_t *na = (uintptr_t *)np;
-
- if (pp->uap_debug) {
- uintptr_t offset = (uintptr_t)np - (uintptr_t)base;
- if (offset + sizeof (*np) > pp->uap_objsize) {
- uu_panic("uu_avl_node_init(%p, %p, %p (\"%s\")): "
- "offset %ld doesn't fit in object (size %ld)\n",
- base, (void *)np, (void *)pp, pp->uap_name,
- (long)offset, (long)pp->uap_objsize);
- }
- if (offset != pp->uap_nodeoffset) {
- uu_panic("uu_avl_node_init(%p, %p, %p (\"%s\")): "
- "offset %ld doesn't match pool's offset (%ld)\n",
- base, (void *)np, (void *)pp, pp->uap_name,
- (long)offset, (long)pp->uap_objsize);
- }
- }
-
- na[0] = POOL_TO_MARKER(pp);
- na[1] = 0;
-}
-
-void
-uu_avl_node_fini(void *base, uu_avl_node_t *np, uu_avl_pool_t *pp)
-{
- uintptr_t *na = (uintptr_t *)np;
-
- if (pp->uap_debug) {
- if (na[0] == DEAD_MARKER && na[1] == DEAD_MARKER) {
- uu_panic("uu_avl_node_fini(%p, %p, %p (\"%s\")): "
- "node already finied\n",
- base, (void *)np, (void *)pp, pp->uap_name);
- }
- if (na[0] != POOL_TO_MARKER(pp) || na[1] != 0) {
- uu_panic("uu_avl_node_fini(%p, %p, %p (\"%s\")): "
- "node corrupt, in tree, or in different pool\n",
- base, (void *)np, (void *)pp, pp->uap_name);
- }
- }
-
- na[0] = DEAD_MARKER;
- na[1] = DEAD_MARKER;
- na[2] = DEAD_MARKER;
-}
-
-struct uu_avl_node_compare_info {
- uu_compare_fn_t *ac_compare;
- void *ac_private;
- void *ac_right;
- void *ac_found;
-};
-
-static int
-uu_avl_node_compare(const void *l, const void *r)
-{
- struct uu_avl_node_compare_info *info =
- (struct uu_avl_node_compare_info *)l;
-
- int res = info->ac_compare(r, info->ac_right, info->ac_private);
-
- if (res == 0) {
- if (info->ac_found == NULL)
- info->ac_found = (void *)r;
- return (-1);
- }
- if (res < 0)
- return (1);
- return (-1);
-}
-
-uu_avl_t *
-uu_avl_create(uu_avl_pool_t *pp, void *parent, uint32_t flags)
-{
- uu_avl_t *ap, *next, *prev;
-
- if (flags & ~UU_AVL_DEBUG) {
- uu_set_error(UU_ERROR_UNKNOWN_FLAG);
- return (NULL);
- }
-
- ap = uu_zalloc(sizeof (*ap));
- if (ap == NULL) {
- uu_set_error(UU_ERROR_NO_MEMORY);
- return (NULL);
- }
-
- ap->ua_pool = pp;
- ap->ua_parent_enc = UU_PTR_ENCODE(parent);
- ap->ua_debug = pp->uap_debug || (flags & UU_AVL_DEBUG);
- ap->ua_index = (pp->uap_last_index = INDEX_NEXT(pp->uap_last_index));
-
- avl_create(&ap->ua_tree, &uu_avl_node_compare, pp->uap_objsize,
- pp->uap_nodeoffset);
-
- ap->ua_null_walk.uaw_next = &ap->ua_null_walk;
- ap->ua_null_walk.uaw_prev = &ap->ua_null_walk;
-
- (void) pthread_mutex_lock(&pp->uap_lock);
- next = &pp->uap_null_avl;
- prev = UU_PTR_DECODE(next->ua_prev_enc);
- ap->ua_next_enc = UU_PTR_ENCODE(next);
- ap->ua_prev_enc = UU_PTR_ENCODE(prev);
- next->ua_prev_enc = UU_PTR_ENCODE(ap);
- prev->ua_next_enc = UU_PTR_ENCODE(ap);
- (void) pthread_mutex_unlock(&pp->uap_lock);
-
- return (ap);
-}
-
-void
-uu_avl_destroy(uu_avl_t *ap)
-{
- uu_avl_pool_t *pp = ap->ua_pool;
-
- if (ap->ua_debug) {
- if (avl_numnodes(&ap->ua_tree) != 0) {
- uu_panic("uu_avl_destroy(%p): tree not empty\n",
- (void *)ap);
- }
- if (ap->ua_null_walk.uaw_next != &ap->ua_null_walk ||
- ap->ua_null_walk.uaw_prev != &ap->ua_null_walk) {
- uu_panic("uu_avl_destroy(%p): outstanding walkers\n",
- (void *)ap);
- }
- }
- (void) pthread_mutex_lock(&pp->uap_lock);
- UU_AVL_PTR(ap->ua_next_enc)->ua_prev_enc = ap->ua_prev_enc;
- UU_AVL_PTR(ap->ua_prev_enc)->ua_next_enc = ap->ua_next_enc;
- (void) pthread_mutex_unlock(&pp->uap_lock);
- ap->ua_prev_enc = UU_PTR_ENCODE(NULL);
- ap->ua_next_enc = UU_PTR_ENCODE(NULL);
-
- ap->ua_pool = NULL;
- avl_destroy(&ap->ua_tree);
-
- uu_free(ap);
-}
-
-size_t
-uu_avl_numnodes(uu_avl_t *ap)
-{
- return (avl_numnodes(&ap->ua_tree));
-}
-
-void *
-uu_avl_first(uu_avl_t *ap)
-{
- return (avl_first(&ap->ua_tree));
-}
-
-void *
-uu_avl_last(uu_avl_t *ap)
-{
- return (avl_last(&ap->ua_tree));
-}
-
-void *
-uu_avl_next(uu_avl_t *ap, void *node)
-{
- return (AVL_NEXT(&ap->ua_tree, node));
-}
-
-void *
-uu_avl_prev(uu_avl_t *ap, void *node)
-{
- return (AVL_PREV(&ap->ua_tree, node));
-}
-
-static void
-_avl_walk_init(uu_avl_walk_t *wp, uu_avl_t *ap, uint32_t flags)
-{
- uu_avl_walk_t *next, *prev;
-
- int robust = (flags & UU_WALK_ROBUST);
- int direction = (flags & UU_WALK_REVERSE)? -1 : 1;
-
- (void) memset(wp, 0, sizeof (*wp));
- wp->uaw_avl = ap;
- wp->uaw_robust = robust;
- wp->uaw_dir = direction;
-
- if (direction > 0)
- wp->uaw_next_result = avl_first(&ap->ua_tree);
- else
- wp->uaw_next_result = avl_last(&ap->ua_tree);
-
- if (ap->ua_debug || robust) {
- wp->uaw_next = next = &ap->ua_null_walk;
- wp->uaw_prev = prev = next->uaw_prev;
- next->uaw_prev = wp;
- prev->uaw_next = wp;
- }
-}
-
-static void *
-_avl_walk_advance(uu_avl_walk_t *wp, uu_avl_t *ap)
-{
- void *np = wp->uaw_next_result;
-
- avl_tree_t *t = &ap->ua_tree;
-
- if (np == NULL)
- return (NULL);
-
- wp->uaw_next_result = (wp->uaw_dir > 0)? AVL_NEXT(t, np) :
- AVL_PREV(t, np);
-
- return (np);
-}
-
-static void
-_avl_walk_fini(uu_avl_walk_t *wp)
-{
- if (wp->uaw_next != NULL) {
- wp->uaw_next->uaw_prev = wp->uaw_prev;
- wp->uaw_prev->uaw_next = wp->uaw_next;
- wp->uaw_next = NULL;
- wp->uaw_prev = NULL;
- }
- wp->uaw_avl = NULL;
- wp->uaw_next_result = NULL;
-}
-
-uu_avl_walk_t *
-uu_avl_walk_start(uu_avl_t *ap, uint32_t flags)
-{
- uu_avl_walk_t *wp;
-
- if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
- uu_set_error(UU_ERROR_UNKNOWN_FLAG);
- return (NULL);
- }
-
- wp = uu_zalloc(sizeof (*wp));
- if (wp == NULL) {
- uu_set_error(UU_ERROR_NO_MEMORY);
- return (NULL);
- }
-
- _avl_walk_init(wp, ap, flags);
- return (wp);
-}
-
-void *
-uu_avl_walk_next(uu_avl_walk_t *wp)
-{
- return (_avl_walk_advance(wp, wp->uaw_avl));
-}
-
-void
-uu_avl_walk_end(uu_avl_walk_t *wp)
-{
- _avl_walk_fini(wp);
- uu_free(wp);
-}
-
-int
-uu_avl_walk(uu_avl_t *ap, uu_walk_fn_t *func, void *private, uint32_t flags)
-{
- void *e;
- uu_avl_walk_t my_walk;
-
- int status = UU_WALK_NEXT;
-
- if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
- uu_set_error(UU_ERROR_UNKNOWN_FLAG);
- return (-1);
- }
-
- _avl_walk_init(&my_walk, ap, flags);
- while (status == UU_WALK_NEXT &&
- (e = _avl_walk_advance(&my_walk, ap)) != NULL)
- status = (*func)(e, private);
- _avl_walk_fini(&my_walk);
-
- if (status >= 0)
- return (0);
- uu_set_error(UU_ERROR_CALLBACK_FAILED);
- return (-1);
-}
-
-void
-uu_avl_remove(uu_avl_t *ap, void *elem)
-{
- uu_avl_walk_t *wp;
- uu_avl_pool_t *pp = ap->ua_pool;
- uintptr_t *na = NODE_ARRAY(pp, elem);
-
- if (ap->ua_debug) {
- /*
- * invalidate outstanding uu_avl_index_ts.
- */
- ap->ua_index = INDEX_NEXT(ap->ua_index);
- }
-
- /*
- * Robust walkers most be advanced, if we are removing the node
- * they are currently using. In debug mode, non-robust walkers
- * are also on the walker list.
- */
- for (wp = ap->ua_null_walk.uaw_next; wp != &ap->ua_null_walk;
- wp = wp->uaw_next) {
- if (wp->uaw_robust) {
- if (elem == wp->uaw_next_result)
- (void) _avl_walk_advance(wp, ap);
- } else if (wp->uaw_next_result != NULL) {
- uu_panic("uu_avl_remove(%p, %p): active non-robust "
- "walker\n", (void *)ap, elem);
- }
- }
-
- avl_remove(&ap->ua_tree, elem);
-
- na[0] = POOL_TO_MARKER(pp);
- na[1] = 0;
-}
-
-void *
-uu_avl_teardown(uu_avl_t *ap, void **cookie)
-{
- void *elem = avl_destroy_nodes(&ap->ua_tree, cookie);
-
- if (elem != NULL) {
- uu_avl_pool_t *pp = ap->ua_pool;
- uintptr_t *na = NODE_ARRAY(pp, elem);
-
- na[0] = POOL_TO_MARKER(pp);
- na[1] = 0;
- }
- return (elem);
-}
-
-void *
-uu_avl_find(uu_avl_t *ap, void *elem, void *private, uu_avl_index_t *out)
-{
- struct uu_avl_node_compare_info info;
- void *result;
-
- info.ac_compare = ap->ua_pool->uap_cmp;
- info.ac_private = private;
- info.ac_right = elem;
- info.ac_found = NULL;
-
- result = avl_find(&ap->ua_tree, &info, out);
- if (out != NULL)
- *out = INDEX_ENCODE(ap, *out);
-
- if (ap->ua_debug && result != NULL)
- uu_panic("uu_avl_find: internal error: avl_find succeeded\n");
-
- return (info.ac_found);
-}
-
-void
-uu_avl_insert(uu_avl_t *ap, void *elem, uu_avl_index_t idx)
-{
- if (ap->ua_debug) {
- uu_avl_pool_t *pp = ap->ua_pool;
- uintptr_t *na = NODE_ARRAY(pp, elem);
-
- if (na[1] != 0)
- uu_panic("uu_avl_insert(%p, %p, %p): node already "
- "in tree, or corrupt\n",
- (void *)ap, elem, (void *)idx);
- if (na[0] == 0)
- uu_panic("uu_avl_insert(%p, %p, %p): node not "
- "initialized\n",
- (void *)ap, elem, (void *)idx);
- if (na[0] != POOL_TO_MARKER(pp))
- uu_panic("uu_avl_insert(%p, %p, %p): node from "
- "other pool, or corrupt\n",
- (void *)ap, elem, (void *)idx);
-
- if (!INDEX_VALID(ap, idx))
- uu_panic("uu_avl_insert(%p, %p, %p): %s\n",
- (void *)ap, elem, (void *)idx,
- INDEX_CHECK(idx)? "outdated index" :
- "invalid index");
-
- /*
- * invalidate outstanding uu_avl_index_ts.
- */
- ap->ua_index = INDEX_NEXT(ap->ua_index);
- }
- avl_insert(&ap->ua_tree, elem, INDEX_DECODE(idx));
-}
-
-void *
-uu_avl_nearest_next(uu_avl_t *ap, uu_avl_index_t idx)
-{
- if (ap->ua_debug && !INDEX_VALID(ap, idx))
- uu_panic("uu_avl_nearest_next(%p, %p): %s\n",
- (void *)ap, (void *)idx, INDEX_CHECK(idx)?
- "outdated index" : "invalid index");
- return (avl_nearest(&ap->ua_tree, INDEX_DECODE(idx), AVL_AFTER));
-}
-
-void *
-uu_avl_nearest_prev(uu_avl_t *ap, uu_avl_index_t idx)
-{
- if (ap->ua_debug && !INDEX_VALID(ap, idx))
- uu_panic("uu_avl_nearest_prev(%p, %p): %s\n",
- (void *)ap, (void *)idx, INDEX_CHECK(idx)?
- "outdated index" : "invalid index");
- return (avl_nearest(&ap->ua_tree, INDEX_DECODE(idx), AVL_BEFORE));
-}
-
-/*
- * called from uu_lockup() and uu_release(), as part of our fork1()-safety.
- */
-void
-uu_avl_lockup(void)
-{
- uu_avl_pool_t *pp;
-
- (void) pthread_mutex_lock(&uu_apool_list_lock);
- for (pp = uu_null_apool.uap_next; pp != &uu_null_apool;
- pp = pp->uap_next)
- (void) pthread_mutex_lock(&pp->uap_lock);
-}
-
-void
-uu_avl_release(void)
-{
- uu_avl_pool_t *pp;
-
- for (pp = uu_null_apool.uap_next; pp != &uu_null_apool;
- pp = pp->uap_next)
- (void) pthread_mutex_unlock(&pp->uap_lock);
- (void) pthread_mutex_unlock(&uu_apool_list_lock);
-}
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/uu_dprintf.c b/cddl/contrib/opensolaris/lib/libuutil/common/uu_dprintf.c
deleted file mode 100644
index 528c3e7f6d25e..0000000000000
--- a/cddl/contrib/opensolaris/lib/libuutil/common/uu_dprintf.c
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * 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 2004 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#include "libuutil_common.h"
-
-#include <errno.h>
-#include <libintl.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define FACILITY_FMT "%s (%s): "
-
-#if !defined(TEXT_DOMAIN)
-#define TEXT_DOMAIN "SYS_TEST"
-#endif
-
-static const char *
-strseverity(uu_dprintf_severity_t severity)
-{
- switch (severity) {
- case UU_DPRINTF_SILENT:
- return (dgettext(TEXT_DOMAIN, "silent"));
- case UU_DPRINTF_FATAL:
- return (dgettext(TEXT_DOMAIN, "FATAL"));
- case UU_DPRINTF_WARNING:
- return (dgettext(TEXT_DOMAIN, "WARNING"));
- case UU_DPRINTF_NOTICE:
- return (dgettext(TEXT_DOMAIN, "note"));
- case UU_DPRINTF_INFO:
- return (dgettext(TEXT_DOMAIN, "info"));
- case UU_DPRINTF_DEBUG:
- return (dgettext(TEXT_DOMAIN, "debug"));
- default:
- return (dgettext(TEXT_DOMAIN, "unspecified"));
- }
-}
-
-uu_dprintf_t *
-uu_dprintf_create(const char *name, uu_dprintf_severity_t severity,
- uint_t flags)
-{
- uu_dprintf_t *D;
-
- if (uu_check_name(name, UU_NAME_DOMAIN) == -1) {
- uu_set_error(UU_ERROR_INVALID_ARGUMENT);
- return (NULL);
- }
-
- if ((D = uu_zalloc(sizeof (uu_dprintf_t))) == NULL)
- return (NULL);
-
- if (name != NULL) {
- D->uud_name = strdup(name);
- if (D->uud_name == NULL) {
- uu_free(D);
- return (NULL);
- }
- } else {
- D->uud_name = NULL;
- }
-
- D->uud_severity = severity;
- D->uud_flags = flags;
-
- return (D);
-}
-
-/*PRINTFLIKE3*/
-void
-uu_dprintf(uu_dprintf_t *D, uu_dprintf_severity_t severity,
- const char *format, ...)
-{
- va_list alist;
-
- /* XXX Assert that severity is not UU_DPRINTF_SILENT. */
-
- if (severity > D->uud_severity)
- return;
-
- (void) fprintf(stderr, FACILITY_FMT, D->uud_name,
- strseverity(severity));
-
- va_start(alist, format);
- (void) vfprintf(stderr, format, alist);
- va_end(alist);
-}
-
-void
-uu_dprintf_destroy(uu_dprintf_t *D)
-{
- if (D->uud_name)
- free(D->uud_name);
-
- uu_free(D);
-}
-
-const char *
-uu_dprintf_getname(uu_dprintf_t *D)
-{
- return (D->uud_name);
-}
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/uu_ident.c b/cddl/contrib/opensolaris/lib/libuutil/common/uu_ident.c
deleted file mode 100644
index 9a643845f8c2b..0000000000000
--- a/cddl/contrib/opensolaris/lib/libuutil/common/uu_ident.c
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * 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 2004 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#include "libuutil_common.h"
-
-#include <string.h>
-
-/*
- * We require names of the form:
- * [provider,]identifier[/[provider,]identifier]...
- *
- * Where provider is either a stock symbol (SUNW) or a java-style reversed
- * domain name (com.sun).
- *
- * Both providers and identifiers must start with a letter, and may
- * only contain alphanumerics, dashes, and underlines. Providers
- * may also contain periods.
- *
- * Note that we do _not_ use the macros in <ctype.h>, since they are affected
- * by the current locale settings.
- */
-
-#define IS_ALPHA(c) \
- (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
-
-#define IS_DIGIT(c) \
- ((c) >= '0' && (c) <= '9')
-
-static int
-is_valid_ident(const char *s, const char *e, int allowdot)
-{
- char c;
-
- if (s >= e)
- return (0); /* name is empty */
-
- c = *s++;
- if (!IS_ALPHA(c))
- return (0); /* does not start with letter */
-
- while (s < e && (c = *s++) != 0) {
- if (IS_ALPHA(c) || IS_DIGIT(c) || c == '-' || c == '_' ||
- (allowdot && c == '.'))
- continue;
- return (0); /* invalid character */
- }
- return (1);
-}
-
-static int
-is_valid_component(const char *b, const char *e, uint_t flags)
-{
- char *sp;
-
- if (flags & UU_NAME_DOMAIN) {
- sp = strchr(b, ',');
- if (sp != NULL && sp < e) {
- if (!is_valid_ident(b, sp, 1))
- return (0);
- b = sp + 1;
- }
- }
-
- return (is_valid_ident(b, e, 0));
-}
-
-int
-uu_check_name(const char *name, uint_t flags)
-{
- const char *end = name + strlen(name);
- const char *p;
-
- if (flags & ~(UU_NAME_DOMAIN | UU_NAME_PATH)) {
- uu_set_error(UU_ERROR_UNKNOWN_FLAG);
- return (-1);
- }
-
- if (!(flags & UU_NAME_PATH)) {
- if (!is_valid_component(name, end, flags))
- goto bad;
- return (0);
- }
-
- while ((p = strchr(name, '/')) != NULL) {
- if (!is_valid_component(name, p - 1, flags))
- goto bad;
- name = p + 1;
- }
- if (!is_valid_component(name, end, flags))
- goto bad;
-
- return (0);
-
-bad:
- uu_set_error(UU_ERROR_INVALID_ARGUMENT);
- return (-1);
-}
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/uu_list.c b/cddl/contrib/opensolaris/lib/libuutil/common/uu_list.c
deleted file mode 100644
index 35c7ba8001030..0000000000000
--- a/cddl/contrib/opensolaris/lib/libuutil/common/uu_list.c
+++ /dev/null
@@ -1,718 +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 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#include "libuutil_common.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/time.h>
-
-#define ELEM_TO_NODE(lp, e) \
- ((uu_list_node_impl_t *)((uintptr_t)(e) + (lp)->ul_offset))
-
-#define NODE_TO_ELEM(lp, n) \
- ((void *)((uintptr_t)(n) - (lp)->ul_offset))
-
-/*
- * uu_list_index_ts define a location for insertion. They are simply a
- * pointer to the object after the insertion point. We store a mark
- * in the low-bits of the index, to help prevent mistakes.
- *
- * When debugging, the index mark changes on every insert and delete, to
- * catch stale references.
- */
-#define INDEX_MAX (sizeof (uintptr_t) - 1)
-#define INDEX_NEXT(m) (((m) == INDEX_MAX)? 1 : ((m) + 1) & INDEX_MAX)
-
-#define INDEX_TO_NODE(i) ((uu_list_node_impl_t *)((i) & ~INDEX_MAX))
-#define NODE_TO_INDEX(p, n) (((uintptr_t)(n) & ~INDEX_MAX) | (p)->ul_index)
-#define INDEX_VALID(p, i) (((i) & INDEX_MAX) == (p)->ul_index)
-#define INDEX_CHECK(i) (((i) & INDEX_MAX) != 0)
-
-#define POOL_TO_MARKER(pp) ((void *)((uintptr_t)(pp) | 1))
-
-static uu_list_pool_t uu_null_lpool = { &uu_null_lpool, &uu_null_lpool };
-static pthread_mutex_t uu_lpool_list_lock = PTHREAD_MUTEX_INITIALIZER;
-
-uu_list_pool_t *
-uu_list_pool_create(const char *name, size_t objsize,
- size_t nodeoffset, uu_compare_fn_t *compare_func, uint32_t flags)
-{
- uu_list_pool_t *pp, *next, *prev;
-
- if (name == NULL ||
- uu_check_name(name, UU_NAME_DOMAIN) == -1 ||
- nodeoffset + sizeof (uu_list_node_t) > objsize) {
- uu_set_error(UU_ERROR_INVALID_ARGUMENT);
- return (NULL);
- }
-
- if (flags & ~UU_LIST_POOL_DEBUG) {
- uu_set_error(UU_ERROR_UNKNOWN_FLAG);
- return (NULL);
- }
-
- pp = uu_zalloc(sizeof (uu_list_pool_t));
- if (pp == NULL) {
- uu_set_error(UU_ERROR_NO_MEMORY);
- return (NULL);
- }
-
- (void) strlcpy(pp->ulp_name, name, sizeof (pp->ulp_name));
- pp->ulp_nodeoffset = nodeoffset;
- pp->ulp_objsize = objsize;
- pp->ulp_cmp = compare_func;
- if (flags & UU_LIST_POOL_DEBUG)
- pp->ulp_debug = 1;
- pp->ulp_last_index = 0;
-
- (void) pthread_mutex_init(&pp->ulp_lock, NULL);
-
- pp->ulp_null_list.ul_next_enc = UU_PTR_ENCODE(&pp->ulp_null_list);
- pp->ulp_null_list.ul_prev_enc = UU_PTR_ENCODE(&pp->ulp_null_list);
-
- (void) pthread_mutex_lock(&uu_lpool_list_lock);
- pp->ulp_next = next = &uu_null_lpool;
- pp->ulp_prev = prev = next->ulp_prev;
- next->ulp_prev = pp;
- prev->ulp_next = pp;
- (void) pthread_mutex_unlock(&uu_lpool_list_lock);
-
- return (pp);
-}
-
-void
-uu_list_pool_destroy(uu_list_pool_t *pp)
-{
- if (pp->ulp_debug) {
- if (pp->ulp_null_list.ul_next_enc !=
- UU_PTR_ENCODE(&pp->ulp_null_list) ||
- pp->ulp_null_list.ul_prev_enc !=
- UU_PTR_ENCODE(&pp->ulp_null_list)) {
- uu_panic("uu_list_pool_destroy: Pool \"%.*s\" (%p) has "
- "outstanding lists, or is corrupt.\n",
- (int)sizeof (pp->ulp_name), pp->ulp_name,
- (void *)pp);
- }
- }
- (void) pthread_mutex_lock(&uu_lpool_list_lock);
- pp->ulp_next->ulp_prev = pp->ulp_prev;
- pp->ulp_prev->ulp_next = pp->ulp_next;
- (void) pthread_mutex_unlock(&uu_lpool_list_lock);
- pp->ulp_prev = NULL;
- pp->ulp_next = NULL;
- uu_free(pp);
-}
-
-void
-uu_list_node_init(void *base, uu_list_node_t *np_arg, uu_list_pool_t *pp)
-{
- uu_list_node_impl_t *np = (uu_list_node_impl_t *)np_arg;
-
- if (pp->ulp_debug) {
- uintptr_t offset = (uintptr_t)np - (uintptr_t)base;
- if (offset + sizeof (*np) > pp->ulp_objsize) {
- uu_panic("uu_list_node_init(%p, %p, %p (\"%s\")): "
- "offset %ld doesn't fit in object (size %ld)\n",
- base, (void *)np, (void *)pp, pp->ulp_name,
- (long)offset, (long)pp->ulp_objsize);
- }
- if (offset != pp->ulp_nodeoffset) {
- uu_panic("uu_list_node_init(%p, %p, %p (\"%s\")): "
- "offset %ld doesn't match pool's offset (%ld)\n",
- base, (void *)np, (void *)pp, pp->ulp_name,
- (long)offset, (long)pp->ulp_objsize);
- }
- }
- np->uln_next = POOL_TO_MARKER(pp);
- np->uln_prev = NULL;
-}
-
-void
-uu_list_node_fini(void *base, uu_list_node_t *np_arg, uu_list_pool_t *pp)
-{
- uu_list_node_impl_t *np = (uu_list_node_impl_t *)np_arg;
-
- if (pp->ulp_debug) {
- if (np->uln_next == NULL &&
- np->uln_prev == NULL) {
- uu_panic("uu_list_node_fini(%p, %p, %p (\"%s\")): "
- "node already finied\n",
- base, (void *)np_arg, (void *)pp, pp->ulp_name);
- }
- if (np->uln_next != POOL_TO_MARKER(pp) ||
- np->uln_prev != NULL) {
- uu_panic("uu_list_node_fini(%p, %p, %p (\"%s\")): "
- "node corrupt or on list\n",
- base, (void *)np_arg, (void *)pp, pp->ulp_name);
- }
- }
- np->uln_next = NULL;
- np->uln_prev = NULL;
-}
-
-uu_list_t *
-uu_list_create(uu_list_pool_t *pp, void *parent, uint32_t flags)
-{
- uu_list_t *lp, *next, *prev;
-
- if (flags & ~(UU_LIST_DEBUG | UU_LIST_SORTED)) {
- uu_set_error(UU_ERROR_UNKNOWN_FLAG);
- return (NULL);
- }
-
- if ((flags & UU_LIST_SORTED) && pp->ulp_cmp == NULL) {
- if (pp->ulp_debug)
- uu_panic("uu_list_create(%p, ...): requested "
- "UU_LIST_SORTED, but pool has no comparison func\n",
- (void *)pp);
- uu_set_error(UU_ERROR_NOT_SUPPORTED);
- return (NULL);
- }
-
- lp = uu_zalloc(sizeof (*lp));
- if (lp == NULL) {
- uu_set_error(UU_ERROR_NO_MEMORY);
- return (NULL);
- }
-
- lp->ul_pool = pp;
- lp->ul_parent_enc = UU_PTR_ENCODE(parent);
- lp->ul_offset = pp->ulp_nodeoffset;
- lp->ul_debug = pp->ulp_debug || (flags & UU_LIST_DEBUG);
- lp->ul_sorted = (flags & UU_LIST_SORTED);
- lp->ul_numnodes = 0;
- lp->ul_index = (pp->ulp_last_index = INDEX_NEXT(pp->ulp_last_index));
-
- lp->ul_null_node.uln_next = &lp->ul_null_node;
- lp->ul_null_node.uln_prev = &lp->ul_null_node;
-
- lp->ul_null_walk.ulw_next = &lp->ul_null_walk;
- lp->ul_null_walk.ulw_prev = &lp->ul_null_walk;
-
- (void) pthread_mutex_lock(&pp->ulp_lock);
- next = &pp->ulp_null_list;
- prev = UU_PTR_DECODE(next->ul_prev_enc);
- lp->ul_next_enc = UU_PTR_ENCODE(next);
- lp->ul_prev_enc = UU_PTR_ENCODE(prev);
- next->ul_prev_enc = UU_PTR_ENCODE(lp);
- prev->ul_next_enc = UU_PTR_ENCODE(lp);
- (void) pthread_mutex_unlock(&pp->ulp_lock);
-
- return (lp);
-}
-
-void
-uu_list_destroy(uu_list_t *lp)
-{
- uu_list_pool_t *pp = lp->ul_pool;
-
- if (lp->ul_debug) {
- if (lp->ul_null_node.uln_next != &lp->ul_null_node ||
- lp->ul_null_node.uln_prev != &lp->ul_null_node) {
- uu_panic("uu_list_destroy(%p): list not empty\n",
- (void *)lp);
- }
- if (lp->ul_numnodes != 0) {
- uu_panic("uu_list_destroy(%p): numnodes is nonzero, "
- "but list is empty\n", (void *)lp);
- }
- if (lp->ul_null_walk.ulw_next != &lp->ul_null_walk ||
- lp->ul_null_walk.ulw_prev != &lp->ul_null_walk) {
- uu_panic("uu_list_destroy(%p): outstanding walkers\n",
- (void *)lp);
- }
- }
-
- (void) pthread_mutex_lock(&pp->ulp_lock);
- UU_LIST_PTR(lp->ul_next_enc)->ul_prev_enc = lp->ul_prev_enc;
- UU_LIST_PTR(lp->ul_prev_enc)->ul_next_enc = lp->ul_next_enc;
- (void) pthread_mutex_unlock(&pp->ulp_lock);
- lp->ul_prev_enc = UU_PTR_ENCODE(NULL);
- lp->ul_next_enc = UU_PTR_ENCODE(NULL);
- lp->ul_pool = NULL;
- uu_free(lp);
-}
-
-static void
-list_insert(uu_list_t *lp, uu_list_node_impl_t *np, uu_list_node_impl_t *prev,
- uu_list_node_impl_t *next)
-{
- if (lp->ul_debug) {
- if (next->uln_prev != prev || prev->uln_next != next)
- uu_panic("insert(%p): internal error: %p and %p not "
- "neighbors\n", (void *)lp, (void *)next,
- (void *)prev);
-
- if (np->uln_next != POOL_TO_MARKER(lp->ul_pool) ||
- np->uln_prev != NULL) {
- uu_panic("insert(%p): elem %p node %p corrupt, "
- "not initialized, or already in a list.\n",
- (void *)lp, NODE_TO_ELEM(lp, np), (void *)np);
- }
- /*
- * invalidate outstanding uu_list_index_ts.
- */
- lp->ul_index = INDEX_NEXT(lp->ul_index);
- }
- np->uln_next = next;
- np->uln_prev = prev;
- next->uln_prev = np;
- prev->uln_next = np;
-
- lp->ul_numnodes++;
-}
-
-void
-uu_list_insert(uu_list_t *lp, void *elem, uu_list_index_t idx)
-{
- uu_list_node_impl_t *np;
-
- np = INDEX_TO_NODE(idx);
- if (np == NULL)
- np = &lp->ul_null_node;
-
- if (lp->ul_debug) {
- if (!INDEX_VALID(lp, idx))
- uu_panic("uu_list_insert(%p, %p, %p): %s\n",
- (void *)lp, elem, (void *)idx,
- INDEX_CHECK(idx)? "outdated index" :
- "invalid index");
- if (np->uln_prev == NULL)
- uu_panic("uu_list_insert(%p, %p, %p): out-of-date "
- "index\n", (void *)lp, elem, (void *)idx);
- }
-
- list_insert(lp, ELEM_TO_NODE(lp, elem), np->uln_prev, np);
-}
-
-void *
-uu_list_find(uu_list_t *lp, void *elem, void *private, uu_list_index_t *out)
-{
- int sorted = lp->ul_sorted;
- uu_compare_fn_t *func = lp->ul_pool->ulp_cmp;
- uu_list_node_impl_t *np;
-
- if (func == NULL) {
- if (out != NULL)
- *out = 0;
- uu_set_error(UU_ERROR_NOT_SUPPORTED);
- return (NULL);
- }
- for (np = lp->ul_null_node.uln_next; np != &lp->ul_null_node;
- np = np->uln_next) {
- void *ep = NODE_TO_ELEM(lp, np);
- int cmp = func(ep, elem, private);
- if (cmp == 0) {
- if (out != NULL)
- *out = NODE_TO_INDEX(lp, np);
- return (ep);
- }
- if (sorted && cmp > 0) {
- if (out != NULL)
- *out = NODE_TO_INDEX(lp, np);
- return (NULL);
- }
- }
- if (out != NULL)
- *out = NODE_TO_INDEX(lp, 0);
- return (NULL);
-}
-
-void *
-uu_list_nearest_next(uu_list_t *lp, uu_list_index_t idx)
-{
- uu_list_node_impl_t *np = INDEX_TO_NODE(idx);
-
- if (np == NULL)
- np = &lp->ul_null_node;
-
- if (lp->ul_debug) {
- if (!INDEX_VALID(lp, idx))
- uu_panic("uu_list_nearest_next(%p, %p): %s\n",
- (void *)lp, (void *)idx,
- INDEX_CHECK(idx)? "outdated index" :
- "invalid index");
- if (np->uln_prev == NULL)
- uu_panic("uu_list_nearest_next(%p, %p): out-of-date "
- "index\n", (void *)lp, (void *)idx);
- }
-
- if (np == &lp->ul_null_node)
- return (NULL);
- else
- return (NODE_TO_ELEM(lp, np));
-}
-
-void *
-uu_list_nearest_prev(uu_list_t *lp, uu_list_index_t idx)
-{
- uu_list_node_impl_t *np = INDEX_TO_NODE(idx);
-
- if (np == NULL)
- np = &lp->ul_null_node;
-
- if (lp->ul_debug) {
- if (!INDEX_VALID(lp, idx))
- uu_panic("uu_list_nearest_prev(%p, %p): %s\n",
- (void *)lp, (void *)idx, INDEX_CHECK(idx)?
- "outdated index" : "invalid index");
- if (np->uln_prev == NULL)
- uu_panic("uu_list_nearest_prev(%p, %p): out-of-date "
- "index\n", (void *)lp, (void *)idx);
- }
-
- if ((np = np->uln_prev) == &lp->ul_null_node)
- return (NULL);
- else
- return (NODE_TO_ELEM(lp, np));
-}
-
-static void
-list_walk_init(uu_list_walk_t *wp, uu_list_t *lp, uint32_t flags)
-{
- uu_list_walk_t *next, *prev;
-
- int robust = (flags & UU_WALK_ROBUST);
- int direction = (flags & UU_WALK_REVERSE)? -1 : 1;
-
- (void) memset(wp, 0, sizeof (*wp));
- wp->ulw_list = lp;
- wp->ulw_robust = robust;
- wp->ulw_dir = direction;
- if (direction > 0)
- wp->ulw_next_result = lp->ul_null_node.uln_next;
- else
- wp->ulw_next_result = lp->ul_null_node.uln_prev;
-
- if (lp->ul_debug || robust) {
- /*
- * Add this walker to the list's list of walkers so
- * uu_list_remove() can advance us if somebody tries to
- * remove ulw_next_result.
- */
- wp->ulw_next = next = &lp->ul_null_walk;
- wp->ulw_prev = prev = next->ulw_prev;
- next->ulw_prev = wp;
- prev->ulw_next = wp;
- }
-}
-
-static uu_list_node_impl_t *
-list_walk_advance(uu_list_walk_t *wp, uu_list_t *lp)
-{
- uu_list_node_impl_t *np = wp->ulw_next_result;
- uu_list_node_impl_t *next;
-
- if (np == &lp->ul_null_node)
- return (NULL);
-
- next = (wp->ulw_dir > 0)? np->uln_next : np->uln_prev;
-
- wp->ulw_next_result = next;
- return (np);
-}
-
-static void
-list_walk_fini(uu_list_walk_t *wp)
-{
- /* GLXXX debugging? */
- if (wp->ulw_next != NULL) {
- wp->ulw_next->ulw_prev = wp->ulw_prev;
- wp->ulw_prev->ulw_next = wp->ulw_next;
- wp->ulw_next = NULL;
- wp->ulw_prev = NULL;
- }
- wp->ulw_list = NULL;
- wp->ulw_next_result = NULL;
-}
-
-uu_list_walk_t *
-uu_list_walk_start(uu_list_t *lp, uint32_t flags)
-{
- uu_list_walk_t *wp;
-
- if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
- uu_set_error(UU_ERROR_UNKNOWN_FLAG);
- return (NULL);
- }
-
- wp = uu_zalloc(sizeof (*wp));
- if (wp == NULL) {
- uu_set_error(UU_ERROR_NO_MEMORY);
- return (NULL);
- }
-
- list_walk_init(wp, lp, flags);
- return (wp);
-}
-
-void *
-uu_list_walk_next(uu_list_walk_t *wp)
-{
- uu_list_t *lp = wp->ulw_list;
- uu_list_node_impl_t *np = list_walk_advance(wp, lp);
-
- if (np == NULL)
- return (NULL);
-
- return (NODE_TO_ELEM(lp, np));
-}
-
-void
-uu_list_walk_end(uu_list_walk_t *wp)
-{
- list_walk_fini(wp);
- uu_free(wp);
-}
-
-int
-uu_list_walk(uu_list_t *lp, uu_walk_fn_t *func, void *private, uint32_t flags)
-{
- uu_list_node_impl_t *np;
-
- int status = UU_WALK_NEXT;
-
- int robust = (flags & UU_WALK_ROBUST);
- int reverse = (flags & UU_WALK_REVERSE);
-
- if (flags & ~(UU_WALK_ROBUST | UU_WALK_REVERSE)) {
- uu_set_error(UU_ERROR_UNKNOWN_FLAG);
- return (-1);
- }
-
- if (lp->ul_debug || robust) {
- uu_list_walk_t my_walk;
- void *e;
-
- list_walk_init(&my_walk, lp, flags);
- while (status == UU_WALK_NEXT &&
- (e = uu_list_walk_next(&my_walk)) != NULL)
- status = (*func)(e, private);
- list_walk_fini(&my_walk);
- } else {
- if (!reverse) {
- for (np = lp->ul_null_node.uln_next;
- status == UU_WALK_NEXT && np != &lp->ul_null_node;
- np = np->uln_next) {
- status = (*func)(NODE_TO_ELEM(lp, np), private);
- }
- } else {
- for (np = lp->ul_null_node.uln_prev;
- status == UU_WALK_NEXT && np != &lp->ul_null_node;
- np = np->uln_prev) {
- status = (*func)(NODE_TO_ELEM(lp, np), private);
- }
- }
- }
- if (status >= 0)
- return (0);
- uu_set_error(UU_ERROR_CALLBACK_FAILED);
- return (-1);
-}
-
-void
-uu_list_remove(uu_list_t *lp, void *elem)
-{
- uu_list_node_impl_t *np = ELEM_TO_NODE(lp, elem);
- uu_list_walk_t *wp;
-
- if (lp->ul_debug) {
- if (np->uln_prev == NULL)
- uu_panic("uu_list_remove(%p, %p): elem not on list\n",
- (void *)lp, elem);
- /*
- * invalidate outstanding uu_list_index_ts.
- */
- lp->ul_index = INDEX_NEXT(lp->ul_index);
- }
-
- /*
- * robust walkers must be advanced. In debug mode, non-robust
- * walkers are also on the list. If there are any, it's an error.
- */
- for (wp = lp->ul_null_walk.ulw_next; wp != &lp->ul_null_walk;
- wp = wp->ulw_next) {
- if (wp->ulw_robust) {
- if (np == wp->ulw_next_result)
- (void) list_walk_advance(wp, lp);
- } else if (wp->ulw_next_result != NULL) {
- uu_panic("uu_list_remove(%p, %p): active non-robust "
- "walker\n", (void *)lp, elem);
- }
- }
-
- np->uln_next->uln_prev = np->uln_prev;
- np->uln_prev->uln_next = np->uln_next;
-
- lp->ul_numnodes--;
-
- np->uln_next = POOL_TO_MARKER(lp->ul_pool);
- np->uln_prev = NULL;
-}
-
-void *
-uu_list_teardown(uu_list_t *lp, void **cookie)
-{
- void *ep;
-
- /*
- * XXX: disable list modification until list is empty
- */
- if (lp->ul_debug && *cookie != NULL)
- uu_panic("uu_list_teardown(%p, %p): unexpected cookie\n",
- (void *)lp, (void *)cookie);
-
- ep = uu_list_first(lp);
- if (ep)
- uu_list_remove(lp, ep);
- return (ep);
-}
-
-int
-uu_list_insert_before(uu_list_t *lp, void *target, void *elem)
-{
- uu_list_node_impl_t *np = ELEM_TO_NODE(lp, target);
-
- if (target == NULL)
- np = &lp->ul_null_node;
-
- if (lp->ul_debug) {
- if (np->uln_prev == NULL)
- uu_panic("uu_list_insert_before(%p, %p, %p): %p is "
- "not currently on a list\n",
- (void *)lp, target, elem, target);
- }
- if (lp->ul_sorted) {
- if (lp->ul_debug)
- uu_panic("uu_list_insert_before(%p, ...): list is "
- "UU_LIST_SORTED\n", (void *)lp);
- uu_set_error(UU_ERROR_NOT_SUPPORTED);
- return (-1);
- }
-
- list_insert(lp, ELEM_TO_NODE(lp, elem), np->uln_prev, np);
- return (0);
-}
-
-int
-uu_list_insert_after(uu_list_t *lp, void *target, void *elem)
-{
- uu_list_node_impl_t *np = ELEM_TO_NODE(lp, target);
-
- if (target == NULL)
- np = &lp->ul_null_node;
-
- if (lp->ul_debug) {
- if (np->uln_prev == NULL)
- uu_panic("uu_list_insert_after(%p, %p, %p): %p is "
- "not currently on a list\n",
- (void *)lp, target, elem, target);
- }
- if (lp->ul_sorted) {
- if (lp->ul_debug)
- uu_panic("uu_list_insert_after(%p, ...): list is "
- "UU_LIST_SORTED\n", (void *)lp);
- uu_set_error(UU_ERROR_NOT_SUPPORTED);
- return (-1);
- }
-
- list_insert(lp, ELEM_TO_NODE(lp, elem), np, np->uln_next);
- return (0);
-}
-
-size_t
-uu_list_numnodes(uu_list_t *lp)
-{
- return (lp->ul_numnodes);
-}
-
-void *
-uu_list_first(uu_list_t *lp)
-{
- uu_list_node_impl_t *n = lp->ul_null_node.uln_next;
- if (n == &lp->ul_null_node)
- return (NULL);
- return (NODE_TO_ELEM(lp, n));
-}
-
-void *
-uu_list_last(uu_list_t *lp)
-{
- uu_list_node_impl_t *n = lp->ul_null_node.uln_prev;
- if (n == &lp->ul_null_node)
- return (NULL);
- return (NODE_TO_ELEM(lp, n));
-}
-
-void *
-uu_list_next(uu_list_t *lp, void *elem)
-{
- uu_list_node_impl_t *n = ELEM_TO_NODE(lp, elem);
-
- n = n->uln_next;
- if (n == &lp->ul_null_node)
- return (NULL);
- return (NODE_TO_ELEM(lp, n));
-}
-
-void *
-uu_list_prev(uu_list_t *lp, void *elem)
-{
- uu_list_node_impl_t *n = ELEM_TO_NODE(lp, elem);
-
- n = n->uln_prev;
- if (n == &lp->ul_null_node)
- return (NULL);
- return (NODE_TO_ELEM(lp, n));
-}
-
-/*
- * called from uu_lockup() and uu_release(), as part of our fork1()-safety.
- */
-void
-uu_list_lockup(void)
-{
- uu_list_pool_t *pp;
-
- (void) pthread_mutex_lock(&uu_lpool_list_lock);
- for (pp = uu_null_lpool.ulp_next; pp != &uu_null_lpool;
- pp = pp->ulp_next)
- (void) pthread_mutex_lock(&pp->ulp_lock);
-}
-
-void
-uu_list_release(void)
-{
- uu_list_pool_t *pp;
-
- for (pp = uu_null_lpool.ulp_next; pp != &uu_null_lpool;
- pp = pp->ulp_next)
- (void) pthread_mutex_unlock(&pp->ulp_lock);
- (void) pthread_mutex_unlock(&uu_lpool_list_lock);
-}
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/uu_misc.c b/cddl/contrib/opensolaris/lib/libuutil/common/uu_misc.c
deleted file mode 100644
index b673834e4dcf5..0000000000000
--- a/cddl/contrib/opensolaris/lib/libuutil/common/uu_misc.c
+++ /dev/null
@@ -1,277 +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) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
- */
-
-#include "libuutil_common.h"
-
-#define HAVE_ASSFAIL 1
-
-#include <assert.h>
-#include <errno.h>
-#include <libintl.h>
-#include <pthread.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/debug.h>
-#include <thread.h>
-#include <unistd.h>
-#include <ctype.h>
-
-#if !defined(TEXT_DOMAIN)
-#define TEXT_DOMAIN "SYS_TEST"
-#endif
-
-/*
- * All of the old code under !defined(PTHREAD_ONCE_KEY_NP)
- * is here to enable the building of a native version of
- * libuutil.so when the build machine has not yet been upgraded
- * to a version of libc that provides pthread_key_create_once_np().
- * It should all be deleted when solaris_nevada ships.
- * The code is not MT-safe in a relaxed memory model.
- */
-
-#if defined(PTHREAD_ONCE_KEY_NP)
-static pthread_key_t uu_error_key = PTHREAD_ONCE_KEY_NP;
-#else /* PTHREAD_ONCE_KEY_NP */
-static pthread_key_t uu_error_key = 0;
-static pthread_mutex_t uu_key_lock = PTHREAD_MUTEX_INITIALIZER;
-#endif /* PTHREAD_ONCE_KEY_NP */
-
-static int uu_error_key_setup = 0;
-
-static pthread_mutex_t uu_panic_lock = PTHREAD_MUTEX_INITIALIZER;
-/* LINTED static unused */
-static const char *uu_panic_format;
-/* LINTED static unused */
-static va_list uu_panic_args;
-static pthread_t uu_panic_thread;
-
-static uint32_t _uu_main_error;
-
-void
-uu_set_error(uint_t code)
-{
-
-#if defined(PTHREAD_ONCE_KEY_NP)
- if (pthread_key_create_once_np(&uu_error_key, NULL) != 0)
- uu_error_key_setup = -1;
- else
- uu_error_key_setup = 1;
-#else /* PTHREAD_ONCE_KEY_NP */
- if (uu_error_key_setup == 0) {
- (void) pthread_mutex_lock(&uu_key_lock);
- if (uu_error_key_setup == 0) {
- if (pthread_key_create(&uu_error_key, NULL) != 0)
- uu_error_key_setup = -1;
- else
- uu_error_key_setup = 1;
- }
- (void) pthread_mutex_unlock(&uu_key_lock);
- }
-#endif /* PTHREAD_ONCE_KEY_NP */
- if (uu_error_key_setup > 0)
- (void) pthread_setspecific(uu_error_key,
- (void *)(uintptr_t)code);
-}
-
-uint32_t
-uu_error(void)
-{
-
- if (uu_error_key_setup < 0) /* can't happen? */
- return (UU_ERROR_UNKNOWN);
-
- /*
- * Because UU_ERROR_NONE == 0, if uu_set_error() was
- * never called, then this will return UU_ERROR_NONE:
- */
- return ((uint32_t)(uintptr_t)pthread_getspecific(uu_error_key));
-}
-
-const char *
-uu_strerror(uint32_t code)
-{
- const char *str;
-
- switch (code) {
- case UU_ERROR_NONE:
- str = dgettext(TEXT_DOMAIN, "No error");
- break;
-
- case UU_ERROR_INVALID_ARGUMENT:
- str = dgettext(TEXT_DOMAIN, "Invalid argument");
- break;
-
- case UU_ERROR_UNKNOWN_FLAG:
- str = dgettext(TEXT_DOMAIN, "Unknown flag passed");
- break;
-
- case UU_ERROR_NO_MEMORY:
- str = dgettext(TEXT_DOMAIN, "Out of memory");
- break;
-
- case UU_ERROR_CALLBACK_FAILED:
- str = dgettext(TEXT_DOMAIN, "Callback-initiated failure");
- break;
-
- case UU_ERROR_NOT_SUPPORTED:
- str = dgettext(TEXT_DOMAIN, "Operation not supported");
- break;
-
- case UU_ERROR_EMPTY:
- str = dgettext(TEXT_DOMAIN, "No value provided");
- break;
-
- case UU_ERROR_UNDERFLOW:
- str = dgettext(TEXT_DOMAIN, "Value too small");
- break;
-
- case UU_ERROR_OVERFLOW:
- str = dgettext(TEXT_DOMAIN, "Value too large");
- break;
-
- case UU_ERROR_INVALID_CHAR:
- str = dgettext(TEXT_DOMAIN,
- "Value contains unexpected character");
- break;
-
- case UU_ERROR_INVALID_DIGIT:
- str = dgettext(TEXT_DOMAIN,
- "Value contains digit not in base");
- break;
-
- case UU_ERROR_SYSTEM:
- str = dgettext(TEXT_DOMAIN, "Underlying system error");
- break;
-
- case UU_ERROR_UNKNOWN:
- str = dgettext(TEXT_DOMAIN, "Error status not known");
- break;
-
- default:
- errno = ESRCH;
- str = NULL;
- break;
- }
- return (str);
-}
-
-void
-uu_panic(const char *format, ...)
-{
- va_list args;
-
- va_start(args, format);
-
- (void) pthread_mutex_lock(&uu_panic_lock);
- if (uu_panic_thread == 0) {
- uu_panic_thread = pthread_self();
- uu_panic_format = format;
- va_copy(uu_panic_args, args);
- }
- (void) pthread_mutex_unlock(&uu_panic_lock);
-
- (void) vfprintf(stderr, format, args);
-
- if (uu_panic_thread == pthread_self())
- abort();
- else
- for (;;)
- (void) pause();
-}
-
-int
-assfail(const char *astring, const char *file, int line)
-{
- __assert(astring, file, line);
- /*NOTREACHED*/
- return (0);
-}
-
-static void
-uu_lockup(void)
-{
- (void) pthread_mutex_lock(&uu_panic_lock);
-#if !defined(PTHREAD_ONCE_KEY_NP)
- (void) pthread_mutex_lock(&uu_key_lock);
-#endif
- uu_avl_lockup();
- uu_list_lockup();
-}
-
-static void
-uu_release(void)
-{
- (void) pthread_mutex_unlock(&uu_panic_lock);
-#if !defined(PTHREAD_ONCE_KEY_NP)
- (void) pthread_mutex_unlock(&uu_key_lock);
-#endif
- uu_avl_release();
- uu_list_release();
-}
-
-static void
-uu_release_child(void)
-{
- uu_panic_format = NULL;
- uu_panic_thread = 0;
-
- uu_release();
-}
-
-#pragma init(uu_init)
-static void
-uu_init(void)
-{
- (void) pthread_atfork(uu_lockup, uu_release, uu_release_child);
-}
-
-/*
- * Dump a block of memory in hex+ascii, for debugging
- */
-void
-uu_dump(FILE *out, const char *prefix, const void *buf, size_t len)
-{
- const unsigned char *p = buf;
- int i;
-
- for (i = 0; i < len; i += 16) {
- int j;
-
- (void) fprintf(out, "%s", prefix);
- for (j = 0; j < 16 && i + j < len; j++) {
- (void) fprintf(out, "%2.2x ", p[i + j]);
- }
- for (; j < 16; j++) {
- (void) fprintf(out, " ");
- }
- for (j = 0; j < 16 && i + j < len; j++) {
- (void) fprintf(out, "%c",
- isprint(p[i + j]) ? p[i + j] : '.');
- }
- (void) fprintf(out, "\n");
- }
-}
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/uu_open.c b/cddl/contrib/opensolaris/lib/libuutil/common/uu_open.c
deleted file mode 100644
index 7256662e38f6d..0000000000000
--- a/cddl/contrib/opensolaris/lib/libuutil/common/uu_open.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * 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 2004 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#include "libuutil_common.h"
-
-#include <sys/time.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#ifdef _LP64
-#define TMPPATHFMT "%s/uu%ld"
-#else /* _LP64 */
-#define TMPPATHFMT "%s/uu%lld"
-#endif /* _LP64 */
-
-/*ARGSUSED*/
-int
-uu_open_tmp(const char *dir, uint_t uflags)
-{
- int f;
- char *fname = uu_zalloc(PATH_MAX);
-
- if (fname == NULL)
- return (-1);
-
- for (;;) {
- (void) snprintf(fname, PATH_MAX, "%s/uu%lld", dir, gethrtime());
-
- f = open(fname, O_CREAT | O_EXCL | O_RDWR, 0600);
-
- if (f >= 0 || errno != EEXIST)
- break;
- }
-
- if (f >= 0)
- (void) unlink(fname);
-
- uu_free(fname);
-
- return (f);
-}
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/uu_pname.c b/cddl/contrib/opensolaris/lib/libuutil/common/uu_pname.c
deleted file mode 100644
index 20626ace6b2fc..0000000000000
--- a/cddl/contrib/opensolaris/lib/libuutil/common/uu_pname.c
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * 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 2005 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#include "libuutil_common.h"
-
-#include <libintl.h>
-#include <limits.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <errno.h>
-#include <wchar.h>
-#include <unistd.h>
-
-static const char PNAME_FMT[] = "%s: ";
-static const char ERRNO_FMT[] = ": %s\n";
-
-static const char *pname;
-
-static void
-uu_die_internal(int status, const char *format, va_list alist) __NORETURN;
-
-int uu_exit_ok_value = EXIT_SUCCESS;
-int uu_exit_fatal_value = EXIT_FAILURE;
-int uu_exit_usage_value = 2;
-
-int *
-uu_exit_ok(void)
-{
- return (&uu_exit_ok_value);
-}
-
-int *
-uu_exit_fatal(void)
-{
- return (&uu_exit_fatal_value);
-}
-
-int *
-uu_exit_usage(void)
-{
- return (&uu_exit_usage_value);
-}
-
-void
-uu_alt_exit(int profile)
-{
- switch (profile) {
- case UU_PROFILE_DEFAULT:
- uu_exit_ok_value = EXIT_SUCCESS;
- uu_exit_fatal_value = EXIT_FAILURE;
- uu_exit_usage_value = 2;
- break;
- case UU_PROFILE_LAUNCHER:
- uu_exit_ok_value = EXIT_SUCCESS;
- uu_exit_fatal_value = 124;
- uu_exit_usage_value = 125;
- break;
- }
-}
-
-static void
-uu_warn_internal(int err, const char *format, va_list alist)
-{
- if (pname != NULL)
- (void) fprintf(stderr, PNAME_FMT, pname);
-
- (void) vfprintf(stderr, format, alist);
-
- if (strrchr(format, '\n') == NULL)
- (void) fprintf(stderr, ERRNO_FMT, strerror(err));
-}
-
-void
-uu_vwarn(const char *format, va_list alist)
-{
- uu_warn_internal(errno, format, alist);
-}
-
-/*PRINTFLIKE1*/
-void
-uu_warn(const char *format, ...)
-{
- va_list alist;
- va_start(alist, format);
- uu_warn_internal(errno, format, alist);
- va_end(alist);
-}
-
-static void
-uu_die_internal(int status, const char *format, va_list alist)
-{
- uu_warn_internal(errno, format, alist);
-#ifdef DEBUG
- {
- char *cp;
-
- if (!issetugid()) {
- cp = getenv("UU_DIE_ABORTS");
- if (cp != NULL && *cp != '\0')
- abort();
- }
- }
-#endif
- exit(status);
-}
-
-void
-uu_vdie(const char *format, va_list alist)
-{
- uu_die_internal(UU_EXIT_FATAL, format, alist);
-}
-
-/*PRINTFLIKE1*/
-void
-uu_die(const char *format, ...)
-{
- va_list alist;
- va_start(alist, format);
- uu_die_internal(UU_EXIT_FATAL, format, alist);
- va_end(alist);
-}
-
-void
-uu_vxdie(int status, const char *format, va_list alist)
-{
- uu_die_internal(status, format, alist);
-}
-
-/*PRINTFLIKE2*/
-void
-uu_xdie(int status, const char *format, ...)
-{
- va_list alist;
- va_start(alist, format);
- uu_die_internal(status, format, alist);
- va_end(alist);
-}
-
-const char *
-uu_setpname(char *arg0)
-{
- /*
- * Having a NULL argv[0], while uncommon, is possible. It
- * makes more sense to handle this event in uu_setpname rather
- * than in each of its consumers.
- */
- if (arg0 == NULL) {
- pname = "unknown_command";
- return (pname);
- }
-
- /*
- * Guard against '/' at end of command invocation.
- */
- for (;;) {
- char *p = strrchr(arg0, '/');
- if (p == NULL) {
- pname = arg0;
- break;
- } else {
- if (*(p + 1) == '\0') {
- *p = '\0';
- continue;
- }
-
- pname = p + 1;
- break;
- }
- }
-
- return (pname);
-}
-
-const char *
-uu_getpname(void)
-{
- return (pname);
-}
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/uu_string.c b/cddl/contrib/opensolaris/lib/libuutil/common/uu_string.c
deleted file mode 100644
index 66afba05e849f..0000000000000
--- a/cddl/contrib/opensolaris/lib/libuutil/common/uu_string.c
+++ /dev/null
@@ -1,56 +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) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
- */
-
-/*
- * String helper functions
- */
-
-#include <string.h>
-#include <sys/types.h>
-#include <stdio.h>
-#include <malloc.h>
-#include <ctype.h>
-#include "libuutil.h"
-
-/* Return true if strings are equal */
-boolean_t
-uu_streq(const char *a, const char *b)
-{
- return (strcmp(a, b) == 0);
-}
-
-/* Return true if strings are equal, case-insensitively */
-boolean_t
-uu_strcaseeq(const char *a, const char *b)
-{
- return (strcasecmp(a, b) == 0);
-}
-
-/* Return true if string a Begins With string b */
-boolean_t
-uu_strbw(const char *a, const char *b)
-{
- return (strncmp(a, b, strlen(b)) == 0);
-}
diff --git a/cddl/contrib/opensolaris/lib/libuutil/common/uu_strtoint.c b/cddl/contrib/opensolaris/lib/libuutil/common/uu_strtoint.c
deleted file mode 100644
index 8fd1148365cb3..0000000000000
--- a/cddl/contrib/opensolaris/lib/libuutil/common/uu_strtoint.c
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * 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 2004 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#include "libuutil_common.h"
-
-#include <limits.h>
-#include <ctype.h>
-
-#define MAX_BASE 36
-
-#define IS_DIGIT(x) ((x) >= '0' && (x) <= '9')
-
-#define CTOI(x) (((x) >= '0' && (x) <= '9') ? (x) - '0' : \
- ((x) >= 'a' && (x) <= 'z') ? (x) + 10 - 'a' : (x) + 10 - 'A')
-
-static int
-strtoint(const char *s_arg, uint64_t *out, uint32_t base, int sign)
-{
- const unsigned char *s = (const unsigned char *)s_arg;
-
- uint64_t val = 0;
- uint64_t multmax;
-
- unsigned c, i;
-
- int neg = 0;
-
- int bad_digit = 0;
- int bad_char = 0;
- int overflow = 0;
-
- if (s == NULL || base == 1 || base > MAX_BASE) {
- uu_set_error(UU_ERROR_INVALID_ARGUMENT);
- return (-1);
- }
-
- while ((c = *s) != 0 && isspace(c))
- s++;
-
- switch (c) {
- case '-':
- if (!sign)
- overflow = 1; /* becomes underflow below */
- neg = 1;
- /*FALLTHRU*/
- case '+':
- c = *++s;
- break;
- default:
- break;
- }
-
- if (c == '\0') {
- uu_set_error(UU_ERROR_EMPTY);
- return (-1);
- }
-
- if (base == 0) {
- if (c != '0')
- base = 10;
- else if (s[1] == 'x' || s[1] == 'X')
- base = 16;
- else
- base = 8;
- }
-
- if (base == 16 && c == '0' && (s[1] == 'x' || s[1] == 'X'))
- c = *(s += 2);
-
- if ((val = CTOI(c)) >= base) {
- if (IS_DIGIT(c))
- bad_digit = 1;
- else
- bad_char = 1;
- val = 0;
- }
-
- multmax = (uint64_t)UINT64_MAX / (uint64_t)base;
-
- for (c = *++s; c != '\0'; c = *++s) {
- if ((i = CTOI(c)) >= base) {
- if (isspace(c))
- break;
- if (IS_DIGIT(c))
- bad_digit = 1;
- else
- bad_char = 1;
- i = 0;
- }
-
- if (val > multmax)
- overflow = 1;
-
- val *= base;
- if ((uint64_t)UINT64_MAX - val < (uint64_t)i)
- overflow = 1;
-
- val += i;
- }
-
- while ((c = *s) != 0) {
- if (!isspace(c))
- bad_char = 1;
- s++;
- }
-
- if (sign) {
- if (neg) {
- if (val > -(uint64_t)INT64_MIN)
- overflow = 1;
- } else {
- if (val > INT64_MAX)
- overflow = 1;
- }
- }
-
- if (neg)
- val = -val;
-
- if (bad_char | bad_digit | overflow) {
- if (bad_char)
- uu_set_error(UU_ERROR_INVALID_CHAR);
- else if (bad_digit)
- uu_set_error(UU_ERROR_INVALID_DIGIT);
- else if (overflow) {
- if (neg)
- uu_set_error(UU_ERROR_UNDERFLOW);
- else
- uu_set_error(UU_ERROR_OVERFLOW);
- }
- return (-1);
- }
-
- *out = val;
- return (0);
-}
-
-int
-uu_strtoint(const char *s, void *v, size_t sz, int base,
- int64_t min, int64_t max)
-{
- uint64_t val_u;
- int64_t val;
-
- if (min > max)
- goto bad_argument;
-
- switch (sz) {
- case 1:
- if (max > INT8_MAX || min < INT8_MIN)
- goto bad_argument;
- break;
- case 2:
- if (max > INT16_MAX || min < INT16_MIN)
- goto bad_argument;
- break;
- case 4:
- if (max > INT32_MAX || min < INT32_MIN)
- goto bad_argument;
- break;
- case 8:
- if (max > INT64_MAX || min < INT64_MIN)
- goto bad_argument;
- break;
- default:
- goto bad_argument;
- }
-
- if (min == 0 && max == 0) {
- min = -(1ULL << (8 * sz - 1));
- max = (1ULL << (8 * sz - 1)) - 1;
- }
-
- if (strtoint(s, &val_u, base, 1) == -1)
- return (-1);
-
- val = (int64_t)val_u;
-
- if (val < min) {
- uu_set_error(UU_ERROR_UNDERFLOW);
- return (-1);
- } else if (val > max) {
- uu_set_error(UU_ERROR_OVERFLOW);
- return (-1);
- }
-
- switch (sz) {
- case 1:
- *(int8_t *)v = val;
- return (0);
- case 2:
- *(int16_t *)v = val;
- return (0);
- case 4:
- *(int32_t *)v = val;
- return (0);
- case 8:
- *(int64_t *)v = val;
- return (0);
- default:
- break; /* fall through to bad_argument */
- }
-
-bad_argument:
- uu_set_error(UU_ERROR_INVALID_ARGUMENT);
- return (-1);
-}
-
-int
-uu_strtouint(const char *s, void *v, size_t sz, int base,
- uint64_t min, uint64_t max)
-{
- uint64_t val;
-
- if (min > max)
- goto bad_argument;
-
- switch (sz) {
- case 1:
- if (max > UINT8_MAX)
- goto bad_argument;
- break;
- case 2:
- if (max > UINT16_MAX)
- goto bad_argument;
- break;
- case 4:
- if (max > UINT32_MAX)
- goto bad_argument;
- break;
- case 8:
- if (max > UINT64_MAX)
- goto bad_argument;
- break;
- default:
- goto bad_argument;
- }
-
- if (min == 0 && max == 0) {
- /* we have to be careful, since << can overflow */
- max = (1ULL << (8 * sz - 1)) * 2 - 1;
- }
-
- if (strtoint(s, &val, base, 0) == -1)
- return (-1);
-
- if (val < min) {
- uu_set_error(UU_ERROR_UNDERFLOW);
- return (-1);
- } else if (val > max) {
- uu_set_error(UU_ERROR_OVERFLOW);
- return (-1);
- }
-
- switch (sz) {
- case 1:
- *(uint8_t *)v = val;
- return (0);
- case 2:
- *(uint16_t *)v = val;
- return (0);
- case 4:
- *(uint32_t *)v = val;
- return (0);
- case 8:
- *(uint64_t *)v = val;
- return (0);
- default:
- break; /* shouldn't happen, fall through */
- }
-
-bad_argument:
- uu_set_error(UU_ERROR_INVALID_ARGUMENT);
- return (-1);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
deleted file mode 100644
index 1899e318d53ef..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs.h
+++ /dev/null
@@ -1,894 +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 Pawel Jakub Dawidek. All rights reserved.
- * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
- * Copyright 2019 Joyent, Inc.
- * 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 Nexenta Systems, Inc.
- * Copyright (c) 2019 Datto Inc.
- */
-
-#ifndef _LIBZFS_H
-#define _LIBZFS_H
-
-#include <assert.h>
-#include <libnvpair.h>
-#include <sys/mnttab.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/varargs.h>
-#include <sys/fs/zfs.h>
-#include <sys/avl.h>
-#include <sys/zfs_ioctl.h>
-#include <libzfs_core.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Miscellaneous ZFS constants
- */
-#define ZFS_MAXPROPLEN MAXPATHLEN
-#define ZPOOL_MAXPROPLEN MAXPATHLEN
-
-/*
- * libzfs errors
- */
-typedef enum zfs_error {
- EZFS_SUCCESS = 0, /* no error -- success */
- EZFS_NOMEM = 2000, /* out of memory */
- EZFS_BADPROP, /* invalid property value */
- EZFS_PROPREADONLY, /* cannot set readonly property */
- EZFS_PROPTYPE, /* property does not apply to dataset type */
- EZFS_PROPNONINHERIT, /* property is not inheritable */
- EZFS_PROPSPACE, /* bad quota or reservation */
- EZFS_BADTYPE, /* dataset is not of appropriate type */
- EZFS_BUSY, /* pool or dataset is busy */
- EZFS_EXISTS, /* pool or dataset already exists */
- EZFS_NOENT, /* no such pool or dataset */
- EZFS_BADSTREAM, /* bad backup stream */
- EZFS_DSREADONLY, /* dataset is readonly */
- EZFS_VOLTOOBIG, /* volume is too large for 32-bit system */
- EZFS_INVALIDNAME, /* invalid dataset name */
- EZFS_BADRESTORE, /* unable to restore to destination */
- EZFS_BADBACKUP, /* backup failed */
- EZFS_BADTARGET, /* bad attach/detach/replace target */
- EZFS_NODEVICE, /* no such device in pool */
- EZFS_BADDEV, /* invalid device to add */
- EZFS_NOREPLICAS, /* no valid replicas */
- EZFS_RESILVERING, /* currently resilvering */
- EZFS_BADVERSION, /* unsupported version */
- EZFS_POOLUNAVAIL, /* pool is currently unavailable */
- EZFS_DEVOVERFLOW, /* too many devices in one vdev */
- EZFS_BADPATH, /* must be an absolute path */
- EZFS_CROSSTARGET, /* rename or clone across pool or dataset */
- EZFS_ZONED, /* used improperly in local zone */
- EZFS_MOUNTFAILED, /* failed to mount dataset */
- EZFS_UMOUNTFAILED, /* failed to unmount dataset */
- EZFS_UNSHARENFSFAILED, /* unshare(1M) failed */
- EZFS_SHARENFSFAILED, /* share(1M) failed */
- EZFS_PERM, /* permission denied */
- EZFS_NOSPC, /* out of space */
- EZFS_FAULT, /* bad address */
- EZFS_IO, /* I/O error */
- EZFS_INTR, /* signal received */
- EZFS_ISSPARE, /* device is a hot spare */
- EZFS_INVALCONFIG, /* invalid vdev configuration */
- EZFS_RECURSIVE, /* recursive dependency */
- EZFS_NOHISTORY, /* no history object */
- EZFS_POOLPROPS, /* couldn't retrieve pool props */
- EZFS_POOL_NOTSUP, /* ops not supported for this type of pool */
- EZFS_POOL_INVALARG, /* invalid argument for this pool operation */
- EZFS_NAMETOOLONG, /* dataset name is too long */
- EZFS_OPENFAILED, /* open of device failed */
- EZFS_NOCAP, /* couldn't get capacity */
- EZFS_LABELFAILED, /* write of label failed */
- EZFS_BADWHO, /* invalid permission who */
- EZFS_BADPERM, /* invalid permission */
- EZFS_BADPERMSET, /* invalid permission set name */
- EZFS_NODELEGATION, /* delegated administration is disabled */
- EZFS_UNSHARESMBFAILED, /* failed to unshare over smb */
- EZFS_SHARESMBFAILED, /* failed to share over smb */
- EZFS_BADCACHE, /* bad cache file */
- EZFS_ISL2CACHE, /* device is for the level 2 ARC */
- EZFS_VDEVNOTSUP, /* unsupported vdev type */
- EZFS_NOTSUP, /* ops not supported on this dataset */
- EZFS_ACTIVE_SPARE, /* pool has active shared spare devices */
- EZFS_UNPLAYED_LOGS, /* log device has unplayed logs */
- EZFS_REFTAG_RELE, /* snapshot release: tag not found */
- EZFS_REFTAG_HOLD, /* snapshot hold: tag already exists */
- EZFS_TAGTOOLONG, /* snapshot hold/rele: tag too long */
- EZFS_PIPEFAILED, /* pipe create failed */
- EZFS_THREADCREATEFAILED, /* thread create failed */
- EZFS_POSTSPLIT_ONLINE, /* onlining a disk after splitting it */
- EZFS_SCRUBBING, /* currently scrubbing */
- EZFS_NO_SCRUB, /* no active scrub */
- EZFS_DIFF, /* general failure of zfs diff */
- EZFS_DIFFDATA, /* bad zfs diff data */
- EZFS_POOLREADONLY, /* pool is in read-only mode */
- EZFS_SCRUB_PAUSED, /* scrub currently paused */
- EZFS_ACTIVE_POOL, /* pool is imported on a different system */
- EZFS_NO_PENDING, /* cannot cancel, no operation is pending */
- EZFS_CHECKPOINT_EXISTS, /* checkpoint exists */
- EZFS_DISCARDING_CHECKPOINT, /* currently discarding a checkpoint */
- EZFS_NO_CHECKPOINT, /* pool has no checkpoint */
- EZFS_DEVRM_IN_PROGRESS, /* a device is currently being removed */
- EZFS_VDEV_TOO_BIG, /* a device is too big to be used */
- EZFS_TOOMANY, /* argument list too long */
- EZFS_INITIALIZING, /* currently initializing */
- EZFS_NO_INITIALIZE, /* no active initialize */
- EZFS_WRONG_PARENT, /* invalid parent dataset (e.g ZVOL) */
- EZFS_IOC_NOTSUPPORTED, /* operation not supported by zfs module */
- EZFS_UNKNOWN
-} zfs_error_t;
-
-/*
- * UEFI boot support parameters. When creating whole disk boot pool,
- * zpool create should allow to create EFI System partition for UEFI boot
- * program. In case of BIOS, the EFI System partition is not used
- * even if it does exist.
- */
-typedef enum zpool_boot_label {
- ZPOOL_NO_BOOT_LABEL = 0,
- ZPOOL_CREATE_BOOT_LABEL,
- ZPOOL_COPY_BOOT_LABEL
-} zpool_boot_label_t;
-
-/*
- * The following data structures are all part
- * of the zfs_allow_t data structure which is
- * used for printing 'allow' permissions.
- * It is a linked list of zfs_allow_t's which
- * then contain avl tree's for user/group/sets/...
- * and each one of the entries in those trees have
- * avl tree's for the permissions they belong to and
- * whether they are local,descendent or local+descendent
- * permissions. The AVL trees are used primarily for
- * sorting purposes, but also so that we can quickly find
- * a given user and or permission.
- */
-typedef struct zfs_perm_node {
- avl_node_t z_node;
- char z_pname[MAXPATHLEN];
-} zfs_perm_node_t;
-
-typedef struct zfs_allow_node {
- avl_node_t z_node;
- char z_key[MAXPATHLEN]; /* name, such as joe */
- avl_tree_t z_localdescend; /* local+descendent perms */
- avl_tree_t z_local; /* local permissions */
- avl_tree_t z_descend; /* descendent permissions */
-} zfs_allow_node_t;
-
-typedef struct zfs_allow {
- struct zfs_allow *z_next;
- char z_setpoint[MAXPATHLEN];
- avl_tree_t z_sets;
- avl_tree_t z_crperms;
- avl_tree_t z_user;
- avl_tree_t z_group;
- avl_tree_t z_everyone;
-} zfs_allow_t;
-
-/*
- * Basic handle types
- */
-typedef struct zfs_handle zfs_handle_t;
-typedef struct zpool_handle zpool_handle_t;
-typedef struct libzfs_handle libzfs_handle_t;
-
-/*
- * Library initialization
- */
-extern libzfs_handle_t *libzfs_init(void);
-extern void libzfs_fini(libzfs_handle_t *);
-
-extern libzfs_handle_t *zpool_get_handle(zpool_handle_t *);
-extern libzfs_handle_t *zfs_get_handle(zfs_handle_t *);
-
-extern void libzfs_print_on_error(libzfs_handle_t *, boolean_t);
-
-extern void zfs_save_arguments(int argc, char **, char *, int);
-extern int zpool_log_history(libzfs_handle_t *, const char *);
-
-extern int libzfs_errno(libzfs_handle_t *);
-extern const char *libzfs_error_action(libzfs_handle_t *);
-extern const char *libzfs_error_description(libzfs_handle_t *);
-extern int zfs_standard_error(libzfs_handle_t *, int, const char *);
-extern void libzfs_mnttab_init(libzfs_handle_t *);
-extern void libzfs_mnttab_fini(libzfs_handle_t *);
-extern void libzfs_mnttab_cache(libzfs_handle_t *, boolean_t);
-extern int libzfs_mnttab_find(libzfs_handle_t *, const char *,
- struct mnttab *);
-extern void libzfs_mnttab_add(libzfs_handle_t *, const char *,
- const char *, const char *);
-extern void libzfs_mnttab_remove(libzfs_handle_t *, const char *);
-
-/*
- * Basic handle functions
- */
-extern zpool_handle_t *zpool_open(libzfs_handle_t *, const char *);
-extern zpool_handle_t *zpool_open_canfail(libzfs_handle_t *, const char *);
-extern void zpool_close(zpool_handle_t *);
-extern const char *zpool_get_name(zpool_handle_t *);
-extern int zpool_get_state(zpool_handle_t *);
-extern const char *zpool_state_to_name(vdev_state_t, vdev_aux_t);
-extern const char *zpool_pool_state_to_name(pool_state_t);
-extern void zpool_free_handles(libzfs_handle_t *);
-extern int zpool_nextboot(libzfs_handle_t *, uint64_t, uint64_t, const char *);
-
-/*
- * Iterate over all active pools in the system.
- */
-typedef int (*zpool_iter_f)(zpool_handle_t *, void *);
-extern int zpool_iter(libzfs_handle_t *, zpool_iter_f, void *);
-extern boolean_t zpool_skip_pool(const char *);
-
-/*
- * Functions to create and destroy pools
- */
-extern int zpool_create(libzfs_handle_t *, const char *, nvlist_t *,
- nvlist_t *, nvlist_t *);
-extern int zpool_destroy(zpool_handle_t *, const char *);
-extern int zpool_add(zpool_handle_t *, nvlist_t *);
-
-typedef struct splitflags {
- /* do not split, but return the config that would be split off */
- int dryrun : 1;
-
- /* after splitting, import the pool */
- int import : 1;
- int name_flags;
-} splitflags_t;
-
-/*
- * Functions to manipulate pool and vdev state
- */
-extern int zpool_scan(zpool_handle_t *, pool_scan_func_t, pool_scrub_cmd_t);
-extern int zpool_initialize(zpool_handle_t *, pool_initialize_func_t,
- nvlist_t *);
-extern int zpool_clear(zpool_handle_t *, const char *, nvlist_t *);
-extern int zpool_reguid(zpool_handle_t *);
-extern int zpool_reopen(zpool_handle_t *);
-
-extern int zpool_sync_one(zpool_handle_t *, void *);
-
-extern int zpool_vdev_online(zpool_handle_t *, const char *, int,
- vdev_state_t *);
-extern int zpool_vdev_offline(zpool_handle_t *, const char *, boolean_t);
-extern int zpool_vdev_attach(zpool_handle_t *, const char *,
- const char *, nvlist_t *, int);
-extern int zpool_vdev_detach(zpool_handle_t *, const char *);
-extern int zpool_vdev_remove(zpool_handle_t *, const char *);
-extern int zpool_vdev_remove_cancel(zpool_handle_t *);
-extern int zpool_vdev_indirect_size(zpool_handle_t *, const char *, uint64_t *);
-extern int zpool_vdev_split(zpool_handle_t *, char *, nvlist_t **, nvlist_t *,
- splitflags_t);
-
-extern int zpool_vdev_fault(zpool_handle_t *, uint64_t, vdev_aux_t);
-extern int zpool_vdev_degrade(zpool_handle_t *, uint64_t, vdev_aux_t);
-extern int zpool_vdev_clear(zpool_handle_t *, uint64_t);
-
-extern nvlist_t *zpool_find_vdev(zpool_handle_t *, const char *, boolean_t *,
- boolean_t *, boolean_t *);
-extern nvlist_t *zpool_find_vdev_by_physpath(zpool_handle_t *, const char *,
- boolean_t *, boolean_t *, boolean_t *);
-extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, const char *,
- zpool_boot_label_t, uint64_t, int *);
-
-/*
- * Functions to manage pool properties
- */
-extern int zpool_set_prop(zpool_handle_t *, const char *, const char *);
-extern int zpool_get_prop(zpool_handle_t *, zpool_prop_t, char *,
- size_t proplen, zprop_source_t *, boolean_t);
-extern uint64_t zpool_get_prop_int(zpool_handle_t *, zpool_prop_t,
- zprop_source_t *);
-
-extern const char *zpool_prop_to_name(zpool_prop_t);
-extern const char *zpool_prop_values(zpool_prop_t);
-
-/*
- * Pool health statistics.
- */
-typedef enum {
- /*
- * The following correspond to faults as defined in the (fault.fs.zfs.*)
- * event namespace. Each is associated with a corresponding message ID.
- * This must be kept in sync with the zfs_msgid_table in
- * lib/libzfs/libzfs_status.c.
- */
- ZPOOL_STATUS_CORRUPT_CACHE, /* corrupt /kernel/drv/zpool.cache */
- ZPOOL_STATUS_MISSING_DEV_R, /* missing device with replicas */
- ZPOOL_STATUS_MISSING_DEV_NR, /* missing device with no replicas */
- ZPOOL_STATUS_CORRUPT_LABEL_R, /* bad device label with replicas */
- ZPOOL_STATUS_CORRUPT_LABEL_NR, /* bad device label with no replicas */
- ZPOOL_STATUS_BAD_GUID_SUM, /* sum of device guids didn't match */
- ZPOOL_STATUS_CORRUPT_POOL, /* pool metadata is corrupted */
- ZPOOL_STATUS_CORRUPT_DATA, /* data errors in user (meta)data */
- ZPOOL_STATUS_FAILING_DEV, /* device experiencing errors */
- ZPOOL_STATUS_VERSION_NEWER, /* newer on-disk version */
- ZPOOL_STATUS_HOSTID_MISMATCH, /* last accessed by another system */
- ZPOOL_STATUS_HOSTID_ACTIVE, /* currently active on another system */
- ZPOOL_STATUS_HOSTID_REQUIRED, /* multihost=on and hostid=0 */
- ZPOOL_STATUS_IO_FAILURE_WAIT, /* failed I/O, failmode 'wait' */
- ZPOOL_STATUS_IO_FAILURE_CONTINUE, /* failed I/O, failmode 'continue' */
- ZPOOL_STATUS_IO_FAILURE_MMP, /* failed MMP, failmode not 'panic' */
- ZPOOL_STATUS_BAD_LOG, /* cannot read log chain(s) */
-
- /*
- * If the pool has unsupported features but can still be opened in
- * read-only mode, its status is ZPOOL_STATUS_UNSUP_FEAT_WRITE. If the
- * pool has unsupported features but cannot be opened at all, its
- * status is ZPOOL_STATUS_UNSUP_FEAT_READ.
- */
- ZPOOL_STATUS_UNSUP_FEAT_READ, /* unsupported features for read */
- ZPOOL_STATUS_UNSUP_FEAT_WRITE, /* unsupported features for write */
-
- /*
- * These faults have no corresponding message ID. At the time we are
- * checking the status, the original reason for the FMA fault (I/O or
- * checksum errors) has been lost.
- */
- ZPOOL_STATUS_FAULTED_DEV_R, /* faulted device with replicas */
- ZPOOL_STATUS_FAULTED_DEV_NR, /* faulted device with no replicas */
-
- /*
- * The following are not faults per se, but still an error possibly
- * requiring administrative attention. There is no corresponding
- * message ID.
- */
- ZPOOL_STATUS_VERSION_OLDER, /* older legacy on-disk version */
- ZPOOL_STATUS_FEAT_DISABLED, /* supported features are disabled */
- ZPOOL_STATUS_RESILVERING, /* device being resilvered */
- ZPOOL_STATUS_OFFLINE_DEV, /* device offline */
- ZPOOL_STATUS_REMOVED_DEV, /* removed device */
- ZPOOL_STATUS_NON_NATIVE_ASHIFT, /* (e.g. 512e dev with ashift of 9) */
-
- /*
- * Finally, the following indicates a healthy pool.
- */
- ZPOOL_STATUS_OK
-} zpool_status_t;
-
-extern zpool_status_t zpool_get_status(zpool_handle_t *, char **);
-extern zpool_status_t zpool_import_status(nvlist_t *, char **);
-extern void zpool_dump_ddt(const ddt_stat_t *dds, const ddt_histogram_t *ddh);
-
-/*
- * Statistics and configuration functions.
- */
-extern nvlist_t *zpool_get_config(zpool_handle_t *, nvlist_t **);
-extern nvlist_t *zpool_get_features(zpool_handle_t *);
-extern int zpool_refresh_stats(zpool_handle_t *, boolean_t *);
-extern int zpool_get_errlog(zpool_handle_t *, nvlist_t **);
-extern boolean_t zpool_is_bootable(zpool_handle_t *);
-
-/*
- * Import and export functions
- */
-extern int zpool_export(zpool_handle_t *, boolean_t, const char *);
-extern int zpool_export_force(zpool_handle_t *, const char *);
-extern int zpool_import(libzfs_handle_t *, nvlist_t *, const char *,
- char *altroot);
-extern int zpool_import_props(libzfs_handle_t *, nvlist_t *, const char *,
- nvlist_t *, int);
-extern void zpool_print_unsup_feat(nvlist_t *config);
-
-/*
- * Search for pools to import
- */
-
-typedef struct importargs {
- char **path; /* a list of paths to search */
- int paths; /* number of paths to search */
- char *poolname; /* name of a pool to find */
- uint64_t guid; /* guid of a pool to find */
- char *cachefile; /* cachefile to use for import */
- int can_be_active : 1; /* can the pool be active? */
- int unique : 1; /* does 'poolname' already exist? */
- int exists : 1; /* set on return if pool already exists */
- nvlist_t *policy; /* load policy (max txg, rewind, etc.) */
-} importargs_t;
-
-extern nvlist_t *zpool_search_import(libzfs_handle_t *, importargs_t *);
-extern int zpool_tryimport(libzfs_handle_t *hdl, char *target,
- nvlist_t **configp, importargs_t *args);
-
-/* legacy pool search routines */
-extern nvlist_t *zpool_find_import(libzfs_handle_t *, int, char **);
-extern nvlist_t *zpool_find_import_cached(libzfs_handle_t *, const char *,
- char *, uint64_t);
-
-/*
- * Miscellaneous pool functions
- */
-struct zfs_cmd;
-
-extern const char *zfs_history_event_names[];
-
-typedef enum {
- VDEV_NAME_PATH = 1 << 0,
- VDEV_NAME_GUID = 1 << 1,
- VDEV_NAME_FOLLOW_LINKS = 1 << 2,
- VDEV_NAME_TYPE_ID = 1 << 3,
-} vdev_name_t;
-
-extern char *zpool_vdev_name(libzfs_handle_t *, zpool_handle_t *, nvlist_t *,
- int name_flags);
-extern int zpool_upgrade(zpool_handle_t *, uint64_t);
-extern int zpool_get_history(zpool_handle_t *, nvlist_t **, uint64_t *,
- boolean_t *);
-extern int zpool_history_unpack(char *, uint64_t, uint64_t *,
- nvlist_t ***, uint_t *);
-extern void zpool_obj_to_path(zpool_handle_t *, uint64_t, uint64_t, char *,
- size_t len);
-extern int zfs_ioctl(libzfs_handle_t *, int request, struct zfs_cmd *);
-extern int zpool_get_physpath(zpool_handle_t *, char *, size_t);
-extern void zpool_explain_recover(libzfs_handle_t *, const char *, int,
- nvlist_t *);
-extern int zpool_checkpoint(zpool_handle_t *);
-extern int zpool_discard_checkpoint(zpool_handle_t *);
-
-/*
- * Basic handle manipulations. These functions do not create or destroy the
- * underlying datasets, only the references to them.
- */
-extern zfs_handle_t *zfs_open(libzfs_handle_t *, const char *, int);
-extern zfs_handle_t *zfs_handle_dup(zfs_handle_t *);
-extern void zfs_close(zfs_handle_t *);
-extern zfs_type_t zfs_get_type(const zfs_handle_t *);
-extern const char *zfs_get_name(const zfs_handle_t *);
-extern zpool_handle_t *zfs_get_pool_handle(const zfs_handle_t *);
-extern const char *zfs_get_pool_name(const zfs_handle_t *);
-
-/*
- * Property management functions. Some functions are shared with the kernel,
- * and are found in sys/fs/zfs.h.
- */
-
-/*
- * zfs dataset property management
- */
-extern const char *zfs_prop_default_string(zfs_prop_t);
-extern uint64_t zfs_prop_default_numeric(zfs_prop_t);
-extern const char *zfs_prop_column_name(zfs_prop_t);
-extern boolean_t zfs_prop_align_right(zfs_prop_t);
-
-extern nvlist_t *zfs_valid_proplist(libzfs_handle_t *, zfs_type_t,
- nvlist_t *, uint64_t, zfs_handle_t *, zpool_handle_t *, const char *);
-
-extern const char *zfs_prop_to_name(zfs_prop_t);
-extern int zfs_prop_set(zfs_handle_t *, const char *, const char *);
-extern int zfs_prop_set_list(zfs_handle_t *, nvlist_t *);
-extern int zfs_prop_get(zfs_handle_t *, zfs_prop_t, char *, size_t,
- zprop_source_t *, char *, size_t, boolean_t);
-extern int zfs_prop_get_recvd(zfs_handle_t *, const char *, char *, size_t,
- boolean_t);
-extern int zfs_prop_get_numeric(zfs_handle_t *, zfs_prop_t, uint64_t *,
- zprop_source_t *, char *, size_t);
-extern int zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,
- uint64_t *propvalue);
-extern int zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
- char *propbuf, int proplen, boolean_t literal);
-extern int zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
- uint64_t *propvalue);
-extern int zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
- char *propbuf, int proplen, boolean_t literal);
-extern int zfs_prop_get_feature(zfs_handle_t *zhp, const char *propname,
- char *buf, size_t len);
-extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t);
-extern int zfs_prop_inherit(zfs_handle_t *, const char *, boolean_t);
-extern const char *zfs_prop_values(zfs_prop_t);
-extern int zfs_prop_is_string(zfs_prop_t prop);
-extern nvlist_t *zfs_get_user_props(zfs_handle_t *);
-extern nvlist_t *zfs_get_recvd_props(zfs_handle_t *);
-extern nvlist_t *zfs_get_clones_nvl(zfs_handle_t *);
-
-
-typedef struct zprop_list {
- int pl_prop;
- char *pl_user_prop;
- struct zprop_list *pl_next;
- boolean_t pl_all;
- size_t pl_width;
- size_t pl_recvd_width;
- boolean_t pl_fixed;
-} zprop_list_t;
-
-extern int zfs_expand_proplist(zfs_handle_t *, zprop_list_t **, boolean_t,
- boolean_t);
-extern void zfs_prune_proplist(zfs_handle_t *, uint8_t *);
-
-#define ZFS_MOUNTPOINT_NONE "none"
-#define ZFS_MOUNTPOINT_LEGACY "legacy"
-
-#define ZFS_FEATURE_DISABLED "disabled"
-#define ZFS_FEATURE_ENABLED "enabled"
-#define ZFS_FEATURE_ACTIVE "active"
-
-#define ZFS_UNSUPPORTED_INACTIVE "inactive"
-#define ZFS_UNSUPPORTED_READONLY "readonly"
-
-/*
- * zpool property management
- */
-extern int zpool_expand_proplist(zpool_handle_t *, zprop_list_t **);
-extern int zpool_prop_get_feature(zpool_handle_t *, const char *, char *,
- size_t);
-extern const char *zpool_prop_default_string(zpool_prop_t);
-extern uint64_t zpool_prop_default_numeric(zpool_prop_t);
-extern const char *zpool_prop_column_name(zpool_prop_t);
-extern boolean_t zpool_prop_align_right(zpool_prop_t);
-
-/*
- * Functions shared by zfs and zpool property management.
- */
-extern int zprop_iter(zprop_func func, void *cb, boolean_t show_all,
- boolean_t ordered, zfs_type_t type);
-extern int zprop_get_list(libzfs_handle_t *, char *, zprop_list_t **,
- zfs_type_t);
-extern void zprop_free_list(zprop_list_t *);
-
-#define ZFS_GET_NCOLS 5
-
-typedef enum {
- GET_COL_NONE,
- GET_COL_NAME,
- GET_COL_PROPERTY,
- GET_COL_VALUE,
- GET_COL_RECVD,
- GET_COL_SOURCE
-} zfs_get_column_t;
-
-/*
- * Functions for printing zfs or zpool properties
- */
-typedef struct zprop_get_cbdata {
- int cb_sources;
- zfs_get_column_t cb_columns[ZFS_GET_NCOLS];
- int cb_colwidths[ZFS_GET_NCOLS + 1];
- boolean_t cb_scripted;
- boolean_t cb_literal;
- boolean_t cb_first;
- zprop_list_t *cb_proplist;
- zfs_type_t cb_type;
-} zprop_get_cbdata_t;
-
-void zprop_print_one_property(const char *, zprop_get_cbdata_t *,
- const char *, const char *, zprop_source_t, const char *,
- const char *);
-
-/*
- * Iterator functions.
- */
-typedef int (*zfs_iter_f)(zfs_handle_t *, void *);
-extern int zfs_iter_root(libzfs_handle_t *, zfs_iter_f, void *);
-extern int zfs_iter_children(zfs_handle_t *, zfs_iter_f, void *);
-extern int zfs_iter_dependents(zfs_handle_t *, boolean_t, zfs_iter_f, void *);
-extern int zfs_iter_filesystems(zfs_handle_t *, zfs_iter_f, void *);
-extern int zfs_iter_snapshots(zfs_handle_t *, boolean_t, zfs_iter_f, void *,
- uint64_t, uint64_t);
-extern int zfs_iter_snapshots_sorted(zfs_handle_t *, zfs_iter_f, void *,
- uint64_t, uint64_t);
-extern int zfs_iter_snapspec(zfs_handle_t *, const char *, zfs_iter_f, void *);
-extern int zfs_iter_bookmarks(zfs_handle_t *, zfs_iter_f, void *);
-
-typedef struct get_all_cb {
- zfs_handle_t **cb_handles;
- size_t cb_alloc;
- size_t cb_used;
-} get_all_cb_t;
-
-void zfs_foreach_mountpoint(libzfs_handle_t *, zfs_handle_t **, size_t,
- zfs_iter_f, void*, boolean_t);
-
-void libzfs_add_handle(get_all_cb_t *, zfs_handle_t *);
-
-/*
- * Functions to create and destroy datasets.
- */
-extern int zfs_create(libzfs_handle_t *, const char *, zfs_type_t,
- nvlist_t *);
-extern int zfs_create_ancestors(libzfs_handle_t *, const char *);
-extern int zfs_destroy(zfs_handle_t *, boolean_t);
-extern int zfs_destroy_snaps(zfs_handle_t *, char *, boolean_t);
-extern int zfs_destroy_snaps_nvl(libzfs_handle_t *, nvlist_t *, boolean_t);
-extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *);
-extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t, nvlist_t *);
-extern int zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps,
- nvlist_t *props);
-extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t);
-
-typedef struct renameflags {
- /* recursive rename */
- int recurse : 1;
-
- /* don't unmount file systems */
- int nounmount : 1;
-
- /* force unmount file systems */
- int forceunmount : 1;
-} renameflags_t;
-
-extern int zfs_rename(zfs_handle_t *, const char *, const char *,
- renameflags_t flags);
-
-typedef struct sendflags {
- /* print informational messages (ie, -v was specified) */
- boolean_t verbose;
-
- /* recursive send (ie, -R) */
- boolean_t replicate;
-
- /* for incrementals, do all intermediate snapshots */
- boolean_t doall;
-
- /* if dataset is a clone, do incremental from its origin */
- boolean_t fromorigin;
-
- /* do deduplication */
- boolean_t dedup;
-
- /* send properties (ie, -p) */
- boolean_t props;
-
- /* do not send (no-op, ie. -n) */
- boolean_t dryrun;
-
- /* parsable verbose output (ie. -P) */
- boolean_t parsable;
-
- /* show progress (ie. -v) */
- boolean_t progress;
-
- /* large blocks (>128K) are permitted */
- boolean_t largeblock;
-
- /* WRITE_EMBEDDED records of type DATA are permitted */
- boolean_t embed_data;
-
- /* compressed WRITE records are permitted */
- boolean_t compress;
-
- /* show progress as process title(ie. -V) */
- boolean_t progressastitle;
-} sendflags_t;
-
-typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
-
-extern int zfs_send(zfs_handle_t *, const char *, const char *,
- sendflags_t *, int, snapfilter_cb_t, void *, nvlist_t **);
-extern int zfs_send_one(zfs_handle_t *, const char *, int, sendflags_t flags);
-extern int zfs_send_resume(libzfs_handle_t *, sendflags_t *, int outfd,
- const char *);
-extern nvlist_t *zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl,
- const char *token);
-
-extern int zfs_promote(zfs_handle_t *);
-extern int zfs_hold(zfs_handle_t *, const char *, const char *,
- boolean_t, int);
-extern int zfs_hold_nvl(zfs_handle_t *, int, nvlist_t *);
-extern int zfs_release(zfs_handle_t *, const char *, const char *, boolean_t);
-extern int zfs_get_holds(zfs_handle_t *, nvlist_t **);
-extern uint64_t zvol_volsize_to_reservation(uint64_t, nvlist_t *);
-
-typedef int (*zfs_userspace_cb_t)(void *arg, const char *domain,
- uid_t rid, uint64_t space);
-
-extern int zfs_userspace(zfs_handle_t *, zfs_userquota_prop_t,
- zfs_userspace_cb_t, void *);
-
-extern int zfs_get_fsacl(zfs_handle_t *, nvlist_t **);
-extern int zfs_set_fsacl(zfs_handle_t *, boolean_t, nvlist_t *);
-
-typedef struct recvflags {
- /* print informational messages (ie, -v was specified) */
- boolean_t verbose;
-
- /* the destination is a prefix, not the exact fs (ie, -d) */
- boolean_t isprefix;
-
- /*
- * Only the tail of the sent snapshot path is appended to the
- * destination to determine the received snapshot name (ie, -e).
- */
- boolean_t istail;
-
- /* do not actually do the recv, just check if it would work (ie, -n) */
- boolean_t dryrun;
-
- /* rollback/destroy filesystems as necessary (eg, -F) */
- boolean_t force;
-
- /* set "canmount=off" on all modified filesystems */
- boolean_t canmountoff;
-
- /*
- * Mark the file systems as "resumable" and do not destroy them if the
- * receive is interrupted
- */
- boolean_t resumable;
-
- /* byteswap flag is used internally; callers need not specify */
- boolean_t byteswap;
-
- /* do not mount file systems as they are extracted (private) */
- boolean_t nomount;
-
- /* force unmount while recv snapshot (private) */
- boolean_t forceunmount;
-} recvflags_t;
-
-extern int zfs_receive(libzfs_handle_t *, const char *, nvlist_t *,
- recvflags_t *, int, avl_tree_t *);
-
-typedef enum diff_flags {
- ZFS_DIFF_PARSEABLE = 0x1,
- ZFS_DIFF_TIMESTAMP = 0x2,
- ZFS_DIFF_CLASSIFY = 0x4
-} diff_flags_t;
-
-extern int zfs_show_diffs(zfs_handle_t *, int, const char *, const char *,
- int);
-
-/*
- * Miscellaneous functions.
- */
-extern const char *zfs_type_to_name(zfs_type_t);
-extern void zfs_refresh_properties(zfs_handle_t *);
-extern int zfs_name_valid(const char *, zfs_type_t);
-extern zfs_handle_t *zfs_path_to_zhandle(libzfs_handle_t *, char *, zfs_type_t);
-extern boolean_t zfs_dataset_exists(libzfs_handle_t *, const char *,
- zfs_type_t);
-extern int zfs_spa_version(zfs_handle_t *, int *);
-extern boolean_t zfs_bookmark_exists(const char *path);
-extern ulong_t get_system_hostid(void);
-
-/*
- * Mount support functions.
- */
-extern boolean_t is_mounted(libzfs_handle_t *, const char *special, char **);
-extern boolean_t zfs_is_mounted(zfs_handle_t *, char **);
-extern int zfs_mount(zfs_handle_t *, const char *, int);
-extern int zfs_mount_at(zfs_handle_t *, const char *, int, const char *);
-extern int zfs_unmount(zfs_handle_t *, const char *, int);
-extern int zfs_unmountall(zfs_handle_t *, int);
-
-/*
- * Share support functions.
- */
-extern boolean_t zfs_is_shared(zfs_handle_t *);
-extern int zfs_share(zfs_handle_t *);
-extern int zfs_unshare(zfs_handle_t *);
-
-/*
- * Protocol-specific share support functions.
- */
-extern boolean_t zfs_is_shared_nfs(zfs_handle_t *, char **);
-extern boolean_t zfs_is_shared_smb(zfs_handle_t *, char **);
-extern int zfs_share_nfs(zfs_handle_t *);
-extern int zfs_share_smb(zfs_handle_t *);
-extern int zfs_shareall(zfs_handle_t *);
-extern int zfs_unshare_nfs(zfs_handle_t *, const char *);
-extern int zfs_unshare_smb(zfs_handle_t *, const char *);
-extern int zfs_unshareall_nfs(zfs_handle_t *);
-extern int zfs_unshareall_smb(zfs_handle_t *);
-extern int zfs_unshareall_bypath(zfs_handle_t *, const char *);
-extern int zfs_unshareall(zfs_handle_t *);
-extern int zfs_deleg_share_nfs(libzfs_handle_t *, char *, char *, char *,
- void *, void *, int, zfs_share_op_t);
-
-/*
- * FreeBSD-specific jail support function.
- */
-extern int zfs_jail(zfs_handle_t *, int, int);
-
-/*
- * When dealing with nvlists, verify() is extremely useful
- */
-#ifndef verify
-#ifdef NDEBUG
-#define verify(EX) ((void)(EX))
-#else
-#define verify(EX) assert(EX)
-#endif
-#endif
-
-/*
- * Utility function to convert a number to a human-readable form.
- */
-extern void zfs_nicenum(uint64_t, char *, size_t);
-extern int zfs_nicestrtonum(libzfs_handle_t *, const char *, uint64_t *);
-
-/*
- * Given a device or file, determine if it is part of a pool.
- */
-extern int zpool_in_use(libzfs_handle_t *, int, pool_state_t *, char **,
- boolean_t *);
-
-/*
- * Label manipulation.
- */
-extern int zpool_read_label(int, nvlist_t **);
-extern int zpool_read_all_labels(int, nvlist_t **);
-extern int zpool_clear_label(int);
-extern int zpool_set_bootenv(zpool_handle_t *, const char *);
-extern int zpool_get_bootenv(zpool_handle_t *, char *, size_t, off_t);
-
-/* is this zvol valid for use as a dump device? */
-extern int zvol_check_dump_config(char *);
-
-/*
- * Management interfaces for SMB ACL files
- */
-
-int zfs_smb_acl_add(libzfs_handle_t *, char *, char *, char *);
-int zfs_smb_acl_remove(libzfs_handle_t *, char *, char *, char *);
-int zfs_smb_acl_purge(libzfs_handle_t *, char *, char *);
-int zfs_smb_acl_rename(libzfs_handle_t *, char *, char *, char *, char *);
-
-/*
- * Enable and disable datasets within a pool by mounting/unmounting and
- * sharing/unsharing them.
- */
-extern int zpool_enable_datasets(zpool_handle_t *, const char *, int);
-extern int zpool_disable_datasets(zpool_handle_t *, boolean_t);
-
-/*
- * Mappings between vdev and FRU.
- */
-extern void libzfs_fru_refresh(libzfs_handle_t *);
-extern const char *libzfs_fru_lookup(libzfs_handle_t *, const char *);
-extern const char *libzfs_fru_devpath(libzfs_handle_t *, const char *);
-extern boolean_t libzfs_fru_compare(libzfs_handle_t *, const char *,
- const char *);
-extern boolean_t libzfs_fru_notself(libzfs_handle_t *, const char *);
-extern int zpool_fru_set(zpool_handle_t *, uint64_t, const char *);
-
-#ifndef illumos
-extern int zmount(const char *, const char *, int, char *, char *, int, char *,
- int);
-#endif
-extern int zfs_remap_indirects(libzfs_handle_t *hdl, const char *);
-
-/* Allow consumers to initialize libshare externally for optimal performance */
-extern int zfs_init_libshare_arg(libzfs_handle_t *, int, void *);
-/*
- * For most consumers, zfs_init_libshare_arg is sufficient on its own, and
- * zfs_uninit_libshare is unnecessary. zfs_uninit_libshare should only be called
- * if the caller has already initialized libshare for one set of zfs handles,
- * and wishes to share or unshare filesystems outside of that set. In that case,
- * the caller should uninitialize libshare, and then re-initialize it with the
- * new handles being shared or unshared.
- */
-extern void zfs_uninit_libshare(libzfs_handle_t *);
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBZFS_H */
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_changelist.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_changelist.c
deleted file mode 100644
index 7bbb68328f29b..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_changelist.c
+++ /dev/null
@@ -1,736 +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 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- *
- * Portions Copyright 2007 Ramprakash Jelari
- * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>.
- * All rights reserved.
- * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
- */
-
-#include <libintl.h>
-#include <libuutil.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <zone.h>
-
-#include <libzfs.h>
-
-#include "libzfs_impl.h"
-
-/*
- * Structure to keep track of dataset state. Before changing the 'sharenfs' or
- * 'mountpoint' property, we record whether the filesystem was previously
- * mounted/shared. This prior state dictates whether we remount/reshare the
- * dataset after the property has been changed.
- *
- * The interface consists of the following sequence of functions:
- *
- * changelist_gather()
- * changelist_prefix()
- * < change property >
- * changelist_postfix()
- * changelist_free()
- *
- * Other interfaces:
- *
- * changelist_remove() - remove a node from a gathered list
- * changelist_rename() - renames all datasets appropriately when doing a rename
- * changelist_unshare() - unshares all the nodes in a given changelist
- * changelist_haszonedchild() - check if there is any child exported to
- * a local zone
- */
-typedef struct prop_changenode {
- zfs_handle_t *cn_handle;
- int cn_shared;
- int cn_mounted;
- int cn_zoned;
- boolean_t cn_needpost; /* is postfix() needed? */
- uu_list_node_t cn_listnode;
-} prop_changenode_t;
-
-struct prop_changelist {
- zfs_prop_t cl_prop;
- zfs_prop_t cl_realprop;
- zfs_prop_t cl_shareprop; /* used with sharenfs/sharesmb */
- uu_list_pool_t *cl_pool;
- uu_list_t *cl_list;
- boolean_t cl_waslegacy;
- boolean_t cl_allchildren;
- boolean_t cl_alldependents;
- int cl_mflags; /* Mount flags */
- int cl_gflags; /* Gather request flags */
- boolean_t cl_haszonedchild;
- boolean_t cl_sorted;
-};
-
-/*
- * If the property is 'mountpoint', go through and unmount filesystems as
- * necessary. We don't do the same for 'sharenfs', because we can just re-share
- * with different options without interrupting service. We do handle 'sharesmb'
- * since there may be old resource names that need to be removed.
- */
-int
-changelist_prefix(prop_changelist_t *clp)
-{
- prop_changenode_t *cn;
- int ret = 0;
-
- if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
- clp->cl_prop != ZFS_PROP_SHARESMB)
- return (0);
-
- for (cn = uu_list_first(clp->cl_list); cn != NULL;
- cn = uu_list_next(clp->cl_list, cn)) {
-
- /* if a previous loop failed, set the remaining to false */
- if (ret == -1) {
- cn->cn_needpost = B_FALSE;
- continue;
- }
-
- /*
- * If we are in the global zone, but this dataset is exported
- * to a local zone, do nothing.
- */
- if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
- continue;
-
- if (!ZFS_IS_VOLUME(cn->cn_handle)) {
- /*
- * Do the property specific processing.
- */
- switch (clp->cl_prop) {
- case ZFS_PROP_MOUNTPOINT:
- if (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)
- break;
- if (zfs_unmount(cn->cn_handle, NULL,
- clp->cl_mflags) != 0) {
- ret = -1;
- cn->cn_needpost = B_FALSE;
- }
- break;
- case ZFS_PROP_SHARESMB:
- (void) zfs_unshare_smb(cn->cn_handle, NULL);
- break;
-
- default:
- break;
- }
- }
- }
-
- if (ret == -1)
- (void) changelist_postfix(clp);
-
- return (ret);
-}
-
-/*
- * If the property is 'mountpoint' or 'sharenfs', go through and remount and/or
- * reshare the filesystems as necessary. In changelist_gather() we recorded
- * whether the filesystem was previously shared or mounted. The action we take
- * depends on the previous state, and whether the value was previously 'legacy'.
- * For non-legacy properties, we only remount/reshare the filesystem if it was
- * previously mounted/shared. Otherwise, we always remount/reshare the
- * filesystem.
- */
-int
-changelist_postfix(prop_changelist_t *clp)
-{
- prop_changenode_t *cn;
- char shareopts[ZFS_MAXPROPLEN];
- int errors = 0;
- libzfs_handle_t *hdl;
-#ifdef illumos
- size_t num_datasets = 0, i;
- zfs_handle_t **zhandle_arr;
- sa_init_selective_arg_t sharearg;
-#endif
-
- /*
- * If we're changing the mountpoint, attempt to destroy the underlying
- * mountpoint. All other datasets will have inherited from this dataset
- * (in which case their mountpoints exist in the filesystem in the new
- * location), or have explicit mountpoints set (in which case they won't
- * be in the changelist).
- */
- if ((cn = uu_list_last(clp->cl_list)) == NULL)
- return (0);
-
- if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
- !(clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)) {
- remove_mountpoint(cn->cn_handle);
- }
-
- /*
- * It is possible that the changelist_prefix() used libshare
- * to unshare some entries. Since libshare caches data, an
- * attempt to reshare during postfix can fail unless libshare
- * is uninitialized here so that it will reinitialize later.
- */
- if (cn->cn_handle != NULL) {
- hdl = cn->cn_handle->zfs_hdl;
- assert(hdl != NULL);
- zfs_uninit_libshare(hdl);
-
-#ifdef illumos
- /*
- * For efficiencies sake, we initialize libshare for only a few
- * shares (the ones affected here). Future initializations in
- * this process should just use the cached initialization.
- */
- for (cn = uu_list_last(clp->cl_list); cn != NULL;
- cn = uu_list_prev(clp->cl_list, cn)) {
- num_datasets++;
- }
-
- zhandle_arr = zfs_alloc(hdl,
- num_datasets * sizeof (zfs_handle_t *));
- for (i = 0, cn = uu_list_last(clp->cl_list); cn != NULL;
- cn = uu_list_prev(clp->cl_list, cn)) {
- zhandle_arr[i++] = cn->cn_handle;
- zfs_refresh_properties(cn->cn_handle);
- }
- assert(i == num_datasets);
- sharearg.zhandle_arr = zhandle_arr;
- sharearg.zhandle_len = num_datasets;
- errors = zfs_init_libshare_arg(hdl, SA_INIT_SHARE_API_SELECTIVE,
- &sharearg);
- free(zhandle_arr);
-#endif
- }
- /*
- * We walk the datasets in reverse, because we want to mount any parent
- * datasets before mounting the children. We walk all datasets even if
- * there are errors.
- */
- for (cn = uu_list_last(clp->cl_list); cn != NULL;
- cn = uu_list_prev(clp->cl_list, cn)) {
-
- boolean_t sharenfs;
- boolean_t sharesmb;
- boolean_t mounted;
-
- /*
- * If we are in the global zone, but this dataset is exported
- * to a local zone, do nothing.
- */
- if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
- continue;
-
- /* Only do post-processing if it's required */
- if (!cn->cn_needpost)
- continue;
- cn->cn_needpost = B_FALSE;
-
-#ifndef illumos
- zfs_refresh_properties(cn->cn_handle);
-#endif
-
- if (ZFS_IS_VOLUME(cn->cn_handle))
- continue;
-
- /*
- * Remount if previously mounted or mountpoint was legacy,
- * or sharenfs or sharesmb property is set.
- */
- sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
- shareopts, sizeof (shareopts), NULL, NULL, 0,
- B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
-
- sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB,
- shareopts, sizeof (shareopts), NULL, NULL, 0,
- B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
-
- mounted = (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT) ||
- zfs_is_mounted(cn->cn_handle, NULL);
-
- if (!mounted && (cn->cn_mounted ||
- ((sharenfs || sharesmb || clp->cl_waslegacy) &&
- (zfs_prop_get_int(cn->cn_handle,
- ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON)))) {
-
- if (zfs_mount(cn->cn_handle, NULL, 0) != 0)
- errors++;
- else
- mounted = TRUE;
- }
-
- /*
- * If the file system is mounted we always re-share even
- * if the filesystem is currently shared, so that we can
- * adopt any new options.
- */
- if (sharenfs && mounted)
- errors += zfs_share_nfs(cn->cn_handle);
- else if (cn->cn_shared || clp->cl_waslegacy)
- errors += zfs_unshare_nfs(cn->cn_handle, NULL);
- if (sharesmb && mounted)
- errors += zfs_share_smb(cn->cn_handle);
- else if (cn->cn_shared || clp->cl_waslegacy)
- errors += zfs_unshare_smb(cn->cn_handle, NULL);
- }
-
- return (errors ? -1 : 0);
-}
-
-/*
- * Is this "dataset" a child of "parent"?
- */
-boolean_t
-isa_child_of(const char *dataset, const char *parent)
-{
- int len;
-
- len = strlen(parent);
-
- if (strncmp(dataset, parent, len) == 0 &&
- (dataset[len] == '@' || dataset[len] == '/' ||
- dataset[len] == '\0'))
- return (B_TRUE);
- else
- return (B_FALSE);
-
-}
-
-/*
- * If we rename a filesystem, child filesystem handles are no longer valid
- * since we identify each dataset by its name in the ZFS namespace. As a
- * result, we have to go through and fix up all the names appropriately. We
- * could do this automatically if libzfs kept track of all open handles, but
- * this is a lot less work.
- */
-void
-changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
-{
- prop_changenode_t *cn;
- char newname[ZFS_MAX_DATASET_NAME_LEN];
-
- for (cn = uu_list_first(clp->cl_list); cn != NULL;
- cn = uu_list_next(clp->cl_list, cn)) {
- /*
- * Do not rename a clone that's not in the source hierarchy.
- */
- if (!isa_child_of(cn->cn_handle->zfs_name, src))
- continue;
-
- /*
- * Destroy the previous mountpoint if needed.
- */
- remove_mountpoint(cn->cn_handle);
-
- (void) strlcpy(newname, dst, sizeof (newname));
- (void) strcat(newname, cn->cn_handle->zfs_name + strlen(src));
-
- (void) strlcpy(cn->cn_handle->zfs_name, newname,
- sizeof (cn->cn_handle->zfs_name));
- }
-}
-
-/*
- * Given a gathered changelist for the 'sharenfs' or 'sharesmb' property,
- * unshare all the datasets in the list.
- */
-int
-changelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto)
-{
- prop_changenode_t *cn;
- int ret = 0;
-
- if (clp->cl_prop != ZFS_PROP_SHARENFS &&
- clp->cl_prop != ZFS_PROP_SHARESMB)
- return (0);
-
- for (cn = uu_list_first(clp->cl_list); cn != NULL;
- cn = uu_list_next(clp->cl_list, cn)) {
- if (zfs_unshare_proto(cn->cn_handle, NULL, proto) != 0)
- ret = -1;
- }
-
- return (ret);
-}
-
-/*
- * Check if there is any child exported to a local zone in a given changelist.
- * This information has already been recorded while gathering the changelist
- * via changelist_gather().
- */
-int
-changelist_haszonedchild(prop_changelist_t *clp)
-{
- return (clp->cl_haszonedchild);
-}
-
-/*
- * Remove a node from a gathered list.
- */
-void
-changelist_remove(prop_changelist_t *clp, const char *name)
-{
- prop_changenode_t *cn;
-
- for (cn = uu_list_first(clp->cl_list); cn != NULL;
- cn = uu_list_next(clp->cl_list, cn)) {
-
- if (strcmp(cn->cn_handle->zfs_name, name) == 0) {
- uu_list_remove(clp->cl_list, cn);
- zfs_close(cn->cn_handle);
- free(cn);
- return;
- }
- }
-}
-
-/*
- * Release any memory associated with a changelist.
- */
-void
-changelist_free(prop_changelist_t *clp)
-{
- prop_changenode_t *cn;
- void *cookie;
-
- if (clp->cl_list) {
- cookie = NULL;
- while ((cn = uu_list_teardown(clp->cl_list, &cookie)) != NULL) {
- zfs_close(cn->cn_handle);
- free(cn);
- }
-
- uu_list_destroy(clp->cl_list);
- }
- if (clp->cl_pool)
- uu_list_pool_destroy(clp->cl_pool);
-
- free(clp);
-}
-
-static int
-change_one(zfs_handle_t *zhp, void *data)
-{
- prop_changelist_t *clp = data;
- char property[ZFS_MAXPROPLEN];
- char where[64];
- prop_changenode_t *cn;
- zprop_source_t sourcetype;
- zprop_source_t share_sourcetype;
-
- /*
- * We only want to unmount/unshare those filesystems that may inherit
- * from the target filesystem. If we find any filesystem with a
- * locally set mountpoint, we ignore any children since changing the
- * property will not affect them. If this is a rename, we iterate
- * over all children regardless, since we need them unmounted in
- * order to do the rename. Also, if this is a volume and we're doing
- * a rename, then always add it to the changelist.
- */
-
- if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) &&
- zfs_prop_get(zhp, clp->cl_prop, property,
- sizeof (property), &sourcetype, where, sizeof (where),
- B_FALSE) != 0) {
- zfs_close(zhp);
- return (0);
- }
-
- /*
- * If we are "watching" sharenfs or sharesmb
- * then check out the companion property which is tracked
- * in cl_shareprop
- */
- if (clp->cl_shareprop != ZPROP_INVAL &&
- zfs_prop_get(zhp, clp->cl_shareprop, property,
- sizeof (property), &share_sourcetype, where, sizeof (where),
- B_FALSE) != 0) {
- zfs_close(zhp);
- return (0);
- }
-
- if (clp->cl_alldependents || clp->cl_allchildren ||
- sourcetype == ZPROP_SRC_DEFAULT ||
- sourcetype == ZPROP_SRC_INHERITED ||
- (clp->cl_shareprop != ZPROP_INVAL &&
- (share_sourcetype == ZPROP_SRC_DEFAULT ||
- share_sourcetype == ZPROP_SRC_INHERITED))) {
- if ((cn = zfs_alloc(zfs_get_handle(zhp),
- sizeof (prop_changenode_t))) == NULL) {
- zfs_close(zhp);
- return (-1);
- }
-
- cn->cn_handle = zhp;
- cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
- zfs_is_mounted(zhp, NULL);
- cn->cn_shared = zfs_is_shared(zhp);
- cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
- cn->cn_needpost = B_TRUE;
-
- /* Indicate if any child is exported to a local zone. */
- if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
- clp->cl_haszonedchild = B_TRUE;
-
- uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
-
- if (clp->cl_sorted) {
- uu_list_index_t idx;
-
- (void) uu_list_find(clp->cl_list, cn, NULL,
- &idx);
- uu_list_insert(clp->cl_list, cn, idx);
- } else {
- /*
- * Add this child to beginning of the list. Children
- * below this one in the hierarchy will get added above
- * this one in the list. This produces a list in
- * reverse dataset name order.
- * This is necessary when the original mountpoint
- * is legacy or none.
- */
- verify(uu_list_insert_before(clp->cl_list,
- uu_list_first(clp->cl_list), cn) == 0);
- }
-
- if (!clp->cl_alldependents)
- return (zfs_iter_children(zhp, change_one, data));
- } else {
- zfs_close(zhp);
- }
-
- return (0);
-}
-
-/*ARGSUSED*/
-static int
-compare_mountpoints(const void *a, const void *b, void *unused)
-{
- const prop_changenode_t *ca = a;
- const prop_changenode_t *cb = b;
-
- char mounta[MAXPATHLEN];
- char mountb[MAXPATHLEN];
-
- boolean_t hasmounta, hasmountb;
-
- /*
- * When unsharing or unmounting filesystems, we need to do it in
- * mountpoint order. This allows the user to have a mountpoint
- * hierarchy that is different from the dataset hierarchy, and still
- * allow it to be changed. However, if either dataset doesn't have a
- * mountpoint (because it is a volume or a snapshot), we place it at the
- * end of the list, because it doesn't affect our change at all.
- */
- hasmounta = (zfs_prop_get(ca->cn_handle, ZFS_PROP_MOUNTPOINT, mounta,
- sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
- hasmountb = (zfs_prop_get(cb->cn_handle, ZFS_PROP_MOUNTPOINT, mountb,
- sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
-
- if (!hasmounta && hasmountb)
- return (-1);
- else if (hasmounta && !hasmountb)
- return (1);
- else if (!hasmounta && !hasmountb)
- return (0);
- else
- return (strcmp(mountb, mounta));
-}
-
-/*
- * Given a ZFS handle and a property, construct a complete list of datasets
- * that need to be modified as part of this process. For anything but the
- * 'mountpoint' and 'sharenfs' properties, this just returns an empty list.
- * Otherwise, we iterate over all children and look for any datasets that
- * inherit the property. For each such dataset, we add it to the list and
- * mark whether it was shared beforehand.
- */
-prop_changelist_t *
-changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
- int mnt_flags)
-{
- prop_changelist_t *clp;
- prop_changenode_t *cn;
- zfs_handle_t *temp;
- char property[ZFS_MAXPROPLEN];
- uu_compare_fn_t *compare = NULL;
- boolean_t legacy = B_FALSE;
-
- if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL)
- return (NULL);
-
- /*
- * For mountpoint-related tasks, we want to sort everything by
- * mountpoint, so that we mount and unmount them in the appropriate
- * order, regardless of their position in the hierarchy.
- */
- if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
- prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS ||
- prop == ZFS_PROP_SHARESMB) {
-
- if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
- property, sizeof (property),
- NULL, NULL, 0, B_FALSE) == 0 &&
- (strcmp(property, "legacy") == 0 ||
- strcmp(property, "none") == 0)) {
-
- legacy = B_TRUE;
- }
- if (!legacy) {
- compare = compare_mountpoints;
- clp->cl_sorted = B_TRUE;
- }
- }
-
- clp->cl_pool = uu_list_pool_create("changelist_pool",
- sizeof (prop_changenode_t),
- offsetof(prop_changenode_t, cn_listnode),
- compare, 0);
- if (clp->cl_pool == NULL) {
- assert(uu_error() == UU_ERROR_NO_MEMORY);
- (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
- changelist_free(clp);
- return (NULL);
- }
-
- clp->cl_list = uu_list_create(clp->cl_pool, NULL,
- clp->cl_sorted ? UU_LIST_SORTED : 0);
- clp->cl_gflags = gather_flags;
- clp->cl_mflags = mnt_flags;
-
- if (clp->cl_list == NULL) {
- assert(uu_error() == UU_ERROR_NO_MEMORY);
- (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
- changelist_free(clp);
- return (NULL);
- }
-
- /*
- * If this is a rename or the 'zoned' property, we pretend we're
- * changing the mountpoint and flag it so we can catch all children in
- * change_one().
- *
- * Flag cl_alldependents to catch all children plus the dependents
- * (clones) that are not in the hierarchy.
- */
- if (prop == ZFS_PROP_NAME) {
- clp->cl_prop = ZFS_PROP_MOUNTPOINT;
- clp->cl_alldependents = B_TRUE;
- } else if (prop == ZFS_PROP_ZONED) {
- clp->cl_prop = ZFS_PROP_MOUNTPOINT;
- clp->cl_allchildren = B_TRUE;
- } else if (prop == ZFS_PROP_CANMOUNT) {
- clp->cl_prop = ZFS_PROP_MOUNTPOINT;
- } else if (prop == ZFS_PROP_VOLSIZE) {
- clp->cl_prop = ZFS_PROP_MOUNTPOINT;
- } else {
- clp->cl_prop = prop;
- }
- clp->cl_realprop = prop;
-
- if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
- clp->cl_prop != ZFS_PROP_SHARENFS &&
- clp->cl_prop != ZFS_PROP_SHARESMB)
- return (clp);
-
- /*
- * If watching SHARENFS or SHARESMB then
- * also watch its companion property.
- */
- if (clp->cl_prop == ZFS_PROP_SHARENFS)
- clp->cl_shareprop = ZFS_PROP_SHARESMB;
- else if (clp->cl_prop == ZFS_PROP_SHARESMB)
- clp->cl_shareprop = ZFS_PROP_SHARENFS;
-
- if (clp->cl_alldependents) {
- if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) {
- changelist_free(clp);
- return (NULL);
- }
- } else if (zfs_iter_children(zhp, change_one, clp) != 0) {
- changelist_free(clp);
- return (NULL);
- }
-
- /*
- * We have to re-open ourselves because we auto-close all the handles
- * and can't tell the difference.
- */
- if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp),
- ZFS_TYPE_DATASET)) == NULL) {
- changelist_free(clp);
- return (NULL);
- }
-
- /*
- * Always add ourself to the list. We add ourselves to the end so that
- * we're the last to be unmounted.
- */
- if ((cn = zfs_alloc(zhp->zfs_hdl,
- sizeof (prop_changenode_t))) == NULL) {
- zfs_close(temp);
- changelist_free(clp);
- return (NULL);
- }
-
- cn->cn_handle = temp;
- cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
- zfs_is_mounted(temp, NULL);
- cn->cn_shared = zfs_is_shared(temp);
- cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
- cn->cn_needpost = B_TRUE;
-
- uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
- if (clp->cl_sorted) {
- uu_list_index_t idx;
- (void) uu_list_find(clp->cl_list, cn, NULL, &idx);
- uu_list_insert(clp->cl_list, cn, idx);
- } else {
- /*
- * Add the target dataset to the end of the list.
- * The list is not really unsorted. The list will be
- * in reverse dataset name order. This is necessary
- * when the original mountpoint is legacy or none.
- */
- verify(uu_list_insert_after(clp->cl_list,
- uu_list_last(clp->cl_list), cn) == 0);
- }
-
- /*
- * If the mountpoint property was previously 'legacy', or 'none',
- * record it as the behavior of changelist_postfix() will be different.
- */
- if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && legacy) {
- /*
- * do not automatically mount ex-legacy datasets if
- * we specifically set canmount to noauto
- */
- if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) !=
- ZFS_CANMOUNT_NOAUTO)
- clp->cl_waslegacy = B_TRUE;
- }
-
- return (clp);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
deleted file mode 100644
index 7545331b40b42..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * CDDL HEADER SART
- *
- * 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) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
- */
-
-#include "libzfs_compat.h"
-
-int zfs_ioctl_version = ZFS_IOCVER_UNDEF;
-static int zfs_spa_version = -1;
-
-/*
- * Get zfs_ioctl_version
- */
-int
-get_zfs_ioctl_version(void)
-{
- size_t ver_size;
- int ver = ZFS_IOCVER_NONE;
-
- ver_size = sizeof(ver);
- sysctlbyname("vfs.zfs.version.ioctl", &ver, &ver_size, NULL, 0);
-
- return (ver);
-}
-
-/*
- * Get the SPA version
- */
-static int
-get_zfs_spa_version(void)
-{
- size_t ver_size;
- int ver = 0;
-
- ver_size = sizeof(ver);
- sysctlbyname("vfs.zfs.version.spa", &ver, &ver_size, NULL, 0);
-
- return (ver);
-}
-
-/*
- * This is FreeBSD version of ioctl, because Solaris' ioctl() updates
- * zc_nvlist_dst_size even if an error is returned, on FreeBSD if an
- * error is returned zc_nvlist_dst_size won't be updated.
- */
-int
-zcmd_ioctl(int fd, int request, zfs_cmd_t *zc)
-{
- size_t oldsize;
- int ret, cflag = ZFS_CMD_COMPAT_NONE;
-
- if (zfs_ioctl_version == ZFS_IOCVER_UNDEF)
- zfs_ioctl_version = get_zfs_ioctl_version();
-
- if (zfs_ioctl_version >= ZFS_IOCVER_DEADMAN) {
- switch (zfs_ioctl_version) {
- case ZFS_IOCVER_INLANES:
- cflag = ZFS_CMD_COMPAT_INLANES;
- break;
- case ZFS_IOCVER_RESUME:
- cflag = ZFS_CMD_COMPAT_RESUME;
- break;
- case ZFS_IOCVER_EDBP:
- cflag = ZFS_CMD_COMPAT_EDBP;
- break;
- case ZFS_IOCVER_ZCMD:
- cflag = ZFS_CMD_COMPAT_ZCMD;
- break;
- case ZFS_IOCVER_LZC:
- cflag = ZFS_CMD_COMPAT_LZC;
- break;
- case ZFS_IOCVER_DEADMAN:
- cflag = ZFS_CMD_COMPAT_DEADMAN;
- break;
- }
- } else {
- /*
- * If vfs.zfs.version.ioctl is not defined, assume we have v28
- * compatible binaries and use vfs.zfs.version.spa to test for v15
- */
- cflag = ZFS_CMD_COMPAT_V28;
-
- if (zfs_spa_version < 0)
- zfs_spa_version = get_zfs_spa_version();
-
- if (zfs_spa_version == SPA_VERSION_15 ||
- zfs_spa_version == SPA_VERSION_14 ||
- zfs_spa_version == SPA_VERSION_13)
- cflag = ZFS_CMD_COMPAT_V15;
- }
-
- oldsize = zc->zc_nvlist_dst_size;
- ret = zcmd_ioctl_compat(fd, request, zc, cflag);
-
- if (ret == 0 && oldsize < zc->zc_nvlist_dst_size) {
- ret = -1;
- errno = ENOMEM;
- }
-
- return (ret);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.h b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.h
deleted file mode 100644
index 37616683330a2..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_compat.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * CDDL HEADER SART
- *
- * 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) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
- */
-
-#ifndef _LIBZFS_COMPAT_H
-#define _LIBZFS_COMPAT_H
-
-#include <zfs_ioctl_compat.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int get_zfs_ioctl_version(void);
-int zcmd_ioctl(int fd, int request, zfs_cmd_t *zc);
-
-#define ioctl(fd, ioc, zc) zcmd_ioctl((fd), (ioc), (zc))
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBZFS_COMPAT_H */
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_config.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_config.c
deleted file mode 100644
index b33d86432dc5f..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_config.c
+++ /dev/null
@@ -1,469 +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) 2012 by Delphix. All rights reserved.
- * Copyright (c) 2015 by Syneto S.R.L. All rights reserved.
- * Copyright 2016 Nexenta Systems, Inc.
- */
-
-/*
- * The pool configuration repository is stored in /etc/zfs/zpool.cache as a
- * single packed nvlist. While it would be nice to just read in this
- * file from userland, this wouldn't work from a local zone. So we have to have
- * a zpool ioctl to return the complete configuration for all pools. In the
- * global zone, this will be identical to reading the file and unpacking it in
- * userland.
- */
-
-#include <errno.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <string.h>
-#include <unistd.h>
-#include <libintl.h>
-#include <libuutil.h>
-
-#include "libzfs_impl.h"
-
-typedef struct config_node {
- char *cn_name;
- nvlist_t *cn_config;
- uu_avl_node_t cn_avl;
-} config_node_t;
-
-/* ARGSUSED */
-static int
-config_node_compare(const void *a, const void *b, void *unused)
-{
- int ret;
-
- const config_node_t *ca = (config_node_t *)a;
- const config_node_t *cb = (config_node_t *)b;
-
- ret = strcmp(ca->cn_name, cb->cn_name);
-
- if (ret < 0)
- return (-1);
- else if (ret > 0)
- return (1);
- else
- return (0);
-}
-
-void
-namespace_clear(libzfs_handle_t *hdl)
-{
- if (hdl->libzfs_ns_avl) {
- config_node_t *cn;
- void *cookie = NULL;
-
- while ((cn = uu_avl_teardown(hdl->libzfs_ns_avl,
- &cookie)) != NULL) {
- nvlist_free(cn->cn_config);
- free(cn->cn_name);
- free(cn);
- }
-
- uu_avl_destroy(hdl->libzfs_ns_avl);
- hdl->libzfs_ns_avl = NULL;
- }
-
- if (hdl->libzfs_ns_avlpool) {
- uu_avl_pool_destroy(hdl->libzfs_ns_avlpool);
- hdl->libzfs_ns_avlpool = NULL;
- }
-}
-
-/*
- * Loads the pool namespace, or re-loads it if the cache has changed.
- */
-static int
-namespace_reload(libzfs_handle_t *hdl)
-{
- nvlist_t *config;
- config_node_t *cn;
- nvpair_t *elem;
- zfs_cmd_t zc = { 0 };
- void *cookie;
-
- if (hdl->libzfs_ns_gen == 0) {
- /*
- * This is the first time we've accessed the configuration
- * cache. Initialize the AVL tree and then fall through to the
- * common code.
- */
- if ((hdl->libzfs_ns_avlpool = uu_avl_pool_create("config_pool",
- sizeof (config_node_t),
- offsetof(config_node_t, cn_avl),
- config_node_compare, UU_DEFAULT)) == NULL)
- return (no_memory(hdl));
-
- if ((hdl->libzfs_ns_avl = uu_avl_create(hdl->libzfs_ns_avlpool,
- NULL, UU_DEFAULT)) == NULL)
- return (no_memory(hdl));
- }
-
- if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
- return (-1);
-
- for (;;) {
- zc.zc_cookie = hdl->libzfs_ns_gen;
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_CONFIGS, &zc) != 0) {
- switch (errno) {
- case EEXIST:
- /*
- * The namespace hasn't changed.
- */
- zcmd_free_nvlists(&zc);
- return (0);
-
- case ENOMEM:
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- break;
-
- default:
- zcmd_free_nvlists(&zc);
- return (zfs_standard_error(hdl, errno,
- dgettext(TEXT_DOMAIN, "failed to read "
- "pool configuration")));
- }
- } else {
- hdl->libzfs_ns_gen = zc.zc_cookie;
- break;
- }
- }
-
- if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
-
- zcmd_free_nvlists(&zc);
-
- /*
- * Clear out any existing configuration information.
- */
- cookie = NULL;
- while ((cn = uu_avl_teardown(hdl->libzfs_ns_avl, &cookie)) != NULL) {
- nvlist_free(cn->cn_config);
- free(cn->cn_name);
- free(cn);
- }
-
- elem = NULL;
- while ((elem = nvlist_next_nvpair(config, elem)) != NULL) {
- nvlist_t *child;
- uu_avl_index_t where;
-
- if ((cn = zfs_alloc(hdl, sizeof (config_node_t))) == NULL) {
- nvlist_free(config);
- return (-1);
- }
-
- if ((cn->cn_name = zfs_strdup(hdl,
- nvpair_name(elem))) == NULL) {
- free(cn);
- nvlist_free(config);
- return (-1);
- }
-
- verify(nvpair_value_nvlist(elem, &child) == 0);
- if (nvlist_dup(child, &cn->cn_config, 0) != 0) {
- free(cn->cn_name);
- free(cn);
- nvlist_free(config);
- return (no_memory(hdl));
- }
- verify(uu_avl_find(hdl->libzfs_ns_avl, cn, NULL, &where)
- == NULL);
-
- uu_avl_insert(hdl->libzfs_ns_avl, cn, where);
- }
-
- nvlist_free(config);
- return (0);
-}
-
-/*
- * Retrieve the configuration for the given pool. The configuration is a nvlist
- * describing the vdevs, as well as the statistics associated with each one.
- */
-nvlist_t *
-zpool_get_config(zpool_handle_t *zhp, nvlist_t **oldconfig)
-{
- if (oldconfig)
- *oldconfig = zhp->zpool_old_config;
- return (zhp->zpool_config);
-}
-
-/*
- * Retrieves a list of enabled features and their refcounts and caches it in
- * the pool handle.
- */
-nvlist_t *
-zpool_get_features(zpool_handle_t *zhp)
-{
- nvlist_t *config, *features;
-
- config = zpool_get_config(zhp, NULL);
-
- if (config == NULL || !nvlist_exists(config,
- ZPOOL_CONFIG_FEATURE_STATS)) {
- int error;
- boolean_t missing = B_FALSE;
-
- error = zpool_refresh_stats(zhp, &missing);
-
- if (error != 0 || missing)
- return (NULL);
-
- config = zpool_get_config(zhp, NULL);
- }
-
- if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_FEATURE_STATS,
- &features) != 0)
- return (NULL);
-
- return (features);
-}
-
-/*
- * Refresh the vdev statistics associated with the given pool. This is used in
- * iostat to show configuration changes and determine the delta from the last
- * time the function was called. This function can fail, in case the pool has
- * been destroyed.
- */
-int
-zpool_refresh_stats(zpool_handle_t *zhp, boolean_t *missing)
-{
- zfs_cmd_t zc = { 0 };
- int error;
- nvlist_t *config;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- *missing = B_FALSE;
- (void) strcpy(zc.zc_name, zhp->zpool_name);
-
- if (zhp->zpool_config_size == 0)
- zhp->zpool_config_size = 1 << 16;
-
- if (zcmd_alloc_dst_nvlist(hdl, &zc, zhp->zpool_config_size) != 0)
- return (-1);
-
- for (;;) {
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_POOL_STATS,
- &zc) == 0) {
- /*
- * The real error is returned in the zc_cookie field.
- */
- error = zc.zc_cookie;
- break;
- }
-
- if (errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- } else {
- zcmd_free_nvlists(&zc);
- if (errno == ENOENT || errno == EINVAL)
- *missing = B_TRUE;
- zhp->zpool_state = POOL_STATE_UNAVAIL;
- return (0);
- }
- }
-
- if (zcmd_read_dst_nvlist(hdl, &zc, &config) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
-
- zcmd_free_nvlists(&zc);
-
- zhp->zpool_config_size = zc.zc_nvlist_dst_size;
-
- if (zhp->zpool_config != NULL) {
- uint64_t oldtxg, newtxg;
-
- verify(nvlist_lookup_uint64(zhp->zpool_config,
- ZPOOL_CONFIG_POOL_TXG, &oldtxg) == 0);
- verify(nvlist_lookup_uint64(config,
- ZPOOL_CONFIG_POOL_TXG, &newtxg) == 0);
-
- nvlist_free(zhp->zpool_old_config);
-
- if (oldtxg != newtxg) {
- nvlist_free(zhp->zpool_config);
- zhp->zpool_old_config = NULL;
- } else {
- zhp->zpool_old_config = zhp->zpool_config;
- }
- }
-
- zhp->zpool_config = config;
- if (error)
- zhp->zpool_state = POOL_STATE_UNAVAIL;
- else
- zhp->zpool_state = POOL_STATE_ACTIVE;
-
- return (0);
-}
-
-/*
- * The following environment variables are undocumented
- * and should be used for testing purposes only:
- *
- * __ZFS_POOL_EXCLUDE - don't iterate over the pools it lists
- * __ZFS_POOL_RESTRICT - iterate only over the pools it lists
- *
- * This function returns B_TRUE if the pool should be skipped
- * during iteration.
- */
-boolean_t
-zpool_skip_pool(const char *poolname)
-{
- static boolean_t initialized = B_FALSE;
- static const char *exclude = NULL;
- static const char *restricted = NULL;
-
- const char *cur, *end;
- int len;
- int namelen = strlen(poolname);
-
- if (!initialized) {
- initialized = B_TRUE;
- exclude = getenv("__ZFS_POOL_EXCLUDE");
- restricted = getenv("__ZFS_POOL_RESTRICT");
- }
-
- if (exclude != NULL) {
- cur = exclude;
- do {
- end = strchr(cur, ' ');
- len = (NULL == end) ? strlen(cur) : (end - cur);
- if (len == namelen && 0 == strncmp(cur, poolname, len))
- return (B_TRUE);
- cur += (len + 1);
- } while (NULL != end);
- }
-
- if (NULL == restricted)
- return (B_FALSE);
-
- cur = restricted;
- do {
- end = strchr(cur, ' ');
- len = (NULL == end) ? strlen(cur) : (end - cur);
-
- if (len == namelen && 0 == strncmp(cur, poolname, len)) {
- return (B_FALSE);
- }
-
- cur += (len + 1);
- } while (NULL != end);
-
- return (B_TRUE);
-}
-
-/*
- * Iterate over all pools in the system.
- */
-int
-zpool_iter(libzfs_handle_t *hdl, zpool_iter_f func, void *data)
-{
- config_node_t *cn;
- zpool_handle_t *zhp;
- int ret;
-
- /*
- * If someone makes a recursive call to zpool_iter(), we want to avoid
- * refreshing the namespace because that will invalidate the parent
- * context. We allow recursive calls, but simply re-use the same
- * namespace AVL tree.
- */
- if (!hdl->libzfs_pool_iter && namespace_reload(hdl) != 0)
- return (-1);
-
- hdl->libzfs_pool_iter++;
- for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL;
- cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) {
-
- if (zpool_skip_pool(cn->cn_name))
- continue;
-
- if (zpool_open_silent(hdl, cn->cn_name, &zhp) != 0) {
- hdl->libzfs_pool_iter--;
- return (-1);
- }
-
- if (zhp == NULL)
- continue;
-
- if ((ret = func(zhp, data)) != 0) {
- hdl->libzfs_pool_iter--;
- return (ret);
- }
- }
- hdl->libzfs_pool_iter--;
-
- return (0);
-}
-
-/*
- * Iterate over root datasets, calling the given function for each. The zfs
- * handle passed each time must be explicitly closed by the callback.
- */
-int
-zfs_iter_root(libzfs_handle_t *hdl, zfs_iter_f func, void *data)
-{
- config_node_t *cn;
- zfs_handle_t *zhp;
- int ret;
-
- if (namespace_reload(hdl) != 0)
- return (-1);
-
- for (cn = uu_avl_first(hdl->libzfs_ns_avl); cn != NULL;
- cn = uu_avl_next(hdl->libzfs_ns_avl, cn)) {
-
- if (zpool_skip_pool(cn->cn_name))
- continue;
-
- if ((zhp = make_dataset_handle(hdl, cn->cn_name)) == NULL)
- continue;
-
- if ((ret = func(zhp, data)) != 0)
- return (ret);
- }
-
- return (0);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
deleted file mode 100644
index 7075d060c78de..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
+++ /dev/null
@@ -1,5284 +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) 2018, Joyent, Inc. All rights reserved.
- * Copyright (c) 2011, 2016 by Delphix. All rights reserved.
- * Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved.
- * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved.
- * Copyright (c) 2013 Martin Matuska. All rights reserved.
- * Copyright (c) 2013 Steven Hartland. All rights reserved.
- * Copyright (c) 2014 Integros [integros.com]
- * Copyright 2017 Nexenta Systems, Inc.
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
- * Copyright 2017-2018 RackTop Systems.
- * Copyright (c) 2019 Datto Inc.
- */
-
-#include <ctype.h>
-#include <errno.h>
-#include <libintl.h>
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <unistd.h>
-#include <stddef.h>
-#include <zone.h>
-#include <fcntl.h>
-#include <sys/mntent.h>
-#include <sys/mount.h>
-#include <priv.h>
-#include <pwd.h>
-#include <grp.h>
-#include <stddef.h>
-#ifdef illumos
-#include <idmap.h>
-#endif
-
-#include <sys/dnode.h>
-#include <sys/spa.h>
-#include <sys/zap.h>
-#include <sys/misc.h>
-#include <libzfs.h>
-
-#include "zfs_namecheck.h"
-#include "zfs_prop.h"
-#include "libzfs_impl.h"
-#include "zfs_deleg.h"
-
-static int userquota_propname_decode(const char *propname, boolean_t zoned,
- zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp);
-
-/*
- * Given a single type (not a mask of types), return the type in a human
- * readable form.
- */
-const char *
-zfs_type_to_name(zfs_type_t type)
-{
- switch (type) {
- case ZFS_TYPE_FILESYSTEM:
- return (dgettext(TEXT_DOMAIN, "filesystem"));
- case ZFS_TYPE_SNAPSHOT:
- return (dgettext(TEXT_DOMAIN, "snapshot"));
- case ZFS_TYPE_VOLUME:
- return (dgettext(TEXT_DOMAIN, "volume"));
- case ZFS_TYPE_POOL:
- return (dgettext(TEXT_DOMAIN, "pool"));
- case ZFS_TYPE_BOOKMARK:
- return (dgettext(TEXT_DOMAIN, "bookmark"));
- default:
- assert(!"unhandled zfs_type_t");
- }
-
- return (NULL);
-}
-
-/*
- * Validate a ZFS path. This is used even before trying to open the dataset, to
- * provide a more meaningful error message. We call zfs_error_aux() to
- * explain exactly why the name was not valid.
- */
-int
-zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
- boolean_t modifying)
-{
- namecheck_err_t why;
- char what;
-
- if (entity_namecheck(path, &why, &what) != 0) {
- if (hdl != NULL) {
- switch (why) {
- case NAME_ERR_TOOLONG:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "name is too long"));
- break;
-
- case NAME_ERR_LEADING_SLASH:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "leading slash in name"));
- break;
-
- case NAME_ERR_EMPTY_COMPONENT:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "empty component in name"));
- break;
-
- case NAME_ERR_TRAILING_SLASH:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "trailing slash in name"));
- break;
-
- case NAME_ERR_INVALCHAR:
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "invalid character "
- "'%c' in name"), what);
- break;
-
- case NAME_ERR_MULTIPLE_DELIMITERS:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "multiple '@' and/or '#' delimiters in "
- "name"));
- break;
-
- case NAME_ERR_NOLETTER:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool doesn't begin with a letter"));
- break;
-
- case NAME_ERR_RESERVED:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "name is reserved"));
- break;
-
- case NAME_ERR_DISKLIKE:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "reserved disk name"));
- break;
-
- default:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "(%d) not defined"), why);
- break;
- }
- }
-
- return (0);
- }
-
- if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {
- if (hdl != NULL)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "snapshot delimiter '@' is not expected here"));
- return (0);
- }
-
- if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) {
- if (hdl != NULL)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "missing '@' delimiter in snapshot name"));
- return (0);
- }
-
- if (!(type & ZFS_TYPE_BOOKMARK) && strchr(path, '#') != NULL) {
- if (hdl != NULL)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "bookmark delimiter '#' is not expected here"));
- return (0);
- }
-
- if (type == ZFS_TYPE_BOOKMARK && strchr(path, '#') == NULL) {
- if (hdl != NULL)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "missing '#' delimiter in bookmark name"));
- return (0);
- }
-
- if (modifying && strchr(path, '%') != NULL) {
- if (hdl != NULL)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid character %c in name"), '%');
- return (0);
- }
-
- return (-1);
-}
-
-int
-zfs_name_valid(const char *name, zfs_type_t type)
-{
- if (type == ZFS_TYPE_POOL)
- return (zpool_name_valid(NULL, B_FALSE, name));
- return (zfs_validate_name(NULL, name, type, B_FALSE));
-}
-
-/*
- * This function takes the raw DSL properties, and filters out the user-defined
- * properties into a separate nvlist.
- */
-static nvlist_t *
-process_user_props(zfs_handle_t *zhp, nvlist_t *props)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- nvpair_t *elem;
- nvlist_t *propval;
- nvlist_t *nvl;
-
- if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
- (void) no_memory(hdl);
- return (NULL);
- }
-
- elem = NULL;
- while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
- if (!zfs_prop_user(nvpair_name(elem)))
- continue;
-
- verify(nvpair_value_nvlist(elem, &propval) == 0);
- if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) {
- nvlist_free(nvl);
- (void) no_memory(hdl);
- return (NULL);
- }
- }
-
- return (nvl);
-}
-
-static zpool_handle_t *
-zpool_add_handle(zfs_handle_t *zhp, const char *pool_name)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- zpool_handle_t *zph;
-
- if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) {
- if (hdl->libzfs_pool_handles != NULL)
- zph->zpool_next = hdl->libzfs_pool_handles;
- hdl->libzfs_pool_handles = zph;
- }
- return (zph);
-}
-
-static zpool_handle_t *
-zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- zpool_handle_t *zph = hdl->libzfs_pool_handles;
-
- while ((zph != NULL) &&
- (strncmp(pool_name, zpool_get_name(zph), len) != 0))
- zph = zph->zpool_next;
- return (zph);
-}
-
-/*
- * Returns a handle to the pool that contains the provided dataset.
- * If a handle to that pool already exists then that handle is returned.
- * Otherwise, a new handle is created and added to the list of handles.
- */
-static zpool_handle_t *
-zpool_handle(zfs_handle_t *zhp)
-{
- char *pool_name;
- int len;
- zpool_handle_t *zph;
-
- len = strcspn(zhp->zfs_name, "/@#") + 1;
- pool_name = zfs_alloc(zhp->zfs_hdl, len);
- (void) strlcpy(pool_name, zhp->zfs_name, len);
-
- zph = zpool_find_handle(zhp, pool_name, len);
- if (zph == NULL)
- zph = zpool_add_handle(zhp, pool_name);
-
- free(pool_name);
- return (zph);
-}
-
-void
-zpool_free_handles(libzfs_handle_t *hdl)
-{
- zpool_handle_t *next, *zph = hdl->libzfs_pool_handles;
-
- while (zph != NULL) {
- next = zph->zpool_next;
- zpool_close(zph);
- zph = next;
- }
- hdl->libzfs_pool_handles = NULL;
-}
-
-/*
- * Utility function to gather stats (objset and zpl) for the given object.
- */
-static int
-get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
-
- (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
-
- while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) {
- if (errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, zc) != 0) {
- return (-1);
- }
- } else {
- return (-1);
- }
- }
- return (0);
-}
-
-/*
- * Utility function to get the received properties of the given object.
- */
-static int
-get_recvd_props_ioctl(zfs_handle_t *zhp)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- nvlist_t *recvdprops;
- zfs_cmd_t zc = { 0 };
- int err;
-
- if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
- return (-1);
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) {
- if (errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- return (-1);
- }
- } else {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- }
-
- err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops);
- zcmd_free_nvlists(&zc);
- if (err != 0)
- return (-1);
-
- nvlist_free(zhp->zfs_recvd_props);
- zhp->zfs_recvd_props = recvdprops;
-
- return (0);
-}
-
-static int
-put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc)
-{
- nvlist_t *allprops, *userprops;
-
- zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */
-
- if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) {
- return (-1);
- }
-
- /*
- * XXX Why do we store the user props separately, in addition to
- * storing them in zfs_props?
- */
- if ((userprops = process_user_props(zhp, allprops)) == NULL) {
- nvlist_free(allprops);
- return (-1);
- }
-
- nvlist_free(zhp->zfs_props);
- nvlist_free(zhp->zfs_user_props);
-
- zhp->zfs_props = allprops;
- zhp->zfs_user_props = userprops;
-
- return (0);
-}
-
-static int
-get_stats(zfs_handle_t *zhp)
-{
- int rc = 0;
- zfs_cmd_t zc = { 0 };
-
- if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
- return (-1);
- if (get_stats_ioctl(zhp, &zc) != 0)
- rc = -1;
- else if (put_stats_zhdl(zhp, &zc) != 0)
- rc = -1;
- zcmd_free_nvlists(&zc);
- return (rc);
-}
-
-/*
- * Refresh the properties currently stored in the handle.
- */
-void
-zfs_refresh_properties(zfs_handle_t *zhp)
-{
- (void) get_stats(zhp);
-}
-
-/*
- * Makes a handle from the given dataset name. Used by zfs_open() and
- * zfs_iter_* to create child handles on the fly.
- */
-static int
-make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc)
-{
- if (put_stats_zhdl(zhp, zc) != 0)
- return (-1);
-
- /*
- * We've managed to open the dataset and gather statistics. Determine
- * the high-level type.
- */
- if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
- zhp->zfs_head_type = ZFS_TYPE_VOLUME;
- else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
- zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM;
- else if (zhp->zfs_dmustats.dds_type == DMU_OST_OTHER)
- return (-1);
- else
- abort();
-
- if (zhp->zfs_dmustats.dds_is_snapshot)
- zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
- else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
- zhp->zfs_type = ZFS_TYPE_VOLUME;
- else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
- zhp->zfs_type = ZFS_TYPE_FILESYSTEM;
- else
- abort(); /* we should never see any other types */
-
- if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL)
- return (-1);
-
- return (0);
-}
-
-zfs_handle_t *
-make_dataset_handle(libzfs_handle_t *hdl, const char *path)
-{
- zfs_cmd_t zc = { 0 };
-
- zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
-
- if (zhp == NULL)
- return (NULL);
-
- zhp->zfs_hdl = hdl;
- (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
- if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) {
- free(zhp);
- return (NULL);
- }
- if (get_stats_ioctl(zhp, &zc) == -1) {
- zcmd_free_nvlists(&zc);
- free(zhp);
- return (NULL);
- }
- if (make_dataset_handle_common(zhp, &zc) == -1) {
- free(zhp);
- zhp = NULL;
- }
- zcmd_free_nvlists(&zc);
- return (zhp);
-}
-
-zfs_handle_t *
-make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
-{
- zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
-
- if (zhp == NULL)
- return (NULL);
-
- zhp->zfs_hdl = hdl;
- (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
- if (make_dataset_handle_common(zhp, zc) == -1) {
- free(zhp);
- return (NULL);
- }
- return (zhp);
-}
-
-zfs_handle_t *
-make_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc)
-{
- zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
-
- if (zhp == NULL)
- return (NULL);
-
- zhp->zfs_hdl = pzhp->zfs_hdl;
- (void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
- zhp->zfs_head_type = pzhp->zfs_type;
- zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
- zhp->zpool_hdl = zpool_handle(zhp);
- return (zhp);
-}
-
-zfs_handle_t *
-zfs_handle_dup(zfs_handle_t *zhp_orig)
-{
- zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
-
- if (zhp == NULL)
- return (NULL);
-
- zhp->zfs_hdl = zhp_orig->zfs_hdl;
- zhp->zpool_hdl = zhp_orig->zpool_hdl;
- (void) strlcpy(zhp->zfs_name, zhp_orig->zfs_name,
- sizeof (zhp->zfs_name));
- zhp->zfs_type = zhp_orig->zfs_type;
- zhp->zfs_head_type = zhp_orig->zfs_head_type;
- zhp->zfs_dmustats = zhp_orig->zfs_dmustats;
- if (zhp_orig->zfs_props != NULL) {
- if (nvlist_dup(zhp_orig->zfs_props, &zhp->zfs_props, 0) != 0) {
- (void) no_memory(zhp->zfs_hdl);
- zfs_close(zhp);
- return (NULL);
- }
- }
- if (zhp_orig->zfs_user_props != NULL) {
- if (nvlist_dup(zhp_orig->zfs_user_props,
- &zhp->zfs_user_props, 0) != 0) {
- (void) no_memory(zhp->zfs_hdl);
- zfs_close(zhp);
- return (NULL);
- }
- }
- if (zhp_orig->zfs_recvd_props != NULL) {
- if (nvlist_dup(zhp_orig->zfs_recvd_props,
- &zhp->zfs_recvd_props, 0)) {
- (void) no_memory(zhp->zfs_hdl);
- zfs_close(zhp);
- return (NULL);
- }
- }
- zhp->zfs_mntcheck = zhp_orig->zfs_mntcheck;
- if (zhp_orig->zfs_mntopts != NULL) {
- zhp->zfs_mntopts = zfs_strdup(zhp_orig->zfs_hdl,
- zhp_orig->zfs_mntopts);
- }
- zhp->zfs_props_table = zhp_orig->zfs_props_table;
- return (zhp);
-}
-
-boolean_t
-zfs_bookmark_exists(const char *path)
-{
- nvlist_t *bmarks;
- nvlist_t *props;
- char fsname[ZFS_MAX_DATASET_NAME_LEN];
- char *bmark_name;
- char *pound;
- int err;
- boolean_t rv;
-
-
- (void) strlcpy(fsname, path, sizeof (fsname));
- pound = strchr(fsname, '#');
- if (pound == NULL)
- return (B_FALSE);
-
- *pound = '\0';
- bmark_name = pound + 1;
- props = fnvlist_alloc();
- err = lzc_get_bookmarks(fsname, props, &bmarks);
- nvlist_free(props);
- if (err != 0) {
- nvlist_free(bmarks);
- return (B_FALSE);
- }
-
- rv = nvlist_exists(bmarks, bmark_name);
- nvlist_free(bmarks);
- return (rv);
-}
-
-zfs_handle_t *
-make_bookmark_handle(zfs_handle_t *parent, const char *path,
- nvlist_t *bmark_props)
-{
- zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
-
- if (zhp == NULL)
- return (NULL);
-
- /* Fill in the name. */
- zhp->zfs_hdl = parent->zfs_hdl;
- (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
-
- /* Set the property lists. */
- if (nvlist_dup(bmark_props, &zhp->zfs_props, 0) != 0) {
- free(zhp);
- return (NULL);
- }
-
- /* Set the types. */
- zhp->zfs_head_type = parent->zfs_head_type;
- zhp->zfs_type = ZFS_TYPE_BOOKMARK;
-
- if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL) {
- nvlist_free(zhp->zfs_props);
- free(zhp);
- return (NULL);
- }
-
- return (zhp);
-}
-
-struct zfs_open_bookmarks_cb_data {
- const char *path;
- zfs_handle_t *zhp;
-};
-
-static int
-zfs_open_bookmarks_cb(zfs_handle_t *zhp, void *data)
-{
- struct zfs_open_bookmarks_cb_data *dp = data;
-
- /*
- * Is it the one we are looking for?
- */
- if (strcmp(dp->path, zfs_get_name(zhp)) == 0) {
- /*
- * We found it. Save it and let the caller know we are done.
- */
- dp->zhp = zhp;
- return (EEXIST);
- }
-
- /*
- * Not found. Close the handle and ask for another one.
- */
- zfs_close(zhp);
- return (0);
-}
-
-/*
- * Opens the given snapshot, bookmark, filesystem, or volume. The 'types'
- * argument is a mask of acceptable types. The function will print an
- * appropriate error message and return NULL if it can't be opened.
- */
-zfs_handle_t *
-zfs_open(libzfs_handle_t *hdl, const char *path, int types)
-{
- zfs_handle_t *zhp;
- char errbuf[1024];
- char *bookp;
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
-
- /*
- * Validate the name before we even try to open it.
- */
- if (!zfs_validate_name(hdl, path, types, B_FALSE)) {
- (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
- return (NULL);
- }
-
- /*
- * Bookmarks needs to be handled separately.
- */
- bookp = strchr(path, '#');
- if (bookp == NULL) {
- /*
- * Try to get stats for the dataset, which will tell us if it
- * exists.
- */
- errno = 0;
- if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
- (void) zfs_standard_error(hdl, errno, errbuf);
- return (NULL);
- }
- } else {
- char dsname[ZFS_MAX_DATASET_NAME_LEN];
- zfs_handle_t *pzhp;
- struct zfs_open_bookmarks_cb_data cb_data = {path, NULL};
-
- /*
- * We need to cut out '#' and everything after '#'
- * to get the parent dataset name only.
- */
- assert(bookp - path < sizeof (dsname));
- (void) strncpy(dsname, path, bookp - path);
- dsname[bookp - path] = '\0';
-
- /*
- * Create handle for the parent dataset.
- */
- errno = 0;
- if ((pzhp = make_dataset_handle(hdl, dsname)) == NULL) {
- (void) zfs_standard_error(hdl, errno, errbuf);
- return (NULL);
- }
-
- /*
- * Iterate bookmarks to find the right one.
- */
- errno = 0;
- if ((zfs_iter_bookmarks(pzhp, zfs_open_bookmarks_cb,
- &cb_data) == 0) && (cb_data.zhp == NULL)) {
- (void) zfs_error(hdl, EZFS_NOENT, errbuf);
- zfs_close(pzhp);
- return (NULL);
- }
- if (cb_data.zhp == NULL) {
- (void) zfs_standard_error(hdl, errno, errbuf);
- zfs_close(pzhp);
- return (NULL);
- }
- zhp = cb_data.zhp;
-
- /*
- * Cleanup.
- */
- zfs_close(pzhp);
- }
-
- if (zhp == NULL) {
- char *at = strchr(path, '@');
-
- if (at != NULL)
- *at = '\0';
- errno = 0;
- if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
- (void) zfs_standard_error(hdl, errno, errbuf);
- return (NULL);
- }
- if (at != NULL)
- *at = '@';
- (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
- zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
- }
-
- if (!(types & zhp->zfs_type)) {
- (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
- zfs_close(zhp);
- return (NULL);
- }
-
- return (zhp);
-}
-
-/*
- * Release a ZFS handle. Nothing to do but free the associated memory.
- */
-void
-zfs_close(zfs_handle_t *zhp)
-{
- if (zhp->zfs_mntopts)
- free(zhp->zfs_mntopts);
- nvlist_free(zhp->zfs_props);
- nvlist_free(zhp->zfs_user_props);
- nvlist_free(zhp->zfs_recvd_props);
- free(zhp);
-}
-
-typedef struct mnttab_node {
- struct mnttab mtn_mt;
- avl_node_t mtn_node;
-} mnttab_node_t;
-
-static int
-libzfs_mnttab_cache_compare(const void *arg1, const void *arg2)
-{
- const mnttab_node_t *mtn1 = (const mnttab_node_t *)arg1;
- const mnttab_node_t *mtn2 = (const mnttab_node_t *)arg2;
- int rv;
-
- rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special);
-
- return (AVL_ISIGN(rv));
-}
-
-void
-libzfs_mnttab_init(libzfs_handle_t *hdl)
-{
- pthread_mutex_init(&hdl->libzfs_mnttab_cache_lock, NULL);
- assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0);
- avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare,
- sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node));
-}
-
-void
-libzfs_mnttab_update(libzfs_handle_t *hdl)
-{
- struct mnttab entry;
-
- rewind(hdl->libzfs_mnttab);
- while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
- mnttab_node_t *mtn;
-
- if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
- continue;
- mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
- mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special);
- mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp);
- mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype);
- mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts);
- avl_add(&hdl->libzfs_mnttab_cache, mtn);
- }
-}
-
-void
-libzfs_mnttab_fini(libzfs_handle_t *hdl)
-{
- void *cookie = NULL;
- mnttab_node_t *mtn;
-
- while ((mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie))
- != NULL) {
- free(mtn->mtn_mt.mnt_special);
- free(mtn->mtn_mt.mnt_mountp);
- free(mtn->mtn_mt.mnt_fstype);
- free(mtn->mtn_mt.mnt_mntopts);
- free(mtn);
- }
- avl_destroy(&hdl->libzfs_mnttab_cache);
- (void) pthread_mutex_destroy(&hdl->libzfs_mnttab_cache_lock);
-}
-
-void
-libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable)
-{
- hdl->libzfs_mnttab_enable = enable;
-}
-
-int
-libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname,
- struct mnttab *entry)
-{
- mnttab_node_t find;
- mnttab_node_t *mtn;
- int ret = ENOENT;
-
- if (!hdl->libzfs_mnttab_enable) {
- struct mnttab srch = { 0 };
-
- if (avl_numnodes(&hdl->libzfs_mnttab_cache))
- libzfs_mnttab_fini(hdl);
- rewind(hdl->libzfs_mnttab);
- srch.mnt_special = (char *)fsname;
- srch.mnt_fstype = MNTTYPE_ZFS;
- if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0)
- return (0);
- else
- return (ENOENT);
- }
-
- pthread_mutex_lock(&hdl->libzfs_mnttab_cache_lock);
- if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0)
- libzfs_mnttab_update(hdl);
-
- find.mtn_mt.mnt_special = (char *)fsname;
- mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL);
- if (mtn) {
- *entry = mtn->mtn_mt;
- ret = 0;
- }
- pthread_mutex_unlock(&hdl->libzfs_mnttab_cache_lock);
- return (ret);
-}
-
-void
-libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special,
- const char *mountp, const char *mntopts)
-{
- mnttab_node_t *mtn;
-
- pthread_mutex_lock(&hdl->libzfs_mnttab_cache_lock);
- if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) {
- mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
- mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special);
- mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp);
- mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS);
- mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts);
- avl_add(&hdl->libzfs_mnttab_cache, mtn);
- }
- pthread_mutex_unlock(&hdl->libzfs_mnttab_cache_lock);
-}
-
-void
-libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname)
-{
- mnttab_node_t find;
- mnttab_node_t *ret;
-
- pthread_mutex_lock(&hdl->libzfs_mnttab_cache_lock);
- find.mtn_mt.mnt_special = (char *)fsname;
- if ((ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL))
- != NULL) {
- avl_remove(&hdl->libzfs_mnttab_cache, ret);
- free(ret->mtn_mt.mnt_special);
- free(ret->mtn_mt.mnt_mountp);
- free(ret->mtn_mt.mnt_fstype);
- free(ret->mtn_mt.mnt_mntopts);
- free(ret);
- }
- pthread_mutex_unlock(&hdl->libzfs_mnttab_cache_lock);
-}
-
-int
-zfs_spa_version(zfs_handle_t *zhp, int *spa_version)
-{
- zpool_handle_t *zpool_handle = zhp->zpool_hdl;
-
- if (zpool_handle == NULL)
- return (-1);
-
- *spa_version = zpool_get_prop_int(zpool_handle,
- ZPOOL_PROP_VERSION, NULL);
- return (0);
-}
-
-/*
- * The choice of reservation property depends on the SPA version.
- */
-static int
-zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop)
-{
- int spa_version;
-
- if (zfs_spa_version(zhp, &spa_version) < 0)
- return (-1);
-
- if (spa_version >= SPA_VERSION_REFRESERVATION)
- *resv_prop = ZFS_PROP_REFRESERVATION;
- else
- *resv_prop = ZFS_PROP_RESERVATION;
-
- return (0);
-}
-
-/*
- * Given an nvlist of properties to set, validates that they are correct, and
- * parses any numeric properties (index, boolean, etc) if they are specified as
- * strings.
- */
-nvlist_t *
-zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
- uint64_t zoned, zfs_handle_t *zhp, zpool_handle_t *zpool_hdl,
- const char *errbuf)
-{
- nvpair_t *elem;
- uint64_t intval;
- char *strval;
- zfs_prop_t prop;
- nvlist_t *ret;
- int chosen_normal = -1;
- int chosen_utf = -1;
-
- if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) {
- (void) no_memory(hdl);
- return (NULL);
- }
-
- /*
- * Make sure this property is valid and applies to this type.
- */
-
- elem = NULL;
- while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
- const char *propname = nvpair_name(elem);
-
- prop = zfs_name_to_prop(propname);
- if (prop == ZPROP_INVAL && zfs_prop_user(propname)) {
- /*
- * This is a user property: make sure it's a
- * string, and that it's less than ZAP_MAXNAMELEN.
- */
- if (nvpair_type(elem) != DATA_TYPE_STRING) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a string"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property name '%s' is too long"),
- propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- (void) nvpair_value_string(elem, &strval);
- if (nvlist_add_string(ret, propname, strval) != 0) {
- (void) no_memory(hdl);
- goto error;
- }
- continue;
- }
-
- /*
- * Currently, only user properties can be modified on
- * snapshots.
- */
- if (type == ZFS_TYPE_SNAPSHOT) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "this property can not be modified for snapshots"));
- (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
- goto error;
- }
-
- if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) {
- zfs_userquota_prop_t uqtype;
- char newpropname[128];
- char domain[128];
- uint64_t rid;
- uint64_t valary[3];
-
- if (userquota_propname_decode(propname, zoned,
- &uqtype, domain, sizeof (domain), &rid) != 0) {
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN,
- "'%s' has an invalid user/group name"),
- propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (uqtype != ZFS_PROP_USERQUOTA &&
- uqtype != ZFS_PROP_GROUPQUOTA) {
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "'%s' is readonly"),
- propname);
- (void) zfs_error(hdl, EZFS_PROPREADONLY,
- errbuf);
- goto error;
- }
-
- if (nvpair_type(elem) == DATA_TYPE_STRING) {
- (void) nvpair_value_string(elem, &strval);
- if (strcmp(strval, "none") == 0) {
- intval = 0;
- } else if (zfs_nicestrtonum(hdl,
- strval, &intval) != 0) {
- (void) zfs_error(hdl,
- EZFS_BADPROP, errbuf);
- goto error;
- }
- } else if (nvpair_type(elem) ==
- DATA_TYPE_UINT64) {
- (void) nvpair_value_uint64(elem, &intval);
- if (intval == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "use 'none' to disable "
- "userquota/groupquota"));
- goto error;
- }
- } else {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a number"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- /*
- * Encode the prop name as
- * userquota@<hex-rid>-domain, to make it easy
- * for the kernel to decode.
- */
- (void) snprintf(newpropname, sizeof (newpropname),
- "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype],
- (longlong_t)rid, domain);
- valary[0] = uqtype;
- valary[1] = rid;
- valary[2] = intval;
- if (nvlist_add_uint64_array(ret, newpropname,
- valary, 3) != 0) {
- (void) no_memory(hdl);
- goto error;
- }
- continue;
- } else if (prop == ZPROP_INVAL && zfs_prop_written(propname)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' is readonly"),
- propname);
- (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
- goto error;
- }
-
- if (prop == ZPROP_INVAL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid property '%s'"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (!zfs_prop_valid_for_type(prop, type)) {
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "'%s' does not "
- "apply to datasets of this type"), propname);
- (void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
- goto error;
- }
-
- if (zfs_prop_readonly(prop) &&
- (!zfs_prop_setonce(prop) || zhp != NULL)) {
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "'%s' is readonly"),
- propname);
- (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
- goto error;
- }
-
- if (zprop_parse_value(hdl, elem, prop, type, ret,
- &strval, &intval, errbuf) != 0)
- goto error;
-
- /*
- * Perform some additional checks for specific properties.
- */
- switch (prop) {
- case ZFS_PROP_VERSION:
- {
- int version;
-
- if (zhp == NULL)
- break;
- version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
- if (intval < version) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "Can not downgrade; already at version %u"),
- version);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
- }
-
- case ZFS_PROP_VOLBLOCKSIZE:
- case ZFS_PROP_RECORDSIZE:
- {
- int maxbs = SPA_MAXBLOCKSIZE;
- if (zpool_hdl != NULL) {
- maxbs = zpool_get_prop_int(zpool_hdl,
- ZPOOL_PROP_MAXBLOCKSIZE, NULL);
- }
- /*
- * Volumes are limited to a volblocksize of 128KB,
- * because they typically service workloads with
- * small random writes, which incur a large performance
- * penalty with large blocks.
- */
- if (prop == ZFS_PROP_VOLBLOCKSIZE)
- maxbs = SPA_OLD_MAXBLOCKSIZE;
- /*
- * The value must be a power of two between
- * SPA_MINBLOCKSIZE and maxbs.
- */
- if (intval < SPA_MINBLOCKSIZE ||
- intval > maxbs || !ISP2(intval)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be power of 2 from 512B "
- "to %uKB"), propname, maxbs >> 10);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
- }
-
- case ZFS_PROP_SPECIAL_SMALL_BLOCKS:
- if (zpool_hdl != NULL) {
- char state[64] = "";
-
- /*
- * Issue a warning but do not fail so that
- * tests for setable properties succeed.
- */
- if (zpool_prop_get_feature(zpool_hdl,
- "feature@allocation_classes", state,
- sizeof (state)) != 0 ||
- strcmp(state, ZFS_FEATURE_ACTIVE) != 0) {
- (void) fprintf(stderr, gettext(
- "%s: property requires a special "
- "device in the pool\n"), propname);
- }
- }
- if (intval != 0 &&
- (intval < SPA_MINBLOCKSIZE ||
- intval > SPA_OLD_MAXBLOCKSIZE || !ISP2(intval))) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid '%s=%d' property: must be zero or "
- "a power of 2 from 512B to 128K"), propname,
- intval);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
-
- case ZFS_PROP_MLSLABEL:
- {
-#ifdef illumos
- /*
- * Verify the mlslabel string and convert to
- * internal hex label string.
- */
-
- m_label_t *new_sl;
- char *hex = NULL; /* internal label string */
-
- /* Default value is already OK. */
- if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
- break;
-
- /* Verify the label can be converted to binary form */
- if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) ||
- (str_to_label(strval, &new_sl, MAC_LABEL,
- L_NO_CORRECTION, NULL) == -1)) {
- goto badlabel;
- }
-
- /* Now translate to hex internal label string */
- if (label_to_str(new_sl, &hex, M_INTERNAL,
- DEF_NAMES) != 0) {
- if (hex)
- free(hex);
- goto badlabel;
- }
- m_label_free(new_sl);
-
- /* If string is already in internal form, we're done. */
- if (strcmp(strval, hex) == 0) {
- free(hex);
- break;
- }
-
- /* Replace the label string with the internal form. */
- (void) nvlist_remove(ret, zfs_prop_to_name(prop),
- DATA_TYPE_STRING);
- verify(nvlist_add_string(ret, zfs_prop_to_name(prop),
- hex) == 0);
- free(hex);
-
- break;
-
-badlabel:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid mlslabel '%s'"), strval);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- m_label_free(new_sl); /* OK if null */
-#else /* !illumos */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "mlslabel is not supported on FreeBSD"));
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
-#endif /* illumos */
- goto error;
-
- }
-
- case ZFS_PROP_MOUNTPOINT:
- {
- namecheck_err_t why;
-
- if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 ||
- strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0)
- break;
-
- if (mountpoint_namecheck(strval, &why)) {
- switch (why) {
- case NAME_ERR_LEADING_SLASH:
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN,
- "'%s' must be an absolute path, "
- "'none', or 'legacy'"), propname);
- break;
- case NAME_ERR_TOOLONG:
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN,
- "component of '%s' is too long"),
- propname);
- break;
-
- default:
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN,
- "(%d) not defined"),
- why);
- break;
- }
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- }
-
- /*FALLTHRU*/
-
- case ZFS_PROP_SHARESMB:
- case ZFS_PROP_SHARENFS:
- /*
- * For the mountpoint and sharenfs or sharesmb
- * properties, check if it can be set in a
- * global/non-global zone based on
- * the zoned property value:
- *
- * global zone non-global zone
- * --------------------------------------------------
- * zoned=on mountpoint (no) mountpoint (yes)
- * sharenfs (no) sharenfs (no)
- * sharesmb (no) sharesmb (no)
- *
- * zoned=off mountpoint (yes) N/A
- * sharenfs (yes)
- * sharesmb (yes)
- */
- if (zoned) {
- if (getzoneid() == GLOBAL_ZONEID) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' cannot be set on "
- "dataset in a non-global zone"),
- propname);
- (void) zfs_error(hdl, EZFS_ZONED,
- errbuf);
- goto error;
- } else if (prop == ZFS_PROP_SHARENFS ||
- prop == ZFS_PROP_SHARESMB) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' cannot be set in "
- "a non-global zone"), propname);
- (void) zfs_error(hdl, EZFS_ZONED,
- errbuf);
- goto error;
- }
- } else if (getzoneid() != GLOBAL_ZONEID) {
- /*
- * If zoned property is 'off', this must be in
- * a global zone. If not, something is wrong.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' cannot be set while dataset "
- "'zoned' property is set"), propname);
- (void) zfs_error(hdl, EZFS_ZONED, errbuf);
- goto error;
- }
-
- /*
- * At this point, it is legitimate to set the
- * property. Now we want to make sure that the
- * property value is valid if it is sharenfs.
- */
- if ((prop == ZFS_PROP_SHARENFS ||
- prop == ZFS_PROP_SHARESMB) &&
- strcmp(strval, "on") != 0 &&
- strcmp(strval, "off") != 0) {
- zfs_share_proto_t proto;
-
- if (prop == ZFS_PROP_SHARESMB)
- proto = PROTO_SMB;
- else
- proto = PROTO_NFS;
-
- /*
- * Must be an valid sharing protocol
- * option string so init the libshare
- * in order to enable the parser and
- * then parse the options. We use the
- * control API since we don't care about
- * the current configuration and don't
- * want the overhead of loading it
- * until we actually do something.
- */
-
- if (zfs_init_libshare(hdl,
- SA_INIT_CONTROL_API) != SA_OK) {
- /*
- * An error occurred so we can't do
- * anything
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' cannot be set: problem "
- "in share initialization"),
- propname);
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- goto error;
- }
-
- if (zfs_parse_options(strval, proto) != SA_OK) {
- /*
- * There was an error in parsing so
- * deal with it by issuing an error
- * message and leaving after
- * uninitializing the the libshare
- * interface.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' cannot be set to invalid "
- "options"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- zfs_uninit_libshare(hdl);
- goto error;
- }
- zfs_uninit_libshare(hdl);
- }
-
- break;
-
- case ZFS_PROP_UTF8ONLY:
- chosen_utf = (int)intval;
- break;
-
- case ZFS_PROP_NORMALIZE:
- chosen_normal = (int)intval;
- break;
-
- default:
- break;
- }
-
- /*
- * For changes to existing volumes, we have some additional
- * checks to enforce.
- */
- if (type == ZFS_TYPE_VOLUME && zhp != NULL) {
- uint64_t volsize = zfs_prop_get_int(zhp,
- ZFS_PROP_VOLSIZE);
- uint64_t blocksize = zfs_prop_get_int(zhp,
- ZFS_PROP_VOLBLOCKSIZE);
- char buf[64];
-
- switch (prop) {
- case ZFS_PROP_RESERVATION:
- if (intval > volsize) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' is greater than current "
- "volume size"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- goto error;
- }
- break;
-
- case ZFS_PROP_REFRESERVATION:
- if (intval > volsize && intval != UINT64_MAX) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' is greater than current "
- "volume size"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- goto error;
- }
- break;
-
- case ZFS_PROP_VOLSIZE:
- if (intval % blocksize != 0) {
- zfs_nicenum(blocksize, buf,
- sizeof (buf));
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a multiple of "
- "volume block size (%s)"),
- propname, buf);
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- goto error;
- }
-
- if (intval == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' cannot be zero"),
- propname);
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- goto error;
- }
- break;
-
- default:
- break;
- }
- }
- }
-
- /*
- * If normalization was chosen, but no UTF8 choice was made,
- * enforce rejection of non-UTF8 names.
- *
- * If normalization was chosen, but rejecting non-UTF8 names
- * was explicitly not chosen, it is an error.
- */
- if (chosen_normal > 0 && chosen_utf < 0) {
- if (nvlist_add_uint64(ret,
- zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) {
- (void) no_memory(hdl);
- goto error;
- }
- } else if (chosen_normal > 0 && chosen_utf == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be set 'on' if normalization chosen"),
- zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- return (ret);
-
-error:
- nvlist_free(ret);
- return (NULL);
-}
-
-int
-zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
-{
- uint64_t old_volsize;
- uint64_t new_volsize;
- uint64_t old_reservation;
- uint64_t new_reservation;
- zfs_prop_t resv_prop;
- nvlist_t *props;
-
- /*
- * If this is an existing volume, and someone is setting the volsize,
- * make sure that it matches the reservation, or add it if necessary.
- */
- old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
- if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
- return (-1);
- old_reservation = zfs_prop_get_int(zhp, resv_prop);
-
- props = fnvlist_alloc();
- fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
- zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE));
-
- if ((zvol_volsize_to_reservation(old_volsize, props) !=
- old_reservation) || nvlist_exists(nvl,
- zfs_prop_to_name(resv_prop))) {
- fnvlist_free(props);
- return (0);
- }
- if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
- &new_volsize) != 0) {
- fnvlist_free(props);
- return (-1);
- }
- new_reservation = zvol_volsize_to_reservation(new_volsize, props);
- fnvlist_free(props);
-
- if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop),
- new_reservation) != 0) {
- (void) no_memory(zhp->zfs_hdl);
- return (-1);
- }
- return (1);
-}
-
-/*
- * Helper for 'zfs {set|clone} refreservation=auto'. Must be called after
- * zfs_valid_proplist(), as it is what sets the UINT64_MAX sentinal value.
- * Return codes must match zfs_add_synthetic_resv().
- */
-static int
-zfs_fix_auto_resv(zfs_handle_t *zhp, nvlist_t *nvl)
-{
- uint64_t volsize;
- uint64_t resvsize;
- zfs_prop_t prop;
- nvlist_t *props;
-
- if (!ZFS_IS_VOLUME(zhp)) {
- return (0);
- }
-
- if (zfs_which_resv_prop(zhp, &prop) != 0) {
- return (-1);
- }
-
- if (prop != ZFS_PROP_REFRESERVATION) {
- return (0);
- }
-
- if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(prop), &resvsize) != 0) {
- /* No value being set, so it can't be "auto" */
- return (0);
- }
- if (resvsize != UINT64_MAX) {
- /* Being set to a value other than "auto" */
- return (0);
- }
-
- props = fnvlist_alloc();
-
- fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
- zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE));
-
- if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
- &volsize) != 0) {
- volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
- }
-
- resvsize = zvol_volsize_to_reservation(volsize, props);
- fnvlist_free(props);
-
- (void) nvlist_remove_all(nvl, zfs_prop_to_name(prop));
- if (nvlist_add_uint64(nvl, zfs_prop_to_name(prop), resvsize) != 0) {
- (void) no_memory(zhp->zfs_hdl);
- return (-1);
- }
- return (1);
-}
-
-void
-zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
- char *errbuf)
-{
- switch (err) {
-
- case ENOSPC:
- /*
- * For quotas and reservations, ENOSPC indicates
- * something different; setting a quota or reservation
- * doesn't use any disk space.
- */
- switch (prop) {
- case ZFS_PROP_QUOTA:
- case ZFS_PROP_REFQUOTA:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "size is less than current used or "
- "reserved space"));
- (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
- break;
-
- case ZFS_PROP_RESERVATION:
- case ZFS_PROP_REFRESERVATION:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "size is greater than available space"));
- (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
- break;
-
- default:
- (void) zfs_standard_error(hdl, err, errbuf);
- break;
- }
- break;
-
- case EBUSY:
- (void) zfs_standard_error(hdl, EBUSY, errbuf);
- break;
-
- case EROFS:
- (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf);
- break;
-
- case E2BIG:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property value too long"));
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- break;
-
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool and or dataset must be upgraded to set this "
- "property or value"));
- (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
- break;
-
- case ERANGE:
- case EDOM:
- if (prop == ZFS_PROP_COMPRESSION ||
- prop == ZFS_PROP_RECORDSIZE) {
- (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property setting is not allowed on "
- "bootable datasets"));
- (void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
- } else if (prop == ZFS_PROP_CHECKSUM ||
- prop == ZFS_PROP_DEDUP) {
- (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property setting is not allowed on "
- "root pools"));
- (void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
- } else {
- (void) zfs_standard_error(hdl, err, errbuf);
- }
- break;
-
- case EINVAL:
- if (prop == ZPROP_INVAL) {
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- } else {
- (void) zfs_standard_error(hdl, err, errbuf);
- }
- break;
-
- case EOVERFLOW:
- /*
- * This platform can't address a volume this big.
- */
-#ifdef _ILP32
- if (prop == ZFS_PROP_VOLSIZE) {
- (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf);
- break;
- }
-#endif
- /* FALLTHROUGH */
- default:
- (void) zfs_standard_error(hdl, err, errbuf);
- }
-}
-
-/*
- * Given a property name and value, set the property for the given dataset.
- */
-int
-zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
-{
- int ret = -1;
- char errbuf[1024];
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- nvlist_t *nvl = NULL;
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
- zhp->zfs_name);
-
- if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
- nvlist_add_string(nvl, propname, propval) != 0) {
- (void) no_memory(hdl);
- goto error;
- }
-
- ret = zfs_prop_set_list(zhp, nvl);
-
-error:
- nvlist_free(nvl);
- return (ret);
-}
-
-
-
-/*
- * Given an nvlist of property names and values, set the properties for the
- * given dataset.
- */
-int
-zfs_prop_set_list(zfs_handle_t *zhp, nvlist_t *props)
-{
- zfs_cmd_t zc = { 0 };
- int ret = -1;
- prop_changelist_t **cls = NULL;
- int cl_idx;
- char errbuf[1024];
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- nvlist_t *nvl;
- int nvl_len;
- int added_resv = 0;
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
- zhp->zfs_name);
-
- if ((nvl = zfs_valid_proplist(hdl, zhp->zfs_type, props,
- zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, zhp->zpool_hdl,
- errbuf)) == NULL)
- goto error;
-
- /*
- * We have to check for any extra properties which need to be added
- * before computing the length of the nvlist.
- */
- for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL);
- elem != NULL;
- elem = nvlist_next_nvpair(nvl, elem)) {
- if (zfs_name_to_prop(nvpair_name(elem)) == ZFS_PROP_VOLSIZE &&
- (added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1) {
- goto error;
- }
- }
-
- if (added_resv != 1 &&
- (added_resv = zfs_fix_auto_resv(zhp, nvl)) == -1) {
- goto error;
- }
-
- /*
- * Check how many properties we're setting and allocate an array to
- * store changelist pointers for postfix().
- */
- nvl_len = 0;
- for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL);
- elem != NULL;
- elem = nvlist_next_nvpair(nvl, elem))
- nvl_len++;
- if ((cls = calloc(nvl_len, sizeof (prop_changelist_t *))) == NULL)
- goto error;
-
- cl_idx = 0;
- for (nvpair_t *elem = nvlist_next_nvpair(nvl, NULL);
- elem != NULL;
- elem = nvlist_next_nvpair(nvl, elem)) {
-
- zfs_prop_t prop = zfs_name_to_prop(nvpair_name(elem));
-
- assert(cl_idx < nvl_len);
- /*
- * We don't want to unmount & remount the dataset when changing
- * its canmount property to 'on' or 'noauto'. We only use
- * the changelist logic to unmount when setting canmount=off.
- */
- if (prop != ZFS_PROP_CANMOUNT ||
- (fnvpair_value_uint64(elem) == ZFS_CANMOUNT_OFF &&
- zfs_is_mounted(zhp, NULL))) {
- cls[cl_idx] = changelist_gather(zhp, prop, 0, 0);
- if (cls[cl_idx] == NULL)
- goto error;
- }
-
- if (prop == ZFS_PROP_MOUNTPOINT &&
- changelist_haszonedchild(cls[cl_idx])) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "child dataset with inherited mountpoint is used "
- "in a non-global zone"));
- ret = zfs_error(hdl, EZFS_ZONED, errbuf);
- goto error;
- }
-
- /* We don't support those properties on FreeBSD. */
- switch (prop) {
- case ZFS_PROP_DEVICES:
- case ZFS_PROP_ISCSIOPTIONS:
- case ZFS_PROP_XATTR:
- case ZFS_PROP_VSCAN:
- case ZFS_PROP_NBMAND:
- case ZFS_PROP_MLSLABEL:
- (void) snprintf(errbuf, sizeof (errbuf),
- "property '%s' not supported on FreeBSD",
- nvpair_name(elem));
- ret = zfs_error(hdl, EZFS_PERM, errbuf);
- goto error;
- }
-
- if (cls[cl_idx] != NULL &&
- (ret = changelist_prefix(cls[cl_idx])) != 0)
- goto error;
-
- cl_idx++;
- }
- assert(cl_idx == nvl_len);
-
- /*
- * Execute the corresponding ioctl() to set this list of properties.
- */
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- if ((ret = zcmd_write_src_nvlist(hdl, &zc, nvl)) != 0 ||
- (ret = zcmd_alloc_dst_nvlist(hdl, &zc, 0)) != 0)
- goto error;
-
- ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
-
- if (ret != 0) {
- if (zc.zc_nvlist_dst_filled == B_FALSE) {
- (void) zfs_standard_error(hdl, errno, errbuf);
- goto error;
- }
-
- /* Get the list of unset properties back and report them. */
- nvlist_t *errorprops = NULL;
- if (zcmd_read_dst_nvlist(hdl, &zc, &errorprops) != 0)
- goto error;
- for (nvpair_t *elem = nvlist_next_nvpair(errorprops, NULL);
- elem != NULL;
- elem = nvlist_next_nvpair(errorprops, elem)) {
- zfs_prop_t prop = zfs_name_to_prop(nvpair_name(elem));
- zfs_setprop_error(hdl, prop, errno, errbuf);
- }
- nvlist_free(errorprops);
-
- if (added_resv && errno == ENOSPC) {
- /* clean up the volsize property we tried to set */
- uint64_t old_volsize = zfs_prop_get_int(zhp,
- ZFS_PROP_VOLSIZE);
- nvlist_free(nvl);
- nvl = NULL;
- zcmd_free_nvlists(&zc);
-
- if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
- goto error;
- if (nvlist_add_uint64(nvl,
- zfs_prop_to_name(ZFS_PROP_VOLSIZE),
- old_volsize) != 0)
- goto error;
- if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0)
- goto error;
- (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
- }
- } else {
- for (cl_idx = 0; cl_idx < nvl_len; cl_idx++) {
- if (cls[cl_idx] != NULL) {
- int clp_err = changelist_postfix(cls[cl_idx]);
- if (clp_err != 0)
- ret = clp_err;
- }
- }
-
- /*
- * Refresh the statistics so the new property value
- * is reflected.
- */
- if (ret == 0)
- (void) get_stats(zhp);
- }
-
-error:
- nvlist_free(nvl);
- zcmd_free_nvlists(&zc);
- if (cls != NULL) {
- for (cl_idx = 0; cl_idx < nvl_len; cl_idx++) {
- if (cls[cl_idx] != NULL)
- changelist_free(cls[cl_idx]);
- }
- free(cls);
- }
- return (ret);
-}
-
-/*
- * Given a property, inherit the value from the parent dataset, or if received
- * is TRUE, revert to the received value, if any.
- */
-int
-zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received)
-{
- zfs_cmd_t zc = { 0 };
- int ret;
- prop_changelist_t *cl;
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- char errbuf[1024];
- zfs_prop_t prop;
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot inherit %s for '%s'"), propname, zhp->zfs_name);
-
- zc.zc_cookie = received;
- if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) {
- /*
- * For user properties, the amount of work we have to do is very
- * small, so just do it here.
- */
- if (!zfs_prop_user(propname)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid property"));
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
- }
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
-
- if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0)
- return (zfs_standard_error(hdl, errno, errbuf));
-
- return (0);
- }
-
- /*
- * Verify that this property is inheritable.
- */
- if (zfs_prop_readonly(prop))
- return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf));
-
- if (!zfs_prop_inheritable(prop) && !received)
- return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf));
-
- /*
- * Check to see if the value applies to this type
- */
- if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
- return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));
-
- /*
- * Normalize the name, to get rid of shorthand abbreviations.
- */
- propname = zfs_prop_to_name(prop);
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
-
- if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID &&
- zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "dataset is used in a non-global zone"));
- return (zfs_error(hdl, EZFS_ZONED, errbuf));
- }
-
- /*
- * Determine datasets which will be affected by this change, if any.
- */
- if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)
- return (-1);
-
- if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "child dataset with inherited mountpoint is used "
- "in a non-global zone"));
- ret = zfs_error(hdl, EZFS_ZONED, errbuf);
- goto error;
- }
-
- if ((ret = changelist_prefix(cl)) != 0)
- goto error;
-
- if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) {
- return (zfs_standard_error(hdl, errno, errbuf));
- } else {
-
- if ((ret = changelist_postfix(cl)) != 0)
- goto error;
-
- /*
- * Refresh the statistics so the new property is reflected.
- */
- (void) get_stats(zhp);
- }
-
-error:
- changelist_free(cl);
- return (ret);
-}
-
-/*
- * True DSL properties are stored in an nvlist. The following two functions
- * extract them appropriately.
- */
-static uint64_t
-getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
-{
- nvlist_t *nv;
- uint64_t value;
-
- *source = NULL;
- if (nvlist_lookup_nvlist(zhp->zfs_props,
- zfs_prop_to_name(prop), &nv) == 0) {
- verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0);
- (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
- } else {
- verify(!zhp->zfs_props_table ||
- zhp->zfs_props_table[prop] == B_TRUE);
- value = zfs_prop_default_numeric(prop);
- *source = "";
- }
-
- return (value);
-}
-
-static const char *
-getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
-{
- nvlist_t *nv;
- const char *value;
-
- *source = NULL;
- if (nvlist_lookup_nvlist(zhp->zfs_props,
- zfs_prop_to_name(prop), &nv) == 0) {
- value = fnvlist_lookup_string(nv, ZPROP_VALUE);
- (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
- } else {
- verify(!zhp->zfs_props_table ||
- zhp->zfs_props_table[prop] == B_TRUE);
- value = zfs_prop_default_string(prop);
- *source = "";
- }
-
- return (value);
-}
-
-static boolean_t
-zfs_is_recvd_props_mode(zfs_handle_t *zhp)
-{
- return (zhp->zfs_props == zhp->zfs_recvd_props);
-}
-
-static void
-zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie)
-{
- *cookie = (uint64_t)(uintptr_t)zhp->zfs_props;
- zhp->zfs_props = zhp->zfs_recvd_props;
-}
-
-static void
-zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie)
-{
- zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie;
- *cookie = 0;
-}
-
-/*
- * Internal function for getting a numeric property. Both zfs_prop_get() and
- * zfs_prop_get_int() are built using this interface.
- *
- * Certain properties can be overridden using 'mount -o'. In this case, scan
- * the contents of the /etc/mnttab entry, searching for the appropriate options.
- * If they differ from the on-disk values, report the current values and mark
- * the source "temporary".
- */
-static int
-get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
- char **source, uint64_t *val)
-{
- zfs_cmd_t zc = { 0 };
- nvlist_t *zplprops = NULL;
- struct mnttab mnt;
- char *mntopt_on = NULL;
- char *mntopt_off = NULL;
- boolean_t received = zfs_is_recvd_props_mode(zhp);
-
- *source = NULL;
-
- switch (prop) {
- case ZFS_PROP_ATIME:
- mntopt_on = MNTOPT_ATIME;
- mntopt_off = MNTOPT_NOATIME;
- break;
-
- case ZFS_PROP_DEVICES:
- mntopt_on = MNTOPT_DEVICES;
- mntopt_off = MNTOPT_NODEVICES;
- break;
-
- case ZFS_PROP_EXEC:
- mntopt_on = MNTOPT_EXEC;
- mntopt_off = MNTOPT_NOEXEC;
- break;
-
- case ZFS_PROP_READONLY:
- mntopt_on = MNTOPT_RO;
- mntopt_off = MNTOPT_RW;
- break;
-
- case ZFS_PROP_SETUID:
- mntopt_on = MNTOPT_SETUID;
- mntopt_off = MNTOPT_NOSETUID;
- break;
-
- case ZFS_PROP_XATTR:
- mntopt_on = MNTOPT_XATTR;
- mntopt_off = MNTOPT_NOXATTR;
- break;
-
- case ZFS_PROP_NBMAND:
- mntopt_on = MNTOPT_NBMAND;
- mntopt_off = MNTOPT_NONBMAND;
- break;
-
- default:
- break;
- }
-
- /*
- * Because looking up the mount options is potentially expensive
- * (iterating over all of /etc/mnttab), we defer its calculation until
- * we're looking up a property which requires its presence.
- */
- if (!zhp->zfs_mntcheck &&
- (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) {
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- struct mnttab entry;
-
- if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) {
- zhp->zfs_mntopts = zfs_strdup(hdl,
- entry.mnt_mntopts);
- if (zhp->zfs_mntopts == NULL)
- return (-1);
- }
-
- zhp->zfs_mntcheck = B_TRUE;
- }
-
- if (zhp->zfs_mntopts == NULL)
- mnt.mnt_mntopts = "";
- else
- mnt.mnt_mntopts = zhp->zfs_mntopts;
-
- switch (prop) {
- case ZFS_PROP_ATIME:
- case ZFS_PROP_DEVICES:
- case ZFS_PROP_EXEC:
- case ZFS_PROP_READONLY:
- case ZFS_PROP_SETUID:
- case ZFS_PROP_XATTR:
- case ZFS_PROP_NBMAND:
- *val = getprop_uint64(zhp, prop, source);
-
- if (received)
- break;
-
- if (hasmntopt(&mnt, mntopt_on) && !*val) {
- *val = B_TRUE;
- if (src)
- *src = ZPROP_SRC_TEMPORARY;
- } else if (hasmntopt(&mnt, mntopt_off) && *val) {
- *val = B_FALSE;
- if (src)
- *src = ZPROP_SRC_TEMPORARY;
- }
- break;
-
- case ZFS_PROP_CANMOUNT:
- case ZFS_PROP_VOLSIZE:
- case ZFS_PROP_QUOTA:
- case ZFS_PROP_REFQUOTA:
- case ZFS_PROP_RESERVATION:
- case ZFS_PROP_REFRESERVATION:
- case ZFS_PROP_FILESYSTEM_LIMIT:
- case ZFS_PROP_SNAPSHOT_LIMIT:
- case ZFS_PROP_FILESYSTEM_COUNT:
- case ZFS_PROP_SNAPSHOT_COUNT:
- *val = getprop_uint64(zhp, prop, source);
-
- if (*source == NULL) {
- /* not default, must be local */
- *source = zhp->zfs_name;
- }
- break;
-
- case ZFS_PROP_MOUNTED:
- *val = (zhp->zfs_mntopts != NULL);
- break;
-
- case ZFS_PROP_NUMCLONES:
- *val = zhp->zfs_dmustats.dds_num_clones;
- break;
-
- case ZFS_PROP_VERSION:
- case ZFS_PROP_NORMALIZE:
- case ZFS_PROP_UTF8ONLY:
- case ZFS_PROP_CASE:
- if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) ||
- zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
- return (-1);
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 ||
- nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop),
- val) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- nvlist_free(zplprops);
- zcmd_free_nvlists(&zc);
- break;
-
- case ZFS_PROP_INCONSISTENT:
- *val = zhp->zfs_dmustats.dds_inconsistent;
- break;
-
- default:
- switch (zfs_prop_get_type(prop)) {
- case PROP_TYPE_NUMBER:
- case PROP_TYPE_INDEX:
- *val = getprop_uint64(zhp, prop, source);
- /*
- * If we tried to use a default value for a
- * readonly property, it means that it was not
- * present. Note this only applies to "truly"
- * readonly properties, not set-once properties
- * like volblocksize.
- */
- if (zfs_prop_readonly(prop) &&
- !zfs_prop_setonce(prop) &&
- *source != NULL && (*source)[0] == '\0') {
- *source = NULL;
- return (-1);
- }
- break;
-
- case PROP_TYPE_STRING:
- default:
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "cannot get non-numeric property"));
- return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP,
- dgettext(TEXT_DOMAIN, "internal error")));
- }
- }
-
- return (0);
-}
-
-/*
- * Calculate the source type, given the raw source string.
- */
-static void
-get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source,
- char *statbuf, size_t statlen)
-{
- if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY)
- return;
-
- if (source == NULL) {
- *srctype = ZPROP_SRC_NONE;
- } else if (source[0] == '\0') {
- *srctype = ZPROP_SRC_DEFAULT;
- } else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) {
- *srctype = ZPROP_SRC_RECEIVED;
- } else {
- if (strcmp(source, zhp->zfs_name) == 0) {
- *srctype = ZPROP_SRC_LOCAL;
- } else {
- (void) strlcpy(statbuf, source, statlen);
- *srctype = ZPROP_SRC_INHERITED;
- }
- }
-
-}
-
-int
-zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
- size_t proplen, boolean_t literal)
-{
- zfs_prop_t prop;
- int err = 0;
-
- if (zhp->zfs_recvd_props == NULL)
- if (get_recvd_props_ioctl(zhp) != 0)
- return (-1);
-
- prop = zfs_name_to_prop(propname);
-
- if (prop != ZPROP_INVAL) {
- uint64_t cookie;
- if (!nvlist_exists(zhp->zfs_recvd_props, propname))
- return (-1);
- zfs_set_recvd_props_mode(zhp, &cookie);
- err = zfs_prop_get(zhp, prop, propbuf, proplen,
- NULL, NULL, 0, literal);
- zfs_unset_recvd_props_mode(zhp, &cookie);
- } else {
- nvlist_t *propval;
- char *recvdval;
- if (nvlist_lookup_nvlist(zhp->zfs_recvd_props,
- propname, &propval) != 0)
- return (-1);
- verify(nvlist_lookup_string(propval, ZPROP_VALUE,
- &recvdval) == 0);
- (void) strlcpy(propbuf, recvdval, proplen);
- }
-
- return (err == 0 ? 0 : -1);
-}
-
-static int
-get_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen)
-{
- nvlist_t *value;
- nvpair_t *pair;
-
- value = zfs_get_clones_nvl(zhp);
- if (value == NULL)
- return (-1);
-
- propbuf[0] = '\0';
- for (pair = nvlist_next_nvpair(value, NULL); pair != NULL;
- pair = nvlist_next_nvpair(value, pair)) {
- if (propbuf[0] != '\0')
- (void) strlcat(propbuf, ",", proplen);
- (void) strlcat(propbuf, nvpair_name(pair), proplen);
- }
-
- return (0);
-}
-
-struct get_clones_arg {
- uint64_t numclones;
- nvlist_t *value;
- const char *origin;
- char buf[ZFS_MAX_DATASET_NAME_LEN];
-};
-
-int
-get_clones_cb(zfs_handle_t *zhp, void *arg)
-{
- struct get_clones_arg *gca = arg;
-
- if (gca->numclones == 0) {
- zfs_close(zhp);
- return (0);
- }
-
- if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, gca->buf, sizeof (gca->buf),
- NULL, NULL, 0, B_TRUE) != 0)
- goto out;
- if (strcmp(gca->buf, gca->origin) == 0) {
- fnvlist_add_boolean(gca->value, zfs_get_name(zhp));
- gca->numclones--;
- }
-
-out:
- (void) zfs_iter_children(zhp, get_clones_cb, gca);
- zfs_close(zhp);
- return (0);
-}
-
-nvlist_t *
-zfs_get_clones_nvl(zfs_handle_t *zhp)
-{
- nvlist_t *nv, *value;
-
- if (nvlist_lookup_nvlist(zhp->zfs_props,
- zfs_prop_to_name(ZFS_PROP_CLONES), &nv) != 0) {
- struct get_clones_arg gca;
-
- /*
- * if this is a snapshot, then the kernel wasn't able
- * to get the clones. Do it by slowly iterating.
- */
- if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT)
- return (NULL);
- if (nvlist_alloc(&nv, NV_UNIQUE_NAME, 0) != 0)
- return (NULL);
- if (nvlist_alloc(&value, NV_UNIQUE_NAME, 0) != 0) {
- nvlist_free(nv);
- return (NULL);
- }
-
- gca.numclones = zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES);
- gca.value = value;
- gca.origin = zhp->zfs_name;
-
- if (gca.numclones != 0) {
- zfs_handle_t *root;
- char pool[ZFS_MAX_DATASET_NAME_LEN];
- char *cp = pool;
-
- /* get the pool name */
- (void) strlcpy(pool, zhp->zfs_name, sizeof (pool));
- (void) strsep(&cp, "/@");
- root = zfs_open(zhp->zfs_hdl, pool,
- ZFS_TYPE_FILESYSTEM);
-
- (void) get_clones_cb(root, &gca);
- }
-
- if (gca.numclones != 0 ||
- nvlist_add_nvlist(nv, ZPROP_VALUE, value) != 0 ||
- nvlist_add_nvlist(zhp->zfs_props,
- zfs_prop_to_name(ZFS_PROP_CLONES), nv) != 0) {
- nvlist_free(nv);
- nvlist_free(value);
- return (NULL);
- }
- nvlist_free(nv);
- nvlist_free(value);
- verify(0 == nvlist_lookup_nvlist(zhp->zfs_props,
- zfs_prop_to_name(ZFS_PROP_CLONES), &nv));
- }
-
- verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) == 0);
-
- return (value);
-}
-
-/*
- * Accepts a property and value and checks that the value
- * matches the one found by the channel program. If they are
- * not equal, print both of them.
- */
-void
-zcp_check(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t intval,
- const char *strval)
-{
- if (!zhp->zfs_hdl->libzfs_prop_debug)
- return;
- int error;
- char *poolname = zhp->zpool_hdl->zpool_name;
- const char *program =
- "args = ...\n"
- "ds = args['dataset']\n"
- "prop = args['property']\n"
- "value, setpoint = zfs.get_prop(ds, prop)\n"
- "return {value=value, setpoint=setpoint}\n";
- nvlist_t *outnvl;
- nvlist_t *retnvl;
- nvlist_t *argnvl = fnvlist_alloc();
-
- fnvlist_add_string(argnvl, "dataset", zhp->zfs_name);
- fnvlist_add_string(argnvl, "property", zfs_prop_to_name(prop));
-
- error = lzc_channel_program_nosync(poolname, program,
- 10 * 1000 * 1000, 10 * 1024 * 1024, argnvl, &outnvl);
-
- if (error == 0) {
- retnvl = fnvlist_lookup_nvlist(outnvl, "return");
- if (zfs_prop_get_type(prop) == PROP_TYPE_NUMBER) {
- int64_t ans;
- error = nvlist_lookup_int64(retnvl, "value", &ans);
- if (error != 0) {
- (void) fprintf(stderr, "zcp check error: %u\n",
- error);
- return;
- }
- if (ans != intval) {
- (void) fprintf(stderr,
- "%s: zfs found %lld, but zcp found %lld\n",
- zfs_prop_to_name(prop),
- (longlong_t)intval, (longlong_t)ans);
- }
- } else {
- char *str_ans;
- error = nvlist_lookup_string(retnvl, "value", &str_ans);
- if (error != 0) {
- (void) fprintf(stderr, "zcp check error: %u\n",
- error);
- return;
- }
- if (strcmp(strval, str_ans) != 0) {
- (void) fprintf(stderr,
- "%s: zfs found %s, but zcp found %s\n",
- zfs_prop_to_name(prop),
- strval, str_ans);
- }
- }
- } else {
- (void) fprintf(stderr,
- "zcp check failed, channel program error: %u\n", error);
- }
- nvlist_free(argnvl);
- nvlist_free(outnvl);
-}
-
-/*
- * Retrieve a property from the given object. If 'literal' is specified, then
- * numbers are left as exact values. Otherwise, numbers are converted to a
- * human-readable form.
- *
- * Returns 0 on success, or -1 on error.
- */
-int
-zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
- zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal)
-{
- char *source = NULL;
- uint64_t val;
- const char *str;
- const char *strval;
- boolean_t received = zfs_is_recvd_props_mode(zhp);
-
- /*
- * Check to see if this property applies to our object
- */
- if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
- return (-1);
-
- if (received && zfs_prop_readonly(prop))
- return (-1);
-
- if (src)
- *src = ZPROP_SRC_NONE;
-
- switch (prop) {
- case ZFS_PROP_CREATION:
- /*
- * 'creation' is a time_t stored in the statistics. We convert
- * this into a string unless 'literal' is specified.
- */
- {
- val = getprop_uint64(zhp, prop, &source);
- time_t time = (time_t)val;
- struct tm t;
-
- if (literal ||
- localtime_r(&time, &t) == NULL ||
- strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
- &t) == 0)
- (void) snprintf(propbuf, proplen, "%llu", val);
- }
- zcp_check(zhp, prop, val, NULL);
- break;
-
- case ZFS_PROP_MOUNTPOINT:
- /*
- * Getting the precise mountpoint can be tricky.
- *
- * - for 'none' or 'legacy', return those values.
- * - for inherited mountpoints, we want to take everything
- * after our ancestor and append it to the inherited value.
- *
- * If the pool has an alternate root, we want to prepend that
- * root to any values we return.
- */
-
- str = getprop_string(zhp, prop, &source);
-
- if (str[0] == '/') {
- char buf[MAXPATHLEN];
- char *root = buf;
- const char *relpath;
-
- /*
- * If we inherit the mountpoint, even from a dataset
- * with a received value, the source will be the path of
- * the dataset we inherit from. If source is
- * ZPROP_SOURCE_VAL_RECVD, the received value is not
- * inherited.
- */
- if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) {
- relpath = "";
- } else {
- relpath = zhp->zfs_name + strlen(source);
- if (relpath[0] == '/')
- relpath++;
- }
-
- if ((zpool_get_prop(zhp->zpool_hdl,
- ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL,
- B_FALSE)) || (strcmp(root, "-") == 0))
- root[0] = '\0';
- /*
- * Special case an alternate root of '/'. This will
- * avoid having multiple leading slashes in the
- * mountpoint path.
- */
- if (strcmp(root, "/") == 0)
- root++;
-
- /*
- * If the mountpoint is '/' then skip over this
- * if we are obtaining either an alternate root or
- * an inherited mountpoint.
- */
- if (str[1] == '\0' && (root[0] != '\0' ||
- relpath[0] != '\0'))
- str++;
-
- if (relpath[0] == '\0')
- (void) snprintf(propbuf, proplen, "%s%s",
- root, str);
- else
- (void) snprintf(propbuf, proplen, "%s%s%s%s",
- root, str, relpath[0] == '@' ? "" : "/",
- relpath);
- } else {
- /* 'legacy' or 'none' */
- (void) strlcpy(propbuf, str, proplen);
- }
- zcp_check(zhp, prop, NULL, propbuf);
- break;
-
- case ZFS_PROP_ORIGIN:
- str = getprop_string(zhp, prop, &source);
- if (str == NULL)
- return (-1);
- (void) strlcpy(propbuf, str, proplen);
- zcp_check(zhp, prop, NULL, str);
- break;
-
- case ZFS_PROP_CLONES:
- if (get_clones_string(zhp, propbuf, proplen) != 0)
- return (-1);
- break;
-
- case ZFS_PROP_QUOTA:
- case ZFS_PROP_REFQUOTA:
- case ZFS_PROP_RESERVATION:
- case ZFS_PROP_REFRESERVATION:
-
- if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
- return (-1);
- /*
- * If quota or reservation is 0, we translate this into 'none'
- * (unless literal is set), and indicate that it's the default
- * value. Otherwise, we print the number nicely and indicate
- * that its set locally.
- */
- if (val == 0) {
- if (literal)
- (void) strlcpy(propbuf, "0", proplen);
- else
- (void) strlcpy(propbuf, "none", proplen);
- } else {
- if (literal)
- (void) snprintf(propbuf, proplen, "%llu",
- (u_longlong_t)val);
- else
- zfs_nicenum(val, propbuf, proplen);
- }
- zcp_check(zhp, prop, val, NULL);
- break;
-
- case ZFS_PROP_FILESYSTEM_LIMIT:
- case ZFS_PROP_SNAPSHOT_LIMIT:
- case ZFS_PROP_FILESYSTEM_COUNT:
- case ZFS_PROP_SNAPSHOT_COUNT:
-
- if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
- return (-1);
-
- /*
- * If limit is UINT64_MAX, we translate this into 'none' (unless
- * literal is set), and indicate that it's the default value.
- * Otherwise, we print the number nicely and indicate that it's
- * set locally.
- */
- if (literal) {
- (void) snprintf(propbuf, proplen, "%llu",
- (u_longlong_t)val);
- } else if (val == UINT64_MAX) {
- (void) strlcpy(propbuf, "none", proplen);
- } else {
- zfs_nicenum(val, propbuf, proplen);
- }
-
- zcp_check(zhp, prop, val, NULL);
- break;
-
- case ZFS_PROP_REFRATIO:
- case ZFS_PROP_COMPRESSRATIO:
- if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
- return (-1);
- (void) snprintf(propbuf, proplen, "%llu.%02llux",
- (u_longlong_t)(val / 100),
- (u_longlong_t)(val % 100));
- zcp_check(zhp, prop, val, NULL);
- break;
-
- case ZFS_PROP_TYPE:
- switch (zhp->zfs_type) {
- case ZFS_TYPE_FILESYSTEM:
- str = "filesystem";
- break;
- case ZFS_TYPE_VOLUME:
- str = "volume";
- break;
- case ZFS_TYPE_SNAPSHOT:
- str = "snapshot";
- break;
- case ZFS_TYPE_BOOKMARK:
- str = "bookmark";
- break;
- default:
- abort();
- }
- (void) snprintf(propbuf, proplen, "%s", str);
- zcp_check(zhp, prop, NULL, propbuf);
- break;
-
- case ZFS_PROP_MOUNTED:
- /*
- * The 'mounted' property is a pseudo-property that described
- * whether the filesystem is currently mounted. Even though
- * it's a boolean value, the typical values of "on" and "off"
- * don't make sense, so we translate to "yes" and "no".
- */
- if (get_numeric_property(zhp, ZFS_PROP_MOUNTED,
- src, &source, &val) != 0)
- return (-1);
- if (val)
- (void) strlcpy(propbuf, "yes", proplen);
- else
- (void) strlcpy(propbuf, "no", proplen);
- break;
-
- case ZFS_PROP_NAME:
- /*
- * The 'name' property is a pseudo-property derived from the
- * dataset name. It is presented as a real property to simplify
- * consumers.
- */
- (void) strlcpy(propbuf, zhp->zfs_name, proplen);
- zcp_check(zhp, prop, NULL, propbuf);
- break;
-
- case ZFS_PROP_MLSLABEL:
- {
-#ifdef illumos
- m_label_t *new_sl = NULL;
- char *ascii = NULL; /* human readable label */
-
- (void) strlcpy(propbuf,
- getprop_string(zhp, prop, &source), proplen);
-
- if (literal || (strcasecmp(propbuf,
- ZFS_MLSLABEL_DEFAULT) == 0))
- break;
-
- /*
- * Try to translate the internal hex string to
- * human-readable output. If there are any
- * problems just use the hex string.
- */
-
- if (str_to_label(propbuf, &new_sl, MAC_LABEL,
- L_NO_CORRECTION, NULL) == -1) {
- m_label_free(new_sl);
- break;
- }
-
- if (label_to_str(new_sl, &ascii, M_LABEL,
- DEF_NAMES) != 0) {
- if (ascii)
- free(ascii);
- m_label_free(new_sl);
- break;
- }
- m_label_free(new_sl);
-
- (void) strlcpy(propbuf, ascii, proplen);
- free(ascii);
-#else /* !illumos */
- propbuf[0] = '\0';
-#endif /* illumos */
- }
- break;
-
- case ZFS_PROP_GUID:
- case ZFS_PROP_CREATETXG:
- /*
- * GUIDs are stored as numbers, but they are identifiers.
- * We don't want them to be pretty printed, because pretty
- * printing mangles the ID into a truncated and useless value.
- */
- if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
- return (-1);
- (void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val);
- zcp_check(zhp, prop, val, NULL);
- break;
-
- default:
- switch (zfs_prop_get_type(prop)) {
- case PROP_TYPE_NUMBER:
- if (get_numeric_property(zhp, prop, src,
- &source, &val) != 0) {
- return (-1);
- }
-
- if (literal) {
- (void) snprintf(propbuf, proplen, "%llu",
- (u_longlong_t)val);
- } else {
- zfs_nicenum(val, propbuf, proplen);
- }
- zcp_check(zhp, prop, val, NULL);
- break;
-
- case PROP_TYPE_STRING:
- str = getprop_string(zhp, prop, &source);
- if (str == NULL)
- return (-1);
-
- (void) strlcpy(propbuf, str, proplen);
- zcp_check(zhp, prop, NULL, str);
- break;
-
- case PROP_TYPE_INDEX:
- if (get_numeric_property(zhp, prop, src,
- &source, &val) != 0)
- return (-1);
- if (zfs_prop_index_to_string(prop, val, &strval) != 0)
- return (-1);
-
- (void) strlcpy(propbuf, strval, proplen);
- zcp_check(zhp, prop, NULL, strval);
- break;
-
- default:
- abort();
- }
- }
-
- get_source(zhp, src, source, statbuf, statlen);
-
- return (0);
-}
-
-/*
- * Utility function to get the given numeric property. Does no validation that
- * the given property is the appropriate type; should only be used with
- * hard-coded property types.
- */
-uint64_t
-zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
-{
- char *source;
- uint64_t val;
-
- (void) get_numeric_property(zhp, prop, NULL, &source, &val);
-
- return (val);
-}
-
-int
-zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val)
-{
- char buf[64];
-
- (void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val);
- return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf));
-}
-
-/*
- * Similar to zfs_prop_get(), but returns the value as an integer.
- */
-int
-zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
- zprop_source_t *src, char *statbuf, size_t statlen)
-{
- char *source;
-
- /*
- * Check to see if this property applies to our object
- */
- if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) {
- return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,
- dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
- zfs_prop_to_name(prop)));
- }
-
- if (src)
- *src = ZPROP_SRC_NONE;
-
- if (get_numeric_property(zhp, prop, src, &source, value) != 0)
- return (-1);
-
- get_source(zhp, src, source, statbuf, statlen);
-
- return (0);
-}
-
-static int
-idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser,
- char **domainp, idmap_rid_t *ridp)
-{
-#ifdef illumos
- idmap_get_handle_t *get_hdl = NULL;
- idmap_stat status;
- int err = EINVAL;
-
- if (idmap_get_create(&get_hdl) != IDMAP_SUCCESS)
- goto out;
-
- if (isuser) {
- err = idmap_get_sidbyuid(get_hdl, id,
- IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
- } else {
- err = idmap_get_sidbygid(get_hdl, id,
- IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
- }
- if (err == IDMAP_SUCCESS &&
- idmap_get_mappings(get_hdl) == IDMAP_SUCCESS &&
- status == IDMAP_SUCCESS)
- err = 0;
- else
- err = EINVAL;
-out:
- if (get_hdl)
- idmap_get_destroy(get_hdl);
- return (err);
-#else /* !illumos */
- assert(!"invalid code path");
- return (EINVAL); // silence compiler warning
-#endif /* illumos */
-}
-
-/*
- * convert the propname into parameters needed by kernel
- * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829
- * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789
- */
-static int
-userquota_propname_decode(const char *propname, boolean_t zoned,
- zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp)
-{
- zfs_userquota_prop_t type;
- char *cp, *end;
- char *numericsid = NULL;
- boolean_t isuser;
-
- domain[0] = '\0';
- *ridp = 0;
- /* Figure out the property type ({user|group}{quota|space}) */
- for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) {
- if (strncmp(propname, zfs_userquota_prop_prefixes[type],
- strlen(zfs_userquota_prop_prefixes[type])) == 0)
- break;
- }
- if (type == ZFS_NUM_USERQUOTA_PROPS)
- return (EINVAL);
- *typep = type;
-
- isuser = (type == ZFS_PROP_USERQUOTA ||
- type == ZFS_PROP_USERUSED);
-
- cp = strchr(propname, '@') + 1;
-
- if (strchr(cp, '@')) {
-#ifdef illumos
- /*
- * It's a SID name (eg "user@domain") that needs to be
- * turned into S-1-domainID-RID.
- */
- int flag = 0;
- idmap_stat stat, map_stat;
- uid_t pid;
- idmap_rid_t rid;
- idmap_get_handle_t *gh = NULL;
-
- stat = idmap_get_create(&gh);
- if (stat != IDMAP_SUCCESS) {
- idmap_get_destroy(gh);
- return (ENOMEM);
- }
- if (zoned && getzoneid() == GLOBAL_ZONEID)
- return (ENOENT);
- if (isuser) {
- stat = idmap_getuidbywinname(cp, NULL, flag, &pid);
- if (stat < 0)
- return (ENOENT);
- stat = idmap_get_sidbyuid(gh, pid, flag, &numericsid,
- &rid, &map_stat);
- } else {
- stat = idmap_getgidbywinname(cp, NULL, flag, &pid);
- if (stat < 0)
- return (ENOENT);
- stat = idmap_get_sidbygid(gh, pid, flag, &numericsid,
- &rid, &map_stat);
- }
- if (stat < 0) {
- idmap_get_destroy(gh);
- return (ENOENT);
- }
- stat = idmap_get_mappings(gh);
- idmap_get_destroy(gh);
-
- if (stat < 0) {
- return (ENOENT);
- }
- if (numericsid == NULL)
- return (ENOENT);
- cp = numericsid;
- *ridp = rid;
- /* will be further decoded below */
-#else /* !illumos */
- return (ENOENT);
-#endif /* illumos */
- }
-
- if (strncmp(cp, "S-1-", 4) == 0) {
- /* It's a numeric SID (eg "S-1-234-567-89") */
- (void) strlcpy(domain, cp, domainlen);
- errno = 0;
- if (*ridp == 0) {
- cp = strrchr(domain, '-');
- *cp = '\0';
- cp++;
- *ridp = strtoull(cp, &end, 10);
- } else {
- end = "";
- }
- if (numericsid) {
- free(numericsid);
- numericsid = NULL;
- }
- if (errno != 0 || *end != '\0')
- return (EINVAL);
- } else if (!isdigit(*cp)) {
- /*
- * It's a user/group name (eg "user") that needs to be
- * turned into a uid/gid
- */
- if (zoned && getzoneid() == GLOBAL_ZONEID)
- return (ENOENT);
- if (isuser) {
- struct passwd *pw;
- pw = getpwnam(cp);
- if (pw == NULL)
- return (ENOENT);
- *ridp = pw->pw_uid;
- } else {
- struct group *gr;
- gr = getgrnam(cp);
- if (gr == NULL)
- return (ENOENT);
- *ridp = gr->gr_gid;
- }
- } else {
- /* It's a user/group ID (eg "12345"). */
- uid_t id = strtoul(cp, &end, 10);
- idmap_rid_t rid;
- char *mapdomain;
-
- if (*end != '\0')
- return (EINVAL);
- if (id > MAXUID) {
- /* It's an ephemeral ID. */
- if (idmap_id_to_numeric_domain_rid(id, isuser,
- &mapdomain, &rid) != 0)
- return (ENOENT);
- (void) strlcpy(domain, mapdomain, domainlen);
- *ridp = rid;
- } else {
- *ridp = id;
- }
- }
-
- ASSERT3P(numericsid, ==, NULL);
- return (0);
-}
-
-static int
-zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
- uint64_t *propvalue, zfs_userquota_prop_t *typep)
-{
- int err;
- zfs_cmd_t zc = { 0 };
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- err = userquota_propname_decode(propname,
- zfs_prop_get_int(zhp, ZFS_PROP_ZONED),
- typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid);
- zc.zc_objset_type = *typep;
- if (err)
- return (err);
-
- err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc);
- if (err)
- return (err);
-
- *propvalue = zc.zc_cookie;
- return (0);
-}
-
-int
-zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,
- uint64_t *propvalue)
-{
- zfs_userquota_prop_t type;
-
- return (zfs_prop_get_userquota_common(zhp, propname, propvalue,
- &type));
-}
-
-int
-zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
- char *propbuf, int proplen, boolean_t literal)
-{
- int err;
- uint64_t propvalue;
- zfs_userquota_prop_t type;
-
- err = zfs_prop_get_userquota_common(zhp, propname, &propvalue,
- &type);
-
- if (err)
- return (err);
-
- if (literal) {
- (void) snprintf(propbuf, proplen, "%llu", propvalue);
- } else if (propvalue == 0 &&
- (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) {
- (void) strlcpy(propbuf, "none", proplen);
- } else {
- zfs_nicenum(propvalue, propbuf, proplen);
- }
- return (0);
-}
-
-int
-zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
- uint64_t *propvalue)
-{
- int err;
- zfs_cmd_t zc = { 0 };
- const char *snapname;
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- snapname = strchr(propname, '@') + 1;
- if (strchr(snapname, '@')) {
- (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
- } else {
- /* snapname is the short name, append it to zhp's fsname */
- char *cp;
-
- (void) strlcpy(zc.zc_value, zhp->zfs_name,
- sizeof (zc.zc_value));
- cp = strchr(zc.zc_value, '@');
- if (cp != NULL)
- *cp = '\0';
- (void) strlcat(zc.zc_value, "@", sizeof (zc.zc_value));
- (void) strlcat(zc.zc_value, snapname, sizeof (zc.zc_value));
- }
-
- err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc);
- if (err)
- return (err);
-
- *propvalue = zc.zc_cookie;
- return (0);
-}
-
-int
-zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
- char *propbuf, int proplen, boolean_t literal)
-{
- int err;
- uint64_t propvalue;
-
- err = zfs_prop_get_written_int(zhp, propname, &propvalue);
-
- if (err)
- return (err);
-
- if (literal) {
- (void) snprintf(propbuf, proplen, "%llu", propvalue);
- } else {
- zfs_nicenum(propvalue, propbuf, proplen);
- }
- return (0);
-}
-
-/*
- * Returns the name of the given zfs handle.
- */
-const char *
-zfs_get_name(const zfs_handle_t *zhp)
-{
- return (zhp->zfs_name);
-}
-
-/*
- * Returns the name of the parent pool for the given zfs handle.
- */
-const char *
-zfs_get_pool_name(const zfs_handle_t *zhp)
-{
- return (zhp->zpool_hdl->zpool_name);
-}
-
-/*
- * Returns the type of the given zfs handle.
- */
-zfs_type_t
-zfs_get_type(const zfs_handle_t *zhp)
-{
- return (zhp->zfs_type);
-}
-
-/*
- * Is one dataset name a child dataset of another?
- *
- * Needs to handle these cases:
- * Dataset 1 "a/foo" "a/foo" "a/foo" "a/foo"
- * Dataset 2 "a/fo" "a/foobar" "a/bar/baz" "a/foo/bar"
- * Descendant? No. No. No. Yes.
- */
-static boolean_t
-is_descendant(const char *ds1, const char *ds2)
-{
- size_t d1len = strlen(ds1);
-
- /* ds2 can't be a descendant if it's smaller */
- if (strlen(ds2) < d1len)
- return (B_FALSE);
-
- /* otherwise, compare strings and verify that there's a '/' char */
- return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0));
-}
-
-/*
- * Given a complete name, return just the portion that refers to the parent.
- * Will return -1 if there is no parent (path is just the name of the
- * pool).
- */
-static int
-parent_name(const char *path, char *buf, size_t buflen)
-{
- char *slashp;
-
- (void) strlcpy(buf, path, buflen);
-
- if ((slashp = strrchr(buf, '/')) == NULL)
- return (-1);
- *slashp = '\0';
-
- return (0);
-}
-
-/*
- * If accept_ancestor is false, then check to make sure that the given path has
- * a parent, and that it exists. If accept_ancestor is true, then find the
- * closest existing ancestor for the given path. In prefixlen return the
- * length of already existing prefix of the given path. We also fetch the
- * 'zoned' property, which is used to validate property settings when creating
- * new datasets.
- */
-static int
-check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
- boolean_t accept_ancestor, int *prefixlen)
-{
- zfs_cmd_t zc = { 0 };
- char parent[ZFS_MAX_DATASET_NAME_LEN];
- char *slash;
- zfs_handle_t *zhp;
- char errbuf[1024];
- uint64_t is_zoned;
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot create '%s'"), path);
-
- /* get parent, and check to see if this is just a pool */
- if (parent_name(path, parent, sizeof (parent)) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "missing dataset name"));
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- }
-
- /* check to see if the pool exists */
- if ((slash = strchr(parent, '/')) == NULL)
- slash = parent + strlen(parent);
- (void) strncpy(zc.zc_name, parent, slash - parent);
- zc.zc_name[slash - parent] = '\0';
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 &&
- errno == ENOENT) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "no such pool '%s'"), zc.zc_name);
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
- }
-
- /* check to see if the parent dataset exists */
- while ((zhp = make_dataset_handle(hdl, parent)) == NULL) {
- if (errno == ENOENT && accept_ancestor) {
- /*
- * Go deeper to find an ancestor, give up on top level.
- */
- if (parent_name(parent, parent, sizeof (parent)) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "no such pool '%s'"), zc.zc_name);
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
- }
- } else if (errno == ENOENT) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "parent does not exist"));
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
- } else
- return (zfs_standard_error(hdl, errno, errbuf));
- }
-
- is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
- if (zoned != NULL)
- *zoned = is_zoned;
-
- /* we are in a non-global zone, but parent is in the global zone */
- if (getzoneid() != GLOBAL_ZONEID && !is_zoned) {
- (void) zfs_standard_error(hdl, EPERM, errbuf);
- zfs_close(zhp);
- return (-1);
- }
-
- /* make sure parent is a filesystem */
- if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "parent is not a filesystem"));
- (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
- zfs_close(zhp);
- return (-1);
- }
-
- zfs_close(zhp);
- if (prefixlen != NULL)
- *prefixlen = strlen(parent);
- return (0);
-}
-
-/*
- * Finds whether the dataset of the given type(s) exists.
- */
-boolean_t
-zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types)
-{
- zfs_handle_t *zhp;
-
- if (!zfs_validate_name(hdl, path, types, B_FALSE))
- return (B_FALSE);
-
- /*
- * Try to get stats for the dataset, which will tell us if it exists.
- */
- if ((zhp = make_dataset_handle(hdl, path)) != NULL) {
- int ds_type = zhp->zfs_type;
-
- zfs_close(zhp);
- if (types & ds_type)
- return (B_TRUE);
- }
- return (B_FALSE);
-}
-
-/*
- * Given a path to 'target', create all the ancestors between
- * the prefixlen portion of the path, and the target itself.
- * Fail if the initial prefixlen-ancestor does not already exist.
- */
-int
-create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
-{
- zfs_handle_t *h;
- char *cp;
- const char *opname;
-
- /* make sure prefix exists */
- cp = target + prefixlen;
- if (*cp != '/') {
- assert(strchr(cp, '/') == NULL);
- h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
- } else {
- *cp = '\0';
- h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
- *cp = '/';
- }
- if (h == NULL)
- return (-1);
- zfs_close(h);
-
- /*
- * Attempt to create, mount, and share any ancestor filesystems,
- * up to the prefixlen-long one.
- */
- for (cp = target + prefixlen + 1;
- (cp = strchr(cp, '/')) != NULL; *cp = '/', cp++) {
-
- *cp = '\0';
-
- h = make_dataset_handle(hdl, target);
- if (h) {
- /* it already exists, nothing to do here */
- zfs_close(h);
- continue;
- }
-
- if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
- NULL) != 0) {
- opname = dgettext(TEXT_DOMAIN, "create");
- goto ancestorerr;
- }
-
- h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
- if (h == NULL) {
- opname = dgettext(TEXT_DOMAIN, "open");
- goto ancestorerr;
- }
-
- if (zfs_mount(h, NULL, 0) != 0) {
- opname = dgettext(TEXT_DOMAIN, "mount");
- goto ancestorerr;
- }
-
- if (zfs_share(h) != 0) {
- opname = dgettext(TEXT_DOMAIN, "share");
- goto ancestorerr;
- }
-
- zfs_close(h);
- }
-
- return (0);
-
-ancestorerr:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "failed to %s ancestor '%s'"), opname, target);
- return (-1);
-}
-
-/*
- * Creates non-existing ancestors of the given path.
- */
-int
-zfs_create_ancestors(libzfs_handle_t *hdl, const char *path)
-{
- int prefix;
- char *path_copy;
- char errbuf[1024];
- int rc = 0;
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot create '%s'"), path);
-
- /*
- * Check that we are not passing the nesting limit
- * before we start creating any ancestors.
- */
- if (dataset_nestcheck(path) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "maximum name nesting depth exceeded"));
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- }
-
- if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0)
- return (-1);
-
- if ((path_copy = strdup(path)) != NULL) {
- rc = create_parents(hdl, path_copy, prefix);
- free(path_copy);
- }
- if (path_copy == NULL || rc != 0)
- return (-1);
-
- return (0);
-}
-
-/*
- * Create a new filesystem or volume.
- */
-int
-zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
- nvlist_t *props)
-{
- int ret;
- uint64_t size = 0;
- uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
- char errbuf[1024];
- uint64_t zoned;
- enum lzc_dataset_type ost;
- zpool_handle_t *zpool_handle;
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot create '%s'"), path);
-
- /* validate the path, taking care to note the extended error message */
- if (!zfs_validate_name(hdl, path, type, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
-
- if (dataset_nestcheck(path) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "maximum name nesting depth exceeded"));
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- }
-
- /* validate parents exist */
- if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0)
- return (-1);
-
- /*
- * The failure modes when creating a dataset of a different type over
- * one that already exists is a little strange. In particular, if you
- * try to create a dataset on top of an existing dataset, the ioctl()
- * will return ENOENT, not EEXIST. To prevent this from happening, we
- * first try to see if the dataset exists.
- */
- if (zfs_dataset_exists(hdl, path, ZFS_TYPE_DATASET)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "dataset already exists"));
- return (zfs_error(hdl, EZFS_EXISTS, errbuf));
- }
-
- if (type == ZFS_TYPE_VOLUME)
- ost = LZC_DATSET_TYPE_ZVOL;
- else
- ost = LZC_DATSET_TYPE_ZFS;
-
- /* open zpool handle for prop validation */
- char pool_path[ZFS_MAX_DATASET_NAME_LEN];
- (void) strlcpy(pool_path, path, sizeof (pool_path));
-
- /* truncate pool_path at first slash */
- char *p = strchr(pool_path, '/');
- if (p != NULL)
- *p = '\0';
-
- if ((zpool_handle = zpool_open(hdl, pool_path)) == NULL)
- return (-1);
-
- if (props && (props = zfs_valid_proplist(hdl, type, props,
- zoned, NULL, zpool_handle, errbuf)) == 0) {
- zpool_close(zpool_handle);
- return (-1);
- }
- zpool_close(zpool_handle);
-
- if (type == ZFS_TYPE_VOLUME) {
- /*
- * If we are creating a volume, the size and block size must
- * satisfy a few restraints. First, the blocksize must be a
- * valid block size between SPA_{MIN,MAX}BLOCKSIZE. Second, the
- * volsize must be a multiple of the block size, and cannot be
- * zero.
- */
- if (props == NULL || nvlist_lookup_uint64(props,
- zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) {
- nvlist_free(props);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "missing volume size"));
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
- }
-
- if ((ret = nvlist_lookup_uint64(props,
- zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
- &blocksize)) != 0) {
- if (ret == ENOENT) {
- blocksize = zfs_prop_default_numeric(
- ZFS_PROP_VOLBLOCKSIZE);
- } else {
- nvlist_free(props);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "missing volume block size"));
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
- }
- }
-
- if (size == 0) {
- nvlist_free(props);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "volume size cannot be zero"));
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
- }
-
- if (size % blocksize != 0) {
- nvlist_free(props);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "volume size must be a multiple of volume block "
- "size"));
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
- }
- }
-
- /* create the dataset */
- ret = lzc_create(path, ost, props);
- nvlist_free(props);
-
- /* check for failure */
- if (ret != 0) {
- char parent[ZFS_MAX_DATASET_NAME_LEN];
- (void) parent_name(path, parent, sizeof (parent));
-
- switch (errno) {
- case ENOENT:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "no such parent '%s'"), parent);
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
-
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded to set this "
- "property or value"));
- return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
- case ERANGE:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid property value(s) specified"));
- return (zfs_error(hdl, EZFS_BADPROP, errbuf));
-#ifdef _ILP32
- case EOVERFLOW:
- /*
- * This platform can't address a volume this big.
- */
- if (type == ZFS_TYPE_VOLUME)
- return (zfs_error(hdl, EZFS_VOLTOOBIG,
- errbuf));
-#endif
- /* FALLTHROUGH */
- default:
- return (zfs_standard_error(hdl, errno, errbuf));
- }
- }
-
- return (0);
-}
-
-/*
- * Destroys the given dataset. The caller must make sure that the filesystem
- * isn't mounted, and that there are no active dependents. If the file system
- * does not exist this function does nothing.
- */
-int
-zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
-{
- int error;
-
- if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT && defer)
- return (EINVAL);
-
- if (zhp->zfs_type == ZFS_TYPE_BOOKMARK) {
- nvlist_t *nv = fnvlist_alloc();
- fnvlist_add_boolean(nv, zhp->zfs_name);
- error = lzc_destroy_bookmarks(nv, NULL);
- fnvlist_free(nv);
- if (error != 0) {
- return (zfs_standard_error_fmt(zhp->zfs_hdl, error,
- dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
- zhp->zfs_name));
- }
- return (0);
- }
-
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
- nvlist_t *nv = fnvlist_alloc();
- fnvlist_add_boolean(nv, zhp->zfs_name);
- error = lzc_destroy_snaps(nv, defer, NULL);
- fnvlist_free(nv);
- } else {
- error = lzc_destroy(zhp->zfs_name);
- }
-
- if (error != 0 && error != ENOENT) {
- return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
- dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
- zhp->zfs_name));
- }
-
- remove_mountpoint(zhp);
-
- return (0);
-}
-
-struct destroydata {
- nvlist_t *nvl;
- const char *snapname;
-};
-
-static int
-zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
-{
- struct destroydata *dd = arg;
- char name[ZFS_MAX_DATASET_NAME_LEN];
- int rv = 0;
-
- (void) snprintf(name, sizeof (name),
- "%s@%s", zhp->zfs_name, dd->snapname);
-
- if (lzc_exists(name))
- verify(nvlist_add_boolean(dd->nvl, name) == 0);
-
- rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd);
- zfs_close(zhp);
- return (rv);
-}
-
-/*
- * Destroys all snapshots with the given name in zhp & descendants.
- */
-int
-zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
-{
- int ret;
- struct destroydata dd = { 0 };
-
- dd.snapname = snapname;
- verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) == 0);
- (void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd);
-
- if (nvlist_empty(dd.nvl)) {
- ret = zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
- dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
- zhp->zfs_name, snapname);
- } else {
- ret = zfs_destroy_snaps_nvl(zhp->zfs_hdl, dd.nvl, defer);
- }
- nvlist_free(dd.nvl);
- return (ret);
-}
-
-/*
- * Destroys all the snapshots named in the nvlist.
- */
-int
-zfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer)
-{
- int ret;
- nvlist_t *errlist = NULL;
-
- ret = lzc_destroy_snaps(snaps, defer, &errlist);
-
- if (ret == 0) {
- nvlist_free(errlist);
- return (0);
- }
-
- if (nvlist_empty(errlist)) {
- char errbuf[1024];
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot destroy snapshots"));
-
- ret = zfs_standard_error(hdl, ret, errbuf);
- }
- for (nvpair_t *pair = nvlist_next_nvpair(errlist, NULL);
- pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) {
- char errbuf[1024];
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot destroy snapshot %s"),
- nvpair_name(pair));
-
- switch (fnvpair_value_int32(pair)) {
- case EEXIST:
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "snapshot is cloned"));
- ret = zfs_error(hdl, EZFS_EXISTS, errbuf);
- break;
- default:
- ret = zfs_standard_error(hdl, errno, errbuf);
- break;
- }
- }
-
- nvlist_free(errlist);
- return (ret);
-}
-
-/*
- * Clones the given dataset. The target must be of the same type as the source.
- */
-int
-zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
-{
- char parent[ZFS_MAX_DATASET_NAME_LEN];
- int ret;
- char errbuf[1024];
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- uint64_t zoned;
-
- assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot create '%s'"), target);
-
- /* validate the target/clone name */
- if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
-
- /* validate parents exist */
- if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0)
- return (-1);
-
- (void) parent_name(target, parent, sizeof (parent));
-
- /* do the clone */
-
- if (props) {
- zfs_type_t type;
-
- if (ZFS_IS_VOLUME(zhp)) {
- type = ZFS_TYPE_VOLUME;
- } else {
- type = ZFS_TYPE_FILESYSTEM;
- }
- if ((props = zfs_valid_proplist(hdl, type, props, zoned,
- zhp, zhp->zpool_hdl, errbuf)) == NULL)
- return (-1);
- if (zfs_fix_auto_resv(zhp, props) == -1) {
- nvlist_free(props);
- return (-1);
- }
- }
-
- ret = lzc_clone(target, zhp->zfs_name, props);
- nvlist_free(props);
-
- if (ret != 0) {
- switch (errno) {
-
- case ENOENT:
- /*
- * The parent doesn't exist. We should have caught this
- * above, but there may a race condition that has since
- * destroyed the parent.
- *
- * At this point, we don't know whether it's the source
- * that doesn't exist anymore, or whether the target
- * dataset doesn't exist.
- */
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "no such parent '%s'"), parent);
- return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
-
- case EXDEV:
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "source and target pools differ"));
- return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET,
- errbuf));
-
- default:
- return (zfs_standard_error(zhp->zfs_hdl, errno,
- errbuf));
- }
- }
-
- return (ret);
-}
-
-/*
- * Promotes the given clone fs to be the clone parent.
- */
-int
-zfs_promote(zfs_handle_t *zhp)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- char snapname[ZFS_MAX_DATASET_NAME_LEN];
- int ret;
- char errbuf[1024];
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot promote '%s'"), zhp->zfs_name);
-
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "snapshots can not be promoted"));
- return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
- }
-
- if (zhp->zfs_dmustats.dds_origin[0] == '\0') {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "not a cloned filesystem"));
- return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
- }
-
- if (!zfs_validate_name(hdl, zhp->zfs_name, zhp->zfs_type, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
-
- ret = lzc_promote(zhp->zfs_name, snapname, sizeof (snapname));
-
- if (ret != 0) {
- switch (ret) {
- case EEXIST:
- /* There is a conflicting snapshot name. */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "conflicting snapshot '%s' from parent '%s'"),
- snapname, zhp->zfs_dmustats.dds_origin);
- return (zfs_error(hdl, EZFS_EXISTS, errbuf));
-
- default:
- return (zfs_standard_error(hdl, ret, errbuf));
- }
- }
- return (ret);
-}
-
-typedef struct snapdata {
- nvlist_t *sd_nvl;
- const char *sd_snapname;
-} snapdata_t;
-
-static int
-zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
-{
- snapdata_t *sd = arg;
- char name[ZFS_MAX_DATASET_NAME_LEN];
- int rv = 0;
-
- if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) == 0) {
- (void) snprintf(name, sizeof (name),
- "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
-
- fnvlist_add_boolean(sd->sd_nvl, name);
-
- rv = zfs_iter_filesystems(zhp, zfs_snapshot_cb, sd);
- }
- zfs_close(zhp);
-
- return (rv);
-}
-
-int
-zfs_remap_indirects(libzfs_handle_t *hdl, const char *fs)
-{
- int err;
- char errbuf[1024];
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot remap dataset '%s'"), fs);
-
- err = lzc_remap(fs);
-
- if (err != 0) {
- switch (err) {
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded"));
- (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
- break;
- case EINVAL:
- (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
- break;
- default:
- (void) zfs_standard_error(hdl, err, errbuf);
- break;
- }
- }
-
- return (err);
-}
-
-/*
- * Creates snapshots. The keys in the snaps nvlist are the snapshots to be
- * created.
- */
-int
-zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props)
-{
- int ret;
- char errbuf[1024];
- nvpair_t *elem;
- nvlist_t *errors;
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot create snapshots "));
-
- elem = NULL;
- while ((elem = nvlist_next_nvpair(snaps, elem)) != NULL) {
- const char *snapname = nvpair_name(elem);
-
- /* validate the target name */
- if (!zfs_validate_name(hdl, snapname, ZFS_TYPE_SNAPSHOT,
- B_TRUE)) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot create snapshot '%s'"), snapname);
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- }
- }
-
- /*
- * get pool handle for prop validation. assumes all snaps are in the
- * same pool, as does lzc_snapshot (below).
- */
- char pool[ZFS_MAX_DATASET_NAME_LEN];
- elem = nvlist_next_nvpair(snaps, NULL);
- (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
- pool[strcspn(pool, "/@")] = '\0';
- zpool_handle_t *zpool_hdl = zpool_open(hdl, pool);
-
- if (props != NULL &&
- (props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
- props, B_FALSE, NULL, zpool_hdl, errbuf)) == NULL) {
- zpool_close(zpool_hdl);
- return (-1);
- }
- zpool_close(zpool_hdl);
-
- ret = lzc_snapshot(snaps, props, &errors);
-
- if (ret != 0) {
- boolean_t printed = B_FALSE;
- for (elem = nvlist_next_nvpair(errors, NULL);
- elem != NULL;
- elem = nvlist_next_nvpair(errors, elem)) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot create snapshot '%s'"), nvpair_name(elem));
- (void) zfs_standard_error(hdl,
- fnvpair_value_int32(elem), errbuf);
- printed = B_TRUE;
- }
- if (!printed) {
- switch (ret) {
- case EXDEV:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "multiple snapshots of same "
- "fs not allowed"));
- (void) zfs_error(hdl, EZFS_EXISTS, errbuf);
-
- break;
- default:
- (void) zfs_standard_error(hdl, ret, errbuf);
- }
- }
- }
-
- nvlist_free(props);
- nvlist_free(errors);
- return (ret);
-}
-
-int
-zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
- nvlist_t *props)
-{
- int ret;
- snapdata_t sd = { 0 };
- char fsname[ZFS_MAX_DATASET_NAME_LEN];
- char *cp;
- zfs_handle_t *zhp;
- char errbuf[1024];
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot snapshot %s"), path);
-
- if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
-
- (void) strlcpy(fsname, path, sizeof (fsname));
- cp = strchr(fsname, '@');
- *cp = '\0';
- sd.sd_snapname = cp + 1;
-
- if ((zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM |
- ZFS_TYPE_VOLUME)) == NULL) {
- return (-1);
- }
-
- verify(nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) == 0);
- if (recursive) {
- (void) zfs_snapshot_cb(zfs_handle_dup(zhp), &sd);
- } else {
- fnvlist_add_boolean(sd.sd_nvl, path);
- }
-
- ret = zfs_snapshot_nvl(hdl, sd.sd_nvl, props);
- nvlist_free(sd.sd_nvl);
- zfs_close(zhp);
- return (ret);
-}
-
-/*
- * Destroy any more recent snapshots. We invoke this callback on any dependents
- * of the snapshot first. If the 'cb_dependent' member is non-zero, then this
- * is a dependent and we should just destroy it without checking the transaction
- * group.
- */
-typedef struct rollback_data {
- const char *cb_target; /* the snapshot */
- uint64_t cb_create; /* creation time reference */
- boolean_t cb_error;
- boolean_t cb_force;
-} rollback_data_t;
-
-static int
-rollback_destroy_dependent(zfs_handle_t *zhp, void *data)
-{
- rollback_data_t *cbp = data;
- prop_changelist_t *clp;
-
- /* We must destroy this clone; first unmount it */
- clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
- cbp->cb_force ? MS_FORCE: 0);
- if (clp == NULL || changelist_prefix(clp) != 0) {
- cbp->cb_error = B_TRUE;
- zfs_close(zhp);
- return (0);
- }
- if (zfs_destroy(zhp, B_FALSE) != 0)
- cbp->cb_error = B_TRUE;
- else
- changelist_remove(clp, zhp->zfs_name);
- (void) changelist_postfix(clp);
- changelist_free(clp);
-
- zfs_close(zhp);
- return (0);
-}
-
-static int
-rollback_destroy(zfs_handle_t *zhp, void *data)
-{
- rollback_data_t *cbp = data;
-
- if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > cbp->cb_create) {
- cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
- rollback_destroy_dependent, cbp);
-
- cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
- }
-
- zfs_close(zhp);
- return (0);
-}
-
-/*
- * Given a dataset, rollback to a specific snapshot, discarding any
- * data changes since then and making it the active dataset.
- *
- * Any snapshots and bookmarks more recent than the target are
- * destroyed, along with their dependents (i.e. clones).
- */
-int
-zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
-{
- rollback_data_t cb = { 0 };
- int err;
- boolean_t restore_resv = 0;
- uint64_t min_txg = 0, old_volsize = 0, new_volsize;
- zfs_prop_t resv_prop;
-
- assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||
- zhp->zfs_type == ZFS_TYPE_VOLUME);
-
- /*
- * Destroy all recent snapshots and their dependents.
- */
- cb.cb_force = force;
- cb.cb_target = snap->zfs_name;
- cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
-
- if (cb.cb_create > 0)
- min_txg = cb.cb_create;
-
- (void) zfs_iter_snapshots(zhp, B_FALSE, rollback_destroy, &cb,
- min_txg, 0);
-
- (void) zfs_iter_bookmarks(zhp, rollback_destroy, &cb);
-
- if (cb.cb_error)
- return (-1);
-
- /*
- * Now that we have verified that the snapshot is the latest,
- * rollback to the given snapshot.
- */
-
- if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
- if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
- return (-1);
- old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
- restore_resv =
- (old_volsize == zfs_prop_get_int(zhp, resv_prop));
- }
-
- /*
- * Pass both the filesystem and the wanted snapshot names,
- * we would get an error back if the snapshot is destroyed or
- * a new snapshot is created before this request is processed.
- */
- err = lzc_rollback_to(zhp->zfs_name, snap->zfs_name);
- if (err != 0) {
- char errbuf[1024];
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
- zhp->zfs_name);
- switch (err) {
- case EEXIST:
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "there is a snapshot or bookmark more recent "
- "than '%s'"), snap->zfs_name);
- (void) zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf);
- break;
- case ESRCH:
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "'%s' is not found among snapshots of '%s'"),
- snap->zfs_name, zhp->zfs_name);
- (void) zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf);
- break;
- case EINVAL:
- (void) zfs_error(zhp->zfs_hdl, EZFS_BADTYPE, errbuf);
- break;
- default:
- (void) zfs_standard_error(zhp->zfs_hdl, err, errbuf);
- }
- return (err);
- }
-
- /*
- * For volumes, if the pre-rollback volsize matched the pre-
- * rollback reservation and the volsize has changed then set
- * the reservation property to the post-rollback volsize.
- * Make a new handle since the rollback closed the dataset.
- */
- if ((zhp->zfs_type == ZFS_TYPE_VOLUME) &&
- (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) {
- if (restore_resv) {
- new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
- if (old_volsize != new_volsize)
- err = zfs_prop_set_int(zhp, resv_prop,
- new_volsize);
- }
- zfs_close(zhp);
- }
- return (err);
-}
-
-/*
- * Renames the given dataset.
- */
-int
-zfs_rename(zfs_handle_t *zhp, const char *source, const char *target,
- renameflags_t flags)
-{
- int ret = 0;
- zfs_cmd_t zc = { 0 };
- char *delim;
- prop_changelist_t *cl = NULL;
- zfs_handle_t *zhrp = NULL;
- char *parentname = NULL;
- char parent[ZFS_MAX_DATASET_NAME_LEN];
- char property[ZFS_MAXPROPLEN];
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- char errbuf[1024];
-
- /* if we have the same exact name, just return success */
- if (strcmp(zhp->zfs_name, target) == 0)
- return (0);
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot rename to '%s'"), target);
-
- if (source != NULL) {
- /*
- * This is recursive snapshots rename, put snapshot name
- * (that might not exist) into zfs_name.
- */
- assert(flags.recurse);
-
- (void) strlcat(zhp->zfs_name, "@", sizeof(zhp->zfs_name));
- (void) strlcat(zhp->zfs_name, source, sizeof(zhp->zfs_name));
- zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
- }
-
- /* make sure source name is valid */
- if (!zfs_validate_name(hdl, zhp->zfs_name, zhp->zfs_type, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
-
- /*
- * Make sure the target name is valid
- */
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT ||
- zhp->zfs_type == ZFS_TYPE_BOOKMARK) {
- const char sep = zhp->zfs_type == ZFS_TYPE_SNAPSHOT ? '@' : '#';
-
- if ((strchr(target, sep) == NULL) || *target == sep) {
- /*
- * Snapshot target name is abbreviated,
- * reconstruct full dataset name
- */
- (void) strlcpy(parent, zhp->zfs_name, sizeof (parent));
- delim = strchr(parent, sep);
- if (strchr(target, sep) == NULL)
- *(++delim) = '\0';
- else
- *delim = '\0';
- (void) strlcat(parent, target, sizeof (parent));
- target = parent;
- } else {
- /*
- * Make sure we're renaming within the same dataset.
- */
- delim = strchr(target, sep);
- if (strncmp(zhp->zfs_name, target, delim - target)
- != 0 || zhp->zfs_name[delim - target] != sep) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "%s must be part of same dataset"),
- zhp->zfs_type == ZFS_TYPE_SNAPSHOT ?
- "snapshots" : "bookmarks");
- return (zfs_error(hdl, EZFS_CROSSTARGET,
- errbuf));
- }
- }
-
- if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- } else {
- if (flags.recurse) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "recursive rename must be a snapshot"));
- return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
- }
-
- if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
-
- /* validate parents */
- if (check_parents(hdl, target, NULL, B_FALSE, NULL) != 0)
- return (-1);
-
- /* make sure we're in the same pool */
- verify((delim = strchr(target, '/')) != NULL);
- if (strncmp(zhp->zfs_name, target, delim - target) != 0 ||
- zhp->zfs_name[delim - target] != '/') {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "datasets must be within same pool"));
- return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
- }
-
- /* new name cannot be a child of the current dataset name */
- if (is_descendant(zhp->zfs_name, target)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "New dataset name cannot be a descendant of "
- "current dataset name"));
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- }
- }
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name);
-
- if (getzoneid() == GLOBAL_ZONEID &&
- zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "dataset is used in a non-global zone"));
- return (zfs_error(hdl, EZFS_ZONED, errbuf));
- }
-
- /*
- * Avoid unmounting file systems with mountpoint property set to
- * 'legacy' or 'none' even if -u option is not given.
- */
- if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
- !flags.recurse && !flags.nounmount &&
- zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, property,
- sizeof (property), NULL, NULL, 0, B_FALSE) == 0 &&
- (strcmp(property, "legacy") == 0 ||
- strcmp(property, "none") == 0)) {
- flags.nounmount = B_TRUE;
- }
- if (flags.recurse) {
- parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
- if (parentname == NULL) {
- ret = -1;
- goto error;
- }
- delim = strchr(parentname, '@');
- *delim = '\0';
- zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET);
- if (zhrp == NULL) {
- ret = -1;
- goto error;
- }
- } else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT &&
- zhp->zfs_type != ZFS_TYPE_BOOKMARK) {
- if ((cl = changelist_gather(zhp, ZFS_PROP_NAME,
- flags.nounmount ? CL_GATHER_DONT_UNMOUNT : 0,
- flags.forceunmount ? MS_FORCE : 0)) == NULL) {
- return (-1);
- }
-
- if (changelist_haszonedchild(cl)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "child dataset with inherited mountpoint is used "
- "in a non-global zone"));
- (void) zfs_error(hdl, EZFS_ZONED, errbuf);
- ret = -1;
- goto error;
- }
-
- if ((ret = changelist_prefix(cl)) != 0)
- goto error;
- }
-
- if (ZFS_IS_VOLUME(zhp))
- zc.zc_objset_type = DMU_OST_ZVOL;
- else
- zc.zc_objset_type = DMU_OST_ZFS;
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));
-
- zc.zc_cookie = flags.recurse ? 1 : 0;
- if (flags.nounmount)
- zc.zc_cookie |= 2;
-
- if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) {
- /*
- * if it was recursive, the one that actually failed will
- * be in zc.zc_name
- */
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot rename '%s'"), zc.zc_name);
-
- if (flags.recurse && errno == EEXIST) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "a child dataset already has a snapshot "
- "with the new name"));
- (void) zfs_error(hdl, EZFS_EXISTS, errbuf);
- } else if (errno == EINVAL) {
- (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
- } else {
- (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
- }
-
- /*
- * On failure, we still want to remount any filesystems that
- * were previously mounted, so we don't alter the system state.
- */
- if (cl != NULL)
- (void) changelist_postfix(cl);
- } else {
- if (cl != NULL) {
- changelist_rename(cl, zfs_get_name(zhp), target);
- ret = changelist_postfix(cl);
- }
- }
-
-error:
- if (parentname != NULL) {
- free(parentname);
- }
- if (zhrp != NULL) {
- zfs_close(zhrp);
- }
- if (cl != NULL) {
- changelist_free(cl);
- }
- return (ret);
-}
-
-nvlist_t *
-zfs_get_user_props(zfs_handle_t *zhp)
-{
- return (zhp->zfs_user_props);
-}
-
-nvlist_t *
-zfs_get_recvd_props(zfs_handle_t *zhp)
-{
- if (zhp->zfs_recvd_props == NULL)
- if (get_recvd_props_ioctl(zhp) != 0)
- return (NULL);
- return (zhp->zfs_recvd_props);
-}
-
-/*
- * This function is used by 'zfs list' to determine the exact set of columns to
- * display, and their maximum widths. This does two main things:
- *
- * - If this is a list of all properties, then expand the list to include
- * all native properties, and set a flag so that for each dataset we look
- * for new unique user properties and add them to the list.
- *
- * - For non fixed-width properties, keep track of the maximum width seen
- * so that we can size the column appropriately. If the user has
- * requested received property values, we also need to compute the width
- * of the RECEIVED column.
- */
-int
-zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received,
- boolean_t literal)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- zprop_list_t *entry;
- zprop_list_t **last, **start;
- nvlist_t *userprops, *propval;
- nvpair_t *elem;
- char *strval;
- char buf[ZFS_MAXPROPLEN];
-
- if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0)
- return (-1);
-
- userprops = zfs_get_user_props(zhp);
-
- entry = *plp;
- if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) {
- /*
- * Go through and add any user properties as necessary. We
- * start by incrementing our list pointer to the first
- * non-native property.
- */
- start = plp;
- while (*start != NULL) {
- if ((*start)->pl_prop == ZPROP_INVAL)
- break;
- start = &(*start)->pl_next;
- }
-
- elem = NULL;
- while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) {
- /*
- * See if we've already found this property in our list.
- */
- for (last = start; *last != NULL;
- last = &(*last)->pl_next) {
- if (strcmp((*last)->pl_user_prop,
- nvpair_name(elem)) == 0)
- break;
- }
-
- if (*last == NULL) {
- if ((entry = zfs_alloc(hdl,
- sizeof (zprop_list_t))) == NULL ||
- ((entry->pl_user_prop = zfs_strdup(hdl,
- nvpair_name(elem)))) == NULL) {
- free(entry);
- return (-1);
- }
-
- entry->pl_prop = ZPROP_INVAL;
- entry->pl_width = strlen(nvpair_name(elem));
- entry->pl_all = B_TRUE;
- *last = entry;
- }
- }
- }
-
- /*
- * Now go through and check the width of any non-fixed columns
- */
- for (entry = *plp; entry != NULL; entry = entry->pl_next) {
- if (entry->pl_fixed && !literal)
- continue;
-
- if (entry->pl_prop != ZPROP_INVAL) {
- if (zfs_prop_get(zhp, entry->pl_prop,
- buf, sizeof (buf), NULL, NULL, 0, literal) == 0) {
- if (strlen(buf) > entry->pl_width)
- entry->pl_width = strlen(buf);
- }
- if (received && zfs_prop_get_recvd(zhp,
- zfs_prop_to_name(entry->pl_prop),
- buf, sizeof (buf), literal) == 0)
- if (strlen(buf) > entry->pl_recvd_width)
- entry->pl_recvd_width = strlen(buf);
- } else {
- if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop,
- &propval) == 0) {
- verify(nvlist_lookup_string(propval,
- ZPROP_VALUE, &strval) == 0);
- if (strlen(strval) > entry->pl_width)
- entry->pl_width = strlen(strval);
- }
- if (received && zfs_prop_get_recvd(zhp,
- entry->pl_user_prop,
- buf, sizeof (buf), literal) == 0)
- if (strlen(buf) > entry->pl_recvd_width)
- entry->pl_recvd_width = strlen(buf);
- }
- }
-
- return (0);
-}
-
-int
-zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
- char *resource, void *export, void *sharetab,
- int sharemax, zfs_share_op_t operation)
-{
- zfs_cmd_t zc = { 0 };
- int error;
-
- (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
- if (resource)
- (void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string));
- zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab;
- zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export;
- zc.zc_share.z_sharetype = operation;
- zc.zc_share.z_sharemax = sharemax;
- error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc);
- return (error);
-}
-
-void
-zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
-{
- nvpair_t *curr;
-
- /*
- * Keep a reference to the props-table against which we prune the
- * properties.
- */
- zhp->zfs_props_table = props;
-
- curr = nvlist_next_nvpair(zhp->zfs_props, NULL);
-
- while (curr) {
- zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr));
- nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr);
-
- /*
- * User properties will result in ZPROP_INVAL, and since we
- * only know how to prune standard ZFS properties, we always
- * leave these in the list. This can also happen if we
- * encounter an unknown DSL property (when running older
- * software, for example).
- */
- if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE)
- (void) nvlist_remove(zhp->zfs_props,
- nvpair_name(curr), nvpair_type(curr));
- curr = next;
- }
-}
-
-#ifdef illumos
-static int
-zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path,
- zfs_smb_acl_op_t cmd, char *resource1, char *resource2)
-{
- zfs_cmd_t zc = { 0 };
- nvlist_t *nvlist = NULL;
- int error;
-
- (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
- zc.zc_cookie = (uint64_t)cmd;
-
- if (cmd == ZFS_SMB_ACL_RENAME) {
- if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) {
- (void) no_memory(hdl);
- return (0);
- }
- }
-
- switch (cmd) {
- case ZFS_SMB_ACL_ADD:
- case ZFS_SMB_ACL_REMOVE:
- (void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string));
- break;
- case ZFS_SMB_ACL_RENAME:
- if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC,
- resource1) != 0) {
- (void) no_memory(hdl);
- return (-1);
- }
- if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET,
- resource2) != 0) {
- (void) no_memory(hdl);
- return (-1);
- }
- if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) {
- nvlist_free(nvlist);
- return (-1);
- }
- break;
- case ZFS_SMB_ACL_PURGE:
- break;
- default:
- return (-1);
- }
- error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc);
- nvlist_free(nvlist);
- return (error);
-}
-
-int
-zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset,
- char *path, char *resource)
-{
- return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD,
- resource, NULL));
-}
-
-int
-zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset,
- char *path, char *resource)
-{
- return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE,
- resource, NULL));
-}
-
-int
-zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path)
-{
- return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE,
- NULL, NULL));
-}
-
-int
-zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path,
- char *oldname, char *newname)
-{
- return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME,
- oldname, newname));
-}
-#endif /* illumos */
-
-int
-zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
- zfs_userspace_cb_t func, void *arg)
-{
- zfs_cmd_t zc = { 0 };
- zfs_useracct_t buf[100];
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- int ret;
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- zc.zc_objset_type = type;
- zc.zc_nvlist_dst = (uintptr_t)buf;
-
- for (;;) {
- zfs_useracct_t *zua = buf;
-
- zc.zc_nvlist_dst_size = sizeof (buf);
- if (zfs_ioctl(hdl, ZFS_IOC_USERSPACE_MANY, &zc) != 0) {
- char errbuf[1024];
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot get used/quota for %s"), zc.zc_name);
- return (zfs_standard_error_fmt(hdl, errno, errbuf));
- }
- if (zc.zc_nvlist_dst_size == 0)
- break;
-
- while (zc.zc_nvlist_dst_size > 0) {
- if ((ret = func(arg, zua->zu_domain, zua->zu_rid,
- zua->zu_space)) != 0)
- return (ret);
- zua++;
- zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
- }
- }
-
- return (0);
-}
-
-struct holdarg {
- nvlist_t *nvl;
- const char *snapname;
- const char *tag;
- boolean_t recursive;
- int error;
-};
-
-static int
-zfs_hold_one(zfs_handle_t *zhp, void *arg)
-{
- struct holdarg *ha = arg;
- char name[ZFS_MAX_DATASET_NAME_LEN];
- int rv = 0;
-
- (void) snprintf(name, sizeof (name),
- "%s@%s", zhp->zfs_name, ha->snapname);
-
- if (lzc_exists(name))
- fnvlist_add_string(ha->nvl, name, ha->tag);
-
- if (ha->recursive)
- rv = zfs_iter_filesystems(zhp, zfs_hold_one, ha);
- zfs_close(zhp);
- return (rv);
-}
-
-int
-zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
- boolean_t recursive, int cleanup_fd)
-{
- int ret;
- struct holdarg ha;
-
- ha.nvl = fnvlist_alloc();
- ha.snapname = snapname;
- ha.tag = tag;
- ha.recursive = recursive;
- (void) zfs_hold_one(zfs_handle_dup(zhp), &ha);
-
- if (nvlist_empty(ha.nvl)) {
- char errbuf[1024];
-
- fnvlist_free(ha.nvl);
- ret = ENOENT;
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot hold snapshot '%s@%s'"),
- zhp->zfs_name, snapname);
- (void) zfs_standard_error(zhp->zfs_hdl, ret, errbuf);
- return (ret);
- }
-
- ret = zfs_hold_nvl(zhp, cleanup_fd, ha.nvl);
- fnvlist_free(ha.nvl);
-
- return (ret);
-}
-
-int
-zfs_hold_nvl(zfs_handle_t *zhp, int cleanup_fd, nvlist_t *holds)
-{
- int ret;
- nvlist_t *errors;
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- char errbuf[1024];
- nvpair_t *elem;
-
- errors = NULL;
- ret = lzc_hold(holds, cleanup_fd, &errors);
-
- if (ret == 0) {
- /* There may be errors even in the success case. */
- fnvlist_free(errors);
- return (0);
- }
-
- if (nvlist_empty(errors)) {
- /* no hold-specific errors */
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot hold"));
- switch (ret) {
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded"));
- (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
- break;
- case EINVAL:
- (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
- break;
- default:
- (void) zfs_standard_error(hdl, ret, errbuf);
- }
- }
-
- for (elem = nvlist_next_nvpair(errors, NULL);
- elem != NULL;
- elem = nvlist_next_nvpair(errors, elem)) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot hold snapshot '%s'"), nvpair_name(elem));
- switch (fnvpair_value_int32(elem)) {
- case E2BIG:
- /*
- * Temporary tags wind up having the ds object id
- * prepended. So even if we passed the length check
- * above, it's still possible for the tag to wind
- * up being slightly too long.
- */
- (void) zfs_error(hdl, EZFS_TAGTOOLONG, errbuf);
- break;
- case EINVAL:
- (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
- break;
- case EEXIST:
- (void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf);
- break;
- default:
- (void) zfs_standard_error(hdl,
- fnvpair_value_int32(elem), errbuf);
- }
- }
-
- fnvlist_free(errors);
- return (ret);
-}
-
-static int
-zfs_release_one(zfs_handle_t *zhp, void *arg)
-{
- struct holdarg *ha = arg;
- char name[ZFS_MAX_DATASET_NAME_LEN];
- int rv = 0;
- nvlist_t *existing_holds;
-
- (void) snprintf(name, sizeof (name),
- "%s@%s", zhp->zfs_name, ha->snapname);
-
- if (lzc_get_holds(name, &existing_holds) != 0) {
- ha->error = ENOENT;
- } else if (!nvlist_exists(existing_holds, ha->tag)) {
- ha->error = ESRCH;
- } else {
- nvlist_t *torelease = fnvlist_alloc();
- fnvlist_add_boolean(torelease, ha->tag);
- fnvlist_add_nvlist(ha->nvl, name, torelease);
- fnvlist_free(torelease);
- }
-
- if (ha->recursive)
- rv = zfs_iter_filesystems(zhp, zfs_release_one, ha);
- zfs_close(zhp);
- return (rv);
-}
-
-int
-zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
- boolean_t recursive)
-{
- int ret;
- struct holdarg ha;
- nvlist_t *errors = NULL;
- nvpair_t *elem;
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- char errbuf[1024];
-
- ha.nvl = fnvlist_alloc();
- ha.snapname = snapname;
- ha.tag = tag;
- ha.recursive = recursive;
- ha.error = 0;
- (void) zfs_release_one(zfs_handle_dup(zhp), &ha);
-
- if (nvlist_empty(ha.nvl)) {
- fnvlist_free(ha.nvl);
- ret = ha.error;
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot release hold from snapshot '%s@%s'"),
- zhp->zfs_name, snapname);
- if (ret == ESRCH) {
- (void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf);
- } else {
- (void) zfs_standard_error(hdl, ret, errbuf);
- }
- return (ret);
- }
-
- ret = lzc_release(ha.nvl, &errors);
- fnvlist_free(ha.nvl);
-
- if (ret == 0) {
- /* There may be errors even in the success case. */
- fnvlist_free(errors);
- return (0);
- }
-
- if (nvlist_empty(errors)) {
- /* no hold-specific errors */
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot release"));
- switch (errno) {
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded"));
- (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
- break;
- default:
- (void) zfs_standard_error_fmt(hdl, errno, errbuf);
- }
- }
-
- for (elem = nvlist_next_nvpair(errors, NULL);
- elem != NULL;
- elem = nvlist_next_nvpair(errors, elem)) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot release hold from snapshot '%s'"),
- nvpair_name(elem));
- switch (fnvpair_value_int32(elem)) {
- case ESRCH:
- (void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf);
- break;
- case EINVAL:
- (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
- break;
- default:
- (void) zfs_standard_error_fmt(hdl,
- fnvpair_value_int32(elem), errbuf);
- }
- }
-
- fnvlist_free(errors);
- return (ret);
-}
-
-int
-zfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- int nvsz = 2048;
- void *nvbuf;
- int err = 0;
- char errbuf[1024];
-
- assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
- zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
-
-tryagain:
-
- nvbuf = malloc(nvsz);
- if (nvbuf == NULL) {
- err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
- goto out;
- }
-
- zc.zc_nvlist_dst_size = nvsz;
- zc.zc_nvlist_dst = (uintptr_t)nvbuf;
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"),
- zc.zc_name);
- switch (errno) {
- case ENOMEM:
- free(nvbuf);
- nvsz = zc.zc_nvlist_dst_size;
- goto tryagain;
-
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded"));
- err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
- break;
- case EINVAL:
- err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
- break;
- case ENOENT:
- err = zfs_error(hdl, EZFS_NOENT, errbuf);
- break;
- default:
- err = zfs_standard_error_fmt(hdl, errno, errbuf);
- break;
- }
- } else {
- /* success */
- int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
- if (rc) {
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(
- TEXT_DOMAIN, "cannot get permissions on '%s'"),
- zc.zc_name);
- err = zfs_standard_error_fmt(hdl, rc, errbuf);
- }
- }
-
- free(nvbuf);
-out:
- return (err);
-}
-
-int
-zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- char *nvbuf;
- char errbuf[1024];
- size_t nvsz;
- int err;
-
- assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
- zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
-
- err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);
- assert(err == 0);
-
- nvbuf = malloc(nvsz);
-
- err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);
- assert(err == 0);
-
- zc.zc_nvlist_src_size = nvsz;
- zc.zc_nvlist_src = (uintptr_t)nvbuf;
- zc.zc_perm_action = un;
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- if (zfs_ioctl(hdl, ZFS_IOC_SET_FSACL, &zc) != 0) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot set permissions on '%s'"),
- zc.zc_name);
- switch (errno) {
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded"));
- err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
- break;
- case EINVAL:
- err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
- break;
- case ENOENT:
- err = zfs_error(hdl, EZFS_NOENT, errbuf);
- break;
- default:
- err = zfs_standard_error_fmt(hdl, errno, errbuf);
- break;
- }
- }
-
- free(nvbuf);
-
- return (err);
-}
-
-int
-zfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)
-{
- int err;
- char errbuf[1024];
-
- err = lzc_get_holds(zhp->zfs_name, nvl);
-
- if (err != 0) {
- libzfs_handle_t *hdl = zhp->zfs_hdl;
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
- zhp->zfs_name);
- switch (err) {
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded"));
- err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
- break;
- case EINVAL:
- err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
- break;
- case ENOENT:
- err = zfs_error(hdl, EZFS_NOENT, errbuf);
- break;
- default:
- err = zfs_standard_error_fmt(hdl, errno, errbuf);
- break;
- }
- }
-
- return (err);
-}
-
-/*
- * Convert the zvol's volume size to an appropriate reservation.
- * Note: If this routine is updated, it is necessary to update the ZFS test
- * suite's shell version in reservation.kshlib.
- */
-uint64_t
-zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
-{
- uint64_t numdb;
- uint64_t nblocks, volblocksize;
- int ncopies;
- char *strval;
-
- if (nvlist_lookup_string(props,
- zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0)
- ncopies = atoi(strval);
- else
- ncopies = 1;
- if (nvlist_lookup_uint64(props,
- zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
- &volblocksize) != 0)
- volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
- nblocks = volsize/volblocksize;
- /* start with metadnode L0-L6 */
- numdb = 7;
- /* calculate number of indirects */
- while (nblocks > 1) {
- nblocks += DNODES_PER_LEVEL - 1;
- nblocks /= DNODES_PER_LEVEL;
- numdb += nblocks;
- }
- numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1);
- volsize *= ncopies;
- /*
- * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't
- * compressed, but in practice they compress down to about
- * 1100 bytes
- */
- numdb *= 1ULL << DN_MAX_INDBLKSHIFT;
- volsize += numdb;
- return (volsize);
-}
-
-/*
- * Attach/detach the given filesystem to/from the given jail.
- */
-int
-zfs_jail(zfs_handle_t *zhp, int jailid, int attach)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- zfs_cmd_t zc = { 0 };
- char errbuf[1024];
- unsigned long cmd;
- int ret;
-
- if (attach) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name);
- } else {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot unjail '%s'"), zhp->zfs_name);
- }
-
- switch (zhp->zfs_type) {
- case ZFS_TYPE_VOLUME:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "volumes can not be jailed"));
- return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
- case ZFS_TYPE_SNAPSHOT:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "snapshots can not be jailed"));
- return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
- }
- assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- zc.zc_objset_type = DMU_OST_ZFS;
- zc.zc_jailid = jailid;
-
- cmd = attach ? ZFS_IOC_JAIL : ZFS_IOC_UNJAIL;
- if ((ret = ioctl(hdl->libzfs_fd, cmd, &zc)) != 0)
- zfs_standard_error(hdl, errno, errbuf);
-
- return (ret);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_diff.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_diff.c
deleted file mode 100644
index db132190154c2..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_diff.c
+++ /dev/null
@@ -1,834 +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) 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2015, 2018 by Delphix. All rights reserved.
- * Copyright 2016 Joyent, Inc.
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
- */
-
-/*
- * zfs diff support
- */
-#include <ctype.h>
-#include <errno.h>
-#include <libintl.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <pthread.h>
-#include <sys/zfs_ioctl.h>
-#include <libzfs.h>
-#include "libzfs_impl.h"
-
-#define ZDIFF_SNAPDIR "/.zfs/snapshot/"
-#define ZDIFF_SHARESDIR "/.zfs/shares/"
-#define ZDIFF_PREFIX "zfs-diff-%d"
-
-#define ZDIFF_ADDED '+'
-#define ZDIFF_MODIFIED 'M'
-#define ZDIFF_REMOVED '-'
-#define ZDIFF_RENAMED 'R'
-
-typedef struct differ_info {
- zfs_handle_t *zhp;
- char *fromsnap;
- char *frommnt;
- char *tosnap;
- char *tomnt;
- char *ds;
- char *dsmnt;
- char *tmpsnap;
- char errbuf[1024];
- boolean_t isclone;
- boolean_t scripted;
- boolean_t classify;
- boolean_t timestamped;
- uint64_t shares;
- int zerr;
- int cleanupfd;
- int outputfd;
- int datafd;
-} differ_info_t;
-
-/*
- * Given a {dsname, object id}, get the object path
- */
-static int
-get_stats_for_obj(differ_info_t *di, const char *dsname, uint64_t obj,
- char *pn, int maxlen, zfs_stat_t *sb)
-{
- zfs_cmd_t zc = { 0 };
- int error;
-
- (void) strlcpy(zc.zc_name, dsname, sizeof (zc.zc_name));
- zc.zc_obj = obj;
-
- errno = 0;
- error = ioctl(di->zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJ_TO_STATS, &zc);
- di->zerr = errno;
-
- /* we can get stats even if we failed to get a path */
- (void) memcpy(sb, &zc.zc_stat, sizeof (zfs_stat_t));
- if (error == 0) {
- ASSERT(di->zerr == 0);
- (void) strlcpy(pn, zc.zc_value, maxlen);
- return (0);
- }
-
- if (di->zerr == ESTALE) {
- (void) snprintf(pn, maxlen, "(on_delete_queue)");
- return (0);
- } else if (di->zerr == EPERM) {
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "The sys_config privilege or diff delegated permission "
- "is needed\nto discover path names"));
- return (-1);
- } else {
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "Unable to determine path or stats for "
- "object %jd in %s"), (uintmax_t)obj, dsname);
- return (-1);
- }
-}
-
-/*
- * stream_bytes
- *
- * Prints a file name out a character at a time. If the character is
- * not in the range of what we consider "printable" ASCII, display it
- * as an escaped 3-digit octal value. ASCII values less than a space
- * are all control characters and we declare the upper end as the
- * DELete character. This also is the last 7-bit ASCII character.
- * We choose to treat all 8-bit ASCII as not printable for this
- * application.
- */
-static void
-stream_bytes(FILE *fp, const char *string)
-{
- char c;
-
- while ((c = *string++) != '\0') {
- if (c > ' ' && c != '\\' && c < '\177') {
- (void) fprintf(fp, "%c", c);
- } else {
- (void) fprintf(fp, "\\%03o", (uint8_t)c);
- }
- }
-}
-
-static void
-print_what(FILE *fp, mode_t what)
-{
- char symbol;
-
- switch (what & S_IFMT) {
- case S_IFBLK:
- symbol = 'B';
- break;
- case S_IFCHR:
- symbol = 'C';
- break;
- case S_IFDIR:
- symbol = '/';
- break;
-#ifdef S_IFDOOR
- case S_IFDOOR:
- symbol = '>';
- break;
-#endif
- case S_IFIFO:
- symbol = '|';
- break;
- case S_IFLNK:
- symbol = '@';
- break;
-#ifdef S_IFPORT
- case S_IFPORT:
- symbol = 'P';
- break;
-#endif
- case S_IFSOCK:
- symbol = '=';
- break;
- case S_IFREG:
- symbol = 'F';
- break;
- default:
- symbol = '?';
- break;
- }
- (void) fprintf(fp, "%c", symbol);
-}
-
-static void
-print_cmn(FILE *fp, differ_info_t *di, const char *file)
-{
- stream_bytes(fp, di->dsmnt);
- stream_bytes(fp, file);
-}
-
-static void
-print_rename(FILE *fp, differ_info_t *di, const char *old, const char *new,
- zfs_stat_t *isb)
-{
- if (di->timestamped)
- (void) fprintf(fp, "%10lld.%09lld\t",
- (longlong_t)isb->zs_ctime[0],
- (longlong_t)isb->zs_ctime[1]);
- (void) fprintf(fp, "%c\t", ZDIFF_RENAMED);
- if (di->classify) {
- print_what(fp, isb->zs_mode);
- (void) fprintf(fp, "\t");
- }
- print_cmn(fp, di, old);
- if (di->scripted)
- (void) fprintf(fp, "\t");
- else
- (void) fprintf(fp, " -> ");
- print_cmn(fp, di, new);
- (void) fprintf(fp, "\n");
-}
-
-static void
-print_link_change(FILE *fp, differ_info_t *di, int delta, const char *file,
- zfs_stat_t *isb)
-{
- if (di->timestamped)
- (void) fprintf(fp, "%10lld.%09lld\t",
- (longlong_t)isb->zs_ctime[0],
- (longlong_t)isb->zs_ctime[1]);
- (void) fprintf(fp, "%c\t", ZDIFF_MODIFIED);
- if (di->classify) {
- print_what(fp, isb->zs_mode);
- (void) fprintf(fp, "\t");
- }
- print_cmn(fp, di, file);
- (void) fprintf(fp, "\t(%+d)", delta);
- (void) fprintf(fp, "\n");
-}
-
-static void
-print_file(FILE *fp, differ_info_t *di, char type, const char *file,
- zfs_stat_t *isb)
-{
- if (di->timestamped)
- (void) fprintf(fp, "%10lld.%09lld\t",
- (longlong_t)isb->zs_ctime[0],
- (longlong_t)isb->zs_ctime[1]);
- (void) fprintf(fp, "%c\t", type);
- if (di->classify) {
- print_what(fp, isb->zs_mode);
- (void) fprintf(fp, "\t");
- }
- print_cmn(fp, di, file);
- (void) fprintf(fp, "\n");
-}
-
-static int
-write_inuse_diffs_one(FILE *fp, differ_info_t *di, uint64_t dobj)
-{
- struct zfs_stat fsb, tsb;
- mode_t fmode, tmode;
- char fobjname[MAXPATHLEN], tobjname[MAXPATHLEN];
- int fobjerr, tobjerr;
- int change;
-
- if (dobj == di->shares)
- return (0);
-
- /*
- * Check the from and to snapshots for info on the object. If
- * we get ENOENT, then the object just didn't exist in that
- * snapshot. If we get ENOTSUP, then we tried to get
- * info on a non-ZPL object, which we don't care about anyway.
- */
- fobjerr = get_stats_for_obj(di, di->fromsnap, dobj, fobjname,
- MAXPATHLEN, &fsb);
- if (fobjerr && di->zerr != ENOENT && di->zerr != ENOTSUP)
- return (-1);
-
- tobjerr = get_stats_for_obj(di, di->tosnap, dobj, tobjname,
- MAXPATHLEN, &tsb);
- if (tobjerr && di->zerr != ENOENT && di->zerr != ENOTSUP)
- return (-1);
-
- /*
- * Unallocated object sharing the same meta dnode block
- */
- if (fobjerr && tobjerr) {
- ASSERT(di->zerr == ENOENT || di->zerr == ENOTSUP);
- di->zerr = 0;
- return (0);
- }
-
- di->zerr = 0; /* negate get_stats_for_obj() from side that failed */
- fmode = fsb.zs_mode & S_IFMT;
- tmode = tsb.zs_mode & S_IFMT;
- if (fmode == S_IFDIR || tmode == S_IFDIR || fsb.zs_links == 0 ||
- tsb.zs_links == 0)
- change = 0;
- else
- change = tsb.zs_links - fsb.zs_links;
-
- if (fobjerr) {
- if (change) {
- print_link_change(fp, di, change, tobjname, &tsb);
- return (0);
- }
- print_file(fp, di, ZDIFF_ADDED, tobjname, &tsb);
- return (0);
- } else if (tobjerr) {
- if (change) {
- print_link_change(fp, di, change, fobjname, &fsb);
- return (0);
- }
- print_file(fp, di, ZDIFF_REMOVED, fobjname, &fsb);
- return (0);
- }
-
- if (fmode != tmode && fsb.zs_gen == tsb.zs_gen)
- tsb.zs_gen++; /* Force a generational difference */
-
- /* Simple modification or no change */
- if (fsb.zs_gen == tsb.zs_gen) {
- /* No apparent changes. Could we assert !this? */
- if (fsb.zs_ctime[0] == tsb.zs_ctime[0] &&
- fsb.zs_ctime[1] == tsb.zs_ctime[1])
- return (0);
- if (change) {
- print_link_change(fp, di, change,
- change > 0 ? fobjname : tobjname, &tsb);
- } else if (strcmp(fobjname, tobjname) == 0) {
- print_file(fp, di, ZDIFF_MODIFIED, fobjname, &tsb);
- } else {
- print_rename(fp, di, fobjname, tobjname, &tsb);
- }
- return (0);
- } else {
- /* file re-created or object re-used */
- print_file(fp, di, ZDIFF_REMOVED, fobjname, &fsb);
- print_file(fp, di, ZDIFF_ADDED, tobjname, &tsb);
- return (0);
- }
-}
-
-static int
-write_inuse_diffs(FILE *fp, differ_info_t *di, dmu_diff_record_t *dr)
-{
- uint64_t o;
- int err;
-
- for (o = dr->ddr_first; o <= dr->ddr_last; o++) {
- if ((err = write_inuse_diffs_one(fp, di, o)) != 0)
- return (err);
- }
- return (0);
-}
-
-static int
-describe_free(FILE *fp, differ_info_t *di, uint64_t object, char *namebuf,
- int maxlen)
-{
- struct zfs_stat sb;
-
- if (get_stats_for_obj(di, di->fromsnap, object, namebuf,
- maxlen, &sb) != 0) {
- return (-1);
- }
- /* Don't print if in the delete queue on from side */
- if (di->zerr == ESTALE) {
- di->zerr = 0;
- return (0);
- }
-
- print_file(fp, di, ZDIFF_REMOVED, namebuf, &sb);
- return (0);
-}
-
-static int
-write_free_diffs(FILE *fp, differ_info_t *di, dmu_diff_record_t *dr)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *lhdl = di->zhp->zfs_hdl;
- char fobjname[MAXPATHLEN];
-
- (void) strlcpy(zc.zc_name, di->fromsnap, sizeof (zc.zc_name));
- zc.zc_obj = dr->ddr_first - 1;
-
- ASSERT(di->zerr == 0);
-
- while (zc.zc_obj < dr->ddr_last) {
- int err;
-
- err = ioctl(lhdl->libzfs_fd, ZFS_IOC_NEXT_OBJ, &zc);
- if (err == 0) {
- if (zc.zc_obj == di->shares) {
- zc.zc_obj++;
- continue;
- }
- if (zc.zc_obj > dr->ddr_last) {
- break;
- }
- err = describe_free(fp, di, zc.zc_obj, fobjname,
- MAXPATHLEN);
- if (err)
- break;
- } else if (errno == ESRCH) {
- break;
- } else {
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "next allocated object (> %jd) find failure"),
- (uintmax_t)zc.zc_obj);
- di->zerr = errno;
- break;
- }
- }
- if (di->zerr)
- return (-1);
- return (0);
-}
-
-static void *
-differ(void *arg)
-{
- differ_info_t *di = arg;
- dmu_diff_record_t dr;
- FILE *ofp;
- int err = 0;
-
- if ((ofp = fdopen(di->outputfd, "w")) == NULL) {
- di->zerr = errno;
- (void) strerror_r(errno, di->errbuf, sizeof (di->errbuf));
- (void) close(di->datafd);
- return ((void *)-1);
- }
-
- for (;;) {
- char *cp = (char *)&dr;
- int len = sizeof (dr);
- int rv;
-
- do {
- rv = read(di->datafd, cp, len);
- cp += rv;
- len -= rv;
- } while (len > 0 && rv > 0);
-
- if (rv < 0 || (rv == 0 && len != sizeof (dr))) {
- di->zerr = EPIPE;
- break;
- } else if (rv == 0) {
- /* end of file at a natural breaking point */
- break;
- }
-
- switch (dr.ddr_type) {
- case DDR_FREE:
- err = write_free_diffs(ofp, di, &dr);
- break;
- case DDR_INUSE:
- err = write_inuse_diffs(ofp, di, &dr);
- break;
- default:
- di->zerr = EPIPE;
- break;
- }
-
- if (err || di->zerr)
- break;
- }
-
- (void) fclose(ofp);
- (void) close(di->datafd);
- if (err)
- return ((void *)-1);
- if (di->zerr) {
- ASSERT(di->zerr == EPIPE);
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "Internal error: bad data from diff IOCTL"));
- return ((void *)-1);
- }
- return ((void *)0);
-}
-
-static int
-find_shares_object(differ_info_t *di)
-{
- char fullpath[MAXPATHLEN];
- struct stat64 sb = { 0 };
-
- (void) strlcpy(fullpath, di->dsmnt, MAXPATHLEN);
- (void) strlcat(fullpath, ZDIFF_SHARESDIR, MAXPATHLEN);
-
- if (stat64(fullpath, &sb) != 0) {
-#ifdef illumos
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN, "Cannot stat %s"), fullpath);
- return (zfs_error(di->zhp->zfs_hdl, EZFS_DIFF, di->errbuf));
-#else
- return (0);
-#endif
- }
-
- di->shares = (uint64_t)sb.st_ino;
- return (0);
-}
-
-static int
-make_temp_snapshot(differ_info_t *di)
-{
- libzfs_handle_t *hdl = di->zhp->zfs_hdl;
- zfs_cmd_t zc = { 0 };
-
- (void) snprintf(zc.zc_value, sizeof (zc.zc_value),
- ZDIFF_PREFIX, getpid());
- (void) strlcpy(zc.zc_name, di->ds, sizeof (zc.zc_name));
- zc.zc_cleanup_fd = di->cleanupfd;
-
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_TMP_SNAPSHOT, &zc) != 0) {
- int err = errno;
- if (err == EPERM) {
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN, "The diff delegated "
- "permission is needed in order\nto create a "
- "just-in-time snapshot for diffing\n"));
- return (zfs_error(hdl, EZFS_DIFF, di->errbuf));
- } else {
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN, "Cannot create just-in-time "
- "snapshot of '%s'"), zc.zc_name);
- return (zfs_standard_error(hdl, err, di->errbuf));
- }
- }
-
- di->tmpsnap = zfs_strdup(hdl, zc.zc_value);
- di->tosnap = zfs_asprintf(hdl, "%s@%s", di->ds, di->tmpsnap);
- return (0);
-}
-
-static void
-teardown_differ_info(differ_info_t *di)
-{
- free(di->ds);
- free(di->dsmnt);
- free(di->fromsnap);
- free(di->frommnt);
- free(di->tosnap);
- free(di->tmpsnap);
- free(di->tomnt);
- (void) close(di->cleanupfd);
-}
-
-static int
-get_snapshot_names(differ_info_t *di, const char *fromsnap,
- const char *tosnap)
-{
- libzfs_handle_t *hdl = di->zhp->zfs_hdl;
- char *atptrf = NULL;
- char *atptrt = NULL;
- int fdslen, fsnlen;
- int tdslen, tsnlen;
-
- /*
- * Can accept
- * dataset@snap1
- * dataset@snap1 dataset@snap2
- * dataset@snap1 @snap2
- * dataset@snap1 dataset
- * @snap1 dataset@snap2
- */
- if (tosnap == NULL) {
- /* only a from snapshot given, must be valid */
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "Badly formed snapshot name %s"), fromsnap);
-
- if (!zfs_validate_name(hdl, fromsnap, ZFS_TYPE_SNAPSHOT,
- B_FALSE)) {
- return (zfs_error(hdl, EZFS_INVALIDNAME,
- di->errbuf));
- }
-
- atptrf = strchr(fromsnap, '@');
- ASSERT(atptrf != NULL);
- fdslen = atptrf - fromsnap;
-
- di->fromsnap = zfs_strdup(hdl, fromsnap);
- di->ds = zfs_strdup(hdl, fromsnap);
- di->ds[fdslen] = '\0';
-
- /* the to snap will be a just-in-time snap of the head */
- return (make_temp_snapshot(di));
- }
-
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "Unable to determine which snapshots to compare"));
-
- atptrf = strchr(fromsnap, '@');
- atptrt = strchr(tosnap, '@');
- fdslen = atptrf ? atptrf - fromsnap : strlen(fromsnap);
- tdslen = atptrt ? atptrt - tosnap : strlen(tosnap);
- fsnlen = strlen(fromsnap) - fdslen; /* includes @ sign */
- tsnlen = strlen(tosnap) - tdslen; /* includes @ sign */
-
- if (fsnlen <= 1 || tsnlen == 1 || (fdslen == 0 && tdslen == 0) ||
- (fsnlen == 0 && tsnlen == 0)) {
- return (zfs_error(hdl, EZFS_INVALIDNAME, di->errbuf));
- } else if ((fdslen > 0 && tdslen > 0) &&
- ((tdslen != fdslen || strncmp(fromsnap, tosnap, fdslen) != 0))) {
- /*
- * not the same dataset name, might be okay if
- * tosnap is a clone of a fromsnap descendant.
- */
- char origin[ZFS_MAX_DATASET_NAME_LEN];
- zprop_source_t src;
- zfs_handle_t *zhp;
-
- di->ds = zfs_alloc(di->zhp->zfs_hdl, tdslen + 1);
- (void) strncpy(di->ds, tosnap, tdslen);
- di->ds[tdslen] = '\0';
-
- zhp = zfs_open(hdl, di->ds, ZFS_TYPE_FILESYSTEM);
- while (zhp != NULL) {
- if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin,
- sizeof (origin), &src, NULL, 0, B_FALSE) != 0) {
- (void) zfs_close(zhp);
- zhp = NULL;
- break;
- }
- if (strncmp(origin, fromsnap, fsnlen) == 0)
- break;
-
- (void) zfs_close(zhp);
- zhp = zfs_open(hdl, origin, ZFS_TYPE_FILESYSTEM);
- }
-
- if (zhp == NULL) {
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "Not an earlier snapshot from the same fs"));
- return (zfs_error(hdl, EZFS_INVALIDNAME, di->errbuf));
- } else {
- (void) zfs_close(zhp);
- }
-
- di->isclone = B_TRUE;
- di->fromsnap = zfs_strdup(hdl, fromsnap);
- if (tsnlen) {
- di->tosnap = zfs_strdup(hdl, tosnap);
- } else {
- return (make_temp_snapshot(di));
- }
- } else {
- int dslen = fdslen ? fdslen : tdslen;
-
- di->ds = zfs_alloc(hdl, dslen + 1);
- (void) strncpy(di->ds, fdslen ? fromsnap : tosnap, dslen);
- di->ds[dslen] = '\0';
-
- di->fromsnap = zfs_asprintf(hdl, "%s%s", di->ds, atptrf);
- if (tsnlen) {
- di->tosnap = zfs_asprintf(hdl, "%s%s", di->ds, atptrt);
- } else {
- return (make_temp_snapshot(di));
- }
- }
- return (0);
-}
-
-static int
-get_mountpoint(differ_info_t *di, char *dsnm, char **mntpt)
-{
- boolean_t mounted;
-
- mounted = is_mounted(di->zhp->zfs_hdl, dsnm, mntpt);
- if (mounted == B_FALSE) {
- (void) snprintf(di->errbuf, sizeof (di->errbuf),
- dgettext(TEXT_DOMAIN,
- "Cannot diff an unmounted snapshot"));
- return (zfs_error(di->zhp->zfs_hdl, EZFS_BADTYPE, di->errbuf));
- }
-
- /* Avoid a double slash at the beginning of root-mounted datasets */
- if (**mntpt == '/' && *(*mntpt + 1) == '\0')
- **mntpt = '\0';
- return (0);
-}
-
-static int
-get_mountpoints(differ_info_t *di)
-{
- char *strptr;
- char *frommntpt;
-
- /*
- * first get the mountpoint for the parent dataset
- */
- if (get_mountpoint(di, di->ds, &di->dsmnt) != 0)
- return (-1);
-
- strptr = strchr(di->tosnap, '@');
- ASSERT3P(strptr, !=, NULL);
- di->tomnt = zfs_asprintf(di->zhp->zfs_hdl, "%s%s%s", di->dsmnt,
- ZDIFF_SNAPDIR, ++strptr);
-
- strptr = strchr(di->fromsnap, '@');
- ASSERT3P(strptr, !=, NULL);
-
- frommntpt = di->dsmnt;
- if (di->isclone) {
- char *mntpt;
- int err;
-
- *strptr = '\0';
- err = get_mountpoint(di, di->fromsnap, &mntpt);
- *strptr = '@';
- if (err != 0)
- return (-1);
- frommntpt = mntpt;
- }
-
- di->frommnt = zfs_asprintf(di->zhp->zfs_hdl, "%s%s%s", frommntpt,
- ZDIFF_SNAPDIR, ++strptr);
-
- if (di->isclone)
- free(frommntpt);
-
- return (0);
-}
-
-static int
-setup_differ_info(zfs_handle_t *zhp, const char *fromsnap,
- const char *tosnap, differ_info_t *di)
-{
- di->zhp = zhp;
-
- di->cleanupfd = open(ZFS_DEV, O_RDWR|O_EXCL);
- VERIFY(di->cleanupfd >= 0);
-
- if (get_snapshot_names(di, fromsnap, tosnap) != 0)
- return (-1);
-
- if (get_mountpoints(di) != 0)
- return (-1);
-
- if (find_shares_object(di) != 0)
- return (-1);
-
- return (0);
-}
-
-int
-zfs_show_diffs(zfs_handle_t *zhp, int outfd, const char *fromsnap,
- const char *tosnap, int flags)
-{
- zfs_cmd_t zc = { 0 };
- char errbuf[1024];
- differ_info_t di = { 0 };
- pthread_t tid;
- int pipefd[2];
- int iocerr;
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "zfs diff failed"));
-
- if (setup_differ_info(zhp, fromsnap, tosnap, &di)) {
- teardown_differ_info(&di);
- return (-1);
- }
-
- if (pipe(pipefd)) {
- zfs_error_aux(zhp->zfs_hdl, strerror(errno));
- teardown_differ_info(&di);
- return (zfs_error(zhp->zfs_hdl, EZFS_PIPEFAILED, errbuf));
- }
-
- di.scripted = (flags & ZFS_DIFF_PARSEABLE);
- di.classify = (flags & ZFS_DIFF_CLASSIFY);
- di.timestamped = (flags & ZFS_DIFF_TIMESTAMP);
-
- di.outputfd = outfd;
- di.datafd = pipefd[0];
-
- if (pthread_create(&tid, NULL, differ, &di)) {
- zfs_error_aux(zhp->zfs_hdl, strerror(errno));
- (void) close(pipefd[0]);
- (void) close(pipefd[1]);
- teardown_differ_info(&di);
- return (zfs_error(zhp->zfs_hdl,
- EZFS_THREADCREATEFAILED, errbuf));
- }
-
- /* do the ioctl() */
- (void) strlcpy(zc.zc_value, di.fromsnap, strlen(di.fromsnap) + 1);
- (void) strlcpy(zc.zc_name, di.tosnap, strlen(di.tosnap) + 1);
- zc.zc_cookie = pipefd[1];
-
- iocerr = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DIFF, &zc);
- if (iocerr != 0) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "Unable to obtain diffs"));
- if (errno == EPERM) {
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "\n The sys_mount privilege or diff delegated "
- "permission is needed\n to execute the "
- "diff ioctl"));
- } else if (errno == EXDEV) {
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "\n Not an earlier snapshot from the same fs"));
- } else if (errno != EPIPE || di.zerr == 0) {
- zfs_error_aux(zhp->zfs_hdl, strerror(errno));
- }
- (void) close(pipefd[1]);
- (void) pthread_cancel(tid);
- (void) pthread_join(tid, NULL);
- teardown_differ_info(&di);
- if (di.zerr != 0 && di.zerr != EPIPE) {
- zfs_error_aux(zhp->zfs_hdl, strerror(di.zerr));
- return (zfs_error(zhp->zfs_hdl, EZFS_DIFF, di.errbuf));
- } else {
- return (zfs_error(zhp->zfs_hdl, EZFS_DIFFDATA, errbuf));
- }
- }
-
- (void) close(pipefd[1]);
- (void) pthread_join(tid, NULL);
-
- if (di.zerr != 0) {
- zfs_error_aux(zhp->zfs_hdl, strerror(di.zerr));
- return (zfs_error(zhp->zfs_hdl, EZFS_DIFF, di.errbuf));
- }
- teardown_differ_info(&di);
- return (0);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_fru.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_fru.c
deleted file mode 100644
index 474470c416ea9..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_fru.c
+++ /dev/null
@@ -1,452 +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.
- */
-
-#include <dlfcn.h>
-#include <errno.h>
-#include <libintl.h>
-#include <link.h>
-#include <pthread.h>
-#include <strings.h>
-#include <unistd.h>
-
-#include <libzfs.h>
-
-#include <fm/libtopo.h>
-#include <sys/fm/protocol.h>
-#include <sys/systeminfo.h>
-
-#include "libzfs_impl.h"
-
-/*
- * This file is responsible for determining the relationship between I/O
- * devices paths and physical locations. In the world of MPxIO and external
- * enclosures, the device path is not synonymous with the physical location.
- * If you remove a drive and insert it into a different slot, it will end up
- * with the same path under MPxIO. If you recable storage enclosures, the
- * device paths may change. All of this makes it difficult to implement the
- * 'autoreplace' property, which is supposed to automatically manage disk
- * replacement based on physical slot.
- *
- * In order to work around these limitations, we have a per-vdev FRU property
- * that is the libtopo path (minus disk-specific authority information) to the
- * physical location of the device on the system. This is an optional
- * property, and is only needed when using the 'autoreplace' property or when
- * generating FMA faults against vdevs.
- */
-
-/*
- * Because the FMA packages depend on ZFS, we have to dlopen() libtopo in case
- * it is not present. We only need this once per library instance, so it is
- * not part of the libzfs handle.
- */
-static void *_topo_dlhandle;
-static topo_hdl_t *(*_topo_open)(int, const char *, int *);
-static void (*_topo_close)(topo_hdl_t *);
-static char *(*_topo_snap_hold)(topo_hdl_t *, const char *, int *);
-static void (*_topo_snap_release)(topo_hdl_t *);
-static topo_walk_t *(*_topo_walk_init)(topo_hdl_t *, const char *,
- topo_walk_cb_t, void *, int *);
-static int (*_topo_walk_step)(topo_walk_t *, int);
-static void (*_topo_walk_fini)(topo_walk_t *);
-static void (*_topo_hdl_strfree)(topo_hdl_t *, char *);
-static char *(*_topo_node_name)(tnode_t *);
-static int (*_topo_prop_get_string)(tnode_t *, const char *, const char *,
- char **, int *);
-static int (*_topo_node_fru)(tnode_t *, nvlist_t **, nvlist_t *, int *);
-static int (*_topo_fmri_nvl2str)(topo_hdl_t *, nvlist_t *, char **, int *);
-static int (*_topo_fmri_strcmp_noauth)(topo_hdl_t *, const char *,
- const char *);
-
-#define ZFS_FRU_HASH_SIZE 257
-
-static size_t
-fru_strhash(const char *key)
-{
- ulong_t g, h = 0;
- const char *p;
-
- for (p = key; *p != '\0'; p++) {
- h = (h << 4) + *p;
-
- if ((g = (h & 0xf0000000)) != 0) {
- h ^= (g >> 24);
- h ^= g;
- }
- }
-
- return (h % ZFS_FRU_HASH_SIZE);
-}
-
-static int
-libzfs_fru_gather(topo_hdl_t *thp, tnode_t *tn, void *arg)
-{
- libzfs_handle_t *hdl = arg;
- nvlist_t *fru;
- char *devpath, *frustr;
- int err;
- libzfs_fru_t *frup;
- size_t idx;
-
- /*
- * If this is the chassis node, and we don't yet have the system
- * chassis ID, then fill in this value now.
- */
- if (hdl->libzfs_chassis_id[0] == '\0' &&
- strcmp(_topo_node_name(tn), "chassis") == 0) {
- if (_topo_prop_get_string(tn, FM_FMRI_AUTHORITY,
- FM_FMRI_AUTH_CHASSIS, &devpath, &err) == 0)
- (void) strlcpy(hdl->libzfs_chassis_id, devpath,
- sizeof (hdl->libzfs_chassis_id));
- }
-
- /*
- * Skip non-disk nodes.
- */
- if (strcmp(_topo_node_name(tn), "disk") != 0)
- return (TOPO_WALK_NEXT);
-
- /*
- * Get the devfs path and FRU.
- */
- if (_topo_prop_get_string(tn, "io", "devfs-path", &devpath, &err) != 0)
- return (TOPO_WALK_NEXT);
-
- if (libzfs_fru_lookup(hdl, devpath) != NULL) {
- _topo_hdl_strfree(thp, devpath);
- return (TOPO_WALK_NEXT);
- }
-
- if (_topo_node_fru(tn, &fru, NULL, &err) != 0) {
- _topo_hdl_strfree(thp, devpath);
- return (TOPO_WALK_NEXT);
- }
-
- /*
- * Convert the FRU into a string.
- */
- if (_topo_fmri_nvl2str(thp, fru, &frustr, &err) != 0) {
- nvlist_free(fru);
- _topo_hdl_strfree(thp, devpath);
- return (TOPO_WALK_NEXT);
- }
-
- nvlist_free(fru);
-
- /*
- * Finally, we have a FRU string and device path. Add it to the hash.
- */
- if ((frup = calloc(sizeof (libzfs_fru_t), 1)) == NULL) {
- _topo_hdl_strfree(thp, devpath);
- _topo_hdl_strfree(thp, frustr);
- return (TOPO_WALK_NEXT);
- }
-
- if ((frup->zf_device = strdup(devpath)) == NULL ||
- (frup->zf_fru = strdup(frustr)) == NULL) {
- free(frup->zf_device);
- free(frup);
- _topo_hdl_strfree(thp, devpath);
- _topo_hdl_strfree(thp, frustr);
- return (TOPO_WALK_NEXT);
- }
-
- _topo_hdl_strfree(thp, devpath);
- _topo_hdl_strfree(thp, frustr);
-
- idx = fru_strhash(frup->zf_device);
- frup->zf_chain = hdl->libzfs_fru_hash[idx];
- hdl->libzfs_fru_hash[idx] = frup;
- frup->zf_next = hdl->libzfs_fru_list;
- hdl->libzfs_fru_list = frup;
-
- return (TOPO_WALK_NEXT);
-}
-
-/*
- * Called during initialization to setup the dynamic libtopo connection.
- */
-#pragma init(libzfs_init_fru)
-static void
-libzfs_init_fru(void)
-{
- char path[MAXPATHLEN];
- char isa[257];
-
-#if defined(_LP64)
- if (sysinfo(SI_ARCHITECTURE_64, isa, sizeof (isa)) < 0)
- isa[0] = '\0';
-#else
- isa[0] = '\0';
-#endif
- (void) snprintf(path, sizeof (path),
- "/usr/lib/fm/%s/libtopo.so", isa);
-
- if ((_topo_dlhandle = dlopen(path, RTLD_LAZY)) == NULL)
- return;
-
- _topo_open = (topo_hdl_t *(*)())
- dlsym(_topo_dlhandle, "topo_open");
- _topo_close = (void (*)())
- dlsym(_topo_dlhandle, "topo_close");
- _topo_snap_hold = (char *(*)())
- dlsym(_topo_dlhandle, "topo_snap_hold");
- _topo_snap_release = (void (*)())
- dlsym(_topo_dlhandle, "topo_snap_release");
- _topo_walk_init = (topo_walk_t *(*)())
- dlsym(_topo_dlhandle, "topo_walk_init");
- _topo_walk_step = (int (*)())
- dlsym(_topo_dlhandle, "topo_walk_step");
- _topo_walk_fini = (void (*)())
- dlsym(_topo_dlhandle, "topo_walk_fini");
- _topo_hdl_strfree = (void (*)())
- dlsym(_topo_dlhandle, "topo_hdl_strfree");
- _topo_node_name = (char *(*)())
- dlsym(_topo_dlhandle, "topo_node_name");
- _topo_prop_get_string = (int (*)())
- dlsym(_topo_dlhandle, "topo_prop_get_string");
- _topo_node_fru = (int (*)())
- dlsym(_topo_dlhandle, "topo_node_fru");
- _topo_fmri_nvl2str = (int (*)())
- dlsym(_topo_dlhandle, "topo_fmri_nvl2str");
- _topo_fmri_strcmp_noauth = (int (*)())
- dlsym(_topo_dlhandle, "topo_fmri_strcmp_noauth");
-
- if (_topo_open == NULL || _topo_close == NULL ||
- _topo_snap_hold == NULL || _topo_snap_release == NULL ||
- _topo_walk_init == NULL || _topo_walk_step == NULL ||
- _topo_walk_fini == NULL || _topo_hdl_strfree == NULL ||
- _topo_node_name == NULL || _topo_prop_get_string == NULL ||
- _topo_node_fru == NULL || _topo_fmri_nvl2str == NULL ||
- _topo_fmri_strcmp_noauth == NULL) {
- (void) dlclose(_topo_dlhandle);
- _topo_dlhandle = NULL;
- }
-}
-
-/*
- * Refresh the mappings from device path -> FMRI. We do this by walking the
- * hc topology looking for disk nodes, and recording the io/devfs-path and FRU.
- * Note that we strip out the disk-specific authority information (serial,
- * part, revision, etc) so that we are left with only the identifying
- * characteristics of the slot (hc path and chassis-id).
- */
-void
-libzfs_fru_refresh(libzfs_handle_t *hdl)
-{
- int err;
- char *uuid;
- topo_hdl_t *thp;
- topo_walk_t *twp;
-
- if (_topo_dlhandle == NULL)
- return;
-
- /*
- * Clear the FRU hash and initialize our basic structures.
- */
- libzfs_fru_clear(hdl, B_FALSE);
-
- if ((hdl->libzfs_topo_hdl = _topo_open(TOPO_VERSION,
- NULL, &err)) == NULL)
- return;
-
- thp = hdl->libzfs_topo_hdl;
-
- if ((uuid = _topo_snap_hold(thp, NULL, &err)) == NULL)
- return;
-
- _topo_hdl_strfree(thp, uuid);
-
- if (hdl->libzfs_fru_hash == NULL &&
- (hdl->libzfs_fru_hash =
- calloc(ZFS_FRU_HASH_SIZE, sizeof (void *))) == NULL)
- return;
-
- /*
- * We now have a topo snapshot, so iterate over the hc topology looking
- * for disks to add to the hash.
- */
- twp = _topo_walk_init(thp, FM_FMRI_SCHEME_HC,
- libzfs_fru_gather, hdl, &err);
- if (twp != NULL) {
- (void) _topo_walk_step(twp, TOPO_WALK_CHILD);
- _topo_walk_fini(twp);
- }
-}
-
-/*
- * Given a devfs path, return the FRU for the device, if known. This will
- * automatically call libzfs_fru_refresh() if it hasn't already been called by
- * the consumer. The string returned is valid until the next call to
- * libzfs_fru_refresh().
- */
-const char *
-libzfs_fru_lookup(libzfs_handle_t *hdl, const char *devpath)
-{
- size_t idx = fru_strhash(devpath);
- libzfs_fru_t *frup;
-
- if (hdl->libzfs_fru_hash == NULL)
- libzfs_fru_refresh(hdl);
-
- if (hdl->libzfs_fru_hash == NULL)
- return (NULL);
-
- for (frup = hdl->libzfs_fru_hash[idx]; frup != NULL;
- frup = frup->zf_chain) {
- if (strcmp(devpath, frup->zf_device) == 0)
- return (frup->zf_fru);
- }
-
- return (NULL);
-}
-
-/*
- * Given a fru path, return the device path. This will automatically call
- * libzfs_fru_refresh() if it hasn't already been called by the consumer. The
- * string returned is valid until the next call to libzfs_fru_refresh().
- */
-const char *
-libzfs_fru_devpath(libzfs_handle_t *hdl, const char *fru)
-{
- libzfs_fru_t *frup;
- size_t idx;
-
- if (hdl->libzfs_fru_hash == NULL)
- libzfs_fru_refresh(hdl);
-
- if (hdl->libzfs_fru_hash == NULL)
- return (NULL);
-
- for (idx = 0; idx < ZFS_FRU_HASH_SIZE; idx++) {
- for (frup = hdl->libzfs_fru_hash[idx]; frup != NULL;
- frup = frup->zf_next) {
- if (_topo_fmri_strcmp_noauth(hdl->libzfs_topo_hdl,
- fru, frup->zf_fru))
- return (frup->zf_device);
- }
- }
-
- return (NULL);
-}
-
-/*
- * Change the stored FRU for the given vdev.
- */
-int
-zpool_fru_set(zpool_handle_t *zhp, uint64_t vdev_guid, const char *fru)
-{
- zfs_cmd_t zc = { 0 };
-
- (void) strncpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- (void) strncpy(zc.zc_value, fru, sizeof (zc.zc_value));
- zc.zc_guid = vdev_guid;
-
- if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_VDEV_SETFRU, &zc) != 0)
- return (zpool_standard_error_fmt(zhp->zpool_hdl, errno,
- dgettext(TEXT_DOMAIN, "cannot set FRU")));
-
- return (0);
-}
-
-/*
- * Compare to two FRUs, ignoring any authority information.
- */
-boolean_t
-libzfs_fru_compare(libzfs_handle_t *hdl, const char *a, const char *b)
-{
- if (hdl->libzfs_fru_hash == NULL)
- libzfs_fru_refresh(hdl);
-
- if (hdl->libzfs_fru_hash == NULL)
- return (strcmp(a, b) == 0);
-
- return (_topo_fmri_strcmp_noauth(hdl->libzfs_topo_hdl, a, b));
-}
-
-/*
- * This special function checks to see whether the FRU indicates it's supposed
- * to be in the system chassis, but the chassis-id doesn't match. This can
- * happen in a clustered case, where both head nodes have the same logical
- * disk, but opening the device on the other head node is meaningless.
- */
-boolean_t
-libzfs_fru_notself(libzfs_handle_t *hdl, const char *fru)
-{
- const char *chassisid;
- size_t len;
-
- if (hdl->libzfs_fru_hash == NULL)
- libzfs_fru_refresh(hdl);
-
- if (hdl->libzfs_chassis_id[0] == '\0')
- return (B_FALSE);
-
- if (strstr(fru, "/chassis=0/") == NULL)
- return (B_FALSE);
-
- if ((chassisid = strstr(fru, ":chassis-id=")) == NULL)
- return (B_FALSE);
-
- chassisid += 12;
- len = strlen(hdl->libzfs_chassis_id);
- if (strncmp(chassisid, hdl->libzfs_chassis_id, len) == 0 &&
- (chassisid[len] == '/' || chassisid[len] == ':'))
- return (B_FALSE);
-
- return (B_TRUE);
-}
-
-/*
- * Clear memory associated with the FRU hash.
- */
-void
-libzfs_fru_clear(libzfs_handle_t *hdl, boolean_t final)
-{
- libzfs_fru_t *frup;
-
- while ((frup = hdl->libzfs_fru_list) != NULL) {
- hdl->libzfs_fru_list = frup->zf_next;
- free(frup->zf_device);
- free(frup->zf_fru);
- free(frup);
- }
-
- hdl->libzfs_fru_list = NULL;
-
- if (hdl->libzfs_topo_hdl != NULL) {
- _topo_snap_release(hdl->libzfs_topo_hdl);
- _topo_close(hdl->libzfs_topo_hdl);
- hdl->libzfs_topo_hdl = NULL;
- }
-
- if (final) {
- free(hdl->libzfs_fru_hash);
- } else if (hdl->libzfs_fru_hash != NULL) {
- bzero(hdl->libzfs_fru_hash,
- ZFS_FRU_HASH_SIZE * sizeof (void *));
- }
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h
deleted file mode 100644
index a0338afadb8f5..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_impl.h
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * CDDL HEADER SART
- *
- * 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 Pawel Jakub Dawidek. All rights reserved.
- * Copyright (c) 2011, 2017 by Delphix. All rights reserved.
- * Copyright (c) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
- */
-
-#ifndef _LIBZFS_IMPL_H
-#define _LIBZFS_IMPL_H
-
-#include <sys/fs/zfs.h>
-#include <sys/spa.h>
-#include <sys/nvpair.h>
-#include <sys/dmu.h>
-#include <sys/zfs_ioctl.h>
-
-#include <libshare.h>
-#include <libuutil.h>
-#include <libzfs.h>
-#include <libzfs_core.h>
-#include <libzfs_compat.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef VERIFY
-#undef VERIFY
-#endif
-#define VERIFY verify
-
-typedef struct libzfs_fru {
- char *zf_device;
- char *zf_fru;
- struct libzfs_fru *zf_chain;
- struct libzfs_fru *zf_next;
-} libzfs_fru_t;
-
-struct libzfs_handle {
- int libzfs_error;
- int libzfs_fd;
- FILE *libzfs_mnttab;
- FILE *libzfs_sharetab;
- zpool_handle_t *libzfs_pool_handles;
- uu_avl_pool_t *libzfs_ns_avlpool;
- uu_avl_t *libzfs_ns_avl;
- uint64_t libzfs_ns_gen;
- int libzfs_desc_active;
- char libzfs_action[1024];
- char libzfs_desc[1024];
- int libzfs_printerr;
- int libzfs_storeerr; /* stuff error messages into buffer */
- void *libzfs_sharehdl; /* libshare handle */
- boolean_t libzfs_mnttab_enable;
- /*
- * We need a lock to handle the case where parallel mount
- * threads are populating the mnttab cache simultaneously. The
- * lock only protects the integrity of the avl tree, and does
- * not protect the contents of the mnttab entries themselves.
- */
- pthread_mutex_t libzfs_mnttab_cache_lock;
- avl_tree_t libzfs_mnttab_cache;
- int libzfs_pool_iter;
- libzfs_fru_t **libzfs_fru_hash;
- libzfs_fru_t *libzfs_fru_list;
- char libzfs_chassis_id[256];
- boolean_t libzfs_prop_debug;
-};
-
-struct zfs_handle {
- libzfs_handle_t *zfs_hdl;
- zpool_handle_t *zpool_hdl;
- char zfs_name[ZFS_MAX_DATASET_NAME_LEN];
- zfs_type_t zfs_type; /* type including snapshot */
- zfs_type_t zfs_head_type; /* type excluding snapshot */
- dmu_objset_stats_t zfs_dmustats;
- nvlist_t *zfs_props;
- nvlist_t *zfs_user_props;
- nvlist_t *zfs_recvd_props;
- boolean_t zfs_mntcheck;
- char *zfs_mntopts;
- uint8_t *zfs_props_table;
-};
-
-/*
- * This is different from checking zfs_type, because it will also catch
- * snapshots of volumes.
- */
-#define ZFS_IS_VOLUME(zhp) ((zhp)->zfs_head_type == ZFS_TYPE_VOLUME)
-
-struct zpool_handle {
- libzfs_handle_t *zpool_hdl;
- zpool_handle_t *zpool_next;
- char zpool_name[ZFS_MAX_DATASET_NAME_LEN];
- int zpool_state;
- size_t zpool_config_size;
- nvlist_t *zpool_config;
- nvlist_t *zpool_old_config;
- nvlist_t *zpool_props;
- diskaddr_t zpool_start_block;
-};
-
-typedef enum {
- PROTO_NFS = 0,
- PROTO_SMB = 1,
- PROTO_END = 2
-} zfs_share_proto_t;
-
-/*
- * The following can be used as a bitmask and any new values
- * added must preserve that capability.
- */
-typedef enum {
- SHARED_NOT_SHARED = 0x0,
- SHARED_NFS = 0x2,
- SHARED_SMB = 0x4
-} zfs_share_type_t;
-
-#define CONFIG_BUF_MINSIZE 262144
-
-int zfs_error(libzfs_handle_t *, int, const char *);
-int zfs_error_fmt(libzfs_handle_t *, int, const char *, ...);
-void zfs_error_aux(libzfs_handle_t *, const char *, ...);
-void *zfs_alloc(libzfs_handle_t *, size_t);
-void *zfs_realloc(libzfs_handle_t *, void *, size_t, size_t);
-char *zfs_asprintf(libzfs_handle_t *, const char *, ...);
-char *zfs_strdup(libzfs_handle_t *, const char *);
-int no_memory(libzfs_handle_t *);
-
-int zfs_standard_error(libzfs_handle_t *, int, const char *);
-int zfs_standard_error_fmt(libzfs_handle_t *, int, const char *, ...);
-int zpool_standard_error(libzfs_handle_t *, int, const char *);
-int zpool_standard_error_fmt(libzfs_handle_t *, int, const char *, ...);
-
-int get_dependents(libzfs_handle_t *, boolean_t, const char *, char ***,
- size_t *);
-zfs_handle_t *make_dataset_handle_zc(libzfs_handle_t *, zfs_cmd_t *);
-zfs_handle_t *make_dataset_simple_handle_zc(zfs_handle_t *, zfs_cmd_t *);
-
-int zprop_parse_value(libzfs_handle_t *, nvpair_t *, int, zfs_type_t,
- nvlist_t *, char **, uint64_t *, const char *);
-int zprop_expand_list(libzfs_handle_t *hdl, zprop_list_t **plp,
- zfs_type_t type);
-
-/*
- * Use this changelist_gather() flag to force attempting mounts
- * on each change node regardless of whether or not it is currently
- * mounted.
- */
-#define CL_GATHER_MOUNT_ALWAYS 0x01
-/*
- * Use this changelist_gather() flag to prevent unmounting of file systems.
- */
-#define CL_GATHER_DONT_UNMOUNT 0x02
-
-typedef struct prop_changelist prop_changelist_t;
-
-int zcmd_alloc_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *, size_t);
-int zcmd_write_src_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t *);
-int zcmd_write_conf_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t *);
-int zcmd_expand_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *);
-int zcmd_read_dst_nvlist(libzfs_handle_t *, zfs_cmd_t *, nvlist_t **);
-void zcmd_free_nvlists(zfs_cmd_t *);
-
-int changelist_prefix(prop_changelist_t *);
-int changelist_postfix(prop_changelist_t *);
-void changelist_rename(prop_changelist_t *, const char *, const char *);
-void changelist_remove(prop_changelist_t *, const char *);
-void changelist_free(prop_changelist_t *);
-prop_changelist_t *changelist_gather(zfs_handle_t *, zfs_prop_t, int, int);
-int changelist_unshare(prop_changelist_t *, zfs_share_proto_t *);
-int changelist_haszonedchild(prop_changelist_t *);
-
-void remove_mountpoint(zfs_handle_t *);
-int create_parents(libzfs_handle_t *, char *, int);
-boolean_t isa_child_of(const char *dataset, const char *parent);
-
-zfs_handle_t *make_dataset_handle(libzfs_handle_t *, const char *);
-zfs_handle_t *make_bookmark_handle(zfs_handle_t *, const char *,
- nvlist_t *props);
-
-int zpool_open_silent(libzfs_handle_t *, const char *, zpool_handle_t **);
-
-boolean_t zpool_name_valid(libzfs_handle_t *, boolean_t, const char *);
-
-int zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
- boolean_t modifying);
-
-void namespace_clear(libzfs_handle_t *);
-
-/*
- * libshare (sharemgr) interfaces used internally.
- */
-
-extern int zfs_init_libshare(libzfs_handle_t *, int);
-extern int zfs_parse_options(char *, zfs_share_proto_t);
-
-extern int zfs_unshare_proto(zfs_handle_t *,
- const char *, zfs_share_proto_t *);
-
-extern void libzfs_fru_clear(libzfs_handle_t *, boolean_t);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBZFS_IMPL_H */
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c
deleted file mode 100644
index 87c8dd14898b6..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_import.c
+++ /dev/null
@@ -1,1929 +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, 2017 by Delphix. All rights reserved.
- * Copyright 2015 RackTop Systems.
- * Copyright 2016 Nexenta Systems, Inc.
- */
-
-/*
- * Pool import support functions.
- *
- * To import a pool, we rely on reading the configuration information from the
- * ZFS label of each device. If we successfully read the label, then we
- * organize the configuration information in the following hierarchy:
- *
- * pool guid -> toplevel vdev guid -> label txg
- *
- * Duplicate entries matching this same tuple will be discarded. Once we have
- * examined every device, we pick the best label txg config for each toplevel
- * vdev. We then arrange these toplevel vdevs into a complete pool config, and
- * update any paths that have changed. Finally, we attempt to import the pool
- * using our derived config, and record the results.
- */
-
-#include <aio.h>
-#include <ctype.h>
-#include <devid.h>
-#include <dirent.h>
-#include <errno.h>
-#include <libintl.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <thread_pool.h>
-#include <libgeom.h>
-
-#include <sys/vdev_impl.h>
-
-#include "libzfs.h"
-#include "libzfs_impl.h"
-
-/*
- * Intermediate structures used to gather configuration information.
- */
-typedef struct config_entry {
- uint64_t ce_txg;
- nvlist_t *ce_config;
- struct config_entry *ce_next;
-} config_entry_t;
-
-typedef struct vdev_entry {
- uint64_t ve_guid;
- config_entry_t *ve_configs;
- struct vdev_entry *ve_next;
-} vdev_entry_t;
-
-typedef struct pool_entry {
- uint64_t pe_guid;
- vdev_entry_t *pe_vdevs;
- struct pool_entry *pe_next;
-} pool_entry_t;
-
-typedef struct name_entry {
- char *ne_name;
- uint64_t ne_guid;
- struct name_entry *ne_next;
-} name_entry_t;
-
-typedef struct pool_list {
- pool_entry_t *pools;
- name_entry_t *names;
-} pool_list_t;
-
-static char *
-get_devid(const char *path)
-{
-#ifdef have_devid
- int fd;
- ddi_devid_t devid;
- char *minor, *ret;
-
- if ((fd = open(path, O_RDONLY)) < 0)
- return (NULL);
-
- minor = NULL;
- ret = NULL;
- if (devid_get(fd, &devid) == 0) {
- if (devid_get_minor_name(fd, &minor) == 0)
- ret = devid_str_encode(devid, minor);
- if (minor != NULL)
- devid_str_free(minor);
- devid_free(devid);
- }
- (void) close(fd);
-
- return (ret);
-#else
- return (NULL);
-#endif
-}
-
-
-/*
- * Go through and fix up any path and/or devid information for the given vdev
- * configuration.
- */
-static int
-fix_paths(nvlist_t *nv, name_entry_t *names)
-{
- nvlist_t **child;
- uint_t c, children;
- uint64_t guid;
- name_entry_t *ne, *best;
- char *path, *devid;
- int matched;
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) == 0) {
- for (c = 0; c < children; c++)
- if (fix_paths(child[c], names) != 0)
- return (-1);
- return (0);
- }
-
- /*
- * This is a leaf (file or disk) vdev. In either case, go through
- * the name list and see if we find a matching guid. If so, replace
- * the path and see if we can calculate a new devid.
- *
- * There may be multiple names associated with a particular guid, in
- * which case we have overlapping slices or multiple paths to the same
- * disk. If this is the case, then we want to pick the path that is
- * the most similar to the original, where "most similar" is the number
- * of matching characters starting from the end of the path. This will
- * preserve slice numbers even if the disks have been reorganized, and
- * will also catch preferred disk names if multiple paths exist.
- */
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0);
- if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0)
- path = NULL;
-
- matched = 0;
- best = NULL;
- for (ne = names; ne != NULL; ne = ne->ne_next) {
- if (ne->ne_guid == guid) {
- const char *src, *dst;
- int count;
-
- if (path == NULL) {
- best = ne;
- break;
- }
-
- src = ne->ne_name + strlen(ne->ne_name) - 1;
- dst = path + strlen(path) - 1;
- for (count = 0; src >= ne->ne_name && dst >= path;
- src--, dst--, count++)
- if (*src != *dst)
- break;
-
- /*
- * At this point, 'count' is the number of characters
- * matched from the end.
- */
- if (count > matched || best == NULL) {
- best = ne;
- matched = count;
- }
- }
- }
-
- if (best == NULL)
- return (0);
-
- if (nvlist_add_string(nv, ZPOOL_CONFIG_PATH, best->ne_name) != 0)
- return (-1);
-
- if ((devid = get_devid(best->ne_name)) == NULL) {
- (void) nvlist_remove_all(nv, ZPOOL_CONFIG_DEVID);
- } else {
- if (nvlist_add_string(nv, ZPOOL_CONFIG_DEVID, devid) != 0) {
- devid_str_free(devid);
- return (-1);
- }
- devid_str_free(devid);
- }
-
- return (0);
-}
-
-/*
- * Add the given configuration to the list of known devices.
- */
-static int
-add_config(libzfs_handle_t *hdl, pool_list_t *pl, const char *path,
- nvlist_t *config)
-{
- uint64_t pool_guid, vdev_guid, top_guid, txg, state;
- pool_entry_t *pe;
- vdev_entry_t *ve;
- config_entry_t *ce;
- name_entry_t *ne;
-
- /*
- * If this is a hot spare not currently in use or level 2 cache
- * device, add it to the list of names to translate, but don't do
- * anything else.
- */
- if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
- &state) == 0 &&
- (state == POOL_STATE_SPARE || state == POOL_STATE_L2CACHE) &&
- nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, &vdev_guid) == 0) {
- if ((ne = zfs_alloc(hdl, sizeof (name_entry_t))) == NULL)
- return (-1);
-
- if ((ne->ne_name = zfs_strdup(hdl, path)) == NULL) {
- free(ne);
- return (-1);
- }
-
- ne->ne_guid = vdev_guid;
- ne->ne_next = pl->names;
- pl->names = ne;
-
- return (0);
- }
-
- /*
- * If we have a valid config but cannot read any of these fields, then
- * it means we have a half-initialized label. In vdev_label_init()
- * we write a label with txg == 0 so that we can identify the device
- * in case the user refers to the same disk later on. If we fail to
- * create the pool, we'll be left with a label in this state
- * which should not be considered part of a valid pool.
- */
- if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
- &pool_guid) != 0 ||
- nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
- &vdev_guid) != 0 ||
- nvlist_lookup_uint64(config, ZPOOL_CONFIG_TOP_GUID,
- &top_guid) != 0 ||
- nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG,
- &txg) != 0 || txg == 0) {
- return (0);
- }
-
- /*
- * First, see if we know about this pool. If not, then add it to the
- * list of known pools.
- */
- for (pe = pl->pools; pe != NULL; pe = pe->pe_next) {
- if (pe->pe_guid == pool_guid)
- break;
- }
-
- if (pe == NULL) {
- if ((pe = zfs_alloc(hdl, sizeof (pool_entry_t))) == NULL) {
- return (-1);
- }
- pe->pe_guid = pool_guid;
- pe->pe_next = pl->pools;
- pl->pools = pe;
- }
-
- /*
- * Second, see if we know about this toplevel vdev. Add it if its
- * missing.
- */
- for (ve = pe->pe_vdevs; ve != NULL; ve = ve->ve_next) {
- if (ve->ve_guid == top_guid)
- break;
- }
-
- if (ve == NULL) {
- if ((ve = zfs_alloc(hdl, sizeof (vdev_entry_t))) == NULL) {
- return (-1);
- }
- ve->ve_guid = top_guid;
- ve->ve_next = pe->pe_vdevs;
- pe->pe_vdevs = ve;
- }
-
- /*
- * Third, see if we have a config with a matching transaction group. If
- * so, then we do nothing. Otherwise, add it to the list of known
- * configs.
- */
- for (ce = ve->ve_configs; ce != NULL; ce = ce->ce_next) {
- if (ce->ce_txg == txg)
- break;
- }
-
- if (ce == NULL) {
- if ((ce = zfs_alloc(hdl, sizeof (config_entry_t))) == NULL) {
- return (-1);
- }
- ce->ce_txg = txg;
- ce->ce_config = fnvlist_dup(config);
- ce->ce_next = ve->ve_configs;
- ve->ve_configs = ce;
- }
-
- /*
- * At this point we've successfully added our config to the list of
- * known configs. The last thing to do is add the vdev guid -> path
- * mappings so that we can fix up the configuration as necessary before
- * doing the import.
- */
- if ((ne = zfs_alloc(hdl, sizeof (name_entry_t))) == NULL)
- return (-1);
-
- if ((ne->ne_name = zfs_strdup(hdl, path)) == NULL) {
- free(ne);
- return (-1);
- }
-
- ne->ne_guid = vdev_guid;
- ne->ne_next = pl->names;
- pl->names = ne;
-
- return (0);
-}
-
-/*
- * Returns true if the named pool matches the given GUID.
- */
-static int
-pool_active(libzfs_handle_t *hdl, const char *name, uint64_t guid,
- boolean_t *isactive)
-{
- zpool_handle_t *zhp;
- uint64_t theguid;
-
- if (zpool_open_silent(hdl, name, &zhp) != 0)
- return (-1);
-
- if (zhp == NULL) {
- *isactive = B_FALSE;
- return (0);
- }
-
- verify(nvlist_lookup_uint64(zhp->zpool_config, ZPOOL_CONFIG_POOL_GUID,
- &theguid) == 0);
-
- zpool_close(zhp);
-
- *isactive = (theguid == guid);
- return (0);
-}
-
-static nvlist_t *
-refresh_config(libzfs_handle_t *hdl, nvlist_t *config)
-{
- nvlist_t *nvl;
- zfs_cmd_t zc = { 0 };
- int err, dstbuf_size;
-
- if (zcmd_write_conf_nvlist(hdl, &zc, config) != 0)
- return (NULL);
-
- dstbuf_size = MAX(CONFIG_BUF_MINSIZE, zc.zc_nvlist_conf_size * 4);
-
- if (zcmd_alloc_dst_nvlist(hdl, &zc, dstbuf_size) != 0) {
- zcmd_free_nvlists(&zc);
- return (NULL);
- }
-
- while ((err = ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_TRYIMPORT,
- &zc)) != 0 && errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- zcmd_free_nvlists(&zc);
- return (NULL);
- }
- }
-
- if (err) {
- zcmd_free_nvlists(&zc);
- return (NULL);
- }
-
- if (zcmd_read_dst_nvlist(hdl, &zc, &nvl) != 0) {
- zcmd_free_nvlists(&zc);
- return (NULL);
- }
-
- zcmd_free_nvlists(&zc);
- return (nvl);
-}
-
-/*
- * Determine if the vdev id is a hole in the namespace.
- */
-boolean_t
-vdev_is_hole(uint64_t *hole_array, uint_t holes, uint_t id)
-{
- for (int c = 0; c < holes; c++) {
-
- /* Top-level is a hole */
- if (hole_array[c] == id)
- return (B_TRUE);
- }
- return (B_FALSE);
-}
-
-/*
- * Convert our list of pools into the definitive set of configurations. We
- * start by picking the best config for each toplevel vdev. Once that's done,
- * we assemble the toplevel vdevs into a full config for the pool. We make a
- * pass to fix up any incorrect paths, and then add it to the main list to
- * return to the user.
- */
-static nvlist_t *
-get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok,
- nvlist_t *policy)
-{
- pool_entry_t *pe;
- vdev_entry_t *ve;
- config_entry_t *ce;
- nvlist_t *ret = NULL, *config = NULL, *tmp = NULL, *nvtop, *nvroot;
- nvlist_t **spares, **l2cache;
- uint_t i, nspares, nl2cache;
- boolean_t config_seen;
- uint64_t best_txg;
- char *name, *hostname = NULL;
- uint64_t guid;
- uint_t children = 0;
- nvlist_t **child = NULL;
- uint_t holes;
- uint64_t *hole_array, max_id;
- uint_t c;
- boolean_t isactive;
- uint64_t hostid;
- nvlist_t *nvl;
- boolean_t found_one = B_FALSE;
- boolean_t valid_top_config = B_FALSE;
-
- if (nvlist_alloc(&ret, 0, 0) != 0)
- goto nomem;
-
- for (pe = pl->pools; pe != NULL; pe = pe->pe_next) {
- uint64_t id, max_txg = 0;
-
- if (nvlist_alloc(&config, NV_UNIQUE_NAME, 0) != 0)
- goto nomem;
- config_seen = B_FALSE;
-
- /*
- * Iterate over all toplevel vdevs. Grab the pool configuration
- * from the first one we find, and then go through the rest and
- * add them as necessary to the 'vdevs' member of the config.
- */
- for (ve = pe->pe_vdevs; ve != NULL; ve = ve->ve_next) {
-
- /*
- * Determine the best configuration for this vdev by
- * selecting the config with the latest transaction
- * group.
- */
- best_txg = 0;
- for (ce = ve->ve_configs; ce != NULL;
- ce = ce->ce_next) {
-
- if (ce->ce_txg > best_txg) {
- tmp = ce->ce_config;
- best_txg = ce->ce_txg;
- }
- }
-
- /*
- * We rely on the fact that the max txg for the
- * pool will contain the most up-to-date information
- * about the valid top-levels in the vdev namespace.
- */
- if (best_txg > max_txg) {
- (void) nvlist_remove(config,
- ZPOOL_CONFIG_VDEV_CHILDREN,
- DATA_TYPE_UINT64);
- (void) nvlist_remove(config,
- ZPOOL_CONFIG_HOLE_ARRAY,
- DATA_TYPE_UINT64_ARRAY);
-
- max_txg = best_txg;
- hole_array = NULL;
- holes = 0;
- max_id = 0;
- valid_top_config = B_FALSE;
-
- if (nvlist_lookup_uint64(tmp,
- ZPOOL_CONFIG_VDEV_CHILDREN, &max_id) == 0) {
- verify(nvlist_add_uint64(config,
- ZPOOL_CONFIG_VDEV_CHILDREN,
- max_id) == 0);
- valid_top_config = B_TRUE;
- }
-
- if (nvlist_lookup_uint64_array(tmp,
- ZPOOL_CONFIG_HOLE_ARRAY, &hole_array,
- &holes) == 0) {
- verify(nvlist_add_uint64_array(config,
- ZPOOL_CONFIG_HOLE_ARRAY,
- hole_array, holes) == 0);
- }
- }
-
- if (!config_seen) {
- /*
- * Copy the relevant pieces of data to the pool
- * configuration:
- *
- * version
- * pool guid
- * name
- * comment (if available)
- * pool state
- * hostid (if available)
- * hostname (if available)
- */
- uint64_t state, version;
- char *comment = NULL;
-
- version = fnvlist_lookup_uint64(tmp,
- ZPOOL_CONFIG_VERSION);
- fnvlist_add_uint64(config,
- ZPOOL_CONFIG_VERSION, version);
- guid = fnvlist_lookup_uint64(tmp,
- ZPOOL_CONFIG_POOL_GUID);
- fnvlist_add_uint64(config,
- ZPOOL_CONFIG_POOL_GUID, guid);
- name = fnvlist_lookup_string(tmp,
- ZPOOL_CONFIG_POOL_NAME);
- fnvlist_add_string(config,
- ZPOOL_CONFIG_POOL_NAME, name);
-
- if (nvlist_lookup_string(tmp,
- ZPOOL_CONFIG_COMMENT, &comment) == 0)
- fnvlist_add_string(config,
- ZPOOL_CONFIG_COMMENT, comment);
-
- state = fnvlist_lookup_uint64(tmp,
- ZPOOL_CONFIG_POOL_STATE);
- fnvlist_add_uint64(config,
- ZPOOL_CONFIG_POOL_STATE, state);
-
- hostid = 0;
- if (nvlist_lookup_uint64(tmp,
- ZPOOL_CONFIG_HOSTID, &hostid) == 0) {
- fnvlist_add_uint64(config,
- ZPOOL_CONFIG_HOSTID, hostid);
- hostname = fnvlist_lookup_string(tmp,
- ZPOOL_CONFIG_HOSTNAME);
- fnvlist_add_string(config,
- ZPOOL_CONFIG_HOSTNAME, hostname);
- }
-
- config_seen = B_TRUE;
- }
-
- /*
- * Add this top-level vdev to the child array.
- */
- verify(nvlist_lookup_nvlist(tmp,
- ZPOOL_CONFIG_VDEV_TREE, &nvtop) == 0);
- verify(nvlist_lookup_uint64(nvtop, ZPOOL_CONFIG_ID,
- &id) == 0);
-
- if (id >= children) {
- nvlist_t **newchild;
-
- newchild = zfs_alloc(hdl, (id + 1) *
- sizeof (nvlist_t *));
- if (newchild == NULL)
- goto nomem;
-
- for (c = 0; c < children; c++)
- newchild[c] = child[c];
-
- free(child);
- child = newchild;
- children = id + 1;
- }
- if (nvlist_dup(nvtop, &child[id], 0) != 0)
- goto nomem;
-
- }
-
- /*
- * If we have information about all the top-levels then
- * clean up the nvlist which we've constructed. This
- * means removing any extraneous devices that are
- * beyond the valid range or adding devices to the end
- * of our array which appear to be missing.
- */
- if (valid_top_config) {
- if (max_id < children) {
- for (c = max_id; c < children; c++)
- nvlist_free(child[c]);
- children = max_id;
- } else if (max_id > children) {
- nvlist_t **newchild;
-
- newchild = zfs_alloc(hdl, (max_id) *
- sizeof (nvlist_t *));
- if (newchild == NULL)
- goto nomem;
-
- for (c = 0; c < children; c++)
- newchild[c] = child[c];
-
- free(child);
- child = newchild;
- children = max_id;
- }
- }
-
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
- &guid) == 0);
-
- /*
- * The vdev namespace may contain holes as a result of
- * device removal. We must add them back into the vdev
- * tree before we process any missing devices.
- */
- if (holes > 0) {
- ASSERT(valid_top_config);
-
- for (c = 0; c < children; c++) {
- nvlist_t *holey;
-
- if (child[c] != NULL ||
- !vdev_is_hole(hole_array, holes, c))
- continue;
-
- if (nvlist_alloc(&holey, NV_UNIQUE_NAME,
- 0) != 0)
- goto nomem;
-
- /*
- * Holes in the namespace are treated as
- * "hole" top-level vdevs and have a
- * special flag set on them.
- */
- if (nvlist_add_string(holey,
- ZPOOL_CONFIG_TYPE,
- VDEV_TYPE_HOLE) != 0 ||
- nvlist_add_uint64(holey,
- ZPOOL_CONFIG_ID, c) != 0 ||
- nvlist_add_uint64(holey,
- ZPOOL_CONFIG_GUID, 0ULL) != 0) {
- nvlist_free(holey);
- goto nomem;
- }
- child[c] = holey;
- }
- }
-
- /*
- * Look for any missing top-level vdevs. If this is the case,
- * create a faked up 'missing' vdev as a placeholder. We cannot
- * simply compress the child array, because the kernel performs
- * certain checks to make sure the vdev IDs match their location
- * in the configuration.
- */
- for (c = 0; c < children; c++) {
- if (child[c] == NULL) {
- nvlist_t *missing;
- if (nvlist_alloc(&missing, NV_UNIQUE_NAME,
- 0) != 0)
- goto nomem;
- if (nvlist_add_string(missing,
- ZPOOL_CONFIG_TYPE,
- VDEV_TYPE_MISSING) != 0 ||
- nvlist_add_uint64(missing,
- ZPOOL_CONFIG_ID, c) != 0 ||
- nvlist_add_uint64(missing,
- ZPOOL_CONFIG_GUID, 0ULL) != 0) {
- nvlist_free(missing);
- goto nomem;
- }
- child[c] = missing;
- }
- }
-
- /*
- * Put all of this pool's top-level vdevs into a root vdev.
- */
- if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0)
- goto nomem;
- if (nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE,
- VDEV_TYPE_ROOT) != 0 ||
- nvlist_add_uint64(nvroot, ZPOOL_CONFIG_ID, 0ULL) != 0 ||
- nvlist_add_uint64(nvroot, ZPOOL_CONFIG_GUID, guid) != 0 ||
- nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
- child, children) != 0) {
- nvlist_free(nvroot);
- goto nomem;
- }
-
- for (c = 0; c < children; c++)
- nvlist_free(child[c]);
- free(child);
- children = 0;
- child = NULL;
-
- /*
- * Go through and fix up any paths and/or devids based on our
- * known list of vdev GUID -> path mappings.
- */
- if (fix_paths(nvroot, pl->names) != 0) {
- nvlist_free(nvroot);
- goto nomem;
- }
-
- /*
- * Add the root vdev to this pool's configuration.
- */
- if (nvlist_add_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- nvroot) != 0) {
- nvlist_free(nvroot);
- goto nomem;
- }
- nvlist_free(nvroot);
-
- /*
- * zdb uses this path to report on active pools that were
- * imported or created using -R.
- */
- if (active_ok)
- goto add_pool;
-
- /*
- * Determine if this pool is currently active, in which case we
- * can't actually import it.
- */
- verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
- &name) == 0);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
- &guid) == 0);
-
- if (pool_active(hdl, name, guid, &isactive) != 0)
- goto error;
-
- if (isactive) {
- nvlist_free(config);
- config = NULL;
- continue;
- }
-
- if (policy != NULL) {
- if (nvlist_add_nvlist(config, ZPOOL_LOAD_POLICY,
- policy) != 0)
- goto nomem;
- }
-
- if ((nvl = refresh_config(hdl, config)) == NULL) {
- nvlist_free(config);
- config = NULL;
- continue;
- }
-
- nvlist_free(config);
- config = nvl;
-
- /*
- * Go through and update the paths for spares, now that we have
- * them.
- */
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- &spares, &nspares) == 0) {
- for (i = 0; i < nspares; i++) {
- if (fix_paths(spares[i], pl->names) != 0)
- goto nomem;
- }
- }
-
- /*
- * Update the paths for l2cache devices.
- */
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
- &l2cache, &nl2cache) == 0) {
- for (i = 0; i < nl2cache; i++) {
- if (fix_paths(l2cache[i], pl->names) != 0)
- goto nomem;
- }
- }
-
- /*
- * Restore the original information read from the actual label.
- */
- (void) nvlist_remove(config, ZPOOL_CONFIG_HOSTID,
- DATA_TYPE_UINT64);
- (void) nvlist_remove(config, ZPOOL_CONFIG_HOSTNAME,
- DATA_TYPE_STRING);
- if (hostid != 0) {
- verify(nvlist_add_uint64(config, ZPOOL_CONFIG_HOSTID,
- hostid) == 0);
- verify(nvlist_add_string(config, ZPOOL_CONFIG_HOSTNAME,
- hostname) == 0);
- }
-
-add_pool:
- /*
- * Add this pool to the list of configs.
- */
- verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
- &name) == 0);
- if (nvlist_add_nvlist(ret, name, config) != 0)
- goto nomem;
-
- found_one = B_TRUE;
- nvlist_free(config);
- config = NULL;
- }
-
- if (!found_one) {
- nvlist_free(ret);
- ret = NULL;
- }
-
- return (ret);
-
-nomem:
- (void) no_memory(hdl);
-error:
- nvlist_free(config);
- nvlist_free(ret);
- for (c = 0; c < children; c++)
- nvlist_free(child[c]);
- free(child);
-
- return (NULL);
-}
-
-/*
- * Return the offset of the given label.
- */
-static uint64_t
-label_offset(uint64_t size, int l)
-{
- ASSERT(P2PHASE_TYPED(size, sizeof (vdev_label_t), uint64_t) == 0);
- return (l * sizeof (vdev_label_t) + (l < VDEV_LABELS / 2 ?
- 0 : size - VDEV_LABELS * sizeof (vdev_label_t)));
-}
-
-/*
- * Given a file descriptor, read the label information and return an nvlist
- * describing the configuration, if there is one.
- * Return 0 on success, or -1 on failure
- */
-int
-zpool_read_label(int fd, nvlist_t **config)
-{
- struct stat64 statbuf;
- int l;
- vdev_label_t *label;
- uint64_t state, txg, size;
-
- *config = NULL;
-
- if (fstat64(fd, &statbuf) == -1)
- return (-1);
- size = P2ALIGN_TYPED(statbuf.st_size, sizeof (vdev_label_t), uint64_t);
-
- if ((label = malloc(sizeof (vdev_label_t))) == NULL)
- return (-1);
-
- for (l = 0; l < VDEV_LABELS; l++) {
- if (pread64(fd, label, sizeof (vdev_label_t),
- label_offset(size, l)) != sizeof (vdev_label_t))
- continue;
-
- if (nvlist_unpack(label->vl_vdev_phys.vp_nvlist,
- sizeof (label->vl_vdev_phys.vp_nvlist), config, 0) != 0)
- continue;
-
- if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_STATE,
- &state) != 0 || state > POOL_STATE_L2CACHE) {
- nvlist_free(*config);
- continue;
- }
-
- if (state != POOL_STATE_SPARE && state != POOL_STATE_L2CACHE &&
- (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_TXG,
- &txg) != 0 || txg == 0)) {
- nvlist_free(*config);
- continue;
- }
-
- free(label);
- return (0);
- }
-
- free(label);
- *config = NULL;
- errno = ENOENT;
- return (-1);
-}
-
-/*
- * Given a file descriptor, read the label information and return an nvlist
- * describing the configuration, if there is one.
- * returns the number of valid labels found
- * If a label is found, returns it via config. The caller is responsible for
- * freeing it.
- */
-int
-zpool_read_all_labels(int fd, nvlist_t **config)
-{
- struct stat64 statbuf;
- struct aiocb aiocbs[VDEV_LABELS];
- struct aiocb *aiocbps[VDEV_LABELS];
- int l;
- vdev_phys_t *labels;
- uint64_t state, txg, size;
- int nlabels = 0;
-
- *config = NULL;
-
- if (fstat64(fd, &statbuf) == -1)
- return (0);
- size = P2ALIGN_TYPED(statbuf.st_size, sizeof (vdev_label_t), uint64_t);
-
- if ((labels = calloc(VDEV_LABELS, sizeof (vdev_phys_t))) == NULL)
- return (0);
-
- memset(aiocbs, 0, sizeof(aiocbs));
- for (l = 0; l < VDEV_LABELS; l++) {
- aiocbs[l].aio_fildes = fd;
- aiocbs[l].aio_offset = label_offset(size, l) + VDEV_SKIP_SIZE;
- aiocbs[l].aio_buf = &labels[l];
- aiocbs[l].aio_nbytes = sizeof(vdev_phys_t);
- aiocbs[l].aio_lio_opcode = LIO_READ;
- aiocbps[l] = &aiocbs[l];
- }
-
- if (lio_listio(LIO_WAIT, aiocbps, VDEV_LABELS, NULL) != 0) {
- if (errno == EAGAIN || errno == EINTR || errno == EIO) {
- for (l = 0; l < VDEV_LABELS; l++) {
- errno = 0;
- int r = aio_error(&aiocbs[l]);
- if (r != EINVAL)
- (void)aio_return(&aiocbs[l]);
- }
- }
- free(labels);
- return (0);
- }
-
- for (l = 0; l < VDEV_LABELS; l++) {
- nvlist_t *temp = NULL;
-
- if (aio_return(&aiocbs[l]) != sizeof(vdev_phys_t))
- continue;
-
- if (nvlist_unpack(labels[l].vp_nvlist,
- sizeof (labels[l].vp_nvlist), &temp, 0) != 0)
- continue;
-
- if (nvlist_lookup_uint64(temp, ZPOOL_CONFIG_POOL_STATE,
- &state) != 0 || state > POOL_STATE_L2CACHE) {
- nvlist_free(temp);
- temp = NULL;
- continue;
- }
-
- if (state != POOL_STATE_SPARE && state != POOL_STATE_L2CACHE &&
- (nvlist_lookup_uint64(temp, ZPOOL_CONFIG_POOL_TXG,
- &txg) != 0 || txg == 0)) {
- nvlist_free(temp);
- temp = NULL;
- continue;
- }
- if (temp)
- *config = temp;
-
- nlabels++;
- }
-
- free(labels);
- return (nlabels);
-}
-
-typedef struct rdsk_node {
- char *rn_name;
- int rn_dfd;
- libzfs_handle_t *rn_hdl;
- nvlist_t *rn_config;
- avl_tree_t *rn_avl;
- avl_node_t rn_node;
- boolean_t rn_nozpool;
-} rdsk_node_t;
-
-static int
-slice_cache_compare(const void *arg1, const void *arg2)
-{
- const char *nm1 = ((rdsk_node_t *)arg1)->rn_name;
- const char *nm2 = ((rdsk_node_t *)arg2)->rn_name;
- char *nm1slice, *nm2slice;
- int rv;
-
- /*
- * slices zero and two are the most likely to provide results,
- * so put those first
- */
- nm1slice = strstr(nm1, "s0");
- nm2slice = strstr(nm2, "s0");
- if (nm1slice && !nm2slice) {
- return (-1);
- }
- if (!nm1slice && nm2slice) {
- return (1);
- }
- nm1slice = strstr(nm1, "s2");
- nm2slice = strstr(nm2, "s2");
- if (nm1slice && !nm2slice) {
- return (-1);
- }
- if (!nm1slice && nm2slice) {
- return (1);
- }
-
- rv = strcmp(nm1, nm2);
- if (rv == 0)
- return (0);
- return (rv > 0 ? 1 : -1);
-}
-
-#ifdef illumos
-static void
-check_one_slice(avl_tree_t *r, char *diskname, uint_t partno,
- diskaddr_t size, uint_t blksz)
-{
- rdsk_node_t tmpnode;
- rdsk_node_t *node;
- char sname[MAXNAMELEN];
-
- tmpnode.rn_name = &sname[0];
- (void) snprintf(tmpnode.rn_name, MAXNAMELEN, "%s%u",
- diskname, partno);
- /*
- * protect against division by zero for disk labels that
- * contain a bogus sector size
- */
- if (blksz == 0)
- blksz = DEV_BSIZE;
- /* too small to contain a zpool? */
- if ((size < (SPA_MINDEVSIZE / blksz)) &&
- (node = avl_find(r, &tmpnode, NULL)))
- node->rn_nozpool = B_TRUE;
-}
-#endif /* illumos */
-
-static void
-nozpool_all_slices(avl_tree_t *r, const char *sname)
-{
-#ifdef illumos
- char diskname[MAXNAMELEN];
- char *ptr;
- int i;
-
- (void) strncpy(diskname, sname, MAXNAMELEN);
- if (((ptr = strrchr(diskname, 's')) == NULL) &&
- ((ptr = strrchr(diskname, 'p')) == NULL))
- return;
- ptr[0] = 's';
- ptr[1] = '\0';
- for (i = 0; i < NDKMAP; i++)
- check_one_slice(r, diskname, i, 0, 1);
- ptr[0] = 'p';
- for (i = 0; i <= FD_NUMPART; i++)
- check_one_slice(r, diskname, i, 0, 1);
-#endif /* illumos */
-}
-
-#ifdef illumos
-static void
-check_slices(avl_tree_t *r, int fd, const char *sname)
-{
- struct extvtoc vtoc;
- struct dk_gpt *gpt;
- char diskname[MAXNAMELEN];
- char *ptr;
- int i;
-
- (void) strncpy(diskname, sname, MAXNAMELEN);
- if ((ptr = strrchr(diskname, 's')) == NULL || !isdigit(ptr[1]))
- return;
- ptr[1] = '\0';
-
- if (read_extvtoc(fd, &vtoc) >= 0) {
- for (i = 0; i < NDKMAP; i++)
- check_one_slice(r, diskname, i,
- vtoc.v_part[i].p_size, vtoc.v_sectorsz);
- } else if (efi_alloc_and_read(fd, &gpt) >= 0) {
- /*
- * on x86 we'll still have leftover links that point
- * to slices s[9-15], so use NDKMAP instead
- */
- for (i = 0; i < NDKMAP; i++)
- check_one_slice(r, diskname, i,
- gpt->efi_parts[i].p_size, gpt->efi_lbasize);
- /* nodes p[1-4] are never used with EFI labels */
- ptr[0] = 'p';
- for (i = 1; i <= FD_NUMPART; i++)
- check_one_slice(r, diskname, i, 0, 1);
- efi_free(gpt);
- }
-}
-#endif /* illumos */
-
-static void
-zpool_open_func(void *arg)
-{
- rdsk_node_t *rn = arg;
- struct stat64 statbuf;
- nvlist_t *config;
- int fd;
-
- if (rn->rn_nozpool)
- return;
- if ((fd = openat64(rn->rn_dfd, rn->rn_name, O_RDONLY)) < 0) {
- /* symlink to a device that's no longer there */
- if (errno == ENOENT)
- nozpool_all_slices(rn->rn_avl, rn->rn_name);
- return;
- }
- /*
- * Ignore failed stats. We only want regular
- * files, character devs and block devs.
- */
- if (fstat64(fd, &statbuf) != 0 ||
- (!S_ISREG(statbuf.st_mode) &&
- !S_ISCHR(statbuf.st_mode) &&
- !S_ISBLK(statbuf.st_mode))) {
- (void) close(fd);
- return;
- }
- /* this file is too small to hold a zpool */
-#ifdef illumos
- if (S_ISREG(statbuf.st_mode) &&
- statbuf.st_size < SPA_MINDEVSIZE) {
- (void) close(fd);
- return;
- } else if (!S_ISREG(statbuf.st_mode)) {
- /*
- * Try to read the disk label first so we don't have to
- * open a bunch of minor nodes that can't have a zpool.
- */
- check_slices(rn->rn_avl, fd, rn->rn_name);
- }
-#else /* !illumos */
- if (statbuf.st_size < SPA_MINDEVSIZE) {
- (void) close(fd);
- return;
- }
-#endif /* illumos */
-
- if ((zpool_read_label(fd, &config)) != 0 && errno == ENOMEM) {
- (void) close(fd);
- (void) no_memory(rn->rn_hdl);
- return;
- }
- (void) close(fd);
-
- rn->rn_config = config;
-}
-
-/*
- * Given a file descriptor, clear (zero) the label information.
- */
-int
-zpool_clear_label(int fd)
-{
- struct stat64 statbuf;
- int l;
- vdev_label_t *label;
- uint64_t size;
-
- if (fstat64(fd, &statbuf) == -1)
- return (0);
- size = P2ALIGN_TYPED(statbuf.st_size, sizeof (vdev_label_t), uint64_t);
-
- if ((label = calloc(sizeof (vdev_label_t), 1)) == NULL)
- return (-1);
-
- for (l = 0; l < VDEV_LABELS; l++) {
- if (pwrite64(fd, label, sizeof (vdev_label_t),
- label_offset(size, l)) != sizeof (vdev_label_t)) {
- free(label);
- return (-1);
- }
- }
-
- free(label);
- return (0);
-}
-
-/*
- * Given a list of directories to search, find all pools stored on disk. This
- * includes partial pools which are not available to import. If no args are
- * given (argc is 0), then the default directory (/dev/dsk) is searched.
- * poolname or guid (but not both) are provided by the caller when trying
- * to import a specific pool.
- */
-static nvlist_t *
-zpool_find_import_impl(libzfs_handle_t *hdl, importargs_t *iarg)
-{
- int i, dirs = iarg->paths;
- struct dirent64 *dp;
- char path[MAXPATHLEN];
- char *end, **dir = iarg->path;
- size_t pathleft;
- nvlist_t *ret = NULL;
- static char *default_dir = "/dev";
- pool_list_t pools = { 0 };
- pool_entry_t *pe, *penext;
- vdev_entry_t *ve, *venext;
- config_entry_t *ce, *cenext;
- name_entry_t *ne, *nenext;
- avl_tree_t slice_cache;
- rdsk_node_t *slice;
- void *cookie;
- boolean_t skip_zvols = B_FALSE;
- int value;
- size_t size = sizeof(value);
-
- if (dirs == 0) {
- dirs = 1;
- dir = &default_dir;
- }
-
- if (sysctlbyname("vfs.zfs.vol.recursive", &value, &size, NULL, 0) == 0
- && value == 0) {
- skip_zvols = B_TRUE;
- }
-
- /*
- * Go through and read the label configuration information from every
- * possible device, organizing the information according to pool GUID
- * and toplevel GUID.
- */
- for (i = 0; i < dirs; i++) {
- tpool_t *t;
- char rdsk[MAXPATHLEN];
- int dfd;
- boolean_t config_failed = B_FALSE;
- DIR *dirp;
-
- /* use realpath to normalize the path */
- if (realpath(dir[i], path) == 0) {
- (void) zfs_error_fmt(hdl, EZFS_BADPATH,
- dgettext(TEXT_DOMAIN, "cannot open '%s'"), dir[i]);
- goto error;
- }
- end = &path[strlen(path)];
- *end++ = '/';
- *end = 0;
- pathleft = &path[sizeof (path)] - end;
-
-#ifdef illumos
- /*
- * Using raw devices instead of block devices when we're
- * reading the labels skips a bunch of slow operations during
- * close(2) processing, so we replace /dev/dsk with /dev/rdsk.
- */
- if (strcmp(path, ZFS_DISK_ROOTD) == 0)
- (void) strlcpy(rdsk, ZFS_RDISK_ROOTD, sizeof (rdsk));
- else
-#endif
- (void) strlcpy(rdsk, path, sizeof (rdsk));
-
- if ((dfd = open64(rdsk, O_RDONLY)) < 0 ||
- (dirp = fdopendir(dfd)) == NULL) {
- if (dfd >= 0)
- (void) close(dfd);
- zfs_error_aux(hdl, strerror(errno));
- (void) zfs_error_fmt(hdl, EZFS_BADPATH,
- dgettext(TEXT_DOMAIN, "cannot open '%s'"),
- rdsk);
- goto error;
- }
-
- avl_create(&slice_cache, slice_cache_compare,
- sizeof (rdsk_node_t), offsetof(rdsk_node_t, rn_node));
-
- if (strcmp(rdsk, "/dev/") == 0) {
- struct gmesh mesh;
- struct gclass *mp;
- struct ggeom *gp;
- struct gprovider *pp;
-
- errno = geom_gettree(&mesh);
- if (errno != 0) {
- zfs_error_aux(hdl, strerror(errno));
- (void) zfs_error_fmt(hdl, EZFS_BADPATH,
- dgettext(TEXT_DOMAIN, "cannot get GEOM tree"));
- goto error;
- }
-
- LIST_FOREACH(mp, &mesh.lg_class, lg_class) {
- if (skip_zvols &&
- strcmp(mp->lg_name, "ZFS::ZVOL") == 0) {
- continue;
- }
- LIST_FOREACH(gp, &mp->lg_geom, lg_geom) {
- LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
- slice = zfs_alloc(hdl, sizeof (rdsk_node_t));
- slice->rn_name = zfs_strdup(hdl, pp->lg_name);
- slice->rn_avl = &slice_cache;
- slice->rn_dfd = dfd;
- slice->rn_hdl = hdl;
- slice->rn_nozpool = B_FALSE;
- avl_add(&slice_cache, slice);
- }
- }
- }
-
- geom_deletetree(&mesh);
- goto skipdir;
- }
-
- /*
- * This is not MT-safe, but we have no MT consumers of libzfs
- */
- while ((dp = readdir64(dirp)) != NULL) {
- const char *name = dp->d_name;
- if (name[0] == '.' &&
- (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
- continue;
-
- slice = zfs_alloc(hdl, sizeof (rdsk_node_t));
- slice->rn_name = zfs_strdup(hdl, name);
- slice->rn_avl = &slice_cache;
- slice->rn_dfd = dfd;
- slice->rn_hdl = hdl;
- slice->rn_nozpool = B_FALSE;
- avl_add(&slice_cache, slice);
- }
-skipdir:
- /*
- * create a thread pool to do all of this in parallel;
- * rn_nozpool is not protected, so this is racy in that
- * multiple tasks could decide that the same slice can
- * not hold a zpool, which is benign. Also choose
- * double the number of processors; we hold a lot of
- * locks in the kernel, so going beyond this doesn't
- * buy us much.
- */
- t = tpool_create(1, 2 * sysconf(_SC_NPROCESSORS_ONLN),
- 0, NULL);
- for (slice = avl_first(&slice_cache); slice;
- (slice = avl_walk(&slice_cache, slice,
- AVL_AFTER)))
- (void) tpool_dispatch(t, zpool_open_func, slice);
- tpool_wait(t);
- tpool_destroy(t);
-
- cookie = NULL;
- while ((slice = avl_destroy_nodes(&slice_cache,
- &cookie)) != NULL) {
- if (slice->rn_config != NULL && !config_failed) {
- nvlist_t *config = slice->rn_config;
- boolean_t matched = B_TRUE;
-
- if (iarg->poolname != NULL) {
- char *pname;
-
- matched = nvlist_lookup_string(config,
- ZPOOL_CONFIG_POOL_NAME,
- &pname) == 0 &&
- strcmp(iarg->poolname, pname) == 0;
- } else if (iarg->guid != 0) {
- uint64_t this_guid;
-
- matched = nvlist_lookup_uint64(config,
- ZPOOL_CONFIG_POOL_GUID,
- &this_guid) == 0 &&
- iarg->guid == this_guid;
- }
- if (matched) {
- /*
- * use the non-raw path for the config
- */
- (void) strlcpy(end, slice->rn_name,
- pathleft);
- if (add_config(hdl, &pools, path,
- config) != 0)
- config_failed = B_TRUE;
- }
- nvlist_free(config);
- }
- free(slice->rn_name);
- free(slice);
- }
- avl_destroy(&slice_cache);
-
- (void) closedir(dirp);
-
- if (config_failed)
- goto error;
- }
-
- ret = get_configs(hdl, &pools, iarg->can_be_active, iarg->policy);
-
-error:
- for (pe = pools.pools; pe != NULL; pe = penext) {
- penext = pe->pe_next;
- for (ve = pe->pe_vdevs; ve != NULL; ve = venext) {
- venext = ve->ve_next;
- for (ce = ve->ve_configs; ce != NULL; ce = cenext) {
- cenext = ce->ce_next;
- nvlist_free(ce->ce_config);
- free(ce);
- }
- free(ve);
- }
- free(pe);
- }
-
- for (ne = pools.names; ne != NULL; ne = nenext) {
- nenext = ne->ne_next;
- free(ne->ne_name);
- free(ne);
- }
-
- return (ret);
-}
-
-nvlist_t *
-zpool_find_import(libzfs_handle_t *hdl, int argc, char **argv)
-{
- importargs_t iarg = { 0 };
-
- iarg.paths = argc;
- iarg.path = argv;
-
- return (zpool_find_import_impl(hdl, &iarg));
-}
-
-/*
- * Given a cache file, return the contents as a list of importable pools.
- * poolname or guid (but not both) are provided by the caller when trying
- * to import a specific pool.
- */
-nvlist_t *
-zpool_find_import_cached(libzfs_handle_t *hdl, const char *cachefile,
- char *poolname, uint64_t guid)
-{
- char *buf;
- int fd;
- struct stat64 statbuf;
- nvlist_t *raw, *src, *dst;
- nvlist_t *pools;
- nvpair_t *elem;
- char *name;
- uint64_t this_guid;
- boolean_t active;
-
- verify(poolname == NULL || guid == 0);
-
- if ((fd = open(cachefile, O_RDONLY)) < 0) {
- zfs_error_aux(hdl, "%s", strerror(errno));
- (void) zfs_error(hdl, EZFS_BADCACHE,
- dgettext(TEXT_DOMAIN, "failed to open cache file"));
- return (NULL);
- }
-
- if (fstat64(fd, &statbuf) != 0) {
- zfs_error_aux(hdl, "%s", strerror(errno));
- (void) close(fd);
- (void) zfs_error(hdl, EZFS_BADCACHE,
- dgettext(TEXT_DOMAIN, "failed to get size of cache file"));
- return (NULL);
- }
-
- if ((buf = zfs_alloc(hdl, statbuf.st_size)) == NULL) {
- (void) close(fd);
- return (NULL);
- }
-
- if (read(fd, buf, statbuf.st_size) != statbuf.st_size) {
- (void) close(fd);
- free(buf);
- (void) zfs_error(hdl, EZFS_BADCACHE,
- dgettext(TEXT_DOMAIN,
- "failed to read cache file contents"));
- return (NULL);
- }
-
- (void) close(fd);
-
- if (nvlist_unpack(buf, statbuf.st_size, &raw, 0) != 0) {
- free(buf);
- (void) zfs_error(hdl, EZFS_BADCACHE,
- dgettext(TEXT_DOMAIN,
- "invalid or corrupt cache file contents"));
- return (NULL);
- }
-
- free(buf);
-
- /*
- * Go through and get the current state of the pools and refresh their
- * state.
- */
- if (nvlist_alloc(&pools, 0, 0) != 0) {
- (void) no_memory(hdl);
- nvlist_free(raw);
- return (NULL);
- }
-
- elem = NULL;
- while ((elem = nvlist_next_nvpair(raw, elem)) != NULL) {
- src = fnvpair_value_nvlist(elem);
-
- name = fnvlist_lookup_string(src, ZPOOL_CONFIG_POOL_NAME);
- if (poolname != NULL && strcmp(poolname, name) != 0)
- continue;
-
- this_guid = fnvlist_lookup_uint64(src, ZPOOL_CONFIG_POOL_GUID);
- if (guid != 0 && guid != this_guid)
- continue;
-
- if (pool_active(hdl, name, this_guid, &active) != 0) {
- nvlist_free(raw);
- nvlist_free(pools);
- return (NULL);
- }
-
- if (active)
- continue;
-
- if (nvlist_add_string(src, ZPOOL_CONFIG_CACHEFILE,
- cachefile) != 0) {
- (void) no_memory(hdl);
- nvlist_free(raw);
- nvlist_free(pools);
- return (NULL);
- }
-
- if ((dst = refresh_config(hdl, src)) == NULL) {
- nvlist_free(raw);
- nvlist_free(pools);
- return (NULL);
- }
-
- if (nvlist_add_nvlist(pools, nvpair_name(elem), dst) != 0) {
- (void) no_memory(hdl);
- nvlist_free(dst);
- nvlist_free(raw);
- nvlist_free(pools);
- return (NULL);
- }
- nvlist_free(dst);
- }
-
- nvlist_free(raw);
- return (pools);
-}
-
-static int
-name_or_guid_exists(zpool_handle_t *zhp, void *data)
-{
- importargs_t *import = data;
- int found = 0;
-
- if (import->poolname != NULL) {
- char *pool_name;
-
- verify(nvlist_lookup_string(zhp->zpool_config,
- ZPOOL_CONFIG_POOL_NAME, &pool_name) == 0);
- if (strcmp(pool_name, import->poolname) == 0)
- found = 1;
- } else {
- uint64_t pool_guid;
-
- verify(nvlist_lookup_uint64(zhp->zpool_config,
- ZPOOL_CONFIG_POOL_GUID, &pool_guid) == 0);
- if (pool_guid == import->guid)
- found = 1;
- }
-
- zpool_close(zhp);
- return (found);
-}
-
-nvlist_t *
-zpool_search_import(libzfs_handle_t *hdl, importargs_t *import)
-{
- nvlist_t *pools = NULL;
-
- verify(import->poolname == NULL || import->guid == 0);
-
- if (import->unique)
- import->exists = zpool_iter(hdl, name_or_guid_exists, import);
-
- if (import->cachefile != NULL)
- pools = zpool_find_import_cached(hdl, import->cachefile,
- import->poolname, import->guid);
- else
- pools = zpool_find_import_impl(hdl, import);
-
- return (pools);
-}
-
-static boolean_t
-pool_match(nvlist_t *cfg, char *tgt)
-{
- uint64_t v, guid = strtoull(tgt, NULL, 0);
- char *s;
-
- if (guid != 0) {
- if (nvlist_lookup_uint64(cfg, ZPOOL_CONFIG_POOL_GUID, &v) == 0)
- return (v == guid);
- } else {
- if (nvlist_lookup_string(cfg, ZPOOL_CONFIG_POOL_NAME, &s) == 0)
- return (strcmp(s, tgt) == 0);
- }
- return (B_FALSE);
-}
-
-int
-zpool_tryimport(libzfs_handle_t *hdl, char *target, nvlist_t **configp,
- importargs_t *args)
-{
- nvlist_t *pools;
- nvlist_t *match = NULL;
- nvlist_t *config = NULL;
- char *sepp = NULL;
- int count = 0;
- char *targetdup = strdup(target);
-
- *configp = NULL;
-
- if ((sepp = strpbrk(targetdup, "/@")) != NULL) {
- *sepp = '\0';
- }
-
- pools = zpool_search_import(hdl, args);
-
- if (pools != NULL) {
- nvpair_t *elem = NULL;
- while ((elem = nvlist_next_nvpair(pools, elem)) != NULL) {
- VERIFY0(nvpair_value_nvlist(elem, &config));
- if (pool_match(config, targetdup)) {
- count++;
- if (match != NULL) {
- /* multiple matches found */
- continue;
- } else {
- match = config;
- }
- }
- }
- }
-
- if (count == 0) {
- free(targetdup);
- return (ENOENT);
- }
-
- if (count > 1) {
- free(targetdup);
- return (EINVAL);
- }
-
- *configp = match;
- free(targetdup);
-
- return (0);
-}
-
-boolean_t
-find_guid(nvlist_t *nv, uint64_t guid)
-{
- uint64_t tmp;
- nvlist_t **child;
- uint_t c, children;
-
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &tmp) == 0);
- if (tmp == guid)
- return (B_TRUE);
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) == 0) {
- for (c = 0; c < children; c++)
- if (find_guid(child[c], guid))
- return (B_TRUE);
- }
-
- return (B_FALSE);
-}
-
-typedef struct aux_cbdata {
- const char *cb_type;
- uint64_t cb_guid;
- zpool_handle_t *cb_zhp;
-} aux_cbdata_t;
-
-static int
-find_aux(zpool_handle_t *zhp, void *data)
-{
- aux_cbdata_t *cbp = data;
- nvlist_t **list;
- uint_t i, count;
- uint64_t guid;
- nvlist_t *nvroot;
-
- verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
-
- if (nvlist_lookup_nvlist_array(nvroot, cbp->cb_type,
- &list, &count) == 0) {
- for (i = 0; i < count; i++) {
- verify(nvlist_lookup_uint64(list[i],
- ZPOOL_CONFIG_GUID, &guid) == 0);
- if (guid == cbp->cb_guid) {
- cbp->cb_zhp = zhp;
- return (1);
- }
- }
- }
-
- zpool_close(zhp);
- return (0);
-}
-
-/*
- * Determines if the pool is in use. If so, it returns true and the state of
- * the pool as well as the name of the pool. Both strings are allocated and
- * must be freed by the caller.
- */
-int
-zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr,
- boolean_t *inuse)
-{
- nvlist_t *config;
- char *name;
- boolean_t ret;
- uint64_t guid, vdev_guid;
- zpool_handle_t *zhp;
- nvlist_t *pool_config;
- uint64_t stateval, isspare;
- aux_cbdata_t cb = { 0 };
- boolean_t isactive;
-
- *inuse = B_FALSE;
-
- if (zpool_read_label(fd, &config) != 0 && errno == ENOMEM) {
- (void) no_memory(hdl);
- return (-1);
- }
-
- if (config == NULL)
- return (0);
-
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
- &stateval) == 0);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
- &vdev_guid) == 0);
-
- if (stateval != POOL_STATE_SPARE && stateval != POOL_STATE_L2CACHE) {
- verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
- &name) == 0);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
- &guid) == 0);
- }
-
- switch (stateval) {
- case POOL_STATE_EXPORTED:
- /*
- * A pool with an exported state may in fact be imported
- * read-only, so check the in-core state to see if it's
- * active and imported read-only. If it is, set
- * its state to active.
- */
- if (pool_active(hdl, name, guid, &isactive) == 0 && isactive &&
- (zhp = zpool_open_canfail(hdl, name)) != NULL) {
- if (zpool_get_prop_int(zhp, ZPOOL_PROP_READONLY, NULL))
- stateval = POOL_STATE_ACTIVE;
-
- /*
- * All we needed the zpool handle for is the
- * readonly prop check.
- */
- zpool_close(zhp);
- }
-
- ret = B_TRUE;
- break;
-
- case POOL_STATE_ACTIVE:
- /*
- * For an active pool, we have to determine if it's really part
- * of a currently active pool (in which case the pool will exist
- * and the guid will be the same), or whether it's part of an
- * active pool that was disconnected without being explicitly
- * exported.
- */
- if (pool_active(hdl, name, guid, &isactive) != 0) {
- nvlist_free(config);
- return (-1);
- }
-
- if (isactive) {
- /*
- * Because the device may have been removed while
- * offlined, we only report it as active if the vdev is
- * still present in the config. Otherwise, pretend like
- * it's not in use.
- */
- if ((zhp = zpool_open_canfail(hdl, name)) != NULL &&
- (pool_config = zpool_get_config(zhp, NULL))
- != NULL) {
- nvlist_t *nvroot;
-
- verify(nvlist_lookup_nvlist(pool_config,
- ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
- ret = find_guid(nvroot, vdev_guid);
- } else {
- ret = B_FALSE;
- }
-
- /*
- * If this is an active spare within another pool, we
- * treat it like an unused hot spare. This allows the
- * user to create a pool with a hot spare that currently
- * in use within another pool. Since we return B_TRUE,
- * libdiskmgt will continue to prevent generic consumers
- * from using the device.
- */
- if (ret && nvlist_lookup_uint64(config,
- ZPOOL_CONFIG_IS_SPARE, &isspare) == 0 && isspare)
- stateval = POOL_STATE_SPARE;
-
- if (zhp != NULL)
- zpool_close(zhp);
- } else {
- stateval = POOL_STATE_POTENTIALLY_ACTIVE;
- ret = B_TRUE;
- }
- break;
-
- case POOL_STATE_SPARE:
- /*
- * For a hot spare, it can be either definitively in use, or
- * potentially active. To determine if it's in use, we iterate
- * over all pools in the system and search for one with a spare
- * with a matching guid.
- *
- * Due to the shared nature of spares, we don't actually report
- * the potentially active case as in use. This means the user
- * can freely create pools on the hot spares of exported pools,
- * but to do otherwise makes the resulting code complicated, and
- * we end up having to deal with this case anyway.
- */
- cb.cb_zhp = NULL;
- cb.cb_guid = vdev_guid;
- cb.cb_type = ZPOOL_CONFIG_SPARES;
- if (zpool_iter(hdl, find_aux, &cb) == 1) {
- name = (char *)zpool_get_name(cb.cb_zhp);
- ret = B_TRUE;
- } else {
- ret = B_FALSE;
- }
- break;
-
- case POOL_STATE_L2CACHE:
-
- /*
- * Check if any pool is currently using this l2cache device.
- */
- cb.cb_zhp = NULL;
- cb.cb_guid = vdev_guid;
- cb.cb_type = ZPOOL_CONFIG_L2CACHE;
- if (zpool_iter(hdl, find_aux, &cb) == 1) {
- name = (char *)zpool_get_name(cb.cb_zhp);
- ret = B_TRUE;
- } else {
- ret = B_FALSE;
- }
- break;
-
- default:
- ret = B_FALSE;
- }
-
-
- if (ret) {
- if ((*namestr = zfs_strdup(hdl, name)) == NULL) {
- if (cb.cb_zhp)
- zpool_close(cb.cb_zhp);
- nvlist_free(config);
- return (-1);
- }
- *state = (pool_state_t)stateval;
- }
-
- if (cb.cb_zhp)
- zpool_close(cb.cb_zhp);
-
- nvlist_free(config);
- *inuse = ret;
- return (0);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c
deleted file mode 100644
index 36138676e7db3..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_iter.c
+++ /dev/null
@@ -1,546 +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) 2013, 2015 by Delphix. All rights reserved.
- * Copyright (c) 2012 Pawel Jakub Dawidek. All rights reserved.
- * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2019 Datto Inc.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <unistd.h>
-#include <stddef.h>
-#include <libintl.h>
-#include <libzfs.h>
-
-#include "libzfs_impl.h"
-
-int
-zfs_iter_clones(zfs_handle_t *zhp, zfs_iter_f func, void *data)
-{
- nvlist_t *nvl = zfs_get_clones_nvl(zhp);
- nvpair_t *pair;
-
- if (nvl == NULL)
- return (0);
-
- for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL;
- pair = nvlist_next_nvpair(nvl, pair)) {
- zfs_handle_t *clone = zfs_open(zhp->zfs_hdl, nvpair_name(pair),
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- if (clone != NULL) {
- int err = func(clone, data);
- if (err != 0)
- return (err);
- }
- }
- return (0);
-}
-
-static int
-zfs_do_list_ioctl(zfs_handle_t *zhp, unsigned long arg, zfs_cmd_t *zc)
-{
- int rc;
- uint64_t orig_cookie;
-
- orig_cookie = zc->zc_cookie;
-top:
- (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
- rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
-
- if (rc == -1) {
- switch (errno) {
- case ENOMEM:
- /* expand nvlist memory and try again */
- if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
- zcmd_free_nvlists(zc);
- return (-1);
- }
- zc->zc_cookie = orig_cookie;
- goto top;
- /*
- * An errno value of ESRCH indicates normal completion.
- * If ENOENT is returned, then the underlying dataset
- * has been removed since we obtained the handle.
- */
- case ESRCH:
- case ENOENT:
- rc = 1;
- break;
- default:
- rc = zfs_standard_error(zhp->zfs_hdl, errno,
- dgettext(TEXT_DOMAIN,
- "cannot iterate filesystems"));
- break;
- }
- }
- return (rc);
-}
-
-/*
- * Iterate over all child filesystems
- */
-int
-zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
-{
- zfs_cmd_t zc = { 0 };
- zfs_handle_t *nzhp;
- int ret;
-
- if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
- return (0);
-
- if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
- return (-1);
-
- while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
- &zc)) == 0) {
- /*
- * Silently ignore errors, as the only plausible explanation is
- * that the pool has since been removed.
- */
- if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
- &zc)) == NULL) {
- continue;
- }
-
- if ((ret = func(nzhp, data)) != 0) {
- zcmd_free_nvlists(&zc);
- return (ret);
- }
- }
- zcmd_free_nvlists(&zc);
- return ((ret < 0) ? ret : 0);
-}
-
-/*
- * Iterate over all snapshots
- */
-int
-zfs_iter_snapshots(zfs_handle_t *zhp, boolean_t simple, zfs_iter_f func,
- void *data, uint64_t min_txg, uint64_t max_txg)
-{
- zfs_cmd_t zc = { 0 };
- zfs_handle_t *nzhp;
- int ret;
- nvlist_t *range_nvl = NULL;
-
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT ||
- zhp->zfs_type == ZFS_TYPE_BOOKMARK)
- return (0);
-
- zc.zc_simple = simple;
-
- if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
- return (-1);
-
- if (min_txg != 0) {
- range_nvl = fnvlist_alloc();
- fnvlist_add_uint64(range_nvl, SNAP_ITER_MIN_TXG, min_txg);
- }
- if (max_txg != 0) {
- if (range_nvl == NULL)
- range_nvl = fnvlist_alloc();
- fnvlist_add_uint64(range_nvl, SNAP_ITER_MAX_TXG, max_txg);
- }
-
- if (range_nvl != NULL &&
- zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, range_nvl) != 0) {
- zcmd_free_nvlists(&zc);
- fnvlist_free(range_nvl);
- return (-1);
- }
-
- while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
- &zc)) == 0) {
-
- if (simple)
- nzhp = make_dataset_simple_handle_zc(zhp, &zc);
- else
- nzhp = make_dataset_handle_zc(zhp->zfs_hdl, &zc);
- if (nzhp == NULL)
- continue;
-
- if ((ret = func(nzhp, data)) != 0) {
- zcmd_free_nvlists(&zc);
- fnvlist_free(range_nvl);
- return (ret);
- }
- }
- zcmd_free_nvlists(&zc);
- fnvlist_free(range_nvl);
- return ((ret < 0) ? ret : 0);
-}
-
-/*
- * Iterate over all bookmarks
- */
-int
-zfs_iter_bookmarks(zfs_handle_t *zhp, zfs_iter_f func, void *data)
-{
- zfs_handle_t *nzhp;
- nvlist_t *props = NULL;
- nvlist_t *bmarks = NULL;
- int err;
-
- if ((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT | ZFS_TYPE_BOOKMARK)) != 0)
- return (0);
-
- /* Setup the requested properties nvlist. */
- props = fnvlist_alloc();
- fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_GUID));
- fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_CREATETXG));
- fnvlist_add_boolean(props, zfs_prop_to_name(ZFS_PROP_CREATION));
-
- if ((err = lzc_get_bookmarks(zhp->zfs_name, props, &bmarks)) != 0)
- goto out;
-
- for (nvpair_t *pair = nvlist_next_nvpair(bmarks, NULL);
- pair != NULL; pair = nvlist_next_nvpair(bmarks, pair)) {
- char name[ZFS_MAX_DATASET_NAME_LEN];
- char *bmark_name;
- nvlist_t *bmark_props;
-
- bmark_name = nvpair_name(pair);
- bmark_props = fnvpair_value_nvlist(pair);
-
- (void) snprintf(name, sizeof (name), "%s#%s", zhp->zfs_name,
- bmark_name);
-
- nzhp = make_bookmark_handle(zhp, name, bmark_props);
- if (nzhp == NULL)
- continue;
-
- if ((err = func(nzhp, data)) != 0)
- goto out;
- }
-
-out:
- fnvlist_free(props);
- fnvlist_free(bmarks);
-
- return (err);
-}
-
-/*
- * Routines for dealing with the sorted snapshot functionality
- */
-typedef struct zfs_node {
- zfs_handle_t *zn_handle;
- avl_node_t zn_avlnode;
-} zfs_node_t;
-
-static int
-zfs_sort_snaps(zfs_handle_t *zhp, void *data)
-{
- avl_tree_t *avl = data;
- zfs_node_t *node;
- zfs_node_t search;
-
- search.zn_handle = zhp;
- node = avl_find(avl, &search, NULL);
- if (node) {
- /*
- * If this snapshot was renamed while we were creating the
- * AVL tree, it's possible that we already inserted it under
- * its old name. Remove the old handle before adding the new
- * one.
- */
- zfs_close(node->zn_handle);
- avl_remove(avl, node);
- free(node);
- }
-
- node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
- node->zn_handle = zhp;
- avl_add(avl, node);
-
- return (0);
-}
-
-static int
-zfs_snapshot_compare(const void *larg, const void *rarg)
-{
- zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
- zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
- uint64_t lcreate, rcreate;
-
- /*
- * 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);
-
- return (AVL_CMP(lcreate, rcreate));
-}
-
-int
-zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data,
- uint64_t min_txg, uint64_t max_txg)
-{
- int ret = 0;
- zfs_node_t *node;
- avl_tree_t avl;
- void *cookie = NULL;
-
- avl_create(&avl, zfs_snapshot_compare,
- sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
-
- ret = zfs_iter_snapshots(zhp, B_FALSE, zfs_sort_snaps, &avl, min_txg,
- max_txg);
-
- for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
- ret |= callback(node->zn_handle, data);
-
- while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
- free(node);
-
- avl_destroy(&avl);
-
- return (ret);
-}
-
-typedef struct {
- char *ssa_first;
- char *ssa_last;
- boolean_t ssa_seenfirst;
- boolean_t ssa_seenlast;
- zfs_iter_f ssa_func;
- void *ssa_arg;
-} snapspec_arg_t;
-
-static int
-snapspec_cb(zfs_handle_t *zhp, void *arg)
-{
- snapspec_arg_t *ssa = arg;
- const char *shortsnapname;
- int err = 0;
-
- if (ssa->ssa_seenlast)
- return (0);
-
- shortsnapname = strchr(zfs_get_name(zhp), '@') + 1;
- if (!ssa->ssa_seenfirst && strcmp(shortsnapname, ssa->ssa_first) == 0)
- ssa->ssa_seenfirst = B_TRUE;
- if (strcmp(shortsnapname, ssa->ssa_last) == 0)
- ssa->ssa_seenlast = B_TRUE;
-
- if (ssa->ssa_seenfirst) {
- err = ssa->ssa_func(zhp, ssa->ssa_arg);
- } else {
- zfs_close(zhp);
- }
-
- return (err);
-}
-
-/*
- * spec is a string like "A,B%C,D"
- *
- * <snaps>, where <snaps> can be:
- * <snap> (single snapshot)
- * <snap>%<snap> (range of snapshots, inclusive)
- * %<snap> (range of snapshots, starting with earliest)
- * <snap>% (range of snapshots, ending with last)
- * % (all snapshots)
- * <snaps>[,...] (comma separated list of the above)
- *
- * If a snapshot can not be opened, continue trying to open the others, but
- * return ENOENT at the end.
- */
-int
-zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig,
- zfs_iter_f func, void *arg)
-{
- char *buf, *comma_separated, *cp;
- int err = 0;
- int ret = 0;
-
- buf = zfs_strdup(fs_zhp->zfs_hdl, spec_orig);
- cp = buf;
-
- while ((comma_separated = strsep(&cp, ",")) != NULL) {
- char *pct = strchr(comma_separated, '%');
- if (pct != NULL) {
- snapspec_arg_t ssa = { 0 };
- ssa.ssa_func = func;
- ssa.ssa_arg = arg;
-
- if (pct == comma_separated)
- ssa.ssa_seenfirst = B_TRUE;
- else
- ssa.ssa_first = comma_separated;
- *pct = '\0';
- ssa.ssa_last = pct + 1;
-
- /*
- * If there is a lastname specified, make sure it
- * exists.
- */
- if (ssa.ssa_last[0] != '\0') {
- char snapname[ZFS_MAX_DATASET_NAME_LEN];
- (void) snprintf(snapname, sizeof (snapname),
- "%s@%s", zfs_get_name(fs_zhp),
- ssa.ssa_last);
- if (!zfs_dataset_exists(fs_zhp->zfs_hdl,
- snapname, ZFS_TYPE_SNAPSHOT)) {
- ret = ENOENT;
- continue;
- }
- }
-
- err = zfs_iter_snapshots_sorted(fs_zhp,
- snapspec_cb, &ssa, 0, 0);
- if (ret == 0)
- ret = err;
- if (ret == 0 && (!ssa.ssa_seenfirst ||
- (ssa.ssa_last[0] != '\0' && !ssa.ssa_seenlast))) {
- ret = ENOENT;
- }
- } else {
- char snapname[ZFS_MAX_DATASET_NAME_LEN];
- zfs_handle_t *snap_zhp;
- (void) snprintf(snapname, sizeof (snapname), "%s@%s",
- zfs_get_name(fs_zhp), comma_separated);
- snap_zhp = make_dataset_handle(fs_zhp->zfs_hdl,
- snapname);
- if (snap_zhp == NULL) {
- ret = ENOENT;
- continue;
- }
- err = func(snap_zhp, arg);
- if (ret == 0)
- ret = err;
- }
- }
-
- free(buf);
- return (ret);
-}
-
-/*
- * Iterate over all children, snapshots and filesystems
- * Process snapshots before filesystems because they are nearer the input
- * handle: this is extremely important when used with zfs_iter_f functions
- * looking for data, following the logic that we would like to find it as soon
- * and as close as possible.
- */
-int
-zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
-{
- int ret;
-
- if ((ret = zfs_iter_snapshots(zhp, B_FALSE, func, data, 0, 0)) != 0)
- return (ret);
-
- return (zfs_iter_filesystems(zhp, func, data));
-}
-
-
-typedef struct iter_stack_frame {
- struct iter_stack_frame *next;
- zfs_handle_t *zhp;
-} iter_stack_frame_t;
-
-typedef struct iter_dependents_arg {
- boolean_t first;
- boolean_t allowrecursion;
- iter_stack_frame_t *stack;
- zfs_iter_f func;
- void *data;
-} iter_dependents_arg_t;
-
-static int
-iter_dependents_cb(zfs_handle_t *zhp, void *arg)
-{
- iter_dependents_arg_t *ida = arg;
- int err = 0;
- boolean_t first = ida->first;
- ida->first = B_FALSE;
-
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
- err = zfs_iter_clones(zhp, iter_dependents_cb, ida);
- } else if (zhp->zfs_type != ZFS_TYPE_BOOKMARK) {
- iter_stack_frame_t isf;
- iter_stack_frame_t *f;
-
- /*
- * check if there is a cycle by seeing if this fs is already
- * on the stack.
- */
- for (f = ida->stack; f != NULL; f = f->next) {
- if (f->zhp->zfs_dmustats.dds_guid ==
- zhp->zfs_dmustats.dds_guid) {
- if (ida->allowrecursion) {
- zfs_close(zhp);
- return (0);
- } else {
- zfs_error_aux(zhp->zfs_hdl,
- dgettext(TEXT_DOMAIN,
- "recursive dependency at '%s'"),
- zfs_get_name(zhp));
- err = zfs_error(zhp->zfs_hdl,
- EZFS_RECURSIVE,
- dgettext(TEXT_DOMAIN,
- "cannot determine dependent "
- "datasets"));
- zfs_close(zhp);
- return (err);
- }
- }
- }
-
- isf.zhp = zhp;
- isf.next = ida->stack;
- ida->stack = &isf;
- err = zfs_iter_filesystems(zhp, iter_dependents_cb, ida);
- if (err == 0) {
- err = zfs_iter_snapshots(zhp, B_FALSE,
- iter_dependents_cb, ida, 0, 0);
- }
- ida->stack = isf.next;
- }
-
- if (!first && err == 0)
- err = ida->func(zhp, ida->data);
- else
- zfs_close(zhp);
-
- return (err);
-}
-
-int
-zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
- zfs_iter_f func, void *data)
-{
- iter_dependents_arg_t ida;
- ida.allowrecursion = allowrecursion;
- ida.stack = NULL;
- ida.func = func;
- ida.data = data;
- ida.first = B_TRUE;
- return (iter_dependents_cb(zfs_handle_dup(zhp), &ida));
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c
deleted file mode 100644
index 9d4948cc7173e..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_mount.c
+++ /dev/null
@@ -1,1734 +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 2015 Nexenta Systems, Inc. All rights reserved.
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2014, 2016 by Delphix. All rights reserved.
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
- * Copyright 2017 Joyent, Inc.
- * Copyright 2017 RackTop Systems.
- * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
- */
-
-/*
- * Routines to manage ZFS mounts. We separate all the nasty routines that have
- * to deal with the OS. The following functions are the main entry points --
- * they are used by mount and unmount and when changing a filesystem's
- * mountpoint.
- *
- * zfs_is_mounted()
- * zfs_mount()
- * zfs_unmount()
- * zfs_unmountall()
- *
- * This file also contains the functions used to manage sharing filesystems via
- * NFS and iSCSI:
- *
- * zfs_is_shared()
- * zfs_share()
- * zfs_unshare()
- *
- * zfs_is_shared_nfs()
- * zfs_is_shared_smb()
- * zfs_share_proto()
- * zfs_shareall();
- * zfs_unshare_nfs()
- * zfs_unshare_smb()
- * zfs_unshareall_nfs()
- * zfs_unshareall_smb()
- * zfs_unshareall()
- * zfs_unshareall_bypath()
- *
- * The following functions are available for pool consumers, and will
- * mount/unmount and share/unshare all datasets within pool:
- *
- * zpool_enable_datasets()
- * zpool_disable_datasets()
- */
-
-#include <dirent.h>
-#include <dlfcn.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <libgen.h>
-#include <libintl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <unistd.h>
-#include <zone.h>
-#include <sys/mntent.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/statvfs.h>
-
-#include <libzfs.h>
-
-#include "libzfs_impl.h"
-#include <thread_pool.h>
-
-#include <libshare.h>
-#define MAXISALEN 257 /* based on sysinfo(2) man page */
-
-static int mount_tp_nthr = 512; /* tpool threads for multi-threaded mounting */
-
-static void zfs_mount_task(void *);
-static int zfs_share_proto(zfs_handle_t *, zfs_share_proto_t *);
-zfs_share_type_t zfs_is_shared_proto(zfs_handle_t *, char **,
- zfs_share_proto_t);
-
-/*
- * The share protocols table must be in the same order as the zfs_share_proto_t
- * enum in libzfs_impl.h
- */
-typedef struct {
- zfs_prop_t p_prop;
- char *p_name;
- int p_share_err;
- int p_unshare_err;
-} proto_table_t;
-
-proto_table_t proto_table[PROTO_END] = {
- {ZFS_PROP_SHARENFS, "nfs", EZFS_SHARENFSFAILED, EZFS_UNSHARENFSFAILED},
- {ZFS_PROP_SHARESMB, "smb", EZFS_SHARESMBFAILED, EZFS_UNSHARESMBFAILED},
-};
-
-zfs_share_proto_t nfs_only[] = {
- PROTO_NFS,
- PROTO_END
-};
-
-zfs_share_proto_t smb_only[] = {
- PROTO_SMB,
- PROTO_END
-};
-zfs_share_proto_t share_all_proto[] = {
- PROTO_NFS,
- PROTO_SMB,
- PROTO_END
-};
-
-/*
- * Search the sharetab for the given mountpoint and protocol, returning
- * a zfs_share_type_t value.
- */
-static zfs_share_type_t
-is_shared(libzfs_handle_t *hdl, const char *mountpoint, zfs_share_proto_t proto)
-{
- char buf[MAXPATHLEN], *tab;
- char *ptr;
-
- if (hdl->libzfs_sharetab == NULL)
- return (SHARED_NOT_SHARED);
-
- (void) fseek(hdl->libzfs_sharetab, 0, SEEK_SET);
-
- while (fgets(buf, sizeof (buf), hdl->libzfs_sharetab) != NULL) {
-
- /* the mountpoint is the first entry on each line */
- if ((tab = strchr(buf, '\t')) == NULL)
- continue;
-
- *tab = '\0';
- if (strcmp(buf, mountpoint) == 0) {
-#ifdef illumos
- /*
- * the protocol field is the third field
- * skip over second field
- */
- ptr = ++tab;
- if ((tab = strchr(ptr, '\t')) == NULL)
- continue;
- ptr = ++tab;
- if ((tab = strchr(ptr, '\t')) == NULL)
- continue;
- *tab = '\0';
- if (strcmp(ptr,
- proto_table[proto].p_name) == 0) {
- switch (proto) {
- case PROTO_NFS:
- return (SHARED_NFS);
- case PROTO_SMB:
- return (SHARED_SMB);
- default:
- return (0);
- }
- }
-#else
- if (proto == PROTO_NFS)
- return (SHARED_NFS);
-#endif
- }
- }
-
- return (SHARED_NOT_SHARED);
-}
-
-#ifdef illumos
-static boolean_t
-dir_is_empty_stat(const char *dirname)
-{
- struct stat st;
-
- /*
- * We only want to return false if the given path is a non empty
- * directory, all other errors are handled elsewhere.
- */
- if (stat(dirname, &st) < 0 || !S_ISDIR(st.st_mode)) {
- return (B_TRUE);
- }
-
- /*
- * An empty directory will still have two entries in it, one
- * entry for each of "." and "..".
- */
- if (st.st_size > 2) {
- return (B_FALSE);
- }
-
- return (B_TRUE);
-}
-
-static boolean_t
-dir_is_empty_readdir(const char *dirname)
-{
- DIR *dirp;
- struct dirent64 *dp;
- int dirfd;
-
- if ((dirfd = openat(AT_FDCWD, dirname,
- O_RDONLY | O_NDELAY | O_LARGEFILE | O_CLOEXEC, 0)) < 0) {
- return (B_TRUE);
- }
-
- if ((dirp = fdopendir(dirfd)) == NULL) {
- (void) close(dirfd);
- return (B_TRUE);
- }
-
- while ((dp = readdir64(dirp)) != NULL) {
-
- if (strcmp(dp->d_name, ".") == 0 ||
- strcmp(dp->d_name, "..") == 0)
- continue;
-
- (void) closedir(dirp);
- return (B_FALSE);
- }
-
- (void) closedir(dirp);
- return (B_TRUE);
-}
-
-/*
- * Returns true if the specified directory is empty. If we can't open the
- * directory at all, return true so that the mount can fail with a more
- * informative error message.
- */
-static boolean_t
-dir_is_empty(const char *dirname)
-{
- struct statvfs64 st;
-
- /*
- * If the statvfs call fails or the filesystem is not a ZFS
- * filesystem, fall back to the slow path which uses readdir.
- */
- if ((statvfs64(dirname, &st) != 0) ||
- (strcmp(st.f_basetype, "zfs") != 0)) {
- return (dir_is_empty_readdir(dirname));
- }
-
- /*
- * At this point, we know the provided path is on a ZFS
- * filesystem, so we can use stat instead of readdir to
- * determine if the directory is empty or not. We try to avoid
- * using readdir because that requires opening "dirname"; this
- * open file descriptor can potentially end up in a child
- * process if there's a concurrent fork, thus preventing the
- * zfs_mount() from otherwise succeeding (the open file
- * descriptor inherited by the child process will cause the
- * parent's mount to fail with EBUSY). The performance
- * implications of replacing the open, read, and close with a
- * single stat is nice; but is not the main motivation for the
- * added complexity.
- */
- return (dir_is_empty_stat(dirname));
-}
-#endif
-
-/*
- * Checks to see if the mount is active. If the filesystem is mounted, we fill
- * in 'where' with the current mountpoint, and return 1. Otherwise, we return
- * 0.
- */
-boolean_t
-is_mounted(libzfs_handle_t *zfs_hdl, const char *special, char **where)
-{
- struct mnttab entry;
-
- if (libzfs_mnttab_find(zfs_hdl, special, &entry) != 0)
- return (B_FALSE);
-
- if (where != NULL)
- *where = zfs_strdup(zfs_hdl, entry.mnt_mountp);
-
- return (B_TRUE);
-}
-
-boolean_t
-zfs_is_mounted(zfs_handle_t *zhp, char **where)
-{
- return (is_mounted(zhp->zfs_hdl, zfs_get_name(zhp), where));
-}
-
-static boolean_t
-zfs_is_mountable_internal(zfs_handle_t *zhp, const char *mountpoint)
-{
-
- if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) &&
- getzoneid() == GLOBAL_ZONEID)
- return (B_FALSE);
-
- return (B_TRUE);
-}
-
-/*
- * Returns true if the given dataset is mountable, false otherwise. Returns the
- * mountpoint in 'buf'.
- */
-static boolean_t
-zfs_is_mountable(zfs_handle_t *zhp, char *buf, size_t buflen,
- zprop_source_t *source)
-{
- char sourceloc[MAXNAMELEN];
- zprop_source_t sourcetype;
-
- if (!zfs_prop_valid_for_type(ZFS_PROP_MOUNTPOINT, zhp->zfs_type))
- return (B_FALSE);
-
- verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, buf, buflen,
- &sourcetype, sourceloc, sizeof (sourceloc), B_FALSE) == 0);
-
- if (strcmp(buf, ZFS_MOUNTPOINT_NONE) == 0 ||
- strcmp(buf, ZFS_MOUNTPOINT_LEGACY) == 0)
- return (B_FALSE);
-
- if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_OFF)
- return (B_FALSE);
-
- if (!zfs_is_mountable_internal(zhp, buf))
- return (B_FALSE);
-
- if (source)
- *source = sourcetype;
-
- return (B_TRUE);
-}
-
-/*
- * Mount the given filesystem.
- */
-int
-zfs_mount(zfs_handle_t *zhp, const char *options, int flags)
-{
- char mountpoint[ZFS_MAXPROPLEN];
-
- if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
- return (0);
-
- return (zfs_mount_at(zhp, options, flags, mountpoint));
-}
-
-int
-zfs_mount_at(zfs_handle_t *zhp, const char *options, int flags,
- const char *mountpoint)
-{
- struct stat buf;
- char mntopts[MNT_LINE_MAX];
- libzfs_handle_t *hdl = zhp->zfs_hdl;
-
- if (options == NULL)
- mntopts[0] = '\0';
- else
- (void) strlcpy(mntopts, options, sizeof (mntopts));
-
- /*
- * If the pool is imported read-only then all mounts must be read-only
- */
- if (zpool_get_prop_int(zhp->zpool_hdl, ZPOOL_PROP_READONLY, NULL))
- flags |= MS_RDONLY;
-
- if (!zfs_is_mountable_internal(zhp, mountpoint))
- return (B_FALSE);
-
- /* Create the directory if it doesn't already exist */
- if (lstat(mountpoint, &buf) != 0) {
- if (mkdirp(mountpoint, 0755) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "failed to create mountpoint"));
- return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
- dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
- mountpoint));
- }
- }
-
-#ifdef illumos /* FreeBSD: overlay mounts are not checked. */
- /*
- * Determine if the mountpoint is empty. If so, refuse to perform the
- * mount. We don't perform this check if MS_OVERLAY is specified, which
- * would defeat the point. We also avoid this check if 'remount' is
- * specified.
- */
- if ((flags & MS_OVERLAY) == 0 &&
- strstr(mntopts, MNTOPT_REMOUNT) == NULL &&
- !dir_is_empty(mountpoint)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "directory is not empty"));
- return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
- dgettext(TEXT_DOMAIN, "cannot mount '%s'"), mountpoint));
- }
-#endif
-
- /* perform the mount */
- if (zmount(zfs_get_name(zhp), mountpoint, flags,
- MNTTYPE_ZFS, NULL, 0, mntopts, sizeof (mntopts)) != 0) {
- /*
- * Generic errors are nasty, but there are just way too many
- * from mount(), and they're well-understood. We pick a few
- * common ones to improve upon.
- */
- if (errno == EBUSY) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "mountpoint or dataset is busy"));
- } else if (errno == EPERM) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "Insufficient privileges"));
- } else if (errno == ENOTSUP) {
- char buf[256];
- int spa_version;
-
- VERIFY(zfs_spa_version(zhp, &spa_version) == 0);
- (void) snprintf(buf, sizeof (buf),
- dgettext(TEXT_DOMAIN, "Can't mount a version %lld "
- "file system on a version %d pool. Pool must be"
- " upgraded to mount this file system."),
- (u_longlong_t)zfs_prop_get_int(zhp,
- ZFS_PROP_VERSION), spa_version);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, buf));
- } else {
- zfs_error_aux(hdl, strerror(errno));
- }
- return (zfs_error_fmt(hdl, EZFS_MOUNTFAILED,
- dgettext(TEXT_DOMAIN, "cannot mount '%s'"),
- zhp->zfs_name));
- }
-
- /* add the mounted entry into our cache */
- libzfs_mnttab_add(hdl, zfs_get_name(zhp), mountpoint,
- mntopts);
- return (0);
-}
-
-/*
- * Unmount a single filesystem.
- */
-static int
-unmount_one(libzfs_handle_t *hdl, const char *mountpoint, int flags)
-{
- if (umount2(mountpoint, flags) != 0) {
- zfs_error_aux(hdl, strerror(errno));
- return (zfs_error_fmt(hdl, EZFS_UMOUNTFAILED,
- dgettext(TEXT_DOMAIN, "cannot unmount '%s'"),
- mountpoint));
- }
-
- return (0);
-}
-
-/*
- * Unmount the given filesystem.
- */
-int
-zfs_unmount(zfs_handle_t *zhp, const char *mountpoint, int flags)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- struct mnttab entry;
- char *mntpt = NULL;
-
- /* check to see if we need to unmount the filesystem */
- if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
- libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0)) {
- /*
- * mountpoint may have come from a call to
- * getmnt/getmntany if it isn't NULL. If it is NULL,
- * we know it comes from libzfs_mnttab_find which can
- * then get freed later. We strdup it to play it safe.
- */
- if (mountpoint == NULL)
- mntpt = zfs_strdup(hdl, entry.mnt_mountp);
- else
- mntpt = zfs_strdup(hdl, mountpoint);
-
- /*
- * Unshare and unmount the filesystem
- */
- if (zfs_unshare_proto(zhp, mntpt, share_all_proto) != 0)
- return (-1);
-
- if (unmount_one(hdl, mntpt, flags) != 0) {
- free(mntpt);
- (void) zfs_shareall(zhp);
- return (-1);
- }
- libzfs_mnttab_remove(hdl, zhp->zfs_name);
- free(mntpt);
- }
-
- return (0);
-}
-
-/*
- * Unmount this filesystem and any children inheriting the mountpoint property.
- * To do this, just act like we're changing the mountpoint property, but don't
- * remount the filesystems afterwards.
- */
-int
-zfs_unmountall(zfs_handle_t *zhp, int flags)
-{
- prop_changelist_t *clp;
- int ret;
-
- clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT, 0, flags);
- if (clp == NULL)
- return (-1);
-
- ret = changelist_prefix(clp);
- changelist_free(clp);
-
- return (ret);
-}
-
-boolean_t
-zfs_is_shared(zfs_handle_t *zhp)
-{
- zfs_share_type_t rc = 0;
- zfs_share_proto_t *curr_proto;
-
- if (ZFS_IS_VOLUME(zhp))
- return (B_FALSE);
-
- for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
- curr_proto++)
- rc |= zfs_is_shared_proto(zhp, NULL, *curr_proto);
-
- return (rc ? B_TRUE : B_FALSE);
-}
-
-int
-zfs_share(zfs_handle_t *zhp)
-{
- assert(!ZFS_IS_VOLUME(zhp));
- return (zfs_share_proto(zhp, share_all_proto));
-}
-
-int
-zfs_unshare(zfs_handle_t *zhp)
-{
- assert(!ZFS_IS_VOLUME(zhp));
- return (zfs_unshareall(zhp));
-}
-
-/*
- * Check to see if the filesystem is currently shared.
- */
-zfs_share_type_t
-zfs_is_shared_proto(zfs_handle_t *zhp, char **where, zfs_share_proto_t proto)
-{
- char *mountpoint;
- zfs_share_type_t rc;
-
- if (!zfs_is_mounted(zhp, &mountpoint))
- return (SHARED_NOT_SHARED);
-
- if ((rc = is_shared(zhp->zfs_hdl, mountpoint, proto))
- != SHARED_NOT_SHARED) {
- if (where != NULL)
- *where = mountpoint;
- else
- free(mountpoint);
- return (rc);
- } else {
- free(mountpoint);
- return (SHARED_NOT_SHARED);
- }
-}
-
-boolean_t
-zfs_is_shared_nfs(zfs_handle_t *zhp, char **where)
-{
- return (zfs_is_shared_proto(zhp, where,
- PROTO_NFS) != SHARED_NOT_SHARED);
-}
-
-boolean_t
-zfs_is_shared_smb(zfs_handle_t *zhp, char **where)
-{
- return (zfs_is_shared_proto(zhp, where,
- PROTO_SMB) != SHARED_NOT_SHARED);
-}
-
-/*
- * Make sure things will work if libshare isn't installed by using
- * wrapper functions that check to see that the pointers to functions
- * initialized in _zfs_init_libshare() are actually present.
- */
-
-#ifdef illumos
-static sa_handle_t (*_sa_init)(int);
-static sa_handle_t (*_sa_init_arg)(int, void *);
-static void (*_sa_fini)(sa_handle_t);
-static sa_share_t (*_sa_find_share)(sa_handle_t, char *);
-static int (*_sa_enable_share)(sa_share_t, char *);
-static int (*_sa_disable_share)(sa_share_t, char *);
-static char *(*_sa_errorstr)(int);
-static int (*_sa_parse_legacy_options)(sa_group_t, char *, char *);
-static boolean_t (*_sa_needs_refresh)(sa_handle_t *);
-static libzfs_handle_t *(*_sa_get_zfs_handle)(sa_handle_t);
-static int (*_sa_zfs_process_share)(sa_handle_t, sa_group_t, sa_share_t,
- char *, char *, zprop_source_t, char *, char *, char *);
-static void (*_sa_update_sharetab_ts)(sa_handle_t);
-#endif
-
-/*
- * _zfs_init_libshare()
- *
- * Find the libshare.so.1 entry points that we use here and save the
- * values to be used later. This is triggered by the runtime loader.
- * Make sure the correct ISA version is loaded.
- */
-
-#pragma init(_zfs_init_libshare)
-static void
-_zfs_init_libshare(void)
-{
-#ifdef illumos
- void *libshare;
- char path[MAXPATHLEN];
- char isa[MAXISALEN];
-
-#if defined(_LP64)
- if (sysinfo(SI_ARCHITECTURE_64, isa, MAXISALEN) == -1)
- isa[0] = '\0';
-#else
- isa[0] = '\0';
-#endif
- (void) snprintf(path, MAXPATHLEN,
- "/usr/lib/%s/libshare.so.1", isa);
-
- if ((libshare = dlopen(path, RTLD_LAZY | RTLD_GLOBAL)) != NULL) {
- _sa_init = (sa_handle_t (*)(int))dlsym(libshare, "sa_init");
- _sa_init_arg = (sa_handle_t (*)(int, void *))dlsym(libshare,
- "sa_init_arg");
- _sa_fini = (void (*)(sa_handle_t))dlsym(libshare, "sa_fini");
- _sa_find_share = (sa_share_t (*)(sa_handle_t, char *))
- dlsym(libshare, "sa_find_share");
- _sa_enable_share = (int (*)(sa_share_t, char *))dlsym(libshare,
- "sa_enable_share");
- _sa_disable_share = (int (*)(sa_share_t, char *))dlsym(libshare,
- "sa_disable_share");
- _sa_errorstr = (char *(*)(int))dlsym(libshare, "sa_errorstr");
- _sa_parse_legacy_options = (int (*)(sa_group_t, char *, char *))
- dlsym(libshare, "sa_parse_legacy_options");
- _sa_needs_refresh = (boolean_t (*)(sa_handle_t *))
- dlsym(libshare, "sa_needs_refresh");
- _sa_get_zfs_handle = (libzfs_handle_t *(*)(sa_handle_t))
- dlsym(libshare, "sa_get_zfs_handle");
- _sa_zfs_process_share = (int (*)(sa_handle_t, sa_group_t,
- sa_share_t, char *, char *, zprop_source_t, char *,
- char *, char *))dlsym(libshare, "sa_zfs_process_share");
- _sa_update_sharetab_ts = (void (*)(sa_handle_t))
- dlsym(libshare, "sa_update_sharetab_ts");
- if (_sa_init == NULL || _sa_init_arg == NULL ||
- _sa_fini == NULL || _sa_find_share == NULL ||
- _sa_enable_share == NULL || _sa_disable_share == NULL ||
- _sa_errorstr == NULL || _sa_parse_legacy_options == NULL ||
- _sa_needs_refresh == NULL || _sa_get_zfs_handle == NULL ||
- _sa_zfs_process_share == NULL ||
- _sa_update_sharetab_ts == NULL) {
- _sa_init = NULL;
- _sa_init_arg = NULL;
- _sa_fini = NULL;
- _sa_disable_share = NULL;
- _sa_enable_share = NULL;
- _sa_errorstr = NULL;
- _sa_parse_legacy_options = NULL;
- (void) dlclose(libshare);
- _sa_needs_refresh = NULL;
- _sa_get_zfs_handle = NULL;
- _sa_zfs_process_share = NULL;
- _sa_update_sharetab_ts = NULL;
- }
- }
-#endif
-}
-
-/*
- * zfs_init_libshare(zhandle, service)
- *
- * Initialize the libshare API if it hasn't already been initialized.
- * In all cases it returns 0 if it succeeded and an error if not. The
- * service value is which part(s) of the API to initialize and is a
- * direct map to the libshare sa_init(service) interface.
- */
-static int
-zfs_init_libshare_impl(libzfs_handle_t *zhandle, int service, void *arg)
-{
-#ifdef illumos
- /*
- * libshare is either not installed or we're in a branded zone. The
- * rest of the wrapper functions around the libshare calls already
- * handle NULL function pointers, but we don't want the callers of
- * zfs_init_libshare() to fail prematurely if libshare is not available.
- */
- if (_sa_init == NULL)
- return (SA_OK);
-
- /*
- * Attempt to refresh libshare. This is necessary if there was a cache
- * miss for a new ZFS dataset that was just created, or if state of the
- * sharetab file has changed since libshare was last initialized. We
- * want to make sure so check timestamps to see if a different process
- * has updated any of the configuration. If there was some non-ZFS
- * change, we need to re-initialize the internal cache.
- */
- if (_sa_needs_refresh != NULL &&
- _sa_needs_refresh(zhandle->libzfs_sharehdl)) {
- zfs_uninit_libshare(zhandle);
- zhandle->libzfs_sharehdl = _sa_init_arg(service, arg);
- }
-
- if (zhandle && zhandle->libzfs_sharehdl == NULL)
- zhandle->libzfs_sharehdl = _sa_init_arg(service, arg);
-
- if (zhandle->libzfs_sharehdl == NULL)
- return (SA_NO_MEMORY);
-#endif
-
- return (SA_OK);
-}
-int
-zfs_init_libshare(libzfs_handle_t *zhandle, int service)
-{
- return (zfs_init_libshare_impl(zhandle, service, NULL));
-}
-
-int
-zfs_init_libshare_arg(libzfs_handle_t *zhandle, int service, void *arg)
-{
- return (zfs_init_libshare_impl(zhandle, service, arg));
-}
-
-
-/*
- * zfs_uninit_libshare(zhandle)
- *
- * Uninitialize the libshare API if it hasn't already been
- * uninitialized. It is OK to call multiple times.
- */
-void
-zfs_uninit_libshare(libzfs_handle_t *zhandle)
-{
- if (zhandle != NULL && zhandle->libzfs_sharehdl != NULL) {
-#ifdef illumos
- if (_sa_fini != NULL)
- _sa_fini(zhandle->libzfs_sharehdl);
-#endif
- zhandle->libzfs_sharehdl = NULL;
- }
-}
-
-/*
- * zfs_parse_options(options, proto)
- *
- * Call the legacy parse interface to get the protocol specific
- * options using the NULL arg to indicate that this is a "parse" only.
- */
-int
-zfs_parse_options(char *options, zfs_share_proto_t proto)
-{
-#ifdef illumos
- if (_sa_parse_legacy_options != NULL) {
- return (_sa_parse_legacy_options(NULL, options,
- proto_table[proto].p_name));
- }
- return (SA_CONFIG_ERR);
-#else
- return (SA_OK);
-#endif
-}
-
-#ifdef illumos
-/*
- * zfs_sa_find_share(handle, path)
- *
- * wrapper around sa_find_share to find a share path in the
- * configuration.
- */
-static sa_share_t
-zfs_sa_find_share(sa_handle_t handle, char *path)
-{
- if (_sa_find_share != NULL)
- return (_sa_find_share(handle, path));
- return (NULL);
-}
-
-/*
- * zfs_sa_enable_share(share, proto)
- *
- * Wrapper for sa_enable_share which enables a share for a specified
- * protocol.
- */
-static int
-zfs_sa_enable_share(sa_share_t share, char *proto)
-{
- if (_sa_enable_share != NULL)
- return (_sa_enable_share(share, proto));
- return (SA_CONFIG_ERR);
-}
-
-/*
- * zfs_sa_disable_share(share, proto)
- *
- * Wrapper for sa_enable_share which disables a share for a specified
- * protocol.
- */
-static int
-zfs_sa_disable_share(sa_share_t share, char *proto)
-{
- if (_sa_disable_share != NULL)
- return (_sa_disable_share(share, proto));
- return (SA_CONFIG_ERR);
-}
-#endif /* illumos */
-
-/*
- * Share the given filesystem according to the options in the specified
- * protocol specific properties (sharenfs, sharesmb). We rely
- * on "libshare" to the dirty work for us.
- */
-static int
-zfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto)
-{
- char mountpoint[ZFS_MAXPROPLEN];
- char shareopts[ZFS_MAXPROPLEN];
- char sourcestr[ZFS_MAXPROPLEN];
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- zfs_share_proto_t *curr_proto;
- zprop_source_t sourcetype;
- int error, ret;
-
- if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint), NULL))
- return (0);
-
- for (curr_proto = proto; *curr_proto != PROTO_END; curr_proto++) {
- /*
- * Return success if there are no share options.
- */
- if (zfs_prop_get(zhp, proto_table[*curr_proto].p_prop,
- shareopts, sizeof (shareopts), &sourcetype, sourcestr,
- ZFS_MAXPROPLEN, B_FALSE) != 0 ||
- strcmp(shareopts, "off") == 0)
- continue;
-#ifdef illumos
- ret = zfs_init_libshare_arg(hdl, SA_INIT_ONE_SHARE_FROM_HANDLE,
- zhp);
- if (ret != SA_OK) {
- (void) zfs_error_fmt(hdl, EZFS_SHARENFSFAILED,
- dgettext(TEXT_DOMAIN, "cannot share '%s': %s"),
- zfs_get_name(zhp), _sa_errorstr != NULL ?
- _sa_errorstr(ret) : "");
- return (-1);
- }
-#endif
-
- /*
- * If the 'zoned' property is set, then zfs_is_mountable()
- * will have already bailed out if we are in the global zone.
- * But local zones cannot be NFS servers, so we ignore it for
- * local zones as well.
- */
- if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED))
- continue;
-
-#ifdef illumos
- share = zfs_sa_find_share(hdl->libzfs_sharehdl, mountpoint);
- if (share == NULL) {
- /*
- * This may be a new file system that was just
- * created so isn't in the internal cache
- * (second time through). Rather than
- * reloading the entire configuration, we can
- * assume ZFS has done the checking and it is
- * safe to add this to the internal
- * configuration.
- */
- if (_sa_zfs_process_share(hdl->libzfs_sharehdl,
- NULL, NULL, mountpoint,
- proto_table[*curr_proto].p_name, sourcetype,
- shareopts, sourcestr, zhp->zfs_name) != SA_OK) {
- (void) zfs_error_fmt(hdl,
- proto_table[*curr_proto].p_share_err,
- dgettext(TEXT_DOMAIN, "cannot share '%s'"),
- zfs_get_name(zhp));
- return (-1);
- }
- share = zfs_sa_find_share(hdl->libzfs_sharehdl,
- mountpoint);
- }
- if (share != NULL) {
- int err;
- err = zfs_sa_enable_share(share,
- proto_table[*curr_proto].p_name);
- if (err != SA_OK) {
- (void) zfs_error_fmt(hdl,
- proto_table[*curr_proto].p_share_err,
- dgettext(TEXT_DOMAIN, "cannot share '%s'"),
- zfs_get_name(zhp));
- return (-1);
- }
- } else
-#else
- if (*curr_proto != PROTO_NFS) {
- fprintf(stderr, "Unsupported share protocol: %d.\n",
- *curr_proto);
- continue;
- }
-
- if (strcmp(shareopts, "on") == 0)
- error = fsshare(ZFS_EXPORTS_PATH, mountpoint, "");
- else
- error = fsshare(ZFS_EXPORTS_PATH, mountpoint, shareopts);
- if (error != 0)
-#endif
- {
- (void) zfs_error_fmt(hdl,
- proto_table[*curr_proto].p_share_err,
- dgettext(TEXT_DOMAIN, "cannot share '%s'"),
- zfs_get_name(zhp));
- return (-1);
- }
-
- }
- return (0);
-}
-
-
-int
-zfs_share_nfs(zfs_handle_t *zhp)
-{
- return (zfs_share_proto(zhp, nfs_only));
-}
-
-int
-zfs_share_smb(zfs_handle_t *zhp)
-{
- return (zfs_share_proto(zhp, smb_only));
-}
-
-int
-zfs_shareall(zfs_handle_t *zhp)
-{
- return (zfs_share_proto(zhp, share_all_proto));
-}
-
-/*
- * Unshare a filesystem by mountpoint.
- */
-static int
-unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint,
- zfs_share_proto_t proto)
-{
-#ifdef illumos
- sa_share_t share;
- int err;
- char *mntpt;
-
- /*
- * Mountpoint could get trashed if libshare calls getmntany
- * which it does during API initialization, so strdup the
- * value.
- */
- mntpt = zfs_strdup(hdl, mountpoint);
-
- /*
- * make sure libshare initialized, initialize everything because we
- * don't know what other unsharing may happen later. Functions up the
- * stack are allowed to initialize instead a subset of shares at the
- * time the set is known.
- */
- if ((err = zfs_init_libshare_arg(hdl, SA_INIT_ONE_SHARE_FROM_NAME,
- (void *)name)) != SA_OK) {
- free(mntpt); /* don't need the copy anymore */
- return (zfs_error_fmt(hdl, proto_table[proto].p_unshare_err,
- dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"),
- name, _sa_errorstr(err)));
- }
-
- share = zfs_sa_find_share(hdl->libzfs_sharehdl, mntpt);
- free(mntpt); /* don't need the copy anymore */
-
- if (share != NULL) {
- err = zfs_sa_disable_share(share, proto_table[proto].p_name);
- if (err != SA_OK) {
- return (zfs_error_fmt(hdl,
- proto_table[proto].p_unshare_err,
- dgettext(TEXT_DOMAIN, "cannot unshare '%s': %s"),
- name, _sa_errorstr(err)));
- }
- } else {
- return (zfs_error_fmt(hdl, proto_table[proto].p_unshare_err,
- dgettext(TEXT_DOMAIN, "cannot unshare '%s': not found"),
- name));
- }
-#else
- char buf[MAXPATHLEN];
- FILE *fp;
- int err;
-
- if (proto != PROTO_NFS) {
- fprintf(stderr, "No SMB support in FreeBSD yet.\n");
- return (EOPNOTSUPP);
- }
-
- err = fsunshare(ZFS_EXPORTS_PATH, mountpoint);
- if (err != 0) {
- zfs_error_aux(hdl, "%s", strerror(err));
- return (zfs_error_fmt(hdl, EZFS_UNSHARENFSFAILED,
- dgettext(TEXT_DOMAIN,
- "cannot unshare '%s'"), name));
- }
-#endif
- return (0);
-}
-
-/*
- * Unshare the given filesystem.
- */
-int
-zfs_unshare_proto(zfs_handle_t *zhp, const char *mountpoint,
- zfs_share_proto_t *proto)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- struct mnttab entry;
- char *mntpt = NULL;
-
- /* check to see if need to unmount the filesystem */
- rewind(zhp->zfs_hdl->libzfs_mnttab);
- if (mountpoint != NULL)
- mountpoint = mntpt = zfs_strdup(hdl, mountpoint);
-
- if (mountpoint != NULL || ((zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) &&
- libzfs_mnttab_find(hdl, zfs_get_name(zhp), &entry) == 0)) {
- zfs_share_proto_t *curr_proto;
-
- if (mountpoint == NULL)
- mntpt = zfs_strdup(zhp->zfs_hdl, entry.mnt_mountp);
-
- for (curr_proto = proto; *curr_proto != PROTO_END;
- curr_proto++) {
-
- if (is_shared(hdl, mntpt, *curr_proto) &&
- unshare_one(hdl, zhp->zfs_name,
- mntpt, *curr_proto) != 0) {
- if (mntpt != NULL)
- free(mntpt);
- return (-1);
- }
- }
- }
- if (mntpt != NULL)
- free(mntpt);
-
- return (0);
-}
-
-int
-zfs_unshare_nfs(zfs_handle_t *zhp, const char *mountpoint)
-{
- return (zfs_unshare_proto(zhp, mountpoint, nfs_only));
-}
-
-int
-zfs_unshare_smb(zfs_handle_t *zhp, const char *mountpoint)
-{
- return (zfs_unshare_proto(zhp, mountpoint, smb_only));
-}
-
-/*
- * Same as zfs_unmountall(), but for NFS and SMB unshares.
- */
-int
-zfs_unshareall_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto)
-{
- prop_changelist_t *clp;
- int ret;
-
- clp = changelist_gather(zhp, ZFS_PROP_SHARENFS, 0, 0);
- if (clp == NULL)
- return (-1);
-
- ret = changelist_unshare(clp, proto);
- changelist_free(clp);
-
- return (ret);
-}
-
-int
-zfs_unshareall_nfs(zfs_handle_t *zhp)
-{
- return (zfs_unshareall_proto(zhp, nfs_only));
-}
-
-int
-zfs_unshareall_smb(zfs_handle_t *zhp)
-{
- return (zfs_unshareall_proto(zhp, smb_only));
-}
-
-int
-zfs_unshareall(zfs_handle_t *zhp)
-{
- return (zfs_unshareall_proto(zhp, share_all_proto));
-}
-
-int
-zfs_unshareall_bypath(zfs_handle_t *zhp, const char *mountpoint)
-{
- return (zfs_unshare_proto(zhp, mountpoint, share_all_proto));
-}
-
-/*
- * Remove the mountpoint associated with the current dataset, if necessary.
- * We only remove the underlying directory if:
- *
- * - The mountpoint is not 'none' or 'legacy'
- * - The mountpoint is non-empty
- * - The mountpoint is the default or inherited
- * - The 'zoned' property is set, or we're in a local zone
- *
- * Any other directories we leave alone.
- */
-void
-remove_mountpoint(zfs_handle_t *zhp)
-{
- char mountpoint[ZFS_MAXPROPLEN];
- zprop_source_t source;
-
- if (!zfs_is_mountable(zhp, mountpoint, sizeof (mountpoint),
- &source))
- return;
-
- if (source == ZPROP_SRC_DEFAULT ||
- source == ZPROP_SRC_INHERITED) {
- /*
- * Try to remove the directory, silently ignoring any errors.
- * The filesystem may have since been removed or moved around,
- * and this error isn't really useful to the administrator in
- * any way.
- */
- (void) rmdir(mountpoint);
- }
-}
-
-/*
- * Add the given zfs handle to the cb_handles array, dynamically reallocating
- * the array if it is out of space
- */
-void
-libzfs_add_handle(get_all_cb_t *cbp, zfs_handle_t *zhp)
-{
- if (cbp->cb_alloc == cbp->cb_used) {
- size_t newsz;
- zfs_handle_t **newhandles;
-
- newsz = cbp->cb_alloc != 0 ? cbp->cb_alloc * 2 : 64;
- newhandles = zfs_realloc(zhp->zfs_hdl,
- cbp->cb_handles, cbp->cb_alloc * sizeof (zfs_handle_t *),
- newsz * sizeof (zfs_handle_t *));
- cbp->cb_handles = newhandles;
- cbp->cb_alloc = newsz;
- }
- cbp->cb_handles[cbp->cb_used++] = zhp;
-}
-
-/*
- * Recursive helper function used during file system enumeration
- */
-static int
-zfs_iter_cb(zfs_handle_t *zhp, void *data)
-{
- get_all_cb_t *cbp = data;
-
- if (!(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM)) {
- zfs_close(zhp);
- return (0);
- }
-
- if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_NOAUTO) {
- zfs_close(zhp);
- 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) {
- zfs_close(zhp);
- return (0);
- }
-
- libzfs_add_handle(cbp, zhp);
- if (zfs_iter_filesystems(zhp, zfs_iter_cb, cbp) != 0) {
- zfs_close(zhp);
- return (-1);
- }
- return (0);
-}
-
-/*
- * Sort comparator that compares two mountpoint paths. We sort these paths so
- * that subdirectories immediately follow their parents. This means that we
- * effectively treat the '/' character as the lowest value non-nul char.
- * Since filesystems from non-global zones can have the same mountpoint
- * as other filesystems, the comparator sorts global zone filesystems to
- * the top of the list. This means that the global zone will traverse the
- * filesystem list in the correct order and can stop when it sees the
- * first zoned filesystem. In a non-global zone, only the delegated
- * filesystems are seen.
- *
- * An example sorted list using this comparator would look like:
- *
- * /foo
- * /foo/bar
- * /foo/bar/baz
- * /foo/baz
- * /foo.bar
- * /foo (NGZ1)
- * /foo (NGZ2)
- *
- * The mount code depend on this ordering to deterministically iterate
- * over filesystems in order to spawn parallel mount tasks.
- */
-static int
-mountpoint_cmp(const void *arga, const void *argb)
-{
- zfs_handle_t *const *zap = arga;
- zfs_handle_t *za = *zap;
- zfs_handle_t *const *zbp = argb;
- zfs_handle_t *zb = *zbp;
- char mounta[MAXPATHLEN];
- char mountb[MAXPATHLEN];
- const char *a = mounta;
- const char *b = mountb;
- boolean_t gota, gotb;
- uint64_t zoneda, zonedb;
-
- zoneda = zfs_prop_get_int(za, ZFS_PROP_ZONED);
- zonedb = zfs_prop_get_int(zb, ZFS_PROP_ZONED);
- if (zoneda && !zonedb)
- return (1);
- if (!zoneda && zonedb)
- return (-1);
- gota = (zfs_get_type(za) == ZFS_TYPE_FILESYSTEM);
- if (gota)
- verify(zfs_prop_get(za, ZFS_PROP_MOUNTPOINT, mounta,
- sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
- gotb = (zfs_get_type(zb) == ZFS_TYPE_FILESYSTEM);
- if (gotb)
- verify(zfs_prop_get(zb, ZFS_PROP_MOUNTPOINT, mountb,
- sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
-
- if (gota && gotb) {
- while (*a != '\0' && (*a == *b)) {
- a++;
- b++;
- }
- if (*a == *b)
- return (0);
- if (*a == '\0')
- return (-1);
- if (*b == '\0')
- return (1);
- if (*a == '/')
- return (-1);
- if (*b == '/')
- return (1);
- return (*a < *b ? -1 : *a > *b);
- }
-
- if (gota)
- return (-1);
- if (gotb)
- return (1);
-
- /*
- * If neither filesystem has a mountpoint, revert to sorting by
- * datset name.
- */
- return (strcmp(zfs_get_name(za), zfs_get_name(zb)));
-}
-
-/*
- * Return true if path2 is a child of path1 or path2 equals path1 or
- * path1 is "/" (path2 is always a child of "/").
- */
-static boolean_t
-libzfs_path_contains(const char *path1, const char *path2)
-{
- return (strcmp(path1, path2) == 0 || strcmp(path1, "/") == 0 ||
- (strstr(path2, path1) == path2 && path2[strlen(path1)] == '/'));
-}
-
-
-static int
-non_descendant_idx(zfs_handle_t **handles, size_t num_handles, int idx)
-{
- char parent[ZFS_MAXPROPLEN];
- char child[ZFS_MAXPROPLEN];
- int i;
-
- verify(zfs_prop_get(handles[idx], ZFS_PROP_MOUNTPOINT, parent,
- sizeof (parent), NULL, NULL, 0, B_FALSE) == 0);
-
- for (i = idx + 1; i < num_handles; i++) {
- verify(zfs_prop_get(handles[i], ZFS_PROP_MOUNTPOINT, child,
- sizeof (child), NULL, NULL, 0, B_FALSE) == 0);
- if (!libzfs_path_contains(parent, child))
- break;
- }
- return (i);
-}
-
-typedef struct mnt_param {
- libzfs_handle_t *mnt_hdl;
- tpool_t *mnt_tp;
- zfs_handle_t **mnt_zhps; /* filesystems to mount */
- size_t mnt_num_handles;
- int mnt_idx; /* Index of selected entry to mount */
- zfs_iter_f mnt_func;
- void *mnt_data;
-} mnt_param_t;
-
-/*
- * Allocate and populate the parameter struct for mount function, and
- * schedule mounting of the entry selected by idx.
- */
-static void
-zfs_dispatch_mount(libzfs_handle_t *hdl, zfs_handle_t **handles,
- size_t num_handles, int idx, zfs_iter_f func, void *data, tpool_t *tp)
-{
- mnt_param_t *mnt_param = zfs_alloc(hdl, sizeof (mnt_param_t));
-
- mnt_param->mnt_hdl = hdl;
- mnt_param->mnt_tp = tp;
- mnt_param->mnt_zhps = handles;
- mnt_param->mnt_num_handles = num_handles;
- mnt_param->mnt_idx = idx;
- mnt_param->mnt_func = func;
- mnt_param->mnt_data = data;
-
- (void) tpool_dispatch(tp, zfs_mount_task, (void*)mnt_param);
-}
-
-/*
- * This is the structure used to keep state of mounting or sharing operations
- * during a call to zpool_enable_datasets().
- */
-typedef struct mount_state {
- /*
- * ms_mntstatus is set to -1 if any mount fails. While multiple threads
- * could update this variable concurrently, no synchronization is
- * needed as it's only ever set to -1.
- */
- int ms_mntstatus;
- int ms_mntflags;
- const char *ms_mntopts;
-} mount_state_t;
-
-static int
-zfs_mount_one(zfs_handle_t *zhp, void *arg)
-{
- mount_state_t *ms = arg;
- int ret = 0;
-
- if (zfs_mount(zhp, ms->ms_mntopts, ms->ms_mntflags) != 0)
- ret = ms->ms_mntstatus = -1;
- return (ret);
-}
-
-static int
-zfs_share_one(zfs_handle_t *zhp, void *arg)
-{
- mount_state_t *ms = arg;
- int ret = 0;
-
- if (zfs_share(zhp) != 0)
- ret = ms->ms_mntstatus = -1;
- return (ret);
-}
-
-/*
- * Thread pool function to mount one file system. On completion, it finds and
- * schedules its children to be mounted. This depends on the sorting done in
- * zfs_foreach_mountpoint(). Note that the degenerate case (chain of entries
- * each descending from the previous) will have no parallelism since we always
- * have to wait for the parent to finish mounting before we can schedule
- * its children.
- */
-static void
-zfs_mount_task(void *arg)
-{
- mnt_param_t *mp = arg;
- int idx = mp->mnt_idx;
- zfs_handle_t **handles = mp->mnt_zhps;
- size_t num_handles = mp->mnt_num_handles;
- char mountpoint[ZFS_MAXPROPLEN];
-
- verify(zfs_prop_get(handles[idx], ZFS_PROP_MOUNTPOINT, mountpoint,
- sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
-
- if (mp->mnt_func(handles[idx], mp->mnt_data) != 0)
- return;
-
- /*
- * We dispatch tasks to mount filesystems with mountpoints underneath
- * this one. We do this by dispatching the next filesystem with a
- * descendant mountpoint of the one we just mounted, then skip all of
- * its descendants, dispatch the next descendant mountpoint, and so on.
- * The non_descendant_idx() function skips over filesystems that are
- * descendants of the filesystem we just dispatched.
- */
- for (int i = idx + 1; i < num_handles;
- i = non_descendant_idx(handles, num_handles, i)) {
- char child[ZFS_MAXPROPLEN];
- verify(zfs_prop_get(handles[i], ZFS_PROP_MOUNTPOINT,
- child, sizeof (child), NULL, NULL, 0, B_FALSE) == 0);
-
- if (!libzfs_path_contains(mountpoint, child))
- break; /* not a descendant, return */
- zfs_dispatch_mount(mp->mnt_hdl, handles, num_handles, i,
- mp->mnt_func, mp->mnt_data, mp->mnt_tp);
- }
- free(mp);
-}
-
-/*
- * Issue the func callback for each ZFS handle contained in the handles
- * array. This function is used to mount all datasets, and so this function
- * guarantees that filesystems for parent mountpoints are called before their
- * children. As such, before issuing any callbacks, we first sort the array
- * of handles by mountpoint.
- *
- * Callbacks are issued in one of two ways:
- *
- * 1. Sequentially: If the parallel argument is B_FALSE or the ZFS_SERIAL_MOUNT
- * environment variable is set, then we issue callbacks sequentially.
- *
- * 2. In parallel: If the parallel argument is B_TRUE and the ZFS_SERIAL_MOUNT
- * environment variable is not set, then we use a tpool to dispatch threads
- * to mount filesystems in parallel. This function dispatches tasks to mount
- * the filesystems at the top-level mountpoints, and these tasks in turn
- * are responsible for recursively mounting filesystems in their children
- * mountpoints.
- */
-void
-zfs_foreach_mountpoint(libzfs_handle_t *hdl, zfs_handle_t **handles,
- size_t num_handles, zfs_iter_f func, void *data, boolean_t parallel)
-{
- zoneid_t zoneid = getzoneid();
-
- /*
- * The ZFS_SERIAL_MOUNT environment variable is an undocumented
- * variable that can be used as a convenience to do a/b comparison
- * of serial vs. parallel mounting.
- */
- boolean_t serial_mount = !parallel ||
- (getenv("ZFS_SERIAL_MOUNT") != NULL);
-
- /*
- * Sort the datasets by mountpoint. See mountpoint_cmp for details
- * of how these are sorted.
- */
- qsort(handles, num_handles, sizeof (zfs_handle_t *), mountpoint_cmp);
-
- if (serial_mount) {
- for (int i = 0; i < num_handles; i++) {
- func(handles[i], data);
- }
- return;
- }
-
- /*
- * Issue the callback function for each dataset using a parallel
- * algorithm that uses a thread pool to manage threads.
- */
- tpool_t *tp = tpool_create(1, mount_tp_nthr, 0, NULL);
-
- /*
- * There may be multiple "top level" mountpoints outside of the pool's
- * root mountpoint, e.g.: /foo /bar. Dispatch a mount task for each of
- * these.
- */
- for (int i = 0; i < num_handles;
- i = non_descendant_idx(handles, num_handles, i)) {
- /*
- * Since the mountpoints have been sorted so that the zoned
- * filesystems are at the end, a zoned filesystem seen from
- * the global zone means that we're done.
- */
- if (zoneid == GLOBAL_ZONEID &&
- zfs_prop_get_int(handles[i], ZFS_PROP_ZONED))
- break;
- zfs_dispatch_mount(hdl, handles, num_handles, i, func, data,
- tp);
- }
-
- tpool_wait(tp); /* wait for all scheduled mounts to complete */
- tpool_destroy(tp);
-}
-
-/*
- * Mount and share all datasets within the given pool. This assumes that no
- * datasets within the pool are currently mounted.
- */
-#pragma weak zpool_mount_datasets = zpool_enable_datasets
-int
-zpool_enable_datasets(zpool_handle_t *zhp, const char *mntopts, int flags)
-{
- get_all_cb_t cb = { 0 };
- mount_state_t ms = { 0 };
- zfs_handle_t *zfsp;
- int ret = 0;
-
- if ((zfsp = zfs_open(zhp->zpool_hdl, zhp->zpool_name,
- ZFS_TYPE_DATASET)) == NULL)
- goto out;
-
- /*
- * Gather all non-snapshot datasets within the pool. Start by adding
- * the root filesystem for this pool to the list, and then iterate
- * over all child filesystems.
- */
- libzfs_add_handle(&cb, zfsp);
- if (zfs_iter_filesystems(zfsp, zfs_iter_cb, &cb) != 0)
- goto out;
-
- /*
- * Mount all filesystems
- */
- ms.ms_mntopts = mntopts;
- ms.ms_mntflags = flags;
- zfs_foreach_mountpoint(zhp->zpool_hdl, cb.cb_handles, cb.cb_used,
- zfs_mount_one, &ms, B_TRUE);
- if (ms.ms_mntstatus != 0)
- ret = ms.ms_mntstatus;
-
- /*
- * Share all filesystems that need to be shared. This needs to be
- * a separate pass because libshare is not mt-safe, and so we need
- * to share serially.
- */
- ms.ms_mntstatus = 0;
- zfs_foreach_mountpoint(zhp->zpool_hdl, cb.cb_handles, cb.cb_used,
- zfs_share_one, &ms, B_FALSE);
- if (ms.ms_mntstatus != 0)
- ret = ms.ms_mntstatus;
-
-out:
- for (int i = 0; i < cb.cb_used; i++)
- zfs_close(cb.cb_handles[i]);
- free(cb.cb_handles);
-
- return (ret);
-}
-
-static int
-mountpoint_compare(const void *a, const void *b)
-{
- const char *mounta = *((char **)a);
- const char *mountb = *((char **)b);
-
- return (strcmp(mountb, mounta));
-}
-
-/* alias for 2002/240 */
-#pragma weak zpool_unmount_datasets = zpool_disable_datasets
-/*
- * Unshare and unmount all datasets within the given pool. We don't want to
- * rely on traversing the DSL to discover the filesystems within the pool,
- * because this may be expensive (if not all of them are mounted), and can fail
- * arbitrarily (on I/O error, for example). Instead, we walk /etc/mnttab and
- * gather all the filesystems that are currently mounted.
- */
-int
-zpool_disable_datasets(zpool_handle_t *zhp, boolean_t force)
-{
- int used, alloc;
- struct mnttab entry;
- size_t namelen;
- char **mountpoints = NULL;
- zfs_handle_t **datasets = NULL;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- int i;
- int ret = -1;
- int flags = (force ? MS_FORCE : 0);
-#ifdef illumos
- sa_init_selective_arg_t sharearg;
-#endif
-
- namelen = strlen(zhp->zpool_name);
-
- rewind(hdl->libzfs_mnttab);
- used = alloc = 0;
- while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
- /*
- * Ignore non-ZFS entries.
- */
- if (entry.mnt_fstype == NULL ||
- strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
- continue;
-
- /*
- * Ignore filesystems not within this pool.
- */
- if (entry.mnt_mountp == NULL ||
- strncmp(entry.mnt_special, zhp->zpool_name, namelen) != 0 ||
- (entry.mnt_special[namelen] != '/' &&
- entry.mnt_special[namelen] != '\0'))
- continue;
-
- /*
- * At this point we've found a filesystem within our pool. Add
- * it to our growing list.
- */
- if (used == alloc) {
- if (alloc == 0) {
- if ((mountpoints = zfs_alloc(hdl,
- 8 * sizeof (void *))) == NULL)
- goto out;
-
- if ((datasets = zfs_alloc(hdl,
- 8 * sizeof (void *))) == NULL)
- goto out;
-
- alloc = 8;
- } else {
- void *ptr;
-
- if ((ptr = zfs_realloc(hdl, mountpoints,
- alloc * sizeof (void *),
- alloc * 2 * sizeof (void *))) == NULL)
- goto out;
- mountpoints = ptr;
-
- if ((ptr = zfs_realloc(hdl, datasets,
- alloc * sizeof (void *),
- alloc * 2 * sizeof (void *))) == NULL)
- goto out;
- datasets = ptr;
-
- alloc *= 2;
- }
- }
-
- if ((mountpoints[used] = zfs_strdup(hdl,
- entry.mnt_mountp)) == NULL)
- goto out;
-
- /*
- * This is allowed to fail, in case there is some I/O error. It
- * is only used to determine if we need to remove the underlying
- * mountpoint, so failure is not fatal.
- */
- datasets[used] = make_dataset_handle(hdl, entry.mnt_special);
-
- used++;
- }
-
- /*
- * At this point, we have the entire list of filesystems, so sort it by
- * mountpoint.
- */
-#ifdef illumos
- sharearg.zhandle_arr = datasets;
- sharearg.zhandle_len = used;
- ret = zfs_init_libshare_arg(hdl, SA_INIT_SHARE_API_SELECTIVE,
- &sharearg);
- if (ret != 0)
- goto out;
-#endif
- qsort(mountpoints, used, sizeof (char *), mountpoint_compare);
-
- /*
- * Walk through and first unshare everything.
- */
- for (i = 0; i < used; i++) {
- zfs_share_proto_t *curr_proto;
- for (curr_proto = share_all_proto; *curr_proto != PROTO_END;
- curr_proto++) {
- if (is_shared(hdl, mountpoints[i], *curr_proto) &&
- unshare_one(hdl, mountpoints[i],
- mountpoints[i], *curr_proto) != 0)
- goto out;
- }
- }
-
- /*
- * Now unmount everything, removing the underlying directories as
- * appropriate.
- */
- for (i = 0; i < used; i++) {
- if (unmount_one(hdl, mountpoints[i], flags) != 0)
- goto out;
- }
-
- for (i = 0; i < used; i++) {
- if (datasets[i])
- remove_mountpoint(datasets[i]);
- }
-
- ret = 0;
-out:
- for (i = 0; i < used; i++) {
- if (datasets[i])
- zfs_close(datasets[i]);
- free(mountpoints[i]);
- }
- free(datasets);
- free(mountpoints);
-
- return (ret);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
deleted file mode 100644
index 434f77e27da91..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_pool.c
+++ /dev/null
@@ -1,4669 +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, 2020 by Delphix. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
- * Copyright 2016 Nexenta Systems, Inc.
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
- * Copyright (c) 2017 Datto Inc.
- * Copyright (c) 2017, Intel Corporation.
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <ctype.h>
-#include <errno.h>
-#include <devid.h>
-#include <fcntl.h>
-#include <libintl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <unistd.h>
-#include <libgen.h>
-#include <sys/zfs_ioctl.h>
-#include <dlfcn.h>
-
-#include "zfs_namecheck.h"
-#include "zfs_prop.h"
-#include "libzfs_impl.h"
-#include "zfs_comutil.h"
-#include "zfeature_common.h"
-
-static int read_efi_label(nvlist_t *, diskaddr_t *, boolean_t *);
-static boolean_t zpool_vdev_is_interior(const char *name);
-
-#define BACKUP_SLICE "s2"
-
-typedef struct prop_flags {
- int create:1; /* Validate property on creation */
- int import:1; /* Validate property on import */
-} prop_flags_t;
-
-/*
- * ====================================================================
- * zpool property functions
- * ====================================================================
- */
-
-static int
-zpool_get_all_props(zpool_handle_t *zhp)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
-
- if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
- return (-1);
-
- while (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_GET_PROPS, &zc) != 0) {
- if (errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- } else {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- }
-
- if (zcmd_read_dst_nvlist(hdl, &zc, &zhp->zpool_props) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
-
- zcmd_free_nvlists(&zc);
-
- return (0);
-}
-
-static int
-zpool_props_refresh(zpool_handle_t *zhp)
-{
- nvlist_t *old_props;
-
- old_props = zhp->zpool_props;
-
- if (zpool_get_all_props(zhp) != 0)
- return (-1);
-
- nvlist_free(old_props);
- return (0);
-}
-
-static char *
-zpool_get_prop_string(zpool_handle_t *zhp, zpool_prop_t prop,
- zprop_source_t *src)
-{
- nvlist_t *nv, *nvl;
- uint64_t ival;
- char *value;
- zprop_source_t source;
-
- nvl = zhp->zpool_props;
- if (nvlist_lookup_nvlist(nvl, zpool_prop_to_name(prop), &nv) == 0) {
- verify(nvlist_lookup_uint64(nv, ZPROP_SOURCE, &ival) == 0);
- source = ival;
- verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0);
- } else {
- source = ZPROP_SRC_DEFAULT;
- if ((value = (char *)zpool_prop_default_string(prop)) == NULL)
- value = "-";
- }
-
- if (src)
- *src = source;
-
- return (value);
-}
-
-uint64_t
-zpool_get_prop_int(zpool_handle_t *zhp, zpool_prop_t prop, zprop_source_t *src)
-{
- nvlist_t *nv, *nvl;
- uint64_t value;
- zprop_source_t source;
-
- if (zhp->zpool_props == NULL && zpool_get_all_props(zhp)) {
- /*
- * zpool_get_all_props() has most likely failed because
- * the pool is faulted, but if all we need is the top level
- * vdev's guid then get it from the zhp config nvlist.
- */
- if ((prop == ZPOOL_PROP_GUID) &&
- (nvlist_lookup_nvlist(zhp->zpool_config,
- ZPOOL_CONFIG_VDEV_TREE, &nv) == 0) &&
- (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &value)
- == 0)) {
- return (value);
- }
- return (zpool_prop_default_numeric(prop));
- }
-
- nvl = zhp->zpool_props;
- if (nvlist_lookup_nvlist(nvl, zpool_prop_to_name(prop), &nv) == 0) {
- verify(nvlist_lookup_uint64(nv, ZPROP_SOURCE, &value) == 0);
- source = value;
- verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0);
- } else {
- source = ZPROP_SRC_DEFAULT;
- value = zpool_prop_default_numeric(prop);
- }
-
- if (src)
- *src = source;
-
- return (value);
-}
-
-/*
- * Map VDEV STATE to printed strings.
- */
-const char *
-zpool_state_to_name(vdev_state_t state, vdev_aux_t aux)
-{
- switch (state) {
- case VDEV_STATE_CLOSED:
- case VDEV_STATE_OFFLINE:
- return (gettext("OFFLINE"));
- case VDEV_STATE_REMOVED:
- return (gettext("REMOVED"));
- case VDEV_STATE_CANT_OPEN:
- if (aux == VDEV_AUX_CORRUPT_DATA || aux == VDEV_AUX_BAD_LOG)
- return (gettext("FAULTED"));
- else if (aux == VDEV_AUX_SPLIT_POOL)
- return (gettext("SPLIT"));
- else
- return (gettext("UNAVAIL"));
- case VDEV_STATE_FAULTED:
- return (gettext("FAULTED"));
- case VDEV_STATE_DEGRADED:
- return (gettext("DEGRADED"));
- case VDEV_STATE_HEALTHY:
- return (gettext("ONLINE"));
-
- default:
- break;
- }
-
- return (gettext("UNKNOWN"));
-}
-
-/*
- * Map POOL STATE to printed strings.
- */
-const char *
-zpool_pool_state_to_name(pool_state_t state)
-{
- switch (state) {
- case POOL_STATE_ACTIVE:
- return (gettext("ACTIVE"));
- case POOL_STATE_EXPORTED:
- return (gettext("EXPORTED"));
- case POOL_STATE_DESTROYED:
- return (gettext("DESTROYED"));
- case POOL_STATE_SPARE:
- return (gettext("SPARE"));
- case POOL_STATE_L2CACHE:
- return (gettext("L2CACHE"));
- case POOL_STATE_UNINITIALIZED:
- return (gettext("UNINITIALIZED"));
- case POOL_STATE_UNAVAIL:
- return (gettext("UNAVAIL"));
- case POOL_STATE_POTENTIALLY_ACTIVE:
- return (gettext("POTENTIALLY_ACTIVE"));
- }
-
- return (gettext("UNKNOWN"));
-}
-
-/*
- * Get a zpool property value for 'prop' and return the value in
- * a pre-allocated buffer.
- */
-int
-zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
- zprop_source_t *srctype, boolean_t literal)
-{
- uint64_t intval;
- const char *strval;
- zprop_source_t src = ZPROP_SRC_NONE;
- nvlist_t *nvroot;
- vdev_stat_t *vs;
- uint_t vsc;
-
- if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
- switch (prop) {
- case ZPOOL_PROP_NAME:
- (void) strlcpy(buf, zpool_get_name(zhp), len);
- break;
-
- case ZPOOL_PROP_HEALTH:
- (void) strlcpy(buf,
- zpool_pool_state_to_name(POOL_STATE_UNAVAIL), len);
- break;
-
- case ZPOOL_PROP_GUID:
- intval = zpool_get_prop_int(zhp, prop, &src);
- (void) snprintf(buf, len, "%llu", intval);
- break;
-
- case ZPOOL_PROP_ALTROOT:
- case ZPOOL_PROP_CACHEFILE:
- case ZPOOL_PROP_COMMENT:
- if (zhp->zpool_props != NULL ||
- zpool_get_all_props(zhp) == 0) {
- (void) strlcpy(buf,
- zpool_get_prop_string(zhp, prop, &src),
- len);
- break;
- }
- /* FALLTHROUGH */
- default:
- (void) strlcpy(buf, "-", len);
- break;
- }
-
- if (srctype != NULL)
- *srctype = src;
- return (0);
- }
-
- if (zhp->zpool_props == NULL && zpool_get_all_props(zhp) &&
- prop != ZPOOL_PROP_NAME)
- return (-1);
-
- switch (zpool_prop_get_type(prop)) {
- case PROP_TYPE_STRING:
- (void) strlcpy(buf, zpool_get_prop_string(zhp, prop, &src),
- len);
- break;
-
- case PROP_TYPE_NUMBER:
- intval = zpool_get_prop_int(zhp, prop, &src);
-
- switch (prop) {
- case ZPOOL_PROP_SIZE:
- case ZPOOL_PROP_ALLOCATED:
- case ZPOOL_PROP_FREE:
- case ZPOOL_PROP_FREEING:
- case ZPOOL_PROP_LEAKED:
- if (literal) {
- (void) snprintf(buf, len, "%llu",
- (u_longlong_t)intval);
- } else {
- (void) zfs_nicenum(intval, buf, len);
- }
- break;
- case ZPOOL_PROP_BOOTSIZE:
- case ZPOOL_PROP_EXPANDSZ:
- case ZPOOL_PROP_CHECKPOINT:
- if (intval == 0) {
- (void) strlcpy(buf, "-", len);
- } else if (literal) {
- (void) snprintf(buf, len, "%llu",
- (u_longlong_t)intval);
- } else {
- (void) zfs_nicenum(intval, buf, len);
- }
- break;
- case ZPOOL_PROP_CAPACITY:
- if (literal) {
- (void) snprintf(buf, len, "%llu",
- (u_longlong_t)intval);
- } else {
- (void) snprintf(buf, len, "%llu%%",
- (u_longlong_t)intval);
- }
- break;
- case ZPOOL_PROP_FRAGMENTATION:
- if (intval == UINT64_MAX) {
- (void) strlcpy(buf, "-", len);
- } else {
- (void) snprintf(buf, len, "%llu%%",
- (u_longlong_t)intval);
- }
- break;
- case ZPOOL_PROP_DEDUPRATIO:
- (void) snprintf(buf, len, "%llu.%02llux",
- (u_longlong_t)(intval / 100),
- (u_longlong_t)(intval % 100));
- break;
- case ZPOOL_PROP_HEALTH:
- verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
- ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
- verify(nvlist_lookup_uint64_array(nvroot,
- ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &vsc)
- == 0);
-
- (void) strlcpy(buf, zpool_state_to_name(intval,
- vs->vs_aux), len);
- break;
- case ZPOOL_PROP_VERSION:
- if (intval >= SPA_VERSION_FEATURES) {
- (void) snprintf(buf, len, "-");
- break;
- }
- /* FALLTHROUGH */
- default:
- (void) snprintf(buf, len, "%llu", intval);
- }
- break;
-
- case PROP_TYPE_INDEX:
- intval = zpool_get_prop_int(zhp, prop, &src);
- if (zpool_prop_index_to_string(prop, intval, &strval)
- != 0)
- return (-1);
- (void) strlcpy(buf, strval, len);
- break;
-
- default:
- abort();
- }
-
- if (srctype)
- *srctype = src;
-
- return (0);
-}
-
-/*
- * Check if the bootfs name has the same pool name as it is set to.
- * Assuming bootfs is a valid dataset name.
- */
-static boolean_t
-bootfs_name_valid(const char *pool, const char *bootfs)
-{
- int len = strlen(pool);
-
- if (!zfs_name_valid(bootfs, ZFS_TYPE_FILESYSTEM|ZFS_TYPE_SNAPSHOT))
- return (B_FALSE);
-
- if (strncmp(pool, bootfs, len) == 0 &&
- (bootfs[len] == '/' || bootfs[len] == '\0'))
- return (B_TRUE);
-
- return (B_FALSE);
-}
-
-boolean_t
-zpool_is_bootable(zpool_handle_t *zhp)
-{
- char bootfs[ZFS_MAX_DATASET_NAME_LEN];
-
- return (zpool_get_prop(zhp, ZPOOL_PROP_BOOTFS, bootfs,
- sizeof (bootfs), NULL, B_FALSE) == 0 && strncmp(bootfs, "-",
- sizeof (bootfs)) != 0);
-}
-
-
-/*
- * Given an nvlist of zpool properties to be set, validate that they are
- * correct, and parse any numeric properties (index, boolean, etc) if they are
- * specified as strings.
- */
-static nvlist_t *
-zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
- nvlist_t *props, uint64_t version, prop_flags_t flags, char *errbuf)
-{
- nvpair_t *elem;
- nvlist_t *retprops;
- zpool_prop_t prop;
- char *strval;
- uint64_t intval;
- char *slash, *check;
- struct stat64 statbuf;
- zpool_handle_t *zhp;
-
- if (nvlist_alloc(&retprops, NV_UNIQUE_NAME, 0) != 0) {
- (void) no_memory(hdl);
- return (NULL);
- }
-
- elem = NULL;
- while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
- const char *propname = nvpair_name(elem);
-
- prop = zpool_name_to_prop(propname);
- if (prop == ZPOOL_PROP_INVAL && zpool_prop_feature(propname)) {
- int err;
- char *fname = strchr(propname, '@') + 1;
-
- err = zfeature_lookup_name(fname, NULL);
- if (err != 0) {
- ASSERT3U(err, ==, ENOENT);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid feature '%s'"), fname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (nvpair_type(elem) != DATA_TYPE_STRING) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a string"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- (void) nvpair_value_string(elem, &strval);
- if (strcmp(strval, ZFS_FEATURE_ENABLED) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' can only be set to "
- "'enabled'"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (nvlist_add_uint64(retprops, propname, 0) != 0) {
- (void) no_memory(hdl);
- goto error;
- }
- continue;
- }
-
- /*
- * Make sure this property is valid and applies to this type.
- */
- if (prop == ZPOOL_PROP_INVAL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid property '%s'"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (zpool_prop_readonly(prop)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' "
- "is readonly"), propname);
- (void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
- goto error;
- }
-
- if (zprop_parse_value(hdl, elem, prop, ZFS_TYPE_POOL, retprops,
- &strval, &intval, errbuf) != 0)
- goto error;
-
- /*
- * Perform additional checking for specific properties.
- */
- switch (prop) {
- case ZPOOL_PROP_VERSION:
- if (intval < version ||
- !SPA_VERSION_IS_SUPPORTED(intval)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' number %d is invalid."),
- propname, intval);
- (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
- goto error;
- }
- break;
-
- case ZPOOL_PROP_BOOTSIZE:
- if (!flags.create) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' can only be set during pool "
- "creation"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
-
- case ZPOOL_PROP_BOOTFS:
- if (flags.create || flags.import) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' cannot be set at creation "
- "or import time"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (version < SPA_VERSION_BOOTFS) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded to support "
- "'%s' property"), propname);
- (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
- goto error;
- }
-
- /*
- * bootfs property value has to be a dataset name and
- * the dataset has to be in the same pool as it sets to.
- */
- if (strval[0] != '\0' && !bootfs_name_valid(poolname,
- strval)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' "
- "is an invalid name"), strval);
- (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
- goto error;
- }
-
- if ((zhp = zpool_open_canfail(hdl, poolname)) == NULL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "could not open pool '%s'"), poolname);
- (void) zfs_error(hdl, EZFS_OPENFAILED, errbuf);
- goto error;
- }
- zpool_close(zhp);
- break;
-
- case ZPOOL_PROP_ALTROOT:
- if (!flags.create && !flags.import) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' can only be set during pool "
- "creation or import"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
-
- if (strval[0] != '/') {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "bad alternate root '%s'"), strval);
- (void) zfs_error(hdl, EZFS_BADPATH, errbuf);
- goto error;
- }
- break;
-
- case ZPOOL_PROP_CACHEFILE:
- if (strval[0] == '\0')
- break;
-
- if (strcmp(strval, "none") == 0)
- break;
-
- if (strval[0] != '/') {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' must be empty, an "
- "absolute path, or 'none'"), propname);
- (void) zfs_error(hdl, EZFS_BADPATH, errbuf);
- goto error;
- }
-
- slash = strrchr(strval, '/');
-
- if (slash[1] == '\0' || strcmp(slash, "/.") == 0 ||
- strcmp(slash, "/..") == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' is not a valid file"), strval);
- (void) zfs_error(hdl, EZFS_BADPATH, errbuf);
- goto error;
- }
-
- *slash = '\0';
-
- if (strval[0] != '\0' &&
- (stat64(strval, &statbuf) != 0 ||
- !S_ISDIR(statbuf.st_mode))) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' is not a valid directory"),
- strval);
- (void) zfs_error(hdl, EZFS_BADPATH, errbuf);
- goto error;
- }
-
- *slash = '/';
- break;
-
- case ZPOOL_PROP_COMMENT:
- for (check = strval; *check != '\0'; check++) {
- if (!isprint(*check)) {
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN,
- "comment may only have printable "
- "characters"));
- (void) zfs_error(hdl, EZFS_BADPROP,
- errbuf);
- goto error;
- }
- }
- if (strlen(strval) > ZPROP_MAX_COMMENT) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "comment must not exceed %d characters"),
- ZPROP_MAX_COMMENT);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
-
- case ZPOOL_PROP_READONLY:
- if (!flags.import) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' can only be set at "
- "import time"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
-
- case ZPOOL_PROP_TNAME:
- if (!flags.create) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s' can only be set at "
- "creation time"), propname);
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
-
- case ZPOOL_PROP_MULTIHOST:
- if (get_system_hostid() == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "requires a non-zero system hostid"));
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- goto error;
- }
- break;
-
- default:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "property '%s'(%d) not defined"), propname, prop);
- break;
- }
- }
-
- return (retprops);
-error:
- nvlist_free(retprops);
- return (NULL);
-}
-
-/*
- * Set zpool property : propname=propval.
- */
-int
-zpool_set_prop(zpool_handle_t *zhp, const char *propname, const char *propval)
-{
- zfs_cmd_t zc = { 0 };
- int ret = -1;
- char errbuf[1024];
- nvlist_t *nvl = NULL;
- nvlist_t *realprops;
- uint64_t version;
- prop_flags_t flags = { 0 };
-
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
- zhp->zpool_name);
-
- if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
- return (no_memory(zhp->zpool_hdl));
-
- if (nvlist_add_string(nvl, propname, propval) != 0) {
- nvlist_free(nvl);
- return (no_memory(zhp->zpool_hdl));
- }
-
- version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL);
- if ((realprops = zpool_valid_proplist(zhp->zpool_hdl,
- zhp->zpool_name, nvl, version, flags, errbuf)) == NULL) {
- nvlist_free(nvl);
- return (-1);
- }
-
- nvlist_free(nvl);
- nvl = realprops;
-
- /*
- * Execute the corresponding ioctl() to set this property.
- */
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
-
- if (zcmd_write_src_nvlist(zhp->zpool_hdl, &zc, nvl) != 0) {
- nvlist_free(nvl);
- return (-1);
- }
-
- ret = zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_SET_PROPS, &zc);
-
- zcmd_free_nvlists(&zc);
- nvlist_free(nvl);
-
- if (ret)
- (void) zpool_standard_error(zhp->zpool_hdl, errno, errbuf);
- else
- (void) zpool_props_refresh(zhp);
-
- return (ret);
-}
-
-int
-zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp)
-{
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- zprop_list_t *entry;
- char buf[ZFS_MAXPROPLEN];
- nvlist_t *features = NULL;
- zprop_list_t **last;
- boolean_t firstexpand = (NULL == *plp);
-
- if (zprop_expand_list(hdl, plp, ZFS_TYPE_POOL) != 0)
- return (-1);
-
- last = plp;
- while (*last != NULL)
- last = &(*last)->pl_next;
-
- if ((*plp)->pl_all)
- features = zpool_get_features(zhp);
-
- if ((*plp)->pl_all && firstexpand) {
- for (int i = 0; i < SPA_FEATURES; i++) {
- zprop_list_t *entry = zfs_alloc(hdl,
- sizeof (zprop_list_t));
- entry->pl_prop = ZPROP_INVAL;
- entry->pl_user_prop = zfs_asprintf(hdl, "feature@%s",
- spa_feature_table[i].fi_uname);
- entry->pl_width = strlen(entry->pl_user_prop);
- entry->pl_all = B_TRUE;
-
- *last = entry;
- last = &entry->pl_next;
- }
- }
-
- /* add any unsupported features */
- for (nvpair_t *nvp = nvlist_next_nvpair(features, NULL);
- nvp != NULL; nvp = nvlist_next_nvpair(features, nvp)) {
- char *propname;
- boolean_t found;
- zprop_list_t *entry;
-
- if (zfeature_is_supported(nvpair_name(nvp)))
- continue;
-
- propname = zfs_asprintf(hdl, "unsupported@%s",
- nvpair_name(nvp));
-
- /*
- * Before adding the property to the list make sure that no
- * other pool already added the same property.
- */
- found = B_FALSE;
- entry = *plp;
- while (entry != NULL) {
- if (entry->pl_user_prop != NULL &&
- strcmp(propname, entry->pl_user_prop) == 0) {
- found = B_TRUE;
- break;
- }
- entry = entry->pl_next;
- }
- if (found) {
- free(propname);
- continue;
- }
-
- entry = zfs_alloc(hdl, sizeof (zprop_list_t));
- entry->pl_prop = ZPROP_INVAL;
- entry->pl_user_prop = propname;
- entry->pl_width = strlen(entry->pl_user_prop);
- entry->pl_all = B_TRUE;
-
- *last = entry;
- last = &entry->pl_next;
- }
-
- for (entry = *plp; entry != NULL; entry = entry->pl_next) {
-
- if (entry->pl_fixed)
- continue;
-
- if (entry->pl_prop != ZPROP_INVAL &&
- zpool_get_prop(zhp, entry->pl_prop, buf, sizeof (buf),
- NULL, B_FALSE) == 0) {
- if (strlen(buf) > entry->pl_width)
- entry->pl_width = strlen(buf);
- }
- }
-
- return (0);
-}
-
-/*
- * Get the state for the given feature on the given ZFS pool.
- */
-int
-zpool_prop_get_feature(zpool_handle_t *zhp, const char *propname, char *buf,
- size_t len)
-{
- uint64_t refcount;
- boolean_t found = B_FALSE;
- nvlist_t *features = zpool_get_features(zhp);
- boolean_t supported;
- const char *feature = strchr(propname, '@') + 1;
-
- supported = zpool_prop_feature(propname);
- ASSERT(supported || zpool_prop_unsupported(propname));
-
- /*
- * Convert from feature name to feature guid. This conversion is
- * unecessary for unsupported@... properties because they already
- * use guids.
- */
- if (supported) {
- int ret;
- spa_feature_t fid;
-
- ret = zfeature_lookup_name(feature, &fid);
- if (ret != 0) {
- (void) strlcpy(buf, "-", len);
- return (ENOTSUP);
- }
- feature = spa_feature_table[fid].fi_guid;
- }
-
- if (nvlist_lookup_uint64(features, feature, &refcount) == 0)
- found = B_TRUE;
-
- if (supported) {
- if (!found) {
- (void) strlcpy(buf, ZFS_FEATURE_DISABLED, len);
- } else {
- if (refcount == 0)
- (void) strlcpy(buf, ZFS_FEATURE_ENABLED, len);
- else
- (void) strlcpy(buf, ZFS_FEATURE_ACTIVE, len);
- }
- } else {
- if (found) {
- if (refcount == 0) {
- (void) strcpy(buf, ZFS_UNSUPPORTED_INACTIVE);
- } else {
- (void) strcpy(buf, ZFS_UNSUPPORTED_READONLY);
- }
- } else {
- (void) strlcpy(buf, "-", len);
- return (ENOTSUP);
- }
- }
-
- return (0);
-}
-
-/*
- * Don't start the slice at the default block of 34; many storage
- * devices will use a stripe width of 128k, so start there instead.
- */
-#define NEW_START_BLOCK 256
-
-/*
- * Validate the given pool name, optionally putting an extended error message in
- * 'buf'.
- */
-boolean_t
-zpool_name_valid(libzfs_handle_t *hdl, boolean_t isopen, const char *pool)
-{
- namecheck_err_t why;
- char what;
- int ret;
-
- ret = pool_namecheck(pool, &why, &what);
-
- /*
- * The rules for reserved pool names were extended at a later point.
- * But we need to support users with existing pools that may now be
- * invalid. So we only check for this expanded set of names during a
- * create (or import), and only in userland.
- */
- if (ret == 0 && !isopen &&
- (strncmp(pool, "mirror", 6) == 0 ||
- strncmp(pool, "raidz", 5) == 0 ||
- strncmp(pool, "spare", 5) == 0 ||
- strcmp(pool, "log") == 0)) {
- if (hdl != NULL)
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "name is reserved"));
- return (B_FALSE);
- }
-
-
- if (ret != 0) {
- if (hdl != NULL) {
- switch (why) {
- case NAME_ERR_TOOLONG:
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "name is too long"));
- break;
-
- case NAME_ERR_INVALCHAR:
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "invalid character "
- "'%c' in pool name"), what);
- break;
-
- case NAME_ERR_NOLETTER:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "name must begin with a letter"));
- break;
-
- case NAME_ERR_RESERVED:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "name is reserved"));
- break;
-
- case NAME_ERR_DISKLIKE:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool name is reserved"));
- break;
-
- case NAME_ERR_LEADING_SLASH:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "leading slash in name"));
- break;
-
- case NAME_ERR_EMPTY_COMPONENT:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "empty component in name"));
- break;
-
- case NAME_ERR_TRAILING_SLASH:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "trailing slash in name"));
- break;
-
- case NAME_ERR_MULTIPLE_DELIMITERS:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "multiple '@' and/or '#' delimiters in "
- "name"));
- break;
-
- default:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "(%d) not defined"), why);
- break;
- }
- }
- return (B_FALSE);
- }
-
- return (B_TRUE);
-}
-
-/*
- * Open a handle to the given pool, even if the pool is currently in the FAULTED
- * state.
- */
-zpool_handle_t *
-zpool_open_canfail(libzfs_handle_t *hdl, const char *pool)
-{
- zpool_handle_t *zhp;
- boolean_t missing;
-
- /*
- * Make sure the pool name is valid.
- */
- if (!zpool_name_valid(hdl, B_TRUE, pool)) {
- (void) zfs_error_fmt(hdl, EZFS_INVALIDNAME,
- dgettext(TEXT_DOMAIN, "cannot open '%s'"),
- pool);
- return (NULL);
- }
-
- if ((zhp = zfs_alloc(hdl, sizeof (zpool_handle_t))) == NULL)
- return (NULL);
-
- zhp->zpool_hdl = hdl;
- (void) strlcpy(zhp->zpool_name, pool, sizeof (zhp->zpool_name));
-
- if (zpool_refresh_stats(zhp, &missing) != 0) {
- zpool_close(zhp);
- return (NULL);
- }
-
- if (missing) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "no such pool"));
- (void) zfs_error_fmt(hdl, EZFS_NOENT,
- dgettext(TEXT_DOMAIN, "cannot open '%s'"), pool);
- zpool_close(zhp);
- return (NULL);
- }
-
- return (zhp);
-}
-
-/*
- * Like the above, but silent on error. Used when iterating over pools (because
- * the configuration cache may be out of date).
- */
-int
-zpool_open_silent(libzfs_handle_t *hdl, const char *pool, zpool_handle_t **ret)
-{
- zpool_handle_t *zhp;
- boolean_t missing;
-
- if ((zhp = zfs_alloc(hdl, sizeof (zpool_handle_t))) == NULL)
- return (-1);
-
- zhp->zpool_hdl = hdl;
- (void) strlcpy(zhp->zpool_name, pool, sizeof (zhp->zpool_name));
-
- if (zpool_refresh_stats(zhp, &missing) != 0) {
- zpool_close(zhp);
- return (-1);
- }
-
- if (missing) {
- zpool_close(zhp);
- *ret = NULL;
- return (0);
- }
-
- *ret = zhp;
- return (0);
-}
-
-/*
- * Similar to zpool_open_canfail(), but refuses to open pools in the faulted
- * state.
- */
-zpool_handle_t *
-zpool_open(libzfs_handle_t *hdl, const char *pool)
-{
- zpool_handle_t *zhp;
-
- if ((zhp = zpool_open_canfail(hdl, pool)) == NULL)
- return (NULL);
-
- if (zhp->zpool_state == POOL_STATE_UNAVAIL) {
- (void) zfs_error_fmt(hdl, EZFS_POOLUNAVAIL,
- dgettext(TEXT_DOMAIN, "cannot open '%s'"), zhp->zpool_name);
- zpool_close(zhp);
- return (NULL);
- }
-
- return (zhp);
-}
-
-/*
- * Close the handle. Simply frees the memory associated with the handle.
- */
-void
-zpool_close(zpool_handle_t *zhp)
-{
- nvlist_free(zhp->zpool_config);
- nvlist_free(zhp->zpool_old_config);
- nvlist_free(zhp->zpool_props);
- free(zhp);
-}
-
-/*
- * Return the name of the pool.
- */
-const char *
-zpool_get_name(zpool_handle_t *zhp)
-{
- return (zhp->zpool_name);
-}
-
-
-/*
- * Return the state of the pool (ACTIVE or UNAVAILABLE)
- */
-int
-zpool_get_state(zpool_handle_t *zhp)
-{
- return (zhp->zpool_state);
-}
-
-/*
- * Check if vdev list contains a special vdev
- */
-static boolean_t
-zpool_has_special_vdev(nvlist_t *nvroot)
-{
- nvlist_t **child;
- uint_t children;
-
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, &child,
- &children) == 0) {
- for (uint_t c = 0; c < children; c++) {
- char *bias;
-
- if (nvlist_lookup_string(child[c],
- ZPOOL_CONFIG_ALLOCATION_BIAS, &bias) == 0 &&
- strcmp(bias, VDEV_ALLOC_BIAS_SPECIAL) == 0) {
- return (B_TRUE);
- }
- }
- }
- return (B_FALSE);
-}
-
-/*
- * Create the named pool, using the provided vdev list. It is assumed
- * that the consumer has already validated the contents of the nvlist, so we
- * don't have to worry about error semantics.
- */
-int
-zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
- nvlist_t *props, nvlist_t *fsprops)
-{
- zfs_cmd_t zc = { 0 };
- nvlist_t *zc_fsprops = NULL;
- nvlist_t *zc_props = NULL;
- char msg[1024];
- int ret = -1;
-
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot create '%s'"), pool);
-
- if (!zpool_name_valid(hdl, B_FALSE, pool))
- return (zfs_error(hdl, EZFS_INVALIDNAME, msg));
-
- if (zcmd_write_conf_nvlist(hdl, &zc, nvroot) != 0)
- return (-1);
-
- if (props) {
- prop_flags_t flags = { .create = B_TRUE, .import = B_FALSE };
-
- if ((zc_props = zpool_valid_proplist(hdl, pool, props,
- SPA_VERSION_1, flags, msg)) == NULL) {
- goto create_failed;
- }
- }
-
- if (fsprops) {
- uint64_t zoned;
- char *zonestr;
-
- zoned = ((nvlist_lookup_string(fsprops,
- zfs_prop_to_name(ZFS_PROP_ZONED), &zonestr) == 0) &&
- strcmp(zonestr, "on") == 0);
-
- if ((zc_fsprops = zfs_valid_proplist(hdl, ZFS_TYPE_FILESYSTEM,
- fsprops, zoned, NULL, NULL, msg)) == NULL) {
- goto create_failed;
- }
-
- if (nvlist_exists(zc_fsprops,
- zfs_prop_to_name(ZFS_PROP_SPECIAL_SMALL_BLOCKS)) &&
- !zpool_has_special_vdev(nvroot)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "%s property requires a special vdev"),
- zfs_prop_to_name(ZFS_PROP_SPECIAL_SMALL_BLOCKS));
- (void) zfs_error(hdl, EZFS_BADPROP, msg);
- goto create_failed;
- }
-
- if (!zc_props &&
- (nvlist_alloc(&zc_props, NV_UNIQUE_NAME, 0) != 0)) {
- goto create_failed;
- }
- if (nvlist_add_nvlist(zc_props,
- ZPOOL_ROOTFS_PROPS, zc_fsprops) != 0) {
- goto create_failed;
- }
- }
-
- if (zc_props && zcmd_write_src_nvlist(hdl, &zc, zc_props) != 0)
- goto create_failed;
-
- (void) strlcpy(zc.zc_name, pool, sizeof (zc.zc_name));
-
- if ((ret = zfs_ioctl(hdl, ZFS_IOC_POOL_CREATE, &zc)) != 0) {
-
- zcmd_free_nvlists(&zc);
- nvlist_free(zc_props);
- nvlist_free(zc_fsprops);
-
- switch (errno) {
- case EBUSY:
- /*
- * This can happen if the user has specified the same
- * device multiple times. We can't reliably detect this
- * until we try to add it and see we already have a
- * label.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "one or more vdevs refer to the same device"));
- return (zfs_error(hdl, EZFS_BADDEV, msg));
-
- case ERANGE:
- /*
- * This happens if the record size is smaller or larger
- * than the allowed size range, or not a power of 2.
- *
- * NOTE: although zfs_valid_proplist is called earlier,
- * this case may have slipped through since the
- * pool does not exist yet and it is therefore
- * impossible to read properties e.g. max blocksize
- * from the pool.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "record size invalid"));
- return (zfs_error(hdl, EZFS_BADPROP, msg));
-
- case EOVERFLOW:
- /*
- * This occurs when one of the devices is below
- * SPA_MINDEVSIZE. Unfortunately, we can't detect which
- * device was the problem device since there's no
- * reliable way to determine device size from userland.
- */
- {
- char buf[64];
-
- zfs_nicenum(SPA_MINDEVSIZE, buf, sizeof (buf));
-
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "one or more devices is less than the "
- "minimum size (%s)"), buf);
- }
- return (zfs_error(hdl, EZFS_BADDEV, msg));
-
- case ENOSPC:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "one or more devices is out of space"));
- return (zfs_error(hdl, EZFS_BADDEV, msg));
-
- case ENOTBLK:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "cache device must be a disk or disk slice"));
- return (zfs_error(hdl, EZFS_BADDEV, msg));
-
- default:
- return (zpool_standard_error(hdl, errno, msg));
- }
- }
-
-create_failed:
- zcmd_free_nvlists(&zc);
- nvlist_free(zc_props);
- nvlist_free(zc_fsprops);
- return (ret);
-}
-
-/*
- * Destroy the given pool. It is up to the caller to ensure that there are no
- * datasets left in the pool.
- */
-int
-zpool_destroy(zpool_handle_t *zhp, const char *log_str)
-{
- zfs_cmd_t zc = { 0 };
- zfs_handle_t *zfp = NULL;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- char msg[1024];
-
- if (zhp->zpool_state == POOL_STATE_ACTIVE &&
- (zfp = zfs_open(hdl, zhp->zpool_name, ZFS_TYPE_FILESYSTEM)) == NULL)
- return (-1);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_history = (uint64_t)(uintptr_t)log_str;
-
- if (zfs_ioctl(hdl, ZFS_IOC_POOL_DESTROY, &zc) != 0) {
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot destroy '%s'"), zhp->zpool_name);
-
- if (errno == EROFS) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "one or more devices is read only"));
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- } else {
- (void) zpool_standard_error(hdl, errno, msg);
- }
-
- if (zfp)
- zfs_close(zfp);
- return (-1);
- }
-
- if (zfp) {
- remove_mountpoint(zfp);
- zfs_close(zfp);
- }
-
- return (0);
-}
-
-/*
- * Create a checkpoint in the given pool.
- */
-int
-zpool_checkpoint(zpool_handle_t *zhp)
-{
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- char msg[1024];
- int error;
-
- error = lzc_pool_checkpoint(zhp->zpool_name);
- if (error != 0) {
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot checkpoint '%s'"), zhp->zpool_name);
- (void) zpool_standard_error(hdl, error, msg);
- return (-1);
- }
-
- return (0);
-}
-
-/*
- * Discard the checkpoint from the given pool.
- */
-int
-zpool_discard_checkpoint(zpool_handle_t *zhp)
-{
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- char msg[1024];
- int error;
-
- error = lzc_pool_checkpoint_discard(zhp->zpool_name);
- if (error != 0) {
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot discard checkpoint in '%s'"), zhp->zpool_name);
- (void) zpool_standard_error(hdl, error, msg);
- return (-1);
- }
-
- return (0);
-}
-
-/*
- * Add the given vdevs to the pool. The caller must have already performed the
- * necessary verification to ensure that the vdev specification is well-formed.
- */
-int
-zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
-{
- zfs_cmd_t zc = { 0 };
- int ret;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- char msg[1024];
- nvlist_t **spares, **l2cache;
- uint_t nspares, nl2cache;
-
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot add to '%s'"), zhp->zpool_name);
-
- if (zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL) <
- SPA_VERSION_SPARES &&
- nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- &spares, &nspares) == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool must be "
- "upgraded to add hot spares"));
- return (zfs_error(hdl, EZFS_BADVERSION, msg));
- }
-
- if (zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL) <
- SPA_VERSION_L2CACHE &&
- nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
- &l2cache, &nl2cache) == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool must be "
- "upgraded to add cache devices"));
- return (zfs_error(hdl, EZFS_BADVERSION, msg));
- }
-
- if (zcmd_write_conf_nvlist(hdl, &zc, nvroot) != 0)
- return (-1);
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
-
- if (zfs_ioctl(hdl, ZFS_IOC_VDEV_ADD, &zc) != 0) {
- switch (errno) {
- case EBUSY:
- /*
- * This can happen if the user has specified the same
- * device multiple times. We can't reliably detect this
- * until we try to add it and see we already have a
- * label.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "one or more vdevs refer to the same device"));
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- break;
-
- case EINVAL:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid config; a pool with removing/removed "
- "vdevs does not support adding raidz vdevs"));
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- break;
-
- case EOVERFLOW:
- /*
- * This occurrs when one of the devices is below
- * SPA_MINDEVSIZE. Unfortunately, we can't detect which
- * device was the problem device since there's no
- * reliable way to determine device size from userland.
- */
- {
- char buf[64];
-
- zfs_nicenum(SPA_MINDEVSIZE, buf, sizeof (buf));
-
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "device is less than the minimum "
- "size (%s)"), buf);
- }
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- break;
-
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded to add these vdevs"));
- (void) zfs_error(hdl, EZFS_BADVERSION, msg);
- break;
-
- case EDOM:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "root pool can not have multiple vdevs"
- " or separate logs"));
- (void) zfs_error(hdl, EZFS_POOL_NOTSUP, msg);
- break;
-
- case ENOTBLK:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "cache device must be a disk or disk slice"));
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- break;
-
- default:
- (void) zpool_standard_error(hdl, errno, msg);
- }
-
- ret = -1;
- } else {
- ret = 0;
- }
-
- zcmd_free_nvlists(&zc);
-
- return (ret);
-}
-
-/*
- * Exports the pool from the system. The caller must ensure that there are no
- * mounted datasets in the pool.
- */
-static int
-zpool_export_common(zpool_handle_t *zhp, boolean_t force, boolean_t hardforce,
- const char *log_str)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
-
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot export '%s'"), zhp->zpool_name);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_cookie = force;
- zc.zc_guid = hardforce;
- zc.zc_history = (uint64_t)(uintptr_t)log_str;
-
- if (zfs_ioctl(zhp->zpool_hdl, ZFS_IOC_POOL_EXPORT, &zc) != 0) {
- switch (errno) {
- case EXDEV:
- zfs_error_aux(zhp->zpool_hdl, dgettext(TEXT_DOMAIN,
- "use '-f' to override the following errors:\n"
- "'%s' has an active shared spare which could be"
- " used by other pools once '%s' is exported."),
- zhp->zpool_name, zhp->zpool_name);
- return (zfs_error(zhp->zpool_hdl, EZFS_ACTIVE_SPARE,
- msg));
- default:
- return (zpool_standard_error_fmt(zhp->zpool_hdl, errno,
- msg));
- }
- }
-
- return (0);
-}
-
-int
-zpool_export(zpool_handle_t *zhp, boolean_t force, const char *log_str)
-{
- return (zpool_export_common(zhp, force, B_FALSE, log_str));
-}
-
-int
-zpool_export_force(zpool_handle_t *zhp, const char *log_str)
-{
- return (zpool_export_common(zhp, B_TRUE, B_TRUE, log_str));
-}
-
-static void
-zpool_rewind_exclaim(libzfs_handle_t *hdl, const char *name, boolean_t dryrun,
- nvlist_t *config)
-{
- nvlist_t *nv = NULL;
- uint64_t rewindto;
- int64_t loss = -1;
- struct tm t;
- char timestr[128];
-
- if (!hdl->libzfs_printerr || config == NULL)
- return;
-
- if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0 ||
- nvlist_lookup_nvlist(nv, ZPOOL_CONFIG_REWIND_INFO, &nv) != 0) {
- return;
- }
-
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0)
- return;
- (void) nvlist_lookup_int64(nv, ZPOOL_CONFIG_REWIND_TIME, &loss);
-
- if (localtime_r((time_t *)&rewindto, &t) != NULL &&
- strftime(timestr, 128, 0, &t) != 0) {
- if (dryrun) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "Would be able to return %s "
- "to its state as of %s.\n"),
- name, timestr);
- } else {
- (void) printf(dgettext(TEXT_DOMAIN,
- "Pool %s returned to its state as of %s.\n"),
- name, timestr);
- }
- if (loss > 120) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "%s approximately %lld "),
- dryrun ? "Would discard" : "Discarded",
- (loss + 30) / 60);
- (void) printf(dgettext(TEXT_DOMAIN,
- "minutes of transactions.\n"));
- } else if (loss > 0) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "%s approximately %lld "),
- dryrun ? "Would discard" : "Discarded", loss);
- (void) printf(dgettext(TEXT_DOMAIN,
- "seconds of transactions.\n"));
- }
- }
-}
-
-void
-zpool_explain_recover(libzfs_handle_t *hdl, const char *name, int reason,
- nvlist_t *config)
-{
- nvlist_t *nv = NULL;
- int64_t loss = -1;
- uint64_t edata = UINT64_MAX;
- uint64_t rewindto;
- struct tm t;
- char timestr[128];
-
- if (!hdl->libzfs_printerr)
- return;
-
- if (reason >= 0)
- (void) printf(dgettext(TEXT_DOMAIN, "action: "));
- else
- (void) printf(dgettext(TEXT_DOMAIN, "\t"));
-
- /* All attempted rewinds failed if ZPOOL_CONFIG_LOAD_TIME missing */
- if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0 ||
- nvlist_lookup_nvlist(nv, ZPOOL_CONFIG_REWIND_INFO, &nv) != 0 ||
- nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0)
- goto no_info;
-
- (void) nvlist_lookup_int64(nv, ZPOOL_CONFIG_REWIND_TIME, &loss);
- (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_DATA_ERRORS,
- &edata);
-
- (void) printf(dgettext(TEXT_DOMAIN,
- "Recovery is possible, but will result in some data loss.\n"));
-
- if (localtime_r((time_t *)&rewindto, &t) != NULL &&
- strftime(timestr, 128, 0, &t) != 0) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "\tReturning the pool to its state as of %s\n"
- "\tshould correct the problem. "),
- timestr);
- } else {
- (void) printf(dgettext(TEXT_DOMAIN,
- "\tReverting the pool to an earlier state "
- "should correct the problem.\n\t"));
- }
-
- if (loss > 120) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "Approximately %lld minutes of data\n"
- "\tmust be discarded, irreversibly. "), (loss + 30) / 60);
- } else if (loss > 0) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "Approximately %lld seconds of data\n"
- "\tmust be discarded, irreversibly. "), loss);
- }
- if (edata != 0 && edata != UINT64_MAX) {
- if (edata == 1) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "After rewind, at least\n"
- "\tone persistent user-data error will remain. "));
- } else {
- (void) printf(dgettext(TEXT_DOMAIN,
- "After rewind, several\n"
- "\tpersistent user-data errors will remain. "));
- }
- }
- (void) printf(dgettext(TEXT_DOMAIN,
- "Recovery can be attempted\n\tby executing 'zpool %s -F %s'. "),
- reason >= 0 ? "clear" : "import", name);
-
- (void) printf(dgettext(TEXT_DOMAIN,
- "A scrub of the pool\n"
- "\tis strongly recommended after recovery.\n"));
- return;
-
-no_info:
- (void) printf(dgettext(TEXT_DOMAIN,
- "Destroy and re-create the pool from\n\ta backup source.\n"));
-}
-
-/*
- * zpool_import() is a contracted interface. Should be kept the same
- * if possible.
- *
- * Applications should use zpool_import_props() to import a pool with
- * new properties value to be set.
- */
-int
-zpool_import(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
- char *altroot)
-{
- nvlist_t *props = NULL;
- int ret;
-
- if (altroot != NULL) {
- if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) {
- return (zfs_error_fmt(hdl, EZFS_NOMEM,
- dgettext(TEXT_DOMAIN, "cannot import '%s'"),
- newname));
- }
-
- if (nvlist_add_string(props,
- zpool_prop_to_name(ZPOOL_PROP_ALTROOT), altroot) != 0 ||
- nvlist_add_string(props,
- zpool_prop_to_name(ZPOOL_PROP_CACHEFILE), "none") != 0) {
- nvlist_free(props);
- return (zfs_error_fmt(hdl, EZFS_NOMEM,
- dgettext(TEXT_DOMAIN, "cannot import '%s'"),
- newname));
- }
- }
-
- ret = zpool_import_props(hdl, config, newname, props,
- ZFS_IMPORT_NORMAL);
- nvlist_free(props);
- return (ret);
-}
-
-static void
-print_vdev_tree(libzfs_handle_t *hdl, const char *name, nvlist_t *nv,
- int indent)
-{
- nvlist_t **child;
- uint_t c, children;
- char *vname;
- uint64_t is_log = 0;
-
- (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_LOG,
- &is_log);
-
- if (name != NULL)
- (void) printf("\t%*s%s%s\n", indent, "", name,
- is_log ? " [log]" : "");
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0)
- return;
-
- for (c = 0; c < children; c++) {
- vname = zpool_vdev_name(hdl, NULL, child[c], VDEV_NAME_TYPE_ID);
- print_vdev_tree(hdl, vname, child[c], indent + 2);
- free(vname);
- }
-}
-
-void
-zpool_print_unsup_feat(nvlist_t *config)
-{
- nvlist_t *nvinfo, *unsup_feat;
-
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nvinfo) ==
- 0);
- verify(nvlist_lookup_nvlist(nvinfo, ZPOOL_CONFIG_UNSUP_FEAT,
- &unsup_feat) == 0);
-
- for (nvpair_t *nvp = nvlist_next_nvpair(unsup_feat, NULL); nvp != NULL;
- nvp = nvlist_next_nvpair(unsup_feat, nvp)) {
- char *desc;
-
- verify(nvpair_type(nvp) == DATA_TYPE_STRING);
- verify(nvpair_value_string(nvp, &desc) == 0);
-
- if (strlen(desc) > 0)
- (void) printf("\t%s (%s)\n", nvpair_name(nvp), desc);
- else
- (void) printf("\t%s\n", nvpair_name(nvp));
- }
-}
-
-/*
- * Import the given pool using the known configuration and a list of
- * properties to be set. The configuration should have come from
- * zpool_find_import(). The 'newname' parameters control whether the pool
- * is imported with a different name.
- */
-int
-zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
- nvlist_t *props, int flags)
-{
- zfs_cmd_t zc = { 0 };
- zpool_load_policy_t policy;
- nvlist_t *nv = NULL;
- nvlist_t *nvinfo = NULL;
- nvlist_t *missing = NULL;
- char *thename;
- char *origname;
- int ret;
- int error = 0;
- char errbuf[1024];
-
- verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
- &origname) == 0);
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot import pool '%s'"), origname);
-
- if (newname != NULL) {
- if (!zpool_name_valid(hdl, B_FALSE, newname))
- return (zfs_error_fmt(hdl, EZFS_INVALIDNAME,
- dgettext(TEXT_DOMAIN, "cannot import '%s'"),
- newname));
- thename = (char *)newname;
- } else {
- thename = origname;
- }
-
- if (props != NULL) {
- uint64_t version;
- prop_flags_t flags = { .create = B_FALSE, .import = B_TRUE };
-
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
- &version) == 0);
-
- if ((props = zpool_valid_proplist(hdl, origname,
- props, version, flags, errbuf)) == NULL)
- return (-1);
- if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
- nvlist_free(props);
- return (-1);
- }
- nvlist_free(props);
- }
-
- (void) strlcpy(zc.zc_name, thename, sizeof (zc.zc_name));
-
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
- &zc.zc_guid) == 0);
-
- if (zcmd_write_conf_nvlist(hdl, &zc, config) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- if (zcmd_alloc_dst_nvlist(hdl, &zc, zc.zc_nvlist_conf_size * 2) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
-
- zc.zc_cookie = flags;
- while ((ret = zfs_ioctl(hdl, ZFS_IOC_POOL_IMPORT, &zc)) != 0 &&
- errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- }
- if (ret != 0)
- error = errno;
-
- (void) zcmd_read_dst_nvlist(hdl, &zc, &nv);
-
- zcmd_free_nvlists(&zc);
-
- zpool_get_load_policy(config, &policy);
-
- if (error) {
- char desc[1024];
- char aux[256];
-
- /*
- * Dry-run failed, but we print out what success
- * looks like if we found a best txg
- */
- if (policy.zlp_rewind & ZPOOL_TRY_REWIND) {
- zpool_rewind_exclaim(hdl, newname ? origname : thename,
- B_TRUE, nv);
- nvlist_free(nv);
- return (-1);
- }
-
- if (newname == NULL)
- (void) snprintf(desc, sizeof (desc),
- dgettext(TEXT_DOMAIN, "cannot import '%s'"),
- thename);
- else
- (void) snprintf(desc, sizeof (desc),
- dgettext(TEXT_DOMAIN, "cannot import '%s' as '%s'"),
- origname, thename);
-
- switch (error) {
- case ENOTSUP:
- if (nv != NULL && nvlist_lookup_nvlist(nv,
- ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0 &&
- nvlist_exists(nvinfo, ZPOOL_CONFIG_UNSUP_FEAT)) {
- (void) printf(dgettext(TEXT_DOMAIN, "This "
- "pool uses the following feature(s) not "
- "supported by this system:\n"));
- zpool_print_unsup_feat(nv);
- if (nvlist_exists(nvinfo,
- ZPOOL_CONFIG_CAN_RDONLY)) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "All unsupported features are only "
- "required for writing to the pool."
- "\nThe pool can be imported using "
- "'-o readonly=on'.\n"));
- }
- }
- /*
- * Unsupported version.
- */
- (void) zfs_error(hdl, EZFS_BADVERSION, desc);
- break;
-
- case EREMOTEIO:
- if (nv != NULL && nvlist_lookup_nvlist(nv,
- ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0) {
- char *hostname = "<unknown>";
- uint64_t hostid = 0;
- mmp_state_t mmp_state;
-
- mmp_state = fnvlist_lookup_uint64(nvinfo,
- ZPOOL_CONFIG_MMP_STATE);
-
- if (nvlist_exists(nvinfo,
- ZPOOL_CONFIG_MMP_HOSTNAME))
- hostname = fnvlist_lookup_string(nvinfo,
- ZPOOL_CONFIG_MMP_HOSTNAME);
-
- if (nvlist_exists(nvinfo,
- ZPOOL_CONFIG_MMP_HOSTID))
- hostid = fnvlist_lookup_uint64(nvinfo,
- ZPOOL_CONFIG_MMP_HOSTID);
-
- if (mmp_state == MMP_STATE_ACTIVE) {
- (void) snprintf(aux, sizeof (aux),
- dgettext(TEXT_DOMAIN, "pool is imp"
- "orted on host '%s' (hostid=%lx).\n"
- "Export the pool on the other "
- "system, then run 'zpool import'."),
- hostname, (unsigned long) hostid);
- } else if (mmp_state == MMP_STATE_NO_HOSTID) {
- (void) snprintf(aux, sizeof (aux),
- dgettext(TEXT_DOMAIN, "pool has "
- "the multihost property on and "
- "the\nsystem's hostid is not "
- "set.\n"));
- }
-
- (void) zfs_error_aux(hdl, aux);
- }
- (void) zfs_error(hdl, EZFS_ACTIVE_POOL, desc);
- break;
-
- case EINVAL:
- (void) zfs_error(hdl, EZFS_INVALCONFIG, desc);
- break;
-
- case EROFS:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "one or more devices is read only"));
- (void) zfs_error(hdl, EZFS_BADDEV, desc);
- break;
-
- case ENXIO:
- if (nv && nvlist_lookup_nvlist(nv,
- ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0 &&
- nvlist_lookup_nvlist(nvinfo,
- ZPOOL_CONFIG_MISSING_DEVICES, &missing) == 0) {
- (void) printf(dgettext(TEXT_DOMAIN,
- "The devices below are missing or "
- "corrupted, use '-m' to import the pool "
- "anyway:\n"));
- print_vdev_tree(hdl, NULL, missing, 2);
- (void) printf("\n");
- }
- (void) zpool_standard_error(hdl, error, desc);
- break;
-
- case EEXIST:
- (void) zpool_standard_error(hdl, error, desc);
- break;
- case ENAMETOOLONG:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "new name of at least one dataset is longer than "
- "the maximum allowable length"));
- (void) zfs_error(hdl, EZFS_NAMETOOLONG, desc);
- break;
- default:
- (void) zpool_standard_error(hdl, error, desc);
- zpool_explain_recover(hdl,
- newname ? origname : thename, -error, nv);
- break;
- }
-
- nvlist_free(nv);
- ret = -1;
- } else {
- zpool_handle_t *zhp;
-
- /*
- * This should never fail, but play it safe anyway.
- */
- if (zpool_open_silent(hdl, thename, &zhp) != 0)
- ret = -1;
- else if (zhp != NULL)
- zpool_close(zhp);
- if (policy.zlp_rewind &
- (ZPOOL_DO_REWIND | ZPOOL_TRY_REWIND)) {
- zpool_rewind_exclaim(hdl, newname ? origname : thename,
- ((policy.zlp_rewind & ZPOOL_TRY_REWIND) != 0), nv);
- }
- nvlist_free(nv);
- return (0);
- }
-
- return (ret);
-}
-
-/*
- * Scan the pool.
- */
-int
-zpool_scan(zpool_handle_t *zhp, pool_scan_func_t func, pool_scrub_cmd_t cmd)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- int err;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_cookie = func;
- zc.zc_flags = cmd;
-
- if (zfs_ioctl(hdl, ZFS_IOC_POOL_SCAN, &zc) == 0)
- return (0);
-
- err = errno;
-
- /* ECANCELED on a scrub means we resumed a paused scrub */
- if (err == ECANCELED && func == POOL_SCAN_SCRUB &&
- cmd == POOL_SCRUB_NORMAL)
- return (0);
-
- if (err == ENOENT && func != POOL_SCAN_NONE && cmd == POOL_SCRUB_NORMAL)
- return (0);
-
- if (func == POOL_SCAN_SCRUB) {
- if (cmd == POOL_SCRUB_PAUSE) {
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot pause scrubbing %s"), zc.zc_name);
- } else {
- assert(cmd == POOL_SCRUB_NORMAL);
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot scrub %s"), zc.zc_name);
- }
- } else if (func == POOL_SCAN_NONE) {
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot cancel scrubbing %s"),
- zc.zc_name);
- } else {
- assert(!"unexpected result");
- }
-
- if (err == EBUSY) {
- nvlist_t *nvroot;
- pool_scan_stat_t *ps = NULL;
- uint_t psc;
-
- verify(nvlist_lookup_nvlist(zhp->zpool_config,
- ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
- (void) nvlist_lookup_uint64_array(nvroot,
- ZPOOL_CONFIG_SCAN_STATS, (uint64_t **)&ps, &psc);
- if (ps && ps->pss_func == POOL_SCAN_SCRUB) {
- if (cmd == POOL_SCRUB_PAUSE)
- return (zfs_error(hdl, EZFS_SCRUB_PAUSED, msg));
- else
- return (zfs_error(hdl, EZFS_SCRUBBING, msg));
- } else {
- return (zfs_error(hdl, EZFS_RESILVERING, msg));
- }
- } else if (err == ENOENT) {
- return (zfs_error(hdl, EZFS_NO_SCRUB, msg));
- } else {
- return (zpool_standard_error(hdl, err, msg));
- }
-}
-
-static int
-xlate_init_err(int err)
-{
- switch (err) {
- case ENODEV:
- return (EZFS_NODEVICE);
- case EINVAL:
- case EROFS:
- return (EZFS_BADDEV);
- case EBUSY:
- return (EZFS_INITIALIZING);
- case ESRCH:
- return (EZFS_NO_INITIALIZE);
- }
- return (err);
-}
-
-/*
- * Begin, suspend, or cancel the initialization (initializing of all free
- * blocks) for the given vdevs in the given pool.
- */
-int
-zpool_initialize(zpool_handle_t *zhp, pool_initialize_func_t cmd_type,
- nvlist_t *vds)
-{
- char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- nvlist_t *errlist;
-
- /* translate vdev names to guids */
- nvlist_t *vdev_guids = fnvlist_alloc();
- nvlist_t *guids_to_paths = fnvlist_alloc();
- boolean_t spare, cache;
- nvlist_t *tgt;
- nvpair_t *elem;
-
- for (elem = nvlist_next_nvpair(vds, NULL); elem != NULL;
- elem = nvlist_next_nvpair(vds, elem)) {
- char *vd_path = nvpair_name(elem);
- tgt = zpool_find_vdev(zhp, vd_path, &spare, &cache, NULL);
-
- if ((tgt == NULL) || cache || spare) {
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot initialize '%s'"),
- vd_path);
- int err = (tgt == NULL) ? EZFS_NODEVICE :
- (spare ? EZFS_ISSPARE : EZFS_ISL2CACHE);
- fnvlist_free(vdev_guids);
- fnvlist_free(guids_to_paths);
- return (zfs_error(hdl, err, msg));
- }
-
- uint64_t guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID);
- fnvlist_add_uint64(vdev_guids, vd_path, guid);
-
- (void) snprintf(msg, sizeof (msg), "%llu", guid);
- fnvlist_add_string(guids_to_paths, msg, vd_path);
- }
-
- int err = lzc_initialize(zhp->zpool_name, cmd_type, vdev_guids,
- &errlist);
- fnvlist_free(vdev_guids);
-
- if (err == 0) {
- fnvlist_free(guids_to_paths);
- return (0);
- }
-
- nvlist_t *vd_errlist = NULL;
- if (errlist != NULL) {
- vd_errlist = fnvlist_lookup_nvlist(errlist,
- ZPOOL_INITIALIZE_VDEVS);
- }
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "operation failed"));
-
- for (elem = nvlist_next_nvpair(vd_errlist, NULL); elem != NULL;
- elem = nvlist_next_nvpair(vd_errlist, elem)) {
- int64_t vd_error = xlate_init_err(fnvpair_value_int64(elem));
- char *path = fnvlist_lookup_string(guids_to_paths,
- nvpair_name(elem));
- (void) zfs_error_fmt(hdl, vd_error, "cannot initialize '%s'",
- path);
- }
-
- fnvlist_free(guids_to_paths);
- if (vd_errlist != NULL)
- return (-1);
-
- return (zpool_standard_error(hdl, err, msg));
-}
-
-#ifdef illumos
-/*
- * This provides a very minimal check whether a given string is likely a
- * c#t#d# style string. Users of this are expected to do their own
- * verification of the s# part.
- */
-#define CTD_CHECK(str) (str && str[0] == 'c' && isdigit(str[1]))
-
-/*
- * More elaborate version for ones which may start with "/dev/dsk/"
- * and the like.
- */
-static int
-ctd_check_path(char *str)
-{
- /*
- * If it starts with a slash, check the last component.
- */
- if (str && str[0] == '/') {
- char *tmp = strrchr(str, '/');
-
- /*
- * If it ends in "/old", check the second-to-last
- * component of the string instead.
- */
- if (tmp != str && strcmp(tmp, "/old") == 0) {
- for (tmp--; *tmp != '/'; tmp--)
- ;
- }
- str = tmp + 1;
- }
- return (CTD_CHECK(str));
-}
-#endif
-
-/*
- * Find a vdev that matches the search criteria specified. We use the
- * the nvpair name to determine how we should look for the device.
- * 'avail_spare' is set to TRUE if the provided guid refers to an AVAIL
- * spare; but FALSE if its an INUSE spare.
- */
-static nvlist_t *
-vdev_to_nvlist_iter(nvlist_t *nv, nvlist_t *search, boolean_t *avail_spare,
- boolean_t *l2cache, boolean_t *log)
-{
- uint_t c, children;
- nvlist_t **child;
- nvlist_t *ret;
- uint64_t is_log;
- char *srchkey;
- nvpair_t *pair = nvlist_next_nvpair(search, NULL);
-
- /* Nothing to look for */
- if (search == NULL || pair == NULL)
- return (NULL);
-
- /* Obtain the key we will use to search */
- srchkey = nvpair_name(pair);
-
- switch (nvpair_type(pair)) {
- case DATA_TYPE_UINT64:
- if (strcmp(srchkey, ZPOOL_CONFIG_GUID) == 0) {
- uint64_t srchval, theguid;
-
- verify(nvpair_value_uint64(pair, &srchval) == 0);
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID,
- &theguid) == 0);
- if (theguid == srchval)
- return (nv);
- }
- break;
-
- case DATA_TYPE_STRING: {
- char *srchval, *val;
-
- verify(nvpair_value_string(pair, &srchval) == 0);
- if (nvlist_lookup_string(nv, srchkey, &val) != 0)
- break;
-
- /*
- * Search for the requested value. Special cases:
- *
- * - ZPOOL_CONFIG_PATH for whole disk entries. To support
- * UEFI boot, these end in "s0" or "s0/old" or "s1" or
- * "s1/old". The "s0" or "s1" part is hidden from the user,
- * but included in the string, so this matches around it.
- * - looking for a top-level vdev name (i.e. ZPOOL_CONFIG_TYPE).
- *
- * Otherwise, all other searches are simple string compares.
- */
-#ifdef illumos
- if (strcmp(srchkey, ZPOOL_CONFIG_PATH) == 0 &&
- ctd_check_path(val)) {
- uint64_t wholedisk = 0;
-
- (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK,
- &wholedisk);
- if (wholedisk) {
- int slen = strlen(srchval);
- int vlen = strlen(val);
-
- if (slen != vlen - 2)
- break;
-
- /*
- * make_leaf_vdev() should only set
- * wholedisk for ZPOOL_CONFIG_PATHs which
- * will include "/dev/dsk/", giving plenty of
- * room for the indices used next.
- */
- ASSERT(vlen >= 6);
-
- /*
- * strings identical except trailing "s0"
- */
- if ((strcmp(&val[vlen - 2], "s0") == 0 ||
- strcmp(&val[vlen - 2], "s1") == 0) &&
- strncmp(srchval, val, slen) == 0)
- return (nv);
-
- /*
- * strings identical except trailing "s0/old"
- */
- if ((strcmp(&val[vlen - 6], "s0/old") == 0 ||
- strcmp(&val[vlen - 6], "s1/old") == 0) &&
- strcmp(&srchval[slen - 4], "/old") == 0 &&
- strncmp(srchval, val, slen - 4) == 0)
- return (nv);
-
- break;
- }
- } else if (strcmp(srchkey, ZPOOL_CONFIG_TYPE) == 0 && val) {
-#else
- if (strcmp(srchkey, ZPOOL_CONFIG_TYPE) == 0 && val) {
-#endif
- char *type, *idx, *end, *p;
- uint64_t id, vdev_id;
-
- /*
- * Determine our vdev type, keeping in mind
- * that the srchval is composed of a type and
- * vdev id pair (i.e. mirror-4).
- */
- if ((type = strdup(srchval)) == NULL)
- return (NULL);
-
- if ((p = strrchr(type, '-')) == NULL) {
- free(type);
- break;
- }
- idx = p + 1;
- *p = '\0';
-
- /*
- * If the types don't match then keep looking.
- */
- if (strncmp(val, type, strlen(val)) != 0) {
- free(type);
- break;
- }
-
- verify(zpool_vdev_is_interior(type));
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ID,
- &id) == 0);
-
- errno = 0;
- vdev_id = strtoull(idx, &end, 10);
-
- free(type);
- if (errno != 0)
- return (NULL);
-
- /*
- * Now verify that we have the correct vdev id.
- */
- if (vdev_id == id)
- return (nv);
- }
-
- /*
- * Common case
- */
- if (strcmp(srchval, val) == 0)
- return (nv);
- break;
- }
-
- default:
- break;
- }
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0)
- return (NULL);
-
- for (c = 0; c < children; c++) {
- if ((ret = vdev_to_nvlist_iter(child[c], search,
- avail_spare, l2cache, NULL)) != NULL) {
- /*
- * The 'is_log' value is only set for the toplevel
- * vdev, not the leaf vdevs. So we always lookup the
- * log device from the root of the vdev tree (where
- * 'log' is non-NULL).
- */
- if (log != NULL &&
- nvlist_lookup_uint64(child[c],
- ZPOOL_CONFIG_IS_LOG, &is_log) == 0 &&
- is_log) {
- *log = B_TRUE;
- }
- return (ret);
- }
- }
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
- &child, &children) == 0) {
- for (c = 0; c < children; c++) {
- if ((ret = vdev_to_nvlist_iter(child[c], search,
- avail_spare, l2cache, NULL)) != NULL) {
- *avail_spare = B_TRUE;
- return (ret);
- }
- }
- }
-
- if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
- &child, &children) == 0) {
- for (c = 0; c < children; c++) {
- if ((ret = vdev_to_nvlist_iter(child[c], search,
- avail_spare, l2cache, NULL)) != NULL) {
- *l2cache = B_TRUE;
- return (ret);
- }
- }
- }
-
- return (NULL);
-}
-
-/*
- * Given a physical path (minus the "/devices" prefix), find the
- * associated vdev.
- */
-nvlist_t *
-zpool_find_vdev_by_physpath(zpool_handle_t *zhp, const char *ppath,
- boolean_t *avail_spare, boolean_t *l2cache, boolean_t *log)
-{
- nvlist_t *search, *nvroot, *ret;
-
- verify(nvlist_alloc(&search, NV_UNIQUE_NAME, KM_SLEEP) == 0);
- verify(nvlist_add_string(search, ZPOOL_CONFIG_PHYS_PATH, ppath) == 0);
-
- verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
-
- *avail_spare = B_FALSE;
- *l2cache = B_FALSE;
- if (log != NULL)
- *log = B_FALSE;
- ret = vdev_to_nvlist_iter(nvroot, search, avail_spare, l2cache, log);
- nvlist_free(search);
-
- return (ret);
-}
-
-/*
- * Determine if we have an "interior" top-level vdev (i.e mirror/raidz).
- */
-static boolean_t
-zpool_vdev_is_interior(const char *name)
-{
- if (strncmp(name, VDEV_TYPE_RAIDZ, strlen(VDEV_TYPE_RAIDZ)) == 0 ||
- strncmp(name, VDEV_TYPE_SPARE, strlen(VDEV_TYPE_SPARE)) == 0 ||
- strncmp(name,
- VDEV_TYPE_REPLACING, strlen(VDEV_TYPE_REPLACING)) == 0 ||
- strncmp(name, VDEV_TYPE_MIRROR, strlen(VDEV_TYPE_MIRROR)) == 0)
- return (B_TRUE);
- return (B_FALSE);
-}
-
-nvlist_t *
-zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare,
- boolean_t *l2cache, boolean_t *log)
-{
- char buf[MAXPATHLEN];
- char *end;
- nvlist_t *nvroot, *search, *ret;
- uint64_t guid;
-
- verify(nvlist_alloc(&search, NV_UNIQUE_NAME, KM_SLEEP) == 0);
-
- guid = strtoull(path, &end, 10);
- if (guid != 0 && *end == '\0') {
- verify(nvlist_add_uint64(search, ZPOOL_CONFIG_GUID, guid) == 0);
- } else if (zpool_vdev_is_interior(path)) {
- verify(nvlist_add_string(search, ZPOOL_CONFIG_TYPE, path) == 0);
- } else if (path[0] != '/') {
- (void) snprintf(buf, sizeof (buf), "%s%s", _PATH_DEV, path);
- verify(nvlist_add_string(search, ZPOOL_CONFIG_PATH, buf) == 0);
- } else {
- verify(nvlist_add_string(search, ZPOOL_CONFIG_PATH, path) == 0);
- }
-
- verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
-
- *avail_spare = B_FALSE;
- *l2cache = B_FALSE;
- if (log != NULL)
- *log = B_FALSE;
- ret = vdev_to_nvlist_iter(nvroot, search, avail_spare, l2cache, log);
- nvlist_free(search);
-
- return (ret);
-}
-
-static int
-vdev_is_online(nvlist_t *nv)
-{
- uint64_t ival;
-
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_OFFLINE, &ival) == 0 ||
- nvlist_lookup_uint64(nv, ZPOOL_CONFIG_FAULTED, &ival) == 0 ||
- nvlist_lookup_uint64(nv, ZPOOL_CONFIG_REMOVED, &ival) == 0)
- return (0);
-
- return (1);
-}
-
-/*
- * Helper function for zpool_get_physpaths().
- */
-static int
-vdev_get_one_physpath(nvlist_t *config, char *physpath, size_t physpath_size,
- size_t *bytes_written)
-{
- size_t bytes_left, pos, rsz;
- char *tmppath;
- const char *format;
-
- if (nvlist_lookup_string(config, ZPOOL_CONFIG_PHYS_PATH,
- &tmppath) != 0)
- return (EZFS_NODEVICE);
-
- pos = *bytes_written;
- bytes_left = physpath_size - pos;
- format = (pos == 0) ? "%s" : " %s";
-
- rsz = snprintf(physpath + pos, bytes_left, format, tmppath);
- *bytes_written += rsz;
-
- if (rsz >= bytes_left) {
- /* if physpath was not copied properly, clear it */
- if (bytes_left != 0) {
- physpath[pos] = 0;
- }
- return (EZFS_NOSPC);
- }
- return (0);
-}
-
-static int
-vdev_get_physpaths(nvlist_t *nv, char *physpath, size_t phypath_size,
- size_t *rsz, boolean_t is_spare)
-{
- char *type;
- int ret;
-
- if (nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) != 0)
- return (EZFS_INVALCONFIG);
-
- if (strcmp(type, VDEV_TYPE_DISK) == 0) {
- /*
- * An active spare device has ZPOOL_CONFIG_IS_SPARE set.
- * For a spare vdev, we only want to boot from the active
- * spare device.
- */
- if (is_spare) {
- uint64_t spare = 0;
- (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_SPARE,
- &spare);
- if (!spare)
- return (EZFS_INVALCONFIG);
- }
-
- if (vdev_is_online(nv)) {
- if ((ret = vdev_get_one_physpath(nv, physpath,
- phypath_size, rsz)) != 0)
- return (ret);
- }
- } else if (strcmp(type, VDEV_TYPE_MIRROR) == 0 ||
- strcmp(type, VDEV_TYPE_RAIDZ) == 0 ||
- strcmp(type, VDEV_TYPE_REPLACING) == 0 ||
- (is_spare = (strcmp(type, VDEV_TYPE_SPARE) == 0))) {
- nvlist_t **child;
- uint_t count;
- int i, ret;
-
- if (nvlist_lookup_nvlist_array(nv,
- ZPOOL_CONFIG_CHILDREN, &child, &count) != 0)
- return (EZFS_INVALCONFIG);
-
- for (i = 0; i < count; i++) {
- ret = vdev_get_physpaths(child[i], physpath,
- phypath_size, rsz, is_spare);
- if (ret == EZFS_NOSPC)
- return (ret);
- }
- }
-
- return (EZFS_POOL_INVALARG);
-}
-
-/*
- * Get phys_path for a root pool config.
- * Return 0 on success; non-zero on failure.
- */
-static int
-zpool_get_config_physpath(nvlist_t *config, char *physpath, size_t phypath_size)
-{
- size_t rsz;
- nvlist_t *vdev_root;
- nvlist_t **child;
- uint_t count;
- char *type;
-
- rsz = 0;
-
- if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &vdev_root) != 0)
- return (EZFS_INVALCONFIG);
-
- if (nvlist_lookup_string(vdev_root, ZPOOL_CONFIG_TYPE, &type) != 0 ||
- nvlist_lookup_nvlist_array(vdev_root, ZPOOL_CONFIG_CHILDREN,
- &child, &count) != 0)
- return (EZFS_INVALCONFIG);
-
- /*
- * root pool can only have a single top-level vdev.
- */
- if (strcmp(type, VDEV_TYPE_ROOT) != 0 || count != 1)
- return (EZFS_POOL_INVALARG);
-
- (void) vdev_get_physpaths(child[0], physpath, phypath_size, &rsz,
- B_FALSE);
-
- /* No online devices */
- if (rsz == 0)
- return (EZFS_NODEVICE);
-
- return (0);
-}
-
-/*
- * Get phys_path for a root pool
- * Return 0 on success; non-zero on failure.
- */
-int
-zpool_get_physpath(zpool_handle_t *zhp, char *physpath, size_t phypath_size)
-{
- return (zpool_get_config_physpath(zhp->zpool_config, physpath,
- phypath_size));
-}
-
-/*
- * If the device has being dynamically expanded then we need to relabel
- * the disk to use the new unallocated space.
- */
-static int
-zpool_relabel_disk(libzfs_handle_t *hdl, const char *name)
-{
-#ifdef illumos
- char path[MAXPATHLEN];
- char errbuf[1024];
- int fd, error;
- int (*_efi_use_whole_disk)(int);
-
- if ((_efi_use_whole_disk = (int (*)(int))dlsym(RTLD_DEFAULT,
- "efi_use_whole_disk")) == NULL)
- return (-1);
-
- (void) snprintf(path, sizeof (path), "%s/%s", ZFS_RDISK_ROOT, name);
-
- if ((fd = open(path, O_RDWR | O_NDELAY)) < 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
- "relabel '%s': unable to open device"), name);
- return (zfs_error(hdl, EZFS_OPENFAILED, errbuf));
- }
-
- /*
- * It's possible that we might encounter an error if the device
- * does not have any unallocated space left. If so, we simply
- * ignore that error and continue on.
- */
- error = _efi_use_whole_disk(fd);
- (void) close(fd);
- if (error && error != VT_ENOSPC) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "cannot "
- "relabel '%s': unable to read disk capacity"), name);
- return (zfs_error(hdl, EZFS_NOCAP, errbuf));
- }
-#endif /* illumos */
- return (0);
-}
-
-/*
- * Bring the specified vdev online. The 'flags' parameter is a set of the
- * ZFS_ONLINE_* flags.
- */
-int
-zpool_vdev_online(zpool_handle_t *zhp, const char *path, int flags,
- vdev_state_t *newstate)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- char *pathname;
- nvlist_t *tgt;
- boolean_t avail_spare, l2cache, islog;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- if (flags & ZFS_ONLINE_EXPAND) {
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot expand %s"), path);
- } else {
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot online %s"), path);
- }
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache,
- &islog)) == NULL)
- return (zfs_error(hdl, EZFS_NODEVICE, msg));
-
- verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
-
- if (avail_spare)
- return (zfs_error(hdl, EZFS_ISSPARE, msg));
-
- if ((flags & ZFS_ONLINE_EXPAND ||
- zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOEXPAND, NULL)) &&
- nvlist_lookup_string(tgt, ZPOOL_CONFIG_PATH, &pathname) == 0) {
- uint64_t wholedisk = 0;
-
- (void) nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_WHOLE_DISK,
- &wholedisk);
-
- /*
- * XXX - L2ARC 1.0 devices can't support expansion.
- */
- if (l2cache) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "cannot expand cache devices"));
- return (zfs_error(hdl, EZFS_VDEVNOTSUP, msg));
- }
-
- if (wholedisk) {
- pathname += strlen(ZFS_DISK_ROOT) + 1;
- (void) zpool_relabel_disk(hdl, pathname);
- }
- }
-
- zc.zc_cookie = VDEV_STATE_ONLINE;
- zc.zc_obj = flags;
-
- if (zfs_ioctl(hdl, ZFS_IOC_VDEV_SET_STATE, &zc) != 0) {
- if (errno == EINVAL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "was split "
- "from this pool into a new one. Use '%s' "
- "instead"), "zpool detach");
- return (zfs_error(hdl, EZFS_POSTSPLIT_ONLINE, msg));
- }
- return (zpool_standard_error(hdl, errno, msg));
- }
-
- *newstate = zc.zc_cookie;
- return (0);
-}
-
-/*
- * Take the specified vdev offline
- */
-int
-zpool_vdev_offline(zpool_handle_t *zhp, const char *path, boolean_t istmp)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- nvlist_t *tgt;
- boolean_t avail_spare, l2cache;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot offline %s"), path);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache,
- NULL)) == NULL)
- return (zfs_error(hdl, EZFS_NODEVICE, msg));
-
- verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
-
- if (avail_spare)
- return (zfs_error(hdl, EZFS_ISSPARE, msg));
-
- zc.zc_cookie = VDEV_STATE_OFFLINE;
- zc.zc_obj = istmp ? ZFS_OFFLINE_TEMPORARY : 0;
-
- if (zfs_ioctl(hdl, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
- return (0);
-
- switch (errno) {
- case EBUSY:
-
- /*
- * There are no other replicas of this device.
- */
- return (zfs_error(hdl, EZFS_NOREPLICAS, msg));
-
- case EEXIST:
- /*
- * The log device has unplayed logs
- */
- return (zfs_error(hdl, EZFS_UNPLAYED_LOGS, msg));
-
- default:
- return (zpool_standard_error(hdl, errno, msg));
- }
-}
-
-/*
- * Mark the given vdev faulted.
- */
-int
-zpool_vdev_fault(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot fault %llu"), guid);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_guid = guid;
- zc.zc_cookie = VDEV_STATE_FAULTED;
- zc.zc_obj = aux;
-
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
- return (0);
-
- switch (errno) {
- case EBUSY:
-
- /*
- * There are no other replicas of this device.
- */
- return (zfs_error(hdl, EZFS_NOREPLICAS, msg));
-
- default:
- return (zpool_standard_error(hdl, errno, msg));
- }
-
-}
-
-/*
- * Mark the given vdev degraded.
- */
-int
-zpool_vdev_degrade(zpool_handle_t *zhp, uint64_t guid, vdev_aux_t aux)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot degrade %llu"), guid);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_guid = guid;
- zc.zc_cookie = VDEV_STATE_DEGRADED;
- zc.zc_obj = aux;
-
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_VDEV_SET_STATE, &zc) == 0)
- return (0);
-
- return (zpool_standard_error(hdl, errno, msg));
-}
-
-/*
- * Returns TRUE if the given nvlist is a vdev that was originally swapped in as
- * a hot spare.
- */
-static boolean_t
-is_replacing_spare(nvlist_t *search, nvlist_t *tgt, int which)
-{
- nvlist_t **child;
- uint_t c, children;
- char *type;
-
- if (nvlist_lookup_nvlist_array(search, ZPOOL_CONFIG_CHILDREN, &child,
- &children) == 0) {
- verify(nvlist_lookup_string(search, ZPOOL_CONFIG_TYPE,
- &type) == 0);
-
- if (strcmp(type, VDEV_TYPE_SPARE) == 0 &&
- children == 2 && child[which] == tgt)
- return (B_TRUE);
-
- for (c = 0; c < children; c++)
- if (is_replacing_spare(child[c], tgt, which))
- return (B_TRUE);
- }
-
- return (B_FALSE);
-}
-
-/*
- * Attach new_disk (fully described by nvroot) to old_disk.
- * If 'replacing' is specified, the new disk will replace the old one.
- */
-int
-zpool_vdev_attach(zpool_handle_t *zhp,
- const char *old_disk, const char *new_disk, nvlist_t *nvroot, int replacing)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- int ret;
- nvlist_t *tgt;
- boolean_t avail_spare, l2cache, islog;
- uint64_t val;
- char *newname;
- nvlist_t **child;
- uint_t children;
- nvlist_t *config_root;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- boolean_t rootpool = zpool_is_bootable(zhp);
-
- if (replacing)
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot replace %s with %s"), old_disk, new_disk);
- else
- (void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
- "cannot attach %s to %s"), new_disk, old_disk);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, old_disk, &avail_spare, &l2cache,
- &islog)) == NULL)
- return (zfs_error(hdl, EZFS_NODEVICE, msg));
-
- if (avail_spare)
- return (zfs_error(hdl, EZFS_ISSPARE, msg));
-
- if (l2cache)
- return (zfs_error(hdl, EZFS_ISL2CACHE, msg));
-
- verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
- zc.zc_cookie = replacing;
-
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
- &child, &children) != 0 || children != 1) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "new device must be a single disk"));
- return (zfs_error(hdl, EZFS_INVALCONFIG, msg));
- }
-
- verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
- ZPOOL_CONFIG_VDEV_TREE, &config_root) == 0);
-
- if ((newname = zpool_vdev_name(NULL, NULL, child[0], 0)) == NULL)
- return (-1);
-
- /*
- * If the target is a hot spare that has been swapped in, we can only
- * replace it with another hot spare.
- */
- if (replacing &&
- nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_IS_SPARE, &val) == 0 &&
- (zpool_find_vdev(zhp, newname, &avail_spare, &l2cache,
- NULL) == NULL || !avail_spare) &&
- is_replacing_spare(config_root, tgt, 1)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "can only be replaced by another hot spare"));
- free(newname);
- return (zfs_error(hdl, EZFS_BADTARGET, msg));
- }
-
- free(newname);
-
- if (zcmd_write_conf_nvlist(hdl, &zc, nvroot) != 0)
- return (-1);
-
- ret = zfs_ioctl(hdl, ZFS_IOC_VDEV_ATTACH, &zc);
-
- zcmd_free_nvlists(&zc);
-
- if (ret == 0) {
- if (rootpool) {
- /*
- * XXX need a better way to prevent user from
- * booting up a half-baked vdev.
- */
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Make "
- "sure to wait until resilver is done "
- "before rebooting.\n"));
- (void) fprintf(stderr, "\n");
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "If "
- "you boot from pool '%s', you may need to update\n"
- "boot code on newly attached disk '%s'.\n\n"
- "Assuming you use GPT partitioning and 'da0' is "
- "your new boot disk\n"
- "you may use the following command:\n\n"
- "\tgpart bootcode -b /boot/pmbr -p "
- "/boot/gptzfsboot -i 1 da0\n\n"),
- zhp->zpool_name, new_disk);
- }
- return (0);
- }
-
- switch (errno) {
- case ENOTSUP:
- /*
- * Can't attach to or replace this type of vdev.
- */
- if (replacing) {
- uint64_t version = zpool_get_prop_int(zhp,
- ZPOOL_PROP_VERSION, NULL);
-
- if (islog)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "cannot replace a log with a spare"));
- else if (version >= SPA_VERSION_MULTI_REPLACE)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "already in replacing/spare config; wait "
- "for completion or use 'zpool detach'"));
- else
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "cannot replace a replacing device"));
- } else {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "can only attach to mirrors and top-level "
- "disks"));
- }
- (void) zfs_error(hdl, EZFS_BADTARGET, msg);
- break;
-
- case EINVAL:
- /*
- * The new device must be a single disk.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "new device must be a single disk"));
- (void) zfs_error(hdl, EZFS_INVALCONFIG, msg);
- break;
-
- case EBUSY:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "%s is busy, "
- "or device removal is in progress"),
- new_disk);
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- break;
-
- case EOVERFLOW:
- /*
- * The new device is too small.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "device is too small"));
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- break;
-
- case EDOM:
- /*
- * The new device has a different alignment requirement.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "devices have different sector alignment"));
- (void) zfs_error(hdl, EZFS_BADDEV, msg);
- break;
-
- case ENAMETOOLONG:
- /*
- * The resulting top-level vdev spec won't fit in the label.
- */
- (void) zfs_error(hdl, EZFS_DEVOVERFLOW, msg);
- break;
-
- default:
- (void) zpool_standard_error(hdl, errno, msg);
- }
-
- return (-1);
-}
-
-/*
- * Detach the specified device.
- */
-int
-zpool_vdev_detach(zpool_handle_t *zhp, const char *path)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- nvlist_t *tgt;
- boolean_t avail_spare, l2cache;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot detach %s"), path);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache,
- NULL)) == NULL)
- return (zfs_error(hdl, EZFS_NODEVICE, msg));
-
- if (avail_spare)
- return (zfs_error(hdl, EZFS_ISSPARE, msg));
-
- if (l2cache)
- return (zfs_error(hdl, EZFS_ISL2CACHE, msg));
-
- verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
-
- if (zfs_ioctl(hdl, ZFS_IOC_VDEV_DETACH, &zc) == 0)
- return (0);
-
- switch (errno) {
-
- case ENOTSUP:
- /*
- * Can't detach from this type of vdev.
- */
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "only "
- "applicable to mirror and replacing vdevs"));
- (void) zfs_error(hdl, EZFS_BADTARGET, msg);
- break;
-
- case EBUSY:
- /*
- * There are no other replicas of this device.
- */
- (void) zfs_error(hdl, EZFS_NOREPLICAS, msg);
- break;
-
- default:
- (void) zpool_standard_error(hdl, errno, msg);
- }
-
- return (-1);
-}
-
-/*
- * Find a mirror vdev in the source nvlist.
- *
- * The mchild array contains a list of disks in one of the top-level mirrors
- * of the source pool. The schild array contains a list of disks that the
- * user specified on the command line. We loop over the mchild array to
- * see if any entry in the schild array matches.
- *
- * If a disk in the mchild array is found in the schild array, we return
- * the index of that entry. Otherwise we return -1.
- */
-static int
-find_vdev_entry(zpool_handle_t *zhp, nvlist_t **mchild, uint_t mchildren,
- nvlist_t **schild, uint_t schildren)
-{
- uint_t mc;
-
- for (mc = 0; mc < mchildren; mc++) {
- uint_t sc;
- char *mpath = zpool_vdev_name(zhp->zpool_hdl, zhp,
- mchild[mc], 0);
-
- for (sc = 0; sc < schildren; sc++) {
- char *spath = zpool_vdev_name(zhp->zpool_hdl, zhp,
- schild[sc], 0);
- boolean_t result = (strcmp(mpath, spath) == 0);
-
- free(spath);
- if (result) {
- free(mpath);
- return (mc);
- }
- }
-
- free(mpath);
- }
-
- return (-1);
-}
-
-/*
- * Split a mirror pool. If newroot points to null, then a new nvlist
- * is generated and it is the responsibility of the caller to free it.
- */
-int
-zpool_vdev_split(zpool_handle_t *zhp, char *newname, nvlist_t **newroot,
- nvlist_t *props, splitflags_t flags)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- nvlist_t *tree, *config, **child, **newchild, *newconfig = NULL;
- nvlist_t **varray = NULL, *zc_props = NULL;
- uint_t c, children, newchildren, lastlog = 0, vcount, found = 0;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- uint64_t vers;
- boolean_t freelist = B_FALSE, memory_err = B_TRUE;
- int retval = 0;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "Unable to split %s"), zhp->zpool_name);
-
- if (!zpool_name_valid(hdl, B_FALSE, newname))
- return (zfs_error(hdl, EZFS_INVALIDNAME, msg));
-
- if ((config = zpool_get_config(zhp, NULL)) == NULL) {
- (void) fprintf(stderr, gettext("Internal error: unable to "
- "retrieve pool configuration\n"));
- return (-1);
- }
-
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &tree)
- == 0);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, &vers) == 0);
-
- if (props) {
- prop_flags_t flags = { .create = B_FALSE, .import = B_TRUE };
- if ((zc_props = zpool_valid_proplist(hdl, zhp->zpool_name,
- props, vers, flags, msg)) == NULL)
- return (-1);
- }
-
- if (nvlist_lookup_nvlist_array(tree, ZPOOL_CONFIG_CHILDREN, &child,
- &children) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "Source pool is missing vdev tree"));
- nvlist_free(zc_props);
- return (-1);
- }
-
- varray = zfs_alloc(hdl, children * sizeof (nvlist_t *));
- vcount = 0;
-
- if (*newroot == NULL ||
- nvlist_lookup_nvlist_array(*newroot, ZPOOL_CONFIG_CHILDREN,
- &newchild, &newchildren) != 0)
- newchildren = 0;
-
- for (c = 0; c < children; c++) {
- uint64_t is_log = B_FALSE, is_hole = B_FALSE;
- char *type;
- nvlist_t **mchild, *vdev;
- uint_t mchildren;
- int entry;
-
- /*
- * Unlike cache & spares, slogs are stored in the
- * ZPOOL_CONFIG_CHILDREN array. We filter them out here.
- */
- (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG,
- &is_log);
- (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_HOLE,
- &is_hole);
- if (is_log || is_hole) {
- /*
- * Create a hole vdev and put it in the config.
- */
- if (nvlist_alloc(&vdev, NV_UNIQUE_NAME, 0) != 0)
- goto out;
- if (nvlist_add_string(vdev, ZPOOL_CONFIG_TYPE,
- VDEV_TYPE_HOLE) != 0)
- goto out;
- if (nvlist_add_uint64(vdev, ZPOOL_CONFIG_IS_HOLE,
- 1) != 0)
- goto out;
- if (lastlog == 0)
- lastlog = vcount;
- varray[vcount++] = vdev;
- continue;
- }
- lastlog = 0;
- verify(nvlist_lookup_string(child[c], ZPOOL_CONFIG_TYPE, &type)
- == 0);
- if (strcmp(type, VDEV_TYPE_MIRROR) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "Source pool must be composed only of mirrors\n"));
- retval = zfs_error(hdl, EZFS_INVALCONFIG, msg);
- goto out;
- }
-
- verify(nvlist_lookup_nvlist_array(child[c],
- ZPOOL_CONFIG_CHILDREN, &mchild, &mchildren) == 0);
-
- /* find or add an entry for this top-level vdev */
- if (newchildren > 0 &&
- (entry = find_vdev_entry(zhp, mchild, mchildren,
- newchild, newchildren)) >= 0) {
- /* We found a disk that the user specified. */
- vdev = mchild[entry];
- ++found;
- } else {
- /* User didn't specify a disk for this vdev. */
- vdev = mchild[mchildren - 1];
- }
-
- if (nvlist_dup(vdev, &varray[vcount++], 0) != 0)
- goto out;
- }
-
- /* did we find every disk the user specified? */
- if (found != newchildren) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "Device list must "
- "include at most one disk from each mirror"));
- retval = zfs_error(hdl, EZFS_INVALCONFIG, msg);
- goto out;
- }
-
- /* Prepare the nvlist for populating. */
- if (*newroot == NULL) {
- if (nvlist_alloc(newroot, NV_UNIQUE_NAME, 0) != 0)
- goto out;
- freelist = B_TRUE;
- if (nvlist_add_string(*newroot, ZPOOL_CONFIG_TYPE,
- VDEV_TYPE_ROOT) != 0)
- goto out;
- } else {
- verify(nvlist_remove_all(*newroot, ZPOOL_CONFIG_CHILDREN) == 0);
- }
-
- /* Add all the children we found */
- if (nvlist_add_nvlist_array(*newroot, ZPOOL_CONFIG_CHILDREN, varray,
- lastlog == 0 ? vcount : lastlog) != 0)
- goto out;
-
- /*
- * If we're just doing a dry run, exit now with success.
- */
- if (flags.dryrun) {
- memory_err = B_FALSE;
- freelist = B_FALSE;
- goto out;
- }
-
- /* now build up the config list & call the ioctl */
- if (nvlist_alloc(&newconfig, NV_UNIQUE_NAME, 0) != 0)
- goto out;
-
- if (nvlist_add_nvlist(newconfig,
- ZPOOL_CONFIG_VDEV_TREE, *newroot) != 0 ||
- nvlist_add_string(newconfig,
- ZPOOL_CONFIG_POOL_NAME, newname) != 0 ||
- nvlist_add_uint64(newconfig, ZPOOL_CONFIG_VERSION, vers) != 0)
- goto out;
-
- /*
- * The new pool is automatically part of the namespace unless we
- * explicitly export it.
- */
- if (!flags.import)
- zc.zc_cookie = ZPOOL_EXPORT_AFTER_SPLIT;
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_string, newname, sizeof (zc.zc_string));
- if (zcmd_write_conf_nvlist(hdl, &zc, newconfig) != 0)
- goto out;
- if (zc_props != NULL && zcmd_write_src_nvlist(hdl, &zc, zc_props) != 0)
- goto out;
-
- if (zfs_ioctl(hdl, ZFS_IOC_VDEV_SPLIT, &zc) != 0) {
- retval = zpool_standard_error(hdl, errno, msg);
- goto out;
- }
-
- freelist = B_FALSE;
- memory_err = B_FALSE;
-
-out:
- if (varray != NULL) {
- int v;
-
- for (v = 0; v < vcount; v++)
- nvlist_free(varray[v]);
- free(varray);
- }
- zcmd_free_nvlists(&zc);
- nvlist_free(zc_props);
- nvlist_free(newconfig);
- if (freelist) {
- nvlist_free(*newroot);
- *newroot = NULL;
- }
-
- if (retval != 0)
- return (retval);
-
- if (memory_err)
- return (no_memory(hdl));
-
- return (0);
-}
-
-/*
- * Remove the given device.
- */
-int
-zpool_vdev_remove(zpool_handle_t *zhp, const char *path)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- nvlist_t *tgt;
- boolean_t avail_spare, l2cache, islog;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- uint64_t version;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot remove %s"), path);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache,
- &islog)) == NULL)
- return (zfs_error(hdl, EZFS_NODEVICE, msg));
-
- version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL);
- if (islog && version < SPA_VERSION_HOLES) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded to support log removal"));
- return (zfs_error(hdl, EZFS_BADVERSION, msg));
- }
-
- zc.zc_guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID);
-
- if (zfs_ioctl(hdl, ZFS_IOC_VDEV_REMOVE, &zc) == 0)
- return (0);
-
- switch (errno) {
-
- case EINVAL:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid config; all top-level vdevs must "
- "have the same sector size and not be raidz."));
- (void) zfs_error(hdl, EZFS_INVALCONFIG, msg);
- break;
-
- case EBUSY:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "Pool busy; removal may already be in progress"));
- (void) zfs_error(hdl, EZFS_BUSY, msg);
- break;
-
- default:
- (void) zpool_standard_error(hdl, errno, msg);
- }
- return (-1);
-}
-
-int
-zpool_vdev_remove_cancel(zpool_handle_t *zhp)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot cancel removal"));
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_cookie = 1;
-
- if (zfs_ioctl(hdl, ZFS_IOC_VDEV_REMOVE, &zc) == 0)
- return (0);
-
- return (zpool_standard_error(hdl, errno, msg));
-}
-
-int
-zpool_vdev_indirect_size(zpool_handle_t *zhp, const char *path,
- uint64_t *sizep)
-{
- char msg[1024];
- nvlist_t *tgt;
- boolean_t avail_spare, l2cache, islog;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot determine indirect size of %s"),
- path);
-
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache,
- &islog)) == NULL)
- return (zfs_error(hdl, EZFS_NODEVICE, msg));
-
- if (avail_spare || l2cache || islog) {
- *sizep = 0;
- return (0);
- }
-
- if (nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_INDIRECT_SIZE, sizep) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "indirect size not available"));
- return (zfs_error(hdl, EINVAL, msg));
- }
- return (0);
-}
-
-/*
- * Clear the errors for the pool, or the particular device if specified.
- */
-int
-zpool_clear(zpool_handle_t *zhp, const char *path, nvlist_t *rewindnvl)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- nvlist_t *tgt;
- zpool_load_policy_t policy;
- boolean_t avail_spare, l2cache;
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- nvlist_t *nvi = NULL;
- int error;
-
- if (path)
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot clear errors for %s"),
- path);
- else
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot clear errors for %s"),
- zhp->zpool_name);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if (path) {
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare,
- &l2cache, NULL)) == NULL)
- return (zfs_error(hdl, EZFS_NODEVICE, msg));
-
- /*
- * Don't allow error clearing for hot spares. Do allow
- * error clearing for l2cache devices.
- */
- if (avail_spare)
- return (zfs_error(hdl, EZFS_ISSPARE, msg));
-
- verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID,
- &zc.zc_guid) == 0);
- }
-
- zpool_get_load_policy(rewindnvl, &policy);
- zc.zc_cookie = policy.zlp_rewind;
-
- if (zcmd_alloc_dst_nvlist(hdl, &zc, zhp->zpool_config_size * 2) != 0)
- return (-1);
-
- if (zcmd_write_src_nvlist(hdl, &zc, rewindnvl) != 0)
- return (-1);
-
- while ((error = zfs_ioctl(hdl, ZFS_IOC_CLEAR, &zc)) != 0 &&
- errno == ENOMEM) {
- if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- }
-
- if (!error || ((policy.zlp_rewind & ZPOOL_TRY_REWIND) &&
- errno != EPERM && errno != EACCES)) {
- if (policy.zlp_rewind &
- (ZPOOL_DO_REWIND | ZPOOL_TRY_REWIND)) {
- (void) zcmd_read_dst_nvlist(hdl, &zc, &nvi);
- zpool_rewind_exclaim(hdl, zc.zc_name,
- ((policy.zlp_rewind & ZPOOL_TRY_REWIND) != 0),
- nvi);
- nvlist_free(nvi);
- }
- zcmd_free_nvlists(&zc);
- return (0);
- }
-
- zcmd_free_nvlists(&zc);
- return (zpool_standard_error(hdl, errno, msg));
-}
-
-/*
- * Similar to zpool_clear(), but takes a GUID (used by fmd).
- */
-int
-zpool_vdev_clear(zpool_handle_t *zhp, uint64_t guid)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot clear errors for %llx"),
- guid);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_guid = guid;
- zc.zc_cookie = ZPOOL_NO_REWIND;
-
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_CLEAR, &zc) == 0)
- return (0);
-
- return (zpool_standard_error(hdl, errno, msg));
-}
-
-/*
- * Change the GUID for a pool.
- */
-int
-zpool_reguid(zpool_handle_t *zhp)
-{
- char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
- zfs_cmd_t zc = { 0 };
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot reguid '%s'"), zhp->zpool_name);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if (zfs_ioctl(hdl, ZFS_IOC_POOL_REGUID, &zc) == 0)
- return (0);
-
- return (zpool_standard_error(hdl, errno, msg));
-}
-
-/*
- * Reopen the pool.
- */
-int
-zpool_reopen(zpool_handle_t *zhp)
-{
- zfs_cmd_t zc = { 0 };
- char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot reopen '%s'"),
- zhp->zpool_name);
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if (zfs_ioctl(hdl, ZFS_IOC_POOL_REOPEN, &zc) == 0)
- return (0);
- return (zpool_standard_error(hdl, errno, msg));
-}
-
-/* call into libzfs_core to execute the sync IOCTL per pool */
-int
-zpool_sync_one(zpool_handle_t *zhp, void *data)
-{
- int ret;
- libzfs_handle_t *hdl = zpool_get_handle(zhp);
- const char *pool_name = zpool_get_name(zhp);
- boolean_t *force = data;
- nvlist_t *innvl = fnvlist_alloc();
-
- fnvlist_add_boolean_value(innvl, "force", *force);
- if ((ret = lzc_sync(pool_name, innvl, NULL)) != 0) {
- nvlist_free(innvl);
- return (zpool_standard_error_fmt(hdl, ret,
- dgettext(TEXT_DOMAIN, "sync '%s' failed"), pool_name));
- }
- nvlist_free(innvl);
-
- return (0);
-}
-
-/*
- * Convert from a devid string to a path.
- */
-static char *
-devid_to_path(char *devid_str)
-{
- ddi_devid_t devid;
- char *minor;
- char *path;
- devid_nmlist_t *list = NULL;
- int ret;
-
- if (devid_str_decode(devid_str, &devid, &minor) != 0)
- return (NULL);
-
- ret = devid_deviceid_to_nmlist("/dev", devid, minor, &list);
-
- devid_str_free(minor);
- devid_free(devid);
-
- if (ret != 0)
- return (NULL);
-
- /*
- * In a case the strdup() fails, we will just return NULL below.
- */
- path = strdup(list[0].devname);
-
- devid_free_nmlist(list);
-
- return (path);
-}
-
-/*
- * Convert from a path to a devid string.
- */
-static char *
-path_to_devid(const char *path)
-{
-#ifdef have_devid
- int fd;
- ddi_devid_t devid;
- char *minor, *ret;
-
- if ((fd = open(path, O_RDONLY)) < 0)
- return (NULL);
-
- minor = NULL;
- ret = NULL;
- if (devid_get(fd, &devid) == 0) {
- if (devid_get_minor_name(fd, &minor) == 0)
- ret = devid_str_encode(devid, minor);
- if (minor != NULL)
- devid_str_free(minor);
- devid_free(devid);
- }
- (void) close(fd);
-
- return (ret);
-#else
- return (NULL);
-#endif
-}
-
-/*
- * Issue the necessary ioctl() to update the stored path value for the vdev. We
- * ignore any failure here, since a common case is for an unprivileged user to
- * type 'zpool status', and we'll display the correct information anyway.
- */
-static void
-set_path(zpool_handle_t *zhp, nvlist_t *nv, const char *path)
-{
- zfs_cmd_t zc = { 0 };
-
- (void) strncpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- (void) strncpy(zc.zc_value, path, sizeof (zc.zc_value));
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID,
- &zc.zc_guid) == 0);
-
- (void) ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_VDEV_SETPATH, &zc);
-}
-
-/*
- * Given a vdev, return the name to display in iostat. If the vdev has a path,
- * we use that, stripping off any leading "/dev/dsk/"; if not, we use the type.
- * We also check if this is a whole disk, in which case we strip off the
- * trailing 's0' slice name.
- *
- * This routine is also responsible for identifying when disks have been
- * reconfigured in a new location. The kernel will have opened the device by
- * devid, but the path will still refer to the old location. To catch this, we
- * first do a path -> devid translation (which is fast for the common case). If
- * the devid matches, we're done. If not, we do a reverse devid -> path
- * translation and issue the appropriate ioctl() to update the path of the vdev.
- * If 'zhp' is NULL, then this is an exported pool, and we don't need to do any
- * of these checks.
- */
-char *
-zpool_vdev_name(libzfs_handle_t *hdl, zpool_handle_t *zhp, nvlist_t *nv,
- int name_flags)
-{
- char *path, *devid, *env;
- uint64_t value;
- char buf[64];
- vdev_stat_t *vs;
- uint_t vsc;
- int have_stats;
- int have_path;
-
- env = getenv("ZPOOL_VDEV_NAME_PATH");
- if (env && (strtoul(env, NULL, 0) > 0 ||
- !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2)))
- name_flags |= VDEV_NAME_PATH;
-
- env = getenv("ZPOOL_VDEV_NAME_GUID");
- if (env && (strtoul(env, NULL, 0) > 0 ||
- !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2)))
- name_flags |= VDEV_NAME_GUID;
-
- env = getenv("ZPOOL_VDEV_NAME_FOLLOW_LINKS");
- if (env && (strtoul(env, NULL, 0) > 0 ||
- !strncasecmp(env, "YES", 3) || !strncasecmp(env, "ON", 2)))
- name_flags |= VDEV_NAME_FOLLOW_LINKS;
-
- have_stats = nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS,
- (uint64_t **)&vs, &vsc) == 0;
- have_path = nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0;
-
- /*
- * If the device is not currently present, assume it will not
- * come back at the same device path. Display the device by GUID.
- */
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, &value) == 0 ||
- (name_flags & VDEV_NAME_GUID) != 0 ||
- have_path && have_stats && vs->vs_state <= VDEV_STATE_CANT_OPEN) {
- nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &value);
- (void) snprintf(buf, sizeof (buf), "%llu", (u_longlong_t)value);
- path = buf;
- } else if (have_path) {
-
- /*
- * If the device is dead (faulted, offline, etc) then don't
- * bother opening it. Otherwise we may be forcing the user to
- * open a misbehaving device, which can have undesirable
- * effects.
- */
- if ((have_stats == 0 ||
- vs->vs_state >= VDEV_STATE_DEGRADED) &&
- zhp != NULL &&
- nvlist_lookup_string(nv, ZPOOL_CONFIG_DEVID, &devid) == 0) {
- /*
- * Determine if the current path is correct.
- */
- char *newdevid = path_to_devid(path);
-
- if (newdevid == NULL ||
- strcmp(devid, newdevid) != 0) {
- char *newpath;
-
- if ((newpath = devid_to_path(devid)) != NULL) {
- /*
- * Update the path appropriately.
- */
- set_path(zhp, nv, newpath);
- if (nvlist_add_string(nv,
- ZPOOL_CONFIG_PATH, newpath) == 0)
- verify(nvlist_lookup_string(nv,
- ZPOOL_CONFIG_PATH,
- &path) == 0);
- free(newpath);
- }
- }
-
- if (newdevid)
- devid_str_free(newdevid);
- }
-
-#ifdef illumos
- if (name_flags & VDEV_NAME_FOLLOW_LINKS) {
- char *rp = realpath(path, NULL);
- if (rp) {
- strlcpy(buf, rp, sizeof (buf));
- path = buf;
- free(rp);
- }
- }
-
- if (strncmp(path, ZFS_DISK_ROOTD, strlen(ZFS_DISK_ROOTD)) == 0)
- path += strlen(ZFS_DISK_ROOTD);
-
- /*
- * Remove the partition from the path it this is a whole disk.
- */
- if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_WHOLE_DISK, &value)
- == 0 && value && !(name_flags & VDEV_NAME_PATH)) {
- int pathlen = strlen(path);
- char *tmp = zfs_strdup(hdl, path);
-
- /*
- * If it starts with c#, and ends with "s0" or "s1",
- * chop the slice off, or if it ends with "s0/old" or
- * "s1/old", remove the slice from the middle.
- */
- if (CTD_CHECK(tmp)) {
- if (strcmp(&tmp[pathlen - 2], "s0") == 0 ||
- strcmp(&tmp[pathlen - 2], "s1") == 0) {
- tmp[pathlen - 2] = '\0';
- } else if (pathlen > 6 &&
- (strcmp(&tmp[pathlen - 6], "s0/old") == 0 ||
- strcmp(&tmp[pathlen - 6], "s1/old") == 0)) {
- (void) strcpy(&tmp[pathlen - 6],
- "/old");
- }
- }
- return (tmp);
- }
-#else /* !illumos */
- if (strncmp(path, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
- path += sizeof(_PATH_DEV) - 1;
-#endif /* illumos */
- } else {
- verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &path) == 0);
-
- /*
- * If it's a raidz device, we need to stick in the parity level.
- */
- if (strcmp(path, VDEV_TYPE_RAIDZ) == 0) {
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NPARITY,
- &value) == 0);
- (void) snprintf(buf, sizeof (buf), "%s%llu", path,
- (u_longlong_t)value);
- path = buf;
- }
-
- /*
- * We identify each top-level vdev by using a <type-id>
- * naming convention.
- */
- if (name_flags & VDEV_NAME_TYPE_ID) {
- uint64_t id;
-
- verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ID,
- &id) == 0);
- (void) snprintf(buf, sizeof (buf), "%s-%llu", path,
- (u_longlong_t)id);
- path = buf;
- }
- }
-
- return (zfs_strdup(hdl, path));
-}
-
-static int
-zbookmark_mem_compare(const void *a, const void *b)
-{
- return (memcmp(a, b, sizeof (zbookmark_phys_t)));
-}
-
-/*
- * Retrieve the persistent error log, uniquify the members, and return to the
- * caller.
- */
-int
-zpool_get_errlog(zpool_handle_t *zhp, nvlist_t **nverrlistp)
-{
- zfs_cmd_t zc = { 0 };
- uint64_t count;
- zbookmark_phys_t *zb = NULL;
- int i;
-
- /*
- * Retrieve the raw error list from the kernel. If the number of errors
- * has increased, allocate more space and continue until we get the
- * entire list.
- */
- verify(nvlist_lookup_uint64(zhp->zpool_config, ZPOOL_CONFIG_ERRCOUNT,
- &count) == 0);
- if (count == 0)
- return (0);
- if ((zc.zc_nvlist_dst = (uintptr_t)zfs_alloc(zhp->zpool_hdl,
- count * sizeof (zbookmark_phys_t))) == (uintptr_t)NULL)
- return (-1);
- zc.zc_nvlist_dst_size = count;
- (void) strcpy(zc.zc_name, zhp->zpool_name);
- for (;;) {
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_ERROR_LOG,
- &zc) != 0) {
- free((void *)(uintptr_t)zc.zc_nvlist_dst);
- if (errno == ENOMEM) {
- void *dst;
-
- count = zc.zc_nvlist_dst_size;
- dst = zfs_alloc(zhp->zpool_hdl, count *
- sizeof (zbookmark_phys_t));
- if (dst == NULL)
- return (-1);
- zc.zc_nvlist_dst = (uintptr_t)dst;
- } else {
- return (-1);
- }
- } else {
- break;
- }
- }
-
- /*
- * Sort the resulting bookmarks. This is a little confusing due to the
- * implementation of ZFS_IOC_ERROR_LOG. The bookmarks are copied last
- * to first, and 'zc_nvlist_dst_size' indicates the number of boomarks
- * _not_ copied as part of the process. So we point the start of our
- * array appropriate and decrement the total number of elements.
- */
- zb = ((zbookmark_phys_t *)(uintptr_t)zc.zc_nvlist_dst) +
- zc.zc_nvlist_dst_size;
- count -= zc.zc_nvlist_dst_size;
-
- qsort(zb, count, sizeof (zbookmark_phys_t), zbookmark_mem_compare);
-
- verify(nvlist_alloc(nverrlistp, 0, KM_SLEEP) == 0);
-
- /*
- * Fill in the nverrlistp with nvlist's of dataset and object numbers.
- */
- for (i = 0; i < count; i++) {
- nvlist_t *nv;
-
- /* ignoring zb_blkid and zb_level for now */
- if (i > 0 && zb[i-1].zb_objset == zb[i].zb_objset &&
- zb[i-1].zb_object == zb[i].zb_object)
- continue;
-
- if (nvlist_alloc(&nv, NV_UNIQUE_NAME, KM_SLEEP) != 0)
- goto nomem;
- if (nvlist_add_uint64(nv, ZPOOL_ERR_DATASET,
- zb[i].zb_objset) != 0) {
- nvlist_free(nv);
- goto nomem;
- }
- if (nvlist_add_uint64(nv, ZPOOL_ERR_OBJECT,
- zb[i].zb_object) != 0) {
- nvlist_free(nv);
- goto nomem;
- }
- if (nvlist_add_nvlist(*nverrlistp, "ejk", nv) != 0) {
- nvlist_free(nv);
- goto nomem;
- }
- nvlist_free(nv);
- }
-
- free((void *)(uintptr_t)zc.zc_nvlist_dst);
- return (0);
-
-nomem:
- free((void *)(uintptr_t)zc.zc_nvlist_dst);
- return (no_memory(zhp->zpool_hdl));
-}
-
-/*
- * Upgrade a ZFS pool to the latest on-disk version.
- */
-int
-zpool_upgrade(zpool_handle_t *zhp, uint64_t new_version)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) strcpy(zc.zc_name, zhp->zpool_name);
- zc.zc_cookie = new_version;
-
- if (zfs_ioctl(hdl, ZFS_IOC_POOL_UPGRADE, &zc) != 0)
- return (zpool_standard_error_fmt(hdl, errno,
- dgettext(TEXT_DOMAIN, "cannot upgrade '%s'"),
- zhp->zpool_name));
- return (0);
-}
-
-void
-zfs_save_arguments(int argc, char **argv, char *string, int len)
-{
- (void) strlcpy(string, basename(argv[0]), len);
- for (int i = 1; i < argc; i++) {
- (void) strlcat(string, " ", len);
- (void) strlcat(string, argv[i], len);
- }
-}
-
-int
-zpool_log_history(libzfs_handle_t *hdl, const char *message)
-{
- zfs_cmd_t zc = { 0 };
- nvlist_t *args;
- int err;
-
- args = fnvlist_alloc();
- fnvlist_add_string(args, "message", message);
- err = zcmd_write_src_nvlist(hdl, &zc, args);
- if (err == 0)
- err = ioctl(hdl->libzfs_fd, ZFS_IOC_LOG_HISTORY, &zc);
- nvlist_free(args);
- zcmd_free_nvlists(&zc);
- return (err);
-}
-
-/*
- * Perform ioctl to get some command history of a pool.
- *
- * 'buf' is the buffer to fill up to 'len' bytes. 'off' is the
- * logical offset of the history buffer to start reading from.
- *
- * Upon return, 'off' is the next logical offset to read from and
- * 'len' is the actual amount of bytes read into 'buf'.
- */
-static int
-get_history(zpool_handle_t *zhp, char *buf, uint64_t *off, uint64_t *len)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
-
- zc.zc_history = (uint64_t)(uintptr_t)buf;
- zc.zc_history_len = *len;
- zc.zc_history_offset = *off;
-
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_POOL_GET_HISTORY, &zc) != 0) {
- switch (errno) {
- case EPERM:
- return (zfs_error_fmt(hdl, EZFS_PERM,
- dgettext(TEXT_DOMAIN,
- "cannot show history for pool '%s'"),
- zhp->zpool_name));
- case ENOENT:
- return (zfs_error_fmt(hdl, EZFS_NOHISTORY,
- dgettext(TEXT_DOMAIN, "cannot get history for pool "
- "'%s'"), zhp->zpool_name));
- case ENOTSUP:
- return (zfs_error_fmt(hdl, EZFS_BADVERSION,
- dgettext(TEXT_DOMAIN, "cannot get history for pool "
- "'%s', pool must be upgraded"), zhp->zpool_name));
- default:
- return (zpool_standard_error_fmt(hdl, errno,
- dgettext(TEXT_DOMAIN,
- "cannot get history for '%s'"), zhp->zpool_name));
- }
- }
-
- *len = zc.zc_history_len;
- *off = zc.zc_history_offset;
-
- return (0);
-}
-
-/*
- * Process the buffer of nvlists, unpacking and storing each nvlist record
- * into 'records'. 'leftover' is set to the number of bytes that weren't
- * processed as there wasn't a complete record.
- */
-int
-zpool_history_unpack(char *buf, uint64_t bytes_read, uint64_t *leftover,
- nvlist_t ***records, uint_t *numrecords)
-{
- uint64_t reclen;
- nvlist_t *nv;
- int i;
-
- while (bytes_read > sizeof (reclen)) {
-
- /* get length of packed record (stored as little endian) */
- for (i = 0, reclen = 0; i < sizeof (reclen); i++)
- reclen += (uint64_t)(((uchar_t *)buf)[i]) << (8*i);
-
- if (bytes_read < sizeof (reclen) + reclen)
- break;
-
- /* unpack record */
- if (nvlist_unpack(buf + sizeof (reclen), reclen, &nv, 0) != 0)
- return (ENOMEM);
- bytes_read -= sizeof (reclen) + reclen;
- buf += sizeof (reclen) + reclen;
-
- /* add record to nvlist array */
- (*numrecords)++;
- if (ISP2(*numrecords + 1)) {
- *records = realloc(*records,
- *numrecords * 2 * sizeof (nvlist_t *));
- }
- (*records)[*numrecords - 1] = nv;
- }
-
- *leftover = bytes_read;
- return (0);
-}
-
-/* from spa_history.c: spa_history_create_obj() */
-#define HIS_BUF_LEN_DEF (128 << 10)
-#define HIS_BUF_LEN_MAX (1 << 30)
-
-/*
- * Retrieve the command history of a pool.
- */
-int
-zpool_get_history(zpool_handle_t *zhp, nvlist_t **nvhisp, uint64_t *off,
- boolean_t *eof)
-{
- char *buf;
- uint64_t buflen = HIS_BUF_LEN_DEF;
- nvlist_t **records = NULL;
- uint_t numrecords = 0;
- int err, i;
- uint64_t start = *off;
-
- buf = malloc(buflen);
- if (buf == NULL)
- return (ENOMEM);
- /* process about 1MB at a time */
- while (*off - start < 1024 * 1024) {
- uint64_t bytes_read = buflen;
- uint64_t leftover;
-
- if ((err = get_history(zhp, buf, off, &bytes_read)) != 0)
- break;
-
- /* if nothing else was read in, we're at EOF, just return */
- if (bytes_read == 0) {
- *eof = B_TRUE;
- break;
- }
-
- if ((err = zpool_history_unpack(buf, bytes_read,
- &leftover, &records, &numrecords)) != 0)
- break;
- *off -= leftover;
- if (leftover == bytes_read) {
- /*
- * no progress made, because buffer is not big enough
- * to hold this record; resize and retry.
- */
- buflen *= 2;
- free(buf);
- buf = NULL;
- if ((buflen >= HIS_BUF_LEN_MAX) ||
- ((buf = malloc(buflen)) == NULL)) {
- err = ENOMEM;
- break;
- }
- }
- }
-
- free(buf);
-
- if (!err) {
- verify(nvlist_alloc(nvhisp, NV_UNIQUE_NAME, 0) == 0);
- verify(nvlist_add_nvlist_array(*nvhisp, ZPOOL_HIST_RECORD,
- records, numrecords) == 0);
- }
- for (i = 0; i < numrecords; i++)
- nvlist_free(records[i]);
- free(records);
-
- return (err);
-}
-
-void
-zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj,
- char *pathname, size_t len)
-{
- zfs_cmd_t zc = { 0 };
- boolean_t mounted = B_FALSE;
- char *mntpnt = NULL;
- char dsname[ZFS_MAX_DATASET_NAME_LEN];
-
- if (dsobj == 0) {
- /* special case for the MOS */
- (void) snprintf(pathname, len, "<metadata>:<0x%llx>", obj);
- return;
- }
-
- /* get the dataset's name */
- (void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- zc.zc_obj = dsobj;
- if (ioctl(zhp->zpool_hdl->libzfs_fd,
- ZFS_IOC_DSOBJ_TO_DSNAME, &zc) != 0) {
- /* just write out a path of two object numbers */
- (void) snprintf(pathname, len, "<0x%llx>:<0x%llx>",
- dsobj, obj);
- return;
- }
- (void) strlcpy(dsname, zc.zc_value, sizeof (dsname));
-
- /* find out if the dataset is mounted */
- mounted = is_mounted(zhp->zpool_hdl, dsname, &mntpnt);
-
- /* get the corrupted object's path */
- (void) strlcpy(zc.zc_name, dsname, sizeof (zc.zc_name));
- zc.zc_obj = obj;
- if (ioctl(zhp->zpool_hdl->libzfs_fd, ZFS_IOC_OBJ_TO_PATH,
- &zc) == 0) {
- if (mounted) {
- (void) snprintf(pathname, len, "%s%s", mntpnt,
- zc.zc_value);
- } else {
- (void) snprintf(pathname, len, "%s:%s",
- dsname, zc.zc_value);
- }
- } else {
- (void) snprintf(pathname, len, "%s:<0x%llx>", dsname, obj);
- }
- free(mntpnt);
-}
-
-int
-zpool_set_bootenv(zpool_handle_t *zhp, const char *envmap)
-{
- int error = lzc_set_bootenv(zhp->zpool_name, envmap);
- if (error != 0) {
- (void) zpool_standard_error_fmt(zhp->zpool_hdl, error,
- dgettext(TEXT_DOMAIN,
- "error setting bootenv in pool '%s'"), zhp->zpool_name);
- }
-
- return (error);
-}
-
-int
-zpool_get_bootenv(zpool_handle_t *zhp, char *outbuf, size_t size, off_t offset)
-{
- nvlist_t *nvl;
- int error = lzc_get_bootenv(zhp->zpool_name, &nvl);;
- if (error != 0) {
- (void) zpool_standard_error_fmt(zhp->zpool_hdl, error,
- dgettext(TEXT_DOMAIN,
- "error getting bootenv in pool '%s'"), zhp->zpool_name);
- return (-1);
- }
- char *envmap = fnvlist_lookup_string(nvl, "envmap");
- if (offset >= strlen(envmap)) {
- fnvlist_free(nvl);
- return (0);
- }
-
- strlcpy(outbuf, envmap + offset, size);
- int bytes = MIN(strlen(envmap + offset), size);
- fnvlist_free(nvl);
- return (bytes);
-}
-
-#ifdef illumos
-/*
- * Read the EFI label from the config, if a label does not exist then
- * pass back the error to the caller. If the caller has passed a non-NULL
- * diskaddr argument then we set it to the starting address of the EFI
- * partition. If the caller has passed a non-NULL boolean argument, then
- * we set it to indicate if the disk does have efi system partition.
- */
-static int
-read_efi_label(nvlist_t *config, diskaddr_t *sb, boolean_t *system)
-{
- char *path;
- int fd;
- char diskname[MAXPATHLEN];
- boolean_t boot = B_FALSE;
- int err = -1;
- int slice;
-
- if (nvlist_lookup_string(config, ZPOOL_CONFIG_PATH, &path) != 0)
- return (err);
-
- (void) snprintf(diskname, sizeof (diskname), "%s%s", ZFS_RDISK_ROOT,
- strrchr(path, '/'));
- if ((fd = open(diskname, O_RDONLY|O_NDELAY)) >= 0) {
- struct dk_gpt *vtoc;
-
- if ((err = efi_alloc_and_read(fd, &vtoc)) >= 0) {
- for (slice = 0; slice < vtoc->efi_nparts; slice++) {
- if (vtoc->efi_parts[slice].p_tag == V_SYSTEM)
- boot = B_TRUE;
- if (vtoc->efi_parts[slice].p_tag == V_USR)
- break;
- }
- if (sb != NULL && vtoc->efi_parts[slice].p_tag == V_USR)
- *sb = vtoc->efi_parts[slice].p_start;
- if (system != NULL)
- *system = boot;
- efi_free(vtoc);
- }
- (void) close(fd);
- }
- return (err);
-}
-
-/*
- * determine where a partition starts on a disk in the current
- * configuration
- */
-static diskaddr_t
-find_start_block(nvlist_t *config)
-{
- nvlist_t **child;
- uint_t c, children;
- diskaddr_t sb = MAXOFFSET_T;
- uint64_t wholedisk;
-
- if (nvlist_lookup_nvlist_array(config,
- ZPOOL_CONFIG_CHILDREN, &child, &children) != 0) {
- if (nvlist_lookup_uint64(config,
- ZPOOL_CONFIG_WHOLE_DISK,
- &wholedisk) != 0 || !wholedisk) {
- return (MAXOFFSET_T);
- }
- if (read_efi_label(config, &sb, NULL) < 0)
- sb = MAXOFFSET_T;
- return (sb);
- }
-
- for (c = 0; c < children; c++) {
- sb = find_start_block(child[c]);
- if (sb != MAXOFFSET_T) {
- return (sb);
- }
- }
- return (MAXOFFSET_T);
-}
-#endif /* illumos */
-
-/*
- * Label an individual disk. The name provided is the short name,
- * stripped of any leading /dev path.
- */
-int
-zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, const char *name,
- zpool_boot_label_t boot_type, uint64_t boot_size, int *slice)
-{
-#ifdef illumos
- char path[MAXPATHLEN];
- struct dk_gpt *vtoc;
- int fd;
- size_t resv = EFI_MIN_RESV_SIZE;
- uint64_t slice_size;
- diskaddr_t start_block;
- char errbuf[1024];
-
- /* prepare an error message just in case */
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot label '%s'"), name);
-
- if (zhp) {
- nvlist_t *nvroot;
-
- verify(nvlist_lookup_nvlist(zhp->zpool_config,
- ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
-
- if (zhp->zpool_start_block == 0)
- start_block = find_start_block(nvroot);
- else
- start_block = zhp->zpool_start_block;
- zhp->zpool_start_block = start_block;
- } else {
- /* new pool */
- start_block = NEW_START_BLOCK;
- }
-
- (void) snprintf(path, sizeof (path), "%s/%s%s", ZFS_RDISK_ROOT, name,
- BACKUP_SLICE);
-
- if ((fd = open(path, O_RDWR | O_NDELAY)) < 0) {
- /*
- * This shouldn't happen. We've long since verified that this
- * is a valid device.
- */
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "unable to open device"));
- return (zfs_error(hdl, EZFS_OPENFAILED, errbuf));
- }
-
- if (efi_alloc_and_init(fd, EFI_NUMPAR, &vtoc) != 0) {
- /*
- * The only way this can fail is if we run out of memory, or we
- * were unable to read the disk's capacity
- */
- if (errno == ENOMEM)
- (void) no_memory(hdl);
-
- (void) close(fd);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "unable to read disk capacity"), name);
-
- return (zfs_error(hdl, EZFS_NOCAP, errbuf));
- }
-
- /*
- * Why we use V_USR: V_BACKUP confuses users, and is considered
- * disposable by some EFI utilities (since EFI doesn't have a backup
- * slice). V_UNASSIGNED is supposed to be used only for zero size
- * partitions, and efi_write() will fail if we use it. V_ROOT, V_BOOT,
- * etc. were all pretty specific. V_USR is as close to reality as we
- * can get, in the absence of V_OTHER.
- */
- /* first fix the partition start block */
- if (start_block == MAXOFFSET_T)
- start_block = NEW_START_BLOCK;
-
- /*
- * EFI System partition is using slice 0.
- * ZFS is on slice 1 and slice 8 is reserved.
- * We assume the GPT partition table without system
- * partition has zfs p_start == NEW_START_BLOCK.
- * If start_block != NEW_START_BLOCK, it means we have
- * system partition. Correct solution would be to query/cache vtoc
- * from existing vdev member.
- */
- if (boot_type == ZPOOL_CREATE_BOOT_LABEL) {
- if (boot_size % vtoc->efi_lbasize != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "boot partition size must be a multiple of %d"),
- vtoc->efi_lbasize);
- (void) close(fd);
- efi_free(vtoc);
- return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
- }
- /*
- * System partition size checks.
- * Note the 1MB is quite arbitrary value, since we
- * are creating dedicated pool, it should be enough
- * to hold fat + efi bootloader. May need to be
- * adjusted if the bootloader size will grow.
- */
- if (boot_size < 1024 * 1024) {
- char buf[64];
- zfs_nicenum(boot_size, buf, sizeof (buf));
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "Specified size %s for EFI System partition is too "
- "small, the minimum size is 1MB."), buf);
- (void) close(fd);
- efi_free(vtoc);
- return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
- }
- /* 33MB is tested with mkfs -F pcfs */
- if (hdl->libzfs_printerr &&
- ((vtoc->efi_lbasize == 512 &&
- boot_size < 33 * 1024 * 1024) ||
- (vtoc->efi_lbasize == 4096 &&
- boot_size < 256 * 1024 * 1024))) {
- char buf[64];
- zfs_nicenum(boot_size, buf, sizeof (buf));
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "Warning: EFI System partition size %s is "
- "not allowing to create FAT32 file\nsystem, which "
- "may result in unbootable system.\n"), buf);
- }
- /* Adjust zfs partition start by size of system partition. */
- start_block += boot_size / vtoc->efi_lbasize;
- }
-
- if (start_block == NEW_START_BLOCK) {
- /*
- * Use default layout.
- * ZFS is on slice 0 and slice 8 is reserved.
- */
- slice_size = vtoc->efi_last_u_lba + 1;
- slice_size -= EFI_MIN_RESV_SIZE;
- slice_size -= start_block;
- if (slice != NULL)
- *slice = 0;
-
- vtoc->efi_parts[0].p_start = start_block;
- vtoc->efi_parts[0].p_size = slice_size;
-
- vtoc->efi_parts[0].p_tag = V_USR;
- (void) strcpy(vtoc->efi_parts[0].p_name, "zfs");
-
- vtoc->efi_parts[8].p_start = slice_size + start_block;
- vtoc->efi_parts[8].p_size = resv;
- vtoc->efi_parts[8].p_tag = V_RESERVED;
- } else {
- slice_size = start_block - NEW_START_BLOCK;
- vtoc->efi_parts[0].p_start = NEW_START_BLOCK;
- vtoc->efi_parts[0].p_size = slice_size;
- vtoc->efi_parts[0].p_tag = V_SYSTEM;
- (void) strcpy(vtoc->efi_parts[0].p_name, "loader");
- if (slice != NULL)
- *slice = 1;
- /* prepare slice 1 */
- slice_size = vtoc->efi_last_u_lba + 1 - slice_size;
- slice_size -= resv;
- slice_size -= NEW_START_BLOCK;
- vtoc->efi_parts[1].p_start = start_block;
- vtoc->efi_parts[1].p_size = slice_size;
- vtoc->efi_parts[1].p_tag = V_USR;
- (void) strcpy(vtoc->efi_parts[1].p_name, "zfs");
-
- vtoc->efi_parts[8].p_start = slice_size + start_block;
- vtoc->efi_parts[8].p_size = resv;
- vtoc->efi_parts[8].p_tag = V_RESERVED;
- }
-
- if (efi_write(fd, vtoc) != 0) {
- /*
- * Some block drivers (like pcata) may not support EFI
- * GPT labels. Print out a helpful error message dir-
- * ecting the user to manually label the disk and give
- * a specific slice.
- */
- (void) close(fd);
- efi_free(vtoc);
-
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "try using fdisk(1M) and then provide a specific slice"));
- return (zfs_error(hdl, EZFS_LABELFAILED, errbuf));
- }
-
- (void) close(fd);
- efi_free(vtoc);
-#endif /* illumos */
- return (0);
-}
-
-static boolean_t
-supported_dump_vdev_type(libzfs_handle_t *hdl, nvlist_t *config, char *errbuf)
-{
- char *type;
- nvlist_t **child;
- uint_t children, c;
-
- verify(nvlist_lookup_string(config, ZPOOL_CONFIG_TYPE, &type) == 0);
- if (strcmp(type, VDEV_TYPE_FILE) == 0 ||
- strcmp(type, VDEV_TYPE_HOLE) == 0 ||
- strcmp(type, VDEV_TYPE_MISSING) == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "vdev type '%s' is not supported"), type);
- (void) zfs_error(hdl, EZFS_VDEVNOTSUP, errbuf);
- return (B_FALSE);
- }
- if (nvlist_lookup_nvlist_array(config, ZPOOL_CONFIG_CHILDREN,
- &child, &children) == 0) {
- for (c = 0; c < children; c++) {
- if (!supported_dump_vdev_type(hdl, child[c], errbuf))
- return (B_FALSE);
- }
- }
- return (B_TRUE);
-}
-
-/*
- * Check if this zvol is allowable for use as a dump device; zero if
- * it is, > 0 if it isn't, < 0 if it isn't a zvol.
- *
- * Allowable storage configurations include mirrors, all raidz variants, and
- * pools with log, cache, and spare devices. Pools which are backed by files or
- * have missing/hole vdevs are not suitable.
- */
-int
-zvol_check_dump_config(char *arg)
-{
- zpool_handle_t *zhp = NULL;
- nvlist_t *config, *nvroot;
- char *p, *volname;
- nvlist_t **top;
- uint_t toplevels;
- libzfs_handle_t *hdl;
- char errbuf[1024];
- char poolname[ZFS_MAX_DATASET_NAME_LEN];
- int pathlen = strlen(ZVOL_FULL_DEV_DIR);
- int ret = 1;
-
- if (strncmp(arg, ZVOL_FULL_DEV_DIR, pathlen)) {
- return (-1);
- }
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "dump is not supported on device '%s'"), arg);
-
- if ((hdl = libzfs_init()) == NULL)
- return (1);
- libzfs_print_on_error(hdl, B_TRUE);
-
- volname = arg + pathlen;
-
- /* check the configuration of the pool */
- if ((p = strchr(volname, '/')) == NULL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "malformed dataset name"));
- (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
- return (1);
- } else if (p - volname >= ZFS_MAX_DATASET_NAME_LEN) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "dataset name is too long"));
- (void) zfs_error(hdl, EZFS_NAMETOOLONG, errbuf);
- return (1);
- } else {
- (void) strncpy(poolname, volname, p - volname);
- poolname[p - volname] = '\0';
- }
-
- if ((zhp = zpool_open(hdl, poolname)) == NULL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "could not open pool '%s'"), poolname);
- (void) zfs_error(hdl, EZFS_OPENFAILED, errbuf);
- goto out;
- }
- config = zpool_get_config(zhp, NULL);
- if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "could not obtain vdev configuration for '%s'"), poolname);
- (void) zfs_error(hdl, EZFS_INVALCONFIG, errbuf);
- goto out;
- }
-
- verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN,
- &top, &toplevels) == 0);
-
- if (!supported_dump_vdev_type(hdl, top[0], errbuf)) {
- goto out;
- }
- ret = 0;
-
-out:
- if (zhp)
- zpool_close(zhp);
- libzfs_fini(hdl);
- return (ret);
-}
-
-int
-zpool_nextboot(libzfs_handle_t *hdl, uint64_t pool_guid, uint64_t dev_guid,
- const char *command)
-{
- zfs_cmd_t zc = { 0 };
- nvlist_t *args;
- char *packed;
- size_t size;
- int error;
-
- args = fnvlist_alloc();
- fnvlist_add_uint64(args, ZPOOL_CONFIG_POOL_GUID, pool_guid);
- fnvlist_add_uint64(args, ZPOOL_CONFIG_GUID, dev_guid);
- fnvlist_add_string(args, "command", command);
- error = zcmd_write_src_nvlist(hdl, &zc, args);
- if (error == 0)
- error = ioctl(hdl->libzfs_fd, ZFS_IOC_NEXTBOOT, &zc);
- zcmd_free_nvlists(&zc);
- nvlist_free(args);
- return (error);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
deleted file mode 100644
index 2e2e1020ad8af..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_sendrecv.c
+++ /dev/null
@@ -1,3924 +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, 2015 by Delphix. All rights reserved.
- * Copyright (c) 2012, Joyent, Inc. All rights reserved.
- * Copyright (c) 2012 Pawel Jakub Dawidek. All rights reserved.
- * Copyright (c) 2013 Steven Hartland. All rights reserved.
- * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
- * Copyright (c) 2014 Integros [integros.com]
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
- * Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>. All rights reserved.
- * Copyright (c) 2019 Datto Inc.
- */
-
-#include <assert.h>
-#include <ctype.h>
-#include <errno.h>
-#include <libintl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <unistd.h>
-#include <stddef.h>
-#include <fcntl.h>
-#include <sys/param.h>
-#include <sys/mount.h>
-#include <pthread.h>
-#include <umem.h>
-#include <time.h>
-
-#include <libzfs.h>
-#include <libzfs_core.h>
-
-#include "zfs_namecheck.h"
-#include "zfs_prop.h"
-#include "zfs_fletcher.h"
-#include "libzfs_impl.h"
-#include <zlib.h>
-#include <sha2.h>
-#include <sys/zio_checksum.h>
-#include <sys/ddt.h>
-
-#ifdef __FreeBSD__
-extern int zfs_ioctl_version;
-#endif
-
-/* in libzfs_dataset.c */
-extern void zfs_setprop_error(libzfs_handle_t *, zfs_prop_t, int, char *);
-/* We need to use something for ENODATA. */
-#define ENODATA EIDRM
-
-static int zfs_receive_impl(libzfs_handle_t *, const char *, const char *,
- recvflags_t *, int, const char *, nvlist_t *, avl_tree_t *, char **, int,
- uint64_t *, const char *);
-static int guid_to_name(libzfs_handle_t *, const char *,
- uint64_t, boolean_t, char *);
-
-static const zio_cksum_t zero_cksum = { 0 };
-
-typedef struct dedup_arg {
- int inputfd;
- int outputfd;
- libzfs_handle_t *dedup_hdl;
-} dedup_arg_t;
-
-typedef struct progress_arg {
- zfs_handle_t *pa_zhp;
- int pa_fd;
- boolean_t pa_parsable;
- boolean_t pa_astitle;
- uint64_t pa_size;
-} progress_arg_t;
-
-typedef struct dataref {
- uint64_t ref_guid;
- uint64_t ref_object;
- uint64_t ref_offset;
-} dataref_t;
-
-typedef struct dedup_entry {
- struct dedup_entry *dde_next;
- zio_cksum_t dde_chksum;
- uint64_t dde_prop;
- dataref_t dde_ref;
-} dedup_entry_t;
-
-#define MAX_DDT_PHYSMEM_PERCENT 20
-#define SMALLEST_POSSIBLE_MAX_DDT_MB 128
-
-typedef struct dedup_table {
- dedup_entry_t **dedup_hash_array;
- umem_cache_t *ddecache;
- uint64_t max_ddt_size; /* max dedup table size in bytes */
- uint64_t cur_ddt_size; /* current dedup table size in bytes */
- uint64_t ddt_count;
- int numhashbits;
- boolean_t ddt_full;
-} dedup_table_t;
-
-static int
-high_order_bit(uint64_t n)
-{
- int count;
-
- for (count = 0; n != 0; count++)
- n >>= 1;
- return (count);
-}
-
-static size_t
-ssread(void *buf, size_t len, FILE *stream)
-{
- size_t outlen;
-
- if ((outlen = fread(buf, len, 1, stream)) == 0)
- return (0);
-
- return (outlen);
-}
-
-static void
-ddt_hash_append(libzfs_handle_t *hdl, dedup_table_t *ddt, dedup_entry_t **ddepp,
- zio_cksum_t *cs, uint64_t prop, dataref_t *dr)
-{
- dedup_entry_t *dde;
-
- if (ddt->cur_ddt_size >= ddt->max_ddt_size) {
- if (ddt->ddt_full == B_FALSE) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "Dedup table full. Deduplication will continue "
- "with existing table entries"));
- ddt->ddt_full = B_TRUE;
- }
- return;
- }
-
- if ((dde = umem_cache_alloc(ddt->ddecache, UMEM_DEFAULT))
- != NULL) {
- assert(*ddepp == NULL);
- dde->dde_next = NULL;
- dde->dde_chksum = *cs;
- dde->dde_prop = prop;
- dde->dde_ref = *dr;
- *ddepp = dde;
- ddt->cur_ddt_size += sizeof (dedup_entry_t);
- ddt->ddt_count++;
- }
-}
-
-/*
- * Using the specified dedup table, do a lookup for an entry with
- * the checksum cs. If found, return the block's reference info
- * in *dr. Otherwise, insert a new entry in the dedup table, using
- * the reference information specified by *dr.
- *
- * return value: true - entry was found
- * false - entry was not found
- */
-static boolean_t
-ddt_update(libzfs_handle_t *hdl, dedup_table_t *ddt, zio_cksum_t *cs,
- uint64_t prop, dataref_t *dr)
-{
- uint32_t hashcode;
- dedup_entry_t **ddepp;
-
- hashcode = BF64_GET(cs->zc_word[0], 0, ddt->numhashbits);
-
- for (ddepp = &(ddt->dedup_hash_array[hashcode]); *ddepp != NULL;
- ddepp = &((*ddepp)->dde_next)) {
- if (ZIO_CHECKSUM_EQUAL(((*ddepp)->dde_chksum), *cs) &&
- (*ddepp)->dde_prop == prop) {
- *dr = (*ddepp)->dde_ref;
- return (B_TRUE);
- }
- }
- ddt_hash_append(hdl, ddt, ddepp, cs, prop, dr);
- return (B_FALSE);
-}
-
-static int
-dump_record(dmu_replay_record_t *drr, void *payload, int payload_len,
- zio_cksum_t *zc, int outfd)
-{
- ASSERT3U(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum),
- ==, sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t));
- (void) fletcher_4_incremental_native(drr,
- offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum), zc);
- if (drr->drr_type != DRR_BEGIN) {
- ASSERT(ZIO_CHECKSUM_IS_ZERO(&drr->drr_u.
- drr_checksum.drr_checksum));
- drr->drr_u.drr_checksum.drr_checksum = *zc;
- }
- (void) fletcher_4_incremental_native(
- &drr->drr_u.drr_checksum.drr_checksum, sizeof (zio_cksum_t), zc);
- if (write(outfd, drr, sizeof (*drr)) == -1)
- return (errno);
- if (payload_len != 0) {
- (void) fletcher_4_incremental_native(payload, payload_len, zc);
- if (write(outfd, payload, payload_len) == -1)
- return (errno);
- }
- return (0);
-}
-
-/*
- * This function is started in a separate thread when the dedup option
- * has been requested. The main send thread determines the list of
- * snapshots to be included in the send stream and makes the ioctl calls
- * for each one. But instead of having the ioctl send the output to the
- * the output fd specified by the caller of zfs_send()), the
- * ioctl is told to direct the output to a pipe, which is read by the
- * alternate thread running THIS function. This function does the
- * dedup'ing by:
- * 1. building a dedup table (the DDT)
- * 2. doing checksums on each data block and inserting a record in the DDT
- * 3. looking for matching checksums, and
- * 4. sending a DRR_WRITE_BYREF record instead of a write record whenever
- * a duplicate block is found.
- * The output of this function then goes to the output fd requested
- * by the caller of zfs_send().
- */
-static void *
-cksummer(void *arg)
-{
- dedup_arg_t *dda = arg;
- char *buf = zfs_alloc(dda->dedup_hdl, SPA_MAXBLOCKSIZE);
- dmu_replay_record_t thedrr;
- dmu_replay_record_t *drr = &thedrr;
- FILE *ofp;
- int outfd;
- dedup_table_t ddt;
- zio_cksum_t stream_cksum;
- uint64_t physmem = sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE);
- uint64_t numbuckets;
-
- ddt.max_ddt_size =
- MAX((physmem * MAX_DDT_PHYSMEM_PERCENT) / 100,
- SMALLEST_POSSIBLE_MAX_DDT_MB << 20);
-
- numbuckets = ddt.max_ddt_size / (sizeof (dedup_entry_t));
-
- /*
- * numbuckets must be a power of 2. Increase number to
- * a power of 2 if necessary.
- */
- if (!ISP2(numbuckets))
- numbuckets = 1 << high_order_bit(numbuckets);
-
- ddt.dedup_hash_array = calloc(numbuckets, sizeof (dedup_entry_t *));
- ddt.ddecache = umem_cache_create("dde", sizeof (dedup_entry_t), 0,
- NULL, NULL, NULL, NULL, NULL, 0);
- ddt.cur_ddt_size = numbuckets * sizeof (dedup_entry_t *);
- ddt.numhashbits = high_order_bit(numbuckets) - 1;
- ddt.ddt_full = B_FALSE;
-
- outfd = dda->outputfd;
- ofp = fdopen(dda->inputfd, "r");
- while (ssread(drr, sizeof (*drr), ofp) != 0) {
-
- /*
- * kernel filled in checksum, we are going to write same
- * record, but need to regenerate checksum.
- */
- if (drr->drr_type != DRR_BEGIN) {
- bzero(&drr->drr_u.drr_checksum.drr_checksum,
- sizeof (drr->drr_u.drr_checksum.drr_checksum));
- }
-
- switch (drr->drr_type) {
- case DRR_BEGIN:
- {
- struct drr_begin *drrb = &drr->drr_u.drr_begin;
- int fflags;
- int sz = 0;
- ZIO_SET_CHECKSUM(&stream_cksum, 0, 0, 0, 0);
-
- ASSERT3U(drrb->drr_magic, ==, DMU_BACKUP_MAGIC);
-
- /* set the DEDUP feature flag for this stream */
- fflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo);
- fflags |= (DMU_BACKUP_FEATURE_DEDUP |
- DMU_BACKUP_FEATURE_DEDUPPROPS);
- DMU_SET_FEATUREFLAGS(drrb->drr_versioninfo, fflags);
-
- if (drr->drr_payloadlen != 0) {
- sz = drr->drr_payloadlen;
-
- if (sz > SPA_MAXBLOCKSIZE) {
- buf = zfs_realloc(dda->dedup_hdl, buf,
- SPA_MAXBLOCKSIZE, sz);
- }
- (void) ssread(buf, sz, ofp);
- if (ferror(stdin))
- perror("fread");
- }
- if (dump_record(drr, buf, sz, &stream_cksum,
- outfd) != 0)
- goto out;
- break;
- }
-
- case DRR_END:
- {
- struct drr_end *drre = &drr->drr_u.drr_end;
- /* use the recalculated checksum */
- drre->drr_checksum = stream_cksum;
- if (dump_record(drr, NULL, 0, &stream_cksum,
- outfd) != 0)
- goto out;
- break;
- }
-
- case DRR_OBJECT:
- {
- struct drr_object *drro = &drr->drr_u.drr_object;
- if (drro->drr_bonuslen > 0) {
- (void) ssread(buf,
- P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8),
- ofp);
- }
- if (dump_record(drr, buf,
- P2ROUNDUP((uint64_t)drro->drr_bonuslen, 8),
- &stream_cksum, outfd) != 0)
- goto out;
- break;
- }
-
- case DRR_SPILL:
- {
- struct drr_spill *drrs = &drr->drr_u.drr_spill;
- (void) ssread(buf, drrs->drr_length, ofp);
- if (dump_record(drr, buf, drrs->drr_length,
- &stream_cksum, outfd) != 0)
- goto out;
- break;
- }
-
- case DRR_FREEOBJECTS:
- {
- if (dump_record(drr, NULL, 0, &stream_cksum,
- outfd) != 0)
- goto out;
- break;
- }
-
- case DRR_WRITE:
- {
- struct drr_write *drrw = &drr->drr_u.drr_write;
- dataref_t dataref;
- uint64_t payload_size;
-
- payload_size = DRR_WRITE_PAYLOAD_SIZE(drrw);
- (void) ssread(buf, payload_size, ofp);
-
- /*
- * Use the existing checksum if it's dedup-capable,
- * else calculate a SHA256 checksum for it.
- */
-
- if (ZIO_CHECKSUM_EQUAL(drrw->drr_key.ddk_cksum,
- zero_cksum) ||
- !DRR_IS_DEDUP_CAPABLE(drrw->drr_checksumflags)) {
- SHA256_CTX ctx;
- zio_cksum_t tmpsha256;
-
- SHA256Init(&ctx);
- SHA256Update(&ctx, buf, payload_size);
- SHA256Final(&tmpsha256, &ctx);
- drrw->drr_key.ddk_cksum.zc_word[0] =
- BE_64(tmpsha256.zc_word[0]);
- drrw->drr_key.ddk_cksum.zc_word[1] =
- BE_64(tmpsha256.zc_word[1]);
- drrw->drr_key.ddk_cksum.zc_word[2] =
- BE_64(tmpsha256.zc_word[2]);
- drrw->drr_key.ddk_cksum.zc_word[3] =
- BE_64(tmpsha256.zc_word[3]);
- drrw->drr_checksumtype = ZIO_CHECKSUM_SHA256;
- drrw->drr_checksumflags = DRR_CHECKSUM_DEDUP;
- }
-
- dataref.ref_guid = drrw->drr_toguid;
- dataref.ref_object = drrw->drr_object;
- dataref.ref_offset = drrw->drr_offset;
-
- if (ddt_update(dda->dedup_hdl, &ddt,
- &drrw->drr_key.ddk_cksum, drrw->drr_key.ddk_prop,
- &dataref)) {
- dmu_replay_record_t wbr_drr = {0};
- struct drr_write_byref *wbr_drrr =
- &wbr_drr.drr_u.drr_write_byref;
-
- /* block already present in stream */
- wbr_drr.drr_type = DRR_WRITE_BYREF;
-
- wbr_drrr->drr_object = drrw->drr_object;
- wbr_drrr->drr_offset = drrw->drr_offset;
- wbr_drrr->drr_length = drrw->drr_logical_size;
- wbr_drrr->drr_toguid = drrw->drr_toguid;
- wbr_drrr->drr_refguid = dataref.ref_guid;
- wbr_drrr->drr_refobject =
- dataref.ref_object;
- wbr_drrr->drr_refoffset =
- dataref.ref_offset;
-
- wbr_drrr->drr_checksumtype =
- drrw->drr_checksumtype;
- wbr_drrr->drr_checksumflags =
- drrw->drr_checksumtype;
- wbr_drrr->drr_key.ddk_cksum =
- drrw->drr_key.ddk_cksum;
- wbr_drrr->drr_key.ddk_prop =
- drrw->drr_key.ddk_prop;
-
- if (dump_record(&wbr_drr, NULL, 0,
- &stream_cksum, outfd) != 0)
- goto out;
- } else {
- /* block not previously seen */
- if (dump_record(drr, buf, payload_size,
- &stream_cksum, outfd) != 0)
- goto out;
- }
- break;
- }
-
- case DRR_WRITE_EMBEDDED:
- {
- struct drr_write_embedded *drrwe =
- &drr->drr_u.drr_write_embedded;
- (void) ssread(buf,
- P2ROUNDUP((uint64_t)drrwe->drr_psize, 8), ofp);
- if (dump_record(drr, buf,
- P2ROUNDUP((uint64_t)drrwe->drr_psize, 8),
- &stream_cksum, outfd) != 0)
- goto out;
- break;
- }
-
- case DRR_FREE:
- {
- if (dump_record(drr, NULL, 0, &stream_cksum,
- outfd) != 0)
- goto out;
- break;
- }
-
- default:
- (void) fprintf(stderr, "INVALID record type 0x%x\n",
- drr->drr_type);
- /* should never happen, so assert */
- assert(B_FALSE);
- }
- }
-out:
- umem_cache_destroy(ddt.ddecache);
- free(ddt.dedup_hash_array);
- free(buf);
- (void) fclose(ofp);
-
- return (NULL);
-}
-
-/*
- * Routines for dealing with the AVL tree of fs-nvlists
- */
-typedef struct fsavl_node {
- avl_node_t fn_node;
- nvlist_t *fn_nvfs;
- char *fn_snapname;
- uint64_t fn_guid;
-} fsavl_node_t;
-
-static int
-fsavl_compare(const void *arg1, const void *arg2)
-{
- const fsavl_node_t *fn1 = (const fsavl_node_t *)arg1;
- const fsavl_node_t *fn2 = (const fsavl_node_t *)arg2;
-
- return (AVL_CMP(fn1->fn_guid, fn2->fn_guid));
-}
-
-/*
- * Given the GUID of a snapshot, find its containing filesystem and
- * (optionally) name.
- */
-static nvlist_t *
-fsavl_find(avl_tree_t *avl, uint64_t snapguid, char **snapname)
-{
- fsavl_node_t fn_find;
- fsavl_node_t *fn;
-
- fn_find.fn_guid = snapguid;
-
- fn = avl_find(avl, &fn_find, NULL);
- if (fn) {
- if (snapname)
- *snapname = fn->fn_snapname;
- return (fn->fn_nvfs);
- }
- return (NULL);
-}
-
-static void
-fsavl_destroy(avl_tree_t *avl)
-{
- fsavl_node_t *fn;
- void *cookie;
-
- if (avl == NULL)
- return;
-
- cookie = NULL;
- while ((fn = avl_destroy_nodes(avl, &cookie)) != NULL)
- free(fn);
- avl_destroy(avl);
- free(avl);
-}
-
-/*
- * Given an nvlist, produce an avl tree of snapshots, ordered by guid
- */
-static avl_tree_t *
-fsavl_create(nvlist_t *fss)
-{
- avl_tree_t *fsavl;
- nvpair_t *fselem = NULL;
-
- if ((fsavl = malloc(sizeof (avl_tree_t))) == NULL)
- return (NULL);
-
- avl_create(fsavl, fsavl_compare, sizeof (fsavl_node_t),
- offsetof(fsavl_node_t, fn_node));
-
- while ((fselem = nvlist_next_nvpair(fss, fselem)) != NULL) {
- nvlist_t *nvfs, *snaps;
- nvpair_t *snapelem = NULL;
-
- VERIFY(0 == nvpair_value_nvlist(fselem, &nvfs));
- VERIFY(0 == nvlist_lookup_nvlist(nvfs, "snaps", &snaps));
-
- while ((snapelem =
- nvlist_next_nvpair(snaps, snapelem)) != NULL) {
- fsavl_node_t *fn;
- uint64_t guid;
-
- VERIFY(0 == nvpair_value_uint64(snapelem, &guid));
- if ((fn = malloc(sizeof (fsavl_node_t))) == NULL) {
- fsavl_destroy(fsavl);
- return (NULL);
- }
- fn->fn_nvfs = nvfs;
- fn->fn_snapname = nvpair_name(snapelem);
- fn->fn_guid = guid;
-
- /*
- * Note: if there are multiple snaps with the
- * same GUID, we ignore all but one.
- */
- if (avl_find(fsavl, fn, NULL) == NULL)
- avl_add(fsavl, fn);
- else
- free(fn);
- }
- }
-
- return (fsavl);
-}
-
-/*
- * Routines for dealing with the giant nvlist of fs-nvlists, etc.
- */
-typedef struct send_data {
- /*
- * assigned inside every recursive call,
- * restored from *_save on return:
- *
- * guid of fromsnap snapshot in parent dataset
- * txg of fromsnap snapshot in current dataset
- * txg of tosnap snapshot in current dataset
- */
-
- uint64_t parent_fromsnap_guid;
- uint64_t fromsnap_txg;
- uint64_t tosnap_txg;
-
- /* the nvlists get accumulated during depth-first traversal */
- nvlist_t *parent_snaps;
- nvlist_t *fss;
- nvlist_t *snapprops;
-
- /* send-receive configuration, does not change during traversal */
- const char *fsname;
- const char *fromsnap;
- const char *tosnap;
- boolean_t recursive;
- boolean_t verbose;
- boolean_t replicate;
-
- /*
- * The header nvlist is of the following format:
- * {
- * "tosnap" -> string
- * "fromsnap" -> string (if incremental)
- * "fss" -> {
- * id -> {
- *
- * "name" -> string (full name; for debugging)
- * "parentfromsnap" -> number (guid of fromsnap in parent)
- *
- * "props" -> { name -> value (only if set here) }
- * "snaps" -> { name (lastname) -> number (guid) }
- * "snapprops" -> { name (lastname) -> { name -> value } }
- *
- * "origin" -> number (guid) (if clone)
- * "sent" -> boolean (not on-disk)
- * }
- * }
- * }
- *
- */
-} send_data_t;
-
-static void send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv);
-
-static int
-send_iterate_snap(zfs_handle_t *zhp, void *arg)
-{
- send_data_t *sd = arg;
- uint64_t guid = zhp->zfs_dmustats.dds_guid;
- uint64_t txg = zhp->zfs_dmustats.dds_creation_txg;
- char *snapname;
- nvlist_t *nv;
-
- snapname = strrchr(zhp->zfs_name, '@')+1;
-
- if (sd->tosnap_txg != 0 && txg > sd->tosnap_txg) {
- if (sd->verbose) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "skipping snapshot %s because it was created "
- "after the destination snapshot (%s)\n"),
- zhp->zfs_name, sd->tosnap);
- }
- zfs_close(zhp);
- return (0);
- }
-
- VERIFY(0 == nvlist_add_uint64(sd->parent_snaps, snapname, guid));
- /*
- * NB: if there is no fromsnap here (it's a newly created fs in
- * an incremental replication), we will substitute the tosnap.
- */
- if ((sd->fromsnap && strcmp(snapname, sd->fromsnap) == 0) ||
- (sd->parent_fromsnap_guid == 0 && sd->tosnap &&
- strcmp(snapname, sd->tosnap) == 0)) {
- sd->parent_fromsnap_guid = guid;
- }
-
- VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
- send_iterate_prop(zhp, nv);
- VERIFY(0 == nvlist_add_nvlist(sd->snapprops, snapname, nv));
- nvlist_free(nv);
-
- zfs_close(zhp);
- return (0);
-}
-
-static void
-send_iterate_prop(zfs_handle_t *zhp, nvlist_t *nv)
-{
- nvpair_t *elem = NULL;
-
- while ((elem = nvlist_next_nvpair(zhp->zfs_props, elem)) != NULL) {
- char *propname = nvpair_name(elem);
- zfs_prop_t prop = zfs_name_to_prop(propname);
- nvlist_t *propnv;
-
- if (!zfs_prop_user(propname)) {
- /*
- * Realistically, this should never happen. However,
- * we want the ability to add DSL properties without
- * needing to make incompatible version changes. We
- * need to ignore unknown properties to allow older
- * software to still send datasets containing these
- * properties, with the unknown properties elided.
- */
- if (prop == ZPROP_INVAL)
- continue;
-
- if (zfs_prop_readonly(prop))
- continue;
- }
-
- verify(nvpair_value_nvlist(elem, &propnv) == 0);
- if (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_RESERVATION ||
- prop == ZFS_PROP_REFQUOTA ||
- prop == ZFS_PROP_REFRESERVATION) {
- char *source;
- uint64_t value;
- verify(nvlist_lookup_uint64(propnv,
- ZPROP_VALUE, &value) == 0);
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
- continue;
- /*
- * May have no source before SPA_VERSION_RECVD_PROPS,
- * but is still modifiable.
- */
- if (nvlist_lookup_string(propnv,
- ZPROP_SOURCE, &source) == 0) {
- if ((strcmp(source, zhp->zfs_name) != 0) &&
- (strcmp(source,
- ZPROP_SOURCE_VAL_RECVD) != 0))
- continue;
- }
- } else {
- char *source;
- if (nvlist_lookup_string(propnv,
- ZPROP_SOURCE, &source) != 0)
- continue;
- if ((strcmp(source, zhp->zfs_name) != 0) &&
- (strcmp(source, ZPROP_SOURCE_VAL_RECVD) != 0))
- continue;
- }
-
- if (zfs_prop_user(propname) ||
- zfs_prop_get_type(prop) == PROP_TYPE_STRING) {
- char *value;
- verify(nvlist_lookup_string(propnv,
- ZPROP_VALUE, &value) == 0);
- VERIFY(0 == nvlist_add_string(nv, propname, value));
- } else {
- uint64_t value;
- verify(nvlist_lookup_uint64(propnv,
- ZPROP_VALUE, &value) == 0);
- VERIFY(0 == nvlist_add_uint64(nv, propname, value));
- }
- }
-}
-
-/*
- * returns snapshot creation txg
- * and returns 0 if the snapshot does not exist
- */
-static uint64_t
-get_snap_txg(libzfs_handle_t *hdl, const char *fs, const char *snap)
-{
- char name[ZFS_MAX_DATASET_NAME_LEN];
- uint64_t txg = 0;
-
- if (fs == NULL || fs[0] == '\0' || snap == NULL || snap[0] == '\0')
- return (txg);
-
- (void) snprintf(name, sizeof (name), "%s@%s", fs, snap);
- if (zfs_dataset_exists(hdl, name, ZFS_TYPE_SNAPSHOT)) {
- zfs_handle_t *zhp = zfs_open(hdl, name, ZFS_TYPE_SNAPSHOT);
- if (zhp != NULL) {
- txg = zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG);
- zfs_close(zhp);
- }
- }
-
- return (txg);
-}
-
-/*
- * recursively generate nvlists describing datasets. See comment
- * for the data structure send_data_t above for description of contents
- * of the nvlist.
- */
-static int
-send_iterate_fs(zfs_handle_t *zhp, void *arg)
-{
- send_data_t *sd = arg;
- nvlist_t *nvfs, *nv;
- int rv = 0;
- uint64_t min_txg = 0, max_txg = 0;
- uint64_t parent_fromsnap_guid_save = sd->parent_fromsnap_guid;
- uint64_t fromsnap_txg_save = sd->fromsnap_txg;
- uint64_t tosnap_txg_save = sd->tosnap_txg;
- uint64_t txg = zhp->zfs_dmustats.dds_creation_txg;
- uint64_t guid = zhp->zfs_dmustats.dds_guid;
- uint64_t fromsnap_txg, tosnap_txg;
- char guidstring[64];
-
- fromsnap_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name, sd->fromsnap);
- if (fromsnap_txg != 0)
- sd->fromsnap_txg = fromsnap_txg;
-
- tosnap_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name, sd->tosnap);
- if (tosnap_txg != 0)
- sd->tosnap_txg = tosnap_txg;
-
- /*
- * on the send side, if the current dataset does not have tosnap,
- * perform two additional checks:
- *
- * - skip sending the current dataset if it was created later than
- * the parent tosnap
- * - return error if the current dataset was created earlier than
- * the parent tosnap
- */
- if (sd->tosnap != NULL && tosnap_txg == 0) {
- if (sd->tosnap_txg != 0 && txg > sd->tosnap_txg) {
- if (sd->verbose) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "skipping dataset %s: snapshot %s does "
- "not exist\n"), zhp->zfs_name, sd->tosnap);
- }
- } else {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "cannot send %s@%s%s: snapshot %s@%s does not "
- "exist\n"), sd->fsname, sd->tosnap, sd->recursive ?
- dgettext(TEXT_DOMAIN, " recursively") : "",
- zhp->zfs_name, sd->tosnap);
- rv = -1;
- }
- goto out;
- }
-
- nvfs = fnvlist_alloc();
- fnvlist_add_string(nvfs, "name", zhp->zfs_name);
- fnvlist_add_uint64(nvfs, "parentfromsnap",
- sd->parent_fromsnap_guid);
-
- if (zhp->zfs_dmustats.dds_origin[0]) {
- zfs_handle_t *origin = zfs_open(zhp->zfs_hdl,
- zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT);
- if (origin == NULL) {
- rv = -1;
- goto out;
- }
- VERIFY(0 == nvlist_add_uint64(nvfs, "origin",
- origin->zfs_dmustats.dds_guid));
- }
-
- /* iterate over props */
- VERIFY(0 == nvlist_alloc(&nv, NV_UNIQUE_NAME, 0));
- send_iterate_prop(zhp, nv);
- VERIFY(0 == nvlist_add_nvlist(nvfs, "props", nv));
- nvlist_free(nv);
-
- /* iterate over snaps, and set sd->parent_fromsnap_guid */
- if (!sd->replicate && fromsnap_txg != 0)
- min_txg = fromsnap_txg;
- if (!sd->replicate && tosnap_txg != 0)
- max_txg = tosnap_txg;
- sd->parent_fromsnap_guid = 0;
- VERIFY(0 == nvlist_alloc(&sd->parent_snaps, NV_UNIQUE_NAME, 0));
- VERIFY(0 == nvlist_alloc(&sd->snapprops, NV_UNIQUE_NAME, 0));
- (void) zfs_iter_snapshots_sorted(zhp, send_iterate_snap, sd,
- min_txg, max_txg);
- VERIFY(0 == nvlist_add_nvlist(nvfs, "snaps", sd->parent_snaps));
- VERIFY(0 == nvlist_add_nvlist(nvfs, "snapprops", sd->snapprops));
- fnvlist_free(sd->parent_snaps);
- fnvlist_free(sd->snapprops);
-
- /* add this fs to nvlist */
- (void) snprintf(guidstring, sizeof (guidstring),
- "0x%llx", (longlong_t)guid);
- VERIFY(0 == nvlist_add_nvlist(sd->fss, guidstring, nvfs));
- nvlist_free(nvfs);
-
- /* iterate over children */
- if (sd->recursive)
- rv = zfs_iter_filesystems(zhp, send_iterate_fs, sd);
-
-out:
- sd->parent_fromsnap_guid = parent_fromsnap_guid_save;
- sd->fromsnap_txg = fromsnap_txg_save;
- sd->tosnap_txg = tosnap_txg_save;
-
- zfs_close(zhp);
- return (rv);
-}
-
-static int
-gather_nvlist(libzfs_handle_t *hdl, const char *fsname, const char *fromsnap,
- const char *tosnap, boolean_t recursive, boolean_t verbose,
- boolean_t replicate, nvlist_t **nvlp, avl_tree_t **avlp)
-{
- zfs_handle_t *zhp;
- int error;
- uint64_t min_txg = 0, max_txg = 0;
- send_data_t sd = { 0 };
-
- zhp = zfs_open(hdl, fsname, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- if (zhp == NULL)
- return (EZFS_BADTYPE);
-
- VERIFY(0 == nvlist_alloc(&sd.fss, NV_UNIQUE_NAME, 0));
- sd.fsname = fsname;
- sd.fromsnap = fromsnap;
- sd.tosnap = tosnap;
- sd.recursive = recursive;
- sd.verbose = verbose;
- sd.replicate = replicate;
-
- if ((error = send_iterate_fs(zhp, &sd)) != 0) {
- nvlist_free(sd.fss);
- if (avlp != NULL)
- *avlp = NULL;
- *nvlp = NULL;
- return (error);
- }
-
- if (avlp != NULL && (*avlp = fsavl_create(sd.fss)) == NULL) {
- nvlist_free(sd.fss);
- *nvlp = NULL;
- return (EZFS_NOMEM);
- }
-
- *nvlp = sd.fss;
- return (0);
-}
-
-/*
- * Routines specific to "zfs send"
- */
-typedef struct send_dump_data {
- /* these are all just the short snapname (the part after the @) */
- const char *fromsnap;
- const char *tosnap;
- char prevsnap[ZFS_MAX_DATASET_NAME_LEN];
- uint64_t prevsnap_obj;
- boolean_t seenfrom, seento, replicate, doall, fromorigin;
- boolean_t verbose, dryrun, parsable, progress, embed_data, std_out;
- boolean_t progressastitle;
- boolean_t large_block, compress;
- int outfd;
- boolean_t err;
- nvlist_t *fss;
- nvlist_t *snapholds;
- avl_tree_t *fsavl;
- snapfilter_cb_t *filter_cb;
- void *filter_cb_arg;
- nvlist_t *debugnv;
- char holdtag[ZFS_MAX_DATASET_NAME_LEN];
- int cleanup_fd;
- uint64_t size;
-} send_dump_data_t;
-
-static int
-zfs_send_space(zfs_handle_t *zhp, const char *snapname, const char *from,
- enum lzc_send_flags flags, uint64_t *spacep)
-{
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- int error;
-
- assert(snapname != NULL);
- error = lzc_send_space(snapname, from, flags, spacep);
-
- if (error != 0) {
- char errbuf[1024];
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "warning: cannot estimate space for '%s'"), snapname);
-
- switch (error) {
- case EXDEV:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "not an earlier snapshot from the same fs"));
- return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
-
- case ENOENT:
- if (zfs_dataset_exists(hdl, snapname,
- ZFS_TYPE_SNAPSHOT)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "incremental source (%s) does not exist"),
- snapname);
- }
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
-
- case EDQUOT:
- case EFBIG:
- case EIO:
- case ENOLINK:
- case ENOSPC:
- case ENXIO:
- case EPIPE:
- case ERANGE:
- case EFAULT:
- case EROFS:
- case EINVAL:
- zfs_error_aux(hdl, strerror(error));
- return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
-
- default:
- return (zfs_standard_error(hdl, error, errbuf));
- }
- }
-
- return (0);
-}
-
-/*
- * Dumps a backup of the given snapshot (incremental from fromsnap if it's not
- * NULL) to the file descriptor specified by outfd.
- */
-static int
-dump_ioctl(zfs_handle_t *zhp, const char *fromsnap, uint64_t fromsnap_obj,
- boolean_t fromorigin, int outfd, enum lzc_send_flags flags,
- nvlist_t *debugnv)
-{
- zfs_cmd_t zc = { 0 };
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- nvlist_t *thisdbg;
-
- assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
- assert(fromsnap_obj == 0 || !fromorigin);
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- zc.zc_cookie = outfd;
- zc.zc_obj = fromorigin;
- zc.zc_sendobj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
- zc.zc_fromobj = fromsnap_obj;
- zc.zc_flags = flags;
-
- VERIFY(0 == nvlist_alloc(&thisdbg, NV_UNIQUE_NAME, 0));
- if (fromsnap && fromsnap[0] != '\0') {
- VERIFY(0 == nvlist_add_string(thisdbg,
- "fromsnap", fromsnap));
- }
-
- if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SEND, &zc) != 0) {
- char errbuf[1024];
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "warning: cannot send '%s'"), zhp->zfs_name);
-
- VERIFY(0 == nvlist_add_uint64(thisdbg, "error", errno));
- if (debugnv) {
- VERIFY(0 == nvlist_add_nvlist(debugnv,
- zhp->zfs_name, thisdbg));
- }
- nvlist_free(thisdbg);
-
- switch (errno) {
- case EXDEV:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "not an earlier snapshot from the same fs"));
- return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
-
- case ENOENT:
- if (zfs_dataset_exists(hdl, zc.zc_name,
- ZFS_TYPE_SNAPSHOT)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "incremental source (@%s) does not exist"),
- zc.zc_value);
- }
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
-
- case EDQUOT:
- case EFBIG:
- case EIO:
- case ENOLINK:
- case ENOSPC:
-#ifdef illumos
- case ENOSTR:
-#endif
- case ENXIO:
- case EPIPE:
- case ERANGE:
- case EFAULT:
- case EROFS:
- zfs_error_aux(hdl, strerror(errno));
- return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
-
- default:
- return (zfs_standard_error(hdl, errno, errbuf));
- }
- }
-
- if (debugnv)
- VERIFY(0 == nvlist_add_nvlist(debugnv, zhp->zfs_name, thisdbg));
- nvlist_free(thisdbg);
-
- return (0);
-}
-
-static void
-gather_holds(zfs_handle_t *zhp, send_dump_data_t *sdd)
-{
- assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
-
- /*
- * zfs_send() only sets snapholds for sends that need them,
- * e.g. replication and doall.
- */
- if (sdd->snapholds == NULL)
- return;
-
- fnvlist_add_string(sdd->snapholds, zhp->zfs_name, sdd->holdtag);
-}
-
-static void *
-send_progress_thread(void *arg)
-{
- progress_arg_t *pa = arg;
- zfs_cmd_t zc = { 0 };
- zfs_handle_t *zhp = pa->pa_zhp;
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- unsigned long long bytes, total;
- char buf[16];
- time_t t;
- struct tm *tm;
-
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
- if (!pa->pa_parsable && !pa->pa_astitle)
- (void) fprintf(stderr, "TIME SENT SNAPSHOT\n");
-
- /*
- * Print the progress from ZFS_IOC_SEND_PROGRESS every second.
- */
- for (;;) {
- (void) sleep(1);
-
- zc.zc_cookie = pa->pa_fd;
- if (zfs_ioctl(hdl, ZFS_IOC_SEND_PROGRESS, &zc) != 0)
- return ((void *)-1);
-
- (void) time(&t);
- tm = localtime(&t);
- bytes = zc.zc_cookie;
-
- if (pa->pa_astitle) {
- int pct;
- if (pa->pa_size > bytes)
- pct = 100 * bytes / pa->pa_size;
- else
- pct = 100;
-
- setproctitle("sending %s (%d%%: %llu/%llu)",
- zhp->zfs_name, pct, bytes, pa->pa_size);
- } else if (pa->pa_parsable) {
- (void) fprintf(stderr, "%02d:%02d:%02d\t%llu\t%s\n",
- tm->tm_hour, tm->tm_min, tm->tm_sec,
- bytes, zhp->zfs_name);
- } else {
- zfs_nicenum(bytes, buf, sizeof (buf));
- (void) fprintf(stderr, "%02d:%02d:%02d %5s %s\n",
- tm->tm_hour, tm->tm_min, tm->tm_sec,
- buf, zhp->zfs_name);
- }
- }
-}
-
-static void
-send_print_verbose(FILE *fout, const char *tosnap, const char *fromsnap,
- uint64_t size, boolean_t parsable)
-{
- if (parsable) {
- if (fromsnap != NULL) {
- (void) fprintf(fout, "incremental\t%s\t%s",
- fromsnap, tosnap);
- } else {
- (void) fprintf(fout, "full\t%s",
- tosnap);
- }
- } else {
- if (fromsnap != NULL) {
- if (strchr(fromsnap, '@') == NULL &&
- strchr(fromsnap, '#') == NULL) {
- (void) fprintf(fout, dgettext(TEXT_DOMAIN,
- "send from @%s to %s"),
- fromsnap, tosnap);
- } else {
- (void) fprintf(fout, dgettext(TEXT_DOMAIN,
- "send from %s to %s"),
- fromsnap, tosnap);
- }
- } else {
- (void) fprintf(fout, dgettext(TEXT_DOMAIN,
- "full send of %s"),
- tosnap);
- }
- }
-
- if (parsable) {
- (void) fprintf(fout, "\t%llu",
- (longlong_t)size);
- } else if (size != 0) {
- char buf[16];
- zfs_nicenum(size, buf, sizeof (buf));
- (void) fprintf(fout, dgettext(TEXT_DOMAIN,
- " estimated size is %s"), buf);
- }
- (void) fprintf(fout, "\n");
-}
-
-static int
-dump_snapshot(zfs_handle_t *zhp, void *arg)
-{
- send_dump_data_t *sdd = arg;
- progress_arg_t pa = { 0 };
- pthread_t tid;
- char *thissnap;
- enum lzc_send_flags flags = 0;
- int err;
- boolean_t isfromsnap, istosnap, fromorigin;
- boolean_t exclude = B_FALSE;
- FILE *fout = sdd->std_out ? stdout : stderr;
-
- err = 0;
- thissnap = strchr(zhp->zfs_name, '@') + 1;
- isfromsnap = (sdd->fromsnap != NULL &&
- strcmp(sdd->fromsnap, thissnap) == 0);
-
- if (!sdd->seenfrom && isfromsnap) {
- gather_holds(zhp, sdd);
- sdd->seenfrom = B_TRUE;
- (void) strcpy(sdd->prevsnap, thissnap);
- sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
- zfs_close(zhp);
- return (0);
- }
-
- if (sdd->seento || !sdd->seenfrom) {
- zfs_close(zhp);
- return (0);
- }
-
- istosnap = (strcmp(sdd->tosnap, thissnap) == 0);
- if (istosnap)
- sdd->seento = B_TRUE;
-
- if (sdd->large_block)
- flags |= LZC_SEND_FLAG_LARGE_BLOCK;
- if (sdd->embed_data)
- flags |= LZC_SEND_FLAG_EMBED_DATA;
- if (sdd->compress)
- flags |= LZC_SEND_FLAG_COMPRESS;
-
- if (!sdd->doall && !isfromsnap && !istosnap) {
- if (sdd->replicate) {
- char *snapname;
- nvlist_t *snapprops;
- /*
- * Filter out all intermediate snapshots except origin
- * snapshots needed to replicate clones.
- */
- nvlist_t *nvfs = fsavl_find(sdd->fsavl,
- zhp->zfs_dmustats.dds_guid, &snapname);
-
- VERIFY(0 == nvlist_lookup_nvlist(nvfs,
- "snapprops", &snapprops));
- VERIFY(0 == nvlist_lookup_nvlist(snapprops,
- thissnap, &snapprops));
- exclude = !nvlist_exists(snapprops, "is_clone_origin");
- } else {
- exclude = B_TRUE;
- }
- }
-
- /*
- * If a filter function exists, call it to determine whether
- * this snapshot will be sent.
- */
- if (exclude || (sdd->filter_cb != NULL &&
- sdd->filter_cb(zhp, sdd->filter_cb_arg) == B_FALSE)) {
- /*
- * This snapshot is filtered out. Don't send it, and don't
- * set prevsnap_obj, so it will be as if this snapshot didn't
- * exist, and the next accepted snapshot will be sent as
- * an incremental from the last accepted one, or as the
- * first (and full) snapshot in the case of a replication,
- * non-incremental send.
- */
- zfs_close(zhp);
- return (0);
- }
-
- gather_holds(zhp, sdd);
- fromorigin = sdd->prevsnap[0] == '\0' &&
- (sdd->fromorigin || sdd->replicate);
-
- if (sdd->verbose || sdd->progress) {
- uint64_t size = 0;
- char fromds[ZFS_MAX_DATASET_NAME_LEN];
-
- if (sdd->prevsnap[0] != '\0') {
- (void) strlcpy(fromds, zhp->zfs_name, sizeof (fromds));
- *(strchr(fromds, '@') + 1) = '\0';
- (void) strlcat(fromds, sdd->prevsnap, sizeof (fromds));
- }
- if (zfs_send_space(zhp, zhp->zfs_name,
- sdd->prevsnap[0] ? fromds : NULL, flags, &size) != 0) {
- size = 0; /* cannot estimate send space */
- } else {
- send_print_verbose(fout, zhp->zfs_name,
- sdd->prevsnap[0] ? sdd->prevsnap : NULL,
- size, sdd->parsable);
- }
- sdd->size += size;
- }
-
- if (!sdd->dryrun) {
- /*
- * If progress reporting is requested, spawn a new thread to
- * poll ZFS_IOC_SEND_PROGRESS at a regular interval.
- */
- if (sdd->progress) {
- pa.pa_zhp = zhp;
- pa.pa_fd = sdd->outfd;
- pa.pa_parsable = sdd->parsable;
- pa.pa_size = sdd->size;
- pa.pa_astitle = sdd->progressastitle;
-
- if ((err = pthread_create(&tid, NULL,
- send_progress_thread, &pa)) != 0) {
- zfs_close(zhp);
- return (err);
- }
- }
-
- err = dump_ioctl(zhp, sdd->prevsnap, sdd->prevsnap_obj,
- fromorigin, sdd->outfd, flags, sdd->debugnv);
-
- if (sdd->progress) {
- (void) pthread_cancel(tid);
- (void) pthread_join(tid, NULL);
- }
- }
-
- (void) strcpy(sdd->prevsnap, thissnap);
- sdd->prevsnap_obj = zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID);
- zfs_close(zhp);
- return (err);
-}
-
-static int
-dump_filesystem(zfs_handle_t *zhp, void *arg)
-{
- int rv = 0;
- uint64_t min_txg = 0, max_txg = 0;
- send_dump_data_t *sdd = arg;
- boolean_t missingfrom = B_FALSE;
- zfs_cmd_t zc = { 0 };
-
- (void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
- zhp->zfs_name, sdd->tosnap);
- if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "WARNING: could not send %s@%s: does not exist\n"),
- zhp->zfs_name, sdd->tosnap);
- sdd->err = B_TRUE;
- return (0);
- }
-
- if (sdd->replicate && sdd->fromsnap) {
- /*
- * If this fs does not have fromsnap, and we're doing
- * recursive, we need to send a full stream from the
- * beginning (or an incremental from the origin if this
- * is a clone). If we're doing non-recursive, then let
- * them get the error.
- */
- (void) snprintf(zc.zc_name, sizeof (zc.zc_name), "%s@%s",
- zhp->zfs_name, sdd->fromsnap);
- if (ioctl(zhp->zfs_hdl->libzfs_fd,
- ZFS_IOC_OBJSET_STATS, &zc) != 0) {
- missingfrom = B_TRUE;
- }
- }
-
- sdd->seenfrom = sdd->seento = sdd->prevsnap[0] = 0;
- sdd->prevsnap_obj = 0;
- if (sdd->fromsnap == NULL || missingfrom)
- sdd->seenfrom = B_TRUE;
-
- if (!sdd->replicate && sdd->fromsnap != NULL)
- min_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name,
- sdd->fromsnap);
- if (!sdd->replicate && sdd->tosnap != NULL)
- max_txg = get_snap_txg(zhp->zfs_hdl, zhp->zfs_name,
- sdd->tosnap);
-
- rv = zfs_iter_snapshots_sorted(zhp, dump_snapshot, arg,
- min_txg, max_txg);
- if (!sdd->seenfrom) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "WARNING: could not send %s@%s:\n"
- "incremental source (%s@%s) does not exist\n"),
- zhp->zfs_name, sdd->tosnap,
- zhp->zfs_name, sdd->fromsnap);
- sdd->err = B_TRUE;
- } else if (!sdd->seento) {
- if (sdd->fromsnap) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "WARNING: could not send %s@%s:\n"
- "incremental source (%s@%s) "
- "is not earlier than it\n"),
- zhp->zfs_name, sdd->tosnap,
- zhp->zfs_name, sdd->fromsnap);
- } else {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "WARNING: "
- "could not send %s@%s: does not exist\n"),
- zhp->zfs_name, sdd->tosnap);
- }
- sdd->err = B_TRUE;
- }
-
- return (rv);
-}
-
-static int
-dump_filesystems(zfs_handle_t *rzhp, void *arg)
-{
- send_dump_data_t *sdd = arg;
- nvpair_t *fspair;
- boolean_t needagain, progress;
-
- if (!sdd->replicate)
- return (dump_filesystem(rzhp, sdd));
-
- /* Mark the clone origin snapshots. */
- for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
- fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
- nvlist_t *nvfs;
- uint64_t origin_guid = 0;
-
- VERIFY(0 == nvpair_value_nvlist(fspair, &nvfs));
- (void) nvlist_lookup_uint64(nvfs, "origin", &origin_guid);
- if (origin_guid != 0) {
- char *snapname;
- nvlist_t *origin_nv = fsavl_find(sdd->fsavl,
- origin_guid, &snapname);
- if (origin_nv != NULL) {
- nvlist_t *snapprops;
- VERIFY(0 == nvlist_lookup_nvlist(origin_nv,
- "snapprops", &snapprops));
- VERIFY(0 == nvlist_lookup_nvlist(snapprops,
- snapname, &snapprops));
- VERIFY(0 == nvlist_add_boolean(
- snapprops, "is_clone_origin"));
- }
- }
- }
-again:
- needagain = progress = B_FALSE;
- for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
- fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
- nvlist_t *fslist, *parent_nv;
- char *fsname;
- zfs_handle_t *zhp;
- int err;
- uint64_t origin_guid = 0;
- uint64_t parent_guid = 0;
-
- VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
- if (nvlist_lookup_boolean(fslist, "sent") == 0)
- continue;
-
- VERIFY(nvlist_lookup_string(fslist, "name", &fsname) == 0);
- (void) nvlist_lookup_uint64(fslist, "origin", &origin_guid);
- (void) nvlist_lookup_uint64(fslist, "parentfromsnap",
- &parent_guid);
-
- if (parent_guid != 0) {
- parent_nv = fsavl_find(sdd->fsavl, parent_guid, NULL);
- if (!nvlist_exists(parent_nv, "sent")) {
- /* parent has not been sent; skip this one */
- needagain = B_TRUE;
- continue;
- }
- }
-
- if (origin_guid != 0) {
- nvlist_t *origin_nv = fsavl_find(sdd->fsavl,
- origin_guid, NULL);
- if (origin_nv != NULL &&
- !nvlist_exists(origin_nv, "sent")) {
- /*
- * origin has not been sent yet;
- * skip this clone.
- */
- needagain = B_TRUE;
- continue;
- }
- }
-
- zhp = zfs_open(rzhp->zfs_hdl, fsname, ZFS_TYPE_DATASET);
- if (zhp == NULL)
- return (-1);
- err = dump_filesystem(zhp, sdd);
- VERIFY(nvlist_add_boolean(fslist, "sent") == 0);
- progress = B_TRUE;
- zfs_close(zhp);
- if (err)
- return (err);
- }
- if (needagain) {
- assert(progress);
- goto again;
- }
-
- /* clean out the sent flags in case we reuse this fss */
- for (fspair = nvlist_next_nvpair(sdd->fss, NULL); fspair;
- fspair = nvlist_next_nvpair(sdd->fss, fspair)) {
- nvlist_t *fslist;
-
- VERIFY(nvpair_value_nvlist(fspair, &fslist) == 0);
- (void) nvlist_remove_all(fslist, "sent");
- }
-
- return (0);
-}
-
-nvlist_t *
-zfs_send_resume_token_to_nvlist(libzfs_handle_t *hdl, const char *token)
-{
- unsigned int version;
- int nread;
- unsigned long long checksum, packed_len;
-
- /*
- * Decode token header, which is:
- * <token version>-<checksum of payload>-<uncompressed payload length>
- * Note that the only supported token version is 1.
- */
- nread = sscanf(token, "%u-%llx-%llx-",
- &version, &checksum, &packed_len);
- if (nread != 3) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "resume token is corrupt (invalid format)"));
- return (NULL);
- }
-
- if (version != ZFS_SEND_RESUME_TOKEN_VERSION) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "resume token is corrupt (invalid version %u)"),
- version);
- return (NULL);
- }
-
- /* convert hexadecimal representation to binary */
- token = strrchr(token, '-') + 1;
- int len = strlen(token) / 2;
- unsigned char *compressed = zfs_alloc(hdl, len);
- for (int i = 0; i < len; i++) {
- nread = sscanf(token + i * 2, "%2hhx", compressed + i);
- if (nread != 1) {
- free(compressed);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "resume token is corrupt "
- "(payload is not hex-encoded)"));
- return (NULL);
- }
- }
-
- /* verify checksum */
- zio_cksum_t cksum;
- fletcher_4_native(compressed, len, NULL, &cksum);
- if (cksum.zc_word[0] != checksum) {
- free(compressed);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "resume token is corrupt (incorrect checksum)"));
- return (NULL);
- }
-
- /* uncompress */
- void *packed = zfs_alloc(hdl, packed_len);
- uLongf packed_len_long = packed_len;
- if (uncompress(packed, &packed_len_long, compressed, len) != Z_OK ||
- packed_len_long != packed_len) {
- free(packed);
- free(compressed);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "resume token is corrupt (decompression failed)"));
- return (NULL);
- }
-
- /* unpack nvlist */
- nvlist_t *nv;
- int error = nvlist_unpack(packed, packed_len, &nv, KM_SLEEP);
- free(packed);
- free(compressed);
- if (error != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "resume token is corrupt (nvlist_unpack failed)"));
- return (NULL);
- }
- return (nv);
-}
-
-int
-zfs_send_resume(libzfs_handle_t *hdl, sendflags_t *flags, int outfd,
- const char *resume_token)
-{
- char errbuf[1024];
- char *toname;
- char *fromname = NULL;
- uint64_t resumeobj, resumeoff, toguid, fromguid, bytes;
- zfs_handle_t *zhp;
- int error = 0;
- char name[ZFS_MAX_DATASET_NAME_LEN];
- enum lzc_send_flags lzc_flags = 0;
- uint64_t size = 0;
- FILE *fout = (flags->verbose && flags->dryrun) ? stdout : stderr;
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot resume send"));
-
- nvlist_t *resume_nvl =
- zfs_send_resume_token_to_nvlist(hdl, resume_token);
- if (resume_nvl == NULL) {
- /*
- * zfs_error_aux has already been set by
- * zfs_send_resume_token_to_nvlist
- */
- return (zfs_error(hdl, EZFS_FAULT, errbuf));
- }
- if (flags->verbose) {
- (void) fprintf(fout, dgettext(TEXT_DOMAIN,
- "resume token contents:\n"));
- nvlist_print(fout, resume_nvl);
- }
-
- if (nvlist_lookup_string(resume_nvl, "toname", &toname) != 0 ||
- nvlist_lookup_uint64(resume_nvl, "object", &resumeobj) != 0 ||
- nvlist_lookup_uint64(resume_nvl, "offset", &resumeoff) != 0 ||
- nvlist_lookup_uint64(resume_nvl, "bytes", &bytes) != 0 ||
- nvlist_lookup_uint64(resume_nvl, "toguid", &toguid) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "resume token is corrupt"));
- return (zfs_error(hdl, EZFS_FAULT, errbuf));
- }
- fromguid = 0;
- (void) nvlist_lookup_uint64(resume_nvl, "fromguid", &fromguid);
-
- if (flags->largeblock || nvlist_exists(resume_nvl, "largeblockok"))
- lzc_flags |= LZC_SEND_FLAG_LARGE_BLOCK;
- if (flags->embed_data || nvlist_exists(resume_nvl, "embedok"))
- lzc_flags |= LZC_SEND_FLAG_EMBED_DATA;
- if (flags->compress || nvlist_exists(resume_nvl, "compressok"))
- lzc_flags |= LZC_SEND_FLAG_COMPRESS;
-
- if (guid_to_name(hdl, toname, toguid, B_FALSE, name) != 0) {
- if (zfs_dataset_exists(hdl, toname, ZFS_TYPE_DATASET)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' is no longer the same snapshot used in "
- "the initial send"), toname);
- } else {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' used in the initial send no longer exists"),
- toname);
- }
- return (zfs_error(hdl, EZFS_BADPATH, errbuf));
- }
- zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
- if (zhp == NULL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "unable to access '%s'"), name);
- return (zfs_error(hdl, EZFS_BADPATH, errbuf));
- }
-
- if (fromguid != 0) {
- if (guid_to_name(hdl, toname, fromguid, B_TRUE, name) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "incremental source %#llx no longer exists"),
- (longlong_t)fromguid);
- return (zfs_error(hdl, EZFS_BADPATH, errbuf));
- }
- fromname = name;
- }
-
- if (flags->progress || flags->verbose) {
- error = lzc_send_space(zhp->zfs_name, fromname,
- lzc_flags, &size);
- if (error == 0)
- size = MAX(0, (int64_t)(size - bytes));
- }
- if (flags->verbose) {
- send_print_verbose(fout, zhp->zfs_name, fromname,
- size, flags->parsable);
- }
-
- if (!flags->dryrun) {
- progress_arg_t pa = { 0 };
- pthread_t tid;
- /*
- * If progress reporting is requested, spawn a new thread to
- * poll ZFS_IOC_SEND_PROGRESS at a regular interval.
- */
- if (flags->progress) {
- pa.pa_zhp = zhp;
- pa.pa_fd = outfd;
- pa.pa_parsable = flags->parsable;
- pa.pa_size = size;
- pa.pa_astitle = flags->progressastitle;
-
- error = pthread_create(&tid, NULL,
- send_progress_thread, &pa);
- if (error != 0) {
- zfs_close(zhp);
- return (error);
- }
- }
-
- error = lzc_send_resume(zhp->zfs_name, fromname, outfd,
- lzc_flags, resumeobj, resumeoff);
-
- if (flags->progress) {
- (void) pthread_cancel(tid);
- (void) pthread_join(tid, NULL);
- }
-
- char errbuf[1024];
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "warning: cannot send '%s'"), zhp->zfs_name);
-
- zfs_close(zhp);
-
- switch (error) {
- case 0:
- return (0);
- case EXDEV:
- case ENOENT:
- case EDQUOT:
- case EFBIG:
- case EIO:
- case ENOLINK:
- case ENOSPC:
-#ifdef illumos
- case ENOSTR:
-#endif
- case ENXIO:
- case EPIPE:
- case ERANGE:
- case EFAULT:
- case EROFS:
- zfs_error_aux(hdl, strerror(errno));
- return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
-
- default:
- return (zfs_standard_error(hdl, errno, errbuf));
- }
- }
-
-
- zfs_close(zhp);
-
- return (error);
-}
-
-/*
- * Generate a send stream for the dataset identified by the argument zhp.
- *
- * The content of the send stream is the snapshot identified by
- * 'tosnap'. Incremental streams are requested in two ways:
- * - from the snapshot identified by "fromsnap" (if non-null) or
- * - from the origin of the dataset identified by zhp, which must
- * be a clone. In this case, "fromsnap" is null and "fromorigin"
- * is TRUE.
- *
- * The send stream is recursive (i.e. dumps a hierarchy of snapshots) and
- * uses a special header (with a hdrtype field of DMU_COMPOUNDSTREAM)
- * if "replicate" is set. If "doall" is set, dump all the intermediate
- * snapshots. The DMU_COMPOUNDSTREAM header is used in the "doall"
- * case too. If "props" is set, send properties.
- */
-int
-zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
- sendflags_t *flags, int outfd, snapfilter_cb_t filter_func,
- void *cb_arg, nvlist_t **debugnvp)
-{
- char errbuf[1024];
- send_dump_data_t sdd = { 0 };
- int err = 0;
- nvlist_t *fss = NULL;
- avl_tree_t *fsavl = NULL;
- static uint64_t holdseq;
- int spa_version;
- pthread_t tid = 0;
- int pipefd[2];
- dedup_arg_t dda = { 0 };
- int featureflags = 0;
- FILE *fout;
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot send '%s'"), zhp->zfs_name);
-
- if (fromsnap && fromsnap[0] == '\0') {
- zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
- "zero-length incremental source"));
- return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
- }
-
- if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM) {
- uint64_t version;
- version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
- if (version >= ZPL_VERSION_SA) {
- featureflags |= DMU_BACKUP_FEATURE_SA_SPILL;
- }
- }
-
- if (flags->dedup && !flags->dryrun) {
- featureflags |= (DMU_BACKUP_FEATURE_DEDUP |
- DMU_BACKUP_FEATURE_DEDUPPROPS);
- if ((err = pipe(pipefd)) != 0) {
- zfs_error_aux(zhp->zfs_hdl, strerror(errno));
- return (zfs_error(zhp->zfs_hdl, EZFS_PIPEFAILED,
- errbuf));
- }
- dda.outputfd = outfd;
- dda.inputfd = pipefd[1];
- dda.dedup_hdl = zhp->zfs_hdl;
- if ((err = pthread_create(&tid, NULL, cksummer, &dda)) != 0) {
- (void) close(pipefd[0]);
- (void) close(pipefd[1]);
- zfs_error_aux(zhp->zfs_hdl, strerror(errno));
- return (zfs_error(zhp->zfs_hdl,
- EZFS_THREADCREATEFAILED, errbuf));
- }
- }
-
- if (flags->replicate || flags->doall || flags->props) {
- dmu_replay_record_t drr = { 0 };
- char *packbuf = NULL;
- size_t buflen = 0;
- zio_cksum_t zc = { 0 };
-
- if (flags->replicate || flags->props) {
- nvlist_t *hdrnv;
-
- VERIFY(0 == nvlist_alloc(&hdrnv, NV_UNIQUE_NAME, 0));
- if (fromsnap) {
- VERIFY(0 == nvlist_add_string(hdrnv,
- "fromsnap", fromsnap));
- }
- VERIFY(0 == nvlist_add_string(hdrnv, "tosnap", tosnap));
- if (!flags->replicate) {
- VERIFY(0 == nvlist_add_boolean(hdrnv,
- "not_recursive"));
- }
-
- err = gather_nvlist(zhp->zfs_hdl, zhp->zfs_name,
- fromsnap, tosnap, flags->replicate, flags->verbose,
- flags->replicate, &fss, &fsavl);
- if (err)
- goto err_out;
- VERIFY(0 == nvlist_add_nvlist(hdrnv, "fss", fss));
- err = nvlist_pack(hdrnv, &packbuf, &buflen,
- NV_ENCODE_XDR, 0);
- if (debugnvp)
- *debugnvp = hdrnv;
- else
- nvlist_free(hdrnv);
- if (err)
- goto stderr_out;
- }
-
- if (!flags->dryrun) {
- /* write first begin record */
- drr.drr_type = DRR_BEGIN;
- drr.drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC;
- DMU_SET_STREAM_HDRTYPE(drr.drr_u.drr_begin.
- drr_versioninfo, DMU_COMPOUNDSTREAM);
- DMU_SET_FEATUREFLAGS(drr.drr_u.drr_begin.
- drr_versioninfo, featureflags);
- (void) snprintf(drr.drr_u.drr_begin.drr_toname,
- sizeof (drr.drr_u.drr_begin.drr_toname),
- "%s@%s", zhp->zfs_name, tosnap);
- drr.drr_payloadlen = buflen;
-
- err = dump_record(&drr, packbuf, buflen, &zc, outfd);
- free(packbuf);
- if (err != 0)
- goto stderr_out;
-
- /* write end record */
- bzero(&drr, sizeof (drr));
- drr.drr_type = DRR_END;
- drr.drr_u.drr_end.drr_checksum = zc;
- err = write(outfd, &drr, sizeof (drr));
- if (err == -1) {
- err = errno;
- goto stderr_out;
- }
-
- err = 0;
- }
- }
-
- /* dump each stream */
- sdd.fromsnap = fromsnap;
- sdd.tosnap = tosnap;
- if (tid != 0)
- sdd.outfd = pipefd[0];
- else
- sdd.outfd = outfd;
- sdd.replicate = flags->replicate;
- sdd.doall = flags->doall;
- sdd.fromorigin = flags->fromorigin;
- sdd.fss = fss;
- sdd.fsavl = fsavl;
- sdd.verbose = flags->verbose;
- sdd.parsable = flags->parsable;
- sdd.progress = flags->progress;
- sdd.progressastitle = flags->progressastitle;
- sdd.dryrun = flags->dryrun;
- sdd.large_block = flags->largeblock;
- sdd.embed_data = flags->embed_data;
- sdd.compress = flags->compress;
- sdd.filter_cb = filter_func;
- sdd.filter_cb_arg = cb_arg;
- if (debugnvp)
- sdd.debugnv = *debugnvp;
- if (sdd.verbose && sdd.dryrun)
- sdd.std_out = B_TRUE;
- fout = sdd.std_out ? stdout : stderr;
-
- /*
- * Some flags require that we place user holds on the datasets that are
- * being sent so they don't get destroyed during the send. We can skip
- * this step if the pool is imported read-only since the datasets cannot
- * be destroyed.
- */
- if (!flags->dryrun && !zpool_get_prop_int(zfs_get_pool_handle(zhp),
- ZPOOL_PROP_READONLY, NULL) &&
- zfs_spa_version(zhp, &spa_version) == 0 &&
- spa_version >= SPA_VERSION_USERREFS &&
- (flags->doall || flags->replicate)) {
- ++holdseq;
- (void) snprintf(sdd.holdtag, sizeof (sdd.holdtag),
- ".send-%d-%llu", getpid(), (u_longlong_t)holdseq);
- sdd.cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
- if (sdd.cleanup_fd < 0) {
- err = errno;
- goto stderr_out;
- }
- sdd.snapholds = fnvlist_alloc();
- } else {
- sdd.cleanup_fd = -1;
- sdd.snapholds = NULL;
- }
- if (flags->progress || flags->verbose || sdd.snapholds != NULL) {
- /*
- * Do a verbose no-op dry run to get all the verbose output
- * or to gather snapshot hold's before generating any data,
- * then do a non-verbose real run to generate the streams.
- */
- sdd.dryrun = B_TRUE;
- err = dump_filesystems(zhp, &sdd);
-
- if (err != 0)
- goto stderr_out;
-
- if (flags->verbose) {
- if (flags->parsable) {
- (void) fprintf(fout, "size\t%llu\n",
- (longlong_t)sdd.size);
- } else {
- char buf[16];
- zfs_nicenum(sdd.size, buf, sizeof (buf));
- (void) fprintf(fout, dgettext(TEXT_DOMAIN,
- "total estimated size is %s\n"), buf);
- }
- }
-
- /* Ensure no snaps found is treated as an error. */
- if (!sdd.seento) {
- err = ENOENT;
- goto err_out;
- }
-
- /* Skip the second run if dryrun was requested. */
- if (flags->dryrun)
- goto err_out;
-
- if (sdd.snapholds != NULL) {
- err = zfs_hold_nvl(zhp, sdd.cleanup_fd, sdd.snapholds);
- if (err != 0)
- goto stderr_out;
-
- fnvlist_free(sdd.snapholds);
- sdd.snapholds = NULL;
- }
-
- sdd.dryrun = B_FALSE;
- sdd.verbose = B_FALSE;
- }
-
- err = dump_filesystems(zhp, &sdd);
- fsavl_destroy(fsavl);
- nvlist_free(fss);
-
- /* Ensure no snaps found is treated as an error. */
- if (err == 0 && !sdd.seento)
- err = ENOENT;
-
- if (tid != 0) {
- if (err != 0)
- (void) pthread_cancel(tid);
- (void) close(pipefd[0]);
- (void) pthread_join(tid, NULL);
- }
-
- if (sdd.cleanup_fd != -1) {
- VERIFY(0 == close(sdd.cleanup_fd));
- sdd.cleanup_fd = -1;
- }
-
- if (!flags->dryrun && (flags->replicate || flags->doall ||
- flags->props)) {
- /*
- * write final end record. NB: want to do this even if
- * there was some error, because it might not be totally
- * failed.
- */
- dmu_replay_record_t drr = { 0 };
- drr.drr_type = DRR_END;
- if (write(outfd, &drr, sizeof (drr)) == -1) {
- return (zfs_standard_error(zhp->zfs_hdl,
- errno, errbuf));
- }
- }
-
- return (err || sdd.err);
-
-stderr_out:
- err = zfs_standard_error(zhp->zfs_hdl, err, errbuf);
-err_out:
- fsavl_destroy(fsavl);
- nvlist_free(fss);
- fnvlist_free(sdd.snapholds);
-
- if (sdd.cleanup_fd != -1)
- VERIFY(0 == close(sdd.cleanup_fd));
- if (tid != 0) {
- (void) pthread_cancel(tid);
- (void) close(pipefd[0]);
- (void) pthread_join(tid, NULL);
- }
- return (err);
-}
-
-int
-zfs_send_one(zfs_handle_t *zhp, const char *from, int fd, sendflags_t flags)
-{
- int err = 0;
- libzfs_handle_t *hdl = zhp->zfs_hdl;
- enum lzc_send_flags lzc_flags = 0;
- FILE *fout = (flags.verbose && flags.dryrun) ? stdout : stderr;
- char errbuf[1024];
-
- if (flags.largeblock)
- lzc_flags |= LZC_SEND_FLAG_LARGE_BLOCK;
- if (flags.embed_data)
- lzc_flags |= LZC_SEND_FLAG_EMBED_DATA;
- if (flags.compress)
- lzc_flags |= LZC_SEND_FLAG_COMPRESS;
-
- if (flags.verbose) {
- uint64_t size = 0;
- err = lzc_send_space(zhp->zfs_name, from, lzc_flags, &size);
- if (err == 0) {
- send_print_verbose(fout, zhp->zfs_name, from, size,
- flags.parsable);
- if (flags.parsable) {
- (void) fprintf(fout, "size\t%llu\n",
- (longlong_t)size);
- } else {
- char buf[16];
- zfs_nicenum(size, buf, sizeof (buf));
- (void) fprintf(fout, dgettext(TEXT_DOMAIN,
- "total estimated size is %s\n"), buf);
- }
- } else {
- (void) fprintf(stderr, "Cannot estimate send size: "
- "%s\n", strerror(errno));
- }
- }
-
- if (flags.dryrun)
- return (err);
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "warning: cannot send '%s'"), zhp->zfs_name);
-
- err = lzc_send(zhp->zfs_name, from, fd, lzc_flags);
- if (err != 0) {
- switch (errno) {
- case EXDEV:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "not an earlier snapshot from the same fs"));
- return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
-
- case ENOENT:
- case ESRCH:
- if (lzc_exists(zhp->zfs_name)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "incremental source (%s) does not exist"),
- from);
- }
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
-
- case EBUSY:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "target is busy; if a filesystem, "
- "it must not be mounted"));
- return (zfs_error(hdl, EZFS_BUSY, errbuf));
-
- case EDQUOT:
- case EFBIG:
- case EIO:
- case ENOLINK:
- case ENOSPC:
-#ifdef illumos
- case ENOSTR:
-#endif
- case ENXIO:
- case EPIPE:
- case ERANGE:
- case EFAULT:
- case EROFS:
- zfs_error_aux(hdl, strerror(errno));
- return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
-
- default:
- return (zfs_standard_error(hdl, errno, errbuf));
- }
- }
- return (err != 0);
-}
-
-/*
- * Routines specific to "zfs recv"
- */
-
-static int
-recv_read(libzfs_handle_t *hdl, int fd, void *buf, int ilen,
- boolean_t byteswap, zio_cksum_t *zc)
-{
- char *cp = buf;
- int rv;
- int len = ilen;
-
- assert(ilen <= SPA_MAXBLOCKSIZE);
-
- do {
- rv = read(fd, cp, len);
- cp += rv;
- len -= rv;
- } while (rv > 0);
-
- if (rv < 0 || len != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "failed to read from stream"));
- return (zfs_error(hdl, EZFS_BADSTREAM, dgettext(TEXT_DOMAIN,
- "cannot receive")));
- }
-
- if (zc) {
- if (byteswap)
- (void) fletcher_4_incremental_byteswap(buf, ilen, zc);
- else
- (void) fletcher_4_incremental_native(buf, ilen, zc);
- }
- return (0);
-}
-
-static int
-recv_read_nvlist(libzfs_handle_t *hdl, int fd, int len, nvlist_t **nvp,
- boolean_t byteswap, zio_cksum_t *zc)
-{
- char *buf;
- int err;
-
- buf = zfs_alloc(hdl, len);
- if (buf == NULL)
- return (ENOMEM);
-
- err = recv_read(hdl, fd, buf, len, byteswap, zc);
- if (err != 0) {
- free(buf);
- return (err);
- }
-
- err = nvlist_unpack(buf, len, nvp, 0);
- free(buf);
- if (err != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
- "stream (malformed nvlist)"));
- return (EINVAL);
- }
- return (0);
-}
-
-static int
-recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
- int baselen, char *newname, recvflags_t *flags)
-{
- static int seq;
- int err;
- prop_changelist_t *clp;
- zfs_handle_t *zhp;
-
- zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
- if (zhp == NULL)
- return (-1);
- clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
- flags->force ? MS_FORCE : 0);
- zfs_close(zhp);
- if (clp == NULL)
- return (-1);
- err = changelist_prefix(clp);
- if (err)
- return (err);
-
- if (tryname) {
- (void) strcpy(newname, tryname);
- if (flags->verbose) {
- (void) printf("attempting rename %s to %s\n",
- name, newname);
- }
- err = lzc_rename(name, newname);
- if (err == 0)
- changelist_rename(clp, name, tryname);
- } else {
- err = ENOENT;
- }
-
- if (err != 0 && strncmp(name + baselen, "recv-", 5) != 0) {
- seq++;
-
- (void) snprintf(newname, ZFS_MAX_DATASET_NAME_LEN,
- "%.*srecv-%u-%u", baselen, name, getpid(), seq);
- if (flags->verbose) {
- (void) printf("failed - trying rename %s to %s\n",
- name, newname);
- }
- err = lzc_rename(name, newname);
- if (err == 0)
- changelist_rename(clp, name, newname);
- if (err && flags->verbose) {
- (void) printf("failed (%u) - "
- "will try again on next pass\n", errno);
- }
- err = EAGAIN;
- } else if (flags->verbose) {
- if (err == 0)
- (void) printf("success\n");
- else
- (void) printf("failed (%u)\n", errno);
- }
-
- (void) changelist_postfix(clp);
- changelist_free(clp);
-
- return (err);
-}
-
-static int
-recv_destroy(libzfs_handle_t *hdl, const char *name, int baselen,
- char *newname, recvflags_t *flags)
-{
- int err = 0;
- prop_changelist_t *clp;
- zfs_handle_t *zhp;
- boolean_t defer = B_FALSE;
- int spa_version;
-
- zhp = zfs_open(hdl, name, ZFS_TYPE_DATASET);
- if (zhp == NULL)
- return (-1);
- clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
- flags->force ? MS_FORCE : 0);
- if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
- zfs_spa_version(zhp, &spa_version) == 0 &&
- spa_version >= SPA_VERSION_USERREFS)
- defer = B_TRUE;
- zfs_close(zhp);
- if (clp == NULL)
- return (-1);
- err = changelist_prefix(clp);
- if (err)
- return (err);
-
- if (flags->verbose)
- (void) printf("attempting destroy %s\n", name);
- if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
- nvlist_t *nv = fnvlist_alloc();
- fnvlist_add_boolean(nv, name);
- err = lzc_destroy_snaps(nv, defer, NULL);
- fnvlist_free(nv);
- } else {
- err = lzc_destroy(name);
- }
- if (err == 0) {
- if (flags->verbose)
- (void) printf("success\n");
- changelist_remove(clp, name);
- }
-
- (void) changelist_postfix(clp);
- changelist_free(clp);
-
- /*
- * Deferred destroy might destroy the snapshot or only mark it to be
- * destroyed later, and it returns success in either case.
- */
- if (err != 0 || (defer && zfs_dataset_exists(hdl, name,
- ZFS_TYPE_SNAPSHOT))) {
- err = recv_rename(hdl, name, NULL, baselen, newname, flags);
- }
-
- return (err);
-}
-
-typedef struct guid_to_name_data {
- uint64_t guid;
- boolean_t bookmark_ok;
- char *name;
- char *skip;
-} guid_to_name_data_t;
-
-static int
-guid_to_name_cb(zfs_handle_t *zhp, void *arg)
-{
- guid_to_name_data_t *gtnd = arg;
- const char *slash;
- int err;
-
- if (gtnd->skip != NULL &&
- (slash = strrchr(zhp->zfs_name, '/')) != NULL &&
- strcmp(slash + 1, gtnd->skip) == 0) {
- zfs_close(zhp);
- return (0);
- }
-
- if (zfs_prop_get_int(zhp, ZFS_PROP_GUID) == gtnd->guid) {
- (void) strcpy(gtnd->name, zhp->zfs_name);
- zfs_close(zhp);
- return (EEXIST);
- }
-
- err = zfs_iter_children(zhp, guid_to_name_cb, gtnd);
- if (err != EEXIST && gtnd->bookmark_ok)
- err = zfs_iter_bookmarks(zhp, guid_to_name_cb, gtnd);
- zfs_close(zhp);
- return (err);
-}
-
-/*
- * Attempt to find the local dataset associated with this guid. In the case of
- * multiple matches, we attempt to find the "best" match by searching
- * progressively larger portions of the hierarchy. This allows one to send a
- * tree of datasets individually and guarantee that we will find the source
- * guid within that hierarchy, even if there are multiple matches elsewhere.
- */
-static int
-guid_to_name(libzfs_handle_t *hdl, const char *parent, uint64_t guid,
- boolean_t bookmark_ok, char *name)
-{
- char pname[ZFS_MAX_DATASET_NAME_LEN];
- guid_to_name_data_t gtnd;
-
- gtnd.guid = guid;
- gtnd.bookmark_ok = bookmark_ok;
- gtnd.name = name;
- gtnd.skip = NULL;
-
- /*
- * Search progressively larger portions of the hierarchy, starting
- * with the filesystem specified by 'parent'. This will
- * select the "most local" version of the origin snapshot in the case
- * that there are multiple matching snapshots in the system.
- */
- (void) strlcpy(pname, parent, sizeof (pname));
- char *cp = strrchr(pname, '@');
- if (cp == NULL)
- cp = strchr(pname, '\0');
- for (; cp != NULL; cp = strrchr(pname, '/')) {
- /* Chop off the last component and open the parent */
- *cp = '\0';
- zfs_handle_t *zhp = make_dataset_handle(hdl, pname);
-
- if (zhp == NULL)
- continue;
- int err = guid_to_name_cb(zfs_handle_dup(zhp), &gtnd);
- if (err != EEXIST)
- err = zfs_iter_children(zhp, guid_to_name_cb, &gtnd);
- if (err != EEXIST && bookmark_ok)
- err = zfs_iter_bookmarks(zhp, guid_to_name_cb, &gtnd);
- zfs_close(zhp);
- if (err == EEXIST)
- return (0);
-
- /*
- * Remember the last portion of the dataset so we skip it next
- * time through (as we've already searched that portion of the
- * hierarchy).
- */
- gtnd.skip = strrchr(pname, '/') + 1;
- }
-
- return (ENOENT);
-}
-
-/*
- * Return +1 if guid1 is before guid2, 0 if they are the same, and -1 if
- * guid1 is after guid2.
- */
-static int
-created_before(libzfs_handle_t *hdl, avl_tree_t *avl,
- uint64_t guid1, uint64_t guid2)
-{
- nvlist_t *nvfs;
- char *fsname, *snapname;
- char buf[ZFS_MAX_DATASET_NAME_LEN];
- int rv;
- zfs_handle_t *guid1hdl, *guid2hdl;
- uint64_t create1, create2;
-
- if (guid2 == 0)
- return (0);
- if (guid1 == 0)
- return (1);
-
- nvfs = fsavl_find(avl, guid1, &snapname);
- VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
- (void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
- guid1hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
- if (guid1hdl == NULL)
- return (-1);
-
- nvfs = fsavl_find(avl, guid2, &snapname);
- VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
- (void) snprintf(buf, sizeof (buf), "%s@%s", fsname, snapname);
- guid2hdl = zfs_open(hdl, buf, ZFS_TYPE_SNAPSHOT);
- if (guid2hdl == NULL) {
- zfs_close(guid1hdl);
- return (-1);
- }
-
- create1 = zfs_prop_get_int(guid1hdl, ZFS_PROP_CREATETXG);
- create2 = zfs_prop_get_int(guid2hdl, ZFS_PROP_CREATETXG);
-
- if (create1 < create2)
- rv = -1;
- else if (create1 > create2)
- rv = +1;
- else
- rv = 0;
-
- zfs_close(guid1hdl);
- zfs_close(guid2hdl);
-
- return (rv);
-}
-
-static int
-recv_incremental_replication(libzfs_handle_t *hdl, const char *tofs,
- recvflags_t *flags, nvlist_t *stream_nv, avl_tree_t *stream_avl,
- nvlist_t *renamed)
-{
- nvlist_t *local_nv, *deleted = NULL;
- avl_tree_t *local_avl;
- nvpair_t *fselem, *nextfselem;
- char *fromsnap;
- char newname[ZFS_MAX_DATASET_NAME_LEN];
- char guidname[32];
- int error;
- boolean_t needagain, progress, recursive;
- char *s1, *s2;
-
- VERIFY(0 == nvlist_lookup_string(stream_nv, "fromsnap", &fromsnap));
-
- recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
- ENOENT);
-
- if (flags->dryrun)
- return (0);
-
-again:
- needagain = progress = B_FALSE;
-
- VERIFY(0 == nvlist_alloc(&deleted, NV_UNIQUE_NAME, 0));
-
- if ((error = gather_nvlist(hdl, tofs, fromsnap, NULL,
- recursive, B_FALSE, B_FALSE, &local_nv, &local_avl)) != 0)
- return (error);
-
- /*
- * Process deletes and renames
- */
- for (fselem = nvlist_next_nvpair(local_nv, NULL);
- fselem; fselem = nextfselem) {
- nvlist_t *nvfs, *snaps;
- nvlist_t *stream_nvfs = NULL;
- nvpair_t *snapelem, *nextsnapelem;
- uint64_t fromguid = 0;
- uint64_t originguid = 0;
- uint64_t stream_originguid = 0;
- uint64_t parent_fromsnap_guid, stream_parent_fromsnap_guid;
- char *fsname, *stream_fsname;
-
- nextfselem = nvlist_next_nvpair(local_nv, fselem);
-
- VERIFY(0 == nvpair_value_nvlist(fselem, &nvfs));
- VERIFY(0 == nvlist_lookup_nvlist(nvfs, "snaps", &snaps));
- VERIFY(0 == nvlist_lookup_string(nvfs, "name", &fsname));
- VERIFY(0 == nvlist_lookup_uint64(nvfs, "parentfromsnap",
- &parent_fromsnap_guid));
- (void) nvlist_lookup_uint64(nvfs, "origin", &originguid);
-
- /*
- * First find the stream's fs, so we can check for
- * a different origin (due to "zfs promote")
- */
- for (snapelem = nvlist_next_nvpair(snaps, NULL);
- snapelem; snapelem = nvlist_next_nvpair(snaps, snapelem)) {
- uint64_t thisguid;
-
- VERIFY(0 == nvpair_value_uint64(snapelem, &thisguid));
- stream_nvfs = fsavl_find(stream_avl, thisguid, NULL);
-
- if (stream_nvfs != NULL)
- break;
- }
-
- /* check for promote */
- (void) nvlist_lookup_uint64(stream_nvfs, "origin",
- &stream_originguid);
- if (stream_nvfs && originguid != stream_originguid) {
- switch (created_before(hdl, local_avl,
- stream_originguid, originguid)) {
- case 1: {
- /* promote it! */
- zfs_cmd_t zc = { 0 };
- nvlist_t *origin_nvfs;
- char *origin_fsname;
-
- if (flags->verbose)
- (void) printf("promoting %s\n", fsname);
-
- origin_nvfs = fsavl_find(local_avl, originguid,
- NULL);
- VERIFY(0 == nvlist_lookup_string(origin_nvfs,
- "name", &origin_fsname));
- (void) strlcpy(zc.zc_value, origin_fsname,
- sizeof (zc.zc_value));
- (void) strlcpy(zc.zc_name, fsname,
- sizeof (zc.zc_name));
- error = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
- if (error == 0)
- progress = B_TRUE;
- break;
- }
- default:
- break;
- case -1:
- fsavl_destroy(local_avl);
- nvlist_free(local_nv);
- return (-1);
- }
- /*
- * We had/have the wrong origin, therefore our
- * list of snapshots is wrong. Need to handle
- * them on the next pass.
- */
- needagain = B_TRUE;
- continue;
- }
-
- for (snapelem = nvlist_next_nvpair(snaps, NULL);
- snapelem; snapelem = nextsnapelem) {
- uint64_t thisguid;
- char *stream_snapname;
- nvlist_t *found, *props;
-
- nextsnapelem = nvlist_next_nvpair(snaps, snapelem);
-
- VERIFY(0 == nvpair_value_uint64(snapelem, &thisguid));
- found = fsavl_find(stream_avl, thisguid,
- &stream_snapname);
-
- /* check for delete */
- if (found == NULL) {
- char name[ZFS_MAX_DATASET_NAME_LEN];
-
- if (!flags->force)
- continue;
-
- (void) snprintf(name, sizeof (name), "%s@%s",
- fsname, nvpair_name(snapelem));
-
- error = recv_destroy(hdl, name,
- strlen(fsname)+1, newname, flags);
- if (error)
- needagain = B_TRUE;
- else
- progress = B_TRUE;
- sprintf(guidname, "%" PRIu64, thisguid);
- nvlist_add_boolean(deleted, guidname);
- continue;
- }
-
- stream_nvfs = found;
-
- if (0 == nvlist_lookup_nvlist(stream_nvfs, "snapprops",
- &props) && 0 == nvlist_lookup_nvlist(props,
- stream_snapname, &props)) {
- zfs_cmd_t zc = { 0 };
-
- zc.zc_cookie = B_TRUE; /* received */
- (void) snprintf(zc.zc_name, sizeof (zc.zc_name),
- "%s@%s", fsname, nvpair_name(snapelem));
- if (zcmd_write_src_nvlist(hdl, &zc,
- props) == 0) {
- (void) zfs_ioctl(hdl,
- ZFS_IOC_SET_PROP, &zc);
- zcmd_free_nvlists(&zc);
- }
- }
-
- /* check for different snapname */
- if (strcmp(nvpair_name(snapelem),
- stream_snapname) != 0) {
- char name[ZFS_MAX_DATASET_NAME_LEN];
- char tryname[ZFS_MAX_DATASET_NAME_LEN];
-
- (void) snprintf(name, sizeof (name), "%s@%s",
- fsname, nvpair_name(snapelem));
- (void) snprintf(tryname, sizeof (name), "%s@%s",
- fsname, stream_snapname);
-
- error = recv_rename(hdl, name, tryname,
- strlen(fsname)+1, newname, flags);
- if (error)
- needagain = B_TRUE;
- else
- progress = B_TRUE;
- }
-
- if (strcmp(stream_snapname, fromsnap) == 0)
- fromguid = thisguid;
- }
-
- /* check for delete */
- if (stream_nvfs == NULL) {
- if (!flags->force)
- continue;
-
- error = recv_destroy(hdl, fsname, strlen(tofs)+1,
- newname, flags);
- if (error)
- needagain = B_TRUE;
- else
- progress = B_TRUE;
- sprintf(guidname, "%" PRIu64, parent_fromsnap_guid);
- nvlist_add_boolean(deleted, guidname);
- continue;
- }
-
- if (fromguid == 0) {
- if (flags->verbose) {
- (void) printf("local fs %s does not have "
- "fromsnap (%s in stream); must have "
- "been deleted locally; ignoring\n",
- fsname, fromsnap);
- }
- continue;
- }
-
- VERIFY(0 == nvlist_lookup_string(stream_nvfs,
- "name", &stream_fsname));
- VERIFY(0 == nvlist_lookup_uint64(stream_nvfs,
- "parentfromsnap", &stream_parent_fromsnap_guid));
-
- s1 = strrchr(fsname, '/');
- s2 = strrchr(stream_fsname, '/');
-
- /*
- * Check if we're going to rename based on parent guid change
- * and the current parent guid was also deleted. If it was then
- * rename will fail and is likely unneeded, so avoid this and
- * force an early retry to determine the new
- * parent_fromsnap_guid.
- */
- if (stream_parent_fromsnap_guid != 0 &&
- parent_fromsnap_guid != 0 &&
- stream_parent_fromsnap_guid != parent_fromsnap_guid) {
- sprintf(guidname, "%" PRIu64, parent_fromsnap_guid);
- if (nvlist_exists(deleted, guidname)) {
- progress = B_TRUE;
- needagain = B_TRUE;
- goto doagain;
- }
- }
-
- /*
- * Check for rename. If the exact receive path is specified, it
- * does not count as a rename, but we still need to check the
- * datasets beneath it.
- */
- if ((stream_parent_fromsnap_guid != 0 &&
- parent_fromsnap_guid != 0 &&
- stream_parent_fromsnap_guid != parent_fromsnap_guid) ||
- ((flags->isprefix || strcmp(tofs, fsname) != 0) &&
- (s1 != NULL) && (s2 != NULL) && strcmp(s1, s2) != 0)) {
- nvlist_t *parent;
- char tryname[ZFS_MAX_DATASET_NAME_LEN];
-
- parent = fsavl_find(local_avl,
- stream_parent_fromsnap_guid, NULL);
- /*
- * NB: parent might not be found if we used the
- * tosnap for stream_parent_fromsnap_guid,
- * because the parent is a newly-created fs;
- * we'll be able to rename it after we recv the
- * new fs.
- */
- if (parent != NULL) {
- char *pname;
-
- VERIFY(0 == nvlist_lookup_string(parent, "name",
- &pname));
- (void) snprintf(tryname, sizeof (tryname),
- "%s%s", pname, strrchr(stream_fsname, '/'));
- } else {
- tryname[0] = '\0';
- if (flags->verbose) {
- (void) printf("local fs %s new parent "
- "not found\n", fsname);
- }
- }
-
- newname[0] = '\0';
-
- error = recv_rename(hdl, fsname, tryname,
- strlen(tofs)+1, newname, flags);
-
- if (renamed != NULL && newname[0] != '\0') {
- VERIFY(0 == nvlist_add_boolean(renamed,
- newname));
- }
-
- if (error)
- needagain = B_TRUE;
- else
- progress = B_TRUE;
- }
- }
-
-doagain:
- fsavl_destroy(local_avl);
- nvlist_free(local_nv);
- nvlist_free(deleted);
-
- if (needagain && progress) {
- /* do another pass to fix up temporary names */
- if (flags->verbose)
- (void) printf("another pass:\n");
- goto again;
- }
-
- return (needagain);
-}
-
-static int
-zfs_receive_package(libzfs_handle_t *hdl, int fd, const char *destname,
- recvflags_t *flags, dmu_replay_record_t *drr, zio_cksum_t *zc,
- char **top_zfs, int cleanup_fd, uint64_t *action_handlep)
-{
- nvlist_t *stream_nv = NULL;
- avl_tree_t *stream_avl = NULL;
- char *fromsnap = NULL;
- char *sendsnap = NULL;
- char *cp;
- char tofs[ZFS_MAX_DATASET_NAME_LEN];
- char sendfs[ZFS_MAX_DATASET_NAME_LEN];
- char errbuf[1024];
- dmu_replay_record_t drre;
- int error;
- boolean_t anyerr = B_FALSE;
- boolean_t softerr = B_FALSE;
- boolean_t recursive;
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot receive"));
-
- assert(drr->drr_type == DRR_BEGIN);
- assert(drr->drr_u.drr_begin.drr_magic == DMU_BACKUP_MAGIC);
- assert(DMU_GET_STREAM_HDRTYPE(drr->drr_u.drr_begin.drr_versioninfo) ==
- DMU_COMPOUNDSTREAM);
-
- /*
- * Read in the nvlist from the stream.
- */
- if (drr->drr_payloadlen != 0) {
- error = recv_read_nvlist(hdl, fd, drr->drr_payloadlen,
- &stream_nv, flags->byteswap, zc);
- if (error) {
- error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
- goto out;
- }
- }
-
- recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
- ENOENT);
-
- if (recursive && strchr(destname, '@')) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "cannot specify snapshot name for multi-snapshot stream"));
- error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
- goto out;
- }
-
- /*
- * Read in the end record and verify checksum.
- */
- if (0 != (error = recv_read(hdl, fd, &drre, sizeof (drre),
- flags->byteswap, NULL)))
- goto out;
- if (flags->byteswap) {
- drre.drr_type = BSWAP_32(drre.drr_type);
- drre.drr_u.drr_end.drr_checksum.zc_word[0] =
- BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[0]);
- drre.drr_u.drr_end.drr_checksum.zc_word[1] =
- BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[1]);
- drre.drr_u.drr_end.drr_checksum.zc_word[2] =
- BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[2]);
- drre.drr_u.drr_end.drr_checksum.zc_word[3] =
- BSWAP_64(drre.drr_u.drr_end.drr_checksum.zc_word[3]);
- }
- if (drre.drr_type != DRR_END) {
- error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
- goto out;
- }
- if (!ZIO_CHECKSUM_EQUAL(drre.drr_u.drr_end.drr_checksum, *zc)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "incorrect header checksum"));
- error = zfs_error(hdl, EZFS_BADSTREAM, errbuf);
- goto out;
- }
-
- (void) nvlist_lookup_string(stream_nv, "fromsnap", &fromsnap);
-
- if (drr->drr_payloadlen != 0) {
- nvlist_t *stream_fss;
-
- VERIFY(0 == nvlist_lookup_nvlist(stream_nv, "fss",
- &stream_fss));
- if ((stream_avl = fsavl_create(stream_fss)) == NULL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "couldn't allocate avl tree"));
- error = zfs_error(hdl, EZFS_NOMEM, errbuf);
- goto out;
- }
-
- if (fromsnap != NULL && recursive) {
- nvlist_t *renamed = NULL;
- nvpair_t *pair = NULL;
-
- (void) strlcpy(tofs, destname, sizeof (tofs));
- if (flags->isprefix) {
- struct drr_begin *drrb = &drr->drr_u.drr_begin;
- int i;
-
- if (flags->istail) {
- cp = strrchr(drrb->drr_toname, '/');
- if (cp == NULL) {
- (void) strlcat(tofs, "/",
- sizeof (tofs));
- i = 0;
- } else {
- i = (cp - drrb->drr_toname);
- }
- } else {
- i = strcspn(drrb->drr_toname, "/@");
- }
- /* zfs_receive_one() will create_parents() */
- (void) strlcat(tofs, &drrb->drr_toname[i],
- sizeof (tofs));
- *strchr(tofs, '@') = '\0';
- }
-
- if (!flags->dryrun && !flags->nomount) {
- VERIFY(0 == nvlist_alloc(&renamed,
- NV_UNIQUE_NAME, 0));
- }
-
- softerr = recv_incremental_replication(hdl, tofs, flags,
- stream_nv, stream_avl, renamed);
-
- /* Unmount renamed filesystems before receiving. */
- while ((pair = nvlist_next_nvpair(renamed,
- pair)) != NULL) {
- zfs_handle_t *zhp;
- prop_changelist_t *clp = NULL;
-
- zhp = zfs_open(hdl, nvpair_name(pair),
- ZFS_TYPE_FILESYSTEM);
- if (zhp != NULL) {
- clp = changelist_gather(zhp,
- ZFS_PROP_MOUNTPOINT, 0,
- flags->forceunmount ? MS_FORCE : 0);
- zfs_close(zhp);
- if (clp != NULL) {
- softerr |=
- changelist_prefix(clp);
- changelist_free(clp);
- }
- }
- }
-
- nvlist_free(renamed);
- }
- }
-
- /*
- * Get the fs specified by the first path in the stream (the top level
- * specified by 'zfs send') and pass it to each invocation of
- * zfs_receive_one().
- */
- (void) strlcpy(sendfs, drr->drr_u.drr_begin.drr_toname,
- sizeof (sendfs));
- if ((cp = strchr(sendfs, '@')) != NULL) {
- *cp = '\0';
- /*
- * Find the "sendsnap", the final snapshot in a replication
- * stream. zfs_receive_one() handles certain errors
- * differently, depending on if the contained stream is the
- * last one or not.
- */
- sendsnap = (cp + 1);
- }
-
- /* Finally, receive each contained stream */
- do {
- /*
- * we should figure out if it has a recoverable
- * error, in which case do a recv_skip() and drive on.
- * Note, if we fail due to already having this guid,
- * zfs_receive_one() will take care of it (ie,
- * recv_skip() and return 0).
- */
- error = zfs_receive_impl(hdl, destname, NULL, flags, fd,
- sendfs, stream_nv, stream_avl, top_zfs, cleanup_fd,
- action_handlep, sendsnap);
- if (error == ENODATA) {
- error = 0;
- break;
- }
- anyerr |= error;
- } while (error == 0);
-
- if (drr->drr_payloadlen != 0 && recursive && fromsnap != NULL) {
- /*
- * Now that we have the fs's they sent us, try the
- * renames again.
- */
- softerr = recv_incremental_replication(hdl, tofs, flags,
- stream_nv, stream_avl, NULL);
- }
-
-out:
- fsavl_destroy(stream_avl);
- nvlist_free(stream_nv);
- if (softerr)
- error = -2;
- if (anyerr)
- error = -1;
- return (error);
-}
-
-static void
-trunc_prop_errs(int truncated)
-{
- ASSERT(truncated != 0);
-
- if (truncated == 1)
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "1 more property could not be set\n"));
- else
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN,
- "%d more properties could not be set\n"), truncated);
-}
-
-static int
-recv_skip(libzfs_handle_t *hdl, int fd, boolean_t byteswap)
-{
- dmu_replay_record_t *drr;
- void *buf = zfs_alloc(hdl, SPA_MAXBLOCKSIZE);
- char errbuf[1024];
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot receive:"));
-
- /* XXX would be great to use lseek if possible... */
- drr = buf;
-
- while (recv_read(hdl, fd, drr, sizeof (dmu_replay_record_t),
- byteswap, NULL) == 0) {
- if (byteswap)
- drr->drr_type = BSWAP_32(drr->drr_type);
-
- switch (drr->drr_type) {
- case DRR_BEGIN:
- if (drr->drr_payloadlen != 0) {
- (void) recv_read(hdl, fd, buf,
- drr->drr_payloadlen, B_FALSE, NULL);
- }
- break;
-
- case DRR_END:
- free(buf);
- return (0);
-
- case DRR_OBJECT:
- if (byteswap) {
- drr->drr_u.drr_object.drr_bonuslen =
- BSWAP_32(drr->drr_u.drr_object.
- drr_bonuslen);
- }
- (void) recv_read(hdl, fd, buf,
- P2ROUNDUP(drr->drr_u.drr_object.drr_bonuslen, 8),
- B_FALSE, NULL);
- break;
-
- case DRR_WRITE:
- if (byteswap) {
- drr->drr_u.drr_write.drr_logical_size =
- BSWAP_64(
- drr->drr_u.drr_write.drr_logical_size);
- drr->drr_u.drr_write.drr_compressed_size =
- BSWAP_64(
- drr->drr_u.drr_write.drr_compressed_size);
- }
- uint64_t payload_size =
- DRR_WRITE_PAYLOAD_SIZE(&drr->drr_u.drr_write);
- (void) recv_read(hdl, fd, buf,
- payload_size, B_FALSE, NULL);
- break;
- case DRR_SPILL:
- if (byteswap) {
- drr->drr_u.drr_spill.drr_length =
- BSWAP_64(drr->drr_u.drr_spill.drr_length);
- }
- (void) recv_read(hdl, fd, buf,
- drr->drr_u.drr_spill.drr_length, B_FALSE, NULL);
- break;
- case DRR_WRITE_EMBEDDED:
- if (byteswap) {
- drr->drr_u.drr_write_embedded.drr_psize =
- BSWAP_32(drr->drr_u.drr_write_embedded.
- drr_psize);
- }
- (void) recv_read(hdl, fd, buf,
- P2ROUNDUP(drr->drr_u.drr_write_embedded.drr_psize,
- 8), B_FALSE, NULL);
- break;
- case DRR_WRITE_BYREF:
- case DRR_FREEOBJECTS:
- case DRR_FREE:
- break;
-
- default:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid record type"));
- return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
- }
- }
-
- free(buf);
- return (-1);
-}
-
-static void
-recv_ecksum_set_aux(libzfs_handle_t *hdl, const char *target_snap,
- boolean_t resumable)
-{
- char target_fs[ZFS_MAX_DATASET_NAME_LEN];
-
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "checksum mismatch or incomplete stream"));
-
- if (!resumable)
- return;
- (void) strlcpy(target_fs, target_snap, sizeof (target_fs));
- *strchr(target_fs, '@') = '\0';
- zfs_handle_t *zhp = zfs_open(hdl, target_fs,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- if (zhp == NULL)
- return;
-
- char token_buf[ZFS_MAXPROPLEN];
- int error = zfs_prop_get(zhp, ZFS_PROP_RECEIVE_RESUME_TOKEN,
- token_buf, sizeof (token_buf),
- NULL, NULL, 0, B_TRUE);
- if (error == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "checksum mismatch or incomplete stream.\n"
- "Partially received snapshot is saved.\n"
- "A resuming stream can be generated on the sending "
- "system by running:\n"
- " zfs send -t %s"),
- token_buf);
- }
- zfs_close(zhp);
-}
-
-/*
- * Restores a backup of tosnap from the file descriptor specified by infd.
- */
-static int
-zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
- const char *originsnap, recvflags_t *flags, dmu_replay_record_t *drr,
- dmu_replay_record_t *drr_noswap, const char *sendfs, nvlist_t *stream_nv,
- avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
- uint64_t *action_handlep, const char *finalsnap)
-{
- zfs_cmd_t zc = { 0 };
- time_t begin_time;
- int ioctl_err, ioctl_errno, err;
- char *cp;
- struct drr_begin *drrb = &drr->drr_u.drr_begin;
- char errbuf[1024];
- char prop_errbuf[1024];
- const char *chopprefix;
- boolean_t newfs = B_FALSE;
- boolean_t stream_wantsnewfs;
- uint64_t parent_snapguid = 0;
- prop_changelist_t *clp = NULL;
- nvlist_t *snapprops_nvlist = NULL;
- zprop_errflags_t prop_errflags;
- boolean_t recursive;
- char *snapname = NULL;
-
- begin_time = time(NULL);
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot receive"));
-
- recursive = (nvlist_lookup_boolean(stream_nv, "not_recursive") ==
- ENOENT);
-
- if (stream_avl != NULL) {
- nvlist_t *fs = fsavl_find(stream_avl, drrb->drr_toguid,
- &snapname);
- nvlist_t *props;
- int ret;
-
- (void) nvlist_lookup_uint64(fs, "parentfromsnap",
- &parent_snapguid);
- err = nvlist_lookup_nvlist(fs, "props", &props);
- if (err)
- VERIFY(0 == nvlist_alloc(&props, NV_UNIQUE_NAME, 0));
-
- if (flags->canmountoff) {
- VERIFY(0 == nvlist_add_uint64(props,
- zfs_prop_to_name(ZFS_PROP_CANMOUNT), 0));
- }
- ret = zcmd_write_src_nvlist(hdl, &zc, props);
- if (err)
- nvlist_free(props);
-
- if (0 == nvlist_lookup_nvlist(fs, "snapprops", &props)) {
- VERIFY(0 == nvlist_lookup_nvlist(props,
- snapname, &snapprops_nvlist));
- }
-
- if (ret != 0)
- return (-1);
- }
-
- cp = NULL;
-
- /*
- * Determine how much of the snapshot name stored in the stream
- * we are going to tack on to the name they specified on the
- * command line, and how much we are going to chop off.
- *
- * If they specified a snapshot, chop the entire name stored in
- * the stream.
- */
- if (flags->istail) {
- /*
- * A filesystem was specified with -e. We want to tack on only
- * the tail of the sent snapshot path.
- */
- if (strchr(tosnap, '@')) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
- "argument - snapshot not allowed with -e"));
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- }
-
- chopprefix = strrchr(sendfs, '/');
-
- if (chopprefix == NULL) {
- /*
- * The tail is the poolname, so we need to
- * prepend a path separator.
- */
- int len = strlen(drrb->drr_toname);
- cp = malloc(len + 2);
- cp[0] = '/';
- (void) strcpy(&cp[1], drrb->drr_toname);
- chopprefix = cp;
- } else {
- chopprefix = drrb->drr_toname + (chopprefix - sendfs);
- }
- } else if (flags->isprefix) {
- /*
- * A filesystem was specified with -d. We want to tack on
- * everything but the first element of the sent snapshot path
- * (all but the pool name).
- */
- if (strchr(tosnap, '@')) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
- "argument - snapshot not allowed with -d"));
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- }
-
- chopprefix = strchr(drrb->drr_toname, '/');
- if (chopprefix == NULL)
- chopprefix = strchr(drrb->drr_toname, '@');
- } else if (strchr(tosnap, '@') == NULL) {
- /*
- * If a filesystem was specified without -d or -e, we want to
- * tack on everything after the fs specified by 'zfs send'.
- */
- chopprefix = drrb->drr_toname + strlen(sendfs);
- } else {
- /* A snapshot was specified as an exact path (no -d or -e). */
- if (recursive) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "cannot specify snapshot name for multi-snapshot "
- "stream"));
- return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
- }
- chopprefix = drrb->drr_toname + strlen(drrb->drr_toname);
- }
-
- ASSERT(strstr(drrb->drr_toname, sendfs) == drrb->drr_toname);
- ASSERT(chopprefix > drrb->drr_toname);
- ASSERT(chopprefix <= drrb->drr_toname + strlen(drrb->drr_toname));
- ASSERT(chopprefix[0] == '/' || chopprefix[0] == '@' ||
- chopprefix[0] == '\0');
-
- /*
- * Determine name of destination snapshot, store in zc_value.
- */
- (void) strcpy(zc.zc_value, tosnap);
- (void) strncat(zc.zc_value, chopprefix, sizeof (zc.zc_value));
-#ifdef __FreeBSD__
- if (zfs_ioctl_version == ZFS_IOCVER_UNDEF)
- zfs_ioctl_version = get_zfs_ioctl_version();
- /*
- * For forward compatibility hide tosnap in zc_value
- */
- if (zfs_ioctl_version < ZFS_IOCVER_LZC)
- (void) strcpy(zc.zc_value + strlen(zc.zc_value) + 1, tosnap);
-#endif
- free(cp);
- if (!zfs_name_valid(zc.zc_value, ZFS_TYPE_SNAPSHOT)) {
- zcmd_free_nvlists(&zc);
- return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
- }
-
- /*
- * Determine the name of the origin snapshot, store in zc_string.
- */
- if (originsnap) {
- (void) strncpy(zc.zc_string, originsnap, sizeof (zc.zc_string));
- if (flags->verbose)
- (void) printf("using provided clone origin %s\n",
- zc.zc_string);
- } else if (drrb->drr_flags & DRR_FLAG_CLONE) {
- if (guid_to_name(hdl, zc.zc_value,
- drrb->drr_fromguid, B_FALSE, zc.zc_string) != 0) {
- zcmd_free_nvlists(&zc);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "local origin for clone %s does not exist"),
- zc.zc_value);
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
- }
- if (flags->verbose)
- (void) printf("found clone origin %s\n", zc.zc_string);
- }
-
- boolean_t resuming = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo) &
- DMU_BACKUP_FEATURE_RESUMING;
- stream_wantsnewfs = (drrb->drr_fromguid == 0 ||
- (drrb->drr_flags & DRR_FLAG_CLONE) || originsnap) && !resuming;
-
- if (stream_wantsnewfs) {
- /*
- * if the parent fs does not exist, look for it based on
- * the parent snap GUID
- */
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot receive new filesystem stream"));
-
- (void) strcpy(zc.zc_name, zc.zc_value);
- cp = strrchr(zc.zc_name, '/');
- if (cp)
- *cp = '\0';
- if (cp &&
- !zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
- char suffix[ZFS_MAX_DATASET_NAME_LEN];
- (void) strcpy(suffix, strrchr(zc.zc_value, '/'));
- if (guid_to_name(hdl, zc.zc_name, parent_snapguid,
- B_FALSE, zc.zc_value) == 0) {
- *strchr(zc.zc_value, '@') = '\0';
- (void) strcat(zc.zc_value, suffix);
- }
- }
- } else {
- /*
- * If the fs does not exist, look for it based on the
- * fromsnap GUID.
- */
- if (resuming) {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot receive resume stream"));
- } else {
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN,
- "cannot receive incremental stream"));
- }
-
- (void) strcpy(zc.zc_name, zc.zc_value);
- *strchr(zc.zc_name, '@') = '\0';
-
- /*
- * If the exact receive path was specified and this is the
- * topmost path in the stream, then if the fs does not exist we
- * should look no further.
- */
- if ((flags->isprefix || (*(chopprefix = drrb->drr_toname +
- strlen(sendfs)) != '\0' && *chopprefix != '@')) &&
- !zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
- char snap[ZFS_MAX_DATASET_NAME_LEN];
- (void) strcpy(snap, strchr(zc.zc_value, '@'));
- if (guid_to_name(hdl, zc.zc_name, drrb->drr_fromguid,
- B_FALSE, zc.zc_value) == 0) {
- *strchr(zc.zc_value, '@') = '\0';
- (void) strcat(zc.zc_value, snap);
- }
- }
- }
-
- (void) strcpy(zc.zc_name, zc.zc_value);
- *strchr(zc.zc_name, '@') = '\0';
-
- if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
- zfs_handle_t *zhp;
-
- /*
- * Destination fs exists. It must be one of these cases:
- * - an incremental send stream
- * - the stream specifies a new fs (full stream or clone)
- * and they want us to blow away the existing fs (and
- * have therefore specified -F and removed any snapshots)
- * - we are resuming a failed receive.
- */
- if (stream_wantsnewfs) {
- boolean_t is_volume = drrb->drr_type == DMU_OST_ZVOL;
- if (!flags->force) {
- zcmd_free_nvlists(&zc);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "destination '%s' exists\n"
- "must specify -F to overwrite it"),
- zc.zc_name);
- return (zfs_error(hdl, EZFS_EXISTS, errbuf));
- }
- if (ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT,
- &zc) == 0) {
- zcmd_free_nvlists(&zc);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "destination has snapshots (eg. %s)\n"
- "must destroy them to overwrite it"),
- zc.zc_name);
- return (zfs_error(hdl, EZFS_EXISTS, errbuf));
- }
- if (is_volume && strrchr(zc.zc_name, '/') == NULL) {
- zcmd_free_nvlists(&zc);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "destination '%s' is the root dataset\n"
- "cannot overwrite with a ZVOL"),
- zc.zc_name);
- return (zfs_error(hdl, EZFS_EXISTS, errbuf));
- }
- if (is_volume &&
- ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT,
- &zc) == 0) {
- zcmd_free_nvlists(&zc);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "destination has children (eg. %s)\n"
- "cannot overwrite with a ZVOL"),
- zc.zc_name);
- return (zfs_error(hdl, EZFS_WRONG_PARENT,
- errbuf));
- }
- }
-
- if ((zhp = zfs_open(hdl, zc.zc_name,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) {
- zcmd_free_nvlists(&zc);
- return (-1);
- }
-
- if (stream_wantsnewfs &&
- zhp->zfs_dmustats.dds_origin[0]) {
- zcmd_free_nvlists(&zc);
- zfs_close(zhp);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "destination '%s' is a clone\n"
- "must destroy it to overwrite it"),
- zc.zc_name);
- return (zfs_error(hdl, EZFS_EXISTS, errbuf));
- }
-
- if (!flags->dryrun && zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
- (stream_wantsnewfs || resuming)) {
- /* We can't do online recv in this case */
- clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
- flags->forceunmount ? MS_FORCE : 0);
- if (clp == NULL) {
- zfs_close(zhp);
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- if (changelist_prefix(clp) != 0) {
- changelist_free(clp);
- zfs_close(zhp);
- zcmd_free_nvlists(&zc);
- return (-1);
- }
- }
-
- /*
- * If we are resuming a newfs, set newfs here so that we will
- * mount it if the recv succeeds this time. We can tell
- * that it was a newfs on the first recv because the fs
- * itself will be inconsistent (if the fs existed when we
- * did the first recv, we would have received it into
- * .../%recv).
- */
- if (resuming && zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT))
- newfs = B_TRUE;
-
- zfs_close(zhp);
- } else {
- zfs_handle_t *zhp;
-
- /*
- * Destination filesystem does not exist. Therefore we better
- * be creating a new filesystem (either from a full backup, or
- * a clone). It would therefore be invalid if the user
- * specified only the pool name (i.e. if the destination name
- * contained no slash character).
- */
- if (!stream_wantsnewfs ||
- (cp = strrchr(zc.zc_name, '/')) == NULL) {
- zcmd_free_nvlists(&zc);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "destination '%s' does not exist"), zc.zc_name);
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
- }
-
- /*
- * Trim off the final dataset component so we perform the
- * recvbackup ioctl to the filesystems's parent.
- */
- *cp = '\0';
-
- if (flags->isprefix && !flags->istail && !flags->dryrun &&
- create_parents(hdl, zc.zc_value, strlen(tosnap)) != 0) {
- zcmd_free_nvlists(&zc);
- return (zfs_error(hdl, EZFS_BADRESTORE, errbuf));
- }
-
- /* validate parent */
- zhp = zfs_open(hdl, zc.zc_name, ZFS_TYPE_DATASET);
- if (zhp == NULL) {
- zcmd_free_nvlists(&zc);
- return (zfs_error(hdl, EZFS_BADRESTORE, errbuf));
- }
- if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
- zcmd_free_nvlists(&zc);
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "parent '%s' is not a filesystem"), zc.zc_name);
- zfs_close(zhp);
- return (zfs_error(hdl, EZFS_WRONG_PARENT, errbuf));
- }
- zfs_close(zhp);
-
- newfs = B_TRUE;
- }
-
- zc.zc_begin_record = *drr_noswap;
- zc.zc_cookie = infd;
- zc.zc_guid = flags->force;
- zc.zc_resumable = flags->resumable;
- if (flags->verbose) {
- (void) printf("%s %s stream of %s into %s\n",
- flags->dryrun ? "would receive" : "receiving",
- drrb->drr_fromguid ? "incremental" : "full",
- drrb->drr_toname, zc.zc_value);
- (void) fflush(stdout);
- }
-
- if (flags->dryrun) {
- zcmd_free_nvlists(&zc);
- return (recv_skip(hdl, infd, flags->byteswap));
- }
-
- zc.zc_nvlist_dst = (uint64_t)(uintptr_t)prop_errbuf;
- zc.zc_nvlist_dst_size = sizeof (prop_errbuf);
- zc.zc_cleanup_fd = cleanup_fd;
- zc.zc_action_handle = *action_handlep;
-
- err = ioctl_err = zfs_ioctl(hdl, ZFS_IOC_RECV, &zc);
- ioctl_errno = errno;
- prop_errflags = (zprop_errflags_t)zc.zc_obj;
-
- if (err == 0) {
- nvlist_t *prop_errors;
- VERIFY(0 == nvlist_unpack((void *)(uintptr_t)zc.zc_nvlist_dst,
- zc.zc_nvlist_dst_size, &prop_errors, 0));
-
- nvpair_t *prop_err = NULL;
-
- while ((prop_err = nvlist_next_nvpair(prop_errors,
- prop_err)) != NULL) {
- char tbuf[1024];
- zfs_prop_t prop;
- int intval;
-
- prop = zfs_name_to_prop(nvpair_name(prop_err));
- (void) nvpair_value_int32(prop_err, &intval);
- if (strcmp(nvpair_name(prop_err),
- ZPROP_N_MORE_ERRORS) == 0) {
- trunc_prop_errs(intval);
- break;
- } else if (snapname == NULL || finalsnap == NULL ||
- strcmp(finalsnap, snapname) == 0 ||
- strcmp(nvpair_name(prop_err),
- zfs_prop_to_name(ZFS_PROP_REFQUOTA)) != 0) {
- /*
- * Skip the special case of, for example,
- * "refquota", errors on intermediate
- * snapshots leading up to a final one.
- * That's why we have all of the checks above.
- *
- * See zfs_ioctl.c's extract_delay_props() for
- * a list of props which can fail on
- * intermediate snapshots, but shouldn't
- * affect the overall receive.
- */
- (void) snprintf(tbuf, sizeof (tbuf),
- dgettext(TEXT_DOMAIN,
- "cannot receive %s property on %s"),
- nvpair_name(prop_err), zc.zc_name);
- zfs_setprop_error(hdl, prop, intval, tbuf);
- }
- }
- nvlist_free(prop_errors);
- }
-
- zc.zc_nvlist_dst = 0;
- zc.zc_nvlist_dst_size = 0;
- zcmd_free_nvlists(&zc);
-
- if (err == 0 && snapprops_nvlist) {
- zfs_cmd_t zc2 = { 0 };
-
- (void) strcpy(zc2.zc_name, zc.zc_value);
- zc2.zc_cookie = B_TRUE; /* received */
- if (zcmd_write_src_nvlist(hdl, &zc2, snapprops_nvlist) == 0) {
- (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc2);
- zcmd_free_nvlists(&zc2);
- }
- }
-
- if (err && (ioctl_errno == ENOENT || ioctl_errno == EEXIST)) {
- /*
- * It may be that this snapshot already exists,
- * in which case we want to consume & ignore it
- * rather than failing.
- */
- avl_tree_t *local_avl;
- nvlist_t *local_nv, *fs;
- cp = strchr(zc.zc_value, '@');
-
- /*
- * XXX Do this faster by just iterating over snaps in
- * this fs. Also if zc_value does not exist, we will
- * get a strange "does not exist" error message.
- */
- *cp = '\0';
- if (gather_nvlist(hdl, zc.zc_value, NULL, NULL, B_FALSE,
- B_FALSE, B_FALSE, &local_nv, &local_avl) == 0) {
- *cp = '@';
- fs = fsavl_find(local_avl, drrb->drr_toguid, NULL);
- fsavl_destroy(local_avl);
- nvlist_free(local_nv);
-
- if (fs != NULL) {
- if (flags->verbose) {
- (void) printf("snap %s already exists; "
- "ignoring\n", zc.zc_value);
- }
- err = ioctl_err = recv_skip(hdl, infd,
- flags->byteswap);
- }
- }
- *cp = '@';
- }
-
- if (ioctl_err != 0) {
- switch (ioctl_errno) {
- case ENODEV:
- cp = strchr(zc.zc_value, '@');
- *cp = '\0';
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "most recent snapshot of %s does not\n"
- "match incremental source"), zc.zc_value);
- (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
- *cp = '@';
- break;
- case ETXTBSY:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "destination %s has been modified\n"
- "since most recent snapshot"), zc.zc_name);
- (void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
- break;
- case EEXIST:
- cp = strchr(zc.zc_value, '@');
- if (newfs) {
- /* it's the containing fs that exists */
- *cp = '\0';
- }
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "destination already exists"));
- (void) zfs_error_fmt(hdl, EZFS_EXISTS,
- dgettext(TEXT_DOMAIN, "cannot restore to %s"),
- zc.zc_value);
- *cp = '@';
- break;
- case EINVAL:
- (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
- break;
- case ECKSUM:
- recv_ecksum_set_aux(hdl, zc.zc_value, flags->resumable);
- (void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
- break;
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded to receive this stream."));
- (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
- break;
- case EDQUOT:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "destination %s space quota exceeded"), zc.zc_name);
- (void) zfs_error(hdl, EZFS_NOSPC, errbuf);
- break;
- default:
- (void) zfs_standard_error(hdl, ioctl_errno, errbuf);
- }
- }
-
- /*
- * Mount the target filesystem (if created). Also mount any
- * children of the target filesystem if we did a replication
- * receive (indicated by stream_avl being non-NULL).
- */
- cp = strchr(zc.zc_value, '@');
- if (cp && (ioctl_err == 0 || !newfs)) {
- zfs_handle_t *h;
-
- *cp = '\0';
- h = zfs_open(hdl, zc.zc_value,
- ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
- if (h != NULL) {
- if (h->zfs_type == ZFS_TYPE_VOLUME) {
- *cp = '@';
- } else if (newfs || stream_avl) {
- /*
- * Track the first/top of hierarchy fs,
- * for mounting and sharing later.
- */
- if (top_zfs && *top_zfs == NULL)
- *top_zfs = zfs_strdup(hdl, zc.zc_value);
- }
- zfs_close(h);
- }
- *cp = '@';
- }
-
- if (clp) {
- if (!flags->nomount)
- err |= changelist_postfix(clp);
- changelist_free(clp);
- }
-
- if (prop_errflags & ZPROP_ERR_NOCLEAR) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: "
- "failed to clear unreceived properties on %s"),
- zc.zc_name);
- (void) fprintf(stderr, "\n");
- }
- if (prop_errflags & ZPROP_ERR_NORESTORE) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "Warning: "
- "failed to restore original properties on %s"),
- zc.zc_name);
- (void) fprintf(stderr, "\n");
- }
-
- if (err || ioctl_err)
- return (-1);
-
- *action_handlep = zc.zc_action_handle;
-
- if (flags->verbose) {
- char buf1[64];
- char buf2[64];
- uint64_t bytes = zc.zc_cookie;
- time_t delta = time(NULL) - begin_time;
- if (delta == 0)
- delta = 1;
- zfs_nicenum(bytes, buf1, sizeof (buf1));
- zfs_nicenum(bytes/delta, buf2, sizeof (buf1));
-
- (void) printf("received %sB stream in %lu seconds (%sB/sec)\n",
- buf1, delta, buf2);
- }
-
- return (0);
-}
-
-static int
-zfs_receive_impl(libzfs_handle_t *hdl, const char *tosnap,
- const char *originsnap, recvflags_t *flags, int infd, const char *sendfs,
- nvlist_t *stream_nv, avl_tree_t *stream_avl, char **top_zfs, int cleanup_fd,
- uint64_t *action_handlep, const char *finalsnap)
-{
- int err;
- dmu_replay_record_t drr, drr_noswap;
- struct drr_begin *drrb = &drr.drr_u.drr_begin;
- char errbuf[1024];
- zio_cksum_t zcksum = { 0 };
- uint64_t featureflags;
- int hdrtype;
-
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot receive"));
-
- if (flags->isprefix &&
- !zfs_dataset_exists(hdl, tosnap, ZFS_TYPE_DATASET)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified fs "
- "(%s) does not exist"), tosnap);
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
- }
- if (originsnap &&
- !zfs_dataset_exists(hdl, originsnap, ZFS_TYPE_DATASET)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "specified origin fs "
- "(%s) does not exist"), originsnap);
- return (zfs_error(hdl, EZFS_NOENT, errbuf));
- }
-
- /* read in the BEGIN record */
- if (0 != (err = recv_read(hdl, infd, &drr, sizeof (drr), B_FALSE,
- &zcksum)))
- return (err);
-
- if (drr.drr_type == DRR_END || drr.drr_type == BSWAP_32(DRR_END)) {
- /* It's the double end record at the end of a package */
- return (ENODATA);
- }
-
- /* the kernel needs the non-byteswapped begin record */
- drr_noswap = drr;
-
- flags->byteswap = B_FALSE;
- if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
- /*
- * We computed the checksum in the wrong byteorder in
- * recv_read() above; do it again correctly.
- */
- bzero(&zcksum, sizeof (zio_cksum_t));
- (void) fletcher_4_incremental_byteswap(&drr,
- sizeof (drr), &zcksum);
- flags->byteswap = B_TRUE;
-
- drr.drr_type = BSWAP_32(drr.drr_type);
- drr.drr_payloadlen = BSWAP_32(drr.drr_payloadlen);
- drrb->drr_magic = BSWAP_64(drrb->drr_magic);
- drrb->drr_versioninfo = BSWAP_64(drrb->drr_versioninfo);
- drrb->drr_creation_time = BSWAP_64(drrb->drr_creation_time);
- drrb->drr_type = BSWAP_32(drrb->drr_type);
- drrb->drr_flags = BSWAP_32(drrb->drr_flags);
- drrb->drr_toguid = BSWAP_64(drrb->drr_toguid);
- drrb->drr_fromguid = BSWAP_64(drrb->drr_fromguid);
- }
-
- if (drrb->drr_magic != DMU_BACKUP_MAGIC || drr.drr_type != DRR_BEGIN) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
- "stream (bad magic number)"));
- return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
- }
-
- featureflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo);
- hdrtype = DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo);
-
- if (!DMU_STREAM_SUPPORTED(featureflags) ||
- (hdrtype != DMU_SUBSTREAM && hdrtype != DMU_COMPOUNDSTREAM)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "stream has unsupported feature, feature flags = %lx"),
- featureflags);
- return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
- }
-
- if (strchr(drrb->drr_toname, '@') == NULL) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
- "stream (bad snapshot name)"));
- return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
- }
-
- if (DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == DMU_SUBSTREAM) {
- char nonpackage_sendfs[ZFS_MAX_DATASET_NAME_LEN];
- if (sendfs == NULL) {
- /*
- * We were not called from zfs_receive_package(). Get
- * the fs specified by 'zfs send'.
- */
- char *cp;
- (void) strlcpy(nonpackage_sendfs,
- drr.drr_u.drr_begin.drr_toname,
- sizeof (nonpackage_sendfs));
- if ((cp = strchr(nonpackage_sendfs, '@')) != NULL)
- *cp = '\0';
- sendfs = nonpackage_sendfs;
- VERIFY(finalsnap == NULL);
- }
- return (zfs_receive_one(hdl, infd, tosnap, originsnap, flags,
- &drr, &drr_noswap, sendfs, stream_nv, stream_avl, top_zfs,
- cleanup_fd, action_handlep, finalsnap));
- } else {
- assert(DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
- DMU_COMPOUNDSTREAM);
- return (zfs_receive_package(hdl, infd, tosnap, flags, &drr,
- &zcksum, top_zfs, cleanup_fd, action_handlep));
- }
-}
-
-/*
- * Restores a backup of tosnap from the file descriptor specified by infd.
- * Return 0 on total success, -2 if some things couldn't be
- * destroyed/renamed/promoted, -1 if some things couldn't be received.
- * (-1 will override -2, if -1 and the resumable flag was specified the
- * transfer can be resumed if the sending side supports it).
- */
-int
-zfs_receive(libzfs_handle_t *hdl, const char *tosnap, nvlist_t *props,
- recvflags_t *flags, int infd, avl_tree_t *stream_avl)
-{
- char *top_zfs = NULL;
- int err;
- int cleanup_fd;
- uint64_t action_handle = 0;
- char *originsnap = NULL;
-
- if (props) {
- err = nvlist_lookup_string(props, "origin", &originsnap);
- if (err && err != ENOENT)
- return (err);
- }
-
- cleanup_fd = open(ZFS_DEV, O_RDWR|O_EXCL);
- VERIFY(cleanup_fd >= 0);
-
- err = zfs_receive_impl(hdl, tosnap, originsnap, flags, infd, NULL, NULL,
- stream_avl, &top_zfs, cleanup_fd, &action_handle, NULL);
-
- VERIFY(0 == close(cleanup_fd));
-
- if (err == 0 && !flags->nomount && top_zfs) {
- zfs_handle_t *zhp;
- prop_changelist_t *clp;
-
- zhp = zfs_open(hdl, top_zfs, ZFS_TYPE_FILESYSTEM);
- if (zhp != NULL) {
- clp = changelist_gather(zhp, ZFS_PROP_MOUNTPOINT,
- CL_GATHER_MOUNT_ALWAYS,
- flags->forceunmount ? MS_FORCE : 0);
- zfs_close(zhp);
- if (clp != NULL) {
- /* mount and share received datasets */
- err = changelist_postfix(clp);
- changelist_free(clp);
- }
- }
- if (zhp == NULL || clp == NULL || err)
- err = -1;
- }
- if (top_zfs)
- free(top_zfs);
-
- return (err);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_status.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_status.c
deleted file mode 100644
index d32662022cf54..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_status.c
+++ /dev/null
@@ -1,511 +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 by Delphix. All rights reserved.
- * Copyright (c) 2013 Steven Hartland. All rights reserved.
- */
-
-/*
- * This file contains the functions which analyze the status of a pool. This
- * include both the status of an active pool, as well as the status exported
- * pools. Returns one of the ZPOOL_STATUS_* defines describing the status of
- * the pool. This status is independent (to a certain degree) from the state of
- * the pool. A pool's state describes only whether or not it is capable of
- * providing the necessary fault tolerance for data. The status describes the
- * overall status of devices. A pool that is online can still have a device
- * that is experiencing errors.
- *
- * Only a subset of the possible faults can be detected using 'zpool status',
- * and not all possible errors correspond to a FMA message ID. The explanation
- * is left up to the caller, depending on whether it is a live pool or an
- * import.
- */
-
-#include <libzfs.h>
-#include <string.h>
-#include <unistd.h>
-#include "libzfs_impl.h"
-#include "zfeature_common.h"
-
-/*
- * Message ID table. This must be kept in sync with the ZPOOL_STATUS_* defines
- * in libzfs.h. Note that there are some status results which go past the end
- * of this table, and hence have no associated message ID.
- */
-static char *zfs_msgid_table[] = {
- "ZFS-8000-14", /* ZPOOL_STATUS_CORRUPT_CACHE */
- "ZFS-8000-2Q", /* ZPOOL_STATUS_MISSING_DEV_R */
- "ZFS-8000-3C", /* ZPOOL_STATUS_MISSING_DEV_NR */
- "ZFS-8000-4J", /* ZPOOL_STATUS_CORRUPT_LABEL_R */
- "ZFS-8000-5E", /* ZPOOL_STATUS_CORRUPT_LABEL_NR */
- "ZFS-8000-6X", /* ZPOOL_STATUS_BAD_GUID_SUM */
- "ZFS-8000-72", /* ZPOOL_STATUS_CORRUPT_POOL */
- "ZFS-8000-8A", /* ZPOOL_STATUS_CORRUPT_DATA */
- "ZFS-8000-9P", /* ZPOOL_STATUS_FAILING_DEV */
- "ZFS-8000-A5", /* ZPOOL_STATUS_VERSION_NEWER */
- "ZFS-8000-EY", /* ZPOOL_STATUS_HOSTID_MISMATCH */
- "ZFS-8000-EY", /* ZPOOL_STATUS_HOSTID_ACTIVE */
- "ZFS-8000-EY", /* ZPOOL_STATUS_HOSTID_REQUIRED */
- "ZFS-8000-HC", /* ZPOOL_STATUS_IO_FAILURE_WAIT */
- "ZFS-8000-JQ", /* ZPOOL_STATUS_IO_FAILURE_CONTINUE */
- "ZFS-8000-MM", /* ZPOOL_STATUS_IO_FAILURE_MMP */
- "ZFS-8000-K4", /* ZPOOL_STATUS_BAD_LOG */
- /*
- * The following results have no message ID.
- * ZPOOL_STATUS_UNSUP_FEAT_READ
- * ZPOOL_STATUS_UNSUP_FEAT_WRITE
- * ZPOOL_STATUS_FAULTED_DEV_R
- * ZPOOL_STATUS_FAULTED_DEV_NR
- * ZPOOL_STATUS_VERSION_OLDER
- * ZPOOL_STATUS_FEAT_DISABLED
- * ZPOOL_STATUS_RESILVERING
- * ZPOOL_STATUS_OFFLINE_DEV
- * ZPOOL_STATUS_REMOVED_DEV
- * ZPOOL_STATUS_OK
- */
-};
-
-#define NMSGID (sizeof (zfs_msgid_table) / sizeof (zfs_msgid_table[0]))
-
-/* ARGSUSED */
-static int
-vdev_missing(vdev_stat_t *vs, uint_t vsc)
-{
- return (vs->vs_state == VDEV_STATE_CANT_OPEN &&
- vs->vs_aux == VDEV_AUX_OPEN_FAILED);
-}
-
-/* ARGSUSED */
-static int
-vdev_faulted(vdev_stat_t *vs, uint_t vsc)
-{
- return (vs->vs_state == VDEV_STATE_FAULTED);
-}
-
-/* ARGSUSED */
-static int
-vdev_errors(vdev_stat_t *vs, uint_t vsc)
-{
- return (vs->vs_state == VDEV_STATE_DEGRADED ||
- vs->vs_read_errors != 0 || vs->vs_write_errors != 0 ||
- vs->vs_checksum_errors != 0);
-}
-
-/* ARGSUSED */
-static int
-vdev_broken(vdev_stat_t *vs, uint_t vsc)
-{
- return (vs->vs_state == VDEV_STATE_CANT_OPEN);
-}
-
-/* ARGSUSED */
-static int
-vdev_offlined(vdev_stat_t *vs, uint_t vsc)
-{
- return (vs->vs_state == VDEV_STATE_OFFLINE);
-}
-
-/* ARGSUSED */
-static int
-vdev_removed(vdev_stat_t *vs, uint_t vsc)
-{
- return (vs->vs_state == VDEV_STATE_REMOVED);
-}
-
-static int
-vdev_non_native_ashift(vdev_stat_t *vs, uint_t vsc)
-{
- return (VDEV_STAT_VALID(vs_physical_ashift, vsc) &&
- vs->vs_configured_ashift < vs->vs_physical_ashift);
-}
-
-/*
- * Detect if any leaf devices that have seen errors or could not be opened.
- */
-static boolean_t
-find_vdev_problem(nvlist_t *vdev, int (*func)(vdev_stat_t *, uint_t),
- boolean_t ignore_replacing)
-{
- nvlist_t **child;
- vdev_stat_t *vs;
- uint_t c, vsc, children;
-
- /*
- * Ignore problems within a 'replacing' vdev, since we're presumably in
- * the process of repairing any such errors, and don't want to call them
- * out again. We'll pick up the fact that a resilver is happening
- * later.
- */
- if (ignore_replacing == B_TRUE) {
- char *type;
-
- verify(nvlist_lookup_string(vdev, ZPOOL_CONFIG_TYPE,
- &type) == 0);
- if (strcmp(type, VDEV_TYPE_REPLACING) == 0)
- return (B_FALSE);
- }
-
- if (nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_CHILDREN, &child,
- &children) == 0) {
- for (c = 0; c < children; c++)
- if (find_vdev_problem(child[c], func, ignore_replacing))
- return (B_TRUE);
- } else {
- verify(nvlist_lookup_uint64_array(vdev, ZPOOL_CONFIG_VDEV_STATS,
- (uint64_t **)&vs, &vsc) == 0);
-
- if (func(vs, vsc) != 0)
- return (B_TRUE);
- }
-
- /*
- * Check any L2 cache devs
- */
- if (nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_L2CACHE, &child,
- &children) == 0) {
- for (c = 0; c < children; c++)
- if (find_vdev_problem(child[c], func, ignore_replacing))
- return (B_TRUE);
- }
-
- return (B_FALSE);
-}
-
-/*
- * Active pool health status.
- *
- * To determine the status for a pool, we make several passes over the config,
- * picking the most egregious error we find. In order of importance, we do the
- * following:
- *
- * - Check for a complete and valid configuration
- * - Look for any faulted or missing devices in a non-replicated config
- * - Check for any data errors
- * - Check for any faulted or missing devices in a replicated config
- * - Look for any devices showing errors
- * - Check for any resilvering devices
- *
- * There can obviously be multiple errors within a single pool, so this routine
- * only picks the most damaging of all the current errors to report.
- */
-static zpool_status_t
-check_status(nvlist_t *config, boolean_t isimport)
-{
- nvlist_t *nvroot;
- vdev_stat_t *vs;
- pool_scan_stat_t *ps = NULL;
- uint_t vsc, psc;
- uint64_t nerr;
- uint64_t version;
- uint64_t stateval;
- uint64_t suspended;
- uint64_t hostid = 0;
- unsigned long system_hostid = get_system_hostid();
-
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION,
- &version) == 0);
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
- verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS,
- (uint64_t **)&vs, &vsc) == 0);
- verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
- &stateval) == 0);
-
- /*
- * Currently resilvering a vdev
- */
- (void) nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_SCAN_STATS,
- (uint64_t **)&ps, &psc);
- if (ps != NULL && ps->pss_func == POOL_SCAN_RESILVER &&
- ps->pss_state == DSS_SCANNING)
- return (ZPOOL_STATUS_RESILVERING);
-
- /*
- * The multihost property is set and the pool may be active.
- */
- if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
- vs->vs_aux == VDEV_AUX_ACTIVE) {
- mmp_state_t mmp_state;
- nvlist_t *nvinfo;
-
- nvinfo = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO);
- mmp_state = fnvlist_lookup_uint64(nvinfo,
- ZPOOL_CONFIG_MMP_STATE);
-
- if (mmp_state == MMP_STATE_ACTIVE)
- return (ZPOOL_STATUS_HOSTID_ACTIVE);
- else if (mmp_state == MMP_STATE_NO_HOSTID)
- return (ZPOOL_STATUS_HOSTID_REQUIRED);
- else
- return (ZPOOL_STATUS_HOSTID_MISMATCH);
- }
-
- /*
- * Pool last accessed by another system.
- */
- (void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID, &hostid);
- if (hostid != 0 && (unsigned long)hostid != system_hostid &&
- stateval == POOL_STATE_ACTIVE)
- return (ZPOOL_STATUS_HOSTID_MISMATCH);
-
- /*
- * Newer on-disk version.
- */
- if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
- vs->vs_aux == VDEV_AUX_VERSION_NEWER)
- return (ZPOOL_STATUS_VERSION_NEWER);
-
- /*
- * Unsupported feature(s).
- */
- if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
- vs->vs_aux == VDEV_AUX_UNSUP_FEAT) {
- nvlist_t *nvinfo;
-
- verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO,
- &nvinfo) == 0);
- if (nvlist_exists(nvinfo, ZPOOL_CONFIG_CAN_RDONLY))
- return (ZPOOL_STATUS_UNSUP_FEAT_WRITE);
- return (ZPOOL_STATUS_UNSUP_FEAT_READ);
- }
-
- /*
- * Check that the config is complete.
- */
- if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
- vs->vs_aux == VDEV_AUX_BAD_GUID_SUM)
- return (ZPOOL_STATUS_BAD_GUID_SUM);
-
- /*
- * Check whether the pool has suspended.
- */
- if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_SUSPENDED,
- &suspended) == 0) {
- uint64_t reason;
-
- if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_SUSPENDED_REASON,
- &reason) == 0 && reason == ZIO_SUSPEND_MMP)
- return (ZPOOL_STATUS_IO_FAILURE_MMP);
-
- if (suspended == ZIO_FAILURE_MODE_CONTINUE)
- return (ZPOOL_STATUS_IO_FAILURE_CONTINUE);
- return (ZPOOL_STATUS_IO_FAILURE_WAIT);
- }
-
- /*
- * Could not read a log.
- */
- if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
- vs->vs_aux == VDEV_AUX_BAD_LOG) {
- return (ZPOOL_STATUS_BAD_LOG);
- }
-
- /*
- * Bad devices in non-replicated config.
- */
- if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
- find_vdev_problem(nvroot, vdev_faulted, B_TRUE))
- return (ZPOOL_STATUS_FAULTED_DEV_NR);
-
- if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
- find_vdev_problem(nvroot, vdev_missing, B_TRUE))
- return (ZPOOL_STATUS_MISSING_DEV_NR);
-
- if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
- find_vdev_problem(nvroot, vdev_broken, B_TRUE))
- return (ZPOOL_STATUS_CORRUPT_LABEL_NR);
-
- /*
- * Corrupted pool metadata
- */
- if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
- vs->vs_aux == VDEV_AUX_CORRUPT_DATA)
- return (ZPOOL_STATUS_CORRUPT_POOL);
-
- /*
- * Persistent data errors.
- */
- if (!isimport) {
- if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRCOUNT,
- &nerr) == 0 && nerr != 0)
- return (ZPOOL_STATUS_CORRUPT_DATA);
- }
-
- /*
- * Missing devices in a replicated config.
- */
- if (find_vdev_problem(nvroot, vdev_faulted, B_TRUE))
- return (ZPOOL_STATUS_FAULTED_DEV_R);
- if (find_vdev_problem(nvroot, vdev_missing, B_TRUE))
- return (ZPOOL_STATUS_MISSING_DEV_R);
- if (find_vdev_problem(nvroot, vdev_broken, B_TRUE))
- return (ZPOOL_STATUS_CORRUPT_LABEL_R);
-
- /*
- * Devices with errors
- */
- if (!isimport && find_vdev_problem(nvroot, vdev_errors, B_TRUE))
- return (ZPOOL_STATUS_FAILING_DEV);
-
- /*
- * Offlined devices
- */
- if (find_vdev_problem(nvroot, vdev_offlined, B_TRUE))
- return (ZPOOL_STATUS_OFFLINE_DEV);
-
- /*
- * Removed device
- */
- if (find_vdev_problem(nvroot, vdev_removed, B_TRUE))
- return (ZPOOL_STATUS_REMOVED_DEV);
-
- /*
- * Suboptimal, but usable, ashift configuration.
- */
- if (find_vdev_problem(nvroot, vdev_non_native_ashift, B_FALSE))
- return (ZPOOL_STATUS_NON_NATIVE_ASHIFT);
-
- /*
- * Outdated, but usable, version
- */
- if (SPA_VERSION_IS_SUPPORTED(version) && version != SPA_VERSION)
- return (ZPOOL_STATUS_VERSION_OLDER);
-
- /*
- * Usable pool with disabled features
- */
- if (version >= SPA_VERSION_FEATURES) {
- int i;
- nvlist_t *feat;
-
- if (isimport) {
- feat = fnvlist_lookup_nvlist(config,
- ZPOOL_CONFIG_LOAD_INFO);
- if (nvlist_exists(feat, ZPOOL_CONFIG_ENABLED_FEAT))
- feat = fnvlist_lookup_nvlist(feat,
- ZPOOL_CONFIG_ENABLED_FEAT);
- } else {
- feat = fnvlist_lookup_nvlist(config,
- ZPOOL_CONFIG_FEATURE_STATS);
- }
-
- for (i = 0; i < SPA_FEATURES; i++) {
- zfeature_info_t *fi = &spa_feature_table[i];
- if (!nvlist_exists(feat, fi->fi_guid))
- return (ZPOOL_STATUS_FEAT_DISABLED);
- }
- }
-
- return (ZPOOL_STATUS_OK);
-}
-
-zpool_status_t
-zpool_get_status(zpool_handle_t *zhp, char **msgid)
-{
- zpool_status_t ret = check_status(zhp->zpool_config, B_FALSE);
-
- if (ret >= NMSGID)
- *msgid = NULL;
- else
- *msgid = zfs_msgid_table[ret];
-
- return (ret);
-}
-
-zpool_status_t
-zpool_import_status(nvlist_t *config, char **msgid)
-{
- zpool_status_t ret = check_status(config, B_TRUE);
-
- if (ret >= NMSGID)
- *msgid = NULL;
- else
- *msgid = zfs_msgid_table[ret];
-
- return (ret);
-}
-
-static void
-dump_ddt_stat(const ddt_stat_t *dds, int h)
-{
- char refcnt[6];
- char blocks[6], lsize[6], psize[6], dsize[6];
- char ref_blocks[6], ref_lsize[6], ref_psize[6], ref_dsize[6];
-
- if (dds == NULL || dds->dds_blocks == 0)
- return;
-
- if (h == -1)
- (void) strcpy(refcnt, "Total");
- else
- zfs_nicenum(1ULL << h, refcnt, sizeof (refcnt));
-
- zfs_nicenum(dds->dds_blocks, blocks, sizeof (blocks));
- zfs_nicenum(dds->dds_lsize, lsize, sizeof (lsize));
- zfs_nicenum(dds->dds_psize, psize, sizeof (psize));
- zfs_nicenum(dds->dds_dsize, dsize, sizeof (dsize));
- zfs_nicenum(dds->dds_ref_blocks, ref_blocks, sizeof (ref_blocks));
- zfs_nicenum(dds->dds_ref_lsize, ref_lsize, sizeof (ref_lsize));
- zfs_nicenum(dds->dds_ref_psize, ref_psize, sizeof (ref_psize));
- zfs_nicenum(dds->dds_ref_dsize, ref_dsize, sizeof (ref_dsize));
-
- (void) printf("%6s %6s %5s %5s %5s %6s %5s %5s %5s\n",
- refcnt,
- blocks, lsize, psize, dsize,
- ref_blocks, ref_lsize, ref_psize, ref_dsize);
-}
-
-/*
- * Print the DDT histogram and the column totals.
- */
-void
-zpool_dump_ddt(const ddt_stat_t *dds_total, const ddt_histogram_t *ddh)
-{
- int h;
-
- (void) printf("\n");
-
- (void) printf("bucket "
- " allocated "
- " referenced \n");
- (void) printf("______ "
- "______________________________ "
- "______________________________\n");
-
- (void) printf("%6s %6s %5s %5s %5s %6s %5s %5s %5s\n",
- "refcnt",
- "blocks", "LSIZE", "PSIZE", "DSIZE",
- "blocks", "LSIZE", "PSIZE", "DSIZE");
-
- (void) printf("%6s %6s %5s %5s %5s %6s %5s %5s %5s\n",
- "------",
- "------", "-----", "-----", "-----",
- "------", "-----", "-----", "-----");
-
- for (h = 0; h < 64; h++)
- dump_ddt_stat(&ddh->ddh_stat[h], h);
-
- dump_ddt_stat(dds_total, -1);
-
- (void) printf("\n");
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c b/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c
deleted file mode 100644
index 4439bcbbee57f..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_util.c
+++ /dev/null
@@ -1,1661 +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 2019 Joyent, Inc.
- * Copyright (c) 2011, 2018 by Delphix. All rights reserved.
- * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
- * Copyright (c) 2017 Datto Inc.
- */
-
-/*
- * Internal utility routines for the ZFS library.
- */
-
-#include <sys/param.h>
-#include <sys/linker.h>
-#include <sys/module.h>
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <fcntl.h>
-#include <libintl.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <strings.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <math.h>
-#include <sys/mnttab.h>
-#include <sys/mntent.h>
-#include <sys/types.h>
-#include <libcmdutils.h>
-
-#include <libzfs.h>
-#include <libzfs_core.h>
-
-#include "libzfs_impl.h"
-#include "zfs_prop.h"
-#include "zfs_comutil.h"
-#include "zfeature_common.h"
-
-
-int
-libzfs_errno(libzfs_handle_t *hdl)
-{
- return (hdl->libzfs_error);
-}
-
-const char *
-libzfs_error_action(libzfs_handle_t *hdl)
-{
- return (hdl->libzfs_action);
-}
-
-const char *
-libzfs_error_description(libzfs_handle_t *hdl)
-{
- if (hdl->libzfs_desc[0] != '\0')
- return (hdl->libzfs_desc);
-
- switch (hdl->libzfs_error) {
- case EZFS_NOMEM:
- return (dgettext(TEXT_DOMAIN, "out of memory"));
- case EZFS_BADPROP:
- return (dgettext(TEXT_DOMAIN, "invalid property value"));
- case EZFS_PROPREADONLY:
- return (dgettext(TEXT_DOMAIN, "read-only property"));
- case EZFS_PROPTYPE:
- return (dgettext(TEXT_DOMAIN, "property doesn't apply to "
- "datasets of this type"));
- case EZFS_PROPNONINHERIT:
- return (dgettext(TEXT_DOMAIN, "property cannot be inherited"));
- case EZFS_PROPSPACE:
- return (dgettext(TEXT_DOMAIN, "invalid quota or reservation"));
- case EZFS_BADTYPE:
- return (dgettext(TEXT_DOMAIN, "operation not applicable to "
- "datasets of this type"));
- case EZFS_BUSY:
- return (dgettext(TEXT_DOMAIN, "pool or dataset is busy"));
- case EZFS_EXISTS:
- return (dgettext(TEXT_DOMAIN, "pool or dataset exists"));
- case EZFS_NOENT:
- return (dgettext(TEXT_DOMAIN, "no such pool or dataset"));
- case EZFS_BADSTREAM:
- return (dgettext(TEXT_DOMAIN, "invalid backup stream"));
- case EZFS_DSREADONLY:
- return (dgettext(TEXT_DOMAIN, "dataset is read-only"));
- case EZFS_VOLTOOBIG:
- return (dgettext(TEXT_DOMAIN, "volume size exceeds limit for "
- "this system"));
- case EZFS_INVALIDNAME:
- return (dgettext(TEXT_DOMAIN, "invalid name"));
- case EZFS_BADRESTORE:
- return (dgettext(TEXT_DOMAIN, "unable to restore to "
- "destination"));
- case EZFS_BADBACKUP:
- return (dgettext(TEXT_DOMAIN, "backup failed"));
- case EZFS_BADTARGET:
- return (dgettext(TEXT_DOMAIN, "invalid target vdev"));
- case EZFS_NODEVICE:
- return (dgettext(TEXT_DOMAIN, "no such device in pool"));
- case EZFS_BADDEV:
- return (dgettext(TEXT_DOMAIN, "invalid device"));
- case EZFS_NOREPLICAS:
- return (dgettext(TEXT_DOMAIN, "no valid replicas"));
- case EZFS_RESILVERING:
- return (dgettext(TEXT_DOMAIN, "currently resilvering"));
- case EZFS_BADVERSION:
- return (dgettext(TEXT_DOMAIN, "unsupported version or "
- "feature"));
- case EZFS_POOLUNAVAIL:
- return (dgettext(TEXT_DOMAIN, "pool is unavailable"));
- case EZFS_DEVOVERFLOW:
- return (dgettext(TEXT_DOMAIN, "too many devices in one vdev"));
- case EZFS_BADPATH:
- return (dgettext(TEXT_DOMAIN, "must be an absolute path"));
- case EZFS_CROSSTARGET:
- return (dgettext(TEXT_DOMAIN, "operation crosses datasets or "
- "pools"));
- case EZFS_ZONED:
- return (dgettext(TEXT_DOMAIN, "dataset in use by local zone"));
- case EZFS_MOUNTFAILED:
- return (dgettext(TEXT_DOMAIN, "mount failed"));
- case EZFS_UMOUNTFAILED:
- return (dgettext(TEXT_DOMAIN, "umount failed"));
- case EZFS_UNSHARENFSFAILED:
- return (dgettext(TEXT_DOMAIN, "unshare(1M) failed"));
- case EZFS_SHARENFSFAILED:
- return (dgettext(TEXT_DOMAIN, "share(1M) failed"));
- case EZFS_UNSHARESMBFAILED:
- return (dgettext(TEXT_DOMAIN, "smb remove share failed"));
- case EZFS_SHARESMBFAILED:
- return (dgettext(TEXT_DOMAIN, "smb add share failed"));
- case EZFS_PERM:
- return (dgettext(TEXT_DOMAIN, "permission denied"));
- case EZFS_NOSPC:
- return (dgettext(TEXT_DOMAIN, "out of space"));
- case EZFS_FAULT:
- return (dgettext(TEXT_DOMAIN, "bad address"));
- case EZFS_IO:
- return (dgettext(TEXT_DOMAIN, "I/O error"));
- case EZFS_INTR:
- return (dgettext(TEXT_DOMAIN, "signal received"));
- case EZFS_ISSPARE:
- return (dgettext(TEXT_DOMAIN, "device is reserved as a hot "
- "spare"));
- case EZFS_INVALCONFIG:
- return (dgettext(TEXT_DOMAIN, "invalid vdev configuration"));
- case EZFS_RECURSIVE:
- return (dgettext(TEXT_DOMAIN, "recursive dataset dependency"));
- case EZFS_NOHISTORY:
- return (dgettext(TEXT_DOMAIN, "no history available"));
- case EZFS_POOLPROPS:
- return (dgettext(TEXT_DOMAIN, "failed to retrieve "
- "pool properties"));
- case EZFS_POOL_NOTSUP:
- return (dgettext(TEXT_DOMAIN, "operation not supported "
- "on this type of pool"));
- case EZFS_POOL_INVALARG:
- return (dgettext(TEXT_DOMAIN, "invalid argument for "
- "this pool operation"));
- case EZFS_NAMETOOLONG:
- return (dgettext(TEXT_DOMAIN, "dataset name is too long"));
- case EZFS_OPENFAILED:
- return (dgettext(TEXT_DOMAIN, "open failed"));
- case EZFS_NOCAP:
- return (dgettext(TEXT_DOMAIN,
- "disk capacity information could not be retrieved"));
- case EZFS_LABELFAILED:
- return (dgettext(TEXT_DOMAIN, "write of label failed"));
- case EZFS_BADWHO:
- return (dgettext(TEXT_DOMAIN, "invalid user/group"));
- case EZFS_BADPERM:
- return (dgettext(TEXT_DOMAIN, "invalid permission"));
- case EZFS_BADPERMSET:
- return (dgettext(TEXT_DOMAIN, "invalid permission set name"));
- case EZFS_NODELEGATION:
- return (dgettext(TEXT_DOMAIN, "delegated administration is "
- "disabled on pool"));
- case EZFS_BADCACHE:
- return (dgettext(TEXT_DOMAIN, "invalid or missing cache file"));
- case EZFS_ISL2CACHE:
- return (dgettext(TEXT_DOMAIN, "device is in use as a cache"));
- case EZFS_VDEVNOTSUP:
- return (dgettext(TEXT_DOMAIN, "vdev specification is not "
- "supported"));
- case EZFS_NOTSUP:
- return (dgettext(TEXT_DOMAIN, "operation not supported "
- "on this dataset"));
- case EZFS_IOC_NOTSUPPORTED:
- return (dgettext(TEXT_DOMAIN, "operation not supported by "
- "zfs kernel module"));
- case EZFS_ACTIVE_SPARE:
- return (dgettext(TEXT_DOMAIN, "pool has active shared spare "
- "device"));
- case EZFS_UNPLAYED_LOGS:
- return (dgettext(TEXT_DOMAIN, "log device has unplayed intent "
- "logs"));
- case EZFS_REFTAG_RELE:
- return (dgettext(TEXT_DOMAIN, "no such tag on this dataset"));
- case EZFS_REFTAG_HOLD:
- return (dgettext(TEXT_DOMAIN, "tag already exists on this "
- "dataset"));
- case EZFS_TAGTOOLONG:
- return (dgettext(TEXT_DOMAIN, "tag too long"));
- case EZFS_PIPEFAILED:
- return (dgettext(TEXT_DOMAIN, "pipe create failed"));
- case EZFS_THREADCREATEFAILED:
- return (dgettext(TEXT_DOMAIN, "thread create failed"));
- case EZFS_POSTSPLIT_ONLINE:
- return (dgettext(TEXT_DOMAIN, "disk was split from this pool "
- "into a new one"));
- case EZFS_SCRUB_PAUSED:
- return (dgettext(TEXT_DOMAIN, "scrub is paused; "
- "use 'zpool scrub' to resume"));
- case EZFS_SCRUBBING:
- return (dgettext(TEXT_DOMAIN, "currently scrubbing; "
- "use 'zpool scrub -s' to cancel current scrub"));
- case EZFS_NO_SCRUB:
- return (dgettext(TEXT_DOMAIN, "there is no active scrub"));
- case EZFS_DIFF:
- return (dgettext(TEXT_DOMAIN, "unable to generate diffs"));
- case EZFS_DIFFDATA:
- return (dgettext(TEXT_DOMAIN, "invalid diff data"));
- case EZFS_POOLREADONLY:
- return (dgettext(TEXT_DOMAIN, "pool is read-only"));
- case EZFS_NO_PENDING:
- return (dgettext(TEXT_DOMAIN, "operation is not "
- "in progress"));
- case EZFS_CHECKPOINT_EXISTS:
- return (dgettext(TEXT_DOMAIN, "checkpoint exists"));
- case EZFS_DISCARDING_CHECKPOINT:
- return (dgettext(TEXT_DOMAIN, "currently discarding "
- "checkpoint"));
- case EZFS_NO_CHECKPOINT:
- return (dgettext(TEXT_DOMAIN, "checkpoint does not exist"));
- case EZFS_DEVRM_IN_PROGRESS:
- return (dgettext(TEXT_DOMAIN, "device removal in progress"));
- case EZFS_VDEV_TOO_BIG:
- return (dgettext(TEXT_DOMAIN, "device exceeds supported size"));
- case EZFS_ACTIVE_POOL:
- return (dgettext(TEXT_DOMAIN, "pool is imported on a "
- "different host"));
- case EZFS_TOOMANY:
- return (dgettext(TEXT_DOMAIN, "argument list too long"));
- case EZFS_INITIALIZING:
- return (dgettext(TEXT_DOMAIN, "currently initializing"));
- case EZFS_NO_INITIALIZE:
- return (dgettext(TEXT_DOMAIN, "there is no active "
- "initialization"));
- case EZFS_WRONG_PARENT:
- return (dgettext(TEXT_DOMAIN, "invalid parent dataset"));
- case EZFS_UNKNOWN:
- return (dgettext(TEXT_DOMAIN, "unknown error"));
- default:
- assert(hdl->libzfs_error == 0);
- return (dgettext(TEXT_DOMAIN, "no error"));
- }
-}
-
-/*PRINTFLIKE2*/
-void
-zfs_error_aux(libzfs_handle_t *hdl, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
-
- (void) vsnprintf(hdl->libzfs_desc, sizeof (hdl->libzfs_desc),
- fmt, ap);
- hdl->libzfs_desc_active = 1;
-
- va_end(ap);
-}
-
-static void
-zfs_verror(libzfs_handle_t *hdl, int error, const char *fmt, va_list ap)
-{
- (void) vsnprintf(hdl->libzfs_action, sizeof (hdl->libzfs_action),
- fmt, ap);
- hdl->libzfs_error = error;
-
- if (hdl->libzfs_desc_active)
- hdl->libzfs_desc_active = 0;
- else
- hdl->libzfs_desc[0] = '\0';
-
- if (hdl->libzfs_printerr) {
- if (error == EZFS_UNKNOWN) {
- (void) fprintf(stderr, dgettext(TEXT_DOMAIN, "internal "
- "error: %s\n"), libzfs_error_description(hdl));
- abort();
- }
-
- (void) fprintf(stderr, "%s: %s\n", hdl->libzfs_action,
- libzfs_error_description(hdl));
- if (error == EZFS_NOMEM)
- exit(1);
- }
-}
-
-int
-zfs_error(libzfs_handle_t *hdl, int error, const char *msg)
-{
- return (zfs_error_fmt(hdl, error, "%s", msg));
-}
-
-/*PRINTFLIKE3*/
-int
-zfs_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
-
- zfs_verror(hdl, error, fmt, ap);
-
- va_end(ap);
-
- return (-1);
-}
-
-static int
-zfs_common_error(libzfs_handle_t *hdl, int error, const char *fmt,
- va_list ap)
-{
- switch (error) {
- case EPERM:
- case EACCES:
- zfs_verror(hdl, EZFS_PERM, fmt, ap);
- return (-1);
-
- case ECANCELED:
- zfs_verror(hdl, EZFS_NODELEGATION, fmt, ap);
- return (-1);
-
- case EIO:
- zfs_verror(hdl, EZFS_IO, fmt, ap);
- return (-1);
-
- case EFAULT:
- zfs_verror(hdl, EZFS_FAULT, fmt, ap);
- return (-1);
-
- case EINTR:
- zfs_verror(hdl, EZFS_INTR, fmt, ap);
- return (-1);
- }
-
- return (0);
-}
-
-int
-zfs_standard_error(libzfs_handle_t *hdl, int error, const char *msg)
-{
- return (zfs_standard_error_fmt(hdl, error, "%s", msg));
-}
-
-/*PRINTFLIKE3*/
-int
-zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
-
- if (zfs_common_error(hdl, error, fmt, ap) != 0) {
- va_end(ap);
- return (-1);
- }
-
- switch (error) {
- case ENXIO:
- case ENODEV:
- case EPIPE:
- zfs_verror(hdl, EZFS_IO, fmt, ap);
- break;
-
- case ENOENT:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "dataset does not exist"));
- zfs_verror(hdl, EZFS_NOENT, fmt, ap);
- break;
-
- case ENOSPC:
- case EDQUOT:
- zfs_verror(hdl, EZFS_NOSPC, fmt, ap);
- va_end(ap);
- return (-1);
-
- case EEXIST:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "dataset already exists"));
- zfs_verror(hdl, EZFS_EXISTS, fmt, ap);
- break;
-
- case EBUSY:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "dataset is busy"));
- zfs_verror(hdl, EZFS_BUSY, fmt, ap);
- break;
- case EROFS:
- zfs_verror(hdl, EZFS_POOLREADONLY, fmt, ap);
- break;
- case ENAMETOOLONG:
- zfs_verror(hdl, EZFS_NAMETOOLONG, fmt, ap);
- break;
- case ENOTSUP:
- zfs_verror(hdl, EZFS_BADVERSION, fmt, ap);
- break;
- case EAGAIN:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool I/O is currently suspended"));
- zfs_verror(hdl, EZFS_POOLUNAVAIL, fmt, ap);
- break;
- case EREMOTEIO:
- zfs_verror(hdl, EZFS_ACTIVE_POOL, fmt, ap);
- break;
- case ZFS_ERR_IOC_CMD_UNAVAIL:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "the loaded zfs "
- "module does not support this operation. A reboot may "
- "be required to enable this operation."));
- zfs_verror(hdl, EZFS_IOC_NOTSUPPORTED, fmt, ap);
- break;
- case ZFS_ERR_IOC_ARG_UNAVAIL:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "the loaded zfs "
- "module does not support an option for this operation. "
- "A reboot may be required to enable this option."));
- zfs_verror(hdl, EZFS_IOC_NOTSUPPORTED, fmt, ap);
- break;
- case ZFS_ERR_IOC_ARG_REQUIRED:
- case ZFS_ERR_IOC_ARG_BADTYPE:
- zfs_verror(hdl, EZFS_IOC_NOTSUPPORTED, fmt, ap);
- break;
- default:
- zfs_error_aux(hdl, strerror(error));
- zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
- break;
- }
-
- va_end(ap);
- return (-1);
-}
-
-int
-zpool_standard_error(libzfs_handle_t *hdl, int error, const char *msg)
-{
- return (zpool_standard_error_fmt(hdl, error, "%s", msg));
-}
-
-/*PRINTFLIKE3*/
-int
-zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
-
- if (zfs_common_error(hdl, error, fmt, ap) != 0) {
- va_end(ap);
- return (-1);
- }
-
- switch (error) {
- case ENODEV:
- zfs_verror(hdl, EZFS_NODEVICE, fmt, ap);
- break;
-
- case ENOENT:
- zfs_error_aux(hdl,
- dgettext(TEXT_DOMAIN, "no such pool or dataset"));
- zfs_verror(hdl, EZFS_NOENT, fmt, ap);
- break;
-
- case EEXIST:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool already exists"));
- zfs_verror(hdl, EZFS_EXISTS, fmt, ap);
- break;
-
- case EBUSY:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool is busy"));
- zfs_verror(hdl, EZFS_BUSY, fmt, ap);
- break;
-
- case ENXIO:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "one or more devices is currently unavailable"));
- zfs_verror(hdl, EZFS_BADDEV, fmt, ap);
- break;
-
- case ENAMETOOLONG:
- zfs_verror(hdl, EZFS_DEVOVERFLOW, fmt, ap);
- break;
-
- case ENOTSUP:
- zfs_verror(hdl, EZFS_POOL_NOTSUP, fmt, ap);
- break;
-
- case EINVAL:
- zfs_verror(hdl, EZFS_POOL_INVALARG, fmt, ap);
- break;
-
- case ENOSPC:
- case EDQUOT:
- zfs_verror(hdl, EZFS_NOSPC, fmt, ap);
- va_end(ap);
- return (-1);
-
- case EAGAIN:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool I/O is currently suspended"));
- zfs_verror(hdl, EZFS_POOLUNAVAIL, fmt, ap);
- break;
-
- case EROFS:
- zfs_verror(hdl, EZFS_POOLREADONLY, fmt, ap);
- break;
- /* There is no pending operation to cancel */
- case ESRCH:
- zfs_verror(hdl, EZFS_NO_PENDING, fmt, ap);
- break;
- case EREMOTEIO:
- zfs_verror(hdl, EZFS_ACTIVE_POOL, fmt, ap);
- break;
- case ZFS_ERR_CHECKPOINT_EXISTS:
- zfs_verror(hdl, EZFS_CHECKPOINT_EXISTS, fmt, ap);
- break;
- case ZFS_ERR_DISCARDING_CHECKPOINT:
- zfs_verror(hdl, EZFS_DISCARDING_CHECKPOINT, fmt, ap);
- break;
- case ZFS_ERR_NO_CHECKPOINT:
- zfs_verror(hdl, EZFS_NO_CHECKPOINT, fmt, ap);
- break;
- case ZFS_ERR_DEVRM_IN_PROGRESS:
- zfs_verror(hdl, EZFS_DEVRM_IN_PROGRESS, fmt, ap);
- break;
- case ZFS_ERR_VDEV_TOO_BIG:
- zfs_verror(hdl, EZFS_VDEV_TOO_BIG, fmt, ap);
- break;
- case ZFS_ERR_WRONG_PARENT:
- zfs_verror(hdl, EZFS_WRONG_PARENT, fmt, ap);
- break;
- case ZFS_ERR_IOC_CMD_UNAVAIL:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "the loaded zfs "
- "module does not support this operation. A reboot may "
- "be required to enable this operation."));
- zfs_verror(hdl, EZFS_IOC_NOTSUPPORTED, fmt, ap);
- break;
- case ZFS_ERR_IOC_ARG_UNAVAIL:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "the loaded zfs "
- "module does not support an option for this operation. "
- "A reboot may be required to enable this option."));
- zfs_verror(hdl, EZFS_IOC_NOTSUPPORTED, fmt, ap);
- break;
- case ZFS_ERR_IOC_ARG_REQUIRED:
- case ZFS_ERR_IOC_ARG_BADTYPE:
- zfs_verror(hdl, EZFS_IOC_NOTSUPPORTED, fmt, ap);
- break;
- default:
- zfs_error_aux(hdl, strerror(error));
- zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
- }
-
- va_end(ap);
- return (-1);
-}
-
-/*
- * Display an out of memory error message and abort the current program.
- */
-int
-no_memory(libzfs_handle_t *hdl)
-{
- return (zfs_error(hdl, EZFS_NOMEM, "internal error"));
-}
-
-/*
- * A safe form of malloc() which will die if the allocation fails.
- */
-void *
-zfs_alloc(libzfs_handle_t *hdl, size_t size)
-{
- void *data;
-
- if ((data = calloc(1, size)) == NULL)
- (void) no_memory(hdl);
-
- return (data);
-}
-
-/*
- * A safe form of asprintf() which will die if the allocation fails.
- */
-/*PRINTFLIKE2*/
-char *
-zfs_asprintf(libzfs_handle_t *hdl, const char *fmt, ...)
-{
- va_list ap;
- char *ret;
- int err;
-
- va_start(ap, fmt);
-
- err = vasprintf(&ret, fmt, ap);
-
- va_end(ap);
-
- if (err < 0)
- (void) no_memory(hdl);
-
- return (ret);
-}
-
-/*
- * A safe form of realloc(), which also zeroes newly allocated space.
- */
-void *
-zfs_realloc(libzfs_handle_t *hdl, void *ptr, size_t oldsize, size_t newsize)
-{
- void *ret;
-
- if ((ret = realloc(ptr, newsize)) == NULL) {
- (void) no_memory(hdl);
- return (NULL);
- }
-
- bzero((char *)ret + oldsize, (newsize - oldsize));
- return (ret);
-}
-
-/*
- * A safe form of strdup() which will die if the allocation fails.
- */
-char *
-zfs_strdup(libzfs_handle_t *hdl, const char *str)
-{
- char *ret;
-
- if ((ret = strdup(str)) == NULL)
- (void) no_memory(hdl);
-
- return (ret);
-}
-
-/*
- * Convert a number to an appropriately human-readable output.
- */
-void
-zfs_nicenum(uint64_t num, char *buf, size_t buflen)
-{
- nicenum(num, buf, buflen);
-}
-
-void
-libzfs_print_on_error(libzfs_handle_t *hdl, boolean_t printerr)
-{
- hdl->libzfs_printerr = printerr;
-}
-
-static int
-libzfs_load(void)
-{
- int error;
-
- if (modfind("zfs") < 0) {
- /* Not present in kernel, try loading it. */
- if (kldload("zfs") < 0 || modfind("zfs") < 0) {
- if (errno != EEXIST)
- return (-1);
- }
- }
- return (0);
-}
-
-libzfs_handle_t *
-libzfs_init(void)
-{
- libzfs_handle_t *hdl;
-
- if ((hdl = calloc(1, sizeof (libzfs_handle_t))) == NULL) {
- return (NULL);
- }
-
- if (libzfs_load() < 0) {
- free(hdl);
- return (NULL);
- }
-
- if ((hdl->libzfs_fd = open(ZFS_DEV, O_RDWR)) < 0) {
- free(hdl);
- return (NULL);
- }
-
- if ((hdl->libzfs_mnttab = fopen(MNTTAB, "r")) == NULL) {
- (void) close(hdl->libzfs_fd);
- free(hdl);
- return (NULL);
- }
-
- hdl->libzfs_sharetab = fopen(ZFS_EXPORTS_PATH, "r");
-
- if (libzfs_core_init() != 0) {
- (void) close(hdl->libzfs_fd);
- (void) fclose(hdl->libzfs_mnttab);
- (void) fclose(hdl->libzfs_sharetab);
- free(hdl);
- return (NULL);
- }
-
- zfs_prop_init();
- zpool_prop_init();
- zpool_feature_init();
- libzfs_mnttab_init(hdl);
-
- if (getenv("ZFS_PROP_DEBUG") != NULL) {
- hdl->libzfs_prop_debug = B_TRUE;
- }
-
- return (hdl);
-}
-
-void
-libzfs_fini(libzfs_handle_t *hdl)
-{
- (void) close(hdl->libzfs_fd);
- if (hdl->libzfs_mnttab)
- (void) fclose(hdl->libzfs_mnttab);
- if (hdl->libzfs_sharetab)
- (void) fclose(hdl->libzfs_sharetab);
- zfs_uninit_libshare(hdl);
- zpool_free_handles(hdl);
-#ifdef illumos
- libzfs_fru_clear(hdl, B_TRUE);
-#endif
- namespace_clear(hdl);
- libzfs_mnttab_fini(hdl);
- libzfs_core_fini();
- free(hdl);
-}
-
-libzfs_handle_t *
-zpool_get_handle(zpool_handle_t *zhp)
-{
- return (zhp->zpool_hdl);
-}
-
-libzfs_handle_t *
-zfs_get_handle(zfs_handle_t *zhp)
-{
- return (zhp->zfs_hdl);
-}
-
-zpool_handle_t *
-zfs_get_pool_handle(const zfs_handle_t *zhp)
-{
- return (zhp->zpool_hdl);
-}
-
-/*
- * Given a name, determine whether or not it's a valid path
- * (starts with '/' or "./"). If so, walk the mnttab trying
- * to match the device number. If not, treat the path as an
- * fs/vol/snap/bkmark name.
- */
-zfs_handle_t *
-zfs_path_to_zhandle(libzfs_handle_t *hdl, char *path, zfs_type_t argtype)
-{
- struct stat64 statbuf;
- struct extmnttab entry;
- int ret;
-
- if (path[0] != '/' && strncmp(path, "./", strlen("./")) != 0) {
- /*
- * It's not a valid path, assume it's a name of type 'argtype'.
- */
- return (zfs_open(hdl, path, argtype));
- }
-
- if (stat64(path, &statbuf) != 0) {
- (void) fprintf(stderr, "%s: %s\n", path, strerror(errno));
- return (NULL);
- }
-
-#ifdef illumos
- rewind(hdl->libzfs_mnttab);
- while ((ret = getextmntent(hdl->libzfs_mnttab, &entry, 0)) == 0) {
- if (makedevice(entry.mnt_major, entry.mnt_minor) ==
- statbuf.st_dev) {
- break;
- }
- }
-#else
- {
- struct statfs sfs;
-
- ret = statfs(path, &sfs);
- if (ret == 0)
- statfs2mnttab(&sfs, &entry);
- else {
- (void) fprintf(stderr, "%s: %s\n", path,
- strerror(errno));
- }
- }
-#endif /* illumos */
- if (ret != 0) {
- return (NULL);
- }
-
- if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0) {
- (void) fprintf(stderr, gettext("'%s': not a ZFS filesystem\n"),
- path);
- return (NULL);
- }
-
- return (zfs_open(hdl, entry.mnt_special, ZFS_TYPE_FILESYSTEM));
-}
-
-/*
- * Initialize the zc_nvlist_dst member to prepare for receiving an nvlist from
- * an ioctl().
- */
-int
-zcmd_alloc_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, size_t len)
-{
- if (len == 0)
- len = 16 * 1024;
- zc->zc_nvlist_dst_size = len;
- zc->zc_nvlist_dst =
- (uint64_t)(uintptr_t)zfs_alloc(hdl, zc->zc_nvlist_dst_size);
- if (zc->zc_nvlist_dst == 0)
- return (-1);
-
- return (0);
-}
-
-/*
- * Called when an ioctl() which returns an nvlist fails with ENOMEM. This will
- * expand the nvlist to the size specified in 'zc_nvlist_dst_size', which was
- * filled in by the kernel to indicate the actual required size.
- */
-int
-zcmd_expand_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc)
-{
- free((void *)(uintptr_t)zc->zc_nvlist_dst);
- zc->zc_nvlist_dst =
- (uint64_t)(uintptr_t)zfs_alloc(hdl, zc->zc_nvlist_dst_size);
- if (zc->zc_nvlist_dst == 0)
- return (-1);
-
- return (0);
-}
-
-/*
- * Called to free the src and dst nvlists stored in the command structure.
- */
-void
-zcmd_free_nvlists(zfs_cmd_t *zc)
-{
- free((void *)(uintptr_t)zc->zc_nvlist_conf);
- free((void *)(uintptr_t)zc->zc_nvlist_src);
- free((void *)(uintptr_t)zc->zc_nvlist_dst);
- zc->zc_nvlist_conf = NULL;
- zc->zc_nvlist_src = NULL;
- zc->zc_nvlist_dst = NULL;
-}
-
-static int
-zcmd_write_nvlist_com(libzfs_handle_t *hdl, uint64_t *outnv, uint64_t *outlen,
- nvlist_t *nvl)
-{
- char *packed;
- size_t len;
-
- verify(nvlist_size(nvl, &len, NV_ENCODE_NATIVE) == 0);
-
- if ((packed = zfs_alloc(hdl, len)) == NULL)
- return (-1);
-
- verify(nvlist_pack(nvl, &packed, &len, NV_ENCODE_NATIVE, 0) == 0);
-
- *outnv = (uint64_t)(uintptr_t)packed;
- *outlen = len;
-
- return (0);
-}
-
-int
-zcmd_write_conf_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t *nvl)
-{
- return (zcmd_write_nvlist_com(hdl, &zc->zc_nvlist_conf,
- &zc->zc_nvlist_conf_size, nvl));
-}
-
-int
-zcmd_write_src_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t *nvl)
-{
- return (zcmd_write_nvlist_com(hdl, &zc->zc_nvlist_src,
- &zc->zc_nvlist_src_size, nvl));
-}
-
-/*
- * Unpacks an nvlist from the ZFS ioctl command structure.
- */
-int
-zcmd_read_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, nvlist_t **nvlp)
-{
- if (nvlist_unpack((void *)(uintptr_t)zc->zc_nvlist_dst,
- zc->zc_nvlist_dst_size, nvlp, 0) != 0)
- return (no_memory(hdl));
-
- return (0);
-}
-
-int
-zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
-{
- return (ioctl(hdl->libzfs_fd, request, zc));
-}
-
-/*
- * ================================================================
- * API shared by zfs and zpool property management
- * ================================================================
- */
-
-static void
-zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
-{
- zprop_list_t *pl = cbp->cb_proplist;
- int i;
- char *title;
- size_t len;
-
- cbp->cb_first = B_FALSE;
- if (cbp->cb_scripted)
- return;
-
- /*
- * Start with the length of the column headers.
- */
- cbp->cb_colwidths[GET_COL_NAME] = strlen(dgettext(TEXT_DOMAIN, "NAME"));
- cbp->cb_colwidths[GET_COL_PROPERTY] = strlen(dgettext(TEXT_DOMAIN,
- "PROPERTY"));
- cbp->cb_colwidths[GET_COL_VALUE] = strlen(dgettext(TEXT_DOMAIN,
- "VALUE"));
- cbp->cb_colwidths[GET_COL_RECVD] = strlen(dgettext(TEXT_DOMAIN,
- "RECEIVED"));
- cbp->cb_colwidths[GET_COL_SOURCE] = strlen(dgettext(TEXT_DOMAIN,
- "SOURCE"));
-
- /* first property is always NAME */
- assert(cbp->cb_proplist->pl_prop ==
- ((type == ZFS_TYPE_POOL) ? ZPOOL_PROP_NAME : ZFS_PROP_NAME));
-
- /*
- * Go through and calculate the widths for each column. For the
- * 'source' column, we kludge it up by taking the worst-case scenario of
- * inheriting from the longest name. This is acceptable because in the
- * majority of cases 'SOURCE' is the last column displayed, and we don't
- * use the width anyway. Note that the 'VALUE' column can be oversized,
- * if the name of the property is much longer than any values we find.
- */
- for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) {
- /*
- * 'PROPERTY' column
- */
- if (pl->pl_prop != ZPROP_INVAL) {
- const char *propname = (type == ZFS_TYPE_POOL) ?
- zpool_prop_to_name(pl->pl_prop) :
- zfs_prop_to_name(pl->pl_prop);
-
- len = strlen(propname);
- if (len > cbp->cb_colwidths[GET_COL_PROPERTY])
- cbp->cb_colwidths[GET_COL_PROPERTY] = len;
- } else {
- len = strlen(pl->pl_user_prop);
- if (len > cbp->cb_colwidths[GET_COL_PROPERTY])
- cbp->cb_colwidths[GET_COL_PROPERTY] = len;
- }
-
- /*
- * 'VALUE' column. The first property is always the 'name'
- * property that was tacked on either by /sbin/zfs's
- * zfs_do_get() or when calling zprop_expand_list(), so we
- * ignore its width. If the user specified the name property
- * to display, then it will be later in the list in any case.
- */
- if (pl != cbp->cb_proplist &&
- pl->pl_width > cbp->cb_colwidths[GET_COL_VALUE])
- cbp->cb_colwidths[GET_COL_VALUE] = pl->pl_width;
-
- /* 'RECEIVED' column. */
- if (pl != cbp->cb_proplist &&
- pl->pl_recvd_width > cbp->cb_colwidths[GET_COL_RECVD])
- cbp->cb_colwidths[GET_COL_RECVD] = pl->pl_recvd_width;
-
- /*
- * 'NAME' and 'SOURCE' columns
- */
- if (pl->pl_prop == (type == ZFS_TYPE_POOL ? ZPOOL_PROP_NAME :
- ZFS_PROP_NAME) &&
- pl->pl_width > cbp->cb_colwidths[GET_COL_NAME]) {
- cbp->cb_colwidths[GET_COL_NAME] = pl->pl_width;
- cbp->cb_colwidths[GET_COL_SOURCE] = pl->pl_width +
- strlen(dgettext(TEXT_DOMAIN, "inherited from"));
- }
- }
-
- /*
- * Now go through and print the headers.
- */
- for (i = 0; i < ZFS_GET_NCOLS; i++) {
- switch (cbp->cb_columns[i]) {
- case GET_COL_NAME:
- title = dgettext(TEXT_DOMAIN, "NAME");
- break;
- case GET_COL_PROPERTY:
- title = dgettext(TEXT_DOMAIN, "PROPERTY");
- break;
- case GET_COL_VALUE:
- title = dgettext(TEXT_DOMAIN, "VALUE");
- break;
- case GET_COL_RECVD:
- title = dgettext(TEXT_DOMAIN, "RECEIVED");
- break;
- case GET_COL_SOURCE:
- title = dgettext(TEXT_DOMAIN, "SOURCE");
- break;
- default:
- title = NULL;
- }
-
- if (title != NULL) {
- if (i == (ZFS_GET_NCOLS - 1) ||
- cbp->cb_columns[i + 1] == GET_COL_NONE)
- (void) printf("%s", title);
- else
- (void) printf("%-*s ",
- cbp->cb_colwidths[cbp->cb_columns[i]],
- title);
- }
- }
- (void) printf("\n");
-}
-
-/*
- * Display a single line of output, according to the settings in the callback
- * structure.
- */
-void
-zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp,
- const char *propname, const char *value, zprop_source_t sourcetype,
- const char *source, const char *recvd_value)
-{
- int i;
- const char *str = NULL;
- char buf[128];
-
- /*
- * Ignore those source types that the user has chosen to ignore.
- */
- if ((sourcetype & cbp->cb_sources) == 0)
- return;
-
- if (cbp->cb_first)
- zprop_print_headers(cbp, cbp->cb_type);
-
- for (i = 0; i < ZFS_GET_NCOLS; i++) {
- switch (cbp->cb_columns[i]) {
- case GET_COL_NAME:
- str = name;
- break;
-
- case GET_COL_PROPERTY:
- str = propname;
- break;
-
- case GET_COL_VALUE:
- str = value;
- break;
-
- case GET_COL_SOURCE:
- switch (sourcetype) {
- case ZPROP_SRC_NONE:
- str = "-";
- break;
-
- case ZPROP_SRC_DEFAULT:
- str = "default";
- break;
-
- case ZPROP_SRC_LOCAL:
- str = "local";
- break;
-
- case ZPROP_SRC_TEMPORARY:
- str = "temporary";
- break;
-
- case ZPROP_SRC_INHERITED:
- (void) snprintf(buf, sizeof (buf),
- "inherited from %s", source);
- str = buf;
- break;
- case ZPROP_SRC_RECEIVED:
- str = "received";
- break;
-
- default:
- str = NULL;
- assert(!"unhandled zprop_source_t");
- }
- break;
-
- case GET_COL_RECVD:
- str = (recvd_value == NULL ? "-" : recvd_value);
- break;
-
- default:
- continue;
- }
-
- if (cbp->cb_columns[i + 1] == GET_COL_NONE)
- (void) printf("%s", str);
- else if (cbp->cb_scripted)
- (void) printf("%s\t", str);
- else
- (void) printf("%-*s ",
- cbp->cb_colwidths[cbp->cb_columns[i]],
- str);
- }
-
- (void) printf("\n");
-}
-
-/*
- * Given a numeric suffix, convert the value into a number of bits that the
- * resulting value must be shifted.
- */
-static int
-str2shift(libzfs_handle_t *hdl, const char *buf)
-{
- const char *ends = "BKMGTPEZ";
- int i;
-
- if (buf[0] == '\0')
- return (0);
- for (i = 0; i < strlen(ends); i++) {
- if (toupper(buf[0]) == ends[i])
- break;
- }
- if (i == strlen(ends)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid numeric suffix '%s'"), buf);
- return (-1);
- }
-
- /*
- * We want to allow trailing 'b' characters for 'GB' or 'Mb'. But don't
- * allow 'BB' - that's just weird.
- */
- if (buf[1] == '\0' || (toupper(buf[1]) == 'B' && buf[2] == '\0' &&
- toupper(buf[0]) != 'B'))
- return (10*i);
-
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid numeric suffix '%s'"), buf);
- return (-1);
-}
-
-/*
- * Convert a string of the form '100G' into a real number. Used when setting
- * properties or creating a volume. 'buf' is used to place an extended error
- * message for the caller to use.
- */
-int
-zfs_nicestrtonum(libzfs_handle_t *hdl, const char *value, uint64_t *num)
-{
- char *end;
- int shift;
-
- *num = 0;
-
- /* Check to see if this looks like a number. */
- if ((value[0] < '0' || value[0] > '9') && value[0] != '.') {
- if (hdl)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "bad numeric value '%s'"), value);
- return (-1);
- }
-
- /* Rely on strtoull() to process the numeric portion. */
- errno = 0;
- *num = strtoull(value, &end, 10);
-
- /*
- * Check for ERANGE, which indicates that the value is too large to fit
- * in a 64-bit value.
- */
- if (errno == ERANGE) {
- if (hdl)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "numeric value is too large"));
- return (-1);
- }
-
- /*
- * If we have a decimal value, then do the computation with floating
- * point arithmetic. Otherwise, use standard arithmetic.
- */
- if (*end == '.') {
- double fval = strtod(value, &end);
-
- if ((shift = str2shift(hdl, end)) == -1)
- return (-1);
-
- fval *= pow(2, shift);
-
- if (fval > UINT64_MAX) {
- if (hdl)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "numeric value is too large"));
- return (-1);
- }
-
- *num = (uint64_t)fval;
- } else {
- if ((shift = str2shift(hdl, end)) == -1)
- return (-1);
-
- /* Check for overflow */
- if (shift >= 64 || (*num << shift) >> shift != *num) {
- if (hdl)
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "numeric value is too large"));
- return (-1);
- }
-
- *num <<= shift;
- }
-
- return (0);
-}
-
-/*
- * Given a propname=value nvpair to set, parse any numeric properties
- * (index, boolean, etc) if they are specified as strings and add the
- * resulting nvpair to the returned nvlist.
- *
- * At the DSL layer, all properties are either 64-bit numbers or strings.
- * We want the user to be able to ignore this fact and specify properties
- * as native values (numbers, for example) or as strings (to simplify
- * command line utilities). This also handles converting index types
- * (compression, checksum, etc) from strings to their on-disk index.
- */
-int
-zprop_parse_value(libzfs_handle_t *hdl, nvpair_t *elem, int prop,
- zfs_type_t type, nvlist_t *ret, char **svalp, uint64_t *ivalp,
- const char *errbuf)
-{
- data_type_t datatype = nvpair_type(elem);
- zprop_type_t proptype;
- const char *propname;
- char *value;
- boolean_t isnone = B_FALSE;
- boolean_t isauto = B_FALSE;
-
- if (type == ZFS_TYPE_POOL) {
- proptype = zpool_prop_get_type(prop);
- propname = zpool_prop_to_name(prop);
- } else {
- proptype = zfs_prop_get_type(prop);
- propname = zfs_prop_to_name(prop);
- }
-
- /*
- * Convert any properties to the internal DSL value types.
- */
- *svalp = NULL;
- *ivalp = 0;
-
- switch (proptype) {
- case PROP_TYPE_STRING:
- if (datatype != DATA_TYPE_STRING) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a string"), nvpair_name(elem));
- goto error;
- }
- (void) nvpair_value_string(elem, svalp);
- if (strlen(*svalp) >= ZFS_MAXPROPLEN) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' is too long"), nvpair_name(elem));
- goto error;
- }
- break;
-
- case PROP_TYPE_NUMBER:
- if (datatype == DATA_TYPE_STRING) {
- (void) nvpair_value_string(elem, &value);
- if (strcmp(value, "none") == 0) {
- isnone = B_TRUE;
- } else if (strcmp(value, "auto") == 0) {
- isauto = B_TRUE;
- } else if (zfs_nicestrtonum(hdl, value, ivalp) != 0) {
- goto error;
- }
- } else if (datatype == DATA_TYPE_UINT64) {
- (void) nvpair_value_uint64(elem, ivalp);
- } else {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a number"), nvpair_name(elem));
- goto error;
- }
-
- /*
- * Quota special: force 'none' and don't allow 0.
- */
- if ((type & ZFS_TYPE_DATASET) && *ivalp == 0 && !isnone &&
- (prop == ZFS_PROP_QUOTA || prop == ZFS_PROP_REFQUOTA)) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "use 'none' to disable quota/refquota"));
- goto error;
- }
-
- /*
- * Special handling for "*_limit=none". In this case it's not
- * 0 but UINT64_MAX.
- */
- if ((type & ZFS_TYPE_DATASET) && isnone &&
- (prop == ZFS_PROP_FILESYSTEM_LIMIT ||
- prop == ZFS_PROP_SNAPSHOT_LIMIT)) {
- *ivalp = UINT64_MAX;
- }
-
- /*
- * Special handling for setting 'refreservation' to 'auto'. Use
- * UINT64_MAX to tell the caller to use zfs_fix_auto_resv().
- * 'auto' is only allowed on volumes.
- */
- if (isauto) {
- switch (prop) {
- case ZFS_PROP_REFRESERVATION:
- if ((type & ZFS_TYPE_VOLUME) == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s=auto' only allowed on "
- "volumes"), nvpair_name(elem));
- goto error;
- }
- *ivalp = UINT64_MAX;
- break;
- default:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'auto' is invalid value for '%s'"),
- nvpair_name(elem));
- goto error;
- }
- }
-
- break;
-
- case PROP_TYPE_INDEX:
- if (datatype != DATA_TYPE_STRING) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be a string"), nvpair_name(elem));
- goto error;
- }
-
- (void) nvpair_value_string(elem, &value);
-
- if (zprop_string_to_index(prop, value, ivalp, type) != 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "'%s' must be one of '%s'"), propname,
- zprop_values(prop, type));
- goto error;
- }
- break;
-
- default:
- abort();
- }
-
- /*
- * Add the result to our return set of properties.
- */
- if (*svalp != NULL) {
- if (nvlist_add_string(ret, propname, *svalp) != 0) {
- (void) no_memory(hdl);
- return (-1);
- }
- } else {
- if (nvlist_add_uint64(ret, propname, *ivalp) != 0) {
- (void) no_memory(hdl);
- return (-1);
- }
- }
-
- return (0);
-error:
- (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
- return (-1);
-}
-
-static int
-addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
- zfs_type_t type)
-{
- int prop;
- zprop_list_t *entry;
-
- prop = zprop_name_to_prop(propname, type);
-
- if (prop != ZPROP_INVAL && !zprop_valid_for_type(prop, type))
- prop = ZPROP_INVAL;
-
- /*
- * When no property table entry can be found, return failure if
- * this is a pool property or if this isn't a user-defined
- * dataset property,
- */
- if (prop == ZPROP_INVAL && ((type == ZFS_TYPE_POOL &&
- !zpool_prop_feature(propname) &&
- !zpool_prop_unsupported(propname)) ||
- (type == ZFS_TYPE_DATASET && !zfs_prop_user(propname) &&
- !zfs_prop_userquota(propname) && !zfs_prop_written(propname)))) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "invalid property '%s'"), propname);
- return (zfs_error(hdl, EZFS_BADPROP,
- dgettext(TEXT_DOMAIN, "bad property list")));
- }
-
- if ((entry = zfs_alloc(hdl, sizeof (zprop_list_t))) == NULL)
- return (-1);
-
- entry->pl_prop = prop;
- if (prop == ZPROP_INVAL) {
- if ((entry->pl_user_prop = zfs_strdup(hdl, propname)) ==
- NULL) {
- free(entry);
- return (-1);
- }
- entry->pl_width = strlen(propname);
- } else {
- entry->pl_width = zprop_width(prop, &entry->pl_fixed,
- type);
- }
-
- *listp = entry;
-
- return (0);
-}
-
-/*
- * Given a comma-separated list of properties, construct a property list
- * containing both user-defined and native properties. This function will
- * return a NULL list if 'all' is specified, which can later be expanded
- * by zprop_expand_list().
- */
-int
-zprop_get_list(libzfs_handle_t *hdl, char *props, zprop_list_t **listp,
- zfs_type_t type)
-{
- *listp = NULL;
-
- /*
- * If 'all' is specified, return a NULL list.
- */
- if (strcmp(props, "all") == 0)
- return (0);
-
- /*
- * If no props were specified, return an error.
- */
- if (props[0] == '\0') {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "no properties specified"));
- return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN,
- "bad property list")));
- }
-
- /*
- * It would be nice to use getsubopt() here, but the inclusion of column
- * aliases makes this more effort than it's worth.
- */
- while (*props != '\0') {
- size_t len;
- char *p;
- char c;
-
- if ((p = strchr(props, ',')) == NULL) {
- len = strlen(props);
- p = props + len;
- } else {
- len = p - props;
- }
-
- /*
- * Check for empty options.
- */
- if (len == 0) {
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "empty property name"));
- return (zfs_error(hdl, EZFS_BADPROP,
- dgettext(TEXT_DOMAIN, "bad property list")));
- }
-
- /*
- * Check all regular property names.
- */
- c = props[len];
- props[len] = '\0';
-
- if (strcmp(props, "space") == 0) {
- static char *spaceprops[] = {
- "name", "avail", "used", "usedbysnapshots",
- "usedbydataset", "usedbyrefreservation",
- "usedbychildren", NULL
- };
- int i;
-
- for (i = 0; spaceprops[i]; i++) {
- if (addlist(hdl, spaceprops[i], listp, type))
- return (-1);
- listp = &(*listp)->pl_next;
- }
- } else {
- if (addlist(hdl, props, listp, type))
- return (-1);
- listp = &(*listp)->pl_next;
- }
-
- props = p;
- if (c == ',')
- props++;
- }
-
- return (0);
-}
-
-void
-zprop_free_list(zprop_list_t *pl)
-{
- zprop_list_t *next;
-
- while (pl != NULL) {
- next = pl->pl_next;
- free(pl->pl_user_prop);
- free(pl);
- pl = next;
- }
-}
-
-typedef struct expand_data {
- zprop_list_t **last;
- libzfs_handle_t *hdl;
- zfs_type_t type;
-} expand_data_t;
-
-int
-zprop_expand_list_cb(int prop, void *cb)
-{
- zprop_list_t *entry;
- expand_data_t *edp = cb;
-
- if ((entry = zfs_alloc(edp->hdl, sizeof (zprop_list_t))) == NULL)
- return (ZPROP_INVAL);
-
- entry->pl_prop = prop;
- entry->pl_width = zprop_width(prop, &entry->pl_fixed, edp->type);
- entry->pl_all = B_TRUE;
-
- *(edp->last) = entry;
- edp->last = &entry->pl_next;
-
- return (ZPROP_CONT);
-}
-
-int
-zprop_expand_list(libzfs_handle_t *hdl, zprop_list_t **plp, zfs_type_t type)
-{
- zprop_list_t *entry;
- zprop_list_t **last;
- expand_data_t exp;
-
- if (*plp == NULL) {
- /*
- * If this is the very first time we've been called for an 'all'
- * specification, expand the list to include all native
- * properties.
- */
- last = plp;
-
- exp.last = last;
- exp.hdl = hdl;
- exp.type = type;
-
- if (zprop_iter_common(zprop_expand_list_cb, &exp, B_FALSE,
- B_FALSE, type) == ZPROP_INVAL)
- return (-1);
-
- /*
- * Add 'name' to the beginning of the list, which is handled
- * specially.
- */
- if ((entry = zfs_alloc(hdl, sizeof (zprop_list_t))) == NULL)
- return (-1);
-
- entry->pl_prop = (type == ZFS_TYPE_POOL) ? ZPOOL_PROP_NAME :
- ZFS_PROP_NAME;
- entry->pl_width = zprop_width(entry->pl_prop,
- &entry->pl_fixed, type);
- entry->pl_all = B_TRUE;
- entry->pl_next = *plp;
- *plp = entry;
- }
- return (0);
-}
-
-int
-zprop_iter(zprop_func func, void *cb, boolean_t show_all, boolean_t ordered,
- zfs_type_t type)
-{
- return (zprop_iter_common(func, cb, show_all, ordered, type));
-}
-
-ulong_t
-get_system_hostid(void)
-{
- char *env;
-
- /*
- * Allow the hostid to be subverted for testing.
- */
- env = getenv("ZFS_HOSTID");
- if (env) {
- ulong_t hostid = strtoull(env, NULL, 16);
- return (hostid & 0xFFFFFFFF);
- }
-
- return (gethostid());
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c b/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c
deleted file mode 100644
index 2a6b5cc5927cd..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.c
+++ /dev/null
@@ -1,1234 +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) 2012, 2020 by Delphix. All rights reserved.
- * Copyright (c) 2013 Steven Hartland. All rights reserved.
- * Copyright (c) 2014 Integros [integros.com]
- * Copyright 2017 RackTop Systems.
- * Copyright (c) 2017 Datto Inc.
- */
-
-/*
- * LibZFS_Core (lzc) is intended to replace most functionality in libzfs.
- * It has the following characteristics:
- *
- * - Thread Safe. libzfs_core is accessible concurrently from multiple
- * threads. This is accomplished primarily by avoiding global data
- * (e.g. caching). Since it's thread-safe, there is no reason for a
- * process to have multiple libzfs "instances". Therefore, we store
- * our few pieces of data (e.g. the file descriptor) in global
- * variables. The fd is reference-counted so that the libzfs_core
- * library can be "initialized" multiple times (e.g. by different
- * consumers within the same process).
- *
- * - Committed Interface. The libzfs_core interface will be committed,
- * therefore consumers can compile against it and be confident that
- * their code will continue to work on future releases of this code.
- * Currently, the interface is Evolving (not Committed), but we intend
- * to commit to it once it is more complete and we determine that it
- * meets the needs of all consumers.
- *
- * - Programatic Error Handling. libzfs_core communicates errors with
- * defined error numbers, and doesn't print anything to stdout/stderr.
- *
- * - Thin Layer. libzfs_core is a thin layer, marshaling arguments
- * to/from the kernel ioctls. There is generally a 1:1 correspondence
- * between libzfs_core functions and ioctls to /dev/zfs.
- *
- * - Clear Atomicity. Because libzfs_core functions are generally 1:1
- * with kernel ioctls, and kernel ioctls are general atomic, each
- * libzfs_core function is atomic. For example, creating multiple
- * snapshots with a single call to lzc_snapshot() is atomic -- it
- * can't fail with only some of the requested snapshots created, even
- * in the event of power loss or system crash.
- *
- * - Continued libzfs Support. Some higher-level operations (e.g.
- * support for "zfs send -R") are too complicated to fit the scope of
- * libzfs_core. This functionality will continue to live in libzfs.
- * Where appropriate, libzfs will use the underlying atomic operations
- * of libzfs_core. For example, libzfs may implement "zfs send -R |
- * zfs receive" by using individual "send one snapshot", rename,
- * destroy, and "receive one snapshot" operations in libzfs_core.
- * /sbin/zfs and /zbin/zpool will link with both libzfs and
- * libzfs_core. Other consumers should aim to use only libzfs_core,
- * since that will be the supported, stable interface going forwards.
- */
-
-#define _IN_LIBZFS_CORE_
-
-#include <libzfs_core.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#ifdef ZFS_DEBUG
-#include <stdio.h>
-#endif
-#include <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <sys/nvpair.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/zfs_ioctl.h>
-#include "libzfs_core_compat.h"
-#include "libzfs_compat.h"
-
-#ifdef __FreeBSD__
-extern int zfs_ioctl_version;
-#endif
-
-static int g_fd = -1;
-static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
-static int g_refcount;
-
-#ifdef ZFS_DEBUG
-static zfs_ioc_t fail_ioc_cmd;
-static zfs_errno_t fail_ioc_err;
-
-static void
-libzfs_core_debug_ioc(void)
-{
- /*
- * To test running newer user space binaries with kernel's
- * that don't yet support an ioctl or a new ioctl arg we
- * provide an override to intentionally fail an ioctl.
- *
- * USAGE:
- * The override variable, ZFS_IOC_TEST, is of the form "cmd:err"
- *
- * For example, to fail a ZFS_IOC_POOL_CHECKPOINT with a
- * ZFS_ERR_IOC_CMD_UNAVAIL, the string would be "0x5a4d:1029"
- *
- * $ sudo sh -c "ZFS_IOC_TEST=0x5a4d:1029 zpool checkpoint tank"
- * cannot checkpoint 'tank': the loaded zfs module does not support
- * this operation. A reboot may be required to enable this operation.
- */
- if (fail_ioc_cmd == 0) {
- char *ioc_test = getenv("ZFS_IOC_TEST");
- unsigned int ioc_num = 0, ioc_err = 0;
-
- if (ioc_test != NULL &&
- sscanf(ioc_test, "%i:%i", &ioc_num, &ioc_err) == 2 &&
- ioc_num < ZFS_IOC_LAST) {
- fail_ioc_cmd = ioc_num;
- fail_ioc_err = ioc_err;
- }
- }
-}
-#endif
-
-int
-libzfs_core_init(void)
-{
- (void) pthread_mutex_lock(&g_lock);
- if (g_refcount == 0) {
- g_fd = open("/dev/zfs", O_RDWR);
- if (g_fd < 0) {
- (void) pthread_mutex_unlock(&g_lock);
- return (errno);
- }
- }
- g_refcount++;
-
-#ifdef ZFS_DEBUG
- libzfs_core_debug_ioc();
-#endif
- (void) pthread_mutex_unlock(&g_lock);
-
- return (0);
-}
-
-void
-libzfs_core_fini(void)
-{
- (void) pthread_mutex_lock(&g_lock);
- ASSERT3S(g_refcount, >, 0);
-
- if (g_refcount > 0)
- g_refcount--;
-
- if (g_refcount == 0 && g_fd != -1) {
- (void) close(g_fd);
- g_fd = -1;
- }
- (void) pthread_mutex_unlock(&g_lock);
-}
-
-static int
-lzc_ioctl(zfs_ioc_t ioc, const char *name,
- nvlist_t *source, nvlist_t **resultp)
-{
- zfs_cmd_t zc = { 0 };
- int error = 0;
- char *packed = NULL;
-#ifdef __FreeBSD__
- nvlist_t *oldsource;
-#endif
- size_t size = 0;
-
- ASSERT3S(g_refcount, >, 0);
- VERIFY3S(g_fd, !=, -1);
-
-#ifdef ZFS_DEBUG
- if (ioc == fail_ioc_cmd)
- return (fail_ioc_err);
-#endif
-
- if (name != NULL)
- (void) strlcpy(zc.zc_name, name, sizeof (zc.zc_name));
-
-#ifdef __FreeBSD__
- if (zfs_ioctl_version == ZFS_IOCVER_UNDEF)
- zfs_ioctl_version = get_zfs_ioctl_version();
-
- if (zfs_ioctl_version < ZFS_IOCVER_LZC) {
- oldsource = source;
- error = lzc_compat_pre(&zc, &ioc, &source);
- if (error)
- return (error);
- }
-#endif
-
- if (source != NULL) {
- packed = fnvlist_pack(source, &size);
- zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed;
- zc.zc_nvlist_src_size = size;
- }
-
- if (resultp != NULL) {
- *resultp = NULL;
- if (ioc == ZFS_IOC_CHANNEL_PROGRAM) {
- zc.zc_nvlist_dst_size = fnvlist_lookup_uint64(source,
- ZCP_ARG_MEMLIMIT);
- } else {
- zc.zc_nvlist_dst_size = MAX(size * 2, 128 * 1024);
- }
- zc.zc_nvlist_dst = (uint64_t)(uintptr_t)
- malloc(zc.zc_nvlist_dst_size);
-#ifdef illumos
- if (zc.zc_nvlist_dst == NULL) {
-#else
- if (zc.zc_nvlist_dst == 0) {
-#endif
- error = ENOMEM;
- goto out;
- }
- }
-
- while (ioctl(g_fd, ioc, &zc) != 0) {
- /*
- * If ioctl exited with ENOMEM, we retry the ioctl after
- * increasing the size of the destination nvlist.
- *
- * Channel programs that exit with ENOMEM ran over the
- * lua memory sandbox; they should not be retried.
- */
- if (errno == ENOMEM && resultp != NULL &&
- ioc != ZFS_IOC_CHANNEL_PROGRAM) {
- free((void *)(uintptr_t)zc.zc_nvlist_dst);
- zc.zc_nvlist_dst_size *= 2;
- zc.zc_nvlist_dst = (uint64_t)(uintptr_t)
- malloc(zc.zc_nvlist_dst_size);
-#ifdef illumos
- if (zc.zc_nvlist_dst == NULL) {
-#else
- if (zc.zc_nvlist_dst == 0) {
-#endif
- error = ENOMEM;
- goto out;
- }
- } else {
- error = errno;
- break;
- }
- }
-
-#ifdef __FreeBSD__
- if (zfs_ioctl_version < ZFS_IOCVER_LZC)
- lzc_compat_post(&zc, ioc);
-#endif
- if (zc.zc_nvlist_dst_filled) {
- *resultp = fnvlist_unpack((void *)(uintptr_t)zc.zc_nvlist_dst,
- zc.zc_nvlist_dst_size);
- }
-#ifdef __FreeBSD__
- if (zfs_ioctl_version < ZFS_IOCVER_LZC)
- lzc_compat_outnvl(&zc, ioc, resultp);
-#endif
-out:
-#ifdef __FreeBSD__
- if (zfs_ioctl_version < ZFS_IOCVER_LZC) {
- if (source != oldsource)
- nvlist_free(source);
- source = oldsource;
- }
-#endif
- fnvlist_pack_free(packed, size);
- free((void *)(uintptr_t)zc.zc_nvlist_dst);
- return (error);
-}
-
-int
-lzc_create(const char *fsname, enum lzc_dataset_type type, nvlist_t *props)
-{
- int error;
- nvlist_t *args = fnvlist_alloc();
- fnvlist_add_int32(args, "type", (dmu_objset_type_t)type);
- if (props != NULL)
- fnvlist_add_nvlist(args, "props", props);
- error = lzc_ioctl(ZFS_IOC_CREATE, fsname, args, NULL);
- nvlist_free(args);
- return (error);
-}
-
-int
-lzc_clone(const char *fsname, const char *origin,
- nvlist_t *props)
-{
- int error;
- nvlist_t *args = fnvlist_alloc();
- fnvlist_add_string(args, "origin", origin);
- if (props != NULL)
- fnvlist_add_nvlist(args, "props", props);
- error = lzc_ioctl(ZFS_IOC_CLONE, fsname, args, NULL);
- nvlist_free(args);
- return (error);
-}
-
-int
-lzc_promote(const char *fsname, char *snapnamebuf, int snapnamelen)
-{
- /*
- * The promote ioctl is still legacy, so we need to construct our
- * own zfs_cmd_t rather than using lzc_ioctl().
- */
- zfs_cmd_t zc = { 0 };
-
- ASSERT3S(g_refcount, >, 0);
- VERIFY3S(g_fd, !=, -1);
-
- (void) strlcpy(zc.zc_name, fsname, sizeof (zc.zc_name));
- if (ioctl(g_fd, ZFS_IOC_PROMOTE, &zc) != 0) {
- int error = errno;
- if (error == EEXIST && snapnamebuf != NULL)
- (void) strlcpy(snapnamebuf, zc.zc_string, snapnamelen);
- return (error);
- }
- return (0);
-}
-
-int
-lzc_remap(const char *fsname)
-{
- int error;
- nvlist_t *args = fnvlist_alloc();
- error = lzc_ioctl(ZFS_IOC_REMAP, fsname, args, NULL);
- nvlist_free(args);
- return (error);
-}
-
-int
-lzc_rename(const char *source, const char *target)
-{
- zfs_cmd_t zc = { 0 };
- int error;
-
- ASSERT3S(g_refcount, >, 0);
- VERIFY3S(g_fd, !=, -1);
-
- (void) strlcpy(zc.zc_name, source, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));
- error = ioctl(g_fd, ZFS_IOC_RENAME, &zc);
- if (error != 0)
- error = errno;
- return (error);
-}
-
-int
-lzc_destroy(const char *fsname)
-{
- int error;
-
- nvlist_t *args = fnvlist_alloc();
- error = lzc_ioctl(ZFS_IOC_DESTROY, fsname, args, NULL);
- nvlist_free(args);
- return (error);
-}
-
-/*
- * Creates snapshots.
- *
- * The keys in the snaps nvlist are the snapshots to be created.
- * They must all be in the same pool.
- *
- * The props nvlist is properties to set. Currently only user properties
- * are supported. { user:prop_name -> string value }
- *
- * The returned results nvlist will have an entry for each snapshot that failed.
- * The value will be the (int32) error code.
- *
- * The return value will be 0 if all snapshots were created, otherwise it will
- * be the errno of a (unspecified) snapshot that failed.
- */
-int
-lzc_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t **errlist)
-{
- nvpair_t *elem;
- nvlist_t *args;
- int error;
- char pool[ZFS_MAX_DATASET_NAME_LEN];
-
- *errlist = NULL;
-
- /* determine the pool name */
- elem = nvlist_next_nvpair(snaps, NULL);
- if (elem == NULL)
- return (0);
- (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
- pool[strcspn(pool, "/@")] = '\0';
-
- args = fnvlist_alloc();
- fnvlist_add_nvlist(args, "snaps", snaps);
- if (props != NULL)
- fnvlist_add_nvlist(args, "props", props);
-
- error = lzc_ioctl(ZFS_IOC_SNAPSHOT, pool, args, errlist);
- nvlist_free(args);
-
- return (error);
-}
-
-/*
- * Destroys snapshots.
- *
- * The keys in the snaps nvlist are the snapshots to be destroyed.
- * They must all be in the same pool.
- *
- * Snapshots that do not exist will be silently ignored.
- *
- * If 'defer' is not set, and a snapshot has user holds or clones, the
- * destroy operation will fail and none of the snapshots will be
- * destroyed.
- *
- * If 'defer' is set, and a snapshot has user holds or clones, it will be
- * marked for deferred destruction, and will be destroyed when the last hold
- * or clone is removed/destroyed.
- *
- * The return value will be 0 if all snapshots were destroyed (or marked for
- * later destruction if 'defer' is set) or didn't exist to begin with.
- *
- * Otherwise the return value will be the errno of a (unspecified) snapshot
- * that failed, no snapshots will be destroyed, and the errlist will have an
- * entry for each snapshot that failed. The value in the errlist will be
- * the (int32) error code.
- */
-int
-lzc_destroy_snaps(nvlist_t *snaps, boolean_t defer, nvlist_t **errlist)
-{
- nvpair_t *elem;
- nvlist_t *args;
- int error;
- char pool[ZFS_MAX_DATASET_NAME_LEN];
-
- /* determine the pool name */
- elem = nvlist_next_nvpair(snaps, NULL);
- if (elem == NULL)
- return (0);
- (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
- pool[strcspn(pool, "/@")] = '\0';
-
- args = fnvlist_alloc();
- fnvlist_add_nvlist(args, "snaps", snaps);
- if (defer)
- fnvlist_add_boolean(args, "defer");
-
- error = lzc_ioctl(ZFS_IOC_DESTROY_SNAPS, pool, args, errlist);
- nvlist_free(args);
-
- return (error);
-}
-
-int
-lzc_snaprange_space(const char *firstsnap, const char *lastsnap,
- uint64_t *usedp)
-{
- nvlist_t *args;
- nvlist_t *result;
- int err;
- char fs[ZFS_MAX_DATASET_NAME_LEN];
- char *atp;
-
- /* determine the fs name */
- (void) strlcpy(fs, firstsnap, sizeof (fs));
- atp = strchr(fs, '@');
- if (atp == NULL)
- return (EINVAL);
- *atp = '\0';
-
- args = fnvlist_alloc();
- fnvlist_add_string(args, "firstsnap", firstsnap);
-
- err = lzc_ioctl(ZFS_IOC_SPACE_SNAPS, lastsnap, args, &result);
- nvlist_free(args);
- if (err == 0)
- *usedp = fnvlist_lookup_uint64(result, "used");
- fnvlist_free(result);
-
- return (err);
-}
-
-boolean_t
-lzc_exists(const char *dataset)
-{
- /*
- * The objset_stats ioctl is still legacy, so we need to construct our
- * own zfs_cmd_t rather than using lzc_ioctl().
- */
- zfs_cmd_t zc = { 0 };
-
- ASSERT3S(g_refcount, >, 0);
- VERIFY3S(g_fd, !=, -1);
-
- (void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
- return (ioctl(g_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0);
-}
-
-/*
- * outnvl is unused.
- * It was added to preserve the function signature in case it is
- * needed in the future.
- */
-/*ARGSUSED*/
-int
-lzc_sync(const char *pool_name, nvlist_t *innvl, nvlist_t **outnvl)
-{
- return (lzc_ioctl(ZFS_IOC_POOL_SYNC, pool_name, innvl, NULL));
-}
-
-/*
- * Create "user holds" on snapshots. If there is a hold on a snapshot,
- * the snapshot can not be destroyed. (However, it can be marked for deletion
- * by lzc_destroy_snaps(defer=B_TRUE).)
- *
- * The keys in the nvlist are snapshot names.
- * The snapshots must all be in the same pool.
- * The value is the name of the hold (string type).
- *
- * If cleanup_fd is not -1, it must be the result of open("/dev/zfs", O_EXCL).
- * In this case, when the cleanup_fd is closed (including on process
- * termination), the holds will be released. If the system is shut down
- * uncleanly, the holds will be released when the pool is next opened
- * or imported.
- *
- * Holds for snapshots which don't exist will be skipped and have an entry
- * added to errlist, but will not cause an overall failure.
- *
- * The return value will be 0 if all holds, for snapshots that existed,
- * were succesfully created.
- *
- * Otherwise the return value will be the errno of a (unspecified) hold that
- * failed and no holds will be created.
- *
- * In all cases the errlist will have an entry for each hold that failed
- * (name = snapshot), with its value being the error code (int32).
- */
-int
-lzc_hold(nvlist_t *holds, int cleanup_fd, nvlist_t **errlist)
-{
- char pool[ZFS_MAX_DATASET_NAME_LEN];
- nvlist_t *args;
- nvpair_t *elem;
- int error;
-
- /* determine the pool name */
- elem = nvlist_next_nvpair(holds, NULL);
- if (elem == NULL)
- return (0);
- (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
- pool[strcspn(pool, "/@")] = '\0';
-
- args = fnvlist_alloc();
- fnvlist_add_nvlist(args, "holds", holds);
- if (cleanup_fd != -1)
- fnvlist_add_int32(args, "cleanup_fd", cleanup_fd);
-
- error = lzc_ioctl(ZFS_IOC_HOLD, pool, args, errlist);
- nvlist_free(args);
- return (error);
-}
-
-/*
- * Release "user holds" on snapshots. If the snapshot has been marked for
- * deferred destroy (by lzc_destroy_snaps(defer=B_TRUE)), it does not have
- * any clones, and all the user holds are removed, then the snapshot will be
- * destroyed.
- *
- * The keys in the nvlist are snapshot names.
- * The snapshots must all be in the same pool.
- * The value is a nvlist whose keys are the holds to remove.
- *
- * Holds which failed to release because they didn't exist will have an entry
- * added to errlist, but will not cause an overall failure.
- *
- * The return value will be 0 if the nvl holds was empty or all holds that
- * existed, were successfully removed.
- *
- * Otherwise the return value will be the errno of a (unspecified) hold that
- * failed to release and no holds will be released.
- *
- * In all cases the errlist will have an entry for each hold that failed to
- * to release.
- */
-int
-lzc_release(nvlist_t *holds, nvlist_t **errlist)
-{
- char pool[ZFS_MAX_DATASET_NAME_LEN];
- nvpair_t *elem;
-
- /* determine the pool name */
- elem = nvlist_next_nvpair(holds, NULL);
- if (elem == NULL)
- return (0);
- (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
- pool[strcspn(pool, "/@")] = '\0';
-
- return (lzc_ioctl(ZFS_IOC_RELEASE, pool, holds, errlist));
-}
-
-/*
- * Retrieve list of user holds on the specified snapshot.
- *
- * On success, *holdsp will be set to a nvlist which the caller must free.
- * The keys are the names of the holds, and the value is the creation time
- * of the hold (uint64) in seconds since the epoch.
- */
-int
-lzc_get_holds(const char *snapname, nvlist_t **holdsp)
-{
- return (lzc_ioctl(ZFS_IOC_GET_HOLDS, snapname, NULL, holdsp));
-}
-
-/*
- * Generate a zfs send stream for the specified snapshot and write it to
- * the specified file descriptor.
- *
- * "snapname" is the full name of the snapshot to send (e.g. "pool/fs@snap")
- *
- * If "from" is NULL, a full (non-incremental) stream will be sent.
- * If "from" is non-NULL, it must be the full name of a snapshot or
- * bookmark to send an incremental from (e.g. "pool/fs@earlier_snap" or
- * "pool/fs#earlier_bmark"). If non-NULL, the specified snapshot or
- * bookmark must represent an earlier point in the history of "snapname").
- * It can be an earlier snapshot in the same filesystem or zvol as "snapname",
- * or it can be the origin of "snapname"'s filesystem, or an earlier
- * snapshot in the origin, etc.
- *
- * "fd" is the file descriptor to write the send stream to.
- *
- * If "flags" contains LZC_SEND_FLAG_LARGE_BLOCK, the stream is permitted
- * to contain DRR_WRITE records with drr_length > 128K, and DRR_OBJECT
- * records with drr_blksz > 128K.
- *
- * If "flags" contains LZC_SEND_FLAG_EMBED_DATA, the stream is permitted
- * to contain DRR_WRITE_EMBEDDED records with drr_etype==BP_EMBEDDED_TYPE_DATA,
- * which the receiving system must support (as indicated by support
- * for the "embedded_data" feature).
- */
-int
-lzc_send(const char *snapname, const char *from, int fd,
- enum lzc_send_flags flags)
-{
- return (lzc_send_resume(snapname, from, fd, flags, 0, 0));
-}
-
-int
-lzc_send_resume(const char *snapname, const char *from, int fd,
- enum lzc_send_flags flags, uint64_t resumeobj, uint64_t resumeoff)
-{
- nvlist_t *args;
- int err;
-
- args = fnvlist_alloc();
- fnvlist_add_int32(args, "fd", fd);
- if (from != NULL)
- fnvlist_add_string(args, "fromsnap", from);
- if (flags & LZC_SEND_FLAG_LARGE_BLOCK)
- fnvlist_add_boolean(args, "largeblockok");
- if (flags & LZC_SEND_FLAG_EMBED_DATA)
- fnvlist_add_boolean(args, "embedok");
- if (flags & LZC_SEND_FLAG_COMPRESS)
- fnvlist_add_boolean(args, "compressok");
- if (resumeobj != 0 || resumeoff != 0) {
- fnvlist_add_uint64(args, "resume_object", resumeobj);
- fnvlist_add_uint64(args, "resume_offset", resumeoff);
- }
- err = lzc_ioctl(ZFS_IOC_SEND_NEW, snapname, args, NULL);
- nvlist_free(args);
- return (err);
-}
-
-/*
- * "from" can be NULL, a snapshot, or a bookmark.
- *
- * If from is NULL, a full (non-incremental) stream will be estimated. This
- * is calculated very efficiently.
- *
- * If from is a snapshot, lzc_send_space uses the deadlists attached to
- * each snapshot to efficiently estimate the stream size.
- *
- * If from is a bookmark, the indirect blocks in the destination snapshot
- * are traversed, looking for blocks with a birth time since the creation TXG of
- * the snapshot this bookmark was created from. This will result in
- * significantly more I/O and be less efficient than a send space estimation on
- * an equivalent snapshot.
- */
-int
-lzc_send_space(const char *snapname, const char *from,
- enum lzc_send_flags flags, uint64_t *spacep)
-{
- nvlist_t *args;
- nvlist_t *result;
- int err;
-
- args = fnvlist_alloc();
- if (from != NULL)
- fnvlist_add_string(args, "from", from);
- if (flags & LZC_SEND_FLAG_LARGE_BLOCK)
- fnvlist_add_boolean(args, "largeblockok");
- if (flags & LZC_SEND_FLAG_EMBED_DATA)
- fnvlist_add_boolean(args, "embedok");
- if (flags & LZC_SEND_FLAG_COMPRESS)
- fnvlist_add_boolean(args, "compressok");
- err = lzc_ioctl(ZFS_IOC_SEND_SPACE, snapname, args, &result);
- nvlist_free(args);
- if (err == 0)
- *spacep = fnvlist_lookup_uint64(result, "space");
- nvlist_free(result);
- return (err);
-}
-
-static int
-recv_read(int fd, void *buf, int ilen)
-{
- char *cp = buf;
- int rv;
- int len = ilen;
-
- do {
- rv = read(fd, cp, len);
- cp += rv;
- len -= rv;
- } while (rv > 0);
-
- if (rv < 0 || len != 0)
- return (EIO);
-
- return (0);
-}
-
-static int
-recv_impl(const char *snapname, nvlist_t *props, const char *origin,
- boolean_t force, boolean_t resumable, int fd,
- const dmu_replay_record_t *begin_record)
-{
- /*
- * The receive ioctl is still legacy, so we need to construct our own
- * zfs_cmd_t rather than using zfsc_ioctl().
- */
- zfs_cmd_t zc = { 0 };
- char *atp;
- char *packed = NULL;
- size_t size;
- int error;
-
- ASSERT3S(g_refcount, >, 0);
- VERIFY3S(g_fd, !=, -1);
-
- /* zc_name is name of containing filesystem */
- (void) strlcpy(zc.zc_name, snapname, sizeof (zc.zc_name));
- atp = strchr(zc.zc_name, '@');
- if (atp == NULL)
- return (EINVAL);
- *atp = '\0';
-
- /* if the fs does not exist, try its parent. */
- if (!lzc_exists(zc.zc_name)) {
- char *slashp = strrchr(zc.zc_name, '/');
- if (slashp == NULL)
- return (ENOENT);
- *slashp = '\0';
-
- }
-
- /* zc_value is full name of the snapshot to create */
- (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
-
- if (props != NULL) {
- /* zc_nvlist_src is props to set */
- packed = fnvlist_pack(props, &size);
- zc.zc_nvlist_src = (uint64_t)(uintptr_t)packed;
- zc.zc_nvlist_src_size = size;
- }
-
- /* zc_string is name of clone origin (if DRR_FLAG_CLONE) */
- if (origin != NULL)
- (void) strlcpy(zc.zc_string, origin, sizeof (zc.zc_string));
-
- /* zc_begin_record is non-byteswapped BEGIN record */
- if (begin_record == NULL) {
- error = recv_read(fd, &zc.zc_begin_record,
- sizeof (zc.zc_begin_record));
- if (error != 0)
- goto out;
- } else {
- zc.zc_begin_record = *begin_record;
- }
-
- /* zc_cookie is fd to read from */
- zc.zc_cookie = fd;
-
- /* zc guid is force flag */
- zc.zc_guid = force;
-
- zc.zc_resumable = resumable;
-
- /* zc_cleanup_fd is unused */
- zc.zc_cleanup_fd = -1;
-
- error = ioctl(g_fd, ZFS_IOC_RECV, &zc);
- if (error != 0)
- error = errno;
-
-out:
- if (packed != NULL)
- fnvlist_pack_free(packed, size);
- free((void*)(uintptr_t)zc.zc_nvlist_dst);
- return (error);
-}
-
-/*
- * The simplest receive case: receive from the specified fd, creating the
- * specified snapshot. Apply the specified properties as "received" properties
- * (which can be overridden by locally-set properties). If the stream is a
- * clone, its origin snapshot must be specified by 'origin'. The 'force'
- * flag will cause the target filesystem to be rolled back or destroyed if
- * necessary to receive.
- *
- * Return 0 on success or an errno on failure.
- *
- * Note: this interface does not work on dedup'd streams
- * (those with DMU_BACKUP_FEATURE_DEDUP).
- */
-int
-lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
- boolean_t force, int fd)
-{
- return (recv_impl(snapname, props, origin, force, B_FALSE, fd, NULL));
-}
-
-/*
- * Like lzc_receive, but if the receive fails due to premature stream
- * termination, the intermediate state will be preserved on disk. In this
- * case, ECKSUM will be returned. The receive may subsequently be resumed
- * with a resuming send stream generated by lzc_send_resume().
- */
-int
-lzc_receive_resumable(const char *snapname, nvlist_t *props, const char *origin,
- boolean_t force, int fd)
-{
- return (recv_impl(snapname, props, origin, force, B_TRUE, fd, NULL));
-}
-
-/*
- * Like lzc_receive, but allows the caller to read the begin record and then to
- * pass it in. That could be useful if the caller wants to derive, for example,
- * the snapname or the origin parameters based on the information contained in
- * the begin record.
- * The begin record must be in its original form as read from the stream,
- * in other words, it should not be byteswapped.
- *
- * The 'resumable' parameter allows to obtain the same behavior as with
- * lzc_receive_resumable.
- */
-int
-lzc_receive_with_header(const char *snapname, nvlist_t *props,
- const char *origin, boolean_t force, boolean_t resumable, int fd,
- const dmu_replay_record_t *begin_record)
-{
- if (begin_record == NULL)
- return (EINVAL);
- return (recv_impl(snapname, props, origin, force, resumable, fd,
- begin_record));
-}
-
-/*
- * Roll back this filesystem or volume to its most recent snapshot.
- * If snapnamebuf is not NULL, it will be filled in with the name
- * of the most recent snapshot.
- * Note that the latest snapshot may change if a new one is concurrently
- * created or the current one is destroyed. lzc_rollback_to can be used
- * to roll back to a specific latest snapshot.
- *
- * Return 0 on success or an errno on failure.
- */
-int
-lzc_rollback(const char *fsname, char *snapnamebuf, int snapnamelen)
-{
- nvlist_t *args;
- nvlist_t *result;
- int err;
-
- args = fnvlist_alloc();
- err = lzc_ioctl(ZFS_IOC_ROLLBACK, fsname, args, &result);
- nvlist_free(args);
- if (err == 0 && snapnamebuf != NULL) {
- const char *snapname = fnvlist_lookup_string(result, "target");
- (void) strlcpy(snapnamebuf, snapname, snapnamelen);
- }
- nvlist_free(result);
-
- return (err);
-}
-
-/*
- * Roll back this filesystem or volume to the specified snapshot,
- * if possible.
- *
- * Return 0 on success or an errno on failure.
- */
-int
-lzc_rollback_to(const char *fsname, const char *snapname)
-{
- nvlist_t *args;
- nvlist_t *result;
- int err;
-
- args = fnvlist_alloc();
- fnvlist_add_string(args, "target", snapname);
- err = lzc_ioctl(ZFS_IOC_ROLLBACK, fsname, args, &result);
- nvlist_free(args);
- nvlist_free(result);
- return (err);
-}
-
-/*
- * Creates bookmarks.
- *
- * The bookmarks nvlist maps from name of the bookmark (e.g. "pool/fs#bmark") to
- * the name of the snapshot (e.g. "pool/fs@snap"). All the bookmarks and
- * snapshots must be in the same pool.
- *
- * The returned results nvlist will have an entry for each bookmark that failed.
- * The value will be the (int32) error code.
- *
- * The return value will be 0 if all bookmarks were created, otherwise it will
- * be the errno of a (undetermined) bookmarks that failed.
- */
-int
-lzc_bookmark(nvlist_t *bookmarks, nvlist_t **errlist)
-{
- nvpair_t *elem;
- int error;
- char pool[ZFS_MAX_DATASET_NAME_LEN];
-
- /* determine the pool name */
- elem = nvlist_next_nvpair(bookmarks, NULL);
- if (elem == NULL)
- return (0);
- (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
- pool[strcspn(pool, "/#")] = '\0';
-
- error = lzc_ioctl(ZFS_IOC_BOOKMARK, pool, bookmarks, errlist);
-
- return (error);
-}
-
-/*
- * Retrieve bookmarks.
- *
- * Retrieve the list of bookmarks for the given file system. The props
- * parameter is an nvlist of property names (with no values) that will be
- * returned for each bookmark.
- *
- * The following are valid properties on bookmarks, all of which are numbers
- * (represented as uint64 in the nvlist)
- *
- * "guid" - globally unique identifier of the snapshot it refers to
- * "createtxg" - txg when the snapshot it refers to was created
- * "creation" - timestamp when the snapshot it refers to was created
- *
- * The format of the returned nvlist as follows:
- * <short name of bookmark> -> {
- * <name of property> -> {
- * "value" -> uint64
- * }
- * }
- */
-int
-lzc_get_bookmarks(const char *fsname, nvlist_t *props, nvlist_t **bmarks)
-{
- return (lzc_ioctl(ZFS_IOC_GET_BOOKMARKS, fsname, props, bmarks));
-}
-
-/*
- * Destroys bookmarks.
- *
- * The keys in the bmarks nvlist are the bookmarks to be destroyed.
- * They must all be in the same pool. Bookmarks are specified as
- * <fs>#<bmark>.
- *
- * Bookmarks that do not exist will be silently ignored.
- *
- * The return value will be 0 if all bookmarks that existed were destroyed.
- *
- * Otherwise the return value will be the errno of a (undetermined) bookmark
- * that failed, no bookmarks will be destroyed, and the errlist will have an
- * entry for each bookmarks that failed. The value in the errlist will be
- * the (int32) error code.
- */
-int
-lzc_destroy_bookmarks(nvlist_t *bmarks, nvlist_t **errlist)
-{
- nvpair_t *elem;
- int error;
- char pool[ZFS_MAX_DATASET_NAME_LEN];
-
- /* determine the pool name */
- elem = nvlist_next_nvpair(bmarks, NULL);
- if (elem == NULL)
- return (0);
- (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
- pool[strcspn(pool, "/#")] = '\0';
-
- error = lzc_ioctl(ZFS_IOC_DESTROY_BOOKMARKS, pool, bmarks, errlist);
-
- return (error);
-}
-
-static int
-lzc_channel_program_impl(const char *pool, const char *program, boolean_t sync,
- uint64_t instrlimit, uint64_t memlimit, nvlist_t *argnvl, nvlist_t **outnvl)
-{
- int error;
- nvlist_t *args;
-
- args = fnvlist_alloc();
- fnvlist_add_string(args, ZCP_ARG_PROGRAM, program);
- fnvlist_add_nvlist(args, ZCP_ARG_ARGLIST, argnvl);
- fnvlist_add_boolean_value(args, ZCP_ARG_SYNC, sync);
- fnvlist_add_uint64(args, ZCP_ARG_INSTRLIMIT, instrlimit);
- fnvlist_add_uint64(args, ZCP_ARG_MEMLIMIT, memlimit);
- error = lzc_ioctl(ZFS_IOC_CHANNEL_PROGRAM, pool, args, outnvl);
- fnvlist_free(args);
-
- return (error);
-}
-
-/*
- * Executes a channel program.
- *
- * If this function returns 0 the channel program was successfully loaded and
- * ran without failing. Note that individual commands the channel program ran
- * may have failed and the channel program is responsible for reporting such
- * errors through outnvl if they are important.
- *
- * This method may also return:
- *
- * EINVAL The program contains syntax errors, or an invalid memory or time
- * limit was given. No part of the channel program was executed.
- * If caused by syntax errors, 'outnvl' contains information about the
- * errors.
- *
- * EDOM The program was executed, but encountered a runtime error, such as
- * calling a function with incorrect arguments, invoking the error()
- * function directly, failing an assert() command, etc. Some portion
- * of the channel program may have executed and committed changes.
- * Information about the failure can be found in 'outnvl'.
- *
- * ENOMEM The program fully executed, but the output buffer was not large
- * enough to store the returned value. No output is returned through
- * 'outnvl'.
- *
- * ENOSPC The program was terminated because it exceeded its memory usage
- * limit. Some portion of the channel program may have executed and
- * committed changes to disk. No output is returned through 'outnvl'.
- *
- * ETIMEDOUT The program was terminated because it exceeded its Lua instruction
- * limit. Some portion of the channel program may have executed and
- * committed changes to disk. No output is returned through 'outnvl'.
- */
-int
-lzc_channel_program(const char *pool, const char *program, uint64_t instrlimit,
- uint64_t memlimit, nvlist_t *argnvl, nvlist_t **outnvl)
-{
- return (lzc_channel_program_impl(pool, program, B_TRUE, instrlimit,
- memlimit, argnvl, outnvl));
-}
-
-/*
- * Creates a checkpoint for the specified pool.
- *
- * If this function returns 0 the pool was successfully checkpointed.
- *
- * This method may also return:
- *
- * ZFS_ERR_CHECKPOINT_EXISTS
- * The pool already has a checkpoint. A pools can only have one
- * checkpoint at most, at any given time.
- *
- * ZFS_ERR_DISCARDING_CHECKPOINT
- * ZFS is in the middle of discarding a checkpoint for this pool.
- * The pool can be checkpointed again once the discard is done.
- *
- * ZFS_DEVRM_IN_PROGRESS
- * A vdev is currently being removed. The pool cannot be
- * checkpointed until the device removal is done.
- *
- * ZFS_VDEV_TOO_BIG
- * One or more top-level vdevs exceed the maximum vdev size
- * supported for this feature.
- */
-int
-lzc_pool_checkpoint(const char *pool)
-{
- int error;
-
- nvlist_t *result = NULL;
- nvlist_t *args = fnvlist_alloc();
-
- error = lzc_ioctl(ZFS_IOC_POOL_CHECKPOINT, pool, args, &result);
-
- fnvlist_free(args);
- fnvlist_free(result);
-
- return (error);
-}
-
-/*
- * Discard the checkpoint from the specified pool.
- *
- * If this function returns 0 the checkpoint was successfully discarded.
- *
- * This method may also return:
- *
- * ZFS_ERR_NO_CHECKPOINT
- * The pool does not have a checkpoint.
- *
- * ZFS_ERR_DISCARDING_CHECKPOINT
- * ZFS is already in the middle of discarding the checkpoint.
- */
-int
-lzc_pool_checkpoint_discard(const char *pool)
-{
- int error;
-
- nvlist_t *result = NULL;
- nvlist_t *args = fnvlist_alloc();
-
- error = lzc_ioctl(ZFS_IOC_POOL_DISCARD_CHECKPOINT, pool, args, &result);
-
- fnvlist_free(args);
- fnvlist_free(result);
-
- return (error);
-}
-
-/*
- * Executes a read-only channel program.
- *
- * A read-only channel program works programmatically the same way as a
- * normal channel program executed with lzc_channel_program(). The only
- * difference is it runs exclusively in open-context and therefore can
- * return faster. The downside to that, is that the program cannot change
- * on-disk state by calling functions from the zfs.sync submodule.
- *
- * The return values of this function (and their meaning) are exactly the
- * same as the ones described in lzc_channel_program().
- */
-int
-lzc_channel_program_nosync(const char *pool, const char *program,
- uint64_t timeout, uint64_t memlimit, nvlist_t *argnvl, nvlist_t **outnvl)
-{
- return (lzc_channel_program_impl(pool, program, B_FALSE, timeout,
- memlimit, argnvl, outnvl));
-}
-
-/*
- * Changes initializing state.
- *
- * vdevs should be a list of (<key>, guid) where guid is a uint64 vdev GUID.
- * The key is ignored.
- *
- * If there are errors related to vdev arguments, per-vdev errors are returned
- * in an nvlist with the key "vdevs". Each error is a (guid, errno) pair where
- * guid is stringified with PRIu64, and errno is one of the following as
- * an int64_t:
- * - ENODEV if the device was not found
- * - EINVAL if the devices is not a leaf or is not concrete (e.g. missing)
- * - EROFS if the device is not writeable
- * - EBUSY start requested but the device is already being initialized
- * - ESRCH cancel/suspend requested but device is not being initialized
- *
- * If the errlist is empty, then return value will be:
- * - EINVAL if one or more arguments was invalid
- * - Other spa_open failures
- * - 0 if the operation succeeded
- */
-int
-lzc_initialize(const char *poolname, pool_initialize_func_t cmd_type,
- nvlist_t *vdevs, nvlist_t **errlist)
-{
- int error;
- nvlist_t *args = fnvlist_alloc();
- fnvlist_add_uint64(args, ZPOOL_INITIALIZE_COMMAND, (uint64_t)cmd_type);
- fnvlist_add_nvlist(args, ZPOOL_INITIALIZE_VDEVS, vdevs);
-
- error = lzc_ioctl(ZFS_IOC_POOL_INITIALIZE, poolname, args, errlist);
-
- fnvlist_free(args);
-
- return (error);
-}
-
-/*
- * Set the bootenv contents for the given pool.
- */
-int
-lzc_set_bootenv(const char *pool, const char *env)
-{
- nvlist_t *args = fnvlist_alloc();
- fnvlist_add_string(args, "envmap", env);
- int error = lzc_ioctl(ZFS_IOC_SET_BOOTENV, pool, args, NULL);
- fnvlist_free(args);
- return (error);
-}
-
-/*
- * Get the contents of the bootenv of the given pool.
- */
-int
-lzc_get_bootenv(const char *pool, nvlist_t **outnvl)
-{
- return (lzc_ioctl(ZFS_IOC_GET_BOOTENV, pool, NULL, outnvl));
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h b/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h
deleted file mode 100644
index 76c4fa1bf6b46..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core.h
+++ /dev/null
@@ -1,114 +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) 2012, 2020 by Delphix. All rights reserved.
- * Copyright (c) 2013 by Martin Matuska <mm@FreeBSD.org>. All rights reserved.
- * Copyright 2017 RackTop Systems.
- * Copyright (c) 2017 Datto Inc.
- */
-
-#ifndef _LIBZFS_CORE_H
-#define _LIBZFS_CORE_H
-
-#include <libnvpair.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/fs/zfs.h>
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int libzfs_core_init(void);
-void libzfs_core_fini(void);
-
-/*
- * NB: this type should be kept binary compatible with dmu_objset_type_t.
- */
-enum lzc_dataset_type {
- LZC_DATSET_TYPE_ZFS = 2,
- LZC_DATSET_TYPE_ZVOL
-};
-
-int lzc_remap(const char *fsname);
-int lzc_snapshot(nvlist_t *, nvlist_t *, nvlist_t **);
-int lzc_create(const char *, enum lzc_dataset_type, nvlist_t *);
-int lzc_clone(const char *, const char *, nvlist_t *);
-int lzc_promote(const char *, char *, int);
-int lzc_destroy_snaps(nvlist_t *, boolean_t, nvlist_t **);
-int lzc_bookmark(nvlist_t *, nvlist_t **);
-int lzc_get_bookmarks(const char *, nvlist_t *, nvlist_t **);
-int lzc_destroy_bookmarks(nvlist_t *, nvlist_t **);
-int lzc_initialize(const char *, pool_initialize_func_t, nvlist_t *,
- nvlist_t **);
-
-int lzc_snaprange_space(const char *, const char *, uint64_t *);
-
-int lzc_hold(nvlist_t *, int, nvlist_t **);
-int lzc_release(nvlist_t *, nvlist_t **);
-int lzc_get_holds(const char *, nvlist_t **);
-
-enum lzc_send_flags {
- LZC_SEND_FLAG_EMBED_DATA = 1 << 0,
- LZC_SEND_FLAG_LARGE_BLOCK = 1 << 1,
- LZC_SEND_FLAG_COMPRESS = 1 << 2
-};
-
-int lzc_send(const char *, const char *, int, enum lzc_send_flags);
-int lzc_send_resume(const char *, const char *, int,
- enum lzc_send_flags, uint64_t, uint64_t);
-int lzc_send_space(const char *, const char *, enum lzc_send_flags, uint64_t *);
-
-struct dmu_replay_record;
-
-int lzc_receive(const char *, nvlist_t *, const char *, boolean_t, int);
-int lzc_receive_resumable(const char *, nvlist_t *, const char *,
- boolean_t, int);
-int lzc_receive_with_header(const char *, nvlist_t *, const char *, boolean_t,
- boolean_t, int, const struct dmu_replay_record *);
-
-boolean_t lzc_exists(const char *);
-
-int lzc_rollback(const char *, char *, int);
-int lzc_rollback_to(const char *, const char *);
-
-int lzc_sync(const char *, nvlist_t *, nvlist_t **);
-
-int lzc_rename(const char *, const char *);
-int lzc_destroy(const char *);
-
-int lzc_channel_program(const char *, const char *, uint64_t,
- uint64_t, nvlist_t *, nvlist_t **);
-int lzc_channel_program_nosync(const char *, const char *, uint64_t,
- uint64_t, nvlist_t *, nvlist_t **);
-
-int lzc_pool_checkpoint(const char *);
-int lzc_pool_checkpoint_discard(const char *);
-
-int lzc_set_bootenv(const char *, const char *);
-int lzc_get_bootenv(const char *, nvlist_t **);
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBZFS_CORE_H */
diff --git a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core_compat.c b/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core_compat.c
deleted file mode 100644
index a3b872ee29da4..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core_compat.c
+++ /dev/null
@@ -1,189 +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) 2013 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
- */
-
-#include <sys/zfs_ioctl.h>
-#include <zfs_ioctl_compat.h>
-#include "libzfs_core_compat.h"
-
-extern int zfs_ioctl_version;
-
-int
-lzc_compat_pre(zfs_cmd_t *zc, zfs_ioc_t *ioc, nvlist_t **source)
-{
- nvlist_t *nvl = NULL;
- nvpair_t *pair, *hpair;
- char *buf, *val;
- zfs_ioc_t vecnum;
- uint32_t type32;
- int32_t cleanup_fd;
- int error = 0;
- int pos;
-
- if (zfs_ioctl_version >= ZFS_IOCVER_LZC)
- return (0);
-
- vecnum = *ioc;
-
- switch (vecnum) {
- case ZFS_IOC_CREATE:
- type32 = fnvlist_lookup_int32(*source, "type");
- zc->zc_objset_type = (uint64_t)type32;
- nvlist_lookup_nvlist(*source, "props", &nvl);
- *source = nvl;
- break;
- case ZFS_IOC_CLONE:
- buf = fnvlist_lookup_string(*source, "origin");
- strlcpy(zc->zc_value, buf, MAXPATHLEN);
- nvlist_lookup_nvlist(*source, "props", &nvl);
- *ioc = ZFS_IOC_CREATE;
- *source = nvl;
- break;
- case ZFS_IOC_SNAPSHOT:
- nvl = fnvlist_lookup_nvlist(*source, "snaps");
- pair = nvlist_next_nvpair(nvl, NULL);
- if (pair != NULL) {
- buf = nvpair_name(pair);
- pos = strcspn(buf, "@");
- strlcpy(zc->zc_name, buf, pos + 1);
- strlcpy(zc->zc_value, buf + pos + 1, MAXPATHLEN);
- } else
- error = EINVAL;
- /* old kernel cannot create multiple snapshots */
- if (!error && nvlist_next_nvpair(nvl, pair) != NULL)
- error = EOPNOTSUPP;
- nvlist_free(nvl);
- nvl = NULL;
- nvlist_lookup_nvlist(*source, "props", &nvl);
- *source = nvl;
- break;
- case ZFS_IOC_SPACE_SNAPS:
- buf = fnvlist_lookup_string(*source, "firstsnap");
- strlcpy(zc->zc_value, buf, MAXPATHLEN);
- break;
- case ZFS_IOC_DESTROY_SNAPS:
- nvl = fnvlist_lookup_nvlist(*source, "snaps");
- pair = nvlist_next_nvpair(nvl, NULL);
- if (pair != NULL) {
- buf = nvpair_name(pair);
- pos = strcspn(buf, "@");
- strlcpy(zc->zc_name, buf, pos + 1);
- } else
- error = EINVAL;
- /* old kernel cannot atomically destroy multiple snaps */
- if (!error && nvlist_next_nvpair(nvl, pair) != NULL)
- error = EOPNOTSUPP;
- *source = nvl;
- break;
- case ZFS_IOC_HOLD:
- nvl = fnvlist_lookup_nvlist(*source, "holds");
- pair = nvlist_next_nvpair(nvl, NULL);
- if (pair != NULL) {
- buf = nvpair_name(pair);
- pos = strcspn(buf, "@");
- strlcpy(zc->zc_name, buf, pos + 1);
- strlcpy(zc->zc_value, buf + pos + 1, MAXPATHLEN);
- if (nvpair_value_string(pair, &val) == 0)
- strlcpy(zc->zc_string, val, MAXNAMELEN);
- else
- error = EINVAL;
- } else
- error = EINVAL;
- /* old kernel cannot atomically create multiple holds */
- if (!error && nvlist_next_nvpair(nvl, pair) != NULL)
- error = EOPNOTSUPP;
- nvlist_free(nvl);
- if (nvlist_lookup_int32(*source, "cleanup_fd",
- &cleanup_fd) == 0)
- zc->zc_cleanup_fd = cleanup_fd;
- else
- zc->zc_cleanup_fd = -1;
- break;
- case ZFS_IOC_RELEASE:
- pair = nvlist_next_nvpair(*source, NULL);
- if (pair != NULL) {
- buf = nvpair_name(pair);
- pos = strcspn(buf, "@");
- strlcpy(zc->zc_name, buf, pos + 1);
- strlcpy(zc->zc_value, buf + pos + 1, MAXPATHLEN);
- if (nvpair_value_nvlist(pair, &nvl) == 0) {
- hpair = nvlist_next_nvpair(nvl, NULL);
- if (hpair != NULL)
- strlcpy(zc->zc_string,
- nvpair_name(hpair), MAXNAMELEN);
- else
- error = EINVAL;
- if (!error && nvlist_next_nvpair(nvl,
- hpair) != NULL)
- error = EOPNOTSUPP;
- } else
- error = EINVAL;
- } else
- error = EINVAL;
- /* old kernel cannot atomically release multiple holds */
- if (!error && nvlist_next_nvpair(nvl, pair) != NULL)
- error = EOPNOTSUPP;
- break;
- }
-
- return (error);
-}
-
-void
-lzc_compat_post(zfs_cmd_t *zc, const zfs_ioc_t ioc)
-{
- if (zfs_ioctl_version >= ZFS_IOCVER_LZC)
- return;
-
- switch (ioc) {
- case ZFS_IOC_CREATE:
- case ZFS_IOC_CLONE:
- case ZFS_IOC_SNAPSHOT:
- case ZFS_IOC_SPACE_SNAPS:
- case ZFS_IOC_DESTROY_SNAPS:
- zc->zc_nvlist_dst_filled = B_FALSE;
- break;
- }
-}
-
-int
-lzc_compat_outnvl(zfs_cmd_t *zc, const zfs_ioc_t ioc, nvlist_t **outnvl)
-{
- nvlist_t *nvl;
-
- if (zfs_ioctl_version >= ZFS_IOCVER_LZC)
- return (0);
-
- switch (ioc) {
- case ZFS_IOC_SPACE_SNAPS:
- nvl = fnvlist_alloc();
- fnvlist_add_uint64(nvl, "used", zc->zc_cookie);
- fnvlist_add_uint64(nvl, "compressed", zc->zc_objset_type);
- fnvlist_add_uint64(nvl, "uncompressed", zc->zc_perm_action);
- *outnvl = nvl;
- break;
- }
-
- return (0);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core_compat.h b/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core_compat.h
deleted file mode 100644
index 6527c4b2576f5..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzfs_core/common/libzfs_core_compat.h
+++ /dev/null
@@ -1,47 +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) 2013 by Martin Matuska <mm@FreeBSD.org>. All rights reserved.
- */
-
-#ifndef _LIBZFS_CORE_COMPAT_H
-#define _LIBZFS_CORE_COMPAT_H
-
-#include <libnvpair.h>
-#include <sys/param.h>
-#include <sys/types.h>
-#include <sys/fs/zfs.h>
-#include <sys/zfs_ioctl.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-int lzc_compat_pre(zfs_cmd_t *, zfs_ioc_t *, nvlist_t **);
-void lzc_compat_post(zfs_cmd_t *, const zfs_ioc_t);
-int lzc_compat_outnvl(zfs_cmd_t *, const zfs_ioc_t, nvlist_t **);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _LIBZFS_CORE_COMPAT_H */
diff --git a/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c b/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c
deleted file mode 100644
index 9b54e419705b2..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzpool/common/kernel.c
+++ /dev/null
@@ -1,1238 +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, 2015 by Delphix. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
- */
-
-#include <assert.h>
-#include <fcntl.h>
-#include <poll.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <zlib.h>
-#include <libgen.h>
-#include <sys/assfail.h>
-#include <sys/spa.h>
-#include <sys/stat.h>
-#include <sys/processor.h>
-#include <sys/zfs_context.h>
-#include <sys/rrwlock.h>
-#include <sys/zmod.h>
-#include <sys/utsname.h>
-#include <sys/systeminfo.h>
-#include <libzfs.h>
-
-/*
- * Emulation of kernel services in userland.
- */
-
-#ifndef __FreeBSD__
-int aok;
-#endif
-uint64_t physmem;
-vnode_t *rootdir = (vnode_t *)0xabcd1234;
-char hw_serial[HW_HOSTID_LEN];
-#ifdef illumos
-kmutex_t cpu_lock;
-#endif
-
-/* If set, all blocks read will be copied to the specified directory. */
-char *vn_dumpdir = NULL;
-
-struct utsname utsname = {
- "userland", "libzpool", "1", "1", "na"
-};
-
-/* this only exists to have its address taken */
-struct proc p0;
-
-/*
- * =========================================================================
- * threads
- * =========================================================================
- */
-/*ARGSUSED*/
-kthread_t *
-zk_thread_create(void (*func)(), void *arg)
-{
- thread_t tid;
-
- VERIFY(thr_create(0, 0, (void *(*)(void *))func, arg, THR_DETACHED,
- &tid) == 0);
-
- return ((void *)(uintptr_t)tid);
-}
-
-/*
- * =========================================================================
- * kstats
- * =========================================================================
- */
-/*ARGSUSED*/
-kstat_t *
-kstat_create(char *module, int instance, char *name, char *class,
- uchar_t type, ulong_t ndata, uchar_t ks_flag)
-{
- return (NULL);
-}
-
-/*ARGSUSED*/
-void
-kstat_named_init(kstat_named_t *knp, const char *name, uchar_t type)
-{}
-
-/*ARGSUSED*/
-void
-kstat_install(kstat_t *ksp)
-{}
-
-/*ARGSUSED*/
-void
-kstat_delete(kstat_t *ksp)
-{}
-
-/*
- * =========================================================================
- * mutexes
- * =========================================================================
- */
-void
-zmutex_init(kmutex_t *mp)
-{
- mp->m_owner = NULL;
- mp->initialized = B_TRUE;
- (void) _mutex_init(&mp->m_lock, USYNC_THREAD, NULL);
-}
-
-void
-zmutex_destroy(kmutex_t *mp)
-{
- ASSERT(mp->initialized == B_TRUE);
- ASSERT(mp->m_owner == NULL);
- (void) _mutex_destroy(&(mp)->m_lock);
- mp->m_owner = (void *)-1UL;
- mp->initialized = B_FALSE;
-}
-
-int
-zmutex_owned(kmutex_t *mp)
-{
- ASSERT(mp->initialized == B_TRUE);
-
- return (mp->m_owner == curthread);
-}
-
-void
-mutex_enter(kmutex_t *mp)
-{
- ASSERT(mp->initialized == B_TRUE);
- ASSERT(mp->m_owner != (void *)-1UL);
- ASSERT(mp->m_owner != curthread);
- VERIFY(mutex_lock(&mp->m_lock) == 0);
- ASSERT(mp->m_owner == NULL);
- mp->m_owner = curthread;
-}
-
-int
-mutex_tryenter(kmutex_t *mp)
-{
- ASSERT(mp->initialized == B_TRUE);
- ASSERT(mp->m_owner != (void *)-1UL);
- if (0 == mutex_trylock(&mp->m_lock)) {
- ASSERT(mp->m_owner == NULL);
- mp->m_owner = curthread;
- return (1);
- } else {
- return (0);
- }
-}
-
-void
-mutex_exit(kmutex_t *mp)
-{
- ASSERT(mp->initialized == B_TRUE);
- ASSERT(mutex_owner(mp) == curthread);
- mp->m_owner = NULL;
- VERIFY(mutex_unlock(&mp->m_lock) == 0);
-}
-
-void *
-mutex_owner(kmutex_t *mp)
-{
- ASSERT(mp->initialized == B_TRUE);
- return (mp->m_owner);
-}
-
-/*
- * =========================================================================
- * rwlocks
- * =========================================================================
- */
-/*ARGSUSED*/
-void
-rw_init(krwlock_t *rwlp, char *name, int type, void *arg)
-{
- rwlock_init(&rwlp->rw_lock, USYNC_THREAD, NULL);
- rwlp->rw_owner = NULL;
- rwlp->initialized = B_TRUE;
- rwlp->rw_count = 0;
-}
-
-void
-rw_destroy(krwlock_t *rwlp)
-{
- ASSERT(rwlp->rw_count == 0);
- rwlock_destroy(&rwlp->rw_lock);
- rwlp->rw_owner = (void *)-1UL;
- rwlp->initialized = B_FALSE;
-}
-
-void
-rw_enter(krwlock_t *rwlp, krw_t rw)
-{
- //ASSERT(!RW_LOCK_HELD(rwlp));
- ASSERT(rwlp->initialized == B_TRUE);
- ASSERT(rwlp->rw_owner != (void *)-1UL);
- ASSERT(rwlp->rw_owner != curthread);
-
- if (rw == RW_READER) {
- VERIFY(rw_rdlock(&rwlp->rw_lock) == 0);
- ASSERT(rwlp->rw_count >= 0);
- atomic_add_int(&rwlp->rw_count, 1);
- } else {
- VERIFY(rw_wrlock(&rwlp->rw_lock) == 0);
- ASSERT(rwlp->rw_count == 0);
- rwlp->rw_count = -1;
- rwlp->rw_owner = curthread;
- }
-}
-
-void
-rw_exit(krwlock_t *rwlp)
-{
- ASSERT(rwlp->initialized == B_TRUE);
- ASSERT(rwlp->rw_owner != (void *)-1UL);
-
- if (rwlp->rw_owner == curthread) {
- /* Write locked. */
- ASSERT(rwlp->rw_count == -1);
- rwlp->rw_count = 0;
- rwlp->rw_owner = NULL;
- } else {
- /* Read locked. */
- ASSERT(rwlp->rw_count > 0);
- atomic_add_int(&rwlp->rw_count, -1);
- }
- VERIFY(rw_unlock(&rwlp->rw_lock) == 0);
-}
-
-int
-rw_tryenter(krwlock_t *rwlp, krw_t rw)
-{
- int rv;
-
- ASSERT(rwlp->initialized == B_TRUE);
- ASSERT(rwlp->rw_owner != (void *)-1UL);
- ASSERT(rwlp->rw_owner != curthread);
-
- if (rw == RW_READER)
- rv = rw_tryrdlock(&rwlp->rw_lock);
- else
- rv = rw_trywrlock(&rwlp->rw_lock);
-
- if (rv == 0) {
- ASSERT(rwlp->rw_owner == NULL);
- if (rw == RW_READER) {
- ASSERT(rwlp->rw_count >= 0);
- atomic_add_int(&rwlp->rw_count, 1);
- } else {
- ASSERT(rwlp->rw_count == 0);
- rwlp->rw_count = -1;
- rwlp->rw_owner = curthread;
- }
- return (1);
- }
-
- return (0);
-}
-
-/*ARGSUSED*/
-int
-rw_tryupgrade(krwlock_t *rwlp)
-{
- ASSERT(rwlp->initialized == B_TRUE);
- ASSERT(rwlp->rw_owner != (void *)-1UL);
-
- return (0);
-}
-
-int
-rw_lock_held(krwlock_t *rwlp)
-{
-
- return (rwlp->rw_count != 0);
-}
-
-/*
- * =========================================================================
- * condition variables
- * =========================================================================
- */
-/*ARGSUSED*/
-void
-cv_init(kcondvar_t *cv, char *name, int type, void *arg)
-{
- VERIFY(cond_init(cv, name, NULL) == 0);
-}
-
-void
-cv_destroy(kcondvar_t *cv)
-{
- VERIFY(cond_destroy(cv) == 0);
-}
-
-void
-cv_wait(kcondvar_t *cv, kmutex_t *mp)
-{
- ASSERT(mutex_owner(mp) == curthread);
- mp->m_owner = NULL;
- int ret = cond_wait(cv, &mp->m_lock);
- VERIFY(ret == 0 || ret == EINTR);
- mp->m_owner = curthread;
-}
-
-/*
- * NB: this emulates FreeBSD cv_wait_sig(9), not the illumos one.
- * Meanings of the return code are different.
- * NB: this does not actually catch any signals.
- */
-int
-cv_wait_sig(kcondvar_t *cv, kmutex_t *mp)
-{
- cv_wait(cv, mp);
- return (0);
-}
-
-clock_t
-cv_timedwait(kcondvar_t *cv, kmutex_t *mp, clock_t abstime)
-{
- int error;
- struct timespec ts;
- struct timeval tv;
- clock_t delta;
-
- abstime += ddi_get_lbolt();
-top:
- delta = abstime - ddi_get_lbolt();
- if (delta <= 0)
- return (-1);
-
- if (gettimeofday(&tv, NULL) != 0)
- assert(!"gettimeofday() failed");
-
- ts.tv_sec = tv.tv_sec + delta / hz;
- ts.tv_nsec = tv.tv_usec * 1000 + (delta % hz) * (NANOSEC / hz);
- ASSERT(ts.tv_nsec >= 0);
-
- if (ts.tv_nsec >= NANOSEC) {
- ts.tv_sec++;
- ts.tv_nsec -= NANOSEC;
- }
-
- ASSERT(mutex_owner(mp) == curthread);
- mp->m_owner = NULL;
- error = pthread_cond_timedwait(cv, &mp->m_lock, &ts);
- mp->m_owner = curthread;
-
- if (error == EINTR)
- goto top;
-
- if (error == ETIMEDOUT)
- return (-1);
-
- ASSERT(error == 0);
-
- return (1);
-}
-
-/*ARGSUSED*/
-clock_t
-cv_timedwait_hires(kcondvar_t *cv, kmutex_t *mp, hrtime_t tim, hrtime_t res,
- int flag)
-{
- int error;
- timespec_t ts;
- hrtime_t delta;
-
- ASSERT(flag == 0 || flag == CALLOUT_FLAG_ABSOLUTE);
-
-top:
- delta = tim;
- if (flag & CALLOUT_FLAG_ABSOLUTE)
- delta -= gethrtime();
-
- if (delta <= 0)
- return (-1);
-
- clock_gettime(CLOCK_REALTIME, &ts);
- ts.tv_sec += delta / NANOSEC;
- ts.tv_nsec += delta % NANOSEC;
- if (ts.tv_nsec >= NANOSEC) {
- ts.tv_sec++;
- ts.tv_nsec -= NANOSEC;
- }
-
- ASSERT(mutex_owner(mp) == curthread);
- mp->m_owner = NULL;
- error = pthread_cond_timedwait(cv, &mp->m_lock, &ts);
- mp->m_owner = curthread;
-
- if (error == ETIMEDOUT)
- return (-1);
-
- if (error == EINTR)
- goto top;
-
- ASSERT(error == 0);
-
- return (1);
-}
-
-void
-cv_signal(kcondvar_t *cv)
-{
- VERIFY(cond_signal(cv) == 0);
-}
-
-void
-cv_broadcast(kcondvar_t *cv)
-{
- VERIFY(cond_broadcast(cv) == 0);
-}
-
-/*
- * =========================================================================
- * vnode operations
- * =========================================================================
- */
-/*
- * Note: for the xxxat() versions of these functions, we assume that the
- * starting vp is always rootdir (which is true for spa_directory.c, the only
- * ZFS consumer of these interfaces). We assert this is true, and then emulate
- * them by adding '/' in front of the path.
- */
-
-/*ARGSUSED*/
-int
-vn_open(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2, int x3)
-{
- int fd;
- int dump_fd;
- vnode_t *vp;
- int old_umask;
- char realpath[MAXPATHLEN];
- struct stat64 st;
-
- /*
- * If we're accessing a real disk from userland, we need to use
- * the character interface to avoid caching. This is particularly
- * important if we're trying to look at a real in-kernel storage
- * pool from userland, e.g. via zdb, because otherwise we won't
- * see the changes occurring under the segmap cache.
- * On the other hand, the stupid character device returns zero
- * for its size. So -- gag -- we open the block device to get
- * its size, and remember it for subsequent VOP_GETATTR().
- */
- if (strncmp(path, "/dev/", 5) == 0) {
- char *dsk;
- fd = open64(path, O_RDONLY);
- if (fd == -1)
- return (errno);
- if (fstat64(fd, &st) == -1) {
- close(fd);
- return (errno);
- }
- close(fd);
- (void) sprintf(realpath, "%s", path);
- dsk = strstr(path, "/dsk/");
- if (dsk != NULL)
- (void) sprintf(realpath + (dsk - path) + 1, "r%s",
- dsk + 1);
- } else {
- (void) sprintf(realpath, "%s", path);
- if (!(flags & FCREAT) && stat64(realpath, &st) == -1)
- return (errno);
- }
-
- if (flags & FCREAT)
- old_umask = umask(0);
-
- /*
- * The construct 'flags - FREAD' conveniently maps combinations of
- * FREAD and FWRITE to the corresponding O_RDONLY, O_WRONLY, and O_RDWR.
- */
- fd = open64(realpath, flags - FREAD, mode);
-
- if (flags & FCREAT)
- (void) umask(old_umask);
-
- if (vn_dumpdir != NULL) {
- char dumppath[MAXPATHLEN];
- (void) snprintf(dumppath, sizeof (dumppath),
- "%s/%s", vn_dumpdir, basename(realpath));
- dump_fd = open64(dumppath, O_CREAT | O_WRONLY, 0666);
- if (dump_fd == -1)
- return (errno);
- } else {
- dump_fd = -1;
- }
-
- if (fd == -1)
- return (errno);
-
- if (fstat64(fd, &st) == -1) {
- close(fd);
- return (errno);
- }
-
- (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
-
- *vpp = vp = umem_zalloc(sizeof (vnode_t), UMEM_NOFAIL);
-
- vp->v_fd = fd;
- vp->v_size = st.st_size;
- vp->v_path = spa_strdup(path);
- vp->v_dump_fd = dump_fd;
-
- return (0);
-}
-
-/*ARGSUSED*/
-int
-vn_openat(char *path, int x1, int flags, int mode, vnode_t **vpp, int x2,
- int x3, vnode_t *startvp, int fd)
-{
- char *realpath = umem_alloc(strlen(path) + 2, UMEM_NOFAIL);
- int ret;
-
- ASSERT(startvp == rootdir);
- (void) sprintf(realpath, "/%s", path);
-
- /* fd ignored for now, need if want to simulate nbmand support */
- ret = vn_open(realpath, x1, flags, mode, vpp, x2, x3);
-
- umem_free(realpath, strlen(path) + 2);
-
- return (ret);
-}
-
-/*ARGSUSED*/
-int
-vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len, offset_t offset,
- int x1, int x2, rlim64_t x3, void *x4, ssize_t *residp)
-{
- ssize_t iolen, split;
-
- if (uio == UIO_READ) {
- iolen = pread64(vp->v_fd, addr, len, offset);
- if (vp->v_dump_fd != -1) {
- int status =
- pwrite64(vp->v_dump_fd, addr, iolen, offset);
- ASSERT(status != -1);
- }
- } else {
- /*
- * To simulate partial disk writes, we split writes into two
- * system calls so that the process can be killed in between.
- */
- int sectors = len >> SPA_MINBLOCKSHIFT;
- split = (sectors > 0 ? rand() % sectors : 0) <<
- SPA_MINBLOCKSHIFT;
- iolen = pwrite64(vp->v_fd, addr, split, offset);
- iolen += pwrite64(vp->v_fd, (char *)addr + split,
- len - split, offset + split);
- }
-
- if (iolen == -1)
- return (errno);
- if (residp)
- *residp = len - iolen;
- else if (iolen != len)
- return (EIO);
- return (0);
-}
-
-void
-vn_close(vnode_t *vp, int openflag, cred_t *cr, kthread_t *td)
-{
- close(vp->v_fd);
- if (vp->v_dump_fd != -1)
- close(vp->v_dump_fd);
- spa_strfree(vp->v_path);
- umem_free(vp, sizeof (vnode_t));
-}
-
-/*
- * At a minimum we need to update the size since vdev_reopen()
- * will no longer call vn_openat().
- */
-int
-fop_getattr(vnode_t *vp, vattr_t *vap)
-{
- struct stat64 st;
-
- if (fstat64(vp->v_fd, &st) == -1) {
- close(vp->v_fd);
- return (errno);
- }
-
- vap->va_size = st.st_size;
- return (0);
-}
-
-#ifdef ZFS_DEBUG
-
-/*
- * =========================================================================
- * Figure out which debugging statements to print
- * =========================================================================
- */
-
-static char *dprintf_string;
-static int dprintf_print_all;
-
-int
-dprintf_find_string(const char *string)
-{
- char *tmp_str = dprintf_string;
- int len = strlen(string);
-
- /*
- * Find out if this is a string we want to print.
- * String format: file1.c,function_name1,file2.c,file3.c
- */
-
- while (tmp_str != NULL) {
- if (strncmp(tmp_str, string, len) == 0 &&
- (tmp_str[len] == ',' || tmp_str[len] == '\0'))
- return (1);
- tmp_str = strchr(tmp_str, ',');
- if (tmp_str != NULL)
- tmp_str++; /* Get rid of , */
- }
- return (0);
-}
-
-void
-dprintf_setup(int *argc, char **argv)
-{
- int i, j;
-
- /*
- * Debugging can be specified two ways: by setting the
- * environment variable ZFS_DEBUG, or by including a
- * "debug=..." argument on the command line. The command
- * line setting overrides the environment variable.
- */
-
- for (i = 1; i < *argc; i++) {
- int len = strlen("debug=");
- /* First look for a command line argument */
- if (strncmp("debug=", argv[i], len) == 0) {
- dprintf_string = argv[i] + len;
- /* Remove from args */
- for (j = i; j < *argc; j++)
- argv[j] = argv[j+1];
- argv[j] = NULL;
- (*argc)--;
- }
- }
-
- if (dprintf_string == NULL) {
- /* Look for ZFS_DEBUG environment variable */
- dprintf_string = getenv("ZFS_DEBUG");
- }
-
- /*
- * Are we just turning on all debugging?
- */
- if (dprintf_find_string("on"))
- dprintf_print_all = 1;
-
- if (dprintf_string != NULL)
- zfs_flags |= ZFS_DEBUG_DPRINTF;
-}
-
-int
-sysctl_handle_64(SYSCTL_HANDLER_ARGS)
-{
- return (0);
-}
-
-/*
- * =========================================================================
- * debug printfs
- * =========================================================================
- */
-void
-__dprintf(const char *file, const char *func, int line, const char *fmt, ...)
-{
- const char *newfile;
- va_list adx;
-
- /*
- * Get rid of annoying "../common/" prefix to filename.
- */
- newfile = strrchr(file, '/');
- if (newfile != NULL) {
- newfile = newfile + 1; /* Get rid of leading / */
- } else {
- newfile = file;
- }
-
- if (dprintf_print_all ||
- dprintf_find_string(newfile) ||
- dprintf_find_string(func)) {
- /* Print out just the function name if requested */
- flockfile(stdout);
- if (dprintf_find_string("pid"))
- (void) printf("%d ", getpid());
- if (dprintf_find_string("tid"))
- (void) printf("%lu ", thr_self());
-#if 0
- if (dprintf_find_string("cpu"))
- (void) printf("%u ", getcpuid());
-#endif
- if (dprintf_find_string("time"))
- (void) printf("%llu ", gethrtime());
- if (dprintf_find_string("long"))
- (void) printf("%s, line %d: ", newfile, line);
- (void) printf("%s: ", func);
- va_start(adx, fmt);
- (void) vprintf(fmt, adx);
- va_end(adx);
- funlockfile(stdout);
- }
-}
-
-#endif /* ZFS_DEBUG */
-
-/*
- * =========================================================================
- * cmn_err() and panic()
- * =========================================================================
- */
-static char ce_prefix[CE_IGNORE][10] = { "", "NOTICE: ", "WARNING: ", "" };
-static char ce_suffix[CE_IGNORE][2] = { "", "\n", "\n", "" };
-
-void
-vpanic(const char *fmt, va_list adx)
-{
- char buf[512];
- (void) vsnprintf(buf, 512, fmt, adx);
- assfail(buf, NULL, 0);
- abort(); /* necessary to make vpanic meet noreturn requirements */
-}
-
-void
-panic(const char *fmt, ...)
-{
- va_list adx;
-
- va_start(adx, fmt);
- vpanic(fmt, adx);
- va_end(adx);
-}
-
-void
-vcmn_err(int ce, const char *fmt, va_list adx)
-{
- if (ce == CE_PANIC)
- vpanic(fmt, adx);
- if (ce != CE_NOTE) { /* suppress noise in userland stress testing */
- (void) fprintf(stderr, "%s", ce_prefix[ce]);
- (void) vfprintf(stderr, fmt, adx);
- (void) fprintf(stderr, "%s", ce_suffix[ce]);
- }
-}
-
-/*PRINTFLIKE2*/
-void
-cmn_err(int ce, const char *fmt, ...)
-{
- va_list adx;
-
- va_start(adx, fmt);
- vcmn_err(ce, fmt, adx);
- va_end(adx);
-}
-
-/*
- * =========================================================================
- * kobj interfaces
- * =========================================================================
- */
-struct _buf *
-kobj_open_file(char *name)
-{
- struct _buf *file;
- vnode_t *vp;
-
- /* set vp as the _fd field of the file */
- if (vn_openat(name, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0, rootdir,
- -1) != 0)
- return ((void *)-1UL);
-
- file = umem_zalloc(sizeof (struct _buf), UMEM_NOFAIL);
- file->_fd = (intptr_t)vp;
- return (file);
-}
-
-int
-kobj_read_file(struct _buf *file, char *buf, unsigned size, unsigned off)
-{
- ssize_t resid;
-
- vn_rdwr(UIO_READ, (vnode_t *)file->_fd, buf, size, (offset_t)off,
- UIO_SYSSPACE, 0, 0, 0, &resid);
-
- return (size - resid);
-}
-
-void
-kobj_close_file(struct _buf *file)
-{
- vn_close((vnode_t *)file->_fd, 0, NULL, NULL);
- umem_free(file, sizeof (struct _buf));
-}
-
-int
-kobj_get_filesize(struct _buf *file, uint64_t *size)
-{
- struct stat64 st;
- vnode_t *vp = (vnode_t *)file->_fd;
-
- if (fstat64(vp->v_fd, &st) == -1) {
- vn_close(vp, 0, NULL, NULL);
- return (errno);
- }
- *size = st.st_size;
- return (0);
-}
-
-/*
- * =========================================================================
- * misc routines
- * =========================================================================
- */
-
-void
-delay(clock_t ticks)
-{
- poll(0, 0, ticks * (1000 / hz));
-}
-
-#if 0
-/*
- * Find highest one bit set.
- * Returns bit number + 1 of highest bit that is set, otherwise returns 0.
- */
-int
-highbit64(uint64_t i)
-{
- int h = 1;
-
- if (i == 0)
- return (0);
- if (i & 0xffffffff00000000ULL) {
- h += 32; i >>= 32;
- }
- if (i & 0xffff0000) {
- h += 16; i >>= 16;
- }
- if (i & 0xff00) {
- h += 8; i >>= 8;
- }
- if (i & 0xf0) {
- h += 4; i >>= 4;
- }
- if (i & 0xc) {
- h += 2; i >>= 2;
- }
- if (i & 0x2) {
- h += 1;
- }
- return (h);
-}
-#endif
-
-static int random_fd = -1, urandom_fd = -1;
-
-static int
-random_get_bytes_common(uint8_t *ptr, size_t len, int fd)
-{
- size_t resid = len;
- ssize_t bytes;
-
- ASSERT(fd != -1);
-
- while (resid != 0) {
- bytes = read(fd, ptr, resid);
- ASSERT3S(bytes, >=, 0);
- ptr += bytes;
- resid -= bytes;
- }
-
- return (0);
-}
-
-int
-random_get_bytes(uint8_t *ptr, size_t len)
-{
- return (random_get_bytes_common(ptr, len, random_fd));
-}
-
-int
-random_get_pseudo_bytes(uint8_t *ptr, size_t len)
-{
- return (random_get_bytes_common(ptr, len, urandom_fd));
-}
-
-int
-ddi_strtoul(const char *hw_serial, char **nptr, int base, unsigned long *result)
-{
- char *end;
-
- *result = strtoul(hw_serial, &end, base);
- if (*result == 0)
- return (errno);
- return (0);
-}
-
-int
-ddi_strtoull(const char *str, char **nptr, int base, u_longlong_t *result)
-{
- char *end;
-
- *result = strtoull(str, &end, base);
- if (*result == 0)
- return (errno);
- return (0);
-}
-
-#ifdef illumos
-/* ARGSUSED */
-cyclic_id_t
-cyclic_add(cyc_handler_t *hdlr, cyc_time_t *when)
-{
- return (1);
-}
-
-/* ARGSUSED */
-void
-cyclic_remove(cyclic_id_t id)
-{
-}
-
-/* ARGSUSED */
-int
-cyclic_reprogram(cyclic_id_t id, hrtime_t expiration)
-{
- return (1);
-}
-#endif
-
-/*
- * =========================================================================
- * kernel emulation setup & teardown
- * =========================================================================
- */
-static int
-umem_out_of_memory(void)
-{
- char errmsg[] = "out of memory -- generating core dump\n";
-
- write(fileno(stderr), errmsg, sizeof (errmsg));
- abort();
- return (0);
-}
-
-void
-kernel_init(int mode)
-{
- extern uint_t rrw_tsd_key;
-
- umem_nofail_callback(umem_out_of_memory);
-
- physmem = sysconf(_SC_PHYS_PAGES);
-
- dprintf("physmem = %llu pages (%.2f GB)\n", physmem,
- (double)physmem * sysconf(_SC_PAGE_SIZE) / (1ULL << 30));
-
- (void) snprintf(hw_serial, sizeof (hw_serial), "%ld",
- (mode & FWRITE) ? get_system_hostid() : 0);
-
- VERIFY((random_fd = open("/dev/random", O_RDONLY)) != -1);
- VERIFY((urandom_fd = open("/dev/urandom", O_RDONLY)) != -1);
-
- system_taskq_init();
-
-#ifdef illumos
- mutex_init(&cpu_lock, NULL, MUTEX_DEFAULT, NULL);
-#endif
-
- spa_init(mode);
-
- tsd_create(&rrw_tsd_key, rrw_tsd_destroy);
-}
-
-void
-kernel_fini(void)
-{
- spa_fini();
-
- system_taskq_fini();
-
- close(random_fd);
- close(urandom_fd);
-
- random_fd = -1;
- urandom_fd = -1;
-}
-
-/* ARGSUSED */
-uint32_t
-zone_get_hostid(void *zonep)
-{
- /*
- * We're emulating the system's hostid in userland.
- */
- return (strtoul(hw_serial, NULL, 10));
-}
-
-int
-z_uncompress(void *dst, size_t *dstlen, const void *src, size_t srclen)
-{
- int ret;
- uLongf len = *dstlen;
-
- if ((ret = uncompress(dst, &len, src, srclen)) == Z_OK)
- *dstlen = (size_t)len;
-
- return (ret);
-}
-
-int
-z_compress_level(void *dst, size_t *dstlen, const void *src, size_t srclen,
- int level)
-{
- int ret;
- uLongf len = *dstlen;
-
- if ((ret = compress2(dst, &len, src, srclen, level)) == Z_OK)
- *dstlen = (size_t)len;
-
- return (ret);
-}
-
-uid_t
-crgetuid(cred_t *cr)
-{
- return (0);
-}
-
-uid_t
-crgetruid(cred_t *cr)
-{
- return (0);
-}
-
-gid_t
-crgetgid(cred_t *cr)
-{
- return (0);
-}
-
-int
-crgetngroups(cred_t *cr)
-{
- return (0);
-}
-
-gid_t *
-crgetgroups(cred_t *cr)
-{
- return (NULL);
-}
-
-int
-zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr)
-{
- return (0);
-}
-
-int
-zfs_secpolicy_rename_perms(const char *from, const char *to, cred_t *cr)
-{
- return (0);
-}
-
-int
-zfs_secpolicy_destroy_perms(const char *name, cred_t *cr)
-{
- return (0);
-}
-
-ksiddomain_t *
-ksid_lookupdomain(const char *dom)
-{
- ksiddomain_t *kd;
-
- kd = umem_zalloc(sizeof (ksiddomain_t), UMEM_NOFAIL);
- kd->kd_name = spa_strdup(dom);
- return (kd);
-}
-
-void
-ksiddomain_rele(ksiddomain_t *ksid)
-{
- spa_strfree(ksid->kd_name);
- umem_free(ksid, sizeof (ksiddomain_t));
-}
-
-/*
- * Do not change the length of the returned string; it must be freed
- * with strfree().
- */
-char *
-kmem_asprintf(const char *fmt, ...)
-{
- int size;
- va_list adx;
- char *buf;
-
- va_start(adx, fmt);
- size = vsnprintf(NULL, 0, fmt, adx) + 1;
- va_end(adx);
-
- buf = kmem_alloc(size, KM_SLEEP);
-
- va_start(adx, fmt);
- size = vsnprintf(buf, size, fmt, adx);
- va_end(adx);
-
- return (buf);
-}
-
-/* ARGSUSED */
-int
-zfs_onexit_fd_hold(int fd, minor_t *minorp)
-{
- *minorp = 0;
- return (0);
-}
-
-/* ARGSUSED */
-void
-zfs_onexit_fd_rele(int fd)
-{
-}
-
-/* ARGSUSED */
-int
-zfs_onexit_add_cb(minor_t minor, void (*func)(void *), void *data,
- uint64_t *action_handle)
-{
- return (0);
-}
-
-/* ARGSUSED */
-int
-zfs_onexit_del_cb(minor_t minor, uint64_t action_handle, boolean_t fire)
-{
- return (0);
-}
-
-/* ARGSUSED */
-int
-zfs_onexit_cb_data(minor_t minor, uint64_t action_handle, void **data)
-{
- return (0);
-}
-
-#ifdef __FreeBSD__
-/* ARGSUSED */
-int
-zvol_create_minors(const char *name)
-{
- return (0);
-}
-#endif
-
-#ifdef illumos
-void
-bioinit(buf_t *bp)
-{
- bzero(bp, sizeof (buf_t));
-}
-
-void
-biodone(buf_t *bp)
-{
- if (bp->b_iodone != NULL) {
- (*(bp->b_iodone))(bp);
- return;
- }
- ASSERT((bp->b_flags & B_DONE) == 0);
- bp->b_flags |= B_DONE;
-}
-
-void
-bioerror(buf_t *bp, int error)
-{
- ASSERT(bp != NULL);
- ASSERT(error >= 0);
-
- if (error != 0) {
- bp->b_flags |= B_ERROR;
- } else {
- bp->b_flags &= ~B_ERROR;
- }
- bp->b_error = error;
-}
-
-
-int
-geterror(struct buf *bp)
-{
- int error = 0;
-
- if (bp->b_flags & B_ERROR) {
- error = bp->b_error;
- if (!error)
- error = EIO;
- }
- return (error);
-}
-#endif
diff --git a/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h b/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h
deleted file mode 100644
index 6f1a17f278529..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzpool/common/sys/zfs_context.h
+++ /dev/null
@@ -1,838 +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, 2016 by Delphix. All rights reserved.
- * Copyright (c) 2012, Joyent, Inc. All rights reserved.
- */
-/*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
- */
-
-#ifndef _SYS_ZFS_CONTEXT_H
-#define _SYS_ZFS_CONTEXT_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define _SYS_MUTEX_H
-#define _SYS_RWLOCK_H
-#define _SYS_CONDVAR_H
-#define _SYS_SYSTM_H
-#define _SYS_T_LOCK_H
-#define _SYS_VNODE_H
-#define _SYS_VFS_H
-#define _SYS_SUNDDI_H
-#define _SYS_CALLB_H
-#define _SYS_SCHED_H_
-
-#include <solaris.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <stdarg.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#include <strings.h>
-#include <thread.h>
-#include <assert.h>
-#include <limits.h>
-#include <dirent.h>
-#include <time.h>
-#include <math.h>
-#include <umem.h>
-#include <inttypes.h>
-#include <fsshare.h>
-#include <pthread.h>
-#include <sched.h>
-#include <setjmp.h>
-#include <sys/debug.h>
-#include <sys/note.h>
-#include <sys/types.h>
-#include <sys/cred.h>
-#include <sys/atomic.h>
-#include <sys/sysmacros.h>
-#include <sys/bitmap.h>
-#include <sys/resource.h>
-#include <sys/byteorder.h>
-#include <sys/list.h>
-#include <sys/time.h>
-#include <sys/uio.h>
-#include <sys/mntent.h>
-#include <sys/mnttab.h>
-#include <sys/zfs_debug.h>
-#include <sys/sdt.h>
-#include <sys/kstat.h>
-#include <sys/u8_textprep.h>
-#include <sys/kernel.h>
-#include <sys/disk.h>
-#include <sys/sysevent.h>
-#include <sys/sysevent/eventdefs.h>
-#include <sys/sysevent/dev.h>
-#include <machine/atomic.h>
-#include <sys/debug.h>
-#ifdef illumos
-#include "zfs.h"
-#endif
-
-#define ZFS_EXPORTS_PATH "/etc/zfs/exports"
-
-/*
- * Debugging
- */
-
-/*
- * Note that we are not using the debugging levels.
- */
-
-#define CE_CONT 0 /* continuation */
-#define CE_NOTE 1 /* notice */
-#define CE_WARN 2 /* warning */
-#define CE_PANIC 3 /* panic */
-#define CE_IGNORE 4 /* print nothing */
-
-/*
- * ZFS debugging
- */
-
-#define ZFS_LOG(...) do { } while (0)
-
-typedef u_longlong_t rlim64_t;
-#define RLIM64_INFINITY ((rlim64_t)-3)
-
-#ifdef ZFS_DEBUG
-extern void dprintf_setup(int *argc, char **argv);
-#endif /* ZFS_DEBUG */
-
-extern void cmn_err(int, const char *, ...);
-extern void vcmn_err(int, const char *, __va_list);
-extern void panic(const char *, ...) __NORETURN;
-extern void vpanic(const char *, __va_list) __NORETURN;
-
-#define fm_panic panic
-
-extern int aok;
-
-/*
- * DTrace SDT probes have different signatures in userland than they do in
- * the kernel. If they're being used in kernel code, re-define them out of
- * existence for their counterparts in libzpool.
- *
- * Here's an example of how to use the set-error probes in userland:
- * zfs$target:::set-error /arg0 == EBUSY/ {stack();}
- *
- * Here's an example of how to use DTRACE_PROBE probes in userland:
- * If there is a probe declared as follows:
- * DTRACE_PROBE2(zfs__probe_name, uint64_t, blkid, dnode_t *, dn);
- * Then you can use it as follows:
- * zfs$target:::probe2 /copyinstr(arg0) == "zfs__probe_name"/
- * {printf("%u %p\n", arg1, arg2);}
- */
-
-#ifdef DTRACE_PROBE
-#undef DTRACE_PROBE
-#endif /* DTRACE_PROBE */
-#ifdef illumos
-#define DTRACE_PROBE(a) \
- ZFS_PROBE0(#a)
-#endif
-
-#ifdef DTRACE_PROBE1
-#undef DTRACE_PROBE1
-#endif /* DTRACE_PROBE1 */
-#ifdef illumos
-#define DTRACE_PROBE1(a, b, c) \
- ZFS_PROBE1(#a, (unsigned long)c)
-#endif
-
-#ifdef DTRACE_PROBE2
-#undef DTRACE_PROBE2
-#endif /* DTRACE_PROBE2 */
-#ifdef illumos
-#define DTRACE_PROBE2(a, b, c, d, e) \
- ZFS_PROBE2(#a, (unsigned long)c, (unsigned long)e)
-#endif
-
-#ifdef DTRACE_PROBE3
-#undef DTRACE_PROBE3
-#endif /* DTRACE_PROBE3 */
-#ifdef illumos
-#define DTRACE_PROBE3(a, b, c, d, e, f, g) \
- ZFS_PROBE3(#a, (unsigned long)c, (unsigned long)e, (unsigned long)g)
-#endif
-
-#ifdef DTRACE_PROBE4
-#undef DTRACE_PROBE4
-#endif /* DTRACE_PROBE4 */
-#ifdef illumos
-#define DTRACE_PROBE4(a, b, c, d, e, f, g, h, i) \
- ZFS_PROBE4(#a, (unsigned long)c, (unsigned long)e, (unsigned long)g, \
- (unsigned long)i)
-#endif
-
-#ifdef illumos
-/*
- * We use the comma operator so that this macro can be used without much
- * additional code. For example, "return (EINVAL);" becomes
- * "return (SET_ERROR(EINVAL));". Note that the argument will be evaluated
- * twice, so it should not have side effects (e.g. something like:
- * "return (SET_ERROR(log_error(EINVAL, info)));" would log the error twice).
- */
-#define SET_ERROR(err) (ZFS_SET_ERROR(err), err)
-#else /* !illumos */
-
-#define DTRACE_PROBE(a) ((void)0)
-#define DTRACE_PROBE1(a, b, c) ((void)0)
-#define DTRACE_PROBE2(a, b, c, d, e) ((void)0)
-#define DTRACE_PROBE3(a, b, c, d, e, f, g) ((void)0)
-#define DTRACE_PROBE4(a, b, c, d, e, f, g, h, i) ((void)0)
-
-#define SET_ERROR(err) (err)
-#endif /* !illumos */
-
-/*
- * Threads
- */
-#define curthread ((void *)(uintptr_t)thr_self())
-
-#define kpreempt(x) sched_yield()
-
-typedef struct kthread kthread_t;
-
-#define thread_create(stk, stksize, func, arg, len, pp, state, pri) \
- zk_thread_create(func, arg)
-#define thread_exit() thr_exit(NULL)
-#define thread_join(t) panic("libzpool cannot join threads")
-
-#define newproc(f, a, cid, pri, ctp, pid) (ENOSYS)
-
-/* in libzpool, p0 exists only to have its address taken */
-struct proc {
- uintptr_t this_is_never_used_dont_dereference_it;
-};
-
-extern struct proc p0;
-#define curproc (&p0)
-
-#define PS_NONE -1
-
-extern kthread_t *zk_thread_create(void (*func)(void*), void *arg);
-
-#define issig(why) (FALSE)
-#define ISSIG(thr, why) (FALSE)
-
-/*
- * Mutexes
- */
-typedef struct kmutex {
- void *m_owner;
- boolean_t initialized;
- mutex_t m_lock;
-} kmutex_t;
-
-#define MUTEX_DEFAULT USYNC_THREAD
-#undef MUTEX_HELD
-#undef MUTEX_NOT_HELD
-#define MUTEX_HELD(m) ((m)->m_owner == curthread)
-#define MUTEX_NOT_HELD(m) (!MUTEX_HELD(m))
-#define _mutex_held(m) pthread_mutex_isowned_np(m)
-
-/*
- * Argh -- we have to get cheesy here because the kernel and userland
- * have different signatures for the same routine.
- */
-//extern int _mutex_init(mutex_t *mp, int type, void *arg);
-//extern int _mutex_destroy(mutex_t *mp);
-//extern int _mutex_owned(mutex_t *mp);
-
-#define mutex_init(mp, b, c, d) zmutex_init((kmutex_t *)(mp))
-#define mutex_destroy(mp) zmutex_destroy((kmutex_t *)(mp))
-#define mutex_owned(mp) zmutex_owned((kmutex_t *)(mp))
-
-extern void zmutex_init(kmutex_t *mp);
-extern void zmutex_destroy(kmutex_t *mp);
-extern int zmutex_owned(kmutex_t *mp);
-extern void mutex_enter(kmutex_t *mp);
-extern void mutex_exit(kmutex_t *mp);
-extern int mutex_tryenter(kmutex_t *mp);
-extern void *mutex_owner(kmutex_t *mp);
-
-/*
- * RW locks
- */
-typedef struct krwlock {
- int rw_count;
- void *rw_owner;
- boolean_t initialized;
- rwlock_t rw_lock;
-} krwlock_t;
-
-typedef int krw_t;
-
-#define RW_READER 0
-#define RW_WRITER 1
-#define RW_DEFAULT USYNC_THREAD
-
-#undef RW_READ_HELD
-#define RW_READ_HELD(x) ((x)->rw_owner == NULL && (x)->rw_count > 0)
-
-#undef RW_WRITE_HELD
-#define RW_WRITE_HELD(x) ((x)->rw_owner == curthread)
-#define RW_LOCK_HELD(x) rw_lock_held(x)
-
-#undef RW_LOCK_HELD
-#define RW_LOCK_HELD(x) (RW_READ_HELD(x) || RW_WRITE_HELD(x))
-
-extern void rw_init(krwlock_t *rwlp, char *name, int type, void *arg);
-extern void rw_destroy(krwlock_t *rwlp);
-extern void rw_enter(krwlock_t *rwlp, krw_t rw);
-extern int rw_tryenter(krwlock_t *rwlp, krw_t rw);
-extern int rw_tryupgrade(krwlock_t *rwlp);
-extern void rw_exit(krwlock_t *rwlp);
-extern int rw_lock_held(krwlock_t *rwlp);
-#define rw_downgrade(rwlp) do { } while (0)
-
-extern uid_t crgetuid(cred_t *cr);
-extern uid_t crgetruid(cred_t *cr);
-extern gid_t crgetgid(cred_t *cr);
-extern int crgetngroups(cred_t *cr);
-extern gid_t *crgetgroups(cred_t *cr);
-
-/*
- * Condition variables
- */
-typedef cond_t kcondvar_t;
-
-#define CV_DEFAULT USYNC_THREAD
-#define CALLOUT_FLAG_ABSOLUTE 0x2
-
-extern void cv_init(kcondvar_t *cv, char *name, int type, void *arg);
-extern void cv_destroy(kcondvar_t *cv);
-extern void cv_wait(kcondvar_t *cv, kmutex_t *mp);
-extern int cv_wait_sig(kcondvar_t *cv, kmutex_t *mp);
-extern clock_t cv_timedwait(kcondvar_t *cv, kmutex_t *mp, clock_t abstime);
-#define cv_timedwait_sig(cvp, mp, t) cv_timedwait(cvp, mp, t)
-extern clock_t cv_timedwait_hires(kcondvar_t *cvp, kmutex_t *mp, hrtime_t tim,
- hrtime_t res, int flag);
-#define cv_timedwait_sig_hires(cvp, mp, t, r, f) \
- cv_timedwait_hires(cvp, mp, t, r, f)
-extern void cv_signal(kcondvar_t *cv);
-extern void cv_broadcast(kcondvar_t *cv);
-
-/*
- * Thread-specific data
- */
-#define tsd_get(k) pthread_getspecific(k)
-#define tsd_set(k, v) pthread_setspecific(k, v)
-#define tsd_create(kp, d) pthread_key_create(kp, d)
-#define tsd_destroy(kp) /* nothing */
-
-/*
- * Kernel memory
- */
-#define KM_SLEEP UMEM_NOFAIL
-#define KM_PUSHPAGE KM_SLEEP
-#define KM_NOSLEEP UMEM_DEFAULT
-#define KM_NORMALPRI 0 /* not needed with UMEM_DEFAULT */
-#define KMC_NODEBUG UMC_NODEBUG
-#define KMC_NOTOUCH 0 /* not needed for userland caches */
-#define KM_NODEBUG 0
-#define kmem_alloc(_s, _f) umem_alloc(_s, _f)
-#define kmem_zalloc(_s, _f) umem_zalloc(_s, _f)
-#define kmem_free(_b, _s) umem_free(_b, _s)
-#define kmem_size() (physmem * PAGESIZE)
-#define kmem_cache_create(_a, _b, _c, _d, _e, _f, _g, _h, _i) \
- umem_cache_create(_a, _b, _c, _d, _e, _f, _g, _h, _i)
-#define kmem_cache_destroy(_c) umem_cache_destroy(_c)
-#define kmem_cache_alloc(_c, _f) umem_cache_alloc(_c, _f)
-#define kmem_cache_free(_c, _b) umem_cache_free(_c, _b)
-#define kmem_debugging() 0
-#define kmem_cache_reap_active() (B_FALSE)
-#define kmem_cache_reap_soon(_c) /* nothing */
-#define kmem_cache_set_move(_c, _cb) /* nothing */
-#define POINTER_INVALIDATE(_pp) /* nothing */
-#define POINTER_IS_VALID(_p) 0
-
-typedef umem_cache_t kmem_cache_t;
-
-typedef enum kmem_cbrc {
- KMEM_CBRC_YES,
- KMEM_CBRC_NO,
- KMEM_CBRC_LATER,
- KMEM_CBRC_DONT_NEED,
- KMEM_CBRC_DONT_KNOW
-} kmem_cbrc_t;
-
-/*
- * Task queues
- */
-typedef struct taskq taskq_t;
-typedef uintptr_t taskqid_t;
-typedef void (task_func_t)(void *);
-
-typedef struct taskq_ent {
- struct taskq_ent *tqent_next;
- struct taskq_ent *tqent_prev;
- task_func_t *tqent_func;
- void *tqent_arg;
- uintptr_t tqent_flags;
-} taskq_ent_t;
-
-#define TQENT_FLAG_PREALLOC 0x1 /* taskq_dispatch_ent used */
-
-#define TASKQ_PREPOPULATE 0x0001
-#define TASKQ_CPR_SAFE 0x0002 /* Use CPR safe protocol */
-#define TASKQ_DYNAMIC 0x0004 /* Use dynamic thread scheduling */
-#define TASKQ_THREADS_CPU_PCT 0x0008 /* Scale # threads by # cpus */
-#define TASKQ_DC_BATCH 0x0010 /* Mark threads as batch */
-
-#define TQ_SLEEP KM_SLEEP /* Can block for memory */
-#define TQ_NOSLEEP KM_NOSLEEP /* cannot block for memory; may fail */
-#define TQ_NOQUEUE 0x02 /* Do not enqueue if can't dispatch */
-#define TQ_FRONT 0x08 /* Queue in front */
-
-#define TASKQID_INVALID ((taskqid_t)0)
-
-extern taskq_t *system_taskq;
-
-extern taskq_t *taskq_create(const char *, int, pri_t, int, int, uint_t);
-#define taskq_create_proc(a, b, c, d, e, p, f) \
- (taskq_create(a, b, c, d, e, f))
-#define taskq_create_sysdc(a, b, d, e, p, dc, f) \
- (taskq_create(a, b, maxclsyspri, d, e, f))
-extern taskqid_t taskq_dispatch(taskq_t *, task_func_t, void *, uint_t);
-extern void taskq_dispatch_ent(taskq_t *, task_func_t, void *, uint_t,
- taskq_ent_t *);
-extern void taskq_destroy(taskq_t *);
-extern void taskq_wait(taskq_t *);
-extern void taskq_wait_id(taskq_t *, taskqid_t);
-extern int taskq_member(taskq_t *, void *);
-extern void system_taskq_init(void);
-extern void system_taskq_fini(void);
-
-#define taskq_dispatch_safe(tq, func, arg, flags, task) \
- taskq_dispatch((tq), (func), (arg), (flags))
-
-#define XVA_MAPSIZE 3
-#define XVA_MAGIC 0x78766174
-
-/*
- * vnodes
- */
-typedef struct vnode {
- uint64_t v_size;
- int v_fd;
- char *v_path;
- int v_dump_fd;
-} vnode_t;
-
-extern char *vn_dumpdir;
-#define AV_SCANSTAMP_SZ 32 /* length of anti-virus scanstamp */
-
-typedef struct xoptattr {
- timestruc_t xoa_createtime; /* Create time of file */
- uint8_t xoa_archive;
- uint8_t xoa_system;
- uint8_t xoa_readonly;
- uint8_t xoa_hidden;
- uint8_t xoa_nounlink;
- uint8_t xoa_immutable;
- uint8_t xoa_appendonly;
- uint8_t xoa_nodump;
- uint8_t xoa_settable;
- uint8_t xoa_opaque;
- uint8_t xoa_av_quarantined;
- uint8_t xoa_av_modified;
- uint8_t xoa_av_scanstamp[AV_SCANSTAMP_SZ];
- uint8_t xoa_reparse;
- uint8_t xoa_offline;
- uint8_t xoa_sparse;
-} xoptattr_t;
-
-typedef struct vattr {
- uint_t va_mask; /* bit-mask of attributes */
- u_offset_t va_size; /* file size in bytes */
-} vattr_t;
-
-
-typedef struct xvattr {
- vattr_t xva_vattr; /* Embedded vattr structure */
- uint32_t xva_magic; /* Magic Number */
- uint32_t xva_mapsize; /* Size of attr bitmap (32-bit words) */
- uint32_t *xva_rtnattrmapp; /* Ptr to xva_rtnattrmap[] */
- uint32_t xva_reqattrmap[XVA_MAPSIZE]; /* Requested attrs */
- uint32_t xva_rtnattrmap[XVA_MAPSIZE]; /* Returned attrs */
- xoptattr_t xva_xoptattrs; /* Optional attributes */
-} xvattr_t;
-
-typedef struct vsecattr {
- uint_t vsa_mask; /* See below */
- int vsa_aclcnt; /* ACL entry count */
- void *vsa_aclentp; /* pointer to ACL entries */
- int vsa_dfaclcnt; /* default ACL entry count */
- void *vsa_dfaclentp; /* pointer to default ACL entries */
- size_t vsa_aclentsz; /* ACE size in bytes of vsa_aclentp */
-} vsecattr_t;
-
-#define AT_TYPE 0x00001
-#define AT_MODE 0x00002
-#define AT_UID 0x00004
-#define AT_GID 0x00008
-#define AT_FSID 0x00010
-#define AT_NODEID 0x00020
-#define AT_NLINK 0x00040
-#define AT_SIZE 0x00080
-#define AT_ATIME 0x00100
-#define AT_MTIME 0x00200
-#define AT_CTIME 0x00400
-#define AT_RDEV 0x00800
-#define AT_BLKSIZE 0x01000
-#define AT_NBLOCKS 0x02000
-#define AT_SEQ 0x08000
-#define AT_XVATTR 0x10000
-
-#define CRCREAT 0
-
-extern int fop_getattr(vnode_t *vp, vattr_t *vap);
-
-#define VOP_CLOSE(vp, f, c, o, cr, ct) 0
-#define VOP_PUTPAGE(vp, of, sz, fl, cr, ct) 0
-#define VOP_GETATTR(vp, vap, cr) fop_getattr((vp), (vap));
-
-#define VOP_FSYNC(vp, f, cr, ct) fsync((vp)->v_fd)
-
-#define VN_RELE(vp) vn_close(vp, 0, NULL, NULL)
-#define VN_RELE_ASYNC(vp, taskq) vn_close(vp, 0, NULL, NULL)
-
-#define vn_lock(vp, type)
-#define VOP_UNLOCK(vp)
-
-extern int vn_open(char *path, int x1, int oflags, int mode, vnode_t **vpp,
- int x2, int x3);
-extern int vn_openat(char *path, int x1, int oflags, int mode, vnode_t **vpp,
- int x2, int x3, vnode_t *vp, int fd);
-extern int vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len,
- offset_t offset, int x1, int x2, rlim64_t x3, void *x4, ssize_t *residp);
-extern void vn_close(vnode_t *vp, int openflag, cred_t *cr, kthread_t *td);
-
-#define vn_remove(path, x1, x2) remove(path)
-#define vn_rename(from, to, seg) rename((from), (to))
-#define vn_is_readonly(vp) B_FALSE
-
-extern vnode_t *rootdir;
-
-#include <sys/file.h> /* for FREAD, FWRITE, etc */
-#define FTRUNC O_TRUNC
-
-/*
- * Random stuff
- */
-#define ddi_get_lbolt() (gethrtime() >> 23)
-#define ddi_get_lbolt64() (gethrtime() >> 23)
-#define hz 119 /* frequency when using gethrtime() >> 23 for lbolt */
-
-extern void delay(clock_t ticks);
-
-#define SEC_TO_TICK(sec) ((sec) * hz)
-#define NSEC_TO_TICK(nsec) ((nsec) / (NANOSEC / hz))
-
-#define gethrestime_sec() time(NULL)
-#define gethrestime(t) \
- do {\
- (t)->tv_sec = gethrestime_sec();\
- (t)->tv_nsec = 0;\
- } while (0);
-
-#define max_ncpus 64
-#define boot_ncpus (sysconf(_SC_NPROCESSORS_ONLN))
-
-#define minclsyspri 60
-#define maxclsyspri 99
-
-#define CPU_SEQID (thr_self() & (max_ncpus - 1))
-
-#define kcred NULL
-#define CRED() NULL
-
-#ifndef ptob
-#define ptob(x) ((x) * PAGESIZE)
-#endif
-
-extern uint64_t physmem;
-
-extern int highbit64(uint64_t i);
-extern int random_get_bytes(uint8_t *ptr, size_t len);
-extern int random_get_pseudo_bytes(uint8_t *ptr, size_t len);
-
-extern void kernel_init(int);
-extern void kernel_fini(void);
-
-struct spa;
-extern void nicenum(uint64_t num, char *buf, size_t);
-extern void show_pool_stats(struct spa *);
-extern int set_global_var(char *arg);
-
-typedef struct callb_cpr {
- kmutex_t *cc_lockp;
-} callb_cpr_t;
-
-#define CALLB_CPR_INIT(cp, lockp, func, name) { \
- (cp)->cc_lockp = lockp; \
-}
-
-#define CALLB_CPR_SAFE_BEGIN(cp) { \
- ASSERT(MUTEX_HELD((cp)->cc_lockp)); \
-}
-
-#define CALLB_CPR_SAFE_END(cp, lockp) { \
- ASSERT(MUTEX_HELD((cp)->cc_lockp)); \
-}
-
-#define CALLB_CPR_EXIT(cp) { \
- ASSERT(MUTEX_HELD((cp)->cc_lockp)); \
- mutex_exit((cp)->cc_lockp); \
-}
-
-#define zone_dataset_visible(x, y) (1)
-#define INGLOBALZONE(z) (1)
-extern uint32_t zone_get_hostid(void *zonep);
-
-extern char *kmem_asprintf(const char *fmt, ...);
-#define strfree(str) kmem_free((str), strlen(str) + 1)
-
-/*
- * Hostname information
- */
-extern struct utsname utsname;
-extern char hw_serial[]; /* for userland-emulated hostid access */
-extern int ddi_strtoul(const char *str, char **nptr, int base,
- unsigned long *result);
-
-extern int ddi_strtoull(const char *str, char **nptr, int base,
- u_longlong_t *result);
-
-/* ZFS Boot Related stuff. */
-
-struct _buf {
- intptr_t _fd;
-};
-
-struct bootstat {
- uint64_t st_size;
-};
-
-typedef struct ace_object {
- uid_t a_who;
- uint32_t a_access_mask;
- uint16_t a_flags;
- uint16_t a_type;
- uint8_t a_obj_type[16];
- uint8_t a_inherit_obj_type[16];
-} ace_object_t;
-
-
-#define ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05
-#define ACE_ACCESS_DENIED_OBJECT_ACE_TYPE 0x06
-#define ACE_SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07
-#define ACE_SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08
-
-extern struct _buf *kobj_open_file(char *name);
-extern int kobj_read_file(struct _buf *file, char *buf, unsigned size,
- unsigned off);
-extern void kobj_close_file(struct _buf *file);
-extern int kobj_get_filesize(struct _buf *file, uint64_t *size);
-extern int zfs_secpolicy_snapshot_perms(const char *name, cred_t *cr);
-extern int zfs_secpolicy_rename_perms(const char *from, const char *to,
- cred_t *cr);
-extern int zfs_secpolicy_destroy_perms(const char *name, cred_t *cr);
-extern zoneid_t getzoneid(void);
-/* Random compatibility stuff. */
-#define pwrite64(d, p, n, o) pwrite(d, p, n, o)
-#define readdir64(d) readdir(d)
-#define SIGPENDING(td) (0)
-#define root_mount_wait() do { } while (0)
-#define root_mounted() (1)
-
-#define noinline __attribute__((noinline))
-#define likely(x) __builtin_expect((x), 1)
-
-struct file {
- void *dummy;
-};
-
-#define FCREAT O_CREAT
-#define FOFFMAX 0x0
-
-/* SID stuff */
-typedef struct ksiddomain {
- uint_t kd_ref;
- uint_t kd_len;
- char *kd_name;
-} ksiddomain_t;
-
-ksiddomain_t *ksid_lookupdomain(const char *);
-void ksiddomain_rele(ksiddomain_t *);
-
-typedef uint32_t idmap_rid_t;
-
-#define DDI_SLEEP KM_SLEEP
-#define ddi_log_sysevent(_a, _b, _c, _d, _e, _f, _g) (0)
-
-#define SX_SYSINIT(name, lock, desc)
-
-#define SYSCTL_HANDLER_ARGS struct sysctl_oid *oidp, void *arg1, \
- intptr_t arg2, struct sysctl_req *req
-
-/*
- * This describes the access space for a sysctl request. This is needed
- * so that we can use the interface from the kernel or from user-space.
- */
-struct sysctl_req {
- struct thread *td; /* used for access checking */
- int lock; /* wiring state */
- void *oldptr;
- size_t oldlen;
- size_t oldidx;
- int (*oldfunc)(struct sysctl_req *, const void *, size_t);
- void *newptr;
- size_t newlen;
- size_t newidx;
- int (*newfunc)(struct sysctl_req *, void *, size_t);
- size_t validlen;
- int flags;
-};
-
-SLIST_HEAD(sysctl_oid_list, sysctl_oid);
-
-/*
- * This describes one "oid" in the MIB tree. Potentially more nodes can
- * be hidden behind it, expanded by the handler.
- */
-struct sysctl_oid {
- struct sysctl_oid_list *oid_parent;
- SLIST_ENTRY(sysctl_oid) oid_link;
- int oid_number;
- u_int oid_kind;
- void *oid_arg1;
- intptr_t oid_arg2;
- const char *oid_name;
- int (*oid_handler)(SYSCTL_HANDLER_ARGS);
- const char *oid_fmt;
- int oid_refcnt;
- u_int oid_running;
- const char *oid_descr;
-};
-
-#define SYSCTL_DECL(...)
-#define SYSCTL_NODE(...)
-#define SYSCTL_INT(...)
-#define SYSCTL_UINT(...)
-#define SYSCTL_ULONG(...)
-#define SYSCTL_PROC(...)
-#define SYSCTL_QUAD(...)
-#define SYSCTL_UQUAD(...)
-#ifdef TUNABLE_INT
-#undef TUNABLE_INT
-#undef TUNABLE_ULONG
-#undef TUNABLE_QUAD
-#endif
-#define TUNABLE_INT(...)
-#define TUNABLE_ULONG(...)
-#define TUNABLE_QUAD(...)
-
-int sysctl_handle_64(SYSCTL_HANDLER_ARGS);
-
-/* Errors */
-
-#ifndef ERESTART
-#define ERESTART (-1)
-#endif
-
-#ifdef illumos
-/*
- * Cyclic information
- */
-extern kmutex_t cpu_lock;
-
-typedef uintptr_t cyclic_id_t;
-typedef uint16_t cyc_level_t;
-typedef void (*cyc_func_t)(void *);
-
-#define CY_LOW_LEVEL 0
-#define CY_INFINITY INT64_MAX
-#define CYCLIC_NONE ((cyclic_id_t)0)
-
-typedef struct cyc_time {
- hrtime_t cyt_when;
- hrtime_t cyt_interval;
-} cyc_time_t;
-
-typedef struct cyc_handler {
- cyc_func_t cyh_func;
- void *cyh_arg;
- cyc_level_t cyh_level;
-} cyc_handler_t;
-
-extern cyclic_id_t cyclic_add(cyc_handler_t *, cyc_time_t *);
-extern void cyclic_remove(cyclic_id_t);
-extern int cyclic_reprogram(cyclic_id_t, hrtime_t);
-#endif /* illumos */
-
-#ifdef illumos
-/*
- * Buf structure
- */
-#define B_BUSY 0x0001
-#define B_DONE 0x0002
-#define B_ERROR 0x0004
-#define B_READ 0x0040 /* read when I/O occurs */
-#define B_WRITE 0x0100 /* non-read pseudo-flag */
-
-typedef struct buf {
- int b_flags;
- size_t b_bcount;
- union {
- caddr_t b_addr;
- } b_un;
-
- lldaddr_t _b_blkno;
-#define b_lblkno _b_blkno._f
- size_t b_resid;
- size_t b_bufsize;
- int (*b_iodone)(struct buf *);
- int b_error;
- void *b_private;
-} buf_t;
-
-extern void bioinit(buf_t *);
-extern void biodone(buf_t *);
-extern void bioerror(buf_t *, int);
-extern int geterror(buf_t *);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _SYS_ZFS_CONTEXT_H */
diff --git a/cddl/contrib/opensolaris/lib/libzpool/common/taskq.c b/cddl/contrib/opensolaris/lib/libzpool/common/taskq.c
deleted file mode 100644
index 595d766e93df4..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzpool/common/taskq.c
+++ /dev/null
@@ -1,353 +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 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-/*
- * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
- * Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
- * Copyright (c) 2014 by Delphix. All rights reserved.
- */
-
-#include <sys/zfs_context.h>
-
-int taskq_now;
-taskq_t *system_taskq;
-
-#define TASKQ_ACTIVE 0x00010000
-#define TASKQ_NAMELEN 31
-
-struct taskq {
- char tq_name[TASKQ_NAMELEN + 1];
- kmutex_t tq_lock;
- krwlock_t tq_threadlock;
- kcondvar_t tq_dispatch_cv;
- kcondvar_t tq_wait_cv;
- thread_t *tq_threadlist;
- int tq_flags;
- int tq_active;
- int tq_nthreads;
- int tq_nalloc;
- int tq_minalloc;
- int tq_maxalloc;
- kcondvar_t tq_maxalloc_cv;
- int tq_maxalloc_wait;
- taskq_ent_t *tq_freelist;
- taskq_ent_t tq_task;
-};
-
-static taskq_ent_t *
-task_alloc(taskq_t *tq, int tqflags)
-{
- taskq_ent_t *t;
- int rv;
-
-again: if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
- tq->tq_freelist = t->tqent_next;
- } else {
- if (tq->tq_nalloc >= tq->tq_maxalloc) {
- if (!(tqflags & KM_SLEEP))
- return (NULL);
-
- /*
- * We don't want to exceed tq_maxalloc, but we can't
- * wait for other tasks to complete (and thus free up
- * task structures) without risking deadlock with
- * the caller. So, we just delay for one second
- * to throttle the allocation rate. If we have tasks
- * complete before one second timeout expires then
- * taskq_ent_free will signal us and we will
- * immediately retry the allocation.
- */
- tq->tq_maxalloc_wait++;
-#ifdef __FreeBSD__
- rv = cv_timedwait(&tq->tq_maxalloc_cv,
- &tq->tq_lock, hz);
-#else
- rv = cv_timedwait(&tq->tq_maxalloc_cv,
- &tq->tq_lock, ddi_get_lbolt() + hz);
-#endif
- tq->tq_maxalloc_wait--;
- if (rv > 0)
- goto again; /* signaled */
- }
- mutex_exit(&tq->tq_lock);
-
- t = kmem_alloc(sizeof (taskq_ent_t), tqflags & KM_SLEEP);
-
- mutex_enter(&tq->tq_lock);
- if (t != NULL)
- tq->tq_nalloc++;
- }
- return (t);
-}
-
-static void
-task_free(taskq_t *tq, taskq_ent_t *t)
-{
- if (tq->tq_nalloc <= tq->tq_minalloc) {
- t->tqent_next = tq->tq_freelist;
- tq->tq_freelist = t;
- } else {
- tq->tq_nalloc--;
- mutex_exit(&tq->tq_lock);
- kmem_free(t, sizeof (taskq_ent_t));
- mutex_enter(&tq->tq_lock);
- }
-
- if (tq->tq_maxalloc_wait)
- cv_signal(&tq->tq_maxalloc_cv);
-}
-
-taskqid_t
-taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t tqflags)
-{
- taskq_ent_t *t;
-
- if (taskq_now) {
- func(arg);
- return (1);
- }
-
- mutex_enter(&tq->tq_lock);
- ASSERT(tq->tq_flags & TASKQ_ACTIVE);
- if ((t = task_alloc(tq, tqflags)) == NULL) {
- mutex_exit(&tq->tq_lock);
- return (0);
- }
- if (tqflags & TQ_FRONT) {
- t->tqent_next = tq->tq_task.tqent_next;
- t->tqent_prev = &tq->tq_task;
- } else {
- t->tqent_next = &tq->tq_task;
- t->tqent_prev = tq->tq_task.tqent_prev;
- }
- t->tqent_next->tqent_prev = t;
- t->tqent_prev->tqent_next = t;
- t->tqent_func = func;
- t->tqent_arg = arg;
- t->tqent_flags = 0;
- cv_signal(&tq->tq_dispatch_cv);
- mutex_exit(&tq->tq_lock);
- return (1);
-}
-
-void
-taskq_dispatch_ent(taskq_t *tq, task_func_t func, void *arg, uint_t flags,
- taskq_ent_t *t)
-{
- ASSERT(func != NULL);
- ASSERT(!(tq->tq_flags & TASKQ_DYNAMIC));
-
- /*
- * Mark it as a prealloc'd task. This is important
- * to ensure that we don't free it later.
- */
- t->tqent_flags |= TQENT_FLAG_PREALLOC;
- /*
- * Enqueue the task to the underlying queue.
- */
- mutex_enter(&tq->tq_lock);
-
- if (flags & TQ_FRONT) {
- t->tqent_next = tq->tq_task.tqent_next;
- t->tqent_prev = &tq->tq_task;
- } else {
- t->tqent_next = &tq->tq_task;
- t->tqent_prev = tq->tq_task.tqent_prev;
- }
- t->tqent_next->tqent_prev = t;
- t->tqent_prev->tqent_next = t;
- t->tqent_func = func;
- t->tqent_arg = arg;
- cv_signal(&tq->tq_dispatch_cv);
- mutex_exit(&tq->tq_lock);
-}
-
-void
-taskq_wait(taskq_t *tq)
-{
- mutex_enter(&tq->tq_lock);
- while (tq->tq_task.tqent_next != &tq->tq_task || tq->tq_active != 0)
- cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
- mutex_exit(&tq->tq_lock);
-}
-
-void
-taskq_wait_id(taskq_t *tq, taskqid_t id)
-{
- taskq_wait(tq);
-}
-
-static void *
-taskq_thread(void *arg)
-{
- taskq_t *tq = arg;
- taskq_ent_t *t;
- boolean_t prealloc;
-
- mutex_enter(&tq->tq_lock);
- while (tq->tq_flags & TASKQ_ACTIVE) {
- if ((t = tq->tq_task.tqent_next) == &tq->tq_task) {
- if (--tq->tq_active == 0)
- cv_broadcast(&tq->tq_wait_cv);
- cv_wait(&tq->tq_dispatch_cv, &tq->tq_lock);
- tq->tq_active++;
- continue;
- }
- t->tqent_prev->tqent_next = t->tqent_next;
- t->tqent_next->tqent_prev = t->tqent_prev;
- t->tqent_next = NULL;
- t->tqent_prev = NULL;
- prealloc = t->tqent_flags & TQENT_FLAG_PREALLOC;
- mutex_exit(&tq->tq_lock);
-
- rw_enter(&tq->tq_threadlock, RW_READER);
- t->tqent_func(t->tqent_arg);
- rw_exit(&tq->tq_threadlock);
-
- mutex_enter(&tq->tq_lock);
- if (!prealloc)
- task_free(tq, t);
- }
- tq->tq_nthreads--;
- cv_broadcast(&tq->tq_wait_cv);
- mutex_exit(&tq->tq_lock);
- return (NULL);
-}
-
-/*ARGSUSED*/
-taskq_t *
-taskq_create(const char *name, int nthreads, pri_t pri,
- int minalloc, int maxalloc, uint_t flags)
-{
- taskq_t *tq = kmem_zalloc(sizeof (taskq_t), KM_SLEEP);
- int t;
-
- if (flags & TASKQ_THREADS_CPU_PCT) {
- int pct;
- ASSERT3S(nthreads, >=, 0);
- ASSERT3S(nthreads, <=, 100);
- pct = MIN(nthreads, 100);
- pct = MAX(pct, 0);
-
- nthreads = (sysconf(_SC_NPROCESSORS_ONLN) * pct) / 100;
- nthreads = MAX(nthreads, 1); /* need at least 1 thread */
- } else {
- ASSERT3S(nthreads, >=, 1);
- }
-
- rw_init(&tq->tq_threadlock, NULL, RW_DEFAULT, NULL);
- mutex_init(&tq->tq_lock, NULL, MUTEX_DEFAULT, NULL);
- cv_init(&tq->tq_dispatch_cv, NULL, CV_DEFAULT, NULL);
- cv_init(&tq->tq_wait_cv, NULL, CV_DEFAULT, NULL);
- cv_init(&tq->tq_maxalloc_cv, NULL, CV_DEFAULT, NULL);
- (void) strncpy(tq->tq_name, name, TASKQ_NAMELEN + 1);
- tq->tq_flags = flags | TASKQ_ACTIVE;
- tq->tq_active = nthreads;
- tq->tq_nthreads = nthreads;
- tq->tq_minalloc = minalloc;
- tq->tq_maxalloc = maxalloc;
- tq->tq_task.tqent_next = &tq->tq_task;
- tq->tq_task.tqent_prev = &tq->tq_task;
- tq->tq_threadlist = kmem_alloc(nthreads * sizeof (thread_t), KM_SLEEP);
-
- if (flags & TASKQ_PREPOPULATE) {
- mutex_enter(&tq->tq_lock);
- while (minalloc-- > 0)
- task_free(tq, task_alloc(tq, KM_SLEEP));
- mutex_exit(&tq->tq_lock);
- }
-
- for (t = 0; t < nthreads; t++)
- (void) thr_create(0, 0, taskq_thread,
- tq, THR_BOUND, &tq->tq_threadlist[t]);
-
- return (tq);
-}
-
-void
-taskq_destroy(taskq_t *tq)
-{
- int t;
- int nthreads = tq->tq_nthreads;
-
- taskq_wait(tq);
-
- mutex_enter(&tq->tq_lock);
-
- tq->tq_flags &= ~TASKQ_ACTIVE;
- cv_broadcast(&tq->tq_dispatch_cv);
-
- while (tq->tq_nthreads != 0)
- cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
-
- tq->tq_minalloc = 0;
- while (tq->tq_nalloc != 0) {
- ASSERT(tq->tq_freelist != NULL);
- task_free(tq, task_alloc(tq, KM_SLEEP));
- }
-
- mutex_exit(&tq->tq_lock);
-
- for (t = 0; t < nthreads; t++)
- (void) thr_join(tq->tq_threadlist[t], NULL, NULL);
-
- kmem_free(tq->tq_threadlist, nthreads * sizeof (thread_t));
-
- rw_destroy(&tq->tq_threadlock);
- mutex_destroy(&tq->tq_lock);
- cv_destroy(&tq->tq_dispatch_cv);
- cv_destroy(&tq->tq_wait_cv);
- cv_destroy(&tq->tq_maxalloc_cv);
-
- kmem_free(tq, sizeof (taskq_t));
-}
-
-int
-taskq_member(taskq_t *tq, void *t)
-{
- int i;
-
- if (taskq_now)
- return (1);
-
- for (i = 0; i < tq->tq_nthreads; i++)
- if (tq->tq_threadlist[i] == (thread_t)(uintptr_t)t)
- return (1);
-
- return (0);
-}
-
-void
-system_taskq_init(void)
-{
- system_taskq = taskq_create("system_taskq", 64, minclsyspri, 4, 512,
- TASKQ_DYNAMIC | TASKQ_PREPOPULATE);
-}
-
-void
-system_taskq_fini(void)
-{
- taskq_destroy(system_taskq);
- system_taskq = NULL; /* defensive */
-}
diff --git a/cddl/contrib/opensolaris/lib/libzpool/common/util.c b/cddl/contrib/opensolaris/lib/libzpool/common/util.c
deleted file mode 100644
index d2ed31a468321..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzpool/common/util.c
+++ /dev/null
@@ -1,196 +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) 2016 by Delphix. All rights reserved.
- * Copyright (c) 2017, Intel Corporation.
- */
-
-#include <assert.h>
-#include <sys/zfs_context.h>
-#include <sys/avl.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/spa.h>
-#include <sys/fs/zfs.h>
-#include <sys/refcount.h>
-#include <dlfcn.h>
-
-/*
- * Routines needed by more than one client of libzpool.
- */
-
-static void
-show_vdev_stats(const char *desc, const char *ctype, nvlist_t *nv, int indent)
-{
- vdev_stat_t *vs;
- vdev_stat_t v0 = { 0 };
- uint64_t sec;
- uint64_t is_log = 0;
- nvlist_t **child;
- uint_t c, children;
- char used[6], avail[6];
- char rops[6], wops[6], rbytes[6], wbytes[6], rerr[6], werr[6], cerr[6];
-
- if (indent == 0 && desc != NULL) {
- (void) printf(" "
- " capacity operations bandwidth ---- errors ----\n");
- (void) printf("description "
- "used avail read write read write read write cksum\n");
- }
-
- if (desc != NULL) {
- char *suffix = "", *bias = NULL;
- char bias_suffix[32];
-
- (void) nvlist_lookup_uint64(nv, ZPOOL_CONFIG_IS_LOG, &is_log);
- (void) nvlist_lookup_string(nv, ZPOOL_CONFIG_ALLOCATION_BIAS,
- &bias);
- if (nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS,
- (uint64_t **)&vs, &c) != 0)
- vs = &v0;
-
- if (bias != NULL) {
- (void) snprintf(bias_suffix, sizeof (bias_suffix),
- " (%s)", bias);
- suffix = bias_suffix;
- } else if (is_log) {
- suffix = " (log)";
- }
-
- sec = MAX(1, vs->vs_timestamp / NANOSEC);
-
- nicenum(vs->vs_alloc, used, sizeof (used));
- nicenum(vs->vs_space - vs->vs_alloc, avail, sizeof (avail));
- nicenum(vs->vs_ops[ZIO_TYPE_READ] / sec, rops, sizeof (rops));
- nicenum(vs->vs_ops[ZIO_TYPE_WRITE] / sec, wops, sizeof (wops));
- nicenum(vs->vs_bytes[ZIO_TYPE_READ] / sec, rbytes,
- sizeof (rbytes));
- nicenum(vs->vs_bytes[ZIO_TYPE_WRITE] / sec, wbytes,
- sizeof (wbytes));
- nicenum(vs->vs_read_errors, rerr, sizeof (rerr));
- nicenum(vs->vs_write_errors, werr, sizeof (werr));
- nicenum(vs->vs_checksum_errors, cerr, sizeof (cerr));
-
- (void) printf("%*s%s%*s%*s%*s %5s %5s %5s %5s %5s %5s %5s\n",
- indent, "",
- desc,
- (int)(indent+strlen(desc)-25-(vs->vs_space ? 0 : 12)),
- suffix,
- vs->vs_space ? 6 : 0, vs->vs_space ? used : "",
- vs->vs_space ? 6 : 0, vs->vs_space ? avail : "",
- rops, wops, rbytes, wbytes, rerr, werr, cerr);
- }
-
- if (nvlist_lookup_nvlist_array(nv, ctype, &child, &children) != 0)
- return;
-
- for (c = 0; c < children; c++) {
- nvlist_t *cnv = child[c];
- char *cname, *tname;
- uint64_t np;
- if (nvlist_lookup_string(cnv, ZPOOL_CONFIG_PATH, &cname) &&
- nvlist_lookup_string(cnv, ZPOOL_CONFIG_TYPE, &cname))
- cname = "<unknown>";
- tname = calloc(1, strlen(cname) + 2);
- (void) strcpy(tname, cname);
- if (nvlist_lookup_uint64(cnv, ZPOOL_CONFIG_NPARITY, &np) == 0)
- tname[strlen(tname)] = '0' + np;
- show_vdev_stats(tname, ctype, cnv, indent + 2);
- free(tname);
- }
-}
-
-void
-show_pool_stats(spa_t *spa)
-{
- nvlist_t *config, *nvroot;
- char *name;
-
- VERIFY(spa_get_stats(spa_name(spa), &config, NULL, 0) == 0);
-
- VERIFY(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
- &nvroot) == 0);
- VERIFY(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
- &name) == 0);
-
- show_vdev_stats(name, ZPOOL_CONFIG_CHILDREN, nvroot, 0);
- show_vdev_stats(NULL, ZPOOL_CONFIG_L2CACHE, nvroot, 0);
- show_vdev_stats(NULL, ZPOOL_CONFIG_SPARES, nvroot, 0);
-
- nvlist_free(config);
-}
-
-/*
- * Sets given global variable in libzpool to given unsigned 32-bit value.
- * arg: "<variable>=<value>"
- */
-int
-set_global_var(char *arg)
-{
- void *zpoolhdl;
- char *varname = arg, *varval;
- u_longlong_t val;
-
-#ifndef _LITTLE_ENDIAN
- /*
- * On big endian systems changing a 64-bit variable would set the high
- * 32 bits instead of the low 32 bits, which could cause unexpected
- * results.
- */
- fprintf(stderr, "Setting global variables is only supported on "
- "little-endian systems\n", varname);
- return (ENOTSUP);
-#endif
- if ((varval = strchr(arg, '=')) != NULL) {
- *varval = '\0';
- varval++;
- val = strtoull(varval, NULL, 0);
- if (val > UINT32_MAX) {
- fprintf(stderr, "Value for global variable '%s' must "
- "be a 32-bit unsigned integer\n", varname);
- return (EOVERFLOW);
- }
- } else {
- return (EINVAL);
- }
-
- zpoolhdl = dlopen("libzpool.so", RTLD_LAZY);
- if (zpoolhdl != NULL) {
- uint32_t *var;
- var = dlsym(zpoolhdl, varname);
- if (var == NULL) {
- fprintf(stderr, "Global variable '%s' does not exist "
- "in libzpool.so\n", varname);
- return (EINVAL);
- }
- *var = (uint32_t)val;
-
- dlclose(zpoolhdl);
- } else {
- fprintf(stderr, "Failed to open libzpool.so to set global "
- "variable\n");
- return (EIO);
- }
-
- return (0);
-}
diff --git a/cddl/contrib/opensolaris/lib/libzpool/common/zfs.d b/cddl/contrib/opensolaris/lib/libzpool/common/zfs.d
deleted file mode 100644
index 1351733c807bf..0000000000000
--- a/cddl/contrib/opensolaris/lib/libzpool/common/zfs.d
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * 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.
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright (c) 2013 by Delphix. All rights reserved.
- */
-
-provider zfs {
- probe probe0(char *probename);
- probe probe1(char *probename, unsigned long arg1);
- probe probe2(char *probename, unsigned long arg1, unsigned long arg2);
- probe probe3(char *probename, unsigned long arg1, unsigned long arg2,
- unsigned long arg3);
- probe probe4(char *probename, unsigned long arg1, unsigned long arg2,
- unsigned long arg3, unsigned long arg4);
-
- probe set__error(int err);
-};
-
-#pragma D attributes Evolving/Evolving/ISA provider zfs provider
-#pragma D attributes Private/Private/Unknown provider zfs module
-#pragma D attributes Private/Private/Unknown provider zfs function
-#pragma D attributes Evolving/Evolving/ISA provider zfs name
-#pragma D attributes Evolving/Evolving/ISA provider zfs args
diff --git a/cddl/contrib/opensolaris/tools/ctf/cvt/util.c b/cddl/contrib/opensolaris/tools/ctf/cvt/util.c
index fb76cbaeb422b..0eda56dbf65aa 100644
--- a/cddl/contrib/opensolaris/tools/ctf/cvt/util.c
+++ b/cddl/contrib/opensolaris/tools/ctf/cvt/util.c
@@ -29,6 +29,7 @@
* Utility functions
*/
+#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
diff --git a/cddl/lib/Makefile b/cddl/lib/Makefile
index b65983dd64a7f..399e314e76b14 100644
--- a/cddl/lib/Makefile
+++ b/cddl/lib/Makefile
@@ -6,27 +6,40 @@ SUBDIR= drti \
libavl \
libctf \
libdtrace \
+ ${_libicp} \
+ ${_libicp_rescue} \
libnvpair \
+ libspl \
+ ${_libtpool} \
libumem \
libuutil \
${_libzfs_core} \
${_libzfs} \
${_libzpool} \
+ ${_libzutil}
SUBDIR.${MK_TESTS}+= tests
.if ${MK_ZFS} != "no"
_libzfs_core= libzfs_core
+_libicp= libicp
+_libicp_rescue= libicp_rescue
_libzfs= libzfs
+_libzutil= libzutil
.if ${MK_LIBTHR} != "no"
_libzpool= libzpool
+_libtpool= libtpool
.endif
.endif
+SUBDIR_DEPEND_libctf= libspl
SUBDIR_DEPEND_libdtrace= libctf
+SUBDIR_DEPEND_libtpool= libspl
+SUBDIR_DEPEND_libuutil= libavl libspl
SUBDIR_DEPEND_libzfs_core= libnvpair
-SUBDIR_DEPEND_libzfs= libavl libnvpair libumem libuutil libzfs_core
-SUBDIR_DEPEND_libzpool= libavl libnvpair libumem
+SUBDIR_DEPEND_libzfs= libavl libnvpair libumem libuutil libzfs_core libzutil
+SUBDIR_DEPEND_libzpool= libavl libnvpair libumem libicp
+SUBDIR_DEPEND_libzutil= libavl libtpool
SUBDIR_PARALLEL=
diff --git a/cddl/lib/drti/Makefile b/cddl/lib/drti/Makefile
index 19cbd748d2345..eeb1c865e632a 100644
--- a/cddl/lib/drti/Makefile
+++ b/cddl/lib/drti/Makefile
@@ -11,7 +11,14 @@ FILESDIR= ${LIBDIR}/dtrace
CLEANFILES= ${FILES}
# These FILES qualify as libraries for the purpose of LIBRARIES_ONLY.
.undef LIBRARIES_ONLY
-
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+CFLAGS+= -DHAVE_ISSETUGID
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
-I${SRCTOP}/cddl/compat/opensolaris/include \
-I${OPENSOLARIS_USR_DISTDIR}/head \
diff --git a/cddl/lib/libavl/Makefile b/cddl/lib/libavl/Makefile
index 995e4d013e501..2f7b9ad30856e 100644
--- a/cddl/lib/libavl/Makefile
+++ b/cddl/lib/libavl/Makefile
@@ -1,12 +1,15 @@
# $FreeBSD$
-.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/avl
+.PATH: ${SRCTOP}/sys/contrib/openzfs/module/avl
PACKAGE= runtime
LIB= avl
SRCS= avl.c
WARNS?= 3
-CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
-
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
.include <bsd.lib.mk>
diff --git a/cddl/lib/libctf/Makefile b/cddl/lib/libctf/Makefile
index ed7ebc5351deb..b45bda3385e62 100644
--- a/cddl/lib/libctf/Makefile
+++ b/cddl/lib/libctf/Makefile
@@ -21,6 +21,14 @@ MAN= ctf.5
WARNS?= 2
CFLAGS+= -DCTF_OLD_VERSIONS
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+CFLAGS+= -DHAVE_ISSETUGID
+
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
-I${SRCTOP}/cddl/compat/opensolaris/include \
-I${OPENSOLARIS_USR_DISTDIR}/head \
@@ -28,6 +36,6 @@ CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
-I${OPENSOLARIS_USR_DISTDIR}/lib/libctf/common \
-I${OPENSOLARIS_SYS_DISTDIR}/uts/common
-LIBADD+= z
+LIBADD+= spl z
.include <bsd.lib.mk>
diff --git a/cddl/lib/libdtrace/Makefile b/cddl/lib/libdtrace/Makefile
index 11565b03b0ac3..e0125cad2b380 100644
--- a/cddl/lib/libdtrace/Makefile
+++ b/cddl/lib/libdtrace/Makefile
@@ -66,6 +66,16 @@ FILESMODE= ${NOBINMODE}
WARNS?= 1
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+CFLAGS+= -DHAVE_ISSETUGID
+
+
CFLAGS+= -I${.OBJDIR} -I${.CURDIR} \
-I${SRCTOP}/sys/cddl/dev/dtrace/${MACHINE_ARCH} \
-I${SRCTOP}/sys/cddl/compat/opensolaris \
diff --git a/cddl/lib/libicp/Makefile b/cddl/lib/libicp/Makefile
new file mode 100644
index 0000000000000..36858338ac6be
--- /dev/null
+++ b/cddl/lib/libicp/Makefile
@@ -0,0 +1,101 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/contrib/openzfs/module/icp
+
+PACKAGE= runtime
+LIB= icp
+LIBADD=
+
+
+.if ${MACHINE_ARCH} == "amd64"
+ASM_SOURCES_C = asm-x86_64/aes/aeskey.c
+ASM_SOURCES_AS = \
+ asm-x86_64/aes/aes_amd64.S \
+ asm-x86_64/aes/aes_aesni.S \
+ asm-x86_64/modes/gcm_pclmulqdq.S \
+ asm-x86_64/modes/aesni-gcm-x86_64.S \
+ asm-x86_64/modes/ghash-x86_64.S \
+ asm-x86_64/sha1/sha1-x86_64.S \
+ asm-x86_64/sha2/sha256_impl.S \
+ asm-x86_64/sha2/sha512_impl.S
+
+CFLAGS+= -D__amd64 -D_SYS_STACK_H -UHAVE_AES
+.else
+ASM_SOURCES_C =
+ASM_SOURCES_AS =
+.endif
+
+
+KERNEL_C = \
+ spi/kcf_spi.c \
+ api/kcf_ctxops.c \
+ api/kcf_digest.c \
+ api/kcf_cipher.c \
+ api/kcf_miscapi.c \
+ api/kcf_mac.c \
+ algs/aes/aes_impl_aesni.c \
+ algs/aes/aes_impl_generic.c \
+ algs/aes/aes_impl_x86-64.c \
+ algs/aes/aes_impl.c \
+ algs/aes/aes_modes.c \
+ algs/edonr/edonr.c \
+ algs/modes/modes.c \
+ algs/modes/cbc.c \
+ algs/modes/gcm_generic.c \
+ algs/modes/gcm_pclmulqdq.c \
+ algs/modes/gcm.c \
+ algs/modes/ctr.c \
+ algs/modes/ccm.c \
+ algs/modes/ecb.c \
+ algs/sha1/sha1.c \
+ algs/sha2/sha2.c \
+ algs/skein/skein.c \
+ algs/skein/skein_block.c \
+ algs/skein/skein_iv.c \
+ illumos-crypto.c \
+ io/aes.c \
+ io/edonr_mod.c \
+ io/sha1_mod.c \
+ io/sha2_mod.c \
+ io/skein_mod.c \
+ os/modhash.c \
+ os/modconf.c \
+ core/kcf_sched.c \
+ core/kcf_prov_lib.c \
+ core/kcf_callprov.c \
+ core/kcf_mech_tabs.c \
+ core/kcf_prov_tabs.c \
+ $(ASM_SOURCES_C)
+
+
+
+
+
+
+SRCS= $(ASM_SOURCES_AS) $(KERNEL_C)
+
+WARNS?= 2
+SHLIB_MAJOR= 3
+CSTD= c99
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+CFLAGS+= -DHAVE_ISSETUGID
+CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
+
+
+CFLAGS.aes_amd64.S+= -DLOCORE
+CFLAGS.aes_aesni.S+= -DLOCORE
+CFLAGS.gcm_pclmulqdq.S+= -DLOCORE
+CFLAGS.aesni-gcm-x86_64.S+= -DLOCORE
+CFLAGS.ghash-x86_64.S+= -DLOCORE
+CFLAGS.sha1-x86_64.S+= -DLOCORE
+CFLAGS.sha256_impl.S+= -DLOCORE
+CFLAGS.sha512_impl.S+= -DLOCORE
+
+.include <bsd.lib.mk>
diff --git a/cddl/lib/libicp_rescue/Makefile b/cddl/lib/libicp_rescue/Makefile
new file mode 100644
index 0000000000000..1ebe1b0ff649e
--- /dev/null
+++ b/cddl/lib/libicp_rescue/Makefile
@@ -0,0 +1,99 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/contrib/openzfs/module/icp
+
+PACKAGE= runtime
+LIB= icp_rescue
+LIBADD=
+
+
+.if ${MACHINE_ARCH} == "amd64"
+ASM_SOURCES_C = asm-x86_64/aes/aeskey.c
+ASM_SOURCES_AS = \
+ asm-x86_64/aes/aes_amd64.S \
+ asm-x86_64/aes/aes_aesni.S \
+ asm-x86_64/modes/gcm_pclmulqdq.S \
+ asm-x86_64/modes/aesni-gcm-x86_64.S \
+ asm-x86_64/sha1/sha1-x86_64.S \
+ asm-x86_64/sha2/sha256_impl.S \
+ asm-x86_64/sha2/sha512_impl.S
+
+CFLAGS+= -D__amd64 -D_SYS_STACK_H
+.else
+ASM_SOURCES_C =
+ASM_SOURCES_AS =
+.endif
+
+
+KERNEL_C = \
+ spi/kcf_spi.c \
+ api/kcf_ctxops.c \
+ api/kcf_digest.c \
+ api/kcf_cipher.c \
+ api/kcf_miscapi.c \
+ api/kcf_mac.c \
+ algs/aes/aes_impl_aesni.c \
+ algs/aes/aes_impl_generic.c \
+ algs/aes/aes_impl_x86-64.c \
+ algs/aes/aes_impl.c \
+ algs/aes/aes_modes.c \
+ algs/edonr/edonr.c \
+ algs/modes/modes.c \
+ algs/modes/cbc.c \
+ algs/modes/gcm_generic.c \
+ algs/modes/gcm_pclmulqdq.c \
+ algs/modes/gcm.c \
+ algs/modes/ctr.c \
+ algs/modes/ccm.c \
+ algs/modes/ecb.c \
+ algs/sha1/sha1.c \
+ algs/sha2/sha2.c \
+ algs/skein/skein_block.c \
+ illumos-crypto.c \
+ io/aes.c \
+ io/edonr_mod.c \
+ io/sha1_mod.c \
+ io/sha2_mod.c \
+ io/skein_mod.c \
+ os/modhash.c \
+ os/modconf.c \
+ core/kcf_sched.c \
+ core/kcf_prov_lib.c \
+ core/kcf_callprov.c \
+ core/kcf_mech_tabs.c \
+ core/kcf_prov_tabs.c \
+ $(ASM_SOURCES_C)
+
+
+
+
+
+
+SRCS= $(ASM_SOURCES_AS) $(KERNEL_C)
+
+WARNS?= 2
+SHLIB_MAJOR= 3
+CSTD= c99
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+CFLAGS+= -DHAVE_ISSETUGID -UHAVE_AVX -DRESCUE
+CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
+
+
+CFLAGS.aes_amd64.S+= -DLOCORE
+CFLAGS.aes_aesni.S+= -DLOCORE
+CFLAGS.gcm_pclmulqdq.S+= -DLOCORE
+CFLAGS.aesni-gcm-x86_64.S+= -DLOCORE
+CFLAGS.ghash-x86_64.S+= -DLOCORE
+CFLAGS.sha1-x86_64.S+= -DLOCORE
+CFLAGS.sha256_impl.S+= -DLOCORE
+CFLAGS.sha512_impl.S+= -DLOCORE
+CFLAGS.gcm.c+= -UCAN_USE_GCM_ASM
+
+.include <bsd.lib.mk>
diff --git a/cddl/lib/libnvpair/Makefile b/cddl/lib/libnvpair/Makefile
index 90c3295d5048a..670253eff7c12 100644
--- a/cddl/lib/libnvpair/Makefile
+++ b/cddl/lib/libnvpair/Makefile
@@ -1,36 +1,30 @@
# $FreeBSD$
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
-.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/nvpair
+.PATH: ${SRCTOP}/sys/contrib/openzfs/module/nvpair
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libnvpair
LIB= nvpair
PACKAGE= runtime
-INCS= libnvpair.h
+# user
SRCS= libnvpair.c \
- nvpair_alloc_system.c \
- nvpair_json.c \
- opensolaris_fnvpair.c \
- opensolaris_nvpair.c \
- opensolaris_nvpair_alloc_fixed.c
+ libnvpair_json.c \
+ nvpair_alloc_system.c
+# kernel
+SRCS+= nvpair_alloc_fixed.c \
+ nvpair.c \
+ fnvpair.c
-WARNS?= 1
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
-CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
+WARNS?= 2
+CFLAGS+= -DIN_BASE -DHAVE_RPC_TYPES
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
CFLAGS+= -I${SRCTOP}/sys
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+CFLAGS+= -DHAVE_ISSETUGID -DHAVE_CONFIG_H -DHAVE_XDR_BYTESREC
+
-# This library uses macros to define fprintf behavior for several object types
-# The compiler will see the non-string literal arguments to the fprintf calls and
-# omit warnings for them. Quiesce these warnings in contrib code:
-#
-# cddl/contrib/opensolaris/lib/libnvpair/libnvpair.c:743:12: warning: format
-# string is not a string literal (potentially insecure) [-Wformat-security]
-# ARENDER(pctl, nvlist_array, nvl, name, val, nelem);
-#
-CFLAGS+= -Wno-format-security
+CFLAGS.nvpair.c+= -UHAVE_RPC_TYPES
.include <bsd.lib.mk>
diff --git a/cddl/lib/libspl/Makefile b/cddl/lib/libspl/Makefile
new file mode 100644
index 0000000000000..64317c41b730a
--- /dev/null
+++ b/cddl/lib/libspl/Makefile
@@ -0,0 +1,56 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libspl
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libspl/os/freebsd
+.PATH: ${SRCTOP}/sys/contrib/openzfs/include
+
+
+LIB= spl
+LIBADD=
+PACKAGE= runtime
+
+SRCS = \
+ assert.c \
+ list.c \
+ mkdirp.c \
+ page.c \
+ strlcat.c \
+ strlcpy.c \
+ timestamp.c \
+ zone.c \
+ include/sys/list.h \
+ include/sys/list_impl.h
+
+SRCS += \
+ getexecname.c \
+ gethostid.c \
+ getmntany.c \
+ mnttab.c
+
+
+.if ${MACHINE_ARCH} == "amd64"
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libspl/asm-x86_64
+SRCS += atomic.S
+.elif ${MACHINE_ARCH} == "i386"
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libspl/asm-i386
+SRCS += atomic.S
+.else
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libspl/asm-generic
+SRCS += atomic.c
+.endif
+
+
+WARNS?= 2
+CSTD= c99
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+CFLAGS+= -DHAVE_ISSETUGID
+CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
+CFLAGS.atomic.S+= -DLOCORE
+
+.include <bsd.lib.mk>
diff --git a/cddl/lib/libtpool/Makefile b/cddl/lib/libtpool/Makefile
new file mode 100644
index 0000000000000..637385bc842e5
--- /dev/null
+++ b/cddl/lib/libtpool/Makefile
@@ -0,0 +1,27 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libtpool
+.PATH: ${SRCTOP}/sys/contrib/openzfs/include
+
+
+LIB= tpool
+LIBADD= spl
+PACKAGE= runtime
+
+INCS= thread_pool_impl.h
+SRCS= thread_pool.c
+
+WARNS?= 2
+CSTD= c99
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+CFLAGS+= -DHAVE_ISSETUGID
+CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
+
+.include <bsd.lib.mk>
diff --git a/cddl/lib/libuutil/Makefile b/cddl/lib/libuutil/Makefile
index 63997c70c236a..76567b50c6103 100644
--- a/cddl/lib/libuutil/Makefile
+++ b/cddl/lib/libuutil/Makefile
@@ -1,11 +1,10 @@
# $FreeBSD$
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libuutil/common
-.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/avl
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libuutil
PACKAGE= runtime
LIB= uutil
-SRCS= avl.c \
+SRCS=\
uu_alloc.c \
uu_avl.c \
uu_dprintf.c \
@@ -14,14 +13,17 @@ SRCS= avl.c \
uu_misc.c \
uu_open.c \
uu_pname.c \
- uu_strtoint.c
+ uu_string.c
-WARNS?= 1
-CFLAGS+= -DNATIVE_BUILD
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libuutil/common
-CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
+WARNS?= 2
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+
+LIBADD= avl spl
.include <bsd.lib.mk>
diff --git a/cddl/lib/libzfs/Makefile b/cddl/lib/libzfs/Makefile
index b42365cf0d93f..cd2d2b99deb04 100644
--- a/cddl/lib/libzfs/Makefile
+++ b/cddl/lib/libzfs/Makefile
@@ -1,62 +1,102 @@
# $FreeBSD$
-.PATH: ${SRCTOP}/cddl/compat/opensolaris/misc
-.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
-.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libcmdutils/common
+.PATH: ${SRCTOP}/sys/contrib/openzfs/module/icp
+.PATH: ${SRCTOP}/sys/contrib/openzfs/module/zcommon
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libzfs
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libzfs/os/freebsd
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libshare
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libshare/os/freebsd
+.PATH: ${SRCTOP}/sys/contrib/openzfs/include
+.PATH: ${SRCTOP}/sys/contrib/openzfs/module/zstd
+.PATH: ${SRCTOP}/sys/contrib/openzfs/module/zstd/lib
PACKAGE= runtime
LIB= zfs
-LIBADD= md pthread umem util uutil m avl bsdxml geom nvpair z zfs_core
-SRCS= deviceid.c \
- fsshare.c \
- mkdirp.c \
- mnttab.c \
- thread_pool.c \
- zmount.c \
- zone.c
-
-SRCS+= nicenum.c
-
-SRCS+= libzfs_changelist.c \
- libzfs_compat.c \
- libzfs_config.c \
- libzfs_dataset.c \
- libzfs_diff.c \
- libzfs_import.c \
- libzfs_iter.c \
- libzfs_mount.c \
- libzfs_pool.c \
- libzfs_sendrecv.c \
- libzfs_status.c \
- libzfs_util.c \
- zfeature_common.c \
- zfs_comutil.c \
- zfs_deleg.c \
- zfs_fletcher.c \
- zfs_namecheck.c \
- zfs_prop.c \
- zpool_prop.c \
- zprop_common.c \
-
-WARNS?= 0
-SHLIB_MAJOR= 3
+LIBADD= md pthread umem util uutil m avl bsdxml geom nvpair z zfs_core zutil
+
+INCS= libzfs.h
+USER_C = \
+ libzfs_changelist.c \
+ libzfs_config.c \
+ libzfs_crypto.c \
+ libzfs_dataset.c \
+ libzfs_diff.c \
+ libzfs_import.c \
+ libzfs_iter.c \
+ libzfs_mount.c \
+ libzfs_pool.c \
+ libzfs_sendrecv.c \
+ libzfs_status.c \
+ libzfs_util.c
+
+# FreeBSD
+USER_C += \
+ libzfs_compat.c \
+ libzfs_ioctl_compat.c \
+ libzfs_zmount.c
+
+# libshare
+USER_C += \
+ libshare.c \
+ nfs.c \
+ smb.c
+
+
+KERNEL_C = \
+ algs/sha2/sha2.c \
+ cityhash.c \
+ zfeature_common.c \
+ zfs_comutil.c \
+ zfs_deleg.c \
+ zfs_fletcher.c \
+ zfs_fletcher_superscalar.c \
+ zfs_fletcher_superscalar4.c \
+ zfs_namecheck.c \
+ zfs_prop.c \
+ zfs_uio.c \
+ zpool_prop.c \
+ zprop_common.c
+
+
+KERNEL_C+= zstd.c \
+ zfs_zstd.c
+
+
+ARCH_C =
+.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386"
+ARCH_C += zfs_fletcher_intel.c \
+ zfs_fletcher_sse.c
+CFLAGS += -DHAVE_SSE2
+.endif
+.if ${MACHINE_ARCH} == "amd64"
+ARCH_C += zfs_fletcher_avx512.c
+CFLAGS+= -DHAVE_AVX2 -DHAVE_AVX -D__x86_64 -DHAVE_AVX512F
+.endif
+.if ${MACHINE_ARCH} == "aarch64"
+ARCH_C += zfs_fletcher_aarch64_neon.c
+.endif
+
+SRCS= $(USER_C) $(KERNEL_C) $(ARCH_C)
+
+WARNS?= 2
+SHLIB_MAJOR= 4
CSTD= c99
-CFLAGS+= -DZFS_NO_ACL
-CFLAGS+= -I${SRCTOP}/sbin/mount
-CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libshare
+CFLAGS+= -I${SRCTOP}/sys/contrib/ck/include
+CFLAGS+= -I${SRCTOP}/sys
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libuutil/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libcmdutils
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
+CFLAGS+= -I${SRCDIR}/sys/contrib/openzfs/module/zstd/include
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+CFLAGS+= -DHAVE_ISSETUGID
+CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
+CFLAGS.zfs_zstd.c= -Wno-cast-qual -Wno-pointer-arith
+CFLAGS.zstd.c= -fno-tree-vectorize
+
.include <bsd.lib.mk>
diff --git a/cddl/lib/libzfs_core/Makefile b/cddl/lib/libzfs_core/Makefile
index 412a5d2be9389..52747bfcc2d87 100644
--- a/cddl/lib/libzfs_core/Makefile
+++ b/cddl/lib/libzfs_core/Makefile
@@ -1,37 +1,28 @@
# $FreeBSD$
-.PATH: ${SRCTOP}/cddl/compat/opensolaris/misc
-.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
-.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libzfs_core
+.PATH: ${SRCTOP}/sys/contrib/openzfs/include
+
LIB= zfs_core
LIBADD= nvpair
PACKAGE= runtime
INCS= libzfs_core.h
-SRCS= libzfs_core.c \
- libzfs_core_compat.c \
- zfs_ioctl_compat.c
-
-SRCS+= libzfs_compat.c
+SRCS= libzfs_core.c
-WARNS?= 0
+WARNS?= 2
CSTD= c99
-CFLAGS+= -DZFS_NO_ACL
-CFLAGS+= -I${SRCTOP}/sbin/mount
-CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libzfs_core/common
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libuutil/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+CFLAGS+= -DHAVE_ISSETUGID
+CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
.include <bsd.lib.mk>
diff --git a/cddl/lib/libzpool/Makefile b/cddl/lib/libzpool/Makefile
index 576b89b7725a8..e1090c628c1f3 100644
--- a/cddl/lib/libzpool/Makefile
+++ b/cddl/lib/libzpool/Makefile
@@ -1,20 +1,17 @@
# $FreeBSD$
-.include "${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/Makefile.files"
# ZFS_COMMON_SRCS
-.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
+.PATH: ${SRCTOP}/sys/contrib/openzfs/module/zfs
+.PATH: ${SRCTOP}/sys/contrib/openzfs/module/zcommon
+.PATH: ${SRCTOP}/sys/contrib/openzfs/module/unicode
# LUA_SRCS
-.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua
-# ZFS_SHARED_SRCS
-.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
-# LZ4_COMMON_SRCS
-.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/lz4
-# KERNEL_SRCS
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
-# LIST_SRCS
-.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/os
-# ATOMIC_SRCS
+.PATH: ${SRCTOP}/sys/contrib/openzfs/module/lua
+
+.PATH: ${SRCTOP}/sys/contrib/openzfs/module/os/linux/zfs
+
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libzpool
+
.if exists(${SRCTOP}/sys/cddl/contrib/opensolaris/common/atomic/${MACHINE_ARCH}/opensolaris_atomic.S)
.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/atomic/${MACHINE_ARCH}
ATOMIC_SRCS= opensolaris_atomic.S
@@ -23,40 +20,218 @@ ACFLAGS+= -Wa,--noexecstack
.PATH: ${SRCTOP}/sys/cddl/compat/opensolaris/kern
ATOMIC_SRCS= opensolaris_atomic.c
.endif
-# UNICODE_SRCS
-.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/unicode
-# LIBCMDUTILS_SRCS
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libcmdutils/common
+
+.if ${MACHINE_ARCH} == "powerpc"
+# Don't waste GOT entries on small data.
+PICFLAG= -fPIC
+.endif
LIB= zpool
-ZFS_COMMON_SRCS= ${ZFS_COMMON_OBJS:C/.o$/.c/} trim_map.c
-ZFS_SHARED_SRCS= ${ZFS_SHARED_OBJS:C/.o$/.c/}
-LZ4_COMMON_SRCS= lz4.c
-LUA_SRCS= ${LUA_OBJS:C/.o$/.c/}
-KERNEL_SRCS= kernel.c taskq.c util.c
-LIST_SRCS= list.c
-UNICODE_SRCS= u8_textprep.c
-LIBCMDUTILS_SRCS=nicenum.c
-
-SRCS= ${ZFS_COMMON_SRCS} ${ZFS_SHARED_SRCS} ${LUA_SRCS} \
- ${LZ4_COMMON_SRCS} ${KERNEL_SRCS} ${LIST_SRCS} ${ATOMIC_SRCS} \
- ${UNICODE_SRCS} ${LIBCMDUTILS_SRCS}
-
-WARNS?= 0
-CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/lua
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/lz4
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libcmdutils
+
+
+USER_C = \
+ kernel.c \
+ taskq.c \
+ util.c
+
+KERNEL_C = \
+ zfeature_common.c \
+ zfs_comutil.c \
+ zfs_deleg.c \
+ zfs_fletcher.c \
+ zfs_fletcher_superscalar.c \
+ zfs_fletcher_superscalar4.c \
+ zfs_namecheck.c \
+ zfs_prop.c \
+ zfs_uio.c \
+ zpool_prop.c \
+ zprop_common.c \
+ abd.c \
+ abd_os.c \
+ aggsum.c \
+ arc.c \
+ arc_os.c \
+ blkptr.c \
+ bplist.c \
+ bpobj.c \
+ bptree.c \
+ btree.c \
+ bqueue.c \
+ cityhash.c \
+ dbuf.c \
+ dbuf_stats.c \
+ ddt.c \
+ ddt_zap.c \
+ dmu.c \
+ dmu_diff.c \
+ dmu_object.c \
+ dmu_objset.c \
+ dmu_recv.c \
+ dmu_redact.c \
+ dmu_send.c \
+ dmu_traverse.c \
+ dmu_tx.c \
+ dmu_zfetch.c \
+ dnode.c \
+ dnode_sync.c \
+ dsl_bookmark.c \
+ dsl_dataset.c \
+ dsl_deadlist.c \
+ dsl_deleg.c \
+ dsl_dir.c \
+ dsl_crypt.c \
+ dsl_pool.c \
+ dsl_prop.c \
+ dsl_scan.c \
+ dsl_synctask.c \
+ dsl_destroy.c \
+ dsl_userhold.c \
+ edonr_zfs.c \
+ hkdf.c \
+ fm.c \
+ gzip.c \
+ lzjb.c \
+ lz4.c \
+ metaslab.c \
+ mmp.c \
+ multilist.c \
+ objlist.c \
+ pathname.c \
+ range_tree.c \
+ refcount.c \
+ rrwlock.c \
+ sa.c \
+ sha256.c \
+ skein_zfs.c \
+ spa.c \
+ spa_boot.c \
+ spa_checkpoint.c \
+ spa_config.c \
+ spa_errlog.c \
+ spa_history.c \
+ spa_log_spacemap.c \
+ spa_misc.c \
+ spa_stats.c \
+ space_map.c \
+ space_reftree.c \
+ txg.c \
+ trace.c \
+ uberblock.c \
+ unique.c \
+ vdev.c \
+ vdev_cache.c \
+ vdev_file.c \
+ vdev_indirect_births.c \
+ vdev_indirect.c \
+ vdev_indirect_mapping.c \
+ vdev_initialize.c \
+ vdev_label.c \
+ vdev_mirror.c \
+ vdev_missing.c \
+ vdev_queue.c \
+ vdev_raidz.c \
+ vdev_raidz_math_aarch64_neon.c \
+ vdev_raidz_math_aarch64_neonx2.c \
+ vdev_raidz_math_avx2.c \
+ vdev_raidz_math_avx512bw.c \
+ vdev_raidz_math_avx512f.c \
+ vdev_raidz_math.c \
+ vdev_raidz_math_scalar.c \
+ vdev_rebuild.c \
+ vdev_removal.c \
+ vdev_root.c \
+ vdev_trim.c \
+ zap.c \
+ zap_leaf.c \
+ zap_micro.c \
+ zcp.c \
+ zcp_get.c \
+ zcp_global.c \
+ zcp_iter.c \
+ zcp_set.c \
+ zcp_synctask.c \
+ zfeature.c \
+ zfs_byteswap.c \
+ zfs_debug.c \
+ zfs_fm.c \
+ zfs_fuid.c \
+ zfs_sa.c \
+ zfs_znode.c \
+ zfs_ratelimit.c \
+ zfs_rlock.c \
+ zil.c \
+ zio.c \
+ zio_checksum.c \
+ zio_compress.c \
+ zio_crypt.c \
+ zio_inject.c \
+ zle.c \
+ zrlock.c \
+ zthr.c
+
+ARCH_C =
+.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386"
+ARCH_C += vdev_raidz_math_sse2.c \
+ vdev_raidz_math_ssse3.c \
+ zfs_fletcher_intel.c \
+ zfs_fletcher_sse.c
+CFLAGS += -DHAVE_SSE2 -DHAVE_SSE3
+.endif
+.if ${MACHINE_ARCH} == "amd64"
+ARCH_C += zfs_fletcher_avx512.c
+CFLAGS+= -DHAVE_AVX2 -DHAVE_AVX -D__x86_64 -DHAVE_AVX512F \
+ -DHAVE_AVX512BW
+.endif
+.if ${MACHINE_ARCH} == "aarch64"
+ARCH_C += zfs_fletcher_aarch64_neon.c
+.endif
+
+LUA_C = \
+ lapi.c \
+ lauxlib.c \
+ lbaselib.c \
+ lcode.c \
+ lcompat.c \
+ lcorolib.c \
+ lctype.c \
+ ldebug.c \
+ ldo.c \
+ lfunc.c \
+ lgc.c \
+ llex.c \
+ lmem.c \
+ lobject.c \
+ lopcodes.c \
+ lparser.c \
+ lstate.c \
+ lstring.c \
+ lstrlib.c \
+ ltable.c \
+ ltablib.c \
+ ltm.c \
+ lvm.c \
+ lzio.c
+
+UNICODE_C = u8_textprep.c uconv.c
+
+SRCS= ${USER_C} ${KERNEL_C} ${LUA_C} ${UNICODE_C} ${ARCH_C}
+
+WARNS?= 2
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+CFLAGS+= -DHAVE_ISSETUGID
+CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
+CFLAGS+= -I${SRCTOP}/sys/modules/zfs
+CFLAGS+= -DLIB_ZPOOL_BUILD -DZFS_DEBUG
+
+
# XXX: pthread doesn't have mutex_owned() equivalent, so we need to look
# into libthr private structures. That's sooo evil, but it's only for
# ZFS debugging tools needs.
@@ -64,10 +239,9 @@ CFLAGS+= -DWANTS_MUTEX_OWNED
CFLAGS+= -I${SRCTOP}/lib/libpthread/thread
CFLAGS+= -I${SRCTOP}/lib/libpthread/sys
CFLAGS+= -I${SRCTOP}/lib/libthr/arch/${MACHINE_CPUARCH}/include
-CFLAGS.lz4.c+= -D_FAKE_KERNEL
CFLAGS.gcc+= -fms-extensions
-LIBADD= md pthread z nvpair avl umem
+LIBADD= md pthread z spl icp nvpair avl umem
# atomic.S doesn't like profiling.
MK_PROFILE= no
diff --git a/cddl/lib/libzpool/Makefile.depend b/cddl/lib/libzpool/Makefile.depend
index 97914fc35322a..06045f57b4371 100644
--- a/cddl/lib/libzpool/Makefile.depend
+++ b/cddl/lib/libzpool/Makefile.depend
@@ -3,6 +3,7 @@
DIRDEPS = \
cddl/lib/libavl \
+ cddl/lib/libicp \
cddl/lib/libnvpair \
cddl/lib/libumem \
gnu/lib/csu \
diff --git a/cddl/lib/libzutil/Makefile b/cddl/lib/libzutil/Makefile
new file mode 100644
index 0000000000000..7aea9da14e90e
--- /dev/null
+++ b/cddl/lib/libzutil/Makefile
@@ -0,0 +1,42 @@
+# $FreeBSD$
+
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libzutil
+.PATH: ${SRCTOP}/sys/contrib/openzfs/lib/libzutil/os/freebsd
+.PATH: ${SRCTOP}/sys/contrib/openzfs/module/os/freebsd/zfs
+
+LIB= zutil
+LIBADD= avl tpool
+PACKAGE= runtime
+
+INCS = zutil_import.h
+
+SRCS = \
+ zutil_device_path.c \
+ zutil_import.c \
+ zutil_import.h \
+ zutil_nicenum.c \
+ zutil_pool.c
+
+SRCS += \
+ zutil_device_path_os.c \
+ zutil_import_os.c \
+ zutil_compat.c
+
+SRCS += zfs_ioctl_compat.c
+
+
+WARNS?= 2
+CSTD= c99
+
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/zfs
+CFLAGS+= -I${SRCTOP}/sys
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libzutil
+CFLAGS+= -DHAVE_ISSETUGID -DIN_BASE
+CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
+
+.include <bsd.lib.mk>
diff --git a/cddl/sbin/zfs/Makefile b/cddl/sbin/zfs/Makefile
index e3645bd362b70..40f713d069c86 100644
--- a/cddl/sbin/zfs/Makefile
+++ b/cddl/sbin/zfs/Makefile
@@ -1,27 +1,77 @@
# $FreeBSD$
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/cmd/zfs
+ZFSTOP= ${SRCTOP}/sys/contrib/openzfs
+
+.PATH: ${ZFSTOP}/cmd/zfs
+.PATH: ${ZFSTOP}/man/man8
+.PATH: ${ZFSTOP}/module/os/freebsd/spl
PACKAGE= runtime
PROG= zfs
-MAN= zfs.8 zfs-program.8
-SRCS= zfs_main.c zfs_iter.c
+MAN= \
+ zfs.8 \
+ zfs-allow.8 \
+ zfs-bookmark.8 \
+ zfs-change-key.8 \
+ zfs-clone.8 \
+ zfs-create.8 \
+ zfs-destroy.8 \
+ zfs-diff.8 \
+ zfs-get.8 \
+ zfs-groupspace.8 \
+ zfs-hold.8 \
+ zfs-inherit.8 \
+ zfs-jail.8 \
+ zfs-list.8 \
+ zfs-load-key.8 \
+ zfs-mount.8 \
+ zfs-program.8 \
+ zfs-project.8 \
+ zfs-projectspace.8 \
+ zfs-promote.8 \
+ zfs-receive.8 \
+ zfs-recv.8 \
+ zfs-redact.8 \
+ zfs-release.8 \
+ zfs-rename.8 \
+ zfs-rollback.8 \
+ zfs-send.8 \
+ zfs-set.8 \
+ zfs-share.8 \
+ zfs-snapshot.8 \
+ zfs-unallow.8 \
+ zfs-unjail.8 \
+ zfs-unload-key.8 \
+ zfs-unmount.8 \
+ zfs-upgrade.8 \
+ zfs-userspace.8 \
+ zfs-wait.8 \
+ zfsconcepts.8 \
+ zfsprops.8
+SRCS= \
+ zfs_iter.c \
+ zfs_iter.h \
+ zfs_main.c \
+ zfs_util.h \
+ zfs_project.c \
+ zfs_projectutil.h
-WARNS?= 0
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
-CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libuutil/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libumem/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
+WARNS?= 2
-LIBADD= jail nvpair uutil zfs_core zfs
+CFLAGS+= \
+ -DIN_BASE \
+ -I${ZFSTOP}/include \
+ -I${ZFSTOP}/include/os/freebsd \
+ -I${ZFSTOP}/lib/libspl/include \
+ -I${ZFSTOP}/lib/libspl/include/os/freebsd \
+ -I${SRCTOP}/sys \
+ -I${SRCTOP}/cddl/compat/opensolaris/include \
+ -I${ZFSTOP}/module/icp/include \
+ -include ${ZFSTOP}/include/os/freebsd/spl/sys/ccompile.h \
+ -DHAVE_ISSETUGID \
+ -include ${SRCTOP}/sys/modules/zfs/zfs_config.h \
+ -I${SRCTOP}/sys/modules/zfs
+LIBADD= jail avl nvpair geom uutil zfs_core spl tpool zutil zfs m crypto
+LDADD+= -pthread
.include <bsd.prog.mk>
diff --git a/cddl/sbin/zpool/Makefile b/cddl/sbin/zpool/Makefile
index f4a3b9b6f5ccf..6928cb132780a 100644
--- a/cddl/sbin/zpool/Makefile
+++ b/cddl/sbin/zpool/Makefile
@@ -1,32 +1,76 @@
# $FreeBSD$
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/cmd/zpool
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/cmd/stat/common
-.PATH: ${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
+ZFSTOP= ${SRCTOP}/sys/contrib/openzfs
+
+.PATH: ${ZFSTOP}/man/man5
+.PATH: ${ZFSTOP}/man/man8
+.PATH: ${ZFSTOP}/cmd/zpool
+.PATH: ${ZFSTOP}/cmd/zpool/os/freebsd
+
PACKAGE= runtime
PROG= zpool
-MAN= zpool.8 zpool-features.7
-SRCS= zpool_main.c zpool_vdev.c zpool_iter.c zpool_util.c zfs_comutil.c
-SRCS+= timestamp.c
+MAN= \
+ spl-module-parameters.5 \
+ zfs-module-parameters.5 \
+ zpool.8 \
+ zpool-add.8 \
+ zpool-attach.8 \
+ zpool-checkpoint.8 \
+ zpool-clear.8 \
+ zpool-create.8 \
+ zpool-destroy.8 \
+ zpool-detach.8 \
+ zpool-events.8 \
+ zpool-export.8 \
+ zpool-features.5 \
+ zpool-get.8 \
+ zpool-history.8 \
+ zpool-import.8 \
+ zpool-initialize.8 \
+ zpool-iostat.8 \
+ zpool-labelclear.8 \
+ zpool-list.8 \
+ zpool-offline.8 \
+ zpool-online.8 \
+ zpool-reguid.8 \
+ zpool-remove.8 \
+ zpool-reopen.8 \
+ zpool-replace.8 \
+ zpool-resilver.8 \
+ zpool-scrub.8 \
+ zpool-set.8 \
+ zpool-split.8 \
+ zpool-status.8 \
+ zpool-sync.8 \
+ zpool-trim.8 \
+ zpool-upgrade.8 \
+ zpool-wait.8 \
+ zpoolconcepts.8 \
+ zpoolprops.8
+SRCS= \
+ zpool_iter.c \
+ zpool_main.c \
+ zpool_util.c \
+ zpool_util.h \
+ zpool_vdev.c \
+ zpool_vdev_os.c
-WARNS?= 0
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
-CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libuutil/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libumem/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/cmd/stat/common
+WARNS?= 2
-LIBADD= geom nvpair uutil zfs
+CFLAGS+= \
+ -DIN_BASE \
+ -I${ZFSTOP}/include \
+ -I${ZFSTOP}/lib/libspl/include \
+ -I${ZFSTOP}/lib/libspl/include/os/freebsd \
+ -I${SRCTOP}/sys \
+ -I${SRCTOP}/cddl/compat/opensolaris/include \
+ -I${ZFSTOP}/cmd/zpool \
+ -include ${ZFSTOP}/include/os/freebsd/spl/sys/ccompile.h \
+ -DHAVE_ISSETUGID \
+ -include ${SRCTOP}/sys/modules/zfs/zfs_config.h \
+ -DSYSCONFDIR=\"/etc\"
+LIBADD= geom nvpair uutil zfs zutil avl spl tpool zfs_core m
+LDADD+= -pthread
.include <bsd.prog.mk>
diff --git a/cddl/usr.bin/Makefile b/cddl/usr.bin/Makefile
index 573f99b55bd86..3416244b5b0b3 100644
--- a/cddl/usr.bin/Makefile
+++ b/cddl/usr.bin/Makefile
@@ -7,7 +7,7 @@ SUBDIR= \
ctfdump \
ctfmerge \
${_zinject} \
- ${_zlook} \
+ ${_zstream} \
${_zstreamdump} \
${_ztest}
@@ -15,10 +15,9 @@ SUBDIR.${MK_TESTS}+= tests
.if ${MK_ZFS} != "no"
_zinject= zinject
-#_zlook= zlook
.if ${MK_LIBTHR} != "no"
_ztest= ztest
-_zstreamdump = zstreamdump
+_zstream = zstream
.endif
.endif
diff --git a/cddl/usr.bin/ctfconvert/Makefile b/cddl/usr.bin/ctfconvert/Makefile
index 42061c1fa3d3b..699b1985e246b 100644
--- a/cddl/usr.bin/ctfconvert/Makefile
+++ b/cddl/usr.bin/ctfconvert/Makefile
@@ -27,6 +27,12 @@ SRCS= alist.c \
traverse.c \
util.c
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
-I${SRCTOP}/cddl/compat/opensolaris/include \
-I${OPENSOLARIS_USR_DISTDIR} \
@@ -35,8 +41,9 @@ CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
-I${OPENSOLARIS_USR_DISTDIR}/tools/ctf/common \
-I${OPENSOLARIS_USR_DISTDIR}/tools/ctf/cvt \
-I${OPENSOLARIS_SYS_DISTDIR}/uts/common
+CFLAGS+= -DHAVE_ISSETUGID
-LIBADD= dwarf elf z pthread
+LIBADD= spl dwarf elf z pthread
HAS_TESTS=
SUBDIR.${MK_TESTS}+= tests
diff --git a/cddl/usr.bin/ctfconvert/Makefile.depend b/cddl/usr.bin/ctfconvert/Makefile.depend
index 44e570745e224..eb40124b7c902 100644
--- a/cddl/usr.bin/ctfconvert/Makefile.depend
+++ b/cddl/usr.bin/ctfconvert/Makefile.depend
@@ -5,6 +5,7 @@ DIRDEPS = \
gnu/lib/csu \
include \
include/xlocale \
+ cddl/lib/libspl \
lib/${CSU_DIR} \
lib/libc \
lib/libcompiler_rt \
diff --git a/cddl/usr.bin/ctfdump/Makefile b/cddl/usr.bin/ctfdump/Makefile
index 953acfd3abb84..e7347b272dac2 100644
--- a/cddl/usr.bin/ctfdump/Makefile
+++ b/cddl/usr.bin/ctfdump/Makefile
@@ -8,6 +8,13 @@ SRCS= dump.c \
symbol.c \
utils.c
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
+CFLAGS+= -I${SRCTOP}/sys
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
CFLAGS+= -I${OPENSOLARIS_USR_DISTDIR} \
-I${OPENSOLARIS_SYS_DISTDIR} \
-I${OPENSOLARIS_USR_DISTDIR}/head \
@@ -16,6 +23,7 @@ CFLAGS+= -I${OPENSOLARIS_USR_DISTDIR} \
-I${SRCTOP}/cddl/compat/opensolaris/include \
-I${OPENSOLARIS_USR_DISTDIR}/tools/ctf/common \
-I${OPENSOLARIS_SYS_DISTDIR}/uts/common
+CFLAGS+= -DHAVE_ISSETUGID
LIBADD= elf z
diff --git a/cddl/usr.bin/ctfmerge/Makefile b/cddl/usr.bin/ctfmerge/Makefile
index c863ea0edf871..20e79463fea5e 100644
--- a/cddl/usr.bin/ctfmerge/Makefile
+++ b/cddl/usr.bin/ctfmerge/Makefile
@@ -24,6 +24,13 @@ SRCS= alist.c \
WARNS?= 1
+
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
-I${SRCTOP}/cddl/compat/opensolaris/include \
-I${OPENSOLARIS_USR_DISTDIR} \
@@ -32,7 +39,8 @@ CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
-I${OPENSOLARIS_USR_DISTDIR}/tools/ctf/common \
-I${OPENSOLARIS_USR_DISTDIR}/tools/ctf/cvt \
-I${OPENSOLARIS_SYS_DISTDIR}/uts/common
+CFLAGS+= -DHAVE_ISSETUGID
-LIBADD= elf z pthread
+LIBADD= spl elf z pthread
.include <bsd.prog.mk>
diff --git a/cddl/usr.bin/zinject/Makefile b/cddl/usr.bin/zinject/Makefile
index 5cfd7e29edb06..fcc472c24214b 100644
--- a/cddl/usr.bin/zinject/Makefile
+++ b/cddl/usr.bin/zinject/Makefile
@@ -1,24 +1,28 @@
# $FreeBSD$
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/cmd/zinject
+ZFSTOP= ${SRCTOP}/sys/contrib/openzfs
+
+.PATH: ${ZFSTOP}/cmd/zinject
+.PATH: ${ZFSTOP}/man/man8
PROG= zinject
+INCS= zinject.h
SRCS= zinject.c translate.c
-MAN=
+MAN= zinject.8
-WARNS?= 0
-CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs/
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
+WARNS?= 2
+CFLAGS+= \
+ -DIN_BASE \
+ -I${ZFSTOP}/include \
+ -I${ZFSTOP}/lib/libspl/include \
+ -I${ZFSTOP}/lib/libspl/include/os/freebsd \
+ -I${SRCTOP}/sys \
+ -I${SRCTOP}/cddl/compat/opensolaris/include \
+ -I${ZFSTOP}/module/icp/include \
+ -include ${ZFSTOP}/include/os/freebsd/spl/sys/ccompile.h \
+ -DHAVE_ISSETUGID \
+ -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
-LIBADD= geom m nvpair umem uutil zfs_core zfs zpool
+LIBADD= geom m nvpair umem uutil avl spl zfs_core zfs zutil zpool
.include <bsd.prog.mk>
diff --git a/cddl/usr.bin/zlook/Makefile b/cddl/usr.bin/zlook/Makefile
deleted file mode 100644
index e36be05e7b71d..0000000000000
--- a/cddl/usr.bin/zlook/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-# $FreeBSD$
-
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/cmd/zlook
-
-PROG= zlook
-MAN=
-
-WARNS?= 0
-CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
-
-.include <bsd.prog.mk>
diff --git a/cddl/usr.bin/zstream/Makefile b/cddl/usr.bin/zstream/Makefile
new file mode 100644
index 0000000000000..d6ac8c5f3b16f
--- /dev/null
+++ b/cddl/usr.bin/zstream/Makefile
@@ -0,0 +1,32 @@
+# $FreeBSD$
+
+ZFSTOP= ${SRCTOP}/sys/contrib/openzfs
+
+.PATH: ${ZFSTOP}/cmd/zstream
+.PATH: ${ZFSTOP}/man/man8
+
+PROG= zstream
+MAN= zstream.8
+INCS= zstream.h
+SRCS= \
+ zstream.c \
+ zstream_dump.c \
+ zstream_redup.c \
+ zstream_token.c
+
+WARNS?= 2
+CFLAGS+= \
+ -DIN_BASE \
+ -I${ZFSTOP}/include \
+ -I${ZFSTOP}/lib/libspl/include \
+ -I${ZFSTOP}/lib/libspl/include/os/freebsd \
+ -I${SRCTOP}/sys \
+ -I${SRCTOP}/cddl/compat/opensolaris/include \
+ -I${ZFSTOP}/module/icp/include \
+ -include ${ZFSTOP}/include/os/freebsd/spl/sys/ccompile.h \
+ -DHAVE_ISSETUGID \
+ -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
+
+LIBADD= geom m nvpair umem uutil avl spl zfs_core zfs zutil zpool
+
+.include <bsd.prog.mk>
diff --git a/cddl/usr.bin/zstreamdump/Makefile b/cddl/usr.bin/zstreamdump/Makefile
index 69fd399e14d29..63f365d0445ab 100644
--- a/cddl/usr.bin/zstreamdump/Makefile
+++ b/cddl/usr.bin/zstreamdump/Makefile
@@ -1,23 +1,11 @@
# $FreeBSD$
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/cmd/zstreamdump
+ZFSTOP= ${SRCTOP}/sys/contrib/openzfs
-PROG= zstreamdump
-MAN= zstreamdump.1
+.PATH: ${ZFSTOP}/cmd/zstreamdump
+.PATH: ${ZFSTOP}/man/man8
-WARNS?= 0
-CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
-
-LIBADD= m nvpair umem zpool zfs pthread z avl
-
-CSTD= c99
+SCRIPTS= zstreamdump
+MAN= zstreamdump.8
.include <bsd.prog.mk>
diff --git a/cddl/usr.bin/ztest/Makefile b/cddl/usr.bin/ztest/Makefile
index 5b44aa2d80974..de8ae801bb766 100644
--- a/cddl/usr.bin/ztest/Makefile
+++ b/cddl/usr.bin/ztest/Makefile
@@ -1,30 +1,33 @@
# $FreeBSD$
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/cmd/ztest
+ZFSTOP= ${SRCTOP}/sys/contrib/openzfs
+
+.PATH: ${ZFSTOP}/cmd/ztest
+.PATH: ${ZFSTOP}/man/man1
PROG= ztest
-MAN=
-
-WARNS?= 0
-CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libcmdutils
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
-
-LIBADD= geom m nvpair umem zpool pthread avl zfs_core zfs uutil
+MAN= ztest.1
+
+WARNS?= 2
+CFLAGS+= \
+ -DIN_BASE \
+ -I${ZFSTOP}/include \
+ -I${ZFSTOP}/lib/libspl/include \
+ -I${ZFSTOP}/lib/libspl/include/os/freebsd \
+ -I${SRCTOP}/sys \
+ -I${SRCTOP}/cddl/compat/opensolaris/include \
+ -I${ZFSTOP}/module/icp/include \
+ -include ${ZFSTOP}/include/os/freebsd/spl/sys/ccompile.h \
+ -DHAVE_ISSETUGID \
+ -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
+
+LIBADD= geom m nvpair umem zpool pthread avl zfs_core spl zutil zfs uutil icp
CSTD= c99
# Since there are many asserts in this program, it makes no sense to compile
# it without debugging.
-CFLAGS+= -g -DDEBUG=1 -Wno-format
+CFLAGS+= -g -DDEBUG=1 -Wno-format -DZFS_DEBUG=1
CFLAGS.gcc+= -fms-extensions
HAS_TESTS=
diff --git a/cddl/usr.sbin/dtrace/Makefile b/cddl/usr.sbin/dtrace/Makefile
index 501b7c14861a8..19a55d5dc1bef 100644
--- a/cddl/usr.sbin/dtrace/Makefile
+++ b/cddl/usr.sbin/dtrace/Makefile
@@ -10,6 +10,13 @@ BINDIR?= /usr/sbin
WARNS?= 1
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
+CFLAGS+= -I${SRCTOP}/sys
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
-I${SRCTOP}/cddl/compat/opensolaris/include \
-I${OPENSOLARIS_USR_DISTDIR}/head \
@@ -17,12 +24,13 @@ CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
-I${OPENSOLARIS_USR_DISTDIR}/lib/libproc/common \
-I${OPENSOLARIS_SYS_DISTDIR}/uts/common \
-I${OPENSOLARIS_SYS_DISTDIR}/compat
+CFLAGS+= -DHAVE_ISSETUGID
# Optional debugging stuff...
#CFLAGS+= -DNEED_ERRLOC
#YFLAGS+= -d
-LIBADD= dtrace ctf elf proc
+LIBADD= dtrace ctf elf proc spl
.if ${MK_DTRACE_TESTS} != "no"
SUBDIR+= tests
diff --git a/cddl/usr.sbin/lockstat/Makefile b/cddl/usr.sbin/lockstat/Makefile
index 3ffcc7049f35d..6efb9e6fa5c50 100644
--- a/cddl/usr.sbin/lockstat/Makefile
+++ b/cddl/usr.sbin/lockstat/Makefile
@@ -8,6 +8,14 @@ BINDIR?= /usr/sbin
WARNS?= 1
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
+CFLAGS+= -I${SRCTOP}/sys
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
+CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
-I${SRCTOP}/cddl/compat/opensolaris/include \
-I${OPENSOLARIS_USR_DISTDIR}/head \
@@ -16,6 +24,7 @@ CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
-I${OPENSOLARIS_SYS_DISTDIR}/uts/common \
-I${OPENSOLARIS_SYS_DISTDIR}/compat \
-I${SRCTOP}/sys
+CFLAGS+= -DHAVE_ISSETUGID
CFLAGS+= -DNEED_ERRLOC -g
diff --git a/cddl/usr.sbin/plockstat/Makefile b/cddl/usr.sbin/plockstat/Makefile
index cb263fb427a78..f65ac8146e4e6 100644
--- a/cddl/usr.sbin/plockstat/Makefile
+++ b/cddl/usr.sbin/plockstat/Makefile
@@ -8,6 +8,13 @@ BINDIR?= /usr/sbin
WARNS?= 1
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
+CFLAGS+= -I${SRCTOP}/sys
+CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
-I${SRCTOP}/cddl/compat/opensolaris/include \
-I${OPENSOLARIS_USR_DISTDIR}/head \
@@ -17,6 +24,7 @@ CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris \
-I${OPENSOLARIS_SYS_DISTDIR}/compat \
-I${SRCTOP}/cddl/lib/libdtrace \
-I${SRCTOP}/sys
+CFLAGS+= -DHAVE_ISSETUGID
LIBADD= dtrace proc
diff --git a/cddl/usr.sbin/zdb/Makefile b/cddl/usr.sbin/zdb/Makefile
index 8c919341f8e39..efd8c746128fe 100644
--- a/cddl/usr.sbin/zdb/Makefile
+++ b/cddl/usr.sbin/zdb/Makefile
@@ -1,33 +1,33 @@
# $FreeBSD$
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/cmd/zdb
+ZFSTOP= ${SRCTOP}/sys/contrib/openzfs
+
+.PATH: ${ZFSTOP}/cmd/zdb
+.PATH: ${ZFSTOP}/man/man8
PROG= zdb
MAN= zdb.8
+INCS= zdb.h
SRCS= zdb.c zdb_il.c
WARNS?= 2
CSTD= c99
-CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libcmdutils
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libuutil/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
-
-LIBADD= nvpair umem uutil zfs zpool
+CFLAGS+= \
+ -DIN_BASE \
+ -I${ZFSTOP}/include \
+ -I${ZFSTOP}/lib/libspl/include \
+ -I${ZFSTOP}/lib/libspl/include/os/freebsd \
+ -I${ZFSTOP}/lib/libspl/include/os/freebsd/spl \
+ -I${SRCTOP}/sys \
+ -include ${ZFSTOP}/include/os/freebsd/spl/sys/ccompile.h \
+ -DHAVE_ISSETUGID
+
+LIBADD= nvpair umem uutil zfs spl avl zutil zpool
CFLAGS.gcc+= -fms-extensions
# Since there are many asserts in this program, it makes no sense to compile
# it without debugging.
-CFLAGS+= -g -DDEBUG=1
+CFLAGS+= -g -DDEBUG=1 -DZFS_DEBUG=1
.include <bsd.prog.mk>
diff --git a/cddl/usr.sbin/zfsd/Makefile.common b/cddl/usr.sbin/zfsd/Makefile.common
index 7d45a3ed16951..95882dec175a4 100644
--- a/cddl/usr.sbin/zfsd/Makefile.common
+++ b/cddl/usr.sbin/zfsd/Makefile.common
@@ -10,29 +10,24 @@ SRCS= callout.cc \
zpool_list.cc \
zfsd_main.cc
-WARNS?= 3
+WARNS?= 2
# Ignore warnings about Solaris specific pragmas.
IGNORE_PRAGMA= YES
-INCFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
-INCFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
-INCFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
-INCFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
-INCFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
-INCFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libuutil/common
-INCFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libumem/common
-INCFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
-INCFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
-INCFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
-INCFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
-INCFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
-INCFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
-INCFLAGS+= -I${SRCTOP}/cddl/usr.sbin
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd/spl
+CFLAGS+= -I${SRCTOP}/sys
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+CFLAGS+= -I${SRCTOP}/cddl/usr.sbin
-CFLAGS+= -DNEED_SOLARIS_BOOLEAN ${INCFLAGS}
+# use issetugid(2)
+CFLAGS+= -D_MACHINE_FLOAT_H_ -DHAVE_ISSETUGID
-LIBADD+= devdctl zfs zfs_core util geom bsdxml sbuf nvpair uutil
+LIBADD+= devdctl zfs zfs_core util geom bsdxml sbuf nvpair avl uutil zutil
cscope:
find ${.CURDIR} -type f -a \( -name "*.[ch]" -o -name "*.cc" \) \
diff --git a/cddl/usr.sbin/zfsd/callout.cc b/cddl/usr.sbin/zfsd/callout.cc
index 2671c5d3e7837..3e5cd5779559f 100644
--- a/cddl/usr.sbin/zfsd/callout.cc
+++ b/cddl/usr.sbin/zfsd/callout.cc
@@ -39,6 +39,7 @@
* timer services built on top of the POSIX interval timer.
*/
+#include <sys/byteorder.h>
#include <sys/time.h>
#include <signal.h>
diff --git a/cddl/usr.sbin/zfsd/case_file.cc b/cddl/usr.sbin/zfsd/case_file.cc
index 19c4abe45fc9d..da2125b4d716d 100644
--- a/cddl/usr.sbin/zfsd/case_file.cc
+++ b/cddl/usr.sbin/zfsd/case_file.cc
@@ -39,11 +39,13 @@
* accumulate in order to mark a device as degraded.
*/
#include <sys/cdefs.h>
+#include <sys/byteorder.h>
#include <sys/time.h>
#include <sys/fs/zfs.h>
#include <dirent.h>
+#include <fcntl.h>
#include <iomanip>
#include <fstream>
#include <functional>
@@ -75,7 +77,6 @@
__FBSDID("$FreeBSD$");
/*============================ Namespace Control =============================*/
-using std::auto_ptr;
using std::hex;
using std::ifstream;
using std::stringstream;
@@ -239,8 +240,6 @@ CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev)
{
ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID);
zpool_handle_t *pool(zpl.empty() ? NULL : zpl.front());
- zpool_boot_label_t boot_type;
- uint64_t boot_size;
if (pool == NULL || !RefreshVdevState()) {
/*
@@ -333,13 +332,7 @@ CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev)
}
/* Write a label on the newly inserted disk. */
- if (zpool_is_bootable(pool))
- boot_type = ZPOOL_COPY_BOOT_LABEL;
- else
- boot_type = ZPOOL_NO_BOOT_LABEL;
- boot_size = zpool_get_prop_int(pool, ZPOOL_PROP_BOOTSIZE, NULL);
- if (zpool_label_disk(g_zfsHandle, pool, devPath.c_str(),
- boot_type, boot_size, NULL) != 0) {
+ if (zpool_label_disk(g_zfsHandle, pool, devPath.c_str()) != 0) {
syslog(LOG_ERR,
"Replace vdev(%s/%s) by physical path (label): %s: %s\n",
zpool_get_name(pool), VdevGUIDString().c_str(),
@@ -1118,7 +1111,7 @@ CaseFile::Replace(const char* vdev_type, const char* path, bool isspare) {
nvlist_free(newvd);
retval = (zpool_vdev_attach(zhp, oldstr.c_str(), path, nvroot,
- /*replace*/B_TRUE) == 0);
+ /*replace*/B_TRUE, /*rebuild*/ B_FALSE) == 0);
if (retval)
syslog(LOG_INFO, "Replacing vdev(%s/%s) with %s\n",
poolname, oldstr.c_str(), path);
diff --git a/cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc b/cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc
index d65295075c1f1..e2833170f9ac7 100644
--- a/cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc
+++ b/cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc
@@ -30,6 +30,7 @@
* Authors: Alan Somers (Spectra Logic Corporation)
*/
#include <sys/cdefs.h>
+#include <sys/byteorder.h>
#include <stdarg.h>
#include <syslog.h>
diff --git a/cddl/usr.sbin/zfsd/vdev.cc b/cddl/usr.sbin/zfsd/vdev.cc
index 687b0647ceb55..508af8cda9258 100644
--- a/cddl/usr.sbin/zfsd/vdev.cc
+++ b/cddl/usr.sbin/zfsd/vdev.cc
@@ -39,6 +39,7 @@
*/
#include <syslog.h>
#include <sys/cdefs.h>
+#include <sys/byteorder.h>
#include <sys/fs/zfs.h>
#include <libzfs.h>
diff --git a/cddl/usr.sbin/zfsd/vdev_iterator.cc b/cddl/usr.sbin/zfsd/vdev_iterator.cc
index 31a4ce962970e..b5a4f22c1c605 100644
--- a/cddl/usr.sbin/zfsd/vdev_iterator.cc
+++ b/cddl/usr.sbin/zfsd/vdev_iterator.cc
@@ -38,6 +38,7 @@
* Implementation of the VdevIterator class.
*/
#include <sys/cdefs.h>
+#include <sys/byteorder.h>
#include <sys/fs/zfs.h>
#include <stdint.h>
diff --git a/cddl/usr.sbin/zfsd/zfsd.cc b/cddl/usr.sbin/zfsd/zfsd.cc
index 2f17b474e493a..876cca836e9e8 100644
--- a/cddl/usr.sbin/zfsd/zfsd.cc
+++ b/cddl/usr.sbin/zfsd/zfsd.cc
@@ -42,10 +42,12 @@
*/
#include <sys/cdefs.h>
+#include <sys/byteorder.h>
#include <sys/param.h>
#include <sys/fs/zfs.h>
#include <err.h>
+#include <fcntl.h>
#include <libgeom.h>
#include <libutil.h>
#include <poll.h>
diff --git a/cddl/usr.sbin/zfsd/zfsd_event.cc b/cddl/usr.sbin/zfsd/zfsd_event.cc
index 707a868c67e8a..688e7c0354a28 100644
--- a/cddl/usr.sbin/zfsd/zfsd_event.cc
+++ b/cddl/usr.sbin/zfsd/zfsd_event.cc
@@ -34,6 +34,7 @@
* \file zfsd_event.cc
*/
#include <sys/cdefs.h>
+#include <sys/byteorder.h>
#include <sys/time.h>
#include <sys/fs/zfs.h>
#include <sys/vdev_impl.h>
@@ -41,12 +42,13 @@
#include <syslog.h>
#include <libzfs.h>
+#include <libzutil.h>
/*
* Undefine flush, defined by cpufunc.h on sparc64, because it conflicts with
* C++ flush methods
*/
#undef flush
-
+#undef __init
#include <list>
#include <map>
#include <sstream>
@@ -190,7 +192,8 @@ GeomEvent::ReadLabel(int devFd, bool &inUse, bool &degraded)
if (poolName != NULL)
free(poolName);
- nlabels = zpool_read_all_labels(devFd, &devLabel);
+ if (zpool_read_label(devFd, &devLabel, &nlabels) != 0)
+ return (NULL);
/*
* If we find a disk with fewer than the maximum number of
* labels, it might be the whole disk of a partitioned disk
diff --git a/cddl/usr.sbin/zfsd/zfsd_exception.cc b/cddl/usr.sbin/zfsd/zfsd_exception.cc
index 7859a654003e2..7ebb74ca8c6a7 100644
--- a/cddl/usr.sbin/zfsd/zfsd_exception.cc
+++ b/cddl/usr.sbin/zfsd/zfsd_exception.cc
@@ -36,6 +36,7 @@
* Implementation of the ZfsdException class.
*/
#include <sys/cdefs.h>
+#include <sys/byteorder.h>
#include <sys/fs/zfs.h>
#include <syslog.h>
diff --git a/cddl/usr.sbin/zfsd/zpool_list.cc b/cddl/usr.sbin/zfsd/zpool_list.cc
index 1d1540b294a22..82c35736df13a 100644
--- a/cddl/usr.sbin/zfsd/zpool_list.cc
+++ b/cddl/usr.sbin/zfsd/zpool_list.cc
@@ -38,6 +38,7 @@
* Implementation of the ZpoolList class.
*/
#include <sys/cdefs.h>
+#include <sys/byteorder.h>
#include <sys/fs/zfs.h>
#include <stdint.h>
diff --git a/cddl/usr.sbin/zhack/Makefile b/cddl/usr.sbin/zhack/Makefile
index 1347111c7c175..24800c693cf36 100644
--- a/cddl/usr.sbin/zhack/Makefile
+++ b/cddl/usr.sbin/zhack/Makefile
@@ -1,6 +1,6 @@
# $FreeBSD$
-.PATH: ${SRCTOP}/cddl/contrib/opensolaris/cmd/zhack
+.PATH: ${SRCTOP}/sys/contrib/openzfs/cmd/zhack
PROG= zhack
MAN=
@@ -8,20 +8,20 @@ MAN=
WARNS?= 0
CSTD= c99
-CFLAGS+= -I${SRCTOP}/sys/cddl/compat/opensolaris
+WARNS?= 2
+CFLAGS+= -DIN_BASE
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/include
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/lib/libspl/include/os/freebsd
+CFLAGS+= -I${SRCTOP}/sys
CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/include
-CFLAGS+= -I${SRCTOP}/cddl/compat/opensolaris/lib/libumem
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libnvpair
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libuutil/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzfs_core/common
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/lib/libzpool/common
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/uts/common
-CFLAGS+= -I${SRCTOP}/sys/cddl/contrib/opensolaris/common/zfs
-CFLAGS+= -I${SRCTOP}/cddl/contrib/opensolaris/head
-
-LIBADD= nvpair zfs zpool
+CFLAGS+= -I${SRCTOP}/sys/contrib/openzfs/module/icp/include
+CFLAGS+= -include ${SRCTOP}/sys/contrib/openzfs/include/os/freebsd/spl/sys/ccompile.h
+CFLAGS+= -DHAVE_ISSETUGID
+CFLAGS+= -include ${SRCTOP}/sys/modules/zfs/zfs_config.h
+
+
+LIBADD= nvpair zfs spl zutil zpool
CFLAGS+= -DDEBUG=1
#DEBUG_FLAGS+= -g