zstreamdump.c revision 268649
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
27/*
28 * Copyright (c) 2013 by Delphix. All rights reserved.
29 */
30
31#include <ctype.h>
32#include <libnvpair.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <strings.h>
36#include <unistd.h>
37
38#include <sys/dmu.h>
39#include <sys/zfs_ioctl.h>
40#include <zfs_fletcher.h>
41
42/*
43 * If dump mode is enabled, the number of bytes to print per line
44 */
45#define	BYTES_PER_LINE	16
46/*
47 * If dump mode is enabled, the number of bytes to group together, separated
48 * by newlines or spaces
49 */
50#define	DUMP_GROUPING	4
51
52uint64_t total_write_size = 0;
53uint64_t total_stream_len = 0;
54FILE *send_stream = 0;
55boolean_t do_byteswap = B_FALSE;
56boolean_t do_cksum = B_TRUE;
57#define	INITIAL_BUFLEN (1<<20)
58
59static void
60usage(void)
61{
62	(void) fprintf(stderr, "usage: zstreamdump [-v] [-C] [-d] < file\n");
63	(void) fprintf(stderr, "\t -v -- verbose\n");
64	(void) fprintf(stderr, "\t -C -- suppress checksum verification\n");
65	(void) fprintf(stderr, "\t -d -- dump contents of blocks modified, "
66	    "implies verbose\n");
67	exit(1);
68}
69
70/*
71 * ssread - send stream read.
72 *
73 * Read while computing incremental checksum
74 */
75
76static size_t
77ssread(void *buf, size_t len, zio_cksum_t *cksum)
78{
79	size_t outlen;
80
81	if ((outlen = fread(buf, len, 1, send_stream)) == 0)
82		return (0);
83
84	if (do_cksum && cksum) {
85		if (do_byteswap)
86			fletcher_4_incremental_byteswap(buf, len, cksum);
87		else
88			fletcher_4_incremental_native(buf, len, cksum);
89	}
90	total_stream_len += len;
91	return (outlen);
92}
93
94/*
95 * Print part of a block in ASCII characters
96 */
97static void
98print_ascii_block(char *subbuf, int length)
99{
100	int i;
101
102	for (i = 0; i < length; i++) {
103		char char_print = isprint(subbuf[i]) ? subbuf[i] : '.';
104		if (i != 0 && i % DUMP_GROUPING == 0) {
105			(void) printf(" ");
106		}
107		(void) printf("%c", char_print);
108	}
109	(void) printf("\n");
110}
111
112/*
113 * print_block - Dump the contents of a modified block to STDOUT
114 *
115 * Assume that buf has capacity evenly divisible by BYTES_PER_LINE
116 */
117static void
118print_block(char *buf, int length)
119{
120	int i;
121	/*
122	 * Start printing ASCII characters at a constant offset, after
123	 * the hex prints. Leave 3 characters per byte on a line (2 digit
124	 * hex number plus 1 space) plus spaces between characters and
125	 * groupings.
126	 */
127	int ascii_start = BYTES_PER_LINE * 3 +
128	    BYTES_PER_LINE / DUMP_GROUPING + 2;
129
130	for (i = 0; i < length; i += BYTES_PER_LINE) {
131		int j;
132		int this_line_length = MIN(BYTES_PER_LINE, length - i);
133		int print_offset = 0;
134
135		for (j = 0; j < this_line_length; j++) {
136			int buf_offset = i + j;
137
138			/*
139			 * Separate every DUMP_GROUPING bytes by a space.
140			 */
141			if (buf_offset % DUMP_GROUPING == 0) {
142				print_offset += printf(" ");
143			}
144
145			/*
146			 * Print the two-digit hex value for this byte.
147			 */
148			unsigned char hex_print = buf[buf_offset];
149			print_offset += printf("%02x ", hex_print);
150		}
151
152		(void) printf("%*s", ascii_start - print_offset, " ");
153
154		print_ascii_block(buf + i, this_line_length);
155	}
156}
157
158int
159main(int argc, char *argv[])
160{
161	char *buf = malloc(INITIAL_BUFLEN);
162	uint64_t drr_record_count[DRR_NUMTYPES] = { 0 };
163	uint64_t total_records = 0;
164	dmu_replay_record_t thedrr;
165	dmu_replay_record_t *drr = &thedrr;
166	struct drr_begin *drrb = &thedrr.drr_u.drr_begin;
167	struct drr_end *drre = &thedrr.drr_u.drr_end;
168	struct drr_object *drro = &thedrr.drr_u.drr_object;
169	struct drr_freeobjects *drrfo = &thedrr.drr_u.drr_freeobjects;
170	struct drr_write *drrw = &thedrr.drr_u.drr_write;
171	struct drr_write_byref *drrwbr = &thedrr.drr_u.drr_write_byref;
172	struct drr_free *drrf = &thedrr.drr_u.drr_free;
173	struct drr_spill *drrs = &thedrr.drr_u.drr_spill;
174	struct drr_write_embedded *drrwe = &thedrr.drr_u.drr_write_embedded;
175	char c;
176	boolean_t verbose = B_FALSE;
177	boolean_t first = B_TRUE;
178	/*
179	 * dump flag controls whether the contents of any modified data blocks
180	 * are printed to the console during processing of the stream. Warning:
181	 * for large streams, this can obviously lead to massive prints.
182	 */
183	boolean_t dump = B_FALSE;
184	int err;
185	zio_cksum_t zc = { 0 };
186	zio_cksum_t pcksum = { 0 };
187
188	while ((c = getopt(argc, argv, ":vCd")) != -1) {
189		switch (c) {
190		case 'C':
191			do_cksum = B_FALSE;
192			break;
193		case 'v':
194			verbose = B_TRUE;
195			break;
196		case 'd':
197			dump = B_TRUE;
198			verbose = B_TRUE;
199			break;
200		case ':':
201			(void) fprintf(stderr,
202			    "missing argument for '%c' option\n", optopt);
203			usage();
204			break;
205		case '?':
206			(void) fprintf(stderr, "invalid option '%c'\n",
207			    optopt);
208			usage();
209		}
210	}
211
212	if (isatty(STDIN_FILENO)) {
213		(void) fprintf(stderr,
214		    "Error: Backup stream can not be read "
215		    "from a terminal.\n"
216		    "You must redirect standard input.\n");
217		exit(1);
218	}
219
220	send_stream = stdin;
221	pcksum = zc;
222	while (ssread(drr, sizeof (dmu_replay_record_t), &zc)) {
223
224		/*
225		 * If this is the first DMU record being processed, check for
226		 * the magic bytes and figure out the endian-ness based on them.
227		 */
228		if (first) {
229			if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
230				do_byteswap = B_TRUE;
231				if (do_cksum) {
232					ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0);
233					/*
234					 * recalculate header checksum now
235					 * that we know it needs to be
236					 * byteswapped.
237					 */
238					fletcher_4_incremental_byteswap(drr,
239					    sizeof (dmu_replay_record_t), &zc);
240				}
241			} else if (drrb->drr_magic != DMU_BACKUP_MAGIC) {
242				(void) fprintf(stderr, "Invalid stream "
243				    "(bad magic number)\n");
244				exit(1);
245			}
246			first = B_FALSE;
247		}
248		if (do_byteswap) {
249			drr->drr_type = BSWAP_32(drr->drr_type);
250			drr->drr_payloadlen =
251			    BSWAP_32(drr->drr_payloadlen);
252		}
253
254		/*
255		 * At this point, the leading fields of the replay record
256		 * (drr_type and drr_payloadlen) have been byte-swapped if
257		 * necessary, but the rest of the data structure (the
258		 * union of type-specific structures) is still in its
259		 * original state.
260		 */
261		if (drr->drr_type >= DRR_NUMTYPES) {
262			(void) printf("INVALID record found: type 0x%x\n",
263			    drr->drr_type);
264			(void) printf("Aborting.\n");
265			exit(1);
266		}
267
268		drr_record_count[drr->drr_type]++;
269		total_records++;
270
271		switch (drr->drr_type) {
272		case DRR_BEGIN:
273			if (do_byteswap) {
274				drrb->drr_magic = BSWAP_64(drrb->drr_magic);
275				drrb->drr_versioninfo =
276				    BSWAP_64(drrb->drr_versioninfo);
277				drrb->drr_creation_time =
278				    BSWAP_64(drrb->drr_creation_time);
279				drrb->drr_type = BSWAP_32(drrb->drr_type);
280				drrb->drr_flags = BSWAP_32(drrb->drr_flags);
281				drrb->drr_toguid = BSWAP_64(drrb->drr_toguid);
282				drrb->drr_fromguid =
283				    BSWAP_64(drrb->drr_fromguid);
284			}
285
286			(void) printf("BEGIN record\n");
287			(void) printf("\thdrtype = %lld\n",
288			    DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo));
289			(void) printf("\tfeatures = %llx\n",
290			    DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo));
291			(void) printf("\tmagic = %llx\n",
292			    (u_longlong_t)drrb->drr_magic);
293			(void) printf("\tcreation_time = %llx\n",
294			    (u_longlong_t)drrb->drr_creation_time);
295			(void) printf("\ttype = %u\n", drrb->drr_type);
296			(void) printf("\tflags = 0x%x\n", drrb->drr_flags);
297			(void) printf("\ttoguid = %llx\n",
298			    (u_longlong_t)drrb->drr_toguid);
299			(void) printf("\tfromguid = %llx\n",
300			    (u_longlong_t)drrb->drr_fromguid);
301			(void) printf("\ttoname = %s\n", drrb->drr_toname);
302			if (verbose)
303				(void) printf("\n");
304
305			if ((DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo) ==
306			    DMU_COMPOUNDSTREAM) && drr->drr_payloadlen != 0) {
307				nvlist_t *nv;
308				int sz = drr->drr_payloadlen;
309
310				if (sz > INITIAL_BUFLEN) {
311					free(buf);
312					buf = malloc(sz);
313				}
314				(void) ssread(buf, sz, &zc);
315				if (ferror(send_stream))
316					perror("fread");
317				err = nvlist_unpack(buf, sz, &nv, 0);
318				if (err)
319					perror(strerror(err));
320				nvlist_print(stdout, nv);
321				nvlist_free(nv);
322			}
323			break;
324
325		case DRR_END:
326			if (do_byteswap) {
327				drre->drr_checksum.zc_word[0] =
328				    BSWAP_64(drre->drr_checksum.zc_word[0]);
329				drre->drr_checksum.zc_word[1] =
330				    BSWAP_64(drre->drr_checksum.zc_word[1]);
331				drre->drr_checksum.zc_word[2] =
332				    BSWAP_64(drre->drr_checksum.zc_word[2]);
333				drre->drr_checksum.zc_word[3] =
334				    BSWAP_64(drre->drr_checksum.zc_word[3]);
335			}
336			/*
337			 * We compare against the *previous* checksum
338			 * value, because the stored checksum is of
339			 * everything before the DRR_END record.
340			 */
341			if (do_cksum && !ZIO_CHECKSUM_EQUAL(drre->drr_checksum,
342			    pcksum)) {
343				(void) printf("Expected checksum differs from "
344				    "checksum in stream.\n");
345				(void) printf("Expected checksum = "
346				    "%llx/%llx/%llx/%llx\n",
347				    pcksum.zc_word[0],
348				    pcksum.zc_word[1],
349				    pcksum.zc_word[2],
350				    pcksum.zc_word[3]);
351			}
352			(void) printf("END checksum = %llx/%llx/%llx/%llx\n",
353			    drre->drr_checksum.zc_word[0],
354			    drre->drr_checksum.zc_word[1],
355			    drre->drr_checksum.zc_word[2],
356			    drre->drr_checksum.zc_word[3]);
357
358			ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0);
359			break;
360
361		case DRR_OBJECT:
362			if (do_byteswap) {
363				drro->drr_object = BSWAP_64(drro->drr_object);
364				drro->drr_type = BSWAP_32(drro->drr_type);
365				drro->drr_bonustype =
366				    BSWAP_32(drro->drr_bonustype);
367				drro->drr_blksz = BSWAP_32(drro->drr_blksz);
368				drro->drr_bonuslen =
369				    BSWAP_32(drro->drr_bonuslen);
370				drro->drr_toguid = BSWAP_64(drro->drr_toguid);
371			}
372			if (verbose) {
373				(void) printf("OBJECT object = %llu type = %u "
374				    "bonustype = %u blksz = %u bonuslen = %u\n",
375				    (u_longlong_t)drro->drr_object,
376				    drro->drr_type,
377				    drro->drr_bonustype,
378				    drro->drr_blksz,
379				    drro->drr_bonuslen);
380			}
381			if (drro->drr_bonuslen > 0) {
382				(void) ssread(buf,
383				    P2ROUNDUP(drro->drr_bonuslen, 8), &zc);
384				if (dump) {
385					print_block(buf,
386					    P2ROUNDUP(drro->drr_bonuslen, 8));
387				}
388			}
389			break;
390
391		case DRR_FREEOBJECTS:
392			if (do_byteswap) {
393				drrfo->drr_firstobj =
394				    BSWAP_64(drrfo->drr_firstobj);
395				drrfo->drr_numobjs =
396				    BSWAP_64(drrfo->drr_numobjs);
397				drrfo->drr_toguid = BSWAP_64(drrfo->drr_toguid);
398			}
399			if (verbose) {
400				(void) printf("FREEOBJECTS firstobj = %llu "
401				    "numobjs = %llu\n",
402				    (u_longlong_t)drrfo->drr_firstobj,
403				    (u_longlong_t)drrfo->drr_numobjs);
404			}
405			break;
406
407		case DRR_WRITE:
408			if (do_byteswap) {
409				drrw->drr_object = BSWAP_64(drrw->drr_object);
410				drrw->drr_type = BSWAP_32(drrw->drr_type);
411				drrw->drr_offset = BSWAP_64(drrw->drr_offset);
412				drrw->drr_length = BSWAP_64(drrw->drr_length);
413				drrw->drr_toguid = BSWAP_64(drrw->drr_toguid);
414				drrw->drr_key.ddk_prop =
415				    BSWAP_64(drrw->drr_key.ddk_prop);
416			}
417			/*
418			 * If this is verbose and/or dump output,
419			 * print info on the modified block
420			 */
421			if (verbose) {
422				(void) printf("WRITE object = %llu type = %u "
423				    "checksum type = %u\n"
424				    "offset = %llu length = %llu "
425				    "props = %llx\n",
426				    (u_longlong_t)drrw->drr_object,
427				    drrw->drr_type,
428				    drrw->drr_checksumtype,
429				    (u_longlong_t)drrw->drr_offset,
430				    (u_longlong_t)drrw->drr_length,
431				    (u_longlong_t)drrw->drr_key.ddk_prop);
432			}
433			/*
434			 * Read the contents of the block in from STDIN to buf
435			 */
436			(void) ssread(buf, drrw->drr_length, &zc);
437			/*
438			 * If in dump mode
439			 */
440			if (dump) {
441				print_block(buf, drrw->drr_length);
442			}
443			total_write_size += drrw->drr_length;
444			break;
445
446		case DRR_WRITE_BYREF:
447			if (do_byteswap) {
448				drrwbr->drr_object =
449				    BSWAP_64(drrwbr->drr_object);
450				drrwbr->drr_offset =
451				    BSWAP_64(drrwbr->drr_offset);
452				drrwbr->drr_length =
453				    BSWAP_64(drrwbr->drr_length);
454				drrwbr->drr_toguid =
455				    BSWAP_64(drrwbr->drr_toguid);
456				drrwbr->drr_refguid =
457				    BSWAP_64(drrwbr->drr_refguid);
458				drrwbr->drr_refobject =
459				    BSWAP_64(drrwbr->drr_refobject);
460				drrwbr->drr_refoffset =
461				    BSWAP_64(drrwbr->drr_refoffset);
462				drrwbr->drr_key.ddk_prop =
463				    BSWAP_64(drrwbr->drr_key.ddk_prop);
464			}
465			if (verbose) {
466				(void) printf("WRITE_BYREF object = %llu "
467				    "checksum type = %u props = %llx\n"
468				    "offset = %llu length = %llu\n"
469				    "toguid = %llx refguid = %llx\n"
470				    "refobject = %llu refoffset = %llu\n",
471				    (u_longlong_t)drrwbr->drr_object,
472				    drrwbr->drr_checksumtype,
473				    (u_longlong_t)drrwbr->drr_key.ddk_prop,
474				    (u_longlong_t)drrwbr->drr_offset,
475				    (u_longlong_t)drrwbr->drr_length,
476				    (u_longlong_t)drrwbr->drr_toguid,
477				    (u_longlong_t)drrwbr->drr_refguid,
478				    (u_longlong_t)drrwbr->drr_refobject,
479				    (u_longlong_t)drrwbr->drr_refoffset);
480			}
481			break;
482
483		case DRR_FREE:
484			if (do_byteswap) {
485				drrf->drr_object = BSWAP_64(drrf->drr_object);
486				drrf->drr_offset = BSWAP_64(drrf->drr_offset);
487				drrf->drr_length = BSWAP_64(drrf->drr_length);
488			}
489			if (verbose) {
490				(void) printf("FREE object = %llu "
491				    "offset = %llu length = %lld\n",
492				    (u_longlong_t)drrf->drr_object,
493				    (u_longlong_t)drrf->drr_offset,
494				    (longlong_t)drrf->drr_length);
495			}
496			break;
497		case DRR_SPILL:
498			if (do_byteswap) {
499				drrs->drr_object = BSWAP_64(drrs->drr_object);
500				drrs->drr_length = BSWAP_64(drrs->drr_length);
501			}
502			if (verbose) {
503				(void) printf("SPILL block for object = %llu "
504				    "length = %llu\n", drrs->drr_object,
505				    drrs->drr_length);
506			}
507			(void) ssread(buf, drrs->drr_length, &zc);
508			if (dump) {
509				print_block(buf, drrs->drr_length);
510			}
511			break;
512		case DRR_WRITE_EMBEDDED:
513			if (do_byteswap) {
514				drrwe->drr_object =
515				    BSWAP_64(drrwe->drr_object);
516				drrwe->drr_offset =
517				    BSWAP_64(drrwe->drr_offset);
518				drrwe->drr_length =
519				    BSWAP_64(drrwe->drr_length);
520				drrwe->drr_toguid =
521				    BSWAP_64(drrwe->drr_toguid);
522				drrwe->drr_lsize =
523				    BSWAP_32(drrwe->drr_lsize);
524				drrwe->drr_psize =
525				    BSWAP_32(drrwe->drr_psize);
526			}
527			if (verbose) {
528				(void) printf("WRITE_EMBEDDED object = %llu "
529				    "offset = %llu length = %llu\n"
530				    "toguid = %llx comp = %u etype = %u "
531				    "lsize = %u psize = %u\n",
532				    (u_longlong_t)drrwe->drr_object,
533				    (u_longlong_t)drrwe->drr_offset,
534				    (u_longlong_t)drrwe->drr_length,
535				    (u_longlong_t)drrwe->drr_toguid,
536				    drrwe->drr_compression,
537				    drrwe->drr_etype,
538				    drrwe->drr_lsize,
539				    drrwe->drr_psize);
540			}
541			(void) ssread(buf,
542			    P2ROUNDUP(drrwe->drr_psize, 8), &zc);
543			break;
544		}
545		pcksum = zc;
546	}
547	free(buf);
548
549	/* Print final summary */
550
551	(void) printf("SUMMARY:\n");
552	(void) printf("\tTotal DRR_BEGIN records = %lld\n",
553	    (u_longlong_t)drr_record_count[DRR_BEGIN]);
554	(void) printf("\tTotal DRR_END records = %lld\n",
555	    (u_longlong_t)drr_record_count[DRR_END]);
556	(void) printf("\tTotal DRR_OBJECT records = %lld\n",
557	    (u_longlong_t)drr_record_count[DRR_OBJECT]);
558	(void) printf("\tTotal DRR_FREEOBJECTS records = %lld\n",
559	    (u_longlong_t)drr_record_count[DRR_FREEOBJECTS]);
560	(void) printf("\tTotal DRR_WRITE records = %lld\n",
561	    (u_longlong_t)drr_record_count[DRR_WRITE]);
562	(void) printf("\tTotal DRR_WRITE_BYREF records = %lld\n",
563	    (u_longlong_t)drr_record_count[DRR_WRITE_BYREF]);
564	(void) printf("\tTotal DRR_WRITE_EMBEDDED records = %lld\n",
565	    (u_longlong_t)drr_record_count[DRR_WRITE_EMBEDDED]);
566	(void) printf("\tTotal DRR_FREE records = %lld\n",
567	    (u_longlong_t)drr_record_count[DRR_FREE]);
568	(void) printf("\tTotal DRR_SPILL records = %lld\n",
569	    (u_longlong_t)drr_record_count[DRR_SPILL]);
570	(void) printf("\tTotal records = %lld\n",
571	    (u_longlong_t)total_records);
572	(void) printf("\tTotal write size = %lld (0x%llx)\n",
573	    (u_longlong_t)total_write_size, (u_longlong_t)total_write_size);
574	(void) printf("\tTotal stream length = %lld (0x%llx)\n",
575	    (u_longlong_t)total_stream_len, (u_longlong_t)total_stream_len);
576	return (0);
577}
578