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