[PATCH] partitions/msdos: Support relative offset BSD partitions

From: Ross Lagerwall
Date: Sat Jun 13 2015 - 04:11:57 EST


FreeBSD can use relative addressing for the offsets in its disklabel (it
appears that bsdlabel uses relative offsets and sysinstall uses absolute
offsets). When Linux reads the disklabel, either the partitions fail the
sanity checks and are considered invalid or they use the wrong offset
and data corruption ensues.

To fix this, determine if relative addressing is used by looking at
partition "c" in the disk label which is meant to represent the entire
slice. If "c" has an offset of 0, then relative addressing is used,
otherwise absolute addressing is used.

This change includes a cleanup to allow altering behavior based on the
type of disklabel.

The idea comes from the FreeBSD patch for GRUB:
http://lists.freebsd.org/pipermail/freebsd-ports-bugs/2010-November/201081.html

Signed-off-by: Ross Lagerwall <rosslagerwall@xxxxxxxxx>
---
block/partitions/msdos.c | 33 ++++++++++++++++++++++++++-------
include/linux/genhd.h | 1 +
2 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/block/partitions/msdos.c b/block/partitions/msdos.c
index 93e7c1b..e8421c4 100644
--- a/block/partitions/msdos.c
+++ b/block/partitions/msdos.c
@@ -265,18 +265,30 @@ static void parse_solaris_x86(struct parsed_partitions *state,
}

#if defined(CONFIG_BSD_DISKLABEL)
+enum flavour {
+ FLAVOUR_BSD,
+ FLAVOUR_NETBSD,
+ FLAVOUR_OPENBSD,
+};
+
+static const char *const flavours[] = {
+ [FLAVOUR_BSD] = "bsd",
+ [FLAVOUR_NETBSD] = "netbsd",
+ [FLAVOUR_OPENBSD] = "openbsd",
+};
/*
* Create devices for BSD partitions listed in a disklabel, under a
* dos-like partition. See parse_extended() for more information.
*/
static void parse_bsd(struct parsed_partitions *state,
- sector_t offset, sector_t size, int origin, char *flavour,
- int max_partitions)
+ sector_t offset, sector_t size, int origin,
+ enum flavour flavour, int max_partitions)
{
Sector sect;
struct bsd_disklabel *l;
struct bsd_partition *p;
char tmp[64];
+ sector_t delta = 0;

l = read_part_sector(state, offset + 1, &sect);
if (!l)
@@ -286,11 +298,17 @@ static void parse_bsd(struct parsed_partitions *state,
return;
}

- snprintf(tmp, sizeof(tmp), " %s%d: <%s:", state->name, origin, flavour);
+ snprintf(tmp, sizeof(tmp), " %s%d: <%s:", state->name, origin,
+ flavours[flavour]);
strlcat(state->pp_buf, tmp, PAGE_SIZE);

if (le16_to_cpu(l->d_npartitions) < max_partitions)
max_partitions = le16_to_cpu(l->d_npartitions);
+ /* Determine if sector offsets are relative. */
+ if (BSD_WHOLE_DISK < le16_to_cpu(l->d_npartitions) &&
+ flavour == FLAVOUR_BSD)
+ delta = offset -
+ le32_to_cpu(l->d_partitions[BSD_WHOLE_DISK].p_offset);
for (p = l->d_partitions; p - l->d_partitions < max_partitions; p++) {
sector_t bsd_start, bsd_size;

@@ -298,7 +316,7 @@ static void parse_bsd(struct parsed_partitions *state,
break;
if (p->p_fstype == BSD_FS_UNUSED)
continue;
- bsd_start = le32_to_cpu(p->p_offset);
+ bsd_start = le32_to_cpu(p->p_offset) + delta;
bsd_size = le32_to_cpu(p->p_size);
if (offset == bsd_start && size == bsd_size)
/* full parent partition, we have it already */
@@ -323,7 +341,7 @@ static void parse_freebsd(struct parsed_partitions *state,
sector_t offset, sector_t size, int origin)
{
#ifdef CONFIG_BSD_DISKLABEL
- parse_bsd(state, offset, size, origin, "bsd", BSD_MAXPARTITIONS);
+ parse_bsd(state, offset, size, origin, FLAVOUR_BSD, BSD_MAXPARTITIONS);
#endif
}

@@ -331,7 +349,8 @@ static void parse_netbsd(struct parsed_partitions *state,
sector_t offset, sector_t size, int origin)
{
#ifdef CONFIG_BSD_DISKLABEL
- parse_bsd(state, offset, size, origin, "netbsd", BSD_MAXPARTITIONS);
+ parse_bsd(state, offset, size, origin, FLAVOUR_NETBSD,
+ BSD_MAXPARTITIONS);
#endif
}

@@ -339,7 +358,7 @@ static void parse_openbsd(struct parsed_partitions *state,
sector_t offset, sector_t size, int origin)
{
#ifdef CONFIG_BSD_DISKLABEL
- parse_bsd(state, offset, size, origin, "openbsd",
+ parse_bsd(state, offset, size, origin, FLAVOUR_OPENBSD,
OPENBSD_MAXPARTITIONS);
#endif
}
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index ec274e0..d60bfdd 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -491,6 +491,7 @@ struct solaris_x86_vtoc {
#define BSD_MAXPARTITIONS 16
#define OPENBSD_MAXPARTITIONS 16
#define BSD_FS_UNUSED 0 /* disklabel unused partition entry ID */
+#define BSD_WHOLE_DISK 2 /* partition representing entire disk */
struct bsd_disklabel {
__le32 d_magic; /* the magic number */
__s16 d_type; /* drive type */
--
2.4.2

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/