Print this page
3888 zfs recv -F should destroy any snapshots created since the incremental source
Reviewed by: George Wilson <george.wilson@delphix.com>
Reviewed by: Adam Leventhal <ahl@delphix.com>
Reviewed by: Peng Dai <peng.dai@delphix.com>

*** 652,661 **** --- 652,662 ---- typedef struct dmu_recv_begin_arg { const char *drba_origin; dmu_recv_cookie_t *drba_cookie; cred_t *drba_cred; + uint64_t drba_snapobj; } dmu_recv_begin_arg_t; static int recv_begin_check_existing_impl(dmu_recv_begin_arg_t *drba, dsl_dataset_t *ds, uint64_t fromguid)
*** 662,676 **** { uint64_t val; int error; dsl_pool_t *dp = ds->ds_dir->dd_pool; - /* must not have any changes since most recent snapshot */ - if (!drba->drba_cookie->drc_force && - dsl_dataset_modified_since_lastsnap(ds)) - return (SET_ERROR(ETXTBSY)); - /* temporary clone name must not exist */ error = zap_lookup(dp->dp_meta_objset, ds->ds_dir->dd_phys->dd_child_dir_zapobj, recv_clone_name, 8, 1, &val); if (error != ENOENT) --- 663,672 ----
*** 682,726 **** 8, 1, &val); if (error != ENOENT) return (error == 0 ? EEXIST : error); if (fromguid != 0) { ! /* if incremental, most recent snapshot must match fromguid */ ! if (ds->ds_prev == NULL) ! return (SET_ERROR(ENODEV)); ! /* ! * most recent snapshot must match fromguid, or there are no ! * changes since the fromguid one ! */ ! if (ds->ds_prev->ds_phys->ds_guid != fromguid) { ! uint64_t birth = ds->ds_prev->ds_phys->ds_bp.blk_birth; ! uint64_t obj = ds->ds_prev->ds_phys->ds_prev_snap_obj; while (obj != 0) { - dsl_dataset_t *snap; error = dsl_dataset_hold_obj(dp, obj, FTAG, &snap); if (error != 0) return (SET_ERROR(ENODEV)); ! if (snap->ds_phys->ds_creation_txg < birth) { dsl_dataset_rele(snap, FTAG); return (SET_ERROR(ENODEV)); } ! if (snap->ds_phys->ds_guid == fromguid) { ! dsl_dataset_rele(snap, FTAG); ! break; /* it's ok */ ! } obj = snap->ds_phys->ds_prev_snap_obj; dsl_dataset_rele(snap, FTAG); } if (obj == 0) return (SET_ERROR(ENODEV)); } } else { /* if full, most recent snapshot must be $ORIGIN */ if (ds->ds_phys->ds_prev_snap_txg >= TXG_INITIAL) return (SET_ERROR(ENODEV)); } return (0); } --- 678,728 ---- 8, 1, &val); if (error != ENOENT) return (error == 0 ? EEXIST : error); if (fromguid != 0) { ! dsl_dataset_t *snap; ! uint64_t obj = ds->ds_phys->ds_prev_snap_obj; ! /* Find snapshot in this dir that matches fromguid. */ while (obj != 0) { error = dsl_dataset_hold_obj(dp, obj, FTAG, &snap); if (error != 0) return (SET_ERROR(ENODEV)); ! if (snap->ds_dir != ds->ds_dir) { dsl_dataset_rele(snap, FTAG); return (SET_ERROR(ENODEV)); } ! if (snap->ds_phys->ds_guid == fromguid) ! break; obj = snap->ds_phys->ds_prev_snap_obj; dsl_dataset_rele(snap, FTAG); } if (obj == 0) return (SET_ERROR(ENODEV)); + + if (drba->drba_cookie->drc_force) { + drba->drba_snapobj = obj; + } else { + /* + * If we are not forcing, there must be no + * changes since fromsnap. + */ + if (dsl_dataset_modified_since_snap(ds, snap)) { + dsl_dataset_rele(snap, FTAG); + return (SET_ERROR(ETXTBSY)); } + drba->drba_snapobj = ds->ds_prev->ds_object; + } + + dsl_dataset_rele(snap, FTAG); } else { /* if full, most recent snapshot must be $ORIGIN */ if (ds->ds_phys->ds_prev_snap_txg >= TXG_INITIAL) return (SET_ERROR(ENODEV)); + drba->drba_snapobj = ds->ds_phys->ds_prev_snap_obj; } return (0); }
*** 825,836 **** DS_FLAG_CI_DATASET : 0; error = dsl_dataset_hold(dp, tofs, FTAG, &ds); if (error == 0) { /* create temporary clone */ dsobj = dsl_dataset_create_sync(ds->ds_dir, recv_clone_name, ! ds->ds_prev, crflags, drba->drba_cred, tx); dsl_dataset_rele(ds, FTAG); } else { dsl_dir_t *dd; const char *tail; dsl_dataset_t *origin = NULL; --- 827,844 ---- DS_FLAG_CI_DATASET : 0; error = dsl_dataset_hold(dp, tofs, FTAG, &ds); if (error == 0) { /* create temporary clone */ + dsl_dataset_t *snap = NULL; + if (drba->drba_snapobj != 0) { + VERIFY0(dsl_dataset_hold_obj(dp, + drba->drba_snapobj, FTAG, &snap)); + } dsobj = dsl_dataset_create_sync(ds->ds_dir, recv_clone_name, ! snap, crflags, drba->drba_cred, tx); ! dsl_dataset_rele(snap, FTAG); dsl_dataset_rele(ds, FTAG); } else { dsl_dir_t *dd; const char *tail; dsl_dataset_t *origin = NULL;
*** 1544,1553 **** --- 1552,1587 ---- dsl_dataset_t *origin_head; error = dsl_dataset_hold(dp, drc->drc_tofs, FTAG, &origin_head); if (error != 0) return (error); + if (drc->drc_force) { + /* + * We will destroy any snapshots in tofs (i.e. before + * origin_head) that are after the origin (which is + * the snap before drc_ds, because drc_ds can not + * have any snaps of its own). + */ + uint64_t obj = origin_head->ds_phys->ds_prev_snap_obj; + while (obj != drc->drc_ds->ds_phys->ds_prev_snap_obj) { + dsl_dataset_t *snap; + error = dsl_dataset_hold_obj(dp, obj, FTAG, + &snap); + if (error != 0) + return (error); + if (snap->ds_dir != origin_head->ds_dir) + error = SET_ERROR(EINVAL); + if (error == 0) { + error = dsl_destroy_snapshot_check_impl( + snap, B_FALSE); + } + obj = snap->ds_phys->ds_prev_snap_obj; + dsl_dataset_rele(snap, FTAG); + if (error != 0) + return (error); + } + } error = dsl_dataset_clone_swap_check_impl(drc->drc_ds, origin_head, drc->drc_force, drc->drc_owner, tx); if (error != 0) { dsl_dataset_rele(origin_head, FTAG); return (error);
*** 1578,1587 **** --- 1612,1642 ---- if (!drc->drc_newfs) { dsl_dataset_t *origin_head; VERIFY0(dsl_dataset_hold(dp, drc->drc_tofs, FTAG, &origin_head)); + + if (drc->drc_force) { + /* + * Destroy any snapshots of drc_tofs (origin_head) + * after the origin (the snap before drc_ds). + */ + uint64_t obj = origin_head->ds_phys->ds_prev_snap_obj; + while (obj != drc->drc_ds->ds_phys->ds_prev_snap_obj) { + dsl_dataset_t *snap; + VERIFY0(dsl_dataset_hold_obj(dp, obj, FTAG, + &snap)); + ASSERT3P(snap->ds_dir, ==, origin_head->ds_dir); + obj = snap->ds_phys->ds_prev_snap_obj; + dsl_destroy_snapshot_sync_impl(snap, + B_FALSE, tx); + dsl_dataset_rele(snap, FTAG); + } + } + VERIFY3P(drc->drc_ds->ds_prev, ==, + origin_head->ds_prev); + dsl_dataset_clone_swap_sync_impl(drc->drc_ds, origin_head, tx); dsl_dataset_snapshot_sync_impl(origin_head, drc->drc_tosnap, tx);