]>
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 | |
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. | |
30af21b0 | 23 | * Copyright (c) 2012, 2018 by Delphix. All rights reserved. |
572e2857 BB |
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 | struct vnode *da_vp; /* file to which we are reporting */ | |
45 | offset_t *da_offp; | |
46 | int da_err; /* error that stopped diff search */ | |
47 | dmu_diff_record_t da_ddr; | |
48 | }; | |
49 | ||
50 | static int | |
51 | write_record(struct diffarg *da) | |
52 | { | |
53 | ssize_t resid; /* have to get resid to get detailed errno */ | |
54 | ||
55 | if (da->da_ddr.ddr_type == DDR_NONE) { | |
56 | da->da_err = 0; | |
57 | return (0); | |
58 | } | |
59 | ||
60 | da->da_err = vn_rdwr(UIO_WRITE, da->da_vp, (caddr_t)&da->da_ddr, | |
61 | sizeof (da->da_ddr), 0, UIO_SYSSPACE, FAPPEND, | |
62 | RLIM64_INFINITY, CRED(), &resid); | |
63 | *da->da_offp += sizeof (da->da_ddr); | |
64 | return (da->da_err); | |
65 | } | |
66 | ||
67 | static int | |
68 | report_free_dnode_range(struct diffarg *da, uint64_t first, uint64_t last) | |
69 | { | |
70 | ASSERT(first <= last); | |
71 | if (da->da_ddr.ddr_type != DDR_FREE || | |
72 | first != da->da_ddr.ddr_last + 1) { | |
73 | if (write_record(da) != 0) | |
74 | return (da->da_err); | |
75 | da->da_ddr.ddr_type = DDR_FREE; | |
76 | da->da_ddr.ddr_first = first; | |
77 | da->da_ddr.ddr_last = last; | |
78 | return (0); | |
79 | } | |
80 | da->da_ddr.ddr_last = last; | |
81 | return (0); | |
82 | } | |
83 | ||
84 | static int | |
85 | report_dnode(struct diffarg *da, uint64_t object, dnode_phys_t *dnp) | |
86 | { | |
87 | ASSERT(dnp != NULL); | |
88 | if (dnp->dn_type == DMU_OT_NONE) | |
89 | return (report_free_dnode_range(da, object, object)); | |
90 | ||
91 | if (da->da_ddr.ddr_type != DDR_INUSE || | |
92 | object != da->da_ddr.ddr_last + 1) { | |
93 | if (write_record(da) != 0) | |
94 | return (da->da_err); | |
95 | da->da_ddr.ddr_type = DDR_INUSE; | |
96 | da->da_ddr.ddr_first = da->da_ddr.ddr_last = object; | |
97 | return (0); | |
98 | } | |
99 | da->da_ddr.ddr_last = object; | |
100 | return (0); | |
101 | } | |
102 | ||
103 | #define DBP_SPAN(dnp, level) \ | |
104 | (((uint64_t)dnp->dn_datablkszsec) << (SPA_MINBLOCKSHIFT + \ | |
105 | (level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT))) | |
106 | ||
107 | /* ARGSUSED */ | |
108 | static int | |
294f6806 | 109 | diff_cb(spa_t *spa, zilog_t *zilog, const blkptr_t *bp, |
5dbd68a3 | 110 | const zbookmark_phys_t *zb, const dnode_phys_t *dnp, void *arg) |
572e2857 BB |
111 | { |
112 | struct diffarg *da = arg; | |
113 | int err = 0; | |
114 | ||
115 | if (issig(JUSTLOOKING) && issig(FORREAL)) | |
2e528b49 | 116 | return (SET_ERROR(EINTR)); |
572e2857 | 117 | |
30af21b0 PD |
118 | if (zb->zb_level == ZB_DNODE_LEVEL || |
119 | zb->zb_object != DMU_META_DNODE_OBJECT) | |
572e2857 BB |
120 | return (0); |
121 | ||
b0bc7a84 | 122 | if (BP_IS_HOLE(bp)) { |
572e2857 BB |
123 | uint64_t span = DBP_SPAN(dnp, zb->zb_level); |
124 | uint64_t dnobj = (zb->zb_blkid * span) >> DNODE_SHIFT; | |
125 | ||
126 | err = report_free_dnode_range(da, dnobj, | |
127 | dnobj + (span >> DNODE_SHIFT) - 1); | |
128 | if (err) | |
129 | return (err); | |
130 | } else if (zb->zb_level == 0) { | |
131 | dnode_phys_t *blk; | |
132 | arc_buf_t *abuf; | |
2a432414 | 133 | arc_flags_t aflags = ARC_FLAG_WAIT; |
572e2857 | 134 | int blksz = BP_GET_LSIZE(bp); |
2c24b5b1 | 135 | int zio_flags = ZIO_FLAG_CANFAIL; |
572e2857 BB |
136 | int i; |
137 | ||
2c24b5b1 TC |
138 | if (BP_IS_PROTECTED(bp)) |
139 | zio_flags |= ZIO_FLAG_RAW; | |
140 | ||
294f6806 | 141 | if (arc_read(NULL, spa, bp, arc_getbuf_func, &abuf, |
2c24b5b1 | 142 | ZIO_PRIORITY_ASYNC_READ, zio_flags, &aflags, zb) != 0) |
2e528b49 | 143 | return (SET_ERROR(EIO)); |
572e2857 BB |
144 | |
145 | blk = abuf->b_data; | |
146 | for (i = 0; i < blksz >> DNODE_SHIFT; i++) { | |
147 | uint64_t dnobj = (zb->zb_blkid << | |
148 | (DNODE_BLOCK_SHIFT - DNODE_SHIFT)) + i; | |
149 | err = report_dnode(da, dnobj, blk+i); | |
150 | if (err) | |
151 | break; | |
152 | } | |
d3c2ae1c | 153 | arc_buf_destroy(abuf, &abuf); |
572e2857 BB |
154 | if (err) |
155 | return (err); | |
156 | /* Don't care about the data blocks */ | |
157 | return (TRAVERSE_VISIT_NO_CHILDREN); | |
158 | } | |
159 | return (0); | |
160 | } | |
161 | ||
162 | int | |
13fe0198 MA |
163 | dmu_diff(const char *tosnap_name, const char *fromsnap_name, |
164 | struct vnode *vp, offset_t *offp) | |
572e2857 BB |
165 | { |
166 | struct diffarg da; | |
13fe0198 MA |
167 | dsl_dataset_t *fromsnap; |
168 | dsl_dataset_t *tosnap; | |
169 | dsl_pool_t *dp; | |
170 | int error; | |
171 | uint64_t fromtxg; | |
172 | ||
173 | if (strchr(tosnap_name, '@') == NULL || | |
174 | strchr(fromsnap_name, '@') == NULL) | |
2e528b49 | 175 | return (SET_ERROR(EINVAL)); |
572e2857 | 176 | |
13fe0198 MA |
177 | error = dsl_pool_hold(tosnap_name, FTAG, &dp); |
178 | if (error != 0) | |
179 | return (error); | |
572e2857 | 180 | |
13fe0198 MA |
181 | error = dsl_dataset_hold(dp, tosnap_name, FTAG, &tosnap); |
182 | if (error != 0) { | |
183 | dsl_pool_rele(dp, FTAG); | |
184 | return (error); | |
185 | } | |
572e2857 | 186 | |
13fe0198 MA |
187 | error = dsl_dataset_hold(dp, fromsnap_name, FTAG, &fromsnap); |
188 | if (error != 0) { | |
189 | dsl_dataset_rele(tosnap, FTAG); | |
190 | dsl_pool_rele(dp, FTAG); | |
191 | return (error); | |
192 | } | |
572e2857 | 193 | |
da536844 | 194 | if (!dsl_dataset_is_before(tosnap, fromsnap, 0)) { |
13fe0198 MA |
195 | dsl_dataset_rele(fromsnap, FTAG); |
196 | dsl_dataset_rele(tosnap, FTAG); | |
197 | dsl_pool_rele(dp, FTAG); | |
2e528b49 | 198 | return (SET_ERROR(EXDEV)); |
572e2857 BB |
199 | } |
200 | ||
d683ddbb | 201 | fromtxg = dsl_dataset_phys(fromsnap)->ds_creation_txg; |
13fe0198 MA |
202 | dsl_dataset_rele(fromsnap, FTAG); |
203 | ||
204 | dsl_dataset_long_hold(tosnap, FTAG); | |
205 | dsl_pool_rele(dp, FTAG); | |
572e2857 BB |
206 | |
207 | da.da_vp = vp; | |
208 | da.da_offp = offp; | |
209 | da.da_ddr.ddr_type = DDR_NONE; | |
210 | da.da_ddr.ddr_first = da.da_ddr.ddr_last = 0; | |
211 | da.da_err = 0; | |
212 | ||
2c24b5b1 TC |
213 | /* |
214 | * Since zfs diff only looks at dnodes which are stored in plaintext | |
215 | * (other than bonus buffers), we don't technically need to decrypt | |
216 | * the dataset to perform this operation. However, the command line | |
217 | * utility will still fail if the keys are not loaded because the | |
218 | * dataset isn't mounted and because it will fail when it attempts to | |
219 | * call the ZFS_IOC_OBJ_TO_STATS ioctl. | |
220 | */ | |
13fe0198 | 221 | error = traverse_dataset(tosnap, fromtxg, |
2c24b5b1 TC |
222 | TRAVERSE_PRE | TRAVERSE_PREFETCH_METADATA | TRAVERSE_NO_DECRYPT, |
223 | diff_cb, &da); | |
572e2857 | 224 | |
13fe0198 MA |
225 | if (error != 0) { |
226 | da.da_err = error; | |
572e2857 BB |
227 | } else { |
228 | /* we set the da.da_err we return as side-effect */ | |
229 | (void) write_record(&da); | |
230 | } | |
231 | ||
13fe0198 MA |
232 | dsl_dataset_long_rele(tosnap, FTAG); |
233 | dsl_dataset_rele(tosnap, FTAG); | |
234 | ||
572e2857 BB |
235 | return (da.da_err); |
236 | } |