aboutsummaryrefslogtreecommitdiffstats
path: root/deadshred.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--deadshred.c231
1 files changed, 231 insertions, 0 deletions
diff --git a/deadshred.c b/deadshred.c
new file mode 100644
index 0000000..5323d15
--- /dev/null
+++ b/deadshred.c
@@ -0,0 +1,231 @@
+/* See LICENSE file for copyright and license details. */
+#include <sys/mount.h>
+#include <libsimple.h>
+#include <libsimple-arg.h>
+
+
+USAGE("file");
+
+
+struct span {
+ off_t start;
+ off_t end;
+ size_t blocksize;
+};
+
+
+static struct span *spans = NULL;
+static size_t nspans = 0;
+static size_t spans_size = 0;
+
+static off_t shredded = 0;
+static off_t total_size = 0;
+
+static char reservoir[128U << 10];
+static size_t reservoir_off = sizeof(reservoir);
+
+static char total_size_1000[256];
+static char total_size_1024[256];
+
+
+static off_t
+filesize(int fd, const char *fname)
+{
+ struct stat st;
+
+ if (fstat(fd, &st))
+ eprintf("fstat %s:", fname);
+
+ switch (st.st_mode & S_IFMT) {
+ case S_IFREG:
+ break;
+ case S_IFBLK:
+ if (ioctl(fd, BLKGETSIZE64, &st.st_size) < 0)
+ eprintf("ioctl %s BLKGETSIZE64:", fname);
+ break;
+ default:
+ eprintf("%s: not a regular file or block device", fname);
+ }
+
+ return st.st_size;
+}
+
+
+static void
+ensure_random(size_t needed)
+{
+ size_t off;
+ ssize_t r;
+
+ if (sizeof(reservoir) - reservoir_off >= needed)
+ return;
+
+ for (off = 0; off < reservoir_off;) {
+ r = read(STDIN_FILENO, &reservoir[off], reservoir_off - off);
+ if (r <= 0) {
+ if (!r)
+ eprintf("random source depleted");
+ if (errno == EINTR)
+ continue;
+ eprintf("read <stdin>:");
+ }
+ off += (size_t)r;
+ }
+
+ reservoir_off = 0;
+}
+
+
+static off_t
+add_span(off_t off, off_t max_end, size_t blocksize)
+{
+ off_t end;
+
+ end = MIN((off | ((off_t)blocksize - 1)) + 1, max_end);
+ while ((off_t)(blocksize >> 1) >= end - off)
+ blocksize >>= 1;
+
+ if (nspans == spans_size) {
+ spans_size += 1024;
+ spans = ereallocarray(spans, spans_size, sizeof(*spans));
+ }
+
+ spans[nspans].start = off;
+ spans[nspans].end = end;
+ spans[nspans].blocksize = (size_t)blocksize;
+ nspans++;
+}
+
+
+static char *
+humansize1000(off_t s, char *buf)
+{
+ const char *units = "kMGTPEZYRQ";
+ size_t unit = 0;
+ if (s < 1000) {
+ sprintf(buf, "%u B", (unsigned)s);
+ return buf;
+ }
+ s /= 100;
+ while (units[unit + 1U] && s >= 10000) {
+ s /= 1000;
+ unit++;
+ }
+ sprintf(buf, "%u.%u %cB", (unsigned)s / 10U, (unsigned)s % 10U, units[unit]);
+ return buf;
+}
+
+
+static char *
+humansize1024(off_t s, char *buf)
+{
+ const char *units = "KMGTPEZYRQ";
+ size_t unit = 0;
+ if (s < 1024) {
+ sprintf(buf, "%u B", (unsigned)s);
+ return buf;
+ }
+ while (units[unit + 1U] && s >= 1024 * 1024) {
+ s /= 1024;
+ unit++;
+ }
+ sprintf(buf, "%lu.%u %ciB", (unsigned long int)s / 1024U, (unsigned long int)((s * 10 % 10240) / 1024U), units[unit]);
+ return buf;
+}
+
+
+static int
+print_progress(int done)
+{
+ static char buf1[1024] = {0};
+ static char buf2[1024] = {0};
+ static int i = 0;
+
+ char subbuf1[256];
+ char subbuf2[256];
+
+ sprintf(i == 0 ? buf1 : buf2,
+ "%.2lf %% (%s, %s) of %s (%s) shredded\033[K\n%s",
+ 100. * (double)shredded / (double)total_size,
+ humansize1000(shredded, subbuf1),
+ humansize1024(shredded, subbuf2),
+ total_size_1000,
+ total_size_1024,
+ done ? "" : "\033[A");
+
+ if (strcmp(buf1, buf2)) {
+ fprintf(stderr, "%s", i == 0 ? buf1 : buf2);
+ fflush(stderr);
+ }
+
+ i ^= 1;
+}
+
+
+static void
+shredspan(int fd, const struct span *span, const char *fname)
+{
+ off_t off;
+ ssize_t r;
+
+ for (off = span->start; off < span->end;) {
+ print_progress(0);
+ 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 (r < 0) {
+ if (errno != EINTR)
+ off = add_span(off, span->end, span->blocksize == 1U ? 1U : span->blocksize);
+ continue;
+ }
+ off += (off_t)r;
+ shredded += (off_t)r;
+ reservoir_off += (size_t)r;
+ }
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ char buf1[256];
+ char buf2[256];
+ size_t i;
+ int fd;
+
+ ARGBEGIN {
+ default:
+ usage();
+ } ARGEND;
+
+ if (argc != 1)
+ usage();
+
+ fd = open(argv[0], O_WRONLY);
+ if (fd < 0)
+ eprintf("open %s O_WRONLY:", argv[0]);
+
+ spans = emalloc(sizeof(*spans));
+ spans[0].start = 0;
+ spans[0].end = total_size = filesize(fd, argv[0]);
+ spans[0].blocksize = 8192U;
+ nspans = 1U;
+ spans_size = 1U;
+
+ humansize1000(total_size, total_size_1000);
+ humansize1024(total_size, total_size_1024);
+
+ 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));
+ }
+
+ close(fd);
+
+ print_progress(1);
+ return 0;
+}