xref: /netbsd-src/sbin/fdisk/fdisk.c (revision ce0bb6e8d2e560ecacbe865a848624f94498063b)
1 /*	$NetBSD: fdisk.c,v 1.10 1995/03/18 14:55:36 cgd Exp $	*/
2 
3 /*
4  * Mach Operating System
5  * Copyright (c) 1992 Carnegie Mellon University
6  * All Rights Reserved.
7  *
8  * Permission to use, copy, modify and distribute this software and its
9  * documentation is hereby granted, provided that both the copyright
10  * notice and this permission notice appear in all copies of the
11  * software, derivative works or modified versions, and any portions
12  * thereof, and that both notices appear in supporting documentation.
13  *
14  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
15  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
16  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
17  *
18  * Carnegie Mellon requests users of this software to return to
19  *
20  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
21  *  School of Computer Science
22  *  Carnegie Mellon University
23  *  Pittsburgh PA 15213-3890
24  *
25  * any improvements or extensions that they make and grant Carnegie Mellon
26  * the rights to redistribute these changes.
27  */
28 
29 #ifndef lint
30 static char rcsid[] = "$NetBSD: fdisk.c,v 1.10 1995/03/18 14:55:36 cgd Exp $";
31 #endif /* not lint */
32 
33 #include <sys/types.h>
34 #include <sys/disklabel.h>
35 #include <sys/ioctl.h>
36 #include <sys/stat.h>
37 
38 #include <ctype.h>
39 #include <err.h>
40 #include <fcntl.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 
46 #define LBUF 100
47 static char lbuf[LBUF];
48 
49 /*
50  * 14-Dec-89  Robert Baron (rvb) at Carnegie-Mellon University
51  *	Copyright (c) 1989	Robert. V. Baron
52  *	Created.
53  */
54 
55 char *disk = "/dev/rwd0d";
56 
57 struct disklabel disklabel;		/* disk parameters */
58 
59 int cylinders, sectors, heads, cylindersectors, disksectors;
60 
61 struct mboot {
62 	unsigned char padding[2]; /* force the longs to be long alligned */
63 	unsigned char bootinst[DOSPARTOFF];
64 	struct	dos_partition parts[4];
65 	unsigned short int	signature;
66 };
67 struct mboot mboot;
68 
69 #define ACTIVE 0x80
70 #define BOOT_MAGIC 0xAA55
71 
72 int dos_cylinders;
73 int dos_heads;
74 int dos_sectors;
75 int dos_cylindersectors;
76 
77 #define DOSSECT(s,c)	(((s) & 0x3f) | (((c) >> 2) & 0xc0))
78 #define DOSCYL(c)	((c) & 0xff)
79 int partition = -1;
80 
81 int a_flag;		/* set active partition */
82 int i_flag;		/* replace partition data */
83 int u_flag;		/* update partition data */
84 
85 unsigned char bootcode[] = {
86 0x33, 0xc0, 0xfa, 0x8e, 0xd0, 0xbc, 0x00, 0x7c, 0x8e, 0xc0, 0x8e, 0xd8, 0xfb, 0x8b, 0xf4, 0xbf,
87 0x00, 0x06, 0xb9, 0x00, 0x02, 0xfc, 0xf3, 0xa4, 0xea, 0x1d, 0x06, 0x00, 0x00, 0xb0, 0x04, 0xbe,
88 0xbe, 0x07, 0x80, 0x3c, 0x80, 0x74, 0x0c, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x75, 0xf4, 0xbe, 0xbd,
89 0x06, 0xeb, 0x43, 0x8b, 0xfe, 0x8b, 0x14, 0x8b, 0x4c, 0x02, 0x83, 0xc6, 0x10, 0xfe, 0xc8, 0x74,
90 0x0a, 0x80, 0x3c, 0x80, 0x75, 0xf4, 0xbe, 0xbd, 0x06, 0xeb, 0x2b, 0xbd, 0x05, 0x00, 0xbb, 0x00,
91 0x7c, 0xb8, 0x01, 0x02, 0xcd, 0x13, 0x73, 0x0c, 0x33, 0xc0, 0xcd, 0x13, 0x4d, 0x75, 0xef, 0xbe,
92 0x9e, 0x06, 0xeb, 0x12, 0x81, 0x3e, 0xfe, 0x7d, 0x55, 0xaa, 0x75, 0x07, 0x8b, 0xf7, 0xea, 0x00,
93 0x7c, 0x00, 0x00, 0xbe, 0x85, 0x06, 0x2e, 0xac, 0x0a, 0xc0, 0x74, 0x06, 0xb4, 0x0e, 0xcd, 0x10,
94 0xeb, 0xf4, 0xfb, 0xeb, 0xfe,
95 'M', 'i', 's', 's', 'i', 'n', 'g', ' ',
96 	'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
97 'E', 'r', 'r', 'o', 'r', ' ', 'l', 'o', 'a', 'd', 'i', 'n', 'g', ' ',
98 	'o', 'p', 'e', 'r', 'a', 't', 'i', 'n', 'g', ' ', 's', 'y', 's', 't', 'e', 'm', 0,
99 'I', 'n', 'v', 'a', 'l', 'i', 'd', ' ',
100 	'p', 'a', 'r', 't', 'i', 't', 'i', 'o', 'n', ' ', 't', 'a', 'b', 'l', 'e', 0,
101 'A', 'u', 't', 'h', 'o', 'r', ' ', '-', ' ',
102 	'S', 'i', 'e', 'g', 'm', 'a', 'r', ' ', 'S', 'c', 'h', 'm', 'i', 'd', 't', 0,0,0,
103 
104   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
105   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
106   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
107   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
108   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
109   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
110   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
111   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
112   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
113   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
114   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
115   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
116   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0
117 };
118 
119 struct part_type {
120 	int type;
121 	char *name;
122 } part_types[] = {
123 	{0x00, "unused"},
124 	{0x01, "Primary DOS with 12 bit FAT"},
125 	{0x02, "XENIX / filesystem"},
126 	{0x03, "XENIX /usr filesystem"},
127 	{0x04, "Primary DOS with 16 bit FAT"},
128 	{0x05, "Extended DOS"},
129 	{0x06, "Primary 'big' DOS (> 32MB)"},
130 	{0x07, "OS/2 HPFS, QNX or Advanced UNIX"},
131 	{0x08, "AIX filesystem"},
132 	{0x09, "AIX boot partition or Coherent"},
133 	{0x0A, "OS/2 Boot Manager or OPUS"},
134 	{0x10, "OPUS"},
135 	{0x40, "VENIX 286"},
136 	{0x50, "DM"},
137 	{0x51, "DM"},
138 	{0x52, "CP/M or Microport SysV/AT"},
139 	{0x56, "GB"},
140 	{0x61, "Speed"},
141 	{0x63, "ISC UNIX, other System V/386, GNU HURD or Mach"},
142 	{0x64, "Novell Netware 2.xx"},
143 	{0x65, "Novell Netware 3.xx"},
144 	{0x75, "PCIX"},
145 	{0x80, "Minix 1.1 ... 1.4a"},
146 	{0x81, "Minix 1.4b ... 1.5.10"},
147 	{0x82, "Linux swap"},
148 	{0x83, "Linux filesystem"},
149 	{0x93, "Amoeba filesystem"},
150 	{0x94, "Amoeba bad block table"},
151 	{0xA5, "NetBSD or 386BSD"},
152 	{0xB7, "BSDI BSD/386 filesystem"},
153 	{0xB8, "BSDI BSD/386 swap"},
154 	{0xDB, "Concurrent CPM or C.DOS or CTOS"},
155 	{0xE1, "Speed"},
156 	{0xE3, "Speed"},
157 	{0xE4, "Speed"},
158 	{0xF1, "Speed"},
159 	{0xF2, "DOS 3.3+ Secondary"},
160 	{0xF4, "Speed"},
161 	{0xFF, "BBT (Bad Blocks Table)"},
162 };
163 
164 void	usage __P((void));
165 void	print_s0 __P((int));
166 void	print_part __P((int));
167 void	init_sector0 __P((int));
168 void	change_part __P((int));
169 void	print_params __P((void));
170 void	change_active __P((int));
171 void	get_params_to_use __P((void));
172 void	dos __P((int, unsigned char *, unsigned char *, unsigned char *));
173 int	open_disk __P((int));
174 int	read_disk __P((int, void *));
175 int	write_disk __P((int, void *));
176 int	get_params __P((void));
177 int	read_s0 __P((void));
178 int	write_s0 __P((void));
179 int	yesno __P((char *));
180 void	decimal __P((char *, int *));
181 int	type_match __P((const void *, const void *));
182 char	*get_type __P((int));
183 
184 int
185 main(argc, argv)
186 	int argc;
187 	char *argv[];
188 {
189 	int ch;
190 	int part;
191 
192 	a_flag = i_flag = u_flag = 0;
193 	while ((ch = getopt(argc, argv, "0123aiu")) != -1)
194 		switch (ch) {
195 		case '0':
196 			partition = 0;
197 			break;
198 		case '1':
199 			partition = 1;
200 			break;
201 		case '2':
202 			partition = 2;
203 			break;
204 		case '3':
205 			partition = 3;
206 			break;
207 		case 'a':
208 			a_flag = 1;
209 			break;
210 		case 'i':
211 			i_flag = 1;
212 		case 'u':
213 			u_flag = 1;
214 			break;
215 		default:
216 			usage();
217 		}
218 	argc -= optind;
219 	argv += optind;
220 
221 	if (argc > 0)
222 		disk = argv[0];
223 
224 	if (open_disk(a_flag || i_flag || u_flag) < 0)
225 		exit(1);
226 
227 	printf("******* Working on device %s *******\n", disk);
228 	if (u_flag)
229 		get_params_to_use();
230 	else
231 		print_params();
232 
233 	if (read_s0())
234 		init_sector0(1);
235 
236 	printf("Warning: BIOS sector numbering starts with sector 1\n");
237 	printf("Information from DOS bootblock is:\n");
238 	if (partition == -1) {
239 		for (part = 0; part < NDOSPART; part++)
240 			change_part(part);
241 	} else
242 		change_part(partition);
243 
244 	if (u_flag || a_flag)
245 		change_active(partition);
246 
247 	if (u_flag || a_flag) {
248 		printf("\nWe haven't changed the partition table yet.  ");
249 		printf("This is your last chance.\n");
250 		print_s0(-1);
251 		if (yesno("Should we write new partition table?"))
252 			write_s0();
253 	}
254 
255 	exit(0);
256 }
257 
258 void
259 usage()
260 {
261 
262 	(void)fprintf(stderr, "usage: fdisk [-aiu] [-0|-1|-2|-3] [device]\n");
263 	exit(1);
264 }
265 
266 void
267 print_s0(which)
268 	int which;
269 {
270 	int part;
271 
272 	print_params();
273 	printf("Information from DOS bootblock is:\n");
274 	if (which == -1) {
275 		for (part = 0; part < NDOSPART; part++)
276 			printf("%d: ", part), print_part(part);
277 	} else
278 		print_part(which);
279 }
280 
281 static struct dos_partition mtpart = { 0 };
282 
283 void
284 print_part(part)
285 	int part;
286 {
287 	struct dos_partition *partp;
288 
289 	partp = &mboot.parts[part];
290 	if (!memcmp(partp, &mtpart, sizeof(struct dos_partition))) {
291 		printf("<UNUSED>\n");
292 		return;
293 	}
294 	printf("sysid %d (%s)\n", partp->dp_typ, get_type(partp->dp_typ));
295 	printf("    start %d, size %d (%d MB), flag %x\n",
296 	    partp->dp_start, partp->dp_size,
297 	    partp->dp_size * 512 / (1024 * 1024), partp->dp_flag);
298 	printf("\tbeg: cylinder %4d, head %3d, sector %2d\n",
299 	    DPCYL(partp->dp_scyl, partp->dp_ssect),
300 	    partp->dp_shd, DPSECT(partp->dp_ssect));
301 	printf("\tend: cylinder %4d, head %3d, sector %2d\n",
302 	    DPCYL(partp->dp_ecyl, partp->dp_esect),
303 	    partp->dp_ehd, DPSECT(partp->dp_esect));
304 }
305 
306 void
307 init_sector0(start)
308 	int start;
309 {
310 	struct dos_partition *partp;
311 
312 	memcpy(mboot.bootinst, bootcode, sizeof(bootcode));
313 	mboot.signature = BOOT_MAGIC;
314 
315 	partp = &mboot.parts[3];
316 	partp->dp_typ = DOSPTYP_386BSD;
317 	partp->dp_flag = ACTIVE;
318 	partp->dp_start = start;
319 	partp->dp_size = disksectors - start;
320 
321 	dos(partp->dp_start,
322 	    &partp->dp_scyl, &partp->dp_shd, &partp->dp_ssect);
323 	dos(partp->dp_start + partp->dp_size - 1,
324 	    &partp->dp_ecyl, &partp->dp_ehd, &partp->dp_esect);
325 }
326 
327 void
328 change_part(part)
329 	int part;
330 {
331 	struct dos_partition *partp;
332 
333 	partp = &mboot.parts[part];
334 
335 	printf("The data for partition %d is:\n", part);
336 	print_part(part);
337 
338 	if (!u_flag || !yesno("Do you want to change it?"))
339 		return;
340 
341 	if (i_flag) {
342 		memset(partp, 0, sizeof(*partp));
343 		if (part == 3) {
344 			init_sector0(1);
345 			printf("\nThe static data for the DOS partition 3 has been reinitialized to:\n");
346 			print_part(part);
347 		}
348 	}
349 
350 	do {
351 		{
352 			int sysid, start, size;
353 
354 			sysid = partp->dp_typ,
355 			start = partp->dp_start,
356 			size = partp->dp_size;
357 			decimal("sysid", &sysid);
358 			decimal("start", &start);
359 			decimal("size", &size);
360 			partp->dp_typ = sysid;
361 			partp->dp_start = start;
362 			partp->dp_size = size;
363 		}
364 
365 		if (yesno("Explicitly specify beg/end address?")) {
366 			int tsector, tcylinder, thead;
367 
368 			tcylinder = DPCYL(partp->dp_scyl, partp->dp_ssect);
369 			thead = partp->dp_shd;
370 			tsector = DPSECT(partp->dp_ssect);
371 			decimal("beginning cylinder", &tcylinder);
372 			decimal("beginning head", &thead);
373 			decimal("beginning sector", &tsector);
374 			partp->dp_scyl = DOSCYL(tcylinder);
375 			partp->dp_shd = thead;
376 			partp->dp_ssect = DOSSECT(tsector, tcylinder);
377 
378 			tcylinder = DPCYL(partp->dp_ecyl, partp->dp_esect);
379 			thead = partp->dp_ehd;
380 			tsector = DPSECT(partp->dp_esect);
381 			decimal("ending cylinder", &tcylinder);
382 			decimal("ending head", &thead);
383 			decimal("ending sector", &tsector);
384 			partp->dp_ecyl = DOSCYL(tcylinder);
385 			partp->dp_ehd = thead;
386 			partp->dp_esect = DOSSECT(tsector, tcylinder);
387 		} else {
388 			dos(partp->dp_start,
389 			    &partp->dp_scyl, &partp->dp_shd, &partp->dp_ssect);
390 			dos(partp->dp_start + partp->dp_size - 1,
391 			    &partp->dp_ecyl, &partp->dp_ehd, &partp->dp_esect);
392 		}
393 
394 		print_part(part);
395 	} while (!yesno("Is this entry okay?"));
396 }
397 
398 void
399 print_params()
400 {
401 
402 	printf("parameters extracted from in-core disklabel are:\n");
403 	printf("cylinders=%d heads=%d sectors/track=%d (%d sectors/cylinder)\n\n",
404 	    cylinders, heads, sectors, cylindersectors);
405 	if (dos_sectors > 63 || dos_cylinders > 1023 || dos_heads > 255)
406 		printf("Figures below won't work with BIOS for partitions not in cylinder 1\n");
407 	printf("parameters to be used for BIOS calculations are:\n");
408 	printf("cylinders=%d heads=%d sectors/track=%d (%d sectors/cylinder)\n\n",
409 	    dos_cylinders, dos_heads, dos_sectors, dos_cylindersectors);
410 }
411 
412 void
413 change_active(which)
414 	int which;
415 {
416 	struct dos_partition *partp;
417 	int part;
418 	int active = 3;
419 
420 	partp = &mboot.parts[0];
421 
422 	if (a_flag && which != -1)
423 		active = which;
424 	else {
425 		for (part = 0; part < NDOSPART; part++)
426 			if (partp[part].dp_flag & ACTIVE)
427 				active = part;
428 	}
429 	if (yesno("Do you want to change the active partition?")) {
430 		do {
431 			decimal("active partition", &active);
432 		} while (!yesno("Are you happy with this choice?"));
433 	}
434 	for (part = 0; part < NDOSPART; part++)
435 		partp[part].dp_flag &= ~ACTIVE;
436 	partp[active].dp_flag |= ACTIVE;
437 }
438 
439 void
440 get_params_to_use()
441 {
442 
443 	print_params();
444 	if (yesno("Do you want to change our idea of what BIOS thinks?")) {
445 		do {
446 			decimal("BIOS's idea of #cylinders", &dos_cylinders);
447 			decimal("BIOS's idea of #heads", &dos_heads);
448 			decimal("BIOS's idea of #sectors", &dos_sectors);
449 			dos_cylindersectors = dos_heads * dos_sectors;
450 			print_params();
451 		} while (!yesno("Are you happy with this choice?"));
452 	}
453 }
454 
455 /***********************************************\
456 * Change real numbers into strange dos numbers	*
457 \***********************************************/
458 void
459 dos(sector, cylinderp, headp, sectorp)
460 	int sector;
461 	unsigned char *cylinderp, *headp, *sectorp;
462 {
463 	int cylinder, head;
464 
465 	cylinder = sector / dos_cylindersectors;
466 	sector -= cylinder * dos_cylindersectors;
467 
468 	head = sector / dos_sectors;
469 	sector -= head * dos_sectors;
470 
471 	*cylinderp = DOSCYL(cylinder);
472 	*headp = head;
473 	*sectorp = DOSSECT(sector + 1, cylinder);
474 }
475 
476 int fd;
477 
478 int
479 open_disk(u_flag)
480 	int u_flag;
481 {
482 	struct stat st;
483 
484 	if ((fd = open(disk, u_flag ? O_RDWR : O_RDONLY)) == -1) {
485 		warn("%s", disk);
486 		return (-1);
487 	}
488 	if (fstat(fd, &st) == -1) {
489 		close(fd);
490 		warn("%s", disk);
491 		return (-1);
492 	}
493 	if (!S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode)) {
494 		close(fd);
495 		warnx("%s is not a character device or regular file", disk);
496 		return (-1);
497 	}
498 	if (get_params() == -1) {
499 		close(fd);
500 		return (-1);
501 	}
502 	return (0);
503 }
504 
505 int
506 read_disk(sector, buf)
507 	int sector;
508 	void *buf;
509 {
510 
511 	if (lseek(fd, (off_t)(sector * 512), 0) == -1)
512 		return (-1);
513 	return (read(fd, buf, 512));
514 }
515 
516 int
517 write_disk(sector, buf)
518 	int sector;
519 	void *buf;
520 {
521 
522 	if (lseek(fd, (off_t)(sector * 512), 0) == -1)
523 		return (-1);
524 	return (write(fd, buf, 512));
525 }
526 
527 int
528 get_params()
529 {
530 
531 	if (ioctl(fd, DIOCGDINFO, &disklabel) == -1) {
532 		warn("DIOCGDINFO");
533 		return (-1);
534 	}
535 
536 	dos_cylinders = cylinders = disklabel.d_ncylinders;
537 	dos_heads = heads = disklabel.d_ntracks;
538 	dos_sectors = sectors = disklabel.d_nsectors;
539 	dos_cylindersectors = cylindersectors = heads * sectors;
540 	disksectors = cylinders * heads * sectors;
541 
542 	return (0);
543 }
544 
545 int
546 read_s0()
547 {
548 
549 	if (read_disk(0, mboot.bootinst) == -1) {
550 		warn("can't read fdisk partition table");
551 		return (-1);
552 	}
553 	if (mboot.signature != BOOT_MAGIC) {
554 		warn("invalid fdisk partition table found");
555 		/* So should we initialize things? */
556 		return (-1);
557 	}
558 	return (0);
559 }
560 
561 int
562 write_s0()
563 {
564 	int flag;
565 
566 	/*
567 	 * write enable label sector before write (if necessary),
568 	 * disable after writing.
569 	 * needed if the disklabel protected area also protects
570 	 * sector 0. (e.g. empty disk)
571 	 */
572 	flag = 1;
573 	if (ioctl(fd, DIOCWLABEL, &flag) < 0)
574 		warn("DIOCWLABEL");
575 	if (write_disk(0, mboot.bootinst) == -1) {
576 		warn("can't write fdisk partition table");
577 		return -1;
578 	}
579 	flag = 0;
580 	if (ioctl(fd, DIOCWLABEL, &flag) < 0)
581 		warn("DIOCWLABEL");
582 }
583 
584 int
585 yesno(str)
586 	char *str;
587 {
588 	int ch, first;
589 
590 	printf("%s [n] ", str);
591 
592 	first = ch = getchar();
593 	while (ch != '\n' && ch != EOF)
594 		ch = getchar();
595 	return (first == 'y' || first == 'Y');
596 }
597 
598 void
599 decimal(str, num)
600 	char *str;
601 	int *num;
602 {
603 	int acc = 0;
604 	char *cp;
605 
606 	for (;; printf("%s is not a valid decimal number.\n", lbuf)) {
607 		printf("Supply a decimal value for \"%s\" [%d] ", str, *num);
608 
609 		fgets(lbuf, LBUF, stdin);
610 		lbuf[strlen(lbuf)-1] = '\0';
611 		cp = lbuf;
612 
613 		cp += strspn(cp, " \t");
614 		if (*cp == '\0')
615 			return;
616 
617 		if (!isdigit(*cp))
618 			continue;
619 		acc = strtol(lbuf, &cp, 10);
620 
621 		cp += strspn(cp, " \t");
622 		if (*cp != '\0')
623 			continue;
624 
625 		*num = acc;
626 		return;
627 	}
628 
629 }
630 
631 int
632 type_match(key, item)
633 	const void *key, *item;
634 {
635 	const int *typep = key;
636 	const struct part_type *ptr = item;
637 
638 	if (*typep < ptr->type)
639 		return (-1);
640 	if (*typep > ptr->type)
641 		return (1);
642 	return (0);
643 }
644 
645 char *
646 get_type(type)
647 	int type;
648 {
649 	struct part_type *ptr;
650 
651 	ptr = bsearch(&type, part_types,
652 	    sizeof(part_types) / sizeof(struct part_type),
653 	    sizeof(struct part_type), type_match);
654 	if (ptr == 0)
655 		return ("unknown");
656 	else
657 		return (ptr->name);
658 }
659