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