aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore15
-rw-r--r--LICENSE15
-rw-r--r--Makefile37
-rw-r--r--TODO9
-rw-r--r--config.mk8
-rw-r--r--deadshred.c231
6 files changed, 315 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a0cae35
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,15 @@
+*\#*
+*~
+*.o
+*.a
+*.lo
+*.su
+*.so
+*.so.*
+*.dll
+*.dylib
+*.gch
+*.gcov
+*.gcno
+*.gcda
+/deadshred
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..fccd785
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+© 2024 Mattias Andrée <maandree@kth.se>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1e91bbd
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,37 @@
+.POSIX:
+
+CONFIGFILE = config.mk
+include $(CONFIGFILE)
+
+OBJ =\
+ deadshred.o
+
+HDR =
+
+all: deadshred
+$(OBJ): $(HDR)
+
+.c.o:
+ $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS)
+
+deadshred: $(OBJ)
+ $(CC) -o $@ $(OBJ) $(LDFLAGS)
+
+install: deadshred
+ mkdir -p -- "$(DESTDIR)$(PREFIX)/bin"
+ mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man1/"
+ cp -- deadshred "$(DESTDIR)$(PREFIX)/bin/"
+ cp -- deadshred.1 "$(DESTDIR)$(MANPREFIX)/man1/"
+
+uninstall:
+ -rm -f -- "$(DESTDIR)$(PREFIX)/bin/deadshred"
+ -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/deadshred.1"
+
+clean:
+ -rm -f -- *.o *.a *.lo *.su *.so *.so.* *.gch *.gcov *.gcno *.gcda
+ -rm -f -- deadshred
+
+.SUFFIXES:
+.SUFFIXES: .o .c
+
+.PHONY: all install uninstall clean
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..1aede57
--- /dev/null
+++ b/TODO
@@ -0,0 +1,9 @@
+Add README
+Add man page
+Add `-n iterations`
+Add shred map for continuing later (print to stdout on SIGTERM)
+
+Enhance progress printout with:
+ exact bytes count
+ bad sections (count, bytes)
+ shredded bytes per seconds
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..aeb8cec
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,8 @@
+PREFIX = /usr
+MANPREFIX = $(PREFIX)/share/man
+
+CC = c99
+
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE
+CFLAGS =
+LDFLAGS = -lsimple
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;
+}