From f2ec1ea65155efb0c2a0f82a85fa651282ab8958 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Fri, 10 Jan 2025 11:23:54 +0300 Subject: [PATCH 1/7] ptgen: fix misprint and simplify calculation a bit 2 << ((10 * exp) - 1) is equal to 1 << (10 * exp). This allows us simplify a formula and remove extra if. Signed-off-by: Mikhail Kshevetskiy --- src/ptgen.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ptgen.c b/src/ptgen.c index c7d6bc0..e5fee0d 100644 --- a/src/ptgen.c +++ b/src/ptgen.c @@ -203,10 +203,8 @@ static long to_kbytes(const char *string) return 0; } - /* result: number + 1024^(exp) */ - if (exp == 0) - return result; - return result * (2 << ((10 * exp) - 1)); + /* result: number * 1024^(exp) */ + return result * (1 << (10 * exp)); } /* convert the sector number into a CHS value for the partition table */ From 94a794fc33aef11020697d6cea0ecdff449df603 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Fri, 10 Jan 2025 11:31:10 +0300 Subject: [PATCH 2/7] ptgen: use long long instead of long for sizes This is necessary to support large disks. Signed-off-by: Mikhail Kshevetskiy --- src/ptgen.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/ptgen.c b/src/ptgen.c index e5fee0d..24861a0 100644 --- a/src/ptgen.c +++ b/src/ptgen.c @@ -121,9 +121,9 @@ struct pte { }; struct partinfo { - unsigned long actual_start; - unsigned long start; - unsigned long size; + unsigned long long actual_start; + unsigned long long start; + unsigned long long size; int type; int hybrid; char *name; @@ -180,13 +180,13 @@ char *filename = NULL; * * returns the size in KByte */ -static long to_kbytes(const char *string) +static long long to_kbytes(const char *string) { int exp = 0; - long result; + long long result; char *end; - result = strtoul(string, &end, 0); + result = strtoull(string, &end, 0); switch (tolower(*end)) { case 'k' : case '\0' : exp = 0; break; @@ -325,7 +325,7 @@ static inline void init_utf16(char *str, uint16_t *buf, unsigned bufsize) static int gen_ptable(uint32_t signature, int nr) { struct pte pte[MBR_ENTRY_MAX]; - unsigned long start, len, sect = 0; + unsigned long long start, len, sect = 0; int i, fd, ret = -1; memset(pte, 0, sizeof(struct pte) * MBR_ENTRY_MAX); @@ -343,7 +343,7 @@ static int gen_ptable(uint32_t signature, int nr) start = sect + sectors; if (parts[i].start != 0) { if (parts[i].start * 2 < start) { - fprintf(stderr, "Invalid start %ld for partition %d!\n", + fprintf(stderr, "Invalid start %lld for partition %d!\n", parts[i].start, i); return ret; } @@ -362,13 +362,13 @@ static int gen_ptable(uint32_t signature, int nr) to_chs(start + len - 1, pte[i].chs_end); if (verbose) - fprintf(stderr, "Partition %d: start=%ld, end=%ld, size=%ld\n", + fprintf(stderr, "Partition %d: start=%lld, end=%lld, size=%lld\n", i, - (long)start * DISK_SECTOR_SIZE, - (long)(start + len) * DISK_SECTOR_SIZE, - (long)len * DISK_SECTOR_SIZE); - printf("%ld\n", (long)start * DISK_SECTOR_SIZE); - printf("%ld\n", (long)len * DISK_SECTOR_SIZE); + start * DISK_SECTOR_SIZE, + (start + len) * DISK_SECTOR_SIZE, + len * DISK_SECTOR_SIZE); + printf("%lld\n", start * DISK_SECTOR_SIZE); + printf("%lld\n", len * DISK_SECTOR_SIZE); } if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) { @@ -432,7 +432,7 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) start = sect; if (parts[i].start != 0) { if (parts[i].start * 2 < start) { - fprintf(stderr, "Invalid start %ld for partition %d!\n", + fprintf(stderr, "Invalid start %lld for partition %d!\n", parts[i].start, i); return ret; } @@ -645,7 +645,7 @@ int main (int argc, char **argv) parts[part].required = required; parts[part].name = name; parts[part].hybrid = hybrid; - fprintf(stderr, "part %ld %ld\n", parts[part].start, parts[part].size); + fprintf(stderr, "part %lld %lld\n", parts[part].start, parts[part].size); parts[part++].type = type; /* * reset 'name','required' and 'hybrid' From 431b15a6e62683ae8350169a374d28509fe9c58c Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Mon, 30 Dec 2024 04:48:47 +0300 Subject: [PATCH 3/7] ptgen: fix protective MBR partition size Creating gpt partition with ptgen results in invalid PMBR. Steps to reproduce: 1) compile ptgen with WANT_ALTERNATE_PTABLE gcc -Wall -DWANT_ALTERNATE_PTABLE -o ptgen ptgen.c cyg_crc32.c 2) Create an image with ptgen ./ptgen -g -o s.img -p 509m 3) Investigate an image with /sbin/fdisk /sbin/fdisk s.img 4) fdisk reports GPT PMBR size mismatch Welcome to fdisk (util-linux 2.40.2). Changes will remain in memory only, until you decide to write them. Be careful before using the write command. GPT PMBR size mismatch (1042497 != 1042498) will be corrected by write. Command (m for help): 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 24861a0..cc5e744 100644 --- a/src/ptgen.c +++ b/src/ptgen.c @@ -490,7 +490,7 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) pte[0].type = 0xEE; pte[0].start = cpu_to_le32(GPT_HEADER_SECTOR); - pte[0].length = cpu_to_le32(end - GPT_HEADER_SECTOR); + pte[0].length = cpu_to_le32(end + 1 - GPT_HEADER_SECTOR); to_chs(GPT_HEADER_SECTOR, pte[0].chs_start); to_chs(end, pte[0].chs_end); From ef99b9dea5cb2ef4307a0e00377c97acdd593c34 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Mon, 30 Dec 2024 05:28:49 +0300 Subject: [PATCH 4/7] ptgen: do not create stub partition to fill a gap if gap caused by alignment ptgen tends to create stub partitions between the end of GPT entry table and the start of the first disk. Normally fdisk/gdisk aligns partition to 1MB boundary, so there is 2014 sectors gap before the first partition. It is absolutely normal. This patch somehow mimics fdisk/gdisk behavior and does NOT create stub partition in the gap caused by alignment. Signed-off-by: Mikhail Kshevetskiy --- src/ptgen.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ptgen.c b/src/ptgen.c index cc5e744..37eeac0 100644 --- a/src/ptgen.c +++ b/src/ptgen.c @@ -478,7 +478,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].start != 0) && + (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 5f1659cce5c2f252d1a3c1ab0dd37c21f3ff064f Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Mon, 30 Dec 2024 02:58:15 +0300 Subject: [PATCH 5/7] ptgen: allow non-default placement of gpt entry table Usually, the primary GPT entries places in the sector 2 (next sector after the primary GPT header). The boot ROM of some SoCs (like Airoha AN7581) on the other hand reads the bootloader code from only one fixed offset which overlaps with the normal position of the primary GPT entries. This patch adds a parameter to ptgen to customise the position of the primary GPT entries. Signed-off-by: Mikhail Kshevetskiy --- src/ptgen.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/ptgen.c b/src/ptgen.c index 37eeac0..ed9efa3 100644 --- a/src/ptgen.c +++ b/src/ptgen.c @@ -172,6 +172,7 @@ bool use_guid_partition_table = false; struct partinfo parts[GPT_ENTRY_MAX]; char *filename = NULL; +uint64_t gpt_first_entry_sector = GPT_FIRST_ENTRY_SECTOR; /* * parse the size argument, which is either @@ -408,15 +409,15 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) .revision = cpu_to_le32(GPT_REVISION), .size = cpu_to_le32(GPT_HEADER_SIZE), .self = cpu_to_le64(GPT_HEADER_SECTOR), - .first_usable = cpu_to_le64(GPT_FIRST_ENTRY_SECTOR + GPT_ENTRY_SIZE * GPT_ENTRY_MAX / DISK_SECTOR_SIZE), - .first_entry = cpu_to_le64(GPT_FIRST_ENTRY_SECTOR), + .first_usable = cpu_to_le64(gpt_first_entry_sector + GPT_SIZE), + .first_entry = cpu_to_le64(gpt_first_entry_sector), .disk_guid = guid, .entry_num = cpu_to_le32(GPT_ENTRY_MAX), .entry_size = cpu_to_le32(GPT_ENTRY_SIZE), }; struct gpte gpte[GPT_ENTRY_MAX]; uint64_t start, end; - uint64_t sect = GPT_SIZE + GPT_FIRST_ENTRY_SECTOR; + uint64_t sect = GPT_SIZE + gpt_first_entry_sector; int fd, ret = -1; unsigned i, pmbr = 1; @@ -479,8 +480,8 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) } if ((parts[0].start != 0) && - (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); + (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; gpte[GPT_ENTRY_MAX - 1].guid = guid; @@ -500,6 +501,10 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) gpth.entry_crc32 = cpu_to_le32(gpt_crc32(gpte, GPT_ENTRY_SIZE * GPT_ENTRY_MAX)); gpth.crc32 = cpu_to_le32(gpt_crc32((char *)&gpth, GPT_HEADER_SIZE)); + if (verbose) + fprintf(stderr, "PartitionEntryLBA=%" PRIu64 ", FirstUsableLBA=%" PRIu64 ", LastUsableLBA=%" PRIu64 "\n", + gpt_first_entry_sector, gpt_first_entry_sector + GPT_SIZE, end - GPT_SIZE - 1); + if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) { fprintf(stderr, "Can't open output file '%s'\n",filename); return ret; @@ -528,7 +533,7 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) goto fail; } - lseek(fd, GPT_FIRST_ENTRY_SECTOR * DISK_SECTOR_SIZE, SEEK_SET); + lseek(fd, gpt_first_entry_sector * DISK_SECTOR_SIZE, SEEK_SET); if (write(fd, &gpte, GPT_ENTRY_SIZE * GPT_ENTRY_MAX) != GPT_ENTRY_SIZE * GPT_ENTRY_MAX) { fputs("write failed.\n", stderr); goto fail; @@ -569,7 +574,9 @@ static void usage(char *prog) { fprintf(stderr, "Usage: %s [-v] [-n] [-g] -h -s -o \n" " [-a ] [-l ] [-G ]\n" + " [-e ]\n" " [[-t | -T ] [-r] [-N ] -p [@]...] \n", prog); + exit(EXIT_FAILURE); } @@ -606,7 +613,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:vnHN:gl:rS:G:")) != -1) { + while ((ch = getopt(argc, argv, "h:s:p:a:t:T:o:vnHN:gl:rS:G:e:")) != -1) { switch (ch) { case 'o': filename = optarg; @@ -623,6 +630,15 @@ int main (int argc, char **argv) case 'H': hybrid = 1; break; + case 'e': + /* based on DISK_SECTOR_SIZE = 512 */ + gpt_first_entry_sector = 2 * to_kbytes(optarg); + if (gpt_first_entry_sector < GPT_FIRST_ENTRY_SECTOR) { + fprintf(stderr, "GPT First Entry offset must not be smaller than %d KBytes\n", + GPT_FIRST_ENTRY_SECTOR / 2); + exit(EXIT_FAILURE); + } + break; case 'h': heads = (int)strtoul(optarg, NULL, 0); break; From d524e8f68432e3e8d433ea95b3cf1449f3b3fee7 Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Mon, 30 Dec 2024 03:42:08 +0300 Subject: [PATCH 6/7] ptgen: allow image generation for a specified disk size Sometimes we know an exact size of the disk and want to create a proper disk image. This patch allows such operation. A special case of zero disk size is supported. In this case the disk size will be automatically calculated on the base of provided partitions list. Signed-off-by: Mikhail Kshevetskiy --- src/ptgen.c | 84 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 27 deletions(-) diff --git a/src/ptgen.c b/src/ptgen.c index ed9efa3..d675abd 100644 --- a/src/ptgen.c +++ b/src/ptgen.c @@ -172,7 +172,9 @@ bool use_guid_partition_table = false; struct partinfo parts[GPT_ENTRY_MAX]; char *filename = NULL; +int gpt_alternate = false; uint64_t gpt_first_entry_sector = GPT_FIRST_ENTRY_SECTOR; +uint64_t gpt_last_usable_sector = 0; /* * parse the size argument, which is either @@ -441,6 +443,12 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) } else if (kb_align != 0) { start = round_to_kb(start); } + if ((gpt_last_usable_sector > 0) && + (start + parts[i].size * 2 > gpt_last_usable_sector + 1)) { + fprintf(stderr, "Partition %d ends after last usable sector %ld\n", + i, gpt_last_usable_sector); + return ret; + } parts[i].actual_start = start; gpte[i].start = cpu_to_le64(start); @@ -488,7 +496,10 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) gpte[GPT_ENTRY_MAX - 1].guid.b[sizeof(guid_t) -1] += GPT_ENTRY_MAX; } - end = sect + GPT_SIZE; + if (gpt_last_usable_sector == 0) + gpt_last_usable_sector = sect - 1; + + end = gpt_last_usable_sector + GPT_SIZE + 1; pte[0].type = 0xEE; pte[0].start = cpu_to_le32(GPT_HEADER_SECTOR); @@ -496,14 +507,14 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) to_chs(GPT_HEADER_SECTOR, pte[0].chs_start); to_chs(end, pte[0].chs_end); - gpth.last_usable = cpu_to_le64(end - GPT_SIZE - 1); + gpth.last_usable = cpu_to_le64(gpt_last_usable_sector); gpth.alternate = cpu_to_le64(end); gpth.entry_crc32 = cpu_to_le32(gpt_crc32(gpte, GPT_ENTRY_SIZE * GPT_ENTRY_MAX)); gpth.crc32 = cpu_to_le32(gpt_crc32((char *)&gpth, GPT_HEADER_SIZE)); if (verbose) fprintf(stderr, "PartitionEntryLBA=%" PRIu64 ", FirstUsableLBA=%" PRIu64 ", LastUsableLBA=%" PRIu64 "\n", - gpt_first_entry_sector, gpt_first_entry_sector + GPT_SIZE, end - GPT_SIZE - 1); + gpt_first_entry_sector, gpt_first_entry_sector + GPT_SIZE, gpt_last_usable_sector); if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) { fprintf(stderr, "Can't open output file '%s'\n",filename); @@ -539,30 +550,30 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) goto fail; } -#ifdef WANT_ALTERNATE_PTABLE - /* The alternate partition table (We omit it by default) */ - swap(gpth.self, gpth.alternate); - gpth.first_entry = cpu_to_le64(end - GPT_ENTRY_SIZE * GPT_ENTRY_MAX / DISK_SECTOR_SIZE), - gpth.crc32 = 0; - gpth.crc32 = cpu_to_le32(gpt_crc32(&gpth, GPT_HEADER_SIZE)); - - lseek(fd, end * DISK_SECTOR_SIZE - GPT_ENTRY_SIZE * GPT_ENTRY_MAX, SEEK_SET); - if (write(fd, &gpte, GPT_ENTRY_SIZE * GPT_ENTRY_MAX) != GPT_ENTRY_SIZE * GPT_ENTRY_MAX) { - fputs("write failed.\n", stderr); - goto fail; - } + if (gpt_alternate) { + /* The alternate partition table (We omit it by default) */ + swap(gpth.self, gpth.alternate); + gpth.first_entry = cpu_to_le64(end - GPT_ENTRY_SIZE * GPT_ENTRY_MAX / DISK_SECTOR_SIZE), + gpth.crc32 = 0; + gpth.crc32 = cpu_to_le32(gpt_crc32(&gpth, GPT_HEADER_SIZE)); + + lseek(fd, end * DISK_SECTOR_SIZE - GPT_ENTRY_SIZE * GPT_ENTRY_MAX, SEEK_SET); + if (write(fd, &gpte, GPT_ENTRY_SIZE * GPT_ENTRY_MAX) != GPT_ENTRY_SIZE * GPT_ENTRY_MAX) { + fputs("write failed.\n", stderr); + goto fail; + } - lseek(fd, end * DISK_SECTOR_SIZE, SEEK_SET); - if (write(fd, &gpth, GPT_HEADER_SIZE) != GPT_HEADER_SIZE) { - fputs("write failed.\n", stderr); - goto fail; - } - lseek(fd, (end + 1) * DISK_SECTOR_SIZE -1, SEEK_SET); - if (write(fd, "\x00", 1) != 1) { - fputs("write failed.\n", stderr); - goto fail; + lseek(fd, end * DISK_SECTOR_SIZE, SEEK_SET); + if (write(fd, &gpth, GPT_HEADER_SIZE) != GPT_HEADER_SIZE) { + fputs("write failed.\n", stderr); + goto fail; + } + lseek(fd, (end + 1) * DISK_SECTOR_SIZE -1, SEEK_SET); + if (write(fd, "\x00", 1) != 1) { + fputs("write failed.\n", stderr); + goto fail; + } } -#endif ret = 0; fail: @@ -574,7 +585,7 @@ static void usage(char *prog) { fprintf(stderr, "Usage: %s [-v] [-n] [-g] -h -s -o \n" " [-a ] [-l ] [-G ]\n" - " [-e ]\n" + " [-e ] [-d ]\n" " [[-t | -T ] [-r] [-N ] -p [@]...] \n", prog); exit(EXIT_FAILURE); @@ -609,11 +620,12 @@ int main (int argc, char **argv) int part = 0; char *name = NULL; unsigned short int hybrid = 0, required = 0; + uint64_t total_sectors; uint32_t signature = 0x5452574F; /* 'OWRT' */ 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:vnHN:gl:rS:G:e:")) != -1) { + while ((ch = getopt(argc, argv, "h:s:p:a:t:T:o:vnHN:gl:rS:G:e:d:")) != -1) { switch (ch) { case 'o': filename = optarg; @@ -639,6 +651,24 @@ int main (int argc, char **argv) exit(EXIT_FAILURE); } break; + case 'd': + /* + * Zero disk_size is specially allowed. It means: find a disk size + * on the base of provided partitions list. + * + * based on DISK_SECTOR_SIZE = 512 + */ + gpt_alternate = true; + total_sectors = 2 * to_kbytes(optarg); + if (total_sectors != 0) { + if (total_sectors <= 2 * GPT_SIZE + 3) { + fprintf(stderr, "GPT disk size must be larger than %d KBytes\n", + (2 * GPT_SIZE + 3) * DISK_SECTOR_SIZE / 1024); + exit(EXIT_FAILURE); + } + gpt_last_usable_sector = total_sectors - GPT_SIZE - 2; + } + break; case 'h': heads = (int)strtoul(optarg, NULL, 0); break; From d8ed18185d3dd32c3b8c708befa0bc579b3ffc4e Mon Sep 17 00:00:00 2001 From: Mikhail Kshevetskiy Date: Mon, 30 Dec 2024 06:44:59 +0300 Subject: [PATCH 7/7] ptgen: create separate images for gpt data structure This patch creates 2 or 3 gpt images: image.start: PMBR + GPT Header image.entry: GPT Entry Table image.end: GPT Entry Table + Alternative GPT HEADER If GPT Entry table follow GPT Header without gap, then image.entry will be joined to the end of image.start file. Signed-off-by: Mikhail Kshevetskiy --- src/ptgen.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/src/ptgen.c b/src/ptgen.c index d675abd..d9fb744 100644 --- a/src/ptgen.c +++ b/src/ptgen.c @@ -172,6 +172,7 @@ bool use_guid_partition_table = false; struct partinfo parts[GPT_ENTRY_MAX]; char *filename = NULL; +int gpt_split_image = false; int gpt_alternate = false; uint64_t gpt_first_entry_sector = GPT_FIRST_ENTRY_SECTOR; uint64_t gpt_last_usable_sector = 0; @@ -422,6 +423,7 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) uint64_t sect = GPT_SIZE + gpt_first_entry_sector; int fd, ret = -1; unsigned i, pmbr = 1; + char img_name[strlen(filename) + 20]; memset(pte, 0, sizeof(struct pte) * MBR_ENTRY_MAX); memset(gpte, 0, GPT_ENTRY_SIZE * GPT_ENTRY_MAX); @@ -516,8 +518,13 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) fprintf(stderr, "PartitionEntryLBA=%" PRIu64 ", FirstUsableLBA=%" PRIu64 ", LastUsableLBA=%" PRIu64 "\n", gpt_first_entry_sector, gpt_first_entry_sector + GPT_SIZE, gpt_last_usable_sector); - if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) { - fprintf(stderr, "Can't open output file '%s'\n",filename); + if (!gpt_split_image) + strcpy(img_name, filename); + else + snprintf(img_name, sizeof(img_name), "%s.start", filename); + + if ((fd = open(img_name, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) { + fprintf(stderr, "Can't open output file '%s'\n",img_name); return ret; } @@ -544,7 +551,24 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) goto fail; } - lseek(fd, gpt_first_entry_sector * DISK_SECTOR_SIZE, SEEK_SET); + lseek(fd, 2 * DISK_SECTOR_SIZE - 1, SEEK_SET); + if (write(fd, "\x00", 1) != 1) { + fputs("write failed.\n", stderr); + goto fail; + } + + if (!gpt_split_image || (gpt_first_entry_sector == GPT_FIRST_ENTRY_SECTOR)) { + lseek(fd, gpt_first_entry_sector * DISK_SECTOR_SIZE, SEEK_SET); + } else { + close(fd); + + snprintf(img_name, sizeof(img_name), "%s.entry", filename); + if ((fd = open(img_name, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) { + fprintf(stderr, "Can't open output file '%s'\n",img_name); + return ret; + } + } + if (write(fd, &gpte, GPT_ENTRY_SIZE * GPT_ENTRY_MAX) != GPT_ENTRY_SIZE * GPT_ENTRY_MAX) { fputs("write failed.\n", stderr); goto fail; @@ -557,7 +581,19 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) gpth.crc32 = 0; gpth.crc32 = cpu_to_le32(gpt_crc32(&gpth, GPT_HEADER_SIZE)); - lseek(fd, end * DISK_SECTOR_SIZE - GPT_ENTRY_SIZE * GPT_ENTRY_MAX, SEEK_SET); + if (!gpt_split_image) { + lseek(fd, end * DISK_SECTOR_SIZE - GPT_ENTRY_SIZE * GPT_ENTRY_MAX, SEEK_SET); + } else { + close(fd); + + end = GPT_SIZE; + snprintf(img_name, sizeof(img_name), "%s.end", filename); + if ((fd = open(img_name, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) { + fprintf(stderr, "Can't open output file '%s'\n",img_name); + return ret; + } + } + if (write(fd, &gpte, GPT_ENTRY_SIZE * GPT_ENTRY_MAX) != GPT_ENTRY_SIZE * GPT_ENTRY_MAX) { fputs("write failed.\n", stderr); goto fail; @@ -583,7 +619,7 @@ static int gen_gptable(uint32_t signature, guid_t guid, unsigned nr) static void usage(char *prog) { - fprintf(stderr, "Usage: %s [-v] [-n] [-g] -h -s -o \n" + fprintf(stderr, "Usage: %s [-v] [-n] [-b] [-g] -h -s -o \n" " [-a ] [-l ] [-G ]\n" " [-e ] [-d ]\n" " [[-t | -T ] [-r] [-N ] -p [@]...] \n", prog); @@ -625,7 +661,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:vnHN:gl:rS:G:e:d:")) != -1) { + while ((ch = getopt(argc, argv, "h:s:p:a:t:T:o:vnbHN:gl:rS:G:e:d:")) != -1) { switch (ch) { case 'o': filename = optarg; @@ -669,6 +705,10 @@ int main (int argc, char **argv) gpt_last_usable_sector = total_sectors - GPT_SIZE - 2; } break; + case 'b': + gpt_alternate = true; + gpt_split_image = true; + break; case 'h': heads = (int)strtoul(optarg, NULL, 0); break;