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

systemd / systemd / 13424846548

19 Feb 2025 10:09PM UTC coverage: 71.768% (+0.02%) from 71.753%
13424846548

push

github

web-flow
tree-wide: tweaks to mount point inode creation (#36308)

Some love for make_mount_point_inode_from_xyz() and ports PID 1 over to
it for mount units.

Alternative to #36290

58 of 91 new or added lines in 7 files covered. (63.74%)

1322 existing lines in 48 files now uncovered.

293681 of 409206 relevant lines covered (71.77%)

717000.31 hits per line

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

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

3
#if HAVE_VALGRIND_MEMCHECK_H
4
#include <valgrind/memcheck.h>
5
#endif
6

7
#include <linux/dm-ioctl.h>
8
#include <linux/loop.h>
9
#include <sys/file.h>
10
#include <sys/mount.h>
11
#include <sys/prctl.h>
12
#include <sys/wait.h>
13
#include <sysexits.h>
14

15
#if HAVE_OPENSSL
16
#include <openssl/err.h>
17
#include <openssl/pem.h>
18
#include <openssl/x509.h>
19
#endif
20

21
#include "sd-device.h"
22
#include "sd-id128.h"
23
#include "sd-json.h"
24
#include "sd-varlink.h"
25

26
#include "architecture.h"
27
#include "ask-password-api.h"
28
#include "blkid-util.h"
29
#include "blockdev-util.h"
30
#include "btrfs-util.h"
31
#include "chase.h"
32
#include "conf-files.h"
33
#include "constants.h"
34
#include "copy.h"
35
#include "cryptsetup-util.h"
36
#include "device-nodes.h"
37
#include "device-private.h"
38
#include "device-util.h"
39
#include "devnum-util.h"
40
#include "discover-image.h"
41
#include "dissect-image.h"
42
#include "dm-util.h"
43
#include "env-file.h"
44
#include "env-util.h"
45
#include "extension-util.h"
46
#include "fd-util.h"
47
#include "fileio.h"
48
#include "fs-util.h"
49
#include "fsck-util.h"
50
#include "gpt.h"
51
#include "hexdecoct.h"
52
#include "hostname-setup.h"
53
#include "id128-util.h"
54
#include "import-util.h"
55
#include "io-util.h"
56
#include "json-util.h"
57
#include "missing_mount.h"
58
#include "missing_syscall.h"
59
#include "mkdir-label.h"
60
#include "mount-util.h"
61
#include "mountpoint-util.h"
62
#include "namespace-util.h"
63
#include "nulstr-util.h"
64
#include "openssl-util.h"
65
#include "os-util.h"
66
#include "path-util.h"
67
#include "proc-cmdline.h"
68
#include "process-util.h"
69
#include "raw-clone.h"
70
#include "resize-fs.h"
71
#include "signal-util.h"
72
#include "sparse-endian.h"
73
#include "stat-util.h"
74
#include "stdio-util.h"
75
#include "string-table.h"
76
#include "string-util.h"
77
#include "strv.h"
78
#include "tmpfile-util.h"
79
#include "udev-util.h"
80
#include "user-util.h"
81
#include "xattr-util.h"
82

83
/* how many times to wait for the device nodes to appear */
84
#define N_DEVICE_NODE_LIST_ATTEMPTS 10
85

86
int dissect_fstype_ok(const char *fstype) {
289✔
87
        const char *e;
289✔
88
        bool b;
289✔
89

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

94
        e = secure_getenv("SYSTEMD_DISSECT_FILE_SYSTEMS");
289✔
95
        if (e) {
289✔
96
                _cleanup_strv_free_ char **l = NULL;
×
97

98
                l = strv_split(e, ":");
×
99
                if (!l)
×
100
                        return -ENOMEM;
×
101

102
                b = strv_contains(l, fstype);
×
103
        } else
104
                b = STR_IN_SET(fstype,
289✔
105
                               "btrfs",
106
                               "erofs",
107
                               "ext4",
108
                               "f2fs",
109
                               "squashfs",
110
                               "vfat",
111
                               "xfs");
112
        if (b)
289✔
113
                return true;
114

115
        log_debug("File system type '%s' is not allowed to be mounted as result of automatic dissection.", fstype);
289✔
116
        return false;
117
}
118

119
int probe_sector_size(int fd, uint32_t *ret) {
1,854✔
120

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

124
        assert_cc(sizeof(GptHeader) == 92);
1,854✔
125

126
        /* We expect a sector size in the range 512…4096. The GPT header is located in the second
127
         * sector. Hence it could be at byte 512 at the earliest, and at byte 4096 at the latest. And we must
128
         * read with granularity of the largest sector size we care about. Which means 8K. */
129
        uint8_t sectors[2 * 4096];
1,854✔
130
        uint32_t found = 0;
1,854✔
131
        ssize_t n;
1,854✔
132

133
        assert(fd >= 0);
1,854✔
134
        assert(ret);
1,854✔
135

136
        n = pread(fd, sectors, sizeof(sectors), 0);
1,854✔
137
        if (n < 0)
1,854✔
138
                return -errno;
×
139
        if (n != sizeof(sectors)) /* too short? */
1,854✔
140
                goto not_found;
590✔
141

142
        /* Let's see if we find the GPT partition header with various expected sector sizes */
143
        for (uint32_t sz = 512; sz <= 4096; sz <<= 1) {
6,320✔
144
                const GptHeader *p;
5,056✔
145

146
                assert(sizeof(sectors) >= sz * 2);
5,056✔
147
                p = (const GptHeader*) (sectors + sz);
5,056✔
148

149
                if (!gpt_header_has_signature(p))
5,056✔
150
                        continue;
4,926✔
151

152
                if (found != 0)
130✔
153
                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTUNIQ),
×
154
                                               "Detected valid partition table at offsets matching multiple sector sizes, refusing.");
155

156
                found = sz;
157
        }
158

159
        if (found != 0) {
1,264✔
160
                log_debug("Determined sector size %" PRIu32 " based on discovered partition table.", found);
130✔
161
                *ret = found;
130✔
162
                return 1; /* indicate we *did* find it */
130✔
163
        }
164

165
not_found:
1,134✔
166
        log_debug("Couldn't find any partition table to derive sector size of.");
1,724✔
167
        *ret = 512; /* pick the traditional default */
1,724✔
168
        return 0;   /* indicate we didn't find it */
1,724✔
169
}
170

171
int probe_sector_size_prefer_ioctl(int fd, uint32_t *ret) {
311✔
172
        struct stat st;
311✔
173

174
        assert(fd >= 0);
311✔
175
        assert(ret);
311✔
176

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

180
        if (fstat(fd, &st) < 0)
311✔
181
                return -errno;
×
182

183
        if (S_ISBLK(st.st_mode))
311✔
184
                return blockdev_get_sector_size(fd, ret);
269✔
185

186
        return probe_sector_size(fd, ret);
42✔
187
}
188

189
int probe_filesystem_full(
153✔
190
                int fd,
191
                const char *path,
192
                uint64_t offset,
193
                uint64_t size,
194
                char **ret_fstype) {
195

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

200
#if HAVE_BLKID
201
        _cleanup_(blkid_free_probep) blkid_probe b = NULL;
153✔
202
        _cleanup_free_ char *path_by_fd = NULL;
153✔
203
        _cleanup_close_ int fd_close = -EBADF;
153✔
204
        const char *fstype;
153✔
205
        int r;
153✔
206

207
        assert(fd >= 0 || path);
153✔
208
        assert(ret_fstype);
153✔
209

210
        if (fd < 0) {
153✔
211
                fd_close = open(path, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
×
212
                if (fd_close < 0)
×
213
                        return -errno;
×
214

215
                fd = fd_close;
216
        }
217

218
        if (!path) {
153✔
219
                r = fd_get_path(fd, &path_by_fd);
×
220
                if (r < 0)
×
221
                        return r;
222

223
                path = path_by_fd;
×
224
        }
225

226
        if (size == 0) /* empty size? nothing found! */
153✔
227
                goto not_found;
×
228

229
        b = blkid_new_probe();
153✔
230
        if (!b)
153✔
231
                return -ENOMEM;
232

233
        /* The Linux kernel maintains separate block device caches for main ("whole") and partition block
234
         * devices, which means making a change to one might not be reflected immediately when reading via
235
         * the other. That's massively confusing when mixing accesses to such devices. Let's address this in
236
         * a limited way: when probing a file system that is not at the beginning of the block device we
237
         * apparently probe a partition via the main block device, and in that case let's first flush the
238
         * main block device cache, so that we get the data that the per-partition block device last
239
         * sync'ed on.
240
         *
241
         * This only works under the assumption that any tools that write to the partition block devices
242
         * issue an syncfs()/fsync() on the device after making changes. Typically file system formatting
243
         * tools that write a superblock onto a partition block device do that, however. */
244
        if (offset != 0)
153✔
245
                if (ioctl(fd, BLKFLSBUF, 0) < 0)
12✔
246
                        log_debug_errno(errno, "Failed to flush block device cache, ignoring: %m");
12✔
247

248
        errno = 0;
153✔
249
        r = blkid_probe_set_device(
153✔
250
                        b,
251
                        fd,
252
                        offset,
253
                        size == UINT64_MAX ? 0 : size); /* when blkid sees size=0 it understands "everything". We prefer using UINT64_MAX for that */
254
        if (r != 0)
153✔
255
                return errno_or_else(ENOMEM);
×
256

257
        blkid_probe_enable_superblocks(b, 1);
153✔
258
        blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
153✔
259

260
        errno = 0;
153✔
261
        r = blkid_do_safeprobe(b);
153✔
262
        if (r == _BLKID_SAFEPROBE_NOT_FOUND)
153✔
263
                goto not_found;
4✔
264
        if (r == _BLKID_SAFEPROBE_AMBIGUOUS)
149✔
265
                return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN),
×
266
                                       "Results ambiguous for partition %s", path);
267
        if (r == _BLKID_SAFEPROBE_ERROR)
149✔
268
                return log_debug_errno(errno_or_else(EIO), "Failed to probe partition %s: %m", path);
×
269

270
        assert(r == _BLKID_SAFEPROBE_FOUND);
149✔
271

272
        (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
149✔
273

274
        if (fstype) {
149✔
275
                log_debug("Probed fstype '%s' on partition %s.", fstype, path);
149✔
276
                return strdup_to_full(ret_fstype, fstype);
149✔
277
        }
278

279
not_found:
×
280
        log_debug("No type detected on partition %s", path);
4✔
281
        *ret_fstype = NULL;
4✔
282
        return 0;
4✔
283
#else
284
        return -EOPNOTSUPP;
285
#endif
286
}
287

288
#if HAVE_BLKID
289
static int image_policy_may_use(
1,976✔
290
                const ImagePolicy *policy,
291
                PartitionDesignator designator) {
292

293
        PartitionPolicyFlags f;
1,976✔
294

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

298
        f = image_policy_get_exhaustively(policy, designator);
1,976✔
299
        if (f < 0)
1,976✔
300
                return f;
301

302
        if ((f & _PARTITION_POLICY_USE_MASK) == PARTITION_POLICY_ABSENT)
1,976✔
303
                /* only flag set in policy is "absent"? then this partition may not exist at all */
304
                return log_debug_errno(
4✔
305
                                SYNTHETIC_ERRNO(ERFKILL),
306
                                "Partition of designator '%s' exists, but not allowed by policy, refusing.",
307
                                partition_designator_to_string(designator));
308
        if ((f & _PARTITION_POLICY_USE_MASK & ~PARTITION_POLICY_ABSENT) == PARTITION_POLICY_UNUSED) {
1,972✔
309
                /* only "unused" or "unused" + "absent" are set? then don't use it */
310
                log_debug("Partition of designator '%s' exists, and policy dictates to ignore it, doing so.",
25✔
311
                          partition_designator_to_string(designator));
312
                return false; /* ignore! */
25✔
313
        }
314

315
        return true; /* use! */
316
}
317

318
static int image_policy_check_protection(
2,978✔
319
                const ImagePolicy *policy,
320
                PartitionDesignator designator,
321
                PartitionPolicyFlags found_flags) {
322

323
        PartitionPolicyFlags policy_flags;
2,978✔
324

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

327
        if (found_flags < 0)
2,978✔
328
                return found_flags;
329

330
        policy_flags = image_policy_get_exhaustively(policy, designator);
2,978✔
331
        if (policy_flags < 0)
2,978✔
332
                return policy_flags;
333

334
        if ((found_flags & policy_flags) == 0) {
2,978✔
335
                _cleanup_free_ char *found_flags_string = NULL, *policy_flags_string = NULL;
3✔
336

337
                (void) partition_policy_flags_to_string(found_flags, /* simplify= */ true, &found_flags_string);
3✔
338
                (void) partition_policy_flags_to_string(policy_flags, /* simplify= */ true, &policy_flags_string);
3✔
339

340
                return log_debug_errno(SYNTHETIC_ERRNO(ERFKILL), "Partition %s discovered with policy '%s' but '%s' was required, refusing.",
3✔
341
                                       partition_designator_to_string(designator),
342
                                       strnull(found_flags_string), strnull(policy_flags_string));
343
        }
344

345
        return 0;
346
}
347

348
static int image_policy_check_partition_flags(
1,929✔
349
                const ImagePolicy *policy,
350
                PartitionDesignator designator,
351
                uint64_t gpt_flags) {
352

353
        PartitionPolicyFlags policy_flags;
1,929✔
354
        bool b;
1,929✔
355

356
        /* Checks if the partition flags in the policy match reality */
357

358
        policy_flags = image_policy_get_exhaustively(policy, designator);
1,929✔
359
        if (policy_flags < 0)
1,929✔
360
                return policy_flags;
361

362
        b = FLAGS_SET(gpt_flags, SD_GPT_FLAG_READ_ONLY);
1,929✔
363
        if ((policy_flags & _PARTITION_POLICY_READ_ONLY_MASK) == (b ? PARTITION_POLICY_READ_ONLY_OFF : PARTITION_POLICY_READ_ONLY_ON))
3,816✔
364
                return log_debug_errno(SYNTHETIC_ERRNO(ERFKILL), "Partition %s has 'read-only' flag incorrectly set (must be %s, is %s), refusing.",
×
365
                                       partition_designator_to_string(designator),
366
                                       one_zero(!b), one_zero(b));
367

368
        b = FLAGS_SET(gpt_flags, SD_GPT_FLAG_GROWFS);
1,929✔
369
        if ((policy_flags & _PARTITION_POLICY_GROWFS_MASK) == (b ? PARTITION_POLICY_GROWFS_OFF : PARTITION_POLICY_GROWFS_ON))
3,849✔
370
                return log_debug_errno(SYNTHETIC_ERRNO(ERFKILL), "Partition %s has 'growfs' flag incorrectly set (must be %s, is %s), refusing.",
×
371
                                       partition_designator_to_string(designator),
372
                                       one_zero(!b), one_zero(b));
373

374
        return 0;
375
}
376

377
static int dissected_image_probe_filesystems(
79✔
378
                DissectedImage *m,
379
                int fd,
380
                const ImagePolicy *policy) {
381

382
        int r;
79✔
383

384
        assert(m);
79✔
385

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

388
        for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
1,106✔
389
                DissectedPartition *p = m->partitions + i;
1,027✔
390
                PartitionPolicyFlags found_flags;
1,027✔
391

392
                if (!p->found)
1,027✔
393
                        continue;
820✔
394

395
                if (!p->fstype) {
207✔
396
                        /* If we have an fd referring to the partition block device, use that. Otherwise go
397
                         * via the whole block device or backing regular file, and read via offset. */
398
                        if (p->mount_node_fd >= 0)
110✔
399
                                r = probe_filesystem_full(p->mount_node_fd, p->node, 0, UINT64_MAX, &p->fstype);
98✔
400
                        else
401
                                r = probe_filesystem_full(fd, p->node, p->offset, p->size, &p->fstype);
12✔
402
                        if (r < 0)
110✔
403
                                return r;
404
                }
405

406
                if (streq_ptr(p->fstype, "crypto_LUKS")) {
207✔
407
                        m->encrypted = true;
1✔
408
                        found_flags = PARTITION_POLICY_ENCRYPTED; /* found this one, and its definitely encrypted */
1✔
409
                } else
410
                        /* found it, but it's definitely not encrypted, hence mask the encrypted flag, but
411
                         * set all other ways that indicate "present". */
412
                        found_flags = PARTITION_POLICY_UNPROTECTED|PARTITION_POLICY_VERITY|PARTITION_POLICY_SIGNED;
413

414
                if (p->fstype && fstype_is_ro(p->fstype))
207✔
415
                        p->rw = false;
67✔
416

417
                if (!p->rw)
207✔
418
                        p->growfs = false;
113✔
419

420
                /* We might have learnt more about the file system now (i.e. whether it is encrypted or not),
421
                 * hence we need to validate this against policy again, to see if the policy still matches
422
                 * with this new information. Note that image_policy_check_protection() will check for
423
                 * overlap between what's allowed in the policy and what we pass as 'found_policy' here. In
424
                 * the unencrypted case we thus might pass an overly unspecific mask here (i.e. unprotected
425
                 * OR verity OR signed), but that's fine since the earlier policy check already checked more
426
                 * specific which of those three cases where OK. Keep in mind that this function here only
427
                 * looks at specific partitions (and thus can only deduce encryption or not) but not the
428
                 * overall partition table (and thus cannot deduce verity or not). The earlier dissection
429
                 * checks already did the relevant checks that look at the whole partition table, and
430
                 * enforced policy there as needed. */
431
                r = image_policy_check_protection(policy, i, found_flags);
207✔
432
                if (r < 0)
207✔
433
                        return r;
434
        }
435

436
        return 0;
437
}
438

439
static void check_partition_flags(
233✔
440
                const char *node,
441
                unsigned long long pflags,
442
                unsigned long long supported) {
443

444
        assert(node);
233✔
445

446
        /* Mask away all flags supported by this partition's type and the three flags the UEFI spec defines generically */
447
        pflags &= ~(supported |
233✔
448
                    SD_GPT_FLAG_REQUIRED_PARTITION |
449
                    SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL |
233✔
450
                    SD_GPT_FLAG_LEGACY_BIOS_BOOTABLE);
451

452
        if (pflags == 0)
233✔
453
                return;
454

455
        /* If there are other bits set, then log about it, to make things discoverable */
456
        for (unsigned i = 0; i < sizeof(pflags) * 8; i++) {
×
457
                unsigned long long bit = 1ULL << i;
×
458
                if (!FLAGS_SET(pflags, bit))
×
459
                        continue;
×
460

461
                log_debug("Unexpected partition flag %llu set on %s!", bit, node);
×
462
        }
463
}
464

465
static int dissected_image_new(const char *path, DissectedImage **ret) {
1,870✔
466
        _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
×
467
        _cleanup_free_ char *name = NULL;
1,870✔
468
        int r;
1,870✔
469

470
        assert(ret);
1,870✔
471

472
        if (path) {
1,870✔
473
                _cleanup_free_ char *filename = NULL;
1,870✔
474

475
                r = path_extract_filename(path, &filename);
1,870✔
476
                if (r < 0)
1,870✔
477
                        return r;
478

479
                r = raw_strip_suffixes(filename, &name);
1,870✔
480
                if (r < 0)
1,870✔
481
                        return r;
482

483
                if (!image_name_is_valid(name)) {
1,870✔
484
                        log_debug("Image name %s is not valid, ignoring.", strna(name));
1✔
485
                        name = mfree(name);
1✔
486
                }
487
        }
488

489
        m = new(DissectedImage, 1);
1,870✔
490
        if (!m)
1,870✔
491
                return -ENOMEM;
492

493
        *m = (DissectedImage) {
1,870✔
494
                .has_init_system = -1,
495
                .image_name = TAKE_PTR(name),
1,870✔
496
        };
497

498
        for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++)
26,180✔
499
                m->partitions[i] = DISSECTED_PARTITION_NULL;
24,310✔
500

501
        *ret = TAKE_PTR(m);
1,870✔
502
        return 0;
1,870✔
503
}
504
#endif
505

506
static void dissected_partition_done(DissectedPartition *p) {
23,529✔
507
        assert(p);
23,529✔
508

509
        free(p->fstype);
23,529✔
510
        free(p->node);
23,529✔
511
        free(p->label);
23,529✔
512
        free(p->decrypted_fstype);
23,529✔
513
        free(p->decrypted_node);
23,529✔
514
        free(p->mount_options);
23,529✔
515
        safe_close(p->mount_node_fd);
23,529✔
516
        safe_close(p->fsmount_fd);
23,529✔
517

518
        *p = DISSECTED_PARTITION_NULL;
23,529✔
519
}
23,529✔
520

521
#if HAVE_BLKID
522
static int diskseq_should_be_used(
2,209✔
523
                const char *whole_devname,
524
                uint64_t diskseq,
525
                DissectImageFlags flags) {
526

527
        int r;
2,209✔
528

529
        assert(whole_devname);
2,209✔
530

531
        /* No diskseq. We cannot use by-diskseq symlink. */
532
        if (diskseq == 0)
2,209✔
533
                return false;
2,209✔
534

535
        /* Do not use by-diskseq link unless DISSECT_IMAGE_DISKSEQ_DEVNODE flag is explicitly set. */
536
        if (!FLAGS_SET(flags, DISSECT_IMAGE_DISKSEQ_DEVNODE))
2,135✔
537
                return false;
538

539
        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
2,209✔
540
        r = sd_device_new_from_devname(&dev, whole_devname);
×
541
        if (r < 0)
×
542
                return r;
543

544
        /* When ID_IGNORE_DISKSEQ udev property is set, the by-diskseq symlink will not be created. */
545
        r = device_get_property_bool(dev, "ID_IGNORE_DISKSEQ");
×
546
        if (r >= 0)
×
547
                return !r; /* If explicitly specified, use it. */
×
548
        if (r != -ENOENT)
×
549
                return r;
×
550

551
        return true;
552
}
553

554
static int make_partition_devname(
2,209✔
555
                const char *whole_devname,
556
                uint64_t diskseq,
557
                int nr,
558
                DissectImageFlags flags,
559
                char **ret) {
560

561
        _cleanup_free_ char *s = NULL;
2,209✔
562
        int r;
2,209✔
563

564
        assert(whole_devname);
2,209✔
565
        assert(nr != 0); /* zero is not a valid partition nr */
2,209✔
566
        assert(ret);
2,209✔
567

568
        r = diskseq_should_be_used(whole_devname, diskseq, flags);
2,209✔
569
        if (r < 0)
2,209✔
570
                log_debug_errno(r, "Failed to determine if diskseq should be used for %s, assuming no, ignoring: %m", whole_devname);
×
571
        if (r <= 0) {
2,209✔
572
                /* Given a whole block device node name (e.g. /dev/sda or /dev/loop7) generate a partition
573
                 * device name (e.g. /dev/sda7 or /dev/loop7p5). The rule the kernel uses is simple: if whole
574
                 * block device node name ends in a digit, then suffix a 'p', followed by the partition
575
                 * number. Otherwise, just suffix the partition number without any 'p'. */
576

577
                if (nr < 0) { /* whole disk? */
2,209✔
578
                        s = strdup(whole_devname);
1,722✔
579
                        if (!s)
1,722✔
580
                                return -ENOMEM;
581
                } else {
582
                        size_t l = strlen(whole_devname);
487✔
583
                        if (l < 1) /* underflow check for the subtraction below */
487✔
584
                                return -EINVAL;
585

586
                        bool need_p = ascii_isdigit(whole_devname[l-1]); /* Last char a digit? */
487✔
587

588
                        if (asprintf(&s, "%s%s%i", whole_devname, need_p ? "p" : "", nr) < 0)
551✔
589
                                return -ENOMEM;
590
                }
591
        } else {
592
                if (nr < 0) /* whole disk? */
×
593
                        r = asprintf(&s, "/dev/disk/by-diskseq/%" PRIu64, diskseq);
×
594
                else
595
                        r = asprintf(&s, "/dev/disk/by-diskseq/%" PRIu64 "-part%i", diskseq, nr);
×
596
                if (r < 0)
×
597
                        return -ENOMEM;
598
        }
599

600
        *ret = TAKE_PTR(s);
2,209✔
601
        return 0;
2,209✔
602
}
603

