• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 22045760807

16 Feb 2026 12:10AM UTC coverage: 72.384% (-0.3%) from 72.694%
22045760807

push

github

web-flow
boot: fix buffer alignment when doing block I/O (#40465)

UEFI Block I/O Protocol has `Media->IoAlign` field dictating the minimum
alignment for I/O buffer. It's quite surprising this has been lingering
here unnoticed for years, seems like most UEFI implementations have
small or no alignment requirements. U-Boot is not the case here, and
requires at least 512 byte alignment, hence attempt to read GPT
partition table fail and in effect systemd-boot can not find XBOOTLDR
partition.

These patches allow to boot from XBOOTLDR partition on U-Boot - tested
with latest systemd revision and U-Boot master
(`8de6e8f8a`) on x64 and ARM32, of which
both are failing without the patch.

Also fixes Bitlocker probing logic, which is the only other place where
raw block I/O is used, however this is untested.

311273 of 430029 relevant lines covered (72.38%)

1216197.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.21
/src/shared/dissect-image.h
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
#pragma once
3

4
#include "sd-id128.h"
5

6
#include "architecture.h"
7
#include "gpt.h"
8
#include "iovec-util.h"
9
#include "shared-forward.h"
10

11
typedef struct DecryptedImage DecryptedImage;
12

13
typedef struct DissectedPartition {
14
        bool found:1;
15
        bool ignored:1;
16
        bool rw:1;
17
        bool growfs:1;
18
        int partno;                 /* -1 if there was no partition and the images contains a file system directly */
19
        Architecture architecture;  /* Intended architecture: either native, secondary or unset ARCHITECTURE_INVALID. */
20
        sd_id128_t uuid;            /* Partition entry UUID as reported by the GPT */
21
        char *fstype;
22
        char *node;
23
        char *label;
24
        char *decrypted_node;
25
        char *decrypted_fstype;
26
        char *mount_options;
27
        int mount_node_fd;
28
        uint64_t size;
29
        uint64_t offset;
30
        uint64_t gpt_flags;
31
        int fsmount_fd;
32
} DissectedPartition;
33

34
#define DISSECTED_PARTITION_NULL                                        \
35
        ((DissectedPartition) {                                         \
36
                .partno = -1,                                           \
37
                .architecture = _ARCHITECTURE_INVALID,                  \
38
                .mount_node_fd = -EBADF,                                \
39
                .fsmount_fd = -EBADF,                                   \
40
        })
41
#define TAKE_PARTITION(p)                                       \
42
        ({                                                      \
43
                DissectedPartition *_pp = &(p), _p = *_pp;      \
44
                *_pp = DISSECTED_PARTITION_NULL;                \
45
                _p;                                             \
46
        })
47

48
typedef enum DissectImageFlags {
49
        DISSECT_IMAGE_DEVICE_READ_ONLY          = 1 << 0,  /* Make device read-only */
50
        DISSECT_IMAGE_DISCARD_ON_LOOP           = 1 << 1,  /* Turn on "discard" if on a loop device and file system supports it */
51
        DISSECT_IMAGE_DISCARD                   = 1 << 2,  /* Turn on "discard" if file system supports it, on all block devices */
52
        DISSECT_IMAGE_DISCARD_ON_CRYPTO         = 1 << 3,  /* Turn on "discard" also on crypto devices */
53
        DISSECT_IMAGE_DISCARD_ANY               = DISSECT_IMAGE_DISCARD_ON_LOOP |
54
                                                  DISSECT_IMAGE_DISCARD |
55
                                                  DISSECT_IMAGE_DISCARD_ON_CRYPTO,
56
        DISSECT_IMAGE_GPT_ONLY                  = 1 << 4,  /* Only recognize images with GPT partition tables */
57
        DISSECT_IMAGE_GENERIC_ROOT              = 1 << 5,  /* If no partition table or only single generic partition, assume it's the root fs */
58
        DISSECT_IMAGE_MOUNT_ROOT_ONLY           = 1 << 6,  /* Mount only the root and /usr partitions */
59
        DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY       = 1 << 7,  /* Mount only the non-root and non-/usr partitions */
60
        DISSECT_IMAGE_VALIDATE_OS               = 1 << 8,  /* Refuse mounting images that aren't identifiable as OS images */
61
        DISSECT_IMAGE_VALIDATE_OS_EXT           = 1 << 9,  /* Refuse mounting images that aren't identifiable as OS extension images */
62
        DISSECT_IMAGE_RELAX_VAR_CHECK           = 1 << 10, /* Don't insist that the UUID of /var is hashed from /etc/machine-id */
63
        DISSECT_IMAGE_FSCK                      = 1 << 11, /* File system check the partition before mounting (no effect when combined with DISSECT_IMAGE_READ_ONLY) */
64
        DISSECT_IMAGE_NO_PARTITION_TABLE        = 1 << 12, /* Only recognize single file system images */
65
        DISSECT_IMAGE_VERITY_SHARE              = 1 << 13, /* When activating a verity device, reuse existing one if already open */
66
        DISSECT_IMAGE_MKDIR                     = 1 << 14, /* Make top-level directory to mount right before mounting, if missing */
67
        DISSECT_IMAGE_USR_NO_ROOT               = 1 << 15, /* If no root fs is in the image, but /usr is, then allow this (so that we can mount the rootfs as tmpfs or so */
68
        DISSECT_IMAGE_REQUIRE_ROOT              = 1 << 16, /* Don't accept disks without root partition (or at least /usr partition if DISSECT_IMAGE_USR_NO_ROOT is set) */
69
        DISSECT_IMAGE_MOUNT_READ_ONLY           = 1 << 17, /* Make mounts read-only */
70
        DISSECT_IMAGE_READ_ONLY                 = DISSECT_IMAGE_DEVICE_READ_ONLY |
71
                                                  DISSECT_IMAGE_MOUNT_READ_ONLY,
72
        DISSECT_IMAGE_GROWFS                    = 1 << 18, /* Grow file systems in partitions marked for that to the size of the partitions after mount */
73
        DISSECT_IMAGE_MOUNT_IDMAPPED            = 1 << 19, /* Mount mounts with kernel 5.12-style userns ID mapping, if file system type doesn't support uid=/gid= */
74
        DISSECT_IMAGE_ADD_PARTITION_DEVICES     = 1 << 20, /* Create partition devices via BLKPG_ADD_PARTITION */
75
        DISSECT_IMAGE_PIN_PARTITION_DEVICES     = 1 << 21, /* Open dissected partitions and decrypted partitions and pin them by fd */
76
        DISSECT_IMAGE_RELAX_EXTENSION_CHECK     = 1 << 22, /* Don't insist that the extension-release file name matches the image name */
77
        DISSECT_IMAGE_DISKSEQ_DEVNODE           = 1 << 23, /* Prefer /dev/disk/by-diskseq/… device nodes */
78
        DISSECT_IMAGE_ALLOW_EMPTY               = 1 << 24, /* Allow that no usable partitions is present */
79
        DISSECT_IMAGE_TRY_ATOMIC_MOUNT_EXCHANGE = 1 << 25, /* Try to mount the image beneath the specified mountpoint, rather than on top of it, and then umount the top */
80
        DISSECT_IMAGE_ALLOW_USERSPACE_VERITY    = 1 << 26, /* Allow userspace verity keyring in /etc/verity.d/ and related dirs */
81
        DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH    = 1 << 27, /* Allow interactive authorization when going through mountfsd */
82
        DISSECT_IMAGE_FOREIGN_UID               = 1 << 28, /* Request a foreign UID range mapping */
83
        DISSECT_IMAGE_IDENTITY_UID              = 1 << 29, /* Explicitly request an identity UID range mapping */
84
} DissectImageFlags;
85

86
typedef struct DissectedImage {
87
        bool encrypted:1;
88
        bool has_verity:1;         /* verity available in image, but not necessarily used */
89
        bool has_verity_sig:1;     /* pkcs#7 signature embedded in image */
90
        bool verity_ready:1;       /* verity available, fully specified and usable */
91
        bool verity_sig_ready:1;   /* verity signature logic, fully specified and usable */
92
        bool single_file_system:1; /* MBR/GPT or single file system */
93

94
        LoopDevice *loop;
95
        DissectedPartition partitions[_PARTITION_DESIGNATOR_MAX];
96
        DecryptedImage *decrypted_image;
97

98
        uint32_t sector_size;
99
        uint64_t image_size;
100

101
        char *image_name;
102
        sd_id128_t image_uuid;
103

104
        /* Meta information extracted from /etc/os-release and similar */
105
        char *hostname;
106
        sd_id128_t machine_id;
107
        char **machine_info;
108
        char **os_release;
109
        char **initrd_release;
110
        char **confext_release;
111
        char **sysext_release;
112
        int has_init_system;
113
} DissectedImage;
114

115
typedef struct MountOptions {
116
        char *options[_PARTITION_DESIGNATOR_MAX];
117
} MountOptions;
118

119
typedef struct VeritySettings {
120
        /* Binary root hash for the Verity Merkle tree */
121
        struct iovec root_hash;
122

123
        /* PKCS#7 signature of the above */
124
        struct iovec root_hash_sig;
125

126
        /* Path to the verity data file, if stored externally */
127
        char *data_path;
128

129
        /* PARTITION_ROOT or PARTITION_USR, depending on what these Verity settings are for */
130
        PartitionDesignator designator;
131
} VeritySettings;
132

133
#define VERITY_SETTINGS_DEFAULT (VeritySettings) {              \
134
                .designator = _PARTITION_DESIGNATOR_INVALID     \
135
        }
