199d5e173STim Haley /* 299d5e173STim Haley * CDDL HEADER START 399d5e173STim Haley * 499d5e173STim Haley * The contents of this file are subject to the terms of the 599d5e173STim Haley * Common Development and Distribution License (the "License"). 699d5e173STim Haley * You may not use this file except in compliance with the License. 799d5e173STim Haley * 899d5e173STim Haley * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 999d5e173STim Haley * or http://www.opensolaris.org/os/licensing. 1099d5e173STim Haley * See the License for the specific language governing permissions 1199d5e173STim Haley * and limitations under the License. 1299d5e173STim Haley * 1399d5e173STim Haley * When distributing Covered Code, include this CDDL HEADER in each 1499d5e173STim Haley * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1599d5e173STim Haley * If applicable, add the following below this CDDL HEADER, with the 1699d5e173STim Haley * fields enclosed by brackets "[]" replaced with your own identifying 1799d5e173STim Haley * information: Portions Copyright [yyyy] [name of copyright owner] 1899d5e173STim Haley * 1999d5e173STim Haley * CDDL HEADER END 2099d5e173STim Haley */ 2199d5e173STim Haley /* 2299d5e173STim Haley * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 2399d5e173STim Haley */ 2499d5e173STim Haley 2599d5e173STim Haley #include <sys/dmu.h> 2699d5e173STim Haley #include <sys/dmu_impl.h> 2799d5e173STim Haley #include <sys/dmu_tx.h> 2899d5e173STim Haley #include <sys/dbuf.h> 2999d5e173STim Haley #include <sys/dnode.h> 3099d5e173STim Haley #include <sys/zfs_context.h> 3199d5e173STim Haley #include <sys/dmu_objset.h> 3299d5e173STim Haley #include <sys/dmu_traverse.h> 3399d5e173STim Haley #include <sys/dsl_dataset.h> 3499d5e173STim Haley #include <sys/dsl_dir.h> 3599d5e173STim Haley #include <sys/dsl_pool.h> 3699d5e173STim Haley #include <sys/dsl_synctask.h> 3799d5e173STim Haley #include <sys/zfs_ioctl.h> 3899d5e173STim Haley #include <sys/zap.h> 3999d5e173STim Haley #include <sys/zio_checksum.h> 4099d5e173STim Haley #include <sys/zfs_znode.h> 4199d5e173STim Haley 4299d5e173STim Haley struct diffarg { 4399d5e173STim Haley struct vnode *da_vp; /* file to which we are reporting */ 4499d5e173STim Haley offset_t *da_offp; 4599d5e173STim Haley int da_err; /* error that stopped diff search */ 4699d5e173STim Haley dmu_diff_record_t da_ddr; 4799d5e173STim Haley }; 4899d5e173STim Haley 4999d5e173STim Haley static int 5099d5e173STim Haley write_record(struct diffarg *da) 5199d5e173STim Haley { 5299d5e173STim Haley ssize_t resid; /* have to get resid to get detailed errno */ 5399d5e173STim Haley 5499d5e173STim Haley if (da->da_ddr.ddr_type == DDR_NONE) { 5599d5e173STim Haley da->da_err = 0; 5699d5e173STim Haley return (0); 5799d5e173STim Haley } 5899d5e173STim Haley 5999d5e173STim Haley da->da_err = vn_rdwr(UIO_WRITE, da->da_vp, (caddr_t)&da->da_ddr, 6099d5e173STim Haley sizeof (da->da_ddr), 0, UIO_SYSSPACE, FAPPEND, 6199d5e173STim Haley RLIM64_INFINITY, CRED(), &resid); 6299d5e173STim Haley *da->da_offp += sizeof (da->da_ddr); 6399d5e173STim Haley return (da->da_err); 6499d5e173STim Haley } 6599d5e173STim Haley 6699d5e173STim Haley static int 6799d5e173STim Haley report_free_dnode_range(struct diffarg *da, uint64_t first, uint64_t last) 6899d5e173STim Haley { 6999d5e173STim Haley ASSERT(first <= last); 7099d5e173STim Haley if (da->da_ddr.ddr_type != DDR_FREE || 7199d5e173STim Haley first != da->da_ddr.ddr_last + 1) { 7299d5e173STim Haley if (write_record(da) != 0) 7399d5e173STim Haley return (da->da_err); 7499d5e173STim Haley da->da_ddr.ddr_type = DDR_FREE; 7599d5e173STim Haley da->da_ddr.ddr_first = first; 7699d5e173STim Haley da->da_ddr.ddr_last = last; 7799d5e173STim Haley return (0); 7899d5e173STim Haley } 7999d5e173STim Haley da->da_ddr.ddr_last = last; 8099d5e173STim Haley return (0); 8199d5e173STim Haley } 8299d5e173STim Haley 8399d5e173STim Haley static int 8499d5e173STim Haley report_dnode(struct diffarg *da, uint64_t object, dnode_phys_t *dnp) 8599d5e173STim Haley { 8699d5e173STim Haley ASSERT(dnp != NULL); 8799d5e173STim Haley if (dnp->dn_type == DMU_OT_NONE) 8899d5e173STim Haley return (report_free_dnode_range(da, object, object)); 8999d5e173STim Haley 9099d5e173STim Haley if (da->da_ddr.ddr_type != DDR_INUSE || 9199d5e173STim Haley object != da->da_ddr.ddr_last + 1) { 9299d5e173STim Haley if (write_record(da) != 0) 9399d5e173STim Haley return (da->da_err); 9499d5e173STim Haley da->da_ddr.ddr_type = DDR_INUSE; 9599d5e173STim Haley da->da_ddr.ddr_first = da->da_ddr.ddr_last = object; 9699d5e173STim Haley return (0); 9799d5e173STim Haley } 9899d5e173STim Haley da->da_ddr.ddr_last = object; 9999d5e173STim Haley return (0); 10099d5e173STim Haley } 10199d5e173STim Haley 10299d5e173STim Haley #define DBP_SPAN(dnp, level) \ 10399d5e173STim Haley (((uint64_t)dnp->dn_datablkszsec) << (SPA_MINBLOCKSHIFT + \ 10499d5e173STim Haley (level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT))) 10599d5e173STim Haley 10699d5e173STim Haley /* ARGSUSED */ 10799d5e173STim Haley static int 108*1b912ec7SGeorge Wilson diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, 10999d5e173STim Haley const zbookmark_t *zb, const dnode_phys_t *dnp, void *arg) 11099d5e173STim Haley { 11199d5e173STim Haley struct diffarg *da = arg; 11299d5e173STim Haley int err = 0; 11399d5e173STim Haley 11499d5e173STim Haley if (issig(JUSTLOOKING) && issig(FORREAL)) 11599d5e173STim Haley return (EINTR); 11699d5e173STim Haley 11799d5e173STim Haley if (zb->zb_object != DMU_META_DNODE_OBJECT) 11899d5e173STim Haley return (0); 11999d5e173STim Haley 12099d5e173STim Haley if (bp == NULL) { 12199d5e173STim Haley uint64_t span = DBP_SPAN(dnp, zb->zb_level); 12299d5e173STim Haley uint64_t dnobj = (zb->zb_blkid * span) >> DNODE_SHIFT; 12399d5e173STim Haley 12499d5e173STim Haley err = report_free_dnode_range(da, dnobj, 12599d5e173STim Haley dnobj + (span >> DNODE_SHIFT) - 1); 12699d5e173STim Haley if (err) 12799d5e173STim Haley return (err); 12899d5e173STim Haley } else if (zb->zb_level == 0) { 12999d5e173STim Haley dnode_phys_t *blk; 13099d5e173STim Haley arc_buf_t *abuf; 13199d5e173STim Haley uint32_t aflags = ARC_WAIT; 13299d5e173STim Haley int blksz = BP_GET_LSIZE(bp); 13399d5e173STim Haley int i; 13499d5e173STim Haley 135*1b912ec7SGeorge Wilson if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf, 136*1b912ec7SGeorge Wilson ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, 137*1b912ec7SGeorge Wilson &aflags, zb) != 0) 13899d5e173STim Haley return (EIO); 13999d5e173STim Haley 14099d5e173STim Haley blk = abuf->b_data; 14199d5e173STim Haley for (i = 0; i < blksz >> DNODE_SHIFT; i++) { 14299d5e173STim Haley uint64_t dnobj = (zb->zb_blkid << 14399d5e173STim Haley (DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i; 14499d5e173STim Haley err = report_dnode(da, dnobj, blk+i); 14599d5e173STim Haley if (err) 14699d5e173STim Haley break; 14799d5e173STim Haley } 14899d5e173STim Haley (void) arc_buf_remove_ref(abuf, &abuf); 14999d5e173STim Haley if (err) 15099d5e173STim Haley return (err); 15199d5e173STim Haley /* Don't care about the data blocks */ 15299d5e173STim Haley return (TRAVERSE_VISIT_NO_CHILDREN); 15399d5e173STim Haley } 15499d5e173STim Haley return (0); 15599d5e173STim Haley } 15699d5e173STim Haley 15799d5e173STim Haley int 15899d5e173STim Haley dmu_diff(objset_t *tosnap, objset_t *fromsnap, struct vnode *vp, offset_t *offp) 15999d5e173STim Haley { 16099d5e173STim Haley struct diffarg da; 16199d5e173STim Haley dsl_dataset_t *ds = tosnap->os_dsl_dataset; 16299d5e173STim Haley dsl_dataset_t *fromds = fromsnap->os_dsl_dataset; 16399d5e173STim Haley dsl_dataset_t *findds; 16499d5e173STim Haley dsl_dataset_t *relds; 16599d5e173STim Haley int err = 0; 16699d5e173STim Haley 16799d5e173STim Haley /* make certain we are looking at snapshots */ 16899d5e173STim Haley if (!dsl_dataset_is_snapshot(ds) || !dsl_dataset_is_snapshot(fromds)) 16999d5e173STim Haley return (EINVAL); 17099d5e173STim Haley 17199d5e173STim Haley /* fromsnap must be earlier and from the same lineage as tosnap */ 17299d5e173STim Haley if (fromds->ds_phys->ds_creation_txg >= ds->ds_phys->ds_creation_txg) 17399d5e173STim Haley return (EXDEV); 17499d5e173STim Haley 17599d5e173STim Haley relds = NULL; 17699d5e173STim Haley findds = ds; 17799d5e173STim Haley 17899d5e173STim Haley while (fromds->ds_dir != findds->ds_dir) { 17999d5e173STim Haley dsl_pool_t *dp = ds->ds_dir->dd_pool; 18099d5e173STim Haley 18199d5e173STim Haley if (!dsl_dir_is_clone(findds->ds_dir)) { 18299d5e173STim Haley if (relds) 18399d5e173STim Haley dsl_dataset_rele(relds, FTAG); 18499d5e173STim Haley return (EXDEV); 18599d5e173STim Haley } 18699d5e173STim Haley 18799d5e173STim Haley rw_enter(&dp->dp_config_rwlock, RW_READER); 18899d5e173STim Haley err = dsl_dataset_hold_obj(dp, 18999d5e173STim Haley findds->ds_dir->dd_phys->dd_origin_obj, FTAG, &findds); 19099d5e173STim Haley rw_exit(&dp->dp_config_rwlock); 19199d5e173STim Haley 19299d5e173STim Haley if (relds) 19399d5e173STim Haley dsl_dataset_rele(relds, FTAG); 19499d5e173STim Haley 19599d5e173STim Haley if (err) 19699d5e173STim Haley return (EXDEV); 19799d5e173STim Haley 19899d5e173STim Haley relds = findds; 19999d5e173STim Haley } 20099d5e173STim Haley 20199d5e173STim Haley if (relds) 20299d5e173STim Haley dsl_dataset_rele(relds, FTAG); 20399d5e173STim Haley 20499d5e173STim Haley da.da_vp = vp; 20599d5e173STim Haley da.da_offp = offp; 20699d5e173STim Haley da.da_ddr.ddr_type = DDR_NONE; 20799d5e173STim Haley da.da_ddr.ddr_first = da.da_ddr.ddr_last = 0; 20899d5e173STim Haley da.da_err = 0; 20999d5e173STim Haley 21099d5e173STim Haley err = traverse_dataset(ds, fromds->ds_phys->ds_creation_txg, 21199d5e173STim Haley TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA, diff_cb, &da); 21299d5e173STim Haley 21399d5e173STim Haley if (err) { 21499d5e173STim Haley da.da_err = err; 21599d5e173STim Haley } else { 21699d5e173STim Haley /* we set the da.da_err we return as side-effect */ 21799d5e173STim Haley (void) write_record(&da); 21899d5e173STim Haley } 21999d5e173STim Haley 22099d5e173STim Haley return (da.da_err); 22199d5e173STim Haley } 222