disk.c revision 106238
1228690Sdes/* 2228690Sdes * ---------------------------------------------------------------------------- 3228690Sdes * "THE BEER-WARE LICENSE" (Revision 42): 4228690Sdes * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 5228690Sdes * can do whatever you want with this stuff. If we meet some day, and you think 6228690Sdes * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7228690Sdes * ---------------------------------------------------------------------------- 8228690Sdes */ 9263421Sdes 10228690Sdes#include <sys/cdefs.h> 11228690Sdes__FBSDID("$FreeBSD: head/lib/libdisk/disk.c 106238 2002-10-31 05:51:25Z nyan $"); 12228690Sdes 13247568Sdes#include <stdio.h> 14247568Sdes#include <stdlib.h> 15247568Sdes#include <unistd.h> 16228690Sdes#include <fcntl.h> 17228690Sdes#include <string.h> 18228690Sdes#include <inttypes.h> 19228690Sdes#include <err.h> 20228690Sdes#include <sys/sysctl.h> 21228690Sdes#include <sys/stdint.h> 22228690Sdes#include <sys/types.h> 23228690Sdes#include <sys/stat.h> 24228690Sdes#include <sys/ioctl.h> 25228690Sdes#include <sys/disklabel.h> 26228690Sdes#include <sys/diskslice.h> 27228690Sdes#include <sys/diskmbr.h> 28228690Sdes#include <paths.h> 29263421Sdes#include "libdisk.h" 30228690Sdes 31228690Sdes#include <ctype.h> 32228690Sdes#include <errno.h> 33228690Sdes#include <assert.h> 34228690Sdes 35228690Sdes#define DOSPTYP_EXTENDED 5 36228690Sdes#ifdef DEBUG 37263421Sdes#define DPRINT(x) warn x 38228690Sdes#define DPRINTX(x) warnx x 39228690Sdes#else 40228690Sdes#define DPRINT(x) 41228690Sdes#define DPRINTX(x) 42228690Sdes#endif 43228690Sdes 44228690Sdesconst char * 45228690Sdeschunk_name(chunk_e type) 46228690Sdes{ 47228690Sdes switch(type) { 48228690Sdes case unused: return ("unused"); 49228690Sdes case mbr: return ("mbr"); 50228690Sdes case part: return ("part"); 51228690Sdes case gpt: return ("gpt"); 52228690Sdes case pc98: return ("pc98"); 53228690Sdes case sun: return ("sun"); 54228690Sdes case freebsd: return ("freebsd"); 55228690Sdes case fat: return ("fat"); 56228690Sdes case spare: return ("spare"); 57228690Sdes default: return ("??"); 58228690Sdes } 59228690Sdes}; 60228690Sdes 61228690Sdesstruct disk * 62228690SdesOpen_Disk(const char *name) 63228690Sdes{ 64228690Sdes return Int_Open_Disk(name); 65228690Sdes} 66228690Sdes 67228690Sdesstruct disk * 68228690SdesInt_Open_Disk(const char *name) 69228690Sdes{ 70228690Sdes char *conftxt = NULL; 71228690Sdes struct disk *d; 72228690Sdes size_t txtsize; 73228690Sdes int error, i; 74228690Sdes char *p, *q, *r, *a, *b, *n, *t; 75228690Sdes off_t o, len, off; 76228690Sdes u_int l, s, ty, sc, hd, alt; 77228690Sdes off_t lo[10]; 78228690Sdes 79228690Sdes error = sysctlbyname("kern.geom.conftxt", NULL, &txtsize, NULL, 0); 80228690Sdes if (error) { 81228690Sdes warn("kern.geom.conftxt sysctl not available, giving up!"); 82228690Sdes return (NULL); 83228690Sdes } 84228690Sdes conftxt = (char *) malloc(txtsize+1); 85228690Sdes if (conftxt == NULL) { 86228690Sdes DPRINT(("cannot malloc memory for conftxt")); 87228690Sdes return (NULL); 88228690Sdes } 89228690Sdes error = sysctlbyname("kern.geom.conftxt", conftxt, &txtsize, NULL, 0); 90228690Sdes if (error) { 91228690Sdes DPRINT(("error reading kern.geom.conftxt from the system")); 92228690Sdes free(conftxt); 93228690Sdes return (NULL); 94228690Sdes } 95228690Sdes conftxt[txtsize] = '\0'; /* in case kernel bug is still there */ 96228690Sdes 97228690Sdes for (p = conftxt; p != NULL && *p; p = strchr(p, '\n')) { 98228690Sdes if (*p == '\n') 99228690Sdes p++; 100228690Sdes a = strsep(&p, " "); 101228690Sdes if (strcmp(a, "0")) 102228690Sdes continue; 103228690Sdes 104228690Sdes a = strsep(&p, " "); 105228690Sdes if (strcmp(a, "DISK")) 106228690Sdes continue; 107228690Sdes 108228690Sdes a = strsep(&p, " "); 109228690Sdes if (strcmp(a, name)) 110228690Sdes continue; 111228690Sdes break; 112228690Sdes } 113228690Sdes 114228690Sdes q = strchr(p, '\n'); 115228690Sdes if (q != NULL) 116228690Sdes *q++ = '\0'; 117228690Sdes 118228690Sdes d = (struct disk *)calloc(sizeof *d, 1); 119263421Sdes if(d == NULL) 120228690Sdes return NULL; 121228690Sdes 122228690Sdes d->name = strdup(name); 123228690Sdes 124228690Sdes a = strsep(&p, " "); /* length in bytes */ 125228690Sdes o = strtoimax(a, &r, 0); 126228690Sdes if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 127228690Sdes 128228690Sdes a = strsep(&p, " "); /* sectorsize */ 129228690Sdes s = strtoul(a, &r, 0); 130228690Sdes if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 131228690Sdes 132228690Sdes if (Add_Chunk(d, 0, o / s, name, whole, 0, 0, "-")) 133228690Sdes DPRINT(("Failed to add 'whole' chunk")); 134263421Sdes 135228690Sdes len = o / s; 136228690Sdes 137228690Sdes for (;;) { 138228690Sdes a = strsep(&p, " "); 139228690Sdes if (a == NULL) 140228690Sdes break; 141228690Sdes b = strsep(&p, " "); 142228690Sdes o = strtoul(b, &r, 0); 143228690Sdes if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 144228690Sdes if (!strcmp(a, "hd")) 145228690Sdes d->bios_hd = o; 146228690Sdes else if (!strcmp(a, "sc")) 147228690Sdes d->bios_sect = o; 148228690Sdes else 149263421Sdes printf("HUH ? <%s> <%s>\n", a, b); 150228690Sdes } 151228690Sdes 152228690Sdes p = q; 153228690Sdes lo[0] = 0; 154228690Sdes 155228690Sdes for (; p != NULL && *p; p = q) { 156228690Sdes q = strchr(p, '\n'); 157228690Sdes if (q != NULL) 158228690Sdes *q++ = '\0'; 159228690Sdes a = strsep(&p, " "); /* Index */ 160228690Sdes if (!strcmp(a, "0")) 161228690Sdes break; 162228690Sdes l = strtoimax(a, &r, 0); 163228690Sdes if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 164263421Sdes t = strsep(&p, " "); /* Type {SUN, BSD, MBR, GPT} */ 165228690Sdes n = strsep(&p, " "); /* name */ 166228690Sdes a = strsep(&p, " "); /* len */ 167228690Sdes len = strtoimax(a, &r, 0); 168228690Sdes if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 169228690Sdes a = strsep(&p, " "); /* secsize */ 170228690Sdes s = strtoimax(a, &r, 0); 171228690Sdes if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 172228690Sdes for (;;) { 173228690Sdes a = strsep(&p, " "); 174228690Sdes if (a == NULL) 175228690Sdes break; 176228690Sdes b = strsep(&p, " "); 177228690Sdes o = strtoimax(b, &r, 0); 178228690Sdes if (*r) { printf("BARF %d <%d>\n", __LINE__, *r); exit (0); } 179263421Sdes if (!strcmp(a, "o")) 180228690Sdes off = o; 181228690Sdes else if (!strcmp(a, "i")) 182228690Sdes i = o; 183228690Sdes else if (!strcmp(a, "ty")) 184228690Sdes ty = o; 185228690Sdes else if (!strcmp(a, "sc")) 186228690Sdes sc = o; 187228690Sdes else if (!strcmp(a, "hd")) 188228690Sdes hd = o; 189228690Sdes else if (!strcmp(a, "alt")) 190228690Sdes alt = o; 191228690Sdes } 192228690Sdes 193228690Sdes /* PLATFORM POLICY BEGIN ------------------------------------- */ 194263421Sdes if (platform == p_sparc64 && !strcmp(t, "SUN") && i == 2) 195228690Sdes continue; 196228690Sdes if (platform == p_sparc64 && !strcmp(t, "SUN") && 197228690Sdes d->chunks->part->part == NULL) { 198228690Sdes d->bios_hd = hd; 199228690Sdes d->bios_sect = sc; 200228690Sdes o = d->chunks->size / (hd * sc); 201228690Sdes o *= (hd * sc); 202228690Sdes o -= alt * hd * sc; 203228690Sdes if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-")) 204228690Sdes DPRINT(("Failed to add 'freebsd' chunk")); 205228690Sdes } 206228690Sdes if (platform == p_alpha && !strcmp(t, "BSD") && 207228690Sdes d->chunks->part->part == NULL) { 208228690Sdes o = d->chunks->size; 209228690Sdes if (Add_Chunk(d, 0, d->chunks->size, name, freebsd, 0, 0, "-")) 210228690Sdes DPRINT(("Failed to add 'freebsd' chunk")); 211228690Sdes } 212228690Sdes if (!strcmp(t, "BSD") && i == RAW_PART) 213228690Sdes continue; 214228690Sdes /* PLATFORM POLICY END --------------------------------------- */ 215228690Sdes 216228690Sdes off /= s; 217228690Sdes len /= s; 218228690Sdes off += lo[l - 1]; 219228690Sdes lo[l] = off; 220228690Sdes printf("%s [%s] %jd %jd\n", t, n, (intmax_t)(off / s), (intmax_t) (len / s)); 221228690Sdes if (!strcmp(t, "SUN")) 222228690Sdes i = Add_Chunk(d, off, len, n, part, 0, 0, 0); 223228690Sdes else if (!strcmp(t, "MBR") && ty == 165) 224228690Sdes i = Add_Chunk(d, off, len, n, freebsd, 0, 0, 0); 225228690Sdes else if (!strcmp(t, "MBR")) 226228690Sdes i = Add_Chunk(d, off, len, n, mbr, 0, 0, 0); 227228690Sdes else if (!strcmp(t, "BSD")) 228228690Sdes i = Add_Chunk(d, off, len, n, part, 0, 0, 0); 229228690Sdes else if (!strcmp(t, "PC98")) 230228690Sdes i = Add_Chunk(d, off, len, n, pc98, 0, 0, 0); 231228690Sdes else if (!strcmp(t, "GPT")) 232228690Sdes i = Add_Chunk(d, off, len, n, gpt, 0, 0, 0); 233228690Sdes else 234228690Sdes {printf("BARF %d\n", __LINE__); exit(0); } 235228690Sdes printf("error = %d\n", i); 236228690Sdes } 237228690Sdes /* PLATFORM POLICY BEGIN ------------------------------------- */ 238228690Sdes /* We have a chance to do things on a blank disk here */ 239228690Sdesprintf("c %p\n", d->chunks); 240228690Sdesprintf("c->p %p\n", d->chunks->part); 241228690Sdesprintf("c->p->p %p\n", d->chunks->part->part); 242228690Sdes if (platform == p_sparc64 && d->chunks->part->part == NULL) { 243228690Sdesprintf("HERE %d\n", __LINE__); 244228690Sdes hd = d->bios_hd; 245228690Sdes sc = d->bios_sect; 246228690Sdes o = d->chunks->size / (hd * sc); 247228690Sdes o *= (hd * sc); 248228690Sdes o -= 2 * hd * sc; 249228690Sdesprintf("HERE %d\n", __LINE__); 250228690Sdes if (Add_Chunk(d, 0, o, name, freebsd, 0, 0, "-")) 251228690Sdes DPRINT(("Failed to add 'freebsd' chunk")); 252228690Sdes } 253228690Sdes /* PLATFORM POLICY END --------------------------------------- */ 254228690Sdes 255228690Sdes return (d); 256228690Sdes i = 0; 257228690Sdes} 258228690Sdes 259228690Sdesvoid 260228690SdesDebug_Disk(struct disk *d) 261228690Sdes{ 262228690Sdes printf("Debug_Disk(%s)", d->name); 263228690Sdes#if 0 264228690Sdes printf(" real_geom=%lu/%lu/%lu", d->real_cyl, d->real_hd, d->real_sect); 265228690Sdes#endif 266228690Sdes printf(" bios_geom=%lu/%lu/%lu = %lu\n", 267228690Sdes d->bios_cyl, d->bios_hd, d->bios_sect, 268228690Sdes d->bios_cyl * d->bios_hd * d->bios_sect); 269228690Sdes#if defined(PC98) 270228690Sdes printf(" boot1=%p, boot2=%p, bootipl=%p, bootmenu=%p\n", 271228690Sdes d->boot1, d->boot2, d->bootipl, d->bootmenu); 272228690Sdes#elif defined(__i386__) 273247568Sdes printf(" boot1=%p, boot2=%p, bootmgr=%p\n", 274247568Sdes d->boot1, d->boot2, d->bootmgr); 275228690Sdes#elif defined(__alpha__) 276228690Sdes printf(" boot1=%p, bootmgr=%p\n", 277228690Sdes d->boot1, d->bootmgr); 278228690Sdes#elif defined(__ia64__) 279263421Sdes printf("\n"); 280263421Sdes#else 281263421Sdes/* Should be: error "Debug_Disk: unknown arch"; */ 282263421Sdes#endif 283263421Sdes Debug_Chunk(d->chunks); 284263421Sdes} 285263421Sdes 286263421Sdesvoid 287263421SdesFree_Disk(struct disk *d) 288263421Sdes{ 289263421Sdes if(d->chunks) Free_Chunk(d->chunks); 290263421Sdes if(d->name) free(d->name); 291263421Sdes#ifdef PC98 292263421Sdes if(d->bootipl) free(d->bootipl); 293263421Sdes if(d->bootmenu) free(d->bootmenu); 294263421Sdes#else 295263421Sdes#if !defined(__ia64__) 296263421Sdes if(d->bootmgr) free(d->bootmgr); 297228690Sdes#endif 298228690Sdes#endif 299228690Sdes#if !defined(__ia64__) 300228690Sdes if(d->boot1) free(d->boot1); 301228690Sdes#endif 302228690Sdes#if defined(__i386__) 303228690Sdes if(d->boot2) free(d->boot2); 304228690Sdes#endif 305228690Sdes free(d); 306228690Sdes} 307228690Sdes 308228690Sdes#if 0 309228690Sdesvoid 310228690SdesCollapse_Disk(struct disk *d) 311228690Sdes{ 312228690Sdes 313228690Sdes while(Collapse_Chunk(d, d->chunks)) 314228690Sdes ; 315228690Sdes} 316228690Sdes#endif 317228690Sdes 318228690Sdesstatic int 319228690Sdesqstrcmp(const void* a, const void* b) 320228690Sdes{ 321228690Sdes 322228690Sdes char *str1 = *(char**)a; 323263421Sdes char *str2 = *(char**)b; 324228690Sdes return strcmp(str1, str2); 325228690Sdes} 326228690Sdes 327228690Sdeschar ** 328263421SdesDisk_Names() 329228690Sdes{ 330228690Sdes int disk_cnt; 331228690Sdes static char **disks; 332228690Sdes int error; 333228690Sdes size_t listsize; 334228690Sdes char *disklist; 335228690Sdes 336228690Sdes error = sysctlbyname("kern.disks", NULL, &listsize, NULL, 0); 337228690Sdes if (error) { 338228690Sdes warn("kern.disks sysctl not available"); 339228690Sdes return NULL; 340228690Sdes } 341228690Sdes 342247568Sdes disks = malloc(sizeof *disks * (1 + MAX_NO_DISKS)); 343247568Sdes if (disks == NULL) 344247568Sdes return NULL; 345247568Sdes disklist = (char *)malloc(listsize + 1); 346247568Sdes if (disklist == NULL) { 347247568Sdes free(disks); 348247568Sdes return NULL; 349247568Sdes } 350228690Sdes memset(disks,0,sizeof *disks * (1 + MAX_NO_DISKS)); 351228690Sdes memset(disklist, 0, listsize + 1); 352228690Sdes error = sysctlbyname("kern.disks", disklist, &listsize, NULL, 0); 353263421Sdes if (error) { 354263421Sdes free(disklist); 355263421Sdes free(disks); 356263421Sdes return NULL; 357263421Sdes } 358263421Sdes for (disk_cnt = 0; disk_cnt < MAX_NO_DISKS; disk_cnt++) { 359263421Sdes disks[disk_cnt] = strsep(&disklist, " "); 360263421Sdes if (disks[disk_cnt] == NULL) 361263421Sdes break; 362228690Sdes } 363228690Sdes qsort(disks, disk_cnt, sizeof(char*), qstrcmp); 364228690Sdes return disks; 365228690Sdes} 366228690Sdes 367228690Sdes#ifdef PC98 368228690Sdesvoid 369228690SdesSet_Boot_Mgr(struct disk *d, const u_char *bootipl, const size_t bootipl_size, 370228690Sdes const u_char *bootmenu, const size_t bootmenu_size) 371228690Sdes#else 372228690Sdesvoid 373228690SdesSet_Boot_Mgr(struct disk *d, const u_char *b, const size_t s) 374228690Sdes#endif 375228690Sdes{ 376228690Sdes#if !defined(__ia64__) 377228690Sdes#ifdef PC98 378228690Sdes if (bootipl_size % d->sector_size != 0) 379228690Sdes return; 380228690Sdes if (d->bootipl) 381228690Sdes free(d->bootipl); 382228690Sdes if (!bootipl) { 383228690Sdes d->bootipl = NULL; 384228690Sdes } else { 385228690Sdes d->bootipl_size = bootipl_size; 386228690Sdes d->bootipl = malloc(bootipl_size); 387228690Sdes if(!d->bootipl) return; 388228690Sdes memcpy(d->bootipl, bootipl, bootipl_size); 389263421Sdes } 390263421Sdes 391228690Sdes if (bootmenu_size % d->sector_size != 0) 392228690Sdes return; 393228690Sdes if (d->bootmenu) 394228690Sdes free(d->bootmenu); 395228690Sdes if (!bootmenu) { 396228690Sdes d->bootmenu = NULL; 397228690Sdes } else { 398228690Sdes d->bootmenu_size = bootmenu_size; 399228690Sdes d->bootmenu = malloc(bootmenu_size); 400228690Sdes if(!d->bootmenu) return; 401228690Sdes memcpy(d->bootmenu, bootmenu, bootmenu_size); 402228690Sdes } 403228690Sdes#else 404228690Sdes if (s % d->sector_size != 0) 405228690Sdes return; 406228690Sdes if (d->bootmgr) 407228690Sdes free(d->bootmgr); 408228690Sdes if (!b) { 409228690Sdes d->bootmgr = NULL; 410228690Sdes } else { 411228690Sdes d->bootmgr_size = s; 412228690Sdes d->bootmgr = malloc(s); 413228690Sdes if(!d->bootmgr) return; 414228690Sdes memcpy(d->bootmgr, b, s); 415228690Sdes } 416228690Sdes#endif 417228690Sdes#endif 418228690Sdes} 419228690Sdes 420228690Sdesint 421228690SdesSet_Boot_Blocks(struct disk *d, const u_char *b1, const u_char *b2) 422228690Sdes{ 423228690Sdes#if defined(__i386__) 424228690Sdes if (d->boot1) free(d->boot1); 425228690Sdes d->boot1 = malloc(512); 426228690Sdes if(!d->boot1) return -1; 427228690Sdes memcpy(d->boot1, b1, 512); 428228690Sdes if (d->boot2) free(d->boot2); 429228690Sdes d->boot2 = malloc(15 * 512); 430228690Sdes if(!d->boot2) return -1; 431228690Sdes memcpy(d->boot2, b2, 15 * 512); 432228690Sdes#elif defined(__alpha__) 433228690Sdes if (d->boot1) free(d->boot1); 434228690Sdes d->boot1 = malloc(15 * 512); 435228690Sdes if(!d->boot1) return -1; 436228690Sdes memcpy(d->boot1, b1, 15 * 512); 437228690Sdes#elif defined(__sparc64__) 438228690Sdes if (d->boot1 != NULL) 439228690Sdes free(d->boot1); 440228690Sdes d->boot1 = malloc(16 * 512); 441228690Sdes if (d->boot1 == NULL) 442228690Sdes return (-1); 443228690Sdes memcpy(d->boot1, b1, 16 * 512); 444228690Sdes#elif defined(__ia64__) 445228690Sdes /* nothing */ 446228690Sdes#else 447228690Sdes/* Should be: #error "Set_Boot_Blocks: unknown arch"; */ 448228690Sdes#endif 449228690Sdes return 0; 450228690Sdes} 451228690Sdes 452228690Sdes#ifdef PC98 453228690Sdesconst char * 454228690Sdesslice_type_name( int type, int subtype ) 455228690Sdes{ 456228690Sdes 457228690Sdes switch (type) { 458228690Sdes case 0: 459228690Sdes return "whole"; 460228690Sdes case 2: 461228690Sdes return "fat"; 462228690Sdes case 3: 463228690Sdes switch (subtype) { 464228690Sdes case 0xc494: return "freebsd"; 465228690Sdes default: return "unknown"; 466228690Sdes } 467228690Sdes default: 468 return "unknown"; 469 } 470} 471#else /* PC98 */ 472const char * 473slice_type_name( int type, int subtype ) 474{ 475 476 switch (type) { 477 case 0: 478 return "whole"; 479 case 1: 480 switch (subtype) { 481 case 1: return "fat (12-bit)"; 482 case 2: return "XENIX /"; 483 case 3: return "XENIX /usr"; 484 case 4: return "fat (16-bit,<=32Mb)"; 485 case 5: return "extended DOS"; 486 case 6: return "fat (16-bit,>32Mb)"; 487 case 7: return "NTFS/HPFS/QNX"; 488 case 8: return "AIX bootable"; 489 case 9: return "AIX data"; 490 case 10: return "OS/2 bootmgr"; 491 case 11: return "fat (32-bit)"; 492 case 12: return "fat (32-bit,LBA)"; 493 case 14: return "fat (16-bit,>32Mb,LBA)"; 494 case 15: return "extended DOS, LBA"; 495 case 18: return "Compaq Diagnostic"; 496 case 84: return "OnTrack diskmgr"; 497 case 100: return "Netware 2.x"; 498 case 101: return "Netware 3.x"; 499 case 115: return "SCO UnixWare"; 500 case 128: return "Minix 1.1"; 501 case 129: return "Minix 1.5"; 502 case 130: return "linux_swap"; 503 case 131: return "ext2fs"; 504 case 166: return "OpenBSD FFS"; /* 0xA6 */ 505 case 169: return "NetBSD FFS"; /* 0xA9 */ 506 case 182: return "OpenBSD"; /* dedicated */ 507 case 183: return "bsd/os"; 508 case 184: return "bsd/os swap"; 509 case 238: return "EFI GPT"; 510 case 239: return "EFI Sys. Part."; 511 default: return "unknown"; 512 } 513 case 2: 514 return "fat"; 515 case 3: 516 switch (subtype) { 517 case 165: return "freebsd"; 518 default: return "unknown"; 519 } 520 case 4: 521 return "extended"; 522 case 5: 523 return "part"; 524 case 6: 525 return "unused"; 526 default: 527 return "unknown"; 528 } 529} 530#endif /* PC98 */ 531