1 /* $NetBSD: dkctl.c,v 1.26 2018/01/07 12:29:25 kre 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.26 2018/01/07 12:29:25 kre 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_listwedges(int, char *[]); 104 static void disk_makewedges(int, char *[]); 105 static void disk_strategy(int, char *[]); 106 107 static struct command commands[] = { 108 { "addwedge", 109 "name startblk blkcnt ptype", 110 disk_addwedge, 111 O_RDWR }, 112 113 { "badsector", 114 "flush | list | retry", 115 disk_badsectors, 116 O_RDWR }, 117 118 { "delwedge", 119 "dk", 120 disk_delwedge, 121 O_RDWR }, 122 123 { "getcache", 124 "", 125 disk_getcache, 126 O_RDONLY }, 127 128 { "getwedgeinfo", 129 "", 130 disk_getwedgeinfo, 131 O_RDONLY }, 132 133 { "keeplabel", 134 YESNO_ARG, 135 disk_keeplabel, 136 O_RDWR }, 137 138 { "listwedges", 139 "", 140 disk_listwedges, 141 O_RDONLY }, 142 143 { "makewedges", 144 "", 145 disk_makewedges, 146 O_RDWR }, 147 148 { "setcache", 149 "none | r | w | rw [save]", 150 disk_setcache, 151 O_RDWR }, 152 153 { "strategy", 154 "[name]", 155 disk_strategy, 156 O_RDWR }, 157 158 { "synccache", 159 "[force]", 160 disk_synccache, 161 O_RDWR }, 162 163 { NULL, 164 NULL, 165 NULL, 166 0 }, 167 }; 168 169 int 170 main(int argc, char *argv[]) 171 { 172 173 /* Must have at least: device command */ 174 if (argc < 2) 175 usage(); 176 177 dvname = argv[1]; 178 if (argc == 2) 179 showall(); 180 else 181 run(argc - 2, argv + 2); 182 183 return EXIT_SUCCESS; 184 } 185 186 static void 187 run(int argc, char *argv[]) 188 { 189 struct command *command; 190 191 command = lookup(argv[0]); 192 193 /* Open the device. */ 194 fd = opendisk(dvname, command->open_flags, dvname_store, 195 sizeof(dvname_store), 0); 196 if (fd == -1) 197 err(EXIT_FAILURE, "%s", dvname); 198 dvname = dvname_store; 199 200 (*command->cmd_func)(argc, argv); 201 202 /* Close the device. */ 203 (void)close(fd); 204 } 205 206 static struct command * 207 lookup(const char *name) 208 { 209 int i; 210 211 /* Look up the command. */ 212 for (i = 0; commands[i].cmd_name != NULL; i++) 213 if (strcmp(name, commands[i].cmd_name) == 0) 214 break; 215 if (commands[i].cmd_name == NULL) 216 errx(EXIT_FAILURE, "unknown command: %s", name); 217 218 return &commands[i]; 219 } 220 221 static void 222 usage(void) 223 { 224 int i; 225 226 fprintf(stderr, 227 "Usage: %s device\n" 228 " %s device command [arg [...]]\n", 229 getprogname(), getprogname()); 230 231 fprintf(stderr, " Available commands:\n"); 232 for (i = 0; commands[i].cmd_name != NULL; i++) 233 fprintf(stderr, "\t%s %s\n", commands[i].cmd_name, 234 commands[i].arg_names); 235 236 exit(EXIT_FAILURE); 237 } 238 239 static void 240 showall(void) 241 { 242 static const char *cmds[] = { "strategy", "getcache", "listwedges" }; 243 size_t i; 244 char *args[2]; 245 246 args[1] = NULL; 247 for (i = 0; i < __arraycount(cmds); i++) { 248 printf("%s:\n", cmds[i]); 249 args[0] = __UNCONST(cmds[i]); 250 run(1, args); 251 putchar('\n'); 252 } 253 } 254 255 static void 256 disk_strategy(int argc, char *argv[]) 257 { 258 struct disk_strategy odks; 259 struct disk_strategy dks; 260 261 memset(&dks, 0, sizeof(dks)); 262 if (ioctl(fd, DIOCGSTRATEGY, &odks) == -1) { 263 err(EXIT_FAILURE, "%s: DIOCGSTRATEGY", dvname); 264 } 265 266 memset(&dks, 0, sizeof(dks)); 267 switch (argc) { 268 case 1: 269 /* show the buffer queue strategy used */ 270 printf("%s: %s\n", dvname, odks.dks_name); 271 return; 272 case 2: 273 /* set the buffer queue strategy */ 274 strlcpy(dks.dks_name, argv[1], sizeof(dks.dks_name)); 275 if (ioctl(fd, DIOCSSTRATEGY, &dks) == -1) { 276 err(EXIT_FAILURE, "%s: DIOCSSTRATEGY", dvname); 277 } 278 printf("%s: %s -> %s\n", dvname, odks.dks_name, argv[1]); 279 break; 280 default: 281 usage(); 282 /* NOTREACHED */ 283 } 284 } 285 286 static void 287 disk_getcache(int argc, char *argv[]) 288 { 289 int bits; 290 291 if (ioctl(fd, DIOCGCACHE, &bits) == -1) 292 err(EXIT_FAILURE, "%s: getcache", dvname); 293 294 if ((bits & (DKCACHE_READ|DKCACHE_WRITE)) == 0) 295 printf("%s: No caches enabled\n", dvname); 296 else { 297 if (bits & DKCACHE_READ) 298 printf("%s: read cache enabled\n", dvname); 299 if (bits & DKCACHE_WRITE) 300 printf("%s: write-back cache enabled\n", dvname); 301 } 302 303 printf("%s: read cache enable is %schangeable\n", dvname, 304 (bits & DKCACHE_RCHANGE) ? "" : "not "); 305 printf("%s: write cache enable is %schangeable\n", dvname, 306 (bits & DKCACHE_WCHANGE) ? "" : "not "); 307 308 printf("%s: cache parameters are %ssavable\n", dvname, 309 (bits & DKCACHE_SAVE) ? "" : "not "); 310 311 #ifdef DKCACHE_FUA 312 printf("%s: cache Force Unit Access (FUA) %ssupported\n", dvname, 313 (bits & DKCACHE_FUA) ? "" : "not "); 314 #endif /* DKCACHE_FUA */ 315 316 #ifdef DKCACHE_DPO 317 printf("%s: cache Disable Page Out (DPO) %ssupported\n", dvname, 318 (bits & DKCACHE_DPO) ? "" : "not "); 319 #endif /* DKCACHE_DPO */ 320 } 321 322 static void 323 disk_setcache(int argc, char *argv[]) 324 { 325 int bits; 326 327 if (argc > 3 || argc == 1) 328 usage(); 329 330 if (strcmp(argv[1], "none") == 0) 331 bits = 0; 332 else if (strcmp(argv[1], "r") == 0) 333 bits = DKCACHE_READ; 334 else if (strcmp(argv[1], "w") == 0) 335 bits = DKCACHE_WRITE; 336 else if (strcmp(argv[1], "rw") == 0) 337 bits = DKCACHE_READ|DKCACHE_WRITE; 338 else 339 usage(); 340 341 if (argc == 3) { 342 if (strcmp(argv[2], "save") == 0) 343 bits |= DKCACHE_SAVE; 344 else 345 usage(); 346 } 347 348 if (ioctl(fd, DIOCSCACHE, &bits) == -1) 349 err(EXIT_FAILURE, "%s: %s", dvname, argv[0]); 350 } 351 352 static void 353 disk_synccache(int argc, char *argv[]) 354 { 355 int force; 356 357 switch (argc) { 358 case 1: 359 force = 0; 360 break; 361 362 case 2: 363 if (strcmp(argv[1], "force") == 0) 364 force = 1; 365 else 366 usage(); 367 break; 368 369 default: 370 usage(); 371 } 372 373 if (ioctl(fd, DIOCCACHESYNC, &force) == -1) 374 err(EXIT_FAILURE, "%s: %s", dvname, argv[0]); 375 } 376 377 static void 378 disk_keeplabel(int argc, char *argv[]) 379 { 380 int keep; 381 int yn; 382 383 if (argc != 2) 384 usage(); 385 386 yn = yesno(argv[1]); 387 if (yn < 0) 388 usage(); 389 390 keep = yn == YES; 391 392 if (ioctl(fd, DIOCKLABEL, &keep) == -1) 393 err(EXIT_FAILURE, "%s: %s", dvname, argv[0]); 394 } 395 396 397 static void 398 disk_badsectors(int argc, char *argv[]) 399 { 400 struct disk_badsectors *dbs, *dbs2, buffer[200]; 401 SLIST_HEAD(, disk_badsectors) dbstop; 402 struct disk_badsecinfo dbsi; 403 daddr_t blk, totbad, bad; 404 u_int32_t count; 405 struct stat sb; 406 u_char *block; 407 time_t tm; 408 409 if (argc != 2) 410 usage(); 411 412 if (strcmp(argv[1], "list") == 0) { 413 /* 414 * Copy the list of kernel bad sectors out in chunks that fit 415 * into buffer[]. Updating dbsi_skip means we don't sit here 416 * forever only getting the first chunk that fit in buffer[]. 417 */ 418 dbsi.dbsi_buffer = (caddr_t)buffer; 419 dbsi.dbsi_bufsize = sizeof(buffer); 420 dbsi.dbsi_skip = 0; 421 dbsi.dbsi_copied = 0; 422 dbsi.dbsi_left = 0; 423 424 do { 425 if (ioctl(fd, DIOCBSLIST, (caddr_t)&dbsi) == -1) 426 err(EXIT_FAILURE, "%s: badsectors list", dvname); 427 428 dbs = (struct disk_badsectors *)dbsi.dbsi_buffer; 429 for (count = dbsi.dbsi_copied; count > 0; count--) { 430 tm = dbs->dbs_failedat.tv_sec; 431 printf("%s: blocks %" PRIdaddr " - %" PRIdaddr " failed at %s", 432 dvname, dbs->dbs_min, dbs->dbs_max, 433 ctime(&tm)); 434 dbs++; 435 } 436 dbsi.dbsi_skip += dbsi.dbsi_copied; 437 } while (dbsi.dbsi_left != 0); 438 439 } else if (strcmp(argv[1], "flush") == 0) { 440 if (ioctl(fd, DIOCBSFLUSH) == -1) 441 err(EXIT_FAILURE, "%s: badsectors flush", dvname); 442 443 } else if (strcmp(argv[1], "retry") == 0) { 444 /* 445 * Enforce use of raw device here because the block device 446 * causes access to blocks to be clustered in a larger group, 447 * making it impossible to determine which individual sectors 448 * are the cause of a problem. 449 */ 450 if (fstat(fd, &sb) == -1) 451 err(EXIT_FAILURE, "fstat"); 452 453 if (!S_ISCHR(sb.st_mode)) { 454 fprintf(stderr, "'badsector retry' must be used %s\n", 455 "with character device"); 456 exit(1); 457 } 458 459 SLIST_INIT(&dbstop); 460 461 /* 462 * Build up a copy of the in-kernel list in a number of stages. 463 * That the list we build up here is in the reverse order to 464 * the kernel's is of no concern. 465 */ 466 dbsi.dbsi_buffer = (caddr_t)buffer; 467 dbsi.dbsi_bufsize = sizeof(buffer); 468 dbsi.dbsi_skip = 0; 469 dbsi.dbsi_copied = 0; 470 dbsi.dbsi_left = 0; 471 472 do { 473 if (ioctl(fd, DIOCBSLIST, (caddr_t)&dbsi) == -1) 474 err(EXIT_FAILURE, "%s: badsectors list", dvname); 475 476 dbs = (struct disk_badsectors *)dbsi.dbsi_buffer; 477 for (count = dbsi.dbsi_copied; count > 0; count--) { 478 dbs2 = malloc(sizeof *dbs2); 479 if (dbs2 == NULL) 480 err(EXIT_FAILURE, NULL); 481 *dbs2 = *dbs; 482 SLIST_INSERT_HEAD(&dbstop, dbs2, dbs_next); 483 dbs++; 484 } 485 dbsi.dbsi_skip += dbsi.dbsi_copied; 486 } while (dbsi.dbsi_left != 0); 487 488 /* 489 * Just calculate and print out something that will hopefully 490 * provide some useful information about what's going to take 491 * place next (if anything.) 492 */ 493 bad = 0; 494 totbad = 0; 495 if ((block = calloc(1, DEV_BSIZE)) == NULL) 496 err(EXIT_FAILURE, NULL); 497 SLIST_FOREACH(dbs, &dbstop, dbs_next) { 498 bad++; 499 totbad += dbs->dbs_max - dbs->dbs_min + 1; 500 } 501 502 printf("%s: bad sector clusters %"PRIdaddr 503 " total sectors %"PRIdaddr"\n", dvname, bad, totbad); 504 505 /* 506 * Clear out the kernel's list of bad sectors, ready for us 507 * to test all those it thought were bad. 508 */ 509 if (ioctl(fd, DIOCBSFLUSH) == -1) 510 err(EXIT_FAILURE, "%s: badsectors flush", dvname); 511 512 printf("%s: bad sectors flushed\n", dvname); 513 514 /* 515 * For each entry we obtained from the kernel, retry each 516 * individual sector recorded as bad by seeking to it and 517 * attempting to read it in. Print out a line item for each 518 * bad block we verify. 519 * 520 * PRIdaddr is used here because the type of dbs_max is daddr_t 521 * and that may be either a 32bit or 64bit number(!) 522 */ 523 SLIST_FOREACH(dbs, &dbstop, dbs_next) { 524 printf("%s: Retrying %"PRIdaddr" - %" 525 PRIdaddr"\n", dvname, dbs->dbs_min, dbs->dbs_max); 526 527 for (blk = dbs->dbs_min; blk <= dbs->dbs_max; blk++) { 528 if (lseek(fd, (off_t)blk * DEV_BSIZE, 529 SEEK_SET) == -1) { 530 warn("%s: lseek block %" PRIdaddr "", 531 dvname, blk); 532 continue; 533 } 534 printf("%s: block %"PRIdaddr" - ", dvname, blk); 535 if (read(fd, block, DEV_BSIZE) != DEV_BSIZE) 536 printf("failed\n"); 537 else 538 printf("ok\n"); 539 fflush(stdout); 540 } 541 } 542 } 543 } 544 545 static void 546 disk_addwedge(int argc, char *argv[]) 547 { 548 struct dkwedge_info dkw; 549 char *cp; 550 daddr_t start; 551 uint64_t size; 552 553 if (argc != 5) 554 usage(); 555 556 /* XXX Unicode: dkw_wname is supposed to be utf-8 */ 557 if (strlcpy((char *)dkw.dkw_wname, argv[1], sizeof(dkw.dkw_wname)) >= 558 sizeof(dkw.dkw_wname)) 559 errx(EXIT_FAILURE, "Wedge name too long; max %zd characters", 560 sizeof(dkw.dkw_wname) - 1); 561 562 if (strlcpy(dkw.dkw_ptype, argv[4], sizeof(dkw.dkw_ptype)) >= 563 sizeof(dkw.dkw_ptype)) 564 errx(EXIT_FAILURE, "Wedge partition type too long; max %zd characters", 565 sizeof(dkw.dkw_ptype) - 1); 566 567 errno = 0; 568 start = strtoll(argv[2], &cp, 0); 569 if (*cp != '\0') 570 errx(EXIT_FAILURE, "Invalid start block: %s", argv[2]); 571 if (errno == ERANGE && (start == LLONG_MAX || 572 start == LLONG_MIN)) 573 errx(EXIT_FAILURE, "Start block out of range."); 574 if (start < 0) 575 errx(EXIT_FAILURE, "Start block must be >= 0."); 576 577 errno = 0; 578 size = strtoull(argv[3], &cp, 0); 579 if (*cp != '\0') 580 errx(EXIT_FAILURE, "Invalid block count: %s", argv[3]); 581 if (errno == ERANGE && (size == ULLONG_MAX)) 582 errx(EXIT_FAILURE, "Block count out of range."); 583 584 dkw.dkw_offset = start; 585 dkw.dkw_size = size; 586 587 if (ioctl(fd, DIOCAWEDGE, &dkw) == -1) 588 err(EXIT_FAILURE, "%s: %s", dvname, argv[0]); 589 else 590 printf("%s created successfully.\n", dkw.dkw_devname); 591 592 } 593 594 static void 595 disk_delwedge(int argc, char *argv[]) 596 { 597 struct dkwedge_info dkw; 598 599 if (argc != 2) 600 usage(); 601 602 if (strlcpy(dkw.dkw_devname, argv[1], sizeof(dkw.dkw_devname)) >= 603 sizeof(dkw.dkw_devname)) 604 errx(EXIT_FAILURE, "Wedge dk name too long; max %zd characters", 605 sizeof(dkw.dkw_devname) - 1); 606 607 if (ioctl(fd, DIOCDWEDGE, &dkw) == -1) 608 err(EXIT_FAILURE, "%s: %s", dvname, argv[0]); 609 } 610 611 static void 612 disk_getwedgeinfo(int argc, char *argv[]) 613 { 614 struct dkwedge_info dkw; 615 616 if (argc != 1) 617 usage(); 618 619 if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == -1) 620 err(EXIT_FAILURE, "%s: getwedgeinfo", dvname); 621 622 printf("%s at %s: %s\n", dkw.dkw_devname, dkw.dkw_parent, 623 dkw.dkw_wname); /* XXX Unicode */ 624 printf("%s: %"PRIu64" blocks at %"PRId64", type: %s\n", 625 dkw.dkw_devname, dkw.dkw_size, dkw.dkw_offset, dkw.dkw_ptype); 626 } 627 628 static void 629 disk_listwedges(int argc, char *argv[]) 630 { 631 struct dkwedge_info *dkw; 632 struct dkwedge_list dkwl; 633 size_t bufsize; 634 u_int i; 635 int c; 636 bool error, quiet; 637 638 optreset = 1; 639 optind = 1; 640 quiet = error = false; 641 while ((c = getopt(argc, argv, "qe")) != -1) 642 switch (c) { 643 case 'e': 644 error = true; 645 break; 646 case 'q': 647 quiet = true; 648 break; 649 default: 650 usage(); 651 } 652 653 argc -= optind; 654 argv += optind; 655 656 if (argc != 0) 657 usage(); 658 659 dkw = NULL; 660 dkwl.dkwl_buf = dkw; 661 dkwl.dkwl_bufsize = 0; 662 663 for (;;) { 664 if (ioctl(fd, DIOCLWEDGES, &dkwl) == -1) 665 err(EXIT_FAILURE, "%s: listwedges", dvname); 666 if (dkwl.dkwl_nwedges == dkwl.dkwl_ncopied) 667 break; 668 bufsize = dkwl.dkwl_nwedges * sizeof(*dkw); 669 if (dkwl.dkwl_bufsize < bufsize) { 670 dkw = realloc(dkwl.dkwl_buf, bufsize); 671 if (dkw == NULL) 672 errx(EXIT_FAILURE, "%s: listwedges: unable to " 673 "allocate wedge info buffer", dvname); 674 dkwl.dkwl_buf = dkw; 675 dkwl.dkwl_bufsize = bufsize; 676 } 677 } 678 679 if (dkwl.dkwl_nwedges == 0) { 680 if (!quiet) 681 printf("%s: no wedges configured\n", dvname); 682 if (error) 683 exit(EXIT_FAILURE); 684 return; 685 } 686 687 if (quiet) 688 return; 689 690 qsort(dkw, dkwl.dkwl_nwedges, sizeof(*dkw), dkw_sort); 691 692 printf("%s: %u wedge%s:\n", dvname, dkwl.dkwl_nwedges, 693 dkwl.dkwl_nwedges == 1 ? "" : "s"); 694 for (i = 0; i < dkwl.dkwl_nwedges; i++) { 695 printf("%s: %s, %"PRIu64" blocks at %"PRId64", type: %s\n", 696 dkw[i].dkw_devname, 697 dkw[i].dkw_wname, /* XXX Unicode */ 698 dkw[i].dkw_size, dkw[i].dkw_offset, dkw[i].dkw_ptype); 699 } 700 } 701 702 static void 703 disk_makewedges(int argc, char *argv[]) 704 { 705 int bits; 706 707 if (argc != 1) 708 usage(); 709 710 if (ioctl(fd, DIOCMWEDGES, &bits) == -1) 711 err(EXIT_FAILURE, "%s: %s", dvname, argv[0]); 712 else 713 printf("successfully scanned %s.\n", dvname); 714 } 715 716 static int 717 dkw_sort(const void *a, const void *b) 718 { 719 const struct dkwedge_info *dkwa = a, *dkwb = b; 720 const daddr_t oa = dkwa->dkw_offset, ob = dkwb->dkw_offset; 721 722 return (oa < ob) ? -1 : (oa > ob) ? 1 : 0; 723 } 724 725 /* 726 * return YES, NO or -1. 727 */ 728 static int 729 yesno(const char *p) 730 { 731 732 if (!strcmp(p, YES_STR)) 733 return YES; 734 if (!strcmp(p, NO_STR)) 735 return NO; 736 return -1; 737 } 738