604
static int open_partition(
1,919✔
605
                const char *node,
606
                bool is_partition,
607
                const LoopDevice *loop) {
608

609
        _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
1,919✔
610
        _cleanup_close_ int fd = -EBADF;
1,919✔
611
        dev_t devnum;
1,919✔
612
        int r;
1,919✔
613

614
        assert(node);
1,919✔
615
        assert(loop);
1,919✔
616

617
        fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
1,919✔
618
        if (fd < 0)
1,919✔
619
                return -errno;
×
620

621
        /* Check if the block device is a child of (or equivalent to) the originally provided one. */
622
        r = block_device_new_from_fd(fd, is_partition ? BLOCK_DEVICE_LOOKUP_WHOLE_DISK : 0, &dev);
1,919✔
623
        if (r < 0)
1,919✔
624
                return r;
625

626
        r = sd_device_get_devnum(dev, &devnum);
1,919✔
627
        if (r < 0)
1,919✔
628
                return r;
629

630
        if (loop->devno != devnum)
1,919✔
631
                return -ENXIO;
632

633
        /* Also check diskseq. */
634
        if (loop->diskseq != 0) {
1,919✔
635
                uint64_t diskseq;
1,919✔
636

637
                r = fd_get_diskseq(fd, &diskseq);
1,919✔
638
                if (r < 0)
1,919✔
639
                        return r;
×
640

641
                if (loop->diskseq != diskseq)
1,919✔
642
                        return -ENXIO;
643
        }
644

645
        log_debug("Opened %s (fd=%i, whole_block_devnum=" DEVNUM_FORMAT_STR ", diskseq=%" PRIu64 ").",
1,919✔
646
                  node, fd, DEVNUM_FORMAT_VAL(loop->devno), loop->diskseq);
647
        return TAKE_FD(fd);
648
}
649

650
static int compare_arch(Architecture a, Architecture b) {
12✔
651
        if (a == b)
12✔
652
                return 0;
653

654
        if (a == native_architecture())
×
655
                return 1;
656

657
        if (b == native_architecture())
×
658
                return -1;
659

660
#ifdef ARCHITECTURE_SECONDARY
661
        if (a == ARCHITECTURE_SECONDARY)
×
662
                return 1;
663

664
        if (b == ARCHITECTURE_SECONDARY)
×
665
                return -1;
×
666
#endif
667

668
        return 0;
669
}
670

671
static int dissect_image(
1,860✔
672
                DissectedImage *m,
673
                int fd,
674
                const char *devname,
675
                const VeritySettings *verity,
676
                const MountOptions *mount_options,
677
                const ImagePolicy *policy,
678
                DissectImageFlags flags) {
679

680
        sd_id128_t root_uuid = SD_ID128_NULL, root_verity_uuid = SD_ID128_NULL;
1,860✔
681
        sd_id128_t usr_uuid = SD_ID128_NULL, usr_verity_uuid = SD_ID128_NULL;
1,860✔
682
        bool is_gpt, is_mbr, multiple_generic = false,
1,860✔
683
                generic_rw = false,  /* initialize to appease gcc */
1,860✔
684
                generic_growfs = false;
1,860✔
685
        _cleanup_(blkid_free_probep) blkid_probe b = NULL;
1,860✔
686
        _cleanup_free_ char *generic_node = NULL;
1,860✔
687
        sd_id128_t generic_uuid = SD_ID128_NULL;
1,860✔
688
        const char *pttype = NULL, *sptuuid = NULL;
1,860✔
689
        blkid_partlist pl;
1,860✔
690
        int r, generic_nr = -1, n_partitions;
1,860✔
691

692
        assert(m);
1,860✔
693
        assert(fd >= 0);
1,860✔
694
        assert(devname);
1,860✔
695
        assert(!verity || verity->designator < 0 || IN_SET(verity->designator, PARTITION_ROOT, PARTITION_USR));
1,860✔
696
        assert(!verity || verity->root_hash || verity->root_hash_size == 0);
1,860✔
697
        assert(!verity || verity->root_hash_sig || verity->root_hash_sig_size == 0);
1,860✔
698
        assert(!verity || (verity->root_hash || !verity->root_hash_sig));
1,860✔
699
        assert(!((flags & DISSECT_IMAGE_GPT_ONLY) && (flags & DISSECT_IMAGE_NO_PARTITION_TABLE)));
1,860✔
700
        assert(m->sector_size > 0);
1,860✔
701

702
        /* Probes a disk image, and returns information about what it found in *ret.
703
         *
704
         * Returns -ENOPKG if no suitable partition table or file system could be found.
705
         * Returns -EADDRNOTAVAIL if a root hash was specified but no matching root/verity partitions found.
706
         * Returns -ENXIO if we couldn't find any partition suitable as root or /usr partition
707
         * Returns -ENOTUNIQ if we only found multiple generic partitions and thus don't know what to do with that
708
         * Returns -ERFKILL if image doesn't match image policy
709
         * 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)
710
         * Returns -EPROTONOSUPPORT if DISSECT_IMAGE_ADD_PARTITION_DEVICES is set but the block device does not have partition logic enabled
711
         * Returns -ENOMSG if we didn't find a single usable partition (and DISSECT_IMAGE_REFUSE_EMPTY is set)
712
         * Returns -EUCLEAN if some file system had an ambiguous file system superblock signature
713
         */
714

715
        uint64_t diskseq = m->loop ? m->loop->diskseq : 0;
1,860✔
716

717
        if (verity && verity->root_hash) {
1,860✔
718
                sd_id128_t fsuuid, vuuid;
63✔
719

720
                /* If a root hash is supplied, then we use the root partition that has a UUID that match the
721
                 * first 128-bit of the root hash. And we use the verity partition that has a UUID that match
722
                 * the final 128-bit. */
723

724
                if (verity->root_hash_size < sizeof(sd_id128_t))
63✔
725
                        return -EINVAL;
×
726

727
                memcpy(&fsuuid, verity->root_hash, sizeof(sd_id128_t));
63✔
728
                memcpy(&vuuid, (const uint8_t*) verity->root_hash + verity->root_hash_size - sizeof(sd_id128_t), sizeof(sd_id128_t));
63✔
729

730
                if (sd_id128_is_null(fsuuid))
63✔
731
                        return -EINVAL;
×
732
                if (sd_id128_is_null(vuuid))
63✔
733
                        return -EINVAL;
×
734

735
                /* If the verity data declares it's for the /usr partition, then search for that, in all
736
                 * other cases assume it's for the root partition. */
737
                if (verity->designator == PARTITION_USR) {
63✔
738
                        usr_uuid = fsuuid;
×
739
                        usr_verity_uuid = vuuid;
×
740
                } else {
741
                        root_uuid = fsuuid;
63✔
742
                        root_verity_uuid = vuuid;
63✔
743
                }
744
        }
745

746
        b = blkid_new_probe();
1,860✔
747
        if (!b)
1,860✔
748
                return -ENOMEM;
749

750
        errno = 0;
1,860✔
751
        r = blkid_probe_set_device(b, fd, 0, 0);
1,860✔
752
        if (r != 0)
1,860✔
753
                return errno_or_else(ENOMEM);
×
754

755
        errno = 0;
1,860✔
756
        r = blkid_probe_set_sectorsize(b, m->sector_size);
1,860✔
757
        if (r != 0)
1,860✔
758
                return errno_or_else(EIO);
×
759

760
        if ((flags & DISSECT_IMAGE_GPT_ONLY) == 0) {
1,860✔
761
                /* Look for file system superblocks, unless we only shall look for GPT partition tables */
762
                blkid_probe_enable_superblocks(b, 1);
1,858✔
763
                blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_USAGE|BLKID_SUBLKS_UUID);
1,858✔
764
        }
765

766
        blkid_probe_enable_partitions(b, 1);
1,860✔
767
        blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
1,860✔
768

769
        errno = 0;
1,860✔
770
        r = blkid_do_safeprobe(b);
1,860✔
771
        if (r == _BLKID_SAFEPROBE_ERROR)
1,860✔
772
                return errno_or_else(EIO);
×
773
        if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND))
1,860✔
774
                return log_debug_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to identify any partition table.");
×
775

776
        assert(r == _BLKID_SAFEPROBE_FOUND);
1,860✔
777

778
        if ((!(flags & DISSECT_IMAGE_GPT_ONLY) &&
1,860✔
779
            (flags & DISSECT_IMAGE_GENERIC_ROOT)) ||
115✔
780
            (flags & DISSECT_IMAGE_NO_PARTITION_TABLE)) {
115✔
781
                const char *usage = NULL;
1,805✔
782

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

785
                (void) blkid_probe_lookup_value(b, "USAGE", &usage, NULL);
1,805✔
786
                if (STRPTR_IN_SET(usage, "filesystem", "crypto")) {
1,805✔
787
                        _cleanup_free_ char *t = NULL, *n = NULL, *o = NULL;
1,722✔
788
                        const char *fstype = NULL, *options = NULL, *suuid = NULL;
1,722✔
789
                        _cleanup_close_ int mount_node_fd = -EBADF;
1,722✔
790
                        sd_id128_t uuid = SD_ID128_NULL;
1,722✔
791
                        PartitionPolicyFlags found_flags;
1,722✔
792
                        bool encrypted;
1,722✔
793

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

796
                        r = image_policy_may_use(policy, PARTITION_ROOT);
1,722✔
797
                        if (r < 0)
1,722✔
798
                                return r;
799
                        if (r == 0) /* policy says ignore this, so we ignore it */
1,722✔
800
                                return -ENOPKG;
801

802
                        (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
1,722✔
803
                        (void) blkid_probe_lookup_value(b, "UUID", &suuid, NULL);
1,722✔
804

805
                        encrypted = streq_ptr(fstype, "crypto_LUKS");
1,722✔
806

807
                        if (verity_settings_data_covers(verity, PARTITION_ROOT))
1,722✔
808
                                found_flags = verity->root_hash_sig ? PARTITION_POLICY_SIGNED : PARTITION_POLICY_VERITY;
40✔
809
                        else
810
                                found_flags = encrypted ? PARTITION_POLICY_ENCRYPTED : PARTITION_POLICY_UNPROTECTED;
1,682✔
811

812
                        r = image_policy_check_protection(policy, PARTITION_ROOT, found_flags);
1,722✔
813
                        if (r < 0)
1,722✔
814
                                return r;
815

816
                        r = image_policy_check_partition_flags(policy, PARTITION_ROOT, 0); /* we have no gpt partition flags, hence check against all bits off */
1,722✔
817
                        if (r < 0)
1,722✔
818
                                return r;
819

820
                        if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES)) {
1,722✔
821
                                mount_node_fd = open_partition(devname, /* is_partition = */ false, m->loop);
1,722✔
822
                                if (mount_node_fd < 0)
1,722✔
823
                                        return mount_node_fd;
824
                        }
825

826
                        if (fstype) {
1,722✔
827
                                t = strdup(fstype);
1,722✔
828
                                if (!t)
1,722✔
829
                                        return -ENOMEM;
830
                        }
831

832
                        if (suuid) {
1,722✔
833
                                /* blkid will return FAT's serial number as UUID, hence it is quite possible
834
                                 * that parsing this will fail. We'll ignore the ID, since it's just too
835
                                 * short to be useful as true identifier. */
836
                                r = sd_id128_from_string(suuid, &uuid);
50✔
837
                                if (r < 0)
50✔
838
                                        log_debug_errno(r, "Failed to parse file system UUID '%s', ignoring: %m", suuid);
×
839
                        }
840

841
                        r = make_partition_devname(devname, diskseq, -1, flags, &n);
1,722✔
842
                        if (r < 0)
1,722✔
843
                                return r;
844

845
                        m->single_file_system = true;
1,722✔
846
                        m->encrypted = encrypted;
1,722✔
847

848
                        m->has_verity = verity && verity->data_path;
1,722✔
849
                        m->verity_ready = verity_settings_data_covers(verity, PARTITION_ROOT);
1,722✔
850

851
                        m->has_verity_sig = false; /* signature not embedded, must be specified */
1,722✔
852
                        m->verity_sig_ready = m->verity_ready && verity->root_hash_sig;
1,722✔
853

854
                        m->image_uuid = uuid;
1,722✔
855

856
                        options = mount_options_from_designator(mount_options, PARTITION_ROOT);
1,722✔
857
                        if (options) {
1,722✔
858
                                o = strdup(options);
3✔
859
                                if (!o)
3✔
860
                                        return -ENOMEM;
861
                        }
862

863
                        m->partitions[PARTITION_ROOT] = (DissectedPartition) {
3,444✔
864
                                .found = true,
865
                                .rw = !m->verity_ready && !fstype_is_ro(fstype),
1,722✔
866
                                .partno = -1,
867
                                .architecture = _ARCHITECTURE_INVALID,
868
                                .fstype = TAKE_PTR(t),
1,722✔
869
                                .node = TAKE_PTR(n),
1,722✔
870
                                .mount_options = TAKE_PTR(o),
1,722✔
871
                                .mount_node_fd = TAKE_FD(mount_node_fd),
1,722✔
872
                                .offset = 0,
873
                                .size = UINT64_MAX,
874
                                .fsmount_fd = -EBADF,
875
                        };
876

877
                        return 0;
1,722✔
878
                }
879
        }
880

