disk.c revision 106368
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 106368 2002-11-03 01:37:08Z marcel $"); 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/diskmbr.h> 28#include <sys/uuid.h> 29#include <sys/gpt.h> 30#include <paths.h> 31#include "libdisk.h" 32 33#include <ctype.h> 34#include <errno.h> 35#include <assert.h> 36#include <uuid.h> 37 38#define DOSPTYP_EXTENDED 5 39#ifdef DEBUG 40#define DPRINT(x) warn x 41#define DPRINTX(x) warnx x 42#else 43#define DPRINT(x) 44#define DPRINTX(x) 45#endif 46 47const char * 48chunk_name(chunk_e type) 49{ 50 switch(type) { 51 case unused: return ("unused"); 52 case mbr: return ("mbr"); 53 case part: return ("part"); 54 case gpt: return ("gpt"); 55 case pc98: return ("pc98"); 56 case sun: return ("sun"); 57 case freebsd: return ("freebsd"); 58 case fat: return ("fat"); 59 case spare: return ("spare"); 60 default: return ("??"); 61 } 62}; 63 64static chunk_e 65uuid_type(uuid_t *uuid) 66{ 67 static uuid_t _efi = GPT_ENT_TYPE_EFI; 68 static uuid_t _mbr = GPT_ENT_TYPE_MBR; 69 static uuid_t _fbsd = GPT_ENT_TYPE_FREEBSD; 70 static uuid_t _swap = GPT_ENT_TYPE_FREEBSD_SWAP; 71 static uuid_t _ufs = GPT_ENT_TYPE_FREEBSD_UFS; 72 static uuid_t _vinum = GPT_ENT_TYPE_FREEBSD_VINUM; 73 74 if (uuid_is_nil(uuid, NULL)) 75 return (unused); 76 if (uuid_equal(uuid, &_efi, NULL)) 77 return (fat); 78 if (uuid_equal(uuid, &_mbr, NULL)) 79 return (mbr); 80 if (uuid_equal(uuid, &_fbsd, NULL)) 81 return (freebsd); 82 if (uuid_equal(uuid, &_swap, NULL)) 83 return (part); 84 if (uuid_equal(uuid, &_ufs, NULL)) 85 return (part); 86 if (uuid_equal(uuid, &_vinum, NULL)) 87 return (part); 88 return (spare); 89} 90 91struct disk * 92Open_Disk(const char *name) 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; 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) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 158 159 a = strsep(&p, " "); /* sectorsize */ 160 s = strtoul(a, &r, 0); 161 if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 162 163 d->sector_size = s; 164 len /= s; /* media size in number of sectors. */ 165 166 if (Add_Chunk(d, 0, len, name, whole, 0, 0, "-")) 167 DPRINT(("Failed to add 'whole' chunk")); 168 169 for (;;) { 170 a = strsep(&p, " "); 171 if (a == NULL) 172 break; 173 b = strsep(&p, " "); 174 o = strtoul(b, &r, 0); 175 if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 176 if (!strcmp(a, "hd")) 177 d->bios_hd = o; 178 else if (!strcmp(a, "sc")) 179 d->bios_sect = o; 180 else 181 printf("HUH ? <%s> <%s>\n", a, b); 182 } 183 184 /* 185 * Calculate the number of cylinders this disk must have. If we have 186 * an obvious insanity, we set the number of cyclinders to zero. 187 */ 188 o = d->bios_hd * d->bios_sect; 189 d->bios_cyl = (o != 0 && (len % o) == 0) ? len / o : 0; 190 191 p = q; 192 lo[0] = 0; 193 194 for (; p != NULL && *p; p = q) { 195 q = strchr(p, '\n'); 196 if (q != NULL) 197 *q++ = '\0'; 198 a = strsep(&p, " "); /* Index */ 199 if (!strcmp(a, "0")) 200 break; 201 l = strtoimax(a, &r, 0); 202 if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 203 t = strsep(&p, " "); /* Type {SUN, BSD, MBR, GPT} */ 204 n = strsep(&p, " "); /* name */ 205 a = strsep(&p, " "); /* len */ 206 len = strtoimax(a, &r, 0); 207 if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 208 a = strsep(&p, " "); /* secsize */ 209 s = strtoimax(a, &r, 0); 210 if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 211 for (;;) { 212 a = strsep(&p, " "); 213 if (a == NULL) 214 break; 215 b = strsep(&p, " "); 216 o = strtoimax(b, &r, 0); 217 if (*r) { 218 uint32_t status; 219 220 uuid_from_string(b, &uuid, &status); 221 if (status != uuid_s_ok) { 222 printf("BARF %d <%d>\n", __LINE__, *r); 223 exit (0); 224 } 225 o = uuid_type(&uuid); 226 } 227 if (!strcmp(a, "o")) 228 off = o; 229 else if (!strcmp(a, "i")) 230 i = o; 231 else if (!strcmp(a, "ty")) 232 ty = o; 233 else if (!strcmp(a, "sc")) 234 sc = o; 235 else if (!strcmp(a, "hd")) 236 hd = o; 237 else if (!strcmp(a, "alt")) 238 alt = o; 239 } 240 241 /* PLATFORM POLICY BEGIN ------------------------------------- */ 242 if (platform == p_sparc64 && !strcmp(t, "SUN") && i == 2) 243 continue; 244 if (platform == p_sparc64 && !strcmp(t, "SUN") && 245 d->chunks->part->part == NULL) { 246 d->bios_hd = hd; 247 d->bios_sect = sc; 248 o = d->chunks->size / (hd * sc); 249 o *= (hd * sc); 250 o -= alt * hd * sc; 251 if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-")) 252 DPRINT(("Failed to add 'freebsd' chunk")); 253 } 254 if (platform == p_alpha && !strcmp(t, "BSD") && 255 d->chunks->part->part == NULL) { 256 o = d->chunks->size; 257 if (Add_Chunk(d, 0, d->chunks->size, name, freebsd, 0, 0, "-")) 258 DPRINT(("Failed to add 'freebsd' chunk")); 259 } 260 if (!strcmp(t, "BSD") && i == RAW_PART) 261 continue; 262 /* PLATFORM POLICY END --------------------------------------- */ 263 264 off /= s; 265 len /= s; 266 off += lo[l - 1]; 267 lo[l] = off; 268 printf("%s [%s] %jd %jd\n", t, n, (intmax_t)(off / s), (intmax_t) (len / s)); 269 if (!strcmp(t, "SUN")) 270 i = Add_Chunk(d, off, len, n, part, 0, 0, 0); 271 else if (!strncmp(t, "MBR", 3) && ty == 165) 272 i = Add_Chunk(d, off, len, n, freebsd, ty, 0, 0); 273 else if (!strncmp(t, "MBR", 3)) 274 i = Add_Chunk(d, off, len, n, mbr, ty, 0, 0); 275 else if (!strcmp(t, "BSD")) 276 i = Add_Chunk(d, off, len, n, part, 0, 0, 0); 277 else if (!strcmp(t, "PC98")) 278 i = Add_Chunk(d, off, len, n, pc98, 0, 0, 0); 279 else if (!strcmp(t, "GPT")) 280 i = Add_Chunk(d, off, len, n, ty, 0, 0, 0); 281 else 282 { printf("BARF %d\n", __LINE__); exit(0); } 283 printf("error = %d\n", i); 284 } 285 /* PLATFORM POLICY BEGIN ------------------------------------- */ 286 /* We have a chance to do things on a blank disk here */ 287printf("c %p\n", d->chunks); 288printf("c->p %p\n", d->chunks->part); 289printf("c->p->p %p\n", d->chunks->part->part); 290 if (platform == p_sparc64 && d->chunks->part->part == NULL) { 291printf("HERE %d\n", __LINE__); 292 hd = d->bios_hd; 293 sc = d->bios_sect; 294 o = d->chunks->size / (hd * sc); 295 o *= (hd * sc); 296 o -= 2 * hd * sc; 297printf("HERE %d\n", __LINE__); 298 if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-")) 299 DPRINT(("Failed to add 'freebsd' chunk")); 300 } 301 /* PLATFORM POLICY END --------------------------------------- */ 302 303 return (d); 304 i = 0; 305} 306 307void 308Debug_Disk(struct disk *d) 309{ 310 printf("Debug_Disk(%s)", d->name); 311#if 0 312 printf(" real_geom=%lu/%lu/%lu", d->real_cyl, d->real_hd, d->real_sect); 313#endif 314 printf(" bios_geom=%lu/%lu/%lu = %lu\n", 315 d->bios_cyl, d->bios_hd, d->bios_sect, 316 d->bios_cyl * d->bios_hd * d->bios_sect); 317#if defined(PC98) 318 printf(" boot1=%p, boot2=%p, bootipl=%p, bootmenu=%p\n", 319 d->boot1, d->boot2, d->bootipl, d->bootmenu); 320#elif defined(__i386__) 321 printf(" boot1=%p, boot2=%p, bootmgr=%p\n", 322 d->boot1, d->boot2, d->bootmgr); 323#elif defined(__alpha__) 324 printf(" boot1=%p, bootmgr=%p\n", 325 d->boot1, d->bootmgr); 326#elif defined(__ia64__) 327 printf("\n"); 328#else 329/* Should be: error "Debug_Disk: unknown arch"; */ 330#endif 331 Debug_Chunk(d->chunks); 332} 333 334void 335Free_Disk(struct disk *d) 336{ 337 if(d->chunks) Free_Chunk(d->chunks); 338 if(d->name) free(d->name); 339#ifdef PC98 340 if(d->bootipl) free(d->bootipl); 341 if(d->bootmenu) free(d->bootmenu); 342#else 343#if !defined(__ia64__) 344 if(d->bootmgr) free(d->bootmgr); 345#endif 346#endif 347#if !defined(__ia64__) 348 if(d->boot1) free(d->boot1); 349#endif 350#if defined(__i386__) 351 if(d->boot2) free(d->boot2); 352#endif 353 free(d); 354} 355 356#if 0 357void 358Collapse_Disk(struct disk *d) 359{ 360 361 while(Collapse_Chunk(d, d->chunks)) 362 ; 363} 364#endif 365 366static int 367qstrcmp(const void* a, const void* b) 368{ 369 370 char *str1 = *(char**)a; 371 char *str2 = *(char**)b; 372 return strcmp(str1, str2); 373} 374 375char ** 376Disk_Names() 377{ 378 int disk_cnt; 379 static char **disks; 380 int error; 381 size_t listsize; 382 char *disklist; 383 384 error = sysctlbyname("kern.disks", NULL, &listsize, NULL, 0); 385 if (error) { 386 warn("kern.disks sysctl not available"); 387 return NULL; 388 } 389 390 disks = malloc(sizeof *disks * (1 + MAX_NO_DISKS)); 391 if (disks == NULL) 392 return NULL; 393 disklist = (char *)malloc(listsize + 1); 394 if (disklist == NULL) { 395 free(disks); 396 return NULL; 397 } 398 memset(disks,0,sizeof *disks * (1 + MAX_NO_DISKS)); 399 memset(disklist, 0, listsize + 1); 400 error = sysctlbyname("kern.disks", disklist, &listsize, NULL, 0); 401 if (error) { 402 free(disklist); 403 free(disks); 404 return NULL; 405 } 406 for (disk_cnt = 0; disk_cnt < MAX_NO_DISKS; disk_cnt++) { 407 disks[disk_cnt] = strsep(&disklist, " "); 408 if (disks[disk_cnt] == NULL) 409 break; 410 } 411 qsort(disks, disk_cnt, sizeof(char*), qstrcmp); 412 return disks; 413} 414 415#ifdef PC98 416void 417Set_Boot_Mgr(struct disk *d, const u_char *bootipl, const size_t bootipl_size, 418 const u_char *bootmenu, const size_t bootmenu_size) 419#else 420void 421Set_Boot_Mgr(struct disk *d, const u_char *b, const size_t s) 422#endif 423{ 424#if !defined(__ia64__) 425#ifdef PC98 426 if (bootipl_size % d->sector_size != 0) 427 return; 428 if (d->bootipl) 429 free(d->bootipl); 430 if (!bootipl) { 431 d->bootipl = NULL; 432 } else { 433 d->bootipl_size = bootipl_size; 434 d->bootipl = malloc(bootipl_size); 435 if(!d->bootipl) return; 436 memcpy(d->bootipl, bootipl, bootipl_size); 437 } 438 439 if (bootmenu_size % d->sector_size != 0) 440 return; 441 if (d->bootmenu) 442 free(d->bootmenu); 443 if (!bootmenu) { 444 d->bootmenu = NULL; 445 } else { 446 d->bootmenu_size = bootmenu_size; 447 d->bootmenu = malloc(bootmenu_size); 448 if(!d->bootmenu) return; 449 memcpy(d->bootmenu, bootmenu, bootmenu_size); 450 } 451#else 452 if (s % d->sector_size != 0) 453 return; 454 if (d->bootmgr) 455 free(d->bootmgr); 456 if (!b) { 457 d->bootmgr = NULL; 458 } else { 459 d->bootmgr_size = s; 460 d->bootmgr = malloc(s); 461 if(!d->bootmgr) return; 462 memcpy(d->bootmgr, b, s); 463 } 464#endif 465#endif 466} 467 468int 469Set_Boot_Blocks(struct disk *d, const u_char *b1, const u_char *b2) 470{ 471#if defined(__i386__) 472 if (d->boot1) free(d->boot1); 473 d->boot1 = malloc(512); 474 if(!d->boot1) return -1; 475 memcpy(d->boot1, b1, 512); 476 if (d->boot2) free(d->boot2); 477 d->boot2 = malloc(15 * 512); 478 if(!d->boot2) return -1; 479 memcpy(d->boot2, b2, 15 * 512); 480#elif defined(__alpha__) 481 if (d->boot1) free(d->boot1); 482 d->boot1 = malloc(15 * 512); 483 if(!d->boot1) return -1; 484 memcpy(d->boot1, b1, 15 * 512); 485#elif defined(__sparc64__) 486 if (d->boot1 != NULL) 487 free(d->boot1); 488 d->boot1 = malloc(16 * 512); 489 if (d->boot1 == NULL) 490 return (-1); 491 memcpy(d->boot1, b1, 16 * 512); 492#elif defined(__ia64__) 493 /* nothing */ 494#else 495/* Should be: #error "Set_Boot_Blocks: unknown arch"; */ 496#endif 497 return 0; 498} 499 500#ifdef PC98 501const char * 502slice_type_name( int type, int subtype ) 503{ 504 505 switch (type) { 506 case 0: 507 return "whole"; 508 case 2: 509 return "fat"; 510 case 3: 511 switch (subtype) { 512 case 0xc494: return "freebsd"; 513 default: return "unknown"; 514 } 515 default: 516 return "unknown"; 517 } 518} 519#else /* PC98 */ 520const char * 521slice_type_name( int type, int subtype ) 522{ 523 524 switch (type) { 525 case 0: 526 return "whole"; 527 case 1: 528 switch (subtype) { 529 case 1: return "fat (12-bit)"; 530 case 2: return "XENIX /"; 531 case 3: return "XENIX /usr"; 532 case 4: return "fat (16-bit,<=32Mb)"; 533 case 5: return "extended DOS"; 534 case 6: return "fat (16-bit,>32Mb)"; 535 case 7: return "NTFS/HPFS/QNX"; 536 case 8: return "AIX bootable"; 537 case 9: return "AIX data"; 538 case 10: return "OS/2 bootmgr"; 539 case 11: return "fat (32-bit)"; 540 case 12: return "fat (32-bit,LBA)"; 541 case 14: return "fat (16-bit,>32Mb,LBA)"; 542 case 15: return "extended DOS, LBA"; 543 case 18: return "Compaq Diagnostic"; 544 case 84: return "OnTrack diskmgr"; 545 case 100: return "Netware 2.x"; 546 case 101: return "Netware 3.x"; 547 case 115: return "SCO UnixWare"; 548 case 128: return "Minix 1.1"; 549 case 129: return "Minix 1.5"; 550 case 130: return "linux_swap"; 551 case 131: return "ext2fs"; 552 case 166: return "OpenBSD FFS"; /* 0xA6 */ 553 case 169: return "NetBSD FFS"; /* 0xA9 */ 554 case 182: return "OpenBSD"; /* dedicated */ 555 case 183: return "bsd/os"; 556 case 184: return "bsd/os swap"; 557 case 238: return "EFI GPT"; 558 case 239: return "EFI Sys. Part."; 559 default: return "unknown"; 560 } 561 case 2: 562 return "fat"; 563 case 3: 564 switch (subtype) { 565 case 165: return "freebsd"; 566 default: return "unknown"; 567 } 568 case 4: 569 return "extended"; 570 case 5: 571 return "part"; 572 case 6: 573 return "unused"; 574 default: 575 return "unknown"; 576 } 577} 578#endif /* PC98 */ 579