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

systemd / systemd / 14895667988

07 May 2025 08:57PM UTC coverage: 72.225% (-0.007%) from 72.232%
14895667988

push

github

yuwata
network: log_link_message_debug_errno() automatically append %m if necessary

Follow-up for d28746ef5.
Fixes CID#1609753.

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

20297 existing lines in 338 files now uncovered.

297407 of 411780 relevant lines covered (72.22%)

695716.85 hits per line

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

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

3
#include <errno.h>
4
#include <stdio.h>
5
#include <sys/stat.h>
6

7
#include "sd-device.h"
8

9
#include "alloc-util.h"
10
#include "argv-util.h"
11
#include "cryptsetup-util.h"
12
#include "extract-word.h"
13
#include "fileio.h"
14
#include "fstab-util.h"
15
#include "hexdecoct.h"
16
#include "log.h"
17
#include "main-func.h"
18
#include "parse-util.h"
19
#include "path-util.h"
20
#include "pretty-print.h"
21
#include "process-util.h"
22
#include "string-util.h"
23
#include "terminal-util.h"
24
#include "verbs.h"
25

26
static char *arg_hash = NULL; /* the hash algorithm */
27
static bool arg_superblock = true;
28
static int arg_format = 1;
29
static uint64_t arg_data_block_size = 4096;
30
static uint64_t arg_hash_block_size = 4096;
31
static uint64_t arg_data_blocks = 0;
32
static uint64_t arg_hash_offset = 0;
33
static void *arg_salt = NULL;
34
static uint64_t arg_salt_size = 32;
35
static char *arg_uuid = NULL;
36
static uint32_t arg_activate_flags = CRYPT_ACTIVATE_READONLY;
37
static char *arg_fec_what = NULL;
38
static uint64_t arg_fec_offset = 0;
39
static uint64_t arg_fec_roots = 2;
40
static void *arg_root_hash_signature = NULL;
41
static size_t arg_root_hash_signature_size = 0;
42
static bool arg_root_hash_signature_auto = false;
43

44
STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
×
45
STATIC_DESTRUCTOR_REGISTER(arg_salt, freep);
×
46
STATIC_DESTRUCTOR_REGISTER(arg_uuid, freep);
×
47
STATIC_DESTRUCTOR_REGISTER(arg_fec_what, freep);
×
UNCOV
48
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_signature, freep);
×
49

50
static int help(void) {
×
51
        _cleanup_free_ char *link = NULL;
×
UNCOV
52
        int r;
×
53

54
        r = terminal_urlify_man("systemd-veritysetup@.service", "8", &link);
×
55
        if (r < 0)
×
UNCOV
56
                return log_oom();
×
57

UNCOV
58
        printf("%s attach VOLUME DATADEVICE HASHDEVICE ROOTHASH [OPTIONS]\n"
×
59
               "%s detach VOLUME\n\n"
60
               "Attach or detach a verity protected block device.\n"
61
               "\nSee the %s for details.\n",
62
               program_invocation_short_name,
63
               program_invocation_short_name,
64
               link);
65

66
        return 0;
67
}
68

69
static int parse_roothashsig_option(const char *option, bool strict) {
×
70
        _cleanup_free_ void *rhs = NULL;
×
71
        size_t rhss = 0;
×
72
        bool set_auto = false;
×
UNCOV
73
        int r;
×
74

UNCOV
75
        assert(option);
×
76

77
        const char *value = startswith(option, "base64:");
×
78
        if (value) {
×
79
                r = unbase64mem(value, &rhs, &rhss);
×
80
                if (r < 0)
×
UNCOV
81
                        return log_error_errno(r, "Failed to parse root hash signature '%s': %m", option);
×
82

83
        } else if (path_is_absolute(option)) {
×
UNCOV
84
                r = read_full_file_full(
×
85
                                AT_FDCWD,
86
                                option,
87
                                /* offset= */ UINT64_MAX,
88
                                /* size= */ SIZE_MAX,
89
                                READ_FULL_FILE_CONNECT_SOCKET,
90
                                /* bind_name= */ NULL,
91
                                (char**) &rhs,
92
                                &rhss);
93
                if (r < 0)
×
UNCOV
94
                        return log_error_errno(r, "Failed to read root hash signature: %m");
×
95

UNCOV
96
        } else if (streq(option, "auto"))
×
97
                /* auto → Derive signature from udev property ID_DISSECT_PART_ROOTHASH_SIG */
98
                set_auto = true;
99
        else if (strict)
×
UNCOV
100
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
101
                                       "root-hash-signature= expects either full path to signature file or "
102
                                       "base64 string encoding signature prefixed by base64:.");
