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