881
        (void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
138✔
882
        if (!pttype)
138✔
883
                return -ENOPKG;
884

885
        is_gpt = streq_ptr(pttype, "gpt");
90✔
886
        is_mbr = streq_ptr(pttype, "dos");
90✔
887

888
        if (!is_gpt && ((flags & DISSECT_IMAGE_GPT_ONLY) || !is_mbr))
90✔
889
                return -ENOPKG;
890

891
        /* We support external verity data partitions only if the image has no partition table */
892
        if (verity && verity->data_path)
90✔
893
                return -EBADR;
894

895
        if (FLAGS_SET(flags, DISSECT_IMAGE_ADD_PARTITION_DEVICES)) {
90✔
896
                /* Safety check: refuse block devices that carry a partition table but for which the kernel doesn't
897
                 * do partition scanning. */
898
                r = blockdev_partscan_enabled_fd(fd);
74✔
899
                if (r < 0)
74✔
900
                        return r;
901
                if (r == 0)
74✔
902
                        return -EPROTONOSUPPORT;
903
        }
904

905
        (void) blkid_probe_lookup_value(b, "PTUUID", &sptuuid, NULL);
90✔
906
        if (sptuuid) {
90✔
907
                r = sd_id128_from_string(sptuuid, &m->image_uuid);
90✔
908
                if (r < 0)
90✔
909
                        log_debug_errno(r, "Failed to parse partition table UUID '%s', ignoring: %m", sptuuid);
×
910
        }
911

912
        errno = 0;
90✔
913
        pl = blkid_probe_get_partitions(b);
90✔
914
        if (!pl)
90✔
915
                return errno_or_else(ENOMEM);
×
916

917
        errno = 0;
90✔
918
        n_partitions = blkid_partlist_numof_partitions(pl);
90✔
919
        if (n_partitions < 0)
90✔
920
                return errno_or_else(EIO);
×
921

922
        for (int i = 0; i < n_partitions; i++) {
348✔
923
                _cleanup_free_ char *node = NULL;
262✔
924
                unsigned long long pflags;
262✔
925
                blkid_loff_t start, size;
262✔
926
                blkid_partition pp;
262✔
927
                int nr;
262✔
928

929
                errno = 0;
262✔
930
                pp = blkid_partlist_get_partition(pl, i);
262✔
931
                if (!pp)
262✔
932
                        return errno_or_else(EIO);
×
933

934
                pflags = blkid_partition_get_flags(pp);
262✔
935

936
                errno = 0;
262✔
937
                nr = blkid_partition_get_partno(pp);
262✔
938
                if (nr < 0)
262✔
939
                        return errno_or_else(EIO);
×
940

941
                errno = 0;
262✔
942
                start = blkid_partition_get_start(pp);
262✔
943
                if (start < 0)
262✔
944
                        return errno_or_else(EIO);
×
945

946
                assert((uint64_t) start < UINT64_MAX/512);
262✔
947

948
                errno = 0;
262✔
949
                size = blkid_partition_get_size(pp);
262✔
950
                if (size < 0)
262✔
951
                        return errno_or_else(EIO);
×
952

953
                assert((uint64_t) size < UINT64_MAX/512);
262✔
954

955
                /* While probing we need the non-diskseq device node name to access the thing, hence mask off
956
                 * DISSECT_IMAGE_DISKSEQ_DEVNODE. */
957
                r = make_partition_devname(devname, diskseq, nr, flags & ~DISSECT_IMAGE_DISKSEQ_DEVNODE, &node);
262✔
958
                if (r < 0)
262✔
959
                        return r;
960

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

973
                if (FLAGS_SET(flags, DISSECT_IMAGE_ADD_PARTITION_DEVICES)) {
262✔
974
                        r = block_device_add_partition(fd, node, nr, (uint64_t) start * 512, (uint64_t) size * 512);
216✔
975
                        if (r < 0) {
216✔
976
                                if (r != -EBUSY)
216✔
977
                                        return log_debug_errno(r, "BLKPG_ADD_PARTITION failed: %m");
×
978

979
                                log_debug_errno(r, "Kernel was quicker than us in adding partition %i.", nr);
216✔
980
                        } else
981
                                log_debug("We were quicker than kernel in adding partition %i.", nr);
×
982
                }
983

984
                if (is_gpt) {
262✔
985
                        const char *fstype = NULL, *label;
262✔
986
                        sd_id128_t type_id, id;
262✔
987
                        GptPartitionType type;
262✔
988
                        bool rw = true, growfs = false;
262✔
989

990
                        r = blkid_partition_get_uuid_id128(pp, &id);
262✔
991
                        if (r < 0) {
262✔
992
                                log_debug_errno(r, "Failed to read partition UUID, ignoring: %m");
×
993
                                continue;
33✔
994
                        }
995

996
                        r = blkid_partition_get_type_id128(pp, &type_id);
262✔
997
                        if (r < 0) {
262✔
998
                                log_debug_errno(r, "Failed to read partition type UUID, ignoring: %m");
×
999
                                continue;
×
1000
                        }
1001

1002
                        type = gpt_partition_type_from_uuid(type_id);
262✔
1003

1004
                        label = blkid_partition_get_name(pp); /* libblkid returns NULL here if empty */
262✔
1005

1006
                        log_debug("Dissecting %s partition with label %s and UUID %s",
267✔
1007
                                  strna(partition_designator_to_string(type.designator)), strna(label), SD_ID128_TO_UUID_STRING(id));
1008

1009
                        if (IN_SET(type.designator,
262✔
1010
                                   PARTITION_HOME,
1011
                                   PARTITION_SRV,
1012
                                   PARTITION_XBOOTLDR,
1013
                                   PARTITION_TMP)) {
1014

1015
                                check_partition_flags(node, pflags,
28✔
1016
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
1017

1018
                                if (pflags & SD_GPT_FLAG_NO_AUTO)
28✔
1019
                                        continue;
×
1020

1021
                                rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
28✔
1022
                                growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
28✔
1023

1024
                        } else if (type.designator == PARTITION_ESP) {
234✔
1025

1026
                                /* Note that we don't check the SD_GPT_FLAG_NO_AUTO flag for the ESP, as it is
1027
                                 * not defined there. We instead check the SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL, as
1028
                                 * recommended by the UEFI spec (See "12.3.3 Number and Location of System
1029
                                 * Partitions"). */
1030

1031
                                if (pflags & SD_GPT_FLAG_NO_BLOCK_IO_PROTOCOL)
29✔
1032
                                        continue;
×
1033

1034
                                fstype = "vfat";
1035

1036
                        } else if (type.designator == PARTITION_ROOT) {
1037

1038
                                check_partition_flags(node, pflags,
76✔
1039
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
1040

1041
                                if (pflags & SD_GPT_FLAG_NO_AUTO)
76✔
1042
                                        continue;
×
1043

1044
                                /* If a root ID is specified, ignore everything but the root id */
1045
                                if (!sd_id128_is_null(root_uuid) && !sd_id128_equal(root_uuid, id))
152✔
1046
                                        continue;
×
1047

1048
                                rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
76✔
1049
                                growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
76✔
1050

1051
                        } else if (type.designator == PARTITION_ROOT_VERITY) {
1052

1053
                                check_partition_flags(node, pflags,
50✔
1054
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
1055

1056
                                if (pflags & SD_GPT_FLAG_NO_AUTO)
50✔
1057
                                        continue;
×
1058

1059
                                m->has_verity = true;
50✔
1060

1061
                                /* If no verity configuration is specified, then don't do verity */
1062
                                if (!verity)
50✔
1063
                                        continue;
4✔
1064
                                if (verity->designator >= 0 && verity->designator != PARTITION_ROOT)
46✔
1065
                                        continue;
×
1066

1067
                                /* If root hash is specified, then ignore everything but the root id */
1068
                                if (!sd_id128_is_null(root_verity_uuid) && !sd_id128_equal(root_verity_uuid, id))
92✔
1069
                                        continue;
×
1070

1071
                                fstype = "DM_verity_hash";
1072
                                rw = false;
1073

1074
                        } else if (type.designator == PARTITION_ROOT_VERITY_SIG) {
1075

1076
                                check_partition_flags(node, pflags,
50✔
1077
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
1078

1079
                                if (pflags & SD_GPT_FLAG_NO_AUTO)
50✔
1080
                                        continue;
×
1081

1082
                                m->has_verity_sig = true;
50✔
1083

1084
                                if (!verity)
50✔
1085
                                        continue;
4✔
1086
                                if (verity->designator >= 0 && verity->designator != PARTITION_ROOT)
46✔
1087
                                        continue;
×
1088

1089
                                fstype = "verity_hash_signature";
1090
                                rw = false;
1091

1092
                        } else if (type.designator == PARTITION_USR) {
1093

1094
                                check_partition_flags(node, pflags,
2✔
1095
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
1096

1097
                                if (pflags & SD_GPT_FLAG_NO_AUTO)
2✔
1098
                                        continue;
×
1099

1100
                                /* If a usr ID is specified, ignore everything but the usr id */
1101
                                if (!sd_id128_is_null(usr_uuid) && !sd_id128_equal(usr_uuid, id))
4✔
1102
                                        continue;
×
1103

1104
                                rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
2✔
1105
                                growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
2✔
1106

1107
                        } else if (type.designator == PARTITION_USR_VERITY) {
1108

1109
                                check_partition_flags(node, pflags,
×
1110
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
1111

1112
                                if (pflags & SD_GPT_FLAG_NO_AUTO)
×
1113
                                        continue;
×
1114

1115
                                m->has_verity = true;
×
1116

1117
                                if (!verity)
×
1118
                                        continue;
×
1119
                                if (verity->designator >= 0 && verity->designator != PARTITION_USR)
×
1120
                                        continue;
×
1121

1122
                                /* If usr hash is specified, then ignore everything but the usr id */
1123
                                if (!sd_id128_is_null(usr_verity_uuid) && !sd_id128_equal(usr_verity_uuid, id))
×
1124
                                        continue;
×
1125

1126
                                fstype = "DM_verity_hash";
1127
                                rw = false;
1128

1129
                        } else if (type.designator == PARTITION_USR_VERITY_SIG) {
1130

1131
                                check_partition_flags(node, pflags,
×
1132
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY);
1133

1134
                                if (pflags & SD_GPT_FLAG_NO_AUTO)
×
1135
                                        continue;
×
1136

1137
                                m->has_verity_sig = true;
×
1138

1139
                                if (!verity)
×
1140
                                        continue;
×
1141
                                if (verity->designator >= 0 && verity->designator != PARTITION_USR)
×
1142
                                        continue;
×
1143

1144
                                fstype = "verity_hash_signature";
1145
                                rw = false;
1146

1147
                        } else if (type.designator == PARTITION_SWAP) {
1148

1149
                                check_partition_flags(node, pflags, SD_GPT_FLAG_NO_AUTO);
1✔
1150

1151
                                if (pflags & SD_GPT_FLAG_NO_AUTO)
1✔
1152
                                        continue;
×
1153

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

1159
                        /* We don't have a designator for SD_GPT_LINUX_GENERIC so check the UUID instead. */
1160
                        } else if (sd_id128_equal(type.uuid, SD_GPT_LINUX_GENERIC)) {
26✔
1161

1162
                                check_partition_flags(node, pflags,
26✔
1163
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
1164

1165
                                if (pflags & SD_GPT_FLAG_NO_AUTO)
26✔
1166
                                        continue;
×
1167

1168
                                if (generic_node)
26✔
1169
                                        multiple_generic = true;
1170
                                else {
1171
                                        generic_nr = nr;
26✔
1172
                                        generic_rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
26✔
1173
                                        generic_growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
26✔
1174
                                        generic_uuid = id;
26✔
1175
                                        generic_node = TAKE_PTR(node);
26✔
1176
                                }
1177

1178
                        } else if (type.designator == PARTITION_VAR) {
×
1179

1180
                                check_partition_flags(node, pflags,
×
1181
                                                      SD_GPT_FLAG_NO_AUTO | SD_GPT_FLAG_READ_ONLY | SD_GPT_FLAG_GROWFS);
1182

1183
                                if (pflags & SD_GPT_FLAG_NO_AUTO)
×
1184
                                        continue;
×
1185

1186
                                if (!FLAGS_SET(flags, DISSECT_IMAGE_RELAX_VAR_CHECK)) {
×
1187
                                        sd_id128_t var_uuid;
×
1188

1189
                                        /* For /var we insist that the uuid of the partition matches the
1190
                                         * HMAC-SHA256 of the /var GPT partition type uuid, keyed by machine
1191
                                         * ID. Why? Unlike the other partitions /var is inherently
1192
                                         * installation specific, hence we need to be careful not to mount it
1193
                                         * in the wrong installation. By hashing the partition UUID from
1194
                                         * /etc/machine-id we can securely bind the partition to the
1195
                                         * installation. */
1196

1197
                                        r = sd_id128_get_machine_app_specific(SD_GPT_VAR, &var_uuid);
×
1198
                                        if (r < 0)
×
1199
                                                return r;
×
1200

1201
                                        if (!sd_id128_equal(var_uuid, id)) {
×
1202
                                                log_debug("Found a /var/ partition, but its UUID didn't match our expectations "
×
1203
                                                          "(found: " SD_ID128_UUID_FORMAT_STR ", expected: " SD_ID128_UUID_FORMAT_STR "), ignoring.",
1204
                                                          SD_ID128_FORMAT_VAL(id), SD_ID128_FORMAT_VAL(var_uuid));
1205
                                                continue;
×
1206
                                        }
1207
                                }
1208

1209
                                rw = !(pflags & SD_GPT_FLAG_READ_ONLY);
×
1210
                                growfs = FLAGS_SET(pflags, SD_GPT_FLAG_GROWFS);
×
1211
                        }
1212

1213
                        if (type.designator != _PARTITION_DESIGNATOR_INVALID) {
254✔
1214
                                _cleanup_free_ char *t = NULL, *o = NULL, *l = NULL, *n = NULL;
228✔
1215
                                _cleanup_close_ int mount_node_fd = -EBADF;
228✔
1216
                                const char *options = NULL;
228✔
1217

1218
                                r = image_policy_may_use(policy, type.designator);
228✔
1219
                                if (r < 0)
228✔
1220
                                        return r;
1221
                                if (r == 0) {
224✔
1222
                                        /* Policy says: ignore; Remember this fact, so that we later can distinguish between "found but ignored" and "not found at all" */
1223

1224
                                        if (!m->partitions[type.designator].found)
25✔
1225
                                                m->partitions[type.designator].ignored = true;
25✔
1226

1227
                                        continue;
25✔
1228
                                }
1229

1230
                                if (m->partitions[type.designator].found) {
199✔
1231
                                        int c;
12✔
1232

1233
                                        /* For most partition types the first one we see wins. Except for the
1234
                                         * rootfs and /usr, where we do a version compare of the label, and
1235
                                         * let the newest version win. This permits a simple A/B versioning
1236
                                         * scheme in OS images. */
1237

1238
                                        c = compare_arch(type.arch, m->partitions[type.designator].architecture);
12✔
1239
                                        if (c < 0) /* the arch we already found is better than the one we found now */
12✔
1240
                                                continue;
×
1241
                                        if (c == 0 && /* same arch? then go by version in label */
24✔
1242
                                            (!partition_designator_is_versioned(type.designator) ||
24✔
1243
                                             strverscmp_improved(label, m->partitions[type.designator].label) <= 0))
12✔
1244
                                                continue;
×
1245

1246
                                        dissected_partition_done(m->partitions + type.designator);
12✔
1247
                                }
1248

1249
                                if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES) &&
199✔
1250
                                    type.designator != PARTITION_SWAP) {
1251
                                        mount_node_fd = open_partition(node, /* is_partition = */ true, m->loop);
171✔
1252
                                        if (mount_node_fd < 0)
171✔
1253
                                                return mount_node_fd;
1254
                                }
1255

1256
                                r = make_partition_devname(devname, diskseq, nr, flags, &n);
199✔
1257
                                if (r < 0)
199✔
1258
                                        return r;
1259

1260
                                if (fstype) {
199✔
1261
                                        t = strdup(fstype);
99✔
1262
                                        if (!t)
99✔
1263
                                                return -ENOMEM;
1264
                                }
1265

1266
                                if (label) {
199✔
1267
                                        l = strdup(label);
194✔
1268
                                        if (!l)
194✔
1269
                                                return -ENOMEM;
1270
                                }
1271

1272
                                options = mount_options_from_designator(mount_options, type.designator);
199✔
1273
                                if (options) {
199✔
1274
                                        o = strdup(options);
1✔
1275
                                        if (!o)
1✔
1276
                                                return -ENOMEM;
1277
                                }
1278

1279
                                m->partitions[type.designator] = (DissectedPartition) {
199✔
1280
                                        .found = true,
1281
                                        .partno = nr,
1282
                                        .rw = rw,
1283
                                        .growfs = growfs,
1284
                                        .architecture = type.arch,
199✔
1285
                                        .node = TAKE_PTR(n),
199✔
1286
                                        .fstype = TAKE_PTR(t),
199✔
1287
                                        .label = TAKE_PTR(l),
199✔
1288
                                        .uuid = id,
1289
                                        .mount_options = TAKE_PTR(o),
199✔
1290
                                        .mount_node_fd = TAKE_FD(mount_node_fd),
199✔
1291
                                        .offset = (uint64_t) start * 512,
199✔
1292
                                        .size = (uint64_t) size * 512,
199✔
1293
                                        .gpt_flags = pflags,
1294
                                        .fsmount_fd = -EBADF,
1295
                                };
1296
                        }
1297

1298
                } else if (is_mbr) {
×
1299

1300
                        switch (blkid_partition_get_type(pp)) {
×
1301

1302
                        case 0x83: /* Linux partition */
×
1303

1304
                                if (pflags != 0x80) /* Bootable flag */
×
1305
                                        continue;
×
1306

1307
                                if (generic_node)
×
1308
                                        multiple_generic = true;
1309
                                else {
1310
                                        generic_nr = nr;
×
1311
                                        generic_rw = true;
×
1312
                                        generic_growfs = false;
×
1313
                                        generic_node = TAKE_PTR(node);
×
1314
                                }
1315

1316
                                break;
1317

1318
                        case 0xEA: { /* Boot Loader Spec extended $BOOT partition */
×
1319
                                _cleanup_close_ int mount_node_fd = -EBADF;
×
1320
                                _cleanup_free_ char *o = NULL, *n = NULL;
×
1321
                                sd_id128_t id = SD_ID128_NULL;
×
1322
                                const char *options = NULL;
×
1323

1324
                                r = image_policy_may_use(policy, PARTITION_XBOOTLDR);
×
1325
                                if (r < 0)
×
1326
                                        return r;
1327
                                if (r == 0) { /* policy says: ignore */
×
1328
                                        if (!m->partitions[PARTITION_XBOOTLDR].found)
×
1329
                                                m->partitions[PARTITION_XBOOTLDR].ignored = true;
×
1330

1331
                                        continue;
×
1332
                                }
1333

1334
                                /* First one wins */
1335
                                if (m->partitions[PARTITION_XBOOTLDR].found)
×
1336
                                        continue;
×
1337

1338
                                if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES)) {
×
1339
                                        mount_node_fd = open_partition(node, /* is_partition = */ true, m->loop);
×
1340
                                        if (mount_node_fd < 0)
×
1341
                                                return mount_node_fd;
1342
                                }
1343

1344
                                (void) blkid_partition_get_uuid_id128(pp, &id);
×
1345

1346
                                r = make_partition_devname(devname, diskseq, nr, flags, &n);
×
1347
                                if (r < 0)
×
1348
                                        return r;
1349

1350
                                options = mount_options_from_designator(mount_options, PARTITION_XBOOTLDR);
×
1351
                                if (options) {
×
1352
                                        o = strdup(options);
×
1353
                                        if (!o)
×
1354
                                                return -ENOMEM;
1355
                                }
1356

1357
                                m->partitions[PARTITION_XBOOTLDR] = (DissectedPartition) {
×
1358
                                        .found = true,
1359
                                        .partno = nr,
1360
                                        .rw = true,
1361
                                        .growfs = false,
1362
                                        .architecture = _ARCHITECTURE_INVALID,
1363
                                        .node = TAKE_PTR(n),
×
1364
                                        .uuid = id,
1365
                                        .mount_options = TAKE_PTR(o),
×
1366
                                        .mount_node_fd = TAKE_FD(mount_node_fd),
×
1367
                                        .offset = (uint64_t) start * 512,
×
1368
                                        .size = (uint64_t) size * 512,
×
1369
                                        .fsmount_fd = -EBADF,
1370
                                };
1371

1372
                                break;
×
1373
                        }}
1374
                }
1375
        }
1376

1377
        if (!m->partitions[PARTITION_ROOT].found &&
86✔
1378
                (m->partitions[PARTITION_ROOT_VERITY].found ||
29✔
1379
                 m->partitions[PARTITION_ROOT_VERITY_SIG].found))
1380
                        return -EADDRNOTAVAIL; /* Verity found but no matching rootfs? Something is off, refuse. */
1381

1382
        /* Hmm, we found a signature partition but no Verity data? Something is off. */
1383
        if (m->partitions[PARTITION_ROOT_VERITY_SIG].found && !m->partitions[PARTITION_ROOT_VERITY].found)
86✔
1384
                return -EADDRNOTAVAIL;
1385

1386
        if (!m->partitions[PARTITION_USR].found &&
85✔
1387
                (m->partitions[PARTITION_USR_VERITY].found ||
83✔
1388
                 m->partitions[PARTITION_USR_VERITY_SIG].found))
1389
                        return -EADDRNOTAVAIL; /* as above */
1390

1391
        /* as above */
1392
        if (m->partitions[PARTITION_USR_VERITY_SIG].found && !m->partitions[PARTITION_USR_VERITY].found)
85✔
1393
                return -EADDRNOTAVAIL;
1394

1395
        /* If root and /usr are combined then insist that the architecture matches */
1396
        if (m->partitions[PARTITION_ROOT].found &&
85✔
1397
            m->partitions[PARTITION_USR].found &&
2✔
1398
            (m->partitions[PARTITION_ROOT].architecture >= 0 &&
2✔
1399
             m->partitions[PARTITION_USR].architecture >= 0 &&
2✔
1400
             m->partitions[PARTITION_ROOT].architecture != m->partitions[PARTITION_USR].architecture))
1401
                return -EADDRNOTAVAIL;
1402

1403
        if (!m->partitions[PARTITION_ROOT].found &&
85✔
1404
            !m->partitions[PARTITION_USR].found &&
29✔
1405
            (flags & DISSECT_IMAGE_GENERIC_ROOT) &&
29✔
1406
            (!verity || !verity->root_hash || verity->designator != PARTITION_USR)) {
29✔
1407

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

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

1417
                /* If we didn't find a generic node, then we can't fix this up either */
1418
                if (generic_node) {
29✔
1419
                        r = image_policy_may_use(policy, PARTITION_ROOT);
26✔
1420
                        if (r < 0)
26✔
1421
                                return r;
1422
                        if (r == 0)
26✔
1423
                                /* Policy says: ignore; remember that we did */
1424
                                m->partitions[PARTITION_ROOT].ignored = true;
×
1425
                        else {
1426
                                _cleanup_close_ int mount_node_fd = -EBADF;
26✔
1427
                                _cleanup_free_ char *o = NULL, *n = NULL;
×
1428
                                const char *options;
26✔
1429

1430
                                if (FLAGS_SET(flags, DISSECT_IMAGE_PIN_PARTITION_DEVICES)) {
26✔
1431
                                        mount_node_fd = open_partition(generic_node, /* is_partition = */ true, m->loop);
26✔
1432
                                        if (mount_node_fd < 0)
26✔
1433
                                                return mount_node_fd;
1434
                                }
1435

1436
                                r = make_partition_devname(devname, diskseq, generic_nr, flags, &n);
26✔
1437
                                if (r < 0)
26✔
1438
                                        return r;
1439

1440
                                options = mount_options_from_designator(mount_options, PARTITION_ROOT);
26✔
1441
                                if (options) {
26✔
1442
                                        o = strdup(options);
×
1443
                                        if (!o)
×
1444
                                                return -ENOMEM;
1445
                                }
1446

1447
                                assert(generic_nr >= 0);
26✔
1448
                                m->partitions[PARTITION_ROOT] = (DissectedPartition) {
26✔
1449
                                        .found = true,
1450
                                        .rw = generic_rw,
1451
                                        .growfs = generic_growfs,
1452
                                        .partno = generic_nr,
1453
                                        .architecture = _ARCHITECTURE_INVALID,
1454
                                        .node = TAKE_PTR(n),
26✔
1455
                                        .uuid = generic_uuid,
1456
                                        .mount_options = TAKE_PTR(o),
26✔
1457
                                        .mount_node_fd = TAKE_FD(mount_node_fd),
26✔
1458
                                        .offset = UINT64_MAX,
1459
                                        .size = UINT64_MAX,
1460
                                        .fsmount_fd = -EBADF,
1461
                                };
1462
                        }
1463
                }
1464
        }
1465

1466
        /* 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 */
1467
        if (FLAGS_SET(flags, DISSECT_IMAGE_REQUIRE_ROOT) &&
85✔
1468
            !(m->partitions[PARTITION_ROOT].found || (m->partitions[PARTITION_USR].found && FLAGS_SET(flags, DISSECT_IMAGE_USR_NO_ROOT))))
25✔
1469
                return -ENXIO;
1470

1471
        if (m->partitions[PARTITION_ROOT_VERITY].found) {
84✔
1472
                /* We only support one verity partition per image, i.e. can't do for both /usr and root fs */
1473
                if (m->partitions[PARTITION_USR_VERITY].found)
37✔
1474
                        return -ENOTUNIQ;
1475

1476
                /* We don't support verity enabled root with a split out /usr. Neither with nor without
1477
                 * verity there. (Note that we do support verity-less root with verity-full /usr, though.) */
1478
                if (m->partitions[PARTITION_USR].found)
37✔
1479
                        return -EADDRNOTAVAIL;
1480
        }
1481

1482
        if (verity) {
84✔
1483
                /* If a verity designator is specified, then insist that the matching partition exists */
1484
                if (verity->designator >= 0 && !m->partitions[verity->designator].found)
79✔
1485
                        return -EADDRNOTAVAIL;
1486

1487
                bool have_verity_sig_partition;
79✔
1488
                if (verity->designator >= 0)
79✔
1489
                        have_verity_sig_partition = m->partitions[verity->designator == PARTITION_USR ? PARTITION_USR_VERITY_SIG : PARTITION_ROOT_VERITY_SIG].found;
2✔
1490
                else
1491
                        have_verity_sig_partition = m->partitions[PARTITION_USR_VERITY_SIG].found || m->partitions[PARTITION_ROOT_VERITY_SIG].found;
124✔
1492

1493
                if (verity->root_hash) {
79✔
1494
                        /* If we have an explicit root hash and found the partitions for it, then we are ready to use
1495
                         * Verity, set things up for it */
1496

1497
                        if (verity->designator < 0 || verity->designator == PARTITION_ROOT) {
20✔
1498
                                if (!m->partitions[PARTITION_ROOT_VERITY].found || !m->partitions[PARTITION_ROOT].found)
20✔
1499
                                        return -EADDRNOTAVAIL;
1500

1501
                                /* If we found a verity setup, then the root partition is necessarily read-only. */
1502
                                m->partitions[PARTITION_ROOT].rw = false;
19✔
1503
                                m->verity_ready = true;
19✔
1504

1505
                        } else {
1506
                                assert(verity->designator == PARTITION_USR);
×
1507

1508
                                if (!m->partitions[PARTITION_USR_VERITY].found || !m->partitions[PARTITION_USR].found)
×
1509
                                        return -EADDRNOTAVAIL;
1510

1511
                                m->partitions[PARTITION_USR].rw = false;
×
1512
                                m->verity_ready = true;
×
1513
                        }
1514

1515
                        if (m->verity_ready)
19✔
1516
                                m->verity_sig_ready = verity->root_hash_sig || have_verity_sig_partition;
19✔
1517

1518
                } else if (have_verity_sig_partition) {
59✔
1519

1520
                        /* If we found an embedded signature partition, we are ready, too. */
1521

1522
                        m->verity_ready = m->verity_sig_ready = true;
13✔
1523
                        if (verity->designator >= 0)
13✔
1524
                                m->partitions[verity->designator == PARTITION_USR ? PARTITION_USR : PARTITION_ROOT].rw = false;
×
1525
                        else if (m->partitions[PARTITION_USR_VERITY_SIG].found)
13✔
1526
                                m->partitions[PARTITION_USR].rw = false;
×
1527
                        else if (m->partitions[PARTITION_ROOT_VERITY_SIG].found)
13✔
1528
                                m->partitions[PARTITION_ROOT].rw = false;
13✔
1529
                }
1530
        }
1531

1532
        bool any = false;
1533

1534
        /* After we discovered all partitions let's see if the verity requirements match the policy. (Note:
1535
         * we don't check encryption requirements here, because we haven't probed the file system yet, hence
1536
         * don't know if this is encrypted or not) */
1537
        for (PartitionDesignator di = 0; di < _PARTITION_DESIGNATOR_MAX; di++) {
1,129✔
1538
                PartitionDesignator vi, si;
1,049✔
1539
                PartitionPolicyFlags found_flags;
1,049✔
1540

1541
                any = any || m->partitions[di].found;
1,049✔
1542

1543
                vi = partition_verity_of(di);
1,049✔
1544
                si = partition_verity_sig_of(di);
1,049✔
1545

1546
                /* Determine the verity protection level for this partition. */
1547
                found_flags = m->partitions[di].found ?
2,098✔
1548
                        (vi >= 0 && m->partitions[vi].found ?
83✔
1549
                         (si >= 0 && m->partitions[si].found ? PARTITION_POLICY_SIGNED : PARTITION_POLICY_VERITY) :
37✔
1550
                         PARTITION_POLICY_ENCRYPTED|PARTITION_POLICY_UNPROTECTED) :
1,086✔
1551
                        (m->partitions[di].ignored ? PARTITION_POLICY_UNUSED : PARTITION_POLICY_ABSENT);
840✔
1552

1553
                r = image_policy_check_protection(policy, di, found_flags);
1,049✔
1554
                if (r < 0)
1,049✔
1555
                        return r;
1556

1557
                if (m->partitions[di].found) {
1,046✔
1558
                        r = image_policy_check_partition_flags(policy, di, m->partitions[di].gpt_flags);
207✔
1559
                        if (r < 0)
207✔
1560
                                return r;
1561
                }
1562
        }
1563

1564
        if (!any && !FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_EMPTY))
80✔
1565
                return -ENOMSG;
1566

1567
        r = dissected_image_probe_filesystems(m, fd, policy);
79✔
1568
        if (r < 0)
79✔
1569
                return r;
×
1570

1571
        return 0;
1572
}
1573
#endif
1574

1575
int dissect_image_file(
16✔
1576
                const char *path,
1577
                const VeritySettings *verity,
1578
                const MountOptions *mount_options,
1579
                const ImagePolicy *image_policy,
1580
                DissectImageFlags flags,
1581
                DissectedImage **ret) {
1582

1583
#if HAVE_BLKID
1584
        _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
×
1585
        _cleanup_close_ int fd = -EBADF;
16✔
1586
        struct stat st;
16✔
1587
        int r;
16✔
1588

1589
        assert(path);
16✔
1590

1591
        fd = open(path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
16✔
1592
        if (fd < 0)
16✔
1593
                return -errno;
×
1594

1595
        if (fstat(fd, &st) < 0)
16✔
1596
                return -errno;
×
1597

1598
        r = stat_verify_regular(&st);
16✔
1599
        if (r < 0)
16✔
1600
                return r;
1601

1602
        r = dissected_image_new(path, &m);
16✔
1603
        if (r < 0)
16✔
1604
                return r;
1605

1606
        m->image_size = st.st_size;
16✔
1607

1608
        r = probe_sector_size(fd, &m->sector_size);
16✔
1609
        if (r < 0)
16✔
1610
                return r;
1611

1612
        r = dissect_image(m, fd, path, verity, mount_options, image_policy, flags);
16✔
1613
        if (r < 0)
16✔
1614
                return r;
1615

1616
        if (ret)
9✔
1617
                *ret = TAKE_PTR(m);
1✔
1618
        return 0;
1619
#else
1620
        return -EOPNOTSUPP;
1621
#endif
1622
}
1623

1624
int dissect_log_error(int log_level, int r, const char *name, const VeritySettings *verity) {
123✔
1625
        assert(log_level >= 0 && log_level <= LOG_DEBUG);
123✔
1626
        assert(name);
123✔
1627

1628
        switch (r) {
123✔
1629

1630
        case 0 ... INT_MAX: /* success! */
1631
                return r;
1632

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

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

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

1642
        case -EADDRNOTAVAIL:
1643
                return log_full_errno(log_level, r, "%s: No root partition for specified root hash found.", name);
1✔
1644

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

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

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

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

1657
        case -EBADR:
1658
                return log_full_errno(log_level, r,
×
1659
                                      "Combining partitioned images (such as '%s') with external Verity data (such as '%s') not supported. "
1660
                                      "(Consider setting $SYSTEMD_DISSECT_VERITY_SIDECAR=0 to disable automatic discovery of external Verity data.)",
1661
                                      name, strna(verity ? verity->data_path : NULL));
1662

1663
        case -ERFKILL:
1664
                return log_full_errno(log_level, r, "%s: Image does not match image policy.", name);
5✔
1665

1666
        case -ENOMSG:
1667
                return log_full_errno(log_level, r, "%s: No suitable partitions found.", name);
1✔
1668

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

1672
        default:
1673
                return log_full_errno(log_level, r, "%s: Cannot dissect image: %m", name);
×
1674
        }
1675
}
1676

1677
int dissect_image_file_and_warn(
15✔
1678
                const char *path,
1679
                const VeritySettings *verity,
1680
                const MountOptions *mount_options,
1681
                const ImagePolicy *image_policy,
1682
                DissectImageFlags flags,
1683
                DissectedImage **ret) {
1684

1685
        return dissect_log_error(
15✔
1686
                        LOG_ERR,
1687
                        dissect_image_file(path, verity, mount_options, image_policy, flags, ret),
1688
                        path,
1689
                        verity);
1690
}
1691

1692
void dissected_image_close(DissectedImage *m) {
11✔
1693
        if (!m)
11✔
1694
                return;
1695

1696
        /* Closes all fds we keep open associated with this, but nothing else */
1697

1698
        FOREACH_ARRAY(p, m->partitions, _PARTITION_DESIGNATOR_MAX) {
154✔
1699
                p->mount_node_fd = safe_close(p->mount_node_fd);
143✔
1700
                p->fsmount_fd = safe_close(p->fsmount_fd);
143✔
1701
        }
1702

1703
        m->loop = loop_device_unref(m->loop);
11✔
1704
}
1705

1706
DissectedImage* dissected_image_unref(DissectedImage *m) {
1,809✔
1707
        if (!m)
1,809✔
1708
                return NULL;
1709

1710
        /* First, clear dissected partitions. */
1711
        for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++)
25,326✔
1712
                dissected_partition_done(m->partitions + i);
23,517✔
1713

1714
        /* Second, free decrypted images. This must be after dissected_partition_done(), as freeing
1715
         * DecryptedImage may try to deactivate partitions. */
1716
        decrypted_image_unref(m->decrypted_image);
1,809✔
1717

1718
        /* Third, unref LoopDevice. This must be called after the above two, as freeing LoopDevice may try to
1719
         * remove existing partitions on the loopback block device. */
1720
        loop_device_unref(m->loop);
1,809✔
1721

1722
        free(m->image_name);
1,809✔
1723
        free(m->hostname);
1,809✔
1724
        strv_free(m->machine_info);
1,809✔
1725
        strv_free(m->os_release);
1,809✔
1726
        strv_free(m->initrd_release);
1,809✔
1727
        strv_free(m->confext_release);
1,809✔
1728
        strv_free(m->sysext_release);
1,809✔
1729

1730
        return mfree(m);
1,809✔
1731
}
1732

1733
static int is_loop_device(const char *path) {
87✔
1734
        char s[SYS_BLOCK_PATH_MAX("/../loop/")];
87✔
1735
        struct stat st;
87✔
1736

1737
        assert(path);
87✔
1738

1739
        if (stat(path, &st) < 0)
87✔
1740
                return -errno;
×
1741

1742
        if (!S_ISBLK(st.st_mode))
87✔
1743
                return -ENOTBLK;
1744

1745
        xsprintf_sys_block_path(s, "/loop/", st.st_dev);
87✔
1746
        if (access(s, F_OK) < 0) {
87✔
1747
                if (errno != ENOENT)
87✔
1748
                        return -errno;
×
1749

1750
                /* The device itself isn't a loop device, but maybe it's a partition and its parent is? */
1751
                xsprintf_sys_block_path(s, "/../loop/", st.st_dev);
87✔
1752
                if (access(s, F_OK) < 0)
87✔
1753
                        return errno == ENOENT ? false : -errno;
87✔
1754
        }
1755

1756
        return true;
1757
}
1758

1759
static int run_fsck(int node_fd, const char *fstype) {
6✔
1760
        int r, exit_status;
6✔
1761
        pid_t pid;
6✔
1762

1763
        assert(node_fd >= 0);
6✔
1764
        assert(fstype);
6✔
1765

1766
        r = fsck_exists_for_fstype(fstype);
6✔
1767
        if (r < 0) {
6✔
1768
                log_debug_errno(r, "Couldn't determine whether fsck for %s exists, proceeding anyway.", fstype);
×
1769
                return 0;
×
1770
        }
1771
        if (r == 0) {
6✔
1772
                log_debug("Not checking partition %s, as fsck for %s does not exist.", FORMAT_PROC_FD_PATH(node_fd), fstype);
×
1773
                return 0;
×
1774
        }
1775

1776
        r = safe_fork_full(
6✔
1777
                        "(fsck)",
1778
                        NULL,
1779
                        &node_fd, 1, /* Leave the node fd open */
1780
                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_CLOEXEC_OFF,
1781
                        &pid);
1782
        if (r < 0)
12✔
1783
                return log_debug_errno(r, "Failed to fork off fsck: %m");
×
1784
        if (r == 0) {
12✔
1785
                /* Child */
1786
                execlp("fsck", "fsck", "-aT", FORMAT_PROC_FD_PATH(node_fd), NULL);
6✔
1787
                log_open();
6✔
1788
                log_debug_errno(errno, "Failed to execl() fsck: %m");
×
1789
                _exit(FSCK_OPERATIONAL_ERROR);
×
1790
        }
1791

1792
        exit_status = wait_for_terminate_and_check("fsck", pid, 0);
6✔
1793
        if (exit_status < 0)
6✔
1794
                return log_debug_errno(exit_status, "Failed to fork off fsck: %m");
×
1795

1796
        if ((exit_status & ~FSCK_ERROR_CORRECTED) != FSCK_SUCCESS) {
6✔
1797
                log_debug("fsck failed with exit status %i.", exit_status);
×
1798

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

1802
                log_debug("Ignoring fsck error.");
×
1803
        }
1804

1805
        return 0;
1806
}
1807

1808
static int fs_grow(const char *node_path, int mount_fd, const char *mount_path) {
4✔
1809
        _cleanup_close_ int _mount_fd = -EBADF, node_fd = -EBADF;
4✔
1810
        uint64_t size, newsize;
4✔
1811
        const char *id;
4✔
1812
        int r;
4✔
1813

1814
        assert(node_path);
4✔
1815
        assert(mount_fd >= 0 || mount_path);
4✔
1816

1817
        node_fd = open(node_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
4✔
1818
        if (node_fd < 0)
4✔
1819
                return log_debug_errno(errno, "Failed to open node device %s: %m", node_path);
×
1820

1821
        r = blockdev_get_device_size(node_fd, &size);
4✔
1822
        if (r < 0)
4✔
1823
                return log_debug_errno(r, "Failed to get block device size of %s: %m", node_path);
×
1824

1825
        if (mount_fd < 0) {
4✔
1826
                assert(mount_path);
4✔
1827

1828
                _mount_fd = open(mount_path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
4✔
1829
                if (_mount_fd < 0)
4✔
1830
                        return log_debug_errno(errno, "Failed to open mounted file system %s: %m", mount_path);
×
1831

1832
                mount_fd = _mount_fd;
1833
        } else {
1834
                mount_fd = fd_reopen_condition(mount_fd, O_RDONLY|O_DIRECTORY|O_CLOEXEC, O_RDONLY|O_DIRECTORY|O_CLOEXEC, &_mount_fd);
×
1835
                if (mount_fd < 0)
×
1836
                        return log_debug_errno(errno, "Failed to reopen mount node: %m");
×
1837
        }
1838

1839
        id = mount_path ?: node_path;
4✔
1840

1841
        log_debug("Resizing \"%s\" to %"PRIu64" bytes...", id, size);
4✔
1842
        r = resize_fs(mount_fd, size, &newsize);
4✔
1843
        if (r < 0)
4✔
1844
                return log_debug_errno(r, "Failed to resize \"%s\" to %"PRIu64" bytes: %m", id, size);
2✔
1845

1846
        if (newsize == size)
2✔
1847
                log_debug("Successfully resized \"%s\" to %s bytes.",
2✔
1848
                          id, FORMAT_BYTES(newsize));
1849
        else {
1850
                assert(newsize < size);
×
1851
                log_debug("Successfully resized \"%s\" to %s bytes (%"PRIu64" bytes lost due to blocksize).",
×
1852
                          id, FORMAT_BYTES(newsize), size - newsize);
1853
        }
1854

1855
        return 0;
1856
}
1857

1858
int partition_pick_mount_options(
289✔
1859
                PartitionDesignator d,
1860
                const char *fstype,
1861
                bool rw,
1862
                bool discard,
1863
                char **ret_options,
1864
                unsigned long *ret_ms_flags) {
1865

1866
        _cleanup_free_ char *options = NULL;
289✔
1867

1868
        assert(ret_options);
289✔
1869

1870
        /* Selects a baseline of bind mount flags, that should always apply.
1871
         *
1872
         * Firstly, we set MS_NODEV universally on all mounts, since we don't want to allow device nodes outside of /dev/.
1873
         *
1874
         * On /var/tmp/ we'll also set MS_NOSUID, same as we set for /tmp/ on the host.
1875
         *
1876
         * On the ESP and XBOOTLDR partitions we'll also disable symlinks, and execution. These file systems
1877
         * are generally untrusted (i.e. not encrypted or authenticated), and typically VFAT hence we should
1878
         * be as restrictive as possible, and this shouldn't hurt, since the functionality is not available
1879
         * there anyway. */
1880

1881
        unsigned long flags = MS_NODEV;
289✔
1882

1883
        if (!rw)
289✔
1884
                flags |= MS_RDONLY;
159✔
1885

1886
        switch (d) {
289✔
1887

1888
        case PARTITION_ESP:
54✔
1889
        case PARTITION_XBOOTLDR:
1890
                flags |= MS_NOSUID|MS_NOEXEC|ms_nosymfollow_supported();
54✔
1891

1892
                /* The ESP might contain a pre-boot random seed. Let's make this unaccessible to regular
1893
                 * userspace. ESP/XBOOTLDR is almost certainly VFAT, hence if we don't know assume it is. */
1894
                if (!fstype || fstype_can_fmask_dmask(fstype))
54✔
1895
                        if (!strextend_with_separator(&options, ",", "fmask=0177,dmask=0077"))
28✔
1896
                                return -ENOMEM;
1897
                break;
1898

1899
        case PARTITION_TMP:
×
1900
                flags |= MS_NOSUID;
×
1901
                break;
×
1902

1903
        default:
1904
                break;
1905
        }
1906

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

1927
                if (option && !strextend_with_separator(&options, ",", option))
159✔
1928
                        return -ENOMEM;
1929
        }
1930

1931
        if (discard && fstype && fstype_can_discard(fstype))
289✔
1932
                if (!strextend_with_separator(&options, ",", "discard"))
×
1933
                        return -ENOMEM;
1934

1935
        if (!ret_ms_flags) /* Fold flags into option string if ret_flags specified as NULL */
289✔
1936
                if (!strextend_with_separator(&options, ",",
×
1937
                                              FLAGS_SET(flags, MS_RDONLY) ? "ro" : "rw",
1938
                                              FLAGS_SET(flags, MS_NODEV) ? "nodev" : "dev",
1939
                                              FLAGS_SET(flags, MS_NOSUID) ? "nosuid" : "suid",
1940
                                              FLAGS_SET(flags, MS_NOEXEC) ? "noexec" : "exec",
1941
                                              FLAGS_SET(flags, MS_NOSYMFOLLOW) ? "nosymfollow" : NULL))
1942
                        /* NB: we suppress 'symfollow' here, since it's the default, and old /bin/mount might not know it */
1943
                        return -ENOMEM;
1944

1945
        if (ret_ms_flags)
289✔
1946
                *ret_ms_flags = flags;
289✔
1947

1948
        *ret_options = TAKE_PTR(options);
289✔
1949
        return 0;
289✔
1950
}
1951

1952
static bool need_user_mapping(uid_t uid_shift, uid_t uid_range) {
536✔
1953

1954
        if (!uid_is_valid(uid_shift))
536✔
1955
                return false;
1956

1957
        return uid_shift != 0 || uid_range != UINT32_MAX;
10✔
1958
}
1959

1960
static int mount_partition(
1,378✔
1961
                PartitionDesignator d,
1962
                DissectedPartition *m,
1963
                const char *where,
1964
                const char *directory,
1965
                uid_t uid_shift,
1966
                uid_t uid_range,
1967
                int userns_fd,
1968
                DissectImageFlags flags) {
1969

1970
        _cleanup_free_ char *chased = NULL, *options = NULL;
1,378✔
1971
        const char *p = NULL, *node, *fstype = NULL;
1,378✔
1972
        bool rw, discard, grow;
1,378✔
1973
        unsigned long ms_flags;
1,378✔
1974
        int r;
1,378✔
1975

1976
        assert(m);
1,378✔
1977

1978
        if (!m->found)
1,378✔
1979
                return 0;
1980

1981
        /* Check the various combinations when we can't do anything anymore */
1982
        if (m->fsmount_fd < 0 && m->mount_node_fd < 0)
297✔
1983
                return 0;
1984
        if (m->fsmount_fd >= 0 && !where)
297✔
1985
                return 0;
1986
        if (!where && m->mount_node_fd < 0)
289✔
1987
                return 0;
1988

1989
        if (m->fsmount_fd < 0) {
297✔
1990
                fstype = dissected_partition_fstype(m);
289✔
1991
                if (!fstype)
289✔
1992
                        return -EAFNOSUPPORT;
1993

1994
                /* We are looking at an encrypted partition? This either means stacked encryption, or the
1995
                 * caller didn't call dissected_image_decrypt() beforehand. Let's return a recognizable error
1996
                 * for this case. */
1997
                if (streq(fstype, "crypto_LUKS"))
289✔
1998
                        return -EUNATCH;
1999

2000
                r = dissect_fstype_ok(fstype);
289✔
2001
                if (r < 0)
289✔
2002
                        return r;
2003
                if (!r)
289✔
2004
                        return -EIDRM; /* Recognizable error */
2005
        }
2006

2007
        node = m->mount_node_fd < 0 ? NULL : FORMAT_PROC_FD_PATH(m->mount_node_fd);
297✔
2008
        rw = m->rw && !(flags & DISSECT_IMAGE_MOUNT_READ_ONLY);
297✔
2009

2010
        discard = ((flags & DISSECT_IMAGE_DISCARD) ||
297✔
2011
                   ((flags & DISSECT_IMAGE_DISCARD_ON_LOOP) && (m->node && is_loop_device(m->node) > 0)));
296✔
2012

2013
        grow = rw && m->growfs && FLAGS_SET(flags, DISSECT_IMAGE_GROWFS);
297✔
2014

2015
        if (FLAGS_SET(flags, DISSECT_IMAGE_FSCK) && rw && m->mount_node_fd >= 0 && m->fsmount_fd < 0) {
297✔
2016
                r = run_fsck(m->mount_node_fd, fstype);
6✔
2017
                if (r < 0)
6✔
2018
                        return r;
2019
        }
2020

2021
        if (where) {
297✔
2022
                if (directory) {
295✔
2023
                        /* Automatically create missing mount points inside the image, if necessary. */
2024
                        r = mkdir_p_root(where, directory, uid_shift, (gid_t) uid_shift, 0755);
56✔
2025
                        if (r < 0 && r != -EROFS)
56✔
2026
                                return r;
2027

2028
                        r = chase(directory, where, CHASE_PREFIX_ROOT, &chased, NULL);
56✔
2029
                        if (r < 0)
56✔
2030
                                return r;
2031

2032
                        p = chased;
56✔
2033
                } else {
2034
                        /* Create top-level mount if missing – but only if this is asked for. This won't modify the
2035
                         * image (as the branch above does) but the host hierarchy, and the created directory might
2036
                         * survive our mount in the host hierarchy hence. */
2037
                        if (FLAGS_SET(flags, DISSECT_IMAGE_MKDIR)) {
239✔
2038
                                r = mkdir_p(where, 0755);
11✔
2039
                                if (r < 0)
11✔
2040
                                        return r;
2041
                        }
2042

2043
                        p = where;
2044
                }
2045
        }
2046

2047
        if (m->fsmount_fd < 0) {
297✔
2048
                r = partition_pick_mount_options(d, fstype, rw, discard, &options, &ms_flags);
289✔
2049
                if (r < 0)
289✔
2050
                        return r;
2051

2052
                if (need_user_mapping(uid_shift, uid_range) && fstype_can_uid_gid(fstype)) {
289✔
2053
                        _cleanup_free_ char *uid_option = NULL;
×
2054

2055
                        if (asprintf(&uid_option, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
×
2056
                                return -ENOMEM;
2057

2058
                        if (!strextend_with_separator(&options, ",", uid_option))
×
2059
                                return -ENOMEM;
2060

2061
                        userns_fd = -EBADF; /* Not needed */
×
2062
                }
2063

2064
                if (!isempty(m->mount_options))
289✔
2065
                        if (!strextend_with_separator(&options, ",", m->mount_options))
4✔
2066
                                return -ENOMEM;
2067
        }
2068

2069
        if (p) {
297✔
2070
                if (m->fsmount_fd >= 0) {
295✔
2071
                        /* Case #1: Attach existing fsmount fd to the file system */
2072

2073
                        r = mount_exchange_graceful(
16✔
2074
                                        m->fsmount_fd,
2075
                                        p,
2076
                                        FLAGS_SET(flags, DISSECT_IMAGE_TRY_ATOMIC_MOUNT_EXCHANGE));
8✔
2077
                        if (r < 0)
8✔
2078
                                return log_debug_errno(r, "Failed to mount image on '%s': %m", p);
×
2079

2080
                } else {
2081
                        assert(node);
287✔
2082

2083
                        /* Case #2: Mount directly into place */
2084
                        r = mount_nofollow_verbose(LOG_DEBUG, node, p, fstype, ms_flags, options);
287✔
2085
                        if (r < 0)
287✔
2086
                                return r;
2087

2088
                        if (grow)
287✔
2089
                                (void) fs_grow(node, -EBADF, p);
4✔
2090

2091
                        if (userns_fd >= 0) {
287✔
2092
                                r = remount_idmap_fd(STRV_MAKE(p), userns_fd, /* extra_mount_attr_set= */ 0);
×
2093
                                if (r < 0)
×
2094
                                        return r;
×
2095
                        }
2096
                }
2097
        } else {
2098
                assert(node);
2✔
2099

2100
                /* Case #3: Create fsmount fd */
2101

2102
                m->fsmount_fd = make_fsmount(LOG_DEBUG, node, fstype, ms_flags, options, userns_fd);
2✔
2103
                if (m->fsmount_fd < 0)
2✔
2104
                        return m->fsmount_fd;
2105

2106
                if (grow)
2✔
2107
                        (void) fs_grow(node, m->fsmount_fd, NULL);
×
2108
        }
2109

2110
        return 1;
2111
}
2112

2113
static int mount_root_tmpfs(const char *where, uid_t uid_shift, uid_t uid_range, DissectImageFlags flags) {
×
2114
        _cleanup_free_ char *options = NULL;
×
2115
        int r;
×
2116

2117
        assert(where);
×
2118

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

2121
        if (FLAGS_SET(flags, DISSECT_IMAGE_MKDIR)) {
×
2122
                r = mkdir_p(where, 0755);
×
2123
                if (r < 0)
×
2124
                        return r;
2125
        }
2126

2127
        if (need_user_mapping(uid_shift, uid_range)) {
×
2128
                if (asprintf(&options, "uid=" UID_FMT ",gid=" GID_FMT, uid_shift, (gid_t) uid_shift) < 0)
×
2129
                        return -ENOMEM;
2130
        }
2131

2132
        r = mount_nofollow_verbose(LOG_DEBUG, "rootfs", where, "tmpfs", MS_NODEV, options);
×
2133
        if (r < 0)
×
2134
                return r;
×
2135

2136
        return 1;
2137
}
2138

2139
static int mount_point_is_available(const char *where, const char *path, bool missing_ok) {
215✔
2140
        _cleanup_free_ char *p = NULL;
215✔
2141
        int r;
215✔
2142

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

2146
        r = chase(path, where, CHASE_PREFIX_ROOT, &p, NULL);
215✔
2147
        if (r == -ENOENT)
215✔
2148
                return missing_ok;
94✔
2149
        if (r < 0)
121✔
2150
                return log_debug_errno(r, "Failed to chase \"%s\": %m", path);
×
2151

2152
        r = dir_is_empty(p, /* ignore_hidden_or_backup= */ false);
121✔
2153
        if (r == -ENOTDIR)
121✔
2154
                return false;
2155
        if (r < 0)
121✔
2156
                return log_debug_errno(r, "Failed to check directory \"%s\": %m", p);
×
2157
        return r > 0;
121✔
2158
}
2159

2160
int dissected_image_mount(
247✔
2161
                DissectedImage *m,
2162
                const char *where,
2163
                uid_t uid_shift,
2164
                uid_t uid_range,
2165
                int userns_fd,
2166
                DissectImageFlags flags) {
2167

2168
        _cleanup_close_ int my_userns_fd = -EBADF;
247✔
2169
        int r;
247✔
2170

2171
        assert(m);
247✔
2172

2173
        if (FLAGS_SET(flags, DISSECT_IMAGE_FOREIGN_UID)) /* For image based mounts we currently require an identity mapping */
247✔
2174
                return -EOPNOTSUPP;
2175

2176
        /* If 'where' is NULL then we'll use the new mount API to create fsmount() fds for the mounts and
2177
         * store them in DissectedPartition.fsmount_fd.
2178
         *
2179
         * If 'where' is not NULL then we'll either mount the partitions to the right places ourselves,
2180
         * or use DissectedPartition.fsmount_fd and bind it to the right places.
2181
         *
2182
         * This allows splitting the setting up the superblocks and the binding to file systems paths into
2183
         * two distinct and differently privileged components: one that gets the fsmount fds, and the other
2184
         * that then applies them.
2185
         *
2186
         * Returns:
2187
         *
2188
         *  -ENXIO        → No root partition found
2189
         *  -EMEDIUMTYPE  → DISSECT_IMAGE_VALIDATE_OS set but no os-release/extension-release file found
2190
         *  -EUNATCH      → Encrypted partition found for which no dm-crypt was set up yet
2191
         *  -EUCLEAN      → fsck for file system failed
2192
         *  -EBUSY        → File system already mounted/used elsewhere (kernel)
2193
         *  -EAFNOSUPPORT → File system type not supported or not known
2194
         *  -EIDRM        → File system is not among allowlisted "common" file systems
2195
         */
2196

2197
        if (!where && (flags & (DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_VALIDATE_OS_EXT)) != 0)
247✔
2198
                return -EOPNOTSUPP; /* for now, not supported */
2199

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

2204
        if (userns_fd < 0 && need_user_mapping(uid_shift, uid_range) && FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_IDMAPPED)) {
247✔
2205

2206
                my_userns_fd = make_userns(uid_shift, uid_range, UID_INVALID, UID_INVALID, REMOUNT_IDMAPPING_HOST_ROOT);
2✔
2207
                if (my_userns_fd < 0)
2✔
2208
                        return my_userns_fd;
2209

2210
                userns_fd = my_userns_fd;
2211
        }
2212

2213
        if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0) {
247✔
2214

2215
                /* First mount the root fs. If there's none we use a tmpfs. */
2216
                if (m->partitions[PARTITION_ROOT].found) {
241✔
2217
                        r = mount_partition(PARTITION_ROOT, m->partitions + PARTITION_ROOT, where, NULL, uid_shift, uid_range, userns_fd, flags);
241✔
2218
                        if (r < 0)
241✔
2219
                                return r;
2220

2221
                } else if (where) {
×
2222
                        r = mount_root_tmpfs(where, uid_shift, uid_range, flags);
×
2223
                        if (r < 0)
×
2224
                                return r;
2225
                }
2226

2227
                /* For us mounting root always means mounting /usr as well */
2228
                r = mount_partition(PARTITION_USR, m->partitions + PARTITION_USR, where, "/usr", uid_shift, uid_range, userns_fd, flags);
241✔
2229
                if (r < 0)
241✔
2230
                        return r;
2231
        }
2232

2233
        if ((flags & DISSECT_IMAGE_MOUNT_NON_ROOT_ONLY) == 0 &&
241✔
2234
            (flags & (DISSECT_IMAGE_VALIDATE_OS|DISSECT_IMAGE_VALIDATE_OS_EXT)) != 0) {
241✔
2235
                /* If either one of the validation flags are set, ensure that the image qualifies as
2236
                 * one or the other (or both). */
2237
                bool ok = false;
79✔
2238

2239
                assert(where);
79✔
2240

2241
                if (FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS)) {
79✔
2242
                        r = path_is_os_tree(where);
44✔
2243
                        if (r < 0)
44✔
2244
                                return r;
2245
                        if (r > 0)
44✔
2246
                                ok = true;
2247
                }
2248
                if (!ok && FLAGS_SET(flags, DISSECT_IMAGE_VALIDATE_OS_EXT) && m->image_name) {
46✔
2249
                        r = extension_has_forbidden_content(where);
46✔
2250
                        if (r < 0)
46✔
2251
                                return r;
2252
                        if (r == 0) {
46✔
2253
                                r = path_is_extension_tree(IMAGE_SYSEXT, where, m->image_name, FLAGS_SET(flags, DISSECT_IMAGE_RELAX_EXTENSION_CHECK));
46✔
2254
                                if (r == 0)
46✔
2255
                                        r = path_is_extension_tree(IMAGE_CONFEXT, where, m->image_name, FLAGS_SET(flags, DISSECT_IMAGE_RELAX_EXTENSION_CHECK));
7✔
2256
                                if (r < 0)
46✔
2257
                                        return r;
2258
                                if (r > 0)
46✔
2259
                                        ok = true;
2260
                        }
2261
                }
2262

2263
                if (!ok)
×
2264
                        return -ENOMEDIUM;
×
2265
        }
2266

2267
        if (flags & DISSECT_IMAGE_MOUNT_ROOT_ONLY)
247✔
2268
                return 0;
2269

2270
        r = mount_partition(PARTITION_HOME, m->partitions + PARTITION_HOME, where, "/home", uid_shift, uid_range, userns_fd, flags);
187✔
2271
        if (r < 0)
187✔
2272
                return r;
2273

2274
        r = mount_partition(PARTITION_SRV, m->partitions + PARTITION_SRV, where, "/srv", uid_shift, uid_range, userns_fd, flags);
187✔
2275
        if (r < 0)
187✔
2276
                return r;
2277

2278
        r = mount_partition(PARTITION_VAR, m->partitions + PARTITION_VAR, where, "/var", uid_shift, uid_range, userns_fd, flags);
187✔
2279
        if (r < 0)
187✔
2280
                return r;
2281

2282
        r = mount_partition(PARTITION_TMP, m->partitions + PARTITION_TMP, where, "/var/tmp", uid_shift, uid_range, userns_fd, flags);
187✔
2283
        if (r < 0)
187✔
2284
                return r;
2285

2286
        int slash_boot_is_available = 0;
187✔
2287
        if (where) {
187✔
2288
                r = slash_boot_is_available = mount_point_is_available(where, "/boot", /* missing_ok = */ true);
185✔
2289
                if (r < 0)
185✔
2290
                        return r;
2291
        }
2292
        if (!where || slash_boot_is_available) {
187✔
2293
                r = mount_partition(PARTITION_XBOOTLDR, m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, uid_range, userns_fd, flags);
120✔
2294
                if (r < 0)
120✔
2295
                        return r;
2296
                slash_boot_is_available = !r;
120✔
2297
        }
2298

2299
        if (m->partitions[PARTITION_ESP].found) {
187✔
2300
                const char *esp_path = NULL;
28✔
2301

2302
                if (where) {
28✔
2303
                        /* Mount the ESP to /boot/ if it exists and is empty and we didn't already mount the
2304
                         * XBOOTLDR partition into it. Otherwise, use /efi instead, but only if it exists
2305
                         * and is empty. */
2306

2307
                        if (slash_boot_is_available) {
28✔
2308
                                r = mount_point_is_available(where, "/boot", /* missing_ok = */ false);
2✔
2309
                                if (r < 0)
2✔
2310
                                        return r;
2311
                                if (r > 0)
2✔
2312
                                        esp_path = "/boot";
2313
                        }
2314

2315
                        if (!esp_path) {
2316
                                r = mount_point_is_available(where, "/efi", /* missing_ok = */ true);
28✔
2317
                                if (r < 0)
28✔
2318
                                        return r;
2319
                                if (r > 0)
28✔
2320
                                        esp_path = "/efi";
28✔
2321
                        }
2322
                }
2323

2324
                /* OK, let's mount the ESP now (possibly creating the dir if missing) */
2325
                r = mount_partition(PARTITION_ESP, m->partitions + PARTITION_ESP, where, esp_path, uid_shift, uid_range, userns_fd, flags);
28✔
2326
                if (r < 0)
28✔
2327
                        return r;
×
2328
        }
2329

2330
        return 0;
2331
}
2332

2333
int dissected_image_mount_and_warn(
81✔
2334
                DissectedImage *m,
2335
                const char *where,
2336
                uid_t uid_shift,
2337
                uid_t uid_range,
2338
                int userns_fd,
2339
                DissectImageFlags flags) {
2340

2341
        int r;
81✔
2342

2343
        assert(m);
81✔
2344

2345
        r = dissected_image_mount(m, where, uid_shift, uid_range, userns_fd, flags);
81✔
2346
        if (r == -ENXIO)
81✔
2347
                return log_error_errno(r, "Failed to mount image: No root file system found in image.");
×
2348
        if (r == -EMEDIUMTYPE)
81✔
2349
                return log_error_errno(r, "Failed to mount image: No suitable os-release/extension-release file in image found.");
×
2350
        if (r == -EUNATCH)
81✔
2351
                return log_error_errno(r, "Failed to mount image: Encrypted file system discovered, but decryption not requested.");
×
2352
        if (r == -EUCLEAN)
81✔
2353
                return log_error_errno(r, "Failed to mount image: File system check on image failed.");
×
2354
        if (r == -EBUSY)
81✔
2355
                return log_error_errno(r, "Failed to mount image: File system already mounted elsewhere.");
×
2356
        if (r == -EAFNOSUPPORT)
81✔
2357
                return log_error_errno(r, "Failed to mount image: File system type not supported or not known.");
×
2358
        if (r == -EIDRM)
81✔
2359
                return log_error_errno(r, "Failed to mount image: File system is too uncommon, refused.");
×
2360
        if (r < 0)
81✔
2361
                return log_error_errno(r, "Failed to mount image: %m");
×
2362

2363
        return r;
2364
}
2365

2366
#if HAVE_LIBCRYPTSETUP
2367
struct DecryptedPartition {
2368
        struct crypt_device *device;
2369
        char *name;
2370
        bool relinquished;
2371
};
2372
#endif
2373

2374
typedef struct DecryptedPartition DecryptedPartition;
2375

2376
struct DecryptedImage {
2377
        unsigned n_ref;
2378
        DecryptedPartition *decrypted;
2379
        size_t n_decrypted;
2380
};
2381

2382
static DecryptedImage* decrypted_image_free(DecryptedImage *d) {
43✔
2383
#if HAVE_LIBCRYPTSETUP
2384
        int r;
43✔
2385

2386
        if (!d)
43✔
2387
                return NULL;
2388

2389
        for (size_t i = 0; i < d->n_decrypted; i++) {
86✔
2390
                DecryptedPartition *p = d->decrypted + i;
43✔
2391

2392
                if (p->device && p->name && !p->relinquished) {
43✔
2393
                        _cleanup_free_ char *node = NULL;
2✔
2394

2395
                        node = path_join("/dev/mapper", p->name);
2✔
2396
                        if (node) {
2✔
2397
                                r = btrfs_forget_device(node);
2✔
2398
                                if (r < 0 && r != -ENOENT)
2✔
2399
                                        log_debug_errno(r, "Failed to forget btrfs device %s, ignoring: %m", node);
×
2400
                        } else
2401
                                log_oom_debug();
×
2402

2403
                        /* Let's deactivate lazily, as the dm volume may be already/still used by other processes. */
2404
                        r = sym_crypt_deactivate_by_name(p->device, p->name, CRYPT_DEACTIVATE_DEFERRED);
2✔
2405
                        if (r < 0)
2✔
2406
                                log_debug_errno(r, "Failed to deactivate encrypted partition %s", p->name);
2✔
2407
                }
2408

2409
                if (p->device)
43✔
2410
                        sym_crypt_free(p->device);
43✔
2411
                free(p->name);
43✔
2412
        }
2413

2414
        free(d->decrypted);
43✔
2415
        free(d);
43✔
2416
#endif
2417
        return NULL;
43✔
2418
}
2419

2420
DEFINE_TRIVIAL_REF_UNREF_FUNC(DecryptedImage, decrypted_image, decrypted_image_free);
1,809✔
2421

2422
#if HAVE_LIBCRYPTSETUP
2423
static int decrypted_image_new(DecryptedImage **ret) {
43✔
2424
        _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
43✔
2425

2426
        assert(ret);
43✔
2427

2428
        d = new(DecryptedImage, 1);
43✔
2429
        if (!d)
43✔
2430
                return -ENOMEM;
2431

2432
        *d = (DecryptedImage) {
43✔
2433
                .n_ref = 1,
2434
        };
2435

2436
        *ret = TAKE_PTR(d);
43✔
2437
        return 0;
43✔
2438
}
2439

2440
static int make_dm_name_and_node(const void *original_node, const char *suffix, char **ret_name, char **ret_node) {
48✔
2441
        _cleanup_free_ char *name = NULL, *node = NULL;
48✔
2442
        const char *base;
48✔
2443

2444
        assert(original_node);
48✔
2445
        assert(suffix);
48✔
2446
        assert(ret_name);
48✔
2447
        assert(ret_node);
48✔
2448

2449
        base = strrchr(original_node, '/');
48✔
2450
        if (!base)
48✔
2451
                base = original_node;
2452
        else
2453
                base++;
5✔
2454
        if (isempty(base))
96✔
2455
                return -EINVAL;
2456

2457
        name = strjoin(base, suffix);
48✔
2458
        if (!name)
48✔
2459
                return -ENOMEM;
2460
        if (!filename_is_valid(name))
48✔
2461
                return -EINVAL;
2462

2463
        node = path_join(sym_crypt_get_dir(), name);
48✔
2464
        if (!node)
48✔
2465
                return -ENOMEM;
2466

2467
        *ret_name = TAKE_PTR(name);
48✔
2468
        *ret_node = TAKE_PTR(node);
48✔
2469

2470
        return 0;
48✔
2471
}
2472

2473
static int decrypt_partition(
75✔
2474
                DissectedPartition *m,
2475
                const char *passphrase,
2476
                DissectImageFlags flags,
2477
                DecryptedImage *d) {
2478

2479
        _cleanup_free_ char *node = NULL, *name = NULL;
75✔
2480
        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
×
2481
        _cleanup_close_ int fd = -EBADF;
75✔
2482
        int r;
75✔
2483

2484
        assert(m);
75✔
2485
        assert(d);
75✔
2486

2487
        if (!m->found || !m->node || !m->fstype)
75✔
2488
                return 0;
2489

2490
        if (!streq(m->fstype, "crypto_LUKS"))
75✔
2491
                return 0;
2492

2493
        if (!passphrase)
×
2494
                return -ENOKEY;
2495

2496
        r = dlopen_cryptsetup();
×
2497
        if (r < 0)
×
2498
                return r;
2499

2500
        r = make_dm_name_and_node(m->node, "-decrypted", &name, &node);
×
2501
        if (r < 0)
×
2502
                return r;
2503

2504
        if (!GREEDY_REALLOC0(d->decrypted, d->n_decrypted + 1))
×
2505
                return -ENOMEM;
2506

2507
        r = sym_crypt_init(&cd, m->node);
×
2508
        if (r < 0)
×
2509
                return log_debug_errno(r, "Failed to initialize dm-crypt: %m");
×
2510

2511
        cryptsetup_enable_logging(cd);
×
2512

2513
        r = sym_crypt_load(cd, CRYPT_LUKS, NULL);
×
2514
        if (r < 0)
×
2515
                return log_debug_errno(r, "Failed to load LUKS metadata: %m");
×
2516

2517
        r = sym_crypt_activate_by_passphrase(cd, name, CRYPT_ANY_SLOT, passphrase, strlen(passphrase),
×
2518
                                             ((flags & DISSECT_IMAGE_DEVICE_READ_ONLY) ? CRYPT_ACTIVATE_READONLY : 0) |
2519
                                             ((flags & DISSECT_IMAGE_DISCARD_ON_CRYPTO) ? CRYPT_ACTIVATE_ALLOW_DISCARDS : 0));
2520
        if (r < 0) {
×
2521
                log_debug_errno(r, "Failed to activate LUKS device: %m");
×
2522
                return r == -EPERM ? -EKEYREJECTED : r;
×
2523
        }
2524

2525
        fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
×
2526
        if (fd < 0)
×
2527
                return log_debug_errno(errno, "Failed to open %s: %m", node);
×
2528

2529
        d->decrypted[d->n_decrypted++] = (DecryptedPartition) {
×
2530
                .name = TAKE_PTR(name),
×
2531
                .device = TAKE_PTR(cd),
×
2532
        };
2533

2534
        m->decrypted_node = TAKE_PTR(node);
×
2535
        close_and_replace(m->mount_node_fd, fd);
×
2536

2537
        return 0;
×
2538
}
2539

2540
static int verity_can_reuse(
5✔
2541
                const VeritySettings *verity,
2542
                const char *name,
2543
                struct crypt_device **ret_cd) {
2544

2545
        /* If the same volume was already open, check that the root hashes match, and reuse it if they do */
2546
        _cleanup_free_ char *root_hash_existing = NULL;
5✔
2547
        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
5✔
2548
        struct crypt_params_verity crypt_params = {};
5✔
2549
        size_t root_hash_existing_size;
5✔
2550
        int r;
5✔
2551

2552
        assert(verity);
5✔
2553
        assert(name);
5✔
2554
        assert(ret_cd);
5✔
2555

2556
        r = sym_crypt_init_by_name(&cd, name);
5✔
2557
        if (r < 0)
5✔
2558
                return log_debug_errno(r, "Error opening verity device, crypt_init_by_name failed: %m");
×
2559

2560
        cryptsetup_enable_logging(cd);
5✔
2561

2562
        r = sym_crypt_get_verity_info(cd, &crypt_params);
5✔
2563
        if (r < 0)
5✔
2564
                return log_debug_errno(r, "Error opening verity device, crypt_get_verity_info failed: %m");
×
2565

2566
        root_hash_existing_size = verity->root_hash_size;
5✔
2567
        root_hash_existing = malloc0(root_hash_existing_size);
5✔
2568
        if (!root_hash_existing)
5✔
2569
                return -ENOMEM;
2570

2571
        r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, root_hash_existing, &root_hash_existing_size, NULL, 0);
5✔
2572
        if (r < 0)
5✔
2573
                return log_debug_errno(r, "Error opening verity device, crypt_volume_key_get failed: %m");
×
2574
        if (verity->root_hash_size != root_hash_existing_size ||
5✔
2575
            memcmp(root_hash_existing, verity->root_hash, verity->root_hash_size) != 0)
5✔
2576
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Error opening verity device, it already exists but root hashes are different.");
×
2577

2578
#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
2579
        /* Ensure that, if signatures are supported, we only reuse the device if the previous mount used the
2580
         * same settings, so that a previous unsigned mount will not be reused if the user asks to use
2581
         * signing for the new one, and vice versa. */
2582
        if (!!verity->root_hash_sig != !!(crypt_params.flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE))
5✔
2583
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Error opening verity device, it already exists but signature settings are not the same.");
5✔
2584
#endif
2585

2586
        *ret_cd = TAKE_PTR(cd);
×
2587
        return 0;
×
2588
}
2589

2590
static char* dm_deferred_remove_clean(char *name) {
5✔
2591
        if (!name)
5✔
2592
                return NULL;
2593

2594
        (void) sym_crypt_deactivate_by_name(NULL, name, CRYPT_DEACTIVATE_DEFERRED);
5✔
2595
        return mfree(name);
5✔
2596
}
2597
DEFINE_TRIVIAL_CLEANUP_FUNC(char *, dm_deferred_remove_clean);
48✔
2598

2599
static int validate_signature_userspace(const VeritySettings *verity, DissectImageFlags flags) {
38✔
2600
        int r;
38✔
2601

2602
        if (!FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_USERSPACE_VERITY)) {
38✔
2603
                log_debug("Userspace dm-verity signature authentication disabled via flag.");
×
2604
                return 0;
38✔
2605
        }
2606

2607
        r = secure_getenv_bool("SYSTEMD_ALLOW_USERSPACE_VERITY");
38✔
2608
        if (r < 0 && r != -ENXIO) {
38✔
2609
                log_debug_errno(r, "Failed to parse $SYSTEMD_ALLOW_USERSPACE_VERITY environment variable, refusing userspace dm-verity signature authentication.");
×
2610
                return 0;
×
2611
        }
2612
        if (!r) {
38✔
2613
                log_debug("Userspace dm-verity signature authentication disabled via $SYSTEMD_ALLOW_USERSPACE_VERITY environment variable.");
×
2614
                return 0;
×
2615
        }
2616

2617
        bool b;
38✔
2618
        r = proc_cmdline_get_bool("systemd.allow_userspace_verity", PROC_CMDLINE_TRUE_WHEN_MISSING, &b);
38✔
2619
        if (r < 0) {
38✔
2620
                log_debug_errno(r, "Failed to parse systemd.allow_userspace_verity= kernel command line option, refusing userspace dm-verity signature authentication.");
×
2621
                return 0;
×
2622
        }
2623
        if (!b) {
38✔
2624
                log_debug("Userspace dm-verity signature authentication disabled via systemd.allow_userspace_verity= kernel command line variable.");
×
2625
                return 0;
×
2626
        }
2627

2628
#if HAVE_OPENSSL
2629
        _cleanup_(sk_X509_free_allp) STACK_OF(X509) *sk = NULL;
38✔
2630
        _cleanup_strv_free_ char **certs = NULL;
×
2631
        _cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
38✔
2632
        _cleanup_free_ char *s = NULL;
38✔
2633
        _cleanup_(BIO_freep) BIO *bio = NULL; /* 'bio' must be freed first, 's' second, hence keep this order
38✔
2634
                                               * of declaration in place, please */
2635
        const unsigned char *d;
38✔
2636

2637
        assert(verity);
38✔
2638
        assert(verity->root_hash);
38✔
2639
        assert(verity->root_hash_sig);
38✔
2640

2641
        /* Because installing a signature certificate into the kernel chain is so messy, let's optionally do
2642
         * userspace validation. */
2643

2644
        r = conf_files_list_nulstr(&certs, ".crt", NULL, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, CONF_PATHS_NULSTR("verity.d"));
38✔
2645
        if (r < 0)
38✔
2646
                return log_debug_errno(r, "Failed to enumerate certificates: %m");
×
2647
        if (strv_isempty(certs)) {
38✔
2648
                log_debug("No userspace dm-verity certificates found.");
×
2649
                return 0;
×
2650
        }
2651

2652
        d = verity->root_hash_sig;
38✔
2653
        p7 = d2i_PKCS7(NULL, &d, (long) verity->root_hash_sig_size);
38✔
2654
        if (!p7)
38✔
2655
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse PKCS7 DER signature data.");
×
2656

2657
        s = hexmem(verity->root_hash, verity->root_hash_size);
38✔
2658
        if (!s)
38✔
2659
                return log_oom_debug();
×
2660

2661
        bio = BIO_new_mem_buf(s, strlen(s));
38✔
2662
        if (!bio)
38✔
2663
                return log_oom_debug();
×
2664

2665
        sk = sk_X509_new_null();
38✔
2666
        if (!sk)
38✔
2667
                return log_oom_debug();
×
2668

2669
        STRV_FOREACH(i, certs) {
119✔
2670
                _cleanup_(X509_freep) X509 *c = NULL;
×
2671
                _cleanup_fclose_ FILE *f = NULL;
81✔
2672

2673
                f = fopen(*i, "re");
81✔
2674
                if (!f) {
81✔
2675
                        log_debug_errno(errno, "Failed to open '%s', ignoring: %m", *i);
×
2676
                        continue;
×
2677
                }
2678

2679
                c = PEM_read_X509(f, NULL, NULL, NULL);
81✔
2680
                if (!c) {
81✔
2681
                        log_debug("Failed to load X509 certificate '%s', ignoring.", *i);
×
2682
                        continue;
×
2683
                }
2684

2685
                if (sk_X509_push(sk, c) == 0)
81✔
2686
                        return log_oom_debug();
×
2687

2688
                TAKE_PTR(c);
81✔
2689
        }
2690

2691
        r = PKCS7_verify(p7, sk, NULL, bio, NULL, PKCS7_NOINTERN|PKCS7_NOVERIFY);
38✔
2692
        if (r)
38✔
2693
                log_debug("Userspace PKCS#7 validation succeeded.");
38✔
2694
        else
2695
                log_debug("Userspace PKCS#7 validation failed: %s", ERR_error_string(ERR_get_error(), NULL));
38✔
2696

2697
        return r;
2698
#else
2699
        log_debug("Not doing client-side validation of dm-verity root hash signatures, OpenSSL support disabled.");
2700
        return 0;
2701
#endif
2702
}
2703

