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

systemd / systemd / 23877209284

01 Apr 2026 05:23PM UTC coverage: 72.343% (-0.06%) from 72.404%
23877209284

push

github

bluca
hwdb/keyboard: fix enter key for X+ piccolo

The main enter key gives a code for keypad one... Map it to
regular enter key.

318395 of 440116 relevant lines covered (72.34%)

1157750.86 hits per line

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

62.76
/src/shared/mstack.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

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

7
#include "sd-varlink.h"
8

9
#include "alloc-util.h"
10
#include "chase.h"
11
#include "dissect-image.h"
12
#include "errno-util.h"
13
#include "fd-util.h"
14
#include "fs-util.h"
15
#include "log.h"
16
#include "loop-util.h"
17
#include "macro.h"
18
#include "mount-util.h"
19
#include "mstack.h"
20
#include "path-util.h"
21
#include "process-util.h"
22
#include "recurse-dir.h"
23
#include "rm-rf.h"
24
#include "sort-util.h"
25
#include "stat-util.h"
26
#include "string-table.h"
27
#include "string-util.h"
28
#include "tmpfile-util.h"
29
#include "uid-classification.h"
30
#include "unit-name.h"
31
#include "vpick.h"
32

33
static void mstack_mount_done(MStackMount *m) {
71✔
34
        assert(m);
71✔
35

36
        m->where = mfree(m->where);
71✔
37
        m->what = mfree(m->what);
71✔
38
        m->what_fd = safe_close(m->what_fd);
71✔
39
        m->mount_fd = safe_close(m->mount_fd);
71✔
40
        m->sort_key = mfree(m->sort_key);
71✔
41
        m->dissected_image = dissected_image_unref(m->dissected_image);
71✔
42
}
71✔
43

44
static void mstack_done(MStack *mstack) {
19✔
45
        assert(mstack);
19✔
46

47
        FOREACH_ARRAY(m, mstack->mounts, mstack->n_mounts)
90✔
48
                mstack_mount_done(m);
71✔
49

50
        mstack->mounts = mfree(mstack->mounts);
19✔
51
        mstack->n_mounts = 0;
19✔
52
        mstack->root_mount = NULL;
19✔
53
        mstack->has_tmpfs_root = mstack->has_overlayfs = false;
19✔
54
        mstack->path = mfree(mstack->path);
19✔
55
        safe_close(mstack->root_mount_fd);
19✔
56
        safe_close(mstack->usr_mount_fd);
19✔
57
}
19✔
58

59
MStack* mstack_free(MStack *mstack) {
10,350✔
60
        if (!mstack)
10,350✔
61
                return NULL;
62

63
        mstack_done(mstack);
18✔
64

65
        return mfree(mstack);
18✔
66
}
67

68
static int validate_prefix_name(const char *name, const char *prefix, char **ret_parameter) {
163✔
69
        _cleanup_free_ char *p = NULL;
326✔
70

71
        assert(name);
163✔
72
        assert(prefix);
163✔
73

74
        const char *a = startswith(name, prefix);
163✔
75
        if (isempty(a)) {
163✔
76
                if (ret_parameter)
69✔
77
                        *ret_parameter = NULL;
69✔
78

79
                return false;
69✔
80
        }
81

82
        p = strdup(a);
94✔
83
        if (!p)
94✔
84
                return -ENOMEM;
85

86
        if (ret_parameter)
94✔
87
                *ret_parameter = TAKE_PTR(p);
94✔
88

89
        return true;
90
}
91

92
static MStackMount *mstack_find(MStack *mstack, MStackMountType t, const char *sort_key, const char *where) {
152✔
93
        assert(mstack);
152✔
94

95
        FOREACH_ARRAY(m, mstack->mounts, mstack->n_mounts) {
348✔
96

97
                if (t >= 0 && m->mount_type != t)
196✔
98
                        continue;
149✔
99

100
                if (sort_key && !streq_ptr(m->sort_key, sort_key))
47✔
101
                        continue;
36✔
102

103
                if (where && !path_equal(m->where, where))
11✔
104
                        continue;
11✔
105

106
                return m;
107
        }
108

109
        return NULL;
110
}
111

112
static int mstack_load_one(MStack *mstack, const char *dir, int dir_fd, const char *fname) {
130✔
113
        int r;
130✔
114

115
        assert(mstack);
130✔
116
        assert(dir_fd >= 0);
130✔
117
        assert(fname);
130✔
118

119
        _cleanup_close_ int what_fd = openat(dir_fd, fname, O_PATH|O_CLOEXEC);
260✔
120
        if (what_fd < 0)
130✔
121
                return log_debug_errno(errno, "Failed to open %s/%s: %m", dir, fname);
×
122

123
        struct stat st;
130✔
124
        if (fstat(what_fd, &st) < 0)
130✔
125
                return log_debug_errno(errno, "Failed to stat %s/%s: %m", dir, fname);
×
126

127
        ImageType image_type = _IMAGE_TYPE_INVALID;
130✔
128
        _cleanup_free_ char *what = NULL, *unsuffixed = NULL;
130✔
129
        if (S_ISDIR(st.st_mode)) {
130✔
130

131
                const char *dotv = endswith(fname, ".v");
130✔
132
                if (dotv) {
130✔
133
                        const char *dotrawv = endswith(fname, ".raw.v");
×
134

135
                        PickFilter filter = {
×
136
                                .type_mask = dotrawv ? (1U << DT_REG) : ((1U << DT_DIR) | (1U << DT_BLK)),
×
137
                                .suffix = dotrawv ? ".raw" : NULL,
×
138
                                .architecture = _ARCHITECTURE_INVALID,
139
                        };
140

141
                        _cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
×
142
                        r = path_pick(dir, dir_fd, fname, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE, &result);
×
143
                        if (r < 0)
×
144
                                return log_debug_errno(r, "Failed to resolve '%s' directory: %m", fname);
×
145
                        if (r == 0)
×
146
                                return log_debug_errno(SYNTHETIC_ERRNO(ENOENT), "Found no suitable entry in '%s': %m", fname);
×
147

148
                        what = TAKE_PTR(result.path);
×
149
                        close_and_replace(what_fd, result.fd);
×
150
                        st = result.st;
×
151

152
                        unsuffixed = strndup(fname, (dotrawv ?: dotv) - fname);
×
153
                        if (!unsuffixed)
×
154
                                return log_oom();
×
155

156
                        image_type = S_ISDIR(st.st_mode) ? IMAGE_DIRECTORY :
×
157
                                     S_ISREG(st.st_mode) ? IMAGE_RAW :
158
                                     S_ISBLK(st.st_mode) ? IMAGE_BLOCK : _IMAGE_TYPE_INVALID;
159

160
                        assert(image_type >= 0);
×
161
                } else
162
                        image_type = IMAGE_DIRECTORY;
163

164
        } else if (S_ISREG(st.st_mode)) {
×
165
                const char *e = endswith(fname, ".raw");
×
166
                if (!e)
×
167
                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Unexpected suffix of '%s/%s', refusing.", dir, fname);
×
168

169
                unsuffixed = strndup(fname, e - fname);
×
170
                if (!unsuffixed)
×
171
                        return -ENOMEM;
172

173
                image_type = IMAGE_RAW;
174

175
        } else if (S_ISBLK(st.st_mode))
×
176
                image_type = IMAGE_BLOCK;
177
        else
178
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Unexpected inode type of '%s/%s', refusing.", dir, fname);
×
179

180
        if (!what) {
×
181
                what = strdup(fname);
130✔
182
                if (!what)
130✔
183
                        return -ENOMEM;
184
        }
185

186
        if (!unsuffixed) {
130✔
187
                unsuffixed = strdup(what);
130✔
188
                if (!unsuffixed)
130✔
189
                        return -ENOMEM;
190
        }
191

192
        if (!GREEDY_REALLOC(mstack->mounts, mstack->n_mounts+1))
130✔
193
                return -ENOMEM;
194

195
        MStackMount *m = mstack->mounts + mstack->n_mounts;
130✔
196

197
        _cleanup_free_ char *parameter = NULL;
130✔
198
        r = validate_prefix_name(unsuffixed, "layer@", &parameter);
130✔
199
        if (r < 0)
130✔
200
                return log_debug_errno(r, "Failed to check prefix of %s/%s: %m", dir, fname);
×
201
        if (r > 0) {
130✔
202
                /* Paranoia: let's refuse two layers that have the same sort key. Howe can that happen?
203
                 * People might have a .raw layer and one dir layer with the same name. Or one with .v and
204
                 * one without. */
205
                if (mstack_find(mstack, MSTACK_LAYER, parameter, /* where= */ NULL))
72✔
206
                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "Duplicate layer '%s', refusing.", parameter);
×
207

208
                *m = (MStackMount) {
72✔
209
                        .mount_type = MSTACK_LAYER,
210
                        .what = TAKE_PTR(what),
72✔
211
                        .what_fd = TAKE_FD(what_fd),
72✔
212
                        .mount_fd = -EBADF,
213
                        .sort_key = TAKE_PTR(parameter),
72✔
214
                        .image_type = image_type,
215
                };
216

217
                mstack->n_mounts++;
72✔
218
                log_debug("Found mstack layer '%s' ('%s', owned by UID " UID_FMT ")", m->sort_key, m->what, st.st_uid);
72✔
219
                return 0;
72✔
220
        }
