aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <maandree@kth.se>2024-09-19 16:24:07 +0200
committerMattias Andrée <maandree@kth.se>2024-09-19 16:24:07 +0200
commite17bc0f9444e1e7c150c5023901d5b55875dd6af (patch)
treef4f52b920d98400d4f56ebec8c535b7fbb95e907
parentPrint info about bad writes (diff)
downloaddeadshred-e17bc0f9444e1e7c150c5023901d5b55875dd6af.tar.gz
deadshred-e17bc0f9444e1e7c150c5023901d5b55875dd6af.tar.bz2
deadshred-e17bc0f9444e1e7c150c5023901d5b55875dd6af.tar.xz
misc
Signed-off-by: Mattias Andrée <maandree@kth.se>
-rw-r--r--README17
-rw-r--r--TODO7
-rw-r--r--config.mk6
-rw-r--r--deadshred.163
-rw-r--r--deadshred.c651
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 <sys/mount.h>
+#include <pthread.h>
#include <libsimple.h>
#include <libsimple-arg.h>
-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 <linux/kd.h>
+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 <internal pipe>:");
+ }
+ 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 <buffer> %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=<function>, .sa_mask={}, .sa_flags=0} NULL:");
+ if (sigaction(SIGINT, &sa, NULL))
+ eprintf("sigaction SIGINT {.sa_handler=<function>, .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 <stdin>:");
+ } 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, "<stdout>");
+ if (close(STDOUT_FILENO))
+ eprintf("write <stdout>");
+ }
return 0;
}