dkctl.c revision 1.27 1 /* $NetBSD: dkctl.c,v 1.27 2024/09/14 08:30:44 mlelstv Exp $ */
2
3 /*
4 * Copyright 2001 Wasabi Systems, Inc.
5 * All rights reserved.
6 *
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
23 * written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 /*
39 * dkctl(8) -- a program to manipulate disks.
40 */
41 #include <sys/cdefs.h>
42
43 #ifndef lint
44 __RCSID("$NetBSD: dkctl.c,v 1.27 2024/09/14 08:30:44 mlelstv Exp $");
45 #endif
46
47 #include <sys/param.h>
48 #include <sys/ioctl.h>
49 #include <sys/stat.h>
50 #include <sys/dkio.h>
51 #include <sys/disk.h>
52 #include <sys/queue.h>
53 #include <err.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <stdbool.h>
59 #include <string.h>
60 #include <unistd.h>
61 #include <util.h>
62
63 #define YES 1
64 #define NO 0
65
66 /* I don't think nl_langinfo is suitable in this case */
67 #define YES_STR "yes"
68 #define NO_STR "no"
69 #define YESNO_ARG YES_STR " | " NO_STR
70
71 #ifndef PRIdaddr
72 #define PRIdaddr PRId64
73 #endif
74
75 struct command {
76 const char *cmd_name;
77 const char *arg_names;
78 void (*cmd_func)(int, char *[]);
79 int open_flags;
80 };
81
82 static struct command *lookup(const char *);
83 __dead static void usage(void);
84 static void run(int, char *[]);
85 static void showall(void);
86
87 static int fd; /* file descriptor for device */
88 static const char *dvname; /* device name */
89 static char dvname_store[MAXPATHLEN]; /* for opendisk(3) */
90
91 static int dkw_sort(const void *, const void *);
92 static int yesno(const char *);
93
94 static void disk_getcache(int, char *[]);
95 static void disk_setcache(int, char *[]);
96 static void disk_synccache(int, char *[]);
97 static void disk_keeplabel(int, char *[]);
98 static void disk_badsectors(int, char *[]);
99
100 static void disk_addwedge(int, char *[]);
101 static void disk_delwedge(int, char *[]);
102 static void disk_getwedgeinfo(int, char *[]);
103 static void disk_getgeometry(int, char *[]);
104 static void disk_listwedges(int, char *[]);
105 static void disk_makewedges(int, char *[]);
106 static void disk_strategy(int, char *[]);
107
108 static struct command commands[] = {
109 { "addwedge",
110 "name startblk blkcnt ptype",
111 disk_addwedge,
112 O_RDWR },
113
114 { "badsector",
115 "flush | list | retry",
116 disk_badsectors,
117 O_RDWR },
118
119 { "delwedge",
120 "dk",
121 disk_delwedge,
122 O_RDWR },
123
124 { "getcache",
125 "",
126 disk_getcache,
127 O_RDONLY },
128
129 { "getwedgeinfo",
130 "",
131 disk_getwedgeinfo,
132 O_RDONLY },
133
134 { "getgeometry",
135 "",
136 disk_getgeometry,
137 O_RDONLY },
138
139 { "keeplabel",
140 YESNO_ARG,
141 disk_keeplabel,
142 O_RDWR },
143
144 { "listwedges",
145 "",
146 disk_listwedges,
147 O_RDONLY },
148
149 { "makewedges",
150 "",
151 disk_makewedges,
152 O_RDWR },
153
154 { "setcache",
155 "none | r | w | rw [save]",
156 disk_setcache,
157 O_RDWR },
158
159 { "strategy",
160 "[name]",
161 disk_strategy,
162 O_RDWR },
163
164 { "synccache",
165 "[force]",
166 disk_synccache,
167 O_RDWR },
168
169 { NULL,
170 NULL,
171 NULL,
172 0 },
173 };
174
175 int
176 main(int argc, char *argv[])
177 {
178
179 /* Must have at least: device command */
180 if (argc < 2)
181 usage();
182
183 dvname = argv[1];
184 if (argc == 2)
185 showall();
186 else
187 run(argc - 2, argv + 2);
188
189 return EXIT_SUCCESS;
190 }
191
192 static void
193 run(int argc, char *argv[])
194 {
195 struct command *command;
196
197 command = lookup(argv[0]);
198
199 /* Open the device. */
200 fd = opendisk(dvname, command->open_flags, dvname_store,
201 sizeof(dvname_store), 0);
202 if (fd == -1)
203 err(EXIT_FAILURE, "%s", dvname);
204 dvname = dvname_store;
205
206 (*command->cmd_func)(argc, argv);
207
208 /* Close the device. */
209 (void)close(fd);
210 }
211
212 static struct command *
213 lookup(const char *name)
214 {
215 int i;
216
217 /* Look up the command. */
218 for (i = 0; commands[i].cmd_name != NULL; i++)
219 if (strcmp(name, commands[i].cmd_name) == 0)
220 break;
221 if (commands[i].cmd_name == NULL)
222 errx(EXIT_FAILURE, "unknown command: %s", name);
223
224 return &commands[i];
225 }
226
227 static void
228 usage(void)
229 {
230 int i;
231
232 fprintf(stderr,
233 "Usage: %s device\n"
234 " %s device command [arg [...]]\n",
235 getprogname(), getprogname());
236
237 fprintf(stderr, " Available commands:\n");
238 for (i = 0; commands[i].cmd_name != NULL; i++)
239 fprintf(stderr, "\t%s %s\n", commands[i].cmd_name,
240 commands[i].arg_names);
241
242 exit(EXIT_FAILURE);
243 }
244
245 static void
246 showall(void)
247 {
248 static const char *cmds[] = { "strategy", "getcache", "listwedges" };
249 size_t i;
250 char *args[2];
251
252 args[1] = NULL;
253 for (i = 0; i < __arraycount(cmds); i++) {
254 printf("%s:\n", cmds[i]);
255 args[0] = __UNCONST(cmds[i]);
256 run(1, args);
257 putchar('\n');
258 }
259 }
260
261 static void
262 disk_strategy(int argc, char *argv[])
263 {
264 struct disk_strategy odks;
265 struct disk_strategy dks;
266
267 memset(&dks, 0, sizeof(dks));
268 if (ioctl(fd, DIOCGSTRATEGY, &odks) == -1) {
269 err(EXIT_FAILURE, "%s: DIOCGSTRATEGY", dvname);
270 }
271
272 memset(&dks, 0, sizeof(dks));
273 switch (argc) {
274 case 1:
275 /* show the buffer queue strategy used */
276 printf("%s: %s\n", dvname, odks.dks_name);
277 return;
278 case 2:
279 /* set the buffer queue strategy */
280 strlcpy(dks.dks_name, argv[1], sizeof(dks.dks_name));
281 if (ioctl(fd, DIOCSSTRATEGY, &dks) == -1) {
282 err(EXIT_FAILURE, "%s: DIOCSSTRATEGY", dvname);
283 }
284 printf("%s: %s -> %s\n", dvname, odks.dks_name, argv[1]);
285 break;
286 default:
287 usage();
288 /* NOTREACHED */
289 }
290 }
291
292 static void
293 disk_getcache(int argc, char *argv[])
294 {
295 int bits;
296
297 if (ioctl(fd, DIOCGCACHE, &bits) == -1)
298 err(EXIT_FAILURE, "%s: getcache", dvname);
299
300 if ((bits & (DKCACHE_READ|DKCACHE_WRITE)) == 0)
301 printf("%s: No caches enabled\n", dvname);
302 else {
303 if (bits & DKCACHE_READ)
304 printf("%s: read cache enabled\n", dvname);
305 if (bits & DKCACHE_WRITE)
306 printf("%s: write-back cache enabled\n", dvname);
307 }
308
309 printf("%s: read cache enable is %schangeable\n", dvname,
310 (bits & DKCACHE_RCHANGE) ? "" : "not ");
311 printf("%s: write cache enable is %schangeable\n", dvname,
312 (bits & DKCACHE_WCHANGE) ? "" : "not ");
313
314 printf("%s: cache parameters are %ssavable\n", dvname,
315 (bits & DKCACHE_SAVE) ? "" : "not ");
316
317 #ifdef DKCACHE_FUA
318 printf("%s: cache Force Unit Access (FUA) %ssupported\n", dvname,
319 (bits & DKCACHE_FUA) ? "" : "not ");
320 #endif /* DKCACHE_FUA */
321
322 #ifdef DKCACHE_DPO
323 printf("%s: cache Disable Page Out (DPO) %ssupported\n", dvname,
324 (bits & DKCACHE_DPO) ? "" : "not ");
325 #endif /* DKCACHE_DPO */
326 }
327
328 static void
329 disk_setcache(int argc, char *argv[])
330 {
331 int bits;
332
333 if (argc > 3 || argc == 1)
334 usage();
335
336 if (strcmp(argv[1], "none") == 0)
337 bits = 0;
338 else if (strcmp(argv[1], "r") == 0)
339 bits = DKCACHE_READ;
340 else if (strcmp(argv[1], "w") == 0)
341 bits = DKCACHE_WRITE;
342 else if (strcmp(argv[1], "rw") == 0)
343 bits = DKCACHE_READ|DKCACHE_WRITE;
344 else
345 usage();
346
347 if (argc == 3) {
348 if (strcmp(argv[2], "save") == 0)
349 bits |= DKCACHE_SAVE;
350 else
351 usage();
352 }
353
354 if (ioctl(fd, DIOCSCACHE, &bits) == -1)
355 err(EXIT_FAILURE, "%s: %s", dvname, argv[0]);
356 }
357
358 static void
359 disk_synccache(int argc, char *argv[])
360 {
361 int force;
362
363 switch (argc) {
364 case 1:
365 force = 0;
366 break;
367
368 case 2:
369 if (strcmp(argv[1], "force") == 0)
370 force = 1;
371 else
372 usage();
373 break;
374
375 default:
376 usage();
377 }
378
379 if (ioctl(fd, DIOCCACHESYNC, &force) == -1)
380 err(EXIT_FAILURE, "%s: %s", dvname, argv[0]);
381 }
382
383 static void
384 disk_keeplabel(int argc, char *argv[])
385 {
386 int keep;
387 int yn;
388
389 if (argc != 2)
390 usage();
391
392 yn = yesno(argv[1]);
393 if (yn < 0)
394 usage();
395
396 keep = yn == YES;
397
398 if (ioctl(fd, DIOCKLABEL, &keep) == -1)
399 err(EXIT_FAILURE, "%s: %s", dvname, argv[0]);
400 }
401
402
403 static void
404 disk_badsectors(int argc, char *argv[])
405 {
406 struct disk_badsectors *dbs, *dbs2, buffer[200];
407 SLIST_HEAD(, disk_badsectors) dbstop;
408 struct disk_badsecinfo dbsi;
409 daddr_t blk, totbad, bad;
410 u_int32_t count;
411 struct stat sb;
412 u_char *block;
413 time_t tm;
414
415 if (argc != 2)
416 usage();
417
418 if (strcmp(argv[1], "list") == 0) {
419 /*
420 * Copy the list of kernel bad sectors out in chunks that fit
421 * into buffer[]. Updating dbsi_skip means we don't sit here
422 * forever only getting the first chunk that fit in buffer[].
423 */
424 dbsi.dbsi_buffer = (caddr_t)buffer;
425 dbsi.dbsi_bufsize = sizeof(buffer);
426 dbsi.dbsi_skip = 0;
427 dbsi.dbsi_copied = 0;
428 dbsi.dbsi_left = 0;
429
430 do {
431 if (ioctl(fd, DIOCBSLIST, (caddr_t)&dbsi) == -1)
432 err(EXIT_FAILURE, "%s: badsectors list", dvname);
433
434 dbs = (struct disk_badsectors *)dbsi.dbsi_buffer;
435 for (count = dbsi.dbsi_copied; count > 0; count--) {
436 tm = dbs->dbs_failedat.tv_sec;
437 printf("%s: blocks %" PRIdaddr " - %" PRIdaddr " failed at %s",
438 dvname, dbs->dbs_min, dbs->dbs_max,
439 ctime(&tm));
440 dbs++;
441 }
442 dbsi.dbsi_skip += dbsi.dbsi_copied;
443 } while (dbsi.dbsi_left != 0);
444
445 } else if (strcmp(argv[1], "flush") == 0) {
446 if (ioctl(fd, DIOCBSFLUSH) == -1)
447 err(EXIT_FAILURE, "%s: badsectors flush", dvname);
448
449 } else if (strcmp(argv[1], "retry") == 0) {
450 /*
451 * Enforce use of raw device here because the block device
452 * causes access to blocks to be clustered in a larger group,
453 * making it impossible to determine which individual sectors
454 * are the cause of a problem.
455 */
456 if (fstat(fd, &sb) == -1)
457 err(EXIT_FAILURE, "fstat");
458
459 if (!S_ISCHR(sb.st_mode)) {
460 fprintf(stderr, "'badsector retry' must be used %s\n",
461 "with character device");
462 exit(1);
463 }
464
465 SLIST_INIT(&dbstop);
466
467 /*
468 * Build up a copy of the in-kernel list in a number of stages.
469 * That the list we build up here is in the reverse order to
470 * the kernel's is of no concern.
471 */
472 dbsi.dbsi_buffer = (caddr_t)buffer;
473 dbsi.dbsi_bufsize = sizeof(buffer);
474 dbsi.dbsi_skip = 0;
475 dbsi.dbsi_copied = 0;
476 dbsi.dbsi_left = 0;
477
478 do {
479 if (ioctl(fd, DIOCBSLIST, (caddr_t)&dbsi) == -1)
480 err(EXIT_FAILURE, "%s: badsectors list", dvname);
481
482 dbs = (struct disk_badsectors *)dbsi.dbsi_buffer;
483 for (count = dbsi.dbsi_copied; count > 0; count--) {
484 dbs2 = malloc(sizeof *dbs2);
485 if (dbs2 == NULL)
486 err(EXIT_FAILURE, NULL);
487 *dbs2 = *dbs;
488 SLIST_INSERT_HEAD(&dbstop, dbs2, dbs_next);
489 dbs++;
490 }
491 dbsi.dbsi_skip += dbsi.dbsi_copied;
492 } while (dbsi.dbsi_left != 0);
493
494 /*
495 * Just calculate and print out something that will hopefully
496 * provide some useful information about what's going to take
497 * place next (if anything.)
498 */
499 bad = 0;
500 totbad = 0;
501 if ((block = calloc(1, DEV_BSIZE)) == NULL)
502 err(EXIT_FAILURE, NULL);
503 SLIST_FOREACH(dbs, &dbstop, dbs_next) {
504 bad++;
505 totbad += dbs->dbs_max - dbs->dbs_min + 1;
506 }
507
508 printf("%s: bad sector clusters %"PRIdaddr
509 " total sectors %"PRIdaddr"\n", dvname, bad, totbad);
510
511 /*
512 * Clear out the kernel's list of bad sectors, ready for us
513 * to test all those it thought were bad.
514 */
515 if (ioctl(fd, DIOCBSFLUSH) == -1)
516 err(EXIT_FAILURE, "%s: badsectors flush", dvname);
517
518 printf("%s: bad sectors flushed\n", dvname);
519
520 /*
521 * For each entry we obtained from the kernel, retry each
522 * individual sector recorded as bad by seeking to it and
523 * attempting to read it in. Print out a line item for each
524 * bad block we verify.
525 *
526 * PRIdaddr is used here because the type of dbs_max is daddr_t
527 * and that may be either a 32bit or 64bit number(!)
528 */
529 SLIST_FOREACH(dbs, &dbstop, dbs_next) {
530 printf("%s: Retrying %"PRIdaddr" - %"
531 PRIdaddr"\n", dvname, dbs->dbs_min, dbs->dbs_max);
532
533 for (blk = dbs->dbs_min; blk <= dbs->dbs_max; blk++) {
534 if (lseek(fd, (off_t)blk * DEV_BSIZE,
535 SEEK_SET) == -1) {
536 warn("%s: lseek block %" PRIdaddr "",
537 dvname, blk);
538 continue;
539 }
540 printf("%s: block %"PRIdaddr" - ", dvname, blk);
541 if (read(fd, block, DEV_BSIZE) != DEV_BSIZE)
542 printf("failed\n");
543 else
544 printf("ok\n");
545 fflush(stdout);
546 }
547 }
548 }
549 }
550
551 static void
552 disk_addwedge(int argc, char *argv[])
553 {
554 struct dkwedge_info dkw;
555 char *cp;
556 daddr_t start;
557 uint64_t size;
558
559 if (argc != 5)
560 usage();
561
562 /* XXX Unicode: dkw_wname is supposed to be utf-8 */
563 if (strlcpy((char *)dkw.dkw_wname, argv[1], sizeof(dkw.dkw_wname)) >=
564 sizeof(dkw.dkw_wname))
565 errx(EXIT_FAILURE, "Wedge name too long; max %zd characters",
566 sizeof(dkw.dkw_wname) - 1);
567
568 if (strlcpy(dkw.dkw_ptype, argv[4], sizeof(dkw.dkw_ptype)) >=
569 sizeof(dkw.dkw_ptype))
570 errx(EXIT_FAILURE, "Wedge partition type too long; max %zd characters",
571 sizeof(dkw.dkw_ptype) - 1);
572
573 errno = 0;
574 start = strtoll(argv[2], &cp, 0);
575 if (*cp != '\0')
576 errx(EXIT_FAILURE, "Invalid start block: %s", argv[2]);
577 if (errno == ERANGE && (start == LLONG_MAX ||
578 start == LLONG_MIN))
579 errx(EXIT_FAILURE, "Start block out of range.");
580 if (start < 0)
581 errx(EXIT_FAILURE, "Start block must be >= 0.");
582
583 errno = 0;
584 size = strtoull(argv[3], &cp, 0);
585 if (*cp != '\0')
586 errx(EXIT_FAILURE, "Invalid block count: %s", argv[3]);
587 if (errno == ERANGE && (size == ULLONG_MAX))
588 errx(EXIT_FAILURE, "Block count out of range.");
589
590 dkw.dkw_offset = start;
591 dkw.dkw_size = size;
592
593 if (ioctl(fd, DIOCAWEDGE, &dkw) == -1)
594 err(EXIT_FAILURE, "%s: %s", dvname, argv[0]);
595 else
596 printf("%s created successfully.\n", dkw.dkw_devname);
597
598 }
599
600 static void
601 disk_delwedge(int argc, char *argv[])
602 {
603 struct dkwedge_info dkw;
604
605 if (argc != 2)
606 usage();
607
608 if (strlcpy(dkw.dkw_devname, argv[1], sizeof(dkw.dkw_devname)) >=
609 sizeof(dkw.dkw_devname))
610 errx(EXIT_FAILURE, "Wedge dk name too long; max %zd characters",
611 sizeof(dkw.dkw_devname) - 1);
612
613 if (ioctl(fd, DIOCDWEDGE, &dkw) == -1)
614 err(EXIT_FAILURE, "%s: %s", dvname, argv[0]);
615 }
616
617 static void
618 disk_getwedgeinfo(int argc, char *argv[])
619 {
620 struct dkwedge_info dkw;
621
622 if (argc != 1)
623 usage();
624
625 if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == -1)
626 err(EXIT_FAILURE, "%s: getwedgeinfo", dvname);
627
628 printf("%s at %s: %s\n", dkw.dkw_devname, dkw.dkw_parent,
629 dkw.dkw_wname); /* XXX Unicode */
630 printf("%s: %"PRIu64" blocks at %"PRId64", type: %s\n",
631 dkw.dkw_devname, dkw.dkw_size, dkw.dkw_offset, dkw.dkw_ptype);
632 }
633
634 static void
635 disk_getgeometry(int argc, char *argv[])
636 {
637 off_t bytes;
638 u_int secsize;
639
640 if (argc != 1)
641 usage();
642
643 if (ioctl(fd, DIOCGMEDIASIZE, &bytes) == -1)
644 err(EXIT_FAILURE, "%s: getmediasize", dvname);
645
646 secsize = DEV_BSIZE;
647 if (ioctl(fd, DIOCGSECTORSIZE, &secsize) == -1)
648 warn("%s: getsectorsize", dvname);
649
650 printf("%s: %"PRIu64" bytes in %"PRIu64" blocks of %u bytes\n",
651 dvname, bytes, bytes/secsize, secsize);
652 }
653
654 static void
655 disk_listwedges(int argc, char *argv[])
656 {
657 struct dkwedge_info *dkw;
658 struct dkwedge_list dkwl;
659 size_t bufsize;
660 u_int i;
661 int c;
662 bool error, quiet;
663
664 optreset = 1;
665 optind = 1;
666 quiet = error = false;
667 while ((c = getopt(argc, argv, "qe")) != -1)
668 switch (c) {
669 case 'e':
670 error = true;
671 break;
672 case 'q':
673 quiet = true;
674 break;
675 default:
676 usage();
677 }
678
679 argc -= optind;
680 argv += optind;
681
682 if (argc != 0)
683 usage();
684
685 dkw = NULL;
686 dkwl.dkwl_buf = dkw;
687 dkwl.dkwl_bufsize = 0;
688
689 for (;;) {
690 if (ioctl(fd, DIOCLWEDGES, &dkwl) == -1)
691 err(EXIT_FAILURE, "%s: listwedges", dvname);
692 if (dkwl.dkwl_nwedges == dkwl.dkwl_ncopied)
693 break;
694 bufsize = dkwl.dkwl_nwedges * sizeof(*dkw);
695 if (dkwl.dkwl_bufsize < bufsize) {
696 dkw = realloc(dkwl.dkwl_buf, bufsize);
697 if (dkw == NULL)
698 errx(EXIT_FAILURE, "%s: listwedges: unable to "
699 "allocate wedge info buffer", dvname);
700 dkwl.dkwl_buf = dkw;
701 dkwl.dkwl_bufsize = bufsize;
702 }
703 }
704
705 if (dkwl.dkwl_nwedges == 0) {
706 if (!quiet)
707 printf("%s: no wedges configured\n", dvname);
708 if (error)
709 exit(EXIT_FAILURE);
710 return;
711 }
712
713 if (quiet)
714 return;
715
716 qsort(dkw, dkwl.dkwl_nwedges, sizeof(*dkw), dkw_sort);
717
718 printf("%s: %u wedge%s:\n", dvname, dkwl.dkwl_nwedges,
719 dkwl.dkwl_nwedges == 1 ? "" : "s");
720 for (i = 0; i < dkwl.dkwl_nwedges; i++) {
721 printf("%s: %s, %"PRIu64" blocks at %"PRId64", type: %s\n",
722 dkw[i].dkw_devname,
723 dkw[i].dkw_wname, /* XXX Unicode */
724 dkw[i].dkw_size, dkw[i].dkw_offset, dkw[i].dkw_ptype);
725 }
726 }
727
728 static void
729 disk_makewedges(int argc, char *argv[])
730 {
731 int bits;
732
733 if (argc != 1)
734 usage();
735
736 if (ioctl(fd, DIOCMWEDGES, &bits) == -1)
737 err(EXIT_FAILURE, "%s: %s", dvname, argv[0]);
738 else
739 printf("successfully scanned %s.\n", dvname);
740 }
741
742 static int
743 dkw_sort(const void *a, const void *b)
744 {
745 const struct dkwedge_info *dkwa = a, *dkwb = b;
746 const daddr_t oa = dkwa->dkw_offset, ob = dkwb->dkw_offset;
747
748 return (oa < ob) ? -1 : (oa > ob) ? 1 : 0;
749 }
750
751 /*
752 * return YES, NO or -1.
753 */
754 static int
755 yesno(const char *p)
756 {
757
758 if (!strcmp(p, YES_STR))
759 return YES;
760 if (!strcmp(p, NO_STR))
761 return NO;
762 return -1;
763 }
764