From e17bc0f9444e1e7c150c5023901d5b55875dd6af Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Thu, 19 Sep 2024 16:24:07 +0200 Subject: misc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- README | 17 +- TODO | 7 +- config.mk | 6 +- deadshred.1 | 63 +++++- deadshred.c | 651 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------ 5 files changed, 676 insertions(+), 68 deletions(-) diff --git a/README b/README index e15cfdc..010786f 100644 --- a/README +++ b/README @@ -2,7 +2,7 @@ NAME deadshred - override the contents of a device that may be broken SYNOPSIS - deadshred device [< random-source] + deadshred [-o offset] [-l length | -e postend] device [< random-source] DESCRIPTION The deadshred utility fills a file or block devices with @@ -12,7 +12,20 @@ DESCRIPTION to override and retries it later. OPTIONS - No options are supported. + The following options are supported: + + -e postend + Position in the device to stop writing at. The byte + indexed by the value postend will not be overwritten. + + -l length + The number of bytes in the device to overwrite. If the + -o option is unused (or the offset 0 is used), this is + equivalent to the -e option, however the -o option is + used, writing will stop at length bytes past offset. + + -o offset + The index of the first byte in the device to overwrite. OPERANDS The following operand is supported: diff --git a/TODO b/TODO index 9a3b805..d85ebb9 100644 --- a/TODO +++ b/TODO @@ -1,6 +1 @@ -Add `-n iterations` -Add offset..length option -Add shred map for continuing later (print to stdout on SIGTERM) - -Enhance progress printout with: - shredded bytes per seconds (dont't forget machine and process suspension) +Add shred map operand diff --git a/config.mk b/config.mk index d2d807d..b250eca 100644 --- a/config.mk +++ b/config.mk @@ -1,9 +1,9 @@ PREFIX = /usr MANPREFIX = $(PREFIX)/share/man -CC = c99 +CC = cc -std=c11 CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE -CFLAGS = -LDFLAGS = -lsimple +CFLAGS = -lpthread +LDFLAGS = -lsimple -lpthread #libsimple>=1.7 diff --git a/deadshred.1 b/deadshred.1 index be586b2..5c3eb63 100644 --- a/deadshred.1 +++ b/deadshred.1 @@ -4,6 +4,13 @@ deadshred \- override the contents of a device that may be broken .SH SYNOPSIS .B deadshred +[-o +.IR offset ] +[-l +.I length +| +-e +.IR postend ] .I device [< .IR random-source ] @@ -20,7 +27,61 @@ utility will skip any section it fails to override and retries it later. .SH OPTIONS -No options are supported. +The following options are supported: +.TP +.BR -e \ \fIpostend\fP +Position in the +.I device +to stop writing at. The byte indexed by the value +.I postend +will not be overwritten. +.TP +.BR -l \ \fIlength\fP +The number of bytes in the +.I device +to overwrite. If the +.B -o +option is unused (or the +.I offset +0 is used), this is equivalent to the +.B -e +option, however the +.B -o +option is used, writing will stop at +.I length +bytes past +.IR offset . +.TP +.BR -o \ \fIoffset\fP +The index of the first byte in the +.I device +to overwrite. +.PP +The value of the +.B -elo +flags can be expressed a sum of terms (delimited by +SP +.RB (' " " '), +comma +.RB (' , '), +or plus +.RB (' + ')) +expressed either as a integer representing a byte count, +or a real number with an optionally prefixed byte size +unit, which shall be either +.B B +(for bytes, which requires the number to be an integer) +or a prefix followed by +.B iB +(for base 1024) +or +.B B +(for base 1000), or just the prefix (also for base 1024). +The lowest supported prefix is +.B k +.RB ( K +is recognised as a synonym, but the value is otherwise +case sensitive). .SH OPERANDS The following operand is supported: diff --git a/deadshred.c b/deadshred.c index 3321a4e..c25298e 100644 --- a/deadshred.c +++ b/deadshred.c @@ -1,12 +1,18 @@ /* See LICENSE file for copyright and license details. */ #include +#include #include #include -USAGE("device [< random-source]"); +USAGE("[-o offset] [-l length | -e postend] device [< random-source]"); +enum direction { + FORWARDS = 0, + BACKWARDS = 1 +}; + struct span { off_t start; off_t end; @@ -15,6 +21,8 @@ struct span { }; +static _Atomic volatile sig_atomic_t exiting = 0; + static struct span *spans = NULL; static size_t nspans = 0; static size_t spans_size = 0; @@ -33,6 +41,34 @@ static uintmax_t bad_writes = 0; static uintmax_t bad_sections = 0; static off_t bad_bytes = 0; +static clockid_t clck = CLOCK_MONOTONIC_COARSE; +static const char *clkcstr = "CLOCK_MONOTONIC_COARSE"; +static struct timespec last_success = {-1, 0}; +static struct timespec max_success = {-1, 0}; +static struct timespec start_time; + +#define MILLISECONDS(X) X##000000L +static const struct timespec progress_print_interval = {0, MILLISECONDS(500)}; +static const struct timespec poll_timeout = {0, MILLISECONDS(500)}; +static int progress_print_sig_pipe[2]; +static pthread_mutex_t progress_mutex; + +/* TODO deal with machine and process suspension */ +static struct timespec write_average_begin_times[5]; +static off_t write_average_amounts[ELEMSOF(write_average_begin_times)] = {0}; +static int write_average_i = 0; + +static enum direction direction = FORWARDS; +static uintmax_t pass_nr = 1; + + +static void +signal_handler(int signo) +{ + (void) signo; + exiting = 1; +} + static off_t filesize(int fd, const char *fname) @@ -88,13 +124,12 @@ ensure_random(size_t needed) } -static off_t -add_span(off_t off, off_t max_end, size_t blocksize) +static void +add_span(off_t off, off_t amount, size_t blocksize) { - off_t end; + off_t end = off + amount; - end = MIN((off | ((off_t)blocksize - 1)) + 1, max_end); - while ((off_t)(blocksize >> 1) >= end - off) + while ((off_t)(blocksize >> 1) >= amount) blocksize >>= 1; if (nspans == spans_size) { @@ -104,11 +139,9 @@ add_span(off_t off, off_t max_end, size_t blocksize) spans[nspans].start = off; spans[nspans].end = end; - spans[nspans].bad = end - off; - spans[nspans].blocksize = (size_t)blocksize; + spans[nspans].bad = amount; + spans[nspans].blocksize = blocksize; nspans++; - - return end - off; } @@ -149,39 +182,363 @@ humansize1024(off_t s, char *buf) } +#if defined(__GNUC__) +__attribute__((__pure__)) +#endif +static off_t +unhumansize(const char *s, char flag) +{ + off_t sum = 0, term, digit, divisor, power, base; + + if (!isdigit(s[0]) && !(s[0] == '-' && isdigit(s[1]))) + usage(); + + do { + divisor = 1; + term = 0; + while (isdigit(*s)) { + digit = (*s++ & 15); + if (term > (OFF_MAX - digit) / 10) + eprintf("value of -%c flag is too large", flag); + term = term * 10 + digit; + } + if (*s == '.') { + s++; + while (isdigit(*s)) { + digit = (*s++ & 15); + if (term > (OFF_MAX - digit) / 10) + eprintf("value of -%c flag is too large", flag); + term = term * 10 + digit; + divisor *= 10; + } + } + + power = 0; + switch (*s) { + case 'Q': power++; /* fall through */ + case 'R': power++; /* fall through */ + case 'Y': power++; /* fall through */ + case 'Z': power++; /* fall through */ + case 'E': power++; /* fall through */ + case 'P': power++; /* fall through */ + case 'T': power++; /* fall through */ + case 'G': power++; /* fall through */ + case 'M': power++; /* fall through */ + case 'k': case 'K': power++; + if (s[1] == 'i' || s[2] == 'B') { + base = 1024; + s = &s[3]; + } else if (s[1] == 'B') { + base = 1000; + s = &s[2]; + } else { + base = 1024; + s = &s[1]; + } + while (power) { + term *= base; + power--; + } + break; + case 'B': + if (!power && divisor > 1) + usage(); + s++; + break; + default: + break; + } + sum += term /= divisor; + + while (*s == ' ' || *s == ',' || *s == '+') + s++; + + } while (isdigit(s[0]) || (s[0] == '.' && isdigit(s[1]))); + + return sum; +} + + +static const char * +durationstr(const struct timespec *dur, char *buf, int second_decimals) +{ + uintmax_t ss, s, m, h, d, ss_div = UINTMAX_C(1000000000); + char *p; + const char *unit; + int i; + + if (dur->tv_sec < 0 || dur->tv_nsec < 0) + return "-"; + + if (second_decimals < 0) + second_decimals = 0; + else if (second_decimals > 9) + second_decimals = 9; + + for (i = 0; i < second_decimals; i++) + ss_div /= 10U; + ss = (uintmax_t)dur->tv_nsec / ss_div; + s = (uintmax_t)dur->tv_sec % 60U; + m = (uintmax_t)dur->tv_sec / 60U % 60U; + h = (uintmax_t)dur->tv_sec / 60U / 60U % 24U; + d = (uintmax_t)dur->tv_sec / 60U / 60U / 24U; + + p = buf; + if (d) + p += sprintf(p, "%ju days, ", d); + if (h) { + p += sprintf(p, "%ju:%02ju:%02ju", h, m, s); + unit = "hours"; + } else if (m) { + p += sprintf(p, "%ju:%02ju", m, s); + unit = "minutes"; + } else { + p += sprintf(p, "%ju", s); + unit = "seconds"; + } + if (second_decimals) + p += sprintf(p, ".%0*ju", second_decimals, ss); + p += sprintf(p, " %s", unit); + + return buf; +} + + +#if defined(__linux__) +#include +static int +have_micro_symbol(void) +{ + static int ret = -1; + if (ret < 0) { + struct unimapdesc desc; + struct unipair *pairs = NULL; + size_t i; + ret = 1; + desc.entry_ct = 0; + desc.entries = NULL; + if (ioctl(STDIN_FILENO, GIO_UNIMAP, &desc)) + if (!desc.entry_ct) + goto out; + desc.entries = pairs = ecalloc(desc.entry_ct, sizeof(*pairs)); + if (ioctl(STDIN_FILENO, GIO_UNIMAP, &desc)) + goto out; + for (i = 0; i < desc.entry_ct; i++) + if (desc.entries[i++].unicode == 0xB5U) + goto out; + ret = 0; + out: + free(pairs); + } + return ret; +} +#else +# define have_micro_symbol() 1 +#endif + + +static int +was_write_average_overrun(int i, const struct timespec *now, int seconds) +{ + struct timespec diff; + libsimple_difftimespec(&diff, now, &write_average_begin_times[i]); + if (diff.tv_sec >= seconds) + return 1; + if (diff.tv_sec == seconds - 1 && diff.tv_nsec >= 900000000L) + return 1; + return 0; +} + + static void -print_progress(int done) +shift_write_average(void) { - static char buf1[1024] = {0}; - static char buf2[1024] = {0}; - static int i = 0; + write_average_i--; + memmove(&write_average_begin_times[0], &write_average_begin_times[1], + (size_t)write_average_i * sizeof(*write_average_begin_times)); + memmove(&write_average_amounts[0], &write_average_amounts[1], + (size_t)write_average_i * sizeof(*write_average_amounts)); +} + + +static void +print_progress(int done, const struct timespec *now) +{ + static char buf1[2048] = {0}; + static char buf2[2048] = {0}; + static int bufi = 0; char subbuf1[256]; char subbuf2[256]; char subbuf3[256]; char subbuf4[256]; + char subbuf5[256]; + char subbuf6[256]; + char subbuf7[256]; + char write_average_buf[512]; + struct timespec since_success = {-1, 0}; + struct timespec time_spent, write_average_time; + int i; + off_t write_average_sum = 0; + double write_average; + + for (i = 0; i <= write_average_i; i++) + write_average_sum += write_average_amounts[i]; + libsimple_difftimespec(&write_average_time, now, &write_average_begin_times[0]); + if (write_average_time.tv_sec < 0 || (write_average_time.tv_sec == 0 && write_average_time.tv_nsec < 100000000L)) { + stpcpy(write_average_buf, "-"); + } else if (write_average_sum == 0) { + stpcpy(write_average_buf, "0"); + } else { + const char *units_big = "kMGTPEZYRQ"; + const char *units_small = "munpfazyrq"; + int unit = -1; + write_average = (double)write_average_time.tv_nsec; + write_average /= 1000000000L; + write_average += (double)write_average_time.tv_sec; + write_average = (double)write_average_sum / write_average; + if (write_average < (double)0.01f) { + do { + write_average *= 1000; + unit++; + } while (units_small[unit + 1] && write_average < (double)0.01f); + if (units_small[unit] == 'u' && have_micro_symbol()) + sprintf(write_average_buf, "%.02lf µB/s", write_average); + else + sprintf(write_average_buf, "%.02lf %cB/s", write_average, units_small[unit]); + } else { + while (units_big[unit + 1] && write_average >= 1000) { + write_average /= 1000; + unit++; + } + if (unit < 0) + sprintf(write_average_buf, "%.02lf B/s", write_average); + else + sprintf(write_average_buf, "%.02lf %cB/s", write_average, units_big[unit]); + } + } + + if (last_success.tv_sec >= 0) { + libsimple_difftimespec(&since_success, now, &last_success); + if (libsimple_cmptimespec(&since_success, &max_success) > 0) + max_success = since_success; + } + + /* TODO deal with machine and process suspension */ + libsimple_difftimespec(&time_spent, now, &start_time); - sprintf(i == 0 ? buf1 : buf2, + sprintf(bufi == 0 ? buf1 : buf2, "%ji bytes (%s, %s, %.2lf %%) of %s (%s) shredded\033[K\n" - "failed writes: %ju; bad sections: %ju (%s, %s)\033[K\n%s", - (intmax_t)shredded, + "failed writes: %ju; bad sections: %ju (%s, %s)\033[K\n" + "time spent shredding: %s; performance: %s\033[K\n" + "time since last successful write: %s\033[K\n" + "maximum time until a successful write: %s\033[K\n" + "pass: %ju; pass direction: %s\033[K\n" + "%s", + /* line 1 { */ + (intmax_t)shredded, humansize1000(shredded, subbuf1), humansize1024(shredded, subbuf2), 100 * (double)shredded / (double)total_size, total_size_1000, total_size_1024, - bad_writes, - bad_sections, + /* } line 2 { */ + bad_writes, + bad_sections, humansize1000(bad_bytes, subbuf3), humansize1024(bad_bytes, subbuf4), - done ? "" : "\033[A\033[A"); + /* } line 3 { */ + durationstr(&time_spent, subbuf5, 1), + write_average_buf, + /* } line 4 { */ + durationstr(&since_success, subbuf6, 2), + /* } line 5 { */ + durationstr(&max_success, subbuf7, 2), + /* } line 6 { */ + pass_nr, + direction == FORWARDS ? "forwards" : "backwards", + /* } */ + done ? "" : "\033[6A"); if (strcmp(buf1, buf2)) { - fprintf(stderr, "%s", i == 0 ? buf1 : buf2); + fprintf(stderr, "%s", bufi == 0 ? buf1 : buf2); fflush(stderr); } - i ^= 1; + bufi ^= 1; +} + + +static void +update_progress(const struct timespec *now) +{ + if (was_write_average_overrun(write_average_i, now, 1)) { + write_average_i++; + if (write_average_i == ELEMSOF(write_average_amounts)) + shift_write_average(); + write_average_begin_times[write_average_i] = *now; + write_average_amounts[write_average_i] = 0; + } + while (write_average_i && was_write_average_overrun(0, now, (int)ELEMSOF(write_average_amounts))) + shift_write_average(); +} + + +static void * +progress_print_loop(void *user) +{ + struct pollfd pfd = {.fd = progress_print_sig_pipe[0], .events = POLLOUT}; + struct timespec now; + ssize_t r; + int terminate = 0; + sigset_t sigset; + + (void) user; + + sigemptyset(&sigset); + sigaddset(&sigset, SIGTERM); + sigaddset(&sigset, SIGINT); + errno = pthread_sigmask(SIG_UNBLOCK, &sigset, NULL); + if (errno) + eprintf("pthread_sigmask SIG_UNBLOCK {SIGTERM, SIGINT} NULL:"); + + do { + switch (ppoll(&pfd, 1U, &poll_timeout, NULL)) { + case -1: + if (errno != EINTR) + eprintf("ppoll:"); + if (exiting) + break; + continue; + case 0: + break; + default: + r = read(progress_print_sig_pipe[0], &now, sizeof(now)); + if (r == (ssize_t)sizeof(now)) { + goto have_time; + } else if (!r) { + terminate = 1; + } else if (r < 0) { + if (errno == EINTR) + continue; + eprintf("read :"); + } + break; + } + if (clock_gettime(clck, &now)) + eprintf("clock_gettime %s:", clkcstr); + have_time: + pthread_mutex_lock(&progress_mutex); + if (exiting) { + fprintf(stderr, "\033[K\nTermination initialised by user...\033[K\n\033[K\n"); + terminate = 1; + } + print_progress(0, &now); + update_progress(&now); + pthread_mutex_unlock(&progress_mutex); + } while (!terminate); + + return NULL; } @@ -190,48 +547,92 @@ shredspan(int fd, struct span *span, const char *fname) { off_t off, n; ssize_t r; - clockid_t clck = CLOCK_MONOTONIC_COARSE; - const char *clkcstr = "CLOCK_MONOTONIC_COARSE"; struct timespec now, when = {0, 0}; int bad = span->bad > 0; - if (clock_gettime(clck, &now)) { - clck = CLOCK_MONOTONIC; - clkcstr = "CLOCK_MONOTONIC"; - } + pthread_mutex_lock(&progress_mutex); + + off = (direction == FORWARDS ? span->start : span->end); + while (direction == FORWARDS ? off < span->end : off > span->start) { + if (exiting) { + userexit: + if (direction == FORWARDS) + span->start = off; + else + span->end = off; + close(progress_print_sig_pipe[1]); + progress_print_sig_pipe[1] = -1; + goto out; + } - for (off = span->start; off < span->end;) { if (clock_gettime(clck, &now)) eprintf("clock_gettime %s:", clkcstr); - if (now.tv_sec > when.tv_sec || (now.tv_sec == when.tv_sec && now.tv_nsec >= when.tv_nsec)) { - when = now; - when.tv_nsec += 500000000L; - when.tv_sec += when.tv_nsec / 1000000000L; - when.tv_nsec = when.tv_nsec % 1000000000L; - print_progress(0); + if (libsimple_cmptimespec(&now, &when) >= 0) { + libsimple_sumtimespec(&when, &now, &progress_print_interval); + write(progress_print_sig_pipe[1], &now, sizeof(now)); } - if (lseek(fd, off, SEEK_SET) < 0) - eprintf("lseek %s %ji SEEK_SET\n", fname, (intmax_t)off); ensure_random(span->blocksize); - r = write(fd, &reservoir[reservoir_off], (size_t)MIN((off_t)span->blocksize, span->end - off)); + if (direction == FORWARDS) { + n = MIN((off_t)span->blocksize, span->end - off); + } else { + n = off--; + off &= ~(off_t)(span->blocksize - 1U); + if (off < span->start) + off = span->start; + n -= off; + if (!n) + break; + } + pthread_mutex_unlock(&progress_mutex); + pwrite_again: + r = pwrite(fd, &reservoir[reservoir_off], (size_t)n, off); if (r < 0) { - if (errno != EINTR) { - off += n = add_span(off, span->end, span->blocksize == 1U ? 1U : span->blocksize); - if (!span->bad) - bad_bytes += n; - if (bad) - bad = 0; - else - bad_sections += 1U; + if (errno == EINTR) { + if (exiting) { + pthread_mutex_lock(&progress_mutex); + goto userexit; + } + goto pwrite_again; } + pthread_mutex_lock(&progress_mutex); + if (errno != EIO) + weprintf("pwrite %s %zu %ji:", fname, (size_t)n, (intmax_t)off); + add_span(off, n, span->blocksize == 1U ? 1U : span->blocksize); + if (direction == FORWARDS) + off += n; + if (!span->bad) + bad_bytes += n; + if (bad) + bad = 0; + else + bad_sections += 1U; when.tv_sec = 0; when.tv_nsec = 0; bad_writes += 1U; continue; } - off += (off_t)r; + pthread_mutex_lock(&progress_mutex); + if (direction == FORWARDS) { + off += (off_t)r; + } else if ((off_t)r < n) { + n -= (off_t)r; + add_span(off + (off_t)r, n, span->blocksize == 1U ? 1U : span->blocksize); + if (direction == FORWARDS) + off += n; + if (!span->bad) + bad_bytes += n; + if (bad) + bad = 0; + else + bad_sections += 1U; + when.tv_sec = 0; + when.tv_nsec = 0; + bad_writes += 1U; + } shredded += (off_t)r; reservoir_off += (size_t)r; + write_average_amounts[write_average_i] += (off_t)r; + last_success = now; if (span->bad) { bad_bytes -= (off_t)r; span->bad -= (off_t)r; @@ -240,16 +641,68 @@ shredspan(int fd, struct span *span, const char *fname) if (bad && !span->bad) bad_sections -= 1U; + +out: + pthread_mutex_unlock(&progress_mutex); +} + + +static void +dump_map(int fd, const char *fname) +{ + size_t i; + int r; + + if (!nspans) + return; + + for (i = 0; i < nspans; i++) { + r = dprintf(fd, "%s%jx-%jx/%zx", + i ? "," : "0x", + (uintmax_t)spans[i].start, + (uintmax_t)spans[i].end, + spans[i].blocksize); + if (r < 0) + goto fail; + } + r = dprintf(fd, "\n"); + if (r < 0) + goto fail; + + return; + +fail: + eprintf("dprintf %s:", fname); } int main(int argc, char *argv[]) { - size_t i; + off_t off = -1, len = -1, end = -1; + size_t i, j; int fd; + struct timespec now; + struct sigaction sa; + pthread_t progress_print_thread; + sigset_t sigset; ARGBEGIN { + case 'o': + if (off >= 0) + usage(); + off = unhumansize(ARG(), FLAG()); + break; + case 'l': + if (len >= 0 || end >= 0) + usage(); + len = unhumansize(ARG(), FLAG()); + break; + case 'e': + if (len >= 0 || end >= 0) + usage(); + end = unhumansize(ARG(), FLAG()); + break; default: usage(); } ARGEND; @@ -257,36 +710,122 @@ main(int argc, char *argv[]) if (argc != 1) usage(); - fd = open(argv[0], O_WRONLY); + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = &signal_handler; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGTERM, &sa, NULL)) + eprintf("sigaction SIGTERM {.sa_handler=, .sa_mask={}, .sa_flags=0} NULL:"); + if (sigaction(SIGINT, &sa, NULL)) + eprintf("sigaction SIGINT {.sa_handler=, .sa_mask={}, .sa_flags=0} NULL:"); + + sigemptyset(&sigset); + sigaddset(&sigset, SIGTERM); + sigaddset(&sigset, SIGINT); + errno = pthread_sigmask(SIG_BLOCK, &sigset, NULL); + if (errno) + eprintf("pthread_sigmask SIG_BLOCK {SIGTERM, SIGINT} NULL:"); + + fd = open(argv[0], O_WRONLY | O_DSYNC); if (fd < 0) - eprintf("open %s O_WRONLY:", argv[0]); + eprintf("open %s O_WRONLY|O_DSYNC:", argv[0]); use_stdin = !isatty(STDIN_FILENO); + if (use_stdin) { + struct stat st; + if (fstat(STDIN_FILENO, &st)) { + if (errno == EBADF) + use_stdin = 0; + else + eprintf("fstat :"); + } else { + if (S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode)) + weprintf("stdin is open but is not a TTY, character device, FIFO, or socket"); + } + } if (!use_stdin) { libsimple_srand(); } spans = emalloc(sizeof(*spans)); spans[0].start = 0; - spans[0].end = total_size = filesize(fd, argv[0]); + spans[0].end = filesize(fd, argv[0]); spans[0].bad = 0; - spans[0].blocksize = 8192U; + spans[0].blocksize = sizeof(reservoir); + while (spans[0].blocksize & (spans[0].blocksize - 1U)) + spans[0].blocksize &= (spans[0].blocksize - 1U); nspans = 1U; spans_size = 1U; + if (off >= 0) { + if (off > spans[0].end) + eprintf("value of -o flag is beyond the end of the file"); + spans[0].start = off; + } + if (len >= 0) { + if (len > OFF_MAX - spans[0].start) + eprintf("the sum of the values of -o and -l flag is too large"); + end = spans[0].start + len; + if (end > spans[0].end) + eprintf("the sum of the values of -o and -l flag is beyond the end of the file"); + spans[0].end = end; + } else if (end >= 0) { + if (end > spans[0].end) + eprintf("the value of -e flag is beyond the end of the file"); + spans[0].end = end; + } + + total_size = spans[0].end - spans[0].start; humansize1000(total_size, total_size_1000); humansize1024(total_size, total_size_1024); + if (pipe(progress_print_sig_pipe)) + eprintf("pipe:"); + + errno = pthread_mutex_init(&progress_mutex, NULL); + if (errno) + eprintf("pthread_mutex_init NULL:"); + errno = pthread_create(&progress_print_thread, NULL, &progress_print_loop, NULL); + if (errno) + eprintf("pthread_create NULL:"); + + if (clock_gettime(clck, &start_time)) { + clck = CLOCK_MONOTONIC; + clkcstr = "CLOCK_MONOTONIC"; + if (clock_gettime(clck, &start_time)) + eprintf("clock_gettime %s:", clkcstr); + } + write_average_begin_times[0] = start_time; + while (nspans) { size_t old_nspans = nspans; for (i = 0; i < old_nspans; i++) shredspan(fd, &spans[i], argv[0]); - nspans -= old_nspans; - memmove(&spans[0], &spans[old_nspans], nspans * sizeof(*spans)); + for (i = 0, j = nspans, nspans -= old_nspans; i < nspans;) + spans[i++] = spans[--j]; + if (exiting) + break; + direction ^= 1; + pass_nr++; } close(fd); - - print_progress(1); + if (progress_print_sig_pipe[1] >= 0) + close(progress_print_sig_pipe[1]); + + errno = pthread_join(progress_print_thread, NULL); + if (errno) + weprintf("pthread_join:"); + pthread_mutex_destroy(&progress_mutex); + + if (clock_gettime(clck, &now)) + eprintf("clock_gettime %s:", clkcstr); + print_progress(1, &now); + + if (nspans) { + /* TODO document in man page */ + dump_map(STDOUT_FILENO, ""); + if (close(STDOUT_FILENO)) + eprintf("write "); + } return 0; } -- cgit v1.2.3-70-g09d2