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