2704
static int do_crypt_activate_verity(
43✔
2705
                struct crypt_device *cd,
2706
                const char *name,
2707
                const VeritySettings *verity,
2708
                DissectImageFlags flags) {
2709

2710
        bool check_signature;
43✔
2711
        int r, k;
43✔
2712

2713
        assert(cd);
43✔
2714
        assert(name);
43✔
2715
        assert(verity);
43✔
2716

2717
        if (verity->root_hash_sig) {
43✔
2718
                r = secure_getenv_bool("SYSTEMD_DISSECT_VERITY_SIGNATURE");
38✔
2719
                if (r < 0 && r != -ENXIO)
38✔
2720
                        log_debug_errno(r, "Failed to parse $SYSTEMD_DISSECT_VERITY_SIGNATURE");
×
2721

2722
                check_signature = r != 0;
38✔
2723
        } else
2724
                check_signature = false;
2725

2726
        if (check_signature) {
38✔
2727

2728
#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
2729
                /* First, if we have support for signed keys in the kernel, then try that first. */
2730
                r = sym_crypt_activate_by_signed_key(
76✔
2731
                                cd,
2732
                                name,
2733
                                verity->root_hash,
38✔
2734
                                verity->root_hash_size,
38✔
2735
                                verity->root_hash_sig,
38✔
2736
                                verity->root_hash_sig_size,
38✔
2737
                                CRYPT_ACTIVATE_READONLY);
2738
                if (r >= 0)
38✔
2739
                        return r;
2740

2741
                log_debug_errno(r, "Validation of dm-verity signature failed via the kernel, trying userspace validation instead: %m");
38✔
2742
#else
2743
                log_debug("Activation of verity device with signature requested, but not supported via the kernel by %s due to missing crypt_activate_by_signed_key(), trying userspace validation instead.",
2744
                          program_invocation_short_name);
2745
                r = 0; /* Set for the propagation below */
2746
#endif
2747

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

2751
                /* Preferably propagate the original kernel error, so that the fallback logic can work,
2752
                 * as the device-mapper is finicky around concurrent activations of the same volume */
2753
                k = validate_signature_userspace(verity, flags);
38✔
2754
                if (k < 0)
38✔
2755
                        return r < 0 ? r : k;
2756
                if (k == 0)
38✔
2757
                        return log_debug_errno(r < 0 ? r : SYNTHETIC_ERRNO(ENOKEY),
×
2758
                                               "Activation of signed Verity volume worked neither via the kernel nor in userspace, can't activate.");
2759
        }
2760

2761
        return sym_crypt_activate_by_volume_key(
43✔
2762
                        cd,
2763
                        name,
2764
                        verity->root_hash,
43✔
2765
                        verity->root_hash_size,
43✔
2766
                        CRYPT_ACTIVATE_READONLY);
2767
}
2768

