disk.c revision 106761
1351290Sdim/* 2351290Sdim * ---------------------------------------------------------------------------- 3351290Sdim * "THE BEER-WARE LICENSE" (Revision 42): 4351290Sdim * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 5351290Sdim * can do whatever you want with this stuff. If we meet some day, and you think 6351290Sdim * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7351290Sdim * ---------------------------------------------------------------------------- 8351290Sdim */ 9351290Sdim 10351290Sdim#include <sys/cdefs.h> 11351290Sdim__FBSDID("$FreeBSD: head/lib/libdisk/disk.c 106761 2002-11-11 10:08:46Z phk $"); 12351290Sdim 13351290Sdim#include <stdio.h> 14351290Sdim#include <stdlib.h> 15351290Sdim#include <unistd.h> 16351290Sdim#include <fcntl.h> 17351290Sdim#include <string.h> 18351290Sdim#include <inttypes.h> 19351290Sdim#include <err.h> 20351290Sdim#include <sys/sysctl.h> 21351290Sdim#include <sys/stdint.h> 22351290Sdim#include <sys/types.h> 23351290Sdim#include <sys/stat.h> 24351290Sdim#include <sys/ioctl.h> 25351290Sdim#include <sys/disklabel.h> 26351290Sdim#include <sys/diskslice.h> 27351290Sdim#include <sys/uuid.h> 28351290Sdim#include <sys/gpt.h> 29351290Sdim#include <paths.h> 30351290Sdim#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 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; 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) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 157 158 a = strsep(&p, " "); /* sectorsize */ 159 s = strtoul(a, &r, 0); 160 if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 161 162 d->sector_size = s; 163 len /= s; /* media size in number of sectors. */ 164 165 if (Add_Chunk(d, 0, len, name, whole, 0, 0, "-")) 166 DPRINT(("Failed to add 'whole' chunk")); 167 168 for (;;) { 169 a = strsep(&p, " "); 170 if (a == NULL) 171 break; 172 b = strsep(&p, " "); 173 o = strtoul(b, &r, 0); 174 if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 175 if (!strcmp(a, "hd")) 176 d->bios_hd = o; 177 else if (!strcmp(a, "sc")) 178 d->bios_sect = o; 179 else 180 printf("HUH ? <%s> <%s>\n", a, b); 181 } 182 183 /* 184 * Calculate the number of cylinders this disk must have. If we have 185 * an obvious insanity, we set the number of cyclinders to zero. 186 */ 187 o = d->bios_hd * d->bios_sect; 188 d->bios_cyl = (o != 0 && (len % o) == 0) ? len / o : 0; 189 190 p = q; 191 lo[0] = 0; 192 193 for (; p != NULL && *p; p = q) { 194 q = strchr(p, '\n'); 195 if (q != NULL) 196 *q++ = '\0'; 197 a = strsep(&p, " "); /* Index */ 198 if (!strcmp(a, "0")) 199 break; 200 l = strtoimax(a, &r, 0); 201 if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 202 t = strsep(&p, " "); /* Type {SUN, BSD, MBR, GPT} */ 203 n = strsep(&p, " "); /* name */ 204 a = strsep(&p, " "); /* len */ 205 len = strtoimax(a, &r, 0); 206 if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 207 a = strsep(&p, " "); /* secsize */ 208 s = strtoimax(a, &r, 0); 209 if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 210 for (;;) { 211 a = strsep(&p, " "); 212 if (a == NULL) 213 break; 214 b = strsep(&p, " "); 215 o = strtoimax(b, &r, 0); 216 if (*r) { 217 uint32_t status; 218 219 uuid_from_string(b, &uuid, &status); 220 if (status != uuid_s_ok) { 221 printf("BARF %d <%d>\n", __LINE__, *r); 222 exit (0); 223 } 224 o = uuid_type(&uuid); 225 } 226 if (!strcmp(a, "o")) 227 off = o; 228 else if (!strcmp(a, "i")) 229 i = o; 230 else if (!strcmp(a, "ty")) 231 ty = o; 232 else if (!strcmp(a, "sc")) 233 sc = o; 234 else if (!strcmp(a, "hd")) 235 hd = o; 236 else if (!strcmp(a, "alt")) 237 alt = o; 238 } 239 240 /* PLATFORM POLICY BEGIN ------------------------------------- */ 241 if (platform == p_sparc64 && !strcmp(t, "SUN") && i == 2) 242 continue; 243 if (platform == p_sparc64 && !strcmp(t, "SUN") && 244 d->chunks->part->part == NULL) { 245 d->bios_hd = hd; 246 d->bios_sect = sc; 247 o = d->chunks->size / (hd * sc); 248 o *= (hd * sc); 249 o -= alt * hd * sc; 250 if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-")) 251 DPRINT(("Failed to add 'freebsd' chunk")); 252 } 253 if (platform == p_alpha && !strcmp(t, "BSD") && 254 d->chunks->part->part == NULL) { 255 if (Add_Chunk(d, 0, d->chunks->size, name, freebsd, 0, 0, "-")) 256 DPRINT(("Failed to add 'freebsd' chunk")); 257 } 258 if (!strcmp(t, "BSD") && i == RAW_PART) 259 continue; 260 /* PLATFORM POLICY END --------------------------------------- */ 261 262 off /= s; 263 len /= s; 264 off += lo[l - 1]; 265 lo[l] = off; 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 } 308 /* PLATFORM POLICY BEGIN ------------------------------------- */ 309 /* We have a chance to do things on a blank disk here */ 310 if (platform == p_sparc64 && d->chunks->part->part == NULL) { 311 hd = d->bios_hd; 312 sc = d->bios_sect; 313 o = d->chunks->size / (hd * sc); 314 o *= (hd * sc); 315 o -= 2 * hd * sc; 316 if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-")) 317 DPRINT(("Failed to add 'freebsd' chunk")); 318 } 319 /* PLATFORM POLICY END --------------------------------------- */ 320 321 return (d); 322 i = 0; 323} 324 325void 326Debug_Disk(struct disk *d) 327{ 328 printf("Debug_Disk(%s)", d->name); 329#if 0 330 printf(" real_geom=%lu/%lu/%lu", d->real_cyl, d->real_hd, d->real_sect); 331#endif 332 printf(" bios_geom=%lu/%lu/%lu = %lu\n", 333 d->bios_cyl, d->bios_hd, d->bios_sect, 334 d->bios_cyl * d->bios_hd * d->bios_sect); 335#if defined(PC98) 336 printf(" boot1=%p, boot2=%p, bootipl=%p, bootmenu=%p\n", 337 d->boot1, d->boot2, d->bootipl, d->bootmenu); 338#elif defined(__i386__) 339 printf(" boot1=%p, boot2=%p, bootmgr=%p\n", 340 d->boot1, d->boot2, d->bootmgr); 341#elif defined(__alpha__) 342 printf(" boot1=%p, bootmgr=%p\n", 343 d->boot1, d->bootmgr); 344#elif defined(__ia64__) 345 printf("\n"); 346#else 347/* Should be: error "Debug_Disk: unknown arch"; */ 348#endif 349 Debug_Chunk(d->chunks); 350} 351 352void 353Free_Disk(struct disk *d) 354{ 355 if(d->chunks) Free_Chunk(d->chunks); 356 if(d->name) free(d->name); 357#ifdef PC98 358 if(d->bootipl) free(d->bootipl); 359 if(d->bootmenu) free(d->bootmenu); 360#else 361#if !defined(__ia64__) 362 if(d->bootmgr) free(d->bootmgr); 363#endif 364#endif 365#if !defined(__ia64__) 366 if(d->boot1) free(d->boot1); 367#endif 368#if defined(__i386__) 369 if(d->boot2) free(d->boot2); 370#endif 371 free(d); 372} 373 374#if 0 375void 376Collapse_Disk(struct disk *d) 377{ 378 379 while(Collapse_Chunk(d, d->chunks)) 380 ; 381} 382#endif 383 384static int 385qstrcmp(const void* a, const void* b) 386{ 387 388 char *str1 = *(char**)a; 389 char *str2 = *(char**)b; 390 return strcmp(str1, str2); 391} 392 393char ** 394Disk_Names() 395{ 396 int disk_cnt; 397 static char **disks; 398 int error; 399 size_t listsize; 400 char *disklist; 401 402 error = sysctlbyname("kern.disks", NULL, &listsize, NULL, 0); 403 if (error) { 404 warn("kern.disks sysctl not available"); 405 return NULL; 406 } 407 408 disks = malloc(sizeof *disks * (1 + MAX_NO_DISKS)); 409 if (disks == NULL) 410 return NULL; 411 disklist = (char *)malloc(listsize + 1); 412 if (disklist == NULL) { 413 free(disks); 414 return NULL; 415 } 416 memset(disks,0,sizeof *disks * (1 + MAX_NO_DISKS)); 417 memset(disklist, 0, listsize + 1); 418 error = sysctlbyname("kern.disks", disklist, &listsize, NULL, 0); 419 if (error) { 420 free(disklist); 421 free(disks); 422 return NULL; 423 } 424 for (disk_cnt = 0; disk_cnt < MAX_NO_DISKS; disk_cnt++) { 425 disks[disk_cnt] = strsep(&disklist, " "); 426 if (disks[disk_cnt] == NULL) 427 break; 428 } 429 qsort(disks, disk_cnt, sizeof(char*), qstrcmp); 430 return disks; 431} 432 433#ifdef PC98 434void 435Set_Boot_Mgr(struct disk *d, const u_char *bootipl, const size_t bootipl_size, 436 const u_char *bootmenu, const size_t bootmenu_size) 437#else 438void 439Set_Boot_Mgr(struct disk *d, const u_char *b, const size_t s) 440#endif 441{ 442#if !defined(__ia64__) 443#ifdef PC98 444 if (bootipl_size % d->sector_size != 0) 445 return; 446 if (d->bootipl) 447 free(d->bootipl); 448 if (!bootipl) { 449 d->bootipl = NULL; 450 } else { 451 d->bootipl_size = bootipl_size; 452 d->bootipl = malloc(bootipl_size); 453 if(!d->bootipl) return; 454 memcpy(d->bootipl, bootipl, bootipl_size); 455 } 456 457 if (bootmenu_size % d->sector_size != 0) 458 return; 459 if (d->bootmenu) 460 free(d->bootmenu); 461 if (!bootmenu) { 462 d->bootmenu = NULL; 463 } else { 464 d->bootmenu_size = bootmenu_size; 465 d->bootmenu = malloc(bootmenu_size); 466 if(!d->bootmenu) return; 467 memcpy(d->bootmenu, bootmenu, bootmenu_size); 468 } 469#else 470 if (s % d->sector_size != 0) 471 return; 472 if (d->bootmgr) 473 free(d->bootmgr); 474 if (!b) { 475 d->bootmgr = NULL; 476 } else { 477 d->bootmgr_size = s; 478 d->bootmgr = malloc(s); 479 if(!d->bootmgr) return; 480 memcpy(d->bootmgr, b, s); 481 } 482#endif 483#endif 484} 485 486int 487Set_Boot_Blocks(struct disk *d, const u_char *b1, const u_char *b2) 488{ 489#if defined(__i386__) 490 if (d->boot1) free(d->boot1); 491 d->boot1 = malloc(512); 492 if(!d->boot1) return -1; 493 memcpy(d->boot1, b1, 512); 494 if (d->boot2) free(d->boot2); 495 d->boot2 = malloc(15 * 512); 496 if(!d->boot2) return -1; 497 memcpy(d->boot2, b2, 15 * 512); 498#elif defined(__alpha__) 499 if (d->boot1) free(d->boot1); 500 d->boot1 = malloc(15 * 512); 501 if(!d->boot1) return -1; 502 memcpy(d->boot1, b1, 15 * 512); 503#elif defined(__sparc64__) 504 if (d->boot1 != NULL) 505 free(d->boot1); 506 d->boot1 = malloc(16 * 512); 507 if (d->boot1 == NULL) 508 return (-1); 509 memcpy(d->boot1, b1, 16 * 512); 510#elif defined(__ia64__) 511 /* nothing */ 512#else 513/* Should be: #error "Set_Boot_Blocks: unknown arch"; */ 514#endif 515 return 0; 516} 517 518#ifdef PC98 519const char * 520slice_type_name( int type, int subtype ) 521{ 522 523 switch (type) { 524 case whole: 525 return "whole"; 526 case fat: 527 return "fat"; 528 case freebsd: 529 switch (subtype) { 530 case 0xc494: return "freebsd"; 531 default: return "unknown"; 532 } 533 case unused: 534 return "unused"; 535 default: 536 return "unknown"; 537 } 538} 539#else /* PC98 */ 540const char * 541slice_type_name( int type, int subtype ) 542{ 543 544 switch (type) { 545 case whole: 546 return "whole"; 547 case mbr: 548 switch (subtype) { 549 case 1: return "fat (12-bit)"; 550 case 2: return "XENIX /"; 551 case 3: return "XENIX /usr"; 552 case 4: return "fat (16-bit,<=32Mb)"; 553 case 5: return "extended DOS"; 554 case 6: return "fat (16-bit,>32Mb)"; 555 case 7: return "NTFS/HPFS/QNX"; 556 case 8: return "AIX bootable"; 557 case 9: return "AIX data"; 558 case 10: return "OS/2 bootmgr"; 559 case 11: return "fat (32-bit)"; 560 case 12: return "fat (32-bit,LBA)"; 561 case 14: return "fat (16-bit,>32Mb,LBA)"; 562 case 15: return "extended DOS, LBA"; 563 case 18: return "Compaq Diagnostic"; 564 case 84: return "OnTrack diskmgr"; 565 case 100: return "Netware 2.x"; 566 case 101: return "Netware 3.x"; 567 case 115: return "SCO UnixWare"; 568 case 128: return "Minix 1.1"; 569 case 129: return "Minix 1.5"; 570 case 130: return "linux_swap"; 571 case 131: return "ext2fs"; 572 case 166: return "OpenBSD FFS"; /* 0xA6 */ 573 case 169: return "NetBSD FFS"; /* 0xA9 */ 574 case 182: return "OpenBSD"; /* dedicated */ 575 case 183: return "bsd/os"; 576 case 184: return "bsd/os swap"; 577 case 238: return "EFI GPT"; 578 case 239: return "EFI Sys. Part."; 579 default: return "unknown"; 580 } 581 case fat: 582 return "fat"; 583 case freebsd: 584 switch (subtype) { 585 case 165: return "freebsd"; 586 default: return "unknown"; 587 } 588 case extended: 589 return "extended"; 590 case part: 591 return "part"; 592 case efi: 593 return "efi"; 594 case unused: 595 return "unused"; 596 default: 597 return "unknown"; 598 } 599} 600#endif /* PC98 */ 601