disk.c revision 113083
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 113083 2003-04-04 16:35:16Z phk $"); 12 13#include <stdio.h> 14#include <stdlib.h> 15#include <unistd.h> 16#include <fcntl.h> 17#include <string.h> 18#include <inttypes.h> 19#include <err.h> 20#include <sys/sysctl.h> 21#include <sys/stdint.h> 22#include <sys/types.h> 23#include <sys/stat.h> 24#include <sys/ioctl.h> 25#include <sys/disklabel.h> 26#include <sys/uuid.h> 27#include <sys/gpt.h> 28#include <paths.h> 29#include "libdisk.h" 30 31#include <ctype.h> 32#include <errno.h> 33#include <assert.h> 34#include <uuid.h> 35 36#ifdef DEBUG 37#define DPRINT(x) warn x 38#define DPRINTX(x) warnx x 39#else 40#define DPRINT(x) 41#define DPRINTX(x) 42#endif 43 44const char * 45chunk_name(chunk_e type) 46{ 47 switch(type) { 48 case unused: return ("unused"); 49 case mbr: return ("mbr"); 50 case part: return ("part"); 51 case gpt: return ("gpt"); 52 case pc98: return ("pc98"); 53 case sun: return ("sun"); 54 case freebsd: return ("freebsd"); 55 case fat: return ("fat"); 56 case spare: return ("spare"); 57 case efi: return ("efi"); 58 default: return ("??"); 59 } 60}; 61 62static chunk_e 63uuid_type(uuid_t *uuid) 64{ 65 static uuid_t _efi = GPT_ENT_TYPE_EFI; 66 static uuid_t _mbr = GPT_ENT_TYPE_MBR; 67 static uuid_t _fbsd = GPT_ENT_TYPE_FREEBSD; 68 static uuid_t _swap = GPT_ENT_TYPE_FREEBSD_SWAP; 69 static uuid_t _ufs = GPT_ENT_TYPE_FREEBSD_UFS; 70 static uuid_t _vinum = GPT_ENT_TYPE_FREEBSD_VINUM; 71 72 if (uuid_is_nil(uuid, NULL)) 73 return (unused); 74 if (uuid_equal(uuid, &_efi, NULL)) 75 return (efi); 76 if (uuid_equal(uuid, &_mbr, NULL)) 77 return (mbr); 78 if (uuid_equal(uuid, &_fbsd, NULL)) 79 return (freebsd); 80 if (uuid_equal(uuid, &_swap, NULL)) 81 return (part); 82 if (uuid_equal(uuid, &_ufs, NULL)) 83 return (part); 84 if (uuid_equal(uuid, &_vinum, NULL)) 85 return (part); 86 return (spare); 87} 88 89struct disk * 90Open_Disk(const char *name) 91{ 92 93 return Int_Open_Disk(name); 94} 95 96struct disk * 97Int_Open_Disk(const char *name) 98{ 99 uuid_t uuid; 100 char *conftxt = NULL; 101 struct disk *d; 102 size_t txtsize; 103 int error, i; 104 char *p, *q, *r, *a, *b, *n, *t, *sn; 105 off_t o, len, off; 106 u_int l, s, ty, sc, hd, alt; 107 off_t lo[10]; 108 109 error = sysctlbyname("kern.geom.conftxt", NULL, &txtsize, NULL, 0); 110 if (error) { 111 warn("kern.geom.conftxt sysctl not available, giving up!"); 112 return (NULL); 113 } 114 conftxt = (char *) malloc(txtsize+1); 115 if (conftxt == NULL) { 116 DPRINT(("cannot malloc memory for conftxt")); 117 return (NULL); 118 } 119 error = sysctlbyname("kern.geom.conftxt", conftxt, &txtsize, NULL, 0); 120 if (error) { 121 DPRINT(("error reading kern.geom.conftxt from the system")); 122 free(conftxt); 123 return (NULL); 124 } 125 conftxt[txtsize] = '\0'; /* in case kernel bug is still there */ 126 127 for (p = conftxt; p != NULL && *p; p = strchr(p, '\n')) { 128 if (*p == '\n') 129 p++; 130 a = strsep(&p, " "); 131 if (strcmp(a, "0")) 132 continue; 133 134 a = strsep(&p, " "); 135 if (strcmp(a, "DISK")) 136 continue; 137 138 a = strsep(&p, " "); 139 if (strcmp(a, name)) 140 continue; 141 break; 142 } 143 144 q = strchr(p, '\n'); 145 if (q != NULL) 146 *q++ = '\0'; 147 148 d = (struct disk *)calloc(sizeof *d, 1); 149 if(d == NULL) 150 return NULL; 151 152 d->name = strdup(name); 153 154 a = strsep(&p, " "); /* length in bytes */ 155 len = strtoimax(a, &r, 0); 156 if (*r) { 157 printf("BARF %d <%d>\n", __LINE__, *r); 158 exit (0); 159 } 160 161 a = strsep(&p, " "); /* sectorsize */ 162 s = strtoul(a, &r, 0); 163 if (*r) { 164 printf("BARF %d <%d>\n", __LINE__, *r); 165 exit (0); 166 } 167 168 if (s == 0) 169 return (NULL); 170 d->sector_size = s; 171 len /= s; /* media size in number of sectors. */ 172 173 if (Add_Chunk(d, 0, len, name, whole, 0, 0, "-")) 174 DPRINT(("Failed to add 'whole' chunk")); 175 176 for (;;) { 177 a = strsep(&p, " "); 178 if (a == NULL) 179 break; 180 b = strsep(&p, " "); 181 o = strtoul(b, &r, 0); 182 if (*r) { 183 printf("BARF %d <%d>\n", __LINE__, *r); 184 exit (0); 185 } 186 if (!strcmp(a, "hd")) 187 d->bios_hd = o; 188 else if (!strcmp(a, "sc")) 189 d->bios_sect = o; 190 else 191 printf("HUH ? <%s> <%s>\n", a, b); 192 } 193 194 /* 195 * Calculate the number of cylinders this disk must have. If we have 196 * an obvious insanity, we set the number of cyclinders to zero. 197 */ 198 o = d->bios_hd * d->bios_sect; 199 d->bios_cyl = (o != 0) ? len / o : 0; 200 201 p = q; 202 lo[0] = 0; 203 204 for (; p != NULL && *p; p = q) { 205 q = strchr(p, '\n'); 206 if (q != NULL) 207 *q++ = '\0'; 208 a = strsep(&p, " "); /* Index */ 209 if (!strcmp(a, "0")) 210 break; 211 l = strtoimax(a, &r, 0); 212 if (*r) { 213 printf("BARF %d <%d>\n", __LINE__, *r); 214 exit (0); 215 } 216 t = strsep(&p, " "); /* Type {SUN, BSD, MBR, PC98, GPT} */ 217 n = strsep(&p, " "); /* name */ 218 a = strsep(&p, " "); /* len */ 219 len = strtoimax(a, &r, 0); 220 if (*r) { 221 printf("BARF %d <%d>\n", __LINE__, *r); 222 exit (0); 223 } 224 a = strsep(&p, " "); /* secsize */ 225 s = strtoimax(a, &r, 0); 226 if (*r) { 227 printf("BARF %d <%d>\n", __LINE__, *r); 228 exit (0); 229 } 230 for (;;) { 231 a = strsep(&p, " "); 232 if (a == NULL) 233 break; 234 /* XXX: Slice name may include a space. */ 235 if (!strcmp(a, "sn")) { 236 sn = p; 237 break; 238 } 239 b = strsep(&p, " "); 240 o = strtoimax(b, &r, 0); 241 if (*r) { 242 uint32_t status; 243 244 uuid_from_string(b, &uuid, &status); 245 if (status != uuid_s_ok) { 246 printf("BARF %d <%d>\n", __LINE__, *r); 247 exit (0); 248 } 249 o = uuid_type(&uuid); 250 } 251 if (!strcmp(a, "o")) 252 off = o; 253 else if (!strcmp(a, "i")) 254 i = o; 255 else if (!strcmp(a, "ty")) 256 ty = o; 257 else if (!strcmp(a, "sc")) 258 sc = o; 259 else if (!strcmp(a, "hd")) 260 hd = o; 261 else if (!strcmp(a, "alt")) 262 alt = o; 263 } 264 265 /* PLATFORM POLICY BEGIN ----------------------------------- */ 266 if (platform == p_sparc64 && !strcmp(t, "SUN") && i == 2) 267 continue; 268 if (platform == p_sparc64 && !strcmp(t, "SUN") && 269 d->chunks->part->part == NULL) { 270 d->bios_hd = hd; 271 d->bios_sect = sc; 272 o = d->chunks->size / (hd * sc); 273 o *= (hd * sc); 274 o -= alt * hd * sc; 275 if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-")) 276 DPRINT(("Failed to add 'freebsd' chunk")); 277 } 278 if (platform == p_alpha && !strcmp(t, "BSD") && 279 d->chunks->part->part == NULL) { 280 if (Add_Chunk(d, 0, d->chunks->size, name, freebsd, 281 0, 0, "-")) 282 DPRINT(("Failed to add 'freebsd' chunk")); 283 } 284 if (!strcmp(t, "BSD") && i == RAW_PART) 285 continue; 286 /* PLATFORM POLICY END ------------------------------------- */ 287 288 off /= s; 289 len /= s; 290 off += lo[l - 1]; 291 lo[l] = off; 292 if (!strcmp(t, "SUN")) 293 i = Add_Chunk(d, off, len, n, part, 0, 0, 0); 294 else if (!strncmp(t, "MBR", 3)) { 295 switch (ty) { 296 case 0xa5: 297 i = Add_Chunk(d, off, len, n, freebsd, ty, 0, 0); 298 break; 299 case 0x01: 300 case 0x04: 301 case 0x06: 302 case 0x0b: 303 case 0x0c: 304 case 0x0e: 305 i = Add_Chunk(d, off, len, n, fat, ty, 0, 0); 306 break; 307 case 0xef: /* EFI */ 308 i = Add_Chunk(d, off, len, n, efi, ty, 0, 0); 309 break; 310 default: 311 i = Add_Chunk(d, off, len, n, mbr, ty, 0, 0); 312 break; 313 } 314 } else if (!strcmp(t, "BSD")) 315 i = Add_Chunk(d, off, len, n, part, ty, 0, 0); 316 else if (!strcmp(t, "PC98")) { 317 switch (ty & 0x7f) { 318 case 0x14: 319 i = Add_Chunk(d, off, len, n, freebsd, ty, 0, 320 sn); 321 break; 322 case 0x20: 323 case 0x21: 324 case 0x22: 325 case 0x23: 326 case 0x24: 327 i = Add_Chunk(d, off, len, n, fat, ty, 0, sn); 328 break; 329 default: 330 i = Add_Chunk(d, off, len, n, pc98, ty, 0, sn); 331 break; 332 } 333 } else if (!strcmp(t, "GPT")) 334 i = Add_Chunk(d, off, len, n, ty, 0, 0, 0); 335 else if (!strcmp(t, "BDE")) 336 ; /* nothing */ 337 else { 338 printf("BARF %d\n", __LINE__); 339 exit(0); 340 } 341 } 342 /* PLATFORM POLICY BEGIN ------------------------------------- */ 343 /* We have a chance to do things on a blank disk here */ 344 if (platform == p_sparc64 && d->chunks->part->part == NULL) { 345 hd = d->bios_hd; 346 sc = d->bios_sect; 347 o = d->chunks->size / (hd * sc); 348 o *= (hd * sc); 349 o -= 2 * hd * sc; 350 if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-")) 351 DPRINT(("Failed to add 'freebsd' chunk")); 352 } 353 /* PLATFORM POLICY END --------------------------------------- */ 354 355 return (d); 356 i = 0; 357} 358 359void 360Debug_Disk(struct disk *d) 361{ 362 363 printf("Debug_Disk(%s)", d->name); 364#if 0 365 printf(" real_geom=%lu/%lu/%lu", 366 d->real_cyl, d->real_hd, d->real_sect); 367#endif 368 printf(" bios_geom=%lu/%lu/%lu = %lu\n", 369 d->bios_cyl, d->bios_hd, d->bios_sect, 370 d->bios_cyl * d->bios_hd * d->bios_sect); 371#if defined(PC98) 372 printf(" boot1=%p, boot2=%p, bootipl=%p, bootmenu=%p\n", 373 d->boot1, d->boot2, d->bootipl, d->bootmenu); 374#elif defined(__i386__) 375 printf(" boot1=%p, boot2=%p, bootmgr=%p\n", 376 d->boot1, d->boot2, d->bootmgr); 377#elif defined(__alpha__) 378 printf(" boot1=%p, bootmgr=%p\n", 379 d->boot1, d->bootmgr); 380#elif defined(__ia64__) 381 printf("\n"); 382#else 383/* Should be: error "Debug_Disk: unknown arch"; */ 384#endif 385 Debug_Chunk(d->chunks); 386} 387 388void 389Free_Disk(struct disk *d) 390{ 391 if (d->chunks) 392 Free_Chunk(d->chunks); 393 if (d->name) 394 free(d->name); 395#ifdef PC98 396 if (d->bootipl) 397 free(d->bootipl); 398 if (d->bootmenu) 399 free(d->bootmenu); 400#else 401#if !defined(__ia64__) 402 if (d->bootmgr) 403 free(d->bootmgr); 404#endif 405#endif 406#if !defined(__ia64__) 407 if (d->boot1) 408 free(d->boot1); 409#endif 410#if defined(__i386__) 411 if (d->boot2) 412 free(d->boot2); 413#endif 414 free(d); 415} 416 417#if 0 418void 419Collapse_Disk(struct disk *d) 420{ 421 422 while (Collapse_Chunk(d, d->chunks)) 423 ; 424} 425#endif 426 427static int 428qstrcmp(const void* a, const void* b) 429{ 430 char *str1 = *(char**)a; 431 char *str2 = *(char**)b; 432 433 return strcmp(str1, str2); 434} 435 436char ** 437Disk_Names() 438{ 439 int disk_cnt; 440 static char **disks; 441 int error; 442 size_t listsize; 443 char *disklist; 444 445 error = sysctlbyname("kern.disks", NULL, &listsize, NULL, 0); 446 if (error) { 447 warn("kern.disks sysctl not available"); 448 return NULL; 449 } 450 451 disks = malloc(sizeof *disks * (1 + MAX_NO_DISKS)); 452 if (disks == NULL) 453 return NULL; 454 disklist = (char *)malloc(listsize + 1); 455 if (disklist == NULL) { 456 free(disks); 457 return NULL; 458 } 459 memset(disks,0,sizeof *disks * (1 + MAX_NO_DISKS)); 460 memset(disklist, 0, listsize + 1); 461 error = sysctlbyname("kern.disks", disklist, &listsize, NULL, 0); 462 if (error) { 463 free(disklist); 464 free(disks); 465 return NULL; 466 } 467 for (disk_cnt = 0; disk_cnt < MAX_NO_DISKS; disk_cnt++) { 468 disks[disk_cnt] = strsep(&disklist, " "); 469 if (disks[disk_cnt] == NULL) 470 break; 471 } 472 qsort(disks, disk_cnt, sizeof(char*), qstrcmp); 473 return disks; 474} 475 476#ifdef PC98 477void 478Set_Boot_Mgr(struct disk *d, const u_char *bootipl, const size_t bootipl_size, 479 const u_char *bootmenu, const size_t bootmenu_size) 480#else 481void 482Set_Boot_Mgr(struct disk *d, const u_char *b, const size_t s) 483#endif 484{ 485#if !defined(__ia64__) 486#ifdef PC98 487 if (d->sector_size == 0) 488 return; 489 if (bootipl_size % d->sector_size != 0) 490 return; 491 if (d->bootipl) 492 free(d->bootipl); 493 if (!bootipl) { 494 d->bootipl = NULL; 495 } else { 496 d->bootipl_size = bootipl_size; 497 d->bootipl = malloc(bootipl_size); 498 if (!d->bootipl) 499 return; 500 memcpy(d->bootipl, bootipl, bootipl_size); 501 } 502 503 if (bootmenu_size % d->sector_size != 0) 504 return; 505 if (d->bootmenu) 506 free(d->bootmenu); 507 if (!bootmenu) { 508 d->bootmenu = NULL; 509 } else { 510 d->bootmenu_size = bootmenu_size; 511 d->bootmenu = malloc(bootmenu_size); 512 if (!d->bootmenu) 513 return; 514 memcpy(d->bootmenu, bootmenu, bootmenu_size); 515 } 516#else 517 if (d->sector_size == 0) 518 return; 519 if (s % d->sector_size != 0) 520 return; 521 if (d->bootmgr) 522 free(d->bootmgr); 523 if (!b) { 524 d->bootmgr = NULL; 525 } else { 526 d->bootmgr_size = s; 527 d->bootmgr = malloc(s); 528 if (!d->bootmgr) 529 return; 530 memcpy(d->bootmgr, b, s); 531 } 532#endif 533#endif 534} 535 536int 537Set_Boot_Blocks(struct disk *d, const u_char *b1, const u_char *b2) 538{ 539#if defined(__i386__) 540 if (d->boot1) 541 free(d->boot1); 542 d->boot1 = malloc(512); 543 if (!d->boot1) 544 return -1; 545 memcpy(d->boot1, b1, 512); 546 if (d->boot2) 547 free(d->boot2); 548 d->boot2 = malloc(15 * 512); 549 if (!d->boot2) 550 return -1; 551 memcpy(d->boot2, b2, 15 * 512); 552#elif defined(__alpha__) 553 if (d->boot1) 554 free(d->boot1); 555 d->boot1 = malloc(15 * 512); 556 if (!d->boot1) 557 return -1; 558 memcpy(d->boot1, b1, 15 * 512); 559#elif defined(__sparc64__) 560 if (d->boot1 != NULL) 561 free(d->boot1); 562 d->boot1 = malloc(16 * 512); 563 if (d->boot1 == NULL) 564 return (-1); 565 memcpy(d->boot1, b1, 16 * 512); 566#elif defined(__ia64__) 567 /* nothing */ 568#else 569/* Should be: #error "Set_Boot_Blocks: unknown arch"; */ 570#endif 571 return 0; 572} 573 574#ifdef PC98 575const char * 576slice_type_name( int type, int subtype ) 577{ 578 579 switch (type) { 580 case whole: 581 return "whole"; 582 case fat: 583 return "fat"; 584 case freebsd: 585 switch (subtype) { 586 case 0xc494: return "freebsd"; 587 default: return "unknown"; 588 } 589 case unused: 590 return "unused"; 591 default: 592 return "unknown"; 593 } 594} 595#else /* PC98 */ 596const char * 597slice_type_name( int type, int subtype ) 598{ 599 600 switch (type) { 601 case whole: 602 return "whole"; 603 case mbr: 604 switch (subtype) { 605 case 1: return "fat (12-bit)"; 606 case 2: return "XENIX /"; 607 case 3: return "XENIX /usr"; 608 case 4: return "fat (16-bit,<=32Mb)"; 609 case 5: return "extended DOS"; 610 case 6: return "fat (16-bit,>32Mb)"; 611 case 7: return "NTFS/HPFS/QNX"; 612 case 8: return "AIX bootable"; 613 case 9: return "AIX data"; 614 case 10: return "OS/2 bootmgr"; 615 case 11: return "fat (32-bit)"; 616 case 12: return "fat (32-bit,LBA)"; 617 case 14: return "fat (16-bit,>32Mb,LBA)"; 618 case 15: return "extended DOS, LBA"; 619 case 18: return "Compaq Diagnostic"; 620 case 84: return "OnTrack diskmgr"; 621 case 100: return "Netware 2.x"; 622 case 101: return "Netware 3.x"; 623 case 115: return "SCO UnixWare"; 624 case 128: return "Minix 1.1"; 625 case 129: return "Minix 1.5"; 626 case 130: return "linux_swap"; 627 case 131: return "ext2fs"; 628 case 166: return "OpenBSD FFS"; /* 0xA6 */ 629 case 169: return "NetBSD FFS"; /* 0xA9 */ 630 case 182: return "OpenBSD"; /* dedicated */ 631 case 183: return "bsd/os"; 632 case 184: return "bsd/os swap"; 633 case 238: return "EFI GPT"; 634 case 239: return "EFI Sys. Part."; 635 default: return "unknown"; 636 } 637 case fat: 638 return "fat"; 639 case freebsd: 640 switch (subtype) { 641 case 165: return "freebsd"; 642 default: return "unknown"; 643 } 644 case extended: 645 return "extended"; 646 case part: 647 return "part"; 648 case efi: 649 return "efi"; 650 case unused: 651 return "unused"; 652 default: 653 return "unknown"; 654 } 655} 656#endif /* PC98 */ 657