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. 237802d7bfSMatthew Ahrens * Copyright (c) 2012, 2014 by Delphix. All rights reserved. 2499d5e173STim Haley */ 2599d5e173STim Haley 2699d5e173STim Haley #include <sys/dmu.h> 2799d5e173STim Haley #include <sys/dmu_impl.h> 2899d5e173STim Haley #include <sys/dmu_tx.h> 2999d5e173STim Haley #include <sys/dbuf.h> 3099d5e173STim Haley #include <sys/dnode.h> 3199d5e173STim Haley #include <sys/zfs_context.h> 3299d5e173STim Haley #include <sys/dmu_objset.h> 3399d5e173STim Haley #include <sys/dmu_traverse.h> 3499d5e173STim Haley #include <sys/dsl_dataset.h> 3599d5e173STim Haley #include <sys/dsl_dir.h> 3699d5e173STim Haley #include <sys/dsl_pool.h> 3799d5e173STim Haley #include <sys/dsl_synctask.h> 3899d5e173STim Haley #include <sys/zfs_ioctl.h> 3999d5e173STim Haley #include <sys/zap.h> 4099d5e173STim Haley #include <sys/zio_checksum.h> 4199d5e173STim Haley #include <sys/zfs_znode.h> 4299d5e173STim Haley 4399d5e173STim Haley struct diffarg { 4499d5e173STim Haley struct vnode *da_vp; /* file to which we are reporting */ 4599d5e173STim Haley offset_t *da_offp; 4699d5e173STim Haley int da_err; /* error that stopped diff search */ 4799d5e173STim Haley dmu_diff_record_t da_ddr; 4899d5e173STim Haley }; 4999d5e173STim Haley 5099d5e173STim Haley static int 5199d5e173STim Haley write_record(struct diffarg *da) 5299d5e173STim Haley { 5399d5e173STim Haley ssize_t resid; /* have to get resid to get detailed errno */ 5499d5e173STim Haley 5599d5e173STim Haley if (da->da_ddr.ddr_type == DDR_NONE) { 5699d5e173STim Haley da->da_err = 0; 5799d5e173STim Haley return (0); 5899d5e173STim Haley } 5999d5e173STim Haley 6099d5e173STim Haley da->da_err = vn_rdwr(UIO_WRITE, da->da_vp, (caddr_t)&da->da_ddr, 6199d5e173STim Haley sizeof (da->da_ddr), 0, UIO_SYSSPACE, FAPPEND, 6299d5e173STim Haley RLIM64_INFINITY, CRED(), &resid); 6399d5e173STim Haley *da->da_offp += sizeof (da->da_ddr); 6499d5e173STim Haley return (da->da_err); 6599d5e173STim Haley } 6699d5e173STim Haley 6799d5e173STim Haley static int 6899d5e173STim Haley report_free_dnode_range(struct diffarg *da, uint64_t first, uint64_t last) 6999d5e173STim Haley { 7099d5e173STim Haley ASSERT(first <= last); 7199d5e173STim Haley if (da->da_ddr.ddr_type != DDR_FREE || 7299d5e173STim Haley first != da->da_ddr.ddr_last + 1) { 7399d5e173STim Haley if (write_record(da) != 0) 7499d5e173STim Haley return (da->da_err); 7599d5e173STim Haley da->da_ddr.ddr_type = DDR_FREE; 7699d5e173STim Haley da->da_ddr.ddr_first = first; 7799d5e173STim Haley da->da_ddr.ddr_last = last; 7899d5e173STim Haley return (0); 7999d5e173STim Haley } 8099d5e173STim Haley da->da_ddr.ddr_last = last; 8199d5e173STim Haley return (0); 8299d5e173STim Haley } 8399d5e173STim Haley 8499d5e173STim Haley static int 8599d5e173STim Haley report_dnode(struct diffarg *da, uint64_t object, dnode_phys_t *dnp) 8699d5e173STim Haley { 8799d5e173STim Haley ASSERT(dnp != NULL); 8899d5e173STim Haley if (dnp->dn_type == DMU_OT_NONE) 8999d5e173STim Haley return (report_free_dnode_range(da, object, object)); 9099d5e173STim Haley 9199d5e173STim Haley if (da->da_ddr.ddr_type != DDR_INUSE || 9299d5e173STim Haley object != da->da_ddr.ddr_last + 1) { 9399d5e173STim Haley if (write_record(da) != 0) 9499d5e173STim Haley return (da->da_err); 9599d5e173STim Haley da->da_ddr.ddr_type = DDR_INUSE; 9699d5e173STim Haley da->da_ddr.ddr_first = da->da_ddr.ddr_last = object; 9799d5e173STim Haley return (0); 9899d5e173STim Haley } 9999d5e173STim Haley da->da_ddr.ddr_last = object; 10099d5e173STim Haley return (0); 10199d5e173STim Haley } 10299d5e173STim Haley 10399d5e173STim Haley #define DBP_SPAN(dnp, level) \ 10499d5e173STim Haley (((uint64_t)dnp->dn_datablkszsec) << (SPA_MINBLOCKSHIFT + \ 10599d5e173STim Haley (level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT))) 10699d5e173STim Haley 10799d5e173STim Haley /* ARGSUSED */ 10899d5e173STim Haley static int 1091b912ec7SGeorge Wilson diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, 1107802d7bfSMatthew Ahrens const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg) 11199d5e173STim Haley { 11299d5e173STim Haley struct diffarg *da = arg; 11399d5e173STim Haley int err = 0; 11499d5e173STim Haley 11599d5e173STim Haley if (issig(JUSTLOOKING) && issig(FORREAL)) 116be6fd75aSMatthew Ahrens return (SET_ERROR(EINTR)); 11799d5e173STim Haley 118*a2cdcdd2SPaul Dagnelie if (bp == NULL || zb->zb_object != DMU_META_DNODE_OBJECT) 11999d5e173STim Haley return (0); 12099d5e173STim Haley 12143466aaeSMax Grossman if (BP_IS_HOLE(bp)) { 12299d5e173STim Haley uint64_t span = DBP_SPAN(dnp, zb->zb_level); 12399d5e173STim Haley uint64_t dnobj = (zb->zb_blkid * span) >> DNODE_SHIFT; 12499d5e173STim Haley 12599d5e173STim Haley err = report_free_dnode_range(da, dnobj, 12699d5e173STim Haley dnobj + (span >> DNODE_SHIFT) - 1); 12799d5e173STim Haley if (err) 12899d5e173STim Haley return (err); 12999d5e173STim Haley } else if (zb->zb_level == 0) { 13099d5e173STim Haley dnode_phys_t *blk; 13199d5e173STim Haley arc_buf_t *abuf; 1327adb730bSGeorge Wilson arc_flags_t aflags = ARC_FLAG_WAIT; 13399d5e173STim Haley int blksz = BP_GET_LSIZE(bp); 13499d5e173STim Haley int i; 13599d5e173STim Haley 1361b912ec7SGeorge Wilson if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf, 1371b912ec7SGeorge Wilson ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, 1381b912ec7SGeorge Wilson &aflags, zb) != 0) 139be6fd75aSMatthew Ahrens return (SET_ERROR(EIO)); 14099d5e173STim Haley 14199d5e173STim Haley blk = abuf->b_data; 14299d5e173STim Haley for (i = 0; i < blksz >> DNODE_SHIFT; i++) { 14399d5e173STim Haley uint64_t dnobj = (zb->zb_blkid << 14499d5e173STim Haley (DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i; 14599d5e173STim Haley err = report_dnode(da, dnobj, blk+i); 14699d5e173STim Haley if (err) 14799d5e173STim Haley break; 14899d5e173STim Haley } 14999d5e173STim Haley (void) arc_buf_remove_ref(abuf, &abuf); 15099d5e173STim Haley if (err) 15199d5e173STim Haley return (err); 15299d5e173STim Haley /* Don't care about the data blocks */ 15399d5e173STim Haley return (TRAVERSE_VISIT_NO_CHILDREN); 15499d5e173STim Haley } 15599d5e173STim Haley return (0); 15699d5e173STim Haley } 15799d5e173STim Haley 15899d5e173STim Haley int 1593b2aab18SMatthew Ahrens dmu_diff(const char *tosnap_name, const char *fromsnap_name, 1603b2aab18SMatthew Ahrens struct vnode *vp, offset_t *offp) 16199d5e173STim Haley { 16299d5e173STim Haley struct diffarg da; 1633b2aab18SMatthew Ahrens dsl_dataset_t *fromsnap; 1643b2aab18SMatthew Ahrens dsl_dataset_t *tosnap; 1653b2aab18SMatthew Ahrens dsl_pool_t *dp; 1663b2aab18SMatthew Ahrens int error; 1673b2aab18SMatthew Ahrens uint64_t fromtxg; 16899d5e173STim Haley 1693b2aab18SMatthew Ahrens if (strchr(tosnap_name, '@') == NULL || 1703b2aab18SMatthew Ahrens strchr(fromsnap_name, '@') == NULL) 171be6fd75aSMatthew Ahrens return (SET_ERROR(EINVAL)); 17299d5e173STim Haley 1733b2aab18SMatthew Ahrens error = dsl_pool_hold(tosnap_name, FTAG, &dp); 1743b2aab18SMatthew Ahrens if (error != 0) 1753b2aab18SMatthew Ahrens return (error); 17699d5e173STim Haley 1773b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, tosnap_name, FTAG, &tosnap); 1783b2aab18SMatthew Ahrens if (error != 0) { 1793b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 1803b2aab18SMatthew Ahrens return (error); 1813b2aab18SMatthew Ahrens } 18299d5e173STim Haley 1833b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, fromsnap_name, FTAG, &fromsnap); 1843b2aab18SMatthew Ahrens if (error != 0) { 1853b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 1863b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 1873b2aab18SMatthew Ahrens return (error); 1883b2aab18SMatthew Ahrens } 18999d5e173STim Haley 19078f17100SMatthew Ahrens if (!dsl_dataset_is_before(tosnap, fromsnap, 0)) { 1913b2aab18SMatthew Ahrens dsl_dataset_rele(fromsnap, FTAG); 1923b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 1933b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 194be6fd75aSMatthew Ahrens return (SET_ERROR(EXDEV)); 19599d5e173STim Haley } 19699d5e173STim Haley 197c1379625SJustin T. Gibbs fromtxg = dsl_dataset_phys(fromsnap)->ds_creation_txg; 1983b2aab18SMatthew Ahrens dsl_dataset_rele(fromsnap, FTAG); 19999d5e173STim Haley 2003b2aab18SMatthew Ahrens dsl_dataset_long_hold(tosnap, FTAG); 2013b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 20299d5e173STim Haley 20399d5e173STim Haley da.da_vp = vp; 20499d5e173STim Haley da.da_offp = offp; 20599d5e173STim Haley da.da_ddr.ddr_type = DDR_NONE; 20699d5e173STim Haley da.da_ddr.ddr_first = da.da_ddr.ddr_last = 0; 20799d5e173STim Haley da.da_err = 0; 20899d5e173STim Haley 2093b2aab18SMatthew Ahrens error = traverse_dataset(tosnap, fromtxg, 21099d5e173STim Haley TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA, diff_cb, &da); 21199d5e173STim Haley 2123b2aab18SMatthew Ahrens if (error != 0) { 2133b2aab18SMatthew Ahrens da.da_err = error; 21499d5e173STim Haley } else { 21599d5e173STim Haley /* we set the da.da_err we return as side-effect */ 21699d5e173STim Haley (void) write_record(&da); 21799d5e173STim Haley } 21899d5e173STim Haley 2193b2aab18SMatthew Ahrens dsl_dataset_long_rele(tosnap, FTAG); 2203b2aab18SMatthew Ahrens dsl_dataset_rele(tosnap, FTAG); 2213b2aab18SMatthew Ahrens 22299d5e173STim Haley return (da.da_err); 22399d5e173STim Haley } 224