diff --git a/README.rst b/README.rst index c729a29..2ba3087 100644 --- a/README.rst +++ b/README.rst @@ -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. diff --git a/genimage.c b/genimage.c index d626dec..a10a001 100644 --- a/genimage.c +++ b/genimage.c @@ -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), @@ -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"); diff --git a/genimage.h b/genimage.h index 63995ac..7eafe57 100644 --- a/genimage.h +++ b/genimage.h @@ -40,6 +40,7 @@ struct partition { unsigned char partition_type; cfg_bool_t bootable; cfg_bool_t extended; + cfg_bool_t forced_primary; cfg_bool_t read_only; cfg_bool_t hidden; cfg_bool_t no_automount; diff --git a/image-hd.c b/image-hd.c index d1fe303..1b2bfe8 100644 --- a/image-hd.c +++ b/image-hd.c @@ -39,6 +39,7 @@ struct hdimage { unsigned int extended_partition; unsigned long long align; unsigned long long extended_lba; + unsigned long long extended_size; uint32_t disksig; const char *disk_uuid; int table_type; @@ -137,7 +138,7 @@ static int hdimage_insert_mbr(struct image *image, struct list_head *partitions) struct hdimage *hd = image->handler_priv; struct mbr_tail mbr; struct partition *part; - int ret, i = 0; + int ret, i = 0, extended_written = 0; if (hd->table_type == TYPE_HYBRID) { image_info(image, "writing hybrid MBR\n"); @@ -160,6 +161,9 @@ static int hdimage_insert_mbr(struct image *image, struct list_head *partitions) if (hd->table_type == TYPE_HYBRID && part->extended) continue; + if (part->extended && extended_written) + continue; + entry = &mbr.part_entry[i]; entry->boot = part->bootable ? 0x80 : 0x00; @@ -171,12 +175,21 @@ static int hdimage_insert_mbr(struct image *image, struct list_head *partitions) else { entry->partition_type = 0x0F; entry->relative_sectors = (hd->extended_lba)/512; - entry->total_sectors = (image->size - hd->extended_lba)/512; + entry->total_sectors = hd->extended_size/512; } hdimage_setup_chs(entry); - 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); + + if (part->extended) { + if (!extended_written) { + extended_written = 1; + i++; + } + continue; + } i++; } @@ -215,8 +228,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; @@ -245,7 +259,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; @@ -756,6 +770,7 @@ 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)); @@ -821,11 +836,38 @@ 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) + mbr_entries = hd->extended_partition; 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 && partition_table_entries > 4) { + hd->extended_partition = mbr_entries = 4; + } + if (part->forced_primary) { + ++forced_primary_entries; + ++mbr_entries; + if (partition_table_entries <= hd->extended_partition) { + 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; + } + image_debug(image, "mbr_entries %d\n", mbr_entries); + } 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) { @@ -834,9 +876,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) { @@ -961,7 +1000,7 @@ static int hdimage_setup(struct image *image, cfg_t *cfg) /* reserve space for extended boot record if necessary */ if (part->in_partition_table) ++partition_table_entries; - part->extended = has_extended && part->in_partition_table && + part->extended = !part->forced_primary && has_extended && part->in_partition_table && (partition_table_entries >= hd->extended_partition); if (part->extended) { now += hd->align; @@ -1053,6 +1092,10 @@ static int hdimage_setup(struct image *image, cfg_t *cfg) } else if (part->extended) hd->file_size = part->offset - hd->align + 512; + + if (part->extended) { + hd->extended_size = part->offset + part->size - hd->extended_lba; + } } if (hybrid_entries > 3) { diff --git a/test/hdimage-fail10.config b/test/hdimage-fail10.config new file mode 100644 index 0000000..782c090 --- /dev/null +++ b/test/hdimage-fail10.config @@ -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" + } +} diff --git a/test/hdimage-fail11.config b/test/hdimage-fail11.config new file mode 100644 index 0000000..06bf64b --- /dev/null +++ b/test/hdimage-fail11.config @@ -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 */ + } +} diff --git a/test/hdimage-fail8.config b/test/hdimage-fail8.config new file mode 100644 index 0000000..8f55faa --- /dev/null +++ b/test/hdimage-fail8.config @@ -0,0 +1,28 @@ +image test.hdimage { + hdimage { + align = 1M + extended-partition = 1 + } + partition part1 { + image = "part1.img" + partition-type = 0x83 + forced-primary = "yes" + /* forced-primary can be only used for partitions defined after the extended partition */ + } + partition part2 { + image = "part1.img" + partition-type = 0x83 + } + partition part3 { + image = "part1.img" + partition-type = 0x83 + } + partition part4 { + image = "part1.img" + partition-type = 0x83 + } + partition part5 { + image = "part1.img" + partition-type = 0x83 + } +} diff --git a/test/hdimage-fail9.config b/test/hdimage-fail9.config new file mode 100644 index 0000000..d811b7b --- /dev/null +++ b/test/hdimage-fail9.config @@ -0,0 +1,27 @@ +image test.hdimage { + hdimage { + align = 1M + } + partition primary1 { + image = "part1.img" + partition-type = 0x83 + } + partition primary2 { + image = "part1.img" + partition-type = 0x83 + } + partition primary3 { + image = "part1.img" + partition-type = 0x83 + } + partition primary4 { + image = "part1.img" + partition-type = 0x83 + } + partition primary5 { + image = "part1.img" + partition-type = 0x83 + /* part4 is implicitly extended -> too many primary entries */ + forced-primary = "yes" + } +} diff --git a/test/hdimage-forced-primary.config b/test/hdimage-forced-primary.config new file mode 100644 index 0000000..c15b3a5 --- /dev/null +++ b/test/hdimage-forced-primary.config @@ -0,0 +1,47 @@ +image test.hdimage { + hdimage { + align = 1M + disk-signature = 0x12345678 + extended-partition = 2 + } + partition part1 { + image = "part1.img" + partition-type = 0xc + bootable = "yes" + } + /* + * partition 2 will be the extended partition entry + * partitions 3-4 will be primary partitions at the end + * partition 5 is first logical partition of the extended partition + */ + partition part5-logical { + image = "part1.img" + partition-type = 0x83 + } + partition part6-logical { + image = "part2.img" + partition-type = 0x83 + } + partition part7-logical { + image = "part1.img" + partition-type = 0x83 + } + partition part8-logical { + image = "part2.img" + partition-type = 0x83 + } + partition part9-logical { + image = "part1.img" + partition-type = 0x83 + } + partition part3 { + image = "part1.img" + partition-type = 0x83 + forced-primary = "yes" + } + partition part4 { + image = "part2.img" + partition-type = 0x82 + forced-primary = "yes" + } +} diff --git a/test/hdimage-forced-primary.fdisk b/test/hdimage-forced-primary.fdisk new file mode 100644 index 0000000..ff0e903 --- /dev/null +++ b/test/hdimage-forced-primary.fdisk @@ -0,0 +1,10 @@ +Disk identifier: 0x12345678 +images/test.hdimage1:start=2048,size=2048,type=c,bootable +images/test.hdimage2:start=4096,size=20480,type=f +images/test.hdimage3:start=24576,size=2048,type=83 +images/test.hdimage4:start=26624,size=2048,type=82 +images/test.hdimage5:start=6144,size=2048,type=83 +images/test.hdimage6:start=10240,size=2048,type=83 +images/test.hdimage7:start=14336,size=2048,type=83 +images/test.hdimage8:start=18432,size=2048,type=83 +images/test.hdimage9:start=22528,size=2048,type=83 diff --git a/test/hdimage.test b/test/hdimage.test index 967c22c..bd728a7 100755 --- a/test/hdimage.test +++ b/test/hdimage.test @@ -96,7 +96,11 @@ test_expect_success "hdimage syntax" " test_must_fail run_genimage hdimage-fail4.config && test_must_fail run_genimage hdimage-fail5.config && test_must_fail run_genimage hdimage-fail6.config && - test_must_fail run_genimage hdimage-fail7.config + test_must_fail run_genimage hdimage-fail7.config && + test_must_fail run_genimage hdimage-fail8.config && + test_must_fail run_genimage hdimage-fail9.config && + test_must_fail run_genimage hdimage-fail10.config && + test_must_fail run_genimage hdimage-fail11.config " setup_gpt_files() { @@ -163,6 +167,14 @@ test_expect_success "hdimage no-partition" " test_cmp 'hdimage-nopart.hexdump' '${testdir}/hdimage-nopart.hexdump' " +test_expect_success "hdimage forced-primary" " + setup_test_images && + run_genimage hdimage-forced-primary.config && + sfdisk_validate images/test.hdimage && + sanitized_fdisk_sfdisk images/test.hdimage > hdimage.fdisk && + test_cmp '${testdir}/hdimage-forced-primary.fdisk' hdimage.fdisk +" + test_done # vim: syntax=sh