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