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