xref: /netbsd-src/sbin/dkctl/dkctl.c (revision 69fc74887bf4cb3b8c869e64a13619bda7d25978)
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