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
main(int argc,char * argv[])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
run(int argc,char * argv[])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 *
lookup(const char * name)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
usage(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
showall(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
disk_strategy(int argc,char * argv[])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
disk_getcache(int argc,char * argv[])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
disk_setcache(int argc,char * argv[])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
disk_synccache(int argc,char * argv[])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
disk_keeplabel(int argc,char * argv[])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
disk_badsectors(int argc,char * argv[])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
disk_addwedge(int argc,char * argv[])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
disk_delwedge(int argc,char * argv[])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
disk_getwedgeinfo(int argc,char * argv[])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
disk_getgeometry(int argc,char * argv[])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
disk_listwedges(int argc,char * argv[])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
disk_makewedges(int argc,char * argv[])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
dkw_sort(const void * a,const void * b)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
yesno(const char * p)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