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

systemd / systemd / 20766109442

06 Jan 2026 09:50PM UTC coverage: 72.714% (+0.3%) from 72.444%
20766109442

push

github

YHNdnzj
man: do not manually update man/rules/meson.build

Follow-up for 25393c7c9.

310283 of 426715 relevant lines covered (72.71%)

1142928.51 hits per line

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

78.51
/src/shared/dissect-image.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fnmatch.h>
4
#include <linux/loop.h>
5
#include <sys/file.h>
6
#include <sys/mount.h>
7
#include <unistd.h>
8

9
#if HAVE_OPENSSL
10
#include <openssl/err.h>
11
#include <openssl/pem.h>
12
#include <openssl/x509.h>
13
#endif
14

15
#include "sd-device.h"
16
#include "sd-id128.h"
17
#include "sd-json.h"
18
#include "sd-varlink.h"
19

20
#include "architecture.h"
21
#include "ask-password-api.h"
22
#include "blkid-util.h"
23
#include "blockdev-util.h"
24
#include "btrfs-util.h"
25
#include "chase.h"
26
#include "conf-files.h"
27
#include "constants.h"
28
#include "copy.h"
29
#include "cryptsetup-util.h"
30
#include "device-private.h"
31
#include "devnum-util.h"
32
#include "dissect-image.h"
33
#include "dm-util.h"
34
#include "env-file.h"
35
#include "env-util.h"
36
#include "errno-util.h"
37
#include "extension-util.h"
38
#include "extract-word.h"
39
#include "fd-util.h"
40
#include "fileio.h"
41
#include "format-util.h"
42
#include "fsck-util.h"
43
#include "gpt.h"
44
#include "hash-funcs.h"
45
#include "hexdecoct.h"
46
#include "hostname-setup.h"
47
#include "image-policy.h"
48
#include "import-util.h"
49
#include "io-util.h"
50
#include "iovec-util.h"
51
#include "json-util.h"
52
#include "libmount-util.h"
53
#include "loop-util.h"
54
#include "mkdir-label.h"
55
#include "mount-util.h"
56
#include "mountpoint-util.h"
57
#include "namespace-util.h"
58
#include "nulstr-util.h"
59
#include "openssl-util.h"
60
#include "os-util.h"
61
#include "path-util.h"
62
#include "pidref.h"
63
#include "proc-cmdline.h"
64
#include "process-util.h"
65
#include "resize-fs.h"
66
#include "runtime-scope.h"
67
#include "siphash24.h"
68
#include "stat-util.h"
69
#include "string-util.h"
70
#include "strv.h"
71
#include "time-util.h"
72
#include "udev-util.h"
73
#include "user-util.h"
74
#include "varlink-util.h"
75
#include "xattr-util.h"
76

77
/* how many times to wait for the device nodes to appear */
78
#define N_DEVICE_NODE_LIST_ATTEMPTS 10
79

80
static int allowed_fstypes(char ***ret_strv) {
2,625✔
81
        _cleanup_strv_free_ char **l = NULL;
2,625✔
82
        const char *e;
2,625✔
83

84
        assert(ret_strv);
2,625✔
85

86
        e = secure_getenv("SYSTEMD_DISSECT_FILE_SYSTEMS");
2,625✔
87
        if (e)
2,625✔
88
                l = strv_split(e, ":");
×
89
        else
90
                l = strv_new("btrfs",
2,625✔
91
                             "erofs",
92
                             "ext4",
93
                             "f2fs",
94
                             "squashfs",
95
                             "vfat",
96
                             "xfs");
97
        if (!l)
2,625✔
98
                return -ENOMEM;
99

100
        *ret_strv = TAKE_PTR(l);
2,625✔
101
        return 0;
2,625✔
102
}
103

104
int dissect_fstype_ok(const char *fstype) {
328✔
105
        _cleanup_strv_free_ char **l = NULL;
328✔
106
        int r;
328✔
107

108
        /* When we automatically mount file systems, be a bit conservative by default what we are willing to
109
         * mount, just as an extra safety net to not mount with badly maintained legacy file system
110
         * drivers. */
111

112
        r = allowed_fstypes(&l);
328✔
113
        if (r < 0)
328✔
114
                return r;
115

116
        if (strv_contains(l, fstype))
328✔
117
                return true;
118

119
        log_debug("File system type '%s' is not allowed to be mounted as result of automatic dissection.", fstype);
×
120
        return false;
121
}
122

123
#if HAVE_BLKID
124
static const char *getenv_fstype(PartitionDesignator d) {
642✔
125

126
        if (d < 0 ||
642✔
127
            partition_designator_is_verity(d) ||
504✔
128
            d == PARTITION_SWAP)
129
                return NULL;
130

131
        char *v = strjoina("SYSTEMD_DISSECT_FSTYPE_", partition_designator_to_string(d));
1,880✔
132
        return secure_getenv(ascii_strupper(v));
376✔
133
}
134
#endif
135

136
int probe_sector_size(int fd, uint32_t *ret) {
1,899✔
137

138
        /* Disk images might be for 512B or for 4096 sector sizes, let's try to auto-detect that by searching
139
         * for the GPT headers at the relevant byte offsets */
140

141
        assert_cc(sizeof(GptHeader) == 92);
1,899✔
142

143
        /* We expect a sector size in the range 512…4096. The GPT header is located in the second
144
         * sector. Hence it could be at byte 512 at the earliest, and at byte 4096 at the latest. And we must
145
         * read with granularity of the largest sector size we care about. Which means 8K. */
146
        uint8_t sectors[2 * 4096];
1,899✔
147
        uint32_t found = 0;
1,899✔
148
        ssize_t n;
1,899✔
149

150
        assert(fd >= 0);
1,899✔
151
        assert(ret);
1,899✔
152

153
        n = pread(fd, sectors, sizeof(sectors), 0);
1,899✔
154
        if (n < 0)
1,899✔
155
                return -errno;
×
156
        if (n != sizeof(sectors)) /* too short? */
1,899✔
157
                goto not_found;
616✔
158

159
        /* Let's see if we find the GPT partition header with various expected sector sizes */
160
        for (uint32_t sz = 512; sz <= 4096; sz <<= 1) {
6,415✔
161
                const GptHeader *p;
5,132✔
162

163
                assert(sizeof(sectors) >= sz * 2);
5,132✔
164
                p = (const GptHeader*) (sectors + sz);
5,132✔
165

166
                if (!gpt_header_has_signature(p))
5,132✔
167
                        continue;
4,985✔
168

169
                if (found != 0)
147✔
170
                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
×
171
                                               "Detected valid partition table at offsets matching multiple sector sizes, refusing.");
172

173
                found = sz;
174
        }
175

176
        if (found != 0) {
1,283✔
177
                log_debug("Determined sector size %" PRIu32 " based on discovered partition table.", found);
147✔
178
                *ret = found;
147✔
179
                return 1; /* indicate we *did* find it */
147✔
180
        }
181

182
not_found:
1,136✔
183
        log_debug("Couldn't find any partition table to derive sector size of.");
1,752✔
184
        *ret = 512; /* pick the traditional default */
1,752✔
185
        return 0;   /* indicate we didn't find it */
1,752✔
186
}
187

188
int probe_sector_size_prefer_ioctl(int fd, uint32_t *ret) {
314✔
189
        struct stat st;
314✔
190

191
        assert(fd >= 0);
314✔
192
        assert(ret);
314✔
193

194
        /* Just like probe_sector_size(), but if we are looking at a block device, will use the already
195
         * configured sector size rather than probing by contents */
196

197
        if (fstat(fd, &st) < 0)
314✔
198
                return -errno;
×
199

200
        if (S_ISBLK(st.st_mode))
314✔
201
                return blockdev_get_sector_size(fd, ret);
272✔
202

203
        return probe_sector_size(fd, ret);
42✔
204
}
205

206
#if HAVE_BLKID
207
static int probe_blkid_filter(blkid_probe p) {
2,297✔
208
        _cleanup_strv_free_ char **fstypes = NULL;
2,297✔
209
        int r;
2,297✔
210

211
        assert(p);
2,297✔
212

213
        r = allowed_fstypes(&fstypes);
2,297✔
214
        if (r < 0)
2,297✔
215
                return r;
216

217
        errno = 0;
2,297✔
218
        r = sym_blkid_probe_filter_superblocks_type(p, BLKID_FLTR_ONLYIN, fstypes);
2,297✔
219
        if (r != 0)
2,297✔
220
                return errno_or_else(EINVAL);
×
221

222
        errno = 0;
2,297✔
223
        r = sym_blkid_probe_filter_superblocks_usage(p, BLKID_FLTR_NOTIN, BLKID_USAGE_RAID);
2,297✔
224
        if (r != 0)
2,297✔
225
                return errno_or_else(EINVAL);
×
226

227
        return 0;
228
}
229
#endif
230

231
int probe_filesystem_full(
279✔
232
                int fd,
233
                const char *path,
234
                uint64_t offset,
235
                uint64_t size,
236
                bool restrict_fstypes,
237
                char **ret_fstype) {
238

239
        /* Try to find device content type and return it in *ret_fstype. If nothing is found,
240
         * 0/NULL will be returned. -EUCLEAN will be returned for ambiguous results, and a
241
         * different error otherwise. */
242

243
#if HAVE_BLKID
244
        _cleanup_(blkid_free_probep) blkid_probe b = NULL;
×
245
        _cleanup_free_ char *path_by_fd = NULL;
279✔
246
        _cleanup_close_ int fd_close = -EBADF;
279✔
247
        const char *fstype;
279✔
248
        int r;
279✔
249

250
        assert(fd >= 0 || path);
279✔
251
        assert(ret_fstype);
279✔
252

253
        r = dlopen_libblkid();
279✔
254
        if (r < 0)
279✔
255
                return r;
256

257
        if (fd < 0) {
279✔
258
                fd_close = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
×
259
                if (fd_close < 0)
×
260
                        return -errno;
×
261

262
                fd = fd_close;
263
        }
264

265
        if (!path) {
279✔
266
                r = fd_get_path(fd, &path_by_fd);
×
267
                if (r < 0)
×
268
                        return r;
269

270
                path = path_by_fd;
×
271
        }
272

273
        if (size == 0) /* empty size? nothing found! */
279✔
274
                goto not_found;
×
275

276
        b = sym_blkid_new_probe();
279✔
277
        if (!b)
279✔
278
                return -ENOMEM;
279

280
        if (restrict_fstypes) {
279✔
281
                r = probe_blkid_filter(b);
279✔
282
                if (r < 0)
279✔
283
                        return r;
284
        }
285

286
        /* The Linux kernel maintains separate block device caches for main ("whole") and partition block
287
         * devices, which means making a change to one might not be reflected immediately when reading via
288
         * the other. That's massively confusing when mixing accesses to such devices. Let's address this in
289
         * a limited way: when probing a file system that is not at the beginning of the block device we
290
         * apparently probe a partition via the main block device, and in that case let's first flush the
291
         * main block device cache, so that we get the data that the per-partition block device last
292
         * sync'ed on.
293
         *
294
         * This only works under the assumption that any tools that write to the partition block devices
295
         * issue an syncfs()/fsync() on the device after making changes. Typically file system formatting
296
         * tools that write a superblock onto a partition block device do that, however. */
297
        if (offset != 0)
279✔
298
                if (ioctl(fd, BLKFLSBUF, 0) < 0)
122✔
299
                        log_debug_errno(errno, "Failed to flush block device cache, ignoring: %m");
13✔
300

301
        errno = 0;
279✔
302
        r = sym_blkid_probe_set_device(
279✔
303
                        b,
304
                        fd,
305
                        offset,
306
                        size == UINT64_MAX ? 0 : size); /* when blkid sees size=0 it understands "everything". We prefer using UINT64_MAX for that */
307
        if (r != 0)
279✔
308
                return errno_or_else(ENOMEM);
×
309

310
        sym_blkid_probe_enable_superblocks(b, 1);
279✔
311
        sym_blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
279✔
312

313
        errno = 0;
279✔
314
        r = sym_blkid_do_safeprobe(b);
279✔
315
        if (r == _BLKID_SAFEPROBE_NOT_FOUND)
279✔
316
                goto not_found;
3✔
317
        if (r == _BLKID_SAFEPROBE_AMBIGUOUS)
276✔
318
                return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN),
×
319
                                       "Results ambiguous for partition %s", path);
320
        if (r == _BLKID_SAFEPROBE_ERROR)
276✔
321
                return log_debug_errno(errno_or_else(EIO), "Failed to probe partition %s: %m", path);
×
322

323
        assert(r == _BLKID_SAFEPROBE_FOUND);
276✔
324

325
        (void) sym_blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
276✔
326
        if (fstype) {
276✔
327
                log_debug("Probed fstype '%s' on partition %s.", fstype, path);
276✔
328
                return strdup_to_full(ret_fstype, fstype);
276✔
329
        }
330

331
not_found:
×
332
        log_debug("No type detected on partition %s", path);
3✔
333
        *ret_fstype = NULL;
3✔
334
        return 0;
3✔
335
#else
336
        return -EOPNOTSUPP;
337
#endif
338
}
339

340
#if HAVE_BLKID
341
static int image_policy_may_use(
2,279✔
342
                const ImagePolicy *policy,
343
                PartitionDesignator designator) {
344

345
        PartitionPolicyFlags f;
2,279✔
346

347
        /* For each partition we find in the partition table do a first check if it may exist at all given
348
         * the policy, or if it shall be ignored. */
349

350
        f = image_policy_get_exhaustively(policy, designator);
2,279✔
351
        if (f < 0)
2,279✔
352
                return f;
353

354
        if ((f & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_ABSENT)
2,279✔
355
                /* only flag set in policy is "absent"? then this partition may not exist at all */
356
                return log_debug_errno(
4✔
357
                                SYNTHETIC_ERRNO(ERFKILL),
358
                                "Partition of designator '%s' exists, but not allowed by policy, refusing.",
359
                                partition_designator_to_string(designator));
360
        if ((f & _PARTITION_POLICY_USE_MASK & ~PARTITION_POLICY_ABSENT) == PARTITION_POLICY_UNUSED) {
2,275✔
361
                /* only "unused" or "unused" + "absent" are set? then don't use it */
362
                log_debug("Partition of designator '%s' exists, and policy dictates to ignore it, doing so.",
27✔
363
                          partition_designator_to_string(designator));
364
                return false; /* ignore! */
27✔
365
        }
366

367
        return true; /* use! */
368
}
369

370
static int image_policy_check_protection(
4,889✔
371
                const ImagePolicy *policy,
372
                PartitionDesignator designator,
373
                PartitionPolicyFlags found_flags) {
374

375
        PartitionPolicyFlags policy_flags;
4,889✔
376

377
        /* Checks if the flags in the policy for the designated partition overlap the flags of what we found */
378

379
        if (found_flags < 0)
4,889✔
380
                return found_flags;
381

382
        policy_flags = image_policy_get_exhaustively(policy, designator);
4,889✔
383
        if (policy_flags < 0)
4,889✔
384
                return policy_flags;
385

386
        if ((found_flags & policy_flags) == 0) {
4,889✔
387
                _cleanup_free_ char *found_flags_string = NULL, *policy_flags_string = NULL;
4✔
388

389
                (void) partition_policy_flags_to_string(found_flags, /* simplify= */ true, &found_flags_string);
4✔
390
                (void) partition_policy_flags_to_string(policy_flags, /* simplify= */ true, &policy_flags_string);
4✔
391

392
                return log_debug_errno(SYNTHETIC_ERRNO(ERFKILL), "Partition %s discovered with policy '%s' but '%s' was required, refusing.",
4✔
393
                                       partition_designator_to_string(designator),
394
                                       strnull(found_flags_string), strnull(policy_flags_string));
395
        }
396

397
        return 0;
398
}
399

400
/* internal LUKS2 header defines */
401
#define LUKS2_FIXED_HDR_SIZE UINT64_C(0x1000)
402
#define LUKS2_MAGIC "LUKS\xba\xbe"
403

404
/* Matches the beginning of 'struct luks2_hdr_disk' from cryptsetup */
405
struct luks_header_incomplete {
406
                char luks_magic[sizeof(LUKS2_MAGIC) - 1];
407
                be16_t version;
408
                be64_t hdr_len;
409
};
410

411
/* 'integrity' information from LUKS JSON header. Currently, only 'type' is extracted/checked. */
412
struct luks_integrity_data {
413
        char *type;
414
};
415

416
static int integrity_information(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
1✔
417
        static const sd_json_dispatch_field table[] = {
1✔
418
                { "type", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(struct luks_integrity_data, type), SD_JSON_MANDATORY },
419
                {}
420
        };
421

422
        return sd_json_dispatch(v, table, flags, userdata);
1✔
423
}
424

425
/* cryptsetup needs a loop device to work with a partition which has offset/size but
426
 * dissect may be running unprivileged. Implement a minimal custom LUKS header parser
427
 * checking integrity protection information. */
428
static int partition_is_luks2_integrity(int part_fd, uint64_t offset, uint64_t size) {
5✔
429
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
5✔
430
        _cleanup_free_ char *json = NULL;
5✔
431
        sd_json_variant *w;
5✔
432
        const char *key;
5✔
433
        struct luks_header_incomplete header;
5✔
434
        ssize_t sz, json_len;
5✔
435
        int r;
5✔
436

437
        assert(part_fd >= 0);
5✔
438

439
        if (size < LUKS2_FIXED_HDR_SIZE) {
5✔
440
                log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Partition is too small to contain a LUKS header.");
×
441
                return 0;
×
442
        }
443

444
        sz = pread(part_fd, &header, sizeof(header), offset);
5✔
445
        if (sz < 0)
5✔
446
                return log_error_errno(errno, "Failed to read LUKS header.");
×
447
        if (sz != sizeof(header))
5✔
448
                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read LUKS header.");
×
449

450
        if (memcmp(header.luks_magic, LUKS2_MAGIC, sizeof(header.luks_magic)) != 0)
5✔
451
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Partition's magic is not LUKS.");
×
452

453
        if (be16toh(header.version) != 2)
5✔
454
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unsupported LUKS header version: %" PRIu16 ".", be16toh(header.version));
×
455

456
        if (be64toh(header.hdr_len) > size)
5✔
457
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "LUKS header length exceeds partition size.");
×
458

459
        if (be64toh(header.hdr_len) <= LUKS2_FIXED_HDR_SIZE || offset > UINT64_MAX - be64toh(header.hdr_len))
5✔
460
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid LUKS header length: %" PRIu64 ".", be64toh(header.hdr_len));
×
461

462
        json_len = be64toh(header.hdr_len) - LUKS2_FIXED_HDR_SIZE;
5✔
463
        json = malloc(json_len + 1);
5✔
464
        if (!json)
5✔
465
                return -ENOMEM;
466

467
        sz = pread(part_fd, json, json_len, offset + LUKS2_FIXED_HDR_SIZE);
5✔
468
        if (sz < 0)
5✔
469
                return log_error_errno(errno, "Failed to read LUKS JSON header.");
×
470
        if (sz != json_len)
5✔
471
                return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read LUKS JSON header.");
×
472
        json[sz] = '\0';
5✔
473

474
        r = sd_json_parse(json, /* flags = */ 0, &v, /* reterr_line = */ NULL, /* reterr_column = */ NULL);
5✔
475
        if (r < 0)
5✔
476
                return log_error_errno(r, "Failed to parse LUKS JSON header.");
×
477

478
        v = sd_json_variant_by_key(v, "segments");
5✔
479
        if (!v || !sd_json_variant_is_object(v)) {
5✔
480
                log_debug("LUKS JSON header lacks 'segments' information, assuming no integrity.");
×
481
                return 0;
×
482
        }
483

484
        /* Verify that all segments have integrity protection */
485
        JSON_VARIANT_OBJECT_FOREACH(key, w, v) {
6✔
486
                struct luks_integrity_data data = {};
5✔
487

488
                static const sd_json_dispatch_field dispatch_segment[] = {
5✔
489
                        { "integrity", SD_JSON_VARIANT_OBJECT, integrity_information, 0, SD_JSON_MANDATORY },
490
                        {}
491
                };
492

493
                r = sd_json_dispatch(w, dispatch_segment, SD_JSON_ALLOW_EXTENSIONS, &data);
5✔
494
                if (r < 0) {
5✔
495
                        log_debug("Failed to get integrity information from LUKS JSON for segment %s, assuming no integrity.", key);
4✔
496
                        return 0;
4✔
497
                }
498

499
                /* We don't require a particular integrity algorithm, everything but 'none' (which shouldn't
500
                 * be there in the first place but is theoretically possible) works. */
501
                if (streq(data.type, "none"))
1✔
502
                        return 0;
503
        }
504

505
        return 1;
1✔
506
}
507

508
static int image_policy_check_partition_flags(
2,227✔
509
                const ImagePolicy *policy,
510
                PartitionDesignator designator,
511
                uint64_t gpt_flags) {
512

513
        PartitionPolicyFlags policy_flags;
2,227✔
514
        bool b;
2,227✔
515

516
        /* Checks if the partition flags in the policy match reality */
517

518
        policy_flags = image_policy_get_exhaustively(policy, designator);
2,227✔
519
        if (policy_flags < 0)
2,227✔
520
                return policy_flags;
521

522
        b = FLAGS_SET(gpt_flags, SD_GPT_FLAG_READ_ONLY);
2,227✔
523
        if ((policy_flags & _PARTITION_POLICY_READ_ONLY_MASK) == (b ? PARTITION_POLICY_READ_ONLY_OFF : PARTITION_POLICY_READ_ONLY_ON))
4,382✔
524
                return log_debug_errno(SYNTHETIC_ERRNO(ERFKILL), "Partition %s has 'read-only' flag incorrectly set (must be %s, is %s), refusing.",
×
525
                                       partition_designator_to_string(designator),
526
                                       one_zero(!b), one_zero(b));
527

528
        b = FLAGS_SET(gpt_flags, SD_GPT_FLAG_GROWFS);
2,227✔
529
        if ((policy_flags & _PARTITION_POLICY_GROWFS_MASK) == (b ? PARTITION_POLICY_GROWFS_OFF : PARTITION_POLICY_GROWFS_ON))
4,328✔
530
                return log_debug_errno(SYNTHETIC_ERRNO(ERFKILL), "Partition %s has 'growfs' flag incorrectly set (must be %s, is %s), refusing.",
×
531
                                       partition_designator_to_string(designator),
532
                                       one_zero(!b), one_zero(b));
533

534
        return 0;
535
}
536

537
static int dissected_image_probe_filesystems(
203✔
538
                DissectedImage *m,
539
                int fd,
540
                const ImagePolicy *policy) {
541

542
        int r;
203✔
543

544
        assert(m);
203✔
545

546
        /* Fill in file system types if we don't know them yet. */
547

548
        for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
2,842✔
549
                DissectedPartition *p = m->partitions + i;
2,639✔
550
                PartitionPolicyFlags found_flags;
2,639✔
551

552
                if (!p->found)
2,639✔
553
                        continue;
2,161✔
554

555
                if (!p->fstype) {
478✔
556
                        /* If we have an fd referring to the partition block device, use that. Otherwise go
557
                         * via the whole block device or backing regular file, and read via offset. */
558
                        if (p->mount_node_fd >= 0)
215✔
559
                                r = probe_filesystem_full(p->mount_node_fd, p->node, 0, UINT64_MAX, /* bool restrict_fstypes= */ true, &p->fstype);
93✔
560
                        else
561
                                r = probe_filesystem_full(fd, p->node, p->offset, p->size, /* bool restrict_fstypes= */ true, &p->fstype);
122✔
562
                        if (r < 0)
215✔
563
                                return r;
564
                }
565

566
                if (streq_ptr(p->fstype, "crypto_LUKS")) {
478✔
567
                        m->encrypted = true;
5✔
568

569
                        if (p->mount_node_fd >= 0)
5✔
570
                                r = partition_is_luks2_integrity(p->mount_node_fd, /* offset = */ 0, /* size = */ UINT64_MAX);
3✔
571
                        else
572
                                r = partition_is_luks2_integrity(fd, p->offset, p->size);
2✔
573
                        if (r < 0)
5✔
574
                                return r;
575

576
                        /* found this one, it's definitely encrypted + with or without integrity checking */
577
                        found_flags = PARTITION_POLICY_UNUSED|(r > 0 ? PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY : PARTITION_POLICY_ENCRYPTED);
5✔
578
                } else
579
                        /* found it, but it's definitely not encrypted, hence mask the encrypted flag, but
580
                         * set all other ways that indicate "present". */
581
                        found_flags = PARTITION_POLICY_UNUSED|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED;
582

583
                if (p->fstype && fstype_is_ro(p->fstype))
478✔
584
                        p->rw = false;
96✔
585

586
                if (!p->rw)
478✔
587
                        p->growfs = false;
157✔
588

589
                /* We might have learnt more about the file system now (i.e. whether it is encrypted or not),
590
                 * hence we need to validate this against policy again, to see if the policy still matches
591
                 * with this new information. Note that image_policy_check_protection() will check for
592
                 * overlap between what's allowed in the policy and what we pass as 'found_policy' here. In
593
                 * the unencrypted case we thus might pass an overly unspecific mask here (i.e. unprotected
594
                 * OR verity OR signed), but that's fine since the earlier policy check already checked more
595
                 * specific which of those three cases where OK. Keep in mind that this function here only
596
                 * looks at specific partitions (and thus can only deduce encryption or not) but not the
597
                 * overall partition table (and thus cannot deduce verity or not). The earlier dissection
598
                 * checks already did the relevant checks that look at the whole partition table, and
599
                 * enforced policy there as needed. */
600
                r = image_policy_check_protection(policy, i, found_flags);
478✔
601
                if (r < 0)
478✔
602
                        return r;
603
        }
604

605
        return 0;
606
}
607

608
static void check_partition_flags(
505✔
609
                const char *node,
610
                unsigned long long pflags,
611
                unsigned long long supported) {
612

613
        assert(node);
505✔
614

615
        /* Mask away all flags supported by this partition's type and the three flags the UEFI spec defines generically */
616
        pflags &= ~(supported |
505✔
617
                    SD_GPT_FLAG_REQUIRED_PARTITION |
618
                    SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL |
505✔
619
                    SD_GPT_FLAG_LEGACY_BIOS_BOOTABLE);
620

621
        if (pflags == 0)
505✔
622
                return;
623

624
        /* If there are other bits set, then log about it, to make things discoverable */
625
        for (unsigned i = 0; i < sizeof(pflags) * 8; i++) {
×
626
                unsigned long long bit = 1ULL << i;
×
627
                if (!FLAGS_SET(pflags, bit))
×
628
                        continue;
×
629

630
                log_debug("Unexpected partition flag %llu set on %s!", bit, node);
×
631
        }
632
}
633
#endif
634

635
static int make_image_name(const char *path, char **ret) {
2,030✔
636
        int r;
2,030✔
637

638
        assert(path);
2,030✔
639
        assert(ret);
2,030✔
640

641
        _cleanup_free_ char *filename = NULL;
2,030✔
642
        r = path_extract_filename(path, &filename);
2,030✔
643
        if (r < 0)
2,030✔
644
                return r;
645

646
        _cleanup_free_ char *name = NULL;
2,030✔
647
        r = raw_strip_suffixes(filename, &name);
2,030✔
648
        if (r < 0)
2,030✔
649
                return r;
650

651
        if (!image_name_is_valid(name)) {
2,030✔
652
                log_debug("Image name %s is not valid, ignoring.", strna(name));
1✔
653
                *ret = NULL;
1✔
654
                return 0;
1✔
655
        }
656

657
        *ret = TAKE_PTR(name);
2,029✔
658
        return 1;
2,029✔
659
}
660

661
#if HAVE_BLKID
662
static int dissected_image_new(const char *path, DissectedImage **ret) {
2,030✔
663
        _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
×
664
        _cleanup_free_ char *name = NULL;
2,030✔
665
        int r;
2,030✔
666

667
        assert(ret);
2,030✔
668

669
        if (path) {
2,030✔
670
                r = make_image_name(path, &name);
2,018✔
671
                if (r < 0)
2,018✔
672
                        return r;
673
        }
674

675
        m = new(DissectedImage, 1);
2,030✔
676
        if (!m)
2,030✔
677
                return -ENOMEM;
678

679
        *m = (DissectedImage) {
2,030✔
680
                .has_init_system = -1,
681
                .image_name = TAKE_PTR(name),
2,030✔
682
        };
683

684
        for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++)
28,420✔
685
                m->partitions[i] = DISSECTED_PARTITION_NULL;
26,390✔
686

687
        *ret = TAKE_PTR(m);
2,030✔
688
        return 0;
2,030✔
689
}
690
#endif
691

692
static void dissected_partition_done(DissectedPartition *p) {
25,611✔
693
        assert(p);
25,611✔
694

695
        free(p->fstype);
25,611✔
696
        free(p->node);
25,611✔
697
        free(p->label);
25,611✔
698
        free(p->decrypted_fstype);
25,611✔
699
        free(p->decrypted_node);
25,611✔
700
        free(p->mount_options);
25,611✔
701
        safe_close(p->mount_node_fd);
25,611✔
702
        safe_close(p->fsmount_fd);
25,611✔
703

704
        *p = DISSECTED_PARTITION_NULL;
25,611✔
705
}
25,611✔
706

