]> git.wh0rd.org - home.git/blobdiff - ddnuke.c
cros-board: update
[home.git] / ddnuke.c
index 774830e82eb18efa852ed3636ddb01818079d0d0..02c6c215fa56762074a9675c8c9f3a6d6bcce0b3 100644 (file)
--- a/ddnuke.c
+++ b/ddnuke.c
 #define _LARGEFILE64_SOURCE
 #define _GNU_SOURCE
 
+#include <ctype.h>
+#include <err.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <locale.h>
+#include <spawn.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sysexits.h>
 #include <time.h>
 #include <unistd.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
 #include <linux/fs.h>
 
-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 <dev> [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] <dev>\n"
+               "\n"
+               "Options:\n"
+               " -q            Run quietly\n"
+               " -y            Assume yes to all prompts\n"
+               " -b <size>     How many bytes to write at once. (default: 1Mib)\n"
+               " -f <count>    How many blocks to write before flush (stdout & disk). (default: 32)\n"
+               " -o <offset>   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;
 }