1 /*- 2 * SPDX-License-Identifier: Beerware 3 * 4 * ---------------------------------------------------------------------------- 5 * "THE BEER-WARE LICENSE" (Revision 42): 6 * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you 7 * can do whatever you want with this stuff. If we meet some day, and you think 8 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 9 * ---------------------------------------------------------------------------- 10 */ 11 12 #include <sys/param.h> 13 #include <sys/queue.h> 14 #include <sys/disk.h> 15 #include <sys/stat.h> 16 17 #include <assert.h> 18 #include <err.h> 19 #include <errno.h> 20 #include <math.h> 21 #include <fcntl.h> 22 #include <signal.h> 23 #include <stdint.h> 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <termios.h> 28 #include <time.h> 29 #include <unistd.h> 30 31 /* 32 * This is a compromise between speed and wasted effort 33 */ 34 #define COMPROMISE_SIZE (128<<10) 35 36 struct lump { 37 uint64_t start; 38 uint64_t len; 39 unsigned pass; 40 TAILQ_ENTRY(lump) list; 41 }; 42 43 struct period { 44 time_t t0; 45 time_t t1; 46 char str[20]; 47 uint64_t bytes_read; 48 TAILQ_ENTRY(period) list; 49 }; 50 TAILQ_HEAD(period_head, period); 51 52 static volatile sig_atomic_t aborting = 0; 53 static int verbose = 0; 54 static uint64_t big_read; 55 static uint64_t medium_read; 56 static uint64_t small_read; 57 static uint64_t total_size; 58 static uint64_t done_size; 59 static uint64_t wasted_size; 60 static char *input; 61 static char *write_worklist_file = NULL; 62 static char *read_worklist_file = NULL; 63 static const char *unreadable_pattern = "_UNREAD_"; 64 static int write_errors_are_fatal = 1; 65 static int read_fd, write_fd; 66 static FILE *log_file = NULL; 67 static char *work_buf; 68 static char *pattern_buf; 69 static double error_pause; 70 static double interval; 71 72 static unsigned nlumps; 73 static double n_reads, n_good_reads; 74 static time_t t_first; 75 static TAILQ_HEAD(, lump) lumps = TAILQ_HEAD_INITIALIZER(lumps); 76 static struct period_head minute = TAILQ_HEAD_INITIALIZER(minute); 77 static struct period_head quarter = TAILQ_HEAD_INITIALIZER(quarter); 78 static struct period_head hour = TAILQ_HEAD_INITIALIZER(quarter); 79 static struct period_head day = TAILQ_HEAD_INITIALIZER(quarter); 80 81 /**********************************************************************/ 82 83 static void 84 account_good_read_period(time_t now, uint64_t bytes, 85 struct period_head *ph, time_t dt) 86 { 87 struct period *pp; 88 const char *fmt; 89 struct tm tm1; 90 91 pp = TAILQ_FIRST(ph); 92 if (pp == NULL || pp->t1 < now) { 93 pp = calloc(1UL, sizeof(*pp)); 94 assert(pp != NULL); 95 pp->t0 = (now / dt) * dt; 96 pp->t1 = (now / dt + 1) * dt; 97 assert(localtime_r(&pp->t0, &tm1) != NULL); 98 if (dt < 86400) 99 fmt = "%H:%M"; 100 else 101 fmt = "%d%b"; 102 assert(strftime(pp->str, sizeof pp->str, fmt, &tm1) != 0); 103 TAILQ_INSERT_HEAD(ph, pp, list); 104 } 105 pp->bytes_read += bytes; 106 } 107 108 static void 109 account_good_read(time_t now, uint64_t bytes) 110 { 111 112 account_good_read_period(now, bytes, &minute, 60L); 113 account_good_read_period(now, bytes, &quarter, 900L); 114 account_good_read_period(now, bytes, &hour, 3600L); 115 account_good_read_period(now, bytes, &day, 86400L); 116 } 117 118 static void 119 report_one_period(const char *period, struct period_head *ph) 120 { 121 struct period *pp; 122 int n; 123 124 n = 0; 125 printf("%s ", period); 126 TAILQ_FOREACH(pp, ph, list) { 127 if (++n == 4) { 128 TAILQ_REMOVE(ph, pp, list); 129 free(pp); 130 break; 131 } 132 printf("\xe2\x94\x82 %s %14ju ", 133 pp->str, (uintmax_t)pp->bytes_read); 134 } 135 for (; n < 3; n++) { 136 printf("\xe2\x94\x82 %5s %14s ", "", ""); 137 } 138 printf("\x1b[K\n"); 139 } 140 141 static void 142 report_periods(void) 143 { 144 report_one_period("1m ", &minute); 145 report_one_period("15m", &quarter); 146 report_one_period("1h ", &hour); 147 report_one_period("1d ", &day); 148 } 149 150 /**********************************************************************/ 151 152 static void 153 set_verbose(void) 154 { 155 156 verbose = 1; 157 } 158 159 static void 160 report_header(const char *term) 161 { 162 printf("%13s %7s %13s %5s %13s %13s %9s%s", 163 "start", 164 "size", 165 "block-len", 166 "pass", 167 "done", 168 "remaining", 169 "% done", 170 term 171 ); 172 } 173 174 #define REPORTWID 79 175 176 static void 177 report_hline(const char *how) 178 { 179 int j; 180 181 for (j = 0; j < REPORTWID; j++) { 182 if (how && (j == 4 || j == 29 || j == 54)) { 183 printf("%s", how); 184 } else { 185 printf("\xe2\x94\x80"); 186 } 187 } 188 printf("\x1b[K\n"); 189 } 190 191 static uint64_t hist[REPORTWID]; 192 static uint64_t prev_done = ~0UL; 193 194 static void 195 report_histogram(uint64_t start) 196 { 197 uint64_t j, bucket, fp, fe, k, now; 198 double a; 199 struct lump *lp2; 200 201 bucket = total_size / REPORTWID; 202 if (total_size > bucket * REPORTWID) 203 bucket += 1; 204 if (done_size != prev_done) { 205 memset(hist, 0, sizeof hist); 206 TAILQ_FOREACH(lp2, &lumps, list) { 207 fp = lp2->start; 208 fe = lp2->start + lp2->len; 209 for (j = fp / bucket; fp < fe; j++) { 210 k = (j + 1) * bucket; 211 if (k > fe) 212 k = fe; 213 k -= fp; 214 hist[j] += k; 215 fp += k; 216 } 217 } 218 prev_done = done_size; 219 } 220 now = start / bucket; 221 for (j = 0; j < REPORTWID; j++) { 222 a = round(8 * (double)hist[j] / bucket); 223 assert (a >= 0 && a < 9); 224 if (a == 0 && hist[j]) 225 a = 1; 226 if (j == now) 227 printf("\x1b[31m"); 228 if (a == 0) { 229 putchar(' '); 230 } else { 231 putchar(0xe2); 232 putchar(0x96); 233 putchar(0x80 + (char)a); 234 } 235 if (j == now) 236 printf("\x1b[0m"); 237 } 238 putchar('\n'); 239 } 240 241 static void 242 report(uint64_t sz) 243 { 244 struct winsize wsz; 245 const struct lump *lp = TAILQ_FIRST(&lumps); 246 int j; 247 unsigned pass = 0; 248 uintmax_t start = 0, length = 0; 249 time_t t_now = time(NULL); 250 251 if (lp != NULL) { 252 pass = lp->pass; 253 start = lp->start; 254 length = lp->len; 255 } 256 257 if (verbose) { 258 printf("\x1b[H%s\x1b[K\n", input); 259 report_header("\x1b[K\n"); 260 } 261 262 printf("%13ju %7ju %13ju %5u %13ju %13ju %9.4f", 263 start, 264 (uintmax_t)sz, 265 length, 266 pass, 267 (uintmax_t)done_size, 268 (uintmax_t)(total_size - done_size), 269 100*(double)done_size/(double)total_size 270 ); 271 272 if (verbose) { 273 printf("\x1b[K\n"); 274 report_hline(NULL); 275 report_histogram(start); 276 if (TAILQ_EMPTY(&minute)) { 277 report_hline(NULL); 278 } else { 279 report_hline("\xe2\x94\xac"); 280 report_periods(); 281 report_hline("\xe2\x94\xb4"); 282 } 283 printf("Missing: %u", nlumps); 284 printf(" Success: %.0f/%.0f =", n_good_reads, n_reads); 285 printf(" %.4f%%", 100 * n_good_reads / n_reads); 286 printf(" Duration: %.3fs", (t_now - t_first) / n_reads); 287 printf("\x1b[K\n"); 288 report_hline(NULL); 289 j = ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz); 290 if (!j) 291 printf("\x1b[%d;1H", wsz.ws_row); 292 } else { 293 printf("\n"); 294 } 295 } 296 297 /**********************************************************************/ 298 299 static void 300 new_lump(uint64_t start, uint64_t len, unsigned pass) 301 { 302 struct lump *lp; 303 304 assert(len > 0); 305 lp = malloc(sizeof *lp); 306 if (lp == NULL) 307 err(1, "Malloc failed"); 308 lp->start = start; 309 lp->len = len; 310 lp->pass = pass; 311 TAILQ_INSERT_TAIL(&lumps, lp, list); 312 nlumps += 1; 313 } 314 315 /********************************************************************** 316 * Save the worklist if -w was given 317 */ 318 319 static void 320 save_worklist(void) 321 { 322 FILE *file; 323 struct lump *llp; 324 char buf[PATH_MAX]; 325 326 if (write_fd >= 0 && fdatasync(write_fd)) 327 err(1, "Write error, probably disk full"); 328 329 if (write_worklist_file != NULL) { 330 snprintf(buf, sizeof(buf), "%s.tmp", write_worklist_file); 331 fprintf(stderr, "\nSaving worklist ..."); 332 333 file = fopen(buf, "w"); 334 if (file == NULL) 335 err(1, "Error opening file %s", buf); 336 337 TAILQ_FOREACH(llp, &lumps, list) { 338 assert (llp->len > 0); 339 fprintf(file, "%ju %ju %u\n", 340 (uintmax_t)llp->start, 341 (uintmax_t)llp->len, 342 llp->pass); 343 } 344 fflush(file); 345 if (ferror(file) || fdatasync(fileno(file)) || fclose(file)) 346 err(1, "Error writing file %s", buf); 347 if (rename(buf, write_worklist_file)) 348 err(1, "Error renaming %s to %s", 349 buf, write_worklist_file); 350 fprintf(stderr, " done.\n"); 351 } 352 } 353 354 /* Read the worklist if -r was given */ 355 static uint64_t 356 read_worklist(void) 357 { 358 uintmax_t start, length; 359 uint64_t missing = 0; 360 unsigned pass, lines; 361 FILE *file; 362 363 fprintf(stderr, "Reading worklist ..."); 364 file = fopen(read_worklist_file, "r"); 365 if (file == NULL) 366 err(1, "Error opening file %s", read_worklist_file); 367 368 lines = 0; 369 for (;;) { 370 ++lines; 371 if (3 != fscanf(file, "%ju %ju %u\n", &start, &length, &pass)) { 372 if (!feof(file)) 373 err(1, "Error parsing file %s at line %u", 374 read_worklist_file, lines); 375 else 376 break; 377 } 378 if (length > 0) { 379 new_lump(start, length, pass); 380 missing += length; 381 } 382 } 383 if (fclose(file)) 384 err(1, "Error closing file %s", read_worklist_file); 385 fprintf(stderr, " done.\n"); 386 /* 387 * Return the number of bytes outstanding 388 */ 389 return (missing); 390 } 391 392 /**********************************************************************/ 393 394 static void 395 write_buf(int fd, const void *buf, uint64_t length, uint64_t where) 396 { 397 int64_t i; 398 399 i = pwrite(fd, buf, length, (off_t)where); 400 if (i > 0 && (uint64_t)i == length) 401 return; 402 403 printf("\nWrite error at %ju/%ju: %jd (%s)\n", 404 (uintmax_t)where, 405 (uintmax_t)length, 406 (intmax_t)i, strerror(errno)); 407 save_worklist(); 408 if (write_errors_are_fatal) 409 exit(3); 410 } 411 412 static void 413 fill_buf(char *buf, int64_t len, const char *pattern) 414 { 415 int64_t sz = strlen(pattern); 416 int64_t i; 417 418 for (i = 0; i < len; i += sz) { 419 memcpy(buf + i, pattern, MIN(len - i, sz)); 420 } 421 } 422 423 /**********************************************************************/ 424 425 static void 426 usage(void) 427 { 428 fprintf(stderr, "usage: recoverdisk " 429 "[-b big_read] [-i interval ] [-r readlist] " 430 "[-s interval] [-w writelist] source [destination]\n"); 431 /* XXX update */ 432 exit(1); 433 } 434 435 static void 436 sighandler(int sig) 437 { 438 439 (void)sig; 440 aborting = 1; 441 } 442 443 /**********************************************************************/ 444 445 static int64_t 446 attempt_one_lump(time_t t_now) 447 { 448 struct lump *lp; 449 uint64_t sz; 450 int64_t retval; 451 int error; 452 453 lp = TAILQ_FIRST(&lumps); 454 if (lp == NULL) 455 return(0); 456 457 if (lp->pass == 0) { 458 sz = MIN(lp->len, big_read); 459 } else if (lp->pass == 1) { 460 sz = MIN(lp->len, medium_read); 461 } else { 462 sz = MIN(lp->len, small_read); 463 } 464 465 assert(sz != 0); 466 467 n_reads += 1; 468 retval = pread(read_fd, work_buf, sz, lp->start); 469 470 #if 0 /* enable this when testing */ 471 if (!(random() & 0xf)) { 472 retval = -1; 473 errno = EIO; 474 usleep(20000); 475 } else { 476 usleep(2000); 477 } 478 #endif 479 480 error = errno; 481 if (retval > 0) { 482 n_good_reads += 1; 483 sz = retval; 484 done_size += sz; 485 if (write_fd >= 0) { 486 write_buf(write_fd, work_buf, sz, lp->start); 487 } 488 if (log_file != NULL) { 489 fprintf(log_file, "%jd %ju %ju\n", 490 (intmax_t)t_now, 491 (uintmax_t)lp->start, 492 (uintmax_t)sz 493 ); 494 fflush(log_file); 495 } 496 } else { 497 wasted_size += sz; 498 printf("%14ju %7ju read error %d: (%s)", 499 (uintmax_t)lp->start, 500 (uintmax_t)sz, error, strerror(error)); 501 if (error_pause > 1) { 502 printf(" (Pausing %g s)", error_pause); 503 } 504 printf("\n"); 505 506 if (write_fd >= 0 && pattern_buf != NULL) { 507 write_buf(write_fd, pattern_buf, sz, lp->start); 508 } 509 new_lump(lp->start, sz, lp->pass + 1); 510 retval = -sz; 511 } 512 lp->start += sz; 513 lp->len -= sz; 514 if (lp->len == 0) { 515 TAILQ_REMOVE(&lumps, lp, list); 516 nlumps -= 1; 517 free(lp); 518 } 519 errno = error; 520 return (retval); 521 } 522 523 524 /**********************************************************************/ 525 526 static void 527 determine_total_size(void) 528 { 529 struct stat sb; 530 int error; 531 532 if (total_size != 0) 533 return; 534 535 error = fstat(read_fd, &sb); 536 if (error < 0) 537 err(1, "fstat failed"); 538 539 if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) { 540 #ifdef DIOCGMEDIASIZE 541 off_t mediasize; 542 error = ioctl(read_fd, DIOCGMEDIASIZE, &mediasize); 543 if (error == 0 && mediasize > 0) { 544 total_size = mediasize; 545 printf("# Got total_size from DIOCGMEDIASIZE: %ju\n", 546 (uintmax_t)total_size); 547 return; 548 } 549 #endif 550 } else if (S_ISREG(sb.st_mode) && sb.st_size > 0) { 551 total_size = sb.st_size; 552 printf("# Got total_size from stat(2): %ju\n", 553 (uintmax_t)total_size); 554 return; 555 } else { 556 errx(1, "Input must be device or regular file"); 557 } 558 fprintf(stderr, "Specify total size with -t option\n"); 559 exit(1); 560 } 561 562 static void 563 determine_read_sizes(void) 564 { 565 int error; 566 u_int sectorsize; 567 off_t stripesize; 568 569 #ifdef DIOCGSECTORSIZE 570 if (small_read == 0) { 571 error = ioctl(read_fd, DIOCGSECTORSIZE, §orsize); 572 if (error >= 0 && sectorsize > 0) { 573 small_read = sectorsize; 574 printf("# Got small_read from DIOCGSECTORSIZE: %ju\n", 575 (uintmax_t)small_read 576 ); 577 } 578 } 579 #endif 580 581 if (small_read == 0) { 582 small_read = 512; 583 printf("# Defaulting small_read to %ju\n", (uintmax_t)small_read); 584 } 585 586 if (medium_read && (medium_read % small_read)) { 587 errx(1, 588 "medium_read (%ju) is not a multiple of small_read (%ju)\n", 589 (uintmax_t)medium_read, (uintmax_t)small_read 590 ); 591 } 592 593 if (big_read != 0 && (big_read % small_read)) { 594 errx(1, 595 "big_read (%ju) is not a multiple of small_read (%ju)\n", 596 (uintmax_t)big_read, (uintmax_t)small_read 597 ); 598 } 599 600 #ifdef DIOCGSTRIPESIZE 601 if (medium_read == 0) { 602 error = ioctl(read_fd, DIOCGSTRIPESIZE, &stripesize); 603 if (error < 0 || stripesize <= 0) { 604 // nope 605 } else if ((uint64_t)stripesize < small_read) { 606 // nope 607 } else if (stripesize % small_read) { 608 // nope 609 } else if (stripesize <= COMPROMISE_SIZE) { 610 medium_read = stripesize; 611 printf("# Got medium_read from DIOCGSTRIPESIZE: %ju\n", 612 (uintmax_t)medium_read 613 ); 614 } 615 } 616 #endif 617 618 #if defined(DIOCGFWSECTORS) && defined(DIOCGFWHEADS) 619 if (medium_read == 0) { 620 u_int fwsectors = 0, fwheads = 0; 621 error = ioctl(read_fd, DIOCGFWSECTORS, &fwsectors); 622 if (error) 623 fwsectors = 0; 624 error = ioctl(read_fd, DIOCGFWHEADS, &fwheads); 625 if (error) 626 fwheads = 0; 627 if (fwsectors * fwheads * small_read <= COMPROMISE_SIZE) { 628 medium_read = fwsectors * fwheads * small_read; 629 printf( 630 "# Got medium_read from DIOCGFW{SECTORS*HEADS}: %ju\n", 631 (uintmax_t)medium_read 632 ); 633 } else if (fwsectors * small_read <= COMPROMISE_SIZE) { 634 medium_read = fwsectors * small_read; 635 printf( 636 "# Got medium_read from DIOCGFWSECTORS: %ju\n", 637 (uintmax_t)medium_read 638 ); 639 } 640 } 641 #endif 642 643 if (big_read == 0 && medium_read != 0) { 644 if (medium_read * 2 > COMPROMISE_SIZE) { 645 big_read = medium_read; 646 medium_read = 0; 647 } else { 648 big_read = COMPROMISE_SIZE; 649 big_read -= big_read % medium_read; 650 } 651 printf("# Got big_read from medium_read: %ju\n", 652 (uintmax_t)big_read 653 ); 654 } 655 656 if (big_read == 0) { 657 big_read = COMPROMISE_SIZE; 658 big_read -= big_read % small_read; 659 printf("# Defaulting big_read to %ju\n", 660 (uintmax_t)big_read 661 ); 662 } 663 664 if (medium_read >= big_read) 665 medium_read = 0; 666 667 if (medium_read == 0) { 668 /* 669 * We do not want to go directly to single sectors, but 670 * we also dont want to waste time doing multi-sector 671 * reads with high failure probability. 672 */ 673 uint64_t h = big_read; 674 uint64_t l = small_read; 675 while (h > l) { 676 h >>= 2; 677 l <<= 1; 678 } 679 medium_read = h; 680 printf("# Got medium_read from small_read & big_read: %ju\n", 681 (uintmax_t)medium_read 682 ); 683 } 684 printf("# Bigsize = %ju, medium_read = %ju, small_read = %ju\n", 685 (uintmax_t)big_read, (uintmax_t)medium_read, (uintmax_t)small_read); 686 687 assert(0 < small_read); 688 689 assert(0 < medium_read); 690 assert(medium_read >= small_read); 691 assert(medium_read <= big_read); 692 assert(medium_read % small_read == 0); 693 694 assert(0 < big_read); 695 assert(big_read >= medium_read); 696 assert(big_read % small_read == 0); 697 } 698 699 /**********************************************************************/ 700 701 static void 702 monitor_read_sizes(uint64_t failed_size) 703 { 704 705 if (failed_size == big_read && medium_read != small_read) { 706 if (n_reads < n_good_reads + 3) 707 return; 708 fprintf( 709 stderr, 710 "Too many failures for big reads." 711 " (%.0f bad of %.0f)" 712 " Shifting to medium_reads.\n", 713 n_reads - n_good_reads, n_reads 714 ); 715 big_read = medium_read; 716 medium_read = small_read; 717 wasted_size = 0; 718 return; 719 } 720 721 if (big_read > small_read && wasted_size / small_read > 200) { 722 fprintf( 723 stderr, 724 "Too much wasted effort." 725 " (%.0f bad of %.0f)" 726 " Shifting to small_reads.\n", 727 n_reads - n_good_reads, n_reads 728 ); 729 big_read = small_read; 730 medium_read = small_read; 731 return; 732 } 733 } 734 735 /**********************************************************************/ 736 737 int 738 main(int argc, char * const argv[]) 739 { 740 int ch; 741 int64_t sz; 742 int error; 743 time_t t_now, t_report, t_save; 744 time_t snapshot = 60, unsaved; 745 setbuf(stdout, NULL); 746 setbuf(stderr, NULL); 747 748 while ((ch = getopt(argc, argv, "b:i:l:p:m:r:w:s:t:u:v")) != -1) { 749 switch (ch) { 750 case 'b': 751 big_read = strtoul(optarg, NULL, 0); 752 break; 753 case 'i': 754 interval = strtod(optarg, NULL); 755 break; 756 case 'l': 757 log_file = fopen(optarg, "a"); 758 if (log_file == NULL) { 759 err(1, "Could not open logfile for append"); 760 } 761 break; 762 case 'p': 763 error_pause = strtod(optarg, NULL); 764 break; 765 case 'm': 766 medium_read = strtoul(optarg, NULL, 0); 767 break; 768 case 'r': 769 read_worklist_file = strdup(optarg); 770 if (read_worklist_file == NULL) 771 err(1, "Cannot allocate enough memory"); 772 break; 773 case 's': 774 small_read = strtoul(optarg, NULL, 0); 775 break; 776 case 't': 777 total_size = strtoul(optarg, NULL, 0); 778 break; 779 case 'u': 780 unreadable_pattern = optarg; 781 break; 782 case 'v': 783 set_verbose(); 784 break; 785 case 'w': 786 write_worklist_file = strdup(optarg); 787 if (write_worklist_file == NULL) 788 err(1, "Cannot allocate enough memory"); 789 break; 790 default: 791 usage(); 792 /* NOTREACHED */ 793 } 794 } 795 argc -= optind; 796 argv += optind; 797 798 if (argc < 1 || argc > 2) 799 usage(); 800 801 input = argv[0]; 802 read_fd = open(argv[0], O_RDONLY); 803 if (read_fd < 0) 804 err(1, "Cannot open read descriptor %s", argv[0]); 805 806 determine_total_size(); 807 808 determine_read_sizes(); 809 810 work_buf = malloc(big_read); 811 assert (work_buf != NULL); 812 813 if (argc > 1) { 814 write_fd = open(argv[1], O_WRONLY | O_CREAT, DEFFILEMODE); 815 if (write_fd < 0) 816 err(1, "Cannot open write descriptor %s", argv[1]); 817 if (ftruncate(write_fd, (off_t)total_size) < 0) 818 err(1, "Cannot truncate output %s to %ju bytes", 819 argv[1], (uintmax_t)total_size); 820 } else { 821 write_fd = -1; 822 } 823 824 if (strlen(unreadable_pattern)) { 825 pattern_buf = malloc(big_read); 826 assert(pattern_buf != NULL); 827 fill_buf(pattern_buf, big_read, unreadable_pattern); 828 } 829 830 if (read_worklist_file != NULL) { 831 done_size = total_size - read_worklist(); 832 } else { 833 new_lump(0UL, total_size, 0UL); 834 done_size = 0; 835 } 836 if (write_worklist_file != NULL) 837 signal(SIGINT, sighandler); 838 839 sz = 0; 840 if (!verbose) 841 report_header("\n"); 842 else 843 printf("\x1b[2J"); 844 845 t_first = time(NULL); 846 t_report = t_first; 847 t_save = t_first; 848 unsaved = 0; 849 while (!aborting) { 850 if (interval > 0) { 851 usleep((unsigned long)(1e6 * interval)); 852 } 853 t_now = time(NULL); 854 sz = attempt_one_lump(t_now); 855 error = errno; 856 857 if (sz == 0) { 858 break; 859 } 860 861 if (sz > 0) { 862 unsaved += 1; 863 } 864 if (unsaved && (t_save + snapshot) < t_now) { 865 save_worklist(); 866 unsaved = 0; 867 t_save = t_now; 868 if (!verbose) { 869 report_header("\n"); 870 t_report = t_now; 871 } 872 } 873 if (sz > 0) { 874 if (verbose) { 875 account_good_read(t_now, sz); 876 } 877 if (t_report != t_now) { 878 report(sz); 879 t_report = t_now; 880 } 881 continue; 882 } 883 884 monitor_read_sizes(-sz); 885 886 if (error == EINVAL) { 887 printf("Try with -b 131072 or lower ?\n"); 888 aborting = 1; 889 break; 890 } 891 if (error == ENXIO) { 892 printf("Input device probably detached...\n"); 893 aborting = 1; 894 break; 895 } 896 report(-sz); 897 t_report = t_now; 898 if (error_pause > 0) { 899 usleep((unsigned long)(1e6 * error_pause)); 900 } 901 } 902 save_worklist(); 903 free(work_buf); 904 if (pattern_buf != NULL) 905 free(pattern_buf); 906 printf("%s", aborting ? "Aborted\n" : "Completed\n"); 907 report(0UL); 908 return (0); // XXX 909 } 910