221

222
        if (streq(unsuffixed, "rw")) {
58✔
223
                if (mstack_find(mstack, MSTACK_RW, /* sort_key= */ NULL, /* where= */ NULL))
36✔
224
                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "Duplicate rw entry, refusing.");
×
225

226
                *m = (MStackMount) {
36✔
227
                        .mount_type = MSTACK_RW,
228
                        .what = TAKE_PTR(what),
36✔
229
                        .what_fd = TAKE_FD(what_fd),
36✔
230
                        .mount_fd = -EBADF,
231
                        .image_type = image_type,
232
                };
233

234
                mstack->n_mounts++;
36✔
235
                log_debug("Found mstack rw layer ('%s')", m->what);
36✔
236
                return 0;
36✔
237
        }
238

239
        MStackMountType bind_type = _MSTACK_MOUNT_TYPE_INVALID;
22✔
240
        r = validate_prefix_name(unsuffixed, "bind@", &parameter);
22✔
241
        if (r < 0)
22✔
242
                return log_debug_errno(r, "Failed to check prefix of %s/%s: %m", dir, fname);
×
243
        if (r > 0)
22✔
244
                bind_type = MSTACK_BIND;
245
        else {
246
                r = validate_prefix_name(unsuffixed, "robind@", &parameter);
11✔
247
                if (r < 0)
11✔
248
                        return log_debug_errno(r, "Failed to check prefix of %s/%s: %m", dir, fname);
×
249
                if (r > 0)
11✔
250
                        bind_type = MSTACK_ROBIND;
251
        }
252
        if (bind_type >= 0) {
×
253
                _cleanup_free_ char *where = NULL;
22✔
254
                r = unit_name_path_unescape(parameter, &where);
22✔
255
                if (r < 0)
22✔
256
                        return log_debug_errno(r, "Cannot unescape path '%s' of '%s/%s'", parameter, dir, fname);
×
257

258
                if (mstack_find(mstack, MSTACK_BIND, /* sort_key= */ NULL, /* where= */ where) ||
44✔
259
                    mstack_find(mstack, MSTACK_ROBIND, /* sort_key= */ NULL, /* where= */ where))
22✔
260
                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "Duplicate bind entry, refusing");
×
261

262
                *m = (MStackMount) {
22✔
263
                        .mount_type = bind_type,
264
                        .what = TAKE_PTR(what),
22✔
265
                        .what_fd = TAKE_FD(what_fd),
22✔
266
                        .mount_fd = -EBADF,
267
                        .where = TAKE_PTR(where),
22✔
268
                        .image_type = image_type,
269
                };
270

271
                mstack->n_mounts++;
22✔
272
                log_debug("Found mstack bind layer '%s' ('%s')", empty_to_root(m->where), m->what);
22✔
273
                return 0;
22✔
274
        }
275

276
        if (streq(unsuffixed, "root")) {
×
277
                if (mstack_find(mstack, MSTACK_ROOT, /* sort_key= */ NULL, /* where= */ NULL))
×
278
                        return log_debug_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "Duplicate root entry, refusing");
×
279

280
                *m = (MStackMount) {
×
281
                        .mount_type = MSTACK_ROOT,
282
                        .what = TAKE_PTR(what),
×
283
                        .what_fd = TAKE_FD(what_fd),
×
284
                        .mount_fd = -EBADF,
285
                        .image_type = image_type,
286
                };
287

288
                mstack->n_mounts++;
×
289
                log_debug("Found mstack root layer ('%s')", m->what);
×
290
                return 0;
×
291
        }
292

293
        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Unrecognized entry '%s/%s', refusing", dir, fname);
×
294
}
295

296
static int mount_compare_func(const MStackMount *a, const MStackMount *b) {
257✔
297
        int r;
257✔
298

299
        assert(a);
257✔
300
        assert(b);
257✔
301

302
        /* If we apply this mstack in read-only mode then we'll convert the 'rw' layer which normally is an
303
         * upperdir into the topmost lowerdir. When sorting the mstack it is hence essential, that the "rw"
304
         * layer ends up *after* the regular layers. Enforce this here via a compile-time check. */
305
        assert_cc(MSTACK_RW > MSTACK_LAYER);
257✔
306

307
        r = CMP(a->mount_type, b->mount_type);
257✔
308
        if (r != 0)
166✔
309
                return r;
185✔
310

311
        r = path_compare(a->where, b->where);
72✔
312
        if (r != 0)
72✔
313
                return r;
314

315
        r = strverscmp_improved(a->sort_key, b->sort_key);
72✔
316
        if (r != 0)
72✔
317
                return r;
72✔
318

319
        return 0;
320
}
321

