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