136

137
typedef struct ImageFilter {
138
        /* A per designator glob matching against the partition label */
139
        char *pattern[_PARTITION_DESIGNATOR_MAX];
140
} ImageFilter;
141

142
typedef struct ExtensionReleaseData {
143
        char *os_release_id;
144
        char *os_release_id_like;
145
        char *os_release_version_id;
146
        char *os_release_sysext_level;
147
        char *os_release_confext_level;
148
        char *os_release_extension_scope;
149
} ExtensionReleaseData;
150

151
MountOptions* mount_options_free_all(MountOptions *options);
152
DEFINE_TRIVIAL_CLEANUP_FUNC(MountOptions*, mount_options_free_all);
467✔
153
const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator);
154
int mount_options_set_and_consume(MountOptions **options, PartitionDesignator d, char *s);
155
int mount_options_dup(const MountOptions *source, MountOptions **ret);
156
int mount_options_to_string(const MountOptions *mount_options, char **ret);
157

158
int probe_filesystem_full(int fd, const char *path, uint64_t offset, uint64_t size, bool restrict_fstypes, char **ret_fstype);
159
static inline int probe_filesystem(const char *path, char **ret_fstype) {
×
160
        return probe_filesystem_full(-1, path, 0, UINT64_MAX, /* bool restrict_fstypes= */ false, ret_fstype);
×
161
}
162

