disk.c revision 137512
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 137512 2004-11-10 07:50:16Z phk $");
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/uuid.h>
27#include <sys/gpt.h>
28#include <paths.h>
29#include "libdisk.h"
30
31#include <ctype.h>
32#include <errno.h>
33#include <assert.h>
34#include <uuid.h>
35
36const enum platform platform =
37#if defined (P_DEBUG)
38	P_DEBUG
39#elif defined (PC98)
40	p_pc98
41#elif defined(__i386__)
42	p_i386
43#elif defined(__alpha__)
44	p_alpha
45#elif defined(__sparc64__)
46	p_sparc64
47#elif defined(__ia64__)
48	p_ia64
49#elif defined(__ppc__)
50	p_ppc
51#elif defined(__amd64__)
52	p_amd64
53#else
54	IHAVENOIDEA
55#endif
56	;
57
58const char *
59chunk_name(chunk_e type)
60{
61	switch(type) {
62	case unused:	return ("unused");
63	case mbr:	return ("mbr");
64	case part:	return ("part");
65	case gpt:	return ("gpt");
66	case pc98:	return ("pc98");
67	case sun:	return ("sun");
68	case freebsd:	return ("freebsd");
69	case fat:	return ("fat");
70	case spare:	return ("spare");
71	case efi:	return ("efi");
72	case apple:     return ("apple");
73	default:	return ("??");
74	}
75}
76
77struct disk *
78Open_Disk(const char *name)
79{
80	char *conftxt;
81	size_t txtsize;
82	int error;
83
84	error = sysctlbyname("kern.geom.conftxt", NULL, &txtsize, NULL, 0);
85	if (error) {
86		warn("kern.geom.conftxt sysctl not available, giving up!");
87		return (NULL);
88	}
89	conftxt = malloc(txtsize+1);
90	if (conftxt == NULL) {
91		warn("cannot malloc memory for conftxt");
92		return (NULL);
93	}
94	error = sysctlbyname("kern.geom.conftxt", conftxt, &txtsize, NULL, 0);
95	if (error) {
96		warn("error reading kern.geom.conftxt from the system");
97		free(conftxt);
98		return (NULL);
99	}
100	conftxt[txtsize] = '\0';	/* in case kernel bug is still there */
101
102	return Int_Open_Disk(name, conftxt);
103}
104
105void
106Debug_Disk(struct disk *d)
107{
108
109	printf("Debug_Disk(%s)", d->name);
110
111#ifndef __ia64__
112	printf("  bios_geom=%lu/%lu/%lu = %lu\n",
113		d->bios_cyl, d->bios_hd, d->bios_sect,
114		d->bios_cyl * d->bios_hd * d->bios_sect);
115#if defined(PC98)
116	printf("  boot1=%p, boot2=%p, bootipl=%p, bootmenu=%p\n",
117		d->boot1, d->boot2, d->bootipl, d->bootmenu);
118#elif defined(__i386__) || defined(__amd64__)
119	printf("  boot1=%p, boot2=%p, bootmgr=%p\n",
120		d->boot1, d->boot2, d->bootmgr);
121#elif defined(__alpha__)
122	printf("  boot1=%p, bootmgr=%p\n",
123		d->boot1, d->bootmgr);
124#else
125/* Should be: error "Debug_Disk: unknown arch"; */
126#endif
127#else	/* __ia64__ */
128	printf("  media size=%lu, sector size=%lu\n", d->media_size,
129	    d->sector_size);
130#endif
131
132	Debug_Chunk(d->chunks);
133}
134
135void
136Free_Disk(struct disk *d)
137{
138	if (d->chunks)
139		Free_Chunk(d->chunks);
140	if (d->name)
141		free(d->name);
142#ifdef PC98
143	if (d->bootipl)
144		free(d->bootipl);
145	if (d->bootmenu)
146		free(d->bootmenu);
147#else
148#if !defined(__ia64__)
149	if (d->bootmgr)
150		free(d->bootmgr);
151#endif
152#endif
153#if !defined(__ia64__)
154	if (d->boot1)
155		free(d->boot1);
156#endif
157#if defined(__i386__) || defined(__amd64__)
158	if (d->boot2)
159		free(d->boot2);
160#endif
161	free(d);
162}
163
164#if 0
165void
166Collapse_Disk(struct disk *d)
167{
168
169	while (Collapse_Chunk(d, d->chunks))
170		;
171}
172#endif
173
174static int
175qstrcmp(const void* a, const void* b)
176{
177	const char *str1 = *(char* const*)a;
178	const char *str2 = *(char* const*)b;
179
180	return strcmp(str1, str2);
181}
182
183char **
184Disk_Names()
185{
186	int disk_cnt;
187	static char **disks;
188	int error;
189	size_t listsize;
190	char *disklist;
191
192	error = sysctlbyname("kern.disks", NULL, &listsize, NULL, 0);
193	if (error) {
194		warn("kern.disks sysctl not available");
195		return NULL;
196	}
197
198	if (listsize == 0)
199		return (NULL);
200
201	disks = malloc(sizeof *disks * (1 + MAX_NO_DISKS));
202	if (disks == NULL)
203		return NULL;
204	disklist = (char *)malloc(listsize + 1);
205	if (disklist == NULL) {
206		free(disks);
207		return NULL;
208	}
209	memset(disks,0,sizeof *disks * (1 + MAX_NO_DISKS));
210	memset(disklist, 0, listsize + 1);
211	error = sysctlbyname("kern.disks", disklist, &listsize, NULL, 0);
212	if (error || disklist[0] == 0) {
213		free(disklist);
214		free(disks);
215		return NULL;
216	}
217	for (disk_cnt = 0; disk_cnt < MAX_NO_DISKS; disk_cnt++) {
218		disks[disk_cnt] = strsep(&disklist, " ");
219		if (disks[disk_cnt] == NULL)
220			break;
221	}
222	qsort(disks, disk_cnt, sizeof(char*), qstrcmp);
223	return disks;
224}
225
226#ifdef PC98
227void
228Set_Boot_Mgr(struct disk *d, const u_char *bootipl, const size_t bootipl_size,
229	const u_char *bootmenu, const size_t bootmenu_size)
230#else
231void
232Set_Boot_Mgr(struct disk *d, const u_char *b, const size_t s)
233#endif
234{
235#if !defined(__ia64__)
236#ifdef PC98
237	if (d->sector_size == 0)
238		return;
239	if (bootipl_size % d->sector_size != 0)
240		return;
241	if (d->bootipl)
242		free(d->bootipl);
243	if (!bootipl) {
244		d->bootipl = NULL;
245	} else {
246		d->bootipl_size = bootipl_size;
247		d->bootipl = malloc(bootipl_size);
248		if (!d->bootipl)
249			return;
250		memcpy(d->bootipl, bootipl, bootipl_size);
251	}
252
253	if (bootmenu_size % d->sector_size != 0)
254		return;
255	if (d->bootmenu)
256		free(d->bootmenu);
257	if (!bootmenu) {
258		d->bootmenu = NULL;
259	} else {
260		d->bootmenu_size = bootmenu_size;
261		d->bootmenu = malloc(bootmenu_size);
262		if (!d->bootmenu)
263			return;
264		memcpy(d->bootmenu, bootmenu, bootmenu_size);
265	}
266#else
267	if (d->sector_size == 0)
268		return;
269	if (s % d->sector_size != 0)
270		return;
271	if (d->bootmgr)
272		free(d->bootmgr);
273	if (!b) {
274		d->bootmgr = NULL;
275	} else {
276		d->bootmgr_size = s;
277		d->bootmgr = malloc(s);
278		if (!d->bootmgr)
279			return;
280		memcpy(d->bootmgr, b, s);
281	}
282#endif
283#endif
284}
285
286int
287Set_Boot_Blocks(struct disk *d, const u_char *b1, const u_char *b2)
288{
289#if defined(__i386__) || defined(__amd64__)
290	if (d->boot1)
291		free(d->boot1);
292	d->boot1 = malloc(512);
293	if (!d->boot1)
294		return -1;
295	memcpy(d->boot1, b1, 512);
296	if (d->boot2)
297		free(d->boot2);
298	d->boot2 = malloc(15 * 512);
299	if (!d->boot2)
300		return -1;
301	memcpy(d->boot2, b2, 15 * 512);
302#elif defined(__alpha__)
303	if (d->boot1)
304		free(d->boot1);
305	d->boot1 = malloc(15 * 512);
306	if (!d->boot1)
307		return -1;
308	memcpy(d->boot1, b1, 15 * 512);
309#elif defined(__sparc64__)
310	if (d->boot1 != NULL)
311		free(d->boot1);
312	d->boot1 = malloc(16 * 512);
313	if (d->boot1 == NULL)
314		return (-1);
315	memcpy(d->boot1, b1, 16 * 512);
316#elif defined(__ia64__)
317	/* nothing */
318#else
319/* Should be: #error "Set_Boot_Blocks: unknown arch"; */
320#endif
321	return 0;
322}
323
324#ifdef PC98
325const char *
326slice_type_name( int type, int subtype )
327{
328
329	switch (type) {
330	case whole:
331		return "whole";
332	case fat:
333		return "fat";
334	case freebsd:
335		switch (subtype) {
336		case 0xc494:	return "freebsd";
337		default:	return "unknown";
338		}
339	case unused:
340		return "unused";
341	default:
342		return "unknown";
343	}
344}
345#else /* PC98 */
346const char *
347slice_type_name( int type, int subtype )
348{
349
350	switch (type) {
351	case whole:
352		return "whole";
353	case mbr:
354		switch (subtype) {
355		case 1:		return "fat (12-bit)";
356		case 2:		return "XENIX /";
357		case 3:		return "XENIX /usr";
358		case 4:         return "fat (16-bit,<=32Mb)";
359		case 5:		return "extended DOS";
360		case 6:         return "fat (16-bit,>32Mb)";
361		case 7:         return "NTFS/HPFS/QNX";
362		case 8:         return "AIX bootable";
363		case 9:         return "AIX data";
364		case 10:	return "OS/2 bootmgr";
365		case 11:        return "fat (32-bit)";
366		case 12:        return "fat (32-bit,LBA)";
367		case 14:        return "fat (16-bit,>32Mb,LBA)";
368		case 15:        return "extended DOS, LBA";
369		case 18:        return "Compaq Diagnostic";
370		case 84:	return "OnTrack diskmgr";
371		case 100:	return "Netware 2.x";
372		case 101:	return "Netware 3.x";
373		case 115:	return "SCO UnixWare";
374		case 128:	return "Minix 1.1";
375		case 129:	return "Minix 1.5";
376		case 130:	return "linux_swap";
377		case 131:	return "ext2fs";
378		case 166:	return "OpenBSD FFS";	/* 0xA6 */
379		case 169:	return "NetBSD FFS";	/* 0xA9 */
380		case 182:	return "OpenBSD";	/* dedicated */
381		case 183:	return "bsd/os";
382		case 184:	return "bsd/os swap";
383		case 191:	return "Solaris (new)";
384		case 238:	return "EFI GPT";
385		case 239:	return "EFI Sys. Part.";
386		default:	return "unknown";
387		}
388	case fat:
389		return "fat";
390	case freebsd:
391		switch (subtype) {
392		case 165:	return "freebsd";
393		default:	return "unknown";
394		}
395	case extended:
396		return "extended";
397	case part:
398		return "part";
399	case efi:
400		return "efi";
401	case unused:
402		return "unused";
403	default:
404		return "unknown";
405	}
406}
407#endif /* PC98 */
408