From f890bd41225f86a29b0c851f4659a6f9ee68a25b Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Mon, 5 Jan 2026 23:41:02 +0300 Subject: [PATCH 1/4] ptgen: fix bug caused by not completely correct reverts The commit 0782d243d23e (Revert "ptgen: do not create stub partition to fill a gap if gap caused by alignment") fixes one issue but introduce another. If "-e " option is used and there is no gap between GPT Entry Table and 1-st partition, then 1) A GPT stub partition will be created, but this should not be done because of no space for it. 2) A stub partition will be incorrect (start_sector > end_sector) This patch fixes an issue. Fixes: 0782d243d23e (Revert "ptgen: do not create stub partition to fill a gap if gap caused by alignment") Signed-off-by: Mikhail Kshevetskiy --- src/ptgen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ptgen.c b/src/ptgen.c index d480908..dd27b07 100644 --- a/src/ptgen.c +++ b/src/ptgen.c @@ -489,7 +489,7 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) printf("%" PRIu64 "\n", (sect - start) * DISK_SECTOR_SIZE); } - if (parts[0].actual_start > GPT_FIRST_ENTRY_SECTOR + GPT_SIZE) { + if (parts[0].actual_start > gpt_first_entry_sector + GPT_SIZE) { gpte[GPT_ENTRY_MAX - 1].start = cpu_to_le64(gpt_first_entry_sector + GPT_SIZE); gpte[GPT_ENTRY_MAX - 1].end = cpu_to_le64(parts[0].actual_start - 1); gpte[GPT_ENTRY_MAX - 1].type = GUID_PARTITION_BIOS_BOOT; From da0f7837be4716d3ed9402cdc9504dc94a66d7a6 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Mon, 5 Jan 2026 07:55:50 +0300 Subject: [PATCH 2/4] ptgen: allow to specify index of gpt entries to be used This patch allows to specify the GPT entry index to use. Subsequent partitions will use sequential indexes, starting with the specified one. This will continue until a new starting index is specified. The patch ensures that the same index will never be used twice. [Explanation within example] ptgen creates gpt partitions using consecutive indexes starting from zero. For example the following command ptgen -o mmc.img -g -d 32M \ -p 2M \ # describe /dev/mmcblk0p1 (index=0) -p 4M \ # describe /dev/mmcblk0p2 (index=1) -p 25M # describe /dev/mmcblk0p3 (index=2) will create $ /sbin/fdisk mmc.img ... Device Start End Sectors Size Type mmc.img1 34 4129 4096 2M Linux filesystem <= see here mmc.img2 4130 12321 8192 4M Linux filesystem mmc.img3 12322 63521 51200 25M Linux filesystem Sometimes it's necessary to create one or more partitions whose indexes don't follow above rule. For example, we might want the partition located at the very beginning of the mmc flash to be named as /dev/mmcblk0p7. Within a patch this can be achieved by a command ptgen -o mmc.img -g -d 32M \ -i 6 -p 2M \ # describe /dev/mmcblk0p7 (index=6) -i 0 -p 4M \ # describe /dev/mmcblk0p1 (index=0) -p 20M # describe /dev/mmcblk0p2 (index=1) $ /sbin/fdisk mmc.img ... Device Start End Sectors Size Type mmc.img1 4130 12321 8192 4M Linux filesystem mmc.img2 12322 63521 51200 25M Linux filesystem mmc.img7 34 4129 4096 2M Linux filesystem <== see here So we done it. Signed-off-by: Mikhail Kshevetskiy --- src/ptgen.c | 53 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 13 deletions(-) diff --git a/src/ptgen.c b/src/ptgen.c index dd27b07..d0786e9 100644 --- a/src/ptgen.c +++ b/src/ptgen.c @@ -131,6 +131,7 @@ struct partinfo { bool has_guid; guid_t guid; uint64_t gattr; /* GPT partition attributes */ + unsigned part_index; /* index of GPT entry to be used */ }; /* GPT Partition table header */ @@ -170,6 +171,7 @@ int kb_align = 0; bool ignore_null_sized_partition = false; bool use_guid_partition_table = false; struct partinfo parts[GPT_ENTRY_MAX]; +bool entry_used[GPT_ENTRY_MAX] = { false }; char *filename = NULL; int gpt_split_image = false; @@ -422,7 +424,7 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) uint64_t start, end; uint64_t sect = GPT_SIZE + gpt_first_entry_sector; int fd, ret = -1; - unsigned i, pmbr = 1; + unsigned i, index, pmbr = 1; char img_name[strlen(filename) + 20]; memset(pte, 0, sizeof(struct pte) * MBR_ENTRY_MAX); @@ -452,13 +454,15 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) return ret; } parts[i].actual_start = start; - gpte[i].start = cpu_to_le64(start); + + index = parts[i].part_index; + gpte[index].start = cpu_to_le64(start); sect = start + parts[i].size * 2; - gpte[i].end = cpu_to_le64(sect -1); - gpte[i].guid = guid; - gpte[i].guid.b[sizeof(guid_t) -1] += i + 1; - gpte[i].type = parts[i].guid; + gpte[index].end = cpu_to_le64(sect -1); + gpte[index].guid = guid; + gpte[index].guid.b[sizeof(guid_t) -1] += i + 1; + gpte[index].type = parts[i].guid; if (parts[i].hybrid && pmbr < MBR_ENTRY_MAX) { pte[pmbr].active = ((i + 1) == active) ? 0x80 : 0; @@ -469,16 +473,16 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) to_chs(sect - 1, pte[1].chs_end); pmbr++; } - gpte[i].attr = parts[i].gattr; + gpte[index].attr = parts[i].gattr; if (parts[i].name) - init_utf16(parts[i].name, (uint16_t *)gpte[i].name, GPT_ENTRY_NAME_SIZE / sizeof(uint16_t)); + init_utf16(parts[i].name, (uint16_t *)gpte[index].name, GPT_ENTRY_NAME_SIZE / sizeof(uint16_t)); if ((i + 1) == (unsigned)active) - gpte[i].attr |= GPT_ATTR_LEGACY_BOOT; + gpte[index].attr |= GPT_ATTR_LEGACY_BOOT; if (parts[i].required) - gpte[i].attr |= GPT_ATTR_PLAT_REQUIRED; + gpte[index].attr |= GPT_ATTR_PLAT_REQUIRED; if (verbose) fprintf(stderr, "Partition %d: start=%" PRIu64 ", end=%" PRIu64 ", size=%" PRIu64 "\n", @@ -489,7 +493,8 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) printf("%" PRIu64 "\n", (sect - start) * DISK_SECTOR_SIZE); } - if (parts[0].actual_start > gpt_first_entry_sector + GPT_SIZE) { + if (parts[0].actual_start > gpt_first_entry_sector + GPT_SIZE && + !entry_used[GPT_ENTRY_MAX - 1]) { gpte[GPT_ENTRY_MAX - 1].start = cpu_to_le64(gpt_first_entry_sector + GPT_SIZE); gpte[GPT_ENTRY_MAX - 1].end = cpu_to_le64(parts[0].actual_start - 1); gpte[GPT_ENTRY_MAX - 1].type = GUID_PARTITION_BIOS_BOOT; @@ -620,7 +625,7 @@ static void usage(char *prog) { fprintf(stderr, "Usage: %s [-v] [-n] [-b] [-g] -h -s -o \n" " [-a ] [-l ] [-G ]\n" - " [-e ] [-d ]\n" + " [-e ] [-i ] [-d ]\n" " [[-t | -T ] [-r] [-N ] -p [@]...] \n", prog); exit(EXIT_FAILURE); @@ -653,6 +658,7 @@ int main (int argc, char **argv) char *p; int ch; int part = 0; + unsigned part_index = 0; char *name = NULL; unsigned short int hybrid = 0, required = 0; uint64_t total_sectors; @@ -660,7 +666,7 @@ int main (int argc, char **argv) guid_t guid = GUID_INIT( signature, 0x2211, 0x4433, \ 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0x00); - while ((ch = getopt(argc, argv, "h:s:p:a:t:T:o:vnbHN:gl:rS:G:e:d:")) != -1) { + while ((ch = getopt(argc, argv, "h:s:p:a:t:T:o:vnbHN:gl:rS:G:e:d:i:")) != -1) { switch (ch) { case 'o': filename = optarg; @@ -719,6 +725,11 @@ int main (int argc, char **argv) fputs("Too many partitions\n", stderr); exit(EXIT_FAILURE); } + if (entry_used[part_index]) { + fprintf(stderr, "Partition entry with index %d already defined\n", part_index); + exit(EXIT_FAILURE); + } + p = strchr(optarg, '@'); if (p) { *(p++) = 0; @@ -731,6 +742,7 @@ int main (int argc, char **argv) parts[part].required = required; parts[part].name = name; parts[part].hybrid = hybrid; + parts[part].part_index = part_index; fprintf(stderr, "part %lld %lld\n", parts[part].start, parts[part].size); parts[part++].type = type; /* @@ -740,6 +752,13 @@ int main (int argc, char **argv) name = NULL; required = 0; hybrid = 0; + + /* mark index as used, switch to next index */ + entry_used[part_index++] = true; + if (part_index > GPT_ENTRY_MAX - 1 || + (!use_guid_partition_table && part_index > 3)) + part_index = 0; + break; case 'N': name = optarg; @@ -747,6 +766,14 @@ int main (int argc, char **argv) case 'r': required = 1; break; + case 'i': + part_index = (int)strtoul(optarg, NULL, 0); + if (part_index > GPT_ENTRY_MAX - 1 || + (!use_guid_partition_table && part_index > 3)) { + fprintf(stderr, "Too big GPT/MBR entry index %d\n", part_index); + exit(EXIT_FAILURE); + } + break; case 't': type = (char)strtoul(optarg, NULL, 16); break; From d79a966226f0d6855e4a7ad710a8e36673abc546 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Mon, 5 Jan 2026 08:47:15 +0300 Subject: [PATCH 3/4] ptgen: add an option to disable stub partition creation ptgen tends to create stub partitions between the end of GPT entry table and the start of the first disk. This patch adds an option to disable this behavior. Signed-off-by: Mikhail Kshevetskiy --- src/ptgen.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/ptgen.c b/src/ptgen.c index d0786e9..a98b4cc 100644 --- a/src/ptgen.c +++ b/src/ptgen.c @@ -170,6 +170,7 @@ int sectors = -1; int kb_align = 0; bool ignore_null_sized_partition = false; bool use_guid_partition_table = false; +bool allow_stub_partition = true; struct partinfo parts[GPT_ENTRY_MAX]; bool entry_used[GPT_ENTRY_MAX] = { false }; char *filename = NULL; @@ -494,7 +495,7 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) } if (parts[0].actual_start > gpt_first_entry_sector + GPT_SIZE && - !entry_used[GPT_ENTRY_MAX - 1]) { + allow_stub_partition && !entry_used[GPT_ENTRY_MAX - 1]) { gpte[GPT_ENTRY_MAX - 1].start = cpu_to_le64(gpt_first_entry_sector + GPT_SIZE); gpte[GPT_ENTRY_MAX - 1].end = cpu_to_le64(parts[0].actual_start - 1); gpte[GPT_ENTRY_MAX - 1].type = GUID_PARTITION_BIOS_BOOT; @@ -623,7 +624,7 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) static void usage(char *prog) { - fprintf(stderr, "Usage: %s [-v] [-n] [-b] [-g] -h -s -o \n" + fprintf(stderr, "Usage: %s [-v] [-n] [-D] [-b] [-g] -h -s -o \n" " [-a ] [-l ] [-G ]\n" " [-e ] [-i ] [-d ]\n" " [[-t | -T ] [-r] [-N ] -p [@]...] \n", prog); @@ -666,7 +667,7 @@ int main (int argc, char **argv) guid_t guid = GUID_INIT( signature, 0x2211, 0x4433, \ 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0x00); - while ((ch = getopt(argc, argv, "h:s:p:a:t:T:o:vnbHN:gl:rS:G:e:d:i:")) != -1) { + while ((ch = getopt(argc, argv, "h:s:p:a:t:T:o:DvnbHN:gl:rS:G:e:d:i:")) != -1) { switch (ch) { case 'o': filename = optarg; @@ -674,6 +675,9 @@ int main (int argc, char **argv) case 'v': verbose++; break; + case 'D': + allow_stub_partition = false; + break; case 'n': ignore_null_sized_partition = true; break; From 702db157d2420c33a51c936d3bde2ea0c30ee2b4 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Mon, 5 Jan 2026 11:15:50 +0300 Subject: [PATCH 4/4] ptgen: add long option support ptgen help is bad: - there are a lot of single letter options (so the option name tells nothing) - some options does not mentioned in the ptgen help - there are no any options explanations This patch - adds long option support - provides more usefull help - add description for missed options Signed-off-by: Mikhail Kshevetskiy --- src/ptgen.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/src/ptgen.c b/src/ptgen.c index a98b4cc..61cf830 100644 --- a/src/ptgen.c +++ b/src/ptgen.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "cyg_crc.h" #if __BYTE_ORDER == __BIG_ENDIAN @@ -624,10 +625,35 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) static void usage(char *prog) { - fprintf(stderr, "Usage: %s [-v] [-n] [-D] [-b] [-g] -h -s -o \n" - " [-a ] [-l ] [-G ]\n" - " [-e ] [-i ] [-d ]\n" - " [[-t | -T ] [-r] [-N ] -p [@]...] \n", prog); + fprintf(stderr, "Usage: %s [OPTION]...\n" + "Generate hard drive image with predefined partitions\n" + "\n" + "Mandatory arguments to long options are mandatory for short options too.\n" + " -?, --help display this help and exit\n" + " -v, --verbose enable vervose output\n" + " -l, --alignment=SIZE align partition boundaries to SIZE Kbytes\n" + " -n, --ignore-null-sized-parts do not create null sized partitions\n" + " -D, --disable-gpt-stub-part do not fill a gap before 1-st GPT partition\n" + " -o, --output=FILENAME write an image to a file FILENAME\n" + " -h, --heads=COUNT use CHS scheme, defines heads number\n" + " -s, --sectors=COUNT use CHS scheme, defines sectors count\n" + " -S, --mbr-disk-signature=VALUE defines MBR disk signature [default: 0x5452574F ('OWRT')]\n" + " -a, --active-part=PART_NUMBER defines active (boot) partition\n" + " -g, --gpt use GPT instead of MBR\n" + " -G, --gpt-guid=GUID defines custom GPT GUID\n" + " -e, --gpt-entry-offset=OFFSET defines custom placement of GPT Entry table (default: 1K)\n" + " -d, --gpt-disk-size=SIZE defines total size of disk image (used for ALT GPT headers)\n" + " -b, --gpt-split-images generate 2 or 3 images (depends on entry table placement):\n" + " GPT header + GPT Entry Table, Alt Entry Table + ALT Header\n" + " GPT header, GPT Entry Table, Alt Entry Table + ALT Header\n" + " -p, --part=SIZE[@START] defines partition of size SIZE, started at offset START\n" + " -t, --mbr-part-type=TYPE defines partition type by MBR partition type\n" + " -T, --gpt-part-type=TYPE_NAME defines partinion type by GPT type name\n" + " -N, --gpt-part-name=NAME defines GPT partition name\n" + " -r, --gpt-part-attr-required mark partition with GPT required attribute\n" + " -H, --gpt-part-hybrid put GPT partition to the MBR as well\n" + " -i, --gpt-part-index=INDEX use custom INDEX for the partition in the GPT Entry Table\n", + prog); exit(EXIT_FAILURE); } @@ -667,7 +693,39 @@ int main (int argc, char **argv) guid_t guid = GUID_INIT( signature, 0x2211, 0x4433, \ 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0x00); - while ((ch = getopt(argc, argv, "h:s:p:a:t:T:o:DvnbHN:gl:rS:G:e:d:i:")) != -1) { + while (1) { + int option_index = 0; + static struct option long_options[] = { + {"help", no_argument, 0, '?'}, + {"verbose", no_argument, 0, 'v'}, + {"kb_alignment", required_argument, 0, 'l'}, + {"ignore-null-sized-parts", no_argument, 0, 'n'}, + {"disable-gpt-stub-part", no_argument, 0, 'D'}, + {"output", required_argument, 0, 'o'}, + {"heads", required_argument, 0, 'h'}, + {"sectors", required_argument, 0, 's'}, + {"mbr-disk-signature", required_argument, 0, 'S'}, + {"active-part", required_argument, 0, 'a'}, + {"gpt", no_argument, 0, 'g'}, + {"gpt-guid", required_argument, 0, 'G'}, + {"gpt-entry-offset", required_argument, 0, 'e'}, + {"gpt-disk-size", required_argument, 0, 'd'}, + {"gpt-split-images", no_argument, 0, 'b'}, + {"part", required_argument, 0, 'p'}, + {"mbr-part-type", required_argument, 0, 't'}, + {"gpt-part-type", required_argument, 0, 'T'}, + {"gpt-part-name", required_argument, 0, 'N'}, + {"gpt-part-attr-required", no_argument, 0, 'r'}, + {"gpt-part-hybrid", no_argument, 0, 'H'}, + {"gpt-part-index", required_argument, 0, 'i'}, + {NULL, 0, 0, 0 }, + }; + + ch = getopt_long(argc, argv, "?h:s:p:a:t:T:o:DvnbHN:gl:rS:G:e:d:i:", + long_options, &option_index); + if (ch == -1) + break; + switch (ch) { case 'o': filename = optarg;