103
        else
104
                return false;
105

UNCOV
106
        if (!HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY)
×
107
                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
108
                                       "Activation of verity device with signature requested, but cryptsetup does not support crypt_activate_by_signed_key().");
109

110
        free_and_replace(arg_root_hash_signature, rhs);
×
111
        arg_root_hash_signature_size = rhss;
×
UNCOV
112
        arg_root_hash_signature_auto = set_auto;
×
113

UNCOV
114
        return true;
×
115
}
116

117
static int parse_block_size(const char *t, uint64_t *size) {
×
118
        uint64_t u;
×
UNCOV
119
        int r;
×
120

121
        r = parse_size(t, 1024, &u);
×
122
        if (r < 0)
×
UNCOV
123
                return r;
×
124

UNCOV
125
        if (u < 512 || u > (512 * 1024))
×
126
                return -ERANGE;
127

UNCOV
128
        if ((u % 512) != 0 || !ISPOWEROF2(u))
×
129
                return -EINVAL;
130

UNCOV
131
        *size = u;
×
132

UNCOV
133
        return 0;
×
134
}
135

136
static int parse_options(const char *options) {
×
UNCOV
137
        int r;
×
138

139
        /* backward compatibility with the obsolete ROOTHASHSIG positional argument */
140
        r = parse_roothashsig_option(options, /* strict= */ false);
×
UNCOV
141
        if (r < 0)
×
142
                return r;
143
        if (r > 0) {
×
UNCOV
144
                log_warning("Usage of ROOTHASHSIG positional argument is deprecated. "
×
145
                            "Please use the option root-hash-signature=%s instead.", options);
UNCOV
146
                return 0;
×
147
        }
148

149
        for (;;) {
×
150
                _cleanup_free_ char *word = NULL;
×
UNCOV
151
                char *val;
×
152

153
                r = extract_first_word(&options, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
×
154
                if (r < 0)
×
155
                        return log_error_errno(r, "Failed to parse options: %m");
×
UNCOV
156
                if (r == 0)
×
157
                        break;
158

159
                if (STR_IN_SET(word, "noauto", "auto", "nofail", "fail", "_netdev"))
×
UNCOV
160
                        continue;
×
161

162
                if (isempty(word))
×
163
                        continue;
×
164
                else if (streq(word, "ignore-corruption"))
×
165
                        arg_activate_flags |= CRYPT_ACTIVATE_IGNORE_CORRUPTION;
×
166
                else if (streq(word, "restart-on-corruption"))
×
167
                        arg_activate_flags |= CRYPT_ACTIVATE_RESTART_ON_CORRUPTION;
×
168
                else if (streq(word, "ignore-zero-blocks"))
×
UNCOV
169
                        arg_activate_flags |= CRYPT_ACTIVATE_IGNORE_ZERO_BLOCKS;
×
170
#ifdef CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE
171
                else if (streq(word, "check-at-most-once"))
×
UNCOV
172
                        arg_activate_flags |= CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE;
×
173
#endif
174
#ifdef CRYPT_ACTIVATE_PANIC_ON_CORRUPTION
175
                else if (streq(word, "panic-on-corruption"))
×
UNCOV
176
                        arg_activate_flags |= CRYPT_ACTIVATE_PANIC_ON_CORRUPTION;
×
177
#endif
UNCOV
178
                else if ((val = startswith(word, "superblock="))) {
×
179

180
                        r = parse_boolean(val);
×
181
                        if (r < 0)
×
UNCOV
182
                                return log_error_errno(r, "Failed to parse boolean '%s': %m", word);
×
183

184
                        arg_superblock = r;
×
UNCOV
185
                } else if ((val = startswith(word, "format="))) {
×
186

187
                        if (!STR_IN_SET(val, "0", "1"))
×
UNCOV
188
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "format= expects either 0 (original Chrome OS version) or "
×
189
                                                                                "1 (modern version).");
190

191
                        arg_format = val[0] - '0';
×
192
                } else if ((val = startswith(word, "data-block-size="))) {
×
UNCOV
193
                        uint64_t sz;
×
194

195
                        r = parse_block_size(val, &sz);
×
196
                        if (r < 0)
×
UNCOV
197
                                return log_error_errno(r, "Failed to parse size '%s': %m", word);
×
198

199
                        arg_data_block_size = sz;
×
200
                } else if ((val = startswith(word, "hash-block-size="))) {
×
UNCOV
201
                        uint64_t sz;
×
202

203
                        r = parse_block_size(val, &sz);
×
204
                        if (r < 0)
×
UNCOV
205
                                return log_error_errno(r, "Failed to parse size '%s': %m", word);
×
206

207
                        arg_hash_block_size = sz;
×
208
                } else if ((val = startswith(word, "data-blocks="))) {
×
UNCOV
209
                        uint64_t u;
×
210

211
                        r = safe_atou64(val, &u);
×
212
                        if (r < 0)
×
UNCOV
213
                                return log_error_errno(r, "Failed to parse number '%s': %m", word);
×
214

215
                        arg_data_blocks = u;
×
216
                } else if ((val = startswith(word, "hash-offset="))) {
×
UNCOV
217
                        uint64_t off;
×
218

219
                        r = parse_size(val, 1024, &off);
×
220
                        if (r < 0)
×
221
                                return log_error_errno(r, "Failed to parse offset '%s': %m", word);
×
222
                        if (off % 512 != 0)
×
UNCOV
223
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "hash-offset= expects a 512-byte aligned value.");
×
224

225
                        arg_hash_offset = off;
×
UNCOV
226
                } else if ((val = startswith(word, "salt="))) {
×
227

228
                        if (!string_is_safe(val))
×
UNCOV
229
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "salt= is not valid.");
×
230

231
                        if (isempty(val)) {
×
232
                                arg_salt = mfree(arg_salt);
×
233
                                arg_salt_size = 32;
×
234
                        } else if (streq(val, "-")) {
×
235
                                arg_salt = mfree(arg_salt);
×
UNCOV
236
                                arg_salt_size = 0;
×
237
                        } else {
238
                                size_t l;
×
UNCOV
239
                                void *m;
×
240

241
                                r = unhexmem(val, &m, &l);
×
242
                                if (r < 0)
×
UNCOV
243
                                        return log_error_errno(r, "Failed to parse salt '%s': %m", word);
×
244

245
                                free_and_replace(arg_salt, m);
×
UNCOV
246
                                arg_salt_size = l;
×
247
                        }
UNCOV
248
                } else if ((val = startswith(word, "uuid="))) {
×
249

250
                        r = sd_id128_from_string(val, NULL);
×
251
                        if (r < 0)
×
UNCOV
252
                                return log_error_errno(r, "Failed to parse UUID '%s': %m", word);
×
253

254
                        r = free_and_strdup(&arg_uuid, val);
×
255
                        if (r < 0)
×
256
                                return log_oom();
×
UNCOV
257
                } else if ((val = startswith(word, "hash="))) {
×
258

259
                        r = free_and_strdup(&arg_hash, val);
×
260
                        if (r < 0)
×
261
                                return log_oom();
×
262
                } else if ((val = startswith(word, "fec-device="))) {
×
UNCOV
263
                        _cleanup_free_ char *what = NULL;
×
264

265
                        what = fstab_node_to_udev_node(val);
×
266
                        if (!what)
×
UNCOV
267
                                return log_oom();
×
268

269
                        if (!path_is_absolute(what))
×
UNCOV
270
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "fec-device= expects an absolute path.");
×
271

