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. 23249643Smm * Copyright (c) 2013 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 132247406Smmdiff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, 133219089Spjd const zbookmark_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)) 139249643Smm return (SET_ERROR(EINTR)); 140219089Spjd 141219089Spjd if (zb->zb_object != DMU_META_DNODE_OBJECT) 142219089Spjd return (0); 143219089Spjd 144263398Sdelphij 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; 155219089Spjd uint32_t aflags = ARC_WAIT; 156219089Spjd int blksz = BP_GET_LSIZE(bp); 157219089Spjd int i; 158219089Spjd 159247406Smm if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf, 160247406Smm ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, 161247406Smm &aflags, zb) != 0) 162249643Smm 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 } 172219089Spjd (void) arc_buf_remove_ref(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 182249643Smmdmu_diff(const char *tosnap_name, const char *fromsnap_name, 183249643Smm#ifdef illumos 184249643Smm struct vnode *vp, offset_t *offp) 185249643Smm#else 186249643Smm struct file *fp, offset_t *offp) 187249643Smm#endif 188219089Spjd{ 189219089Spjd struct diffarg da; 190249643Smm dsl_dataset_t *fromsnap; 191249643Smm dsl_dataset_t *tosnap; 192249643Smm dsl_pool_t *dp; 193249643Smm int error; 194249643Smm uint64_t fromtxg; 195219089Spjd 196249643Smm if (strchr(tosnap_name, '@') == NULL || 197249643Smm strchr(fromsnap_name, '@') == NULL) 198249643Smm return (SET_ERROR(EINVAL)); 199219089Spjd 200249643Smm error = dsl_pool_hold(tosnap_name, FTAG, &dp); 201249643Smm if (error != 0) 202249643Smm return (error); 203219089Spjd 204249643Smm error = dsl_dataset_hold(dp, tosnap_name, FTAG, &tosnap); 205249643Smm if (error != 0) { 206249643Smm dsl_pool_rele(dp, FTAG); 207249643Smm return (error); 208249643Smm } 209219089Spjd 210249643Smm error = dsl_dataset_hold(dp, fromsnap_name, FTAG, &fromsnap); 211249643Smm if (error != 0) { 212249643Smm dsl_dataset_rele(tosnap, FTAG); 213249643Smm dsl_pool_rele(dp, FTAG); 214249643Smm return (error); 215249643Smm } 216219089Spjd 217263410Sdelphij if (!dsl_dataset_is_before(tosnap, fromsnap, 0)) { 218249643Smm dsl_dataset_rele(fromsnap, FTAG); 219249643Smm dsl_dataset_rele(tosnap, FTAG); 220249643Smm dsl_pool_rele(dp, FTAG); 221249643Smm return (SET_ERROR(EXDEV)); 222249643Smm } 223219089Spjd 224249643Smm fromtxg = fromsnap->ds_phys->ds_creation_txg; 225249643Smm dsl_dataset_rele(fromsnap, FTAG); 226219089Spjd 227249643Smm dsl_dataset_long_hold(tosnap, FTAG); 228249643Smm 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 237249643Smm error = traverse_dataset(tosnap, fromtxg, 238219089Spjd TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA, diff_cb, &da); 239219089Spjd 240249643Smm if (error != 0) { 241249643Smm 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 247249643Smm dsl_dataset_long_rele(tosnap, FTAG); 248249643Smm dsl_dataset_rele(tosnap, FTAG); 249249643Smm 250219089Spjd return (da.da_err); 251219089Spjd} 252