summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormm <mm@FreeBSD.org>2013-03-01 21:01:45 +0000
committermm <mm@FreeBSD.org>2013-03-01 21:01:45 +0000
commit7536305c3efabe443f1f43800fac6cab0a4cab1e (patch)
tree7fcb3883c06157f9ae23b19b2e00ea82abf90719
parent5adf0a759371d12ed632e868221180f2fb38ffea (diff)
downloadFreeBSD-src-7536305c3efabe443f1f43800fac6cab0a4cab1e.zip
FreeBSD-src-7536305c3efabe443f1f43800fac6cab0a4cab1e.tar.gz
Update vendor/illumos/dist and vendor-sys/illumos/dist
to illumos-gate 13973:4972ab336f54 Illumos ZFS issues: 3464 zfs synctask code needs restructuring
-rw-r--r--cmd/zdb/zdb.c9
-rw-r--r--cmd/zfs/zfs_main.c116
-rw-r--r--cmd/zhack/zhack.c27
-rw-r--r--cmd/ztest/ztest.c190
-rw-r--r--lib/libzfs/common/libzfs.h6
-rw-r--r--lib/libzfs/common/libzfs_dataset.c316
-rw-r--r--lib/libzfs/common/libzfs_sendrecv.c12
-rw-r--r--lib/libzfs_core/common/libzfs_core.c102
-rw-r--r--lib/libzfs_core/common/libzfs_core.h4
-rw-r--r--lib/libzpool/common/kernel.c5
-rw-r--r--lib/libzpool/common/sys/zfs_context.h15
-rw-r--r--man/man1m/zfs.1m6
12 files changed, 520 insertions, 288 deletions
diff --git a/cmd/zdb/zdb.c b/cmd/zdb/zdb.c
index 2234a71..46a630f 100644
--- a/cmd/zdb/zdb.c
+++ b/cmd/zdb/zdb.c
@@ -1658,7 +1658,9 @@ dump_dir(objset_t *os)
int print_header = 1;
int i, error;
+ dsl_pool_config_enter(dmu_objset_pool(os), FTAG);
dmu_objset_fast_stat(os, &dds);
+ dsl_pool_config_exit(dmu_objset_pool(os), FTAG);
if (dds.dds_type < DMU_OST_NUMTYPES)
type = objset_types[dds.dds_type];
@@ -2109,7 +2111,6 @@ zdb_blkptr_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp,
zio_nowait(zio_read(NULL, spa, bp, data, size,
zdb_blkptr_done, zcb, ZIO_PRIORITY_ASYNC_READ, flags, zb));
-
}
zcb->zcb_readfails = 0;
@@ -2297,8 +2298,10 @@ dump_block_stats(spa_t *spa)
*/
(void) bpobj_iterate_nofree(&spa->spa_deferred_bpobj,
count_block_cb, &zcb, NULL);
- (void) bpobj_iterate_nofree(&spa->spa_dsl_pool->dp_free_bpobj,
- count_block_cb, &zcb, NULL);
+ if (spa_version(spa) >= SPA_VERSION_DEADLISTS) {
+ (void) bpobj_iterate_nofree(&spa->spa_dsl_pool->dp_free_bpobj,
+ count_block_cb, &zcb, NULL);
+ }
if (spa_feature_is_active(spa,
&spa_feature_table[SPA_FEATURE_ASYNC_DESTROY])) {
VERIFY3U(0, ==, bptree_iterate(spa->spa_meta_objset,
diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index e5e35e6..5e95aed 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -898,6 +898,7 @@ typedef struct destroy_cbdata {
boolean_t cb_parsable;
boolean_t cb_dryrun;
nvlist_t *cb_nvl;
+ nvlist_t *cb_batchedsnaps;
/* first snap in contiguous run */
char *cb_firstsnap;
@@ -994,9 +995,27 @@ destroy_callback(zfs_handle_t *zhp, void *data)
zfs_close(zhp);
return (0);
}
+ if (cb->cb_dryrun) {
+ zfs_close(zhp);
+ return (0);
+ }
+
+ /*
+ * We batch up all contiguous snapshots (even of different
+ * filesystems) and destroy them with one ioctl. We can't
+ * simply do all snap deletions and then all fs deletions,
+ * because we must delete a clone before its origin.
+ */
+ if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) {
+ fnvlist_add_boolean(cb->cb_batchedsnaps, name);
+ } else {
+ int error = zfs_destroy_snaps_nvl(g_zfs,
+ cb->cb_batchedsnaps, B_FALSE);
+ fnvlist_free(cb->cb_batchedsnaps);
+ cb->cb_batchedsnaps = fnvlist_alloc();
- if (!cb->cb_dryrun) {
- if (zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 ||
+ if (error != 0 ||
+ zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 ||
zfs_destroy(zhp, cb->cb_defer_destroy) != 0) {
zfs_close(zhp);
return (-1);
@@ -1152,8 +1171,10 @@ static int
zfs_do_destroy(int argc, char **argv)
{
destroy_cbdata_t cb = { 0 };
+ int rv = 0;
+ int err = 0;
int c;
- zfs_handle_t *zhp;
+ zfs_handle_t *zhp = NULL;
char *at;
zfs_type_t type = ZFS_TYPE_DATASET;
@@ -1207,11 +1228,9 @@ zfs_do_destroy(int argc, char **argv)
at = strchr(argv[0], '@');
if (at != NULL) {
- int err = 0;
/* Build the list of snaps to destroy in cb_nvl. */
- if (nvlist_alloc(&cb.cb_nvl, NV_UNIQUE_NAME, 0) != 0)
- nomem();
+ cb.cb_nvl = fnvlist_alloc();
*at = '\0';
zhp = zfs_open(g_zfs, argv[0],
@@ -1222,17 +1241,15 @@ zfs_do_destroy(int argc, char **argv)
cb.cb_snapspec = at + 1;
if (gather_snapshots(zfs_handle_dup(zhp), &cb) != 0 ||
cb.cb_error) {
- zfs_close(zhp);
- nvlist_free(cb.cb_nvl);
- return (1);
+ rv = 1;
+ goto out;
}
if (nvlist_empty(cb.cb_nvl)) {
(void) fprintf(stderr, gettext("could not find any "
"snapshots to destroy; check snapshot names.\n"));
- zfs_close(zhp);
- nvlist_free(cb.cb_nvl);
- return (1);
+ rv = 1;
+ goto out;
}
if (cb.cb_verbose) {
@@ -1251,18 +1268,26 @@ zfs_do_destroy(int argc, char **argv)
}
if (!cb.cb_dryrun) {
- if (cb.cb_doclones)
+ if (cb.cb_doclones) {
+ cb.cb_batchedsnaps = fnvlist_alloc();
err = destroy_clones(&cb);
+ if (err == 0) {
+ err = zfs_destroy_snaps_nvl(g_zfs,
+ cb.cb_batchedsnaps, B_FALSE);
+ }
+ if (err != 0) {
+ rv = 1;
+ goto out;
+ }
+ }
if (err == 0) {
- err = zfs_destroy_snaps_nvl(zhp, cb.cb_nvl,
+ err = zfs_destroy_snaps_nvl(g_zfs, cb.cb_nvl,
cb.cb_defer_destroy);
}
}
- zfs_close(zhp);
- nvlist_free(cb.cb_nvl);
if (err != 0)
- return (1);
+ rv = 1;
} else {
/* Open the given dataset */
if ((zhp = zfs_open(g_zfs, argv[0], type)) == NULL)
@@ -1283,8 +1308,8 @@ zfs_do_destroy(int argc, char **argv)
zfs_get_name(zhp));
(void) fprintf(stderr, gettext("use 'zpool destroy %s' "
"to destroy the pool itself\n"), zfs_get_name(zhp));
- zfs_close(zhp);
- return (1);
+ rv = 1;
+ goto out;
}
/*
@@ -1294,30 +1319,42 @@ zfs_do_destroy(int argc, char **argv)
if (!cb.cb_doclones &&
zfs_iter_dependents(zhp, B_TRUE, destroy_check_dependent,
&cb) != 0) {
- zfs_close(zhp);
- return (1);
+ rv = 1;
+ goto out;
}
if (cb.cb_error) {
- zfs_close(zhp);
- return (1);
+ rv = 1;
+ goto out;
}
+ cb.cb_batchedsnaps = fnvlist_alloc();
if (zfs_iter_dependents(zhp, B_FALSE, destroy_callback,
&cb) != 0) {
- zfs_close(zhp);
- return (1);
+ rv = 1;
+ goto out;
}
/*
* Do the real thing. The callback will close the
* handle regardless of whether it succeeds or not.
*/
- if (destroy_callback(zhp, &cb) != 0)
- return (1);
+ err = destroy_callback(zhp, &cb);
+ zhp = NULL;
+ if (err == 0) {
+ err = zfs_destroy_snaps_nvl(g_zfs,
+ cb.cb_batchedsnaps, cb.cb_defer_destroy);
+ }
+ if (err != 0)
+ rv = 1;
}
- return (0);
+out:
+ fnvlist_free(cb.cb_batchedsnaps);
+ fnvlist_free(cb.cb_nvl);
+ if (zhp != NULL)
+ zfs_close(zhp);
+ return (rv);
}
static boolean_t
@@ -5052,28 +5089,12 @@ cleanup2:
return (error);
}
-/*
- * zfs allow [-r] [-t] <tag> <snap> ...
- *
- * -r Recursively hold
- * -t Temporary hold (hidden option)
- *
- * Apply a user-hold with the given tag to the list of snapshots.
- */
static int
zfs_do_allow(int argc, char **argv)
{
return (zfs_do_allow_unallow_impl(argc, argv, B_FALSE));
}
-/*
- * zfs unallow [-r] [-t] <tag> <snap> ...
- *
- * -r Recursively hold
- * -t Temporary hold (hidden option)
- *
- * Apply a user-hold with the given tag to the list of snapshots.
- */
static int
zfs_do_unallow(int argc, char **argv)
{
@@ -5087,7 +5108,6 @@ zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
int i;
const char *tag;
boolean_t recursive = B_FALSE;
- boolean_t temphold = B_FALSE;
const char *opts = holding ? "rt" : "r";
int c;
@@ -5097,9 +5117,6 @@ zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
case 'r':
recursive = B_TRUE;
break;
- case 't':
- temphold = B_TRUE;
- break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
@@ -5148,7 +5165,7 @@ zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
}
if (holding) {
if (zfs_hold(zhp, delim+1, tag, recursive,
- temphold, B_FALSE, -1, 0, 0) != 0)
+ B_FALSE, -1) != 0)
++errors;
} else {
if (zfs_release(zhp, delim+1, tag, recursive) != 0)
@@ -5164,7 +5181,6 @@ zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
* zfs hold [-r] [-t] <tag> <snap> ...
*
* -r Recursively hold
- * -t Temporary hold (hidden option)
*
* Apply a user-hold with the given tag to the list of snapshots.
*/
diff --git a/cmd/zhack/zhack.c b/cmd/zhack/zhack.c
index 7cc83d2..d9ad36c 100644
--- a/cmd/zhack/zhack.c
+++ b/cmd/zhack/zhack.c
@@ -46,6 +46,7 @@
#include <sys/zio_checksum.h>
#include <sys/zio_compress.h>
#include <sys/zfeature.h>
+#include <sys/dmu_tx.h>
#undef ZFS_MAXNAMELEN
#undef verify
#include <libzfs.h>
@@ -273,10 +274,10 @@ zhack_do_feature_stat(int argc, char **argv)
}
static void
-feature_enable_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+feature_enable_sync(void *arg, dmu_tx_t *tx)
{
- spa_t *spa = arg1;
- zfeature_info_t *feature = arg2;
+ spa_t *spa = dmu_tx_pool(tx)->dp_spa;
+ zfeature_info_t *feature = arg;
spa_feature_enable(spa, feature, tx);
spa_history_log_internal(spa, "zhack enable feature", tx,
@@ -344,8 +345,8 @@ zhack_do_feature_enable(int argc, char **argv)
if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
fatal("feature already enabled: %s", feature.fi_guid);
- VERIFY3U(0, ==, dsl_sync_task_do(spa->spa_dsl_pool, NULL,
- feature_enable_sync, spa, &feature, 5));
+ VERIFY0(dsl_sync_task(spa_name(spa), NULL,
+ feature_enable_sync, &feature, 5));
spa_close(spa, FTAG);
@@ -353,10 +354,10 @@ zhack_do_feature_enable(int argc, char **argv)
}
static void
-feature_incr_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+feature_incr_sync(void *arg, dmu_tx_t *tx)
{
- spa_t *spa = arg1;
- zfeature_info_t *feature = arg2;
+ spa_t *spa = dmu_tx_pool(tx)->dp_spa;
+ zfeature_info_t *feature = arg;
spa_feature_incr(spa, feature, tx);
spa_history_log_internal(spa, "zhack feature incr", tx,
@@ -364,10 +365,10 @@ feature_incr_sync(void *arg1, void *arg2, dmu_tx_t *tx)
}
static void
-feature_decr_sync(void *arg1, void *arg2, dmu_tx_t *tx)
+feature_decr_sync(void *arg, dmu_tx_t *tx)
{
- spa_t *spa = arg1;
- zfeature_info_t *feature = arg2;
+ spa_t *spa = dmu_tx_pool(tx)->dp_spa;
+ zfeature_info_t *feature = arg;
spa_feature_decr(spa, feature, tx);
spa_history_log_internal(spa, "zhack feature decr", tx,
@@ -442,8 +443,8 @@ zhack_do_feature_ref(int argc, char **argv)
if (decr && !spa_feature_is_active(spa, &feature))
fatal("feature refcount already 0: %s", feature.fi_guid);
- VERIFY3U(0, ==, dsl_sync_task_do(spa->spa_dsl_pool, NULL,
- decr ? feature_decr_sync : feature_incr_sync, spa, &feature, 5));
+ VERIFY0(dsl_sync_task(spa_name(spa), NULL,
+ decr ? feature_decr_sync : feature_incr_sync, &feature, 5));
spa_close(spa, FTAG);
}
diff --git a/cmd/ztest/ztest.c b/cmd/ztest/ztest.c
index b51ab84..ed46055 100644
--- a/cmd/ztest/ztest.c
+++ b/cmd/ztest/ztest.c
@@ -103,10 +103,12 @@
#include <sys/metaslab_impl.h>
#include <sys/dsl_prop.h>
#include <sys/dsl_dataset.h>
+#include <sys/dsl_destroy.h>
#include <sys/dsl_scan.h>
#include <sys/zio_checksum.h>
#include <sys/refcount.h>
#include <sys/zfeature.h>
+#include <sys/dsl_userhold.h>
#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
@@ -365,7 +367,7 @@ ztest_info_t ztest_info[] = {
{ ztest_scrub, 1, &zopt_rarely },
{ ztest_spa_upgrade, 1, &zopt_rarely },
{ ztest_dsl_dataset_promote_busy, 1, &zopt_rarely },
- { ztest_vdev_attach_detach, 1, &zopt_rarely },
+ { ztest_vdev_attach_detach, 1, &zopt_sometimes },
{ ztest_vdev_LUN_growth, 1, &zopt_rarely },
{ ztest_vdev_add_remove, 1,
&ztest_opts.zo_vdevtime },
@@ -1006,9 +1008,8 @@ ztest_dsl_prop_set_uint64(char *osname, zfs_prop_t prop, uint64_t value,
uint64_t curval;
int error;
- error = dsl_prop_set(osname, propname,
- (inherit ? ZPROP_SRC_NONE : ZPROP_SRC_LOCAL),
- sizeof (value), 1, &value);
+ error = dsl_prop_set_int(osname, propname,
+ (inherit ? ZPROP_SRC_NONE : ZPROP_SRC_LOCAL), value);
if (error == ENOSPC) {
ztest_record_enospc(FTAG);
@@ -1016,8 +1017,7 @@ ztest_dsl_prop_set_uint64(char *osname, zfs_prop_t prop, uint64_t value,
}
ASSERT0(error);
- VERIFY3U(dsl_prop_get(osname, propname, sizeof (curval),
- 1, &curval, setpoint), ==, 0);
+ VERIFY0(dsl_prop_get_integer(osname, propname, &curval, setpoint));
if (ztest_opts.zo_verbose >= 6) {
VERIFY(zfs_prop_index_to_string(prop, curval, &valname) == 0);
@@ -2479,8 +2479,7 @@ ztest_vdev_add_remove(ztest_ds_t *zd, uint64_t id)
int error;
VERIFY(mutex_lock(&ztest_vdev_lock) == 0);
- leaves =
- MAX(zs->zs_mirrors + zs->zs_splits, 1) * ztest_opts.zo_raidz;
+ leaves = MAX(zs->zs_mirrors + zs->zs_splits, 1) * ztest_opts.zo_raidz;
spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
@@ -3180,7 +3179,7 @@ ztest_objset_destroy_cb(const char *name, void *arg)
/*
* Verify that the dataset contains a directory object.
*/
- VERIFY3U(0, ==, dmu_objset_hold(name, FTAG, &os));
+ VERIFY0(dmu_objset_own(name, DMU_OST_OTHER, B_TRUE, FTAG, &os));
error = dmu_object_info(os, ZTEST_DIROBJ, &doi);
if (error != ENOENT) {
/* We could have crashed in the middle of destroying it */
@@ -3188,12 +3187,16 @@ ztest_objset_destroy_cb(const char *name, void *arg)
ASSERT3U(doi.doi_type, ==, DMU_OT_ZAP_OTHER);
ASSERT3S(doi.doi_physical_blocks_512, >=, 0);
}
- dmu_objset_rele(os, FTAG);
+ dmu_objset_disown(os, FTAG);
/*
* Destroy the dataset.
*/
- VERIFY3U(0, ==, dmu_objset_destroy(name, B_FALSE));
+ if (strchr(name, '@') != NULL) {
+ VERIFY0(dsl_destroy_snapshot(name, B_FALSE));
+ } else {
+ VERIFY0(dsl_destroy_head(name));
+ }
return (0);
}
@@ -3203,16 +3206,17 @@ ztest_snapshot_create(char *osname, uint64_t id)
char snapname[MAXNAMELEN];
int error;
- (void) snprintf(snapname, MAXNAMELEN, "%s@%llu", osname,
- (u_longlong_t)id);
+ (void) snprintf(snapname, sizeof (snapname), "%llu", (u_longlong_t)id);
- error = dmu_objset_snapshot_one(osname, strchr(snapname, '@') + 1);
+ error = dmu_objset_snapshot_one(osname, snapname);
if (error == ENOSPC) {
ztest_record_enospc(FTAG);
return (B_FALSE);
}
- if (error != 0 && error != EEXIST)
- fatal(0, "ztest_snapshot_create(%s) = %d", snapname, error);
+ if (error != 0 && error != EEXIST) {
+ fatal(0, "ztest_snapshot_create(%s@%s) = %d", osname,
+ snapname, error);
+ }
return (B_TRUE);
}
@@ -3225,7 +3229,7 @@ ztest_snapshot_destroy(char *osname, uint64_t id)
(void) snprintf(snapname, MAXNAMELEN, "%s@%llu", osname,
(u_longlong_t)id);
- error = dmu_objset_destroy(snapname, B_FALSE);
+ error = dsl_destroy_snapshot(snapname, B_FALSE);
if (error != 0 && error != ENOENT)
fatal(0, "ztest_snapshot_destroy(%s) = %d", snapname, error);
return (B_TRUE);
@@ -3271,7 +3275,8 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id)
/*
* Verify that the destroyed dataset is no longer in the namespace.
*/
- VERIFY3U(ENOENT, ==, dmu_objset_hold(name, FTAG, &os));
+ VERIFY3U(ENOENT, ==, dmu_objset_own(name, DMU_OST_OTHER, B_TRUE,
+ FTAG, &os));
/*
* Verify that we can create a new dataset.
@@ -3286,8 +3291,7 @@ ztest_dmu_objset_create_destroy(ztest_ds_t *zd, uint64_t id)
fatal(0, "dmu_objset_create(%s) = %d", name, error);
}
- VERIFY3U(0, ==,
- dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, FTAG, &os));
+ VERIFY0(dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, FTAG, &os));
ztest_zd_init(&zdtmp, NULL, os);
@@ -3363,21 +3367,21 @@ ztest_dsl_dataset_cleanup(char *osname, uint64_t id)
(void) snprintf(clone2name, MAXNAMELEN, "%s/c2_%llu", osname, id);
(void) snprintf(snap3name, MAXNAMELEN, "%s@s3_%llu", clone1name, id);
- error = dmu_objset_destroy(clone2name, B_FALSE);
+ error = dsl_destroy_head(clone2name);
if (error && error != ENOENT)
- fatal(0, "dmu_objset_destroy(%s) = %d", clone2name, error);
- error = dmu_objset_destroy(snap3name, B_FALSE);
+ fatal(0, "dsl_destroy_head(%s) = %d", clone2name, error);
+ error = dsl_destroy_snapshot(snap3name, B_FALSE);
if (error && error != ENOENT)
- fatal(0, "dmu_objset_destroy(%s) = %d", snap3name, error);
- error = dmu_objset_destroy(snap2name, B_FALSE);
+ fatal(0, "dsl_destroy_snapshot(%s) = %d", snap3name, error);
+ error = dsl_destroy_snapshot(snap2name, B_FALSE);
if (error && error != ENOENT)
- fatal(0, "dmu_objset_destroy(%s) = %d", snap2name, error);
- error = dmu_objset_destroy(clone1name, B_FALSE);
+ fatal(0, "dsl_destroy_snapshot(%s) = %d", snap2name, error);
+ error = dsl_destroy_head(clone1name);
if (error && error != ENOENT)
- fatal(0, "dmu_objset_destroy(%s) = %d", clone1name, error);
- error = dmu_objset_destroy(snap1name, B_FALSE);
+ fatal(0, "dsl_destroy_head(%s) = %d", clone1name, error);
+ error = dsl_destroy_snapshot(snap1name, B_FALSE);
if (error && error != ENOENT)
- fatal(0, "dmu_objset_destroy(%s) = %d", snap1name, error);
+ fatal(0, "dsl_destroy_snapshot(%s) = %d", snap1name, error);
}
/*
@@ -3386,8 +3390,7 @@ ztest_dsl_dataset_cleanup(char *osname, uint64_t id)
void
ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id)
{
- objset_t *clone;
- dsl_dataset_t *ds;
+ objset_t *os;
char snap1name[MAXNAMELEN];
char clone1name[MAXNAMELEN];
char snap2name[MAXNAMELEN];
@@ -3415,12 +3418,7 @@ ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id)
fatal(0, "dmu_take_snapshot(%s) = %d", snap1name, error);
}
- error = dmu_objset_hold(snap1name, FTAG, &clone);
- if (error)
- fatal(0, "dmu_open_snapshot(%s) = %d", snap1name, error);
-
- error = dmu_objset_clone(clone1name, dmu_objset_ds(clone), 0);
- dmu_objset_rele(clone, FTAG);
+ error = dmu_objset_clone(clone1name, snap1name);
if (error) {
if (error == ENOSPC) {
ztest_record_enospc(FTAG);
@@ -3447,12 +3445,7 @@ ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id)
fatal(0, "dmu_open_snapshot(%s) = %d", snap3name, error);
}
- error = dmu_objset_hold(snap3name, FTAG, &clone);
- if (error)
- fatal(0, "dmu_open_snapshot(%s) = %d", snap3name, error);
-
- error = dmu_objset_clone(clone2name, dmu_objset_ds(clone), 0);
- dmu_objset_rele(clone, FTAG);
+ error = dmu_objset_clone(clone2name, snap3name);
if (error) {
if (error == ENOSPC) {
ztest_record_enospc(FTAG);
@@ -3461,14 +3454,14 @@ ztest_dsl_dataset_promote_busy(ztest_ds_t *zd, uint64_t id)
fatal(0, "dmu_objset_create(%s) = %d", clone2name, error);
}
- error = dsl_dataset_own(snap2name, B_FALSE, FTAG, &ds);
+ error = dmu_objset_own(snap2name, DMU_OST_ANY, B_TRUE, FTAG, &os);
if (error)
- fatal(0, "dsl_dataset_own(%s) = %d", snap2name, error);
+ fatal(0, "dmu_objset_own(%s) = %d", snap2name, error);
error = dsl_dataset_promote(clone2name, NULL);
if (error != EBUSY)
fatal(0, "dsl_dataset_promote(%s), %d, not EBUSY", clone2name,
error);
- dsl_dataset_disown(ds, FTAG);
+ dmu_objset_disown(os, FTAG);
out:
ztest_dsl_dataset_cleanup(osname, id);
@@ -4280,7 +4273,7 @@ ztest_zap_parallel(ztest_ds_t *zd, uint64_t id)
}
count = -1ULL;
- VERIFY(zap_count(os, object, &count) == 0);
+ VERIFY0(zap_count(os, object, &count));
ASSERT(count != -1ULL);
/*
@@ -4591,6 +4584,22 @@ ztest_spa_prop_get_set(ztest_ds_t *zd, uint64_t id)
(void) rw_unlock(&ztest_name_lock);
}
+static int
+user_release_one(const char *snapname, const char *holdname)
+{
+ nvlist_t *snaps, *holds;
+ int error;
+
+ snaps = fnvlist_alloc();
+ holds = fnvlist_alloc();
+ fnvlist_add_boolean(holds, holdname);
+ fnvlist_add_nvlist(snaps, snapname, holds);
+ fnvlist_free(holds);
+ error = dsl_dataset_user_release(snaps, NULL);
+ fnvlist_free(snaps);
+ return (error);
+}
+
/*
* Test snapshot hold/release and deferred destroy.
*/
@@ -4605,22 +4614,30 @@ ztest_dmu_snapshot_hold(ztest_ds_t *zd, uint64_t id)
char clonename[100];
char tag[100];
char osname[MAXNAMELEN];
+ nvlist_t *holds;
(void) rw_rdlock(&ztest_name_lock);
dmu_objset_name(os, osname);
- (void) snprintf(snapname, 100, "sh1_%llu", id);
- (void) snprintf(fullname, 100, "%s@%s", osname, snapname);
- (void) snprintf(clonename, 100, "%s/ch1_%llu", osname, id);
- (void) snprintf(tag, 100, "%tag_%llu", id);
+ (void) snprintf(snapname, sizeof (snapname), "sh1_%llu", id);
+ (void) snprintf(fullname, sizeof (fullname), "%s@%s", osname, snapname);
+ (void) snprintf(clonename, sizeof (clonename),
+ "%s/ch1_%llu", osname, id);
+ (void) snprintf(tag, sizeof (tag), "tag_%llu", id);
/*
* Clean up from any previous run.
*/
- (void) dmu_objset_destroy(clonename, B_FALSE);
- (void) dsl_dataset_user_release(osname, snapname, tag, B_FALSE);
- (void) dmu_objset_destroy(fullname, B_FALSE);
+ error = dsl_destroy_head(clonename);
+ if (error != ENOENT)
+ ASSERT0(error);
+ error = user_release_one(fullname, tag);
+ if (error != ESRCH && error != ENOENT)
+ ASSERT0(error);
+ error = dsl_destroy_snapshot(fullname, B_FALSE);
+ if (error != ENOENT)
+ ASSERT0(error);
/*
* Create snapshot, clone it, mark snap for deferred destroy,
@@ -4635,12 +4652,7 @@ ztest_dmu_snapshot_hold(ztest_ds_t *zd, uint64_t id)
fatal(0, "dmu_objset_snapshot(%s) = %d", fullname, error);
}
- error = dmu_objset_hold(fullname, FTAG, &origin);
- if (error)
- fatal(0, "dmu_objset_hold(%s) = %d", fullname, error);
-
- error = dmu_objset_clone(clonename, dmu_objset_ds(origin), 0);
- dmu_objset_rele(origin, FTAG);
+ error = dmu_objset_clone(clonename, fullname);
if (error) {
if (error == ENOSPC) {
ztest_record_enospc("dmu_objset_clone");
@@ -4649,15 +4661,15 @@ ztest_dmu_snapshot_hold(ztest_ds_t *zd, uint64_t id)
fatal(0, "dmu_objset_clone(%s) = %d", clonename, error);
}
- error = dmu_objset_destroy(fullname, B_TRUE);
+ error = dsl_destroy_snapshot(fullname, B_TRUE);
if (error) {
- fatal(0, "dmu_objset_destroy(%s, B_TRUE) = %d",
+ fatal(0, "dsl_destroy_snapshot(%s, B_TRUE) = %d",
fullname, error);
}
- error = dmu_objset_destroy(clonename, B_FALSE);
+ error = dsl_destroy_head(clonename);
if (error)
- fatal(0, "dmu_objset_destroy(%s) = %d", clonename, error);
+ fatal(0, "dsl_destroy_head(%s) = %d", clonename, error);
error = dmu_objset_hold(fullname, FTAG, &origin);
if (error != ENOENT)
@@ -4677,28 +4689,31 @@ ztest_dmu_snapshot_hold(ztest_ds_t *zd, uint64_t id)
fatal(0, "dmu_objset_snapshot(%s) = %d", fullname, error);
}
- error = dsl_dataset_user_hold(osname, snapname, tag, B_FALSE,
- B_TRUE, -1);
+ holds = fnvlist_alloc();
+ fnvlist_add_string(holds, fullname, tag);
+ error = dsl_dataset_user_hold(holds, 0, NULL);
+ fnvlist_free(holds);
+
if (error)
fatal(0, "dsl_dataset_user_hold(%s)", fullname, tag);
- error = dmu_objset_destroy(fullname, B_FALSE);
+ error = dsl_destroy_snapshot(fullname, B_FALSE);
if (error != EBUSY) {
- fatal(0, "dmu_objset_destroy(%s, B_FALSE) = %d",
+ fatal(0, "dsl_destroy_snapshot(%s, B_FALSE) = %d",
fullname, error);
}
- error = dmu_objset_destroy(fullname, B_TRUE);
+ error = dsl_destroy_snapshot(fullname, B_TRUE);
if (error) {
- fatal(0, "dmu_objset_destroy(%s, B_TRUE) = %d",
+ fatal(0, "dsl_destroy_snapshot(%s, B_TRUE) = %d",
fullname, error);
}
- error = dsl_dataset_user_release(osname, snapname, tag, B_FALSE);
+ error = user_release_one(fullname, tag);
if (error)
- fatal(0, "dsl_dataset_user_release(%s)", fullname, tag);
+ fatal(0, "user_release_one(%s)", fullname, tag);
- VERIFY(dmu_objset_hold(fullname, FTAG, &origin) == ENOENT);
+ VERIFY3U(dmu_objset_hold(fullname, FTAG, &origin), ==, ENOENT);
out:
(void) rw_unlock(&ztest_name_lock);
@@ -4952,8 +4967,12 @@ ztest_ddt_repair(ztest_ds_t *zd, uint64_t id)
*/
for (int i = 0; i < copies; i++) {
uint64_t offset = i * blocksize;
- VERIFY0(dmu_buf_hold(os, object, offset, FTAG, &db,
- DMU_READ_NO_PREFETCH));
+ int error = dmu_buf_hold(os, object, offset, FTAG, &db,
+ DMU_READ_NO_PREFETCH);
+ if (error != 0) {
+ fatal(B_FALSE, "dmu_buf_hold(%p, %llu, %llu) = %u",
+ os, (long long)object, (long long) offset, error);
+ }
ASSERT(db->db_offset == offset);
ASSERT(db->db_size == blocksize);
ASSERT(ztest_pattern_match(db->db_data, db->db_size, pattern) ||
@@ -5163,6 +5182,7 @@ ztest_spa_import_export(char *oldname, char *newname)
nvlist_t *config, *newconfig;
uint64_t pool_guid;
spa_t *spa;
+ int error;
if (ztest_opts.zo_verbose >= 4) {
(void) printf("import/export: old = %s, new = %s\n",
@@ -5207,7 +5227,12 @@ ztest_spa_import_export(char *oldname, char *newname)
/*
* Import it under the new name.
*/
- VERIFY3U(0, ==, spa_import(newname, config, NULL, 0));
+ error = spa_import(newname, config, NULL, 0);
+ if (error != 0) {
+ dump_nvlist(config, 0);
+ fatal(B_FALSE, "couldn't import pool %s as %s: error %u",
+ oldname, newname, error);
+ }
ztest_walk_pool_directory("pools after import");
@@ -5414,7 +5439,7 @@ ztest_dataset_open(int d)
}
ASSERT(error == 0 || error == EEXIST);
- VERIFY0(dmu_objset_hold(name, zd, &os));
+ VERIFY0(dmu_objset_own(name, DMU_OST_OTHER, B_FALSE, zd, &os));
(void) rw_unlock(&ztest_name_lock);
ztest_zd_init(zd, ZTEST_GET_SHARED_DS(d), os);
@@ -5455,7 +5480,7 @@ ztest_dataset_close(int d)
ztest_ds_t *zd = &ztest_ds[d];
zil_close(zd->zd_zilog);
- dmu_objset_rele(zd->zd_os, zd);
+ dmu_objset_disown(zd->zd_os, zd);
ztest_zd_fini(zd);
}
@@ -5499,13 +5524,14 @@ ztest_run(ztest_shared_t *zs)
* Open our pool.
*/
kernel_init(FREAD | FWRITE);
- VERIFY(spa_open(ztest_opts.zo_pool, &spa, FTAG) == 0);
+ VERIFY0(spa_open(ztest_opts.zo_pool, &spa, FTAG));
spa->spa_debug = B_TRUE;
ztest_spa = spa;
- VERIFY3U(0, ==, dmu_objset_hold(ztest_opts.zo_pool, FTAG, &os));
+ VERIFY0(dmu_objset_own(ztest_opts.zo_pool,
+ DMU_OST_ANY, B_TRUE, FTAG, &os));
zs->zs_guid = dmu_objset_fsid_guid(os);
- dmu_objset_rele(os, FTAG);
+ dmu_objset_disown(os, FTAG);
spa->spa_dedup_ditto = 2 * ZIO_DEDUPDITTO_MIN;
diff --git a/lib/libzfs/common/libzfs.h b/lib/libzfs/common/libzfs.h
index 49c86dd..4d1e818 100644
--- a/lib/libzfs/common/libzfs.h
+++ b/lib/libzfs/common/libzfs.h
@@ -550,7 +550,7 @@ 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_destroy_snaps_nvl(libzfs_handle_t *, nvlist_t *, boolean_t);
extern int zfs_clone(zfs_handle_t *, const char *, nvlist_t *);
extern int zfs_snapshot(libzfs_handle_t *, const char *, boolean_t, nvlist_t *);
extern int zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps,
@@ -593,8 +593,8 @@ 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_hold(zfs_handle_t *, const char *, const char *,
+ boolean_t, boolean_t, int);
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 *);
diff --git a/lib/libzfs/common/libzfs_dataset.c b/lib/libzfs/common/libzfs_dataset.c
index 3ad1ed6..6121a0f 100644
--- a/lib/libzfs/common/libzfs_dataset.c
+++ b/lib/libzfs/common/libzfs_dataset.c
@@ -1973,10 +1973,7 @@ get_clones_cb(zfs_handle_t *zhp, void *arg)
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));
- }
+ fnvlist_add_boolean(gca->value, zfs_get_name(zhp));
gca->numclones--;
}
@@ -3142,45 +3139,49 @@ zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
zhp->zfs_name, snapname);
} else {
- ret = zfs_destroy_snaps_nvl(zhp, dd.nvl, defer);
+ ret = zfs_destroy_snaps_nvl(zhp->zfs_hdl, dd.nvl, defer);
}
nvlist_free(dd.nvl);
return (ret);
}
/*
- * Destroys all the snapshots named in the nvlist. They must be underneath
- * the zhp (either snapshots of it, or snapshots of its descendants).
+ * Destroys all the snapshots named in the nvlist.
*/
int
-zfs_destroy_snaps_nvl(zfs_handle_t *zhp, nvlist_t *snaps, boolean_t defer)
+zfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer)
{
int ret;
nvlist_t *errlist;
ret = lzc_destroy_snaps(snaps, defer, &errlist);
- if (ret != 0) {
- for (nvpair_t *pair = nvlist_next_nvpair(errlist, NULL);
- pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) {
- char errbuf[1024];
- (void) snprintf(errbuf, sizeof (errbuf),
- dgettext(TEXT_DOMAIN, "cannot destroy snapshot %s"),
- nvpair_name(pair));
+ if (ret == 0)
+ return (0);
- switch (fnvpair_value_int32(pair)) {
- case EEXIST:
- zfs_error_aux(zhp->zfs_hdl,
- dgettext(TEXT_DOMAIN,
- "snapshot is cloned"));
- ret = zfs_error(zhp->zfs_hdl, EZFS_EXISTS,
- errbuf);
- break;
- default:
- ret = zfs_standard_error(zhp->zfs_hdl, errno,
- errbuf);
- break;
- }
+ if (nvlist_next_nvpair(errlist, NULL) == NULL) {
+ char errbuf[1024];
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot destroy snapshots"));
+
+ ret = zfs_standard_error(hdl, ret, errbuf);
+ }
+ for (nvpair_t *pair = nvlist_next_nvpair(errlist, NULL);
+ pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) {
+ char errbuf[1024];
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot destroy snapshot %s"),
+ nvpair_name(pair));
+
+ switch (fnvpair_value_int32(pair)) {
+ case EEXIST:
+ zfs_error_aux(hdl,
+ dgettext(TEXT_DOMAIN, "snapshot is cloned"));
+ ret = zfs_error(hdl, EZFS_EXISTS, errbuf);
+ break;
+ default:
+ ret = zfs_standard_error(hdl, errno, errbuf);
+ break;
}
}
@@ -4047,7 +4048,7 @@ zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
zc.zc_nvlist_dst_size = sizeof (buf);
if (zfs_ioctl(hdl, ZFS_IOC_USERSPACE_MANY, &zc) != 0) {
- char errbuf[ZFS_MAXNAMELEN + 32];
+ char errbuf[1024];
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN,
@@ -4069,37 +4070,83 @@ zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
return (0);
}
+struct holdarg {
+ nvlist_t *nvl;
+ const char *snapname;
+ const char *tag;
+ boolean_t recursive;
+};
+
+static int
+zfs_hold_one(zfs_handle_t *zhp, void *arg)
+{
+ struct holdarg *ha = arg;
+ zfs_handle_t *szhp;
+ char name[ZFS_MAXNAMELEN];
+ int rv = 0;
+
+ (void) snprintf(name, sizeof (name),
+ "%s@%s", zhp->zfs_name, ha->snapname);
+
+ szhp = make_dataset_handle(zhp->zfs_hdl, name);
+ if (szhp) {
+ fnvlist_add_string(ha->nvl, name, ha->tag);
+ zfs_close(szhp);
+ }
+
+ if (ha->recursive)
+ rv = zfs_iter_filesystems(zhp, zfs_hold_one, ha);
+ zfs_close(zhp);
+ return (rv);
+}
+
int
zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
- boolean_t recursive, boolean_t temphold, boolean_t enoent_ok,
- int cleanup_fd, uint64_t dsobj, uint64_t createtxg)
+ boolean_t recursive, boolean_t enoent_ok, int cleanup_fd)
{
- zfs_cmd_t zc = { 0 };
+ int ret;
+ struct holdarg ha;
+ nvlist_t *errors;
libzfs_handle_t *hdl = zhp->zfs_hdl;
+ char errbuf[1024];
+ nvpair_t *elem;
- ASSERT(!recursive || dsobj == 0);
+ ha.nvl = fnvlist_alloc();
+ ha.snapname = snapname;
+ ha.tag = tag;
+ ha.recursive = recursive;
+ (void) zfs_hold_one(zfs_handle_dup(zhp), &ha);
+ ret = lzc_hold(ha.nvl, cleanup_fd, &errors);
+ fnvlist_free(ha.nvl);
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
- if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string))
- >= sizeof (zc.zc_string))
- return (zfs_error(hdl, EZFS_TAGTOOLONG, tag));
- zc.zc_cookie = recursive;
- zc.zc_temphold = temphold;
- zc.zc_cleanup_fd = cleanup_fd;
- zc.zc_sendobj = dsobj;
- zc.zc_createtxg = createtxg;
+ if (ret == 0)
+ return (0);
- if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) {
- char errbuf[ZFS_MAXNAMELEN+32];
+ if (nvlist_next_nvpair(errors, NULL) == NULL) {
+ /* no hold-specific errors */
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN, "cannot hold"));
+ switch (ret) {
+ case ENOTSUP:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "pool must be upgraded"));
+ (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
+ break;
+ case EINVAL:
+ (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
+ break;
+ default:
+ (void) zfs_standard_error(hdl, ret, errbuf);
+ }
+ }
- /*
- * if it was recursive, the one that actually failed will be in
- * zc.zc_name.
- */
- (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot hold '%s@%s'"), zc.zc_name, snapname);
- switch (errno) {
+ for (elem = nvlist_next_nvpair(errors, NULL);
+ elem != NULL;
+ elem = nvlist_next_nvpair(errors, elem)) {
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "cannot hold snapshot '%s'"), nvpair_name(elem));
+ switch (fnvpair_value_int32(elem)) {
case E2BIG:
/*
* Temporary tags wind up having the ds object id
@@ -4107,66 +4154,122 @@ zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
* above, it's still possible for the tag to wind
* up being slightly too long.
*/
- return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf));
- case ENOTSUP:
- zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "pool must be upgraded"));
- return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
+ (void) zfs_error(hdl, EZFS_TAGTOOLONG, errbuf);
+ break;
case EINVAL:
- return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
+ (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
+ break;
case EEXIST:
- return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf));
+ (void) zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf);
+ break;
case ENOENT:
if (enoent_ok)
return (ENOENT);
/* FALLTHROUGH */
default:
- return (zfs_standard_error_fmt(hdl, errno, errbuf));
+ (void) zfs_standard_error(hdl,
+ fnvpair_value_int32(elem), errbuf);
}
}
- return (0);
+ fnvlist_free(errors);
+ return (ret);
+}
+
+struct releasearg {
+ nvlist_t *nvl;
+ const char *snapname;
+ const char *tag;
+ boolean_t recursive;
+};
+
+static int
+zfs_release_one(zfs_handle_t *zhp, void *arg)
+{
+ struct holdarg *ha = arg;
+ zfs_handle_t *szhp;
+ char name[ZFS_MAXNAMELEN];
+ int rv = 0;
+
+ (void) snprintf(name, sizeof (name),
+ "%s@%s", zhp->zfs_name, ha->snapname);
+
+ szhp = make_dataset_handle(zhp->zfs_hdl, name);
+ if (szhp) {
+ nvlist_t *holds = fnvlist_alloc();
+ fnvlist_add_boolean(holds, ha->tag);
+ fnvlist_add_nvlist(ha->nvl, name, holds);
+ zfs_close(szhp);
+ }
+
+ if (ha->recursive)
+ rv = zfs_iter_filesystems(zhp, zfs_release_one, ha);
+ zfs_close(zhp);
+ return (rv);
}
int
zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
boolean_t recursive)
{
- zfs_cmd_t zc = { 0 };
+ int ret;
+ struct holdarg ha;
+ nvlist_t *errors;
+ nvpair_t *elem;
libzfs_handle_t *hdl = zhp->zfs_hdl;
- (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
- (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
- if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string))
- >= sizeof (zc.zc_string))
- return (zfs_error(hdl, EZFS_TAGTOOLONG, tag));
- zc.zc_cookie = recursive;
+ ha.nvl = fnvlist_alloc();
+ ha.snapname = snapname;
+ ha.tag = tag;
+ ha.recursive = recursive;
+ (void) zfs_release_one(zfs_handle_dup(zhp), &ha);
+ ret = lzc_release(ha.nvl, &errors);
+ fnvlist_free(ha.nvl);
- if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) {
- char errbuf[ZFS_MAXNAMELEN+32];
+ if (ret == 0)
+ return (0);
+
+ if (nvlist_next_nvpair(errors, NULL) == NULL) {
+ /* no hold-specific errors */
+ char errbuf[1024];
- /*
- * if it was recursive, the one that actually failed will be in
- * zc.zc_name.
- */
(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
- "cannot release '%s' from '%s@%s'"), tag, zc.zc_name,
- snapname);
+ "cannot release"));
switch (errno) {
- case ESRCH:
- return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf));
case ENOTSUP:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"pool must be upgraded"));
- return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
+ (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
+ break;
+ default:
+ (void) zfs_standard_error_fmt(hdl, errno, errbuf);
+ }
+ }
+
+ for (elem = nvlist_next_nvpair(errors, NULL);
+ elem != NULL;
+ elem = nvlist_next_nvpair(errors, elem)) {
+ char errbuf[1024];
+
+ (void) snprintf(errbuf, sizeof (errbuf),
+ dgettext(TEXT_DOMAIN,
+ "cannot release hold from snapshot '%s'"),
+ nvpair_name(elem));
+ switch (fnvpair_value_int32(elem)) {
+ case ESRCH:
+ (void) zfs_error(hdl, EZFS_REFTAG_RELE, errbuf);
+ break;
case EINVAL:
- return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
+ (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
+ break;
default:
- return (zfs_standard_error_fmt(hdl, errno, errbuf));
+ (void) zfs_standard_error_fmt(hdl,
+ fnvpair_value_int32(elem), errbuf);
}
}
- return (0);
+ fnvlist_free(errors);
+ return (ret);
}
int
@@ -4177,7 +4280,7 @@ zfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl)
int nvsz = 2048;
void *nvbuf;
int err = 0;
- char errbuf[ZFS_MAXNAMELEN+32];
+ char errbuf[1024];
assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
@@ -4242,7 +4345,7 @@ 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];
+ char errbuf[1024];
size_t nvsz;
int err;
@@ -4293,38 +4396,18 @@ zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
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;
- }
+ int err;
+ char errbuf[1024];
- zc.zc_nvlist_dst_size = nvsz;
- zc.zc_nvlist_dst = (uintptr_t)nvbuf;
+ err = lzc_get_holds(zhp->zfs_name, nvl);
- (void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
+ if (err != 0) {
+ libzfs_handle_t *hdl = zhp->zfs_hdl;
- 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;
-
+ zhp->zfs_name);
+ switch (err) {
case ENOTSUP:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"pool must be upgraded"));
@@ -4340,19 +4423,8 @@ tryagain:
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);
}
diff --git a/lib/libzfs/common/libzfs_sendrecv.c b/lib/libzfs/common/libzfs_sendrecv.c
index ee6e643..ea0634e 100644
--- a/lib/libzfs/common/libzfs_sendrecv.c
+++ b/lib/libzfs/common/libzfs_sendrecv.c
@@ -972,9 +972,7 @@ hold_for_send(zfs_handle_t *zhp, send_dump_data_t *sdd)
*/
if (pzhp) {
error = zfs_hold(pzhp, thissnap, sdd->holdtag,
- B_FALSE, B_TRUE, B_TRUE, sdd->cleanup_fd,
- zfs_prop_get_int(zhp, ZFS_PROP_OBJSETID),
- zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG));
+ B_FALSE, B_TRUE, sdd->cleanup_fd);
zfs_close(pzhp);
}
@@ -1713,12 +1711,11 @@ recv_rename(libzfs_handle_t *hdl, const char *name, const char *tryname,
err = ENOENT;
}
- if (err != 0 && strncmp(name+baselen, "recv-", 5) != 0) {
+ if (err != 0 && strncmp(name + baselen, "recv-", 5) != 0) {
seq++;
- (void) strncpy(newname, name, baselen);
- (void) snprintf(newname+baselen, ZFS_MAXNAMELEN-baselen,
- "recv-%u-%u", getpid(), seq);
+ (void) snprintf(newname, ZFS_MAXNAMELEN, "%.*srecv-%u-%u",
+ baselen, name, getpid(), seq);
(void) strlcpy(zc.zc_value, newname, sizeof (zc.zc_value));
if (flags->verbose) {
@@ -2643,7 +2640,6 @@ zfs_receive_one(libzfs_handle_t *hdl, int infd, const char *tosnap,
/*
* Determine name of destination snapshot, store in zc_value.
*/
- (void) strcpy(zc.zc_top_ds, tosnap);
(void) strcpy(zc.zc_value, tosnap);
(void) strncat(zc.zc_value, chopprefix, sizeof (zc.zc_value));
free(cp);
diff --git a/lib/libzfs_core/common/libzfs_core.c b/lib/libzfs_core/common/libzfs_core.c
index 73afd50..5e8ee07 100644
--- a/lib/libzfs_core/common/libzfs_core.c
+++ b/lib/libzfs_core/common/libzfs_core.c
@@ -132,6 +132,7 @@ lzc_ioctl(zfs_ioc_t ioc, const char *name,
zc.zc_nvlist_src_size = size;
if (resultp != NULL) {
+ *resultp = NULL;
zc.zc_nvlist_dst_size = MAX(size * 2, 128 * 1024);
zc.zc_nvlist_dst = (uint64_t)(uintptr_t)
malloc(zc.zc_nvlist_dst_size);
@@ -159,8 +160,6 @@ lzc_ioctl(zfs_ioc_t ioc, const char *name,
if (zc.zc_nvlist_dst_filled) {
*resultp = fnvlist_unpack((void *)(uintptr_t)zc.zc_nvlist_dst,
zc.zc_nvlist_dst_size);
- } else if (resultp != NULL) {
- *resultp = NULL;
}
out:
@@ -209,7 +208,7 @@ lzc_clone(const char *fsname, const char *origin,
* The value will be the (int32) error code.
*
* The return value will be 0 if all snapshots were created, otherwise it will
- * be the errno of a (undetermined) snapshot that failed.
+ * be the errno of a (unspecified) snapshot that failed.
*/
int
lzc_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t **errlist)
@@ -258,7 +257,7 @@ lzc_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t **errlist)
* The return value will be 0 if all snapshots were destroyed (or marked for
* later destruction if 'defer' is set) or didn't exist to begin with.
*
- * Otherwise the return value will be the errno of a (undetermined) snapshot
+ * Otherwise the return value will be the errno of a (unspecified) snapshot
* that failed, no snapshots will be destroyed, and the errlist will have an
* entry for each snapshot that failed. The value in the errlist will be
* the (int32) error code.
@@ -333,6 +332,101 @@ lzc_exists(const char *dataset)
}
/*
+ * Create "user holds" on snapshots. If there is a hold on a snapshot,
+ * the snapshot can not be destroyed. (However, it can be marked for deletion
+ * by lzc_destroy_snaps(defer=B_TRUE).)
+ *
+ * The keys in the nvlist are snapshot names.
+ * The snapshots must all be in the same pool.
+ * The value is the name of the hold (string type).
+ *
+ * If cleanup_fd is not -1, it must be the result of open("/dev/zfs", O_EXCL).
+ * In this case, when the cleanup_fd is closed (including on process
+ * termination), the holds will be released. If the system is shut down
+ * uncleanly, the holds will be released when the pool is next opened
+ * or imported.
+ *
+ * The return value will be 0 if all holds were created. Otherwise the return
+ * value will be the errno of a (unspecified) hold that failed, no holds will
+ * be created, and the errlist will have an entry for each hold that
+ * failed (name = snapshot). The value in the errlist will be the error
+ * code (int32).
+ */
+int
+lzc_hold(nvlist_t *holds, int cleanup_fd, nvlist_t **errlist)
+{
+ char pool[MAXNAMELEN];
+ nvlist_t *args;
+ nvpair_t *elem;
+ int error;
+
+ /* determine the pool name */
+ elem = nvlist_next_nvpair(holds, NULL);
+ if (elem == NULL)
+ return (0);
+ (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
+ pool[strcspn(pool, "/@")] = '\0';
+
+ args = fnvlist_alloc();
+ fnvlist_add_nvlist(args, "holds", holds);
+ if (cleanup_fd != -1)
+ fnvlist_add_int32(args, "cleanup_fd", cleanup_fd);
+
+ error = lzc_ioctl(ZFS_IOC_HOLD, pool, args, errlist);
+ nvlist_free(args);
+ return (error);
+}
+
+/*
+ * Release "user holds" on snapshots. If the snapshot has been marked for
+ * deferred destroy (by lzc_destroy_snaps(defer=B_TRUE)), it does not have
+ * any clones, and all the user holds are removed, then the snapshot will be
+ * destroyed.
+ *
+ * The keys in the nvlist are snapshot names.
+ * The snapshots must all be in the same pool.
+ * The value is a nvlist whose keys are the holds to remove.
+ *
+ * The return value will be 0 if all holds were removed.
+ * Otherwise the return value will be the errno of a (unspecified) release
+ * that failed, no holds will be released, and the errlist will have an
+ * entry for each snapshot that has failed releases (name = snapshot).
+ * The value in the errlist will be the error code (int32) of a failed release.
+ */
+int
+lzc_release(nvlist_t *holds, nvlist_t **errlist)
+{
+ char pool[MAXNAMELEN];
+ nvpair_t *elem;
+
+ /* determine the pool name */
+ elem = nvlist_next_nvpair(holds, NULL);
+ if (elem == NULL)
+ return (0);
+ (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
+ pool[strcspn(pool, "/@")] = '\0';
+
+ return (lzc_ioctl(ZFS_IOC_RELEASE, pool, holds, errlist));
+}
+
+/*
+ * Retrieve list of user holds on the specified snapshot.
+ *
+ * On success, *holdsp will be set to a nvlist which the caller must free.
+ * The keys are the names of the holds, and the value is the creation time
+ * of the hold (uint64) in seconds since the epoch.
+ */
+int
+lzc_get_holds(const char *snapname, nvlist_t **holdsp)
+{
+ int error;
+ nvlist_t *innvl = fnvlist_alloc();
+ error = lzc_ioctl(ZFS_IOC_GET_HOLDS, snapname, innvl, holdsp);
+ fnvlist_free(innvl);
+ return (error);
+}
+
+/*
* If fromsnap is NULL, a full (non-incremental) stream will be sent.
*/
int
diff --git a/lib/libzfs_core/common/libzfs_core.h b/lib/libzfs_core/common/libzfs_core.h
index 9edc884..f5fd6cd 100644
--- a/lib/libzfs_core/common/libzfs_core.h
+++ b/lib/libzfs_core/common/libzfs_core.h
@@ -46,6 +46,10 @@ int lzc_destroy_snaps(nvlist_t *snaps, boolean_t defer, nvlist_t **errlist);
int lzc_snaprange_space(const char *firstsnap, const char *lastsnap,
uint64_t *usedp);
+int lzc_hold(nvlist_t *holds, int cleanup_fd, nvlist_t **errlist);
+int lzc_release(nvlist_t *holds, nvlist_t **errlist);
+int lzc_get_holds(const char *snapname, nvlist_t **holdsp);
+
int lzc_send(const char *snapname, const char *fromsnap, int fd);
int lzc_receive(const char *snapname, nvlist_t *props, const char *origin,
boolean_t force, int fd);
diff --git a/lib/libzpool/common/kernel.c b/lib/libzpool/common/kernel.c
index cc0d542..9628094 100644
--- a/lib/libzpool/common/kernel.c
+++ b/lib/libzpool/common/kernel.c
@@ -34,6 +34,7 @@
#include <sys/stat.h>
#include <sys/processor.h>
#include <sys/zfs_context.h>
+#include <sys/rrwlock.h>
#include <sys/zmod.h>
#include <sys/utsname.h>
#include <sys/systeminfo.h>
@@ -859,6 +860,8 @@ umem_out_of_memory(void)
void
kernel_init(int mode)
{
+ extern uint_t rrw_tsd_key;
+
umem_nofail_callback(umem_out_of_memory);
physmem = sysconf(_SC_PHYS_PAGES);
@@ -877,6 +880,8 @@ kernel_init(int mode)
mutex_init(&cpu_lock, NULL, MUTEX_DEFAULT, NULL);
spa_init(mode);
+
+ tsd_create(&rrw_tsd_key, rrw_tsd_destroy);
}
void
diff --git a/lib/libzpool/common/sys/zfs_context.h b/lib/libzpool/common/sys/zfs_context.h
index bcb27cf..7802da4 100644
--- a/lib/libzpool/common/sys/zfs_context.h
+++ b/lib/libzpool/common/sys/zfs_context.h
@@ -61,6 +61,8 @@ extern "C" {
#include <dirent.h>
#include <time.h>
#include <procfs.h>
+#include <pthread.h>
+#include <sys/debug.h>
#include <libsysevent.h>
#include <sys/note.h>
#include <sys/types.h>
@@ -224,6 +226,9 @@ typedef int krw_t;
#undef RW_WRITE_HELD
#define RW_WRITE_HELD(x) _rw_write_held(&(x)->rw_lock)
+#undef RW_LOCK_HELD
+#define RW_LOCK_HELD(x) (RW_READ_HELD(x) || RW_WRITE_HELD(x))
+
extern void rw_init(krwlock_t *rwlp, char *name, int type, void *arg);
extern void rw_destroy(krwlock_t *rwlp);
extern void rw_enter(krwlock_t *rwlp, krw_t rw);
@@ -253,6 +258,14 @@ extern void cv_signal(kcondvar_t *cv);
extern void cv_broadcast(kcondvar_t *cv);
/*
+ * Thread-specific data
+ */
+#define tsd_get(k) pthread_getspecific(k)
+#define tsd_set(k, v) pthread_setspecific(k, v)
+#define tsd_create(kp, d) pthread_key_create(kp, d)
+#define tsd_destroy(kp) /* nothing */
+
+/*
* kstat creation, installation and deletion
*/
extern kstat_t *kstat_create(const char *, int,
@@ -519,7 +532,7 @@ typedef struct callb_cpr {
#define INGLOBALZONE(z) (1)
extern char *kmem_asprintf(const char *fmt, ...);
-#define strfree(str) kmem_free((str), strlen(str)+1)
+#define strfree(str) kmem_free((str), strlen(str) + 1)
/*
* Hostname information
diff --git a/man/man1m/zfs.1m b/man/man1m/zfs.1m
index 18441da..c4d579d 100644
--- a/man/man1m/zfs.1m
+++ b/man/man1m/zfs.1m
@@ -1868,7 +1868,9 @@ descendent file systems.
.ad
.sp .6
.RS 4n
-Recursively destroy all dependents.
+Recursively destroy all clones of these snapshots, including the clones,
+snapshots, and children. If this flag is specified, the \fB-d\fR flag will
+have no effect.
.RE
.sp
@@ -1904,7 +1906,7 @@ Print verbose information about the deleted data.
.RE
.sp
-Extreme care should be taken when applying either the \fB-r\fR or the \fB-f\fR
+Extreme care should be taken when applying either the \fB-r\fR or the \fB-R\fR
options, as they can destroy large portions of a pool and cause unexpected
behavior for mounted file systems in use.
.RE
OpenPOWER on IntegriCloud