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