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