322
static void mstack_remove(MStack *mstack, MStackMountType t) {
×
323
        assert(mstack);
×
324

325
        size_t z = 0;
×
326
        FOREACH_ARRAY(m, mstack->mounts, mstack->n_mounts) {
×
327
                if (m->mount_type == t)
×
328
                        mstack_mount_done(m);
×
329
                else
330
                        mstack->mounts[z++] = *m;
×
331
        }
332

333
        mstack->n_mounts = z;
×
334
}
×
335

336
static int mstack_normalize(MStack *mstack) {
36✔
337
        int r;
36✔
338

339
        assert(mstack);
36✔
340

341
        typesafe_qsort(mstack->mounts, mstack->n_mounts, mount_compare_func);
36✔
342

343
        size_t n_layers = 0;
36✔
344
        bool has_rw = false, has_root_bind = false, has_usr_bind = false, has_root = false;
36✔
345
        FOREACH_ARRAY(m, mstack->mounts, mstack->n_mounts) {
166✔
346
                switch (m->mount_type) {
130✔
347
                case MSTACK_LAYER:
72✔
348
                        n_layers++;
72✔
349
                        break;
72✔
350

351
                case MSTACK_RW:
36✔
352
                        assert(!has_rw);
36✔
353
                        has_rw = true;
354
                        break;
355

356
                case MSTACK_BIND:
22✔
357
                case MSTACK_ROBIND:
358
                        if (empty_or_root(m->where))
22✔
359
                                has_root_bind = true;
360
                        else if (path_equal(m->where, "/usr"))
22✔
361
                                has_usr_bind = true;
×
362
                        break;
363

364
                case MSTACK_ROOT:
×
365
                        assert(!has_root);
×
366
                        has_root = true;
367
                        break;
368

369
                default:
×
370
                        assert_not_reached();
×
371
                }
372
        }
373

374
        /* If the overlayfs stack is fully obstructed, kill it */
375
        if (has_root_bind || (has_root && has_usr_bind)) {
36✔
376
                mstack_remove(mstack, MSTACK_LAYER);
×
377
                mstack_remove(mstack, MSTACK_RW);
×
378

379
                n_layers = 0;
×
380
                has_rw = false;
×
381
        }
382

383
        /* Only a single read-only or read-write layer? Turn into bind mount! */
384
        if (n_layers + has_rw == 1) {
36✔
385
                FOREACH_ARRAY(m, mstack->mounts, mstack->n_mounts) {
×
386
                        if (m->mount_type == MSTACK_LAYER)
×
387
                                m->mount_type = MSTACK_ROBIND;
×
388
                        else if (m->mount_type == MSTACK_RW)
×
389
                                m->mount_type = MSTACK_BIND;
×
390
                        else
391
                                continue;
×
392

393
                        if (has_root) {
×
394
                                /* If there's a root dir, let's only bind mount the /usr/ subdir */
395
                                _cleanup_close_ int usr_fd = openat(m->what_fd, "usr", O_CLOEXEC|O_PATH|O_NOFOLLOW|O_DIRECTORY);
36✔
396
                                if (usr_fd < 0)
×
397
                                        return log_debug_errno(errno, "Failed to open /usr/ subdir: %m");
×
398

399
                                _cleanup_free_ char *usr = path_join(m->what, "usr");
×
400
                                if (!usr)
×
401
                                        return log_oom();
×
402

403
                                r = free_and_strdup_warn(&m->where, "/usr");
×
404
                                if (r < 0)
×
405
                                        return r;
406

407
                                close_and_replace(m->what_fd, usr_fd);
×
408
                                free_and_replace(m->what, usr);
×
409
                        } else {
410
                                r = free_and_strdup_warn(&m->where, "/");
×
411
                                if (r < 0)
×
412
                                        return r;
413

414
                                has_root_bind = true;
415
                        }
416
                }
417

418
                n_layers = 0;
419
                has_rw = false;
420
        }
421

422
        /* If the root dir is overmounted, we can drop the original root */
423
        if (has_root_bind) {
36✔
424
                mstack_remove(mstack, MSTACK_ROOT);
×
425
                has_root = false;
×
426
        }
427

428
        /* After converting, let's sort things again */
429
        typesafe_qsort(mstack->mounts, mstack->n_mounts, mount_compare_func);
36✔
430

431
        /* Find root mount (unless it's the overlayfs stack) */
432
        FOREACH_ARRAY(m, mstack->mounts, mstack->n_mounts)
166✔
433
                if ((m->mount_type == MSTACK_ROOT) ||
130✔
434
                    (IN_SET(m->mount_type, MSTACK_BIND, MSTACK_ROBIND) && empty_or_root(m->where))) {
130✔
435
                        assert(!mstack->root_mount);
×
436
                        mstack->root_mount = m;
×
437
                }
438
        assert((has_root || has_root_bind) == !!mstack->root_mount);
36✔
439

440
        mstack->has_tmpfs_root = n_layers == 0 && !has_rw && !has_root_bind && !has_root;
36✔
441
        mstack->has_overlayfs = n_layers > 0 || has_rw;
36✔
442
        return 0;
36✔
443
}
444

445
static int mstack_load_now(MStack *mstack, const char *dir, int dir_fd, MStackFlags flags) {
36✔
446
        _cleanup_close_ int _dir_fd = -EBADF;
36✔
447
        int r;
36✔
448

449
        assert(mstack);
36✔
450

451
        r = free_and_strdup_warn(&mstack->path, dir);
36✔
452
        if (r < 0)
36✔
453
                return r;
454

455
        /* Expects dir_fd already opened. If not, then we'll open it based on 'dir' */
456
        if (dir_fd < 0) {
36✔
457
                _dir_fd = openat(AT_FDCWD, isempty(dir) ? "." : dir, O_DIRECTORY|O_CLOEXEC);
34✔
458
                if (_dir_fd < 0)
17✔
459
                        return log_debug_errno(errno, "Failed to to open '%s': %m", dir);
×
460

461
                dir_fd = _dir_fd;
462
        } else {
463
                /* Possibly convert an O_PATH fd to a real one */
464
                dir_fd = fd_reopen_condition(dir_fd, O_DIRECTORY|O_CLOEXEC, O_PATH|O_DIRECTORY, &_dir_fd);
19✔
465
                if (dir_fd < 0)
19✔
466
                        return log_debug_errno(dir_fd, "Failed to reopen '%s': %m", dir);
×
467
        }
468

469
        _cleanup_free_ DirectoryEntries *de = NULL;
36✔
470
        r = readdir_all(dir_fd, RECURSE_DIR_IGNORE_DOT, &de);
36✔
471
        if (r < 0)
36✔
472
                return r;
473

474
        FOREACH_ARRAY(i, de->entries, de->n_entries) {
166✔
475
                r = mstack_load_one(mstack, dir, dir_fd, (*i)->d_name);
130✔
476
                if (r < 0)
130✔
477
                        return r;
478
        }
479

480
        return mstack_normalize(mstack);
36✔
481
}
482

483
static int mount_get_fd(MStackMount *m) {
76✔
484
        assert(m);
76✔
485

486
        if (m->dissected_image) {
76✔
487
                assert(m->dissected_image->partitions[PARTITION_ROOT].found);
×
488
                return ASSERT_FD(m->dissected_image->partitions[PARTITION_ROOT].fsmount_fd);
×
489
        }
490

491
        if (m->mount_fd >= 0)
76✔
492
                return m->mount_fd;
493

494
        return m->what_fd;
40✔
495
}
496

