Print this page
2882 implement libzfs_core
2883 changing "canmount" property to "on" should not always remount dataset
2900 "zfs snapshot" should be able to create multiple, arbitrary snapshots at once
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Chris Siden <christopher.siden@delphix.com>
Reviewed by: Garrett D'Amore <garrett@damore.org>
Reviewed by: Bill Pijewski <wdp@joyent.com>
Reviewed by: Dan Kruchinin <dan.kruchinin@gmail.com>
*** 18,27 ****
--- 18,28 ----
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012 by Delphix. All rights reserved.
*/
/* Portions Copyright 2010 Robert Milkowski */
#include <sys/cred.h>
*** 697,730 ****
{
dsl_dir_t *dd = arg1;
spa_t *spa = dd->dd_pool->dp_spa;
struct oscarg *oa = arg2;
uint64_t obj;
ASSERT(dmu_tx_is_syncing(tx));
obj = dsl_dataset_create_sync(dd, oa->lastname,
oa->clone_origin, oa->flags, oa->cr, tx);
! if (oa->clone_origin == NULL) {
! dsl_pool_t *dp = dd->dd_pool;
! dsl_dataset_t *ds;
! blkptr_t *bp;
! objset_t *os;
!
! VERIFY3U(0, ==, dsl_dataset_hold_obj(dp, obj, FTAG, &ds));
bp = dsl_dataset_get_blkptr(ds);
! ASSERT(BP_IS_HOLE(bp));
!
! os = dmu_objset_create_impl(spa, ds, bp, oa->type, tx);
if (oa->userfunc)
oa->userfunc(os, oa->userarg, oa->cr, tx);
- dsl_dataset_rele(ds, FTAG);
}
! spa_history_log_internal(LOG_DS_CREATE, spa, tx, "dataset = %llu", obj);
}
int
dmu_objset_create(const char *name, dmu_objset_type_t type, uint64_t flags,
void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg)
--- 698,734 ----
{
dsl_dir_t *dd = arg1;
spa_t *spa = dd->dd_pool->dp_spa;
struct oscarg *oa = arg2;
uint64_t obj;
+ dsl_dataset_t *ds;
+ blkptr_t *bp;
ASSERT(dmu_tx_is_syncing(tx));
obj = dsl_dataset_create_sync(dd, oa->lastname,
oa->clone_origin, oa->flags, oa->cr, tx);
! VERIFY3U(0, ==, dsl_dataset_hold_obj(dd->dd_pool, obj, FTAG, &ds));
bp = dsl_dataset_get_blkptr(ds);
! if (BP_IS_HOLE(bp)) {
! objset_t *os =
! dmu_objset_create_impl(spa, ds, bp, oa->type, tx);
if (oa->userfunc)
oa->userfunc(os, oa->userarg, oa->cr, tx);
}
! if (oa->clone_origin == NULL) {
! spa_history_log_internal_ds(ds, "create", tx, "");
! } else {
! char namebuf[MAXNAMELEN];
! dsl_dataset_name(oa->clone_origin, namebuf);
! spa_history_log_internal_ds(ds, "clone", tx,
! "origin=%s (%llu)", namebuf, oa->clone_origin->ds_object);
! }
! dsl_dataset_rele(ds, FTAG);
}
int
dmu_objset_create(const char *name, dmu_objset_type_t type, uint64_t flags,
void (*func)(objset_t *os, void *arg, cred_t *cr, dmu_tx_t *tx), void *arg)
*** 797,834 ****
}
return (error);
}
! struct snaparg {
! dsl_sync_task_group_t *dstg;
! char *snapname;
! char *htag;
! char failed[MAXPATHLEN];
! boolean_t recursive;
! boolean_t needsuspend;
! boolean_t temporary;
! nvlist_t *props;
! struct dsl_ds_holdarg *ha; /* only needed in the temporary case */
! dsl_dataset_t *newds;
! };
static int
snapshot_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
objset_t *os = arg1;
! struct snaparg *sn = arg2;
int error;
/* The props have already been checked by zfs_check_userprops(). */
error = dsl_dataset_snapshot_check(os->os_dsl_dataset,
! sn->snapname, tx);
if (error)
return (error);
! if (sn->temporary) {
/*
* Ideally we would just call
* dsl_dataset_user_hold_check() and
* dsl_dataset_destroy_check() here. However the
* dataset we want to hold and destroy is the snapshot
--- 801,844 ----
}
return (error);
}
! typedef struct snapallarg {
! dsl_sync_task_group_t *saa_dstg;
! boolean_t saa_needsuspend;
! nvlist_t *saa_props;
!
! /* the following are used only if 'temporary' is set: */
! boolean_t saa_temporary;
! const char *saa_htag;
! struct dsl_ds_holdarg *saa_ha;
! dsl_dataset_t *saa_newds;
! } snapallarg_t;
!
! typedef struct snaponearg {
! const char *soa_longname; /* long snap name */
! const char *soa_snapname; /* short snap name */
! snapallarg_t *soa_saa;
! } snaponearg_t;
static int
snapshot_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
objset_t *os = arg1;
! snaponearg_t *soa = arg2;
! snapallarg_t *saa = soa->soa_saa;
int error;
/* The props have already been checked by zfs_check_userprops(). */
error = dsl_dataset_snapshot_check(os->os_dsl_dataset,
! soa->soa_snapname, tx);
if (error)
return (error);
! if (saa->saa_temporary) {
/*
* Ideally we would just call
* dsl_dataset_user_hold_check() and
* dsl_dataset_destroy_check() here. However the
* dataset we want to hold and destroy is the snapshot
*** 842,1017 ****
return (ENOTSUP);
/*
* Not checking number of tags because the tag will be
* unique, as it will be the only tag.
*/
! if (strlen(sn->htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN)
return (E2BIG);
! sn->ha = kmem_alloc(sizeof (struct dsl_ds_holdarg), KM_SLEEP);
! sn->ha->temphold = B_TRUE;
! sn->ha->htag = sn->htag;
}
return (error);
}
static void
snapshot_sync(void *arg1, void *arg2, dmu_tx_t *tx)
{
objset_t *os = arg1;
dsl_dataset_t *ds = os->os_dsl_dataset;
! struct snaparg *sn = arg2;
! dsl_dataset_snapshot_sync(ds, sn->snapname, tx);
! if (sn->props) {
dsl_props_arg_t pa;
! pa.pa_props = sn->props;
pa.pa_source = ZPROP_SRC_LOCAL;
dsl_props_set_sync(ds->ds_prev, &pa, tx);
}
! if (sn->temporary) {
struct dsl_ds_destroyarg da;
! dsl_dataset_user_hold_sync(ds->ds_prev, sn->ha, tx);
! kmem_free(sn->ha, sizeof (struct dsl_ds_holdarg));
! sn->ha = NULL;
! sn->newds = ds->ds_prev;
da.ds = ds->ds_prev;
da.defer = B_TRUE;
dsl_dataset_destroy_sync(&da, FTAG, tx);
}
}
static int
! dmu_objset_snapshot_one(const char *name, void *arg)
{
! struct snaparg *sn = arg;
objset_t *os;
int err;
- char *cp;
-
- /*
- * If the objset starts with a '%', then ignore it unless it was
- * explicitly named (ie, not recursive). These hidden datasets
- * are always inconsistent, and by not opening them here, we can
- * avoid a race with dsl_dir_destroy_check().
- */
- cp = strrchr(name, '/');
- if (cp && cp[1] == '%' && sn->recursive)
- return (0);
! (void) strcpy(sn->failed, name);
! /*
! * Check permissions if we are doing a recursive snapshot. The
! * permission checks for the starting dataset have already been
! * performed in zfs_secpolicy_snapshot()
! */
! if (sn->recursive && (err = zfs_secpolicy_snapshot_perms(name, CRED())))
! return (err);
!
! err = dmu_objset_hold(name, sn, &os);
if (err != 0)
return (err);
/*
* If the objset is in an inconsistent state (eg, in the process
! * of being destroyed), don't snapshot it. As with %hidden
! * datasets, we return EBUSY if this name was explicitly
! * requested (ie, not recursive), and otherwise ignore it.
*/
if (os->os_dsl_dataset->ds_phys->ds_flags & DS_FLAG_INCONSISTENT) {
! dmu_objset_rele(os, sn);
! return (sn->recursive ? 0 : EBUSY);
}
! if (sn->needsuspend) {
err = zil_suspend(dmu_objset_zil(os));
if (err) {
! dmu_objset_rele(os, sn);
return (err);
}
}
! dsl_sync_task_create(sn->dstg, snapshot_check, snapshot_sync,
! os, sn, 3);
return (0);
}
int
! dmu_objset_snapshot(char *fsname, char *snapname, char *tag,
! nvlist_t *props, boolean_t recursive, boolean_t temporary, int cleanup_fd)
{
dsl_sync_task_t *dst;
! struct snaparg sn;
spa_t *spa;
! minor_t minor;
int err;
! (void) strcpy(sn.failed, fsname);
! err = spa_open(fsname, &spa, FTAG);
if (err)
return (err);
- if (temporary) {
if (cleanup_fd < 0) {
spa_close(spa, FTAG);
return (EINVAL);
}
if ((err = zfs_onexit_fd_hold(cleanup_fd, &minor)) != 0) {
spa_close(spa, FTAG);
return (err);
}
- }
! sn.dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
! sn.snapname = snapname;
! sn.htag = tag;
! sn.props = props;
! sn.recursive = recursive;
! sn.needsuspend = (spa_version(spa) < SPA_VERSION_FAST_SNAP);
! sn.temporary = temporary;
! sn.ha = NULL;
! sn.newds = NULL;
!
! if (recursive) {
! err = dmu_objset_find(fsname,
! dmu_objset_snapshot_one, &sn, DS_FIND_CHILDREN);
! } else {
! err = dmu_objset_snapshot_one(fsname, &sn);
! }
if (err == 0)
! err = dsl_sync_task_group_wait(sn.dstg);
! for (dst = list_head(&sn.dstg->dstg_tasks); dst;
! dst = list_next(&sn.dstg->dstg_tasks, dst)) {
objset_t *os = dst->dst_arg1;
! dsl_dataset_t *ds = os->os_dsl_dataset;
! if (dst->dst_err) {
! dsl_dataset_name(ds, sn.failed);
! } else if (temporary) {
! dsl_register_onexit_hold_cleanup(sn.newds, tag, minor);
! }
! if (sn.needsuspend)
zil_resume(dmu_objset_zil(os));
! dmu_objset_rele(os, &sn);
}
- if (err)
- (void) strcpy(fsname, sn.failed);
- if (temporary)
zfs_onexit_fd_rele(cleanup_fd);
! dsl_sync_task_group_destroy(sn.dstg);
spa_close(spa, FTAG);
return (err);
}
static void
dmu_objset_sync_dnodes(list_t *list, list_t *newlist, dmu_tx_t *tx)
{
dnode_t *dn;
--- 852,1078 ----
return (ENOTSUP);
/*
* Not checking number of tags because the tag will be
* unique, as it will be the only tag.
*/
! if (strlen(saa->saa_htag) + MAX_TAG_PREFIX_LEN >= MAXNAMELEN)
return (E2BIG);
! saa->saa_ha = kmem_alloc(sizeof (struct dsl_ds_holdarg),
! KM_SLEEP);
! saa->saa_ha->temphold = B_TRUE;
! saa->saa_ha->htag = saa->saa_htag;
}
return (error);
}
static void
snapshot_sync(void *arg1, void *arg2, dmu_tx_t *tx)
{
objset_t *os = arg1;
dsl_dataset_t *ds = os->os_dsl_dataset;
! snaponearg_t *soa = arg2;
! snapallarg_t *saa = soa->soa_saa;
! dsl_dataset_snapshot_sync(ds, soa->soa_snapname, tx);
! if (saa->saa_props != NULL) {
dsl_props_arg_t pa;
! pa.pa_props = saa->saa_props;
pa.pa_source = ZPROP_SRC_LOCAL;
dsl_props_set_sync(ds->ds_prev, &pa, tx);
}
! if (saa->saa_temporary) {
struct dsl_ds_destroyarg da;
! dsl_dataset_user_hold_sync(ds->ds_prev, saa->saa_ha, tx);
! kmem_free(saa->saa_ha, sizeof (struct dsl_ds_holdarg));
! saa->saa_ha = NULL;
! saa->saa_newds = ds->ds_prev;
da.ds = ds->ds_prev;
da.defer = B_TRUE;
dsl_dataset_destroy_sync(&da, FTAG, tx);
}
}
static int
! snapshot_one_impl(const char *snapname, void *arg)
{
! char fsname[MAXPATHLEN];
! snapallarg_t *saa = arg;
! snaponearg_t *soa;
objset_t *os;
int err;
! (void) strlcpy(fsname, snapname, sizeof (fsname));
! strchr(fsname, '@')[0] = '\0';
! err = dmu_objset_hold(fsname, saa, &os);
if (err != 0)
return (err);
/*
* If the objset is in an inconsistent state (eg, in the process
! * of being destroyed), don't snapshot it.
*/
if (os->os_dsl_dataset->ds_phys->ds_flags & DS_FLAG_INCONSISTENT) {
! dmu_objset_rele(os, saa);
! return (EBUSY);
}
! if (saa->saa_needsuspend) {
err = zil_suspend(dmu_objset_zil(os));
if (err) {
! dmu_objset_rele(os, saa);
return (err);
}
}
!
! soa = kmem_zalloc(sizeof (*soa), KM_SLEEP);
! soa->soa_saa = saa;
! soa->soa_longname = snapname;
! soa->soa_snapname = strchr(snapname, '@') + 1;
!
! dsl_sync_task_create(saa->saa_dstg, snapshot_check, snapshot_sync,
! os, soa, 3);
return (0);
}
+ /*
+ * The snapshots must all be in the same pool.
+ */
int
! dmu_objset_snapshot(nvlist_t *snaps, nvlist_t *props, nvlist_t *errors)
{
dsl_sync_task_t *dst;
! snapallarg_t saa = { 0 };
spa_t *spa;
! int rv = 0;
! int err;
! nvpair_t *pair;
!
! pair = nvlist_next_nvpair(snaps, NULL);
! if (pair == NULL)
! return (0);
!
! err = spa_open(nvpair_name(pair), &spa, FTAG);
! if (err)
! return (err);
! saa.saa_dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
! saa.saa_props = props;
! saa.saa_needsuspend = (spa_version(spa) < SPA_VERSION_FAST_SNAP);
!
! for (pair = nvlist_next_nvpair(snaps, NULL); pair != NULL;
! pair = nvlist_next_nvpair(snaps, pair)) {
! err = snapshot_one_impl(nvpair_name(pair), &saa);
! if (err != 0) {
! if (errors != NULL) {
! fnvlist_add_int32(errors,
! nvpair_name(pair), err);
! }
! rv = err;
! }
! }
!
! /*
! * If any call to snapshot_one_impl() failed, don't execute the
! * sync task. The error handling code below will clean up the
! * snaponearg_t from any successful calls to
! * snapshot_one_impl().
! */
! if (rv == 0)
! err = dsl_sync_task_group_wait(saa.saa_dstg);
! if (err != 0)
! rv = err;
!
! for (dst = list_head(&saa.saa_dstg->dstg_tasks); dst;
! dst = list_next(&saa.saa_dstg->dstg_tasks, dst)) {
! objset_t *os = dst->dst_arg1;
! snaponearg_t *soa = dst->dst_arg2;
! if (dst->dst_err != 0) {
! if (errors != NULL) {
! fnvlist_add_int32(errors,
! soa->soa_longname, dst->dst_err);
! }
! rv = dst->dst_err;
! }
!
! if (saa.saa_needsuspend)
! zil_resume(dmu_objset_zil(os));
! dmu_objset_rele(os, &saa);
! kmem_free(soa, sizeof (*soa));
! }
!
! dsl_sync_task_group_destroy(saa.saa_dstg);
! spa_close(spa, FTAG);
! return (rv);
! }
!
! int
! dmu_objset_snapshot_one(const char *fsname, const char *snapname)
! {
int err;
+ char *longsnap = kmem_asprintf("%s@%s", fsname, snapname);
+ nvlist_t *snaps = fnvlist_alloc();
! fnvlist_add_boolean(snaps, longsnap);
! err = dmu_objset_snapshot(snaps, NULL, NULL);
! fnvlist_free(snaps);
! strfree(longsnap);
! return (err);
! }
! int
! dmu_objset_snapshot_tmp(const char *snapname, const char *tag, int cleanup_fd)
! {
! dsl_sync_task_t *dst;
! snapallarg_t saa = { 0 };
! spa_t *spa;
! minor_t minor;
! int err;
!
! err = spa_open(snapname, &spa, FTAG);
if (err)
return (err);
+ saa.saa_dstg = dsl_sync_task_group_create(spa_get_dsl(spa));
+ saa.saa_htag = tag;
+ saa.saa_needsuspend = (spa_version(spa) < SPA_VERSION_FAST_SNAP);
+ saa.saa_temporary = B_TRUE;
if (cleanup_fd < 0) {
spa_close(spa, FTAG);
return (EINVAL);
}
if ((err = zfs_onexit_fd_hold(cleanup_fd, &minor)) != 0) {
spa_close(spa, FTAG);
return (err);
}
! err = snapshot_one_impl(snapname, &saa);
if (err == 0)
! err = dsl_sync_task_group_wait(saa.saa_dstg);
! for (dst = list_head(&saa.saa_dstg->dstg_tasks); dst;
! dst = list_next(&saa.saa_dstg->dstg_tasks, dst)) {
objset_t *os = dst->dst_arg1;
! dsl_register_onexit_hold_cleanup(saa.saa_newds, tag, minor);
! if (saa.saa_needsuspend)
zil_resume(dmu_objset_zil(os));
! dmu_objset_rele(os, &saa);
}
zfs_onexit_fd_rele(cleanup_fd);
! dsl_sync_task_group_destroy(saa.saa_dstg);
spa_close(spa, FTAG);
return (err);
}
+
static void
dmu_objset_sync_dnodes(list_t *list, list_t *newlist, dmu_tx_t *tx)
{
dnode_t *dn;