]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libdisk/disk.c
correct arg order to strlcpy/strlcat under #ifdef alpha
[FreeBSD/FreeBSD.git] / lib / libdisk / disk.c
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$");
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
36 const char *chunk_n[] = {
37         "whole",
38         "unknown",
39         "fat",
40         "freebsd",
41         "extended",
42         "part",
43         "unused",
44         NULL
45 };
46
47 struct disk *
48 Open_Disk(const char *name)
49 {
50         return Int_Open_Disk(name, 0);
51 }
52
53 #ifndef PC98
54 static u_int32_t
55 Read_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
62 struct disk *
63 Int_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         strlcpy(device, _PATH_DEV, sizeof(device));
80         strlcat(device, name, sizeof(device));
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                 snprintf(sname, sizeof(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                 strlcpy(pname, _PATH_DEV, sizeof(pname));
264                 strlcat(pname, sname, sizeof(pname));
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                         snprintf(pname, sizeof(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                 strlcpy(pname, _PATH_DEV, sizeof(pname));
329                 strlcat(pname, name, sizeof(pname));
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                         snprintf(pname, sizeof(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
383 pc98_mo_done:
384 #endif
385         close(fd);
386         Fixup_Names(d);
387         return d;
388 }
389
390 void
391 Debug_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
414 void
415 Free_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
432 struct disk *
433 Clone_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
476 void
477 Collapse_Disk(struct disk *d)
478 {
479
480         while(Collapse_Chunk(d, d->chunks))
481                 ;
482 }
483 #endif
484
485 #ifdef PC98
486 static char * device_list[] = {"wd", "aacd", "ad", "da", "afd", "fla", "idad", "mlxd", "amrd", "twed", "ar", "fd", 0};
487 #else
488 static char * device_list[] = {"aacd", "ad", "da", "afd", "fla", "idad", "mlxd", "amrd", "twed", "ar", "fd", 0};
489 #endif
490
491 int 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
499 char **
500 Disk_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                         snprintf(diskname, sizeof(diskname), "%s%d",
533                                 device_list[j], i);
534                         snprintf(disk, sizeof(disk), _PATH_DEV"%s", diskname);
535                         if (stat(disk, &st) || !(st.st_mode & S_IFCHR))
536                                 continue;
537                         if ((fd = open(disk, O_RDWR)) == -1)
538                                 continue;
539                         if (ioctl(fd, DIOCGSLICEINFO, &ds) == -1) {
540 #ifdef DEBUG
541                                 warn("DIOCGSLICEINFO %s", disk);
542 #endif
543                                 close(fd);
544                                 continue;
545                         }
546                         close(fd);
547                         disks[disk_cnt++] = strdup(diskname);
548                         if(disk_cnt >= MAX_NO_DISKS)
549                                 break;
550                 }
551         }
552     }
553     qsort(disks, disk_cnt, sizeof(char*), qstrcmp);
554     
555     return disks;
556 }
557
558 #ifdef PC98
559 void
560 Set_Boot_Mgr(struct disk *d, const u_char *bootipl, const size_t bootipl_size,
561              const u_char *bootmenu, const size_t bootmenu_size)
562 #else
563 void
564 Set_Boot_Mgr(struct disk *d, const u_char *b, const size_t s)
565 #endif
566 {
567 #ifdef PC98
568         if (bootipl_size % d->sector_size != 0)
569                 return;
570         if (d->bootipl)
571                 free(d->bootipl);
572         if (!bootipl) {
573                 d->bootipl = NULL;
574         } else {
575                 d->bootipl_size = bootipl_size;
576                 d->bootipl = malloc(bootipl_size);
577                 if(!d->bootipl) return;
578                 memcpy(d->bootipl, bootipl, bootipl_size);
579         }
580
581         if (bootmenu_size % d->sector_size != 0)
582                 return;
583         if (d->bootmenu)
584                 free(d->bootmenu);
585         if (!bootmenu) {
586                 d->bootmenu = NULL;
587         } else {
588                 d->bootmenu_size = bootmenu_size;
589                 d->bootmenu = malloc(bootmenu_size);
590                 if(!d->bootmenu) return;
591                 memcpy(d->bootmenu, bootmenu, bootmenu_size);
592         }
593 #else
594         if (s % d->sector_size != 0)
595                 return;
596         if (d->bootmgr)
597                 free(d->bootmgr);
598         if (!b) {
599                 d->bootmgr = NULL;
600         } else {
601                 d->bootmgr_size = s;
602                 d->bootmgr = malloc(s);
603                 if(!d->bootmgr) return;
604                 memcpy(d->bootmgr, b, s);
605         }
606 #endif
607 }
608
609 int
610 Set_Boot_Blocks(struct disk *d, const u_char *b1, const u_char *b2)
611 {
612 #if defined(__i386__)
613         if (d->boot1) free(d->boot1);
614         d->boot1 = malloc(512);
615         if(!d->boot1) return -1;
616         memcpy(d->boot1, b1, 512);
617         if (d->boot2) free(d->boot2);
618         d->boot2 = malloc(15 * 512);
619         if(!d->boot2) return -1;
620         memcpy(d->boot2, b2, 15 * 512);
621 #elif defined(__alpha__)
622         if (d->boot1) free(d->boot1);
623         d->boot1 = malloc(15 * 512);
624         if(!d->boot1) return -1;
625         memcpy(d->boot1, b1, 15 * 512);
626 #endif
627         return 0;
628 }
629
630 const char *
631 slice_type_name( int type, int subtype )
632 {
633         switch (type) {
634                 case 0:         return "whole";
635 #ifndef PC98
636                 case 1:         switch (subtype) {
637                                         case 1:         return "fat (12-bit)";
638                                         case 2:         return "XENIX /";
639                                         case 3:         return "XENIX /usr";
640                                         case 4:         return "fat (16-bit,<=32Mb)";
641                                         case 5:         return "extended DOS";
642                                         case 6:         return "fat (16-bit,>32Mb)";
643                                         case 7:         return "NTFS/HPFS/QNX";
644                                         case 8:         return "AIX bootable";
645                                         case 9:         return "AIX data";
646                                         case 10:        return "OS/2 bootmgr";
647                                         case 11:        return "fat (32-bit)";
648                                         case 12:        return "fat (32-bit,LBA)";
649                                         case 14:        return "fat (16-bit,>32Mb,LBA)";
650                                         case 15:        return "extended DOS, LBA";
651                                         case 18:        return "Compaq Diagnostic";
652                                         case 84:        return "OnTrack diskmgr";
653                                         case 100:       return "Netware 2.x";
654                                         case 101:       return "Netware 3.x";
655                                         case 115:       return "SCO UnixWare";
656                                         case 128:       return "Minix 1.1";
657                                         case 129:       return "Minix 1.5";
658                                         case 130:       return "linux_swap";
659                                         case 131:       return "ext2fs";
660                                         case 166:       return "OpenBSD FFS";   /* 0xA6 */
661                                         case 169:       return "NetBSD FFS";    /* 0xA9 */
662                                         case 182:       return "OpenBSD";               /* dedicated */
663                                         case 183:       return "bsd/os";
664                                         case 184:       return "bsd/os swap";
665                                         case 238:       return "EFI GPT";
666                                         case 239:       return "EFI Sys. Part.";
667                                         default:        return "unknown";
668                                 }
669 #endif
670                 case 2:         return "fat";
671                 case 3:         switch (subtype) {
672 #ifdef  PC98
673                                         case 0xc494:    return "freebsd";
674 #else
675                                         case 165:       return "freebsd";
676 #endif
677                                         default:        return "unknown";
678                                 }
679 #ifndef PC98
680                 case 4:         return "extended";
681                 case 5:         return "part";
682                 case 6:         return "unused";
683 #endif
684                 default:        return "unknown";
685         }
686 }