]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libfdisk: (gpt) add functionality to move backup header
authorKarel Zak <kzak@redhat.com>
Wed, 5 Feb 2020 13:37:41 +0000 (14:37 +0100)
committerKarel Zak <kzak@redhat.com>
Wed, 5 Feb 2020 13:37:41 +0000 (14:37 +0100)
- add fdisk_gpt_disable_relocation() to disable move backup header to standard location

- add fdisk_gpt_enable_minimize() to move backup header behind last partition

Note that fdisk_gpt_disable_relocation() has to be used before fdisk_assign_device(),
because automatic relocation is done when libfdisk reads the header from the device.

For example:
lb = fdisk_get_label(cxt, "gpt");

fdisk_gpt_disable_relocation(lb, 1);
fdisk_assign_device(cxt, devname, 0);

For fdisk_gpt_enable_minimize() it's not important as recalculation is
done before write, for example:

fdisk_assign_device(cxt, devname, 0);
fdisk_gpt_enable_minimize(fdisk_get_label(cxt, NULL), 1);

... add partition, etc ...

fdisk_write_disklabel(cxt);

Signed-off-by: Karel Zak <kzak@redhat.com>
libfdisk/docs/libfdisk-sections.txt
libfdisk/src/gpt.c
libfdisk/src/libfdisk.h.in
libfdisk/src/libfdisk.sym

index 6675c1100e22aef994452899038e49f6d81ce28c..9a620395edec6f063ef9f9c9971cb44db33dcd62 100644 (file)
@@ -233,6 +233,8 @@ fdisk_gpt_is_hybrid
 fdisk_gpt_get_partition_attrs
 fdisk_gpt_set_partition_attrs
 fdisk_gpt_set_npartitions
+fdisk_gpt_disable_relocation
+fdisk_gpt_enable_minimize
 GPT_FLAG_REQUIRED
 GPT_FLAG_NOBLOCK
 GPT_FLAG_LEGACYBOOT
index 973a2612e7378b155b4d515643a436d15cf1af7d..c453ae9f011d594874fb1752ae77d9a1c0183d93 100644 (file)
@@ -172,6 +172,9 @@ struct fdisk_gpt_label {
        struct gpt_header       *bheader;       /* backup header */
 
        unsigned char *ents;                    /* entries (partitions) */
+
+       unsigned int no_relocate :1,            /* do not fix backup location */
+                    minimize :1;
 };
 
 static void gpt_deinit(struct fdisk_label *lb);
