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.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or https://opensource.org/licenses/CDDL-1.0.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
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]
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.
28 #include <sys/dmu_impl.h>
29 #include <sys/dmu_tx.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>
41 #include <sys/zio_checksum.h>
42 #include <sys/zfs_znode.h>
43 #include <sys/zfs_file.h>
46 typedef struct dmu_diffarg
{
47 zfs_file_t
*da_fp
; /* file to which we are reporting */
49 int da_err
; /* error that stopped diff search */
50 dmu_diff_record_t da_ddr
;
54 write_record(dmu_diffarg_t
*da
)
59 if (da
->da_ddr
.ddr_type
== DDR_NONE
) {
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
);
72 report_free_dnode_range(dmu_diffarg_t
*da
, uint64_t first
, uint64_t last
)
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)
79 da
->da_ddr
.ddr_type
= DDR_FREE
;
80 da
->da_ddr
.ddr_first
= first
;
81 da
->da_ddr
.ddr_last
= last
;
84 da
->da_ddr
.ddr_last
= last
;
89 report_dnode(dmu_diffarg_t
*da
, uint64_t object
, dnode_phys_t
*dnp
)
92 if (dnp
->dn_type
== DMU_OT_NONE
)
93 return (report_free_dnode_range(da
, object
, object
));
95 if (da
->da_ddr
.ddr_type
!= DDR_INUSE
||
96 object
!= da
->da_ddr
.ddr_last
+ 1) {
97 if (write_record(da
) != 0)
99 da
->da_ddr
.ddr_type
= DDR_INUSE
;
100 da
->da_ddr
.ddr_first
= da
->da_ddr
.ddr_last
= object
;
103 da
->da_ddr
.ddr_last
= object
;
107 #define DBP_SPAN(dnp, level) \
108 (((uint64_t)dnp->dn_datablkszsec) << (SPA_MINBLOCKSHIFT + \
109 (level) * (dnp->dn_indblkshift - SPA_BLKPTRSHIFT)))
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
)
116 dmu_diffarg_t
*da
= arg
;
120 return (SET_ERROR(EINTR
));
122 if (zb
->zb_level
== ZB_DNODE_LEVEL
||
123 zb
->zb_object
!= DMU_META_DNODE_OBJECT
)
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
;
130 err
= report_free_dnode_range(da
, dnobj
,
131 dnobj
+ (span
>> DNODE_SHIFT
) - 1);
134 } else if (zb
->zb_level
== 0) {
137 arc_flags_t aflags
= ARC_FLAG_WAIT
;
138 int epb
= BP_GET_LSIZE(bp
) >> DNODE_SHIFT
;
139 int zio_flags
= ZIO_FLAG_CANFAIL
;
142 if (BP_IS_PROTECTED(bp
))
143 zio_flags
|= ZIO_FLAG_RAW
;
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
));
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
);
157 arc_buf_destroy(abuf
, &abuf
);
160 /* Don't care about the data blocks */
161 return (TRAVERSE_VISIT_NO_CHILDREN
);
167 dmu_diff(const char *tosnap_name
, const char *fromsnap_name
,
168 zfs_file_t
*fp
, offset_t
*offp
)
171 dsl_dataset_t
*fromsnap
;
172 dsl_dataset_t
*tosnap
;
177 if (strchr(tosnap_name
, '@') == NULL
||
178 strchr(fromsnap_name
, '@') == NULL
)
179 return (SET_ERROR(EINVAL
));
181 error
= dsl_pool_hold(tosnap_name
, FTAG
, &dp
);
185 error
= dsl_dataset_hold(dp
, tosnap_name
, FTAG
, &tosnap
);
187 dsl_pool_rele(dp
, FTAG
);
191 error
= dsl_dataset_hold(dp
, fromsnap_name
, FTAG
, &fromsnap
);
193 dsl_dataset_rele(tosnap
, FTAG
);
194 dsl_pool_rele(dp
, FTAG
);
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
));
205 fromtxg
= dsl_dataset_phys(fromsnap
)->ds_creation_txg
;
206 dsl_dataset_rele(fromsnap
, FTAG
);
208 dsl_dataset_long_hold(tosnap
, FTAG
);
209 dsl_pool_rele(dp
, FTAG
);
213 da
.da_ddr
.ddr_type
= DDR_NONE
;
214 da
.da_ddr
.ddr_first
= da
.da_ddr
.ddr_last
= 0;
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.
225 error
= traverse_dataset(tosnap
, fromtxg
,
226 TRAVERSE_PRE
| TRAVERSE_PREFETCH_METADATA
| TRAVERSE_NO_DECRYPT
,
232 /* we set the da.da_err we return as side-effect */
233 (void) write_record(&da
);
236 dsl_dataset_long_rele(tosnap
, FTAG
);
237 dsl_dataset_rele(tosnap
, FTAG
);