]> git.wh0rd.org Git - home.git/blob - ddnuke.c
gitconfig: new cif & dc helpers
[home.git] / ddnuke.c
1 /*
2  * released into the public domain
3  * written by Mike Frysinger <vapier>
4  */
5
6 #pragma GCC diagnostic warning "-Wall"
7 #pragma GCC diagnostic warning "-Wextra"
8
9 #define _FILE_OFFSET_BITS 64
10 #define _LARGEFILE_SOURCE
11 #define _LARGEFILE64_SOURCE
12 #define _GNU_SOURCE
13
14 #include <ctype.h>
15 #include <err.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <inttypes.h>
19 #include <locale.h>
20 #include <spawn.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sysexits.h>
26 #include <time.h>
27 #include <unistd.h>
28 #include <sys/ioctl.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32 #include <linux/fs.h>
33
34 static unsigned long long mbps(struct timespec *stime, struct timespec *etime, off_t len)
35 {
36         uint64_t dtime;
37         clock_gettime(CLOCK_MONOTONIC, etime);
38         dtime = ((etime->tv_sec - stime->tv_sec) * 1000 * 1000 * 1000) +
39                 (etime->tv_nsec - stime->tv_nsec);
40         *stime = *etime;
41         return 1000 * len / dtime;
42 }
43
44 static off_t get_blk_size(int fd)
45 {
46         uint64_t size;
47         if (ioctl(fd, BLKGETSIZE64, &size))
48                 err(EX_DATAERR, "ioctl(BLKGETSIZE64) failed");
49         return size;
50 }
51
52 static off_t get_size(int fd)
53 {
54         struct stat st;
55
56         if (fstat(fd, &st))
57                 err(EX_DATAERR, "could not stat %i", fd);
58
59         if (S_ISREG(st.st_mode))
60                 return st.st_size;
61         else if (S_ISBLK(st.st_mode))
62                 return get_blk_size(fd);
63
64         errno = EINVAL;
65         err(EX_DATAERR, "unknown type of file");
66 }
67
68 static void nuke(int fd, off_t offset, off_t max_size, unsigned char pattern)
69 {
70         static char mem[1024 * 1024];
71         memset(mem, pattern, sizeof(mem));
72         if (pattern)
73                 printf("Writing 0x%X to the output\n", pattern);
74
75         if (lseek(fd, offset, SEEK_SET) != offset)
76                 err(EX_DATAERR, "lseek(%"PRIu64"u) failed", offset);
77
78         unsigned long long speed = 0;
79         struct timespec stime, etime, itime;
80         clock_gettime(CLOCK_MONOTONIC, &stime);
81         itime = stime;
82
83         off_t pos = offset, last_pos = 0;
84         ssize_t ret;
85         int fsync_pos = 0;
86         while (pos < max_size) {
87                 /* This will round up to sizeof(mem) ... */
88                 ret = write(fd, mem, sizeof(mem));
89                 pos += ret;
90                 if (ret != sizeof(mem)) {
91                         if (pos != max_size)
92                                 printf("\nread() returned %zi (wanted %zu)\n%'llu MB/s over entire run\n",
93                                         ret, sizeof(mem), mbps(&itime, &etime, pos));
94                         return;
95                 }
96
97                 unsigned long long eta = speed ? (max_size - pos) / (speed * 1000 * 1000) : 0;
98                 printf("\r\e[2K%'llu bytes %u%% (%'llu MB/s) (ETA ~%llum%llus)",
99                         (unsigned long long)pos, (unsigned)((pos * 100) / max_size), speed,
100                         eta / 60, eta % 60);
101
102                 if ((++fsync_pos % 32) == 0) {
103                         speed = mbps(&stime, &etime, pos - last_pos);
104                         last_pos = pos;
105                         fflush(stdout);
106                         fsync(fd);
107                 }
108         }
109
110         printf("\n");
111 }
112
113 static void usage(int status)
114 {
115         fprintf(
116                 status ? stderr : stdout,
117                 "Usage: ddnuke [options] <dev>\n"
118                 "\n"
119                 "Options:\n"
120                 " -q            Run quietly\n"
121                 " -y            Assume yes to all prompts\n"
122                 " -o <offset>   Position to start writing\n"
123                 " -r            Write three times: 0x00, then 0xaa, then 0x55\n"
124         );
125         exit(status);
126 }
127
128 int main(int argc, char *argv[])
129 {
130         off_t offset = 0;
131         const char *file;
132         bool quiet = false;
133         bool random = false;
134         bool yes = false;
135         int o;
136
137         setlocale(LC_NUMERIC, "en_US");
138
139         while ((o = getopt(argc, argv, "ho:qry")) != -1) {
140                 switch (o) {
141                 case 'o':
142                         offset = atoll(optarg);
143                         break;
144                 case 'q':
145                         quiet = true;
146                         break;
147                 case 'r':
148                         random = true;
149                         break;
150                 case 'y':
151                         yes = true;
152                         break;
153                 case 'h':
154                         usage(EX_OK);
155                         break;
156                 default:
157                         usage(EX_USAGE);
158                         break;
159                 }
160         }
161         if (argc != optind + 1)
162                 usage(EX_USAGE);
163         file = argv[optind];
164
165         int fd = open(file, O_WRONLY|O_CLOEXEC);
166         if (fd == -1)
167                 err(EX_NOINPUT, "open(%s) failed", file);
168
169         if (!quiet) {
170                 pid_t pid;
171                 const char *argv[] = {"fdisk", "-l", file, NULL};
172                 if (posix_spawnp(&pid, argv[0], NULL, NULL, (char *const *)argv, NULL))
173                         err(EX_OSERR, "spawn(%s %s) failed", argv[0], file);
174                 waitpid(pid, NULL, 0);
175                 printf("\n");
176         }
177
178         if (!yes) {
179                 printf("Do you want to wipe '%s'? (y/N) ", file);
180                 fflush(stdout);
181                 char c = 0;
182                 if (fread(&c, 1, 1, stdin)) {/* don't care */}
183                 if (tolower(c) != 'y')
184                         errx(EX_UNAVAILABLE, "User decided not to wipe");
185         }
186
187         off_t max_size = get_size(fd);
188
189         nuke(fd, offset, max_size, 0x00);
190         if (random) {
191                 nuke(fd, offset, max_size, 0xaa);
192                 nuke(fd, offset, max_size, 0x55);
193         }
194
195         return EX_OK;
196 }