From ad6c9cbcd8548425e9db258e51906b569eb46c7f Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Tue, 17 Sep 2024 18:25:35 +0200 Subject: First commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- deadshred.c | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 deadshred.c (limited to 'deadshred.c') 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 +#include +#include + + +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 :"); + } + 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; +} -- cgit v1.2.3-70-g09d2