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

systemd / systemd / 15288324789

27 May 2025 07:40PM UTC coverage: 71.981% (-0.07%) from 72.046%
15288324789

push

github

yuwata
timedate: print better errors when systemd-timesyncd.service unavailable

If the error is a common bus error indicating the service is not
available, print a more user-friendly message indicating so.

0 of 7 new or added lines in 1 file covered. (0.0%)

3467 existing lines in 62 files now uncovered.

299170 of 415625 relevant lines covered (71.98%)

704053.27 hits per line

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

0.0
/src/mountfsd/mountwork.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <linux/loop.h>
4
#include <poll.h>
5
#include <stdlib.h>
6
#include <sys/mount.h>
7

8
#include "sd-daemon.h"
9
#include "sd-event.h"
10
#include "sd-varlink.h"
11

12
#include "argv-util.h"
13
#include "bus-polkit.h"
14
#include "chase.h"
15
#include "discover-image.h"
16
#include "dissect-image.h"
17
#include "env-util.h"
18
#include "errno-util.h"
19
#include "fd-util.h"
20
#include "fs-util.h"
21
#include "format-util.h"
22
#include "hashmap.h"
23
#include "image-policy.h"
24
#include "io-util.h"
25
#include "json-util.h"
26
#include "loop-util.h"
27
#include "main-func.h"
28
#include "memory-util.h"
29
#include "namespace-util.h"
30
#include "nsresource.h"
31
#include "nulstr-util.h"
32
#include "os-util.h"
33
#include "path-util.h"
34
#include "pidref.h"
35
#include "stat-util.h"
36
#include "string-table.h"
37
#include "string-util.h"
38
#include "strv.h"
39
#include "tmpfile-util.h"
40
#include "time-util.h"
41
#include "uid-classification.h"
42
#include "uid-range.h"
43
#include "varlink-io.systemd.MountFileSystem.h"
44
#include "varlink-util.h"
45

46
#define ITERATIONS_MAX 64U
47
#define RUNTIME_MAX_USEC (5 * USEC_PER_MINUTE)
48
#define PRESSURE_SLEEP_TIME_USEC (50 * USEC_PER_MSEC)
49
#define LISTEN_IDLE_USEC (90 * USEC_PER_SEC)
50

51
static const ImagePolicy image_policy_untrusted = {
52
        .n_policies = 2,
53
        .policies = {
54
                { PARTITION_ROOT,     PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT },
55
                { PARTITION_USR,      PARTITION_POLICY_SIGNED|PARTITION_POLICY_ABSENT },
56
        },
57
        .default_flags = PARTITION_POLICY_IGNORE,
58
};
59

60
static int json_dispatch_image_policy(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
×
61
        _cleanup_(image_policy_freep) ImagePolicy *q = NULL;
×
UNCOV
62
        ImagePolicy **p = ASSERT_PTR(userdata);
×
63
        int r;
×
64

65
        assert(p);
×
66

67
        if (sd_json_variant_is_null(variant)) {
×
UNCOV
68
                *p = image_policy_free(*p);
×
UNCOV
69
                return 0;
×
70
        }
71

UNCOV
72
        if (!sd_json_variant_is_string(variant))
×
73
                return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name));
×
74

75
        r = image_policy_from_string(sd_json_variant_string(variant), &q);
×
UNCOV
76
        if (r < 0)
×
77
                return json_log(variant, flags, r, "JSON field '%s' is not a valid image policy.", strna(name));
×
78

79
        image_policy_free(*p);
×
UNCOV
80
        *p = TAKE_PTR(q);
×
UNCOV
81
        return 0;
×
82
}
83

84
typedef struct MountImageParameters {
85
        unsigned image_fd_idx;
86
        unsigned userns_fd_idx;
87
        int read_only;
88
        int growfs;
89
        char *password;
90
        ImagePolicy *image_policy;
91
} MountImageParameters;
92

UNCOV
93
static void mount_image_parameters_done(MountImageParameters *p) {
×
94
        assert(p);
×
95

96
        p->password = erase_and_free(p->password);
×
UNCOV
97
        p->image_policy = image_policy_free(p->image_policy);
×
98
}
×
99

UNCOV
100
static int validate_image_fd(int fd, MountImageParameters *p) {
×
101
        int r, fl;
×
102

UNCOV
103
        assert(fd >= 0);
×
104
        assert(p);
×
105

UNCOV
106
        r = fd_verify_regular(fd);
×
UNCOV
107
        if (r < 0)
×
108
                return r;
109

110
        fl = fd_verify_safe_flags(fd);
×
UNCOV
111
        if (fl < 0)
×
112
                return log_debug_errno(fl, "Image file descriptor has unsafe flags set: %m");
×
113

114
        switch (fl & O_ACCMODE_STRICT) {
×
115

116
        case O_RDONLY:
×
UNCOV
117
                p->read_only = true;
×
UNCOV
118
                break;
×
119

120
        case O_RDWR:
121
                break;
122

123
        default:
124
                return -EBADF;
125
        }
126

127
        return 0;
128
}
129

130
static int verify_trusted_image_fd_by_path(int fd) {
×
131
        _cleanup_free_ char *p = NULL;
×
UNCOV
132
        struct stat sta;
×
133
        int r;
×
134

135
        assert(fd >= 0);
×
136

137
        r = secure_getenv_bool("SYSTEMD_MOUNTFSD_TRUSTED_DIRECTORIES");
×
138
        if (r == -ENXIO)  {
×
139
                if (!DEFAULT_MOUNTFSD_TRUSTED_DIRECTORIES) {
×
UNCOV
140
                        log_debug("Trusted directory mechanism disabled at compile time.");
×
141
                        return false;
×
142
                }
143
        } else if (r < 0) {
×
144
                log_debug_errno(r, "Failed to parse $SYSTEMD_MOUNTFSD_TRUSTED_DIRECTORIES environment variable, not trusting any image.");
×
145
                return false;
×
146
        } else if (!r) {
×
UNCOV
147
                log_debug("Trusted directory mechanism disabled via $SYSTEMD_MOUNTFSD_TRUSTED_DIRECTORIES environment variable.");
×
UNCOV
148
                return false;
×
149
        }
150

151
        r = fd_get_path(fd, &p);
×
152
        if (r < 0)
×
153
                return log_debug_errno(r, "Failed to get path of passed image file descriptor: %m");
×
UNCOV
154
        if (fstat(fd, &sta) < 0)
×
155
                return log_debug_errno(errno, "Failed to stat() passed image file descriptor: %m");
×
156

157
        log_debug("Checking if image '%s' is in trusted directories.", p);
×
158

159
        for (ImageClass c = 0; c < _IMAGE_CLASS_MAX; c++)
×
160
                NULSTR_FOREACH(s, image_search_path[c]) {
×
161
                        _cleanup_close_ int dir_fd = -EBADF, inode_fd = -EBADF;
×
162
                        _cleanup_free_ char *q = NULL;
×
UNCOV
163
                        struct stat stb;
×
164
                        const char *e;
×
165

166
                        r = chase(s, NULL, CHASE_SAFE, &q, &dir_fd);
×
167
                        if (r == -ENOENT)
×
168
                                continue;
×
169
                        if (r < 0) {
×
UNCOV
170
                                log_warning_errno(r, "Failed to resolve search path '%s', ignoring: %m", s);
×
UNCOV
171
                                continue;
×
172
                        }
173

174
                        /* Check that the inode refers to a file immediately inside the image directory,
175
                         * i.e. not the image directory itself, and nothing further down the tree */
176
                        e = path_startswith(p, q);
×
UNCOV
177
                        if (isempty(e))
×
178
                                continue;
×
179

180
                        e += strspn(e, "/");
×
UNCOV
181
                        if (!filename_is_valid(e))
×
182
                                continue;
×
183

184
                        r = chaseat(dir_fd, e, CHASE_SAFE, NULL, &inode_fd);
×
UNCOV
185
                        if (r < 0)
×
186
                                return log_error_errno(r, "Couldn't verify that specified image '%s' is in search path '%s': %m", p, s);
×
187

UNCOV
188
                        if (fstat(inode_fd, &stb) < 0)
×
189
                                return log_error_errno(errno, "Failed to stat image file '%s/%s': %m", q, e);
×
190

191
                        if (stat_inode_same(&sta, &stb)) {
×
UNCOV
192
                                log_debug("Image '%s' is *in* trusted directories.", p);
×
UNCOV
193
                                return true; /* Yay */
×
194
                        }
195
                }
196

UNCOV
197
        log_debug("Image '%s' is *not* in trusted directories.", p);
×
198
        return false;
199
}
200