2769
static usec_t verity_timeout(void) {
×
2770
        usec_t t = 100 * USEC_PER_MSEC;
×
2771
        const char *e;
×
2772
        int r;
×
2773

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

2777
        e = getenv("SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC");
×
2778
        if (!e)
×
2779
                return t;
×
2780

2781
        r = parse_sec(e, &t);
×
2782
        if (r < 0)
×
2783
                log_debug_errno(r,
×
2784
                                "Failed to parse timeout specified in $SYSTEMD_DISSECT_VERITY_TIMEOUT_SEC, "
2785
                                "using the default timeout (%s).",
2786
                                FORMAT_TIMESPAN(t, USEC_PER_MSEC));
2787

2788
        return t;
×
2789
}
2790

2791
static int verity_partition(
48✔
2792
                PartitionDesignator designator,
2793
                DissectedPartition *m,
2794
                DissectedPartition *v,
2795
                const VeritySettings *verity,
2796
                DissectImageFlags flags,
2797
                DecryptedImage *d) {
2798

2799
        _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL;
×
2800
        _cleanup_free_ char *node = NULL, *name = NULL;
48✔
2801
        _cleanup_close_ int mount_node_fd = -EBADF;
48✔
2802
        int r;
48✔
2803

2804
        assert(m);
48✔
2805
        assert(v || (verity && verity->data_path));
48✔
2806

2807
        if (!verity || !verity->root_hash)
48✔
2808
                return 0;
2809
        if (!((verity->designator < 0 && designator == PARTITION_ROOT) ||
48✔
2810
              (verity->designator == designator)))
2811
                return 0;
2812

2813
        if (!m->found || !m->node || !m->fstype)
48✔
2814
                return 0;
2815
        if (!verity->data_path) {
48✔
2816
                if (!v->found || !v->node || !v->fstype)
16✔
2817
                        return 0;
2818

2819
                if (!streq(v->fstype, "DM_verity_hash"))
16✔
2820
                        return 0;
2821
        }
2822

2823
        r = dlopen_cryptsetup();
48✔
2824
        if (r < 0)
48✔
2825
                return r;
2826

2827
        if (FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)) {
48✔
2828
                /* Use the roothash, which is unique per volume, as the device node name, so that it can be reused */
2829
                _cleanup_free_ char *root_hash_encoded = NULL;
43✔
2830

2831
                root_hash_encoded = hexmem(verity->root_hash, verity->root_hash_size);
43✔
2832
                if (!root_hash_encoded)
43✔
2833
                        return -ENOMEM;
×
2834

2835
                r = make_dm_name_and_node(root_hash_encoded, "-verity", &name, &node);
43✔
2836
        } else
2837
                r = make_dm_name_and_node(m->node, "-verity", &name, &node);
5✔
2838
        if (r < 0)
48✔
2839
                return r;
2840

2841
        r = sym_crypt_init(&cd, verity->data_path ?: v->node);
48✔
2842
        if (r < 0)
48✔
2843
                return r;
2844

2845
        cryptsetup_enable_logging(cd);
48✔
2846

2847
        r = sym_crypt_load(cd, CRYPT_VERITY, NULL);
48✔
2848
        if (r < 0)
48✔
2849
                return r;
2850

2851
        r = sym_crypt_set_data_device(cd, m->node);
48✔
2852
        if (r < 0)
48✔
2853
                return r;
2854

2855
        if (!GREEDY_REALLOC0(d->decrypted, d->n_decrypted + 1))
48✔
2856
                return -ENOMEM;
2857

2858
        /* If activating fails because the device already exists, check the metadata and reuse it if it matches.
2859
         * In case of ENODEV/ENOENT, which can happen if another process is activating at the exact same time,
2860
         * retry a few times before giving up. */
