]>
Commit | Line | Data |
---|---|---|
572e2857 BB |
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 | |
1d3ba0bf | 9 | * or https://opensource.org/licenses/CDDL-1.0. |
572e2857 BB |
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. | |
30af21b0 | 23 | * Copyright (c) 2012, 2018 by Delphix. All rights reserved. |
d359e99c | 24 | * Copyright (c) 2019, loli10K <ezomori.nozomu@gmail.com>. All rights reserved. |
572e2857 BB |
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> | |
da92d5cb | 43 | #include <sys/zfs_file.h> |
572e2857 | 44 | |
da92d5cb MM |
45 | |
46 | typedef struct dmu_diffarg { | |
47 | zfs_file_t *da_fp; /* file to which we are reporting */ | |
572e2857 BB |
48 | offset_t *da_offp; |
49 | int da_err; /* error that stopped diff search */ | |
50 | dmu_diff_record_t da_ddr; | |
da92d5cb | 51 | } dmu_diffarg_t; |
572e2857 | 52 | |
bff8fb39 | 53 | static int |
da92d5cb | 54 | write_record(dmu_diffarg_t *da) |
572e2857 | 55 | { |
da92d5cb MM |
56 | zfs_file_t *fp; |
57 | ssize_t resid; | |
572e2857 BB |
58 | |
59 | if (da->da_ddr.ddr_type == DDR_NONE) { | |
60 | da->da_err = 0; | |
61 | return (0); | |
62 | } | |
63 | ||
da92d5cb MM |
64 | fp = da->da_fp; |
65 | da->da_err = zfs_file_write(fp, (caddr_t)&da->da_ddr, | |
66 | sizeof (da->da_ddr), &resid); | |
572e2857 BB |
67 | *da->da_offp += sizeof (da->da_ddr); |
68 | return (da->da_err); | |
69 | } | |
70 | ||
71 | static int | |
da92d5cb | 72 | report_free_dnode_range(dmu_diffarg_t *da, uint64_t first, uint64_t last) |
572e2857 BB |
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 | |
da92d5cb | 89 | report_dnode(dmu_diffarg_t *da, uint64_t object, dnode_phys_t *dnp) |
572e2857 BB |
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 | ||
572e2857 | 111 | static int |
294f6806 | 112 | diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, |
5dbd68a3 | 113 | const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg) |
572e2857 | 114 | { |
14e4e3cb | 115 | (void) zilog; |
da92d5cb | 116 | dmu_diffarg_t *da = arg; |
572e2857 BB |
117 | int err = 0; |
118 | ||
119 | if (issig(JUSTLOOKING) && issig(FORREAL)) | |
2e528b49 | 120 | return (SET_ERROR(EINTR)); |
572e2857 | 121 | |
30af21b0 PD |
122 | if (zb->zb_level == ZB_DNODE_LEVEL || |
123 | zb->zb_object != DMU_META_DNODE_OBJECT) | |
572e2857 BB |
124 | return (0); |
125 | ||
b0bc7a84 | 126 | if (BP_IS_HOLE(bp)) { |
572e2857 BB |
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; | |
2a432414 | 137 | arc_flags_t aflags = ARC_FLAG_WAIT; |
d359e99c | 138 | int epb = BP_GET_LSIZE(bp) >> DNODE_SHIFT; |
2c24b5b1 | 139 | int zio_flags = ZIO_FLAG_CANFAIL; |
572e2857 BB |
140 | int i; |
141 | ||
2c24b5b1 TC |
142 | if (BP_IS_PROTECTED(bp)) |
143 | zio_flags |= ZIO_FLAG_RAW; | |
144 | ||
294f6806 | 145 | if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf, |
2c24b5b1 | 146 | ZIO_PRIORITY_ASYNC_READ, zio_flags, &aflags, zb) != 0) |
2e528b49 | 147 | return (SET_ERROR(EIO)); |
572e2857 BB |
148 | |
149 | blk = abuf->b_data; | |
d359e99c | 150 | for (i = 0; i < epb; i += blk[i].dn_extra_slots + 1) { |
572e2857 BB |
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 | } | |
d3c2ae1c | 157 | arc_buf_destroy(abuf, &abuf); |
572e2857 BB |
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 | |
13fe0198 | 167 | dmu_diff(const char *tosnap_name, const char *fromsnap_name, |
da92d5cb | 168 | zfs_file_t *fp, offset_t *offp) |
572e2857 | 169 | { |
da92d5cb | 170 | dmu_diffarg_t da; |
13fe0198 MA |
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) | |
2e528b49 | 179 | return (SET_ERROR(EINVAL)); |
572e2857 | 180 | |
13fe0198 MA |
181 | error = dsl_pool_hold(tosnap_name, FTAG, &dp); |
182 | if (error != 0) | |
183 | return (error); | |
572e2857 | 184 | |
13fe0198 MA |
185 | error = dsl_dataset_hold(dp, tosnap_name, FTAG, &tosnap); |
186 | if (error != 0) { | |
187 | dsl_pool_rele(dp, FTAG); | |
188 | return (error); | |
189 | } | |
572e2857 | 190 | |
13fe0198 MA |
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 | } | |
572e2857 | 197 | |
da536844 | 198 | if (!dsl_dataset_is_before(tosnap, fromsnap, 0)) { |
13fe0198 MA |
199 | dsl_dataset_rele(fromsnap, FTAG); |
200 | dsl_dataset_rele(tosnap, FTAG); | |
201 | dsl_pool_rele(dp, FTAG); | |
2e528b49 | 202 | return (SET_ERROR(EXDEV)); |
572e2857 BB |
203 | } |
204 | ||
d683ddbb | 205 | fromtxg = dsl_dataset_phys(fromsnap)->ds_creation_txg; |
13fe0198 MA |
206 | dsl_dataset_rele(fromsnap, FTAG); |
207 | ||
208 | dsl_dataset_long_hold(tosnap, FTAG); | |
209 | dsl_pool_rele(dp, FTAG); | |
572e2857 | 210 | |
da92d5cb | 211 | da.da_fp = fp; |
572e2857 BB |
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 | ||
2c24b5b1 TC |
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 | */ | |
13fe0198 | 225 | error = traverse_dataset(tosnap, fromtxg, |
2c24b5b1 TC |
226 | TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA | TRAVERSE_NO_DECRYPT, |
227 | diff_cb, &da); | |
572e2857 | 228 | |
13fe0198 MA |
229 | if (error != 0) { |
230 | da.da_err = error; | |
572e2857 BB |
231 | } else { |
232 | /* we set the da.da_err we return as side-effect */ | |
233 | (void) write_record(&da); | |
234 | } | |
235 | ||
13fe0198 MA |
236 | dsl_dataset_long_rele(tosnap, FTAG); |
237 | dsl_dataset_rele(tosnap, FTAG); | |
238 | ||
572e2857 BB |
239 | return (da.da_err); |
240 | } |