UNCOV
201
static int determine_image_policy(
×
202
                int image_fd,
203
                bool trusted,
204
                ImagePolicy *client_policy,
205
                ImagePolicy **ret) {
206

207
        _cleanup_(image_policy_freep) ImagePolicy *envvar_policy = NULL;
×
208
        const ImagePolicy *default_policy;
×
UNCOV
209
        const char *envvar, *e;
×
210
        int r;
×
211

UNCOV
212
        assert(image_fd >= 0);
×
213
        assert(ret);
×
214

UNCOV
215
        if (trusted) {
×
216
                envvar = "SYSTEMD_MOUNTFSD_IMAGE_POLICY_TRUSTED";
217
                default_policy = &image_policy_allow;
218
        } else {
UNCOV
219
                envvar = "SYSTEMD_MOUNTFSD_IMAGE_POLICY_UNTRUSTED";
×
UNCOV
220
                default_policy = &image_policy_untrusted;
×
221
        }
222

223
        e = secure_getenv(envvar);
×
224
        if (e) {
×
225
                r = image_policy_from_string(e, &envvar_policy);
×
UNCOV
226
                if (r < 0)
×
227
                        return log_error_errno(r, "Failed to parse image policy supplied via $%s: %m", envvar);
×
228

UNCOV
229
                default_policy = envvar_policy;
×
230
        }
231

UNCOV
232
        return image_policy_intersect(default_policy, client_policy, ret);
×
233
}
234

UNCOV
235
static int validate_userns(sd_varlink *link, int *userns_fd) {
×
236
        int r;
×
237

UNCOV
238
        assert(link);
×
239
        assert(userns_fd);
×
240

UNCOV
241
        if (*userns_fd < 0)
×
242
                return 0;
243

244
        r = fd_verify_safe_flags(*userns_fd);
×
UNCOV
245
        if (r < 0)
×
246
                return log_debug_errno(r, "User namespace file descriptor has unsafe flags set: %m");
×
247

UNCOV
248
        r = fd_is_namespace(*userns_fd, NAMESPACE_USER);
×
249
        if (r < 0)
×
250
                return r;
UNCOV
251
        if (r == 0)
×
UNCOV
252
                return sd_varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
×
253

254
        /* Our own host user namespace? Then close the fd, and handle it as if none was specified. */
255
        r = is_our_namespace(*userns_fd, NAMESPACE_USER);
×
256
        if (r < 0)
×
257
                return log_debug_errno(r, "Failed to determine if user namespace provided by client is our own.");
×
258
        if (r > 0) {
×
UNCOV
259
                log_debug("User namespace provided by client is our own.");
×
UNCOV
260
                *userns_fd = safe_close(*userns_fd);
×
261
        }
262

263
        return 0;
264
}
265