2861
        for (unsigned i = 0; i < N_DEVICE_NODE_LIST_ATTEMPTS; i++) {
48✔
2862
                _cleanup_(dm_deferred_remove_cleanp) char *restore_deferred_remove = NULL;
48✔
2863
                _cleanup_(sym_crypt_freep) struct crypt_device *existing_cd = NULL;
×
2864
                _cleanup_close_ int fd = -EBADF;
48✔
2865

2866
                /* First, check if the device already exists. */
2867
                fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
48✔
2868
                if (fd < 0 && !ERRNO_IS_DEVICE_ABSENT(errno))
48✔
2869
                        return log_debug_errno(errno, "Failed to open verity device %s: %m", node);
×
2870
                if (fd >= 0)
48✔
2871
                        goto check; /* The device already exists. Let's check it. */
5✔
2872

2873
                /* The symlink to the device node does not exist yet. Assume not activated, and let's activate it. */
2874
                r = do_crypt_activate_verity(cd, name, verity, flags);
43✔
2875
                if (r >= 0)
43✔
2876
                        goto try_open; /* The device is activated. Let's open it. */
43✔
2877
                /* libdevmapper can return EINVAL when the device is already in the activation stage.
2878
                 * There's no way to distinguish this situation from a genuine error due to invalid
2879
                 * parameters, so immediately fall back to activating the device with a unique name.
2880
                 * Improvements in libcrypsetup can ensure this never happens:
2881
                 * https://gitlab.com/cryptsetup/cryptsetup/-/merge_requests/96 */
2882
                if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
×
2883
                        break;
2884
                /* Volume is being opened but not ready, crypt_init_by_name would fail, try to open again if
2885
                 * sharing is enabled. */
2886
                if (r == -ENODEV && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
×
2887
                        goto try_again;
×
2888
                if (!IN_SET(r,
×
2889
                            -EEXIST, /* Volume has already been opened and ready to be used. */
2890
                            -EBUSY   /* Volume is being opened but not ready, crypt_init_by_name() can fetch details. */))
2891
                        return log_debug_errno(r, "Failed to activate verity device %s: %m", node);
×
2892

2893
        check:
5✔
2894
                /* To avoid races, disable automatic removal on umount while setting up the new device. Restore it on failure. */
2895
                r = dm_deferred_remove_cancel(name);
5✔
2896
                /* -EBUSY and -ENXIO: the device has already been removed or being removed. We cannot
2897
                 * use the device, try to open again. See target_message() in drivers/md/dm-ioctl.c
2898
                 * and dm_cancel_deferred_remove() in drivers/md/dm.c */
2899
                if (IN_SET(r, -EBUSY, -ENXIO))
5✔
2900
                        goto try_again;
×
2901
                if (r < 0)
5✔
2902
                        return log_debug_errno(r, "Failed to disable automated deferred removal for verity device %s: %m", node);
×
2903

2904
                restore_deferred_remove = strdup(name);
5✔
2905
                if (!restore_deferred_remove)
5✔
2906
                        return log_oom_debug();
×
2907

2908
                r = verity_can_reuse(verity, name, &existing_cd);
5✔
2909
                /* Same as above, -EINVAL can randomly happen when it actually means -EEXIST */
2910
                if (r == -EINVAL && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
5✔
2911
                        break;
2912
                if (IN_SET(r,
×
2913
                           -ENOENT, /* Removed?? */
2914
                           -EBUSY,  /* Volume is being opened but not ready, crypt_init_by_name() can fetch details. */
2915
                           -ENODEV  /* Volume is being opened but not ready, crypt_init_by_name() would fail, try to open again. */ ))
2916
                        goto try_again;
×
2917
                if (r < 0)
×
2918
                        return log_debug_errno(r, "Failed to check if existing verity device %s can be reused: %m", node);
×
2919

2920
                if (fd < 0) {
×
2921
                        /* devmapper might say that the device exists, but the devlink might not yet have been
2922
                         * created. Check and wait for the udev event in that case. */
2923
                        r = device_wait_for_devlink(node, "block", verity_timeout(), NULL);
×
2924
                        /* Fallback to activation with a unique device if it's taking too long */
2925
                        if (r == -ETIMEDOUT && FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE))
×
2926
                                break;
2927
                        if (r < 0)
×
2928
                                return log_debug_errno(r, "Failed to wait device node symlink %s: %m", node);
×
2929
                }
2930

2931
        try_open:
×
2932
                if (fd < 0) {
43✔
2933
                        /* Now, the device is activated and devlink is created. Let's open it. */
2934
                        fd = open(node, O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY);
43✔
2935
                        if (fd < 0) {
43✔
2936
                                if (!ERRNO_IS_DEVICE_ABSENT(errno))
×
2937
                                        return log_debug_errno(errno, "Failed to open verity device %s: %m", node);
×
2938

2939
                                /* The device has already been removed?? */
2940
                                goto try_again;
×
2941
                        }
2942
                }
2943

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

2947
                mount_node_fd = TAKE_FD(fd);
43✔
2948
                if (existing_cd)
43✔
2949
                        crypt_free_and_replace(cd, existing_cd);
×
2950

2951
                goto success;
43✔
2952

2953
        try_again:
×
2954
                /* Device is being removed by another process. Let's wait for a while. */
2955
                (void) usleep_safe(2 * USEC_PER_MSEC);
×
2956
        }
2957

2958
        /* All trials failed or a conflicting verity device exists. Let's try to activate with a unique name. */
2959
        if (FLAGS_SET(flags, DISSECT_IMAGE_VERITY_SHARE)) {
5✔
2960
                /* Before trying to activate with unique name, we need to free crypt_device object.
2961
                 * Otherwise, we get error from libcryptsetup like the following:
2962
                 * ------
2963
                 * systemd[1234]: Cannot use device /dev/loop5 which is in use (already mapped or mounted).
2964
                 * ------
2965
                 */
2966
                sym_crypt_free(cd);
5✔
2967
                cd = NULL;
5✔
2968
                return verity_partition(designator, m, v, verity, flags & ~DISSECT_IMAGE_VERITY_SHARE, d);
5✔
2969
        }
2970

2971
        return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "All attempts to activate verity device %s failed.", name);
48✔
2972

2973
success:
43✔
2974
        d->decrypted[d->n_decrypted++] = (DecryptedPartition) {
43✔
2975
                .name = TAKE_PTR(name),
43✔
2976
                .device = TAKE_PTR(cd),
43✔
2977
        };
2978

2979
        m->decrypted_node = TAKE_PTR(node);
43✔
2980
        close_and_replace(m->mount_node_fd, mount_node_fd);
43✔
2981

2982
        return 0;
43✔
2983
}
2984
#endif
2985

2986
int dissected_image_decrypt(
140✔
2987
                DissectedImage *m,
2988
                const char *passphrase,
2989
                const VeritySettings *verity,
2990
                DissectImageFlags flags) {
2991

2992
#if HAVE_LIBCRYPTSETUP
2993
        _cleanup_(decrypted_image_unrefp) DecryptedImage *d = NULL;
140✔
2994
        int r;
140✔
2995
#endif
2996

2997
        assert(m);
140✔
2998
        assert(!verity || verity->root_hash || verity->root_hash_size == 0);
140✔
2999

3000
        /* Returns:
3001
         *
3002
         *      = 0           → There was nothing to decrypt
3003
         *      > 0           → Decrypted successfully
3004
         *      -ENOKEY       → There's something to decrypt but no key was supplied
3005
         *      -EKEYREJECTED → Passed key was not correct
3006
         *      -EBUSY        → Generic Verity error (kernel is not very explanatory)
3007
         */
3008

3009
        if (verity && verity->root_hash && verity->root_hash_size < sizeof(sd_id128_t))
140✔
3010
                return -EINVAL;
3011

3012
        if (!m->encrypted && !m->verity_ready)
140✔
3013
                return 0;
3014

3015
#if HAVE_LIBCRYPTSETUP
3016
        r = decrypted_image_new(&d);
43✔
3017
        if (r < 0)
43✔
3018
                return r;
3019

3020
        for (PartitionDesignator i = 0; i < _PARTITION_DESIGNATOR_MAX; i++) {
602✔
3021
                DissectedPartition *p = m->partitions + i;
559✔
3022
                PartitionDesignator k;
559✔
3023

3024
                if (!p->found)
559✔
3025
                        continue;
484✔
3026

3027
                r = decrypt_partition(p, passphrase, flags, d);
75✔
3028
                if (r < 0)
75✔
3029
                        return r;
3030

3031
                k = partition_verity_of(i);
75✔
3032
                if (k >= 0) {
75✔
3033
                        flags |= getenv_bool("SYSTEMD_VERITY_SHARING") != 0 ? DISSECT_IMAGE_VERITY_SHARE : 0;
43✔
3034

3035
                        r = verity_partition(i, p, m->partitions + k, verity, flags, d);
43✔
3036
                        if (r < 0)
43✔
3037
                                return r;
3038
                }
3039

3040
                if (!p->decrypted_fstype && p->mount_node_fd >= 0 && p->decrypted_node) {
75✔
3041
                        r = probe_filesystem_full(p->mount_node_fd, p->decrypted_node, 0, UINT64_MAX, &p->decrypted_fstype);
43✔
3042
                        if (r < 0 && r != -EUCLEAN)
43✔
3043
                                return r;
3044
                }
3045
        }
3046

3047
        m->decrypted_image = TAKE_PTR(d);
43✔
3048

3049
        return 1;
43✔
3050
#else
3051
        return -EOPNOTSUPP;
3052
#endif
3053
}
3054

3055
int dissected_image_decrypt_interactively(
73✔
3056
                DissectedImage *m,
3057
                const char *passphrase,
3058
                const VeritySettings *verity,
3059
                DissectImageFlags flags) {
3060

3061
        _cleanup_strv_free_erase_ char **z = NULL;
73✔
3062
        int n = 3, r;
73✔
3063

3064
        if (passphrase)
73✔
3065
                n--;
×
3066

3067
        for (;;) {
73✔
3068
                r = dissected_image_decrypt(m, passphrase, verity, flags);
146✔
3069
                if (r >= 0)
73✔
3070
                        return r;
3071
                if (r == -EKEYREJECTED)
×
3072
                        log_error_errno(r, "Incorrect passphrase, try again!");
×
3073
                else if (r != -ENOKEY)
×
3074
                        return log_error_errno(r, "Failed to decrypt image: %m");
×
3075

3076
                if (--n < 0)
×
3077
                        return log_error_errno(SYNTHETIC_ERRNO(EKEYREJECTED),
×
3078
                                               "Too many retries.");
3079

3080
                z = strv_free_erase(z);
×
3081

3082
                static const AskPasswordRequest req = {
×
3083
                        .tty_fd = -EBADF,
3084
                        .message = "Please enter image passphrase:",
3085
                        .id = "dissect",
3086
                        .keyring = "dissect",
3087
                        .credential = "dissect.passphrase",
3088
                        .until = USEC_INFINITY,
3089
                        .hup_fd = -EBADF,
3090
                };
3091

3092
                r = ask_password_auto(&req, /* flags= */ 0, &z);
×
3093
                if (r < 0)
×
3094
                        return log_error_errno(r, "Failed to query for passphrase: %m");
×
3095

3096
                assert(!strv_isempty(z));
73✔
3097
                passphrase = z[0];
3098
        }
3099
}
3100

3101
static int decrypted_image_relinquish(DecryptedImage *d) {
41✔
3102
        assert(d);
41✔
3103

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

3107
#if HAVE_LIBCRYPTSETUP
3108
        int r;
3109

3110
        for (size_t i = 0; i < d->n_decrypted; i++) {
82✔
3111
                DecryptedPartition *p = d->decrypted + i;
41✔
3112

3113
                if (p->relinquished)
41✔
3114
                        continue;
×
3115

3116
                r = sym_crypt_deactivate_by_name(NULL, p->name, CRYPT_DEACTIVATE_DEFERRED);
41✔
3117
                if (r < 0)
41✔
3118
                        return log_debug_errno(r, "Failed to mark %s for auto-removal: %m", p->name);
×
3119

3120
                p->relinquished = true;
41✔
3121
        }
3122
#endif
3123

3124
        return 0;
3125
}
3126

3127
int dissected_image_relinquish(DissectedImage *m) {
139✔
3128
        int r;
139✔
3129

3130
        assert(m);
139✔
3131

3132
        if (m->decrypted_image) {
139✔
3133
                r = decrypted_image_relinquish(m->decrypted_image);
41✔
3134
                if (r < 0)
41✔
3135
                        return r;
3136
        }
3137

3138
        if (m->loop)
139✔
3139
                loop_device_relinquish(m->loop);
138✔
3140

3141
        return 0;
3142
}
3143

3144
static char *build_auxiliary_path(const char *image, const char *suffix) {
611✔
3145
        const char *e;
611✔
3146
        char *n;
611✔
3147

3148
        assert(image);
611✔
3149
        assert(suffix);
611✔
3150

3151
        e = endswith(image, ".raw");
611✔
3152
        if (!e)
611✔
3153
                return strjoin(e, suffix);
233✔
3154

3155
        n = new(char, e - image + strlen(suffix) + 1);
378✔
3156
        if (!n)
378✔
3157
                return NULL;
3158

3159
        strcpy(mempcpy(n, image, e - image), suffix);
378✔
3160
        return n;
378✔
3161
}
3162

3163
void verity_settings_done(VeritySettings *v) {
47,667✔
3164
        assert(v);
47,667✔
3165

3166
        v->root_hash = mfree(v->root_hash);
47,667✔
3167
        v->root_hash_size = 0;
47,667✔
3168

3169
        v->root_hash_sig = mfree(v->root_hash_sig);
47,667✔
3170
        v->root_hash_sig_size = 0;
47,667✔
3171

3172
        v->data_path = mfree(v->data_path);
47,667✔
3173
}
47,667✔
3174

3175
VeritySettings* verity_settings_free(VeritySettings *v) {
2✔
3176
        if (!v)
2✔
3177
                return NULL;
3178

3179
        verity_settings_done(v);
2✔
3180
        return mfree(v);
2✔
3181
}
3182

3183
void verity_settings_hash_func(const VeritySettings *s, struct siphash *state) {
4✔
3184
        assert(s);
4✔
3185

3186
        siphash24_compress_typesafe(s->root_hash_size, state);
4✔
3187
        siphash24_compress(s->root_hash, s->root_hash_size, state);
4✔
3188
}
4✔
3189

3190
int verity_settings_compare_func(const VeritySettings *x, const VeritySettings *y) {
2✔
3191
        int r;
2✔
3192

3193
        r = CMP(x->root_hash_size, y->root_hash_size);
2✔
3194
        if (r != 0)
2✔
UNCOV
3195
                return r;
×
3196

3197
        return memcmp(x->root_hash, y->root_hash, x->root_hash_size);
2✔
3198
}
3199

3200
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(verity_settings_hash_ops, VeritySettings, verity_settings_hash_func, verity_settings_compare_func, VeritySettings, verity_settings_free);
2✔
3201

3202
int verity_settings_load(
207✔
3203
                VeritySettings *verity,
3204
                const char *image,
3205
                const char *root_hash_path,
3206
                const char *root_hash_sig_path) {
3207

UNCOV
3208
        _cleanup_free_ void *root_hash = NULL, *root_hash_sig = NULL;
×
3209
        size_t root_hash_size = 0, root_hash_sig_size = 0;
207✔
3210
        _cleanup_free_ char *verity_data_path = NULL;
207✔
3211
        PartitionDesignator designator;
207✔
3212
        int r;
207✔
3213

3214
        assert(verity);
207✔
3215
        assert(image);
207✔
3216
        assert(verity->designator < 0 || IN_SET(verity->designator, PARTITION_ROOT, PARTITION_USR));
207✔
3217

3218
        /* If we are asked to load the root hash for a device node, exit early */
3219
        if (is_device_path(image))
207✔
3220
                return 0;
3221

3222
        r = secure_getenv_bool("SYSTEMD_DISSECT_VERITY_SIDECAR");
207✔
3223
        if (r < 0 && r != -ENXIO)
207✔
UNCOV
3224
                log_debug_errno(r, "Failed to parse $SYSTEMD_DISSECT_VERITY_SIDECAR, ignoring: %m");
×
3225
        if (r == 0)
207✔
3226
                return 0;
3227

3228
        designator = verity->designator;
207✔
3229

3230
        /* We only fill in what isn't already filled in */
3231

3232
        if (!verity->root_hash) {
207✔
3233
                _cleanup_free_ char *text = NULL;
181✔
3234

3235
                if (root_hash_path) {
181✔
3236
                        /* If explicitly specified it takes precedence */
UNCOV
3237
                        r = read_one_line_file(root_hash_path, &text);
×
UNCOV
3238
                        if (r < 0)
×
3239
                                return r;
3240

UNCOV
3241
                        if (designator < 0)
×
UNCOV
3242
                                designator = PARTITION_ROOT;
×
3243
                } else {
3244
                        /* Otherwise look for xattr and separate file, and first for the data for root and if
3245
                         * that doesn't exist for /usr */
3246

3247
                        if (designator < 0 || designator == PARTITION_ROOT) {
181✔
3248
                                r = getxattr_malloc(image, "user.verity.roothash", &text);
181✔
3249
                                if (r < 0) {
181✔
3250
                                        _cleanup_free_ char *p = NULL;
181✔
3251

3252
                                        if (r != -ENOENT && !ERRNO_IS_XATTR_ABSENT(r))
181✔
3253
                                                return r;
3254

3255
                                        p = build_auxiliary_path(image, ".roothash");
181✔
3256
                                        if (!p)
181✔
3257
                                                return -ENOMEM;
3258

3259
                                        r = read_one_line_file(p, &text);
181✔
3260
                                        if (r < 0 && r != -ENOENT)
181✔
3261
                                                return r;
3262
                                }
3263

3264
                                if (text)
181✔
3265
                                        designator = PARTITION_ROOT;
37✔
3266
                        }
3267

3268
                        if (!text && (designator < 0 || designator == PARTITION_USR)) {
181✔
3269
                                /* So in the "roothash" xattr/file name above the "root" of course primarily
3270
                                 * refers to the root of the Verity Merkle tree. But coincidentally it also
3271
                                 * is the hash for the *root* file system, i.e. the "root" neatly refers to
3272
                                 * two distinct concepts called "root". Taking benefit of this happy
3273
                                 * coincidence we call the file with the root hash for the /usr/ file system
3274
                                 * `usrhash`, because `usrroothash` or `rootusrhash` would just be too
3275
                                 * confusing. We thus drop the reference to the root of the Merkle tree, and
3276
                                 * just indicate which file system it's about. */
3277
                                r = getxattr_malloc(image, "user.verity.usrhash", &text);
144✔
3278
                                if (r < 0) {
144✔
3279
                                        _cleanup_free_ char *p = NULL;
144✔
3280

3281
                                        if (r != -ENOENT && !ERRNO_IS_XATTR_ABSENT(r))
144✔
3282
                                                return r;
3283

3284
                                        p = build_auxiliary_path(image, ".usrhash");
144✔
3285
                                        if (!p)
144✔
3286
                                                return -ENOMEM;
3287

3288
                                        r = read_one_line_file(p, &text);
144✔
3289
                                        if (r < 0 && r != -ENOENT)
144✔
3290
                                                return r;
3291
                                }
3292

3293
                                if (text)
144✔
UNCOV
3294
                                        designator = PARTITION_USR;
×
3295
                        }
3296
                }
3297

3298
                if (text) {
181✔
3299
                        r = unhexmem(text, &root_hash, &root_hash_size);
37✔
3300
                        if (r < 0)
37✔
3301
                                return r;
3302
                        if (root_hash_size < sizeof(sd_id128_t))
37✔
3303
                                return -EINVAL;
3304
                }
3305
        }
3306

3307
        if ((root_hash || verity->root_hash) && !verity->root_hash_sig) {
207✔
3308
                if (root_hash_sig_path) {
63✔
UNCOV
3309
                        r = read_full_file(root_hash_sig_path, (char**) &root_hash_sig, &root_hash_sig_size);
×
UNCOV
3310
                        if (r < 0 && r != -ENOENT)
×
3311
                                return r;
3312

UNCOV
3313
                        if (designator < 0)
×
UNCOV
3314
                                designator = PARTITION_ROOT;
×
3315
                } else {
3316
                        if (designator < 0 || designator == PARTITION_ROOT) {
63✔
3317
                                _cleanup_free_ char *p = NULL;
63✔
3318

3319
                                /* Follow naming convention recommended by the relevant RFC:
3320
                                 * https://tools.ietf.org/html/rfc5751#section-3.2.1 */
3321
                                p = build_auxiliary_path(image, ".roothash.p7s");
63✔
3322
                                if (!p)
63✔
3323
                                        return -ENOMEM;
3324

3325
                                r = read_full_file(p, (char**) &root_hash_sig, &root_hash_sig_size);
63✔
3326
                                if (r < 0 && r != -ENOENT)
63✔
3327
                                        return r;
3328
                                if (r >= 0)
63✔
3329
                                        designator = PARTITION_ROOT;
31✔
3330
                        }
3331

3332
                        if (!root_hash_sig && (designator < 0 || designator == PARTITION_USR)) {
63✔
3333
                                _cleanup_free_ char *p = NULL;
19✔
3334

3335
                                p = build_auxiliary_path(image, ".usrhash.p7s");
19✔
3336
                                if (!p)
19✔
3337
                                        return -ENOMEM;
3338

3339
                                r = read_full_file(p, (char**) &root_hash_sig, &root_hash_sig_size);
19✔
3340
                                if (r < 0 && r != -ENOENT)
19✔
3341
                                        return r;
3342
                                if (r >= 0)
19✔
UNCOV
3343
                                        designator = PARTITION_USR;
×
3344
                        }
3345
                }
3346

3347
                if (root_hash_sig && root_hash_sig_size == 0) /* refuse empty size signatures */
63✔
3348
                        return -EINVAL;
3349
        }
3350

3351
        if (!verity->data_path) {
207✔
3352
                _cleanup_free_ char *p = NULL;
204✔
3353

3354
                p = build_auxiliary_path(image, ".verity");
204✔
3355
                if (!p)
204✔
3356
                        return -ENOMEM;
3357

3358
                if (access(p, F_OK) < 0) {
204✔
3359
                        if (errno != ENOENT)
167✔
UNCOV
3360
                                return -errno;
×
3361
                } else
3362
                        verity_data_path = TAKE_PTR(p);
3363
        }
3364

3365
        if (root_hash) {
207✔
3366
                verity->root_hash = TAKE_PTR(root_hash);
37✔
3367
                verity->root_hash_size = root_hash_size;
37✔
3368
        }
3369

3370
        if (root_hash_sig) {
207✔
3371
                verity->root_hash_sig = TAKE_PTR(root_hash_sig);
31✔
3372
                verity->root_hash_sig_size = root_hash_sig_size;
31✔
3373
        }
3374

3375
        if (verity_data_path)
207✔
3376
                verity->data_path = TAKE_PTR(verity_data_path);
37✔
3377

3378
        if (verity->designator < 0)
207✔
3379
                verity->designator = designator;
203✔
3380

3381
        return 1;
3382
}
3383

