disk.c revision 112333
1/*
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
8 */
9
10#include <sys/cdefs.h>
11__FBSDID("$FreeBSD: head/lib/libdisk/disk.c 112333 2003-03-17 07:25:50Z phk $");
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <fcntl.h>
17#include <string.h>
18#include <inttypes.h>
19#include <err.h>
20#include <sys/sysctl.h>
21#include <sys/stdint.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <sys/ioctl.h>
25#include <sys/disklabel.h>
26#include <sys/diskslice.h>
27#include <sys/uuid.h>
28#include <sys/gpt.h>
29#include <paths.h>
30#include "libdisk.h"
31
32#include <ctype.h>
33#include <errno.h>
34#include <assert.h>
35#include <uuid.h>
36
37#ifdef DEBUG
38#define	DPRINT(x)	warn x
39#define	DPRINTX(x)	warnx x
40#else
41#define	DPRINT(x)
42#define	DPRINTX(x)
43#endif
44
45const char *
46chunk_name(chunk_e type)
47{
48	switch(type) {
49	case unused:	return ("unused");
50	case mbr:	return ("mbr");
51	case part:	return ("part");
52	case gpt:	return ("gpt");
53	case pc98:	return ("pc98");
54	case sun:	return ("sun");
55	case freebsd:	return ("freebsd");
56	case fat:	return ("fat");
57	case spare:	return ("spare");
58	case efi:	return ("efi");
59	default:	return ("??");
60	}
61};
62
63static chunk_e
64uuid_type(uuid_t *uuid)
65{
66	static uuid_t _efi = GPT_ENT_TYPE_EFI;
67	static uuid_t _mbr = GPT_ENT_TYPE_MBR;
68	static uuid_t _fbsd = GPT_ENT_TYPE_FREEBSD;
69	static uuid_t _swap = GPT_ENT_TYPE_FREEBSD_SWAP;
70	static uuid_t _ufs = GPT_ENT_TYPE_FREEBSD_UFS;
71	static uuid_t _vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
72
73	if (uuid_is_nil(uuid, NULL))
74		return (unused);
75	if (uuid_equal(uuid, &_efi, NULL))
76		return (efi);
77	if (uuid_equal(uuid, &_mbr, NULL))
78		return (mbr);
79	if (uuid_equal(uuid, &_fbsd, NULL))
80		return (freebsd);
81	if (uuid_equal(uuid, &_swap, NULL))
82		return (part);
83	if (uuid_equal(uuid, &_ufs, NULL))
84		return (part);
85	if (uuid_equal(uuid, &_vinum, NULL))
86		return (part);
87	return (spare);
88}
89
90struct disk *
91Open_Disk(const char *name)
92{
93
94	return Int_Open_Disk(name);
95}
96
97struct disk *
98Int_Open_Disk(const char *name)
99{
100	uuid_t uuid;
101	char *conftxt = NULL;
102	struct disk *d;
103	size_t txtsize;
104	int error, i;
105	char *p, *q, *r, *a, *b, *n, *t, *sn;
106	off_t o, len, off;
107	u_int l, s, ty, sc, hd, alt;
108	off_t lo[10];
109
110	error = sysctlbyname("kern.geom.conftxt", NULL, &txtsize, NULL, 0);
111	if (error) {
112		warn("kern.geom.conftxt sysctl not available, giving up!");
113		return (NULL);
114	}
115	conftxt = (char *) malloc(txtsize+1);
116	if (conftxt == NULL) {
117		DPRINT(("cannot malloc memory for conftxt"));
118		return (NULL);
119	}
120	error = sysctlbyname("kern.geom.conftxt", conftxt, &txtsize, NULL, 0);
121	if (error) {
122		DPRINT(("error reading kern.geom.conftxt from the system"));
123		free(conftxt);
124		return (NULL);
125	}
126	conftxt[txtsize] = '\0';	/* in case kernel bug is still there */
127
128	for (p = conftxt; p != NULL && *p; p = strchr(p, '\n')) {
129		if (*p == '\n')
130			p++;
131		a = strsep(&p, " ");
132		if (strcmp(a, "0"))
133			continue;
134
135		a = strsep(&p, " ");
136		if (strcmp(a, "DISK"))
137			continue;
138
139		a = strsep(&p, " ");
140		if (strcmp(a, name))
141			continue;
142		break;
143	}
144
145	q = strchr(p, '\n');
146	if (q != NULL)
147		*q++ = '\0';
148
149	d = (struct disk *)calloc(sizeof *d, 1);
150	if(d == NULL)
151		return NULL;
152
153	d->name = strdup(name);
154
155	a = strsep(&p, " ");	/* length in bytes */
156	len = strtoimax(a, &r, 0);
157	if (*r) {
158		printf("BARF %d <%d>\n", __LINE__, *r);
159		exit (0);
160	}
161
162	a = strsep(&p, " ");	/* sectorsize */
163	s = strtoul(a, &r, 0);
164	if (*r) {
165		printf("BARF %d <%d>\n", __LINE__, *r);
166		exit (0);
167	}
168
169	if (s == 0)
170		return (NULL);
171	d->sector_size = s;
172	len /= s;	/* media size in number of sectors. */
173
174	if (Add_Chunk(d, 0, len, name, whole, 0, 0, "-"))
175		DPRINT(("Failed to add 'whole' chunk"));
176
177	for (;;) {
178		a = strsep(&p, " ");
179		if (a == NULL)
180			break;
181		b = strsep(&p, " ");
182		o = strtoul(b, &r, 0);
183		if (*r) {
184			printf("BARF %d <%d>\n", __LINE__, *r);
185			exit (0);
186		}
187		if (!strcmp(a, "hd"))
188			d->bios_hd = o;
189		else if (!strcmp(a, "sc"))
190			d->bios_sect = o;
191		else
192			printf("HUH ? <%s> <%s>\n", a, b);
193	}
194
195	/*
196	 * Calculate the number of cylinders this disk must have. If we have
197	 * an obvious insanity, we set the number of cyclinders to zero.
198	 */
199	o = d->bios_hd * d->bios_sect;
200	d->bios_cyl = (o != 0) ? len / o : 0;
201
202	p = q;
203	lo[0] = 0;
204
205	for (; p != NULL && *p; p = q) {
206		q = strchr(p, '\n');
207		if (q != NULL)
208			*q++ = '\0';
209		a = strsep(&p, " ");	/* Index */
210		if (!strcmp(a, "0"))
211			break;
212		l = strtoimax(a, &r, 0);
213		if (*r) {
214			printf("BARF %d <%d>\n", __LINE__, *r);
215			exit (0);
216		}
217		t = strsep(&p, " ");	/* Type {SUN, BSD, MBR, PC98, GPT} */
218		n = strsep(&p, " ");	/* name */
219		a = strsep(&p, " ");	/* len */
220		len = strtoimax(a, &r, 0);
221		if (*r) {
222			printf("BARF %d <%d>\n", __LINE__, *r);
223			exit (0);
224		}
225		a = strsep(&p, " ");	/* secsize */
226		s = strtoimax(a, &r, 0);
227		if (*r) {
228			printf("BARF %d <%d>\n", __LINE__, *r);
229			exit (0);
230		}
231		for (;;) {
232			a = strsep(&p, " ");
233			if (a == NULL)
234				break;
235			/* XXX: Slice name may include a space. */
236			if (!strcmp(a, "sn")) {
237				sn = p;
238				break;
239			}
240			b = strsep(&p, " ");
241			o = strtoimax(b, &r, 0);
242			if (*r) {
243				uint32_t status;
244
245				uuid_from_string(b, &uuid, &status);
246				if (status != uuid_s_ok) {
247					printf("BARF %d <%d>\n", __LINE__, *r);
248					exit (0);
249				}
250				o = uuid_type(&uuid);
251			}
252			if (!strcmp(a, "o"))
253				off = o;
254			else if (!strcmp(a, "i"))
255				i = o;
256			else if (!strcmp(a, "ty"))
257				ty = o;
258			else if (!strcmp(a, "sc"))
259				sc = o;
260			else if (!strcmp(a, "hd"))
261				hd = o;
262			else if (!strcmp(a, "alt"))
263				alt = o;
264		}
265
266		/* PLATFORM POLICY BEGIN ----------------------------------- */
267		if (platform == p_sparc64 && !strcmp(t, "SUN") && i == 2)
268			continue;
269		if (platform == p_sparc64 && !strcmp(t, "SUN") &&
270		    d->chunks->part->part == NULL) {
271			d->bios_hd = hd;
272			d->bios_sect = sc;
273			o = d->chunks->size / (hd * sc);
274			o *= (hd * sc);
275			o -= alt * hd * sc;
276			if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-"))
277				DPRINT(("Failed to add 'freebsd' chunk"));
278		}
279		if (platform == p_alpha && !strcmp(t, "BSD") &&
280		    d->chunks->part->part == NULL) {
281			if (Add_Chunk(d, 0, d->chunks->size, name, freebsd,
282				      0, 0, "-"))
283				DPRINT(("Failed to add 'freebsd' chunk"));
284		}
285		if (!strcmp(t, "BSD") && i == RAW_PART)
286			continue;
287		/* PLATFORM POLICY END ------------------------------------- */
288
289		off /= s;
290		len /= s;
291		off += lo[l - 1];
292		lo[l] = off;
293		if (!strcmp(t, "SUN"))
294			i = Add_Chunk(d, off, len, n, part, 0, 0, 0);
295		else if (!strncmp(t, "MBR", 3)) {
296			switch (ty) {
297			case 0xa5:
298				i = Add_Chunk(d, off, len, n, freebsd, ty, 0, 0);
299				break;
300			case 0x01:
301			case 0x04:
302			case 0x06:
303			case 0x0b:
304			case 0x0c:
305			case 0x0e:
306				i = Add_Chunk(d, off, len, n, fat, ty, 0, 0);
307				break;
308			case 0xef:	/* EFI */
309				i = Add_Chunk(d, off, len, n, efi, ty, 0, 0);
310				break;
311			default:
312				i = Add_Chunk(d, off, len, n, mbr, ty, 0, 0);
313				break;
314			}
315		} else if (!strcmp(t, "BSD"))
316			i = Add_Chunk(d, off, len, n, part, ty, 0, 0);
317		else if (!strcmp(t, "PC98")) {
318			switch (ty & 0x7f) {
319			case 0x14:
320				i = Add_Chunk(d, off, len, n, freebsd, ty, 0,
321					      sn);
322				break;
323			case 0x20:
324			case 0x21:
325			case 0x22:
326			case 0x23:
327			case 0x24:
328				i = Add_Chunk(d, off, len, n, fat, ty, 0, sn);
329				break;
330			default:
331				i = Add_Chunk(d, off, len, n, pc98, ty, 0, sn);
332				break;
333			}
334		} else if (!strcmp(t, "GPT"))
335			i = Add_Chunk(d, off, len, n, ty, 0, 0, 0);
336		else if (!strcmp(t, "BDE"))
337			; /* nothing */
338		else {
339			printf("BARF %d\n", __LINE__);
340			exit(0);
341		}
342	}
343	/* PLATFORM POLICY BEGIN ------------------------------------- */
344	/* We have a chance to do things on a blank disk here */
345	if (platform == p_sparc64 && d->chunks->part->part == NULL) {
346		hd = d->bios_hd;
347		sc = d->bios_sect;
348		o = d->chunks->size / (hd * sc);
349		o *= (hd * sc);
350		o -= 2 * hd * sc;
351		if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-"))
352			DPRINT(("Failed to add 'freebsd' chunk"));
353	}
354	/* PLATFORM POLICY END --------------------------------------- */
355
356	return (d);
357	i = 0;
358}
359
360void
361Debug_Disk(struct disk *d)
362{
363
364	printf("Debug_Disk(%s)", d->name);
365#if 0
366	printf("  real_geom=%lu/%lu/%lu",
367	       d->real_cyl, d->real_hd, d->real_sect);
368#endif
369	printf("  bios_geom=%lu/%lu/%lu = %lu\n",
370		d->bios_cyl, d->bios_hd, d->bios_sect,
371		d->bios_cyl * d->bios_hd * d->bios_sect);
372#if defined(PC98)
373	printf("  boot1=%p, boot2=%p, bootipl=%p, bootmenu=%p\n",
374		d->boot1, d->boot2, d->bootipl, d->bootmenu);
375#elif defined(__i386__)
376	printf("  boot1=%p, boot2=%p, bootmgr=%p\n",
377		d->boot1, d->boot2, d->bootmgr);
378#elif defined(__alpha__)
379	printf("  boot1=%p, bootmgr=%p\n",
380		d->boot1, d->bootmgr);
381#elif defined(__ia64__)
382	printf("\n");
383#else
384/* Should be: error "Debug_Disk: unknown arch"; */
385#endif
386	Debug_Chunk(d->chunks);
387}
388
389void
390Free_Disk(struct disk *d)
391{
392	if (d->chunks)
393		Free_Chunk(d->chunks);
394	if (d->name)
395		free(d->name);
396#ifdef PC98
397	if (d->bootipl)
398		free(d->bootipl);
399	if (d->bootmenu)
400		free(d->bootmenu);
401#else
402#if !defined(__ia64__)
403	if (d->bootmgr)
404		free(d->bootmgr);
405#endif
406#endif
407#if !defined(__ia64__)
408	if (d->boot1)
409		free(d->boot1);
410#endif
411#if defined(__i386__)
412	if (d->boot2)
413		free(d->boot2);
414#endif
415	free(d);
416}
417
418#if 0
419void
420Collapse_Disk(struct disk *d)
421{
422
423	while (Collapse_Chunk(d, d->chunks))
424		;
425}
426#endif
427
428static int
429qstrcmp(const void* a, const void* b)
430{
431	char *str1 = *(char**)a;
432	char *str2 = *(char**)b;
433
434	return strcmp(str1, str2);
435}
436
437char **
438Disk_Names()
439{
440	int disk_cnt;
441	static char **disks;
442	int error;
443	size_t listsize;
444	char *disklist;
445
446	error = sysctlbyname("kern.disks", NULL, &listsize, NULL, 0);
447	if (error) {
448		warn("kern.disks sysctl not available");
449		return NULL;
450	}
451
452	disks = malloc(sizeof *disks * (1 + MAX_NO_DISKS));
453	if (disks == NULL)
454		return NULL;
455	disklist = (char *)malloc(listsize + 1);
456	if (disklist == NULL) {
457		free(disks);
458		return NULL;
459	}
460	memset(disks,0,sizeof *disks * (1 + MAX_NO_DISKS));
461	memset(disklist, 0, listsize + 1);
462	error = sysctlbyname("kern.disks", disklist, &listsize, NULL, 0);
463	if (error) {
464		free(disklist);
465		free(disks);
466		return NULL;
467	}
468	for (disk_cnt = 0; disk_cnt < MAX_NO_DISKS; disk_cnt++) {
469		disks[disk_cnt] = strsep(&disklist, " ");
470		if (disks[disk_cnt] == NULL)
471			break;
472	}
473	qsort(disks, disk_cnt, sizeof(char*), qstrcmp);
474	return disks;
475}
476
477#ifdef PC98
478void
479Set_Boot_Mgr(struct disk *d, const u_char *bootipl, const size_t bootipl_size,
480	const u_char *bootmenu, const size_t bootmenu_size)
481#else
482void
483Set_Boot_Mgr(struct disk *d, const u_char *b, const size_t s)
484#endif
485{
486#if !defined(__ia64__)
487#ifdef PC98
488	if (d->sector_size == 0)
489		return;
490	if (bootipl_size % d->sector_size != 0)
491		return;
492	if (d->bootipl)
493		free(d->bootipl);
494	if (!bootipl) {
495		d->bootipl = NULL;
496	} else {
497		d->bootipl_size = bootipl_size;
498		d->bootipl = malloc(bootipl_size);
499		if (!d->bootipl)
500			return;
501		memcpy(d->bootipl, bootipl, bootipl_size);
502	}
503
504	if (bootmenu_size % d->sector_size != 0)
505		return;
506	if (d->bootmenu)
507		free(d->bootmenu);
508	if (!bootmenu) {
509		d->bootmenu = NULL;
510	} else {
511		d->bootmenu_size = bootmenu_size;
512		d->bootmenu = malloc(bootmenu_size);
513		if (!d->bootmenu)
514			return;
515		memcpy(d->bootmenu, bootmenu, bootmenu_size);
516	}
517#else
518	if (d->sector_size == 0)
519		return;
520	if (s % d->sector_size != 0)
521		return;
522	if (d->bootmgr)
523		free(d->bootmgr);
524	if (!b) {
525		d->bootmgr = NULL;
526	} else {
527		d->bootmgr_size = s;
528		d->bootmgr = malloc(s);
529		if (!d->bootmgr)
530			return;
531		memcpy(d->bootmgr, b, s);
532	}
533#endif
534#endif
535}
536
537int
538Set_Boot_Blocks(struct disk *d, const u_char *b1, const u_char *b2)
539{
540#if defined(__i386__)
541	if (d->boot1)
542		free(d->boot1);
543	d->boot1 = malloc(512);
544	if (!d->boot1)
545		return -1;
546	memcpy(d->boot1, b1, 512);
547	if (d->boot2)
548		free(d->boot2);
549	d->boot2 = malloc(15 * 512);
550	if (!d->boot2)
551		return -1;
552	memcpy(d->boot2, b2, 15 * 512);
553#elif defined(__alpha__)
554	if (d->boot1)
555		free(d->boot1);
556	d->boot1 = malloc(15 * 512);
557	if (!d->boot1)
558		return -1;
559	memcpy(d->boot1, b1, 15 * 512);
560#elif defined(__sparc64__)
561	if (d->boot1 != NULL)
562		free(d->boot1);
563	d->boot1 = malloc(16 * 512);
564	if (d->boot1 == NULL)
565		return (-1);
566	memcpy(d->boot1, b1, 16 * 512);
567#elif defined(__ia64__)
568	/* nothing */
569#else
570/* Should be: #error "Set_Boot_Blocks: unknown arch"; */
571#endif
572	return 0;
573}
574
575#ifdef PC98
576const char *
577slice_type_name( int type, int subtype )
578{
579
580	switch (type) {
581	case whole:
582		return "whole";
583	case fat:
584		return "fat";
585	case freebsd:
586		switch (subtype) {
587		case 0xc494:	return "freebsd";
588		default:	return "unknown";
589		}
590	case unused:
591		return "unused";
592	default:
593		return "unknown";
594	}
595}
596#else /* PC98 */
597const char *
598slice_type_name( int type, int subtype )
599{
600
601	switch (type) {
602	case whole:
603		return "whole";
604	case mbr:
605		switch (subtype) {
606		case 1:		return "fat (12-bit)";
607		case 2:		return "XENIX /";
608		case 3:		return "XENIX /usr";
609		case 4:         return "fat (16-bit,<=32Mb)";
610		case 5:		return "extended DOS";
611		case 6:         return "fat (16-bit,>32Mb)";
612		case 7:         return "NTFS/HPFS/QNX";
613		case 8:         return "AIX bootable";
614		case 9:         return "AIX data";
615		case 10:	return "OS/2 bootmgr";
616		case 11:        return "fat (32-bit)";
617		case 12:        return "fat (32-bit,LBA)";
618		case 14:        return "fat (16-bit,>32Mb,LBA)";
619		case 15:        return "extended DOS, LBA";
620		case 18:        return "Compaq Diagnostic";
621		case 84:	return "OnTrack diskmgr";
622		case 100:	return "Netware 2.x";
623		case 101:	return "Netware 3.x";
624		case 115:	return "SCO UnixWare";
625		case 128:	return "Minix 1.1";
626		case 129:	return "Minix 1.5";
627		case 130:	return "linux_swap";
628		case 131:	return "ext2fs";
629		case 166:	return "OpenBSD FFS";	/* 0xA6 */
630		case 169:	return "NetBSD FFS";	/* 0xA9 */
631		case 182:	return "OpenBSD";	/* dedicated */
632		case 183:	return "bsd/os";
633		case 184:	return "bsd/os swap";
634		case 238:	return "EFI GPT";
635		case 239:	return "EFI Sys. Part.";
636		default:	return "unknown";
637		}
638	case fat:
639		return "fat";
640	case freebsd:
641		switch (subtype) {
642		case 165:	return "freebsd";
643		default:	return "unknown";
644		}
645	case extended:
646		return "extended";
647	case part:
648		return "part";
649	case efi:
650		return "efi";
651	case unused:
652		return "unused";
653	default:
654		return "unknown";
655	}
656}
657#endif /* PC98 */
658