[PATCH RESEND] partitions/msdos: Support relative offset BSD partitions
From: Ross Lagerwall
Date: Mon Jun 22 2015 - 17:53:02 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, §);
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
Please read the FAQ at http://www.tux.org/lkml/