X-Git-Url: https://git.wh0rd.org/?p=home.git;a=blobdiff_plain;f=ddnuke.c;h=1712d16b6f0e0c4ab49a3cf59aef9d9d0f12d1b2;hp=774830e82eb18efa852ed3636ddb01818079d0d0;hb=HEAD;hpb=e5e72e456dd1418b083e3feed94356231851f046 diff --git a/ddnuke.c b/ddnuke.c index 774830e..02c6c21 100644 --- a/ddnuke.c +++ b/ddnuke.c @@ -11,27 +11,26 @@ #define _LARGEFILE64_SOURCE #define _GNU_SOURCE +#include +#include #include #include #include #include +#include +#include #include #include #include +#include #include #include #include #include +#include +#include #include -static const char zero[1024 * 1024]; - -#define errp(msg, args...) \ - do { \ - printf("%s: " msg ": %m\n", program_invocation_short_name, ## args); \ - exit(1); \ - } while (0) - static unsigned long long mbps(struct timespec *stime, struct timespec *etime, off_t len) { uint64_t dtime; @@ -46,7 +45,7 @@ static off_t get_blk_size(int fd) { uint64_t size; if (ioctl(fd, BLKGETSIZE64, &size)) - errp("ioctl(BLKGETSIZE64) failed"); + err(EX_DATAERR, "ioctl(BLKGETSIZE64) failed"); return size; } @@ -55,7 +54,7 @@ static off_t get_size(int fd) struct stat st; if (fstat(fd, &st)) - errp("could not stat %i", fd); + err(EX_DATAERR, "could not stat %i", fd); if (S_ISREG(st.st_mode)) return st.st_size; @@ -63,35 +62,18 @@ static off_t get_size(int fd) return get_blk_size(fd); errno = EINVAL; - errp("unknown type of file"); + err(EX_DATAERR, "unknown type of file"); } -int main(int argc, char *argv[]) +static void nuke(int fd, off_t block_size, off_t offset, off_t max_size, off_t flush_count, unsigned char pattern) { - off_t offset = 0; - const char *file; - - setlocale(LC_NUMERIC, "en_US"); - - switch (argc) { - case 3: - offset = atoll(argv[2]); - case 2: - file = argv[1]; - break; - default: - puts("Usage: ddnuke [offset in bytes]"); - return 1; - } - - int fd = open(file, O_WRONLY); - if (fd == -1) - errp("open(%s) failed", file); - - off_t max_size = get_size(fd); + char mem[block_size]; + memset(mem, pattern, sizeof(mem)); + if (pattern) + printf("Writing 0x%X to the output\n", pattern); if (lseek(fd, offset, SEEK_SET) != offset) - errp("lseek(%"PRIu64"u) failed", offset); + err(EX_DATAERR, "lseek(%"PRIu64"u) failed", offset); unsigned long long speed = 0; struct timespec stime, etime, itime; @@ -102,18 +84,22 @@ int main(int argc, char *argv[]) ssize_t ret; int fsync_pos = 0; while (pos < max_size) { - ret = write(fd, zero, sizeof(zero)); + /* This will round up to sizeof(mem) ... */ + ret = write(fd, mem, sizeof(mem)); pos += ret; - if (ret != sizeof(zero)) { - printf("\ngot back %zi (wanted %zu)\n%'llu MB/s over entire run\n", - ret, sizeof(zero), mbps(&itime, &etime, pos)); - return 0; + if (ret != block_size) { + if (pos != max_size) + printf("\nread() returned %zi (wanted %zu)\n%'llu MB/s over entire run\n", + ret, sizeof(mem), mbps(&itime, &etime, pos)); + return; } - printf("%'llu bytes %u%% (%'llu MB/s)%20s\r", (unsigned long long)pos, - (unsigned)((pos * 100) / max_size), speed, ""); + unsigned long long eta = speed ? (max_size - pos) / (speed * 1000 * 1000) : 0; + printf("\r%'llu bytes %u%% (%'llu MB/s) (ETA ~%llum%llus)\e[K", + (unsigned long long)pos, (unsigned)((pos * 100) / max_size), speed, + eta / 60, eta % 60); - if ((++fsync_pos % 16) == 0) { + if ((++fsync_pos % flush_count) == 0) { speed = mbps(&stime, &etime, pos - last_pos); last_pos = pos; fflush(stdout); @@ -122,5 +108,99 @@ int main(int argc, char *argv[]) } printf("\n"); - return 0; +} + +static void usage(int status) +{ + fprintf( + status ? stderr : stdout, + "Usage: ddnuke [options] \n" + "\n" + "Options:\n" + " -q Run quietly\n" + " -y Assume yes to all prompts\n" + " -b How many bytes to write at once. (default: 1Mib)\n" + " -f How many blocks to write before flush (stdout & disk). (default: 32)\n" + " -o Position to start writing\n" + " -r Write three times: 0x00, then 0xaa, then 0x55\n" + ); + exit(status); +} + +int main(int argc, char *argv[]) +{ + off_t block_size = 1024 * 1024; + off_t flush_count = 32; + off_t offset = 0; + const char *file; + bool quiet = false; + bool random = false; + bool yes = false; + int o; + + setlocale(LC_NUMERIC, "en_US"); + + while ((o = getopt(argc, argv, "hb:f:o:qry")) != -1) { + switch (o) { + case 'b': + block_size = atoll(optarg); + break; + case 'f': + flush_count = atoll(optarg); + break; + case 'o': + offset = atoll(optarg); + break; + case 'q': + quiet = true; + break; + case 'r': + random = true; + break; + case 'y': + yes = true; + break; + case 'h': + usage(EX_OK); + break; + default: + usage(EX_USAGE); + break; + } + } + if (argc != optind + 1) + usage(EX_USAGE); + file = argv[optind]; + + int fd = open(file, O_WRONLY|O_CLOEXEC); + if (fd == -1) + err(EX_NOINPUT, "open(%s) failed", file); + + if (!quiet) { + pid_t pid; + const char *argv[] = {"fdisk", "-l", file, NULL}; + if (posix_spawnp(&pid, argv[0], NULL, NULL, (char *const *)argv, NULL)) + err(EX_OSERR, "spawn(%s %s) failed", argv[0], file); + waitpid(pid, NULL, 0); + printf("\n"); + } + + if (!yes) { + printf("Do you want to wipe '%s'? (y/N) ", file); + fflush(stdout); + char c = 0; + if (fread(&c, 1, 1, stdin)) {/* don't care */} + if (tolower(c) != 'y') + errx(EX_UNAVAILABLE, "User decided not to wipe"); + } + + off_t max_size = get_size(fd); + + nuke(fd, block_size, offset, max_size, flush_count, 0x00); + if (random) { + nuke(fd, block_size, offset, max_size, flush_count, 0xaa); + nuke(fd, block_size, offset, max_size, flush_count, 0x55); + } + + return EX_OK; }