/* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #ifndef PC98 #include #endif #include #include "libdisk.h" #ifndef PC98 #define HAVE_GEOM #endif #include #include #include #ifndef PC98 #define DOSPTYP_EXTENDED 5 #endif #ifdef DEBUG #define DPRINT(x) warn x #define DPRINTX(x) warnx x #else #define DPRINT(x) #define DPRINTX(x) #endif const char *chunk_n[] = { "whole", "unknown", "fat", "freebsd", "extended", "part", "unused", NULL }; struct disk * Open_Disk(const char *name) { return Int_Open_Disk(name, 0); } #ifndef PC98 static u_int32_t Read_Int32(u_int32_t *p) { u_int8_t *bp = (u_int8_t *)p; return bp[0] | (bp[1] << 8) | (bp[2] << 16) | (bp[3] << 24); } #endif /* * XXX BEGIN HACK XXX * Scan/parse the XML geom data to retrieve what we need to * carry out the work of Int_Open_Disk. This is a total hack * and should be replaced with a real XML parser. */ typedef enum { XML_MESH, XML_MESH_END, XML_CLASS, XML_CLASS_END, XML_GEOM, XML_GEOM_END, XML_CONFIG, XML_CONFIG_END, XML_PROVIDER, XML_PROVIDER_END, XML_NAME, XML_NAME_END, XML_INDEX, XML_INDEX_END, XML_SECLENGTH, XML_SECLENGTH_END, XML_SECOFFSET, XML_SECOFFSET_END, XML_TYPE, XML_TYPE_END, XML_MEDIASIZE, XML_MEDIASIZE_END, XML_SECTORSIZE, XML_SECTORSIZE_END, XML_FWHEADS, XML_FWHEADS_END, XML_FWSECTORS, XML_FWSECTORS_END, XML_OTHER, XML_OTHER_END } XMLToken; const struct { XMLToken t; const char* token; const char* name; } xmltokens[] = { { XML_MESH, "mesh", "XML_MESH" }, { XML_CLASS, "class", "XML_CLASS" }, { XML_GEOM, "geom", "XML_GEOM" }, { XML_CONFIG, "config", "XML_CONFIG" }, { XML_PROVIDER, "provider", "XML_PROVIDE" }, { XML_NAME, "name", "XML_NAME" }, { XML_INDEX, "index", "XML_INDEX" }, { XML_SECLENGTH, "seclength", "XML_SECLENGTH" }, { XML_SECOFFSET, "secoffset", "XML_SECOFFSET" }, { XML_TYPE, "type", "XML_TYPE" }, { XML_FWHEADS, "fwheads", "XML_FWHEADS" }, { XML_FWSECTORS, "fwsectors", "XML_FWSECTORS" }, { XML_MEDIASIZE, "mediasize", "XML_MEDIASIZE" }, { XML_SECTORSIZE, "sectorsize", "XML_SECTORSIZE" }, /* NB: this must be last */ { XML_OTHER, NULL, "XML_OTHER" }, }; #define N(x) (sizeof (x) / sizeof (x[0])) #ifdef DEBUG static const char* xmltokenname(XMLToken t) { int i; for (i = 0; i < N(xmltokens); i++) { if (t == xmltokens[i].t) return xmltokens[i].name; if ((t-1) == xmltokens[i].t) { static char tbuf[80]; snprintf(tbuf, sizeof (tbuf), "%s_END", xmltokens[i].name); return tbuf; } } return "???"; } #endif /*DEBUG*/ /* * Parse the next XML token delimited by <..>. If the token * has a "builtin terminator" (<... />) then just skip it and * go the next token. */ static int xmltoken(const char *start, const char **next, XMLToken *t) { const char *cp = start; const char *token; int i; again: while (*cp != '<') { if (*cp == '\0') { *next = cp; DPRINTX(("xmltoken: EOD")); return 0; } cp++; } token = ++cp; for (; *cp && *cp != '>' && !isspace(*cp); cp++) ; if (*cp == '\0') { *next = cp; DPRINTX(("xmltoken: EOD")); return 0; } *t = (*token == '/'); if (*t) token++; for (i = 0; xmltokens[i].token != NULL; i++) if (strncasecmp(token, xmltokens[i].token, cp-token) == 0) break; *t += xmltokens[i].t; /* now collect the remainder of the string */ for (; *cp != '>' && *cp != '\0'; cp++) ; if (*cp == '\0') { *next = cp; DPRINTX(("xmltoken: EOD")); return 0; } if (cp > token && cp[-1] == '/') { /* e.g. */ start = cp+1; goto again; } *next = cp+1; DPRINTX(("xmltoken: %s \"%.*s\"", xmltokenname(*t), cp-token, token)); return 1; } /* * Parse and discard XML up to the token terminator. */ static int discardxml(const char **next, XMLToken terminator) { const char *xml = *next; XMLToken t; DPRINTX(("discard XML up to %s", xmltokenname(terminator))); for (;;) { if (xmltoken(xml, next, &t) == 0) return EINVAL; if (t == terminator) break; if ((t & 1) == 0) { int error = discardxml(next, t+1); if (error) return error; } xml = *next; } return 0; } /* * Parse XML from between a range of markers; e.g. ... . * When the specified class name is located we descend looking for the * geometry information given by diskname. Once inside there we process * tags calling f back for each useful one. The arg is passed into f * for use in storing the parsed data. */ static int parsexmlpair( const char *xml, const char **next, const char *classname, XMLToken terminator, const char *diskname, int (*f)(void *, XMLToken, u_int *, u_int64_t), void *arg ) { const char *cp; XMLToken t; int error; u_int ix = (u_int) -1; DPRINTX(("parse XML up to %s", xmltokenname(terminator))); do { if (xmltoken(xml, next, &t) == 0) { error = EINVAL; break; } if (t == terminator) { error = 0; break; } if (t & 1) { /* w/o matching */ DPRINTX(("Unexpected token %s", xmltokenname(t))); error = EINVAL; break; } switch ((int) t) { case XML_NAME: for (cp = *next; *cp && *cp != '<'; cp++) ; if (*cp == '\0') { DPRINTX(("parsexmlpair: EOD")); error = EINVAL; goto done; } DPRINTX(("parsexmlpair: \"%.*s\"", cp-*next, *next)); switch ((int) terminator) { case XML_CLASS_END: if (strncasecmp(*next, classname, cp-*next)) return discardxml(next, terminator); break; case XML_GEOM_END: if (strncasecmp(*next, diskname, cp-*next)) return discardxml(next, terminator); break; } break; case XML_SECOFFSET: case XML_SECLENGTH: case XML_TYPE: if (ix == (u_int) -1) { DPRINTX(("parsexmlpair: slice data w/o " "preceding index")); error = EINVAL; goto done; } /* fall thru... */ case XML_INDEX: case XML_FWHEADS: case XML_FWSECTORS: case XML_MEDIASIZE: case XML_SECTORSIZE: if (terminator != XML_CONFIG_END && terminator != XML_PROVIDER_END) { DPRINTX(("parsexmlpair: %s in unexpected " "context: terminator %s", xmltokenname(t), xmltokenname(terminator))); error = EINVAL; goto done; } error = (*f)(arg, t, &ix, strtoull(*next, NULL, 10)); if (error) goto done; break; } error = parsexmlpair(*next, &xml, classname, t+1, diskname, f, arg); } while (error == 0); done: return error; } /* * XML parser. Just barely smart enough to handle the * gibberish that geom passed back from the kernel. */ static int xmlparse( const char *confxml, const char *classname, const char *diskname, int (*f)(void *, XMLToken, u_int *, u_int64_t), void *arg ) { const char *next; XMLToken t; int error; next = confxml; while (xmltoken(next, &next, &t) && t != XML_MESH) ; if (t == XML_MESH) error = parsexmlpair(next, &next, classname, XML_MESH_END, diskname, f, arg); else { DPRINTX(("xmlparse: expecting mesh token, got %s", xmltokenname(t))); error = EINVAL; } return (error ? -1 : 0); } /* * Callback to collect slice-related data. */ static int assignToSlice(void *arg, XMLToken t, u_int *slice, u_int64_t v) { struct diskslices *ds = (struct diskslices *) arg; switch ((int) t) { case XML_INDEX: *slice = BASE_SLICE + (u_int) v; if (*slice >= MAX_SLICES) { DPRINTX(("assignToSlice: invalid slice index %u > max %u", *slice, MAX_SLICES)); return EINVAL; } if (*slice >= ds->dss_nslices) ds->dss_nslices = (*slice)+1; break; case XML_SECOFFSET: ds->dss_slices[*slice].ds_offset = (u_long) v; break; case XML_SECLENGTH: ds->dss_slices[*slice].ds_size = (u_long) v; break; case XML_TYPE: ds->dss_slices[*slice].ds_type = (int) v; break; } return 0; } /* * Callback to collect disk-related data. */ static int assignToDisk(void *arg, XMLToken t, u_int *slice, u_int64_t v) { struct disklabel *dl = (struct disklabel *) arg; switch ((int) t) { case XML_FWHEADS: dl->d_ntracks = (u_int32_t) v; case XML_FWSECTORS: dl->d_nsectors = (u_int32_t) v; break; case XML_MEDIASIZE: /* store this temporarily; it gets moved later */ dl->d_secpercyl = v >> 32; dl->d_secperunit = v & 0xffffffff; break; case XML_SECTORSIZE: dl->d_secsize = (u_int32_t) v; break; } return 0; } #ifdef __i386__ /* * Callback to collect partition-related data. */ static int assignToPartition(void *arg, XMLToken t, u_int *part, u_int64_t v) { struct disklabel *dl = (struct disklabel *) arg; switch ((int) t) { case XML_INDEX: *part = (u_int) v; if (*part >= MAXPARTITIONS) { DPRINTX(("assignToPartition: invalid partition index %u > max %u", *part, MAXPARTITIONS)); return EINVAL; } if (*part >= dl->d_npartitions) dl->d_npartitions = (*part)+1; break; case XML_SECOFFSET: dl->d_partitions[*part].p_offset = (u_int32_t) v; break; case XML_SECLENGTH: dl->d_partitions[*part].p_size = (u_int32_t) v; break; case XML_TYPE: dl->d_partitions[*part].p_fstype = (u_int8_t) v; break; } return 0; } #endif /* __i386__ */ #undef N struct disk * Int_Open_Disk(const char *name, u_long size) { int i; int fd = -1; struct diskslices ds; struct disklabel dl; char device[64]; struct disk *d; #ifdef PC98 unsigned char *p; #else struct dos_partition *dp; void *p; #endif char *confxml = NULL; size_t xmlsize; u_int64_t mediasize; int error; strlcpy(device, _PATH_DEV, sizeof(device)); strlcat(device, name, sizeof(device)); d = (struct disk *)malloc(sizeof *d); if(!d) return NULL; memset(d, 0, sizeof *d); fd = open(device, O_RDONLY); if (fd < 0) { DPRINT(("open(%s) failed", device)); goto bad; } memset(&dl, 0, sizeof dl); memset(&ds, 0, sizeof ds); /* * Read and hack-parse the XML that provides the info we need. */ error = sysctlbyname("kern.geom.confxml", NULL, &xmlsize, NULL, 0); if (error) { warn("kern.geom.confxml sysctl not available, giving up!"); goto bad; } confxml = (char *) malloc(xmlsize+1); if (confxml == NULL) { DPRINT(("cannot malloc memory for confxml")); goto bad; } error = sysctlbyname("kern.geom.confxml", confxml, &xmlsize, NULL, 0); if (error) { DPRINT(("error reading kern.geom.confxml from the system")); goto bad; } confxml[xmlsize] = '\0'; /* in case kernel bug is still there */ if (xmlparse(confxml, "MBR", name, assignToSlice, &ds) != 0) { DPRINTX(("Error parsing MBR geometry specification.")); goto bad; } if (xmlparse(confxml, "DISK", name, assignToDisk, &dl) != 0) { DPRINTX(("Error parsing DISK geometry specification.")); goto bad; } if (dl.d_nsectors == 0) { DPRINTX(("No (zero) sector information in DISK geometry")); goto bad; } if (dl.d_ntracks == 0) { DPRINTX(("No (zero) track information in DISK geometry")); goto bad; } if (dl.d_secsize == 0) { DPRINTX(("No (zero) sector size information in DISK geometry")); goto bad; } if (dl.d_secpercyl == 0 && dl.d_secperunit == 0) { DPRINTX(("No (zero) media size information in DISK geometry")); goto bad; } /* * Now patch up disklabel and diskslice. */ d->sector_size = dl.d_secsize; /* NB: media size was stashed in two parts while parsing */ mediasize = (((u_int64_t) dl.d_secpercyl) << 32) + dl.d_secperunit; dl.d_secpercyl = 0; dl.d_secperunit = 0; size = mediasize / d->sector_size; dl.d_ncylinders = size / (dl.d_ntracks * dl.d_nsectors); /* "whole disk" slice maintained for compatibility */ ds.dss_slices[WHOLE_DISK_SLICE].ds_size = size; #ifdef PC98 p = (unsigned char*)read_block(fd, 1, d->sector_size); #else p = read_block(fd, 0, d->sector_size); dp = (struct dos_partition*)(p + DOSPARTOFF); for (i = 0; i < NDOSPART; i++) { if (Read_Int32(&dp->dp_start) >= size) continue; if (Read_Int32(&dp->dp_start) + Read_Int32(&dp->dp_size) >= size) continue; if (!Read_Int32(&dp->dp_size)) continue; } free(p); #endif d->bios_sect = dl.d_nsectors; d->bios_hd = dl.d_ntracks; d->name = strdup(name); if (dl.d_ntracks && dl.d_nsectors) d->bios_cyl = size / (dl.d_ntracks * dl.d_nsectors); if (Add_Chunk(d, 0, size, name, whole, 0, 0, "-")) DPRINT(("Failed to add 'whole' chunk")); #ifdef __i386__ #ifdef PC98 /* XXX -- Quick Hack! * Check MS-DOS MO */ if ((*p == 0xf0 || *p == 0xf8) && (*(p+1) == 0xff) && (*(p+2) == 0xff)) { Add_Chunk(d, 0, size, name, fat, 0xa0a0, 0, name); free(p); goto pc98_mo_done; } free(p); #endif /* PC98 */ for(i=BASE_SLICE;i ds.dss_slices[i].ds_size) continue; snprintf(pname, sizeof(pname), "%s%c", sname, j + 'a'); if (Add_Chunk(d, dl.d_partitions[j].p_offset + ds.dss_slices[i].ds_offset, dl.d_partitions[j].p_size, pname,part, dl.d_partitions[j].p_fstype, #ifdef PC98 0, ds.dss_slices[i].ds_name) && j != 3) #else 0, "") && j != 3) #endif DPRINT(( "Failed to add chunk for partition %c [%lu,%lu]", j + 'a', dl.d_partitions[j].p_offset, dl.d_partitions[j].p_size)); } } #endif /* __i386__ */ #ifdef __alpha__ { struct disklabel dl; char pname[20]; int j,k; strlcpy(pname, _PATH_DEV, sizeof(pname)); strlcat(pname, name, sizeof(pname)); j = open(pname, O_RDONLY); if (j < 0) { DPRINT(("open(%s)", pname)); goto nolabel; } k = ioctl(j, DIOCGDINFO, &dl); if (k < 0) { DPRINT(("ioctl(%s, DIOCGDINFO)", pname)); close(j); goto nolabel; } close(j); All_FreeBSD(d, 1); for(j = 0; j <= dl.d_npartitions; j++) { if (j == RAW_PART) continue; if (j == 3) continue; if (j == dl.d_npartitions) { j = 3; dl.d_npartitions = 0; } if (!dl.d_partitions[j].p_size) continue; if (dl.d_partitions[j].p_size + dl.d_partitions[j].p_offset > ds.dss_slices[WHOLE_DISK_SLICE].ds_size) continue; snprintf(pname, sizeof(pname), "%s%c", name, j + 'a'); if (Add_Chunk(d, dl.d_partitions[j].p_offset, dl.d_partitions[j].p_size, pname,part, dl.d_partitions[j].p_fstype, 0, "") && j != 3) DPRINT(( "Failed to add chunk for partition %c [%lu,%lu]", j + 'a', dl.d_partitions[j].p_offset, dl.d_partitions[j].p_size)); } nolabel:; } #endif /* __alpha__ */ #ifdef PC98 pc98_mo_done: #endif close(fd); Fixup_Names(d); return d; bad: if (confxml != NULL) free(confxml); if (fd >= 0) close(fd); return NULL; } void Debug_Disk(struct disk *d) { printf("Debug_Disk(%s)", d->name); #if 0 printf(" real_geom=%lu/%lu/%lu", d->real_cyl, d->real_hd, d->real_sect); #endif printf(" bios_geom=%lu/%lu/%lu = %lu\n", d->bios_cyl, d->bios_hd, d->bios_sect, d->bios_cyl * d->bios_hd * d->bios_sect); #if defined(PC98) printf(" boot1=%p, boot2=%p, bootipl=%p, bootmenu=%p\n", d->boot1, d->boot2, d->bootipl, d->bootmenu); #elif defined(__i386__) printf(" boot1=%p, boot2=%p, bootmgr=%p\n", d->boot1, d->boot2, d->bootmgr); #elif defined(__alpha__) printf(" boot1=%p, bootmgr=%p\n", d->boot1, d->bootmgr); #elif defined(__ia64__) printf("\n"); #else /* Should be: error "Debug_Disk: unknown arch"; */ #endif Debug_Chunk(d->chunks); } void Free_Disk(struct disk *d) { if(d->chunks) Free_Chunk(d->chunks); if(d->name) free(d->name); #ifdef PC98 if(d->bootipl) free(d->bootipl); if(d->bootmenu) free(d->bootmenu); #else #if !defined(__ia64__) if(d->bootmgr) free(d->bootmgr); #endif #endif #if !defined(__ia64__) if(d->boot1) free(d->boot1); #endif #if defined(__i386__) if(d->boot2) free(d->boot2); #endif free(d); } #if 0 void Collapse_Disk(struct disk *d) { while(Collapse_Chunk(d, d->chunks)) ; } #endif static int qstrcmp(const void* a, const void* b) { char *str1 = *(char**)a; char *str2 = *(char**)b; return strcmp(str1, str2); } char ** Disk_Names() { int disk_cnt; static char **disks; int error; size_t listsize; char *disklist; error = sysctlbyname("kern.disks", NULL, &listsize, NULL, 0); if (error) { warn("kern.disks sysctl not available"); return NULL; } disks = malloc(sizeof *disks * (1 + MAX_NO_DISKS)); if (disks == NULL) return NULL; disklist = (char *)malloc(listsize + 1); if (disklist == NULL) { free(disks); return NULL; } memset(disks,0,sizeof *disks * (1 + MAX_NO_DISKS)); memset(disklist, 0, listsize + 1); error = sysctlbyname("kern.disks", disklist, &listsize, NULL, 0); if (error) { free(disklist); free(disks); return NULL; } for (disk_cnt = 0; disk_cnt < MAX_NO_DISKS; disk_cnt++) { disks[disk_cnt] = strsep(&disklist, " "); if (disks[disk_cnt] == NULL) break; } qsort(disks, disk_cnt, sizeof(char*), qstrcmp); return disks; } #ifdef PC98 void Set_Boot_Mgr(struct disk *d, const u_char *bootipl, const size_t bootipl_size, const u_char *bootmenu, const size_t bootmenu_size) #else void Set_Boot_Mgr(struct disk *d, const u_char *b, const size_t s) #endif { #if !defined(__ia64__) #ifdef PC98 if (bootipl_size % d->sector_size != 0) return; if (d->bootipl) free(d->bootipl); if (!bootipl) { d->bootipl = NULL; } else { d->bootipl_size = bootipl_size; d->bootipl = malloc(bootipl_size); if(!d->bootipl) return; memcpy(d->bootipl, bootipl, bootipl_size); } if (bootmenu_size % d->sector_size != 0) return; if (d->bootmenu) free(d->bootmenu); if (!bootmenu) { d->bootmenu = NULL; } else { d->bootmenu_size = bootmenu_size; d->bootmenu = malloc(bootmenu_size); if(!d->bootmenu) return; memcpy(d->bootmenu, bootmenu, bootmenu_size); } #else if (s % d->sector_size != 0) return; if (d->bootmgr) free(d->bootmgr); if (!b) { d->bootmgr = NULL; } else { d->bootmgr_size = s; d->bootmgr = malloc(s); if(!d->bootmgr) return; memcpy(d->bootmgr, b, s); } #endif #endif } int Set_Boot_Blocks(struct disk *d, const u_char *b1, const u_char *b2) { #if defined(__i386__) if (d->boot1) free(d->boot1); d->boot1 = malloc(512); if(!d->boot1) return -1; memcpy(d->boot1, b1, 512); if (d->boot2) free(d->boot2); d->boot2 = malloc(15 * 512); if(!d->boot2) return -1; memcpy(d->boot2, b2, 15 * 512); #elif defined(__alpha__) if (d->boot1) free(d->boot1); d->boot1 = malloc(15 * 512); if(!d->boot1) return -1; memcpy(d->boot1, b1, 15 * 512); #elif defined(__ia64__) /* nothing */ #else /* Should be: #error "Set_Boot_Blocks: unknown arch"; */ #endif return 0; } const char * slice_type_name( int type, int subtype ) { switch (type) { case 0: return "whole"; #ifndef PC98 case 1: switch (subtype) { case 1: return "fat (12-bit)"; case 2: return "XENIX /"; case 3: return "XENIX /usr"; case 4: return "fat (16-bit,<=32Mb)"; case 5: return "extended DOS"; case 6: return "fat (16-bit,>32Mb)"; case 7: return "NTFS/HPFS/QNX"; case 8: return "AIX bootable"; case 9: return "AIX data"; case 10: return "OS/2 bootmgr"; case 11: return "fat (32-bit)"; case 12: return "fat (32-bit,LBA)"; case 14: return "fat (16-bit,>32Mb,LBA)"; case 15: return "extended DOS, LBA"; case 18: return "Compaq Diagnostic"; case 84: return "OnTrack diskmgr"; case 100: return "Netware 2.x"; case 101: return "Netware 3.x"; case 115: return "SCO UnixWare"; case 128: return "Minix 1.1"; case 129: return "Minix 1.5"; case 130: return "linux_swap"; case 131: return "ext2fs"; case 166: return "OpenBSD FFS"; /* 0xA6 */ case 169: return "NetBSD FFS"; /* 0xA9 */ case 182: return "OpenBSD"; /* dedicated */ case 183: return "bsd/os"; case 184: return "bsd/os swap"; case 238: return "EFI GPT"; case 239: return "EFI Sys. Part."; default: return "unknown"; } #endif case 2: return "fat"; case 3: switch (subtype) { #ifdef PC98 case 0xc494: return "freebsd"; #else case 165: return "freebsd"; #endif default: return "unknown"; } #ifndef PC98 case 4: return "extended"; case 5: return "part"; case 6: return "unused"; #endif default: return "unknown"; } }