707
static int acquire_sig_for_roothash(
64✔
708
                int fd,
709
                uint64_t partition_offset,
710
                uint64_t partition_size,
711
                struct iovec *ret_root_hash,
712
                struct iovec *ret_root_hash_sig) {
713

714
        int r;
64✔
715

716
        assert(fd >= 0);
64✔
717

718
        if (partition_offset == UINT64_MAX || partition_size == UINT64_MAX)
64✔
719
                return -EINVAL;
64✔
720

721
        if (partition_size > 4*1024*1024) /* Signature data cannot possible be larger than 4M, refuse that */
64✔
722
                return log_debug_errno(SYNTHETIC_ERRNO(EFBIG), "Verity signature partition is larger than 4M, refusing.");
×
723

724
        _cleanup_free_ char *buf = new(char, partition_size+1);
128✔
725
        if (!buf)
64✔
726
                return -ENOMEM;
727

728
        ssize_t n = pread(fd, buf, partition_size, partition_offset);
64✔
729
        if (n < 0)
64✔
730
                return -ENOMEM;
731
        if ((uint64_t) n != partition_size)
64✔
732
                return -EIO;
733

734
        const char *e = memchr(buf, 0, partition_size);
64✔
735
        if (e) {
64✔
736
                /* If we found a NUL byte then the rest of the data must be NUL too */
737
                if (!memeqzero(e, partition_size - (e - buf)))
64✔
738
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature data contains embedded NUL byte.");
×
739
        } else
740
                buf[partition_size] = 0;
×
741

742
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
64✔
743
        r = sd_json_parse(buf, 0, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
64✔
744
        if (r < 0)
64✔
745
                return log_debug_errno(r, "Failed to parse signature JSON data: %m");
×
746

747
        sd_json_variant *rh = sd_json_variant_by_key(v, "rootHash");
64✔
748
        if (!rh)
64✔
749
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature JSON object lacks 'rootHash' field.");
×
750

751
        _cleanup_(iovec_done) struct iovec root_hash = {};
64✔
752
        r = sd_json_variant_unhex(rh, &root_hash.iov_base, &root_hash.iov_len);
64✔
753
        if (r < 0)
64✔
754
                return log_debug_errno(r, "Failed to parse root hash field: %m");
×
755

756
        sd_json_variant *sig = sd_json_variant_by_key(v, "signature");
64✔
757
        if (!sig)
64✔
758
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature JSON object lacks 'signature' field.");
×
759

760
        _cleanup_(iovec_done) struct iovec root_hash_sig = {};
64✔
761
        r = sd_json_variant_unbase64(sig, &root_hash_sig.iov_base, &root_hash_sig.iov_len);
64✔
762
        if (r < 0)
64✔
763
                return log_debug_errno(r, "Failed to parse signature field: %m");
×
764

765
        if (ret_root_hash)
64✔
766
                *ret_root_hash = TAKE_STRUCT(root_hash);
64✔
767

768
        if (ret_root_hash_sig)
64✔
769
                *ret_root_hash_sig = TAKE_STRUCT(root_hash_sig);
43✔
770

771
        return 0;
772
}
773

774
#if HAVE_BLKID
775
static int diskseq_should_be_used(
2,889✔
776
                const char *whole_devname,
777
                uint64_t diskseq,
778
                DissectImageFlags flags) {
779

780
        int r;
2,889✔
781

782
        assert(whole_devname);
2,889✔
783

784
        /* No diskseq. We cannot use by-diskseq symlink. */
785
        if (diskseq == 0)
2,889✔
786
                return false;
2,889✔
787

788
        /* Do not use by-diskseq link unless DISSECT_IMAGE_DISKSEQ_DEVNODE flag is explicitly set. */
789
        if (!FLAGS_SET(flags, DISSECT_IMAGE_DISKSEQ_DEVNODE))
2,811✔
790
                return false;
791

792
        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
2,889✔
793
        r = sd_device_new_from_devname(&dev, whole_devname);
×
794
        if (r < 0)
×
795
                return r;
796

797
        /* When ID_IGNORE_DISKSEQ udev property is set, the by-diskseq symlink will not be created. */
798
        r = device_get_property_bool(dev, "ID_IGNORE_DISKSEQ");
×
799
        if (r >= 0)
×
800
                return !r; /* If explicitly specified, use it. */
×
801
        if (r != -ENOENT)
×
802
                return r;
×
803

804
        return true;
805
}
806

807
static int make_partition_devname(
2,889✔
808
                const char *whole_devname,
809
                uint64_t diskseq,
810
                int nr,
811
                DissectImageFlags flags,
812
                char **ret) {
813

814
        _cleanup_free_ char *s = NULL;
2,889✔
815
        int r;
2,889✔
816

817
        assert(whole_devname);
2,889✔
818
        assert(nr != 0); /* zero is not a valid partition nr */
2,889✔
819
        assert(ret);
2,889✔
820

821
        r = diskseq_should_be_used(whole_devname, diskseq, flags);
2,889✔
822
        if (r < 0)
2,889✔
823
                log_debug_errno(r, "Failed to determine if diskseq should be used for %s, assuming no, ignoring: %m", whole_devname);
×
824
        if (r <= 0) {
2,889✔
825
                /* Given a whole block device node name (e.g. /dev/sda or /dev/loop7) generate a partition
826
                 * device name (e.g. /dev/sda7 or /dev/loop7p5). The rule the kernel uses is simple: if whole
827
                 * block device node name ends in a digit, then suffix a 'p', followed by the partition
828
                 * number. Otherwise, just suffix the partition number without any 'p'. */
829

830
                if (nr < 0) { /* whole disk? */
2,889✔
831
                        s = strdup(whole_devname);
1,749✔
832
                        if (!s)
1,749✔
833
                                return -ENOMEM;
834
                } else {
835
                        r = partition_node_of(whole_devname, nr, &s);
1,140✔
836
                        if (r < 0)
1,140✔
837
                                return r;
838
                }
839
        } else {
840
                if (nr < 0) /* whole disk? */
×
841
                        r = asprintf(&s, "/dev/disk/by-diskseq/%" PRIu64, diskseq);
×
842
                else
843
                        r = asprintf(&s, "/dev/disk/by-diskseq/%" PRIu64 "-part%i", diskseq, nr);
×
844
                if (r < 0)
×
845
                        return -ENOMEM;
846
        }
847

848
        *ret = TAKE_PTR(s);
2,889✔
849
        return 0;
2,889✔
850
}
851

852
static int open_partition(
2,002✔
853
                const char *node,
854
                bool is_partition,
855
                const LoopDevice *loop) {
856

857
        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
2,002✔
858
        _cleanup_close_ int fd = -EBADF;
2,002✔
859
        dev_t devnum;
2,002✔
860
        int r;
2,002✔
861

862
        assert(node);
2,002✔
863
        assert(loop || !is_partition);
2,002✔
864

865
        fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
2,002✔
866
        if (fd < 0)
2,002✔
867
                return -errno;
×
868

869
        if (!loop)
2,002✔
870
                return TAKE_FD(fd);
2,002✔
871

872
        /* Check if the block device is a child of (or equivalent to) the originally provided one. */
873
        r = block_device_new_from_fd(fd, is_partition ? BLOCK_DEVICE_LOOKUP_WHOLE_DISK : 0, &dev);
2,002✔
874
        if (r < 0)
2,002✔
875
                return r;
876

877
        r = sd_device_get_devnum(dev, &devnum);
2,002✔
878
        if (r < 0)
2,002✔
879
                return r;
880

881
        if (loop->devno != devnum)
2,002✔
882
                return -ENXIO;
883

884
        /* Also check diskseq. */
885
        if (loop->diskseq != 0) {
2,002✔
886
                uint64_t diskseq;
2,002✔
887

888
                r = fd_get_diskseq(fd, &diskseq);
2,002✔
889
                if (r < 0)
2,002✔
890
                        return r;
×
891

892
                if (loop->diskseq != diskseq)
2,002✔
893
                        return -ENXIO;
894
        }
895

896
        log_debug("Opened %s (fd=%i, whole_block_devnum=" DEVNUM_FORMAT_STR ", diskseq=%" PRIu64 ").",
2,002✔
897
                  node, fd, DEVNUM_FORMAT_VAL(loop->devno), loop->diskseq);
898
        return TAKE_FD(fd);
899
}
900

901
static int compare_arch(Architecture a, Architecture b) {
14✔
902
        if (a == b)
14✔
903
                return 0;
904

905
        if (a == native_architecture())
×
906
                return 1;
907

908
        if (b == native_architecture())
×
909
                return -1;
910

911
#ifdef ARCHITECTURE_SECONDARY
912
        if (a == ARCHITECTURE_SECONDARY)
×
913
                return 1;
914

915
        if (b == ARCHITECTURE_SECONDARY)
×
916
                return -1;
×
917
#endif
918

919
        return 0;
920
}
921

922
static bool image_filter_test(const ImageFilter *filter, PartitionDesignator d, const char *label) {
2,530✔
923
        assert(d < _PARTITION_DESIGNATOR_MAX);
2,530✔
924

925
        if (d < 0) /* For unspecified designators we have no filter expression */
2,530✔
926
                return true;
927

928
        if (!filter || !filter->pattern[d])
2,392✔
929
                return true;
930

931
        return fnmatch(filter->pattern[d], strempty(label),  FNM_NOESCAPE) == 0;
×
932
}
933

934
static int dissect_image_from_unpartitioned(
1,750✔
935
                const char *devname,
936
                uint64_t diskseq,
937
                const sd_id128_t *uuid,
938
                bool encrypted,
939
                const VeritySettings *verity,
940
                const MountOptions *mount_options,
941
                const ImagePolicy *policy,
942
                const ImageFilter *filter,
943
                int *mount_node_fd, /* taken over on success */
944
                char **fstype, /* taken over on success */
945
                DissectedImage *m,
946
                DissectImageFlags flags,
947
                PartitionPolicyFlags found_flags) {
948

949
        _cleanup_free_ char *n = NULL, *o = NULL;
1,750✔
950
        const char *options = NULL;
1,750✔
951
        int r;
1,750✔
952

953
        assert(devname);
1,750✔
954
        assert(m);
1,750✔
955
        assert(fstype);
1,750✔
956

957
        if (!image_filter_test(filter, PARTITION_ROOT, /* label= */ NULL)) /* do a filter check with an empty partition label */
1,750✔
958
                return -ECOMM;
959

960
        r = image_policy_may_use(policy, PARTITION_ROOT);
1,750✔
961
        if (r < 0)
1,750✔
962
                return r;
963
        if (r == 0) /* policy says ignore this, so we ignore it */
1,750✔
964
                return -ENOPKG;
965

966
        r = image_policy_check_protection(policy, PARTITION_ROOT, found_flags);
1,750✔
967
        if (r < 0)
1,750✔
968
                return r;
969

970
        r = image_policy_check_partition_flags(policy, PARTITION_ROOT, 0); /* we have no gpt partition flags, hence check against all bits off */
1,749✔
971
        if (r < 0)
1,749✔
972
                return r;
973

974
        r = make_partition_devname(devname, diskseq, /* nr= */ -1, flags, &n);
1,749✔
975
        if (r < 0)
1,749✔
976
                return r;
977

978
        m->single_file_system = true;
1,749✔
979
        m->encrypted = encrypted;
1,749✔
980

981
        m->has_verity = verity && verity->data_path;
1,749✔
982
        m->verity_ready = verity_settings_data_covers(verity, PARTITION_ROOT);
1,749✔
983

984
        m->has_verity_sig = false; /* signature not embedded, must be specified */
1,749✔
985
        m->verity_sig_ready = m->verity_ready && iovec_is_set(&verity->root_hash);
1,749✔
986

987
        if (uuid)
1,749✔
988
                m->image_uuid = *uuid;
1,749✔
989

990
        options = mount_options_from_designator(mount_options, PARTITION_ROOT);
1,749✔
991
        if (options) {
1,749✔
992
                o = strdup(options);
3✔
993
                if (!o)
3✔
994
                        return -ENOMEM;
995
        }
996

997
        m->partitions[PARTITION_ROOT] = (DissectedPartition) {
3,498✔
998
                .found = true,
999
                .rw = !m->verity_ready && !fstype_is_ro(*fstype),
1,749✔
1000
                .partno = -1,
1001
                .architecture = _ARCHITECTURE_INVALID,
1002
                .fstype = TAKE_PTR(*fstype),
1,749✔
1003
                .node = TAKE_PTR(n),
1,749✔
1004
                .mount_options = TAKE_PTR(o),
1,749✔
1005
                .mount_node_fd = mount_node_fd ? TAKE_FD(*mount_node_fd) : -EBADF,
1,749✔
1006
                .size = UINT64_MAX,
1007
                .fsmount_fd = -EBADF,
1008
        };
1009

1010
        return 0;
1,749✔
1011
}
1012

1013
static int dissect_image(
2,018✔
1014
                DissectedImage *m,
1015
                int fd,
1016
                const char *devname,
1017
                const VeritySettings *verity,
1018
                const MountOptions *mount_options,
1019
                const ImagePolicy *policy,
1020
                const ImageFilter *filter,
1021
                DissectImageFlags flags) {
1022

1023
        sd_id128_t root_uuid = SD_ID128_NULL, root_verity_uuid = SD_ID128_NULL;
2,018✔
1024
        sd_id128_t usr_uuid = SD_ID128_NULL, usr_verity_uuid = SD_ID128_NULL;
2,018✔
1025
        bool is_gpt, is_mbr, multiple_generic = false,
2,018✔
1026
                generic_rw = false,  /* initialize to appease gcc */
2,018✔
1027
                generic_growfs = false;
2,018✔
1028
        _cleanup_(blkid_free_probep) blkid_probe b = NULL;
×
1029
        _cleanup_free_ char *generic_node = NULL;
2,018✔
1030
        sd_id128_t generic_uuid = SD_ID128_NULL;
2,018✔
1031
        const char *pttype = NULL;
2,018✔
1032
        blkid_partlist pl;
2,018✔
1033
        int r, generic_nr = -1, n_partitions;
2,018✔
1034

1035
        assert(m);
2,018✔
1036
        assert(fd >= 0);
2,018✔
1037
        assert(devname);
2,018✔
1038
        assert(!verity || verity->designator < 0 || IN_SET(verity->designator, PARTITION_ROOT, PARTITION_USR));
2,018✔
1039
        assert(!verity || iovec_is_valid(&verity->root_hash));
2,018✔
1040
        assert(!verity || iovec_is_valid(&verity->root_hash_sig));
422✔
1041
        assert(!verity || iovec_is_set(&verity->root_hash) || !iovec_is_set(&verity->root_hash_sig));
422✔
1042
        assert(!((flags & DISSECT_IMAGE_GPT_ONLY) && (flags & DISSECT_IMAGE_NO_PARTITION_TABLE)));
2,018✔
1043
        assert(m->sector_size > 0);
2,018✔
1044

1045
        /* Probes a disk image, and returns information about what it found in *ret.
1046
         *
1047
         * Returns -ENOPKG if no suitable partition table or file system could be found.
1048
         * Returns -EADDRNOTAVAIL if a root hash was specified but no matching root/verity partitions found.
1049
         * Returns -ENXIO if we couldn't find any partition suitable as root or /usr partition
1050
         * Returns -ENOTUNIQ if we only found multiple generic partitions and thus don't know what to do with that
1051
         * Returns -ERFKILL if image doesn't match image policy
1052
         * Returns -EBADR if verity data was provided externally for an image that has a GPT partition table (i.e. is not just a naked fs)
1053
         * Returns -EPROTONOSUPPORT if DISSECT_IMAGE_ADD_PARTITION_DEVICES is set but the block device does not have partition logic enabled
1054
         * Returns -ENOMSG if we didn't find a single usable partition (and DISSECT_IMAGE_REFUSE_EMPTY is set)
1055
         * Returns -EUCLEAN if some file system had an ambiguous file system superblock signature
1056
         */
1057

1058
        uint64_t diskseq = m->loop ? m->loop->diskseq : 0;
2,018✔
1059

1060
        if (verity && iovec_is_set(&verity->root_hash)) {
2,018✔
1061
                sd_id128_t fsuuid, vuuid;
66✔
1062

1063
                /* If a root hash is supplied, then we use the root partition that has a UUID that match the
1064
                 * first 128-bit of the root hash. And we use the verity partition that has a UUID that match
1065
                 * the final 128-bit. */
1066

1067
                if (verity->root_hash.iov_len < sizeof(sd_id128_t))
66✔
1068
                        return -EINVAL;
×
1069

1070
                memcpy(&fsuuid, verity->root_hash.iov_base, sizeof(sd_id128_t));
66✔
1071
                memcpy(&vuuid, (const uint8_t*) verity->root_hash.iov_base + verity->root_hash.iov_len - sizeof(sd_id128_t), sizeof(sd_id128_t));
66✔
1072

1073
                if (sd_id128_is_null(fsuuid))
66✔
1074
                        return -EINVAL;
×
1075
                if (sd_id128_is_null(vuuid))
66✔
1076
                        return -EINVAL;
×
1077

1078
                /* If the verity data declares it's for the /usr partition, then search for that, in all
1079
                 * other cases assume it's for the root partition. */
1080
                if (verity->designator == PARTITION_USR) {
66✔
1081
                        usr_uuid = fsuuid;
×
1082
                        usr_verity_uuid = vuuid;
×
1083
                } else {
1084
                        root_uuid = fsuuid;
66✔
1085
                        root_verity_uuid = vuuid;
66✔
1086
                }
1087
        }
1088

1089
        r = dlopen_libblkid();
2,018✔
1090
        if (r < 0)
2,018✔
1091
                return r;
1092

1093
        b = sym_blkid_new_probe();
2,018✔
1094
        if (!b)
2,018✔
1095
                return -ENOMEM;
1096

1097
        r = probe_blkid_filter(b);
2,018✔
1098
        if (r < 0)
2,018✔
1099
                return r;
1100

1101
        errno = 0;
2,018✔
1102
        r = sym_blkid_probe_set_device(b, fd, 0, 0);
2,018✔
1103
        if (r != 0)
2,018✔
1104
                return errno_or_else(ENOMEM);
×
1105

1106
        errno = 0;
2,018✔
1107
        r = sym_blkid_probe_set_sectorsize(b, m->sector_size);
2,018✔
1108
        if (r != 0)
2,018✔
1109
                return errno_or_else(EIO);
×
1110

1111
        if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
2,018✔
1112
                /* Look for file system superblocks, unless we only shall look for GPT partition tables */
1113
                sym_blkid_probe_enable_superblocks(b, 1);
1,909✔
1114
                sym_blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE|BLKID_SUBLKS_UUID);
1,909✔
1115
        }
1116

1117
        sym_blkid_probe_enable_partitions(b, 1);
2,018✔
1118
        sym_blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
2,018✔
1119

1120
        errno = 0;
2,018✔
1121
        r = sym_blkid_do_safeprobe(b);
2,018✔
1122
        if (r == _BLKID_SAFEPROBE_ERROR)
2,018✔
1123
                return errno_or_else(EIO);
×
1124
        if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND))
2,018✔
1125
                return log_debug_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to identify any partition table.");
×
1126

1127
        assert(r == _BLKID_SAFEPROBE_FOUND);
2,018✔
1128

1129
        if ((!(flags & DISSECT_IMAGE_GPT_ONLY) &&
2,018✔
1130
            (flags & DISSECT_IMAGE_GENERIC_ROOT)) ||
1,909✔
1131
            (flags & DISSECT_IMAGE_NO_PARTITION_TABLE)) {
237✔
1132
                _cleanup_free_ char *root_fstype_string = NULL;
1,850✔
1133
                const char *usage = NULL;
1,850✔
1134
                bool encrypted;
1,850✔
1135

1136
                r = partition_policy_determine_fstype(policy, PARTITION_ROOT, &encrypted, &root_fstype_string);
1,850✔
1137
                if (r < 0)
1,850✔
1138
                        return r;
1139

1140
                /* If flags permit this, also allow using non-partitioned single-filesystem images */
1141

1142
                if (root_fstype_string)
1,850✔
1143
                        usage = encrypted ? "crypto" : "filesystem";
×
1144
                else
1145
                        (void) sym_blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
1,850✔
1146
                if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
1,850✔
1147
                        _cleanup_free_ char *t = NULL;
1,750✔
1148
                        const char *fstype = NULL;
1,750✔
1149
                        _cleanup_close_ int mount_node_fd = -EBADF;
1,750✔
1150
                        sd_id128_t uuid = SD_ID128_NULL;
1,750✔
1151
                        PartitionPolicyFlags found_flags;
1,750✔
1152

1153
                        /* OK, we have found a file system, that's our root partition then. */
1154

1155
                        if (!root_fstype_string) {
1,750✔
1156
                                (void) sym_blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
1,750✔
1157

1158
                                /* blkid will return FAT's serial number as UUID, hence it is quite possible that
1159
                                * parsing this will fail. We'll ignore the ID, since it's just too short to be
1160
                                * useful as true identifier. */
1161
                                (void) blkid_probe_lookup_value_id128(b, "UUID", &uuid);
1,750✔
1162
                        } else
1163
                                /* The policy fstype flags translate to the literal fstype name of each filesystem. */
1164
                                fstype = root_fstype_string;
×
1165

1166
                        encrypted = encrypted || streq_ptr(fstype, "crypto_LUKS");
1,750✔
1167

1168
                        if (verity_settings_data_covers(verity, PARTITION_ROOT))
1,750✔
1169
                                found_flags = iovec_is_set(&verity->root_hash_sig) ? PARTITION_POLICY_SIGNED : PARTITION_POLICY_VERITY;
86✔
1170
                        else if (encrypted) {
1,707✔
1171
                                r = partition_is_luks2_integrity(fd, /* offset = */ 0, /* size = */ UINT64_MAX);
×
1172
                                if (r < 0)
×
1173
                                        return r;
1174

1175
                                found_flags = r > 0 ? PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY : PARTITION_POLICY_ENCRYPTED;
×
1176
                        } else
1177
                                found_flags = PARTITION_POLICY_UNPROTECTED;
1178

1179
                        if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES)) {
1,750✔
1180
                                mount_node_fd = open_partition(devname, /* is_partition= */ false, m->loop);
1,750✔
1181
                                if (mount_node_fd < 0)
1,750✔
1182
                                        return mount_node_fd;
1183
                        }
1184

1185
                        if (fstype) {
1,750✔
1186
                                t = strdup(fstype);
1,750✔
1187
                                if (!t)
1,750✔
1188
                                        return -ENOMEM;
1189
                        }
1190

1191
                        return dissect_image_from_unpartitioned(
1,750✔
1192
                                        devname,
1193
                                        diskseq,
1194
                                        &uuid,
1195
                                        encrypted,
1196
                                        verity,
1197
                                        mount_options,
1198
                                        policy,
1199
                                        filter,
1200
                                        &mount_node_fd,
1201
                                        &t,
1202
                                        m,
1203
                                        flags,
1204
                                        found_flags);
1205
                }
1206
        }
1207

1208
        (void) sym_blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
268✔
1209
        if (!pttype)
268✔
1210
                return -ENOPKG;
1211

1212
        is_gpt = streq_ptr(pttype, "gpt");
214✔
1213
        is_mbr = streq_ptr(pttype, "dos");
214✔
1214

1215
        if (!is_gpt && ((flags & DISSECT_IMAGE_GPT_ONLY) || !is_mbr))
214✔
1216
                return -ENOPKG;
1217

1218
        /* We support external verity data partitions only if the image has no partition table */
1219
        if (verity && verity->data_path)
214✔
1220
                return -EBADR;
1221

1222
        if (FLAGS_SET(flags, DISSECT_IMAGE_ADD_PARTITION_DEVICES)) {
214✔
1223
                /* Safety check: refuse block devices that carry a partition table but for which the kernel doesn't
1224
                 * do partition scanning. */
1225
                r = blockdev_partscan_enabled_fd(fd);
89✔
1226
                if (r < 0)
89✔
1227
                        return r;
1228
                if (r == 0)
89✔
1229
                        return -EPROTONOSUPPORT;
1230
        }
1231

1232
        (void) blkid_probe_lookup_value_id128(b, "PTUUID", &m->image_uuid);
214✔
1233

1234
        errno = 0;
214✔
1235
        pl = sym_blkid_probe_get_partitions(b);
214✔
1236
        if (!pl)
214✔
1237
                return errno_or_else(ENOMEM);
×
1238

1239
        errno = 0;
214✔
1240
        n_partitions = sym_blkid_partlist_numof_partitions(pl);
214✔
1241
        if (n_partitions < 0)
214✔
1242
                return errno_or_else(EIO);
×
1243

