]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/dissect-image.c
util-lib: rename path_check_fstype to path_is_fs_type
[thirdparty/systemd.git] / src / shared / dissect-image.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
8c1be37e
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2016 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
349cc4a5 21#if HAVE_LIBCRYPTSETUP
18b5886e 22#include <libcryptsetup.h>
dd59868b
OK
23#ifndef CRYPT_LUKS
24#define CRYPT_LUKS NULL
25#endif
18b5886e 26#endif
8c1be37e 27#include <sys/mount.h>
3b925504
LP
28#include <sys/prctl.h>
29#include <sys/wait.h>
8c1be37e
LP
30
31#include "architecture.h"
18b5886e 32#include "ask-password-api.h"
8c1be37e 33#include "blkid-util.h"
3b925504
LP
34#include "copy.h"
35#include "def.h"
553e15f2 36#include "device-nodes.h"
8c1be37e 37#include "dissect-image.h"
18b5886e 38#include "fd-util.h"
78ebe980 39#include "fileio.h"
2eedfd2d 40#include "fs-util.h"
8c1be37e 41#include "gpt.h"
78ebe980 42#include "hexdecoct.h"
3b925504
LP
43#include "hostname-util.h"
44#include "id128-util.h"
dcce98a4 45#include "linux-3.13/dm-ioctl.h"
8c1be37e
LP
46#include "mount-util.h"
47#include "path-util.h"
3b925504
LP
48#include "process-util.h"
49#include "raw-clone.h"
50#include "signal-util.h"
8c1be37e 51#include "stat-util.h"
18b5886e 52#include "stdio-util.h"
8c1be37e
LP
53#include "string-table.h"
54#include "string-util.h"
2eedfd2d 55#include "strv.h"
8c1be37e 56#include "udev-util.h"
41488e1f 57#include "xattr-util.h"
8c1be37e 58
d1c536f5 59_unused_ static int probe_filesystem(const char *node, char **ret_fstype) {
349cc4a5 60#if HAVE_BLKID
18b5886e
LP
61 _cleanup_blkid_free_probe_ blkid_probe b = NULL;
62 const char *fstype;
63 int r;
64
65 b = blkid_new_probe_from_filename(node);
66 if (!b)
67 return -ENOMEM;
68
69 blkid_probe_enable_superblocks(b, 1);
70 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
71
72 errno = 0;
73 r = blkid_do_safeprobe(b);
4c701096 74 if (IN_SET(r, -2, 1)) {
18b5886e
LP
75 log_debug("Failed to identify any partition type on partition %s", node);
76 goto not_found;
77 }
b382db9f
ZJS
78 if (r != 0)
79 return -errno ?: -EIO;
18b5886e
LP
80
81 (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
82
83 if (fstype) {
84 char *t;
85
86 t = strdup(fstype);
87 if (!t)
88 return -ENOMEM;
89
90 *ret_fstype = t;
91 return 1;
92 }
93
94not_found:
95 *ret_fstype = NULL;
96 return 0;
d1c536f5
ZJS
97#else
98 return -EOPNOTSUPP;
a75e27eb 99#endif
d1c536f5 100}
18b5886e 101
9b6deb03 102int dissect_image(int fd, const void *root_hash, size_t root_hash_size, DissectImageFlags flags, DissectedImage **ret) {
8c1be37e 103
349cc4a5 104#if HAVE_BLKID
4623e8e6 105 sd_id128_t root_uuid = SD_ID128_NULL, verity_uuid = SD_ID128_NULL;
8c1be37e
LP
106 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
107 bool is_gpt, is_mbr, generic_rw, multiple_generic = false;
108 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
109 _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
110 _cleanup_blkid_free_probe_ blkid_probe b = NULL;
111 _cleanup_udev_unref_ struct udev *udev = NULL;
112 _cleanup_free_ char *generic_node = NULL;
be30ad41 113 sd_id128_t generic_uuid = SD_ID128_NULL;
9b6deb03 114 const char *pttype = NULL;
8c1be37e
LP
115 struct udev_list_entry *first, *item;
116 blkid_partlist pl;
117 int r, generic_nr;
118 struct stat st;
119 unsigned i;
120
121 assert(fd >= 0);
122 assert(ret);
4623e8e6 123 assert(root_hash || root_hash_size == 0);
8c1be37e
LP
124
125 /* Probes a disk image, and returns information about what it found in *ret.
126 *
4623e8e6
LP
127 * Returns -ENOPKG if no suitable partition table or file system could be found.
128 * Returns -EADDRNOTAVAIL if a root hash was specified but no matching root/verity partitions found. */
129
130 if (root_hash) {
131 /* If a root hash is supplied, then we use the root partition that has a UUID that match the first
132 * 128bit of the root hash. And we use the verity partition that has a UUID that match the final
133 * 128bit. */
134
135 if (root_hash_size < sizeof(sd_id128_t))
136 return -EINVAL;
137
138 memcpy(&root_uuid, root_hash, sizeof(sd_id128_t));
139 memcpy(&verity_uuid, (const uint8_t*) root_hash + root_hash_size - sizeof(sd_id128_t), sizeof(sd_id128_t));
140
141 if (sd_id128_is_null(root_uuid))
142 return -EINVAL;
143 if (sd_id128_is_null(verity_uuid))
144 return -EINVAL;
145 }
8c1be37e
LP
146
147 if (fstat(fd, &st) < 0)
148 return -errno;
149
150 if (!S_ISBLK(st.st_mode))
151 return -ENOTBLK;
152
153 b = blkid_new_probe();
154 if (!b)
155 return -ENOMEM;
156
157 errno = 0;
158 r = blkid_probe_set_device(b, fd, 0, 0);
b382db9f
ZJS
159 if (r != 0)
160 return -errno ?: -ENOMEM;
8c1be37e 161
9b6deb03
LP
162 if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
163 /* Look for file system superblocks, unless we only shall look for GPT partition tables */
164 blkid_probe_enable_superblocks(b, 1);
165 blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE);
166 }
167
8c1be37e
LP
168 blkid_probe_enable_partitions(b, 1);
169 blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
170
171 errno = 0;
172 r = blkid_do_safeprobe(b);
4c701096 173 if (IN_SET(r, -2, 1)) {
8c1be37e
LP
174 log_debug("Failed to identify any partition table.");
175 return -ENOPKG;
176 }
b382db9f
ZJS
177 if (r != 0)
178 return -errno ?: -EIO;
8c1be37e
LP
179
180 m = new0(DissectedImage, 1);
181 if (!m)
182 return -ENOMEM;
183
e0f9e7bd
LP
184 if (!(flags & DISSECT_IMAGE_GPT_ONLY) &&
185 (flags & DISSECT_IMAGE_REQUIRE_ROOT)) {
9b6deb03 186 const char *usage = NULL;
8c1be37e 187
9b6deb03
LP
188 (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
189 if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
190 _cleanup_free_ char *t = NULL, *n = NULL;
191 const char *fstype = NULL;
8c1be37e 192
9b6deb03
LP
193 /* OK, we have found a file system, that's our root partition then. */
194 (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
8c1be37e 195
9b6deb03
LP
196 if (fstype) {
197 t = strdup(fstype);
198 if (!t)
199 return -ENOMEM;
200 }
201
202 if (asprintf(&n, "/dev/block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
203 return -ENOMEM;
8c1be37e 204
9b6deb03
LP
205 m->partitions[PARTITION_ROOT] = (DissectedPartition) {
206 .found = true,
207 .rw = true,
208 .partno = -1,
209 .architecture = _ARCHITECTURE_INVALID,
210 .fstype = t,
211 .node = n,
212 };
8c1be37e 213
9b6deb03 214 t = n = NULL;
8c1be37e 215
9b6deb03 216 m->encrypted = streq(fstype, "crypto_LUKS");
18b5886e 217
9b6deb03
LP
218 *ret = m;
219 m = NULL;
8c1be37e 220
9b6deb03
LP
221 return 0;
222 }
8c1be37e
LP
223 }
224
225 (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
226 if (!pttype)
227 return -ENOPKG;
228
229 is_gpt = streq_ptr(pttype, "gpt");
230 is_mbr = streq_ptr(pttype, "dos");
231
9b6deb03 232 if (!is_gpt && ((flags & DISSECT_IMAGE_GPT_ONLY) || !is_mbr))
8c1be37e
LP
233 return -ENOPKG;
234
235 errno = 0;
236 pl = blkid_probe_get_partitions(b);
b382db9f
ZJS
237 if (!pl)
238 return -errno ?: -ENOMEM;
8c1be37e
LP
239
240 udev = udev_new();
241 if (!udev)
242 return -errno;
243
244 d = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
245 if (!d)
246 return -ENOMEM;
247
248 for (i = 0;; i++) {
249 int n, z;
250
251 if (i >= 10) {
252 log_debug("Kernel partitions never appeared.");
253 return -ENXIO;
254 }
255
256 e = udev_enumerate_new(udev);
257 if (!e)
258 return -errno;
259
260 r = udev_enumerate_add_match_parent(e, d);
261 if (r < 0)
262 return r;
263
264 r = udev_enumerate_scan_devices(e);
265 if (r < 0)
266 return r;
267
268 /* Count the partitions enumerated by the kernel */
269 n = 0;
270 first = udev_enumerate_get_list_entry(e);
271 udev_list_entry_foreach(item, first)
272 n++;
273
274 /* Count the partitions enumerated by blkid */
275 z = blkid_partlist_numof_partitions(pl);
276 if (n == z + 1)
277 break;
278 if (n > z + 1) {
279 log_debug("blkid and kernel partition list do not match.");
280 return -EIO;
281 }
282 if (n < z + 1) {
759aaedc 283 unsigned j = 0;
8c1be37e
LP
284
285 /* The kernel has probed fewer partitions than blkid? Maybe the kernel prober is still running
286 * or it got EBUSY because udev already opened the device. Let's reprobe the device, which is a
287 * synchronous call that waits until probing is complete. */
288
759aaedc
LP
289 for (;;) {
290 if (j++ > 20)
291 return -EBUSY;
8c1be37e 292
759aaedc 293 if (ioctl(fd, BLKRRPART, 0) < 0) {
8c1be37e 294 r = -errno;
759aaedc
LP
295
296 if (r == -EINVAL) {
297 struct loop_info64 info;
298
299 /* If we are running on a loop device that has partition scanning off,
300 * return an explicit recognizable error about this, so that callers
301 * can generate a proper message explaining the situation. */
302
303 if (ioctl(fd, LOOP_GET_STATUS64, &info) >= 0 && (info.lo_flags & LO_FLAGS_PARTSCAN) == 0) {
304 log_debug("Device is loop device and partition scanning is off!");
305 return -EPROTONOSUPPORT;
306 }
307 }
308 if (r != -EBUSY)
309 return r;
310 } else
8c1be37e
LP
311 break;
312
313 /* If something else has the device open, such as an udev rule, the ioctl will return
314 * EBUSY. Since there's no way to wait until it isn't busy anymore, let's just wait a
315 * bit, and try again.
316 *
317 * This is really something they should fix in the kernel! */
318
759aaedc 319 (void) usleep(50 * USEC_PER_MSEC);
8c1be37e 320 }
8c1be37e
LP
321 }
322
323 e = udev_enumerate_unref(e);
324 }
325
326 first = udev_enumerate_get_list_entry(e);
327 udev_list_entry_foreach(item, first) {
328 _cleanup_udev_device_unref_ struct udev_device *q;
9b6deb03 329 unsigned long long pflags;
8c1be37e 330 blkid_partition pp;
7be1420f 331 const char *node, *sysname;
8c1be37e
LP
332 dev_t qn;
333 int nr;
334
335 q = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
336 if (!q)
337 return -errno;
338
339 qn = udev_device_get_devnum(q);
340 if (major(qn) == 0)
341 continue;
342
343 if (st.st_rdev == qn)
344 continue;
345
7be1420f
LP
346 /* Filter out weird MMC RPMB partitions, which cannot reasonably be read, see
347 * https://github.com/systemd/systemd/issues/5806 */
348 sysname = udev_device_get_sysname(q);
349 if (sysname && startswith(sysname, "mmcblk") && endswith(sysname, "rpmb"))
350 continue;
351
8c1be37e
LP
352 node = udev_device_get_devnode(q);
353 if (!node)
354 continue;
355
356 pp = blkid_partlist_devno_to_partition(pl, qn);
357 if (!pp)
358 continue;
359
9b6deb03 360 pflags = blkid_partition_get_flags(pp);
8c1be37e
LP
361
362 nr = blkid_partition_get_partno(pp);
363 if (nr < 0)
364 continue;
365
366 if (is_gpt) {
367 int designator = _PARTITION_DESIGNATOR_INVALID, architecture = _ARCHITECTURE_INVALID;
4623e8e6
LP
368 const char *stype, *sid, *fstype = NULL;
369 sd_id128_t type_id, id;
8c1be37e
LP
370 bool rw = true;
371
4623e8e6
LP
372 sid = blkid_partition_get_uuid(pp);
373 if (!sid)
374 continue;
375 if (sd_id128_from_string(sid, &id) < 0)
376 continue;
377
8c1be37e
LP
378 stype = blkid_partition_get_type_string(pp);
379 if (!stype)
380 continue;
8c1be37e
LP
381 if (sd_id128_from_string(stype, &type_id) < 0)
382 continue;
383
384 if (sd_id128_equal(type_id, GPT_HOME)) {
a48dd347
LP
385
386 if (pflags & GPT_FLAG_NO_AUTO)
387 continue;
388
8c1be37e 389 designator = PARTITION_HOME;
9b6deb03 390 rw = !(pflags & GPT_FLAG_READ_ONLY);
8c1be37e 391 } else if (sd_id128_equal(type_id, GPT_SRV)) {
a48dd347
LP
392
393 if (pflags & GPT_FLAG_NO_AUTO)
394 continue;
395
8c1be37e 396 designator = PARTITION_SRV;
9b6deb03 397 rw = !(pflags & GPT_FLAG_READ_ONLY);
8c1be37e 398 } else if (sd_id128_equal(type_id, GPT_ESP)) {
a48dd347
LP
399
400 /* Note that we don't check the GPT_FLAG_NO_AUTO flag for the ESP, as it is not defined
401 * there. We instead check the GPT_FLAG_NO_BLOCK_IO_PROTOCOL, as recommended by the
402 * UEFI spec (See "12.3.3 Number and Location of System Partitions"). */
403
404 if (pflags & GPT_FLAG_NO_BLOCK_IO_PROTOCOL)
405 continue;
406
8c1be37e
LP
407 designator = PARTITION_ESP;
408 fstype = "vfat";
409 }
410#ifdef GPT_ROOT_NATIVE
411 else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) {
4623e8e6 412
a48dd347
LP
413 if (pflags & GPT_FLAG_NO_AUTO)
414 continue;
415
4623e8e6
LP
416 /* If a root ID is specified, ignore everything but the root id */
417 if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
418 continue;
419
8c1be37e
LP
420 designator = PARTITION_ROOT;
421 architecture = native_architecture();
9b6deb03 422 rw = !(pflags & GPT_FLAG_READ_ONLY);
4f8b86e3 423 } else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE_VERITY)) {
4623e8e6 424
a48dd347
LP
425 if (pflags & GPT_FLAG_NO_AUTO)
426 continue;
427
4623e8e6
LP
428 m->can_verity = true;
429
430 /* Ignore verity unless a root hash is specified */
431 if (sd_id128_is_null(verity_uuid) || !sd_id128_equal(verity_uuid, id))
432 continue;
433
434 designator = PARTITION_ROOT_VERITY;
435 fstype = "DM_verity_hash";
436 architecture = native_architecture();
437 rw = false;
438 }
439#endif
8c1be37e
LP
440#ifdef GPT_ROOT_SECONDARY
441 else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY)) {
4623e8e6 442
a48dd347
LP
443 if (pflags & GPT_FLAG_NO_AUTO)
444 continue;
445
4623e8e6
LP
446 /* If a root ID is specified, ignore everything but the root id */
447 if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
448 continue;
449
8c1be37e
LP
450 designator = PARTITION_ROOT_SECONDARY;
451 architecture = SECONDARY_ARCHITECTURE;
9b6deb03 452 rw = !(pflags & GPT_FLAG_READ_ONLY);
4f8b86e3 453 } else if (sd_id128_equal(type_id, GPT_ROOT_SECONDARY_VERITY)) {
a48dd347
LP
454
455 if (pflags & GPT_FLAG_NO_AUTO)
456 continue;
457
4623e8e6
LP
458 m->can_verity = true;
459
460 /* Ignore verity unless root has is specified */
461 if (sd_id128_is_null(verity_uuid) || !sd_id128_equal(verity_uuid, id))
462 continue;
463
464 designator = PARTITION_ROOT_SECONDARY_VERITY;
465 fstype = "DM_verity_hash";
466 architecture = SECONDARY_ARCHITECTURE;
467 rw = false;
468 }
8c1be37e
LP
469#endif
470 else if (sd_id128_equal(type_id, GPT_SWAP)) {
a48dd347
LP
471
472 if (pflags & GPT_FLAG_NO_AUTO)
473 continue;
474
8c1be37e
LP
475 designator = PARTITION_SWAP;
476 fstype = "swap";
477 } else if (sd_id128_equal(type_id, GPT_LINUX_GENERIC)) {
478
a48dd347
LP
479 if (pflags & GPT_FLAG_NO_AUTO)
480 continue;
481
8c1be37e
LP
482 if (generic_node)
483 multiple_generic = true;
484 else {
485 generic_nr = nr;
9b6deb03 486 generic_rw = !(pflags & GPT_FLAG_READ_ONLY);
be30ad41 487 generic_uuid = id;
8c1be37e
LP
488 generic_node = strdup(node);
489 if (!generic_node)
490 return -ENOMEM;
491 }
492 }
493
494 if (designator != _PARTITION_DESIGNATOR_INVALID) {
495 _cleanup_free_ char *t = NULL, *n = NULL;
496
497 /* First one wins */
498 if (m->partitions[designator].found)
499 continue;
500
501 if (fstype) {
502 t = strdup(fstype);
503 if (!t)
504 return -ENOMEM;
505 }
506
507 n = strdup(node);
508 if (!n)
509 return -ENOMEM;
510
511 m->partitions[designator] = (DissectedPartition) {
512 .found = true,
513 .partno = nr,
514 .rw = rw,
515 .architecture = architecture,
516 .node = n,
517 .fstype = t,
be30ad41 518 .uuid = id,
8c1be37e
LP
519 };
520
521 n = t = NULL;
522 }
523
524 } else if (is_mbr) {
525
9b6deb03 526 if (pflags != 0x80) /* Bootable flag */
8c1be37e
LP
527 continue;
528
529 if (blkid_partition_get_type(pp) != 0x83) /* Linux partition */
530 continue;
531
532 if (generic_node)
533 multiple_generic = true;
534 else {
535 generic_nr = nr;
536 generic_rw = true;
537 generic_node = strdup(node);
538 if (!generic_node)
539 return -ENOMEM;
540 }
541 }
542 }
543
544 if (!m->partitions[PARTITION_ROOT].found) {
545 /* No root partition found? Then let's see if ther's one for the secondary architecture. And if not
546 * either, then check if there's a single generic one, and use that. */
547
4623e8e6 548 if (m->partitions[PARTITION_ROOT_VERITY].found)
e0f9e7bd 549 return -EADDRNOTAVAIL;
4623e8e6 550
8c1be37e
LP
551 if (m->partitions[PARTITION_ROOT_SECONDARY].found) {
552 m->partitions[PARTITION_ROOT] = m->partitions[PARTITION_ROOT_SECONDARY];
553 zero(m->partitions[PARTITION_ROOT_SECONDARY]);
4623e8e6
LP
554
555 m->partitions[PARTITION_ROOT_VERITY] = m->partitions[PARTITION_ROOT_SECONDARY_VERITY];
556 zero(m->partitions[PARTITION_ROOT_SECONDARY_VERITY]);
557
e0f9e7bd
LP
558 } else if (flags & DISSECT_IMAGE_REQUIRE_ROOT) {
559
560 /* If the root has was set, then we won't fallback to a generic node, because the root hash
561 * decides */
562 if (root_hash)
563 return -EADDRNOTAVAIL;
8c1be37e 564
e0f9e7bd
LP
565 /* If we didn't find a generic node, then we can't fix this up either */
566 if (!generic_node)
567 return -ENXIO;
568
569 /* If we didn't find a properly marked root partition, but we did find a single suitable
570 * generic Linux partition, then use this as root partition, if the caller asked for it. */
8c1be37e
LP
571 if (multiple_generic)
572 return -ENOTUNIQ;
573
574 m->partitions[PARTITION_ROOT] = (DissectedPartition) {
575 .found = true,
576 .rw = generic_rw,
577 .partno = generic_nr,
578 .architecture = _ARCHITECTURE_INVALID,
579 .node = generic_node,
be30ad41 580 .uuid = generic_uuid,
8c1be37e
LP
581 };
582
583 generic_node = NULL;
e0f9e7bd 584 }
8c1be37e
LP
585 }
586
4623e8e6 587 if (root_hash) {
e0f9e7bd 588 if (!m->partitions[PARTITION_ROOT_VERITY].found || !m->partitions[PARTITION_ROOT].found)
4623e8e6
LP
589 return -EADDRNOTAVAIL;
590
591 /* If we found the primary root with the hash, then we definitely want to suppress any secondary root
592 * (which would be weird, after all the root hash should only be assigned to one pair of
593 * partitions... */
594 m->partitions[PARTITION_ROOT_SECONDARY].found = false;
595 m->partitions[PARTITION_ROOT_SECONDARY_VERITY].found = false;
596
597 /* If we found a verity setup, then the root partition is necessarily read-only. */
598 m->partitions[PARTITION_ROOT].rw = false;
599
600 m->verity = true;
601 }
602
18b5886e
LP
603 blkid_free_probe(b);
604 b = NULL;
605
8c1be37e
LP
606 /* Fill in file system types if we don't know them yet. */
607 for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
18b5886e 608 DissectedPartition *p = m->partitions + i;
8c1be37e 609
18b5886e 610 if (!p->found)
8c1be37e
LP
611 continue;
612
18b5886e
LP
613 if (!p->fstype && p->node) {
614 r = probe_filesystem(p->node, &p->fstype);
615 if (r < 0)
616 return r;
8c1be37e
LP
617 }
618
18b5886e
LP
619 if (streq_ptr(p->fstype, "crypto_LUKS"))
620 m->encrypted = true;
896f937f
LP
621
622 if (p->fstype && fstype_is_ro(p->fstype))
623 p->rw = false;
8c1be37e
LP
624 }
625
626 *ret = m;
627 m = NULL;
628
629 return 0;
630#else
631 return -EOPNOTSUPP;
632#endif
633}
634
635DissectedImage* dissected_image_unref(DissectedImage *m) {
636 unsigned i;
637
638 if (!m)
639 return NULL;
640
641 for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
642 free(m->partitions[i].fstype);
643 free(m->partitions[i].node);
18b5886e
LP
644 free(m->partitions[i].decrypted_fstype);
645 free(m->partitions[i].decrypted_node);
8c1be37e
LP
646 }
647
3b925504
LP
648 free(m->hostname);
649 strv_free(m->machine_info);
650 strv_free(m->os_release);
651
5fecf46d 652 return mfree(m);
8c1be37e
LP
653}
654
18b5886e 655static int is_loop_device(const char *path) {
553e15f2 656 char s[SYS_BLOCK_PATH_MAX("/../loop/")];
18b5886e
LP
657 struct stat st;
658
659 assert(path);
660
661 if (stat(path, &st) < 0)
662 return -errno;
663
664 if (!S_ISBLK(st.st_mode))
665 return -ENOTBLK;
666
553e15f2 667 xsprintf_sys_block_path(s, "/loop/", st.st_dev);
18b5886e
LP
668 if (access(s, F_OK) < 0) {
669 if (errno != ENOENT)
670 return -errno;
671
672 /* The device itself isn't a loop device, but maybe it's a partition and its parent is? */
553e15f2 673 xsprintf_sys_block_path(s, "/../loop/", st.st_dev);
18b5886e
LP
674 if (access(s, F_OK) < 0)
675 return errno == ENOENT ? false : -errno;
676 }
677
678 return true;
679}
680
681static int mount_partition(
682 DissectedPartition *m,
683 const char *where,
684 const char *directory,
685 DissectImageFlags flags) {
686
687 const char *p, *options = NULL, *node, *fstype;
2eedfd2d 688 _cleanup_free_ char *chased = NULL;
8c1be37e 689 bool rw;
2eedfd2d 690 int r;
8c1be37e
LP
691
692 assert(m);
693 assert(where);
694
18b5886e
LP
695 node = m->decrypted_node ?: m->node;
696 fstype = m->decrypted_fstype ?: m->fstype;
697
698 if (!m->found || !node || !fstype)
8c1be37e
LP
699 return 0;
700
18b5886e
LP
701 /* Stacked encryption? Yuck */
702 if (streq_ptr(fstype, "crypto_LUKS"))
703 return -ELOOP;
704
705 rw = m->rw && !(flags & DISSECT_IMAGE_READ_ONLY);
8c1be37e 706
2eedfd2d
LP
707 if (directory) {
708 r = chase_symlinks(directory, where, CHASE_PREFIX_ROOT, &chased);
709 if (r < 0)
710 return r;
711
712 p = chased;
713 } else
8c1be37e
LP
714 p = where;
715
18b5886e 716 /* If requested, turn on discard support. */
154d2269 717 if (fstype_can_discard(fstype) &&
18b5886e
LP
718 ((flags & DISSECT_IMAGE_DISCARD) ||
719 ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && is_loop_device(m->node))))
720 options = "discard";
8c1be37e 721
18b5886e 722 return mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options);
8c1be37e
LP
723}
724
18b5886e 725int dissected_image_mount(DissectedImage *m, const char *where, DissectImageFlags flags) {
8c1be37e
LP
726 int r;
727
728 assert(m);
729 assert(where);
730
731 if (!m->partitions[PARTITION_ROOT].found)
732 return -ENXIO;
733
734 r = mount_partition(m->partitions + PARTITION_ROOT, where, NULL, flags);
735 if (r < 0)
736 return r;
737
738 r = mount_partition(m->partitions + PARTITION_HOME, where, "/home", flags);
739 if (r < 0)
740 return r;
741
742 r = mount_partition(m->partitions + PARTITION_SRV, where, "/srv", flags);
743 if (r < 0)
744 return r;
745
746 if (m->partitions[PARTITION_ESP].found) {
2eedfd2d 747 const char *mp;
8c1be37e
LP
748
749 /* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */
750
2eedfd2d
LP
751 FOREACH_STRING(mp, "/efi", "/boot") {
752 _cleanup_free_ char *p = NULL;
753
754 r = chase_symlinks(mp, where, CHASE_PREFIX_ROOT, &p);
8c1be37e 755 if (r < 0)
2eedfd2d
LP
756 continue;
757
758 r = dir_is_empty(p);
759 if (r > 0) {
760 r = mount_partition(m->partitions + PARTITION_ESP, where, mp, flags);
761 if (r < 0)
762 return r;
763 }
8c1be37e
LP
764 }
765 }
766
767 return 0;
768}
769
349cc4a5 770#if HAVE_LIBCRYPTSETUP
18b5886e
LP
771typedef struct DecryptedPartition {
772 struct crypt_device *device;
773 char *name;
774 bool relinquished;
775} DecryptedPartition;
776
777struct DecryptedImage {
778 DecryptedPartition *decrypted;
779 size_t n_decrypted;
780 size_t n_allocated;
781};
782#endif
783
784DecryptedImage* decrypted_image_unref(DecryptedImage* d) {
349cc4a5 785#if HAVE_LIBCRYPTSETUP
18b5886e
LP
786 size_t i;
787 int r;
788
789 if (!d)
790 return NULL;
791
792 for (i = 0; i < d->n_decrypted; i++) {
793 DecryptedPartition *p = d->decrypted + i;
794
795 if (p->device && p->name && !p->relinquished) {
796 r = crypt_deactivate(p->device, p->name);
797 if (r < 0)
798 log_debug_errno(r, "Failed to deactivate encrypted partition %s", p->name);
799 }
800
801 if (p->device)
802 crypt_free(p->device);
803 free(p->name);
804 }
805
806 free(d);
807#endif
808 return NULL;
809}
810
349cc4a5 811#if HAVE_LIBCRYPTSETUP
4623e8e6
LP
812
813static int make_dm_name_and_node(const void *original_node, const char *suffix, char **ret_name, char **ret_node) {
814 _cleanup_free_ char *name = NULL, *node = NULL;
815 const char *base;
816
817 assert(original_node);
818 assert(suffix);
819 assert(ret_name);
820 assert(ret_node);
821
822 base = strrchr(original_node, '/');
823 if (!base)
824 return -EINVAL;
825 base++;
826 if (isempty(base))
827 return -EINVAL;
828
829 name = strjoin(base, suffix);
830 if (!name)
831 return -ENOMEM;
832 if (!filename_is_valid(name))
833 return -EINVAL;
834
835 node = strjoin(crypt_get_dir(), "/", name);
836 if (!node)
837 return -ENOMEM;
838
839 *ret_name = name;
840 *ret_node = node;
841
842 name = node = NULL;
843 return 0;
844}
845
18b5886e
LP
846static int decrypt_partition(
847 DissectedPartition *m,
848 const char *passphrase,
849 DissectImageFlags flags,
850 DecryptedImage *d) {
851
852 _cleanup_free_ char *node = NULL, *name = NULL;
853 struct crypt_device *cd;
18b5886e
LP
854 int r;
855
856 assert(m);
857 assert(d);
858
859 if (!m->found || !m->node || !m->fstype)
860 return 0;
861
862 if (!streq(m->fstype, "crypto_LUKS"))
863 return 0;
864
bdd73ac5
ZJS
865 if (!passphrase)
866 return -ENOKEY;
867
4623e8e6
LP
868 r = make_dm_name_and_node(m->node, "-decrypted", &name, &node);
869 if (r < 0)
870 return r;
18b5886e
LP
871
872 if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
873 return -ENOMEM;
874
875 r = crypt_init(&cd, m->node);
876 if (r < 0)
715cbb81 877 return log_debug_errno(r, "Failed to initialize dm-crypt: %m");
18b5886e 878
dd59868b 879 r = crypt_load(cd, CRYPT_LUKS, NULL);
715cbb81
LP
880 if (r < 0) {
881 log_debug_errno(r, "Failed to load LUKS metadata: %m");
18b5886e 882 goto fail;
715cbb81 883 }
18b5886e
LP
884
885 r = crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase),
886 ((flags & DISSECT_IMAGE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
887 ((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
715cbb81
LP
888 if (r < 0)
889 log_debug_errno(r, "Failed to activate LUKS device: %m");
18b5886e
LP
890 if (r == -EPERM) {
891 r = -EKEYREJECTED;
892 goto fail;
893 }
894 if (r < 0)
895 goto fail;
896
897 d->decrypted[d->n_decrypted].name = name;
898 name = NULL;
899
900 d->decrypted[d->n_decrypted].device = cd;
901 d->n_decrypted++;
902
903 m->decrypted_node = node;
904 node = NULL;
905
906 return 0;
907
4623e8e6
LP
908fail:
909 crypt_free(cd);
910 return r;
911}
912
913static int verity_partition(
914 DissectedPartition *m,
915 DissectedPartition *v,
916 const void *root_hash,
917 size_t root_hash_size,
918 DissectImageFlags flags,
919 DecryptedImage *d) {
920
921 _cleanup_free_ char *node = NULL, *name = NULL;
922 struct crypt_device *cd;
923 int r;
924
925 assert(m);
926 assert(v);
927
928 if (!root_hash)
929 return 0;
930
931 if (!m->found || !m->node || !m->fstype)
932 return 0;
933 if (!v->found || !v->node || !v->fstype)
934 return 0;
935
936 if (!streq(v->fstype, "DM_verity_hash"))
937 return 0;
938
939 r = make_dm_name_and_node(m->node, "-verity", &name, &node);
940 if (r < 0)
941 return r;
942
943 if (!GREEDY_REALLOC0(d->decrypted, d->n_allocated, d->n_decrypted + 1))
944 return -ENOMEM;
945
946 r = crypt_init(&cd, v->node);
947 if (r < 0)
948 return r;
949
950 r = crypt_load(cd, CRYPT_VERITY, NULL);
951 if (r < 0)
952 goto fail;
953
954 r = crypt_set_data_device(cd, m->node);
955 if (r < 0)
956 goto fail;
957
958 r = crypt_activate_by_volume_key(cd, name, root_hash, root_hash_size, CRYPT_ACTIVATE_READONLY);
959 if (r < 0)
960 goto fail;
961
962 d->decrypted[d->n_decrypted].name = name;
963 name = NULL;
964
965 d->decrypted[d->n_decrypted].device = cd;
966 d->n_decrypted++;
967
968 m->decrypted_node = node;
969 node = NULL;
970
971 return 0;
972
18b5886e
LP
973fail:
974 crypt_free(cd);
975 return r;
976}
977#endif
978
979int dissected_image_decrypt(
980 DissectedImage *m,
981 const char *passphrase,
4623e8e6
LP
982 const void *root_hash,
983 size_t root_hash_size,
18b5886e
LP
984 DissectImageFlags flags,
985 DecryptedImage **ret) {
986
987 _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
349cc4a5 988#if HAVE_LIBCRYPTSETUP
18b5886e
LP
989 unsigned i;
990 int r;
991#endif
992
993 assert(m);
4623e8e6 994 assert(root_hash || root_hash_size == 0);
18b5886e
LP
995
996 /* Returns:
997 *
998 * = 0 → There was nothing to decrypt
999 * > 0 → Decrypted successfully
d1c536f5 1000 * -ENOKEY → There's something to decrypt but no key was supplied
18b5886e
LP
1001 * -EKEYREJECTED → Passed key was not correct
1002 */
1003
4623e8e6
LP
1004 if (root_hash && root_hash_size < sizeof(sd_id128_t))
1005 return -EINVAL;
1006
1007 if (!m->encrypted && !m->verity) {
18b5886e
LP
1008 *ret = NULL;
1009 return 0;
1010 }
1011
349cc4a5 1012#if HAVE_LIBCRYPTSETUP
18b5886e
LP
1013 d = new0(DecryptedImage, 1);
1014 if (!d)
1015 return -ENOMEM;
1016
1017 for (i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
1018 DissectedPartition *p = m->partitions + i;
4623e8e6 1019 int k;
18b5886e
LP
1020
1021 if (!p->found)
1022 continue;
1023
1024 r = decrypt_partition(p, passphrase, flags, d);
1025 if (r < 0)
1026 return r;
1027
4623e8e6
LP
1028 k = PARTITION_VERITY_OF(i);
1029 if (k >= 0) {
1030 r = verity_partition(p, m->partitions + k, root_hash, root_hash_size, flags, d);
1031 if (r < 0)
1032 return r;
1033 }
1034
18b5886e
LP
1035 if (!p->decrypted_fstype && p->decrypted_node) {
1036 r = probe_filesystem(p->decrypted_node, &p->decrypted_fstype);
1037 if (r < 0)
1038 return r;
1039 }
1040 }
1041
1042 *ret = d;
1043 d = NULL;
1044
1045 return 1;
1046#else
1047 return -EOPNOTSUPP;
1048#endif
1049}
1050
1051int dissected_image_decrypt_interactively(
1052 DissectedImage *m,
1053 const char *passphrase,
4623e8e6
LP
1054 const void *root_hash,
1055 size_t root_hash_size,
18b5886e
LP
1056 DissectImageFlags flags,
1057 DecryptedImage **ret) {
1058
1059 _cleanup_strv_free_erase_ char **z = NULL;
1060 int n = 3, r;
1061
1062 if (passphrase)
1063 n--;
1064
1065 for (;;) {
4623e8e6 1066 r = dissected_image_decrypt(m, passphrase, root_hash, root_hash_size, flags, ret);
18b5886e
LP
1067 if (r >= 0)
1068 return r;
1069 if (r == -EKEYREJECTED)
1070 log_error_errno(r, "Incorrect passphrase, try again!");
1071 else if (r != -ENOKEY) {
1072 log_error_errno(r, "Failed to decrypt image: %m");
1073 return r;
1074 }
1075
1076 if (--n < 0) {
1077 log_error("Too many retries.");
1078 return -EKEYREJECTED;
1079 }
1080
1081 z = strv_free(z);
1082
1083 r = ask_password_auto("Please enter image passphrase!", NULL, "dissect", "dissect", USEC_INFINITY, 0, &z);
1084 if (r < 0)
1085 return log_error_errno(r, "Failed to query for passphrase: %m");
1086
1087 passphrase = z[0];
1088 }
1089}
1090
349cc4a5 1091#if HAVE_LIBCRYPTSETUP
18b5886e
LP
1092static int deferred_remove(DecryptedPartition *p) {
1093
1094 struct dm_ioctl dm = {
1095 .version = {
1096 DM_VERSION_MAJOR,
1097 DM_VERSION_MINOR,
1098 DM_VERSION_PATCHLEVEL
1099 },
1100 .data_size = sizeof(dm),
1101 .flags = DM_DEFERRED_REMOVE,
1102 };
1103
1104 _cleanup_close_ int fd = -1;
1105
1106 assert(p);
1107
1108 /* Unfortunately, libcryptsetup doesn't provide a proper API for this, hence call the ioctl() directly. */
1109
1110 fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
1111 if (fd < 0)
1112 return -errno;
1113
1114 strncpy(dm.name, p->name, sizeof(dm.name));
1115
1116 if (ioctl(fd, DM_DEV_REMOVE, &dm))
1117 return -errno;
1118
1119 return 0;
1120}
1121#endif
1122
1123int decrypted_image_relinquish(DecryptedImage *d) {
1124
349cc4a5 1125#if HAVE_LIBCRYPTSETUP
18b5886e
LP
1126 size_t i;
1127 int r;
1128#endif
1129
1130 assert(d);
1131
1132 /* Turns on automatic removal after the last use ended for all DM devices of this image, and sets a boolean so
1133 * that we don't clean it up ourselves either anymore */
1134
349cc4a5 1135#if HAVE_LIBCRYPTSETUP
18b5886e
LP
1136 for (i = 0; i < d->n_decrypted; i++) {
1137 DecryptedPartition *p = d->decrypted + i;
1138
1139 if (p->relinquished)
1140 continue;
1141
1142 r = deferred_remove(p);
1143 if (r < 0)
1144 return log_debug_errno(r, "Failed to mark %s for auto-removal: %m", p->name);
1145
1146 p->relinquished = true;
1147 }
1148#endif
1149
1150 return 0;
1151}
1152
78ebe980
LP
1153int root_hash_load(const char *image, void **ret, size_t *ret_size) {
1154 _cleanup_free_ char *text = NULL;
1155 _cleanup_free_ void *k = NULL;
78ebe980
LP
1156 size_t l;
1157 int r;
1158
1159 assert(image);
1160 assert(ret);
1161 assert(ret_size);
1162
1163 if (is_device_path(image)) {
1164 /* If we are asked to load the root hash for a device node, exit early */
1165 *ret = NULL;
1166 *ret_size = 0;
1167 return 0;
1168 }
1169
41488e1f
LP
1170 r = getxattr_malloc(image, "user.verity.roothash", &text, true);
1171 if (r < 0) {
1172 char *fn, *e, *n;
78ebe980 1173
41488e1f
LP
1174 if (!IN_SET(r, -ENODATA, -EOPNOTSUPP, -ENOENT))
1175 return r;
78ebe980 1176
41488e1f
LP
1177 fn = newa(char, strlen(image) + strlen(".roothash") + 1);
1178 n = stpcpy(fn, image);
1179 e = endswith(fn, ".raw");
1180 if (e)
1181 n = e;
1182
1183 strcpy(n, ".roothash");
1184
1185 r = read_one_line_file(fn, &text);
1186 if (r == -ENOENT) {
1187 *ret = NULL;
1188 *ret_size = 0;
1189 return 0;
1190 }
1191 if (r < 0)
1192 return r;
78ebe980 1193 }
78ebe980
LP
1194
1195 r = unhexmem(text, strlen(text), &k, &l);
1196 if (r < 0)
1197 return r;
1198 if (l < sizeof(sd_id128_t))
1199 return -EINVAL;
1200
1201 *ret = k;
1202 *ret_size = l;
1203
1204 k = NULL;
1205
1206 return 1;
1207}
1208
3b925504
LP
1209int dissected_image_acquire_metadata(DissectedImage *m) {
1210
1211 enum {
1212 META_HOSTNAME,
1213 META_MACHINE_ID,
1214 META_MACHINE_INFO,
1215 META_OS_RELEASE,
1216 _META_MAX,
1217 };
1218
1219 static const char *const paths[_META_MAX] = {
1220 [META_HOSTNAME] = "/etc/hostname\0",
1221 [META_MACHINE_ID] = "/etc/machine-id\0",
1222 [META_MACHINE_INFO] = "/etc/machine-info\0",
1223 [META_OS_RELEASE] = "/etc/os-release\0/usr/lib/os-release\0",
1224 };
1225
1226 _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL;
1227 _cleanup_(rmdir_and_freep) char *t = NULL;
1228 _cleanup_(sigkill_waitp) pid_t child = 0;
1229 sd_id128_t machine_id = SD_ID128_NULL;
1230 _cleanup_free_ char *hostname = NULL;
1231 unsigned n_meta_initialized = 0, k;
1232 int fds[2 * _META_MAX], r;
1233 siginfo_t si;
1234
1235 BLOCK_SIGNALS(SIGCHLD);
1236
1237 assert(m);
1238
1239 for (; n_meta_initialized < _META_MAX; n_meta_initialized ++)
1240 if (pipe2(fds + 2*n_meta_initialized, O_CLOEXEC) < 0) {
1241 r = -errno;
1242 goto finish;
1243 }
1244
1245 r = mkdtemp_malloc("/tmp/dissect-XXXXXX", &t);
1246 if (r < 0)
1247 goto finish;
1248
1249 child = raw_clone(SIGCHLD|CLONE_NEWNS);
1250 if (child < 0) {
1251 r = -errno;
1252 goto finish;
1253 }
1254
1255 if (child == 0) {
1256
1257 (void) reset_all_signal_handlers();
1258 (void) reset_signal_mask();
1259 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
1260
1261 /* Make sure we never propagate to the host */
1262 if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0)
1263 _exit(EXIT_FAILURE);
1264
1265 r = dissected_image_mount(m, t, DISSECT_IMAGE_READ_ONLY);
1266 if (r < 0)
1267 _exit(EXIT_FAILURE);
1268
1269 for (k = 0; k < _META_MAX; k++) {
1270 _cleanup_close_ int fd = -1;
1271 const char *p;
1272
1273 fds[2*k] = safe_close(fds[2*k]);
1274
1275 NULSTR_FOREACH(p, paths[k]) {
1276 _cleanup_free_ char *q = NULL;
1277
1278 r = chase_symlinks(p, t, CHASE_PREFIX_ROOT, &q);
1279 if (r < 0)
1280 continue;
1281
1282 fd = open(q, O_RDONLY|O_CLOEXEC|O_NOCTTY);
1283 if (fd >= 0)
1284 break;
1285 }
1286 if (fd < 0)
1287 continue;
1288
1289 r = copy_bytes(fd, fds[2*k+1], (uint64_t) -1, 0);
1290 if (r < 0)
1291 _exit(EXIT_FAILURE);
1292
1293 fds[2*k+1] = safe_close(fds[2*k+1]);
1294 }
1295
1296 _exit(EXIT_SUCCESS);
1297 }
1298
1299 for (k = 0; k < _META_MAX; k++) {
1300 _cleanup_fclose_ FILE *f = NULL;
1301
1302 fds[2*k+1] = safe_close(fds[2*k+1]);
1303
1304 f = fdopen(fds[2*k], "re");
1305 if (!f) {
1306 r = -errno;
1307 goto finish;
1308 }
1309
1310 fds[2*k] = -1;
1311
1312 switch (k) {
1313
1314 case META_HOSTNAME:
1315 r = read_etc_hostname_stream(f, &hostname);
1316 if (r < 0)
1317 log_debug_errno(r, "Failed to read /etc/hostname: %m");
1318
1319 break;
1320
1321 case META_MACHINE_ID: {
1322 _cleanup_free_ char *line = NULL;
1323
1324 r = read_line(f, LONG_LINE_MAX, &line);
1325 if (r < 0)
1326 log_debug_errno(r, "Failed to read /etc/machine-id: %m");
1327 else if (r == 33) {
1328 r = sd_id128_from_string(line, &machine_id);
1329 if (r < 0)
1330 log_debug_errno(r, "Image contains invalid /etc/machine-id: %s", line);
1331 } else if (r == 0)
1332 log_debug("/etc/machine-id file is empty.");
1333 else
1334 log_debug("/etc/machine-id has unexpected length %i.", r);
1335
1336 break;
1337 }
1338
1339 case META_MACHINE_INFO:
1340 r = load_env_file_pairs(f, "machine-info", NULL, &machine_info);
1341 if (r < 0)
1342 log_debug_errno(r, "Failed to read /etc/machine-info: %m");
1343
1344 break;
1345
1346 case META_OS_RELEASE:
1347 r = load_env_file_pairs(f, "os-release", NULL, &os_release);
1348 if (r < 0)
1349 log_debug_errno(r, "Failed to read OS release file: %m");
1350
1351 break;
1352 }
1353 }
1354
1355 r = wait_for_terminate(child, &si);
1356 if (r < 0)
1357 goto finish;
1358 child = 0;
1359
1360 if (si.si_code != CLD_EXITED || si.si_status != EXIT_SUCCESS) {
1361 r = -EPROTO;
1362 goto finish;
1363 }
1364
1365 free_and_replace(m->hostname, hostname);
1366 m->machine_id = machine_id;
1367 strv_free_and_replace(m->machine_info, machine_info);
1368 strv_free_and_replace(m->os_release, os_release);
1369
1370finish:
1371 for (k = 0; k < n_meta_initialized; k++)
1372 safe_close_pair(fds + 2*k);
1373
1374 return r;
1375}
1376
8c1be37e
LP
1377static const char *const partition_designator_table[] = {
1378 [PARTITION_ROOT] = "root",
1379 [PARTITION_ROOT_SECONDARY] = "root-secondary",
1380 [PARTITION_HOME] = "home",
1381 [PARTITION_SRV] = "srv",
1382 [PARTITION_ESP] = "esp",
1383 [PARTITION_SWAP] = "swap",
4623e8e6
LP
1384 [PARTITION_ROOT_VERITY] = "root-verity",
1385 [PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity",
8c1be37e
LP
1386};
1387
1388DEFINE_STRING_TABLE_LOOKUP(partition_designator, int);