From b11e1b7ea12f220d1c9be085d56dbd5ccfc3bce0 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Wed, 30 Sep 2020 12:38:59 +0200 Subject: [PATCH] libfdisk: (gpt) reduce number of entries to fit small device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default is 128 partitions (entries in partitions array). The size of the entry is 128 bytes, it means 32 sectors (512-byte) for the array. The default is too large for tiny devices and it seems better to dynamically reduce the number to fix the device size. The reduction is reported to user. Example: $ dd if=/dev/zero of=8-sectors.img count=8 bs=512 $ fdisk 8-sectors.img ... Command (m for help): g Created a new GPT disklabel (GUID: 3DB3EECE-BCCA-6C46-95FA-7E23783BB3B2). The maximal number of partitions is 8 (default is 128). Command (m for help): x Expert command (m for help): p Disk 8-sectors.img: 4 KiB, 4096 bytes, 8 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: gpt Disk identifier: 3DB3EECE-BCCA-6C46-95FA-7E23783BB3B2 First LBA: 4 Last LBA: 4 Alternative LBA: 7 Partition entries LBA: 2 Allocated partition entries: 8 In this case, sectors: 0 - PMBR 1 - GPT header 2-3 - GPT array of partitions (8 entries, 128 bytes each) 4 - partition data 5-6 - backup GPT array of partitions 7 - backup GPT header The smallest image is 6 sectors with 4 entries in array of partitions. Note that Linux kernel has no problem to accept 4K image with 1 512-bytes GPT partition. # losetup -P -f 4K.img # lsblk /dev/loop0 NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT loop0 7:0 0 4K 0 loop └─loop0p1 259:5 0 512B 0 part ... but we need to fix libblkid, it ignores so small devices to probe for GPT :) Addresses: https://github.com/karelzak/util-linux/issues/1147 Signed-off-by: Karel Zak --- libfdisk/src/gpt.c | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/libfdisk/src/gpt.c b/libfdisk/src/gpt.c index 563c77073..4b0e26389 100644 --- a/libfdisk/src/gpt.c +++ b/libfdisk/src/gpt.c @@ -742,10 +742,12 @@ static int get_script_u64(struct fdisk_context *cxt, uint64_t *num, const char * } static int count_first_last_lba(struct fdisk_context *cxt, - uint64_t *first, uint64_t *last) + uint64_t *first, uint64_t *last, + uint32_t *maxents) { int rc = 0; uint64_t flba = 0, llba = 0; + uint32_t nents; assert(cxt); assert(first); @@ -753,12 +755,24 @@ static int count_first_last_lba(struct fdisk_context *cxt, *first = *last = 0; - /* UEFI default */ - rc = gpt_calculate_last_lba(NULL, GPT_NPARTITIONS, &llba, cxt); - if (rc == 0) - gpt_calculate_first_lba(NULL, GPT_NPARTITIONS, &flba, cxt); + /* The default is GPT_NPARTITIONS, if the device is not large enough + * than reduce this number of partitions and try to recalculate it + * again, until we get something useful or return error. + */ + for (nents = GPT_NPARTITIONS; nents > 0; nents--) { + rc = gpt_calculate_last_lba(NULL, nents, &llba, cxt); + if (rc == 0) + rc = gpt_calculate_first_lba(NULL, nents, &flba, cxt); + if (llba < flba) + rc = -ENOSPC; + else if (rc == 0) + break; + } + if (rc) return rc; + if (maxents) + *maxents = nents; /* script default */ if (cxt->script) { @@ -809,6 +823,7 @@ static int gpt_mknew_header(struct fdisk_context *cxt, struct gpt_header *header, uint64_t lba) { uint64_t first, last; + uint32_t nents = 0; int has_id = 0, rc; if (!cxt || !header) @@ -825,17 +840,15 @@ static int gpt_mknew_header(struct fdisk_context *cxt, header->size = cpu_to_le32(sizeof(struct gpt_header) - sizeof(header->reserved2)); - /* - * 128 partitions are the default. It can go beyond that, but - * we're creating a de facto header here, so no funny business. - */ - header->npartition_entries = cpu_to_le32(GPT_NPARTITIONS); - header->sizeof_partition_entry = cpu_to_le32(sizeof(struct gpt_entry)); - - rc = count_first_last_lba(cxt, &first, &last); + /* Set {First,Last}LBA and number of the partitions + * (default is GPT_NPARTITIONS) */ + rc = count_first_last_lba(cxt, &first, &last, &nents); if (rc) return rc; + header->npartition_entries = cpu_to_le32(nents); + header->sizeof_partition_entry = cpu_to_le32(sizeof(struct gpt_entry)); + header->first_usable_lba = cpu_to_le64(first); header->last_usable_lba = cpu_to_le64(last); @@ -2637,6 +2650,10 @@ static int gpt_create_disklabel(struct fdisk_context *cxt) guid_to_string(&guid, str); fdisk_label_set_changed(cxt->label, 1); fdisk_info(cxt, _("Created a new GPT disklabel (GUID: %s)."), str); + + if (gpt_get_nentries(gpt) < GPT_NPARTITIONS) + fdisk_info(cxt, _("The maximal number of partitions is %d (default is %d)."), + gpt_get_nentries(gpt), GPT_NPARTITIONS); done: return rc; } @@ -3089,7 +3106,7 @@ static int gpt_reset_alignment(struct fdisk_context *cxt) /* estimate ranges for GPT */ uint64_t first, last; - count_first_last_lba(cxt, &first, &last); + count_first_last_lba(cxt, &first, &last, NULL); if (cxt->first_lba < first) cxt->first_lba = first; if (cxt->last_lba > last)