--- a/usr/src/uts/common/fs/zfs/dmu_send.c Wed Oct 24 16:50:08 2007 -0700
+++ b/usr/src/uts/common/fs/zfs/dmu_send.c Wed Oct 24 16:54:46 2007 -0700
@@ -295,12 +295,9 @@
zio_cksum_t zc;
};
-/* ARGSUSED */
static int
-replay_incremental_check(void *arg1, void *arg2, dmu_tx_t *tx)
+replay_incremental_check(dsl_dataset_t *ds, struct drr_begin *drrb)
{
- dsl_dataset_t *ds = arg1;
- struct drr_begin *drrb = arg2;
const char *snapname;
int err;
uint64_t val;
@@ -312,10 +309,6 @@
/* most recent snapshot must match fromguid */
if (ds->ds_prev->ds_phys->ds_guid != drrb->drr_fromguid)
return (ENODEV);
- /* must not have any changes since most recent snapshot */
- if (ds->ds_phys->ds_bp.blk_birth >
- ds->ds_prev->ds_phys->ds_creation_txg)
- return (ETXTBSY);
/* new snapshot name must not exist */
snapname = strrchr(drrb->drr_toname, '@');
@@ -326,16 +319,31 @@
err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset,
ds->ds_phys->ds_snapnames_zapobj, snapname, 8, 1, &val);
if (err == 0)
- return (EEXIST);
+ return (EEXIST);
if (err != ENOENT)
- return (err);
+ return (err);
return (0);
}
/* ARGSUSED */
+static int
+replay_offline_incremental_check(void *arg1, void *arg2, dmu_tx_t *tx)
+{
+ dsl_dataset_t *ds = arg1;
+ struct drr_begin *drrb = arg2;
+
+ /* must not have any changes since most recent snapshot */
+ if (dsl_dataset_modified_since_lastsnap(ds))
+ return (ETXTBSY);
+
+ return (replay_incremental_check(ds, drrb));
+}
+
+/* ARGSUSED */
static void
-replay_incremental_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
+replay_offline_incremental_sync(void *arg1, void *arg2, cred_t *cr,
+ dmu_tx_t *tx)
{
dsl_dataset_t *ds = arg1;
dmu_buf_will_dirty(ds->ds_dbuf, tx);
@@ -402,6 +410,57 @@
dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
}
+struct onlineincarg {
+ dsl_dir_t *dd;
+ dsl_dataset_t *ohds;
+ boolean_t force;
+ const char *cosname;
+};
+
+/* ARGSUSED */
+static int
+replay_online_incremental_check(void *arg1, void *arg2, dmu_tx_t *tx)
+{
+ struct onlineincarg *oia = arg1;
+
+ if (dsl_dataset_modified_since_lastsnap(oia->ohds) && !oia->force)
+ return (ETXTBSY);
+
+ return (replay_incremental_check(oia->ohds, arg2));
+}
+
+/* ARGSUSED */
+static void
+replay_online_incremental_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
+{
+ struct onlineincarg *oia = arg1;
+ dsl_dataset_t *ohds = oia->ohds;
+ dsl_dir_t *dd = oia->dd;
+ dsl_dataset_t *ods, *ds;
+ uint64_t dsobj;
+
+ VERIFY(0 == dsl_dataset_open_obj(ohds->ds_dir->dd_pool,
+ ohds->ds_phys->ds_prev_snap_obj, NULL,
+ DS_MODE_STANDARD, FTAG, &ods));
+
+ dsobj = dsl_dataset_create_sync(dd, strrchr(oia->cosname, '/') + 1,
+ ods, tx);
+
+ /* open the temporary clone */
+ VERIFY(0 == dsl_dataset_open_obj(dd->dd_pool, dsobj, NULL,
+ DS_MODE_EXCLUSIVE, FTAG, &ds));
+
+ dmu_buf_will_dirty(ds->ds_dbuf, tx);
+ ds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT;
+
+ spa_history_internal_log(LOG_DS_REPLAY_INC_SYNC,
+ ds->ds_dir->dd_pool->dp_spa, tx, cr, "dataset = %lld",
+ ds->ds_phys->ds_dir_obj);
+
+ dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
+ dsl_dataset_close(ods, DS_MODE_STANDARD, FTAG);
+}
+
static int
replay_end_check(void *arg1, void *arg2, dmu_tx_t *tx)
{
@@ -729,13 +788,16 @@
int
dmu_recvbackup(char *tosnap, struct drr_begin *drrb, uint64_t *sizep,
- boolean_t force, vnode_t *vp, uint64_t voffset)
+ boolean_t force, boolean_t online, vnode_t *vp, uint64_t voffset,
+ char *cosname)
{
struct restorearg ra;
dmu_replay_record_t *drr;
char *cp;
objset_t *os = NULL;
zio_cksum_t pzc;
+ char *clonebuf = NULL;
+ size_t len;
bzero(&ra, sizeof (ra));
ra.vp = vp;
@@ -790,8 +852,9 @@
/*
* Process the begin in syncing context.
*/
- if (drrb->drr_fromguid) {
- /* incremental backup */
+ if (drrb->drr_fromguid && !online) {
+ /* offline incremental receive */
+
dsl_dataset_t *ds = NULL;
cp = strchr(tosnap, '@');
@@ -816,11 +879,52 @@
(void) dsl_dataset_rollback(ds);
}
ra.err = dsl_sync_task_do(ds->ds_dir->dd_pool,
- replay_incremental_check, replay_incremental_sync,
- ds, drrb, 1);
+ replay_offline_incremental_check,
+ replay_offline_incremental_sync, ds, drrb, 1);
dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
+ } else if (drrb->drr_fromguid && online) {
+ /* online incremental receive */
+
+ const char *tail;
+ struct onlineincarg oia = { 0 };
+
+ /*
+ * Get the dsl_dir for the parent of the
+ * temporary clone.
+ */
+ cp = strchr(tosnap, '@');
+ *cp = '\0';
+
+ /* tmp clone is: tonsap + '/' + '%' + "snapX" */
+ len = strlen(tosnap) + 2 + strlen(cp + 1) + 1;
+ clonebuf = kmem_alloc(len, KM_SLEEP);
+ (void) snprintf(clonebuf, len, "%s%c%c%s%c",
+ tosnap, '/', '%', cp + 1, '\0');
+ ra.err = dsl_dir_open(tosnap, FTAG, &oia.dd, &tail);
+ *cp = '@';
+ if (ra.err)
+ goto out;
+
+ /* open the dataset we are logically receiving into */
+ *cp = '\0';
+ ra.err = dsl_dataset_open(tosnap, DS_MODE_STANDARD,
+ FTAG, &oia.ohds);
+ *cp = '@';
+ if (ra.err) {
+ dsl_dir_close(oia.dd, FTAG);
+ goto out;
+ }
+
+ oia.force = force;
+ oia.cosname = clonebuf;
+ ra.err = dsl_sync_task_do(oia.dd->dd_pool,
+ replay_online_incremental_check,
+ replay_online_incremental_sync, &oia, drrb, 5);
+ dsl_dataset_close(oia.ohds, DS_MODE_STANDARD, FTAG);
+ dsl_dir_close(oia.dd, FTAG);
} else {
/* full backup */
+
dsl_dir_t *dd = NULL;
const char *tail;
@@ -854,8 +958,8 @@
cp = strchr(tosnap, '@');
*cp = '\0';
- ra.err = dmu_objset_open(tosnap, DMU_OST_ANY,
- DS_MODE_PRIMARY | DS_MODE_INCONSISTENT, &os);
+ ra.err = dmu_objset_open(clonebuf == NULL ? tosnap : clonebuf,
+ DMU_OST_ANY, DS_MODE_PRIMARY | DS_MODE_INCONSISTENT, &os);
*cp = '@';
ASSERT3U(ra.err, ==, 0);
@@ -918,9 +1022,11 @@
goto out;
}
- ra.err = dsl_sync_task_do(dmu_objset_ds(os)->
- ds_dir->dd_pool, replay_end_check, replay_end_sync,
- os, drrb, 3);
+ if (clonebuf == NULL) {
+ ra.err = dsl_sync_task_do(dmu_objset_ds(os)->
+ ds_dir->dd_pool, replay_end_check,
+ replay_end_sync, os, drrb, 3);
+ }
goto out;
}
default:
@@ -931,8 +1037,11 @@
}
out:
- if (os)
+ if (os) {
+ if (drrb->drr_fromguid && online && !ra.err)
+ dmu_objset_name(os, cosname);
dmu_objset_close(os);
+ }
/*
* Make sure we don't rollback/destroy unless we actually
@@ -949,15 +1058,29 @@
cp = strchr(tosnap, '@');
*cp = '\0';
- err = dsl_dataset_open(tosnap,
+ err = dsl_dataset_open(clonebuf == NULL ? tosnap : clonebuf,
DS_MODE_EXCLUSIVE | DS_MODE_INCONSISTENT,
FTAG, &ds);
if (err == 0) {
txg_wait_synced(ds->ds_dir->dd_pool, 0);
if (drrb->drr_fromguid) {
- /* incremental: rollback to most recent snap */
- (void) dsl_dataset_rollback(ds);
- dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
+ if (clonebuf != NULL) {
+ /*
+ * online incremental: destroy
+ * the temporarily created clone.
+ */
+ dsl_dataset_close(ds, DS_MODE_EXCLUSIVE,
+ FTAG);
+ (void) dmu_objset_destroy(clonebuf);
+ } else {
+ /*
+ * offline incremental: rollback to
+ * most recent snapshot.
+ */
+ (void) dsl_dataset_rollback(ds);
+ dsl_dataset_close(ds, DS_MODE_EXCLUSIVE,
+ FTAG);
+ }
} else {
/* full: destroy whole fs */
dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG);
@@ -967,8 +1090,26 @@
*cp = '@';
}
+ if (clonebuf != NULL)
+ kmem_free(clonebuf, len);
kmem_free(ra.buf, ra.bufsize);
if (sizep)
*sizep = ra.voff;
return (ra.err);
}
+
+int
+dmu_replay_end_snapshot(char *name, struct drr_begin *drrb)
+{
+ objset_t *os;
+ int err;
+
+ err = dmu_objset_open(name, DMU_OST_ZFS, DS_MODE_STANDARD, &os);
+ if (err)
+ return (err);
+
+ err = dsl_sync_task_do(dmu_objset_ds(os)->ds_dir->dd_pool,
+ replay_end_check, replay_end_sync, os, drrb, 3);
+ dmu_objset_close(os);
+ return (err);
+}