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