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