disk.c revision 104272
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 104272 2002-10-01 07:24:55Z phk $");
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <fcntl.h>
17#include <string.h>
18#include <err.h>
19#include <sys/sysctl.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22#include <sys/ioctl.h>
23#include <sys/disklabel.h>
24#include <sys/diskslice.h>
25#include <sys/diskmbr.h>
26#include <paths.h>
27#include "libdisk.h"
28
29#define DOSPTYP_EXTENDED        5
30#define DOSPTYP_ONTRACK         84
31
32const char *chunk_n[] = {
33	"whole",
34	"unknown",
35	"fat",
36	"freebsd",
37	"extended",
38	"part",
39	"unused",
40	NULL
41};
42
43struct disk *
44Open_Disk(const char *name)
45{
46	return Int_Open_Disk(name, 0);
47}
48
49#ifndef PC98
50static u_int32_t
51Read_Int32(u_int32_t *p)
52{
53    u_int8_t *bp = (u_int8_t *)p;
54    return bp[0] | (bp[1] << 8) | (bp[2] << 16) | (bp[3] << 24);
55}
56#endif
57
58struct disk *
59Int_Open_Disk(const char *name, u_long size)
60{
61	int i,fd;
62	struct diskslices ds;
63	struct disklabel dl;
64	char device[64], *buf;
65	struct disk *d;
66	u_long sector_size;
67#ifdef PC98
68	unsigned char *p;
69#else
70	struct dos_partition *dp;
71	void *p;
72#endif
73	u_long offset = 0;
74
75	strcpy(device, _PATH_DEV);
76	strcat(device, name);
77
78	d = (struct disk *)malloc(sizeof *d);
79	if(!d) return NULL;
80	memset(d, 0, sizeof *d);
81
82	fd = open(device, O_RDONLY);
83	if (fd < 0) {
84#ifdef DEBUG
85		warn("open(%s) failed", device);
86#endif
87		return 0;
88	}
89
90	memset(&dl, 0, sizeof dl);
91	ioctl(fd, DIOCGDINFO, &dl);
92	i = ioctl(fd, DIOCGSLICEINFO, &ds);
93	if (i < 0) {
94#ifdef DEBUG
95		warn("DIOCGSLICEINFO(%s) failed", device);
96#endif
97		close(fd);
98		return 0;
99	}
100
101#ifdef DEBUG
102	for(i = 0; i < ds.dss_nslices; i++)
103		if(ds.dss_slices[i].ds_openmask)
104			printf("  open(%d)=0x%2x",
105				i, ds.dss_slices[i].ds_openmask);
106	printf("\n");
107#endif
108
109/* XXX --- ds.dss_slice[WHOLE_DISK_SLICE].ds.size of MO disk is wrong!!! */
110#ifdef PC98
111	if (!size)
112		size = dl.d_ncylinders * dl.d_ntracks * dl.d_nsectors;
113#else
114	if (!size)
115		size = ds.dss_slices[WHOLE_DISK_SLICE].ds_size;
116#endif
117
118	/* determine media sector size */
119	if ((buf = malloc(MAX_SEC_SIZE)) == NULL)
120		return NULL;
121	for (sector_size = MIN_SEC_SIZE; sector_size <= MAX_SEC_SIZE; sector_size *= 2) {
122		if (read(fd, buf, sector_size) == sector_size) {
123			d->sector_size = sector_size;
124			break;
125		}
126	}
127	free (buf);
128	if (sector_size > MAX_SEC_SIZE)
129		return NULL; /* could not determine sector size */
130
131#ifdef PC98
132	p = (unsigned char*)read_block(fd, 1, sector_size);
133#else
134	p = read_block(fd, 0, sector_size);
135	dp = (struct dos_partition*)(p + DOSPARTOFF);
136	for (i = 0; i < NDOSPART; i++) {
137		if (Read_Int32(&dp->dp_start) >= size)
138		    continue;
139		if (Read_Int32(&dp->dp_start) + Read_Int32(&dp->dp_size) >= size)
140		    continue;
141		if (!Read_Int32(&dp->dp_size))
142		    continue;
143
144		if (dp->dp_typ == DOSPTYP_ONTRACK) {
145			d->flags |= DISK_ON_TRACK;
146			offset = 63;
147		}
148
149	}
150	free(p);
151#endif
152
153	d->bios_sect = dl.d_nsectors;
154	d->bios_hd = dl.d_ntracks;
155
156	d->name = strdup(name);
157
158
159	if (dl.d_ntracks && dl.d_nsectors)
160		d->bios_cyl = size / (dl.d_ntracks * dl.d_nsectors);
161
162#ifdef PC98
163	if (Add_Chunk(d, -offset, size, name, whole, 0, 0, "-"))
164#else
165	if (Add_Chunk(d, -offset, size, name, whole, 0, 0))
166#endif
167#ifdef DEBUG
168		warn("Failed to add 'whole' chunk");
169#else
170		{}
171#endif
172
173#ifdef __i386__
174#ifdef PC98
175	/* XXX -- Quick Hack!
176	 * Check MS-DOS MO
177	 */
178	if ((*p == 0xf0 || *p == 0xf8) &&
179	    (*(p+1) == 0xff) &&
180	    (*(p+2) == 0xff)) {
181		Add_Chunk(d, 0, size, name, fat, 0xa0a0, 0, name);
182	    free(p);
183	    goto pc98_mo_done;
184	}
185	free(p);
186#endif /* PC98 */
187	for(i=BASE_SLICE;i<ds.dss_nslices;i++) {
188		char sname[20];
189		chunk_e ce;
190		u_long flags=0;
191		int subtype=0;
192
193		if (! ds.dss_slices[i].ds_size)
194			continue;
195		ds.dss_slices[i].ds_offset -= offset;
196		sprintf(sname, "%ss%d", name, i - 1);
197#ifdef PC98
198		subtype = ds.dss_slices[i].ds_type |
199			ds.dss_slices[i].ds_subtype << 8;
200		switch (ds.dss_slices[i].ds_type & 0x7f) {
201			case 0x14:
202				ce = freebsd;
203				break;
204			case 0x20:
205			case 0x21:
206			case 0x22:
207			case 0x23:
208			case 0x24:
209				ce = fat;
210				break;
211#else /* IBM-PC */
212		subtype = ds.dss_slices[i].ds_type;
213		switch (ds.dss_slices[i].ds_type) {
214			case 0xa5:
215				ce = freebsd;
216				break;
217			case 0x1:
218			case 0x6:
219			case 0x4:
220			case 0xb:
221			case 0xc:
222			case 0xe:
223				ce = fat;
224				break;
225			case DOSPTYP_EXTENDED:
226			case 0xf:
227				ce = extended;
228				break;
229#endif
230			default:
231				ce = unknown;
232				break;
233		}
234#ifdef PC98
235		if (Add_Chunk(d, ds.dss_slices[i].ds_offset,
236			ds.dss_slices[i].ds_size, sname, ce, subtype, flags,
237			ds.dss_slices[i].ds_name))
238#else
239		if (Add_Chunk(d, ds.dss_slices[i].ds_offset,
240			ds.dss_slices[i].ds_size, sname, ce, subtype, flags))
241#endif
242#ifdef DEBUG
243			warn("failed to add chunk for slice %d", i - 1);
244#else
245			{}
246#endif
247
248#ifdef PC98
249		if ((ds.dss_slices[i].ds_type & 0x7f) != 0x14)
250#else
251		if (ds.dss_slices[i].ds_type != 0xa5)
252#endif
253			continue;
254		{
255		struct disklabel dl;
256		char pname[20];
257		int j, k;
258
259		strcpy(pname, _PATH_DEV);
260		strcat(pname, sname);
261		j = open(pname, O_RDONLY);
262		if (j < 0) {
263#ifdef DEBUG
264			warn("open(%s)", pname);
265#endif
266			continue;
267		}
268		k = ioctl(j, DIOCGDINFO, &dl);
269		if (k < 0) {
270#ifdef DEBUG
271			warn("ioctl(%s, DIOCGDINFO)", pname);
272#endif
273			close(j);
274			continue;
275		}
276		close(j);
277
278		for(j = 0; j <= dl.d_npartitions; j++) {
279			if (j == RAW_PART)
280				continue;
281			if (j == 3)
282				continue;
283			if (j == dl.d_npartitions) {
284				j = 3;
285				dl.d_npartitions = 0;
286			}
287			if (!dl.d_partitions[j].p_size)
288				continue;
289			if (dl.d_partitions[j].p_size +
290			    dl.d_partitions[j].p_offset >
291			    ds.dss_slices[i].ds_size)
292				continue;
293			sprintf(pname, "%s%c", sname, j + 'a');
294			if (Add_Chunk(d,
295				dl.d_partitions[j].p_offset +
296				ds.dss_slices[i].ds_offset,
297				dl.d_partitions[j].p_size,
298				pname,part,
299				dl.d_partitions[j].p_fstype,
300#ifdef PC98
301				0,
302				ds.dss_slices[i].ds_name) && j != 3)
303#else
304				0) && j != 3)
305#endif
306#ifdef DEBUG
307				warn(
308			"Failed to add chunk for partition %c [%lu,%lu]",
309			j + 'a', dl.d_partitions[j].p_offset,
310			dl.d_partitions[j].p_size);
311#else
312				{}
313#endif
314		}
315		}
316	}
317#endif /* __i386__ */
318#ifdef __alpha__
319	{
320		struct disklabel dl;
321		char pname[20];
322		int j,k;
323
324		strcpy(pname, _PATH_DEV);
325		strcat(pname, name);
326		j = open(pname, O_RDONLY);
327		if (j < 0) {
328#ifdef DEBUG
329			warn("open(%s)", pname);
330#endif
331			goto nolabel;
332		}
333		k = ioctl(j, DIOCGDINFO, &dl);
334		if (k < 0) {
335#ifdef DEBUG
336			warn("ioctl(%s, DIOCGDINFO)", pname);
337#endif
338			close(j);
339			goto nolabel;
340		}
341		close(j);
342		All_FreeBSD(d, 1);
343
344		for(j = 0; j <= dl.d_npartitions; j++) {
345			if (j == RAW_PART)
346				continue;
347			if (j == 3)
348				continue;
349			if (j == dl.d_npartitions) {
350				j = 3;
351				dl.d_npartitions = 0;
352			}
353			if (!dl.d_partitions[j].p_size)
354				continue;
355			if (dl.d_partitions[j].p_size +
356			    dl.d_partitions[j].p_offset >
357			    ds.dss_slices[WHOLE_DISK_SLICE].ds_size)
358				continue;
359			sprintf(pname, "%s%c", name, j + 'a');
360			if (Add_Chunk(d,
361				      dl.d_partitions[j].p_offset,
362				      dl.d_partitions[j].p_size,
363				      pname,part,
364				      dl.d_partitions[j].p_fstype,
365				      0) && j != 3)
366#ifdef DEBUG
367				warn(
368					"Failed to add chunk for partition %c [%lu,%lu]",
369					j + 'a', dl.d_partitions[j].p_offset,
370					dl.d_partitions[j].p_size);
371#else
372			{}
373#endif
374		}
375	nolabel:;
376	}
377#endif /* __alpha__ */
378#ifdef PC98
379pc98_mo_done:
380#endif
381	close(fd);
382	Fixup_Names(d);
383	return d;
384}
385
386void
387Debug_Disk(struct disk *d)
388{
389	printf("Debug_Disk(%s)", d->name);
390	printf("  flags=%lx", d->flags);
391#if 0
392	printf("  real_geom=%lu/%lu/%lu", d->real_cyl, d->real_hd, d->real_sect);
393#endif
394	printf("  bios_geom=%lu/%lu/%lu = %lu\n",
395		d->bios_cyl, d->bios_hd, d->bios_sect,
396		d->bios_cyl * d->bios_hd * d->bios_sect);
397#if defined(PC98)
398	printf("  boot1=%p, boot2=%p, bootipl=%p, bootmenu=%p\n",
399		d->boot1, d->boot2, d->bootipl, d->bootmenu);
400#elif defined(__i386__)
401	printf("  boot1=%p, boot2=%p, bootmgr=%p\n",
402		d->boot1, d->boot2, d->bootmgr);
403#elif defined(__alpha__)
404	printf("  boot1=%p, bootmgr=%p\n",
405		d->boot1, d->bootmgr);
406#endif
407	Debug_Chunk(d->chunks);
408}
409
410void
411Free_Disk(struct disk *d)
412{
413	if(d->chunks) Free_Chunk(d->chunks);
414	if(d->name) free(d->name);
415#ifdef PC98
416	if(d->bootipl) free(d->bootipl);
417	if(d->bootmenu) free(d->bootmenu);
418#else
419	if(d->bootmgr) free(d->bootmgr);
420#endif
421	if(d->boot1) free(d->boot1);
422#if defined(__i386__)
423	if(d->boot2) free(d->boot2);
424#endif
425	free(d);
426}
427
428struct disk *
429Clone_Disk(struct disk *d)
430{
431	struct disk *d2;
432
433	d2 = (struct disk*) malloc(sizeof *d2);
434	if(!d2) return NULL;
435	*d2 = *d;
436	d2->name = strdup(d2->name);
437	d2->chunks = Clone_Chunk(d2->chunks);
438#ifdef PC98
439	if(d2->bootipl) {
440		d2->bootipl = malloc(d2->bootipl_size);
441		memcpy(d2->bootipl, d->bootipl, d2->bootipl_size);
442	}
443	if(d2->bootmenu) {
444		d2->bootmenu = malloc(d2->bootmenu_size);
445		memcpy(d2->bootmenu, d->bootmenu, d2->bootmenu_size);
446	}
447#else
448	if(d2->bootmgr) {
449		d2->bootmgr = malloc(d2->bootmgr_size);
450		memcpy(d2->bootmgr, d->bootmgr, d2->bootmgr_size);
451	}
452#endif
453#if defined(__i386__)
454	if(d2->boot1) {
455		d2->boot1 = malloc(512);
456		memcpy(d2->boot1, d->boot1, 512);
457	}
458	if(d2->boot2) {
459		d2->boot2 = malloc(512 * 15);
460		memcpy(d2->boot2, d->boot2, 512 * 15);
461	}
462#elif defined(__alpha__)
463	if(d2->boot1) {
464		d2->boot1 = malloc(512 * 15);
465		memcpy(d2->boot1, d->boot1, 512 * 15);
466	}
467#endif
468	return d2;
469}
470
471#if 0
472void
473Collapse_Disk(struct disk *d)
474{
475
476	while(Collapse_Chunk(d, d->chunks))
477		;
478}
479#endif
480
481#ifdef PC98
482static char * device_list[] = {"wd", "aacd", "ad", "da", "afd", "fla", "idad", "mlxd", "amrd", "twed", "ar", "fd", 0};
483#else
484static char * device_list[] = {"aacd", "ad", "da", "afd", "fla", "idad", "mlxd", "amrd", "twed", "ar", "fd", 0};
485#endif
486
487int qstrcmp(const void* a, const void* b) {
488
489	char *str1 = *(char**)a;
490	char *str2 = *(char**)b;
491	return strcmp(str1, str2);
492
493}
494
495char **
496Disk_Names()
497{
498    int i,j,disk_cnt;
499    char disk[25];
500    char diskname[25];
501    struct stat st;
502    struct diskslices ds;
503    int fd;
504    static char **disks;
505    int error;
506    size_t listsize;
507    char *disklist, **dp;
508
509    disks = malloc(sizeof *disks * (1 + MAX_NO_DISKS));
510    memset(disks,0,sizeof *disks * (1 + MAX_NO_DISKS));
511    error = sysctlbyname("kern.disks", NULL, &listsize, NULL, 0);
512    if (!error) {
513	    disklist = (char *)malloc(listsize);
514	    error = sysctlbyname("kern.disks", disklist, &listsize, NULL, 0);
515	    if (error)
516		    return NULL;
517	    disk_cnt = 0;
518	    for (dp = disks; ((*dp = strsep(&disklist, " ")) != NULL) &&
519			 disk_cnt < MAX_NO_DISKS; disk_cnt++, dp++);
520    } else {
521    warn("kern.disks sysctl not available");
522    disk_cnt = 0;
523	for (j = 0; device_list[j]; j++) {
524		if(disk_cnt >= MAX_NO_DISKS)
525			break;
526		for (i = 0; i < MAX_NO_DISKS; i++) {
527			sprintf(diskname, "%s%d", device_list[j], i);
528			sprintf(disk, _PATH_DEV"%s", diskname);
529			if (stat(disk, &st) || !(st.st_mode & S_IFCHR))
530				continue;
531			if ((fd = open(disk, O_RDWR)) == -1)
532				continue;
533			if (ioctl(fd, DIOCGSLICEINFO, &ds) == -1) {
534#ifdef DEBUG
535				warn("DIOCGSLICEINFO %s", disk);
536#endif
537				close(fd);
538				continue;
539			}
540			close(fd);
541			disks[disk_cnt++] = strdup(diskname);
542			if(disk_cnt >= MAX_NO_DISKS)
543				break;
544		}
545	}
546    }
547    qsort(disks, disk_cnt, sizeof(char*), qstrcmp);
548
549    return disks;
550}
551
552#ifdef PC98
553void
554Set_Boot_Mgr(struct disk *d, const u_char *bootipl, const size_t bootipl_size,
555	     const u_char *bootmenu, const size_t bootmenu_size)
556#else
557void
558Set_Boot_Mgr(struct disk *d, const u_char *b, const size_t s)
559#endif
560{
561#ifdef PC98
562	if (bootipl_size % d->sector_size != 0)
563		return;
564	if (d->bootipl)
565		free(d->bootipl);
566	if (!bootipl) {
567		d->bootipl = NULL;
568	} else {
569		d->bootipl_size = bootipl_size;
570		d->bootipl = malloc(bootipl_size);
571		if(!d->bootipl) return;
572		memcpy(d->bootipl, bootipl, bootipl_size);
573	}
574
575	if (bootmenu_size % d->sector_size != 0)
576		return;
577	if (d->bootmenu)
578		free(d->bootmenu);
579	if (!bootmenu) {
580		d->bootmenu = NULL;
581	} else {
582		d->bootmenu_size = bootmenu_size;
583		d->bootmenu = malloc(bootmenu_size);
584		if(!d->bootmenu) return;
585		memcpy(d->bootmenu, bootmenu, bootmenu_size);
586	}
587#else
588	if (s % d->sector_size != 0)
589		return;
590	if (d->bootmgr)
591		free(d->bootmgr);
592	if (!b) {
593		d->bootmgr = NULL;
594	} else {
595		d->bootmgr_size = s;
596		d->bootmgr = malloc(s);
597		if(!d->bootmgr) return;
598		memcpy(d->bootmgr, b, s);
599	}
600#endif
601}
602
603int
604Set_Boot_Blocks(struct disk *d, const u_char *b1, const u_char *b2)
605{
606#if defined(__i386__)
607	if (d->boot1) free(d->boot1);
608	d->boot1 = malloc(512);
609	if(!d->boot1) return -1;
610	memcpy(d->boot1, b1, 512);
611	if (d->boot2) free(d->boot2);
612	d->boot2 = malloc(15 * 512);
613	if(!d->boot2) return -1;
614	memcpy(d->boot2, b2, 15 * 512);
615#elif defined(__alpha__)
616	if (d->boot1) free(d->boot1);
617	d->boot1 = malloc(15 * 512);
618	if(!d->boot1) return -1;
619	memcpy(d->boot1, b1, 15 * 512);
620#endif
621	return 0;
622}
623
624const char *
625slice_type_name( int type, int subtype )
626{
627	switch (type) {
628		case 0:		return "whole";
629#ifndef	PC98
630		case 1:		switch (subtype) {
631					case 1:		return "fat (12-bit)";
632					case 2:		return "XENIX /";
633					case 3:		return "XENIX /usr";
634					case 4:         return "fat (16-bit,<=32Mb)";
635					case 5:		return "extended DOS";
636					case 6:         return "fat (16-bit,>32Mb)";
637					case 7:         return "NTFS/HPFS/QNX";
638					case 8:         return "AIX bootable";
639					case 9:         return "AIX data";
640					case 10:	return "OS/2 bootmgr";
641					case 11:        return "fat (32-bit)";
642					case 12:        return "fat (32-bit,LBA)";
643					case 14:        return "fat (16-bit,>32Mb,LBA)";
644					case 15:        return "extended DOS, LBA";
645					case 18:        return "Compaq Diagnostic";
646					case 84:	return "OnTrack diskmgr";
647					case 100:	return "Netware 2.x";
648					case 101:	return "Netware 3.x";
649					case 115:	return "SCO UnixWare";
650					case 128:	return "Minix 1.1";
651					case 129:	return "Minix 1.5";
652					case 130:	return "linux_swap";
653					case 131:	return "ext2fs";
654					case 166:	return "OpenBSD FFS";	/* 0xA6 */
655					case 169:	return "NetBSD FFS";	/* 0xA9 */
656					case 182:	return "OpenBSD";		/* dedicated */
657					case 183:	return "bsd/os";
658					case 184:	return "bsd/os swap";
659					case 238:	return "EFI GPT";
660					case 239:	return "EFI Sys. Part.";
661					default:	return "unknown";
662				}
663#endif
664		case 2:		return "fat";
665		case 3:		switch (subtype) {
666#ifdef	PC98
667					case 0xc494:	return "freebsd";
668#else
669					case 165:	return "freebsd";
670#endif
671					default:	return "unknown";
672				}
673#ifndef	PC98
674		case 4:		return "extended";
675		case 5:		return "part";
676		case 6:		return "unused";
677#endif
678		default:	return "unknown";
679	}
680}
681