disk.c revision 114300
150476Speter/*
21987Swollman * ----------------------------------------------------------------------------
31987Swollman * "THE BEER-WARE LICENSE" (Revision 42):
41987Swollman * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
5100346Sru * can do whatever you want with this stuff. If we meet some day, and you think
6100346Sru * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7100346Sru * ----------------------------------------------------------------------------
8100346Sru */
9100346Sru
10100346Sru#include <sys/cdefs.h>
11100346Sru__FBSDID("$FreeBSD: head/lib/libdisk/disk.c 114300 2003-04-30 17:14:58Z obrien $");
12100346Sru
13100346Sru#include <stdio.h>
14100346Sru#include <stdlib.h>
15100346Sru#include <unistd.h>
16100346Sru#include <fcntl.h>
17100346Sru#include <string.h>
18100346Sru#include <inttypes.h>
19100346Sru#include <err.h>
20100346Sru#include <sys/sysctl.h>
21100346Sru#include <sys/stdint.h>
22100346Sru#include <sys/types.h>
23100346Sru#include <sys/stat.h>
24100346Sru#include <sys/ioctl.h>
25100346Sru#include <sys/disklabel.h>
26100346Sru#include <sys/uuid.h>
27100346Sru#include <sys/gpt.h>
28100346Sru#include <paths.h>
29100346Sru#include "libdisk.h"
30100346Sru
31100346Sru#include <ctype.h>
32100346Sru#include <errno.h>
33100346Sru#include <assert.h>
34100346Sru#include <uuid.h>
35100346Sru
36100346Sru#ifdef DEBUG
374257Sphk#define	DPRINT(x)	warn x
38100346Sru#define	DPRINTX(x)	warnx x
39100346Sru#else
40100346Sru#define	DPRINT(x)
41100346Sru#define	DPRINTX(x)
42100346Sru#endif
43100346Sru
44100346Sruconst enum platform platform =
45100346Sru#if defined (P_DEBUG)
46100346Sru	P_DEBUG
47100346Sru#elif defined (PC98)
48100346Sru	p_pc98
49100346Sru#elif defined(__i386__)
50100346Sru	p_i386
51100346Sru#elif defined(__alpha__)
52100346Sru	p_alpha
53100346Sru#elif defined(__sparc64__)
54100346Sru	p_sparc64
55100346Sru#elif defined(__ia64__)
56100346Sru	p_ia64
57100346Sru#elif defined(__ppc__)
58100346Sru	p_ppc
59100346Sru#else
60100346Sru	IHAVENOIDEA
61100346Sru#endif
62100346Sru	;
63100346Sru
64100346Sruconst char *
65100346Sruchunk_name(chunk_e type)
66100346Sru{
67100346Sru	switch(type) {
68100346Sru	case unused:	return ("unused");
69100346Sru	case mbr:	return ("mbr");
70100346Sru	case part:	return ("part");
71100346Sru	case gpt:	return ("gpt");
72100346Sru	case pc98:	return ("pc98");
73100346Sru	case sun:	return ("sun");
74100346Sru	case freebsd:	return ("freebsd");
75100346Sru	case fat:	return ("fat");
76100346Sru	case spare:	return ("spare");
77100346Sru	case efi:	return ("efi");
78100346Sru	default:	return ("??");
79100346Sru	}
80100346Sru};
81100346Sru
82100346Srustatic chunk_e
83100346Sruuuid_type(uuid_t *uuid)
84100346Sru{
85100346Sru	static uuid_t _efi = GPT_ENT_TYPE_EFI;
86100346Sru	static uuid_t _mbr = GPT_ENT_TYPE_MBR;
87100346Sru	static uuid_t _fbsd = GPT_ENT_TYPE_FREEBSD;
88100346Sru	static uuid_t _swap = GPT_ENT_TYPE_FREEBSD_SWAP;
89100346Sru	static uuid_t _ufs = GPT_ENT_TYPE_FREEBSD_UFS;
90100346Sru	static uuid_t _vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
91100346Sru
92100346Sru	if (uuid_is_nil(uuid, NULL))
93100346Sru		return (unused);
94100346Sru	if (uuid_equal(uuid, &_efi, NULL))
95100346Sru		return (efi);
96100346Sru	if (uuid_equal(uuid, &_mbr, NULL))
97100346Sru		return (mbr);
98100346Sru	if (uuid_equal(uuid, &_fbsd, NULL))
99100346Sru		return (freebsd);
100100346Sru	if (uuid_equal(uuid, &_swap, NULL))
101100346Sru		return (part);
102100346Sru	if (uuid_equal(uuid, &_ufs, NULL))
103100346Sru		return (part);
104100346Sru	if (uuid_equal(uuid, &_vinum, NULL))
105100346Sru		return (part);
106100346Sru	return (spare);
107100346Sru}
108100346Sru
109100346Srustruct disk *
110100346SruOpen_Disk(const char *name)
111100346Sru{
112100346Sru
113100346Sru	return Int_Open_Disk(name);
114100346Sru}
115100346Sru
116100346Srustruct disk *
117100346SruInt_Open_Disk(const char *name)
118100346Sru{
119100346Sru	uuid_t uuid;
120100346Sru	char *conftxt = NULL;
121100346Sru	struct disk *d;
122100346Sru	size_t txtsize;
123100346Sru	int error, i;
124100346Sru	char *p, *q, *r, *a, *b, *n, *t, *sn;
125100346Sru	off_t o, len, off;
126100346Sru	u_int l, s, ty, sc, hd, alt;
127100346Sru	off_t lo[10];
128100346Sru
129100346Sru	error = sysctlbyname("kern.geom.conftxt", NULL, &txtsize, NULL, 0);
130100346Sru	if (error) {
131100346Sru		warn("kern.geom.conftxt sysctl not available, giving up!");
132100346Sru		return (NULL);
133100346Sru	}
134100346Sru	conftxt = (char *) malloc(txtsize+1);
135100346Sru	if (conftxt == NULL) {
136100346Sru		DPRINT(("cannot malloc memory for conftxt"));
137100346Sru		return (NULL);
138100346Sru	}
139100346Sru	error = sysctlbyname("kern.geom.conftxt", conftxt, &txtsize, NULL, 0);
140100346Sru	if (error) {
141100346Sru		DPRINT(("error reading kern.geom.conftxt from the system"));
142100346Sru		free(conftxt);
143100346Sru		return (NULL);
144100346Sru	}
145100346Sru	conftxt[txtsize] = '\0';	/* in case kernel bug is still there */
146100346Sru
147100346Sru	for (p = conftxt; p != NULL && *p; p = strchr(p, '\n')) {
148100346Sru		if (*p == '\n')
149100346Sru			p++;
150100346Sru		a = strsep(&p, " ");
151100346Sru		if (strcmp(a, "0"))
152100346Sru			continue;
153100346Sru
154100346Sru		a = strsep(&p, " ");
155100346Sru		if (strcmp(a, "DISK"))
156100346Sru			continue;
157100346Sru
158100346Sru		a = strsep(&p, " ");
159100346Sru		if (strcmp(a, name))
160100346Sru			continue;
161100346Sru		break;
162100346Sru	}
163100346Sru
164100346Sru	q = strchr(p, '\n');
165100346Sru	if (q != NULL)
166100346Sru		*q++ = '\0';
167100346Sru
168100346Sru	d = (struct disk *)calloc(sizeof *d, 1);
169100346Sru	if(d == NULL)
170100346Sru		return NULL;
171100346Sru
172100346Sru	d->name = strdup(name);
173100346Sru
174100346Sru	a = strsep(&p, " ");	/* length in bytes */
175100346Sru	len = strtoimax(a, &r, 0);
176100346Sru	if (*r) {
177100346Sru		printf("BARF %d <%d>\n", __LINE__, *r);
178100346Sru		exit (0);
179100346Sru	}
180100346Sru
181100346Sru	a = strsep(&p, " ");	/* sectorsize */
182100346Sru	s = strtoul(a, &r, 0);
183100346Sru	if (*r) {
184100346Sru		printf("BARF %d <%d>\n", __LINE__, *r);
185100346Sru		exit (0);
186100346Sru	}
187100346Sru
188100346Sru	if (s == 0)
189100346Sru		return (NULL);
190100346Sru	d->sector_size = s;
191100346Sru	len /= s;	/* media size in number of sectors. */
192100346Sru
193100346Sru	if (Add_Chunk(d, 0, len, name, whole, 0, 0, "-"))
194100346Sru		DPRINT(("Failed to add 'whole' chunk"));
195100346Sru
196100346Sru	for (;;) {
197100346Sru		a = strsep(&p, " ");
198100346Sru		if (a == NULL)
199100346Sru			break;
200100346Sru		b = strsep(&p, " ");
201100346Sru		o = strtoul(b, &r, 0);
202100346Sru		if (*r) {
203100346Sru			printf("BARF %d <%d>\n", __LINE__, *r);
204100346Sru			exit (0);
205100346Sru		}
206100346Sru		if (!strcmp(a, "hd"))
207100346Sru			d->bios_hd = o;
208100346Sru		else if (!strcmp(a, "sc"))
209100346Sru			d->bios_sect = o;
210100346Sru		else
211100346Sru			printf("HUH ? <%s> <%s>\n", a, b);
212100346Sru	}
213100346Sru
214100346Sru	/*
215100346Sru	 * Calculate the number of cylinders this disk must have. If we have
216100346Sru	 * an obvious insanity, we set the number of cyclinders to zero.
217100346Sru	 */
218100346Sru	o = d->bios_hd * d->bios_sect;
219100346Sru	d->bios_cyl = (o != 0) ? len / o : 0;
220100346Sru
221100346Sru	p = q;
222100346Sru	lo[0] = 0;
223100346Sru
224100346Sru	for (; p != NULL && *p; p = q) {
225100346Sru		q = strchr(p, '\n');
226100346Sru		if (q != NULL)
227100346Sru			*q++ = '\0';
228100346Sru		a = strsep(&p, " ");	/* Index */
229100346Sru		if (!strcmp(a, "0"))
230100346Sru			break;
231100346Sru		l = strtoimax(a, &r, 0);
232100346Sru		if (*r) {
233100346Sru			printf("BARF %d <%d>\n", __LINE__, *r);
234100346Sru			exit (0);
235100346Sru		}
236100346Sru		t = strsep(&p, " ");	/* Type {SUN, BSD, MBR, PC98, GPT} */
237100346Sru		n = strsep(&p, " ");	/* name */
238100346Sru		a = strsep(&p, " ");	/* len */
239100346Sru		len = strtoimax(a, &r, 0);
240100346Sru		if (*r) {
241100346Sru			printf("BARF %d <%d>\n", __LINE__, *r);
242100346Sru			exit (0);
243100346Sru		}
244100346Sru		a = strsep(&p, " ");	/* secsize */
245100346Sru		s = strtoimax(a, &r, 0);
246100346Sru		if (*r) {
247100346Sru			printf("BARF %d <%d>\n", __LINE__, *r);
248100346Sru			exit (0);
249100346Sru		}
2504257Sphk		for (;;) {
251100346Sru			a = strsep(&p, " ");
2521987Swollman			if (a == NULL)
253100346Sru				break;
254100346Sru			/* XXX: Slice name may include a space. */
25554351Smarcel			if (!strcmp(a, "sn")) {
25654351Smarcel				sn = p;
25754351Smarcel				break;
25854351Smarcel			}
25954351Smarcel			b = strsep(&p, " ");
260100346Sru			o = strtoimax(b, &r, 0);
261100346Sru			if (*r) {
2622365Sbde				uint32_t status;
263100346Sru
264100346Sru				uuid_from_string(b, &uuid, &status);
265104288Sru				if (status != uuid_s_ok) {
266100346Sru					printf("BARF %d <%d>\n", __LINE__, *r);
267100346Sru					exit (0);
26813537Sbde				}
269100346Sru				o = uuid_type(&uuid);
270100346Sru			}
271100346Sru			if (!strcmp(a, "o"))
272100872Sru				off = o;
273100346Sru			else if (!strcmp(a, "i"))
27413537Sbde				i = o;
2751987Swollman			else if (!strcmp(a, "ty"))
276100346Sru				ty = o;
277100346Sru			else if (!strcmp(a, "sc"))
278100346Sru				sc = o;
279100346Sru			else if (!strcmp(a, "hd"))
28054351Smarcel				hd = o;
28154351Smarcel			else if (!strcmp(a, "alt"))
28254351Smarcel				alt = o;
28354351Smarcel		}
28454351Smarcel
28554351Smarcel		/* PLATFORM POLICY BEGIN ----------------------------------- */
28654351Smarcel		if (platform == p_sparc64 && !strcmp(t, "SUN") && i == 2)
28785214Sdarrenr			continue;
28892868Sru		if (platform == p_sparc64 && !strcmp(t, "SUN") &&
28992868Sru		    d->chunks->part->part == NULL) {
29092868Sru			d->bios_hd = hd;
29185214Sdarrenr			d->bios_sect = sc;
29292868Sru			o = d->chunks->size / (hd * sc);
29385214Sdarrenr			o *= (hd * sc);
29485214Sdarrenr			o -= alt * hd * sc;
295			if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-"))
296				DPRINT(("Failed to add 'freebsd' chunk"));
297		}
298		if (platform == p_alpha && !strcmp(t, "BSD") &&
299		    d->chunks->part->part == NULL) {
300			if (Add_Chunk(d, 0, d->chunks->size, name, freebsd,
301				      0, 0, "-"))
302				DPRINT(("Failed to add 'freebsd' chunk"));
303		}
304		if (!strcmp(t, "BSD") && i == RAW_PART)
305			continue;
306		/* PLATFORM POLICY END ------------------------------------- */
307
308		off /= s;
309		len /= s;
310		off += lo[l - 1];
311		lo[l] = off;
312		if (!strcmp(t, "SUN"))
313			i = Add_Chunk(d, off, len, n, part, 0, 0, 0);
314		else if (!strncmp(t, "MBR", 3)) {
315			switch (ty) {
316			case 0xa5:
317				i = Add_Chunk(d, off, len, n, freebsd, ty, 0, 0);
318				break;
319			case 0x01:
320			case 0x04:
321			case 0x06:
322			case 0x0b:
323			case 0x0c:
324			case 0x0e:
325				i = Add_Chunk(d, off, len, n, fat, ty, 0, 0);
326				break;
327			case 0xef:	/* EFI */
328				i = Add_Chunk(d, off, len, n, efi, ty, 0, 0);
329				break;
330			default:
331				i = Add_Chunk(d, off, len, n, mbr, ty, 0, 0);
332				break;
333			}
334		} else if (!strcmp(t, "BSD"))
335			i = Add_Chunk(d, off, len, n, part, ty, 0, 0);
336		else if (!strcmp(t, "PC98")) {
337			switch (ty & 0x7f) {
338			case 0x14:
339				i = Add_Chunk(d, off, len, n, freebsd, ty, 0,
340					      sn);
341				break;
342			case 0x20:
343			case 0x21:
344			case 0x22:
345			case 0x23:
346			case 0x24:
347				i = Add_Chunk(d, off, len, n, fat, ty, 0, sn);
348				break;
349			default:
350				i = Add_Chunk(d, off, len, n, pc98, ty, 0, sn);
351				break;
352			}
353		} else if (!strcmp(t, "GPT"))
354			i = Add_Chunk(d, off, len, n, ty, 0, 0, 0);
355		else if (!strcmp(t, "BDE"))
356			; /* nothing */
357		else {
358			printf("BARF %d\n", __LINE__);
359			exit(0);
360		}
361	}
362	/* PLATFORM POLICY BEGIN ------------------------------------- */
363	/* We have a chance to do things on a blank disk here */
364	if (platform == p_sparc64 && d->chunks->part->part == NULL) {
365		hd = d->bios_hd;
366		sc = d->bios_sect;
367		o = d->chunks->size / (hd * sc);
368		o *= (hd * sc);
369		o -= 2 * hd * sc;
370		if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-"))
371			DPRINT(("Failed to add 'freebsd' chunk"));
372	}
373	/* PLATFORM POLICY END --------------------------------------- */
374
375	return (d);
376	i = 0;
377}
378
379void
380Debug_Disk(struct disk *d)
381{
382
383	printf("Debug_Disk(%s)", d->name);
384#if 0
385	printf("  real_geom=%lu/%lu/%lu",
386	       d->real_cyl, d->real_hd, d->real_sect);
387#endif
388	printf("  bios_geom=%lu/%lu/%lu = %lu\n",
389		d->bios_cyl, d->bios_hd, d->bios_sect,
390		d->bios_cyl * d->bios_hd * d->bios_sect);
391#if defined(PC98)
392	printf("  boot1=%p, boot2=%p, bootipl=%p, bootmenu=%p\n",
393		d->boot1, d->boot2, d->bootipl, d->bootmenu);
394#elif defined(__i386__)
395	printf("  boot1=%p, boot2=%p, bootmgr=%p\n",
396		d->boot1, d->boot2, d->bootmgr);
397#elif defined(__alpha__)
398	printf("  boot1=%p, bootmgr=%p\n",
399		d->boot1, d->bootmgr);
400#elif defined(__ia64__)
401	printf("\n");
402#else
403/* Should be: error "Debug_Disk: unknown arch"; */
404#endif
405	Debug_Chunk(d->chunks);
406}
407
408void
409Free_Disk(struct disk *d)
410{
411	if (d->chunks)
412		Free_Chunk(d->chunks);
413	if (d->name)
414		free(d->name);
415#ifdef PC98
416	if (d->bootipl)
417		free(d->bootipl);
418	if (d->bootmenu)
419		free(d->bootmenu);
420#else
421#if !defined(__ia64__)
422	if (d->bootmgr)
423		free(d->bootmgr);
424#endif
425#endif
426#if !defined(__ia64__)
427	if (d->boot1)
428		free(d->boot1);
429#endif
430#if defined(__i386__)
431	if (d->boot2)
432		free(d->boot2);
433#endif
434	free(d);
435}
436
437#if 0
438void
439Collapse_Disk(struct disk *d)
440{
441
442	while (Collapse_Chunk(d, d->chunks))
443		;
444}
445#endif
446
447static int
448qstrcmp(const void* a, const void* b)
449{
450	char *str1 = *(char**)a;
451	char *str2 = *(char**)b;
452
453	return strcmp(str1, str2);
454}
455
456char **
457Disk_Names()
458{
459	int disk_cnt;
460	static char **disks;
461	int error;
462	size_t listsize;
463	char *disklist;
464
465	error = sysctlbyname("kern.disks", NULL, &listsize, NULL, 0);
466	if (error) {
467		warn("kern.disks sysctl not available");
468		return NULL;
469	}
470
471	disks = malloc(sizeof *disks * (1 + MAX_NO_DISKS));
472	if (disks == NULL)
473		return NULL;
474	disklist = (char *)malloc(listsize + 1);
475	if (disklist == NULL) {
476		free(disks);
477		return NULL;
478	}
479	memset(disks,0,sizeof *disks * (1 + MAX_NO_DISKS));
480	memset(disklist, 0, listsize + 1);
481	error = sysctlbyname("kern.disks", disklist, &listsize, NULL, 0);
482	if (error) {
483		free(disklist);
484		free(disks);
485		return NULL;
486	}
487	for (disk_cnt = 0; disk_cnt < MAX_NO_DISKS; disk_cnt++) {
488		disks[disk_cnt] = strsep(&disklist, " ");
489		if (disks[disk_cnt] == NULL)
490			break;
491	}
492	qsort(disks, disk_cnt, sizeof(char*), qstrcmp);
493	return disks;
494}
495
496#ifdef PC98
497void
498Set_Boot_Mgr(struct disk *d, const u_char *bootipl, const size_t bootipl_size,
499	const u_char *bootmenu, const size_t bootmenu_size)
500#else
501void
502Set_Boot_Mgr(struct disk *d, const u_char *b, const size_t s)
503#endif
504{
505#if !defined(__ia64__)
506#ifdef PC98
507	if (d->sector_size == 0)
508		return;
509	if (bootipl_size % d->sector_size != 0)
510		return;
511	if (d->bootipl)
512		free(d->bootipl);
513	if (!bootipl) {
514		d->bootipl = NULL;
515	} else {
516		d->bootipl_size = bootipl_size;
517		d->bootipl = malloc(bootipl_size);
518		if (!d->bootipl)
519			return;
520		memcpy(d->bootipl, bootipl, bootipl_size);
521	}
522
523	if (bootmenu_size % d->sector_size != 0)
524		return;
525	if (d->bootmenu)
526		free(d->bootmenu);
527	if (!bootmenu) {
528		d->bootmenu = NULL;
529	} else {
530		d->bootmenu_size = bootmenu_size;
531		d->bootmenu = malloc(bootmenu_size);
532		if (!d->bootmenu)
533			return;
534		memcpy(d->bootmenu, bootmenu, bootmenu_size);
535	}
536#else
537	if (d->sector_size == 0)
538		return;
539	if (s % d->sector_size != 0)
540		return;
541	if (d->bootmgr)
542		free(d->bootmgr);
543	if (!b) {
544		d->bootmgr = NULL;
545	} else {
546		d->bootmgr_size = s;
547		d->bootmgr = malloc(s);
548		if (!d->bootmgr)
549			return;
550		memcpy(d->bootmgr, b, s);
551	}
552#endif
553#endif
554}
555
556int
557Set_Boot_Blocks(struct disk *d, const u_char *b1, const u_char *b2)
558{
559#if defined(__i386__)
560	if (d->boot1)
561		free(d->boot1);
562	d->boot1 = malloc(512);
563	if (!d->boot1)
564		return -1;
565	memcpy(d->boot1, b1, 512);
566	if (d->boot2)
567		free(d->boot2);
568	d->boot2 = malloc(15 * 512);
569	if (!d->boot2)
570		return -1;
571	memcpy(d->boot2, b2, 15 * 512);
572#elif defined(__alpha__)
573	if (d->boot1)
574		free(d->boot1);
575	d->boot1 = malloc(15 * 512);
576	if (!d->boot1)
577		return -1;
578	memcpy(d->boot1, b1, 15 * 512);
579#elif defined(__sparc64__)
580	if (d->boot1 != NULL)
581		free(d->boot1);
582	d->boot1 = malloc(16 * 512);
583	if (d->boot1 == NULL)
584		return (-1);
585	memcpy(d->boot1, b1, 16 * 512);
586#elif defined(__ia64__)
587	/* nothing */
588#else
589/* Should be: #error "Set_Boot_Blocks: unknown arch"; */
590#endif
591	return 0;
592}
593
594#ifdef PC98
595const char *
596slice_type_name( int type, int subtype )
597{
598
599	switch (type) {
600	case whole:
601		return "whole";
602	case fat:
603		return "fat";
604	case freebsd:
605		switch (subtype) {
606		case 0xc494:	return "freebsd";
607		default:	return "unknown";
608		}
609	case unused:
610		return "unused";
611	default:
612		return "unknown";
613	}
614}
615#else /* PC98 */
616const char *
617slice_type_name( int type, int subtype )
618{
619
620	switch (type) {
621	case whole:
622		return "whole";
623	case mbr:
624		switch (subtype) {
625		case 1:		return "fat (12-bit)";
626		case 2:		return "XENIX /";
627		case 3:		return "XENIX /usr";
628		case 4:         return "fat (16-bit,<=32Mb)";
629		case 5:		return "extended DOS";
630		case 6:         return "fat (16-bit,>32Mb)";
631		case 7:         return "NTFS/HPFS/QNX";
632		case 8:         return "AIX bootable";
633		case 9:         return "AIX data";
634		case 10:	return "OS/2 bootmgr";
635		case 11:        return "fat (32-bit)";
636		case 12:        return "fat (32-bit,LBA)";
637		case 14:        return "fat (16-bit,>32Mb,LBA)";
638		case 15:        return "extended DOS, LBA";
639		case 18:        return "Compaq Diagnostic";
640		case 84:	return "OnTrack diskmgr";
641		case 100:	return "Netware 2.x";
642		case 101:	return "Netware 3.x";
643		case 115:	return "SCO UnixWare";
644		case 128:	return "Minix 1.1";
645		case 129:	return "Minix 1.5";
646		case 130:	return "linux_swap";
647		case 131:	return "ext2fs";
648		case 166:	return "OpenBSD FFS";	/* 0xA6 */
649		case 169:	return "NetBSD FFS";	/* 0xA9 */
650		case 182:	return "OpenBSD";	/* dedicated */
651		case 183:	return "bsd/os";
652		case 184:	return "bsd/os swap";
653		case 238:	return "EFI GPT";
654		case 239:	return "EFI Sys. Part.";
655		default:	return "unknown";
656		}
657	case fat:
658		return "fat";
659	case freebsd:
660		switch (subtype) {
661		case 165:	return "freebsd";
662		default:	return "unknown";
663		}
664	case extended:
665		return "extended";
666	case part:
667		return "part";
668	case efi:
669		return "efi";
670	case unused:
671		return "unused";
672	default:
673		return "unknown";
674	}
675}
676#endif /* PC98 */
677