xref: /netbsd/usr.sbin/diskpart/diskpart.c (revision bf9ec67e)
1 /*
2  * Copyright (c) 1983, 1988, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include <sys/cdefs.h>
35 #ifndef lint
36 __COPYRIGHT("@(#) Copyright (c) 1983, 1988, 1993\n\
37 	The Regents of the University of California.  All rights reserved.\n");
38 #endif /* not lint */
39 
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "from: @(#)diskpart.c	8.3 (Berkeley) 11/30/94";
43 #else
44 __RCSID("$NetBSD: diskpart.c,v 1.12 2002/05/27 16:39:29 drochner Exp $");
45 #endif
46 #endif /* not lint */
47 
48 /*
49  * Program to calculate standard disk partition sizes.
50  */
51 #include <sys/param.h>
52 #define DKTYPENAMES
53 #include <sys/disklabel.h>
54 
55 #include <ctype.h>
56 #include <disktab.h>
57 #include <limits.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 
63 #define	for_now			/* show all of `c' partition for disklabel */
64 #define	NPARTITIONS	8
65 #define	PART(x)		(x - 'a')
66 
67 /*
68  * Default partition sizes, where they exist.
69  */
70 #define	NDEFAULTS	4
71 int	defpart[NDEFAULTS][NPARTITIONS] = {
72    { 15884, 66880, 0, 15884, 307200, 0, 0, 291346 },	/* ~ 356+ Mbytes */
73    { 15884, 33440, 0, 15884, 55936, 0, 0, 291346 },	/* ~ 206-355 Mbytes */
74    { 15884, 33440, 0, 15884, 55936, 0, 0, 0 },		/* ~ 61-205 Mbytes */
75    { 15884, 10032, 0, 15884, 0, 0, 0, 0 },		/* ~ 20-60 Mbytes */
76 };
77 
78 /*
79  * Each array defines a layout for a disk;
80  * that is, the collection of partitions totally
81  * covers the physical space on a disk.
82  */
83 #define	NLAYOUTS	3
84 char	layouts[NLAYOUTS][NPARTITIONS] = {
85    { 'a', 'b', 'h', 'g' },
86    { 'a', 'b', 'h', 'd', 'e', 'f' },
87    { 'c' },
88 };
89 
90 /*
91  * Default disk block and disk block fragment
92  * sizes for each file system.  Those file systems
93  * with zero block and frag sizes are special cases
94  * (e.g. swap areas or for access to the entire device).
95  */
96 struct	partition defparam[NPARTITIONS] = {
97 	{ 0, 0, { 1024 }, FS_UNUSED, 8, { 0 }, },		/* a */
98 	{ 0, 0, { 1024 }, FS_SWAP,   8, { 0 }, },		/* b */
99 	{ 0, 0, { 1024 }, FS_UNUSED, 8, { 0 }, },		/* c */
100 	{ 0, 0, {  512 }, FS_UNUSED, 8, { 0 }, },		/* d */
101 	{ 0, 0, { 1024 }, FS_UNUSED, 8, { 0 }, },		/* e */
102 	{ 0, 0, { 1024 }, FS_UNUSED, 8, { 0 }, },		/* f */
103 	{ 0, 0, { 1024 }, FS_UNUSED, 8, { 0 }, },		/* g */
104 	{ 0, 0, { 1024 }, FS_UNUSED, 8, { 0 }, }		/* h */
105 };
106 
107 /*
108  * Each disk has some space reserved for a bad sector
109  * forwarding table.  DEC standard 144 uses the first
110  * 5 even numbered sectors in the last track of the
111  * last cylinder for replicated storage of the bad sector
112  * table; another 126 sectors past this is needed as a
113  * pool of replacement sectors.
114  */
115 int	badsecttable = 126;	/* # sectors */
116 
117 int	pflag;			/* print device driver partition tables */
118 int	dflag;			/* print disktab entry */
119 
120 int	gettype __P((const char *, const char *const *));
121 int	main __P((int, char **));
122 struct disklabel *promptfordisk __P((const char *));
123 void	usage __P((void));
124 
125 int
126 main(argc, argv)
127 	int argc;
128 	char *argv[];
129 {
130 
131 	struct disklabel *dp;
132 	int curcyl, spc, def, part, layout, j, ch;
133 	int threshhold, numcyls[NPARTITIONS], startcyl[NPARTITIONS];
134 	off_t totsize = 0;
135 	char *lp, *tyname;
136 
137 	while ((ch = getopt(argc, argv, "pds:")) != -1) {
138 		switch (ch) {
139 		case 'd':
140 			dflag++;
141 			break;
142 
143 		case 'p':
144 			pflag++;
145 			break;
146 
147 		case 's':
148 			totsize = strtoul(optarg, &lp, 10);
149 			if (*lp != '\0')
150 				usage();
151 			break;
152 
153 		case '?':
154 		default:
155 			usage();
156 			/* NOTREACHED */
157 		}
158 	}
159 	argc -= optind;
160 	argv += optind;
161 
162 	if (argc != 1) {
163 		usage();
164 		/* NOTREACHED */
165 	}
166 
167 	dp = getdiskbyname(*argv);
168 	if (dp == NULL) {
169 		if (isatty(0))
170 			dp = promptfordisk(*argv);
171 		if (dp == NULL) {
172 			fprintf(stderr, "%s: unknown disk type\n", *argv);
173 			exit(1);
174 		}
175 	}
176 	if (dp->d_flags & D_REMOVABLE)
177 		tyname = "removable";
178 	else if (dp->d_flags & D_RAMDISK)
179 		tyname = "simulated";
180 	else
181 		tyname = "winchester";
182 	spc = dp->d_secpercyl;
183 	/*
184 	 * Bad sector table contains one track for the replicated
185 	 * copies of the table and enough full tracks preceding
186 	 * the last track to hold the pool of free blocks to which
187 	 * bad sectors are mapped.
188 	 * If disk size was specified explicitly, use specified size.
189 	 */
190 	if (dp->d_type == DTYPE_SMD && dp->d_flags & D_BADSECT &&
191 	    totsize == 0) {
192 		badsecttable = dp->d_nsectors +
193 		    roundup(badsecttable, dp->d_nsectors);
194 		threshhold = howmany(spc, badsecttable);
195 	} else {
196 		badsecttable = 0;
197 		threshhold = 0;
198 	}
199 	/*
200 	 * If disk size was specified, recompute number of cylinders
201 	 * that may be used, and set badsecttable to any remaining
202 	 * fraction of the last cylinder.
203 	 */
204 	if (totsize != 0) {
205 		dp->d_ncylinders = howmany(totsize, spc);
206 		badsecttable = spc * dp->d_ncylinders - totsize;
207 	}
208 
209 	/*
210 	 * Figure out if disk is large enough for
211 	 * expanded swap area and 'd', 'e', and 'f'
212 	 * partitions.  Otherwise, use smaller defaults
213 	 * based on RK07.
214 	 */
215 	for (def = 0; def < NDEFAULTS; def++) {
216 		curcyl = 0;
217 		for (part = PART('a'); part < NPARTITIONS; part++)
218 			curcyl += howmany(defpart[def][part], spc);
219 		if (curcyl < dp->d_ncylinders - threshhold)
220 			break;
221 	}
222 	if (def >= NDEFAULTS) {
223 		fprintf(stderr, "%s: disk too small, calculate by hand\n",
224 			*argv);
225 		exit(1);
226 	}
227 
228 	/*
229 	 * Calculate number of cylinders allocated to each disk
230 	 * partition.  We may waste a bit of space here, but it's
231 	 * in the interest of (very backward) compatibility
232 	 * (for mixed disk systems).
233 	 */
234 	for (curcyl = 0, part = PART('a'); part < NPARTITIONS; part++) {
235 		numcyls[part] = 0;
236 		if (defpart[def][part] != 0) {
237 			numcyls[part] = howmany(defpart[def][part], spc);
238 			curcyl += numcyls[part];
239 		}
240 	}
241 	numcyls[PART('f')] = dp->d_ncylinders - curcyl;
242 	numcyls[PART('g')] =
243 		numcyls[PART('d')] + numcyls[PART('e')] + numcyls[PART('f')];
244 	numcyls[PART('c')] = dp->d_ncylinders;
245 	defpart[def][PART('f')] = numcyls[PART('f')] * spc - badsecttable;
246 	defpart[def][PART('g')] = numcyls[PART('g')] * spc - badsecttable;
247 	defpart[def][PART('c')] = numcyls[PART('c')] * spc;
248 #ifndef for_now
249 	if (totsize || !pflag)
250 #else
251 	if (totsize)
252 #endif
253 		defpart[def][PART('c')] -= badsecttable;
254 
255 	/*
256 	 * Calculate starting cylinder number for each partition.
257 	 * Note the 'h' partition is physically located before the
258 	 * 'g' or 'd' partition.  This is reflected in the layout
259 	 * arrays defined above.
260 	 */
261 	for (layout = 0; layout < NLAYOUTS; layout++) {
262 		curcyl = 0;
263 		for (lp = layouts[layout]; *lp != 0; lp++) {
264 			startcyl[PART(*lp)] = curcyl;
265 			curcyl += numcyls[PART(*lp)];
266 		}
267 	}
268 
269 	if (pflag) {
270 		printf("}, %s_sizes[%d] = {\n", dp->d_typename, NPARTITIONS);
271 		for (part = PART('a'); part < NPARTITIONS; part++) {
272 			if (numcyls[part] == 0) {
273 				printf("\t0,\t0,\n");
274 				continue;
275 			}
276 			if (dp->d_type != DTYPE_MSCP) {
277 			       printf("\t%d,\t%d,\t\t/* %c=cyl %d thru %d */\n",
278 					defpart[def][part], startcyl[part],
279 					'A' + part, startcyl[part],
280 					startcyl[part] + numcyls[part] - 1);
281 				continue;
282 			}
283 			printf("\t%d,\t%d,\t\t/* %c=sectors %d thru %d */\n",
284 				defpart[def][part], spc * startcyl[part],
285 				'A' + part, spc * startcyl[part],
286 				spc * startcyl[part] + defpart[def][part] - 1);
287 		}
288 		exit(0);
289 	}
290 	if (dflag) {
291 		int nparts;
292 
293 		/*
294 		 * In case the disk is in the ``in-between'' range
295 		 * where the 'g' partition is smaller than the 'h'
296 		 * partition, reverse the frag sizes so the /usr partition
297 		 * is always set up with a frag size larger than the
298 		 * user's partition.
299 		 */
300 		if (defpart[def][PART('g')] < defpart[def][PART('h')]) {
301 			int temp;
302 
303 			temp = defparam[PART('h')].p_fsize;
304 			defparam[PART('h')].p_fsize =
305 				defparam[PART('g')].p_fsize;
306 			defparam[PART('g')].p_fsize = temp;
307 		}
308 		printf("%s:\\\n", dp->d_typename);
309 		printf("\t:ty=%s:ns#%d:nt#%d:nc#%d:", tyname,
310 			dp->d_nsectors, dp->d_ntracks, dp->d_ncylinders);
311 		if (dp->d_secpercyl != dp->d_nsectors * dp->d_ntracks)
312 			printf("sc#%d:", dp->d_secpercyl);
313 		if (dp->d_type == DTYPE_SMD && dp->d_flags & D_BADSECT)
314 			printf("sf:");
315 		printf("\\\n\t:dt=%s:", dktypenames[dp->d_type]);
316 		for (part = NDDATA - 1; part >= 0; part--)
317 			if (dp->d_drivedata[part])
318 				break;
319 		for (j = 0; j <= part; j++)
320 			printf("d%d#%d:", j, dp->d_drivedata[j]);
321 		printf("\\\n");
322 		for (nparts = 0, part = PART('a'); part < NPARTITIONS; part++)
323 			if (defpart[def][part] != 0)
324 				nparts++;
325 		for (part = PART('a'); part < NPARTITIONS; part++) {
326 			if (defpart[def][part] == 0)
327 				continue;
328 			printf("\t:p%c#%d:", 'a' + part, defpart[def][part]);
329 			printf("o%c#%d:b%c#%d:f%c#%d:",
330 			    'a' + part, spc * startcyl[part],
331 			    'a' + part,
332 			    defparam[part].p_frag * defparam[part].p_fsize,
333 			    'a' + part, defparam[part].p_fsize);
334 			if (defparam[part].p_fstype == FS_SWAP)
335 				printf("t%c=swap:", 'a' + part);
336 			nparts--;
337 			printf("%s\n", nparts > 0 ? "\\" : "");
338 		}
339 #ifdef for_now
340 		defpart[def][PART('c')] -= badsecttable;
341 		part = PART('c');
342 		printf("#\t:p%c#%d:", 'a' + part, defpart[def][part]);
343 		printf("o%c#%d:b%c#%d:f%c#%d:\n",
344 		    'a' + part, spc * startcyl[part],
345 		    'a' + part,
346 		    defparam[part].p_frag * defparam[part].p_fsize,
347 		    'a' + part, defparam[part].p_fsize);
348 #endif
349 		exit(0);
350 	}
351 	printf("%s: #sectors/track=%d, #tracks/cylinder=%d #cylinders=%d\n",
352 		dp->d_typename, dp->d_nsectors, dp->d_ntracks,
353 		dp->d_ncylinders);
354 	printf("\n    Partition\t   Size\t Offset\t   Range\n");
355 	for (part = PART('a'); part < NPARTITIONS; part++) {
356 		printf("\t%c\t", 'a' + part);
357 		if (numcyls[part] == 0) {
358 			printf(" unused\n");
359 			continue;
360 		}
361 		printf("%7d\t%7d\t%4d - %d%s\n",
362 			defpart[def][part], startcyl[part] * spc,
363 			startcyl[part], startcyl[part] + numcyls[part] - 1,
364 			defpart[def][part] % spc ? "*" : "");
365 	}
366 	exit(0);
367 }
368 
369 struct disklabel disk;
370 
371 struct	field {
372 	char		*f_name;
373 	char		*f_defaults;
374 	u_int32_t	*f_location;
375 } fields[] = {
376 	{ "sector size",		"512",	&disk.d_secsize },
377 	{ "#sectors/track",		0,	&disk.d_nsectors },
378 	{ "#tracks/cylinder",		0,	&disk.d_ntracks },
379 	{ "#cylinders",			0,	&disk.d_ncylinders },
380 	{ 0, 0, 0 },
381 };
382 
383 struct disklabel *
384 promptfordisk(name)
385 	const char *name;
386 {
387 	struct disklabel *dp = &disk;
388 	struct field *fp;
389 	int i;
390 	const char *const *tp;
391 	char buf[BUFSIZ], *cp;
392 
393 	strncpy(dp->d_typename, name, sizeof(dp->d_typename));
394 	fprintf(stderr,
395 		"%s: unknown disk type, want to supply parameters (y/n)? ",
396 		name);
397 	if ((fgets(buf, BUFSIZ, stdin) == NULL) || buf[0] != 'y')
398 		return ((struct disklabel *)0);
399 	for (;;) {
400 		fprintf(stderr, "Disk/controller type (%s)? ", dktypenames[1]);
401 		if (fgets(buf, BUFSIZ, stdin) == NULL)
402 			return ((struct disklabel *)0);
403 		if ((cp = strchr(buf, '\n')) != NULL)
404 			*cp = '\0';
405 		if (buf[0] == '\0') {
406 			dp->d_type = 1;
407 			break;
408 		}
409 		if ((i = gettype(buf, dktypenames)) >= 0) {
410 			dp->d_type = i;
411 			break;
412 		}
413 		fprintf(stderr, "%s: unrecognized controller type\n", buf);
414 		fprintf(stderr, "use one of:\n");
415 		for (tp = dktypenames; *tp; tp++)
416 			if (strchr(*tp, ' ') == 0)
417 				fprintf(stderr, "\t%s\n", *tp);
418 	}
419 gettype:
420 	dp->d_flags = 0;
421 	fprintf(stderr, "type (winchester|removable|simulated)? ");
422 	if (fgets(buf, BUFSIZ, stdin) == NULL)
423 		return ((struct disklabel *)0);
424 	if ((cp = strchr(buf, '\n')) != NULL)
425 		*cp = '\0';
426 	if (buf[0] == '\0')
427 		goto gettype;
428 	switch (buf[0]) {
429 	case 'r':
430 		dp->d_flags = D_REMOVABLE;
431 		break;
432 	case 's':
433 		dp->d_flags = D_RAMDISK;
434 		break;
435 	case 'w':
436 		break;
437 	default:
438 		fprintf(stderr, "%s: bad disk type\n", buf);
439 		/* FALLTHROUGH */
440 	case '\0':
441 		goto gettype;
442 	}
443 	fprintf(stderr, "(type <cr> to get default value, if only one)\n");
444 	if (dp->d_type == DTYPE_SMD) {
445 		fprintf(stderr,
446 		    "Do '%s' disks support bad144 bad block forwarding (yes)? ",
447 		    dp->d_typename);
448 		if (fgets(buf, BUFSIZ, stdin) == NULL)
449 			return ((struct disklabel *)0);
450 		if (buf[0] != 'n')
451 			dp->d_flags |= D_BADSECT;
452 	}
453 	for (fp = fields; fp->f_name != NULL; fp++) {
454 again:
455 		fprintf(stderr, "%s ", fp->f_name);
456 		if (fp->f_defaults != NULL)
457 			fprintf(stderr, "(%s)", fp->f_defaults);
458 		fprintf(stderr, "? ");
459 		if (fgets(buf, BUFSIZ, stdin) == NULL)
460 			return ((struct disklabel *)0);
461 		if ((cp = strchr(buf, '\n')) != NULL)
462 			*cp = '\0';
463 		cp = buf;
464 		if (*cp == '\0') {
465 			if (fp->f_defaults == NULL) {
466 				fprintf(stderr, "no default value\n");
467 				goto again;
468 			}
469 			cp = fp->f_defaults;
470 		}
471 		*fp->f_location = atol(cp);
472 		if (*fp->f_location == 0) {
473 			fprintf(stderr, "%s: bad value\n", cp);
474 			goto again;
475 		}
476 	}
477 	fprintf(stderr, "sectors/cylinder (%d)? ",
478 	    dp->d_nsectors * dp->d_ntracks);
479 	if (fgets(buf, BUFSIZ, stdin) == NULL)
480 		return ((struct disklabel *)0);
481 	if ((cp = strchr(buf, '\n')) != NULL)
482 		*cp = '\0';
483 	if (buf[0] == 0)
484 		dp->d_secpercyl = dp->d_nsectors * dp->d_ntracks;
485 	else
486 		dp->d_secpercyl = atol(buf);
487 	fprintf(stderr, "Drive-type-specific parameters, <cr> to terminate:\n");
488 	for (i = 0; i < NDDATA; i++) {
489 		fprintf(stderr, "d%d? ", i);
490 		if (fgets(buf, BUFSIZ, stdin) == NULL)
491 			return ((struct disklabel *)0);
492 		if ((cp = strchr(buf, '\n')) != NULL)
493 			*cp = '\0';
494 		if (buf[0] == 0)
495 			break;
496 		dp->d_drivedata[i] = atol(buf);
497 	}
498 	return (dp);
499 }
500 
501 int
502 gettype(t, names)
503 	const char *t;
504 	const char *const *names;
505 {
506 	const char *const *nm;
507 
508 	for (nm = names; *nm; nm++)
509 		if (strcasecmp(t, *nm) == 0)
510 			return (nm - names);
511 	if (isdigit(*t))
512 		return (atoi(t));
513 	return (-1);
514 }
515 
516 void
517 usage(void)
518 {
519 	(void)fprintf(stderr, "Usage: diskpart [-dp] [-s size] disk-type\n");
520 	exit(1);
521 }
522