497
static bool mount_is_ro(MStackMount *m, MStackFlags flags) {
162✔
498
        assert(m);
162✔
499

500
        return FLAGS_SET(flags, MSTACK_RDONLY) ||
162✔
501
                IN_SET(m->mount_type, MSTACK_LAYER, MSTACK_ROBIND);
114✔
502
}
503

504
static const char* mount_name(MStackMount *m) {
70✔
505
        assert(m);
70✔
506

507
        /* Returns some vaguely useful identifier for this layer, for showing in debug output */
508

509
        if (m->sort_key)
70✔
510
                return m->sort_key;
511

512
        if (m->where)
30✔
513
                return m->where;
514

515
        return mstack_mount_type_to_string(m->mount_type);
20✔
516
}
517

518
int mstack_open_images(
20✔
519
                MStack *mstack,
520
                sd_varlink *mountfsd_link,
521
                int userns_fd,
522
                const ImagePolicy *image_policy,
523
                const ImageFilter *image_filter,
524
                MStackFlags flags) {
525

526
        int r;
20✔
527

528
        assert(mstack);
20✔
529

530
        _cleanup_(sd_varlink_unrefp) sd_varlink *_vl = NULL;
20✔
531
        if (userns_fd >= 0 && !mountfsd_link) {
20✔
532
                /* User a single connection for all mounts */
533
                r = mountfsd_connect(&_vl);
×
534
                if (r < 0)
×
535
                        return r;
536

537
                mountfsd_link = _vl;
×
538
        }
539

540
        FOREACH_ARRAY(m, mstack->mounts, mstack->n_mounts) {
90✔
541

542
                DissectImageFlags dissect_image_flags =
70✔
543
                        DISSECT_IMAGE_DISCARD|
544
                        DISSECT_IMAGE_GENERIC_ROOT|
545
                        DISSECT_IMAGE_REQUIRE_ROOT|
546
                        DISSECT_IMAGE_MOUNT_ROOT_ONLY|
547
                        DISSECT_IMAGE_FSCK|
548
                        DISSECT_IMAGE_USR_NO_ROOT|
549
                        DISSECT_IMAGE_GROWFS|
550
                        DISSECT_IMAGE_ADD_PARTITION_DEVICES|
551
                        DISSECT_IMAGE_PIN_PARTITION_DEVICES|
552
                        DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
553

554
                SET_FLAG(dissect_image_flags, DISSECT_IMAGE_READ_ONLY, mount_is_ro(m, flags));
70✔
555
                SET_FLAG(dissect_image_flags, DISSECT_IMAGE_FOREIGN_UID, userns_fd >= 0);
70✔
556

557
                switch (m->image_type) {
70✔
558

559
                case IMAGE_RAW:
×
560
                case IMAGE_BLOCK:
561
                        assert(!m->dissected_image);
×
562

563
                        if (userns_fd >= 0) {
×
564
                                r = mountfsd_mount_image_fd(
×
565
                                                mountfsd_link,
566
                                                m->what_fd,
567
                                                userns_fd,
568
                                                /* options= */ NULL,
569
                                                image_policy,
570
                                                /* verity= */ NULL,
571
                                                dissect_image_flags,
572
                                                &m->dissected_image);
573
                                if (r < 0)
×
574
                                        return r;
575
                        } else {
576
                                _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
×
577
                                _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
×
578

579
                                r = loop_device_make_by_path_at(
×
580
                                                m->what_fd,
581
                                                /* path= */ NULL,
582
                                                FLAGS_SET(flags, MSTACK_RDONLY) ? O_RDONLY : -1,
×
583
                                                /* sector_size= */ UINT32_MAX,
584
                                                LO_FLAGS_PARTSCAN,
585
                                                LOCK_SH,
586
                                                &loop_device);
587
                                if (r < 0)
×
588
                                        return log_debug_errno(r, "Failed to allocate loopback device for '%s': %m", m->what);
×
589

590
                                _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
×
591
                                r = dissect_loop_device_and_warn(
×
592
                                                loop_device,
593
                                                &verity,
594
                                                /* mount_options= */ NULL,
595
                                                image_policy,
596
                                                image_filter,
597
                                                dissect_image_flags,
598
                                                &dissected_image);
599
                                if (r < 0)
×
600
                                        return r;
601

602
                                if (!dissected_image->partitions[PARTITION_ROOT].found)
×
603
                                        return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Currently images without root partition are not supported: %m");
×
604

605
                                r = dissected_image_load_verity_sig_partition(
×
606
                                                dissected_image,
607
                                                loop_device->fd,
×
608
                                                &verity);
609
                                if (r < 0)
×
610
                                        return log_debug_errno(r, "Failed to load Verity signature partition of '%s': %m", m->what);
×
611

612
                                r = dissected_image_guess_verity_roothash(
×
613
                                                dissected_image,
614
                                                &verity);
615
                                if (r < 0)
×
616
                                        return log_debug_errno(r, "Failed to guess Verity root hash of '%s': %m", m->what);
×
617

618
                                r = dissected_image_decrypt(
×
619
                                                dissected_image,
620
                                                /* root= */ NULL,
621
                                                /* passphrase= */ NULL,
622
                                                &verity,
623
                                                image_policy,
624
                                                dissect_image_flags);
625
                                if (r < 0)
×
626
                                        return log_debug_errno(r, "Failed to decrypt image '%s': %m", m->what);
×
627

628
                                r = dissected_image_mount(
×
629
                                                dissected_image,
630
                                                /* where= */ NULL,               /* allocate as mount fds, do not attach anywhere */
631
                                                /* uid_shift= */ UID_INVALID,
632
                                                /* uid_range= */ UID_INVALID,
633
                                                /* userns_fd = */ -EBADF,
634
                                                dissect_image_flags);
635
                                if (r < 0)
×
636
                                        return log_debug_errno(r, "Failed to mount image '%s': %m", m->what);
×
637

638
                                r = loop_device_flock(loop_device, LOCK_UN);
×
639
                                if (r < 0)
×
640
                                        return log_debug_errno(r, "Failed to unlock loopback block device: %m");
×
641

642
                                r = dissected_image_relinquish(dissected_image);
×
643
                                if (r < 0)
×
644
                                        return log_debug_errno(r, "Failed to relinquish DM and loopback block devices: %m");
×
645

646
                                m->dissected_image = TAKE_PTR(dissected_image);
×
647
                        }
648

649
                        log_debug("Acquired mstack DDI layer '%s'", mount_name(m));
×
650
                        break;
651

652
                case IMAGE_DIRECTORY:
70✔
653
                case IMAGE_SUBVOLUME:
654
                        assert(m->mount_fd < 0);
70✔
655

656
                        if (userns_fd >= 0) {
70✔
657
                                r = mountfsd_mount_directory_fd(
30✔
658
                                                mountfsd_link,
659
                                                m->what_fd,
660
                                                userns_fd,
661
                                                dissect_image_flags,
662
                                                &m->mount_fd);
663
                                if (r < 0)
30✔
664
                                        return r;
665
                        } else {
666
                                m->mount_fd = open_tree_attr_with_fallback(
40✔
667
                                                mount_get_fd(m),
668
                                                /* path= */ "",
669
                                                OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC|AT_EMPTY_PATH,
670
                                                &(struct mount_attr) {
120✔
671
                                                        .attr_set = mount_is_ro(m, flags) ? MOUNT_ATTR_RDONLY : 0,
40✔
672
                                                        .attr_clr = mount_is_ro(m, flags) ? 0 : MOUNT_ATTR_RDONLY,
40✔
673
                                                        .propagation = MS_PRIVATE, /* disconnect us from bind mount source */
674
                                                });
675
                                if (m->mount_fd < 0)
40✔
676
                                        return log_debug_errno(m->mount_fd, "Failed to create bind mount inode '%s': %m", m->where);
×
677
                        }
678

679
                        log_debug("Acquired bind mount for layer '%s'.", mount_name(m));
70✔
680
                        break;
681

682
                default:
×
683
                        assert_not_reached();
×
684
                }
685
        }
686

687
        return 0;
688
}
689

690
static int mstack_has_writable_layers(MStack *mstack, MStackFlags flags) {
19✔
691
        assert(mstack);
19✔
692

693
        if (FLAGS_SET(flags, MSTACK_RDONLY))
19✔
694
                return false;
695

696
        FOREACH_ARRAY(m, mstack->mounts, mstack->n_mounts)
42✔
697
                if (m->mount_type == MSTACK_RW)
42✔
698
                        return true;
699

700
        return false;
701
}
702

703
static int fsconfig_add_layer(int sb_fd, const char *key, int layer_fd) {
30✔
704
        int r;
30✔
705

706
        assert(sb_fd >= 0);
30✔
707
        assert(key);
30✔
708
        assert(layer_fd >= 0);
30✔
709

710
        if (DEBUG_LOGGING) {
30✔
711
                _cleanup_free_ char *pretty = NULL;
30✔
712
                (void) fd_get_path(layer_fd, &pretty);
30✔
713
                log_debug("Adding '%s' as layer '%s' to overlayfs.", key, pretty);
30✔
714
        }
715

716
        r = RET_NERRNO(fsconfig(sb_fd, FSCONFIG_SET_FD, key, /* value= */ NULL, layer_fd));
30✔
717
        if (r != -EBADF && !ERRNO_IS_NEG_NOT_SUPPORTED(r))
30✔
718
                return r;
30✔
719

720
        /* overlayfs learnt support for FSCONFIG_SET_FD only with linux 6.13, hence provide a fallback here via /proc/self/fd/ */
721

722
        // FIXME: This compatibility code path shall be removed once kernel 6.13
723
        //        becomes the new minimal baseline
724

725
        const char *layer_path = FORMAT_PROC_FD_PATH(layer_fd);
×
726
        log_debug_errno(r, "FSCONFIG_SET_FD for layer '%s' failed, falling back to FSCONFIG_SET with '%s': %m", key, layer_path);
×
727
        return RET_NERRNO(fsconfig(sb_fd, FSCONFIG_SET_STRING, key, layer_path, /* aux= */ 0));
30✔
728
}
729

730
static int mstack_make_overlayfs(
19✔
731
                MStack *mstack,
732
                const char *temp_mount_dir,
733
                MStackFlags flags,
734
                int *ret_overlayfs_mnt_fd) {
735

736
        int r;
19✔
737

738
        assert(mstack);
19✔
739
        assert(temp_mount_dir);
19✔
740
        assert(ret_overlayfs_mnt_fd);
19✔
741

742
        if (!mstack->has_overlayfs) {
19✔
743
                *ret_overlayfs_mnt_fd = -EBADF;
×
744
                return 0;
×
745
        }
746

747
        bool writable = mstack_has_writable_layers(mstack, flags);
19✔
748

749
        _cleanup_close_ int sb_fd = fsopen("overlay", FSOPEN_CLOEXEC);
22✔
750
        if (sb_fd < 0)
19✔
751
                return log_debug_errno(errno, "Failed to create overlayfs: %m");
×
752

753
        _cleanup_close_pair_ int errno_pipe_fds[2] = EBADF_PAIR;
3✔
754
        if (pipe2(errno_pipe_fds, O_CLOEXEC) < 0)
19✔
755
                return log_debug_errno(errno, "Failed to open pipe: %m");
×
756

757
        /* If we operate unpriv, we have to attach the layers to a place in the fs, before we can pass them
758
         * to overlayfs (see comments below), hence fork off a child with a private mount namespace, so that
759
         * no one else sees that. */
760
        r = pidref_safe_fork("(layerfd)",
19✔
761
                      FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_REOPEN_LOG|FORK_WAIT|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE,
762
                      /* ret= */ NULL);
763
        if (r < 0) {
19✔
764
                errno_pipe_fds[1] = safe_close(errno_pipe_fds[1]);
×
765

766
                int q = read_errno(errno_pipe_fds[0]);
×
767
                if (q < 0 && q != -EIO)
×
768
                        return q;
769

770
                return r;
×
771
        }
772
        if (r == 0) {
19✔
773
                /* child */
774

775
                /* Kernel expects the stack in reverse order, hence go from back to front */
776
                for (size_t i = mstack->n_mounts; i > 0; i--) {
16✔
777
                        MStackMount *m = mstack->mounts + i - 1;
44✔
778

779
                        if (!IN_SET(m->mount_type, MSTACK_RW, MSTACK_LAYER))
44✔
780
                                continue;
8✔
781

782
                        /* overlayfs refuses to work with layers on mounts not owned by our userns, hence create a
783
                         * clone that is owned by our userns */
784
                        _cleanup_close_ int cloned_fd = mount_fd_clone(ASSERT_FD(mount_get_fd(m)), /* recursive= */ false, /* replacement_fd= */ NULL);
84✔
785
                        if (cloned_fd < 0)
24✔
786
                                report_errno_and_exit(errno_pipe_fds[1], cloned_fd);
×
787

788
                        /* When working with detached mounts overlayfs (which requires kernel 6.14) currently
789
                         * insists on upperdir being the root inode of the mount. But that collides with the
790
                         * requirement that upperdir/workdir are on the same mount and siblings. Bummer. To
791
                         * work around this we'll temporarily attach the thing, which relaxes the rules
792
                         * sufficiently. */
793
                        if (move_mount(cloned_fd, "", -EBADF, temp_mount_dir, MOVE_MOUNT_F_EMPTY_PATH) < 0)
24✔
794
                                report_errno_and_exit(errno_pipe_fds[1], -errno);
×
795

796
                        /* Open the layer immediately after attaching */
797
                        _cleanup_close_ int temp_fd = open(temp_mount_dir, O_PATH|O_CLOEXEC);
48✔
798
                        if (temp_fd < 0)
24✔
799
                                report_errno_and_exit(errno_pipe_fds[1], -errno);
×
800

801
                        switch (m->mount_type) {
24✔
802

803
                        case MSTACK_RW: {
12✔
804
                                if (mount_is_ro(m, flags)) {
12✔
805
                                        /* If invoked in read-only mode we'll not create the data dir, but use it if it exists */
806
                                        _cleanup_close_ int data_fd = openat(temp_fd, "data", O_CLOEXEC|O_NOFOLLOW|O_DIRECTORY);
3✔
807
                                        if (data_fd < 0) {
3✔
808
                                                if (errno == ENOENT) /* If the 'data' dir doesn't exist, just skip
3✔
809
                                                                      * over it, it apparently was never created, but
810
                                                                      * that's fine for a read-only invocation */
811
                                                        break;
812

813
                                                log_debug_errno(errno, "Failed to open 'data' directory below 'rw' layer: %m");
×
814
                                                report_errno_and_exit(errno_pipe_fds[1], -errno);
×
815
                                        }
816

817
                                        /* Downgrade to regular lowerdir if read-only is requested */
818
                                        r = fsconfig_add_layer(sb_fd, "lowerdir+", data_fd);
×
819
                                        if (r < 0) {
×
820
                                                log_debug_errno(r, "Failed to set mount layer lowerdir+=%s/data: %m", m->what);
×
821
                                                report_errno_and_exit(errno_pipe_fds[1], r);
×
822
                                        }
823
                                } else {
824
                                        /* If invoked in writable mode, let's create the data dir if it is missing */
825
                                        _cleanup_close_ int data_fd = open_mkdir_at(temp_fd, "data", O_CLOEXEC|O_NOFOLLOW, 0755);
33✔
826
                                        if (data_fd < 0) {
9✔
827
                                                log_debug_errno(data_fd, "Failed to open 'data' directory below 'rw' layer: %m");
×
828
                                                report_errno_and_exit(errno_pipe_fds[1], data_fd);
×
829
                                        }
830

831
                                        r = fsconfig_add_layer(sb_fd, "upperdir", data_fd);
9✔
832
                                        if (r < 0) {
9✔
833
                                                log_debug_errno(r, "Failed to set mount layer upperdir=%s/data: %m", m->what);
×
834
                                                report_errno_and_exit(errno_pipe_fds[1], r);
×
835
                                        }
836

837
                                        /* Similar, create the work directory */
838
                                        _cleanup_close_ int work_fd = open_mkdir_at(temp_fd, "work", O_CLOEXEC|O_NOFOLLOW, 0755);
18✔
839
                                        if (work_fd < 0) {
9✔
840
                                                log_debug_errno(work_fd, "Failed to open 'work' directory below 'rw' layer: %m");
×
841
                                                report_errno_and_exit(errno_pipe_fds[1], work_fd);
×
842
                                        }
843

844
                                        /* rm_rf_children() takes possession of the fd no matter what, let's dup it here */
845
                                        int dup_fd = fcntl(work_fd, F_DUPFD_CLOEXEC, 3);
9✔
846
                                        if (dup_fd < 0) {
9✔
847
                                                log_debug_errno(errno, "Failed to duplicate work fd: %m");
×
848
                                                report_errno_and_exit(errno_pipe_fds[1], -errno);
×
849
                                        }
850

851
                                        /* Empty the work directory, just in case it existed before. It's supposed to be empty. */
852
                                        r = rm_rf_children(dup_fd, REMOVE_PHYSICAL, /* root_dev= */ NULL);
9✔
853
                                        if (r < 0)
9✔
854
                                                log_debug_errno(r, "Failed to empty 'work' directory below 'rw' layer, ignoring: %m");
×
855

856
                                        r = fsconfig_add_layer(sb_fd, "workdir", work_fd);
9✔
857
                                        if (r < 0) {
9✔
858
                                                log_debug_errno(r, "Failed to set mount layer workdir=%s/work: %m", m->what);
×
859
                                                report_errno_and_exit(errno_pipe_fds[1], r);
×
860
                                        }
861

862
                                        break;
9✔
863
                                }
864
                                break;
865
                        }
866

867
                        case MSTACK_LAYER:
12✔
868
                                r = fsconfig_add_layer(sb_fd, "lowerdir+", temp_fd);
12✔
869
                                if (r < 0) {
12✔
870
                                        log_debug_errno(r, "Failed to set mount layer lowerdir+=%s: %m", m->what);
×
871
                                        report_errno_and_exit(errno_pipe_fds[1], r);
×
872
                                }
873

874
                                break;
875

876
                        default:
877
                                break;
878
                        }
879
                }
880

881
                if (!writable && fsconfig(sb_fd, FSCONFIG_SET_FLAG, "ro", /* value= */ NULL, /* aux= */ 0) < 0) {
4✔
882
                        log_debug_errno(errno, "Failed to set read-only mount flag: %m");
×
883
                        report_errno_and_exit(errno_pipe_fds[1], -errno);
×
884
                }
885

886
                if (fsconfig(sb_fd, FSCONFIG_SET_FLAG, "userxattr", /* value= */ NULL, /* aux= */ 0) < 0) {
4✔
887
                        log_debug_errno(errno, "Failed to set userxattr mount flag: %m");
×
888
                        report_errno_and_exit(errno_pipe_fds[1], -errno);
×
889
                }
890

891
                if (fsconfig(sb_fd, FSCONFIG_SET_STRING, "source", mstack->path, /* aux= */ 0) < 0) {
4✔
892
                        log_debug_errno(errno, "Failed to set mount source: %m");
×
893
                        report_errno_and_exit(errno_pipe_fds[1], -errno);
×
894
                }
895

896
                /* This is where the superblock is materialized. It must be called from the child's
897
                 * namespace, where the mounts are attached as described above, otherwise overlayfs is
898
                 * unhappy and will refuse the superblock to be created. */
899
                if (fsconfig(sb_fd, FSCONFIG_CMD_CREATE, /* key= */ NULL, /* value= */ NULL, /* aux= */ 0) < 0) {
4✔
900
                        log_debug_errno(errno, "Failed to realize overlayfs: %m");
×
901
                        report_errno_and_exit(errno_pipe_fds[1], -errno);
×
902
                }
903

904
                report_errno_and_exit(errno_pipe_fds[1], 0);
4✔
905
        }
906

907
        _cleanup_close_ int overlayfs_mnt_fd = fsmount(sb_fd, FSMOUNT_CLOEXEC, 0);
6✔
908
        if (overlayfs_mnt_fd < 0)
3✔
909
                return log_debug_errno(errno, "Failed to create mount fd: %m");
×
910

911
        if (mount_setattr(overlayfs_mnt_fd, "", AT_EMPTY_PATH,
3✔
912
                          &(struct mount_attr) {
3✔
913
                                  .attr_set = writable ? 0 : MOUNT_ATTR_RDONLY,
3✔
914
                                  .attr_clr = writable ? MOUNT_ATTR_RDONLY : 0,
3✔
915
                          }, sizeof(struct mount_attr)) < 0)
916
                return log_debug_errno(errno, "Failed to mark root bind mount read-only: %m");
×
917

918
        *ret_overlayfs_mnt_fd = TAKE_FD(overlayfs_mnt_fd);
3✔
919
        return 1;
3✔
920
}
921

