• 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

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

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

6
#include "alloc-util.h"
7
#include "cryptsetup-util.h"
8
#include "dropin.h"
9
#include "escape.h"
10
#include "fd-util.h"
11
#include "fileio.h"
12
#include "fstab-util.h"
13
#include "generator.h"
14
#include "hashmap.h"
15
#include "id128-util.h"
16
#include "log.h"
17
#include "mkdir.h"
18
#include "parse-util.h"
19
#include "path-util.h"
20
#include "proc-cmdline.h"
21
#include "specifier.h"
22
#include "string-util.h"
23
#include "strv.h"
24
#include "time-util.h"
25
#include "unit-name.h"
26

27
typedef struct crypto_device {
28
        char *uuid;
29
        char *keyfile;
30
        char *keydev;
31
        char *headerdev;
32
        char *datadev;
33
        char *name;
34
        char *options;
35
        bool create;
36
} crypto_device;
37

38
static const char *arg_dest = NULL;
39
static bool arg_enabled = true;
40
static bool arg_read_crypttab = true;
41
static const char *arg_crypttab = NULL;
42
static const char *arg_runtime_directory = NULL;
43
static bool arg_allow_list = false;
44
static Hashmap *arg_disks = NULL;
45
static char *arg_default_options = NULL;
46
static char *arg_default_keyfile = NULL;
47

48
STATIC_DESTRUCTOR_REGISTER(arg_disks, hashmap_freep);
1✔
49
STATIC_DESTRUCTOR_REGISTER(arg_default_options, freep);
1✔
50
STATIC_DESTRUCTOR_REGISTER(arg_default_keyfile, freep);
1✔
51

52
static int split_locationspec(const char *locationspec, char **ret_file, char **ret_device) {
35✔
53
        _cleanup_free_ char *file = NULL, *device = NULL;
35✔
54
        const char *c;
35✔
55

56
        assert(ret_file);
35✔
57
        assert(ret_device);
35✔
58

59
        if (!locationspec) {
35✔
60
                *ret_file = *ret_device = NULL;
×
61
                return 0;
×
62
        }
63

64
        c = strrchr(locationspec, ':');
35✔
65
        if (c) {
35✔
66
                /* The device part has to be either an absolute path to device node (/dev/something,
67
                 * /dev/foo/something, or even possibly /dev/foo/something:part), or a fstab device
68
                 * specification starting with LABEL= or similar. The file part has the same syntax.
69
                 *
70
                 * Let's try to guess if the second part looks like a device specification, or just part of a
71
                 * filename with a colon. fstab_node_to_udev_node() will convert the fstab device syntax to
72
                 * an absolute path. If we didn't get an absolute path, assume that it is just part of the
73
                 * first file argument. */
74

75
                device = fstab_node_to_udev_node(c + 1);
5✔
76
                if (!device)
5✔
77
                        return log_oom();
×
78

79
                if (path_is_absolute(device))
5✔
80
                        file = strndup(locationspec, c-locationspec);
5✔
81
                else {
82
                        log_debug("Location specification argument contains a colon, but \"%s\" doesn't look like a device specification.\n"
×
83
                                  "Assuming that \"%s\" is a single device specification.",
84
                                  c + 1, locationspec);
85
                        device = mfree(device);
×
86
                        c = NULL;
×
87
                }
88
        }
89

90
        if (!c)
5✔
91
                /* No device specified */
92
                file = strdup(locationspec);
30✔
93

94
        if (!file)
35✔
95
                return log_oom();
×
96

97
        *ret_file = TAKE_PTR(file);
35✔
98
        *ret_device = TAKE_PTR(device);
35✔
99

100
        return 0;
35✔
101
}
102

