Skip to content

Commit

Permalink
Merge pull request #248 from sairon/mbr-forced-primary
Browse files Browse the repository at this point in the history
image-hd: add forced-primary flag for higher MBR layout flexibility
  • Loading branch information
michaelolbrich authored Jun 21, 2024
2 parents df4923c + d52b233 commit 6d217c0
Show file tree
Hide file tree
Showing 12 changed files with 282 additions and 43 deletions.
6 changes: 6 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,14 @@ EXTRA_DIST += \
test/hdimage-fail5.config \
test/hdimage-fail6.config \
test/hdimage-fail7.config \
test/hdimage-fail8.config \
test/hdimage-fail9.config \
test/hdimage-fail10.config \
test/hdimage-fail11.config \
test/hdimage-nopart.config \
test/hdimage-nopart.hexdump \
test/hdimage-forced-primary.config \
test/hdimage-forced-primary.fdisk \
test/include-aaa.fdisk \
test/include-bbb.fdisk \
test/include-ccc.fdisk \
Expand Down
5 changes: 5 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ Partition options:
:bootable: Boolean specifying whether to set the bootable flag.
:in-partition-table: Boolean specifying whether to include this partition in
the partition table. Defaults to true.
:forced-primary: Force this partition to be a primary partition in the
MBR partition table, useful when the extended partition should be
followed by primary partitions. If there are more partitions
defined after the first forced-primary, they must be also defined
as forced-primary. Defaults to false.
:partition-uuid: UUID string used by GPT partition tables to specify the partition
id. Defaults to a random value.
:partition-type-uuid: String used by GPT partition tables to specify the partition type.
Expand Down
2 changes: 2 additions & 0 deletions genimage.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ static cfg_opt_t partition_opts[] = {
CFG_STR("align", NULL, CFGF_NONE),
CFG_INT("partition-type", 0, CFGF_NONE),
CFG_BOOL("bootable", cfg_false, CFGF_NONE),
CFG_BOOL("forced-primary", cfg_false, CFGF_NONE),
CFG_BOOL("read-only", cfg_false, CFGF_NONE),
CFG_BOOL("hidden", cfg_false, CFGF_NONE),
CFG_BOOL("no-automount", cfg_false, CFGF_NONE),
Expand Down Expand Up @@ -396,6 +397,7 @@ static int parse_partitions(struct image *image, cfg_t *imagesec)
part->align = cfg_getint_suffix(partsec, "align");
part->partition_type = cfg_getint(partsec, "partition-type");
part->bootable = cfg_getbool(partsec, "bootable");
part->forced_primary = cfg_getbool(partsec, "forced-primary");
part->read_only = cfg_getbool(partsec, "read-only");
part->hidden = cfg_getbool(partsec, "hidden");
part->no_automount = cfg_getbool(partsec, "no-automount");
Expand Down
3 changes: 2 additions & 1 deletion genimage.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ struct partition {
unsigned long long align;
unsigned char partition_type;
cfg_bool_t bootable;
cfg_bool_t extended;
cfg_bool_t logical;
cfg_bool_t forced_primary;
cfg_bool_t read_only;
cfg_bool_t hidden;
cfg_bool_t no_automount;
Expand Down
118 changes: 77 additions & 41 deletions image-hd.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@
#define TYPE_GPT 2
#define TYPE_HYBRID (TYPE_MBR|TYPE_GPT)

#define PARTITION_TYPE_EXTENDED 0x0F

struct hdimage {
unsigned int extended_partition;
unsigned int extended_partition_index;
struct partition *extended_partition;
unsigned long long align;
unsigned long long extended_lba;
uint32_t disksig;
const char *disk_uuid;
int table_type;
Expand Down Expand Up @@ -153,32 +155,24 @@ static int hdimage_insert_mbr(struct image *image, struct list_head *partitions)
list_for_each_entry(part, partitions, list) {
struct mbr_partition_entry *entry;

if (!part->in_partition_table)
if (!part->in_partition_table || part->logical)
continue;

if (hd->table_type == TYPE_HYBRID && !part->partition_type)
continue;

if (hd->table_type == TYPE_HYBRID && part->extended)
continue;

entry = &mbr.part_entry[i];

entry->boot = part->bootable ? 0x80 : 0x00;
if (!part->extended) {
entry->partition_type = part->partition_type;
entry->relative_sectors = part->offset/512;
entry->total_sectors = part->size/512;
}
else {
entry->partition_type = 0x0F;
entry->relative_sectors = (hd->extended_lba)/512;
entry->total_sectors = (image->size - hd->extended_lba)/512;
}
entry->partition_type = part->partition_type;
entry->relative_sectors = part->offset/512;
entry->total_sectors = part->size/512;
hdimage_setup_chs(entry, 0);

if (part->extended)
break;
image_debug(image, "[MBR entry %d]: type=%x start=%d size=%d\n",
i, entry->partition_type,
entry->relative_sectors, entry->total_sectors);

i++;
}

Expand Down Expand Up @@ -217,8 +211,9 @@ static int hdimage_insert_ebr(struct image *image, struct partition *part)
struct mbr_partition_entry *entry;
char ebr[4*sizeof(struct mbr_partition_entry)+2], *part_table;
int ret;
unsigned long long ebr_offset = part->offset - hd->align + 446;

image_info(image, "writing EBR\n");
image_debug(image, "writing EBR to sector %llu\n", ebr_offset / 512);

memset(ebr, 0, sizeof(ebr));
part_table = ebr;
Expand All @@ -233,16 +228,16 @@ static int hdimage_insert_ebr(struct image *image, struct partition *part)
hdimage_setup_chs(entry, (part->offset - hd->align) / 512);
struct partition *p = part;
list_for_each_entry_continue(p, &image->partitions, list) {
if (!p->extended)
if (!p->logical)
continue;
++entry;
entry->boot = 0x00;
entry->partition_type = 0x0F;
entry->relative_sectors = (p->offset - hd->align - hd->extended_lba)/512;
entry->partition_type = PARTITION_TYPE_EXTENDED;
entry->relative_sectors = (p->offset - hd->align - hd->extended_partition->offset)/512;
entry->total_sectors = (p->size + hd->align)/512;
// absolute CHS address of the next EBR
// equals to relative address within extended partition + partition start
hdimage_setup_chs(entry, hd->extended_lba / 512);
hdimage_setup_chs(entry, hd->extended_partition->offset / 512);
break;
}

Expand All @@ -251,7 +246,7 @@ static int hdimage_insert_ebr(struct image *image, struct partition *part)
part_table[1] = 0xaa;

ret = insert_data(image, ebr, imageoutfile(image), sizeof(ebr),
part->offset - hd->align + 446);
ebr_offset);
if (ret) {
image_error(image, "failed to write EBR\n");
return ret;
Expand Down Expand Up @@ -583,13 +578,15 @@ static int hdimage_generate(struct image *image)
list_for_each_entry(part, &image->partitions, list) {
struct image *child;

image_info(image, "adding partition '%s'%s%s%s%s ...\n", part->name,
image_info(image, "adding %s partition '%s'%s%s%s%s ...\n",
part->logical ? "logical" : "primary",
part->name,
part->in_partition_table ? " (in MBR)" : "",
part->image ? " from '": "",
part->image ? part->image : "",
part->image ? "'" : "");

if (part->extended) {
if (part->logical) {
ret = hdimage_insert_ebr(image, part);
if (ret) {
image_error(image, "failed to write EBR\n");
Expand Down Expand Up @@ -762,13 +759,14 @@ static int hdimage_setup(struct image *image, cfg_t *cfg)
struct partition *autoresize_part = NULL;
int has_extended;
unsigned int partition_table_entries = 0, hybrid_entries = 0;
unsigned int mbr_entries = 0, forced_primary_entries = 0;
unsigned long long now = 0;
const char *disk_signature, *table_type;
struct hdimage *hd = xzalloc(sizeof(*hd));
struct partition *gpt_backup = NULL;

hd->align = cfg_getint_suffix(cfg, "align");
hd->extended_partition = cfg_getint(cfg, "extended-partition");
hd->extended_partition_index = cfg_getint(cfg, "extended-partition");
disk_signature = cfg_getstr(cfg, "disk-signature");
table_type = cfg_getstr(cfg, "partition-table-type");
hd->gpt_location = cfg_getint_suffix(cfg, "gpt-location");
Expand Down Expand Up @@ -815,10 +813,10 @@ static int hdimage_setup(struct image *image, cfg_t *cfg)
if (!hd->align)
hd->align = hd->table_type == TYPE_NONE ? 1 : 512;

if (hd->extended_partition > 4) {
if (hd->extended_partition_index > 4) {
image_error(image, "invalid extended partition index (%i). must be "
"inferior or equal to 4 (0 for automatic)\n",
hd->extended_partition);
hd->extended_partition_index);
return -EINVAL;
}

Expand All @@ -827,11 +825,41 @@ static int hdimage_setup(struct image *image, cfg_t *cfg)
"multiple of 1 sector (512 bytes)\n", hd->align);
return -EINVAL;
}
if (hd->table_type == TYPE_MBR && hd->extended_partition_index)
mbr_entries = hd->extended_partition_index;

has_extended = hd->extended_partition_index > 0;

list_for_each_entry(part, &image->partitions, list) {
if (hd->table_type == TYPE_NONE)
part->in_partition_table = false;
if (part->in_partition_table)
++partition_table_entries;
if (hd->table_type == TYPE_MBR && part->in_partition_table) {
if (!hd->extended_partition_index && partition_table_entries > 4) {
hd->extended_partition_index = mbr_entries = 4;
has_extended = true;
}
if (part->forced_primary) {
++forced_primary_entries;
++mbr_entries;
if (partition_table_entries <= hd->extended_partition_index) {
image_error(image, "partition %s: forced-primary can only be used for "
"partitions following the extended partition\n",
part->name);
return -EINVAL;
}
} else if (forced_primary_entries > 0) {
image_error(image,
"cannot create non-primary partition %s after forced-primary partition\n",
part->name);
return -EINVAL;
}
if (mbr_entries > 4) {
image_error(image, "too many primary partitions\n");
return -EINVAL;
}
}
if (!part->align)
part->align = (part->in_partition_table || hd->table_type == TYPE_NONE) ? hd->align : 1;
if (part->in_partition_table && part->align % hd->align) {
Expand All @@ -840,10 +868,6 @@ static int hdimage_setup(struct image *image, cfg_t *cfg)
part->align, part->name, hd->align);
}
}
if (hd->table_type == TYPE_MBR && !hd->extended_partition &&
partition_table_entries > 4)
hd->extended_partition = 4;
has_extended = hd->extended_partition > 0;

if (hd->disk_uuid) {
if (!(hd->table_type & TYPE_GPT)) {
Expand Down Expand Up @@ -964,12 +988,12 @@ static int hdimage_setup(struct image *image, cfg_t *cfg)
if (part->partition_type)
++hybrid_entries;
}
/* reserve space for extended boot record if necessary */
if (part->in_partition_table)
++partition_table_entries;
part->extended = has_extended && part->in_partition_table &&
(partition_table_entries >= hd->extended_partition);
if (part->extended) {
part->logical = !part->forced_primary && has_extended && part->in_partition_table &&
(partition_table_entries >= hd->extended_partition_index);
if (part->logical) {
/* reserve space for extended boot record */
now += hd->align;
now = roundup(now, part->align);
}
Expand All @@ -984,8 +1008,6 @@ static int hdimage_setup(struct image *image, cfg_t *cfg)
if (!part->offset && (part->in_partition_table || hd->table_type == TYPE_NONE)) {
part->offset = roundup(now, part->align);
}
if (part->extended && !hd->extended_lba)
hd->extended_lba = part->offset - hd->align;

if (part->offset % part->align) {
image_error(image, "part %s offset (%lld) must be a"
Expand Down Expand Up @@ -1033,7 +1055,7 @@ static int hdimage_setup(struct image *image, cfg_t *cfg)
part->name);
return -EINVAL;
}
if (!part->extended) {
if (!part->logical) {
int ret = check_overlap(image, part);
if (ret)
return ret;
Expand All @@ -1057,8 +1079,22 @@ static int hdimage_setup(struct image *image, cfg_t *cfg)
hd->file_size = part->offset + child->size;
}
}
else if (part->extended)
else if (part->logical)
hd->file_size = part->offset - hd->align + 512;

if (has_extended && hd->extended_partition_index == partition_table_entries) {
struct partition *p = fake_partition("[Extended]", now - hd->align - part->size,
0);
p->in_partition_table = true;
p->partition_type = PARTITION_TYPE_EXTENDED;

hd->extended_partition = p;
list_add_tail(&p->list, &part->list);
}

if (part->logical) {
hd->extended_partition->size = now - hd->extended_partition->offset;
}
}

if (hybrid_entries > 3) {
Expand Down
33 changes: 33 additions & 0 deletions test/hdimage-fail10.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
image test.hdimage {
hdimage {
align = 1M
extended-partition = 3
}
partition primary1 {
image = "part1.img"
partition-type = 0x83
}
partition primary2 {
image = "part1.img"
partition-type = 0x83
}
partition extended1 {
image = "part1.img"
partition-type = 0x83
}
partition extended2 {
image = "part1.img"
partition-type = 0x83
}
partition primary3 {
image = "part1.img"
partition-type = 0x83
forced-primary = "yes"
}
partition primary4 {
image = "part1.img"
partition-type = 0x83
/* would be 5th primary partition */
forced-primary = "yes"
}
}
32 changes: 32 additions & 0 deletions test/hdimage-fail11.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
image test.hdimage {
hdimage {
align = 1M
extended-partition = 1
}
partition extended1 {
image = "part1.img"
partition-type = 0x83
}
partition extended2 {
image = "part1.img"
partition-type = 0x83
}
partition extended3 {
image = "part1.img"
partition-type = 0x83
}
partition extended4 {
image = "part1.img"
partition-type = 0x83
}
partition primary2 {
image = "part1.img"
partition-type = 0x83
forced-primary = "yes"
}
partition extended5 {
image = "part1.img"
partition-type = 0x83
/* extended partition would overlap the forced-primary one */
}
}
Loading

0 comments on commit 6d217c0

Please sign in to comment.