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 52 uint64_t total_write_size = 0; 53 uint64_t total_stream_len = 0; 54 FILE *send_stream = 0; 55 boolean_t do_byteswap = B_FALSE; 56 boolean_t do_cksum = B_TRUE; 57 #define INITIAL_BUFLEN (1<<20) 58 59 static void 60 usage(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 76 static size_t 77 ssread(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 */ 97 static void 98 print_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 */ 117 static void 118 print_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 158 int 159 main(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