163
int dissect_log_error(int log_level, int r, const char *name, const VeritySettings *verity);
164
int dissect_image_file(const char *path, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, const ImageFilter *filter, DissectImageFlags flags, DissectedImage **ret);
165
int dissect_image_file_and_warn(const char *path, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, const ImageFilter *filter, DissectImageFlags flags, DissectedImage **ret);
166
int dissect_loop_device(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, const ImageFilter *image_filter, DissectImageFlags flags, DissectedImage **ret);
167
int dissect_loop_device_and_warn(LoopDevice *loop, const VeritySettings *verity, const MountOptions *mount_options, const ImagePolicy *image_policy, const ImageFilter *image_filter, DissectImageFlags flags, DissectedImage **ret);
168
int dissected_image_new_from_existing_verity(const char *src, const VeritySettings *verity, const MountOptions *options, const ImagePolicy *image_policy, const ImageFilter *image_filter, RuntimeScope runtime_scope, DissectImageFlags dissect_image_flags, DissectedImage **ret);
169

170
void dissected_image_close(DissectedImage *m);
171
DissectedImage* dissected_image_unref(DissectedImage *m);
172
DEFINE_TRIVIAL_CLEANUP_FUNC(DissectedImage*, dissected_image_unref);
9,643✔
173

174
int dissected_image_decrypt(DissectedImage *m, const char *root, const char *passphrase, const VeritySettings *verity, const ImagePolicy *image_policy, DissectImageFlags flags);
175
int dissected_image_decrypt_interactively(DissectedImage *m, const char *passphrase, const VeritySettings *verity, const ImagePolicy *image_policy, DissectImageFlags flags);
176
int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, uid_t uid_range, int userns_fd, DissectImageFlags flags);
177
int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t uid_shift, uid_t uid_range, int userns_fd, DissectImageFlags flags);
178

179
int dissected_image_acquire_metadata(DissectedImage *m, int userns_fd, DissectImageFlags extra_flags);
180
int dissected_image_name_from_path(const char *path, char **ret);
181

182
Architecture dissected_image_architecture(DissectedImage *m);
183

184
static inline bool dissected_image_is_bootable_os(DissectedImage *m) {
32✔
185
        return m && m->has_init_system > 0;
32✔
186
}
187

188
static inline bool dissected_image_is_bootable_uefi(DissectedImage *m) {
27✔
189
        return m && m->partitions[PARTITION_ESP].found && dissected_image_is_bootable_os(m);
32✔
190
}
191

192
bool dissected_image_is_portable(DissectedImage *m) _pure_;
193
bool dissected_image_is_initrd(DissectedImage *m) _pure_;
194

195
DecryptedImage* decrypted_image_ref(DecryptedImage *p);
196
DecryptedImage* decrypted_image_unref(DecryptedImage *p);
197
DEFINE_TRIVIAL_CLEANUP_FUNC(DecryptedImage*, decrypted_image_unref);
347✔
198

199
int dissected_image_relinquish(DissectedImage *m);
200

