g_part_mbr.c revision 270552
169953Smsmith/*- 269953Smsmith * Copyright (c) 2007, 2008 Marcel Moolenaar 369953Smsmith * All rights reserved. 469953Smsmith * 569953Smsmith * Redistribution and use in source and binary forms, with or without 669953Smsmith * modification, are permitted provided that the following conditions 769953Smsmith * are met: 869953Smsmith * 969953Smsmith * 1. Redistributions of source code must retain the above copyright 1069953Smsmith * notice, this list of conditions and the following disclaimer. 1169953Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1269953Smsmith * notice, this list of conditions and the following disclaimer in the 1369953Smsmith * documentation and/or other materials provided with the distribution. 1469953Smsmith * 1569953Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1669953Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1769953Smsmith * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1869953Smsmith * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1969953Smsmith * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2069953Smsmith * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2169953Smsmith * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2269953Smsmith * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2369953Smsmith * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2469953Smsmith * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2569953Smsmith */ 2669953Smsmith 2769953Smsmith#include <sys/cdefs.h> 2869953Smsmith__FBSDID("$FreeBSD: stable/10/sys/geom/part/g_part_mbr.c 270552 2014-08-25 12:49:10Z ae $"); 2969953Smsmith 3069953Smsmith#include <sys/param.h> 3169953Smsmith#include <sys/bio.h> 3269953Smsmith#include <sys/diskmbr.h> 3369953Smsmith#include <sys/endian.h> 3469953Smsmith#include <sys/kernel.h> 3569953Smsmith#include <sys/kobj.h> 3669953Smsmith#include <sys/limits.h> 3769953Smsmith#include <sys/lock.h> 3869953Smsmith#include <sys/malloc.h> 3969953Smsmith#include <sys/mutex.h> 4069953Smsmith#include <sys/queue.h> 4169953Smsmith#include <sys/sbuf.h> 4269953Smsmith#include <sys/systm.h> 4369953Smsmith#include <sys/sysctl.h> 4469953Smsmith#include <geom/geom.h> 4569953Smsmith#include <geom/geom_int.h> 4669953Smsmith#include <geom/part/g_part.h> 4769953Smsmith 4869953Smsmith#include "g_part_if.h" 4969953Smsmith 5069953SmsmithFEATURE(geom_part_mbr, "GEOM partitioning class for MBR support"); 5169953Smsmith 5269953SmsmithSYSCTL_DECL(_kern_geom_part); 5369953Smsmithstatic SYSCTL_NODE(_kern_geom_part, OID_AUTO, mbr, CTLFLAG_RW, 0, 5469953Smsmith "GEOM_PART_MBR Master Boot Record"); 5569953Smsmith 5669953Smsmithstatic u_int enforce_chs = 1; 5769953SmsmithSYSCTL_UINT(_kern_geom_part_mbr, OID_AUTO, enforce_chs, 5869953Smsmith CTLFLAG_RWTUN, &enforce_chs, 1, "Enforce alignment to CHS addressing"); 5969953Smsmith 6069953Smsmith#define MBRSIZE 512 6169953Smsmith 6269953Smsmithstruct g_part_mbr_table { 6369953Smsmith struct g_part_table base; 6469953Smsmith u_char mbr[MBRSIZE]; 6569953Smsmith}; 6669953Smsmith 6769953Smsmithstruct g_part_mbr_entry { 6869953Smsmith struct g_part_entry base; 6969953Smsmith struct dos_partition ent; 7069953Smsmith}; 7169953Smsmith 7269953Smsmithstatic int g_part_mbr_add(struct g_part_table *, struct g_part_entry *, 7369953Smsmith struct g_part_parms *); 7469953Smsmithstatic int g_part_mbr_bootcode(struct g_part_table *, struct g_part_parms *); 7569953Smsmithstatic int g_part_mbr_create(struct g_part_table *, struct g_part_parms *); 7669953Smsmithstatic int g_part_mbr_destroy(struct g_part_table *, struct g_part_parms *); 7769953Smsmithstatic void g_part_mbr_dumpconf(struct g_part_table *, struct g_part_entry *, 7869953Smsmith struct sbuf *, const char *); 7969953Smsmithstatic int g_part_mbr_dumpto(struct g_part_table *, struct g_part_entry *); 8069953Smsmithstatic int g_part_mbr_modify(struct g_part_table *, struct g_part_entry *, 8169953Smsmith struct g_part_parms *); 8269953Smsmithstatic const char *g_part_mbr_name(struct g_part_table *, struct g_part_entry *, 8369953Smsmith char *, size_t); 8469953Smsmithstatic int g_part_mbr_probe(struct g_part_table *, struct g_consumer *); 8569953Smsmithstatic int g_part_mbr_read(struct g_part_table *, struct g_consumer *); 8669953Smsmithstatic int g_part_mbr_setunset(struct g_part_table *, struct g_part_entry *, 8769953Smsmith const char *, unsigned int); 8869953Smsmithstatic const char *g_part_mbr_type(struct g_part_table *, struct g_part_entry *, 8969953Smsmith char *, size_t); 9069953Smsmithstatic int g_part_mbr_write(struct g_part_table *, struct g_consumer *); 9169953Smsmithstatic int g_part_mbr_resize(struct g_part_table *, struct g_part_entry *, 9269953Smsmith struct g_part_parms *); 9369953Smsmith 9469953Smsmithstatic kobj_method_t g_part_mbr_methods[] = { 9569953Smsmith KOBJMETHOD(g_part_add, g_part_mbr_add), 9669953Smsmith KOBJMETHOD(g_part_bootcode, g_part_mbr_bootcode), 9769953Smsmith KOBJMETHOD(g_part_create, g_part_mbr_create), 9869953Smsmith KOBJMETHOD(g_part_destroy, g_part_mbr_destroy), 9969953Smsmith KOBJMETHOD(g_part_dumpconf, g_part_mbr_dumpconf), 10069953Smsmith KOBJMETHOD(g_part_dumpto, g_part_mbr_dumpto), 10169953Smsmith KOBJMETHOD(g_part_modify, g_part_mbr_modify), 10269953Smsmith KOBJMETHOD(g_part_resize, g_part_mbr_resize), 10369953Smsmith KOBJMETHOD(g_part_name, g_part_mbr_name), 10469953Smsmith KOBJMETHOD(g_part_probe, g_part_mbr_probe), 10569953Smsmith KOBJMETHOD(g_part_read, g_part_mbr_read), 10669953Smsmith KOBJMETHOD(g_part_setunset, g_part_mbr_setunset), 10769953Smsmith KOBJMETHOD(g_part_type, g_part_mbr_type), 10869953Smsmith KOBJMETHOD(g_part_write, g_part_mbr_write), 10969953Smsmith { 0, 0 } 11069953Smsmith}; 11169953Smsmith 11269953Smsmithstatic struct g_part_scheme g_part_mbr_scheme = { 11369953Smsmith "MBR", 11469953Smsmith g_part_mbr_methods, 11569953Smsmith sizeof(struct g_part_mbr_table), 11669953Smsmith .gps_entrysz = sizeof(struct g_part_mbr_entry), 11769953Smsmith .gps_minent = NDOSPART, 11869953Smsmith .gps_maxent = NDOSPART, 11969953Smsmith .gps_bootcodesz = MBRSIZE, 12069953Smsmith}; 12169953SmsmithG_PART_SCHEME_DECLARE(g_part_mbr); 12269953Smsmith 12369953Smsmithstatic struct g_part_mbr_alias { 12469953Smsmith u_char typ; 12569953Smsmith int alias; 12669953Smsmith} mbr_alias_match[] = { 12769953Smsmith { DOSPTYP_386BSD, G_PART_ALIAS_FREEBSD }, 12869953Smsmith { DOSPTYP_EXT, G_PART_ALIAS_EBR }, 12969953Smsmith { DOSPTYP_NTFS, G_PART_ALIAS_MS_NTFS }, 13069953Smsmith { DOSPTYP_FAT16, G_PART_ALIAS_MS_FAT16 }, 13169953Smsmith { DOSPTYP_FAT32, G_PART_ALIAS_MS_FAT32 }, 13269953Smsmith { DOSPTYP_EXTLBA, G_PART_ALIAS_EBR }, 13369953Smsmith { DOSPTYP_LDM, G_PART_ALIAS_MS_LDM_DATA }, 13469953Smsmith { DOSPTYP_LINSWP, G_PART_ALIAS_LINUX_SWAP }, 13569953Smsmith { DOSPTYP_LINUX, G_PART_ALIAS_LINUX_DATA }, 13669953Smsmith { DOSPTYP_LINLVM, G_PART_ALIAS_LINUX_LVM }, 13769953Smsmith { DOSPTYP_LINRAID, G_PART_ALIAS_LINUX_RAID }, 13869953Smsmith { DOSPTYP_PPCBOOT, G_PART_ALIAS_FREEBSD_BOOT }, 13969953Smsmith { DOSPTYP_VMFS, G_PART_ALIAS_VMFS }, 14069953Smsmith { DOSPTYP_VMKDIAG, G_PART_ALIAS_VMKDIAG }, 14169953Smsmith}; 14269953Smsmith 14369953Smsmithstatic int 14469953Smsmithmbr_parse_type(const char *type, u_char *dp_typ) 14569953Smsmith{ 14669953Smsmith const char *alias; 14769953Smsmith char *endp; 14869953Smsmith long lt; 14969953Smsmith int i; 15069953Smsmith 15169953Smsmith if (type[0] == '!') { 15269953Smsmith lt = strtol(type + 1, &endp, 0); 15369953Smsmith if (type[1] == '\0' || *endp != '\0' || lt <= 0 || lt >= 256) 15469953Smsmith return (EINVAL); 15569953Smsmith *dp_typ = (u_char)lt; 15669953Smsmith return (0); 15769953Smsmith } 15869953Smsmith for (i = 0; 15969953Smsmith i < sizeof(mbr_alias_match) / sizeof(mbr_alias_match[0]); i++) { 16069953Smsmith alias = g_part_alias_name(mbr_alias_match[i].alias); 16169953Smsmith if (strcasecmp(type, alias) == 0) { 16269953Smsmith *dp_typ = mbr_alias_match[i].typ; 16369953Smsmith return (0); 16469953Smsmith } 16569953Smsmith } 16669953Smsmith return (EINVAL); 16769953Smsmith} 16869953Smsmith 16969953Smsmithstatic int 17069953Smsmithmbr_probe_bpb(u_char *bpb) 17169953Smsmith{ 17269953Smsmith uint16_t secsz; 17369953Smsmith uint8_t clstsz; 17469953Smsmith 17569953Smsmith#define PO2(x) ((x & (x - 1)) == 0) 17669953Smsmith secsz = le16dec(bpb); 17769953Smsmith if (secsz < 512 || secsz > 4096 || !PO2(secsz)) 17869953Smsmith return (0); 17969953Smsmith clstsz = bpb[2]; 18069953Smsmith if (clstsz < 1 || clstsz > 128 || !PO2(clstsz)) 18169953Smsmith return (0); 18269953Smsmith#undef PO2 18369953Smsmith 18469953Smsmith return (1); 18569953Smsmith} 18669953Smsmith 18769953Smsmithstatic void 18869953Smsmithmbr_set_chs(struct g_part_table *table, uint32_t lba, u_char *cylp, u_char *hdp, 18969953Smsmith u_char *secp) 19069953Smsmith{ 19169953Smsmith uint32_t cyl, hd, sec; 19269953Smsmith 19369953Smsmith sec = lba % table->gpt_sectors + 1; 19469953Smsmith lba /= table->gpt_sectors; 19569953Smsmith hd = lba % table->gpt_heads; 19669953Smsmith lba /= table->gpt_heads; 19769953Smsmith cyl = lba; 19869953Smsmith if (cyl > 1023) 19969953Smsmith sec = hd = cyl = ~0; 20069953Smsmith 20169953Smsmith *cylp = cyl & 0xff; 20269953Smsmith *hdp = hd & 0xff; 20369953Smsmith *secp = (sec & 0x3f) | ((cyl >> 2) & 0xc0); 20469953Smsmith} 20569953Smsmith 20669953Smsmithstatic int 20769953Smsmithmbr_align(struct g_part_table *basetable, uint32_t *start, uint32_t *size) 20869953Smsmith{ 20969953Smsmith uint32_t sectors; 21069953Smsmith 21169953Smsmith if (enforce_chs == 0) 21269953Smsmith return (0); 21369953Smsmith sectors = basetable->gpt_sectors; 21469953Smsmith if (*size < sectors) 21569953Smsmith return (EINVAL); 21669953Smsmith if (start != NULL && (*start % sectors)) { 21769953Smsmith *size += (*start % sectors) - sectors; 21869953Smsmith *start -= (*start % sectors) - sectors; 21969953Smsmith } 22069953Smsmith if (*size % sectors) 22169953Smsmith *size -= (*size % sectors); 22269953Smsmith if (*size < sectors) 22369953Smsmith return (EINVAL); 22469953Smsmith return (0); 22569953Smsmith} 22669953Smsmith 22769953Smsmithstatic int 22869953Smsmithg_part_mbr_add(struct g_part_table *basetable, struct g_part_entry *baseentry, 22969953Smsmith struct g_part_parms *gpp) 23069953Smsmith{ 23169953Smsmith struct g_part_mbr_entry *entry; 23269953Smsmith uint32_t start, size; 23369953Smsmith 23469953Smsmith if (gpp->gpp_parms & G_PART_PARM_LABEL) 23569953Smsmith return (EINVAL); 23669953Smsmith 23769953Smsmith entry = (struct g_part_mbr_entry *)baseentry; 23869953Smsmith start = gpp->gpp_start; 23969953Smsmith size = gpp->gpp_size; 24069953Smsmith if (mbr_align(basetable, &start, &size) != 0) 24169953Smsmith return (EINVAL); 24269953Smsmith if (baseentry->gpe_deleted) 24369953Smsmith bzero(&entry->ent, sizeof(entry->ent)); 24469953Smsmith 24569953Smsmith KASSERT(baseentry->gpe_start <= start, ("%s", __func__)); 24669953Smsmith KASSERT(baseentry->gpe_end >= start + size - 1, ("%s", __func__)); 24769953Smsmith baseentry->gpe_start = start; 24869953Smsmith baseentry->gpe_end = start + size - 1; 24969953Smsmith entry->ent.dp_start = start; 25069953Smsmith entry->ent.dp_size = size; 25169953Smsmith mbr_set_chs(basetable, baseentry->gpe_start, &entry->ent.dp_scyl, 25269953Smsmith &entry->ent.dp_shd, &entry->ent.dp_ssect); 25369953Smsmith mbr_set_chs(basetable, baseentry->gpe_end, &entry->ent.dp_ecyl, 25469953Smsmith &entry->ent.dp_ehd, &entry->ent.dp_esect); 25569953Smsmith return (mbr_parse_type(gpp->gpp_type, &entry->ent.dp_typ)); 25669953Smsmith} 25769953Smsmith 25869953Smsmithstatic int 25969953Smsmithg_part_mbr_bootcode(struct g_part_table *basetable, struct g_part_parms *gpp) 26069953Smsmith{ 26169953Smsmith struct g_part_mbr_table *table; 26269953Smsmith uint32_t dsn; 26369953Smsmith 26469953Smsmith if (gpp->gpp_codesize != MBRSIZE) 26569953Smsmith return (ENODEV); 26669953Smsmith 26769953Smsmith table = (struct g_part_mbr_table *)basetable; 26869953Smsmith dsn = *(uint32_t *)(table->mbr + DOSDSNOFF); 26969953Smsmith bcopy(gpp->gpp_codeptr, table->mbr, DOSPARTOFF); 27069953Smsmith if (dsn != 0) 27169953Smsmith *(uint32_t *)(table->mbr + DOSDSNOFF) = dsn; 27269953Smsmith return (0); 27369953Smsmith} 27469953Smsmith 27569953Smsmithstatic int 27669953Smsmithg_part_mbr_create(struct g_part_table *basetable, struct g_part_parms *gpp) 27769953Smsmith{ 27869953Smsmith struct g_provider *pp; 27969953Smsmith struct g_part_mbr_table *table; 28069953Smsmith 28169953Smsmith pp = gpp->gpp_provider; 28269953Smsmith if (pp->sectorsize < MBRSIZE) 28369953Smsmith return (ENOSPC); 28469953Smsmith 28569953Smsmith basetable->gpt_first = basetable->gpt_sectors; 28669953Smsmith basetable->gpt_last = MIN(pp->mediasize / pp->sectorsize, 28769953Smsmith UINT32_MAX) - 1; 28869953Smsmith 28969953Smsmith table = (struct g_part_mbr_table *)basetable; 29069953Smsmith le16enc(table->mbr + DOSMAGICOFFSET, DOSMAGIC); 29169953Smsmith return (0); 29269953Smsmith} 29369953Smsmith 29469953Smsmithstatic int 29569953Smsmithg_part_mbr_destroy(struct g_part_table *basetable, struct g_part_parms *gpp) 29669953Smsmith{ 29769953Smsmith 29869953Smsmith /* Wipe the first sector to clear the partitioning. */ 29969953Smsmith basetable->gpt_smhead |= 1; 30069953Smsmith return (0); 30169953Smsmith} 30269953Smsmith 30369953Smsmithstatic void 30469953Smsmithg_part_mbr_dumpconf(struct g_part_table *table, struct g_part_entry *baseentry, 30569953Smsmith struct sbuf *sb, const char *indent) 30669953Smsmith{ 30769953Smsmith struct g_part_mbr_entry *entry; 30869953Smsmith 30969953Smsmith entry = (struct g_part_mbr_entry *)baseentry; 31069953Smsmith if (indent == NULL) { 31169953Smsmith /* conftxt: libdisk compatibility */ 31269953Smsmith sbuf_printf(sb, " xs MBR xt %u", entry->ent.dp_typ); 31369953Smsmith } else if (entry != NULL) { 31469953Smsmith /* confxml: partition entry information */ 31569953Smsmith sbuf_printf(sb, "%s<rawtype>%u</rawtype>\n", indent, 31669953Smsmith entry->ent.dp_typ); 31769953Smsmith if (entry->ent.dp_flag & 0x80) 31869953Smsmith sbuf_printf(sb, "%s<attrib>active</attrib>\n", indent); 31969953Smsmith } else { 32069953Smsmith /* confxml: scheme information */ 32169953Smsmith } 32269953Smsmith} 32369953Smsmith 32469953Smsmithstatic int 32569953Smsmithg_part_mbr_dumpto(struct g_part_table *table, struct g_part_entry *baseentry) 32669953Smsmith{ 32769953Smsmith struct g_part_mbr_entry *entry; 32869953Smsmith 32969953Smsmith /* Allow dumping to a FreeBSD partition or Linux swap partition only. */ 33069953Smsmith entry = (struct g_part_mbr_entry *)baseentry; 33169953Smsmith return ((entry->ent.dp_typ == DOSPTYP_386BSD || 33269953Smsmith entry->ent.dp_typ == DOSPTYP_LINSWP) ? 1 : 0); 33369953Smsmith} 33469953Smsmith 33569953Smsmithstatic int 33669953Smsmithg_part_mbr_modify(struct g_part_table *basetable, 33769953Smsmith struct g_part_entry *baseentry, struct g_part_parms *gpp) 33869953Smsmith{ 33969953Smsmith struct g_part_mbr_entry *entry; 34069953Smsmith 34169953Smsmith if (gpp->gpp_parms & G_PART_PARM_LABEL) 34269953Smsmith return (EINVAL); 34369953Smsmith 34469953Smsmith entry = (struct g_part_mbr_entry *)baseentry; 34569953Smsmith if (gpp->gpp_parms & G_PART_PARM_TYPE) 34669953Smsmith return (mbr_parse_type(gpp->gpp_type, &entry->ent.dp_typ)); 34769953Smsmith return (0); 34869953Smsmith} 34969953Smsmith 35069953Smsmithstatic int 35169953Smsmithg_part_mbr_resize(struct g_part_table *basetable, 35269953Smsmith struct g_part_entry *baseentry, struct g_part_parms *gpp) 35369953Smsmith{ 35469953Smsmith struct g_part_mbr_entry *entry; 35569953Smsmith struct g_provider *pp; 35669953Smsmith uint32_t size; 35769953Smsmith 35869953Smsmith if (baseentry == NULL) { 35969953Smsmith pp = LIST_FIRST(&basetable->gpt_gp->consumer)->provider; 36069953Smsmith basetable->gpt_last = MIN(pp->mediasize / pp->sectorsize, 36169953Smsmith UINT32_MAX) - 1; 36269953Smsmith return (0); 36369953Smsmith } 36469953Smsmith size = gpp->gpp_size; 36569953Smsmith if (mbr_align(basetable, NULL, &size) != 0) 36669953Smsmith return (EINVAL); 36769953Smsmith /* XXX: prevent unexpected shrinking. */ 36869953Smsmith pp = baseentry->gpe_pp; 36969953Smsmith if ((g_debugflags & 0x10) == 0 && size < gpp->gpp_size && 37069953Smsmith pp->mediasize / pp->sectorsize > size) 37169953Smsmith return (EBUSY); 37269953Smsmith entry = (struct g_part_mbr_entry *)baseentry; 37369953Smsmith baseentry->gpe_end = baseentry->gpe_start + size - 1; 37469953Smsmith entry->ent.dp_size = size; 37569953Smsmith mbr_set_chs(basetable, baseentry->gpe_end, &entry->ent.dp_ecyl, 37669953Smsmith &entry->ent.dp_ehd, &entry->ent.dp_esect); 37769953Smsmith return (0); 37869953Smsmith} 37969953Smsmith 38069953Smsmithstatic const char * 38169953Smsmithg_part_mbr_name(struct g_part_table *table, struct g_part_entry *baseentry, 38269953Smsmith char *buf, size_t bufsz) 38369953Smsmith{ 38469953Smsmith 38569953Smsmith snprintf(buf, bufsz, "s%d", baseentry->gpe_index); 38669953Smsmith return (buf); 38769953Smsmith} 38869953Smsmith 38969953Smsmithstatic int 39069953Smsmithg_part_mbr_probe(struct g_part_table *table, struct g_consumer *cp) 39169953Smsmith{ 39269953Smsmith char psn[8]; 39369953Smsmith struct g_provider *pp; 39469953Smsmith u_char *buf, *p; 39569953Smsmith int error, index, res, sum; 39669953Smsmith uint16_t magic; 39769953Smsmith 39869953Smsmith pp = cp->provider; 39969953Smsmith 40069953Smsmith /* Sanity-check the provider. */ 40169953Smsmith if (pp->sectorsize < MBRSIZE || pp->mediasize < pp->sectorsize) 40269953Smsmith return (ENOSPC); 40369953Smsmith if (pp->sectorsize > 4096) 40469953Smsmith return (ENXIO); 40569953Smsmith 40669953Smsmith /* We don't nest under an MBR (see EBR instead). */ 40769953Smsmith error = g_getattr("PART::scheme", cp, &psn); 40869953Smsmith if (error == 0 && strcmp(psn, g_part_mbr_scheme.name) == 0) 40969953Smsmith return (ELOOP); 41069953Smsmith 41169953Smsmith /* Check that there's a MBR. */ 41269953Smsmith buf = g_read_data(cp, 0L, pp->sectorsize, &error); 41369953Smsmith if (buf == NULL) 41469953Smsmith return (error); 41569953Smsmith 41669953Smsmith /* We goto out on mismatch. */ 41769953Smsmith res = ENXIO; 41869953Smsmith 41969953Smsmith magic = le16dec(buf + DOSMAGICOFFSET); 42069953Smsmith if (magic != DOSMAGIC) 42169953Smsmith goto out; 42269953Smsmith 42369953Smsmith for (index = 0; index < NDOSPART; index++) { 42469953Smsmith p = buf + DOSPARTOFF + index * DOSPARTSIZE; 42569953Smsmith if (p[0] != 0 && p[0] != 0x80) 42669953Smsmith goto out; 42769953Smsmith } 42869953Smsmith 42969953Smsmith /* 43069953Smsmith * If the partition table does not consist of all zeroes, 43169953Smsmith * assume we have a MBR. If it's all zeroes, we could have 43269953Smsmith * a boot sector. For example, a boot sector that doesn't 43369953Smsmith * have boot code -- common on non-i386 hardware. In that 43469953Smsmith * case we check if we have a possible BPB. If so, then we 43569953Smsmith * assume we have a boot sector instead. 43669953Smsmith */ 43769953Smsmith sum = 0; 43869953Smsmith for (index = 0; index < NDOSPART * DOSPARTSIZE; index++) 43969953Smsmith sum += buf[DOSPARTOFF + index]; 44069953Smsmith if (sum != 0 || !mbr_probe_bpb(buf + 0x0b)) 44169953Smsmith res = G_PART_PROBE_PRI_NORM; 44269953Smsmith 44369953Smsmith out: 44469953Smsmith g_free(buf); 44569953Smsmith return (res); 44669953Smsmith} 44769953Smsmith 44869953Smsmithstatic int 44969953Smsmithg_part_mbr_read(struct g_part_table *basetable, struct g_consumer *cp) 45069953Smsmith{ 45169953Smsmith struct dos_partition ent; 45269953Smsmith struct g_provider *pp; 45369953Smsmith struct g_part_mbr_table *table; 45469953Smsmith struct g_part_mbr_entry *entry; 45569953Smsmith u_char *buf, *p; 45669953Smsmith off_t chs, msize, first; 45769953Smsmith u_int sectors, heads; 45869953Smsmith int error, index; 45969953Smsmith 46069953Smsmith pp = cp->provider; 46169953Smsmith table = (struct g_part_mbr_table *)basetable; 46269953Smsmith first = basetable->gpt_sectors; 46369953Smsmith msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX); 46469953Smsmith 46569953Smsmith buf = g_read_data(cp, 0L, pp->sectorsize, &error); 46669953Smsmith if (buf == NULL) 46769953Smsmith return (error); 46869953Smsmith 46969953Smsmith bcopy(buf, table->mbr, sizeof(table->mbr)); 47069953Smsmith for (index = NDOSPART - 1; index >= 0; index--) { 47169953Smsmith p = buf + DOSPARTOFF + index * DOSPARTSIZE; 47269953Smsmith ent.dp_flag = p[0]; 47369953Smsmith ent.dp_shd = p[1]; 47469953Smsmith ent.dp_ssect = p[2]; 47569953Smsmith ent.dp_scyl = p[3]; 47669953Smsmith ent.dp_typ = p[4]; 47769953Smsmith ent.dp_ehd = p[5]; 47869953Smsmith ent.dp_esect = p[6]; 47969953Smsmith ent.dp_ecyl = p[7]; 48069953Smsmith ent.dp_start = le32dec(p + 8); 48169953Smsmith ent.dp_size = le32dec(p + 12); 48269953Smsmith if (ent.dp_typ == 0 || ent.dp_typ == DOSPTYP_PMBR) 48369953Smsmith continue; 48469953Smsmith if (ent.dp_start == 0 || ent.dp_size == 0) 48569953Smsmith continue; 48669953Smsmith sectors = ent.dp_esect & 0x3f; 48769953Smsmith if (sectors > basetable->gpt_sectors && 488 !basetable->gpt_fixgeom) { 489 g_part_geometry_heads(msize, sectors, &chs, &heads); 490 if (chs != 0) { 491 basetable->gpt_sectors = sectors; 492 basetable->gpt_heads = heads; 493 } 494 } 495 if (ent.dp_start < first) 496 first = ent.dp_start; 497 entry = (struct g_part_mbr_entry *)g_part_new_entry(basetable, 498 index + 1, ent.dp_start, ent.dp_start + ent.dp_size - 1); 499 entry->ent = ent; 500 } 501 502 basetable->gpt_entries = NDOSPART; 503 basetable->gpt_first = basetable->gpt_sectors; 504 basetable->gpt_last = msize - 1; 505 506 if (first < basetable->gpt_first) 507 basetable->gpt_first = 1; 508 509 g_free(buf); 510 return (0); 511} 512 513static int 514g_part_mbr_setunset(struct g_part_table *table, struct g_part_entry *baseentry, 515 const char *attrib, unsigned int set) 516{ 517 struct g_part_entry *iter; 518 struct g_part_mbr_entry *entry; 519 int changed; 520 521 if (baseentry == NULL) 522 return (ENODEV); 523 if (strcasecmp(attrib, "active") != 0) 524 return (EINVAL); 525 526 /* Only one entry can have the active attribute. */ 527 LIST_FOREACH(iter, &table->gpt_entry, gpe_entry) { 528 if (iter->gpe_deleted) 529 continue; 530 changed = 0; 531 entry = (struct g_part_mbr_entry *)iter; 532 if (iter == baseentry) { 533 if (set && (entry->ent.dp_flag & 0x80) == 0) { 534 entry->ent.dp_flag |= 0x80; 535 changed = 1; 536 } else if (!set && (entry->ent.dp_flag & 0x80)) { 537 entry->ent.dp_flag &= ~0x80; 538 changed = 1; 539 } 540 } else { 541 if (set && (entry->ent.dp_flag & 0x80)) { 542 entry->ent.dp_flag &= ~0x80; 543 changed = 1; 544 } 545 } 546 if (changed && !iter->gpe_created) 547 iter->gpe_modified = 1; 548 } 549 return (0); 550} 551 552static const char * 553g_part_mbr_type(struct g_part_table *basetable, struct g_part_entry *baseentry, 554 char *buf, size_t bufsz) 555{ 556 struct g_part_mbr_entry *entry; 557 int i; 558 559 entry = (struct g_part_mbr_entry *)baseentry; 560 for (i = 0; 561 i < sizeof(mbr_alias_match) / sizeof(mbr_alias_match[0]); i++) { 562 if (mbr_alias_match[i].typ == entry->ent.dp_typ) 563 return (g_part_alias_name(mbr_alias_match[i].alias)); 564 } 565 snprintf(buf, bufsz, "!%d", entry->ent.dp_typ); 566 return (buf); 567} 568 569static int 570g_part_mbr_write(struct g_part_table *basetable, struct g_consumer *cp) 571{ 572 struct g_part_entry *baseentry; 573 struct g_part_mbr_entry *entry; 574 struct g_part_mbr_table *table; 575 u_char *p; 576 int error, index; 577 578 table = (struct g_part_mbr_table *)basetable; 579 baseentry = LIST_FIRST(&basetable->gpt_entry); 580 for (index = 1; index <= basetable->gpt_entries; index++) { 581 p = table->mbr + DOSPARTOFF + (index - 1) * DOSPARTSIZE; 582 entry = (baseentry != NULL && index == baseentry->gpe_index) 583 ? (struct g_part_mbr_entry *)baseentry : NULL; 584 if (entry != NULL && !baseentry->gpe_deleted) { 585 p[0] = entry->ent.dp_flag; 586 p[1] = entry->ent.dp_shd; 587 p[2] = entry->ent.dp_ssect; 588 p[3] = entry->ent.dp_scyl; 589 p[4] = entry->ent.dp_typ; 590 p[5] = entry->ent.dp_ehd; 591 p[6] = entry->ent.dp_esect; 592 p[7] = entry->ent.dp_ecyl; 593 le32enc(p + 8, entry->ent.dp_start); 594 le32enc(p + 12, entry->ent.dp_size); 595 } else 596 bzero(p, DOSPARTSIZE); 597 598 if (entry != NULL) 599 baseentry = LIST_NEXT(baseentry, gpe_entry); 600 } 601 602 error = g_write_data(cp, 0, table->mbr, cp->provider->sectorsize); 603 return (error); 604} 605