922
int mstack_make_mounts(
19✔
923
                MStack *mstack,
924
                const char *temp_mount_dir,
925
                MStackFlags flags) {
926

927
        int r;
19✔
928

929
        assert(mstack);
19✔
930
        assert(temp_mount_dir);
19✔
931

932
        _cleanup_close_ int overlayfs_mnt_fd = -EBADF;
3✔
933
        r = mstack_make_overlayfs(mstack, temp_mount_dir, flags, &overlayfs_mnt_fd);
19✔
934
        if (r < 0)
3✔
935
                return r;
936
        if (r > 0)
3✔
937
                log_debug("Acquired mstack overlayfs mount.");
3✔
938

939
        assert(mstack->root_mount_fd < 0);
3✔
940
        if (mstack->root_mount) {
3✔
941
                assert(!mstack->has_tmpfs_root);
×
942

943
                mstack->root_mount_fd = fcntl(mount_get_fd(mstack->root_mount), F_DUPFD_CLOEXEC, 3);
×
944
                if (mstack->root_mount_fd < 0)
×
945
                        return log_debug_errno(errno, "Failed to create root bind mount: %m");
×
946

947
                log_debug("Acquired mstack root bind mount.");
×
948

949
        } else if (mstack->has_tmpfs_root) {
3✔
950
                _cleanup_close_ int sb_fd = fsopen("tmpfs", FSOPEN_CLOEXEC);
3✔
951
                if (sb_fd < 0)
×
952
                        return log_debug_errno(errno, "Failed to create tmpfs: %m");
×
953

954
                if (fsconfig(sb_fd, FSCONFIG_SET_STRING, "source", mstack->path, 0) < 0)
×
955
                        return log_debug_errno(errno, "Failed to set mount source: %m");
×
956

957
                if (fsconfig(sb_fd, FSCONFIG_SET_STRING, "mode", "0755", 0) < 0)
×
958
                        return log_debug_errno(errno, "Failed to set mount source: %m");
×
959

960
                if (fsconfig(sb_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0) < 0)
×
961
                        return log_debug_errno(errno, "Failed to realize tmpfs: %m");
×
962

963
                mstack->root_mount_fd = fsmount(sb_fd, FSMOUNT_CLOEXEC, 0);
×
964
                if (mstack->root_mount_fd < 0)
×
965
                        return log_debug_errno(errno, "Failed to create mount fd: %m");
×
966

967
                log_debug("Acquired root tmpfs mount.");
×
968
        }
969

970
        if (mstack->root_mount_fd >= 0 && overlayfs_mnt_fd >= 0) {
3✔
971
                /* If we have an overlayfs and a root fs, then the overlayfs should be placed on /usr/. */
972
                mstack->usr_mount_fd = open_tree(overlayfs_mnt_fd, "usr", OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC|AT_SYMLINK_NOFOLLOW);
×
973
                if (mstack->usr_mount_fd < 0)
×
974
                        return log_debug_errno(errno, "Failed to create bind mount inode '/usr/': %m");
×
975

976
                if (mount_setattr(mstack->usr_mount_fd, "", AT_EMPTY_PATH,
×
977
                                  &(struct mount_attr) {
×
978
                                          .attr_set = mount_is_ro(mstack->root_mount, flags) ? MOUNT_ATTR_RDONLY : 0,
×
979
                                          .attr_clr = mount_is_ro(mstack->root_mount, flags) ? 0 : MOUNT_ATTR_RDONLY,
×
980
                                          .propagation = MS_PRIVATE, /* disconnect us from bind mount source */
981
                                  }, sizeof(struct mount_attr)) < 0)
982
                        return log_debug_errno(errno, "Failed to mark usr bind mount read-only: %m");
×
983

984
                log_debug("Acquired mstack overlayfs '/usr/' submount.");
×
985
        }
986

987
        /* If we acquired no other root fs, then the overlayfs is our root */
988
        if (mstack->root_mount_fd < 0)
3✔
989
                mstack->root_mount_fd = TAKE_FD(overlayfs_mnt_fd);
3✔
990

991
        return 0;
992
}
993

994
int mstack_bind_mounts(
3✔
995
                MStack *mstack,
996
                const char *where,
997
                int where_fd,
998
                MStackFlags flags,
999
                int *ret_root_fd) {
1000

1001
        int r;
3✔
1002

1003
        assert(mstack);
3✔
1004

1005
        _cleanup_close_ int _where_fd = -EBADF;
3✔
1006
        if (where_fd == AT_FDCWD) {
3✔
1007
                _where_fd = open(".", O_CLOEXEC|O_PATH|O_DIRECTORY);
×
1008
                if (_where_fd < 0)
×
1009
                        return log_debug_errno(errno, "Failed to open current working directory: %m");
×
1010
                where_fd = _where_fd;
1011
        } else if (where_fd < 0) {
3✔
1012
                r = chase(where,
6✔
1013
                          /* root= */ NULL,
1014
                          (FLAGS_SET(flags, MSTACK_MKDIR) ? CHASE_MKDIR_0755 : 0)|CHASE_MUST_BE_DIRECTORY,
3✔
1015
                          /* ret_path= */ NULL,
1016
                          &_where_fd);
1017
                if (r < 0)
3✔
1018
                        return log_debug_errno(r, "Failed to open '%s': %m", where);
×
1019

1020
                where_fd = _where_fd;
3✔
1021
        }
1022

1023
        assert(mstack->root_mount_fd >= 0);
3✔
1024
        if (move_mount(mstack->root_mount_fd, "", where_fd, "", MOVE_MOUNT_F_EMPTY_PATH|MOVE_MOUNT_T_EMPTY_PATH) < 0)
3✔
1025
                return log_debug_errno(errno, "Failed to attach mstack root mount to '%s': %m", where);
×
1026

1027
        log_debug("Attached mstack root mount to '%s'.", where);
3✔
1028

1029
        _cleanup_close_ int root_fd = open(where, O_CLOEXEC|O_PATH|O_DIRECTORY|O_NOFOLLOW);
6✔
1030
        if (root_fd < 0)
3✔
1031
                return log_debug_errno(errno, "Failed to mount root mount '%s': %m", where);
×
1032

1033
        if (mstack->usr_mount_fd >= 0) {
3✔
1034
                _cleanup_close_ int subdir_fd = -EBADF;
×
1035
                r = chaseat(root_fd, "usr", CHASE_AT_RESOLVE_IN_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_MKDIR_0755|CHASE_MUST_BE_DIRECTORY, /* ret_path= */ NULL, &subdir_fd);
×
1036
                if (r < 0)
×
1037
                        return log_debug_errno(r, "Failed to open mount point inode '%s': %m", where);
×
1038

1039
                if (move_mount(mstack->usr_mount_fd, "", subdir_fd, "", MOVE_MOUNT_F_EMPTY_PATH|MOVE_MOUNT_T_EMPTY_PATH) < 0)
×
1040
                        return log_debug_errno(errno, "Failed to attach bind mount to '/usr/' subdir: %m");
×
1041

1042
                log_debug("Attached mstack '/usr/' mount to '%s/usr/'.", where);
×
1043
        }
1044

1045
        FOREACH_ARRAY(m, mstack->mounts, mstack->n_mounts) {
14✔
1046

1047
                if (!IN_SET(m->mount_type, MSTACK_BIND, MSTACK_ROBIND) ||
11✔
1048
                    m == mstack->root_mount)
2✔
1049
                        continue;
9✔
1050

1051
                assert(m->mount_fd >= 0);
2✔
1052

1053
                _cleanup_close_ int subdir_fd = -EBADF;
2✔
1054
                r = chaseat(root_fd, m->where, CHASE_AT_RESOLVE_IN_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_MKDIR_0755|CHASE_MUST_BE_DIRECTORY, /* ret_path= */ NULL, &subdir_fd);
2✔
1055
                if (r < 0)
2✔
1056
                        return log_debug_errno(r, "Failed to open mount point inode '%s': %m", m->where);
×
1057

1058
                if (move_mount(m->mount_fd, "", subdir_fd, "", MOVE_MOUNT_F_EMPTY_PATH|MOVE_MOUNT_T_EMPTY_PATH) < 0)
2✔
1059
                        return log_debug_errno(errno, "Failed to attach bind mount to '%s' subdir: %m", m->where);
×
1060

1061
                log_debug("Attached mstack '%s/' mount to '%s/%s/'.", m->where, where, m->where);
2✔
1062
        }
1063

1064
        /* If we have a tmpfs root, the above might have created mount point inodes. Hence we left the tmpfs
1065
         * writable for that. Let's fix that now. Also, let's enable propagation for the future. (Reminder:
1066
         * we disconnect propagation from the host, but we *want* propagation by default for everything
1067
         * created further down the tree. Hence we'll set MS_SHARED here right-away.) */
1068
        if (mount_setattr(root_fd, "", AT_EMPTY_PATH|AT_RECURSIVE,
3✔
1069
                          &(struct mount_attr) {
3✔
1070
                                  .attr_set = FLAGS_SET(flags, MSTACK_RDONLY) ? MOUNT_ATTR_RDONLY : 0,
3✔
1071
                                  .attr_clr = FLAGS_SET(flags, MSTACK_RDONLY) ? 0 : MOUNT_ATTR_RDONLY,
3✔
1072
                                  .propagation = MS_SHARED,
1073
                          }, sizeof(struct mount_attr)) < 0)
1074
                return log_debug_errno(errno, "Failed to mark root bind mount read-only: %m");
×
1075

1076
        if (ret_root_fd)
3✔
1077
                *ret_root_fd = TAKE_FD(root_fd);
1✔
1078

1079
        return 0;
1080
}
1081

