12743Sahrens /* 22743Sahrens * CDDL HEADER START 32743Sahrens * 42743Sahrens * The contents of this file are subject to the terms of the 52743Sahrens * Common Development and Distribution License (the "License"). 62743Sahrens * You may not use this file except in compliance with the License. 72743Sahrens * 82743Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 92743Sahrens * or http://www.opensolaris.org/os/licensing. 102743Sahrens * See the License for the specific language governing permissions 112743Sahrens * and limitations under the License. 122743Sahrens * 132743Sahrens * When distributing Covered Code, include this CDDL HEADER in each 142743Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 152743Sahrens * If applicable, add the following below this CDDL HEADER, with the 162743Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 172743Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 182743Sahrens * 192743Sahrens * CDDL HEADER END 202743Sahrens */ 212743Sahrens /* 223547Smaybee * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 232743Sahrens * Use is subject to license terms. 242743Sahrens */ 252743Sahrens 262743Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 272743Sahrens 282743Sahrens #include <sys/dmu.h> 292743Sahrens #include <sys/dmu_impl.h> 302743Sahrens #include <sys/dmu_tx.h> 312743Sahrens #include <sys/dbuf.h> 322743Sahrens #include <sys/dnode.h> 332743Sahrens #include <sys/zfs_context.h> 342743Sahrens #include <sys/dmu_objset.h> 352743Sahrens #include <sys/dmu_traverse.h> 362743Sahrens #include <sys/dsl_dataset.h> 372743Sahrens #include <sys/dsl_dir.h> 382743Sahrens #include <sys/dsl_pool.h> 392743Sahrens #include <sys/dsl_synctask.h> 402743Sahrens #include <sys/zfs_ioctl.h> 412743Sahrens #include <sys/zap.h> 422743Sahrens #include <sys/zio_checksum.h> 432743Sahrens 442743Sahrens struct backuparg { 452743Sahrens dmu_replay_record_t *drr; 462743Sahrens vnode_t *vp; 472743Sahrens objset_t *os; 482743Sahrens zio_cksum_t zc; 492743Sahrens int err; 502743Sahrens }; 512743Sahrens 522743Sahrens static int 532743Sahrens dump_bytes(struct backuparg *ba, void *buf, int len) 542743Sahrens { 552743Sahrens ssize_t resid; /* have to get resid to get detailed errno */ 562743Sahrens ASSERT3U(len % 8, ==, 0); 572743Sahrens 582743Sahrens fletcher_4_incremental_native(buf, len, &ba->zc); 592743Sahrens ba->err = vn_rdwr(UIO_WRITE, ba->vp, 602743Sahrens (caddr_t)buf, len, 612743Sahrens 0, UIO_SYSSPACE, FAPPEND, RLIM64_INFINITY, CRED(), &resid); 622743Sahrens return (ba->err); 632743Sahrens } 642743Sahrens 652743Sahrens static int 662743Sahrens dump_free(struct backuparg *ba, uint64_t object, uint64_t offset, 672743Sahrens uint64_t length) 682743Sahrens { 692743Sahrens /* write a FREE record */ 702743Sahrens bzero(ba->drr, sizeof (dmu_replay_record_t)); 712743Sahrens ba->drr->drr_type = DRR_FREE; 722743Sahrens ba->drr->drr_u.drr_free.drr_object = object; 732743Sahrens ba->drr->drr_u.drr_free.drr_offset = offset; 742743Sahrens ba->drr->drr_u.drr_free.drr_length = length; 752743Sahrens 762743Sahrens if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t))) 772743Sahrens return (EINTR); 782743Sahrens return (0); 792743Sahrens } 802743Sahrens 812743Sahrens static int 822743Sahrens dump_data(struct backuparg *ba, dmu_object_type_t type, 832743Sahrens uint64_t object, uint64_t offset, int blksz, void *data) 842743Sahrens { 852743Sahrens /* write a DATA record */ 862743Sahrens bzero(ba->drr, sizeof (dmu_replay_record_t)); 872743Sahrens ba->drr->drr_type = DRR_WRITE; 882743Sahrens ba->drr->drr_u.drr_write.drr_object = object; 892743Sahrens ba->drr->drr_u.drr_write.drr_type = type; 902743Sahrens ba->drr->drr_u.drr_write.drr_offset = offset; 912743Sahrens ba->drr->drr_u.drr_write.drr_length = blksz; 922743Sahrens 932743Sahrens if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t))) 942743Sahrens return (EINTR); 952743Sahrens if (dump_bytes(ba, data, blksz)) 962743Sahrens return (EINTR); 972743Sahrens return (0); 982743Sahrens } 992743Sahrens 1002743Sahrens static int 1012743Sahrens dump_freeobjects(struct backuparg *ba, uint64_t firstobj, uint64_t numobjs) 1022743Sahrens { 1032743Sahrens /* write a FREEOBJECTS record */ 1042743Sahrens bzero(ba->drr, sizeof (dmu_replay_record_t)); 1052743Sahrens ba->drr->drr_type = DRR_FREEOBJECTS; 1062743Sahrens ba->drr->drr_u.drr_freeobjects.drr_firstobj = firstobj; 1072743Sahrens ba->drr->drr_u.drr_freeobjects.drr_numobjs = numobjs; 1082743Sahrens 1092743Sahrens if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t))) 1102743Sahrens return (EINTR); 1112743Sahrens return (0); 1122743Sahrens } 1132743Sahrens 1142743Sahrens static int 1152743Sahrens dump_dnode(struct backuparg *ba, uint64_t object, dnode_phys_t *dnp) 1162743Sahrens { 1172743Sahrens if (dnp == NULL || dnp->dn_type == DMU_OT_NONE) 1182743Sahrens return (dump_freeobjects(ba, object, 1)); 1192743Sahrens 1202743Sahrens /* write an OBJECT record */ 1212743Sahrens bzero(ba->drr, sizeof (dmu_replay_record_t)); 1222743Sahrens ba->drr->drr_type = DRR_OBJECT; 1232743Sahrens ba->drr->drr_u.drr_object.drr_object = object; 1242743Sahrens ba->drr->drr_u.drr_object.drr_type = dnp->dn_type; 1252743Sahrens ba->drr->drr_u.drr_object.drr_bonustype = dnp->dn_bonustype; 1262743Sahrens ba->drr->drr_u.drr_object.drr_blksz = 1272743Sahrens dnp->dn_datablkszsec << SPA_MINBLOCKSHIFT; 1282743Sahrens ba->drr->drr_u.drr_object.drr_bonuslen = dnp->dn_bonuslen; 1292743Sahrens ba->drr->drr_u.drr_object.drr_checksum = dnp->dn_checksum; 1302743Sahrens ba->drr->drr_u.drr_object.drr_compress = dnp->dn_compress; 1312743Sahrens 1322743Sahrens if (dump_bytes(ba, ba->drr, sizeof (dmu_replay_record_t))) 1332743Sahrens return (EINTR); 1342743Sahrens 1352743Sahrens if (dump_bytes(ba, DN_BONUS(dnp), P2ROUNDUP(dnp->dn_bonuslen, 8))) 1362743Sahrens return (EINTR); 1372743Sahrens 1382743Sahrens /* free anything past the end of the file */ 1392743Sahrens if (dump_free(ba, object, (dnp->dn_maxblkid + 1) * 1402743Sahrens (dnp->dn_datablkszsec << SPA_MINBLOCKSHIFT), -1ULL)) 1412743Sahrens return (EINTR); 1422743Sahrens if (ba->err) 1432743Sahrens return (EINTR); 1442743Sahrens return (0); 1452743Sahrens } 1462743Sahrens 1472743Sahrens #define BP_SPAN(dnp, level) \ 1482743Sahrens (((uint64_t)dnp->dn_datablkszsec) << (SPA_MINBLOCKSHIFT + \ 1492743Sahrens (level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT))) 1502743Sahrens 1512743Sahrens static int 1522743Sahrens backup_cb(traverse_blk_cache_t *bc, spa_t *spa, void *arg) 1532743Sahrens { 1542743Sahrens struct backuparg *ba = arg; 1552743Sahrens uint64_t object = bc->bc_bookmark.zb_object; 1562743Sahrens int level = bc->bc_bookmark.zb_level; 1572743Sahrens uint64_t blkid = bc->bc_bookmark.zb_blkid; 1582743Sahrens blkptr_t *bp = bc->bc_blkptr.blk_birth ? &bc->bc_blkptr : NULL; 1592743Sahrens dmu_object_type_t type = bp ? BP_GET_TYPE(bp) : DMU_OT_NONE; 1602743Sahrens void *data = bc->bc_data; 1612743Sahrens int err = 0; 1622743Sahrens 1632743Sahrens if (issig(JUSTLOOKING) && issig(FORREAL)) 1642743Sahrens return (EINTR); 1652743Sahrens 1662743Sahrens ASSERT(data || bp == NULL); 1672743Sahrens 1682743Sahrens if (bp == NULL && object == 0) { 1692743Sahrens uint64_t span = BP_SPAN(bc->bc_dnode, level); 1702743Sahrens uint64_t dnobj = (blkid * span) >> DNODE_SHIFT; 1712743Sahrens err = dump_freeobjects(ba, dnobj, span >> DNODE_SHIFT); 1722743Sahrens } else if (bp == NULL) { 1732743Sahrens uint64_t span = BP_SPAN(bc->bc_dnode, level); 1742743Sahrens err = dump_free(ba, object, blkid * span, span); 1752743Sahrens } else if (data && level == 0 && type == DMU_OT_DNODE) { 1762743Sahrens dnode_phys_t *blk = data; 1772743Sahrens int i; 1782743Sahrens int blksz = BP_GET_LSIZE(bp); 1792743Sahrens 1802743Sahrens for (i = 0; i < blksz >> DNODE_SHIFT; i++) { 1812743Sahrens uint64_t dnobj = 1822743Sahrens (blkid << (DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i; 1832743Sahrens err = dump_dnode(ba, dnobj, blk+i); 1842743Sahrens if (err) 1852743Sahrens break; 1862743Sahrens } 1872743Sahrens } else if (level == 0 && 1882743Sahrens type != DMU_OT_DNODE && type != DMU_OT_OBJSET) { 1892743Sahrens int blksz = BP_GET_LSIZE(bp); 1902743Sahrens if (data == NULL) { 1912743Sahrens uint32_t aflags = ARC_WAIT; 1922743Sahrens arc_buf_t *abuf; 1932743Sahrens zbookmark_t zb; 1942743Sahrens 1952743Sahrens zb.zb_objset = ba->os->os->os_dsl_dataset->ds_object; 1962743Sahrens zb.zb_object = object; 1972743Sahrens zb.zb_level = level; 1982743Sahrens zb.zb_blkid = blkid; 1992743Sahrens (void) arc_read(NULL, spa, bp, 2002743Sahrens dmu_ot[type].ot_byteswap, arc_getbuf_func, &abuf, 2012743Sahrens ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_MUSTSUCCEED, 2022743Sahrens &aflags, &zb); 2032743Sahrens 2042743Sahrens if (abuf) { 2052743Sahrens err = dump_data(ba, type, object, blkid * blksz, 2062743Sahrens blksz, abuf->b_data); 2072743Sahrens (void) arc_buf_remove_ref(abuf, &abuf); 2082743Sahrens } 2092743Sahrens } else { 2102743Sahrens err = dump_data(ba, type, object, blkid * blksz, 2112743Sahrens blksz, data); 2122743Sahrens } 2132743Sahrens } 2142743Sahrens 2152743Sahrens ASSERT(err == 0 || err == EINTR); 2162743Sahrens return (err); 2172743Sahrens } 2182743Sahrens 2192743Sahrens int 2202743Sahrens dmu_sendbackup(objset_t *tosnap, objset_t *fromsnap, vnode_t *vp) 2212743Sahrens { 2222743Sahrens dsl_dataset_t *ds = tosnap->os->os_dsl_dataset; 2232743Sahrens dsl_dataset_t *fromds = fromsnap ? fromsnap->os->os_dsl_dataset : NULL; 2242743Sahrens dmu_replay_record_t *drr; 2252743Sahrens struct backuparg ba; 2262743Sahrens int err; 2272743Sahrens 2282743Sahrens /* tosnap must be a snapshot */ 2292743Sahrens if (ds->ds_phys->ds_next_snap_obj == 0) 2302743Sahrens return (EINVAL); 2312743Sahrens 2322743Sahrens /* fromsnap must be an earlier snapshot from the same fs as tosnap */ 2332743Sahrens if (fromds && (ds->ds_dir != fromds->ds_dir || 2342743Sahrens fromds->ds_phys->ds_creation_txg >= 2352743Sahrens ds->ds_phys->ds_creation_txg)) 2362743Sahrens return (EXDEV); 2372743Sahrens 2382743Sahrens drr = kmem_zalloc(sizeof (dmu_replay_record_t), KM_SLEEP); 2392743Sahrens drr->drr_type = DRR_BEGIN; 2402743Sahrens drr->drr_u.drr_begin.drr_magic = DMU_BACKUP_MAGIC; 2412743Sahrens drr->drr_u.drr_begin.drr_version = DMU_BACKUP_VERSION; 2422743Sahrens drr->drr_u.drr_begin.drr_creation_time = 2432743Sahrens ds->ds_phys->ds_creation_time; 2442743Sahrens drr->drr_u.drr_begin.drr_type = tosnap->os->os_phys->os_type; 2452743Sahrens drr->drr_u.drr_begin.drr_toguid = ds->ds_phys->ds_guid; 2462743Sahrens if (fromds) 2472743Sahrens drr->drr_u.drr_begin.drr_fromguid = fromds->ds_phys->ds_guid; 2482743Sahrens dsl_dataset_name(ds, drr->drr_u.drr_begin.drr_toname); 2492743Sahrens 2502743Sahrens ba.drr = drr; 2512743Sahrens ba.vp = vp; 2522743Sahrens ba.os = tosnap; 2532743Sahrens ZIO_SET_CHECKSUM(&ba.zc, 0, 0, 0, 0); 2542743Sahrens 2552743Sahrens if (dump_bytes(&ba, drr, sizeof (dmu_replay_record_t))) { 2562743Sahrens kmem_free(drr, sizeof (dmu_replay_record_t)); 2572743Sahrens return (ba.err); 2582743Sahrens } 2592743Sahrens 2602743Sahrens err = traverse_dsl_dataset(ds, 2612743Sahrens fromds ? fromds->ds_phys->ds_creation_txg : 0, 2622743Sahrens ADVANCE_PRE | ADVANCE_HOLES | ADVANCE_DATA | ADVANCE_NOLOCK, 2632743Sahrens backup_cb, &ba); 2642743Sahrens 2652743Sahrens if (err) { 2662743Sahrens if (err == EINTR && ba.err) 2672743Sahrens err = ba.err; 2683655Sgw25295 kmem_free(drr, sizeof (dmu_replay_record_t)); 2692743Sahrens return (err); 2702743Sahrens } 2712743Sahrens 2722743Sahrens bzero(drr, sizeof (dmu_replay_record_t)); 2732743Sahrens drr->drr_type = DRR_END; 2742743Sahrens drr->drr_u.drr_end.drr_checksum = ba.zc; 2752743Sahrens 2763655Sgw25295 if (dump_bytes(&ba, drr, sizeof (dmu_replay_record_t))) { 2773655Sgw25295 kmem_free(drr, sizeof (dmu_replay_record_t)); 2782743Sahrens return (ba.err); 2793655Sgw25295 } 2802743Sahrens 2812743Sahrens kmem_free(drr, sizeof (dmu_replay_record_t)); 2822743Sahrens 2832743Sahrens return (0); 2842743Sahrens } 2852743Sahrens 2862743Sahrens struct restorearg { 2872743Sahrens int err; 2882743Sahrens int byteswap; 2892743Sahrens vnode_t *vp; 2902743Sahrens char *buf; 2912743Sahrens uint64_t voff; 2922743Sahrens int buflen; /* number of valid bytes in buf */ 2932743Sahrens int bufoff; /* next offset to read */ 2942743Sahrens int bufsize; /* amount of memory allocated for buf */ 2952743Sahrens zio_cksum_t zc; 2962743Sahrens }; 2972743Sahrens 2982743Sahrens /* ARGSUSED */ 2992743Sahrens static int 3002743Sahrens replay_incremental_check(void *arg1, void *arg2, dmu_tx_t *tx) 3012743Sahrens { 3022743Sahrens dsl_dataset_t *ds = arg1; 3032743Sahrens struct drr_begin *drrb = arg2; 3042743Sahrens const char *snapname; 3052743Sahrens int err; 3062743Sahrens uint64_t val; 3072743Sahrens 3082743Sahrens /* must already be a snapshot of this fs */ 3092743Sahrens if (ds->ds_phys->ds_prev_snap_obj == 0) 3102743Sahrens return (ENODEV); 3112743Sahrens 3122743Sahrens /* most recent snapshot must match fromguid */ 3132743Sahrens if (ds->ds_prev->ds_phys->ds_guid != drrb->drr_fromguid) 3142743Sahrens return (ENODEV); 3152743Sahrens /* must not have any changes since most recent snapshot */ 3162743Sahrens if (ds->ds_phys->ds_bp.blk_birth > 3172743Sahrens ds->ds_prev->ds_phys->ds_creation_txg) 3182743Sahrens return (ETXTBSY); 3192743Sahrens 3202743Sahrens /* new snapshot name must not exist */ 3212743Sahrens snapname = strrchr(drrb->drr_toname, '@'); 3222743Sahrens if (snapname == NULL) 3232743Sahrens return (EEXIST); 3242743Sahrens 3252743Sahrens snapname++; 3262743Sahrens err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset, 3272743Sahrens ds->ds_phys->ds_snapnames_zapobj, snapname, 8, 1, &val); 3282743Sahrens if (err == 0) 3292743Sahrens return (EEXIST); 3302743Sahrens if (err != ENOENT) 3312743Sahrens return (err); 3322743Sahrens 3332743Sahrens return (0); 3342743Sahrens } 3352743Sahrens 3362743Sahrens /* ARGSUSED */ 3372743Sahrens static void 3384543Smarks replay_incremental_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx) 3392743Sahrens { 3402743Sahrens dsl_dataset_t *ds = arg1; 3412743Sahrens dmu_buf_will_dirty(ds->ds_dbuf, tx); 3422743Sahrens ds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT; 3434543Smarks 3444543Smarks spa_history_internal_log(LOG_DS_REPLAY_INC_SYNC, 3454543Smarks ds->ds_dir->dd_pool->dp_spa, tx, cr, "dataset = %lld", 3464543Smarks ds->ds_phys->ds_dir_obj); 3472743Sahrens } 3482743Sahrens 3492743Sahrens /* ARGSUSED */ 3502743Sahrens static int 3512743Sahrens replay_full_check(void *arg1, void *arg2, dmu_tx_t *tx) 3522743Sahrens { 3532743Sahrens dsl_dir_t *dd = arg1; 3542743Sahrens struct drr_begin *drrb = arg2; 3552743Sahrens objset_t *mos = dd->dd_pool->dp_meta_objset; 3562743Sahrens char *cp; 3572743Sahrens uint64_t val; 3582743Sahrens int err; 3592743Sahrens 3602743Sahrens cp = strchr(drrb->drr_toname, '@'); 3612743Sahrens *cp = '\0'; 3622743Sahrens err = zap_lookup(mos, dd->dd_phys->dd_child_dir_zapobj, 3632743Sahrens strrchr(drrb->drr_toname, '/') + 1, 3642743Sahrens sizeof (uint64_t), 1, &val); 3652743Sahrens *cp = '@'; 3662743Sahrens 3672743Sahrens if (err != ENOENT) 3682743Sahrens return (err ? err : EEXIST); 3692743Sahrens 3702743Sahrens return (0); 3712743Sahrens } 3722743Sahrens 3732743Sahrens static void 3744543Smarks replay_full_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx) 3752743Sahrens { 3762743Sahrens dsl_dir_t *dd = arg1; 3772743Sahrens struct drr_begin *drrb = arg2; 3782743Sahrens char *cp; 3792743Sahrens dsl_dataset_t *ds; 3802743Sahrens uint64_t dsobj; 3812743Sahrens 3822743Sahrens cp = strchr(drrb->drr_toname, '@'); 3832743Sahrens *cp = '\0'; 3842743Sahrens dsobj = dsl_dataset_create_sync(dd, strrchr(drrb->drr_toname, '/') + 1, 3852743Sahrens NULL, tx); 3862743Sahrens 3872743Sahrens VERIFY(0 == dsl_dataset_open_obj(dd->dd_pool, dsobj, NULL, 3882743Sahrens DS_MODE_EXCLUSIVE, FTAG, &ds)); 3892743Sahrens 3902743Sahrens (void) dmu_objset_create_impl(dsl_dataset_get_spa(ds), 3913547Smaybee ds, &ds->ds_phys->ds_bp, drrb->drr_type, tx); 3922743Sahrens 3932743Sahrens dmu_buf_will_dirty(ds->ds_dbuf, tx); 3942743Sahrens ds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT; 3952743Sahrens 3964543Smarks spa_history_internal_log(LOG_DS_REPLAY_FULL_SYNC, 3974543Smarks ds->ds_dir->dd_pool->dp_spa, tx, cr, "dataset = %lld", 3984543Smarks ds->ds_phys->ds_dir_obj); 3994543Smarks 4004543Smarks *cp = '@'; 4014543Smarks 4022743Sahrens dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG); 4032743Sahrens } 4042743Sahrens 4052743Sahrens static int 4062743Sahrens replay_end_check(void *arg1, void *arg2, dmu_tx_t *tx) 4072743Sahrens { 4082743Sahrens objset_t *os = arg1; 4092743Sahrens struct drr_begin *drrb = arg2; 4102743Sahrens char *snapname; 4112743Sahrens 4122743Sahrens /* XXX verify that drr_toname is in dd */ 4132743Sahrens 4142743Sahrens snapname = strchr(drrb->drr_toname, '@'); 4152743Sahrens if (snapname == NULL) 4162743Sahrens return (EINVAL); 4172743Sahrens snapname++; 4182743Sahrens 4192743Sahrens return (dsl_dataset_snapshot_check(os, snapname, tx)); 4202743Sahrens } 4212743Sahrens 4222743Sahrens static void 4234543Smarks replay_end_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx) 4242743Sahrens { 4252743Sahrens objset_t *os = arg1; 4262743Sahrens struct drr_begin *drrb = arg2; 4272743Sahrens char *snapname; 4282743Sahrens dsl_dataset_t *ds, *hds; 4292743Sahrens 4302743Sahrens snapname = strchr(drrb->drr_toname, '@') + 1; 4312743Sahrens 4324543Smarks dsl_dataset_snapshot_sync(os, snapname, cr, tx); 4332743Sahrens 4342743Sahrens /* set snapshot's creation time and guid */ 4352743Sahrens hds = os->os->os_dsl_dataset; 4362743Sahrens VERIFY(0 == dsl_dataset_open_obj(hds->ds_dir->dd_pool, 4372743Sahrens hds->ds_phys->ds_prev_snap_obj, NULL, 4382743Sahrens DS_MODE_PRIMARY | DS_MODE_READONLY | DS_MODE_INCONSISTENT, 4392743Sahrens FTAG, &ds)); 4402743Sahrens 4412743Sahrens dmu_buf_will_dirty(ds->ds_dbuf, tx); 4422743Sahrens ds->ds_phys->ds_creation_time = drrb->drr_creation_time; 4432743Sahrens ds->ds_phys->ds_guid = drrb->drr_toguid; 4442743Sahrens ds->ds_phys->ds_flags &= ~DS_FLAG_INCONSISTENT; 4452743Sahrens 4464543Smarks /* log the end of the receive */ 4474543Smarks spa_history_internal_log(LOG_DS_RECEIVE, ds->ds_dir->dd_pool->dp_spa, 4484543Smarks tx, cr, "dataset = %llu", ds->ds_phys->ds_dir_obj); 4494543Smarks 4502743Sahrens dsl_dataset_close(ds, DS_MODE_PRIMARY, FTAG); 4512743Sahrens 4522743Sahrens dmu_buf_will_dirty(hds->ds_dbuf, tx); 4532743Sahrens hds->ds_phys->ds_flags &= ~DS_FLAG_INCONSISTENT; 4542743Sahrens } 4552743Sahrens 4562743Sahrens static void * 4572743Sahrens restore_read(struct restorearg *ra, int len) 4582743Sahrens { 4592743Sahrens void *rv; 4602743Sahrens 4612743Sahrens /* some things will require 8-byte alignment, so everything must */ 4622743Sahrens ASSERT3U(len % 8, ==, 0); 4632743Sahrens 4642743Sahrens while (ra->buflen - ra->bufoff < len) { 4652743Sahrens ssize_t resid; 4662743Sahrens int leftover = ra->buflen - ra->bufoff; 4672743Sahrens 4682743Sahrens (void) memmove(ra->buf, ra->buf + ra->bufoff, leftover); 4692743Sahrens ra->err = vn_rdwr(UIO_READ, ra->vp, 4702743Sahrens (caddr_t)ra->buf + leftover, ra->bufsize - leftover, 4712743Sahrens ra->voff, UIO_SYSSPACE, FAPPEND, 4722743Sahrens RLIM64_INFINITY, CRED(), &resid); 4732743Sahrens 4742743Sahrens ra->voff += ra->bufsize - leftover - resid; 4752743Sahrens ra->buflen = ra->bufsize - resid; 4762743Sahrens ra->bufoff = 0; 4772743Sahrens if (resid == ra->bufsize - leftover) 4782743Sahrens ra->err = EINVAL; 4792743Sahrens if (ra->err) 4802743Sahrens return (NULL); 4812743Sahrens /* Could compute checksum here? */ 4822743Sahrens } 4832743Sahrens 4842743Sahrens ASSERT3U(ra->bufoff % 8, ==, 0); 4852743Sahrens ASSERT3U(ra->buflen - ra->bufoff, >=, len); 4862743Sahrens rv = ra->buf + ra->bufoff; 4872743Sahrens ra->bufoff += len; 4882743Sahrens if (ra->byteswap) 4892743Sahrens fletcher_4_incremental_byteswap(rv, len, &ra->zc); 4902743Sahrens else 4912743Sahrens fletcher_4_incremental_native(rv, len, &ra->zc); 4922743Sahrens return (rv); 4932743Sahrens } 4942743Sahrens 4952743Sahrens static void 4962743Sahrens backup_byteswap(dmu_replay_record_t *drr) 4972743Sahrens { 4982743Sahrens #define DO64(X) (drr->drr_u.X = BSWAP_64(drr->drr_u.X)) 4992743Sahrens #define DO32(X) (drr->drr_u.X = BSWAP_32(drr->drr_u.X)) 5002743Sahrens drr->drr_type = BSWAP_32(drr->drr_type); 5012743Sahrens switch (drr->drr_type) { 5022743Sahrens case DRR_BEGIN: 5032743Sahrens DO64(drr_begin.drr_magic); 5042743Sahrens DO64(drr_begin.drr_version); 5052743Sahrens DO64(drr_begin.drr_creation_time); 5062743Sahrens DO32(drr_begin.drr_type); 5072743Sahrens DO64(drr_begin.drr_toguid); 5082743Sahrens DO64(drr_begin.drr_fromguid); 5092743Sahrens break; 5102743Sahrens case DRR_OBJECT: 5112743Sahrens DO64(drr_object.drr_object); 5122743Sahrens /* DO64(drr_object.drr_allocation_txg); */ 5132743Sahrens DO32(drr_object.drr_type); 5142743Sahrens DO32(drr_object.drr_bonustype); 5152743Sahrens DO32(drr_object.drr_blksz); 5162743Sahrens DO32(drr_object.drr_bonuslen); 5172743Sahrens break; 5182743Sahrens case DRR_FREEOBJECTS: 5192743Sahrens DO64(drr_freeobjects.drr_firstobj); 5202743Sahrens DO64(drr_freeobjects.drr_numobjs); 5212743Sahrens break; 5222743Sahrens case DRR_WRITE: 5232743Sahrens DO64(drr_write.drr_object); 5242743Sahrens DO32(drr_write.drr_type); 5252743Sahrens DO64(drr_write.drr_offset); 5262743Sahrens DO64(drr_write.drr_length); 5272743Sahrens break; 5282743Sahrens case DRR_FREE: 5292743Sahrens DO64(drr_free.drr_object); 5302743Sahrens DO64(drr_free.drr_offset); 5312743Sahrens DO64(drr_free.drr_length); 5322743Sahrens break; 5332743Sahrens case DRR_END: 5342743Sahrens DO64(drr_end.drr_checksum.zc_word[0]); 5352743Sahrens DO64(drr_end.drr_checksum.zc_word[1]); 5362743Sahrens DO64(drr_end.drr_checksum.zc_word[2]); 5372743Sahrens DO64(drr_end.drr_checksum.zc_word[3]); 5382743Sahrens break; 5392743Sahrens } 5402743Sahrens #undef DO64 5412743Sahrens #undef DO32 5422743Sahrens } 5432743Sahrens 5442743Sahrens static int 5452743Sahrens restore_object(struct restorearg *ra, objset_t *os, struct drr_object *drro) 5462743Sahrens { 5472743Sahrens int err; 5482743Sahrens dmu_tx_t *tx; 5492743Sahrens 5502743Sahrens err = dmu_object_info(os, drro->drr_object, NULL); 5512743Sahrens 5522743Sahrens if (err != 0 && err != ENOENT) 5532743Sahrens return (EINVAL); 5542743Sahrens 5552743Sahrens if (drro->drr_type == DMU_OT_NONE || 5562743Sahrens drro->drr_type >= DMU_OT_NUMTYPES || 5572743Sahrens drro->drr_bonustype >= DMU_OT_NUMTYPES || 5582743Sahrens drro->drr_checksum >= ZIO_CHECKSUM_FUNCTIONS || 5592743Sahrens drro->drr_compress >= ZIO_COMPRESS_FUNCTIONS || 5602743Sahrens P2PHASE(drro->drr_blksz, SPA_MINBLOCKSIZE) || 5612743Sahrens drro->drr_blksz < SPA_MINBLOCKSIZE || 5622743Sahrens drro->drr_blksz > SPA_MAXBLOCKSIZE || 5632743Sahrens drro->drr_bonuslen > DN_MAX_BONUSLEN) { 5642743Sahrens return (EINVAL); 5652743Sahrens } 5662743Sahrens 5672743Sahrens tx = dmu_tx_create(os); 5682743Sahrens 5692743Sahrens if (err == ENOENT) { 5702743Sahrens /* currently free, want to be allocated */ 5712743Sahrens dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); 5722743Sahrens dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, 1); 5732743Sahrens err = dmu_tx_assign(tx, TXG_WAIT); 5742743Sahrens if (err) { 5752743Sahrens dmu_tx_abort(tx); 5762743Sahrens return (err); 5772743Sahrens } 5782743Sahrens err = dmu_object_claim(os, drro->drr_object, 5792743Sahrens drro->drr_type, drro->drr_blksz, 5802743Sahrens drro->drr_bonustype, drro->drr_bonuslen, tx); 5812743Sahrens } else { 5822743Sahrens /* currently allocated, want to be allocated */ 5832743Sahrens dmu_tx_hold_bonus(tx, drro->drr_object); 5842743Sahrens /* 5852743Sahrens * We may change blocksize, so need to 5862743Sahrens * hold_write 5872743Sahrens */ 5882743Sahrens dmu_tx_hold_write(tx, drro->drr_object, 0, 1); 5892743Sahrens err = dmu_tx_assign(tx, TXG_WAIT); 5902743Sahrens if (err) { 5912743Sahrens dmu_tx_abort(tx); 5922743Sahrens return (err); 5932743Sahrens } 5942743Sahrens 5952743Sahrens err = dmu_object_reclaim(os, drro->drr_object, 5962743Sahrens drro->drr_type, drro->drr_blksz, 5972743Sahrens drro->drr_bonustype, drro->drr_bonuslen, tx); 5982743Sahrens } 5992743Sahrens if (err) { 6002743Sahrens dmu_tx_commit(tx); 6012743Sahrens return (EINVAL); 6022743Sahrens } 6032743Sahrens 6042743Sahrens dmu_object_set_checksum(os, drro->drr_object, drro->drr_checksum, tx); 6052743Sahrens dmu_object_set_compress(os, drro->drr_object, drro->drr_compress, tx); 6062743Sahrens 6072743Sahrens if (drro->drr_bonuslen) { 6082743Sahrens dmu_buf_t *db; 6092743Sahrens void *data; 6102743Sahrens VERIFY(0 == dmu_bonus_hold(os, drro->drr_object, FTAG, &db)); 6112743Sahrens dmu_buf_will_dirty(db, tx); 6122743Sahrens 613*4944Smaybee ASSERT3U(db->db_size, >=, drro->drr_bonuslen); 614*4944Smaybee data = restore_read(ra, P2ROUNDUP(drro->drr_bonuslen, 8)); 6152743Sahrens if (data == NULL) { 6162743Sahrens dmu_tx_commit(tx); 6172743Sahrens return (ra->err); 6182743Sahrens } 619*4944Smaybee bcopy(data, db->db_data, drro->drr_bonuslen); 6202743Sahrens if (ra->byteswap) { 6212743Sahrens dmu_ot[drro->drr_bonustype].ot_byteswap(db->db_data, 6222743Sahrens drro->drr_bonuslen); 6232743Sahrens } 6242743Sahrens dmu_buf_rele(db, FTAG); 6252743Sahrens } 6262743Sahrens dmu_tx_commit(tx); 6272743Sahrens return (0); 6282743Sahrens } 6292743Sahrens 6302743Sahrens /* ARGSUSED */ 6312743Sahrens static int 6322743Sahrens restore_freeobjects(struct restorearg *ra, objset_t *os, 6332743Sahrens struct drr_freeobjects *drrfo) 6342743Sahrens { 6352743Sahrens uint64_t obj; 6362743Sahrens 6372743Sahrens if (drrfo->drr_firstobj + drrfo->drr_numobjs < drrfo->drr_firstobj) 6382743Sahrens return (EINVAL); 6392743Sahrens 6402743Sahrens for (obj = drrfo->drr_firstobj; 6413087Sahrens obj < drrfo->drr_firstobj + drrfo->drr_numobjs; 6423087Sahrens (void) dmu_object_next(os, &obj, FALSE, 0)) { 6432743Sahrens dmu_tx_t *tx; 6442743Sahrens int err; 6452743Sahrens 6462743Sahrens if (dmu_object_info(os, obj, NULL) != 0) 6472743Sahrens continue; 6482743Sahrens 6492743Sahrens tx = dmu_tx_create(os); 6502743Sahrens dmu_tx_hold_bonus(tx, obj); 6512743Sahrens err = dmu_tx_assign(tx, TXG_WAIT); 6522743Sahrens if (err) { 6532743Sahrens dmu_tx_abort(tx); 6542743Sahrens return (err); 6552743Sahrens } 6562743Sahrens err = dmu_object_free(os, obj, tx); 6572743Sahrens dmu_tx_commit(tx); 6582743Sahrens if (err && err != ENOENT) 6592743Sahrens return (EINVAL); 6602743Sahrens } 6612743Sahrens return (0); 6622743Sahrens } 6632743Sahrens 6642743Sahrens static int 6652743Sahrens restore_write(struct restorearg *ra, objset_t *os, 6662743Sahrens struct drr_write *drrw) 6672743Sahrens { 6682743Sahrens dmu_tx_t *tx; 6692743Sahrens void *data; 6702743Sahrens int err; 6712743Sahrens 6722743Sahrens if (drrw->drr_offset + drrw->drr_length < drrw->drr_offset || 6732743Sahrens drrw->drr_type >= DMU_OT_NUMTYPES) 6742743Sahrens return (EINVAL); 6752743Sahrens 6762743Sahrens data = restore_read(ra, drrw->drr_length); 6772743Sahrens if (data == NULL) 6782743Sahrens return (ra->err); 6792743Sahrens 6802743Sahrens if (dmu_object_info(os, drrw->drr_object, NULL) != 0) 6812743Sahrens return (EINVAL); 6822743Sahrens 6832743Sahrens tx = dmu_tx_create(os); 6842743Sahrens 6852743Sahrens dmu_tx_hold_write(tx, drrw->drr_object, 6862743Sahrens drrw->drr_offset, drrw->drr_length); 6872743Sahrens err = dmu_tx_assign(tx, TXG_WAIT); 6882743Sahrens if (err) { 6892743Sahrens dmu_tx_abort(tx); 6902743Sahrens return (err); 6912743Sahrens } 6922743Sahrens if (ra->byteswap) 6932743Sahrens dmu_ot[drrw->drr_type].ot_byteswap(data, drrw->drr_length); 6942743Sahrens dmu_write(os, drrw->drr_object, 6952743Sahrens drrw->drr_offset, drrw->drr_length, data, tx); 6962743Sahrens dmu_tx_commit(tx); 6972743Sahrens return (0); 6982743Sahrens } 6992743Sahrens 7002743Sahrens /* ARGSUSED */ 7012743Sahrens static int 7022743Sahrens restore_free(struct restorearg *ra, objset_t *os, 7032743Sahrens struct drr_free *drrf) 7042743Sahrens { 7052743Sahrens dmu_tx_t *tx; 7062743Sahrens int err; 7072743Sahrens 7082743Sahrens if (drrf->drr_length != -1ULL && 7092743Sahrens drrf->drr_offset + drrf->drr_length < drrf->drr_offset) 7102743Sahrens return (EINVAL); 7112743Sahrens 7122743Sahrens if (dmu_object_info(os, drrf->drr_object, NULL) != 0) 7132743Sahrens return (EINVAL); 7142743Sahrens 7152743Sahrens tx = dmu_tx_create(os); 7162743Sahrens 7172743Sahrens dmu_tx_hold_free(tx, drrf->drr_object, 7182743Sahrens drrf->drr_offset, drrf->drr_length); 7192743Sahrens err = dmu_tx_assign(tx, TXG_WAIT); 7202743Sahrens if (err) { 7212743Sahrens dmu_tx_abort(tx); 7222743Sahrens return (err); 7232743Sahrens } 7242743Sahrens err = dmu_free_range(os, drrf->drr_object, 7252743Sahrens drrf->drr_offset, drrf->drr_length, tx); 7262743Sahrens dmu_tx_commit(tx); 7272743Sahrens return (err); 7282743Sahrens } 7292743Sahrens 7302743Sahrens int 7312743Sahrens dmu_recvbackup(char *tosnap, struct drr_begin *drrb, uint64_t *sizep, 7322743Sahrens boolean_t force, vnode_t *vp, uint64_t voffset) 7332743Sahrens { 7342743Sahrens struct restorearg ra; 7352743Sahrens dmu_replay_record_t *drr; 7362743Sahrens char *cp; 7372743Sahrens objset_t *os = NULL; 7382743Sahrens zio_cksum_t pzc; 7392743Sahrens 7402743Sahrens bzero(&ra, sizeof (ra)); 7412743Sahrens ra.vp = vp; 7422743Sahrens ra.voff = voffset; 7432743Sahrens ra.bufsize = 1<<20; 7442743Sahrens ra.buf = kmem_alloc(ra.bufsize, KM_SLEEP); 7452743Sahrens 7462743Sahrens if (drrb->drr_magic == DMU_BACKUP_MAGIC) { 7472743Sahrens ra.byteswap = FALSE; 7482743Sahrens } else if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) { 7492743Sahrens ra.byteswap = TRUE; 7502743Sahrens } else { 7512743Sahrens ra.err = EINVAL; 7522743Sahrens goto out; 7532743Sahrens } 7542743Sahrens 7552743Sahrens /* 7562743Sahrens * NB: this assumes that struct drr_begin will be the largest in 7572743Sahrens * dmu_replay_record_t's drr_u, and thus we don't need to pad it 7582743Sahrens * with zeros to make it the same length as we wrote out. 7592743Sahrens */ 7602743Sahrens ((dmu_replay_record_t *)ra.buf)->drr_type = DRR_BEGIN; 7612743Sahrens ((dmu_replay_record_t *)ra.buf)->drr_pad = 0; 7622743Sahrens ((dmu_replay_record_t *)ra.buf)->drr_u.drr_begin = *drrb; 7632743Sahrens if (ra.byteswap) { 7642743Sahrens fletcher_4_incremental_byteswap(ra.buf, 7652743Sahrens sizeof (dmu_replay_record_t), &ra.zc); 7662743Sahrens } else { 7672743Sahrens fletcher_4_incremental_native(ra.buf, 7682743Sahrens sizeof (dmu_replay_record_t), &ra.zc); 7692743Sahrens } 7702743Sahrens (void) strcpy(drrb->drr_toname, tosnap); /* for the sync funcs */ 7712743Sahrens 7722743Sahrens if (ra.byteswap) { 7732743Sahrens drrb->drr_magic = BSWAP_64(drrb->drr_magic); 7742743Sahrens drrb->drr_version = BSWAP_64(drrb->drr_version); 7752743Sahrens drrb->drr_creation_time = BSWAP_64(drrb->drr_creation_time); 7762743Sahrens drrb->drr_type = BSWAP_32(drrb->drr_type); 7772743Sahrens drrb->drr_toguid = BSWAP_64(drrb->drr_toguid); 7782743Sahrens drrb->drr_fromguid = BSWAP_64(drrb->drr_fromguid); 7792743Sahrens } 7802743Sahrens 7812743Sahrens ASSERT3U(drrb->drr_magic, ==, DMU_BACKUP_MAGIC); 7822743Sahrens 7832743Sahrens if (drrb->drr_version != DMU_BACKUP_VERSION || 7842743Sahrens drrb->drr_type >= DMU_OST_NUMTYPES || 7852743Sahrens strchr(drrb->drr_toname, '@') == NULL) { 7862743Sahrens ra.err = EINVAL; 7872743Sahrens goto out; 7882743Sahrens } 7892743Sahrens 7902743Sahrens /* 7912743Sahrens * Process the begin in syncing context. 7922743Sahrens */ 7932743Sahrens if (drrb->drr_fromguid) { 7942743Sahrens /* incremental backup */ 7952743Sahrens dsl_dataset_t *ds = NULL; 7962743Sahrens 7972743Sahrens cp = strchr(tosnap, '@'); 7982743Sahrens *cp = '\0'; 7992743Sahrens ra.err = dsl_dataset_open(tosnap, DS_MODE_EXCLUSIVE, FTAG, &ds); 8002743Sahrens *cp = '@'; 8012743Sahrens if (ra.err) 8022743Sahrens goto out; 8032743Sahrens 8042743Sahrens /* 8052743Sahrens * Only do the rollback if the most recent snapshot 8062743Sahrens * matches the incremental source 8072743Sahrens */ 8082743Sahrens if (force) { 8092885Sahrens if (ds->ds_prev == NULL || 8102885Sahrens ds->ds_prev->ds_phys->ds_guid != 8112743Sahrens drrb->drr_fromguid) { 8122743Sahrens dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG); 8133655Sgw25295 kmem_free(ra.buf, ra.bufsize); 8142743Sahrens return (ENODEV); 8152743Sahrens } 8162743Sahrens (void) dsl_dataset_rollback(ds); 8172743Sahrens } 8182743Sahrens ra.err = dsl_sync_task_do(ds->ds_dir->dd_pool, 8192743Sahrens replay_incremental_check, replay_incremental_sync, 8202743Sahrens ds, drrb, 1); 8212743Sahrens dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG); 8222743Sahrens } else { 8232743Sahrens /* full backup */ 8242743Sahrens dsl_dir_t *dd = NULL; 8252743Sahrens const char *tail; 8262743Sahrens 8272743Sahrens /* can't restore full backup into topmost fs, for now */ 8282743Sahrens if (strrchr(drrb->drr_toname, '/') == NULL) { 8292743Sahrens ra.err = EINVAL; 8302743Sahrens goto out; 8312743Sahrens } 8322743Sahrens 8332743Sahrens cp = strchr(tosnap, '@'); 8342743Sahrens *cp = '\0'; 8352743Sahrens ra.err = dsl_dir_open(tosnap, FTAG, &dd, &tail); 8362743Sahrens *cp = '@'; 8372743Sahrens if (ra.err) 8382743Sahrens goto out; 8392743Sahrens if (tail == NULL) { 8402743Sahrens ra.err = EEXIST; 8412743Sahrens goto out; 8422743Sahrens } 8432743Sahrens 8442743Sahrens ra.err = dsl_sync_task_do(dd->dd_pool, replay_full_check, 8452743Sahrens replay_full_sync, dd, drrb, 5); 8462743Sahrens dsl_dir_close(dd, FTAG); 8472743Sahrens } 8482743Sahrens if (ra.err) 8492743Sahrens goto out; 8502743Sahrens 8512743Sahrens /* 8522743Sahrens * Open the objset we are modifying. 8532743Sahrens */ 8542743Sahrens 8552743Sahrens cp = strchr(tosnap, '@'); 8562743Sahrens *cp = '\0'; 8572743Sahrens ra.err = dmu_objset_open(tosnap, DMU_OST_ANY, 8582743Sahrens DS_MODE_PRIMARY | DS_MODE_INCONSISTENT, &os); 8592743Sahrens *cp = '@'; 8602743Sahrens ASSERT3U(ra.err, ==, 0); 8612743Sahrens 8622743Sahrens /* 8632743Sahrens * Read records and process them. 8642743Sahrens */ 8652743Sahrens pzc = ra.zc; 8662743Sahrens while (ra.err == 0 && 8672743Sahrens NULL != (drr = restore_read(&ra, sizeof (*drr)))) { 8682743Sahrens if (issig(JUSTLOOKING) && issig(FORREAL)) { 8692743Sahrens ra.err = EINTR; 8702743Sahrens goto out; 8712743Sahrens } 8722743Sahrens 8732743Sahrens if (ra.byteswap) 8742743Sahrens backup_byteswap(drr); 8752743Sahrens 8762743Sahrens switch (drr->drr_type) { 8772743Sahrens case DRR_OBJECT: 8782743Sahrens { 8792743Sahrens /* 8802743Sahrens * We need to make a copy of the record header, 8812743Sahrens * because restore_{object,write} may need to 8822743Sahrens * restore_read(), which will invalidate drr. 8832743Sahrens */ 8842743Sahrens struct drr_object drro = drr->drr_u.drr_object; 8852743Sahrens ra.err = restore_object(&ra, os, &drro); 8862743Sahrens break; 8872743Sahrens } 8882743Sahrens case DRR_FREEOBJECTS: 8892743Sahrens { 8902743Sahrens struct drr_freeobjects drrfo = 8912743Sahrens drr->drr_u.drr_freeobjects; 8922743Sahrens ra.err = restore_freeobjects(&ra, os, &drrfo); 8932743Sahrens break; 8942743Sahrens } 8952743Sahrens case DRR_WRITE: 8962743Sahrens { 8972743Sahrens struct drr_write drrw = drr->drr_u.drr_write; 8982743Sahrens ra.err = restore_write(&ra, os, &drrw); 8992743Sahrens break; 9002743Sahrens } 9012743Sahrens case DRR_FREE: 9022743Sahrens { 9032743Sahrens struct drr_free drrf = drr->drr_u.drr_free; 9042743Sahrens ra.err = restore_free(&ra, os, &drrf); 9052743Sahrens break; 9062743Sahrens } 9072743Sahrens case DRR_END: 9082743Sahrens { 9092743Sahrens struct drr_end drre = drr->drr_u.drr_end; 9102743Sahrens /* 9112743Sahrens * We compare against the *previous* checksum 9122743Sahrens * value, because the stored checksum is of 9132743Sahrens * everything before the DRR_END record. 9142743Sahrens */ 9152743Sahrens if (drre.drr_checksum.zc_word[0] != 0 && 9163093Sahrens !ZIO_CHECKSUM_EQUAL(drre.drr_checksum, pzc)) { 9172743Sahrens ra.err = ECKSUM; 9182743Sahrens goto out; 9192743Sahrens } 9202743Sahrens 9212743Sahrens ra.err = dsl_sync_task_do(dmu_objset_ds(os)-> 9222743Sahrens ds_dir->dd_pool, replay_end_check, replay_end_sync, 9232743Sahrens os, drrb, 3); 9242743Sahrens goto out; 9252743Sahrens } 9262743Sahrens default: 9272743Sahrens ra.err = EINVAL; 9282743Sahrens goto out; 9292743Sahrens } 9302743Sahrens pzc = ra.zc; 9312743Sahrens } 9322743Sahrens 9332743Sahrens out: 9342743Sahrens if (os) 9352743Sahrens dmu_objset_close(os); 9362743Sahrens 9372743Sahrens /* 9382743Sahrens * Make sure we don't rollback/destroy unless we actually 9392743Sahrens * processed the begin properly. 'os' will only be set if this 9402743Sahrens * is the case. 9412743Sahrens */ 9422743Sahrens if (ra.err && os && tosnap && strchr(tosnap, '@')) { 9432743Sahrens /* 9442743Sahrens * rollback or destroy what we created, so we don't 9452743Sahrens * leave it in the restoring state. 9462743Sahrens */ 9472743Sahrens dsl_dataset_t *ds; 9482743Sahrens int err; 9492743Sahrens 9502743Sahrens cp = strchr(tosnap, '@'); 9512743Sahrens *cp = '\0'; 9522743Sahrens err = dsl_dataset_open(tosnap, 9532743Sahrens DS_MODE_EXCLUSIVE | DS_MODE_INCONSISTENT, 9542743Sahrens FTAG, &ds); 9552743Sahrens if (err == 0) { 9562743Sahrens txg_wait_synced(ds->ds_dir->dd_pool, 0); 9572743Sahrens if (drrb->drr_fromguid) { 9582743Sahrens /* incremental: rollback to most recent snap */ 9592743Sahrens (void) dsl_dataset_rollback(ds); 9602743Sahrens dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG); 9612743Sahrens } else { 9622743Sahrens /* full: destroy whole fs */ 9632743Sahrens dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG); 9642743Sahrens (void) dsl_dataset_destroy(tosnap); 9652743Sahrens } 9662743Sahrens } 9672743Sahrens *cp = '@'; 9682743Sahrens } 9692743Sahrens 9702743Sahrens kmem_free(ra.buf, ra.bufsize); 9712743Sahrens if (sizep) 9722743Sahrens *sizep = ra.voff; 9732743Sahrens return (ra.err); 9742743Sahrens } 975