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