1219089Spjd/* 2219089Spjd * CDDL HEADER START 3219089Spjd * 4219089Spjd * The contents of this file are subject to the terms of the 5219089Spjd * Common Development and Distribution License (the "License"). 6219089Spjd * You may not use this file except in compliance with the License. 7219089Spjd * 8219089Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9219089Spjd * or http://www.opensolaris.org/os/licensing. 10219089Spjd * See the License for the specific language governing permissions 11219089Spjd * and limitations under the License. 12219089Spjd * 13219089Spjd * When distributing Covered Code, include this CDDL HEADER in each 14219089Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15219089Spjd * If applicable, add the following below this CDDL HEADER, with the 16219089Spjd * fields enclosed by brackets "[]" replaced with your own identifying 17219089Spjd * information: Portions Copyright [yyyy] [name of copyright owner] 18219089Spjd * 19219089Spjd * CDDL HEADER END 20219089Spjd */ 21219089Spjd/* 22219089Spjd * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 23307266Smav * Copyright (c) 2012, 2015 by Delphix. All rights reserved. 24219089Spjd */ 25219089Spjd 26219089Spjd#include <sys/dmu.h> 27219089Spjd#include <sys/dmu_impl.h> 28219089Spjd#include <sys/dmu_tx.h> 29219089Spjd#include <sys/dbuf.h> 30219089Spjd#include <sys/dnode.h> 31219089Spjd#include <sys/zfs_context.h> 32219089Spjd#include <sys/dmu_objset.h> 33219089Spjd#include <sys/dmu_traverse.h> 34219089Spjd#include <sys/dsl_dataset.h> 35219089Spjd#include <sys/dsl_dir.h> 36219089Spjd#include <sys/dsl_pool.h> 37219089Spjd#include <sys/dsl_synctask.h> 38219089Spjd#include <sys/zfs_ioctl.h> 39219089Spjd#include <sys/zap.h> 40219089Spjd#include <sys/zio_checksum.h> 41219089Spjd#include <sys/zfs_znode.h> 42219089Spjd 43219089Spjdstruct diffarg { 44219089Spjd struct file *da_fp; /* file to which we are reporting */ 45219089Spjd offset_t *da_offp; 46219089Spjd int da_err; /* error that stopped diff search */ 47219089Spjd dmu_diff_record_t da_ddr; 48219089Spjd kthread_t *da_td; 49219089Spjd}; 50219089Spjd 51219089Spjdstatic int 52219089Spjdwrite_bytes(struct diffarg *da) 53219089Spjd{ 54219089Spjd struct uio auio; 55219089Spjd struct iovec aiov; 56219089Spjd 57219089Spjd aiov.iov_base = (caddr_t)&da->da_ddr; 58219089Spjd aiov.iov_len = sizeof (da->da_ddr); 59219089Spjd auio.uio_iov = &aiov; 60219089Spjd auio.uio_iovcnt = 1; 61219089Spjd auio.uio_resid = aiov.iov_len; 62219089Spjd auio.uio_segflg = UIO_SYSSPACE; 63219089Spjd auio.uio_rw = UIO_WRITE; 64219089Spjd auio.uio_offset = (off_t)-1; 65219089Spjd auio.uio_td = da->da_td; 66219089Spjd#ifdef _KERNEL 67219089Spjd if (da->da_fp->f_type == DTYPE_VNODE) 68219089Spjd bwillwrite(); 69219089Spjd return (fo_write(da->da_fp, &auio, da->da_td->td_ucred, 0, da->da_td)); 70219089Spjd#else 71219089Spjd fprintf(stderr, "%s: returning EOPNOTSUPP\n", __func__); 72219089Spjd return (EOPNOTSUPP); 73219089Spjd#endif 74219089Spjd} 75219089Spjd 76219089Spjdstatic int 77219089Spjdwrite_record(struct diffarg *da) 78219089Spjd{ 79219089Spjd 80219089Spjd if (da->da_ddr.ddr_type == DDR_NONE) { 81219089Spjd da->da_err = 0; 82219089Spjd return (0); 83219089Spjd } 84219089Spjd 85219089Spjd da->da_err = write_bytes(da); 86219089Spjd *da->da_offp += sizeof (da->da_ddr); 87219089Spjd return (da->da_err); 88219089Spjd} 89219089Spjd 90219089Spjdstatic int 91219089Spjdreport_free_dnode_range(struct diffarg *da, uint64_t first, uint64_t last) 92219089Spjd{ 93219089Spjd ASSERT(first <= last); 94219089Spjd if (da->da_ddr.ddr_type != DDR_FREE || 95219089Spjd first != da->da_ddr.ddr_last + 1) { 96219089Spjd if (write_record(da) != 0) 97219089Spjd return (da->da_err); 98219089Spjd da->da_ddr.ddr_type = DDR_FREE; 99219089Spjd da->da_ddr.ddr_first = first; 100219089Spjd da->da_ddr.ddr_last = last; 101219089Spjd return (0); 102219089Spjd } 103219089Spjd da->da_ddr.ddr_last = last; 104219089Spjd return (0); 105219089Spjd} 106219089Spjd 107219089Spjdstatic int 108219089Spjdreport_dnode(struct diffarg *da, uint64_t object, dnode_phys_t *dnp) 109219089Spjd{ 110219089Spjd ASSERT(dnp != NULL); 111219089Spjd if (dnp->dn_type == DMU_OT_NONE) 112219089Spjd return (report_free_dnode_range(da, object, object)); 113219089Spjd 114219089Spjd if (da->da_ddr.ddr_type != DDR_INUSE || 115219089Spjd object != da->da_ddr.ddr_last + 1) { 116219089Spjd if (write_record(da) != 0) 117219089Spjd return (da->da_err); 118219089Spjd da->da_ddr.ddr_type = DDR_INUSE; 119219089Spjd da->da_ddr.ddr_first = da->da_ddr.ddr_last = object; 120219089Spjd return (0); 121219089Spjd } 122219089Spjd da->da_ddr.ddr_last = object; 123219089Spjd return (0); 124219089Spjd} 125219089Spjd 126219089Spjd#define DBP_SPAN(dnp, level) \ 127219089Spjd (((uint64_t)dnp->dn_datablkszsec) << (SPA_MINBLOCKSHIFT + \ 128219089Spjd (level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT))) 129219089Spjd 130219089Spjd/* ARGSUSED */ 131219089Spjdstatic int 132246666Smmdiff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, 133268657Sdelphij const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg) 134219089Spjd{ 135219089Spjd struct diffarg *da = arg; 136219089Spjd int err = 0; 137219089Spjd 138219089Spjd if (issig(JUSTLOOKING) && issig(FORREAL)) 139249195Smm return (SET_ERROR(EINTR)); 140219089Spjd 141288571Smav if (bp == NULL || zb->zb_object != DMU_META_DNODE_OBJECT) 142219089Spjd return (0); 143219089Spjd 144263397Sdelphij if (BP_IS_HOLE(bp)) { 145219089Spjd uint64_t span = DBP_SPAN(dnp, zb->zb_level); 146219089Spjd uint64_t dnobj = (zb->zb_blkid * span) >> DNODE_SHIFT; 147219089Spjd 148219089Spjd err = report_free_dnode_range(da, dnobj, 149219089Spjd dnobj + (span >> DNODE_SHIFT) - 1); 150219089Spjd if (err) 151219089Spjd return (err); 152219089Spjd } else if (zb->zb_level == 0) { 153219089Spjd dnode_phys_t *blk; 154219089Spjd arc_buf_t *abuf; 155277586Sdelphij arc_flags_t aflags = ARC_FLAG_WAIT; 156219089Spjd int blksz = BP_GET_LSIZE(bp); 157219089Spjd int i; 158219089Spjd 159246666Smm if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf, 160246666Smm ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, 161246666Smm &aflags, zb) != 0) 162249195Smm return (SET_ERROR(EIO)); 163219089Spjd 164219089Spjd blk = abuf->b_data; 165219089Spjd for (i = 0; i < blksz >> DNODE_SHIFT; i++) { 166219089Spjd uint64_t dnobj = (zb->zb_blkid << 167219089Spjd (DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i; 168219089Spjd err = report_dnode(da, dnobj, blk+i); 169219089Spjd if (err) 170219089Spjd break; 171219089Spjd } 172307266Smav arc_buf_destroy(abuf, &abuf); 173219089Spjd if (err) 174219089Spjd return (err); 175219089Spjd /* Don't care about the data blocks */ 176219089Spjd return (TRAVERSE_VISIT_NO_CHILDREN); 177219089Spjd } 178219089Spjd return (0); 179219089Spjd} 180219089Spjd 181219089Spjdint 182248571Smmdmu_diff(const char *tosnap_name, const char *fromsnap_name, 183248571Smm#ifdef illumos 184248571Smm struct vnode *vp, offset_t *offp) 185248571Smm#else 186248571Smm struct file *fp, offset_t *offp) 187248571Smm#endif 188219089Spjd{ 189219089Spjd struct diffarg da; 190248571Smm dsl_dataset_t *fromsnap; 191248571Smm dsl_dataset_t *tosnap; 192248571Smm dsl_pool_t *dp; 193248571Smm int error; 194248571Smm uint64_t fromtxg; 195219089Spjd 196248571Smm if (strchr(tosnap_name, '@') == NULL || 197248571Smm strchr(fromsnap_name, '@') == NULL) 198249195Smm return (SET_ERROR(EINVAL)); 199219089Spjd 200248571Smm error = dsl_pool_hold(tosnap_name, FTAG, &dp); 201248571Smm if (error != 0) 202248571Smm return (error); 203219089Spjd 204248571Smm error = dsl_dataset_hold(dp, tosnap_name, FTAG, &tosnap); 205248571Smm if (error != 0) { 206248571Smm dsl_pool_rele(dp, FTAG); 207248571Smm return (error); 208248571Smm } 209219089Spjd 210248571Smm error = dsl_dataset_hold(dp, fromsnap_name, FTAG, &fromsnap); 211248571Smm if (error != 0) { 212248571Smm dsl_dataset_rele(tosnap, FTAG); 213248571Smm dsl_pool_rele(dp, FTAG); 214248571Smm return (error); 215248571Smm } 216219089Spjd 217263407Sdelphij if (!dsl_dataset_is_before(tosnap, fromsnap, 0)) { 218248571Smm dsl_dataset_rele(fromsnap, FTAG); 219248571Smm dsl_dataset_rele(tosnap, FTAG); 220248571Smm dsl_pool_rele(dp, FTAG); 221249195Smm return (SET_ERROR(EXDEV)); 222248571Smm } 223219089Spjd 224277585Sdelphij fromtxg = dsl_dataset_phys(fromsnap)->ds_creation_txg; 225248571Smm dsl_dataset_rele(fromsnap, FTAG); 226219089Spjd 227248571Smm dsl_dataset_long_hold(tosnap, FTAG); 228248571Smm dsl_pool_rele(dp, FTAG); 229219089Spjd 230219089Spjd da.da_fp = fp; 231219089Spjd da.da_offp = offp; 232219089Spjd da.da_ddr.ddr_type = DDR_NONE; 233219089Spjd da.da_ddr.ddr_first = da.da_ddr.ddr_last = 0; 234219089Spjd da.da_err = 0; 235219089Spjd da.da_td = curthread; 236219089Spjd 237248571Smm error = traverse_dataset(tosnap, fromtxg, 238219089Spjd TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA, diff_cb, &da); 239219089Spjd 240248571Smm if (error != 0) { 241248571Smm da.da_err = error; 242219089Spjd } else { 243219089Spjd /* we set the da.da_err we return as side-effect */ 244219089Spjd (void) write_record(&da); 245219089Spjd } 246219089Spjd 247248571Smm dsl_dataset_long_rele(tosnap, FTAG); 248248571Smm dsl_dataset_rele(tosnap, FTAG); 249248571Smm 250219089Spjd return (da.da_err); 251219089Spjd} 252