103
static int generate_device_mount(
4✔
104
                const char *name,
105
                const char *device,
106
                const char *type_prefix, /* "keydev" or "headerdev" */
107
                const char *device_timeout,
108
                bool canfail,
109
                bool readonly,
110
                char **unit,
111
                char **mount) {
112

113
        _cleanup_free_ char *u = NULL, *where = NULL, *name_escaped = NULL, *device_unit = NULL;
4✔
114
        _cleanup_fclose_ FILE *f = NULL;
4✔
115
        int r;
4✔
116
        usec_t timeout_us;
4✔
117

118
        assert(name);
4✔
119
        assert(device);
4✔
120
        assert(unit);
4✔
121
        assert(mount);
4✔
122

123
        r = mkdir_parents(arg_runtime_directory, 0755);
4✔
124
        if (r < 0)
4✔
125
                return r;
126

127
        r = mkdir(arg_runtime_directory, 0700);
4✔
128
        if (r < 0 && errno != EEXIST)
4✔
129
                return -errno;
×
130

131
        name_escaped = cescape(name);
4✔
132
        if (!name_escaped)
4✔
133
                return -ENOMEM;
134

135
        where = strjoin(arg_runtime_directory, "/", type_prefix, "-", name_escaped);
4✔
136
        if (!where)
4✔
137
                return -ENOMEM;
138

139
        r = mkdir(where, 0700);
4✔
140
        if (r < 0 && errno != EEXIST)
4✔
141
                return -errno;
×
142

143
        r = unit_name_from_path(where, ".mount", &u);
4✔
144
        if (r < 0)
4✔
145
                return r;
146

147
        r = generator_open_unit_file(arg_dest, NULL, u, &f);
4✔
148
        if (r < 0)
4✔
149
                return r;
150

151
        fprintf(f,
10✔
152
                "[Unit]\n"
153
                "DefaultDependencies=no\n\n"
154
                "[Mount]\n"
155
                "What=%s\n"
156
                "Where=%s\n"
157
                "Options=%s%s\n", device, where, readonly ? "ro" : "rw", canfail ? ",nofail" : "");
158

159
        if (device_timeout) {
4✔
160
                r = parse_sec_fix_0(device_timeout, &timeout_us);
×
161
                if (r >= 0) {
×
162
                        r = unit_name_from_path(device, ".device", &device_unit);
×
163
                        if (r < 0)
×
164
                                return log_error_errno(r, "Failed to generate unit name: %m");
×
165

166
                        r = write_drop_in_format(arg_dest, device_unit, 90, "device-timeout",
×
167
                                "# Automatically generated by systemd-cryptsetup-generator \n\n"
168
                                "[Unit]\nJobRunningTimeoutSec=%s", device_timeout);
169
                        if (r < 0)
×
170
                                return log_error_errno(r, "Failed to write device drop-in: %m");
×
171

172
                } else
173
                        log_warning_errno(r, "Failed to parse %s, ignoring: %m", device_timeout);
×
174

175
        }
176

177
        r = fflush_and_check(f);
4✔
178
        if (r < 0)
4✔
179
                return r;
180

181
        *unit = TAKE_PTR(u);
4✔
182
        *mount = TAKE_PTR(where);
4✔
183

184
        return 0;
4✔
185
}
186

187
static int generate_device_umount(const char *name,
4✔
188
                                  const char *device_mount,
189
                                  const char *type_prefix, /* "keydev" or "headerdev" */
190
                                  char **ret_umount_unit) {
191
        _cleanup_fclose_ FILE *f = NULL;
4✔
192
        _cleanup_free_ char *u = NULL, *name_escaped = NULL, *mount = NULL;
4✔
193
        int r;
4✔
194

195
        assert(name);
4✔
196
        assert(ret_umount_unit);
4✔
197

198
        name_escaped = cescape(name);
4✔
199
        if (!name_escaped)
4✔
200
                return -ENOMEM;
201

202
        u = strjoin(type_prefix, "-", name_escaped, "-umount.service");
4✔
203
        if (!u)
4✔
204
                return -ENOMEM;
205

206
        r = unit_name_from_path(device_mount, ".mount", &mount);
4✔
207
        if (r < 0)
4✔
208
                return r;
209

210
        r = generator_open_unit_file(arg_dest, NULL, u, &f);
4✔
211
        if (r < 0)
4✔
212
                return r;
213

214
        fprintf(f,
4✔
215
                "[Unit]\n"
216
                "DefaultDependencies=no\n"
217
                "After=%s\n\n"
218
                "[Service]\n"
219
                "ExecStart=-" UMOUNT_PATH " %s\n\n", mount, device_mount);
220

221
        r = fflush_and_check(f);
4✔
222
        if (r < 0)
4✔
223
                return r;
224

225
        *ret_umount_unit = TAKE_PTR(u);
4✔
226
        return 0;
4✔
227
}
228

229
static int print_dependencies(FILE *f, const char* device_path, const char* timeout_value, bool canfail) {
30✔
230
        int r;
30✔
231

232
        assert(f);
30✔
233
        assert(device_path);
30✔
234

235
        if (!mangle_none(device_path))
30✔
236
                /* None, nothing to do */
237
                return 0;
30✔
238

239
        if (PATH_IN_SET(device_path,
24✔
240
                        "/dev/urandom",
241
                        "/dev/random",
242
                        "/dev/hw_random",
243
                        "/dev/hwrng")) {
244
                /* RNG device, add random dep */
245
                fputs("After=systemd-random-seed.service\n", f);
×
246
                return 0;
×
247
        }
248

249
        _cleanup_free_ char *udev_node = fstab_node_to_udev_node(device_path);
48✔
250
        if (!udev_node)
24✔
251
                return log_oom();
×
252

253
        if (path_equal(udev_node, "/dev/null"))
24✔
254
                return 0;
255

256
        if (path_startswith(udev_node, "/dev/")) {
24✔
257
                /* We are dealing with a block device, add dependency for corresponding unit */
258
                _cleanup_free_ char *unit = NULL;
×
259

260
                r = unit_name_from_path(udev_node, ".device", &unit);
×
261
                if (r < 0)
×
262
                        return log_error_errno(r, "Failed to generate unit name: %m");
×
263

264
                fprintf(f, "After=%1$s\n", unit);
×
265
                if (canfail) {
×
266
                        fprintf(f, "Wants=%1$s\n", unit);
×
267
                        if (timeout_value) {
×
268
                                r = write_drop_in_format(arg_dest, unit, 90, "device-timeout",
×
269
                                        "# Automatically generated by systemd-cryptsetup-generator \n\n"
270
                                        "[Unit]\nJobRunningTimeoutSec=%s", timeout_value);
271
                                if (r < 0)
×
272
                                        return log_error_errno(r, "Failed to write device drop-in: %m");
×
273
                        }
274
                } else
275
                        fprintf(f, "Requires=%1$s\n", unit);
×
276
        } else {
277
                /* Regular file, add mount dependency */
278
                _cleanup_free_ char *escaped_path = specifier_escape(device_path);
48✔
279
                if (!escaped_path)
24✔
280
                        return log_oom();
×
281

282
                fprintf(f, "%s=%s\n", canfail ? "WantsMountsFor" : "RequiresMountsFor", escaped_path);
46✔
283
        }
284

285
        return 0;
286
}
287

288
static bool attach_in_initrd(const char *name, const char *options) {
22✔
289
        assert(name);
22✔
290

291
        /* Imply x-initrd.attach in case the volume name is among those defined in the Discoverable Partition
292
         * Specification for partitions that we require to be mounted during the initrd → host transition,
293
         * i.e. for the root fs itself, and /usr/. This mirrors similar behaviour in
294
         * systemd-fstab-generator. */
295

296
        return fstab_test_option(options, "x-initrd.attach\0") ||
44✔
297
                STR_IN_SET(name, "root", "usr");
22✔
298
}
299

300
static int create_disk(
22✔
301
                const char *name,
302
                const char *device,
303
                const char *key_file,
304
                const char *keydev,
305
                const char *headerdev,
306
                const char *options,
307
                const char *source) {
308

309
        _cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL,
×
310
                *keydev_mount = NULL, *keyfile_timeout_value = NULL,
×
311
                *filtered = NULL, *u_escaped = NULL, *name_escaped = NULL, *header_path = NULL, *key_file_buffer = NULL,
×
312
                *tmp_fstype = NULL, *filtered_header = NULL, *headerdev_mount = NULL;
22✔
313
        _cleanup_fclose_ FILE *f = NULL;
22✔
314
        const char *dmname;
22✔
315
        bool noauto, nofail, swap, netdev;
22✔
316
        int r, detached_header, keyfile_can_timeout, tmp;
22✔
317

318
        assert(name);
22✔
319
        assert(device);
22✔
320

321
        noauto = fstab_test_yes_no_option(options, "noauto\0" "auto\0");
22✔
322
        nofail = fstab_test_yes_no_option(options, "nofail\0" "fail\0");
22✔
323
        swap = fstab_test_option(options, "swap\0");
22✔
324
        netdev = fstab_test_option(options, "_netdev\0");
22✔
325

326
        keyfile_can_timeout = fstab_filter_options(options,
22✔
327
                                                   "keyfile-timeout\0",
328
                                                   NULL, &keyfile_timeout_value, NULL, NULL);
329
        if (keyfile_can_timeout < 0)
22✔
330
                return log_error_errno(keyfile_can_timeout, "Failed to parse keyfile-timeout= option value: %m");
×
331

332
        detached_header = fstab_filter_options(
42✔
333
                options,
334
                "header\0",
335
                NULL,
336
                &header_path,
337
                NULL,
338
                headerdev ? &filtered_header : NULL);
339
        if (detached_header < 0)
22✔
340
                return log_error_errno(detached_header, "Failed to parse header= option value: %m");
×
341

342
        tmp = fstab_filter_options(options, "tmp\0", NULL, &tmp_fstype, NULL, NULL);
22✔
343
        if (tmp < 0)
22✔
344
                return log_error_errno(tmp, "Failed to parse tmp= option value: %m");
×
345

346
        if (tmp && swap)
22✔
347
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
348
                                       "Device '%s' cannot be both 'tmp' and 'swap'. Ignoring.",
349
                                       name);
350

351
        name_escaped = specifier_escape(name);
22✔
352
        if (!name_escaped)
22✔
353
                return log_oom();
×
354

355
        e = unit_name_escape(name);
22✔
356
        if (!e)
22✔
357
                return log_oom();
×
358

359
        u = fstab_node_to_udev_node(device);
22✔
360
        if (!u)
22✔
361
                return log_oom();
×
362

363
        r = unit_name_build("systemd-cryptsetup", e, ".service", &n);
22✔
364
        if (r < 0)
22✔
365
                return log_error_errno(r, "Failed to generate unit name: %m");
×
366

367
        u_escaped = specifier_escape(u);
22✔
368
        if (!u_escaped)
22✔
369
                return log_oom();
×
370

371
        r = unit_name_from_path(u, ".device", &d);
22✔
372
        if (r < 0)
22✔
373
                return log_error_errno(r, "Failed to generate unit name: %m");
×
374

375
        if (keydev && !key_file)
22✔
376
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
377
                                       "Key device is specified, but path to the key file is missing.");
378

379
        r = generator_open_unit_file(arg_dest, NULL, n, &f);
22✔
380
        if (r < 0)
22✔
381
                return r;
382

383
        r = generator_write_cryptsetup_unit_section(f, source);
22✔
384
        if (r < 0)
22✔
385
                return r;
386

387
        if (netdev)
22✔
388
                fprintf(f, "After=remote-fs-pre.target\n");
×
389

390
        /* If initrd takes care of attaching the disk then it should also detach it during shutdown. */
391
        if (!attach_in_initrd(name, options))
22✔
392
                fprintf(f,
22✔
393
                        "Conflicts=umount.target\n"
394
                        "Before=umount.target\n");
395

396
        if (keydev) {
22✔
397
                _cleanup_free_ char *unit = NULL, *umount_unit = NULL;
2✔
398

399
                r = generate_device_mount(
2✔
400
                        name,
401
                        keydev,
402
                        "keydev",
403
                        keyfile_timeout_value,
404
                        /* canfail = */ keyfile_can_timeout > 0,
405
                        /* readonly= */ true,
406
                        &unit,
407
                        &keydev_mount);
408
                if (r < 0)
2✔
409
                        return log_error_errno(r, "Failed to generate keydev mount unit: %m");
×
410

411
                r = generate_device_umount(name, keydev_mount, "keydev", &umount_unit);
2✔
412
                if (r < 0)
2✔
413
                        return log_error_errno(r, "Failed to generate keydev umount unit: %m");
×
414

415
                key_file_buffer = path_join(keydev_mount, key_file);
2✔
416
                if (!key_file_buffer)
2✔
417
                        return log_oom();
×
418

419
                key_file = key_file_buffer;
2✔
420

421
                fprintf(f, "After=%s\n", unit);
2✔
422
                if (keyfile_can_timeout > 0)
2✔
423
                        fprintf(f, "Wants=%s\n", unit);
×
424
                else
425
                        fprintf(f, "Requires=%s\n", unit);
2✔
426

427
                if (umount_unit)
2✔
428
                        fprintf(f,
2✔
429
                                "Wants=%s\n"
430
                                "Before=%s\n",
431
                                umount_unit,
432
                                umount_unit
433
                        );
434
        }
435

436
        if (headerdev) {
22✔
437
                _cleanup_free_ char *unit = NULL, *umount_unit = NULL, *p = NULL;
2✔
438

439
                r = generate_device_mount(
2✔
440
                        name,
441
                        headerdev,
442
                        "headerdev",
443
                        NULL,
444
                        /* canfail=  */ false, /* header is always necessary */
445
                        /* readonly= */ false, /* LUKS2 recovery requires rw header access */
446
                        &unit,
447
                        &headerdev_mount);
448
                if (r < 0)
2✔
449
                        return log_error_errno(r, "Failed to generate header device mount unit: %m");
×
450

451
                r = generate_device_umount(name, headerdev_mount, "headerdev", &umount_unit);
2✔
452
                if (r < 0)
2✔
453
                        return log_error_errno(r, "Failed to generate header device umount unit: %m");
×
454

455
                p = path_join(headerdev_mount, header_path);
2✔
456
                if (!p)
2✔
457
                        return log_oom();
×
458

459
                free_and_replace(header_path, p);
2✔
460

461
                if (isempty(filtered_header))
2✔
462
                        p = strjoin("header=", header_path);
×
463
                else
464
                        p = strjoin(filtered_header, ",header=", header_path);
2✔
465

466
                if (!p)
2✔
467
                        return log_oom();
×
468

469
                free_and_replace(filtered_header, p);
2✔
470
                options = filtered_header;
2✔
471

472
                fprintf(f, "After=%s\n"
2✔
473
                           "Requires=%s\n", unit, unit);
474

475
                if (umount_unit)
2✔
476
                        fprintf(f,
2✔
477
                                "Wants=%s\n"
478
                                "Before=%s\n",
479
                                umount_unit,
480
                                umount_unit
481
                        );
482
        }
483

484
        if (!nofail)
22✔
485
                fprintf(f,
42✔
486
                        "Before=%s\n",
487
                        netdev ? "remote-cryptsetup.target" : "cryptsetup.target");
488

489
        if (key_file && !keydev) {
22✔
490
                r = print_dependencies(f, key_file,
40✔
491
                        keyfile_timeout_value,
492
                        /* canfail= */ keyfile_can_timeout > 0 || nofail);
20✔
493
                if (r < 0)
20✔
494
                        return r;
495
        }
496

497
        /* Check if a header option was specified */
498
        if (detached_header > 0 && !headerdev) {
22✔
499
                r = print_dependencies(f, header_path,
10✔
500
                        /* timeout_value= */ NULL,
501
                        /* canfail= */ nofail);
502
                if (r < 0)
10✔
503
                        return r;
504
        }
505

506
        if (path_startswith(u, "/dev/"))
22✔
UNCOV
507
                fprintf(f,
×
508
                        "BindsTo=%s\n"
509
                        "After=%s\n",
510
                        d, d);
511
        else
512
                /* For loopback devices make sure to explicitly load loop.ko, as this code might run very
513
                 * early where device nodes created via systemd-tmpfiles-setup-dev.service might not be
514
                 * around yet. Hence let's sync on the module itself. */
515
                fprintf(f,
22✔
516
                        "RequiresMountsFor=%s\n"
517
                        "Wants=modprobe@loop.service\n"
518
                        "After=modprobe@loop.service\n",
519
                        u_escaped);
520

521
        r = generator_write_device_timeout(arg_dest, device, options, &filtered);
22✔
522
        if (r < 0)
22✔
523
                log_warning_errno(r, "Failed to write device timeout drop-in: %m");
×
524

525
        r = generator_write_cryptsetup_service_section(f, name, u, key_file, filtered);
22✔
526
        if (r < 0)
22✔
527
                return r;
528

529
        if (tmp) {
22✔
530
                _cleanup_free_ char *tmp_fstype_escaped = NULL;
×
531

532
                if (tmp_fstype) {
×
533
                        tmp_fstype_escaped = specifier_escape(tmp_fstype);
×
534
                        if (!tmp_fstype_escaped)
×
535
                                return log_oom();
×
536
                }
537

538
                fprintf(f,
×
539
                        "ExecStartPost=" LIBEXECDIR "/systemd-makefs '%s' '/dev/mapper/%s'\n",
540
                        tmp_fstype_escaped ?: "ext4", name_escaped);
541
        }
542

543
        if (swap)
22✔
544
                fprintf(f,
×
545
                        "ExecStartPost=" LIBEXECDIR "/systemd-makefs swap '/dev/mapper/%s'\n",
546
                        name_escaped);
547

548
        r = fflush_and_check(f);
22✔
549
        if (r < 0)
22✔
550
                return log_error_errno(r, "Failed to write unit file %s: %m", n);
×
551

552
        if (!noauto) {
22✔
553
                r = generator_add_symlink(arg_dest,
65✔
554
                                          netdev ? "remote-cryptsetup.target" : "cryptsetup.target",
555
                                          nofail ? "wants" : "requires", n);
556
                if (r < 0)
22✔
557
                        return r;
558
        }
559

560
        dmname = strjoina("dev-mapper-", e, ".device");
154✔
561
        r = generator_add_symlink(arg_dest, dmname, "requires", n);
22✔
562
        if (r < 0)
22✔
563
                return r;
564

565
        if (!noauto && !nofail) {
22✔
566
                r = write_drop_in(arg_dest, dmname, 40, "device-timeout",
21✔
567
                                  "# Automatically generated by systemd-cryptsetup-generator\n\n"
568
                                  "[Unit]\n"
569
                                  "JobTimeoutSec=infinity\n");
570
                if (r < 0)
21✔
571
                        log_warning_errno(r, "Failed to write device timeout drop-in: %m");
22✔
572
        }
573

574
        return 0;
575
}
576

577
static crypto_device* crypt_device_free(crypto_device *d) {
1✔
578
        if (!d)
1✔
579
                return NULL;
580

581
        free(d->uuid);
1✔
582
        free(d->keyfile);
1✔
583
        free(d->keydev);
1✔
584
        free(d->headerdev);
1✔
585
        free(d->datadev);
1✔
586
        free(d->name);
1✔
587
        free(d->options);
1✔
588
        return mfree(d);
1✔
589
}
590

591
static crypto_device *get_crypto_device(const char *uuid) {
2✔
592
        int r;
2✔
593
        crypto_device *d;
2✔
594

595
        assert(uuid);
2✔
596

597
        d = hashmap_get(arg_disks, uuid);
2✔
598
        if (!d) {
2✔
599
                d = new0(struct crypto_device, 1);
1✔
600
                if (!d)
1✔
601
                        return NULL;
602

603
                d->uuid = strdup(uuid);
1✔
604
                if (!d->uuid)
1✔
605
                        return mfree(d);
×
606

607
                r = hashmap_put(arg_disks, d->uuid, d);
1✔
608
                if (r < 0) {
1✔
609
                        free(d->uuid);
×
610
                        return mfree(d);
×
611
                }
612
        }
613

614
        return d;
615
}
616

617
static bool warn_uuid_invalid(const char *uuid, const char *key) {
2✔
618
        assert(key);
2✔
619

620
        if (!id128_is_valid(uuid)) {
2✔
621
                log_warning("Failed to parse %s= kernel command line switch. UUID is invalid, ignoring.", key);
×
622
                return true;
×
623
        }
624

625
        return false;
626
}
627

628
static int filter_header_device(const char *options,
23✔
629
                                char **ret_headerdev,
630
                                char **ret_filtered_headerdev_options) {
631
        int r;
23✔
632
        _cleanup_free_ char *headerfile = NULL, *headerdev = NULL, *headerspec = NULL,
×
633
                            *filtered_headerdev = NULL, *filtered_headerspec = NULL;
23✔
634

635
        assert(ret_headerdev);
23✔
636
        assert(ret_filtered_headerdev_options);
23✔
637

638
        r = fstab_filter_options(options, "header\0", NULL, &headerspec, NULL, &filtered_headerspec);
23✔
639
        if (r < 0)
23✔
640
                return log_error_errno(r, "Failed to parse header= option value: %m");
×
641

642
        if (r > 0) {
23✔
643
                r = split_locationspec(headerspec, &headerfile, &headerdev);
12✔
644
                if (r < 0)
12✔
645
                        return r;
646

647
                if (isempty(filtered_headerspec))
12✔
648
                        filtered_headerdev = strjoin("header=", headerfile);
×
649
                else
650
                        filtered_headerdev = strjoin(filtered_headerspec, ",header=", headerfile);
12✔
651

652
                if (!filtered_headerdev)
12✔
653
                        return log_oom();
×
654
        } else
655
                filtered_headerdev = TAKE_PTR(filtered_headerspec);
11✔
656

657
        *ret_filtered_headerdev_options = TAKE_PTR(filtered_headerdev);
23✔
658
        *ret_headerdev = TAKE_PTR(headerdev);
23✔
659

660
        return 0;
23✔
661
}
662

663
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
47✔
664
        _cleanup_free_ char *uuid = NULL, *uuid_value = NULL;
47✔
665
        crypto_device *d;
47✔
666
        int r;
47✔
667

668
        if (streq(key, "luks")) {
47✔
669

670
                r = value ? parse_boolean(value) : 1;
×
671
                if (r < 0)
×
672
                        log_warning("Failed to parse luks= kernel command line switch %s. Ignoring.", value);
×
673
                else
674
                        arg_enabled = r;
×
675

676
        } else if (streq(key, "luks.crypttab")) {
47✔
677

678
                r = value ? parse_boolean(value) : 1;
×
679
                if (r < 0)
×
680
                        log_warning("Failed to parse luks.crypttab= kernel command line switch %s. Ignoring.", value);
×
681
                else
682
                        arg_read_crypttab = r;
×
683

684
        } else if (streq(key, "luks.uuid")) {
47✔
685

686
                if (proc_cmdline_value_missing(key, value))
×
687
                        return 0;
688

689
                d = get_crypto_device(startswith(value, "luks-") ?: value);
×
690
                if (!d)
×
691
                        return log_oom();
×
692

693
                d->create = arg_allow_list = true;
×
694

695
        } else if (streq(key, "luks.options")) {
47✔
696
                _cleanup_free_ char *headerdev = NULL, *filtered_headerdev_options = NULL;
1✔
697

698
                if (proc_cmdline_value_missing(key, value))
1✔
699
                        return 0;
700

701
                r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
1✔
702
                if (r != 2)
1✔
703
                        return free_and_strdup_warn(&arg_default_options, value);
×
704

705
                if (warn_uuid_invalid(uuid, key))
1✔
706
                        return 0;
707

708
                d = get_crypto_device(uuid);
1✔
709
                if (!d)
1✔
710
                        return log_oom();
×
711

712
                r = filter_header_device(uuid_value, &headerdev, &filtered_headerdev_options);
1✔
713
                if (r < 0)
1✔
714
                        return r;
715

716
                free_and_replace(d->options, filtered_headerdev_options);
1✔
717
                free_and_replace(d->headerdev, headerdev);
1✔
718
        } else if (streq(key, "luks.key")) {
46✔
719
                size_t n;
1✔
720
                _cleanup_free_ char *keyfile = NULL, *keydev = NULL;
×
721
                const char *keyspec;
1✔
722

723
                if (proc_cmdline_value_missing(key, value))
1✔
724
                        return 0;
725

726
                n = strspn(value, ALPHANUMERICAL "-");
1✔
727
                if (value[n] != '=')
1✔
728
                        return free_and_strdup_warn(&arg_default_keyfile, value);
×
729

730
                uuid = strndup(value, n);
1✔
731
                if (!uuid)
1✔
732
                        return log_oom();
×
733

734
                if (warn_uuid_invalid(uuid, key))
1✔
735
                        return 0;
736

737
                d = get_crypto_device(uuid);
1✔
738
                if (!d)
1✔
739
                        return log_oom();
×
740

741
                keyspec = value + n + 1;
1✔
742
                r = split_locationspec(keyspec, &keyfile, &keydev);
1✔
743
                if (r < 0)
1✔
744
                        return r;
745

746
                free_and_replace(d->keyfile, keyfile);
1✔
747
                free_and_replace(d->keydev, keydev);
1✔
748
        } else if (streq(key, "luks.data")) {
45✔
749
                size_t n;
×
750
                _cleanup_free_ char *datadev = NULL;
×
751

752
                if (proc_cmdline_value_missing(key, value))
×
753
                        return 0;
754

755
                n = strspn(value, ALPHANUMERICAL "-");
×
756
                if (value[n] != '=') {
×
757
                        log_warning("Failed to parse luks.data= kernel command line switch. UUID is invalid, ignoring.");
×
758
                        return 0;
×
759
                }
760

761
                uuid = strndup(value, n);
×
762
                if (!uuid)
×
763
                        return log_oom();
×
764

765
                if (warn_uuid_invalid(uuid, key))
×
766
                        return 0;
767

768
                d = get_crypto_device(uuid);
×
769
                if (!d)
×
770
                        return log_oom();
×
771

772
                datadev = fstab_node_to_udev_node(value + n + 1);
×
773
                if (!datadev)
×
774
                        return log_oom();
×
775

776
                free_and_replace(d->datadev, datadev);
×
777
        } else if (streq(key, "luks.name")) {
45✔
778

UNCOV
779
                if (proc_cmdline_value_missing(key, value))
×
780
                        return 0;
781

UNCOV
782
                r = sscanf(value, "%m[0-9a-fA-F-]=%ms", &uuid, &uuid_value);
×
UNCOV
783
                if (r == 2) {
×
UNCOV
784
                        d = get_crypto_device(uuid);
×
UNCOV
785
                        if (!d)
×
786
                                return log_oom();
×
787

UNCOV
788
                        d->create = arg_allow_list = true;
×
789

UNCOV
790
                        free_and_replace(d->name, uuid_value);
×
791
                } else
792
                        log_warning("Failed to parse luks name switch %s. Ignoring.", value);
×
793
        }
794

795
        return 0;
796
}
797

798
static int add_crypttab_device(const char *name, const char *device, const char *keyspec, const char *options) {
22✔
799
        _cleanup_free_ char *keyfile = NULL, *keydev = NULL, *headerdev = NULL, *filtered_header = NULL;
22✔
800
        crypto_device *d = NULL;
22✔
801
        int r;
22✔
802

803
        const char *uuid = startswith(device, "UUID=");
22✔
804
        if (!uuid)
22✔
805
                uuid = path_startswith(device, "/dev/disk/by-uuid/");
22✔
806
        if (!uuid)
22✔
807
                uuid = startswith(name, "luks-");
22✔
808
        if (uuid)
22✔
809
                d = hashmap_get(arg_disks, uuid);
×
810

811
        if (arg_allow_list && !d) {
22✔
812
                log_info("Not creating device '%s' because it was not specified on the kernel command line.", name);
×
813
                return 0;
×
814
        }
815

816
        r = split_locationspec(keyspec, &keyfile, &keydev);
22✔
817
        if (r < 0)
22✔
818
                return r;
819

820
        if (options && (!d || !d->options)) {
22✔
821
                r = filter_header_device(options, &headerdev, &filtered_header);
22✔
822
                if (r < 0)
22✔
823
                        return r;
824
                options = filtered_header;
22✔
825
        }
826

827
        r = create_disk(name,
22✔
828
                        device,
829
                        keyfile,
830
                        keydev,
831
                        (d && d->options) ? d->headerdev : headerdev,
×
832
                        (d && d->options) ? d->options : options,
×
833
                        arg_crypttab);
834
        if (r < 0)
22✔
835
                return r;
836

837
        if (d)
22✔
838
                d->create = false;
×
839

840
        return 0;
841
}
842