1082
int mstack_apply(
5✔
1083
                const char *dir,
1084
                int dir_fd,
1085
                const char *where,
1086
                const char *temp_mount_dir,
1087
                sd_varlink *link,
1088
                int userns_fd,
1089
                const ImagePolicy *image_policy,
1090
                const ImageFilter *image_filter,
1091
                MStackFlags flags,
1092
                int *ret_root_fd) {
1093
        int r;
5✔
1094

1095
        assert(where);
5✔
1096

1097
        _cleanup_(mstack_done) MStack mstack = MSTACK_INIT;
1✔
1098
        r = mstack_load_now(&mstack, dir, dir_fd, flags);
5✔
1099
        if (r < 0)
5✔
1100
                return r;
1101

1102
        r = mstack_open_images(&mstack, link, userns_fd, image_policy, image_filter, flags);
5✔
1103
        if (r < 0)
5✔
1104
                return r;
1105

1106
        _cleanup_(rmdir_and_freep) char *t = NULL;
1✔
1107
        if (!temp_mount_dir) {
5✔
1108
                r = mkdtemp_malloc("/tmp/mstack-temporary-XXXXXX", &t);
5✔
1109
                if (r < 0)
5✔
1110
                        return r;
1111

1112
                temp_mount_dir = t;
5✔
1113
        }
1114

1115
        r = mstack_make_mounts(&mstack, temp_mount_dir, flags);
5✔
1116
        if (r < 0)
1✔
1117
                return r;
1118

1119
        return mstack_bind_mounts(&mstack, where, /* where_fd= */ -EBADF, flags, ret_root_fd);
1✔
1120
}
1121

1122
int mstack_load(const char *dir, int dir_fd, MStack **ret) {
31✔
1123
        int r;
31✔
1124

1125
        assert(ret);
31✔
1126

1127
        /* Well-known errors:
1128
         *
1129
         *     -ENOTUNIQ → Multiple conflicting layers for the same path defined
1130
         *     -EBADMSG  → Bad file suffix, inode type for layer, or unrecognized entry
1131
         */
1132

1133
        _cleanup_(mstack_freep) MStack *mstack = new(MStack, 1);
62✔
1134
        if (!mstack)
31✔
1135
                return -ENOMEM;
1136

1137
        *mstack = MSTACK_INIT;
31✔
1138

1139
        r = mstack_load_now(mstack, dir, dir_fd, /* flags= */ 0);
31✔
1140
        if (r < 0)
31✔
1141
                return r;
1142

1143
        *ret = TAKE_PTR(mstack);
31✔
1144
        return 0;
31✔
1145
}
1146

1147
int mstack_is_read_only(MStack *mstack) {
15✔
1148
        assert(mstack);
15✔
1149

1150
        /* Checks if the mstack consists of only read-only layers and bind mounts */
1151

1152
        if (mstack->has_tmpfs_root)
15✔
1153
                return false;
1154

1155
        FOREACH_ARRAY(m, mstack->mounts, mstack->n_mounts)
45✔
1156
                if (IN_SET(m->mount_type, MSTACK_ROOT, MSTACK_RW, MSTACK_BIND))
45✔
1157
                        return false;
1158

1159
        return true;
1160
}
1161

1162
int mstack_is_foreign_uid_owned(MStack *mstack) {
15✔
1163
        int r;
15✔
1164

1165
        assert(mstack);
15✔
1166

1167
        /* Checks if any of the layers are owned by the host's foreign UID range */
1168

1169
        FOREACH_ARRAY(m, mstack->mounts, mstack->n_mounts) {
45✔
1170

1171
                if (!IN_SET(m->image_type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME))
39✔
1172
                        continue;
×
1173

1174
                assert(m->what_fd >= 0);
39✔
1175

1176
                struct stat st;
39✔
1177
                if (fstat(m->what_fd, &st) < 0)
39✔
1178
                        return -errno;
9✔
1179

1180
                r = stat_verify_directory(&st);
39✔
1181
                if (r < 0)
39✔
1182
                        return r;
1183

1184
                if (uid_is_foreign(st.st_uid))
39✔
1185
                        return true;
1186
        }
1187

1188
        return false;
1189
}
1190

1191
static const char *const mstack_mount_type_table[] = {
1192
        [MSTACK_ROOT]   = "root",
1193
        [MSTACK_LAYER]  = "layer",
1194
        [MSTACK_RW]     = "rw",
1195
        [MSTACK_BIND]   = "bind",
1196
        [MSTACK_ROBIND] = "robind",
1197
};
1198

1199
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(mstack_mount_type, MStackMountType);
23✔
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