hammer2 - Fix flush issues with unmounted PFSs and shutdown panic
[dragonfly.git] / lib / libtcplay / io.c
blobebac5ac45eab5f02484ce24191db5af1e73baa76
1 /*
2 * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in
13 * the documentation and/or other materials provided with the
14 * distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
30 #include <sys/types.h>
31 #if defined(__DragonFly__)
32 #include <sys/diskslice.h>
33 #elif defined(__linux__)
34 #include <linux/fs.h>
35 #include <sys/ioctl.h>
36 #endif
37 #include <sys/stat.h>
38 #include <sys/uio.h>
39 #include <sys/select.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <termios.h>
46 #include <unistd.h>
47 #include <signal.h>
49 #include "tcplay.h"
51 void *
52 read_to_safe_mem(const char *file, off_t offset, size_t *sz)
54 void *mem = NULL;
55 ssize_t r = 0;
56 int fd;
58 if ((fd = open(file, O_RDONLY)) < 0) {
59 tc_log(1, "Error opening file %s\n", file);
60 return NULL;
63 if ((mem = alloc_safe_mem(*sz)) == NULL) {
64 tc_log(1, "Error allocating memory\n");
65 goto out;
68 if ((lseek(fd, offset, (offset >= 0) ? SEEK_SET : SEEK_END) < 0)) {
69 tc_log(1, "Error seeking on file %s\n", file);
70 goto m_err;
73 if ((r = read(fd, mem, *sz)) <= 0) {
74 tc_log(1, "Error reading from file %s\n", file);
75 goto m_err;
78 out:
79 *sz = r;
80 close(fd);
81 return mem;
82 /* NOT REACHED */
84 m_err:
85 free_safe_mem(mem);
86 close(fd);
87 return NULL;
90 static size_t get_random_total_bytes = 0;
91 static size_t get_random_read_bytes = 0;
94 float
95 get_random_read_progress(void)
97 return (get_random_total_bytes == 0) ? 0.0 :
98 (1.0 * get_random_read_bytes) /
99 (1.0 * get_random_total_bytes) * 100.0;
102 static
103 void
104 get_random_summary(void)
106 float pct_done = get_random_read_progress();
108 tc_log(0, "Gathering true randomness, %.0f%% done.\n", pct_done);
112 get_random(unsigned char *buf, size_t len, int weak)
114 int fd;
115 ssize_t r;
116 size_t rd = 0;
117 size_t sz;
118 struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; /* 10 ms */
121 if ((fd = open((weak) ? "/dev/urandom" : "/dev/random", O_RDONLY)) < 0) {
122 tc_log(1, "Error opening /dev/random\n");
123 return -1;
126 summary_fn = get_random_summary;
127 get_random_total_bytes = len;
128 tc_internal_state = STATE_GET_RANDOM;
130 /* Get random data in 16-byte chunks */
131 sz = 16;
132 while (rd < len) {
133 get_random_read_bytes = rd;
135 if ((len - rd) < sz)
136 sz = (len - rd);
138 if ((r = read(fd, buf+rd, sz)) < 0) {
139 tc_log(1, "Error reading from /dev/random(%d): %s\n",
140 fd, strerror(errno));
141 close(fd);
142 summary_fn = NULL;
143 tc_internal_state = STATE_UNKNOWN;
144 return -1;
146 rd += r;
147 nanosleep(&ts, NULL);
150 close(fd);
151 summary_fn = NULL;
153 tc_internal_state = STATE_UNKNOWN;
154 return 0;
157 static disksz_t secure_erase_total_bytes = 0;
158 static disksz_t secure_erase_erased_bytes = 0;
160 float
161 get_secure_erase_progress(void)
163 return (secure_erase_total_bytes == 0) ? 0.0 :
164 (1.0 * secure_erase_erased_bytes) /
165 (1.0 * secure_erase_total_bytes) * 100.0;
168 static
169 void
170 secure_erase_summary(void)
172 float pct_done = get_secure_erase_progress();
173 tc_log(0, "Securely erasing, %.0f%% done.\n", pct_done);
177 secure_erase(const char *dev, disksz_t bytes, size_t blksz)
179 size_t erased = 0;
180 int fd_rand, fd;
181 char buf[ERASE_BUFFER_SIZE];
182 ssize_t r, w;
183 size_t sz;
185 if (blksz > MAX_BLKSZ) {
186 tc_log(1, "blksz > MAX_BLKSZ\n");
187 return -1;
190 if ((fd_rand = open("/dev/urandom", O_RDONLY)) < 0) {
191 tc_log(1, "Error opening /dev/urandom\n");
192 return -1;
195 if ((fd = open(dev, O_WRONLY)) < 0) {
196 close(fd_rand);
197 tc_log(1, "Error opening %s\n", dev);
198 return -1;
201 summary_fn = secure_erase_summary;
202 secure_erase_total_bytes = bytes;
204 tc_internal_state = STATE_ERASE;
206 sz = ERASE_BUFFER_SIZE;
207 while (erased < bytes) {
208 secure_erase_erased_bytes = erased;
209 /* Switch to block size when not much is remaining */
210 if ((bytes - erased) <= ERASE_BUFFER_SIZE)
211 sz = blksz;
213 if ((r = read(fd_rand, buf, sz)) < 0) {
214 tc_log(1, "Error reading from /dev/urandom\n");
215 close(fd);
216 close(fd_rand);
217 summary_fn = NULL;
218 tc_internal_state = STATE_UNKNOWN;
219 return -1;
222 if (r < (ssize_t)blksz)
223 continue;
225 if ((w = write(fd, buf, r)) < 0) {
226 tc_log(1, "Error writing to %s\n", dev);
227 close(fd);
228 close(fd_rand);
229 summary_fn = NULL;
230 tc_internal_state = STATE_UNKNOWN;
231 return -1;
234 erased += (size_t)w;
237 close(fd);
238 close(fd_rand);
240 summary_fn = NULL;
242 tc_internal_state = STATE_UNKNOWN;
243 return 0;
246 #if defined(__DragonFly__)
248 get_disk_info(const char *dev, disksz_t *blocks, size_t *bsize)
250 struct partinfo pinfo;
251 int fd;
253 if ((fd = open(dev, O_RDONLY)) < 0) {
254 tc_log(1, "Error opening %s\n", dev);
255 return -1;
258 memset(&pinfo, 0, sizeof(struct partinfo));
260 if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
261 close(fd);
262 return -1;
265 *blocks = (disksz_t)pinfo.media_blocks;
266 *bsize = pinfo.media_blksize;
268 close(fd);
269 return 0;
271 #elif defined(__linux__)
273 get_disk_info(const char *dev, disksz_t *blocks, size_t *bsize)
275 uint64_t nbytes;
276 int blocksz;
277 int fd;
279 if ((fd = open(dev, O_RDONLY)) < 0) {
280 tc_log(1, "Error opening %s\n", dev);
281 return -1;
284 if ((ioctl(fd, BLKSSZGET, &blocksz)) < 0) {
285 close(fd);
286 return -1;
289 if ((ioctl(fd, BLKGETSIZE64, &nbytes)) < 0) {
290 close(fd);
291 return -1;
294 *blocks = (disksz_t)(nbytes / blocksz);
295 *bsize = (size_t)(blocksz);
297 close(fd);
298 return 0;
300 #endif
303 write_to_disk(const char *dev, off_t offset, size_t blksz, void *mem,
304 size_t bytes)
306 unsigned char *mem_buf = NULL;
307 ssize_t w;
308 size_t sz;
309 off_t internal_off;
310 int fd;
312 /* Align to block sizes */
313 internal_off = offset % blksz;
314 #ifdef DEBUG
315 printf("offset: %"PRIu64", internal offset: %"PRIu64"\n",
316 (uint64_t)offset, (uint64_t)internal_off);
317 #endif
318 offset = (offset/blksz) * blksz;
320 if ((internal_off + bytes) > blksz) {
321 tc_log(1, "This should never happen: internal_off + bytes > "
322 "blksz (write_to_disk)\n");
323 return -1;
326 if ((bytes < blksz) || (internal_off != 0)) {
327 sz = blksz;
328 if ((mem_buf = read_to_safe_mem(dev, offset, &sz)) == NULL) {
329 tc_log(1, "Error buffering data on "
330 "write_to_disk(%s)\n", dev);
331 return -1;
334 memcpy(mem_buf + internal_off, mem, bytes);
337 if ((fd = open(dev, O_WRONLY)) < 0) {
338 tc_log(1, "Error opening device %s\n", dev);
339 return -1;
342 if ((lseek(fd, offset, (offset >= 0) ? SEEK_SET : SEEK_END) < 0)) {
343 tc_log(1, "Error seeking on device %s\n", dev);
344 close(fd);
345 return -1;
348 if ((w = write(fd, (mem_buf != NULL) ? mem_buf : mem, bytes)) <= 0) {
349 tc_log(1, "Error writing to device %s\n", dev);
350 close(fd);
351 return -1;
354 close(fd);
356 if (mem_buf != NULL)
357 free_safe_mem(mem_buf);
358 return 0;
363 write_to_file(const char *file, void *mem, size_t bytes)
365 int fd;
366 ssize_t w;
368 if ((fd = open(file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
369 tc_log(1, "Error opening file %s\n", file);
370 return -1;
373 if ((w = write(fd, mem, bytes)) < 0) {
374 tc_log(1, "Error writing to file %s\n", file);
375 close(fd);
376 return -1;
379 close(fd);
380 return 0;
384 static struct termios termios_old;
385 static int tty_fd;
387 static void sigint_termios(int sa)
389 tcsetattr(tty_fd, TCSAFLUSH, &termios_old);
390 exit(sa);
394 read_passphrase(const char *prompt, char *pass, size_t passlen, size_t bufsz, time_t timeout)
396 struct termios termios_new;
397 struct timeval to;
398 fd_set fds;
399 ssize_t n;
400 int fd = STDIN_FILENO, r = 0, nready;
401 struct sigaction act, old_act;
402 int is_tty = isatty(fd);
404 if (is_tty == 0)
405 errno = 0;
407 memset(pass, 0, bufsz);
409 printf("%s", prompt);
410 fflush(stdout);
412 /* If input is being provided by something which is not a terminal, don't
413 * change the settings. */
414 if (is_tty) {
415 tcgetattr(fd, &termios_old);
416 memcpy(&termios_new, &termios_old, sizeof(termios_new));
417 termios_new.c_lflag &= ~ECHO;
419 act.sa_handler = sigint_termios;
420 act.sa_flags = SA_RESETHAND;
421 sigemptyset(&act.sa_mask);
423 tty_fd = fd;
424 sigaction(SIGINT, &act, &old_act);
426 tcsetattr(fd, TCSAFLUSH, &termios_new);
429 if (timeout > 0) {
430 memset(&to, 0, sizeof(to));
431 to.tv_sec = timeout;
433 FD_ZERO(&fds);
434 FD_SET(fd, &fds);
435 nready = select(fd + 1, &fds, NULL, NULL, &to);
436 if (nready <= 0) {
437 r = EINTR;
438 goto out;
442 n = read(fd, pass, bufsz-1);
443 if (n > 0) {
444 pass[n-1] = '\0'; /* Strip trailing \n */
445 } else {
446 r = EIO;
449 /* Warn about passphrase trimming */
450 if (strlen(pass) > MAX_PASSSZ)
451 tc_log(0, "WARNING: Passphrase is being trimmed to %zu "
452 "characters, discarding rest.\n", passlen);
454 /* Cut off after passlen characters */
455 pass[passlen] = '\0';
457 out:
458 if (is_tty) {
459 tcsetattr(fd, TCSAFLUSH, &termios_old);
460 putchar('\n');
462 sigaction(SIGINT, &old_act, NULL);
465 return r;