1244
        for (int i = 0; i < n_partitions; i++) {
852✔
1245
                _cleanup_free_ char *node = NULL;
642✔
1246
                unsigned long long pflags;
642✔
1247
                blkid_loff_t start, size;
642✔
1248
                blkid_partition pp;
642✔
1249
                int nr;
642✔
1250

1251
                errno = 0;
642✔
1252
                pp = sym_blkid_partlist_get_partition(pl, i);
642✔
1253
                if (!pp)
642✔
1254
                        return errno_or_else(EIO);
×
1255

1256
                pflags = sym_blkid_partition_get_flags(pp);
642✔
1257

1258
                errno = 0;
642✔
1259
                nr = sym_blkid_partition_get_partno(pp);
642✔
1260
                if (nr < 0)
642✔
1261
                        return errno_or_else(EIO);
×
1262

1263
                errno = 0;
642✔
1264
                start = sym_blkid_partition_get_start(pp);
642✔
1265
                if (start < 0)
642✔
1266
                        return errno_or_else(EIO);
×
1267

1268
                assert((uint64_t) start < UINT64_MAX/512);
642✔
1269

1270
                errno = 0;
642✔
1271
                size = sym_blkid_partition_get_size(pp);
642✔
1272
                if (size < 0)
642✔
1273
                        return errno_or_else(EIO);
×
1274

1275
                assert((uint64_t) size < UINT64_MAX/512);
642✔
1276

1277
                /* While probing we need the non-diskseq device node name to access the thing, hence mask off
1278
                 * DISSECT_IMAGE_DISKSEQ_DEVNODE. */
1279
                r = make_partition_devname(devname, diskseq, nr, flags & ~DISSECT_IMAGE_DISKSEQ_DEVNODE, &node);
642✔
1280
                if (r < 0)
642✔
1281
                        return r;
1282

1283
                /* So here's the thing: after the main ("whole") block device popped up it might take a while
1284
                 * before the kernel fully probed the partition table. Waiting for that to finish is icky in
1285
                 * userspace. So here's what we do instead. We issue the BLKPG_ADD_PARTITION ioctl to add the
1286
                 * partition ourselves, racing against the kernel. Good thing is: if this call fails with
1287
                 * EBUSY then the kernel was quicker than us, and that's totally OK, the outcome is good for
1288
                 * us: the device node will exist. If OTOH our call was successful we won the race. Which is
1289
                 * also good as the outcome is the same: the partition block device exists, and we can use
1290
                 * it.
1291
                 *
1292
                 * Kernel returns EBUSY if there's already a partition by that number or an overlapping
1293
                 * partition already existent. */
1294

1295
                if (FLAGS_SET(flags, DISSECT_IMAGE_ADD_PARTITION_DEVICES)) {
642✔
1296
                        r = block_device_add_partition(fd, node, nr, (uint64_t) start * 512, (uint64_t) size * 512);
272✔
1297
                        if (r < 0) {
272✔
1298
                                if (r != -EBUSY)
272✔
1299
                                        return log_debug_errno(r, "BLKPG_ADD_PARTITION failed: %m");
×
1300

1301
                                log_debug_errno(r, "Kernel was quicker than us in adding partition %i.", nr);
272✔
1302
                        } else
1303
                                log_debug("We were quicker than kernel in adding partition %i.", nr);
×
1304
                }
1305

1306
                if (is_gpt) {
642✔
1307
                        const char *label;
642✔
1308
                        sd_id128_t type_id, id;
642✔
1309
                        GptPartitionType type;
642✔
1310
                        bool rw = true, growfs = false;
642✔
1311

1312
                        r = blkid_partition_get_uuid_id128(pp, &id);
642✔
1313
                        if (r < 0) {
642✔
1314
                                log_debug_errno(r, "Failed to read partition UUID, ignoring: %m");
×
1315
                                continue;
28✔
1316
                        }
1317

1318
                        r = blkid_partition_get_type_id128(pp, &type_id);
642✔
1319
                        if (r < 0) {
642✔
1320
                                log_debug_errno(r, "Failed to read partition type UUID, ignoring: %m");
×
1321
                                continue;
×
1322
                        }
1323

1324
                        type = gpt_partition_type_from_uuid(type_id);
642✔
1325

1326
                        label = sym_blkid_partition_get_name(pp); /* libblkid returns NULL here if empty */
642✔
1327

1328
                        /* systemd-sysupdate expects empty partitions to be marked with an "_empty" label, hence ignore them here. */
1329
                        if (streq_ptr(label, "_empty"))
642✔
1330
                                continue;
×
1331

1332
                        if (!image_filter_test(filter, type.designator, label))
642✔
1333
                                continue;
×
1334

1335
                        log_debug("Dissecting %s partition with label %s and UUID %s.",
749✔
1336
                                  strna(partition_designator_to_string(type.designator)), strna(label), SD_ID128_TO_UUID_STRING(id));
1337

1338
                        /* Note that we don't check the SD_GPT_FLAG_NO_AUTO flag for the ESP, as it is
1339
                         * not defined there. We instead check the SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL, as
1340
                         * recommended by the UEFI spec (See "12.3.3 Number and Location of System
1341
                         * Partitions"). */
1342
                        if (FLAGS_SET(pflags, SD_GPT_FLAG_NO_AUTO) && type.designator != PARTITION_ESP) {
642✔
1343
                                log_debug("Partition has 'no auto' flag set, ignoring.");
×
1344
                                continue;
×
1345
                        }
1346

1347
                        if (!verity && partition_designator_is_verity(type.designator)) {
642✔
1348
                                log_debug("Partition is a verity hash or verity signature partition but no verity was requested, ignoring.");
×
1349
                                continue;
×
1350
                        }
1351

1352
                        PartitionDesignator vd = partition_verity_to_data(type.designator);
642✔
1353
                        if (verity && verity->designator >= 0 && vd >= 0 && vd != verity->designator) {
642✔
1354
                                log_debug("Partition is a %s partition but verity was only requested for the %s partition, ignoring.",
×
1355
                                          partition_designator_to_string(type.designator),
1356
                                          partition_designator_to_string(verity->designator));
1357
                                continue;
×
1358
                        }
1359

1360
                        const char *fstype = getenv_fstype(type.designator);
642✔
1361

1362
                        if (IN_SET(type.designator,
642✔
1363
                                   PARTITION_HOME,
1364
                                   PARTITION_SRV,
1365
                                   PARTITION_XBOOTLDR,
1366
                                   PARTITION_TMP)) {
1367

1368
                                check_partition_flags(node, pflags,
33✔
1369
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
1370

1371
                                rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
33✔
1372
                                growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
33✔
1373

1374
                                /* XBOOTLDR cannot be integrity protected (since firmware needs to access
1375
                                 * it), hence be restrictive with the fs choice when dissecting. */
1376
                                if (type.designator == PARTITION_XBOOTLDR && !fstype)
33✔
1377
                                        fstype = "vfat";
1✔
1378

1379
                        } else if (type.designator == PARTITION_ESP) {
609✔
1380

1381
                                if (FLAGS_SET(pflags, SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL)) {
137✔
1382
                                        log_debug("ESP Partition has 'no block io' flag set, ignoring.");
×
1383
                                        continue;
×
1384
                                }
1385

1386
                                /* Effectively the ESP has to be VFAT, let's enforce this */
1387
                                if (!fstype)
137✔
1388
                                        fstype = "vfat";
137✔
1389

1390
                        } else if (type.designator == PARTITION_ROOT) {
472✔
1391

1392
                                check_partition_flags(node, pflags,
202✔
1393
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
1394

1395
                                /* If a root ID is specified, ignore everything but the root id */
1396
                                if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id)) {
404✔
1397
                                        log_debug("Partition UUID '%s' does not match expected UUID '%s' derived from root verity hash, ignoring.",
×
1398
                                                  SD_ID128_TO_UUID_STRING(id),
1399
                                                  SD_ID128_TO_UUID_STRING(root_uuid));
1400
                                        continue;
×
1401
                                }
1402

1403
                                rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
202✔
1404
                                growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
202✔
1405

1406
                        } else if (type.designator == PARTITION_ROOT_VERITY) {
270✔
1407

1408
                                check_partition_flags(node, pflags,
62✔
1409
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
1410

1411
                                m->has_verity = true;
62✔
1412

1413
                                /* If root hash is specified, then ignore everything but the root id */
1414
                                if (!sd_id128_is_null(root_verity_uuid) && !sd_id128_equal(root_verity_uuid, id)) {
124✔
1415
                                        log_debug("Partition UUID '%s' does not match expected UUID '%s' derived from root verity hash, ignoring.",
×
1416
                                                  SD_ID128_TO_UUID_STRING(id),
1417
                                                  SD_ID128_TO_UUID_STRING(root_verity_uuid));
1418
                                        continue;
×
1419
                                }
1420

1421
                                fstype = "DM_verity_hash";
1422
                                rw = false;
1423

1424
                        } else if (type.designator == PARTITION_ROOT_VERITY_SIG) {
208✔
1425
                                if (verity && iovec_is_set(&verity->root_hash)) {
62✔
1426
                                        _cleanup_(iovec_done) struct iovec root_hash = {};
21✔
1427

1428
                                        r = acquire_sig_for_roothash(
42✔
1429
                                                        fd,
1430
                                                        start * 512,
21✔
1431
                                                        size * 512,
21✔
1432
                                                        &root_hash,
1433
                                                        /* ret_root_hash_sig= */ NULL);
1434
                                        if (r < 0)
21✔
1435
                                                return r;
×
1436
                                        if (iovec_memcmp(&verity->root_hash, &root_hash) != 0) {
21✔
1437
                                                if (DEBUG_LOGGING) {
×
1438
                                                        _cleanup_free_ char *found = NULL, *expected = NULL;
×
1439

1440
                                                        found = hexmem(root_hash.iov_base, root_hash.iov_len);
×
1441
                                                        expected = hexmem(verity->root_hash.iov_base, verity->root_hash.iov_len);
×
1442

1443
                                                        log_debug("Root hash in signature JSON data (%s) doesn't match configured hash (%s).", strna(found), strna(expected));
×
1444
                                                }
1445
                                                continue;
×
1446
                                        }
1447
                                }
1448

1449
                                check_partition_flags(node, pflags,
62✔
1450
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
1451

1452
                                m->has_verity_sig = true;
62✔
1453
                                fstype = "verity_hash_signature";
62✔
1454
                                rw = false;
62✔
1455

1456
                        } else if (type.designator == PARTITION_USR) {
146✔
1457

1458
                                check_partition_flags(node, pflags,
3✔
1459
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
1460

1461
                                /* If a usr ID is specified, ignore everything but the usr id */
1462
                                if (!sd_id128_is_null(usr_uuid) && !sd_id128_equal(usr_uuid, id)) {
6✔
1463
                                        log_debug("Partition UUID '%s' does not match expected UUID '%s' derived from usr verity hash, ignoring.",
×
1464
                                                  SD_ID128_TO_UUID_STRING(id),
1465
                                                  SD_ID128_TO_UUID_STRING(usr_uuid));
1466
                                        continue;
×
1467
                                }
1468

1469
                                rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
3✔
1470
                                growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
3✔
1471

1472
                        } else if (type.designator == PARTITION_USR_VERITY) {
143✔
1473

1474
                                check_partition_flags(node, pflags,
1✔
1475
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
1476

1477
                                m->has_verity = true;
1✔
1478

1479
                                /* If usr hash is specified, then ignore everything but the usr id */
1480
                                if (!sd_id128_is_null(usr_verity_uuid) && !sd_id128_equal(usr_verity_uuid, id)) {
2✔
1481
                                        log_debug("Partition UUID '%s' does not match expected UUID '%s' derived from usr verity hash, ignoring.",
×
1482
                                                  SD_ID128_TO_UUID_STRING(id),
1483
                                                  SD_ID128_TO_UUID_STRING(usr_uuid));
1484
                                        continue;
×
1485
                                }
1486

1487
                                fstype = "DM_verity_hash";
1488
                                rw = false;
1489

1490
                        } else if (type.designator == PARTITION_USR_VERITY_SIG) {
142✔
1491
                                if (verity && iovec_is_set(&verity->root_hash)) {
×
1492
                                        _cleanup_(iovec_done) struct iovec root_hash = {};
×
1493

1494
                                        r = acquire_sig_for_roothash(
×
1495
                                                        fd,
1496
                                                        start * 512,
×
1497
                                                        size * 512,
×
1498
                                                        &root_hash,
1499
                                                        /* ret_root_hash_sig= */ NULL);
1500
                                        if (r < 0)
×
1501
                                                return r;
×
1502
                                        if (iovec_memcmp(&verity->root_hash, &root_hash) != 0) {
×
1503
                                                if (DEBUG_LOGGING) {
×
1504
                                                        _cleanup_free_ char *found = NULL, *expected = NULL;
×
1505

1506
                                                        found = hexmem(root_hash.iov_base, root_hash.iov_len);
×
1507
                                                        expected = hexmem(verity->root_hash.iov_base, verity->root_hash.iov_len);
×
1508

1509
                                                        log_debug("Root hash in signature JSON data (%s) doesn't match configured hash (%s).", strna(found), strna(expected));
×
1510
                                                }
1511
                                                continue;
×
1512
                                        }
1513
                                }
1514

1515
                                check_partition_flags(node, pflags,
×
1516
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
1517

1518
                                m->has_verity_sig = true;
×
1519
                                fstype = "verity_hash_signature";
×
1520
                                rw = false;
×
1521

1522
                        } else if (type.designator == PARTITION_SWAP) {
142✔
1523

1524
                                check_partition_flags(node, pflags, SD_GPT_FLAG_NO_AUTO);
3✔
1525

1526
                                /* Note: we don't set fstype = "swap" here, because we still need to probe if
1527
                                 * it might be encrypted (i.e. fstype "crypt_LUKS") or unencrypted
1528
                                 * (i.e. fstype "swap"), and the only way to figure that out is via fstype
1529
                                 * probing. */
1530

1531
                        /* We don't have a designator for SD_GPT_LINUX_GENERIC so check the UUID instead. */
1532
                        } else if (sd_id128_equal(type.uuid, SD_GPT_LINUX_GENERIC)) {
139✔
1533

1534
                                if (!image_filter_test(filter, PARTITION_ROOT, label))
138✔
1535
                                        continue;
×
1536

1537
                                check_partition_flags(node, pflags,
138✔
1538
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
1539

1540
                                if (generic_node)
138✔
1541
                                        multiple_generic = true;
1542
                                else {
1543
                                        generic_nr = nr;
136✔
1544
                                        generic_rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
136✔
1545
                                        generic_growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
136✔
1546
                                        generic_uuid = id;
136✔
1547
                                        generic_node = TAKE_PTR(node);
136✔
1548
                                }
1549

1550
                        } else if (type.designator == PARTITION_VAR) {
1✔
1551

1552
                                check_partition_flags(node, pflags,
1✔
1553
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
1554

1555
                                if (!FLAGS_SET(flags, DISSECT_IMAGE_RELAX_VAR_CHECK)) {
1✔
1556
                                        sd_id128_t var_uuid;
1✔
1557

1558
                                        /* For /var we insist that the uuid of the partition matches the
1559
                                         * HMAC-SHA256 of the /var GPT partition type uuid, keyed by machine
1560
                                         * ID. Why? Unlike the other partitions /var is inherently
1561
                                         * installation specific, hence we need to be careful not to mount it
1562
                                         * in the wrong installation. By hashing the partition UUID from
1563
                                         * /etc/machine-id we can securely bind the partition to the
1564
                                         * installation. */
1565

1566
                                        r = sd_id128_get_machine_app_specific(SD_GPT_VAR, &var_uuid);
1✔
1567
                                        if (r < 0)
1✔
1568
                                                return r;
×
1569

1570
                                        if (!sd_id128_equal(var_uuid, id)) {
1✔
1571
                                                log_debug("Found a /var/ partition, but its UUID didn't match our expectations "
1✔
1572
                                                          "(found: " SD_ID128_UUID_FORMAT_STR ", expected: " SD_ID128_UUID_FORMAT_STR "), ignoring.",
1573
                                                          SD_ID128_FORMAT_VAL(id), SD_ID128_FORMAT_VAL(var_uuid));
1574
                                                continue;
1✔
1575
                                        }
1576
                                }
1577

1578
                                rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
×
1579
                                growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
×
1580
                        }
1581

1582
                        if (type.designator != _PARTITION_DESIGNATOR_INVALID) {
641✔
1583
                                _cleanup_free_ char *t = NULL, *o = NULL, *l = NULL, *n = NULL;
503✔
1584
                                _cleanup_close_ int mount_node_fd = -EBADF;
503✔
1585
                                const char *options = NULL;
503✔
1586

1587
                                r = image_policy_may_use(policy, type.designator);
503✔
1588
                                if (r < 0)
503✔
1589
                                        return r;
1590
                                if (r == 0) {
499✔
1591
                                        /* Policy says: ignore; Remember this fact, so that we later can distinguish between "found but ignored" and "not found at all" */
1592

1593
                                        if (!m->partitions[type.designator].found)
27✔
1594
                                                m->partitions[type.designator].ignored = true;
27✔
1595

1596
                                        continue;
27✔
1597
                                }
1598

1599
                                if (m->partitions[type.designator].found) {
472✔
1600
                                        int c;
14✔
1601

1602
                                        /* For most partition types the first one we see wins. Except for the
1603
                                         * rootfs and /usr, where we do a version compare of the label, and
1604
                                         * let the newest version win. This permits a simple A/B versioning
1605
                                         * scheme in OS images. */
1606

1607
                                        c = compare_arch(type.arch, m->partitions[type.designator].architecture);
14✔
1608
                                        if (c < 0) /* the arch we already found is better than the one we found now */
14✔
1609
                                                continue;
×
1610
                                        if (c == 0 && /* same arch? then go by version in label */
28✔
1611
                                            (!partition_designator_is_versioned(type.designator) ||
28✔
1612
                                             strverscmp_improved(label, m->partitions[type.designator].label) <= 0))
14✔
1613
                                                continue;
×
1614

1615
                                        dissected_partition_done(m->partitions + type.designator);
14✔
1616
                                }
1617

1618
                                if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES) &&
472✔
1619
                                    type.designator != PARTITION_SWAP) {
1620
                                        mount_node_fd = open_partition(node, /* is_partition= */ true, m->loop);
226✔
1621
                                        if (mount_node_fd < 0)
226✔
1622
                                                return mount_node_fd;
1623
                                }
1624

1625
                                r = make_partition_devname(devname, diskseq, nr, flags, &n);
472✔
1626
                                if (r < 0)
472✔
1627
                                        return r;
1628

1629
                                /* Local override via env var or designator type wins */
1630
                                if (fstype) {
472✔
1631
                                        t = strdup(fstype);
265✔
1632
                                        if (!t)
265✔
1633
                                                return -ENOMEM;
1634
                                } else {
1635
                                        r = partition_policy_determine_fstype(policy, type.designator, /* ret_encrypted= */ NULL, &t);
207✔
1636
                                        if (r < 0)
207✔
1637
                                                return r;
1638
                                }
1639

1640
                                if (label) {
472✔
1641
                                        l = strdup(label);
467✔
1642
                                        if (!l)
467✔
1643
                                                return -ENOMEM;
1644
                                }
1645

1646
                                options = mount_options_from_designator(mount_options, type.designator);
472✔
1647
                                if (options) {
472✔
1648
                                        o = strdup(options);
1✔
1649
                                        if (!o)
1✔
1650
                                                return -ENOMEM;
1651
                                }
1652

1653
                                m->partitions[type.designator] = (DissectedPartition) {
472✔
1654
                                        .found = true,
1655
                                        .partno = nr,
1656
                                        .rw = rw,
1657
                                        .growfs = growfs,
1658
                                        .architecture = type.arch,
472✔
1659
                                        .node = TAKE_PTR(n),
472✔
1660
                                        .fstype = TAKE_PTR(t),
472✔
1661
                                        .label = TAKE_PTR(l),
472✔
1662
                                        .uuid = id,
1663
                                        .mount_options = TAKE_PTR(o),
472✔
1664
                                        .mount_node_fd = TAKE_FD(mount_node_fd),
472✔
1665
                                        .offset = (uint64_t) start * 512,
472✔
1666
                                        .size = (uint64_t) size * 512,
472✔
1667
                                        .gpt_flags = pflags,
1668
                                        .fsmount_fd = -EBADF,
1669
                                };
1670
                        }
1671

1672
                } else if (is_mbr) {
×
1673

1674
                        switch (sym_blkid_partition_get_type(pp)) {
×
1675

1676
                        case 0x83: /* Linux partition */
×
1677

1678
                                if (pflags != 0x80) /* Bootable flag */
×
1679
                                        continue;
×
1680

1681
                                if (!image_filter_test(filter, PARTITION_ROOT, /* label= */ NULL))
×
1682
                                        continue;
×
1683

1684
                                if (generic_node)
×
1685
                                        multiple_generic = true;
1686
                                else {
1687
                                        generic_nr = nr;
×
1688
                                        generic_rw = true;
×
1689
                                        generic_growfs = false;
×
1690
                                        generic_node = TAKE_PTR(node);
×
1691
                                }
1692

1693
                                break;
1694

1695
                        case 0xEA: { /* Boot Loader Spec extended $BOOT partition */
×
1696
                                _cleanup_close_ int mount_node_fd = -EBADF;
×
1697
                                _cleanup_free_ char *o = NULL, *n = NULL;
×
1698
                                sd_id128_t id = SD_ID128_NULL;
×
1699
                                const char *options = NULL;
×
1700

1701
                                if (!image_filter_test(filter, PARTITION_XBOOTLDR, /* label= */ NULL))
×
1702
                                        continue;
×
1703

1704
                                r = image_policy_may_use(policy, PARTITION_XBOOTLDR);
×
1705
                                if (r < 0)
×
1706
                                        return r;
1707
                                if (r == 0) { /* policy says: ignore */
×
1708
                                        if (!m->partitions[PARTITION_XBOOTLDR].found)
×
1709
                                                m->partitions[PARTITION_XBOOTLDR].ignored = true;
×
1710

1711
                                        continue;
×
1712
                                }
1713

1714
                                /* First one wins */
1715
                                if (m->partitions[PARTITION_XBOOTLDR].found)
×
1716
                                        continue;
×
1717

1718
                                if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES)) {
×
1719
                                        mount_node_fd = open_partition(node, /* is_partition= */ true, m->loop);
×
1720
                                        if (mount_node_fd < 0)
×
1721
                                                return mount_node_fd;
1722
                                }
1723

1724
                                (void) blkid_partition_get_uuid_id128(pp, &id);
×
1725

1726
                                r = make_partition_devname(devname, diskseq, nr, flags, &n);
×
1727
                                if (r < 0)
×
1728
                                        return r;
1729

1730
                                options = mount_options_from_designator(mount_options, PARTITION_XBOOTLDR);
×
1731
                                if (options) {
×
1732
                                        o = strdup(options);
×
1733
                                        if (!o)
×
1734
                                                return -ENOMEM;
1735
                                }
1736

1737
                                m->partitions[PARTITION_XBOOTLDR] = (DissectedPartition) {
×
1738
                                        .found = true,
1739
                                        .partno = nr,
1740
                                        .rw = true,
1741
                                        .growfs = false,
1742
                                        .architecture = _ARCHITECTURE_INVALID,
1743
                                        .node = TAKE_PTR(n),
×
1744
                                        .uuid = id,
1745
                                        .mount_options = TAKE_PTR(o),
×
1746
                                        .mount_node_fd = TAKE_FD(mount_node_fd),
×
1747
                                        .offset = (uint64_t) start * 512,
×
1748
                                        .size = (uint64_t) size * 512,
×
1749
                                        .fsmount_fd = -EBADF,
1750
                                };
1751

1752
                                break;
×
1753
                        }}
1754
                }
1755
        }
1756

1757
        /* Verity found but no matching rootfs? Something is off, refuse. */
1758
        if (!m->partitions[PARTITION_ROOT].found &&
210✔
1759
                (m->partitions[PARTITION_ROOT_VERITY].found ||
29✔
1760
                 m->partitions[PARTITION_ROOT_VERITY_SIG].found))
1761
                        return log_debug_errno(
×
1762
                                        SYNTHETIC_ERRNO(EADDRNOTAVAIL),
1763
                                        "Found root verity hash partition without matching root data partition.");
1764

1765
        /* Hmm, we found a signature partition but no Verity data? Something is off. */
1766
        if (m->partitions[PARTITION_ROOT_VERITY_SIG].found && !m->partitions[PARTITION_ROOT_VERITY].found)
210✔
1767
                return log_debug_errno(SYNTHETIC_ERRNO(EADDRNOTAVAIL),
1✔
1768
                                       "Found root verity signature partition without matching root verity hash partition.");
1769

1770
        /* as above */
1771
        if (!m->partitions[PARTITION_USR].found &&
209✔
1772
                (m->partitions[PARTITION_USR_VERITY].found ||
206✔
1773
                 m->partitions[PARTITION_USR_VERITY_SIG].found))
1774
                        return log_debug_errno(
×
1775
                                        SYNTHETIC_ERRNO(EADDRNOTAVAIL),
1776
                                        "Found usr verity hash partition without matching usr data partition.");
1777

1778
        /* as above */
1779
        if (m->partitions[PARTITION_USR_VERITY_SIG].found && !m->partitions[PARTITION_USR_VERITY].found)
209✔
1780
                return log_debug_errno(SYNTHETIC_ERRNO(EADDRNOTAVAIL),
×
1781
                                       "Found usr verity signature partition without matching usr verity hash partition.");
1782

1783
        /* If root and /usr are combined then insist that the architecture matches */
1784
        if (m->partitions[PARTITION_ROOT].found &&
209✔
1785
            m->partitions[PARTITION_USR].found &&
3✔
1786
            (m->partitions[PARTITION_ROOT].architecture >= 0 &&
3✔
1787
             m->partitions[PARTITION_USR].architecture >= 0 &&
3✔
1788
             m->partitions[PARTITION_ROOT].architecture != m->partitions[PARTITION_USR].architecture))
1789
                return log_debug_errno(SYNTHETIC_ERRNO(EREMOTE),
×
1790
                                       "Found root and usr partitions with different architectures (%s vs %s).",
1791
                                       architecture_to_string(m->partitions[PARTITION_ROOT].architecture),
1792
                                       architecture_to_string(m->partitions[PARTITION_USR].architecture));
1793

1794
        if (!m->partitions[PARTITION_ROOT].found &&
209✔
1795
            !m->partitions[PARTITION_USR].found &&
29✔
1796
            (flags & DISSECT_IMAGE_GENERIC_ROOT) &&
29✔
1797
            (!verity || !iovec_is_set(&verity->root_hash) || verity->designator != PARTITION_USR)) {
30✔
1798

1799
                /* OK, we found nothing usable, then check if there's a single generic partition, and use
1800
                 * that. If the root hash was set however, then we won't fall back to a generic node, because
1801
                 * the root hash decides. */
1802

1803
                /* If we didn't find a properly marked root partition, but we did find a single suitable
1804
                 * generic Linux partition, then use this as root partition, if the caller asked for it. */
1805
                if (multiple_generic)
29✔
1806
                        return -ENOTUNIQ;
1807

1808
                /* If we didn't find a generic node, then we can't fix this up either */
1809
                if (generic_node) {
29✔
1810
                        r = image_policy_may_use(policy, PARTITION_ROOT);
26✔
1811
                        if (r < 0)
26✔
1812
                                return r;
1813
                        if (r == 0)
26✔
1814
                                /* Policy says: ignore; remember that we did */
1815
                                m->partitions[PARTITION_ROOT].ignored = true;
×
1816
                        else {
1817
                                _cleanup_close_ int mount_node_fd = -EBADF;
26✔
1818
                                _cleanup_free_ char *o = NULL, *n = NULL;
×
1819
                                const char *options;
26✔
1820

1821
                                if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES)) {
26✔
1822
                                        mount_node_fd = open_partition(generic_node, /* is_partition= */ true, m->loop);
26✔
1823
                                        if (mount_node_fd < 0)
26✔
1824
                                                return mount_node_fd;
1825
                                }
1826

1827
                                r = make_partition_devname(devname, diskseq, generic_nr, flags, &n);
26✔
1828
                                if (r < 0)
26✔
1829
                                        return r;
1830

1831
                                options = mount_options_from_designator(mount_options, PARTITION_ROOT);
26✔
1832
                                if (options) {
26✔
1833
                                        o = strdup(options);
×
1834
                                        if (!o)
×
1835
                                                return -ENOMEM;
1836
                                }
1837

1838
                                assert(generic_nr >= 0);
26✔
1839
                                m->partitions[PARTITION_ROOT] = (DissectedPartition) {
26✔
1840
                                        .found = true,
1841
                                        .rw = generic_rw,
1842
                                        .growfs = generic_growfs,
1843
                                        .partno = generic_nr,
1844
                                        .architecture = _ARCHITECTURE_INVALID,
1845
                                        .node = TAKE_PTR(n),
26✔
1846
                                        .uuid = generic_uuid,
1847
                                        .mount_options = TAKE_PTR(o),
26✔
1848
                                        .mount_node_fd = TAKE_FD(mount_node_fd),
26✔
1849
                                        .offset = UINT64_MAX,
1850
                                        .size = UINT64_MAX,
1851
                                        .fsmount_fd = -EBADF,
1852
                                };
1853
                        }
1854
                }
1855
        }
1856

1857
        /* 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 */
1858
        if (FLAGS_SET(flags, DISSECT_IMAGE_REQUIRE_ROOT) &&
209✔
1859
            !(m->partitions[PARTITION_ROOT].found || (m->partitions[PARTITION_USR].found && FLAGS_SET(flags, DISSECT_IMAGE_USR_NO_ROOT))))
40✔
1860
                return log_debug_errno(SYNTHETIC_ERRNO(ENXIO), "Root or usr partition requested but found neither.");
1✔
1861

1862
        if (m->partitions[PARTITION_ROOT_VERITY].found) {
208✔
1863
                /* We only support one verity partition per image, i.e. can't do for both /usr and root fs */
1864
                if (m->partitions[PARTITION_USR_VERITY].found)
53✔
1865
                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "Found both root and usr verity enabled partitions which is not supported.");
×
1866

1867
                /* We don't support verity enabled root with a split out /usr. Neither with nor without
1868
                 * verity there. (Note that we do support verity-less root with verity-full /usr, though.) */
1869
                if (m->partitions[PARTITION_USR].found)
53✔
1870
                        return log_debug_errno(SYNTHETIC_ERRNO(EADDRNOTAVAIL), "Found verity enabled root partition with split usr partition which is not supported.");
×
1871
        }
1872

1873
        if (verity) {
208✔
1874
                /* If a verity designator is specified, then insist that the matching partition exists */
1875
                if (verity->designator >= 0 && !m->partitions[verity->designator].found)
207✔
1876
                        return log_debug_errno(
×
1877
                                SYNTHETIC_ERRNO(EADDRNOTAVAIL),
1878
                                "Explicit %s verity designator was specified but did not find %s partition.",
1879
                                partition_designator_to_string(verity->designator),
1880
                                partition_designator_to_string(verity->designator));
1881

1882
                if (iovec_is_set(&verity->root_hash)) {
1883
                        /* If we have an explicit root hash and found the partitions for it, then we are ready to use
1884
                         * Verity, set things up for it */
1885

1886
                        if (verity->designator < 0 || verity->designator == PARTITION_ROOT) {
20✔
1887
                                if (!m->partitions[PARTITION_ROOT].found)
20✔
1888
                                        return log_debug_errno(
×
1889
                                                        SYNTHETIC_ERRNO(EADDRNOTAVAIL),
1890
                                                        "Verity enabled root partition was requested but did not find a root data partition.");
1891

1892
                                if (!m->partitions[PARTITION_ROOT_VERITY].found)
20✔
1893
                                        return log_debug_errno(
1✔
1894
                                                        SYNTHETIC_ERRNO(EADDRNOTAVAIL),
1895
                                                        "Verity enabled root partition was requested but did not find a root verity hash partition.");
1896

1897
                                /* If we found a verity setup, then the root partition is necessarily read-only. */
1898
                                m->partitions[PARTITION_ROOT].rw = false;
19✔
1899
                        } else {
1900
                                assert(verity->designator == PARTITION_USR);
×
1901

1902
                                if (!m->partitions[PARTITION_USR].found)
×
1903
                                        return log_debug_errno(
×
1904
                                                        SYNTHETIC_ERRNO(EADDRNOTAVAIL),
1905
                                                        "Verity enabled usr partition was requested but did not find a usr data partition.");
1906

1907
                                if (!m->partitions[PARTITION_USR_VERITY].found)
×
1908
                                        return log_debug_errno(
×
1909
                                                        SYNTHETIC_ERRNO(EADDRNOTAVAIL),
1910
                                                        "Verity enabled usr partition was requested but did not find a usr verity hash partition.");
1911

1912

1913
                                m->partitions[PARTITION_USR].rw = false;
×
1914
                        }
1915

1916
                        m->verity_ready = true;
19✔
1917

1918
                        if (iovec_is_set(&verity->root_hash_sig))
19✔
1919
                                m->verity_sig_ready = true;
×
1920
                }
1921
        }
1922

1923
        bool any = false;
1924

1925
        /* After we discovered all partitions let's see if the verity requirements match the policy. (Note:
1926
         * we don't check encryption requirements here, because we haven't probed the file system yet, hence
1927
         * don't know if this is encrypted or not) */
1928
        for (PartitionDesignator di = 0; di < _PARTITION_DESIGNATOR_MAX; di++) {
2,865✔
1929
                any = any || m->partitions[di].found;
2,661✔
1930

1931
                /* Determine the verity protection level for this partition. */
1932
                PartitionPolicyFlags found_flags;
2,661✔
1933
                if (m->partitions[di].found) {
2,661✔
1934
                        found_flags = PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_ENCRYPTEDWITHINTEGRITY|PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_UNUSED;
480✔
1935

1936
                        PartitionDesignator vi = partition_verity_hash_of(di);
480✔
1937
                        if (vi >= 0 && m->partitions[vi].found) {
480✔
1938
                                found_flags |= PARTITION_POLICY_VERITY;
54✔
1939

1940
                                PartitionDesignator si = partition_verity_sig_of(di);
54✔
1941
                                if (si >= 0 && m->partitions[si].found)
54✔
1942
                                        found_flags |= PARTITION_POLICY_SIGNED;
46✔
1943
                        }
1944
                } else
1945
                        found_flags = m->partitions[di].ignored ? PARTITION_POLICY_UNUSED : PARTITION_POLICY_ABSENT;
2,181✔
1946

1947
                if (DEBUG_LOGGING) {
2,661✔
1948
                        _cleanup_free_ char *s = NULL;
2,193✔
1949
                        (void) partition_policy_flags_to_string(found_flags, /* simplify= */ false, &s);
2,193✔
1950
                        log_debug("Found for designator %s: %s.", partition_designator_to_string(di), strna(s));
2,193✔
1951
                }
1952

1953
                r = image_policy_check_protection(policy, di, found_flags);
2,661✔
1954
                if (r < 0)
2,661✔
1955
                        return r;
1956

1957
                if (m->partitions[di].found) {
2,658✔
1958
                        r = image_policy_check_partition_flags(policy, di, m->partitions[di].gpt_flags);
478✔
1959
                        if (r < 0)
478✔
1960
                                return r;
1961
                }
1962
        }
1963

1964
        if (!any && !FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_EMPTY))
204✔
1965
                return -ENOMSG;
1966

1967
        r = dissected_image_probe_filesystems(m, fd, policy);
203✔
1968
        if (r < 0)
203✔
1969
                return r;
×
1970

1971
        return 0;
1972
}
1973
#endif
1974

1975
int dissected_image_new_from_existing_verity(
86✔
1976
                const char *src,
1977
                const VeritySettings *verity,
1978
                const MountOptions *options,
1979
                const ImagePolicy *image_policy,
1980
                const ImageFilter *image_filter,
1981
                RuntimeScope runtime_scope,
1982
                DissectImageFlags dissect_image_flags,
1983
                DissectedImage **ret) {
1984

1985
        /* Look for an already set up dm-verity device with a single filesystem, according to our naming
1986
         * scheme and image policy, and if it is pinned by filesystem type set up the image directly. */
1987

1988
#if HAVE_BLKID
1989
        _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
×
1990
        _cleanup_free_ char *node = NULL, *root_hash_encoded = NULL, *root_fstype_string = NULL;
86✔
1991
        _cleanup_close_ int mount_node_fd = -EBADF;
86✔
1992
        PartitionPolicyFlags found_flags;
86✔
1993
        bool encrypted = false;
86✔
1994
        int r;
86✔
1995

1996
        assert(!verity || verity->designator < 0 || IN_SET(verity->designator, PARTITION_ROOT, PARTITION_USR));
86✔
1997
        assert(!verity || iovec_is_valid(&verity->root_hash));
86✔
1998
        assert(!verity || iovec_is_valid(&verity->root_hash_sig));
86✔
1999
        assert(!verity || iovec_is_set(&verity->root_hash) || !iovec_is_set(&verity->root_hash_sig));
86✔
2000
        assert(ret);
86✔
2001

2002
        /* Shortcut: this deals only with verity images and requires a policy, and only for system services */
2003
        if (runtime_scope != RUNTIME_SCOPE_SYSTEM ||
86✔
2004
            !FLAGS_SET(dissect_image_flags, DISSECT_IMAGE_VERITY_SHARE) ||
80✔
2005
            !image_policy ||
80✔
2006
            !verity ||
80✔
2007
            !verity->data_path ||
73✔
2008
            (verity->designator >= 0 && verity->designator != PARTITION_ROOT) ||
14✔
2009
            !iovec_is_set(&verity->root_hash))
100✔
2010
                return -ENOPKG;
2011

2012
        /* The policy fstype flags translate to the literal fstype name of each filesystem.
2013
         * Input must be a single filesystem image, if the policy specifies more than one, we need to dissect */
2014
        r = partition_policy_determine_fstype(image_policy, PARTITION_ROOT, &encrypted, &root_fstype_string);
14✔
2015
        if (r < 0)
14✔
2016
                return r;
2017
        if (!root_fstype_string)
14✔
2018
                return -ENOPKG;
2019

2020
        if (verity_settings_data_covers(verity, PARTITION_ROOT))
×
2021
                found_flags = iovec_is_set(&verity->root_hash_sig) ? PARTITION_POLICY_SIGNED : PARTITION_POLICY_VERITY;
×
2022
        else
2023
                found_flags = encrypted ? PARTITION_POLICY_ENCRYPTED : PARTITION_POLICY_UNPROTECTED;
×
2024

2025
        root_hash_encoded = hexmem(verity->root_hash.iov_base, verity->root_hash.iov_len);
×
2026
        if (!root_hash_encoded)
×
2027
                return -ENOMEM;
2028

2029
        node = strjoin("/dev/mapper/", root_hash_encoded, "-verity");
×
2030
        if (!node)
×
2031
                return -ENOMEM;
2032

2033
        r = dissected_image_new(src, &dissected_image);
×
2034
        if (r < 0)
×
2035
                return r;
2036

2037
        mount_node_fd = open_partition(node, /* is_partition= */ false, /* loop= */ NULL);
×
2038
        if (mount_node_fd < 0)
×
2039
                return mount_node_fd;
2040

2041
        r = dissect_image_from_unpartitioned(
×
2042
                        node,
2043
                        /* diskseq= */ 0,
2044
                        /* uuid= */ NULL,
2045
                        encrypted,
2046
                        verity,
2047
                        options,
2048
                        image_policy,
2049
                        image_filter,
2050
                        &mount_node_fd,
2051
                        &root_fstype_string,
2052
                        dissected_image,
2053
                        dissect_image_flags,
2054
                        found_flags);
2055
        if (r < 0)
×
2056
                return r;
2057

2058
        *ret = TAKE_PTR(dissected_image);
×
2059

2060
        return 0;
×
2061
#else
2062
        return -EOPNOTSUPP;
2063
#endif
2064
}
2065