3384
int dissected_image_load_verity_sig_partition(
171✔
3385
                DissectedImage *m,
3386
                int fd,
3387
                VeritySettings *verity) {
3388

3389
        _cleanup_free_ void *root_hash = NULL, *root_hash_sig = NULL;
171✔
3390
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
171✔
3391
        size_t root_hash_size, root_hash_sig_size;
171✔
3392
        _cleanup_free_ char *buf = NULL;
171✔
3393
        PartitionDesignator d;
171✔
3394
        DissectedPartition *p;
171✔
3395
        sd_json_variant *rh, *sig;
171✔
3396
        ssize_t n;
171✔
3397
        char *e;
171✔
3398
        int r;
171✔
3399

3400
        assert(m);
171✔
3401
        assert(fd >= 0);
171✔
3402
        assert(verity);
171✔
3403

3404
        if (verity->root_hash && verity->root_hash_sig) /* Already loaded? */
171✔
3405
                return 0;
3406

3407
        r = secure_getenv_bool("SYSTEMD_DISSECT_VERITY_EMBEDDED");
143✔
3408
        if (r < 0 && r != -ENXIO)
143✔
UNCOV
3409
                log_debug_errno(r, "Failed to parse $SYSTEMD_DISSECT_VERITY_EMBEDDED, ignoring: %m");
×
3410
        if (r == 0)
143✔
3411
                return 0;
3412

3413
        d = partition_verity_sig_of(verity->designator < 0 ? PARTITION_ROOT : verity->designator);
143✔
3414
        assert(d >= 0);
143✔
3415

3416
        p = m->partitions + d;
143✔
3417
        if (!p->found)
143✔
3418
                return 0;
3419
        if (p->offset == UINT64_MAX || p->size == UINT64_MAX)
29✔
3420
                return -EINVAL;
3421

3422
        if (p->size > 4*1024*1024) /* Signature data cannot possible be larger than 4M, refuse that */
29✔
UNCOV
3423
                return log_debug_errno(SYNTHETIC_ERRNO(EFBIG), "Verity signature partition is larger than 4M, refusing.");
×
3424

3425
        buf = new(char, p->size+1);
29✔
3426
        if (!buf)
29✔
3427
                return -ENOMEM;
3428

3429
        n = pread(fd, buf, p->size, p->offset);
29✔
3430
        if (n < 0)
29✔
3431
                return -ENOMEM;
3432
        if ((uint64_t) n != p->size)
29✔
3433
                return -EIO;
3434

3435
        e = memchr(buf, 0, p->size);
29✔
3436
        if (e) {
29✔
3437
                /* If we found a NUL byte then the rest of the data must be NUL too */
3438
                if (!memeqzero(e, p->size - (e - buf)))
29✔
UNCOV
3439
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature data contains embedded NUL byte.");
×
3440
        } else
3441
                buf[p->size] = 0;
×
3442

3443
        r = sd_json_parse(buf, 0, &v, NULL, NULL);
29✔
3444
        if (r < 0)
29✔
3445
                return log_debug_errno(r, "Failed to parse signature JSON data: %m");
×
3446

3447
        rh = sd_json_variant_by_key(v, "rootHash");
29✔
3448
        if (!rh)
29✔
UNCOV
3449
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature JSON object lacks 'rootHash' field.");
×
3450

3451
        r = sd_json_variant_unhex(rh, &root_hash, &root_hash_size);
29✔
3452
        if (r < 0)
29✔
UNCOV
3453
                return log_debug_errno(r, "Failed to parse root hash field: %m");
×
3454

3455
        /* Check if specified root hash matches if it is specified */
3456
        if (verity->root_hash &&
48✔
3457
            memcmp_nn(verity->root_hash, verity->root_hash_size, root_hash, root_hash_size) != 0) {
19✔
UNCOV
3458
                _cleanup_free_ char *a = NULL, *b = NULL;
×
3459

UNCOV
3460
                a = hexmem(root_hash, root_hash_size);
×
UNCOV
3461
                b = hexmem(verity->root_hash, verity->root_hash_size);
×
3462

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

3466
        sig = sd_json_variant_by_key(v, "signature");
29✔
3467
        if (!sig)
29✔
UNCOV
3468
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature JSON object lacks 'signature' field.");
×
3469

3470
        r = sd_json_variant_unbase64(sig, &root_hash_sig, &root_hash_sig_size);
29✔
3471
        if (r < 0)
29✔
UNCOV
3472
                return log_debug_errno(r, "Failed to parse signature field: %m");
×
3473

3474
        free_and_replace(verity->root_hash, root_hash);
29✔
3475
        verity->root_hash_size = root_hash_size;
29✔
3476

3477
        free_and_replace(verity->root_hash_sig, root_hash_sig);
29✔
3478
        verity->root_hash_sig_size = root_hash_sig_size;
29✔
3479

3480
        return 1;
29✔
3481
}
3482

3483
int dissected_image_acquire_metadata(
54✔
3484
                DissectedImage *m,
3485
                int userns_fd,
3486
                DissectImageFlags extra_flags) {
3487

3488
        enum {
54✔
3489
                META_HOSTNAME,
3490
                META_MACHINE_ID,
3491
                META_MACHINE_INFO,
3492
                META_OS_RELEASE,
3493
                META_INITRD_RELEASE,
3494
                META_SYSEXT_RELEASE,
3495
                META_CONFEXT_RELEASE,
3496
                META_HAS_INIT_SYSTEM,
3497
                _META_MAX,
3498
        };
3499

3500
        static const char *const paths[_META_MAX] = {
54✔
3501
                [META_HOSTNAME]          = "/etc/hostname\0",
3502
                [META_MACHINE_ID]        = "/etc/machine-id\0",
3503
                [META_MACHINE_INFO]      = "/etc/machine-info\0",
3504
                [META_OS_RELEASE]        = "/etc/os-release\0"
3505
                                           "/usr/lib/os-release\0",
3506
                [META_INITRD_RELEASE]    = "/etc/initrd-release\0"
3507
                                           "/usr/lib/initrd-release\0",
3508
                [META_SYSEXT_RELEASE]    = "sysext-release\0",       /* String used only for logging. */
3509
                [META_CONFEXT_RELEASE]   = "confext-release\0",      /* ditto */
3510
                [META_HAS_INIT_SYSTEM]   = "has-init-system\0",      /* ditto */
3511
        };
3512

3513
        _cleanup_strv_free_ char **machine_info = NULL, **os_release = NULL, **initrd_release = NULL, **sysext_release = NULL, **confext_release = NULL;
54✔
3514
        _cleanup_free_ char *hostname = NULL, *t = NULL;
54✔
3515
        _cleanup_close_pair_ int error_pipe[2] = EBADF_PAIR;
54✔
3516
        _cleanup_(sigkill_waitp) pid_t child = 0;
54✔
3517
        sd_id128_t machine_id = SD_ID128_NULL;
54✔
3518
        unsigned n_meta_initialized = 0;
54✔
3519
        int fds[2 * _META_MAX], r, v;
54✔
3520
        int has_init_system = -1;
54✔
3521
        ssize_t n;
54✔
3522

3523
        BLOCK_SIGNALS(SIGCHLD);
54✔
3524

3525
        assert(m);
54✔
3526

3527
        for (; n_meta_initialized < _META_MAX; n_meta_initialized++) {
486✔
3528
                assert(paths[n_meta_initialized]);
432✔
3529

3530
                if (pipe2(fds + 2*n_meta_initialized, O_CLOEXEC) < 0) {
432✔
3531
                        r = -errno;
×
UNCOV
3532
                        goto finish;
×
3533
                }
3534
        }
3535

3536
        r = get_common_dissect_directory(&t);
54✔
3537
        if (r < 0)
54✔
UNCOV
3538
                goto finish;
×
3539

3540
        if (pipe2(error_pipe, O_CLOEXEC) < 0) {
54✔
UNCOV
3541
                r = -errno;
×
UNCOV
3542
                goto finish;
×
3543
        }
3544

3545
        r = safe_fork("(sd-dissect)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM, &child);
54✔
3546
        if (r < 0)
97✔
UNCOV
3547
                goto finish;
×
3548
        if (r == 0) {
97✔
3549
                /* Child */
3550
                error_pipe[0] = safe_close(error_pipe[0]);
43✔
3551

3552
                if (userns_fd < 0)
43✔
3553
                        r = detach_mount_namespace_harder(0, 0);
42✔
3554
                else
3555
                        r = detach_mount_namespace_userns(userns_fd);
1✔
3556
                if (r < 0) {
43✔
UNCOV
3557
                        log_debug_errno(r, "Failed to detach mount namespace: %m");
×
UNCOV
3558
                        report_errno_and_exit(error_pipe[1], r);
×
3559
                }
3560

3561
                r = dissected_image_mount(
86✔
3562
                                m,
3563
                                t,
3564
                                /* uid_shift= */ UID_INVALID,
3565
                                /* uid_range= */ UID_INVALID,
3566
                                /* userns_fd= */ -EBADF,
3567
                                extra_flags |
3568
                                DISSECT_IMAGE_READ_ONLY |
3569
                                DISSECT_IMAGE_MOUNT_ROOT_ONLY |
43✔
3570
                                DISSECT_IMAGE_USR_NO_ROOT);
3571
                if (r < 0) {
43✔
UNCOV
3572
                        log_debug_errno(r, "Failed to mount dissected image: %m");
×
UNCOV
3573
                        report_errno_and_exit(error_pipe[1], r);
×
3574
                }
3575

3576
                for (unsigned k = 0; k < _META_MAX; k++) {
387✔
3577
                        _cleanup_close_ int fd = -ENOENT;
344✔
3578

3579
                        assert(paths[k]);
344✔
3580

3581
                        fds[2*k] = safe_close(fds[2*k]);
344✔
3582

3583
                        switch (k) {
344✔
3584

3585
                        case META_SYSEXT_RELEASE:
43✔
3586
                                if (!m->image_name)
43✔
UNCOV
3587
                                        goto next;
×
3588

3589
                                /* As per the os-release spec, if the image is an extension it will have a
3590
                                 * file named after the image name in extension-release.d/ - we use the image
3591
                                 * name and try to resolve it with the extension-release helpers, as
3592
                                 * sometimes the image names are mangled on deployment and do not match
3593
                                 * anymore.  Unlike other paths this is not fixed, and the image name can be
3594
                                 * mangled on deployment, so by calling into the helper we allow a fallback
3595
                                 * that matches on the first extension-release file found in the directory,
3596
                                 * if one named after the image cannot be found first. */
3597
                                r = open_extension_release(
43✔
3598
                                                t,
3599
                                                IMAGE_SYSEXT,
3600
                                                m->image_name,
3601
                                                /* relax_extension_release_check= */ false,
3602
                                                /* ret_path= */ NULL,
3603
                                                &fd);
3604
                                if (r < 0)
43✔
3605
                                        fd = r;
30✔
3606
                                break;
3607

3608
                        case META_CONFEXT_RELEASE:
43✔
3609
                                if (!m->image_name)
43✔
UNCOV
3610
                                        goto next;
×
3611

3612
                                /* As above */
3613
                                r = open_extension_release(
43✔
3614
                                                t,
3615
                                                IMAGE_CONFEXT,
3616
                                                m->image_name,
3617
                                                /* relax_extension_release_check= */ false,
3618
                                                /* ret_path= */ NULL,
3619
                                                &fd);
3620
                                if (r < 0)
43✔
3621
                                        fd = r;
38✔
3622

3623
                                break;
3624

3625
                        case META_HAS_INIT_SYSTEM: {
43✔
3626
                                bool found = false;
43✔
3627

3628
                                FOREACH_STRING(init,
157✔
3629
                                               "/usr/lib/systemd/systemd",  /* systemd on /usr/ merged system */
3630
                                               "/lib/systemd/systemd",      /* systemd on /usr/ non-merged systems */
3631
                                               "/sbin/init") {              /* traditional path the Linux kernel invokes */
3632

3633
                                        r = chase(init, t, CHASE_PREFIX_ROOT, NULL, NULL);
129✔
3634
                                        if (r < 0) {
129✔
3635
                                                if (r != -ENOENT)
114✔
3636
                                                        log_debug_errno(r, "Failed to resolve %s, ignoring: %m", init);
114✔
3637
                                        } else {
3638
                                                found = true;
15✔
3639
                                                break;
15✔
3640
                                        }
3641
                                }
3642

3643
                                r = loop_write(fds[2*k+1], &found, sizeof(found));
43✔
3644
                                if (r < 0)
43✔
UNCOV
3645
                                        report_errno_and_exit(error_pipe[1], r);
×
3646

3647
                                goto next;
43✔
3648
                        }
3649

3650
                        default:
3651
                                NULSTR_FOREACH(p, paths[k]) {
471✔
3652
                                        fd = chase_and_open(p, t, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
286✔
3653
                                        if (fd >= 0)
286✔
3654
                                                break;
3655
                                }
3656
                        }
3657

3658
                        if (fd < 0) {
301✔
3659
                                log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]);
253✔
3660
                                goto next;
253✔
3661
                        }
3662

3663
                        r = copy_bytes(fd, fds[2*k+1], UINT64_MAX, 0);
48✔
3664
                        if (r < 0)
48✔
UNCOV
3665
                                report_errno_and_exit(error_pipe[1], r);
×
3666

3667
                next:
48✔
3668
                        fds[2*k+1] = safe_close(fds[2*k+1]);
344✔
3669
                }
3670

3671
                _exit(EXIT_SUCCESS);
43✔
3672
        }
3673

3674
        error_pipe[1] = safe_close(error_pipe[1]);
54✔
3675

3676
        for (unsigned k = 0; k < _META_MAX; k++) {
486✔
3677
                _cleanup_fclose_ FILE *f = NULL;
432✔
3678

3679
                assert(paths[k]);
432✔
3680

3681
                fds[2*k+1] = safe_close(fds[2*k+1]);
432✔
3682

3683
                f = take_fdopen(&fds[2*k], "r");
432✔
3684
                if (!f) {
432✔
UNCOV
3685
                        r = -errno;
×
3686
                        goto finish;
×
3687
                }
3688

3689
                switch (k) {
432✔
3690

3691
                case META_HOSTNAME:
54✔
3692
                        r = read_etc_hostname_stream(f, &hostname);
54✔
3693
                        if (r < 0)
54✔
3694
                                log_debug_errno(r, "Failed to read /etc/hostname of image: %m");
54✔
3695

3696
                        break;
3697

3698
                case META_MACHINE_ID: {
54✔
3699
                        _cleanup_free_ char *line = NULL;
54✔
3700

3701
                        r = read_line(f, LONG_LINE_MAX, &line);
54✔
3702
                        if (r < 0)
54✔
UNCOV
3703
                                log_debug_errno(r, "Failed to read /etc/machine-id of image: %m");
×
3704
                        else if (r == 33) {
54✔
UNCOV
3705
                                r = sd_id128_from_string(line, &machine_id);
×
UNCOV
3706
                                if (r < 0)
×
UNCOV
3707
                                        log_debug_errno(r, "Image contains invalid /etc/machine-id: %s", line);
×
3708
                        } else if (r == 0)
54✔
3709
                                log_debug("/etc/machine-id file of image is empty.");
39✔
3710
                        else if (streq(line, "uninitialized"))
15✔
3711
                                log_debug("/etc/machine-id file of image is uninitialized (likely aborted first boot).");
15✔
3712
                        else
UNCOV
3713
                                log_debug("/etc/machine-id file of image has unexpected length %i.", r);
×
3714

3715
                        break;
54✔
3716
                }
3717

3718
                case META_MACHINE_INFO:
54✔
3719
                        r = load_env_file_pairs(f, "machine-info", &machine_info);
54✔
3720
                        if (r < 0)
54✔
UNCOV
3721
                                log_debug_errno(r, "Failed to read /etc/machine-info of image: %m");
×
3722

3723
                        break;
3724

3725
                case META_OS_RELEASE:
54✔
3726
                        r = load_env_file_pairs(f, "os-release", &os_release);
54✔
3727
                        if (r < 0)
54✔
UNCOV
3728
                                log_debug_errno(r, "Failed to read OS release file of image: %m");
×
3729

3730
                        break;
3731

3732
                case META_INITRD_RELEASE:
54✔
3733
                        r = load_env_file_pairs(f, "initrd-release", &initrd_release);
54✔
3734
                        if (r < 0)
54✔
UNCOV
3735
                                log_debug_errno(r, "Failed to read initrd release file of image: %m");
×
3736

3737
                        break;
3738

3739
                case META_SYSEXT_RELEASE:
54✔
3740
                        r = load_env_file_pairs(f, "sysext-release", &sysext_release);
54✔
3741
                        if (r < 0)
54✔
UNCOV
3742
                                log_debug_errno(r, "Failed to read sysext release file of image: %m");
×
3743

3744
                        break;
3745

3746
                case META_CONFEXT_RELEASE:
54✔
3747
                        r = load_env_file_pairs(f, "confext-release", &confext_release);
54✔
3748
                        if (r < 0)
54✔
3749
                                log_debug_errno(r, "Failed to read confext release file of image: %m");
432✔
3750

3751
                        break;
3752

3753
                case META_HAS_INIT_SYSTEM: {
54✔
3754
                        bool b = false;
54✔
3755
                        size_t nr;
54✔
3756

3757
                        errno = 0;
54✔
3758
                        nr = fread(&b, 1, sizeof(b), f);
54✔
3759
                        if (nr != sizeof(b))
54✔
3760
                                log_debug_errno(errno_or_else(EIO), "Failed to read has-init-system boolean: %m");
×
3761
                        else
3762
                                has_init_system = b;
54✔
3763

3764
                        break;
54✔
3765
                }}
3766
        }
3767

3768
        r = wait_for_terminate_and_check("(sd-dissect)", child, 0);
54✔
3769
        child = 0;
54✔
3770
        if (r < 0)
54✔
UNCOV
3771
                goto finish;
×
3772

3773
        n = read(error_pipe[0], &v, sizeof(v));
54✔
3774
        if (n < 0) {
54✔
UNCOV
3775
                r = -errno;
×
UNCOV
3776
                goto finish;
×
3777
        }
3778
        if (n == sizeof(v)) {
54✔
UNCOV
3779
                r = v; /* propagate error sent to us from child */
×
UNCOV
3780
                goto finish;
×
3781
        }
3782
        if (n != 0) {
54✔
UNCOV
3783
                r = -EIO;
×
UNCOV
3784
                goto finish;
×
3785
        }
3786
        if (r != EXIT_SUCCESS) {
54✔
UNCOV
3787
                r = -EPROTO;
×
3788
                goto finish;
×
3789
        }
3790

3791
        free_and_replace(m->hostname, hostname);
54✔
3792
        m->machine_id = machine_id;
54✔
3793
        strv_free_and_replace(m->machine_info, machine_info);
54✔
3794
        strv_free_and_replace(m->os_release, os_release);
54✔
3795
        strv_free_and_replace(m->initrd_release, initrd_release);
54✔
3796
        strv_free_and_replace(m->sysext_release, sysext_release);
54✔
3797
        strv_free_and_replace(m->confext_release, confext_release);
54✔
3798
        m->has_init_system = has_init_system;
54✔
3799

3800
finish:
54✔
3801
        for (unsigned k = 0; k < n_meta_initialized; k++)
486✔
3802
                safe_close_pair(fds + 2*k);
432✔
3803

3804
        return r;
108✔
3805
}
3806

3807
Architecture dissected_image_architecture(DissectedImage *img) {
45✔
3808
        assert(img);
45✔
3809

3810
        if (img->partitions[PARTITION_ROOT].found &&
45✔
3811
            img->partitions[PARTITION_ROOT].architecture >= 0)
45✔
3812
                return img->partitions[PARTITION_ROOT].architecture;
3813

3814
        if (img->partitions[PARTITION_USR].found &&
19✔
UNCOV
3815
            img->partitions[PARTITION_USR].architecture >= 0)
×
UNCOV
3816
                return img->partitions[PARTITION_USR].architecture;
×
3817

3818
        return _ARCHITECTURE_INVALID;
3819
}
3820

3821
int dissect_loop_device(
1,844✔
3822
                LoopDevice *loop,
3823
                const VeritySettings *verity,
3824
                const MountOptions *mount_options,
3825
                const ImagePolicy *image_policy,
3826
                DissectImageFlags flags,
3827
                DissectedImage **ret) {
3828

3829
#if HAVE_BLKID
3830
        _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL;
1,844✔
3831
        int r;
1,844✔
3832

3833
        assert(loop);
1,844✔
3834

3835
        r = dissected_image_new(loop->backing_file ?: loop->node, &m);
1,844✔
3836
        if (r < 0)
1,844✔
3837
                return r;
3838

3839
        m->loop = loop_device_ref(loop);
1,844✔
3840
        m->image_size = m->loop->device_size;
1,844✔
3841
        m->sector_size = m->loop->sector_size;
1,844✔
3842

3843
        r = dissect_image(m, loop->fd, loop->node, verity, mount_options, image_policy, flags);
1,844✔
3844
        if (r < 0)
1,844✔
3845
                return r;
3846

3847
        if (ret)
1,792✔
3848
                *ret = TAKE_PTR(m);
1,792✔
3849

3850
        return 0;
3851
#else
3852
        return -EOPNOTSUPP;
3853
#endif
3854
}
3855

3856
int dissect_loop_device_and_warn(
108✔
3857
                LoopDevice *loop,
3858
                const VeritySettings *verity,
3859
                const MountOptions *mount_options,
3860
                const ImagePolicy *image_policy,
3861
                DissectImageFlags flags,
3862
                DissectedImage **ret) {
3863

3864
        assert(loop);
108✔
3865

3866
        return dissect_log_error(
108✔
3867
                        LOG_ERR,
3868
                        dissect_loop_device(loop, verity, mount_options, image_policy, flags, ret),
3869
                        loop->backing_file ?: loop->node,
108✔
3870
                        verity);
3871
}
3872

3873
bool dissected_image_verity_candidate(const DissectedImage *image, PartitionDesignator partition_designator) {
48✔
3874
        assert(image);
48✔
3875

3876
        /* Checks if this partition could theoretically do Verity. For non-partitioned images this only works
3877
         * if there's an external verity file supplied, for which we can consult .has_verity. For partitioned
3878
         * images we only check the partition type.
3879
         *
3880
         * This call is used to decide whether to suppress or show a verity column in tabular output of the
3881
         * image. */
3882

3883
        if (image->single_file_system)
48✔
3884
                return partition_designator == PARTITION_ROOT && image->has_verity;
10✔
3885

3886
        return partition_verity_of(partition_designator) >= 0;
43✔
3887
}
3888

3889
bool dissected_image_verity_ready(const DissectedImage *image, PartitionDesignator partition_designator) {
4✔
3890
        PartitionDesignator k;
4✔
3891

3892
        assert(image);
4✔
3893

3894
        /* Checks if this partition has verity data available that we can activate. For non-partitioned this
3895
         * works for the root partition, for others only if the associated verity partition was found. */
3896

3897
        if (!image->verity_ready)
4✔
3898
                return false;
3899

UNCOV
3900
        if (image->single_file_system)
×
UNCOV
3901
                return partition_designator == PARTITION_ROOT;
×
3902

UNCOV
3903
        k = partition_verity_of(partition_designator);
×
UNCOV
3904
        return k >= 0 && image->partitions[k].found;
×
3905
}
3906

3907
bool dissected_image_verity_sig_ready(const DissectedImage *image, PartitionDesignator partition_designator) {
17✔
3908
        PartitionDesignator k;
17✔
3909

3910
        assert(image);
17✔
3911

3912
        /* Checks if this partition has verity signature data available that we can use. */
3913

3914
        if (!image->verity_sig_ready)
17✔
3915
                return false;
3916

3917
        if (image->single_file_system)
13✔
UNCOV
3918
                return partition_designator == PARTITION_ROOT;
×
3919

3920
        k = partition_verity_sig_of(partition_designator);
13✔
3921
        return k >= 0 && image->partitions[k].found;
13✔
3922
}
3923

3924
MountOptions* mount_options_free_all(MountOptions *options) {
49,887✔
3925
        MountOptions *m;
49,887✔
3926

3927
        while ((m = LIST_POP(mount_options, options))) {
49,893✔
3928
                free(m->options);
6✔
3929
                free(m);
6✔
3930
        }
3931

3932
        return NULL;
49,887✔
3933
}
3934

3935
const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator) {
1,959✔
3936
        LIST_FOREACH(mount_options, m, options)
1,959✔
3937
                if (designator == m->partition_designator && !isempty(m->options))
1,969✔
3938
                        return m->options;
3939

3940
        return NULL;
3941
}
3942

3943
int mount_image_privately_interactively(
28✔
3944
                const char *image,
3945
                const ImagePolicy *image_policy,
3946
                DissectImageFlags flags,
3947
                char **ret_directory,
3948
                int *ret_dir_fd,
3949
                LoopDevice **ret_loop_device) {
3950

3951
        _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
28✔
3952
        _cleanup_(loop_device_unrefp) LoopDevice *d = NULL;
28✔
3953
        _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
×
3954
        _cleanup_free_ char *dir = NULL;
28✔
3955
        int r;
28✔
3956

3957
        /* Mounts an OS image at a temporary place, inside a newly created mount namespace of our own. This
3958
         * is used by tools such as systemd-tmpfiles or systemd-firstboot to operate on some disk image
3959
         * easily. */
3960

3961
        assert(image);
28✔
3962
        assert(ret_loop_device);
28✔
3963

3964
        /* We intend to mount this right-away, hence add the partitions if needed and pin them. */
3965
        flags |= DISSECT_IMAGE_ADD_PARTITION_DEVICES |
28✔
3966
                DISSECT_IMAGE_PIN_PARTITION_DEVICES;
3967

3968
        r = verity_settings_load(&verity, image, NULL, NULL);
28✔
3969
        if (r < 0)
28✔
UNCOV
3970
                return log_error_errno(r, "Failed to load root hash data: %m");
×
3971

3972
        r = loop_device_make_by_path(
56✔
3973
                        image,
3974
                        FLAGS_SET(flags, DISSECT_IMAGE_DEVICE_READ_ONLY) ? O_RDONLY : O_RDWR,
3975
                        /* sector_size= */ UINT32_MAX,
3976
                        FLAGS_SET(flags, DISSECT_IMAGE_NO_PARTITION_TABLE) ? 0 : LO_FLAGS_PARTSCAN,
3977
                        LOCK_SH,
3978
                        &d);
3979
        if (r < 0)
28✔
UNCOV
3980
                return log_error_errno(r, "Failed to set up loopback device for %s: %m", image);
×
3981

3982
        r = dissect_loop_device_and_warn(
28✔
3983
                        d,
3984
                        &verity,
3985
                        /* mount_options= */ NULL,
3986
                        image_policy,
3987
                        flags,
3988
                        &dissected_image);
3989
        if (r < 0)
28✔
3990
                return r;
3991

3992
        r = dissected_image_load_verity_sig_partition(dissected_image, d->fd, &verity);
28✔
3993
        if (r < 0)
28✔
3994
                return r;
3995

3996
        r = dissected_image_decrypt_interactively(dissected_image, NULL, &verity, flags);
28✔
3997
        if (r < 0)
28✔
3998
                return r;
3999

4000
        r = detach_mount_namespace();
28✔
4001
        if (r < 0)
28✔
4002
                return log_error_errno(r, "Failed to detach mount namespace: %m");
×
4003

4004
        r = mkdir_p("/run/systemd/mount-rootfs", 0555);
28✔
4005
        if (r < 0)
28✔
UNCOV
4006
                return log_error_errno(r, "Failed to create mount point: %m");
×
4007

4008
        r = dissected_image_mount_and_warn(
28✔
4009
                        dissected_image,
4010
                        "/run/systemd/mount-rootfs",
4011
                        /* uid_shift= */ UID_INVALID,
4012
                        /* uid_range= */ UID_INVALID,
4013
                        /* userns_fd= */ -EBADF,
4014
                        flags);
4015
        if (r < 0)
28✔
4016
                return r;
4017

4018
        r = loop_device_flock(d, LOCK_UN);
28✔
4019
        if (r < 0)
28✔
4020
                return r;
4021

4022
        r = dissected_image_relinquish(dissected_image);
28✔
4023
        if (r < 0)
28✔
UNCOV
4024
                return log_error_errno(r, "Failed to relinquish DM and loopback block devices: %m");
×
4025

4026
        if (ret_directory) {
28✔
4027
                dir = strdup("/run/systemd/mount-rootfs");
28✔
4028
                if (!dir)
28✔
UNCOV
4029
                        return log_oom();
×
4030
        }
4031

4032
        if (ret_dir_fd) {
28✔
4033
                _cleanup_close_ int dir_fd = -EBADF;
28✔
4034

UNCOV
4035
                dir_fd = open("/run/systemd/mount-rootfs", O_CLOEXEC|O_DIRECTORY);
×
UNCOV
4036
                if (dir_fd < 0)
×
UNCOV
4037
                        return log_error_errno(errno, "Failed to open mount point directory: %m");
×
4038

UNCOV
4039
                *ret_dir_fd = TAKE_FD(dir_fd);
×
4040
        }
4041

4042
        if (ret_directory)
28✔
4043
                *ret_directory = TAKE_PTR(dir);
28✔
4044

4045
        *ret_loop_device = TAKE_PTR(d);
28✔
4046
        return 0;
28✔
4047
}
4048

