1fd64dc9aSMaxim Konovalov /*-
264de3fddSPedro F. Giffuni * SPDX-License-Identifier: Beerware
364de3fddSPedro F. Giffuni *
439d969aeSPoul-Henning Kamp * ----------------------------------------------------------------------------
539d969aeSPoul-Henning Kamp * "THE BEER-WARE LICENSE" (Revision 42):
639d969aeSPoul-Henning Kamp * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
739d969aeSPoul-Henning Kamp * can do whatever you want with this stuff. If we meet some day, and you think
839d969aeSPoul-Henning Kamp * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
939d969aeSPoul-Henning Kamp * ----------------------------------------------------------------------------
1039d969aeSPoul-Henning Kamp */
11fd64dc9aSMaxim Konovalov #include <sys/param.h>
1239d969aeSPoul-Henning Kamp #include <sys/queue.h>
1339d969aeSPoul-Henning Kamp #include <sys/disk.h>
14db45c56dSMaxim Sobolev #include <sys/stat.h>
1539d969aeSPoul-Henning Kamp
169546e08eSPoul-Henning Kamp #include <assert.h>
17fd64dc9aSMaxim Konovalov #include <err.h>
18fd64dc9aSMaxim Konovalov #include <errno.h>
199546e08eSPoul-Henning Kamp #include <math.h>
20fd64dc9aSMaxim Konovalov #include <fcntl.h>
21fd64dc9aSMaxim Konovalov #include <signal.h>
22fd64dc9aSMaxim Konovalov #include <stdint.h>
23fd64dc9aSMaxim Konovalov #include <stdio.h>
24fd64dc9aSMaxim Konovalov #include <stdlib.h>
25fd64dc9aSMaxim Konovalov #include <string.h>
269546e08eSPoul-Henning Kamp #include <termios.h>
27fd64dc9aSMaxim Konovalov #include <time.h>
28fd64dc9aSMaxim Konovalov #include <unistd.h>
29fd64dc9aSMaxim Konovalov
309546e08eSPoul-Henning Kamp /* Safe printf into a fixed-size buffer */
319546e08eSPoul-Henning Kamp #define bprintf(buf, fmt, ...) \
329546e08eSPoul-Henning Kamp do { \
339546e08eSPoul-Henning Kamp int ibprintf; \
349546e08eSPoul-Henning Kamp ibprintf = snprintf(buf, sizeof buf, fmt, __VA_ARGS__); \
359546e08eSPoul-Henning Kamp assert(ibprintf >= 0 && ibprintf < (int)sizeof buf); \
369546e08eSPoul-Henning Kamp } while (0)
3739d969aeSPoul-Henning Kamp
3839d969aeSPoul-Henning Kamp struct lump {
3939d969aeSPoul-Henning Kamp off_t start;
4039d969aeSPoul-Henning Kamp off_t len;
4139d969aeSPoul-Henning Kamp int state;
4239d969aeSPoul-Henning Kamp TAILQ_ENTRY(lump) list;
4339d969aeSPoul-Henning Kamp };
4439d969aeSPoul-Henning Kamp
459546e08eSPoul-Henning Kamp struct period {
469546e08eSPoul-Henning Kamp time_t t0;
479546e08eSPoul-Henning Kamp time_t t1;
489546e08eSPoul-Henning Kamp char str[20];
499546e08eSPoul-Henning Kamp off_t bytes_read;
509546e08eSPoul-Henning Kamp TAILQ_ENTRY(period) list;
519546e08eSPoul-Henning Kamp };
529546e08eSPoul-Henning Kamp TAILQ_HEAD(period_head, period);
539546e08eSPoul-Henning Kamp
549546e08eSPoul-Henning Kamp static volatile sig_atomic_t aborting = 0;
559546e08eSPoul-Henning Kamp static int verbose = 0;
569546e08eSPoul-Henning Kamp static size_t bigsize = 1024 * 1024;
579546e08eSPoul-Henning Kamp static size_t medsize;
589546e08eSPoul-Henning Kamp static size_t minsize = 512;
599546e08eSPoul-Henning Kamp static off_t tot_size;
609546e08eSPoul-Henning Kamp static off_t done_size;
619546e08eSPoul-Henning Kamp static char *input;
629546e08eSPoul-Henning Kamp static char *wworklist = NULL;
639546e08eSPoul-Henning Kamp static char *rworklist = NULL;
649546e08eSPoul-Henning Kamp static const char *unreadable_pattern = "_UNREAD_";
659546e08eSPoul-Henning Kamp static const int write_errors_are_fatal = 1;
669546e08eSPoul-Henning Kamp static int fdr, fdw;
679546e08eSPoul-Henning Kamp
6839d969aeSPoul-Henning Kamp static TAILQ_HEAD(, lump) lumps = TAILQ_HEAD_INITIALIZER(lumps);
699546e08eSPoul-Henning Kamp static struct period_head minute = TAILQ_HEAD_INITIALIZER(minute);
709546e08eSPoul-Henning Kamp static struct period_head quarter = TAILQ_HEAD_INITIALIZER(quarter);
719546e08eSPoul-Henning Kamp static struct period_head hour = TAILQ_HEAD_INITIALIZER(quarter);
729546e08eSPoul-Henning Kamp static struct period_head day = TAILQ_HEAD_INITIALIZER(quarter);
739546e08eSPoul-Henning Kamp
749546e08eSPoul-Henning Kamp /**********************************************************************/
759546e08eSPoul-Henning Kamp
769546e08eSPoul-Henning Kamp static void
report_good_read2(time_t now,size_t bytes,struct period_head * ph,time_t dt)779546e08eSPoul-Henning Kamp report_good_read2(time_t now, size_t bytes, struct period_head *ph, time_t dt)
789546e08eSPoul-Henning Kamp {
799546e08eSPoul-Henning Kamp struct period *pp;
809546e08eSPoul-Henning Kamp const char *fmt;
819546e08eSPoul-Henning Kamp struct tm tm1;
829546e08eSPoul-Henning Kamp
839546e08eSPoul-Henning Kamp pp = TAILQ_FIRST(ph);
849546e08eSPoul-Henning Kamp if (pp == NULL || pp->t1 < now) {
856991cb36SJohn Baldwin pp = calloc(1, sizeof(*pp));
869546e08eSPoul-Henning Kamp assert(pp != NULL);
879546e08eSPoul-Henning Kamp pp->t0 = (now / dt) * dt;
889546e08eSPoul-Henning Kamp pp->t1 = (now / dt + 1) * dt;
899546e08eSPoul-Henning Kamp assert(localtime_r(&pp->t0, &tm1) != NULL);
909546e08eSPoul-Henning Kamp if (dt < 86400)
919546e08eSPoul-Henning Kamp fmt = "%H:%M";
929546e08eSPoul-Henning Kamp else
939546e08eSPoul-Henning Kamp fmt = "%d%b";
949546e08eSPoul-Henning Kamp assert(strftime(pp->str, sizeof pp->str, fmt, &tm1) != 0);
959546e08eSPoul-Henning Kamp TAILQ_INSERT_HEAD(ph, pp, list);
969546e08eSPoul-Henning Kamp }
979546e08eSPoul-Henning Kamp pp->bytes_read += bytes;
989546e08eSPoul-Henning Kamp }
999546e08eSPoul-Henning Kamp
1009546e08eSPoul-Henning Kamp static void
report_good_read(time_t now,size_t bytes)1019546e08eSPoul-Henning Kamp report_good_read(time_t now, size_t bytes)
1029546e08eSPoul-Henning Kamp {
1039546e08eSPoul-Henning Kamp
1049546e08eSPoul-Henning Kamp report_good_read2(now, bytes, &minute, 60L);
1059546e08eSPoul-Henning Kamp report_good_read2(now, bytes, &quarter, 900L);
1069546e08eSPoul-Henning Kamp report_good_read2(now, bytes, &hour, 3600L);
1079546e08eSPoul-Henning Kamp report_good_read2(now, bytes, &day, 86400L);
1089546e08eSPoul-Henning Kamp }
1099546e08eSPoul-Henning Kamp
1109546e08eSPoul-Henning Kamp static void
report_one_period(const char * period,struct period_head * ph)1119546e08eSPoul-Henning Kamp report_one_period(const char *period, struct period_head *ph)
1129546e08eSPoul-Henning Kamp {
1139546e08eSPoul-Henning Kamp struct period *pp;
1149546e08eSPoul-Henning Kamp int n;
1159546e08eSPoul-Henning Kamp
1169546e08eSPoul-Henning Kamp n = 0;
1179546e08eSPoul-Henning Kamp printf("%s \xe2\x94\x82", period);
1189546e08eSPoul-Henning Kamp TAILQ_FOREACH(pp, ph, list) {
1199546e08eSPoul-Henning Kamp if (n == 3) {
1209546e08eSPoul-Henning Kamp TAILQ_REMOVE(ph, pp, list);
1219546e08eSPoul-Henning Kamp free(pp);
1229546e08eSPoul-Henning Kamp break;
1239546e08eSPoul-Henning Kamp }
1249546e08eSPoul-Henning Kamp if (n++)
1259546e08eSPoul-Henning Kamp printf(" \xe2\x94\x82");
1269546e08eSPoul-Henning Kamp printf(" %s %14jd", pp->str, pp->bytes_read);
1279546e08eSPoul-Henning Kamp }
1289546e08eSPoul-Henning Kamp for (; n < 3; n++) {
1299546e08eSPoul-Henning Kamp printf(" \xe2\x94\x82");
1309546e08eSPoul-Henning Kamp printf(" %5s %14s", "", "");
1319546e08eSPoul-Henning Kamp }
1329546e08eSPoul-Henning Kamp printf("\x1b[K\n");
1339546e08eSPoul-Henning Kamp }
1349546e08eSPoul-Henning Kamp
1359546e08eSPoul-Henning Kamp static void
report_periods(void)1369546e08eSPoul-Henning Kamp report_periods(void)
1379546e08eSPoul-Henning Kamp {
1389546e08eSPoul-Henning Kamp report_one_period("1m ", &minute);
1399546e08eSPoul-Henning Kamp report_one_period("15m", &quarter);
1409546e08eSPoul-Henning Kamp report_one_period("1h ", &hour);
1419546e08eSPoul-Henning Kamp report_one_period("1d ", &day);
1429546e08eSPoul-Henning Kamp }
1439546e08eSPoul-Henning Kamp
1449546e08eSPoul-Henning Kamp /**********************************************************************/
1459546e08eSPoul-Henning Kamp
1469546e08eSPoul-Henning Kamp static void
set_verbose(void)1479546e08eSPoul-Henning Kamp set_verbose(void)
1489546e08eSPoul-Henning Kamp {
1499546e08eSPoul-Henning Kamp struct winsize wsz;
1509546e08eSPoul-Henning Kamp
1519546e08eSPoul-Henning Kamp if (!isatty(STDIN_FILENO) || ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz))
1529546e08eSPoul-Henning Kamp return;
1539546e08eSPoul-Henning Kamp verbose = 1;
1549546e08eSPoul-Henning Kamp }
1559546e08eSPoul-Henning Kamp
1569546e08eSPoul-Henning Kamp static void
report_header(int eol)1579546e08eSPoul-Henning Kamp report_header(int eol)
1589546e08eSPoul-Henning Kamp {
1599546e08eSPoul-Henning Kamp printf("%13s %7s %13s %5s %13s %13s %9s",
1609546e08eSPoul-Henning Kamp "start",
1619546e08eSPoul-Henning Kamp "size",
1629546e08eSPoul-Henning Kamp "block-len",
1639546e08eSPoul-Henning Kamp "pass",
1649546e08eSPoul-Henning Kamp "done",
1659546e08eSPoul-Henning Kamp "remaining",
1669546e08eSPoul-Henning Kamp "% done");
1679546e08eSPoul-Henning Kamp if (eol)
1689546e08eSPoul-Henning Kamp printf("\x1b[K");
1699546e08eSPoul-Henning Kamp putchar('\n');
1709546e08eSPoul-Henning Kamp }
1719546e08eSPoul-Henning Kamp
1729546e08eSPoul-Henning Kamp #define REPORTWID 79
1739546e08eSPoul-Henning Kamp
1749546e08eSPoul-Henning Kamp static void
report_hline(const char * how)1759546e08eSPoul-Henning Kamp report_hline(const char *how)
1769546e08eSPoul-Henning Kamp {
1779546e08eSPoul-Henning Kamp int j;
1789546e08eSPoul-Henning Kamp
1799546e08eSPoul-Henning Kamp for (j = 0; j < REPORTWID; j++) {
1809546e08eSPoul-Henning Kamp if (how && (j == 4 || j == 29 || j == 54)) {
1819546e08eSPoul-Henning Kamp printf("%s", how);
1829546e08eSPoul-Henning Kamp } else {
1839546e08eSPoul-Henning Kamp printf("\xe2\x94\x80");
1849546e08eSPoul-Henning Kamp }
1859546e08eSPoul-Henning Kamp }
1869546e08eSPoul-Henning Kamp printf("\x1b[K\n");
1879546e08eSPoul-Henning Kamp }
1889546e08eSPoul-Henning Kamp
1899546e08eSPoul-Henning Kamp static off_t hist[REPORTWID];
1909546e08eSPoul-Henning Kamp static off_t last_done = -1;
1919546e08eSPoul-Henning Kamp
1929546e08eSPoul-Henning Kamp static void
report_histogram(const struct lump * lp)1939546e08eSPoul-Henning Kamp report_histogram(const struct lump *lp)
1949546e08eSPoul-Henning Kamp {
1959546e08eSPoul-Henning Kamp off_t j, bucket, fp, fe, k, now;
1969546e08eSPoul-Henning Kamp double a;
1979546e08eSPoul-Henning Kamp struct lump *lp2;
1989546e08eSPoul-Henning Kamp
1999546e08eSPoul-Henning Kamp bucket = tot_size / REPORTWID;
2009546e08eSPoul-Henning Kamp if (tot_size > bucket * REPORTWID)
2019546e08eSPoul-Henning Kamp bucket += 1;
2029546e08eSPoul-Henning Kamp if (done_size != last_done) {
2039546e08eSPoul-Henning Kamp memset(hist, 0, sizeof hist);
2049546e08eSPoul-Henning Kamp TAILQ_FOREACH(lp2, &lumps, list) {
2059546e08eSPoul-Henning Kamp fp = lp2->start;
2069546e08eSPoul-Henning Kamp fe = lp2->start + lp2->len;
2079546e08eSPoul-Henning Kamp for (j = fp / bucket; fp < fe; j++) {
2089546e08eSPoul-Henning Kamp k = (j + 1) * bucket;
2099546e08eSPoul-Henning Kamp if (k > fe)
2109546e08eSPoul-Henning Kamp k = fe;
2119546e08eSPoul-Henning Kamp k -= fp;
2129546e08eSPoul-Henning Kamp hist[j] += k;
2139546e08eSPoul-Henning Kamp fp += k;
2149546e08eSPoul-Henning Kamp }
2159546e08eSPoul-Henning Kamp }
2169546e08eSPoul-Henning Kamp last_done = done_size;
2179546e08eSPoul-Henning Kamp }
2189546e08eSPoul-Henning Kamp now = lp->start / bucket;
2199546e08eSPoul-Henning Kamp for (j = 0; j < REPORTWID; j++) {
2209546e08eSPoul-Henning Kamp a = round(8 * (double)hist[j] / bucket);
2219546e08eSPoul-Henning Kamp assert (a >= 0 && a < 9);
2229546e08eSPoul-Henning Kamp if (a == 0 && hist[j])
2239546e08eSPoul-Henning Kamp a = 1;
2249546e08eSPoul-Henning Kamp if (j == now)
2259546e08eSPoul-Henning Kamp printf("\x1b[31m");
2269546e08eSPoul-Henning Kamp if (a == 0) {
2279546e08eSPoul-Henning Kamp putchar(' ');
2289546e08eSPoul-Henning Kamp } else {
2299546e08eSPoul-Henning Kamp putchar(0xe2);
2309546e08eSPoul-Henning Kamp putchar(0x96);
2319546e08eSPoul-Henning Kamp putchar(0x80 + (int)a);
2329546e08eSPoul-Henning Kamp }
2339546e08eSPoul-Henning Kamp if (j == now)
2349546e08eSPoul-Henning Kamp printf("\x1b[0m");
2359546e08eSPoul-Henning Kamp }
2369546e08eSPoul-Henning Kamp putchar('\n');
2379546e08eSPoul-Henning Kamp }
2389546e08eSPoul-Henning Kamp
2399546e08eSPoul-Henning Kamp static void
report(const struct lump * lp,size_t sz)2409546e08eSPoul-Henning Kamp report(const struct lump *lp, size_t sz)
2419546e08eSPoul-Henning Kamp {
2429546e08eSPoul-Henning Kamp struct winsize wsz;
2439546e08eSPoul-Henning Kamp int j;
2449546e08eSPoul-Henning Kamp
2459546e08eSPoul-Henning Kamp assert(lp != NULL);
2469546e08eSPoul-Henning Kamp
2479546e08eSPoul-Henning Kamp if (verbose) {
2489546e08eSPoul-Henning Kamp printf("\x1b[H%s\x1b[K\n", input);
2499546e08eSPoul-Henning Kamp report_header(1);
2509546e08eSPoul-Henning Kamp } else {
2519546e08eSPoul-Henning Kamp putchar('\r');
2529546e08eSPoul-Henning Kamp }
2539546e08eSPoul-Henning Kamp
2549546e08eSPoul-Henning Kamp printf("%13jd %7zu %13jd %5d %13jd %13jd %9.4f",
2559546e08eSPoul-Henning Kamp (intmax_t)lp->start,
2569546e08eSPoul-Henning Kamp sz,
2579546e08eSPoul-Henning Kamp (intmax_t)lp->len,
2589546e08eSPoul-Henning Kamp lp->state,
2599546e08eSPoul-Henning Kamp (intmax_t)done_size,
2609546e08eSPoul-Henning Kamp (intmax_t)(tot_size - done_size),
2619546e08eSPoul-Henning Kamp 100*(double)done_size/(double)tot_size
2629546e08eSPoul-Henning Kamp );
2639546e08eSPoul-Henning Kamp
2649546e08eSPoul-Henning Kamp if (verbose) {
2659546e08eSPoul-Henning Kamp printf("\x1b[K\n");
2669546e08eSPoul-Henning Kamp report_hline(NULL);
2679546e08eSPoul-Henning Kamp report_histogram(lp);
2689546e08eSPoul-Henning Kamp if (TAILQ_EMPTY(&minute)) {
2699546e08eSPoul-Henning Kamp report_hline(NULL);
2709546e08eSPoul-Henning Kamp } else {
2719546e08eSPoul-Henning Kamp report_hline("\xe2\x94\xac");
2729546e08eSPoul-Henning Kamp report_periods();
2739546e08eSPoul-Henning Kamp report_hline("\xe2\x94\xb4");
2749546e08eSPoul-Henning Kamp }
2759546e08eSPoul-Henning Kamp j = ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz);
2769546e08eSPoul-Henning Kamp if (!j)
2779546e08eSPoul-Henning Kamp printf("\x1b[%d;1H", wsz.ws_row);
2789546e08eSPoul-Henning Kamp }
2799546e08eSPoul-Henning Kamp fflush(stdout);
2809546e08eSPoul-Henning Kamp }
2819546e08eSPoul-Henning Kamp
2829546e08eSPoul-Henning Kamp /**********************************************************************/
28339d969aeSPoul-Henning Kamp
28439d969aeSPoul-Henning Kamp static void
new_lump(off_t start,off_t len,int state)28539d969aeSPoul-Henning Kamp new_lump(off_t start, off_t len, int state)
28639d969aeSPoul-Henning Kamp {
28739d969aeSPoul-Henning Kamp struct lump *lp;
28839d969aeSPoul-Henning Kamp
28939d969aeSPoul-Henning Kamp lp = malloc(sizeof *lp);
29039d969aeSPoul-Henning Kamp if (lp == NULL)
29139d969aeSPoul-Henning Kamp err(1, "Malloc failed");
29239d969aeSPoul-Henning Kamp lp->start = start;
29339d969aeSPoul-Henning Kamp lp->len = len;
29439d969aeSPoul-Henning Kamp lp->state = state;
29539d969aeSPoul-Henning Kamp TAILQ_INSERT_TAIL(&lumps, lp, list);
29639d969aeSPoul-Henning Kamp }
29739d969aeSPoul-Henning Kamp
2989546e08eSPoul-Henning Kamp /**********************************************************************
2999546e08eSPoul-Henning Kamp * Save the worklist if -w was given
3009546e08eSPoul-Henning Kamp */
301fd64dc9aSMaxim Konovalov
302fd64dc9aSMaxim Konovalov static void
save_worklist(void)303fd64dc9aSMaxim Konovalov save_worklist(void)
30439d969aeSPoul-Henning Kamp {
305fd64dc9aSMaxim Konovalov FILE *file;
306584f5546SPoul-Henning Kamp struct lump *llp;
3079546e08eSPoul-Henning Kamp char buf[PATH_MAX];
3089546e08eSPoul-Henning Kamp
3099546e08eSPoul-Henning Kamp if (fdw >= 0 && fdatasync(fdw))
3109546e08eSPoul-Henning Kamp err(1, "Write error, probably disk full");
311fd64dc9aSMaxim Konovalov
312fd64dc9aSMaxim Konovalov if (wworklist != NULL) {
3139546e08eSPoul-Henning Kamp bprintf(buf, "%s.tmp", wworklist);
314fd64dc9aSMaxim Konovalov (void)fprintf(stderr, "\nSaving worklist ...");
3159546e08eSPoul-Henning Kamp (void)fflush(stderr);
316fd64dc9aSMaxim Konovalov
3179546e08eSPoul-Henning Kamp file = fopen(buf, "w");
318fd64dc9aSMaxim Konovalov if (file == NULL)
3199546e08eSPoul-Henning Kamp err(1, "Error opening file %s", buf);
320fd64dc9aSMaxim Konovalov
321584f5546SPoul-Henning Kamp TAILQ_FOREACH(llp, &lumps, list)
322fd64dc9aSMaxim Konovalov fprintf(file, "%jd %jd %d\n",
323584f5546SPoul-Henning Kamp (intmax_t)llp->start, (intmax_t)llp->len,
324584f5546SPoul-Henning Kamp llp->state);
3259546e08eSPoul-Henning Kamp (void)fflush(file);
3269546e08eSPoul-Henning Kamp if (ferror(file) || fdatasync(fileno(file)) || fclose(file))
3279546e08eSPoul-Henning Kamp err(1, "Error writing file %s", buf);
3289546e08eSPoul-Henning Kamp if (rename(buf, wworklist))
3299546e08eSPoul-Henning Kamp err(1, "Error renaming %s to %s", buf, wworklist);
330fd64dc9aSMaxim Konovalov (void)fprintf(stderr, " done.\n");
331fd64dc9aSMaxim Konovalov }
332fd64dc9aSMaxim Konovalov }
333fd64dc9aSMaxim Konovalov
334fd64dc9aSMaxim Konovalov /* Read the worklist if -r was given */
335fd64dc9aSMaxim Konovalov static off_t
read_worklist(off_t t)336fd64dc9aSMaxim Konovalov read_worklist(off_t t)
337fd64dc9aSMaxim Konovalov {
338fd64dc9aSMaxim Konovalov off_t s, l, d;
339fd64dc9aSMaxim Konovalov int state, lines;
340fd64dc9aSMaxim Konovalov FILE *file;
341fd64dc9aSMaxim Konovalov
342fd64dc9aSMaxim Konovalov (void)fprintf(stderr, "Reading worklist ...");
3439546e08eSPoul-Henning Kamp (void)fflush(stderr);
344fd64dc9aSMaxim Konovalov file = fopen(rworklist, "r");
345fd64dc9aSMaxim Konovalov if (file == NULL)
346fd64dc9aSMaxim Konovalov err(1, "Error opening file %s", rworklist);
347fd64dc9aSMaxim Konovalov
348fd64dc9aSMaxim Konovalov lines = 0;
349fd64dc9aSMaxim Konovalov d = t;
350fd64dc9aSMaxim Konovalov for (;;) {
351fd64dc9aSMaxim Konovalov ++lines;
352fd64dc9aSMaxim Konovalov if (3 != fscanf(file, "%jd %jd %d\n", &s, &l, &state)) {
353fd64dc9aSMaxim Konovalov if (!feof(file))
354fd64dc9aSMaxim Konovalov err(1, "Error parsing file %s at line %d",
355fd64dc9aSMaxim Konovalov rworklist, lines);
356fd64dc9aSMaxim Konovalov else
357fd64dc9aSMaxim Konovalov break;
358fd64dc9aSMaxim Konovalov }
359fd64dc9aSMaxim Konovalov new_lump(s, l, state);
360fd64dc9aSMaxim Konovalov d -= l;
361fd64dc9aSMaxim Konovalov }
3629546e08eSPoul-Henning Kamp if (fclose(file))
3639546e08eSPoul-Henning Kamp err(1, "Error closing file %s", rworklist);
364fd64dc9aSMaxim Konovalov (void)fprintf(stderr, " done.\n");
365fd64dc9aSMaxim Konovalov /*
366fd64dc9aSMaxim Konovalov * Return the number of bytes already read
367fd64dc9aSMaxim Konovalov * (at least not in worklist).
368fd64dc9aSMaxim Konovalov */
369fd64dc9aSMaxim Konovalov return (d);
370fd64dc9aSMaxim Konovalov }
371fd64dc9aSMaxim Konovalov
3729546e08eSPoul-Henning Kamp /**********************************************************************/
3739546e08eSPoul-Henning Kamp
3749546e08eSPoul-Henning Kamp static void
write_buf(int fd,const void * buf,ssize_t len,off_t where)3759546e08eSPoul-Henning Kamp write_buf(int fd, const void *buf, ssize_t len, off_t where)
3769546e08eSPoul-Henning Kamp {
3779546e08eSPoul-Henning Kamp ssize_t i;
3789546e08eSPoul-Henning Kamp
3799546e08eSPoul-Henning Kamp i = pwrite(fd, buf, len, where);
3809546e08eSPoul-Henning Kamp if (i == len)
3819546e08eSPoul-Henning Kamp return;
3829546e08eSPoul-Henning Kamp
3839546e08eSPoul-Henning Kamp printf("\nWrite error at %jd/%zu\n\t%s\n",
3849546e08eSPoul-Henning Kamp where, i, strerror(errno));
3859546e08eSPoul-Henning Kamp save_worklist();
3869546e08eSPoul-Henning Kamp if (write_errors_are_fatal)
3879546e08eSPoul-Henning Kamp exit(3);
3889546e08eSPoul-Henning Kamp }
3899546e08eSPoul-Henning Kamp
3909546e08eSPoul-Henning Kamp static void
fill_buf(char * buf,ssize_t len,const char * pattern)3919546e08eSPoul-Henning Kamp fill_buf(char *buf, ssize_t len, const char *pattern)
3929546e08eSPoul-Henning Kamp {
3939546e08eSPoul-Henning Kamp ssize_t sz = strlen(pattern);
3949546e08eSPoul-Henning Kamp ssize_t i, j;
3959546e08eSPoul-Henning Kamp
3969546e08eSPoul-Henning Kamp for (i = 0; i < len; i += sz) {
3979546e08eSPoul-Henning Kamp j = len - i;
3989546e08eSPoul-Henning Kamp if (j > sz)
3999546e08eSPoul-Henning Kamp j = sz;
4009546e08eSPoul-Henning Kamp memcpy(buf + i, pattern, j);
4019546e08eSPoul-Henning Kamp }
4029546e08eSPoul-Henning Kamp }
4039546e08eSPoul-Henning Kamp
4049546e08eSPoul-Henning Kamp /**********************************************************************/
4059546e08eSPoul-Henning Kamp
406fd64dc9aSMaxim Konovalov static void
usage(void)407fd64dc9aSMaxim Konovalov usage(void)
408fd64dc9aSMaxim Konovalov {
4095df69e92SUlrich Spörlein (void)fprintf(stderr, "usage: recoverdisk [-b bigsize] [-r readlist] "
4105df69e92SUlrich Spörlein "[-s interval] [-w writelist] source [destination]\n");
4119546e08eSPoul-Henning Kamp /* XXX update */
412fd64dc9aSMaxim Konovalov exit(1);
413fd64dc9aSMaxim Konovalov }
414fd64dc9aSMaxim Konovalov
415fd64dc9aSMaxim Konovalov static void
sighandler(__unused int sig)416fd64dc9aSMaxim Konovalov sighandler(__unused int sig)
417fd64dc9aSMaxim Konovalov {
418fd64dc9aSMaxim Konovalov
419fd64dc9aSMaxim Konovalov aborting = 1;
420fd64dc9aSMaxim Konovalov }
421fd64dc9aSMaxim Konovalov
422fd64dc9aSMaxim Konovalov int
main(int argc,char * const argv[])423fd64dc9aSMaxim Konovalov main(int argc, char * const argv[])
424fd64dc9aSMaxim Konovalov {
425fd64dc9aSMaxim Konovalov int ch;
4269546e08eSPoul-Henning Kamp size_t sz, j;
4279546e08eSPoul-Henning Kamp int error;
4289546e08eSPoul-Henning Kamp char *buf;
429fd64dc9aSMaxim Konovalov u_int sectorsize;
430d44ae92aSXin LI off_t stripesize;
4311f6ac570SPoul-Henning Kamp time_t t1, t2;
432db45c56dSMaxim Sobolev struct stat sb;
433584f5546SPoul-Henning Kamp u_int n, snapshot = 60;
4349546e08eSPoul-Henning Kamp static struct lump *lp;
43539d969aeSPoul-Henning Kamp
4369546e08eSPoul-Henning Kamp while ((ch = getopt(argc, argv, "b:r:w:s:u:v")) != -1) {
437fd64dc9aSMaxim Konovalov switch (ch) {
438584f5546SPoul-Henning Kamp case 'b':
439584f5546SPoul-Henning Kamp bigsize = strtoul(optarg, NULL, 0);
440584f5546SPoul-Henning Kamp break;
441fd64dc9aSMaxim Konovalov case 'r':
442fd64dc9aSMaxim Konovalov rworklist = strdup(optarg);
443fd64dc9aSMaxim Konovalov if (rworklist == NULL)
444fd64dc9aSMaxim Konovalov err(1, "Cannot allocate enough memory");
445fd64dc9aSMaxim Konovalov break;
446584f5546SPoul-Henning Kamp case 's':
447584f5546SPoul-Henning Kamp snapshot = strtoul(optarg, NULL, 0);
448584f5546SPoul-Henning Kamp break;
4499546e08eSPoul-Henning Kamp case 'u':
4509546e08eSPoul-Henning Kamp unreadable_pattern = optarg;
4519546e08eSPoul-Henning Kamp break;
4529546e08eSPoul-Henning Kamp case 'v':
4539546e08eSPoul-Henning Kamp set_verbose();
4549546e08eSPoul-Henning Kamp break;
455fd64dc9aSMaxim Konovalov case 'w':
456fd64dc9aSMaxim Konovalov wworklist = strdup(optarg);
457fd64dc9aSMaxim Konovalov if (wworklist == NULL)
458fd64dc9aSMaxim Konovalov err(1, "Cannot allocate enough memory");
459fd64dc9aSMaxim Konovalov break;
460fd64dc9aSMaxim Konovalov default:
461fd64dc9aSMaxim Konovalov usage();
462fd64dc9aSMaxim Konovalov /* NOTREACHED */
463fd64dc9aSMaxim Konovalov }
464fd64dc9aSMaxim Konovalov }
465fd64dc9aSMaxim Konovalov argc -= optind;
466fd64dc9aSMaxim Konovalov argv += optind;
46739d969aeSPoul-Henning Kamp
468fd64dc9aSMaxim Konovalov if (argc < 1 || argc > 2)
469fd64dc9aSMaxim Konovalov usage();
47039d969aeSPoul-Henning Kamp
4719546e08eSPoul-Henning Kamp input = argv[0];
472fd64dc9aSMaxim Konovalov fdr = open(argv[0], O_RDONLY);
47339d969aeSPoul-Henning Kamp if (fdr < 0)
474fd64dc9aSMaxim Konovalov err(1, "Cannot open read descriptor %s", argv[0]);
475db45c56dSMaxim Sobolev
476db45c56dSMaxim Sobolev error = fstat(fdr, &sb);
477db45c56dSMaxim Sobolev if (error < 0)
478db45c56dSMaxim Sobolev err(1, "fstat failed");
479db45c56dSMaxim Sobolev if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
480db45c56dSMaxim Sobolev error = ioctl(fdr, DIOCGSECTORSIZE, §orsize);
481db45c56dSMaxim Sobolev if (error < 0)
482db45c56dSMaxim Sobolev err(1, "DIOCGSECTORSIZE failed");
483fd64dc9aSMaxim Konovalov
484399fe479SXin LI error = ioctl(fdr, DIOCGSTRIPESIZE, &stripesize);
485*d830cac9SPoul-Henning Kamp if (error == 0 && stripesize < sectorsize)
486399fe479SXin LI sectorsize = stripesize;
487399fe479SXin LI
488db45c56dSMaxim Sobolev minsize = sectorsize;
489f32d2926SPedro F. Giffuni bigsize = rounddown(bigsize, sectorsize);
490db45c56dSMaxim Sobolev
4919546e08eSPoul-Henning Kamp error = ioctl(fdr, DIOCGMEDIASIZE, &tot_size);
492db45c56dSMaxim Sobolev if (error < 0)
493db45c56dSMaxim Sobolev err(1, "DIOCGMEDIASIZE failed");
494db45c56dSMaxim Sobolev } else {
4959546e08eSPoul-Henning Kamp tot_size = sb.st_size;
496db45c56dSMaxim Sobolev }
497db45c56dSMaxim Sobolev
498584f5546SPoul-Henning Kamp if (bigsize < minsize)
499584f5546SPoul-Henning Kamp bigsize = minsize;
500584f5546SPoul-Henning Kamp
501584f5546SPoul-Henning Kamp for (ch = 0; (bigsize >> ch) > minsize; ch++)
502584f5546SPoul-Henning Kamp continue;
503584f5546SPoul-Henning Kamp medsize = bigsize >> (ch / 2);
504f32d2926SPedro F. Giffuni medsize = rounddown(medsize, minsize);
505584f5546SPoul-Henning Kamp
506ab5792a9SPoul-Henning Kamp fprintf(stderr, "Bigsize = %zu, medsize = %zu, minsize = %zu\n",
507584f5546SPoul-Henning Kamp bigsize, medsize, minsize);
508584f5546SPoul-Henning Kamp
509fd64dc9aSMaxim Konovalov buf = malloc(bigsize);
510fd64dc9aSMaxim Konovalov if (buf == NULL)
511d81c1141SEd Schouten err(1, "Cannot allocate %zu bytes buffer", bigsize);
51239d969aeSPoul-Henning Kamp
513fd64dc9aSMaxim Konovalov if (argc > 1) {
5145df69e92SUlrich Spörlein fdw = open(argv[1], O_WRONLY | O_CREAT, DEFFILEMODE);
515fd64dc9aSMaxim Konovalov if (fdw < 0)
516fd64dc9aSMaxim Konovalov err(1, "Cannot open write descriptor %s", argv[1]);
5179546e08eSPoul-Henning Kamp if (ftruncate(fdw, tot_size) < 0)
5185df69e92SUlrich Spörlein err(1, "Cannot truncate output %s to %jd bytes",
5199546e08eSPoul-Henning Kamp argv[1], (intmax_t)tot_size);
520fd64dc9aSMaxim Konovalov } else
521fd64dc9aSMaxim Konovalov fdw = -1;
522fd64dc9aSMaxim Konovalov
523fd64dc9aSMaxim Konovalov if (rworklist != NULL) {
5249546e08eSPoul-Henning Kamp done_size = read_worklist(tot_size);
525fd64dc9aSMaxim Konovalov } else {
5269546e08eSPoul-Henning Kamp new_lump(0, tot_size, 0);
5279546e08eSPoul-Henning Kamp done_size = 0;
528fd64dc9aSMaxim Konovalov }
529fd64dc9aSMaxim Konovalov if (wworklist != NULL)
530fd64dc9aSMaxim Konovalov signal(SIGINT, sighandler);
53139d969aeSPoul-Henning Kamp
5329546e08eSPoul-Henning Kamp t1 = time(NULL);
5339546e08eSPoul-Henning Kamp sz = 0;
5349546e08eSPoul-Henning Kamp if (!verbose)
5359546e08eSPoul-Henning Kamp report_header(0);
536ca9acde2SPoul-Henning Kamp else
537ca9acde2SPoul-Henning Kamp printf("\x1b[2J");
538584f5546SPoul-Henning Kamp n = 0;
53939d969aeSPoul-Henning Kamp for (;;) {
54039d969aeSPoul-Henning Kamp lp = TAILQ_FIRST(&lumps);
54139d969aeSPoul-Henning Kamp if (lp == NULL)
54239d969aeSPoul-Henning Kamp break;
5439546e08eSPoul-Henning Kamp while (lp->len > 0) {
544601c3cc0SPoul-Henning Kamp
5459546e08eSPoul-Henning Kamp if (lp->state == 0)
5469546e08eSPoul-Henning Kamp sz = MIN(lp->len, (off_t)bigsize);
5479546e08eSPoul-Henning Kamp else if (lp->state == 1)
5489546e08eSPoul-Henning Kamp sz = MIN(lp->len, (off_t)medsize);
5499546e08eSPoul-Henning Kamp else
5509546e08eSPoul-Henning Kamp sz = MIN(lp->len, (off_t)minsize);
5519546e08eSPoul-Henning Kamp assert(sz != 0);
5529546e08eSPoul-Henning Kamp
5539546e08eSPoul-Henning Kamp t2 = time(NULL);
55489d500c6SMatteo Riondato if (t1 != t2 || lp->len < (off_t)bigsize) {
5551f6ac570SPoul-Henning Kamp t1 = t2;
556584f5546SPoul-Henning Kamp if (++n == snapshot) {
557584f5546SPoul-Henning Kamp save_worklist();
558584f5546SPoul-Henning Kamp n = 0;
559584f5546SPoul-Henning Kamp }
5609546e08eSPoul-Henning Kamp report(lp, sz);
5611f6ac570SPoul-Henning Kamp }
5629546e08eSPoul-Henning Kamp
5639546e08eSPoul-Henning Kamp j = pread(fdr, buf, sz, lp->start);
5649546e08eSPoul-Henning Kamp #if 0
5659546e08eSPoul-Henning Kamp if (!(random() & 0xf)) {
5669546e08eSPoul-Henning Kamp j = -1;
5679546e08eSPoul-Henning Kamp errno = EIO;
56839d969aeSPoul-Henning Kamp }
5699546e08eSPoul-Henning Kamp #endif
5709546e08eSPoul-Henning Kamp if (j == sz) {
5719546e08eSPoul-Henning Kamp done_size += sz;
57239d969aeSPoul-Henning Kamp if (fdw >= 0)
5739546e08eSPoul-Henning Kamp write_buf(fdw, buf, sz, lp->start);
5749546e08eSPoul-Henning Kamp lp->start += sz;
5759546e08eSPoul-Henning Kamp lp->len -= sz;
5769546e08eSPoul-Henning Kamp if (verbose && lp->state > 2)
5779546e08eSPoul-Henning Kamp report_good_read(t2, sz);
57839d969aeSPoul-Henning Kamp continue;
57939d969aeSPoul-Henning Kamp }
5809546e08eSPoul-Henning Kamp error = errno;
5819546e08eSPoul-Henning Kamp
5829546e08eSPoul-Henning Kamp printf("%jd %zu %d read error (%s)\n",
5839546e08eSPoul-Henning Kamp lp->start, sz, lp->state, strerror(error));
5849546e08eSPoul-Henning Kamp if (verbose)
5859546e08eSPoul-Henning Kamp report(lp, sz);
586d21ce74dSPoul-Henning Kamp if (fdw >= 0 && strlen(unreadable_pattern)) {
587d21ce74dSPoul-Henning Kamp fill_buf(buf, sz, unreadable_pattern);
588d21ce74dSPoul-Henning Kamp write_buf(fdw, buf, sz, lp->start);
589d21ce74dSPoul-Henning Kamp }
590d21ce74dSPoul-Henning Kamp new_lump(lp->start, sz, lp->state + 1);
591d21ce74dSPoul-Henning Kamp lp->start += sz;
592d21ce74dSPoul-Henning Kamp lp->len -= sz;
5939546e08eSPoul-Henning Kamp if (error == EINVAL) {
5949546e08eSPoul-Henning Kamp printf("Try with -b 131072 or lower ?\n");
5955df69e92SUlrich Spörlein aborting = 1;
5969546e08eSPoul-Henning Kamp break;
5975df69e92SUlrich Spörlein }
5989546e08eSPoul-Henning Kamp if (error == ENXIO) {
5999546e08eSPoul-Henning Kamp printf("Input device probably detached...\n");
600972b3f30SPoul-Henning Kamp aborting = 1;
6019546e08eSPoul-Henning Kamp break;
60239d969aeSPoul-Henning Kamp }
6039546e08eSPoul-Henning Kamp }
6049546e08eSPoul-Henning Kamp if (aborting)
605fd64dc9aSMaxim Konovalov save_worklist();
6069546e08eSPoul-Henning Kamp if (aborting || !TAILQ_NEXT(lp, list))
6079546e08eSPoul-Henning Kamp report(lp, sz);
6089546e08eSPoul-Henning Kamp if (aborting)
6099546e08eSPoul-Henning Kamp break;
6109546e08eSPoul-Henning Kamp assert(lp->len == 0);
611fd64dc9aSMaxim Konovalov TAILQ_REMOVE(&lumps, lp, list);
61239d969aeSPoul-Henning Kamp free(lp);
61339d969aeSPoul-Henning Kamp }
6149546e08eSPoul-Henning Kamp printf("%s", aborting ? "Aborted\n" : "Completed\n");
6159546e08eSPoul-Henning Kamp free(buf);
616fd64dc9aSMaxim Konovalov return (0);
61739d969aeSPoul-Henning Kamp }
618