2066
int dissect_image_file(
18✔
2067
                const char *path,
2068
                const VeritySettings *verity,
2069
                const MountOptions *mount_options,
2070
                const ImagePolicy *image_policy,
2071
                const ImageFilter *image_filter,
2072
                DissectImageFlags flags,
2073
                DissectedImage **ret) {
2074

2075
#if HAVE_BLKID
2076
        _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
×
2077
        _cleanup_close_ int fd = -EBADF;
18✔
2078
        struct stat st;
18✔
2079
        int r;
18✔
2080

2081
        assert(path);
18✔
2082

2083
        fd = open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
18✔
2084
        if (fd < 0)
18✔
2085
                return -errno;
×
2086

2087
        if (fstat(fd, &st) < 0)
18✔
2088
                return -errno;
×
2089

2090
        r = stat_verify_regular(&st);
18✔
2091
        if (r < 0)
18✔
2092
                return r;
2093

2094
        r = dissected_image_new(path, &m);
18✔
2095
        if (r < 0)
18✔
2096
                return r;
2097

2098
        m->image_size = st.st_size;
18✔
2099

2100
        r = probe_sector_size(fd, &m->sector_size);
18✔
2101
        if (r < 0)
18✔
2102
                return r;
2103

2104
        r = dissect_image(m, fd, path, verity, mount_options, image_policy, image_filter, flags);
18✔
2105
        if (r < 0)
18✔
2106
                return r;
2107

2108
        if (ret)
11✔
2109
                *ret = TAKE_PTR(m);
1✔
2110
        return 0;
2111
#else
2112
        return -EOPNOTSUPP;
2113
#endif
2114
}
2115

2116
int dissect_log_error(int log_level, int r, const char *name, const VeritySettings *verity) {
144✔
2117
        assert(log_level >= 0 && log_level <= LOG_DEBUG);
144✔
2118
        assert(name);
144✔
2119

2120
        switch (r) {
144✔
2121

2122
        case 0 ... INT_MAX: /* success! */
2123
                return r;
2124

2125
        case -EOPNOTSUPP:
2126
                return log_full_errno(log_level, r, "Dissecting images is not supported, compiled without blkid support.");
×
2127

2128
        case -ENOPKG:
2129
                return log_full_errno(log_level, r, "%s: Couldn't identify a suitable partition table or file system.", name);
×
2130

2131
        case -ENOMEDIUM:
2132
                return log_full_errno(log_level, r, "%s: The image does not pass os-release/extension-release validation.", name);
×
2133

2134
        case -EADDRNOTAVAIL:
2135
                return log_full_errno(log_level, r, "%s: No root/usr partition for specified root/usr hash found.", name);
1✔
2136

2137
        case -EREMOTE:
2138
                return log_full_errno(log_level, r, "%s: Found root and usr partitions with different architectures", name);
×
2139

2140
        case -ENOTUNIQ:
2141
                return log_full_errno(log_level, r, "%s: Multiple suitable root partitions found in image.", name);
×
2142

2143
        case -ENXIO:
2144
                return log_full_errno(log_level, r, "%s: No suitable root partition found in image.", name);
×
2145

2146
        case -EPROTONOSUPPORT:
2147
                return log_full_errno(log_level, r, "Device '%s' is a loopback block device with partition scanning turned off, please turn it on.", name);
×
2148

2149
        case -ENOTBLK:
2150
                return log_full_errno(log_level, r, "%s: Image is not a block device.", name);
×
2151

2152
        case -EBADR:
2153
                return log_full_errno(log_level, r,
×
2154
                                      "Combining partitioned images (such as '%s') with external Verity data (such as '%s') not supported. "
2155
                                      "(Consider setting $SYSTEMD_DISSECT_VERITY_SIDECAR=0 to disable automatic discovery of external Verity data.)",
2156
                                      name, strna(verity ? verity->data_path : NULL));
2157

2158
        case -ERFKILL:
2159
                return log_full_errno(log_level, r, "%s: Image does not match image policy.", name);
6✔
2160

2161
        case -ENOMSG:
2162
                return log_full_errno(log_level, r, "%s: No suitable partitions found.", name);
1✔
2163

2164
        case -EUCLEAN:
2165
                return log_full_errno(log_level, r, "%s: Partition with ambiguous file system superblock signature found.", name);
×
2166

2167
        default:
2168
                return log_full_errno(log_level, r, "%s: Cannot dissect image: %m", name);
×
2169
        }
2170
}
2171

2172
int dissect_image_file_and_warn(
17✔
2173
                const char *path,
2174
                const VeritySettings *verity,
2175
                const MountOptions *mount_options,
2176
                const ImagePolicy *image_policy,
2177
                const ImageFilter *image_filter,
2178
                DissectImageFlags flags,
2179
                DissectedImage **ret) {
2180

2181
        return dissect_log_error(
17✔
2182
                        LOG_ERR,
2183
                        dissect_image_file(path, verity, mount_options, image_policy, image_filter, flags, ret),
2184
                        path,
2185
                        verity);
2186
}
2187

2188
void dissected_image_close(DissectedImage *m) {
16✔
2189
        if (!m)
16✔
2190
                return;
2191

2192
        /* Closes all fds we keep open associated with this, but nothing else */
2193

2194
        FOREACH_ARRAY(p, m->partitions, _PARTITION_DESIGNATOR_MAX) {
224✔
2195
                p->mount_node_fd = safe_close(p->mount_node_fd);
208✔
2196
                p->fsmount_fd = safe_close(p->fsmount_fd);
208✔
2197
        }
2198

2199
        m->loop = loop_device_unref(m->loop);
16✔
2200
}
2201

2202
DissectedImage* dissected_image_unref(DissectedImage *m) {
1,969✔
2203
        if (!m)
1,969✔
2204
                return NULL;
2205

2206
        /* First, clear dissected partitions. */
2207
        for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++)
27,566✔
2208
                dissected_partition_done(m->partitions + i);
25,597✔
2209

2210
        /* Second, free decrypted images. This must be after dissected_partition_done(), as freeing
2211
         * DecryptedImage may try to deactivate partitions. */
2212
        decrypted_image_unref(m->decrypted_image);
1,969✔
2213

2214
        /* Third, unref LoopDevice. This must be called after the above two, as freeing LoopDevice may try to
2215
         * remove existing partitions on the loopback block device. */
2216
        loop_device_unref(m->loop);
1,969✔
2217

2218
        free(m->image_name);
1,969✔
2219
        free(m->hostname);
1,969✔
2220
        strv_free(m->machine_info);
1,969✔
2221
        strv_free(m->os_release);
1,969✔
2222
        strv_free(m->initrd_release);
1,969✔
2223
        strv_free(m->confext_release);
1,969✔
2224
        strv_free(m->sysext_release);
1,969✔
2225

2226
        return mfree(m);
1,969✔
2227
}
2228

2229
static int is_loop_device(const char *path) {
102✔
2230
        char s[SYS_BLOCK_PATH_MAX("/../loop/")];
102✔
2231
        struct stat st;
102✔
2232

2233
        assert(path);
102✔
2234

2235
        if (stat(path, &st) < 0)
102✔
2236
                return -errno;
×
2237

2238
        if (!S_ISBLK(st.st_mode))
102✔
2239
                return -ENOTBLK;
2240

2241
        xsprintf_sys_block_path(s, "/loop/", st.st_dev);
102✔
2242
        if (access(s, F_OK) < 0) {
102✔
2243
                if (errno != ENOENT)
102✔
2244
                        return -errno;
×
2245

2246
                /* The device itself isn't a loop device, but maybe it's a partition and its parent is? */
2247
                xsprintf_sys_block_path(s, "/../loop/", st.st_dev);
102✔
2248
                if (access(s, F_OK) < 0)
102✔
2249
                        return errno == ENOENT ? false : -errno;
102✔
2250
        }
2251

2252
        return true;
2253
}
2254

2255
static int run_fsck(int node_fd, const char *fstype) {
15✔
2256
        int r, exit_status;
15✔
2257

2258
        assert(node_fd >= 0);
15✔
2259
        assert(fstype);
15✔
2260

2261
        r = fsck_exists_for_fstype(fstype);
15✔
2262
        if (r < 0) {
15✔
2263
                log_debug_errno(r, "Couldn't determine whether fsck for %s exists, proceeding anyway.", fstype);
×
2264
                return 0;
15✔
2265
        }
2266
        if (r == 0) {
15✔
2267
                log_debug("Not checking partition %s, as fsck for %s does not exist.", FORMAT_PROC_FD_PATH(node_fd), fstype);
×
2268
                return 0;
×
2269
        }
2270

2271
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
15✔
2272
        r = pidref_safe_fork_full(
15✔
2273
                        "(fsck)",
2274
                        NULL,
2275
                        &node_fd, 1, /* Leave the node fd open */
2276
                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_CLOEXEC_OFF,
2277
                        &pidref);
2278
        if (r < 0)
30✔
2279
                return log_debug_errno(r, "Failed to fork off fsck: %m");
×
2280
        if (r == 0) {
30✔
2281
                /* Child */
2282
                execlp("fsck", "fsck", "-aT", FORMAT_PROC_FD_PATH(node_fd), NULL);
15✔
2283
                log_open();
15✔
2284
                log_debug_errno(errno, "Failed to execl() fsck: %m");
×
2285
                _exit(FSCK_OPERATIONAL_ERROR);
×
2286
        }
2287

2288
        exit_status = pidref_wait_for_terminate_and_check("fsck", &pidref, 0);
15✔
2289
        if (exit_status < 0)
15✔
2290
                return log_debug_errno(exit_status, "Failed to fork off fsck: %m");
×
2291

2292
        if ((exit_status & ~FSCK_ERROR_CORRECTED) != FSCK_SUCCESS) {
15✔
2293
                log_debug("fsck failed with exit status %i.", exit_status);
×
2294

2295
                if ((exit_status & (FSCK_SYSTEM_SHOULD_REBOOT|FSCK_ERRORS_LEFT_UNCORRECTED)) != 0)
×
2296
                        return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN), "File system is corrupted, refusing.");
×
2297

2298
                log_debug("Ignoring fsck error.");
×
2299
        }
2300

2301
        return 0;
2302
}
2303

2304
static int fs_grow(const char *node_path, int mount_fd, const char *mount_path) {
12✔
2305
        _cleanup_close_ int _mount_fd = -EBADF, node_fd = -EBADF;
12✔
2306
        uint64_t size, newsize;
12✔
2307
        const char *id;
12✔
2308
        int r;
12✔
2309

2310
        assert(node_path);
12✔
2311
        assert(mount_fd >= 0 || mount_path);
12✔
2312

2313
        node_fd = open(node_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
12✔
2314
        if (node_fd < 0)
12✔
2315
                return log_debug_errno(errno, "Failed to open node device %s: %m", node_path);
×
2316

2317
        r = blockdev_get_device_size(node_fd, &size);
12✔
2318
        if (r < 0)
12✔
2319
                return log_debug_errno(r, "Failed to get block device size of %s: %m", node_path);
×
2320

2321
        if (mount_fd < 0) {
12✔
2322
                assert(mount_path);
12✔
2323

2324
                _mount_fd = open(mount_path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
12✔
2325
                if (_mount_fd < 0)
12✔
2326
                        return log_debug_errno(errno, "Failed to open mounted file system %s: %m", mount_path);
×
2327

2328
                mount_fd = _mount_fd;
2329
        } else {
2330
                mount_fd = fd_reopen_condition(mount_fd, O_RDONLY|O_DIRECTORY|O_CLOEXEC, O_RDONLY|O_DIRECTORY|O_CLOEXEC, &_mount_fd);
×
2331
                if (mount_fd < 0)
×
2332
                        return log_debug_errno(errno, "Failed to reopen mount node: %m");
×
2333
        }
2334

2335
        id = mount_path ?: node_path;
12✔
2336

2337
        log_debug("Resizing \"%s\" to %"PRIu64" bytes...", id, size);
12✔
2338
        r = resize_fs(mount_fd, size, &newsize);
12✔
2339
        if (r < 0)
12✔
2340
                return log_debug_errno(r, "Failed to resize \"%s\" to %"PRIu64" bytes: %m", id, size);
6✔
2341

2342
        if (newsize == size)
6✔
2343
                log_debug("Successfully resized \"%s\" to %s bytes.",
6✔
2344
                          id, FORMAT_BYTES(newsize));
2345
        else {
2346
                assert(newsize < size);
×
2347
                log_debug("Successfully resized \"%s\" to %s bytes (%"PRIu64" bytes lost due to blocksize).",
×
2348
                          id, FORMAT_BYTES(newsize), size - newsize);
2349
        }
2350

2351
        return 0;
2352
}
2353

2354
int partition_pick_mount_options(
330✔
2355
                PartitionDesignator d,
2356
                const char *fstype,
2357
                bool rw,
2358
                bool discard,
2359
                char **ret_options,
2360
                unsigned long *ret_ms_flags) {
2361

2362
        _cleanup_free_ char *options = NULL;
330✔
2363

2364
        assert(ret_options);
330✔
2365

2366
        /* Selects a baseline of bind mount flags, that should always apply.
2367
         *
2368
         * Firstly, we set MS_NODEV universally on all mounts, since we don't want to allow device nodes outside of /dev/.
2369
         *
2370
         * On /var/tmp/ we'll also set MS_NOSUID, same as we set for /tmp/ on the host.
2371
         *
2372
         * On the ESP and XBOOTLDR partitions we'll also disable symlinks, and execution. These file systems
2373
         * are generally untrusted (i.e. not encrypted or authenticated), and typically VFAT hence we should
2374
         * be as restrictive as possible, and this shouldn't hurt, since the functionality is not available
2375
         * there anyway. */
2376

2377
        unsigned long flags = MS_NODEV;
330✔
2378

2379
        if (!rw)
330✔
2380
                flags |= MS_RDONLY;
188✔
2381

2382
        switch (d) {
330✔
2383

2384
        case PARTITION_ESP:
55✔
2385
        case PARTITION_XBOOTLDR:
2386
                flags |= MS_NOSUID|MS_NOEXEC|ms_nosymfollow_supported();
55✔
2387

2388
                /* The ESP might contain a pre-boot random seed. Let's make this unaccessible to regular
2389
                 * userspace. ESP/XBOOTLDR is almost certainly VFAT, hence if we don't know assume it is. */
2390
                if (!fstype || fstype_can_fmask_dmask(fstype))
55✔
2391
                        if (!strextend_with_separator(&options, ",", "fmask=0177,dmask=0077"))
29✔
2392
                                return -ENOMEM;
2393
                break;
2394

2395
        case PARTITION_TMP:
×
2396
                flags |= MS_NOSUID;
×
2397
                break;
×
2398

2399
        default:
330✔
2400
                ;
330✔
2401
        }
2402

2403
        /* So, when you request MS_RDONLY from ext4, then this means nothing. It happily still writes to the
2404
         * backing storage. What's worse, the BLKRO[GS]ET flag and (in case of loopback devices)
2405
         * LO_FLAGS_READ_ONLY don't mean anything, they affect userspace accesses only, and write accesses
2406
         * from the upper file system still get propagated through to the underlying file system,
2407
         * unrestricted. To actually get ext4/xfs/btrfs to stop writing to the device we need to specify
2408
         * "norecovery" as mount option, in addition to MS_RDONLY. Yes, this sucks, since it means we need to
2409
         * carry a per file system table here.
2410
         *
2411
         * Note that this means that we might not be able to mount corrupted file systems as read-only
2412
         * anymore (since in some cases the kernel implementations will refuse mounting when corrupted,
2413
         * read-only and "norecovery" is specified). But I think for the case of automatically determined
2414
         * mount options for loopback devices this is the right choice, since otherwise using the same
2415
         * loopback file twice even in read-only mode, is going to fail badly sooner or later. The use case of
2416
         * making reuse of the immutable images "just work" is more relevant to us than having read-only
2417
         * access that actually modifies stuff work on such image files. Or to say this differently: if
2418
         * people want their file systems to be fixed up they should just open them in writable mode, where
2419
         * all these problems don't exist. */
2420
        if (!rw && fstype) {
330✔
2421
                const char *option = fstype_norecovery_option(fstype);
188✔
2422

2423
                if (option && !strextend_with_separator(&options, ",", option))
188✔
2424
                        return -ENOMEM;
2425
        }
2426

2427
        if (discard && fstype && fstype_can_discard(fstype))
330✔
2428
                if (!strextend_with_separator(&options, ",", "discard"))
2✔
2429
                        return -ENOMEM;
2430

2431
        if (!ret_ms_flags) /* Fold flags into option string if ret_flags specified as NULL */
330✔
2432
                if (!strextend_with_separator(&options, ",",
10✔
2433
                                              FLAGS_SET(flags, MS_RDONLY) ? "ro" : "rw",
2434
                                              FLAGS_SET(flags, MS_NODEV) ? "nodev" : "dev",
2435
                                              FLAGS_SET(flags, MS_NOSUID) ? "nosuid" : "suid",
2436
                                              FLAGS_SET(flags, MS_NOEXEC) ? "noexec" : "exec",
2437
                                              FLAGS_SET(flags, MS_NOSYMFOLLOW) ? "nosymfollow" : NULL))
2438
                        /* NB: we suppress 'symfollow' here, since it's the default, and old /bin/mount might not know it */
2439
                        return -ENOMEM;
2440

2441
        if (ret_ms_flags)
328✔
2442
                *ret_ms_flags = flags;
328✔
2443

2444
        *ret_options = TAKE_PTR(options);
330✔
2445
        return 0;
330✔
2446
}
2447

2448
static bool need_user_mapping(uid_t uid_shift, uid_t uid_range) {
607✔
2449

2450
        if (!uid_is_valid(uid_shift))
607✔
2451
                return false;
2452

2453
        return uid_shift != 0 || uid_range != UINT32_MAX;
10✔
2454
}
2455

2456
static int mount_partition(
1,535✔
2457
                PartitionDesignator d,
2458
                DissectedPartition *m,
2459
                const char *where,
2460
                const char *directory,
2461
                uid_t uid_shift,
2462
                uid_t uid_range,
2463
                int userns_fd,
2464
                DissectImageFlags flags) {
2465

2466
        _cleanup_free_ char *chased = NULL, *options = NULL;
1,535✔
2467
        const char *p = NULL, *node, *fstype = NULL;
1,535✔
2468
        bool rw, discard, grow;
1,535✔
2469
        unsigned long ms_flags;
1,535✔
2470
        int r;
1,535✔
2471

2472
        assert(m);
1,535✔
2473

2474
        if (!m->found)
1,535✔
2475
                return 0;
2476

2477
        /* Check the various combinations when we can't do anything anymore */
2478
        if (m->fsmount_fd < 0 && m->mount_node_fd < 0)
338✔
2479
                return 0;
2480
        if (m->fsmount_fd >= 0 && !where)
338✔
2481
                return 0;
2482
        if (!where && m->mount_node_fd < 0)
328✔
2483
                return 0;
2484

2485
        if (m->fsmount_fd < 0) {
338✔
2486
                fstype = dissected_partition_fstype(m);
328✔
2487
                if (!fstype)
328✔
2488
                        return -EAFNOSUPPORT;
2489

2490
                /* We are looking at an encrypted partition? This either means stacked encryption, or the
2491
                 * caller didn't call dissected_image_decrypt() beforehand. Let's return a recognizable error
2492
                 * for this case. */
2493
                if (streq(fstype, "crypto_LUKS"))
328✔
2494
                        return -EUNATCH;
2495

2496
                r = dissect_fstype_ok(fstype);
328✔
2497
                if (r < 0)
328✔
2498
                        return r;
2499
                if (!r)
328✔
2500
                        return -EIDRM; /* Recognizable error */
2501
        }
2502

2503
        node = m->mount_node_fd < 0 ? NULL : FORMAT_PROC_FD_PATH(m->mount_node_fd);
338✔
2504
        rw = m->rw && !(flags & DISSECT_IMAGE_MOUNT_READ_ONLY);
338✔
2505

2506
        discard = ((flags & DISSECT_IMAGE_DISCARD) ||
338✔
2507
                   ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && (m->node && is_loop_device(m->node) > 0)));
337✔
2508

2509
        grow = rw && m->growfs && FLAGS_SET(flags, DISSECT_IMAGE_GROWFS);
338✔
2510

2511
        if (FLAGS_SET(flags, DISSECT_IMAGE_FSCK) && rw && m->mount_node_fd >= 0 && m->fsmount_fd < 0) {
338✔
2512
                r = run_fsck(m->mount_node_fd, fstype);
15✔
2513
                if (r < 0)
15✔
2514
                        return r;
2515
        }
2516

2517
        if (where) {
338✔
2518
                if (directory) {
336✔
2519
                        /* Automatically create missing mount points inside the image, if necessary. */
2520
                        r = mkdir_p_root(where, directory, uid_shift, (gid_t) uid_shift, 0755);
63✔
2521
                        if (r < 0 && r != -EROFS)
63✔
2522
                                return r;
2523

2524
                        r = chase(directory, where, CHASE_PREFIX_ROOT, &chased, NULL);
63✔
2525
                        if (r < 0)
63✔
2526
                                return r;
2527

2528
                        p = chased;
63✔
2529
                } else {
2530
                        /* Create top-level mount if missing – but only if this is asked for. This won't modify the
2531
                         * image (as the branch above does) but the host hierarchy, and the created directory might
2532
                         * survive our mount in the host hierarchy hence. */
2533
                        if (FLAGS_SET(flags, DISSECT_IMAGE_MKDIR)) {
273✔
2534
                                r = mkdir_p(where, 0755);
14✔
2535
                                if (r < 0)
14✔
2536
                                        return r;
2537
                        }
2538

2539
                        p = where;
2540
                }
2541
        }
2542

2543
        if (m->fsmount_fd < 0) {
338✔
2544
                r = partition_pick_mount_options(d, fstype, rw, discard, &options, &ms_flags);
328✔
2545
                if (r < 0)
328✔
2546
                        return r;
2547

2548
                if (need_user_mapping(uid_shift, uid_range) && fstype_can_uid_gid(fstype)) {
328✔
2549
                        _cleanup_free_ char *uid_option = NULL;
×
2550

2551
                        if (asprintf(&uid_option, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
×
2552
                                return -ENOMEM;
2553

2554
                        if (!strextend_with_separator(&options, ",", uid_option))
×
2555
                                return -ENOMEM;
2556

2557
                        userns_fd = -EBADF; /* Not needed */
×
2558
                }
2559

2560
                if (!isempty(m->mount_options))
328✔
2561
                        if (!strextend_with_separator(&options, ",", m->mount_options))
4✔
2562
                                return -ENOMEM;
2563
        }
2564

2565
        if (p) {
338✔
2566
                if (m->fsmount_fd >= 0) {
336✔
2567
                        /* Case #1: Attach existing fsmount fd to the file system */
2568

2569
                        r = mount_exchange_graceful(
20✔
2570
                                        m->fsmount_fd,
2571
                                        p,
2572
                                        FLAGS_SET(flags, DISSECT_IMAGE_TRY_ATOMIC_MOUNT_EXCHANGE));
10✔
2573
                        if (r < 0)
10✔
2574
                                return log_debug_errno(r, "Failed to mount image on '%s': %m", p);
×
2575

2576
                } else {
2577
                        assert(node);
326✔
2578

2579
                        /* Case #2: Mount directly into place */
2580
                        r = mount_nofollow_verbose(LOG_DEBUG, node, p, fstype, ms_flags, options);
326✔
2581
                        if (r < 0)
326✔
2582
                                return r;
2583

2584
                        if (grow)
326✔
2585
                                (void) fs_grow(node, -EBADF, p);
12✔
2586

2587
                        if (userns_fd >= 0) {
326✔
2588
                                r = remount_idmap_fd(STRV_MAKE(p), userns_fd, /* extra_mount_attr_set= */ 0);
×
2589
                                if (r < 0)
×
2590
                                        return r;
×
2591
                        }
2592
                }
2593
        } else {
2594
                assert(node);
2✔
2595

2596
                /* Case #3: Create fsmount fd */
2597

2598
                m->fsmount_fd = make_fsmount(LOG_DEBUG, node, fstype, ms_flags, options, userns_fd);
2✔
2599
                if (m->fsmount_fd < 0)
2✔
2600
                        return m->fsmount_fd;
2601

2602
                if (grow)
2✔
2603
                        (void) fs_grow(node, m->fsmount_fd, NULL);
×
2604
        }
2605

2606
        return 1;
2607
}
2608

2609
static int mount_root_tmpfs(const char *where, uid_t uid_shift, uid_t uid_range, DissectImageFlags flags) {
×
2610
        _cleanup_free_ char *options = NULL;
×
2611
        int r;
×
2612

2613
        assert(where);
×
2614

2615
        /* For images that contain /usr/ but no rootfs, let's mount rootfs as tmpfs */
2616

2617
        if (FLAGS_SET(flags, DISSECT_IMAGE_MKDIR)) {
×
2618
                r = mkdir_p(where, 0755);
×
2619
                if (r < 0)
×
2620
                        return r;
2621
        }
2622

2623
        if (need_user_mapping(uid_shift, uid_range)) {
×
2624
                if (asprintf(&options, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
×
2625
                        return -ENOMEM;
2626
        }
2627

2628
        r = mount_nofollow_verbose(LOG_DEBUG, "rootfs", where, "tmpfs", MS_NODEV, options);
×
2629
        if (r < 0)
×
2630
                return r;
×
2631

2632
        return 1;
2633
}
2634

2635
static int mount_point_is_available(const char *where, const char *path, bool missing_ok) {
236✔
2636
        _cleanup_free_ char *p = NULL;
236✔
2637
        int r;
236✔
2638

2639
        /* Check whether <path> is suitable as a mountpoint, i.e. is an empty directory
2640
         * or does not exist at all (when missing_ok). */
2641

2642
        r = chase(path, where, CHASE_PREFIX_ROOT, &p, NULL);
236✔
2643
        if (r == -ENOENT)
236✔
2644
                return missing_ok;
108✔
2645
        if (r < 0)
128✔
2646
                return log_debug_errno(r, "Failed to chase \"%s\": %m", path);
×
2647

2648
        r = dir_is_empty(p, /* ignore_hidden_or_backup= */ false);
128✔
2649
        if (r == -ENOTDIR)
128✔
2650
                return false;
2651
        if (r < 0)
128✔
2652
                return log_debug_errno(r, "Failed to check directory \"%s\": %m", p);
×
2653
        return r > 0;
128✔
2654
}
2655

2656
int dissected_image_mount(
281✔
2657
                DissectedImage *m,
2658
                const char *where,
2659
                uid_t uid_shift,
2660
                uid_t uid_range,
2661
                int userns_fd,
2662
                DissectImageFlags flags) {
2663

2664
        _cleanup_close_ int my_userns_fd = -EBADF;
281✔
2665
        int r;
281✔
2666

2667
        assert(m);
281✔
2668

2669
        if (FLAGS_SET(flags, DISSECT_IMAGE_FOREIGN_UID)) /* For image based mounts we currently require an identity mapping */
281✔
2670
                return -EOPNOTSUPP;
2671

2672
        /* If 'where' is NULL then we'll use the new mount API to create fsmount() fds for the mounts and
2673
         * store them in DissectedPartition.fsmount_fd.
2674
         *
2675
         * If 'where' is not NULL then we'll either mount the partitions to the right places ourselves,
2676
         * or use DissectedPartition.fsmount_fd and bind it to the right places.
2677
         *
2678
         * This allows splitting the setting up the superblocks and the binding to file systems paths into
2679
         * two distinct and differently privileged components: one that gets the fsmount fds, and the other
2680
         * that then applies them.
2681
         *
2682
         * Returns:
2683
         *
2684
         *  -ENXIO        → No root partition found
2685
         *  -EMEDIUMTYPE  → DISSECT_IMAGE_VALIDATE_OS set but no os-release/extension-release file found
2686
         *  -EUNATCH      → Encrypted partition found for which no dm-crypt was set up yet
2687
         *  -EUCLEAN      → fsck for file system failed
2688
         *  -EBUSY        → File system already mounted/used elsewhere (kernel)
2689
         *  -EAFNOSUPPORT → File system type not supported or not known
2690
         *  -EIDRM        → File system is not among allowlisted "common" file systems
2691
         */
2692

2693
        if (!where && (flags & (DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_VALIDATE_OS_EXT)) != 0)
281✔
2694
                return -EOPNOTSUPP; /* for now, not supported */
2695

2696
        if (!(m->partitions[PARTITION_ROOT].found ||
281✔
2697
              (m->partitions[PARTITION_USR].found && FLAGS_SET(flags, DISSECT_IMAGE_USR_NO_ROOT))))
×
2698
                return -ENXIO; /* Require a root fs or at least a /usr/ fs (the latter is subject to a flag of its own) */
2699

2700
        if (userns_fd < 0 && need_user_mapping(uid_shift, uid_range) && FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_IDMAPPED)) {
281✔
2701

2702
                my_userns_fd = make_userns(uid_shift, uid_range, UID_INVALID, UID_INVALID, REMOUNT_IDMAPPING_HOST_ROOT);
2✔
2703
                if (my_userns_fd < 0)
2✔
2704
                        return my_userns_fd;
2705

2706
                userns_fd = my_userns_fd;
2707
        }
2708

2709
        if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0) {
281✔
2710

2711
                /* First mount the root fs. If there's none we use a tmpfs. */
2712
                if (m->partitions[PARTITION_ROOT].found) {
275✔
2713
                        r = mount_partition(PARTITION_ROOT, m->partitions + PARTITION_ROOT, where, NULL, uid_shift, uid_range, userns_fd, flags);
275✔
2714
                        if (r < 0)
275✔
2715
                                return r;
2716

2717
                } else if (where) {
×
2718
                        r = mount_root_tmpfs(where, uid_shift, uid_range, flags);
×
2719
                        if (r < 0)
×
2720
                                return r;
2721
                }
2722

2723
                /* For us mounting root always means mounting /usr as well */
2724
                r = mount_partition(PARTITION_USR, m->partitions + PARTITION_USR, where, "/usr", uid_shift, uid_range, userns_fd, flags);
275✔
2725
                if (r < 0)
275✔
2726
                        return r;
2727
        }
2728

2729
        if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0 &&
275✔
2730
            (flags & (DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_VALIDATE_OS_EXT)) != 0) {
275✔
2731
                /* If either one of the validation flags are set, ensure that the image qualifies as
2732
                 * one or the other (or both). */
2733
                bool ok = false;
94✔
2734

2735
                assert(where);
94✔
2736

2737
                if (FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS)) {
94✔
2738
                        r = path_is_os_tree(where);
52✔
2739
                        if (r < 0)
52✔
2740
                                return r;
2741
                        if (r > 0)
52✔
2742
                                ok = true;
2743
                }
2744
                if (!ok && FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS_EXT) && m->image_name) {
61✔
2745
                        r = extension_has_forbidden_content(where);
61✔
2746
                        if (r < 0)
61✔
2747
                                return r;
2748
                        if (r == 0) {
61✔
2749
                                r = path_is_extension_tree(IMAGE_SYSEXT, where, m->image_name, FLAGS_SET(flags, DISSECT_IMAGE_RELAX_EXTENSION_CHECK));
61✔
2750
                                if (r == 0)
61✔
2751
                                        r = path_is_extension_tree(IMAGE_CONFEXT, where, m->image_name, FLAGS_SET(flags, DISSECT_IMAGE_RELAX_EXTENSION_CHECK));
7✔
2752
                                if (r < 0)
61✔
2753
                                        return r;
2754
                                if (r > 0)
61✔
2755
                                        ok = true;
2756
                        }
2757
                }
2758

2759
                if (!ok)
×
2760
                        return -ENOMEDIUM;
×
2761
        }
2762

2763
        if (flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY)
281✔
2764
                return 0;
2765

2766
        r = mount_partition(PARTITION_HOME, m->partitions + PARTITION_HOME, where, "/home", uid_shift, uid_range, userns_fd, flags);
206✔
2767
        if (r < 0)
206✔
2768
                return r;
2769

2770
        r = mount_partition(PARTITION_SRV, m->partitions + PARTITION_SRV, where, "/srv", uid_shift, uid_range, userns_fd, flags);
206✔
2771
        if (r < 0)
206✔
2772
                return r;
2773

2774
        r = mount_partition(PARTITION_VAR, m->partitions + PARTITION_VAR, where, "/var", uid_shift, uid_range, userns_fd, flags);
206✔
2775
        if (r < 0)
206✔
2776
                return r;
2777

2778
        r = mount_partition(PARTITION_TMP, m->partitions + PARTITION_TMP, where, "/var/tmp", uid_shift, uid_range, userns_fd, flags);
206✔
2779
        if (r < 0)
206✔
2780
                return r;
2781

2782
        int slash_boot_is_available = 0;
206✔
2783
        if (where) {
206✔
2784
                r = slash_boot_is_available = mount_point_is_available(where, "/boot", /* missing_ok= */ true);
204✔
2785
                if (r < 0)
204✔
2786
                        return r;
2787
        }
2788
        if (!where || slash_boot_is_available) {
206✔
2789
                r = mount_partition(PARTITION_XBOOTLDR, m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, uid_range, userns_fd, flags);
132✔
2790
                if (r < 0)
132✔
2791
                        return r;
2792
                slash_boot_is_available = !r;
132✔
2793
        }
2794

2795
        if (m->partitions[PARTITION_ESP].found) {
206✔
2796
                const char *esp_path = NULL;
29✔
2797

2798
                if (where) {
29✔
2799
                        /* Mount the ESP to /boot/ if it exists and is empty and we didn't already mount the
2800
                         * XBOOTLDR partition into it. Otherwise, use /efi instead, but only if it exists
2801
                         * and is empty. */
2802

2803
                        if (slash_boot_is_available) {
29✔
2804
                                r = mount_point_is_available(where, "/boot", /* missing_ok= */ false);
3✔
2805
                                if (r < 0)
3✔
2806
                                        return r;
2807
                                if (r > 0)
3✔
2808
                                        esp_path = "/boot";
2809
                        }
2810

2811
                        if (!esp_path) {
2812
                                r = mount_point_is_available(where, "/efi", /* missing_ok= */ true);
29✔
2813
                                if (r < 0)
29✔
2814
                                        return r;
2815
                                if (r > 0)
29✔
2816
                                        esp_path = "/efi";
29✔
2817
                        }
2818
                }
2819

2820
                /* OK, let's mount the ESP now (possibly creating the dir if missing) */
2821
                r = mount_partition(PARTITION_ESP, m->partitions + PARTITION_ESP, where, esp_path, uid_shift, uid_range, userns_fd, flags);
29✔
2822
                if (r < 0)
29✔
2823
                        return r;
×
2824
        }
2825

2826
        return 0;
2827
}
2828