4049
static bool mount_options_relax_extension_release_checks(const MountOptions *options) {
65✔
4050
        if (!options)
65✔
4051
                return false;
4052

4053
        return string_contains_word(mount_options_from_designator(options, PARTITION_ROOT), ",", "x-systemd.relax-extension-release-check") ||
6✔
4054
                        string_contains_word(mount_options_from_designator(options, PARTITION_USR), ",", "x-systemd.relax-extension-release-check") ||
12✔
4055
                        string_contains_word(options->options, ",", "x-systemd.relax-extension-release-check");
6✔
4056
}
4057

4058
int verity_dissect_and_mount(
65✔
4059
                int src_fd,
4060
                const char *src,
4061
                const char *dest,
4062
                const MountOptions *options,
4063
                const ImagePolicy *image_policy,
4064
                const char *required_host_os_release_id,
4065
                const char *required_host_os_release_version_id,
4066
                const char *required_host_os_release_sysext_level,
4067
                const char *required_host_os_release_confext_level,
4068
                const char *required_sysext_scope,
4069
                VeritySettings *verity,
4070
                DissectedImage **ret_image) {
4071

4072
        _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
65✔
4073
        _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
65✔
4074
        _cleanup_(verity_settings_done) VeritySettings local_verity = VERITY_SETTINGS_DEFAULT;
65✔
4075
        DissectImageFlags dissect_image_flags;
65✔
4076
        bool relax_extension_release_check;
65✔
4077
        int r;
65✔
4078

4079
        assert(src);
65✔
4080
        /* Verifying release metadata requires mounted image for now, so ensure the check is skipped when
4081
         * opening an image without mounting it immediately (i.e.: 'dest' is NULL). */
4082
        assert(!required_host_os_release_id || dest);
65✔
4083

4084
        relax_extension_release_check = mount_options_relax_extension_release_checks(options);
65✔
4085

4086
        /* We might get an FD for the image, but we use the original path to look for the dm-verity files.
4087
         * The caller might also give us a pre-loaded VeritySettings, in which case we just use it. It will
4088
         * also be extended, as dissected_image_load_verity_sig_partition() is invoked. */
4089
        if (!verity) {
65✔
4090
                r = verity_settings_load(&local_verity, src, NULL, NULL);
2✔
4091
                if (r < 0)
2✔
UNCOV
4092
                        return log_debug_errno(r, "Failed to load root hash: %m");
×
4093

4094
                verity = &local_verity;
4095
        }
4096

4097
        dissect_image_flags =
130✔
4098
                (verity->data_path ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0) |
65✔
4099
                (relax_extension_release_check ? DISSECT_IMAGE_RELAX_EXTENSION_CHECK : 0) |
65✔
4100
                DISSECT_IMAGE_ADD_PARTITION_DEVICES |
4101
                DISSECT_IMAGE_PIN_PARTITION_DEVICES |
65✔
4102
                DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
4103

4104
        /* Note that we don't use loop_device_make here, as the FD is most likely O_PATH which would not be
4105
         * accepted by LOOP_CONFIGURE, so just let loop_device_make_by_path reopen it as a regular FD. */
4106
        r = loop_device_make_by_path(
118✔
4107
                        src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src,
2✔
4108
                        /* open_flags= */ -1,
4109
                        /* sector_size= */ UINT32_MAX,
4110
                        verity->data_path ? 0 : LO_FLAGS_PARTSCAN,
4111
                        LOCK_SH,
4112
                        &loop_device);
4113
        if (r < 0)
65✔
4114
                return log_debug_errno(r, "Failed to create loop device for image: %m");
1✔
4115

4116
        r = dissect_loop_device(
64✔
4117
                        loop_device,
4118
                        verity,
4119
                        options,
4120
                        image_policy,
4121
                        dissect_image_flags,
4122
                        &dissected_image);
4123
        /* No partition table? Might be a single-filesystem image, try again */
4124
        if (!verity->data_path && r == -ENOPKG)
64✔
4125
                 r = dissect_loop_device(
48✔
4126
                                loop_device,
4127
                                verity,
4128
                                options,
4129
                                image_policy,
4130
                                dissect_image_flags | DISSECT_IMAGE_NO_PARTITION_TABLE,
48✔
4131
                                &dissected_image);
4132
        if (r < 0)
64✔
UNCOV
4133
                return log_debug_errno(r, "Failed to dissect image: %m");
×
4134

4135
        r = dissected_image_load_verity_sig_partition(dissected_image, loop_device->fd, verity);
64✔
4136
        if (r < 0)
64✔
4137
                return r;
4138

4139
        r = dissected_image_decrypt(
64✔
4140
                        dissected_image,
4141
                        NULL,
4142
                        verity,
4143
                        dissect_image_flags);
4144
        if (r < 0)
64✔
UNCOV
4145
                return log_debug_errno(r, "Failed to decrypt dissected image: %m");
×
4146

4147
        if (dest) {
64✔
4148
                r = mkdir_p_label(dest, 0755);
62✔
4149
                if (r < 0)
62✔
UNCOV
4150
                        return log_debug_errno(r, "Failed to create destination directory %s: %m", dest);
×
4151
                r = umount_recursive(dest, 0);
62✔
4152
                if (r < 0)
62✔
UNCOV
4153
                        return log_debug_errno(r, "Failed to umount under destination directory %s: %m", dest);
×
4154
        }
4155

4156
        r = dissected_image_mount(
64✔
4157
                        dissected_image,
4158
                        dest,
4159
                        /* uid_shift= */ UID_INVALID,
4160
                        /* uid_range= */ UID_INVALID,
4161
                        /* userns_fd= */ -EBADF,
4162
                        dissect_image_flags);
4163
        if (r < 0)
64✔
UNCOV
4164
                return log_debug_errno(r, "Failed to mount image: %m");
×
4165

4166
        r = loop_device_flock(loop_device, LOCK_UN);
64✔
4167
        if (r < 0)
64✔
UNCOV
4168
                return log_debug_errno(r, "Failed to unlock loopback device: %m");
×
4169

4170
        /* If we got os-release values from the caller, then we need to match them with the image's
4171
         * extension-release.d/ content. Return -EINVAL if there's any mismatch.
4172
         * First, check the distro ID. If that matches, then check the new SYSEXT_LEVEL value if
4173
         * available, or else fallback to VERSION_ID. If neither is present (eg: rolling release),
4174
         * then a simple match on the ID will be performed. */
4175
        if (required_host_os_release_id) {
64✔
4176
                _cleanup_strv_free_ char **extension_release = NULL;
5✔
4177
                ImageClass class = IMAGE_SYSEXT;
5✔
4178

4179
                assert(!isempty(required_host_os_release_id));
5✔
4180

4181
                r = load_extension_release_pairs(dest, IMAGE_SYSEXT, dissected_image->image_name, relax_extension_release_check, &extension_release);
5✔
4182
                if (r == -ENOENT) {
5✔
4183
                        r = load_extension_release_pairs(dest, IMAGE_CONFEXT, dissected_image->image_name, relax_extension_release_check, &extension_release);
2✔
4184
                        if (r >= 0)
2✔
4185
                                class = IMAGE_CONFEXT;
4186
                }
4187
                if (r < 0)
3✔
UNCOV
4188
                        return log_debug_errno(r, "Failed to parse image %s extension-release metadata: %m", dissected_image->image_name);
×
4189

4190
                r = extension_release_validate(
5✔
4191
                                dissected_image->image_name,
5✔
4192
                                required_host_os_release_id,
4193
                                required_host_os_release_version_id,
4194
                                class == IMAGE_SYSEXT ? required_host_os_release_sysext_level : required_host_os_release_confext_level,
4195
                                required_sysext_scope,
4196
                                extension_release,
4197
                                class);
4198
                if (r == 0)
5✔
UNCOV
4199
                        return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Image %s extension-release metadata does not match the root's", dissected_image->image_name);
×
4200
                if (r < 0)
5✔
4201
                        return log_debug_errno(r, "Failed to compare image %s extension-release metadata with the root's os-release: %m", dissected_image->image_name);
×
4202
        }
4203

4204
        r = dissected_image_relinquish(dissected_image);
64✔
4205
        if (r < 0)
64✔
UNCOV
4206
                return log_debug_errno(r, "Failed to relinquish dissected image: %m");
×
4207

4208
        if (ret_image)
64✔
4209
                *ret_image = TAKE_PTR(dissected_image);
2✔
4210

4211
        return 0;
4212
}
4213

4214
int get_common_dissect_directory(char **ret) {
65✔
4215
        _cleanup_free_ char *t = NULL;
65✔
4216
        int r;
65✔
4217

4218
        /* A common location we mount dissected images to. The assumption is that everyone who uses this
4219
         * function runs in their own private mount namespace (with mount propagation off on /run/systemd/,
4220
         * and thus can mount something here without affecting anyone else). */
4221

4222
        t = strdup("/run/systemd/dissect-root");
65✔
4223
        if (!t)
65✔
UNCOV
4224
                return log_oom_debug();
×
4225

4226
        r = mkdir_parents(t, 0755);
65✔
4227
        if (r < 0)
65✔
UNCOV
4228
                return log_debug_errno(r, "Failed to create parent dirs of mount point '%s': %m", t);
×
4229

4230
        r = RET_NERRNO(mkdir(t, 0000)); /* It's supposed to be overmounted, hence let's make this inaccessible */
65✔
4231
        if (r < 0 && r != -EEXIST)
65✔
UNCOV
4232
                return log_debug_errno(r, "Failed to create mount point '%s': %m", t);
×
4233

4234
        if (ret)
65✔
4235
                *ret = TAKE_PTR(t);
65✔
4236

4237
        return 0;
4238
}
4239

4240
#if HAVE_BLKID
4241

4242
static JSON_DISPATCH_ENUM_DEFINE(dispatch_architecture, Architecture, architecture_from_string);
10✔
4243
static JSON_DISPATCH_ENUM_DEFINE(dispatch_partition_designator, PartitionDesignator, partition_designator_from_string);
10✔
4244

4245
typedef struct PartitionFields {
4246
        PartitionDesignator designator;
4247
        bool rw;
4248
        bool growfs;
4249
        unsigned partno;
4250
        Architecture architecture;
4251
        sd_id128_t uuid;
4252
        char *fstype;
4253
        char *label;
4254
        uint64_t size;
4255
        uint64_t offset;
4256
        unsigned fsmount_fd_idx;
4257
} PartitionFields;
4258

4259
static void partition_fields_done(PartitionFields *f) {
10✔
4260
        assert(f);
10✔
4261

4262
        f->fstype = mfree(f->fstype);
10✔
4263
        f->label = mfree(f->label);
10✔
4264
}
10✔
4265

4266
typedef struct MountImageReplyParameters {
4267
        sd_json_variant *partitions;
4268
        char *image_policy;
4269
        uint64_t image_size;
4270
        uint32_t sector_size;
4271
        sd_id128_t image_uuid;
4272
} MountImageReplyParameters;
4273

4274
static void mount_image_reply_parameters_done(MountImageReplyParameters *p) {
12✔
4275
        assert(p);
12✔
4276

4277
        p->image_policy = mfree(p->image_policy);
12✔
4278
        p->partitions = sd_json_variant_unref(p->partitions);
12✔
4279
}
12✔
4280

4281
#endif
4282

4283
int mountfsd_mount_image(
12✔
4284
                const char *path,
4285
                int userns_fd,
4286
                const ImagePolicy *image_policy,
4287
                DissectImageFlags flags,
4288
                DissectedImage **ret) {
4289

4290
#if HAVE_BLKID
4291
        _cleanup_(mount_image_reply_parameters_done) MountImageReplyParameters p = {};
12✔
4292

4293
        static const sd_json_dispatch_field dispatch_table[] = {
12✔
4294
                { "partitions",  SD_JSON_VARIANT_ARRAY,         sd_json_dispatch_variant, offsetof(struct MountImageReplyParameters, partitions),   SD_JSON_MANDATORY },
4295
                { "imagePolicy", SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,  offsetof(struct MountImageReplyParameters, image_policy), 0              },
4296
                { "imageSize",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,  offsetof(struct MountImageReplyParameters, image_size),   SD_JSON_MANDATORY },
4297
                { "sectorSize",  _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32,  offsetof(struct MountImageReplyParameters, sector_size),  SD_JSON_MANDATORY },
4298
                { "imageUuid",   SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,   offsetof(struct MountImageReplyParameters, image_uuid),   0              },
4299
                {}
4300
        };
4301

UNCOV
4302
        _cleanup_(dissected_image_unrefp) DissectedImage *di = NULL;
×
4303
        _cleanup_close_ int image_fd = -EBADF;
12✔
4304
        _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
12✔
4305
        _cleanup_free_ char *ps = NULL;
12✔
4306
        const char *error_id;
12✔
4307
        int r;
12✔
4308

4309
        assert(path);
12✔
4310
        assert(ret);
12✔
4311

4312
        r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.MountFileSystem");
12✔
4313
        if (r < 0)
12✔
4314
                return log_error_errno(r, "Failed to connect to mountfsd: %m");
×
4315

4316
        r = sd_varlink_set_allow_fd_passing_input(vl, true);
12✔
4317
        if (r < 0)
12✔
UNCOV
4318
                return log_error_errno(r, "Failed to enable varlink fd passing for read: %m");
×
4319

4320
        r = sd_varlink_set_allow_fd_passing_output(vl, true);
12✔
4321
        if (r < 0)
12✔
UNCOV
4322
                return log_error_errno(r, "Failed to enable varlink fd passing for write: %m");
×
4323

4324
        image_fd = open(path, O_RDONLY|O_CLOEXEC);
12✔
4325
        if (image_fd < 0)
12✔
UNCOV
4326
                return log_error_errno(errno, "Failed to open '%s': %m", path);
×
4327

4328
        r = sd_varlink_push_dup_fd(vl, image_fd);
12✔
4329
        if (r < 0)
12✔
4330
                return log_error_errno(r, "Failed to push image fd into varlink connection: %m");
×
4331

4332
        if (userns_fd >= 0) {
12✔
4333
                r = sd_varlink_push_dup_fd(vl, userns_fd);
12✔
4334
                if (r < 0)
12✔
UNCOV
4335
                        return log_error_errno(r, "Failed to push image fd into varlink connection: %m");
×
4336
        }
4337

4338
        if (image_policy) {
12✔
UNCOV
4339
                r = image_policy_to_string(image_policy, /* simplify= */ false, &ps);
×
UNCOV
4340
                if (r < 0)
×
UNCOV
4341
                        return log_error_errno(r, "Failed format image policy to string: %m");
×
4342
        }
4343

4344
        sd_json_variant *reply = NULL;
12✔
4345
        r = sd_varlink_callbo(
12✔
4346
                        vl,
4347
                        "io.systemd.MountFileSystem.MountImage",
4348
                        &reply,
4349
                        &error_id,
4350
                        SD_JSON_BUILD_PAIR("imageFileDescriptor", SD_JSON_BUILD_UNSIGNED(0)),
4351
                        SD_JSON_BUILD_PAIR_CONDITION(userns_fd >= 0, "userNamespaceFileDescriptor", SD_JSON_BUILD_UNSIGNED(1)),
4352
                        SD_JSON_BUILD_PAIR("readOnly", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_READ_ONLY))),
4353
                        SD_JSON_BUILD_PAIR("growFileSystems", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_GROWFS))),
4354
                        SD_JSON_BUILD_PAIR_CONDITION(!!ps, "imagePolicy", SD_JSON_BUILD_STRING(ps)),
4355
                        SD_JSON_BUILD_PAIR("allowInteractiveAuthentication", SD_JSON_BUILD_BOOLEAN(FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH))));
4356
        if (r < 0)
12✔
UNCOV
4357
                return log_error_errno(r, "Failed to call MountImage() varlink call: %m");
×
4358
        if (!isempty(error_id))
12✔
4359
                return log_error_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to call MountImage() varlink call: %s", error_id);
2✔
4360

4361
        r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
10✔
4362
        if (r < 0)
10✔
UNCOV
4363
                return log_error_errno(r, "Failed to parse MountImage() reply: %m");
×
4364

4365
        log_debug("Effective image policy: %s", p.image_policy);
10✔
4366

4367
        sd_json_variant *i;
10✔
4368
        JSON_VARIANT_ARRAY_FOREACH(i, p.partitions) {
20✔
4369
                _cleanup_close_ int fsmount_fd = -EBADF;
10✔
4370

UNCOV
4371
                _cleanup_(partition_fields_done) PartitionFields pp = {
×
4372
                        .designator = _PARTITION_DESIGNATOR_INVALID,
4373
                        .architecture = _ARCHITECTURE_INVALID,
4374
                        .size = UINT64_MAX,
4375
                        .offset = UINT64_MAX,
4376
                        .fsmount_fd_idx = UINT_MAX,
4377
                };
4378

4379
                static const sd_json_dispatch_field partition_dispatch_table[] = {
10✔
4380
                        { "designator",          SD_JSON_VARIANT_STRING,        dispatch_partition_designator, offsetof(struct PartitionFields, designator),       SD_JSON_MANDATORY },
4381
                        { "writable",            SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,      offsetof(struct PartitionFields, rw),               SD_JSON_MANDATORY },
4382
                        { "growFileSystem",      SD_JSON_VARIANT_BOOLEAN,       sd_json_dispatch_stdbool,      offsetof(struct PartitionFields, growfs),           SD_JSON_MANDATORY },
4383
                        { "partitionNumber",     _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint,         offsetof(struct PartitionFields, partno),           0                 },
4384
                        { "architecture",        SD_JSON_VARIANT_STRING,        dispatch_architecture,         offsetof(struct PartitionFields, architecture),     0                 },
4385
                        { "partitionUuid",       SD_JSON_VARIANT_STRING,        sd_json_dispatch_id128,        offsetof(struct PartitionFields, uuid),             0                 },
4386
                        { "fileSystemType",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,       offsetof(struct PartitionFields, fstype),           SD_JSON_MANDATORY },
4387
                        { "partitionLabel",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,       offsetof(struct PartitionFields, label),            0                 },
4388
                        { "size",                _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,       offsetof(struct PartitionFields, size),             SD_JSON_MANDATORY },
4389
                        { "offset",              _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,       offsetof(struct PartitionFields, offset),           SD_JSON_MANDATORY },
4390
                        { "mountFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint,         offsetof(struct PartitionFields, fsmount_fd_idx),   SD_JSON_MANDATORY },
4391
                        {}
4392
                };
4393

4394
                r = sd_json_dispatch(i, partition_dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &pp);
10✔
4395
                if (r < 0)
10✔
UNCOV
4396
                        return log_error_errno(r, "Failed to parse partition data: %m");
×
4397

4398
                if (pp.fsmount_fd_idx != UINT_MAX) {
10✔
4399
                        fsmount_fd = sd_varlink_take_fd(vl, pp.fsmount_fd_idx);
10✔
4400
                        if (fsmount_fd < 0)
10✔
4401
                                return fsmount_fd;
4402
                }
4403

4404
                assert(pp.designator >= 0);
10✔
4405

4406
                if (!di) {
10✔
4407
                        r = dissected_image_new(path, &di);
10✔
4408
                        if (r < 0)
10✔
UNCOV
4409
                                return log_error_errno(r, "Failed to allocated new dissected image structure: %m");
×
4410
                }
4411

4412
                if (di->partitions[pp.designator].found)
10✔
UNCOV
4413
                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Duplicate partition data for '%s'.", partition_designator_to_string(pp.designator));
×
4414

4415
                di->partitions[pp.designator] = (DissectedPartition) {
10✔
4416
                        .found = true,
4417
                        .rw = pp.rw,
10✔
4418
                        .growfs = pp.growfs,
10✔
4419
                        .partno = pp.partno,
10✔
4420
                        .architecture = pp.architecture,
10✔
4421
                        .uuid = pp.uuid,
4422
                        .fstype = TAKE_PTR(pp.fstype),
10✔
4423
                        .label = TAKE_PTR(pp.label),
10✔
4424
                        .mount_node_fd = -EBADF,
4425
                        .size = pp.size,
10✔
4426
                        .offset = pp.offset,
10✔
4427
                        .fsmount_fd = TAKE_FD(fsmount_fd),
10✔
4428
                };
4429
        }
4430

4431
        di->image_size = p.image_size;
10✔
4432
        di->sector_size = p.sector_size;
10✔
4433
        di->image_uuid = p.image_uuid;
10✔
4434

4435
        *ret = TAKE_PTR(di);
10✔
4436
        return 0;
10✔
4437
#else
4438
        return -EOPNOTSUPP;
4439
#endif
4440
}
4441

4442
int mountfsd_mount_directory(
7✔
4443
                const char *path,
4444
                int userns_fd,
4445
                DissectImageFlags flags,
4446
                int *ret_mount_fd) {
4447

4448
        int r;
7✔
4449

4450
        /* Pick one identity, not both, that makes no sense. */
4451
        assert(!FLAGS_SET(flags, DISSECT_IMAGE_FOREIGN_UID|DISSECT_IMAGE_IDENTITY_UID));
7✔
4452

4453
        _cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
7✔
4454
        r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.MountFileSystem");
7✔
4455
        if (r < 0)
7✔
UNCOV
4456
                return log_error_errno(r, "Failed to connect to mountfsd: %m");
×
4457

4458
        r = sd_varlink_set_allow_fd_passing_input(vl, true);
7✔
4459
        if (r < 0)
7✔
UNCOV
4460
                return log_error_errno(r, "Failed to enable varlink fd passing for read: %m");
×
4461

4462
        r = sd_varlink_set_allow_fd_passing_output(vl, true);
7✔
4463
        if (r < 0)
7✔
UNCOV
4464
                return log_error_errno(r, "Failed to enable varlink fd passing for write: %m");
×
4465

4466
        _cleanup_close_ int directory_fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
14✔
4467
        if (directory_fd < 0)
7✔
UNCOV
4468
                return log_error_errno(errno, "Failed to open '%s': %m", path);
×
4469

4470
        r = sd_varlink_push_dup_fd(vl, directory_fd);
7✔
4471
        if (r < 0)
7✔
UNCOV
4472
                return log_error_errno(r, "Failed to push image fd into varlink connection: %m");
×
4473

4474
        if (userns_fd >= 0) {
7✔
4475
                r = sd_varlink_push_dup_fd(vl, userns_fd);
7✔
4476
                if (r < 0)
7✔
UNCOV
4477
                        return log_error_errno(r, "Failed to push image fd into varlink connection: %m");
×
4478
        }
4479

4480
        sd_json_variant *reply = NULL;
7✔
4481
        const char *error_id = NULL;
7✔
4482
        r = sd_varlink_callbo(
14✔
4483
                        vl,
4484
                        "io.systemd.MountFileSystem.MountDirectory",
4485
                        &reply,
4486
                        &error_id,
4487
                        SD_JSON_BUILD_PAIR_UNSIGNED("directoryFileDescriptor", 0),
4488
                        SD_JSON_BUILD_PAIR_CONDITION(userns_fd >= 0, "userNamespaceFileDescriptor", SD_JSON_BUILD_UNSIGNED(1)),
4489
                        SD_JSON_BUILD_PAIR_BOOLEAN("readOnly", FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_READ_ONLY)),
4490
                        SD_JSON_BUILD_PAIR_STRING("mode", FLAGS_SET(flags, DISSECT_IMAGE_FOREIGN_UID) ? "foreign" :
4491
                                                          FLAGS_SET(flags, DISSECT_IMAGE_IDENTITY_UID) ? "identity" : "auto"),
4492
                        SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH)));
4493
        if (r < 0)
7✔
UNCOV
4494
                return log_error_errno(r, "Failed to call MountDirectory() varlink call: %m");
×
4495
        if (!isempty(error_id))
7✔
UNCOV
4496
                return log_error_errno(sd_varlink_error_to_errno(error_id, reply), "Failed to call MountDirectory() varlink call: %s", error_id);
×
4497

4498
        static const sd_json_dispatch_field dispatch_table[] = {
7✔
4499
                { "mountFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, 0, SD_JSON_MANDATORY },
4500
                {}
4501
        };
4502

4503
        unsigned fsmount_fd_idx = UINT_MAX;
7✔
4504
        r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &fsmount_fd_idx);
7✔
4505
        if (r < 0)
7✔
UNCOV
4506
                return log_error_errno(r, "Failed to parse MountImage() reply: %m");
×
4507

4508
        _cleanup_close_ int fsmount_fd = sd_varlink_take_fd(vl, fsmount_fd_idx);
14✔
4509
        if (fsmount_fd < 0)
7✔
UNCOV
4510
                return log_error_errno(fsmount_fd, "Failed to take mount fd from Varlink connection: %m");
×
4511

4512
        *ret_mount_fd = TAKE_FD(fsmount_fd);
7✔
4513
        return 0;
7✔
4514
}
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