UNCOV
266
static int vl_method_mount_image(
×
267
                sd_varlink *link,
268
                sd_json_variant *parameters,
269
                sd_varlink_method_flags_t flags,
270
                void *userdata) {
271

UNCOV
272
        static const sd_json_dispatch_field dispatch_table[] = {
×
273
                { "imageFileDescriptor",         SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint,      offsetof(MountImageParameters, image_fd_idx),  SD_JSON_MANDATORY },
274
                { "userNamespaceFileDescriptor", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint,      offsetof(MountImageParameters, userns_fd_idx), 0 },
275
                { "readOnly",                    SD_JSON_VARIANT_BOOLEAN,  sd_json_dispatch_tristate,  offsetof(MountImageParameters, read_only),     0 },
276
                { "growFileSystems",             SD_JSON_VARIANT_BOOLEAN,  sd_json_dispatch_tristate,  offsetof(MountImageParameters, growfs),        0 },
277
                { "password",                    SD_JSON_VARIANT_STRING,   sd_json_dispatch_string,    offsetof(MountImageParameters, password),      0 },
278
                { "imagePolicy",                 SD_JSON_VARIANT_STRING,   json_dispatch_image_policy, offsetof(MountImageParameters, image_policy),  0 },
279
                VARLINK_DISPATCH_POLKIT_FIELD,
280
                {}
281
        };
282

UNCOV
283
        _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
×
UNCOV
284
        _cleanup_(mount_image_parameters_done) MountImageParameters p = {
×
285
                .image_fd_idx = UINT_MAX,
286
                .userns_fd_idx = UINT_MAX,
287
                .read_only = -1,
288
                .growfs = -1,
289
        };
290
        _cleanup_(dissected_image_unrefp) DissectedImage *di = NULL;
×
291
        _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL;
×
292
        _cleanup_(sd_json_variant_unrefp) sd_json_variant *aj = NULL;
×
293
        _cleanup_close_ int image_fd = -EBADF, userns_fd = -EBADF;
×
294
        _cleanup_(image_policy_freep) ImagePolicy *use_policy = NULL;
×
295
        Hashmap **polkit_registry = ASSERT_PTR(userdata);
×
296
        _cleanup_free_ char *ps = NULL;
×
UNCOV
297
        bool image_is_trusted = false;
×
298
        int r;
×
299

UNCOV
300
        assert(link);
×
301
        assert(parameters);
×
302

303
        sd_json_variant_sensitive(parameters); /* might contain passwords */
×
304

UNCOV
305
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
×
UNCOV
306
        if (r != 0)
×
307
                return r;
308

309
        if (p.image_fd_idx != UINT_MAX) {
×
310
                image_fd = sd_varlink_peek_dup_fd(link, p.image_fd_idx);
×
UNCOV
311
                if (image_fd < 0)
×
UNCOV
312
                        return log_debug_errno(image_fd, "Failed to peek image fd from client: %m");
×
313
        }
314

315
        if (p.userns_fd_idx != UINT_MAX) {
×
316
                userns_fd = sd_varlink_peek_dup_fd(link, p.userns_fd_idx);
×
UNCOV
317
                if (userns_fd < 0)
×
UNCOV
318
                        return log_debug_errno(userns_fd, "Failed to peek user namespace fd from client: %m");
×
319
        }
320

UNCOV
321
        r = validate_image_fd(image_fd, &p);
×
UNCOV
322
        if (r < 0)
×
323
                return r;
324

UNCOV
325
        r = validate_userns(link, &userns_fd);
×
UNCOV
326
        if (r != 0)
×
327
                return r;
328

UNCOV
329
        r = verify_trusted_image_fd_by_path(image_fd);
×
330
        if (r < 0)
×
331
                return r;
332
        image_is_trusted = r;
×
333

UNCOV
334
        const char *polkit_details[] = {
×
UNCOV
335
                "read_only", one_zero(p.read_only > 0),
×
336
                NULL,
337
        };
338

339
        const char *polkit_action, *polkit_untrusted_action;
×
UNCOV
340
        PolkitFlags polkit_flags;
×
UNCOV
341
        if (userns_fd < 0) {
×
342
                /* Mount into the host user namespace */
343
                polkit_action = "io.systemd.mount-file-system.mount-image";
344
                polkit_untrusted_action = "io.systemd.mount-file-system.mount-untrusted-image";
345
                polkit_flags = 0;
346
        } else {
347
                /* Mount into a private user namespace */
UNCOV
348
                polkit_action = "io.systemd.mount-file-system.mount-image-privately";
×
UNCOV
349
                polkit_untrusted_action = "io.systemd.mount-file-system.mount-untrusted-image-privately";
×
350

351
                /* If polkit is not around, let's allow mounting authenticated images by default */
UNCOV
352
                polkit_flags = POLKIT_DEFAULT_ALLOW;
×
353
        }
354

355
        /* Let's definitely acquire the regular action privilege, for mounting properly signed images */
UNCOV
356
        r = varlink_verify_polkit_async_full(
×
357
                        link,
358
                        /* bus= */ NULL,
359
                        polkit_action,
360
                        polkit_details,
361
                        /* good_user= */ UID_INVALID,
362
                        polkit_flags,
363
                        polkit_registry);
UNCOV
364
        if (r <= 0)
×
365
                return r;
366

367
        /* Generate the common dissection directory here. We are not going to use it, but the clients might,
368
         * and they likely are unprivileged, hence cannot create it themselves. Hence let's just create it
369
         * here, if it is missing. */
UNCOV
370
        r = get_common_dissect_directory(NULL);
×
UNCOV
371
        if (r < 0)
×
372
                return r;
373

374
        r = loop_device_make(
×
375
                        image_fd,
UNCOV
376
                        p.read_only == 0 ? O_RDONLY : O_RDWR,
×
377
                        0,
378
                        UINT64_MAX,
379
                        UINT32_MAX,
380
                        LO_FLAGS_PARTSCAN,
381
                        LOCK_EX,
382
                        &loop);
UNCOV
383
        if (r < 0)
×
384
                return r;
385

386
        DissectImageFlags dissect_flags =
×
UNCOV
387
                (p.read_only == 0 ? DISSECT_IMAGE_READ_ONLY : 0) |
×
UNCOV
388
                (p.growfs != 0 ? DISSECT_IMAGE_GROWFS : 0) |
×
389
                DISSECT_IMAGE_DISCARD_ANY |
390
                DISSECT_IMAGE_FSCK |
391
                DISSECT_IMAGE_ADD_PARTITION_DEVICES |
UNCOV
392
                DISSECT_IMAGE_PIN_PARTITION_DEVICES |
×
393
                DISSECT_IMAGE_ALLOW_USERSPACE_VERITY;
394

395
        /* Let's see if we have acquired the privilege to mount untrusted images already */
UNCOV
396
        bool polkit_have_untrusted_action =
×
397
                varlink_has_polkit_action(link, polkit_untrusted_action, polkit_details, polkit_registry);
×
398

399
        for (;;) {
×
UNCOV
400
                use_policy = image_policy_free(use_policy);
×
UNCOV
401
                ps = mfree(ps);
×
402

403
                /* We use the image policy for trusted images if either the path is below a trusted
404
                 * directory, or if we have already acquired a PK authentication that tells us that untrusted
405
                 * images are OK */
UNCOV
406
                bool use_trusted_policy =
×
407
                        image_is_trusted ||
408
                        polkit_have_untrusted_action;
409

UNCOV
410
                r = determine_image_policy(
×
411
                                image_fd,
412
                                use_trusted_policy,
413
                                p.image_policy,
414
                                &use_policy);
UNCOV
415
                if (r < 0)
×
416
                        return r;
417

UNCOV
418
                r = image_policy_to_string(use_policy, /* simplify= */ true, &ps);
×
UNCOV
419
                if (r < 0)
×
420
                        return r;
421

422
                log_debug("Using image policy: %s", ps);
×
423

UNCOV
424
                r = dissect_loop_device(
×
425
                                loop,
426
                                &verity,
427
                                /* mount_options= */ NULL,
428
                                use_policy,
429
                                /* image_filter= */ NULL,
430
                                dissect_flags,
431
                                &di);
432
                if (r == -ENOPKG)
×
433
                        return sd_varlink_error(link, "io.systemd.MountFileSystem.IncompatibleImage", NULL);
×
434
                if (r == -ENOTUNIQ)
×
435
                        return sd_varlink_error(link, "io.systemd.MountFileSystem.MultipleRootPartitionsFound", NULL);
×
436
                if (r == -ENXIO)
×
UNCOV
437
                        return sd_varlink_error(link, "io.systemd.MountFileSystem.RootPartitionNotFound", NULL);
×
UNCOV
438
                if (r == -ERFKILL) {
×
439
                        /* The image policy refused this, let's retry after trying to get PolicyKit */
440

441
                        if (!polkit_have_untrusted_action) {
×
UNCOV
442
                                log_debug("Denied by image policy. Trying a stronger polkit authentication before continuing.");
×
UNCOV
443
                                r = varlink_verify_polkit_async_full(
×
444
                                                link,
445
                                                /* bus= */ NULL,
446
                                                polkit_untrusted_action,
447
                                                polkit_details,
448
                                                /* good_user= */ UID_INVALID,
449
                                                /* flags= */ 0,                   /* NB: the image cannot be authenticated, hence unless PK is around to allow this anyway, fail! */
450
                                                polkit_registry);
451
                                if (r <= 0 && !ERRNO_IS_NEG_PRIVILEGE(r))
×
452
                                        return r;
453
                                if (r > 0) {
×
454
                                        /* Try again, now that we know the client has enough privileges. */
455
                                        log_debug("Denied by image policy, retrying after polkit authentication.");
×
UNCOV
456
                                        polkit_have_untrusted_action = true;
×
UNCOV
457
                                        continue;
×
458
                                }
459
                        }
460

461
                        return sd_varlink_error(link, "io.systemd.MountFileSystem.DeniedByImagePolicy", NULL);
×
462
                }
UNCOV
463
                if (r < 0)
×
464
                        return r;
465

466
                /* Success */
UNCOV
467
                break;
×
468
        }
469

470
        r = dissected_image_load_verity_sig_partition(
×
471
                        di,
472
                        loop->fd,
×
473
                        &verity);
UNCOV
474
        if (r < 0)
×
475
                return r;
476

UNCOV
477
        r = dissected_image_guess_verity_roothash(
×
478
                        di,
479
                        &verity);
UNCOV
480
        if (r < 0)
×
481
                return r;
482

483
        r = dissected_image_decrypt(
×
484
                        di,
UNCOV
485
                        p.password,
×
486
                        &verity,
487
                        dissect_flags);
488
        if (r == -ENOKEY) /* new dm-verity userspace returns ENOKEY if the dm-verity signature key is not in
×
489
                           * key chain. That's great. */
UNCOV
490
                return sd_varlink_error(link, "io.systemd.MountFileSystem.KeyNotFound", NULL);
×
UNCOV
491
        if (r == -EBUSY) /* DM kernel subsystem is shit with returning useful errors hence we keep retrying
×
492
                          * under the assumption that some errors are transitional. Which the errors might
493
                          * not actually be. After all retries failed we return EBUSY. Let's turn that into a
494
                          * generic Verity error. It's not very helpful, could mean anything, but at least it
495
                          * gives client a clear idea that this has to do with Verity. */
UNCOV
496
                return sd_varlink_error(link, "io.systemd.MountFileSystem.VerityFailure", NULL);
×
UNCOV
497
        if (r < 0)
×
498
                return r;
499

UNCOV
500
        r = dissected_image_mount(
×
501
                        di,
502
                        /* where= */ NULL,
503
                        /* uid_shift= */ UID_INVALID,
504
                        /* uid_range= */ UID_INVALID,
505
                        userns_fd,
506
                        dissect_flags);
UNCOV
507
        if (r < 0)
×
508
                return r;
509

510
        for (PartitionDesignator d = 0; d < _PARTITION_DESIGNATOR_MAX; d++) {
×
UNCOV
511
                DissectedPartition *pp = di->partitions + d;
×
512
                int fd_idx;
×
513

UNCOV
514
                if (!pp->found)
×
515
                        continue;
×
516

UNCOV
517
                if (pp->fsmount_fd < 0)
×
518
                        continue;
×
519

520
                if (userns_fd >= 0) {
×
521
                        r = nsresource_add_mount(userns_fd, pp->fsmount_fd);
×
UNCOV
522
                        if (r < 0)
×
UNCOV
523
                                return r;
×
524
                }
525

UNCOV
526
                fd_idx = sd_varlink_push_fd(link, pp->fsmount_fd);
×
UNCOV
527
                if (fd_idx < 0)
×
528
                        return fd_idx;
529

530
                TAKE_FD(pp->fsmount_fd);
×
531

532
                const char *m = partition_mountpoint_to_string(d);
×
533
                _cleanup_strv_free_ char **l = NULL;
×
534
                if (!isempty(m)) {
×
535
                        l = strv_split_nulstr(m);
×
UNCOV
536
                        if (!l)
×
UNCOV
537
                                return log_oom_debug();
×
538
                }
539

UNCOV
540
                r = sd_json_variant_append_arraybo(
×
541
                                &aj,
542
                                SD_JSON_BUILD_PAIR("designator", SD_JSON_BUILD_STRING(partition_designator_to_string(d))),
543
                                SD_JSON_BUILD_PAIR("writable", SD_JSON_BUILD_BOOLEAN(pp->rw)),
544
                                SD_JSON_BUILD_PAIR("growFileSystem", SD_JSON_BUILD_BOOLEAN(pp->growfs)),
545
                                SD_JSON_BUILD_PAIR_CONDITION(pp->partno > 0, "partitionNumber", SD_JSON_BUILD_INTEGER(pp->partno)),
546
                                SD_JSON_BUILD_PAIR_CONDITION(pp->architecture > 0, "architecture", SD_JSON_BUILD_STRING(architecture_to_string(pp->architecture))),
547
                                SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(pp->uuid), "partitionUuid", SD_JSON_BUILD_UUID(pp->uuid)),
548
                                SD_JSON_BUILD_PAIR("fileSystemType", SD_JSON_BUILD_STRING(dissected_partition_fstype(pp))),
549
                                SD_JSON_BUILD_PAIR_CONDITION(!!pp->label, "partitionLabel", SD_JSON_BUILD_STRING(pp->label)),
550
                                SD_JSON_BUILD_PAIR("size", SD_JSON_BUILD_INTEGER(pp->size)),
551
                                SD_JSON_BUILD_PAIR("offset", SD_JSON_BUILD_INTEGER(pp->offset)),
552
                                SD_JSON_BUILD_PAIR("mountFileDescriptor", SD_JSON_BUILD_INTEGER(fd_idx)),
553
                                JSON_BUILD_PAIR_STRV_NON_EMPTY("mountPoint", l));
UNCOV
554
                if (r < 0)
×
555
                        return r;
556
        }
