mirror of
https://git.FreeBSD.org/src.git
synced 2025-02-05 18:05:16 +00:00
Update vendor/illumos/dist to pre libzfs_core state (zfs part)
illumos-gate revision 13742:b6bbdd77139c Obtained from: ssh://anonhg@hg.illumos.org/illumos-gate
This commit is contained in:
parent
cb34095d8e
commit
51ab6c0afd
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/vendor/illumos/dist/; revision=238583
@ -18,8 +18,10 @@
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@ -54,6 +56,7 @@
|
||||
#include <sys/zfs_fuid.h>
|
||||
#include <sys/arc.h>
|
||||
#include <sys/ddt.h>
|
||||
#include <sys/zfeature.h>
|
||||
#undef ZFS_MAXNAMELEN
|
||||
#undef verify
|
||||
#include <libzfs.h>
|
||||
@ -63,7 +66,8 @@
|
||||
#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 : "UNKNOWN")
|
||||
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) : DMU_OT_NUMTYPES)
|
||||
|
||||
#ifndef lint
|
||||
@ -102,13 +106,16 @@ static void
|
||||
usage(void)
|
||||
{
|
||||
(void) fprintf(stderr,
|
||||
"Usage: %s [-CumdibcsDvhL] poolname [object...]\n"
|
||||
" %s [-div] dataset [object...]\n"
|
||||
" %s -m [-L] poolname [vdev [metaslab...]]\n"
|
||||
" %s -R poolname vdev:offset:size[:flags]\n"
|
||||
" %s -S poolname\n"
|
||||
" %s -l [-u] device\n"
|
||||
" %s -C\n\n",
|
||||
"Usage: %s [-CumdibcsDvhLXFPA] [-t txg] [-e [-p path...]] "
|
||||
"poolname [object...]\n"
|
||||
" %s [-divPA] [-e -p path...] dataset [object...]\n"
|
||||
" %s -m [-LXFPA] [-t txg] [-e [-p path...]] "
|
||||
"poolname [vdev [metaslab...]]\n"
|
||||
" %s -R [-A] [-e [-p path...]] poolname "
|
||||
"vdev:offset:size[:flags]\n"
|
||||
" %s -S [-PA] [-e [-p path...]] poolname\n"
|
||||
" %s -l [-uA] device\n"
|
||||
" %s -C [-A] [-U config]\n\n",
|
||||
cmdname, cmdname, cmdname, cmdname, cmdname, cmdname, cmdname);
|
||||
|
||||
(void) fprintf(stderr, " Dataset name must include at least one "
|
||||
@ -150,7 +157,7 @@ usage(void)
|
||||
"has altroot/not in a cachefile\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 parsable\n");
|
||||
(void) fprintf(stderr, " -P print numbers in parseable form\n");
|
||||
(void) fprintf(stderr, " -t <txg> -- highest txg to use when "
|
||||
"searching for uberblocks\n");
|
||||
(void) fprintf(stderr, "Specify an option more than once (e.g. -bb) "
|
||||
@ -1085,7 +1092,7 @@ dump_dsl_dataset(objset_t *os, uint64_t object, void *data, size_t size)
|
||||
|
||||
ASSERT(size == sizeof (*ds));
|
||||
crtime = ds->ds_creation_time;
|
||||
zdb_nicenum(ds->ds_used_bytes, used);
|
||||
zdb_nicenum(ds->ds_referenced_bytes, used);
|
||||
zdb_nicenum(ds->ds_compressed_bytes, compressed);
|
||||
zdb_nicenum(ds->ds_uncompressed_bytes, uncompressed);
|
||||
zdb_nicenum(ds->ds_unique_bytes, unique);
|
||||
@ -1127,6 +1134,44 @@ dump_dsl_dataset(objset_t *os, uint64_t object, void *data, size_t size)
|
||||
(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) {
|
||||
sprintf_blkptr(blkbuf, bp);
|
||||
(void) printf("\t%s\n", blkbuf);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
dump_bptree(objset_t *os, uint64_t obj, char *name)
|
||||
{
|
||||
char bytes[32];
|
||||
bptree_phys_t *bt;
|
||||
dmu_buf_t *db;
|
||||
|
||||
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);
|
||||
(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)
|
||||
@ -1880,11 +1925,13 @@ typedef struct zdb_blkstats {
|
||||
*/
|
||||
#define ZDB_OT_DEFERRED (DMU_OT_NUMTYPES + 0)
|
||||
#define ZDB_OT_DITTO (DMU_OT_NUMTYPES + 1)
|
||||
#define ZDB_OT_TOTAL (DMU_OT_NUMTYPES + 2)
|
||||
#define ZDB_OT_OTHER (DMU_OT_NUMTYPES + 2)
|
||||
#define ZDB_OT_TOTAL (DMU_OT_NUMTYPES + 3)
|
||||
|
||||
static char *zdb_ot_extname[] = {
|
||||
"deferred free",
|
||||
"dedup ditto",
|
||||
"other",
|
||||
"Total",
|
||||
};
|
||||
|
||||
@ -1965,9 +2012,10 @@ zdb_blkptr_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, arc_buf_t *pbuf,
|
||||
|
||||
type = BP_GET_TYPE(bp);
|
||||
|
||||
zdb_count_block(zcb, zilog, bp, type);
|
||||
zdb_count_block(zcb, zilog, bp,
|
||||
(type & DMU_OT_NEWTYPE) ? ZDB_OT_OTHER : type);
|
||||
|
||||
is_metadata = (BP_GET_LEVEL(bp) != 0 || dmu_ot[type].ot_metadata);
|
||||
is_metadata = (BP_GET_LEVEL(bp) != 0 || DMU_OT_IS_METADATA(type));
|
||||
|
||||
if (dump_opt['c'] > 1 || (dump_opt['c'] && is_metadata)) {
|
||||
int ioerr;
|
||||
@ -2192,6 +2240,12 @@ dump_block_stats(spa_t *spa)
|
||||
count_block_cb, &zcb, NULL);
|
||||
(void) bpobj_iterate_nofree(&spa->spa_dsl_pool->dp_free_bpobj,
|
||||
count_block_cb, &zcb, NULL);
|
||||
if (spa_feature_is_active(spa,
|
||||
&spa_feature_table[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;
|
||||
@ -2368,7 +2422,7 @@ zdb_ddt_add_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
|
||||
}
|
||||
|
||||
if (BP_IS_HOLE(bp) || BP_GET_CHECKSUM(bp) == ZIO_CHECKSUM_OFF ||
|
||||
BP_GET_LEVEL(bp) > 0 || dmu_ot[BP_GET_TYPE(bp)].ot_metadata)
|
||||
BP_GET_LEVEL(bp) > 0 || DMU_OT_IS_METADATA(BP_GET_TYPE(bp)))
|
||||
return (0);
|
||||
|
||||
ddt_key_fill(&zdde_search.zdde_key, bp);
|
||||
@ -2473,7 +2527,14 @@ dump_zpool(spa_t *spa)
|
||||
dump_bpobj(&spa->spa_deferred_bpobj, "Deferred frees");
|
||||
if (spa_version(spa) >= SPA_VERSION_DEADLISTS) {
|
||||
dump_bpobj(&spa->spa_dsl_pool->dp_free_bpobj,
|
||||
"Pool frees");
|
||||
"Pool snapshot frees");
|
||||
}
|
||||
|
||||
if (spa_feature_is_active(spa,
|
||||
&spa_feature_table[SPA_FEATURE_ASYNC_DESTROY])) {
|
||||
dump_bptree(spa->spa_meta_objset,
|
||||
spa->spa_dsl_pool->dp_bptree_obj,
|
||||
"Pool dataset frees");
|
||||
}
|
||||
dump_dtl(spa->spa_root_vdev, 0);
|
||||
}
|
||||
|
2818
cmd/zfs/zfs_main.c
2818
cmd/zfs/zfs_main.c
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1154
cmd/ztest/ztest.c
1154
cmd/ztest/ztest.c
File diff suppressed because it is too large
Load Diff
@ -20,6 +20,7 @@
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
@ -803,6 +804,10 @@ dump_nvlist(nvlist_t *list, int indent)
|
||||
|
||||
while ((elem = nvlist_next_nvpair(list, elem)) != NULL) {
|
||||
switch (nvpair_type(elem)) {
|
||||
case DATA_TYPE_BOOLEAN:
|
||||
(void) printf("%*s%s\n", indent, "", nvpair_name(elem));
|
||||
break;
|
||||
|
||||
case DATA_TYPE_BOOLEAN_VALUE:
|
||||
(void) nvpair_value_boolean_value(elem, &bool_value);
|
||||
(void) printf("%*s%s: %s\n", indent, "",
|
||||
|
@ -18,13 +18,14 @@
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*
|
||||
* Copyright 2011 Jason King. All rights reserved.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include "libuutil_common.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
@ -318,6 +319,8 @@ uu_list_find(uu_list_t *lp, void *elem, void *private, uu_list_index_t *out)
|
||||
uu_compare_fn_t *func = lp->ul_pool->ulp_cmp;
|
||||
uu_list_node_impl_t *np;
|
||||
|
||||
uu_set_error(UU_ERROR_NONE);
|
||||
|
||||
if (func == NULL) {
|
||||
if (out != NULL)
|
||||
*out = 0;
|
||||
|
@ -21,6 +21,9 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LIBZFS_H
|
||||
@ -229,6 +232,8 @@ typedef struct splitflags {
|
||||
*/
|
||||
extern int zpool_scan(zpool_handle_t *, pool_scan_func_t);
|
||||
extern int zpool_clear(zpool_handle_t *, const char *, nvlist_t *);
|
||||
extern int zpool_reguid(zpool_handle_t *);
|
||||
extern int zpool_reopen(zpool_handle_t *);
|
||||
|
||||
extern int zpool_vdev_online(zpool_handle_t *, const char *, int,
|
||||
vdev_state_t *);
|
||||
@ -285,6 +290,15 @@ typedef enum {
|
||||
ZPOOL_STATUS_IO_FAILURE_CONTINUE, /* failed I/O, failmode 'continue' */
|
||||
ZPOOL_STATUS_BAD_LOG, /* cannot read log chain(s) */
|
||||
|
||||
/*
|
||||
* If the pool has unsupported features but can still be opened in
|
||||
* read-only mode, its status is ZPOOL_STATUS_UNSUP_FEAT_WRITE. If the
|
||||
* pool has unsupported features but cannot be opened at all, its
|
||||
* status is ZPOOL_STATUS_UNSUP_FEAT_READ.
|
||||
*/
|
||||
ZPOOL_STATUS_UNSUP_FEAT_READ, /* unsupported features for read */
|
||||
ZPOOL_STATUS_UNSUP_FEAT_WRITE, /* unsupported features for write */
|
||||
|
||||
/*
|
||||
* These faults have no corresponding message ID. At the time we are
|
||||
* checking the status, the original reason for the FMA fault (I/O or
|
||||
@ -317,6 +331,7 @@ extern void zpool_dump_ddt(const ddt_stat_t *dds, const ddt_histogram_t *ddh);
|
||||
* Statistics and configuration functions.
|
||||
*/
|
||||
extern nvlist_t *zpool_get_config(zpool_handle_t *, nvlist_t **);
|
||||
extern nvlist_t *zpool_get_features(zpool_handle_t *);
|
||||
extern int zpool_refresh_stats(zpool_handle_t *, boolean_t *);
|
||||
extern int zpool_get_errlog(zpool_handle_t *, nvlist_t **);
|
||||
|
||||
@ -329,6 +344,7 @@ extern int zpool_import(libzfs_handle_t *, nvlist_t *, const char *,
|
||||
char *altroot);
|
||||
extern int zpool_import_props(libzfs_handle_t *, nvlist_t *, const char *,
|
||||
nvlist_t *, int);
|
||||
extern void zpool_print_unsup_feat(nvlist_t *config);
|
||||
|
||||
/*
|
||||
* Search for pools to import
|
||||
@ -380,6 +396,7 @@ extern void zpool_explain_recover(libzfs_handle_t *, const char *, int,
|
||||
* underlying datasets, only the references to them.
|
||||
*/
|
||||
extern zfs_handle_t *zfs_open(libzfs_handle_t *, const char *, int);
|
||||
extern zfs_handle_t *zfs_handle_dup(zfs_handle_t *);
|
||||
extern void zfs_close(zfs_handle_t *);
|
||||
extern zfs_type_t zfs_get_type(const zfs_handle_t *);
|
||||
extern const char *zfs_get_name(const zfs_handle_t *);
|
||||
@ -413,12 +430,22 @@ extern int zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,
|
||||
uint64_t *propvalue);
|
||||
extern int zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
|
||||
char *propbuf, int proplen, boolean_t literal);
|
||||
extern int zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
|
||||
uint64_t *propvalue);
|
||||
extern int zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
|
||||
char *propbuf, int proplen, boolean_t literal);
|
||||
extern int zfs_prop_get_feature(zfs_handle_t *zhp, const char *propname,
|
||||
char *buf, size_t len);
|
||||
extern int zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
|
||||
uint64_t *usedp);
|
||||
extern uint64_t zfs_prop_get_int(zfs_handle_t *, zfs_prop_t);
|
||||
extern int zfs_prop_inherit(zfs_handle_t *, const char *, boolean_t);
|
||||
extern const char *zfs_prop_values(zfs_prop_t);
|
||||
extern int zfs_prop_is_string(zfs_prop_t prop);
|
||||
extern nvlist_t *zfs_get_user_props(zfs_handle_t *);
|
||||
extern nvlist_t *zfs_get_recvd_props(zfs_handle_t *);
|
||||
extern nvlist_t *zfs_get_clones_nvl(zfs_handle_t *);
|
||||
|
||||
|
||||
typedef struct zprop_list {
|
||||
int pl_prop;
|
||||
@ -436,10 +463,19 @@ extern void zfs_prune_proplist(zfs_handle_t *, uint8_t *);
|
||||
#define ZFS_MOUNTPOINT_NONE "none"
|
||||
#define ZFS_MOUNTPOINT_LEGACY "legacy"
|
||||
|
||||
#define ZFS_FEATURE_DISABLED "disabled"
|
||||
#define ZFS_FEATURE_ENABLED "enabled"
|
||||
#define ZFS_FEATURE_ACTIVE "active"
|
||||
|
||||
#define ZFS_UNSUPPORTED_INACTIVE "inactive"
|
||||
#define ZFS_UNSUPPORTED_READONLY "readonly"
|
||||
|
||||
/*
|
||||
* zpool property management
|
||||
*/
|
||||
extern int zpool_expand_proplist(zpool_handle_t *, zprop_list_t **);
|
||||
extern int zpool_prop_get_feature(zpool_handle_t *, const char *, char *,
|
||||
size_t);
|
||||
extern const char *zpool_prop_default_string(zpool_prop_t);
|
||||
extern uint64_t zpool_prop_default_numeric(zpool_prop_t);
|
||||
extern const char *zpool_prop_column_name(zpool_prop_t);
|
||||
@ -493,6 +529,7 @@ extern int zfs_iter_dependents(zfs_handle_t *, boolean_t, zfs_iter_f, void *);
|
||||
extern int zfs_iter_filesystems(zfs_handle_t *, zfs_iter_f, void *);
|
||||
extern int zfs_iter_snapshots(zfs_handle_t *, zfs_iter_f, void *);
|
||||
extern int zfs_iter_snapshots_sorted(zfs_handle_t *, zfs_iter_f, void *);
|
||||
extern int zfs_iter_snapspec(zfs_handle_t *, const char *, zfs_iter_f, void *);
|
||||
|
||||
typedef struct get_all_cb {
|
||||
zfs_handle_t **cb_handles;
|
||||
@ -513,79 +550,92 @@ extern int zfs_create(libzfs_handle_t *, const char *, zfs_type_t,
|
||||
extern int zfs_create_ancestors(libzfs_handle_t *, const char *);
|
||||
extern int zfs_destroy(zfs_handle_t *, boolean_t);
|
||||
extern int zfs_destroy_snaps(zfs_handle_t *, char *, boolean_t);
|
||||
extern int zfs_destroy_snaps_nvl(zfs_handle_t *, nvlist_t *, boolean_t);
|
||||
extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *);
|
||||
extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t, nvlist_t *);
|
||||
extern int zfs_rollback(zfs_handle_t *, zfs_handle_t *, boolean_t);
|
||||
extern int zfs_rename(zfs_handle_t *, const char *, boolean_t);
|
||||
extern int zfs_rename(zfs_handle_t *, const char *, boolean_t, boolean_t);
|
||||
|
||||
typedef struct sendflags {
|
||||
/* print informational messages (ie, -v was specified) */
|
||||
int verbose : 1;
|
||||
boolean_t verbose;
|
||||
|
||||
/* recursive send (ie, -R) */
|
||||
int replicate : 1;
|
||||
boolean_t replicate;
|
||||
|
||||
/* for incrementals, do all intermediate snapshots */
|
||||
int doall : 1; /* (ie, -I) */
|
||||
boolean_t doall;
|
||||
|
||||
/* if dataset is a clone, do incremental from its origin */
|
||||
int fromorigin : 1;
|
||||
boolean_t fromorigin;
|
||||
|
||||
/* do deduplication */
|
||||
int dedup : 1;
|
||||
boolean_t dedup;
|
||||
|
||||
/* send properties (ie, -p) */
|
||||
int props : 1;
|
||||
boolean_t props;
|
||||
|
||||
/* do not send (no-op, ie. -n) */
|
||||
boolean_t dryrun;
|
||||
|
||||
/* parsable verbose output (ie. -P) */
|
||||
boolean_t parsable;
|
||||
|
||||
/* show progress (ie. -v) */
|
||||
boolean_t progress;
|
||||
} sendflags_t;
|
||||
|
||||
typedef boolean_t (snapfilter_cb_t)(zfs_handle_t *, void *);
|
||||
|
||||
extern int zfs_send(zfs_handle_t *zhp, const char *fromsnap, const char *tosnap,
|
||||
sendflags_t flags, int outfd, snapfilter_cb_t filter_func,
|
||||
void *cb_arg, nvlist_t **debugnvp);
|
||||
extern int zfs_send(zfs_handle_t *, const char *, const char *,
|
||||
sendflags_t *, int, snapfilter_cb_t, void *, nvlist_t **);
|
||||
|
||||
extern int zfs_promote(zfs_handle_t *);
|
||||
extern int zfs_hold(zfs_handle_t *, const char *, const char *, boolean_t,
|
||||
boolean_t, boolean_t, int, uint64_t, uint64_t);
|
||||
extern int zfs_release(zfs_handle_t *, const char *, const char *, boolean_t);
|
||||
extern int zfs_get_holds(zfs_handle_t *, nvlist_t **);
|
||||
extern uint64_t zvol_volsize_to_reservation(uint64_t, nvlist_t *);
|
||||
|
||||
typedef int (*zfs_userspace_cb_t)(void *arg, const char *domain,
|
||||
uid_t rid, uint64_t space);
|
||||
|
||||
extern int zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
|
||||
zfs_userspace_cb_t func, void *arg);
|
||||
extern int zfs_userspace(zfs_handle_t *, zfs_userquota_prop_t,
|
||||
zfs_userspace_cb_t, void *);
|
||||
|
||||
extern int zfs_get_fsacl(zfs_handle_t *, nvlist_t **);
|
||||
extern int zfs_set_fsacl(zfs_handle_t *, boolean_t, nvlist_t *);
|
||||
|
||||
typedef struct recvflags {
|
||||
/* print informational messages (ie, -v was specified) */
|
||||
int verbose : 1;
|
||||
boolean_t verbose;
|
||||
|
||||
/* the destination is a prefix, not the exact fs (ie, -d) */
|
||||
int isprefix : 1;
|
||||
boolean_t isprefix;
|
||||
|
||||
/*
|
||||
* Only the tail of the sent snapshot path is appended to the
|
||||
* destination to determine the received snapshot name (ie, -e).
|
||||
*/
|
||||
int istail : 1;
|
||||
boolean_t istail;
|
||||
|
||||
/* do not actually do the recv, just check if it would work (ie, -n) */
|
||||
int dryrun : 1;
|
||||
boolean_t dryrun;
|
||||
|
||||
/* rollback/destroy filesystems as necessary (eg, -F) */
|
||||
int force : 1;
|
||||
boolean_t force;
|
||||
|
||||
/* set "canmount=off" on all modified filesystems */
|
||||
int canmountoff : 1;
|
||||
boolean_t canmountoff;
|
||||
|
||||
/* byteswap flag is used internally; callers need not specify */
|
||||
int byteswap : 1;
|
||||
boolean_t byteswap;
|
||||
|
||||
/* do not mount file systems as they are extracted (private) */
|
||||
int nomount : 1;
|
||||
boolean_t nomount;
|
||||
} recvflags_t;
|
||||
|
||||
extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t,
|
||||
extern int zfs_receive(libzfs_handle_t *, const char *, recvflags_t *,
|
||||
int, avl_tree_t *);
|
||||
|
||||
typedef enum diff_flags {
|
||||
|
@ -18,11 +18,16 @@
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The pool configuration repository is stored in /etc/zfs/zpool.cache as a
|
||||
* single packed nvlist. While it would be nice to just read in this
|
||||
@ -217,6 +222,36 @@ zpool_get_config(zpool_handle_t *zhp, nvlist_t **oldconfig)
|
||||
return (zhp->zpool_config);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves a list of enabled features and their refcounts and caches it in
|
||||
* the pool handle.
|
||||
*/
|
||||
nvlist_t *
|
||||
zpool_get_features(zpool_handle_t *zhp)
|
||||
{
|
||||
nvlist_t *config, *features;
|
||||
|
||||
config = zpool_get_config(zhp, NULL);
|
||||
|
||||
if (config == NULL || !nvlist_exists(config,
|
||||
ZPOOL_CONFIG_FEATURE_STATS)) {
|
||||
int error;
|
||||
boolean_t missing = B_FALSE;
|
||||
|
||||
error = zpool_refresh_stats(zhp, &missing);
|
||||
|
||||
if (error != 0 || missing)
|
||||
return (NULL);
|
||||
|
||||
config = zpool_get_config(zhp, NULL);
|
||||
}
|
||||
|
||||
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_FEATURE_STATS,
|
||||
&features) == 0);
|
||||
|
||||
return (features);
|
||||
}
|
||||
|
||||
/*
|
||||
* Refresh the vdev statistics associated with the given pool. This is used in
|
||||
* iostat to show configuration changes and determine the delta from the last
|
||||
|
@ -21,6 +21,9 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2010 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012 DEY Storage Systems, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
@ -132,6 +135,7 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
|
||||
namecheck_err_t why;
|
||||
char what;
|
||||
|
||||
(void) zfs_prop_get_table();
|
||||
if (dataset_namecheck(path, &why, &what) != 0) {
|
||||
if (hdl != NULL) {
|
||||
switch (why) {
|
||||
@ -493,7 +497,7 @@ make_dataset_handle(libzfs_handle_t *hdl, const char *path)
|
||||
return (zhp);
|
||||
}
|
||||
|
||||
static zfs_handle_t *
|
||||
zfs_handle_t *
|
||||
make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
|
||||
{
|
||||
zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
|
||||
@ -510,6 +514,53 @@ make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
|
||||
return (zhp);
|
||||
}
|
||||
|
||||
zfs_handle_t *
|
||||
zfs_handle_dup(zfs_handle_t *zhp_orig)
|
||||
{
|
||||
zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
|
||||
|
||||
if (zhp == NULL)
|
||||
return (NULL);
|
||||
|
||||
zhp->zfs_hdl = zhp_orig->zfs_hdl;
|
||||
zhp->zpool_hdl = zhp_orig->zpool_hdl;
|
||||
(void) strlcpy(zhp->zfs_name, zhp_orig->zfs_name,
|
||||
sizeof (zhp->zfs_name));
|
||||
zhp->zfs_type = zhp_orig->zfs_type;
|
||||
zhp->zfs_head_type = zhp_orig->zfs_head_type;
|
||||
zhp->zfs_dmustats = zhp_orig->zfs_dmustats;
|
||||
if (zhp_orig->zfs_props != NULL) {
|
||||
if (nvlist_dup(zhp_orig->zfs_props, &zhp->zfs_props, 0) != 0) {
|
||||
(void) no_memory(zhp->zfs_hdl);
|
||||
zfs_close(zhp);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
if (zhp_orig->zfs_user_props != NULL) {
|
||||
if (nvlist_dup(zhp_orig->zfs_user_props,
|
||||
&zhp->zfs_user_props, 0) != 0) {
|
||||
(void) no_memory(zhp->zfs_hdl);
|
||||
zfs_close(zhp);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
if (zhp_orig->zfs_recvd_props != NULL) {
|
||||
if (nvlist_dup(zhp_orig->zfs_recvd_props,
|
||||
&zhp->zfs_recvd_props, 0)) {
|
||||
(void) no_memory(zhp->zfs_hdl);
|
||||
zfs_close(zhp);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
zhp->zfs_mntcheck = zhp_orig->zfs_mntcheck;
|
||||
if (zhp_orig->zfs_mntopts != NULL) {
|
||||
zhp->zfs_mntopts = zfs_strdup(zhp_orig->zfs_hdl,
|
||||
zhp_orig->zfs_mntopts);
|
||||
}
|
||||
zhp->zfs_props_table = zhp_orig->zfs_props_table;
|
||||
return (zhp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Opens the given snapshot, filesystem, or volume. The 'types'
|
||||
* argument is a mask of acceptable types. The function will print an
|
||||
@ -873,6 +924,12 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
|
||||
goto error;
|
||||
}
|
||||
continue;
|
||||
} else if (prop == ZPROP_INVAL && zfs_prop_written(propname)) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"'%s' is readonly"),
|
||||
propname);
|
||||
(void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (prop == ZPROP_INVAL) {
|
||||
@ -1846,8 +1903,6 @@ zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
|
||||
err = zfs_prop_get(zhp, prop, propbuf, proplen,
|
||||
NULL, NULL, 0, literal);
|
||||
zfs_unset_recvd_props_mode(zhp, &cookie);
|
||||
} else if (zfs_prop_userquota(propname)) {
|
||||
return (-1);
|
||||
} else {
|
||||
nvlist_t *propval;
|
||||
char *recvdval;
|
||||
@ -1862,6 +1917,120 @@ zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
|
||||
return (err == 0 ? 0 : -1);
|
||||
}
|
||||
|
||||
static int
|
||||
get_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen)
|
||||
{
|
||||
nvlist_t *value;
|
||||
nvpair_t *pair;
|
||||
|
||||
value = zfs_get_clones_nvl(zhp);
|
||||
if (value == NULL)
|
||||
return (-1);
|
||||
|
||||
propbuf[0] = '\0';
|
||||
for (pair = nvlist_next_nvpair(value, NULL); pair != NULL;
|
||||
pair = nvlist_next_nvpair(value, pair)) {
|
||||
if (propbuf[0] != '\0')
|
||||
(void) strlcat(propbuf, ",", proplen);
|
||||
(void) strlcat(propbuf, nvpair_name(pair), proplen);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct get_clones_arg {
|
||||
uint64_t numclones;
|
||||
nvlist_t *value;
|
||||
const char *origin;
|
||||
char buf[ZFS_MAXNAMELEN];
|
||||
};
|
||||
|
||||
int
|
||||
get_clones_cb(zfs_handle_t *zhp, void *arg)
|
||||
{
|
||||
struct get_clones_arg *gca = arg;
|
||||
|
||||
if (gca->numclones == 0) {
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, gca->buf, sizeof (gca->buf),
|
||||
NULL, NULL, 0, B_TRUE) != 0)
|
||||
goto out;
|
||||
if (strcmp(gca->buf, gca->origin) == 0) {
|
||||
if (nvlist_add_boolean(gca->value, zfs_get_name(zhp)) != 0) {
|
||||
zfs_close(zhp);
|
||||
return (no_memory(zhp->zfs_hdl));
|
||||
}
|
||||
gca->numclones--;
|
||||
}
|
||||
|
||||
out:
|
||||
(void) zfs_iter_children(zhp, get_clones_cb, gca);
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
nvlist_t *
|
||||
zfs_get_clones_nvl(zfs_handle_t *zhp)
|
||||
{
|
||||
nvlist_t *nv, *value;
|
||||
|
||||
if (nvlist_lookup_nvlist(zhp->zfs_props,
|
||||
zfs_prop_to_name(ZFS_PROP_CLONES), &nv) != 0) {
|
||||
struct get_clones_arg gca;
|
||||
|
||||
/*
|
||||
* if this is a snapshot, then the kernel wasn't able
|
||||
* to get the clones. Do it by slowly iterating.
|
||||
*/
|
||||
if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT)
|
||||
return (NULL);
|
||||
if (nvlist_alloc(&nv, NV_UNIQUE_NAME, 0) != 0)
|
||||
return (NULL);
|
||||
if (nvlist_alloc(&value, NV_UNIQUE_NAME, 0) != 0) {
|
||||
nvlist_free(nv);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
gca.numclones = zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES);
|
||||
gca.value = value;
|
||||
gca.origin = zhp->zfs_name;
|
||||
|
||||
if (gca.numclones != 0) {
|
||||
zfs_handle_t *root;
|
||||
char pool[ZFS_MAXNAMELEN];
|
||||
char *cp = pool;
|
||||
|
||||
/* get the pool name */
|
||||
(void) strlcpy(pool, zhp->zfs_name, sizeof (pool));
|
||||
(void) strsep(&cp, "/@");
|
||||
root = zfs_open(zhp->zfs_hdl, pool,
|
||||
ZFS_TYPE_FILESYSTEM);
|
||||
|
||||
(void) get_clones_cb(root, &gca);
|
||||
}
|
||||
|
||||
if (gca.numclones != 0 ||
|
||||
nvlist_add_nvlist(nv, ZPROP_VALUE, value) != 0 ||
|
||||
nvlist_add_nvlist(zhp->zfs_props,
|
||||
zfs_prop_to_name(ZFS_PROP_CLONES), nv) != 0) {
|
||||
nvlist_free(nv);
|
||||
nvlist_free(value);
|
||||
return (NULL);
|
||||
}
|
||||
nvlist_free(nv);
|
||||
nvlist_free(value);
|
||||
verify(0 == nvlist_lookup_nvlist(zhp->zfs_props,
|
||||
zfs_prop_to_name(ZFS_PROP_CLONES), &nv));
|
||||
}
|
||||
|
||||
verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) == 0);
|
||||
|
||||
return (value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve a property from the given object. If 'literal' is specified, then
|
||||
* numbers are left as exact values. Otherwise, numbers are converted to a
|
||||
@ -1990,6 +2159,11 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
|
||||
return (-1);
|
||||
break;
|
||||
|
||||
case ZFS_PROP_CLONES:
|
||||
if (get_clones_string(zhp, propbuf, proplen) != 0)
|
||||
return (-1);
|
||||
break;
|
||||
|
||||
case ZFS_PROP_QUOTA:
|
||||
case ZFS_PROP_REFQUOTA:
|
||||
case ZFS_PROP_RESERVATION:
|
||||
@ -2018,6 +2192,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
|
||||
}
|
||||
break;
|
||||
|
||||
case ZFS_PROP_REFRATIO:
|
||||
case ZFS_PROP_COMPRESSRATIO:
|
||||
if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
|
||||
return (-1);
|
||||
@ -2106,6 +2281,17 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
|
||||
}
|
||||
break;
|
||||
|
||||
case ZFS_PROP_GUID:
|
||||
/*
|
||||
* GUIDs are stored as numbers, but they are identifiers.
|
||||
* We don't want them to be pretty printed, because pretty
|
||||
* printing mangles the ID into a truncated and useless value.
|
||||
*/
|
||||
if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
|
||||
return (-1);
|
||||
(void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val);
|
||||
break;
|
||||
|
||||
default:
|
||||
switch (zfs_prop_get_type(prop)) {
|
||||
case PROP_TYPE_NUMBER:
|
||||
@ -2349,7 +2535,7 @@ zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
|
||||
int err;
|
||||
zfs_cmd_t zc = { 0 };
|
||||
|
||||
(void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||
|
||||
err = userquota_propname_decode(propname,
|
||||
zfs_prop_get_int(zhp, ZFS_PROP_ZONED),
|
||||
@ -2401,6 +2587,79 @@ zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
|
||||
uint64_t *propvalue)
|
||||
{
|
||||
int err;
|
||||
zfs_cmd_t zc = { 0 };
|
||||
const char *snapname;
|
||||
|
||||
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||
|
||||
snapname = strchr(propname, '@') + 1;
|
||||
if (strchr(snapname, '@')) {
|
||||
(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
|
||||
} else {
|
||||
/* snapname is the short name, append it to zhp's fsname */
|
||||
char *cp;
|
||||
|
||||
(void) strlcpy(zc.zc_value, zhp->zfs_name,
|
||||
sizeof (zc.zc_value));
|
||||
cp = strchr(zc.zc_value, '@');
|
||||
if (cp != NULL)
|
||||
*cp = '\0';
|
||||
(void) strlcat(zc.zc_value, "@", sizeof (zc.zc_value));
|
||||
(void) strlcat(zc.zc_value, snapname, sizeof (zc.zc_value));
|
||||
}
|
||||
|
||||
err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
*propvalue = zc.zc_cookie;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
|
||||
char *propbuf, int proplen, boolean_t literal)
|
||||
{
|
||||
int err;
|
||||
uint64_t propvalue;
|
||||
|
||||
err = zfs_prop_get_written_int(zhp, propname, &propvalue);
|
||||
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
if (literal) {
|
||||
(void) snprintf(propbuf, proplen, "%llu", propvalue);
|
||||
} else {
|
||||
zfs_nicenum(propvalue, propbuf, proplen);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
|
||||
uint64_t *usedp)
|
||||
{
|
||||
int err;
|
||||
zfs_cmd_t zc = { 0 };
|
||||
|
||||
(void) strlcpy(zc.zc_name, lastsnap->zfs_name, sizeof (zc.zc_name));
|
||||
(void) strlcpy(zc.zc_value, firstsnap->zfs_name, sizeof (zc.zc_value));
|
||||
|
||||
err = ioctl(lastsnap->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_SNAPS, &zc);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
*usedp = zc.zc_cookie;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the name of the given zfs handle.
|
||||
*/
|
||||
@ -2419,128 +2678,6 @@ zfs_get_type(const zfs_handle_t *zhp)
|
||||
return (zhp->zfs_type);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc)
|
||||
{
|
||||
int rc;
|
||||
uint64_t orig_cookie;
|
||||
|
||||
orig_cookie = zc->zc_cookie;
|
||||
top:
|
||||
(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
|
||||
rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
|
||||
|
||||
if (rc == -1) {
|
||||
switch (errno) {
|
||||
case ENOMEM:
|
||||
/* expand nvlist memory and try again */
|
||||
if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
|
||||
zcmd_free_nvlists(zc);
|
||||
return (-1);
|
||||
}
|
||||
zc->zc_cookie = orig_cookie;
|
||||
goto top;
|
||||
/*
|
||||
* An errno value of ESRCH indicates normal completion.
|
||||
* If ENOENT is returned, then the underlying dataset
|
||||
* has been removed since we obtained the handle.
|
||||
*/
|
||||
case ESRCH:
|
||||
case ENOENT:
|
||||
rc = 1;
|
||||
break;
|
||||
default:
|
||||
rc = zfs_standard_error(zhp->zfs_hdl, errno,
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"cannot iterate filesystems"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all child filesystems
|
||||
*/
|
||||
int
|
||||
zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
zfs_handle_t *nzhp;
|
||||
int ret;
|
||||
|
||||
if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
|
||||
return (0);
|
||||
|
||||
if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
|
||||
return (-1);
|
||||
|
||||
while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
|
||||
&zc)) == 0) {
|
||||
/*
|
||||
* Silently ignore errors, as the only plausible explanation is
|
||||
* that the pool has since been removed.
|
||||
*/
|
||||
if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
|
||||
&zc)) == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((ret = func(nzhp, data)) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
zcmd_free_nvlists(&zc);
|
||||
return ((ret < 0) ? ret : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all snapshots
|
||||
*/
|
||||
int
|
||||
zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
zfs_handle_t *nzhp;
|
||||
int ret;
|
||||
|
||||
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
|
||||
return (0);
|
||||
|
||||
if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
|
||||
return (-1);
|
||||
while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
|
||||
&zc)) == 0) {
|
||||
|
||||
if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
|
||||
&zc)) == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((ret = func(nzhp, data)) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
zcmd_free_nvlists(&zc);
|
||||
return ((ret < 0) ? ret : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all children, snapshots and filesystems
|
||||
*/
|
||||
int
|
||||
zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
|
||||
return (ret);
|
||||
|
||||
return (zfs_iter_snapshots(zhp, func, data));
|
||||
}
|
||||
|
||||
/*
|
||||
* Is one dataset name a child dataset of another?
|
||||
*
|
||||
@ -2564,18 +2701,19 @@ is_descendant(const char *ds1, const char *ds2)
|
||||
|
||||
/*
|
||||
* Given a complete name, return just the portion that refers to the parent.
|
||||
* Can return NULL if this is a pool.
|
||||
* Will return -1 if there is no parent (path is just the name of the
|
||||
* pool).
|
||||
*/
|
||||
static int
|
||||
parent_name(const char *path, char *buf, size_t buflen)
|
||||
{
|
||||
char *loc;
|
||||
char *slashp;
|
||||
|
||||
if ((loc = strrchr(path, '/')) == NULL)
|
||||
(void) strlcpy(buf, path, buflen);
|
||||
|
||||
if ((slashp = strrchr(buf, '/')) == NULL)
|
||||
return (-1);
|
||||
|
||||
(void) strncpy(buf, path, MIN(buflen, loc - path));
|
||||
buf[loc - path] = '\0';
|
||||
*slashp = '\0';
|
||||
|
||||
return (0);
|
||||
}
|
||||
@ -2974,9 +3112,8 @@ zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
|
||||
}
|
||||
|
||||
struct destroydata {
|
||||
char *snapname;
|
||||
boolean_t gotone;
|
||||
boolean_t closezhp;
|
||||
nvlist_t *nvl;
|
||||
const char *snapname;
|
||||
};
|
||||
|
||||
static int
|
||||
@ -2985,24 +3122,19 @@ zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
|
||||
struct destroydata *dd = arg;
|
||||
zfs_handle_t *szhp;
|
||||
char name[ZFS_MAXNAMELEN];
|
||||
boolean_t closezhp = dd->closezhp;
|
||||
int rv = 0;
|
||||
|
||||
(void) strlcpy(name, zhp->zfs_name, sizeof (name));
|
||||
(void) strlcat(name, "@", sizeof (name));
|
||||
(void) strlcat(name, dd->snapname, sizeof (name));
|
||||
(void) snprintf(name, sizeof (name),
|
||||
"%s@%s", zhp->zfs_name, dd->snapname);
|
||||
|
||||
szhp = make_dataset_handle(zhp->zfs_hdl, name);
|
||||
if (szhp) {
|
||||
dd->gotone = B_TRUE;
|
||||
verify(nvlist_add_boolean(dd->nvl, name) == 0);
|
||||
zfs_close(szhp);
|
||||
}
|
||||
|
||||
dd->closezhp = B_TRUE;
|
||||
if (!dd->gotone)
|
||||
rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg);
|
||||
if (closezhp)
|
||||
zfs_close(zhp);
|
||||
rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd);
|
||||
zfs_close(zhp);
|
||||
return (rv);
|
||||
}
|
||||
|
||||
@ -3012,29 +3144,45 @@ zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
|
||||
int
|
||||
zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
int ret;
|
||||
struct destroydata dd = { 0 };
|
||||
|
||||
dd.snapname = snapname;
|
||||
(void) zfs_check_snap_cb(zhp, &dd);
|
||||
verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) == 0);
|
||||
(void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd);
|
||||
|
||||
if (!dd.gotone) {
|
||||
return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
|
||||
if (nvlist_next_nvpair(dd.nvl, NULL) == NULL) {
|
||||
ret = zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
|
||||
dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
|
||||
zhp->zfs_name, snapname));
|
||||
zhp->zfs_name, snapname);
|
||||
} else {
|
||||
ret = zfs_destroy_snaps_nvl(zhp, dd.nvl, defer);
|
||||
}
|
||||
nvlist_free(dd.nvl);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroys all the snapshots named in the nvlist. They must be underneath
|
||||
* the zhp (either snapshots of it, or snapshots of its descendants).
|
||||
*/
|
||||
int
|
||||
zfs_destroy_snaps_nvl(zfs_handle_t *zhp, nvlist_t *snaps, boolean_t defer)
|
||||
{
|
||||
int ret;
|
||||
zfs_cmd_t zc = { 0 };
|
||||
|
||||
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||
(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
|
||||
if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, snaps) != 0)
|
||||
return (-1);
|
||||
zc.zc_defer_destroy = defer;
|
||||
|
||||
ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc);
|
||||
ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS_NVL, &zc);
|
||||
if (ret != 0) {
|
||||
char errbuf[1024];
|
||||
|
||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
||||
"cannot destroy '%s@%s'"), zc.zc_name, snapname);
|
||||
"cannot destroy snapshots in %s"), zc.zc_name);
|
||||
|
||||
switch (errno) {
|
||||
case EEXIST:
|
||||
@ -3070,7 +3218,7 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
|
||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
|
||||
"cannot create '%s'"), target);
|
||||
|
||||
/* validate the target name */
|
||||
/* validate the target/clone name */
|
||||
if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
|
||||
return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
|
||||
|
||||
@ -3406,47 +3554,12 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all dependents for a given dataset. This includes both
|
||||
* hierarchical dependents (children) and data dependents (snapshots and
|
||||
* clones). The bulk of the processing occurs in get_dependents() in
|
||||
* libzfs_graph.c.
|
||||
*/
|
||||
int
|
||||
zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
|
||||
zfs_iter_f func, void *data)
|
||||
{
|
||||
char **dependents;
|
||||
size_t count;
|
||||
int i;
|
||||
zfs_handle_t *child;
|
||||
int ret = 0;
|
||||
|
||||
if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name,
|
||||
&dependents, &count) != 0)
|
||||
return (-1);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if ((child = make_dataset_handle(zhp->zfs_hdl,
|
||||
dependents[i])) == NULL)
|
||||
continue;
|
||||
|
||||
if ((ret = func(child, data)) != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
free(dependents[i]);
|
||||
free(dependents);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Renames the given dataset.
|
||||
*/
|
||||
int
|
||||
zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive)
|
||||
zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
|
||||
boolean_t force_unmount)
|
||||
{
|
||||
int ret;
|
||||
zfs_cmd_t zc = { 0 };
|
||||
@ -3558,7 +3671,8 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive)
|
||||
}
|
||||
|
||||
} else {
|
||||
if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL)
|
||||
if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0,
|
||||
force_unmount ? MS_FORCE : 0)) == NULL)
|
||||
return (-1);
|
||||
|
||||
if (changelist_haszonedchild(cl)) {
|
||||
@ -3891,7 +4005,7 @@ zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
|
||||
int error;
|
||||
zfs_useracct_t buf[100];
|
||||
|
||||
(void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||
|
||||
zc.zc_objset_type = type;
|
||||
zc.zc_nvlist_dst = (uintptr_t)buf;
|
||||
@ -4019,6 +4133,193 @@ zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
libzfs_handle_t *hdl = zhp->zfs_hdl;
|
||||
int nvsz = 2048;
|
||||
void *nvbuf;
|
||||
int err = 0;
|
||||
char errbuf[ZFS_MAXNAMELEN+32];
|
||||
|
||||
assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
|
||||
zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
|
||||
|
||||
tryagain:
|
||||
|
||||
nvbuf = malloc(nvsz);
|
||||
if (nvbuf == NULL) {
|
||||
err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
|
||||
goto out;
|
||||
}
|
||||
|
||||
zc.zc_nvlist_dst_size = nvsz;
|
||||
zc.zc_nvlist_dst = (uintptr_t)nvbuf;
|
||||
|
||||
(void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
|
||||
|
||||
if (ioctl(hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) {
|
||||
(void) snprintf(errbuf, sizeof (errbuf),
|
||||
dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"),
|
||||
zc.zc_name);
|
||||
switch (errno) {
|
||||
case ENOMEM:
|
||||
free(nvbuf);
|
||||
nvsz = zc.zc_nvlist_dst_size;
|
||||
goto tryagain;
|
||||
|
||||
case ENOTSUP:
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"pool must be upgraded"));
|
||||
err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
|
||||
break;
|
||||
case EINVAL:
|
||||
err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
|
||||
break;
|
||||
case ENOENT:
|
||||
err = zfs_error(hdl, EZFS_NOENT, errbuf);
|
||||
break;
|
||||
default:
|
||||
err = zfs_standard_error_fmt(hdl, errno, errbuf);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* success */
|
||||
int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
|
||||
if (rc) {
|
||||
(void) snprintf(errbuf, sizeof (errbuf), dgettext(
|
||||
TEXT_DOMAIN, "cannot get permissions on '%s'"),
|
||||
zc.zc_name);
|
||||
err = zfs_standard_error_fmt(hdl, rc, errbuf);
|
||||
}
|
||||
}
|
||||
|
||||
free(nvbuf);
|
||||
out:
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
libzfs_handle_t *hdl = zhp->zfs_hdl;
|
||||
char *nvbuf;
|
||||
char errbuf[ZFS_MAXNAMELEN+32];
|
||||
size_t nvsz;
|
||||
int err;
|
||||
|
||||
assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
|
||||
zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
|
||||
|
||||
err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);
|
||||
assert(err == 0);
|
||||
|
||||
nvbuf = malloc(nvsz);
|
||||
|
||||
err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);
|
||||
assert(err == 0);
|
||||
|
||||
zc.zc_nvlist_src_size = nvsz;
|
||||
zc.zc_nvlist_src = (uintptr_t)nvbuf;
|
||||
zc.zc_perm_action = un;
|
||||
|
||||
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
|
||||
|
||||
if (zfs_ioctl(hdl, ZFS_IOC_SET_FSACL, &zc) != 0) {
|
||||
(void) snprintf(errbuf, sizeof (errbuf),
|
||||
dgettext(TEXT_DOMAIN, "cannot set permissions on '%s'"),
|
||||
zc.zc_name);
|
||||
switch (errno) {
|
||||
case ENOTSUP:
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"pool must be upgraded"));
|
||||
err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
|
||||
break;
|
||||
case EINVAL:
|
||||
err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
|
||||
break;
|
||||
case ENOENT:
|
||||
err = zfs_error(hdl, EZFS_NOENT, errbuf);
|
||||
break;
|
||||
default:
|
||||
err = zfs_standard_error_fmt(hdl, errno, errbuf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(nvbuf);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
libzfs_handle_t *hdl = zhp->zfs_hdl;
|
||||
int nvsz = 2048;
|
||||
void *nvbuf;
|
||||
int err = 0;
|
||||
char errbuf[ZFS_MAXNAMELEN+32];
|
||||
|
||||
assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
|
||||
|
||||
tryagain:
|
||||
|
||||
nvbuf = malloc(nvsz);
|
||||
if (nvbuf == NULL) {
|
||||
err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
|
||||
goto out;
|
||||
}
|
||||
|
||||
zc.zc_nvlist_dst_size = nvsz;
|
||||
zc.zc_nvlist_dst = (uintptr_t)nvbuf;
|
||||
|
||||
(void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
|
||||
|
||||
if (zfs_ioctl(hdl, ZFS_IOC_GET_HOLDS, &zc) != 0) {
|
||||
(void) snprintf(errbuf, sizeof (errbuf),
|
||||
dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
|
||||
zc.zc_name);
|
||||
switch (errno) {
|
||||
case ENOMEM:
|
||||
free(nvbuf);
|
||||
nvsz = zc.zc_nvlist_dst_size;
|
||||
goto tryagain;
|
||||
|
||||
case ENOTSUP:
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"pool must be upgraded"));
|
||||
err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
|
||||
break;
|
||||
case EINVAL:
|
||||
err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
|
||||
break;
|
||||
case ENOENT:
|
||||
err = zfs_error(hdl, EZFS_NOENT, errbuf);
|
||||
break;
|
||||
default:
|
||||
err = zfs_standard_error_fmt(hdl, errno, errbuf);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* success */
|
||||
int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
|
||||
if (rc) {
|
||||
(void) snprintf(errbuf, sizeof (errbuf),
|
||||
dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
|
||||
zc.zc_name);
|
||||
err = zfs_standard_error_fmt(hdl, rc, errbuf);
|
||||
}
|
||||
}
|
||||
|
||||
free(nvbuf);
|
||||
out:
|
||||
return (err);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
|
||||
{
|
||||
|
@ -1,653 +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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Iterate over all children of the current object. This includes the normal
|
||||
* dataset hierarchy, but also arbitrary hierarchies due to clones. We want to
|
||||
* walk all datasets in the pool, and construct a directed graph of the form:
|
||||
*
|
||||
* home
|
||||
* |
|
||||
* +----+----+
|
||||
* | |
|
||||
* v v ws
|
||||
* bar baz |
|
||||
* | |
|
||||
* v v
|
||||
* @yesterday ----> foo
|
||||
*
|
||||
* In order to construct this graph, we have to walk every dataset in the pool,
|
||||
* because the clone parent is stored as a property of the child, not the
|
||||
* parent. The parent only keeps track of the number of clones.
|
||||
*
|
||||
* In the normal case (without clones) this would be rather expensive. To avoid
|
||||
* unnecessary computation, we first try a walk of the subtree hierarchy
|
||||
* starting from the initial node. At each dataset, we construct a node in the
|
||||
* graph and an edge leading from its parent. If we don't see any snapshots
|
||||
* with a non-zero clone count, then we are finished.
|
||||
*
|
||||
* If we do find a cloned snapshot, then we finish the walk of the current
|
||||
* subtree, but indicate that we need to do a complete walk. We then perform a
|
||||
* global walk of all datasets, avoiding the subtree we already processed.
|
||||
*
|
||||
* At the end of this, we'll end up with a directed graph of all relevant (and
|
||||
* possible some irrelevant) datasets in the system. We need to both find our
|
||||
* limiting subgraph and determine a safe ordering in which to destroy the
|
||||
* datasets. We do a topological ordering of our graph starting at our target
|
||||
* dataset, and then walk the results in reverse.
|
||||
*
|
||||
* It's possible for the graph to have cycles if, for example, the user renames
|
||||
* a clone to be the parent of its origin snapshot. The user can request to
|
||||
* generate an error in this case, or ignore the cycle and continue.
|
||||
*
|
||||
* When removing datasets, we want to destroy the snapshots in chronological
|
||||
* order (because this is the most efficient method). In order to accomplish
|
||||
* this, we store the creation transaction group with each vertex and keep each
|
||||
* vertex's edges sorted according to this value. The topological sort will
|
||||
* automatically walk the snapshots in the correct order.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <libintl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libzfs.h>
|
||||
|
||||
#include "libzfs_impl.h"
|
||||
#include "zfs_namecheck.h"
|
||||
|
||||
#define MIN_EDGECOUNT 4
|
||||
|
||||
/*
|
||||
* Vertex structure. Indexed by dataset name, this structure maintains a list
|
||||
* of edges to other vertices.
|
||||
*/
|
||||
struct zfs_edge;
|
||||
typedef struct zfs_vertex {
|
||||
char zv_dataset[ZFS_MAXNAMELEN];
|
||||
struct zfs_vertex *zv_next;
|
||||
int zv_visited;
|
||||
uint64_t zv_txg;
|
||||
struct zfs_edge **zv_edges;
|
||||
int zv_edgecount;
|
||||
int zv_edgealloc;
|
||||
} zfs_vertex_t;
|
||||
|
||||
enum {
|
||||
VISIT_SEEN = 1,
|
||||
VISIT_SORT_PRE,
|
||||
VISIT_SORT_POST
|
||||
};
|
||||
|
||||
/*
|
||||
* Edge structure. Simply maintains a pointer to the destination vertex. There
|
||||
* is no need to store the source vertex, since we only use edges in the context
|
||||
* of the source vertex.
|
||||
*/
|
||||
typedef struct zfs_edge {
|
||||
zfs_vertex_t *ze_dest;
|
||||
struct zfs_edge *ze_next;
|
||||
} zfs_edge_t;
|
||||
|
||||
#define ZFS_GRAPH_SIZE 1027 /* this could be dynamic some day */
|
||||
|
||||
/*
|
||||
* Graph structure. Vertices are maintained in a hash indexed by dataset name.
|
||||
*/
|
||||
typedef struct zfs_graph {
|
||||
zfs_vertex_t **zg_hash;
|
||||
size_t zg_size;
|
||||
size_t zg_nvertex;
|
||||
const char *zg_root;
|
||||
int zg_clone_count;
|
||||
} zfs_graph_t;
|
||||
|
||||
/*
|
||||
* Allocate a new edge pointing to the target vertex.
|
||||
*/
|
||||
static zfs_edge_t *
|
||||
zfs_edge_create(libzfs_handle_t *hdl, zfs_vertex_t *dest)
|
||||
{
|
||||
zfs_edge_t *zep = zfs_alloc(hdl, sizeof (zfs_edge_t));
|
||||
|
||||
if (zep == NULL)
|
||||
return (NULL);
|
||||
|
||||
zep->ze_dest = dest;
|
||||
|
||||
return (zep);
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy an edge.
|
||||
*/
|
||||
static void
|
||||
zfs_edge_destroy(zfs_edge_t *zep)
|
||||
{
|
||||
free(zep);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a new vertex with the given name.
|
||||
*/
|
||||
static zfs_vertex_t *
|
||||
zfs_vertex_create(libzfs_handle_t *hdl, const char *dataset)
|
||||
{
|
||||
zfs_vertex_t *zvp = zfs_alloc(hdl, sizeof (zfs_vertex_t));
|
||||
|
||||
if (zvp == NULL)
|
||||
return (NULL);
|
||||
|
||||
assert(strlen(dataset) < ZFS_MAXNAMELEN);
|
||||
|
||||
(void) strlcpy(zvp->zv_dataset, dataset, sizeof (zvp->zv_dataset));
|
||||
|
||||
if ((zvp->zv_edges = zfs_alloc(hdl,
|
||||
MIN_EDGECOUNT * sizeof (void *))) == NULL) {
|
||||
free(zvp);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
zvp->zv_edgealloc = MIN_EDGECOUNT;
|
||||
|
||||
return (zvp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a vertex. Frees up any associated edges.
|
||||
*/
|
||||
static void
|
||||
zfs_vertex_destroy(zfs_vertex_t *zvp)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < zvp->zv_edgecount; i++)
|
||||
zfs_edge_destroy(zvp->zv_edges[i]);
|
||||
|
||||
free(zvp->zv_edges);
|
||||
free(zvp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a vertex, add an edge to the destination vertex.
|
||||
*/
|
||||
static int
|
||||
zfs_vertex_add_edge(libzfs_handle_t *hdl, zfs_vertex_t *zvp,
|
||||
zfs_vertex_t *dest)
|
||||
{
|
||||
zfs_edge_t *zep = zfs_edge_create(hdl, dest);
|
||||
|
||||
if (zep == NULL)
|
||||
return (-1);
|
||||
|
||||
if (zvp->zv_edgecount == zvp->zv_edgealloc) {
|
||||
void *ptr;
|
||||
|
||||
if ((ptr = zfs_realloc(hdl, zvp->zv_edges,
|
||||
zvp->zv_edgealloc * sizeof (void *),
|
||||
zvp->zv_edgealloc * 2 * sizeof (void *))) == NULL)
|
||||
return (-1);
|
||||
|
||||
zvp->zv_edges = ptr;
|
||||
zvp->zv_edgealloc *= 2;
|
||||
}
|
||||
|
||||
zvp->zv_edges[zvp->zv_edgecount++] = zep;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_edge_compare(const void *a, const void *b)
|
||||
{
|
||||
const zfs_edge_t *ea = *((zfs_edge_t **)a);
|
||||
const zfs_edge_t *eb = *((zfs_edge_t **)b);
|
||||
|
||||
if (ea->ze_dest->zv_txg < eb->ze_dest->zv_txg)
|
||||
return (-1);
|
||||
if (ea->ze_dest->zv_txg > eb->ze_dest->zv_txg)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort the given vertex edges according to the creation txg of each vertex.
|
||||
*/
|
||||
static void
|
||||
zfs_vertex_sort_edges(zfs_vertex_t *zvp)
|
||||
{
|
||||
if (zvp->zv_edgecount == 0)
|
||||
return;
|
||||
|
||||
qsort(zvp->zv_edges, zvp->zv_edgecount, sizeof (void *),
|
||||
zfs_edge_compare);
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct a new graph object. We allow the size to be specified as a
|
||||
* parameter so in the future we can size the hash according to the number of
|
||||
* datasets in the pool.
|
||||
*/
|
||||
static zfs_graph_t *
|
||||
zfs_graph_create(libzfs_handle_t *hdl, const char *dataset, size_t size)
|
||||
{
|
||||
zfs_graph_t *zgp = zfs_alloc(hdl, sizeof (zfs_graph_t));
|
||||
|
||||
if (zgp == NULL)
|
||||
return (NULL);
|
||||
|
||||
zgp->zg_size = size;
|
||||
if ((zgp->zg_hash = zfs_alloc(hdl,
|
||||
size * sizeof (zfs_vertex_t *))) == NULL) {
|
||||
free(zgp);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
zgp->zg_root = dataset;
|
||||
zgp->zg_clone_count = 0;
|
||||
|
||||
return (zgp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroy a graph object. We have to iterate over all the hash chains,
|
||||
* destroying each vertex in the process.
|
||||
*/
|
||||
static void
|
||||
zfs_graph_destroy(zfs_graph_t *zgp)
|
||||
{
|
||||
int i;
|
||||
zfs_vertex_t *current, *next;
|
||||
|
||||
for (i = 0; i < zgp->zg_size; i++) {
|
||||
current = zgp->zg_hash[i];
|
||||
while (current != NULL) {
|
||||
next = current->zv_next;
|
||||
zfs_vertex_destroy(current);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
free(zgp->zg_hash);
|
||||
free(zgp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Graph hash function. Classic bernstein k=33 hash function, taken from
|
||||
* usr/src/cmd/sgs/tools/common/strhash.c
|
||||
*/
|
||||
static size_t
|
||||
zfs_graph_hash(zfs_graph_t *zgp, const char *str)
|
||||
{
|
||||
size_t hash = 5381;
|
||||
int c;
|
||||
|
||||
while ((c = *str++) != 0)
|
||||
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
|
||||
|
||||
return (hash % zgp->zg_size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a dataset name, finds the associated vertex, creating it if necessary.
|
||||
*/
|
||||
static zfs_vertex_t *
|
||||
zfs_graph_lookup(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset,
|
||||
uint64_t txg)
|
||||
{
|
||||
size_t idx = zfs_graph_hash(zgp, dataset);
|
||||
zfs_vertex_t *zvp;
|
||||
|
||||
for (zvp = zgp->zg_hash[idx]; zvp != NULL; zvp = zvp->zv_next) {
|
||||
if (strcmp(zvp->zv_dataset, dataset) == 0) {
|
||||
if (zvp->zv_txg == 0)
|
||||
zvp->zv_txg = txg;
|
||||
return (zvp);
|
||||
}
|
||||
}
|
||||
|
||||
if ((zvp = zfs_vertex_create(hdl, dataset)) == NULL)
|
||||
return (NULL);
|
||||
|
||||
zvp->zv_next = zgp->zg_hash[idx];
|
||||
zvp->zv_txg = txg;
|
||||
zgp->zg_hash[idx] = zvp;
|
||||
zgp->zg_nvertex++;
|
||||
|
||||
return (zvp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given two dataset names, create an edge between them. For the source vertex,
|
||||
* mark 'zv_visited' to indicate that we have seen this vertex, and not simply
|
||||
* created it as a destination of another edge. If 'dest' is NULL, then this
|
||||
* is an individual vertex (i.e. the starting vertex), so don't add an edge.
|
||||
*/
|
||||
static int
|
||||
zfs_graph_add(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *source,
|
||||
const char *dest, uint64_t txg)
|
||||
{
|
||||
zfs_vertex_t *svp, *dvp;
|
||||
|
||||
if ((svp = zfs_graph_lookup(hdl, zgp, source, 0)) == NULL)
|
||||
return (-1);
|
||||
svp->zv_visited = VISIT_SEEN;
|
||||
if (dest != NULL) {
|
||||
dvp = zfs_graph_lookup(hdl, zgp, dest, txg);
|
||||
if (dvp == NULL)
|
||||
return (-1);
|
||||
if (zfs_vertex_add_edge(hdl, svp, dvp) != 0)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all children of the given dataset, adding any vertices
|
||||
* as necessary. Returns -1 if there was an error, or 0 otherwise.
|
||||
* This is a simple recursive algorithm - the ZFS namespace typically
|
||||
* is very flat. We manually invoke the necessary ioctl() calls to
|
||||
* avoid the overhead and additional semantics of zfs_open().
|
||||
*/
|
||||
static int
|
||||
iterate_children(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
zfs_vertex_t *zvp;
|
||||
|
||||
/*
|
||||
* Look up the source vertex, and avoid it if we've seen it before.
|
||||
*/
|
||||
zvp = zfs_graph_lookup(hdl, zgp, dataset, 0);
|
||||
if (zvp == NULL)
|
||||
return (-1);
|
||||
if (zvp->zv_visited == VISIT_SEEN)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* Iterate over all children
|
||||
*/
|
||||
for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
|
||||
ioctl(hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
|
||||
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
|
||||
/*
|
||||
* Get statistics for this dataset, to determine the type of the
|
||||
* dataset and clone statistics. If this fails, the dataset has
|
||||
* since been removed, and we're pretty much screwed anyway.
|
||||
*/
|
||||
zc.zc_objset_stats.dds_origin[0] = '\0';
|
||||
if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
|
||||
continue;
|
||||
|
||||
if (zc.zc_objset_stats.dds_origin[0] != '\0') {
|
||||
if (zfs_graph_add(hdl, zgp,
|
||||
zc.zc_objset_stats.dds_origin, zc.zc_name,
|
||||
zc.zc_objset_stats.dds_creation_txg) != 0)
|
||||
return (-1);
|
||||
/*
|
||||
* Count origins only if they are contained in the graph
|
||||
*/
|
||||
if (isa_child_of(zc.zc_objset_stats.dds_origin,
|
||||
zgp->zg_root))
|
||||
zgp->zg_clone_count--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an edge between the parent and the child.
|
||||
*/
|
||||
if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
|
||||
zc.zc_objset_stats.dds_creation_txg) != 0)
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
* Recursively visit child
|
||||
*/
|
||||
if (iterate_children(hdl, zgp, zc.zc_name))
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now iterate over all snapshots.
|
||||
*/
|
||||
bzero(&zc, sizeof (zc));
|
||||
|
||||
for ((void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
|
||||
ioctl(hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT, &zc) == 0;
|
||||
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name))) {
|
||||
|
||||
/*
|
||||
* Get statistics for this dataset, to determine the type of the
|
||||
* dataset and clone statistics. If this fails, the dataset has
|
||||
* since been removed, and we're pretty much screwed anyway.
|
||||
*/
|
||||
if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Add an edge between the parent and the child.
|
||||
*/
|
||||
if (zfs_graph_add(hdl, zgp, dataset, zc.zc_name,
|
||||
zc.zc_objset_stats.dds_creation_txg) != 0)
|
||||
return (-1);
|
||||
|
||||
zgp->zg_clone_count += zc.zc_objset_stats.dds_num_clones;
|
||||
}
|
||||
|
||||
zvp->zv_visited = VISIT_SEEN;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns false if there are no snapshots with dependent clones in this
|
||||
* subtree or if all of those clones are also in this subtree. Returns
|
||||
* true if there is an error or there are external dependents.
|
||||
*/
|
||||
static boolean_t
|
||||
external_dependents(libzfs_handle_t *hdl, zfs_graph_t *zgp, const char *dataset)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
|
||||
/*
|
||||
* Check whether this dataset is a clone or has clones since
|
||||
* iterate_children() only checks the children.
|
||||
*/
|
||||
(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
|
||||
if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0)
|
||||
return (B_TRUE);
|
||||
|
||||
if (zc.zc_objset_stats.dds_origin[0] != '\0') {
|
||||
if (zfs_graph_add(hdl, zgp,
|
||||
zc.zc_objset_stats.dds_origin, zc.zc_name,
|
||||
zc.zc_objset_stats.dds_creation_txg) != 0)
|
||||
return (B_TRUE);
|
||||
if (isa_child_of(zc.zc_objset_stats.dds_origin, dataset))
|
||||
zgp->zg_clone_count--;
|
||||
}
|
||||
|
||||
if ((zc.zc_objset_stats.dds_num_clones) ||
|
||||
iterate_children(hdl, zgp, dataset))
|
||||
return (B_TRUE);
|
||||
|
||||
return (zgp->zg_clone_count != 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct a complete graph of all necessary vertices. First, iterate over
|
||||
* only our object's children. If no cloned snapshots are found, or all of
|
||||
* the cloned snapshots are in this subtree then return a graph of the subtree.
|
||||
* Otherwise, start at the root of the pool and iterate over all datasets.
|
||||
*/
|
||||
static zfs_graph_t *
|
||||
construct_graph(libzfs_handle_t *hdl, const char *dataset)
|
||||
{
|
||||
zfs_graph_t *zgp = zfs_graph_create(hdl, dataset, ZFS_GRAPH_SIZE);
|
||||
int ret = 0;
|
||||
|
||||
if (zgp == NULL)
|
||||
return (zgp);
|
||||
|
||||
if ((strchr(dataset, '/') == NULL) ||
|
||||
(external_dependents(hdl, zgp, dataset))) {
|
||||
/*
|
||||
* Determine pool name and try again.
|
||||
*/
|
||||
int len = strcspn(dataset, "/@") + 1;
|
||||
char *pool = zfs_alloc(hdl, len);
|
||||
|
||||
if (pool == NULL) {
|
||||
zfs_graph_destroy(zgp);
|
||||
return (NULL);
|
||||
}
|
||||
(void) strlcpy(pool, dataset, len);
|
||||
|
||||
if (iterate_children(hdl, zgp, pool) == -1 ||
|
||||
zfs_graph_add(hdl, zgp, pool, NULL, 0) != 0) {
|
||||
free(pool);
|
||||
zfs_graph_destroy(zgp);
|
||||
return (NULL);
|
||||
}
|
||||
free(pool);
|
||||
}
|
||||
|
||||
if (ret == -1 || zfs_graph_add(hdl, zgp, dataset, NULL, 0) != 0) {
|
||||
zfs_graph_destroy(zgp);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (zgp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a graph, do a recursive topological sort into the given array. This is
|
||||
* really just a depth first search, so that the deepest nodes appear first.
|
||||
* hijack the 'zv_visited' marker to avoid visiting the same vertex twice.
|
||||
*/
|
||||
static int
|
||||
topo_sort(libzfs_handle_t *hdl, boolean_t allowrecursion, char **result,
|
||||
size_t *idx, zfs_vertex_t *zgv)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (zgv->zv_visited == VISIT_SORT_PRE && !allowrecursion) {
|
||||
/*
|
||||
* If we've already seen this vertex as part of our depth-first
|
||||
* search, then we have a cyclic dependency, and we must return
|
||||
* an error.
|
||||
*/
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"recursive dependency at '%s'"),
|
||||
zgv->zv_dataset);
|
||||
return (zfs_error(hdl, EZFS_RECURSIVE,
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"cannot determine dependent datasets")));
|
||||
} else if (zgv->zv_visited >= VISIT_SORT_PRE) {
|
||||
/*
|
||||
* If we've already processed this as part of the topological
|
||||
* sort, then don't bother doing so again.
|
||||
*/
|
||||
return (0);
|
||||
}
|
||||
|
||||
zgv->zv_visited = VISIT_SORT_PRE;
|
||||
|
||||
/* avoid doing a search if we don't have to */
|
||||
zfs_vertex_sort_edges(zgv);
|
||||
for (i = 0; i < zgv->zv_edgecount; i++) {
|
||||
if (topo_sort(hdl, allowrecursion, result, idx,
|
||||
zgv->zv_edges[i]->ze_dest) != 0)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* we may have visited this in the course of the above */
|
||||
if (zgv->zv_visited == VISIT_SORT_POST)
|
||||
return (0);
|
||||
|
||||
if ((result[*idx] = zfs_alloc(hdl,
|
||||
strlen(zgv->zv_dataset) + 1)) == NULL)
|
||||
return (-1);
|
||||
|
||||
(void) strcpy(result[*idx], zgv->zv_dataset);
|
||||
*idx += 1;
|
||||
zgv->zv_visited = VISIT_SORT_POST;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* The only public interface for this file. Do the dirty work of constructing a
|
||||
* child list for the given object. Construct the graph, do the toplogical
|
||||
* sort, and then return the array of strings to the caller.
|
||||
*
|
||||
* The 'allowrecursion' parameter controls behavior when cycles are found. If
|
||||
* it is set, the the cycle is ignored and the results returned as if the cycle
|
||||
* did not exist. If it is not set, then the routine will generate an error if
|
||||
* a cycle is found.
|
||||
*/
|
||||
int
|
||||
get_dependents(libzfs_handle_t *hdl, boolean_t allowrecursion,
|
||||
const char *dataset, char ***result, size_t *count)
|
||||
{
|
||||
zfs_graph_t *zgp;
|
||||
zfs_vertex_t *zvp;
|
||||
|
||||
if ((zgp = construct_graph(hdl, dataset)) == NULL)
|
||||
return (-1);
|
||||
|
||||
if ((*result = zfs_alloc(hdl,
|
||||
zgp->zg_nvertex * sizeof (char *))) == NULL) {
|
||||
zfs_graph_destroy(zgp);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((zvp = zfs_graph_lookup(hdl, zgp, dataset, 0)) == NULL) {
|
||||
free(*result);
|
||||
zfs_graph_destroy(zgp);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
*count = 0;
|
||||
if (topo_sort(hdl, allowrecursion, *result, count, zvp) != 0) {
|
||||
free(*result);
|
||||
zfs_graph_destroy(zgp);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get rid of the last entry, which is our starting vertex and not
|
||||
* strictly a dependent.
|
||||
*/
|
||||
assert(*count > 0);
|
||||
free((*result)[*count - 1]);
|
||||
(*count)--;
|
||||
|
||||
zfs_graph_destroy(zgp);
|
||||
|
||||
return (0);
|
||||
}
|
@ -21,6 +21,7 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LIBFS_IMPL_H
|
||||
@ -115,7 +116,7 @@ struct zpool_handle {
|
||||
diskaddr_t zpool_start_block;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
typedef enum {
|
||||
PROTO_NFS = 0,
|
||||
PROTO_SMB = 1,
|
||||
PROTO_END = 2
|
||||
@ -147,6 +148,7 @@ int zpool_standard_error_fmt(libzfs_handle_t *, int, const char *, ...);
|
||||
|
||||
int get_dependents(libzfs_handle_t *, boolean_t, const char *, char ***,
|
||||
size_t *);
|
||||
zfs_handle_t *make_dataset_handle_zc(libzfs_handle_t *, zfs_cmd_t *);
|
||||
|
||||
|
||||
int zprop_parse_value(libzfs_handle_t *, nvpair_t *, int, zfs_type_t,
|
||||
|
@ -20,6 +20,8 @@
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2011 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -437,7 +439,7 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok)
|
||||
uint_t i, nspares, nl2cache;
|
||||
boolean_t config_seen;
|
||||
uint64_t best_txg;
|
||||
char *name, *hostname;
|
||||
char *name, *hostname, *comment;
|
||||
uint64_t version, guid;
|
||||
uint_t children = 0;
|
||||
nvlist_t **child = NULL;
|
||||
@ -526,6 +528,7 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok)
|
||||
* version
|
||||
* pool guid
|
||||
* name
|
||||
* comment (if available)
|
||||
* pool state
|
||||
* hostid (if available)
|
||||
* hostname (if available)
|
||||
@ -547,11 +550,24 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl, boolean_t active_ok)
|
||||
if (nvlist_add_string(config,
|
||||
ZPOOL_CONFIG_POOL_NAME, name) != 0)
|
||||
goto nomem;
|
||||
|
||||
/*
|
||||
* COMMENT is optional, don't bail if it's not
|
||||
* there, instead, set it to NULL.
|
||||
*/
|
||||
if (nvlist_lookup_string(tmp,
|
||||
ZPOOL_CONFIG_COMMENT, &comment) != 0)
|
||||
comment = NULL;
|
||||
else if (nvlist_add_string(config,
|
||||
ZPOOL_CONFIG_COMMENT, comment) != 0)
|
||||
goto nomem;
|
||||
|
||||
verify(nvlist_lookup_uint64(tmp,
|
||||
ZPOOL_CONFIG_POOL_STATE, &state) == 0);
|
||||
if (nvlist_add_uint64(config,
|
||||
ZPOOL_CONFIG_POOL_STATE, state) != 0)
|
||||
goto nomem;
|
||||
|
||||
hostid = 0;
|
||||
if (nvlist_lookup_uint64(tmp,
|
||||
ZPOOL_CONFIG_HOSTID, &hostid) == 0) {
|
||||
|
462
lib/libzfs/common/libzfs_iter.c
Normal file
462
lib/libzfs/common/libzfs_iter.c
Normal file
@ -0,0 +1,462 @@
|
||||
/*
|
||||
* 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 2010 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2011 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <stddef.h>
|
||||
#include <libintl.h>
|
||||
#include <libzfs.h>
|
||||
|
||||
#include "libzfs_impl.h"
|
||||
|
||||
int
|
||||
zfs_iter_clones(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
nvlist_t *nvl = zfs_get_clones_nvl(zhp);
|
||||
nvpair_t *pair;
|
||||
|
||||
if (nvl == NULL)
|
||||
return (0);
|
||||
|
||||
for (pair = nvlist_next_nvpair(nvl, NULL); pair != NULL;
|
||||
pair = nvlist_next_nvpair(nvl, pair)) {
|
||||
zfs_handle_t *clone = zfs_open(zhp->zfs_hdl, nvpair_name(pair),
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
|
||||
if (clone != NULL) {
|
||||
int err = func(clone, data);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc)
|
||||
{
|
||||
int rc;
|
||||
uint64_t orig_cookie;
|
||||
|
||||
orig_cookie = zc->zc_cookie;
|
||||
top:
|
||||
(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
|
||||
rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
|
||||
|
||||
if (rc == -1) {
|
||||
switch (errno) {
|
||||
case ENOMEM:
|
||||
/* expand nvlist memory and try again */
|
||||
if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
|
||||
zcmd_free_nvlists(zc);
|
||||
return (-1);
|
||||
}
|
||||
zc->zc_cookie = orig_cookie;
|
||||
goto top;
|
||||
/*
|
||||
* An errno value of ESRCH indicates normal completion.
|
||||
* If ENOENT is returned, then the underlying dataset
|
||||
* has been removed since we obtained the handle.
|
||||
*/
|
||||
case ESRCH:
|
||||
case ENOENT:
|
||||
rc = 1;
|
||||
break;
|
||||
default:
|
||||
rc = zfs_standard_error(zhp->zfs_hdl, errno,
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"cannot iterate filesystems"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all child filesystems
|
||||
*/
|
||||
int
|
||||
zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
zfs_handle_t *nzhp;
|
||||
int ret;
|
||||
|
||||
if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
|
||||
return (0);
|
||||
|
||||
if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
|
||||
return (-1);
|
||||
|
||||
while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
|
||||
&zc)) == 0) {
|
||||
/*
|
||||
* Silently ignore errors, as the only plausible explanation is
|
||||
* that the pool has since been removed.
|
||||
*/
|
||||
if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
|
||||
&zc)) == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((ret = func(nzhp, data)) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
zcmd_free_nvlists(&zc);
|
||||
return ((ret < 0) ? ret : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all snapshots
|
||||
*/
|
||||
int
|
||||
zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
zfs_handle_t *nzhp;
|
||||
int ret;
|
||||
|
||||
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
|
||||
return (0);
|
||||
|
||||
if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
|
||||
return (-1);
|
||||
while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
|
||||
&zc)) == 0) {
|
||||
|
||||
if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
|
||||
&zc)) == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((ret = func(nzhp, data)) != 0) {
|
||||
zcmd_free_nvlists(&zc);
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
zcmd_free_nvlists(&zc);
|
||||
return ((ret < 0) ? ret : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Routines for dealing with the sorted snapshot functionality
|
||||
*/
|
||||
typedef struct zfs_node {
|
||||
zfs_handle_t *zn_handle;
|
||||
avl_node_t zn_avlnode;
|
||||
} zfs_node_t;
|
||||
|
||||
static int
|
||||
zfs_sort_snaps(zfs_handle_t *zhp, void *data)
|
||||
{
|
||||
avl_tree_t *avl = data;
|
||||
zfs_node_t *node;
|
||||
zfs_node_t search;
|
||||
|
||||
search.zn_handle = zhp;
|
||||
node = avl_find(avl, &search, NULL);
|
||||
if (node) {
|
||||
/*
|
||||
* If this snapshot was renamed while we were creating the
|
||||
* AVL tree, it's possible that we already inserted it under
|
||||
* its old name. Remove the old handle before adding the new
|
||||
* one.
|
||||
*/
|
||||
zfs_close(node->zn_handle);
|
||||
avl_remove(avl, node);
|
||||
free(node);
|
||||
}
|
||||
|
||||
node = zfs_alloc(zhp->zfs_hdl, sizeof (zfs_node_t));
|
||||
node->zn_handle = zhp;
|
||||
avl_add(avl, node);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
zfs_snapshot_compare(const void *larg, const void *rarg)
|
||||
{
|
||||
zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;
|
||||
zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;
|
||||
uint64_t lcreate, rcreate;
|
||||
|
||||
/*
|
||||
* Sort them according to creation time. We use the hidden
|
||||
* CREATETXG property to get an absolute ordering of snapshots.
|
||||
*/
|
||||
lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);
|
||||
rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);
|
||||
|
||||
if (lcreate < rcreate)
|
||||
return (-1);
|
||||
else if (lcreate > rcreate)
|
||||
return (+1);
|
||||
else
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_iter_snapshots_sorted(zfs_handle_t *zhp, zfs_iter_f callback, void *data)
|
||||
{
|
||||
int ret = 0;
|
||||
zfs_node_t *node;
|
||||
avl_tree_t avl;
|
||||
void *cookie = NULL;
|
||||
|
||||
avl_create(&avl, zfs_snapshot_compare,
|
||||
sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));
|
||||
|
||||
ret = zfs_iter_snapshots(zhp, zfs_sort_snaps, &avl);
|
||||
|
||||
for (node = avl_first(&avl); node != NULL; node = AVL_NEXT(&avl, node))
|
||||
ret |= callback(node->zn_handle, data);
|
||||
|
||||
while ((node = avl_destroy_nodes(&avl, &cookie)) != NULL)
|
||||
free(node);
|
||||
|
||||
avl_destroy(&avl);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char *ssa_first;
|
||||
char *ssa_last;
|
||||
boolean_t ssa_seenfirst;
|
||||
boolean_t ssa_seenlast;
|
||||
zfs_iter_f ssa_func;
|
||||
void *ssa_arg;
|
||||
} snapspec_arg_t;
|
||||
|
||||
static int
|
||||
snapspec_cb(zfs_handle_t *zhp, void *arg) {
|
||||
snapspec_arg_t *ssa = arg;
|
||||
char *shortsnapname;
|
||||
int err = 0;
|
||||
|
||||
if (ssa->ssa_seenlast)
|
||||
return (0);
|
||||
shortsnapname = zfs_strdup(zhp->zfs_hdl,
|
||||
strchr(zfs_get_name(zhp), '@') + 1);
|
||||
|
||||
if (!ssa->ssa_seenfirst && strcmp(shortsnapname, ssa->ssa_first) == 0)
|
||||
ssa->ssa_seenfirst = B_TRUE;
|
||||
|
||||
if (ssa->ssa_seenfirst) {
|
||||
err = ssa->ssa_func(zhp, ssa->ssa_arg);
|
||||
} else {
|
||||
zfs_close(zhp);
|
||||
}
|
||||
|
||||
if (strcmp(shortsnapname, ssa->ssa_last) == 0)
|
||||
ssa->ssa_seenlast = B_TRUE;
|
||||
free(shortsnapname);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* spec is a string like "A,B%C,D"
|
||||
*
|
||||
* <snaps>, where <snaps> can be:
|
||||
* <snap> (single snapshot)
|
||||
* <snap>%<snap> (range of snapshots, inclusive)
|
||||
* %<snap> (range of snapshots, starting with earliest)
|
||||
* <snap>% (range of snapshots, ending with last)
|
||||
* % (all snapshots)
|
||||
* <snaps>[,...] (comma separated list of the above)
|
||||
*
|
||||
* If a snapshot can not be opened, continue trying to open the others, but
|
||||
* return ENOENT at the end.
|
||||
*/
|
||||
int
|
||||
zfs_iter_snapspec(zfs_handle_t *fs_zhp, const char *spec_orig,
|
||||
zfs_iter_f func, void *arg)
|
||||
{
|
||||
char buf[ZFS_MAXNAMELEN];
|
||||
char *comma_separated, *cp;
|
||||
int err = 0;
|
||||
int ret = 0;
|
||||
|
||||
(void) strlcpy(buf, spec_orig, sizeof (buf));
|
||||
cp = buf;
|
||||
|
||||
while ((comma_separated = strsep(&cp, ",")) != NULL) {
|
||||
char *pct = strchr(comma_separated, '%');
|
||||
if (pct != NULL) {
|
||||
snapspec_arg_t ssa = { 0 };
|
||||
ssa.ssa_func = func;
|
||||
ssa.ssa_arg = arg;
|
||||
|
||||
if (pct == comma_separated)
|
||||
ssa.ssa_seenfirst = B_TRUE;
|
||||
else
|
||||
ssa.ssa_first = comma_separated;
|
||||
*pct = '\0';
|
||||
ssa.ssa_last = pct + 1;
|
||||
|
||||
/*
|
||||
* If there is a lastname specified, make sure it
|
||||
* exists.
|
||||
*/
|
||||
if (ssa.ssa_last[0] != '\0') {
|
||||
char snapname[ZFS_MAXNAMELEN];
|
||||
(void) snprintf(snapname, sizeof (snapname),
|
||||
"%s@%s", zfs_get_name(fs_zhp),
|
||||
ssa.ssa_last);
|
||||
if (!zfs_dataset_exists(fs_zhp->zfs_hdl,
|
||||
snapname, ZFS_TYPE_SNAPSHOT)) {
|
||||
ret = ENOENT;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
err = zfs_iter_snapshots_sorted(fs_zhp,
|
||||
snapspec_cb, &ssa);
|
||||
if (ret == 0)
|
||||
ret = err;
|
||||
if (ret == 0 && (!ssa.ssa_seenfirst ||
|
||||
(ssa.ssa_last[0] != '\0' && !ssa.ssa_seenlast))) {
|
||||
ret = ENOENT;
|
||||
}
|
||||
} else {
|
||||
char snapname[ZFS_MAXNAMELEN];
|
||||
zfs_handle_t *snap_zhp;
|
||||
(void) snprintf(snapname, sizeof (snapname), "%s@%s",
|
||||
zfs_get_name(fs_zhp), comma_separated);
|
||||
snap_zhp = make_dataset_handle(fs_zhp->zfs_hdl,
|
||||
snapname);
|
||||
if (snap_zhp == NULL) {
|
||||
ret = ENOENT;
|
||||
continue;
|
||||
}
|
||||
err = func(snap_zhp, arg);
|
||||
if (ret == 0)
|
||||
ret = err;
|
||||
}
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all children, snapshots and filesystems
|
||||
*/
|
||||
int
|
||||
zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
|
||||
return (ret);
|
||||
|
||||
return (zfs_iter_snapshots(zhp, func, data));
|
||||
}
|
||||
|
||||
|
||||
typedef struct iter_stack_frame {
|
||||
struct iter_stack_frame *next;
|
||||
zfs_handle_t *zhp;
|
||||
} iter_stack_frame_t;
|
||||
|
||||
typedef struct iter_dependents_arg {
|
||||
boolean_t first;
|
||||
boolean_t allowrecursion;
|
||||
iter_stack_frame_t *stack;
|
||||
zfs_iter_f func;
|
||||
void *data;
|
||||
} iter_dependents_arg_t;
|
||||
|
||||
static int
|
||||
iter_dependents_cb(zfs_handle_t *zhp, void *arg)
|
||||
{
|
||||
iter_dependents_arg_t *ida = arg;
|
||||
int err;
|
||||
boolean_t first = ida->first;
|
||||
ida->first = B_FALSE;
|
||||
|
||||
if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
|
||||
err = zfs_iter_clones(zhp, iter_dependents_cb, ida);
|
||||
} else {
|
||||
iter_stack_frame_t isf;
|
||||
iter_stack_frame_t *f;
|
||||
|
||||
/*
|
||||
* check if there is a cycle by seeing if this fs is already
|
||||
* on the stack.
|
||||
*/
|
||||
for (f = ida->stack; f != NULL; f = f->next) {
|
||||
if (f->zhp->zfs_dmustats.dds_guid ==
|
||||
zhp->zfs_dmustats.dds_guid) {
|
||||
if (ida->allowrecursion) {
|
||||
zfs_close(zhp);
|
||||
return (0);
|
||||
} else {
|
||||
zfs_error_aux(zhp->zfs_hdl,
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"recursive dependency at '%s'"),
|
||||
zfs_get_name(zhp));
|
||||
err = zfs_error(zhp->zfs_hdl,
|
||||
EZFS_RECURSIVE,
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"cannot determine dependent "
|
||||
"datasets"));
|
||||
zfs_close(zhp);
|
||||
return (err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isf.zhp = zhp;
|
||||
isf.next = ida->stack;
|
||||
ida->stack = &isf;
|
||||
err = zfs_iter_filesystems(zhp, iter_dependents_cb, ida);
|
||||
if (err == 0)
|
||||
err = zfs_iter_snapshots(zhp, iter_dependents_cb, ida);
|
||||
ida->stack = isf.next;
|
||||
}
|
||||
if (!first && err == 0)
|
||||
err = ida->func(zhp, ida->data);
|
||||
return (err);
|
||||
}
|
||||
|
||||
int
|
||||
zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
|
||||
zfs_iter_f func, void *data)
|
||||
{
|
||||
iter_dependents_arg_t ida;
|
||||
ida.allowrecursion = allowrecursion;
|
||||
ida.stack = NULL;
|
||||
ida.func = func;
|
||||
ida.data = data;
|
||||
ida.first = B_TRUE;
|
||||
return (iter_dependents_cb(zfs_handle_dup(zhp), &ida));
|
||||
}
|
@ -21,6 +21,8 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
@ -41,6 +43,7 @@
|
||||
#include "zfs_prop.h"
|
||||
#include "libzfs_impl.h"
|
||||
#include "zfs_comutil.h"
|
||||
#include "zfeature_common.h"
|
||||
|
||||
static int read_efi_label(nvlist_t *config, diskaddr_t *sb);
|
||||
|
||||
@ -233,6 +236,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
|
||||
|
||||
case ZPOOL_PROP_ALTROOT:
|
||||
case ZPOOL_PROP_CACHEFILE:
|
||||
case ZPOOL_PROP_COMMENT:
|
||||
if (zhp->zpool_props != NULL ||
|
||||
zpool_get_all_props(zhp) == 0) {
|
||||
(void) strlcpy(buf,
|
||||
@ -270,6 +274,8 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
|
||||
case ZPOOL_PROP_SIZE:
|
||||
case ZPOOL_PROP_ALLOCATED:
|
||||
case ZPOOL_PROP_FREE:
|
||||
case ZPOOL_PROP_FREEING:
|
||||
case ZPOOL_PROP_EXPANDSZ:
|
||||
(void) zfs_nicenum(intval, buf, len);
|
||||
break;
|
||||
|
||||
@ -294,6 +300,12 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf, size_t len,
|
||||
(void) strlcpy(buf, zpool_state_to_name(intval,
|
||||
vs->vs_aux), len);
|
||||
break;
|
||||
case ZPOOL_PROP_VERSION:
|
||||
if (intval >= SPA_VERSION_FEATURES) {
|
||||
(void) snprintf(buf, len, "-");
|
||||
break;
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
(void) snprintf(buf, len, "%llu", intval);
|
||||
}
|
||||
@ -357,8 +369,8 @@ pool_uses_efi(nvlist_t *config)
|
||||
return (B_FALSE);
|
||||
}
|
||||
|
||||
static boolean_t
|
||||
pool_is_bootable(zpool_handle_t *zhp)
|
||||
boolean_t
|
||||
zpool_is_bootable(zpool_handle_t *zhp)
|
||||
{
|
||||
char bootfs[ZPOOL_MAXNAMELEN];
|
||||
|
||||
@ -382,7 +394,7 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
|
||||
zpool_prop_t prop;
|
||||
char *strval;
|
||||
uint64_t intval;
|
||||
char *slash;
|
||||
char *slash, *check;
|
||||
struct stat64 statbuf;
|
||||
zpool_handle_t *zhp;
|
||||
nvlist_t *nvroot;
|
||||
@ -396,10 +408,48 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
|
||||
while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
|
||||
const char *propname = nvpair_name(elem);
|
||||
|
||||
prop = zpool_name_to_prop(propname);
|
||||
if (prop == ZPROP_INVAL && zpool_prop_feature(propname)) {
|
||||
int err;
|
||||
zfeature_info_t *feature;
|
||||
char *fname = strchr(propname, '@') + 1;
|
||||
|
||||
err = zfeature_lookup_name(fname, &feature);
|
||||
if (err != 0) {
|
||||
ASSERT3U(err, ==, ENOENT);
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"invalid feature '%s'"), fname);
|
||||
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (nvpair_type(elem) != DATA_TYPE_STRING) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"'%s' must be a string"), propname);
|
||||
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
(void) nvpair_value_string(elem, &strval);
|
||||
if (strcmp(strval, ZFS_FEATURE_ENABLED) != 0) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"property '%s' can only be set to "
|
||||
"'enabled'"), propname);
|
||||
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (nvlist_add_uint64(retprops, propname, 0) != 0) {
|
||||
(void) no_memory(hdl);
|
||||
goto error;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure this property is valid and applies to this type.
|
||||
*/
|
||||
if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL) {
|
||||
if (prop == ZPROP_INVAL) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"invalid property '%s'"), propname);
|
||||
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||
@ -422,7 +472,8 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
|
||||
*/
|
||||
switch (prop) {
|
||||
case ZPOOL_PROP_VERSION:
|
||||
if (intval < version || intval > SPA_VERSION) {
|
||||
if (intval < version ||
|
||||
!SPA_VERSION_IS_SUPPORTED(intval)) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"property '%s' number %d is invalid."),
|
||||
propname, intval);
|
||||
@ -541,6 +592,26 @@ zpool_valid_proplist(libzfs_handle_t *hdl, const char *poolname,
|
||||
*slash = '/';
|
||||
break;
|
||||
|
||||
case ZPOOL_PROP_COMMENT:
|
||||
for (check = strval; *check != '\0'; check++) {
|
||||
if (!isprint(*check)) {
|
||||
zfs_error_aux(hdl,
|
||||
dgettext(TEXT_DOMAIN,
|
||||
"comment may only have printable "
|
||||
"characters"));
|
||||
(void) zfs_error(hdl, EZFS_BADPROP,
|
||||
errbuf);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (strlen(strval) > ZPROP_MAX_COMMENT) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"comment must not exceed %d characters"),
|
||||
ZPROP_MAX_COMMENT);
|
||||
(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case ZPOOL_PROP_READONLY:
|
||||
if (!flags.import) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
@ -624,10 +695,77 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp)
|
||||
libzfs_handle_t *hdl = zhp->zpool_hdl;
|
||||
zprop_list_t *entry;
|
||||
char buf[ZFS_MAXPROPLEN];
|
||||
nvlist_t *features = NULL;
|
||||
zprop_list_t **last;
|
||||
boolean_t firstexpand = (NULL == *plp);
|
||||
|
||||
if (zprop_expand_list(hdl, plp, ZFS_TYPE_POOL) != 0)
|
||||
return (-1);
|
||||
|
||||
last = plp;
|
||||
while (*last != NULL)
|
||||
last = &(*last)->pl_next;
|
||||
|
||||
if ((*plp)->pl_all)
|
||||
features = zpool_get_features(zhp);
|
||||
|
||||
if ((*plp)->pl_all && firstexpand) {
|
||||
for (int i = 0; i < SPA_FEATURES; i++) {
|
||||
zprop_list_t *entry = zfs_alloc(hdl,
|
||||
sizeof (zprop_list_t));
|
||||
entry->pl_prop = ZPROP_INVAL;
|
||||
entry->pl_user_prop = zfs_asprintf(hdl, "feature@%s",
|
||||
spa_feature_table[i].fi_uname);
|
||||
entry->pl_width = strlen(entry->pl_user_prop);
|
||||
entry->pl_all = B_TRUE;
|
||||
|
||||
*last = entry;
|
||||
last = &entry->pl_next;
|
||||
}
|
||||
}
|
||||
|
||||
/* add any unsupported features */
|
||||
for (nvpair_t *nvp = nvlist_next_nvpair(features, NULL);
|
||||
nvp != NULL; nvp = nvlist_next_nvpair(features, nvp)) {
|
||||
char *propname;
|
||||
boolean_t found;
|
||||
zprop_list_t *entry;
|
||||
|
||||
if (zfeature_is_supported(nvpair_name(nvp)))
|
||||
continue;
|
||||
|
||||
propname = zfs_asprintf(hdl, "unsupported@%s",
|
||||
nvpair_name(nvp));
|
||||
|
||||
/*
|
||||
* Before adding the property to the list make sure that no
|
||||
* other pool already added the same property.
|
||||
*/
|
||||
found = B_FALSE;
|
||||
entry = *plp;
|
||||
while (entry != NULL) {
|
||||
if (entry->pl_user_prop != NULL &&
|
||||
strcmp(propname, entry->pl_user_prop) == 0) {
|
||||
found = B_TRUE;
|
||||
break;
|
||||
}
|
||||
entry = entry->pl_next;
|
||||
}
|
||||
if (found) {
|
||||
free(propname);
|
||||
continue;
|
||||
}
|
||||
|
||||
entry = zfs_alloc(hdl, sizeof (zprop_list_t));
|
||||
entry->pl_prop = ZPROP_INVAL;
|
||||
entry->pl_user_prop = propname;
|
||||
entry->pl_width = strlen(entry->pl_user_prop);
|
||||
entry->pl_all = B_TRUE;
|
||||
|
||||
*last = entry;
|
||||
last = &entry->pl_next;
|
||||
}
|
||||
|
||||
for (entry = *plp; entry != NULL; entry = entry->pl_next) {
|
||||
|
||||
if (entry->pl_fixed)
|
||||
@ -644,6 +782,66 @@ zpool_expand_proplist(zpool_handle_t *zhp, zprop_list_t **plp)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the state for the given feature on the given ZFS pool.
|
||||
*/
|
||||
int
|
||||
zpool_prop_get_feature(zpool_handle_t *zhp, const char *propname, char *buf,
|
||||
size_t len)
|
||||
{
|
||||
uint64_t refcount;
|
||||
boolean_t found = B_FALSE;
|
||||
nvlist_t *features = zpool_get_features(zhp);
|
||||
boolean_t supported;
|
||||
const char *feature = strchr(propname, '@') + 1;
|
||||
|
||||
supported = zpool_prop_feature(propname);
|
||||
ASSERT(supported || zfs_prop_unsupported(propname));
|
||||
|
||||
/*
|
||||
* Convert from feature name to feature guid. This conversion is
|
||||
* unecessary for unsupported@... properties because they already
|
||||
* use guids.
|
||||
*/
|
||||
if (supported) {
|
||||
int ret;
|
||||
zfeature_info_t *fi;
|
||||
|
||||
ret = zfeature_lookup_name(feature, &fi);
|
||||
if (ret != 0) {
|
||||
(void) strlcpy(buf, "-", len);
|
||||
return (ENOTSUP);
|
||||
}
|
||||
feature = fi->fi_guid;
|
||||
}
|
||||
|
||||
if (nvlist_lookup_uint64(features, feature, &refcount) == 0)
|
||||
found = B_TRUE;
|
||||
|
||||
if (supported) {
|
||||
if (!found) {
|
||||
(void) strlcpy(buf, ZFS_FEATURE_DISABLED, len);
|
||||
} else {
|
||||
if (refcount == 0)
|
||||
(void) strlcpy(buf, ZFS_FEATURE_ENABLED, len);
|
||||
else
|
||||
(void) strlcpy(buf, ZFS_FEATURE_ACTIVE, len);
|
||||
}
|
||||
} else {
|
||||
if (found) {
|
||||
if (refcount == 0) {
|
||||
(void) strcpy(buf, ZFS_UNSUPPORTED_INACTIVE);
|
||||
} else {
|
||||
(void) strcpy(buf, ZFS_UNSUPPORTED_READONLY);
|
||||
}
|
||||
} else {
|
||||
(void) strlcpy(buf, "-", len);
|
||||
return (ENOTSUP);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't start the slice at the default block of 34; many storage
|
||||
@ -1071,7 +1269,7 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
|
||||
return (zfs_error(hdl, EZFS_BADVERSION, msg));
|
||||
}
|
||||
|
||||
if (pool_is_bootable(zhp) && nvlist_lookup_nvlist_array(nvroot,
|
||||
if (zpool_is_bootable(zhp) && nvlist_lookup_nvlist_array(nvroot,
|
||||
ZPOOL_CONFIG_SPARES, &spares, &nspares) == 0) {
|
||||
uint64_t s;
|
||||
|
||||
@ -1230,8 +1428,10 @@ zpool_rewind_exclaim(libzfs_handle_t *hdl, const char *name, boolean_t dryrun,
|
||||
if (!hdl->libzfs_printerr || config == NULL)
|
||||
return;
|
||||
|
||||
if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0)
|
||||
if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0 ||
|
||||
nvlist_lookup_nvlist(nv, ZPOOL_CONFIG_REWIND_INFO, &nv) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0)
|
||||
return;
|
||||
@ -1287,6 +1487,7 @@ zpool_explain_recover(libzfs_handle_t *hdl, const char *name, int reason,
|
||||
|
||||
/* All attempted rewinds failed if ZPOOL_CONFIG_LOAD_TIME missing */
|
||||
if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nv) != 0 ||
|
||||
nvlist_lookup_nvlist(nv, ZPOOL_CONFIG_REWIND_INFO, &nv) != 0 ||
|
||||
nvlist_lookup_uint64(nv, ZPOOL_CONFIG_LOAD_TIME, &rewindto) != 0)
|
||||
goto no_info;
|
||||
|
||||
@ -1409,6 +1610,30 @@ print_vdev_tree(libzfs_handle_t *hdl, const char *name, nvlist_t *nv,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
zpool_print_unsup_feat(nvlist_t *config)
|
||||
{
|
||||
nvlist_t *nvinfo, *unsup_feat;
|
||||
|
||||
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO, &nvinfo) ==
|
||||
0);
|
||||
verify(nvlist_lookup_nvlist(nvinfo, ZPOOL_CONFIG_UNSUP_FEAT,
|
||||
&unsup_feat) == 0);
|
||||
|
||||
for (nvpair_t *nvp = nvlist_next_nvpair(unsup_feat, NULL); nvp != NULL;
|
||||
nvp = nvlist_next_nvpair(unsup_feat, nvp)) {
|
||||
char *desc;
|
||||
|
||||
verify(nvpair_type(nvp) == DATA_TYPE_STRING);
|
||||
verify(nvpair_value_string(nvp, &desc) == 0);
|
||||
|
||||
if (strlen(desc) > 0)
|
||||
(void) printf("\t%s (%s)\n", nvpair_name(nvp), desc);
|
||||
else
|
||||
(void) printf("\t%s\n", nvpair_name(nvp));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Import the given pool using the known configuration and a list of
|
||||
* properties to be set. The configuration should have come from
|
||||
@ -1515,6 +1740,22 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
|
||||
|
||||
switch (error) {
|
||||
case ENOTSUP:
|
||||
if (nv != NULL && nvlist_lookup_nvlist(nv,
|
||||
ZPOOL_CONFIG_LOAD_INFO, &nvinfo) == 0 &&
|
||||
nvlist_exists(nvinfo, ZPOOL_CONFIG_UNSUP_FEAT)) {
|
||||
(void) printf(dgettext(TEXT_DOMAIN, "This "
|
||||
"pool uses the following feature(s) not "
|
||||
"supported by this system:\n"));
|
||||
zpool_print_unsup_feat(nv);
|
||||
if (nvlist_exists(nvinfo,
|
||||
ZPOOL_CONFIG_CAN_RDONLY)) {
|
||||
(void) printf(dgettext(TEXT_DOMAIN,
|
||||
"All unsupported features are only "
|
||||
"required for writing to the pool."
|
||||
"\nThe pool can be imported using "
|
||||
"'-o readonly=on'.\n"));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Unsupported version.
|
||||
*/
|
||||
@ -2355,7 +2596,7 @@ zpool_vdev_attach(zpool_handle_t *zhp,
|
||||
uint_t children;
|
||||
nvlist_t *config_root;
|
||||
libzfs_handle_t *hdl = zhp->zpool_hdl;
|
||||
boolean_t rootpool = pool_is_bootable(zhp);
|
||||
boolean_t rootpool = zpool_is_bootable(zhp);
|
||||
|
||||
if (replacing)
|
||||
(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
|
||||
@ -2966,6 +3207,46 @@ zpool_vdev_clear(zpool_handle_t *zhp, uint64_t guid)
|
||||
return (zpool_standard_error(hdl, errno, msg));
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the GUID for a pool.
|
||||
*/
|
||||
int
|
||||
zpool_reguid(zpool_handle_t *zhp)
|
||||
{
|
||||
char msg[1024];
|
||||
libzfs_handle_t *hdl = zhp->zpool_hdl;
|
||||
zfs_cmd_t zc = { 0 };
|
||||
|
||||
(void) snprintf(msg, sizeof (msg),
|
||||
dgettext(TEXT_DOMAIN, "cannot reguid '%s'"), zhp->zpool_name);
|
||||
|
||||
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
|
||||
if (zfs_ioctl(hdl, ZFS_IOC_POOL_REGUID, &zc) == 0)
|
||||
return (0);
|
||||
|
||||
return (zpool_standard_error(hdl, errno, msg));
|
||||
}
|
||||
|
||||
/*
|
||||
* Reopen the pool.
|
||||
*/
|
||||
int
|
||||
zpool_reopen(zpool_handle_t *zhp)
|
||||
{
|
||||
zfs_cmd_t zc = { 0 };
|
||||
char msg[1024];
|
||||
libzfs_handle_t *hdl = zhp->zpool_hdl;
|
||||
|
||||
(void) snprintf(msg, sizeof (msg),
|
||||
dgettext(TEXT_DOMAIN, "cannot reopen '%s'"),
|
||||
zhp->zpool_name);
|
||||
|
||||
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
|
||||
if (zfs_ioctl(hdl, ZFS_IOC_POOL_REOPEN, &zc) == 0)
|
||||
return (0);
|
||||
return (zpool_standard_error(hdl, errno, msg));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert from a devid string to a path.
|
||||
*/
|
||||
@ -3599,7 +3880,7 @@ zpool_label_disk(libzfs_handle_t *hdl, zpool_handle_t *zhp, char *name)
|
||||
if (zhp) {
|
||||
nvlist_t *nvroot;
|
||||
|
||||
if (pool_is_bootable(zhp)) {
|
||||
if (zpool_is_bootable(zhp)) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"EFI labeled devices are not supported on root "
|
||||
"pools."));
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -18,8 +18,10 @@
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -213,6 +215,20 @@ check_status(nvlist_t *config, boolean_t isimport)
|
||||
vs->vs_aux == VDEV_AUX_VERSION_NEWER)
|
||||
return (ZPOOL_STATUS_VERSION_NEWER);
|
||||
|
||||
/*
|
||||
* Unsupported feature(s).
|
||||
*/
|
||||
if (vs->vs_state == VDEV_STATE_CANT_OPEN &&
|
||||
vs->vs_aux == VDEV_AUX_UNSUP_FEAT) {
|
||||
nvlist_t *nvinfo;
|
||||
|
||||
verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_LOAD_INFO,
|
||||
&nvinfo) == 0);
|
||||
if (nvlist_exists(nvinfo, ZPOOL_CONFIG_CAN_RDONLY))
|
||||
return (ZPOOL_STATUS_UNSUP_FEAT_WRITE);
|
||||
return (ZPOOL_STATUS_UNSUP_FEAT_READ);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the config is complete.
|
||||
*/
|
||||
@ -300,7 +316,7 @@ check_status(nvlist_t *config, boolean_t isimport)
|
||||
/*
|
||||
* Outdated, but usable, version
|
||||
*/
|
||||
if (version < SPA_VERSION)
|
||||
if (SPA_VERSION_IS_SUPPORTED(version) && version != SPA_VERSION)
|
||||
return (ZPOOL_STATUS_VERSION_OLDER);
|
||||
|
||||
return (ZPOOL_STATUS_OK);
|
||||
|
@ -18,8 +18,10 @@
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -44,6 +46,7 @@
|
||||
|
||||
#include "libzfs_impl.h"
|
||||
#include "zfs_prop.h"
|
||||
#include "zfeature_common.h"
|
||||
|
||||
int
|
||||
libzfs_errno(libzfs_handle_t *hdl)
|
||||
@ -111,7 +114,8 @@ libzfs_error_description(libzfs_handle_t *hdl)
|
||||
case EZFS_RESILVERING:
|
||||
return (dgettext(TEXT_DOMAIN, "currently resilvering"));
|
||||
case EZFS_BADVERSION:
|
||||
return (dgettext(TEXT_DOMAIN, "unsupported version"));
|
||||
return (dgettext(TEXT_DOMAIN, "unsupported version or "
|
||||
"feature"));
|
||||
case EZFS_POOLUNAVAIL:
|
||||
return (dgettext(TEXT_DOMAIN, "pool is unavailable"));
|
||||
case EZFS_DEVOVERFLOW:
|
||||
@ -344,6 +348,7 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
|
||||
switch (error) {
|
||||
case ENXIO:
|
||||
case ENODEV:
|
||||
case EPIPE:
|
||||
zfs_verror(hdl, EZFS_IO, fmt, ap);
|
||||
break;
|
||||
|
||||
@ -627,6 +632,7 @@ libzfs_init(void)
|
||||
|
||||
zfs_prop_init();
|
||||
zpool_prop_init();
|
||||
zpool_feature_init();
|
||||
libzfs_mnttab_init(hdl);
|
||||
|
||||
return (hdl);
|
||||
@ -1280,8 +1286,11 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
|
||||
* this is a pool property or if this isn't a user-defined
|
||||
* dataset property,
|
||||
*/
|
||||
if (prop == ZPROP_INVAL && (type == ZFS_TYPE_POOL ||
|
||||
(!zfs_prop_user(propname) && !zfs_prop_userquota(propname)))) {
|
||||
if (prop == ZPROP_INVAL && ((type == ZFS_TYPE_POOL &&
|
||||
!zpool_prop_feature(propname) &&
|
||||
!zpool_prop_unsupported(propname)) ||
|
||||
(type == ZFS_TYPE_DATASET && !zfs_prop_user(propname) &&
|
||||
!zfs_prop_userquota(propname) && !zfs_prop_written(propname)))) {
|
||||
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
|
||||
"invalid property '%s'"), propname);
|
||||
return (zfs_error(hdl, EZFS_BADPROP,
|
||||
@ -1293,7 +1302,8 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
|
||||
|
||||
entry->pl_prop = prop;
|
||||
if (prop == ZPROP_INVAL) {
|
||||
if ((entry->pl_user_prop = zfs_strdup(hdl, propname)) == NULL) {
|
||||
if ((entry->pl_user_prop = zfs_strdup(hdl, propname)) ==
|
||||
NULL) {
|
||||
free(entry);
|
||||
return (-1);
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
@ -45,6 +46,7 @@ int aok;
|
||||
uint64_t physmem;
|
||||
vnode_t *rootdir = (vnode_t *)0xabcd1234;
|
||||
char hw_serial[HW_HOSTID_LEN];
|
||||
vmem_t *zio_arena = NULL;
|
||||
|
||||
struct utsname utsname = {
|
||||
"userland", "libzpool", "1", "1", "na"
|
||||
@ -424,7 +426,9 @@ vn_rdwr(int uio, vnode_t *vp, void *addr, ssize_t len, offset_t offset,
|
||||
* To simulate partial disk writes, we split writes into two
|
||||
* system calls so that the process can be killed in between.
|
||||
*/
|
||||
split = (len > 0 ? rand() % len : 0);
|
||||
int sectors = len >> SPA_MINBLOCKSHIFT;
|
||||
split = (sectors > 0 ? rand() % sectors : 0) <<
|
||||
SPA_MINBLOCKSHIFT;
|
||||
iolen = pwrite64(vp->v_fd, addr, split, offset);
|
||||
iolen += pwrite64(vp->v_fd, (char *)addr + split,
|
||||
len - split, offset + split);
|
||||
|
@ -20,6 +20,9 @@
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _SYS_ZFS_CONTEXT_H
|
||||
@ -212,6 +215,7 @@ struct proc {
|
||||
};
|
||||
|
||||
extern struct proc p0;
|
||||
#define curproc (&p0)
|
||||
|
||||
#define PS_NONE -1
|
||||
|
||||
@ -327,9 +331,12 @@ extern void kstat_delete(kstat_t *);
|
||||
#define kmem_debugging() 0
|
||||
#define kmem_cache_reap_now(_c) /* nothing */
|
||||
#define kmem_cache_set_move(_c, _cb) /* nothing */
|
||||
#define vmem_qcache_reap(_v) /* nothing */
|
||||
#define POINTER_INVALIDATE(_pp) /* nothing */
|
||||
#define POINTER_IS_VALID(_p) 0
|
||||
|
||||
extern vmem_t *zio_arena;
|
||||
|
||||
typedef umem_cache_t kmem_cache_t;
|
||||
|
||||
typedef enum kmem_cbrc {
|
||||
@ -347,6 +354,16 @@ typedef struct taskq taskq_t;
|
||||
typedef uintptr_t taskqid_t;
|
||||
typedef void (task_func_t)(void *);
|
||||
|
||||
typedef struct taskq_ent {
|
||||
struct taskq_ent *tqent_next;
|
||||
struct taskq_ent *tqent_prev;
|
||||
task_func_t *tqent_func;
|
||||
void *tqent_arg;
|
||||
uintptr_t tqent_flags;
|
||||
} taskq_ent_t;
|
||||
|
||||
#define TQENT_FLAG_PREALLOC 0x1 /* taskq_dispatch_ent used */
|
||||
|
||||
#define TASKQ_PREPOPULATE 0x0001
|
||||
#define TASKQ_CPR_SAFE 0x0002 /* Use CPR safe protocol */
|
||||
#define TASKQ_DYNAMIC 0x0004 /* Use dynamic thread scheduling */
|
||||
@ -358,6 +375,7 @@ typedef void (task_func_t)(void *);
|
||||
#define TQ_NOQUEUE 0x02 /* Do not enqueue if can't dispatch */
|
||||
#define TQ_FRONT 0x08 /* Queue in front */
|
||||
|
||||
|
||||
extern taskq_t *system_taskq;
|
||||
|
||||
extern taskq_t *taskq_create(const char *, int, pri_t, int, int, uint_t);
|
||||
@ -366,6 +384,8 @@ extern taskq_t *taskq_create(const char *, int, pri_t, int, int, uint_t);
|
||||
#define taskq_create_sysdc(a, b, d, e, p, dc, f) \
|
||||
(taskq_create(a, b, maxclsyspri, d, e, f))
|
||||
extern taskqid_t taskq_dispatch(taskq_t *, task_func_t, void *, uint_t);
|
||||
extern void taskq_dispatch_ent(taskq_t *, task_func_t, void *, uint_t,
|
||||
taskq_ent_t *);
|
||||
extern void taskq_destroy(taskq_t *);
|
||||
extern void taskq_wait(taskq_t *);
|
||||
extern int taskq_member(taskq_t *, void *);
|
||||
|
@ -22,19 +22,16 @@
|
||||
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
/*
|
||||
* Copyright 2011 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright 2012 Garrett D'Amore <garrett@damore.org>. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <sys/zfs_context.h>
|
||||
|
||||
int taskq_now;
|
||||
taskq_t *system_taskq;
|
||||
|
||||
typedef struct task {
|
||||
struct task *task_next;
|
||||
struct task *task_prev;
|
||||
task_func_t *task_func;
|
||||
void *task_arg;
|
||||
} task_t;
|
||||
|
||||
#define TASKQ_ACTIVE 0x00010000
|
||||
|
||||
struct taskq {
|
||||
@ -51,18 +48,18 @@ struct taskq {
|
||||
int tq_maxalloc;
|
||||
kcondvar_t tq_maxalloc_cv;
|
||||
int tq_maxalloc_wait;
|
||||
task_t *tq_freelist;
|
||||
task_t tq_task;
|
||||
taskq_ent_t *tq_freelist;
|
||||
taskq_ent_t tq_task;
|
||||
};
|
||||
|
||||
static task_t *
|
||||
static taskq_ent_t *
|
||||
task_alloc(taskq_t *tq, int tqflags)
|
||||
{
|
||||
task_t *t;
|
||||
taskq_ent_t *t;
|
||||
int rv;
|
||||
|
||||
again: if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
|
||||
tq->tq_freelist = t->task_next;
|
||||
tq->tq_freelist = t->tqent_next;
|
||||
} else {
|
||||
if (tq->tq_nalloc >= tq->tq_maxalloc) {
|
||||
if (!(tqflags & KM_SLEEP))
|
||||
@ -87,7 +84,7 @@ again: if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
|
||||
}
|
||||
mutex_exit(&tq->tq_lock);
|
||||
|
||||
t = kmem_alloc(sizeof (task_t), tqflags);
|
||||
t = kmem_alloc(sizeof (taskq_ent_t), tqflags);
|
||||
|
||||
mutex_enter(&tq->tq_lock);
|
||||
if (t != NULL)
|
||||
@ -97,15 +94,15 @@ again: if ((t = tq->tq_freelist) != NULL && tq->tq_nalloc >= tq->tq_minalloc) {
|
||||
}
|
||||
|
||||
static void
|
||||
task_free(taskq_t *tq, task_t *t)
|
||||
task_free(taskq_t *tq, taskq_ent_t *t)
|
||||
{
|
||||
if (tq->tq_nalloc <= tq->tq_minalloc) {
|
||||
t->task_next = tq->tq_freelist;
|
||||
t->tqent_next = tq->tq_freelist;
|
||||
tq->tq_freelist = t;
|
||||
} else {
|
||||
tq->tq_nalloc--;
|
||||
mutex_exit(&tq->tq_lock);
|
||||
kmem_free(t, sizeof (task_t));
|
||||
kmem_free(t, sizeof (taskq_ent_t));
|
||||
mutex_enter(&tq->tq_lock);
|
||||
}
|
||||
|
||||
@ -116,7 +113,7 @@ task_free(taskq_t *tq, task_t *t)
|
||||
taskqid_t
|
||||
taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t tqflags)
|
||||
{
|
||||
task_t *t;
|
||||
taskq_ent_t *t;
|
||||
|
||||
if (taskq_now) {
|
||||
func(arg);
|
||||
@ -130,26 +127,59 @@ taskq_dispatch(taskq_t *tq, task_func_t func, void *arg, uint_t tqflags)
|
||||
return (0);
|
||||
}
|
||||
if (tqflags & TQ_FRONT) {
|
||||
t->task_next = tq->tq_task.task_next;
|
||||
t->task_prev = &tq->tq_task;
|
||||
t->tqent_next = tq->tq_task.tqent_next;
|
||||
t->tqent_prev = &tq->tq_task;
|
||||
} else {
|
||||
t->task_next = &tq->tq_task;
|
||||
t->task_prev = tq->tq_task.task_prev;
|
||||
t->tqent_next = &tq->tq_task;
|
||||
t->tqent_prev = tq->tq_task.tqent_prev;
|
||||
}
|
||||
t->task_next->task_prev = t;
|
||||
t->task_prev->task_next = t;
|
||||
t->task_func = func;
|
||||
t->task_arg = arg;
|
||||
t->tqent_next->tqent_prev = t;
|
||||
t->tqent_prev->tqent_next = t;
|
||||
t->tqent_func = func;
|
||||
t->tqent_arg = arg;
|
||||
t->tqent_flags = 0;
|
||||
cv_signal(&tq->tq_dispatch_cv);
|
||||
mutex_exit(&tq->tq_lock);
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
taskq_dispatch_ent(taskq_t *tq, task_func_t func, void *arg, uint_t flags,
|
||||
taskq_ent_t *t)
|
||||
{
|
||||
ASSERT(func != NULL);
|
||||
ASSERT(!(tq->tq_flags & TASKQ_DYNAMIC));
|
||||
|
||||
/*
|
||||
* Mark it as a prealloc'd task. This is important
|
||||
* to ensure that we don't free it later.
|
||||
*/
|
||||
t->tqent_flags |= TQENT_FLAG_PREALLOC;
|
||||
/*
|
||||
* Enqueue the task to the underlying queue.
|
||||
*/
|
||||
mutex_enter(&tq->tq_lock);
|
||||
|
||||
if (flags & TQ_FRONT) {
|
||||
t->tqent_next = tq->tq_task.tqent_next;
|
||||
t->tqent_prev = &tq->tq_task;
|
||||
} else {
|
||||
t->tqent_next = &tq->tq_task;
|
||||
t->tqent_prev = tq->tq_task.tqent_prev;
|
||||
}
|
||||
t->tqent_next->tqent_prev = t;
|
||||
t->tqent_prev->tqent_next = t;
|
||||
t->tqent_func = func;
|
||||
t->tqent_arg = arg;
|
||||
cv_signal(&tq->tq_dispatch_cv);
|
||||
mutex_exit(&tq->tq_lock);
|
||||
}
|
||||
|
||||
void
|
||||
taskq_wait(taskq_t *tq)
|
||||
{
|
||||
mutex_enter(&tq->tq_lock);
|
||||
while (tq->tq_task.task_next != &tq->tq_task || tq->tq_active != 0)
|
||||
while (tq->tq_task.tqent_next != &tq->tq_task || tq->tq_active != 0)
|
||||
cv_wait(&tq->tq_wait_cv, &tq->tq_lock);
|
||||
mutex_exit(&tq->tq_lock);
|
||||
}
|
||||
@ -158,27 +188,32 @@ static void *
|
||||
taskq_thread(void *arg)
|
||||
{
|
||||
taskq_t *tq = arg;
|
||||
task_t *t;
|
||||
taskq_ent_t *t;
|
||||
boolean_t prealloc;
|
||||
|
||||
mutex_enter(&tq->tq_lock);
|
||||
while (tq->tq_flags & TASKQ_ACTIVE) {
|
||||
if ((t = tq->tq_task.task_next) == &tq->tq_task) {
|
||||
if ((t = tq->tq_task.tqent_next) == &tq->tq_task) {
|
||||
if (--tq->tq_active == 0)
|
||||
cv_broadcast(&tq->tq_wait_cv);
|
||||
cv_wait(&tq->tq_dispatch_cv, &tq->tq_lock);
|
||||
tq->tq_active++;
|
||||
continue;
|
||||
}
|
||||
t->task_prev->task_next = t->task_next;
|
||||
t->task_next->task_prev = t->task_prev;
|
||||
t->tqent_prev->tqent_next = t->tqent_next;
|
||||
t->tqent_next->tqent_prev = t->tqent_prev;
|
||||
t->tqent_next = NULL;
|
||||
t->tqent_prev = NULL;
|
||||
prealloc = t->tqent_flags & TQENT_FLAG_PREALLOC;
|
||||
mutex_exit(&tq->tq_lock);
|
||||
|
||||
rw_enter(&tq->tq_threadlock, RW_READER);
|
||||
t->task_func(t->task_arg);
|
||||
t->tqent_func(t->tqent_arg);
|
||||
rw_exit(&tq->tq_threadlock);
|
||||
|
||||
mutex_enter(&tq->tq_lock);
|
||||
task_free(tq, t);
|
||||
if (!prealloc)
|
||||
task_free(tq, t);
|
||||
}
|
||||
tq->tq_nthreads--;
|
||||
cv_broadcast(&tq->tq_wait_cv);
|
||||
@ -217,8 +252,8 @@ taskq_create(const char *name, int nthreads, pri_t pri,
|
||||
tq->tq_nthreads = nthreads;
|
||||
tq->tq_minalloc = minalloc;
|
||||
tq->tq_maxalloc = maxalloc;
|
||||
tq->tq_task.task_next = &tq->tq_task;
|
||||
tq->tq_task.task_prev = &tq->tq_task;
|
||||
tq->tq_task.tqent_next = &tq->tq_task;
|
||||
tq->tq_task.tqent_prev = &tq->tq_task;
|
||||
tq->tq_threadlist = kmem_alloc(nthreads * sizeof (thread_t), KM_SLEEP);
|
||||
|
||||
if (flags & TASKQ_PREPOPULATE) {
|
||||
|
559
man/man1m/zdb.1m
559
man/man1m/zdb.1m
@ -1,87 +1,484 @@
|
||||
'\" te
|
||||
.\" Copyright (c) 2004, Sun Microsystems, Inc. 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]
|
||||
.TH zdb 1M "31 Oct 2005" "SunOS 5.11" "System Administration Commands"
|
||||
.SH NAME
|
||||
zdb \- ZFS debugger
|
||||
.SH SYNOPSIS
|
||||
'\" t
|
||||
.\"
|
||||
.\" 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.
|
||||
.\"
|
||||
.TH "ZDB" "1M" "February 15, 2012" "" ""
|
||||
|
||||
.SH "NAME"
|
||||
\fBzdb\fR - Display zpool debugging and consistency information
|
||||
|
||||
.SH "SYNOPSIS"
|
||||
\fBzdb\fR [-CumdibcsDvhLXFPA] [-e [-p \fIpath\fR...]] [-t \fItxg\fR]
|
||||
\fIpoolname\fR [\fIobject\fR ...]
|
||||
|
||||
.P
|
||||
\fBzdb\fR [-divPA] [-e [-p \fIpath\fR...]] \fIdataset\fR [\fIobject\fR ...]
|
||||
|
||||
.P
|
||||
\fBzdb\fR -m [-LXFPA] [-t \fItxg\fR] [-e [-p \fIpath\fR...]] \fIpoolname\fR
|
||||
[\fIvdev\fR [\fImetaslab\fR ...]]
|
||||
|
||||
.P
|
||||
\fBzdb\fR -R [-A] [-e [-p \fIpath\fR...]] \fIpoolname\fR
|
||||
\fIvdev\fR:\fIoffset\fR:\fIsize\fR[:\fIflags\fR]
|
||||
|
||||
.P
|
||||
\fBzdb\fR -S [-AP] [-e [-p \fIpath\fR...]] \fIpoolname\fR
|
||||
|
||||
.P
|
||||
\fBzdb\fR -l [-uA] \fIdevice\fR
|
||||
|
||||
.P
|
||||
\fBzdb\fR -C [-A] [-U \fIcache\fR]
|
||||
|
||||
.SH "DESCRIPTION"
|
||||
The \fBzdb\fR 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 (and facilities) may change. This is neither
|
||||
a fsck(1M) nor an fsdb(1M) utility.
|
||||
|
||||
.P
|
||||
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.
|
||||
|
||||
.P
|
||||
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:
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-b\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Display statistics regarding the number, size (logical, physical and
|
||||
allocated) and deduplication of blocks.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-c\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Verify the checksum of all metadata blocks while printing block statistics
|
||||
(see \fB-b\fR).
|
||||
.sp
|
||||
If specified multiple times, verify the checksums of all blocks.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-C\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Display information about the configuration. If specified with no other
|
||||
options, instead display information about the cache file
|
||||
(\fB/etc/zfs/zpool.cache\fR). To specify the cache file to display, see
|
||||
\fB-U\fR.
|
||||
.P
|
||||
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 \fB-e\fR also display the configuration that would be used were the
|
||||
pool to be imported.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-d\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Display information about datasets. Specified once, displays basic dataset
|
||||
information: ID, create transaction, size, and object count.
|
||||
.sp
|
||||
If specified multiple times provides greater and greater verbosity.
|
||||
.sp
|
||||
If object IDs are specified, display information about those specific objects only.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-D\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Display deduplication statistics, including the deduplication ratio (dedup),
|
||||
compression ratio (compress), inflation due to the zfs copies property
|
||||
(copies), and an overall effective ratio (dedup * compress / copies).
|
||||
.sp
|
||||
If specified twice, display a histogram of deduplication statistics, showing
|
||||
the allocated (physically present on disk) and referenced (logically
|
||||
referenced in the pool) block counts and sizes by reference count.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-h\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Display pool history similar to \fBzpool history\fR, but include internal
|
||||
changes, transaction, and dataset information.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-i\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Display information about intent log (ZIL) entries relating to each
|
||||
dataset. If specified multiple times, display counts of each intent log
|
||||
transaction type.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-l\fR \fIdevice\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Display the vdev labels from the specified device. If the \fB-u\fR option is
|
||||
also specified, also display the uberblocks on this device.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-L\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Disable leak tracing and the loading of space maps. By default, \fBzdb\fR
|
||||
verifies that all non-free blocks are referenced, which can be very expensive.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-m\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Display the offset, spacemap, and free space of each metaslab.
|
||||
When specified twice, also display information about the maximum contiguous
|
||||
free space and the percentage of free space in each space map. When specified
|
||||
three times display every spacemap record.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-R\fR \fIpoolname\fR \fIvdev\fR:\fIoffset\fR:\fIsize\fR[:\fIflags\fR]
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
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 \'r\' flag, below.
|
||||
.sp
|
||||
The block is specified in terms of a colon-separated tuple \fIvdev\fR (an
|
||||
integer vdev identifier) \fIoffset\fR (the offset within the vdev) \fIsize\fR
|
||||
(the size of the block to read) and, optionally, \fIflags\fR (a set of flags,
|
||||
described below).
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fBb\fR \fIoffset\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Print block pointer
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fBd\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Decompress the block
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fBe\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Byte swap the block
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fBg\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Dump gang block header
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fBi\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Dump indirect block
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fBr\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Dump raw uninterpreted block data
|
||||
.RE
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-s\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Report statistics on \fBzdb\fR\'s I/O. Display operation counts, bandwidth,
|
||||
and error counts of I/O to the pool from \fBzdb\fR.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-S\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Simulate the effects of deduplication, constructing a DDT and then display
|
||||
that DDT as with \fB-DD\fR.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-u\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Display the current uberblock.
|
||||
.RE
|
||||
|
||||
.P
|
||||
Other options:
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-A\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Do not abort should any assertion fail.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-AA\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Enable panic recovery, certain errors which would otherwise be fatal are
|
||||
demoted to warnings.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-AAA\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Do not abort if asserts fail and also enable panic recovery.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-e\fR [-p \fIpath\fR]...
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Operate on an exported pool, not present in \fB/etc/zfs/zpool.cache\fR. The
|
||||
\fB-p\fR flag specifies the path under which devices are to be searched.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-F\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Attempt to make an unreadable pool readable by trying progressively older
|
||||
transactions.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-P\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Print numbers in an unscaled form more amenable to parsing, eg. 1000000 rather
|
||||
than 1M.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-t\fR \fItransaction\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Specify the highest transaction to use when searching for uberblocks. See also
|
||||
the \fB-u\fR and \fB-l\fR options for a means to see the available uberblocks
|
||||
and their associated transaction numbers.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-U\fR \fIcachefile\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Use a cache file other than \fB/etc/zfs/zpool.cache\fR. This option is only
|
||||
valid with \fB-C\fR
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-v\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Enable verbosity. Specify multiple times for increased verbosity.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.na
|
||||
\fB-X\fR
|
||||
.ad
|
||||
.sp .6
|
||||
.RS 4n
|
||||
Attempt \'extreme\' transaction rewind, that is attempt the same recovery as
|
||||
\fB-F\fR but read transactions otherwise deemed too old.
|
||||
.RE
|
||||
|
||||
.P
|
||||
Specifying a display option more than once enables verbosity for only that
|
||||
option, with more occurrences enabling more verbosity.
|
||||
.P
|
||||
If no options are specified, all information about the named pool will be
|
||||
displayed at default verbosity.
|
||||
|
||||
.SH "EXAMPLES"
|
||||
.LP
|
||||
\fBExample 1 \fRDisplay the configuration of imported pool 'rpool'
|
||||
.sp
|
||||
.in +2
|
||||
.nf
|
||||
\fBzdb\fR \fIpool\fR
|
||||
# zdb -C rpool
|
||||
|
||||
MOS Configuration:
|
||||
version: 28
|
||||
name: 'rpool'
|
||||
...
|
||||
.fi
|
||||
|
||||
.SH DESCRIPTION
|
||||
.sp
|
||||
.LP
|
||||
The \fBzdb\fR command is used by support engineers to diagnose failures and
|
||||
gather statistics. Since the \fBZFS\fR file system is always consistent on disk
|
||||
and is self-repairing, \fBzdb\fR should only be run under the direction by a
|
||||
support engineer.
|
||||
.sp
|
||||
.LP
|
||||
If no arguments are specified, \fBzdb\fR, performs basic consistency checks on
|
||||
the pool and associated datasets, and report any problems detected.
|
||||
.sp
|
||||
.LP
|
||||
Any options supported by this command are internal to Sun and subject to change
|
||||
at any time.
|
||||
.SH EXIT STATUS
|
||||
.sp
|
||||
.LP
|
||||
The following exit values are returned:
|
||||
.sp
|
||||
.ne 2
|
||||
.mk
|
||||
.na
|
||||
\fB\fB0\fR\fR
|
||||
.ad
|
||||
.RS 5n
|
||||
.rt
|
||||
The pool is consistent.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.mk
|
||||
.na
|
||||
\fB\fB1\fR\fR
|
||||
.ad
|
||||
.RS 5n
|
||||
.rt
|
||||
An error was detected.
|
||||
.RE
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.mk
|
||||
.na
|
||||
\fB\fB2\fR\fR
|
||||
.ad
|
||||
.RS 5n
|
||||
.rt
|
||||
Invalid command line options were specified.
|
||||
.RE
|
||||
|
||||
.SH ATTRIBUTES
|
||||
.sp
|
||||
.LP
|
||||
See \fBattributes\fR(5) for descriptions of the following attributes:
|
||||
.in -2
|
||||
.sp
|
||||
|
||||
.sp
|
||||
.TS
|
||||
tab() box;
|
||||
cw(2.75i) |cw(2.75i)
|
||||
lw(2.75i) |lw(2.75i)
|
||||
.
|
||||
ATTRIBUTE TYPEATTRIBUTE VALUE
|
||||
_
|
||||
Interface StabilityUnstable
|
||||
.TE
|
||||
|
||||
.SH SEE ALSO
|
||||
.sp
|
||||
.LP
|
||||
\fBzfs\fR(1M), \fBzpool\fR(1M), \fBattributes\fR(5)
|
||||
\fBExample 2 \fRDisplay basic dataset information about 'rpool'
|
||||
.sp
|
||||
.in +2
|
||||
.nf
|
||||
# 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
|
||||
...
|
||||
.fi
|
||||
.in -2
|
||||
.sp
|
||||
|
||||
.LP
|
||||
\fBExample 3 \fRDisplay basic information about object 0 in
|
||||
'rpool/export/home'
|
||||
.sp
|
||||
.in +2
|
||||
.nf
|
||||
# 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
|
||||
.fi
|
||||
.in -2
|
||||
.sp
|
||||
|
||||
.LP
|
||||
\fBExample 4 \fRDisplay the predicted effect of enabling deduplication on 'rpool'
|
||||
.sp
|
||||
.in +2
|
||||
.nf
|
||||
# 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
|
||||
.fi
|
||||
.in -2
|
||||
.sp
|
||||
|
||||
.SH "SEE ALSO"
|
||||
zfs(1M), zpool(1M)
|
||||
|
612
man/man1m/zfs.1m
612
man/man1m/zfs.1m
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -3,7 +3,7 @@
|
||||
.\" 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]
|
||||
.TH zstreamdump 1M "21 Sep 2009" "SunOS 5.11" "System Administration Commands"
|
||||
.TH ZSTREAMDUMP 1M "Sep 21, 2009"
|
||||
.SH NAME
|
||||
zstreamdump \- filter data in zfs send stream
|
||||
.SH SYNOPSIS
|
||||
@ -24,7 +24,6 @@ command, then displays headers and some statistics from that output. See
|
||||
The following options are supported:
|
||||
.sp
|
||||
.ne 2
|
||||
.mk
|
||||
.na
|
||||
\fB\fB-C\fR\fR
|
||||
.ad
|
||||
@ -35,7 +34,6 @@ Suppress the validation of checksums.
|
||||
|
||||
.sp
|
||||
.ne 2
|
||||
.mk
|
||||
.na
|
||||
\fB\fB-v\fR\fR
|
||||
.ad
|
||||
@ -52,13 +50,12 @@ See \fBattributes\fR(5) for descriptions of the following attributes:
|
||||
|
||||
.sp
|
||||
.TS
|
||||
tab() box;
|
||||
cw(2.75i) |cw(2.75i)
|
||||
lw(2.75i) |lw(2.75i)
|
||||
.
|
||||
ATTRIBUTE TYPEATTRIBUTE VALUE
|
||||
box;
|
||||
c | c
|
||||
l | l .
|
||||
ATTRIBUTE TYPE ATTRIBUTE VALUE
|
||||
_
|
||||
Interface StabilityUncommitted
|
||||
Interface Stability Uncommitted
|
||||
.TE
|
||||
|
||||
.SH SEE ALSO
|
||||
|
Loading…
x
Reference in New Issue
Block a user