2829
int dissected_image_mount_and_warn(
97✔
2830
                DissectedImage *m,
2831
                const char *where,
2832
                uid_t uid_shift,
2833
                uid_t uid_range,
2834
                int userns_fd,
2835
                DissectImageFlags flags) {
2836

2837
        int r;
97✔
2838

2839
        assert(m);
97✔
2840

2841
        r = dissected_image_mount(m, where, uid_shift, uid_range, userns_fd, flags);
97✔
2842
        if (r == -ENXIO)
97✔
2843
                return log_error_errno(r, "Failed to mount image: No root file system found in image.");
×
2844
        if (r == -EMEDIUMTYPE)
97✔
2845
                return log_error_errno(r, "Failed to mount image: No suitable os-release/extension-release file in image found.");
×
2846
        if (r == -EUNATCH)
97✔
2847
                return log_error_errno(r, "Failed to mount image: Encrypted file system discovered, but decryption not requested.");
×
2848
        if (r == -EUCLEAN)
97✔
2849
                return log_error_errno(r, "Failed to mount image: File system check on image failed.");
×
2850
        if (r == -EBUSY)
97✔
2851
                return log_error_errno(r, "Failed to mount image: File system already mounted elsewhere.");
×
2852
        if (r == -EAFNOSUPPORT)
97✔
2853
                return log_error_errno(r, "Failed to mount image: File system type not supported or not known.");
×
2854
        if (r == -EIDRM)
97✔
2855
                return log_error_errno(r, "Failed to mount image: File system is too uncommon, refused.");
×
2856
        if (r < 0)
97✔
2857
                return log_error_errno(r, "Failed to mount image: %m");
×
2858

2859
        return r;
2860
}
2861

2862
#if HAVE_LIBCRYPTSETUP
2863
struct DecryptedPartition {
2864
        struct crypt_device *device;
2865
        char *name;
2866
        bool relinquished;
2867
};
2868
#endif
2869

2870
typedef struct DecryptedPartition DecryptedPartition;
2871

2872
struct DecryptedImage {
2873
        unsigned n_ref;
2874
        DecryptedPartition *decrypted;
2875
        size_t n_decrypted;
2876
};
2877

2878
static DecryptedImage* decrypted_image_free(DecryptedImage *d) {
65✔
2879
#if HAVE_LIBCRYPTSETUP
2880
        int r;
65✔
2881

2882
        if (!d)
65✔
2883
                return NULL;
2884

2885
        for (size_t i = 0; i < d->n_decrypted; i++) {
129✔
2886
                DecryptedPartition *p = d->decrypted + i;
64✔
2887

2888
                if (p->device && p->name && !p->relinquished) {
64✔
2889
                        _cleanup_free_ char *node = NULL;
13✔
2890

2891
                        node = path_join("/dev/mapper", p->name);
13✔
2892
                        if (node) {
13✔
2893
                                r = btrfs_forget_device(node);
13✔
2894
                                if (r < 0 && r != -ENOENT)
13✔
2895
                                        log_debug_errno(r, "Failed to forget btrfs device %s, ignoring: %m", node);
×
2896
                        } else
2897
                                log_oom_debug();
×
2898

2899
                        /* Let's deactivate lazily, as the dm volume may be already/still used by other processes. */
2900
                        r = sym_crypt_deactivate_by_name(p->device, p->name, CRYPT_DEACTIVATE_DEFERRED);
13✔
2901
                        if (r < 0)
13✔
2902
                                log_debug_errno(r, "Failed to deactivate encrypted partition %s", p->name);
13✔
2903
                }
2904

2905
                if (p->device)
64✔
2906
                        sym_crypt_free(p->device);
64✔
2907
                free(p->name);
64✔
2908
        }
2909

2910
        free(d->decrypted);
65✔
2911
        free(d);
65✔
2912
#endif
2913
        return NULL;
65✔
2914
}
2915

2916
DEFINE_TRIVIAL_REF_UNREF_FUNC(DecryptedImage, decrypted_image, decrypted_image_free);
1,970✔
2917

2918
#if HAVE_LIBCRYPTSETUP
2919
static int decrypted_image_new(DecryptedImage **ret) {
65✔
2920
        _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
65✔
2921

2922
        assert(ret);
65✔
2923

2924
        d = new(DecryptedImage, 1);
65✔
2925
        if (!d)
65✔
2926
                return -ENOMEM;
2927

2928
        *d = (DecryptedImage) {
65✔
2929
                .n_ref = 1,
2930
        };
2931

2932
        *ret = TAKE_PTR(d);
65✔
2933
        return 0;
65✔
2934
}
2935

2936
static int make_dm_name_and_node(const void *original_node, const char *suffix, char **ret_name, char **ret_node) {
65✔
2937
        _cleanup_free_ char *name = NULL, *node = NULL;
65✔
2938
        const char *base;
65✔
2939

2940
        assert(original_node);
65✔
2941
        assert(suffix);
65✔
2942
        assert(ret_name);
65✔
2943
        assert(ret_node);
65✔
2944

2945
        base = strrchr(original_node, '/');
65✔
2946
        if (!base)
65✔
2947
                base = original_node;
2948
        else
2949
                base++;
41✔
2950
        if (isempty(base))
130✔
2951
                return -EINVAL;
2952

2953
        name = strjoin(base, suffix);
65✔
2954
        if (!name)
65✔
2955
                return -ENOMEM;
2956
        if (!filename_is_valid(name))
65✔
2957
                return -EINVAL;
2958

2959
        node = path_join(sym_crypt_get_dir(), name);
65✔
2960
        if (!node)
65✔
2961
                return -ENOMEM;
2962

2963
        *ret_name = TAKE_PTR(name);
65✔
2964
        *ret_node = TAKE_PTR(node);
65✔
2965

2966
        return 0;
65✔
2967
}
2968

2969
static int decrypt_partition(
135✔
2970
                DissectedPartition *m,
2971
                const char *passphrase,
2972
                DissectImageFlags flags,
2973
                PartitionPolicyFlags policy_flags,
2974
                DecryptedImage *d) {
2975

2976
        _cleanup_free_ char *node = NULL, *name = NULL;
135✔
2977
        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
×
2978
        _cleanup_close_ int fd = -EBADF;
135✔
2979
        int r;
135✔
2980

2981
        assert(m);
135✔
2982
        assert(d);
135✔
2983

2984
        if (!m->found || !m->node || !m->fstype)
135✔
2985
                return 0;
2986

2987
        if (!streq(m->fstype, "crypto_LUKS"))
135✔
2988
                return 0;
2989

2990
        if (!passphrase)
2✔
2991
                return -ENOKEY;
2992

2993
        if (!FLAGS_SET(policy_flags, PARTITION_POLICY_ENCRYPTED))
2✔
2994
                return log_debug_errno(SYNTHETIC_ERRNO(ERFKILL), "Attempted to unlock partition via LUKS, but it's prohibited.");
×
2995

2996
        r = dlopen_cryptsetup();
2✔
2997
        if (r < 0)
2✔
2998
                return r;
2999

3000
        r = make_dm_name_and_node(m->node, "-decrypted", &name, &node);
2✔
3001
        if (r < 0)
2✔
3002
                return r;
3003

3004
        if (!GREEDY_REALLOC0(d->decrypted, d->n_decrypted + 1))
2✔
3005
                return -ENOMEM;
3006

3007
        r = sym_crypt_init(&cd, m->node);
2✔
3008
        if (r < 0)
2✔
3009
                return log_debug_errno(r, "Failed to initialize dm-crypt: %m");
×
3010

3011
        cryptsetup_enable_logging(cd);
2✔
3012

3013
        r = sym_crypt_load(cd, CRYPT_LUKS, NULL);
2✔
3014
        if (r < 0)
2✔
3015
                return log_debug_errno(r, "Failed to load LUKS metadata: %m");
×
3016

3017
        r = sym_crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase),
2✔
3018
                                             ((flags & DISSECT_IMAGE_DEVICE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
3019
                                             ((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
3020
        if (r < 0) {
2✔
3021
                log_debug_errno(r, "Failed to activate LUKS device: %m");
×
3022
                return r == -EPERM ? -EKEYREJECTED : r;
×
3023
        }
3024

3025
        fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
2✔
3026
        if (fd < 0)
2✔
3027
                return log_debug_errno(errno, "Failed to open %s: %m", node);
×
3028

3029
        d->decrypted[d->n_decrypted++] = (DecryptedPartition) {
2✔
3030
                .name = TAKE_PTR(name),
2✔
3031
                .device = TAKE_PTR(cd),
2✔
3032
        };
3033

3034
        m->decrypted_node = TAKE_PTR(node);
2✔
3035
        close_and_replace(m->mount_node_fd, fd);
2✔
3036

3037
        return 0;
2✔
3038
}
3039

3040
static int verity_can_reuse(
8✔
3041
                const VeritySettings *verity,
3042
                const char *name,
3043
                struct crypt_device **ret_cd) {
3044

3045
        /* If the same volume was already open, check that the root hashes match, and reuse it if they do */
3046
        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
8✔
3047
        struct crypt_params_verity crypt_params = {};
8✔
3048
        int r;
8✔
3049

3050
        assert(verity);
8✔
3051
        assert(name);
8✔
3052
        assert(ret_cd);
8✔
3053

3054
        r = sym_crypt_init_by_name(&cd, name);
8✔
3055
        if (r < 0)
8✔
3056
                return log_debug_errno(r, "Error opening verity device, crypt_init_by_name failed: %m");
×
3057

3058
        cryptsetup_enable_logging(cd);
8✔
3059

3060
        r = sym_crypt_get_verity_info(cd, &crypt_params);
8✔
3061
        if (r < 0)
8✔
3062
                return log_debug_errno(r, "Error opening verity device, crypt_get_verity_info failed: %m");
×
3063

3064
        _cleanup_(iovec_done) struct iovec root_hash_existing = {
8✔
3065
                .iov_base = malloc0(verity->root_hash.iov_len),
8✔
3066
                .iov_len = verity->root_hash.iov_len,
3067
        };
3068
        if (!root_hash_existing.iov_base)
8✔
3069
                return -ENOMEM;
3070

3071
        r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, root_hash_existing.iov_base, &root_hash_existing.iov_len, NULL, 0);
8✔
3072
        if (r < 0)
8✔
3073
                return log_debug_errno(r, "Error opening verity device, crypt_volume_key_get failed: %m");
×
3074
        if (iovec_memcmp(&verity->root_hash, &root_hash_existing) != 0)
8✔
3075
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Error opening verity device, it already exists but root hashes are different.");
×
3076

3077
        /* Ensure that, if signatures are supported, we only reuse the device if the previous mount used the
3078
         * same settings, so that a previous unsigned mount will not be reused if the user asks to use
3079
         * signing for the new one, and vice versa. */
3080
        if (iovec_is_set(&verity->root_hash_sig) != FLAGS_SET(crypt_params.flags, CRYPT_VERITY_ROOT_HASH_SIGNATURE))
16✔
3081
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Error opening verity device, it already exists but signature settings are not the same.");
×
3082

3083
        *ret_cd = TAKE_PTR(cd);
8✔
3084
        return 0;
8✔
3085
}
3086

3087
static char* dm_deferred_remove_clean(char *name) {
×
3088
        if (!name)
×
3089
                return NULL;
3090

3091
        (void) sym_crypt_deactivate_by_name(NULL, name, CRYPT_DEACTIVATE_DEFERRED);
×
3092
        return mfree(name);
×
3093
}
3094
DEFINE_TRIVIAL_CLEANUP_FUNC(char *, dm_deferred_remove_clean);
63✔
3095

3096
static int validate_signature_userspace(const VeritySettings *verity, const char *root, DissectImageFlags flags) {
23✔
3097
        int r;
23✔
3098

3099
        /* Returns > 0 if signature checks out, == 0 if not, < 0 on unexpected errors */
3100

3101
        if (!FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_USERSPACE_VERITY)) {
23✔
3102
                log_debug("Userspace dm-verity signature authentication disabled via flag.");
×
3103
                return 0;
23✔
3104
        }
3105

3106
        r = secure_getenv_bool("SYSTEMD_ALLOW_USERSPACE_VERITY");
23✔
3107
        if (r < 0 && r != -ENXIO) {
23✔
3108
                log_debug_errno(r, "Failed to parse $SYSTEMD_ALLOW_USERSPACE_VERITY environment variable, refusing userspace dm-verity signature authentication.");
×
3109
                return 0;
×
3110
        }
3111
        if (!r) {
23✔
3112
                log_debug("Userspace dm-verity signature authentication disabled via $SYSTEMD_ALLOW_USERSPACE_VERITY environment variable.");
×
3113
                return 0;
×
3114
        }
3115

3116
        bool b;
23✔
3117
        r = proc_cmdline_get_bool("systemd.allow_userspace_verity", PROC_CMDLINE_TRUE_WHEN_MISSING, &b);
23✔
3118
        if (r < 0) {
23✔
3119
                log_debug_errno(r, "Failed to parse systemd.allow_userspace_verity= kernel command line option, refusing userspace dm-verity signature authentication.");
×
3120
                return 0;
×
3121
        }
3122
        if (!b) {
23✔
3123
                log_debug("Userspace dm-verity signature authentication disabled via systemd.allow_userspace_verity= kernel command line variable.");
×
3124
                return 0;
×
3125
        }
3126

3127
#if HAVE_OPENSSL
3128
        _cleanup_(sk_X509_free_allp) STACK_OF(X509) *sk = NULL;
23✔
3129
        _cleanup_strv_free_ char **certs = NULL;
×
3130
        _cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
23✔
3131
        _cleanup_free_ char *s = NULL;
23✔
3132
        _cleanup_(BIO_freep) BIO *bio = NULL; /* 'bio' must be freed first, 's' second, hence keep this order
23✔
3133
                                               * of declaration in place, please */
3134
        assert(verity);
23✔
3135
        assert(iovec_is_set(&verity->root_hash));
23✔
3136
        assert(iovec_is_set(&verity->root_hash_sig));
23✔
3137

3138
        /* Because installing a signature certificate into the kernel chain is so messy, let's optionally do
3139
         * userspace validation. */
3140

3141
        r = conf_files_list_nulstr(&certs, ".crt", root, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, CONF_PATHS_NULSTR("verity.d"));
23✔
3142
        if (r < 0)
23✔
3143
                return log_debug_errno(r, "Failed to enumerate certificates: %m");
×
3144
        if (strv_isempty(certs)) {
23✔
3145
                log_debug("No userspace dm-verity certificates found.");
×
3146
                return 0;
×
3147
        }
3148

3149
        const unsigned char *d = verity->root_hash_sig.iov_base;
23✔
3150
        p7 = d2i_PKCS7(NULL, &d, (long) verity->root_hash_sig.iov_len);
23✔
3151
        if (!p7)
23✔
3152
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse PKCS7 DER signature data.");
×
3153

3154
        s = hexmem(verity->root_hash.iov_base, verity->root_hash.iov_len);
23✔
3155
        if (!s)
23✔
3156
                return log_oom_debug();
×
3157

3158
        bio = BIO_new_mem_buf(s, strlen(s));
23✔
3159
        if (!bio)
23✔
3160
                return log_oom_debug();
×
3161

3162
        sk = sk_X509_new_null();
23✔
3163
        if (!sk)
23✔
3164
                return log_oom_debug();
×
3165

3166
        STRV_FOREACH(i, certs) {
64✔
3167
                _cleanup_(X509_freep) X509 *c = NULL;
23✔
3168
                _cleanup_fclose_ FILE *f = NULL;
41✔
3169

3170
                f = fopen(*i, "re");
41✔
3171
                if (!f) {
41✔
3172
                        log_debug_errno(errno, "Failed to open '%s', ignoring: %m", *i);
×
3173
                        continue;
×
3174
                }
3175

3176
                c = PEM_read_X509(f, NULL, NULL, NULL);
41✔
3177
                if (!c) {
41✔
3178
                        log_debug("Failed to load X509 certificate '%s', ignoring.", *i);
×
3179
                        continue;
×
3180
                }
3181

3182
                if (sk_X509_push(sk, c) == 0)
41✔
3183
                        return log_oom_debug();
×
3184

3185
                TAKE_PTR(c);
41✔
3186
        }
3187

3188
        r = PKCS7_verify(p7, sk, NULL, bio, NULL, PKCS7_NOINTERN|PKCS7_NOVERIFY);
23✔
3189
        if (r)
23✔
3190
                log_debug("Userspace PKCS#7 validation succeeded.");
21✔
3191
        else
3192
                log_debug("Userspace PKCS#7 validation failed: %s", ERR_error_string(ERR_get_error(), NULL));
2✔
3193

3194
        return r;
3195
#else
3196
        log_debug("Not doing client-side validation of dm-verity root hash signatures, OpenSSL support disabled.");
3197
        return 0;
3198
#endif
3199
}
3200

3201
static int do_crypt_activate_verity(
55✔
3202
                struct crypt_device *cd,
3203
                const char *root,
3204
                const char *name,
3205
                const VeritySettings *verity,
3206
                DissectImageFlags flags,
3207
                PartitionPolicyFlags policy_flags) {
3208

3209
        bool check_signature;
55✔
3210
        int r, k;
55✔
3211

3212
        assert(cd);
55✔
3213
        assert(name);
55✔
3214
        assert(verity);
55✔
3215

3216
        if (iovec_is_set(&verity->root_hash_sig) && FLAGS_SET(policy_flags, PARTITION_POLICY_SIGNED)) {
107✔
3217
                r = secure_getenv_bool("SYSTEMD_DISSECT_VERITY_SIGNATURE");
52✔
3218
                if (r < 0 && r != -ENXIO)
52✔
3219
                        log_debug_errno(r, "Failed to parse $SYSTEMD_DISSECT_VERITY_SIGNATURE");
×
3220

3221
                check_signature = r != 0;
52✔
3222
        } else
3223
                check_signature = false;
3224

3225
        if (check_signature) {
52✔
3226
                /* First, if we have support for signed keys in the kernel, then try that first. */
3227
                r = sym_crypt_activate_by_signed_key(
104✔
3228
                                cd,
3229
                                name,
3230
                                verity->root_hash.iov_base,
52✔
3231
                                verity->root_hash.iov_len,
52✔
3232
                                verity->root_hash_sig.iov_base,
52✔
3233
                                verity->root_hash_sig.iov_len,
52✔
3234
                                CRYPT_ACTIVATE_READONLY);
3235
                if (r >= 0) {
52✔
3236
                        log_debug("Verity activation via kernel signature logic worked.");
29✔
3237
                        return 0;
29✔
3238
                }
3239

3240
                log_debug_errno(r, "Validation of dm-verity signature failed via the kernel, trying userspace validation instead: %m");
23✔
3241

3242
                /* Let's mangle ENOKEY → EDESTADDRREQ, so that we return a clear, recognizable error if
3243
                 * there's a signature we don't recognize, that is distinct from the LUKS/encryption
3244
                 * -ENOKEY, which means "password required, but I have none". */
3245
                if (r == -ENOKEY)
23✔
3246
                        r = -EDESTADDRREQ;
23✔
3247

3248
                /* So this didn't work via the kernel, then let's try userspace validation instead. If that
3249
                 * works we'll try to activate without telling the kernel the signature. */
3250

3251
                /* Preferably propagate the original kernel error, so that the fallback logic can work,
3252
                 * as the device-mapper is finicky around concurrent activations of the same volume */
3253
                k = validate_signature_userspace(verity, root, flags);
23✔
3254
                if (k < 0)
23✔
3255
                        return k;
3256
                if (k == 0) {
23✔
3257
                        log_debug("Activation of signed Verity volume worked neither via the kernel nor in userspace, can't activate.");
2✔
3258

3259
                        /* So if we had a signature and we're supposed to exclusively allow
3260
                         * signature-based activation, then return the error now */
3261
                        if (!FLAGS_SET(policy_flags, PARTITION_POLICY_VERITY))
2✔
3262
                                return r < 0 ? r : -EDESTADDRREQ;
1✔
3263

3264
                        log_debug("Activation of signed Verity volume without validating signature is permitted by policy. Continuing.");
1✔
3265
                } else
3266
                        log_debug("Verity activation via userspace signature logic worked, activating by root hash.");
21✔
3267

3268
                /* Otherwise let's see what signature-less activation results in. */
3269

3270
        } else if (!FLAGS_SET(policy_flags, PARTITION_POLICY_VERITY))
3✔
3271
                return log_debug_errno(SYNTHETIC_ERRNO(ERFKILL),
×
3272
                                       "No-signature activation of Verity volume not allowed by policy, refusing.");
3273

3274
        r = sym_crypt_activate_by_volume_key(
50✔
3275
                        cd,
3276
                        name,
3277
                        verity->root_hash.iov_base,
25✔
3278
                        verity->root_hash.iov_len,
25✔
3279
                        CRYPT_ACTIVATE_READONLY);
3280
        if (r < 0)
25✔
3281
                return log_debug_errno(r, "Activation of Verity via root hash failed: %m");
×
3282

3283
        log_debug("Activation of Verity via root hash succeeded.");
25✔
3284
        return 0;
3285
}
3286

3287
static usec_t verity_timeout(void) {
×
3288
        usec_t t = 100 * USEC_PER_MSEC;
×
3289
        const char *e;
×
3290
        int r;
×
3291

3292
        /* On slower machines, like non-KVM vm, setting up device may take a long time.
3293
         * Let's make the timeout configurable. */
3294

3295
        e = getenv("SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC");
×
3296
        if (!e)
×
3297
                return t;
×
3298

3299
        r = parse_sec(e, &t);
×
3300
        if (r < 0)
×
3301
                log_debug_errno(r,
×
3302
                                "Failed to parse timeout specified in $SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC, "
3303
                                "using the default timeout (%s).",
3304
                                FORMAT_TIMESPAN(t, USEC_PER_MSEC));
3305

3306
        return t;
×
3307
}
3308

3309
static int verity_partition(
66✔
3310
                PartitionDesignator designator,
3311
                DissectedPartition *m, /* data partition */
3312
                DissectedPartition *v, /* verity partition */
3313
                const char *root, /* The root to get user verity certs from (for a sysext) */
3314
                const VeritySettings *verity,
3315
                DissectImageFlags flags,
3316
                PartitionPolicyFlags policy_flags,
3317
                DecryptedImage *d) {
3318

3319
        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
×
3320
        _cleanup_free_ char *node = NULL, *name = NULL;
66✔
3321
        _cleanup_close_ int mount_node_fd = -EBADF;
66✔
3322
        int r;
66✔
3323

3324
        assert(m);
66✔
3325
        assert(v || (verity && verity->data_path));
66✔
3326

3327
        if (!verity || !iovec_is_set(&verity->root_hash))
130✔
3328
                return 0;
3329
        if (!((verity->designator < 0 && designator == PARTITION_ROOT) ||
64✔
3330
              (verity->designator == designator)))
3331
                return 0;
3332

3333
        if (!m->found || !m->node || !m->fstype)
63✔
3334
                return 0;
3335
        if (!verity->data_path) {
63✔
3336
                if (!v->found || !v->node || !v->fstype)
33✔
3337
                        return 0;
3338

3339
                if (!streq(v->fstype, "DM_verity_hash"))
33✔
3340
                        return 0;
3341
        }
3342

3343
        if (!(policy_flags & (PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED))) {
63✔
3344
                log_debug("Attempted to unlock partition via Verity, but it's prohibited, skipping.");
×
3345
                return 0;
×
3346
        }
3347

3348
        r = dlopen_cryptsetup();
63✔
3349
        if (r < 0)
63✔
3350
                return r;
3351

3352
        if (FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)) {
63✔
3353
                /* Use the roothash, which is unique per volume, as the device node name, so that it can be reused */
3354
                _cleanup_free_ char *root_hash_encoded = NULL;
24✔
3355

3356
                root_hash_encoded = hexmem(verity->root_hash.iov_base, verity->root_hash.iov_len);
24✔
3357
                if (!root_hash_encoded)
24✔
3358
                        return -ENOMEM;
×
3359

3360
                r = make_dm_name_and_node(root_hash_encoded, "-verity", &name, &node);
24✔
3361
        } else
3362
                r = make_dm_name_and_node(m->node, "-verity", &name, &node);
39✔
3363
        if (r < 0)
63✔
3364
                return r;
3365

3366
        r = sym_crypt_init(&cd, verity->data_path ?: v->node);
63✔
3367
        if (r < 0)
63✔
3368
                return r;
3369

3370
        cryptsetup_enable_logging(cd);
63✔
3371

3372
        r = sym_crypt_load(cd, CRYPT_VERITY, NULL);
63✔
3373
        if (r < 0)
63✔
3374
                return r;
3375

3376
        r = sym_crypt_set_data_device(cd, m->node);
63✔
3377
        if (r < 0)
63✔
3378
                return r;
3379

3380
        if (!GREEDY_REALLOC0(d->decrypted, d->n_decrypted + 1))
63✔
3381
                return -ENOMEM;
3382

3383
        /* If activating fails because the device already exists, check the metadata and reuse it if it matches.
3384
         * In case of ENODEV/ENOENT, which can happen if another process is activating at the exact same time,
3385
         * retry a few times before giving up. */
3386
        for (unsigned i = 0; i < N_DEVICE_NODE_LIST_ATTEMPTS; i++) {
63✔
3387
                _cleanup_(dm_deferred_remove_cleanp) char *restore_deferred_remove = NULL;
63✔
3388
                _cleanup_(sym_crypt_freep) struct crypt_device *existing_cd = NULL;
×
3389
                _cleanup_close_ int fd = -EBADF;
63✔
3390

3391
                /* First, check if the device already exists. */
3392
                fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
63✔
3393
                if (fd < 0 && !ERRNO_IS_DEVICE_ABSENT(errno))
63✔
3394
                        return log_debug_errno(errno, "Failed to open verity device %s: %m", node);
×
3395
                if (fd >= 0)
63✔
3396
                        goto check; /* The device already exists. Let's check it. */
8✔
3397

3398
                /* The symlink to the device node does not exist yet. Assume not activated, and let's activate it. */
3399
                r = do_crypt_activate_verity(cd, root, name, verity, flags, policy_flags);
55✔
3400
                if (r >= 0)
55✔
3401
                        goto try_open; /* The device is activated. Let's open it. */
54✔
3402
                /* libdevmapper can return EINVAL when the device is already in the activation stage.
3403
                 * There's no way to distinguish this situation from a genuine error due to invalid
3404
                 * parameters, so immediately fall back to activating the device with a unique name.
3405
                 * Improvements in libcrypsetup can ensure this never happens:
3406
                 * https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/96 */
3407
                if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
1✔
3408
                        break;
3409
                /* Volume is being opened but not ready, crypt_init_by_name would fail, try to open again if
3410
                 * sharing is enabled. */
3411
                if (r == -ENODEV && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
1✔
3412
                        goto try_again;
×
3413
                if (!IN_SET(r,
1✔
3414
                            -EEXIST, /* Volume has already been opened and ready to be used. */
3415
                            -EBUSY   /* Volume is being opened but not ready, crypt_init_by_name() can fetch details. */))
3416
                        return log_debug_errno(r, "Failed to activate verity device %s: %m", node);
1✔
3417

3418
        check:
8✔
3419
                /* To avoid races, disable automatic removal on umount while setting up the new device. Restore it on failure. */
3420
                r = dm_deferred_remove_cancel(name);
8✔
3421
                /* -EBUSY and -ENXIO: the device has already been removed or being removed. We cannot
3422
                 * use the device, try to open again. See target_message() in drivers/md/dm-ioctl.c
3423
                 * and dm_cancel_deferred_remove() in drivers/md/dm.c */
3424
                if (IN_SET(r, -EBUSY, -ENXIO))
8✔
3425
                        goto try_again;
×
3426
                if (r < 0)
8✔
3427
                        return log_debug_errno(r, "Failed to disable automated deferred removal for verity device %s: %m", node);
×
3428

3429
                restore_deferred_remove = strdup(name);
8✔
3430
                if (!restore_deferred_remove)
8✔
3431
                        return log_oom_debug();
×
3432

3433
                r = verity_can_reuse(verity, name, &existing_cd);
8✔
3434
                /* Same as above, -EINVAL can randomly happen when it actually means -EEXIST */
3435
                if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
8✔
3436
                        break;
3437
                if (IN_SET(r,
8✔
3438
                           -ENOENT, /* Removed?? */
3439
                           -EBUSY,  /* Volume is being opened but not ready, crypt_init_by_name() can fetch details. */
3440
                           -ENODEV  /* Volume is being opened but not ready, crypt_init_by_name() would fail, try to open again. */ ))
3441
                        goto try_again;
×
3442
                if (r < 0)
8✔
3443
                        return log_debug_errno(r, "Failed to check if existing verity device %s can be reused: %m", node);
×
3444

3445
                if (fd < 0) {
8✔
3446
                        /* devmapper might say that the device exists, but the devlink might not yet have been
3447
                         * created. Check and wait for the udev event in that case. */
3448
                        r = device_wait_for_devlink(node, "block", verity_timeout(), NULL);
×
3449
                        /* Fallback to activation with a unique device if it's taking too long */
3450
                        if (r == -ETIMEDOUT && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
×
3451
                                break;
3452
                        if (r < 0)
×
3453
                                return log_debug_errno(r, "Failed to wait device node symlink %s: %m", node);
×
3454
                }
3455

3456
        try_open:
×
3457
                if (fd < 0) {
54✔
3458
                        /* Now, the device is activated and devlink is created. Let's open it. */
3459
                        fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
54✔
3460
                        if (fd < 0) {
54✔
3461
                                if (!ERRNO_IS_DEVICE_ABSENT(errno))
×
3462
                                        return log_debug_errno(errno, "Failed to open verity device %s: %m", node);
×
3463

3464
                                /* The device has already been removed?? */
3465
                                goto try_again;
×
3466
                        }
3467
                }
3468

3469
                /* Everything looks good and we'll be able to mount the device, so deferred remove will be re-enabled at that point. */
3470
                restore_deferred_remove = mfree(restore_deferred_remove);
62✔
3471

3472
                mount_node_fd = TAKE_FD(fd);
62✔
3473
                if (existing_cd)
62✔
3474
                        crypt_free_and_replace(cd, existing_cd);
8✔
3475

3476
                goto success;
62✔
3477

3478
        try_again:
×
3479
                /* Device is being removed by another process. Let's wait for a while. */
3480
                (void) usleep_safe(2 * USEC_PER_MSEC);
×
3481
        }
3482

3483
        /* All trials failed or a conflicting verity device exists. Let's try to activate with a unique name. */
3484
        if (FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)) {
×
3485
                /* Before trying to activate with unique name, we need to free crypt_device object.
3486
                 * Otherwise, we get error from libcryptsetup like the following:
3487
                 * ------
3488
                 * systemd[1234]: Cannot use device /dev/loop5 which is in use (already mapped or mounted).
3489
                 * ------
3490
                 */
3491
                sym_crypt_free(cd);
×
3492
                cd = NULL;
×
3493
                return verity_partition(designator, m, v, root, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, policy_flags, d);
×
3494
        }
3495

3496
        return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "All attempts to activate verity device %s failed.", name);
×
3497

3498
success:
62✔
3499
        d->decrypted[d->n_decrypted++] = (DecryptedPartition) {
62✔
3500
                .name = TAKE_PTR(name),
62✔
3501
                .device = TAKE_PTR(cd),
62✔
3502
        };
3503

3504
        m->decrypted_node = TAKE_PTR(node);
62✔
3505
        close_and_replace(m->mount_node_fd, mount_node_fd);
62✔
3506

3507
        return 0;
62✔
3508
}
3509
#endif
3510

3511
int dissected_image_decrypt(
203✔
3512
                DissectedImage *m,
3513
                const char *root, /* The root to get user verity certs from (for a sysext) */
3514
                const char *passphrase,
3515
                const VeritySettings *verity,
3516
                const ImagePolicy *policy,
3517
                DissectImageFlags flags) {
3518

3519
#if HAVE_LIBCRYPTSETUP
3520
        _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
203✔
3521
#endif
3522
        int r;
203✔
3523

3524
        assert(m);
203✔
3525
        assert(!verity || iovec_is_valid(&verity->root_hash));
203✔
3526
        assert(!verity || iovec_is_valid(&verity->root_hash_sig));
203✔
3527

3528
        /* Returns:
3529
         *
3530
         *      = 0           → There was nothing to decrypt/setup
3531
         *      > 0           → Decrypted/setup successfully
3532
         *      -ENOKEY       → dm-crypt: there's something to decrypt but no decryption key was supplied
3533
         *      -EKEYREJECTED → dm-crypt: Passed key was not correct
3534
         *      -EDESTADDRREQ → dm-verity: there's something to setup but no signature was supplied
3535
         *      -EBUSY        → dm-verity: Generic Verity error (kernel is not very explanatory)
3536
         *      -ERFKILL      → image policy not compatible with request
3537
         *      -EEXIST       → DM device already exists under the specified name
3538
         */
3539

3540
        if (verity && iovec_is_set(&verity->root_hash) && verity->root_hash.iov_len < sizeof(sd_id128_t))
266✔
3541
                return -EINVAL;
3542

3543
        if (!m->encrypted && !m->verity_ready)
203✔
3544
                return 0;
3545

3546
        r = secure_getenv_bool("SYSTEMD_VERITY_SHARING");
65✔
3547
        if (r >= 0)
65✔
3548
                SET_FLAG(flags, DISSECT_IMAGE_VERITY_SHARE, r);
3✔
3549

3550
#if HAVE_LIBCRYPTSETUP
3551
        r = decrypted_image_new(&d);
65✔
3552
        if (r < 0)
65✔
3553
                return r;
3554

3555
        for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
897✔
3556
                DissectedPartition *p = m->partitions + i;
833✔
3557
                PartitionDesignator k;
833✔
3558

3559
                if (!p->found)
833✔
3560
                        continue;
698✔
3561

3562
                PartitionPolicyFlags fl = image_policy_get_exhaustively(policy, i);
135✔
3563

3564
                r = decrypt_partition(p, passphrase, flags, fl, d);
135✔
3565
                if (r < 0)
135✔
3566
                        return r;
3567

3568
                k = partition_verity_hash_of(i);
135✔
3569
                if (k >= 0) {
135✔
3570
                        r = verity_partition(i, p, m->partitions + k, root, verity, flags, fl, d);
66✔
3571
                        if (r < 0)
66✔
3572
                                return r;
3573
                }
3574

3575
                if (!p->decrypted_fstype && p->mount_node_fd >= 0 && p->decrypted_node) {
134✔
3576
                        r = probe_filesystem_full(p->mount_node_fd, p->decrypted_node, 0, UINT64_MAX, /* bool restrict_fstypes= */ true, &p->decrypted_fstype);
64✔
3577
                        if (r < 0 && r != -EUCLEAN)
64✔
3578
                                return r;
3579
                }
3580
        }
3581

3582
        m->decrypted_image = TAKE_PTR(d);
64✔
3583
        return 1;
64✔
3584
#else
3585
        return -EOPNOTSUPP;
3586
#endif
3587
}
3588

3589
int dissected_image_decrypt_interactively(
72✔
3590
                DissectedImage *m,
3591
                const char *passphrase,
3592
                const VeritySettings *verity,
3593
                const ImagePolicy *image_policy,
3594
                DissectImageFlags flags) {
3595

3596
        _cleanup_strv_free_erase_ char **z = NULL;
72✔
3597
        int n = 3, r;
72✔
3598

3599
        if (passphrase)
72✔
3600
                n--;
2✔
3601

3602
        for (;;) {
72✔
3603
                r = dissected_image_decrypt(m, /* root= */ NULL, passphrase, verity, image_policy, flags);
144✔
3604
                if (r >= 0)
72✔
3605
                        return r;
3606
                if (r == -EKEYREJECTED)
1✔
3607
                        log_error_errno(r, "Incorrect passphrase, try again!");
×
3608
                else if (r == -EDESTADDRREQ)
1✔
3609
                        return log_error_errno(r, "Image lacks recognized signature.");
1✔
3610
                else if (r == -ERFKILL)
×
3611
                        return log_error_errno(r, "Unlocking of Verity/LUKS volumes not permitted by policy.");
×
3612
                else if (r != -ENOKEY)
×
3613
                        return log_error_errno(r, "Failed to decrypt/set up image: %m");
×
3614

3615
                if (--n < 0)
×
3616
                        return log_error_errno(SYNTHETIC_ERRNO(EKEYREJECTED),
×
3617
                                               "Too many retries.");
3618

3619
                z = strv_free_erase(z);
×
3620

3621
                static const AskPasswordRequest req = {
×
3622
                        .tty_fd = -EBADF,
3623
                        .message = "Please enter image passphrase:",
3624
                        .id = "dissect",
3625
                        .keyring = "dissect",
3626
                        .credential = "dissect.passphrase",
3627
                        .until = USEC_INFINITY,
3628
                        .hup_fd = -EBADF,
3629
                };
3630

3631
                r = ask_password_auto(&req, /* flags= */ 0, &z);
×
3632
                if (r < 0)
×
3633
                        return log_error_errno(r, "Failed to query for passphrase: %m");
×
3634

3635
                assert(!strv_isempty(z));
72✔
3636
                passphrase = z[0];
3637
        }
3638
}
3639

3640
static int decrypted_image_relinquish(DecryptedImage *d) {
51✔
3641
        assert(d);
51✔
3642

3643
        /* Turns on automatic removal after the last use ended for all DM devices of this image, and sets a
3644
         * boolean so that we don't clean it up ourselves either anymore */
3645

3646
#if HAVE_LIBCRYPTSETUP
3647
        int r;
3648

3649
        for (size_t i = 0; i < d->n_decrypted; i++) {
102✔
3650
                DecryptedPartition *p = d->decrypted + i;
51✔
3651

3652
                if (p->relinquished)
51✔
3653
                        continue;
×
3654

3655
                r = sym_crypt_deactivate_by_name(NULL, p->name, CRYPT_DEACTIVATE_DEFERRED);
51✔
3656
                if (r < 0)
51✔
3657
                        return log_debug_errno(r, "Failed to mark %s for auto-removal: %m", p->name);
×
3658

3659
                p->relinquished = true;
51✔
3660
        }
3661
#endif
3662

3663
        return 0;
3664
}
3665

3666
int dissected_image_relinquish(DissectedImage *m) {
163✔
3667
        int r;
163✔
3668

3669
        assert(m);
163✔
3670

3671
        if (m->decrypted_image) {
163✔
3672
                r = decrypted_image_relinquish(m->decrypted_image);
51✔
3673
                if (r < 0)
51✔
3674
                        return r;
3675
        }
3676

3677
        if (m->loop)
163✔
3678
                loop_device_relinquish(m->loop);
160✔
3679

3680
        return 0;
3681
}
3682

3683
void image_filter_done(ImageFilter *f) {
5✔
3684
        assert(f);
5✔
3685

3686
        FOREACH_ELEMENT(p, f->pattern)
70✔
3687
                *p = mfree(*p);
65✔
3688
}
5✔
3689

3690
ImageFilter *image_filter_free(ImageFilter *f) {
111✔
3691
        if (!f)
111✔
3692
                return NULL;
3693

3694
        image_filter_done(f);
5✔
3695
        return mfree(f);
5✔
3696
}
3697

3698
int image_filter_parse(const char *s, ImageFilter **ret) {
10✔
3699
        _cleanup_(image_filter_freep) ImageFilter *f = NULL;
10✔
3700
        int r;
10✔
3701

3702
        if (isempty(s)) {
10✔
3703
                if (ret)
2✔
3704
                        *ret = NULL;
2✔
3705
                return 0;
2✔
3706
        }
3707

3708
        for (;;) {
28✔
3709
                _cleanup_free_ char *word = NULL;
5✔
3710

3711
                r = extract_first_word(&s, &word, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
18✔
3712
                if (r < 0)
18✔
3713
                        return log_debug_errno(r, "Failed to extract word: %m");
×
3714
                if (r == 0)
18✔
3715
                        break;
3716

3717
                _cleanup_free_ char *designator = NULL, *pattern = NULL;
15✔
3718
                const char *x = word;
15✔
3719
                r = extract_many_words(&x, "=", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS, &designator, &pattern);
15✔
3720
                if (r < 0)
15✔
3721
                        return log_debug_errno(r, "Failed to extract designator: %m");
×
3722
                if (r != 2 || !isempty(x))
15✔
3723
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unable to split: %s", word);
3✔
3724

3725
                PartitionDesignator d = partition_designator_from_string(designator);
12✔
3726
                if (d < 0)
12✔
3727
                        return log_debug_errno(d, "Failed to parse partition designator: %s", designator);
1✔
3728

3729
                if (!f) {
11✔
3730
                        f = new0(ImageFilter, 1);
5✔
3731
                        if (!f)
5✔
3732
                                return log_oom_debug();
×
3733
                } else if (f->pattern[d])
6✔
3734
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate pattern for '%s', refusing.", partition_designator_to_string(d));
1✔
3735

3736
                f->pattern[d] = TAKE_PTR(pattern);
10✔
3737
        }
3738

3739
        if (ret)
3✔
3740
                *ret = TAKE_PTR(f);
3✔
3741

3742
        return 0;
3743
}
3744

3745
static char *build_auxiliary_path(const char *image, const char *suffix) {
718✔
3746
        const char *e;
718✔
3747
        char *n;
718✔
3748

3749
        assert(image);
718✔
3750
        assert(suffix);
718✔
3751

3752
        e = endswith(image, ".raw");
718✔
3753
        if (!e)
718✔
3754
                return strjoin(e, suffix);
226✔
3755

3756
        n = new(char, e - image + strlen(suffix) + 1);
492✔
3757
        if (!n)
492✔
3758
                return NULL;
3759

3760
        strcpy(mempcpy(n, image, e - image), suffix);
492✔
3761
        return n;
492✔
3762
}
3763

3764
void verity_settings_done(VeritySettings *v) {
28,029✔
3765
        assert(v);
28,029✔
3766

3767
        iovec_done(&v->root_hash);
28,029✔
3768
        iovec_done(&v->root_hash_sig);
28,029✔
3769
        v->data_path = mfree(v->data_path);
28,029✔
3770
}
28,029✔
3771

3772
VeritySettings* verity_settings_free(VeritySettings *v) {
2✔
3773
        if (!v)
2✔
3774
                return NULL;
3775

3776
        verity_settings_done(v);
2✔
3777
        return mfree(v);
2✔
3778
}
3779

3780
void verity_settings_hash_func(const VeritySettings *s, struct siphash *state) {
4✔
3781
        assert(s);
4✔
3782

3783
        siphash24_compress_typesafe(s->root_hash.iov_len, state);
4✔
3784
        siphash24_compress(s->root_hash.iov_base, s->root_hash.iov_len, state);
4✔
3785
}
4✔
3786

3787
int verity_settings_compare_func(const VeritySettings *x, const VeritySettings *y) {
2✔
3788
        assert(x);
2✔
3789
        assert(y);
2✔
3790

3791
        return iovec_memcmp(&x->root_hash, &y->root_hash);
2✔
3792
}
3793

3794
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(verity_settings_hash_ops, VeritySettings, verity_settings_hash_func, verity_settings_compare_func, VeritySettings, verity_settings_free);
2✔
3795

3796
int verity_settings_load(
249✔
3797
                VeritySettings *verity,
3798
                const char *image,
3799
                const char *root_hash_path,
3800
                const char *root_hash_sig_path) {
3801

3802
        PartitionDesignator designator;
249✔
3803
        int r;
249✔
3804

3805
        assert(verity);
249✔
3806
        assert(image);
249✔
3807
        assert(verity->designator < 0 || IN_SET(verity->designator, PARTITION_ROOT, PARTITION_USR));
249✔
3808

3809
        /* If we are asked to load the root hash for a device node, exit early */
3810
        if (is_device_path(image))
249✔
3811
                return 0;
249✔
3812

3813
        r = secure_getenv_bool("SYSTEMD_DISSECT_VERITY_SIDECAR");
249✔
3814
        if (r < 0 && r != -ENXIO)
249✔
3815
                log_debug_errno(r, "Failed to parse $SYSTEMD_DISSECT_VERITY_SIDECAR, ignoring: %m");
×
3816
        if (r == 0)
249✔
3817
                return 0;
3818

3819
        designator = verity->designator;
249✔
3820

3821
        /* We only fill in what isn't already filled in */
3822

3823
        _cleanup_(iovec_done) struct iovec root_hash = {};
249✔
3824
        if (!iovec_is_set(&verity->root_hash)) {
249✔
3825
                _cleanup_free_ char *text = NULL;
223✔
3826

3827
                if (root_hash_path) {
223✔
3828
                        /* If explicitly specified it takes precedence */
3829
                        r = read_one_line_file(root_hash_path, &text);
×
3830
                        if (r < 0)
×
3831
                                return r;
3832

3833
                        if (designator < 0)
×
3834
                                designator = PARTITION_ROOT;
×
3835
                } else {
3836
                        /* Otherwise look for xattr and separate file, and first for the data for root and if
3837
                         * that doesn't exist for /usr */
3838

3839
                        if (designator < 0 || designator == PARTITION_ROOT) {
223✔
3840
                                r = getxattr_malloc(image, "user.verity.roothash", &text, /* ret_size= */ NULL);
223✔
3841
                                if (r < 0) {
223✔
3842
                                        _cleanup_free_ char *p = NULL;
223✔
3843

3844
                                        if (r != -ENOENT && !ERRNO_IS_XATTR_ABSENT(r))
223✔
3845
                                                return r;
3846

3847
                                        p = build_auxiliary_path(image, ".roothash");
223✔
3848
                                        if (!p)
223✔
3849
                                                return -ENOMEM;
3850

3851
                                        r = read_one_line_file(p, &text);
223✔
3852
                                        if (r < 0 && r != -ENOENT)
223✔
3853
                                                return r;
3854
                                }
3855

3856
                                if (text)
223✔
3857
                                        designator = PARTITION_ROOT;
47✔
3858
                        }
3859

3860
                        if (!text && (designator < 0 || designator == PARTITION_USR)) {
223✔
3861
                                /* So in the "roothash" xattr/file name above the "root" of course primarily
3862
                                 * refers to the root of the Verity Merkle tree. But coincidentally it also
3863
                                 * is the hash for the *root* file system, i.e. the "root" neatly refers to
3864
                                 * two distinct concepts called "root". Taking benefit of this happy
3865
                                 * coincidence we call the file with the root hash for the /usr/ file system
3866
                                 * `usrhash`, because `usrroothash` or `rootusrhash` would just be too
3867
                                 * confusing. We thus drop the reference to the root of the Merkle tree, and
3868
                                 * just indicate which file system it's about. */
3869
                                r = getxattr_malloc(image, "user.verity.usrhash", &text, /* ret_size= */ NULL);
176✔
3870
                                if (r < 0) {
176✔
3871
                                        _cleanup_free_ char *p = NULL;
176✔
3872

3873
                                        if (r != -ENOENT && !ERRNO_IS_XATTR_ABSENT(r))
176✔
3874
                                                return r;
3875

3876
                                        p = build_auxiliary_path(image, ".usrhash");
176✔
3877
                                        if (!p)
176✔
3878
                                                return -ENOMEM;
3879

3880
                                        r = read_one_line_file(p, &text);
176✔
3881
                                        if (r < 0 && r != -ENOENT)
176✔
3882
                                                return r;
3883
                                }
3884

3885
                                if (text)
176✔
3886
                                        designator = PARTITION_USR;
×
3887
                        }
3888
                }
3889

3890
                if (text) {
223✔
3891
                        r = unhexmem(text, &root_hash.iov_base, &root_hash.iov_len);
47✔
3892
                        if (r < 0)
47✔
3893
                                return r;
3894
                        if (root_hash.iov_len < sizeof(sd_id128_t))
47✔
3895
                                return -EINVAL;
3896
                }
3897
        }
3898

3899
        _cleanup_(iovec_done) struct iovec root_hash_sig = {};
249✔
3900
        if ((iovec_is_set(&root_hash) || iovec_is_set(&verity->root_hash)) && !iovec_is_set(&verity->root_hash_sig)) {
322✔
3901
                if (root_hash_sig_path) {
73✔
3902
                        r = read_full_file(root_hash_sig_path, (char**) &root_hash_sig.iov_base, &root_hash_sig.iov_len);
×
3903
                        if (r < 0 && r != -ENOENT)
×
3904
                                return r;
3905

3906
                        if (r >= 0 && root_hash_sig.iov_len == 0) /* refuse empty size signatures */
×
3907
                                return -EINVAL;
3908

3909
                        if (designator < 0)
×
3910
                                designator = PARTITION_ROOT;
×
3911
                } else {
3912
                        if (designator < 0 || designator == PARTITION_ROOT) {
73✔
3913
                                _cleanup_free_ char *p = NULL;
73✔
3914

3915
                                /* Follow naming convention recommended by the relevant RFC:
3916
                                 * https://tools.ietf.org/html/rfc5751#section-3.2.1 */
3917
                                p = build_auxiliary_path(image, ".roothash.p7s");
73✔
3918
                                if (!p)
73✔
3919
                                        return -ENOMEM;
3920

3921
                                r = read_full_file(p, (char**) &root_hash_sig.iov_base, &root_hash_sig.iov_len);
73✔
3922
                                if (r < 0 && r != -ENOENT)
73✔
3923
                                        return r;
3924
                                if (r >= 0) {
73✔
3925
                                        designator = PARTITION_ROOT;
48✔
3926
                                        if (root_hash_sig.iov_len == 0) /* refuse empty size signatures */
48✔
3927
                                                return -EINVAL;
3928
                                }
3929
                        }
3930

3931
                        if (!iovec_is_set(&root_hash_sig) && (designator < 0 || designator == PARTITION_USR)) {
73✔
3932
                                _cleanup_free_ char *p = NULL;
×
3933

3934
                                p = build_auxiliary_path(image, ".usrhash.p7s");
×
3935
                                if (!p)
×
3936
                                        return -ENOMEM;
3937

3938
                                r = read_full_file(p, (char**) &root_hash_sig.iov_base, &root_hash_sig.iov_len);
×
3939
                                if (r < 0 && r != -ENOENT)
×
3940
                                        return r;
3941
                                if (r >= 0) {
×
3942
                                        designator = PARTITION_USR;
×
3943
                                        if (root_hash_sig.iov_len == 0) /* refuse empty size signatures */
×
3944
                                                return -EINVAL;
3945
                                }
3946
                        }
3947
                }
3948
        }
3949

3950
        _cleanup_free_ char *verity_data_path = NULL;
249✔
3951
        if (!verity->data_path) {
249✔
3952
                _cleanup_free_ char *p = NULL;
246✔
3953

3954
                p = build_auxiliary_path(image, ".verity");
246✔
3955
                if (!p)
246✔
3956
                        return -ENOMEM;
3957

3958
                if (access(p, F_OK) < 0) {
246✔
3959
                        if (errno != ENOENT)
199✔
3960
                                return -errno;
×
3961
                } else
3962
                        verity_data_path = TAKE_PTR(p);
3963
        }
3964

3965
        if (iovec_is_set(&root_hash))
249✔
3966
                verity->root_hash = TAKE_STRUCT(root_hash);
47✔
3967

3968
        if (iovec_is_set(&root_hash_sig))
249✔
3969
                verity->root_hash_sig = TAKE_STRUCT(root_hash_sig);
48✔
3970

3971
        if (verity_data_path)
249✔
3972
                verity->data_path = TAKE_PTR(verity_data_path);
47✔
3973

3974
        if (verity->designator < 0)
249✔
3975
                verity->designator = designator;
219✔
3976

3977
        return 1;
3978
}
3979

3980
int verity_settings_copy(VeritySettings *dest, const VeritySettings *source) {
107✔
3981
        assert(dest);
107✔
3982

3983
        if (!source) {
107✔
3984
                *dest = VERITY_SETTINGS_DEFAULT;
×
3985
                return 0;
×
3986
        }
3987

3988
        _cleanup_(iovec_done) struct iovec rh = {};
107✔
3989
        if (iovec_is_set(&source->root_hash)) {
107✔
3990
                if (!iovec_memdup(&source->root_hash, &rh))
×
3991
                        return log_oom_debug();
×
3992
        }
3993

3994
        _cleanup_(iovec_done) struct iovec sig = {};
107✔
3995
        if (iovec_is_set(&source->root_hash_sig)) {
107✔
3996
                if (!iovec_memdup(&source->root_hash_sig, &sig))
×
3997
                        return log_oom_debug();
×
3998
        }
3999

4000
        _cleanup_free_ char *p = NULL;
107✔
4001
        if (source->data_path) {
107✔
4002
                p = strdup(source->data_path);
×
4003
                if (!p)
×
4004
                        return log_oom_debug();
×
4005
        }
4006

4007
        *dest = (VeritySettings) {
107✔
4008
                .root_hash = TAKE_STRUCT(rh),
107✔
4009
                .root_hash_sig = TAKE_STRUCT(sig),
107✔
4010
                .data_path = TAKE_PTR(p),
107✔
4011
                .designator = source->designator,
107✔
4012
        };
4013

4014
        return 1;
107✔
4015
}
4016

4017
int dissected_image_load_verity_sig_partition(
341✔
4018
                DissectedImage *m,
4019
                int fd,
4020
                VeritySettings *verity) {
4021

4022
        int r;
341✔
4023

4024
        assert(m);
341✔
4025
        assert(fd >= 0);
341✔
4026
        assert(verity);
341✔
4027

4028
        if (iovec_is_set(&verity->root_hash) && iovec_is_set(&verity->root_hash_sig)) /* Already loaded? */
400✔
4029
                return 0;
341✔
4030

4031
        r = secure_getenv_bool("SYSTEMD_DISSECT_VERITY_EMBEDDED");
301✔
4032
        if (r < 0 && r != -ENXIO)
301✔
4033
                log_debug_errno(r, "Failed to parse $SYSTEMD_DISSECT_VERITY_EMBEDDED, ignoring: %m");
×
4034
        if (r == 0)
301✔
4035
                return 0;
4036

4037
        PartitionDesignator dd = verity->designator;
301✔
4038
        if (dd < 0) {
301✔
4039
                if (m->partitions[PARTITION_ROOT_VERITY].found)
282✔
4040
                        dd = PARTITION_ROOT;
4041
                else if (m->partitions[PARTITION_USR_VERITY].found)
256✔
4042
                        dd = PARTITION_USR;
4043
                else
4044
                        return 0;
4045
        }
4046

4047
        if (!m->partitions[dd].found)
46✔
4048
                return 0;
4049

4050
        PartitionDesignator dv = partition_verity_hash_of(dd);
46✔
4051
        assert(dv >= 0);
46✔
4052
        if (!m->partitions[dv].found)
46✔
4053
                return 0;
4054

4055
        PartitionDesignator ds = partition_verity_sig_of(dd);
46✔
4056
        assert(ds >= 0);
46✔
4057

4058
        DissectedPartition *p = m->partitions + ds;
46✔
4059
        if (!p->found)
46✔
4060
                return 0;
4061

4062
        _cleanup_(iovec_done) struct iovec root_hash = {}, root_hash_sig = {};
43✔
4063
        r = acquire_sig_for_roothash(
43✔
4064
                        fd,
4065
                        p->offset,
4066
                        p->size,
4067
                        &root_hash,
4068
                        &root_hash_sig);
4069
        if (r < 0)
43✔
4070
                return r;
4071

4072
        /* Check if specified root hash matches if it is specified */
4073
        if (iovec_is_set(&verity->root_hash) &&
62✔
4074
            iovec_memcmp(&verity->root_hash, &root_hash) != 0) {
19✔
4075
                _cleanup_free_ char *a = NULL, *b = NULL;
×
4076

4077
                a = hexmem(root_hash.iov_base, root_hash.iov_len);
×
4078
                b = hexmem(verity->root_hash.iov_base, verity->root_hash.iov_len);
×
4079

4080
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Root hash in signature JSON data (%s) doesn't match configured hash (%s).", strna(a), strna(b));
×
4081
        }
4082

4083
        iovec_done(&verity->root_hash);
43✔
4084
        verity->root_hash = TAKE_STRUCT(root_hash);
43✔
4085

4086
        iovec_done(&verity->root_hash_sig);
43✔
4087
        verity->root_hash_sig = TAKE_STRUCT(root_hash_sig);
43✔
4088

4089
        verity->designator = dd;
43✔
4090

4091
        m->verity_ready = true;
43✔
4092
        m->verity_sig_ready = true;
43✔
4093
        m->partitions[dd].rw = false;
43✔
4094

4095
        return 1;
43✔
4096
}
4097

4098
int dissected_image_guess_verity_roothash(
341✔
4099
                DissectedImage *m,
4100
                VeritySettings *verity) {
4101

4102
        int r;
341✔
4103

4104
        assert(m);
341✔
4105
        assert(verity);
341✔
4106

4107
        /* Guesses the Verity root hash from the partitions we found, taking into account that as per
4108
         * https://uapi-group.org/specifications/specs/discoverable_partitions_specification/ the UUIDS of
4109
         * the data and verity partitions are respectively the first and second halves of the dm-verity
4110
         * roothash.
4111
         *
4112
         * Note of course that relying on this guesswork is mostly useful for later attestation, not so much
4113
         * for a-priori security. */
4114

4115
        if (iovec_is_set(&verity->root_hash)) /* Already loaded? */
341✔
4116
                return 0;
341✔
4117

4118
        r = secure_getenv_bool("SYSTEMD_DISSECT_VERITY_GUESS");
258✔
4119
        if (r < 0 && r != -ENXIO)
258✔
4120
                log_debug_errno(r, "Failed to parse $SYSTEMD_DISSECT_VERITY_GUESS, ignoring: %m");
×
4121
        if (r == 0)
258✔
4122
                return 0;
4123

4124
        PartitionDesignator dd = verity->designator;
258✔
4125
        if (dd < 0) {
258✔
4126
                if (m->partitions[PARTITION_ROOT_VERITY].found)
258✔
4127
                        dd = PARTITION_ROOT;
4128
                else if (m->partitions[PARTITION_USR_VERITY].found)
256✔
4129
                        dd = PARTITION_USR;
4130
                else
4131
                        return 0;
4132
        }
4133

4134
        DissectedPartition *d = m->partitions + dd;
3✔
4135
        if (!d->found)
3✔
4136
                return 0;
4137

4138
        PartitionDesignator dv = partition_verity_hash_of(dd);
3✔
4139
        assert(dv >= 0);
3✔
4140

4141
        DissectedPartition *p = m->partitions + dv;
3✔
4142
        if (!p->found)
3✔
4143
                return 0;
4144

4145
        _cleanup_free_ void *rh = malloc(sizeof(sd_id128_t) * 2);
3✔
4146
        if (!rh)
3✔
4147
                return log_oom_debug();
×
4148

4149
        memcpy(mempcpy(rh, &d->uuid, sizeof(sd_id128_t)), &p->uuid, sizeof(sd_id128_t));
3✔
4150
        verity->root_hash = IOVEC_MAKE(TAKE_PTR(rh), sizeof(sd_id128_t) * 2);
3✔
4151

4152
        verity->designator = dd;
3✔
4153

4154
        m->verity_ready = true;
3✔
4155
        m->partitions[dd].rw = false;
3✔
4156

4157
        return 0;
3✔
4158
}
4159

4160
int dissected_image_acquire_metadata(
70✔
4161
                DissectedImage *m,
4162
                int userns_fd,
4163
                DissectImageFlags extra_flags) {
4164

4165
        enum {
70✔
4166
                META_HOSTNAME,
4167
                META_MACHINE_ID,
4168
                META_MACHINE_INFO,
4169
                META_OS_RELEASE,
4170
                META_INITRD_RELEASE,
4171
                META_SYSEXT_RELEASE,
4172
                META_CONFEXT_RELEASE,
4173
                META_HAS_INIT_SYSTEM,
4174
                _META_MAX,
4175
        };
4176

4177
        static const char *const paths[_META_MAX] = {
70✔
4178
                [META_HOSTNAME]          = "/etc/hostname\0",
4179
                [META_MACHINE_ID]        = "/etc/machine-id\0",
4180
                [META_MACHINE_INFO]      = "/etc/machine-info\0",
4181
                [META_OS_RELEASE]        = "/etc/os-release\0"
4182
                                           "/usr/lib/os-release\0",
4183
                [META_INITRD_RELEASE]    = "/etc/initrd-release\0"
4184
                                           "/usr/lib/initrd-release\0",
4185
                [META_SYSEXT_RELEASE]    = "sysext-release\0",       /* String used only for logging. */
4186
                [META_CONFEXT_RELEASE]   = "confext-release\0",      /* ditto */
4187
                [META_HAS_INIT_SYSTEM]   = "has-init-system\0",      /* ditto */
4188
        };
4189

4190
        _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL, **initrd_release = NULL, **sysext_release = NULL, **confext_release = NULL;
70✔
4191
        _cleanup_free_ char *hostname = NULL, *t = NULL;
70✔
4192
        _cleanup_close_pair_ int error_pipe[2] = EBADF_PAIR;
70✔
4193
        _cleanup_(pidref_done_sigkill_wait) PidRef child = PIDREF_NULL;
70✔
4194
        sd_id128_t machine_id = SD_ID128_NULL;
70✔
4195
        unsigned n_meta_initialized = 0;
70✔
4196
        int fds[2 * _META_MAX], r, v;
70✔
4197
        int has_init_system = -1;
70✔
4198
        ssize_t n;
70✔
4199

4200
        assert(m);
70✔
4201

4202
        r = dlopen_libmount();
70✔
4203
        if (r < 0)
70✔
4204
                return r;
4205

4206
        for (; n_meta_initialized < _META_MAX; n_meta_initialized++) {
630✔
4207
                assert(paths[n_meta_initialized]);
560✔
4208

4209
                if (pipe2(fds + 2*n_meta_initialized, O_CLOEXEC) < 0) {
560✔
4210
                        r = -errno;
×
4211
                        goto finish;
×
4212
                }
4213
        }
4214

4215
        r = get_common_dissect_directory(&t);
70✔
4216
        if (r < 0)
70✔
4217
                goto finish;
×
4218

4219
        if (pipe2(error_pipe, O_CLOEXEC) < 0) {
70✔
4220
                r = -errno;
×
4221
                goto finish;
×
4222
        }
4223

4224
        r = pidref_safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM, &child);
70✔
4225
        if (r < 0)
121✔
4226
                goto finish;
×
4227
        if (r == 0) {
121✔
4228
                /* Child */
4229
                error_pipe[0] = safe_close(error_pipe[0]);
51✔
4230

4231
                if (userns_fd < 0)
51✔
4232
                        r = detach_mount_namespace_harder(0, 0);
50✔
4233
                else
4234
                        r = detach_mount_namespace_userns(userns_fd);
1✔
4235
                if (r < 0) {
51✔
4236
                        log_debug_errno(r, "Failed to detach mount namespace: %m");
×
4237
                        report_errno_and_exit(error_pipe[1], r);
×
4238
                }
4239

4240
                r = dissected_image_mount(
102✔
4241
                                m,
4242
                                t,
4243
                                /* uid_shift= */ UID_INVALID,
4244
                                /* uid_range= */ UID_INVALID,
4245
                                /* userns_fd= */ -EBADF,
4246
                                extra_flags |
4247
                                DISSECT_IMAGE_READ_ONLY |
4248
                                DISSECT_IMAGE_MOUNT_ROOT_ONLY |
51✔
4249
                                DISSECT_IMAGE_USR_NO_ROOT);
4250
                if (r < 0) {
51✔
4251
                        log_debug_errno(r, "Failed to mount dissected image: %m");
×
4252
                        report_errno_and_exit(error_pipe[1], r);
×
4253
                }
4254

4255
                for (unsigned k = 0; k < _META_MAX; k++) {
459✔
4256
                        _cleanup_close_ int fd = -ENOENT;
408✔
4257

4258
                        assert(paths[k]);
408✔
4259

4260
                        fds[2*k] = safe_close(fds[2*k]);
408✔
4261

4262
                        switch (k) {
408✔
4263

4264
                        case META_SYSEXT_RELEASE:
51✔
4265
                                if (!m->image_name)
51✔
4266
                                        goto next;
×
4267

4268
                                /* As per the os-release spec, if the image is an extension it will have a
4269
                                 * file named after the image name in extension-release.d/ - we use the image
4270
                                 * name and try to resolve it with the extension-release helpers, as
4271
                                 * sometimes the image names are mangled on deployment and do not match
4272
                                 * anymore.  Unlike other paths this is not fixed, and the image name can be
4273
                                 * mangled on deployment, so by calling into the helper we allow a fallback
4274
                                 * that matches on the first extension-release file found in the directory,
4275
                                 * if one named after the image cannot be found first. */
4276
                                r = open_extension_release(
51✔
4277
                                                t,
4278
                                                IMAGE_SYSEXT,
4279
                                                m->image_name,
4280
                                                /* relax_extension_release_check= */ false,
4281
                                                /* ret_path= */ NULL,
4282
                                                &fd);
4283
                                if (r < 0)
51✔
4284
                                        fd = r;
30✔
4285
                                break;
4286

4287
                        case META_CONFEXT_RELEASE:
51✔
4288
                                if (!m->image_name)
51✔
4289
                                        goto next;
×
4290

4291
                                /* As above */
4292
                                r = open_extension_release(
51✔
4293
                                                t,
4294
                                                IMAGE_CONFEXT,
4295
                                                m->image_name,
4296
                                                /* relax_extension_release_check= */ false,
4297
                                                /* ret_path= */ NULL,
4298
                                                &fd);
4299
                                if (r < 0)
51✔
4300
                                        fd = r;
46✔
4301

4302
                                break;
4303

4304
                        case META_HAS_INIT_SYSTEM: {
51✔
4305
                                bool found = false;
51✔
4306

4307
                                FOREACH_STRING(init,
189✔
4308
                                               "/usr/lib/systemd/systemd",  /* systemd on /usr/ merged system */
4309
                                               "/lib/systemd/systemd",      /* systemd on /usr/ non-merged systems */
4310
                                               "/sbin/init") {              /* traditional path the Linux kernel invokes */
4311

4312
                                        r = chase(init, t, CHASE_PREFIX_ROOT, NULL, NULL);
153✔
4313
                                        if (r < 0) {
153✔
4314
                                                if (r != -ENOENT)
138✔
4315
                                                        log_debug_errno(r, "Failed to resolve %s, ignoring: %m", init);
138✔
4316
                                        } else {
4317
                                                found = true;
15✔
4318
                                                break;
15✔
4319
                                        }
4320
                                }
4321

4322
                                r = loop_write(fds[2*k+1], &found, sizeof(found));
51✔
4323
                                if (r < 0)
51✔
4324
                                        report_errno_and_exit(error_pipe[1], r);
×
4325

4326
                                goto next;
51✔
4327
                        }
4328

4329
                        default:
4330
                                NULSTR_FOREACH(p, paths[k]) {
567✔
4331
                                        fd = chase_and_open(p, t, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
342✔
4332
                                        if (fd >= 0)
342✔
4333
                                                break;
4334
                                }
4335
                        }
4336

4337
                        if (fd < 0) {
357✔
4338
                                log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]);
301✔
4339
                                goto next;
301✔
4340
                        }
4341

4342
                        r = copy_bytes(fd, fds[2*k+1], UINT64_MAX, 0);
56✔
4343
                        if (r < 0)
56✔
4344
                                report_errno_and_exit(error_pipe[1], r);
×
4345

4346
                next:
56✔
4347
                        fds[2*k+1] = safe_close(fds[2*k+1]);
408✔
4348
                }
4349

4350
                _exit(EXIT_SUCCESS);
51✔
4351
        }
4352

4353
        error_pipe[1] = safe_close(error_pipe[1]);
70✔
4354

4355
        for (unsigned k = 0; k < _META_MAX; k++) {
630✔
4356
                _cleanup_fclose_ FILE *f = NULL;
560✔
4357

4358
                assert(paths[k]);
560✔
4359

4360
                fds[2*k+1] = safe_close(fds[2*k+1]);
560✔
4361

4362
                f = take_fdopen(&fds[2*k], "r");
560✔
4363
                if (!f) {
560✔
4364
                        r = -errno;
×
4365
                        goto finish;
×
4366
                }
4367

4368
                switch (k) {
560✔
4369

4370
                case META_HOSTNAME:
70✔
4371
                        r = read_etc_hostname_stream(f, /* substitute_wildcards= */ false, &hostname);
70✔
4372
                        if (r < 0)
70✔
4373
                                log_debug_errno(r, "Failed to read /etc/hostname of image: %m");
70✔
4374

4375
                        break;
4376

4377
                case META_MACHINE_ID: {
70✔
4378
                        _cleanup_free_ char *line = NULL;
70✔
4379

4380
                        r = read_line(f, LONG_LINE_MAX, &line);
70✔
4381
                        if (r < 0)
70✔
4382
                                log_debug_errno(r, "Failed to read /etc/machine-id of image: %m");
×
4383
                        else if (r == 33) {
70✔
4384
                                r = sd_id128_from_string(line, &machine_id);
×
4385
                                if (r < 0)
×
4386
                                        log_debug_errno(r, "Image contains invalid /etc/machine-id: %s", line);
×
4387
                        } else if (r == 0)
70✔
4388
                                log_debug("/etc/machine-id file of image is empty.");
55✔
4389
                        else if (streq(line, "uninitialized"))
15✔
4390
                                log_debug("/etc/machine-id file of image is uninitialized (likely aborted first boot).");
15✔
4391
                        else
4392
                                log_debug("/etc/machine-id file of image has unexpected length %i.", r);
×
4393

4394
                        break;
70✔
4395
                }
4396

4397
                case META_MACHINE_INFO:
70✔
4398
                        r = load_env_file_pairs(f, "machine-info", &machine_info);
70✔
4399
                        if (r < 0)
70✔
4400
                                log_debug_errno(r, "Failed to read /etc/machine-info of image: %m");
×
4401

4402
                        break;
4403

4404
                case META_OS_RELEASE:
70✔
4405
                        r = load_env_file_pairs(f, "os-release", &os_release);
70✔
4406
                        if (r < 0)
70✔
4407
                                log_debug_errno(r, "Failed to read OS release file of image: %m");
×
4408

4409
                        break;
4410

4411
                case META_INITRD_RELEASE:
70✔
4412
                        r = load_env_file_pairs(f, "initrd-release", &initrd_release);
70✔
4413
                        if (r < 0)
70✔
4414
                                log_debug_errno(r, "Failed to read initrd release file of image: %m");
×
4415

4416
                        break;
4417

4418
                case META_SYSEXT_RELEASE:
70✔
4419
                        r = load_env_file_pairs(f, "sysext-release", &sysext_release);
70✔
4420
                        if (r < 0)
70✔
4421
                                log_debug_errno(r, "Failed to read sysext release file of image: %m");
×
4422

4423
                        break;
4424

4425
                case META_CONFEXT_RELEASE:
70✔
4426
                        r = load_env_file_pairs(f, "confext-release", &confext_release);
70✔
4427
                        if (r < 0)
70✔
4428
                                log_debug_errno(r, "Failed to read confext release file of image: %m");
560✔
4429

4430
                        break;
4431

4432
                case META_HAS_INIT_SYSTEM: {
70✔
4433
                        bool b = false;
70✔
4434
                        size_t nr;
70✔
4435

4436
                        errno = 0;
70✔
4437
                        nr = fread(&b, 1, sizeof(b), f);
70✔
4438
                        if (nr != sizeof(b))
70✔
4439
                                log_debug_errno(errno_or_else(EIO), "Failed to read has-init-system boolean: %m");
×
4440
                        else
4441
                                has_init_system = b;
70✔
4442

4443
                        break;
70✔
4444
                }}
4445
        }
4446

4447
        r = pidref_wait_for_terminate_and_check("(sd-dissect)", &child, 0);
70✔
4448
        if (r < 0)
70✔
4449
                goto finish;
×
4450

4451
        pidref_done(&child);
70✔
4452

4453
        n = read(error_pipe[0], &v, sizeof(v));
70✔
4454
        if (n < 0) {
70✔
4455
                r = -errno;
×
4456
                goto finish;
×
4457
        }
4458
        if (n == sizeof(v)) {
70✔
4459
                r = v; /* propagate error sent to us from child */
×
4460
                goto finish;
×
4461
        }
4462
        if (n != 0) {
70✔
4463
                r = -EIO;
×
4464
                goto finish;
×
4465
        }
4466
        if (r != EXIT_SUCCESS) {
70✔
4467
                r = -EPROTO;
×
4468
                goto finish;
×
4469
        }
4470

4471
        free_and_replace(m->hostname, hostname);
70✔
4472
        m->machine_id = machine_id;
70✔
4473
        strv_free_and_replace(m->machine_info, machine_info);
70✔
4474
        strv_free_and_replace(m->os_release, os_release);
70✔
4475
        strv_free_and_replace(m->initrd_release, initrd_release);
70✔
4476
        strv_free_and_replace(m->sysext_release, sysext_release);
70✔
4477
        strv_free_and_replace(m->confext_release, confext_release);
70✔
4478
        m->has_init_system = has_init_system;
70✔
4479

4480
finish:
70✔
4481
        for (unsigned k = 0; k < n_meta_initialized; k++)
630✔
4482
                safe_close_pair(fds + 2*k);
560✔
4483

4484
        return r;
4485
}
4486

4487
Architecture dissected_image_architecture(DissectedImage *m) {
152✔
4488
        assert(m);
152✔
4489

4490
        if (m->partitions[PARTITION_ROOT].found &&
152✔
4491
            m->partitions[PARTITION_ROOT].architecture >= 0)
152✔
4492
                return m->partitions[PARTITION_ROOT].architecture;
4493

4494
        if (m->partitions[PARTITION_USR].found &&
19✔
4495
            m->partitions[PARTITION_USR].architecture >= 0)
×
4496
                return m->partitions[PARTITION_USR].architecture;
×
4497

4498
        return _ARCHITECTURE_INVALID;
4499
}
4500

4501
bool dissected_image_is_portable(DissectedImage *m) {
32✔
4502
        return m && strv_env_pairs_get(m->os_release, "PORTABLE_PREFIXES");
32✔
4503
}
4504

4505
bool dissected_image_is_initrd(DissectedImage *m) {
32✔
4506
        return m && !strv_isempty(m->initrd_release);
32✔
4507
}
4508

4509
int dissect_loop_device(
2,000✔
4510
                LoopDevice *loop,
4511
                const VeritySettings *verity,
4512
                const MountOptions *mount_options,
4513
                const ImagePolicy *image_policy,
4514
                const ImageFilter *image_filter,
4515
                DissectImageFlags flags,
4516
                DissectedImage **ret) {
4517

4518
#if HAVE_BLKID
4519
        _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
2,000✔
4520
        int r;
2,000✔
4521

4522
        assert(loop);
2,000✔
4523

4524
        r = dissected_image_new(loop->backing_file ?: loop->node, &m);
2,000✔
4525
        if (r < 0)
2,000✔
4526
                return r;
4527

4528
        m->loop = loop_device_ref(loop);
2,000✔
4529
        m->image_size = m->loop->device_size;
2,000✔
4530
        m->sector_size = m->loop->sector_size;
2,000✔
4531

4532
        r = dissect_image(
4,000✔
4533
                        m,
4534
                        loop->fd,
4535
                        loop->node,
2,000✔
4536
                        verity,
4537
                        mount_options,
4538
                        image_policy,
4539
                        image_filter,
4540
                        flags);
4541
        if (r < 0)
2,000✔
4542
                return r;
4543

4544
        if (ret)
1,941✔
4545
                *ret = TAKE_PTR(m);
1,941✔
4546

4547
        return 0;
4548
#else
4549
        return -EOPNOTSUPP;
4550
#endif
4551
}
4552

4553
int dissect_loop_device_and_warn(
127✔
4554
                LoopDevice *loop,
4555
                const VeritySettings *verity,
4556
                const MountOptions *mount_options,
4557
                const ImagePolicy *image_policy,
4558
                const ImageFilter *image_filter,
4559
                DissectImageFlags flags,
4560
                DissectedImage **ret) {
4561

4562
        assert(loop);
127✔
4563

4564
        return dissect_log_error(
127✔
4565
                        LOG_ERR,
4566
                        dissect_loop_device(loop, verity, mount_options, image_policy, image_filter, flags, ret),
4567
                        loop->backing_file ?: loop->node,
127✔
4568
                        verity);
4569
}
4570

4571
bool dissected_image_verity_candidate(const DissectedImage *image, PartitionDesignator partition_designator) {
48✔
4572
        assert(image);
48✔
4573

4574
        /* Checks if this partition could theoretically do Verity. For non-partitioned images this only works
4575
         * if there's an external verity file supplied, for which we can consult .has_verity. For partitioned
4576
         * images we only check the partition type.
4577
         *
4578
         * This call is used to decide whether to suppress or show a verity column in tabular output of the
4579
         * image. */
4580

4581
        if (image->single_file_system)
48✔
4582
                return partition_designator == PARTITION_ROOT && image->has_verity;
10✔
4583

4584
        return partition_verity_hash_of(partition_designator) >= 0;
43✔
4585
}
4586

4587
bool dissected_image_verity_ready(const DissectedImage *image, PartitionDesignator partition_designator) {
4✔
4588
        PartitionDesignator k;
4✔
4589

4590
        assert(image);
4✔
4591

4592
        /* Checks if this partition has verity data available that we can activate. For non-partitioned this
4593
         * works for the root partition, for others only if the associated verity partition was found. */
4594

4595
        if (!image->verity_ready)
4✔
4596
                return false;
4597

4598
        if (image->single_file_system)
×
4599
                return partition_designator == PARTITION_ROOT;
×
4600

4601
        k = partition_verity_hash_of(partition_designator);
×
4602
        return k >= 0 && image->partitions[k].found;
×
4603
}
4604

4605
bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesignator partition_designator) {
17✔
4606
        PartitionDesignator k;
17✔
4607

4608
        assert(image);
17✔
4609

4610
        /* Checks if this partition has verity signature data available that we can use. */
4611

4612
        if (!image->verity_sig_ready)
17✔
4613
                return false;
4614

4615
        if (image->single_file_system)
13✔
4616
                return partition_designator == PARTITION_ROOT;
×
4617

4618
        k = partition_verity_sig_of(partition_designator);
13✔
4619
        return k >= 0 && image->partitions[k].found;
13✔
4620
}
4621

4622
int mount_options_set_and_consume(MountOptions **options, PartitionDesignator d, char *s) {
5✔
4623
        assert(options);
5✔
4624
        assert(d >= 0);
5✔
4625

4626
        if (!*options) {
5✔
4627
                *options = new0(MountOptions, 1);
5✔
4628
                if (!*options) {
5✔
4629
                        free(s);
×
4630
                        return log_oom();
×
4631
                }
4632
        }
4633

4634
        free_and_replace((*options)->options[d], s);
5✔
4635

4636
        return 0;
5✔
4637
}
4638

4639
int mount_options_dup(const MountOptions *source, MountOptions **ret) {
5✔
4640
        _cleanup_(mount_options_free_allp) MountOptions *options = NULL;
5✔
4641

4642
        assert(source);
5✔
4643
        assert(ret);
5✔
4644

4645
        options = new0(MountOptions, 1);
5✔
4646
        if (!options)
5✔
4647
                return log_oom();
×
4648

4649
        for (PartitionDesignator d = 0; d < _PARTITION_DESIGNATOR_MAX; d++)
70✔
4650
                if (source->options[d]) {
65✔
4651
                        options->options[d] = strdup(source->options[d]);
5✔
4652
                        if (!options->options[d])
5✔
4653
                                return log_oom_debug();
×
4654
                }
4655

4656
        *ret = TAKE_PTR(options);
5✔
4657
        return 0;
5✔
4658
}
4659

4660
int mount_options_to_string(const MountOptions *mount_options, char **ret) {
×
4661
        _cleanup_free_ char *s = NULL;
×
4662

4663
        assert(mount_options);
×
4664
        assert(ret);
×
4665

4666
        for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++)
×
4667
                if (!isempty(mount_options->options[i]))
×
4668
                        if (!strextend_with_separator(&s, ":", "%s:%s",
×
4669
                                       partition_designator_to_string(i),
4670
                                       mount_options->options[i]))
4671
                                return log_oom_debug();
×
4672

4673
        *ret = TAKE_PTR(s);
×
4674

4675
        return 0;
×
4676
}
4677

4678
MountOptions* mount_options_free_all(MountOptions *options) {
52,069✔
4679
        if (!options)
52,069✔
4680
                return NULL;
4681

4682
        free_many_charp(options->options, _PARTITION_DESIGNATOR_MAX);
6✔
4683

4684
        return mfree(options);
6✔
4685
}
4686

4687
const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator) {
2,259✔
4688
        assert(designator >= 0 && designator < _PARTITION_DESIGNATOR_MAX);
2,259✔
4689

4690
        if (!options)
2,259✔
4691
                return NULL;
4692

4693
        return options->options[designator];
19✔
4694
}
4695