557

558
        loop_device_relinquish(loop);
×
559

UNCOV
560
        return sd_varlink_replybo(
×
561
                        link,
562
                        SD_JSON_BUILD_PAIR("partitions", SD_JSON_BUILD_VARIANT(aj)),
563
                        SD_JSON_BUILD_PAIR("imagePolicy", SD_JSON_BUILD_STRING(ps)),
564
                        SD_JSON_BUILD_PAIR("imageSize", SD_JSON_BUILD_INTEGER(di->image_size)),
565
                        SD_JSON_BUILD_PAIR("sectorSize", SD_JSON_BUILD_INTEGER(di->sector_size)),
566
                        SD_JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(di->image_uuid), "imageUuid", SD_JSON_BUILD_UUID(di->image_uuid)));
567
}
568

569
typedef enum MountMapMode {
570
        MOUNT_MAP_AUTO = 0,     /* determine automatically from image and caller */
571
        MOUNT_MAP_ROOT,         /* map caller's UID to root in namespace (map 1 UID only) */
572
        MOUNT_MAP_FOREIGN,      /* map foreign UID range to base in namespace (map 64K) */
573
        MOUNT_MAP_IDENTITY,     /* apply identity mapping (map 64K) */
574
        _MOUNT_MAP_MODE_MAX,
575
        _MOUNT_MAP_MODE_INVALID = -EINVAL,
576
} MountMapMode;
577

578
static const char *const mount_map_mode_table[_MOUNT_MAP_MODE_MAX] = {
579
        [MOUNT_MAP_AUTO]     = "auto",
580
        [MOUNT_MAP_ROOT]     = "root",
581
        [MOUNT_MAP_FOREIGN]  = "foreign",
582
        [MOUNT_MAP_IDENTITY] = "identity",
583
};
584

UNCOV
585
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(mount_map_mode, MountMapMode);
×
586

587
typedef struct MountDirectoryParameters {
588
        MountMapMode mode;
589
        unsigned directory_fd_idx;
590
        unsigned userns_fd_idx;
591
        int read_only;
592
} MountDirectoryParameters;
593

594
typedef enum DirectoryOwnership {
595
        DIRECTORY_IS_ROOT_PEER_OWNED,  /* This is returned if the directory is owned by the root user and the peer is root */
596
        DIRECTORY_IS_ROOT_OWNED,       /* This is returned if the directory is owned by the root user (and the peer user is not root) */
597
        DIRECTORY_IS_PEER_OWNED,       /* This is returned if the directory is owned by the peer user (who is not root) */
598
        DIRECTORY_IS_FOREIGN_OWNED,    /* This is returned if the directory is owned by the foreign UID range */
599
        DIRECTORY_IS_OTHERWISE_OWNED,  /* This is returned if the directory is owned by something else */
600
        _DIRECTORY_OWNERSHIP_MAX,
601
        _DIRECTORY_OWNERSHIP_ERRNO_MAX = -ERRNO_MAX, /* Guarantee the whole negative errno range fits */
602
} DirectoryOwnership;
603

UNCOV
604
static MountMapMode default_mount_map_mode(DirectoryOwnership ownership) {
×
605
        /* Derives a suitable mapping mode from the ownership of the base tree */
606

UNCOV
607
        switch (ownership) {
×
608
        case DIRECTORY_IS_PEER_OWNED:
609
                return MOUNT_MAP_ROOT;     /* Map the peer's UID to root in the container */
610

UNCOV
611
        case DIRECTORY_IS_FOREIGN_OWNED:
×
612
                return MOUNT_MAP_FOREIGN;  /* Map the foreign UID range to the container's UID range */
×
613

UNCOV
614
        case DIRECTORY_IS_ROOT_PEER_OWNED:
×
615
        case DIRECTORY_IS_ROOT_OWNED:
616
        case DIRECTORY_IS_OTHERWISE_OWNED:
617
                return MOUNT_MAP_IDENTITY; /* Don't map */
×
618

UNCOV
619
        default:
×
UNCOV
620
                return _MOUNT_MAP_MODE_INVALID;
×
621
        }
622
}
623

624
static JSON_DISPATCH_ENUM_DEFINE(dispatch_mount_directory_mode, MountMapMode, mount_map_mode_from_string);
×
625

