mirror of
https://github.com/pengutronix/genimage.git
synced 2024-11-23 01:34:12 +08:00
3e0a2aea8d
U-Boot documents a GPT partition type GUID for U-Boot environment partitions. Add a shorthand to make it easier to use. [1] https://github.com/u-boot/u-boot/blob/v2024.10/doc/README.gpt#L288 Signed-off-by: Brandon Maier <brandon.maier@gmail.com>
1230 lines
40 KiB
C
1230 lines
40 KiB
C
/*
|
|
* Copyright (c) 2011 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
|
|
* (c) 2011 Michael Olbrich <m.olbrich@pengutronix.de>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2
|
|
* as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <confuse.h>
|
|
#include <fcntl.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <inttypes.h>
|
|
#include <endian.h>
|
|
#include <stdbool.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "genimage.h"
|
|
|
|
#define TYPE_NONE 0
|
|
#define TYPE_MBR 1
|
|
#define TYPE_GPT 2
|
|
#define TYPE_HYBRID (TYPE_MBR|TYPE_GPT)
|
|
|
|
#define PARTITION_TYPE_EXTENDED 0x0F
|
|
|
|
struct hdimage {
|
|
unsigned int extended_partition_index;
|
|
struct partition *extended_partition;
|
|
unsigned long long align;
|
|
uint32_t disksig;
|
|
const char *disk_uuid;
|
|
int table_type;
|
|
unsigned long long gpt_location;
|
|
cfg_bool_t gpt_no_backup;
|
|
cfg_bool_t fill;
|
|
unsigned long long file_size;
|
|
};
|
|
|
|
struct mbr_partition_entry {
|
|
unsigned char boot;
|
|
|
|
unsigned char first_chs[3];
|
|
|
|
unsigned char partition_type;
|
|
|
|
unsigned char last_chs[3];
|
|
|
|
uint32_t relative_sectors;
|
|
uint32_t total_sectors;
|
|
} __attribute__((packed));
|
|
ct_assert(sizeof(struct mbr_partition_entry) == 16);
|
|
|
|
struct mbr_tail {
|
|
uint32_t disk_signature;
|
|
uint16_t copy_protect;
|
|
struct mbr_partition_entry part_entry[4];
|
|
uint16_t boot_signature;
|
|
} __attribute__((packed));
|
|
ct_assert(sizeof(struct mbr_tail) == 72);
|
|
|
|
struct gpt_header {
|
|
unsigned char signature[8];
|
|
uint32_t revision;
|
|
uint32_t header_size;
|
|
uint32_t header_crc;
|
|
uint32_t reserved;
|
|
uint64_t current_lba;
|
|
uint64_t backup_lba;
|
|
uint64_t first_usable_lba;
|
|
uint64_t last_usable_lba;
|
|
unsigned char disk_uuid[16];
|
|
uint64_t starting_lba;
|
|
uint32_t number_entries;
|
|
uint32_t entry_size;
|
|
uint32_t table_crc;
|
|
} __attribute__((packed));
|
|
ct_assert(sizeof(struct gpt_header) == 92);
|
|
|
|
struct gpt_partition_entry {
|
|
unsigned char type_uuid[16];
|
|
unsigned char uuid[16];
|
|
uint64_t first_lba;
|
|
uint64_t last_lba;
|
|
uint64_t flags;
|
|
uint16_t name[36];
|
|
} __attribute__((packed));
|
|
ct_assert(sizeof(struct gpt_partition_entry) == 128);
|
|
|
|
#define GPT_ENTRIES 128
|
|
#define GPT_SECTORS (1 + GPT_ENTRIES * sizeof(struct gpt_partition_entry) / 512)
|
|
#define GPT_REVISION_1_0 0x00010000
|
|
|
|
#define GPT_PE_FLAG_BOOTABLE (1ULL << 2)
|
|
#define GPT_PE_FLAG_READ_ONLY (1ULL << 60)
|
|
#define GPT_PE_FLAG_HIDDEN (1ULL << 62)
|
|
#define GPT_PE_FLAG_NO_AUTO (1ULL << 63)
|
|
|
|
static unsigned long long partition_end(const struct partition *part)
|
|
{
|
|
return part->offset + part->size;
|
|
}
|
|
|
|
static void lba_to_chs(unsigned int lba, unsigned char *chs)
|
|
{
|
|
const unsigned int hpc = 255;
|
|
const unsigned int spt = 63;
|
|
unsigned int s, c;
|
|
|
|
chs[0] = (lba/spt)%hpc;
|
|
c = (lba/(spt * hpc));
|
|
s = (lba > 0) ?(lba%spt + 1) : 0;
|
|
chs[1] = ((c & 0x300) >> 2) | (s & 0xff);
|
|
chs[2] = (c & 0xff);
|
|
}
|
|
|
|
static void hdimage_setup_chs(struct mbr_partition_entry *entry,
|
|
unsigned long long offset_sectors)
|
|
{
|
|
lba_to_chs(entry->relative_sectors + offset_sectors,
|
|
entry->first_chs);
|
|
lba_to_chs(entry->relative_sectors + entry->total_sectors - 1 + offset_sectors,
|
|
entry->last_chs);
|
|
}
|
|
|
|
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;
|
|
|
|
if (hd->table_type == TYPE_HYBRID) {
|
|
image_info(image, "writing hybrid MBR\n");
|
|
} else {
|
|
image_info(image, "writing MBR\n");
|
|
}
|
|
|
|
memset(&mbr, 0, sizeof(mbr));
|
|
memcpy(&mbr.disk_signature, &hd->disksig, sizeof(hd->disksig));
|
|
|
|
list_for_each_entry(part, partitions, list) {
|
|
struct mbr_partition_entry *entry;
|
|
|
|
if (!part->in_partition_table || part->logical)
|
|
continue;
|
|
|
|
if (hd->table_type == TYPE_HYBRID && !part->partition_type)
|
|
continue;
|
|
|
|
entry = &mbr.part_entry[i];
|
|
|
|
entry->boot = part->bootable ? 0x80 : 0x00;
|
|
entry->partition_type = part->partition_type;
|
|
entry->relative_sectors = part->offset/512;
|
|
entry->total_sectors = part->size/512;
|
|
hdimage_setup_chs(entry, 0);
|
|
|
|
image_debug(image, "[MBR entry %d]: type=%x start=%d size=%d\n",
|
|
i, entry->partition_type,
|
|
entry->relative_sectors, entry->total_sectors);
|
|
|
|
i++;
|
|
}
|
|
|
|
if (hd->table_type == TYPE_HYBRID) {
|
|
struct mbr_partition_entry *entry;
|
|
|
|
entry = &mbr.part_entry[i];
|
|
|
|
entry->boot = 0x00;
|
|
|
|
entry->partition_type = 0xee;
|
|
entry->relative_sectors = 1;
|
|
entry->total_sectors = hd->gpt_location / 512 + GPT_SECTORS - 2;
|
|
|
|
hdimage_setup_chs(entry, 0);
|
|
}
|
|
|
|
mbr.boot_signature = htole16(0xaa55);
|
|
|
|
ret = insert_data(image, &mbr, imageoutfile(image), sizeof(mbr), 440);
|
|
if (ret) {
|
|
if (hd->table_type == TYPE_HYBRID) {
|
|
image_error(image, "failed to write hybrid MBR\n");
|
|
} else {
|
|
image_error(image, "failed to write MBR\n");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hdimage_insert_ebr(struct image *image, struct partition *part)
|
|
{
|
|
struct hdimage *hd = image->handler_priv;
|
|
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_debug(image, "writing EBR to sector %llu\n", ebr_offset / 512);
|
|
|
|
memset(ebr, 0, sizeof(ebr));
|
|
part_table = ebr;
|
|
entry = (struct mbr_partition_entry *)part_table;
|
|
|
|
entry->boot = 0x00;
|
|
entry->partition_type = part->partition_type;
|
|
entry->relative_sectors = hd->align/512;
|
|
entry->total_sectors = part->size/512;
|
|
// absolute CHS address of the logical partition
|
|
// equals to absolute partition offset
|
|
hdimage_setup_chs(entry, (part->offset - hd->align) / 512);
|
|
struct partition *p = part;
|
|
list_for_each_entry_continue(p, &image->partitions, list) {
|
|
if (!p->logical)
|
|
continue;
|
|
++entry;
|
|
entry->boot = 0x00;
|
|
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_partition->offset / 512);
|
|
break;
|
|
}
|
|
|
|
part_table += 4 * sizeof(struct mbr_partition_entry);
|
|
part_table[0] = 0x55;
|
|
part_table[1] = 0xaa;
|
|
|
|
ret = insert_data(image, ebr, imageoutfile(image), sizeof(ebr),
|
|
ebr_offset);
|
|
if (ret) {
|
|
image_error(image, "failed to write EBR\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct gpt_partition_type_shortcut_t
|
|
{
|
|
const char * shortcut;
|
|
const char * guid;
|
|
};
|
|
|
|
static const struct gpt_partition_type_shortcut_t gpt_partition_type_shortcuts[] =
|
|
{
|
|
{ "L" , "0fc63daf-8483-4772-8e79-3d69d8477de4" },
|
|
{ "linux" , "0fc63daf-8483-4772-8e79-3d69d8477de4" },
|
|
{ "S" , "0657fd6d-a4ab-43c4-84e5-0933c84b4f4f" },
|
|
{ "swap" , "0657fd6d-a4ab-43c4-84e5-0933c84b4f4f" },
|
|
{ "H" , "933ac7e1-2eb4-4f13-b844-0e14e2aef915" },
|
|
{ "home" , "933ac7e1-2eb4-4f13-b844-0e14e2aef915" },
|
|
{ "U" , "c12a7328-f81f-11d2-ba4b-00a0c93ec93b" },
|
|
{ "uefi" , "c12a7328-f81f-11d2-ba4b-00a0c93ec93b" },
|
|
{ "R" , "a19d880f-05fc-4d3b-a006-743f0f84911e" },
|
|
{ "raid" , "a19d880f-05fc-4d3b-a006-743f0f84911e" },
|
|
{ "V" , "e6d6d379-f507-44c2-a23c-238f2a3df928" },
|
|
{ "lvm" , "e6d6d379-f507-44c2-a23c-238f2a3df928" },
|
|
{ "F" , "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7" },
|
|
{ "fat32" , "ebd0a0a2-b9e5-4433-87c0-68b6b72699c7" },
|
|
{ "barebox-state" , "4778ed65-bf42-45fa-9c5b-287a1dc4aab1" },
|
|
{ "barebox-env" , "6c3737f2-07f8-45d1-ad45-15d260aab24d" },
|
|
{ "u-boot-env" , "3de21764-95bd-54bd-a5c3-4abe786f38a8" },
|
|
/* Discoverable Partitions Specification GUID, see
|
|
* https://uapi-group.org/specifications/specs/discoverable_partitions_specification/
|
|
*/
|
|
{ "root-alpha" , "6523f8ae-3eb1-4e2a-a05a-18b695ae656f" },
|
|
{ "root-arc" , "d27f46ed-2919-4cb8-bd25-9531f3c16534" },
|
|
{ "root-arm" , "69dad710-2ce4-4e3c-b16c-21a1d49abed3" },
|
|
{ "root-arm64" , "b921b045-1df0-41c3-af44-4c6f280d3fae" },
|
|
{ "root-ia64" , "993d8d3d-f80e-4225-855a-9daf8ed7ea97" },
|
|
{ "root-loongarch64" , "77055800-792c-4f94-b39a-98c91b762bb6" },
|
|
{ "root-mips" , "e9434544-6e2c-47cc-bae2-12d6deafb44c" },
|
|
{ "root-mips64" , "d113af76-80ef-41b4-bdb6-0cff4d3d4a25" },
|
|
{ "root-mips-le" , "37c58c8a-d913-4156-a25f-48b1b64e07f0" },
|
|
{ "root-mips64-le" , "700bda43-7a34-4507-b179-eeb93d7a7ca3" },
|
|
{ "root-parisc" , "1aacdb3b-5444-4138-bd9e-e5c2239b2346" },
|
|
{ "root-ppc" , "1de3f1ef-fa98-47b5-8dcd-4a860a654d78" },
|
|
{ "root-ppc64" , "912ade1d-a839-4913-8964-a10eee08fbd2" },
|
|
{ "root-ppc64-le" , "c31c45e6-3f39-412e-80fb-4809c4980599" },
|
|
{ "root-riscv32" , "60d5a7fe-8e7d-435c-b714-3dd8162144e1" },
|
|
{ "root-riscv64" , "72ec70a6-cf74-40e6-bd49-4bda08e8f224" },
|
|
{ "root-s390" , "08a7acea-624c-4a20-91e8-6e0fa67d23f9" },
|
|
{ "root-s390x" , "5eead9a9-fe09-4a1e-a1d7-520d00531306" },
|
|
{ "root-tilegx" , "c50cdd70-3862-4cc3-90e1-809a8c93ee2c" },
|
|
{ "root-x86" , "44479540-f297-41b2-9af7-d131d5f0458a" },
|
|
{ "root-x86-64" , "4f68bce3-e8cd-4db1-96e7-fbcaf984b709" },
|
|
{ "usr-alpha" , "e18cf08c-33ec-4c0d-8246-c6c6fb3da024" },
|
|
{ "usr-arc" , "7978a683-6316-4922-bbee-38bff5a2fecc" },
|
|
{ "usr-arm" , "7d0359a3-02b3-4f0a-865c-654403e70625" },
|
|
{ "usr-arm64" , "b0e01050-ee5f-4390-949a-9101b17104e9" },
|
|
{ "usr-ia64" , "4301d2a6-4e3b-4b2a-bb94-9e0b2c4225ea" },
|
|
{ "usr-loongarch64" , "e611c702-575c-4cbe-9a46-434fa0bf7e3f" },
|
|
{ "usr-mips" , "773b2abc-2a99-4398-8bf5-03baac40d02b" },
|
|
{ "usr-mips64" , "57e13958-7331-4365-8e6e-35eeee17c61b" },
|
|
{ "usr-mips-le" , "0f4868e9-9952-4706-979f-3ed3a473e947" },
|
|
{ "usr-mips64-le" , "c97c1f32-ba06-40b4-9f22-236061b08aa8" },
|
|
{ "usr-parisc" , "dc4a4480-6917-4262-a4ec-db9384949f25" },
|
|
{ "usr-ppc" , "7d14fec5-cc71-415d-9d6c-06bf0b3c3eaf" },
|
|
{ "usr-ppc64" , "2c9739e2-f068-46b3-9fd0-01c5a9afbcca" },
|
|
{ "usr-ppc64-le" , "15bb03af-77e7-4d4a-b12b-c0d084f7491c" },
|
|
{ "usr-riscv32" , "b933fb22-5c3f-4f91-af90-e2bb0fa50702" },
|
|
{ "usr-riscv64" , "beaec34b-8442-439b-a40b-984381ed097d" },
|
|
{ "usr-s390" , "cd0f869b-d0fb-4ca0-b141-9ea87cc78d66" },
|
|
{ "usr-s390x" , "8a4f5770-50aa-4ed3-874a-99b710db6fea" },
|
|
{ "usr-tilegx" , "55497029-c7c1-44cc-aa39-815ed1558630" },
|
|
{ "usr-x86" , "75250d76-8cc6-458e-bd66-bd47cc81a812" },
|
|
{ "usr-x86-64" , "8484680c-9521-48c6-9c11-b0720656f69e" },
|
|
{ "root-alpha-verity" , "fc56d9e9-e6e5-4c06-be32-e74407ce09a5" },
|
|
{ "root-arc-verity" , "24b2d975-0f97-4521-afa1-cd531e421b8d" },
|
|
{ "root-arm-verity" , "7386cdf2-203c-47a9-a498-f2ecce45a2d6" },
|
|
{ "root-arm64-verity" , "df3300ce-d69f-4c92-978c-9bfb0f38d820" },
|
|
{ "root-ia64-verity" , "86ed10d5-b607-45bb-8957-d350f23d0571" },
|
|
{ "root-loongarch64-verity" , "f3393b22-e9af-4613-a948-9d3bfbd0c535" },
|
|
{ "root-mips-verity" , "7a430799-f711-4c7e-8e5b-1d685bd48607" },
|
|
{ "root-mips64-verity" , "579536f8-6a33-4055-a95a-df2d5e2c42a8" },
|
|
{ "root-mips-le-verity" , "d7d150d2-2a04-4a33-8f12-16651205ff7b" },
|
|
{ "root-mips64-le-verity" , "16b417f8-3e06-4f57-8dd2-9b5232f41aa6" },
|
|
{ "root-parisc-verity" , "d212a430-fbc5-49f9-a983-a7feef2b8d0e" },
|
|
{ "root-ppc64-le-verity" , "906bd944-4589-4aae-a4e4-dd983917446a" },
|
|
{ "root-ppc64-verity" , "9225a9a3-3c19-4d89-b4f6-eeff88f17631" },
|
|
{ "root-ppc-verity" , "98cfe649-1588-46dc-b2f0-add147424925" },
|
|
{ "root-riscv32-verity" , "ae0253be-1167-4007-ac68-43926c14c5de" },
|
|
{ "root-riscv64-verity" , "b6ed5582-440b-4209-b8da-5ff7c419ea3d" },
|
|
{ "root-s390-verity" , "7ac63b47-b25c-463b-8df8-b4a94e6c90e1" },
|
|
{ "root-s390x-verity" , "b325bfbe-c7be-4ab8-8357-139e652d2f6b" },
|
|
{ "root-tilegx-verity" , "966061ec-28e4-4b2e-b4a5-1f0a825a1d84" },
|
|
{ "root-x86-64-verity" , "2c7357ed-ebd2-46d9-aec1-23d437ec2bf5" },
|
|
{ "root-x86-verity" , "d13c5d3b-b5d1-422a-b29f-9454fdc89d76" },
|
|
{ "usr-alpha-verity" , "8cce0d25-c0d0-4a44-bd87-46331bf1df67" },
|
|
{ "usr-arc-verity" , "fca0598c-d880-4591-8c16-4eda05c7347c" },
|
|
{ "usr-arm-verity" , "c215d751-7bcd-4649-be90-6627490a4c05" },
|
|
{ "usr-arm64-verity" , "6e11a4e7-fbca-4ded-b9e9-e1a512bb664e" },
|
|
{ "usr-ia64-verity" , "6a491e03-3be7-4545-8e38-83320e0ea880" },
|
|
{ "usr-loongarch64-verity" , "f46b2c26-59ae-48f0-9106-c50ed47f673d" },
|
|
{ "usr-mips-verity" , "6e5a1bc8-d223-49b7-bca8-37a5fcceb996" },
|
|
{ "usr-mips64-verity" , "81cf9d90-7458-4df4-8dcf-c8a3a404f09b" },
|
|
{ "usr-mips-le-verity" , "46b98d8d-b55c-4e8f-aab3-37fca7f80752" },
|
|
{ "usr-mips64-le-verity" , "3c3d61fe-b5f3-414d-bb71-8739a694a4ef" },
|
|
{ "usr-parisc-verity" , "5843d618-ec37-48d7-9f12-cea8e08768b2" },
|
|
{ "usr-ppc64-le-verity" , "ee2b9983-21e8-4153-86d9-b6901a54d1ce" },
|
|
{ "usr-ppc64-verity" , "bdb528a5-a259-475f-a87d-da53fa736a07" },
|
|
{ "usr-ppc-verity" , "df765d00-270e-49e5-bc75-f47bb2118b09" },
|
|
{ "usr-riscv32-verity" , "cb1ee4e3-8cd0-4136-a0a4-aa61a32e8730" },
|
|
{ "usr-riscv64-verity" , "8f1056be-9b05-47c4-81d6-be53128e5b54" },
|
|
{ "usr-s390-verity" , "b663c618-e7bc-4d6d-90aa-11b756bb1797" },
|
|
{ "usr-s390x-verity" , "31741cc4-1a2a-4111-a581-e00b447d2d06" },
|
|
{ "usr-tilegx-verity" , "2fb4bf56-07fa-42da-8132-6b139f2026ae" },
|
|
{ "usr-x86-64-verity" , "77ff5f63-e7b6-4633-acf4-1565b864c0e6" },
|
|
{ "usr-x86-verity" , "8f461b0d-14ee-4e81-9aa9-049b6fb97abd" },
|
|
{ "root-alpha-verity-sig" , "d46495b7-a053-414f-80f7-700c99921ef8" },
|
|
{ "root-arc-verity-sig" , "143a70ba-cbd3-4f06-919f-6c05683a78bc" },
|
|
{ "root-arm-verity-sig" , "42b0455f-eb11-491d-98d3-56145ba9d037" },
|
|
{ "root-arm64-verity-sig" , "6db69de6-29f4-4758-a7a5-962190f00ce3" },
|
|
{ "root-ia64-verity-sig" , "e98b36ee-32ba-4882-9b12-0ce14655f46a" },
|
|
{ "root-loongarch64-verity-sig" , "5afb67eb-ecc8-4f85-ae8e-ac1e7c50e7d0" },
|
|
{ "root-mips-verity-sig" , "bba210a2-9c5d-45ee-9e87-ff2ccbd002d0" },
|
|
{ "root-mips64-verity-sig" , "43ce94d4-0f3d-4999-8250-b9deafd98e6e" },
|
|
{ "root-mips-le-verity-sig" , "c919cc1f-4456-4eff-918c-f75e94525ca5" },
|
|
{ "root-mips64-le-verity-sig" , "904e58ef-5c65-4a31-9c57-6af5fc7c5de7" },
|
|
{ "root-parisc-verity-sig" , "15de6170-65d3-431c-916e-b0dcd8393f25" },
|
|
{ "root-ppc64-le-verity-sig" , "d4a236e7-e873-4c07-bf1d-bf6cf7f1c3c6" },
|
|
{ "root-ppc64-verity-sig" , "f5e2c20c-45b2-4ffa-bce9-2a60737e1aaf" },
|
|
{ "root-ppc-verity-sig" , "1b31b5aa-add9-463a-b2ed-bd467fc857e7" },
|
|
{ "root-riscv32-verity-sig" , "3a112a75-8729-4380-b4cf-764d79934448" },
|
|
{ "root-riscv64-verity-sig" , "efe0f087-ea8d-4469-821a-4c2a96a8386a" },
|
|
{ "root-s390-verity-sig" , "3482388e-4254-435a-a241-766a065f9960" },
|
|
{ "root-s390x-verity-sig" , "c80187a5-73a3-491a-901a-017c3fa953e9" },
|
|
{ "root-tilegx-verity-sig" , "b3671439-97b0-4a53-90f7-2d5a8f3ad47b" },
|
|
{ "root-x86-64-verity-sig" , "41092b05-9fc8-4523-994f-2def0408b176" },
|
|
{ "root-x86-verity-sig" , "5996fc05-109c-48de-808b-23fa0830b676" },
|
|
{ "usr-alpha-verity-sig" , "5c6e1c76-076a-457a-a0fe-f3b4cd21ce6e" },
|
|
{ "usr-arc-verity-sig" , "94f9a9a1-9971-427a-a400-50cb297f0f35" },
|
|
{ "usr-arm-verity-sig" , "d7ff812f-37d1-4902-a810-d76ba57b975a" },
|
|
{ "usr-arm64-verity-sig" , "c23ce4ff-44bd-4b00-b2d4-b41b3419e02a" },
|
|
{ "usr-ia64-verity-sig" , "8de58bc2-2a43-460d-b14e-a76e4a17b47f" },
|
|
{ "usr-loongarch64-verity-sig" , "b024f315-d330-444c-8461-44bbde524e99" },
|
|
{ "usr-mips-verity-sig" , "97ae158d-f216-497b-8057-f7f905770f54" },
|
|
{ "usr-mips64-verity-sig" , "05816ce2-dd40-4ac6-a61d-37d32dc1ba7d" },
|
|
{ "usr-mips-le-verity-sig" , "3e23ca0b-a4bc-4b4e-8087-5ab6a26aa8a9" },
|
|
{ "usr-mips64-le-verity-sig" , "f2c2c7ee-adcc-4351-b5c6-ee9816b66e16" },
|
|
{ "usr-parisc-verity-sig" , "450dd7d1-3224-45ec-9cf2-a43a346d71ee" },
|
|
{ "usr-ppc64-le-verity-sig" , "c8bfbd1e-268e-4521-8bba-bf314c399557" },
|
|
{ "usr-ppc64-verity-sig" , "0b888863-d7f8-4d9e-9766-239fce4d58af" },
|
|
{ "usr-ppc-verity-sig" , "7007891d-d371-4a80-86a4-5cb875b9302e" },
|
|
{ "usr-riscv32-verity-sig" , "c3836a13-3137-45ba-b583-b16c50fe5eb4" },
|
|
{ "usr-riscv64-verity-sig" , "d2f9000a-7a18-453f-b5cd-4d32f77a7b32" },
|
|
{ "usr-s390-verity-sig" , "17440e4f-a8d0-467f-a46e-3912ae6ef2c5" },
|
|
{ "usr-s390x-verity-sig" , "3f324816-667b-46ae-86ee-9b0c0c6c11b4" },
|
|
{ "usr-tilegx-verity-sig" , "4ede75e2-6ccc-4cc8-b9c7-70334b087510" },
|
|
{ "usr-x86-64-verity-sig" , "e7bb33fb-06cf-4e81-8273-e543b413e2e2" },
|
|
{ "usr-x86-verity-sig" , "974a71c0-de41-43c3-be5d-5c5ccd1ad2c0" },
|
|
{ "esp" , "c12a7328-f81f-11d2-ba4b-00a0c93ec93b" },
|
|
{ "xbootldr" , "bc13c2ff-59e6-4262-a352-b275fd6f7172" },
|
|
{ "srv" , "3b8f8425-20e0-4f3b-907f-1a25a76f98e8" },
|
|
{ "var" , "4d21b016-b534-45c2-a9fb-5c16e091fd2d" },
|
|
{ "tmp" , "7ec6f557-3bc5-4aca-b293-16ef5df639d1" },
|
|
{ "user-home" , "773f91ef-66d4-49b5-bd83-d683bf40ad16" },
|
|
{ "linux-generic" , "0fc63daf-8483-4772-8e79-3d69d8477de4" },
|
|
|
|
{ 0, 0 } /* sentinel */
|
|
};
|
|
|
|
static const char *
|
|
gpt_partition_type_lookup(const char * shortcut)
|
|
{
|
|
const struct gpt_partition_type_shortcut_t * s;
|
|
for(s = gpt_partition_type_shortcuts; s->shortcut; s++) {
|
|
if(strcasecmp(s->shortcut, shortcut) == 0) {
|
|
return s->guid;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int hdimage_insert_protective_mbr(struct image *image)
|
|
{
|
|
struct partition mbr;
|
|
struct list_head mbr_list = LIST_HEAD_INIT(mbr_list);
|
|
int ret = 0;
|
|
|
|
image_info(image, "writing protective MBR\n");
|
|
|
|
memset(&mbr, 0, sizeof(struct partition));
|
|
mbr.offset = 512;
|
|
mbr.size = image->size - 512;
|
|
mbr.in_partition_table = 1;
|
|
mbr.partition_type = 0xee;
|
|
list_add_tail(&mbr.list, &mbr_list);
|
|
ret = hdimage_insert_mbr(image, &mbr_list);
|
|
if (ret) {
|
|
image_error(image,"failed to write protective MBR\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hdimage_insert_gpt(struct image *image, struct list_head *partitions)
|
|
{
|
|
struct hdimage *hd = image->handler_priv;
|
|
const char *outfile = imageoutfile(image);
|
|
struct gpt_header header;
|
|
struct gpt_partition_entry table[GPT_ENTRIES];
|
|
unsigned long long smallest_offset = ~0ULL;
|
|
struct partition *part;
|
|
unsigned i, j;
|
|
int ret;
|
|
|
|
image_info(image, "writing GPT\n");
|
|
|
|
memset(&header, 0, sizeof(struct gpt_header));
|
|
memcpy(header.signature, "EFI PART", 8);
|
|
header.revision = htole32(GPT_REVISION_1_0);
|
|
header.header_size = htole32(sizeof(struct gpt_header));
|
|
header.current_lba = htole64(1);
|
|
header.backup_lba = htole64(hd->gpt_no_backup ? 1 :image->size/512 - 1);
|
|
header.first_usable_lba = htole64(~0ULL);
|
|
header.last_usable_lba = htole64(image->size/512 - 1 - GPT_SECTORS);
|
|
uuid_parse(hd->disk_uuid, header.disk_uuid);
|
|
header.starting_lba = htole64(hd->gpt_location/512);
|
|
header.number_entries = htole32(GPT_ENTRIES);
|
|
header.entry_size = htole32(sizeof(struct gpt_partition_entry));
|
|
|
|
i = 0;
|
|
memset(&table, 0, sizeof(table));
|
|
list_for_each_entry(part, partitions, list) {
|
|
if (!part->in_partition_table)
|
|
continue;
|
|
|
|
if (part->offset < smallest_offset)
|
|
smallest_offset = part->offset;
|
|
|
|
uuid_parse(part->partition_type_uuid, table[i].type_uuid);
|
|
uuid_parse(part->partition_uuid, table[i].uuid);
|
|
table[i].first_lba = htole64(part->offset/512);
|
|
table[i].last_lba = htole64((part->offset + part->size)/512 - 1);
|
|
table[i].flags =
|
|
(part->bootable ? GPT_PE_FLAG_BOOTABLE : 0) |
|
|
(part->read_only ? GPT_PE_FLAG_READ_ONLY : 0) |
|
|
(part->hidden ? GPT_PE_FLAG_HIDDEN : 0) |
|
|
(part->no_automount ? GPT_PE_FLAG_NO_AUTO : 0);
|
|
for (j = 0; j < strlen(part->name) && j < 36; j++)
|
|
table[i].name[j] = htole16(part->name[j]);
|
|
|
|
i++;
|
|
}
|
|
if (smallest_offset == ~0ULL)
|
|
smallest_offset = hd->gpt_location + (GPT_SECTORS - 1)*512;
|
|
header.first_usable_lba = htole64(smallest_offset / 512);
|
|
|
|
|
|
header.table_crc = htole32(crc32(table, sizeof(table)));
|
|
|
|
header.header_crc = htole32(crc32(&header, sizeof(header)));
|
|
ret = insert_data(image, &header, outfile, sizeof(header), 512);
|
|
if (ret) {
|
|
image_error(image, "failed to write GPT\n");
|
|
return ret;
|
|
}
|
|
ret = insert_data(image, &table, outfile, sizeof(table), hd->gpt_location);
|
|
if (ret) {
|
|
image_error(image, "failed to write GPT table\n");
|
|
return ret;
|
|
}
|
|
|
|
if (!hd->gpt_no_backup) {
|
|
ret = extend_file(image, image->size);
|
|
if (ret) {
|
|
image_error(image, "failed to pad image to size %lld\n",
|
|
image->size);
|
|
return ret;
|
|
}
|
|
|
|
header.header_crc = 0;
|
|
header.current_lba = htole64(image->size/512 - 1);
|
|
header.backup_lba = htole64(1);
|
|
header.starting_lba = htole64(image->size/512 - GPT_SECTORS);
|
|
header.header_crc = htole32(crc32(&header, sizeof(header)));
|
|
ret = insert_data(image, &table, outfile, sizeof(table),
|
|
image->size - GPT_SECTORS*512);
|
|
if (ret) {
|
|
image_error(image, "failed to write backup GPT table\n");
|
|
return ret;
|
|
}
|
|
ret = insert_data(image, &header, outfile, sizeof(header),
|
|
image->size - 512);
|
|
if (ret) {
|
|
image_error(image, "failed to write backup GPT\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (hd->table_type == TYPE_HYBRID) {
|
|
ret = hdimage_insert_mbr(image, partitions);
|
|
} else {
|
|
ret = hdimage_insert_protective_mbr(image);
|
|
}
|
|
if (ret) {
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int hdimage_generate(struct image *image)
|
|
{
|
|
struct partition *part;
|
|
struct hdimage *hd = image->handler_priv;
|
|
struct stat s;
|
|
int ret;
|
|
|
|
ret = prepare_image(image, hd->file_size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
list_for_each_entry(part, &image->partitions, list) {
|
|
struct image *child;
|
|
|
|
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->logical) {
|
|
ret = hdimage_insert_ebr(image, part);
|
|
if (ret) {
|
|
image_error(image, "failed to write EBR\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (!part->image)
|
|
continue;
|
|
|
|
child = image_get(part->image);
|
|
|
|
if (child->size == 0 && !part->fill)
|
|
continue;
|
|
|
|
if (child->size > part->size) {
|
|
image_error(image, "part %s size (%lld) too small for %s (%lld)\n",
|
|
part->name, part->size, child->file, child->size);
|
|
return -E2BIG;
|
|
}
|
|
|
|
ret = insert_image(image, child, part->fill ? part->size : child->size, part->offset, 0);
|
|
if (ret) {
|
|
image_error(image, "failed to write image partition '%s'\n",
|
|
part->name);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (hd->table_type != TYPE_NONE) {
|
|
if (hd->table_type & TYPE_GPT) {
|
|
ret = hdimage_insert_gpt(image, &image->partitions);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
else {
|
|
ret = hdimage_insert_mbr(image, &image->partitions);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (hd->fill) {
|
|
ret = extend_file(image, image->size);
|
|
if (ret) {
|
|
image_error(image, "failed to fill the image.\n");
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (!is_block_device(imageoutfile(image))) {
|
|
ret = stat(imageoutfile(image), &s);
|
|
if (ret) {
|
|
ret = -errno;
|
|
image_error(image, "stat(%s) failed: %s\n", imageoutfile(image),
|
|
strerror(errno));
|
|
return ret;
|
|
}
|
|
if (hd->file_size != (unsigned long long)s.st_size) {
|
|
image_error(image, "unexpected output file size: %llu != %llu\n",
|
|
hd->file_size, (unsigned long long)s.st_size);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
if (hd->table_type != TYPE_NONE)
|
|
return reload_partitions(image);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned long long roundup(unsigned long long value, unsigned long long align)
|
|
{
|
|
return ((value - 1)/align + 1) * align;
|
|
}
|
|
|
|
static unsigned long long rounddown(unsigned long long value, unsigned long long align)
|
|
{
|
|
return value - (value % align);
|
|
}
|
|
|
|
static unsigned long long min_ull(unsigned long long x, unsigned long long y)
|
|
{
|
|
return x < y ? x : y;
|
|
}
|
|
|
|
static unsigned long long max_ull(unsigned long long x, unsigned long long y)
|
|
{
|
|
return x > y ? x : y;
|
|
}
|
|
|
|
static bool image_has_hole_covering(const char *image,
|
|
unsigned long long start, unsigned long long end)
|
|
{
|
|
struct image *child;
|
|
int i;
|
|
|
|
if (!image)
|
|
return false;
|
|
child = image_get(image);
|
|
for (i = 0; i < child->n_holes; ++i) {
|
|
const struct extent *e = &child->holes[i];
|
|
if (e->start <= start && end <= e->end)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int check_overlap(struct image *image, struct partition *p)
|
|
{
|
|
unsigned long long start, end;
|
|
struct partition *q;
|
|
|
|
list_for_each_entry(q, &image->partitions, list) {
|
|
/* Stop iterating when we reach p. */
|
|
if (p == q)
|
|
return 0;
|
|
/* We must have that p starts beyond where q ends... */
|
|
if (p->offset >= q->offset + q->size)
|
|
continue;
|
|
/* ...or vice versa. */
|
|
if (q->offset >= p->offset + p->size)
|
|
continue;
|
|
|
|
/*
|
|
* Or maybe the image occupying the q partition has an
|
|
* area which it is ok to overwrite. We do not do the
|
|
* "vice versa" check, since images are written to the
|
|
* output file in the order the partitions are
|
|
* specified.
|
|
*/
|
|
start = max_ull(p->offset, q->offset);
|
|
end = min_ull(p->offset + p->size, q->offset + q->size);
|
|
|
|
if (image_has_hole_covering(q->image, start - q->offset, end - q->offset))
|
|
continue;
|
|
|
|
image_error(image,
|
|
"partition %s (offset 0x%llx, size 0x%llx) overlaps previous "
|
|
"partition %s (offset 0x%llx, size 0x%llx)\n",
|
|
p->name, p->offset, p->size,
|
|
q->name, q->offset, q->size);
|
|
if (!q->in_partition_table &&
|
|
(!strcmp(p->name, "[MBR]") || !strncmp(p->name, "[GPT", 4)))
|
|
image_error(image, "bootloaders, etc. that overlap with the "
|
|
"partition table must declare the overlapping "
|
|
"area as a hole.\n");
|
|
return -EINVAL;
|
|
}
|
|
/* This should not be reached. */
|
|
image_error(image, "linked list corruption???\n");
|
|
return -EIO;
|
|
}
|
|
|
|
static struct partition *
|
|
fake_partition(const char *name, unsigned long long offset, unsigned long long size)
|
|
{
|
|
struct partition *p = xzalloc(sizeof(*p));
|
|
|
|
p->name = name;
|
|
p->offset = offset;
|
|
p->size = size;
|
|
p->align = 1;
|
|
return p;
|
|
}
|
|
|
|
static void ensure_extended_partition_index(struct image *image)
|
|
{
|
|
struct hdimage *hd = image->handler_priv;
|
|
struct partition *part;
|
|
int count = 0;
|
|
|
|
if (hd->extended_partition_index)
|
|
return;
|
|
|
|
list_for_each_entry(part, &image->partitions, list) {
|
|
if (!part->in_partition_table)
|
|
continue;
|
|
|
|
if (++count > 4) {
|
|
hd->extended_partition_index = 4;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int setup_logical_partitions(struct image *image)
|
|
{
|
|
struct hdimage *hd = image->handler_priv;
|
|
struct partition *part;
|
|
bool in_extended = false, found_extended = false;
|
|
unsigned int count = 0, mbr_entries = 0;
|
|
|
|
if (hd->extended_partition_index > 4) {
|
|
image_error(image, "invalid extended partition index (%i). must be "
|
|
"less or equal to 4 (0 for automatic)\n",
|
|
hd->extended_partition_index);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (hd->table_type != TYPE_MBR)
|
|
return 0;
|
|
|
|
ensure_extended_partition_index(image);
|
|
|
|
if (!hd->extended_partition_index)
|
|
return 0;
|
|
|
|
list_for_each_entry(part, &image->partitions, list) {
|
|
if (!part->in_partition_table)
|
|
continue;
|
|
|
|
++count;
|
|
|
|
if (hd->extended_partition_index == count) {
|
|
size_t offset = part->offset ? part->offset - hd->align : 0;
|
|
struct partition *p = fake_partition("[Extended]", offset, 0);
|
|
p->in_partition_table = true;
|
|
p->partition_type = PARTITION_TYPE_EXTENDED;
|
|
p->align = hd->align;
|
|
|
|
hd->extended_partition = p;
|
|
/* insert before the first logical partition */
|
|
list_add_tail(&p->list, &part->list);
|
|
|
|
in_extended = found_extended = true;
|
|
++mbr_entries;
|
|
}
|
|
|
|
if (part->forced_primary)
|
|
in_extended = false;
|
|
if (in_extended && !part->forced_primary)
|
|
part->logical = true;
|
|
else
|
|
++mbr_entries;
|
|
|
|
if (part->forced_primary) {
|
|
if (!found_extended) {
|
|
image_error(image, "partition %s: forced-primary can only be used for "
|
|
"partitions following the extended partition\n",
|
|
part->name);
|
|
return -EINVAL;
|
|
}
|
|
} else if (!in_extended && found_extended) {
|
|
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;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int setup_uuid(struct image *image, cfg_t *cfg)
|
|
{
|
|
struct hdimage *hd = image->handler_priv;
|
|
const char *disk_signature = cfg_getstr(cfg, "disk-signature");
|
|
|
|
hd->disk_uuid = cfg_getstr(cfg, "disk-uuid");
|
|
if (hd->disk_uuid) {
|
|
if (!(hd->table_type & TYPE_GPT)) {
|
|
image_error(image, "'disk-uuid' is only valid for gpt and hybrid partition-table-type\n");
|
|
return -EINVAL;
|
|
}
|
|
if (uuid_validate(hd->disk_uuid) == -1) {
|
|
image_error(image, "invalid disk UUID: %s\n", hd->disk_uuid);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
else {
|
|
hd->disk_uuid = uuid_random();
|
|
}
|
|
|
|
if (!disk_signature)
|
|
hd->disksig = 0;
|
|
else if (!strcmp(disk_signature, "random"))
|
|
hd->disksig = random();
|
|
else {
|
|
if (!(hd->table_type & TYPE_MBR)) {
|
|
image_error(image, "'disk-signature' is only valid for mbr and hybrid partition-table-type\n");
|
|
return -EINVAL;
|
|
}
|
|
hd->disksig = strtoul(disk_signature, NULL, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int setup_part_autoresize(struct image *image, struct partition *part, bool *resized)
|
|
{
|
|
struct hdimage *hd = image->handler_priv;
|
|
long long partsize;
|
|
|
|
if (!part->autoresize)
|
|
return 0;
|
|
|
|
if (*resized) {
|
|
image_error(image, "'autoresize' is only supported "
|
|
"for one partition\n");
|
|
return -EINVAL;
|
|
}
|
|
if (image->size == 0) {
|
|
image_error(image, "the image size must be specified "
|
|
"when using an 'autoresize' partition\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
partsize = image->size - part->offset;
|
|
if (hd->table_type & TYPE_GPT)
|
|
partsize -= GPT_SECTORS * 512;
|
|
partsize = rounddown(partsize, part->align);
|
|
if (partsize <= 0) {
|
|
image_error(image, "partitions exceed device size\n");
|
|
return -EINVAL;
|
|
}
|
|
if (partsize < (long long)part->size) {
|
|
image_error(image, "auto-resize partition %s ends up with a size %lld"
|
|
" smaller than minimum %lld\n", part->name, partsize, part->size);
|
|
return -EINVAL;
|
|
}
|
|
part->size = partsize;
|
|
|
|
*resized = true;
|
|
return 0;
|
|
}
|
|
|
|
static int setup_part_image(struct image *image, struct partition *part)
|
|
{
|
|
struct hdimage *hd = image->handler_priv;
|
|
struct image *child;
|
|
|
|
if (!part->image)
|
|
return 0;
|
|
|
|
child = image_get(part->image);
|
|
if (!child) {
|
|
image_error(image, "could not find %s\n",
|
|
part->image);
|
|
return -EINVAL;
|
|
}
|
|
if (!part->size) {
|
|
if (part->in_partition_table)
|
|
part->size = roundup(child->size, part->align);
|
|
else
|
|
part->size = child->size;
|
|
}
|
|
if (child->size > part->size) {
|
|
image_error(image, "part %s size (%lld) too small for %s (%lld)\n",
|
|
part->name, part->size, child->file, child->size);
|
|
return -EINVAL;
|
|
}
|
|
if (part->offset + child->size > hd->file_size) {
|
|
size_t file_size = part->offset + child->size;
|
|
if (file_size > hd->file_size)
|
|
hd->file_size = file_size;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hdimage_setup(struct image *image, cfg_t *cfg)
|
|
{
|
|
struct partition *part;
|
|
unsigned int partition_table_entries = 0, hybrid_entries = 0;
|
|
unsigned long long now = 0;
|
|
const char *table_type;
|
|
struct hdimage *hd = xzalloc(sizeof(*hd));
|
|
struct partition *gpt_backup = NULL;
|
|
bool partition_resized = false;
|
|
int ret;
|
|
|
|
image->handler_priv = hd;
|
|
hd->align = cfg_getint_suffix(cfg, "align");
|
|
hd->extended_partition_index = cfg_getint(cfg, "extended-partition");
|
|
table_type = cfg_getstr(cfg, "partition-table-type");
|
|
hd->gpt_location = cfg_getint_suffix(cfg, "gpt-location");
|
|
hd->gpt_no_backup = cfg_getbool(cfg, "gpt-no-backup");
|
|
hd->fill = cfg_getbool(cfg, "fill");
|
|
|
|
if (is_block_device(imageoutfile(image))) {
|
|
if (image->size) {
|
|
image_error(image, "image size must not be specified for a block device target\n");
|
|
return -EINVAL;
|
|
}
|
|
ret = block_device_size(image, imageoutfile(image), &image->size);
|
|
if (ret)
|
|
return ret;
|
|
image_info(image, "determined size of block device %s to be %llu\n",
|
|
imageoutfile(image), image->size);
|
|
}
|
|
|
|
if (!strcmp(table_type, "none"))
|
|
hd->table_type = TYPE_NONE;
|
|
else if (!strcmp(table_type, "mbr") || !strcmp(table_type, "dos"))
|
|
hd->table_type = TYPE_MBR;
|
|
else if (!strcmp(table_type, "gpt"))
|
|
hd->table_type = TYPE_GPT;
|
|
else if (!strcmp(table_type, "hybrid"))
|
|
hd->table_type = TYPE_HYBRID;
|
|
else {
|
|
image_error(image, "'%s' is not a valid partition-table-type\n",
|
|
table_type);
|
|
return -EINVAL;
|
|
}
|
|
if (cfg_size(cfg, "partition-table") > 0) {
|
|
hd->table_type = cfg_getbool(cfg, "partition-table") ? TYPE_MBR : TYPE_NONE;
|
|
image_info(image, "The option 'partition-table' is deprecated. Use 'partition-table-type' instead\n");
|
|
}
|
|
if (cfg_size(cfg, "gpt") > 0) {
|
|
hd->table_type = cfg_getbool(cfg, "gpt") ? TYPE_GPT : TYPE_MBR;
|
|
image_info(image, "The option 'gpt' is deprecated. Use 'partition-table-type' instead\n");
|
|
}
|
|
|
|
if (!hd->align)
|
|
hd->align = hd->table_type == TYPE_NONE ? 1 : 512;
|
|
|
|
if ((hd->table_type != TYPE_NONE) && ((hd->align % 512) || (hd->align == 0))) {
|
|
image_error(image, "partition alignment (%lld) must be a "
|
|
"multiple of 1 sector (512 bytes)\n", hd->align);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = setup_logical_partitions(image);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = setup_uuid(image, cfg);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (hd->gpt_location == 0) {
|
|
hd->gpt_location = 2*512;
|
|
}
|
|
else if (hd->gpt_location % 512) {
|
|
image_error(image, "GPT table location (%lld) must be a "
|
|
"multiple of 1 sector (512 bytes)\n", hd->gpt_location);
|
|
}
|
|
|
|
if (hd->table_type != TYPE_NONE) {
|
|
struct partition *mbr = fake_partition("[MBR]", 512 - sizeof(struct mbr_tail),
|
|
sizeof(struct mbr_tail));
|
|
|
|
list_add_tail(&mbr->list, &image->partitions);
|
|
now = partition_end(mbr);
|
|
if (hd->table_type & TYPE_GPT) {
|
|
struct partition *gpt_header, *gpt_array;
|
|
unsigned long long backup_offset, backup_size;
|
|
|
|
gpt_header = fake_partition("[GPT header]", 512, 512);
|
|
gpt_array = fake_partition("[GPT array]", hd->gpt_location, (GPT_SECTORS - 1) * 512);
|
|
list_add_tail(&gpt_header->list, &image->partitions);
|
|
list_add_tail(&gpt_array->list, &image->partitions);
|
|
now = partition_end(gpt_array);
|
|
|
|
/* Includes both the backup header and array. */
|
|
backup_size = GPT_SECTORS * 512;
|
|
backup_offset = image->size ? image->size - backup_size : 0;
|
|
gpt_backup = fake_partition("[GPT backup]", backup_offset, backup_size);
|
|
list_add_tail(&gpt_backup->list, &image->partitions);
|
|
}
|
|
}
|
|
|
|
partition_table_entries = 0;
|
|
list_for_each_entry(part, &image->partitions, list) {
|
|
if (hd->table_type == TYPE_NONE)
|
|
part->in_partition_table = false;
|
|
|
|
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) {
|
|
image_error(image, "partition alignment (%lld) of partition %s "
|
|
"must be multiple of image alignment (%lld)",
|
|
part->align, part->name, hd->align);
|
|
}
|
|
|
|
if (part->partition_type_uuid && !(hd->table_type & TYPE_GPT)) {
|
|
image_error(image, "part %s: 'partition-type-uuid' is only valid for gpt and hybrid partition-table-type\n",
|
|
part->name);
|
|
return -EINVAL;
|
|
}
|
|
if (part->partition_type && !(hd->table_type & TYPE_MBR)) {
|
|
image_error(image, "part %s: 'partition-type' is only valid for mbr and hybrid partition-table-type\n",
|
|
part->name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if ((hd->table_type & TYPE_GPT) && part->in_partition_table) {
|
|
if (!part->partition_type_uuid)
|
|
part->partition_type_uuid = "L";
|
|
if (strlen(part->partition_type_uuid) > 0 &&
|
|
uuid_validate(part->partition_type_uuid) != 0) {
|
|
const char *uuid;
|
|
uuid = gpt_partition_type_lookup(part->partition_type_uuid);
|
|
if (!uuid) {
|
|
image_error(image,
|
|
"part %s has invalid type shortcut: %s\n",
|
|
part->name, part->partition_type_uuid);
|
|
return -EINVAL;
|
|
}
|
|
part->partition_type_uuid = uuid;
|
|
}
|
|
if (uuid_validate(part->partition_type_uuid) == -1) {
|
|
image_error(image,
|
|
"part %s has invalid partition type UUID: %s\n",
|
|
part->name, part->partition_type_uuid);
|
|
return -EINVAL;
|
|
}
|
|
if (part->partition_uuid) {
|
|
if (uuid_validate(part->partition_uuid) == -1) {
|
|
image_error(image,
|
|
"part %s has invalid partition UUID: %s\n",
|
|
part->name, part->partition_uuid);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
else {
|
|
part->partition_uuid = uuid_random();
|
|
}
|
|
if (part->partition_type)
|
|
++hybrid_entries;
|
|
}
|
|
if (part->in_partition_table)
|
|
++partition_table_entries;
|
|
if (part->logical) {
|
|
/* reserve space for extended boot record */
|
|
now += hd->align;
|
|
now = roundup(now, part->align);
|
|
}
|
|
if (part == gpt_backup && !part->offset) {
|
|
/*
|
|
* Make sure the backup, and hence the whole
|
|
* image, ends at a 4K boundary.
|
|
*/
|
|
now += part->size;
|
|
part->offset = roundup(now, 4096) - part->size;
|
|
}
|
|
if (!part->offset && (part->in_partition_table || hd->table_type == TYPE_NONE)) {
|
|
part->offset = roundup(now, part->align);
|
|
}
|
|
|
|
if (part->offset % part->align) {
|
|
image_error(image, "part %s offset (%lld) must be a"
|
|
"multiple of %lld bytes\n",
|
|
part->name, part->offset, part->align);
|
|
return -EINVAL;
|
|
}
|
|
ret = setup_part_autoresize(image, part, &partition_resized);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = setup_part_image(image, part);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* the size of the extended partition will be filled in later */
|
|
if (!part->size && part != hd->extended_partition) {
|
|
image_error(image, "part %s size must not be zero\n",
|
|
part->name);
|
|
return -EINVAL;
|
|
}
|
|
if (!part->logical) {
|
|
ret = check_overlap(image, part);
|
|
if (ret)
|
|
return ret;
|
|
} else if (now > part->offset) {
|
|
image_error(image, "part %s overlaps with previous partition\n",
|
|
part->name);
|
|
return -EINVAL;
|
|
}
|
|
if (part->in_partition_table && (part->size % 512)) {
|
|
image_error(image, "part %s size (%lld) must be a "
|
|
"multiple of 1 sector (512 bytes)\n",
|
|
part->name, part->size);
|
|
return -EINVAL;
|
|
}
|
|
if (part->offset + part->size > now)
|
|
now = part->offset + part->size;
|
|
|
|
if (part->logical) {
|
|
size_t file_size = part->offset - hd->align + 512;
|
|
if (file_size > hd->file_size)
|
|
hd->file_size = file_size;
|
|
}
|
|
|
|
if (part->logical) {
|
|
hd->extended_partition->size = now - hd->extended_partition->offset;
|
|
}
|
|
}
|
|
|
|
if (hybrid_entries > 3) {
|
|
image_error(image, "hybrid MBR partitions (%i) exceeds maximum of 3\n",
|
|
hybrid_entries);
|
|
return -EINVAL;
|
|
}
|
|
if (hd->table_type == TYPE_HYBRID && hybrid_entries == 0) {
|
|
image_error(image, "no partition with partition-type but hybrid partition-table-type selected\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (image->size > 0 && now > image->size) {
|
|
image_error(image, "partitions exceed device size\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (image->size == 0) {
|
|
image->size = now;
|
|
}
|
|
|
|
if (hd->fill || ((hd->table_type & TYPE_GPT) && !hd->gpt_no_backup))
|
|
hd->file_size = image->size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static cfg_opt_t hdimage_opts[] = {
|
|
CFG_STR("align", NULL, CFGF_NONE),
|
|
CFG_STR("disk-signature", NULL, CFGF_NONE),
|
|
CFG_STR("disk-uuid", NULL, CFGF_NONE),
|
|
CFG_BOOL("partition-table", cfg_false, CFGF_NODEFAULT),
|
|
CFG_INT("extended-partition", 0, CFGF_NONE),
|
|
CFG_STR("partition-table-type", "mbr", CFGF_NONE),
|
|
CFG_BOOL("gpt", cfg_false, CFGF_NODEFAULT),
|
|
CFG_STR("gpt-location", NULL, CFGF_NONE),
|
|
CFG_BOOL("gpt-no-backup", cfg_false, CFGF_NONE),
|
|
CFG_BOOL("fill", cfg_false, CFGF_NONE),
|
|
CFG_END()
|
|
};
|
|
|
|
struct image_handler hdimage_handler = {
|
|
.type = "hdimage",
|
|
.no_rootpath = cfg_true,
|
|
.generate = hdimage_generate,
|
|
.setup = hdimage_setup,
|
|
.opts = hdimage_opts,
|
|
};
|
|
|