disk.c revision 108292
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 108292 2002-12-26 15:50:45Z nyan $");
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, 0, 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 {
337			printf("BARF %d\n", __LINE__);
338			exit(0);
339		}
340	}
341	/* PLATFORM POLICY BEGIN ------------------------------------- */
342	/* We have a chance to do things on a blank disk here */
343	if (platform == p_sparc64 && d->chunks->part->part == NULL) {
344		hd = d->bios_hd;
345		sc = d->bios_sect;
346		o = d->chunks->size / (hd * sc);
347		o *= (hd * sc);
348		o -= 2 * hd * sc;
349		if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-"))
350			DPRINT(("Failed to add 'freebsd' chunk"));
351	}
352	/* PLATFORM POLICY END --------------------------------------- */
353
354	return (d);
355	i = 0;
356}
357
358void
359Debug_Disk(struct disk *d)
360{
361
362	printf("Debug_Disk(%s)", d->name);
363#if 0
364	printf("  real_geom=%lu/%lu/%lu",
365	       d->real_cyl, d->real_hd, d->real_sect);
366#endif
367	printf("  bios_geom=%lu/%lu/%lu = %lu\n",
368		d->bios_cyl, d->bios_hd, d->bios_sect,
369		d->bios_cyl * d->bios_hd * d->bios_sect);
370#if defined(PC98)
371	printf("  boot1=%p, boot2=%p, bootipl=%p, bootmenu=%p\n",
372		d->boot1, d->boot2, d->bootipl, d->bootmenu);
373#elif defined(__i386__)
374	printf("  boot1=%p, boot2=%p, bootmgr=%p\n",
375		d->boot1, d->boot2, d->bootmgr);
376#elif defined(__alpha__)
377	printf("  boot1=%p, bootmgr=%p\n",
378		d->boot1, d->bootmgr);
379#elif defined(__ia64__)
380	printf("\n");
381#else
382/* Should be: error "Debug_Disk: unknown arch"; */
383#endif
384	Debug_Chunk(d->chunks);
385}
386
387void
388Free_Disk(struct disk *d)
389{
390	if (d->chunks)
391		Free_Chunk(d->chunks);
392	if (d->name)
393		free(d->name);
394#ifdef PC98
395	if (d->bootipl)
396		free(d->bootipl);
397	if (d->bootmenu)
398		free(d->bootmenu);
399#else
400#if !defined(__ia64__)
401	if (d->bootmgr)
402		free(d->bootmgr);
403#endif
404#endif
405#if !defined(__ia64__)
406	if (d->boot1)
407		free(d->boot1);
408#endif
409#if defined(__i386__)
410	if (d->boot2)
411		free(d->boot2);
412#endif
413	free(d);
414}
415
416#if 0
417void
418Collapse_Disk(struct disk *d)
419{
420
421	while (Collapse_Chunk(d, d->chunks))
422		;
423}
424#endif
425
426static int
427qstrcmp(const void* a, const void* b)
428{
429	char *str1 = *(char**)a;
430	char *str2 = *(char**)b;
431
432	return strcmp(str1, str2);
433}
434
435char **
436Disk_Names()
437{
438	int disk_cnt;
439	static char **disks;
440	int error;
441	size_t listsize;
442	char *disklist;
443
444	error = sysctlbyname("kern.disks", NULL, &listsize, NULL, 0);
445	if (error) {
446		warn("kern.disks sysctl not available");
447		return NULL;
448	}
449
450	disks = malloc(sizeof *disks * (1 + MAX_NO_DISKS));
451	if (disks == NULL)
452		return NULL;
453	disklist = (char *)malloc(listsize + 1);
454	if (disklist == NULL) {
455		free(disks);
456		return NULL;
457	}
458	memset(disks,0,sizeof *disks * (1 + MAX_NO_DISKS));
459	memset(disklist, 0, listsize + 1);
460	error = sysctlbyname("kern.disks", disklist, &listsize, NULL, 0);
461	if (error) {
462		free(disklist);
463		free(disks);
464		return NULL;
465	}
466	for (disk_cnt = 0; disk_cnt < MAX_NO_DISKS; disk_cnt++) {
467		disks[disk_cnt] = strsep(&disklist, " ");
468		if (disks[disk_cnt] == NULL)
469			break;
470	}
471	qsort(disks, disk_cnt, sizeof(char*), qstrcmp);
472	return disks;
473}
474
475#ifdef PC98
476void
477Set_Boot_Mgr(struct disk *d, const u_char *bootipl, const size_t bootipl_size,
478	const u_char *bootmenu, const size_t bootmenu_size)
479#else
480void
481Set_Boot_Mgr(struct disk *d, const u_char *b, const size_t s)
482#endif
483{
484#if !defined(__ia64__)
485#ifdef PC98
486	if (d->sector_size == 0)
487		return;
488	if (bootipl_size % d->sector_size != 0)
489		return;
490	if (d->bootipl)
491		free(d->bootipl);
492	if (!bootipl) {
493		d->bootipl = NULL;
494	} else {
495		d->bootipl_size = bootipl_size;
496		d->bootipl = malloc(bootipl_size);
497		if (!d->bootipl)
498			return;
499		memcpy(d->bootipl, bootipl, bootipl_size);
500	}
501
502	if (bootmenu_size % d->sector_size != 0)
503		return;
504	if (d->bootmenu)
505		free(d->bootmenu);
506	if (!bootmenu) {
507		d->bootmenu = NULL;
508	} else {
509		d->bootmenu_size = bootmenu_size;
510		d->bootmenu = malloc(bootmenu_size);
511		if (!d->bootmenu)
512			return;
513		memcpy(d->bootmenu, bootmenu, bootmenu_size);
514	}
515#else
516	if (d->sector_size == 0)
517		return;
518	if (s % d->sector_size != 0)
519		return;
520	if (d->bootmgr)
521		free(d->bootmgr);
522	if (!b) {
523		d->bootmgr = NULL;
524	} else {
525		d->bootmgr_size = s;
526		d->bootmgr = malloc(s);
527		if (!d->bootmgr)
528			return;
529		memcpy(d->bootmgr, b, s);
530	}
531#endif
532#endif
533}
534
535int
536Set_Boot_Blocks(struct disk *d, const u_char *b1, const u_char *b2)
537{
538#if defined(__i386__)
539	if (d->boot1)
540		free(d->boot1);
541	d->boot1 = malloc(512);
542	if (!d->boot1)
543		return -1;
544	memcpy(d->boot1, b1, 512);
545	if (d->boot2)
546		free(d->boot2);
547	d->boot2 = malloc(15 * 512);
548	if (!d->boot2)
549		return -1;
550	memcpy(d->boot2, b2, 15 * 512);
551#elif defined(__alpha__)
552	if (d->boot1)
553		free(d->boot1);
554	d->boot1 = malloc(15 * 512);
555	if (!d->boot1)
556		return -1;
557	memcpy(d->boot1, b1, 15 * 512);
558#elif defined(__sparc64__)
559	if (d->boot1 != NULL)
560		free(d->boot1);
561	d->boot1 = malloc(16 * 512);
562	if (d->boot1 == NULL)
563		return (-1);
564	memcpy(d->boot1, b1, 16 * 512);
565#elif defined(__ia64__)
566	/* nothing */
567#else
568/* Should be: #error "Set_Boot_Blocks: unknown arch"; */
569#endif
570	return 0;
571}
572
573#ifdef PC98
574const char *
575slice_type_name( int type, int subtype )
576{
577
578	switch (type) {
579	case whole:
580		return "whole";
581	case fat:
582		return "fat";
583	case freebsd:
584		switch (subtype) {
585		case 0xc494:	return "freebsd";
586		default:	return "unknown";
587		}
588	case unused:
589		return "unused";
590	default:
591		return "unknown";
592	}
593}
594#else /* PC98 */
595const char *
596slice_type_name( int type, int subtype )
597{
598
599	switch (type) {
600	case whole:
601		return "whole";
602	case mbr:
603		switch (subtype) {
604		case 1:		return "fat (12-bit)";
605		case 2:		return "XENIX /";
606		case 3:		return "XENIX /usr";
607		case 4:         return "fat (16-bit,<=32Mb)";
608		case 5:		return "extended DOS";
609		case 6:         return "fat (16-bit,>32Mb)";
610		case 7:         return "NTFS/HPFS/QNX";
611		case 8:         return "AIX bootable";
612		case 9:         return "AIX data";
613		case 10:	return "OS/2 bootmgr";
614		case 11:        return "fat (32-bit)";
615		case 12:        return "fat (32-bit,LBA)";
616		case 14:        return "fat (16-bit,>32Mb,LBA)";
617		case 15:        return "extended DOS, LBA";
618		case 18:        return "Compaq Diagnostic";
619		case 84:	return "OnTrack diskmgr";
620		case 100:	return "Netware 2.x";
621		case 101:	return "Netware 3.x";
622		case 115:	return "SCO UnixWare";
623		case 128:	return "Minix 1.1";
624		case 129:	return "Minix 1.5";
625		case 130:	return "linux_swap";
626		case 131:	return "ext2fs";
627		case 166:	return "OpenBSD FFS";	/* 0xA6 */
628		case 169:	return "NetBSD FFS";	/* 0xA9 */
629		case 182:	return "OpenBSD";	/* dedicated */
630		case 183:	return "bsd/os";
631		case 184:	return "bsd/os swap";
632		case 238:	return "EFI GPT";
633		case 239:	return "EFI Sys. Part.";
634		default:	return "unknown";
635		}
636	case fat:
637		return "fat";
638	case freebsd:
639		switch (subtype) {
640		case 165:	return "freebsd";
641		default:	return "unknown";
642		}
643	case extended:
644		return "extended";
645	case part:
646		return "part";
647	case efi:
648		return "efi";
649	case unused:
650		return "unused";
651	default:
652		return "unknown";
653	}
654}
655#endif /* PC98 */
656