UNCOV
626
static DirectoryOwnership validate_directory_fd(int fd, uid_t peer_uid) {
×
627
        int r, fl;
×
628

UNCOV
629
        assert(fd >= 0);
×
630

631
        /* Checks if the specified directory fd looks sane. Returns a DirectoryOwnership that categorizes the
632
         * ownership situation in comparison to the peer's UID.
633
         *
634
         * Note one key difference to image validation (as implemented above): for regular files if the
635
         * client provided us with an open fd it implies the client has access, as well as what kind of
636
         * access (i.e. ro or rw). But for directories this doesn't work the same way, as directories are
637
         * always opened read-only only. Hence we use a different mechanism to validate access to them: we
638
         * check if the directory is owned by the peer UID or by the foreign UID range (in the latter case
639
         * one of the parent directories must be owned by the peer though). */
640

641
        struct stat st;
×
UNCOV
642
        if (fstat(fd, &st) < 0)
×
643
                return log_debug_errno(errno, "Failed to stat() directory fd: %m");
×
644

UNCOV
645
        r = stat_verify_directory(&st);
×
UNCOV
646
        if (r < 0)
×
647
                return r;
648

649
        fl = fd_verify_safe_flags_full(fd, O_DIRECTORY);
×
UNCOV
650
        if (fl < 0)
×
651
                return log_debug_errno(fl, "Directory file descriptor has unsafe flags set: %m");
×
652

653
        if (st.st_uid == 0) {
×
654
                if (peer_uid == 0) {
×
UNCOV
655
                        log_debug("Directory file descriptor points to root owned directory, who is also the peer.");
×
656
                        return DIRECTORY_IS_ROOT_PEER_OWNED;
×
657
                }
UNCOV
658
                log_debug("Directory file descriptor points to root owned directory.");
×
659
                return DIRECTORY_IS_ROOT_OWNED;
×
660
        }
661
        if (st.st_uid == peer_uid) {
×
UNCOV
662
                log_debug("Directory file descriptor points to peer owned directory.");
×
UNCOV
663
                return DIRECTORY_IS_PEER_OWNED;
×
664
        }
665

666
        /* For bind mounted directories we check if they are either owned by the client's UID, or by the
667
         * foreign UID set, but in that case the parent directory must be owned by the client's UID, or some
668
         * directory iteratively up the chain */
669

670
        _cleanup_close_ int parent_fd = -EBADF;
×
671
        unsigned n_level;
672
        for (n_level = 0; n_level < 16; n_level++) {
×
673
                /* Stop iteration if we find a directory up the tree that is neither owned by the user, nor is from the foreign UID range */
674
                if (!uid_is_foreign(st.st_uid) || !gid_is_foreign(st.st_gid)) {
×
UNCOV
675
                        log_debug("Directory file descriptor points to directory which itself or its parents is neither owned by foreign UID range nor by the user.");
×
UNCOV
676
                        return DIRECTORY_IS_OTHERWISE_OWNED;
×
677
                }
678

679
                /* If the peer is root, then it doesn't matter if we find a parent owned by root, let's shortcut things. */
680
                if (peer_uid == 0) {
×
UNCOV
681
                        log_debug("Directory file descriptor is owned by foreign UID range, and peer is root.");
×
UNCOV
682
                        return DIRECTORY_IS_FOREIGN_OWNED;
×
683
                }
684

685
                /* Go one level up */
686
                _cleanup_close_ int new_parent_fd = openat(fd, "..", O_DIRECTORY|O_PATH|O_CLOEXEC);
×
UNCOV
687
                if (new_parent_fd < 0)
×
688
                        return log_debug_errno(errno, "Failed to open parent directory of directory file descriptor: %m");
×
689

690
                struct stat new_st;
×
UNCOV
691
                if (fstat(new_parent_fd, &new_st) < 0)
×
UNCOV
692
                        return log_debug_errno(errno, "Failed to stat parent directory of directory file descriptor: %m");
×
693

694
                /* Safety check to see if we hit the root dir */
695
                if (stat_inode_same(&st, &new_st)) {
×
UNCOV
696
                        log_debug("Directory file descriptor is owned by foreign UID range, but didn't find parent directory that is owned by peer among ancestors.");
×
UNCOV
697
                        return DIRECTORY_IS_OTHERWISE_OWNED;
×
698
                }
699

700
                if (new_st.st_uid == peer_uid) { /* Parent inode is owned by the peer. That's good! Everything's fine. */
×
UNCOV
701
                        log_debug("Directory file descriptor is owned by foreign UID range, and ancestor is owned by peer.");
×
UNCOV
702
                        return DIRECTORY_IS_FOREIGN_OWNED;
×
703
                }
704

UNCOV
705
                close_and_replace(parent_fd, new_parent_fd);
×
UNCOV
706
                st = new_st;
×
707
        }
708

UNCOV
709
        log_debug("Failed to find peer owned parent directory after %u levels, refusing.", n_level);
×
710
        return DIRECTORY_IS_OTHERWISE_OWNED;
711
}
712

