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