1/* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21/* 22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright (c) 2012, 2014 by Delphix. All rights reserved. 24 */ 25 26#include <sys/dmu.h> 27#include <sys/dmu_impl.h> 28#include <sys/dmu_tx.h> 29#include <sys/dbuf.h> 30#include <sys/dnode.h> 31#include <sys/zfs_context.h> 32#include <sys/dmu_objset.h> 33#include <sys/dmu_traverse.h> 34#include <sys/dsl_dataset.h> 35#include <sys/dsl_dir.h> 36#include <sys/dsl_pool.h> 37#include <sys/dsl_synctask.h> 38#include <sys/zfs_ioctl.h> 39#include <sys/zap.h> 40#include <sys/zio_checksum.h> 41#include <sys/zfs_znode.h> 42 43struct diffarg { 44 struct file *da_fp; /* file to which we are reporting */ 45 offset_t *da_offp; 46 int da_err; /* error that stopped diff search */ 47 dmu_diff_record_t da_ddr; 48 kthread_t *da_td; 49}; 50 51static int 52write_bytes(struct diffarg *da) 53{ 54 struct uio auio; 55 struct iovec aiov; 56 57 aiov.iov_base = (caddr_t)&da->da_ddr; 58 aiov.iov_len = sizeof (da->da_ddr); 59 auio.uio_iov = &aiov; 60 auio.uio_iovcnt = 1; 61 auio.uio_resid = aiov.iov_len; 62 auio.uio_segflg = UIO_SYSSPACE; 63 auio.uio_rw = UIO_WRITE; 64 auio.uio_offset = (off_t)-1; 65 auio.uio_td = da->da_td; 66#ifdef _KERNEL 67 if (da->da_fp->f_type == DTYPE_VNODE) 68 bwillwrite(); 69 return (fo_write(da->da_fp, &auio, da->da_td->td_ucred, 0, da->da_td)); 70#else 71 fprintf(stderr, "%s: returning EOPNOTSUPP\n", __func__); 72 return (EOPNOTSUPP); 73#endif 74} 75 76static int 77write_record(struct diffarg *da) 78{ 79 80 if (da->da_ddr.ddr_type == DDR_NONE) { 81 da->da_err = 0; 82 return (0); 83 } 84 85 da->da_err = write_bytes(da); 86 *da->da_offp += sizeof (da->da_ddr); 87 return (da->da_err); 88} 89 90static int 91report_free_dnode_range(struct diffarg *da, uint64_t first, uint64_t last) 92{ 93 ASSERT(first <= last); 94 if (da->da_ddr.ddr_type != DDR_FREE || 95 first != da->da_ddr.ddr_last + 1) { 96 if (write_record(da) != 0) 97 return (da->da_err); 98 da->da_ddr.ddr_type = DDR_FREE; 99 da->da_ddr.ddr_first = first; 100 da->da_ddr.ddr_last = last; 101 return (0); 102 } 103 da->da_ddr.ddr_last = last; 104 return (0); 105} 106 107static int 108report_dnode(struct diffarg *da, uint64_t object, dnode_phys_t *dnp) 109{ 110 ASSERT(dnp != NULL); 111 if (dnp->dn_type == DMU_OT_NONE) 112 return (report_free_dnode_range(da, object, object)); 113 114 if (da->da_ddr.ddr_type != DDR_INUSE || 115 object != da->da_ddr.ddr_last + 1) { 116 if (write_record(da) != 0) 117 return (da->da_err); 118 da->da_ddr.ddr_type = DDR_INUSE; 119 da->da_ddr.ddr_first = da->da_ddr.ddr_last = object; 120 return (0); 121 } 122 da->da_ddr.ddr_last = object; 123 return (0); 124} 125 126#define DBP_SPAN(dnp, level) \ 127 (((uint64_t)dnp->dn_datablkszsec) << (SPA_MINBLOCKSHIFT + \ 128 (level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT))) 129 130/* ARGSUSED */ 131static int 132diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, 133 const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg) 134{ 135 struct diffarg *da = arg; 136 int err = 0; 137 138 if (issig(JUSTLOOKING) && issig(FORREAL)) 139 return (SET_ERROR(EINTR)); 140 141 if (bp == NULL || zb->zb_object != DMU_META_DNODE_OBJECT) 142 return (0); 143 144 if (BP_IS_HOLE(bp)) { 145 uint64_t span = DBP_SPAN(dnp, zb->zb_level); 146 uint64_t dnobj = (zb->zb_blkid * span) >> DNODE_SHIFT; 147 148 err = report_free_dnode_range(da, dnobj, 149 dnobj + (span >> DNODE_SHIFT) - 1); 150 if (err) 151 return (err); 152 } else if (zb->zb_level == 0) { 153 dnode_phys_t *blk; 154 arc_buf_t *abuf; 155 arc_flags_t aflags = ARC_FLAG_WAIT; 156 int blksz = BP_GET_LSIZE(bp); 157 int i; 158 159 if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf, 160 ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, 161 &aflags, zb) != 0) 162 return (SET_ERROR(EIO)); 163 164 blk = abuf->b_data; 165 for (i = 0; i < blksz >> DNODE_SHIFT; i++) { 166 uint64_t dnobj = (zb->zb_blkid << 167 (DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i; 168 err = report_dnode(da, dnobj, blk+i); 169 if (err) 170 break; 171 } 172 (void) arc_buf_remove_ref(abuf, &abuf); 173 if (err) 174 return (err); 175 /* Don't care about the data blocks */ 176 return (TRAVERSE_VISIT_NO_CHILDREN); 177 } 178 return (0); 179} 180 181int 182dmu_diff(const char *tosnap_name, const char *fromsnap_name, 183#ifdef illumos 184 struct vnode *vp, offset_t *offp) 185#else 186 struct file *fp, offset_t *offp) 187#endif 188{ 189 struct diffarg da; 190 dsl_dataset_t *fromsnap; 191 dsl_dataset_t *tosnap; 192 dsl_pool_t *dp; 193 int error; 194 uint64_t fromtxg; 195 196 if (strchr(tosnap_name, '@') == NULL || 197 strchr(fromsnap_name, '@') == NULL) 198 return (SET_ERROR(EINVAL)); 199 200 error = dsl_pool_hold(tosnap_name, FTAG, &dp); 201 if (error != 0) 202 return (error); 203 204 error = dsl_dataset_hold(dp, tosnap_name, FTAG, &tosnap); 205 if (error != 0) { 206 dsl_pool_rele(dp, FTAG); 207 return (error); 208 } 209 210 error = dsl_dataset_hold(dp, fromsnap_name, FTAG, &fromsnap); 211 if (error != 0) { 212 dsl_dataset_rele(tosnap, FTAG); 213 dsl_pool_rele(dp, FTAG); 214 return (error); 215 } 216 217 if (!dsl_dataset_is_before(tosnap, fromsnap, 0)) { 218 dsl_dataset_rele(fromsnap, FTAG); 219 dsl_dataset_rele(tosnap, FTAG); 220 dsl_pool_rele(dp, FTAG); 221 return (SET_ERROR(EXDEV)); 222 } 223 224 fromtxg = dsl_dataset_phys(fromsnap)->ds_creation_txg; 225 dsl_dataset_rele(fromsnap, FTAG); 226 227 dsl_dataset_long_hold(tosnap, FTAG); 228 dsl_pool_rele(dp, FTAG); 229 230 da.da_fp = fp; 231 da.da_offp = offp; 232 da.da_ddr.ddr_type = DDR_NONE; 233 da.da_ddr.ddr_first = da.da_ddr.ddr_last = 0; 234 da.da_err = 0; 235 da.da_td = curthread; 236 237 error = traverse_dataset(tosnap, fromtxg, 238 TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA, diff_cb, &da); 239 240 if (error != 0) { 241 da.da_err = error; 242 } else { 243 /* we set the da.da_err we return as side-effect */ 244 (void) write_record(&da); 245 } 246 247 dsl_dataset_long_rele(tosnap, FTAG); 248 dsl_dataset_rele(tosnap, FTAG); 249 250 return (da.da_err); 251} 252