tcplay - update to version 0.9
[dragonfly.git] / lib / libtcplay / io.c
1 /*
2  * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
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.
15  *
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.
28  */
29
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/uio.h>
38 #include <sys/select.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <termios.h>
45 #include <unistd.h>
46
47 #include "tcplay.h"
48
49 void *
50 read_to_safe_mem(const char *file, off_t offset, size_t *sz)
51 {
52         void *mem = NULL;
53         ssize_t r = 0;
54         int fd;
55
56         if ((fd = open(file, O_RDONLY)) < 0) {
57                 tc_log(1, "Error opening file %s\n", file);
58                 return NULL;
59         }
60
61         if ((mem = alloc_safe_mem(*sz)) == NULL) {
62                 tc_log(1, "Error allocating memory\n");
63                 goto out;
64         }
65
66         if ((lseek(fd, offset, SEEK_SET) < 0)) {
67                 tc_log(1, "Error seeking on file %s\n", file);
68                 goto m_err;
69         }
70
71         if ((r = read(fd, mem, *sz)) <= 0) {
72                 tc_log(1, "Error reading from file %s\n", file);
73                 goto m_err;
74         }
75
76 out:
77         *sz = r;
78         close(fd);
79         return mem;
80         /* NOT REACHED */
81
82 m_err:
83         free_safe_mem(mem);
84         close(fd);
85         return NULL;
86 }
87
88 static size_t get_random_total_bytes = 0;
89 static size_t get_random_read_bytes = 0;
90
91 static
92 void
93 get_random_summary(void)
94 {
95         float pct_done;
96
97         pct_done = (1.0 * get_random_read_bytes) /
98             (1.0 * get_random_total_bytes) * 100.0;
99         tc_log(0, "Gathering true randomness, %.0f%% done.\n", pct_done);
100 }
101
102 int
103 get_random(unsigned char *buf, size_t len)
104 {
105         int fd;
106         ssize_t r;
107         size_t rd = 0;
108         size_t sz;
109         struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; /* 10 ms */
110
111
112         if ((fd = open("/dev/random", O_RDONLY)) < 0) {
113                 tc_log(1, "Error opening /dev/random\n");
114                 return -1;
115         }
116
117         summary_fn = get_random_summary;
118         get_random_total_bytes = len;
119
120         /* Get random data in 16-byte chunks */
121         sz = 16;
122         while (rd < len) {
123                 get_random_read_bytes = rd;
124
125                 if ((len - rd) < sz)
126                         sz = (len - rd);
127
128                 if ((r = read(fd, buf+rd, sz)) < 0) {
129                         tc_log(1, "Error reading from /dev/random(%d): %s\n",
130                             fd, strerror(errno));
131                         close(fd);
132                         summary_fn = NULL;
133                         return -1;
134                 }
135                 rd += r;
136                 nanosleep(&ts, NULL);
137         }
138
139         close(fd);
140         summary_fn = NULL;
141
142         return 0;
143 }
144
145 static size_t secure_erase_total_bytes = 0;
146 static size_t secure_erase_erased_bytes = 0;
147
148 static
149 void
150 secure_erase_summary(void)
151 {
152         float pct_done;
153
154         pct_done = (1.0 * secure_erase_erased_bytes) /
155             (1.0 * secure_erase_total_bytes) * 100.0;
156         tc_log(0, "Securely erasing, %.0f%% done.\n", pct_done);
157 }
158
159 int
160 secure_erase(const char *dev, size_t bytes, size_t blksz)
161 {
162         size_t erased = 0;
163         int fd_rand, fd;
164         char buf[ERASE_BUFFER_SIZE];
165         ssize_t r, w;
166         size_t sz;
167
168         if (blksz > MAX_BLKSZ) {
169                 tc_log(1, "blksz > MAX_BLKSZ\n");
170                 return -1;
171         }
172
173         if ((fd_rand = open("/dev/urandom", O_RDONLY)) < 0) {
174                 tc_log(1, "Error opening /dev/urandom\n");
175                 return -1;
176         }
177
178         if ((fd = open(dev, O_WRONLY)) < 0) {
179                 close(fd_rand);
180                 tc_log(1, "Error opening %s\n", dev);
181                 return -1;
182         }
183
184         summary_fn = secure_erase_summary;
185         secure_erase_total_bytes = bytes;
186
187         sz = ERASE_BUFFER_SIZE;
188         while (erased < bytes) {
189                 secure_erase_erased_bytes = erased;
190                 /* Switch to block size when not much is remaining */
191                 if ((bytes - erased) <= ERASE_BUFFER_SIZE)
192                         sz = blksz;
193
194                 if ((r = read(fd_rand, buf, sz)) < 0) {
195                         tc_log(1, "Error reading from /dev/urandom\n");
196                         close(fd);
197                         close(fd_rand);
198                         summary_fn = NULL;
199                         return -1;
200                 }
201
202                 if (r < (ssize_t)blksz)
203                         continue;
204
205                 if ((w = write(fd, buf, r)) < 0) {
206                         tc_log(1, "Error writing to %s\n", dev);
207                         close(fd);
208                         close(fd_rand);
209                         summary_fn = NULL;
210                         return -1;
211                 }
212
213                 erased += (size_t)w;
214         }
215
216         close(fd);
217         close(fd_rand);
218
219         summary_fn = NULL;
220
221         return 0;
222 }
223
224 #if defined(__DragonFly__)
225 int
226 get_disk_info(const char *dev, size_t *blocks, size_t *bsize)
227 {
228         struct partinfo pinfo;
229         int fd;
230
231         if ((fd = open(dev, O_RDONLY)) < 0) {
232                 tc_log(1, "Error opening %s\n", dev);
233                 return -1;
234         }
235
236         memset(&pinfo, 0, sizeof(struct partinfo));
237
238         if (ioctl(fd, DIOCGPART, &pinfo) < 0) {
239                 close(fd);
240                 return -1;
241         }
242
243         *blocks = pinfo.media_blocks;
244         *bsize = pinfo.media_blksize;
245
246         close(fd);
247         return 0;
248 }
249 #elif defined(__linux__)
250 int
251 get_disk_info(const char *dev, size_t *blocks, size_t *bsize)
252 {
253         uint64_t nbytes;
254         int blocksz;
255         int fd;
256
257         if ((fd = open(dev, O_RDONLY)) < 0) {
258                 tc_log(1, "Error opening %s\n", dev);
259                 return -1;
260         }
261
262         if ((ioctl(fd, BLKSSZGET, &blocksz)) < 0) {
263                 close(fd);
264                 return -1;
265         }
266
267         if ((ioctl(fd, BLKGETSIZE64, &nbytes)) < 0) {
268                 close(fd);
269                 return -1;
270         }
271
272         *blocks = (size_t)(nbytes / blocksz);
273         *bsize = (size_t)(blocksz);
274
275         close(fd);
276         return 0;
277 }
278 #endif
279
280 int
281 write_to_disk(const char *dev, off_t offset, size_t blksz, void *mem,
282     size_t bytes)
283 {
284         unsigned char *mem_buf = NULL;
285         ssize_t w;
286         size_t sz;
287         off_t internal_off;
288         int fd;
289
290         /* Align to block sizes */
291         internal_off = offset % blksz;
292 #ifdef DEBUG
293         printf("offset: %"PRIu64", internal offset: %"PRIu64"\n",
294             (uint64_t)offset, (uint64_t)internal_off);
295 #endif
296         offset = (offset/blksz) * blksz;
297
298         if ((internal_off + bytes) > blksz) {
299                 tc_log(1, "This should never happen: internal_off + bytes > "
300                     "blksz (write_to_disk)\n");
301                 return -1;
302         }
303
304         if ((bytes < blksz) || (internal_off != 0)) {
305                 sz = blksz;
306                 if ((mem_buf = read_to_safe_mem(dev, offset, &sz)) == NULL) {
307                         tc_log(1, "Error buffering data on "
308                             "write_to_disk(%s)\n", dev);
309                         return -1;
310                 }
311
312                 memcpy(mem_buf + internal_off, mem, bytes);
313         }
314
315         if ((fd = open(dev, O_WRONLY)) < 0) {
316                 tc_log(1, "Error opening device %s\n", dev);
317                 return -1;
318         }
319
320         if ((lseek(fd, offset, SEEK_SET) < 0)) {
321                 tc_log(1, "Error seeking on device %s\n", dev);
322                 close(fd);
323                 return -1;
324         }
325
326         if ((w = write(fd, (mem_buf != NULL) ? mem_buf : mem, bytes)) <= 0) {
327                 tc_log(1, "Error writing to device %s\n", dev);
328                 close(fd);
329                 return -1;
330         }
331
332         close(fd);
333
334         if (mem_buf != NULL)
335                 free_safe_mem(mem_buf);
336         return 0;
337 }
338
339 int
340 read_passphrase(const char *prompt, char *pass, size_t passlen, time_t timeout)
341 {
342         struct termios termios_old, termios_new;
343         struct timeval to;
344         fd_set fds;
345         ssize_t n;
346         int fd, r = 0, cfd = 0, nready;
347
348         if ((fd = open("/dev/tty", O_RDONLY)) == -1) {
349                 fd = STDIN_FILENO;
350                 cfd = 1;
351         }
352
353         printf(prompt);
354         fflush(stdout);
355
356         memset(pass, 0, passlen);
357
358         tcgetattr(fd, &termios_old);
359         memcpy(&termios_new, &termios_old, sizeof(termios_new));
360         termios_new.c_lflag &= ~ECHO;
361         tcsetattr(fd, TCSAFLUSH, &termios_new);
362
363         if (timeout > 0) {
364                 memset(&to, 0, sizeof(to));
365                 to.tv_sec = timeout;
366
367                 FD_ZERO(&fds);
368                 FD_SET(fd, &fds);
369                 nready = select(fd + 1, &fds, NULL, NULL, &to);
370                 if (nready <= 0) {
371                         r = EINTR;
372                         goto out;
373                 }
374         }
375
376         n = read(fd, pass, passlen-1);
377         if (n > 0) {
378                 pass[n-1] = '\0'; /* Strip trailing \n */
379         } else {
380                 r = EIO;
381         }
382
383 out:
384         if (cfd)
385                 close(fd);
386
387         tcsetattr(fd, TCSAFLUSH, &termios_old);
388         putchar('\n');
389
390         return r;
391 }