systemd/0366-dissect-image-add-prob...

135 lines
5.7 KiB
Diff

From f86ffe711102f53b793c6529c82017b31389cbab Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Tue, 17 Jan 2023 18:06:05 +0100
Subject: [PATCH] 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.
(cherry picked from commit 05c4c59ff127668ddaa85f0a9fd67cee3c41ce00)
Related: #2170883
---
src/shared/dissect-image.c | 82 ++++++++++++++++++++++++++++++++++++++
src/shared/dissect-image.h | 2 +
2 files changed, 84 insertions(+)
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index 462ee4b3e8..5c7d26d6cf 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -61,6 +61,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"
@@ -74,6 +75,87 @@
/* how many times to wait for the device nodes to appear */
#define N_DEVICE_NODE_LIST_ATTEMPTS 10
+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, char **ret_fstype) {
/* Try to find device content type and return it in *ret_fstype. If nothing is found,
* 0/NULL will be returned. -EUCLEAN will be returned for ambiguous results, and an
diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h
index 631d4c7a04..2c7b84f15b 100644
--- a/src/shared/dissect-image.h
+++ b/src/shared/dissect-image.h
@@ -315,3 +315,5 @@ bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesi
int mount_image_privately_interactively(const char *path, DissectImageFlags flags, char **ret_directory, LoopDevice **ret_loop_device);
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 probe_sector_size(int fd, uint32_t *ret);