201
void image_filter_done(ImageFilter *f);
202
ImageFilter *image_filter_free(ImageFilter *f);
203
DEFINE_TRIVIAL_CLEANUP_FUNC(ImageFilter*, image_filter_free);
147✔
204
int image_filter_parse(const char *s, ImageFilter **ret);
205

206
int verity_settings_load(VeritySettings *verity, const char *image, const char *root_hash_path, const char *root_hash_sig_path);
207

208
static inline bool verity_settings_set(const VeritySettings *settings) {
209
        return settings &&
210
                (iovec_is_set(&settings->root_hash) ||
211
                 iovec_is_set(&settings->root_hash_sig) ||
212
                 settings->data_path);
213
}
214

215
void verity_settings_done(VeritySettings *verity);
216
VeritySettings* verity_settings_free(VeritySettings *v);
217
void verity_settings_hash_func(const VeritySettings *s, struct siphash *state);
218
int verity_settings_compare_func(const VeritySettings *x, const VeritySettings *y);
219

220
DEFINE_TRIVIAL_CLEANUP_FUNC(VeritySettings*, verity_settings_free);
×
221
extern const struct hash_ops verity_settings_hash_ops;
222

223
static inline bool verity_settings_data_covers(const VeritySettings *verity, PartitionDesignator d) {
4,051✔
224
        /* Returns true if the verity settings contain sufficient information to cover the specified partition */
225
        return verity &&
4,051✔
226
                ((d >= 0 && verity->designator == d) || (d == PARTITION_ROOT && verity->designator < 0)) &&
4,051✔
227
                iovec_is_set(&verity->root_hash) &&
4,051✔
228
                verity->data_path;
2,966✔
229
}
230

231
int verity_settings_copy(VeritySettings *dest, const VeritySettings *source);
232

233
int dissected_image_load_verity_sig_partition(DissectedImage *m, int fd, VeritySettings *verity);
234
int dissected_image_guess_verity_roothash(DissectedImage *m, VeritySettings *verity);
235

236
bool dissected_image_verity_candidate(const DissectedImage *image, PartitionDesignator d);
237
bool dissected_image_verity_ready(const DissectedImage *image, PartitionDesignator d);
238
bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesignator d);
239

240
int mount_image_privately_interactively(const char *image, const ImagePolicy *image_policy, DissectImageFlags flags, char **ret_directory, int *ret_dir_fd, LoopDevice **ret_loop_device);
241

242
int verity_dissect_and_mount(
243
                int src_fd,
244
                const char *src,
245
                const char *dest,
246
                const MountOptions *options,
247
                const ImagePolicy *image_policy,
248
                const ImageFilter *image_filter,
249
                const ExtensionReleaseData *extension_release_data,
250
                ImageClass required_class,
251
                VeritySettings *verity,
252
                RuntimeScope runtime_scope,
253
                DissectedImage **ret_image);
254

255
int dissect_fstype_ok(const char *fstype);
256

257
int probe_sector_size(int fd, uint32_t *ret);
258
int probe_sector_size_prefer_ioctl(int fd, uint32_t *ret);
259

260
int partition_pick_mount_options(PartitionDesignator d, const char *fstype, bool rw, bool discard, char **ret_options, unsigned long *ret_ms_flags);
261

262
void extension_release_data_done(ExtensionReleaseData *data);
263

264
static inline const char* dissected_partition_fstype(const DissectedPartition *m) {
372✔
265
        assert(m);
372✔
266

267
        return m->decrypted_node ? m->decrypted_fstype : m->fstype;
372✔
268
}
269

270
int get_common_dissect_directory(char **ret);
271

272
int mountfsd_mount_image_fd(int image_fd, int userns_fd, const MountOptions *options, const ImagePolicy *image_policy, const VeritySettings *verity, DissectImageFlags flags, DissectedImage **ret);
273
int mountfsd_mount_image(const char *path, int userns_fd, const MountOptions *options, const ImagePolicy *image_policy, const VeritySettings *verity, DissectImageFlags flags, DissectedImage **ret);
274
int mountfsd_mount_directory_fd(int directory_fd, int userns_fd, DissectImageFlags flags, int *ret_mount_fd);
275
int mountfsd_mount_directory(const char *path, int userns_fd, DissectImageFlags flags, int *ret_mount_fd);
276

277
int mountfsd_make_directory_fd(int parent_fd, const char *name, mode_t mode, DissectImageFlags flags, int *ret_directory_fd);
278
int mountfsd_make_directory(const char *path, mode_t mode, DissectImageFlags flags, int *ret_directory_fd);
279

280
int copy_tree_at_foreign(int source_fd, int target_fd, int userns_fd);
281
int remove_tree_foreign(const char *path, int userns_fd);
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc