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

systemd / systemd / 19979552133

05 Dec 2025 05:29PM UTC coverage: 72.765% (-0.2%) from 72.917%
19979552133

push

github

yuwata
udev-rules: use the right variable

We carefully prepare a copy of a local buffer to save in device cache
and then save the buffer there instead... This leads to abort in free()
on exit (also, copied is leaked).

Reproducer:
 # udevadm test /sys/block/sr0

Follow-up-for: a9559ebcb

Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=2406118

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

1564 existing lines in 65 files now uncovered.

309707 of 425625 relevant lines covered (72.77%)

1149816.14 hits per line

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

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

3
#include <stdlib.h>
4
#include <sys/stat.h>
5
#include <unistd.h>
6

7
#include "alloc-util.h"
8
#include "argv-util.h"
9
#include "dropin.h"
10
#include "escape.h"
11
#include "fd-util.h"
12
#include "fileio.h"
13
#include "fstab-util.h"
14
#include "generator.h"
15
#include "initrd-util.h"
16
#include "log.h"
17
#include "mkdir-label.h"
18
#include "mountpoint-util.h"
19
#include "parse-util.h"
20
#include "path-util.h"
21
#include "special.h"
22
#include "specifier.h"
23
#include "string-util.h"
24
#include "time-util.h"
25
#include "tmpfile-util.h"
26
#include "unit-name.h"
27

28
static int symlink_unless_exists(const char *target, const char *linkpath) {
184✔
29
        (void) mkdir_parents(linkpath, 0755);
184✔
30

31
        if (symlink(target, linkpath) < 0 && errno != EEXIST)
184✔
32
                return log_error_errno(errno, "Failed to create symlink %s: %m", linkpath);
×
33
        return 0;
34
}
35

36
int generator_open_unit_file_full(
378✔
37
                const char *dir,
38
                const char *source,
39
                const char *filename,
40
                FILE **ret_file,
41
                char **ret_final_path,
42
                char **ret_temp_path) {
43

44
        _cleanup_free_ char *p = NULL;
378✔
45
        FILE *f;
378✔
46
        int r;
378✔
47

48
        assert(dir);
378✔
49
        assert(ret_file);
378✔
50

51
        /* If <ret_temp_path> is specified, it creates a temporary unit file and also returns its
52
         * temporary path. */
53

54
        if (ret_temp_path) {
378✔
55
                r = fopen_temporary(dir, &f, &p);
315✔
56
                if (r < 0)
315✔
57
                        return log_error_errno(r, "Failed to create temporary unit file in '%s': %m", dir);
×
58

59
                (void) fchmod(fileno(f), 0644);
315✔
60

61
                *ret_temp_path = TAKE_PTR(p);
315✔
62
        } else {
63
                assert(filename);
63✔
64

65
                p = path_join(dir, filename);
63✔
66
                if (!p)
63✔
67
                        return log_oom();
×
68

69
                r = fopen_unlocked(p, "wxe", &f);
63✔
70
                if (r < 0) {
63✔
71
                        if (source && r == -EEXIST)
×
72
                                return log_error_errno(r,
×
73
                                                       "Failed to create unit file '%s', as it already exists. Duplicate entry in '%s'?",
74
                                                       p, source);
75

76
                        return log_error_errno(r, "Failed to create unit file '%s': %m", p);
×
77
                }
78
        }
79

80
        fprintf(f,
378✔
81
                "# Automatically generated by %s\n\n",
82
                program_invocation_short_name);
83

84
        *ret_file = f;
378✔
85

86
        if (ret_final_path)
378✔
87
                *ret_final_path = TAKE_PTR(p);
×
88

89
        return 0;
90
}
91

92
int generator_add_symlink_full(
184✔
93
                const char *dir,
94
                const char *dst,
95
                const char *dep_type,
96
                const char *src,
97
                const char *instance) {
98

99
        _cleanup_free_ char *dn = NULL, *fn = NULL, *instantiated = NULL, *to = NULL, *from = NULL;
184✔
100
        int r;
184✔
101

102
        assert(dir);
184✔
103
        assert(dst);
184✔
104
        assert(src);
184✔
105

106
        /* If 'dep_type' is specified adds a symlink from <dst>.<dep_type>/ to <src> (if src is absolute) or ../<src> (otherwise).
107
         *
108
         * If 'dep_type' is NULL, it will create a symlink to <src> (i.e. create an alias.
109
         *
110
         * If <instance> is specified, then <src> must be a template unit name, and we'll instantiate it. */
111

112
        r = path_extract_directory(src, &dn);
184✔
113
        if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → just a file name was passed */
184✔
114
                return log_error_errno(r, "Failed to extract directory name from '%s': %m", src);
×
115

116
        r = path_extract_filename(src, &fn);
184✔
117
        if (r < 0)
184✔
118
                return log_error_errno(r, "Failed to extract file name from '%s': %m", src);
×
119
        if (r == O_DIRECTORY)
184✔
120
                return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Expected path to regular file name, but got '%s', refusing.", src);
×
121

122
        if (instance) {
184✔
123
                r = unit_name_replace_instance(fn, instance, &instantiated);
2✔
124
                if (r < 0)
2✔
125
                        return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", fn, instance);
×
126
        }
127

128
        if (dep_type) { /* Create a .wants/ style dep */
184✔
129
                from = path_join(dn ?: "..", fn);
269✔
130
                if (!from)
182✔
131
                        return log_oom();
×
132

133
                to = strjoin(dir, "/", dst, ".", dep_type, "/", instantiated ?: fn);
182✔
134
        } else { /* or create an alias */
135
                from = dn ? path_join(dn, fn) : strdup(fn);
2✔
136
                if (!from)
2✔
137
                        return log_oom();
×
138

139
                to = strjoin(dir, "/", dst);
2✔
140
        }
141
        if (!to)
184✔
142
                return log_oom();
×
143

144
        return symlink_unless_exists(from, to);
184✔
145
}
146

UNCOV
147
static int generator_add_ordering(
×
148
                const char *dir,
149
                const char *src,
150
                const char *order,
151
                const char *dst,
152
                const char *instance) {
153

UNCOV
154
        _cleanup_free_ char *instantiated = NULL, *p = NULL, *fn = NULL;
×
UNCOV
155
        _cleanup_fclose_ FILE *f = NULL;
×
UNCOV
156
        const char *to;
×
UNCOV
157
        int r;
×
158

UNCOV
159
        assert(dir);
×
UNCOV
160
        assert(src);
×
UNCOV
161
        assert(order);
×
UNCOV
162
        assert(dst);
×
163

164
        /* Adds an explicit ordering dependency of type <order> from <src> to <dst>. If <instance> is
165
         * specified, it is inserted into <dst>. */
166

UNCOV
167
        if (instance) {
×
168
                r = unit_name_replace_instance(dst, instance, &instantiated);
×
169
                if (r < 0)
×
170
                        return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", dst, instance);
×
171

172
                to = instantiated;
×
173
        } else
174
                to = dst;
175

UNCOV
176
        fn = strjoin(src, ".d/50-order-", to, ".conf");
×
UNCOV
177
        if (!fn)
×
178
                return log_oom();
×
179

UNCOV
180
        p = path_join(dir, fn);
×
UNCOV
181
        if (!p)
×
182
                return log_oom();
×
183

UNCOV
184
        (void) mkdir_parents_label(p, 0755);
×
185

UNCOV
186
        r = fopen_unlocked(p, "wxe", &f);
×
UNCOV
187
        if (r < 0)
×
188
                return log_error_errno(r, "Failed to create '%s': %m", p);
×
189

UNCOV
190
        fprintf(f,
×
191
                "# Automatically generated by %s\n\n"
192
                "[Unit]\n"
193
                "%s=%s\n",
194
                program_invocation_short_name,
195
                order,
196
                to);
197

UNCOV
198
        r = fflush_and_check(f);
×
UNCOV
199
        if (r < 0)
×
200
                return log_error_errno(r, "Failed to write drop-in '%s': %m", p);
×
201

202
        return 0;
203
}
204

205
static int write_fsck_sysroot_service(
7✔
206
                const char *unit, /* Either SPECIAL_FSCK_ROOT_SERVICE or SPECIAL_FSCK_USR_SERVICE */
207
                const char *dir,
208
                const char *what,
209
                const char *extra_after) {
210

211
        _cleanup_free_ char *device = NULL, *escaped = NULL, *escaped2 = NULL;
7✔
212
        _cleanup_fclose_ FILE *f = NULL;
7✔
213
        int r;
7✔
214

215
        assert(unit);
7✔
216
        assert(dir);
7✔
217
        assert(what);
7✔
218

219
        /* Writes out special versions of systemd-fsck-root.service and systemd-fsck-usr.service for use in
220
         * the initrd. The regular statically shipped versions of these unit files use / and /usr for as
221
         * paths, which doesn't match what we need for the initrd (where the dirs are /sysroot/ +
222
         * /sysusr/usr/), hence we overwrite those versions here. */
223

224
        escaped = specifier_escape(what);
7✔
225
        if (!escaped)
7✔
226
                return log_oom();
×
227

228
        escaped2 = cescape(escaped);
7✔
229
        if (!escaped2)
7✔
230
                return log_oom();
×
231

232
        r = unit_name_from_path(what, ".device", &device);
7✔
233
        if (r < 0)
7✔
234
                return log_error_errno(r, "Failed to convert device \"%s\" to unit name: %m", what);
×
235

236
        r = generator_open_unit_file(dir, /* source = */ NULL, unit, &f);
7✔
237
        if (r < 0)
7✔
238
                return r;
239

240
        fprintf(f,
14✔
241
                "[Unit]\n"
242
                "Description=File System Check on %1$s\n"
243
                "Documentation=man:%2$s(8)\n"
244
                "\n"
245
                "DefaultDependencies=no\n"
246
                "BindsTo=%3$s\n"
247
                "Conflicts=shutdown.target\n"
248
                "After=%4$s%5$slocal-fs-pre.target %3$s\n"
249
                "Before=shutdown.target\n"
250
                "\n"
251
                "[Service]\n"
252
                "Type=oneshot\n"
253
                "RemainAfterExit=yes\n"
254
                "ExecStart=" SYSTEMD_FSCK_PATH " %6$s\n"
255
                "TimeoutSec=infinity\n"
256
                "ImportCredential=fsck.*\n",
257
                escaped,
258
                unit,
259
                device,
260
                strempty(extra_after),
261
                isempty(extra_after) ? "" : " ",
7✔
262
                escaped2);
263

264
        r = fflush_and_check(f);
7✔
265
        if (r < 0)
7✔
266
                return log_error_errno(r, "Failed to write unit %s: %m", unit);
×
267

268
        return 0;
269
}
270

271
int generator_write_fsck_deps(
14✔
272
                FILE *f,
273
                const char *dir,
274
                const char *what,
275
                const char *where,
276
                const char *fstype,
277
                const char *options) {
278

279
        int r;
14✔
280

281
        assert(f);
14✔
282
        assert(dir);
14✔
283
        assert(what);
14✔
284
        assert(where);
14✔
285

286
        /* Let's do an early exit if we are invoked for the root and /usr/ trees in the initrd, to avoid
287
         * generating confusing log messages. */
288
        if (in_initrd() && PATH_IN_SET(where, "/", "/usr")) {
14✔
289
                log_debug("Skipping fsck for %s in initrd.", where);
×
290
                return 0;
×
291
        }
292

293
        if (fstype) {
14✔
294
                if (!fstype_is_blockdev_backed(fstype)) {
10✔
295
                        log_debug("Skipping file system check for non-block based file system '%s'.", what);
×
296
                        return 0;
×
297
                }
298

299
                if (fstype_is_ro(fstype)) {
10✔
300
                        log_debug("Skipping file system check for read-only file system '%s'.", what);
×
301
                        return 0;
×
302
                }
303
        }
304

305
        if (fstab_test_option(options, "bind\0rbind\0")) {
14✔
306
                log_debug("Skipping file system check for bind mount of '%s'.", what);
×
307
                return 0;
×
308
        }
309

310
        if (!is_device_path(what)) {
14✔
311
                log_debug("Checking was requested for \"%s\", but it is not a device.", what);
×
312
                return 0;
×
313
        }
314

315
        if (!isempty(fstype) && !streq(fstype, "auto")) {
24✔
UNCOV
316
                r = fsck_exists_for_fstype(fstype);
×
UNCOV
317
                if (r < 0)
×
318
                        log_warning_errno(r, "Checking was requested for %s, but couldn't detect if fsck.%s may be used, proceeding: %m", what, fstype);
×
UNCOV
319
                else if (r == 0) {
×
320
                        /* treat missing check as essentially OK */
321
                        log_debug("Checking was requested for %s, but fsck.%s does not exist.", what, fstype);
×
322
                        return 0;
×
323
                }
324
        } else {
325
                r = fsck_exists();
14✔
326
                if (r < 0)
14✔
327
                        log_warning_errno(r, "Checking was requested for %s, but couldn't detect if the fsck command may be used, proceeding: %m", what);
×
328
                else if (r == 0) {
14✔
329
                        /* treat missing fsck as essentially OK */
330
                        log_debug("Checking was requested for %s, but the fsck command does not exist.", what);
×
331
                        return 0;
×
332
                }
333
        }
334

335
        if (path_equal(where, "/")) {
14✔
336
                /* We support running the fsck instance for the root fs while it is already mounted, for
337
                 * compatibility with non-initrd boots. It's ugly, but it is how it is. Since – unlike for
338
                 * regular file systems – this means the ordering is reversed (i.e. mount *before* fsck) we
339
                 * have a separate fsck unit for this, independent of systemd-fsck@.service. */
340

UNCOV
341
                const char *lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/" SPECIAL_FSCK_ROOT_SERVICE);
×
342

UNCOV
343
                r = symlink_unless_exists(SYSTEM_DATA_UNIT_DIR "/" SPECIAL_FSCK_ROOT_SERVICE, lnk);
×
UNCOV
344
                if (r < 0)
×
345
                        return r;
×
346
        } else {
347
                _cleanup_free_ char *_fsck = NULL;
14✔
348
                const char *fsck, *dep;
14✔
349

350
                if (in_initrd() && path_equal(where, "/sysroot")) {
24✔
351
                        r = write_fsck_sysroot_service(SPECIAL_FSCK_ROOT_SERVICE, dir, what, SPECIAL_INITRD_ROOT_DEVICE_TARGET);
6✔
352
                        if (r < 0)
6✔
353
                                return r;
354

355
                        fsck = SPECIAL_FSCK_ROOT_SERVICE;
356
                        dep = "Requires";
357

358
                } else if (in_initrd() && path_equal(where, "/sysusr/usr")) {
12✔
359
                        r = write_fsck_sysroot_service(SPECIAL_FSCK_USR_SERVICE, dir, what, NULL);
1✔
360
                        if (r < 0)
1✔
361
                                return r;
362

363
                        fsck = SPECIAL_FSCK_USR_SERVICE;
364
                        dep = "Requires";
365
                } else {
366
                        /* When this is /usr, then let's add a Wants= dependency, otherwise a Requires=
367
                         * dependency. Why? We can't possibly unmount /usr during shutdown, but if we have a
368
                         * Requires= from /usr onto a fsck@.service unit and that unit is shut down, then
369
                         * we'd have to unmount /usr too.  */
370

371
                        dep = path_equal(where, "/usr") ? "Wants" : "Requires";
7✔
372

373
                        r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck);
7✔
374
                        if (r < 0)
7✔
375
                                return log_error_errno(r, "Failed to create fsck service name: %m");
×
376

377
                        fsck = _fsck;
7✔
378
                }
379

380
                fprintf(f,
14✔
381
                        "%1$s=%2$s\n"
382
                        "After=%2$s\n",
383
                        dep, fsck);
384
        }
385

386
        return 0;
387
}
388

389
int generator_write_device_timeout(
46✔
390
                const char *dir,
391
                const char *what,
392
                const char *opts,
393
                char **filtered) {
394

395
        /* Configure how long we wait for a device that backs a mount point or a
396
         * swap partition to show up. This is useful to support endless device timeouts
397
         * for devices that show up only after user input, like crypto devices. */
398

399
        _cleanup_free_ char *node = NULL, *unit = NULL, *timeout = NULL;
46✔
400
        usec_t u;
46✔
401
        int r;
46✔
402

403
        assert(dir);
46✔
404
        assert(what);
46✔
405

406
        r = fstab_filter_options(opts, "comment=systemd.device-timeout\0"
46✔
407
                                       "x-systemd.device-timeout\0",
408
                                 NULL, &timeout, NULL, filtered);
409
        if (r < 0) {
46✔
410
                log_warning_errno(r, "Failed to parse fstab options, ignoring: %m");
×
411
                return 0;
×
412
        }
413
        if (r == 0)
46✔
414
                return 0;
415

416
        r = parse_sec_fix_0(timeout, &u);
1✔
417
        if (r < 0) {
1✔
418
                log_warning("Failed to parse timeout for device '%s', ignoring: %s", what, timeout);
×
419
                return 0;
×
420
        }
421

422
        node = fstab_node_to_udev_node(what);
1✔
423
        if (!node)
1✔
424
                return log_oom();
×
425
        if (!is_device_path(node)) {
1✔
426
                log_warning("'%s' is not a device path, ignoring x-systemd.device-timeout= option.", node);
1✔
427
                return 0;
1✔
428
        }
429

430
        r = unit_name_from_path(node, ".device", &unit);
×
431
        if (r < 0)
×
432
                return log_error_errno(r, "Failed to make unit name from device path '%s': %m", node);
×
433

434
        r = write_drop_in_format(dir, unit, 50, "device-timeout",
×
435
                                 "# Automatically generated by %s\n"
436
                                 "# from supplied options \"%s\"\n\n"
437
                                 "[Unit]\n"
438
                                 "JobRunningTimeoutSec=%s",
439
                                 program_invocation_short_name,
440
                                 opts,
441
                                 timeout);
442
        if (r < 0)
×
443
                return r;
×
444

445
        return 1;
446
}
447

448
int generator_write_unit_timeout(
24✔
449
                FILE *f,
450
                const char *where,
451
                const char *opts,
452
                const char *filter,
453
                const char *unit_setting) {
454

455
        _cleanup_free_ char *timeout = NULL;
24✔
456
        usec_t u;
24✔
457
        int r;
24✔
458

459
        assert(f);
24✔
460
        assert(where);
24✔
461
        assert(filter);
24✔
462
        assert(unit_setting);
24✔
463

464
        r = fstab_filter_options(opts, filter, NULL, &timeout, NULL, NULL);
24✔
465
        if (r < 0)
24✔
466
                return log_error_errno(r, "Failed to parse options for '%s': %m", where);
×
467
        if (r == 0)
24✔
468
                return 0;
469

470
        r = parse_sec_fix_0(timeout, &u);
×
471
        if (r < 0) {
×
472
                log_warning_errno(r, "Failed to parse timeout '%s' for '%s', ignoring: %m", timeout, where);
×
473
                return 0;
×
474
        }
475

476
        fprintf(f, "%s=%s\n", unit_setting, FORMAT_TIMESPAN(u, 0));
×
477

478
        return 0;
479
}
480

481
int generator_write_network_device_deps(
24✔
482
                const char *dir,
483
                const char *what,
484
                const char *where,
485
                const char *opts) {
486

487
        /* fstab records that specify _netdev option should apply the network
488
         * ordering on the actual device depending on network connection. If we
489
         * are not mounting real device (NFS, CIFS), we rely on _netdev effect
490
         * on the mount unit itself. */
491

492
        _cleanup_free_ char *node = NULL, *unit = NULL;
24✔
493
        int r;
24✔
494

495
        assert(dir);
24✔
496
        assert(what);
24✔
497
        assert(where);
24✔
498

499
        if (fstab_is_extrinsic(where, opts))
24✔
500
                return 0;
501

502
        if (!fstab_test_option(opts, "_netdev\0"))
22✔
503
                return 0;
504

505
        node = fstab_node_to_udev_node(what);
×
506
        if (!node)
×
507
                return log_oom();
×
508

509
        /* Nothing to apply dependencies to. */
510
        if (!is_device_path(node))
×
511
                return 0;
512

513
        r = unit_name_from_path(node, ".device", &unit);
×
514
        if (r < 0)
×
515
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m", node);
×
516

517
        /* See mount_add_default_dependencies for explanation why we create such
518
         * dependencies. */
519
        return write_drop_in_format(dir, unit, 50, "netdev-dependencies",
×
520
                                    "# Automatically generated by %s\n\n"
521
                                    "[Unit]\n"
522
                                    "After=" SPECIAL_NETWORK_ONLINE_TARGET " " SPECIAL_NETWORK_TARGET "\n"
523
                                    "Wants=" SPECIAL_NETWORK_ONLINE_TARGET "\n",
524
                                    program_invocation_short_name);
525
}
526

527
int generator_write_initrd_root_device_deps(const char *dir, const char *what) {
6✔
528
        _cleanup_free_ char *unit = NULL;
6✔
529
        int r;
6✔
530

531
        r = unit_name_from_path(what, ".device", &unit);
6✔
532
        if (r < 0)
6✔
533
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
×
534
                                       what);
535

536
        return write_drop_in_format(dir, SPECIAL_INITRD_ROOT_DEVICE_TARGET, 50, "root-device",
6✔
537
                                    "# Automatically generated by %s\n\n"
538
                                    "[Unit]\n"
539
                                    "Requires=%s\n"
540
                                    "After=%s",
541
                                    program_invocation_short_name,
542
                                    unit,
543
                                    unit);
544
}
545

546
int generator_hook_up_mkswap(
×
547
                const char *dir,
548
                const char *what) {
549

550
        _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
×
551
        _cleanup_fclose_ FILE *f = NULL;
×
552
        int r;
×
553

554
        assert(dir);
×
555
        assert(what);
×
556

557
        node = fstab_node_to_udev_node(what);
×
558
        if (!node)
×
559
                return log_oom();
×
560

561
        /* Nothing to work on. */
562
        if (!is_device_path(node))
×
563
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
564
                                       "Cannot format something that is not a device node: %s",
565
                                       node);
566

567
        r = unit_name_from_path_instance("systemd-mkswap", node, ".service", &unit);
×
568
        if (r < 0)
×
569
                return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
×
570
                                       node);
571

572
        escaped = cescape(node);
×
573
        if (!escaped)
×
574
                return log_oom();
×
575

576
        r = unit_name_from_path(what, ".swap", &where_unit);
×
577
        if (r < 0)
×
578
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
×
579
                                       what);
580

581
        r = generator_open_unit_file(dir, /* source = */ NULL, unit, &f);
×
582
        if (r < 0)
×
583
                return r;
584

585
        fprintf(f,
×
586
                "[Unit]\n"
587
                "Description=Make Swap on %%f\n"
588
                "Documentation=man:systemd-mkswap@.service(8)\n"
589
                "\n"
590
                "DefaultDependencies=no\n"
591
                "BindsTo=%%i.device\n"
592
                "After=%%i.device\n"
593
                "Before=%s\n"
594
                "Conflicts=shutdown.target\n"
595
                "Before=shutdown.target\n"
596
                "\n"
597
                "[Service]\n"
598
                "Type=oneshot\n"
599
                "RemainAfterExit=yes\n"
600
                "ExecStart="SYSTEMD_MAKEFS_PATH " swap %s\n"
601
                "TimeoutSec=infinity\n",
602
                where_unit,
603
                escaped);
604

605
        r = fflush_and_check(f);
×
606
        if (r < 0)
×
607
                return log_error_errno(r, "Failed to write unit %s: %m", unit);
×
608

609
        return generator_add_symlink(dir, where_unit, "requires", unit);
×
610
}
611

612
int generator_hook_up_mkfs(
×
613
                const char *dir,
614
                const char *what,
615
                const char *where,
616
                const char *type) {
617

618
        _cleanup_free_ char *node = NULL, *unit = NULL, *escaped = NULL, *where_unit = NULL;
×
619
        _cleanup_fclose_ FILE *f = NULL;
×
620
        const char *fsck_unit;
×
621
        int r;
×
622

623
        assert(dir);
×
624
        assert(what);
×
625
        assert(where);
×
626

627
        node = fstab_node_to_udev_node(what);
×
628
        if (!node)
×
629
                return log_oom();
×
630

631
        /* Nothing to work on. */
632
        if (!is_device_path(node))
×
633
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
634
                                       "Cannot format something that is not a device node: %s",
635
                                       node);
636

637
        if (!type || streq(type, "auto"))
×
638
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
639
                                       "Cannot format partition %s, filesystem type is not specified",
640
                                       node);
641

642
        r = unit_name_from_path_instance("systemd-makefs", node, ".service", &unit);
×
643
        if (r < 0)
×
644
                return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
×
645
                                       node);
646

647
        if (in_initrd() && path_equal(where, "/sysroot"))
×
648
                fsck_unit = SPECIAL_FSCK_ROOT_SERVICE;
649
        else if (in_initrd() && path_equal(where, "/sysusr/usr"))
×
650
                fsck_unit = SPECIAL_FSCK_USR_SERVICE;
651
        else
652
                fsck_unit = "systemd-fsck@%i.service";
653

654
        escaped = cescape(node);
×
655
        if (!escaped)
×
656
                return log_oom();
×
657

658
        r = unit_name_from_path(where, ".mount", &where_unit);
×
659
        if (r < 0)
×
660
                return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
×
661
                                       where);
662

663
        r = generator_open_unit_file(dir, /* source = */ NULL, unit, &f);
×
664
        if (r < 0)
×
665
                return r;
666

667
        fprintf(f,
×
668
                "[Unit]\n"
669
                "Description=Make File System on %%f\n"
670
                "Documentation=man:systemd-makefs@.service(8)\n"
671
                "\n"
672
                "DefaultDependencies=no\n"
673
                "BindsTo=%%i.device\n"
674
                "After=%%i.device\n"
675
                /* fsck might or might not be used, so let's be safe and order
676
                 * ourselves before both systemd-fsck@.service and the mount unit. */
677
                "Before=%s %s\n"
678
                "Conflicts=shutdown.target\n"
679
                "Before=shutdown.target\n"
680
                "\n"
681
                "[Service]\n"
682
                "Type=oneshot\n"
683
                "RemainAfterExit=yes\n"
684
                "ExecStart="SYSTEMD_MAKEFS_PATH " %s %s\n"
685
                "TimeoutSec=infinity\n",
686
                fsck_unit,
687
                where_unit,
688
                type,
689
                escaped);
690
        // XXX: what about local-fs-pre.target?
691

692
        r = fflush_and_check(f);
×
693
        if (r < 0)
×
694
                return log_error_errno(r, "Failed to write unit %s: %m", unit);
×
695

696
        return generator_add_symlink(dir, where_unit, "requires", unit);
×
697
}
698

UNCOV
699
int generator_hook_up_growfs(
×
700
                const char *dir,
701
                const char *where,
702
                const char *target) {
703

UNCOV
704
        const char *growfs_unit, *growfs_unit_path;
×
UNCOV
705
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
UNCOV
706
        int r;
×
707

UNCOV
708
        assert(dir);
×
UNCOV
709
        assert(where);
×
710

UNCOV
711
        r = unit_name_from_path(where, ".mount", &where_unit);
×
UNCOV
712
        if (r < 0)
×
713
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
714

UNCOV
715
        if (empty_or_root(where)) {
×
716
                growfs_unit = SPECIAL_GROWFS_ROOT_SERVICE;
717
                growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_ROOT_SERVICE;
718
        } else {
719
                growfs_unit = SPECIAL_GROWFS_SERVICE;
×
720
                growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_SERVICE;
×
721

722
                r = unit_name_path_escape(where, &instance);
×
723
                if (r < 0)
×
724
                        return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
725
        }
726

UNCOV
727
        if (target) {
×
UNCOV
728
                r = generator_add_ordering(dir, target, "After", growfs_unit, instance);
×
UNCOV
729
                if (r < 0)
×
730
                        return r;
731
        }
732

UNCOV
733
        return generator_add_symlink_full(dir, where_unit, "wants", growfs_unit_path, instance);
×
734
}
735

UNCOV
736
int generator_hook_up_pcrfs(
×
737
                const char *dir,
738
                const char *where,
739
                const char *target) {
740

UNCOV
741
        const char *pcrfs_unit, *pcrfs_unit_path;
×
UNCOV
742
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
UNCOV
743
        int r;
×
744

UNCOV
745
        assert(dir);
×
UNCOV
746
        assert(where);
×
747

UNCOV
748
        r = unit_name_from_path(where, ".mount", &where_unit);
×
UNCOV
749
        if (r < 0)
×
750
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
751

UNCOV
752
        if (empty_or_root(where)) {
×
753
                pcrfs_unit = SPECIAL_PCRFS_ROOT_SERVICE;
754
                pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_ROOT_SERVICE;
755
        } else {
756
                pcrfs_unit = SPECIAL_PCRFS_SERVICE;
×
757
                pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_SERVICE;
×
758

759
                r = unit_name_path_escape(where, &instance);
×
760
                if (r < 0)
×
761
                        return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
762
        }
763

UNCOV
764
        if (target) {
×
UNCOV
765
                r = generator_add_ordering(dir, target, "After", pcrfs_unit, instance);
×
UNCOV
766
                if (r < 0)
×
767
                        return r;
768
        }
769

UNCOV
770
        return generator_add_symlink_full(dir, where_unit, "wants", pcrfs_unit_path, instance);
×
771
}
772

773
int generator_hook_up_validatefs(
×
774
                const char *dir,
775
                const char *where,
776
                const char *target) {
777

778
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
779
        const char *validatefs_unit, *validatefs_unit_path;
×
780
        int r;
×
781

782
        assert(dir);
×
783
        assert(where);
×
784

785
        /* never hook this in for the actual root fs, because it's too late then, we already are running from
786
         * the root fs, it makes no sense to validate it anymore */
787
        if (empty_or_root(where))
×
788
                return 0;
789

790
        r = unit_name_from_path(where, ".mount", &where_unit);
×
791
        if (r < 0)
×
792
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
793

794
        validatefs_unit = SPECIAL_VALIDATEFS_SERVICE;
×
795
        validatefs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_VALIDATEFS_SERVICE;
×
796

797
        r = unit_name_path_escape(where, &instance);
×
798
        if (r < 0)
×
799
                return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
800

801
        if (target) {
×
802
                r = generator_add_ordering(dir, target, "After", validatefs_unit, instance);
×
803
                if (r < 0)
×
804
                        return r;
805
        }
806

807
        return generator_add_symlink_full(dir, where_unit, "wants", validatefs_unit_path, instance);
×
808
}
809

810
int generator_hook_up_quotacheck(
×
811
                const char *dir,
812
                const char *what,
813
                const char *where,
814
                const char *target,
815
                const char *fstype) {
816

817
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
818
        int r;
×
819

820
        assert(dir);
×
821
        assert(where);
×
822

823
        if (isempty(fstype) || streq(fstype, "auto"))
×
824
                return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Couldn't determine filesystem type for %s, quota cannot be activated", what);
×
825
        if (!fstype_needs_quota(fstype))
×
826
                return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Quota was requested for %s, but not supported, ignoring: %s", what, fstype);
×
827

828
        /* quotacheck unit for system root */
829
        if (path_equal(where, "/"))
×
830
                return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTACHECK_ROOT_SERVICE);
×
831

832
        r = unit_name_path_escape(where, &instance);
×
833
        if (r < 0)
×
834
                return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
835

836
        if (target) {
×
837
                r = generator_add_ordering(dir, target, "After", SPECIAL_QUOTACHECK_SERVICE, instance);
×
838
                if (r < 0)
×
839
                        return r;
840
        }
841

842
        r = unit_name_from_path(where, ".mount", &where_unit);
×
843
        if (r < 0)
×
844
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
845

846
        return generator_add_symlink_full(dir, where_unit, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTACHECK_SERVICE, instance);
×
847
}
848

849
int generator_hook_up_quotaon(
×
850
                const char *dir,
851
                const char *where,
852
                const char *target) {
853

854
        _cleanup_free_ char *where_unit = NULL, *instance = NULL;
×
855
        int r;
×
856

857
        assert(dir);
×
858
        assert(where);
×
859

860
        /* quotaon unit for system root is not instantiated */
861
        if (path_equal(where, "/"))
×
862
                return generator_add_symlink(dir,  SPECIAL_LOCAL_FS_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTAON_ROOT_SERVICE);
×
863

864
        r = unit_name_path_escape(where, &instance);
×
865
        if (r < 0)
×
866
                return log_error_errno(r, "Failed to escape path '%s': %m", where);
×
867

868
        if (target) {
×
869
                r = generator_add_ordering(dir, target, "After", SPECIAL_QUOTAON_SERVICE, instance);
×
870
                if (r < 0)
×
871
                        return r;
872
        }
873

874
        r = unit_name_from_path(where, ".mount", &where_unit);
×
875
        if (r < 0)
×
876
                return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
×
877

878
        return generator_add_symlink_full(dir, where_unit, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_QUOTAON_SERVICE, instance);
×
879
}
880

881
int generator_enable_remount_fs_service(const char *dir) {
2✔
882
        /* Pull in systemd-remount-fs.service */
883
        return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants",
2✔
884
                                     SYSTEM_DATA_UNIT_DIR "/" SPECIAL_REMOUNT_FS_SERVICE);
885
}
886

887
int generator_write_blockdev_dependency(FILE *f, const char *what) {
24✔
888
        _cleanup_free_ char *escaped = NULL;
24✔
889
        int r;
24✔
890

891
        assert(f);
24✔
892
        assert(what);
24✔
893

894
        if (!path_startswith(what, "/dev/"))
24✔
895
                return 0;
896

897
        r = unit_name_path_escape(what, &escaped);
16✔
898
        if (r < 0)
16✔
899
                return log_error_errno(r, "Failed to escape device node path %s: %m", what);
×
900

901
        fprintf(f,
16✔
902
                "After=blockdev@%s.target\n",
903
                escaped);
904

905
        return 0;
906
}
907

908
int generator_write_cryptsetup_unit_section(FILE *f, const char *source) {
22✔
909
        assert(f);
22✔
910

911
        fprintf(f,
22✔
912
                "[Unit]\n"
913
                "Description=Cryptography Setup for %%I\n"
914
                "Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)\n");
915

916
        if (source)
22✔
917
                fprintf(f, "SourcePath=%s\n", source);
22✔
918

919
        fprintf(f,
22✔
920
                "\n"
921
                "DefaultDependencies=no\n"
922
                "After=cryptsetup-pre.target systemd-udevd-kernel.socket systemd-tpm2-setup-early.service\n"
923
                "Before=blockdev@dev-mapper-%%i.target\n"
924
                "Wants=blockdev@dev-mapper-%%i.target\n"
925
                "IgnoreOnIsolate=true\n");
926

927
        return 0;
22✔
928
}
929

930
int generator_write_cryptsetup_service_section(
22✔
931
                FILE *f,
932
                const char *name,
933
                const char *what,
934
                const char *key_file,
935
                const char *options) {
936

937
        _cleanup_free_ char *name_escaped = NULL, *what_escaped = NULL, *key_file_escaped = NULL, *options_escaped = NULL;
22✔
938

939
        assert(f);
22✔
940
        assert(name);
22✔
941
        assert(what);
22✔
942

943
        name_escaped = specifier_escape(name);
22✔
944
        if (!name_escaped)
22✔
945
                return log_oom();
×
946

947
        what_escaped = specifier_escape(what);
22✔
948
        if (!what_escaped)
22✔
949
                return log_oom();
×
950

951
        if (key_file) {
22✔
952
                key_file_escaped = specifier_escape(key_file);
22✔
953
                if (!key_file_escaped)
22✔
954
                        return log_oom();
×
955
        }
956

957
        if (options) {
22✔
958
                options_escaped = specifier_escape(options);
22✔
959
                if (!options_escaped)
22✔
960
                        return log_oom();
×
961
        }
962

963
        fprintf(f,
22✔
964
                "\n"
965
                "[Service]\n"
966
                "Type=oneshot\n"
967
                "RemainAfterExit=yes\n"
968
                "TimeoutSec=infinity\n"   /* The binary handles timeouts on its own */
969
                "KeyringMode=shared\n"    /* Make sure we can share cached keys among instances */
970
                "OOMScoreAdjust=500\n"    /* Unlocking can allocate a lot of memory if Argon2 is used */
971
                "ImportCredential=cryptsetup.*\n"
972
                "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n"
973
                "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n",
974
                name_escaped, what_escaped, strempty(key_file_escaped), strempty(options_escaped),
975
                name_escaped);
976

977
        return 0;
978
}
979

980
int generator_write_veritysetup_unit_section(FILE *f, const char *source) {
×
981
        assert(f);
×
982

983
        fprintf(f,
×
984
                "[Unit]\n"
985
                "Description=Integrity Protection Setup for %%I\n"
986
                "Documentation=man:veritytab(5) man:systemd-veritysetup-generator(8) man:systemd-veritysetup@.service(8)\n");
987

988
        if (source)
×
989
                fprintf(f, "SourcePath=%s\n", source);
×
990

991
        fprintf(f,
×
992
                "DefaultDependencies=no\n"
993
                "IgnoreOnIsolate=true\n"
994
                "After=veritysetup-pre.target systemd-udevd-kernel.socket\n"
995
                "Before=blockdev@dev-mapper-%%i.target\n"
996
                "Wants=blockdev@dev-mapper-%%i.target\n");
997

998
        return 0;
×
999
}
1000