UNCOV
713
static int vl_method_mount_directory(
×
714
                sd_varlink *link,
715
                sd_json_variant *parameters,
716
                sd_varlink_method_flags_t flags,
717
                void *userdata) {
718

UNCOV
719
        static const sd_json_dispatch_field dispatch_table[] = {
×
720
                { "mode",                        SD_JSON_VARIANT_STRING,   dispatch_mount_directory_mode, offsetof(MountDirectoryParameters, mode),             0                 },
721
                { "directoryFileDescriptor",     SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint,         offsetof(MountDirectoryParameters, directory_fd_idx), SD_JSON_MANDATORY },
722
                { "userNamespaceFileDescriptor", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint,         offsetof(MountDirectoryParameters, userns_fd_idx),    0                 },
723
                { "readOnly",                    SD_JSON_VARIANT_BOOLEAN,  sd_json_dispatch_tristate,     offsetof(MountDirectoryParameters, read_only),        0                 },
724
                VARLINK_DISPATCH_POLKIT_FIELD,
725
                {}
726
        };
727

UNCOV
728
        MountDirectoryParameters p = {
×
729
                .mode = MOUNT_MAP_AUTO,
730
                .directory_fd_idx = UINT_MAX,
731
                .userns_fd_idx = UINT_MAX,
732
                .read_only = -1,
733
        };
734
        _cleanup_close_ int directory_fd = -EBADF, userns_fd = -EBADF;
×
UNCOV
735
        Hashmap **polkit_registry = ASSERT_PTR(userdata);
×
736
        int r;
×
737

UNCOV
738
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
×
UNCOV
739
        if (r != 0)
×
740
                return r;
741

UNCOV
742
        if (p.directory_fd_idx == UINT_MAX)
×
743
                return sd_varlink_error_invalid_parameter_name(link, "directoryFileDescriptor");
×
744

745
        directory_fd = sd_varlink_peek_dup_fd(link, p.directory_fd_idx);
×
UNCOV
746
        if (directory_fd < 0)
×
747
                return log_debug_errno(directory_fd, "Failed to peek directory fd from client: %m");
×
748

749
        if (p.userns_fd_idx != UINT_MAX) {
×
750
                userns_fd = sd_varlink_peek_dup_fd(link, p.userns_fd_idx);
×
UNCOV
751
                if (userns_fd < 0)
×
UNCOV
752
                        return log_debug_errno(userns_fd, "Failed to peek user namespace fd from client: %m");
×
753
        }
754

755
        uid_t peer_uid;
×
756
        r = sd_varlink_get_peer_uid(link, &peer_uid);
×
UNCOV
757
        if (r < 0)
×
758
                return log_debug_errno(r, "Failed to get client UID: %m");
×
759

UNCOV
760
        DirectoryOwnership owned_by = validate_directory_fd(directory_fd, peer_uid);
×
UNCOV
761
        if (owned_by < 0)
×
762
                return owned_by;
763

UNCOV
764
        r = validate_userns(link, &userns_fd);
×
UNCOV
765
        if (r != 0)
×
766
                return r;
767

768
        /* If no mode is specified, pick sensible default */
769
        if (p.mode <= 0) {
×
UNCOV
770
                p.mode = default_mount_map_mode(owned_by);
×
UNCOV
771
                assert(p.mode > 0);
×
772
        }
773

UNCOV
774
        _cleanup_free_ char *directory_path = NULL;
×
775
        (void) fd_get_path(directory_fd, &directory_path);
×
776

777
        log_debug("Mounting '%s' with mapping mode: %s", strna(directory_path), mount_map_mode_to_string(p.mode));
×
778

779
        const char *polkit_details[] = {
×
UNCOV
780
                "read_only", one_zero(p.read_only > 0),
×
UNCOV
781
                "directory", strna(directory_path),
×
782
                NULL,
783
        };
784

785
        const char *polkit_action, *polkit_untrusted_action;
×
UNCOV
786
        PolkitFlags polkit_flags;
×
UNCOV
787
        if (userns_fd < 0) {
×
788
                /* Mount into the host user namespace */
789
                polkit_action = "io.systemd.mount-file-system.mount-directory";
790
                polkit_untrusted_action = "io.systemd.mount-file-system.mount-untrusted-directory";
791
                polkit_flags = 0;
792
        } else {
793
                /* Mount into a private user namespace */
UNCOV
794
                polkit_action = "io.systemd.mount-file-system.mount-directory-privately";
×
UNCOV
795
                polkit_untrusted_action = "io.systemd.mount-file-system.mount-untrusted-directory-privately";
×
796

797
                /* If polkit is not around, let's allow mounting authenticated images by default */
UNCOV
798
                polkit_flags = POLKIT_DEFAULT_ALLOW;
×
799
        }
800

801
        /* We consider a directory "trusted" if it is owned by the peer or the foreign UID range */
UNCOV
802
        bool trusted_directory = IN_SET(owned_by, DIRECTORY_IS_ROOT_PEER_OWNED, DIRECTORY_IS_PEER_OWNED, DIRECTORY_IS_FOREIGN_OWNED);
×
803

804
        /* Let's definitely acquire the regular action privilege, for mounting properly signed images */
UNCOV
805
        r = varlink_verify_polkit_async_full(
×
806
                        link,
807
                        /* bus= */ NULL,
808
                        trusted_directory ? polkit_action : polkit_untrusted_action,
809
                        polkit_details,
810
                        /* good_user= */ UID_INVALID,
811
                        trusted_directory ? polkit_flags : 0,
812
                        polkit_registry);
UNCOV
813
        if (r <= 0)
×
814
                return r;
815

816
        /* Generate the common dissection directory here. We are not going to use it, but the clients might,
817
         * and they likely are unprivileged, hence cannot create it themselves. Hence let's just create it
818
         * here, if it is missing. */
UNCOV
819
        r = get_common_dissect_directory(NULL);
×
UNCOV
820
        if (r < 0)
×
821
                return r;
822

823
        _cleanup_close_ int mount_fd = open_tree(directory_fd, "", OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC|AT_SYMLINK_NOFOLLOW|AT_EMPTY_PATH);
×
UNCOV
824
        if (mount_fd < 0)
×
825
                return log_debug_errno(errno, "Failed to issue open_tree() of provided directory '%s': %m", strna(directory_path));
×
826

827
        if (p.read_only > 0 && mount_setattr(
×
828
                            mount_fd, "", AT_EMPTY_PATH,
UNCOV
829
                            &(struct mount_attr) {
×
830
                                    .attr_set = MOUNT_ATTR_RDONLY,
831
                            }, MOUNT_ATTR_SIZE_VER0) < 0)
832
                return log_debug_errno(errno, "Failed to enable read-only mode: %m");
×
833

UNCOV
834
        if (p.mode != MOUNT_MAP_IDENTITY) {
×
835
                uid_t start;
×
836

837
                if (userns_fd >= 0) {
×
838
                        _cleanup_(uid_range_freep) UIDRange *uid_range_outside = NULL, *uid_range_inside = NULL, *gid_range_outside = NULL, *gid_range_inside = NULL;
×
839
                        r = uid_range_load_userns_by_fd(userns_fd, UID_RANGE_USERNS_OUTSIDE, &uid_range_outside);
×
840
                        if (r < 0)
×
841
                                return log_debug_errno(r, "Failed to load outside UID range of provided userns: %m");
×
842
                        r = uid_range_load_userns_by_fd(userns_fd, UID_RANGE_USERNS_INSIDE, &uid_range_inside);
×
843
                        if (r < 0)
×
844
                                return log_debug_errno(r, "Failed to load inside UID range of provided userns: %m");
×
845
                        r = uid_range_load_userns_by_fd(userns_fd, GID_RANGE_USERNS_OUTSIDE, &gid_range_outside);
×
846
                        if (r < 0)
×
847
                                return log_debug_errno(r, "Failed to load outside GID range of provided userns: %m");
×
848
                        r = uid_range_load_userns_by_fd(userns_fd, GID_RANGE_USERNS_INSIDE, &gid_range_inside);
×
UNCOV
849
                        if (r < 0)
×
UNCOV
850
                                return log_debug_errno(r, "Failed to load inside GID range of provided userns: %m");
×
851

852
                        /* Be very strict for now */
853
                        if (!uid_range_equal(uid_range_outside, gid_range_outside) ||
×
854
                            !uid_range_equal(uid_range_inside, gid_range_inside) ||
×
855
                            uid_range_outside->n_entries != 1 ||
×
856
                            uid_range_outside->entries[0].nr != 0x10000 ||
×
857
                            uid_range_inside->n_entries != 1 ||
×
858
                            uid_range_inside->entries[0].start != 0 ||
×
UNCOV
859
                            uid_range_inside->entries[0].nr != 0x10000)
×
860
                                return sd_varlink_error_invalid_parameter_name(link, "userNamespaceFileDescriptor");
×
861

UNCOV
862
                        start = uid_range_outside->entries[0].start;
×
863
                } else
864
                        start = 0;
865

866
                _cleanup_free_ char *new_uid_map = NULL;
×
867
                switch (p.mode) {
×
UNCOV
868
                case MOUNT_MAP_ROOT:
×
UNCOV
869
                        r = strextendf(&new_uid_map, UID_FMT " " UID_FMT " " UID_FMT,
×
870
                                       peer_uid, start, (uid_t) 1);
871
                        break;
UNCOV
872
                case MOUNT_MAP_FOREIGN:
×
UNCOV
873
                        r = strextendf(&new_uid_map, UID_FMT " " UID_FMT " " UID_FMT,
×
874
                                       (uid_t) FOREIGN_UID_MIN, start, (uid_t) 0x10000);
875
                        break;
UNCOV
876
                default:
×
877
                        assert_not_reached();
×
878
                }
UNCOV
879
                if (r < 0)
×
880
                        return r;
881

882
                _cleanup_close_ int idmap_userns_fd = userns_acquire(new_uid_map, new_uid_map, /* setgroups_deny= */ true);
×
UNCOV
883
                if (idmap_userns_fd < 0)
×
884
                        return log_debug_errno(idmap_userns_fd, "Failed to acquire user namespace for id mapping: %m");
×
885

UNCOV
886
                if (mount_setattr(mount_fd, "", AT_EMPTY_PATH,
×
UNCOV
887
                                  &(struct mount_attr) {
×
888
                                          .attr_set = MOUNT_ATTR_IDMAP,
889
                                          .userns_fd = idmap_userns_fd,
890
                                          .propagation = MS_PRIVATE,
891
                                  }, MOUNT_ATTR_SIZE_VER0) < 0)
UNCOV
892
                        return log_debug_errno(errno, "Failed to enable id mapping: %m");
×
893
        }
894

895
        if (userns_fd >= 0) {
×
UNCOV
896
                r = nsresource_add_mount(userns_fd, mount_fd);
×
UNCOV
897
                if (r < 0)
×
898
                        return r;
899
        }
900

UNCOV
901
        int fd_idx = sd_varlink_push_fd(link, mount_fd);
×
UNCOV
902
        if (fd_idx < 0)
×
903
                return fd_idx;
904

905
        TAKE_FD(mount_fd);
×
906

UNCOV
907
        return sd_varlink_replybo(
×
908
                        link,
909
                        SD_JSON_BUILD_PAIR("mountFileDescriptor", SD_JSON_BUILD_INTEGER(fd_idx)));
910
}
911