843
static int add_crypttab_devices(void) {
1✔
844
        _cleanup_fclose_ FILE *f = NULL;
1✔
845
        unsigned crypttab_line = 0;
1✔
846
        int r, ret = 0;
1✔
847

848
        if (!arg_read_crypttab)
1✔
849
                return 0;
850

851
        r = fopen_unlocked(arg_crypttab, "re", &f);
1✔
852
        if (r < 0) {
1✔
853
                if (errno != ENOENT)
×
854
                        log_error_errno(errno, "Failed to open %s: %m", arg_crypttab);
×
855
                return 0;
×
856
        }
857

858
        for (;;) {
27✔
859
                _cleanup_free_ char *line = NULL, *name = NULL, *device = NULL, *keyspec = NULL, *options = NULL;
26✔
860
                int k;
27✔
861

862
                r = read_stripped_line(f, LONG_LINE_MAX, &line);
27✔
863
                if (r < 0)
27✔
864
                        return log_error_errno(r, "Failed to read %s: %m", arg_crypttab);
×
865
                if (r == 0)
27✔
866
                        break;
867

868
                crypttab_line++;
26✔
869

870
                if (IN_SET(line[0], 0, '#'))
26✔
871
                        continue;
4✔
872

873
                k = sscanf(line, "%ms %ms %ms %ms", &name, &device, &keyspec, &options);
22✔
874
                if (k < 2 || k > 4) {
22✔
875
                        log_error("Failed to parse %s:%u, ignoring.", arg_crypttab, crypttab_line);
×
876
                        continue;
×
877
                }
878

879
                RET_GATHER(ret, add_crypttab_device(name, device, keyspec, options));
22✔
880
        }
881

882
        return ret;
1✔
883
}
884

885
static int add_proc_cmdline_devices(void) {
1✔
886
        int r, ret = 0;
1✔
887
        crypto_device *d;
1✔
888

889
        HASHMAP_FOREACH(d, arg_disks) {
2✔
UNCOV
890
                _cleanup_free_ char *device = NULL;
×
891

892
                if (!d->create)
1✔
893
                        continue;
1✔
894

UNCOV
895
                if (!d->name) {
×
896
                        d->name = strjoin("luks-", d->uuid);
×
897
                        if (!d->name)
×
898
                                return log_oom();
×
899
                }
900

UNCOV
901
                device = strjoin("UUID=", d->uuid);
×
UNCOV
902
                if (!device)
×
903
                        return log_oom();
×
904

UNCOV
905
                r = create_disk(d->name,
×
UNCOV
906
                                d->datadev ?: device,
×
UNCOV
907
                                d->keyfile ?: arg_default_keyfile,
×
UNCOV
908
                                d->keydev,
×
UNCOV
909
                                d->headerdev,
×
UNCOV
910
                                d->options ?: arg_default_options,
×
911
                                "/proc/cmdline");
UNCOV
912
                RET_GATHER(ret, r);
×
913
        }
914

915
        return ret;
1✔
916
}
917

918
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(crypt_device_hash_ops, char, string_hash_func, string_compare_func,
1✔
919
                                              crypto_device, crypt_device_free);
920

921
static int run(const char *dest, const char *dest_early, const char *dest_late) {
1✔
922
        int r;
1✔
923

924
        assert_se(arg_dest = dest);
1✔
925

926
        arg_crypttab = getenv("SYSTEMD_CRYPTTAB") ?: "/etc/crypttab";
1✔
927
        arg_runtime_directory = getenv("RUNTIME_DIRECTORY") ?: "/run/systemd/cryptsetup";
1✔
928

929
        arg_disks = hashmap_new(&crypt_device_hash_ops);
1✔
930
        if (!arg_disks)
1✔
931
                return log_oom();
×
932

933
        r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
1✔
934
        if (r < 0)
1✔
935
                return log_warning_errno(r, "Failed to parse kernel command line: %m");
×
936

937
        if (!arg_enabled)
1✔
938
                return 0;
939

940
        r = add_crypttab_devices();
1✔
941
        RET_GATHER(r, add_proc_cmdline_devices());
1✔
942

943
        return r;
944
}
945

946
DEFINE_MAIN_GENERATOR_FUNCTION(run);
3✔
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