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.
This commit is contained in:
Lennart Poettering
2023-01-17 18:06:05 +01:00
parent 1163ddb386
commit 05c4c59ff1
2 changed files with 84 additions and 0 deletions

View File

@@ -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,

View File

@@ -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);