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, 2018 by Delphix. All rights reserved. 24 * Copyright (c) 2019, loli10K <ezomori.nozomu@gmail.com>. All rights reserved. 25 */ 26 27 #include <sys/dmu.h> 28 #include <sys/dmu_impl.h> 29 #include <sys/dmu_tx.h> 30 #include <sys/dbuf.h> 31 #include <sys/dnode.h> 32 #include <sys/zfs_context.h> 33 #include <sys/dmu_objset.h> 34 #include <sys/dmu_traverse.h> 35 #include <sys/dsl_dataset.h> 36 #include <sys/dsl_dir.h> 37 #include <sys/dsl_pool.h> 38 #include <sys/dsl_synctask.h> 39 #include <sys/zfs_ioctl.h> 40 #include <sys/zap.h> 41 #include <sys/zio_checksum.h> 42 #include <sys/zfs_znode.h> 43 #include <sys/zfs_file.h> 44 45 46 typedef struct dmu_diffarg { 47 zfs_file_t *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 } dmu_diffarg_t; 52 53 static int 54 write_record(dmu_diffarg_t *da) 55 { 56 zfs_file_t *fp; 57 ssize_t resid; 58 59 if (da->da_ddr.ddr_type == DDR_NONE) { 60 da->da_err = 0; 61 return (0); 62 } 63 64 fp = da->da_fp; 65 da->da_err = zfs_file_write(fp, (caddr_t)&da->da_ddr, 66 sizeof (da->da_ddr), &resid); 67 *da->da_offp += sizeof (da->da_ddr); 68 return (da->da_err); 69 } 70 71 static int 72 report_free_dnode_range(dmu_diffarg_t *da, uint64_t first, uint64_t last) 73 { 74 ASSERT(first <= last); 75 if (da->da_ddr.ddr_type != DDR_FREE || 76 first != da->da_ddr.ddr_last + 1) { 77 if (write_record(da) != 0) 78 return (da->da_err); 79 da->da_ddr.ddr_type = DDR_FREE; 80 da->da_ddr.ddr_first = first; 81 da->da_ddr.ddr_last = last; 82 return (0); 83 } 84 da->da_ddr.ddr_last = last; 85 return (0); 86 } 87 88 static int 89 report_dnode(dmu_diffarg_t *da, uint64_t object, dnode_phys_t *dnp) 90 { 91 ASSERT(dnp != NULL); 92 if (dnp->dn_type == DMU_OT_NONE) 93 return (report_free_dnode_range(da, object, object)); 94 95 if (da->da_ddr.ddr_type != DDR_INUSE || 96 object != da->da_ddr.ddr_last + 1) { 97 if (write_record(da) != 0) 98 return (da->da_err); 99 da->da_ddr.ddr_type = DDR_INUSE; 100 da->da_ddr.ddr_first = da->da_ddr.ddr_last = object; 101 return (0); 102 } 103 da->da_ddr.ddr_last = object; 104 return (0); 105 } 106 107 #define DBP_SPAN(dnp, level) \ 108 (((uint64_t)dnp->dn_datablkszsec) << (SPA_MINBLOCKSHIFT + \ 109 (level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT))) 110 111 static int 112 diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, 113 const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg) 114 { 115 (void) zilog; 116 dmu_diffarg_t *da = arg; 117 int err = 0; 118 119 if (issig(JUSTLOOKING) && issig(FORREAL)) 120 return (SET_ERROR(EINTR)); 121 122 if (zb->zb_level == ZB_DNODE_LEVEL || 123 zb->zb_object != DMU_META_DNODE_OBJECT) 124 return (0); 125 126 if (BP_IS_HOLE(bp)) { 127 uint64_t span = DBP_SPAN(dnp, zb->zb_level); 128 uint64_t dnobj = (zb->zb_blkid * span) >> DNODE_SHIFT; 129 130 err = report_free_dnode_range(da, dnobj, 131 dnobj + (span >> DNODE_SHIFT) - 1); 132 if (err) 133 return (err); 134 } else if (zb->zb_level == 0) { 135 dnode_phys_t *blk; 136 arc_buf_t *abuf; 137 arc_flags_t aflags = ARC_FLAG_WAIT; 138 int epb = BP_GET_LSIZE(bp) >> DNODE_SHIFT; 139 int zio_flags = ZIO_FLAG_CANFAIL; 140 int i; 141 142 if (BP_IS_PROTECTED(bp)) 143 zio_flags |= ZIO_FLAG_RAW; 144 145 if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf, 146 ZIO_PRIORITY_ASYNC_READ, zio_flags, &aflags, zb) != 0) 147 return (SET_ERROR(EIO)); 148 149 blk = abuf->b_data; 150 for (i = 0; i < epb; i += blk[i].dn_extra_slots + 1) { 151 uint64_t dnobj = (zb->zb_blkid << 152 (DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i; 153 err = report_dnode(da, dnobj, blk+i); 154 if (err) 155 break; 156 } 157 arc_buf_destroy(abuf, &abuf); 158 if (err) 159 return (err); 160 /* Don't care about the data blocks */ 161 return (TRAVERSE_VISIT_NO_CHILDREN); 162 } 163 return (0); 164 } 165 166 int 167 dmu_diff(const char *tosnap_name, const char *fromsnap_name, 168 zfs_file_t *fp, offset_t *offp) 169 { 170 dmu_diffarg_t da; 171 dsl_dataset_t *fromsnap; 172 dsl_dataset_t *tosnap; 173 dsl_pool_t *dp; 174 int error; 175 uint64_t fromtxg; 176 177 if (strchr(tosnap_name, '@') == NULL || 178 strchr(fromsnap_name, '@') == NULL) 179 return (SET_ERROR(EINVAL)); 180 181 error = dsl_pool_hold(tosnap_name, FTAG, &dp); 182 if (error != 0) 183 return (error); 184 185 error = dsl_dataset_hold(dp, tosnap_name, FTAG, &tosnap); 186 if (error != 0) { 187 dsl_pool_rele(dp, FTAG); 188 return (error); 189 } 190 191 error = dsl_dataset_hold(dp, fromsnap_name, FTAG, &fromsnap); 192 if (error != 0) { 193 dsl_dataset_rele(tosnap, FTAG); 194 dsl_pool_rele(dp, FTAG); 195 return (error); 196 } 197 198 if (!dsl_dataset_is_before(tosnap, fromsnap, 0)) { 199 dsl_dataset_rele(fromsnap, FTAG); 200 dsl_dataset_rele(tosnap, FTAG); 201 dsl_pool_rele(dp, FTAG); 202 return (SET_ERROR(EXDEV)); 203 } 204 205 fromtxg = dsl_dataset_phys(fromsnap)->ds_creation_txg; 206 dsl_dataset_rele(fromsnap, FTAG); 207 208 dsl_dataset_long_hold(tosnap, FTAG); 209 dsl_pool_rele(dp, FTAG); 210 211 da.da_fp = fp; 212 da.da_offp = offp; 213 da.da_ddr.ddr_type = DDR_NONE; 214 da.da_ddr.ddr_first = da.da_ddr.ddr_last = 0; 215 da.da_err = 0; 216 217 /* 218 * Since zfs diff only looks at dnodes which are stored in plaintext 219 * (other than bonus buffers), we don't technically need to decrypt 220 * the dataset to perform this operation. However, the command line 221 * utility will still fail if the keys are not loaded because the 222 * dataset isn't mounted and because it will fail when it attempts to 223 * call the ZFS_IOC_OBJ_TO_STATS ioctl. 224 */ 225 error = traverse_dataset(tosnap, fromtxg, 226 TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA | TRAVERSE_NO_DECRYPT, 227 diff_cb, &da); 228 229 if (error != 0) { 230 da.da_err = error; 231 } else { 232 /* we set the da.da_err we return as side-effect */ 233 (void) write_record(&da); 234 } 235 236 dsl_dataset_long_rele(tosnap, FTAG); 237 dsl_dataset_rele(tosnap, FTAG); 238 239 return (da.da_err); 240 } 241