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