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 */ 24 25 #include <sys/dmu.h> 26 #include <sys/dmu_impl.h> 27 #include <sys/dmu_tx.h> 28 #include <sys/dbuf.h> 29 #include <sys/dnode.h> 30 #include <sys/zfs_context.h> 31 #include <sys/dmu_objset.h> 32 #include <sys/dmu_traverse.h> 33 #include <sys/dsl_dataset.h> 34 #include <sys/dsl_dir.h> 35 #include <sys/dsl_pool.h> 36 #include <sys/dsl_synctask.h> 37 #include <sys/zfs_ioctl.h> 38 #include <sys/zap.h> 39 #include <sys/zio_checksum.h> 40 #include <sys/zfs_znode.h> 41 42 struct diffarg { 43 struct vnode *da_vp; /* file to which we are reporting */ 44 offset_t *da_offp; 45 int da_err; /* error that stopped diff search */ 46 dmu_diff_record_t da_ddr; 47 }; 48 49 static int 50 write_record(struct diffarg *da) 51 { 52 ssize_t resid; /* have to get resid to get detailed errno */ 53 54 if (da->da_ddr.ddr_type == DDR_NONE) { 55 da->da_err = 0; 56 return (0); 57 } 58 59 da->da_err = vn_rdwr(UIO_WRITE, da->da_vp, (caddr_t)&da->da_ddr, 60 sizeof (da->da_ddr), 0, UIO_SYSSPACE, FAPPEND, 61 RLIM64_INFINITY, CRED(), &resid); 62 *da->da_offp += sizeof (da->da_ddr); 63 return (da->da_err); 64 } 65 66 static int 67 report_free_dnode_range(struct diffarg *da, uint64_t first, uint64_t last) 68 { 69 ASSERT(first <= last); 70 if (da->da_ddr.ddr_type != DDR_FREE || 71 first != da->da_ddr.ddr_last + 1) { 72 if (write_record(da) != 0) 73 return (da->da_err); 74 da->da_ddr.ddr_type = DDR_FREE; 75 da->da_ddr.ddr_first = first; 76 da->da_ddr.ddr_last = last; 77 return (0); 78 } 79 da->da_ddr.ddr_last = last; 80 return (0); 81 } 82 83 static int 84 report_dnode(struct diffarg *da, uint64_t object, dnode_phys_t *dnp) 85 { 86 ASSERT(dnp != NULL); 87 if (dnp->dn_type == DMU_OT_NONE) 88 return (report_free_dnode_range(da, object, object)); 89 90 if (da->da_ddr.ddr_type != DDR_INUSE || 91 object != da->da_ddr.ddr_last + 1) { 92 if (write_record(da) != 0) 93 return (da->da_err); 94 da->da_ddr.ddr_type = DDR_INUSE; 95 da->da_ddr.ddr_first = da->da_ddr.ddr_last = object; 96 return (0); 97 } 98 da->da_ddr.ddr_last = object; 99 return (0); 100 } 101 102 #define DBP_SPAN(dnp, level) \ 103 (((uint64_t)dnp->dn_datablkszsec) << (SPA_MINBLOCKSHIFT + \ 104 (level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT))) 105 106 /* ARGSUSED */ 107 static int 108 diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, 109 const zbookmark_t *zb, const dnode_phys_t *dnp, void *arg) 110 { 111 struct diffarg *da = arg; 112 int err = 0; 113 114 if (issig(JUSTLOOKING) && issig(FORREAL)) 115 return (EINTR); 116 117 if (zb->zb_object != DMU_META_DNODE_OBJECT) 118 return (0); 119 120 if (bp == NULL) { 121 uint64_t span = DBP_SPAN(dnp, zb->zb_level); 122 uint64_t dnobj = (zb->zb_blkid * span) >> DNODE_SHIFT; 123 124 err = report_free_dnode_range(da, dnobj, 125 dnobj + (span >> DNODE_SHIFT) - 1); 126 if (err) 127 return (err); 128 } else if (zb->zb_level == 0) { 129 dnode_phys_t *blk; 130 arc_buf_t *abuf; 131 uint32_t aflags = ARC_WAIT; 132 int blksz = BP_GET_LSIZE(bp); 133 int i; 134 135 if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf, 136 ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, 137 &aflags, zb) != 0) 138 return (EIO); 139 140 blk = abuf->b_data; 141 for (i = 0; i < blksz >> DNODE_SHIFT; i++) { 142 uint64_t dnobj = (zb->zb_blkid << 143 (DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i; 144 err = report_dnode(da, dnobj, blk+i); 145 if (err) 146 break; 147 } 148 (void) arc_buf_remove_ref(abuf, &abuf); 149 if (err) 150 return (err); 151 /* Don't care about the data blocks */ 152 return (TRAVERSE_VISIT_NO_CHILDREN); 153 } 154 return (0); 155 } 156 157 int 158 dmu_diff(objset_t *tosnap, objset_t *fromsnap, struct vnode *vp, offset_t *offp) 159 { 160 struct diffarg da; 161 dsl_dataset_t *ds = tosnap->os_dsl_dataset; 162 dsl_dataset_t *fromds = fromsnap->os_dsl_dataset; 163 dsl_dataset_t *findds; 164 dsl_dataset_t *relds; 165 int err = 0; 166 167 /* make certain we are looking at snapshots */ 168 if (!dsl_dataset_is_snapshot(ds) || !dsl_dataset_is_snapshot(fromds)) 169 return (EINVAL); 170 171 /* fromsnap must be earlier and from the same lineage as tosnap */ 172 if (fromds->ds_phys->ds_creation_txg >= ds->ds_phys->ds_creation_txg) 173 return (EXDEV); 174 175 relds = NULL; 176 findds = ds; 177 178 while (fromds->ds_dir != findds->ds_dir) { 179 dsl_pool_t *dp = ds->ds_dir->dd_pool; 180 181 if (!dsl_dir_is_clone(findds->ds_dir)) { 182 if (relds) 183 dsl_dataset_rele(relds, FTAG); 184 return (EXDEV); 185 } 186 187 rw_enter(&dp->dp_config_rwlock, RW_READER); 188 err = dsl_dataset_hold_obj(dp, 189 findds->ds_dir->dd_phys->dd_origin_obj, FTAG, &findds); 190 rw_exit(&dp->dp_config_rwlock); 191 192 if (relds) 193 dsl_dataset_rele(relds, FTAG); 194 195 if (err) 196 return (EXDEV); 197 198 relds = findds; 199 } 200 201 if (relds) 202 dsl_dataset_rele(relds, FTAG); 203 204 da.da_vp = vp; 205 da.da_offp = offp; 206 da.da_ddr.ddr_type = DDR_NONE; 207 da.da_ddr.ddr_first = da.da_ddr.ddr_last = 0; 208 da.da_err = 0; 209 210 err = traverse_dataset(ds, fromds->ds_phys->ds_creation_txg, 211 TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA, diff_cb, &da); 212 213 if (err) { 214 da.da_err = err; 215 } else { 216 /* we set the da.da_err we return as side-effect */ 217 (void) write_record(&da); 218 } 219 220 return (da.da_err); 221 } 222