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, 2015 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 #ifdef __FreeBSD__ 45 kthread_t *da_td; 46 #endif 47 struct file *da_fp; /* file to which we are reporting */ 48 offset_t *da_offp; 49 int da_err; /* error that stopped diff search */ 50 dmu_diff_record_t da_ddr; 51 }; 52 53 static int 54 write_bytes(struct diffarg *da) 55 { 56 struct uio auio; 57 struct iovec aiov; 58 59 aiov.iov_base = (caddr_t)&da->da_ddr; 60 aiov.iov_len = sizeof (da->da_ddr); 61 auio.uio_iov = &aiov; 62 auio.uio_iovcnt = 1; 63 auio.uio_resid = aiov.iov_len; 64 auio.uio_rw = UIO_WRITE; 65 auio.uio_offset = (off_t)-1; 66 #ifdef __FreeBSD__ 67 auio.uio_segflg = UIO_SYSSPACE; 68 auio.uio_td = da->da_td; 69 #else 70 #ifdef _KERNEL 71 auio.uio_vmspace = vmspace_kernel(); 72 #endif 73 #endif /* __FreeBSD__ */ 74 #ifdef _KERNEL 75 #ifdef __FreeBSD__ 76 if (da->da_fp->f_type == DTYPE_VNODE) 77 bwillwrite(); 78 return (fo_write(da->da_fp, &auio, da->da_td->td_ucred, 0, da->da_td)); 79 #else 80 int flags = 0; 81 82 if (da->da_fp->f_type == DTYPE_VNODE) 83 flags |= FOF_UPDATE_OFFSET; 84 return (*da->da_fp->f_ops->fo_write)(da->da_fp, &da->da_fp->f_offset, 85 &auio, da->da_fp->f_cred, flags); 86 #endif /* __FreeBSD__ */ 87 #else 88 fprintf(stderr, "%s: returning EOPNOTSUPP\n", __func__); 89 return (EOPNOTSUPP); 90 #endif 91 } 92 93 static int 94 write_record(struct diffarg *da) 95 { 96 ssize_t resid; /* have to get resid to get detailed errno */ 97 98 if (da->da_ddr.ddr_type == DDR_NONE) { 99 da->da_err = 0; 100 return (0); 101 } 102 103 da->da_err = write_bytes(da); 104 *da->da_offp += sizeof (da->da_ddr); 105 return (da->da_err); 106 } 107 108 static int 109 report_free_dnode_range(struct diffarg *da, uint64_t first, uint64_t last) 110 { 111 ASSERT(first <= last); 112 if (da->da_ddr.ddr_type != DDR_FREE || 113 first != da->da_ddr.ddr_last + 1) { 114 if (write_record(da) != 0) 115 return (da->da_err); 116 da->da_ddr.ddr_type = DDR_FREE; 117 da->da_ddr.ddr_first = first; 118 da->da_ddr.ddr_last = last; 119 return (0); 120 } 121 da->da_ddr.ddr_last = last; 122 return (0); 123 } 124 125 static int 126 report_dnode(struct diffarg *da, uint64_t object, dnode_phys_t *dnp) 127 { 128 ASSERT(dnp != NULL); 129 if (dnp->dn_type == DMU_OT_NONE) 130 return (report_free_dnode_range(da, object, object)); 131 132 if (da->da_ddr.ddr_type != DDR_INUSE || 133 object != da->da_ddr.ddr_last + 1) { 134 if (write_record(da) != 0) 135 return (da->da_err); 136 da->da_ddr.ddr_type = DDR_INUSE; 137 da->da_ddr.ddr_first = da->da_ddr.ddr_last = object; 138 return (0); 139 } 140 da->da_ddr.ddr_last = object; 141 return (0); 142 } 143 144 #define DBP_SPAN(dnp, level) \ 145 (((uint64_t)dnp->dn_datablkszsec) << (SPA_MINBLOCKSHIFT + \ 146 (level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT))) 147 148 /* ARGSUSED */ 149 static int 150 diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, 151 const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg) 152 { 153 struct diffarg *da = arg; 154 int err = 0; 155 156 if (issig(JUSTLOOKING) && issig(FORREAL)) 157 return (SET_ERROR(EINTR)); 158 159 if (bp == NULL || zb->zb_object != DMU_META_DNODE_OBJECT) 160 return (0); 161 162 if (BP_IS_HOLE(bp)) { 163 uint64_t span = DBP_SPAN(dnp, zb->zb_level); 164 uint64_t dnobj = (zb->zb_blkid * span) >> DNODE_SHIFT; 165 166 err = report_free_dnode_range(da, dnobj, 167 dnobj + (span >> DNODE_SHIFT) - 1); 168 if (err) 169 return (err); 170 } else if (zb->zb_level == 0) { 171 dnode_phys_t *blk; 172 arc_buf_t *abuf; 173 arc_flags_t aflags = ARC_FLAG_WAIT; 174 int blksz = BP_GET_LSIZE(bp); 175 int i; 176 177 if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf, 178 ZIO_PRIORITY_ASYNC_READ, ZIO_FLAG_CANFAIL, 179 &aflags, zb) != 0) 180 return (SET_ERROR(EIO)); 181 182 blk = abuf->b_data; 183 for (i = 0; i < blksz >> DNODE_SHIFT; i++) { 184 uint64_t dnobj = (zb->zb_blkid << 185 (DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i; 186 err = report_dnode(da, dnobj, blk+i); 187 if (err) 188 break; 189 } 190 arc_buf_destroy(abuf, &abuf); 191 if (err) 192 return (err); 193 /* Don't care about the data blocks */ 194 return (TRAVERSE_VISIT_NO_CHILDREN); 195 } 196 return (0); 197 } 198 199 int 200 dmu_diff(const char *tosnap_name, const char *fromsnap_name, 201 struct file *fp, offset_t *offp) 202 { 203 struct diffarg da; 204 dsl_dataset_t *fromsnap; 205 dsl_dataset_t *tosnap; 206 dsl_pool_t *dp; 207 int error; 208 uint64_t fromtxg; 209 210 if (strchr(tosnap_name, '@') == NULL || 211 strchr(fromsnap_name, '@') == NULL) 212 return (SET_ERROR(EINVAL)); 213 214 error = dsl_pool_hold(tosnap_name, FTAG, &dp); 215 if (error != 0) 216 return (error); 217 218 error = dsl_dataset_hold(dp, tosnap_name, FTAG, &tosnap); 219 if (error != 0) { 220 dsl_pool_rele(dp, FTAG); 221 return (error); 222 } 223 224 error = dsl_dataset_hold(dp, fromsnap_name, FTAG, &fromsnap); 225 if (error != 0) { 226 dsl_dataset_rele(tosnap, FTAG); 227 dsl_pool_rele(dp, FTAG); 228 return (error); 229 } 230 231 if (!dsl_dataset_is_before(tosnap, fromsnap, 0)) { 232 dsl_dataset_rele(fromsnap, FTAG); 233 dsl_dataset_rele(tosnap, FTAG); 234 dsl_pool_rele(dp, FTAG); 235 return (SET_ERROR(EXDEV)); 236 } 237 238 fromtxg = dsl_dataset_phys(fromsnap)->ds_creation_txg; 239 dsl_dataset_rele(fromsnap, FTAG); 240 241 dsl_dataset_long_hold(tosnap, FTAG); 242 dsl_pool_rele(dp, FTAG); 243 244 #ifdef __FreeBSD__ 245 da.da_td = curthread; 246 #endif 247 da.da_fp = fp; 248 da.da_offp = offp; 249 da.da_ddr.ddr_type = DDR_NONE; 250 da.da_ddr.ddr_first = da.da_ddr.ddr_last = 0; 251 da.da_err = 0; 252 253 error = traverse_dataset(tosnap, fromtxg, 254 TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA, diff_cb, &da); 255 256 if (error != 0) { 257 da.da_err = error; 258 } else { 259 /* we set the da.da_err we return as side-effect */ 260 (void) write_record(&da); 261 } 262 263 dsl_dataset_long_rele(tosnap, FTAG); 264 dsl_dataset_rele(tosnap, FTAG); 265 266 return (da.da_err); 267 } 268