912
typedef struct MakeDirectoryParameters {
913
        unsigned parent_fd_idx;
914
        const char *name;
915
} MakeDirectoryParameters;
916

917
static int vl_method_make_directory(
×
918
                sd_varlink *link,
919
                sd_json_variant *parameters,
920
                sd_varlink_method_flags_t flags,
921
                void *userdata) {
922

UNCOV
923
        static const sd_json_dispatch_field dispatch_table[] = {
×
924
                { "parentFileDescriptor", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint,        offsetof(MakeDirectoryParameters, parent_fd_idx), SD_JSON_MANDATORY },
925
                { "name",                 SD_JSON_VARIANT_STRING,   json_dispatch_const_filename, offsetof(MakeDirectoryParameters, name),          SD_JSON_MANDATORY },
926
                VARLINK_DISPATCH_POLKIT_FIELD,
927
                {}
928
        };
929

UNCOV
930
        MakeDirectoryParameters p = {
×
931
                .parent_fd_idx = UINT_MAX,
932
        };
933
        Hashmap **polkit_registry = ASSERT_PTR(userdata);
×
UNCOV
934
        int r;
×
935

936
        r = sd_varlink_dispatch(link, parameters, dispatch_table, &p);
×
937
        if (r != 0)
×
UNCOV
938
                return r;
×
939

UNCOV
940
        if (p.parent_fd_idx == UINT_MAX)
×
UNCOV
941
                return sd_varlink_error_invalid_parameter_name(link, "parentFileDescriptor");
×
942

943
        _cleanup_close_ int parent_fd = sd_varlink_peek_dup_fd(link, p.parent_fd_idx);
×
944
        if (parent_fd < 0)
×
945
                return log_debug_errno(parent_fd, "Failed to peek parent directory fd from client: %m");
×
946

947
        uid_t peer_uid;
×
948
        r = sd_varlink_get_peer_uid(link, &peer_uid);
×
UNCOV
949
        if (r < 0)
×
950
                return log_debug_errno(r, "Failed to get client UID: %m");
×
951

952
        struct stat parent_stat;
×
953
        if (fstat(parent_fd, &parent_stat) < 0)
×
954
                return r;
955

956
        r = stat_verify_directory(&parent_stat);
×
957
        if (r < 0)
×
958
                return r;
959

960
        int fl = fd_verify_safe_flags_full(parent_fd, O_DIRECTORY);
×
UNCOV
961
        if (fl < 0)
×
962
                return log_debug_errno(fl, "Directory file descriptor has unsafe flags set: %m");
×
963

964
        _cleanup_free_ char *parent_path = NULL;
×
UNCOV
965
        (void) fd_get_path(parent_fd, &parent_path);
×
966

UNCOV
967
        _cleanup_free_ char *new_path = parent_path ? path_join(parent_path, p.name) : NULL;
×
UNCOV
968
        log_debug("Asked to make directory: %s", strna(new_path));
×
969

970
        const char *polkit_details[] = {
×
971
                "directory", strna(new_path),
×
972
                NULL,
973
        };
974

975
        const char *polkit_action;
×
UNCOV
976
        PolkitFlags polkit_flags;
×
977
        if (parent_stat.st_uid != peer_uid) {
×
978
                polkit_action = "io.systemd.mount-file-system.make-directory-untrusted";
979
                polkit_flags = 0;
980
        } else {
981
                polkit_action = "io.systemd.mount-file-system.make-directory";
×
982
                polkit_flags = POLKIT_DEFAULT_ALLOW;
×
983
        }
984

985
        r = varlink_verify_polkit_async_full(
×
986
                        link,
987
                        /* bus= */ NULL,
988
                        polkit_action,
989
                        polkit_details,
990
                        /* good_user= */ UID_INVALID,
991
                        polkit_flags,
992
                        polkit_registry);
993
        if (r <= 0)
×
994
                return r;
995

UNCOV
996
        _cleanup_free_ char *t = NULL;
×
997
        r = tempfn_random(p.name, "mountfsd", &t);
×
UNCOV
998
        if (r < 0)
×
999
                return r;
1000

1001
        _cleanup_close_ int fd = open_mkdir_at(parent_fd, t, O_CLOEXEC, 0700);
×
UNCOV
1002
        if (fd < 0)
×
1003
                return fd;
1004

1005
        r = RET_NERRNO(fchmod(fd, 0700)); /* Set mode explicitly, as paranoia regarding umask games */
×
UNCOV
1006
        if (r < 0)
×
UNCOV
1007
                goto fail;
×
1008

1009
        r = RET_NERRNO(fchown(fd, FOREIGN_UID_BASE, FOREIGN_UID_BASE));
×
1010
        if (r < 0)
×
1011
                goto fail;
×
1012

1013
        r = rename_noreplace(parent_fd, t, parent_fd, p.name);
×
UNCOV
1014
        if (r < 0)
×
UNCOV
1015
                goto fail;
×
1016

UNCOV
1017
        t = mfree(t); /* temporary filename no longer exists */
×
1018

1019
        int fd_idx = sd_varlink_push_fd(link, fd);
×
UNCOV
1020
        if (fd_idx < 0) {
×
1021
                r = fd_idx;
×
UNCOV
1022
                goto fail;
×
1023
        }
1024

1025
        TAKE_FD(fd);
×
1026

UNCOV
1027
        return sd_varlink_replybo(
×
1028
                        link,
1029
                        SD_JSON_BUILD_PAIR("directoryFileDescriptor", SD_JSON_BUILD_INTEGER(fd_idx)));
1030

1031
fail:
×
1032
        (void) unlinkat(parent_fd, t ?: p.name, AT_REMOVEDIR);
×
UNCOV
1033
        return r;
×
1034
}
1035

UNCOV
1036
static int process_connection(sd_varlink_server *server, int _fd) {
×
1037
        _cleanup_close_ int fd = TAKE_FD(_fd); /* always take possession */
×
UNCOV
1038
        _cleanup_(sd_varlink_close_unrefp) sd_varlink *vl = NULL;
×
UNCOV
1039
        _cleanup_(sd_event_unrefp) sd_event *event = NULL;
×
UNCOV
1040
        int r;
×
1041

1042
        r = sd_event_new(&event);
×
1043
        if (r < 0)
×
1044
                return r;
1045

1046
        r = sd_varlink_server_attach_event(server, event, 0);
×
1047
        if (r < 0)
×
1048
                return log_error_errno(r, "Failed to attach Varlink server to event loop: %m");
×
1049

1050
        r = sd_varlink_server_add_connection(server, fd, &vl);
×
UNCOV
1051
        if (r < 0)
×
UNCOV
1052
                return log_error_errno(r, "Failed to add connection: %m");
×
1053

1054
        TAKE_FD(fd);
×
1055
        vl = sd_varlink_ref(vl);
×
1056

UNCOV
1057
        r = sd_event_loop(event);
×
UNCOV
1058
        if (r < 0)
×
UNCOV
1059
                return log_error_errno(r, "Failed to run event loop: %m");
×
1060

1061
        r = sd_varlink_server_detach_event(server);
×
UNCOV
1062
        if (r < 0)
×
UNCOV
1063
                return log_error_errno(r, "Failed to detach Varlink server from event loop: %m");
×
1064

1065
        return 0;
1066
}
1067

