disk.c revision 106837
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 106837 2002-11-13 05:31:32Z 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/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	return Int_Open_Disk(name);
94}
95
96struct disk *
97Int_Open_Disk(const char *name)
98{
99	uuid_t uuid;
100	char *conftxt = NULL;
101	struct disk *d;
102	size_t txtsize;
103	int error, i;
104	char *p, *q, *r, *a, *b, *n, *t;
105	off_t o, len, off;
106	u_int l, s, ty, sc, hd, alt;
107	off_t lo[10];
108
109	error = sysctlbyname("kern.geom.conftxt", NULL, &txtsize, NULL, 0);
110	if (error) {
111		warn("kern.geom.conftxt sysctl not available, giving up!");
112		return (NULL);
113	}
114	conftxt = (char *) malloc(txtsize+1);
115	if (conftxt == NULL) {
116		DPRINT(("cannot malloc memory for conftxt"));
117		return (NULL);
118	}
119	error = sysctlbyname("kern.geom.conftxt", conftxt, &txtsize, NULL, 0);
120	if (error) {
121		DPRINT(("error reading kern.geom.conftxt from the system"));
122		free(conftxt);
123		return (NULL);
124	}
125	conftxt[txtsize] = '\0';	/* in case kernel bug is still there */
126
127	for (p = conftxt; p != NULL && *p; p = strchr(p, '\n')) {
128		if (*p == '\n')
129			p++;
130		a = strsep(&p, " ");
131		if (strcmp(a, "0"))
132			continue;
133
134		a = strsep(&p, " ");
135		if (strcmp(a, "DISK"))
136			continue;
137
138		a = strsep(&p, " ");
139		if (strcmp(a, name))
140			continue;
141		break;
142	}
143
144	q = strchr(p, '\n');
145	if (q != NULL)
146		*q++ = '\0';
147
148	d = (struct disk *)calloc(sizeof *d, 1);
149	if(d == NULL)
150		return NULL;
151
152	d->name = strdup(name);
153
154	a = strsep(&p, " ");	/* length in bytes */
155	len = strtoimax(a, &r, 0);
156	if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); }
157
158	a = strsep(&p, " ");	/* sectorsize */
159	s = strtoul(a, &r, 0);
160	if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); }
161
162	d->sector_size = s;
163	len /= s;	/* media size in number of sectors. */
164
165	if (Add_Chunk(d, 0, len, name, whole, 0, 0, "-"))
166		DPRINT(("Failed to add 'whole' chunk"));
167
168	for (;;) {
169		a = strsep(&p, " ");
170		if (a == NULL)
171			break;
172		b = strsep(&p, " ");
173		o = strtoul(b, &r, 0);
174		if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); }
175		if (!strcmp(a, "hd"))
176			d->bios_hd = o;
177		else if (!strcmp(a, "sc"))
178			d->bios_sect = o;
179		else
180			printf("HUH ? <%s> <%s>\n", a, b);
181	}
182
183	/*
184	 * Calculate the number of cylinders this disk must have. If we have
185	 * an obvious insanity, we set the number of cyclinders to zero.
186	 */
187	o = d->bios_hd * d->bios_sect;
188	d->bios_cyl = (o != 0 && (len % o) == 0) ? len / o : 0;
189
190	p = q;
191	lo[0] = 0;
192
193	for (; p != NULL && *p; p = q) {
194		q = strchr(p, '\n');
195		if (q != NULL)
196			*q++ = '\0';
197		a = strsep(&p, " ");	/* Index */
198		if (!strcmp(a, "0"))
199			break;
200		l = strtoimax(a, &r, 0);
201		if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); }
202		t = strsep(&p, " ");	/* Type {SUN, BSD, MBR, GPT} */
203		n = strsep(&p, " ");	/* name */
204		a = strsep(&p, " ");	/* len */
205		len = strtoimax(a, &r, 0);
206		if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); }
207		a = strsep(&p, " ");	/* secsize */
208		s = strtoimax(a, &r, 0);
209		if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); }
210		for (;;) {
211			a = strsep(&p, " ");
212			if (a == NULL)
213				break;
214			b = strsep(&p, " ");
215			o = strtoimax(b, &r, 0);
216			if (*r) {
217				uint32_t status;
218
219				uuid_from_string(b, &uuid, &status);
220				if (status != uuid_s_ok) {
221					printf("BARF %d <%d>\n", __LINE__, *r);
222					exit (0);
223				}
224				o = uuid_type(&uuid);
225			}
226			if (!strcmp(a, "o"))
227				off = o;
228			else if (!strcmp(a, "i"))
229				i = o;
230			else if (!strcmp(a, "ty"))
231				ty = o;
232			else if (!strcmp(a, "sc"))
233				sc = o;
234			else if (!strcmp(a, "hd"))
235				hd = o;
236			else if (!strcmp(a, "alt"))
237				alt = o;
238		}
239
240		/* PLATFORM POLICY BEGIN ------------------------------------- */
241		if (platform == p_sparc64 && !strcmp(t, "SUN") && i == 2)
242			continue;
243		if (platform == p_sparc64 && !strcmp(t, "SUN") &&
244		    d->chunks->part->part == NULL) {
245			d->bios_hd = hd;
246			d->bios_sect = sc;
247			o = d->chunks->size / (hd * sc);
248			o *= (hd * sc);
249			o -= alt * hd * sc;
250			if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-"))
251				DPRINT(("Failed to add 'freebsd' chunk"));
252		}
253		if (platform == p_alpha && !strcmp(t, "BSD") &&
254		    d->chunks->part->part == NULL) {
255			if (Add_Chunk(d, 0, d->chunks->size, name, freebsd, 0, 0, "-"))
256				DPRINT(("Failed to add 'freebsd' chunk"));
257		}
258		if (!strcmp(t, "BSD") && i == RAW_PART)
259			continue;
260		/* PLATFORM POLICY END --------------------------------------- */
261
262		off /= s;
263		len /= s;
264		off += lo[l - 1];
265		lo[l] = off;
266		if (!strcmp(t, "SUN"))
267			i = Add_Chunk(d, off, len, n, part, 0, 0, 0);
268		else if (!strncmp(t, "MBR", 3)) {
269			switch (ty) {
270			case 0xa5:
271				i = Add_Chunk(d, off, len, n, freebsd, ty, 0, 0);
272				break;
273			case 0x01:
274			case 0x04:
275			case 0x06:
276			case 0x0b:
277			case 0x0c:
278			case 0x0e:
279				i = Add_Chunk(d, off, len, n, fat, ty, 0, 0);
280				break;
281			case 0xef:	/* EFI */
282				i = Add_Chunk(d, off, len, n, efi, ty, 0, 0);
283				break;
284			default:
285				i = Add_Chunk(d, off, len, n, mbr, ty, 0, 0);
286				break;
287			}
288		} else if (!strcmp(t, "BSD"))
289			i = Add_Chunk(d, off, len, n, part, 0, 0, 0);
290		else if (!strcmp(t, "PC98")) {
291			switch (ty & 0x7f) {
292			case 0x14:
293				i = Add_Chunk(d, off, len, n, freebsd, ty, 0, 0);
294				break;
295			case 0x20:
296			case 0x21:
297			case 0x22:
298			case 0x23:
299			case 0x24:
300				i = Add_Chunk(d, off, len, n, fat, ty, 0, 0);
301				break;
302			default:
303				i = Add_Chunk(d, off, len, n, pc98, ty, 0, 0);
304				break;
305			}
306		} else if (!strcmp(t, "GPT"))
307			i = Add_Chunk(d, off, len, n, ty, 0, 0, 0);
308		else
309			{ printf("BARF %d\n", __LINE__); exit(0); }
310	}
311	/* PLATFORM POLICY BEGIN ------------------------------------- */
312	/* We have a chance to do things on a blank disk here */
313	if (platform == p_sparc64 && d->chunks->part->part == NULL) {
314		hd = d->bios_hd;
315		sc = d->bios_sect;
316		o = d->chunks->size / (hd * sc);
317		o *= (hd * sc);
318		o -= 2 * hd * sc;
319		if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-"))
320			DPRINT(("Failed to add 'freebsd' chunk"));
321	}
322	/* PLATFORM POLICY END --------------------------------------- */
323
324	return (d);
325	i = 0;
326}
327
328void
329Debug_Disk(struct disk *d)
330{
331	printf("Debug_Disk(%s)", d->name);
332#if 0
333	printf("  real_geom=%lu/%lu/%lu", d->real_cyl, d->real_hd, d->real_sect);
334#endif
335	printf("  bios_geom=%lu/%lu/%lu = %lu\n",
336		d->bios_cyl, d->bios_hd, d->bios_sect,
337		d->bios_cyl * d->bios_hd * d->bios_sect);
338#if defined(PC98)
339	printf("  boot1=%p, boot2=%p, bootipl=%p, bootmenu=%p\n",
340		d->boot1, d->boot2, d->bootipl, d->bootmenu);
341#elif defined(__i386__)
342	printf("  boot1=%p, boot2=%p, bootmgr=%p\n",
343		d->boot1, d->boot2, d->bootmgr);
344#elif defined(__alpha__)
345	printf("  boot1=%p, bootmgr=%p\n",
346		d->boot1, d->bootmgr);
347#elif defined(__ia64__)
348	printf("\n");
349#else
350/* Should be: error "Debug_Disk: unknown arch"; */
351#endif
352	Debug_Chunk(d->chunks);
353}
354
355void
356Free_Disk(struct disk *d)
357{
358	if(d->chunks) Free_Chunk(d->chunks);
359	if(d->name) free(d->name);
360#ifdef PC98
361	if(d->bootipl) free(d->bootipl);
362	if(d->bootmenu) free(d->bootmenu);
363#else
364#if !defined(__ia64__)
365	if(d->bootmgr) free(d->bootmgr);
366#endif
367#endif
368#if !defined(__ia64__)
369	if(d->boot1) free(d->boot1);
370#endif
371#if defined(__i386__)
372	if(d->boot2) free(d->boot2);
373#endif
374	free(d);
375}
376
377#if 0
378void
379Collapse_Disk(struct disk *d)
380{
381
382	while(Collapse_Chunk(d, d->chunks))
383		;
384}
385#endif
386
387static int
388qstrcmp(const void* a, const void* b)
389{
390
391	char *str1 = *(char**)a;
392	char *str2 = *(char**)b;
393	return strcmp(str1, str2);
394}
395
396char **
397Disk_Names()
398{
399	int disk_cnt;
400	static char **disks;
401	int error;
402	size_t listsize;
403	char *disklist;
404
405	error = sysctlbyname("kern.disks", NULL, &listsize, NULL, 0);
406	if (error) {
407		warn("kern.disks sysctl not available");
408		return NULL;
409	}
410
411	disks = malloc(sizeof *disks * (1 + MAX_NO_DISKS));
412	if (disks == NULL)
413		return NULL;
414	disklist = (char *)malloc(listsize + 1);
415	if (disklist == NULL) {
416		free(disks);
417		return NULL;
418	}
419	memset(disks,0,sizeof *disks * (1 + MAX_NO_DISKS));
420	memset(disklist, 0, listsize + 1);
421	error = sysctlbyname("kern.disks", disklist, &listsize, NULL, 0);
422	if (error) {
423		free(disklist);
424		free(disks);
425		return NULL;
426	}
427	for (disk_cnt = 0; disk_cnt < MAX_NO_DISKS; disk_cnt++) {
428		disks[disk_cnt] = strsep(&disklist, " ");
429		if (disks[disk_cnt] == NULL)
430			break;
431											}
432	qsort(disks, disk_cnt, sizeof(char*), qstrcmp);
433	return disks;
434}
435
436#ifdef PC98
437void
438Set_Boot_Mgr(struct disk *d, const u_char *bootipl, const size_t bootipl_size,
439	const u_char *bootmenu, const size_t bootmenu_size)
440#else
441void
442Set_Boot_Mgr(struct disk *d, const u_char *b, const size_t s)
443#endif
444{
445#if !defined(__ia64__)
446#ifdef PC98
447	if (bootipl_size % d->sector_size != 0)
448		return;
449	if (d->bootipl)
450		free(d->bootipl);
451	if (!bootipl) {
452		d->bootipl = NULL;
453	} else {
454		d->bootipl_size = bootipl_size;
455		d->bootipl = malloc(bootipl_size);
456		if(!d->bootipl) return;
457		memcpy(d->bootipl, bootipl, bootipl_size);
458	}
459
460	if (bootmenu_size % d->sector_size != 0)
461		return;
462	if (d->bootmenu)
463		free(d->bootmenu);
464	if (!bootmenu) {
465		d->bootmenu = NULL;
466	} else {
467		d->bootmenu_size = bootmenu_size;
468		d->bootmenu = malloc(bootmenu_size);
469		if(!d->bootmenu) return;
470		memcpy(d->bootmenu, bootmenu, bootmenu_size);
471	}
472#else
473	if (s % d->sector_size != 0)
474		return;
475	if (d->bootmgr)
476		free(d->bootmgr);
477	if (!b) {
478		d->bootmgr = NULL;
479	} else {
480		d->bootmgr_size = s;
481		d->bootmgr = malloc(s);
482		if(!d->bootmgr) return;
483		memcpy(d->bootmgr, b, s);
484	}
485#endif
486#endif
487}
488
489int
490Set_Boot_Blocks(struct disk *d, const u_char *b1, const u_char *b2)
491{
492#if defined(__i386__)
493	if (d->boot1) free(d->boot1);
494	d->boot1 = malloc(512);
495	if(!d->boot1) return -1;
496	memcpy(d->boot1, b1, 512);
497	if (d->boot2) free(d->boot2);
498	d->boot2 = malloc(15 * 512);
499	if(!d->boot2) return -1;
500	memcpy(d->boot2, b2, 15 * 512);
501#elif defined(__alpha__)
502	if (d->boot1) free(d->boot1);
503	d->boot1 = malloc(15 * 512);
504	if(!d->boot1) return -1;
505	memcpy(d->boot1, b1, 15 * 512);
506#elif defined(__sparc64__)
507	if (d->boot1 != NULL)
508		free(d->boot1);
509	d->boot1 = malloc(16 * 512);
510	if (d->boot1 == NULL)
511		return (-1);
512	memcpy(d->boot1, b1, 16 * 512);
513#elif defined(__ia64__)
514	/* nothing */
515#else
516/* Should be: #error "Set_Boot_Blocks: unknown arch"; */
517#endif
518	return 0;
519}
520
521#ifdef PC98
522const char *
523slice_type_name( int type, int subtype )
524{
525
526	switch (type) {
527	case whole:
528		return "whole";
529	case fat:
530		return "fat";
531	case freebsd:
532		switch (subtype) {
533		case 0xc494:	return "freebsd";
534		default:	return "unknown";
535		}
536	case unused:
537		return "unused";
538	default:
539		return "unknown";
540	}
541}
542#else /* PC98 */
543const char *
544slice_type_name( int type, int subtype )
545{
546
547	switch (type) {
548	case whole:
549		return "whole";
550	case mbr:
551		switch (subtype) {
552		case 1:		return "fat (12-bit)";
553		case 2:		return "XENIX /";
554		case 3:		return "XENIX /usr";
555		case 4:         return "fat (16-bit,<=32Mb)";
556		case 5:		return "extended DOS";
557		case 6:         return "fat (16-bit,>32Mb)";
558		case 7:         return "NTFS/HPFS/QNX";
559		case 8:         return "AIX bootable";
560		case 9:         return "AIX data";
561		case 10:	return "OS/2 bootmgr";
562		case 11:        return "fat (32-bit)";
563		case 12:        return "fat (32-bit,LBA)";
564		case 14:        return "fat (16-bit,>32Mb,LBA)";
565		case 15:        return "extended DOS, LBA";
566		case 18:        return "Compaq Diagnostic";
567		case 84:	return "OnTrack diskmgr";
568		case 100:	return "Netware 2.x";
569		case 101:	return "Netware 3.x";
570		case 115:	return "SCO UnixWare";
571		case 128:	return "Minix 1.1";
572		case 129:	return "Minix 1.5";
573		case 130:	return "linux_swap";
574		case 131:	return "ext2fs";
575		case 166:	return "OpenBSD FFS";	/* 0xA6 */
576		case 169:	return "NetBSD FFS";	/* 0xA9 */
577		case 182:	return "OpenBSD";	/* dedicated */
578		case 183:	return "bsd/os";
579		case 184:	return "bsd/os swap";
580		case 238:	return "EFI GPT";
581		case 239:	return "EFI Sys. Part.";
582		default:	return "unknown";
583		}
584	case fat:
585		return "fat";
586	case freebsd:
587		switch (subtype) {
588		case 165:	return "freebsd";
589		default:	return "unknown";
590		}
591	case extended:
592		return "extended";
593	case part:
594		return "part";
595	case efi:
596		return "efi";
597	case unused:
598		return "unused";
599	default:
600		return "unknown";
601	}
602}
603#endif /* PC98 */
604