diff options
497 files changed, 2733 insertions, 276678 deletions
diff --git a/Makefile.inc1 b/Makefile.inc1 index 851d20ba1f60..6b77bb195bfd 100644 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -2442,7 +2442,7 @@ _btxld= usr.sbin/btxld # Rebuild ctfconvert and ctfmerge to avoid difficult-to-diagnose failures # resulting from missing bug fixes or ELF Toolchain updates. .if ${MK_CDDL} != "no" -_dtrace_tools= cddl/lib/libctf cddl/usr.bin/ctfconvert \ +_dtrace_tools= cddl/lib/libctf cddl/lib/libspl cddl/usr.bin/ctfconvert \ cddl/usr.bin/ctfmerge .endif @@ -2756,7 +2756,12 @@ _prebuild_libs= ${_kerberos5_lib_libasn1} \ ${_cddl_lib_libumem} ${_cddl_lib_libnvpair} \ ${_cddl_lib_libuutil} \ ${_cddl_lib_libavl} \ + ${_cddl_lib_libicp} \ + ${_cddl_lib_libicp_rescue} \ + ${_cddl_lib_libspl} \ + ${_cddl_lib_libtpool} \ ${_cddl_lib_libzfs_core} ${_cddl_lib_libzfs} \ + ${_cddl_lib_libzutil} \ ${_cddl_lib_libctf} \ lib/libufs \ lib/libutil lib/libpjdlog ${_lib_libypclnt} lib/libz lib/msun \ @@ -2826,21 +2831,34 @@ _cddl_lib_libumem= cddl/lib/libumem _cddl_lib_libnvpair= cddl/lib/libnvpair _cddl_lib_libavl= cddl/lib/libavl _cddl_lib_libuutil= cddl/lib/libuutil +_cddl_lib_libspl= cddl/lib/libspl + +cddl/lib/libuutil__L: cddl/lib/libavl__L cddl/lib/libspl__L + .if ${MK_ZFS} != "no" +_cddl_lib_libicp= cddl/lib/libicp +_cddl_lib_libicp_rescue= cddl/lib/libicp_rescue +_cddl_lib_libtpool= cddl/lib/libtpool +_cddl_lib_libzutil= cddl/lib/libzutil _cddl_lib_libzfs_core= cddl/lib/libzfs_core _cddl_lib_libzfs= cddl/lib/libzfs +cddl/lib/libtpool__L: cddl/lib/libspl__L + +cddl/lib/libzutil__L: cddl/lib/libavl__L cddl/lib/libtpool__L + cddl/lib/libzfs_core__L: cddl/lib/libnvpair__L cddl/lib/libzfs__L: cddl/lib/libzfs_core__L lib/msun__L lib/libutil__L cddl/lib/libzfs__L: lib/libthr__L lib/libmd__L lib/libz__L cddl/lib/libumem__L cddl/lib/libzfs__L: cddl/lib/libuutil__L cddl/lib/libavl__L lib/libgeom__L +cddl/lib/libzfs__L: cddl/lib/libnvpair__L cddl/lib/libzutil__L lib/libbe__L: cddl/lib/libzfs__L .endif _cddl_lib_libctf= cddl/lib/libctf _cddl_lib= cddl/lib -cddl/lib/libctf__L: lib/libz__L +cddl/lib/libctf__L: lib/libz__L cddl/lib/libspl__L .endif # cddl/lib/libdtrace requires lib/libproc and lib/librtld_db _prebuild_libs+= lib/libprocstat lib/libproc lib/librtld_db diff --git a/cddl/compat/opensolaris/include/fcntl.h b/cddl/compat/opensolaris/include/fcntl.h index 548918aaab3a..b962bb4855ee 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 227196a4017f..000000000000 --- 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 f2987a028e74..b5366c566857 100644 --- a/cddl/contrib/opensolaris/cmd/lockstat/sym.c +++ b/cddl/contrib/opensolaris/cmd/lockstat/sym.c @@ -54,6 +54,7 @@ #endif #include <sys/cpuvar.h> + typedef struct syment { uintptr_t addr; char *name; @@ -72,6 +73,11 @@ static char maxsymname[64]; #endif #endif +#define __sElfN(x) typedef __CONCAT(__CONCAT(__CONCAT(Elf,__ELF_WORD_SIZE),_),x) x +__sElfN(Sym); +__sElfN(Shdr); +#define elf_getshdr __elfN(getshdr) + static void add_symbol(char *name, uintptr_t addr, size_t size) { diff --git a/cddl/contrib/opensolaris/cmd/zdb/zdb.8 b/cddl/contrib/opensolaris/cmd/zdb/zdb.8 deleted file mode 100644 index e60c56c7c199..000000000000 --- a/cddl/contrib/opensolaris/cmd/zdb/zdb.8 +++ /dev/null @@ -1,414 +0,0 @@ -.\" -.\" This file and its contents are supplied under the terms of the -.\" Common Development and Distribution License ("CDDL"), version 1.0. -.\" You may only use this file in accordance with the terms of version -.\" 1.0 of the CDDL. -.\" -.\" A full copy of the text of the CDDL should have accompanied this -.\" source. A copy of the CDDL is also available via the Internet at -.\" http://www.illumos.org/license/CDDL. -.\" -.\" -.\" Copyright 2012, Richard Lowe. -.\" Copyright (c) 2012, 2018 by Delphix. All rights reserved. -.\" Copyright 2017 Nexenta Systems, Inc. -.\" -.Dd February 25, 2020 -.Dt ZDB 8 -.Os -.Sh NAME -.Nm zdb -.Nd display zpool debugging and consistency information -.Sh SYNOPSIS -.Nm -.Op Fl AbcdDFGhikLMPsvX -.Op Fl e Oo Fl V Oc Op Fl p Ar path ... -.Op Fl I Ar inflight I/Os -.Oo Fl o Ar var Ns = Ns Ar value Oc Ns ... -.Op Fl t Ar txg -.Op Fl U Ar cache -.Op Fl x Ar dumpdir -.Op Ar poolname Op Ar object ... -.Nm -.Op Fl AdiPv -.Op Fl e Oo Fl V Oc Op Fl p Ar path ... -.Op Fl U Ar cache -.Ar dataset Op Ar object ... -.Nm -.Fl C -.Op Fl A -.Op Fl U Ar cache -.Nm -.Fl E -.Op Fl A -.Ar word0 Ns \&: Ns Ar word1 Ns :...: Ns Ar word15 -.Nm -.Fl l -.Op Fl Aqu -.Ar device -.Nm -.Fl m -.Op Fl AFLPX -.Op Fl e Oo Fl V Oc Op Fl p Ar path ... -.Op Fl t Ar txg -.Op Fl U Ar cache -.Ar poolname Op Ar vdev Op Ar metaslab ... -.Nm -.Fl O -.Ar dataset path -.Nm -.Fl R -.Op Fl A -.Op Fl e Oo Fl V Oc Op Fl p Ar path ... -.Op Fl U Ar cache -.Ar poolname vdev Ns \&: Ns Ar offset Ns \&: Ns Ar size Ns Op : Ns Ar flags -.Nm -.Fl S -.Op Fl AP -.Op Fl e Oo Fl V Oc Op Fl p Ar path ... -.Op Fl U Ar cache -.Ar poolname -.Sh DESCRIPTION -The -.Nm -utility displays information about a ZFS pool useful for debugging and performs -some amount of consistency checking. -It is a not a general purpose tool and options -.Pq and facilities -may change. -This is neither a -.Xr fsck 8 -nor an -.Xr fsdb 8 -utility. -.Pp -The output of this command in general reflects the on-disk structure of a ZFS -pool, and is inherently unstable. -The precise output of most invocations is not documented, a knowledge of ZFS -internals is assumed. -.Pp -If the -.Ar dataset -argument does not contain any -.Qq Sy / -or -.Qq Sy @ -characters, it is interpreted as a pool name. -The root dataset can be specified as -.Ar pool Ns / -.Pq pool name followed by a slash . -.Pp -When operating on an imported and active pool it is possible, though unlikely, -that zdb may interpret inconsistent pool data and behave erratically. -.Sh OPTIONS -Display options: -.Bl -tag -width Ds -.It Fl b -Display statistics regarding the number, size -.Pq logical, physical and allocated -and deduplication of blocks. -.It Fl c -Verify the checksum of all metadata blocks while printing block statistics -.Po see -.Fl b -.Pc . -.Pp -If specified multiple times, verify the checksums of all blocks. -.It Fl C -Display information about the configuration. -If specified with no other options, instead display information about the cache -file -.Pq Pa /boot/zfs/zpool.cache . -To specify the cache file to display, see -.Fl U . -.Pp -If specified multiple times, and a pool name is also specified display both the -cached configuration and the on-disk configuration. -If specified multiple times with -.Fl e -also display the configuration that would be used were the pool to be imported. -.It Fl d -Display information about datasets. -Specified once, displays basic dataset information: ID, create transaction, -size, and object count. -.Pp -If specified multiple times provides greater and greater verbosity. -.Pp -If object IDs are specified, display information about those specific objects -only. -.It Fl D -Display deduplication statistics, including the deduplication ratio -.Pq Sy dedup , -compression ratio -.Pq Sy compress , -inflation due to the zfs copies property -.Pq Sy copies , -and an overall effective ratio -.Pq Sy dedup No * Sy compress No / Sy copies . -.It Fl DD -Display a histogram of deduplication statistics, showing the allocated -.Pq physically present on disk -and referenced -.Pq logically referenced in the pool -block counts and sizes by reference count. -.It Fl DDD -Display the statistics independently for each deduplication table. -.It Fl DDDD -Dump the contents of the deduplication tables describing duplicate blocks. -.It Fl DDDDD -Also dump the contents of the deduplication tables describing unique blocks. -.It Fl E Ar word0 Ns \&: Ns Ar word1 Ns :...: Ns Ar word15 -Decode and display block from an embedded block pointer specified by the -.Ar word -arguments. -.It Fl h -Display pool history similar to -.Nm zpool Cm history , -but include internal changes, transaction, and dataset information. -.It Fl i -Display information about intent log -.Pq ZIL -entries relating to each dataset. -If specified multiple times, display counts of each intent log transaction type. -.It Fl k -Examine the checkpointed state of the pool. -Note, the on disk format of the pool is not reverted to the checkpointed state. -.It Fl l Ar device -Read the vdev labels from the specified device. -.Nm Fl l -will return 0 if valid label was found, 1 if error occurred, and 2 if no valid -labels were found. -.Pp -If the -.Fl q -option is also specified, don't print the labels. -.Pp -If the -.Fl u -option is also specified, also display the uberblocks on this device. -.It Fl L -Disable leak detection and the loading of space maps. -By default, -.Nm -verifies that all non-free blocks are referenced, which can be very expensive. -.It Fl m -Display the offset, spacemap, and free space of each metaslab. -.It Fl mm -Also display information about the on-disk free space histogram associated with -each metaslab. -.It Fl mmm -Display the maximum contiguous free space, the in-core free space histogram, and -the percentage of free space in each space map. -.It Fl mmmm -Display every spacemap record. -.It Fl M -Display the offset, spacemap, and free space of each metaslab. -.It Fl MM -Also display information about the maximum contiguous free space and the -percentage of free space in each space map. -.It Fl MMM -Display every spacemap record. -.It Fl O Ar dataset path -Look up the specified -.Ar path -inside of the -.Ar dataset -and display its metadata and indirect blocks. -Specified -.Ar path -must be relative to the root of -.Ar dataset . -This option can be combined with -.Fl v -for increasing verbosity. -.It Xo -.Fl R Ar poolname vdev Ns \&: Ns Ar offset Ns \&: Ns Ar size Ns Op : Ns Ar flags -.Xc -Read and display a block from the specified device. -By default the block is displayed as a hex dump, but see the description of the -.Sy r -flag, below. -.Pp -The block is specified in terms of a colon-separated tuple -.Ar vdev -.Pq an integer vdev identifier -.Ar offset -.Pq the offset within the vdev -.Ar size -.Pq the size of the block to read -and, optionally, -.Ar flags -.Pq a set of flags, described below . -.Pp -.Bl -tag -compact -width "b offset" -.It Sy b Ar offset -Print block pointer -.It Sy d -Decompress the block -.It Sy e -Byte swap the block -.It Sy g -Dump gang block header -.It Sy i -Dump indirect block -.It Sy r -Dump raw uninterpreted block data -.El -.It Fl s -Report statistics on -.Nm zdb -I/O. -Display operation counts, bandwidth, and error counts of I/O to the pool from -.Nm . -.It Fl S -Simulate the effects of deduplication, constructing a DDT and then display -that DDT as with -.Fl DD . -.It Fl u -Display the current uberblock. -.El -.Pp -Other options: -.Bl -tag -width Ds -.It Fl A -Do not abort should any assertion fail. -.It Fl AA -Enable panic recovery, certain errors which would otherwise be fatal are -demoted to warnings. -.It Fl AAA -Do not abort if asserts fail and also enable panic recovery. -.It Fl e Op Fl p Ar path ... -Operate on an exported pool, not present in -.Pa /boot/zfs/zpool.cache . -The -.Fl p -flag specifies the path under which devices are to be searched. -.It Fl x Ar dumpdir -All blocks accessed will be copied to files in the specified directory. -The blocks will be placed in sparse files whose name is the same as -that of the file or device read. -.Nm -can be then run on the generated files. -Note that the -.Fl bbc -flags are sufficient to access -.Pq and thus copy -all metadata on the pool. -.It Fl F -Attempt to make an unreadable pool readable by trying progressively older -transactions. -.It Fl G -Dump the contents of the zfs_dbgmsg buffer before exiting -.Nm . -zfs_dbgmsg is a buffer used by ZFS to dump advanced debug information. -.It Fl I Ar inflight I/Os -Limit the number of outstanding checksum I/Os to the specified value. -The default value is 200. -This option affects the performance of the -.Fl c -option. -.It Fl o Ar var Ns = Ns Ar value ... -Set the given global libzpool variable to the provided value. -The value must be an unsigned 32-bit integer. -Currently only little-endian systems are supported to avoid accidentally setting -the high 32 bits of 64-bit variables. -.It Fl P -Print numbers in an unscaled form more amenable to parsing, eg. 1000000 rather -than 1M. -.It Fl t Ar transaction -Specify the highest transaction to use when searching for uberblocks. -See also the -.Fl u -and -.Fl l -options for a means to see the available uberblocks and their associated -transaction numbers. -.It Fl U Ar cachefile -Use a cache file other than -.Pa /boot/zfs/zpool.cache . -.It Fl v -Enable verbosity. -Specify multiple times for increased verbosity. -.It Fl V -Attempt verbatim import. -This mimics the behavior of the kernel when loading a pool from a cachefile. -Only usable with -.Fl e . -.It Fl X -Attempt -.Qq extreme -transaction rewind, that is attempt the same recovery as -.Fl F -but read transactions otherwise deemed too old. -.El -.Pp -Specifying a display option more than once enables verbosity for only that -option, with more occurrences enabling more verbosity. -.Pp -If no options are specified, all information about the named pool will be -displayed at default verbosity. -.Sh EXAMPLES -.Bl -tag -width Ds -.It Xo -.Sy Example 1 -Display the configuration of imported pool -.Pa rpool -.Xc -.Bd -literal -# zdb -C rpool - -MOS Configuration: - version: 28 - name: 'rpool' - ... -.Ed -.It Xo -.Sy Example 2 -Display basic dataset information about -.Pa rpool -.Xc -.Bd -literal -# zdb -d rpool -Dataset mos [META], ID 0, cr_txg 4, 26.9M, 1051 objects -Dataset rpool/swap [ZVOL], ID 59, cr_txg 356, 486M, 2 objects - ... -.Ed -.It Xo -.Sy Example 3 -Display basic information about object 0 in -.Pa rpool/export/home -.Xc -.Bd -literal -# zdb -d rpool/export/home 0 -Dataset rpool/export/home [ZPL], ID 137, cr_txg 1546, 32K, 8 objects - - Object lvl iblk dblk dsize lsize %full type - 0 7 16K 16K 15.0K 16K 25.00 DMU dnode -.Ed -.It Xo -.Sy Example 4 -Display the predicted effect of enabling deduplication on -.Pa rpool -.Xc -.Bd -literal -# zdb -S rpool -Simulated DDT histogram: - -bucket allocated referenced -______ ______________________________ ______________________________ -refcnt blocks LSIZE PSIZE DSIZE blocks LSIZE PSIZE DSIZE ------- ------ ----- ----- ----- ------ ----- ----- ----- - 1 694K 27.1G 15.0G 15.0G 694K 27.1G 15.0G 15.0G - 2 35.0K 1.33G 699M 699M 74.7K 2.79G 1.45G 1.45G - ... -dedup = 1.11, compress = 1.80, copies = 1.00, dedup * compress / copies = 2.00 -.Ed -.El -.Sh SEE ALSO -.Xr zfs 8 , -.Xr zpool 8 -.Sh HISTORY -The -.Nm -utility first appeared in -.Fx 7.0 . diff --git a/cddl/contrib/opensolaris/cmd/zdb/zdb.c b/cddl/contrib/opensolaris/cmd/zdb/zdb.c deleted file mode 100644 index d51ddc68908c..000000000000 --- a/cddl/contrib/opensolaris/cmd/zdb/zdb.c +++ /dev/null @@ -1,5749 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ - -/* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2018 by Delphix. All rights reserved. - * Copyright (c) 2014 Integros [integros.com] - * Copyright 2017 Nexenta Systems, Inc. - * Copyright (c) 2017, 2018 Lawrence Livermore National Security, LLC. - * Copyright 2017 RackTop Systems. - */ - -#include <stdio.h> -#include <unistd.h> -#include <stdio_ext.h> -#include <stdlib.h> -#include <ctype.h> -#include <sys/zfs_context.h> -#include <sys/spa.h> -#include <sys/spa_impl.h> -#include <sys/dmu.h> -#include <sys/zap.h> -#include <sys/fs/zfs.h> -#include <sys/zfs_znode.h> -#include <sys/zfs_sa.h> -#include <sys/sa.h> -#include <sys/sa_impl.h> -#include <sys/vdev.h> -#include <sys/vdev_impl.h> -#include <sys/metaslab_impl.h> -#include <sys/dmu_objset.h> -#include <sys/dsl_dir.h> -#include <sys/dsl_dataset.h> -#include <sys/dsl_pool.h> -#include <sys/dbuf.h> -#include <sys/zil.h> -#include <sys/zil_impl.h> -#include <sys/stat.h> -#include <sys/resource.h> -#include <sys/dmu_traverse.h> -#include <sys/zio_checksum.h> -#include <sys/zio_compress.h> -#include <sys/zfs_fuid.h> -#include <sys/arc.h> -#include <sys/ddt.h> -#include <sys/zfeature.h> -#include <sys/abd.h> -#include <sys/blkptr.h> -#include <sys/dsl_scan.h> -#include <zfs_comutil.h> -#include <libcmdutils.h> -#undef verify -#include <libzfs.h> - -#include "zdb.h" - -#define ZDB_COMPRESS_NAME(idx) ((idx) < ZIO_COMPRESS_FUNCTIONS ? \ - zio_compress_table[(idx)].ci_name : "UNKNOWN") -#define ZDB_CHECKSUM_NAME(idx) ((idx) < ZIO_CHECKSUM_FUNCTIONS ? \ - zio_checksum_table[(idx)].ci_name : "UNKNOWN") -#define ZDB_OT_NAME(idx) ((idx) < DMU_OT_NUMTYPES ? \ - dmu_ot[(idx)].ot_name : DMU_OT_IS_VALID(idx) ? \ - dmu_ot_byteswap[DMU_OT_BYTESWAP(idx)].ob_name : "UNKNOWN") -#define ZDB_OT_TYPE(idx) ((idx) < DMU_OT_NUMTYPES ? (idx) : \ - (idx) == DMU_OTN_ZAP_DATA || (idx) == DMU_OTN_ZAP_METADATA ? \ - DMU_OT_ZAP_OTHER : \ - (idx) == DMU_OTN_UINT64_DATA || (idx) == DMU_OTN_UINT64_METADATA ? \ - DMU_OT_UINT64_OTHER : DMU_OT_NUMTYPES) - -#ifndef lint -extern int reference_tracking_enable; -extern boolean_t zfs_recover; -extern uint64_t zfs_arc_max, zfs_arc_meta_limit; -extern int zfs_vdev_async_read_max_active; -extern boolean_t spa_load_verify_dryrun; -extern int aok; -#else -int reference_tracking_enable; -boolean_t zfs_recover; -uint64_t zfs_arc_max, zfs_arc_meta_limit; -int zfs_vdev_async_read_max_active; -boolean_t spa_load_verify_dryrun; -int aok; -#endif - -static const char cmdname[] = "zdb"; -uint8_t dump_opt[256]; - -typedef void object_viewer_t(objset_t *, uint64_t, void *data, size_t size); - -static uint64_t *zopt_object = NULL; -static unsigned zopt_objects = 0; -static libzfs_handle_t *g_zfs; -static uint64_t max_inflight = 1000; -static int leaked_objects = 0; - -static void snprintf_blkptr_compact(char *, size_t, const blkptr_t *); -static void mos_obj_refd(uint64_t); - -/* - * These libumem hooks provide a reasonable set of defaults for the allocator's - * debugging facilities. - */ -const char * -_umem_debug_init() -{ - return ("default,verbose"); /* $UMEM_DEBUG setting */ -} - -const char * -_umem_logging_init(void) -{ - return ("fail,contents"); /* $UMEM_LOGGING setting */ -} - -static void -usage(void) -{ - (void) fprintf(stderr, - "Usage:\t%s [-AbcdDFGhikLMPsvX] [-e [-V] [-p <path> ...]] " - "[-I <inflight I/Os>]\n" - "\t\t[-o <var>=<value>]... [-t <txg>] [-U <cache>] [-x <dumpdir>]\n" - "\t\t[<poolname> [<object> ...]]\n" - "\t%s [-AdiPv] [-e [-V] [-p <path> ...]] [-U <cache>] <dataset> " - "[<object> ...]\n" - "\t%s -C [-A] [-U <cache>]\n" - "\t%s -l [-Aqu] <device>\n" - "\t%s -m [-AFLPX] [-e [-V] [-p <path> ...]] [-t <txg>] " - "[-U <cache>]\n\t\t<poolname> [<vdev> [<metaslab> ...]]\n" - "\t%s -O <dataset> <path>\n" - "\t%s -R [-A] [-e [-V] [-p <path> ...]] [-U <cache>]\n" - "\t\t<poolname> <vdev>:<offset>:<size>[:<flags>]\n" - "\t%s -E [-A] word0:word1:...:word15\n" - "\t%s -S [-AP] [-e [-V] [-p <path> ...]] [-U <cache>] " - "<poolname>\n\n", - cmdname, cmdname, cmdname, cmdname, cmdname, cmdname, cmdname, - cmdname, cmdname); - - (void) fprintf(stderr, " Dataset name must include at least one " - "separator character '/' or '@'\n"); - (void) fprintf(stderr, " If dataset name is specified, only that " - "dataset is dumped\n"); - (void) fprintf(stderr, " If object numbers are specified, only " - "those objects are dumped\n\n"); - (void) fprintf(stderr, " Options to control amount of output:\n"); - (void) fprintf(stderr, " -b block statistics\n"); - (void) fprintf(stderr, " -c checksum all metadata (twice for " - "all data) blocks\n"); - (void) fprintf(stderr, " -C config (or cachefile if alone)\n"); - (void) fprintf(stderr, " -d dataset(s)\n"); - (void) fprintf(stderr, " -D dedup statistics\n"); - (void) fprintf(stderr, " -E decode and display block from an " - "embedded block pointer\n"); - (void) fprintf(stderr, " -h pool history\n"); - (void) fprintf(stderr, " -i intent logs\n"); - (void) fprintf(stderr, " -l read label contents\n"); - (void) fprintf(stderr, " -k examine the checkpointed state " - "of the pool\n"); - (void) fprintf(stderr, " -L disable leak tracking (do not " - "load spacemaps)\n"); - (void) fprintf(stderr, " -m metaslabs\n"); - (void) fprintf(stderr, " -M metaslab groups\n"); - (void) fprintf(stderr, " -O perform object lookups by path\n"); - (void) fprintf(stderr, " -R read and display block from a " - "device\n"); - (void) fprintf(stderr, " -s report stats on zdb's I/O\n"); - (void) fprintf(stderr, " -S simulate dedup to measure effect\n"); - (void) fprintf(stderr, " -v verbose (applies to all " - "others)\n\n"); - (void) fprintf(stderr, " Below options are intended for use " - "with other options:\n"); - (void) fprintf(stderr, " -A ignore assertions (-A), enable " - "panic recovery (-AA) or both (-AAA)\n"); - (void) fprintf(stderr, " -e pool is exported/destroyed/" - "has altroot/not in a cachefile\n"); - (void) fprintf(stderr, " -F attempt automatic rewind within " - "safe range of transaction groups\n"); - (void) fprintf(stderr, " -G dump zfs_dbgmsg buffer before " - "exiting\n"); - (void) fprintf(stderr, " -I <number of inflight I/Os> -- " - "specify the maximum number of " - "checksumming I/Os [default is 200]\n"); - (void) fprintf(stderr, " -o <variable>=<value> set global " - "variable to an unsigned 32-bit integer value\n"); - (void) fprintf(stderr, " -p <path> -- use one or more with " - "-e to specify path to vdev dir\n"); - (void) fprintf(stderr, " -P print numbers in parseable form\n"); - (void) fprintf(stderr, " -q don't print label contents\n"); - (void) fprintf(stderr, " -t <txg> -- highest txg to use when " - "searching for uberblocks\n"); - (void) fprintf(stderr, " -u uberblock\n"); - (void) fprintf(stderr, " -U <cachefile_path> -- use alternate " - "cachefile\n"); - (void) fprintf(stderr, " -V do verbatim import\n"); - (void) fprintf(stderr, " -x <dumpdir> -- " - "dump all read blocks into specified directory\n"); - (void) fprintf(stderr, " -X attempt extreme rewind (does not " - "work with dataset)\n\n"); - (void) fprintf(stderr, "Specify an option more than once (e.g. -bb) " - "to make only that option verbose\n"); - (void) fprintf(stderr, "Default is to dump everything non-verbosely\n"); - exit(1); -} - -static void -dump_debug_buffer() -{ - if (dump_opt['G']) { - (void) printf("\n"); - zfs_dbgmsg_print("zdb"); - } -} - -/* - * Called for usage errors that are discovered after a call to spa_open(), - * dmu_bonus_hold(), or pool_match(). abort() is called for other errors. - */ - -static void -fatal(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - (void) fprintf(stderr, "%s: ", cmdname); - (void) vfprintf(stderr, fmt, ap); - va_end(ap); - (void) fprintf(stderr, "\n"); - - dump_debug_buffer(); - - exit(1); -} - -/* ARGSUSED */ -static void -dump_packed_nvlist(objset_t *os, uint64_t object, void *data, size_t size) -{ - nvlist_t *nv; - size_t nvsize = *(uint64_t *)data; - char *packed = umem_alloc(nvsize, UMEM_NOFAIL); - - VERIFY(0 == dmu_read(os, object, 0, nvsize, packed, DMU_READ_PREFETCH)); - - VERIFY(nvlist_unpack(packed, nvsize, &nv, 0) == 0); - - umem_free(packed, nvsize); - - dump_nvlist(nv, 8); - - nvlist_free(nv); -} - -/* ARGSUSED */ -static void -dump_history_offsets(objset_t *os, uint64_t object, void *data, size_t size) -{ - spa_history_phys_t *shp = data; - - if (shp == NULL) - return; - - (void) printf("\t\tpool_create_len = %llu\n", - (u_longlong_t)shp->sh_pool_create_len); - (void) printf("\t\tphys_max_off = %llu\n", - (u_longlong_t)shp->sh_phys_max_off); - (void) printf("\t\tbof = %llu\n", - (u_longlong_t)shp->sh_bof); - (void) printf("\t\teof = %llu\n", - (u_longlong_t)shp->sh_eof); - (void) printf("\t\trecords_lost = %llu\n", - (u_longlong_t)shp->sh_records_lost); -} - -static void -zdb_nicenum(uint64_t num, char *buf, size_t buflen) -{ - if (dump_opt['P']) - (void) snprintf(buf, buflen, "%llu", (longlong_t)num); - else - nicenum(num, buf, sizeof (buf)); -} - -static const char histo_stars[] = "****************************************"; -static const uint64_t histo_width = sizeof (histo_stars) - 1; - -static void -dump_histogram(const uint64_t *histo, int size, int offset) -{ - int i; - int minidx = size - 1; - int maxidx = 0; - uint64_t max = 0; - - for (i = 0; i < size; i++) { - if (histo[i] > max) - max = histo[i]; - if (histo[i] > 0 && i > maxidx) - maxidx = i; - if (histo[i] > 0 && i < minidx) - minidx = i; - } - - if (max < histo_width) - max = histo_width; - - for (i = minidx; i <= maxidx; i++) { - (void) printf("\t\t\t%3u: %6llu %s\n", - i + offset, (u_longlong_t)histo[i], - &histo_stars[(max - histo[i]) * histo_width / max]); - } -} - -static void -dump_zap_stats(objset_t *os, uint64_t object) -{ - int error; - zap_stats_t zs; - - error = zap_get_stats(os, object, &zs); - if (error) - return; - - if (zs.zs_ptrtbl_len == 0) { - ASSERT(zs.zs_num_blocks == 1); - (void) printf("\tmicrozap: %llu bytes, %llu entries\n", - (u_longlong_t)zs.zs_blocksize, - (u_longlong_t)zs.zs_num_entries); - return; - } - - (void) printf("\tFat ZAP stats:\n"); - - (void) printf("\t\tPointer table:\n"); - (void) printf("\t\t\t%llu elements\n", - (u_longlong_t)zs.zs_ptrtbl_len); - (void) printf("\t\t\tzt_blk: %llu\n", - (u_longlong_t)zs.zs_ptrtbl_zt_blk); - (void) printf("\t\t\tzt_numblks: %llu\n", - (u_longlong_t)zs.zs_ptrtbl_zt_numblks); - (void) printf("\t\t\tzt_shift: %llu\n", - (u_longlong_t)zs.zs_ptrtbl_zt_shift); - (void) printf("\t\t\tzt_blks_copied: %llu\n", - (u_longlong_t)zs.zs_ptrtbl_blks_copied); - (void) printf("\t\t\tzt_nextblk: %llu\n", - (u_longlong_t)zs.zs_ptrtbl_nextblk); - - (void) printf("\t\tZAP entries: %llu\n", - (u_longlong_t)zs.zs_num_entries); - (void) printf("\t\tLeaf blocks: %llu\n", - (u_longlong_t)zs.zs_num_leafs); - (void) printf("\t\tTotal blocks: %llu\n", - (u_longlong_t)zs.zs_num_blocks); - (void) printf("\t\tzap_block_type: 0x%llx\n", - (u_longlong_t)zs.zs_block_type); - (void) printf("\t\tzap_magic: 0x%llx\n", - (u_longlong_t)zs.zs_magic); - (void) printf("\t\tzap_salt: 0x%llx\n", - (u_longlong_t)zs.zs_salt); - - (void) printf("\t\tLeafs with 2^n pointers:\n"); - dump_histogram(zs.zs_leafs_with_2n_pointers, ZAP_HISTOGRAM_SIZE, 0); - - (void) printf("\t\tBlocks with n*5 entries:\n"); - dump_histogram(zs.zs_blocks_with_n5_entries, ZAP_HISTOGRAM_SIZE, 0); - - (void) printf("\t\tBlocks n/10 full:\n"); - dump_histogram(zs.zs_blocks_n_tenths_full, ZAP_HISTOGRAM_SIZE, 0); - - (void) printf("\t\tEntries with n chunks:\n"); - dump_histogram(zs.zs_entries_using_n_chunks, ZAP_HISTOGRAM_SIZE, 0); - - (void) printf("\t\tBuckets with n entries:\n"); - dump_histogram(zs.zs_buckets_with_n_entries, ZAP_HISTOGRAM_SIZE, 0); -} - -/*ARGSUSED*/ -static void -dump_none(objset_t *os, uint64_t object, void *data, size_t size) -{ -} - -/*ARGSUSED*/ -static void -dump_unknown(objset_t *os, uint64_t object, void *data, size_t size) -{ - (void) printf("\tUNKNOWN OBJECT TYPE\n"); -} - -/*ARGSUSED*/ -static void -dump_uint8(objset_t *os, uint64_t object, void *data, size_t size) -{ -} - -/*ARGSUSED*/ -static void -dump_uint64(objset_t *os, uint64_t object, void *data, size_t size) -{ -} - -/*ARGSUSED*/ -static void -dump_zap(objset_t *os, uint64_t object, void *data, size_t size) -{ - zap_cursor_t zc; - zap_attribute_t attr; - void *prop; - unsigned i; - - dump_zap_stats(os, object); - (void) printf("\n"); - - for (zap_cursor_init(&zc, os, object); - zap_cursor_retrieve(&zc, &attr) == 0; - zap_cursor_advance(&zc)) { - (void) printf("\t\t%s = ", attr.za_name); - if (attr.za_num_integers == 0) { - (void) printf("\n"); - continue; - } - prop = umem_zalloc(attr.za_num_integers * - attr.za_integer_length, UMEM_NOFAIL); - (void) zap_lookup(os, object, attr.za_name, - attr.za_integer_length, attr.za_num_integers, prop); - if (attr.za_integer_length == 1) { - (void) printf("%s", (char *)prop); - } else { - for (i = 0; i < attr.za_num_integers; i++) { - switch (attr.za_integer_length) { - case 2: - (void) printf("%u ", - ((uint16_t *)prop)[i]); - break; - case 4: - (void) printf("%u ", - ((uint32_t *)prop)[i]); - break; - case 8: - (void) printf("%lld ", - (u_longlong_t)((int64_t *)prop)[i]); - break; - } - } - } - (void) printf("\n"); - umem_free(prop, attr.za_num_integers * attr.za_integer_length); - } - zap_cursor_fini(&zc); -} - -static void -dump_bpobj(objset_t *os, uint64_t object, void *data, size_t size) -{ - bpobj_phys_t *bpop = data; - char bytes[32], comp[32], uncomp[32]; - - /* make sure the output won't get truncated */ - CTASSERT(sizeof (bytes) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (comp) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (uncomp) >= NN_NUMBUF_SZ); - - if (bpop == NULL) - return; - - zdb_nicenum(bpop->bpo_bytes, bytes, sizeof (bytes)); - zdb_nicenum(bpop->bpo_comp, comp, sizeof (comp)); - zdb_nicenum(bpop->bpo_uncomp, uncomp, sizeof (uncomp)); - - (void) printf("\t\tnum_blkptrs = %llu\n", - (u_longlong_t)bpop->bpo_num_blkptrs); - (void) printf("\t\tbytes = %s\n", bytes); - if (size >= BPOBJ_SIZE_V1) { - (void) printf("\t\tcomp = %s\n", comp); - (void) printf("\t\tuncomp = %s\n", uncomp); - } - if (size >= sizeof (*bpop)) { - (void) printf("\t\tsubobjs = %llu\n", - (u_longlong_t)bpop->bpo_subobjs); - (void) printf("\t\tnum_subobjs = %llu\n", - (u_longlong_t)bpop->bpo_num_subobjs); - } - - if (dump_opt['d'] < 5) - return; - - for (uint64_t i = 0; i < bpop->bpo_num_blkptrs; i++) { - char blkbuf[BP_SPRINTF_LEN]; - blkptr_t bp; - - int err = dmu_read(os, object, - i * sizeof (bp), sizeof (bp), &bp, 0); - if (err != 0) { - (void) printf("got error %u from dmu_read\n", err); - break; - } - snprintf_blkptr_compact(blkbuf, sizeof (blkbuf), &bp); - (void) printf("\t%s\n", blkbuf); - } -} - -/* ARGSUSED */ -static void -dump_bpobj_subobjs(objset_t *os, uint64_t object, void *data, size_t size) -{ - dmu_object_info_t doi; - - VERIFY0(dmu_object_info(os, object, &doi)); - uint64_t *subobjs = kmem_alloc(doi.doi_max_offset, KM_SLEEP); - - int err = dmu_read(os, object, 0, doi.doi_max_offset, subobjs, 0); - if (err != 0) { - (void) printf("got error %u from dmu_read\n", err); - kmem_free(subobjs, doi.doi_max_offset); - return; - } - - int64_t last_nonzero = -1; - for (uint64_t i = 0; i < doi.doi_max_offset / 8; i++) { - if (subobjs[i] != 0) - last_nonzero = i; - } - - for (int64_t i = 0; i <= last_nonzero; i++) { - (void) printf("\t%llu\n", (longlong_t)subobjs[i]); - } - kmem_free(subobjs, doi.doi_max_offset); -} - -/*ARGSUSED*/ -static void -dump_ddt_zap(objset_t *os, uint64_t object, void *data, size_t size) -{ - dump_zap_stats(os, object); - /* contents are printed elsewhere, properly decoded */ -} - -/*ARGSUSED*/ -static void -dump_sa_attrs(objset_t *os, uint64_t object, void *data, size_t size) -{ - zap_cursor_t zc; - zap_attribute_t attr; - - dump_zap_stats(os, object); - (void) printf("\n"); - - for (zap_cursor_init(&zc, os, object); - zap_cursor_retrieve(&zc, &attr) == 0; - zap_cursor_advance(&zc)) { - (void) printf("\t\t%s = ", attr.za_name); - if (attr.za_num_integers == 0) { - (void) printf("\n"); - continue; - } - (void) printf(" %llx : [%d:%d:%d]\n", - (u_longlong_t)attr.za_first_integer, - (int)ATTR_LENGTH(attr.za_first_integer), - (int)ATTR_BSWAP(attr.za_first_integer), - (int)ATTR_NUM(attr.za_first_integer)); - } - zap_cursor_fini(&zc); -} - -/*ARGSUSED*/ -static void -dump_sa_layouts(objset_t *os, uint64_t object, void *data, size_t size) -{ - zap_cursor_t zc; - zap_attribute_t attr; - uint16_t *layout_attrs; - unsigned i; - - dump_zap_stats(os, object); - (void) printf("\n"); - - for (zap_cursor_init(&zc, os, object); - zap_cursor_retrieve(&zc, &attr) == 0; - zap_cursor_advance(&zc)) { - (void) printf("\t\t%s = [", attr.za_name); - if (attr.za_num_integers == 0) { - (void) printf("\n"); - continue; - } - - VERIFY(attr.za_integer_length == 2); - layout_attrs = umem_zalloc(attr.za_num_integers * - attr.za_integer_length, UMEM_NOFAIL); - - VERIFY(zap_lookup(os, object, attr.za_name, - attr.za_integer_length, - attr.za_num_integers, layout_attrs) == 0); - - for (i = 0; i != attr.za_num_integers; i++) - (void) printf(" %d ", (int)layout_attrs[i]); - (void) printf("]\n"); - umem_free(layout_attrs, - attr.za_num_integers * attr.za_integer_length); - } - zap_cursor_fini(&zc); -} - -/*ARGSUSED*/ -static void -dump_zpldir(objset_t *os, uint64_t object, void *data, size_t size) -{ - zap_cursor_t zc; - zap_attribute_t attr; - const char *typenames[] = { - /* 0 */ "not specified", - /* 1 */ "FIFO", - /* 2 */ "Character Device", - /* 3 */ "3 (invalid)", - /* 4 */ "Directory", - /* 5 */ "5 (invalid)", - /* 6 */ "Block Device", - /* 7 */ "7 (invalid)", - /* 8 */ "Regular File", - /* 9 */ "9 (invalid)", - /* 10 */ "Symbolic Link", - /* 11 */ "11 (invalid)", - /* 12 */ "Socket", - /* 13 */ "Door", - /* 14 */ "Event Port", - /* 15 */ "15 (invalid)", - }; - - dump_zap_stats(os, object); - (void) printf("\n"); - - for (zap_cursor_init(&zc, os, object); - zap_cursor_retrieve(&zc, &attr) == 0; - zap_cursor_advance(&zc)) { - (void) printf("\t\t%s = %lld (type: %s)\n", - attr.za_name, ZFS_DIRENT_OBJ(attr.za_first_integer), - typenames[ZFS_DIRENT_TYPE(attr.za_first_integer)]); - } - zap_cursor_fini(&zc); -} - -static int -get_dtl_refcount(vdev_t *vd) -{ - int refcount = 0; - - if (vd->vdev_ops->vdev_op_leaf) { - space_map_t *sm = vd->vdev_dtl_sm; - - if (sm != NULL && - sm->sm_dbuf->db_size == sizeof (space_map_phys_t)) - return (1); - return (0); - } - - for (unsigned c = 0; c < vd->vdev_children; c++) - refcount += get_dtl_refcount(vd->vdev_child[c]); - return (refcount); -} - -static int -get_metaslab_refcount(vdev_t *vd) -{ - int refcount = 0; - - if (vd->vdev_top == vd) { - for (uint64_t m = 0; m < vd->vdev_ms_count; m++) { - space_map_t *sm = vd->vdev_ms[m]->ms_sm; - - if (sm != NULL && - sm->sm_dbuf->db_size == sizeof (space_map_phys_t)) - refcount++; - } - } - for (unsigned c = 0; c < vd->vdev_children; c++) - refcount += get_metaslab_refcount(vd->vdev_child[c]); - - return (refcount); -} - -static int -get_obsolete_refcount(vdev_t *vd) -{ - int refcount = 0; - - uint64_t obsolete_sm_obj = vdev_obsolete_sm_object(vd); - if (vd->vdev_top == vd && obsolete_sm_obj != 0) { - dmu_object_info_t doi; - VERIFY0(dmu_object_info(vd->vdev_spa->spa_meta_objset, - obsolete_sm_obj, &doi)); - if (doi.doi_bonus_size == sizeof (space_map_phys_t)) { - refcount++; - } - } else { - ASSERT3P(vd->vdev_obsolete_sm, ==, NULL); - ASSERT3U(obsolete_sm_obj, ==, 0); - } - for (unsigned c = 0; c < vd->vdev_children; c++) { - refcount += get_obsolete_refcount(vd->vdev_child[c]); - } - - return (refcount); -} - -static int -get_prev_obsolete_spacemap_refcount(spa_t *spa) -{ - uint64_t prev_obj = - spa->spa_condensing_indirect_phys.scip_prev_obsolete_sm_object; - if (prev_obj != 0) { - dmu_object_info_t doi; - VERIFY0(dmu_object_info(spa->spa_meta_objset, prev_obj, &doi)); - if (doi.doi_bonus_size == sizeof (space_map_phys_t)) { - return (1); - } - } - return (0); -} - -static int -get_checkpoint_refcount(vdev_t *vd) -{ - int refcount = 0; - - if (vd->vdev_top == vd && vd->vdev_top_zap != 0 && - zap_contains(spa_meta_objset(vd->vdev_spa), - vd->vdev_top_zap, VDEV_TOP_ZAP_POOL_CHECKPOINT_SM) == 0) - refcount++; - - for (uint64_t c = 0; c < vd->vdev_children; c++) - refcount += get_checkpoint_refcount(vd->vdev_child[c]); - - return (refcount); -} - -static int -verify_spacemap_refcounts(spa_t *spa) -{ - uint64_t expected_refcount = 0; - uint64_t actual_refcount; - - (void) feature_get_refcount(spa, - &spa_feature_table[SPA_FEATURE_SPACEMAP_HISTOGRAM], - &expected_refcount); - actual_refcount = get_dtl_refcount(spa->spa_root_vdev); - actual_refcount += get_metaslab_refcount(spa->spa_root_vdev); - actual_refcount += get_obsolete_refcount(spa->spa_root_vdev); - actual_refcount += get_prev_obsolete_spacemap_refcount(spa); - actual_refcount += get_checkpoint_refcount(spa->spa_root_vdev); - - if (expected_refcount != actual_refcount) { - (void) printf("space map refcount mismatch: expected %lld != " - "actual %lld\n", - (longlong_t)expected_refcount, - (longlong_t)actual_refcount); - return (2); - } - return (0); -} - -static void -dump_spacemap(objset_t *os, space_map_t *sm) -{ - char *ddata[] = { "ALLOC", "FREE", "CONDENSE", "INVALID", - "INVALID", "INVALID", "INVALID", "INVALID" }; - - if (sm == NULL) - return; - - (void) printf("space map object %llu:\n", - (longlong_t)sm->sm_object); - (void) printf(" smp_length = 0x%llx\n", - (longlong_t)sm->sm_phys->smp_length); - (void) printf(" smp_alloc = 0x%llx\n", - (longlong_t)sm->sm_phys->smp_alloc); - - if (dump_opt['d'] < 6 && dump_opt['m'] < 4) - return; - - /* - * Print out the freelist entries in both encoded and decoded form. - */ - uint8_t mapshift = sm->sm_shift; - int64_t alloc = 0; - uint64_t word, entry_id = 0; - for (uint64_t offset = 0; offset < space_map_length(sm); - offset += sizeof (word)) { - - VERIFY0(dmu_read(os, space_map_object(sm), offset, - sizeof (word), &word, DMU_READ_PREFETCH)); - - if (sm_entry_is_debug(word)) { - (void) printf("\t [%6llu] %s: txg %llu pass %llu\n", - (u_longlong_t)entry_id, - ddata[SM_DEBUG_ACTION_DECODE(word)], - (u_longlong_t)SM_DEBUG_TXG_DECODE(word), - (u_longlong_t)SM_DEBUG_SYNCPASS_DECODE(word)); - entry_id++; - continue; - } - - uint8_t words; - char entry_type; - uint64_t entry_off, entry_run, entry_vdev = SM_NO_VDEVID; - - if (sm_entry_is_single_word(word)) { - entry_type = (SM_TYPE_DECODE(word) == SM_ALLOC) ? - 'A' : 'F'; - entry_off = (SM_OFFSET_DECODE(word) << mapshift) + - sm->sm_start; - entry_run = SM_RUN_DECODE(word) << mapshift; - words = 1; - } else { - /* it is a two-word entry so we read another word */ - ASSERT(sm_entry_is_double_word(word)); - - uint64_t extra_word; - offset += sizeof (extra_word); - VERIFY0(dmu_read(os, space_map_object(sm), offset, - sizeof (extra_word), &extra_word, - DMU_READ_PREFETCH)); - - ASSERT3U(offset, <=, space_map_length(sm)); - - entry_run = SM2_RUN_DECODE(word) << mapshift; - entry_vdev = SM2_VDEV_DECODE(word); - entry_type = (SM2_TYPE_DECODE(extra_word) == SM_ALLOC) ? - 'A' : 'F'; - entry_off = (SM2_OFFSET_DECODE(extra_word) << - mapshift) + sm->sm_start; - words = 2; - } - - (void) printf("\t [%6llu] %c range:" - " %010llx-%010llx size: %06llx vdev: %06llu words: %u\n", - (u_longlong_t)entry_id, - entry_type, (u_longlong_t)entry_off, - (u_longlong_t)(entry_off + entry_run), - (u_longlong_t)entry_run, - (u_longlong_t)entry_vdev, words); - - if (entry_type == 'A') - alloc += entry_run; - else - alloc -= entry_run; - entry_id++; - } - if (alloc != space_map_allocated(sm)) { - (void) printf("space_map_object alloc (%lld) INCONSISTENT " - "with space map summary (%lld)\n", - (longlong_t)space_map_allocated(sm), (longlong_t)alloc); - } -} - -static void -dump_metaslab_stats(metaslab_t *msp) -{ - char maxbuf[32]; - range_tree_t *rt = msp->ms_allocatable; - avl_tree_t *t = &msp->ms_allocatable_by_size; - int free_pct = range_tree_space(rt) * 100 / msp->ms_size; - - /* max sure nicenum has enough space */ - CTASSERT(sizeof (maxbuf) >= NN_NUMBUF_SZ); - - zdb_nicenum(metaslab_block_maxsize(msp), maxbuf, sizeof (maxbuf)); - - (void) printf("\t %25s %10lu %7s %6s %4s %4d%%\n", - "segments", avl_numnodes(t), "maxsize", maxbuf, - "freepct", free_pct); - (void) printf("\tIn-memory histogram:\n"); - dump_histogram(rt->rt_histogram, RANGE_TREE_HISTOGRAM_SIZE, 0); -} - -static void -dump_metaslab(metaslab_t *msp) -{ - vdev_t *vd = msp->ms_group->mg_vd; - spa_t *spa = vd->vdev_spa; - space_map_t *sm = msp->ms_sm; - char freebuf[32]; - - zdb_nicenum(msp->ms_size - space_map_allocated(sm), freebuf, - sizeof (freebuf)); - - (void) printf( - "\tmetaslab %6llu offset %12llx spacemap %6llu free %5s\n", - (u_longlong_t)msp->ms_id, (u_longlong_t)msp->ms_start, - (u_longlong_t)space_map_object(sm), freebuf); - - if (dump_opt['m'] > 2 && !dump_opt['L']) { - mutex_enter(&msp->ms_lock); - VERIFY0(metaslab_load(msp)); - range_tree_stat_verify(msp->ms_allocatable); - dump_metaslab_stats(msp); - metaslab_unload(msp); - mutex_exit(&msp->ms_lock); - } - - if (dump_opt['m'] > 1 && sm != NULL && - spa_feature_is_active(spa, SPA_FEATURE_SPACEMAP_HISTOGRAM)) { - /* - * The space map histogram represents free space in chunks - * of sm_shift (i.e. bucket 0 refers to 2^sm_shift). - */ - (void) printf("\tOn-disk histogram:\t\tfragmentation %llu\n", - (u_longlong_t)msp->ms_fragmentation); - dump_histogram(sm->sm_phys->smp_histogram, - SPACE_MAP_HISTOGRAM_SIZE, sm->sm_shift); - } - - ASSERT(msp->ms_size == (1ULL << vd->vdev_ms_shift)); - dump_spacemap(spa->spa_meta_objset, msp->ms_sm); -} - -static void -print_vdev_metaslab_header(vdev_t *vd) -{ - vdev_alloc_bias_t alloc_bias = vd->vdev_alloc_bias; - const char *bias_str; - - bias_str = (alloc_bias == VDEV_BIAS_LOG || vd->vdev_islog) ? - VDEV_ALLOC_BIAS_LOG : - (alloc_bias == VDEV_BIAS_SPECIAL) ? VDEV_ALLOC_BIAS_SPECIAL : - (alloc_bias == VDEV_BIAS_DEDUP) ? VDEV_ALLOC_BIAS_DEDUP : - vd->vdev_islog ? "log" : ""; - - (void) printf("\tvdev %10llu %s\n" - "\t%-10s%5llu %-19s %-15s %-12s\n", - (u_longlong_t)vd->vdev_id, bias_str, - "metaslabs", (u_longlong_t)vd->vdev_ms_count, - "offset", "spacemap", "free"); - (void) printf("\t%15s %19s %15s %12s\n", - "---------------", "-------------------", - "---------------", "------------"); -} - -static void -dump_metaslab_groups(spa_t *spa) -{ - vdev_t *rvd = spa->spa_root_vdev; - metaslab_class_t *mc = spa_normal_class(spa); - uint64_t fragmentation; - - metaslab_class_histogram_verify(mc); - - for (unsigned c = 0; c < rvd->vdev_children; c++) { - vdev_t *tvd = rvd->vdev_child[c]; - metaslab_group_t *mg = tvd->vdev_mg; - - if (mg == NULL || mg->mg_class != mc) - continue; - - metaslab_group_histogram_verify(mg); - mg->mg_fragmentation = metaslab_group_fragmentation(mg); - - (void) printf("\tvdev %10llu\t\tmetaslabs%5llu\t\t" - "fragmentation", - (u_longlong_t)tvd->vdev_id, - (u_longlong_t)tvd->vdev_ms_count); - if (mg->mg_fragmentation == ZFS_FRAG_INVALID) { - (void) printf("%3s\n", "-"); - } else { - (void) printf("%3llu%%\n", - (u_longlong_t)mg->mg_fragmentation); - } - dump_histogram(mg->mg_histogram, RANGE_TREE_HISTOGRAM_SIZE, 0); - } - - (void) printf("\tpool %s\tfragmentation", spa_name(spa)); - fragmentation = metaslab_class_fragmentation(mc); - if (fragmentation == ZFS_FRAG_INVALID) - (void) printf("\t%3s\n", "-"); - else - (void) printf("\t%3llu%%\n", (u_longlong_t)fragmentation); - dump_histogram(mc->mc_histogram, RANGE_TREE_HISTOGRAM_SIZE, 0); -} - -static void -print_vdev_indirect(vdev_t *vd) -{ - vdev_indirect_config_t *vic = &vd->vdev_indirect_config; - vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping; - vdev_indirect_births_t *vib = vd->vdev_indirect_births; - - if (vim == NULL) { - ASSERT3P(vib, ==, NULL); - return; - } - - ASSERT3U(vdev_indirect_mapping_object(vim), ==, - vic->vic_mapping_object); - ASSERT3U(vdev_indirect_births_object(vib), ==, - vic->vic_births_object); - - (void) printf("indirect births obj %llu:\n", - (longlong_t)vic->vic_births_object); - (void) printf(" vib_count = %llu\n", - (longlong_t)vdev_indirect_births_count(vib)); - for (uint64_t i = 0; i < vdev_indirect_births_count(vib); i++) { - vdev_indirect_birth_entry_phys_t *cur_vibe = - &vib->vib_entries[i]; - (void) printf("\toffset %llx -> txg %llu\n", - (longlong_t)cur_vibe->vibe_offset, - (longlong_t)cur_vibe->vibe_phys_birth_txg); - } - (void) printf("\n"); - - (void) printf("indirect mapping obj %llu:\n", - (longlong_t)vic->vic_mapping_object); - (void) printf(" vim_max_offset = 0x%llx\n", - (longlong_t)vdev_indirect_mapping_max_offset(vim)); - (void) printf(" vim_bytes_mapped = 0x%llx\n", - (longlong_t)vdev_indirect_mapping_bytes_mapped(vim)); - (void) printf(" vim_count = %llu\n", - (longlong_t)vdev_indirect_mapping_num_entries(vim)); - - if (dump_opt['d'] <= 5 && dump_opt['m'] <= 3) - return; - - uint32_t *counts = vdev_indirect_mapping_load_obsolete_counts(vim); - - for (uint64_t i = 0; i < vdev_indirect_mapping_num_entries(vim); i++) { - vdev_indirect_mapping_entry_phys_t *vimep = - &vim->vim_entries[i]; - (void) printf("\t<%llx:%llx:%llx> -> " - "<%llx:%llx:%llx> (%x obsolete)\n", - (longlong_t)vd->vdev_id, - (longlong_t)DVA_MAPPING_GET_SRC_OFFSET(vimep), - (longlong_t)DVA_GET_ASIZE(&vimep->vimep_dst), - (longlong_t)DVA_GET_VDEV(&vimep->vimep_dst), - (longlong_t)DVA_GET_OFFSET(&vimep->vimep_dst), - (longlong_t)DVA_GET_ASIZE(&vimep->vimep_dst), - counts[i]); - } - (void) printf("\n"); - - uint64_t obsolete_sm_object = vdev_obsolete_sm_object(vd); - if (obsolete_sm_object != 0) { - objset_t *mos = vd->vdev_spa->spa_meta_objset; - (void) printf("obsolete space map object %llu:\n", - (u_longlong_t)obsolete_sm_object); - ASSERT(vd->vdev_obsolete_sm != NULL); - ASSERT3U(space_map_object(vd->vdev_obsolete_sm), ==, - obsolete_sm_object); - dump_spacemap(mos, vd->vdev_obsolete_sm); - (void) printf("\n"); - } -} - -static void -dump_metaslabs(spa_t *spa) -{ - vdev_t *vd, *rvd = spa->spa_root_vdev; - uint64_t m, c = 0, children = rvd->vdev_children; - - (void) printf("\nMetaslabs:\n"); - - if (!dump_opt['d'] && zopt_objects > 0) { - c = zopt_object[0]; - - if (c >= children) - (void) fatal("bad vdev id: %llu", (u_longlong_t)c); - - if (zopt_objects > 1) { - vd = rvd->vdev_child[c]; - print_vdev_metaslab_header(vd); - - for (m = 1; m < zopt_objects; m++) { - if (zopt_object[m] < vd->vdev_ms_count) - dump_metaslab( - vd->vdev_ms[zopt_object[m]]); - else - (void) fprintf(stderr, "bad metaslab " - "number %llu\n", - (u_longlong_t)zopt_object[m]); - } - (void) printf("\n"); - return; - } - children = c + 1; - } - for (; c < children; c++) { - vd = rvd->vdev_child[c]; - print_vdev_metaslab_header(vd); - - print_vdev_indirect(vd); - - for (m = 0; m < vd->vdev_ms_count; m++) - dump_metaslab(vd->vdev_ms[m]); - (void) printf("\n"); - } -} - -static void -dump_dde(const ddt_t *ddt, const ddt_entry_t *dde, uint64_t index) -{ - const ddt_phys_t *ddp = dde->dde_phys; - const ddt_key_t *ddk = &dde->dde_key; - const char *types[4] = { "ditto", "single", "double", "triple" }; - char blkbuf[BP_SPRINTF_LEN]; - blkptr_t blk; - - for (int p = 0; p < DDT_PHYS_TYPES; p++, ddp++) { - if (ddp->ddp_phys_birth == 0) - continue; - ddt_bp_create(ddt->ddt_checksum, ddk, ddp, &blk); - snprintf_blkptr(blkbuf, sizeof (blkbuf), &blk); - (void) printf("index %llx refcnt %llu %s %s\n", - (u_longlong_t)index, (u_longlong_t)ddp->ddp_refcnt, - types[p], blkbuf); - } -} - -static void -dump_dedup_ratio(const ddt_stat_t *dds) -{ - double rL, rP, rD, D, dedup, compress, copies; - - if (dds->dds_blocks == 0) - return; - - rL = (double)dds->dds_ref_lsize; - rP = (double)dds->dds_ref_psize; - rD = (double)dds->dds_ref_dsize; - D = (double)dds->dds_dsize; - - dedup = rD / D; - compress = rL / rP; - copies = rD / rP; - - (void) printf("dedup = %.2f, compress = %.2f, copies = %.2f, " - "dedup * compress / copies = %.2f\n\n", - dedup, compress, copies, dedup * compress / copies); -} - -static void -dump_ddt(ddt_t *ddt, enum ddt_type type, enum ddt_class class) -{ - char name[DDT_NAMELEN]; - ddt_entry_t dde; - uint64_t walk = 0; - dmu_object_info_t doi; - uint64_t count, dspace, mspace; - int error; - - error = ddt_object_info(ddt, type, class, &doi); - - if (error == ENOENT) - return; - ASSERT(error == 0); - - error = ddt_object_count(ddt, type, class, &count); - ASSERT(error == 0); - if (count == 0) - return; - - dspace = doi.doi_physical_blocks_512 << 9; - mspace = doi.doi_fill_count * doi.doi_data_block_size; - - ddt_object_name(ddt, type, class, name); - - (void) printf("%s: %llu entries, size %llu on disk, %llu in core\n", - name, - (u_longlong_t)count, - (u_longlong_t)(dspace / count), - (u_longlong_t)(mspace / count)); - - if (dump_opt['D'] < 3) - return; - - zpool_dump_ddt(NULL, &ddt->ddt_histogram[type][class]); - - if (dump_opt['D'] < 4) - return; - - if (dump_opt['D'] < 5 && class == DDT_CLASS_UNIQUE) - return; - - (void) printf("%s contents:\n\n", name); - - while ((error = ddt_object_walk(ddt, type, class, &walk, &dde)) == 0) - dump_dde(ddt, &dde, walk); - - ASSERT3U(error, ==, ENOENT); - - (void) printf("\n"); -} - -static void -dump_all_ddts(spa_t *spa) -{ - ddt_histogram_t ddh_total; - ddt_stat_t dds_total; - - bzero(&ddh_total, sizeof (ddh_total)); - bzero(&dds_total, sizeof (dds_total)); - - for (enum zio_checksum c = 0; c < ZIO_CHECKSUM_FUNCTIONS; c++) { - ddt_t *ddt = spa->spa_ddt[c]; - for (enum ddt_type type = 0; type < DDT_TYPES; type++) { - for (enum ddt_class class = 0; class < DDT_CLASSES; - class++) { - dump_ddt(ddt, type, class); - } - } - } - - ddt_get_dedup_stats(spa, &dds_total); - - if (dds_total.dds_blocks == 0) { - (void) printf("All DDTs are empty\n"); - return; - } - - (void) printf("\n"); - - if (dump_opt['D'] > 1) { - (void) printf("DDT histogram (aggregated over all DDTs):\n"); - ddt_get_dedup_histogram(spa, &ddh_total); - zpool_dump_ddt(&dds_total, &ddh_total); - } - - dump_dedup_ratio(&dds_total); -} - -static void -dump_dtl_seg(void *arg, uint64_t start, uint64_t size) -{ - char *prefix = arg; - - (void) printf("%s [%llu,%llu) length %llu\n", - prefix, - (u_longlong_t)start, - (u_longlong_t)(start + size), - (u_longlong_t)(size)); -} - -static void -dump_dtl(vdev_t *vd, int indent) -{ - spa_t *spa = vd->vdev_spa; - boolean_t required; - const char *name[DTL_TYPES] = { "missing", "partial", "scrub", - "outage" }; - char prefix[256]; - - spa_vdev_state_enter(spa, SCL_NONE); - required = vdev_dtl_required(vd); - (void) spa_vdev_state_exit(spa, NULL, 0); - - if (indent == 0) - (void) printf("\nDirty time logs:\n\n"); - - (void) printf("\t%*s%s [%s]\n", indent, "", - vd->vdev_path ? vd->vdev_path : - vd->vdev_parent ? vd->vdev_ops->vdev_op_type : spa_name(spa), - required ? "DTL-required" : "DTL-expendable"); - - for (int t = 0; t < DTL_TYPES; t++) { - range_tree_t *rt = vd->vdev_dtl[t]; - if (range_tree_space(rt) == 0) - continue; - (void) snprintf(prefix, sizeof (prefix), "\t%*s%s", - indent + 2, "", name[t]); - range_tree_walk(rt, dump_dtl_seg, prefix); - if (dump_opt['d'] > 5 && vd->vdev_children == 0) - dump_spacemap(spa->spa_meta_objset, vd->vdev_dtl_sm); - } - - for (unsigned c = 0; c < vd->vdev_children; c++) - dump_dtl(vd->vdev_child[c], indent + 4); -} - -/* from spa_history.c: spa_history_create_obj() */ -#define HIS_BUF_LEN_DEF (128 << 10) -#define HIS_BUF_LEN_MAX (1 << 30) - -static void -dump_history(spa_t *spa) -{ - nvlist_t **events = NULL; - char *buf = NULL; - uint64_t bufsize = HIS_BUF_LEN_DEF; - uint64_t resid, len, off = 0; - uint_t num = 0; - int error; - time_t tsec; - struct tm t; - char tbuf[30]; - char internalstr[MAXPATHLEN]; - - if ((buf = malloc(bufsize)) == NULL) - (void) fprintf(stderr, "Unable to read history: " - "out of memory\n"); - do { - len = bufsize; - - if ((error = spa_history_get(spa, &off, &len, buf)) != 0) { - (void) fprintf(stderr, "Unable to read history: " - "error %d\n", error); - return; - } - - if (zpool_history_unpack(buf, len, &resid, &events, &num) != 0) - break; - off -= resid; - - /* - * If the history block is too big, double the buffer - * size and try again. - */ - if (resid == len) { - free(buf); - buf = NULL; - - bufsize <<= 1; - if ((bufsize >= HIS_BUF_LEN_MAX) || - ((buf = malloc(bufsize)) == NULL)) { - (void) fprintf(stderr, "Unable to read history: " - "out of memory\n"); - return; - } - } - } while (len != 0); - free(buf); - - (void) printf("\nHistory:\n"); - for (unsigned i = 0; i < num; i++) { - uint64_t time, txg, ievent; - char *cmd, *intstr; - boolean_t printed = B_FALSE; - - if (nvlist_lookup_uint64(events[i], ZPOOL_HIST_TIME, - &time) != 0) - goto next; - if (nvlist_lookup_string(events[i], ZPOOL_HIST_CMD, - &cmd) != 0) { - if (nvlist_lookup_uint64(events[i], - ZPOOL_HIST_INT_EVENT, &ievent) != 0) - goto next; - verify(nvlist_lookup_uint64(events[i], - ZPOOL_HIST_TXG, &txg) == 0); - verify(nvlist_lookup_string(events[i], - ZPOOL_HIST_INT_STR, &intstr) == 0); - if (ievent >= ZFS_NUM_LEGACY_HISTORY_EVENTS) - goto next; - - (void) snprintf(internalstr, - sizeof (internalstr), - "[internal %s txg:%ju] %s", - zfs_history_event_names[ievent], (uintmax_t)txg, - intstr); - cmd = internalstr; - } - tsec = time; - (void) localtime_r(&tsec, &t); - (void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t); - (void) printf("%s %s\n", tbuf, cmd); - printed = B_TRUE; - -next: - if (dump_opt['h'] > 1) { - if (!printed) - (void) printf("unrecognized record:\n"); - dump_nvlist(events[i], 2); - } - } -} - -/*ARGSUSED*/ -static void -dump_dnode(objset_t *os, uint64_t object, void *data, size_t size) -{ -} - -static uint64_t -blkid2offset(const dnode_phys_t *dnp, const blkptr_t *bp, - const zbookmark_phys_t *zb) -{ - if (dnp == NULL) { - ASSERT(zb->zb_level < 0); - if (zb->zb_object == 0) - return (zb->zb_blkid); - return (zb->zb_blkid * BP_GET_LSIZE(bp)); - } - - ASSERT(zb->zb_level >= 0); - - return ((zb->zb_blkid << - (zb->zb_level * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT))) * - dnp->dn_datablkszsec << SPA_MINBLOCKSHIFT); -} - -static void -snprintf_blkptr_compact(char *blkbuf, size_t buflen, const blkptr_t *bp) -{ - const dva_t *dva = bp->blk_dva; - int ndvas = dump_opt['d'] > 5 ? BP_GET_NDVAS(bp) : 1; - - if (dump_opt['b'] >= 6) { - snprintf_blkptr(blkbuf, buflen, bp); - return; - } - - if (BP_IS_EMBEDDED(bp)) { - (void) sprintf(blkbuf, - "EMBEDDED et=%u %llxL/%llxP B=%llu", - (int)BPE_GET_ETYPE(bp), - (u_longlong_t)BPE_GET_LSIZE(bp), - (u_longlong_t)BPE_GET_PSIZE(bp), - (u_longlong_t)bp->blk_birth); - return; - } - - blkbuf[0] = '\0'; - for (int i = 0; i < ndvas; i++) - (void) snprintf(blkbuf + strlen(blkbuf), - buflen - strlen(blkbuf), "%llu:%llx:%llx ", - (u_longlong_t)DVA_GET_VDEV(&dva[i]), - (u_longlong_t)DVA_GET_OFFSET(&dva[i]), - (u_longlong_t)DVA_GET_ASIZE(&dva[i])); - - if (BP_IS_HOLE(bp)) { - (void) snprintf(blkbuf + strlen(blkbuf), - buflen - strlen(blkbuf), - "%llxL B=%llu", - (u_longlong_t)BP_GET_LSIZE(bp), - (u_longlong_t)bp->blk_birth); - } else { - (void) snprintf(blkbuf + strlen(blkbuf), - buflen - strlen(blkbuf), - "%llxL/%llxP F=%llu B=%llu/%llu", - (u_longlong_t)BP_GET_LSIZE(bp), - (u_longlong_t)BP_GET_PSIZE(bp), - (u_longlong_t)BP_GET_FILL(bp), - (u_longlong_t)bp->blk_birth, - (u_longlong_t)BP_PHYSICAL_BIRTH(bp)); - } -} - -static void -print_indirect(blkptr_t *bp, const zbookmark_phys_t *zb, - const dnode_phys_t *dnp) -{ - char blkbuf[BP_SPRINTF_LEN]; - int l; - - if (!BP_IS_EMBEDDED(bp)) { - ASSERT3U(BP_GET_TYPE(bp), ==, dnp->dn_type); - ASSERT3U(BP_GET_LEVEL(bp), ==, zb->zb_level); - } - - (void) printf("%16llx ", (u_longlong_t)blkid2offset(dnp, bp, zb)); - - ASSERT(zb->zb_level >= 0); - - for (l = dnp->dn_nlevels - 1; l >= -1; l--) { - if (l == zb->zb_level) { - (void) printf("L%llx", (u_longlong_t)zb->zb_level); - } else { - (void) printf(" "); - } - } - - snprintf_blkptr_compact(blkbuf, sizeof (blkbuf), bp); - (void) printf("%s\n", blkbuf); -} - -static int -visit_indirect(spa_t *spa, const dnode_phys_t *dnp, - blkptr_t *bp, const zbookmark_phys_t *zb) -{ - int err = 0; - - if (bp->blk_birth == 0) - return (0); - - print_indirect(bp, zb, dnp); - - if (BP_GET_LEVEL(bp) > 0 && !BP_IS_HOLE(bp)) { - arc_flags_t flags = ARC_FLAG_WAIT; - int i; - blkptr_t *cbp; - int epb = BP_GET_LSIZE(bp) >> SPA_BLKPTRSHIFT; - arc_buf_t *buf; - uint64_t fill = 0; - - err = arc_read(NULL, spa, bp, arc_getbuf_func, &buf, - ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, &flags, zb); - if (err) - return (err); - ASSERT(buf->b_data); - - /* recursively visit blocks below this */ - cbp = buf->b_data; - for (i = 0; i < epb; i++, cbp++) { - zbookmark_phys_t czb; - - SET_BOOKMARK(&czb, zb->zb_objset, zb->zb_object, - zb->zb_level - 1, - zb->zb_blkid * epb + i); - err = visit_indirect(spa, dnp, cbp, &czb); - if (err) - break; - fill += BP_GET_FILL(cbp); - } - if (!err) - ASSERT3U(fill, ==, BP_GET_FILL(bp)); - arc_buf_destroy(buf, &buf); - } - - return (err); -} - -/*ARGSUSED*/ -static void -dump_indirect(dnode_t *dn) -{ - dnode_phys_t *dnp = dn->dn_phys; - int j; - zbookmark_phys_t czb; - - (void) printf("Indirect blocks:\n"); - - SET_BOOKMARK(&czb, dmu_objset_id(dn->dn_objset), - dn->dn_object, dnp->dn_nlevels - 1, 0); - for (j = 0; j < dnp->dn_nblkptr; j++) { - czb.zb_blkid = j; - (void) visit_indirect(dmu_objset_spa(dn->dn_objset), dnp, - &dnp->dn_blkptr[j], &czb); - } - - (void) printf("\n"); -} - -/*ARGSUSED*/ -static void -dump_dsl_dir(objset_t *os, uint64_t object, void *data, size_t size) -{ - dsl_dir_phys_t *dd = data; - time_t crtime; - char nice[32]; - - /* make sure nicenum has enough space */ - CTASSERT(sizeof (nice) >= NN_NUMBUF_SZ); - - if (dd == NULL) - return; - - ASSERT3U(size, >=, sizeof (dsl_dir_phys_t)); - - crtime = dd->dd_creation_time; - (void) printf("\t\tcreation_time = %s", ctime(&crtime)); - (void) printf("\t\thead_dataset_obj = %llu\n", - (u_longlong_t)dd->dd_head_dataset_obj); - (void) printf("\t\tparent_dir_obj = %llu\n", - (u_longlong_t)dd->dd_parent_obj); - (void) printf("\t\torigin_obj = %llu\n", - (u_longlong_t)dd->dd_origin_obj); - (void) printf("\t\tchild_dir_zapobj = %llu\n", - (u_longlong_t)dd->dd_child_dir_zapobj); - zdb_nicenum(dd->dd_used_bytes, nice, sizeof (nice)); - (void) printf("\t\tused_bytes = %s\n", nice); - zdb_nicenum(dd->dd_compressed_bytes, nice, sizeof (nice)); - (void) printf("\t\tcompressed_bytes = %s\n", nice); - zdb_nicenum(dd->dd_uncompressed_bytes, nice, sizeof (nice)); - (void) printf("\t\tuncompressed_bytes = %s\n", nice); - zdb_nicenum(dd->dd_quota, nice, sizeof (nice)); - (void) printf("\t\tquota = %s\n", nice); - zdb_nicenum(dd->dd_reserved, nice, sizeof (nice)); - (void) printf("\t\treserved = %s\n", nice); - (void) printf("\t\tprops_zapobj = %llu\n", - (u_longlong_t)dd->dd_props_zapobj); - (void) printf("\t\tdeleg_zapobj = %llu\n", - (u_longlong_t)dd->dd_deleg_zapobj); - (void) printf("\t\tflags = %llx\n", - (u_longlong_t)dd->dd_flags); - -#define DO(which) \ - zdb_nicenum(dd->dd_used_breakdown[DD_USED_ ## which], nice, \ - sizeof (nice)); \ - (void) printf("\t\tused_breakdown[" #which "] = %s\n", nice) - DO(HEAD); - DO(SNAP); - DO(CHILD); - DO(CHILD_RSRV); - DO(REFRSRV); -#undef DO - (void) printf("\t\tclones = %llu\n", - (u_longlong_t)dd->dd_clones); -} - -/*ARGSUSED*/ -static void -dump_dsl_dataset(objset_t *os, uint64_t object, void *data, size_t size) -{ - dsl_dataset_phys_t *ds = data; - time_t crtime; - char used[32], compressed[32], uncompressed[32], unique[32]; - char blkbuf[BP_SPRINTF_LEN]; - - /* make sure nicenum has enough space */ - CTASSERT(sizeof (used) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (compressed) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (uncompressed) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (unique) >= NN_NUMBUF_SZ); - - if (ds == NULL) - return; - - ASSERT(size == sizeof (*ds)); - crtime = ds->ds_creation_time; - zdb_nicenum(ds->ds_referenced_bytes, used, sizeof (used)); - zdb_nicenum(ds->ds_compressed_bytes, compressed, sizeof (compressed)); - zdb_nicenum(ds->ds_uncompressed_bytes, uncompressed, - sizeof (uncompressed)); - zdb_nicenum(ds->ds_unique_bytes, unique, sizeof (unique)); - snprintf_blkptr(blkbuf, sizeof (blkbuf), &ds->ds_bp); - - (void) printf("\t\tdir_obj = %llu\n", - (u_longlong_t)ds->ds_dir_obj); - (void) printf("\t\tprev_snap_obj = %llu\n", - (u_longlong_t)ds->ds_prev_snap_obj); - (void) printf("\t\tprev_snap_txg = %llu\n", - (u_longlong_t)ds->ds_prev_snap_txg); - (void) printf("\t\tnext_snap_obj = %llu\n", - (u_longlong_t)ds->ds_next_snap_obj); - (void) printf("\t\tsnapnames_zapobj = %llu\n", - (u_longlong_t)ds->ds_snapnames_zapobj); - (void) printf("\t\tnum_children = %llu\n", - (u_longlong_t)ds->ds_num_children); - (void) printf("\t\tuserrefs_obj = %llu\n", - (u_longlong_t)ds->ds_userrefs_obj); - (void) printf("\t\tcreation_time = %s", ctime(&crtime)); - (void) printf("\t\tcreation_txg = %llu\n", - (u_longlong_t)ds->ds_creation_txg); - (void) printf("\t\tdeadlist_obj = %llu\n", - (u_longlong_t)ds->ds_deadlist_obj); - (void) printf("\t\tused_bytes = %s\n", used); - (void) printf("\t\tcompressed_bytes = %s\n", compressed); - (void) printf("\t\tuncompressed_bytes = %s\n", uncompressed); - (void) printf("\t\tunique = %s\n", unique); - (void) printf("\t\tfsid_guid = %llu\n", - (u_longlong_t)ds->ds_fsid_guid); - (void) printf("\t\tguid = %llu\n", - (u_longlong_t)ds->ds_guid); - (void) printf("\t\tflags = %llx\n", - (u_longlong_t)ds->ds_flags); - (void) printf("\t\tnext_clones_obj = %llu\n", - (u_longlong_t)ds->ds_next_clones_obj); - (void) printf("\t\tprops_obj = %llu\n", - (u_longlong_t)ds->ds_props_obj); - (void) printf("\t\tbp = %s\n", blkbuf); -} - -/* ARGSUSED */ -static int -dump_bptree_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx) -{ - char blkbuf[BP_SPRINTF_LEN]; - - if (bp->blk_birth != 0) { - snprintf_blkptr(blkbuf, sizeof (blkbuf), bp); - (void) printf("\t%s\n", blkbuf); - } - return (0); -} - -static void -dump_bptree(objset_t *os, uint64_t obj, const char *name) -{ - char bytes[32]; - bptree_phys_t *bt; - dmu_buf_t *db; - - /* make sure nicenum has enough space */ - CTASSERT(sizeof (bytes) >= NN_NUMBUF_SZ); - - if (dump_opt['d'] < 3) - return; - - VERIFY3U(0, ==, dmu_bonus_hold(os, obj, FTAG, &db)); - bt = db->db_data; - zdb_nicenum(bt->bt_bytes, bytes, sizeof (bytes)); - (void) printf("\n %s: %llu datasets, %s\n", - name, (unsigned long long)(bt->bt_end - bt->bt_begin), bytes); - dmu_buf_rele(db, FTAG); - - if (dump_opt['d'] < 5) - return; - - (void) printf("\n"); - - (void) bptree_iterate(os, obj, B_FALSE, dump_bptree_cb, NULL, NULL); -} - -/* ARGSUSED */ -static int -dump_bpobj_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx) -{ - char blkbuf[BP_SPRINTF_LEN]; - - ASSERT(bp->blk_birth != 0); - snprintf_blkptr_compact(blkbuf, sizeof (blkbuf), bp); - (void) printf("\t%s\n", blkbuf); - return (0); -} - -static void -dump_full_bpobj(bpobj_t *bpo, const char *name, int indent) -{ - char bytes[32]; - char comp[32]; - char uncomp[32]; - - /* make sure nicenum has enough space */ - CTASSERT(sizeof (bytes) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (comp) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (uncomp) >= NN_NUMBUF_SZ); - - if (dump_opt['d'] < 3) - return; - - zdb_nicenum(bpo->bpo_phys->bpo_bytes, bytes, sizeof (bytes)); - if (bpo->bpo_havesubobj && bpo->bpo_phys->bpo_subobjs != 0) { - zdb_nicenum(bpo->bpo_phys->bpo_comp, comp, sizeof (comp)); - zdb_nicenum(bpo->bpo_phys->bpo_uncomp, uncomp, sizeof (uncomp)); - (void) printf(" %*s: object %llu, %llu local blkptrs, " - "%llu subobjs in object %llu, %s (%s/%s comp)\n", - indent * 8, name, - (u_longlong_t)bpo->bpo_object, - (u_longlong_t)bpo->bpo_phys->bpo_num_blkptrs, - (u_longlong_t)bpo->bpo_phys->bpo_num_subobjs, - (u_longlong_t)bpo->bpo_phys->bpo_subobjs, - bytes, comp, uncomp); - - for (uint64_t i = 0; i < bpo->bpo_phys->bpo_num_subobjs; i++) { - uint64_t subobj; - bpobj_t subbpo; - int error; - VERIFY0(dmu_read(bpo->bpo_os, - bpo->bpo_phys->bpo_subobjs, - i * sizeof (subobj), sizeof (subobj), &subobj, 0)); - error = bpobj_open(&subbpo, bpo->bpo_os, subobj); - if (error != 0) { - (void) printf("ERROR %u while trying to open " - "subobj id %llu\n", - error, (u_longlong_t)subobj); - continue; - } - dump_full_bpobj(&subbpo, "subobj", indent + 1); - bpobj_close(&subbpo); - } - } else { - (void) printf(" %*s: object %llu, %llu blkptrs, %s\n", - indent * 8, name, - (u_longlong_t)bpo->bpo_object, - (u_longlong_t)bpo->bpo_phys->bpo_num_blkptrs, - bytes); - } - - if (dump_opt['d'] < 5) - return; - - - if (indent == 0) { - (void) bpobj_iterate_nofree(bpo, dump_bpobj_cb, NULL, NULL); - (void) printf("\n"); - } -} - -static void -bpobj_count_refd(bpobj_t *bpo) -{ - mos_obj_refd(bpo->bpo_object); - - if (bpo->bpo_havesubobj && bpo->bpo_phys->bpo_subobjs != 0) { - mos_obj_refd(bpo->bpo_phys->bpo_subobjs); - for (uint64_t i = 0; i < bpo->bpo_phys->bpo_num_subobjs; i++) { - uint64_t subobj; - bpobj_t subbpo; - int error; - VERIFY0(dmu_read(bpo->bpo_os, - bpo->bpo_phys->bpo_subobjs, - i * sizeof (subobj), sizeof (subobj), &subobj, 0)); - error = bpobj_open(&subbpo, bpo->bpo_os, subobj); - if (error != 0) { - (void) printf("ERROR %u while trying to open " - "subobj id %llu\n", - error, (u_longlong_t)subobj); - continue; - } - bpobj_count_refd(&subbpo); - bpobj_close(&subbpo); - } - } -} - -static void -dump_deadlist(dsl_deadlist_t *dl) -{ - dsl_deadlist_entry_t *dle; - uint64_t unused; - char bytes[32]; - char comp[32]; - char uncomp[32]; - uint64_t empty_bpobj = - dmu_objset_spa(dl->dl_os)->spa_dsl_pool->dp_empty_bpobj; - - /* force the tree to be loaded */ - dsl_deadlist_space_range(dl, 0, UINT64_MAX, &unused, &unused, &unused); - - if (dl->dl_oldfmt) { - if (dl->dl_bpobj.bpo_object != empty_bpobj) - bpobj_count_refd(&dl->dl_bpobj); - } else { - mos_obj_refd(dl->dl_object); - for (dle = avl_first(&dl->dl_tree); dle; - dle = AVL_NEXT(&dl->dl_tree, dle)) { - if (dle->dle_bpobj.bpo_object != empty_bpobj) - bpobj_count_refd(&dle->dle_bpobj); - } - } - - /* make sure nicenum has enough space */ - CTASSERT(sizeof (bytes) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (comp) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (uncomp) >= NN_NUMBUF_SZ); - - if (dump_opt['d'] < 3) - return; - - if (dl->dl_oldfmt) { - dump_full_bpobj(&dl->dl_bpobj, "old-format deadlist", 0); - return; - } - - zdb_nicenum(dl->dl_phys->dl_used, bytes, sizeof (bytes)); - zdb_nicenum(dl->dl_phys->dl_comp, comp, sizeof (comp)); - zdb_nicenum(dl->dl_phys->dl_uncomp, uncomp, sizeof (uncomp)); - (void) printf("\n Deadlist: %s (%s/%s comp)\n", - bytes, comp, uncomp); - - if (dump_opt['d'] < 4) - return; - - (void) printf("\n"); - - for (dle = avl_first(&dl->dl_tree); dle; - dle = AVL_NEXT(&dl->dl_tree, dle)) { - if (dump_opt['d'] >= 5) { - char buf[128]; - (void) snprintf(buf, sizeof (buf), - "mintxg %llu -> obj %llu", - (longlong_t)dle->dle_mintxg, - (longlong_t)dle->dle_bpobj.bpo_object); - dump_full_bpobj(&dle->dle_bpobj, buf, 0); - } else { - (void) printf("mintxg %llu -> obj %llu\n", - (longlong_t)dle->dle_mintxg, - (longlong_t)dle->dle_bpobj.bpo_object); - } - } -} - -static avl_tree_t idx_tree; -static avl_tree_t domain_tree; -static boolean_t fuid_table_loaded; -static objset_t *sa_os = NULL; -static sa_attr_type_t *sa_attr_table = NULL; - -static int -open_objset(const char *path, dmu_objset_type_t type, void *tag, objset_t **osp) -{ - int err; - uint64_t sa_attrs = 0; - uint64_t version = 0; - - VERIFY3P(sa_os, ==, NULL); - err = dmu_objset_own(path, type, B_TRUE, tag, osp); - if (err != 0) { - (void) fprintf(stderr, "failed to own dataset '%s': %s\n", path, - strerror(err)); - return (err); - } - - if (dmu_objset_type(*osp) == DMU_OST_ZFS) { - (void) zap_lookup(*osp, MASTER_NODE_OBJ, ZPL_VERSION_STR, - 8, 1, &version); - if (version >= ZPL_VERSION_SA) { - (void) zap_lookup(*osp, MASTER_NODE_OBJ, ZFS_SA_ATTRS, - 8, 1, &sa_attrs); - } - err = sa_setup(*osp, sa_attrs, zfs_attr_table, ZPL_END, - &sa_attr_table); - if (err != 0) { - (void) fprintf(stderr, "sa_setup failed: %s\n", - strerror(err)); - dmu_objset_disown(*osp, tag); - *osp = NULL; - } - } - sa_os = *osp; - - return (0); -} - -static void -close_objset(objset_t *os, void *tag) -{ - VERIFY3P(os, ==, sa_os); - if (os->os_sa != NULL) - sa_tear_down(os); - dmu_objset_disown(os, tag); - sa_attr_table = NULL; - sa_os = NULL; -} - -static void -fuid_table_destroy() -{ - if (fuid_table_loaded) { - zfs_fuid_table_destroy(&idx_tree, &domain_tree); - fuid_table_loaded = B_FALSE; - } -} - -/* - * print uid or gid information. - * For normal POSIX id just the id is printed in decimal format. - * For CIFS files with FUID the fuid is printed in hex followed by - * the domain-rid string. - */ -static void -print_idstr(uint64_t id, const char *id_type) -{ - if (FUID_INDEX(id)) { - char *domain; - - domain = zfs_fuid_idx_domain(&idx_tree, FUID_INDEX(id)); - (void) printf("\t%s %llx [%s-%d]\n", id_type, - (u_longlong_t)id, domain, (int)FUID_RID(id)); - } else { - (void) printf("\t%s %llu\n", id_type, (u_longlong_t)id); - } - -} - -static void -dump_uidgid(objset_t *os, uint64_t uid, uint64_t gid) -{ - uint32_t uid_idx, gid_idx; - - uid_idx = FUID_INDEX(uid); - gid_idx = FUID_INDEX(gid); - - /* Load domain table, if not already loaded */ - if (!fuid_table_loaded && (uid_idx || gid_idx)) { - uint64_t fuid_obj; - - /* first find the fuid object. It lives in the master node */ - VERIFY(zap_lookup(os, MASTER_NODE_OBJ, ZFS_FUID_TABLES, - 8, 1, &fuid_obj) == 0); - zfs_fuid_avl_tree_create(&idx_tree, &domain_tree); - (void) zfs_fuid_table_load(os, fuid_obj, - &idx_tree, &domain_tree); - fuid_table_loaded = B_TRUE; - } - - print_idstr(uid, "uid"); - print_idstr(gid, "gid"); -} - -/*ARGSUSED*/ -static void -dump_znode(objset_t *os, uint64_t object, void *data, size_t size) -{ - char path[MAXPATHLEN * 2]; /* allow for xattr and failure prefix */ - sa_handle_t *hdl; - uint64_t xattr, rdev, gen; - uint64_t uid, gid, mode, fsize, parent, links; - uint64_t pflags; - uint64_t acctm[2], modtm[2], chgtm[2], crtm[2]; - time_t z_crtime, z_atime, z_mtime, z_ctime; - sa_bulk_attr_t bulk[12]; - int idx = 0; - int error; - - VERIFY3P(os, ==, sa_os); - if (sa_handle_get(os, object, NULL, SA_HDL_PRIVATE, &hdl)) { - (void) printf("Failed to get handle for SA znode\n"); - return; - } - - SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_UID], NULL, &uid, 8); - SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_GID], NULL, &gid, 8); - SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_LINKS], NULL, - &links, 8); - SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_GEN], NULL, &gen, 8); - SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_MODE], NULL, - &mode, 8); - SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_PARENT], - NULL, &parent, 8); - SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_SIZE], NULL, - &fsize, 8); - SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_ATIME], NULL, - acctm, 16); - SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_MTIME], NULL, - modtm, 16); - SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_CRTIME], NULL, - crtm, 16); - SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_CTIME], NULL, - chgtm, 16); - SA_ADD_BULK_ATTR(bulk, idx, sa_attr_table[ZPL_FLAGS], NULL, - &pflags, 8); - - if (sa_bulk_lookup(hdl, bulk, idx)) { - (void) sa_handle_destroy(hdl); - return; - } - - z_crtime = (time_t)crtm[0]; - z_atime = (time_t)acctm[0]; - z_mtime = (time_t)modtm[0]; - z_ctime = (time_t)chgtm[0]; - - if (dump_opt['d'] > 4) { - error = zfs_obj_to_path(os, object, path, sizeof (path)); - if (error == ESTALE) { - (void) snprintf(path, sizeof (path), "on delete queue"); - } else if (error != 0) { - leaked_objects++; - (void) snprintf(path, sizeof (path), - "path not found, possibly leaked"); - } - (void) printf("\tpath %s\n", path); - } - dump_uidgid(os, uid, gid); - (void) printf("\tatime %s", ctime(&z_atime)); - (void) printf("\tmtime %s", ctime(&z_mtime)); - (void) printf("\tctime %s", ctime(&z_ctime)); - (void) printf("\tcrtime %s", ctime(&z_crtime)); - (void) printf("\tgen %llu\n", (u_longlong_t)gen); - (void) printf("\tmode %llo\n", (u_longlong_t)mode); - (void) printf("\tsize %llu\n", (u_longlong_t)fsize); - (void) printf("\tparent %llu\n", (u_longlong_t)parent); - (void) printf("\tlinks %llu\n", (u_longlong_t)links); - (void) printf("\tpflags %llx\n", (u_longlong_t)pflags); - if (sa_lookup(hdl, sa_attr_table[ZPL_XATTR], &xattr, - sizeof (uint64_t)) == 0) - (void) printf("\txattr %llu\n", (u_longlong_t)xattr); - if (sa_lookup(hdl, sa_attr_table[ZPL_RDEV], &rdev, - sizeof (uint64_t)) == 0) - (void) printf("\trdev 0x%016llx\n", (u_longlong_t)rdev); - sa_handle_destroy(hdl); -} - -/*ARGSUSED*/ -static void -dump_acl(objset_t *os, uint64_t object, void *data, size_t size) -{ -} - -/*ARGSUSED*/ -static void -dump_dmu_objset(objset_t *os, uint64_t object, void *data, size_t size) -{ -} - -static object_viewer_t *object_viewer[DMU_OT_NUMTYPES + 1] = { - dump_none, /* unallocated */ - dump_zap, /* object directory */ - dump_uint64, /* object array */ - dump_none, /* packed nvlist */ - dump_packed_nvlist, /* packed nvlist size */ - dump_none, /* bpobj */ - dump_bpobj, /* bpobj header */ - dump_none, /* SPA space map header */ - dump_none, /* SPA space map */ - dump_none, /* ZIL intent log */ - dump_dnode, /* DMU dnode */ - dump_dmu_objset, /* DMU objset */ - dump_dsl_dir, /* DSL directory */ - dump_zap, /* DSL directory child map */ - dump_zap, /* DSL dataset snap map */ - dump_zap, /* DSL props */ - dump_dsl_dataset, /* DSL dataset */ - dump_znode, /* ZFS znode */ - dump_acl, /* ZFS V0 ACL */ - dump_uint8, /* ZFS plain file */ - dump_zpldir, /* ZFS directory */ - dump_zap, /* ZFS master node */ - dump_zap, /* ZFS delete queue */ - dump_uint8, /* zvol object */ - dump_zap, /* zvol prop */ - dump_uint8, /* other uint8[] */ - dump_uint64, /* other uint64[] */ - dump_zap, /* other ZAP */ - dump_zap, /* persistent error log */ - dump_uint8, /* SPA history */ - dump_history_offsets, /* SPA history offsets */ - dump_zap, /* Pool properties */ - dump_zap, /* DSL permissions */ - dump_acl, /* ZFS ACL */ - dump_uint8, /* ZFS SYSACL */ - dump_none, /* FUID nvlist */ - dump_packed_nvlist, /* FUID nvlist size */ - dump_zap, /* DSL dataset next clones */ - dump_zap, /* DSL scrub queue */ - dump_zap, /* ZFS user/group used */ - dump_zap, /* ZFS user/group quota */ - dump_zap, /* snapshot refcount tags */ - dump_ddt_zap, /* DDT ZAP object */ - dump_zap, /* DDT statistics */ - dump_znode, /* SA object */ - dump_zap, /* SA Master Node */ - dump_sa_attrs, /* SA attribute registration */ - dump_sa_layouts, /* SA attribute layouts */ - dump_zap, /* DSL scrub translations */ - dump_none, /* fake dedup BP */ - dump_zap, /* deadlist */ - dump_none, /* deadlist hdr */ - dump_zap, /* dsl clones */ - dump_bpobj_subobjs, /* bpobj subobjs */ - dump_unknown, /* Unknown type, must be last */ -}; - -static void -dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header, - uint64_t *dnode_slots_used) -{ - dmu_buf_t *db = NULL; - dmu_object_info_t doi; - dnode_t *dn; - void *bonus = NULL; - size_t bsize = 0; - char iblk[32], dblk[32], lsize[32], asize[32], fill[32], dnsize[32]; - char bonus_size[32]; - char aux[50]; - int error; - - /* make sure nicenum has enough space */ - CTASSERT(sizeof (iblk) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (dblk) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (lsize) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (asize) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (bonus_size) >= NN_NUMBUF_SZ); - - if (*print_header) { - (void) printf("\n%10s %3s %5s %5s %5s %6s %5s %6s %s\n", - "Object", "lvl", "iblk", "dblk", "dsize", "dnsize", - "lsize", "%full", "type"); - *print_header = 0; - } - - if (object == 0) { - dn = DMU_META_DNODE(os); - } else { - error = dmu_bonus_hold(os, object, FTAG, &db); - if (error) - fatal("dmu_bonus_hold(%llu) failed, errno %u", - object, error); - bonus = db->db_data; - bsize = db->db_size; - dn = DB_DNODE((dmu_buf_impl_t *)db); - } - dmu_object_info_from_dnode(dn, &doi); - - if (dnode_slots_used != NULL) - *dnode_slots_used = doi.doi_dnodesize / DNODE_MIN_SIZE; - - zdb_nicenum(doi.doi_metadata_block_size, iblk, sizeof (iblk)); - zdb_nicenum(doi.doi_data_block_size, dblk, sizeof (dblk)); - zdb_nicenum(doi.doi_max_offset, lsize, sizeof (lsize)); - zdb_nicenum(doi.doi_physical_blocks_512 << 9, asize, sizeof (asize)); - zdb_nicenum(doi.doi_bonus_size, bonus_size, sizeof (bonus_size)); - zdb_nicenum(doi.doi_dnodesize, dnsize, sizeof (dnsize)); - (void) sprintf(fill, "%6.2f", 100.0 * doi.doi_fill_count * - doi.doi_data_block_size / (object == 0 ? DNODES_PER_BLOCK : 1) / - doi.doi_max_offset); - - aux[0] = '\0'; - - if (doi.doi_checksum != ZIO_CHECKSUM_INHERIT || verbosity >= 6) { - (void) snprintf(aux + strlen(aux), sizeof (aux), " (K=%s)", - ZDB_CHECKSUM_NAME(doi.doi_checksum)); - } - - if (doi.doi_compress != ZIO_COMPRESS_INHERIT || verbosity >= 6) { - (void) snprintf(aux + strlen(aux), sizeof (aux), " (Z=%s)", - ZDB_COMPRESS_NAME(doi.doi_compress)); - } - - (void) printf("%10" PRIu64 - " %3u %5s %5s %5s %5s %5s %6s %s%s\n", - object, doi.doi_indirection, iblk, dblk, - asize, dnsize, lsize, fill, ZDB_OT_NAME(doi.doi_type), aux); - - if (doi.doi_bonus_type != DMU_OT_NONE && verbosity > 3) { - (void) printf("%10s %3s %5s %5s %5s %5s %5s %6s %s\n", - "", "", "", "", "", "", bonus_size, "bonus", - ZDB_OT_NAME(doi.doi_bonus_type)); - } - - if (verbosity >= 4) { - (void) printf("\tdnode flags: %s%s%s\n", - (dn->dn_phys->dn_flags & DNODE_FLAG_USED_BYTES) ? - "USED_BYTES " : "", - (dn->dn_phys->dn_flags & DNODE_FLAG_USERUSED_ACCOUNTED) ? - "USERUSED_ACCOUNTED " : "", - (dn->dn_phys->dn_flags & DNODE_FLAG_SPILL_BLKPTR) ? - "SPILL_BLKPTR" : ""); - (void) printf("\tdnode maxblkid: %llu\n", - (longlong_t)dn->dn_phys->dn_maxblkid); - - object_viewer[ZDB_OT_TYPE(doi.doi_bonus_type)](os, object, - bonus, bsize); - object_viewer[ZDB_OT_TYPE(doi.doi_type)](os, object, NULL, 0); - *print_header = 1; - } - - if (verbosity >= 5) - dump_indirect(dn); - - if (verbosity >= 5) { - /* - * Report the list of segments that comprise the object. - */ - uint64_t start = 0; - uint64_t end; - uint64_t blkfill = 1; - int minlvl = 1; - - if (dn->dn_type == DMU_OT_DNODE) { - minlvl = 0; - blkfill = DNODES_PER_BLOCK; - } - - for (;;) { - char segsize[32]; - /* make sure nicenum has enough space */ - CTASSERT(sizeof (segsize) >= NN_NUMBUF_SZ); - error = dnode_next_offset(dn, - 0, &start, minlvl, blkfill, 0); - if (error) - break; - end = start; - error = dnode_next_offset(dn, - DNODE_FIND_HOLE, &end, minlvl, blkfill, 0); - zdb_nicenum(end - start, segsize, sizeof (segsize)); - (void) printf("\t\tsegment [%016llx, %016llx)" - " size %5s\n", (u_longlong_t)start, - (u_longlong_t)end, segsize); - if (error) - break; - start = end; - } - } - - if (db != NULL) - dmu_buf_rele(db, FTAG); -} - -static void -count_dir_mos_objects(dsl_dir_t *dd) -{ - mos_obj_refd(dd->dd_object); - mos_obj_refd(dsl_dir_phys(dd)->dd_child_dir_zapobj); - mos_obj_refd(dsl_dir_phys(dd)->dd_deleg_zapobj); - mos_obj_refd(dsl_dir_phys(dd)->dd_props_zapobj); - mos_obj_refd(dsl_dir_phys(dd)->dd_clones); -} - -static void -count_ds_mos_objects(dsl_dataset_t *ds) -{ - mos_obj_refd(ds->ds_object); - mos_obj_refd(dsl_dataset_phys(ds)->ds_next_clones_obj); - mos_obj_refd(dsl_dataset_phys(ds)->ds_props_obj); - mos_obj_refd(dsl_dataset_phys(ds)->ds_userrefs_obj); - mos_obj_refd(dsl_dataset_phys(ds)->ds_snapnames_zapobj); - - if (!dsl_dataset_is_snapshot(ds)) { - count_dir_mos_objects(ds->ds_dir); - } -} - -static const char *objset_types[DMU_OST_NUMTYPES] = { - "NONE", "META", "ZPL", "ZVOL", "OTHER", "ANY" }; - -static void -dump_dir(objset_t *os) -{ - dmu_objset_stats_t dds; - uint64_t object, object_count; - uint64_t refdbytes, usedobjs, scratch; - char numbuf[32]; - char blkbuf[BP_SPRINTF_LEN + 20]; - char osname[ZFS_MAX_DATASET_NAME_LEN]; - const char *type = "UNKNOWN"; - int verbosity = dump_opt['d']; - int print_header = 1; - unsigned i; - int error; - uint64_t total_slots_used = 0; - uint64_t max_slot_used = 0; - uint64_t dnode_slots; - - /* make sure nicenum has enough space */ - CTASSERT(sizeof (numbuf) >= NN_NUMBUF_SZ); - - dsl_pool_config_enter(dmu_objset_pool(os), FTAG); - dmu_objset_fast_stat(os, &dds); - dsl_pool_config_exit(dmu_objset_pool(os), FTAG); - - if (dds.dds_type < DMU_OST_NUMTYPES) - type = objset_types[dds.dds_type]; - - if (dds.dds_type == DMU_OST_META) { - dds.dds_creation_txg = TXG_INITIAL; - usedobjs = BP_GET_FILL(os->os_rootbp); - refdbytes = dsl_dir_phys(os->os_spa->spa_dsl_pool->dp_mos_dir)-> - dd_used_bytes; - } else { - dmu_objset_space(os, &refdbytes, &scratch, &usedobjs, &scratch); - } - - ASSERT3U(usedobjs, ==, BP_GET_FILL(os->os_rootbp)); - - zdb_nicenum(refdbytes, numbuf, sizeof (numbuf)); - - if (verbosity >= 4) { - (void) snprintf(blkbuf, sizeof (blkbuf), ", rootbp "); - (void) snprintf_blkptr(blkbuf + strlen(blkbuf), - sizeof (blkbuf) - strlen(blkbuf), os->os_rootbp); - } else { - blkbuf[0] = '\0'; - } - - dmu_objset_name(os, osname); - - (void) printf("Dataset %s [%s], ID %llu, cr_txg %llu, " - "%s, %llu objects%s%s\n", - osname, type, (u_longlong_t)dmu_objset_id(os), - (u_longlong_t)dds.dds_creation_txg, - numbuf, (u_longlong_t)usedobjs, blkbuf, - (dds.dds_inconsistent) ? " (inconsistent)" : ""); - - if (zopt_objects != 0) { - for (i = 0; i < zopt_objects; i++) - dump_object(os, zopt_object[i], verbosity, - &print_header, NULL); - (void) printf("\n"); - return; - } - - if (dump_opt['i'] != 0 || verbosity >= 2) - dump_intent_log(dmu_objset_zil(os)); - - if (dmu_objset_ds(os) != NULL) { - dsl_dataset_t *ds = dmu_objset_ds(os); - dump_deadlist(&ds->ds_deadlist); - - if (dsl_dataset_remap_deadlist_exists(ds)) { - (void) printf("ds_remap_deadlist:\n"); - dump_deadlist(&ds->ds_remap_deadlist); - } - count_ds_mos_objects(ds); - } - - if (verbosity < 2) - return; - - if (BP_IS_HOLE(os->os_rootbp)) - return; - - dump_object(os, 0, verbosity, &print_header, NULL); - object_count = 0; - if (DMU_USERUSED_DNODE(os) != NULL && - DMU_USERUSED_DNODE(os)->dn_type != 0) { - dump_object(os, DMU_USERUSED_OBJECT, verbosity, &print_header, - NULL); - dump_object(os, DMU_GROUPUSED_OBJECT, verbosity, &print_header, - NULL); - } - - object = 0; - while ((error = dmu_object_next(os, &object, B_FALSE, 0)) == 0) { - dump_object(os, object, verbosity, &print_header, &dnode_slots); - object_count++; - total_slots_used += dnode_slots; - max_slot_used = object + dnode_slots - 1; - } - - (void) printf("\n"); - - (void) printf(" Dnode slots:\n"); - (void) printf("\tTotal used: %10llu\n", - (u_longlong_t)total_slots_used); - (void) printf("\tMax used: %10llu\n", - (u_longlong_t)max_slot_used); - (void) printf("\tPercent empty: %10lf\n", - (double)(max_slot_used - total_slots_used)*100 / - (double)max_slot_used); - - (void) printf("\n"); - - if (error != ESRCH) { - (void) fprintf(stderr, "dmu_object_next() = %d\n", error); - abort(); - } - - ASSERT3U(object_count, ==, usedobjs); - - if (leaked_objects != 0) { - (void) printf("%d potentially leaked objects detected\n", - leaked_objects); - leaked_objects = 0; - } -} - -static void -dump_uberblock(uberblock_t *ub, const char *header, const char *footer) -{ - time_t timestamp = ub->ub_timestamp; - - (void) printf("%s", header ? header : ""); - (void) printf("\tmagic = %016llx\n", (u_longlong_t)ub->ub_magic); - (void) printf("\tversion = %llu\n", (u_longlong_t)ub->ub_version); - (void) printf("\ttxg = %llu\n", (u_longlong_t)ub->ub_txg); - (void) printf("\tguid_sum = %llu\n", (u_longlong_t)ub->ub_guid_sum); - (void) printf("\ttimestamp = %llu UTC = %s", - (u_longlong_t)ub->ub_timestamp, asctime(localtime(×tamp))); - - (void) printf("\tmmp_magic = %016llx\n", - (u_longlong_t)ub->ub_mmp_magic); - if (MMP_VALID(ub)) { - (void) printf("\tmmp_delay = %0llu\n", - (u_longlong_t)ub->ub_mmp_delay); - if (MMP_SEQ_VALID(ub)) - (void) printf("\tmmp_seq = %u\n", - (unsigned int) MMP_SEQ(ub)); - if (MMP_FAIL_INT_VALID(ub)) - (void) printf("\tmmp_fail = %u\n", - (unsigned int) MMP_FAIL_INT(ub)); - if (MMP_INTERVAL_VALID(ub)) - (void) printf("\tmmp_write = %u\n", - (unsigned int) MMP_INTERVAL(ub)); - /* After MMP_* to make summarize_uberblock_mmp cleaner */ - (void) printf("\tmmp_valid = %x\n", - (unsigned int) ub->ub_mmp_config & 0xFF); - } - - if (dump_opt['u'] >= 3) { - char blkbuf[BP_SPRINTF_LEN]; - snprintf_blkptr(blkbuf, sizeof (blkbuf), &ub->ub_rootbp); - (void) printf("\trootbp = %s\n", blkbuf); - } - (void) printf("\tcheckpoint_txg = %llu\n", - (u_longlong_t)ub->ub_checkpoint_txg); - (void) printf("%s", footer ? footer : ""); -} - -static void -dump_config(spa_t *spa) -{ - dmu_buf_t *db; - size_t nvsize = 0; - int error = 0; - - - error = dmu_bonus_hold(spa->spa_meta_objset, - spa->spa_config_object, FTAG, &db); - - if (error == 0) { - nvsize = *(uint64_t *)db->db_data; - dmu_buf_rele(db, FTAG); - - (void) printf("\nMOS Configuration:\n"); - dump_packed_nvlist(spa->spa_meta_objset, - spa->spa_config_object, (void *)&nvsize, 1); - } else { - (void) fprintf(stderr, "dmu_bonus_hold(%llu) failed, errno %d", - (u_longlong_t)spa->spa_config_object, error); - } -} - -static void -dump_cachefile(const char *cachefile) -{ - int fd; - struct stat64 statbuf; - char *buf; - nvlist_t *config; - - if ((fd = open64(cachefile, O_RDONLY)) < 0) { - (void) fprintf(stderr, "cannot open '%s': %s\n", cachefile, - strerror(errno)); - exit(1); - } - - if (fstat64(fd, &statbuf) != 0) { - (void) fprintf(stderr, "failed to stat '%s': %s\n", cachefile, - strerror(errno)); - exit(1); - } - - if ((buf = malloc(statbuf.st_size)) == NULL) { - (void) fprintf(stderr, "failed to allocate %llu bytes\n", - (u_longlong_t)statbuf.st_size); - exit(1); - } - - if (read(fd, buf, statbuf.st_size) != statbuf.st_size) { - (void) fprintf(stderr, "failed to read %llu bytes\n", - (u_longlong_t)statbuf.st_size); - exit(1); - } - - (void) close(fd); - - if (nvlist_unpack(buf, statbuf.st_size, &config, 0) != 0) { - (void) fprintf(stderr, "failed to unpack nvlist\n"); - exit(1); - } - - free(buf); - - dump_nvlist(config, 0); - - nvlist_free(config); -} - -#define ZDB_MAX_UB_HEADER_SIZE 32 - -static void -dump_label_uberblocks(vdev_label_t *lbl, uint64_t ashift) -{ - vdev_t vd; - vdev_t *vdp = &vd; - char header[ZDB_MAX_UB_HEADER_SIZE]; - - vd.vdev_ashift = ashift; - vdp->vdev_top = vdp; - - for (int i = 0; i < VDEV_UBERBLOCK_COUNT(vdp); i++) { - uint64_t uoff = VDEV_UBERBLOCK_OFFSET(vdp, i); - uberblock_t *ub = (void *)((char *)lbl + uoff); - - if (uberblock_verify(ub)) - continue; - - if ((dump_opt['u'] < 4) && - (ub->ub_mmp_magic == MMP_MAGIC) && ub->ub_mmp_delay && - (i >= VDEV_UBERBLOCK_COUNT(&vd) - MMP_BLOCKS_PER_LABEL)) - continue; - - (void) snprintf(header, ZDB_MAX_UB_HEADER_SIZE, - "Uberblock[%d]\n", i); - dump_uberblock(ub, header, ""); - } -} - -static char curpath[PATH_MAX]; - -/* - * Iterate through the path components, recursively passing - * current one's obj and remaining path until we find the obj - * for the last one. - */ -static int -dump_path_impl(objset_t *os, uint64_t obj, char *name) -{ - int err; - int header = 1; - uint64_t child_obj; - char *s; - dmu_buf_t *db; - dmu_object_info_t doi; - - if ((s = strchr(name, '/')) != NULL) - *s = '\0'; - err = zap_lookup(os, obj, name, 8, 1, &child_obj); - - (void) strlcat(curpath, name, sizeof (curpath)); - - if (err != 0) { - (void) fprintf(stderr, "failed to lookup %s: %s\n", - curpath, strerror(err)); - return (err); - } - - child_obj = ZFS_DIRENT_OBJ(child_obj); - err = sa_buf_hold(os, child_obj, FTAG, &db); - if (err != 0) { - (void) fprintf(stderr, - "failed to get SA dbuf for obj %llu: %s\n", - (u_longlong_t)child_obj, strerror(err)); - return (EINVAL); - } - dmu_object_info_from_db(db, &doi); - sa_buf_rele(db, FTAG); - - if (doi.doi_bonus_type != DMU_OT_SA && - doi.doi_bonus_type != DMU_OT_ZNODE) { - (void) fprintf(stderr, "invalid bonus type %d for obj %llu\n", - doi.doi_bonus_type, (u_longlong_t)child_obj); - return (EINVAL); - } - - if (dump_opt['v'] > 6) { - (void) printf("obj=%llu %s type=%d bonustype=%d\n", - (u_longlong_t)child_obj, curpath, doi.doi_type, - doi.doi_bonus_type); - } - - (void) strlcat(curpath, "/", sizeof (curpath)); - - switch (doi.doi_type) { - case DMU_OT_DIRECTORY_CONTENTS: - if (s != NULL && *(s + 1) != '\0') - return (dump_path_impl(os, child_obj, s + 1)); - /*FALLTHROUGH*/ - case DMU_OT_PLAIN_FILE_CONTENTS: - dump_object(os, child_obj, dump_opt['v'], &header, NULL); - return (0); - default: - (void) fprintf(stderr, "object %llu has non-file/directory " - "type %d\n", (u_longlong_t)obj, doi.doi_type); - break; - } - - return (EINVAL); -} - -/* - * Dump the blocks for the object specified by path inside the dataset. - */ -static int -dump_path(char *ds, char *path) -{ - int err; - objset_t *os; - uint64_t root_obj; - - err = open_objset(ds, DMU_OST_ZFS, FTAG, &os); - if (err != 0) - return (err); - - err = zap_lookup(os, MASTER_NODE_OBJ, ZFS_ROOT_OBJ, 8, 1, &root_obj); - if (err != 0) { - (void) fprintf(stderr, "can't lookup root znode: %s\n", - strerror(err)); - dmu_objset_disown(os, FTAG); - return (EINVAL); - } - - (void) snprintf(curpath, sizeof (curpath), "dataset=%s path=/", ds); - - err = dump_path_impl(os, root_obj, path); - - close_objset(os, FTAG); - return (err); -} - -static int -dump_label(const char *dev) -{ - int fd; - vdev_label_t label; - char path[MAXPATHLEN]; - char *buf = label.vl_vdev_phys.vp_nvlist; - size_t buflen = sizeof (label.vl_vdev_phys.vp_nvlist); - struct stat64 statbuf; - uint64_t psize, ashift; - boolean_t label_found = B_FALSE; - - (void) strlcpy(path, dev, sizeof (path)); - if (dev[0] == '/') { - if (strncmp(dev, ZFS_DISK_ROOTD, - strlen(ZFS_DISK_ROOTD)) == 0) { - (void) snprintf(path, sizeof (path), "%s%s", - ZFS_RDISK_ROOTD, dev + strlen(ZFS_DISK_ROOTD)); - } - } else if (stat64(path, &statbuf) != 0) { - char *s; - - (void) snprintf(path, sizeof (path), "%s%s", ZFS_RDISK_ROOTD, - dev); - if (((s = strrchr(dev, 's')) == NULL && - (s = strchr(dev, 'p')) == NULL) || - !isdigit(*(s + 1))) - (void) strlcat(path, "s0", sizeof (path)); - } - - if ((fd = open64(path, O_RDONLY)) < 0) { - (void) fprintf(stderr, "cannot open '%s': %s\n", path, - strerror(errno)); - exit(1); - } - - if (fstat64(fd, &statbuf) != 0) { - (void) fprintf(stderr, "failed to stat '%s': %s\n", path, - strerror(errno)); - (void) close(fd); - exit(1); - } - - if (S_ISBLK(statbuf.st_mode)) { - (void) fprintf(stderr, - "cannot use '%s': character device required\n", path); - (void) close(fd); - exit(1); - } - - psize = statbuf.st_size; - psize = P2ALIGN(psize, (uint64_t)sizeof (vdev_label_t)); - - for (int l = 0; l < VDEV_LABELS; l++) { - nvlist_t *config = NULL; - - if (!dump_opt['q']) { - (void) printf("------------------------------------\n"); - (void) printf("LABEL %d\n", l); - (void) printf("------------------------------------\n"); - } - - if (pread64(fd, &label, sizeof (label), - vdev_label_offset(psize, l, 0)) != sizeof (label)) { - if (!dump_opt['q']) - (void) printf("failed to read label %d\n", l); - continue; - } - - if (nvlist_unpack(buf, buflen, &config, 0) != 0) { - if (!dump_opt['q']) - (void) printf("failed to unpack label %d\n", l); - ashift = SPA_MINBLOCKSHIFT; - } else { - nvlist_t *vdev_tree = NULL; - - if (!dump_opt['q']) - dump_nvlist(config, 4); - if ((nvlist_lookup_nvlist(config, - ZPOOL_CONFIG_VDEV_TREE, &vdev_tree) != 0) || - (nvlist_lookup_uint64(vdev_tree, - ZPOOL_CONFIG_ASHIFT, &ashift) != 0)) - ashift = SPA_MINBLOCKSHIFT; - nvlist_free(config); - label_found = B_TRUE; - } - if (dump_opt['u']) - dump_label_uberblocks(&label, ashift); - } - - (void) close(fd); - - return (label_found ? 0 : 2); -} - -static uint64_t dataset_feature_count[SPA_FEATURES]; -static uint64_t remap_deadlist_count = 0; - -/*ARGSUSED*/ -static int -dump_one_dir(const char *dsname, void *arg) -{ - int error; - objset_t *os; - - error = open_objset(dsname, DMU_OST_ANY, FTAG, &os); - if (error != 0) - return (0); - - for (spa_feature_t f = 0; f < SPA_FEATURES; f++) { - if (!dmu_objset_ds(os)->ds_feature_inuse[f]) - continue; - ASSERT(spa_feature_table[f].fi_flags & - ZFEATURE_FLAG_PER_DATASET); - dataset_feature_count[f]++; - } - - if (dsl_dataset_remap_deadlist_exists(dmu_objset_ds(os))) { - remap_deadlist_count++; - } - - dump_dir(os); - close_objset(os, FTAG); - fuid_table_destroy(); - return (0); -} - -/* - * Block statistics. - */ -#define PSIZE_HISTO_SIZE (SPA_OLD_MAXBLOCKSIZE / SPA_MINBLOCKSIZE + 2) -typedef struct zdb_blkstats { - uint64_t zb_asize; - uint64_t zb_lsize; - uint64_t zb_psize; - uint64_t zb_count; - uint64_t zb_gangs; - uint64_t zb_ditto_samevdev; - uint64_t zb_ditto_same_ms; - uint64_t zb_psize_histogram[PSIZE_HISTO_SIZE]; -} zdb_blkstats_t; - -/* - * Extended object types to report deferred frees and dedup auto-ditto blocks. - */ -#define ZDB_OT_DEFERRED (DMU_OT_NUMTYPES + 0) -#define ZDB_OT_DITTO (DMU_OT_NUMTYPES + 1) -#define ZDB_OT_OTHER (DMU_OT_NUMTYPES + 2) -#define ZDB_OT_TOTAL (DMU_OT_NUMTYPES + 3) - -static const char *zdb_ot_extname[] = { - "deferred free", - "dedup ditto", - "other", - "Total", -}; - -#define ZB_TOTAL DN_MAX_LEVELS - -typedef struct zdb_cb { - zdb_blkstats_t zcb_type[ZB_TOTAL + 1][ZDB_OT_TOTAL + 1]; - uint64_t zcb_removing_size; - uint64_t zcb_checkpoint_size; - uint64_t zcb_dedup_asize; - uint64_t zcb_dedup_blocks; - uint64_t zcb_embedded_blocks[NUM_BP_EMBEDDED_TYPES]; - uint64_t zcb_embedded_histogram[NUM_BP_EMBEDDED_TYPES] - [BPE_PAYLOAD_SIZE]; - uint64_t zcb_start; - hrtime_t zcb_lastprint; - uint64_t zcb_totalasize; - uint64_t zcb_errors[256]; - int zcb_readfails; - int zcb_haderrors; - spa_t *zcb_spa; - uint32_t **zcb_vd_obsolete_counts; -} zdb_cb_t; - -/* test if two DVA offsets from same vdev are within the same metaslab */ -static boolean_t -same_metaslab(spa_t *spa, uint64_t vdev, uint64_t off1, uint64_t off2) -{ - vdev_t *vd = vdev_lookup_top(spa, vdev); - uint64_t ms_shift = vd->vdev_ms_shift; - - return ((off1 >> ms_shift) == (off2 >> ms_shift)); -} - -static void -zdb_count_block(zdb_cb_t *zcb, zilog_t *zilog, const blkptr_t *bp, - dmu_object_type_t type) -{ - uint64_t refcnt = 0; - - ASSERT(type < ZDB_OT_TOTAL); - - if (zilog && zil_bp_tree_add(zilog, bp) != 0) - return; - - spa_config_enter(zcb->zcb_spa, SCL_CONFIG, FTAG, RW_READER); - - for (int i = 0; i < 4; i++) { - int l = (i < 2) ? BP_GET_LEVEL(bp) : ZB_TOTAL; - int t = (i & 1) ? type : ZDB_OT_TOTAL; - int equal; - zdb_blkstats_t *zb = &zcb->zcb_type[l][t]; - - zb->zb_asize += BP_GET_ASIZE(bp); - zb->zb_lsize += BP_GET_LSIZE(bp); - zb->zb_psize += BP_GET_PSIZE(bp); - zb->zb_count++; - - /* - * The histogram is only big enough to record blocks up to - * SPA_OLD_MAXBLOCKSIZE; larger blocks go into the last, - * "other", bucket. - */ - unsigned idx = BP_GET_PSIZE(bp) >> SPA_MINBLOCKSHIFT; - idx = MIN(idx, SPA_OLD_MAXBLOCKSIZE / SPA_MINBLOCKSIZE + 1); - zb->zb_psize_histogram[idx]++; - - zb->zb_gangs += BP_COUNT_GANG(bp); - - switch (BP_GET_NDVAS(bp)) { - case 2: - if (DVA_GET_VDEV(&bp->blk_dva[0]) == - DVA_GET_VDEV(&bp->blk_dva[1])) { - zb->zb_ditto_samevdev++; - - if (same_metaslab(zcb->zcb_spa, - DVA_GET_VDEV(&bp->blk_dva[0]), - DVA_GET_OFFSET(&bp->blk_dva[0]), - DVA_GET_OFFSET(&bp->blk_dva[1]))) - zb->zb_ditto_same_ms++; - } - break; - case 3: - equal = (DVA_GET_VDEV(&bp->blk_dva[0]) == - DVA_GET_VDEV(&bp->blk_dva[1])) + - (DVA_GET_VDEV(&bp->blk_dva[0]) == - DVA_GET_VDEV(&bp->blk_dva[2])) + - (DVA_GET_VDEV(&bp->blk_dva[1]) == - DVA_GET_VDEV(&bp->blk_dva[2])); - if (equal != 0) { - zb->zb_ditto_samevdev++; - - if (DVA_GET_VDEV(&bp->blk_dva[0]) == - DVA_GET_VDEV(&bp->blk_dva[1]) && - same_metaslab(zcb->zcb_spa, - DVA_GET_VDEV(&bp->blk_dva[0]), - DVA_GET_OFFSET(&bp->blk_dva[0]), - DVA_GET_OFFSET(&bp->blk_dva[1]))) - zb->zb_ditto_same_ms++; - else if (DVA_GET_VDEV(&bp->blk_dva[0]) == - DVA_GET_VDEV(&bp->blk_dva[2]) && - same_metaslab(zcb->zcb_spa, - DVA_GET_VDEV(&bp->blk_dva[0]), - DVA_GET_OFFSET(&bp->blk_dva[0]), - DVA_GET_OFFSET(&bp->blk_dva[2]))) - zb->zb_ditto_same_ms++; - else if (DVA_GET_VDEV(&bp->blk_dva[1]) == - DVA_GET_VDEV(&bp->blk_dva[2]) && - same_metaslab(zcb->zcb_spa, - DVA_GET_VDEV(&bp->blk_dva[1]), - DVA_GET_OFFSET(&bp->blk_dva[1]), - DVA_GET_OFFSET(&bp->blk_dva[2]))) - zb->zb_ditto_same_ms++; - } - break; - } - } - - spa_config_exit(zcb->zcb_spa, SCL_CONFIG, FTAG); - - if (BP_IS_EMBEDDED(bp)) { - zcb->zcb_embedded_blocks[BPE_GET_ETYPE(bp)]++; - zcb->zcb_embedded_histogram[BPE_GET_ETYPE(bp)] - [BPE_GET_PSIZE(bp)]++; - return; - } - - if (dump_opt['L']) - return; - - if (BP_GET_DEDUP(bp)) { - ddt_t *ddt; - ddt_entry_t *dde; - - ddt = ddt_select(zcb->zcb_spa, bp); - ddt_enter(ddt); - dde = ddt_lookup(ddt, bp, B_FALSE); - - if (dde == NULL) { - refcnt = 0; - } else { - ddt_phys_t *ddp = ddt_phys_select(dde, bp); - ddt_phys_decref(ddp); - refcnt = ddp->ddp_refcnt; - if (ddt_phys_total_refcnt(dde) == 0) - ddt_remove(ddt, dde); - } - ddt_exit(ddt); - } - - VERIFY3U(zio_wait(zio_claim(NULL, zcb->zcb_spa, - refcnt ? 0 : spa_min_claim_txg(zcb->zcb_spa), - bp, NULL, NULL, ZIO_FLAG_CANFAIL)), ==, 0); -} - -/* ARGSUSED */ -static void -zdb_blkptr_done(zio_t *zio) -{ - spa_t *spa = zio->io_spa; - blkptr_t *bp = zio->io_bp; - int ioerr = zio->io_error; - zdb_cb_t *zcb = zio->io_private; - zbookmark_phys_t *zb = &zio->io_bookmark; - - abd_free(zio->io_abd); - - mutex_enter(&spa->spa_scrub_lock); - spa->spa_scrub_inflight--; - spa->spa_load_verify_ios--; - cv_broadcast(&spa->spa_scrub_io_cv); - - if (ioerr && !(zio->io_flags & ZIO_FLAG_SPECULATIVE)) { - char blkbuf[BP_SPRINTF_LEN]; - - zcb->zcb_haderrors = 1; - zcb->zcb_errors[ioerr]++; - - if (dump_opt['b'] >= 2) - snprintf_blkptr(blkbuf, sizeof (blkbuf), bp); - else - blkbuf[0] = '\0'; - - (void) printf("zdb_blkptr_cb: " - "Got error %d reading " - "<%llu, %llu, %lld, %llx> %s -- skipping\n", - ioerr, - (u_longlong_t)zb->zb_objset, - (u_longlong_t)zb->zb_object, - (u_longlong_t)zb->zb_level, - (u_longlong_t)zb->zb_blkid, - blkbuf); - } - mutex_exit(&spa->spa_scrub_lock); -} - -/* ARGSUSED */ -static int -zdb_blkptr_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, - const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg) -{ - zdb_cb_t *zcb = arg; - dmu_object_type_t type; - boolean_t is_metadata; - - if (bp == NULL) - return (0); - - if (dump_opt['b'] >= 5 && bp->blk_birth > 0) { - char blkbuf[BP_SPRINTF_LEN]; - snprintf_blkptr(blkbuf, sizeof (blkbuf), bp); - (void) printf("objset %llu object %llu " - "level %lld offset 0x%llx %s\n", - (u_longlong_t)zb->zb_objset, - (u_longlong_t)zb->zb_object, - (longlong_t)zb->zb_level, - (u_longlong_t)blkid2offset(dnp, bp, zb), - blkbuf); - } - - if (BP_IS_HOLE(bp)) - return (0); - - type = BP_GET_TYPE(bp); - - zdb_count_block(zcb, zilog, bp, - (type & DMU_OT_NEWTYPE) ? ZDB_OT_OTHER : type); - - is_metadata = (BP_GET_LEVEL(bp) != 0 || DMU_OT_IS_METADATA(type)); - - if (!BP_IS_EMBEDDED(bp) && - (dump_opt['c'] > 1 || (dump_opt['c'] && is_metadata))) { - size_t size = BP_GET_PSIZE(bp); - abd_t *abd = abd_alloc(size, B_FALSE); - int flags = ZIO_FLAG_CANFAIL | ZIO_FLAG_SCRUB | ZIO_FLAG_RAW; - - /* If it's an intent log block, failure is expected. */ - if (zb->zb_level == ZB_ZIL_LEVEL) - flags |= ZIO_FLAG_SPECULATIVE; - - mutex_enter(&spa->spa_scrub_lock); - while (spa->spa_load_verify_ios > max_inflight) - cv_wait(&spa->spa_scrub_io_cv, &spa->spa_scrub_lock); - spa->spa_scrub_inflight++; - spa->spa_load_verify_ios++; - mutex_exit(&spa->spa_scrub_lock); - - zio_nowait(zio_read(NULL, spa, bp, abd, size, - zdb_blkptr_done, zcb, ZIO_PRIORITY_ASYNC_READ, flags, zb)); - } - - zcb->zcb_readfails = 0; - - /* only call gethrtime() every 100 blocks */ - static int iters; - if (++iters > 100) - iters = 0; - else - return (0); - - if (dump_opt['b'] < 5 && gethrtime() > zcb->zcb_lastprint + NANOSEC) { - uint64_t now = gethrtime(); - char buf[10]; - uint64_t bytes = zcb->zcb_type[ZB_TOTAL][ZDB_OT_TOTAL].zb_asize; - int kb_per_sec = - 1 + bytes / (1 + ((now - zcb->zcb_start) / 1000 / 1000)); - int sec_remaining = - (zcb->zcb_totalasize - bytes) / 1024 / kb_per_sec; - - /* make sure nicenum has enough space */ - CTASSERT(sizeof (buf) >= NN_NUMBUF_SZ); - - zfs_nicenum(bytes, buf, sizeof (buf)); - (void) fprintf(stderr, - "\r%5s completed (%4dMB/s) " - "estimated time remaining: %uhr %02umin %02usec ", - buf, kb_per_sec / 1024, - sec_remaining / 60 / 60, - sec_remaining / 60 % 60, - sec_remaining % 60); - - zcb->zcb_lastprint = now; - } - - return (0); -} - -static void -zdb_leak(void *arg, uint64_t start, uint64_t size) -{ - vdev_t *vd = arg; - - (void) printf("leaked space: vdev %llu, offset 0x%llx, size %llu\n", - (u_longlong_t)vd->vdev_id, (u_longlong_t)start, (u_longlong_t)size); -} - -static metaslab_ops_t zdb_metaslab_ops = { - NULL /* alloc */ -}; - -static void -zdb_ddt_leak_init(spa_t *spa, zdb_cb_t *zcb) -{ - ddt_bookmark_t ddb; - ddt_entry_t dde; - int error; - - ASSERT(!dump_opt['L']); - - bzero(&ddb, sizeof (ddb)); - while ((error = ddt_walk(spa, &ddb, &dde)) == 0) { - blkptr_t blk; - ddt_phys_t *ddp = dde.dde_phys; - - if (ddb.ddb_class == DDT_CLASS_UNIQUE) - return; - - ASSERT(ddt_phys_total_refcnt(&dde) > 1); - - for (int p = 0; p < DDT_PHYS_TYPES; p++, ddp++) { - if (ddp->ddp_phys_birth == 0) - continue; - ddt_bp_create(ddb.ddb_checksum, - &dde.dde_key, ddp, &blk); - if (p == DDT_PHYS_DITTO) { - zdb_count_block(zcb, NULL, &blk, ZDB_OT_DITTO); - } else { - zcb->zcb_dedup_asize += - BP_GET_ASIZE(&blk) * (ddp->ddp_refcnt - 1); - zcb->zcb_dedup_blocks++; - } - } - ddt_t *ddt = spa->spa_ddt[ddb.ddb_checksum]; - ddt_enter(ddt); - VERIFY(ddt_lookup(ddt, &blk, B_TRUE) != NULL); - ddt_exit(ddt); - } - - ASSERT(error == ENOENT); -} - -/* ARGSUSED */ -static void -claim_segment_impl_cb(uint64_t inner_offset, vdev_t *vd, uint64_t offset, - uint64_t size, void *arg) -{ - /* - * This callback was called through a remap from - * a device being removed. Therefore, the vdev that - * this callback is applied to is a concrete - * vdev. - */ - ASSERT(vdev_is_concrete(vd)); - - VERIFY0(metaslab_claim_impl(vd, offset, size, - spa_min_claim_txg(vd->vdev_spa))); -} - -static void -claim_segment_cb(void *arg, uint64_t offset, uint64_t size) -{ - vdev_t *vd = arg; - - vdev_indirect_ops.vdev_op_remap(vd, offset, size, - claim_segment_impl_cb, NULL); -} - -/* - * After accounting for all allocated blocks that are directly referenced, - * we might have missed a reference to a block from a partially complete - * (and thus unused) indirect mapping object. We perform a secondary pass - * through the metaslabs we have already mapped and claim the destination - * blocks. - */ -static void -zdb_claim_removing(spa_t *spa, zdb_cb_t *zcb) -{ - if (dump_opt['L']) - return; - - if (spa->spa_vdev_removal == NULL) - return; - - spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER); - - spa_vdev_removal_t *svr = spa->spa_vdev_removal; - vdev_t *vd = vdev_lookup_top(spa, svr->svr_vdev_id); - vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping; - - for (uint64_t msi = 0; msi < vd->vdev_ms_count; msi++) { - metaslab_t *msp = vd->vdev_ms[msi]; - - if (msp->ms_start >= vdev_indirect_mapping_max_offset(vim)) - break; - - ASSERT0(range_tree_space(svr->svr_allocd_segs)); - - if (msp->ms_sm != NULL) { - VERIFY0(space_map_load(msp->ms_sm, - svr->svr_allocd_segs, SM_ALLOC)); - - /* - * Clear everything past what has been synced unless - * it's past the spacemap, because we have not allocated - * mappings for it yet. - */ - uint64_t vim_max_offset = - vdev_indirect_mapping_max_offset(vim); - uint64_t sm_end = msp->ms_sm->sm_start + - msp->ms_sm->sm_size; - if (sm_end > vim_max_offset) - range_tree_clear(svr->svr_allocd_segs, - vim_max_offset, sm_end - vim_max_offset); - } - - zcb->zcb_removing_size += - range_tree_space(svr->svr_allocd_segs); - range_tree_vacate(svr->svr_allocd_segs, claim_segment_cb, vd); - } - - spa_config_exit(spa, SCL_CONFIG, FTAG); -} - -/* ARGSUSED */ -static int -increment_indirect_mapping_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx) -{ - zdb_cb_t *zcb = arg; - spa_t *spa = zcb->zcb_spa; - vdev_t *vd; - const dva_t *dva = &bp->blk_dva[0]; - - ASSERT(!dump_opt['L']); - ASSERT3U(BP_GET_NDVAS(bp), ==, 1); - - spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER); - vd = vdev_lookup_top(zcb->zcb_spa, DVA_GET_VDEV(dva)); - ASSERT3P(vd, !=, NULL); - spa_config_exit(spa, SCL_VDEV, FTAG); - - ASSERT(vd->vdev_indirect_config.vic_mapping_object != 0); - ASSERT3P(zcb->zcb_vd_obsolete_counts[vd->vdev_id], !=, NULL); - - vdev_indirect_mapping_increment_obsolete_count( - vd->vdev_indirect_mapping, - DVA_GET_OFFSET(dva), DVA_GET_ASIZE(dva), - zcb->zcb_vd_obsolete_counts[vd->vdev_id]); - - return (0); -} - -static uint32_t * -zdb_load_obsolete_counts(vdev_t *vd) -{ - vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping; - spa_t *spa = vd->vdev_spa; - spa_condensing_indirect_phys_t *scip = - &spa->spa_condensing_indirect_phys; - uint32_t *counts; - - EQUIV(vdev_obsolete_sm_object(vd) != 0, vd->vdev_obsolete_sm != NULL); - counts = vdev_indirect_mapping_load_obsolete_counts(vim); - if (vd->vdev_obsolete_sm != NULL) { - vdev_indirect_mapping_load_obsolete_spacemap(vim, counts, - vd->vdev_obsolete_sm); - } - if (scip->scip_vdev == vd->vdev_id && - scip->scip_prev_obsolete_sm_object != 0) { - space_map_t *prev_obsolete_sm = NULL; - VERIFY0(space_map_open(&prev_obsolete_sm, spa->spa_meta_objset, - scip->scip_prev_obsolete_sm_object, 0, vd->vdev_asize, 0)); - vdev_indirect_mapping_load_obsolete_spacemap(vim, counts, - prev_obsolete_sm); - space_map_close(prev_obsolete_sm); - } - return (counts); -} - -typedef struct checkpoint_sm_exclude_entry_arg { - vdev_t *cseea_vd; - uint64_t cseea_checkpoint_size; -} checkpoint_sm_exclude_entry_arg_t; - -static int -checkpoint_sm_exclude_entry_cb(space_map_entry_t *sme, void *arg) -{ - checkpoint_sm_exclude_entry_arg_t *cseea = arg; - vdev_t *vd = cseea->cseea_vd; - metaslab_t *ms = vd->vdev_ms[sme->sme_offset >> vd->vdev_ms_shift]; - uint64_t end = sme->sme_offset + sme->sme_run; - - ASSERT(sme->sme_type == SM_FREE); - - /* - * Since the vdev_checkpoint_sm exists in the vdev level - * and the ms_sm space maps exist in the metaslab level, - * an entry in the checkpoint space map could theoretically - * cross the boundaries of the metaslab that it belongs. - * - * In reality, because of the way that we populate and - * manipulate the checkpoint's space maps currently, - * there shouldn't be any entries that cross metaslabs. - * Hence the assertion below. - * - * That said, there is no fundamental requirement that - * the checkpoint's space map entries should not cross - * metaslab boundaries. So if needed we could add code - * that handles metaslab-crossing segments in the future. - */ - VERIFY3U(sme->sme_offset, >=, ms->ms_start); - VERIFY3U(end, <=, ms->ms_start + ms->ms_size); - - /* - * By removing the entry from the allocated segments we - * also verify that the entry is there to begin with. - */ - mutex_enter(&ms->ms_lock); - range_tree_remove(ms->ms_allocatable, sme->sme_offset, sme->sme_run); - mutex_exit(&ms->ms_lock); - - cseea->cseea_checkpoint_size += sme->sme_run; - return (0); -} - -static void -zdb_leak_init_vdev_exclude_checkpoint(vdev_t *vd, zdb_cb_t *zcb) -{ - spa_t *spa = vd->vdev_spa; - space_map_t *checkpoint_sm = NULL; - uint64_t checkpoint_sm_obj; - - /* - * If there is no vdev_top_zap, we are in a pool whose - * version predates the pool checkpoint feature. - */ - if (vd->vdev_top_zap == 0) - return; - - /* - * If there is no reference of the vdev_checkpoint_sm in - * the vdev_top_zap, then one of the following scenarios - * is true: - * - * 1] There is no checkpoint - * 2] There is a checkpoint, but no checkpointed blocks - * have been freed yet - * 3] The current vdev is indirect - * - * In these cases we return immediately. - */ - if (zap_contains(spa_meta_objset(spa), vd->vdev_top_zap, - VDEV_TOP_ZAP_POOL_CHECKPOINT_SM) != 0) - return; - - VERIFY0(zap_lookup(spa_meta_objset(spa), vd->vdev_top_zap, - VDEV_TOP_ZAP_POOL_CHECKPOINT_SM, sizeof (uint64_t), 1, - &checkpoint_sm_obj)); - - checkpoint_sm_exclude_entry_arg_t cseea; - cseea.cseea_vd = vd; - cseea.cseea_checkpoint_size = 0; - - VERIFY0(space_map_open(&checkpoint_sm, spa_meta_objset(spa), - checkpoint_sm_obj, 0, vd->vdev_asize, vd->vdev_ashift)); - - VERIFY0(space_map_iterate(checkpoint_sm, - space_map_length(checkpoint_sm), - checkpoint_sm_exclude_entry_cb, &cseea)); - space_map_close(checkpoint_sm); - - zcb->zcb_checkpoint_size += cseea.cseea_checkpoint_size; -} - -static void -zdb_leak_init_exclude_checkpoint(spa_t *spa, zdb_cb_t *zcb) -{ - ASSERT(!dump_opt['L']); - - vdev_t *rvd = spa->spa_root_vdev; - for (uint64_t c = 0; c < rvd->vdev_children; c++) { - ASSERT3U(c, ==, rvd->vdev_child[c]->vdev_id); - zdb_leak_init_vdev_exclude_checkpoint(rvd->vdev_child[c], zcb); - } -} - -static void -load_concrete_ms_allocatable_trees(spa_t *spa, maptype_t maptype) -{ - vdev_t *rvd = spa->spa_root_vdev; - for (uint64_t i = 0; i < rvd->vdev_children; i++) { - vdev_t *vd = rvd->vdev_child[i]; - - ASSERT3U(i, ==, vd->vdev_id); - - if (vd->vdev_ops == &vdev_indirect_ops) - continue; - - for (uint64_t m = 0; m < vd->vdev_ms_count; m++) { - metaslab_t *msp = vd->vdev_ms[m]; - - (void) fprintf(stderr, - "\rloading concrete vdev %llu, " - "metaslab %llu of %llu ...", - (longlong_t)vd->vdev_id, - (longlong_t)msp->ms_id, - (longlong_t)vd->vdev_ms_count); - - mutex_enter(&msp->ms_lock); - metaslab_unload(msp); - - /* - * We don't want to spend the CPU manipulating the - * size-ordered tree, so clear the range_tree ops. - */ - msp->ms_allocatable->rt_ops = NULL; - - if (msp->ms_sm != NULL) { - VERIFY0(space_map_load(msp->ms_sm, - msp->ms_allocatable, maptype)); - } - if (!msp->ms_loaded) - msp->ms_loaded = B_TRUE; - mutex_exit(&msp->ms_lock); - } - } -} - -/* - * vm_idxp is an in-out parameter which (for indirect vdevs) is the - * index in vim_entries that has the first entry in this metaslab. - * On return, it will be set to the first entry after this metaslab. - */ -static void -load_indirect_ms_allocatable_tree(vdev_t *vd, metaslab_t *msp, - uint64_t *vim_idxp) -{ - vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping; - - mutex_enter(&msp->ms_lock); - metaslab_unload(msp); - - /* - * We don't want to spend the CPU manipulating the - * size-ordered tree, so clear the range_tree ops. - */ - msp->ms_allocatable->rt_ops = NULL; - - for (; *vim_idxp < vdev_indirect_mapping_num_entries(vim); - (*vim_idxp)++) { - vdev_indirect_mapping_entry_phys_t *vimep = - &vim->vim_entries[*vim_idxp]; - uint64_t ent_offset = DVA_MAPPING_GET_SRC_OFFSET(vimep); - uint64_t ent_len = DVA_GET_ASIZE(&vimep->vimep_dst); - ASSERT3U(ent_offset, >=, msp->ms_start); - if (ent_offset >= msp->ms_start + msp->ms_size) - break; - - /* - * Mappings do not cross metaslab boundaries, - * because we create them by walking the metaslabs. - */ - ASSERT3U(ent_offset + ent_len, <=, - msp->ms_start + msp->ms_size); - range_tree_add(msp->ms_allocatable, ent_offset, ent_len); - } - - if (!msp->ms_loaded) - msp->ms_loaded = B_TRUE; - mutex_exit(&msp->ms_lock); -} - -static void -zdb_leak_init_prepare_indirect_vdevs(spa_t *spa, zdb_cb_t *zcb) -{ - ASSERT(!dump_opt['L']); - - vdev_t *rvd = spa->spa_root_vdev; - for (uint64_t c = 0; c < rvd->vdev_children; c++) { - vdev_t *vd = rvd->vdev_child[c]; - - ASSERT3U(c, ==, vd->vdev_id); - - if (vd->vdev_ops != &vdev_indirect_ops) - continue; - - /* - * Note: we don't check for mapping leaks on - * removing vdevs because their ms_allocatable's - * are used to look for leaks in allocated space. - */ - zcb->zcb_vd_obsolete_counts[c] = zdb_load_obsolete_counts(vd); - - /* - * Normally, indirect vdevs don't have any - * metaslabs. We want to set them up for - * zio_claim(). - */ - VERIFY0(vdev_metaslab_init(vd, 0)); - - vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping; - uint64_t vim_idx = 0; - for (uint64_t m = 0; m < vd->vdev_ms_count; m++) { - - (void) fprintf(stderr, - "\rloading indirect vdev %llu, " - "metaslab %llu of %llu ...", - (longlong_t)vd->vdev_id, - (longlong_t)vd->vdev_ms[m]->ms_id, - (longlong_t)vd->vdev_ms_count); - - load_indirect_ms_allocatable_tree(vd, vd->vdev_ms[m], - &vim_idx); - } - ASSERT3U(vim_idx, ==, vdev_indirect_mapping_num_entries(vim)); - } -} - -static void -zdb_leak_init(spa_t *spa, zdb_cb_t *zcb) -{ - zcb->zcb_spa = spa; - - if (dump_opt['L']) - return; - - dsl_pool_t *dp = spa->spa_dsl_pool; - vdev_t *rvd = spa->spa_root_vdev; - - /* - * We are going to be changing the meaning of the metaslab's - * ms_allocatable. Ensure that the allocator doesn't try to - * use the tree. - */ - spa->spa_normal_class->mc_ops = &zdb_metaslab_ops; - spa->spa_log_class->mc_ops = &zdb_metaslab_ops; - - zcb->zcb_vd_obsolete_counts = - umem_zalloc(rvd->vdev_children * sizeof (uint32_t *), - UMEM_NOFAIL); - - /* - * For leak detection, we overload the ms_allocatable trees - * to contain allocated segments instead of free segments. - * As a result, we can't use the normal metaslab_load/unload - * interfaces. - */ - zdb_leak_init_prepare_indirect_vdevs(spa, zcb); - load_concrete_ms_allocatable_trees(spa, SM_ALLOC); - - /* - * On load_concrete_ms_allocatable_trees() we loaded all the - * allocated entries from the ms_sm to the ms_allocatable for - * each metaslab. If the pool has a checkpoint or is in the - * middle of discarding a checkpoint, some of these blocks - * may have been freed but their ms_sm may not have been - * updated because they are referenced by the checkpoint. In - * order to avoid false-positives during leak-detection, we - * go through the vdev's checkpoint space map and exclude all - * its entries from their relevant ms_allocatable. - * - * We also aggregate the space held by the checkpoint and add - * it to zcb_checkpoint_size. - * - * Note that at this point we are also verifying that all the - * entries on the checkpoint_sm are marked as allocated in - * the ms_sm of their relevant metaslab. - * [see comment in checkpoint_sm_exclude_entry_cb()] - */ - zdb_leak_init_exclude_checkpoint(spa, zcb); - ASSERT3U(zcb->zcb_checkpoint_size, ==, spa_get_checkpoint_space(spa)); - - /* for cleaner progress output */ - (void) fprintf(stderr, "\n"); - - if (bpobj_is_open(&dp->dp_obsolete_bpobj)) { - ASSERT(spa_feature_is_enabled(spa, - SPA_FEATURE_DEVICE_REMOVAL)); - (void) bpobj_iterate_nofree(&dp->dp_obsolete_bpobj, - increment_indirect_mapping_cb, zcb, NULL); - } - - spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER); - zdb_ddt_leak_init(spa, zcb); - spa_config_exit(spa, SCL_CONFIG, FTAG); -} - -static boolean_t -zdb_check_for_obsolete_leaks(vdev_t *vd, zdb_cb_t *zcb) -{ - boolean_t leaks = B_FALSE; - vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping; - uint64_t total_leaked = 0; - - ASSERT(vim != NULL); - - for (uint64_t i = 0; i < vdev_indirect_mapping_num_entries(vim); i++) { - vdev_indirect_mapping_entry_phys_t *vimep = - &vim->vim_entries[i]; - uint64_t obsolete_bytes = 0; - uint64_t offset = DVA_MAPPING_GET_SRC_OFFSET(vimep); - metaslab_t *msp = vd->vdev_ms[offset >> vd->vdev_ms_shift]; - - /* - * This is not very efficient but it's easy to - * verify correctness. - */ - for (uint64_t inner_offset = 0; - inner_offset < DVA_GET_ASIZE(&vimep->vimep_dst); - inner_offset += 1 << vd->vdev_ashift) { - if (range_tree_contains(msp->ms_allocatable, - offset + inner_offset, 1 << vd->vdev_ashift)) { - obsolete_bytes += 1 << vd->vdev_ashift; - } - } - - int64_t bytes_leaked = obsolete_bytes - - zcb->zcb_vd_obsolete_counts[vd->vdev_id][i]; - ASSERT3U(DVA_GET_ASIZE(&vimep->vimep_dst), >=, - zcb->zcb_vd_obsolete_counts[vd->vdev_id][i]); - if (bytes_leaked != 0 && - (vdev_obsolete_counts_are_precise(vd) || - dump_opt['d'] >= 5)) { - (void) printf("obsolete indirect mapping count " - "mismatch on %llu:%llx:%llx : %llx bytes leaked\n", - (u_longlong_t)vd->vdev_id, - (u_longlong_t)DVA_MAPPING_GET_SRC_OFFSET(vimep), - (u_longlong_t)DVA_GET_ASIZE(&vimep->vimep_dst), - (u_longlong_t)bytes_leaked); - } - total_leaked += ABS(bytes_leaked); - } - - if (!vdev_obsolete_counts_are_precise(vd) && total_leaked > 0) { - int pct_leaked = total_leaked * 100 / - vdev_indirect_mapping_bytes_mapped(vim); - (void) printf("cannot verify obsolete indirect mapping " - "counts of vdev %llu because precise feature was not " - "enabled when it was removed: %d%% (%llx bytes) of mapping" - "unreferenced\n", - (u_longlong_t)vd->vdev_id, pct_leaked, - (u_longlong_t)total_leaked); - } else if (total_leaked > 0) { - (void) printf("obsolete indirect mapping count mismatch " - "for vdev %llu -- %llx total bytes mismatched\n", - (u_longlong_t)vd->vdev_id, - (u_longlong_t)total_leaked); - leaks |= B_TRUE; - } - - vdev_indirect_mapping_free_obsolete_counts(vim, - zcb->zcb_vd_obsolete_counts[vd->vdev_id]); - zcb->zcb_vd_obsolete_counts[vd->vdev_id] = NULL; - - return (leaks); -} - -static boolean_t -zdb_leak_fini(spa_t *spa, zdb_cb_t *zcb) -{ - if (dump_opt['L']) - return (B_FALSE); - - boolean_t leaks = B_FALSE; - - vdev_t *rvd = spa->spa_root_vdev; - for (unsigned c = 0; c < rvd->vdev_children; c++) { - vdev_t *vd = rvd->vdev_child[c]; -#if DEBUG - metaslab_group_t *mg = vd->vdev_mg; -#endif - - if (zcb->zcb_vd_obsolete_counts[c] != NULL) { - leaks |= zdb_check_for_obsolete_leaks(vd, zcb); - } - - for (uint64_t m = 0; m < vd->vdev_ms_count; m++) { - metaslab_t *msp = vd->vdev_ms[m]; - ASSERT3P(mg, ==, msp->ms_group); - - /* - * ms_allocatable has been overloaded - * to contain allocated segments. Now that - * we finished traversing all blocks, any - * block that remains in the ms_allocatable - * represents an allocated block that we - * did not claim during the traversal. - * Claimed blocks would have been removed - * from the ms_allocatable. For indirect - * vdevs, space remaining in the tree - * represents parts of the mapping that are - * not referenced, which is not a bug. - */ - if (vd->vdev_ops == &vdev_indirect_ops) { - range_tree_vacate(msp->ms_allocatable, - NULL, NULL); - } else { - range_tree_vacate(msp->ms_allocatable, - zdb_leak, vd); - } - - if (msp->ms_loaded) { - msp->ms_loaded = B_FALSE; - } - } - - } - - umem_free(zcb->zcb_vd_obsolete_counts, - rvd->vdev_children * sizeof (uint32_t *)); - zcb->zcb_vd_obsolete_counts = NULL; - - return (leaks); -} - -/* ARGSUSED */ -static int -count_block_cb(void *arg, const blkptr_t *bp, dmu_tx_t *tx) -{ - zdb_cb_t *zcb = arg; - - if (dump_opt['b'] >= 5) { - char blkbuf[BP_SPRINTF_LEN]; - snprintf_blkptr(blkbuf, sizeof (blkbuf), bp); - (void) printf("[%s] %s\n", - "deferred free", blkbuf); - } - zdb_count_block(zcb, NULL, bp, ZDB_OT_DEFERRED); - return (0); -} - -static int -dump_block_stats(spa_t *spa) -{ - zdb_cb_t zcb; - zdb_blkstats_t *zb, *tzb; - uint64_t norm_alloc, norm_space, total_alloc, total_found; - int flags = TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA | TRAVERSE_HARD; - boolean_t leaks = B_FALSE; - int err; - - bzero(&zcb, sizeof (zcb)); - (void) printf("\nTraversing all blocks %s%s%s%s%s...\n\n", - (dump_opt['c'] || !dump_opt['L']) ? "to verify " : "", - (dump_opt['c'] == 1) ? "metadata " : "", - dump_opt['c'] ? "checksums " : "", - (dump_opt['c'] && !dump_opt['L']) ? "and verify " : "", - !dump_opt['L'] ? "nothing leaked " : ""); - - /* - * When leak detection is enabled we load all space maps as SM_ALLOC - * maps, then traverse the pool claiming each block we discover. If - * the pool is perfectly consistent, the segment trees will be empty - * when we're done. Anything left over is a leak; any block we can't - * claim (because it's not part of any space map) is a double - * allocation, reference to a freed block, or an unclaimed log block. - * - * When leak detection is disabled (-L option) we still traverse the - * pool claiming each block we discover, but we skip opening any space - * maps. - */ - bzero(&zcb, sizeof (zdb_cb_t)); - zdb_leak_init(spa, &zcb); - - /* - * If there's a deferred-free bplist, process that first. - */ - (void) bpobj_iterate_nofree(&spa->spa_deferred_bpobj, - count_block_cb, &zcb, NULL); - - if (spa_version(spa) >= SPA_VERSION_DEADLISTS) { - (void) bpobj_iterate_nofree(&spa->spa_dsl_pool->dp_free_bpobj, - count_block_cb, &zcb, NULL); - } - - zdb_claim_removing(spa, &zcb); - - if (spa_feature_is_active(spa, SPA_FEATURE_ASYNC_DESTROY)) { - VERIFY3U(0, ==, bptree_iterate(spa->spa_meta_objset, - spa->spa_dsl_pool->dp_bptree_obj, B_FALSE, count_block_cb, - &zcb, NULL)); - } - - if (dump_opt['c'] > 1) - flags |= TRAVERSE_PREFETCH_DATA; - - zcb.zcb_totalasize = metaslab_class_get_alloc(spa_normal_class(spa)); - zcb.zcb_totalasize += metaslab_class_get_alloc(spa_special_class(spa)); - zcb.zcb_totalasize += metaslab_class_get_alloc(spa_dedup_class(spa)); - zcb.zcb_start = zcb.zcb_lastprint = gethrtime(); - err = traverse_pool(spa, 0, flags, zdb_blkptr_cb, &zcb); - - /* - * If we've traversed the data blocks then we need to wait for those - * I/Os to complete. We leverage "The Godfather" zio to wait on - * all async I/Os to complete. - */ - if (dump_opt['c']) { - for (int i = 0; i < max_ncpus; i++) { - (void) zio_wait(spa->spa_async_zio_root[i]); - spa->spa_async_zio_root[i] = zio_root(spa, NULL, NULL, - ZIO_FLAG_CANFAIL | ZIO_FLAG_SPECULATIVE | - ZIO_FLAG_GODFATHER); - } - } - - /* - * Done after zio_wait() since zcb_haderrors is modified in - * zdb_blkptr_done() - */ - zcb.zcb_haderrors |= err; - - if (zcb.zcb_haderrors) { - (void) printf("\nError counts:\n\n"); - (void) printf("\t%5s %s\n", "errno", "count"); - for (int e = 0; e < 256; e++) { - if (zcb.zcb_errors[e] != 0) { - (void) printf("\t%5d %llu\n", - e, (u_longlong_t)zcb.zcb_errors[e]); - } - } - } - - /* - * Report any leaked segments. - */ - leaks |= zdb_leak_fini(spa, &zcb); - - tzb = &zcb.zcb_type[ZB_TOTAL][ZDB_OT_TOTAL]; - - norm_alloc = metaslab_class_get_alloc(spa_normal_class(spa)); - norm_space = metaslab_class_get_space(spa_normal_class(spa)); - - total_alloc = norm_alloc + - metaslab_class_get_alloc(spa_log_class(spa)) + - metaslab_class_get_alloc(spa_special_class(spa)) + - metaslab_class_get_alloc(spa_dedup_class(spa)); - total_found = tzb->zb_asize - zcb.zcb_dedup_asize + - zcb.zcb_removing_size + zcb.zcb_checkpoint_size; - - if (total_found == total_alloc && !dump_opt['L']) { - (void) printf("\n\tNo leaks (block sum matches space" - " maps exactly)\n"); - } else if (!dump_opt['L']) { - (void) printf("block traversal size %llu != alloc %llu " - "(%s %lld)\n", - (u_longlong_t)total_found, - (u_longlong_t)total_alloc, - (dump_opt['L']) ? "unreachable" : "leaked", - (longlong_t)(total_alloc - total_found)); - leaks = B_TRUE; - } - - if (tzb->zb_count == 0) - return (2); - - (void) printf("\n"); - (void) printf("\t%-16s %14llu\n", "bp count:", - (u_longlong_t)tzb->zb_count); - (void) printf("\t%-16s %14llu\n", "ganged count:", - (longlong_t)tzb->zb_gangs); - (void) printf("\t%-16s %14llu avg: %6llu\n", "bp logical:", - (u_longlong_t)tzb->zb_lsize, - (u_longlong_t)(tzb->zb_lsize / tzb->zb_count)); - (void) printf("\t%-16s %14llu avg: %6llu compression: %6.2f\n", - "bp physical:", (u_longlong_t)tzb->zb_psize, - (u_longlong_t)(tzb->zb_psize / tzb->zb_count), - (double)tzb->zb_lsize / tzb->zb_psize); - (void) printf("\t%-16s %14llu avg: %6llu compression: %6.2f\n", - "bp allocated:", (u_longlong_t)tzb->zb_asize, - (u_longlong_t)(tzb->zb_asize / tzb->zb_count), - (double)tzb->zb_lsize / tzb->zb_asize); - (void) printf("\t%-16s %14llu ref>1: %6llu deduplication: %6.2f\n", - "bp deduped:", (u_longlong_t)zcb.zcb_dedup_asize, - (u_longlong_t)zcb.zcb_dedup_blocks, - (double)zcb.zcb_dedup_asize / tzb->zb_asize + 1.0); - (void) printf("\t%-16s %14llu used: %5.2f%%\n", "Normal class:", - (u_longlong_t)norm_alloc, 100.0 * norm_alloc / norm_space); - - if (spa_special_class(spa)->mc_rotor != NULL) { - uint64_t alloc = metaslab_class_get_alloc( - spa_special_class(spa)); - uint64_t space = metaslab_class_get_space( - spa_special_class(spa)); - - (void) printf("\t%-16s %14llu used: %5.2f%%\n", - "Special class", (u_longlong_t)alloc, - 100.0 * alloc / space); - } - - if (spa_dedup_class(spa)->mc_rotor != NULL) { - uint64_t alloc = metaslab_class_get_alloc( - spa_dedup_class(spa)); - uint64_t space = metaslab_class_get_space( - spa_dedup_class(spa)); - - (void) printf("\t%-16s %14llu used: %5.2f%%\n", - "Dedup class", (u_longlong_t)alloc, - 100.0 * alloc / space); - } - - for (bp_embedded_type_t i = 0; i < NUM_BP_EMBEDDED_TYPES; i++) { - if (zcb.zcb_embedded_blocks[i] == 0) - continue; - (void) printf("\n"); - (void) printf("\tadditional, non-pointer bps of type %u: " - "%10llu\n", - i, (u_longlong_t)zcb.zcb_embedded_blocks[i]); - - if (dump_opt['b'] >= 3) { - (void) printf("\t number of (compressed) bytes: " - "number of bps\n"); - dump_histogram(zcb.zcb_embedded_histogram[i], - sizeof (zcb.zcb_embedded_histogram[i]) / - sizeof (zcb.zcb_embedded_histogram[i][0]), 0); - } - } - - if (tzb->zb_ditto_samevdev != 0) { - (void) printf("\tDittoed blocks on same vdev: %llu\n", - (longlong_t)tzb->zb_ditto_samevdev); - } - if (tzb->zb_ditto_same_ms != 0) { - (void) printf("\tDittoed blocks in same metaslab: %llu\n", - (longlong_t)tzb->zb_ditto_same_ms); - } - - for (uint64_t v = 0; v < spa->spa_root_vdev->vdev_children; v++) { - vdev_t *vd = spa->spa_root_vdev->vdev_child[v]; - vdev_indirect_mapping_t *vim = vd->vdev_indirect_mapping; - - if (vim == NULL) { - continue; - } - - char mem[32]; - zdb_nicenum(vdev_indirect_mapping_num_entries(vim), - mem, vdev_indirect_mapping_size(vim)); - - (void) printf("\tindirect vdev id %llu has %llu segments " - "(%s in memory)\n", - (longlong_t)vd->vdev_id, - (longlong_t)vdev_indirect_mapping_num_entries(vim), mem); - } - - if (dump_opt['b'] >= 2) { - int l, t, level; - (void) printf("\nBlocks\tLSIZE\tPSIZE\tASIZE" - "\t avg\t comp\t%%Total\tType\n"); - - for (t = 0; t <= ZDB_OT_TOTAL; t++) { - char csize[32], lsize[32], psize[32], asize[32]; - char avg[32], gang[32]; - const char *typename; - - /* make sure nicenum has enough space */ - CTASSERT(sizeof (csize) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (lsize) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (psize) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (asize) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (avg) >= NN_NUMBUF_SZ); - CTASSERT(sizeof (gang) >= NN_NUMBUF_SZ); - - if (t < DMU_OT_NUMTYPES) - typename = dmu_ot[t].ot_name; - else - typename = zdb_ot_extname[t - DMU_OT_NUMTYPES]; - - if (zcb.zcb_type[ZB_TOTAL][t].zb_asize == 0) { - (void) printf("%6s\t%5s\t%5s\t%5s" - "\t%5s\t%5s\t%6s\t%s\n", - "-", - "-", - "-", - "-", - "-", - "-", - "-", - typename); - continue; - } - - for (l = ZB_TOTAL - 1; l >= -1; l--) { - level = (l == -1 ? ZB_TOTAL : l); - zb = &zcb.zcb_type[level][t]; - - if (zb->zb_asize == 0) - continue; - - if (dump_opt['b'] < 3 && level != ZB_TOTAL) - continue; - - if (level == 0 && zb->zb_asize == - zcb.zcb_type[ZB_TOTAL][t].zb_asize) - continue; - - zdb_nicenum(zb->zb_count, csize, - sizeof (csize)); - zdb_nicenum(zb->zb_lsize, lsize, - sizeof (lsize)); - zdb_nicenum(zb->zb_psize, psize, - sizeof (psize)); - zdb_nicenum(zb->zb_asize, asize, - sizeof (asize)); - zdb_nicenum(zb->zb_asize / zb->zb_count, avg, - sizeof (avg)); - zdb_nicenum(zb->zb_gangs, gang, sizeof (gang)); - - (void) printf("%6s\t%5s\t%5s\t%5s\t%5s" - "\t%5.2f\t%6.2f\t", - csize, lsize, psize, asize, avg, - (double)zb->zb_lsize / zb->zb_psize, - 100.0 * zb->zb_asize / tzb->zb_asize); - - if (level == ZB_TOTAL) - (void) printf("%s\n", typename); - else - (void) printf(" L%d %s\n", - level, typename); - - if (dump_opt['b'] >= 3 && zb->zb_gangs > 0) { - (void) printf("\t number of ganged " - "blocks: %s\n", gang); - } - - if (dump_opt['b'] >= 4) { - (void) printf("psize " - "(in 512-byte sectors): " - "number of blocks\n"); - dump_histogram(zb->zb_psize_histogram, - PSIZE_HISTO_SIZE, 0); - } - } - } - } - - (void) printf("\n"); - - if (leaks) - return (2); - - if (zcb.zcb_haderrors) - return (3); - - return (0); -} - -typedef struct zdb_ddt_entry { - ddt_key_t zdde_key; - uint64_t zdde_ref_blocks; - uint64_t zdde_ref_lsize; - uint64_t zdde_ref_psize; - uint64_t zdde_ref_dsize; - avl_node_t zdde_node; -} zdb_ddt_entry_t; - -/* ARGSUSED */ -static int -zdb_ddt_add_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, - const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg) -{ - avl_tree_t *t = arg; - avl_index_t where; - zdb_ddt_entry_t *zdde, zdde_search; - - if (bp == NULL || BP_IS_HOLE(bp) || BP_IS_EMBEDDED(bp)) - return (0); - - if (dump_opt['S'] > 1 && zb->zb_level == ZB_ROOT_LEVEL) { - (void) printf("traversing objset %llu, %llu objects, " - "%lu blocks so far\n", - (u_longlong_t)zb->zb_objset, - (u_longlong_t)BP_GET_FILL(bp), - avl_numnodes(t)); - } - - if (BP_IS_HOLE(bp) || BP_GET_CHECKSUM(bp) == ZIO_CHECKSUM_OFF || - BP_GET_LEVEL(bp) > 0 || DMU_OT_IS_METADATA(BP_GET_TYPE(bp))) - return (0); - - ddt_key_fill(&zdde_search.zdde_key, bp); - - zdde = avl_find(t, &zdde_search, &where); - - if (zdde == NULL) { - zdde = umem_zalloc(sizeof (*zdde), UMEM_NOFAIL); - zdde->zdde_key = zdde_search.zdde_key; - avl_insert(t, zdde, where); - } - - zdde->zdde_ref_blocks += 1; - zdde->zdde_ref_lsize += BP_GET_LSIZE(bp); - zdde->zdde_ref_psize += BP_GET_PSIZE(bp); - zdde->zdde_ref_dsize += bp_get_dsize_sync(spa, bp); - - return (0); -} - -static void -dump_simulated_ddt(spa_t *spa) -{ - avl_tree_t t; - void *cookie = NULL; - zdb_ddt_entry_t *zdde; - ddt_histogram_t ddh_total; - ddt_stat_t dds_total; - - bzero(&ddh_total, sizeof (ddh_total)); - bzero(&dds_total, sizeof (dds_total)); - avl_create(&t, ddt_entry_compare, - sizeof (zdb_ddt_entry_t), offsetof(zdb_ddt_entry_t, zdde_node)); - - spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER); - - (void) traverse_pool(spa, 0, TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA, - zdb_ddt_add_cb, &t); - - spa_config_exit(spa, SCL_CONFIG, FTAG); - - while ((zdde = avl_destroy_nodes(&t, &cookie)) != NULL) { - ddt_stat_t dds; - uint64_t refcnt = zdde->zdde_ref_blocks; - ASSERT(refcnt != 0); - - dds.dds_blocks = zdde->zdde_ref_blocks / refcnt; - dds.dds_lsize = zdde->zdde_ref_lsize / refcnt; - dds.dds_psize = zdde->zdde_ref_psize / refcnt; - dds.dds_dsize = zdde->zdde_ref_dsize / refcnt; - - dds.dds_ref_blocks = zdde->zdde_ref_blocks; - dds.dds_ref_lsize = zdde->zdde_ref_lsize; - dds.dds_ref_psize = zdde->zdde_ref_psize; - dds.dds_ref_dsize = zdde->zdde_ref_dsize; - - ddt_stat_add(&ddh_total.ddh_stat[highbit64(refcnt) - 1], - &dds, 0); - - umem_free(zdde, sizeof (*zdde)); - } - - avl_destroy(&t); - - ddt_histogram_stat(&dds_total, &ddh_total); - - (void) printf("Simulated DDT histogram:\n"); - - zpool_dump_ddt(&dds_total, &ddh_total); - - dump_dedup_ratio(&dds_total); -} - -static int -verify_device_removal_feature_counts(spa_t *spa) -{ - uint64_t dr_feature_refcount = 0; - uint64_t oc_feature_refcount = 0; - uint64_t indirect_vdev_count = 0; - uint64_t precise_vdev_count = 0; - uint64_t obsolete_counts_object_count = 0; - uint64_t obsolete_sm_count = 0; - uint64_t obsolete_counts_count = 0; - uint64_t scip_count = 0; - uint64_t obsolete_bpobj_count = 0; - int ret = 0; - - spa_condensing_indirect_phys_t *scip = - &spa->spa_condensing_indirect_phys; - if (scip->scip_next_mapping_object != 0) { - vdev_t *vd = spa->spa_root_vdev->vdev_child[scip->scip_vdev]; - ASSERT(scip->scip_prev_obsolete_sm_object != 0); - ASSERT3P(vd->vdev_ops, ==, &vdev_indirect_ops); - - (void) printf("Condensing indirect vdev %llu: new mapping " - "object %llu, prev obsolete sm %llu\n", - (u_longlong_t)scip->scip_vdev, - (u_longlong_t)scip->scip_next_mapping_object, - (u_longlong_t)scip->scip_prev_obsolete_sm_object); - if (scip->scip_prev_obsolete_sm_object != 0) { - space_map_t *prev_obsolete_sm = NULL; - VERIFY0(space_map_open(&prev_obsolete_sm, - spa->spa_meta_objset, - scip->scip_prev_obsolete_sm_object, - 0, vd->vdev_asize, 0)); - dump_spacemap(spa->spa_meta_objset, prev_obsolete_sm); - (void) printf("\n"); - space_map_close(prev_obsolete_sm); - } - - scip_count += 2; - } - - for (uint64_t i = 0; i < spa->spa_root_vdev->vdev_children; i++) { - vdev_t *vd = spa->spa_root_vdev->vdev_child[i]; - vdev_indirect_config_t *vic = &vd->vdev_indirect_config; - - if (vic->vic_mapping_object != 0) { - ASSERT(vd->vdev_ops == &vdev_indirect_ops || - vd->vdev_removing); - indirect_vdev_count++; - - if (vd->vdev_indirect_mapping->vim_havecounts) { - obsolete_counts_count++; - } - } - if (vdev_obsolete_counts_are_precise(vd)) { - ASSERT(vic->vic_mapping_object != 0); - precise_vdev_count++; - } - if (vdev_obsolete_sm_object(vd) != 0) { - ASSERT(vic->vic_mapping_object != 0); - obsolete_sm_count++; - } - } - - (void) feature_get_refcount(spa, - &spa_feature_table[SPA_FEATURE_DEVICE_REMOVAL], - &dr_feature_refcount); - (void) feature_get_refcount(spa, - &spa_feature_table[SPA_FEATURE_OBSOLETE_COUNTS], - &oc_feature_refcount); - - if (dr_feature_refcount != indirect_vdev_count) { - ret = 1; - (void) printf("Number of indirect vdevs (%llu) " \ - "does not match feature count (%llu)\n", - (u_longlong_t)indirect_vdev_count, - (u_longlong_t)dr_feature_refcount); - } else { - (void) printf("Verified device_removal feature refcount " \ - "of %llu is correct\n", - (u_longlong_t)dr_feature_refcount); - } - - if (zap_contains(spa_meta_objset(spa), DMU_POOL_DIRECTORY_OBJECT, - DMU_POOL_OBSOLETE_BPOBJ) == 0) { - obsolete_bpobj_count++; - } - - - obsolete_counts_object_count = precise_vdev_count; - obsolete_counts_object_count += obsolete_sm_count; - obsolete_counts_object_count += obsolete_counts_count; - obsolete_counts_object_count += scip_count; - obsolete_counts_object_count += obsolete_bpobj_count; - obsolete_counts_object_count += remap_deadlist_count; - - if (oc_feature_refcount != obsolete_counts_object_count) { - ret = 1; - (void) printf("Number of obsolete counts objects (%llu) " \ - "does not match feature count (%llu)\n", - (u_longlong_t)obsolete_counts_object_count, - (u_longlong_t)oc_feature_refcount); - (void) printf("pv:%llu os:%llu oc:%llu sc:%llu " - "ob:%llu rd:%llu\n", - (u_longlong_t)precise_vdev_count, - (u_longlong_t)obsolete_sm_count, - (u_longlong_t)obsolete_counts_count, - (u_longlong_t)scip_count, - (u_longlong_t)obsolete_bpobj_count, - (u_longlong_t)remap_deadlist_count); - } else { - (void) printf("Verified indirect_refcount feature refcount " \ - "of %llu is correct\n", - (u_longlong_t)oc_feature_refcount); - } - return (ret); -} - -static void -zdb_set_skip_mmp(char *target) -{ - spa_t *spa; - - /* - * Disable the activity check to allow examination of - * active pools. - */ - mutex_enter(&spa_namespace_lock); - if ((spa = spa_lookup(target)) != NULL) { - spa->spa_import_flags |= ZFS_IMPORT_SKIP_MMP; - } - mutex_exit(&spa_namespace_lock); -} - -#define BOGUS_SUFFIX "_CHECKPOINTED_UNIVERSE" -/* - * Import the checkpointed state of the pool specified by the target - * parameter as readonly. The function also accepts a pool config - * as an optional parameter, else it attempts to infer the config by - * the name of the target pool. - * - * Note that the checkpointed state's pool name will be the name of - * the original pool with the above suffix appened to it. In addition, - * if the target is not a pool name (e.g. a path to a dataset) then - * the new_path parameter is populated with the updated path to - * reflect the fact that we are looking into the checkpointed state. - * - * The function returns a newly-allocated copy of the name of the - * pool containing the checkpointed state. When this copy is no - * longer needed it should be freed with free(3C). Same thing - * applies to the new_path parameter if allocated. - */ -static char * -import_checkpointed_state(char *target, nvlist_t *cfg, char **new_path) -{ - int error = 0; - char *poolname, *bogus_name; - - /* If the target is not a pool, the extract the pool name */ - char *path_start = strchr(target, '/'); - if (path_start != NULL) { - size_t poolname_len = path_start - target; - poolname = strndup(target, poolname_len); - } else { - poolname = target; - } - - if (cfg == NULL) { - zdb_set_skip_mmp(poolname); - error = spa_get_stats(poolname, &cfg, NULL, 0); - if (error != 0) { - fatal("Tried to read config of pool \"%s\" but " - "spa_get_stats() failed with error %d\n", - poolname, error); - } - } - - (void) asprintf(&bogus_name, "%s%s", poolname, BOGUS_SUFFIX); - fnvlist_add_string(cfg, ZPOOL_CONFIG_POOL_NAME, bogus_name); - - error = spa_import(bogus_name, cfg, NULL, - ZFS_IMPORT_MISSING_LOG | ZFS_IMPORT_CHECKPOINT | - ZFS_IMPORT_SKIP_MMP); - if (error != 0) { - fatal("Tried to import pool \"%s\" but spa_import() failed " - "with error %d\n", bogus_name, error); - } - - if (new_path != NULL && path_start != NULL) - (void) asprintf(new_path, "%s%s", bogus_name, path_start); - - if (target != poolname) - free(poolname); - - return (bogus_name); -} - -typedef struct verify_checkpoint_sm_entry_cb_arg { - vdev_t *vcsec_vd; - - /* the following fields are only used for printing progress */ - uint64_t vcsec_entryid; - uint64_t vcsec_num_entries; -} verify_checkpoint_sm_entry_cb_arg_t; - -#define ENTRIES_PER_PROGRESS_UPDATE 10000 - -static int -verify_checkpoint_sm_entry_cb(space_map_entry_t *sme, void *arg) -{ - verify_checkpoint_sm_entry_cb_arg_t *vcsec = arg; - vdev_t *vd = vcsec->vcsec_vd; - metaslab_t *ms = vd->vdev_ms[sme->sme_offset >> vd->vdev_ms_shift]; - uint64_t end = sme->sme_offset + sme->sme_run; - - ASSERT(sme->sme_type == SM_FREE); - - if ((vcsec->vcsec_entryid % ENTRIES_PER_PROGRESS_UPDATE) == 0) { - (void) fprintf(stderr, - "\rverifying vdev %llu, space map entry %llu of %llu ...", - (longlong_t)vd->vdev_id, - (longlong_t)vcsec->vcsec_entryid, - (longlong_t)vcsec->vcsec_num_entries); - } - vcsec->vcsec_entryid++; - - /* - * See comment in checkpoint_sm_exclude_entry_cb() - */ - VERIFY3U(sme->sme_offset, >=, ms->ms_start); - VERIFY3U(end, <=, ms->ms_start + ms->ms_size); - - /* - * The entries in the vdev_checkpoint_sm should be marked as - * allocated in the checkpointed state of the pool, therefore - * their respective ms_allocateable trees should not contain them. - */ - mutex_enter(&ms->ms_lock); - range_tree_verify_not_present(ms->ms_allocatable, - sme->sme_offset, sme->sme_run); - mutex_exit(&ms->ms_lock); - - return (0); -} - -/* - * Verify that all segments in the vdev_checkpoint_sm are allocated - * according to the checkpoint's ms_sm (i.e. are not in the checkpoint's - * ms_allocatable). - * - * Do so by comparing the checkpoint space maps (vdev_checkpoint_sm) of - * each vdev in the current state of the pool to the metaslab space maps - * (ms_sm) of the checkpointed state of the pool. - * - * Note that the function changes the state of the ms_allocatable - * trees of the current spa_t. The entries of these ms_allocatable - * trees are cleared out and then repopulated from with the free - * entries of their respective ms_sm space maps. - */ -static void -verify_checkpoint_vdev_spacemaps(spa_t *checkpoint, spa_t *current) -{ - vdev_t *ckpoint_rvd = checkpoint->spa_root_vdev; - vdev_t *current_rvd = current->spa_root_vdev; - - load_concrete_ms_allocatable_trees(checkpoint, SM_FREE); - - for (uint64_t c = 0; c < ckpoint_rvd->vdev_children; c++) { - vdev_t *ckpoint_vd = ckpoint_rvd->vdev_child[c]; - vdev_t *current_vd = current_rvd->vdev_child[c]; - - space_map_t *checkpoint_sm = NULL; - uint64_t checkpoint_sm_obj; - - if (ckpoint_vd->vdev_ops == &vdev_indirect_ops) { - /* - * Since we don't allow device removal in a pool - * that has a checkpoint, we expect that all removed - * vdevs were removed from the pool before the - * checkpoint. - */ - ASSERT3P(current_vd->vdev_ops, ==, &vdev_indirect_ops); - continue; - } - - /* - * If the checkpoint space map doesn't exist, then nothing - * here is checkpointed so there's nothing to verify. - */ - if (current_vd->vdev_top_zap == 0 || - zap_contains(spa_meta_objset(current), - current_vd->vdev_top_zap, - VDEV_TOP_ZAP_POOL_CHECKPOINT_SM) != 0) - continue; - - VERIFY0(zap_lookup(spa_meta_objset(current), - current_vd->vdev_top_zap, VDEV_TOP_ZAP_POOL_CHECKPOINT_SM, - sizeof (uint64_t), 1, &checkpoint_sm_obj)); - - VERIFY0(space_map_open(&checkpoint_sm, spa_meta_objset(current), - checkpoint_sm_obj, 0, current_vd->vdev_asize, - current_vd->vdev_ashift)); - - verify_checkpoint_sm_entry_cb_arg_t vcsec; - vcsec.vcsec_vd = ckpoint_vd; - vcsec.vcsec_entryid = 0; - vcsec.vcsec_num_entries = - space_map_length(checkpoint_sm) / sizeof (uint64_t); - VERIFY0(space_map_iterate(checkpoint_sm, - space_map_length(checkpoint_sm), - verify_checkpoint_sm_entry_cb, &vcsec)); - dump_spacemap(current->spa_meta_objset, checkpoint_sm); - space_map_close(checkpoint_sm); - } - - /* - * If we've added vdevs since we took the checkpoint, ensure - * that their checkpoint space maps are empty. - */ - if (ckpoint_rvd->vdev_children < current_rvd->vdev_children) { - for (uint64_t c = ckpoint_rvd->vdev_children; - c < current_rvd->vdev_children; c++) { - vdev_t *current_vd = current_rvd->vdev_child[c]; - ASSERT3P(current_vd->vdev_checkpoint_sm, ==, NULL); - } - } - - /* for cleaner progress output */ - (void) fprintf(stderr, "\n"); -} - -/* - * Verifies that all space that's allocated in the checkpoint is - * still allocated in the current version, by checking that everything - * in checkpoint's ms_allocatable (which is actually allocated, not - * allocatable/free) is not present in current's ms_allocatable. - * - * Note that the function changes the state of the ms_allocatable - * trees of both spas when called. The entries of all ms_allocatable - * trees are cleared out and then repopulated from their respective - * ms_sm space maps. In the checkpointed state we load the allocated - * entries, and in the current state we load the free entries. - */ -static void -verify_checkpoint_ms_spacemaps(spa_t *checkpoint, spa_t *current) -{ - vdev_t *ckpoint_rvd = checkpoint->spa_root_vdev; - vdev_t *current_rvd = current->spa_root_vdev; - - load_concrete_ms_allocatable_trees(checkpoint, SM_ALLOC); - load_concrete_ms_allocatable_trees(current, SM_FREE); - - for (uint64_t i = 0; i < ckpoint_rvd->vdev_children; i++) { - vdev_t *ckpoint_vd = ckpoint_rvd->vdev_child[i]; - vdev_t *current_vd = current_rvd->vdev_child[i]; - - if (ckpoint_vd->vdev_ops == &vdev_indirect_ops) { - /* - * See comment in verify_checkpoint_vdev_spacemaps() - */ - ASSERT3P(current_vd->vdev_ops, ==, &vdev_indirect_ops); - continue; - } - - for (uint64_t m = 0; m < ckpoint_vd->vdev_ms_count; m++) { - metaslab_t *ckpoint_msp = ckpoint_vd->vdev_ms[m]; - metaslab_t *current_msp = current_vd->vdev_ms[m]; - - (void) fprintf(stderr, - "\rverifying vdev %llu of %llu, " - "metaslab %llu of %llu ...", - (longlong_t)current_vd->vdev_id, - (longlong_t)current_rvd->vdev_children, - (longlong_t)current_vd->vdev_ms[m]->ms_id, - (longlong_t)current_vd->vdev_ms_count); - - /* - * We walk through the ms_allocatable trees that - * are loaded with the allocated blocks from the - * ms_sm spacemaps of the checkpoint. For each - * one of these ranges we ensure that none of them - * exists in the ms_allocatable trees of the - * current state which are loaded with the ranges - * that are currently free. - * - * This way we ensure that none of the blocks that - * are part of the checkpoint were freed by mistake. - */ - range_tree_walk(ckpoint_msp->ms_allocatable, - (range_tree_func_t *)range_tree_verify_not_present, - current_msp->ms_allocatable); - } - } - - /* for cleaner progress output */ - (void) fprintf(stderr, "\n"); -} - -static void -verify_checkpoint_blocks(spa_t *spa) -{ - ASSERT(!dump_opt['L']); - - spa_t *checkpoint_spa; - char *checkpoint_pool; - nvlist_t *config = NULL; - int error = 0; - - /* - * We import the checkpointed state of the pool (under a different - * name) so we can do verification on it against the current state - * of the pool. - */ - checkpoint_pool = import_checkpointed_state(spa->spa_name, config, - NULL); - ASSERT(strcmp(spa->spa_name, checkpoint_pool) != 0); - - error = spa_open(checkpoint_pool, &checkpoint_spa, FTAG); - if (error != 0) { - fatal("Tried to open pool \"%s\" but spa_open() failed with " - "error %d\n", checkpoint_pool, error); - } - - /* - * Ensure that ranges in the checkpoint space maps of each vdev - * are allocated according to the checkpointed state's metaslab - * space maps. - */ - verify_checkpoint_vdev_spacemaps(checkpoint_spa, spa); - - /* - * Ensure that allocated ranges in the checkpoint's metaslab - * space maps remain allocated in the metaslab space maps of - * the current state. - */ - verify_checkpoint_ms_spacemaps(checkpoint_spa, spa); - - /* - * Once we are done, we get rid of the checkpointed state. - */ - spa_close(checkpoint_spa, FTAG); - free(checkpoint_pool); -} - -static void -dump_leftover_checkpoint_blocks(spa_t *spa) -{ - vdev_t *rvd = spa->spa_root_vdev; - - for (uint64_t i = 0; i < rvd->vdev_children; i++) { - vdev_t *vd = rvd->vdev_child[i]; - - space_map_t *checkpoint_sm = NULL; - uint64_t checkpoint_sm_obj; - - if (vd->vdev_top_zap == 0) - continue; - - if (zap_contains(spa_meta_objset(spa), vd->vdev_top_zap, - VDEV_TOP_ZAP_POOL_CHECKPOINT_SM) != 0) - continue; - - VERIFY0(zap_lookup(spa_meta_objset(spa), vd->vdev_top_zap, - VDEV_TOP_ZAP_POOL_CHECKPOINT_SM, - sizeof (uint64_t), 1, &checkpoint_sm_obj)); - - VERIFY0(space_map_open(&checkpoint_sm, spa_meta_objset(spa), - checkpoint_sm_obj, 0, vd->vdev_asize, vd->vdev_ashift)); - dump_spacemap(spa->spa_meta_objset, checkpoint_sm); - space_map_close(checkpoint_sm); - } -} - -static int -verify_checkpoint(spa_t *spa) -{ - uberblock_t checkpoint; - int error; - - if (!spa_feature_is_active(spa, SPA_FEATURE_POOL_CHECKPOINT)) - return (0); - - error = zap_lookup(spa->spa_meta_objset, DMU_POOL_DIRECTORY_OBJECT, - DMU_POOL_ZPOOL_CHECKPOINT, sizeof (uint64_t), - sizeof (uberblock_t) / sizeof (uint64_t), &checkpoint); - - if (error == ENOENT && !dump_opt['L']) { - /* - * If the feature is active but the uberblock is missing - * then we must be in the middle of discarding the - * checkpoint. - */ - (void) printf("\nPartially discarded checkpoint " - "state found:\n"); - dump_leftover_checkpoint_blocks(spa); - return (0); - } else if (error != 0) { - (void) printf("lookup error %d when looking for " - "checkpointed uberblock in MOS\n", error); - return (error); - } - dump_uberblock(&checkpoint, "\nCheckpointed uberblock found:\n", "\n"); - - if (checkpoint.ub_checkpoint_txg == 0) { - (void) printf("\nub_checkpoint_txg not set in checkpointed " - "uberblock\n"); - error = 3; - } - - if (error == 0 && !dump_opt['L']) - verify_checkpoint_blocks(spa); - - return (error); -} - -/* ARGSUSED */ -static void -mos_leaks_cb(void *arg, uint64_t start, uint64_t size) -{ - for (uint64_t i = start; i < size; i++) { - (void) printf("MOS object %llu referenced but not allocated\n", - (u_longlong_t)i); - } -} - -static range_tree_t *mos_refd_objs; - -static void -mos_obj_refd(uint64_t obj) -{ - if (obj != 0 && mos_refd_objs != NULL) - range_tree_add(mos_refd_objs, obj, 1); -} - -static void -mos_leak_vdev(vdev_t *vd) -{ - mos_obj_refd(vd->vdev_dtl_object); - mos_obj_refd(vd->vdev_ms_array); - mos_obj_refd(vd->vdev_top_zap); - mos_obj_refd(vd->vdev_indirect_config.vic_births_object); - mos_obj_refd(vd->vdev_indirect_config.vic_mapping_object); - mos_obj_refd(vd->vdev_leaf_zap); - if (vd->vdev_checkpoint_sm != NULL) - mos_obj_refd(vd->vdev_checkpoint_sm->sm_object); - if (vd->vdev_indirect_mapping != NULL) { - mos_obj_refd(vd->vdev_indirect_mapping-> - vim_phys->vimp_counts_object); - } - if (vd->vdev_obsolete_sm != NULL) - mos_obj_refd(vd->vdev_obsolete_sm->sm_object); - - for (uint64_t m = 0; m < vd->vdev_ms_count; m++) { - metaslab_t *ms = vd->vdev_ms[m]; - mos_obj_refd(space_map_object(ms->ms_sm)); - } - - for (uint64_t c = 0; c < vd->vdev_children; c++) { - mos_leak_vdev(vd->vdev_child[c]); - } -} - -static int -dump_mos_leaks(spa_t *spa) -{ - int rv = 0; - objset_t *mos = spa->spa_meta_objset; - dsl_pool_t *dp = spa->spa_dsl_pool; - - /* Visit and mark all referenced objects in the MOS */ - - mos_obj_refd(DMU_POOL_DIRECTORY_OBJECT); - mos_obj_refd(spa->spa_pool_props_object); - mos_obj_refd(spa->spa_config_object); - mos_obj_refd(spa->spa_ddt_stat_object); - mos_obj_refd(spa->spa_feat_desc_obj); - mos_obj_refd(spa->spa_feat_enabled_txg_obj); - mos_obj_refd(spa->spa_feat_for_read_obj); - mos_obj_refd(spa->spa_feat_for_write_obj); - mos_obj_refd(spa->spa_history); - mos_obj_refd(spa->spa_errlog_last); - mos_obj_refd(spa->spa_errlog_scrub); - mos_obj_refd(spa->spa_all_vdev_zaps); - mos_obj_refd(spa->spa_dsl_pool->dp_bptree_obj); - mos_obj_refd(spa->spa_dsl_pool->dp_tmp_userrefs_obj); - mos_obj_refd(spa->spa_dsl_pool->dp_scan->scn_phys.scn_queue_obj); - bpobj_count_refd(&spa->spa_deferred_bpobj); - mos_obj_refd(dp->dp_empty_bpobj); - bpobj_count_refd(&dp->dp_obsolete_bpobj); - bpobj_count_refd(&dp->dp_free_bpobj); - mos_obj_refd(spa->spa_l2cache.sav_object); - mos_obj_refd(spa->spa_spares.sav_object); - - mos_obj_refd(spa->spa_condensing_indirect_phys. - scip_next_mapping_object); - mos_obj_refd(spa->spa_condensing_indirect_phys. - scip_prev_obsolete_sm_object); - if (spa->spa_condensing_indirect_phys.scip_next_mapping_object != 0) { - vdev_indirect_mapping_t *vim = - vdev_indirect_mapping_open(mos, - spa->spa_condensing_indirect_phys.scip_next_mapping_object); - mos_obj_refd(vim->vim_phys->vimp_counts_object); - vdev_indirect_mapping_close(vim); - } - - if (dp->dp_origin_snap != NULL) { - dsl_dataset_t *ds; - - dsl_pool_config_enter(dp, FTAG); - VERIFY0(dsl_dataset_hold_obj(dp, - dsl_dataset_phys(dp->dp_origin_snap)->ds_next_snap_obj, - FTAG, &ds)); - count_ds_mos_objects(ds); - dump_deadlist(&ds->ds_deadlist); - dsl_dataset_rele(ds, FTAG); - dsl_pool_config_exit(dp, FTAG); - - count_ds_mos_objects(dp->dp_origin_snap); - dump_deadlist(&dp->dp_origin_snap->ds_deadlist); - } - count_dir_mos_objects(dp->dp_mos_dir); - if (dp->dp_free_dir != NULL) - count_dir_mos_objects(dp->dp_free_dir); - if (dp->dp_leak_dir != NULL) - count_dir_mos_objects(dp->dp_leak_dir); - - mos_leak_vdev(spa->spa_root_vdev); - - for (uint64_t class = 0; class < DDT_CLASSES; class++) { - for (uint64_t type = 0; type < DDT_TYPES; type++) { - for (uint64_t cksum = 0; - cksum < ZIO_CHECKSUM_FUNCTIONS; cksum++) { - ddt_t *ddt = spa->spa_ddt[cksum]; - mos_obj_refd(ddt->ddt_object[type][class]); - } - } - } - - /* - * Visit all allocated objects and make sure they are referenced. - */ - uint64_t object = 0; - while (dmu_object_next(mos, &object, B_FALSE, 0) == 0) { - if (range_tree_contains(mos_refd_objs, object, 1)) { - range_tree_remove(mos_refd_objs, object, 1); - } else { - dmu_object_info_t doi; - const char *name; - dmu_object_info(mos, object, &doi); - if (doi.doi_type & DMU_OT_NEWTYPE) { - dmu_object_byteswap_t bswap = - DMU_OT_BYTESWAP(doi.doi_type); - name = dmu_ot_byteswap[bswap].ob_name; - } else { - name = dmu_ot[doi.doi_type].ot_name; - } - - (void) printf("MOS object %llu (%s) leaked\n", - (u_longlong_t)object, name); - rv = 2; - } - } - (void) range_tree_walk(mos_refd_objs, mos_leaks_cb, NULL); - if (!range_tree_is_empty(mos_refd_objs)) - rv = 2; - range_tree_vacate(mos_refd_objs, NULL, NULL); - range_tree_destroy(mos_refd_objs); - return (rv); -} - -static void -dump_zpool(spa_t *spa) -{ - dsl_pool_t *dp = spa_get_dsl(spa); - int rc = 0; - - if (dump_opt['S']) { - dump_simulated_ddt(spa); - return; - } - - if (!dump_opt['e'] && dump_opt['C'] > 1) { - (void) printf("\nCached configuration:\n"); - dump_nvlist(spa->spa_config, 8); - } - - if (dump_opt['C']) - dump_config(spa); - - if (dump_opt['u']) - dump_uberblock(&spa->spa_uberblock, "\nUberblock:\n", "\n"); - - if (dump_opt['D']) - dump_all_ddts(spa); - - if (dump_opt['d'] > 2 || dump_opt['m']) - dump_metaslabs(spa); - if (dump_opt['M']) - dump_metaslab_groups(spa); - - if (dump_opt['d'] || dump_opt['i']) { - mos_refd_objs = range_tree_create(NULL, NULL); - dump_dir(dp->dp_meta_objset); - - if (dump_opt['d'] >= 3) { - dsl_pool_t *dp = spa->spa_dsl_pool; - dump_full_bpobj(&spa->spa_deferred_bpobj, - "Deferred frees", 0); - if (spa_version(spa) >= SPA_VERSION_DEADLISTS) { - dump_full_bpobj(&dp->dp_free_bpobj, - "Pool snapshot frees", 0); - } - if (bpobj_is_open(&dp->dp_obsolete_bpobj)) { - ASSERT(spa_feature_is_enabled(spa, - SPA_FEATURE_DEVICE_REMOVAL)); - dump_full_bpobj(&dp->dp_obsolete_bpobj, - "Pool obsolete blocks", 0); - } - - if (spa_feature_is_active(spa, - SPA_FEATURE_ASYNC_DESTROY)) { - dump_bptree(spa->spa_meta_objset, - dp->dp_bptree_obj, - "Pool dataset frees"); - } - dump_dtl(spa->spa_root_vdev, 0); - } - (void) dmu_objset_find(spa_name(spa), dump_one_dir, - NULL, DS_FIND_SNAPSHOTS | DS_FIND_CHILDREN); - - if (rc == 0 && !dump_opt['L']) - rc = dump_mos_leaks(spa); - - for (spa_feature_t f = 0; f < SPA_FEATURES; f++) { - uint64_t refcount; - - if (!(spa_feature_table[f].fi_flags & - ZFEATURE_FLAG_PER_DATASET)) { - ASSERT0(dataset_feature_count[f]); - continue; - } - (void) feature_get_refcount(spa, - &spa_feature_table[f], &refcount); - if (dataset_feature_count[f] != refcount) { - (void) printf("%s feature refcount mismatch: " - "%lld datasets != %lld refcount\n", - spa_feature_table[f].fi_uname, - (longlong_t)dataset_feature_count[f], - (longlong_t)refcount); - rc = 2; - } else { - (void) printf("Verified %s feature refcount " - "of %llu is correct\n", - spa_feature_table[f].fi_uname, - (longlong_t)refcount); - } - } - - if (rc == 0) { - rc = verify_device_removal_feature_counts(spa); - } - } - - if (rc == 0 && (dump_opt['b'] || dump_opt['c'])) - rc = dump_block_stats(spa); - - if (rc == 0) - rc = verify_spacemap_refcounts(spa); - - if (dump_opt['s']) - show_pool_stats(spa); - - if (dump_opt['h']) - dump_history(spa); - - if (rc == 0) - rc = verify_checkpoint(spa); - - if (rc != 0) { - dump_debug_buffer(); - exit(rc); - } -} - -#define ZDB_FLAG_CHECKSUM 0x0001 -#define ZDB_FLAG_DECOMPRESS 0x0002 -#define ZDB_FLAG_BSWAP 0x0004 -#define ZDB_FLAG_GBH 0x0008 -#define ZDB_FLAG_INDIRECT 0x0010 -#define ZDB_FLAG_PHYS 0x0020 -#define ZDB_FLAG_RAW 0x0040 -#define ZDB_FLAG_PRINT_BLKPTR 0x0080 - -static int flagbits[256]; - -static void -zdb_print_blkptr(blkptr_t *bp, int flags) -{ - char blkbuf[BP_SPRINTF_LEN]; - - if (flags & ZDB_FLAG_BSWAP) - byteswap_uint64_array((void *)bp, sizeof (blkptr_t)); - - snprintf_blkptr(blkbuf, sizeof (blkbuf), bp); - (void) printf("%s\n", blkbuf); -} - -static void -zdb_dump_indirect(blkptr_t *bp, int nbps, int flags) -{ - int i; - - for (i = 0; i < nbps; i++) - zdb_print_blkptr(&bp[i], flags); -} - -static void -zdb_dump_gbh(void *buf, int flags) -{ - zdb_dump_indirect((blkptr_t *)buf, SPA_GBH_NBLKPTRS, flags); -} - -static void -zdb_dump_block_raw(void *buf, uint64_t size, int flags) -{ - if (flags & ZDB_FLAG_BSWAP) - byteswap_uint64_array(buf, size); - (void) write(1, buf, size); -} - -static void -zdb_dump_block(char *label, void *buf, uint64_t size, int flags) -{ - uint64_t *d = (uint64_t *)buf; - unsigned nwords = size / sizeof (uint64_t); - int do_bswap = !!(flags & ZDB_FLAG_BSWAP); - unsigned i, j; - const char *hdr; - char *c; - - - if (do_bswap) - hdr = " 7 6 5 4 3 2 1 0 f e d c b a 9 8"; - else - hdr = " 0 1 2 3 4 5 6 7 8 9 a b c d e f"; - - (void) printf("\n%s\n%6s %s 0123456789abcdef\n", label, "", hdr); - - for (i = 0; i < nwords; i += 2) { - (void) printf("%06llx: %016llx %016llx ", - (u_longlong_t)(i * sizeof (uint64_t)), - (u_longlong_t)(do_bswap ? BSWAP_64(d[i]) : d[i]), - (u_longlong_t)(do_bswap ? BSWAP_64(d[i + 1]) : d[i + 1])); - - c = (char *)&d[i]; - for (j = 0; j < 2 * sizeof (uint64_t); j++) - (void) printf("%c", isprint(c[j]) ? c[j] : '.'); - (void) printf("\n"); - } -} - -/* - * There are two acceptable formats: - * leaf_name - For example: c1t0d0 or /tmp/ztest.0a - * child[.child]* - For example: 0.1.1 - * - * The second form can be used to specify arbitrary vdevs anywhere - * in the heirarchy. For example, in a pool with a mirror of - * RAID-Zs, you can specify either RAID-Z vdev with 0.0 or 0.1 . - */ -static vdev_t * -zdb_vdev_lookup(vdev_t *vdev, const char *path) -{ - char *s, *p, *q; - unsigned i; - - if (vdev == NULL) - return (NULL); - - /* First, assume the x.x.x.x format */ - i = strtoul(path, &s, 10); - if (s == path || (s && *s != '.' && *s != '\0')) - goto name; - if (i >= vdev->vdev_children) - return (NULL); - - vdev = vdev->vdev_child[i]; - if (*s == '\0') - return (vdev); - return (zdb_vdev_lookup(vdev, s+1)); - -name: - for (i = 0; i < vdev->vdev_children; i++) { - vdev_t *vc = vdev->vdev_child[i]; - - if (vc->vdev_path == NULL) { - vc = zdb_vdev_lookup(vc, path); - if (vc == NULL) - continue; - else - return (vc); - } - - p = strrchr(vc->vdev_path, '/'); - p = p ? p + 1 : vc->vdev_path; - q = &vc->vdev_path[strlen(vc->vdev_path) - 2]; - - if (strcmp(vc->vdev_path, path) == 0) - return (vc); - if (strcmp(p, path) == 0) - return (vc); - if (strcmp(q, "s0") == 0 && strncmp(p, path, q - p) == 0) - return (vc); - } - - return (NULL); -} - -/* ARGSUSED */ -static int -random_get_pseudo_bytes_cb(void *buf, size_t len, void *unused) -{ - return (random_get_pseudo_bytes(buf, len)); -} - -/* - * Read a block from a pool and print it out. The syntax of the - * block descriptor is: - * - * pool:vdev_specifier:offset:size[:flags] - * - * pool - The name of the pool you wish to read from - * vdev_specifier - Which vdev (see comment for zdb_vdev_lookup) - * offset - offset, in hex, in bytes - * size - Amount of data to read, in hex, in bytes - * flags - A string of characters specifying options - * b: Decode a blkptr at given offset within block - * *c: Calculate and display checksums - * d: Decompress data before dumping - * e: Byteswap data before dumping - * g: Display data as a gang block header - * i: Display as an indirect block - * p: Do I/O to physical offset - * r: Dump raw data to stdout - * - * * = not yet implemented - */ -static void -zdb_read_block(char *thing, spa_t *spa) -{ - blkptr_t blk, *bp = &blk; - dva_t *dva = bp->blk_dva; - int flags = 0; - uint64_t offset = 0, size = 0, psize = 0, lsize = 0, blkptr_offset = 0; - zio_t *zio; - vdev_t *vd; - abd_t *pabd; - void *lbuf, *buf; - const char *s, *vdev; - char *p, *dup, *flagstr; - int i, error; - - dup = strdup(thing); - s = strtok(dup, ":"); - vdev = s ? s : ""; - s = strtok(NULL, ":"); - offset = strtoull(s ? s : "", NULL, 16); - s = strtok(NULL, ":"); - size = strtoull(s ? s : "", NULL, 16); - s = strtok(NULL, ":"); - if (s) - flagstr = strdup(s); - else - flagstr = strdup(""); - - s = NULL; - if (size == 0) - s = "size must not be zero"; - if (!IS_P2ALIGNED(size, DEV_BSIZE)) - s = "size must be a multiple of sector size"; - if (!IS_P2ALIGNED(offset, DEV_BSIZE)) - s = "offset must be a multiple of sector size"; - if (s) { - (void) printf("Invalid block specifier: %s - %s\n", thing, s); - free(flagstr); - free(dup); - return; - } - - for (s = strtok(flagstr, ":"); s; s = strtok(NULL, ":")) { - for (i = 0; flagstr[i]; i++) { - int bit = flagbits[(uchar_t)flagstr[i]]; - - if (bit == 0) { - (void) printf("***Invalid flag: %c\n", - flagstr[i]); - continue; - } - flags |= bit; - - /* If it's not something with an argument, keep going */ - if ((bit & (ZDB_FLAG_CHECKSUM | - ZDB_FLAG_PRINT_BLKPTR)) == 0) - continue; - - p = &flagstr[i + 1]; - if (bit == ZDB_FLAG_PRINT_BLKPTR) - blkptr_offset = strtoull(p, &p, 16); - if (*p != ':' && *p != '\0') { - (void) printf("***Invalid flag arg: '%s'\n", s); - free(flagstr); - free(dup); - return; - } - i += p - &flagstr[i + 1]; /* skip over the number */ - } - } - free(flagstr); - - vd = zdb_vdev_lookup(spa->spa_root_vdev, vdev); - if (vd == NULL) { - (void) printf("***Invalid vdev: %s\n", vdev); - free(dup); - return; - } else { - if (vd->vdev_path) - (void) fprintf(stderr, "Found vdev: %s\n", - vd->vdev_path); - else - (void) fprintf(stderr, "Found vdev type: %s\n", - vd->vdev_ops->vdev_op_type); - } - - psize = size; - lsize = size; - - pabd = abd_alloc_linear(SPA_MAXBLOCKSIZE, B_FALSE); - lbuf = umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL); - - BP_ZERO(bp); - - DVA_SET_VDEV(&dva[0], vd->vdev_id); - DVA_SET_OFFSET(&dva[0], offset); - DVA_SET_GANG(&dva[0], !!(flags & ZDB_FLAG_GBH)); - DVA_SET_ASIZE(&dva[0], vdev_psize_to_asize(vd, psize)); - - BP_SET_BIRTH(bp, TXG_INITIAL, TXG_INITIAL); - - BP_SET_LSIZE(bp, lsize); - BP_SET_PSIZE(bp, psize); - BP_SET_COMPRESS(bp, ZIO_COMPRESS_OFF); - BP_SET_CHECKSUM(bp, ZIO_CHECKSUM_OFF); - BP_SET_TYPE(bp, DMU_OT_NONE); - BP_SET_LEVEL(bp, 0); - BP_SET_DEDUP(bp, 0); - BP_SET_BYTEORDER(bp, ZFS_HOST_BYTEORDER); - - spa_config_enter(spa, SCL_STATE, FTAG, RW_READER); - zio = zio_root(spa, NULL, NULL, 0); - - if (vd == vd->vdev_top) { - /* - * Treat this as a normal block read. - */ - zio_nowait(zio_read(zio, spa, bp, pabd, psize, NULL, NULL, - ZIO_PRIORITY_SYNC_READ, - ZIO_FLAG_CANFAIL | ZIO_FLAG_RAW, NULL)); - } else { - /* - * Treat this as a vdev child I/O. - */ - zio_nowait(zio_vdev_child_io(zio, bp, vd, offset, pabd, - psize, ZIO_TYPE_READ, ZIO_PRIORITY_SYNC_READ, - ZIO_FLAG_DONT_CACHE | ZIO_FLAG_DONT_QUEUE | - ZIO_FLAG_DONT_PROPAGATE | ZIO_FLAG_DONT_RETRY | - ZIO_FLAG_CANFAIL | ZIO_FLAG_RAW | ZIO_FLAG_OPTIONAL, - NULL, NULL)); - } - - error = zio_wait(zio); - spa_config_exit(spa, SCL_STATE, FTAG); - - if (error) { - (void) printf("Read of %s failed, error: %d\n", thing, error); - goto out; - } - - if (flags & ZDB_FLAG_DECOMPRESS) { - /* - * We don't know how the data was compressed, so just try - * every decompress function at every inflated blocksize. - */ - enum zio_compress c; - void *pbuf2 = umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL); - void *lbuf2 = umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL); - - abd_copy_to_buf(pbuf2, pabd, psize); - - VERIFY0(abd_iterate_func(pabd, psize, SPA_MAXBLOCKSIZE - psize, - random_get_pseudo_bytes_cb, NULL)); - - VERIFY0(random_get_pseudo_bytes((uint8_t *)pbuf2 + psize, - SPA_MAXBLOCKSIZE - psize)); - - for (lsize = SPA_MAXBLOCKSIZE; lsize > psize; - lsize -= SPA_MINBLOCKSIZE) { - for (c = 0; c < ZIO_COMPRESS_FUNCTIONS; c++) { - if (zio_decompress_data(c, pabd, - lbuf, psize, lsize) == 0 && - zio_decompress_data_buf(c, pbuf2, - lbuf2, psize, lsize) == 0 && - bcmp(lbuf, lbuf2, lsize) == 0) - break; - } - if (c != ZIO_COMPRESS_FUNCTIONS) - break; - lsize -= SPA_MINBLOCKSIZE; - } - - umem_free(pbuf2, SPA_MAXBLOCKSIZE); - umem_free(lbuf2, SPA_MAXBLOCKSIZE); - - if (lsize <= psize) { - (void) printf("Decompress of %s failed\n", thing); - goto out; - } - buf = lbuf; - size = lsize; - } else { - buf = abd_to_buf(pabd); - size = psize; - } - - if (flags & ZDB_FLAG_PRINT_BLKPTR) - zdb_print_blkptr((blkptr_t *)(void *) - ((uintptr_t)buf + (uintptr_t)blkptr_offset), flags); - else if (flags & ZDB_FLAG_RAW) - zdb_dump_block_raw(buf, size, flags); - else if (flags & ZDB_FLAG_INDIRECT) - zdb_dump_indirect((blkptr_t *)buf, size / sizeof (blkptr_t), - flags); - else if (flags & ZDB_FLAG_GBH) - zdb_dump_gbh(buf, flags); - else - zdb_dump_block(thing, buf, size, flags); - -out: - abd_free(pabd); - umem_free(lbuf, SPA_MAXBLOCKSIZE); - free(dup); -} - -static void -zdb_embedded_block(char *thing) -{ - blkptr_t bp; - unsigned long long *words = (void *)&bp; - char *buf; - int err; - - bzero(&bp, sizeof (bp)); - err = sscanf(thing, "%llx:%llx:%llx:%llx:%llx:%llx:%llx:%llx:" - "%llx:%llx:%llx:%llx:%llx:%llx:%llx:%llx", - words + 0, words + 1, words + 2, words + 3, - words + 4, words + 5, words + 6, words + 7, - words + 8, words + 9, words + 10, words + 11, - words + 12, words + 13, words + 14, words + 15); - if (err != 16) { - (void) fprintf(stderr, "invalid input format\n"); - exit(1); - } - ASSERT3U(BPE_GET_LSIZE(&bp), <=, SPA_MAXBLOCKSIZE); - buf = malloc(SPA_MAXBLOCKSIZE); - if (buf == NULL) { - (void) fprintf(stderr, "out of memory\n"); - exit(1); - } - err = decode_embedded_bp(&bp, buf, BPE_GET_LSIZE(&bp)); - if (err != 0) { - (void) fprintf(stderr, "decode failed: %u\n", err); - free(buf); - exit(1); - } - zdb_dump_block_raw(buf, BPE_GET_LSIZE(&bp), 0); - free(buf); -} - -int -main(int argc, char **argv) -{ - int c; - struct rlimit rl = { 1024, 1024 }; - spa_t *spa = NULL; - objset_t *os = NULL; - int dump_all = 1; - int verbose = 0; - int error = 0; - char **searchdirs = NULL; - int nsearch = 0; - char *target, *target_pool; - nvlist_t *policy = NULL; - uint64_t max_txg = UINT64_MAX; - int flags = ZFS_IMPORT_MISSING_LOG; - int rewind = ZPOOL_NEVER_REWIND; - char *spa_config_path_env; - boolean_t target_is_spa = B_TRUE; - nvlist_t *cfg = NULL; - - (void) setrlimit(RLIMIT_NOFILE, &rl); - (void) enable_extended_FILE_stdio(-1, -1); - - dprintf_setup(&argc, argv); - - /* - * If there is an environment variable SPA_CONFIG_PATH it overrides - * default spa_config_path setting. If -U flag is specified it will - * override this environment variable settings once again. - */ - spa_config_path_env = getenv("SPA_CONFIG_PATH"); - if (spa_config_path_env != NULL) - spa_config_path = spa_config_path_env; - - while ((c = getopt(argc, argv, - "AbcCdDeEFGhiI:klLmMo:Op:PqRsSt:uU:vVx:X")) != -1) { - switch (c) { - case 'b': - case 'c': - case 'C': - case 'd': - case 'D': - case 'E': - case 'G': - case 'h': - case 'i': - case 'l': - case 'm': - case 'M': - case 'O': - case 'R': - case 's': - case 'S': - case 'u': - dump_opt[c]++; - dump_all = 0; - break; - case 'A': - case 'e': - case 'F': - case 'k': - case 'L': - case 'P': - case 'q': - case 'X': - dump_opt[c]++; - break; - /* NB: Sort single match options below. */ - case 'I': - max_inflight = strtoull(optarg, NULL, 0); - if (max_inflight == 0) { - (void) fprintf(stderr, "maximum number " - "of inflight I/Os must be greater " - "than 0\n"); - usage(); - } - break; - case 'o': - error = set_global_var(optarg); - if (error != 0) - usage(); - break; - case 'p': - if (searchdirs == NULL) { - searchdirs = umem_alloc(sizeof (char *), - UMEM_NOFAIL); - } else { - char **tmp = umem_alloc((nsearch + 1) * - sizeof (char *), UMEM_NOFAIL); - bcopy(searchdirs, tmp, nsearch * - sizeof (char *)); - umem_free(searchdirs, - nsearch * sizeof (char *)); - searchdirs = tmp; - } - searchdirs[nsearch++] = optarg; - break; - case 't': - max_txg = strtoull(optarg, NULL, 0); - if (max_txg < TXG_INITIAL) { - (void) fprintf(stderr, "incorrect txg " - "specified: %s\n", optarg); - usage(); - } - break; - case 'U': - spa_config_path = optarg; - if (spa_config_path[0] != '/') { - (void) fprintf(stderr, - "cachefile must be an absolute path " - "(i.e. start with a slash)\n"); - usage(); - } - break; - case 'v': - verbose++; - break; - case 'V': - flags = ZFS_IMPORT_VERBATIM; - break; - case 'x': - vn_dumpdir = optarg; - break; - default: - usage(); - break; - } - } - - if (!dump_opt['e'] && searchdirs != NULL) { - (void) fprintf(stderr, "-p option requires use of -e\n"); - usage(); - } - - /* - * ZDB does not typically re-read blocks; therefore limit the ARC - * to 256 MB, which can be used entirely for metadata. - */ - zfs_arc_max = zfs_arc_meta_limit = 256 * 1024 * 1024; - - /* - * "zdb -c" uses checksum-verifying scrub i/os which are async reads. - * "zdb -b" uses traversal prefetch which uses async reads. - * For good performance, let several of them be active at once. - */ - zfs_vdev_async_read_max_active = 10; - - /* - * Disable reference tracking for better performance. - */ - reference_tracking_enable = B_FALSE; - - /* - * Do not fail spa_load when spa_load_verify fails. This is needed - * to load non-idle pools. - */ - spa_load_verify_dryrun = B_TRUE; - - kernel_init(FREAD); - g_zfs = libzfs_init(); - if (g_zfs == NULL) - fatal("Fail to initialize zfs"); - - if (dump_all) - verbose = MAX(verbose, 1); - - for (c = 0; c < 256; c++) { - if (dump_all && strchr("AeEFklLOPRSX", c) == NULL) - dump_opt[c] = 1; - if (dump_opt[c]) - dump_opt[c] += verbose; - } - - aok = (dump_opt['A'] == 1) || (dump_opt['A'] > 2); - zfs_recover = (dump_opt['A'] > 1); - - argc -= optind; - argv += optind; - - if (argc < 2 && dump_opt['R']) - usage(); - - if (dump_opt['E']) { - if (argc != 1) - usage(); - zdb_embedded_block(argv[0]); - return (0); - } - - if (argc < 1) { - if (!dump_opt['e'] && dump_opt['C']) { - dump_cachefile(spa_config_path); - return (0); - } - usage(); - } - - if (dump_opt['l']) - return (dump_label(argv[0])); - - if (dump_opt['O']) { - if (argc != 2) - usage(); - dump_opt['v'] = verbose + 3; - return (dump_path(argv[0], argv[1])); - } - - if (dump_opt['X'] || dump_opt['F']) - rewind = ZPOOL_DO_REWIND | - (dump_opt['X'] ? ZPOOL_EXTREME_REWIND : 0); - - if (nvlist_alloc(&policy, NV_UNIQUE_NAME_TYPE, 0) != 0 || - nvlist_add_uint64(policy, ZPOOL_LOAD_REQUEST_TXG, max_txg) != 0 || - nvlist_add_uint32(policy, ZPOOL_LOAD_REWIND_POLICY, rewind) != 0) - fatal("internal error: %s", strerror(ENOMEM)); - - error = 0; - target = argv[0]; - - if (strpbrk(target, "/@") != NULL) { - size_t targetlen; - - target_pool = strdup(target); - *strpbrk(target_pool, "/@") = '\0'; - - target_is_spa = B_FALSE; - targetlen = strlen(target); - if (targetlen && target[targetlen - 1] == '/') - target[targetlen - 1] = '\0'; - } else { - target_pool = target; - } - - if (dump_opt['e']) { - importargs_t args = { 0 }; - - args.paths = nsearch; - args.path = searchdirs; - args.can_be_active = B_TRUE; - - error = zpool_tryimport(g_zfs, target_pool, &cfg, &args); - - if (error == 0) { - - if (nvlist_add_nvlist(cfg, - ZPOOL_LOAD_POLICY, policy) != 0) { - fatal("can't open '%s': %s", - target, strerror(ENOMEM)); - } - - if (dump_opt['C'] > 1) { - (void) printf("\nConfiguration for import:\n"); - dump_nvlist(cfg, 8); - } - - /* - * Disable the activity check to allow examination of - * active pools. - */ - error = spa_import(target_pool, cfg, NULL, - flags | ZFS_IMPORT_SKIP_MMP); - } - } - - char *checkpoint_pool = NULL; - char *checkpoint_target = NULL; - if (dump_opt['k']) { - checkpoint_pool = import_checkpointed_state(target, cfg, - &checkpoint_target); - - if (checkpoint_target != NULL) - target = checkpoint_target; - - } - - if (error == 0) { - if (dump_opt['k'] && (target_is_spa || dump_opt['R'])) { - ASSERT(checkpoint_pool != NULL); - ASSERT(checkpoint_target == NULL); - - error = spa_open(checkpoint_pool, &spa, FTAG); - if (error != 0) { - fatal("Tried to open pool \"%s\" but " - "spa_open() failed with error %d\n", - checkpoint_pool, error); - } - - } else if (target_is_spa || dump_opt['R']) { - zdb_set_skip_mmp(target); - error = spa_open_rewind(target, &spa, FTAG, policy, - NULL); - if (error) { - /* - * If we're missing the log device then - * try opening the pool after clearing the - * log state. - */ - mutex_enter(&spa_namespace_lock); - if ((spa = spa_lookup(target)) != NULL && - spa->spa_log_state == SPA_LOG_MISSING) { - spa->spa_log_state = SPA_LOG_CLEAR; - error = 0; - } - mutex_exit(&spa_namespace_lock); - - if (!error) { - error = spa_open_rewind(target, &spa, - FTAG, policy, NULL); - } - } - } else { - zdb_set_skip_mmp(target); - error = open_objset(target, DMU_OST_ANY, FTAG, &os); - } - } - nvlist_free(policy); - - if (error) - fatal("can't open '%s': %s", target, strerror(error)); - - argv++; - argc--; - if (!dump_opt['R']) { - if (argc > 0) { - zopt_objects = argc; - zopt_object = calloc(zopt_objects, sizeof (uint64_t)); - for (unsigned i = 0; i < zopt_objects; i++) { - errno = 0; - zopt_object[i] = strtoull(argv[i], NULL, 0); - if (zopt_object[i] == 0 && errno != 0) - fatal("bad number %s: %s", - argv[i], strerror(errno)); - } - } - if (os != NULL) { - dump_dir(os); - } else if (zopt_objects > 0 && !dump_opt['m']) { - dump_dir(spa->spa_meta_objset); - } else { - dump_zpool(spa); - } - } else { - flagbits['b'] = ZDB_FLAG_PRINT_BLKPTR; - flagbits['c'] = ZDB_FLAG_CHECKSUM; - flagbits['d'] = ZDB_FLAG_DECOMPRESS; - flagbits['e'] = ZDB_FLAG_BSWAP; - flagbits['g'] = ZDB_FLAG_GBH; - flagbits['i'] = ZDB_FLAG_INDIRECT; - flagbits['p'] = ZDB_FLAG_PHYS; - flagbits['r'] = ZDB_FLAG_RAW; - - for (int i = 0; i < argc; i++) - zdb_read_block(argv[i], spa); - } - - if (dump_opt['k']) { - free(checkpoint_pool); - if (!target_is_spa) - free(checkpoint_target); - } - - if (os != NULL) - close_objset(os, FTAG); - else - spa_close(spa, FTAG); - - fuid_table_destroy(); - - dump_debug_buffer(); - - libzfs_fini(g_zfs); - kernel_fini(); - - return (error); -} diff --git a/cddl/contrib/opensolaris/cmd/zdb/zdb.h b/cddl/contrib/opensolaris/cmd/zdb/zdb.h deleted file mode 100644 index 49579811efbb..000000000000 --- a/cddl/contrib/opensolaris/cmd/zdb/zdb.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2017 Spectra Logic Corp Inc. All rights reserved. - * Use is subject to license terms. - */ - - -#ifndef _ZDB_H -#define _ZDB_H - -void dump_intent_log(zilog_t *); -extern uint8_t dump_opt[256]; - -#endif /* _ZDB_H */ diff --git a/cddl/contrib/opensolaris/cmd/zdb/zdb_il.c b/cddl/contrib/opensolaris/cmd/zdb/zdb_il.c deleted file mode 100644 index 9f3f23f82da1..000000000000 --- a/cddl/contrib/opensolaris/cmd/zdb/zdb_il.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -/* - * Copyright (c) 2013, 2017 by Delphix. All rights reserved. - */ - -/* - * Print intent log header and statistics. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <ctype.h> -#include <sys/zfs_context.h> -#include <sys/spa.h> -#include <sys/dmu.h> -#include <sys/stat.h> -#include <sys/resource.h> -#include <sys/zil.h> -#include <sys/zil_impl.h> -#include <sys/spa_impl.h> -#include <sys/abd.h> - -#include "zdb.h" - -extern uint8_t dump_opt[256]; - -static char tab_prefix[4] = "\t\t\t"; - -static void -print_log_bp(const blkptr_t *bp, const char *prefix) -{ - char blkbuf[BP_SPRINTF_LEN]; - - snprintf_blkptr(blkbuf, sizeof (blkbuf), bp); - (void) printf("%s%s\n", prefix, blkbuf); -} - -/* ARGSUSED */ -static void -zil_prt_rec_create(zilog_t *zilog, int txtype, void *arg) -{ - lr_create_t *lr = arg; - time_t crtime = lr->lr_crtime[0]; - char *name, *link; - lr_attr_t *lrattr; - - name = (char *)(lr + 1); - - if (lr->lr_common.lrc_txtype == TX_CREATE_ATTR || - lr->lr_common.lrc_txtype == TX_MKDIR_ATTR) { - lrattr = (lr_attr_t *)(lr + 1); - name += ZIL_XVAT_SIZE(lrattr->lr_attr_masksize); - } - - if (txtype == TX_SYMLINK) { - link = name + strlen(name) + 1; - (void) printf("%s%s -> %s\n", tab_prefix, name, link); - } else if (txtype != TX_MKXATTR) { - (void) printf("%s%s\n", tab_prefix, name); - } - - (void) printf("%s%s", tab_prefix, ctime(&crtime)); - (void) printf("%sdoid %" PRIu64 ", foid %" PRIu64 ", slots %" PRIu64 - ", mode %" PRIo64 "\n", - tab_prefix, lr->lr_doid, - (uint64_t)LR_FOID_GET_OBJ(lr->lr_foid), - (uint64_t)LR_FOID_GET_SLOTS(lr->lr_foid), - lr->lr_mode); - (void) printf("%suid %" PRIu64 ", gid %" PRIu64 ", gen %" PRIu64 - ", rdev %#" PRIx64 "\n", - tab_prefix, lr->lr_uid, lr->lr_gid, lr->lr_gen, lr->lr_rdev); -} - -/* ARGSUSED */ -static void -zil_prt_rec_remove(zilog_t *zilog, int txtype, void *arg) -{ - lr_remove_t *lr = arg; - - (void) printf("%sdoid %llu, name %s\n", tab_prefix, - (u_longlong_t)lr->lr_doid, (char *)(lr + 1)); -} - -/* ARGSUSED */ -static void -zil_prt_rec_link(zilog_t *zilog, int txtype, void *arg) -{ - lr_link_t *lr = arg; - - (void) printf("%sdoid %llu, link_obj %llu, name %s\n", tab_prefix, - (u_longlong_t)lr->lr_doid, (u_longlong_t)lr->lr_link_obj, - (char *)(lr + 1)); -} - -/* ARGSUSED */ -static void -zil_prt_rec_rename(zilog_t *zilog, int txtype, void *arg) -{ - lr_rename_t *lr = arg; - char *snm = (char *)(lr + 1); - char *tnm = snm + strlen(snm) + 1; - - (void) printf("%ssdoid %llu, tdoid %llu\n", tab_prefix, - (u_longlong_t)lr->lr_sdoid, (u_longlong_t)lr->lr_tdoid); - (void) printf("%ssrc %s tgt %s\n", tab_prefix, snm, tnm); -} - -/* ARGSUSED */ -static int -zil_prt_rec_write_cb(void *data, size_t len, void *unused) -{ - char *cdata = data; - for (size_t i = 0; i < len; i++) { - if (isprint(*cdata)) - (void) printf("%c ", *cdata); - else - (void) printf("%2X", *cdata); - cdata++; - } - return (0); -} - -/* ARGSUSED */ -static void -zil_prt_rec_write(zilog_t *zilog, int txtype, void *arg) -{ - lr_write_t *lr = arg; - abd_t *data; - blkptr_t *bp = &lr->lr_blkptr; - zbookmark_phys_t zb; - int verbose = MAX(dump_opt['d'], dump_opt['i']); - int error; - - (void) printf("%sfoid %llu, offset %llx, length %llx\n", tab_prefix, - (u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_offset, - (u_longlong_t)lr->lr_length); - - if (txtype == TX_WRITE2 || verbose < 5) - return; - - if (lr->lr_common.lrc_reclen == sizeof (lr_write_t)) { - (void) printf("%shas blkptr, %s\n", tab_prefix, - !BP_IS_HOLE(bp) && - bp->blk_birth >= spa_min_claim_txg(zilog->zl_spa) ? - "will claim" : "won't claim"); - print_log_bp(bp, tab_prefix); - - if (BP_IS_HOLE(bp)) { - (void) printf("\t\t\tLSIZE 0x%llx\n", - (u_longlong_t)BP_GET_LSIZE(bp)); - (void) printf("%s<hole>\n", tab_prefix); - return; - } - if (bp->blk_birth < zilog->zl_header->zh_claim_txg) { - (void) printf("%s<block already committed>\n", - tab_prefix); - return; - } - - SET_BOOKMARK(&zb, dmu_objset_id(zilog->zl_os), - lr->lr_foid, ZB_ZIL_LEVEL, - lr->lr_offset / BP_GET_LSIZE(bp)); - - data = abd_alloc(BP_GET_LSIZE(bp), B_FALSE); - error = zio_wait(zio_read(NULL, zilog->zl_spa, - bp, data, BP_GET_LSIZE(bp), NULL, NULL, - ZIO_PRIORITY_SYNC_READ, ZIO_FLAG_CANFAIL, &zb)); - if (error) - goto out; - } else { - /* data is stored after the end of the lr_write record */ - data = abd_alloc(lr->lr_length, B_FALSE); - abd_copy_from_buf(data, lr + 1, lr->lr_length); - } - - (void) printf("%s", tab_prefix); - (void) abd_iterate_func(data, - 0, MIN(lr->lr_length, (verbose < 6 ? 20 : SPA_MAXBLOCKSIZE)), - zil_prt_rec_write_cb, NULL); - (void) printf("\n"); - -out: - abd_free(data); -} - -/* ARGSUSED */ -static void -zil_prt_rec_truncate(zilog_t *zilog, int txtype, void *arg) -{ - lr_truncate_t *lr = arg; - - (void) printf("%sfoid %llu, offset 0x%llx, length 0x%llx\n", tab_prefix, - (u_longlong_t)lr->lr_foid, (longlong_t)lr->lr_offset, - (u_longlong_t)lr->lr_length); -} - -/* ARGSUSED */ -static void -zil_prt_rec_setattr(zilog_t *zilog, int txtype, void *arg) -{ - lr_setattr_t *lr = arg; - time_t atime = (time_t)lr->lr_atime[0]; - time_t mtime = (time_t)lr->lr_mtime[0]; - - (void) printf("%sfoid %llu, mask 0x%llx\n", tab_prefix, - (u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_mask); - - if (lr->lr_mask & AT_MODE) { - (void) printf("%sAT_MODE %llo\n", tab_prefix, - (longlong_t)lr->lr_mode); - } - - if (lr->lr_mask & AT_UID) { - (void) printf("%sAT_UID %llu\n", tab_prefix, - (u_longlong_t)lr->lr_uid); - } - - if (lr->lr_mask & AT_GID) { - (void) printf("%sAT_GID %llu\n", tab_prefix, - (u_longlong_t)lr->lr_gid); - } - - if (lr->lr_mask & AT_SIZE) { - (void) printf("%sAT_SIZE %llu\n", tab_prefix, - (u_longlong_t)lr->lr_size); - } - - if (lr->lr_mask & AT_ATIME) { - (void) printf("%sAT_ATIME %llu.%09llu %s", tab_prefix, - (u_longlong_t)lr->lr_atime[0], - (u_longlong_t)lr->lr_atime[1], - ctime(&atime)); - } - - if (lr->lr_mask & AT_MTIME) { - (void) printf("%sAT_MTIME %llu.%09llu %s", tab_prefix, - (u_longlong_t)lr->lr_mtime[0], - (u_longlong_t)lr->lr_mtime[1], - ctime(&mtime)); - } -} - -/* ARGSUSED */ -static void -zil_prt_rec_acl(zilog_t *zilog, int txtype, void *arg) -{ - lr_acl_t *lr = arg; - - (void) printf("%sfoid %llu, aclcnt %llu\n", tab_prefix, - (u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_aclcnt); -} - -typedef void (*zil_prt_rec_func_t)(zilog_t *, int, void *); -typedef struct zil_rec_info { - zil_prt_rec_func_t zri_print; - const char *zri_name; - uint64_t zri_count; -} zil_rec_info_t; - -static zil_rec_info_t zil_rec_info[TX_MAX_TYPE] = { - {.zri_print = NULL, .zri_name = "Total "}, - {.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE "}, - {.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR "}, - {.zri_print = zil_prt_rec_create, .zri_name = "TX_MKXATTR "}, - {.zri_print = zil_prt_rec_create, .zri_name = "TX_SYMLINK "}, - {.zri_print = zil_prt_rec_remove, .zri_name = "TX_REMOVE "}, - {.zri_print = zil_prt_rec_remove, .zri_name = "TX_RMDIR "}, - {.zri_print = zil_prt_rec_link, .zri_name = "TX_LINK "}, - {.zri_print = zil_prt_rec_rename, .zri_name = "TX_RENAME "}, - {.zri_print = zil_prt_rec_write, .zri_name = "TX_WRITE "}, - {.zri_print = zil_prt_rec_truncate, .zri_name = "TX_TRUNCATE "}, - {.zri_print = zil_prt_rec_setattr, .zri_name = "TX_SETATTR "}, - {.zri_print = zil_prt_rec_acl, .zri_name = "TX_ACL_V0 "}, - {.zri_print = zil_prt_rec_acl, .zri_name = "TX_ACL_ACL "}, - {.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE_ACL "}, - {.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE_ATTR "}, - {.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE_ACL_ATTR "}, - {.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR_ACL "}, - {.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR_ATTR "}, - {.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR_ACL_ATTR "}, - {.zri_print = zil_prt_rec_write, .zri_name = "TX_WRITE2 "}, -}; - -/* ARGSUSED */ -static int -print_log_record(zilog_t *zilog, lr_t *lr, void *arg, uint64_t claim_txg) -{ - int txtype; - int verbose = MAX(dump_opt['d'], dump_opt['i']); - - /* reduce size of txtype to strip off TX_CI bit */ - txtype = lr->lrc_txtype; - - ASSERT(txtype != 0 && (uint_t)txtype < TX_MAX_TYPE); - ASSERT(lr->lrc_txg); - - (void) printf("\t\t%s%s len %6llu, txg %llu, seq %llu\n", - (lr->lrc_txtype & TX_CI) ? "CI-" : "", - zil_rec_info[txtype].zri_name, - (u_longlong_t)lr->lrc_reclen, - (u_longlong_t)lr->lrc_txg, - (u_longlong_t)lr->lrc_seq); - - if (txtype && verbose >= 3) - zil_rec_info[txtype].zri_print(zilog, txtype, lr); - - zil_rec_info[txtype].zri_count++; - zil_rec_info[0].zri_count++; - - return (0); -} - -/* ARGSUSED */ -static int -print_log_block(zilog_t *zilog, blkptr_t *bp, void *arg, uint64_t claim_txg) -{ - char blkbuf[BP_SPRINTF_LEN + 10]; - int verbose = MAX(dump_opt['d'], dump_opt['i']); - const char *claim; - - if (verbose <= 3) - return (0); - - if (verbose >= 5) { - (void) strcpy(blkbuf, ", "); - snprintf_blkptr(blkbuf + strlen(blkbuf), - sizeof (blkbuf) - strlen(blkbuf), bp); - } else { - blkbuf[0] = '\0'; - } - - if (claim_txg != 0) - claim = "already claimed"; - else if (bp->blk_birth >= spa_min_claim_txg(zilog->zl_spa)) - claim = "will claim"; - else - claim = "won't claim"; - - (void) printf("\tBlock seqno %llu, %s%s\n", - (u_longlong_t)bp->blk_cksum.zc_word[ZIL_ZC_SEQ], claim, blkbuf); - - return (0); -} - -static void -print_log_stats(int verbose) -{ - unsigned i, w, p10; - - if (verbose > 3) - (void) printf("\n"); - - if (zil_rec_info[0].zri_count == 0) - return; - - for (w = 1, p10 = 10; zil_rec_info[0].zri_count >= p10; p10 *= 10) - w++; - - for (i = 0; i < TX_MAX_TYPE; i++) - if (zil_rec_info[i].zri_count || verbose >= 3) - (void) printf("\t\t%s %*llu\n", - zil_rec_info[i].zri_name, w, - (u_longlong_t)zil_rec_info[i].zri_count); - (void) printf("\n"); -} - -/* ARGSUSED */ -void -dump_intent_log(zilog_t *zilog) -{ - const zil_header_t *zh = zilog->zl_header; - int verbose = MAX(dump_opt['d'], dump_opt['i']); - int i; - - if (BP_IS_HOLE(&zh->zh_log) || verbose < 1) - return; - - (void) printf("\n ZIL header: claim_txg %llu, " - "claim_blk_seq %llu, claim_lr_seq %llu", - (u_longlong_t)zh->zh_claim_txg, - (u_longlong_t)zh->zh_claim_blk_seq, - (u_longlong_t)zh->zh_claim_lr_seq); - (void) printf(" replay_seq %llu, flags 0x%llx\n", - (u_longlong_t)zh->zh_replay_seq, (u_longlong_t)zh->zh_flags); - - for (i = 0; i < TX_MAX_TYPE; i++) - zil_rec_info[i].zri_count = 0; - - /* see comment in zil_claim() or zil_check_log_chain() */ - if (zilog->zl_spa->spa_uberblock.ub_checkpoint_txg != 0 && - zh->zh_claim_txg == 0) - return; - - if (verbose >= 2) { - (void) printf("\n"); - (void) zil_parse(zilog, print_log_block, print_log_record, NULL, - zh->zh_claim_txg); - print_log_stats(verbose); - } -} diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs-program.8 b/cddl/contrib/opensolaris/cmd/zfs/zfs-program.8 deleted file mode 100644 index 76bb97c2d96d..000000000000 --- a/cddl/contrib/opensolaris/cmd/zfs/zfs-program.8 +++ /dev/null @@ -1,551 +0,0 @@ -.\" This file and its contents are supplied under the terms of the -.\" Common Development and Distribution License ("CDDL"), version 1.0. -.\" You may only use this file in accordance with the terms of version -.\" 1.0 of the CDDL. -.\" -.\" A full copy of the text of the CDDL should have accompanied this -.\" source. A copy of the CDDL is also available via the Internet at -.\" http://www.illumos.org/license/CDDL. -.\" -.\" -.\" Copyright (c) 2016, 2017 by Delphix. All rights reserved. -.\" Copyright (c) 2018 Datto Inc. -.\" -.Dd April 18, 2020 -.Dt ZFS-PROGRAM 8 -.Os -.Sh NAME -.Nm zfs program -.Nd executes ZFS channel programs -.Sh SYNOPSIS -.Cm zfs program -.Op Fl jn -.Op Fl t Ar instruction-limit -.Op Fl m Ar memory-limit -.Ar pool -.Ar script -.\".Op Ar optional arguments to channel program -.Sh DESCRIPTION -The ZFS channel program interface allows ZFS administrative operations to be -run programmatically as a Lua script. -The entire script is executed atomically, with no other administrative -operations taking effect concurrently. -A library of ZFS calls is made available to channel program scripts. -Channel programs may only be run with root privileges. -.Pp -A modified version of the Lua 5.2 interpreter is used to run channel program -scripts. -The Lua 5.2 manual can be found at: -.Bd -centered -offset indent -.Lk http://www.lua.org/manual/5.2/ -.Ed -.Pp -The channel program given by -.Ar script -will be run on -.Ar pool , -and any attempts to access or modify other pools will cause an error. -.Sh OPTIONS -.Bl -tag -width "-t" -.It Fl j -Display channel program output in JSON format. -When this flag is specified and standard output is empty - -channel program encountered an error. -The details of such an error will be printed to standard error in plain text. -.It Fl n -Executes a read-only channel program, which runs faster. -The program cannot change on-disk state by calling functions from the -zfs.sync submodule. -The program can be used to gather information such as properties and -determining if changes would succeed (zfs.check.*). -Without this flag, all pending changes must be synced to disk before a -channel program can complete. -.It Fl t Ar instruction-limit -Execution time limit, in number of Lua instructions to execute. -If a channel program executes more than the specified number of instructions, -it will be stopped and an error will be returned. -The default limit is 10 million instructions, and it can be set to a maximum of -100 million instructions. -.It Fl m Ar memory-limit -Memory limit, in bytes. -If a channel program attempts to allocate more memory than the given limit, it -will be stopped and an error returned. -The default memory limit is 10 MB, and can be set to a maximum of 100 MB. -.El -.Pp -All remaining argument strings will be passed directly to the Lua script as -described in the -.Sx LUA INTERFACE -section below. -.Sh LUA INTERFACE -A channel program can be invoked either from the command line, or via a library -call to -.Fn lzc_channel_program . -.Ss Arguments -Arguments passed to the channel program are converted to a Lua table. -If invoked from the command line, extra arguments to the Lua script will be -accessible as an array stored in the argument table with the key 'argv': -.Bd -literal -offset indent -args = ... -argv = args["argv"] --- argv == {1="arg1", 2="arg2", ...} -.Ed -.Pp -If invoked from the libZFS interface, an arbitrary argument list can be -passed to the channel program, which is accessible via the same -"..." syntax in Lua: -.Bd -literal -offset indent -args = ... --- args == {"foo"="bar", "baz"={...}, ...} -.Ed -.Pp -Note that because Lua arrays are 1-indexed, arrays passed to Lua from the -libZFS interface will have their indices incremented by 1. -That is, the element -in -.Va arr[0] -in a C array passed to a channel program will be stored in -.Va arr[1] -when accessed from Lua. -.Ss Return Values -Lua return statements take the form: -.Bd -literal -offset indent -return ret0, ret1, ret2, ... -.Ed -.Pp -Return statements returning multiple values are permitted internally in a -channel program script, but attempting to return more than one value from the -top level of the channel program is not permitted and will throw an error. -However, tables containing multiple values can still be returned. -If invoked from the command line, a return statement: -.Bd -literal -offset indent -a = {foo="bar", baz=2} -return a -.Ed -.Pp -Will be output formatted as: -.Bd -literal -offset indent -Channel program fully executed with return value: - return: - baz: 2 - foo: 'bar' -.Ed -.Ss Fatal Errors -If the channel program encounters a fatal error while running, a non-zero exit -status will be returned. -If more information about the error is available, a singleton list will be -returned detailing the error: -.Bd -literal -offset indent -error: "error string, including Lua stack trace" -.Ed -.Pp -If a fatal error is returned, the channel program may have not executed at all, -may have partially executed, or may have fully executed but failed to pass a -return value back to userland. -.Pp -If the channel program exhausts an instruction or memory limit, a fatal error -will be generated and the program will be stopped, leaving the program partially -executed. -No attempt is made to reverse or undo any operations already performed. -Note that because both the instruction count and amount of memory used by a -channel program are deterministic when run against the same inputs and -filesystem state, as long as a channel program has run successfully once, you -can guarantee that it will finish successfully against a similar size system. -.Pp -If a channel program attempts to return too large a value, the program will -fully execute but exit with a nonzero status code and no return value. -.Pp -.Em Note: -ZFS API functions do not generate Fatal Errors when correctly invoked, they -return an error code and the channel program continues executing. -See the -.Sx ZFS API -section below for function-specific details on error return codes. -.Ss Lua to C Value Conversion -When invoking a channel program via the libZFS interface, it is necessary to -translate arguments and return values from Lua values to their C equivalents, -and vice-versa. -.Pp -There is a correspondence between nvlist values in C and Lua tables. -A Lua table which is returned from the channel program will be recursively -converted to an nvlist, with table values converted to their natural -equivalents: -.Bd -literal -offset indent -string -> string -number -> int64 -boolean -> boolean_value -nil -> boolean (no value) -table -> nvlist -.Ed -.Pp -Likewise, table keys are replaced by string equivalents as follows: -.Bd -literal -offset indent -string -> no change -number -> signed decimal string ("%lld") -boolean -> "true" | "false" -.Ed -.Pp -Any collision of table key strings (for example, the string "true" and a -true boolean value) will cause a fatal error. -.Pp -Lua numbers are represented internally as signed 64-bit integers. -.Sh LUA STANDARD LIBRARY -The following Lua built-in base library functions are available: -.Bd -literal -offset indent -assert rawlen -collectgarbage rawget -error rawset -getmetatable select -ipairs setmetatable -next tonumber -pairs tostring -rawequal type -.Ed -.Pp -All functions in the -.Em coroutine , -.Em string , -and -.Em table -built-in submodules are also available. -A complete list and documentation of these modules is available in the Lua -manual. -.Pp -The following functions base library functions have been disabled and are -not available for use in channel programs: -.Bd -literal -offset indent -dofile -loadfile -load -pcall -print -xpcall -.Ed -.Sh ZFS API -.Ss Function Arguments -Each API function takes a fixed set of required positional arguments and -optional keyword arguments. -For example, the destroy function takes a single positional string argument -(the name of the dataset to destroy) and an optional "defer" keyword boolean -argument. -When using parentheses to specify the arguments to a Lua function, only -positional arguments can be used: -.Bd -literal -offset indent -zfs.sync.destroy("rpool@snap") -.Ed -.Pp -To use keyword arguments, functions must be called with a single argument that -is a Lua table containing entries mapping integers to positional arguments and -strings to keyword arguments: -.Bd -literal -offset indent -zfs.sync.destroy({1="rpool@snap", defer=true}) -.Ed -.Pp -The Lua language allows curly braces to be used in place of parenthesis as -syntactic sugar for this calling convention: -.Bd -literal -offset indent -zfs.sync.snapshot{"rpool@snap", defer=true} -.Ed -.Ss Function Return Values -If an API function succeeds, it returns 0. -If it fails, it returns an error code and the channel program continues -executing. -API functions do not generate Fatal Errors except in the case of an -unrecoverable internal file system error. -.Pp -In addition to returning an error code, some functions also return extra -details describing what caused the error. -This extra description is given as a second return value, and will always be a -Lua table, or Nil if no error details were returned. -Different keys will exist in the error details table depending on the function -and error case. -Any such function may be called expecting a single return value: -.Bd -literal -offset indent -errno = zfs.sync.promote(dataset) -.Ed -.Pp -Or, the error details can be retrieved: -.Bd -literal -offset indent -errno, details = zfs.sync.promote(dataset) -if (errno == EEXIST) then - assert(details ~= Nil) - list_of_conflicting_snapshots = details -end -.Ed -.Pp -The following global aliases for API function error return codes are defined -for use in channel programs: -.Bd -literal -offset indent -EPERM ECHILD ENODEV ENOSPC -ENOENT EAGAIN ENOTDIR ESPIPE -ESRCH ENOMEM EISDIR EROFS -EINTR EACCES EINVAL EMLINK -EIO EFAULT ENFILE EPIPE -ENXIO ENOTBLK EMFILE EDOM -E2BIG EBUSY ENOTTY ERANGE -ENOEXEC EEXIST ETXTBSY EDQUOT -EBADF EXDEV EFBIG -.Ed -.Ss API Functions -For detailed descriptions of the exact behavior of any zfs administrative -operations, see the main -.Xr zfs 8 -manual page. -.Bl -tag -width "xx" -.It Em zfs.debug(msg) -Record a debug message in the zfs_dbgmsg log. -A log of these messages can be printed via mdb's "::zfs_dbgmsg" command, or -can be monitored live by running: -.Bd -literal -offset indent - dtrace -n 'zfs-dbgmsg{trace(stringof(arg0))}' -.Ed -.Pp -msg (string) -.Bd -ragged -compact -offset "xxxx" -Debug message to be printed. -.Ed -.It Em zfs.exists(dataset) -Returns true if the given dataset exists, or false if it doesn't. -A fatal error will be thrown if the dataset is not in the target pool. -That is, in a channel program running on rpool, -zfs.exists("rpool/nonexistent_fs") returns false, but -zfs.exists("somepool/fs_that_may_exist") will error. -.Pp -dataset (string) -.Bd -ragged -compact -offset "xxxx" -Dataset to check for existence. -Must be in the target pool. -.Ed -.It Em zfs.get_prop(dataset, property) -Returns two values. -First, a string, number or table containing the property value for the given -dataset. -Second, a string containing the source of the property (i.e. the name of the -dataset in which it was set or nil if it is readonly). -Throws a Lua error if the dataset is invalid or the property doesn't exist. -Note that Lua only supports int64 number types whereas ZFS number properties -are uint64. -This means very large values (like guid) may wrap around and appear negative. -.Pp -dataset (string) -.Bd -ragged -compact -offset "xxxx" -Filesystem or snapshot path to retrieve properties from. -.Ed -.Pp -property (string) -.Bd -ragged -compact -offset "xxxx" -Name of property to retrieve. -All filesystem, snapshot and volume properties are supported except -for 'mounted' and 'iscsioptions.' -Also supports the 'written@snap' and 'written#bookmark' properties and -the '<user|group><quota|used>@id' properties, though the id must be in numeric -form. -.Ed -.El -.Bl -tag -width "xx" -.It Sy zfs.sync submodule -The sync submodule contains functions that modify the on-disk state. -They are executed in "syncing context". -.Pp -The available sync submodule functions are as follows: -.Bl -tag -width "xx" -.It Em zfs.sync.destroy(dataset, [defer=true|false]) -Destroy the given dataset. -Returns 0 on successful destroy, or a nonzero error code if the dataset could -not be destroyed (for example, if the dataset has any active children or -clones). -.Pp -dataset (string) -.Bd -ragged -compact -offset "xxxx" -Filesystem or snapshot to be destroyed. -.Ed -.Pp -[optional] defer (boolean) -.Bd -ragged -compact -offset "xxxx" -Valid only for destroying snapshots. -If set to true, and the snapshot has holds or clones, allows the snapshot to be -marked for deferred deletion rather than failing. -.Ed -.It Em zfs.sync.promote(dataset) -Promote the given clone to a filesystem. -Returns 0 on successful promotion, or a nonzero error code otherwise. -If EEXIST is returned, the second return value will be an array of the clone's -snapshots whose names collide with snapshots of the parent filesystem. -.Pp -dataset (string) -.Bd -ragged -compact -offset "xxxx" -Clone to be promoted. -.Ed -.It Em zfs.sync.rollback(filesystem) -Rollback to the previous snapshot for a dataset. -Returns 0 on successful rollback, or a nonzero error code otherwise. -Rollbacks can be performed on filesystems or zvols, but not on snapshots -or mounted datasets. -EBUSY is returned in the case where the filesystem is mounted. -.Pp -filesystem (string) -.Bd -ragged -compact -offset "xxxx" -Filesystem to rollback. -.Ed -.It Em zfs.sync.snapshot(dataset) -Create a snapshot of a filesystem. -Returns 0 if the snapshot was successfully created, -and a nonzero error code otherwise. -.Pp -Note: Taking a snapshot will fail on any pool older than legacy version 27. -To enable taking snapshots from ZCP scripts, the pool must be upgraded. -.Pp -dataset (string) -.Bd -ragged -compact -offset "xxxx" -Name of snapshot to create. -.Ed -.El -.It Sy zfs.check submodule -For each function in the zfs.sync submodule, there is a corresponding zfs.check -function which performs a "dry run" of the same operation. -Each takes the same arguments as its zfs.sync counterpart and returns 0 if the -operation would succeed, or a non-zero error code if it would fail, along with -any other error details. -That is, each has the same behavior as the corresponding sync function except -for actually executing the requested change. -For example, -.Em zfs.check.destroy("fs") -returns 0 if -.Em zfs.sync.destroy("fs") -would successfully destroy the dataset. -.Pp -The available zfs.check functions are: -.Bl -tag -width "xx" -.It Em zfs.check.destroy(dataset, [defer=true|false]) -.It Em zfs.check.promote(dataset) -.It Em zfs.check.rollback(filesystem) -.It Em zfs.check.snapshot(dataset) -.El -.It Sy zfs.list submodule -The zfs.list submodule provides functions for iterating over datasets and -properties. -Rather than returning tables, these functions act as Lua iterators, and are -generally used as follows: -.Bd -literal -offset indent -for child in zfs.list.children("rpool") do - ... -end -.Ed -.Pp -The available zfs.list functions are: -.Bl -tag -width "xx" -.It Em zfs.list.clones(snapshot) -Iterate through all clones of the given snapshot. -.Pp -snapshot (string) -.Bd -ragged -compact -offset "xxxx" -Must be a valid snapshot path in the current pool. -.Ed -.It Em zfs.list.snapshots(dataset) -Iterate through all snapshots of the given dataset. -Each snapshot is returned as a string containing the full dataset name, e.g. -"pool/fs@snap". -.Pp -dataset (string) -.Bd -ragged -compact -offset "xxxx" -Must be a valid filesystem or volume. -.Ed -.It Em zfs.list.children(dataset) -Iterate through all direct children of the given dataset. -Each child is returned as a string containing the full dataset name, e.g. -"pool/fs/child". -.Pp -dataset (string) -.Bd -ragged -compact -offset "xxxx" -Must be a valid filesystem or volume. -.Ed -.It Em zfs.list.properties(dataset) -Iterate through all user properties for the given dataset. -.Pp -dataset (string) -.Bd -ragged -compact -offset "xxxx" -Must be a valid filesystem, snapshot, or volume. -.Ed -.It Em zfs.list.system_properties(dataset) -Returns an array of strings, the names of the valid system (non-user defined) -properties for the given dataset. -Throws a Lua error if the dataset is invalid. -.Pp -dataset (string) -.Bd -ragged -compact -offset "xxxx" -Must be a valid filesystem, snapshot or volume. -.Ed -.El -.El -.Sh EXAMPLES -.Ss Example 1 -The following channel program recursively destroys a filesystem and all its -snapshots and children in a naive manner. -Note that this does not involve any error handling or reporting. -.Bd -literal -offset indent -function destroy_recursive(root) - for child in zfs.list.children(root) do - destroy_recursive(child) - end - for snap in zfs.list.snapshots(root) do - zfs.sync.destroy(snap) - end - zfs.sync.destroy(root) -end -destroy_recursive("pool/somefs") -.Ed -.Ss Example 2 -A more verbose and robust version of the same channel program, which -properly detects and reports errors, and also takes the dataset to destroy -as a command line argument, would be as follows: -.Bd -literal -offset indent -succeeded = {} -failed = {} - -function destroy_recursive(root) - for child in zfs.list.children(root) do - destroy_recursive(child) - end - for snap in zfs.list.snapshots(root) do - err = zfs.sync.destroy(snap) - if (err ~= 0) then - failed[snap] = err - else - succeeded[snap] = err - end - end - err = zfs.sync.destroy(root) - if (err ~= 0) then - failed[root] = err - else - succeeded[root] = err - end -end - -args = ... -argv = args["argv"] - -destroy_recursive(argv[1]) - -results = {} -results["succeeded"] = succeeded -results["failed"] = failed -return results -.Ed -.Ss Example 3 -The following function performs a forced promote operation by attempting to -promote the given clone and destroying any conflicting snapshots. -.Bd -literal -offset indent -function force_promote(ds) - errno, details = zfs.check.promote(ds) - if (errno == EEXIST) then - assert(details ~= Nil) - for i, snap in ipairs(details) do - zfs.sync.destroy(ds .. "@" .. snap) - end - elseif (errno ~= 0) then - return errno - end - return zfs.sync.promote(ds) -end -.Ed diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs.8 b/cddl/contrib/opensolaris/cmd/zfs/zfs.8 deleted file mode 100644 index 33e0ca4b3040..000000000000 --- a/cddl/contrib/opensolaris/cmd/zfs/zfs.8 +++ /dev/null @@ -1,3973 +0,0 @@ -'\" te -.\" Copyright (c) 2013, Martin Matuska <mm@FreeBSD.org>. -.\" All Rights Reserved. -.\" -.\" The contents of this file are subject to the terms of the -.\" Common Development and Distribution License (the "License"). -.\" You may not use this file except in compliance with the License. -.\" -.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -.\" or http://www.opensolaris.org/os/licensing. -.\" See the License for the specific language governing permissions -.\" and limitations under the License. -.\" -.\" When distributing Covered Code, include this CDDL HEADER in each -.\" file and include the License file at usr/src/OPENSOLARIS.LICENSE. -.\" If applicable, add the following below this CDDL HEADER, with the -.\" fields enclosed by brackets "[]" replaced with your own identifying -.\" information: Portions Copyright [yyyy] [name of copyright owner] -.\" -.\" Copyright (c) 2010, Sun Microsystems, Inc. All Rights Reserved. -.\" Copyright (c) 2011, 2014 by Delphix. All rights reserved. -.\" Copyright (c) 2011, Pawel Jakub Dawidek <pjd@FreeBSD.org> -.\" Copyright (c) 2012, Glen Barber <gjb@FreeBSD.org> -.\" Copyright (c) 2012, Bryan Drewery <bdrewery@FreeBSD.org> -.\" Copyright (c) 2013 by Saso Kiselkov. All rights reserved. -.\" Copyright (c) 2013, Steven Hartland <smh@FreeBSD.org> -.\" Copyright (c) 2016 Nexenta Systems, Inc. All Rights Reserved. -.\" Copyright (c) 2014, Xin LI <delphij@FreeBSD.org> -.\" Copyright (c) 2014-2015, The FreeBSD Foundation, All Rights Reserved. -.\" Copyright 2019 Joyent, Inc. -.\" Copyright (c) 2018 Datto Inc. -.\" -.\" $FreeBSD$ -.\" -.Dd February 16, 2020 -.Dt ZFS 8 -.Os -.Sh NAME -.Nm zfs -.Nd configures ZFS file systems -.Sh SYNOPSIS -.Nm -.Op Fl \&? -.Nm -.Cm create -.Op Fl pu -.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ... Ar filesystem -.Nm -.Cm create -.Op Fl ps -.Op Fl b Ar blocksize -.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ... -.Fl V -.Ar size volume -.Nm -.Cm destroy -.Op Fl fnpRrv -.Ar filesystem Ns | Ns Ar volume -.Nm -.Cm destroy -.Op Fl dnpRrv -.Sm off -.Ar filesystem Ns | Ns volume -.Ns @snap -.Op % Ns Ar snap -.Op , Ns Ar snap Op % Ns Ar snap -.Op , Ns ... -.Sm on -.Nm -.Cm destroy -.Ar filesystem Ns | Ns Ar volume Ns # Ns Ar bookmark -.Nm -.Cm snapshot Ns | Ns Cm snap -.Op Fl r -.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ... -.Ar filesystem@snapname Ns | Ns Ar volume@snapname -.Ar filesystem@snapname Ns | Ns Ar volume@snapname Ns ... -.Nm -.Cm rollback -.Op Fl rRf -.Ar snapshot -.Nm -.Cm clone -.Op Fl p -.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ... -.Ar snapshot filesystem Ns | Ns Ar volume -.Nm -.Cm promote -.Ar clone-filesystem -.Nm -.Cm rename -.Op Fl f -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot -.Nm -.Cm rename -.Op Fl f -.Fl p -.Ar filesystem Ns | Ns Ar volume -.Ar filesystem Ns | Ns Ar volume -.Nm -.Cm rename -.Fl r -.Ar snapshot snapshot -.Nm -.Cm rename -.Ar bookmark bookmark -.Nm -.Cm rename -.Fl u -.Op Fl p -.Ar filesystem filesystem -.Nm -.Cm list -.Op Fl r Ns | Ns Fl d Ar depth -.Op Fl Hp -.Op Fl o Ar property Ns Oo , Ns property Ns Oc Ns ... -.Op Fl t Ar type Ns Oo , Ns type Ns Oc Ns ... -.Oo Fl s Ar property Oc Ns ... -.Oo Fl S Ar property Oc Ns ... -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot | Ns Ar bookmark Ns ... -.Nm -.Cm remap -.Ar filesystem Ns | Ns Ar volume -.Nm -.Cm set -.Ar property Ns = Ns Ar value Oo Ar property Ns = Ns Ar value Oc Ns ... -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns ... -.Nm -.Cm get -.Op Fl r Ns | Ns Fl d Ar depth -.Op Fl Hp -.Op Fl o Ar all | field Ns Oo , Ns Ar field Oc Ns ... -.Op Fl t Ar type Ns Oo Ns , Ar type Oc Ns ... -.Op Fl s Ar source Ns Oo Ns , Ns Ar source Oc Ns ... -.Ar all | property Ns Oo Ns , Ns Ar property Oc Ns ... -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns ... -.Nm -.Cm inherit -.Op Fl rS -.Ar property -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns ... -.Nm -.Cm upgrade -.Op Fl v -.Nm -.Cm upgrade -.Op Fl r -.Op Fl V Ar version -.Fl a | Ar filesystem -.Nm -.Cm userspace -.Op Fl Hinp -.Op Fl o Ar field Ns Oo , Ns Ar field Oc Ns ... -.Oo Fl s Ar field Oc Ns ... -.Oo Fl S Ar field Oc Ns ... -.Op Fl t Ar type Ns Oo Ns , Ns Ar type Oc Ns ... -.Ar filesystem Ns | Ns Ar snapshot -.Nm -.Cm groupspace -.Op Fl Hinp -.Op Fl o Ar field Ns Oo , Ns field Oc Ns ... -.Oo Fl s Ar field Oc Ns ... -.Oo Fl S Ar field Oc Ns ... -.Op Fl t Ar type Ns Oo Ns , Ns Ar type Oc Ns ... -.Ar filesystem Ns | Ns Ar snapshot -.Nm -.Cm mount -.Nm -.Cm mount -.Op Fl vO -.Op Fl o Ar property Ns Oo , Ns Ar property Oc Ns ... -.Fl a | Ar filesystem -.Nm -.Cm unmount Ns | Ns Cm umount -.Op Fl f -.Fl a | Ar filesystem Ns | Ns Ar mountpoint -.Nm -.Cm share -.Fl a | Ar filesystem -.Nm -.Cm unshare -.Fl a | Ar filesystem Ns | Ns Ar mountpoint -.Nm -.Cm bookmark -.Ar snapshot -.Ar bookmark -.Nm -.Cm send -.Op Fl DLPRVcenpv -.Op Fl i Ar snapshot | Fl I Ar snapshot -.Ar snapshot -.Nm -.Cm send -.Op Fl LPcenv -.Op Fl i Ar snapshot Ns | Ns Ar bookmark -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot -.Nm -.Cm send -.Op Fl PVenv -.Fl t Ar receive_resume_token -.Nm -.Cm receive Ns | Ns Cm recv -.Op Fl vnsFMu -.Op Fl o Sy origin Ns = Ns Ar snapshot -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot -.Nm -.Cm receive Ns | Ns Cm recv -.Op Fl vnsFMu -.Op Fl d | e -.Op Fl o Sy origin Ns = Ns Ar snapshot -.Ar filesystem -.Nm -.Cm receive Ns | Ns Cm recv -.Fl A -.Ar filesystem Ns | Ns Ar volume -.Nm -.Cm allow -.Ar filesystem Ns | Ns Ar volume -.Nm -.Cm allow -.Op Fl ldug -.Ar user Ns | Ns Ar group Ns Oo Ns , Ns Ar user Ns | Ns Ar group Oc Ns ... -.Ar perm Ns | Ns Ar @setname Ns -.Oo Ns , Ns Ar perm Ns | Ns Ar @setname Oc Ns ... -.Ar filesystem Ns | Ns Ar volume -.Nm -.Cm allow -.Op Fl ld -.Fl e Ns | Ns Cm everyone -.Ar perm Ns | Ns Ar @setname Ns Op Ns , Ns Ar perm Ns | Ns Ar @setname Ns -.Ns ... -.Ar filesystem Ns | Ns Ar volume -.Nm -.Cm allow -.Fl c -.Ar perm Ns | Ns Ar @setname Ns Op Ns , Ns Ar perm Ns | Ns Ar @setname Ns -.Ns ... -.Ar filesystem Ns | Ns Ar volume -.Nm -.Cm allow -.Fl s -.Ar @setname -.Ar perm Ns | Ns Ar @setname Ns Op Ns , Ns Ar perm Ns | Ns Ar @setname Ns -.Ns ... -.Ar filesystem Ns | Ns Ar volume -.Nm -.Cm unallow -.Op Fl rldug -.Ar user Ns | Ns Ar group Ns Oo Ns , Ns Ar user Ns | Ns Ar group Oc Ns ... -.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns -.Ns ... Oc -.Ar filesystem Ns | Ns Ar volume -.Nm -.Cm unallow -.Op Fl rld -.Fl e Ns | Ns Cm everyone -.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns -.Ns ... Oc -.Ar filesystem Ns | Ns Ar volume -.Nm -.Cm unallow -.Op Fl r -.Fl c -.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns -.Ns ... Oc -.Ar filesystem Ns | Ns Ar volume -.Nm -.Cm unallow -.Op Fl r -.Fl s -.Ar @setname -.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns -.Ns ... Oc -.Ar filesystem Ns | Ns Ar volume -.Nm -.Cm hold -.Op Fl r -.Ar tag snapshot Ns ... -.Nm -.Cm holds -.Op Fl Hp -.Op Fl r Ns | Ns Fl d Ar depth -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns -.Ns ... -.Nm -.Cm release -.Op Fl r -.Ar tag snapshot Ns ... -.Nm -.Cm diff -.Op Fl FHt -.Ar snapshot -.Op Ar snapshot Ns | Ns Ar filesystem -.Nm -.Cm program -.Op Fl jn -.Op Fl t Ar timeout -.Op Fl m Ar memory_limit -.Ar pool script -.Op Ar arg1 No ... -.Nm -.Cm jail -.Ar jailid Ns | Ns Ar jailname filesystem -.Nm -.Cm unjail -.Ar jailid Ns | Ns Ar jailname filesystem -.Sh DESCRIPTION -The -.Nm -command configures -.Tn ZFS -datasets within a -.Tn ZFS -storage pool, as described in -.Xr zpool 8 . -A dataset is identified by a unique path within the -.Tn ZFS -namespace. For example: -.Bd -ragged -offset 4n -.No pool/ Ns Brq filesystem,volume,snapshot -.Ed -.Pp -where the maximum length of a dataset name is -.Dv MAXNAMELEN -(256 bytes) -and the maximum amount of nesting allowed in a path is 50 levels deep. -.Pp -A dataset can be one of the following: -.Bl -hang -width 12n -.It Sy file system -A -.Tn ZFS -dataset of type -.Em filesystem -can be mounted within the standard system namespace and behaves like other file -systems. While -.Tn ZFS -file systems are designed to be -.Tn POSIX -compliant, known issues exist that prevent compliance in some cases. -Applications that depend on standards conformance might fail due to nonstandard -behavior when checking file system free space. -.It Sy volume -A logical volume exported as a raw or block device. This type of dataset should -only be used under special circumstances. File systems are typically used in -most environments. -.It Sy snapshot -A read-only version of a file system or volume at a given point in time. It is -specified as -.Em filesystem@name -or -.Em volume@name . -.El -.Ss ZFS File System Hierarchy -A -.Tn ZFS -storage pool is a logical collection of devices that provide space for -datasets. A storage pool is also the root of the -.Tn ZFS -file system hierarchy. -.Pp -The root of the pool can be accessed as a file system, such as mounting and -unmounting, taking snapshots, and setting properties. The physical storage -characteristics, however, are managed by the -.Xr zpool 8 -command. -.Pp -See -.Xr zpool 8 -for more information on creating and administering pools. -.Ss Snapshots -A snapshot is a read-only copy of a file system or volume. Snapshots can be -created extremely quickly, and initially consume no additional space within the -pool. As data within the active dataset changes, the snapshot consumes more -data than would otherwise be shared with the active dataset. -.Pp -Snapshots can have arbitrary names. Snapshots of volumes can be cloned or -rolled back, but cannot be accessed independently. -.Pp -File system snapshots can be accessed under the -.Pa \&.zfs/snapshot -directory in the root of the file system. Snapshots are automatically mounted -on demand and may be unmounted at regular intervals. The visibility of the -.Pa \&.zfs -directory can be controlled by the -.Sy snapdir -property. -.Ss Clones -A clone is a writable volume or file system whose initial contents are the same -as another dataset. As with snapshots, creating a clone is nearly -instantaneous, and initially consumes no additional space. -.Pp -Clones can only be created from a snapshot. When a snapshot is cloned, it -creates an implicit dependency between the parent and child. Even though the -clone is created somewhere else in the dataset hierarchy, the original snapshot -cannot be destroyed as long as a clone exists. The -.Sy origin -property exposes this dependency, and the -.Cm destroy -command lists any such dependencies, if they exist. -.Pp -The clone parent-child dependency relationship can be reversed by using the -.Cm promote -subcommand. This causes the "origin" file system to become a clone of the -specified file system, which makes it possible to destroy the file system that -the clone was created from. -.Ss Mount Points -Creating a -.Tn ZFS -file system is a simple operation, so the number of file systems per system is -likely to be numerous. To cope with this, -.Tn ZFS -automatically manages mounting and unmounting file systems without the need to -edit the -.Pa /etc/fstab -file. All automatically managed file systems are mounted by -.Tn ZFS -at boot time. -.Pp -By default, file systems are mounted under -.Pa /path , -where -.Ar path -is the name of the file system in the -.Tn ZFS -namespace. Directories are created and destroyed as needed. -.Pp -A file system can also have a mount point set in the -.Sy mountpoint -property. This directory is created as needed, and -.Tn ZFS -automatically mounts the file system when the -.Qq Nm Cm mount Fl a -command is invoked (without editing -.Pa /etc/fstab ) . -The -.Sy mountpoint -property can be inherited, so if -.Em pool/home -has a mount point of -.Pa /home , -then -.Em pool/home/user -automatically inherits a mount point of -.Pa /home/user . -.Pp -A file system -.Sy mountpoint -property of -.Cm none -prevents the file system from being mounted. -.Pp -If needed, -.Tn ZFS -file systems can also be managed with traditional tools -.Pq Xr mount 8 , Xr umount 8 , Xr fstab 5 . -If a file system's mount point is set to -.Cm legacy , -.Tn ZFS -makes no attempt to manage the file system, and the administrator is -responsible for mounting and unmounting the file system. -.Ss Jails -.No A Tn ZFS -dataset can be attached to a jail by using the -.Qq Nm Cm jail -subcommand. You cannot attach a dataset to one jail and the children of the -same dataset to another jail. You can also not attach the root file system -of the jail or any dataset which needs to be mounted before the zfs rc script -is run inside the jail, as it would be attached unmounted until it is -mounted from the rc script inside the jail. To allow management of the -dataset from within a jail, the -.Sy jailed -property has to be set and the jail needs access to the -.Pa /dev/zfs -device. The -.Sy quota -property cannot be changed from within a jail. See -.Xr jail 8 -for information on how to allow mounting -.Tn ZFS -datasets from within a jail. -.Pp -.No A Tn ZFS -dataset can be detached from a jail using the -.Qq Nm Cm unjail -subcommand. -.Pp -After a dataset is attached to a jail and the jailed property is set, a jailed -file system cannot be mounted outside the jail, since the jail administrator -might have set the mount point to an unacceptable value. -.Ss Deduplication -Deduplication is the process for removing redundant data at the block-level, -reducing the total amount of data stored. If a file system has the -.Cm dedup -property enabled, duplicate data blocks are removed synchronously. The result -is that only unique data is stored and common components are shared among -files. -.Ss Native Properties -Properties are divided into two types, native properties and user-defined (or -"user") properties. Native properties either export internal statistics or -control -.Tn ZFS -behavior. In addition, native properties are either editable or read-only. User -properties have no effect on -.Tn ZFS -behavior, but you can use them to annotate datasets in a way that is meaningful -in your environment. For more information about user properties, see the -.Qq Sx User Properties -section, below. -.Pp -Every dataset has a set of properties that export statistics about the dataset -as well as control various behaviors. Properties are inherited from the parent -unless overridden by the child. Some properties apply only to certain types of -datasets (file systems, volumes, or snapshots). -.Pp -The values of numeric properties can be specified using human-readable suffixes -(for example, -.Sy k , KB , M , Gb , -and so forth, up to -.Sy Z -for zettabyte). The following are all valid (and equal) specifications: -.Bd -ragged -offset 4n -1536M, 1.5g, 1.50GB -.Ed -.Pp -The values of non-numeric properties are case sensitive and must be lowercase, -except for -.Sy mountpoint , sharenfs , No and Sy sharesmb . -.Pp -The following native properties consist of read-only statistics about the -dataset. These properties can be neither set, nor inherited. Native properties -apply to all dataset types unless otherwise noted. -.Bl -tag -width 2n -.It Sy available -The amount of space available to the dataset and all its children, assuming -that there is no other activity in the pool. Because space is shared within a -pool, availability can be limited by any number of factors, including physical -pool size, quotas, reservations, or other datasets within the pool. -.Pp -This property can also be referred to by its shortened column name, -.Sy avail . -.It Sy compressratio -For non-snapshots, the compression ratio achieved for the -.Sy used -space of this dataset, expressed as a multiplier. The -.Sy used -property includes descendant datasets, and, for clones, does not include -the space shared with the origin snapshot. For snapshots, the -.Sy compressratio -is the same as the -.Sy refcompressratio -property. Compression can be turned on by running: -.Qq Nm Cm set compression=on Ar dataset -The default value is -.Cm off . -.It Sy createtxg -The transaction group (txg) in which the dataset was created. -Bookmarks have the same -.Sy createtxg -as the snapshot they are initially tied to. -This property is suitable for ordering a list of snapshots, -e.g. for incremental send and receive. -.It Sy creation -The time this dataset was created. -.It Sy clones -For snapshots, this property is a comma-separated list of filesystems or -volumes which are clones of this snapshot. The clones' -.Sy origin -property is this snapshot. If the -.Sy clones -property is not empty, then this snapshot can not be destroyed (even with the -.Fl r -or -.Fl f -options). -.It Sy defer_destroy -This property is -.Cm on -if the snapshot has been marked for deferred destroy by using the -.Qq Nm Cm destroy -d -command. Otherwise, the property is -.Cm off . -.It Sy filesystem_count -The total number of filesystems and volumes that exist under this location in the -dataset tree. -This value is only available when a -.Sy filesystem_limit -has -been set somewhere in the tree under which the dataset resides. -.It Sy guid -The 64 bit GUID of this dataset or bookmark which does not change over its -entire lifetime. -When a snapshot is sent to another pool, the received snapshot has the same -GUID. -Thus, the -.Sy guid -is suitable to identify a snapshot across pools. -.It Sy logicalreferenced -The amount of space that is -.Qq logically -accessible by this dataset. -See the -.Sy referenced -property. -The logical space ignores the effect of the -.Sy compression -and -.Sy copies -properties, giving a quantity closer to the amount of data that applications -see. -However, it does include space consumed by metadata. -.Pp -This property can also be referred to by its shortened column name, -.Sy lrefer . -.It Sy logicalused -The amount of space that is -.Qq logically -consumed by this dataset and all its descendents. -See the -.Sy used -property. -The logical space ignores the effect of the -.Sy compression -and -.Sy copies -properties, giving a quantity closer to the amount of data that applications -see. -.Pp -This property can also be referred to by its shortened column name, -.Sy lused . -.It Sy mounted -For file systems, indicates whether the file system is currently mounted. This -property can be either -.Cm yes -or -.Cm no . -.It Sy origin -For cloned file systems or volumes, the snapshot from which the clone was -created. See also the -.Sy clones -property. -.It Sy receive_resume_token -For filesystems or volumes which have saved partially-completed state from -.Sy zfs receive -s , -this opaque token can be provided to -.Sy zfs send -t -to resume and complete the -.Sy zfs receive . -.It Sy referenced -The amount of data that is accessible by this dataset, which may or may not be -shared with other datasets in the pool. When a snapshot or clone is created, it -initially references the same amount of space as the file system or snapshot it -was created from, since its contents are identical. -.Pp -This property can also be referred to by its shortened column name, -.Sy refer . -.It Sy refcompressratio -The compression ratio achieved for the -.Sy referenced -space of this dataset, expressed as a multiplier. See also the -.Sy compressratio -property. -.It Sy snapshot_count -The total number of snapshots that exist under this location in the dataset tree. -This value is only available when a -.Sy snapshot_limit -has been set somewhere -in the tree under which the dataset resides. -.It Sy type -The type of dataset: -.Sy filesystem , volume , No or Sy snapshot . -.It Sy used -The amount of space consumed by this dataset and all its descendents. This is -the value that is checked against this dataset's quota and reservation. The -space used does not include this dataset's reservation, but does take into -account the reservations of any descendent datasets. The amount of space that a -dataset consumes from its parent, as well as the amount of space that are freed -if this dataset is recursively destroyed, is the greater of its space used and -its reservation. -.Pp -When snapshots (see the -.Qq Sx Snapshots -section) are created, their space is -initially shared between the snapshot and the file system, and possibly with -previous snapshots. As the file system changes, space that was previously -shared becomes unique to the snapshot, and counted in the snapshot's space -used. Additionally, deleting snapshots can increase the amount of space unique -to (and used by) other snapshots. -.Pp -The amount of space used, available, or referenced does not take into account -pending changes. Pending changes are generally accounted for within a few -seconds. Committing a change to a disk using -.Xr fsync 2 -or -.Sy O_SYNC -does not necessarily guarantee that the space usage information is updated -immediately. -.It Sy usedby* -The -.Sy usedby* -properties decompose the -.Sy used -properties into the various reasons that space is used. Specifically, -.Sy used No = -.Sy usedbysnapshots + usedbydataset + usedbychildren + usedbyrefreservation . -These properties are only available for datasets created -with -.Tn ZFS -pool version 13 pools and higher. -.It Sy usedbysnapshots -The amount of space consumed by snapshots of this dataset. In particular, it is -the amount of space that would be freed if all of this dataset's snapshots were -destroyed. Note that this is not simply the sum of the snapshots' -.Sy used -properties because space can be shared by multiple snapshots. -.It Sy usedbydataset -The amount of space used by this dataset itself, which would be freed if the -dataset were destroyed (after first removing any -.Sy refreservation -and destroying any necessary snapshots or descendents). -.It Sy usedbychildren -The amount of space used by children of this dataset, which would be freed if -all the dataset's children were destroyed. -.It Sy usedbyrefreservation -The amount of space used by a -.Sy refreservation -set on this dataset, which would be freed if the -.Sy refreservation -was removed. -.It Sy userused@ Ns Ar user -The amount of space consumed by the specified user in this dataset. Space is -charged to the owner of each file, as displayed by -.Qq Nm ls Fl l . -The amount of space charged is displayed by -.Qq Nm du -and -.Qq Nm ls Fl s . -See the -.Qq Nm Cm userspace -subcommand for more information. -.Pp -Unprivileged users can access only their own space usage. The root user, or a -user who has been granted the -.Sy userused -privilege with -.Qq Nm Cm allow , -can access everyone's usage. -.Pp -The -.Sy userused@ Ns ... -properties are not displayed by -.Qq Nm Cm get all . -The user's name must be appended after the -.Sy @ -symbol, using one of the following forms: -.Bl -bullet -offset 2n -.It -POSIX name (for example, -.Em joe ) -.It -POSIX numeric ID (for example, -.Em 1001 ) -.El -.It Sy userrefs -This property is set to the number of user holds on this snapshot. User holds -are set by using the -.Qq Nm Cm hold -command. -.It Sy groupused@ Ns Ar group -The amount of space consumed by the specified group in this dataset. Space is -charged to the group of each file, as displayed by -.Nm ls Fl l . -See the -.Sy userused@ Ns Ar user -property for more information. -.Pp -Unprivileged users can only access their own groups' space usage. The root -user, or a user who has been granted the -.Sy groupused -privilege with -.Qq Nm Cm allow , -can access all groups' usage. -.It Sy volblocksize Ns = Ns Ar blocksize -For volumes, specifies the block size of the volume. The -.Ar blocksize -cannot be changed once the volume has been written, so it should be set at -volume creation time. The default -.Ar blocksize -for volumes is 8 Kbytes. Any -power of 2 from 512 bytes to 128 Kbytes is valid. -.Pp -This property can also be referred to by its shortened column name, -.Sy volblock . -.It Sy written -The amount of -.Sy referenced -space written to this dataset since the previous snapshot. -.It Sy written@ Ns Ar snapshot -The amount of -.Sy referenced -space written to this dataset since the specified snapshot. This is the space -that is referenced by this dataset but was not referenced by the specified -snapshot. -.Pp -The -.Ar snapshot -may be specified as a short snapshot name (just the part after the -.Sy @ ) , -in which case it will be interpreted as a snapshot in the same filesystem as -this dataset. The -.Ar snapshot -may be a full snapshot name -.Pq Em filesystem@snapshot , -which for clones may be a snapshot in the origin's filesystem (or the origin of -the origin's filesystem, etc). -.El -.Pp -The following native properties can be used to change the behavior of a -.Tn ZFS -dataset. -.Bl -tag -width 2n -.It Xo -.Sy aclinherit Ns = Ns Cm discard | -.Cm noallow | -.Cm restricted | -.Cm passthrough | -.Cm passthrough-x -.Xc -Controls how -.Tn ACL -entries are inherited when files and directories are created. A file system -with an -.Sy aclinherit -property of -.Cm discard -does not inherit any -.Tn ACL -entries. A file system with an -.Sy aclinherit -property value of -.Cm noallow -only inherits inheritable -.Tn ACL -entries that specify "deny" permissions. The property value -.Cm restricted -(the default) removes the -.Em write_acl -and -.Em write_owner -permissions when the -.Tn ACL -entry is inherited. A file system with an -.Sy aclinherit -property value of -.Cm passthrough -inherits all inheritable -.Tn ACL -entries without any modifications made to the -.Tn ACL -entries when they are inherited. A file system with an -.Sy aclinherit -property value of -.Cm passthrough-x -has the same meaning as -.Cm passthrough , -except that the -.Em owner@ , group@ , No and Em everyone@ Tn ACE Ns s -inherit the execute permission only if the file creation mode also requests the -execute bit. -.Pp -When the property value is set to -.Cm passthrough , -files are created with a mode determined by the inheritable -.Tn ACE Ns s. -If no inheritable -.Tn ACE Ns s -exist that affect the mode, then the mode is set in accordance to the requested -mode from the application. -.It Sy aclmode Ns = Ns Cm discard | groupmask | passthrough | restricted -Controls how an -.Tn ACL -is modified during -.Xr chmod 2 . -A file system with an -.Sy aclmode -property of -.Cm discard -(the default) deletes all -.Tn ACL -entries that do not represent the mode of the file. An -.Sy aclmode -property of -.Cm groupmask -reduces permissions granted in all -.Em ALLOW -entries found in the -.Tn ACL -such that they are no greater than the group permissions specified by -.Xr chmod 2 . -A file system with an -.Sy aclmode -property of -.Cm passthrough -indicates that no changes are made to the -.Tn ACL -other than creating or updating the necessary -.Tn ACL -entries to represent the new mode of the file or directory. -An -.Sy aclmode -property of -.Cm restricted -will cause the -.Xr chmod 2 -operation to return an error when used on any file or directory which has -a non-trivial -.Tn ACL -whose entries can not be represented by a mode. -.Xr chmod 2 -is required to change the set user ID, set group ID, or sticky bits on a file -or directory, as they do not have equivalent -.Tn ACL -entries. -In order to use -.Xr chmod 2 -on a file or directory with a non-trivial -.Tn ACL -when -.Sy aclmode -is set to -.Cm restricted , -you must first remove all -.Tn ACL -entries which do not represent the current mode. -.It Sy atime Ns = Ns Cm on | off -Controls whether the access time for files is updated when they are read. -Turning this property off avoids producing write traffic when reading files and -can result in significant performance gains, though it might confuse mailers -and other similar utilities. The default value is -.Cm on . -.It Sy canmount Ns = Ns Cm on | off | noauto -If this property is set to -.Cm off , -the file system cannot be mounted, and is ignored by -.Qq Nm Cm mount Fl a . -Setting this property to -.Cm off -is similar to setting the -.Sy mountpoint -property to -.Cm none , -except that the dataset still has a normal -.Sy mountpoint -property, which can be inherited. Setting this property to -.Cm off -allows datasets to be used solely as a mechanism to inherit properties. One -example of setting -.Sy canmount Ns = Ns Cm off -is to have two datasets with the same -.Sy mountpoint , -so that the children of both datasets appear in the same directory, but might -have different inherited characteristics. -.Pp -When the -.Cm noauto -value is set, a dataset can only be mounted and unmounted explicitly. The -dataset is not mounted automatically when the dataset is created or imported, -nor is it mounted by the -.Qq Nm Cm mount Fl a -command or unmounted by the -.Qq Nm Cm umount Fl a -command. -.Pp -This property is not inherited. -.It Sy checksum Ns = Ns Cm on | off | fletcher2 | fletcher4 | sha256 | noparity | sha512 | skein -Controls the checksum used to verify data integrity. The default value is -.Cm on , -which automatically selects an appropriate algorithm (currently, -.Cm fletcher4 , -but this may change in future releases). The value -.Cm off -disables integrity checking on user data. -The value -.Cm noparity -not only -disables integrity but also disables maintaining parity for user data. This -setting is used internally by a dump device residing on a RAID-Z pool and should -not be used by any other dataset. -Disabling checksums is -.Em NOT -a recommended practice. -The -.Sy sha512 , -and -.Sy skein -checksum algorithms require enabling the appropriate features on the pool. -Please see -.Xr zpool-features 7 -for more information on these algorithms. -.Pp -Changing this property affects only newly-written data. -.Pp -The salted checksum algorithm -.Pq Cm edonr -is currently not supported on FreeBSD. -.It Sy compression Ns = Ns Cm on | off | lzjb | gzip | gzip- Ns Ar N | Cm zle | Cm lz4 -Controls the compression algorithm used for this dataset. -Setting compression to -.Cm on -indicates that the current default compression algorithm should be used. -The default balances compression and decompression speed, with compression -ratio and is expected to work well on a wide variety of workloads. -Unlike all other settings for this property, on does not select a fixed -compression type. -As new compression algorithms are added to ZFS and enabled on a pool, the -default compression algorithm may change. -The current default compression algorthm is either -.Cm lzjb -or, if the -.Sy lz4_compress -feature is enabled, -.Cm lz4 . -The -.Cm lzjb -compression algorithm is optimized for performance while providing decent data -compression. Setting compression to -.Cm on -uses the -.Cm lzjb -compression algorithm. The -.Cm gzip -compression algorithm uses the same compression as the -.Xr gzip 1 -command. You can specify the -.Cm gzip -level by using the value -.Cm gzip- Ns Ar N -where -.Ar N -is an integer from 1 (fastest) to 9 (best compression ratio). Currently, -.Cm gzip -is equivalent to -.Cm gzip-6 -(which is also the default for -.Xr gzip 1 ) . -The -.Cm zle -compression algorithm compresses runs of zeros. -.Pp -The -.Sy lz4 -compression algorithm is a high-performance replacement -for the -.Sy lzjb -algorithm. It features significantly faster -compression and decompression, as well as a moderately higher -compression ratio than -.Sy lzjb , -but can only be used on pools with -the -.Sy lz4_compress -feature set to -.Sy enabled . -See -.Xr zpool-features 7 -for details on ZFS feature flags and the -.Sy lz4_compress -feature. -.Pp -This property can also be referred to by its shortened column name -.Cm compress . -Changing this property affects only newly-written data. -.It Sy copies Ns = Ns Cm 1 | 2 | 3 -Controls the number of copies of data stored for this dataset. These copies are -in addition to any redundancy provided by the pool, for example, mirroring or -RAID-Z. The copies are stored on different disks, if possible. The space used -by multiple copies is charged to the associated file and dataset, changing the -.Sy used -property and counting against quotas and reservations. -.Pp -Changing this property only affects newly-written data. Therefore, set this -property at file system creation time by using the -.Fl o Cm copies= Ns Ar N -option. -.It Sy dedup Ns = Ns Cm on | off | verify | sha256 Ns Oo Cm ,verify Oc | Sy sha512 Ns Oo Cm ,verify Oc | Sy skein Ns Oo Cm ,verify Oc -Configures deduplication for a dataset. The default value is -.Cm off . -The default deduplication checksum is -.Cm sha256 -(this may change in the future). -When -.Sy dedup -is enabled, the checksum defined here overrides the -.Sy checksum -property. Setting the value to -.Cm verify -has the same effect as the setting -.Cm sha256,verify . -.Pp -If set to -.Cm verify , -.Tn ZFS -will do a byte-to-byte comparsion in case of two blocks having the same -signature to make sure the block contents are identical. -.It Sy devices Ns = Ns Cm on | off -The -.Sy devices -property is currently not supported on -.Fx . -.It Sy exec Ns = Ns Cm on | off -Controls whether processes can be executed from within this file system. The -default value is -.Cm on . -.It Sy mlslabel Ns = Ns Ar label | Cm none -The -.Sy mlslabel -property is currently not supported on -.Fx . -.It Sy filesystem_limit Ns = Ns Ar count | Cm none -Limits the number of filesystems and volumes that can exist under this point in -the dataset tree. -The limit is not enforced if the user is allowed to change -the limit. -Setting a -.Sy filesystem_limit -on a descendent of a filesystem that -already has a -.Sy filesystem_limit -does not override the ancestor's -.Sy filesystem_limit , -but rather imposes an additional limit. -This feature must be enabled to be used -.Po see -.Xr zpool-features 7 -.Pc . -.It Sy special_small_blocks Ns = Ns Ar size -This value represents the threshold block size for including small file -blocks into the special allocation class. -Blocks smaller than or equal to this value will be assigned to the special -allocation class while greater blocks will be assigned to the regular class. -Valid values are zero or a power of two from 512B up to 128K. -The default size is 0 which means no small file blocks will be allocated in -the special class. -.Pp -Before setting this property, a special class vdev must be added to the -pool. -See -.Xr zpool 8 -for more details on the special allocation class. -.It Sy mountpoint Ns = Ns Ar path | Cm none | legacy -Controls the mount point used for this file system. -See the -.Qq Sx Mount Points -section for more information on how this property is used. -.Pp -When the -.Sy mountpoint -property is changed for a file system, the file system and any children that -inherit the mount point are unmounted. If the new value is -.Cm legacy , -then they remain unmounted. Otherwise, they are automatically remounted in the -new location if the property was previously -.Cm legacy -or -.Cm none , -or if they were mounted before the property was changed. In addition, any -shared file systems are unshared and shared in the new location. -.It Sy nbmand Ns = Ns Cm on | off -The -.Sy nbmand -property is currently not supported on -.Fx . -.It Sy primarycache Ns = Ns Cm all | none | metadata -Controls what is cached in the primary cache (ARC). If this property is set to -.Cm all , -then both user data and metadata is cached. If this property is set to -.Cm none , -then neither user data nor metadata is cached. If this property is set to -.Cm metadata , -then only metadata is cached. The default value is -.Cm all . -.It Sy quota Ns = Ns Ar size | Cm none -Limits the amount of space a dataset and its descendents can consume. This -property enforces a hard limit on the amount of space used. This includes all -space consumed by descendents, including file systems and snapshots. Setting a -quota on a descendent of a dataset that already has a quota does not override -the ancestor's quota, but rather imposes an additional limit. -.Pp -Quotas cannot be set on volumes, as the -.Sy volsize -property acts as an implicit quota. -.It Sy snapshot_limit Ns = Ns Ar count | Cm none -Limits the number of snapshots that can be created on a dataset and its -descendents. -Setting a -.Sy snapshot_limit -on a descendent of a dataset that already -has a -.Sy snapshot_limit -does not override the ancestor's -.Sy snapshot_limit , -but -rather imposes an additional limit. -The limit is not enforced if the user is -allowed to change the limit. -For example, this means that recursive snapshots -taken from the global zone are counted against each delegated dataset within -a jail. -This feature must be enabled to be used -.Po see -.Xr zpool-features 7 -.Pc . -.It Sy userquota@ Ns Ar user Ns = Ns Ar size | Cm none -Limits the amount of space consumed by the specified user. -Similar to the -.Sy refquota -property, the -.Sy userquota -space calculation does not include space that is used by descendent datasets, -such as snapshots and clones. User space consumption is identified by the -.Sy userspace@ Ns Ar user -property. -.Pp -Enforcement of user quotas may be delayed by several seconds. This delay means -that a user might exceed their quota before the system notices that they are -over quota and begins to refuse additional writes with the -.Em EDQUOT -error message. See the -.Cm userspace -subcommand for more information. -.Pp -Unprivileged users can only access their own groups' space usage. The root -user, or a user who has been granted the -.Sy userquota -privilege with -.Qq Nm Cm allow , -can get and set everyone's quota. -.Pp -This property is not available on volumes, on file systems before version 4, or -on pools before version 15. The -.Sy userquota@ Ns ... -properties are not displayed by -.Qq Nm Cm get all . -The user's name must be appended after the -.Sy @ -symbol, using one of the following forms: -.Bl -bullet -offset 2n -.It -POSIX name (for example, -.Em joe ) -.It -POSIX numeric ID (for example, -.Em 1001 ) -.El -.It Sy groupquota@ Ns Ar group Ns = Ns Ar size | Cm none -Limits the amount of space consumed by the specified group. Group space -consumption is identified by the -.Sy userquota@ Ns Ar user -property. -.Pp -Unprivileged users can access only their own groups' space usage. The root -user, or a user who has been granted the -.Sy groupquota -privilege with -.Qq Nm Cm allow , -can get and set all groups' quotas. -.It Sy readonly Ns = Ns Cm on | off -Controls whether this dataset can be modified. The default value is -.Cm off . -.It Sy recordsize Ns = Ns Ar size -Specifies a suggested block size for files in the file system. This property is -designed solely for use with database workloads that access files in fixed-size -records. -.Tn ZFS -automatically tunes block sizes according to internal algorithms optimized for -typical access patterns. -.Pp -For databases that create very large files but access them in small random -chunks, these algorithms may be suboptimal. Specifying a -.Sy recordsize -greater than or equal to the record size of the database can result in -significant performance gains. Use of this property for general purpose file -systems is strongly discouraged, and may adversely affect performance. -.Pp -The size specified must be a power of two greater than or equal to 512 and less -than or equal to 128 Kbytes. -If the -.Sy large_blocks -feature is enabled on the pool, the size may be up to 1 Mbyte. -See -.Xr zpool-features 7 -for details on ZFS feature flags. -.Pp -Changing the file system's -.Sy recordsize -affects only files created afterward; existing files are unaffected. -.Pp -This property can also be referred to by its shortened column name, -.Sy recsize . -.It Sy redundant_metadata Ns = Ns Cm all | most -Controls what types of metadata are stored redundantly. -ZFS stores an extra copy of metadata, so that if a single block is corrupted, -the amount of user data lost is limited. -This extra copy is in addition to any redundancy provided at the pool level -.Pq e.g. by mirroring or RAID-Z , -and is in addition to an extra copy specified by the -.Sy copies -property -.Pq up to a total of 3 copies . -For example if the pool is mirrored, -.Cm copies Ns = Ns Ar 2 , -and -.Cm redundant_metadata Ns = Ns Ar most , -then ZFS -stores 6 copies of most metadata, and 4 copies of data and some -metadata. -.Pp -When set to -.Cm all , -ZFS stores an extra copy of all metadata. -If a -single on-disk block is corrupt, at worst a single block of user data -.Po which is -.Cm recordsize -bytes long -can be lost. -.Pc -.Pp -When set to -.Cm most , -ZFS stores an extra copy of most types of -metadata. -This can improve performance of random writes, because less -metadata must be written. -In practice, at worst about 100 blocks -.Po of -.Cm recordsize -bytes each -.Pc -of user data can be lost if a single -on-disk block is corrupt. -The exact behavior of which metadata blocks -are stored redundantly may change in future releases. -.Pp -The default value is -.Cm all . -.It Sy refquota Ns = Ns Ar size | Cm none -Limits the amount of space a dataset can consume. This property enforces a hard -limit on the amount of space used. This hard limit does not include space used -by descendents, including file systems and snapshots. -.It Sy refreservation Ns = Ns Ar size | Cm none | Cm auto -The minimum amount of space guaranteed to a dataset, not including its -descendents. When the amount of space used is below this value, the dataset is -treated as if it were taking up the amount of space specified by -.Sy refreservation . -The -.Sy refreservation -reservation is accounted for in the parent datasets' space used, and counts -against the parent datasets' quotas and reservations. -.Pp -If -.Sy refreservation -is set, a snapshot is only allowed if there is enough free pool space outside -of this reservation to accommodate the current number of "referenced" bytes in -the dataset. -.Pp -If -.Sy refreservation -is set to -.Sy auto , -a volume is thick provisioned or not sparse. -.Sy refreservation Ns = Cm auto -is only supported on volumes. -See -.Sy volsize -in the Native Properties -section for more information about sparse volumes. -.Pp -This property can also be referred to by its shortened column name, -.Sy refreserv . -.It Sy reservation Ns = Ns Ar size | Cm none -The minimum amount of space guaranteed to a dataset and its descendents. When -the amount of space used is below this value, the dataset is treated as if it -were taking up the amount of space specified by its reservation. Reservations -are accounted for in the parent datasets' space used, and count against the -parent datasets' quotas and reservations. -.Pp -This property can also be referred to by its shortened column name, -.Sy reserv . -.It Sy secondarycache Ns = Ns Cm all | none | metadata -Controls what is cached in the secondary cache (L2ARC). If this property is set -to -.Cm all , -then both user data and metadata is cached. If this property is set to -.Cm none , -then neither user data nor metadata is cached. If this property is set to -.Cm metadata , -then only metadata is cached. The default value is -.Cm all . -.It Sy setuid Ns = Ns Cm on | off -Controls whether the -.No set- Ns Tn UID -bit is respected for the file system. The default value is -.Cm on . -.It Sy sharesmb Ns = Ns Cm on | off | Ar opts -The -.Sy sharesmb -property currently has no effect on -.Fx . -.It Sy sharenfs Ns = Ns Cm on | off | Ar opts -Controls whether the file system is shared via -.Tn NFS , -and what options are used. A file system with a -.Sy sharenfs -property of -.Cm off -is managed the traditional way via -.Xr exports 5 . -Otherwise, the file system is automatically shared and unshared with the -.Qq Nm Cm share -and -.Qq Nm Cm unshare -commands. If the property is set to -.Cm on -no -.Tn NFS -export options are used. Otherwise, -.Tn NFS -export options are equivalent to the contents of this property. The export -options may be comma-separated. See -.Xr exports 5 -for a list of valid options. -.Pp -When the -.Sy sharenfs -property is changed for a dataset, the -.Xr mountd 8 -daemon is reloaded. -.It Sy logbias Ns = Ns Cm latency | throughput -Provide a hint to -.Tn ZFS -about handling of synchronous requests in this dataset. -If -.Sy logbias -is set to -.Cm latency -(the default), -.Tn ZFS -will use pool log devices (if configured) to handle the requests at low -latency. If -.Sy logbias -is set to -.Cm throughput , -.Tn ZFS -will not use configured pool log devices. -.Tn ZFS -will instead optimize synchronous operations for global pool throughput and -efficient use of resources. -.It Sy snapdir Ns = Ns Cm hidden | visible -Controls whether the -.Pa \&.zfs -directory is hidden or visible in the root of the file system as discussed in -the -.Qq Sx Snapshots -section. The default value is -.Cm hidden . -.It Sy sync Ns = Ns Cm standard | always | disabled -Controls the behavior of synchronous requests (e.g. -.Xr fsync 2 , -O_DSYNC). This property accepts the following values: -.Bl -tag -offset 4n -width 8n -.It Sy standard -This is the POSIX specified behavior of ensuring all synchronous requests are -written to stable storage and all devices are flushed to ensure data is not -cached by device controllers (this is the default). -.It Sy always -All file system transactions are written and flushed before their system calls -return. This has a large performance penalty. -.It Sy disabled -Disables synchronous requests. File system transactions are only committed to -stable storage periodically. This option will give the highest performance. -However, it is very dangerous as -.Tn ZFS -would be ignoring the synchronous transaction demands of applications such as -databases or -.Tn NFS . -Administrators should only use this option when the risks are understood. -.El -.It Sy volsize Ns = Ns Ar size -For volumes, specifies the logical size of the volume. By default, creating a -volume establishes a reservation of equal size. For storage pools with a -version number of 9 or higher, a -.Sy refreservation -is set instead. Any changes to -.Sy volsize -are reflected in an equivalent change to the reservation (or -.Sy refreservation ) . -The -.Sy volsize -can only be set to a multiple of -.Cm volblocksize , -and cannot be zero. -.Pp -The reservation is kept equal to the volume's logical size to prevent -unexpected behavior for consumers. Without the reservation, the volume could -run out of space, resulting in undefined behavior or data corruption, depending -on how the volume is used. These effects can also occur when the volume size is -changed while it is in use (particularly when shrinking the size). Extreme care -should be used when adjusting the volume size. -.Pp -Though not recommended, a "sparse volume" (also known as "thin provisioned") -can be created by specifying the -.Fl s -option to the -.Qq Nm Cm create Fl V -command, or by changing the value of the -.Sy refreservation -property, or -.Sy reservation -property on pool -.Po -version 8 or earlier -.Pc -after the volume has been created. -A "sparse volume" is a volume where the value of -.Sy refreservation -is less then the size of the volume plus the space required to store its -metadata. -Consequently, writes to a sparse volume can fail with -.Sy ENOSPC -when the pool is low on space. For a sparse volume, changes to -.Sy volsize -are not reflected in the -.Sy refreservation . -A volume that is not sparse is said to be "thick provisioned". -A sparse volume can become thick provisioned by setting -.Sy refreservation -to -.Sy auto . -.It Sy volmode Ns = Ns Cm default | geom | dev | none -This property specifies how volumes should be exposed to the OS. -Setting it to -.Sy geom -exposes volumes as -.Xr geom 4 -providers, providing maximal functionality. -Setting it to -.Sy dev -exposes volumes only as cdev device in devfs. -Such volumes can be accessed only as raw disk device files, i.e. they -can not be partitioned, mounted, participate in RAIDs, etc, but they -are faster, and in some use scenarios with untrusted consumer, such as -NAS or VM storage, can be more safe. -Volumes with property set to -.Sy none -are not exposed outside ZFS, but can be snapshoted, cloned, replicated, etc, -that can be suitable for backup purposes. -Value -.Sy default -means that volumes exposition is controlled by system-wide sysctl/tunable -.Va vfs.zfs.vol.mode , -where -.Sy geom , -.Sy dev -and -.Sy none -are encoded as 1, 2 and 3 respectively. -The default values is -.Sy geom . -This property can be changed any time, but so far it is processed only -during volume creation and pool import. -.It Sy vscan Ns = Ns Cm off | on -The -.Sy vscan -property is currently not supported on -.Fx . -.It Sy xattr Ns = Ns Cm off | on -The -.Sy xattr -property is currently not supported on -.Fx . -.It Sy jailed Ns = Ns Cm off | on -Controls whether the dataset is managed from a jail. See the -.Qq Sx Jails -section for more information. The default value is -.Cm off . -.El -.Pp -The following three properties cannot be changed after the file system is -created, and therefore, should be set when the file system is created. If the -properties are not set with the -.Qq Nm Cm create -or -.Nm zpool Cm create -commands, these properties are inherited from the parent dataset. If the parent -dataset lacks these properties due to having been created prior to these -features being supported, the new file system will have the default values for -these properties. -.Bl -tag -width 4n -.It Sy casesensitivity Ns = Ns Cm sensitive | insensitive | mixed -Indicates whether the file name matching algorithm used by the file system -should be case-sensitive, case-insensitive, or allow a combination of both -styles of matching. The default value for the -.Sy casesensitivity -property is -.Cm sensitive . -Traditionally, UNIX and POSIX file systems have case-sensitive file names. -.Pp -The -.Cm mixed -value for the -.Sy casesensitivity -property indicates that the -file system can support requests for both case-sensitive and case-insensitive -matching behavior. -.It Sy normalization Ns = Ns Cm none | formC | formD | formKC | formKD -Indicates whether the file system should perform a -.Sy unicode -normalization of file names whenever two file names are compared, and which -normalization algorithm should be used. File names are always stored -unmodified, names are normalized as part of any comparison process. If this -property is set to a legal value other than -.Cm none , -and the -.Sy utf8only -property was left unspecified, the -.Sy utf8only -property is automatically set to -.Cm on . -The default value of the -.Sy normalization -property is -.Cm none . -This property cannot be changed after the file system is created. -.It Sy utf8only Ns = Ns Cm on | off -Indicates whether the file system should reject file names that include -characters that are not present in the -.Sy UTF-8 -character code set. If this property is explicitly set to -.Cm off , -the normalization property must either not be explicitly set or be set to -.Cm none . -The default value for the -.Sy utf8only -property is -.Cm off . -This property cannot be changed after the file system is created. -.El -.Pp -The -.Sy casesensitivity , normalization , No and Sy utf8only -properties are also new permissions that can be assigned to non-privileged -users by using the -.Tn ZFS -delegated administration feature. -.Ss Temporary Mount Point Properties -When a file system is mounted, either through -.Xr mount 8 -for legacy mounts or the -.Qq Nm Cm mount -command for normal file systems, its mount options are set according to its -properties. The correlation between properties and mount options is as follows: -.Bl -column -offset 4n "PROPERTY" "MOUNT OPTION" -.It "PROPERTY MOUNT OPTION" -.It "atime atime/noatime" -.It "exec exec/noexec" -.It "readonly ro/rw" -.It "setuid suid/nosuid" -.El -.Pp -In addition, these options can be set on a per-mount basis using the -.Fl o -option, without affecting the property that is stored on disk. The values -specified on the command line override the values stored in the dataset. These -properties are reported as "temporary" by the -.Qq Nm Cm get -command. If the properties are changed while the dataset is mounted, the new -setting overrides any temporary settings. -.Ss User Properties -In addition to the standard native properties, -.Tn ZFS -supports arbitrary user properties. User properties have no effect on -.Tn ZFS -behavior, but applications or administrators can use them to annotate datasets -(file systems, volumes, and snapshots). -.Pp -User property names must contain a colon -.Pq Sy \&: -character to distinguish them from native properties. They may contain -lowercase letters, numbers, and the following punctuation characters: colon -.Pq Sy \&: , -dash -.Pq Sy \&- , -period -.Pq Sy \&. -and underscore -.Pq Sy \&_ . -The expected convention is that the property name is divided into two portions -such as -.Em module Ns Sy \&: Ns Em property , -but this namespace is not enforced by -.Tn ZFS . -User property names can be at most 256 characters, and cannot begin with a dash -.Pq Sy \&- . -.Pp -When making programmatic use of user properties, it is strongly suggested to -use a reversed -.Tn DNS -domain name for the -.Ar module -component of property names to reduce the chance that two -independently-developed packages use the same property name for different -purposes. Property names beginning with -.Em com.sun -are reserved for use by Sun Microsystems. -.Pp -The values of user properties are arbitrary strings, are always inherited, and -are never validated. All of the commands that operate on properties -.Po -.Qq Nm Cm list , -.Qq Nm Cm get , -.Qq Nm Cm set -and so forth -.Pc -can be used to manipulate both native properties and user properties. Use the -.Qq Nm Cm inherit -command to clear a user property. If the property is not defined in any parent -dataset, it is removed entirely. Property values are limited to 1024 -characters. -.Sh SUBCOMMANDS -All subcommands that modify state are logged persistently to the pool in their -original form. -.Bl -tag -width 2n -.It Xo -.Nm -.Op Fl \&? -.Xc -.Pp -Displays a help message. -.It Xo -.Nm -.Cm create -.Op Fl pu -.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ... -.Ar filesystem -.Xc -.Pp -Creates a new -.Tn ZFS -file system. The file system is automatically mounted according to the -.Sy mountpoint -property inherited from the parent. -.Bl -tag -width indent -.It Fl p -Creates all the non-existing parent datasets. Datasets created in this manner -are automatically mounted according to the -.Sy mountpoint -property inherited from their parent. Any property specified on the command -line using the -.Fl o -option is ignored. If the target filesystem already exists, the operation -completes successfully. -.It Fl u -Newly created file system is not mounted. -.It Fl o Ar property Ns = Ns Ar value -Sets the specified property as if the command -.Qq Nm Cm set Ar property Ns = Ns Ar value -was invoked at the same time the dataset was created. Any editable -.Tn ZFS -property can also be set at creation time. Multiple -.Fl o -options can be specified. An error results if the same property is specified in -multiple -.Fl o -options. -.El -.It Xo -.Nm -.Cm create -.Op Fl ps -.Op Fl b Ar blocksize -.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ... -.Fl V -.Ar size volume -.Xc -.Pp -Creates a volume of the given size. The volume is exported as a block device in -.Pa /dev/zvol/path , -where -.Ar path -is the name of the volume in the -.Tn ZFS -namespace. The size represents the logical size as exported by the device. By -default, a reservation of equal size is created. -.Pp -.Ar size -is automatically rounded up to the nearest 128 Kbytes to ensure that -the volume has an integral number of blocks regardless of -.Ar blocksize . -.Bl -tag -width indent -.It Fl p -Creates all the non-existing parent datasets. Datasets created in this manner -are automatically mounted according to the -.Sy mountpoint -property inherited from their parent. Any property specified on the command -line using the -.Fl o -option is ignored. If the target filesystem already exists, the operation -completes successfully. -.It Fl s -Creates a sparse volume with no reservation. See -.Sy volsize -in the -.Qq Sx Native Properties -section for more information about sparse volumes. -.It Fl b Ar blocksize -Equivalent to -.Fl o Cm volblocksize Ns = Ns Ar blocksize . -If this option is specified in conjunction with -.Fl o Cm volblocksize , -the resulting behavior is undefined. -.It Fl o Ar property Ns = Ns Ar value -Sets the specified property as if the -.Qq Nm Cm set Ar property Ns = Ns Ar value -command was invoked at the same time the dataset was created. Any editable -.Tn ZFS -property can also be set at creation time. Multiple -.Fl o -options can be specified. An error results if the same property is specified in -multiple -.Fl o -options. -.El -.It Xo -.Nm -.Cm destroy -.Op Fl fnpRrv -.Ar filesystem Ns | Ns Ar volume -.Xc -.Pp -Destroys the given dataset. By default, the command unshares any file systems -that are currently shared, unmounts any file systems that are currently -mounted, and refuses to destroy a dataset that has active dependents (children -or clones). -.Bl -tag -width indent -.It Fl r -Recursively destroy all children. -.It Fl R -Recursively destroy all dependents, including cloned file systems outside the -target hierarchy. -.It Fl f -Force an unmount of any file systems using the -.Qq Nm Cm unmount Fl f -command. This option has no effect on non-file systems or unmounted file -systems. -.It Fl n -Do a dry-run ("No-op") deletion. No data will be deleted. This is useful in -conjunction with the -.Fl v -or -.Fl p -flags to determine what data would be deleted. -.It Fl p -Print machine-parsable verbose information about the deleted data. -.It Fl v -Print verbose information about the deleted data. -.El -.Pp -Extreme care should be taken when applying either the -.Fl r -or the -.Fl R -options, as they can destroy large portions of a pool and cause unexpected -behavior for mounted file systems in use. -.It Xo -.Nm -.Cm destroy -.Op Fl dnpRrv -.Sm off -.Ar snapshot -.Op % Ns Ar snapname -.Op , Ns ... -.Sm on -.Xc -.Pp -The given snapshots are destroyed immediately if and only if the -.Qq Nm Cm destroy -command without the -.Fl d -option would have destroyed it. Such immediate destruction would occur, for -example, if the snapshot had no clones and the user-initiated reference count -were zero. -.Pp -If a snapshot does not qualify for immediate destruction, it is marked for -deferred deletion. In this state, it exists as a usable, visible snapshot until -both of the preconditions listed above are met, at which point it is destroyed. -.Pp -An inclusive range of snapshots may be specified by separating the -first and last snapshots with a percent sign -.Pq Sy % . -The first and/or last snapshots may be left blank, in which case the -filesystem's oldest or newest snapshot will be implied. -.Pp -Multiple snapshots -(or ranges of snapshots) of the same filesystem or volume may be specified -in a comma-separated list of snapshots. -Only the snapshot's short name (the -part after the -.Sy @ ) -should be specified when using a range or comma-separated list to identify -multiple snapshots. -.Bl -tag -width indent -.It Fl r -Destroy (or mark for deferred deletion) all snapshots with this name in -descendent file systems. -.It Fl R -Recursively destroy all clones of these snapshots, including the clones, -snapshots, and children. -If this flag is specified, the -.Fl d -flag will have no effect. -.It Fl n -Do a dry-run ("No-op") deletion. No data will be deleted. This is useful in -conjunction with the -.Fl v -or -.Fl p -flags to determine what data would be deleted. -.It Fl p -Print machine-parsable verbose information about the deleted data. -.It Fl v -Print verbose information about the deleted data. -.It Fl d -Defer snapshot deletion. -.El -.Pp -Extreme care should be taken when applying either the -.Fl r -or the -.Fl R -options, as they can destroy large portions of a pool and cause unexpected -behavior for mounted file systems in use. -.It Xo -.Nm -.Cm destroy -.Ar filesystem Ns | Ns Ar volume Ns # Ns Ar bookmark -.Xc -.Pp -The given bookmark is destroyed. -.It Xo -.Nm -.Cm snapshot Ns | Ns Cm snap -.Op Fl r -.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ... -.Ar filesystem@snapname Ns | Ns volume@snapname -.Ar filesystem@snapname Ns | Ns volume@snapname Ns ... -.Xc -.Pp -Creates snapshots with the given names. All previous modifications by -successful system calls to the file system are part of the snapshots. -Snapshots are taken atomically, so that all snapshots correspond to the same -moment in time. See the -.Qq Sx Snapshots -section for details. -.Bl -tag -width indent -.It Fl r -Recursively create snapshots of all descendent datasets -.It Fl o Ar property Ns = Ns Ar value -Sets the specified property; see -.Qq Nm Cm create -for details. -.El -.It Xo -.Nm -.Cm rollback -.Op Fl rRf -.Ar snapshot -.Xc -.Pp -Roll back the given dataset to a previous snapshot. When a dataset is rolled -back, all data that has changed since the snapshot is discarded, and the -dataset reverts to the state at the time of the snapshot. By default, the -command refuses to roll back to a snapshot other than the most recent one. In -order to do so, all intermediate snapshots and bookmarks must be destroyed -by specifying the -.Fl r -option. -.Pp -The -.Fl rR -options do not recursively destroy the child snapshots of a -recursive snapshot. -Only direct snapshots of the specified filesystem -are destroyed by either of these options. -To completely roll back a -recursive snapshot, you must rollback the individual child snapshots. -.Bl -tag -width indent -.It Fl r -Destroy any snapshots and bookmarks more recent than the one specified. -.It Fl R -Destroy any more recent snapshots and bookmarks, as well as any clones of those -snapshots. -.It Fl f -Used with the -.Fl R -option to force an unmount of any clone file systems that are to be destroyed. -.El -.It Xo -.Nm -.Cm clone -.Op Fl p -.Oo Fl o Ar property Ns = Ns Ar value Oc Ns ... -.Ar snapshot filesystem Ns | Ns Ar volume -.Xc -.Pp -Creates a clone of the given snapshot. See the -.Qq Sx Clones -section for details. The target dataset can be located anywhere in the -.Tn ZFS -hierarchy, and is created as the same type as the original. -.Bl -tag -width indent -.It Fl p -Creates all the non-existing parent datasets. Datasets created in this manner -are automatically mounted according to the -.Sy mountpoint -property inherited from their parent. If the target filesystem or volume -already exists, the operation completes successfully. -.It Fl o Ar property Ns = Ns Ar value -Sets the specified property; see -.Qq Nm Cm create -for details. -.El -.It Xo -.Nm -.Cm promote -.Ar clone-filesystem -.Xc -.Pp -Promotes a clone file system to no longer be dependent on its "origin" -snapshot. This makes it possible to destroy the file system that the clone was -created from. The clone parent-child dependency relationship is reversed, so -that the origin file system becomes a clone of the specified file system. -.Pp -The snapshot that was cloned, and any snapshots previous to this snapshot, are -now owned by the promoted clone. The space they use moves from the origin file -system to the promoted clone, so enough space must be available to accommodate -these snapshots. No new space is consumed by this operation, but the space -accounting is adjusted. The promoted clone must not have any conflicting -snapshot names of its own. The -.Cm rename -subcommand can be used to rename any conflicting snapshots. -.It Xo -.Nm -.Cm rename -.Op Fl f -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot -.Xc -.It Xo -.Nm -.Cm rename -.Op Fl f -.Fl p -.Ar filesystem Ns | Ns Ar volume -.Ar filesystem Ns | Ns Ar volume -.Xc -.It Xo -.Nm -.Cm rename -.Fl u -.Op Fl p -.Ar filesystem filesystem -.Xc -.Pp -Renames the given dataset. The new target can be located anywhere in the -.Tn ZFS -hierarchy, with the exception of snapshots. Snapshots can only be renamed -within the parent file system or volume. When renaming a snapshot, the parent -file system of the snapshot does not need to be specified as part of the second -argument. Renamed file systems can inherit new mount points, in which case they -are unmounted and remounted at the new mount point. -.Bl -tag -width indent -.It Fl p -Creates all the nonexistent parent datasets. Datasets created in this manner -are automatically mounted according to the -.Sy mountpoint -property inherited from their parent. -.It Fl u -Do not remount file systems during rename. If a file system's -.Sy mountpoint -property is set to -.Cm legacy -or -.Cm none , -file system is not unmounted even if this option is not given. -.It Fl f -Force unmount any filesystems that need to be unmounted in the process. -This flag has no effect if used together with the -.Fl u -flag. -.El -.It Xo -.Nm -.Cm rename -.Fl r -.Ar snapshot snapshot -.Xc -.Pp -Recursively rename the snapshots of all descendent datasets. Snapshots are the -only dataset that can be renamed recursively. -.It Xo -.Nm -.Cm rename -.Ar bookmark bookmark -.Xc -.Pp -Renames the given bookmark. -Bookmarks can only be renamed within the parent file system or volume. -When renaming a bookmark, the parent file system or volume of the bookmark -does not need to be specified as part of the second argument. -.It Xo -.Nm -.Cm list -.Op Fl r Ns | Ns Fl d Ar depth -.Op Fl Hp -.Op Fl o Ar property Ns Oo , Ns Ar property Oc Ns ... -.Op Fl t Ar type Ns Oo , Ns Ar type Oc Ns ... -.Oo Fl s Ar property Oc Ns ... -.Oo Fl S Ar property Oc Ns ... -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns ... -.Xc -.Pp -Lists the property information for the given datasets in tabular form. If -specified, you can list property information by the absolute pathname or the -relative pathname. By default, all file systems and volumes are displayed. -Snapshots are displayed if the -.Sy listsnaps -property is -.Cm on -(the default is -.Cm off ) . -The following fields are displayed, -.Sy name , used , available , referenced , mountpoint . -.Bl -tag -width indent -.It Fl r -Recursively display any children of the dataset on the command line. -.It Fl d Ar depth -Recursively display any children of the dataset, limiting the recursion to -.Ar depth . -A depth of -.Sy 1 -will display only the dataset and its direct children. -.It Fl H -Used for scripting mode. Do not print headers and separate fields by a single -tab instead of arbitrary white space. -.It Fl p -Display numbers in parsable (exact) values. -.It Fl o Ar property Ns Oo , Ns Ar property Oc Ns ... -A comma-separated list of properties to display. The property must be: -.Bl -bullet -offset 2n -.It -One of the properties described in the -.Qq Sx Native Properties -section -.It -A user property -.It -The value -.Cm name -to display the dataset name -.It -The value -.Cm space -to display space usage properties on file systems and volumes. This is a -shortcut for specifying -.Fl o -.Sy name,avail,used,usedsnap,usedds,usedrefreserv,usedchild -.Fl t -.Sy filesystem,volume -syntax. -.El -.It Fl t Ar type Ns Oo , Ns Ar type Oc Ns ... -A comma-separated list of types to display, where -.Ar type -is one of -.Sy filesystem , snapshot , snap , volume , bookmark , No or Sy all . -For example, specifying -.Fl t Cm snapshot -displays only snapshots. -.It Fl s Ar property -A property for sorting the output by column in ascending order based on the -value of the property. The property must be one of the properties described in -the -.Qq Sx Properties -section, or the special value -.Cm name -to sort by the dataset name. Multiple properties can be specified at one time -using multiple -.Fl s -property options. Multiple -.Fl s -options are evaluated from left to right in decreasing order of importance. -.Pp -The following is a list of sorting criteria: -.Bl -bullet -offset 2n -.It -Numeric types sort in numeric order. -.It -String types sort in alphabetical order. -.It -Types inappropriate for a row sort that row to the literal bottom, regardless -of the specified ordering. -.It -If no sorting options are specified the existing behavior of -.Qq Nm Cm list -is preserved. -.El -.It Fl S Ar property -Same as the -.Fl s -option, but sorts by property in descending order. -.El -.It Xo -.Nm -.Cm set -.Ar property Ns = Ns Ar value Oo Ar property Ns = Ns Ar value Oc Ns ... -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot -.Xc -.Pp -Sets the property or list of properties to the given value(s) for each dataset. -Only some properties can be edited. See the "Properties" section for more -information on what properties can be set and acceptable values. Numeric values -can be specified as exact values, or in a human-readable form with a suffix of -.Sy B , K , M , G , T , P , E , Z -(for bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes, exabytes, or -zettabytes, respectively). User properties can be set on snapshots. For more -information, see the -.Qq Sx User Properties -section. -.It Xo -.Nm -.Cm get -.Op Fl r Ns | Ns Fl d Ar depth -.Op Fl Hp -.Op Fl o Ar all | field Ns Oo , Ns Ar field Oc Ns ... -.Op Fl t Ar type Ns Oo , Ns Ar type Oc Ns ... -.Op Fl s Ar source Ns Oo , Ns Ar source Oc Ns ... -.Ar all | property Ns Oo , Ns Ar property Oc Ns ... -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns | Ns Ar bookmark Ns ... -.Xc -.Pp -Displays properties for the given datasets. If no datasets are specified, then -the command displays properties for all datasets on the system. For each -property, the following columns are displayed: -.Pp -.Bl -hang -width "property" -offset indent -compact -.It name -Dataset name -.It property -Property name -.It value -Property value -.It source -Property source. Can either be local, default, temporary, inherited, received, -or none -(\&-). -.El -.Pp -All columns except the -.Sy RECEIVED -column are displayed by default. The columns to display can be specified -by using the -.Fl o -option. This command takes a comma-separated list of properties as described in -the -.Qq Sx Native Properties -and -.Qq Sx User Properties -sections. -.Pp -The special value -.Cm all -can be used to display all properties that apply to the given dataset's type -(filesystem, volume, snapshot, or bookmark). -.Bl -tag -width indent -.It Fl r -Recursively display properties for any children. -.It Fl d Ar depth -Recursively display any children of the dataset, limiting the recursion to -.Ar depth . -A depth of -.Sy 1 -will display only the dataset and its direct children. -.It Fl H -Display output in a form more easily parsed by scripts. Any headers are -omitted, and fields are explicitly separated by a single tab instead of an -arbitrary amount of space. -.It Fl p -Display numbers in parsable (exact) values. -.It Fl o Cm all | Ar field Ns Oo , Ns Ar field Oc Ns ... -A comma-separated list of columns to display. Supported values are -.Sy name,property,value,received,source . -Default values are -.Sy name,property,value,source . -The keyword -.Cm all -specifies all columns. -.It Fl t Ar type Ns Oo , Ns Ar type Oc Ns ... -A comma-separated list of types to display, where -.Ar type -is one of -.Sy filesystem , snapshot , volume , No or Sy all . -For example, specifying -.Fl t Cm snapshot -displays only snapshots. -.It Fl s Ar source Ns Oo , Ns Ar source Oc Ns ... -A comma-separated list of sources to display. Those properties coming from a -source other than those in this list are ignored. Each source must be one of -the following: -.Sy local,default,inherited,temporary,received,none . -The default value is all sources. -.El -.It Xo -.Nm -.Cm inherit -.Op Fl rS -.Ar property -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns ... -.Xc -.Pp -Clears the specified property, causing it to be inherited from an ancestor, -restored to default if no ancestor has the property set, or with the -.Fl S -option reverted to the received value if one exists. -See the -.Qq Sx Properties -section for a listing of default values, and details on which properties can be -inherited. -.Bl -tag -width indent -.It Fl r -Recursively inherit the given property for all children. -.It Fl S -Revert the property to the received value if one exists; otherwise operate as -if the -.Fl S -option was not specified. -.El -.It Xo -.Nm -.Cm remap -.Ar filesystem Ns | Ns Ar volume -.Xc -.Pp -Remap the indirect blocks in the given filesystem or volume so that they no -longer reference blocks on previously removed vdevs and we can eventually -shrink the size of the indirect mapping objects for the previously removed -vdevs. Note that remapping all blocks might not be possible and that -references from snapshots will still exist and cannot be remapped. -.It Xo -.Nm -.Cm upgrade -.Op Fl v -.Xc -.Pp -Displays a list of file systems that are not the most recent version. -.Bl -tag -width indent -.It Fl v -Displays -.Tn ZFS -filesystem versions supported by the current software. The current -.Tn ZFS -filesystem version and all previous supported versions are displayed, along -with an explanation of the features provided with each version. -.El -.It Xo -.Nm -.Cm upgrade -.Op Fl r -.Op Fl V Ar version -.Fl a | Ar filesystem -.Xc -.Pp -Upgrades file systems to a new on-disk version. Once this is done, the file -systems will no longer be accessible on systems running older versions of the -software. -.Qq Nm Cm send -streams generated from new snapshots of these file systems cannot be accessed -on systems running older versions of the software. -.Pp -In general, the file system version is independent of the pool version. See -.Xr zpool 8 -for information on the -.Nm zpool Cm upgrade -command. -.Pp -In some cases, the file system version and the pool version are interrelated -and the pool version must be upgraded before the file system version can be -upgraded. -.Bl -tag -width indent -.It Fl r -Upgrade the specified file system and all descendent file systems. -.It Fl V Ar version -Upgrade to the specified -.Ar version . -If the -.Fl V -flag is not specified, this command upgrades to the most recent version. This -option can only be used to increase the version number, and only up to the most -recent version supported by this software. -.It Fl a -Upgrade all file systems on all imported pools. -.It Ar filesystem -Upgrade the specified file system. -.El -.It Xo -.Nm -.Cm userspace -.Op Fl Hinp -.Op Fl o Ar field Ns Oo , Ns Ar field Oc Ns ... -.Oo Fl s Ar field Oc Ns ... -.Oo Fl S Ar field Oc Ns ... -.Op Fl t Ar type Ns Oo , Ns Ar type Oc Ns ... -.Ar filesystem Ns | Ns Ar snapshot -.Xc -.Pp -Displays space consumed by, and quotas on, each user in the specified -filesystem or snapshot. This corresponds to the -.Sy userused@ Ns Ar user -and -.Sy userquota@ Ns Ar user -properties. -.Bl -tag -width indent -.It Fl n -Print numeric ID instead of user/group name. -.It Fl H -Do not print headers, use tab-delimited output. -.It Fl p -Use exact (parsable) numeric output. -.It Fl o Ar field Ns Oo , Ns Ar field Oc Ns ... -Display only the specified fields from the following set: -.Sy type,name,used,quota . -The default is to display all fields. -.It Fl s Ar field -Sort output by this field. The -.Fl s -and -.Fl S -flags may be specified multiple times to sort first by one field, then by -another. The default is -.Fl s Cm type Fl s Cm name . -.It Fl S Ar field -Sort by this field in reverse order. See -.Fl s . -.It Fl t Ar type Ns Oo , Ns Ar type Oc Ns ... -Print only the specified types from the following set: -.Sy all,posixuser,smbuser,posixgroup,smbgroup . -.Pp -The default is -.Fl t Cm posixuser,smbuser . -.Pp -The default can be changed to include group types. -.It Fl i -Translate SID to POSIX ID. This flag currently has no effect on -.Fx . -.El -.It Xo -.Nm -.Cm groupspace -.Op Fl Hinp -.Op Fl o Ar field Ns Oo , Ns Ar field Oc Ns ... -.Oo Fl s Ar field Oc Ns ... -.Oo Fl S Ar field Oc Ns ... -.Op Fl t Ar type Ns Oo , Ns Ar type Oc Ns ... -.Ar filesystem Ns | Ns Ar snapshot -.Xc -.Pp -Displays space consumed by, and quotas on, each group in the specified -filesystem or snapshot. This subcommand is identical to -.Qq Nm Cm userspace , -except that the default types to display are -.Fl t Sy posixgroup,smbgroup . -.It Xo -.Nm -.Cm mount -.Xc -.Pp -Displays all -.Tn ZFS -file systems currently mounted. -.Bl -tag -width indent -.It Fl f -.El -.It Xo -.Nm -.Cm mount -.Op Fl vO -.Op Fl o Ar property Ns Oo , Ns Ar property Oc Ns ... -.Fl a | Ar filesystem -.Xc -.Pp -Mounts -.Tn ZFS -file systems. -.Bl -tag -width indent -.It Fl v -Report mount progress. -.It Fl O -Perform an overlay mount. Overlay mounts are not supported on -.Fx . -.It Fl o Ar property Ns Oo , Ns Ar property Oc Ns ... -An optional, comma-separated list of mount options to use temporarily for the -duration of the mount. See the -.Qq Sx Temporary Mount Point Properties -section for details. -.It Fl a -Mount all available -.Tn ZFS -file systems. -This command may be executed on -.Fx -system startup by -.Pa /etc/rc.d/zfs . -For more information, see variable -.Va zfs_enable -in -.Xr rc.conf 5 . -.It Ar filesystem -Mount the specified filesystem. -.El -.It Xo -.Nm -.Cm unmount Ns | Ns Cm umount -.Op Fl f -.Fl a | Ar filesystem Ns | Ns Ar mountpoint -.Xc -.Pp -Unmounts currently mounted -.Tn ZFS -file systems. -.Bl -tag -width indent -.It Fl f -Forcefully unmount the file system, even if it is currently in use. -.It Fl a -Unmount all available -.Tn ZFS -file systems. -.It Ar filesystem | mountpoint -Unmount the specified filesystem. The command can also be given a path to a -.Tn ZFS -file system mount point on the system. -.El -.It Xo -.Nm -.Cm share -.Fl a | Ar filesystem -.Xc -.Pp -Shares -.Tn ZFS -file systems that have the -.Sy sharenfs -property set. -.Bl -tag -width indent -.It Fl a -Share all -.Tn ZFS -file systems that have the -.Sy sharenfs -property set. -This command may be executed on -.Fx -system startup by -.Pa /etc/rc.d/zfs . -For more information, see variable -.Va zfs_enable -in -.Xr rc.conf 5 . -.It Ar filesystem -Share the specified filesystem according to the -.Tn sharenfs -property. File systems are shared when the -.Tn sharenfs -property is set. -.El -.It Xo -.Nm -.Cm unshare -.Fl a | Ar filesystem Ns | Ns Ar mountpoint -.Xc -.Pp -Unshares -.Tn ZFS -file systems that have the -.Tn sharenfs -property set. -.Bl -tag -width indent -.It Fl a -Unshares -.Tn ZFS -file systems that have the -.Sy sharenfs -property set. -This command may be executed on -.Fx -system shutdown by -.Pa /etc/rc.d/zfs . -For more information, see variable -.Va zfs_enable -in -.Xr rc.conf 5 . -.It Ar filesystem | mountpoint -Unshare the specified filesystem. The command can also be given a path to a -.Tn ZFS -file system shared on the system. -.El -.It Xo -.Nm -.Cm bookmark -.Ar snapshot -.Ar bookmark -.Xc -.Pp -Creates a bookmark of the given snapshot. -Bookmarks mark the point in time -when the snapshot was created, and can be used as the incremental source for -a -.Qq Nm Cm send -command. -.Pp -This feature must be enabled to be used. -See -.Xr zpool-features 7 -for details on ZFS feature flags and the -.Sy bookmark -feature. -.It Xo -.Nm -.Cm send -.Op Fl DLPRVcenpv -.Op Fl i Ar snapshot | Fl I Ar snapshot -.Ar snapshot -.Xc -.Pp -Creates a stream representation of the last -.Ar snapshot -argument (not part of -.Fl i -or -.Fl I ) -which is written to standard output. The output can be redirected to -a file or to a different system (for example, using -.Xr ssh 1 ) . -By default, a full stream is generated. -.Bl -tag -width indent -.It Fl i Ar snapshot -Generate an incremental stream from the first -.Ar snapshot Pq the incremental source -to the second -.Ar snapshot Pq the incremental target . -The incremental source can be specified as the last component of the -snapshot name -.Pq the Em @ No character and following -and -it is assumed to be from the same file system as the incremental target. -.Pp -If the destination is a clone, the source may be the origin snapshot, which -must be fully specified (for example, -.Cm pool/fs@origin , -not just -.Cm @origin ) . -.It Fl I Ar snapshot -Generate a stream package that sends all intermediary snapshots from the first -.Ar snapshot -to the second -.Ar snapshot . -For example, -.Ic -I @a fs@d -is similar to -.Ic -i @a fs@b; -i @b fs@c; -i @c fs@d . -The incremental -source may be specified as with the -.Fl i -option. -.It Fl R, -replicate -Generate a replication stream package, which will replicate the specified -filesystem, and all descendent file systems, up to the named snapshot. When -received, all properties, snapshots, descendent file systems, and clones are -preserved. -.Pp -If the -.Fl i -or -.Fl I -flags are used in conjunction with the -.Fl R -flag, an incremental replication stream is generated. The current values of -properties, and current snapshot and file system names are set when the stream -is received. If the -.Fl F -flag is specified when this stream is received, snapshots and file systems that -do not exist on the sending side are destroyed. -.It Fl D, -dedup -Generate a deduplicated stream. Blocks which would have been sent multiple -times in the send stream will only be sent once. The receiving system must -also support this feature to receive a deduplicated stream. This flag can -be used regardless of the dataset's -.Sy dedup -property, but performance will be much better if the filesystem uses a -dedup-capable checksum (eg. -.Sy sha256 ) . -.It Fl L, -large-block -Generate a stream which may contain blocks larger than 128KB. -This flag -has no effect if the -.Sy large_blocks -pool feature is disabled, or if the -.Sy recordsize -property of this filesystem has never been set above 128KB. -The receiving system must have the -.Sy large_blocks -pool feature enabled as well. -See -.Xr zpool-features 7 -for details on ZFS feature flags and the -.Sy large_blocks -feature. -.It Fl e, -embed -Generate a more compact stream by using WRITE_EMBEDDED records for blocks -which are stored more compactly on disk by the -.Sy embedded_data -pool -feature. -This flag has no effect if the -.Sy embedded_data -feature is -disabled. -The receiving system must have the -.Sy embedded_data -feature -enabled. -If the -.Sy lz4_compress -feature is active on the sending system, -then the receiving system must have that feature enabled as well. -See -.Xr zpool-features 7 -for details on ZFS feature flags and the -.Sy embedded_data -feature. -.It Fl c, -compressed -Generate a more compact stream by using compressed WRITE records for blocks -which are compressed on disk and in memory (see the -.Sy compression -property for details). -If the -.Sy lz4_compress -feature is active on the sending system, then the receiving system must have that -feature enabled as well. If the -.Sy large_blocks -feature is enabled on the sending system but the -.Fl L -option is not supplied in conjunction with -.Fl c -then the data will be decompressed before sending so it can be split -into smaller block sizes. -.It Fl p, -props -Include the dataset's properties in the stream. This flag is implicit when -.Fl R -is specified. The receiving system must also support this feature. -.It Fl n, -dryrun -Do a dry-run ("No-op") send. Do not generate any actual send data. This is -useful in conjunction with the -.Fl v -or -.Fl P -flags to determine what data will be sent. -In this case, the verbose output will be written to -standard output (contrast with a non-dry-run, where the stream is written -to standard output and the verbose output goes to standard error). -.It Fl P, -parsable -Print machine-parsable verbose information about the stream package generated. -.It Fl v, -verbose -Print verbose information about the stream package generated. -This information includes a per-second report of how much data has been sent. -.It Fl V -Set the process title to a per-second report of how much data has been sent. -.El -.Pp -The format of the stream is committed. You will be able to receive your streams -on future versions of -.Tn ZFS . -.It Xo -.Nm -.Cm send -.Op Fl LPcenv -.Op Fl i Ar snapshot Ns | Ns Ar bookmark -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot -.Xc -.Pp -Generate a send stream, which may be of a filesystem, and may be -incremental from a bookmark. -If the destination is a filesystem or volume, -the pool must be read-only, or the filesystem must not be mounted. -When the -stream generated from a filesystem or volume is received, the default snapshot -name will be -.Pq --head-- . -.Bl -tag -width indent -.It Fl i Ar snapshot Ns | Ns Ar bookmark -Generate an incremental send stream. -The incremental source must be an earlier -snapshot in the destination's history. -It will commonly be an earlier -snapshot in the destination's filesystem, in which case it can be -specified as the last component of the name -.Pq the Em # No or Em @ No character and following . -.Pp -If the incremental target is a clone, the incremental source can -be the origin snapshot, or an earlier snapshot in the origin's filesystem, -or the origin's origin, etc. -.It Fl n, -dryrun -Do a dry-run -.Pq Qq No-op -send. -Do not generate any actual send data. -This is useful in conjunction with the -.Fl v -or -.Fl P -flags to determine what data will be sent. -In this case, the verbose output will be written to standard output -.Po contrast with a non-dry-run, where the stream is written to standard output -and the verbose output goes to standard error -.Pc . -.It Fl v, -verbose -Print verbose information about the stream package generated. -This information includes a per-second report of how much data has been sent. -.It Fl L, -large-block -Generate a stream which may contain blocks larger than 128KB. -This flag -has no effect if the -.Sy large_blocks -pool feature is disabled, or if the -.Sy recordsize -property of this filesystem has never been set above 128KB. -The receiving system must have the -.Sy large_blocks -pool feature enabled as well. -See -.Xr zpool-features 7 -for details on ZFS feature flags and the -.Sy large_blocks -feature. -.It Fl P, -parsable -Print machine-parsable verbose information about the stream package generated. -.It Fl c, -compressed -Generate a more compact stream by using compressed WRITE records for blocks -which are compressed on disk and in memory (see the -.Sy compression -property for details). If the -.Sy lz4_compress -feature is active on the sending system, then the receiving system must have -that feature enabled as well. If the -.Sy large_blocks -feature is enabled on the sending system but the -.Fl L -option is not supplied in conjunction with -.Fl c -then the data will be decompressed before sending so it can be split -into smaller block sizes. -.It Fl e, -embed -Generate a more compact stream by using WRITE_EMBEDDED records for blocks -which are stored more compactly on disk by the -.Sy embedded_data -pool -feature. -This flag has no effect if the -.Sy embedded_data -feature is -disabled. -The receiving system must have the -.Sy embedded_data -feature -enabled. -If the -.Sy lz4_compress -feature is active on the sending system, -then the receiving system must have that feature enabled as well. -See -.Xr zpool-features 7 -for details on ZFS feature flags and the -.Sy embedded_data -feature. -.El -.It Xo -.Nm -.Cm send -.Op Fl Penv -.Fl t -.Ar receive_resume_token -.Xc -Creates a send stream which resumes an interrupted receive. The -.Ar receive_resume_token -is the value of this property on the filesystem -or volume that was being received into. See the documentation for -.Sy zfs receive -s -for more details. -.It Xo -.Nm -.Cm receive Ns | Ns Cm recv -.Op Fl vnsFMu -.Op Fl o Sy origin Ns = Ns Ar snapshot -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot -.Xc -.It Xo -.Nm -.Cm receive Ns | Ns Cm recv -.Op Fl vnsFMu -.Op Fl d | e -.Op Fl o Sy origin Ns = Ns Ar snapshot -.Ar filesystem -.Xc -.Pp -Creates a snapshot whose contents are as specified in the stream provided on -standard input. If a full stream is received, then a new file system is created -as well. Streams are created using the -.Qq Nm Cm send -subcommand, which by default creates a full stream. -.Qq Nm Cm recv -can be used as an alias for -.Qq Nm Cm receive . -.Pp -If an incremental stream is received, then the destination file system must -already exist, and its most recent snapshot must match the incremental stream's -source. For -.Sy zvol Ns s, -the destination device link is destroyed and recreated, which means the -.Sy zvol -cannot be accessed during the -.Sy receive -operation. -.Pp -When a snapshot replication package stream that is generated by using the -.Qq Nm Cm send Fl R -command is received, any snapshots that do not exist on the sending location -are destroyed by using the -.Qq Nm Cm destroy Fl d -command. -.Pp -The name of the snapshot (and file system, if a full stream is received) that -this subcommand creates depends on the argument type and the -.Fl d -or -.Fl e -option. -.Pp -If the argument is a snapshot name, the specified -.Ar snapshot -is created. If the argument is a file system or volume name, a snapshot with -the same name as the sent snapshot is created within the specified -.Ar filesystem -or -.Ar volume . -If the -.Fl d -or -.Fl e -option is specified, the snapshot name is determined by appending the sent -snapshot's name to the specified -.Ar filesystem . -If the -.Fl d -option is specified, all but the pool name of the sent snapshot path is -appended (for example, -.Sy b/c@1 -appended from sent snapshot -.Sy a/b/c@1 ) , -and if the -.Fl e -option is specified, only the tail of the sent snapshot path is appended (for -example, -.Sy c@1 -appended from sent snapshot -.Sy a/b/c@1 ) . -In the case of -.Fl d , -any file systems needed to replicate the path of the sent snapshot are created -within the specified file system. -.Bl -tag -width indent -.It Fl d -Use the full sent snapshot path without the first element (without pool name) -to determine the name of the new snapshot as described in the paragraph above. -.It Fl e -Use only the last element of the sent snapshot path to determine the name of -the new snapshot as described in the paragraph above. -.It Fl u -File system that is associated with the received stream is not mounted. -.It Fl v -Print verbose information about the stream and the time required to perform the -receive operation. -.It Fl n -Do not actually receive the stream. This can be useful in conjunction with the -.Fl v -option to verify the name the receive operation would use. -.It Fl o Sy origin Ns = Ns Ar snapshot -Forces the stream to be received as a clone of the given snapshot. -If the stream is a full send stream, this will create the filesystem -described by the stream as a clone of the specified snapshot. Which -snapshot was specified will not affect the success or failure of the -receive, as long as the snapshot does exist. If the stream is an -incremental send stream, all the normal verification will be performed. -.It Fl F -Force a rollback of the file system to the most recent snapshot before -performing the receive operation. If receiving an incremental replication -stream (for example, one generated by -.Qq Nm Cm send Fl R Bro Fl i | Fl I Brc ) , -destroy snapshots and file systems that do not exist on the sending side. -.It Fl M -Force an unmount of the file system while receiving a snapshot. -This option is not supported on Linux. -.It Fl s -If the receive is interrupted, save the partially received state, rather -than deleting it. Interruption may be due to premature termination of -the stream -.Po e.g. due to network failure or failure of the remote system -if the stream is being read over a network connection -.Pc , -a checksum error in the stream, termination of the -.Nm zfs Cm receive -process, or unclean shutdown of the system. -.Pp -The receive can be resumed with a stream generated by -.Nm zfs Cm send Fl t Ar token , -where the -.Ar token -is the value of the -.Sy receive_resume_token -property of the filesystem or volume which is received into. -.Pp -To use this flag, the storage pool must have the -.Sy extensible_dataset -feature enabled. See -.Xr zpool-features 7 -for details on ZFS feature flags. -.El -.It Xo -.Nm -.Cm receive Ns | Ns Cm recv -.Fl A -.Ar filesystem Ns | Ns Ar volume -.Xc -Abort an interrupted -.Nm zfs Cm receive Fl s , -deleting its saved partially received state. -.It Xo -.Nm -.Cm allow -.Ar filesystem Ns | Ns Ar volume -.Xc -.Pp -Displays permissions that have been delegated on the specified filesystem or -volume. See the other forms of -.Qq Nm Cm allow -for more information. -.It Xo -.Nm -.Cm allow -.Op Fl ldug -.Ar user Ns | Ns Ar group Ns Oo Ns , Ns Ar user Ns | Ns Ar group Oc Ns ... -.Ar perm Ns | Ns Ar @setname Ns -.Oo Ns , Ns Ar perm Ns | Ns Ar @setname Oc Ns ... -.Ar filesystem Ns | Ns Ar volume -.Xc -.It Xo -.Nm -.Cm allow -.Op Fl ld -.Fl e Ns | Ns Cm everyone -.Ar perm Ns | Ns Ar @setname Ns Op Ns , Ns Ar perm Ns | Ns Ar @setname Ns -.Ns ... -.Ar filesystem Ns | Ns Ar volume -.Xc -.Pp -Delegates -.Tn ZFS -administration permission for the file systems to non-privileged users. -.Bl -tag -width indent -.It Xo -.Op Fl ug -.Ar user Ns | Ns Ar group Ns Oo , Ar user Ns | Ns Ar group Oc Ns ... -.Xc -Specifies to whom the permissions are delegated. Multiple entities can be -specified as a comma-separated list. If neither of the -.Fl ug -options are specified, then the argument is interpreted preferentially as the -keyword -.Cm everyone , -then as a user name, and lastly as a group name. To specify -a user or group named -.Qq everyone , -use the -.Fl u -or -.Fl g -options. To specify a group with the same name as a user, use the -.Fl g -option. -.It Op Fl e Ns | Ns Cm everyone -Specifies that the permissions be delegated to -.Qq everyone . -.It Xo -.Ar perm Ns | Ns Ar @setname Ns Oo , Ns Ar perm Ns | Ns Ar @setname Oc Ns ... -.Xc -The permissions to delegate. Multiple permissions -may be specified as a comma-separated list. Permission names are the same as -.Tn ZFS -subcommand and property names. See the property list below. Property set names, -which begin with an at sign -.Pq Sy @ , -may be specified. See the -.Fl s -form below for details. -.It Xo -.Op Fl ld -.Ar filesystem Ns | Ns Ar volume -.Xc -Specifies where the permissions are delegated. If neither of the -.Fl ld -options are specified, or both are, then the permissions are allowed for the -file system or volume, and all of its descendents. If only the -.Fl l -option is used, then is allowed "locally" only for the specified file system. -If only the -.Fl d -option is used, then is allowed only for the descendent file systems. -.El -.Pp -Permissions are generally the ability to use a -.Tn ZFS -subcommand or change a -.Tn ZFS -property. The following permissions are available: -.Bl -column -offset 4n "secondarycache" "subcommand" -.It NAME Ta TYPE Ta NOTES -.It allow Ta subcommand Ta Must Xo -also have the permission that is being allowed -.Xc -.It clone Ta subcommand Ta Must Xo -also have the 'create' ability and 'mount' ability in the origin file system -.Xc -.It create Ta subcommand Ta Must also have the 'mount' ability -.It destroy Ta subcommand Ta Must also have the 'mount' ability -.It diff Ta subcommand Ta Allows lookup of paths within a dataset given an -object number, and the ability to create snapshots necessary to 'zfs diff' -.It hold Ta subcommand Ta Allows adding a user hold to a snapshot -.It mount Ta subcommand Ta Allows mount/umount of Tn ZFS No datasets -.It promote Ta subcommand Ta Must Xo -also have the 'mount' and 'promote' ability in the origin file system -.Xc -.It receive Ta subcommand Ta Must also have the 'mount' and 'create' ability -.It release Ta subcommand Ta Allows Xo -releasing a user hold which might destroy the snapshot -.Xc -.It rename Ta subcommand Ta Must Xo -also have the 'mount' and 'create' ability in the new parent -.Xc -.It rollback Ta subcommand Ta Must also have the 'mount' ability -.It send Ta subcommand -.It share Ta subcommand Ta Allows Xo -sharing file systems over the -.Tn NFS -protocol -.Xc -.It snapshot Ta subcommand Ta Must also have the 'mount' ability -.It groupquota Ta other Ta Allows accessing any groupquota@... property -.It groupused Ta other Ta Allows reading any groupused@... property -.It userprop Ta other Ta Allows changing any user property -.It userquota Ta other Ta Allows accessing any userquota@... property -.It userused Ta other Ta Allows reading any userused@... property -.It aclinherit Ta property -.It aclmode Ta property -.It atime Ta property -.It canmount Ta property -.It casesensitivity Ta property -.It checksum Ta property -.It compression Ta property -.It copies Ta property -.It dedup Ta property -.It devices Ta property -.It exec Ta property -.It filesystem_limit Ta property -.It logbias Ta property -.It jailed Ta property -.It mlslabel Ta property -.It mountpoint Ta property -.It nbmand Ta property -.It normalization Ta property -.It primarycache Ta property -.It quota Ta property -.It readonly Ta property -.It recordsize Ta property -.It refquota Ta property -.It refreservation Ta property -.It reservation Ta property -.It secondarycache Ta property -.It setuid Ta property -.It sharenfs Ta property -.It sharesmb Ta property -.It snapdir Ta property -.It snapshot_limit Ta property -.It sync Ta property -.It utf8only Ta property -.It version Ta property -.It volblocksize Ta property -.It volsize Ta property -.It vscan Ta property -.It xattr Ta property -.El -.It Xo -.Nm -.Cm allow -.Fl c -.Ar perm Ns | Ns Ar @setname Ns Op Ns , Ns Ar perm Ns | Ns Ar @setname Ns -.Ns ... -.Ar filesystem Ns | Ns Ar volume -.Xc -.Pp -Sets "create time" permissions. These permissions are granted (locally) to the -creator of any newly-created descendent file system. -.It Xo -.Nm -.Cm allow -.Fl s -.Ar @setname -.Ar perm Ns | Ns Ar @setname Ns Op Ns , Ns Ar perm Ns | Ns Ar @setname Ns -.Ns ... -.Ar filesystem Ns | Ns Ar volume -.Xc -.Pp -Defines or adds permissions to a permission set. The set can be used by other -.Qq Nm Cm allow -commands for the specified file system and its descendents. Sets are evaluated -dynamically, so changes to a set are immediately reflected. Permission sets -follow the same naming restrictions as ZFS file systems, but the name must -begin with an "at sign" -.Pq Sy @ , -and can be no more than 64 characters long. -.It Xo -.Nm -.Cm unallow -.Op Fl rldug -.Ar user Ns | Ns Ar group Ns Oo Ns , Ns Ar user Ns | Ns Ar group Oc Ns ... -.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns -.Ns ... Oc -.Ar filesystem Ns | Ns Ar volume -.Xc -.It Xo -.Nm -.Cm unallow -.Op Fl rld -.Fl e Ns | Ns Cm everyone -.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns -.Ns ... Oc -.Ar filesystem Ns | Ns Ar volume -.Xc -.It Xo -.Nm -.Cm unallow -.Op Fl r -.Fl c -.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns -.Ns ... Oc -.Ar filesystem Ns | Ns Ar volume -.Xc -.Pp -Removes permissions that were granted with the -.Qq Nm Cm allow -command. No permissions are explicitly denied, so other permissions granted are -still in effect. For example, if the permission is granted by an ancestor. If -no permissions are specified, then all permissions for the specified -.Ar user , group , No or everyone -are removed. Specifying -.Cm everyone -.Po or using the Fl e -option -.Pc only removes the permissions that were granted to everyone , -not all permissions for every user and group. See the -.Qq Nm Cm allow -command for a description of the -.Fl ldugec -options. -.Bl -tag -width indent -.It Fl r -Recursively remove the permissions from this file system and all descendents. -.El -.It Xo -.Nm -.Cm unallow -.Op Fl r -.Fl s -.Ar @setname -.Oo Ar perm Ns | Ns Ar @setname Ns Op , Ns Ar perm Ns | Ns Ar @setname Ns -.Ns ... Oc -.Ar filesystem Ns | Ns Ar volume -.Xc -.Pp -Removes permissions from a permission set. If no permissions are specified, -then all permissions are removed, thus removing the set entirely. -.It Xo -.Nm -.Cm hold -.Op Fl r -.Ar tag snapshot Ns ... -.Xc -.Pp -Adds a single reference, named with the -.Ar tag -argument, to the specified snapshot or snapshots. Each snapshot has its own tag -namespace, and tags must be unique within that space. -.Pp -If a hold exists on a snapshot, attempts to destroy that snapshot by using the -.Qq Nm Cm destroy -command returns -.Em EBUSY . -.Bl -tag -width indent -.It Fl r -Specifies that a hold with the given tag is applied recursively to the -snapshots of all descendent file systems. -.El -.It Xo -.Nm -.Cm holds -.Op Fl Hp -.Op Fl r Ns | Ns Fl d Ar depth -.Ar filesystem Ns | Ns Ar volume Ns | Ns Ar snapshot Ns -.Ns ... -.Xc -.Pp -Lists all existing user references for the given dataset or datasets. -.Bl -tag -width indent -.It Fl H -Used for scripting mode. Do not print headers and separate fields by a single -tab instead of arbitrary white space. -.It Fl p -Display numbers in parsable (exact) values. -.It Fl r -Lists the holds that are set on the descendent snapshots of the named datasets -or snapshots, in addition to listing the holds on the named snapshots, if any. -.It Fl d Ar depth -Recursively display any holds on the named snapshots, or descendent snapshots of -the named datasets or snapshots, limiting the recursion to -.Ar depth . -.El -.It Xo -.Nm -.Cm release -.Op Fl r -.Ar tag snapshot Ns ... -.Xc -.Pp -Removes a single reference, named with the -.Ar tag -argument, from the specified snapshot or snapshots. The tag must already exist -for each snapshot. -.Bl -tag -width indent -.It Fl r -Recursively releases a hold with the given tag on the snapshots of all -descendent file systems. -.El -.It Xo -.Nm -.Cm diff -.Op Fl FHt -.Ar snapshot -.Op Ar snapshot Ns | Ns Ar filesystem -.Xc -.Pp -Display the difference between a snapshot of a given filesystem and another -snapshot of that filesystem from a later time or the current contents of the -filesystem. The first column is a character indicating the type of change, -the other columns indicate pathname, new pathname -.Pq in case of rename , -change in link count, and optionally file type and/or change time. -.Pp -The types of change are: -.Bl -column -offset 2n indent -.It \&- Ta path was removed -.It \&+ Ta path was added -.It \&M Ta path was modified -.It \&R Ta path was renamed -.El -.Bl -tag -width indent -.It Fl F -Display an indication of the type of file, in a manner similar to the -.Fl F -option of -.Xr ls 1 . -.Bl -column -offset 2n indent -.It \&B Ta block device -.It \&C Ta character device -.It \&F Ta regular file -.It \&/ Ta directory -.It \&@ Ta symbolic link -.It \&= Ta socket -.It \&> Ta door (not supported on Fx ) -.It \&| Ta named pipe (not supported on Fx ) -.It \&P Ta event port (not supported on Fx ) -.El -.It Fl H -Give more parsable tab-separated output, without header lines and without -arrows. -.It Fl t -Display the path's inode change time as the first column of output. -.El -.It Xo -.Nm -.Cm program -.Op Fl jn -.Op Fl t Ar timeout -.Op Fl m Ar memory_limit -.Ar pool script -.Op Ar arg1 No ... -.Xc -.Pp -Executes -.Ar script -as a ZFS channel program on -.Ar pool . -The ZFS channel -program interface allows ZFS administrative operations to be run -programmatically via a Lua script. -The entire script is executed atomically, with no other administrative -operations taking effect concurrently. -A library of ZFS calls is made available to channel program scripts. -Channel programs may only be run with root privileges. -.Pp -For full documentation of the ZFS channel program interface, see the manual -page for -.Xr zfs-program 8 . -.Bl -tag -width indent -.It Fl j -Display channel program output in JSON format. -When this flag is specified and standard output is empty - -channel program encountered an error. -The details of such an error will be printed to standard error in plain text. -.It Fl n -Executes a read-only channel program, which runs faster. -The program cannot change on-disk state by calling functions from -the zfs.sync submodule. -The program can be used to gather information such as properties and -determining if changes would succeed (zfs.check.*). -Without this flag, all pending changes must be synced to disk before -a channel program can complete. -.It Fl t Ar timeout -Execution time limit, in milliseconds. -If a channel program executes for longer than the provided timeout, it will -be stopped and an error will be returned. -The default timeout is 1000 ms, and can be set to a maximum of 10000 ms. -.It Fl m Ar memory-limit -Memory limit, in bytes. -If a channel program attempts to allocate more memory than the given limit, -it will be stopped and an error returned. -The default memory limit is 10 MB, and can be set to a maximum of 100 MB. -.Pp -All remaining argument strings are passed directly to the channel program as -arguments. -See -.Xr zfs-program 8 -for more information. -.El -.It Xo -.Nm -.Cm jail -.Ar jailid filesystem -.Xc -.Pp -Attaches the specified -.Ar filesystem -to the jail identified by JID -.Ar jailid . -From now on this file system tree can be managed from within a jail if the -.Sy jailed -property has been set. To use this functionality, the jail needs the -.Va allow.mount -and -.Va allow.mount.zfs -parameters set to 1 and the -.Va enforce_statfs -parameter set to a value lower than 2. -.Pp -See -.Xr jail 8 -for more information on managing jails and configuring the parameters above. -.It Xo -.Nm -.Cm unjail -.Ar jailid filesystem -.Xc -.Pp -Detaches the specified -.Ar filesystem -from the jail identified by JID -.Ar jailid . -.El -.Sh EXIT STATUS -The following exit values are returned: -.Bl -tag -offset 2n -width 2n -.It 0 -Successful completion. -.It 1 -An error occurred. -.It 2 -Invalid command line options were specified. -.El -.Sh EXAMPLES -.Bl -tag -width 0n -.It Sy Example 1 No Creating a Tn ZFS No File System Hierarchy -.Pp -The following commands create a file system named -.Em pool/home -and a file system named -.Em pool/home/bob . -The mount point -.Pa /home -is set for the parent file system, and is automatically inherited by the child -file system. -.Bd -literal -offset 2n -.Li # Ic zfs create pool/home -.Li # Ic zfs set mountpoint=/home pool/home -.Li # Ic zfs create pool/home/bob -.Ed -.It Sy Example 2 No Creating a Tn ZFS No Snapshot -.Pp -The following command creates a snapshot named -.Sy yesterday . -This snapshot is mounted on demand in the -.Pa \&.zfs/snapshot -directory at the root of the -.Em pool/home/bob -file system. -.Bd -literal -offset 2n -.Li # Ic zfs snapshot pool/home/bob@yesterday -.Ed -.It Sy Example 3 No Creating and Destroying Multiple Snapshots -.Pp -The following command creates snapshots named -.Em yesterday -of -.Em pool/home -and all of its descendent file systems. Each snapshot is mounted on demand in -the -.Pa \&.zfs/snapshot -directory at the root of its file system. The second command destroys the newly -created snapshots. -.Bd -literal -offset 2n -.Li # Ic zfs snapshot -r pool/home@yesterday -.Li # Ic zfs destroy -r pool/home@yesterday -.Ed -.It Sy Example 4 No Disabling and Enabling File System Compression -.Pp -The following command disables the -.Sy compression -property for all file systems under -.Em pool/home . -The next command explicitly enables -.Sy compression -for -.Em pool/home/anne . -.Bd -literal -offset 2n -.Li # Ic zfs set compression=off pool/home -.Li # Ic zfs set compression=on pool/home/anne -.Ed -.It Sy Example 5 No Listing Tn ZFS No Datasets -.Pp -The following command lists all active file systems and volumes in the system. -Snapshots are displayed if the -.Sy listsnaps -property is -.Cm on . -The default is -.Cm off . -See -.Xr zpool 8 -for more information on pool properties. -.Bd -literal -offset 2n -.Li # Ic zfs list - NAME USED AVAIL REFER MOUNTPOINT - pool 450K 457G 18K /pool - pool/home 315K 457G 21K /home - pool/home/anne 18K 457G 18K /home/anne - pool/home/bob 276K 457G 276K /home/bob -.Ed -.It Sy Example 6 No Setting a Quota on a Tn ZFS No File System -.Pp -The following command sets a quota of 50 Gbytes for -.Em pool/home/bob . -.Bd -literal -offset 2n -.Li # Ic zfs set quota=50G pool/home/bob -.Ed -.It Sy Example 7 No Listing Tn ZFS No Properties -.Pp -The following command lists all properties for -.Em pool/home/bob . -.Bd -literal -offset 2n -.Li # Ic zfs get all pool/home/bob -NAME PROPERTY VALUE SOURCE -pool/home/bob type filesystem - -pool/home/bob creation Tue Jul 21 15:53 2009 - -pool/home/bob used 21K - -pool/home/bob available 20.0G - -pool/home/bob referenced 21K - -pool/home/bob compressratio 1.00x - -pool/home/bob mounted yes - -pool/home/bob quota 20G local -pool/home/bob reservation none default -pool/home/bob recordsize 128K default -pool/home/bob mountpoint /home/bob default -pool/home/bob sharenfs off default -pool/home/bob checksum on default -pool/home/bob compression on local -pool/home/bob atime on default -pool/home/bob devices on default -pool/home/bob exec on default -pool/home/bob filesystem_limit none default -pool/home/bob setuid on default -pool/home/bob readonly off default -pool/home/bob jailed off default -pool/home/bob snapdir hidden default -pool/home/bob snapshot_limit none default -pool/home/bob aclmode discard default -pool/home/bob aclinherit restricted default -pool/home/bob canmount on default -pool/home/bob xattr on default -pool/home/bob copies 1 default -pool/home/bob version 5 - -pool/home/bob utf8only off - -pool/home/bob normalization none - -pool/home/bob casesensitivity sensitive - -pool/home/bob vscan off default -pool/home/bob nbmand off default -pool/home/bob sharesmb off default -pool/home/bob refquota none default -pool/home/bob refreservation none default -pool/home/bob primarycache all default -pool/home/bob secondarycache all default -pool/home/bob usedbysnapshots 0 - -pool/home/bob usedbydataset 21K - -pool/home/bob usedbychildren 0 - -pool/home/bob usedbyrefreservation 0 - -pool/home/bob logbias latency default -pool/home/bob dedup off default -pool/home/bob mlslabel - -pool/home/bob sync standard default -pool/home/bob refcompressratio 1.00x - -.Ed -.Pp -The following command gets a single property value. -.Bd -literal -offset 2n -.Li # Ic zfs get -H -o value compression pool/home/bob -on -.Ed -.Pp -The following command lists all properties with local settings for -.Em pool/home/bob . -.Bd -literal -offset 2n -.Li # Ic zfs get -s local -o name,property,value all pool/home/bob -NAME PROPERTY VALUE -pool/home/bob quota 20G -pool/home/bob compression on -.Ed -.It Sy Example 8 No Rolling Back a Tn ZFS No File System -.Pp -The following command reverts the contents of -.Em pool/home/anne -to the snapshot named -.Em yesterday , -deleting all intermediate snapshots. -.Bd -literal -offset 2n -.Li # Ic zfs rollback -r pool/home/anne@yesterday -.Ed -.It Sy Example 9 No Creating a Tn ZFS No Clone -.Pp -The following command creates a writable file system whose initial contents are -the same as -.Em pool/home/bob@yesterday . -.Bd -literal -offset 2n -.Li # Ic zfs clone pool/home/bob@yesterday pool/clone -.Ed -.It Sy Example 10 No Promoting a Tn ZFS No Clone -.Pp -The following commands illustrate how to test out changes to a file system, and -then replace the original file system with the changed one, using clones, clone -promotion, and renaming: -.Bd -literal -offset 2n -.Li # Ic zfs create pool/project/production -.Ed -.Pp -Populate -.Pa /pool/project/production -with data and continue with the following commands: -.Bd -literal -offset 2n -.Li # Ic zfs snapshot pool/project/production@today -.Li # Ic zfs clone pool/project/production@today pool/project/beta -.Ed -.Pp -Now make changes to -.Pa /pool/project/beta -and continue with the following commands: -.Bd -literal -offset 2n -.Li # Ic zfs promote pool/project/beta -.Li # Ic zfs rename pool/project/production pool/project/legacy -.Li # Ic zfs rename pool/project/beta pool/project/production -.Ed -.Pp -Once the legacy version is no longer needed, it can be destroyed. -.Bd -literal -offset 2n -.Li # Ic zfs destroy pool/project/legacy -.Ed -.It Sy Example 11 No Inheriting Tn ZFS No Properties -.Pp -The following command causes -.Em pool/home/bob -and -.Em pool/home/anne -to inherit the -.Sy checksum -property from their parent. -.Bd -literal -offset 2n -.Li # Ic zfs inherit checksum pool/home/bob pool/home/anne -.Ed -.It Sy Example 12 No Remotely Replicating Tn ZFS No Data -.Pp -The following commands send a full stream and then an incremental stream to a -remote machine, restoring them into -.Sy poolB/received/fs@a -and -.Sy poolB/received/fs@b , -respectively. -.Sy poolB -must contain the file system -.Sy poolB/received , -and must not initially contain -.Sy poolB/received/fs . -.Bd -literal -offset 2n -.Li # Ic zfs send pool/fs@a | ssh host zfs receive poolB/received/fs@a -.Li # Ic zfs send -i a pool/fs@b | ssh host zfs receive poolB/received/fs -.Ed -.It Xo -.Sy Example 13 -Using the -.Qq zfs receive -d -Option -.Xc -.Pp -The following command sends a full stream of -.Sy poolA/fsA/fsB@snap -to a remote machine, receiving it into -.Sy poolB/received/fsA/fsB@snap . -The -.Sy fsA/fsB@snap -portion of the received snapshot's name is determined from the name of the sent -snapshot. -.Sy poolB -must contain the file system -.Sy poolB/received . -If -.Sy poolB/received/fsA -does not exist, it is created as an empty file system. -.Bd -literal -offset 2n -.Li # Ic zfs send poolA/fsA/fsB@snap | ssh host zfs receive -d poolB/received -.Ed -.It Sy Example 14 No Setting User Properties -.Pp -The following example sets the user-defined -.Sy com.example:department -property for a dataset. -.Bd -literal -offset 2n -.Li # Ic zfs set com.example:department=12345 tank/accounting -.Ed -.It Sy Example 15 No Performing a Rolling Snapshot -.Pp -The following example shows how to maintain a history of snapshots with a -consistent naming scheme. To keep a week's worth of snapshots, the user -destroys the oldest snapshot, renames the remaining snapshots, and then creates -a new snapshot, as follows: -.Bd -literal -offset 2n -.Li # Ic zfs destroy -r pool/users@7daysago -.Li # Ic zfs rename -r pool/users@6daysago @7daysago -.Li # Ic zfs rename -r pool/users@5daysago @6daysago -.Li # Ic zfs rename -r pool/users@4daysago @5daysago -.Li # Ic zfs rename -r pool/users@3daysago @4daysago -.Li # Ic zfs rename -r pool/users@2daysago @3daysago -.Li # Ic zfs rename -r pool/users@yesterday @2daysago -.Li # Ic zfs rename -r pool/users@today @yesterday -.Li # Ic zfs snapshot -r pool/users@today -.Ed -.It Xo -.Sy Example 16 -Setting -.Qq sharenfs -Property Options on a ZFS File System -.Xc -.Pp -The following command shows how to set -.Sy sharenfs -property options to enable root access for a specific network on the -.Em tank/home -file system. The contents of the -.Sy sharenfs -property are valid -.Xr exports 5 -options. -.Bd -literal -offset 2n -.Li # Ic zfs set sharenfs="maproot=root,network 192.168.0.0/24" tank/home -.Ed -.Pp -Another way to write this command with the same result is: -.Bd -literal -offset 2n -.Li # Ic set zfs sharenfs="-maproot=root -network 192.168.0.0/24" tank/home -.Ed -.It Xo -.Sy Example 17 -Delegating -.Tn ZFS -Administration Permissions on a -.Tn ZFS -Dataset -.Xc -.Pp -The following example shows how to set permissions so that user -.Em cindys -can create, destroy, mount, and take snapshots on -.Em tank/cindys . -The permissions on -.Em tank/cindys -are also displayed. -.Bd -literal -offset 2n -.Li # Ic zfs allow cindys create,destroy,mount,snapshot tank/cindys -.Li # Ic zfs allow tank/cindys ----- Permissions on tank/cindys -------------------------------------- -Local+Descendent permissions: - user cindys create,destroy,mount,snapshot -.Ed -.It Sy Example 18 No Delegating Create Time Permissions on a Tn ZFS No Dataset -.Pp -The following example shows how to grant anyone in the group -.Em staff -to create file systems in -.Em tank/users . -This syntax also allows staff members to destroy their own file systems, but -not destroy anyone else's file system. The permissions on -.Em tank/users -are also displayed. -.Bd -literal -offset 2n -.Li # Ic zfs allow staff create,mount tank/users -.Li # Ic zfs allow -c destroy tank/users -.Li # Ic zfs allow tank/users ----- Permissions on tank/users --------------------------------------- -Permission sets: - destroy -Local+Descendent permissions: - group staff create,mount -.Ed -.It Xo -.Sy Example 19 -Defining and Granting a Permission Set on a -.Tn ZFS -Dataset -.Xc -.Pp -The following example shows how to define and grant a permission set on the -.Em tank/users -file system. The permissions on -.Em tank/users -are also displayed. -.Bd -literal -offset 2n -.Li # Ic zfs allow -s @pset create,destroy,snapshot,mount tank/users -.Li # Ic zfs allow staff @pset tank/users -.Li # Ic zfs allow tank/users ----- Permissions on tank/users --------------------------------------- -Permission sets: - @pset create,destroy,mount,snapshot -Local+Descendent permissions: - group staff @pset -.Ed -.It Sy Example 20 No Delegating Property Permissions on a Tn ZFS No Dataset -.Pp -The following example shows to grant the ability to set quotas and reservations -on the -.Sy users/home -file system. The permissions on -.Sy users/home -are also displayed. -.Bd -literal -offset 2n -.Li # Ic zfs allow cindys quota,reservation users/home -.Li # Ic zfs allow users/home ----- Permissions on users/home --------------------------------------- -Local+Descendent permissions: - user cindys quota,reservation -.Li # Ic su - cindys -.Li cindys% Ic zfs set quota=10G users/home/marks -.Li cindys% Ic zfs get quota users/home/marks -NAME PROPERTY VALUE SOURCE -users/home/marks quota 10G local -.Ed -.It Sy Example 21 No Removing ZFS Delegated Permissions on a Tn ZFS No Dataset -.Pp -The following example shows how to remove the snapshot permission from the -.Em staff -group on the -.Em tank/users -file system. The permissions on -.Em tank/users -are also displayed. -.Bd -literal -offset 2n -.Li # Ic zfs unallow staff snapshot tank/users -.Li # Ic zfs allow tank/users ----- Permissions on tank/users --------------------------------------- -Permission sets: - @pset create,destroy,mount,snapshot -Local+Descendent permissions: - group staff @pset -.Ed -.It Sy Example 22 Showing the differences between a snapshot and a ZFS Dataset -.Pp -The following example shows how to see what has changed between a prior -snapshot of a ZFS Dataset and its current state. The -.Fl F -option is used to indicate type information for the files affected. -.Bd -literal -offset 2n -.Li # Ic zfs diff tank/test@before tank/test -M / /tank/test/ -M F /tank/test/linked (+1) -R F /tank/test/oldname -> /tank/test/newname -- F /tank/test/deleted -+ F /tank/test/created -M F /tank/test/modified -.Ed -.El -.Sh SEE ALSO -.Xr chmod 2 , -.Xr fsync 2 , -.Xr exports 5 , -.Xr fstab 5 , -.Xr rc.conf 5 , -.Xr jail 8 , -.Xr mount 8 , -.Xr umount 8 , -.Xr zfs-program 8 , -.Xr zpool 8 -.Sh HISTORY -The -.Nm -utility first appeared in -.Fx 7.0 . -.Sh AUTHORS -This manual page is a -.Xr mdoc 7 -reimplementation of the -.Tn OpenSolaris -manual page -.Em zfs(1M) , -modified and customized for -.Fx -and licensed under the -Common Development and Distribution License -.Pq Tn CDDL . -.Pp -The -.Xr mdoc 7 -implementation of this manual page was initially written by -.An Martin Matuska Aq mm@FreeBSD.org . diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.c b/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.c deleted file mode 100644 index a291db083568..000000000000 --- a/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.c +++ /dev/null @@ -1,497 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ - -/* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012 Pawel Jakub Dawidek. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. - * Copyright (c) 2013 by Delphix. All rights reserved. - */ - -#include <libintl.h> -#include <libuutil.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <strings.h> - -#include <libzfs.h> - -#include "zfs_util.h" -#include "zfs_iter.h" - -/* - * This is a private interface used to gather up all the datasets specified on - * the command line so that we can iterate over them in order. - * - * First, we iterate over all filesystems, gathering them together into an - * AVL tree. We report errors for any explicitly specified datasets - * that we couldn't open. - * - * When finished, we have an AVL tree of ZFS handles. We go through and execute - * the provided callback for each one, passing whatever data the user supplied. - */ - -typedef struct zfs_node { - zfs_handle_t *zn_handle; - uu_avl_node_t zn_avlnode; -} zfs_node_t; - -typedef struct callback_data { - uu_avl_t *cb_avl; - int cb_flags; - zfs_type_t cb_types; - zfs_sort_column_t *cb_sortcol; - zprop_list_t **cb_proplist; - int cb_depth_limit; - int cb_depth; - uint8_t cb_props_table[ZFS_NUM_PROPS]; -} callback_data_t; - -uu_avl_pool_t *avl_pool; - -/* - * Include snaps if they were requested or if this a zfs list where types - * were not specified and the "listsnapshots" property is set on this pool. - */ -static boolean_t -zfs_include_snapshots(zfs_handle_t *zhp, callback_data_t *cb) -{ - zpool_handle_t *zph; - - if ((cb->cb_flags & ZFS_ITER_PROP_LISTSNAPS) == 0) - return (cb->cb_types & ZFS_TYPE_SNAPSHOT); - - zph = zfs_get_pool_handle(zhp); - return (zpool_get_prop_int(zph, ZPOOL_PROP_LISTSNAPS, NULL)); -} - -/* - * Called for each dataset. If the object is of an appropriate type, - * add it to the avl tree and recurse over any children as necessary. - */ -static int -zfs_callback(zfs_handle_t *zhp, void *data) -{ - callback_data_t *cb = data; - boolean_t should_close = B_TRUE; - boolean_t include_snaps = zfs_include_snapshots(zhp, cb); - boolean_t include_bmarks = (cb->cb_types & ZFS_TYPE_BOOKMARK); - - if ((zfs_get_type(zhp) & cb->cb_types) || - ((zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) && include_snaps)) { - uu_avl_index_t idx; - zfs_node_t *node = safe_malloc(sizeof (zfs_node_t)); - - node->zn_handle = zhp; - uu_avl_node_init(node, &node->zn_avlnode, avl_pool); - if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol, - &idx) == NULL) { - if (cb->cb_proplist) { - if ((*cb->cb_proplist) && - !(*cb->cb_proplist)->pl_all) - zfs_prune_proplist(zhp, - cb->cb_props_table); - - if (zfs_expand_proplist(zhp, cb->cb_proplist, - (cb->cb_flags & ZFS_ITER_RECVD_PROPS), - (cb->cb_flags & ZFS_ITER_LITERAL_PROPS)) - != 0) { - free(node); - return (-1); - } - } - uu_avl_insert(cb->cb_avl, node, idx); - should_close = B_FALSE; - } else { - free(node); - } - } - - /* - * Recurse if necessary. - */ - if (cb->cb_flags & ZFS_ITER_RECURSE && - ((cb->cb_flags & ZFS_ITER_DEPTH_LIMIT) == 0 || - cb->cb_depth < cb->cb_depth_limit)) { - cb->cb_depth++; - if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) - (void) zfs_iter_filesystems(zhp, zfs_callback, data); - if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT | - ZFS_TYPE_BOOKMARK)) == 0) && include_snaps) - (void) zfs_iter_snapshots(zhp, - (cb->cb_flags & ZFS_ITER_SIMPLE) != 0, zfs_callback, - data, 0, 0); - if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT | - ZFS_TYPE_BOOKMARK)) == 0) && include_bmarks) - (void) zfs_iter_bookmarks(zhp, zfs_callback, data); - cb->cb_depth--; - } - - if (should_close) - zfs_close(zhp); - - return (0); -} - -int -zfs_add_sort_column(zfs_sort_column_t **sc, const char *name, - boolean_t reverse) -{ - zfs_sort_column_t *col; - zfs_prop_t prop; - - if ((prop = zfs_name_to_prop(name)) == ZPROP_INVAL && - !zfs_prop_user(name)) - return (-1); - - col = safe_malloc(sizeof (zfs_sort_column_t)); - - col->sc_prop = prop; - col->sc_reverse = reverse; - if (prop == ZPROP_INVAL) { - col->sc_user_prop = safe_malloc(strlen(name) + 1); - (void) strcpy(col->sc_user_prop, name); - } - - if (*sc == NULL) { - col->sc_last = col; - *sc = col; - } else { - (*sc)->sc_last->sc_next = col; - (*sc)->sc_last = col; - } - - return (0); -} - -void -zfs_free_sort_columns(zfs_sort_column_t *sc) -{ - zfs_sort_column_t *col; - - while (sc != NULL) { - col = sc->sc_next; - free(sc->sc_user_prop); - free(sc); - sc = col; - } -} - -boolean_t -zfs_sort_only_by_name(const zfs_sort_column_t *sc) -{ - - return (sc != NULL && sc->sc_next == NULL && - sc->sc_prop == ZFS_PROP_NAME); -} - -/* ARGSUSED */ -static int -zfs_compare(const void *larg, const void *rarg, void *unused) -{ - zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; - zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; - const char *lname = zfs_get_name(l); - const char *rname = zfs_get_name(r); - char *lat, *rat; - uint64_t lcreate, rcreate; - int ret; - - lat = (char *)strchr(lname, '@'); - rat = (char *)strchr(rname, '@'); - - if (lat != NULL) - *lat = '\0'; - if (rat != NULL) - *rat = '\0'; - - ret = strcmp(lname, rname); - if (ret == 0 && (lat != NULL || rat != NULL)) { - /* - * If we're comparing a dataset to one of its snapshots, we - * always make the full dataset first. - */ - if (lat == NULL) { - ret = -1; - } else if (rat == NULL) { - ret = 1; - } else { - /* - * If we have two snapshots from the same dataset, then - * we want to sort them according to creation time. We - * use the hidden CREATETXG property to get an absolute - * ordering of snapshots. - */ - lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG); - rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG); - - /* - * Both lcreate and rcreate being 0 means we don't have - * properties and we should compare full name. - */ - if (lcreate == 0 && rcreate == 0) - ret = strcmp(lat + 1, rat + 1); - else if (lcreate < rcreate) - ret = -1; - else if (lcreate > rcreate) - ret = 1; - } - } - - if (lat != NULL) - *lat = '@'; - if (rat != NULL) - *rat = '@'; - - return (ret); -} - -/* - * Sort datasets by specified columns. - * - * o Numeric types sort in ascending order. - * o String types sort in alphabetical order. - * o Types inappropriate for a row sort that row to the literal - * bottom, regardless of the specified ordering. - * - * If no sort columns are specified, or two datasets compare equally - * across all specified columns, they are sorted alphabetically by name - * with snapshots grouped under their parents. - */ -static int -zfs_sort(const void *larg, const void *rarg, void *data) -{ - zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; - zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; - zfs_sort_column_t *sc = (zfs_sort_column_t *)data; - zfs_sort_column_t *psc; - - for (psc = sc; psc != NULL; psc = psc->sc_next) { - char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN]; - char *lstr, *rstr; - uint64_t lnum, rnum; - boolean_t lvalid, rvalid; - int ret = 0; - - /* - * We group the checks below the generic code. If 'lstr' and - * 'rstr' are non-NULL, then we do a string based comparison. - * Otherwise, we compare 'lnum' and 'rnum'. - */ - lstr = rstr = NULL; - if (psc->sc_prop == ZPROP_INVAL) { - nvlist_t *luser, *ruser; - nvlist_t *lval, *rval; - - luser = zfs_get_user_props(l); - ruser = zfs_get_user_props(r); - - lvalid = (nvlist_lookup_nvlist(luser, - psc->sc_user_prop, &lval) == 0); - rvalid = (nvlist_lookup_nvlist(ruser, - psc->sc_user_prop, &rval) == 0); - - if (lvalid) - verify(nvlist_lookup_string(lval, - ZPROP_VALUE, &lstr) == 0); - if (rvalid) - verify(nvlist_lookup_string(rval, - ZPROP_VALUE, &rstr) == 0); - } else if (psc->sc_prop == ZFS_PROP_NAME) { - lvalid = rvalid = B_TRUE; - - (void) strlcpy(lbuf, zfs_get_name(l), sizeof (lbuf)); - (void) strlcpy(rbuf, zfs_get_name(r), sizeof (rbuf)); - - lstr = lbuf; - rstr = rbuf; - } else if (zfs_prop_is_string(psc->sc_prop)) { - lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf, - sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0); - rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf, - sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0); - - lstr = lbuf; - rstr = rbuf; - } else { - lvalid = zfs_prop_valid_for_type(psc->sc_prop, - zfs_get_type(l)); - rvalid = zfs_prop_valid_for_type(psc->sc_prop, - zfs_get_type(r)); - - if (lvalid) - (void) zfs_prop_get_numeric(l, psc->sc_prop, - &lnum, NULL, NULL, 0); - if (rvalid) - (void) zfs_prop_get_numeric(r, psc->sc_prop, - &rnum, NULL, NULL, 0); - } - - if (!lvalid && !rvalid) - continue; - else if (!lvalid) - return (1); - else if (!rvalid) - return (-1); - - if (lstr) - ret = strcmp(lstr, rstr); - else if (lnum < rnum) - ret = -1; - else if (lnum > rnum) - ret = 1; - - if (ret != 0) { - if (psc->sc_reverse == B_TRUE) - ret = (ret < 0) ? 1 : -1; - return (ret); - } - } - - return (zfs_compare(larg, rarg, NULL)); -} - -int -zfs_for_each(int argc, char **argv, int flags, zfs_type_t types, - zfs_sort_column_t *sortcol, zprop_list_t **proplist, int limit, - zfs_iter_f callback, void *data) -{ - callback_data_t cb = {0}; - int ret = 0; - zfs_node_t *node; - uu_avl_walk_t *walk; - - avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t), - offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT); - - if (avl_pool == NULL) - nomem(); - - cb.cb_sortcol = sortcol; - cb.cb_flags = flags; - cb.cb_proplist = proplist; - cb.cb_types = types; - cb.cb_depth_limit = limit; - /* - * If cb_proplist is provided then in the zfs_handles created we - * retain only those properties listed in cb_proplist and sortcol. - * The rest are pruned. So, the caller should make sure that no other - * properties other than those listed in cb_proplist/sortcol are - * accessed. - * - * If cb_proplist is NULL then we retain all the properties. We - * always retain the zoned property, which some other properties - * need (userquota & friends), and the createtxg property, which - * we need to sort snapshots. - */ - if (cb.cb_proplist && *cb.cb_proplist) { - zprop_list_t *p = *cb.cb_proplist; - - while (p) { - if (p->pl_prop >= ZFS_PROP_TYPE && - p->pl_prop < ZFS_NUM_PROPS) { - cb.cb_props_table[p->pl_prop] = B_TRUE; - } - p = p->pl_next; - } - - while (sortcol) { - if (sortcol->sc_prop >= ZFS_PROP_TYPE && - sortcol->sc_prop < ZFS_NUM_PROPS) { - cb.cb_props_table[sortcol->sc_prop] = B_TRUE; - } - sortcol = sortcol->sc_next; - } - - cb.cb_props_table[ZFS_PROP_ZONED] = B_TRUE; - cb.cb_props_table[ZFS_PROP_CREATETXG] = B_TRUE; - } else { - (void) memset(cb.cb_props_table, B_TRUE, - sizeof (cb.cb_props_table)); - } - - if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) - nomem(); - - if (argc == 0) { - /* - * If given no arguments, iterate over all datasets. - */ - cb.cb_flags |= ZFS_ITER_RECURSE; - ret = zfs_iter_root(g_zfs, zfs_callback, &cb); - } else { - int i; - zfs_handle_t *zhp; - zfs_type_t argtype; - - /* - * If we're recursive, then we always allow filesystems as - * arguments. If we also are interested in snapshots or - * bookmarks, then we can take volumes as well. - */ - argtype = types; - if (flags & ZFS_ITER_RECURSE) { - argtype |= ZFS_TYPE_FILESYSTEM; - if (types & (ZFS_TYPE_SNAPSHOT | ZFS_TYPE_BOOKMARK)) - argtype |= ZFS_TYPE_VOLUME; - } - - for (i = 0; i < argc; i++) { - if (flags & ZFS_ITER_ARGS_CAN_BE_PATHS) { - zhp = zfs_path_to_zhandle(g_zfs, argv[i], - argtype); - } else { - zhp = zfs_open(g_zfs, argv[i], argtype); - } - if (zhp != NULL) - ret |= zfs_callback(zhp, &cb); - else - ret = 1; - } - } - - /* - * At this point we've got our AVL tree full of zfs handles, so iterate - * over each one and execute the real user callback. - */ - for (node = uu_avl_first(cb.cb_avl); node != NULL; - node = uu_avl_next(cb.cb_avl, node)) - ret |= callback(node->zn_handle, data); - - /* - * Finally, clean up the AVL tree. - */ - if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) - nomem(); - - while ((node = uu_avl_walk_next(walk)) != NULL) { - uu_avl_remove(cb.cb_avl, node); - zfs_close(node->zn_handle); - free(node); - } - - uu_avl_walk_end(walk); - uu_avl_destroy(cb.cb_avl); - uu_avl_pool_destroy(avl_pool); - - return (ret); -} diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.h b/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.h deleted file mode 100644 index b89b466ce6fe..000000000000 --- a/cddl/contrib/opensolaris/cmd/zfs/zfs_iter.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ - -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved. - * Copyright 2013 Nexenta Systems, Inc. All rights reserved. - */ - -#ifndef ZFS_ITER_H -#define ZFS_ITER_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct zfs_sort_column { - struct zfs_sort_column *sc_next; - struct zfs_sort_column *sc_last; - zfs_prop_t sc_prop; - char *sc_user_prop; - boolean_t sc_reverse; -} zfs_sort_column_t; - -#define ZFS_ITER_RECURSE (1 << 0) -#define ZFS_ITER_ARGS_CAN_BE_PATHS (1 << 1) -#define ZFS_ITER_PROP_LISTSNAPS (1 << 2) -#define ZFS_ITER_DEPTH_LIMIT (1 << 3) -#define ZFS_ITER_RECVD_PROPS (1 << 4) -#define ZFS_ITER_SIMPLE (1 << 5) -#define ZFS_ITER_LITERAL_PROPS (1 << 6) - -int zfs_for_each(int, char **, int options, zfs_type_t, - zfs_sort_column_t *, zprop_list_t **, int, zfs_iter_f, void *); -int zfs_add_sort_column(zfs_sort_column_t **, const char *, boolean_t); -void zfs_free_sort_columns(zfs_sort_column_t *); -boolean_t zfs_sort_only_by_name(const zfs_sort_column_t *); - -#ifdef __cplusplus -} -#endif - -#endif /* ZFS_ITER_H */ diff --git a/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c b/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c deleted file mode 100644 index d453ba030488..000000000000 --- a/cddl/contrib/opensolaris/cmd/zfs/zfs_main.c +++ /dev/null @@ -1,7592 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ - -/* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2011, 2016 by Delphix. All rights reserved. - * Copyright 2012 Milan Jurik. All rights reserved. - * Copyright (c) 2012, Joyent, Inc. All rights reserved. - * Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved. - * Copyright (c) 2012 Martin Matuska <mm@FreeBSD.org>. All rights reserved. - * Copyright (c) 2013 Steven Hartland. All rights reserved. - * Copyright (c) 2014 Integros [integros.com] - * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>. - * Copyright 2016 Nexenta Systems, Inc. - * Copyright (c) 2019 Datto Inc. - */ - -#include <assert.h> -#include <ctype.h> -#include <errno.h> -#include <getopt.h> -#include <libgen.h> -#include <libintl.h> -#include <libuutil.h> -#include <libnvpair.h> -#include <locale.h> -#include <stddef.h> -#include <stdio.h> -#include <stdlib.h> -#include <strings.h> -#include <unistd.h> -#include <fcntl.h> -#include <zone.h> -#include <grp.h> -#include <pwd.h> -#include <signal.h> -#include <sys/debug.h> -#include <sys/list.h> -#include <sys/mntent.h> -#include <sys/mnttab.h> -#include <sys/mount.h> -#include <sys/stat.h> -#include <sys/fs/zfs.h> -#include <sys/types.h> -#include <time.h> -#include <err.h> -#include <jail.h> - -#include <libzfs.h> -#include <libzfs_core.h> -#include <zfs_prop.h> -#include <zfs_deleg.h> -#include <libuutil.h> -#ifdef illumos -#include <aclutils.h> -#include <directory.h> -#include <idmap.h> -#include <libshare.h> -#endif - -#include "zfs_iter.h" -#include "zfs_util.h" -#include "zfs_comutil.h" - -libzfs_handle_t *g_zfs; - -static FILE *mnttab_file; -static char history_str[HIS_MAX_RECORD_LEN]; -static boolean_t log_history = B_TRUE; - -static int zfs_do_clone(int argc, char **argv); -static int zfs_do_create(int argc, char **argv); -static int zfs_do_destroy(int argc, char **argv); -static int zfs_do_get(int argc, char **argv); -static int zfs_do_inherit(int argc, char **argv); -static int zfs_do_list(int argc, char **argv); -static int zfs_do_mount(int argc, char **argv); -static int zfs_do_rename(int argc, char **argv); -static int zfs_do_rollback(int argc, char **argv); -static int zfs_do_set(int argc, char **argv); -static int zfs_do_upgrade(int argc, char **argv); -static int zfs_do_snapshot(int argc, char **argv); -static int zfs_do_unmount(int argc, char **argv); -static int zfs_do_share(int argc, char **argv); -static int zfs_do_unshare(int argc, char **argv); -static int zfs_do_send(int argc, char **argv); -static int zfs_do_receive(int argc, char **argv); -static int zfs_do_promote(int argc, char **argv); -static int zfs_do_userspace(int argc, char **argv); -static int zfs_do_allow(int argc, char **argv); -static int zfs_do_unallow(int argc, char **argv); -static int zfs_do_hold(int argc, char **argv); -static int zfs_do_holds(int argc, char **argv); -static int zfs_do_release(int argc, char **argv); -static int zfs_do_diff(int argc, char **argv); -static int zfs_do_jail(int argc, char **argv); -static int zfs_do_unjail(int argc, char **argv); -static int zfs_do_bookmark(int argc, char **argv); -static int zfs_do_remap(int argc, char **argv); -static int zfs_do_channel_program(int argc, char **argv); - -/* - * Enable a reasonable set of defaults for libumem debugging on DEBUG builds. - */ - -#ifdef DEBUG -const char * -_umem_debug_init(void) -{ - return ("default,verbose"); /* $UMEM_DEBUG setting */ -} - -const char * -_umem_logging_init(void) -{ - return ("fail,contents"); /* $UMEM_LOGGING setting */ -} -#endif - -typedef enum { - HELP_CLONE, - HELP_CREATE, - HELP_DESTROY, - HELP_GET, - HELP_INHERIT, - HELP_UPGRADE, - HELP_JAIL, - HELP_UNJAIL, - HELP_LIST, - HELP_MOUNT, - HELP_PROMOTE, - HELP_RECEIVE, - HELP_RENAME, - HELP_ROLLBACK, - HELP_SEND, - HELP_SET, - HELP_SHARE, - HELP_SNAPSHOT, - HELP_UNMOUNT, - HELP_UNSHARE, - HELP_ALLOW, - HELP_UNALLOW, - HELP_USERSPACE, - HELP_GROUPSPACE, - HELP_HOLD, - HELP_HOLDS, - HELP_RELEASE, - HELP_DIFF, - HELP_REMAP, - HELP_BOOKMARK, - HELP_CHANNEL_PROGRAM, -} zfs_help_t; - -typedef struct zfs_command { - const char *name; - int (*func)(int argc, char **argv); - zfs_help_t usage; -} zfs_command_t; - -/* - * Master command table. Each ZFS command has a name, associated function, and - * usage message. The usage messages need to be internationalized, so we have - * to have a function to return the usage message based on a command index. - * - * These commands are organized according to how they are displayed in the usage - * message. An empty command (one with a NULL name) indicates an empty line in - * the generic usage message. - */ -static zfs_command_t command_table[] = { - { "create", zfs_do_create, HELP_CREATE }, - { "destroy", zfs_do_destroy, HELP_DESTROY }, - { NULL }, - { "snapshot", zfs_do_snapshot, HELP_SNAPSHOT }, - { "rollback", zfs_do_rollback, HELP_ROLLBACK }, - { "clone", zfs_do_clone, HELP_CLONE }, - { "promote", zfs_do_promote, HELP_PROMOTE }, - { "rename", zfs_do_rename, HELP_RENAME }, - { "bookmark", zfs_do_bookmark, HELP_BOOKMARK }, - { "program", zfs_do_channel_program, HELP_CHANNEL_PROGRAM }, - { NULL }, - { "list", zfs_do_list, HELP_LIST }, - { NULL }, - { "set", zfs_do_set, HELP_SET }, - { "get", zfs_do_get, HELP_GET }, - { "inherit", zfs_do_inherit, HELP_INHERIT }, - { "upgrade", zfs_do_upgrade, HELP_UPGRADE }, - { "userspace", zfs_do_userspace, HELP_USERSPACE }, - { "groupspace", zfs_do_userspace, HELP_GROUPSPACE }, - { NULL }, - { "mount", zfs_do_mount, HELP_MOUNT }, - { "unmount", zfs_do_unmount, HELP_UNMOUNT }, - { "share", zfs_do_share, HELP_SHARE }, - { "unshare", zfs_do_unshare, HELP_UNSHARE }, - { NULL }, - { "send", zfs_do_send, HELP_SEND }, - { "receive", zfs_do_receive, HELP_RECEIVE }, - { NULL }, - { "allow", zfs_do_allow, HELP_ALLOW }, - { NULL }, - { "unallow", zfs_do_unallow, HELP_UNALLOW }, - { NULL }, - { "hold", zfs_do_hold, HELP_HOLD }, - { "holds", zfs_do_holds, HELP_HOLDS }, - { "release", zfs_do_release, HELP_RELEASE }, - { "diff", zfs_do_diff, HELP_DIFF }, - { NULL }, - { "jail", zfs_do_jail, HELP_JAIL }, - { "unjail", zfs_do_unjail, HELP_UNJAIL }, - { "remap", zfs_do_remap, HELP_REMAP }, -}; - -#define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) - -zfs_command_t *current_command; - -static const char * -get_usage(zfs_help_t idx) -{ - switch (idx) { - case HELP_CLONE: - return (gettext("\tclone [-p] [-o property=value] ... " - "<snapshot> <filesystem|volume>\n")); - case HELP_CREATE: - return (gettext("\tcreate [-pu] [-o property=value] ... " - "<filesystem>\n" - "\tcreate [-ps] [-b blocksize] [-o property=value] ... " - "-V <size> <volume>\n")); - case HELP_DESTROY: - return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n" - "\tdestroy [-dnpRrv] " - "<filesystem|volume>@<snap>[%<snap>][,...]\n" - "\tdestroy <filesystem|volume>#<bookmark>\n")); - case HELP_GET: - return (gettext("\tget [-rHp] [-d max] " - "[-o \"all\" | field[,...]]\n" - "\t [-t type[,...]] [-s source[,...]]\n" - "\t <\"all\" | property[,...]> " - "[filesystem|volume|snapshot|bookmark] ...\n")); - case HELP_INHERIT: - return (gettext("\tinherit [-rS] <property> " - "<filesystem|volume|snapshot> ...\n")); - case HELP_UPGRADE: - return (gettext("\tupgrade [-v]\n" - "\tupgrade [-r] [-V version] <-a | filesystem ...>\n")); - case HELP_JAIL: - return (gettext("\tjail <jailid|jailname> <filesystem>\n")); - case HELP_UNJAIL: - return (gettext("\tunjail <jailid|jailname> <filesystem>\n")); - case HELP_LIST: - return (gettext("\tlist [-Hp] [-r|-d max] [-o property[,...]] " - "[-s property]...\n\t [-S property]... [-t type[,...]] " - "[filesystem|volume|snapshot] ...\n")); - case HELP_MOUNT: - return (gettext("\tmount\n" - "\tmount [-vO] [-o opts] <-a | filesystem>\n")); - case HELP_PROMOTE: - return (gettext("\tpromote <clone-filesystem>\n")); - case HELP_RECEIVE: - return (gettext("\treceive|recv [-vnsFMu] <filesystem|volume|" - "snapshot>\n" - "\treceive|recv [-vnsFMu] [-o origin=<snapshot>] [-d | -e] " - "<filesystem>\n" - "\treceive|recv -A <filesystem|volume>\n")); - case HELP_RENAME: - return (gettext("\trename [-f] <filesystem|volume|snapshot> " - "<filesystem|volume|snapshot>\n" - "\trename [-f] -p <filesystem|volume> <filesystem|volume>\n" - "\trename -r <snapshot> <snapshot>\n" - "\trename <bookmark> <bookmark>\n" - "\trename -u [-p] <filesystem> <filesystem>")); - case HELP_ROLLBACK: - return (gettext("\trollback [-rRf] <snapshot>\n")); - case HELP_SEND: - return (gettext("\tsend [-DnPpRvLec] [-[iI] snapshot] " - "<snapshot>\n" - "\tsend [-LPcenv] [-i snapshot|bookmark] " - "<filesystem|volume|snapshot>\n" - "\tsend [-nvPe] -t <receive_resume_token>\n")); - case HELP_SET: - return (gettext("\tset <property=value> ... " - "<filesystem|volume|snapshot> ...\n")); - case HELP_SHARE: - return (gettext("\tshare <-a | filesystem>\n")); - case HELP_SNAPSHOT: - return (gettext("\tsnapshot|snap [-r] [-o property=value] ... " - "<filesystem|volume>@<snap> ...\n")); - case HELP_UNMOUNT: - return (gettext("\tunmount|umount [-f] " - "<-a | filesystem|mountpoint>\n")); - case HELP_UNSHARE: - return (gettext("\tunshare " - "<-a | filesystem|mountpoint>\n")); - case HELP_ALLOW: - return (gettext("\tallow <filesystem|volume>\n" - "\tallow [-ldug] " - "<\"everyone\"|user|group>[,...] <perm|@setname>[,...]\n" - "\t <filesystem|volume>\n" - "\tallow [-ld] -e <perm|@setname>[,...] " - "<filesystem|volume>\n" - "\tallow -c <perm|@setname>[,...] <filesystem|volume>\n" - "\tallow -s @setname <perm|@setname>[,...] " - "<filesystem|volume>\n")); - case HELP_UNALLOW: - return (gettext("\tunallow [-rldug] " - "<\"everyone\"|user|group>[,...]\n" - "\t [<perm|@setname>[,...]] <filesystem|volume>\n" - "\tunallow [-rld] -e [<perm|@setname>[,...]] " - "<filesystem|volume>\n" - "\tunallow [-r] -c [<perm|@setname>[,...]] " - "<filesystem|volume>\n" - "\tunallow [-r] -s @setname [<perm|@setname>[,...]] " - "<filesystem|volume>\n")); - case HELP_USERSPACE: - return (gettext("\tuserspace [-Hinp] [-o field[,...]] " - "[-s field] ...\n" - "\t [-S field] ... [-t type[,...]] " - "<filesystem|snapshot>\n")); - case HELP_GROUPSPACE: - return (gettext("\tgroupspace [-Hinp] [-o field[,...]] " - "[-s field] ...\n" - "\t [-S field] ... [-t type[,...]] " - "<filesystem|snapshot>\n")); - case HELP_HOLD: - return (gettext("\thold [-r] <tag> <snapshot> ...\n")); - case HELP_HOLDS: - return (gettext("\tholds [-Hp] [-r|-d depth] " - "<filesystem|volume|snapshot> ...\n")); - case HELP_RELEASE: - return (gettext("\trelease [-r] <tag> <snapshot> ...\n")); - case HELP_DIFF: - return (gettext("\tdiff [-FHt] <snapshot> " - "[snapshot|filesystem]\n")); - case HELP_REMAP: - return (gettext("\tremap <filesystem | volume>\n")); - case HELP_BOOKMARK: - return (gettext("\tbookmark <snapshot> <bookmark>\n")); - case HELP_CHANNEL_PROGRAM: - return (gettext("\tprogram [-jn] [-t <instruction limit>] " - "[-m <memory limit (b)>] <pool> <program file> " - "[lua args...]\n")); - } - - abort(); - /* NOTREACHED */ -} - -void -nomem(void) -{ - (void) fprintf(stderr, gettext("internal error: out of memory\n")); - exit(1); -} - -/* - * Utility function to guarantee malloc() success. - */ - -void * -safe_malloc(size_t size) -{ - void *data; - - if ((data = calloc(1, size)) == NULL) - nomem(); - - return (data); -} - -void * -safe_realloc(void *data, size_t size) -{ - void *newp; - if ((newp = realloc(data, size)) == NULL) { - free(data); - nomem(); - } - - return (newp); -} - -static char * -safe_strdup(char *str) -{ - char *dupstr = strdup(str); - - if (dupstr == NULL) - nomem(); - - return (dupstr); -} - -/* - * Callback routine that will print out information for each of - * the properties. - */ -static int -usage_prop_cb(int prop, void *cb) -{ - FILE *fp = cb; - - (void) fprintf(fp, "\t%-15s ", zfs_prop_to_name(prop)); - - if (zfs_prop_readonly(prop)) - (void) fprintf(fp, " NO "); - else - (void) fprintf(fp, "YES "); - - if (zfs_prop_inheritable(prop)) - (void) fprintf(fp, " YES "); - else - (void) fprintf(fp, " NO "); - - if (zfs_prop_values(prop) == NULL) - (void) fprintf(fp, "-\n"); - else - (void) fprintf(fp, "%s\n", zfs_prop_values(prop)); - - return (ZPROP_CONT); -} - -/* - * Display usage message. If we're inside a command, display only the usage for - * that command. Otherwise, iterate over the entire command table and display - * a complete usage message. - */ -static void -usage(boolean_t requested) -{ - int i; - boolean_t show_properties = B_FALSE; - FILE *fp = requested ? stdout : stderr; - - if (current_command == NULL) { - - (void) fprintf(fp, gettext("usage: zfs command args ...\n")); - (void) fprintf(fp, - gettext("where 'command' is one of the following:\n\n")); - - for (i = 0; i < NCOMMAND; i++) { - if (command_table[i].name == NULL) - (void) fprintf(fp, "\n"); - else - (void) fprintf(fp, "%s", - get_usage(command_table[i].usage)); - } - - (void) fprintf(fp, gettext("\nEach dataset is of the form: " - "pool/[dataset/]*dataset[@name]\n")); - } else { - (void) fprintf(fp, gettext("usage:\n")); - (void) fprintf(fp, "%s", get_usage(current_command->usage)); - } - - if (current_command != NULL && - (strcmp(current_command->name, "set") == 0 || - strcmp(current_command->name, "get") == 0 || - strcmp(current_command->name, "inherit") == 0 || - strcmp(current_command->name, "list") == 0)) - show_properties = B_TRUE; - - if (show_properties) { - (void) fprintf(fp, - gettext("\nThe following properties are supported:\n")); - - (void) fprintf(fp, "\n\t%-14s %s %s %s\n\n", - "PROPERTY", "EDIT", "INHERIT", "VALUES"); - - /* Iterate over all properties */ - (void) zprop_iter(usage_prop_cb, fp, B_FALSE, B_TRUE, - ZFS_TYPE_DATASET); - - (void) fprintf(fp, "\t%-15s ", "userused@..."); - (void) fprintf(fp, " NO NO <size>\n"); - (void) fprintf(fp, "\t%-15s ", "groupused@..."); - (void) fprintf(fp, " NO NO <size>\n"); - (void) fprintf(fp, "\t%-15s ", "userquota@..."); - (void) fprintf(fp, "YES NO <size> | none\n"); - (void) fprintf(fp, "\t%-15s ", "groupquota@..."); - (void) fprintf(fp, "YES NO <size> | none\n"); - (void) fprintf(fp, "\t%-15s ", "written@<snap>"); - (void) fprintf(fp, " NO NO <size>\n"); - - (void) fprintf(fp, gettext("\nSizes are specified in bytes " - "with standard units such as K, M, G, etc.\n")); - (void) fprintf(fp, gettext("\nUser-defined properties can " - "be specified by using a name containing a colon (:).\n")); - (void) fprintf(fp, gettext("\nThe {user|group}{used|quota}@ " - "properties must be appended with\n" - "a user or group specifier of one of these forms:\n" - " POSIX name (eg: \"matt\")\n" - " POSIX id (eg: \"126829\")\n" - " SMB name@domain (eg: \"matt@sun\")\n" - " SMB SID (eg: \"S-1-234-567-89\")\n")); - } else { - (void) fprintf(fp, - gettext("\nFor the property list, run: %s\n"), - "zfs set|get"); - (void) fprintf(fp, - gettext("\nFor the delegated permission list, run: %s\n"), - "zfs allow|unallow"); - } - - /* - * See comments at end of main(). - */ - if (getenv("ZFS_ABORT") != NULL) { - (void) printf("dumping core by request\n"); - abort(); - } - - exit(requested ? 0 : 2); -} - -/* - * Take a property=value argument string and add it to the given nvlist. - * Modifies the argument inplace. - */ -static int -parseprop(nvlist_t *props, char *propname) -{ - char *propval, *strval; - - if ((propval = strchr(propname, '=')) == NULL) { - (void) fprintf(stderr, gettext("missing " - "'=' for property=value argument\n")); - return (-1); - } - *propval = '\0'; - propval++; - if (nvlist_lookup_string(props, propname, &strval) == 0) { - (void) fprintf(stderr, gettext("property '%s' " - "specified multiple times\n"), propname); - return (-1); - } - if (nvlist_add_string(props, propname, propval) != 0) - nomem(); - return (0); -} - -static int -parse_depth(char *opt, int *flags) -{ - char *tmp; - int depth; - - depth = (int)strtol(opt, &tmp, 0); - if (*tmp) { - (void) fprintf(stderr, - gettext("%s is not an integer\n"), opt); - usage(B_FALSE); - } - if (depth < 0) { - (void) fprintf(stderr, - gettext("Depth can not be negative.\n")); - usage(B_FALSE); - } - *flags |= (ZFS_ITER_DEPTH_LIMIT|ZFS_ITER_RECURSE); - return (depth); -} - -#define PROGRESS_DELAY 2 /* seconds */ - -static char *pt_reverse = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"; -static time_t pt_begin; -static char *pt_header = NULL; -static boolean_t pt_shown; - -static void -start_progress_timer(void) -{ - pt_begin = time(NULL) + PROGRESS_DELAY; - pt_shown = B_FALSE; -} - -static void -set_progress_header(char *header) -{ - assert(pt_header == NULL); - pt_header = safe_strdup(header); - if (pt_shown) { - (void) printf("%s: ", header); - (void) fflush(stdout); - } -} - -static void -update_progress(char *update) -{ - if (!pt_shown && time(NULL) > pt_begin) { - int len = strlen(update); - - (void) printf("%s: %s%*.*s", pt_header, update, len, len, - pt_reverse); - (void) fflush(stdout); - pt_shown = B_TRUE; - } else if (pt_shown) { - int len = strlen(update); - - (void) printf("%s%*.*s", update, len, len, pt_reverse); - (void) fflush(stdout); - } -} - -static void -finish_progress(char *done) -{ - if (pt_shown) { - (void) printf("%s\n", done); - (void) fflush(stdout); - } - free(pt_header); - pt_header = NULL; -} - -/* - * Check if the dataset is mountable and should be automatically mounted. - */ -static boolean_t -should_auto_mount(zfs_handle_t *zhp) -{ - if (!zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, zfs_get_type(zhp))) - return (B_FALSE); - return (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON); -} - -/* - * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol> - * - * Given an existing dataset, create a writable copy whose initial contents - * are the same as the source. The newly created dataset maintains a - * dependency on the original; the original cannot be destroyed so long as - * the clone exists. - * - * The '-p' flag creates all the non-existing ancestors of the target first. - */ -static int -zfs_do_clone(int argc, char **argv) -{ - zfs_handle_t *zhp = NULL; - boolean_t parents = B_FALSE; - nvlist_t *props; - int ret = 0; - int c; - - if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) - nomem(); - - /* check options */ - while ((c = getopt(argc, argv, "o:p")) != -1) { - switch (c) { - case 'o': - if (parseprop(props, optarg) != 0) - return (1); - break; - case 'p': - parents = B_TRUE; - break; - case '?': - (void) fprintf(stderr, gettext("invalid option '%c'\n"), - optopt); - goto usage; - } - } - - argc -= optind; - argv += optind; - - /* check number of arguments */ - if (argc < 1) { - (void) fprintf(stderr, gettext("missing source dataset " - "argument\n")); - goto usage; - } - if (argc < 2) { - (void) fprintf(stderr, gettext("missing target dataset " - "argument\n")); - goto usage; - } - if (argc > 2) { - (void) fprintf(stderr, gettext("too many arguments\n")); - goto usage; - } - - /* open the source dataset */ - if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_SNAPSHOT)) == NULL) - return (1); - - if (parents && zfs_name_valid(argv[1], ZFS_TYPE_FILESYSTEM | - ZFS_TYPE_VOLUME)) { - /* - * Now create the ancestors of the target dataset. If the - * target already exists and '-p' option was used we should not - * complain. - */ - if (zfs_dataset_exists(g_zfs, argv[1], ZFS_TYPE_FILESYSTEM | - ZFS_TYPE_VOLUME)) - return (0); - if (zfs_create_ancestors(g_zfs, argv[1]) != 0) - return (1); - } - - /* pass to libzfs */ - ret = zfs_clone(zhp, argv[1], props); - - /* create the mountpoint if necessary */ - if (ret == 0) { - zfs_handle_t *clone; - - clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_DATASET); - if (clone != NULL) { - /* - * If the user doesn't want the dataset - * automatically mounted, then skip the mount/share - * step. - */ - if (should_auto_mount(clone)) { - if ((ret = zfs_mount(clone, NULL, 0)) != 0) { - (void) fprintf(stderr, gettext("clone " - "successfully created, " - "but not mounted\n")); - } else if ((ret = zfs_share(clone)) != 0) { - (void) fprintf(stderr, gettext("clone " - "successfully created, " - "but not shared\n")); - } - } - zfs_close(clone); - } - } - - zfs_close(zhp); - nvlist_free(props); - - return (!!ret); - -usage: - if (zhp) - zfs_close(zhp); - nvlist_free(props); - usage(B_FALSE); - return (-1); -} - -/* - * zfs create [-pu] [-o prop=value] ... fs - * zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size - * - * Create a new dataset. This command can be used to create filesystems - * and volumes. Snapshot creation is handled by 'zfs snapshot'. - * For volumes, the user must specify a size to be used. - * - * The '-s' flag applies only to volumes, and indicates that we should not try - * to set the reservation for this volume. By default we set a reservation - * equal to the size for any volume. For pools with SPA_VERSION >= - * SPA_VERSION_REFRESERVATION, we set a refreservation instead. - * - * The '-p' flag creates all the non-existing ancestors of the target first. - * - * The '-u' flag prevents mounting of newly created file system. - */ -static int -zfs_do_create(int argc, char **argv) -{ - zfs_type_t type = ZFS_TYPE_FILESYSTEM; - zfs_handle_t *zhp = NULL; - uint64_t volsize = 0; - int c; - boolean_t noreserve = B_FALSE; - boolean_t bflag = B_FALSE; - boolean_t parents = B_FALSE; - boolean_t nomount = B_FALSE; - int ret = 1; - nvlist_t *props; - uint64_t intval; - - if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) - nomem(); - - /* check options */ - while ((c = getopt(argc, argv, ":V:b:so:pu")) != -1) { - switch (c) { - case 'V': - type = ZFS_TYPE_VOLUME; - if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { - (void) fprintf(stderr, gettext("bad volume " - "size '%s': %s\n"), optarg, - libzfs_error_description(g_zfs)); - goto error; - } - - if (nvlist_add_uint64(props, - zfs_prop_to_name(ZFS_PROP_VOLSIZE), intval) != 0) - nomem(); - volsize = intval; - break; - case 'p': - parents = B_TRUE; - break; - case 'b': - bflag = B_TRUE; - if (zfs_nicestrtonum(g_zfs, optarg, &intval) != 0) { - (void) fprintf(stderr, gettext("bad volume " - "block size '%s': %s\n"), optarg, - libzfs_error_description(g_zfs)); - goto error; - } - - if (nvlist_add_uint64(props, - zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE), - intval) != 0) - nomem(); - break; - case 'o': - if (parseprop(props, optarg) != 0) - goto error; - break; - case 's': - noreserve = B_TRUE; - break; - case 'u': - nomount = B_TRUE; - break; - case ':': - (void) fprintf(stderr, gettext("missing size " - "argument\n")); - goto badusage; - case '?': - (void) fprintf(stderr, gettext("invalid option '%c'\n"), - optopt); - goto badusage; - } - } - - if ((bflag || noreserve) && type != ZFS_TYPE_VOLUME) { - (void) fprintf(stderr, gettext("'-s' and '-b' can only be " - "used when creating a volume\n")); - goto badusage; - } - if (nomount && type != ZFS_TYPE_FILESYSTEM) { - (void) fprintf(stderr, gettext("'-u' can only be " - "used when creating a file system\n")); - goto badusage; - } - - argc -= optind; - argv += optind; - - /* check number of arguments */ - if (argc == 0) { - (void) fprintf(stderr, gettext("missing %s argument\n"), - zfs_type_to_name(type)); - goto badusage; - } - if (argc > 1) { - (void) fprintf(stderr, gettext("too many arguments\n")); - goto badusage; - } - - if (type == ZFS_TYPE_VOLUME && !noreserve) { - zpool_handle_t *zpool_handle; - nvlist_t *real_props = NULL; - uint64_t spa_version; - char *p; - zfs_prop_t resv_prop; - char *strval; - char msg[1024]; - - if ((p = strchr(argv[0], '/')) != NULL) - *p = '\0'; - zpool_handle = zpool_open(g_zfs, argv[0]); - if (p != NULL) - *p = '/'; - if (zpool_handle == NULL) - goto error; - spa_version = zpool_get_prop_int(zpool_handle, - ZPOOL_PROP_VERSION, NULL); - if (spa_version >= SPA_VERSION_REFRESERVATION) - resv_prop = ZFS_PROP_REFRESERVATION; - else - resv_prop = ZFS_PROP_RESERVATION; - - (void) snprintf(msg, sizeof (msg), - gettext("cannot create '%s'"), argv[0]); - if (props && (real_props = zfs_valid_proplist(g_zfs, type, - props, 0, NULL, zpool_handle, msg)) == NULL) { - zpool_close(zpool_handle); - goto error; - } - zpool_close(zpool_handle); - - volsize = zvol_volsize_to_reservation(volsize, real_props); - nvlist_free(real_props); - - if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop), - &strval) != 0) { - if (nvlist_add_uint64(props, - zfs_prop_to_name(resv_prop), volsize) != 0) { - nvlist_free(props); - nomem(); - } - } - } - - if (parents && zfs_name_valid(argv[0], type)) { - /* - * Now create the ancestors of target dataset. If the target - * already exists and '-p' option was used we should not - * complain. - */ - if (zfs_dataset_exists(g_zfs, argv[0], type)) { - ret = 0; - goto error; - } - if (zfs_create_ancestors(g_zfs, argv[0]) != 0) - goto error; - } - - /* pass to libzfs */ - if (zfs_create(g_zfs, argv[0], type, props) != 0) - goto error; - - if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL) - goto error; - - ret = 0; - - /* - * Mount and/or share the new filesystem as appropriate. We provide a - * verbose error message to let the user know that their filesystem was - * in fact created, even if we failed to mount or share it. - * If the user doesn't want the dataset automatically mounted, - * then skip the mount/share step altogether. - */ - if (!nomount && should_auto_mount(zhp)) { - if (zfs_mount(zhp, NULL, 0) != 0) { - (void) fprintf(stderr, gettext("filesystem " - "successfully created, but not mounted\n")); - ret = 1; - } else if (zfs_share(zhp) != 0) { - (void) fprintf(stderr, gettext("filesystem " - "successfully created, but not shared\n")); - ret = 1; - } - } - -error: - if (zhp) - zfs_close(zhp); - nvlist_free(props); - return (ret); -badusage: - nvlist_free(props); - usage(B_FALSE); - return (2); -} - -/* - * zfs destroy [-rRf] <fs, vol> - * zfs destroy [-rRd] <snap> - * - * -r Recursively destroy all children - * -R Recursively destroy all dependents, including clones - * -f Force unmounting of any dependents - * -d If we can't destroy now, mark for deferred destruction - * - * Destroys the given dataset. By default, it will unmount any filesystems, - * and refuse to destroy a dataset that has any dependents. A dependent can - * either be a child, or a clone of a child. - */ -typedef struct destroy_cbdata { - boolean_t cb_first; - boolean_t cb_force; - boolean_t cb_recurse; - boolean_t cb_error; - boolean_t cb_doclones; - zfs_handle_t *cb_target; - boolean_t cb_defer_destroy; - boolean_t cb_verbose; - boolean_t cb_parsable; - boolean_t cb_dryrun; - nvlist_t *cb_nvl; - nvlist_t *cb_batchedsnaps; - - /* first snap in contiguous run */ - char *cb_firstsnap; - /* previous snap in contiguous run */ - char *cb_prevsnap; - int64_t cb_snapused; - char *cb_snapspec; - char *cb_bookmark; -} destroy_cbdata_t; - -/* - * Check for any dependents based on the '-r' or '-R' flags. - */ -static int -destroy_check_dependent(zfs_handle_t *zhp, void *data) -{ - destroy_cbdata_t *cbp = data; - const char *tname = zfs_get_name(cbp->cb_target); - const char *name = zfs_get_name(zhp); - - if (strncmp(tname, name, strlen(tname)) == 0 && - (name[strlen(tname)] == '/' || name[strlen(tname)] == '@')) { - /* - * This is a direct descendant, not a clone somewhere else in - * the hierarchy. - */ - if (cbp->cb_recurse) - goto out; - - if (cbp->cb_first) { - (void) fprintf(stderr, gettext("cannot destroy '%s': " - "%s has children\n"), - zfs_get_name(cbp->cb_target), - zfs_type_to_name(zfs_get_type(cbp->cb_target))); - (void) fprintf(stderr, gettext("use '-r' to destroy " - "the following datasets:\n")); - cbp->cb_first = B_FALSE; - cbp->cb_error = B_TRUE; - } - - (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); - } else { - /* - * This is a clone. We only want to report this if the '-r' - * wasn't specified, or the target is a snapshot. - */ - if (!cbp->cb_recurse && - zfs_get_type(cbp->cb_target) != ZFS_TYPE_SNAPSHOT) - goto out; - - if (cbp->cb_first) { - (void) fprintf(stderr, gettext("cannot destroy '%s': " - "%s has dependent clones\n"), - zfs_get_name(cbp->cb_target), - zfs_type_to_name(zfs_get_type(cbp->cb_target))); - (void) fprintf(stderr, gettext("use '-R' to destroy " - "the following datasets:\n")); - cbp->cb_first = B_FALSE; - cbp->cb_error = B_TRUE; - cbp->cb_dryrun = B_TRUE; - } - - (void) fprintf(stderr, "%s\n", zfs_get_name(zhp)); - } - -out: - zfs_close(zhp); - return (0); -} - -static int -destroy_callback(zfs_handle_t *zhp, void *data) -{ - destroy_cbdata_t *cb = data; - const char *name = zfs_get_name(zhp); - - if (cb->cb_verbose) { - if (cb->cb_parsable) { - (void) printf("destroy\t%s\n", name); - } else if (cb->cb_dryrun) { - (void) printf(gettext("would destroy %s\n"), - name); - } else { - (void) printf(gettext("will destroy %s\n"), - name); - } - } - - /* - * Ignore pools (which we've already flagged as an error before getting - * here). - */ - if (strchr(zfs_get_name(zhp), '/') == NULL && - zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { - zfs_close(zhp); - return (0); - } - if (cb->cb_dryrun) { - zfs_close(zhp); - return (0); - } - - /* - * We batch up all contiguous snapshots (even of different - * filesystems) and destroy them with one ioctl. We can't - * simply do all snap deletions and then all fs deletions, - * because we must delete a clone before its origin. - */ - if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { - fnvlist_add_boolean(cb->cb_batchedsnaps, name); - } else { - int error = zfs_destroy_snaps_nvl(g_zfs, - cb->cb_batchedsnaps, B_FALSE); - fnvlist_free(cb->cb_batchedsnaps); - cb->cb_batchedsnaps = fnvlist_alloc(); - - if (error != 0 || - zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 || - zfs_destroy(zhp, cb->cb_defer_destroy) != 0) { - zfs_close(zhp); - return (-1); - } - } - - zfs_close(zhp); - return (0); -} - -static int -destroy_print_cb(zfs_handle_t *zhp, void *arg) -{ - destroy_cbdata_t *cb = arg; - const char *name = zfs_get_name(zhp); - int err = 0; - - if (nvlist_exists(cb->cb_nvl, name)) { - if (cb->cb_firstsnap == NULL) - cb->cb_firstsnap = strdup(name); - if (cb->cb_prevsnap != NULL) - free(cb->cb_prevsnap); - /* this snap continues the current range */ - cb->cb_prevsnap = strdup(name); - if (cb->cb_firstsnap == NULL || cb->cb_prevsnap == NULL) - nomem(); - if (cb->cb_verbose) { - if (cb->cb_parsable) { - (void) printf("destroy\t%s\n", name); - } else if (cb->cb_dryrun) { - (void) printf(gettext("would destroy %s\n"), - name); - } else { - (void) printf(gettext("will destroy %s\n"), - name); - } - } - } else if (cb->cb_firstsnap != NULL) { - /* end of this range */ - uint64_t used = 0; - err = lzc_snaprange_space(cb->cb_firstsnap, - cb->cb_prevsnap, &used); - cb->cb_snapused += used; - free(cb->cb_firstsnap); - cb->cb_firstsnap = NULL; - free(cb->cb_prevsnap); - cb->cb_prevsnap = NULL; - } - zfs_close(zhp); - return (err); -} - -static int -destroy_print_snapshots(zfs_handle_t *fs_zhp, destroy_cbdata_t *cb) -{ - int err = 0; - assert(cb->cb_firstsnap == NULL); - assert(cb->cb_prevsnap == NULL); - err = zfs_iter_snapshots_sorted(fs_zhp, destroy_print_cb, cb, 0, 0); - if (cb->cb_firstsnap != NULL) { - uint64_t used = 0; - if (err == 0) { - err = lzc_snaprange_space(cb->cb_firstsnap, - cb->cb_prevsnap, &used); - } - cb->cb_snapused += used; - free(cb->cb_firstsnap); - cb->cb_firstsnap = NULL; - free(cb->cb_prevsnap); - cb->cb_prevsnap = NULL; - } - return (err); -} - -static int -snapshot_to_nvl_cb(zfs_handle_t *zhp, void *arg) -{ - destroy_cbdata_t *cb = arg; - int err = 0; - - /* Check for clones. */ - if (!cb->cb_doclones && !cb->cb_defer_destroy) { - cb->cb_target = zhp; - cb->cb_first = B_TRUE; - err = zfs_iter_dependents(zhp, B_TRUE, - destroy_check_dependent, cb); - } - - if (err == 0) { - if (nvlist_add_boolean(cb->cb_nvl, zfs_get_name(zhp))) - nomem(); - } - zfs_close(zhp); - return (err); -} - -static int -gather_snapshots(zfs_handle_t *zhp, void *arg) -{ - destroy_cbdata_t *cb = arg; - int err = 0; - - err = zfs_iter_snapspec(zhp, cb->cb_snapspec, snapshot_to_nvl_cb, cb); - if (err == ENOENT) - err = 0; - if (err != 0) - goto out; - - if (cb->cb_verbose) { - err = destroy_print_snapshots(zhp, cb); - if (err != 0) - goto out; - } - - if (cb->cb_recurse) - err = zfs_iter_filesystems(zhp, gather_snapshots, cb); - -out: - zfs_close(zhp); - return (err); -} - -static int -destroy_clones(destroy_cbdata_t *cb) -{ - nvpair_t *pair; - for (pair = nvlist_next_nvpair(cb->cb_nvl, NULL); - pair != NULL; - pair = nvlist_next_nvpair(cb->cb_nvl, pair)) { - zfs_handle_t *zhp = zfs_open(g_zfs, nvpair_name(pair), - ZFS_TYPE_SNAPSHOT); - if (zhp != NULL) { - boolean_t defer = cb->cb_defer_destroy; - int err = 0; - - /* - * We can't defer destroy non-snapshots, so set it to - * false while destroying the clones. - */ - cb->cb_defer_destroy = B_FALSE; - err = zfs_iter_dependents(zhp, B_FALSE, - destroy_callback, cb); - cb->cb_defer_destroy = defer; - zfs_close(zhp); - if (err != 0) - return (err); - } - } - return (0); -} - -static int -zfs_do_destroy(int argc, char **argv) -{ - destroy_cbdata_t cb = { 0 }; - int rv = 0; - int err = 0; - int c; - zfs_handle_t *zhp = NULL; - char *at, *pound; - zfs_type_t type = ZFS_TYPE_DATASET; - - /* check options */ - while ((c = getopt(argc, argv, "vpndfrR")) != -1) { - switch (c) { - case 'v': - cb.cb_verbose = B_TRUE; - break; - case 'p': - cb.cb_verbose = B_TRUE; - cb.cb_parsable = B_TRUE; - break; - case 'n': - cb.cb_dryrun = B_TRUE; - break; - case 'd': - cb.cb_defer_destroy = B_TRUE; - type = ZFS_TYPE_SNAPSHOT; - break; - case 'f': - cb.cb_force = B_TRUE; - break; - case 'r': - cb.cb_recurse = B_TRUE; - break; - case 'R': - cb.cb_recurse = B_TRUE; - cb.cb_doclones = B_TRUE; - break; - case '?': - default: - (void) fprintf(stderr, gettext("invalid option '%c'\n"), - optopt); - usage(B_FALSE); - } - } - - argc -= optind; - argv += optind; - - /* check number of arguments */ - if (argc == 0) { - (void) fprintf(stderr, gettext("missing dataset argument\n")); - usage(B_FALSE); - } - if (argc > 1) { - (void) fprintf(stderr, gettext("too many arguments\n")); - usage(B_FALSE); - } - - at = strchr(argv[0], '@'); - pound = strchr(argv[0], '#'); - if (at != NULL) { - - /* Build the list of snaps to destroy in cb_nvl. */ - cb.cb_nvl = fnvlist_alloc(); - - *at = '\0'; - zhp = zfs_open(g_zfs, argv[0], - ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME); - if (zhp == NULL) - return (1); - - cb.cb_snapspec = at + 1; - if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 || - cb.cb_error) { - rv = 1; - goto out; - } - - if (nvlist_empty(cb.cb_nvl)) { - (void) fprintf(stderr, gettext("could not find any " - "snapshots to destroy; check snapshot names.\n")); - rv = 1; - goto out; - } - - if (cb.cb_verbose) { - char buf[16]; - zfs_nicenum(cb.cb_snapused, buf, sizeof (buf)); - if (cb.cb_parsable) { - (void) printf("reclaim\t%llu\n", - cb.cb_snapused); - } else if (cb.cb_dryrun) { - (void) printf(gettext("would reclaim %s\n"), - buf); - } else { - (void) printf(gettext("will reclaim %s\n"), - buf); - } - } - - if (!cb.cb_dryrun) { - if (cb.cb_doclones) { - cb.cb_batchedsnaps = fnvlist_alloc(); - err = destroy_clones(&cb); - if (err == 0) { - err = zfs_destroy_snaps_nvl(g_zfs, - cb.cb_batchedsnaps, B_FALSE); - } - if (err != 0) { - rv = 1; - goto out; - } - } - if (err == 0) { - err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl, - cb.cb_defer_destroy); - } - } - - if (err != 0) - rv = 1; - } else if (pound != NULL) { - int err; - nvlist_t *nvl; - - if (cb.cb_dryrun) { - (void) fprintf(stderr, - "dryrun is not supported with bookmark\n"); - return (-1); - } - - if (cb.cb_defer_destroy) { - (void) fprintf(stderr, - "defer destroy is not supported with bookmark\n"); - return (-1); - } - - if (cb.cb_recurse) { - (void) fprintf(stderr, - "recursive is not supported with bookmark\n"); - return (-1); - } - - if (!zfs_bookmark_exists(argv[0])) { - (void) fprintf(stderr, gettext("bookmark '%s' " - "does not exist.\n"), argv[0]); - return (1); - } - - nvl = fnvlist_alloc(); - fnvlist_add_boolean(nvl, argv[0]); - - err = lzc_destroy_bookmarks(nvl, NULL); - if (err != 0) { - (void) zfs_standard_error(g_zfs, err, - "cannot destroy bookmark"); - } - - nvlist_free(cb.cb_nvl); - - return (err); - } else { - /* Open the given dataset */ - if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL) - return (1); - - cb.cb_target = zhp; - - /* - * Perform an explicit check for pools before going any further. - */ - if (!cb.cb_recurse && strchr(zfs_get_name(zhp), '/') == NULL && - zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { - (void) fprintf(stderr, gettext("cannot destroy '%s': " - "operation does not apply to pools\n"), - zfs_get_name(zhp)); - (void) fprintf(stderr, gettext("use 'zfs destroy -r " - "%s' to destroy all datasets in the pool\n"), - zfs_get_name(zhp)); - (void) fprintf(stderr, gettext("use 'zpool destroy %s' " - "to destroy the pool itself\n"), zfs_get_name(zhp)); - rv = 1; - goto out; - } - - /* - * Check for any dependents and/or clones. - */ - cb.cb_first = B_TRUE; - if (!cb.cb_doclones && - zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent, - &cb) != 0) { - rv = 1; - goto out; - } - - if (cb.cb_error) { - rv = 1; - goto out; - } - - cb.cb_batchedsnaps = fnvlist_alloc(); - if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback, - &cb) != 0) { - rv = 1; - goto out; - } - - /* - * Do the real thing. The callback will close the - * handle regardless of whether it succeeds or not. - */ - err = destroy_callback(zhp, &cb); - zhp = NULL; - if (err == 0) { - err = zfs_destroy_snaps_nvl(g_zfs, - cb.cb_batchedsnaps, cb.cb_defer_destroy); - } - if (err != 0) - rv = 1; - } - -out: - fnvlist_free(cb.cb_batchedsnaps); - fnvlist_free(cb.cb_nvl); - if (zhp != NULL) - zfs_close(zhp); - return (rv); -} - -static boolean_t -is_recvd_column(zprop_get_cbdata_t *cbp) -{ - int i; - zfs_get_column_t col; - - for (i = 0; i < ZFS_GET_NCOLS && - (col = cbp->cb_columns[i]) != GET_COL_NONE; i++) - if (col == GET_COL_RECVD) - return (B_TRUE); - return (B_FALSE); -} - -/* - * zfs get [-rHp] [-o all | field[,field]...] [-s source[,source]...] - * < all | property[,property]... > < fs | snap | vol > ... - * - * -r recurse over any child datasets - * -H scripted mode. Headers are stripped, and fields are separated - * by tabs instead of spaces. - * -o Set of fields to display. One of "name,property,value, - * received,source". Default is "name,property,value,source". - * "all" is an alias for all five. - * -s Set of sources to allow. One of - * "local,default,inherited,received,temporary,none". Default is - * all six. - * -p Display values in parsable (literal) format. - * - * Prints properties for the given datasets. The user can control which - * columns to display as well as which property types to allow. - */ - -/* - * Invoked to display the properties for a single dataset. - */ -static int -get_callback(zfs_handle_t *zhp, void *data) -{ - char buf[ZFS_MAXPROPLEN]; - char rbuf[ZFS_MAXPROPLEN]; - zprop_source_t sourcetype; - char source[ZFS_MAX_DATASET_NAME_LEN]; - zprop_get_cbdata_t *cbp = data; - nvlist_t *user_props = zfs_get_user_props(zhp); - zprop_list_t *pl = cbp->cb_proplist; - nvlist_t *propval; - char *strval; - char *sourceval; - boolean_t received = is_recvd_column(cbp); - - for (; pl != NULL; pl = pl->pl_next) { - char *recvdval = NULL; - /* - * Skip the special fake placeholder. This will also skip over - * the name property when 'all' is specified. - */ - if (pl->pl_prop == ZFS_PROP_NAME && - pl == cbp->cb_proplist) - continue; - - if (pl->pl_prop != ZPROP_INVAL) { - if (zfs_prop_get(zhp, pl->pl_prop, buf, - sizeof (buf), &sourcetype, source, - sizeof (source), - cbp->cb_literal) != 0) { - if (pl->pl_all) - continue; - if (!zfs_prop_valid_for_type(pl->pl_prop, - ZFS_TYPE_DATASET)) { - (void) fprintf(stderr, - gettext("No such property '%s'\n"), - zfs_prop_to_name(pl->pl_prop)); - continue; - } - sourcetype = ZPROP_SRC_NONE; - (void) strlcpy(buf, "-", sizeof (buf)); - } - - if (received && (zfs_prop_get_recvd(zhp, - zfs_prop_to_name(pl->pl_prop), rbuf, sizeof (rbuf), - cbp->cb_literal) == 0)) - recvdval = rbuf; - - zprop_print_one_property(zfs_get_name(zhp), cbp, - zfs_prop_to_name(pl->pl_prop), - buf, sourcetype, source, recvdval); - } else if (zfs_prop_userquota(pl->pl_user_prop)) { - sourcetype = ZPROP_SRC_LOCAL; - - if (zfs_prop_get_userquota(zhp, pl->pl_user_prop, - buf, sizeof (buf), cbp->cb_literal) != 0) { - sourcetype = ZPROP_SRC_NONE; - (void) strlcpy(buf, "-", sizeof (buf)); - } - - zprop_print_one_property(zfs_get_name(zhp), cbp, - pl->pl_user_prop, buf, sourcetype, source, NULL); - } else if (zfs_prop_written(pl->pl_user_prop)) { - sourcetype = ZPROP_SRC_LOCAL; - - if (zfs_prop_get_written(zhp, pl->pl_user_prop, - buf, sizeof (buf), cbp->cb_literal) != 0) { - sourcetype = ZPROP_SRC_NONE; - (void) strlcpy(buf, "-", sizeof (buf)); - } - - zprop_print_one_property(zfs_get_name(zhp), cbp, - pl->pl_user_prop, buf, sourcetype, source, NULL); - } else { - if (nvlist_lookup_nvlist(user_props, - pl->pl_user_prop, &propval) != 0) { - if (pl->pl_all) - continue; - sourcetype = ZPROP_SRC_NONE; - strval = "-"; - } else { - verify(nvlist_lookup_string(propval, - ZPROP_VALUE, &strval) == 0); - verify(nvlist_lookup_string(propval, - ZPROP_SOURCE, &sourceval) == 0); - - if (strcmp(sourceval, - zfs_get_name(zhp)) == 0) { - sourcetype = ZPROP_SRC_LOCAL; - } else if (strcmp(sourceval, - ZPROP_SOURCE_VAL_RECVD) == 0) { - sourcetype = ZPROP_SRC_RECEIVED; - } else { - sourcetype = ZPROP_SRC_INHERITED; - (void) strlcpy(source, - sourceval, sizeof (source)); - } - } - - if (received && (zfs_prop_get_recvd(zhp, - pl->pl_user_prop, rbuf, sizeof (rbuf), - cbp->cb_literal) == 0)) - recvdval = rbuf; - - zprop_print_one_property(zfs_get_name(zhp), cbp, - pl->pl_user_prop, strval, sourcetype, - source, recvdval); - } - } - - return (0); -} - -static int -zfs_do_get(int argc, char **argv) -{ - zprop_get_cbdata_t cb = { 0 }; - int i, c, flags = ZFS_ITER_ARGS_CAN_BE_PATHS; - int types = ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK; - char *value, *fields; - int ret = 0; - int limit = 0; - zprop_list_t fake_name = { 0 }; - - /* - * Set up default columns and sources. - */ - cb.cb_sources = ZPROP_SRC_ALL; - cb.cb_columns[0] = GET_COL_NAME; - cb.cb_columns[1] = GET_COL_PROPERTY; - cb.cb_columns[2] = GET_COL_VALUE; - cb.cb_columns[3] = GET_COL_SOURCE; - cb.cb_type = ZFS_TYPE_DATASET; - - /* check options */ - while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) { - switch (c) { - case 'p': - cb.cb_literal = B_TRUE; - break; - case 'd': - limit = parse_depth(optarg, &flags); - break; - case 'r': - flags |= ZFS_ITER_RECURSE; - break; - case 'H': - cb.cb_scripted = B_TRUE; - break; - case ':': - (void) fprintf(stderr, gettext("missing argument for " - "'%c' option\n"), optopt); - usage(B_FALSE); - break; - case 'o': - /* - * Process the set of columns to display. We zero out - * the structure to give us a blank slate. - */ - bzero(&cb.cb_columns, sizeof (cb.cb_columns)); - i = 0; - while (*optarg != '\0') { - static char *col_subopts[] = - { "name", "property", "value", "received", - "source", "all", NULL }; - - if (i == ZFS_GET_NCOLS) { - (void) fprintf(stderr, gettext("too " - "many fields given to -o " - "option\n")); - usage(B_FALSE); - } - - switch (getsubopt(&optarg, col_subopts, - &value)) { - case 0: - cb.cb_columns[i++] = GET_COL_NAME; - break; - case 1: - cb.cb_columns[i++] = GET_COL_PROPERTY; - break; - case 2: - cb.cb_columns[i++] = GET_COL_VALUE; - break; - case 3: - cb.cb_columns[i++] = GET_COL_RECVD; - flags |= ZFS_ITER_RECVD_PROPS; - break; - case 4: - cb.cb_columns[i++] = GET_COL_SOURCE; - break; - case 5: - if (i > 0) { - (void) fprintf(stderr, - gettext("\"all\" conflicts " - "with specific fields " - "given to -o option\n")); - usage(B_FALSE); - } - cb.cb_columns[0] = GET_COL_NAME; - cb.cb_columns[1] = GET_COL_PROPERTY; - cb.cb_columns[2] = GET_COL_VALUE; - cb.cb_columns[3] = GET_COL_RECVD; - cb.cb_columns[4] = GET_COL_SOURCE; - flags |= ZFS_ITER_RECVD_PROPS; - i = ZFS_GET_NCOLS; - break; - default: - (void) fprintf(stderr, - gettext("invalid column name " - "'%s'\n"), suboptarg); - usage(B_FALSE); - } - } - break; - - case 's': - cb.cb_sources = 0; - while (*optarg != '\0') { - static char *source_subopts[] = { - "local", "default", "inherited", - "received", "temporary", "none", - NULL }; - - switch (getsubopt(&optarg, source_subopts, - &value)) { - case 0: - cb.cb_sources |= ZPROP_SRC_LOCAL; - break; - case 1: - cb.cb_sources |= ZPROP_SRC_DEFAULT; - break; - case 2: - cb.cb_sources |= ZPROP_SRC_INHERITED; - break; - case 3: - cb.cb_sources |= ZPROP_SRC_RECEIVED; - break; - case 4: - cb.cb_sources |= ZPROP_SRC_TEMPORARY; - break; - case 5: - cb.cb_sources |= ZPROP_SRC_NONE; - break; - default: - (void) fprintf(stderr, - gettext("invalid source " - "'%s'\n"), suboptarg); - usage(B_FALSE); - } - } - break; - - case 't': - types = 0; - flags &= ~ZFS_ITER_PROP_LISTSNAPS; - while (*optarg != '\0') { - static char *type_subopts[] = { "filesystem", - "volume", "snapshot", "bookmark", - "all", NULL }; - - switch (getsubopt(&optarg, type_subopts, - &value)) { - case 0: - types |= ZFS_TYPE_FILESYSTEM; - break; - case 1: - types |= ZFS_TYPE_VOLUME; - break; - case 2: - types |= ZFS_TYPE_SNAPSHOT; - break; - case 3: - types |= ZFS_TYPE_BOOKMARK; - break; - case 4: - types = ZFS_TYPE_DATASET | - ZFS_TYPE_BOOKMARK; - break; - - default: - (void) fprintf(stderr, - gettext("invalid type '%s'\n"), - suboptarg); - usage(B_FALSE); - } - } - break; - - case '?': - (void) fprintf(stderr, gettext("invalid option '%c'\n"), - optopt); - usage(B_FALSE); - } - } - - argc -= optind; - argv += optind; - - if (argc < 1) { - (void) fprintf(stderr, gettext("missing property " - "argument\n")); - usage(B_FALSE); - } - - fields = argv[0]; - - if (zprop_get_list(g_zfs, fields, &cb.cb_proplist, ZFS_TYPE_DATASET) - != 0) - usage(B_FALSE); - - argc--; - argv++; - - /* - * As part of zfs_expand_proplist(), we keep track of the maximum column - * width for each property. For the 'NAME' (and 'SOURCE') columns, we - * need to know the maximum name length. However, the user likely did - * not specify 'name' as one of the properties to fetch, so we need to - * make sure we always include at least this property for - * print_get_headers() to work properly. - */ - if (cb.cb_proplist != NULL) { - fake_name.pl_prop = ZFS_PROP_NAME; - fake_name.pl_width = strlen(gettext("NAME")); - fake_name.pl_next = cb.cb_proplist; - cb.cb_proplist = &fake_name; - } - - cb.cb_first = B_TRUE; - - /* run for each object */ - ret = zfs_for_each(argc, argv, flags, types, NULL, - &cb.cb_proplist, limit, get_callback, &cb); - - if (cb.cb_proplist == &fake_name) - zprop_free_list(fake_name.pl_next); - else - zprop_free_list(cb.cb_proplist); - - return (ret); -} - -/* - * inherit [-rS] <property> <fs|vol> ... - * - * -r Recurse over all children - * -S Revert to received value, if any - * - * For each dataset specified on the command line, inherit the given property - * from its parent. Inheriting a property at the pool level will cause it to - * use the default value. The '-r' flag will recurse over all children, and is - * useful for setting a property on a hierarchy-wide basis, regardless of any - * local modifications for each dataset. - */ - -typedef struct inherit_cbdata { - const char *cb_propname; - boolean_t cb_received; -} inherit_cbdata_t; - -static int -inherit_recurse_cb(zfs_handle_t *zhp, void *data) -{ - inherit_cbdata_t *cb = data; - zfs_prop_t prop = zfs_name_to_prop(cb->cb_propname); - - /* - * If we're doing it recursively, then ignore properties that - * are not valid for this type of dataset. - */ - if (prop != ZPROP_INVAL && - !zfs_prop_valid_for_type(prop, zfs_get_type(zhp))) - return (0); - - return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0); -} - -static int -inherit_cb(zfs_handle_t *zhp, void *data) -{ - inherit_cbdata_t *cb = data; - - return (zfs_prop_inherit(zhp, cb->cb_propname, cb->cb_received) != 0); -} - -static int -zfs_do_inherit(int argc, char **argv) -{ - int c; - zfs_prop_t prop; - inherit_cbdata_t cb = { 0 }; - char *propname; - int ret = 0; - int flags = 0; - boolean_t received = B_FALSE; - - /* check options */ - while ((c = getopt(argc, argv, "rS")) != -1) { - switch (c) { - case 'r': - flags |= ZFS_ITER_RECURSE; - break; - case 'S': - received = B_TRUE; - break; - case '?': - default: - (void) fprintf(stderr, gettext("invalid option '%c'\n"), - optopt); - usage(B_FALSE); - } - } - - argc -= optind; - argv += optind; - - /* check number of arguments */ - if (argc < 1) { - (void) fprintf(stderr, gettext("missing property argument\n")); - usage(B_FALSE); - } - if (argc < 2) { - (void) fprintf(stderr, gettext("missing dataset argument\n")); - usage(B_FALSE); - } - - propname = argv[0]; - argc--; - argv++; - - if ((prop = zfs_name_to_prop(propname)) != ZPROP_INVAL) { - if (zfs_prop_readonly(prop)) { - (void) fprintf(stderr, gettext( - "%s property is read-only\n"), - propname); - return (1); - } - if (!zfs_prop_inheritable(prop) && !received) { - (void) fprintf(stderr, gettext("'%s' property cannot " - "be inherited\n"), propname); - if (prop == ZFS_PROP_QUOTA || - prop == ZFS_PROP_RESERVATION || - prop == ZFS_PROP_REFQUOTA || - prop == ZFS_PROP_REFRESERVATION) { - (void) fprintf(stderr, gettext("use 'zfs set " - "%s=none' to clear\n"), propname); - (void) fprintf(stderr, gettext("use 'zfs " - "inherit -S %s' to revert to received " - "value\n"), propname); - } - return (1); - } - if (received && (prop == ZFS_PROP_VOLSIZE || - prop == ZFS_PROP_VERSION)) { - (void) fprintf(stderr, gettext("'%s' property cannot " - "be reverted to a received value\n"), propname); - return (1); - } - } else if (!zfs_prop_user(propname)) { - (void) fprintf(stderr, gettext("invalid property '%s'\n"), - propname); - usage(B_FALSE); - } - - cb.cb_propname = propname; - cb.cb_received = received; - - if (flags & ZFS_ITER_RECURSE) { - ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET, - NULL, NULL, 0, inherit_recurse_cb, &cb); - } else { - ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_DATASET, - NULL, NULL, 0, inherit_cb, &cb); - } - - return (ret); -} - -typedef struct upgrade_cbdata { - uint64_t cb_numupgraded; - uint64_t cb_numsamegraded; - uint64_t cb_numfailed; - uint64_t cb_version; - boolean_t cb_newer; - boolean_t cb_foundone; - char cb_lastfs[ZFS_MAX_DATASET_NAME_LEN]; -} upgrade_cbdata_t; - -static int -same_pool(zfs_handle_t *zhp, const char *name) -{ - int len1 = strcspn(name, "/@"); - const char *zhname = zfs_get_name(zhp); - int len2 = strcspn(zhname, "/@"); - - if (len1 != len2) - return (B_FALSE); - return (strncmp(name, zhname, len1) == 0); -} - -static int -upgrade_list_callback(zfs_handle_t *zhp, void *data) -{ - upgrade_cbdata_t *cb = data; - int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); - - /* list if it's old/new */ - if ((!cb->cb_newer && version < ZPL_VERSION) || - (cb->cb_newer && version > ZPL_VERSION)) { - char *str; - if (cb->cb_newer) { - str = gettext("The following filesystems are " - "formatted using a newer software version and\n" - "cannot be accessed on the current system.\n\n"); - } else { - str = gettext("The following filesystems are " - "out of date, and can be upgraded. After being\n" - "upgraded, these filesystems (and any 'zfs send' " - "streams generated from\n" - "subsequent snapshots) will no longer be " - "accessible by older software versions.\n\n"); - } - - if (!cb->cb_foundone) { - (void) puts(str); - (void) printf(gettext("VER FILESYSTEM\n")); - (void) printf(gettext("--- ------------\n")); - cb->cb_foundone = B_TRUE; - } - - (void) printf("%2u %s\n", version, zfs_get_name(zhp)); - } - - return (0); -} - -static int -upgrade_set_callback(zfs_handle_t *zhp, void *data) -{ - upgrade_cbdata_t *cb = data; - int version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION); - int needed_spa_version; - int spa_version; - - if (zfs_spa_version(zhp, &spa_version) < 0) - return (-1); - - needed_spa_version = zfs_spa_version_map(cb->cb_version); - - if (needed_spa_version < 0) - return (-1); - - if (spa_version < needed_spa_version) { - /* can't upgrade */ - (void) printf(gettext("%s: can not be " - "upgraded; the pool version needs to first " - "be upgraded\nto version %d\n\n"), - zfs_get_name(zhp), needed_spa_version); - cb->cb_numfailed++; - return (0); - } - - /* upgrade */ - if (version < cb->cb_version) { - char verstr[16]; - (void) snprintf(verstr, sizeof (verstr), - "%llu", cb->cb_version); - if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) { - /* - * If they did "zfs upgrade -a", then we could - * be doing ioctls to different pools. We need - * to log this history once to each pool, and bypass - * the normal history logging that happens in main(). - */ - (void) zpool_log_history(g_zfs, history_str); - log_history = B_FALSE; - } - if (zfs_prop_set(zhp, "version", verstr) == 0) - cb->cb_numupgraded++; - else - cb->cb_numfailed++; - (void) strcpy(cb->cb_lastfs, zfs_get_name(zhp)); - } else if (version > cb->cb_version) { - /* can't downgrade */ - (void) printf(gettext("%s: can not be downgraded; " - "it is already at version %u\n"), - zfs_get_name(zhp), version); - cb->cb_numfailed++; - } else { - cb->cb_numsamegraded++; - } - return (0); -} - -/* - * zfs upgrade - * zfs upgrade -v - * zfs upgrade [-r] [-V <version>] <-a | filesystem> - */ -static int -zfs_do_upgrade(int argc, char **argv) -{ - boolean_t all = B_FALSE; - boolean_t showversions = B_FALSE; - int ret = 0; - upgrade_cbdata_t cb = { 0 }; - int c; - int flags = ZFS_ITER_ARGS_CAN_BE_PATHS; - - /* check options */ - while ((c = getopt(argc, argv, "rvV:a")) != -1) { - switch (c) { - case 'r': - flags |= ZFS_ITER_RECURSE; - break; - case 'v': - showversions = B_TRUE; - break; - case 'V': - if (zfs_prop_string_to_index(ZFS_PROP_VERSION, - optarg, &cb.cb_version) != 0) { - (void) fprintf(stderr, - gettext("invalid version %s\n"), optarg); - usage(B_FALSE); - } - break; - case 'a': - all = B_TRUE; - break; - case '?': - default: - (void) fprintf(stderr, gettext("invalid option '%c'\n"), - optopt); - usage(B_FALSE); - } - } - - argc -= optind; - argv += optind; - - if ((!all && !argc) && ((flags & ZFS_ITER_RECURSE) | cb.cb_version)) - usage(B_FALSE); - if (showversions && (flags & ZFS_ITER_RECURSE || all || - cb.cb_version || argc)) - usage(B_FALSE); - if ((all || argc) && (showversions)) - usage(B_FALSE); - if (all && argc) - usage(B_FALSE); - - if (showversions) { - /* Show info on available versions. */ - (void) printf(gettext("The following filesystem versions are " - "supported:\n\n")); - (void) printf(gettext("VER DESCRIPTION\n")); - (void) printf("--- -----------------------------------------" - "---------------\n"); - (void) printf(gettext(" 1 Initial ZFS filesystem version\n")); - (void) printf(gettext(" 2 Enhanced directory entries\n")); - (void) printf(gettext(" 3 Case insensitive and filesystem " - "user identifier (FUID)\n")); - (void) printf(gettext(" 4 userquota, groupquota " - "properties\n")); - (void) printf(gettext(" 5 System attributes\n")); - (void) printf(gettext("\nFor more information on a particular " - "version, including supported releases,\n")); - (void) printf("see the ZFS Administration Guide.\n\n"); - ret = 0; - } else if (argc || all) { - /* Upgrade filesystems */ - if (cb.cb_version == 0) - cb.cb_version = ZPL_VERSION; - ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM, - NULL, NULL, 0, upgrade_set_callback, &cb); - (void) printf(gettext("%llu filesystems upgraded\n"), - cb.cb_numupgraded); - if (cb.cb_numsamegraded) { - (void) printf(gettext("%llu filesystems already at " - "this version\n"), - cb.cb_numsamegraded); - } - if (cb.cb_numfailed != 0) - ret = 1; - } else { - /* List old-version filesystems */ - boolean_t found; - (void) printf(gettext("This system is currently running " - "ZFS filesystem version %llu.\n\n"), ZPL_VERSION); - - flags |= ZFS_ITER_RECURSE; - ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM, - NULL, NULL, 0, upgrade_list_callback, &cb); - - found = cb.cb_foundone; - cb.cb_foundone = B_FALSE; - cb.cb_newer = B_TRUE; - - ret = zfs_for_each(0, NULL, flags, ZFS_TYPE_FILESYSTEM, - NULL, NULL, 0, upgrade_list_callback, &cb); - - if (!cb.cb_foundone && !found) { - (void) printf(gettext("All filesystems are " - "formatted with the current version.\n")); - } - } - - return (ret); -} - -/* - * zfs userspace [-Hinp] [-o field[,...]] [-s field [-s field]...] - * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot - * zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...] - * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot - * - * -H Scripted mode; elide headers and separate columns by tabs. - * -i Translate SID to POSIX ID. - * -n Print numeric ID instead of user/group name. - * -o Control which fields to display. - * -p Use exact (parsable) numeric output. - * -s Specify sort columns, descending order. - * -S Specify sort columns, ascending order. - * -t Control which object types to display. - * - * Displays space consumed by, and quotas on, each user in the specified - * filesystem or snapshot. - */ - -/* us_field_types, us_field_hdr and us_field_names should be kept in sync */ -enum us_field_types { - USFIELD_TYPE, - USFIELD_NAME, - USFIELD_USED, - USFIELD_QUOTA -}; -static char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA" }; -static char *us_field_names[] = { "type", "name", "used", "quota" }; -#define USFIELD_LAST (sizeof (us_field_names) / sizeof (char *)) - -#define USTYPE_PSX_GRP (1 << 0) -#define USTYPE_PSX_USR (1 << 1) -#define USTYPE_SMB_GRP (1 << 2) -#define USTYPE_SMB_USR (1 << 3) -#define USTYPE_ALL \ - (USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR) - -static int us_type_bits[] = { - USTYPE_PSX_GRP, - USTYPE_PSX_USR, - USTYPE_SMB_GRP, - USTYPE_SMB_USR, - USTYPE_ALL -}; -static char *us_type_names[] = { "posixgroup", "posixuser", "smbgroup", - "smbuser", "all" }; - -typedef struct us_node { - nvlist_t *usn_nvl; - uu_avl_node_t usn_avlnode; - uu_list_node_t usn_listnode; -} us_node_t; - -typedef struct us_cbdata { - nvlist_t **cb_nvlp; - uu_avl_pool_t *cb_avl_pool; - uu_avl_t *cb_avl; - boolean_t cb_numname; - boolean_t cb_nicenum; - boolean_t cb_sid2posix; - zfs_userquota_prop_t cb_prop; - zfs_sort_column_t *cb_sortcol; - size_t cb_width[USFIELD_LAST]; -} us_cbdata_t; - -static boolean_t us_populated = B_FALSE; - -typedef struct { - zfs_sort_column_t *si_sortcol; - boolean_t si_numname; -} us_sort_info_t; - -static int -us_field_index(char *field) -{ - int i; - - for (i = 0; i < USFIELD_LAST; i++) { - if (strcmp(field, us_field_names[i]) == 0) - return (i); - } - - return (-1); -} - -static int -us_compare(const void *larg, const void *rarg, void *unused) -{ - const us_node_t *l = larg; - const us_node_t *r = rarg; - us_sort_info_t *si = (us_sort_info_t *)unused; - zfs_sort_column_t *sortcol = si->si_sortcol; - boolean_t numname = si->si_numname; - nvlist_t *lnvl = l->usn_nvl; - nvlist_t *rnvl = r->usn_nvl; - int rc = 0; - boolean_t lvb, rvb; - - for (; sortcol != NULL; sortcol = sortcol->sc_next) { - char *lvstr = ""; - char *rvstr = ""; - uint32_t lv32 = 0; - uint32_t rv32 = 0; - uint64_t lv64 = 0; - uint64_t rv64 = 0; - zfs_prop_t prop = sortcol->sc_prop; - const char *propname = NULL; - boolean_t reverse = sortcol->sc_reverse; - - switch (prop) { - case ZFS_PROP_TYPE: - propname = "type"; - (void) nvlist_lookup_uint32(lnvl, propname, &lv32); - (void) nvlist_lookup_uint32(rnvl, propname, &rv32); - if (rv32 != lv32) - rc = (rv32 < lv32) ? 1 : -1; - break; - case ZFS_PROP_NAME: - propname = "name"; - if (numname) { -compare_nums: - (void) nvlist_lookup_uint64(lnvl, propname, - &lv64); - (void) nvlist_lookup_uint64(rnvl, propname, - &rv64); - if (rv64 != lv64) - rc = (rv64 < lv64) ? 1 : -1; - } else { - if ((nvlist_lookup_string(lnvl, propname, - &lvstr) == ENOENT) || - (nvlist_lookup_string(rnvl, propname, - &rvstr) == ENOENT)) { - goto compare_nums; - } - rc = strcmp(lvstr, rvstr); - } - break; - case ZFS_PROP_USED: - case ZFS_PROP_QUOTA: - if (!us_populated) - break; - if (prop == ZFS_PROP_USED) - propname = "used"; - else - propname = "quota"; - (void) nvlist_lookup_uint64(lnvl, propname, &lv64); - (void) nvlist_lookup_uint64(rnvl, propname, &rv64); - if (rv64 != lv64) - rc = (rv64 < lv64) ? 1 : -1; - break; - - default: - break; - } - - if (rc != 0) { - if (rc < 0) - return (reverse ? 1 : -1); - else - return (reverse ? -1 : 1); - } - } - - /* - * If entries still seem to be the same, check if they are of the same - * type (smbentity is added only if we are doing SID to POSIX ID - * translation where we can have duplicate type/name combinations). - */ - if (nvlist_lookup_boolean_value(lnvl, "smbentity", &lvb) == 0 && - nvlist_lookup_boolean_value(rnvl, "smbentity", &rvb) == 0 && - lvb != rvb) - return (lvb < rvb ? -1 : 1); - - return (0); -} - -static inline const char * -us_type2str(unsigned field_type) -{ - switch (field_type) { - case USTYPE_PSX_USR: - return ("POSIX User"); - case USTYPE_PSX_GRP: - return ("POSIX Group"); - case USTYPE_SMB_USR: - return ("SMB User"); - case USTYPE_SMB_GRP: - return ("SMB Group"); - default: - return ("Undefined"); - } -} - -static int -userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space) -{ - us_cbdata_t *cb = (us_cbdata_t *)arg; - zfs_userquota_prop_t prop = cb->cb_prop; - char *name = NULL; - char *propname; - char sizebuf[32]; - us_node_t *node; - uu_avl_pool_t *avl_pool = cb->cb_avl_pool; - uu_avl_t *avl = cb->cb_avl; - uu_avl_index_t idx; - nvlist_t *props; - us_node_t *n; - zfs_sort_column_t *sortcol = cb->cb_sortcol; - unsigned type = 0; - const char *typestr; - size_t namelen; - size_t typelen; - size_t sizelen; - int typeidx, nameidx, sizeidx; - us_sort_info_t sortinfo = { sortcol, cb->cb_numname }; - boolean_t smbentity = B_FALSE; - - if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) - nomem(); - node = safe_malloc(sizeof (us_node_t)); - uu_avl_node_init(node, &node->usn_avlnode, avl_pool); - node->usn_nvl = props; - - if (domain != NULL && domain[0] != '\0') { - /* SMB */ - char sid[MAXNAMELEN + 32]; - uid_t id; -#ifdef illumos - int err; - int flag = IDMAP_REQ_FLG_USE_CACHE; -#endif - - smbentity = B_TRUE; - - (void) snprintf(sid, sizeof (sid), "%s-%u", domain, rid); - - if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) { - type = USTYPE_SMB_GRP; -#ifdef illumos - err = sid_to_id(sid, B_FALSE, &id); -#endif - } else { - type = USTYPE_SMB_USR; -#ifdef illumos - err = sid_to_id(sid, B_TRUE, &id); -#endif - } - -#ifdef illumos - if (err == 0) { - rid = id; - if (!cb->cb_sid2posix) { - if (type == USTYPE_SMB_USR) { - (void) idmap_getwinnamebyuid(rid, flag, - &name, NULL); - } else { - (void) idmap_getwinnamebygid(rid, flag, - &name, NULL); - } - if (name == NULL) - name = sid; - } - } -#endif - } - - if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') { - /* POSIX or -i */ - if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) { - type = USTYPE_PSX_GRP; - if (!cb->cb_numname) { - struct group *g; - - if ((g = getgrgid(rid)) != NULL) - name = g->gr_name; - } - } else { - type = USTYPE_PSX_USR; - if (!cb->cb_numname) { - struct passwd *p; - - if ((p = getpwuid(rid)) != NULL) - name = p->pw_name; - } - } - } - - /* - * Make sure that the type/name combination is unique when doing - * SID to POSIX ID translation (hence changing the type from SMB to - * POSIX). - */ - if (cb->cb_sid2posix && - nvlist_add_boolean_value(props, "smbentity", smbentity) != 0) - nomem(); - - /* Calculate/update width of TYPE field */ - typestr = us_type2str(type); - typelen = strlen(gettext(typestr)); - typeidx = us_field_index("type"); - if (typelen > cb->cb_width[typeidx]) - cb->cb_width[typeidx] = typelen; - if (nvlist_add_uint32(props, "type", type) != 0) - nomem(); - - /* Calculate/update width of NAME field */ - if ((cb->cb_numname && cb->cb_sid2posix) || name == NULL) { - if (nvlist_add_uint64(props, "name", rid) != 0) - nomem(); - namelen = snprintf(NULL, 0, "%u", rid); - } else { - if (nvlist_add_string(props, "name", name) != 0) - nomem(); - namelen = strlen(name); - } - nameidx = us_field_index("name"); - if (namelen > cb->cb_width[nameidx]) - cb->cb_width[nameidx] = namelen; - - /* - * Check if this type/name combination is in the list and update it; - * otherwise add new node to the list. - */ - if ((n = uu_avl_find(avl, node, &sortinfo, &idx)) == NULL) { - uu_avl_insert(avl, node, idx); - } else { - nvlist_free(props); - free(node); - node = n; - props = node->usn_nvl; - } - - /* Calculate/update width of USED/QUOTA fields */ - if (cb->cb_nicenum) - zfs_nicenum(space, sizebuf, sizeof (sizebuf)); - else - (void) snprintf(sizebuf, sizeof (sizebuf), "%llu", space); - sizelen = strlen(sizebuf); - if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED) { - propname = "used"; - if (!nvlist_exists(props, "quota")) - (void) nvlist_add_uint64(props, "quota", 0); - } else { - propname = "quota"; - if (!nvlist_exists(props, "used")) - (void) nvlist_add_uint64(props, "used", 0); - } - sizeidx = us_field_index(propname); - if (sizelen > cb->cb_width[sizeidx]) - cb->cb_width[sizeidx] = sizelen; - - if (nvlist_add_uint64(props, propname, space) != 0) - nomem(); - - return (0); -} - -static void -print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types, - size_t *width, us_node_t *node) -{ - nvlist_t *nvl = node->usn_nvl; - char valstr[MAXNAMELEN]; - boolean_t first = B_TRUE; - int cfield = 0; - int field; - uint32_t ustype; - - /* Check type */ - (void) nvlist_lookup_uint32(nvl, "type", &ustype); - if (!(ustype & types)) - return; - - while ((field = fields[cfield]) != USFIELD_LAST) { - nvpair_t *nvp = NULL; - data_type_t type; - uint32_t val32; - uint64_t val64; - char *strval = NULL; - - while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { - if (strcmp(nvpair_name(nvp), - us_field_names[field]) == 0) - break; - } - - type = nvpair_type(nvp); - switch (type) { - case DATA_TYPE_UINT32: - (void) nvpair_value_uint32(nvp, &val32); - break; - case DATA_TYPE_UINT64: - (void) nvpair_value_uint64(nvp, &val64); - break; - case DATA_TYPE_STRING: - (void) nvpair_value_string(nvp, &strval); - break; - default: - (void) fprintf(stderr, "invalid data type\n"); - } - - switch (field) { - case USFIELD_TYPE: - strval = (char *)us_type2str(val32); - break; - case USFIELD_NAME: - if (type == DATA_TYPE_UINT64) { - (void) sprintf(valstr, "%llu", val64); - strval = valstr; - } - break; - case USFIELD_USED: - case USFIELD_QUOTA: - if (type == DATA_TYPE_UINT64) { - if (parsable) { - (void) sprintf(valstr, "%llu", val64); - } else { - zfs_nicenum(val64, valstr, - sizeof (valstr)); - } - if (field == USFIELD_QUOTA && - strcmp(valstr, "0") == 0) - strval = "none"; - else - strval = valstr; - } - break; - } - - if (!first) { - if (scripted) - (void) printf("\t"); - else - (void) printf(" "); - } - if (scripted) - (void) printf("%s", strval); - else if (field == USFIELD_TYPE || field == USFIELD_NAME) - (void) printf("%-*s", width[field], strval); - else - (void) printf("%*s", width[field], strval); - - first = B_FALSE; - cfield++; - } - - (void) printf("\n"); -} - -static void -print_us(boolean_t scripted, boolean_t parsable, int *fields, int types, - size_t *width, boolean_t rmnode, uu_avl_t *avl) -{ - us_node_t *node; - const char *col; - int cfield = 0; - int field; - - if (!scripted) { - boolean_t first = B_TRUE; - - while ((field = fields[cfield]) != USFIELD_LAST) { - col = gettext(us_field_hdr[field]); - if (field == USFIELD_TYPE || field == USFIELD_NAME) { - (void) printf(first ? "%-*s" : " %-*s", - width[field], col); - } else { - (void) printf(first ? "%*s" : " %*s", - width[field], col); - } - first = B_FALSE; - cfield++; - } - (void) printf("\n"); - } - - for (node = uu_avl_first(avl); node; node = uu_avl_next(avl, node)) { - print_us_node(scripted, parsable, fields, types, width, node); - if (rmnode) - nvlist_free(node->usn_nvl); - } -} - -static int -zfs_do_userspace(int argc, char **argv) -{ - zfs_handle_t *zhp; - zfs_userquota_prop_t p; - - uu_avl_pool_t *avl_pool; - uu_avl_t *avl_tree; - uu_avl_walk_t *walk; - char *delim; - char deffields[] = "type,name,used,quota"; - char *ofield = NULL; - char *tfield = NULL; - int cfield = 0; - int fields[256]; - int i; - boolean_t scripted = B_FALSE; - boolean_t prtnum = B_FALSE; - boolean_t parsable = B_FALSE; - boolean_t sid2posix = B_FALSE; - int ret = 0; - int c; - zfs_sort_column_t *sortcol = NULL; - int types = USTYPE_PSX_USR | USTYPE_SMB_USR; - us_cbdata_t cb; - us_node_t *node; - us_node_t *rmnode; - uu_list_pool_t *listpool; - uu_list_t *list; - uu_avl_index_t idx = 0; - uu_list_index_t idx2 = 0; - - if (argc < 2) - usage(B_FALSE); - - if (strcmp(argv[0], "groupspace") == 0) - /* Toggle default group types */ - types = USTYPE_PSX_GRP | USTYPE_SMB_GRP; - - while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) { - switch (c) { - case 'n': - prtnum = B_TRUE; - break; - case 'H': - scripted = B_TRUE; - break; - case 'p': - parsable = B_TRUE; - break; - case 'o': - ofield = optarg; - break; - case 's': - case 'S': - if (zfs_add_sort_column(&sortcol, optarg, - c == 's' ? B_FALSE : B_TRUE) != 0) { - (void) fprintf(stderr, - gettext("invalid field '%s'\n"), optarg); - usage(B_FALSE); - } - break; - case 't': - tfield = optarg; - break; - case 'i': - sid2posix = B_TRUE; - break; - case ':': - (void) fprintf(stderr, gettext("missing argument for " - "'%c' option\n"), optopt); - usage(B_FALSE); - break; - case '?': - (void) fprintf(stderr, gettext("invalid option '%c'\n"), - optopt); - usage(B_FALSE); - } - } - - argc -= optind; - argv += optind; - - if (argc < 1) { - (void) fprintf(stderr, gettext("missing dataset name\n")); - usage(B_FALSE); - } - if (argc > 1) { - (void) fprintf(stderr, gettext("too many arguments\n")); - usage(B_FALSE); - } - - /* Use default output fields if not specified using -o */ - if (ofield == NULL) - ofield = deffields; - do { - if ((delim = strchr(ofield, ',')) != NULL) - *delim = '\0'; - if ((fields[cfield++] = us_field_index(ofield)) == -1) { - (void) fprintf(stderr, gettext("invalid type '%s' " - "for -o option\n"), ofield); - return (-1); - } - if (delim != NULL) - ofield = delim + 1; - } while (delim != NULL); - fields[cfield] = USFIELD_LAST; - - /* Override output types (-t option) */ - if (tfield != NULL) { - types = 0; - - do { - boolean_t found = B_FALSE; - - if ((delim = strchr(tfield, ',')) != NULL) - *delim = '\0'; - for (i = 0; i < sizeof (us_type_bits) / sizeof (int); - i++) { - if (strcmp(tfield, us_type_names[i]) == 0) { - found = B_TRUE; - types |= us_type_bits[i]; - break; - } - } - if (!found) { - (void) fprintf(stderr, gettext("invalid type " - "'%s' for -t option\n"), tfield); - return (-1); - } - if (delim != NULL) - tfield = delim + 1; - } while (delim != NULL); - } - - if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL) - return (1); - - if ((avl_pool = uu_avl_pool_create("us_avl_pool", sizeof (us_node_t), - offsetof(us_node_t, usn_avlnode), us_compare, UU_DEFAULT)) == NULL) - nomem(); - if ((avl_tree = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) - nomem(); - - /* Always add default sorting columns */ - (void) zfs_add_sort_column(&sortcol, "type", B_FALSE); - (void) zfs_add_sort_column(&sortcol, "name", B_FALSE); - - cb.cb_sortcol = sortcol; - cb.cb_numname = prtnum; - cb.cb_nicenum = !parsable; - cb.cb_avl_pool = avl_pool; - cb.cb_avl = avl_tree; - cb.cb_sid2posix = sid2posix; - - for (i = 0; i < USFIELD_LAST; i++) - cb.cb_width[i] = strlen(gettext(us_field_hdr[i])); - - for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) { - if (((p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA) && - !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) || - ((p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) && - !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP)))) - continue; - cb.cb_prop = p; - if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0) - return (ret); - } - - /* Sort the list */ - if ((node = uu_avl_first(avl_tree)) == NULL) - return (0); - - us_populated = B_TRUE; - - listpool = uu_list_pool_create("tmplist", sizeof (us_node_t), - offsetof(us_node_t, usn_listnode), NULL, UU_DEFAULT); - list = uu_list_create(listpool, NULL, UU_DEFAULT); - uu_list_node_init(node, &node->usn_listnode, listpool); - - while (node != NULL) { - rmnode = node; - node = uu_avl_next(avl_tree, node); - uu_avl_remove(avl_tree, rmnode); - if (uu_list_find(list, rmnode, NULL, &idx2) == NULL) - uu_list_insert(list, rmnode, idx2); - } - - for (node = uu_list_first(list); node != NULL; - node = uu_list_next(list, node)) { - us_sort_info_t sortinfo = { sortcol, cb.cb_numname }; - - if (uu_avl_find(avl_tree, node, &sortinfo, &idx) == NULL) - uu_avl_insert(avl_tree, node, idx); - } - - uu_list_destroy(list); - uu_list_pool_destroy(listpool); - - /* Print and free node nvlist memory */ - print_us(scripted, parsable, fields, types, cb.cb_width, B_TRUE, - cb.cb_avl); - - zfs_free_sort_columns(sortcol); - - /* Clean up the AVL tree */ - if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) - nomem(); - - while ((node = uu_avl_walk_next(walk)) != NULL) { - uu_avl_remove(cb.cb_avl, node); - free(node); - } - - uu_avl_walk_end(walk); - uu_avl_destroy(avl_tree); - uu_avl_pool_destroy(avl_pool); - - return (ret); -} - -/* - * list [-Hp][-r|-d max] [-o property[,...]] [-s property] ... [-S property] ... - * [-t type[,...]] [filesystem|volume|snapshot] ... - * - * -H Scripted mode; elide headers and separate columns by tabs. - * -p Display values in parsable (literal) format. - * -r Recurse over all children. - * -d Limit recursion by depth. - * -o Control which fields to display. - * -s Specify sort columns, descending order. - * -S Specify sort columns, ascending order. - * -t Control which object types to display. - * - * When given no arguments, list all filesystems in the system. - * Otherwise, list the specified datasets, optionally recursing down them if - * '-r' is specified. - */ -typedef struct list_cbdata { - boolean_t cb_first; - boolean_t cb_literal; - boolean_t cb_scripted; - zprop_list_t *cb_proplist; -} list_cbdata_t; - -/* - * Given a list of columns to display, output appropriate headers for each one. - */ -static void -print_header(list_cbdata_t *cb) -{ - zprop_list_t *pl = cb->cb_proplist; - char headerbuf[ZFS_MAXPROPLEN]; - const char *header; - int i; - boolean_t first = B_TRUE; - boolean_t right_justify; - - for (; pl != NULL; pl = pl->pl_next) { - if (!first) { - (void) printf(" "); - } else { - first = B_FALSE; - } - - right_justify = B_FALSE; - if (pl->pl_prop != ZPROP_INVAL) { - header = zfs_prop_column_name(pl->pl_prop); - right_justify = zfs_prop_align_right(pl->pl_prop); - } else { - for (i = 0; pl->pl_user_prop[i] != '\0'; i++) - headerbuf[i] = toupper(pl->pl_user_prop[i]); - headerbuf[i] = '\0'; - header = headerbuf; - } - - if (pl->pl_next == NULL && !right_justify) - (void) printf("%s", header); - else if (right_justify) - (void) printf("%*s", pl->pl_width, header); - else - (void) printf("%-*s", pl->pl_width, header); - } - - (void) printf("\n"); -} - -/* - * Given a dataset and a list of fields, print out all the properties according - * to the described layout. - */ -static void -print_dataset(zfs_handle_t *zhp, list_cbdata_t *cb) -{ - zprop_list_t *pl = cb->cb_proplist; - boolean_t first = B_TRUE; - char property[ZFS_MAXPROPLEN]; - nvlist_t *userprops = zfs_get_user_props(zhp); - nvlist_t *propval; - char *propstr; - boolean_t right_justify; - - for (; pl != NULL; pl = pl->pl_next) { - if (!first) { - if (cb->cb_scripted) - (void) printf("\t"); - else - (void) printf(" "); - } else { - first = B_FALSE; - } - - if (pl->pl_prop == ZFS_PROP_NAME) { - (void) strlcpy(property, zfs_get_name(zhp), - sizeof (property)); - propstr = property; - right_justify = zfs_prop_align_right(pl->pl_prop); - } else if (pl->pl_prop != ZPROP_INVAL) { - if (zfs_prop_get(zhp, pl->pl_prop, property, - sizeof (property), NULL, NULL, 0, - cb->cb_literal) != 0) - propstr = "-"; - else - propstr = property; - right_justify = zfs_prop_align_right(pl->pl_prop); - } else if (zfs_prop_userquota(pl->pl_user_prop)) { - if (zfs_prop_get_userquota(zhp, pl->pl_user_prop, - property, sizeof (property), cb->cb_literal) != 0) - propstr = "-"; - else - propstr = property; - right_justify = B_TRUE; - } else if (zfs_prop_written(pl->pl_user_prop)) { - if (zfs_prop_get_written(zhp, pl->pl_user_prop, - property, sizeof (property), cb->cb_literal) != 0) - propstr = "-"; - else - propstr = property; - right_justify = B_TRUE; - } else { - if (nvlist_lookup_nvlist(userprops, - pl->pl_user_prop, &propval) != 0) - propstr = "-"; - else - verify(nvlist_lookup_string(propval, - ZPROP_VALUE, &propstr) == 0); - right_justify = B_FALSE; - } - - /* - * If this is being called in scripted mode, or if this is the - * last column and it is left-justified, don't include a width - * format specifier. - */ - if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify)) - (void) printf("%s", propstr); - else if (right_justify) - (void) printf("%*s", pl->pl_width, propstr); - else - (void) printf("%-*s", pl->pl_width, propstr); - } - - (void) printf("\n"); -} - -/* - * Generic callback function to list a dataset or snapshot. - */ -static int -list_callback(zfs_handle_t *zhp, void *data) -{ - list_cbdata_t *cbp = data; - - if (cbp->cb_first) { - if (!cbp->cb_scripted) - print_header(cbp); |