]>
Commit | Line | Data |
---|---|---|
b79fc3fe MM |
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 | /* | |
23 | * Copyright 2010 Sun Microsystems, Inc. All rights reserved. | |
24 | * Use is subject to license terms. | |
25 | * | |
26 | * Portions Copyright 2012 Martin Matuska <martin@matuska.org> | |
27 | */ | |
28 | ||
29 | #include <libnvpair.h> | |
30 | #include <stdio.h> | |
31 | #include <stdlib.h> | |
32 | #include <strings.h> | |
33 | #include <unistd.h> | |
34 | ||
35 | #include <sys/dmu.h> | |
36 | #include <sys/zfs_ioctl.h> | |
37 | #include <zfs_fletcher.h> | |
38 | ||
b79fc3fe MM |
39 | uint64_t total_write_size = 0; |
40 | uint64_t total_stream_len = 0; | |
41 | FILE *send_stream = 0; | |
42 | boolean_t do_byteswap = B_FALSE; | |
43 | boolean_t do_cksum = B_TRUE; | |
44 | #define INITIAL_BUFLEN (1<<20) | |
45 | ||
46 | static void | |
47 | usage(void) | |
48 | { | |
49 | (void) fprintf(stderr, "usage: zstreamdump [-v] [-C] < file\n"); | |
50 | (void) fprintf(stderr, "\t -v -- verbose\n"); | |
51 | (void) fprintf(stderr, "\t -C -- suppress checksum verification\n"); | |
52 | exit(1); | |
53 | } | |
54 | ||
55 | /* | |
56 | * ssread - send stream read. | |
57 | * | |
58 | * Read while computing incremental checksum | |
59 | */ | |
60 | ||
61 | static size_t | |
62 | ssread(void *buf, size_t len, zio_cksum_t *cksum) | |
63 | { | |
64 | size_t outlen; | |
65 | ||
66 | if ((outlen = fread(buf, len, 1, send_stream)) == 0) | |
67 | return (0); | |
68 | ||
69 | if (do_cksum && cksum) { | |
70 | if (do_byteswap) | |
71 | fletcher_4_incremental_byteswap(buf, len, cksum); | |
72 | else | |
73 | fletcher_4_incremental_native(buf, len, cksum); | |
74 | } | |
75 | total_stream_len += len; | |
76 | return (outlen); | |
77 | } | |
78 | ||
79 | int | |
80 | main(int argc, char *argv[]) | |
81 | { | |
82 | char *buf = malloc(INITIAL_BUFLEN); | |
9b67f605 MA |
83 | uint64_t drr_record_count[DRR_NUMTYPES] = { 0 }; |
84 | uint64_t total_records = 0; | |
b79fc3fe MM |
85 | dmu_replay_record_t thedrr; |
86 | dmu_replay_record_t *drr = &thedrr; | |
87 | struct drr_begin *drrb = &thedrr.drr_u.drr_begin; | |
88 | struct drr_end *drre = &thedrr.drr_u.drr_end; | |
89 | struct drr_object *drro = &thedrr.drr_u.drr_object; | |
90 | struct drr_freeobjects *drrfo = &thedrr.drr_u.drr_freeobjects; | |
91 | struct drr_write *drrw = &thedrr.drr_u.drr_write; | |
92 | struct drr_write_byref *drrwbr = &thedrr.drr_u.drr_write_byref; | |
93 | struct drr_free *drrf = &thedrr.drr_u.drr_free; | |
94 | struct drr_spill *drrs = &thedrr.drr_u.drr_spill; | |
9b67f605 | 95 | struct drr_write_embedded *drrwe = &thedrr.drr_u.drr_write_embedded; |
b79fc3fe MM |
96 | char c; |
97 | boolean_t verbose = B_FALSE; | |
98 | boolean_t first = B_TRUE; | |
99 | int err; | |
100 | zio_cksum_t zc = { { 0 } }; | |
101 | zio_cksum_t pcksum = { { 0 } }; | |
102 | ||
103 | while ((c = getopt(argc, argv, ":vC")) != -1) { | |
104 | switch (c) { | |
105 | case 'C': | |
106 | do_cksum = B_FALSE; | |
107 | break; | |
108 | case 'v': | |
109 | verbose = B_TRUE; | |
110 | break; | |
111 | case ':': | |
112 | (void) fprintf(stderr, | |
113 | "missing argument for '%c' option\n", optopt); | |
114 | usage(); | |
115 | break; | |
116 | case '?': | |
117 | (void) fprintf(stderr, "invalid option '%c'\n", | |
118 | optopt); | |
119 | usage(); | |
120 | } | |
121 | } | |
122 | ||
123 | if (isatty(STDIN_FILENO)) { | |
124 | (void) fprintf(stderr, | |
125 | "Error: Backup stream can not be read " | |
126 | "from a terminal.\n" | |
127 | "You must redirect standard input.\n"); | |
128 | exit(1); | |
129 | } | |
130 | ||
131 | send_stream = stdin; | |
b79fc3fe MM |
132 | while (ssread(drr, sizeof (dmu_replay_record_t), &zc)) { |
133 | ||
134 | if (first) { | |
135 | if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) { | |
136 | do_byteswap = B_TRUE; | |
137 | if (do_cksum) { | |
138 | ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0); | |
139 | /* | |
140 | * recalculate header checksum now | |
141 | * that we know it needs to be | |
142 | * byteswapped. | |
143 | */ | |
144 | fletcher_4_incremental_byteswap(drr, | |
145 | sizeof (dmu_replay_record_t), &zc); | |
146 | } | |
147 | } else if (drrb->drr_magic != DMU_BACKUP_MAGIC) { | |
148 | (void) fprintf(stderr, "Invalid stream " | |
149 | "(bad magic number)\n"); | |
150 | exit(1); | |
151 | } | |
152 | first = B_FALSE; | |
153 | } | |
154 | if (do_byteswap) { | |
155 | drr->drr_type = BSWAP_32(drr->drr_type); | |
156 | drr->drr_payloadlen = | |
157 | BSWAP_32(drr->drr_payloadlen); | |
158 | } | |
159 | ||
160 | /* | |
161 | * At this point, the leading fields of the replay record | |
162 | * (drr_type and drr_payloadlen) have been byte-swapped if | |
163 | * necessary, but the rest of the data structure (the | |
164 | * union of type-specific structures) is still in its | |
165 | * original state. | |
166 | */ | |
167 | if (drr->drr_type >= DRR_NUMTYPES) { | |
168 | (void) printf("INVALID record found: type 0x%x\n", | |
169 | drr->drr_type); | |
170 | (void) printf("Aborting.\n"); | |
171 | exit(1); | |
172 | } | |
173 | ||
174 | drr_record_count[drr->drr_type]++; | |
9b67f605 | 175 | total_records++; |
b79fc3fe MM |
176 | |
177 | switch (drr->drr_type) { | |
178 | case DRR_BEGIN: | |
179 | if (do_byteswap) { | |
180 | drrb->drr_magic = BSWAP_64(drrb->drr_magic); | |
181 | drrb->drr_versioninfo = | |
182 | BSWAP_64(drrb->drr_versioninfo); | |
183 | drrb->drr_creation_time = | |
184 | BSWAP_64(drrb->drr_creation_time); | |
185 | drrb->drr_type = BSWAP_32(drrb->drr_type); | |
186 | drrb->drr_flags = BSWAP_32(drrb->drr_flags); | |
187 | drrb->drr_toguid = BSWAP_64(drrb->drr_toguid); | |
188 | drrb->drr_fromguid = | |
189 | BSWAP_64(drrb->drr_fromguid); | |
190 | } | |
191 | ||
192 | (void) printf("BEGIN record\n"); | |
193 | (void) printf("\thdrtype = %lld\n", | |
194 | DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo)); | |
195 | (void) printf("\tfeatures = %llx\n", | |
196 | DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo)); | |
197 | (void) printf("\tmagic = %llx\n", | |
198 | (u_longlong_t)drrb->drr_magic); | |
199 | (void) printf("\tcreation_time = %llx\n", | |
200 | (u_longlong_t)drrb->drr_creation_time); | |
201 | (void) printf("\ttype = %u\n", drrb->drr_type); | |
202 | (void) printf("\tflags = 0x%x\n", drrb->drr_flags); | |
203 | (void) printf("\ttoguid = %llx\n", | |
204 | (u_longlong_t)drrb->drr_toguid); | |
205 | (void) printf("\tfromguid = %llx\n", | |
206 | (u_longlong_t)drrb->drr_fromguid); | |
207 | (void) printf("\ttoname = %s\n", drrb->drr_toname); | |
208 | if (verbose) | |
209 | (void) printf("\n"); | |
210 | ||
211 | if ((DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) == | |
212 | DMU_COMPOUNDSTREAM) && drr->drr_payloadlen != 0) { | |
213 | nvlist_t *nv; | |
214 | int sz = drr->drr_payloadlen; | |
215 | ||
216 | if (sz > 1<<20) { | |
217 | free(buf); | |
218 | buf = malloc(sz); | |
219 | } | |
220 | (void) ssread(buf, sz, &zc); | |
221 | if (ferror(send_stream)) | |
222 | perror("fread"); | |
223 | err = nvlist_unpack(buf, sz, &nv, 0); | |
224 | if (err) | |
225 | perror(strerror(err)); | |
226 | nvlist_print(stdout, nv); | |
227 | nvlist_free(nv); | |
228 | } | |
229 | break; | |
230 | ||
231 | case DRR_END: | |
232 | if (do_byteswap) { | |
233 | drre->drr_checksum.zc_word[0] = | |
234 | BSWAP_64(drre->drr_checksum.zc_word[0]); | |
235 | drre->drr_checksum.zc_word[1] = | |
236 | BSWAP_64(drre->drr_checksum.zc_word[1]); | |
237 | drre->drr_checksum.zc_word[2] = | |
238 | BSWAP_64(drre->drr_checksum.zc_word[2]); | |
239 | drre->drr_checksum.zc_word[3] = | |
240 | BSWAP_64(drre->drr_checksum.zc_word[3]); | |
241 | } | |
242 | /* | |
243 | * We compare against the *previous* checksum | |
244 | * value, because the stored checksum is of | |
245 | * everything before the DRR_END record. | |
246 | */ | |
247 | if (do_cksum && !ZIO_CHECKSUM_EQUAL(drre->drr_checksum, | |
248 | pcksum)) { | |
249 | (void) printf("Expected checksum differs from " | |
250 | "checksum in stream.\n"); | |
251 | (void) printf("Expected checksum = " | |
252 | "%llx/%llx/%llx/%llx\n", | |
253 | (long long unsigned int)pcksum.zc_word[0], | |
254 | (long long unsigned int)pcksum.zc_word[1], | |
255 | (long long unsigned int)pcksum.zc_word[2], | |
256 | (long long unsigned int)pcksum.zc_word[3]); | |
257 | } | |
258 | (void) printf("END checksum = %llx/%llx/%llx/%llx\n", | |
259 | (long long unsigned int) | |
260 | drre->drr_checksum.zc_word[0], | |
261 | (long long unsigned int) | |
262 | drre->drr_checksum.zc_word[1], | |
263 | (long long unsigned int) | |
264 | drre->drr_checksum.zc_word[2], | |
265 | (long long unsigned int) | |
266 | drre->drr_checksum.zc_word[3]); | |
267 | ||
268 | ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0); | |
269 | break; | |
270 | ||
271 | case DRR_OBJECT: | |
272 | if (do_byteswap) { | |
273 | drro->drr_object = BSWAP_64(drro->drr_object); | |
274 | drro->drr_type = BSWAP_32(drro->drr_type); | |
275 | drro->drr_bonustype = | |
276 | BSWAP_32(drro->drr_bonustype); | |
277 | drro->drr_blksz = BSWAP_32(drro->drr_blksz); | |
278 | drro->drr_bonuslen = | |
279 | BSWAP_32(drro->drr_bonuslen); | |
280 | drro->drr_toguid = BSWAP_64(drro->drr_toguid); | |
281 | } | |
282 | if (verbose) { | |
283 | (void) printf("OBJECT object = %llu type = %u " | |
284 | "bonustype = %u blksz = %u bonuslen = %u\n", | |
285 | (u_longlong_t)drro->drr_object, | |
286 | drro->drr_type, | |
287 | drro->drr_bonustype, | |
288 | drro->drr_blksz, | |
289 | drro->drr_bonuslen); | |
290 | } | |
291 | if (drro->drr_bonuslen > 0) { | |
9b67f605 MA |
292 | (void) ssread(buf, |
293 | P2ROUNDUP(drro->drr_bonuslen, 8), &zc); | |
b79fc3fe MM |
294 | } |
295 | break; | |
296 | ||
297 | case DRR_FREEOBJECTS: | |
298 | if (do_byteswap) { | |
299 | drrfo->drr_firstobj = | |
300 | BSWAP_64(drrfo->drr_firstobj); | |
301 | drrfo->drr_numobjs = | |
302 | BSWAP_64(drrfo->drr_numobjs); | |
303 | drrfo->drr_toguid = BSWAP_64(drrfo->drr_toguid); | |
304 | } | |
305 | if (verbose) { | |
306 | (void) printf("FREEOBJECTS firstobj = %llu " | |
307 | "numobjs = %llu\n", | |
308 | (u_longlong_t)drrfo->drr_firstobj, | |
309 | (u_longlong_t)drrfo->drr_numobjs); | |
310 | } | |
311 | break; | |
312 | ||
313 | case DRR_WRITE: | |
314 | if (do_byteswap) { | |
315 | drrw->drr_object = BSWAP_64(drrw->drr_object); | |
316 | drrw->drr_type = BSWAP_32(drrw->drr_type); | |
317 | drrw->drr_offset = BSWAP_64(drrw->drr_offset); | |
318 | drrw->drr_length = BSWAP_64(drrw->drr_length); | |
319 | drrw->drr_toguid = BSWAP_64(drrw->drr_toguid); | |
320 | drrw->drr_key.ddk_prop = | |
321 | BSWAP_64(drrw->drr_key.ddk_prop); | |
322 | } | |
323 | if (verbose) { | |
324 | (void) printf("WRITE object = %llu type = %u " | |
325 | "checksum type = %u\n" | |
326 | "offset = %llu length = %llu " | |
327 | "props = %llx\n", | |
328 | (u_longlong_t)drrw->drr_object, | |
329 | drrw->drr_type, | |
330 | drrw->drr_checksumtype, | |
331 | (u_longlong_t)drrw->drr_offset, | |
332 | (u_longlong_t)drrw->drr_length, | |
333 | (u_longlong_t)drrw->drr_key.ddk_prop); | |
334 | } | |
335 | (void) ssread(buf, drrw->drr_length, &zc); | |
336 | total_write_size += drrw->drr_length; | |
337 | break; | |
338 | ||
339 | case DRR_WRITE_BYREF: | |
340 | if (do_byteswap) { | |
341 | drrwbr->drr_object = | |
342 | BSWAP_64(drrwbr->drr_object); | |
343 | drrwbr->drr_offset = | |
344 | BSWAP_64(drrwbr->drr_offset); | |
345 | drrwbr->drr_length = | |
346 | BSWAP_64(drrwbr->drr_length); | |
347 | drrwbr->drr_toguid = | |
348 | BSWAP_64(drrwbr->drr_toguid); | |
349 | drrwbr->drr_refguid = | |
350 | BSWAP_64(drrwbr->drr_refguid); | |
351 | drrwbr->drr_refobject = | |
352 | BSWAP_64(drrwbr->drr_refobject); | |
353 | drrwbr->drr_refoffset = | |
354 | BSWAP_64(drrwbr->drr_refoffset); | |
355 | drrwbr->drr_key.ddk_prop = | |
356 | BSWAP_64(drrwbr->drr_key.ddk_prop); | |
357 | } | |
358 | if (verbose) { | |
359 | (void) printf("WRITE_BYREF object = %llu " | |
360 | "checksum type = %u props = %llx\n" | |
361 | "offset = %llu length = %llu\n" | |
362 | "toguid = %llx refguid = %llx\n" | |
363 | "refobject = %llu refoffset = %llu\n", | |
364 | (u_longlong_t)drrwbr->drr_object, | |
365 | drrwbr->drr_checksumtype, | |
366 | (u_longlong_t)drrwbr->drr_key.ddk_prop, | |
367 | (u_longlong_t)drrwbr->drr_offset, | |
368 | (u_longlong_t)drrwbr->drr_length, | |
369 | (u_longlong_t)drrwbr->drr_toguid, | |
370 | (u_longlong_t)drrwbr->drr_refguid, | |
371 | (u_longlong_t)drrwbr->drr_refobject, | |
372 | (u_longlong_t)drrwbr->drr_refoffset); | |
373 | } | |
374 | break; | |
375 | ||
376 | case DRR_FREE: | |
377 | if (do_byteswap) { | |
378 | drrf->drr_object = BSWAP_64(drrf->drr_object); | |
379 | drrf->drr_offset = BSWAP_64(drrf->drr_offset); | |
380 | drrf->drr_length = BSWAP_64(drrf->drr_length); | |
381 | } | |
382 | if (verbose) { | |
383 | (void) printf("FREE object = %llu " | |
384 | "offset = %llu length = %lld\n", | |
385 | (u_longlong_t)drrf->drr_object, | |
386 | (u_longlong_t)drrf->drr_offset, | |
387 | (longlong_t)drrf->drr_length); | |
388 | } | |
389 | break; | |
390 | case DRR_SPILL: | |
391 | if (do_byteswap) { | |
392 | drrs->drr_object = BSWAP_64(drrs->drr_object); | |
393 | drrs->drr_length = BSWAP_64(drrs->drr_length); | |
394 | } | |
395 | if (verbose) { | |
396 | (void) printf("SPILL block for object = %llu " | |
397 | "length = %llu\n", | |
398 | (long long unsigned int)drrs->drr_object, | |
399 | (long long unsigned int)drrs->drr_length); | |
400 | } | |
401 | (void) ssread(buf, drrs->drr_length, &zc); | |
402 | break; | |
9b67f605 MA |
403 | case DRR_WRITE_EMBEDDED: |
404 | if (do_byteswap) { | |
405 | drrwe->drr_object = | |
406 | BSWAP_64(drrwe->drr_object); | |
407 | drrwe->drr_offset = | |
408 | BSWAP_64(drrwe->drr_offset); | |
409 | drrwe->drr_length = | |
410 | BSWAP_64(drrwe->drr_length); | |
411 | drrwe->drr_toguid = | |
412 | BSWAP_64(drrwe->drr_toguid); | |
413 | drrwe->drr_lsize = | |
414 | BSWAP_32(drrwe->drr_lsize); | |
415 | drrwe->drr_psize = | |
416 | BSWAP_32(drrwe->drr_psize); | |
417 | } | |
418 | if (verbose) { | |
419 | (void) printf("WRITE_EMBEDDED object = %llu " | |
420 | "offset = %llu length = %llu\n" | |
421 | "toguid = %llx comp = %u etype = %u " | |
422 | "lsize = %u psize = %u\n", | |
423 | (u_longlong_t)drrwe->drr_object, | |
424 | (u_longlong_t)drrwe->drr_offset, | |
425 | (u_longlong_t)drrwe->drr_length, | |
426 | (u_longlong_t)drrwe->drr_toguid, | |
427 | drrwe->drr_compression, | |
428 | drrwe->drr_etype, | |
429 | drrwe->drr_lsize, | |
430 | drrwe->drr_psize); | |
431 | } | |
432 | (void) ssread(buf, | |
433 | P2ROUNDUP(drrwe->drr_psize, 8), &zc); | |
434 | break; | |
b79fc3fe MM |
435 | case DRR_NUMTYPES: |
436 | /* should never be reached */ | |
437 | exit(1); | |
438 | } | |
439 | pcksum = zc; | |
440 | } | |
441 | free(buf); | |
442 | ||
443 | /* Print final summary */ | |
444 | ||
445 | (void) printf("SUMMARY:\n"); | |
446 | (void) printf("\tTotal DRR_BEGIN records = %lld\n", | |
447 | (u_longlong_t)drr_record_count[DRR_BEGIN]); | |
448 | (void) printf("\tTotal DRR_END records = %lld\n", | |
449 | (u_longlong_t)drr_record_count[DRR_END]); | |
450 | (void) printf("\tTotal DRR_OBJECT records = %lld\n", | |
451 | (u_longlong_t)drr_record_count[DRR_OBJECT]); | |
452 | (void) printf("\tTotal DRR_FREEOBJECTS records = %lld\n", | |
453 | (u_longlong_t)drr_record_count[DRR_FREEOBJECTS]); | |
454 | (void) printf("\tTotal DRR_WRITE records = %lld\n", | |
455 | (u_longlong_t)drr_record_count[DRR_WRITE]); | |
9b67f605 MA |
456 | (void) printf("\tTotal DRR_WRITE_BYREF records = %lld\n", |
457 | (u_longlong_t)drr_record_count[DRR_WRITE_BYREF]); | |
458 | (void) printf("\tTotal DRR_WRITE_EMBEDDED records = %lld\n", | |
459 | (u_longlong_t)drr_record_count[DRR_WRITE_EMBEDDED]); | |
b79fc3fe MM |
460 | (void) printf("\tTotal DRR_FREE records = %lld\n", |
461 | (u_longlong_t)drr_record_count[DRR_FREE]); | |
462 | (void) printf("\tTotal DRR_SPILL records = %lld\n", | |
463 | (u_longlong_t)drr_record_count[DRR_SPILL]); | |
464 | (void) printf("\tTotal records = %lld\n", | |
9b67f605 | 465 | (u_longlong_t)total_records); |
b79fc3fe MM |
466 | (void) printf("\tTotal write size = %lld (0x%llx)\n", |
467 | (u_longlong_t)total_write_size, (u_longlong_t)total_write_size); | |
468 | (void) printf("\tTotal stream length = %lld (0x%llx)\n", | |
469 | (u_longlong_t)total_stream_len, (u_longlong_t)total_stream_len); | |
470 | return (0); | |
471 | } |