272
                        if (!path_is_normalized(what))
×
UNCOV
273
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "fec-device= expects an normalized path.");
×
274

275
                        r = free_and_strdup(&arg_fec_what, what);
×
276
                        if (r < 0)
×
277
                                return log_oom();
×
278
                } else if ((val = startswith(word, "fec-offset="))) {
×
UNCOV
279
                        uint64_t off;
×
280

281
                        r = parse_size(val, 1024, &off);
×
282
                        if (r < 0)
×
283
                                return log_error_errno(r, "Failed to parse offset '%s': %m", word);
×
284
                        if (off % 512 != 0)
×
UNCOV
285
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "fec-offset= expects a 512-byte aligned value.");
×
286

287
                        arg_fec_offset = off;
×
288
                } else if ((val = startswith(word, "fec-roots="))) {
×
UNCOV
289
                        uint64_t u;
×
290

291
                        r = safe_atou64(val, &u);
×
292
                        if (r < 0)
×
293
                                return log_error_errno(r, "Failed to parse number '%s', ignoring: %m", word);
×
294
                        if (u < 2 || u > 24)
×
UNCOV
295
                                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "fec-rootfs= expects a value between 2 and 24 (including).");
×
296

297
                        arg_fec_roots = u;
×
298
                } else if ((val = startswith(word, "root-hash-signature="))) {
×
299
                        r = parse_roothashsig_option(val, /* strict= */ true);
×
UNCOV
300
                        if (r < 0)
×
301
                                return r;
302

303
                } else
