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 /* 222743Sahrens * Copyright 2006 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; 2682743Sahrens return (err); 2692743Sahrens } 2702743Sahrens 2712743Sahrens bzero(drr, sizeof (dmu_replay_record_t)); 2722743Sahrens drr->drr_type = DRR_END; 2732743Sahrens drr->drr_u.drr_end.drr_checksum = ba.zc; 2742743Sahrens 2752743Sahrens if (dump_bytes(&ba, drr, sizeof (dmu_replay_record_t))) 2762743Sahrens return (ba.err); 2772743Sahrens 2782743Sahrens kmem_free(drr, sizeof (dmu_replay_record_t)); 2792743Sahrens 2802743Sahrens return (0); 2812743Sahrens } 2822743Sahrens 2832743Sahrens struct restorearg { 2842743Sahrens int err; 2852743Sahrens int byteswap; 2862743Sahrens vnode_t *vp; 2872743Sahrens char *buf; 2882743Sahrens uint64_t voff; 2892743Sahrens int buflen; /* number of valid bytes in buf */ 2902743Sahrens int bufoff; /* next offset to read */ 2912743Sahrens int bufsize; /* amount of memory allocated for buf */ 2922743Sahrens zio_cksum_t zc; 2932743Sahrens }; 2942743Sahrens 2952743Sahrens /* ARGSUSED */ 2962743Sahrens static int 2972743Sahrens replay_incremental_check(void *arg1, void *arg2, dmu_tx_t *tx) 2982743Sahrens { 2992743Sahrens dsl_dataset_t *ds = arg1; 3002743Sahrens struct drr_begin *drrb = arg2; 3012743Sahrens const char *snapname; 3022743Sahrens int err; 3032743Sahrens uint64_t val; 3042743Sahrens 3052743Sahrens /* must already be a snapshot of this fs */ 3062743Sahrens if (ds->ds_phys->ds_prev_snap_obj == 0) 3072743Sahrens return (ENODEV); 3082743Sahrens 3092743Sahrens /* most recent snapshot must match fromguid */ 3102743Sahrens if (ds->ds_prev->ds_phys->ds_guid != drrb->drr_fromguid) 3112743Sahrens return (ENODEV); 3122743Sahrens /* must not have any changes since most recent snapshot */ 3132743Sahrens if (ds->ds_phys->ds_bp.blk_birth > 3142743Sahrens ds->ds_prev->ds_phys->ds_creation_txg) 3152743Sahrens return (ETXTBSY); 3162743Sahrens 3172743Sahrens /* new snapshot name must not exist */ 3182743Sahrens snapname = strrchr(drrb->drr_toname, '@'); 3192743Sahrens if (snapname == NULL) 3202743Sahrens return (EEXIST); 3212743Sahrens 3222743Sahrens snapname++; 3232743Sahrens err = zap_lookup(ds->ds_dir->dd_pool->dp_meta_objset, 3242743Sahrens ds->ds_phys->ds_snapnames_zapobj, snapname, 8, 1, &val); 3252743Sahrens if (err == 0) 3262743Sahrens return (EEXIST); 3272743Sahrens if (err != ENOENT) 3282743Sahrens return (err); 3292743Sahrens 3302743Sahrens return (0); 3312743Sahrens } 3322743Sahrens 3332743Sahrens /* ARGSUSED */ 3342743Sahrens static void 3352743Sahrens replay_incremental_sync(void *arg1, void *arg2, dmu_tx_t *tx) 3362743Sahrens { 3372743Sahrens dsl_dataset_t *ds = arg1; 3382743Sahrens dmu_buf_will_dirty(ds->ds_dbuf, tx); 3392743Sahrens ds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT; 3402743Sahrens } 3412743Sahrens 3422743Sahrens /* ARGSUSED */ 3432743Sahrens static int 3442743Sahrens replay_full_check(void *arg1, void *arg2, dmu_tx_t *tx) 3452743Sahrens { 3462743Sahrens dsl_dir_t *dd = arg1; 3472743Sahrens struct drr_begin *drrb = arg2; 3482743Sahrens objset_t *mos = dd->dd_pool->dp_meta_objset; 3492743Sahrens char *cp; 3502743Sahrens uint64_t val; 3512743Sahrens int err; 3522743Sahrens 3532743Sahrens cp = strchr(drrb->drr_toname, '@'); 3542743Sahrens *cp = '\0'; 3552743Sahrens err = zap_lookup(mos, dd->dd_phys->dd_child_dir_zapobj, 3562743Sahrens strrchr(drrb->drr_toname, '/') + 1, 3572743Sahrens sizeof (uint64_t), 1, &val); 3582743Sahrens *cp = '@'; 3592743Sahrens 3602743Sahrens if (err != ENOENT) 3612743Sahrens return (err ? err : EEXIST); 3622743Sahrens 3632743Sahrens return (0); 3642743Sahrens } 3652743Sahrens 3662743Sahrens static void 3672743Sahrens replay_full_sync(void *arg1, void *arg2, dmu_tx_t *tx) 3682743Sahrens { 3692743Sahrens dsl_dir_t *dd = arg1; 3702743Sahrens struct drr_begin *drrb = arg2; 3712743Sahrens char *cp; 3722743Sahrens dsl_dataset_t *ds; 3732743Sahrens uint64_t dsobj; 3742743Sahrens 3752743Sahrens cp = strchr(drrb->drr_toname, '@'); 3762743Sahrens *cp = '\0'; 3772743Sahrens dsobj = dsl_dataset_create_sync(dd, strrchr(drrb->drr_toname, '/') + 1, 3782743Sahrens NULL, tx); 3792743Sahrens *cp = '@'; 3802743Sahrens 3812743Sahrens VERIFY(0 == dsl_dataset_open_obj(dd->dd_pool, dsobj, NULL, 3822743Sahrens DS_MODE_EXCLUSIVE, FTAG, &ds)); 3832743Sahrens 3842743Sahrens (void) dmu_objset_create_impl(dsl_dataset_get_spa(ds), 3852743Sahrens ds, drrb->drr_type, tx); 3862743Sahrens 3872743Sahrens dmu_buf_will_dirty(ds->ds_dbuf, tx); 3882743Sahrens ds->ds_phys->ds_flags |= DS_FLAG_INCONSISTENT; 3892743Sahrens 3902743Sahrens dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG); 3912743Sahrens } 3922743Sahrens 3932743Sahrens static int 3942743Sahrens replay_end_check(void *arg1, void *arg2, dmu_tx_t *tx) 3952743Sahrens { 3962743Sahrens objset_t *os = arg1; 3972743Sahrens struct drr_begin *drrb = arg2; 3982743Sahrens char *snapname; 3992743Sahrens 4002743Sahrens /* XXX verify that drr_toname is in dd */ 4012743Sahrens 4022743Sahrens snapname = strchr(drrb->drr_toname, '@'); 4032743Sahrens if (snapname == NULL) 4042743Sahrens return (EINVAL); 4052743Sahrens snapname++; 4062743Sahrens 4072743Sahrens return (dsl_dataset_snapshot_check(os, snapname, tx)); 4082743Sahrens } 4092743Sahrens 4102743Sahrens static void 4112743Sahrens replay_end_sync(void *arg1, void *arg2, dmu_tx_t *tx) 4122743Sahrens { 4132743Sahrens objset_t *os = arg1; 4142743Sahrens struct drr_begin *drrb = arg2; 4152743Sahrens char *snapname; 4162743Sahrens dsl_dataset_t *ds, *hds; 4172743Sahrens 4182743Sahrens snapname = strchr(drrb->drr_toname, '@') + 1; 4192743Sahrens 4202743Sahrens dsl_dataset_snapshot_sync(os, snapname, tx); 4212743Sahrens 4222743Sahrens /* set snapshot's creation time and guid */ 4232743Sahrens hds = os->os->os_dsl_dataset; 4242743Sahrens VERIFY(0 == dsl_dataset_open_obj(hds->ds_dir->dd_pool, 4252743Sahrens hds->ds_phys->ds_prev_snap_obj, NULL, 4262743Sahrens DS_MODE_PRIMARY | DS_MODE_READONLY | DS_MODE_INCONSISTENT, 4272743Sahrens FTAG, &ds)); 4282743Sahrens 4292743Sahrens dmu_buf_will_dirty(ds->ds_dbuf, tx); 4302743Sahrens ds->ds_phys->ds_creation_time = drrb->drr_creation_time; 4312743Sahrens ds->ds_phys->ds_guid = drrb->drr_toguid; 4322743Sahrens ds->ds_phys->ds_flags &= ~DS_FLAG_INCONSISTENT; 4332743Sahrens 4342743Sahrens dsl_dataset_close(ds, DS_MODE_PRIMARY, FTAG); 4352743Sahrens 4362743Sahrens dmu_buf_will_dirty(hds->ds_dbuf, tx); 4372743Sahrens hds->ds_phys->ds_flags &= ~DS_FLAG_INCONSISTENT; 4382743Sahrens } 4392743Sahrens 4402743Sahrens static void * 4412743Sahrens restore_read(struct restorearg *ra, int len) 4422743Sahrens { 4432743Sahrens void *rv; 4442743Sahrens 4452743Sahrens /* some things will require 8-byte alignment, so everything must */ 4462743Sahrens ASSERT3U(len % 8, ==, 0); 4472743Sahrens 4482743Sahrens while (ra->buflen - ra->bufoff < len) { 4492743Sahrens ssize_t resid; 4502743Sahrens int leftover = ra->buflen - ra->bufoff; 4512743Sahrens 4522743Sahrens (void) memmove(ra->buf, ra->buf + ra->bufoff, leftover); 4532743Sahrens ra->err = vn_rdwr(UIO_READ, ra->vp, 4542743Sahrens (caddr_t)ra->buf + leftover, ra->bufsize - leftover, 4552743Sahrens ra->voff, UIO_SYSSPACE, FAPPEND, 4562743Sahrens RLIM64_INFINITY, CRED(), &resid); 4572743Sahrens 4582743Sahrens ra->voff += ra->bufsize - leftover - resid; 4592743Sahrens ra->buflen = ra->bufsize - resid; 4602743Sahrens ra->bufoff = 0; 4612743Sahrens if (resid == ra->bufsize - leftover) 4622743Sahrens ra->err = EINVAL; 4632743Sahrens if (ra->err) 4642743Sahrens return (NULL); 4652743Sahrens /* Could compute checksum here? */ 4662743Sahrens } 4672743Sahrens 4682743Sahrens ASSERT3U(ra->bufoff % 8, ==, 0); 4692743Sahrens ASSERT3U(ra->buflen - ra->bufoff, >=, len); 4702743Sahrens rv = ra->buf + ra->bufoff; 4712743Sahrens ra->bufoff += len; 4722743Sahrens if (ra->byteswap) 4732743Sahrens fletcher_4_incremental_byteswap(rv, len, &ra->zc); 4742743Sahrens else 4752743Sahrens fletcher_4_incremental_native(rv, len, &ra->zc); 4762743Sahrens return (rv); 4772743Sahrens } 4782743Sahrens 4792743Sahrens static void 4802743Sahrens backup_byteswap(dmu_replay_record_t *drr) 4812743Sahrens { 4822743Sahrens #define DO64(X) (drr->drr_u.X = BSWAP_64(drr->drr_u.X)) 4832743Sahrens #define DO32(X) (drr->drr_u.X = BSWAP_32(drr->drr_u.X)) 4842743Sahrens drr->drr_type = BSWAP_32(drr->drr_type); 4852743Sahrens switch (drr->drr_type) { 4862743Sahrens case DRR_BEGIN: 4872743Sahrens DO64(drr_begin.drr_magic); 4882743Sahrens DO64(drr_begin.drr_version); 4892743Sahrens DO64(drr_begin.drr_creation_time); 4902743Sahrens DO32(drr_begin.drr_type); 4912743Sahrens DO64(drr_begin.drr_toguid); 4922743Sahrens DO64(drr_begin.drr_fromguid); 4932743Sahrens break; 4942743Sahrens case DRR_OBJECT: 4952743Sahrens DO64(drr_object.drr_object); 4962743Sahrens /* DO64(drr_object.drr_allocation_txg); */ 4972743Sahrens DO32(drr_object.drr_type); 4982743Sahrens DO32(drr_object.drr_bonustype); 4992743Sahrens DO32(drr_object.drr_blksz); 5002743Sahrens DO32(drr_object.drr_bonuslen); 5012743Sahrens break; 5022743Sahrens case DRR_FREEOBJECTS: 5032743Sahrens DO64(drr_freeobjects.drr_firstobj); 5042743Sahrens DO64(drr_freeobjects.drr_numobjs); 5052743Sahrens break; 5062743Sahrens case DRR_WRITE: 5072743Sahrens DO64(drr_write.drr_object); 5082743Sahrens DO32(drr_write.drr_type); 5092743Sahrens DO64(drr_write.drr_offset); 5102743Sahrens DO64(drr_write.drr_length); 5112743Sahrens break; 5122743Sahrens case DRR_FREE: 5132743Sahrens DO64(drr_free.drr_object); 5142743Sahrens DO64(drr_free.drr_offset); 5152743Sahrens DO64(drr_free.drr_length); 5162743Sahrens break; 5172743Sahrens case DRR_END: 5182743Sahrens DO64(drr_end.drr_checksum.zc_word[0]); 5192743Sahrens DO64(drr_end.drr_checksum.zc_word[1]); 5202743Sahrens DO64(drr_end.drr_checksum.zc_word[2]); 5212743Sahrens DO64(drr_end.drr_checksum.zc_word[3]); 5222743Sahrens break; 5232743Sahrens } 5242743Sahrens #undef DO64 5252743Sahrens #undef DO32 5262743Sahrens } 5272743Sahrens 5282743Sahrens static int 5292743Sahrens restore_object(struct restorearg *ra, objset_t *os, struct drr_object *drro) 5302743Sahrens { 5312743Sahrens int err; 5322743Sahrens dmu_tx_t *tx; 5332743Sahrens 5342743Sahrens err = dmu_object_info(os, drro->drr_object, NULL); 5352743Sahrens 5362743Sahrens if (err != 0 && err != ENOENT) 5372743Sahrens return (EINVAL); 5382743Sahrens 5392743Sahrens if (drro->drr_type == DMU_OT_NONE || 5402743Sahrens drro->drr_type >= DMU_OT_NUMTYPES || 5412743Sahrens drro->drr_bonustype >= DMU_OT_NUMTYPES || 5422743Sahrens drro->drr_checksum >= ZIO_CHECKSUM_FUNCTIONS || 5432743Sahrens drro->drr_compress >= ZIO_COMPRESS_FUNCTIONS || 5442743Sahrens P2PHASE(drro->drr_blksz, SPA_MINBLOCKSIZE) || 5452743Sahrens drro->drr_blksz < SPA_MINBLOCKSIZE || 5462743Sahrens drro->drr_blksz > SPA_MAXBLOCKSIZE || 5472743Sahrens drro->drr_bonuslen > DN_MAX_BONUSLEN) { 5482743Sahrens return (EINVAL); 5492743Sahrens } 5502743Sahrens 5512743Sahrens tx = dmu_tx_create(os); 5522743Sahrens 5532743Sahrens if (err == ENOENT) { 5542743Sahrens /* currently free, want to be allocated */ 5552743Sahrens dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT); 5562743Sahrens dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, 1); 5572743Sahrens err = dmu_tx_assign(tx, TXG_WAIT); 5582743Sahrens if (err) { 5592743Sahrens dmu_tx_abort(tx); 5602743Sahrens return (err); 5612743Sahrens } 5622743Sahrens err = dmu_object_claim(os, drro->drr_object, 5632743Sahrens drro->drr_type, drro->drr_blksz, 5642743Sahrens drro->drr_bonustype, drro->drr_bonuslen, tx); 5652743Sahrens } else { 5662743Sahrens /* currently allocated, want to be allocated */ 5672743Sahrens dmu_tx_hold_bonus(tx, drro->drr_object); 5682743Sahrens /* 5692743Sahrens * We may change blocksize, so need to 5702743Sahrens * hold_write 5712743Sahrens */ 5722743Sahrens dmu_tx_hold_write(tx, drro->drr_object, 0, 1); 5732743Sahrens err = dmu_tx_assign(tx, TXG_WAIT); 5742743Sahrens if (err) { 5752743Sahrens dmu_tx_abort(tx); 5762743Sahrens return (err); 5772743Sahrens } 5782743Sahrens 5792743Sahrens err = dmu_object_reclaim(os, drro->drr_object, 5802743Sahrens drro->drr_type, drro->drr_blksz, 5812743Sahrens drro->drr_bonustype, drro->drr_bonuslen, tx); 5822743Sahrens } 5832743Sahrens if (err) { 5842743Sahrens dmu_tx_commit(tx); 5852743Sahrens return (EINVAL); 5862743Sahrens } 5872743Sahrens 5882743Sahrens dmu_object_set_checksum(os, drro->drr_object, drro->drr_checksum, tx); 5892743Sahrens dmu_object_set_compress(os, drro->drr_object, drro->drr_compress, tx); 5902743Sahrens 5912743Sahrens if (drro->drr_bonuslen) { 5922743Sahrens dmu_buf_t *db; 5932743Sahrens void *data; 5942743Sahrens VERIFY(0 == dmu_bonus_hold(os, drro->drr_object, FTAG, &db)); 5952743Sahrens dmu_buf_will_dirty(db, tx); 5962743Sahrens 5972743Sahrens ASSERT3U(db->db_size, ==, drro->drr_bonuslen); 5982743Sahrens data = restore_read(ra, P2ROUNDUP(db->db_size, 8)); 5992743Sahrens if (data == NULL) { 6002743Sahrens dmu_tx_commit(tx); 6012743Sahrens return (ra->err); 6022743Sahrens } 6032743Sahrens bcopy(data, db->db_data, db->db_size); 6042743Sahrens if (ra->byteswap) { 6052743Sahrens dmu_ot[drro->drr_bonustype].ot_byteswap(db->db_data, 6062743Sahrens drro->drr_bonuslen); 6072743Sahrens } 6082743Sahrens dmu_buf_rele(db, FTAG); 6092743Sahrens } 6102743Sahrens dmu_tx_commit(tx); 6112743Sahrens return (0); 6122743Sahrens } 6132743Sahrens 6142743Sahrens /* ARGSUSED */ 6152743Sahrens static int 6162743Sahrens restore_freeobjects(struct restorearg *ra, objset_t *os, 6172743Sahrens struct drr_freeobjects *drrfo) 6182743Sahrens { 6192743Sahrens uint64_t obj; 6202743Sahrens 6212743Sahrens if (drrfo->drr_firstobj + drrfo->drr_numobjs < drrfo->drr_firstobj) 6222743Sahrens return (EINVAL); 6232743Sahrens 6242743Sahrens for (obj = drrfo->drr_firstobj; 625*3087Sahrens obj < drrfo->drr_firstobj + drrfo->drr_numobjs; 626*3087Sahrens (void) dmu_object_next(os, &obj, FALSE, 0)) { 6272743Sahrens dmu_tx_t *tx; 6282743Sahrens int err; 6292743Sahrens 6302743Sahrens if (dmu_object_info(os, obj, NULL) != 0) 6312743Sahrens continue; 6322743Sahrens 6332743Sahrens tx = dmu_tx_create(os); 6342743Sahrens dmu_tx_hold_bonus(tx, obj); 6352743Sahrens err = dmu_tx_assign(tx, TXG_WAIT); 6362743Sahrens if (err) { 6372743Sahrens dmu_tx_abort(tx); 6382743Sahrens return (err); 6392743Sahrens } 6402743Sahrens err = dmu_object_free(os, obj, tx); 6412743Sahrens dmu_tx_commit(tx); 6422743Sahrens if (err && err != ENOENT) 6432743Sahrens return (EINVAL); 6442743Sahrens } 6452743Sahrens return (0); 6462743Sahrens } 6472743Sahrens 6482743Sahrens static int 6492743Sahrens restore_write(struct restorearg *ra, objset_t *os, 6502743Sahrens struct drr_write *drrw) 6512743Sahrens { 6522743Sahrens dmu_tx_t *tx; 6532743Sahrens void *data; 6542743Sahrens int err; 6552743Sahrens 6562743Sahrens if (drrw->drr_offset + drrw->drr_length < drrw->drr_offset || 6572743Sahrens drrw->drr_type >= DMU_OT_NUMTYPES) 6582743Sahrens return (EINVAL); 6592743Sahrens 6602743Sahrens data = restore_read(ra, drrw->drr_length); 6612743Sahrens if (data == NULL) 6622743Sahrens return (ra->err); 6632743Sahrens 6642743Sahrens if (dmu_object_info(os, drrw->drr_object, NULL) != 0) 6652743Sahrens return (EINVAL); 6662743Sahrens 6672743Sahrens tx = dmu_tx_create(os); 6682743Sahrens 6692743Sahrens dmu_tx_hold_write(tx, drrw->drr_object, 6702743Sahrens drrw->drr_offset, drrw->drr_length); 6712743Sahrens err = dmu_tx_assign(tx, TXG_WAIT); 6722743Sahrens if (err) { 6732743Sahrens dmu_tx_abort(tx); 6742743Sahrens return (err); 6752743Sahrens } 6762743Sahrens if (ra->byteswap) 6772743Sahrens dmu_ot[drrw->drr_type].ot_byteswap(data, drrw->drr_length); 6782743Sahrens dmu_write(os, drrw->drr_object, 6792743Sahrens drrw->drr_offset, drrw->drr_length, data, tx); 6802743Sahrens dmu_tx_commit(tx); 6812743Sahrens return (0); 6822743Sahrens } 6832743Sahrens 6842743Sahrens /* ARGSUSED */ 6852743Sahrens static int 6862743Sahrens restore_free(struct restorearg *ra, objset_t *os, 6872743Sahrens struct drr_free *drrf) 6882743Sahrens { 6892743Sahrens dmu_tx_t *tx; 6902743Sahrens int err; 6912743Sahrens 6922743Sahrens if (drrf->drr_length != -1ULL && 6932743Sahrens drrf->drr_offset + drrf->drr_length < drrf->drr_offset) 6942743Sahrens return (EINVAL); 6952743Sahrens 6962743Sahrens if (dmu_object_info(os, drrf->drr_object, NULL) != 0) 6972743Sahrens return (EINVAL); 6982743Sahrens 6992743Sahrens tx = dmu_tx_create(os); 7002743Sahrens 7012743Sahrens dmu_tx_hold_free(tx, drrf->drr_object, 7022743Sahrens drrf->drr_offset, drrf->drr_length); 7032743Sahrens err = dmu_tx_assign(tx, TXG_WAIT); 7042743Sahrens if (err) { 7052743Sahrens dmu_tx_abort(tx); 7062743Sahrens return (err); 7072743Sahrens } 7082743Sahrens err = dmu_free_range(os, drrf->drr_object, 7092743Sahrens drrf->drr_offset, drrf->drr_length, tx); 7102743Sahrens dmu_tx_commit(tx); 7112743Sahrens return (err); 7122743Sahrens } 7132743Sahrens 7142743Sahrens int 7152743Sahrens dmu_recvbackup(char *tosnap, struct drr_begin *drrb, uint64_t *sizep, 7162743Sahrens boolean_t force, vnode_t *vp, uint64_t voffset) 7172743Sahrens { 7182743Sahrens struct restorearg ra; 7192743Sahrens dmu_replay_record_t *drr; 7202743Sahrens char *cp; 7212743Sahrens objset_t *os = NULL; 7222743Sahrens zio_cksum_t pzc; 7232743Sahrens 7242743Sahrens bzero(&ra, sizeof (ra)); 7252743Sahrens ra.vp = vp; 7262743Sahrens ra.voff = voffset; 7272743Sahrens ra.bufsize = 1<<20; 7282743Sahrens ra.buf = kmem_alloc(ra.bufsize, KM_SLEEP); 7292743Sahrens 7302743Sahrens if (drrb->drr_magic == DMU_BACKUP_MAGIC) { 7312743Sahrens ra.byteswap = FALSE; 7322743Sahrens } else if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) { 7332743Sahrens ra.byteswap = TRUE; 7342743Sahrens } else { 7352743Sahrens ra.err = EINVAL; 7362743Sahrens goto out; 7372743Sahrens } 7382743Sahrens 7392743Sahrens /* 7402743Sahrens * NB: this assumes that struct drr_begin will be the largest in 7412743Sahrens * dmu_replay_record_t's drr_u, and thus we don't need to pad it 7422743Sahrens * with zeros to make it the same length as we wrote out. 7432743Sahrens */ 7442743Sahrens ((dmu_replay_record_t *)ra.buf)->drr_type = DRR_BEGIN; 7452743Sahrens ((dmu_replay_record_t *)ra.buf)->drr_pad = 0; 7462743Sahrens ((dmu_replay_record_t *)ra.buf)->drr_u.drr_begin = *drrb; 7472743Sahrens if (ra.byteswap) { 7482743Sahrens fletcher_4_incremental_byteswap(ra.buf, 7492743Sahrens sizeof (dmu_replay_record_t), &ra.zc); 7502743Sahrens } else { 7512743Sahrens fletcher_4_incremental_native(ra.buf, 7522743Sahrens sizeof (dmu_replay_record_t), &ra.zc); 7532743Sahrens } 7542743Sahrens (void) strcpy(drrb->drr_toname, tosnap); /* for the sync funcs */ 7552743Sahrens 7562743Sahrens if (ra.byteswap) { 7572743Sahrens drrb->drr_magic = BSWAP_64(drrb->drr_magic); 7582743Sahrens drrb->drr_version = BSWAP_64(drrb->drr_version); 7592743Sahrens drrb->drr_creation_time = BSWAP_64(drrb->drr_creation_time); 7602743Sahrens drrb->drr_type = BSWAP_32(drrb->drr_type); 7612743Sahrens drrb->drr_toguid = BSWAP_64(drrb->drr_toguid); 7622743Sahrens drrb->drr_fromguid = BSWAP_64(drrb->drr_fromguid); 7632743Sahrens } 7642743Sahrens 7652743Sahrens ASSERT3U(drrb->drr_magic, ==, DMU_BACKUP_MAGIC); 7662743Sahrens 7672743Sahrens if (drrb->drr_version != DMU_BACKUP_VERSION || 7682743Sahrens drrb->drr_type >= DMU_OST_NUMTYPES || 7692743Sahrens strchr(drrb->drr_toname, '@') == NULL) { 7702743Sahrens ra.err = EINVAL; 7712743Sahrens goto out; 7722743Sahrens } 7732743Sahrens 7742743Sahrens /* 7752743Sahrens * Process the begin in syncing context. 7762743Sahrens */ 7772743Sahrens if (drrb->drr_fromguid) { 7782743Sahrens /* incremental backup */ 7792743Sahrens dsl_dataset_t *ds = NULL; 7802743Sahrens 7812743Sahrens cp = strchr(tosnap, '@'); 7822743Sahrens *cp = '\0'; 7832743Sahrens ra.err = dsl_dataset_open(tosnap, DS_MODE_EXCLUSIVE, FTAG, &ds); 7842743Sahrens *cp = '@'; 7852743Sahrens if (ra.err) 7862743Sahrens goto out; 7872743Sahrens 7882743Sahrens /* 7892743Sahrens * Only do the rollback if the most recent snapshot 7902743Sahrens * matches the incremental source 7912743Sahrens */ 7922743Sahrens if (force) { 7932885Sahrens if (ds->ds_prev == NULL || 7942885Sahrens ds->ds_prev->ds_phys->ds_guid != 7952743Sahrens drrb->drr_fromguid) { 7962743Sahrens dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG); 7972743Sahrens return (ENODEV); 7982743Sahrens } 7992743Sahrens (void) dsl_dataset_rollback(ds); 8002743Sahrens } 8012743Sahrens ra.err = dsl_sync_task_do(ds->ds_dir->dd_pool, 8022743Sahrens replay_incremental_check, replay_incremental_sync, 8032743Sahrens ds, drrb, 1); 8042743Sahrens dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG); 8052743Sahrens } else { 8062743Sahrens /* full backup */ 8072743Sahrens dsl_dir_t *dd = NULL; 8082743Sahrens const char *tail; 8092743Sahrens 8102743Sahrens /* can't restore full backup into topmost fs, for now */ 8112743Sahrens if (strrchr(drrb->drr_toname, '/') == NULL) { 8122743Sahrens ra.err = EINVAL; 8132743Sahrens goto out; 8142743Sahrens } 8152743Sahrens 8162743Sahrens cp = strchr(tosnap, '@'); 8172743Sahrens *cp = '\0'; 8182743Sahrens ra.err = dsl_dir_open(tosnap, FTAG, &dd, &tail); 8192743Sahrens *cp = '@'; 8202743Sahrens if (ra.err) 8212743Sahrens goto out; 8222743Sahrens if (tail == NULL) { 8232743Sahrens ra.err = EEXIST; 8242743Sahrens goto out; 8252743Sahrens } 8262743Sahrens 8272743Sahrens ra.err = dsl_sync_task_do(dd->dd_pool, replay_full_check, 8282743Sahrens replay_full_sync, dd, drrb, 5); 8292743Sahrens dsl_dir_close(dd, FTAG); 8302743Sahrens } 8312743Sahrens if (ra.err) 8322743Sahrens goto out; 8332743Sahrens 8342743Sahrens /* 8352743Sahrens * Open the objset we are modifying. 8362743Sahrens */ 8372743Sahrens 8382743Sahrens cp = strchr(tosnap, '@'); 8392743Sahrens *cp = '\0'; 8402743Sahrens ra.err = dmu_objset_open(tosnap, DMU_OST_ANY, 8412743Sahrens DS_MODE_PRIMARY | DS_MODE_INCONSISTENT, &os); 8422743Sahrens *cp = '@'; 8432743Sahrens ASSERT3U(ra.err, ==, 0); 8442743Sahrens 8452743Sahrens /* 8462743Sahrens * Read records and process them. 8472743Sahrens */ 8482743Sahrens pzc = ra.zc; 8492743Sahrens while (ra.err == 0 && 8502743Sahrens NULL != (drr = restore_read(&ra, sizeof (*drr)))) { 8512743Sahrens if (issig(JUSTLOOKING) && issig(FORREAL)) { 8522743Sahrens ra.err = EINTR; 8532743Sahrens goto out; 8542743Sahrens } 8552743Sahrens 8562743Sahrens if (ra.byteswap) 8572743Sahrens backup_byteswap(drr); 8582743Sahrens 8592743Sahrens switch (drr->drr_type) { 8602743Sahrens case DRR_OBJECT: 8612743Sahrens { 8622743Sahrens /* 8632743Sahrens * We need to make a copy of the record header, 8642743Sahrens * because restore_{object,write} may need to 8652743Sahrens * restore_read(), which will invalidate drr. 8662743Sahrens */ 8672743Sahrens struct drr_object drro = drr->drr_u.drr_object; 8682743Sahrens ra.err = restore_object(&ra, os, &drro); 8692743Sahrens break; 8702743Sahrens } 8712743Sahrens case DRR_FREEOBJECTS: 8722743Sahrens { 8732743Sahrens struct drr_freeobjects drrfo = 8742743Sahrens drr->drr_u.drr_freeobjects; 8752743Sahrens ra.err = restore_freeobjects(&ra, os, &drrfo); 8762743Sahrens break; 8772743Sahrens } 8782743Sahrens case DRR_WRITE: 8792743Sahrens { 8802743Sahrens struct drr_write drrw = drr->drr_u.drr_write; 8812743Sahrens ra.err = restore_write(&ra, os, &drrw); 8822743Sahrens break; 8832743Sahrens } 8842743Sahrens case DRR_FREE: 8852743Sahrens { 8862743Sahrens struct drr_free drrf = drr->drr_u.drr_free; 8872743Sahrens ra.err = restore_free(&ra, os, &drrf); 8882743Sahrens break; 8892743Sahrens } 8902743Sahrens case DRR_END: 8912743Sahrens { 8922743Sahrens struct drr_end drre = drr->drr_u.drr_end; 8932743Sahrens /* 8942743Sahrens * We compare against the *previous* checksum 8952743Sahrens * value, because the stored checksum is of 8962743Sahrens * everything before the DRR_END record. 8972743Sahrens */ 8982743Sahrens if (drre.drr_checksum.zc_word[0] != 0 && 8992743Sahrens ((drre.drr_checksum.zc_word[0] - pzc.zc_word[0]) | 9002743Sahrens (drre.drr_checksum.zc_word[1] - pzc.zc_word[1]) | 9012743Sahrens (drre.drr_checksum.zc_word[2] - pzc.zc_word[2]) | 9022743Sahrens (drre.drr_checksum.zc_word[3] - pzc.zc_word[3]))) { 9032743Sahrens ra.err = ECKSUM; 9042743Sahrens goto out; 9052743Sahrens } 9062743Sahrens 9072743Sahrens ra.err = dsl_sync_task_do(dmu_objset_ds(os)-> 9082743Sahrens ds_dir->dd_pool, replay_end_check, replay_end_sync, 9092743Sahrens os, drrb, 3); 9102743Sahrens goto out; 9112743Sahrens } 9122743Sahrens default: 9132743Sahrens ra.err = EINVAL; 9142743Sahrens goto out; 9152743Sahrens } 9162743Sahrens pzc = ra.zc; 9172743Sahrens } 9182743Sahrens 9192743Sahrens out: 9202743Sahrens if (os) 9212743Sahrens dmu_objset_close(os); 9222743Sahrens 9232743Sahrens /* 9242743Sahrens * Make sure we don't rollback/destroy unless we actually 9252743Sahrens * processed the begin properly. 'os' will only be set if this 9262743Sahrens * is the case. 9272743Sahrens */ 9282743Sahrens if (ra.err && os && tosnap && strchr(tosnap, '@')) { 9292743Sahrens /* 9302743Sahrens * rollback or destroy what we created, so we don't 9312743Sahrens * leave it in the restoring state. 9322743Sahrens */ 9332743Sahrens dsl_dataset_t *ds; 9342743Sahrens int err; 9352743Sahrens 9362743Sahrens cp = strchr(tosnap, '@'); 9372743Sahrens *cp = '\0'; 9382743Sahrens err = dsl_dataset_open(tosnap, 9392743Sahrens DS_MODE_EXCLUSIVE | DS_MODE_INCONSISTENT, 9402743Sahrens FTAG, &ds); 9412743Sahrens if (err == 0) { 9422743Sahrens txg_wait_synced(ds->ds_dir->dd_pool, 0); 9432743Sahrens if (drrb->drr_fromguid) { 9442743Sahrens /* incremental: rollback to most recent snap */ 9452743Sahrens (void) dsl_dataset_rollback(ds); 9462743Sahrens dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG); 9472743Sahrens } else { 9482743Sahrens /* full: destroy whole fs */ 9492743Sahrens dsl_dataset_close(ds, DS_MODE_EXCLUSIVE, FTAG); 9502743Sahrens (void) dsl_dataset_destroy(tosnap); 9512743Sahrens } 9522743Sahrens } 9532743Sahrens *cp = '@'; 9542743Sahrens } 9552743Sahrens 9562743Sahrens kmem_free(ra.buf, ra.bufsize); 9572743Sahrens if (sizep) 9582743Sahrens *sizep = ra.voff; 9592743Sahrens return (ra.err); 9602743Sahrens } 961