ea71a49292
Resolves: #2161260,#2170883,#2178222,#2190226,#2209912,#2211065,#2213521,#2226980,#2230364,#2231845
135 lines
5.7 KiB
Diff
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);
|