UNCOV
304
                        log_warning("Encountered unknown option '%s', ignoring.", word);
×
305
        }
306

UNCOV
307
        return r;
×
308
}
309

310
static int verb_attach(int argc, char *argv[], void *userdata) {
×
311
        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
×
312
        _cleanup_free_ void *rh = NULL;
×
313
        struct crypt_params_verity p = {};
×
314
        crypt_status_info status;
×
315
        size_t rh_size = 0;
×
UNCOV
316
        int r;
×
317

UNCOV
318
        assert(argc >= 5);
×
319

320
        const char *volume = argv[1],
×
321
                *data_device = argv[2],
×
322
                *verity_device = argv[3],
×
323
                *root_hash = argv[4],
×
UNCOV
324
                *options = mangle_none(argc > 5 ? argv[5] : NULL);
×
325

326
        if (!filename_is_valid(volume))
×
UNCOV
327
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", volume);
×
328

329
        if (options) {
×
330
                r = parse_options(options);
×
331
                if (r < 0)
×
UNCOV
332
                        return log_error_errno(r, "Failed to parse options: %m");
×
333
        }
334

335
        if (empty_or_dash(root_hash) || streq_ptr(root_hash, "auto"))
×
UNCOV
336
                root_hash = NULL;
×
337

338
        _cleanup_(sd_device_unrefp) sd_device *datadev = NULL;
×
339
        if (!root_hash || arg_root_hash_signature_auto) {
×
340
                r = sd_device_new_from_path(&datadev, data_device);
×
341
                if (r < 0)
×
UNCOV
342
                        return log_error_errno(r, "Failed to acquire udev object for data device '%s': %m", data_device);
×
343
        }
344

UNCOV
345
        if (!root_hash) {
×
346
                /* If no literal root hash is specified try to determine it automatically from the
347
                 * ID_DISSECT_PART_ROOTHASH udev property. */
348
                r = sd_device_get_property_value(ASSERT_PTR(datadev), "ID_DISSECT_PART_ROOTHASH", &root_hash);
×
349
                if (r < 0)
×
UNCOV
350
                        return log_error_errno(r, "No root hash specified, and device doesn't carry ID_DISSECT_PART_ROOTHASH property, cannot determine root hash.");
×
351
        }
352

353
        r = unhexmem(root_hash, &rh, &rh_size);
×
354
        if (r < 0)
×
UNCOV
355
                return log_error_errno(r, "Failed to parse root hash: %m");
×
356

357
        if (arg_root_hash_signature_auto) {
×
358
                assert(!arg_root_hash_signature);
×
UNCOV
359
                assert(arg_root_hash_signature_size == 0);
×
360

361
                const char *t;
×
362
                r = sd_device_get_property_value(ASSERT_PTR(datadev), "ID_DISSECT_PART_ROOTHASH_SIG", &t);
×
363
                if (r < 0)
×
UNCOV
364
                        return log_error_errno(r, "Automatic root hash signature pick up requested, and device doesn't carry ID_DISSECT_PART_ROOTHASH_SIG property, cannot determine root hash signature.");
×
365

366
                r = unbase64mem(t, &arg_root_hash_signature, &arg_root_hash_signature_size);
×
367
                if (r < 0)
×
UNCOV
368
                        return log_error_errno(r, "Failed to decode root hash signature data from udev data device: %m");
×
369
        }
370

371
        r = crypt_init(&cd, verity_device);
×
372
        if (r < 0)
×
UNCOV
373
                return log_error_errno(r, "Failed to open verity device %s: %m", verity_device);
×
374

UNCOV
375
        cryptsetup_enable_logging(cd);
×
376

377
        status = crypt_status(cd, volume);
×
378
        if (IN_SET(status, CRYPT_ACTIVE, CRYPT_BUSY)) {
×
379
                log_info("Volume %s already active.", volume);
×
UNCOV
380
                return 0;
×
381
        }
382

383
        if (arg_superblock) {
×
UNCOV
384
                p = (struct crypt_params_verity) {
×
385
                        .fec_device = arg_fec_what,
386
                        .hash_area_offset = arg_hash_offset,
387
                        .fec_area_offset = arg_fec_offset,
388
                        .fec_roots = arg_fec_roots,
389
                };
390

391
                r = crypt_load(cd, CRYPT_VERITY, &p);
×
392
                if (r < 0)
×
UNCOV
393
                        return log_error_errno(r, "Failed to load verity superblock: %m");
×
394
        } else {
UNCOV
395
                p = (struct crypt_params_verity) {
×
396
                        .hash_name = arg_hash,
397
                        .data_device = data_device,
398
                        .fec_device = arg_fec_what,
399
                        .salt = arg_salt,
400
                        .salt_size = arg_salt_size,
401
                        .hash_type = arg_format,
402
                        .data_block_size = arg_data_block_size,
403
                        .hash_block_size = arg_hash_block_size,
404
                        .data_size = arg_data_blocks,
405
                        .hash_area_offset = arg_hash_offset,
406
                        .fec_area_offset = arg_fec_offset,
407
                        .fec_roots = arg_fec_roots,
408
                        .flags = CRYPT_VERITY_NO_HEADER,
409
                };
410

411
                r = crypt_format(cd, CRYPT_VERITY, NULL, NULL, arg_uuid, NULL, 0, &p);
×
412
                if (r < 0)
×
UNCOV
413
                        return log_error_errno(r, "Failed to format verity superblock: %m");
×
414
        }
415

416
        r = crypt_set_data_device(cd, data_device);
×
417
        if (r < 0)
×
UNCOV
418
                return log_error_errno(r, "Failed to configure data device: %m");
×
419

UNCOV
420
        if (arg_root_hash_signature_size > 0) {
×
421
#if HAVE_CRYPT_ACTIVATE_BY_SIGNED_KEY
422
                r = crypt_activate_by_signed_key(cd, volume, rh, rh_size, arg_root_hash_signature, arg_root_hash_signature_size, arg_activate_flags);
×
423
                if (r < 0) {
×
UNCOV
424
                        log_info_errno(r, "Unable to activate verity device '%s' with root hash signature (%m), retrying without.", volume);
×
425

426
                        r = crypt_activate_by_volume_key(cd, volume, rh, rh_size, arg_activate_flags);
×
427
                        if (r < 0)
×
UNCOV
428
                                return log_error_errno(r, "Failed to activate verity device '%s' both with and without root hash signature: %m", volume);
×
429

UNCOV
430
                        log_info("Activation of verity device '%s' succeeded without root hash signature.", volume);
×
431
                }
432
#else
433
                assert_not_reached();
434
#endif
435
        } else
436
                r = crypt_activate_by_volume_key(cd, volume, rh, rh_size, arg_activate_flags);
×
437
        if (r < 0)
×
UNCOV
438
                return log_error_errno(r, "Failed to set up verity device '%s': %m", volume);
×
439

440
        return 0;
441
}
442

443
static int verb_detach(int argc, char *argv[], void *userdata) {
×
444
        _cleanup_(crypt_freep) struct crypt_device *cd = NULL;
×
UNCOV
445
        int r;
×
446

UNCOV
447
        assert(argc == 2);
×
448

UNCOV
449
        const char *volume = argv[1];
×
450

451
        if (!filename_is_valid(volume))
×
UNCOV
452
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Volume name '%s' is not valid.", volume);
×
453

454
        r = crypt_init_by_name(&cd, volume);
×
455
        if (r == -ENODEV) {
×
456
                log_info("Volume %s 'already' inactive.", volume);
×
UNCOV
457
                return 0;
×
458
        }
459
        if (r < 0)
×
UNCOV
460
                return log_error_errno(r, "crypt_init_by_name() for volume '%s' failed: %m", volume);
×
461

UNCOV
462
        cryptsetup_enable_logging(cd);
×
463

464
        r = crypt_deactivate(cd, volume);
×
465
        if (r < 0)
×
UNCOV
466
                return log_error_errno(r, "Failed to deactivate volume '%s': %m", volume);
×
467

468
        return 0;
469
}
470

471
static int run(int argc, char *argv[]) {
×
472
        if (argv_looks_like_help(argc, argv))
×
UNCOV
473
                return help();
×
474

UNCOV
475
        log_setup();
×
476

UNCOV
477
        cryptsetup_enable_logging(NULL);
×
478

UNCOV
479
        umask(0022);
×
480

UNCOV
481
        static const Verb verbs[] = {
×
482
                { "attach", 5, 6, 0, verb_attach },
483
                { "detach", 2, 2, 0, verb_detach },
484
                {}
485
        };
486

UNCOV
487
        return dispatch_verb(argc, argv, verbs, NULL);
×
488
}
489

UNCOV
490
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