disk.c revision 106761
1351290Sdim/*
2351290Sdim * ----------------------------------------------------------------------------
3351290Sdim * "THE BEER-WARE LICENSE" (Revision 42):
4351290Sdim * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
5351290Sdim * can do whatever you want with this stuff. If we meet some day, and you think
6351290Sdim * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7351290Sdim * ----------------------------------------------------------------------------
8351290Sdim */
9351290Sdim
10351290Sdim#include <sys/cdefs.h>
11351290Sdim__FBSDID("$FreeBSD: head/lib/libdisk/disk.c 106761 2002-11-11 10:08:46Z phk $");
12351290Sdim
13351290Sdim#include <stdio.h>
14351290Sdim#include <stdlib.h>
15351290Sdim#include <unistd.h>
16351290Sdim#include <fcntl.h>
17351290Sdim#include <string.h>
18351290Sdim#include <inttypes.h>
19351290Sdim#include <err.h>
20351290Sdim#include <sys/sysctl.h>
21351290Sdim#include <sys/stdint.h>
22351290Sdim#include <sys/types.h>
23351290Sdim#include <sys/stat.h>
24351290Sdim#include <sys/ioctl.h>
25351290Sdim#include <sys/disklabel.h>
26351290Sdim#include <sys/diskslice.h>
27351290Sdim#include <sys/uuid.h>
28351290Sdim#include <sys/gpt.h>
29351290Sdim#include <paths.h>
30351290Sdim#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			default:
282				i = Add_Chunk(d, off, len, n, mbr, ty, 0, 0);
283				break;
284			}
285		} else if (!strcmp(t, "BSD"))
286			i = Add_Chunk(d, off, len, n, part, 0, 0, 0);
287		else if (!strcmp(t, "PC98")) {
288			switch (ty & 0x7f) {
289			case 0x14:
290				i = Add_Chunk(d, off, len, n, freebsd, ty, 0, 0);
291				break;
292			case 0x20:
293			case 0x21:
294			case 0x22:
295			case 0x23:
296			case 0x24:
297				i = Add_Chunk(d, off, len, n, fat, ty, 0, 0);
298				break;
299			default:
300				i = Add_Chunk(d, off, len, n, pc98, ty, 0, 0);
301				break;
302			}
303		} else if (!strcmp(t, "GPT"))
304			i = Add_Chunk(d, off, len, n, ty, 0, 0, 0);
305		else
306			{ printf("BARF %d\n", __LINE__); exit(0); }
307	}
308	/* PLATFORM POLICY BEGIN ------------------------------------- */
309	/* We have a chance to do things on a blank disk here */
310	if (platform == p_sparc64 && d->chunks->part->part == NULL) {
311		hd = d->bios_hd;
312		sc = d->bios_sect;
313		o = d->chunks->size / (hd * sc);
314		o *= (hd * sc);
315		o -= 2 * hd * sc;
316		if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-"))
317			DPRINT(("Failed to add 'freebsd' chunk"));
318	}
319	/* PLATFORM POLICY END --------------------------------------- */
320
321	return (d);
322	i = 0;
323}
324
325void
326Debug_Disk(struct disk *d)
327{
328	printf("Debug_Disk(%s)", d->name);
329#if 0
330	printf("  real_geom=%lu/%lu/%lu", d->real_cyl, d->real_hd, d->real_sect);
331#endif
332	printf("  bios_geom=%lu/%lu/%lu = %lu\n",
333		d->bios_cyl, d->bios_hd, d->bios_sect,
334		d->bios_cyl * d->bios_hd * d->bios_sect);
335#if defined(PC98)
336	printf("  boot1=%p, boot2=%p, bootipl=%p, bootmenu=%p\n",
337		d->boot1, d->boot2, d->bootipl, d->bootmenu);
338#elif defined(__i386__)
339	printf("  boot1=%p, boot2=%p, bootmgr=%p\n",
340		d->boot1, d->boot2, d->bootmgr);
341#elif defined(__alpha__)
342	printf("  boot1=%p, bootmgr=%p\n",
343		d->boot1, d->bootmgr);
344#elif defined(__ia64__)
345	printf("\n");
346#else
347/* Should be: error "Debug_Disk: unknown arch"; */
348#endif
349	Debug_Chunk(d->chunks);
350}
351
352void
353Free_Disk(struct disk *d)
354{
355	if(d->chunks) Free_Chunk(d->chunks);
356	if(d->name) free(d->name);
357#ifdef PC98
358	if(d->bootipl) free(d->bootipl);
359	if(d->bootmenu) free(d->bootmenu);
360#else
361#if !defined(__ia64__)
362	if(d->bootmgr) free(d->bootmgr);
363#endif
364#endif
365#if !defined(__ia64__)
366	if(d->boot1) free(d->boot1);
367#endif
368#if defined(__i386__)
369	if(d->boot2) free(d->boot2);
370#endif
371	free(d);
372}
373
374#if 0
375void
376Collapse_Disk(struct disk *d)
377{
378
379	while(Collapse_Chunk(d, d->chunks))
380		;
381}
382#endif
383
384static int
385qstrcmp(const void* a, const void* b)
386{
387
388	char *str1 = *(char**)a;
389	char *str2 = *(char**)b;
390	return strcmp(str1, str2);
391}
392
393char **
394Disk_Names()
395{
396	int disk_cnt;
397	static char **disks;
398	int error;
399	size_t listsize;
400	char *disklist;
401
402	error = sysctlbyname("kern.disks", NULL, &listsize, NULL, 0);
403	if (error) {
404		warn("kern.disks sysctl not available");
405		return NULL;
406	}
407
408	disks = malloc(sizeof *disks * (1 + MAX_NO_DISKS));
409	if (disks == NULL)
410		return NULL;
411	disklist = (char *)malloc(listsize + 1);
412	if (disklist == NULL) {
413		free(disks);
414		return NULL;
415	}
416	memset(disks,0,sizeof *disks * (1 + MAX_NO_DISKS));
417	memset(disklist, 0, listsize + 1);
418	error = sysctlbyname("kern.disks", disklist, &listsize, NULL, 0);
419	if (error) {
420		free(disklist);
421		free(disks);
422		return NULL;
423	}
424	for (disk_cnt = 0; disk_cnt < MAX_NO_DISKS; disk_cnt++) {
425		disks[disk_cnt] = strsep(&disklist, " ");
426		if (disks[disk_cnt] == NULL)
427			break;
428											}
429	qsort(disks, disk_cnt, sizeof(char*), qstrcmp);
430	return disks;
431}
432
433#ifdef PC98
434void
435Set_Boot_Mgr(struct disk *d, const u_char *bootipl, const size_t bootipl_size,
436	const u_char *bootmenu, const size_t bootmenu_size)
437#else
438void
439Set_Boot_Mgr(struct disk *d, const u_char *b, const size_t s)
440#endif
441{
442#if !defined(__ia64__)
443#ifdef PC98
444	if (bootipl_size % d->sector_size != 0)
445		return;
446	if (d->bootipl)
447		free(d->bootipl);
448	if (!bootipl) {
449		d->bootipl = NULL;
450	} else {
451		d->bootipl_size = bootipl_size;
452		d->bootipl = malloc(bootipl_size);
453		if(!d->bootipl) return;
454		memcpy(d->bootipl, bootipl, bootipl_size);
455	}
456
457	if (bootmenu_size % d->sector_size != 0)
458		return;
459	if (d->bootmenu)
460		free(d->bootmenu);
461	if (!bootmenu) {
462		d->bootmenu = NULL;
463	} else {
464		d->bootmenu_size = bootmenu_size;
465		d->bootmenu = malloc(bootmenu_size);
466		if(!d->bootmenu) return;
467		memcpy(d->bootmenu, bootmenu, bootmenu_size);
468	}
469#else
470	if (s % d->sector_size != 0)
471		return;
472	if (d->bootmgr)
473		free(d->bootmgr);
474	if (!b) {
475		d->bootmgr = NULL;
476	} else {
477		d->bootmgr_size = s;
478		d->bootmgr = malloc(s);
479		if(!d->bootmgr) return;
480		memcpy(d->bootmgr, b, s);
481	}
482#endif
483#endif
484}
485
486int
487Set_Boot_Blocks(struct disk *d, const u_char *b1, const u_char *b2)
488{
489#if defined(__i386__)
490	if (d->boot1) free(d->boot1);
491	d->boot1 = malloc(512);
492	if(!d->boot1) return -1;
493	memcpy(d->boot1, b1, 512);
494	if (d->boot2) free(d->boot2);
495	d->boot2 = malloc(15 * 512);
496	if(!d->boot2) return -1;
497	memcpy(d->boot2, b2, 15 * 512);
498#elif defined(__alpha__)
499	if (d->boot1) free(d->boot1);
500	d->boot1 = malloc(15 * 512);
501	if(!d->boot1) return -1;
502	memcpy(d->boot1, b1, 15 * 512);
503#elif defined(__sparc64__)
504	if (d->boot1 != NULL)
505		free(d->boot1);
506	d->boot1 = malloc(16 * 512);
507	if (d->boot1 == NULL)
508		return (-1);
509	memcpy(d->boot1, b1, 16 * 512);
510#elif defined(__ia64__)
511	/* nothing */
512#else
513/* Should be: #error "Set_Boot_Blocks: unknown arch"; */
514#endif
515	return 0;
516}
517
518#ifdef PC98
519const char *
520slice_type_name( int type, int subtype )
521{
522
523	switch (type) {
524	case whole:
525		return "whole";
526	case fat:
527		return "fat";
528	case freebsd:
529		switch (subtype) {
530		case 0xc494:	return "freebsd";
531		default:	return "unknown";
532		}
533	case unused:
534		return "unused";
535	default:
536		return "unknown";
537	}
538}
539#else /* PC98 */
540const char *
541slice_type_name( int type, int subtype )
542{
543
544	switch (type) {
545	case whole:
546		return "whole";
547	case mbr:
548		switch (subtype) {
549		case 1:		return "fat (12-bit)";
550		case 2:		return "XENIX /";
551		case 3:		return "XENIX /usr";
552		case 4:         return "fat (16-bit,<=32Mb)";
553		case 5:		return "extended DOS";
554		case 6:         return "fat (16-bit,>32Mb)";
555		case 7:         return "NTFS/HPFS/QNX";
556		case 8:         return "AIX bootable";
557		case 9:         return "AIX data";
558		case 10:	return "OS/2 bootmgr";
559		case 11:        return "fat (32-bit)";
560		case 12:        return "fat (32-bit,LBA)";
561		case 14:        return "fat (16-bit,>32Mb,LBA)";
562		case 15:        return "extended DOS, LBA";
563		case 18:        return "Compaq Diagnostic";
564		case 84:	return "OnTrack diskmgr";
565		case 100:	return "Netware 2.x";
566		case 101:	return "Netware 3.x";
567		case 115:	return "SCO UnixWare";
568		case 128:	return "Minix 1.1";
569		case 129:	return "Minix 1.5";
570		case 130:	return "linux_swap";
571		case 131:	return "ext2fs";
572		case 166:	return "OpenBSD FFS";	/* 0xA6 */
573		case 169:	return "NetBSD FFS";	/* 0xA9 */
574		case 182:	return "OpenBSD";	/* dedicated */
575		case 183:	return "bsd/os";
576		case 184:	return "bsd/os swap";
577		case 238:	return "EFI GPT";
578		case 239:	return "EFI Sys. Part.";
579		default:	return "unknown";
580		}
581	case fat:
582		return "fat";
583	case freebsd:
584		switch (subtype) {
585		case 165:	return "freebsd";
586		default:	return "unknown";
587		}
588	case extended:
589		return "extended";
590	case part:
591		return "part";
592	case efi:
593		return "efi";
594	case unused:
595		return "unused";
596	default:
597		return "unknown";
598	}
599}
600#endif /* PC98 */
601