1001
int generator_write_veritysetup_service_section(
×
1002
                FILE *f,
1003
                const char *name,
1004
                const char *data_what,
1005
                const char *hash_what,
1006
                const char *roothash,
1007
                const char *options) {
1008

1009
        _cleanup_free_ char *name_escaped = NULL, *data_what_escaped = NULL, *hash_what_escaped = NULL,
×
1010
                            *roothash_escaped = NULL, *options_escaped = NULL;
×
1011

1012
        assert(f);
×
1013
        assert(name);
×
1014
        assert(data_what);
×
1015
        assert(hash_what);
×
1016

1017
        name_escaped = specifier_escape(name);
×
1018
        if (!name_escaped)
×
1019
                return log_oom();
×
1020

1021
        data_what_escaped = specifier_escape(data_what);
×
1022
        if (!data_what_escaped)
×
1023
                return log_oom();
×
1024

1025
        hash_what_escaped = specifier_escape(hash_what);
×
1026
        if (!hash_what_escaped)
×
1027
                return log_oom();
×
1028

1029
        if (roothash) {
×
1030
                roothash_escaped = specifier_escape(roothash);
×
1031
                if (!roothash_escaped)
×
1032
                        return log_oom();
×
1033
        }
1034

1035
        if (options) {
×
1036
                options_escaped = specifier_escape(options);
×
1037
                if (!options_escaped)
×
1038
                        return log_oom();
×
1039
        }
1040

1041
        fprintf(f,
×
1042
                "\n"
1043
                "[Service]\n"
1044
                "Type=oneshot\n"
1045
                "RemainAfterExit=yes\n"
1046
                "ExecStart=" SYSTEMD_VERITYSETUP_PATH " attach '%s' '%s' '%s' '%s' '%s'\n"
1047
                "ExecStop=" SYSTEMD_VERITYSETUP_PATH " detach '%s'\n",
1048
                name_escaped, data_what_escaped, hash_what_escaped, empty_to_dash(roothash_escaped), strempty(options_escaped),
1049
                name_escaped);
1050

1051
        return 0;
1052
}
1053

1054
void log_setup_generator(void) {
260✔
1055
        if (invoked_by_systemd()) {
260✔
1056
                /* Disable talking to syslog/journal (i.e. the two IPC-based loggers) if we run in system context. */
1057
                if (streq_ptr(getenv("SYSTEMD_SCOPE"), "system"))
189✔
UNCOV
1058
                        log_set_prohibit_ipc(true);
×
1059

1060
                /* This effectively means: journal for per-user service manager generators, kmsg for per-system service manager generators */
1061
                log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
189✔
1062
        } else
1063
                log_set_target(LOG_TARGET_AUTO);
71✔
1064

1065
        log_parse_environment();
260✔
1066
        log_open();
260✔
1067
}
260✔
1068

1069
bool generator_soft_rebooted(void) {
×
1070
        static int cached = -1;
×
1071
        int r;
×
1072

1073
        if (cached >= 0)
×
1074
                return cached;
×
1075

1076
        const char *e = secure_getenv("SYSTEMD_SOFT_REBOOTS_COUNT");
×
1077
        if (!e)
×
1078
                return (cached = false);
×
1079

1080
        unsigned u;
×
1081

1082
        r = safe_atou(e, &u);
×
1083
        if (r < 0) {
×
1084
                log_debug_errno(r, "Failed to parse $SYSTEMD_SOFT_REBOOTS_COUNT, assuming the system hasn't soft-rebooted: %m");
×
1085
                return (cached = false);
×
1086
        }
1087

1088
        return (cached = (u > 0));
×
1089
}
1090

1091
GptAutoRoot parse_gpt_auto_root(const char *switch_name, const char *value) {
17✔
1092
        assert(switch_name);
17✔
1093
        assert(value);
17✔
1094

1095
        /* Parses the 'gpt-auto'/'gpt-auto-root'/'dissect'/'dissect-force' parameters to root=
1096
         *
1097
         * note that we are not using a regular string table here, because the mode names don't fully match
1098
         * the parameter names. And root= being something else is not an error. */
1099

1100
        if (streq(value, "gpt-auto")) {
17✔
1101
                log_debug("Enabling partition auto-detection (respecting factory reset mode), %s is explicitly set to 'gpt-auto'.", switch_name);
×
1102
                return GPT_AUTO_ROOT_ON;
×
1103
        }
1104

1105
        if (streq(value, "gpt-auto-force")) {
17✔
1106
                log_debug("Enabling partition auto-detection (ignoring factory reset mode), %s is explicitly set to 'gpt-auto-force'.", switch_name);
×
1107
                return GPT_AUTO_ROOT_FORCE;
×
1108
        }
1109

1110
        if (streq(value, "dissect")) {
17✔
1111
                log_debug("Enabling partition auto-detection via full image dissection (respecting factory reset mode), %s is explicitly set to 'dissect'.", switch_name);
×
1112
                return GPT_AUTO_ROOT_DISSECT;
×
1113
        }
1114

1115
        if (streq(value, "dissect-force")) {
17✔
1116
                log_debug("Enabling partition auto-detection via full image dissection (ignoring factory reset mode), %s is explicitly set to 'dissect-force'.", switch_name);
×
1117
                return GPT_AUTO_ROOT_DISSECT_FORCE;
×
1118
        }
1119

1120
        if (streq(value, "off"))
17✔
1121
                log_debug("Disabling partition auto-detection, %s handling is explicitly turned off.", switch_name);
×
1122
        else
1123
                log_debug("Disabling partition auto-detection, %s is neither unset, nor set to 'gpt-auto', 'gpt-auto-force', 'dissect' or 'dissect-force'.", switch_name);
17✔
1124

1125
        return GPT_AUTO_ROOT_OFF;
1126
}
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