/* * Copyright (c) 2011 Sascha Hauer , Pengutronix * (c) 2011 Michael Olbrich * * 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 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include "genimage.h" #define TYPE_NONE 0 #define TYPE_MBR 1 #define TYPE_GPT 2 #define TYPE_HYBRID (TYPE_MBR|TYPE_GPT) struct hdimage { unsigned int extended_partition; unsigned long long align; unsigned long long extended_lba; 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) { lba_to_chs(entry->relative_sectors, entry->first_chs); lba_to_chs(entry->relative_sectors + entry->total_sectors - 1, 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) 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; } hdimage_setup_chs(entry); if (part->extended) break; 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); } 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; image_info(image, "writing EBR\n"); 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; hdimage_setup_chs(entry); struct partition *p = part; list_for_each_entry_continue(p, &image->partitions, list) { if (!p->extended) continue; ++entry; entry->boot = 0x00; entry->partition_type = 0x0F; entry->relative_sectors = (p->offset - hd->align - hd->extended_lba)/512; entry->total_sectors = (p->size + hd->align)/512; hdimage_setup_chs(entry); 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), part->offset - hd->align + 446); 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" }, /* 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 partition '%s'%s%s%s%s ...\n", part->name, part->in_partition_table ? " (in MBR)" : "", part->image ? " from '": "", part->image ? part->image : "", part->image ? "'" : ""); if (part->extended) { 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 int hdimage_setup(struct image *image, cfg_t *cfg) { struct partition *part; struct partition *autoresize_part = NULL; int has_extended; unsigned int partition_table_entries = 0, hybrid_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"); disk_signature = cfg_getstr(cfg, "disk-signature"); 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"); hd->disk_uuid = cfg_getstr(cfg, "disk-uuid"); if (is_block_device(imageoutfile(image))) { int ret; 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->extended_partition > 4) { image_error(image, "invalid extended partition index (%i). must be " "inferior or equal to 4 (0 for automatic)\n", hd->extended_partition); return -EINVAL; } 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; } 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 (!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 (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)) { 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); } 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 (part->autoresize) { if (autoresize_part) { image_error(image, "'autoresize' is only supported " "for one partition\n"); return -EINVAL; } autoresize_part = part; if (image->size == 0) { image_error(image, "the image size must be specified " "when using an 'autoresize' partition\n"); return -EINVAL; } } 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; } /* 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) { 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->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" "multiple of %lld bytes\n", part->name, part->offset, part->align); return -EINVAL; } if (part->autoresize) { long long partsize = image->size - part->offset; if (gpt_backup) partsize -= gpt_backup->size; 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; } if (part->image) { struct image *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->size) { image_error(image, "part %s size must not be zero\n", part->name); return -EINVAL; } if (!part->extended) { int 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->image) { struct image *child = image_get(part->image); if (part->offset + child->size > hd->file_size) { hd->file_size = part->offset + child->size; } } else if (part->extended) hd->file_size = part->offset - hd->align + 512; } 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; image->handler_priv = hd; 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, };