1*69fc7488Smlelstv /* $NetBSD: dkctl.c,v 1.27 2024/09/14 08:30:44 mlelstv Exp $ */ 2fcc3de9cSthorpej 3fcc3de9cSthorpej /* 4fcc3de9cSthorpej * Copyright 2001 Wasabi Systems, Inc. 5fcc3de9cSthorpej * All rights reserved. 6fcc3de9cSthorpej * 7fcc3de9cSthorpej * Written by Jason R. Thorpe for Wasabi Systems, Inc. 8fcc3de9cSthorpej * 9fcc3de9cSthorpej * Redistribution and use in source and binary forms, with or without 10fcc3de9cSthorpej * modification, are permitted provided that the following conditions 11fcc3de9cSthorpej * are met: 12fcc3de9cSthorpej * 1. Redistributions of source code must retain the above copyright 13fcc3de9cSthorpej * notice, this list of conditions and the following disclaimer. 14fcc3de9cSthorpej * 2. Redistributions in binary form must reproduce the above copyright 15fcc3de9cSthorpej * notice, this list of conditions and the following disclaimer in the 16fcc3de9cSthorpej * documentation and/or other materials provided with the distribution. 17fcc3de9cSthorpej * 3. All advertising materials mentioning features or use of this software 18fcc3de9cSthorpej * must display the following acknowledgement: 19fcc3de9cSthorpej * This product includes software developed for the NetBSD Project by 20fcc3de9cSthorpej * Wasabi Systems, Inc. 21fcc3de9cSthorpej * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22fcc3de9cSthorpej * or promote products derived from this software without specific prior 23fcc3de9cSthorpej * written permission. 24fcc3de9cSthorpej * 25fcc3de9cSthorpej * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26fcc3de9cSthorpej * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27fcc3de9cSthorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28fcc3de9cSthorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29fcc3de9cSthorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30fcc3de9cSthorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31fcc3de9cSthorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32fcc3de9cSthorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33fcc3de9cSthorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34fcc3de9cSthorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35fcc3de9cSthorpej * POSSIBILITY OF SUCH DAMAGE. 36fcc3de9cSthorpej */ 37fcc3de9cSthorpej 38fcc3de9cSthorpej /* 39fcc3de9cSthorpej * dkctl(8) -- a program to manipulate disks. 40fcc3de9cSthorpej */ 41c2a3b5ecSagc #include <sys/cdefs.h> 42c2a3b5ecSagc 43c2a3b5ecSagc #ifndef lint 44*69fc7488Smlelstv __RCSID("$NetBSD: dkctl.c,v 1.27 2024/09/14 08:30:44 mlelstv Exp $"); 45c2a3b5ecSagc #endif 46c2a3b5ecSagc 47fcc3de9cSthorpej #include <sys/param.h> 48fcc3de9cSthorpej #include <sys/ioctl.h> 494ffcc391Schristos #include <sys/stat.h> 50fcc3de9cSthorpej #include <sys/dkio.h> 518dac8843Sdarrenr #include <sys/disk.h> 528dac8843Sdarrenr #include <sys/queue.h> 53fcc3de9cSthorpej #include <err.h> 54fcc3de9cSthorpej #include <errno.h> 55fcc3de9cSthorpej #include <fcntl.h> 56fcc3de9cSthorpej #include <stdio.h> 57fcc3de9cSthorpej #include <stdlib.h> 58aeb66be1Schristos #include <stdbool.h> 59fcc3de9cSthorpej #include <string.h> 60fcc3de9cSthorpej #include <unistd.h> 61fcc3de9cSthorpej #include <util.h> 62fcc3de9cSthorpej 63e9ae5a9bSyamt #define YES 1 64e9ae5a9bSyamt #define NO 0 65e9ae5a9bSyamt 66e9ae5a9bSyamt /* I don't think nl_langinfo is suitable in this case */ 67e9ae5a9bSyamt #define YES_STR "yes" 68e9ae5a9bSyamt #define NO_STR "no" 69e9ae5a9bSyamt #define YESNO_ARG YES_STR " | " NO_STR 70e9ae5a9bSyamt 718dac8843Sdarrenr #ifndef PRIdaddr 728dac8843Sdarrenr #define PRIdaddr PRId64 738dac8843Sdarrenr #endif 748dac8843Sdarrenr 75fcc3de9cSthorpej struct command { 76fcc3de9cSthorpej const char *cmd_name; 77fcc3de9cSthorpej const char *arg_names; 78fcc3de9cSthorpej void (*cmd_func)(int, char *[]); 79fcc3de9cSthorpej int open_flags; 80fcc3de9cSthorpej }; 81fcc3de9cSthorpej 82307d3558Sjoerg static struct command *lookup(const char *); 83307d3558Sjoerg __dead static void usage(void); 84307d3558Sjoerg static void run(int, char *[]); 85307d3558Sjoerg static void showall(void); 86fcc3de9cSthorpej 87307d3558Sjoerg static int fd; /* file descriptor for device */ 88307d3558Sjoerg static const char *dvname; /* device name */ 89307d3558Sjoerg static char dvname_store[MAXPATHLEN]; /* for opendisk(3) */ 90fcc3de9cSthorpej 91307d3558Sjoerg static int dkw_sort(const void *, const void *); 92307d3558Sjoerg static int yesno(const char *); 93e9ae5a9bSyamt 94307d3558Sjoerg static void disk_getcache(int, char *[]); 95307d3558Sjoerg static void disk_setcache(int, char *[]); 96307d3558Sjoerg static void disk_synccache(int, char *[]); 97307d3558Sjoerg static void disk_keeplabel(int, char *[]); 98307d3558Sjoerg static void disk_badsectors(int, char *[]); 99fcc3de9cSthorpej 100307d3558Sjoerg static void disk_addwedge(int, char *[]); 101307d3558Sjoerg static void disk_delwedge(int, char *[]); 102307d3558Sjoerg static void disk_getwedgeinfo(int, char *[]); 103*69fc7488Smlelstv static void disk_getgeometry(int, char *[]); 104307d3558Sjoerg static void disk_listwedges(int, char *[]); 105cfe8bb2aSmlelstv static void disk_makewedges(int, char *[]); 106307d3558Sjoerg static void disk_strategy(int, char *[]); 1070e37eeedSthorpej 108307d3558Sjoerg static struct command commands[] = { 10995e91b49Swiz { "addwedge", 11095e91b49Swiz "name startblk blkcnt ptype", 11195e91b49Swiz disk_addwedge, 112e9ae5a9bSyamt O_RDWR }, 113e9ae5a9bSyamt 1148dac8843Sdarrenr { "badsector", 1158dac8843Sdarrenr "flush | list | retry", 1168dac8843Sdarrenr disk_badsectors, 1178dac8843Sdarrenr O_RDWR }, 1188dac8843Sdarrenr 1190e37eeedSthorpej { "delwedge", 1200e37eeedSthorpej "dk", 1210e37eeedSthorpej disk_delwedge, 1220e37eeedSthorpej O_RDWR }, 1230e37eeedSthorpej 12495e91b49Swiz { "getcache", 12595e91b49Swiz "", 12695e91b49Swiz disk_getcache, 12795e91b49Swiz O_RDONLY }, 12895e91b49Swiz 1290e37eeedSthorpej { "getwedgeinfo", 1300e37eeedSthorpej "", 1310e37eeedSthorpej disk_getwedgeinfo, 1320e37eeedSthorpej O_RDONLY }, 1330e37eeedSthorpej 134*69fc7488Smlelstv { "getgeometry", 135*69fc7488Smlelstv "", 136*69fc7488Smlelstv disk_getgeometry, 137*69fc7488Smlelstv O_RDONLY }, 138*69fc7488Smlelstv 13995e91b49Swiz { "keeplabel", 14095e91b49Swiz YESNO_ARG, 14195e91b49Swiz disk_keeplabel, 14295e91b49Swiz O_RDWR }, 14395e91b49Swiz 1440e37eeedSthorpej { "listwedges", 1450e37eeedSthorpej "", 1460e37eeedSthorpej disk_listwedges, 1470e37eeedSthorpej O_RDONLY }, 1480e37eeedSthorpej 149cfe8bb2aSmlelstv { "makewedges", 150cfe8bb2aSmlelstv "", 151cfe8bb2aSmlelstv disk_makewedges, 152cfe8bb2aSmlelstv O_RDWR }, 153cfe8bb2aSmlelstv 15495e91b49Swiz { "setcache", 15595e91b49Swiz "none | r | w | rw [save]", 15695e91b49Swiz disk_setcache, 15795e91b49Swiz O_RDWR }, 15895e91b49Swiz 1596171bf3aSyamt { "strategy", 1606171bf3aSyamt "[name]", 1616171bf3aSyamt disk_strategy, 1626171bf3aSyamt O_RDWR }, 1636171bf3aSyamt 16495e91b49Swiz { "synccache", 16595e91b49Swiz "[force]", 16695e91b49Swiz disk_synccache, 16795e91b49Swiz O_RDWR }, 16895e91b49Swiz 169fcc3de9cSthorpej { NULL, 170fcc3de9cSthorpej NULL, 171fcc3de9cSthorpej NULL, 172fcc3de9cSthorpej 0 }, 173fcc3de9cSthorpej }; 174fcc3de9cSthorpej 175fcc3de9cSthorpej int 176fcc3de9cSthorpej main(int argc, char *argv[]) 177fcc3de9cSthorpej { 178fcc3de9cSthorpej 179fcc3de9cSthorpej /* Must have at least: device command */ 180b9691b26Suebayasi if (argc < 2) 181fcc3de9cSthorpej usage(); 182fcc3de9cSthorpej 183fcc3de9cSthorpej dvname = argv[1]; 184b9691b26Suebayasi if (argc == 2) 185b9691b26Suebayasi showall(); 186aeb66be1Schristos else 187aeb66be1Schristos run(argc - 2, argv + 2); 188fcc3de9cSthorpej 189aeb66be1Schristos return EXIT_SUCCESS; 190b9691b26Suebayasi } 191fcc3de9cSthorpej 192307d3558Sjoerg static void 193b9691b26Suebayasi run(int argc, char *argv[]) 194b9691b26Suebayasi { 195b9691b26Suebayasi struct command *command; 196b9691b26Suebayasi 197aeb66be1Schristos command = lookup(argv[0]); 198fcc3de9cSthorpej 199fcc3de9cSthorpej /* Open the device. */ 200b9691b26Suebayasi fd = opendisk(dvname, command->open_flags, dvname_store, 201fcc3de9cSthorpej sizeof(dvname_store), 0); 202fcc3de9cSthorpej if (fd == -1) 203aeb66be1Schristos err(EXIT_FAILURE, "%s", dvname); 204fcc3de9cSthorpej dvname = dvname_store; 205fcc3de9cSthorpej 206b9691b26Suebayasi (*command->cmd_func)(argc, argv); 207b9691b26Suebayasi 208b9691b26Suebayasi /* Close the device. */ 209b9691b26Suebayasi (void)close(fd); 210b9691b26Suebayasi } 211b9691b26Suebayasi 212307d3558Sjoerg static struct command * 213b9691b26Suebayasi lookup(const char *name) 214b9691b26Suebayasi { 215b9691b26Suebayasi int i; 216b9691b26Suebayasi 217b9691b26Suebayasi /* Look up the command. */ 218b9691b26Suebayasi for (i = 0; commands[i].cmd_name != NULL; i++) 219b9691b26Suebayasi if (strcmp(name, commands[i].cmd_name) == 0) 220b9691b26Suebayasi break; 221b9691b26Suebayasi if (commands[i].cmd_name == NULL) 222aeb66be1Schristos errx(EXIT_FAILURE, "unknown command: %s", name); 223b9691b26Suebayasi 224b9691b26Suebayasi return &commands[i]; 225fcc3de9cSthorpej } 226fcc3de9cSthorpej 227307d3558Sjoerg static void 2286d580415Sxtraeme usage(void) 229fcc3de9cSthorpej { 230fcc3de9cSthorpej int i; 231fcc3de9cSthorpej 232b9691b26Suebayasi fprintf(stderr, 233aeb66be1Schristos "Usage: %s device\n" 234b9691b26Suebayasi " %s device command [arg [...]]\n", 235b9691b26Suebayasi getprogname(), getprogname()); 236fcc3de9cSthorpej 237fcc3de9cSthorpej fprintf(stderr, " Available commands:\n"); 238fcc3de9cSthorpej for (i = 0; commands[i].cmd_name != NULL; i++) 239fcc3de9cSthorpej fprintf(stderr, "\t%s %s\n", commands[i].cmd_name, 240fcc3de9cSthorpej commands[i].arg_names); 241fcc3de9cSthorpej 242aeb66be1Schristos exit(EXIT_FAILURE); 243fcc3de9cSthorpej } 244fcc3de9cSthorpej 245307d3558Sjoerg static void 246b9691b26Suebayasi showall(void) 247b9691b26Suebayasi { 248aeb66be1Schristos static const char *cmds[] = { "strategy", "getcache", "listwedges" }; 249aeb66be1Schristos size_t i; 250aeb66be1Schristos char *args[2]; 251b9691b26Suebayasi 252aeb66be1Schristos args[1] = NULL; 253aeb66be1Schristos for (i = 0; i < __arraycount(cmds); i++) { 254aeb66be1Schristos printf("%s:\n", cmds[i]); 255aeb66be1Schristos args[0] = __UNCONST(cmds[i]); 256aeb66be1Schristos run(1, args); 257b9691b26Suebayasi putchar('\n'); 258aeb66be1Schristos } 259b9691b26Suebayasi } 260b9691b26Suebayasi 261307d3558Sjoerg static void 2626171bf3aSyamt disk_strategy(int argc, char *argv[]) 2636171bf3aSyamt { 2646171bf3aSyamt struct disk_strategy odks; 2656171bf3aSyamt struct disk_strategy dks; 2666171bf3aSyamt 2676171bf3aSyamt memset(&dks, 0, sizeof(dks)); 2686171bf3aSyamt if (ioctl(fd, DIOCGSTRATEGY, &odks) == -1) { 2696171bf3aSyamt err(EXIT_FAILURE, "%s: DIOCGSTRATEGY", dvname); 2706171bf3aSyamt } 2716171bf3aSyamt 2726171bf3aSyamt memset(&dks, 0, sizeof(dks)); 2736171bf3aSyamt switch (argc) { 274aeb66be1Schristos case 1: 2756171bf3aSyamt /* show the buffer queue strategy used */ 2766171bf3aSyamt printf("%s: %s\n", dvname, odks.dks_name); 2776171bf3aSyamt return; 278aeb66be1Schristos case 2: 2796171bf3aSyamt /* set the buffer queue strategy */ 280aeb66be1Schristos strlcpy(dks.dks_name, argv[1], sizeof(dks.dks_name)); 2816171bf3aSyamt if (ioctl(fd, DIOCSSTRATEGY, &dks) == -1) { 2826171bf3aSyamt err(EXIT_FAILURE, "%s: DIOCSSTRATEGY", dvname); 2836171bf3aSyamt } 284aeb66be1Schristos printf("%s: %s -> %s\n", dvname, odks.dks_name, argv[1]); 2856171bf3aSyamt break; 2866171bf3aSyamt default: 2876171bf3aSyamt usage(); 2886171bf3aSyamt /* NOTREACHED */ 2896171bf3aSyamt } 2906171bf3aSyamt } 2916171bf3aSyamt 292307d3558Sjoerg static void 293fcc3de9cSthorpej disk_getcache(int argc, char *argv[]) 294fcc3de9cSthorpej { 295fcc3de9cSthorpej int bits; 296fcc3de9cSthorpej 297fcc3de9cSthorpej if (ioctl(fd, DIOCGCACHE, &bits) == -1) 298aeb66be1Schristos err(EXIT_FAILURE, "%s: getcache", dvname); 299fcc3de9cSthorpej 300fcc3de9cSthorpej if ((bits & (DKCACHE_READ|DKCACHE_WRITE)) == 0) 301fcc3de9cSthorpej printf("%s: No caches enabled\n", dvname); 302fcc3de9cSthorpej else { 303fcc3de9cSthorpej if (bits & DKCACHE_READ) 304fcc3de9cSthorpej printf("%s: read cache enabled\n", dvname); 305fcc3de9cSthorpej if (bits & DKCACHE_WRITE) 306fcc3de9cSthorpej printf("%s: write-back cache enabled\n", dvname); 307fcc3de9cSthorpej } 308fcc3de9cSthorpej 309fcc3de9cSthorpej printf("%s: read cache enable is %schangeable\n", dvname, 310fcc3de9cSthorpej (bits & DKCACHE_RCHANGE) ? "" : "not "); 311fcc3de9cSthorpej printf("%s: write cache enable is %schangeable\n", dvname, 312fcc3de9cSthorpej (bits & DKCACHE_WCHANGE) ? "" : "not "); 313fcc3de9cSthorpej 314fcc3de9cSthorpej printf("%s: cache parameters are %ssavable\n", dvname, 315fcc3de9cSthorpej (bits & DKCACHE_SAVE) ? "" : "not "); 316db79755dSjdolecek 317db79755dSjdolecek #ifdef DKCACHE_FUA 318db79755dSjdolecek printf("%s: cache Force Unit Access (FUA) %ssupported\n", dvname, 319db79755dSjdolecek (bits & DKCACHE_FUA) ? "" : "not "); 320db79755dSjdolecek #endif /* DKCACHE_FUA */ 321db79755dSjdolecek 322db79755dSjdolecek #ifdef DKCACHE_DPO 323db79755dSjdolecek printf("%s: cache Disable Page Out (DPO) %ssupported\n", dvname, 324db79755dSjdolecek (bits & DKCACHE_DPO) ? "" : "not "); 325db79755dSjdolecek #endif /* DKCACHE_DPO */ 326fcc3de9cSthorpej } 327fcc3de9cSthorpej 328307d3558Sjoerg static void 329fcc3de9cSthorpej disk_setcache(int argc, char *argv[]) 330fcc3de9cSthorpej { 331fcc3de9cSthorpej int bits; 332fcc3de9cSthorpej 333aeb66be1Schristos if (argc > 3 || argc == 1) 334fcc3de9cSthorpej usage(); 335fcc3de9cSthorpej 336aeb66be1Schristos if (strcmp(argv[1], "none") == 0) 337fcc3de9cSthorpej bits = 0; 338aeb66be1Schristos else if (strcmp(argv[1], "r") == 0) 339fcc3de9cSthorpej bits = DKCACHE_READ; 340aeb66be1Schristos else if (strcmp(argv[1], "w") == 0) 341fcc3de9cSthorpej bits = DKCACHE_WRITE; 342aeb66be1Schristos else if (strcmp(argv[1], "rw") == 0) 343fcc3de9cSthorpej bits = DKCACHE_READ|DKCACHE_WRITE; 344fcc3de9cSthorpej else 345fcc3de9cSthorpej usage(); 346fcc3de9cSthorpej 347aeb66be1Schristos if (argc == 3) { 348aeb66be1Schristos if (strcmp(argv[2], "save") == 0) 349fcc3de9cSthorpej bits |= DKCACHE_SAVE; 350fcc3de9cSthorpej else 351fcc3de9cSthorpej usage(); 352fcc3de9cSthorpej } 353fcc3de9cSthorpej 354fcc3de9cSthorpej if (ioctl(fd, DIOCSCACHE, &bits) == -1) 355aeb66be1Schristos err(EXIT_FAILURE, "%s: %s", dvname, argv[0]); 356fcc3de9cSthorpej } 357fcc3de9cSthorpej 358307d3558Sjoerg static void 359fcc3de9cSthorpej disk_synccache(int argc, char *argv[]) 360fcc3de9cSthorpej { 361fcc3de9cSthorpej int force; 362fcc3de9cSthorpej 363fcc3de9cSthorpej switch (argc) { 364aeb66be1Schristos case 1: 365fcc3de9cSthorpej force = 0; 366fcc3de9cSthorpej break; 367fcc3de9cSthorpej 368aeb66be1Schristos case 2: 369aeb66be1Schristos if (strcmp(argv[1], "force") == 0) 370fcc3de9cSthorpej force = 1; 371fcc3de9cSthorpej else 372fcc3de9cSthorpej usage(); 373fcc3de9cSthorpej break; 374fcc3de9cSthorpej 375fcc3de9cSthorpej default: 376fcc3de9cSthorpej usage(); 377fcc3de9cSthorpej } 378fcc3de9cSthorpej 379fcc3de9cSthorpej if (ioctl(fd, DIOCCACHESYNC, &force) == -1) 380aeb66be1Schristos err(EXIT_FAILURE, "%s: %s", dvname, argv[0]); 381fcc3de9cSthorpej } 382e9ae5a9bSyamt 383307d3558Sjoerg static void 384e9ae5a9bSyamt disk_keeplabel(int argc, char *argv[]) 385e9ae5a9bSyamt { 386e9ae5a9bSyamt int keep; 387e9ae5a9bSyamt int yn; 388e9ae5a9bSyamt 389aeb66be1Schristos if (argc != 2) 390e9ae5a9bSyamt usage(); 391e9ae5a9bSyamt 392aeb66be1Schristos yn = yesno(argv[1]); 393e9ae5a9bSyamt if (yn < 0) 394e9ae5a9bSyamt usage(); 395e9ae5a9bSyamt 396e9ae5a9bSyamt keep = yn == YES; 397e9ae5a9bSyamt 398e9ae5a9bSyamt if (ioctl(fd, DIOCKLABEL, &keep) == -1) 399aeb66be1Schristos err(EXIT_FAILURE, "%s: %s", dvname, argv[0]); 400e9ae5a9bSyamt } 401e9ae5a9bSyamt 4028dac8843Sdarrenr 403307d3558Sjoerg static void 4048dac8843Sdarrenr disk_badsectors(int argc, char *argv[]) 4058dac8843Sdarrenr { 4068dac8843Sdarrenr struct disk_badsectors *dbs, *dbs2, buffer[200]; 4078dac8843Sdarrenr SLIST_HEAD(, disk_badsectors) dbstop; 4088dac8843Sdarrenr struct disk_badsecinfo dbsi; 4098dac8843Sdarrenr daddr_t blk, totbad, bad; 4101608697bSdsl u_int32_t count; 4118dac8843Sdarrenr struct stat sb; 4128dac8843Sdarrenr u_char *block; 413eb1ff84cSmartin time_t tm; 4148dac8843Sdarrenr 415aeb66be1Schristos if (argc != 2) 4168dac8843Sdarrenr usage(); 4178dac8843Sdarrenr 418aeb66be1Schristos if (strcmp(argv[1], "list") == 0) { 4198dac8843Sdarrenr /* 4208dac8843Sdarrenr * Copy the list of kernel bad sectors out in chunks that fit 4218dac8843Sdarrenr * into buffer[]. Updating dbsi_skip means we don't sit here 4228dac8843Sdarrenr * forever only getting the first chunk that fit in buffer[]. 4238dac8843Sdarrenr */ 4248dac8843Sdarrenr dbsi.dbsi_buffer = (caddr_t)buffer; 4258dac8843Sdarrenr dbsi.dbsi_bufsize = sizeof(buffer); 4268dac8843Sdarrenr dbsi.dbsi_skip = 0; 4278dac8843Sdarrenr dbsi.dbsi_copied = 0; 4288dac8843Sdarrenr dbsi.dbsi_left = 0; 4298dac8843Sdarrenr 4308dac8843Sdarrenr do { 4318dac8843Sdarrenr if (ioctl(fd, DIOCBSLIST, (caddr_t)&dbsi) == -1) 432aeb66be1Schristos err(EXIT_FAILURE, "%s: badsectors list", dvname); 4338dac8843Sdarrenr 4348dac8843Sdarrenr dbs = (struct disk_badsectors *)dbsi.dbsi_buffer; 4358dac8843Sdarrenr for (count = dbsi.dbsi_copied; count > 0; count--) { 436eb1ff84cSmartin tm = dbs->dbs_failedat.tv_sec; 4371608697bSdsl printf("%s: blocks %" PRIdaddr " - %" PRIdaddr " failed at %s", 4388dac8843Sdarrenr dvname, dbs->dbs_min, dbs->dbs_max, 439eb1ff84cSmartin ctime(&tm)); 4408745a98aSdarrenr dbs++; 4418dac8843Sdarrenr } 4428dac8843Sdarrenr dbsi.dbsi_skip += dbsi.dbsi_copied; 4438dac8843Sdarrenr } while (dbsi.dbsi_left != 0); 4448dac8843Sdarrenr 445aeb66be1Schristos } else if (strcmp(argv[1], "flush") == 0) { 4468dac8843Sdarrenr if (ioctl(fd, DIOCBSFLUSH) == -1) 447aeb66be1Schristos err(EXIT_FAILURE, "%s: badsectors flush", dvname); 4488dac8843Sdarrenr 449aeb66be1Schristos } else if (strcmp(argv[1], "retry") == 0) { 4508dac8843Sdarrenr /* 4518dac8843Sdarrenr * Enforce use of raw device here because the block device 4528dac8843Sdarrenr * causes access to blocks to be clustered in a larger group, 4538dac8843Sdarrenr * making it impossible to determine which individual sectors 4548dac8843Sdarrenr * are the cause of a problem. 4558dac8843Sdarrenr */ 4568dac8843Sdarrenr if (fstat(fd, &sb) == -1) 457aeb66be1Schristos err(EXIT_FAILURE, "fstat"); 4588dac8843Sdarrenr 4598dac8843Sdarrenr if (!S_ISCHR(sb.st_mode)) { 4608dac8843Sdarrenr fprintf(stderr, "'badsector retry' must be used %s\n", 4618dac8843Sdarrenr "with character device"); 4628dac8843Sdarrenr exit(1); 4638dac8843Sdarrenr } 4648dac8843Sdarrenr 4658dac8843Sdarrenr SLIST_INIT(&dbstop); 4668dac8843Sdarrenr 4678dac8843Sdarrenr /* 4688dac8843Sdarrenr * Build up a copy of the in-kernel list in a number of stages. 4698dac8843Sdarrenr * That the list we build up here is in the reverse order to 4708dac8843Sdarrenr * the kernel's is of no concern. 4718dac8843Sdarrenr */ 4728dac8843Sdarrenr dbsi.dbsi_buffer = (caddr_t)buffer; 4738dac8843Sdarrenr dbsi.dbsi_bufsize = sizeof(buffer); 4748dac8843Sdarrenr dbsi.dbsi_skip = 0; 4758dac8843Sdarrenr dbsi.dbsi_copied = 0; 4768dac8843Sdarrenr dbsi.dbsi_left = 0; 4778dac8843Sdarrenr 4788dac8843Sdarrenr do { 4798dac8843Sdarrenr if (ioctl(fd, DIOCBSLIST, (caddr_t)&dbsi) == -1) 480aeb66be1Schristos err(EXIT_FAILURE, "%s: badsectors list", dvname); 4818dac8843Sdarrenr 4828dac8843Sdarrenr dbs = (struct disk_badsectors *)dbsi.dbsi_buffer; 4838dac8843Sdarrenr for (count = dbsi.dbsi_copied; count > 0; count--) { 48487f04837Sdsl dbs2 = malloc(sizeof *dbs2); 48587f04837Sdsl if (dbs2 == NULL) 486aeb66be1Schristos err(EXIT_FAILURE, NULL); 4878dac8843Sdarrenr *dbs2 = *dbs; 4888dac8843Sdarrenr SLIST_INSERT_HEAD(&dbstop, dbs2, dbs_next); 4898745a98aSdarrenr dbs++; 4908dac8843Sdarrenr } 4918dac8843Sdarrenr dbsi.dbsi_skip += dbsi.dbsi_copied; 4928dac8843Sdarrenr } while (dbsi.dbsi_left != 0); 4938dac8843Sdarrenr 4948dac8843Sdarrenr /* 4958dac8843Sdarrenr * Just calculate and print out something that will hopefully 4968dac8843Sdarrenr * provide some useful information about what's going to take 4978dac8843Sdarrenr * place next (if anything.) 4988dac8843Sdarrenr */ 4998dac8843Sdarrenr bad = 0; 5008dac8843Sdarrenr totbad = 0; 501e948e1b1Srumble if ((block = calloc(1, DEV_BSIZE)) == NULL) 502aeb66be1Schristos err(EXIT_FAILURE, NULL); 5038dac8843Sdarrenr SLIST_FOREACH(dbs, &dbstop, dbs_next) { 5048dac8843Sdarrenr bad++; 5058dac8843Sdarrenr totbad += dbs->dbs_max - dbs->dbs_min + 1; 5068dac8843Sdarrenr } 5078dac8843Sdarrenr 5088dac8843Sdarrenr printf("%s: bad sector clusters %"PRIdaddr 5098dac8843Sdarrenr " total sectors %"PRIdaddr"\n", dvname, bad, totbad); 5108dac8843Sdarrenr 5118dac8843Sdarrenr /* 5128dac8843Sdarrenr * Clear out the kernel's list of bad sectors, ready for us 5138dac8843Sdarrenr * to test all those it thought were bad. 5148dac8843Sdarrenr */ 5158dac8843Sdarrenr if (ioctl(fd, DIOCBSFLUSH) == -1) 516aeb66be1Schristos err(EXIT_FAILURE, "%s: badsectors flush", dvname); 5178dac8843Sdarrenr 5188dac8843Sdarrenr printf("%s: bad sectors flushed\n", dvname); 5198dac8843Sdarrenr 5208dac8843Sdarrenr /* 5218dac8843Sdarrenr * For each entry we obtained from the kernel, retry each 5228dac8843Sdarrenr * individual sector recorded as bad by seeking to it and 5238dac8843Sdarrenr * attempting to read it in. Print out a line item for each 5248dac8843Sdarrenr * bad block we verify. 5258dac8843Sdarrenr * 5268dac8843Sdarrenr * PRIdaddr is used here because the type of dbs_max is daddr_t 5278dac8843Sdarrenr * and that may be either a 32bit or 64bit number(!) 5288dac8843Sdarrenr */ 5298dac8843Sdarrenr SLIST_FOREACH(dbs, &dbstop, dbs_next) { 5308dac8843Sdarrenr printf("%s: Retrying %"PRIdaddr" - %" 5318dac8843Sdarrenr PRIdaddr"\n", dvname, dbs->dbs_min, dbs->dbs_max); 5328dac8843Sdarrenr 5338dac8843Sdarrenr for (blk = dbs->dbs_min; blk <= dbs->dbs_max; blk++) { 5348dac8843Sdarrenr if (lseek(fd, (off_t)blk * DEV_BSIZE, 5358dac8843Sdarrenr SEEK_SET) == -1) { 5361608697bSdsl warn("%s: lseek block %" PRIdaddr "", 5371608697bSdsl dvname, blk); 5388dac8843Sdarrenr continue; 5398dac8843Sdarrenr } 5408dac8843Sdarrenr printf("%s: block %"PRIdaddr" - ", dvname, blk); 5418dac8843Sdarrenr if (read(fd, block, DEV_BSIZE) != DEV_BSIZE) 5428dac8843Sdarrenr printf("failed\n"); 5438dac8843Sdarrenr else 5448dac8843Sdarrenr printf("ok\n"); 5458dac8843Sdarrenr fflush(stdout); 5468dac8843Sdarrenr } 5478dac8843Sdarrenr } 5488dac8843Sdarrenr } 5498dac8843Sdarrenr } 5508dac8843Sdarrenr 551307d3558Sjoerg static void 5520e37eeedSthorpej disk_addwedge(int argc, char *argv[]) 5530e37eeedSthorpej { 5540e37eeedSthorpej struct dkwedge_info dkw; 5550e37eeedSthorpej char *cp; 5560e37eeedSthorpej daddr_t start; 5570e37eeedSthorpej uint64_t size; 5580e37eeedSthorpej 559aeb66be1Schristos if (argc != 5) 5600e37eeedSthorpej usage(); 5610e37eeedSthorpej 562f8a6ea09Sdholland /* XXX Unicode: dkw_wname is supposed to be utf-8 */ 563aeb66be1Schristos if (strlcpy((char *)dkw.dkw_wname, argv[1], sizeof(dkw.dkw_wname)) >= 564ea14c287Schristos sizeof(dkw.dkw_wname)) 565aeb66be1Schristos errx(EXIT_FAILURE, "Wedge name too long; max %zd characters", 5660e37eeedSthorpej sizeof(dkw.dkw_wname) - 1); 5670e37eeedSthorpej 568aeb66be1Schristos if (strlcpy(dkw.dkw_ptype, argv[4], sizeof(dkw.dkw_ptype)) >= 56970e875f6Selad sizeof(dkw.dkw_ptype)) 570aeb66be1Schristos errx(EXIT_FAILURE, "Wedge partition type too long; max %zd characters", 5710e37eeedSthorpej sizeof(dkw.dkw_ptype) - 1); 5720e37eeedSthorpej 5730e37eeedSthorpej errno = 0; 574aeb66be1Schristos start = strtoll(argv[2], &cp, 0); 5750e37eeedSthorpej if (*cp != '\0') 576aeb66be1Schristos errx(EXIT_FAILURE, "Invalid start block: %s", argv[2]); 5770e37eeedSthorpej if (errno == ERANGE && (start == LLONG_MAX || 5780e37eeedSthorpej start == LLONG_MIN)) 579aeb66be1Schristos errx(EXIT_FAILURE, "Start block out of range."); 5800e37eeedSthorpej if (start < 0) 581aeb66be1Schristos errx(EXIT_FAILURE, "Start block must be >= 0."); 5820e37eeedSthorpej 5830e37eeedSthorpej errno = 0; 584aeb66be1Schristos size = strtoull(argv[3], &cp, 0); 5850e37eeedSthorpej if (*cp != '\0') 586aeb66be1Schristos errx(EXIT_FAILURE, "Invalid block count: %s", argv[3]); 5870e37eeedSthorpej if (errno == ERANGE && (size == ULLONG_MAX)) 588aeb66be1Schristos errx(EXIT_FAILURE, "Block count out of range."); 5890e37eeedSthorpej 5900e37eeedSthorpej dkw.dkw_offset = start; 5910e37eeedSthorpej dkw.dkw_size = size; 5920e37eeedSthorpej 5930e37eeedSthorpej if (ioctl(fd, DIOCAWEDGE, &dkw) == -1) 594aeb66be1Schristos err(EXIT_FAILURE, "%s: %s", dvname, argv[0]); 59518f2bbe9Sspz else 59618f2bbe9Sspz printf("%s created successfully.\n", dkw.dkw_devname); 59718f2bbe9Sspz 5980e37eeedSthorpej } 5990e37eeedSthorpej 600307d3558Sjoerg static void 6010e37eeedSthorpej disk_delwedge(int argc, char *argv[]) 6020e37eeedSthorpej { 6030e37eeedSthorpej struct dkwedge_info dkw; 6040e37eeedSthorpej 605aeb66be1Schristos if (argc != 2) 6060e37eeedSthorpej usage(); 6070e37eeedSthorpej 608aeb66be1Schristos if (strlcpy(dkw.dkw_devname, argv[1], sizeof(dkw.dkw_devname)) >= 60970e875f6Selad sizeof(dkw.dkw_devname)) 610aeb66be1Schristos errx(EXIT_FAILURE, "Wedge dk name too long; max %zd characters", 6110e37eeedSthorpej sizeof(dkw.dkw_devname) - 1); 6120e37eeedSthorpej 6130e37eeedSthorpej if (ioctl(fd, DIOCDWEDGE, &dkw) == -1) 614aeb66be1Schristos err(EXIT_FAILURE, "%s: %s", dvname, argv[0]); 6150e37eeedSthorpej } 6160e37eeedSthorpej 617307d3558Sjoerg static void 6180e37eeedSthorpej disk_getwedgeinfo(int argc, char *argv[]) 6190e37eeedSthorpej { 6200e37eeedSthorpej struct dkwedge_info dkw; 6210e37eeedSthorpej 622aeb66be1Schristos if (argc != 1) 6230e37eeedSthorpej usage(); 6240e37eeedSthorpej 6250e37eeedSthorpej if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == -1) 626aeb66be1Schristos err(EXIT_FAILURE, "%s: getwedgeinfo", dvname); 6270e37eeedSthorpej 6280e37eeedSthorpej printf("%s at %s: %s\n", dkw.dkw_devname, dkw.dkw_parent, 6290e37eeedSthorpej dkw.dkw_wname); /* XXX Unicode */ 630bafafd83Smartin printf("%s: %"PRIu64" blocks at %"PRId64", type: %s\n", 6310e37eeedSthorpej dkw.dkw_devname, dkw.dkw_size, dkw.dkw_offset, dkw.dkw_ptype); 6320e37eeedSthorpej } 6330e37eeedSthorpej 634307d3558Sjoerg static void 635*69fc7488Smlelstv disk_getgeometry(int argc, char *argv[]) 636*69fc7488Smlelstv { 637*69fc7488Smlelstv off_t bytes; 638*69fc7488Smlelstv u_int secsize; 639*69fc7488Smlelstv 640*69fc7488Smlelstv if (argc != 1) 641*69fc7488Smlelstv usage(); 642*69fc7488Smlelstv 643*69fc7488Smlelstv if (ioctl(fd, DIOCGMEDIASIZE, &bytes) == -1) 644*69fc7488Smlelstv err(EXIT_FAILURE, "%s: getmediasize", dvname); 645*69fc7488Smlelstv 646*69fc7488Smlelstv secsize = DEV_BSIZE; 647*69fc7488Smlelstv if (ioctl(fd, DIOCGSECTORSIZE, &secsize) == -1) 648*69fc7488Smlelstv warn("%s: getsectorsize", dvname); 649*69fc7488Smlelstv 650*69fc7488Smlelstv printf("%s: %"PRIu64" bytes in %"PRIu64" blocks of %u bytes\n", 651*69fc7488Smlelstv dvname, bytes, bytes/secsize, secsize); 652*69fc7488Smlelstv } 653*69fc7488Smlelstv 654*69fc7488Smlelstv static void 6550e37eeedSthorpej disk_listwedges(int argc, char *argv[]) 6560e37eeedSthorpej { 6570e37eeedSthorpej struct dkwedge_info *dkw; 6580e37eeedSthorpej struct dkwedge_list dkwl; 6590e37eeedSthorpej size_t bufsize; 6600e37eeedSthorpej u_int i; 661aeb66be1Schristos int c; 662aeb66be1Schristos bool error, quiet; 663aeb66be1Schristos 664aeb66be1Schristos optreset = 1; 665aeb66be1Schristos optind = 1; 666aeb66be1Schristos quiet = error = false; 667aeb66be1Schristos while ((c = getopt(argc, argv, "qe")) != -1) 668aeb66be1Schristos switch (c) { 669aeb66be1Schristos case 'e': 670aeb66be1Schristos error = true; 671aeb66be1Schristos break; 672aeb66be1Schristos case 'q': 673aeb66be1Schristos quiet = true; 674aeb66be1Schristos break; 675aeb66be1Schristos default: 676aeb66be1Schristos usage(); 677aeb66be1Schristos } 678aeb66be1Schristos 679aeb66be1Schristos argc -= optind; 680aeb66be1Schristos argv += optind; 6810e37eeedSthorpej 6820e37eeedSthorpej if (argc != 0) 6830e37eeedSthorpej usage(); 6840e37eeedSthorpej 6850e37eeedSthorpej dkw = NULL; 6860e37eeedSthorpej dkwl.dkwl_buf = dkw; 6870e37eeedSthorpej dkwl.dkwl_bufsize = 0; 6880e37eeedSthorpej 6890e37eeedSthorpej for (;;) { 6900e37eeedSthorpej if (ioctl(fd, DIOCLWEDGES, &dkwl) == -1) 691aeb66be1Schristos err(EXIT_FAILURE, "%s: listwedges", dvname); 6920e37eeedSthorpej if (dkwl.dkwl_nwedges == dkwl.dkwl_ncopied) 6930e37eeedSthorpej break; 6940e37eeedSthorpej bufsize = dkwl.dkwl_nwedges * sizeof(*dkw); 6950e37eeedSthorpej if (dkwl.dkwl_bufsize < bufsize) { 6960e37eeedSthorpej dkw = realloc(dkwl.dkwl_buf, bufsize); 6970e37eeedSthorpej if (dkw == NULL) 698aeb66be1Schristos errx(EXIT_FAILURE, "%s: listwedges: unable to " 6990e37eeedSthorpej "allocate wedge info buffer", dvname); 7000e37eeedSthorpej dkwl.dkwl_buf = dkw; 7010e37eeedSthorpej dkwl.dkwl_bufsize = bufsize; 7020e37eeedSthorpej } 7030e37eeedSthorpej } 7040e37eeedSthorpej 7050e37eeedSthorpej if (dkwl.dkwl_nwedges == 0) { 706aeb66be1Schristos if (!quiet) 7070e37eeedSthorpej printf("%s: no wedges configured\n", dvname); 708aeb66be1Schristos if (error) 709aeb66be1Schristos exit(EXIT_FAILURE); 7100e37eeedSthorpej return; 7110e37eeedSthorpej } 7120e37eeedSthorpej 713b36637eeSkre if (quiet) 714b36637eeSkre return; 715b36637eeSkre 716b9691b26Suebayasi qsort(dkw, dkwl.dkwl_nwedges, sizeof(*dkw), dkw_sort); 717b9691b26Suebayasi 7180e37eeedSthorpej printf("%s: %u wedge%s:\n", dvname, dkwl.dkwl_nwedges, 7190e37eeedSthorpej dkwl.dkwl_nwedges == 1 ? "" : "s"); 7200e37eeedSthorpej for (i = 0; i < dkwl.dkwl_nwedges; i++) { 721bafafd83Smartin printf("%s: %s, %"PRIu64" blocks at %"PRId64", type: %s\n", 7220e37eeedSthorpej dkw[i].dkw_devname, 7230e37eeedSthorpej dkw[i].dkw_wname, /* XXX Unicode */ 7240e37eeedSthorpej dkw[i].dkw_size, dkw[i].dkw_offset, dkw[i].dkw_ptype); 7250e37eeedSthorpej } 7260e37eeedSthorpej } 7270e37eeedSthorpej 728cfe8bb2aSmlelstv static void 729cfe8bb2aSmlelstv disk_makewedges(int argc, char *argv[]) 730cfe8bb2aSmlelstv { 731cfe8bb2aSmlelstv int bits; 732cfe8bb2aSmlelstv 733aeb66be1Schristos if (argc != 1) 734cfe8bb2aSmlelstv usage(); 735cfe8bb2aSmlelstv 736cfe8bb2aSmlelstv if (ioctl(fd, DIOCMWEDGES, &bits) == -1) 737aeb66be1Schristos err(EXIT_FAILURE, "%s: %s", dvname, argv[0]); 738cfe8bb2aSmlelstv else 739cfe8bb2aSmlelstv printf("successfully scanned %s.\n", dvname); 740cfe8bb2aSmlelstv } 741cfe8bb2aSmlelstv 742307d3558Sjoerg static int 743b9691b26Suebayasi dkw_sort(const void *a, const void *b) 744b9691b26Suebayasi { 745b9691b26Suebayasi const struct dkwedge_info *dkwa = a, *dkwb = b; 746b9691b26Suebayasi const daddr_t oa = dkwa->dkw_offset, ob = dkwb->dkw_offset; 747b9691b26Suebayasi 748b9691b26Suebayasi return (oa < ob) ? -1 : (oa > ob) ? 1 : 0; 749b9691b26Suebayasi } 750b9691b26Suebayasi 751e9ae5a9bSyamt /* 752e9ae5a9bSyamt * return YES, NO or -1. 753e9ae5a9bSyamt */ 754307d3558Sjoerg static int 755e9ae5a9bSyamt yesno(const char *p) 756e9ae5a9bSyamt { 757e9ae5a9bSyamt 758e9ae5a9bSyamt if (!strcmp(p, YES_STR)) 759e9ae5a9bSyamt return YES; 760e9ae5a9bSyamt if (!strcmp(p, NO_STR)) 761e9ae5a9bSyamt return NO; 762e9ae5a9bSyamt return -1; 763e9ae5a9bSyamt } 764