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