From 387ac2771f1b16af00c417b226c697d08d1301fe Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Wed, 5 Feb 2020 14:37:41 +0100 Subject: [PATCH] libfdisk: (gpt) add functionality to move backup header - 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 --- libfdisk/docs/libfdisk-sections.txt | 2 + libfdisk/src/gpt.c | 132 +++++++++++++++++++++++++--- libfdisk/src/libfdisk.h.in | 3 + libfdisk/src/libfdisk.sym | 2 + 4 files changed, 129 insertions(+), 10 deletions(-) diff --git a/libfdisk/docs/libfdisk-sections.txt b/libfdisk/docs/libfdisk-sections.txt index 6675c1100e..9a620395ed 100644 --- a/libfdisk/docs/libfdisk-sections.txt +++ b/libfdisk/docs/libfdisk-sections.txt @@ -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 diff --git a/libfdisk/src/gpt.c b/libfdisk/src/gpt.c index 973a2612e7..c453ae9f01 100644 --- a/libfdisk/src/gpt.c +++ b/libfdisk/src/gpt.c @@ -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[]) { diff --git a/libfdisk/src/libfdisk.h.in b/libfdisk/src/libfdisk.h.in index 2ba34dc0ac..87006ad98f 100644 --- a/libfdisk/src/libfdisk.h.in +++ b/libfdisk/src/libfdisk.h.in @@ -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) diff --git a/libfdisk/src/libfdisk.sym b/libfdisk/src/libfdisk.sym index eee2d6bda5..f37d18e1ac 100644 --- a/libfdisk/src/libfdisk.sym +++ b/libfdisk/src/libfdisk.sym @@ -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; -- 2.39.2