4696
int mount_image_privately_interactively(
28✔
4697
                const char *image,
4698
                const ImagePolicy *image_policy,
4699
                DissectImageFlags flags,
4700
                char **ret_directory,
4701
                int *ret_dir_fd,
4702
                LoopDevice **ret_loop_device) {
4703

4704
        _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
28✔
4705
        _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
28✔
4706
        _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
×
4707
        _cleanup_free_ char *dir = NULL;
28✔
4708
        int r;
28✔
4709

4710
        /* Mounts an OS image at a temporary place, inside a newly created mount namespace of our own. This
4711
         * is used by tools such as systemd-tmpfiles or systemd-firstboot to operate on some disk image
4712
         * easily. */
4713

4714
        assert(image);
28✔
4715
        assert(ret_loop_device);
28✔
4716

4717
        /* We intend to mount this right-away, hence add the partitions if needed and pin them. */
4718
        flags |= DISSECT_IMAGE_ADD_PARTITION_DEVICES |
28✔
4719
                DISSECT_IMAGE_PIN_PARTITION_DEVICES;
4720

4721
        r = verity_settings_load(&verity, image, NULL, NULL);
28✔
4722
        if (r < 0)
28✔
4723
                return log_error_errno(r, "Failed to load root hash data: %m");
×
4724

4725
        r = loop_device_make_by_path(
56✔
4726
                        image,
4727
                        FLAGS_SET(flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR,
4728
                        /* sector_size= */ UINT32_MAX,
4729
                        FLAGS_SET(flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
4730
                        LOCK_SH,
4731
                        &d);
4732
        if (r < 0)
28✔
4733
                return log_error_errno(r, "Failed to set up loopback device for %s: %m", image);
×
4734

4735
        r = dissect_loop_device_and_warn(
28✔
4736
                        d,
4737
                        &verity,
4738
                        /* mount_options= */ NULL,
4739
                        image_policy,
4740
                        /* image_filter= */ NULL,
4741
                        flags,
4742
                        &dissected_image);
4743
        if (r < 0)
28✔
4744
                return r;
4745

4746
        r = dissected_image_load_verity_sig_partition(dissected_image, d->fd, &verity);
28✔
4747
        if (r < 0)
28✔
4748
                return r;
4749

4750
        r = dissected_image_guess_verity_roothash(dissected_image, &verity);
28✔
4751
        if (r < 0)
28✔
4752
                return r;
4753

4754
        r = dissected_image_decrypt_interactively(dissected_image, NULL, &verity, image_policy, flags);
28✔
4755
        if (r < 0)
28✔
4756
                return r;
4757

4758
        r = detach_mount_namespace();
28✔
4759
        if (r < 0)
28✔
4760
                return log_error_errno(r, "Failed to detach mount namespace: %m");
×
4761

4762
        r = mkdir_p("/run/systemd/mount-rootfs", 0555);
28✔
4763
        if (r < 0)
28✔
4764
                return log_error_errno(r, "Failed to create mount point: %m");
×
4765

4766
        r = dissected_image_mount_and_warn(
28✔
4767
                        dissected_image,
4768
                        "/run/systemd/mount-rootfs",
4769
                        /* uid_shift= */ UID_INVALID,
4770
                        /* uid_range= */ UID_INVALID,
4771
                        /* userns_fd= */ -EBADF,
4772
                        flags);
4773
        if (r < 0)
28✔
4774
                return r;
4775

4776
        r = loop_device_flock(d, LOCK_UN);
28✔
4777
        if (r < 0)
28✔
4778
                return r;
4779

4780
        r = dissected_image_relinquish(dissected_image);
28✔
4781
        if (r < 0)
28✔
4782
                return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m");
×
4783

4784
        if (ret_directory) {
28✔
4785
                dir = strdup("/run/systemd/mount-rootfs");
28✔
4786
                if (!dir)
28✔
4787
                        return log_oom();
×
4788
        }
4789

4790
        if (ret_dir_fd) {
28✔
4791
                _cleanup_close_ int dir_fd = -EBADF;
28✔
4792

4793
                dir_fd = open("/run/systemd/mount-rootfs", O_CLOEXEC|O_DIRECTORY);
×
4794
                if (dir_fd < 0)
×
4795
                        return log_error_errno(errno, "Failed to open mount point directory: %m");
×
4796

4797
                *ret_dir_fd = TAKE_FD(dir_fd);
×
4798
        }
4799

4800
        if (ret_directory)
28✔
4801
                *ret_directory = TAKE_PTR(dir);
28✔
4802

4803
        *ret_loop_device = TAKE_PTR(d);
28✔
4804
        return 0;
28✔
4805
}
4806

4807
static bool mount_options_relax_extension_release_checks(const MountOptions *options) {
78✔
4808
        if (!options)
78✔
4809
                return false;
4810

4811
        return string_contains_word(mount_options_from_designator(options, PARTITION_ROOT), ",", "x-systemd.relax-extension-release-check") ||
12✔
4812
                        string_contains_word(mount_options_from_designator(options, PARTITION_USR), ",", "x-systemd.relax-extension-release-check");
6✔
4813
}
4814

4815
int verity_dissect_and_mount(
78✔
4816
                int src_fd,
4817
                const char *src,
4818
                const char *dest,
4819
                const MountOptions *options,
4820
                const ImagePolicy *image_policy,
4821
                const ImageFilter *image_filter,
4822
                const ExtensionReleaseData *extension_release_data,
4823
                ImageClass required_class,
4824
                VeritySettings *verity,
4825
                RuntimeScope runtime_scope,
4826
                DissectedImage **ret_image) {
4827

4828
        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
78✔
4829
        _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
78✔
4830
        _cleanup_(verity_settings_done) VeritySettings local_verity = VERITY_SETTINGS_DEFAULT;
×
4831
        _cleanup_close_ int userns_fd = -EBADF;
78✔
4832
        DissectImageFlags dissect_image_flags;
78✔
4833
        bool relax_extension_release_check;
78✔
4834
        int r;
78✔
4835

4836
        assert(src);
78✔
4837
        /* Verifying release metadata requires mounted image for now, so ensure the check is skipped when
4838
         * opening an image without mounting it immediately (i.e.: 'dest' is NULL). */
4839
        assert(!extension_release_data || dest);
78✔
4840

4841
        relax_extension_release_check = mount_options_relax_extension_release_checks(options);
78✔
4842

4843
        /* We might get an FD for the image, but we use the original path to look for the dm-verity files.
4844
         * The caller might also give us a pre-loaded VeritySettings, in which case we just use it. It will
4845
         * also be extended, as dissected_image_load_verity_sig_partition() is invoked. */
4846
        if (!verity) {
78✔
4847
                r = verity_settings_load(&local_verity, src, NULL, NULL);
2✔
4848
                if (r < 0)
2✔
4849
                        return log_debug_errno(r, "Failed to load root hash: %m");
×
4850

4851
                verity = &local_verity;
4852
        }
4853

4854
        dissect_image_flags =
156✔
4855
                (verity->data_path ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0) |
78✔
4856
                (relax_extension_release_check ? DISSECT_IMAGE_RELAX_EXTENSION_CHECK : 0) |
78✔
4857
                DISSECT_IMAGE_ADD_PARTITION_DEVICES |
4858
                DISSECT_IMAGE_PIN_PARTITION_DEVICES |
4859
                DISSECT_IMAGE_ALLOW_USERSPACE_VERITY |
78✔
4860
                DISSECT_IMAGE_VERITY_SHARE;
4861

4862
        /* First check if we have a verity device already open and with a fstype pinned by policy. If it
4863
         * cannot be found, then fallback to the slow path (full dissect). */
4864
        r = dissected_image_new_from_existing_verity(
80✔
4865
                        src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src,
2✔
4866
                        verity,
4867
                        options,
4868
                        image_policy,
4869
                        image_filter,
4870
                        runtime_scope,
4871
                        dissect_image_flags,
4872
                        &dissected_image);
4873
        if (r < 0 && !ERRNO_IS_NEG_DEVICE_ABSENT(r) && r != -ENOPKG)
78✔
4874
                return r;
4875
        if (r >= 0)
×
4876
                log_debug("Reusing pre-existing verity-protected image %s", src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src);
×
4877
        else {
4878
                if (runtime_scope == RUNTIME_SCOPE_SYSTEM) {
78✔
4879
                        /* Note that we don't use loop_device_make here, as the FD is most likely O_PATH which would not be
4880
                        * accepted by LOOP_CONFIGURE, so just let loop_device_make_by_path reopen it as a regular FD. */
4881
                        r = loop_device_make_by_path(
74✔
4882
                                        src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src,
2✔
4883
                                        /* open_flags= */ -1,
4884
                                        /* sector_size= */ UINT32_MAX,
4885
                                        verity->data_path ? 0 : LO_FLAGS_PARTSCAN,
74✔
4886
                                        LOCK_SH,
4887
                                        &loop_device);
4888
                        if (r < 0)
74✔
4889
                                return log_debug_errno(r, "Failed to create loop device for image: %m");
1✔
4890

4891
                        r = dissect_loop_device(
73✔
4892
                                        loop_device,
4893
                                        verity,
4894
                                        options,
4895
                                        image_policy,
4896
                                        image_filter,
4897
                                        dissect_image_flags,
4898
                                        &dissected_image);
4899
                        /* No partition table? Might be a single-filesystem image, try again */
4900
                        if (!verity->data_path && r == -ENOPKG)
73✔
4901
                                r = dissect_loop_device(
54✔
4902
                                                loop_device,
4903
                                                verity,
4904
                                                options,
4905
                                                image_policy,
4906
                                                image_filter,
4907
                                                dissect_image_flags | DISSECT_IMAGE_NO_PARTITION_TABLE,
54✔
4908
                                                &dissected_image);
4909
                        if (r < 0)
73✔
4910
                                return log_debug_errno(r, "Failed to dissect image: %m");
×
4911

4912
                        r = dissected_image_load_verity_sig_partition(dissected_image, loop_device->fd, verity);
73✔
4913
                        if (r < 0)
73✔
4914
                                return r;
4915

4916
                        r = dissected_image_guess_verity_roothash(dissected_image, verity);
73✔
4917
                        if (r < 0)
73✔
4918
                                return r;
4919

4920
                        r = dissected_image_decrypt(
73✔
4921
                                        dissected_image,
4922
                                        /* root= */ NULL,
4923
                                        /* passphrase= */ NULL,
4924
                                        verity,
4925
                                        image_policy,
4926
                                        dissect_image_flags);
4927
                        if (r < 0)
73✔
4928
                                return log_debug_errno(r, "Failed to decrypt dissected image: %m");
×
4929
                } else {
4930
                        userns_fd = namespace_open_by_type(NAMESPACE_USER);
4✔
4931
                        if (userns_fd < 0)
4✔
4932
                                return log_debug_errno(userns_fd, "Failed to open our own user namespace: %m");
4✔
4933

4934
                        r = mountfsd_mount_image(
4✔
4935
                                        src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src,
×
4936
                                        userns_fd,
4937
                                        image_policy,
4938
                                        verity,
4939
                                        dissect_image_flags,
4940
                                        &dissected_image);
4941
                        if (r < 0)
4✔
4942
                                return r;
4943
                }
4944
        }
4945

4946
        if (dest) {
73✔
4947
                r = mkdir_p_label(dest, 0755);
71✔
4948
                if (r < 0)
71✔
4949
                        return log_debug_errno(r, "Failed to create destination directory %s: %m", dest);
×
4950
                r = umount_recursive(dest, 0);
71✔
4951
                if (r < 0)
71✔
4952
                        return log_debug_errno(r, "Failed to umount under destination directory %s: %m", dest);
×
4953
        }
4954

4955
        r = dissected_image_mount(
73✔
4956
                        dissected_image,
4957
                        dest,
4958
                        /* uid_shift= */ UID_INVALID,
4959
                        /* uid_range= */ UID_INVALID,
4960
                        userns_fd,
4961
                        dissect_image_flags);
4962
        if (r < 0)
73✔
4963
                return log_debug_errno(r, "Failed to mount image: %m");
×
4964

4965
        if (loop_device) {
73✔
4966
                r = loop_device_flock(loop_device, LOCK_UN);
73✔
4967
                if (r < 0)
73✔
4968
                        return log_debug_errno(r, "Failed to unlock loopback device: %m");
×
4969
        }
4970

4971
        /* If we got os-release values from the caller, then we need to match them with the image's
4972
         * extension-release.d/ content. Return -EINVAL if there's any mismatch.
4973
         * First, check the distro ID. If that matches, then check the new SYSEXT_LEVEL value if
4974
         * available, or else fallback to VERSION_ID. If neither is present (eg: rolling release),
4975
         * then a simple match on the ID will be performed. Also if an extension class was specified,
4976
         * check that it matches or return ENOCSI (which looks like error-no-class if one squints enough). */
4977
        if ((extension_release_data && extension_release_data->os_release_id) || required_class >= 0) {
73✔
4978
                _cleanup_strv_free_ char **extension_release = NULL;
3✔
4979
                ImageClass class = IMAGE_SYSEXT;
13✔
4980

4981
                r = load_extension_release_pairs(dest, required_class >= 0 ? required_class : IMAGE_SYSEXT, dissected_image->image_name, relax_extension_release_check, &extension_release);
19✔
4982
                if (r == -ENOENT) {
13✔
4983
                        if (required_class >= 0)
6✔
4984
                                return log_debug_errno(SYNTHETIC_ERRNO(ENOCSI), "Image %s extension-release metadata does not match the expected class", dissected_image->image_name);
3✔
4985

4986
                        r = load_extension_release_pairs(dest, IMAGE_CONFEXT, dissected_image->image_name, relax_extension_release_check, &extension_release);
3✔
4987
                        if (r >= 0)
3✔
4988
                                class = IMAGE_CONFEXT;
4989
                }
4990
                if (r < 0)
7✔
4991
                        return log_debug_errno(r, "Failed to parse image %s extension-release metadata: %m", dissected_image->image_name);
×
4992

4993
                if (extension_release_data && !isempty(extension_release_data->os_release_id)) {
20✔
4994
                        r = extension_release_validate(
10✔
4995
                                        dissected_image->image_name,
10✔
4996
                                        extension_release_data->os_release_id,
4997
                                        extension_release_data->os_release_id_like,
10✔
4998
                                        extension_release_data->os_release_version_id,
10✔
4999
                                        class == IMAGE_SYSEXT ? extension_release_data->os_release_sysext_level : extension_release_data->os_release_confext_level,
5000
                                        extension_release_data->os_release_extension_scope,
10✔
5001
                                        extension_release,
5002
                                        class);
5003
                        if (r == 0)
10✔
5004
                                return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Image %s extension-release metadata does not match the root's", dissected_image->image_name);
×
5005
                        if (r < 0)
10✔
5006
                                return log_debug_errno(r, "Failed to compare image %s extension-release metadata with the root's os-release: %m", dissected_image->image_name);
×
5007
                }
5008
        }
5009

5010
        r = dissected_image_relinquish(dissected_image);
70✔
5011
        if (r < 0)
70✔
5012
                return log_debug_errno(r, "Failed to relinquish dissected image: %m");
×
5013

5014
        if (ret_image)
70✔
5015
                *ret_image = TAKE_PTR(dissected_image);
2✔
5016

5017
        return 0;
5018
}
5019

5020
void extension_release_data_done(ExtensionReleaseData *data) {
76✔
5021
        assert(data);
76✔
5022

5023
        data->os_release_id = mfree(data->os_release_id);
76✔
5024
        data->os_release_id_like = mfree(data->os_release_id_like);
76✔
5025
        data->os_release_version_id = mfree(data->os_release_version_id);
76✔
5026
        data->os_release_sysext_level = mfree(data->os_release_sysext_level);
76✔
5027
        data->os_release_confext_level = mfree(data->os_release_confext_level);
76✔
5028
        data->os_release_extension_scope = mfree(data->os_release_extension_scope);
76✔
5029
}
76✔
5030

5031
int get_common_dissect_directory(char **ret) {
86✔
5032
        _cleanup_free_ char *t = NULL;
86✔
5033
        int r;
86✔
5034

5035
        /* A common location we mount dissected images to. The assumption is that everyone who uses this
5036
         * function runs in their own private mount namespace (with mount propagation off on /run/systemd/,
5037
         * and thus can mount something here without affecting anyone else). */
5038

5039
        t = strdup("/run/systemd/dissect-root");
86✔
5040
        if (!t)
86✔
5041
                return log_oom_debug();
×
5042

5043
        r = mkdir_parents(t, 0755);
86✔
5044
        if (r < 0)
86✔
5045
                return log_debug_errno(r, "Failed to create parent dirs of mount point '%s': %m", t);
×
5046

5047
        r = RET_NERRNO(mkdir(t, 0000)); /* It's supposed to be overmounted, hence let's make this inaccessible */
86✔
5048
        if (r < 0 && r != -EEXIST)
86✔
5049
                return log_debug_errno(r, "Failed to create mount point '%s': %m", t);
×
5050

5051
        if (ret)
86✔
5052
                *ret = TAKE_PTR(t);
86✔
5053

5054
        return 0;
5055
}
5056

5057
#if HAVE_BLKID
5058

5059
static JSON_DISPATCH_ENUM_DEFINE(dispatch_architecture, Architecture, architecture_from_string);
10✔
5060
static JSON_DISPATCH_ENUM_DEFINE(dispatch_partition_designator, PartitionDesignator, partition_designator_from_string);
12✔
5061

5062
typedef struct PartitionFields {
5063
        PartitionDesignator designator;
5064
        bool rw;
5065
        bool growfs;
5066
        unsigned partno;
5067
        Architecture architecture;
5068
        sd_id128_t uuid;
5069
        char *fstype;
5070
        char *label;
5071
        uint64_t size;
5072
        uint64_t offset;
5073
        unsigned fsmount_fd_idx;
5074
} PartitionFields;
5075

5076
static void partition_fields_done(PartitionFields *f) {
12✔
5077
        assert(f);
12✔
5078

5079
        f->fstype = mfree(f->fstype);
12✔
5080
        f->label = mfree(f->label);
12✔
5081
}
12✔
5082

5083
typedef struct MountImageReplyParameters {
5084
        sd_json_variant *partitions;
5085
        char *image_policy;
5086
        uint64_t image_size;
5087
        uint32_t sector_size;
5088
        sd_id128_t image_uuid;
5089
} MountImageReplyParameters;
5090

5091
static void mount_image_reply_parameters_done(MountImageReplyParameters *p) {
18✔
5092
        assert(p);
18✔
5093

5094
        p->image_policy = mfree(p->image_policy);
18✔
5095
        p->partitions = sd_json_variant_unref(p->partitions);
18✔
5096
}
18✔
5097

5098
#endif
5099

5100
int mountfsd_mount_image_fd(
18✔
5101
                int image_fd,
5102
                int userns_fd,
5103
                const ImagePolicy *image_policy,
5104
                const VeritySettings *verity,
5105
                DissectImageFlags flags,
5106
                DissectedImage **ret) {
5107

5108
#if HAVE_BLKID
5109
        _cleanup_(mount_image_reply_parameters_done) MountImageReplyParameters p = {};
18✔
5110

5111
        static const sd_json_dispatch_field dispatch_table[] = {
18✔
5112
                { "partitions",  SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_variant, offsetof(struct MountImageReplyParameters, partitions),   SD_JSON_MANDATORY },
5113
                { "imagePolicy", SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,  offsetof(struct MountImageReplyParameters, image_policy), 0              },
5114
                { "imageSize",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,  offsetof(struct MountImageReplyParameters, image_size),   SD_JSON_MANDATORY },
5115
                { "sectorSize",  _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32,  offsetof(struct MountImageReplyParameters, sector_size),  SD_JSON_MANDATORY },
5116
                { "imageUuid",   SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,   offsetof(struct MountImageReplyParameters, image_uuid),   0              },
5117
                {}
5118
        };
5119

5120
        _cleanup_(dissected_image_unrefp) DissectedImage *di = NULL;
×
5121
        _cleanup_close_ int verity_data_fd = -EBADF;
18✔
5122
        _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
18✔
5123
        _cleanup_free_ char *ps = NULL;
18✔
5124
        const char *error_id;
18✔
5125
        int r;
18✔
5126

5127
        assert(image_fd >= 0);
18✔
5128
        assert(ret);
18✔
5129

5130
        r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.MountFileSystem");
18✔
5131
        if (r < 0)
18✔
5132
                return log_error_errno(r, "Failed to connect to mountfsd: %m");
×
5133

5134
        r = sd_varlink_set_allow_fd_passing_input(vl, true);
18✔
5135
        if (r < 0)
18✔
5136
                return log_error_errno(r, "Failed to enable varlink fd passing for read: %m");
×
5137

5138
        r = sd_varlink_set_allow_fd_passing_output(vl, true);
18✔
5139
        if (r < 0)
18✔
5140
                return log_error_errno(r, "Failed to enable varlink fd passing for write: %m");
×
5141

5142
        _cleanup_close_ int reopened_fd = -EBADF;
18✔
5143

5144
        image_fd = fd_reopen_condition(image_fd, O_CLOEXEC|O_NOCTTY|O_NONBLOCK|(FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_READ_ONLY) ? O_RDONLY : O_RDWR), O_PATH, &reopened_fd);
30✔
5145
        if (image_fd < 0)
18✔
5146
                return log_error_errno(image_fd, "Failed to reopen fd: %m");
×
5147

5148
        r = sd_varlink_push_dup_fd(vl, image_fd);
18✔
5149
        if (r < 0)
18✔
5150
                return log_error_errno(r, "Failed to push image fd into varlink connection: %m");
×
5151

5152
        if (userns_fd >= 0) {
18✔
5153
                r = sd_varlink_push_dup_fd(vl, userns_fd);
18✔
5154
                if (r < 0)
18✔
5155
                        return log_error_errno(r, "Failed to push image fd into varlink connection: %m");
×
5156
        }
5157

5158
        if (image_policy) {
18✔
5159
                r = image_policy_to_string(image_policy, /* simplify= */ false, &ps);
6✔
5160
                if (r < 0)
6✔
5161
                        return log_error_errno(r, "Failed to format image policy to string: %m");
×
5162
        }
5163

5164
        if (verity && verity->data_path) {
18✔
5165
                verity_data_fd = open(verity->data_path, O_RDONLY|O_CLOEXEC);
4✔
5166
                if (verity_data_fd < 0)
4✔
5167
                        return log_error_errno(errno, "Failed to open verity data file '%s': %m", verity->data_path);
×
5168

5169
                r = sd_varlink_push_dup_fd(vl, verity_data_fd);
4✔
5170
                if (r < 0)
4✔
5171
                        return log_error_errno(r, "Failed to push verity data fd into varlink connection: %m");
×
5172
        }
5173

5174
        sd_json_variant *reply = NULL;
18✔
5175
        r = varlink_callbo_and_log(
24✔
5176
                        vl,
5177
                        "io.systemd.MountFileSystem.MountImage",
5178
                        &reply,
5179
                        &error_id,
5180
                        SD_JSON_BUILD_PAIR_UNSIGNED("imageFileDescriptor", 0),
5181
                        SD_JSON_BUILD_PAIR_CONDITION(userns_fd >= 0, "userNamespaceFileDescriptor", SD_JSON_BUILD_UNSIGNED(1)),
5182
                        SD_JSON_BUILD_PAIR_BOOLEAN("readOnly", FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_READ_ONLY)),
5183
                        SD_JSON_BUILD_PAIR_BOOLEAN("growFileSystems", FLAGS_SET(flags, DISSECT_IMAGE_GROWFS)),
5184
                        SD_JSON_BUILD_PAIR_CONDITION(!!ps, "imagePolicy", SD_JSON_BUILD_STRING(ps)),
5185
                        SD_JSON_BUILD_PAIR_BOOLEAN("veritySharing", FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)),
5186
                        SD_JSON_BUILD_PAIR_CONDITION(verity_data_fd >= 0, "verityDataFileDescriptor", SD_JSON_BUILD_UNSIGNED(userns_fd >= 0 ? 2 : 1)),
5187
                        SD_JSON_BUILD_PAIR_CONDITION(verity && iovec_is_set(&verity->root_hash), "verityRootHash", JSON_BUILD_IOVEC_HEX(&verity->root_hash)),
5188
                        SD_JSON_BUILD_PAIR_CONDITION(verity && iovec_is_set(&verity->root_hash_sig), "verityRootHashSignature", JSON_BUILD_IOVEC_BASE64(&verity->root_hash_sig)),
5189
                        SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH)));
5190
        if (r < 0)
18✔
5191
                return r;
5192

5193
        r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
12✔
5194
        if (r < 0)
12✔
5195
                return log_error_errno(r, "Failed to parse MountImage() reply: %m");
×
5196

5197
        log_debug("Effective image policy: %s", p.image_policy);
12✔
5198

5199
        sd_json_variant *i;
12✔
5200
        JSON_VARIANT_ARRAY_FOREACH(i, p.partitions) {
24✔
5201
                _cleanup_close_ int fsmount_fd = -EBADF;
12✔
5202

5203
                _cleanup_(partition_fields_done) PartitionFields pp = {
×
5204
                        .designator = _PARTITION_DESIGNATOR_INVALID,
5205
                        .architecture = _ARCHITECTURE_INVALID,
5206
                        .size = UINT64_MAX,
5207
                        .offset = UINT64_MAX,
5208
                        .fsmount_fd_idx = UINT_MAX,
5209
                };
5210

5211
                static const sd_json_dispatch_field partition_dispatch_table[] = {
12✔
5212
                        { "designator",          SD_JSON_VARIANT_STRING,        dispatch_partition_designator, offsetof(struct PartitionFields, designator),       SD_JSON_MANDATORY },
5213
                        { "writable",            SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,      offsetof(struct PartitionFields, rw),               SD_JSON_MANDATORY },
5214
                        { "growFileSystem",      SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,      offsetof(struct PartitionFields, growfs),           SD_JSON_MANDATORY },
5215
                        { "partitionNumber",     _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint,         offsetof(struct PartitionFields, partno),           0                 },
5216
                        { "architecture",        SD_JSON_VARIANT_STRING,        dispatch_architecture,         offsetof(struct PartitionFields, architecture),     0                 },
5217
                        { "partitionUuid",       SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,        offsetof(struct PartitionFields, uuid),             0                 },
5218
                        { "fileSystemType",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,       offsetof(struct PartitionFields, fstype),           SD_JSON_MANDATORY },
5219
                        { "partitionLabel",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,       offsetof(struct PartitionFields, label),            0                 },
5220
                        { "size",                _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,       offsetof(struct PartitionFields, size),             SD_JSON_MANDATORY },
5221
                        { "offset",              _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,       offsetof(struct PartitionFields, offset),           SD_JSON_MANDATORY },
5222
                        { "mountFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint,         offsetof(struct PartitionFields, fsmount_fd_idx),   SD_JSON_MANDATORY },
5223
                        {}
5224
                };
5225

5226
                r = sd_json_dispatch(i, partition_dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &pp);
12✔
5227
                if (r < 0)
12✔
5228
                        return log_error_errno(r, "Failed to parse partition data: %m");
×
5229

5230
                if (pp.fsmount_fd_idx != UINT_MAX) {
12✔
5231
                        fsmount_fd = sd_varlink_take_fd(vl, pp.fsmount_fd_idx);
12✔
5232
                        if (fsmount_fd < 0)
12✔
5233
                                return fsmount_fd;
5234
                }
5235

5236
                assert(pp.designator >= 0);
12✔
5237

5238
                if (!di) {
12✔
5239
                        r = dissected_image_new(/* path= */ NULL, &di);
12✔
5240
                        if (r < 0)
12✔
5241
                                return log_error_errno(r, "Failed to allocated new dissected image structure: %m");
×
5242
                }
5243

5244
                if (di->partitions[pp.designator].found)
12✔
5245
                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Duplicate partition data for '%s'.", partition_designator_to_string(pp.designator));
×
5246

5247
                di->partitions[pp.designator] = (DissectedPartition) {
12✔
5248
                        .found = true,
5249
                        .rw = pp.rw,
12✔
5250
                        .growfs = pp.growfs,
12✔
5251
                        .partno = pp.partno,
12✔
5252
                        .architecture = pp.architecture,
12✔
5253
                        .uuid = pp.uuid,
5254
                        .fstype = TAKE_PTR(pp.fstype),
12✔
5255
                        .label = TAKE_PTR(pp.label),
12✔
5256
                        .mount_node_fd = -EBADF,
5257
                        .size = pp.size,
12✔
5258
                        .offset = pp.offset,
12✔
5259
                        .fsmount_fd = TAKE_FD(fsmount_fd),
12✔
5260
                };
5261
        }
5262

5263
        di->image_size = p.image_size;
12✔
5264
        di->sector_size = p.sector_size;
12✔
5265
        di->image_uuid = p.image_uuid;
12✔
5266

5267
        *ret = TAKE_PTR(di);
12✔
5268
        return 0;
12✔
5269
#else
5270
        return -EOPNOTSUPP;
5271
#endif
5272
}
5273

5274
int mountfsd_mount_image(
18✔
5275
                const char *path,
5276
                int userns_fd,
5277
                const ImagePolicy *image_policy,
5278
                const VeritySettings *verity,
5279
                DissectImageFlags flags,
5280
                DissectedImage **ret) {
5281

5282
        int r;
18✔
5283

5284
        assert(path);
18✔
5285
        assert(ret);
18✔
5286

5287
        _cleanup_close_ int image_fd = open(path, O_RDONLY|O_CLOEXEC);
36✔
5288
        if (image_fd < 0)
18✔
5289
                return log_error_errno(errno, "Failed to open '%s': %m", path);
×
5290

5291
        _cleanup_(dissected_image_unrefp) DissectedImage *di = NULL;
18✔
5292
        r = mountfsd_mount_image_fd(image_fd, userns_fd, image_policy, verity, flags, &di);
18✔
5293
        if (r < 0)
18✔
5294
                return r;
5295

5296
        if (!di->image_name) {
12✔
5297
                r = make_image_name(path, &di->image_name);
12✔
5298
                if (r < 0)
12✔
5299
                        return r;
5300
        }
5301

5302
        *ret = TAKE_PTR(di);
12✔
5303
        return 0;
12✔
5304
}
5305

5306
int mountfsd_mount_directory_fd(
30✔
5307
                int directory_fd,
5308
                int userns_fd,
5309
                DissectImageFlags flags,
5310
                int *ret_mount_fd) {
5311

5312
        int r;
30✔
5313

5314
        assert(directory_fd >= 0);
30✔
5315
        assert(ret_mount_fd);
30✔
5316

5317
        /* Pick one identity, not both, that makes no sense. */
5318
        assert(!FLAGS_SET(flags, DISSECT_IMAGE_FOREIGN_UID|DISSECT_IMAGE_IDENTITY_UID));
30✔
5319

5320
        _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
30✔
5321
        r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.MountFileSystem");
30✔
5322
        if (r < 0)
30✔
5323
                return log_error_errno(r, "Failed to connect to mountfsd: %m");
×
5324

5325
        r = sd_varlink_set_allow_fd_passing_input(vl, true);
30✔
5326
        if (r < 0)
30✔
5327
                return log_error_errno(r, "Failed to enable varlink fd passing for read: %m");
×
5328

5329
        r = sd_varlink_set_allow_fd_passing_output(vl, true);
30✔
5330
        if (r < 0)
30✔
5331
                return log_error_errno(r, "Failed to enable varlink fd passing for write: %m");
×
5332

5333
        r = sd_varlink_push_dup_fd(vl, directory_fd);
30✔
5334
        if (r < 0)
30✔
5335
                return log_error_errno(r, "Failed to push directory fd into varlink connection: %m");
×
5336

5337
        if (userns_fd >= 0) {
30✔
5338
                r = sd_varlink_push_dup_fd(vl, userns_fd);
30✔
5339
                if (r < 0)
30✔
5340
                        return log_error_errno(r, "Failed to push user namespace fd into varlink connection: %m");
×
5341
        }
5342

5343
        sd_json_variant *reply = NULL;
30✔
5344
        const char *error_id = NULL;
30✔
5345
        r = varlink_callbo_and_log(
45✔
5346
                        vl,
5347
                        "io.systemd.MountFileSystem.MountDirectory",
5348
                        &reply,
5349
                        &error_id,
5350
                        SD_JSON_BUILD_PAIR_UNSIGNED("directoryFileDescriptor", 0),
5351
                        SD_JSON_BUILD_PAIR_CONDITION(userns_fd >= 0, "userNamespaceFileDescriptor", SD_JSON_BUILD_UNSIGNED(1)),
5352
                        SD_JSON_BUILD_PAIR_BOOLEAN("readOnly", FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_READ_ONLY)),
5353
                        SD_JSON_BUILD_PAIR_STRING("mode", FLAGS_SET(flags, DISSECT_IMAGE_FOREIGN_UID) ? "foreign" :
5354
                                                          FLAGS_SET(flags, DISSECT_IMAGE_IDENTITY_UID) ? "identity" : "auto"),
5355
                        SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH)));
5356
        if (r < 0)
30✔
5357
                return r;
5358

5359
        static const sd_json_dispatch_field dispatch_table[] = {
30✔
5360
                { "mountFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, 0, SD_JSON_MANDATORY },
5361
                {}
5362
        };
5363

5364
        unsigned fsmount_fd_idx = UINT_MAX;
30✔
5365
        r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &fsmount_fd_idx);
30✔
5366
        if (r < 0)
30✔
5367
                return log_error_errno(r, "Failed to parse MountImage() reply: %m");
×
5368

5369
        _cleanup_close_ int fsmount_fd = sd_varlink_take_fd(vl, fsmount_fd_idx);
60✔
5370
        if (fsmount_fd < 0)
30✔
5371
                return log_error_errno(fsmount_fd, "Failed to take mount fd from Varlink connection: %m");
×
5372

5373
        *ret_mount_fd = TAKE_FD(fsmount_fd);
30✔
5374
        return 0;
30✔
5375
}
5376

5377
int mountfsd_mount_directory(
22✔
5378
                const char *path,
5379
                int userns_fd,
5380
                DissectImageFlags flags,
5381
                int *ret_mount_fd) {
5382

5383
        assert(path);
22✔
5384
        assert(ret_mount_fd);
22✔
5385

5386
        _cleanup_close_ int directory_fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_PATH);
44✔
5387
        if (directory_fd < 0)
22✔
5388
                return log_error_errno(errno, "Failed to open '%s': %m", path);
×
5389

5390
        return mountfsd_mount_directory_fd(directory_fd, userns_fd, flags, ret_mount_fd);
22✔
5391
}
5392

5393
int mountfsd_make_directory_fd(
6✔
5394
                int parent_fd,
5395
                const char *name,
5396
                mode_t mode,
5397
                DissectImageFlags flags,
5398
                int *ret_directory_fd) {
5399

5400
        int r;
6✔
5401

5402
        assert(parent_fd >= 0);
6✔
5403
        assert(name);
6✔
5404

5405
        _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
6✔
5406
        r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.MountFileSystem");
6✔
5407
        if (r < 0)
6✔
5408
                return log_error_errno(r, "Failed to connect to mountfsd: %m");
×
5409

5410
        r = sd_varlink_set_allow_fd_passing_input(vl, true);
6✔
5411
        if (r < 0)
6✔
5412
                return log_error_errno(r, "Failed to enable varlink fd passing for read: %m");
×
5413

5414
        r = sd_varlink_set_allow_fd_passing_output(vl, true);
6✔
5415
        if (r < 0)
6✔
5416
                return log_error_errno(r, "Failed to enable varlink fd passing for write: %m");
×
5417

5418
        r = sd_varlink_push_dup_fd(vl, parent_fd);
6✔
5419
        if (r < 0)
6✔
5420
                return log_error_errno(r, "Failed to push parent fd into varlink connection: %m");
×
5421

5422
        sd_json_variant *reply = NULL;
6✔
5423
        const char *error_id = NULL;
6✔
5424
        r = varlink_callbo_and_log(
6✔
5425
                        vl,
5426
                        "io.systemd.MountFileSystem.MakeDirectory",
5427
                        &reply,
5428
                        &error_id,
5429
                        SD_JSON_BUILD_PAIR_UNSIGNED("parentFileDescriptor", 0),
5430
                        SD_JSON_BUILD_PAIR_STRING("name", name),
5431
                        SD_JSON_BUILD_PAIR_CONDITION(!IN_SET(mode, MODE_INVALID, 0700), "mode", SD_JSON_BUILD_UNSIGNED(mode)), /* suppress this field if default/unset */
5432
                        SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH)));
5433
        if (r < 0)
6✔
5434
                return r;
5435

5436
        static const sd_json_dispatch_field dispatch_table[] = {
6✔
5437
                { "directoryFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, 0, SD_JSON_MANDATORY },
5438
                {}
5439
        };
5440

5441
        unsigned directory_fd_idx = UINT_MAX;
6✔
5442
        r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &directory_fd_idx);
6✔
5443
        if (r < 0)
6✔
5444
                return log_error_errno(r, "Failed to parse MountImage() reply: %m");
×
5445

5446
        _cleanup_close_ int directory_fd = sd_varlink_take_fd(vl, directory_fd_idx);
12✔
5447
        if (directory_fd < 0)
6✔
5448
                return log_error_errno(directory_fd, "Failed to take directory fd from Varlink connection: %m");
×
5449

5450
        if (ret_directory_fd)
6✔
5451
                *ret_directory_fd = TAKE_FD(directory_fd);
6✔
5452
        return 0;
5453
}
5454

5455
int mountfsd_make_directory(
6✔
5456
                const char *path,
5457
                mode_t mode,
5458
                DissectImageFlags flags,
5459
                int *ret_directory_fd) {
5460

5461
        int r;
6✔
5462

5463
        _cleanup_free_ char *parent = NULL;
6✔
5464
        r = path_extract_directory(path, &parent);
6✔
5465
        if (r < 0)
6✔
5466
                return log_error_errno(r, "Failed to extract parent directory from '%s': %m", path);
×
5467

5468
        _cleanup_free_ char *dirname = NULL;
6✔
5469
        r = path_extract_filename(path, &dirname);
6✔
5470
        if (r < 0)
6✔
5471
                return log_error_errno(r, "Failed to extract directory name from '%s': %m", path);
×
5472

5473
        _cleanup_close_ int fd = open(parent, O_DIRECTORY|O_CLOEXEC);
12✔
5474
        if (fd < 0)
6✔
5475
                return log_error_errno(r, "Failed to open '%s': %m", parent);
×
5476

5477
        return mountfsd_make_directory_fd(fd, dirname, mode, flags, ret_directory_fd);
6✔
5478
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc