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