diff --git a/sys/geom/geom_io.c b/sys/geom/geom_io.c --- a/sys/geom/geom_io.c +++ b/sys/geom/geom_io.c @@ -872,17 +872,24 @@ struct bio *bp; void *ptr; int errorc; + off_t start_align; + off_t end_align; - KASSERT(length > 0 && length >= cp->provider->sectorsize && + KASSERT(length > 0 && length <= maxphys, ("g_read_data(): invalid length %jd", (intmax_t)length)); + /* allow unaligned reads */ + start_align = (offset % cp->provider->sectorsize); + end_align = (cp->provider->sectorsize - ((offset + length) % + cp->provider->sectorsize)) % cp->provider->sectorsize; + bp = g_alloc_bio(); bp->bio_cmd = BIO_READ; bp->bio_done = NULL; - bp->bio_offset = offset; - bp->bio_length = length; - ptr = g_malloc(length, M_WAITOK); + bp->bio_offset = offset - start_align; + bp->bio_length = length + start_align + end_align; + ptr = g_malloc(bp->bio_length, M_WAITOK); bp->bio_data = ptr; g_io_request(bp, cp); errorc = biowait(bp, "gread"); @@ -894,6 +901,10 @@ if (errorc) { g_free(ptr); ptr = NULL; + } else { + /* check if buffer data should be shifted down */ + if (start_align != 0) + memmove(ptr, (uint8_t *)ptr + start_align, length); } return (ptr); } @@ -928,23 +939,45 @@ g_write_data(struct g_consumer *cp, off_t offset, void *ptr, off_t length) { struct bio *bp; + void *fixup; int error; + off_t start_align; + off_t end_align; - KASSERT(length > 0 && length >= cp->provider->sectorsize && + KASSERT(length > 0 && length <= maxphys, ("g_write_data(): invalid length %jd", (intmax_t)length)); + /* allow unaligned writes */ + start_align = (offset % cp->provider->sectorsize); + end_align = (cp->provider->sectorsize - ((offset + length) % + cp->provider->sectorsize)) % cp->provider->sectorsize; + + /* check if we need to do a fixup request */ + if (start_align != 0 || end_align != 0) { + fixup = g_read_data(cp, offset - start_align, + length + start_align + end_align, &error); + if (fixup == NULL) + return (error); + memcpy((uint8_t *)fixup + start_align, ptr, length); + } else { + fixup = ptr; + } + + /* setup BIO request */ bp = g_alloc_bio(); bp->bio_cmd = BIO_WRITE; bp->bio_done = NULL; - bp->bio_offset = offset; - bp->bio_length = length; - bp->bio_data = ptr; + bp->bio_offset = offset - start_align; + bp->bio_length = length + start_align + end_align; + bp->bio_data = fixup; g_io_request(bp, cp); error = biowait(bp, "gwrite"); if (error == 0 && bp->bio_completed != length) error = EIO; g_destroy_bio(bp); + if (fixup != ptr) + g_free(fixup); return (error); } diff --git a/sys/geom/part/g_part_bsd.c b/sys/geom/part/g_part_bsd.c --- a/sys/geom/part/g_part_bsd.c +++ b/sys/geom/part/g_part_bsd.c @@ -53,12 +53,24 @@ #define BOOT2_OFF (BOOT1_SIZE + LABEL_SIZE) #define BOOT2_SIZE (BBSIZE - BOOT2_OFF) +CTASSERT(sizeof(struct disklabel) <= DEV_BSIZE); + FEATURE(geom_part_bsd, "GEOM partitioning class for BSD disklabels"); +SYSCTL_DECL(_kern_geom_part); +static SYSCTL_NODE(_kern_geom_part, OID_AUTO, bsd, CTLFLAG_RW, 0, + "GEOM_PART_BSD BSD disklabels"); + +static u_int enforce_512bbs = 1; +SYSCTL_UINT(_kern_geom_part_bsd, OID_AUTO, enforce_512bbs, + CTLFLAG_RWTUN, &enforce_512bbs, 0, "Enforce 512 byte block size"); + struct g_part_bsd_table { struct g_part_table base; u_char *bbarea; uint32_t offset; + off_t sectorsize; + off_t sectorfact; }; struct g_part_bsd_entry { @@ -204,7 +216,7 @@ pp = gpp->gpp_provider; - if (pp->sectorsize < sizeof(struct disklabel)) + if (pp->sectorsize < DEV_BSIZE) return (ENOSPC); if (BBSIZE % pp->sectorsize) return (ENOTBLK); @@ -215,15 +227,24 @@ table = (struct g_part_bsd_table *)basetable; table->bbarea = g_malloc(BBSIZE, M_WAITOK | M_ZERO); - ptr = table->bbarea + pp->sectorsize; + + if (enforce_512bbs) { + table->sectorfact = pp->sectorsize / 512; + table->sectorsize = 512; + } else { + table->sectorfact = 1; + table->sectorsize = pp->sectorsize; + } + + ptr = table->bbarea + table->sectorsize; le32enc(ptr + 0, DISKMAGIC); /* d_magic */ - le32enc(ptr + 40, pp->sectorsize); /* d_secsize */ + le32enc(ptr + 40, table->sectorsize); /* d_secsize */ le32enc(ptr + 44, basetable->gpt_sectors); /* d_nsectors */ le32enc(ptr + 48, basetable->gpt_heads); /* d_ntracks */ le32enc(ptr + 52, ncyls); /* d_ncylinders */ le32enc(ptr + 56, secpercyl); /* d_secpercyl */ - le32enc(ptr + 60, msize); /* d_secperunit */ + le32enc(ptr + 60, msize * table->sectorfact); /* d_secperunit */ le16enc(ptr + 72, 3600); /* d_rpm */ le32enc(ptr + 132, DISKMAGIC); /* d_magic2 */ le16enc(ptr + 138, basetable->gpt_entries); /* d_npartitions */ @@ -313,7 +334,7 @@ table = (struct g_part_bsd_table *)basetable; msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX); - le32enc(table->bbarea + pp->sectorsize + 60, msize); /* d_secperunit */ + le32enc(table->bbarea + pp->sectorsize + 60, msize * table->sectorfact); /* d_secperunit */ basetable->gpt_last = msize - 1; LIST_FOREACH(baseentry, &basetable->gpt_entry, gpe_entry) { if (baseentry->gpe_index != RAW_PART + 1) @@ -354,24 +375,33 @@ } static int -g_part_bsd_probe(struct g_part_table *table, struct g_consumer *cp) +g_part_bsd_probe(struct g_part_table *basetable, struct g_consumer *cp) { + struct g_part_bsd_table *table; struct g_provider *pp; u_char *buf; uint32_t magic1, magic2; int error; + table = (struct g_part_bsd_table *)basetable; pp = cp->provider; /* Sanity-check the provider. */ - if (pp->sectorsize < sizeof(struct disklabel) || - pp->mediasize < BBSIZE) + if (pp->sectorsize < DEV_BSIZE || pp->mediasize < BBSIZE) return (ENOSPC); if (BBSIZE % pp->sectorsize) return (ENOTBLK); + if (enforce_512bbs) { + table->sectorfact = pp->sectorsize / 512; + table->sectorsize = 512; + } else { + table->sectorfact = 1; + table->sectorsize = pp->sectorsize; + } + /* Check that there's a disklabel. */ - buf = g_read_data(cp, pp->sectorsize, pp->sectorsize, &error); + buf = g_read_data(cp, table->sectorsize, table->sectorsize, &error); if (buf == NULL) return (error); magic1 = le32dec(buf + 0); @@ -402,9 +432,9 @@ if (table->bbarea == NULL) return (error); - buf = table->bbarea + pp->sectorsize; + buf = table->bbarea + table->sectorsize; - if (le32dec(buf + 40) != pp->sectorsize) + if (le32dec(buf + 40) != table->sectorsize) goto invalid_label; sectors = le32dec(buf + 44); if (sectors < 1 || sectors > 255) @@ -423,8 +453,10 @@ basetable->gpt_heads = heads; chs = le32dec(buf + 60); - if (chs < 1) + if ((chs % table->sectorfact) != 0 || chs < 1) goto invalid_label; + chs /= table->sectorfact; + /* Fix-up a sysinstall bug. */ if (chs > msize) { chs = msize; @@ -449,10 +481,12 @@ part.p_fstype = p[12]; part.p_frag = p[13]; part.p_cpg = le16dec(p + 14); - if (part.p_size == 0) + if (part.p_size == 0 || (part.p_size % table->sectorfact) != 0) continue; - if (part.p_offset < table->offset) + part.p_size /= table->sectorfact; + if (part.p_offset < table->offset || (part.p_offset % table->sectorfact) != 0) continue; + part.p_offset /= table->sectorfact; if (part.p_offset - table->offset > basetable->gpt_last) goto invalid_label; baseentry = g_part_new_entry(basetable, index + 1, @@ -510,14 +544,14 @@ pp = cp->provider; table = (struct g_part_bsd_table *)basetable; baseentry = LIST_FIRST(&basetable->gpt_entry); - label = table->bbarea + pp->sectorsize; + label = table->bbarea + table->sectorsize; for (index = 1; index <= basetable->gpt_entries; index++) { p = label + 148 + (index - 1) * 16; entry = (baseentry != NULL && index == baseentry->gpe_index) ? (struct g_part_bsd_entry *)baseentry : NULL; if (entry != NULL && !baseentry->gpe_deleted) { - le32enc(p + 0, entry->part.p_size); - le32enc(p + 4, entry->part.p_offset); + le32enc(p + 0, entry->part.p_size * table->sectorfact); + le32enc(p + 4, entry->part.p_offset * table->sectorfact); le32enc(p + 8, entry->part.p_fsize); p[12] = entry->part.p_fstype; p[13] = entry->part.p_frag; diff --git a/sys/geom/part/g_part_mbr.c b/sys/geom/part/g_part_mbr.c --- a/sys/geom/part/g_part_mbr.c +++ b/sys/geom/part/g_part_mbr.c @@ -60,10 +60,15 @@ SYSCTL_UINT(_kern_geom_part_mbr, OID_AUTO, enforce_chs, CTLFLAG_RWTUN, &enforce_chs, 0, "Enforce alignment to CHS addressing"); +static u_int enforce_512bbs = 1; +SYSCTL_UINT(_kern_geom_part_mbr, OID_AUTO, enforce_512bbs, + CTLFLAG_RWTUN, &enforce_512bbs, 0, "Enforce 512 byte block size"); + #define MBRSIZE 512 struct g_part_mbr_table { struct g_part_table base; + off_t sectorfact; u_char mbr[MBRSIZE]; }; @@ -296,6 +301,11 @@ table = (struct g_part_mbr_table *)basetable; le16enc(table->mbr + DOSMAGICOFFSET, DOSMAGIC); + + if (enforce_512bbs) + table->sectorfact = pp->sectorsize / MBRSIZE; + else + table->sectorfact = 1; return (0); } @@ -403,14 +413,16 @@ } static int -g_part_mbr_probe(struct g_part_table *table, struct g_consumer *cp) +g_part_mbr_probe(struct g_part_table *basetable, struct g_consumer *cp) { + struct g_part_mbr_table *table; char psn[8]; struct g_provider *pp; u_char *buf, *p; int error, index, res, sum; uint16_t magic; + table = (struct g_part_mbr_table *)basetable; pp = cp->provider; /* Sanity-check the provider. */ @@ -419,13 +431,18 @@ if (pp->sectorsize > 4096) return (ENXIO); + if (enforce_512bbs) + table->sectorfact = pp->sectorsize / MBRSIZE; + else + table->sectorfact = 1; + /* We don't nest under an MBR (see EBR instead). */ error = g_getattr("PART::scheme", cp, &psn); if (error == 0 && strcmp(psn, g_part_mbr_scheme.name) == 0) return (ELOOP); /* Check that there's a MBR. */ - buf = g_read_data(cp, 0L, pp->sectorsize, &error); + buf = g_read_data(cp, 0L, MBRSIZE, &error); if (buf == NULL) return (error); @@ -478,7 +495,7 @@ first = basetable->gpt_sectors; msize = MIN(pp->mediasize / pp->sectorsize, UINT32_MAX); - buf = g_read_data(cp, 0L, pp->sectorsize, &error); + buf = g_read_data(cp, 0L, MBRSIZE, &error); if (buf == NULL) return (error); @@ -495,6 +512,12 @@ ent.dp_ecyl = p[7]; ent.dp_start = le32dec(p + 8); ent.dp_size = le32dec(p + 12); + if (ent.dp_start % table->sectorfact) + continue; + ent.dp_start /= table->sectorfact; + if (ent.dp_size % table->sectorfact) + continue; + ent.dp_size /= table->sectorfact; if (ent.dp_typ == 0 || ent.dp_typ == DOSPTYP_PMBR) continue; if (ent.dp_start == 0 || ent.dp_size == 0) @@ -605,8 +628,8 @@ p[5] = entry->ent.dp_ehd; p[6] = entry->ent.dp_esect; p[7] = entry->ent.dp_ecyl; - le32enc(p + 8, entry->ent.dp_start); - le32enc(p + 12, entry->ent.dp_size); + le32enc(p + 8, entry->ent.dp_start * table->sectorfact); + le32enc(p + 12, entry->ent.dp_size * table->sectorfact); } else bzero(p, DOSPARTSIZE); @@ -614,6 +637,6 @@ baseentry = LIST_NEXT(baseentry, gpe_entry); } - error = g_write_data(cp, 0, table->mbr, cp->provider->sectorsize); + error = g_write_data(cp, 0, table->mbr, MBRSIZE); return (error); }