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