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