]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/dissect-image.c
Merge pull request #19156 from dtardon/enable-warn
[thirdparty/systemd.git] / src / shared / dissect-image.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
8c1be37e 2
10c1b188
LP
3#if HAVE_VALGRIND_MEMCHECK_H
4#include <valgrind/memcheck.h>
5#endif
6
01234e1f
YW
7#include <linux/dm-ioctl.h>
8#include <linux/loop.h>
8c1be37e 9#include <sys/mount.h>
3b925504
LP
10#include <sys/prctl.h>
11#include <sys/wait.h>
f5ea63a5 12#include <sysexits.h>
8c1be37e 13
3c1f2cee 14#include "sd-device.h"
dccca82b
LP
15#include "sd-id128.h"
16
8c1be37e 17#include "architecture.h"
18b5886e 18#include "ask-password-api.h"
8c1be37e 19#include "blkid-util.h"
18c528e9 20#include "blockdev-util.h"
3b925504 21#include "copy.h"
1e2f3230 22#include "cryptsetup-util.h"
3b925504 23#include "def.h"
553e15f2 24#include "device-nodes.h"
8437c059 25#include "device-util.h"
7718ac97 26#include "discover-image.h"
8c1be37e 27#include "dissect-image.h"
a709a315 28#include "dm-util.h"
686d13b9 29#include "env-file.h"
93f59701 30#include "extension-release.h"
18b5886e 31#include "fd-util.h"
78ebe980 32#include "fileio.h"
2eedfd2d 33#include "fs-util.h"
cf32c486 34#include "fsck-util.h"
8c1be37e 35#include "gpt.h"
78ebe980 36#include "hexdecoct.h"
e2054217 37#include "hostname-setup.h"
3b925504 38#include "id128-util.h"
593fe6c0 39#include "import-util.h"
6aa05ebd 40#include "mkdir.h"
8c1be37e 41#include "mount-util.h"
e4de7287 42#include "mountpoint-util.h"
6aa05ebd 43#include "namespace-util.h"
d8b4d14d 44#include "nulstr-util.h"
d58ad743 45#include "os-util.h"
8c1be37e 46#include "path-util.h"
3b925504
LP
47#include "process-util.h"
48#include "raw-clone.h"
49#include "signal-util.h"
8c1be37e 50#include "stat-util.h"
18b5886e 51#include "stdio-util.h"
8c1be37e
LP
52#include "string-table.h"
53#include "string-util.h"
2eedfd2d 54#include "strv.h"
e4de7287 55#include "tmpfile-util.h"
a8040b6d 56#include "udev-util.h"
2d3a5a73 57#include "user-util.h"
41488e1f 58#include "xattr-util.h"
8c1be37e 59
28e2641a
FF
60/* how many times to wait for the device nodes to appear */
61#define N_DEVICE_NODE_LIST_ATTEMPTS 10
62
c34b75a1 63int probe_filesystem(const char *node, char **ret_fstype) {
7cc84b2c 64 /* Try to find device content type and return it in *ret_fstype. If nothing is found,
5238e957 65 * 0/NULL will be returned. -EUCLEAN will be returned for ambiguous results, and an
7cc84b2c
ZJS
66 * different error otherwise. */
67
349cc4a5 68#if HAVE_BLKID
8e766630 69 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
18b5886e
LP
70 const char *fstype;
71 int r;
72
995fa2e5 73 errno = 0;
18b5886e
LP
74 b = blkid_new_probe_from_filename(node);
75 if (!b)
66855de7 76 return errno_or_else(ENOMEM);
18b5886e
LP
77
78 blkid_probe_enable_superblocks(b, 1);
79 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
80
81 errno = 0;
82 r = blkid_do_safeprobe(b);
7cc84b2c
ZJS
83 if (r == 1) {
84 log_debug("No type detected on partition %s", node);
18b5886e
LP
85 goto not_found;
86 }
58dfbfbd
LP
87 if (r == -2)
88 return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN),
89 "Results ambiguous for partition %s", node);
b382db9f 90 if (r != 0)
66855de7 91 return errno_or_else(EIO);
18b5886e
LP
92
93 (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
94
95 if (fstype) {
96 char *t;
97
98 t = strdup(fstype);
99 if (!t)
100 return -ENOMEM;
101
102 *ret_fstype = t;
103 return 1;
104 }
105
106not_found:
107 *ret_fstype = NULL;
108 return 0;
d1c536f5
ZJS
109#else
110 return -EOPNOTSUPP;
a75e27eb 111#endif
d1c536f5 112}
18b5886e 113
40c10d3f 114#if HAVE_BLKID
4ba86848
LP
115static int enumerator_for_parent(sd_device *d, sd_device_enumerator **ret) {
116 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
117 int r;
aae22eb3 118
f70e7f70 119 assert(d);
4ba86848 120 assert(ret);
f70e7f70 121
4ba86848
LP
122 r = sd_device_enumerator_new(&e);
123 if (r < 0)
124 return r;
3c1f2cee 125
210e1cd6
YW
126 r = sd_device_enumerator_add_match_subsystem(e, "block", true);
127 if (r < 0)
128 return r;
129
4ba86848
LP
130 r = sd_device_enumerator_add_match_parent(e, d);
131 if (r < 0)
132 return r;
133
210e1cd6
YW
134 r = sd_device_enumerator_add_match_sysattr(e, "partition", NULL, true);
135 if (r < 0)
136 return r;
137
4ba86848
LP
138 *ret = TAKE_PTR(e);
139 return 0;
cde942f6
JPRV
140}
141
c1737506
LP
142static int device_is_partition(
143 sd_device *d,
144 sd_device *expected_parent,
145 blkid_partition pp) {
146
0a8f9bc6 147 const char *v, *parent_syspath, *expected_parent_syspath;
4ba86848
LP
148 blkid_loff_t bsize, bstart;
149 uint64_t size, start;
150 int partno, bpartno, r;
0a8f9bc6 151 sd_device *parent;
aae22eb3 152
f70e7f70 153 assert(d);
0a8f9bc6 154 assert(expected_parent);
4ba86848 155 assert(pp);
f70e7f70 156
11368b69 157 r = sd_device_get_subsystem(d, &v);
4ba86848
LP
158 if (r < 0)
159 return r;
11368b69
YW
160 if (!streq(v, "block"))
161 return false;
162
163 if (sd_device_get_devtype(d, &v) < 0 || !streq(v, "partition"))
aae22eb3
LP
164 return false;
165
0a8f9bc6
YW
166 r = sd_device_get_parent(d, &parent);
167 if (r < 0)
168 return false; /* Doesn't have a parent? No relevant to us */
169
170 r = sd_device_get_syspath(parent, &parent_syspath); /* Check parent of device of this action */
171 if (r < 0)
172 return r;
173
174 r = sd_device_get_syspath(expected_parent, &expected_parent_syspath); /* Check parent of device we are looking for */
175 if (r < 0)
176 return r;
177
178 if (!path_equal(parent_syspath, expected_parent_syspath))
179 return false; /* Has a different parent than what we need, not interesting to us */
180
7d25c246
LP
181 /* On kernel uevents we may find the partition number in the PARTN= field. Let's use that preferably,
182 * since it's cheaper and more importantly: the sysfs attribute "partition" appears to become
c1737506
LP
183 * available late, hence let's use the property instead, which is available at the moment we see the
184 * uevent. */
185 r = sd_device_get_property_value(d, "PARTN", &v);
186 if (r == -ENOENT)
187 r = sd_device_get_sysattr_value(d, "partition", &v);
4ba86848
LP
188 if (r < 0)
189 return r;
c1737506 190
4ba86848
LP
191 r = safe_atoi(v, &partno);
192 if (r < 0)
193 return r;
ea887be0 194
4ba86848
LP
195 errno = 0;
196 bpartno = blkid_partition_get_partno(pp);
197 if (bpartno < 0)
198 return errno_or_else(EIO);
ea887be0 199
4ba86848
LP
200 if (partno != bpartno)
201 return false;
f70e7f70 202
4ba86848 203 r = sd_device_get_sysattr_value(d, "start", &v);
ea887be0
ZJS
204 if (r < 0)
205 return r;
4ba86848 206 r = safe_atou64(v, &start);
ea887be0
ZJS
207 if (r < 0)
208 return r;
209
4ba86848
LP
210 errno = 0;
211 bstart = blkid_partition_get_start(pp);
212 if (bstart < 0)
213 return errno_or_else(EIO);
214
215 if (start != (uint64_t) bstart)
216 return false;
217
218 r = sd_device_get_sysattr_value(d, "size", &v);
219 if (r < 0)
220 return r;
221 r = safe_atou64(v, &size);
ea887be0
ZJS
222 if (r < 0)
223 return r;
224
4ba86848
LP
225 errno = 0;
226 bsize = blkid_partition_get_size(pp);
227 if (bsize < 0)
228 return errno_or_else(EIO);
229
230 if (size != (uint64_t) bsize)
231 return false;
232
233 return true;
ea887be0
ZJS
234}
235
4ba86848
LP
236static int find_partition(
237 sd_device *parent,
238 blkid_partition pp,
4a62257d 239 usec_t timestamp_not_before,
4ba86848 240 sd_device **ret) {
ea887be0
ZJS
241
242 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
243 sd_device *q;
ea887be0
ZJS
244 int r;
245
4ba86848
LP
246 assert(parent);
247 assert(pp);
248 assert(ret);
f70e7f70 249
4ba86848 250 r = enumerator_for_parent(parent, &e);
ea887be0
ZJS
251 if (r < 0)
252 return r;
253
ea887be0 254 FOREACH_DEVICE(e, q) {
4a62257d
LP
255 uint64_t usec;
256
257 r = sd_device_get_usec_initialized(q, &usec);
258 if (r == -EBUSY) /* Not initialized yet */
259 continue;
260 if (r < 0)
261 return r;
262
263 if (timestamp_not_before != USEC_INFINITY &&
264 usec < timestamp_not_before) /* udev database entry older than our attachment? Then it's not ours */
265 continue;
266
0a8f9bc6 267 r = device_is_partition(q, parent, pp);
4ba86848
LP
268 if (r < 0)
269 return r;
270 if (r > 0) {
271 *ret = sd_device_ref(q);
272 return 0;
052eaf5c 273 }
ea887be0
ZJS
274 }
275
4ba86848
LP
276 return -ENXIO;
277}
10c1b188 278
4ba86848
LP
279struct wait_data {
280 sd_device *parent_device;
281 blkid_partition blkidp;
282 sd_device *found;
75dc190d 283 uint64_t uevent_seqnum_not_before;
4ba86848 284};
ea887be0 285
4ba86848
LP
286static inline void wait_data_done(struct wait_data *d) {
287 sd_device_unref(d->found);
288}
ea887be0 289
4ba86848 290static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, void *userdata) {
4ba86848 291 struct wait_data *w = userdata;
4ba86848
LP
292 int r;
293
294 assert(w);
295
a1130022 296 if (device_for_action(device, SD_DEVICE_REMOVE))
4ba86848
LP
297 return 0;
298
75dc190d
LP
299 if (w->uevent_seqnum_not_before != UINT64_MAX) {
300 uint64_t seqnum;
301
302 r = sd_device_get_seqnum(device, &seqnum);
303 if (r < 0)
304 goto finish;
305
306 if (seqnum <= w->uevent_seqnum_not_before) { /* From an older use of this loop device */
307 log_debug("Dropping event because seqnum too old (%" PRIu64 " <= %" PRIu64 ")",
308 seqnum, w->uevent_seqnum_not_before);
309 return 0;
310 }
311 }
312
0a8f9bc6 313 r = device_is_partition(device, w->parent_device, w->blkidp);
4ba86848
LP
314 if (r < 0)
315 goto finish;
316 if (r == 0) /* Not the one we need */
317 return 0;
318
319 /* It's the one we need! Yay! */
320 assert(!w->found);
321 w->found = sd_device_ref(device);
322 r = 0;
323
324finish:
325 return sd_event_exit(sd_device_monitor_get_event(monitor), r);
ea887be0
ZJS
326}
327
4ba86848
LP
328static int wait_for_partition_device(
329 sd_device *parent,
330 blkid_partition pp,
331 usec_t deadline,
75dc190d 332 uint64_t uevent_seqnum_not_before,
4a62257d 333 usec_t timestamp_not_before,
4ba86848
LP
334 sd_device **ret) {
335
336 _cleanup_(sd_event_source_unrefp) sd_event_source *timeout_source = NULL;
337 _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
338 _cleanup_(sd_event_unrefp) sd_event *event = NULL;
ea887be0
ZJS
339 int r;
340
4ba86848
LP
341 assert(parent);
342 assert(pp);
343 assert(ret);
344
4a62257d 345 r = find_partition(parent, pp, timestamp_not_before, ret);
4ba86848
LP
346 if (r != -ENXIO)
347 return r;
348
349 r = sd_event_new(&event);
350 if (r < 0)
351 return r;
352
353 r = sd_device_monitor_new(&monitor);
354 if (r < 0)
355 return r;
356
357 r = sd_device_monitor_filter_add_match_subsystem_devtype(monitor, "block", "partition");
358 if (r < 0)
359 return r;
360
210e1cd6
YW
361 r = sd_device_monitor_filter_add_match_parent(monitor, parent, true);
362 if (r < 0)
363 return r;
364
365 r = sd_device_monitor_filter_add_match_sysattr(monitor, "partition", NULL, true);
366 if (r < 0)
367 return r;
368
4ba86848
LP
369 r = sd_device_monitor_attach_event(monitor, event);
370 if (r < 0)
371 return r;
372
373 _cleanup_(wait_data_done) struct wait_data w = {
374 .parent_device = parent,
375 .blkidp = pp,
75dc190d 376 .uevent_seqnum_not_before = uevent_seqnum_not_before,
4ba86848 377 };
f70e7f70 378
4ba86848
LP
379 r = sd_device_monitor_start(monitor, device_monitor_handler, &w);
380 if (r < 0)
381 return r;
a8040b6d 382
4ba86848 383 /* Check again, the partition might have appeared in the meantime */
4a62257d 384 r = find_partition(parent, pp, timestamp_not_before, ret);
4ba86848
LP
385 if (r != -ENXIO)
386 return r;
387
388 if (deadline != USEC_INFINITY) {
389 r = sd_event_add_time(
390 event, &timeout_source,
391 CLOCK_MONOTONIC, deadline, 0,
392 NULL, INT_TO_PTR(-ETIMEDOUT));
393 if (r < 0)
ea887be0
ZJS
394 return r;
395 }
396
4ba86848
LP
397 r = sd_event_loop(event);
398 if (r < 0)
399 return r;
400
401 assert(w.found);
402 *ret = TAKE_PTR(w.found);
403 return 0;
ea887be0
ZJS
404}
405
0f7c9a3d
LP
406static void check_partition_flags(
407 const char *node,
408 unsigned long long pflags,
409 unsigned long long supported) {
410
411 assert(node);
412
413 /* Mask away all flags supported by this partition's type and the three flags the UEFI spec defines generically */
414 pflags &= ~(supported | GPT_FLAG_REQUIRED_PARTITION | GPT_FLAG_NO_BLOCK_IO_PROTOCOL | GPT_FLAG_LEGACY_BIOS_BOOTABLE);
415
416 if (pflags == 0)
417 return;
418
419 /* If there are other bits set, then log about it, to make things discoverable */
420 for (unsigned i = 0; i < sizeof(pflags) * 8; i++) {
421 unsigned long long bit = 1ULL << i;
422 if (!FLAGS_SET(pflags, bit))
423 continue;
424
425 log_debug("Unexpected partition flag %llu set on %s!", bit, node);
426 }
427}
428
786e3a52
LP
429static int device_wait_for_initialization_harder(
430 sd_device *device,
431 const char *subsystem,
432 usec_t deadline,
433 sd_device **ret) {
434
435 _cleanup_free_ char *uevent = NULL;
436 usec_t start, left, retrigger_timeout;
437 int r;
438
439 start = now(CLOCK_MONOTONIC);
440 left = usec_sub_unsigned(deadline, start);
441
442 if (DEBUG_LOGGING) {
443 char buf[FORMAT_TIMESPAN_MAX];
444 const char *sn = NULL;
445
446 (void) sd_device_get_sysname(device, &sn);
447 log_debug("Waiting for device '%s' to initialize for %s.", strna(sn), format_timespan(buf, sizeof(buf), left, 0));
448 }
449
450 if (left != USEC_INFINITY)
451 retrigger_timeout = CLAMP(left / 4, 1 * USEC_PER_SEC, 5 * USEC_PER_SEC); /* A fourth of the total timeout, but let's clamp to 1s…5s range */
452 else
453 retrigger_timeout = 2 * USEC_PER_SEC;
454
455 for (;;) {
456 usec_t local_deadline, n;
457 bool last_try;
458
459 n = now(CLOCK_MONOTONIC);
460 assert(n >= start);
461
462 /* Find next deadline, when we'll retrigger */
463 local_deadline = start +
464 DIV_ROUND_UP(n - start, retrigger_timeout) * retrigger_timeout;
465
466 if (deadline != USEC_INFINITY && deadline <= local_deadline) {
467 local_deadline = deadline;
468 last_try = true;
469 } else
470 last_try = false;
471
472 r = device_wait_for_initialization(device, subsystem, local_deadline, ret);
473 if (r >= 0 && DEBUG_LOGGING) {
474 char buf[FORMAT_TIMESPAN_MAX];
475 const char *sn = NULL;
476
477 (void) sd_device_get_sysname(device, &sn);
478 log_debug("Successfully waited for device '%s' to initialize for %s.", strna(sn), format_timespan(buf, sizeof(buf), usec_sub_unsigned(now(CLOCK_MONOTONIC), start), 0));
479
480 }
481 if (r != -ETIMEDOUT || last_try)
482 return r;
483
484 if (!uevent) {
485 const char *syspath;
486
487 r = sd_device_get_syspath(device, &syspath);
488 if (r < 0)
489 return r;
490
491 uevent = path_join(syspath, "uevent");
492 if (!uevent)
493 return -ENOMEM;
494 }
495
496 if (DEBUG_LOGGING) {
497 char buf[FORMAT_TIMESPAN_MAX];
498
499 log_debug("Device didn't initialize within %s, assuming lost event. Retriggering device through %s.",
500 format_timespan(buf, sizeof(buf), usec_sub_unsigned(now(CLOCK_MONOTONIC), start), 0),
501 uevent);
502 }
503
504 r = write_string_file(uevent, "change", WRITE_STRING_FILE_DISABLE_BUFFER);
505 if (r < 0)
506 return r;
507 }
508}
40c10d3f 509#endif
aae22eb3 510
4ba86848
LP
511#define DEVICE_TIMEOUT_USEC (45 * USEC_PER_SEC)
512
08fe0a53
LP
513static void dissected_partition_done(DissectedPartition *p) {
514 assert(p);
515
516 free(p->fstype);
517 free(p->node);
518 free(p->label);
519 free(p->decrypted_fstype);
520 free(p->decrypted_node);
521 free(p->mount_options);
522
523 *p = (DissectedPartition) {
524 .partno = -1,
525 .architecture = -1
526 };
527}
528
4526113f
LP
529int dissect_image(
530 int fd,
89e62e0b 531 const VeritySettings *verity,
18d73705 532 const MountOptions *mount_options,
75dc190d 533 uint64_t uevent_seqnum_not_before,
4a62257d 534 usec_t timestamp_not_before,
4526113f
LP
535 DissectImageFlags flags,
536 DissectedImage **ret) {
8c1be37e 537
349cc4a5 538#if HAVE_BLKID
62ea0ed0
LP
539#ifdef GPT_ROOT_NATIVE
540 sd_id128_t root_uuid = SD_ID128_NULL, root_verity_uuid = SD_ID128_NULL;
541#endif
542#ifdef GPT_USR_NATIVE
543 sd_id128_t usr_uuid = SD_ID128_NULL, usr_verity_uuid = SD_ID128_NULL;
544#endif
1f8fb21c
ZJS
545 bool is_gpt, is_mbr, multiple_generic = false,
546 generic_rw = false; /* initialize to appease gcc */
3c1f2cee 547 _cleanup_(sd_device_unrefp) sd_device *d = NULL;
8c1be37e 548 _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
8e766630 549 _cleanup_(blkid_free_probep) blkid_probe b = NULL;
8c1be37e 550 _cleanup_free_ char *generic_node = NULL;
be30ad41 551 sd_id128_t generic_uuid = SD_ID128_NULL;
593fe6c0 552 const char *pttype = NULL, *sysname = NULL;
8c1be37e 553 blkid_partlist pl;
1f8fb21c 554 int r, generic_nr = -1, n_partitions;
8c1be37e 555 struct stat st;
4ba86848 556 usec_t deadline;
8c1be37e
LP
557
558 assert(fd >= 0);
559 assert(ret);
89e62e0b 560 assert(!verity || verity->root_hash || verity->root_hash_size == 0);
e7cbe5cb 561 assert(!((flags & DISSECT_IMAGE_GPT_ONLY) && (flags & DISSECT_IMAGE_NO_PARTITION_TABLE)));
8c1be37e
LP
562
563 /* Probes a disk image, and returns information about what it found in *ret.
564 *
4623e8e6 565 * Returns -ENOPKG if no suitable partition table or file system could be found.
2679f407
LP
566 * Returns -EADDRNOTAVAIL if a root hash was specified but no matching root/verity partitions found.
567 * Returns -ENXIO if we couldn't find any partition suitable as root or /usr partition
568 * Returns -ENOTUNIQ if we only found multiple generic partitions and thus don't know what to do with that */
4623e8e6 569
89e62e0b 570 if (verity && verity->root_hash) {
aee36b4e
LP
571 sd_id128_t fsuuid, vuuid;
572
573 /* If a root hash is supplied, then we use the root partition that has a UUID that match the
574 * first 128bit of the root hash. And we use the verity partition that has a UUID that match
575 * the final 128bit. */
4623e8e6 576
89e62e0b 577 if (verity->root_hash_size < sizeof(sd_id128_t))
4623e8e6
LP
578 return -EINVAL;
579
aee36b4e
LP
580 memcpy(&fsuuid, verity->root_hash, sizeof(sd_id128_t));
581 memcpy(&vuuid, (const uint8_t*) verity->root_hash + verity->root_hash_size - sizeof(sd_id128_t), sizeof(sd_id128_t));
4623e8e6 582
aee36b4e 583 if (sd_id128_is_null(fsuuid))
4623e8e6 584 return -EINVAL;
aee36b4e 585 if (sd_id128_is_null(vuuid))
4623e8e6 586 return -EINVAL;
aee36b4e
LP
587
588 /* If the verity data declares it's for the /usr partition, then search for that, in all
589 * other cases assume it's for the root partition. */
62ea0ed0 590#ifdef GPT_USR_NATIVE
aee36b4e
LP
591 if (verity->designator == PARTITION_USR) {
592 usr_uuid = fsuuid;
593 usr_verity_uuid = vuuid;
594 } else {
62ea0ed0
LP
595#endif
596#ifdef GPT_ROOT_NATIVE
aee36b4e
LP
597 root_uuid = fsuuid;
598 root_verity_uuid = vuuid;
62ea0ed0
LP
599#endif
600#ifdef GPT_USR_NATIVE
aee36b4e 601 }
62ea0ed0 602#endif
4623e8e6 603 }
8c1be37e
LP
604
605 if (fstat(fd, &st) < 0)
606 return -errno;
607
608 if (!S_ISBLK(st.st_mode))
609 return -ENOTBLK;
610
930aa88f 611 r = sd_device_new_from_stat_rdev(&d, &st);
6c544d14
LP
612 if (r < 0)
613 return r;
614
615 if (!FLAGS_SET(flags, DISSECT_IMAGE_NO_UDEV)) {
616 _cleanup_(sd_device_unrefp) sd_device *initialized = NULL;
617
618 /* If udev support is enabled, then let's wait for the device to be initialized before we doing anything. */
619
786e3a52
LP
620 r = device_wait_for_initialization_harder(
621 d,
622 "block",
623 usec_add(now(CLOCK_MONOTONIC), DEVICE_TIMEOUT_USEC),
624 &initialized);
6c544d14
LP
625 if (r < 0)
626 return r;
627
628 sd_device_unref(d);
629 d = TAKE_PTR(initialized);
630 }
631
8c1be37e
LP
632 b = blkid_new_probe();
633 if (!b)
634 return -ENOMEM;
635
636 errno = 0;
637 r = blkid_probe_set_device(b, fd, 0, 0);
b382db9f 638 if (r != 0)
66855de7 639 return errno_or_else(ENOMEM);
8c1be37e 640
9b6deb03
LP
641 if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
642 /* Look for file system superblocks, unless we only shall look for GPT partition tables */
643 blkid_probe_enable_superblocks(b, 1);
644 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE);
645 }
646
8c1be37e
LP
647 blkid_probe_enable_partitions(b, 1);
648 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
649
650 errno = 0;
651 r = blkid_do_safeprobe(b);
59ba6d0c
LP
652 if (IN_SET(r, -2, 1))
653 return log_debug_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to identify any partition table.");
b382db9f 654 if (r != 0)
66855de7 655 return errno_or_else(EIO);
8c1be37e
LP
656
657 m = new0(DissectedImage, 1);
658 if (!m)
659 return -ENOMEM;
660
593fe6c0
LB
661 r = sd_device_get_sysname(d, &sysname);
662 if (r < 0)
663 return log_debug_errno(r, "Failed to get device sysname: %m");
664 if (startswith(sysname, "loop")) {
665 _cleanup_free_ char *name_stripped = NULL;
666 const char *full_path;
667
668 r = sd_device_get_sysattr_value(d, "loop/backing_file", &full_path);
669 if (r < 0)
670 log_debug_errno(r, "Failed to lookup image name via loop device backing file sysattr, ignoring: %m");
671 else {
672 r = raw_strip_suffixes(basename(full_path), &name_stripped);
673 if (r < 0)
674 return r;
675 }
676
677 free_and_replace(m->image_name, name_stripped);
678 } else {
679 r = free_and_strdup(&m->image_name, sysname);
680 if (r < 0)
681 return r;
682 }
683
684 if (!image_name_is_valid(m->image_name)) {
685 log_debug("Image name %s is not valid, ignoring", strempty(m->image_name));
686 m->image_name = mfree(m->image_name);
687 }
688
e7cbe5cb 689 if ((!(flags & DISSECT_IMAGE_GPT_ONLY) &&
4b5de5dd 690 (flags & DISSECT_IMAGE_GENERIC_ROOT)) ||
e7cbe5cb 691 (flags & DISSECT_IMAGE_NO_PARTITION_TABLE)) {
9b6deb03 692 const char *usage = NULL;
8c1be37e 693
aee36b4e
LP
694 /* If flags permit this, also allow using non-partitioned single-filesystem images */
695
9b6deb03
LP
696 (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
697 if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
6c544d14 698 const char *fstype = NULL, *options = NULL, *devname = NULL;
18d73705 699 _cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
8c1be37e 700
9b6deb03
LP
701 /* OK, we have found a file system, that's our root partition then. */
702 (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
8c1be37e 703
9b6deb03
LP
704 if (fstype) {
705 t = strdup(fstype);
706 if (!t)
707 return -ENOMEM;
708 }
709
6c544d14 710 r = sd_device_get_devname(d, &devname);
54b22b26
LP
711 if (r < 0)
712 return r;
8c1be37e 713
6c544d14
LP
714 n = strdup(devname);
715 if (!n)
716 return -ENOMEM;
717
e7cbe5cb 718 m->single_file_system = true;
aee36b4e 719 m->verity = verity && verity->root_hash && verity->data_path && (verity->designator < 0 || verity->designator == PARTITION_ROOT);
89e62e0b 720 m->can_verity = verity && verity->data_path;
e7cbe5cb 721
f5215bc8 722 options = mount_options_from_designator(mount_options, PARTITION_ROOT);
18d73705
LB
723 if (options) {
724 o = strdup(options);
725 if (!o)
726 return -ENOMEM;
727 }
728
9b6deb03
LP
729 m->partitions[PARTITION_ROOT] = (DissectedPartition) {
730 .found = true,
e7cbe5cb 731 .rw = !m->verity,
9b6deb03
LP
732 .partno = -1,
733 .architecture = _ARCHITECTURE_INVALID,
1cc6c93a
YW
734 .fstype = TAKE_PTR(t),
735 .node = TAKE_PTR(n),
18d73705 736 .mount_options = TAKE_PTR(o),
9b6deb03 737 };
8c1be37e 738
4db1879a 739 m->encrypted = streq_ptr(fstype, "crypto_LUKS");
18b5886e 740
1cc6c93a 741 *ret = TAKE_PTR(m);
9b6deb03
LP
742 return 0;
743 }
8c1be37e
LP
744 }
745
746 (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
747 if (!pttype)
748 return -ENOPKG;
749
750 is_gpt = streq_ptr(pttype, "gpt");
751 is_mbr = streq_ptr(pttype, "dos");
752
9b6deb03 753 if (!is_gpt && ((flags & DISSECT_IMAGE_GPT_ONLY) || !is_mbr))
8c1be37e
LP
754 return -ENOPKG;
755
4ba86848
LP
756 /* Safety check: refuse block devices that carry a partition table but for which the kernel doesn't
757 * do partition scanning. */
758 r = blockdev_partscan_enabled(fd);
759 if (r < 0)
760 return r;
761 if (r == 0)
762 return -EPROTONOSUPPORT;
763
8c1be37e
LP
764 errno = 0;
765 pl = blkid_probe_get_partitions(b);
b382db9f 766 if (!pl)
66855de7 767 return errno_or_else(ENOMEM);
8c1be37e 768
4ba86848
LP
769 errno = 0;
770 n_partitions = blkid_partlist_numof_partitions(pl);
771 if (n_partitions < 0)
772 return errno_or_else(EIO);
8c1be37e 773
4ba86848
LP
774 deadline = usec_add(now(CLOCK_MONOTONIC), DEVICE_TIMEOUT_USEC);
775 for (int i = 0; i < n_partitions; i++) {
776 _cleanup_(sd_device_unrefp) sd_device *q = NULL;
9b6deb03 777 unsigned long long pflags;
8c1be37e 778 blkid_partition pp;
cde942f6 779 const char *node;
8c1be37e
LP
780 int nr;
781
4ba86848
LP
782 errno = 0;
783 pp = blkid_partlist_get_partition(pl, i);
784 if (!pp)
785 return errno_or_else(EIO);
aae22eb3 786
4a62257d 787 r = wait_for_partition_device(d, pp, deadline, uevent_seqnum_not_before, timestamp_not_before, &q);
4ba86848
LP
788 if (r < 0)
789 return r;
7be1420f 790
3c1f2cee
YW
791 r = sd_device_get_devname(q, &node);
792 if (r < 0)
4ba86848 793 return r;
8c1be37e 794
9b6deb03 795 pflags = blkid_partition_get_flags(pp);
8c1be37e 796
4ba86848 797 errno = 0;
8c1be37e
LP
798 nr = blkid_partition_get_partno(pp);
799 if (nr < 0)
4ba86848 800 return errno_or_else(EIO);
8c1be37e
LP
801
802 if (is_gpt) {
569a0e42
LP
803 PartitionDesignator designator = _PARTITION_DESIGNATOR_INVALID;
804 int architecture = _ARCHITECTURE_INVALID;
08fe0a53 805 const char *stype, *sid, *fstype = NULL, *label;
4623e8e6 806 sd_id128_t type_id, id;
8c1be37e
LP
807 bool rw = true;
808
4623e8e6
LP
809 sid = blkid_partition_get_uuid(pp);
810 if (!sid)
811 continue;
812 if (sd_id128_from_string(sid, &id) < 0)
813 continue;
814
8c1be37e
LP
815 stype = blkid_partition_get_type_string(pp);
816 if (!stype)
817 continue;
8c1be37e
LP
818 if (sd_id128_from_string(stype, &type_id) < 0)
819 continue;
820
08fe0a53
LP
821 label = blkid_partition_get_name(pp); /* libblkid returns NULL here if empty */
822
8c1be37e 823 if (sd_id128_equal(type_id, GPT_HOME)) {
a48dd347 824
0f7c9a3d
LP
825 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
826
a48dd347
LP
827 if (pflags & GPT_FLAG_NO_AUTO)
828 continue;
829
8c1be37e 830 designator = PARTITION_HOME;
9b6deb03 831 rw = !(pflags & GPT_FLAG_READ_ONLY);
aee36b4e 832
8c1be37e 833 } else if (sd_id128_equal(type_id, GPT_SRV)) {
a48dd347 834
0f7c9a3d
LP
835 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
836
a48dd347
LP
837 if (pflags & GPT_FLAG_NO_AUTO)
838 continue;
839
8c1be37e 840 designator = PARTITION_SRV;
9b6deb03 841 rw = !(pflags & GPT_FLAG_READ_ONLY);
aee36b4e 842
8c1be37e 843 } else if (sd_id128_equal(type_id, GPT_ESP)) {
a48dd347 844
aee36b4e
LP
845 /* Note that we don't check the GPT_FLAG_NO_AUTO flag for the ESP, as it is
846 * not defined there. We instead check the GPT_FLAG_NO_BLOCK_IO_PROTOCOL, as
847 * recommended by the UEFI spec (See "12.3.3 Number and Location of System
848 * Partitions"). */
a48dd347
LP
849
850 if (pflags & GPT_FLAG_NO_BLOCK_IO_PROTOCOL)
851 continue;
852
8c1be37e
LP
853 designator = PARTITION_ESP;
854 fstype = "vfat";
a8c47660
LP
855
856 } else if (sd_id128_equal(type_id, GPT_XBOOTLDR)) {
857
0f7c9a3d
LP
858 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
859
a8c47660
LP
860 if (pflags & GPT_FLAG_NO_AUTO)
861 continue;
862
863 designator = PARTITION_XBOOTLDR;
864 rw = !(pflags & GPT_FLAG_READ_ONLY);
8c1be37e
LP
865 }
866#ifdef GPT_ROOT_NATIVE
867 else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
4623e8e6 868
0f7c9a3d
LP
869 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
870
a48dd347
LP
871 if (pflags & GPT_FLAG_NO_AUTO)
872 continue;
873
4623e8e6
LP
874 /* If a root ID is specified, ignore everything but the root id */
875 if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
876 continue;
877
8c1be37e
LP
878 designator = PARTITION_ROOT;
879 architecture = native_architecture();
9b6deb03 880 rw = !(pflags & GPT_FLAG_READ_ONLY);
aee36b4e 881
4f8b86e3 882 } else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
4623e8e6 883
0f7c9a3d
LP
884 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
885
a48dd347
LP
886 if (pflags & GPT_FLAG_NO_AUTO)
887 continue;
888
4623e8e6
LP
889 m->can_verity = true;
890
891 /* Ignore verity unless a root hash is specified */
aee36b4e 892 if (sd_id128_is_null(root_verity_uuid) || !sd_id128_equal(root_verity_uuid, id))
4623e8e6
LP
893 continue;
894
895 designator = PARTITION_ROOT_VERITY;
896 fstype = "DM_verity_hash";
897 architecture = native_architecture();
898 rw = false;
899 }
900#endif
8c1be37e
LP
901#ifdef GPT_ROOT_SECONDARY
902 else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
4623e8e6 903
0f7c9a3d
LP
904 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
905
a48dd347
LP
906 if (pflags & GPT_FLAG_NO_AUTO)
907 continue;
908
4623e8e6
LP
909 /* If a root ID is specified, ignore everything but the root id */
910 if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
911 continue;
912
8c1be37e
LP
913 designator = PARTITION_ROOT_SECONDARY;
914 architecture = SECONDARY_ARCHITECTURE;
9b6deb03 915 rw = !(pflags & GPT_FLAG_READ_ONLY);
aee36b4e 916
4f8b86e3 917 } else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
a48dd347 918
0f7c9a3d
LP
919 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
920
a48dd347
LP
921 if (pflags & GPT_FLAG_NO_AUTO)
922 continue;
923
4623e8e6
LP
924 m->can_verity = true;
925
926 /* Ignore verity unless root has is specified */
aee36b4e 927 if (sd_id128_is_null(root_verity_uuid) || !sd_id128_equal(root_verity_uuid, id))
4623e8e6
LP
928 continue;
929
930 designator = PARTITION_ROOT_SECONDARY_VERITY;
931 fstype = "DM_verity_hash";
932 architecture = SECONDARY_ARCHITECTURE;
933 rw = false;
934 }
aee36b4e
LP
935#endif
936#ifdef GPT_USR_NATIVE
937 else if (sd_id128_equal(type_id, GPT_USR_NATIVE)) {
938
939 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
940
941 if (pflags & GPT_FLAG_NO_AUTO)
942 continue;
943
944 /* If a usr ID is specified, ignore everything but the usr id */
945 if (!sd_id128_is_null(usr_uuid) && !sd_id128_equal(usr_uuid, id))
946 continue;
947
948 designator = PARTITION_USR;
949 architecture = native_architecture();
950 rw = !(pflags & GPT_FLAG_READ_ONLY);
951
952 } else if (sd_id128_equal(type_id, GPT_USR_NATIVE_VERITY)) {
953
954 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
955
956 if (pflags & GPT_FLAG_NO_AUTO)
957 continue;
958
959 m->can_verity = true;
960
961 /* Ignore verity unless a usr hash is specified */
962 if (sd_id128_is_null(usr_verity_uuid) || !sd_id128_equal(usr_verity_uuid, id))
963 continue;
964
965 designator = PARTITION_USR_VERITY;
966 fstype = "DM_verity_hash";
967 architecture = native_architecture();
968 rw = false;
969 }
970#endif
971#ifdef GPT_USR_SECONDARY
972 else if (sd_id128_equal(type_id, GPT_USR_SECONDARY)) {
973
974 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
975
976 if (pflags & GPT_FLAG_NO_AUTO)
977 continue;
978
979 /* If a usr ID is specified, ignore everything but the usr id */
980 if (!sd_id128_is_null(usr_uuid) && !sd_id128_equal(usr_uuid, id))
981 continue;
982
983 designator = PARTITION_USR_SECONDARY;
984 architecture = SECONDARY_ARCHITECTURE;
985 rw = !(pflags & GPT_FLAG_READ_ONLY);
986
987 } else if (sd_id128_equal(type_id, GPT_USR_SECONDARY_VERITY)) {
988
989 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
990
991 if (pflags & GPT_FLAG_NO_AUTO)
992 continue;
993
994 m->can_verity = true;
995
996 /* Ignore verity unless usr has is specified */
997 if (sd_id128_is_null(usr_verity_uuid) || !sd_id128_equal(usr_verity_uuid, id))
998 continue;
999
1000 designator = PARTITION_USR_SECONDARY_VERITY;
1001 fstype = "DM_verity_hash";
1002 architecture = SECONDARY_ARCHITECTURE;
1003 rw = false;
1004 }
8c1be37e
LP
1005#endif
1006 else if (sd_id128_equal(type_id, GPT_SWAP)) {
a48dd347 1007
0f7c9a3d
LP
1008 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO);
1009
a48dd347
LP
1010 if (pflags & GPT_FLAG_NO_AUTO)
1011 continue;
1012
8c1be37e
LP
1013 designator = PARTITION_SWAP;
1014 fstype = "swap";
aee36b4e 1015
8c1be37e
LP
1016 } else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
1017
0f7c9a3d
LP
1018 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
1019
a48dd347
LP
1020 if (pflags & GPT_FLAG_NO_AUTO)
1021 continue;
1022
8c1be37e
LP
1023 if (generic_node)
1024 multiple_generic = true;
1025 else {
1026 generic_nr = nr;
9b6deb03 1027 generic_rw = !(pflags & GPT_FLAG_READ_ONLY);
be30ad41 1028 generic_uuid = id;
8c1be37e
LP
1029 generic_node = strdup(node);
1030 if (!generic_node)
1031 return -ENOMEM;
1032 }
d4dffb85
LP
1033
1034 } else if (sd_id128_equal(type_id, GPT_TMP)) {
1035
0f7c9a3d
LP
1036 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
1037
d4dffb85
LP
1038 if (pflags & GPT_FLAG_NO_AUTO)
1039 continue;
1040
1041 designator = PARTITION_TMP;
1042 rw = !(pflags & GPT_FLAG_READ_ONLY);
1043
1044 } else if (sd_id128_equal(type_id, GPT_VAR)) {
1045
0f7c9a3d
LP
1046 check_partition_flags(node, pflags, GPT_FLAG_NO_AUTO|GPT_FLAG_READ_ONLY);
1047
d4dffb85
LP
1048 if (pflags & GPT_FLAG_NO_AUTO)
1049 continue;
1050
1051 if (!FLAGS_SET(flags, DISSECT_IMAGE_RELAX_VAR_CHECK)) {
1052 sd_id128_t var_uuid;
1053
1054 /* For /var we insist that the uuid of the partition matches the
1055 * HMAC-SHA256 of the /var GPT partition type uuid, keyed by machine
1056 * ID. Why? Unlike the other partitions /var is inherently
1057 * installation specific, hence we need to be careful not to mount it
1058 * in the wrong installation. By hashing the partition UUID from
1059 * /etc/machine-id we can securely bind the partition to the
1060 * installation. */
1061
1062 r = sd_id128_get_machine_app_specific(GPT_VAR, &var_uuid);
1063 if (r < 0)
1064 return r;
1065
1066 if (!sd_id128_equal(var_uuid, id)) {
1067 log_debug("Found a /var/ partition, but its UUID didn't match our expectations, ignoring.");
1068 continue;
1069 }
1070 }
1071
1072 designator = PARTITION_VAR;
1073 rw = !(pflags & GPT_FLAG_READ_ONLY);
8c1be37e
LP
1074 }
1075
1076 if (designator != _PARTITION_DESIGNATOR_INVALID) {
08fe0a53 1077 _cleanup_free_ char *t = NULL, *n = NULL, *o = NULL, *l = NULL;
18d73705 1078 const char *options = NULL;
8c1be37e 1079
08fe0a53
LP
1080 if (m->partitions[designator].found) {
1081 /* For most partition types the first one we see wins. Except for the
1082 * rootfs and /usr, where we do a version compare of the label, and
1083 * let the newest version win. This permits a simple A/B versioning
1084 * scheme in OS images. */
1085
1086 if (!PARTITION_DESIGNATOR_VERSIONED(designator) ||
1087 strverscmp_improved(m->partitions[designator].label, label) >= 0)
1088 continue;
1089
1090 dissected_partition_done(m->partitions + designator);
1091 }
8c1be37e
LP
1092
1093 if (fstype) {
1094 t = strdup(fstype);
1095 if (!t)
1096 return -ENOMEM;
1097 }
1098
1099 n = strdup(node);
1100 if (!n)
1101 return -ENOMEM;
1102
08fe0a53
LP
1103 if (label) {
1104 l = strdup(label);
1105 if (!l)
1106 return -ENOMEM;
1107 }
1108
f5215bc8 1109 options = mount_options_from_designator(mount_options, designator);
18d73705
LB
1110 if (options) {
1111 o = strdup(options);
1112 if (!o)
1113 return -ENOMEM;
1114 }
1115
8c1be37e
LP
1116 m->partitions[designator] = (DissectedPartition) {
1117 .found = true,
1118 .partno = nr,
1119 .rw = rw,
1120 .architecture = architecture,
1cc6c93a
YW
1121 .node = TAKE_PTR(n),
1122 .fstype = TAKE_PTR(t),
08fe0a53 1123 .label = TAKE_PTR(l),
be30ad41 1124 .uuid = id,
18d73705 1125 .mount_options = TAKE_PTR(o),
8c1be37e 1126 };
8c1be37e
LP
1127 }
1128
1129 } else if (is_mbr) {
1130
a8c47660 1131 switch (blkid_partition_get_type(pp)) {
8c1be37e 1132
a8c47660
LP
1133 case 0x83: /* Linux partition */
1134
1135 if (pflags != 0x80) /* Bootable flag */
1136 continue;
8c1be37e 1137
a8c47660
LP
1138 if (generic_node)
1139 multiple_generic = true;
1140 else {
1141 generic_nr = nr;
1142 generic_rw = true;
1143 generic_node = strdup(node);
1144 if (!generic_node)
1145 return -ENOMEM;
1146 }
1147
1148 break;
1149
1150 case 0xEA: { /* Boot Loader Spec extended $BOOT partition */
18d73705 1151 _cleanup_free_ char *n = NULL, *o = NULL;
a8c47660 1152 sd_id128_t id = SD_ID128_NULL;
18d73705 1153 const char *sid, *options = NULL;
a8c47660
LP
1154
1155 /* First one wins */
1156 if (m->partitions[PARTITION_XBOOTLDR].found)
1157 continue;
1158
1159 sid = blkid_partition_get_uuid(pp);
1160 if (sid)
1161 (void) sd_id128_from_string(sid, &id);
1162
1163 n = strdup(node);
1164 if (!n)
8c1be37e 1165 return -ENOMEM;
a8c47660 1166
f5215bc8 1167 options = mount_options_from_designator(mount_options, PARTITION_XBOOTLDR);
18d73705
LB
1168 if (options) {
1169 o = strdup(options);
1170 if (!o)
1171 return -ENOMEM;
1172 }
1173
a8c47660
LP
1174 m->partitions[PARTITION_XBOOTLDR] = (DissectedPartition) {
1175 .found = true,
1176 .partno = nr,
1177 .rw = true,
1178 .architecture = _ARCHITECTURE_INVALID,
1179 .node = TAKE_PTR(n),
1180 .uuid = id,
18d73705 1181 .mount_options = TAKE_PTR(o),
a8c47660
LP
1182 };
1183
1184 break;
1185 }}
8c1be37e
LP
1186 }
1187 }
1188
74cb2db9
LP
1189 if (m->partitions[PARTITION_ROOT].found) {
1190 /* If we found the primary arch, then invalidate the secondary arch to avoid any ambiguities,
1191 * since we never want to mount the secondary arch in this case. */
1192 m->partitions[PARTITION_ROOT_SECONDARY].found = false;
1193 m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found = false;
aee36b4e
LP
1194 m->partitions[PARTITION_USR_SECONDARY].found = false;
1195 m->partitions[PARTITION_USR_SECONDARY_VERITY].found = false;
8c1be37e 1196
7cf66030
LP
1197 } else if (m->partitions[PARTITION_ROOT_VERITY].found)
1198 return -EADDRNOTAVAIL; /* Verity found but no matching rootfs? Something is off, refuse. */
4623e8e6 1199
7cf66030 1200 else if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
aee36b4e 1201
7cf66030
LP
1202 /* No root partition found but there's one for the secondary architecture? Then upgrade
1203 * secondary arch to first */
4623e8e6 1204
7cf66030
LP
1205 m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
1206 zero(m->partitions[PARTITION_ROOT_SECONDARY]);
1207 m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
1208 zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
aee36b4e 1209
7cf66030
LP
1210 m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_SECONDARY];
1211 zero(m->partitions[PARTITION_USR_SECONDARY]);
1212 m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY];
1213 zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
e0f9e7bd 1214
7cf66030
LP
1215 } else if (m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found)
1216 return -EADDRNOTAVAIL; /* as above */
18d73705 1217
7cf66030
LP
1218 else if (m->partitions[PARTITION_USR].found) {
1219
1220 /* Invalidate secondary arch /usr/ if we found the primary arch */
1221 m->partitions[PARTITION_USR_SECONDARY].found = false;
1222 m->partitions[PARTITION_USR_SECONDARY_VERITY].found = false;
1223
1224 } else if (m->partitions[PARTITION_USR_VERITY].found)
1225 return -EADDRNOTAVAIL; /* as above */
8c1be37e 1226
7cf66030 1227 else if (m->partitions[PARTITION_USR_SECONDARY].found) {
e0f9e7bd 1228
7cf66030
LP
1229 /* Upgrade secondary arch to primary */
1230 m->partitions[PARTITION_USR] = m->partitions[PARTITION_USR_SECONDARY];
1231 zero(m->partitions[PARTITION_USR_SECONDARY]);
1232 m->partitions[PARTITION_USR_VERITY] = m->partitions[PARTITION_USR_SECONDARY_VERITY];
1233 zero(m->partitions[PARTITION_USR_SECONDARY_VERITY]);
1234
1235 } else if (m->partitions[PARTITION_USR_SECONDARY_VERITY].found)
1236 return -EADDRNOTAVAIL; /* as above */
1237
4b5de5dd
LP
1238 else if ((flags & DISSECT_IMAGE_GENERIC_ROOT) &&
1239 (!verity || !verity->root_hash)) {
7cf66030
LP
1240
1241 /* OK, we found nothing usable, then check if there's a single generic one distro, and use
4b5de5dd
LP
1242 * that. If the root hash was set however, then we won't fall back to a generic node, because
1243 * the root hash decides. */
7cf66030
LP
1244
1245 /* If we didn't find a properly marked root partition, but we did find a single suitable
1246 * generic Linux partition, then use this as root partition, if the caller asked for it. */
1247 if (multiple_generic)
1248 return -ENOTUNIQ;
1249
4b5de5dd
LP
1250 /* If we didn't find a generic node, then we can't fix this up either */
1251 if (generic_node) {
1252 _cleanup_free_ char *o = NULL;
1253 const char *options;
8c1be37e 1254
f5215bc8 1255 options = mount_options_from_designator(mount_options, PARTITION_ROOT);
18d73705
LB
1256 if (options) {
1257 o = strdup(options);
1258 if (!o)
1259 return -ENOMEM;
1260 }
1261
1f8fb21c 1262 assert(generic_nr >= 0);
8c1be37e
LP
1263 m->partitions[PARTITION_ROOT] = (DissectedPartition) {
1264 .found = true,
1265 .rw = generic_rw,
1266 .partno = generic_nr,
1267 .architecture = _ARCHITECTURE_INVALID,
1cc6c93a 1268 .node = TAKE_PTR(generic_node),
be30ad41 1269 .uuid = generic_uuid,
18d73705 1270 .mount_options = TAKE_PTR(o),
8c1be37e 1271 };
e0f9e7bd 1272 }
8c1be37e
LP
1273 }
1274
4b5de5dd
LP
1275 /* Check if we have a root fs if we are told to do check. /usr alone is fine too, but only if appropriate flag for that is set too */
1276 if (FLAGS_SET(flags, DISSECT_IMAGE_REQUIRE_ROOT) &&
1277 !(m->partitions[PARTITION_ROOT].found || (m->partitions[PARTITION_USR].found && FLAGS_SET(flags, DISSECT_IMAGE_USR_NO_ROOT))))
1278 return -ENXIO;
1279
aee36b4e
LP
1280 /* Refuse if we found a verity partition for /usr but no matching file system partition */
1281 if (!m->partitions[PARTITION_USR].found && m->partitions[PARTITION_USR_VERITY].found)
1282 return -EADDRNOTAVAIL;
1283
1284 /* Combinations of verity /usr with verity-less root is OK, but the reverse is not */
c848516f 1285 if (m->partitions[PARTITION_ROOT_VERITY].found && m->partitions[PARTITION_USR].found && !m->partitions[PARTITION_USR_VERITY].found)
aee36b4e
LP
1286 return -EADDRNOTAVAIL;
1287
89e62e0b 1288 if (verity && verity->root_hash) {
aee36b4e
LP
1289 if (verity->designator < 0 || verity->designator == PARTITION_ROOT) {
1290 if (!m->partitions[PARTITION_ROOT_VERITY].found || !m->partitions[PARTITION_ROOT].found)
1291 return -EADDRNOTAVAIL;
1292
1293 /* If we found a verity setup, then the root partition is necessarily read-only. */
1294 m->partitions[PARTITION_ROOT].rw = false;
1295 m->verity = true;
1296 }
4623e8e6 1297
aee36b4e
LP
1298 if (verity->designator == PARTITION_USR) {
1299 if (!m->partitions[PARTITION_USR_VERITY].found || !m->partitions[PARTITION_USR].found)
1300 return -EADDRNOTAVAIL;
4623e8e6 1301
aee36b4e
LP
1302 m->partitions[PARTITION_USR].rw = false;
1303 m->verity = true;
1304 }
4623e8e6
LP
1305 }
1306
18b5886e
LP
1307 blkid_free_probe(b);
1308 b = NULL;
1309
8c1be37e 1310 /* Fill in file system types if we don't know them yet. */
569a0e42 1311 for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
18b5886e 1312 DissectedPartition *p = m->partitions + i;
8c1be37e 1313
18b5886e 1314 if (!p->found)
8c1be37e
LP
1315 continue;
1316
18b5886e
LP
1317 if (!p->fstype && p->node) {
1318 r = probe_filesystem(p->node, &p->fstype);
7cc84b2c 1319 if (r < 0 && r != -EUCLEAN)
18b5886e 1320 return r;
8c1be37e
LP
1321 }
1322
18b5886e
LP
1323 if (streq_ptr(p->fstype, "crypto_LUKS"))
1324 m->encrypted = true;
896f937f
LP
1325
1326 if (p->fstype && fstype_is_ro(p->fstype))
1327 p->rw = false;
8c1be37e
LP
1328 }
1329
1cc6c93a 1330 *ret = TAKE_PTR(m);
8c1be37e
LP
1331 return 0;
1332#else
1333 return -EOPNOTSUPP;
1334#endif
1335}
1336
1337DissectedImage* dissected_image_unref(DissectedImage *m) {
8c1be37e
LP
1338 if (!m)
1339 return NULL;
1340
08fe0a53
LP
1341 for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++)
1342 dissected_partition_done(m->partitions + i);
8c1be37e 1343
593fe6c0 1344 free(m->image_name);
3b925504
LP
1345 free(m->hostname);
1346 strv_free(m->machine_info);
1347 strv_free(m->os_release);
7718ac97 1348 strv_free(m->extension_release);
3b925504 1349
5fecf46d 1350 return mfree(m);
8c1be37e
LP
1351}
1352
18b5886e 1353static int is_loop_device(const char *path) {
553e15f2 1354 char s[SYS_BLOCK_PATH_MAX("/../loop/")];
18b5886e
LP
1355 struct stat st;
1356
1357 assert(path);
1358
1359 if (stat(path, &st) < 0)
1360 return -errno;
1361
1362 if (!S_ISBLK(st.st_mode))
1363 return -ENOTBLK;
1364
553e15f2 1365 xsprintf_sys_block_path(s, "/loop/", st.st_dev);
18b5886e
LP
1366 if (access(s, F_OK) < 0) {
1367 if (errno != ENOENT)
1368 return -errno;
1369
1370 /* The device itself isn't a loop device, but maybe it's a partition and its parent is? */
553e15f2 1371 xsprintf_sys_block_path(s, "/../loop/", st.st_dev);
18b5886e
LP
1372 if (access(s, F_OK) < 0)
1373 return errno == ENOENT ? false : -errno;
1374 }
1375
1376 return true;
1377}
1378
cf32c486
LP
1379static int run_fsck(const char *node, const char *fstype) {
1380 int r, exit_status;
1381 pid_t pid;
1382
1383 assert(node);
1384 assert(fstype);
1385
1386 r = fsck_exists(fstype);
1387 if (r < 0) {
1388 log_debug_errno(r, "Couldn't determine whether fsck for %s exists, proceeding anyway.", fstype);
1389 return 0;
1390 }
1391 if (r == 0) {
1392 log_debug("Not checking partition %s, as fsck for %s does not exist.", node, fstype);
1393 return 0;
1394 }
1395
1396 r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_NULL_STDIO, &pid);
1397 if (r < 0)
1398 return log_debug_errno(r, "Failed to fork off fsck: %m");
1399 if (r == 0) {
1400 /* Child */
1401 execl("/sbin/fsck", "/sbin/fsck", "-aT", node, NULL);
7e0ed2e9 1402 log_open();
cf32c486
LP
1403 log_debug_errno(errno, "Failed to execl() fsck: %m");
1404 _exit(FSCK_OPERATIONAL_ERROR);
1405 }
1406
1407 exit_status = wait_for_terminate_and_check("fsck", pid, 0);
1408 if (exit_status < 0)
1409 return log_debug_errno(exit_status, "Failed to fork off /sbin/fsck: %m");
1410
1411 if ((exit_status & ~FSCK_ERROR_CORRECTED) != FSCK_SUCCESS) {
1412 log_debug("fsck failed with exit status %i.", exit_status);
1413
1414 if ((exit_status & (FSCK_SYSTEM_SHOULD_REBOOT|FSCK_ERRORS_LEFT_UNCORRECTED)) != 0)
1415 return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN), "File system is corrupted, refusing.");
1416
1417 log_debug("Ignoring fsck error.");
1418 }
1419
1420 return 0;
1421}
1422
18b5886e
LP
1423static int mount_partition(
1424 DissectedPartition *m,
1425 const char *where,
1426 const char *directory,
2d3a5a73 1427 uid_t uid_shift,
18b5886e
LP
1428 DissectImageFlags flags) {
1429
2d3a5a73
LP
1430 _cleanup_free_ char *chased = NULL, *options = NULL;
1431 const char *p, *node, *fstype;
8c1be37e 1432 bool rw;
2eedfd2d 1433 int r;
8c1be37e
LP
1434
1435 assert(m);
1436 assert(where);
1437
4dc28665 1438 /* Use decrypted node and matching fstype if available, otherwise use the original device */
18b5886e 1439 node = m->decrypted_node ?: m->node;
4dc28665 1440 fstype = m->decrypted_node ? m->decrypted_fstype: m->fstype;
18b5886e 1441
4dc28665 1442 if (!m->found || !node)
8c1be37e 1443 return 0;
4dc28665
LP
1444 if (!fstype)
1445 return -EAFNOSUPPORT;
8c1be37e 1446
fa45d12c 1447 /* We are looking at an encrypted partition? This either means stacked encryption, or the caller didn't call dissected_image_decrypt() beforehand. Let's return a recognizable error for this case. */
4dc28665 1448 if (streq(fstype, "crypto_LUKS"))
fa45d12c 1449 return -EUNATCH;
18b5886e 1450
ef9c184d 1451 rw = m->rw && !(flags & DISSECT_IMAGE_MOUNT_READ_ONLY);
8c1be37e 1452
cf32c486
LP
1453 if (FLAGS_SET(flags, DISSECT_IMAGE_FSCK) && rw) {
1454 r = run_fsck(node, fstype);
1455 if (r < 0)
1456 return r;
1457 }
1458
2eedfd2d 1459 if (directory) {
334eb5b0
LP
1460 /* Automatically create missing mount points inside the image, if necessary. */
1461 r = mkdir_p_root(where, directory, uid_shift, (gid_t) uid_shift, 0755);
1462 if (r < 0 && r != -EROFS)
1463 return r;
1f0f82f1 1464
a5648b80 1465 r = chase_symlinks(directory, where, CHASE_PREFIX_ROOT, &chased, NULL);
2eedfd2d
LP
1466 if (r < 0)
1467 return r;
1468
1469 p = chased;
9842905e
LP
1470 } else {
1471 /* Create top-level mount if missing – but only if this is asked for. This won't modify the
1472 * image (as the branch above does) but the host hierarchy, and the created directory might
1473 * survive our mount in the host hierarchy hence. */
1474 if (FLAGS_SET(flags, DISSECT_IMAGE_MKDIR)) {
1475 r = mkdir_p(where, 0755);
1476 if (r < 0)
1477 return r;
1478 }
1479
8c1be37e 1480 p = where;
9842905e 1481 }
8c1be37e 1482
18b5886e 1483 /* If requested, turn on discard support. */
154d2269 1484 if (fstype_can_discard(fstype) &&
18b5886e 1485 ((flags & DISSECT_IMAGE_DISCARD) ||
3afda7c7 1486 ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node) > 0))) {
2d3a5a73
LP
1487 options = strdup("discard");
1488 if (!options)
1489 return -ENOMEM;
1490 }
1491
1492 if (uid_is_valid(uid_shift) && uid_shift != 0 && fstype_can_uid_gid(fstype)) {
1493 _cleanup_free_ char *uid_option = NULL;
1494
1495 if (asprintf(&uid_option, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
1496 return -ENOMEM;
1497
c2bc710b 1498 if (!strextend_with_separator(&options, ",", uid_option))
2d3a5a73
LP
1499 return -ENOMEM;
1500 }
8c1be37e 1501
18d73705 1502 if (!isempty(m->mount_options))
c2bc710b 1503 if (!strextend_with_separator(&options, ",", m->mount_options))
18d73705
LB
1504 return -ENOMEM;
1505
b620bf33
LP
1506 /* So, when you request MS_RDONLY from ext4, then this means nothing. It happily still writes to the
1507 * backing storage. What's worse, the BLKRO[GS]ET flag and (in case of loopback devices)
1508 * LO_FLAGS_READ_ONLY don't mean anything, they affect userspace accesses only, and write accesses
1509 * from the upper file system still get propagated through to the underlying file system,
1510 * unrestricted. To actually get ext4/xfs/btrfs to stop writing to the device we need to specify
1511 * "norecovery" as mount option, in addition to MS_RDONLY. Yes, this sucks, since it means we need to
1512 * carry a per file system table here.
1513 *
1514 * Note that this means that we might not be able to mount corrupted file systems as read-only
1515 * anymore (since in some cases the kernel implementations will refuse mounting when corrupted,
1516 * read-only and "norecovery" is specified). But I think for the case of automatically determined
1517 * mount options for loopback devices this is the right choice, since otherwise using the same
1518 * loopback file twice even in read-only mode, is going to fail badly sooner or later. The usecase of
1519 * making reuse of the immutable images "just work" is more relevant to us than having read-only
1520 * access that actually modifies stuff work on such image files. Or to say this differently: if
1521 * people want their file systems to be fixed up they should just open them in writable mode, where
1522 * all these problems don't exist. */
1523 if (!rw && STRPTR_IN_SET(fstype, "ext3", "ext4", "xfs", "btrfs"))
1524 if (!strextend_with_separator(&options, ",", "norecovery"))
1525 return -ENOMEM;
1526
511a8cfe 1527 r = mount_nofollow_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
d9223c07
LP
1528 if (r < 0)
1529 return r;
1530
1531 return 1;
8c1be37e
LP
1532}
1533
7cf66030
LP
1534static int mount_root_tmpfs(const char *where, uid_t uid_shift, DissectImageFlags flags) {
1535 _cleanup_free_ char *options = NULL;
1536 int r;
1537
1538 assert(where);
1539
1540 /* For images that contain /usr/ but no rootfs, let's mount rootfs as tmpfs */
1541
1542 if (FLAGS_SET(flags, DISSECT_IMAGE_MKDIR)) {
1543 r = mkdir_p(where, 0755);
1544 if (r < 0)
1545 return r;
1546 }
1547
1548 if (uid_is_valid(uid_shift)) {
1549 if (asprintf(&options, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
1550 return -ENOMEM;
1551 }
1552
1553 r = mount_nofollow_verbose(LOG_DEBUG, "rootfs", where, "tmpfs", MS_NODEV, options);
1554 if (r < 0)
1555 return r;
1556
1557 return 1;
1558}
1559
2d3a5a73 1560int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) {
1f0f82f1 1561 int r, xbootldr_mounted;
8c1be37e
LP
1562
1563 assert(m);
1564 assert(where);
1565
fa45d12c
LP
1566 /* Returns:
1567 *
1568 * -ENXIO → No root partition found
7718ac97 1569 * -EMEDIUMTYPE → DISSECT_IMAGE_VALIDATE_OS set but no os-release/extension-release file found
fa45d12c
LP
1570 * -EUNATCH → Encrypted partition found for which no dm-crypt was set up yet
1571 * -EUCLEAN → fsck for file system failed
1572 * -EBUSY → File system already mounted/used elsewhere (kernel)
4dc28665 1573 * -EAFNOSUPPORT → File system type not supported or not known
fa45d12c
LP
1574 */
1575
7cf66030
LP
1576 if (!(m->partitions[PARTITION_ROOT].found ||
1577 (m->partitions[PARTITION_USR].found && FLAGS_SET(flags, DISSECT_IMAGE_USR_NO_ROOT))))
1578 return -ENXIO; /* Require a root fs or at least a /usr/ fs (the latter is subject to a flag of its own) */
8c1be37e 1579
2d3a5a73 1580 if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0) {
7cf66030
LP
1581
1582 /* First mount the root fs. If there's none we use a tmpfs. */
1583 if (m->partitions[PARTITION_ROOT].found)
1584 r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, uid_shift, flags);
1585 else
1586 r = mount_root_tmpfs(where, uid_shift, flags);
2d3a5a73
LP
1587 if (r < 0)
1588 return r;
aee36b4e 1589
aee36b4e
LP
1590 /* For us mounting root always means mounting /usr as well */
1591 r = mount_partition(m->partitions + PARTITION_USR, where, "/usr", uid_shift, flags);
1592 if (r < 0)
1593 return r;
03bcb6d4
LP
1594
1595 if (flags & DISSECT_IMAGE_VALIDATE_OS) {
1596 r = path_is_os_tree(where);
1597 if (r < 0)
1598 return r;
7718ac97
LB
1599 if (r == 0) {
1600 r = path_is_extension_tree(where, m->image_name);
1601 if (r < 0)
1602 return r;
1603 if (r == 0)
1604 return -EMEDIUMTYPE;
1605 }
03bcb6d4 1606 }
2d3a5a73
LP
1607 }
1608
705727fd 1609 if (flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY)
2d3a5a73 1610 return 0;
8c1be37e 1611
2d3a5a73 1612 r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", uid_shift, flags);
8c1be37e
LP
1613 if (r < 0)
1614 return r;
1615
2d3a5a73 1616 r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", uid_shift, flags);
8c1be37e
LP
1617 if (r < 0)
1618 return r;
1619
d4dffb85
LP
1620 r = mount_partition(m->partitions + PARTITION_VAR, where, "/var", uid_shift, flags);
1621 if (r < 0)
1622 return r;
1623
1624 r = mount_partition(m->partitions + PARTITION_TMP, where, "/var/tmp", uid_shift, flags);
1625 if (r < 0)
1626 return r;
1627
1f0f82f1
LP
1628 xbootldr_mounted = mount_partition(m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, flags);
1629 if (xbootldr_mounted < 0)
1630 return xbootldr_mounted;
d9223c07 1631
8c1be37e 1632 if (m->partitions[PARTITION_ESP].found) {
1f0f82f1
LP
1633 int esp_done = false;
1634
d9223c07
LP
1635 /* Mount the ESP to /efi if it exists. If it doesn't exist, use /boot instead, but only if it
1636 * exists and is empty, and we didn't already mount the XBOOTLDR partition into it. */
8c1be37e 1637
a5648b80 1638 r = chase_symlinks("/efi", where, CHASE_PREFIX_ROOT, NULL, NULL);
1f0f82f1
LP
1639 if (r < 0) {
1640 if (r != -ENOENT)
d9223c07 1641 return r;
8c1be37e 1642
1f0f82f1
LP
1643 /* /efi doesn't exist. Let's see if /boot is suitable then */
1644
1645 if (!xbootldr_mounted) {
1646 _cleanup_free_ char *p = NULL;
2eedfd2d 1647
1f0f82f1
LP
1648 r = chase_symlinks("/boot", where, CHASE_PREFIX_ROOT, &p, NULL);
1649 if (r < 0) {
1650 if (r != -ENOENT)
1651 return r;
1652 } else if (dir_is_empty(p) > 0) {
1653 /* It exists and is an empty directory. Let's mount the ESP there. */
1654 r = mount_partition(m->partitions + PARTITION_ESP, where, "/boot", uid_shift, flags);
1655 if (r < 0)
1656 return r;
1657
1658 esp_done = true;
1659 }
2eedfd2d 1660 }
8c1be37e 1661 }
1f0f82f1
LP
1662
1663 if (!esp_done) {
1664 /* OK, let's mount the ESP now to /efi (possibly creating the dir if missing) */
1665
1666 r = mount_partition(m->partitions + PARTITION_ESP, where, "/efi", uid_shift, flags);
1667 if (r < 0)
1668 return r;
1669 }
8c1be37e
LP
1670 }
1671
1672 return 0;
1673}
1674
af187ab2
LP
1675int dissected_image_mount_and_warn(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) {
1676 int r;
1677
1678 assert(m);
1679 assert(where);
1680
1681 r = dissected_image_mount(m, where, uid_shift, flags);
1682 if (r == -ENXIO)
1683 return log_error_errno(r, "Not root file system found in image.");
1684 if (r == -EMEDIUMTYPE)
7718ac97 1685 return log_error_errno(r, "No suitable os-release/extension-release file in image found.");
af187ab2
LP
1686 if (r == -EUNATCH)
1687 return log_error_errno(r, "Encrypted file system discovered, but decryption not requested.");
1688 if (r == -EUCLEAN)
1689 return log_error_errno(r, "File system check on image failed.");
1690 if (r == -EBUSY)
1691 return log_error_errno(r, "File system already mounted elsewhere.");
4dc28665
LP
1692 if (r == -EAFNOSUPPORT)
1693 return log_error_errno(r, "File system type not supported or not known.");
af187ab2
LP
1694 if (r < 0)
1695 return log_error_errno(r, "Failed to mount image: %m");
1696
1697 return r;
1698}
1699
349cc4a5 1700#if HAVE_LIBCRYPTSETUP
18b5886e
LP
1701typedef struct DecryptedPartition {
1702 struct crypt_device *device;
1703 char *name;
1704 bool relinquished;
1705} DecryptedPartition;
1706
1707struct DecryptedImage {
1708 DecryptedPartition *decrypted;
1709 size_t n_decrypted;
1710 size_t n_allocated;
1711};
1712#endif
1713
1714DecryptedImage* decrypted_image_unref(DecryptedImage* d) {
349cc4a5 1715#if HAVE_LIBCRYPTSETUP
18b5886e
LP
1716 int r;
1717
1718 if (!d)
1719 return NULL;
1720
67f63ee5 1721 for (size_t i = 0; i < d->n_decrypted; i++) {
18b5886e
LP
1722 DecryptedPartition *p = d->decrypted + i;
1723
1724 if (p->device && p->name && !p->relinquished) {
0d12936d 1725 r = sym_crypt_deactivate_by_name(p->device, p->name, 0);
18b5886e
LP
1726 if (r < 0)
1727 log_debug_errno(r, "Failed to deactivate encrypted partition %s", p->name);
1728 }
1729
1730 if (p->device)
0d12936d 1731 sym_crypt_free(p->device);
18b5886e
LP
1732 free(p->name);
1733 }
1734
f91861e4 1735 free(d->decrypted);
18b5886e
LP
1736 free(d);
1737#endif
1738 return NULL;
1739}
1740
349cc4a5 1741#if HAVE_LIBCRYPTSETUP
4623e8e6
LP
1742
1743static int make_dm_name_and_node(const void *original_node, const char *suffix, char **ret_name, char **ret_node) {
1744 _cleanup_free_ char *name = NULL, *node = NULL;
1745 const char *base;
1746
1747 assert(original_node);
1748 assert(suffix);
1749 assert(ret_name);
1750 assert(ret_node);
1751
1752 base = strrchr(original_node, '/');
1753 if (!base)
ac1f3ad0
LB
1754 base = original_node;
1755 else
1756 base++;
4623e8e6
LP
1757 if (isempty(base))
1758 return -EINVAL;
1759
1760 name = strjoin(base, suffix);
1761 if (!name)
1762 return -ENOMEM;
1763 if (!filename_is_valid(name))
1764 return -EINVAL;
1765
0d12936d 1766 node = path_join(sym_crypt_get_dir(), name);
4623e8e6
LP
1767 if (!node)
1768 return -ENOMEM;
1769
1cc6c93a
YW
1770 *ret_name = TAKE_PTR(name);
1771 *ret_node = TAKE_PTR(node);
4623e8e6 1772
4623e8e6
LP
1773 return 0;
1774}
1775
18b5886e
LP
1776static int decrypt_partition(
1777 DissectedPartition *m,
1778 const char *passphrase,
1779 DissectImageFlags flags,
1780 DecryptedImage *d) {
1781
1782 _cleanup_free_ char *node = NULL, *name = NULL;
0d12936d 1783 _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
18b5886e
LP
1784 int r;
1785
1786 assert(m);
1787 assert(d);
1788
1789 if (!m->found || !m->node || !m->fstype)
1790 return 0;
1791
1792 if (!streq(m->fstype, "crypto_LUKS"))
1793 return 0;
1794
bdd73ac5
ZJS
1795 if (!passphrase)
1796 return -ENOKEY;
1797
0d12936d
LP
1798 r = dlopen_cryptsetup();
1799 if (r < 0)
1800 return r;
1801
4623e8e6
LP
1802 r = make_dm_name_and_node(m->node, "-decrypted", &name, &node);
1803 if (r < 0)
1804 return r;
18b5886e
LP
1805
1806 if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
1807 return -ENOMEM;
1808
0d12936d 1809 r = sym_crypt_init(&cd, m->node);
18b5886e 1810 if (r < 0)
715cbb81 1811 return log_debug_errno(r, "Failed to initialize dm-crypt: %m");
18b5886e 1812
efc3b12f 1813 cryptsetup_enable_logging(cd);
1887032f 1814
0d12936d 1815 r = sym_crypt_load(cd, CRYPT_LUKS, NULL);
294bd454
ZJS
1816 if (r < 0)
1817 return log_debug_errno(r, "Failed to load LUKS metadata: %m");
18b5886e 1818
0d12936d 1819 r = sym_crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase),
ef9c184d 1820 ((flags & DISSECT_IMAGE_DEVICE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
0d12936d 1821 ((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
294bd454 1822 if (r < 0) {
715cbb81 1823 log_debug_errno(r, "Failed to activate LUKS device: %m");
294bd454 1824 return r == -EPERM ? -EKEYREJECTED : r;
18b5886e 1825 }
18b5886e 1826
94344385
LP
1827 d->decrypted[d->n_decrypted++] = (DecryptedPartition) {
1828 .name = TAKE_PTR(name),
1829 .device = TAKE_PTR(cd),
1830 };
18b5886e 1831
1cc6c93a 1832 m->decrypted_node = TAKE_PTR(node);
18b5886e
LP
1833
1834 return 0;
4623e8e6
LP
1835}
1836
89e62e0b
LP
1837static int verity_can_reuse(
1838 const VeritySettings *verity,
1839 const char *name,
1840 struct crypt_device **ret_cd) {
1841
ac1f3ad0
LB
1842 /* If the same volume was already open, check that the root hashes match, and reuse it if they do */
1843 _cleanup_free_ char *root_hash_existing = NULL;
0d12936d 1844 _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
ac1f3ad0 1845 struct crypt_params_verity crypt_params = {};
89e62e0b 1846 size_t root_hash_existing_size;
ac1f3ad0
LB
1847 int r;
1848
89e62e0b
LP
1849 assert(verity);
1850 assert(name);
ac1f3ad0
LB
1851 assert(ret_cd);
1852
0d12936d 1853 r = sym_crypt_init_by_name(&cd, name);
ac1f3ad0
LB
1854 if (r < 0)
1855 return log_debug_errno(r, "Error opening verity device, crypt_init_by_name failed: %m");
0d12936d
LP
1856
1857 r = sym_crypt_get_verity_info(cd, &crypt_params);
ac1f3ad0
LB
1858 if (r < 0)
1859 return log_debug_errno(r, "Error opening verity device, crypt_get_verity_info failed: %m");
0d12936d 1860
89e62e0b
LP
1861 root_hash_existing_size = verity->root_hash_size;
1862 root_hash_existing = malloc0(root_hash_existing_size);
ac1f3ad0
LB
1863 if (!root_hash_existing)
1864 return -ENOMEM;
0d12936d
LP
1865
1866 r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, root_hash_existing, &root_hash_existing_size, NULL, 0);
ac1f3ad0
LB
1867 if (r < 0)
1868 return log_debug_errno(r, "Error opening verity device, crypt_volume_key_get failed: %m");
89e62e0b
LP
1869 if (verity->root_hash_size != root_hash_existing_size ||
1870 memcmp(root_hash_existing, verity->root_hash, verity->root_hash_size) != 0)
ac1f3ad0 1871 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Error opening verity device, it already exists but root hashes are different.");
89e62e0b 1872
ac1f3ad0 1873#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
89e62e0b
LP
1874 /* Ensure that, if signatures are supported, we only reuse the device if the previous mount used the
1875 * same settings, so that a previous unsigned mount will not be reused if the user asks to use
28423d9a 1876 * signing for the new one, and vice versa. */
89e62e0b 1877 if (!!verity->root_hash_sig != !!(crypt_params.flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE))
ac1f3ad0
LB
1878 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Error opening verity device, it already exists but signature settings are not the same.");
1879#endif
1880
1881 *ret_cd = TAKE_PTR(cd);
1882 return 0;
1883}
1884
75db809a 1885static inline char* dm_deferred_remove_clean(char *name) {
ac1f3ad0 1886 if (!name)
75db809a 1887 return NULL;
0d12936d
LP
1888
1889 (void) sym_crypt_deactivate_by_name(NULL, name, CRYPT_DEACTIVATE_DEFERRED);
75db809a 1890 return mfree(name);
ac1f3ad0
LB
1891}
1892DEFINE_TRIVIAL_CLEANUP_FUNC(char *, dm_deferred_remove_clean);
1893
4623e8e6 1894static int verity_partition(
aee36b4e 1895 PartitionDesignator designator,
4623e8e6
LP
1896 DissectedPartition *m,
1897 DissectedPartition *v,
89e62e0b 1898 const VeritySettings *verity,
4623e8e6
LP
1899 DissectImageFlags flags,
1900 DecryptedImage *d) {
1901
0d12936d 1902 _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
ac1f3ad0 1903 _cleanup_(dm_deferred_remove_cleanp) char *restore_deferred_remove = NULL;
89e62e0b 1904 _cleanup_free_ char *node = NULL, *name = NULL;
4623e8e6
LP
1905 int r;
1906
1907 assert(m);
89e62e0b 1908 assert(v || (verity && verity->data_path));
4623e8e6 1909
89e62e0b 1910 if (!verity || !verity->root_hash)
4623e8e6 1911 return 0;
aee36b4e
LP
1912 if (!((verity->designator < 0 && designator == PARTITION_ROOT) ||
1913 (verity->designator == designator)))
1914 return 0;
4623e8e6
LP
1915
1916 if (!m->found || !m->node || !m->fstype)
1917 return 0;
89e62e0b 1918 if (!verity->data_path) {
e7cbe5cb
LB
1919 if (!v->found || !v->node || !v->fstype)
1920 return 0;
4623e8e6 1921
e7cbe5cb
LB
1922 if (!streq(v->fstype, "DM_verity_hash"))
1923 return 0;
1924 }
4623e8e6 1925
0d12936d
LP
1926 r = dlopen_cryptsetup();
1927 if (r < 0)
1928 return r;
1929
ac1f3ad0
LB
1930 if (FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)) {
1931 /* Use the roothash, which is unique per volume, as the device node name, so that it can be reused */
1932 _cleanup_free_ char *root_hash_encoded = NULL;
0d12936d 1933
89e62e0b 1934 root_hash_encoded = hexmem(verity->root_hash, verity->root_hash_size);
ac1f3ad0
LB
1935 if (!root_hash_encoded)
1936 return -ENOMEM;
aee36b4e 1937
ac1f3ad0
LB
1938 r = make_dm_name_and_node(root_hash_encoded, "-verity", &name, &node);
1939 } else
1940 r = make_dm_name_and_node(m->node, "-verity", &name, &node);
4623e8e6
LP
1941 if (r < 0)
1942 return r;
1943
89e62e0b 1944 r = sym_crypt_init(&cd, verity->data_path ?: v->node);
4623e8e6
LP
1945 if (r < 0)
1946 return r;
1947
efc3b12f 1948 cryptsetup_enable_logging(cd);
1887032f 1949
0d12936d 1950 r = sym_crypt_load(cd, CRYPT_VERITY, NULL);
4623e8e6 1951 if (r < 0)
294bd454 1952 return r;
4623e8e6 1953
0d12936d 1954 r = sym_crypt_set_data_device(cd, m->node);
4623e8e6 1955 if (r < 0)
294bd454 1956 return r;
4623e8e6 1957
ac1f3ad0
LB
1958 if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
1959 return -ENOMEM;
1960
1961 /* If activating fails because the device already exists, check the metadata and reuse it if it matches.
1962 * In case of ENODEV/ENOENT, which can happen if another process is activating at the exact same time,
1963 * retry a few times before giving up. */
1964 for (unsigned i = 0; i < N_DEVICE_NODE_LIST_ATTEMPTS; i++) {
89e62e0b 1965 if (verity->root_hash_sig) {
c2923fdc 1966#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
89e62e0b
LP
1967 r = sym_crypt_activate_by_signed_key(
1968 cd,
1969 name,
1970 verity->root_hash,
1971 verity->root_hash_size,
1972 verity->root_hash_sig,
1973 verity->root_hash_sig_size,
1974 CRYPT_ACTIVATE_READONLY);
ac1f3ad0 1975#else
22043172
LP
1976 r = log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1977 "Activation of verity device with signature requested, but not supported by %s due to missing crypt_activate_by_signed_key().", program_invocation_short_name);
ac1f3ad0
LB
1978#endif
1979 } else
89e62e0b
LP
1980 r = sym_crypt_activate_by_volume_key(
1981 cd,
1982 name,
1983 verity->root_hash,
1984 verity->root_hash_size,
1985 CRYPT_ACTIVATE_READONLY);
ac1f3ad0
LB
1986 /* libdevmapper can return EINVAL when the device is already in the activation stage.
1987 * There's no way to distinguish this situation from a genuine error due to invalid
2aed63f4 1988 * parameters, so immediately fall back to activating the device with a unique name.
89e62e0b
LP
1989 * Improvements in libcrypsetup can ensure this never happens:
1990 * https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/96 */
ac1f3ad0 1991 if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
aee36b4e 1992 return verity_partition(designator, m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
9ecb5c10 1993 if (!IN_SET(r,
22043172 1994 0, /* Success */
9ecb5c10 1995 -EEXIST, /* Volume is already open and ready to be used */
22043172
LP
1996 -EBUSY, /* Volume is being opened but not ready, crypt_init_by_name can fetch details */
1997 -ENODEV /* Volume is being opened but not ready, crypt_init_by_name would fail, try to open again */))
ac1f3ad0 1998 return r;
9ecb5c10 1999 if (IN_SET(r, -EEXIST, -EBUSY)) {
ac1f3ad0 2000 struct crypt_device *existing_cd = NULL;
c2923fdc 2001
ac1f3ad0
LB
2002 if (!restore_deferred_remove){
2003 /* To avoid races, disable automatic removal on umount while setting up the new device. Restore it on failure. */
2004 r = dm_deferred_remove_cancel(name);
9ecb5c10
LB
2005 /* If activation returns EBUSY there might be no deferred removal to cancel, that's fine */
2006 if (r < 0 && r != -ENXIO)
ac1f3ad0 2007 return log_debug_errno(r, "Disabling automated deferred removal for verity device %s failed: %m", node);
9ecb5c10
LB
2008 if (r == 0) {
2009 restore_deferred_remove = strdup(name);
2010 if (!restore_deferred_remove)
2011 return -ENOMEM;
2012 }
ac1f3ad0 2013 }
c2923fdc 2014
89e62e0b 2015 r = verity_can_reuse(verity, name, &existing_cd);
ac1f3ad0
LB
2016 /* Same as above, -EINVAL can randomly happen when it actually means -EEXIST */
2017 if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
aee36b4e 2018 return verity_partition(designator, m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
9ecb5c10 2019 if (!IN_SET(r, 0, -ENODEV, -ENOENT, -EBUSY))
ac1f3ad0
LB
2020 return log_debug_errno(r, "Checking whether existing verity device %s can be reused failed: %m", node);
2021 if (r == 0) {
c419b6f0
LB
2022 /* devmapper might say that the device exists, but the devlink might not yet have been
2023 * created. Check and wait for the udev event in that case. */
9e3d9067 2024 r = device_wait_for_devlink(node, "block", usec_add(now(CLOCK_MONOTONIC), 100 * USEC_PER_MSEC), NULL);
c419b6f0
LB
2025 /* Fallback to activation with a unique device if it's taking too long */
2026 if (r == -ETIMEDOUT)
2027 break;
2028 if (r < 0)
2029 return r;
2030
ac1f3ad0 2031 if (cd)
0d12936d 2032 sym_crypt_free(cd);
ac1f3ad0
LB
2033 cd = existing_cd;
2034 }
c2923fdc 2035 }
ac1f3ad0
LB
2036 if (r == 0)
2037 break;
ecab4c47
LB
2038
2039 /* Device is being opened by another process, but it has not finished yet, yield for 2ms */
2040 (void) usleep(2 * USEC_PER_MSEC);
ac1f3ad0
LB
2041 }
2042
ac1f3ad0
LB
2043 /* An existing verity device was reported by libcryptsetup/libdevmapper, but we can't use it at this time.
2044 * Fall back to activating it with a unique device name. */
2045 if (r != 0 && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
aee36b4e 2046 return verity_partition(designator, m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
ac1f3ad0
LB
2047
2048 /* Everything looks good and we'll be able to mount the device, so deferred remove will be re-enabled at that point. */
2049 restore_deferred_remove = mfree(restore_deferred_remove);
4623e8e6 2050
94344385
LP
2051 d->decrypted[d->n_decrypted++] = (DecryptedPartition) {
2052 .name = TAKE_PTR(name),
2053 .device = TAKE_PTR(cd),
2054 };
4623e8e6 2055
1cc6c93a 2056 m->decrypted_node = TAKE_PTR(node);
4623e8e6
LP
2057
2058 return 0;
18b5886e
LP
2059}
2060#endif
2061
2062int dissected_image_decrypt(
2063 DissectedImage *m,
2064 const char *passphrase,
89e62e0b 2065 const VeritySettings *verity,
18b5886e
LP
2066 DissectImageFlags flags,
2067 DecryptedImage **ret) {
2068
349cc4a5 2069#if HAVE_LIBCRYPTSETUP
49b5b3b4 2070 _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
18b5886e
LP
2071 int r;
2072#endif
2073
2074 assert(m);
89e62e0b 2075 assert(!verity || verity->root_hash || verity->root_hash_size == 0);
18b5886e
LP
2076
2077 /* Returns:
2078 *
2079 * = 0 → There was nothing to decrypt
2080 * > 0 → Decrypted successfully
d1c536f5 2081 * -ENOKEY → There's something to decrypt but no key was supplied
18b5886e
LP
2082 * -EKEYREJECTED → Passed key was not correct
2083 */
2084
89e62e0b 2085 if (verity && verity->root_hash && verity->root_hash_size < sizeof(sd_id128_t))
4623e8e6
LP
2086 return -EINVAL;
2087
2088 if (!m->encrypted && !m->verity) {
18b5886e
LP
2089 *ret = NULL;
2090 return 0;
2091 }
2092
349cc4a5 2093#if HAVE_LIBCRYPTSETUP
18b5886e
LP
2094 d = new0(DecryptedImage, 1);
2095 if (!d)
2096 return -ENOMEM;
2097
569a0e42 2098 for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
18b5886e 2099 DissectedPartition *p = m->partitions + i;
22043172 2100 PartitionDesignator k;
18b5886e
LP
2101
2102 if (!p->found)
2103 continue;
2104
2105 r = decrypt_partition(p, passphrase, flags, d);
2106 if (r < 0)
2107 return r;
2108
4623e8e6
LP
2109 k = PARTITION_VERITY_OF(i);
2110 if (k >= 0) {
aee36b4e 2111 r = verity_partition(i, p, m->partitions + k, verity, flags | DISSECT_IMAGE_VERITY_SHARE, d);
4623e8e6
LP
2112 if (r < 0)
2113 return r;
2114 }
2115
18b5886e
LP
2116 if (!p->decrypted_fstype && p->decrypted_node) {
2117 r = probe_filesystem(p->decrypted_node, &p->decrypted_fstype);
7cc84b2c 2118 if (r < 0 && r != -EUCLEAN)
18b5886e
LP
2119 return r;
2120 }
2121 }
2122
1cc6c93a 2123 *ret = TAKE_PTR(d);
18b5886e
LP
2124
2125 return 1;
2126#else
2127 return -EOPNOTSUPP;
2128#endif
2129}
2130
2131int dissected_image_decrypt_interactively(
2132 DissectedImage *m,
2133 const char *passphrase,
89e62e0b 2134 const VeritySettings *verity,
18b5886e
LP
2135 DissectImageFlags flags,
2136 DecryptedImage **ret) {
2137
2138 _cleanup_strv_free_erase_ char **z = NULL;
2139 int n = 3, r;
2140
2141 if (passphrase)
2142 n--;
2143
2144 for (;;) {
89e62e0b 2145 r = dissected_image_decrypt(m, passphrase, verity, flags, ret);
18b5886e
LP
2146 if (r >= 0)
2147 return r;
2148 if (r == -EKEYREJECTED)
2149 log_error_errno(r, "Incorrect passphrase, try again!");
fc95c359
YW
2150 else if (r != -ENOKEY)
2151 return log_error_errno(r, "Failed to decrypt image: %m");
18b5886e 2152
baaa35ad
ZJS
2153 if (--n < 0)
2154 return log_error_errno(SYNTHETIC_ERRNO(EKEYREJECTED),
2155 "Too many retries.");
18b5886e
LP
2156
2157 z = strv_free(z);
2158
8806bb4b 2159 r = ask_password_auto("Please enter image passphrase:", NULL, "dissect", "dissect", "dissect.passphrase", USEC_INFINITY, 0, &z);
18b5886e
LP
2160 if (r < 0)
2161 return log_error_errno(r, "Failed to query for passphrase: %m");
2162
2163 passphrase = z[0];
2164 }
2165}
2166
18b5886e 2167int decrypted_image_relinquish(DecryptedImage *d) {
18b5886e
LP
2168 assert(d);
2169
67f63ee5
ZJS
2170 /* Turns on automatic removal after the last use ended for all DM devices of this image, and sets a
2171 * boolean so that we don't clean it up ourselves either anymore */
18b5886e 2172
349cc4a5 2173#if HAVE_LIBCRYPTSETUP
67f63ee5
ZJS
2174 int r;
2175
2176 for (size_t i = 0; i < d->n_decrypted; i++) {
18b5886e
LP
2177 DecryptedPartition *p = d->decrypted + i;
2178
2179 if (p->relinquished)
2180 continue;
2181
0d12936d 2182 r = sym_crypt_deactivate_by_name(NULL, p->name, CRYPT_DEACTIVATE_DEFERRED);
18b5886e
LP
2183 if (r < 0)
2184 return log_debug_errno(r, "Failed to mark %s for auto-removal: %m", p->name);
2185
2186 p->relinquished = true;
2187 }
2188#endif
2189
2190 return 0;
2191}
2192
89e62e0b
LP
2193static char *build_auxiliary_path(const char *image, const char *suffix) {
2194 const char *e;
2195 char *n;
2196
2197 assert(image);
2198 assert(suffix);
2199
2200 e = endswith(image, ".raw");
2201 if (!e)
2202 return strjoin(e, suffix);
2203
2204 n = new(char, e - image + strlen(suffix) + 1);
2205 if (!n)
2206 return NULL;
2207
2208 strcpy(mempcpy(n, image, e - image), suffix);
2209 return n;
2210}
2211
2212void verity_settings_done(VeritySettings *v) {
2213 assert(v);
2214
2215 v->root_hash = mfree(v->root_hash);
2216 v->root_hash_size = 0;
2217
2218 v->root_hash_sig = mfree(v->root_hash_sig);
2219 v->root_hash_sig_size = 0;
2220
2221 v->data_path = mfree(v->data_path);
2222}
2223
2224int verity_settings_load(
2225 VeritySettings *verity,
f5ea63a5
LP
2226 const char *image,
2227 const char *root_hash_path,
89e62e0b
LP
2228 const char *root_hash_sig_path) {
2229
2230 _cleanup_free_ void *root_hash = NULL, *root_hash_sig = NULL;
2231 size_t root_hash_size = 0, root_hash_sig_size = 0;
2232 _cleanup_free_ char *verity_data_path = NULL;
aee36b4e 2233 PartitionDesignator designator;
78ebe980
LP
2234 int r;
2235
89e62e0b 2236 assert(verity);
78ebe980 2237 assert(image);
aee36b4e 2238 assert(verity->designator < 0 || IN_SET(verity->designator, PARTITION_ROOT, PARTITION_USR));
78ebe980 2239
89e62e0b
LP
2240 /* If we are asked to load the root hash for a device node, exit early */
2241 if (is_device_path(image))
78ebe980 2242 return 0;
78ebe980 2243
aee36b4e
LP
2244 designator = verity->designator;
2245
89e62e0b 2246 /* We only fill in what isn't already filled in */
c2923fdc 2247
89e62e0b 2248 if (!verity->root_hash) {
e7cbe5cb 2249 _cleanup_free_ char *text = NULL;
e7cbe5cb 2250
0389f4fa 2251 if (root_hash_path) {
aee36b4e 2252 /* If explicitly specified it takes precedence */
0389f4fa
LB
2253 r = read_one_line_file(root_hash_path, &text);
2254 if (r < 0)
e7cbe5cb 2255 return r;
aee36b4e
LP
2256
2257 if (designator < 0)
2258 designator = PARTITION_ROOT;
0389f4fa 2259 } else {
aee36b4e
LP
2260 /* Otherwise look for xattr and separate file, and first for the data for root and if
2261 * that doesn't exist for /usr */
0389f4fa 2262
aee36b4e
LP
2263 if (designator < 0 || designator == PARTITION_ROOT) {
2264 r = getxattr_malloc(image, "user.verity.roothash", &text, true);
2265 if (r < 0) {
2266 _cleanup_free_ char *p = NULL;
78ebe980 2267
aee36b4e
LP
2268 if (!IN_SET(r, -ENODATA, -ENOENT) && !ERRNO_IS_NOT_SUPPORTED(r))
2269 return r;
e7cbe5cb 2270
aee36b4e
LP
2271 p = build_auxiliary_path(image, ".roothash");
2272 if (!p)
2273 return -ENOMEM;
2274
2275 r = read_one_line_file(p, &text);
2276 if (r < 0 && r != -ENOENT)
2277 return r;
2278 }
2279
2280 if (text)
2281 designator = PARTITION_ROOT;
2282 }
2283
2284 if (!text && (designator < 0 || designator == PARTITION_USR)) {
2285 /* So in the "roothash" xattr/file name above the "root" of course primarily
2286 * refers to the root of the Verity Merkle tree. But coincidentally it also
2287 * is the hash for the *root* file system, i.e. the "root" neatly refers to
2288 * two distinct concepts called "root". Taking benefit of this happy
2289 * coincidence we call the file with the root hash for the /usr/ file system
2290 * `usrhash`, because `usrroothash` or `rootusrhash` would just be too
2291 * confusing. We thus drop the reference to the root of the Merkle tree, and
2292 * just indicate which file system it's about. */
2293 r = getxattr_malloc(image, "user.verity.usrhash", &text, true);
2294 if (r < 0) {
2295 _cleanup_free_ char *p = NULL;
2296
2297 if (!IN_SET(r, -ENODATA, -ENOENT) && !ERRNO_IS_NOT_SUPPORTED(r))
2298 return r;
2299
2300 p = build_auxiliary_path(image, ".usrhash");
2301 if (!p)
2302 return -ENOMEM;
2303
2304 r = read_one_line_file(p, &text);
2305 if (r < 0 && r != -ENOENT)
2306 return r;
2307 }
2308
2309 if (text)
2310 designator = PARTITION_USR;
0389f4fa 2311 }
e7cbe5cb
LB
2312 }
2313
2314 if (text) {
89e62e0b 2315 r = unhexmem(text, strlen(text), &root_hash, &root_hash_size);
e7cbe5cb
LB
2316 if (r < 0)
2317 return r;
89e62e0b 2318 if (root_hash_size < sizeof(sd_id128_t))
e7cbe5cb
LB
2319 return -EINVAL;
2320 }
2321 }
2322
90f98986 2323 if ((root_hash || verity->root_hash) && !verity->root_hash_sig) {
aee36b4e 2324 if (root_hash_sig_path) {
ae9cf30b 2325 r = read_full_file(root_hash_sig_path, (char**) &root_hash_sig, &root_hash_sig_size);
aee36b4e
LP
2326 if (r < 0 && r != -ENOENT)
2327 return r;
2328
2329 if (designator < 0)
2330 designator = PARTITION_ROOT;
2331 } else {
2332 if (designator < 0 || designator == PARTITION_ROOT) {
2333 _cleanup_free_ char *p = NULL;
2334
2335 /* Follow naming convention recommended by the relevant RFC:
2336 * https://tools.ietf.org/html/rfc5751#section-3.2.1 */
2337 p = build_auxiliary_path(image, ".roothash.p7s");
2338 if (!p)
2339 return -ENOMEM;
89e62e0b 2340
ae9cf30b 2341 r = read_full_file(p, (char**) &root_hash_sig, &root_hash_sig_size);
aee36b4e
LP
2342 if (r < 0 && r != -ENOENT)
2343 return r;
2344 if (r >= 0)
2345 designator = PARTITION_ROOT;
2346 }
2347
2348 if (!root_hash_sig && (designator < 0 || designator == PARTITION_USR)) {
2349 _cleanup_free_ char *p = NULL;
2350
2351 p = build_auxiliary_path(image, ".usrhash.p7s");
2352 if (!p)
2353 return -ENOMEM;
89e62e0b 2354
ae9cf30b 2355 r = read_full_file(p, (char**) &root_hash_sig, &root_hash_sig_size);
aee36b4e
LP
2356 if (r < 0 && r != -ENOENT)
2357 return r;
2358 if (r >= 0)
2359 designator = PARTITION_USR;
2360 }
89e62e0b
LP
2361 }
2362
aee36b4e 2363 if (root_hash_sig && root_hash_sig_size == 0) /* refuse empty size signatures */
89e62e0b
LP
2364 return -EINVAL;
2365 }
2366
2367 if (!verity->data_path) {
2368 _cleanup_free_ char *p = NULL;
2369
2370 p = build_auxiliary_path(image, ".verity");
2371 if (!p)
2372 return -ENOMEM;
2373
2374 if (access(p, F_OK) < 0) {
2375 if (errno != ENOENT)
2376 return -errno;
2377 } else
2378 verity_data_path = TAKE_PTR(p);
2379 }
2380
2381 if (root_hash) {
2382 verity->root_hash = TAKE_PTR(root_hash);
2383 verity->root_hash_size = root_hash_size;
2384 }
2385
2386 if (root_hash_sig) {
2387 verity->root_hash_sig = TAKE_PTR(root_hash_sig);
2388 verity->root_hash_sig_size = root_hash_sig_size;
e7cbe5cb 2389 }
89e62e0b
LP
2390
2391 if (verity_data_path)
2392 verity->data_path = TAKE_PTR(verity_data_path);
78ebe980 2393
aee36b4e
LP
2394 if (verity->designator < 0)
2395 verity->designator = designator;
2396
78ebe980
LP
2397 return 1;
2398}
2399
3b925504
LP
2400int dissected_image_acquire_metadata(DissectedImage *m) {
2401
2402 enum {
2403 META_HOSTNAME,
2404 META_MACHINE_ID,
2405 META_MACHINE_INFO,
2406 META_OS_RELEASE,
7718ac97 2407 META_EXTENSION_RELEASE,
3b925504
LP
2408 _META_MAX,
2409 };
2410
7718ac97
LB
2411 static const char *paths[_META_MAX] = {
2412 [META_HOSTNAME] = "/etc/hostname\0",
2413 [META_MACHINE_ID] = "/etc/machine-id\0",
2414 [META_MACHINE_INFO] = "/etc/machine-info\0",
7533a33b
LP
2415 [META_OS_RELEASE] = "/etc/os-release\0"
2416 "/usr/lib/os-release\0",
7718ac97 2417 [META_EXTENSION_RELEASE] = NULL,
3b925504
LP
2418 };
2419
7718ac97 2420 _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL, **extension_release = NULL;
af8219d5 2421 _cleanup_close_pair_ int error_pipe[2] = { -1, -1 };
3b925504
LP
2422 _cleanup_(rmdir_and_freep) char *t = NULL;
2423 _cleanup_(sigkill_waitp) pid_t child = 0;
2424 sd_id128_t machine_id = SD_ID128_NULL;
2425 _cleanup_free_ char *hostname = NULL;
67f63ee5 2426 unsigned n_meta_initialized = 0;
af8219d5
LP
2427 int fds[2 * _META_MAX], r, v;
2428 ssize_t n;
3b925504
LP
2429
2430 BLOCK_SIGNALS(SIGCHLD);
2431
2432 assert(m);
2433
7718ac97
LB
2434 /* As per the os-release spec, if the image is an extension it will have a file
2435 * named after the image name in extension-release.d/ */
4f67a5d9 2436 if (m->image_name) {
794a579f
LP
2437 char *ext;
2438
2439 ext = strjoina("/usr/lib/extension-release.d/extension-release.", m->image_name, "0");
4f67a5d9
LB
2440 ext[strlen(ext) - 1] = '\0'; /* Extra \0 for NULSTR_FOREACH using placeholder from above */
2441 paths[META_EXTENSION_RELEASE] = ext;
2442 } else
7718ac97
LB
2443 log_debug("No image name available, will skip extension-release metadata");
2444
2445 for (; n_meta_initialized < _META_MAX; n_meta_initialized ++) {
d9119c00
LP
2446 if (!paths[n_meta_initialized]) {
2447 fds[2*n_meta_initialized] = fds[2*n_meta_initialized+1] = -1;
7718ac97 2448 continue;
d9119c00
LP
2449 }
2450
3b925504
LP
2451 if (pipe2(fds + 2*n_meta_initialized, O_CLOEXEC) < 0) {
2452 r = -errno;
2453 goto finish;
2454 }
7718ac97 2455 }
3b925504
LP
2456
2457 r = mkdtemp_malloc("/tmp/dissect-XXXXXX", &t);
2458 if (r < 0)
2459 goto finish;
2460
af8219d5
LP
2461 if (pipe2(error_pipe, O_CLOEXEC) < 0) {
2462 r = -errno;
2463 goto finish;
2464 }
2465
e2047ba9 2466 r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE, &child);
be39f6ee 2467 if (r < 0)
3b925504 2468 goto finish;
be39f6ee 2469 if (r == 0) {
af8219d5
LP
2470 error_pipe[0] = safe_close(error_pipe[0]);
2471
7cf66030
LP
2472 r = dissected_image_mount(
2473 m,
2474 t,
2475 UID_INVALID,
2476 DISSECT_IMAGE_READ_ONLY|
2477 DISSECT_IMAGE_MOUNT_ROOT_ONLY|
2478 DISSECT_IMAGE_VALIDATE_OS|
2479 DISSECT_IMAGE_USR_NO_ROOT);
429d4e41 2480 if (r < 0) {
af8219d5
LP
2481 /* Let parent know the error */
2482 (void) write(error_pipe[1], &r, sizeof(r));
2483
429d4e41 2484 log_debug_errno(r, "Failed to mount dissected image: %m");
3b925504 2485 _exit(EXIT_FAILURE);
429d4e41 2486 }
3b925504 2487
67f63ee5 2488 for (unsigned k = 0; k < _META_MAX; k++) {
37e44c3f 2489 _cleanup_close_ int fd = -ENOENT;
3b925504
LP
2490 const char *p;
2491
7718ac97
LB
2492 if (!paths[k])
2493 continue;
2494
3b925504
LP
2495 fds[2*k] = safe_close(fds[2*k]);
2496
2497 NULSTR_FOREACH(p, paths[k]) {
36952d19 2498 fd = chase_symlinks_and_open(p, t, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
3b925504
LP
2499 if (fd >= 0)
2500 break;
2501 }
36952d19
LP
2502 if (fd < 0) {
2503 log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]);
37e44c3f 2504 fds[2*k+1] = safe_close(fds[2*k+1]);
3b925504 2505 continue;
36952d19 2506 }
3b925504 2507
f5fbe71d 2508 r = copy_bytes(fd, fds[2*k+1], UINT64_MAX, 0);
af8219d5
LP
2509 if (r < 0) {
2510 (void) write(error_pipe[1], &r, sizeof(r));
3b925504 2511 _exit(EXIT_FAILURE);
af8219d5 2512 }
3b925504
LP
2513
2514 fds[2*k+1] = safe_close(fds[2*k+1]);
2515 }
2516
2517 _exit(EXIT_SUCCESS);
2518 }
2519
af8219d5
LP
2520 error_pipe[1] = safe_close(error_pipe[1]);
2521
67f63ee5 2522 for (unsigned k = 0; k < _META_MAX; k++) {
3b925504
LP
2523 _cleanup_fclose_ FILE *f = NULL;
2524
7718ac97
LB
2525 if (!paths[k])
2526 continue;
2527
3b925504
LP
2528 fds[2*k+1] = safe_close(fds[2*k+1]);
2529
4fa744a3 2530 f = take_fdopen(&fds[2*k], "r");
3b925504
LP
2531 if (!f) {
2532 r = -errno;
2533 goto finish;
2534 }
2535
3b925504
LP
2536 switch (k) {
2537
2538 case META_HOSTNAME:
2539 r = read_etc_hostname_stream(f, &hostname);
2540 if (r < 0)
2541 log_debug_errno(r, "Failed to read /etc/hostname: %m");
2542
2543 break;
2544
2545 case META_MACHINE_ID: {
2546 _cleanup_free_ char *line = NULL;
2547
2548 r = read_line(f, LONG_LINE_MAX, &line);
2549 if (r < 0)
2550 log_debug_errno(r, "Failed to read /etc/machine-id: %m");
2551 else if (r == 33) {
2552 r = sd_id128_from_string(line, &machine_id);
2553 if (r < 0)
2554 log_debug_errno(r, "Image contains invalid /etc/machine-id: %s", line);
2555 } else if (r == 0)
2556 log_debug("/etc/machine-id file is empty.");
ab763cb2
HS
2557 else if (streq(line, "uninitialized"))
2558 log_debug("/etc/machine-id file is uninitialized (likely aborted first boot).");
3b925504
LP
2559 else
2560 log_debug("/etc/machine-id has unexpected length %i.", r);
2561
2562 break;
2563 }
2564
2565 case META_MACHINE_INFO:
aa8fbc74 2566 r = load_env_file_pairs(f, "machine-info", &machine_info);
3b925504
LP
2567 if (r < 0)
2568 log_debug_errno(r, "Failed to read /etc/machine-info: %m");
2569
2570 break;
2571
2572 case META_OS_RELEASE:
aa8fbc74 2573 r = load_env_file_pairs(f, "os-release", &os_release);
3b925504
LP
2574 if (r < 0)
2575 log_debug_errno(r, "Failed to read OS release file: %m");
2576
2577 break;
7718ac97
LB
2578
2579 case META_EXTENSION_RELEASE:
2580 r = load_env_file_pairs(f, "extension-release", &extension_release);
2581 if (r < 0)
2582 log_debug_errno(r, "Failed to read extension release file: %m");
2583
2584 break;
3b925504
LP
2585 }
2586 }
2587
2e87a1fd 2588 r = wait_for_terminate_and_check("(sd-dissect)", child, 0);
3b925504 2589 child = 0;
2e87a1fd 2590 if (r < 0)
af8219d5
LP
2591 return r;
2592
2593 n = read(error_pipe[0], &v, sizeof(v));
2594 if (n < 0)
2595 return -errno;
2596 if (n == sizeof(v))
2597 return v; /* propagate error sent to us from child */
2598 if (n != 0)
2599 return -EIO;
2600
2e87a1fd
LP
2601 if (r != EXIT_SUCCESS)
2602 return -EPROTO;
3b925504
LP
2603
2604 free_and_replace(m->hostname, hostname);
2605 m->machine_id = machine_id;
2606 strv_free_and_replace(m->machine_info, machine_info);
2607 strv_free_and_replace(m->os_release, os_release);
7718ac97 2608 strv_free_and_replace(m->extension_release, extension_release);
3b925504
LP
2609
2610finish:
67f63ee5 2611 for (unsigned k = 0; k < n_meta_initialized; k++)
3b925504
LP
2612 safe_close_pair(fds + 2*k);
2613
2614 return r;
2615}
2616
4526113f
LP
2617int dissect_image_and_warn(
2618 int fd,
2619 const char *name,
89e62e0b 2620 const VeritySettings *verity,
18d73705 2621 const MountOptions *mount_options,
75dc190d 2622 uint64_t uevent_seqnum_not_before,
4a62257d 2623 usec_t timestamp_not_before,
4526113f
LP
2624 DissectImageFlags flags,
2625 DissectedImage **ret) {
2626
2627 _cleanup_free_ char *buffer = NULL;
2628 int r;
2629
2630 if (!name) {
2631 r = fd_get_path(fd, &buffer);
2632 if (r < 0)
2633 return r;
2634
2635 name = buffer;
2636 }
2637
4a62257d 2638 r = dissect_image(fd, verity, mount_options, uevent_seqnum_not_before, timestamp_not_before, flags, ret);
4526113f
LP
2639 switch (r) {
2640
2641 case -EOPNOTSUPP:
2642 return log_error_errno(r, "Dissecting images is not supported, compiled without blkid support.");
2643
2644 case -ENOPKG:
2645 return log_error_errno(r, "Couldn't identify a suitable partition table or file system in '%s'.", name);
2646
2647 case -EADDRNOTAVAIL:
2648 return log_error_errno(r, "No root partition for specified root hash found in '%s'.", name);
2649
2650 case -ENOTUNIQ:
2651 return log_error_errno(r, "Multiple suitable root partitions found in image '%s'.", name);
2652
2653 case -ENXIO:
2654 return log_error_errno(r, "No suitable root partition found in image '%s'.", name);
2655
2656 case -EPROTONOSUPPORT:
2657 return log_error_errno(r, "Device '%s' is loopback block device with partition scanning turned off, please turn it on.", name);
2658
2659 default:
2660 if (r < 0)
2661 return log_error_errno(r, "Failed to dissect image '%s': %m", name);
2662
2663 return r;
2664 }
2665}
2666
569a0e42 2667bool dissected_image_can_do_verity(const DissectedImage *image, PartitionDesignator partition_designator) {
e7cbe5cb
LB
2668 if (image->single_file_system)
2669 return partition_designator == PARTITION_ROOT && image->can_verity;
2670
2671 return PARTITION_VERITY_OF(partition_designator) >= 0;
2672}
2673
569a0e42 2674bool dissected_image_has_verity(const DissectedImage *image, PartitionDesignator partition_designator) {
e7cbe5cb
LB
2675 int k;
2676
2677 if (image->single_file_system)
2678 return partition_designator == PARTITION_ROOT && image->verity;
2679
2680 k = PARTITION_VERITY_OF(partition_designator);
2681 return k >= 0 && image->partitions[k].found;
2682}
2683
18d73705
LB
2684MountOptions* mount_options_free_all(MountOptions *options) {
2685 MountOptions *m;
2686
2687 while ((m = options)) {
2688 LIST_REMOVE(mount_options, options, m);
2689 free(m->options);
2690 free(m);
2691 }
2692
2693 return NULL;
2694}
2695
569a0e42 2696const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator) {
f5215bc8 2697 const MountOptions *m;
18d73705 2698
f5215bc8 2699 LIST_FOREACH(mount_options, m, options)
9ece6444 2700 if (designator == m->partition_designator && !isempty(m->options))
18d73705 2701 return m->options;
6aa05ebd 2702
18d73705
LB
2703 return NULL;
2704}
2705
6aa05ebd
LP
2706int mount_image_privately_interactively(
2707 const char *image,
2708 DissectImageFlags flags,
2709 char **ret_directory,
2710 LoopDevice **ret_loop_device,
2711 DecryptedImage **ret_decrypted_image) {
2712
27ec815e 2713 _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
6aa05ebd
LP
2714 _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
2715 _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
2716 _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
2717 _cleanup_(rmdir_and_freep) char *created_dir = NULL;
2718 _cleanup_free_ char *temp = NULL;
2719 int r;
2720
2721 /* Mounts an OS image at a temporary place, inside a newly created mount namespace of our own. This
2722 * is used by tools such as systemd-tmpfiles or systemd-firstboot to operate on some disk image
2723 * easily. */
2724
2725 assert(image);
2726 assert(ret_directory);
2727 assert(ret_loop_device);
2728 assert(ret_decrypted_image);
2729
27ec815e
LP
2730 r = verity_settings_load(&verity, image, NULL, NULL);
2731 if (r < 0)
2732 return log_error_errno(r, "Failed to load root hash data: %m");
2733
6aa05ebd
LP
2734 r = tempfn_random_child(NULL, program_invocation_short_name, &temp);
2735 if (r < 0)
2736 return log_error_errno(r, "Failed to generate temporary mount directory: %m");
2737
2738 r = loop_device_make_by_path(
2739 image,
ef9c184d 2740 FLAGS_SET(flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR,
6aa05ebd
LP
2741 FLAGS_SET(flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
2742 &d);
2743 if (r < 0)
2744 return log_error_errno(r, "Failed to set up loopback device: %m");
2745
4a62257d 2746 r = dissect_image_and_warn(d->fd, image, &verity, NULL, d->uevent_seqnum_not_before, d->timestamp_not_before, flags, &dissected_image);
6aa05ebd
LP
2747 if (r < 0)
2748 return r;
2749
27ec815e 2750 r = dissected_image_decrypt_interactively(dissected_image, NULL, &verity, flags, &decrypted_image);
6aa05ebd
LP
2751 if (r < 0)
2752 return r;
2753
2754 r = detach_mount_namespace();
2755 if (r < 0)
2756 return log_error_errno(r, "Failed to detach mount namespace: %m");
2757
2758 r = mkdir_p(temp, 0700);
2759 if (r < 0)
2760 return log_error_errno(r, "Failed to create mount point: %m");
2761
2762 created_dir = TAKE_PTR(temp);
2763
af187ab2 2764 r = dissected_image_mount_and_warn(dissected_image, created_dir, UID_INVALID, flags);
6aa05ebd 2765 if (r < 0)
af187ab2 2766 return r;
6aa05ebd
LP
2767
2768 if (decrypted_image) {
2769 r = decrypted_image_relinquish(decrypted_image);
2770 if (r < 0)
2771 return log_error_errno(r, "Failed to relinquish DM devices: %m");
2772 }
2773
2774 loop_device_relinquish(d);
2775
2776 *ret_directory = TAKE_PTR(created_dir);
2777 *ret_loop_device = TAKE_PTR(d);
2778 *ret_decrypted_image = TAKE_PTR(decrypted_image);
2779
2780 return 0;
2781}
2782
8c1be37e
LP
2783static const char *const partition_designator_table[] = {
2784 [PARTITION_ROOT] = "root",
2785 [PARTITION_ROOT_SECONDARY] = "root-secondary",
aee36b4e
LP
2786 [PARTITION_USR] = "usr",
2787 [PARTITION_USR_SECONDARY] = "usr-secondary",
8c1be37e
LP
2788 [PARTITION_HOME] = "home",
2789 [PARTITION_SRV] = "srv",
2790 [PARTITION_ESP] = "esp",
a8c47660 2791 [PARTITION_XBOOTLDR] = "xbootldr",
8c1be37e 2792 [PARTITION_SWAP] = "swap",
4623e8e6
LP
2793 [PARTITION_ROOT_VERITY] = "root-verity",
2794 [PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity",
aee36b4e
LP
2795 [PARTITION_USR_VERITY] = "usr-verity",
2796 [PARTITION_USR_SECONDARY_VERITY] = "usr-secondary-verity",
d4dffb85
LP
2797 [PARTITION_TMP] = "tmp",
2798 [PARTITION_VAR] = "var",
8c1be37e
LP
2799};
2800
93f59701
LB
2801int verity_dissect_and_mount(
2802 const char *src,
2803 const char *dest,
2804 const MountOptions *options,
2805 const char *required_host_os_release_id,
2806 const char *required_host_os_release_version_id,
2807 const char *required_host_os_release_sysext_level) {
2808
4beda316
LB
2809 _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
2810 _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL;
2811 _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
2812 _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
2813 DissectImageFlags dissect_image_flags;
2814 int r;
2815
2816 assert(src);
2817 assert(dest);
2818
2819 r = verity_settings_load(&verity, src, NULL, NULL);
2820 if (r < 0)
2821 return log_debug_errno(r, "Failed to load root hash: %m");
2822
2823 dissect_image_flags = verity.data_path ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0;
2824
2825 r = loop_device_make_by_path(
2826 src,
2827 -1,
2828 verity.data_path ? 0 : LO_FLAGS_PARTSCAN,
2829 &loop_device);
2830 if (r < 0)
2831 return log_debug_errno(r, "Failed to create loop device for image: %m");
2832
2833 r = dissect_image(
2834 loop_device->fd,
2835 &verity,
2836 options,
75dc190d 2837 loop_device->uevent_seqnum_not_before,
4a62257d 2838 loop_device->timestamp_not_before,
4beda316
LB
2839 dissect_image_flags,
2840 &dissected_image);
2841 /* No partition table? Might be a single-filesystem image, try again */
2842 if (!verity.data_path && r == -ENOPKG)
2843 r = dissect_image(
2844 loop_device->fd,
2845 &verity,
2846 options,
75dc190d 2847 loop_device->uevent_seqnum_not_before,
4a62257d 2848 loop_device->timestamp_not_before,
75dc190d 2849 dissect_image_flags | DISSECT_IMAGE_NO_PARTITION_TABLE,
4beda316
LB
2850 &dissected_image);
2851 if (r < 0)
2852 return log_debug_errno(r, "Failed to dissect image: %m");
2853
2854 r = dissected_image_decrypt(
2855 dissected_image,
2856 NULL,
2857 &verity,
2858 dissect_image_flags,
2859 &decrypted_image);
2860 if (r < 0)
2861 return log_debug_errno(r, "Failed to decrypt dissected image: %m");
2862
2863 r = mkdir_p_label(dest, 0755);
2864 if (r < 0)
2865 return log_debug_errno(r, "Failed to create destination directory %s: %m", dest);
2866 r = umount_recursive(dest, 0);
2867 if (r < 0)
2868 return log_debug_errno(r, "Failed to umount under destination directory %s: %m", dest);
2869
2870 r = dissected_image_mount(dissected_image, dest, UID_INVALID, dissect_image_flags);
2871 if (r < 0)
2872 return log_debug_errno(r, "Failed to mount image: %m");
2873
93f59701
LB
2874 /* If we got os-release values from the caller, then we need to match them with the image's
2875 * extension-release.d/ content. Return -EINVAL if there's any mismatch.
2876 * First, check the distro ID. If that matches, then check the new SYSEXT_LEVEL value if
2877 * available, or else fallback to VERSION_ID. */
2878 if (required_host_os_release_id &&
2879 (required_host_os_release_version_id || required_host_os_release_sysext_level)) {
2880 _cleanup_strv_free_ char **extension_release = NULL;
2881
2882 r = load_extension_release_pairs(dest, dissected_image->image_name, &extension_release);
2883 if (r < 0)
2884 return log_debug_errno(r, "Failed to parse image %s extension-release metadata: %m", dissected_image->image_name);
2885
2886 r = extension_release_validate(
2887 dissected_image->image_name,
2888 required_host_os_release_id,
2889 required_host_os_release_version_id,
2890 required_host_os_release_sysext_level,
2891 extension_release);
2892 if (r == 0)
2893 return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Image %s extension-release metadata does not match the root's", dissected_image->image_name);
2894 if (r < 0)
2895 return log_debug_errno(r, "Failed to compare image %s extension-release metadata with the root's os-release: %m", dissected_image->image_name);
2896 }
2897
4beda316
LB
2898 if (decrypted_image) {
2899 r = decrypted_image_relinquish(decrypted_image);
2900 if (r < 0)
2901 return log_debug_errno(r, "Failed to relinquish decrypted image: %m");
2902 }
2903
2904 loop_device_relinquish(loop_device);
2905
2906 return 0;
2907}
2908
569a0e42 2909DEFINE_STRING_TABLE_LOOKUP(partition_designator, PartitionDesignator);