disk.c revision 106368
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 106368 2002-11-03 01:37:08Z marcel $");
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/diskmbr.h>
28#include <sys/uuid.h>
29#include <sys/gpt.h>
30#include <paths.h>
31#include "libdisk.h"
32
33#include <ctype.h>
34#include <errno.h>
35#include <assert.h>
36#include <uuid.h>
37
38#define DOSPTYP_EXTENDED        5
39#ifdef DEBUG
40#define	DPRINT(x)	warn x
41#define	DPRINTX(x)	warnx x
42#else
43#define	DPRINT(x)
44#define	DPRINTX(x)
45#endif
46
47const char *
48chunk_name(chunk_e type)
49{
50	switch(type) {
51	case unused:	return ("unused");
52	case mbr:	return ("mbr");
53	case part:	return ("part");
54	case gpt:	return ("gpt");
55	case pc98:	return ("pc98");
56	case sun:	return ("sun");
57	case freebsd:	return ("freebsd");
58	case fat:	return ("fat");
59	case spare:	return ("spare");
60	default:	return ("??");
61	}
62};
63
64static chunk_e
65uuid_type(uuid_t *uuid)
66{
67	static uuid_t _efi = GPT_ENT_TYPE_EFI;
68	static uuid_t _mbr = GPT_ENT_TYPE_MBR;
69	static uuid_t _fbsd = GPT_ENT_TYPE_FREEBSD;
70	static uuid_t _swap = GPT_ENT_TYPE_FREEBSD_SWAP;
71	static uuid_t _ufs = GPT_ENT_TYPE_FREEBSD_UFS;
72	static uuid_t _vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
73
74	if (uuid_is_nil(uuid, NULL))
75		return (unused);
76	if (uuid_equal(uuid, &_efi, NULL))
77		return (fat);
78	if (uuid_equal(uuid, &_mbr, NULL))
79		return (mbr);
80	if (uuid_equal(uuid, &_fbsd, NULL))
81		return (freebsd);
82	if (uuid_equal(uuid, &_swap, NULL))
83		return (part);
84	if (uuid_equal(uuid, &_ufs, NULL))
85		return (part);
86	if (uuid_equal(uuid, &_vinum, NULL))
87		return (part);
88	return (spare);
89}
90
91struct disk *
92Open_Disk(const char *name)
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;
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) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); }
158
159	a = strsep(&p, " ");	/* sectorsize */
160	s = strtoul(a, &r, 0);
161	if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); }
162
163	d->sector_size = s;
164	len /= s;	/* media size in number of sectors. */
165
166	if (Add_Chunk(d, 0, len, name, whole, 0, 0, "-"))
167		DPRINT(("Failed to add 'whole' chunk"));
168
169	for (;;) {
170		a = strsep(&p, " ");
171		if (a == NULL)
172			break;
173		b = strsep(&p, " ");
174		o = strtoul(b, &r, 0);
175		if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); }
176		if (!strcmp(a, "hd"))
177			d->bios_hd = o;
178		else if (!strcmp(a, "sc"))
179			d->bios_sect = o;
180		else
181			printf("HUH ? <%s> <%s>\n", a, b);
182	}
183
184	/*
185	 * Calculate the number of cylinders this disk must have. If we have
186	 * an obvious insanity, we set the number of cyclinders to zero.
187	 */
188	o = d->bios_hd * d->bios_sect;
189	d->bios_cyl = (o != 0 && (len % o) == 0) ? len / o : 0;
190
191	p = q;
192	lo[0] = 0;
193
194	for (; p != NULL && *p; p = q) {
195		q = strchr(p, '\n');
196		if (q != NULL)
197			*q++ = '\0';
198		a = strsep(&p, " ");	/* Index */
199		if (!strcmp(a, "0"))
200			break;
201		l = strtoimax(a, &r, 0);
202		if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); }
203		t = strsep(&p, " ");	/* Type {SUN, BSD, MBR, GPT} */
204		n = strsep(&p, " ");	/* name */
205		a = strsep(&p, " ");	/* len */
206		len = strtoimax(a, &r, 0);
207		if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); }
208		a = strsep(&p, " ");	/* secsize */
209		s = strtoimax(a, &r, 0);
210		if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); }
211		for (;;) {
212			a = strsep(&p, " ");
213			if (a == NULL)
214				break;
215			b = strsep(&p, " ");
216			o = strtoimax(b, &r, 0);
217			if (*r) {
218				uint32_t status;
219
220				uuid_from_string(b, &uuid, &status);
221				if (status != uuid_s_ok) {
222					printf("BARF %d <%d>\n", __LINE__, *r);
223					exit (0);
224				}
225				o = uuid_type(&uuid);
226			}
227			if (!strcmp(a, "o"))
228				off = o;
229			else if (!strcmp(a, "i"))
230				i = o;
231			else if (!strcmp(a, "ty"))
232				ty = o;
233			else if (!strcmp(a, "sc"))
234				sc = o;
235			else if (!strcmp(a, "hd"))
236				hd = o;
237			else if (!strcmp(a, "alt"))
238				alt = o;
239		}
240
241		/* PLATFORM POLICY BEGIN ------------------------------------- */
242		if (platform == p_sparc64 && !strcmp(t, "SUN") && i == 2)
243			continue;
244		if (platform == p_sparc64 && !strcmp(t, "SUN") &&
245		    d->chunks->part->part == NULL) {
246			d->bios_hd = hd;
247			d->bios_sect = sc;
248			o = d->chunks->size / (hd * sc);
249			o *= (hd * sc);
250			o -= alt * hd * sc;
251			if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-"))
252				DPRINT(("Failed to add 'freebsd' chunk"));
253		}
254		if (platform == p_alpha && !strcmp(t, "BSD") &&
255		    d->chunks->part->part == NULL) {
256			o = d->chunks->size;
257			if (Add_Chunk(d, 0, d->chunks->size, name, freebsd, 0, 0, "-"))
258				DPRINT(("Failed to add 'freebsd' chunk"));
259		}
260		if (!strcmp(t, "BSD") && i == RAW_PART)
261			continue;
262		/* PLATFORM POLICY END --------------------------------------- */
263
264		off /= s;
265		len /= s;
266		off += lo[l - 1];
267		lo[l] = off;
268		printf("%s [%s] %jd %jd\n", t, n, (intmax_t)(off / s), (intmax_t) (len / s));
269		if (!strcmp(t, "SUN"))
270			i = Add_Chunk(d, off, len, n, part, 0, 0, 0);
271		else if (!strncmp(t, "MBR", 3) && ty == 165)
272			i = Add_Chunk(d, off, len, n, freebsd, ty, 0, 0);
273		else if (!strncmp(t, "MBR", 3))
274			i = Add_Chunk(d, off, len, n, mbr, ty, 0, 0);
275		else if (!strcmp(t, "BSD"))
276			i = Add_Chunk(d, off, len, n, part, 0, 0, 0);
277		else if (!strcmp(t, "PC98"))
278			i = Add_Chunk(d, off, len, n, pc98, 0, 0, 0);
279		else if (!strcmp(t, "GPT"))
280			i = Add_Chunk(d, off, len, n, ty, 0, 0, 0);
281		else
282			{ printf("BARF %d\n", __LINE__); exit(0); }
283		printf("error = %d\n", i);
284	}
285	/* PLATFORM POLICY BEGIN ------------------------------------- */
286	/* We have a chance to do things on a blank disk here */
287printf("c %p\n", d->chunks);
288printf("c->p %p\n", d->chunks->part);
289printf("c->p->p %p\n", d->chunks->part->part);
290	if (platform == p_sparc64 && d->chunks->part->part == NULL) {
291printf("HERE %d\n", __LINE__);
292		hd = d->bios_hd;
293		sc = d->bios_sect;
294		o = d->chunks->size / (hd * sc);
295		o *= (hd * sc);
296		o -= 2 * hd * sc;
297printf("HERE %d\n", __LINE__);
298		if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-"))
299			DPRINT(("Failed to add 'freebsd' chunk"));
300	}
301	/* PLATFORM POLICY END --------------------------------------- */
302
303	return (d);
304	i = 0;
305}
306
307void
308Debug_Disk(struct disk *d)
309{
310	printf("Debug_Disk(%s)", d->name);
311#if 0
312	printf("  real_geom=%lu/%lu/%lu", d->real_cyl, d->real_hd, d->real_sect);
313#endif
314	printf("  bios_geom=%lu/%lu/%lu = %lu\n",
315		d->bios_cyl, d->bios_hd, d->bios_sect,
316		d->bios_cyl * d->bios_hd * d->bios_sect);
317#if defined(PC98)
318	printf("  boot1=%p, boot2=%p, bootipl=%p, bootmenu=%p\n",
319		d->boot1, d->boot2, d->bootipl, d->bootmenu);
320#elif defined(__i386__)
321	printf("  boot1=%p, boot2=%p, bootmgr=%p\n",
322		d->boot1, d->boot2, d->bootmgr);
323#elif defined(__alpha__)
324	printf("  boot1=%p, bootmgr=%p\n",
325		d->boot1, d->bootmgr);
326#elif defined(__ia64__)
327	printf("\n");
328#else
329/* Should be: error "Debug_Disk: unknown arch"; */
330#endif
331	Debug_Chunk(d->chunks);
332}
333
334void
335Free_Disk(struct disk *d)
336{
337	if(d->chunks) Free_Chunk(d->chunks);
338	if(d->name) free(d->name);
339#ifdef PC98
340	if(d->bootipl) free(d->bootipl);
341	if(d->bootmenu) free(d->bootmenu);
342#else
343#if !defined(__ia64__)
344	if(d->bootmgr) free(d->bootmgr);
345#endif
346#endif
347#if !defined(__ia64__)
348	if(d->boot1) free(d->boot1);
349#endif
350#if defined(__i386__)
351	if(d->boot2) free(d->boot2);
352#endif
353	free(d);
354}
355
356#if 0
357void
358Collapse_Disk(struct disk *d)
359{
360
361	while(Collapse_Chunk(d, d->chunks))
362		;
363}
364#endif
365
366static int
367qstrcmp(const void* a, const void* b)
368{
369
370	char *str1 = *(char**)a;
371	char *str2 = *(char**)b;
372	return strcmp(str1, str2);
373}
374
375char **
376Disk_Names()
377{
378	int disk_cnt;
379	static char **disks;
380	int error;
381	size_t listsize;
382	char *disklist;
383
384	error = sysctlbyname("kern.disks", NULL, &listsize, NULL, 0);
385	if (error) {
386		warn("kern.disks sysctl not available");
387		return NULL;
388	}
389
390	disks = malloc(sizeof *disks * (1 + MAX_NO_DISKS));
391	if (disks == NULL)
392		return NULL;
393	disklist = (char *)malloc(listsize + 1);
394	if (disklist == NULL) {
395		free(disks);
396		return NULL;
397	}
398	memset(disks,0,sizeof *disks * (1 + MAX_NO_DISKS));
399	memset(disklist, 0, listsize + 1);
400	error = sysctlbyname("kern.disks", disklist, &listsize, NULL, 0);
401	if (error) {
402		free(disklist);
403		free(disks);
404		return NULL;
405	}
406	for (disk_cnt = 0; disk_cnt < MAX_NO_DISKS; disk_cnt++) {
407		disks[disk_cnt] = strsep(&disklist, " ");
408		if (disks[disk_cnt] == NULL)
409			break;
410											}
411	qsort(disks, disk_cnt, sizeof(char*), qstrcmp);
412	return disks;
413}
414
415#ifdef PC98
416void
417Set_Boot_Mgr(struct disk *d, const u_char *bootipl, const size_t bootipl_size,
418	const u_char *bootmenu, const size_t bootmenu_size)
419#else
420void
421Set_Boot_Mgr(struct disk *d, const u_char *b, const size_t s)
422#endif
423{
424#if !defined(__ia64__)
425#ifdef PC98
426	if (bootipl_size % d->sector_size != 0)
427		return;
428	if (d->bootipl)
429		free(d->bootipl);
430	if (!bootipl) {
431		d->bootipl = NULL;
432	} else {
433		d->bootipl_size = bootipl_size;
434		d->bootipl = malloc(bootipl_size);
435		if(!d->bootipl) return;
436		memcpy(d->bootipl, bootipl, bootipl_size);
437	}
438
439	if (bootmenu_size % d->sector_size != 0)
440		return;
441	if (d->bootmenu)
442		free(d->bootmenu);
443	if (!bootmenu) {
444		d->bootmenu = NULL;
445	} else {
446		d->bootmenu_size = bootmenu_size;
447		d->bootmenu = malloc(bootmenu_size);
448		if(!d->bootmenu) return;
449		memcpy(d->bootmenu, bootmenu, bootmenu_size);
450	}
451#else
452	if (s % d->sector_size != 0)
453		return;
454	if (d->bootmgr)
455		free(d->bootmgr);
456	if (!b) {
457		d->bootmgr = NULL;
458	} else {
459		d->bootmgr_size = s;
460		d->bootmgr = malloc(s);
461		if(!d->bootmgr) return;
462		memcpy(d->bootmgr, b, s);
463	}
464#endif
465#endif
466}
467
468int
469Set_Boot_Blocks(struct disk *d, const u_char *b1, const u_char *b2)
470{
471#if defined(__i386__)
472	if (d->boot1) free(d->boot1);
473	d->boot1 = malloc(512);
474	if(!d->boot1) return -1;
475	memcpy(d->boot1, b1, 512);
476	if (d->boot2) free(d->boot2);
477	d->boot2 = malloc(15 * 512);
478	if(!d->boot2) return -1;
479	memcpy(d->boot2, b2, 15 * 512);
480#elif defined(__alpha__)
481	if (d->boot1) free(d->boot1);
482	d->boot1 = malloc(15 * 512);
483	if(!d->boot1) return -1;
484	memcpy(d->boot1, b1, 15 * 512);
485#elif defined(__sparc64__)
486	if (d->boot1 != NULL)
487		free(d->boot1);
488	d->boot1 = malloc(16 * 512);
489	if (d->boot1 == NULL)
490		return (-1);
491	memcpy(d->boot1, b1, 16 * 512);
492#elif defined(__ia64__)
493	/* nothing */
494#else
495/* Should be: #error "Set_Boot_Blocks: unknown arch"; */
496#endif
497	return 0;
498}
499
500#ifdef PC98
501const char *
502slice_type_name( int type, int subtype )
503{
504
505	switch (type) {
506	case 0:
507		return "whole";
508	case 2:
509		return "fat";
510	case 3:
511		switch (subtype) {
512		case 0xc494:	return "freebsd";
513		default:	return "unknown";
514		}
515	default:
516		return "unknown";
517	}
518}
519#else /* PC98 */
520const char *
521slice_type_name( int type, int subtype )
522{
523
524	switch (type) {
525	case 0:
526		return "whole";
527	case 1:
528		switch (subtype) {
529		case 1:		return "fat (12-bit)";
530		case 2:		return "XENIX /";
531		case 3:		return "XENIX /usr";
532		case 4:         return "fat (16-bit,<=32Mb)";
533		case 5:		return "extended DOS";
534		case 6:         return "fat (16-bit,>32Mb)";
535		case 7:         return "NTFS/HPFS/QNX";
536		case 8:         return "AIX bootable";
537		case 9:         return "AIX data";
538		case 10:	return "OS/2 bootmgr";
539		case 11:        return "fat (32-bit)";
540		case 12:        return "fat (32-bit,LBA)";
541		case 14:        return "fat (16-bit,>32Mb,LBA)";
542		case 15:        return "extended DOS, LBA";
543		case 18:        return "Compaq Diagnostic";
544		case 84:	return "OnTrack diskmgr";
545		case 100:	return "Netware 2.x";
546		case 101:	return "Netware 3.x";
547		case 115:	return "SCO UnixWare";
548		case 128:	return "Minix 1.1";
549		case 129:	return "Minix 1.5";
550		case 130:	return "linux_swap";
551		case 131:	return "ext2fs";
552		case 166:	return "OpenBSD FFS";	/* 0xA6 */
553		case 169:	return "NetBSD FFS";	/* 0xA9 */
554		case 182:	return "OpenBSD";	/* dedicated */
555		case 183:	return "bsd/os";
556		case 184:	return "bsd/os swap";
557		case 238:	return "EFI GPT";
558		case 239:	return "EFI Sys. Part.";
559		default:	return "unknown";
560		}
561	case 2:
562		return "fat";
563	case 3:
564		switch (subtype) {
565		case 165:	return "freebsd";
566		default:	return "unknown";
567		}
568	case 4:
569		return "extended";
570	case 5:
571		return "part";
572	case 6:
573		return "unused";
574	default:
575		return "unknown";
576	}
577}
578#endif /* PC98 */
579