From: Lennart Poettering Date: Tue, 17 Jan 2023 17:06:05 +0000 (+0100) Subject: dissect-image: add probe_sector_size() helper for detecting sector size of a GPT... X-Git-Tag: v253-rc1~81^2~4 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=05c4c59ff127668ddaa85f0a9fd67cee3c41ce00;p=thirdparty%2Fsystemd.git dissect-image: add probe_sector_size() helper for detecting sector size of a GPT disk image When we operate with DDIs with sector sizes != 512 we need to configure the loopback device to match it, otherwise the image and the kernel block device will disagree what things are. Let's add a prober that tries to determine the sector size of a GPT DDI. It does this by looking for the GPT partition table header at the various byte offsets they must be located on, given a specific sector size. It will try sector size 512, 1024, 2048 and 4096. Of these only the 512 and 4096 really make sense IRL I guess, but let's be thorough. --- diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 45a9e9413cb..d78568d3440 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -62,6 +62,7 @@ #include "raw-clone.h" #include "resize-fs.h" #include "signal-util.h" +#include "sparse-endian.h" #include "stat-util.h" #include "stdio-util.h" #include "string-table.h" @@ -107,6 +108,87 @@ int dissect_fstype_ok(const char *fstype) { return false; } +int probe_sector_size(int fd, uint32_t *ret) { + + struct gpt_header { + char signature[8]; + le32_t revision; + le32_t header_size; + le32_t crc32; + le32_t reserved; + le64_t my_lba; + le64_t alternate_lba; + le64_t first_usable_lba; + le64_t last_usable_lba; + sd_id128_t disk_guid; + le64_t partition_entry_lba; + le32_t number_of_partition_entries; + le32_t size_of_partition_entry; + le32_t partition_entry_array_crc32; + } _packed_; + + /* Disk images might be for 512B or for 4096 sector sizes, let's try to auto-detect that by searching + * for the GPT headers at the relevant byte offsets */ + + assert_cc(sizeof(struct gpt_header) == 92); + + /* We expect a sector size in the range 512…4096. The GPT header is located in the second + * sector. Hence it could be at byte 512 at the earliest, and at byte 4096 at the latest. And we must + * read with granularity of the largest sector size we care about. Which means 8K. */ + uint8_t sectors[2 * 4096]; + uint32_t found = 0; + ssize_t n; + + assert(fd >= 0); + assert(ret); + + n = pread(fd, sectors, sizeof(sectors), 0); + if (n < 0) + return -errno; + if (n != sizeof(sectors)) /* too short? */ + goto not_found; + + /* Let's see if we find the GPT partition header with various expected sector sizes */ + for (uint32_t sz = 512; sz <= 4096; sz <<= 1) { + struct gpt_header *p; + + assert(sizeof(sectors) >= sz * 2); + p = (struct gpt_header*) (sectors + sz); + + if (memcmp(p->signature, (const char[8]) { 'E', 'F', 'I', ' ', 'P', 'A', 'R', 'T' }, 8) != 0) + continue; + + if (le32toh(p->revision) != UINT32_C(0x00010000)) /* the only known revision of the spec: 1.0 */ + continue; + + if (le32toh(p->header_size) < sizeof(struct gpt_header)) + continue; + + if (le32toh(p->header_size) > 4096) /* larger than a sector? something is off… */ + continue; + + if (le64toh(p->my_lba) != 1) /* this sector must claim to be at sector offset 1 */ + continue; + + if (found != 0) + return log_debug_errno(SYNTHETIC_ERRNO(ENOTUNIQ), + "Detected valid partition table at offsets matching multiple sector sizes, refusing."); + + found = sz; + } + + if (found != 0) { + log_debug("Determined sector size %" PRIu32 " based on discovered partition table.", found); + *ret = found; + return 1; /* indicate we *did* find it */ + } + +not_found: + log_debug("Couldn't find any partition table to derive sector size of."); + *ret = 512; /* pick the traditional default */ + return 0; /* indicate we didn't find it */ +} + int probe_filesystem_full( int fd, const char *path, diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index e9ab1f89a36..638524f7161 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -186,3 +186,5 @@ int mount_image_privately_interactively(const char *path, DissectImageFlags flag int verity_dissect_and_mount(int src_fd, const char *src, const char *dest, const MountOptions *options, const char *required_host_os_release_id, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level, const char *required_sysext_scope); int dissect_fstype_ok(const char *fstype); + +int probe_sector_size(int fd, uint32_t *ret);