1 |
|
|
/* $OpenBSD: bioctl.c,v 1.131 2016/05/13 19:06:52 tedu Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2004, 2005 Marco Peereboom |
5 |
|
|
* All rights reserved. |
6 |
|
|
* |
7 |
|
|
* Redistribution and use in source and binary forms, with or without |
8 |
|
|
* modification, are permitted provided that the following conditions |
9 |
|
|
* are met: |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer in the |
14 |
|
|
* documentation and/or other materials provided with the distribution. |
15 |
|
|
* |
16 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND |
17 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
18 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
19 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR |
20 |
|
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
21 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
22 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
23 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
24 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
25 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
26 |
|
|
* SUCH DAMAGE. |
27 |
|
|
* |
28 |
|
|
*/ |
29 |
|
|
|
30 |
|
|
#include <sys/param.h> /* NODEV */ |
31 |
|
|
#include <sys/ioctl.h> |
32 |
|
|
#include <sys/dkio.h> |
33 |
|
|
#include <sys/stat.h> |
34 |
|
|
#include <dev/softraidvar.h> |
35 |
|
|
#include <dev/biovar.h> |
36 |
|
|
|
37 |
|
|
#include <errno.h> |
38 |
|
|
#include <err.h> |
39 |
|
|
#include <fcntl.h> |
40 |
|
|
#include <util.h> |
41 |
|
|
#include <ctype.h> |
42 |
|
|
#include <stdio.h> |
43 |
|
|
#include <stdlib.h> |
44 |
|
|
#include <string.h> |
45 |
|
|
#include <unistd.h> |
46 |
|
|
#include <limits.h> |
47 |
|
|
#include <vis.h> |
48 |
|
|
#include <readpassphrase.h> |
49 |
|
|
|
50 |
|
|
struct locator { |
51 |
|
|
int channel; |
52 |
|
|
int target; |
53 |
|
|
int lun; |
54 |
|
|
}; |
55 |
|
|
|
56 |
|
|
struct timing { |
57 |
|
|
int interval; |
58 |
|
|
int start; |
59 |
|
|
}; |
60 |
|
|
|
61 |
|
|
void usage(void); |
62 |
|
|
const char *str2locator(const char *, struct locator *); |
63 |
|
|
const char *str2patrol(const char *, struct timing *); |
64 |
|
|
void bio_status(struct bio_status *); |
65 |
|
|
int bio_parse_devlist(char *, dev_t *); |
66 |
|
|
void bio_kdf_derive(struct sr_crypto_kdfinfo *, |
67 |
|
|
struct sr_crypto_kdf_pbkdf2 *, char *, int); |
68 |
|
|
void bio_kdf_generate(struct sr_crypto_kdfinfo *); |
69 |
|
|
void derive_key_pkcs(int, u_int8_t *, size_t, u_int8_t *, |
70 |
|
|
size_t, char *, int); |
71 |
|
|
|
72 |
|
|
void bio_inq(char *); |
73 |
|
|
void bio_alarm(char *); |
74 |
|
|
int bio_getvolbyname(char *); |
75 |
|
|
void bio_setstate(char *, int, char *); |
76 |
|
|
void bio_setblink(char *, char *, int); |
77 |
|
|
void bio_blink(char *, int, int); |
78 |
|
|
void bio_createraid(u_int16_t, char *, char *); |
79 |
|
|
void bio_deleteraid(char *); |
80 |
|
|
void bio_changepass(char *); |
81 |
|
|
u_int32_t bio_createflags(char *); |
82 |
|
|
char *bio_vis(char *); |
83 |
|
|
void bio_diskinq(char *); |
84 |
|
|
void bio_patrol(char *); |
85 |
|
|
|
86 |
|
|
int devh = -1; |
87 |
|
|
int human; |
88 |
|
|
int verbose; |
89 |
|
|
u_int32_t cflags = 0; |
90 |
|
|
int rflag = 8192; |
91 |
|
|
char *password; |
92 |
|
|
|
93 |
|
|
void *bio_cookie; |
94 |
|
|
|
95 |
|
|
int rpp_flag = RPP_REQUIRE_TTY; |
96 |
|
|
|
97 |
|
|
int |
98 |
|
|
main(int argc, char *argv[]) |
99 |
|
|
{ |
100 |
|
|
struct bio_locate bl; |
101 |
|
|
extern char *optarg; |
102 |
|
|
u_int64_t func = 0; |
103 |
|
|
char *devicename = NULL; |
104 |
|
|
char *realname = NULL, *al_arg = NULL; |
105 |
|
|
char *bl_arg = NULL, *dev_list = NULL; |
106 |
|
|
char *key_disk = NULL; |
107 |
|
|
const char *errstr; |
108 |
|
|
int ch, blink = 0, changepass = 0, diskinq = 0; |
109 |
|
|
int ss_func = 0; |
110 |
|
|
u_int16_t cr_level = 0; |
111 |
|
|
int biodev = 0; |
112 |
|
|
|
113 |
|
|
if (argc < 2) |
114 |
|
|
usage(); |
115 |
|
|
|
116 |
|
|
while ((ch = getopt(argc, argv, "a:b:C:c:dH:hik:l:O:Pp:qr:R:st:u:v")) != |
117 |
|
|
-1) { |
118 |
|
|
switch (ch) { |
119 |
|
|
case 'a': /* alarm */ |
120 |
|
|
func |= BIOC_ALARM; |
121 |
|
|
al_arg = optarg; |
122 |
|
|
break; |
123 |
|
|
case 'b': /* blink */ |
124 |
|
|
func |= BIOC_BLINK; |
125 |
|
|
blink = BIOC_SBBLINK; |
126 |
|
|
bl_arg = optarg; |
127 |
|
|
break; |
128 |
|
|
case 'C': /* creation flags */ |
129 |
|
|
cflags = bio_createflags(optarg); |
130 |
|
|
break; |
131 |
|
|
case 'c': /* create */ |
132 |
|
|
func |= BIOC_CREATERAID; |
133 |
|
|
if (isdigit((unsigned char)*optarg)) { |
134 |
|
|
cr_level = strtonum(optarg, 0, 10, &errstr); |
135 |
|
|
if (errstr != NULL) |
136 |
|
|
errx(1, "Invalid RAID level"); |
137 |
|
|
} else |
138 |
|
|
cr_level = *optarg; |
139 |
|
|
break; |
140 |
|
|
case 'd': |
141 |
|
|
/* delete volume */ |
142 |
|
|
func |= BIOC_DELETERAID; |
143 |
|
|
break; |
144 |
|
|
case 'u': /* unblink */ |
145 |
|
|
func |= BIOC_BLINK; |
146 |
|
|
blink = BIOC_SBUNBLINK; |
147 |
|
|
bl_arg = optarg; |
148 |
|
|
break; |
149 |
|
|
case 'H': /* set hotspare */ |
150 |
|
|
func |= BIOC_SETSTATE; |
151 |
|
|
ss_func = BIOC_SSHOTSPARE; |
152 |
|
|
al_arg = optarg; |
153 |
|
|
break; |
154 |
|
|
case 'h': |
155 |
|
|
human = 1; |
156 |
|
|
break; |
157 |
|
|
case 'i': /* inquiry */ |
158 |
|
|
func |= BIOC_INQ; |
159 |
|
|
break; |
160 |
|
|
case 'k': /* Key disk. */ |
161 |
|
|
key_disk = optarg; |
162 |
|
|
break; |
163 |
|
|
case 'l': /* device list */ |
164 |
|
|
func |= BIOC_DEVLIST; |
165 |
|
|
dev_list = optarg; |
166 |
|
|
break; |
167 |
|
|
case 'P': |
168 |
|
|
/* Change passphrase. */ |
169 |
|
|
changepass = 1; |
170 |
|
|
break; |
171 |
|
|
case 'p': |
172 |
|
|
password = optarg; |
173 |
|
|
break; |
174 |
|
|
case 'r': |
175 |
|
|
rflag = strtonum(optarg, 1000, 1<<30, &errstr); |
176 |
|
|
if (errstr != NULL) |
177 |
|
|
errx(1, "Number of rounds is %s: %s", |
178 |
|
|
errstr, optarg); |
179 |
|
|
break; |
180 |
|
|
case 'O': |
181 |
|
|
/* set a chunk to offline */ |
182 |
|
|
func |= BIOC_SETSTATE; |
183 |
|
|
ss_func = BIOC_SSOFFLINE; |
184 |
|
|
al_arg = optarg; |
185 |
|
|
break; |
186 |
|
|
case 'R': |
187 |
|
|
/* rebuild to provided chunk/CTL */ |
188 |
|
|
func |= BIOC_SETSTATE; |
189 |
|
|
ss_func = BIOC_SSREBUILD; |
190 |
|
|
al_arg = optarg; |
191 |
|
|
break; |
192 |
|
|
case 's': |
193 |
|
|
rpp_flag = RPP_STDIN; |
194 |
|
|
break; |
195 |
|
|
case 't': /* patrol */ |
196 |
|
|
func |= BIOC_PATROL; |
197 |
|
|
al_arg = optarg; |
198 |
|
|
break; |
199 |
|
|
case 'v': |
200 |
|
|
verbose = 1; |
201 |
|
|
break; |
202 |
|
|
case 'q': |
203 |
|
|
diskinq = 1; |
204 |
|
|
break; |
205 |
|
|
default: |
206 |
|
|
usage(); |
207 |
|
|
/* NOTREACHED */ |
208 |
|
|
} |
209 |
|
|
} |
210 |
|
|
argc -= optind; |
211 |
|
|
argv += optind; |
212 |
|
|
|
213 |
|
|
if (argc != 1 || (changepass && func != 0)) |
214 |
|
|
usage(); |
215 |
|
|
|
216 |
|
|
if (func == 0) |
217 |
|
|
func |= BIOC_INQ; |
218 |
|
|
|
219 |
|
|
devicename = argv[0]; |
220 |
|
|
if (devicename == NULL) |
221 |
|
|
errx(1, "need device"); |
222 |
|
|
|
223 |
|
|
devh = opendev(devicename, O_RDWR, OPENDEV_PART, &realname); |
224 |
|
|
if (devh == -1) { |
225 |
|
|
devh = open("/dev/bio", O_RDWR); |
226 |
|
|
if (devh == -1) |
227 |
|
|
err(1, "Can't open %s", "/dev/bio"); |
228 |
|
|
|
229 |
|
|
bl.bl_name = devicename; |
230 |
|
|
if (ioctl(devh, BIOCLOCATE, &bl)) |
231 |
|
|
errx(1, "Can't locate %s device via %s", |
232 |
|
|
bl.bl_name, "/dev/bio"); |
233 |
|
|
|
234 |
|
|
bio_status(&bl.bl_bio.bio_status); |
235 |
|
|
|
236 |
|
|
bio_cookie = bl.bl_bio.bio_cookie; |
237 |
|
|
biodev = 1; |
238 |
|
|
devicename = NULL; |
239 |
|
|
} |
240 |
|
|
|
241 |
|
|
if (diskinq) { |
242 |
|
|
bio_diskinq(devicename); |
243 |
|
|
} else if (changepass && !biodev) { |
244 |
|
|
bio_changepass(devicename); |
245 |
|
|
} else if (func & BIOC_INQ) { |
246 |
|
|
bio_inq(devicename); |
247 |
|
|
} else if (func == BIOC_ALARM) { |
248 |
|
|
bio_alarm(al_arg); |
249 |
|
|
} else if (func == BIOC_BLINK) { |
250 |
|
|
bio_setblink(devicename, bl_arg, blink); |
251 |
|
|
} else if (func == BIOC_PATROL) { |
252 |
|
|
bio_patrol(al_arg); |
253 |
|
|
} else if (func == BIOC_SETSTATE) { |
254 |
|
|
bio_setstate(al_arg, ss_func, argv[0]); |
255 |
|
|
} else if (func == BIOC_DELETERAID && !biodev) { |
256 |
|
|
bio_deleteraid(devicename); |
257 |
|
|
} else if (func & BIOC_CREATERAID || func & BIOC_DEVLIST) { |
258 |
|
|
if (!(func & BIOC_CREATERAID)) |
259 |
|
|
errx(1, "need -c parameter"); |
260 |
|
|
if (!(func & BIOC_DEVLIST)) |
261 |
|
|
errx(1, "need -l parameter"); |
262 |
|
|
if (!biodev) |
263 |
|
|
errx(1, "must use bio device"); |
264 |
|
|
bio_createraid(cr_level, dev_list, key_disk); |
265 |
|
|
} |
266 |
|
|
|
267 |
|
|
return (0); |
268 |
|
|
} |
269 |
|
|
|
270 |
|
|
void |
271 |
|
|
usage(void) |
272 |
|
|
{ |
273 |
|
|
extern char *__progname; |
274 |
|
|
|
275 |
|
|
fprintf(stderr, |
276 |
|
|
"usage: %s [-hiqv] [-a alarm-function] " |
277 |
|
|
"[-b channel:target[.lun]]\n" |
278 |
|
|
"\t[-H channel:target[.lun]] " |
279 |
|
|
"[-R device | channel:target[.lun]]\n" |
280 |
|
|
"\t[-t patrol-function] " |
281 |
|
|
"[-u channel:target[.lun]] " |
282 |
|
|
"device\n" |
283 |
|
|
" %s [-dhiPqsv] " |
284 |
|
|
"[-C flag[,flag,...]] [-c raidlevel] [-k keydisk]\n" |
285 |
|
|
"\t[-l special[,special,...]] " |
286 |
|
|
"[-O device | channel:target[.lun]]\n" |
287 |
|
|
"\t[-p passfile] [-R device | channel:target[.lun]]\n" |
288 |
|
|
"\t[-r rounds] " |
289 |
|
|
"device\n", __progname, __progname); |
290 |
|
|
|
291 |
|
|
exit(1); |
292 |
|
|
} |
293 |
|
|
|
294 |
|
|
const char * |
295 |
|
|
str2locator(const char *string, struct locator *location) |
296 |
|
|
{ |
297 |
|
|
const char *errstr; |
298 |
|
|
char parse[80], *targ, *lun; |
299 |
|
|
|
300 |
|
|
strlcpy(parse, string, sizeof parse); |
301 |
|
|
targ = strchr(parse, ':'); |
302 |
|
|
if (targ == NULL) |
303 |
|
|
return ("target not specified"); |
304 |
|
|
*targ++ = '\0'; |
305 |
|
|
|
306 |
|
|
lun = strchr(targ, '.'); |
307 |
|
|
if (lun != NULL) { |
308 |
|
|
*lun++ = '\0'; |
309 |
|
|
location->lun = strtonum(lun, 0, 256, &errstr); |
310 |
|
|
if (errstr) |
311 |
|
|
return (errstr); |
312 |
|
|
} else |
313 |
|
|
location->lun = 0; |
314 |
|
|
|
315 |
|
|
location->target = strtonum(targ, 0, 256, &errstr); |
316 |
|
|
if (errstr) |
317 |
|
|
return (errstr); |
318 |
|
|
location->channel = strtonum(parse, 0, 256, &errstr); |
319 |
|
|
if (errstr) |
320 |
|
|
return (errstr); |
321 |
|
|
return (NULL); |
322 |
|
|
} |
323 |
|
|
|
324 |
|
|
const char * |
325 |
|
|
str2patrol(const char *string, struct timing *timing) |
326 |
|
|
{ |
327 |
|
|
const char *errstr; |
328 |
|
|
char parse[80], *interval = NULL, *start = NULL; |
329 |
|
|
|
330 |
|
|
timing->interval = 0; |
331 |
|
|
timing->start = 0; |
332 |
|
|
|
333 |
|
|
strlcpy(parse, string, sizeof parse); |
334 |
|
|
|
335 |
|
|
interval = strchr(parse, '.'); |
336 |
|
|
if (interval != NULL) { |
337 |
|
|
*interval++ = '\0'; |
338 |
|
|
start = strchr(interval, '.'); |
339 |
|
|
if (start != NULL) |
340 |
|
|
*start++ = '\0'; |
341 |
|
|
} |
342 |
|
|
if (interval != NULL) { |
343 |
|
|
/* -1 == continuously */ |
344 |
|
|
timing->interval = strtonum(interval, -1, INT_MAX, &errstr); |
345 |
|
|
if (errstr) |
346 |
|
|
return (errstr); |
347 |
|
|
} |
348 |
|
|
if (start != NULL) { |
349 |
|
|
timing->start = strtonum(start, 0, INT_MAX, &errstr); |
350 |
|
|
if (errstr) |
351 |
|
|
return (errstr); |
352 |
|
|
} |
353 |
|
|
|
354 |
|
|
return (NULL); |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
void |
358 |
|
|
bio_status(struct bio_status *bs) |
359 |
|
|
{ |
360 |
|
|
extern char *__progname; |
361 |
|
|
char *prefix; |
362 |
|
|
int i; |
363 |
|
|
|
364 |
|
|
if (strlen(bs->bs_controller)) |
365 |
|
|
prefix = bs->bs_controller; |
366 |
|
|
else |
367 |
|
|
prefix = __progname; |
368 |
|
|
|
369 |
|
|
for (i = 0; i < bs->bs_msg_count; i++) |
370 |
|
|
printf("%s: %s\n", prefix, bs->bs_msgs[i].bm_msg); |
371 |
|
|
|
372 |
|
|
if (bs->bs_status == BIO_STATUS_ERROR) { |
373 |
|
|
if (bs->bs_msg_count == 0) |
374 |
|
|
errx(1, "unknown error"); |
375 |
|
|
else |
376 |
|
|
exit(1); |
377 |
|
|
} |
378 |
|
|
} |
379 |
|
|
|
380 |
|
|
void |
381 |
|
|
bio_inq(char *name) |
382 |
|
|
{ |
383 |
|
|
char *status, *cache; |
384 |
|
|
char size[64], scsiname[16], volname[32]; |
385 |
|
|
char percent[20], seconds[20]; |
386 |
|
|
int i, d, volheader, hotspare, unused; |
387 |
|
|
char encname[16], serial[32]; |
388 |
|
|
struct bioc_inq bi; |
389 |
|
|
struct bioc_vol bv; |
390 |
|
|
struct bioc_disk bd; |
391 |
|
|
|
392 |
|
|
memset(&bi, 0, sizeof(bi)); |
393 |
|
|
|
394 |
|
|
bi.bi_bio.bio_cookie = bio_cookie; |
395 |
|
|
|
396 |
|
|
if (ioctl(devh, BIOCINQ, &bi)) { |
397 |
|
|
if (errno == ENOTTY) |
398 |
|
|
bio_diskinq(name); |
399 |
|
|
else |
400 |
|
|
err(1, "BIOCINQ"); |
401 |
|
|
return; |
402 |
|
|
} |
403 |
|
|
|
404 |
|
|
bio_status(&bi.bi_bio.bio_status); |
405 |
|
|
|
406 |
|
|
volheader = 0; |
407 |
|
|
for (i = 0; i < bi.bi_novol; i++) { |
408 |
|
|
memset(&bv, 0, sizeof(bv)); |
409 |
|
|
bv.bv_bio.bio_cookie = bio_cookie; |
410 |
|
|
bv.bv_volid = i; |
411 |
|
|
bv.bv_percent = -1; |
412 |
|
|
bv.bv_seconds = 0; |
413 |
|
|
|
414 |
|
|
if (ioctl(devh, BIOCVOL, &bv)) |
415 |
|
|
err(1, "BIOCVOL"); |
416 |
|
|
|
417 |
|
|
bio_status(&bv.bv_bio.bio_status); |
418 |
|
|
|
419 |
|
|
if (name && strcmp(name, bv.bv_dev) != 0) |
420 |
|
|
continue; |
421 |
|
|
|
422 |
|
|
if (!volheader) { |
423 |
|
|
volheader = 1; |
424 |
|
|
printf("%-11s %-10s %14s %-8s\n", |
425 |
|
|
"Volume", "Status", "Size", "Device"); |
426 |
|
|
} |
427 |
|
|
|
428 |
|
|
percent[0] = '\0'; |
429 |
|
|
seconds[0] = '\0'; |
430 |
|
|
if (bv.bv_percent != -1) |
431 |
|
|
snprintf(percent, sizeof percent, |
432 |
|
|
" %d%% done", bv.bv_percent); |
433 |
|
|
if (bv.bv_seconds) |
434 |
|
|
snprintf(seconds, sizeof seconds, |
435 |
|
|
" %u seconds", bv.bv_seconds); |
436 |
|
|
switch (bv.bv_status) { |
437 |
|
|
case BIOC_SVONLINE: |
438 |
|
|
status = BIOC_SVONLINE_S; |
439 |
|
|
break; |
440 |
|
|
case BIOC_SVOFFLINE: |
441 |
|
|
status = BIOC_SVOFFLINE_S; |
442 |
|
|
break; |
443 |
|
|
case BIOC_SVDEGRADED: |
444 |
|
|
status = BIOC_SVDEGRADED_S; |
445 |
|
|
break; |
446 |
|
|
case BIOC_SVBUILDING: |
447 |
|
|
status = BIOC_SVBUILDING_S; |
448 |
|
|
break; |
449 |
|
|
case BIOC_SVREBUILD: |
450 |
|
|
status = BIOC_SVREBUILD_S; |
451 |
|
|
break; |
452 |
|
|
case BIOC_SVSCRUB: |
453 |
|
|
status = BIOC_SVSCRUB_S; |
454 |
|
|
break; |
455 |
|
|
case BIOC_SVINVALID: |
456 |
|
|
default: |
457 |
|
|
status = BIOC_SVINVALID_S; |
458 |
|
|
} |
459 |
|
|
switch (bv.bv_cache) { |
460 |
|
|
case BIOC_CVWRITEBACK: |
461 |
|
|
cache = BIOC_CVWRITEBACK_S; |
462 |
|
|
break; |
463 |
|
|
case BIOC_CVWRITETHROUGH: |
464 |
|
|
cache = BIOC_CVWRITETHROUGH_S; |
465 |
|
|
break; |
466 |
|
|
case BIOC_CVUNKNOWN: |
467 |
|
|
default: |
468 |
|
|
cache = BIOC_CVUNKNOWN_S; |
469 |
|
|
} |
470 |
|
|
|
471 |
|
|
snprintf(volname, sizeof volname, "%s %u", |
472 |
|
|
bi.bi_dev, bv.bv_volid); |
473 |
|
|
|
474 |
|
|
unused = 0; |
475 |
|
|
hotspare = 0; |
476 |
|
|
if (bv.bv_level == -1 && bv.bv_nodisk == 1) |
477 |
|
|
hotspare = 1; |
478 |
|
|
else if (bv.bv_level == -2 && bv.bv_nodisk == 1) |
479 |
|
|
unused = 1; |
480 |
|
|
else { |
481 |
|
|
if (human) |
482 |
|
|
fmt_scaled(bv.bv_size, size); |
483 |
|
|
else |
484 |
|
|
snprintf(size, sizeof size, "%14llu", |
485 |
|
|
bv.bv_size); |
486 |
|
|
switch (bv.bv_level) { |
487 |
|
|
case 'C': |
488 |
|
|
printf("%11s %-10s %14s %-7s CRYPTO%s%s\n", |
489 |
|
|
volname, status, size, bv.bv_dev, |
490 |
|
|
percent, seconds); |
491 |
|
|
break; |
492 |
|
|
case 'c': |
493 |
|
|
printf("%11s %-10s %14s %-7s CONCAT%s%s\n", |
494 |
|
|
volname, status, size, bv.bv_dev, |
495 |
|
|
percent, seconds); |
496 |
|
|
break; |
497 |
|
|
default: |
498 |
|
|
printf("%11s %-10s %14s %-7s RAID%u%s%s %s\n", |
499 |
|
|
volname, status, size, bv.bv_dev, |
500 |
|
|
bv.bv_level, percent, seconds, cache); |
501 |
|
|
break; |
502 |
|
|
} |
503 |
|
|
|
504 |
|
|
} |
505 |
|
|
|
506 |
|
|
for (d = 0; d < bv.bv_nodisk; d++) { |
507 |
|
|
memset(&bd, 0, sizeof(bd)); |
508 |
|
|
bd.bd_bio.bio_cookie = bio_cookie; |
509 |
|
|
bd.bd_diskid = d; |
510 |
|
|
bd.bd_volid = i; |
511 |
|
|
bd.bd_patrol.bdp_percent = -1; |
512 |
|
|
bd.bd_patrol.bdp_seconds = 0; |
513 |
|
|
|
514 |
|
|
if (ioctl(devh, BIOCDISK, &bd)) |
515 |
|
|
err(1, "BIOCDISK"); |
516 |
|
|
|
517 |
|
|
bio_status(&bd.bd_bio.bio_status); |
518 |
|
|
|
519 |
|
|
switch (bd.bd_status) { |
520 |
|
|
case BIOC_SDONLINE: |
521 |
|
|
status = BIOC_SDONLINE_S; |
522 |
|
|
break; |
523 |
|
|
case BIOC_SDOFFLINE: |
524 |
|
|
status = BIOC_SDOFFLINE_S; |
525 |
|
|
break; |
526 |
|
|
case BIOC_SDFAILED: |
527 |
|
|
status = BIOC_SDFAILED_S; |
528 |
|
|
break; |
529 |
|
|
case BIOC_SDREBUILD: |
530 |
|
|
status = BIOC_SDREBUILD_S; |
531 |
|
|
break; |
532 |
|
|
case BIOC_SDHOTSPARE: |
533 |
|
|
status = BIOC_SDHOTSPARE_S; |
534 |
|
|
break; |
535 |
|
|
case BIOC_SDUNUSED: |
536 |
|
|
status = BIOC_SDUNUSED_S; |
537 |
|
|
break; |
538 |
|
|
case BIOC_SDSCRUB: |
539 |
|
|
status = BIOC_SDSCRUB_S; |
540 |
|
|
break; |
541 |
|
|
case BIOC_SDINVALID: |
542 |
|
|
default: |
543 |
|
|
status = BIOC_SDINVALID_S; |
544 |
|
|
} |
545 |
|
|
|
546 |
|
|
if (hotspare || unused) |
547 |
|
|
; /* use volname from parent volume */ |
548 |
|
|
else |
549 |
|
|
snprintf(volname, sizeof volname, " %3u", |
550 |
|
|
bd.bd_diskid); |
551 |
|
|
|
552 |
|
|
if (bv.bv_level == 'C' && bd.bd_size == 0) |
553 |
|
|
snprintf(size, sizeof size, "%14s", "key disk"); |
554 |
|
|
else if (human) |
555 |
|
|
fmt_scaled(bd.bd_size, size); |
556 |
|
|
else |
557 |
|
|
snprintf(size, sizeof size, "%14llu", |
558 |
|
|
bd.bd_size); |
559 |
|
|
snprintf(scsiname, sizeof scsiname, |
560 |
|
|
"%u:%u.%u", |
561 |
|
|
bd.bd_channel, bd.bd_target, bd.bd_lun); |
562 |
|
|
if (bd.bd_procdev[0]) |
563 |
|
|
strlcpy(encname, bd.bd_procdev, sizeof encname); |
564 |
|
|
else |
565 |
|
|
strlcpy(encname, "noencl", sizeof encname); |
566 |
|
|
if (bd.bd_serial[0]) |
567 |
|
|
strlcpy(serial, bd.bd_serial, sizeof serial); |
568 |
|
|
else |
569 |
|
|
strlcpy(serial, "unknown serial", sizeof serial); |
570 |
|
|
|
571 |
|
|
percent[0] = '\0'; |
572 |
|
|
seconds[0] = '\0'; |
573 |
|
|
if (bd.bd_patrol.bdp_percent != -1) |
574 |
|
|
snprintf(percent, sizeof percent, |
575 |
|
|
" patrol %d%% done", bd.bd_patrol.bdp_percent); |
576 |
|
|
if (bd.bd_patrol.bdp_seconds) |
577 |
|
|
snprintf(seconds, sizeof seconds, |
578 |
|
|
" %u seconds", bd.bd_patrol.bdp_seconds); |
579 |
|
|
|
580 |
|
|
printf("%11s %-10s %14s %-7s %-6s <%s>\n", |
581 |
|
|
volname, status, size, scsiname, encname, |
582 |
|
|
bd.bd_vendor); |
583 |
|
|
if (verbose) |
584 |
|
|
printf("%11s %-10s %14s %-7s %-6s '%s'%s%s\n", |
585 |
|
|
"", "", "", "", "", serial, percent, seconds); |
586 |
|
|
} |
587 |
|
|
} |
588 |
|
|
} |
589 |
|
|
|
590 |
|
|
void |
591 |
|
|
bio_alarm(char *arg) |
592 |
|
|
{ |
593 |
|
|
struct bioc_alarm ba; |
594 |
|
|
|
595 |
|
|
memset(&ba, 0, sizeof(ba)); |
596 |
|
|
ba.ba_bio.bio_cookie = bio_cookie; |
597 |
|
|
|
598 |
|
|
switch (arg[0]) { |
599 |
|
|
case 'q': /* silence alarm */ |
600 |
|
|
/* FALLTHROUGH */ |
601 |
|
|
case 's': |
602 |
|
|
ba.ba_opcode = BIOC_SASILENCE; |
603 |
|
|
break; |
604 |
|
|
|
605 |
|
|
case 'e': /* enable alarm */ |
606 |
|
|
ba.ba_opcode = BIOC_SAENABLE; |
607 |
|
|
break; |
608 |
|
|
|
609 |
|
|
case 'd': /* disable alarm */ |
610 |
|
|
ba.ba_opcode = BIOC_SADISABLE; |
611 |
|
|
break; |
612 |
|
|
|
613 |
|
|
case 't': /* test alarm */ |
614 |
|
|
ba.ba_opcode = BIOC_SATEST; |
615 |
|
|
break; |
616 |
|
|
|
617 |
|
|
case 'g': /* get alarm state */ |
618 |
|
|
ba.ba_opcode = BIOC_GASTATUS; |
619 |
|
|
break; |
620 |
|
|
|
621 |
|
|
default: |
622 |
|
|
errx(1, "invalid alarm function: %s", arg); |
623 |
|
|
} |
624 |
|
|
|
625 |
|
|
if (ioctl(devh, BIOCALARM, &ba)) |
626 |
|
|
err(1, "BIOCALARM"); |
627 |
|
|
|
628 |
|
|
bio_status(&ba.ba_bio.bio_status); |
629 |
|
|
|
630 |
|
|
if (arg[0] == 'g') |
631 |
|
|
printf("alarm is currently %s\n", |
632 |
|
|
ba.ba_status ? "enabled" : "disabled"); |
633 |
|
|
} |
634 |
|
|
|
635 |
|
|
int |
636 |
|
|
bio_getvolbyname(char *name) |
637 |
|
|
{ |
638 |
|
|
int id = -1, i; |
639 |
|
|
struct bioc_inq bi; |
640 |
|
|
struct bioc_vol bv; |
641 |
|
|
|
642 |
|
|
memset(&bi, 0, sizeof(bi)); |
643 |
|
|
bi.bi_bio.bio_cookie = bio_cookie; |
644 |
|
|
if (ioctl(devh, BIOCINQ, &bi)) |
645 |
|
|
err(1, "BIOCINQ"); |
646 |
|
|
|
647 |
|
|
bio_status(&bi.bi_bio.bio_status); |
648 |
|
|
|
649 |
|
|
for (i = 0; i < bi.bi_novol; i++) { |
650 |
|
|
memset(&bv, 0, sizeof(bv)); |
651 |
|
|
bv.bv_bio.bio_cookie = bio_cookie; |
652 |
|
|
bv.bv_volid = i; |
653 |
|
|
if (ioctl(devh, BIOCVOL, &bv)) |
654 |
|
|
err(1, "BIOCVOL"); |
655 |
|
|
|
656 |
|
|
bio_status(&bv.bv_bio.bio_status); |
657 |
|
|
|
658 |
|
|
if (name && strcmp(name, bv.bv_dev) != 0) |
659 |
|
|
continue; |
660 |
|
|
id = i; |
661 |
|
|
break; |
662 |
|
|
} |
663 |
|
|
|
664 |
|
|
return (id); |
665 |
|
|
} |
666 |
|
|
|
667 |
|
|
void |
668 |
|
|
bio_setstate(char *arg, int status, char *devicename) |
669 |
|
|
{ |
670 |
|
|
struct bioc_setstate bs; |
671 |
|
|
struct locator location; |
672 |
|
|
struct stat sb; |
673 |
|
|
const char *errstr; |
674 |
|
|
|
675 |
|
|
memset(&bs, 0, sizeof(bs)); |
676 |
|
|
if (stat(arg, &sb) == -1) { |
677 |
|
|
/* use CTL */ |
678 |
|
|
errstr = str2locator(arg, &location); |
679 |
|
|
if (errstr) |
680 |
|
|
errx(1, "Target %s: %s", arg, errstr); |
681 |
|
|
bs.bs_channel = location.channel; |
682 |
|
|
bs.bs_target = location.target; |
683 |
|
|
bs.bs_lun = location.lun; |
684 |
|
|
} else { |
685 |
|
|
/* use other id */ |
686 |
|
|
bs.bs_other_id = sb.st_rdev; |
687 |
|
|
bs.bs_other_id_type = BIOC_SSOTHER_DEVT; |
688 |
|
|
} |
689 |
|
|
|
690 |
|
|
bs.bs_bio.bio_cookie = bio_cookie; |
691 |
|
|
bs.bs_status = status; |
692 |
|
|
|
693 |
|
|
if (status != BIOC_SSHOTSPARE) { |
694 |
|
|
/* make sure user supplied a sd device */ |
695 |
|
|
bs.bs_volid = bio_getvolbyname(devicename); |
696 |
|
|
if (bs.bs_volid == -1) |
697 |
|
|
errx(1, "invalid device %s", devicename); |
698 |
|
|
} |
699 |
|
|
|
700 |
|
|
if (ioctl(devh, BIOCSETSTATE, &bs)) |
701 |
|
|
err(1, "BIOCSETSTATE"); |
702 |
|
|
|
703 |
|
|
bio_status(&bs.bs_bio.bio_status); |
704 |
|
|
} |
705 |
|
|
|
706 |
|
|
void |
707 |
|
|
bio_setblink(char *name, char *arg, int blink) |
708 |
|
|
{ |
709 |
|
|
struct locator location; |
710 |
|
|
struct bioc_blink bb; |
711 |
|
|
struct bioc_inq bi; |
712 |
|
|
struct bioc_vol bv; |
713 |
|
|
struct bioc_disk bd; |
714 |
|
|
const char *errstr; |
715 |
|
|
int v, d, rv; |
716 |
|
|
|
717 |
|
|
errstr = str2locator(arg, &location); |
718 |
|
|
if (errstr) |
719 |
|
|
errx(1, "Target %s: %s", arg, errstr); |
720 |
|
|
|
721 |
|
|
/* try setting blink on the device directly */ |
722 |
|
|
memset(&bb, 0, sizeof(bb)); |
723 |
|
|
bb.bb_bio.bio_cookie = bio_cookie; |
724 |
|
|
bb.bb_status = blink; |
725 |
|
|
bb.bb_target = location.target; |
726 |
|
|
bb.bb_channel = location.channel; |
727 |
|
|
rv = ioctl(devh, BIOCBLINK, &bb); |
728 |
|
|
|
729 |
|
|
if (rv == 0 && bb.bb_bio.bio_status.bs_status == BIO_STATUS_UNKNOWN) |
730 |
|
|
return; |
731 |
|
|
|
732 |
|
|
if (rv == 0 && bb.bb_bio.bio_status.bs_status == BIO_STATUS_SUCCESS) { |
733 |
|
|
bio_status(&bb.bb_bio.bio_status); |
734 |
|
|
return; |
735 |
|
|
} |
736 |
|
|
|
737 |
|
|
/* if the blink didn't work, try to find something that will */ |
738 |
|
|
|
739 |
|
|
memset(&bi, 0, sizeof(bi)); |
740 |
|
|
bi.bi_bio.bio_cookie = bio_cookie; |
741 |
|
|
if (ioctl(devh, BIOCINQ, &bi)) |
742 |
|
|
err(1, "BIOCINQ"); |
743 |
|
|
|
744 |
|
|
bio_status(&bi.bi_bio.bio_status); |
745 |
|
|
|
746 |
|
|
for (v = 0; v < bi.bi_novol; v++) { |
747 |
|
|
memset(&bv, 0, sizeof(bv)); |
748 |
|
|
bv.bv_bio.bio_cookie = bio_cookie; |
749 |
|
|
bv.bv_volid = v; |
750 |
|
|
if (ioctl(devh, BIOCVOL, &bv)) |
751 |
|
|
err(1, "BIOCVOL"); |
752 |
|
|
|
753 |
|
|
bio_status(&bv.bv_bio.bio_status); |
754 |
|
|
|
755 |
|
|
if (name && strcmp(name, bv.bv_dev) != 0) |
756 |
|
|
continue; |
757 |
|
|
|
758 |
|
|
for (d = 0; d < bv.bv_nodisk; d++) { |
759 |
|
|
memset(&bd, 0, sizeof(bd)); |
760 |
|
|
bd.bd_bio.bio_cookie = bio_cookie; |
761 |
|
|
bd.bd_volid = v; |
762 |
|
|
bd.bd_diskid = d; |
763 |
|
|
|
764 |
|
|
if (ioctl(devh, BIOCDISK, &bd)) |
765 |
|
|
err(1, "BIOCDISK"); |
766 |
|
|
|
767 |
|
|
bio_status(&bd.bd_bio.bio_status); |
768 |
|
|
|
769 |
|
|
if (bd.bd_channel == location.channel && |
770 |
|
|
bd.bd_target == location.target && |
771 |
|
|
bd.bd_lun == location.lun) { |
772 |
|
|
if (bd.bd_procdev[0] != '\0') |
773 |
|
|
bio_blink(bd.bd_procdev, |
774 |
|
|
location.target, blink); |
775 |
|
|
else |
776 |
|
|
warnx("Disk %s is not in an enclosure", |
777 |
|
|
arg); |
778 |
|
|
return; |
779 |
|
|
} |
780 |
|
|
} |
781 |
|
|
} |
782 |
|
|
|
783 |
|
|
warnx("Disk %s does not exist", arg); |
784 |
|
|
|
785 |
|
|
return; |
786 |
|
|
} |
787 |
|
|
|
788 |
|
|
void |
789 |
|
|
bio_blink(char *enclosure, int target, int blinktype) |
790 |
|
|
{ |
791 |
|
|
int bioh; |
792 |
|
|
struct bio_locate bl; |
793 |
|
|
struct bioc_blink blink; |
794 |
|
|
|
795 |
|
|
bioh = open("/dev/bio", O_RDWR); |
796 |
|
|
if (bioh == -1) |
797 |
|
|
err(1, "Can't open %s", "/dev/bio"); |
798 |
|
|
|
799 |
|
|
memset(&bl, 0, sizeof(bl)); |
800 |
|
|
bl.bl_name = enclosure; |
801 |
|
|
if (ioctl(bioh, BIOCLOCATE, &bl)) |
802 |
|
|
errx(1, "Can't locate %s device via %s", enclosure, "/dev/bio"); |
803 |
|
|
|
804 |
|
|
bio_status(&bl.bl_bio.bio_status); |
805 |
|
|
|
806 |
|
|
memset(&blink, 0, sizeof(blink)); |
807 |
|
|
blink.bb_bio.bio_cookie = bio_cookie; |
808 |
|
|
blink.bb_status = blinktype; |
809 |
|
|
blink.bb_target = target; |
810 |
|
|
|
811 |
|
|
if (ioctl(bioh, BIOCBLINK, &blink)) |
812 |
|
|
err(1, "BIOCBLINK"); |
813 |
|
|
|
814 |
|
|
bio_status(&blink.bb_bio.bio_status); |
815 |
|
|
|
816 |
|
|
close(bioh); |
817 |
|
|
} |
818 |
|
|
|
819 |
|
|
void |
820 |
|
|
bio_createraid(u_int16_t level, char *dev_list, char *key_disk) |
821 |
|
|
{ |
822 |
|
|
struct bioc_createraid create; |
823 |
|
|
struct sr_crypto_kdfinfo kdfinfo; |
824 |
|
|
struct sr_crypto_kdf_pbkdf2 kdfhint; |
825 |
|
|
struct stat sb; |
826 |
|
|
int rv, no_dev, fd; |
827 |
|
|
dev_t *dt; |
828 |
|
|
u_int16_t min_disks = 0; |
829 |
|
|
|
830 |
|
|
if (!dev_list) |
831 |
|
|
errx(1, "no devices specified"); |
832 |
|
|
|
833 |
|
|
dt = calloc(1, BIOC_CRMAXLEN); |
834 |
|
|
if (!dt) |
835 |
|
|
err(1, "not enough memory for dev_t list"); |
836 |
|
|
|
837 |
|
|
no_dev = bio_parse_devlist(dev_list, dt); |
838 |
|
|
|
839 |
|
|
switch (level) { |
840 |
|
|
case 0: |
841 |
|
|
min_disks = 2; |
842 |
|
|
break; |
843 |
|
|
case 1: |
844 |
|
|
min_disks = 2; |
845 |
|
|
break; |
846 |
|
|
case 5: |
847 |
|
|
min_disks = 3; |
848 |
|
|
break; |
849 |
|
|
case 'C': |
850 |
|
|
min_disks = 1; |
851 |
|
|
break; |
852 |
|
|
case 'c': |
853 |
|
|
min_disks = 2; |
854 |
|
|
break; |
855 |
|
|
default: |
856 |
|
|
errx(1, "unsupported raid level"); |
857 |
|
|
} |
858 |
|
|
|
859 |
|
|
if (no_dev < min_disks) |
860 |
|
|
errx(1, "not enough disks"); |
861 |
|
|
|
862 |
|
|
/* for crypto raid we only allow one single chunk */ |
863 |
|
|
if (level == 'C' && no_dev != min_disks) |
864 |
|
|
errx(1, "not exactly one partition"); |
865 |
|
|
|
866 |
|
|
memset(&create, 0, sizeof(create)); |
867 |
|
|
create.bc_bio.bio_cookie = bio_cookie; |
868 |
|
|
create.bc_level = level; |
869 |
|
|
create.bc_dev_list_len = no_dev * sizeof(dev_t); |
870 |
|
|
create.bc_dev_list = dt; |
871 |
|
|
create.bc_flags = BIOC_SCDEVT | cflags; |
872 |
|
|
create.bc_key_disk = NODEV; |
873 |
|
|
|
874 |
|
|
if (level == 'C' && key_disk == NULL) { |
875 |
|
|
|
876 |
|
|
memset(&kdfinfo, 0, sizeof(kdfinfo)); |
877 |
|
|
memset(&kdfhint, 0, sizeof(kdfhint)); |
878 |
|
|
|
879 |
|
|
create.bc_flags |= BIOC_SCNOAUTOASSEMBLE; |
880 |
|
|
|
881 |
|
|
create.bc_opaque = &kdfhint; |
882 |
|
|
create.bc_opaque_size = sizeof(kdfhint); |
883 |
|
|
create.bc_opaque_flags = BIOC_SOOUT; |
884 |
|
|
|
885 |
|
|
/* try to get KDF hint */ |
886 |
|
|
if (ioctl(devh, BIOCCREATERAID, &create)) |
887 |
|
|
err(1, "ioctl"); |
888 |
|
|
|
889 |
|
|
bio_status(&create.bc_bio.bio_status); |
890 |
|
|
|
891 |
|
|
if (create.bc_opaque_status == BIOC_SOINOUT_OK) { |
892 |
|
|
bio_kdf_derive(&kdfinfo, &kdfhint, "Passphrase: ", 0); |
893 |
|
|
memset(&kdfhint, 0, sizeof(kdfhint)); |
894 |
|
|
} else { |
895 |
|
|
bio_kdf_generate(&kdfinfo); |
896 |
|
|
} |
897 |
|
|
|
898 |
|
|
create.bc_opaque = &kdfinfo; |
899 |
|
|
create.bc_opaque_size = sizeof(kdfinfo); |
900 |
|
|
create.bc_opaque_flags = BIOC_SOIN; |
901 |
|
|
|
902 |
|
|
} else if (level == 'C' && key_disk != NULL) { |
903 |
|
|
|
904 |
|
|
/* Get device number for key disk. */ |
905 |
|
|
fd = opendev(key_disk, O_RDONLY, OPENDEV_BLCK, NULL); |
906 |
|
|
if (fd == -1) |
907 |
|
|
err(1, "could not open %s", key_disk); |
908 |
|
|
if (fstat(fd, &sb) == -1) { |
909 |
|
|
int saved_errno = errno; |
910 |
|
|
close(fd); |
911 |
|
|
errc(1, saved_errno, "could not stat %s", key_disk); |
912 |
|
|
} |
913 |
|
|
close(fd); |
914 |
|
|
create.bc_key_disk = sb.st_rdev; |
915 |
|
|
|
916 |
|
|
memset(&kdfinfo, 0, sizeof(kdfinfo)); |
917 |
|
|
|
918 |
|
|
kdfinfo.genkdf.len = sizeof(kdfinfo.genkdf); |
919 |
|
|
kdfinfo.genkdf.type = SR_CRYPTOKDFT_KEYDISK; |
920 |
|
|
kdfinfo.len = sizeof(kdfinfo); |
921 |
|
|
kdfinfo.flags = SR_CRYPTOKDF_HINT; |
922 |
|
|
|
923 |
|
|
create.bc_opaque = &kdfinfo; |
924 |
|
|
create.bc_opaque_size = sizeof(kdfinfo); |
925 |
|
|
create.bc_opaque_flags = BIOC_SOIN; |
926 |
|
|
|
927 |
|
|
} |
928 |
|
|
|
929 |
|
|
rv = ioctl(devh, BIOCCREATERAID, &create); |
930 |
|
|
explicit_bzero(&kdfinfo, sizeof(kdfinfo)); |
931 |
|
|
if (rv == -1) |
932 |
|
|
err(1, "BIOCCREATERAID"); |
933 |
|
|
|
934 |
|
|
bio_status(&create.bc_bio.bio_status); |
935 |
|
|
|
936 |
|
|
free(dt); |
937 |
|
|
} |
938 |
|
|
|
939 |
|
|
void |
940 |
|
|
bio_kdf_derive(struct sr_crypto_kdfinfo *kdfinfo, struct sr_crypto_kdf_pbkdf2 |
941 |
|
|
*kdfhint, char* prompt, int verify) |
942 |
|
|
{ |
943 |
|
|
if (!kdfinfo) |
944 |
|
|
errx(1, "invalid KDF info"); |
945 |
|
|
if (!kdfhint) |
946 |
|
|
errx(1, "invalid KDF hint"); |
947 |
|
|
|
948 |
|
|
if (kdfhint->len != sizeof(*kdfhint)) |
949 |
|
|
errx(1, "KDF hint has invalid size"); |
950 |
|
|
if (kdfhint->type != SR_CRYPTOKDFT_PBKDF2) |
951 |
|
|
errx(1, "unknown KDF type %d", kdfhint->type); |
952 |
|
|
if (kdfhint->rounds < 1000) |
953 |
|
|
errx(1, "number of KDF rounds too low: %d", kdfhint->rounds); |
954 |
|
|
|
955 |
|
|
kdfinfo->flags = SR_CRYPTOKDF_KEY; |
956 |
|
|
kdfinfo->len = sizeof(*kdfinfo); |
957 |
|
|
|
958 |
|
|
derive_key_pkcs(kdfhint->rounds, |
959 |
|
|
kdfinfo->maskkey, sizeof(kdfinfo->maskkey), |
960 |
|
|
kdfhint->salt, sizeof(kdfhint->salt), prompt, verify); |
961 |
|
|
} |
962 |
|
|
|
963 |
|
|
void |
964 |
|
|
bio_kdf_generate(struct sr_crypto_kdfinfo *kdfinfo) |
965 |
|
|
{ |
966 |
|
|
if (!kdfinfo) |
967 |
|
|
errx(1, "invalid KDF info"); |
968 |
|
|
|
969 |
|
|
kdfinfo->pbkdf2.len = sizeof(kdfinfo->pbkdf2); |
970 |
|
|
kdfinfo->pbkdf2.type = SR_CRYPTOKDFT_PBKDF2; |
971 |
|
|
kdfinfo->pbkdf2.rounds = rflag; |
972 |
|
|
kdfinfo->len = sizeof(*kdfinfo); |
973 |
|
|
kdfinfo->flags = SR_CRYPTOKDF_KEY | SR_CRYPTOKDF_HINT; |
974 |
|
|
|
975 |
|
|
/* generate salt */ |
976 |
|
|
arc4random_buf(kdfinfo->pbkdf2.salt, sizeof(kdfinfo->pbkdf2.salt)); |
977 |
|
|
|
978 |
|
|
derive_key_pkcs(kdfinfo->pbkdf2.rounds, |
979 |
|
|
kdfinfo->maskkey, sizeof(kdfinfo->maskkey), |
980 |
|
|
kdfinfo->pbkdf2.salt, sizeof(kdfinfo->pbkdf2.salt), |
981 |
|
|
"New passphrase: ", 1); |
982 |
|
|
} |
983 |
|
|
|
984 |
|
|
int |
985 |
|
|
bio_parse_devlist(char *lst, dev_t *dt) |
986 |
|
|
{ |
987 |
|
|
char *s, *e; |
988 |
|
|
u_int32_t sz = 0; |
989 |
|
|
int no_dev = 0, i, x; |
990 |
|
|
struct stat sb; |
991 |
|
|
char dev[PATH_MAX]; |
992 |
|
|
int fd; |
993 |
|
|
|
994 |
|
|
if (!lst) |
995 |
|
|
errx(1, "invalid device list"); |
996 |
|
|
|
997 |
|
|
s = e = lst; |
998 |
|
|
/* make sure we have a valid device list like /dev/sdNa,/dev/sdNNa */ |
999 |
|
|
while (*e != '\0') { |
1000 |
|
|
if (*e == ',') |
1001 |
|
|
s = e + 1; |
1002 |
|
|
else if (*(e + 1) == '\0' || *(e + 1) == ',') { |
1003 |
|
|
/* got one */ |
1004 |
|
|
sz = e - s + 1; |
1005 |
|
|
strlcpy(dev, s, sz + 1); |
1006 |
|
|
fd = opendev(dev, O_RDONLY, OPENDEV_BLCK, NULL); |
1007 |
|
|
if (fd == -1) |
1008 |
|
|
err(1, "could not open %s", dev); |
1009 |
|
|
if (fstat(fd, &sb) == -1) { |
1010 |
|
|
int saved_errno = errno; |
1011 |
|
|
close(fd); |
1012 |
|
|
errc(1, saved_errno, "could not stat %s", dev); |
1013 |
|
|
} |
1014 |
|
|
close(fd); |
1015 |
|
|
dt[no_dev] = sb.st_rdev; |
1016 |
|
|
no_dev++; |
1017 |
|
|
if (no_dev > (int)(BIOC_CRMAXLEN / sizeof(dev_t))) |
1018 |
|
|
errx(1, "too many devices on device list"); |
1019 |
|
|
} |
1020 |
|
|
e++; |
1021 |
|
|
} |
1022 |
|
|
|
1023 |
|
|
for (i = 0; i < no_dev; i++) |
1024 |
|
|
for (x = 0; x < no_dev; x++) |
1025 |
|
|
if (dt[i] == dt[x] && x != i) |
1026 |
|
|
errx(1, "duplicate device in list"); |
1027 |
|
|
|
1028 |
|
|
return (no_dev); |
1029 |
|
|
} |
1030 |
|
|
|
1031 |
|
|
u_int32_t |
1032 |
|
|
bio_createflags(char *lst) |
1033 |
|
|
{ |
1034 |
|
|
char *s, *e, fs[32]; |
1035 |
|
|
u_int32_t sz = 0; |
1036 |
|
|
u_int32_t flags = 0; |
1037 |
|
|
|
1038 |
|
|
if (!lst) |
1039 |
|
|
errx(1, "invalid flags list"); |
1040 |
|
|
|
1041 |
|
|
s = e = lst; |
1042 |
|
|
/* make sure we have a valid flags list like force,noassemeble */ |
1043 |
|
|
while (*e != '\0') { |
1044 |
|
|
if (*e == ',') |
1045 |
|
|
s = e + 1; |
1046 |
|
|
else if (*(e + 1) == '\0' || *(e + 1) == ',') { |
1047 |
|
|
/* got one */ |
1048 |
|
|
sz = e - s + 1; |
1049 |
|
|
switch (s[0]) { |
1050 |
|
|
case 'f': |
1051 |
|
|
flags |= BIOC_SCFORCE; |
1052 |
|
|
break; |
1053 |
|
|
case 'n': |
1054 |
|
|
flags |= BIOC_SCNOAUTOASSEMBLE; |
1055 |
|
|
break; |
1056 |
|
|
default: |
1057 |
|
|
strlcpy(fs, s, sz + 1); |
1058 |
|
|
errx(1, "invalid flag %s", fs); |
1059 |
|
|
} |
1060 |
|
|
} |
1061 |
|
|
e++; |
1062 |
|
|
} |
1063 |
|
|
|
1064 |
|
|
return (flags); |
1065 |
|
|
} |
1066 |
|
|
|
1067 |
|
|
void |
1068 |
|
|
bio_deleteraid(char *dev) |
1069 |
|
|
{ |
1070 |
|
|
struct bioc_deleteraid bd; |
1071 |
|
|
memset(&bd, 0, sizeof(bd)); |
1072 |
|
|
|
1073 |
|
|
bd.bd_bio.bio_cookie = bio_cookie; |
1074 |
|
|
/* XXX make this a dev_t instead of a string */ |
1075 |
|
|
strlcpy(bd.bd_dev, dev, sizeof bd.bd_dev); |
1076 |
|
|
if (ioctl(devh, BIOCDELETERAID, &bd)) |
1077 |
|
|
err(1, "BIOCDELETERAID"); |
1078 |
|
|
|
1079 |
|
|
bio_status(&bd.bd_bio.bio_status); |
1080 |
|
|
} |
1081 |
|
|
|
1082 |
|
|
void |
1083 |
|
|
bio_changepass(char *dev) |
1084 |
|
|
{ |
1085 |
|
|
struct bioc_discipline bd; |
1086 |
|
|
struct sr_crypto_kdfpair kdfpair; |
1087 |
|
|
struct sr_crypto_kdfinfo kdfinfo1, kdfinfo2; |
1088 |
|
|
struct sr_crypto_kdf_pbkdf2 kdfhint; |
1089 |
|
|
int rv; |
1090 |
|
|
|
1091 |
|
|
memset(&bd, 0, sizeof(bd)); |
1092 |
|
|
memset(&kdfhint, 0, sizeof(kdfhint)); |
1093 |
|
|
memset(&kdfinfo1, 0, sizeof(kdfinfo1)); |
1094 |
|
|
memset(&kdfinfo2, 0, sizeof(kdfinfo2)); |
1095 |
|
|
|
1096 |
|
|
/* XXX use dev_t instead of string. */ |
1097 |
|
|
strlcpy(bd.bd_dev, dev, sizeof(bd.bd_dev)); |
1098 |
|
|
bd.bd_cmd = SR_IOCTL_GET_KDFHINT; |
1099 |
|
|
bd.bd_size = sizeof(kdfhint); |
1100 |
|
|
bd.bd_data = &kdfhint; |
1101 |
|
|
|
1102 |
|
|
if (ioctl(devh, BIOCDISCIPLINE, &bd)) |
1103 |
|
|
err(1, "BIOCDISCIPLINE"); |
1104 |
|
|
|
1105 |
|
|
bio_status(&bd.bd_bio.bio_status); |
1106 |
|
|
|
1107 |
|
|
/* Current passphrase. */ |
1108 |
|
|
bio_kdf_derive(&kdfinfo1, &kdfhint, "Old passphrase: ", 0); |
1109 |
|
|
|
1110 |
|
|
/* New passphrase. */ |
1111 |
|
|
bio_kdf_derive(&kdfinfo2, &kdfhint, "New passphrase: ", 1); |
1112 |
|
|
|
1113 |
|
|
kdfpair.kdfinfo1 = &kdfinfo1; |
1114 |
|
|
kdfpair.kdfsize1 = sizeof(kdfinfo1); |
1115 |
|
|
kdfpair.kdfinfo2 = &kdfinfo2; |
1116 |
|
|
kdfpair.kdfsize2 = sizeof(kdfinfo2); |
1117 |
|
|
|
1118 |
|
|
bd.bd_cmd = SR_IOCTL_CHANGE_PASSPHRASE; |
1119 |
|
|
bd.bd_size = sizeof(kdfpair); |
1120 |
|
|
bd.bd_data = &kdfpair; |
1121 |
|
|
|
1122 |
|
|
rv = ioctl(devh, BIOCDISCIPLINE, &bd); |
1123 |
|
|
|
1124 |
|
|
memset(&kdfhint, 0, sizeof(kdfhint)); |
1125 |
|
|
explicit_bzero(&kdfinfo1, sizeof(kdfinfo1)); |
1126 |
|
|
explicit_bzero(&kdfinfo2, sizeof(kdfinfo2)); |
1127 |
|
|
|
1128 |
|
|
if (rv) |
1129 |
|
|
err(1, "BIOCDISCIPLINE"); |
1130 |
|
|
|
1131 |
|
|
bio_status(&bd.bd_bio.bio_status); |
1132 |
|
|
} |
1133 |
|
|
|
1134 |
|
|
#define BIOCTL_VIS_NBUF 4 |
1135 |
|
|
#define BIOCTL_VIS_BUFLEN 80 |
1136 |
|
|
|
1137 |
|
|
char * |
1138 |
|
|
bio_vis(char *s) |
1139 |
|
|
{ |
1140 |
|
|
static char rbuf[BIOCTL_VIS_NBUF][BIOCTL_VIS_BUFLEN]; |
1141 |
|
|
static uint idx = 0; |
1142 |
|
|
char *buf; |
1143 |
|
|
|
1144 |
|
|
buf = rbuf[idx++]; |
1145 |
|
|
if (idx == BIOCTL_VIS_NBUF) |
1146 |
|
|
idx = 0; |
1147 |
|
|
|
1148 |
|
|
strnvis(buf, s, BIOCTL_VIS_BUFLEN, VIS_NL|VIS_CSTYLE); |
1149 |
|
|
return (buf); |
1150 |
|
|
} |
1151 |
|
|
|
1152 |
|
|
void |
1153 |
|
|
bio_diskinq(char *sd_dev) |
1154 |
|
|
{ |
1155 |
|
|
struct dk_inquiry di; |
1156 |
|
|
|
1157 |
|
|
if (ioctl(devh, DIOCINQ, &di) == -1) |
1158 |
|
|
err(1, "DIOCINQ"); |
1159 |
|
|
|
1160 |
|
|
printf("%s: <%s, %s, %s>, serial %s\n", sd_dev, bio_vis(di.vendor), |
1161 |
|
|
bio_vis(di.product), bio_vis(di.revision), bio_vis(di.serial)); |
1162 |
|
|
} |
1163 |
|
|
|
1164 |
|
|
void |
1165 |
|
|
bio_patrol(char *arg) |
1166 |
|
|
{ |
1167 |
|
|
struct bioc_patrol bp; |
1168 |
|
|
struct timing timing; |
1169 |
|
|
const char *errstr; |
1170 |
|
|
|
1171 |
|
|
memset(&bp, 0, sizeof(bp)); |
1172 |
|
|
bp.bp_bio.bio_cookie = bio_cookie; |
1173 |
|
|
|
1174 |
|
|
switch (arg[0]) { |
1175 |
|
|
case 'a': |
1176 |
|
|
bp.bp_opcode = BIOC_SPAUTO; |
1177 |
|
|
break; |
1178 |
|
|
|
1179 |
|
|
case 'm': |
1180 |
|
|
bp.bp_opcode = BIOC_SPMANUAL; |
1181 |
|
|
break; |
1182 |
|
|
|
1183 |
|
|
case 'd': |
1184 |
|
|
bp.bp_opcode = BIOC_SPDISABLE; |
1185 |
|
|
break; |
1186 |
|
|
|
1187 |
|
|
case 'g': /* get patrol state */ |
1188 |
|
|
bp.bp_opcode = BIOC_GPSTATUS; |
1189 |
|
|
break; |
1190 |
|
|
|
1191 |
|
|
case 's': /* start/stop patrol */ |
1192 |
|
|
if (strncmp("sta", arg, 3) == 0) |
1193 |
|
|
bp.bp_opcode = BIOC_SPSTART; |
1194 |
|
|
else |
1195 |
|
|
bp.bp_opcode = BIOC_SPSTOP; |
1196 |
|
|
break; |
1197 |
|
|
|
1198 |
|
|
default: |
1199 |
|
|
errx(1, "invalid patrol function: %s", arg); |
1200 |
|
|
} |
1201 |
|
|
|
1202 |
|
|
switch (arg[0]) { |
1203 |
|
|
case 'a': |
1204 |
|
|
errstr = str2patrol(arg, &timing); |
1205 |
|
|
if (errstr) |
1206 |
|
|
errx(1, "Patrol %s: %s", arg, errstr); |
1207 |
|
|
bp.bp_autoival = timing.interval; |
1208 |
|
|
bp.bp_autonext = timing.start; |
1209 |
|
|
break; |
1210 |
|
|
} |
1211 |
|
|
|
1212 |
|
|
if (ioctl(devh, BIOCPATROL, &bp)) |
1213 |
|
|
err(1, "BIOCPATROL"); |
1214 |
|
|
|
1215 |
|
|
bio_status(&bp.bp_bio.bio_status); |
1216 |
|
|
|
1217 |
|
|
if (arg[0] == 'g') { |
1218 |
|
|
const char *mode, *status; |
1219 |
|
|
char interval[40]; |
1220 |
|
|
|
1221 |
|
|
interval[0] = '\0'; |
1222 |
|
|
|
1223 |
|
|
switch (bp.bp_mode) { |
1224 |
|
|
case BIOC_SPMAUTO: |
1225 |
|
|
mode = "auto"; |
1226 |
|
|
snprintf(interval, sizeof interval, |
1227 |
|
|
" interval=%d next=%d", bp.bp_autoival, |
1228 |
|
|
bp.bp_autonext - bp.bp_autonow); |
1229 |
|
|
break; |
1230 |
|
|
case BIOC_SPMMANUAL: |
1231 |
|
|
mode = "manual"; |
1232 |
|
|
break; |
1233 |
|
|
case BIOC_SPMDISABLED: |
1234 |
|
|
mode = "disabled"; |
1235 |
|
|
break; |
1236 |
|
|
default: |
1237 |
|
|
mode = "unknown"; |
1238 |
|
|
break; |
1239 |
|
|
} |
1240 |
|
|
switch (bp.bp_status) { |
1241 |
|
|
case BIOC_SPSSTOPPED: |
1242 |
|
|
status = "stopped"; |
1243 |
|
|
break; |
1244 |
|
|
case BIOC_SPSREADY: |
1245 |
|
|
status = "ready"; |
1246 |
|
|
break; |
1247 |
|
|
case BIOC_SPSACTIVE: |
1248 |
|
|
status = "active"; |
1249 |
|
|
break; |
1250 |
|
|
case BIOC_SPSABORTED: |
1251 |
|
|
status = "aborted"; |
1252 |
|
|
break; |
1253 |
|
|
default: |
1254 |
|
|
status = "unknown"; |
1255 |
|
|
break; |
1256 |
|
|
} |
1257 |
|
|
printf("patrol mode: %s%s\n", mode, interval); |
1258 |
|
|
printf("patrol status: %s\n", status); |
1259 |
|
|
} |
1260 |
|
|
} |
1261 |
|
|
|
1262 |
|
|
void |
1263 |
|
|
derive_key_pkcs(int rounds, u_int8_t *key, size_t keysz, u_int8_t *salt, |
1264 |
|
|
size_t saltsz, char *prompt, int verify) |
1265 |
|
|
{ |
1266 |
|
|
FILE *f; |
1267 |
|
|
size_t pl; |
1268 |
|
|
struct stat sb; |
1269 |
|
|
char passphrase[1024], verifybuf[1024]; |
1270 |
|
|
|
1271 |
|
|
if (!key) |
1272 |
|
|
errx(1, "Invalid key"); |
1273 |
|
|
if (!salt) |
1274 |
|
|
errx(1, "Invalid salt"); |
1275 |
|
|
if (rounds < 1000) |
1276 |
|
|
errx(1, "Too few rounds: %d", rounds); |
1277 |
|
|
|
1278 |
|
|
/* get passphrase */ |
1279 |
|
|
if (password) { |
1280 |
|
|
if ((f = fopen(password, "r")) == NULL) |
1281 |
|
|
err(1, "invalid passphrase file"); |
1282 |
|
|
|
1283 |
|
|
if (fstat(fileno(f), &sb) == -1) |
1284 |
|
|
err(1, "can't stat passphrase file"); |
1285 |
|
|
if (sb.st_uid != 0) |
1286 |
|
|
errx(1, "passphrase file must be owned by root"); |
1287 |
|
|
if ((sb.st_mode & ~S_IFMT) != (S_IRUSR | S_IWUSR)) |
1288 |
|
|
errx(1, "passphrase file has the wrong permissions"); |
1289 |
|
|
|
1290 |
|
|
if (fgets(passphrase, sizeof(passphrase), f) == NULL) |
1291 |
|
|
err(1, "can't read passphrase file"); |
1292 |
|
|
pl = strlen(passphrase); |
1293 |
|
|
if (pl > 0 && passphrase[pl - 1] == '\n') |
1294 |
|
|
passphrase[pl - 1] = '\0'; |
1295 |
|
|
else |
1296 |
|
|
errx(1, "invalid passphrase length"); |
1297 |
|
|
|
1298 |
|
|
fclose(f); |
1299 |
|
|
} else { |
1300 |
|
|
if (readpassphrase(prompt, passphrase, sizeof(passphrase), |
1301 |
|
|
rpp_flag) == NULL) |
1302 |
|
|
err(1, "unable to read passphrase"); |
1303 |
|
|
} |
1304 |
|
|
|
1305 |
|
|
if (verify && !password) { |
1306 |
|
|
/* request user to re-type it */ |
1307 |
|
|
if (readpassphrase("Re-type passphrase: ", verifybuf, |
1308 |
|
|
sizeof(verifybuf), rpp_flag) == NULL) { |
1309 |
|
|
explicit_bzero(passphrase, sizeof(passphrase)); |
1310 |
|
|
err(1, "unable to read passphrase"); |
1311 |
|
|
} |
1312 |
|
|
if ((strlen(passphrase) != strlen(verifybuf)) || |
1313 |
|
|
(strcmp(passphrase, verifybuf) != 0)) { |
1314 |
|
|
explicit_bzero(passphrase, sizeof(passphrase)); |
1315 |
|
|
explicit_bzero(verifybuf, sizeof(verifybuf)); |
1316 |
|
|
errx(1, "Passphrases did not match"); |
1317 |
|
|
} |
1318 |
|
|
/* forget the re-typed one */ |
1319 |
|
|
explicit_bzero(verifybuf, sizeof(verifybuf)); |
1320 |
|
|
} |
1321 |
|
|
|
1322 |
|
|
/* derive key from passphrase */ |
1323 |
|
|
if (pkcs5_pbkdf2(passphrase, strlen(passphrase), salt, saltsz, |
1324 |
|
|
key, keysz, rounds) != 0) |
1325 |
|
|
errx(1, "pbkdf2 failed"); |
1326 |
|
|
|
1327 |
|
|
/* forget passphrase */ |
1328 |
|
|
explicit_bzero(passphrase, sizeof(passphrase)); |
1329 |
|
|
|
1330 |
|
|
return; |
1331 |
|
|
} |