disk.c revision 105005
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 105005 2002-10-12 22:01:57Z kris $"); 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 36const char *chunk_n[] = { 37 "whole", 38 "unknown", 39 "fat", 40 "freebsd", 41 "extended", 42 "part", 43 "unused", 44 NULL 45}; 46 47struct disk * 48Open_Disk(const char *name) 49{ 50 return Int_Open_Disk(name, 0); 51} 52 53#ifndef PC98 54static u_int32_t 55Read_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 62struct disk * 63Int_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 strcpy(device, _PATH_DEV); 80 strcat(device, name); 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 sprintf(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 strcpy(pname, _PATH_DEV); 264 strcat(pname, sname); 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 sprintf(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 strcpy(pname, _PATH_DEV); 329 strcat(pname, name); 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 sprintf(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 383pc98_mo_done: 384#endif 385 close(fd); 386 Fixup_Names(d); 387 return d; 388} 389 390void 391Debug_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 414void 415Free_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 432struct disk * 433Clone_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 476void 477Collapse_Disk(struct disk *d) 478{ 479 480 while(Collapse_Chunk(d, d->chunks)) 481 ; 482} 483#endif 484 485#ifdef PC98 486static char * device_list[] = {"wd", "aacd", "ad", "da", "afd", "fla", "idad", "mlxd", "amrd", "twed", "ar", "fd", 0}; 487#else 488static char * device_list[] = {"aacd", "ad", "da", "afd", "fla", "idad", "mlxd", "amrd", "twed", "ar", "fd", 0}; 489#endif 490 491int 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 499char ** 500Disk_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 sprintf(diskname, "%s%d", device_list[j], i); 533 sprintf(disk, _PATH_DEV"%s", diskname); 534 if (stat(disk, &st) || !(st.st_mode & S_IFCHR)) 535 continue; 536 if ((fd = open(disk, O_RDWR)) == -1) 537 continue; 538 if (ioctl(fd, DIOCGSLICEINFO, &ds) == -1) { 539#ifdef DEBUG 540 warn("DIOCGSLICEINFO %s", disk); 541#endif 542 close(fd); 543 continue; 544 } 545 close(fd); 546 disks[disk_cnt++] = strdup(diskname); 547 if(disk_cnt >= MAX_NO_DISKS) 548 break; 549 } 550 } 551 } 552 qsort(disks, disk_cnt, sizeof(char*), qstrcmp); 553 554 return disks; 555} 556 557#ifdef PC98 558void 559Set_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 562void 563Set_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 608int 609Set_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 629const char * 630slice_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 case 238: return "EFI GPT"; 665 case 239: return "EFI Sys. Part."; 666 default: return "unknown"; 667 } 668#endif 669 case 2: return "fat"; 670 case 3: switch (subtype) { 671#ifdef PC98 672 case 0xc494: return "freebsd"; 673#else 674 case 165: return "freebsd"; 675#endif 676 default: return "unknown"; 677 } 678#ifndef PC98 679 case 4: return "extended"; 680 case 5: return "part"; 681 case 6: return "unused"; 682#endif 683 default: return "unknown"; 684 } 685} 686