UNCOV
1068
static int run(int argc, char *argv[]) {
×
UNCOV
1069
        usec_t start_time, listen_idle_usec, last_busy_usec = USEC_INFINITY;
×
UNCOV
1070
        _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *server = NULL;
×
UNCOV
1071
        _cleanup_hashmap_free_ Hashmap *polkit_registry = NULL;
×
UNCOV
1072
        _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
×
UNCOV
1073
        unsigned n_iterations = 0;
×
UNCOV
1074
        int m, listen_fd, r;
×
1075

UNCOV
1076
        log_setup();
×
1077

UNCOV
1078
        m = sd_listen_fds(false);
×
UNCOV
1079
        if (m < 0)
×
UNCOV
1080
                return log_error_errno(m, "Failed to determine number of listening fds: %m");
×
UNCOV
1081
        if (m == 0)
×
UNCOV
1082
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No socket to listen on received.");
×
UNCOV
1083
        if (m > 1)
×
UNCOV
1084
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Worker can only listen on a single socket at a time.");
×
1085

UNCOV
1086
        listen_fd = SD_LISTEN_FDS_START;
×
1087

UNCOV
1088
        r = fd_nonblock(listen_fd, false);
×
UNCOV
1089
        if (r < 0)
×
UNCOV
1090
                return log_error_errno(r, "Failed to turn off non-blocking mode for listening socket: %m");
×
1091

UNCOV
1092
        r = varlink_server_new(&server,
×
1093
                               SD_VARLINK_SERVER_INHERIT_USERDATA|
1094
                               SD_VARLINK_SERVER_ALLOW_FD_PASSING_INPUT|SD_VARLINK_SERVER_ALLOW_FD_PASSING_OUTPUT,
1095
                               &polkit_registry);
UNCOV
1096
        if (r < 0)
×
UNCOV
1097
                return log_error_errno(r, "Failed to allocate server: %m");
×
1098

UNCOV
1099
        r = sd_varlink_server_add_interface(server, &vl_interface_io_systemd_MountFileSystem);
×
UNCOV
1100
        if (r < 0)
×
UNCOV
1101
                return log_error_errno(r, "Failed to add MountFileSystem interface to varlink server: %m");
×
1102

UNCOV
1103
        r = sd_varlink_server_bind_method_many(
×
1104
                        server,
1105
                        "io.systemd.MountFileSystem.MountImage",     vl_method_mount_image,
1106
                        "io.systemd.MountFileSystem.MountDirectory", vl_method_mount_directory,
1107
                        "io.systemd.MountFileSystem.MakeDirectory",  vl_method_make_directory);
UNCOV
1108
        if (r < 0)
×
UNCOV
1109
                return log_error_errno(r, "Failed to bind methods: %m");
×
1110

UNCOV
1111
        r = sd_varlink_server_set_exit_on_idle(server, true);
×
UNCOV
1112
        if (r < 0)
×
UNCOV
1113
                return log_error_errno(r, "Failed to enable exit-on-idle mode: %m");
×
1114

UNCOV
1115
        r = getenv_bool("MOUNTFS_FIXED_WORKER");
×
UNCOV
1116
        if (r < 0)
×
UNCOV
1117
                return log_error_errno(r, "Failed to parse MOUNTFSD_FIXED_WORKER: %m");
×
UNCOV
1118
        listen_idle_usec = r ? USEC_INFINITY : LISTEN_IDLE_USEC;
×
1119

UNCOV
1120
        r = pidref_set_parent(&parent);
×
UNCOV
1121
        if (r < 0)
×
UNCOV
1122
                return log_error_errno(r, "Failed to acquire pidfd of parent process: %m");
×
1123

UNCOV
1124
        start_time = now(CLOCK_MONOTONIC);
×
1125

UNCOV
1126
        for (;;) {
×
UNCOV
1127
                _cleanup_close_ int fd = -EBADF;
×
UNCOV
1128
                usec_t n;
×
1129

1130
                /* Exit the worker in regular intervals, to flush out all memory use */
UNCOV
1131
                if (n_iterations++ > ITERATIONS_MAX) {
×
UNCOV
1132
                        log_debug("Exiting worker, processed %u iterations, that's enough.", n_iterations);
×
1133
                        break;
1134
                }
1135

UNCOV
1136
                n = now(CLOCK_MONOTONIC);
×
UNCOV
1137
                if (n >= usec_add(start_time, RUNTIME_MAX_USEC)) {
×
UNCOV
1138
                        log_debug("Exiting worker, ran for %s, that's enough.",
×
1139
                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, start_time), 0));
UNCOV
1140
                        break;
×
1141
                }
1142

UNCOV
1143
                if (last_busy_usec == USEC_INFINITY)
×
1144
                        last_busy_usec = n;
UNCOV
1145
                else if (listen_idle_usec != USEC_INFINITY && n >= usec_add(last_busy_usec, listen_idle_usec)) {
×
UNCOV
1146
                        log_debug("Exiting worker, been idle for %s.",
×
1147
                                  FORMAT_TIMESPAN(usec_sub_unsigned(n, last_busy_usec), 0));
UNCOV
1148
                        break;
×
1149
                }
1150

UNCOV
1151
                (void) rename_process("systemd-mountwork: waiting...");
×
UNCOV
1152
                fd = RET_NERRNO(accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC));
×
UNCOV
1153
                (void) rename_process("systemd-mountwork: processing...");
×
1154

UNCOV
1155
                if (fd == -EAGAIN)
×
UNCOV
1156
                        continue; /* The listening socket has SO_RECVTIMEO set, hence a timeout is expected
×
1157
                                   * after a while, let's check if it's time to exit though. */
UNCOV
1158
                if (fd == -EINTR)
×
UNCOV
1159
                        continue; /* Might be that somebody attached via strace, let's just continue in that
×
1160
                                   * case */
UNCOV
1161
                if (fd < 0)
×
UNCOV
1162
                        return log_error_errno(fd, "Failed to accept() from listening socket: %m");
×
1163

UNCOV
1164
                if (now(CLOCK_MONOTONIC) <= usec_add(n, PRESSURE_SLEEP_TIME_USEC)) {
×
1165
                        /* We only slept a very short time? If so, let's see if there are more sockets
1166
                         * pending, and if so, let's ask our parent for more workers */
1167

UNCOV
1168
                        r = fd_wait_for_event(listen_fd, POLLIN, 0);
×
UNCOV
1169
                        if (r < 0)
×
UNCOV
1170
                                return log_error_errno(r, "Failed to test for POLLIN on listening socket: %m");
×
1171

UNCOV
1172
                        if (FLAGS_SET(r, POLLIN)) {
×
UNCOV
1173
                                r = pidref_kill(&parent, SIGUSR2);
×
UNCOV
1174
                                if (r == -ESRCH)
×
UNCOV
1175
                                        return log_error_errno(r, "Parent already died?");
×
UNCOV
1176
                                if (r < 0)
×
UNCOV
1177
                                        return log_error_errno(r, "Failed to send SIGUSR2 signal to parent. %m");
×
1178
                        }
1179
                }
1180

UNCOV
1181
                (void) process_connection(server, TAKE_FD(fd));
×
UNCOV
1182
                last_busy_usec = USEC_INFINITY;
×
1183
        }
1184

1185
        return 0;
1186
}
1187

UNCOV
1188
DEFINE_MAIN_FUNCTION(run);
×
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