We read GPT headers from the device and store it in epd->gpt. When
writing a partition table we assume that epd->gpt is read from
the primary GPT header: We only adjust the partition_entry_lba for
the alternate header. This assumption is wrong, in case the primary
GPT header is corrupted epd->gpt could also come from the alternate
GPT header. When this happens we have to set partition_entry_lba
correctly for the primary GPT header as well.

Unfortunately this is not trivial. While we could just use LBA2 for
the partition entries, this might overwrite a bootloader stored there (see
[1]). Instead the following logic is applied:

When the primary GPT header was good when reading it, keep the partition
table entries where they are. If not, take the first usable LBA and
substract the size we need for the partition entries.

[1] https://lore.barebox.org/[email protected]

Signed-off-by: Sascha Hauer <[email protected]>
---
 common/partitions/efi.c | 31 ++++++++++++++++++++++++++++++-
 1 file changed, 30 insertions(+), 1 deletion(-)

diff --git a/common/partitions/efi.c b/common/partitions/efi.c
index 
1766f0123353d502065673d02e983e6cdd51b9af..0fd60c9fd0442c3c571c685a5b0b41b8f33db7bd
 100644
--- a/common/partitions/efi.c
+++ b/common/partitions/efi.c
@@ -762,7 +762,36 @@ static int __efi_partition_write(struct efi_partition_desc 
*epd, bool primary)
 
        if (primary) {
                my_lba = 1;
-               partition_entry_lba = le64_to_cpu(gpt->partition_entry_lba);
+
+               if (epd->good_pgpt) {
+                       /*
+                        * The primary GPT was good, we can just leave the 
partition
+                        * entries where they are.
+                        */
+                       partition_entry_lba = 
le64_to_cpu(gpt->partition_entry_lba);
+               } else {
+                       /*
+                        * The primary GPT was bad. When this is not a new 
partition
+                        * table, but instead read from the disk, the header is 
from the
+                        * alternate GPT, meaning the partition_entry_lba also 
points
+                        * to the alternate partitions. We have to find a place 
for the
+                        * primary partition entries in this case. We could 
store them
+                        * right after the GPT header, but this might destroy a 
bootloader
+                        * stored there. Instead, substract the size we need 
from the
+                        * first usable LBA.
+                        */
+                       uint64_t first_usable_lba;
+
+                       first_usable_lba = le64_to_cpu(gpt->first_usable_lba);
+
+                       if (first_usable_lba < 32 + 1 + 1) {
+                               pr_err("First usable LBA is %llu which doesn't 
leave "
+                                      "enough space for partition entries\n",
+                                       first_usable_lba);
+                               return -EINVAL;
+                       }
+                       partition_entry_lba = first_usable_lba - 32;
+               }
                gpt->alternate_lba = cpu_to_le64(last_lba(blk));
        } else {
                my_lba = last_lba(blk);

-- 
2.47.3


Reply via email to