@@ -492,7 +495,7 @@ static int gpt_mknew_pmbr(struct fdisk_context *cxt)
 static void gpt_fix_alternative_lba(struct fdisk_context *cxt, struct fdisk_gpt_label *gpt)
 {
        struct gpt_header *p, *b;
-       uint64_t x = 0;
+       uint64_t x = 0, orig;
        size_t nents;
 
        if (!cxt)
@@ -502,6 +505,7 @@ static void gpt_fix_alternative_lba(struct fdisk_context *cxt, struct fdisk_gpt_
        b = gpt->bheader;       /* backup */
 
        nents = le32_to_cpu(p->npartition_entries);
+       orig = le64_to_cpu(p->alternative_lba);
 
        /* reference from primary to backup */
        p->alternative_lba = cpu_to_le64(cxt->total_sectors - 1ULL);
@@ -519,7 +523,64 @@ static void gpt_fix_alternative_lba(struct fdisk_context *cxt, struct fdisk_gpt_
        p->last_usable_lba  = cpu_to_le64(x);
        b->last_usable_lba  = cpu_to_le64(x);
 
-       DBG(GPT, ul_debug("Alternative-LBA updated to: %"PRIu64, le64_to_cpu(p->alternative_lba)));
+       DBG(GPT, ul_debug("Alternative-LBA updated from %"PRIu64" to %"PRIu64,
+                               orig, le64_to_cpu(p->alternative_lba)));
+}
+
+static uint64_t gpt_calculate_minimal_size(struct fdisk_context *cxt, struct fdisk_gpt_label *gpt)
+{
+       size_t i;
+       uint64_t x = 0, total = 0;
+       struct gpt_header *hdr;
+
+       assert(cxt);
+       assert(gpt);
+       assert(gpt->pheader);
+       assert(gpt->ents);
+
+       hdr = gpt->pheader;
+
+       /* LBA behind the last partition */
+       for (i = 0; i < gpt_get_nentries(gpt); i++) {
+               struct gpt_entry *e = gpt_get_entry(gpt, i);
+
+               if (gpt_entry_is_used(e)) {
+                       uint64_t end = gpt_partition_end(e);
+                       if (end > x)
+                               x = end;
+               }
+       }
+       total = x + 1;
+
+       /* the current last LBA usable for partitions */
+       gpt_calculate_last_lba(hdr, le32_to_cpu(hdr->npartition_entries), &x, cxt);
+
+       /* size of all stuff at the end of the device */
+       total += cxt->total_sectors - x;
+
+       DBG(GPT, ul_debug("minimal device is %"PRIu64, total));
+       return total;
+}
+
+static int gpt_possible_minimize(struct fdisk_context *cxt, struct fdisk_gpt_label *gpt)
+{
+       struct gpt_header *hdr = gpt->pheader;
+       uint64_t total = gpt_calculate_minimal_size(cxt, gpt);
+
+       return le64_to_cpu(hdr->alternative_lba) > (total - 1ULL);
+}
+
+/* move backup header behind the last partition */
+static void gpt_minimize_alternative_lba(struct fdisk_context *cxt, struct fdisk_gpt_label *gpt)
+{
+       uint64_t total = gpt_calculate_minimal_size(cxt, gpt);
+       uint64_t orig = cxt->total_sectors;
+
+       /* Let's temporary change size of the device to recalculate backup header */
+       cxt->total_sectors = total;
+       gpt_fix_alternative_lba(cxt, gpt);
+       cxt->total_sectors = orig;
+       fdisk_label_set_changed(cxt->label, 1);
 }
 
 /* some universal differences between the headers */
@@ -1546,17 +1607,27 @@ static int gpt_probe_label(struct fdisk_context *cxt)
 
        /* The headers make be correct, but Backup do not have to be on the end
         * of the device (due to device resize, etc.). Let's fix this issue. */
-       if (le64_to_cpu(gpt->pheader->alternative_lba) > cxt->total_sectors ||
-           le64_to_cpu(gpt->pheader->alternative_lba) < cxt->total_sectors - 1ULL) {
-               fdisk_warnx(cxt, _("The backup GPT table is not on the end of the device. "
-                                  "This problem will be corrected by write."));
+       if (gpt->minimize == 0 &&
+           (le64_to_cpu(gpt->pheader->alternative_lba) > cxt->total_sectors ||
+            le64_to_cpu(gpt->pheader->alternative_lba) < cxt->total_sectors - 1ULL)) {
 
-               gpt_fix_alternative_lba(cxt, gpt);
-               gpt_recompute_crc(gpt->bheader, gpt->ents);
-               gpt_recompute_crc(gpt->pheader, gpt->ents);
-               fdisk_label_set_changed(cxt->label, 1);
+               if (gpt->no_relocate || fdisk_is_readonly(cxt))
+                       fdisk_warnx(cxt, _("The backup GPT table is not on the end of the device."));
+
+               else {
+                       fdisk_warnx(cxt, _("The backup GPT table is not on the end of the device. "
+                                          "This problem will be corrected by write."));
+
+                       gpt_fix_alternative_lba(cxt, gpt);
+                       gpt_recompute_crc(gpt->bheader, gpt->ents);
+                       gpt_recompute_crc(gpt->pheader, gpt->ents);
+                       fdisk_label_set_changed(cxt->label, 1);
+               }
        }
 
+       if (gpt->minimize && gpt_possible_minimize(cxt, gpt))
+               fdisk_label_set_changed(cxt->label, 1);
+
        cxt->label->nparts_max = gpt_get_nentries(gpt);
        cxt->label->nparts_cur = partitions_in_use(gpt);
        return 1;
@@ -2012,6 +2083,9 @@ static int gpt_write_disklabel(struct fdisk_context *cxt)
        if (check_overlap_partitions(gpt))
                goto err0;
 
+       if (gpt->minimize)
+               gpt_minimize_alternative_lba(cxt, gpt);
+
        /* recompute CRCs for both headers */
        gpt_recompute_crc(gpt->pheader, gpt->ents);
        gpt_recompute_crc(gpt->bheader, gpt->ents);
@@ -3096,6 +3170,44 @@ struct fdisk_label *fdisk_new_gpt_label(struct fdisk_context *cxt __attribute__
        return lb;
 }
 
+/**
+ * fdisk_gpt_disable_relocation
+ * @ld: label
+ * @disable: 0 or 1
+ *
+ * Disable automatic backup header relocation to the end of the device. The
+ * header possition is recalculated during libfdisk probing stage by
+ * fdisk_assign_device() and later written by fdisk_write_disklabel(), so you
+ * need to call it before fdisk_assign_device().
+ *
+ * Since: 2.36
+ */
+void fdisk_gpt_disable_relocation(struct fdisk_label *lb, int disable)
+{
+       struct fdisk_gpt_label *gpt = (struct fdisk_gpt_label *) lb;
+
+       assert(gpt);
+       gpt->no_relocate = disable ? 1 : 0;
+}
+
+/**
+ * fdisk_gpt_enable_minimize
+ * @ld: label
+ * @disable: 0 or 1
+ *
+ * Force libfdisk to write backup header to behind last partition. The
+ * header possition is recalculated on fdisk_write_disklabel().
+ *
+ * Since: 2.36
+ */
+void fdisk_gpt_enable_minimize(struct fdisk_label *lb, int enable)
+{
+       struct fdisk_gpt_label *gpt = (struct fdisk_gpt_label *) lb;
+
+       assert(gpt);
+       gpt->minimize = enable ? 1 : 0;
+}
+
 #ifdef TEST_PROGRAM
 static int test_getattr(struct fdisk_test *ts, int argc, char *argv[])
 {
index 2ba34dc0ac64503f8eec02408e9433a57d024090..87006ad98fe441a4fca0f7785bd25d9340157387 100644 (file)
@@ -728,6 +728,9 @@ extern int fdisk_gpt_set_npartitions(struct fdisk_context *cxt, uint32_t entries
 extern int fdisk_gpt_get_partition_attrs(struct fdisk_context *cxt, size_t partnum, uint64_t *attrs);
 extern int fdisk_gpt_set_partition_attrs(struct fdisk_context *cxt, size_t partnum, uint64_t attrs);
 
+extern void fdisk_gpt_disable_relocation(struct fdisk_label *lb, int disable);
+extern void fdisk_gpt_enable_minimize(struct fdisk_label *lb, int enable);
+
 /**
  * fdisk_labelitem_gpt:
  * @GPT_LABELITEM_ID: GPT disklabel UUID (!= partition UUID)
index eee2d6bda57b920e4c0cef4ccd45c825642ccddd..f37d18e1aca91c12d20c3e7de43653060344d0ec 100644 (file)
@@ -310,4 +310,6 @@ FDISK_2.35 {
 } FDISK_2.33;
 FDISK_2.36 {
        fdisk_set_disklabel_id_from_string;
+       fdisk_gpt_disable_relocation;
+       fdisk_gpt_enable_minimize;
 } FDISK_2.35;