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) 2013 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 43 struct diffarg { 44 struct vnode *da_vp; /* 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 }; 49 50 static int 51 write_record(struct diffarg *da) 52 { 53 ssize_t resid; /* have to get resid to get detailed errno */ 54 55 if (da->da_ddr.ddr_type == DDR_NONE) { 56 da->da_err = 0; 57 return (0); 58 } 59 60 da->da_err = vn_rdwr(UIO_WRITE, da->da_vp, (caddr_t)&da->da_ddr, 61 sizeof (da->da_ddr), 0, UIO_SYSSPACE, FAPPEND, 62 RLIM64_INFINITY, CRED(), &resid); 63 *da->da_offp += sizeof (da->da_ddr); 64 return (da->da_err); 65 } 66 67 static int 68 report_free_dnode_range(struct diffarg *da, uint64_t first, uint64_t last) 69 { 70 ASSERT(first <= last); 71 if (da->da_ddr.ddr_type != DDR_FREE || 72 first != da->da_ddr.ddr_last + 1) { 73 if (write_record(da) != 0) 74 return (da->da_err); 75 da->da_ddr.ddr_type = DDR_FREE; 76 da->da_ddr.ddr_first = first; 77 da->da_ddr.ddr_last = last; 78 return (0); 79 } 80 da->da_ddr.ddr_last = last; 81 return (0); 82 } 83 84 static int 85 report_dnode(struct diffarg *da, uint64_t object, dnode_phys_t *dnp) 86 { 87 ASSERT(dnp != NULL); 88 if (dnp->dn_type == DMU_OT_NONE) 89 return (report_free_dnode_range(da, object, object)); 90 91 if (da->da_ddr.ddr_type != DDR_INUSE || 92 object != da->da_ddr.ddr_last + 1) { 93 if (write_record(da) != 0) 94 return (da->da_err); 95 da->da_ddr.ddr_type = DDR_INUSE; 96 da->da_ddr.ddr_first = da->da_ddr.ddr_last = object; 97 return (0); 98 } 99 da->da_ddr.ddr_last = object; 100 return (0); 101 } 102 103 #define DBP_SPAN(dnp, level) \ 104 (((uint64_t)dnp->dn_datablkszsec) << (SPA_MINBLOCKSHIFT + \ 105 (level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT))) 106 107 /* ARGSUSED */ 108 static int 109 diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, 110 const zbookmark_t *zb, const dnode_phys_t *dnp, void *arg) 111 { 112 struct diffarg *da = arg; 113 int err = 0; 114 115 if (issig(JUSTLOOKING) && issig(FORREAL)) 116 return (SET_ERROR(EINTR)); 117 118 if (zb->zb_object != DMU_META_DNODE_OBJECT) 119 return (0); 120 121 if (bp == NULL) { 122 uint64_t span = DBP_SPAN(dnp, zb->zb_level); 123 uint64_t dnobj = (zb->zb_blkid * span) >> DNODE_SHIFT; 124 125 err = report_free_dnode_range(da, dnobj, 126 dnobj + (span >> DNODE_SHIFT) - 1); 127 if (err) 128 return (err); 129 } else if (zb->zb_level == 0) { 130 dnode_phys_t *blk; 131 arc_buf_t *abuf; 132 uint32_t aflags = ARC_WAIT; 133 int blksz = BP_GET_LSIZE(bp); 134 int i; 135 136 if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf, 137 ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, 138 &aflags, zb) != 0) 139 return (SET_ERROR(EIO)); 140 141 blk = abuf->b_data; 142 for (i = 0; i < blksz >> DNODE_SHIFT; i++) { 143 uint64_t dnobj = (zb->zb_blkid << 144 (DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i; 145 err = report_dnode(da, dnobj, blk+i); 146 if (err) 147 break; 148 } 149 (void) arc_buf_remove_ref(abuf, &abuf); 150 if (err) 151 return (err); 152 /* Don't care about the data blocks */ 153 return (TRAVERSE_VISIT_NO_CHILDREN); 154 } 155 return (0); 156 } 157 158 int 159 dmu_diff(const char *tosnap_name, const char *fromsnap_name, 160 struct vnode *vp, offset_t *offp) 161 { 162 struct diffarg da; 163 dsl_dataset_t *fromsnap; 164 dsl_dataset_t *tosnap; 165 dsl_pool_t *dp; 166 int error; 167 uint64_t fromtxg; 168 169 if (strchr(tosnap_name, '@') == NULL || 170 strchr(fromsnap_name, '@') == NULL) 171 return (SET_ERROR(EINVAL)); 172 173 error = dsl_pool_hold(tosnap_name, FTAG, &dp); 174 if (error != 0) 175 return (error); 176 177 error = dsl_dataset_hold(dp, tosnap_name, FTAG, &tosnap); 178 if (error != 0) { 179 dsl_pool_rele(dp, FTAG); 180 return (error); 181 } 182 183 error = dsl_dataset_hold(dp, fromsnap_name, FTAG, &fromsnap); 184 if (error != 0) { 185 dsl_dataset_rele(tosnap, FTAG); 186 dsl_pool_rele(dp, FTAG); 187 return (error); 188 } 189 190 if (!dsl_dataset_is_before(tosnap, fromsnap)) { 191 dsl_dataset_rele(fromsnap, FTAG); 192 dsl_dataset_rele(tosnap, FTAG); 193 dsl_pool_rele(dp, FTAG); 194 return (SET_ERROR(EXDEV)); 195 } 196 197 fromtxg = fromsnap->ds_phys->ds_creation_txg; 198 dsl_dataset_rele(fromsnap, FTAG); 199 200 dsl_dataset_long_hold(tosnap, FTAG); 201 dsl_pool_rele(dp, FTAG); 202 203 da.da_vp = vp; 204 da.da_offp = offp; 205 da.da_ddr.ddr_type = DDR_NONE; 206 da.da_ddr.ddr_first = da.da_ddr.ddr_last = 0; 207 da.da_err = 0; 208 209 error = traverse_dataset(tosnap, fromtxg, 210 TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA, diff_cb, &da); 211 212 if (error != 0) { 213 da.da_err = error; 214 } else { 215 /* we set the da.da_err we return as side-effect */ 216 (void) write_record(&da); 217 } 218 219 dsl_dataset_long_rele(tosnap, FTAG); 220 dsl_dataset_rele(tosnap, FTAG); 221 222 return (da.da_err); 223 } 224