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

systemd / systemd / 14630481637

23 Apr 2025 07:04PM UTC coverage: 72.178% (-0.002%) from 72.18%
14630481637

push

github

DaanDeMeyer
mkosi: Run clangd within the tools tree instead of the build container

Running within the build sandbox has a number of disadvantages:
- We have a separate clangd cache for each distribution/release combo
- It requires to build the full image before clangd can be used
- It breaks every time the image becomes out of date and requires a
  rebuild
- We can't look at system headers as we don't have the knowledge to map
  them from inside the build sandbox to the corresponding path on the host

Instead, let's have mkosi.clangd run clangd within the tools tree. We
already require building systemd for both the host and the target anyway,
and all the dependencies to build systemd are installed in the tools tree
already for that, as well as clangd since it's installed together with the
other clang tooling we install in the tools tree. Unlike the previous approach,
this approach only requires the mkosi tools tree to be built upfront, which has
a much higher chance of not invalidating its cache. We can also trivially map
system header lookups from within the sandbox to the path within mkosi.tools
on the host so that starts working as well.

297054 of 411557 relevant lines covered (72.18%)

686269.58 hits per line

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

53.78
/src/sysupdate/sysupdate-pattern.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include "alloc-util.h"
4
#include "hexdecoct.h"
5
#include "list.h"
6
#include "log.h"
7
#include "parse-util.h"
8
#include "path-util.h"
9
#include "stdio-util.h"
10
#include "string-util.h"
11
#include "sysupdate-instance.h"
12
#include "sysupdate-pattern.h"
13

14
typedef enum PatternElementType {
15
        PATTERN_LITERAL,
16
        PATTERN_VERSION,
17
        PATTERN_PARTITION_UUID,
18
        PATTERN_PARTITION_FLAGS,
19
        PATTERN_MTIME,
20
        PATTERN_MODE,
21
        PATTERN_SIZE,
22
        PATTERN_TRIES_DONE,
23
        PATTERN_TRIES_LEFT,
24
        PATTERN_NO_AUTO,
25
        PATTERN_READ_ONLY,
26
        PATTERN_GROWFS,
27
        PATTERN_SHA256SUM,
28
        PATTERN_SLASH,
29
        _PATTERN_ELEMENT_TYPE_MAX,
30
        _PATTERN_ELEMENT_TYPE_INVALID = -EINVAL,
31
} PatternElementType;
32

33
typedef struct PatternElement PatternElement;
34

35
struct PatternElement {
36
        PatternElementType type;
37
        LIST_FIELDS(PatternElement, elements);
38
        char literal[];
39
};
40

41
static PatternElement *pattern_element_free_all(PatternElement *e) {
15,074✔
42
        LIST_CLEAR(elements, e, free);
63,164✔
43

44
        return NULL;
15,074✔
45
}
46

47
DEFINE_TRIVIAL_CLEANUP_FUNC(PatternElement*, pattern_element_free_all);
31,444✔
48

49
static PatternElementType pattern_element_type_from_char(char c) {
17,506✔
50
        switch (c) {
17,506✔
51
        case 'v':
52
                return PATTERN_VERSION;
53
        case 'u':
×
54
                return PATTERN_PARTITION_UUID;
×
55
        case 'f':
×
56
                return PATTERN_PARTITION_FLAGS;
×
57
        case 't':
×
58
                return PATTERN_MTIME;
×
59
        case 'm':
×
60
                return PATTERN_MODE;
×
61
        case 's':
×
62
                return PATTERN_SIZE;
×
63
        case 'd':
440✔
64
                return PATTERN_TRIES_DONE;
440✔
65
        case 'l':
696✔
66
                return PATTERN_TRIES_LEFT;
696✔
67
        case 'a':
×
68
                return PATTERN_NO_AUTO;
×
69
        case 'r':
×
70
                return PATTERN_READ_ONLY;
×
71
        case 'g':
×
72
                return PATTERN_GROWFS;
×
73
        case 'h':
×
74
                return PATTERN_SHA256SUM;
×
75
        default:
×
76
                return _PATTERN_ELEMENT_TYPE_INVALID;
×
77
        }
78
}
79

80
static bool valid_char(char x) {
196,272✔
81

82
        /* Let's refuse control characters here, and let's reserve some characters typically used in pattern
83
         * languages so that we can use them later, possibly. */
84

85
        if ((unsigned) x < ' ' || x >= 127)
196,272✔
86
                return false;
87

88
        return !IN_SET(x, '$', '*', '?', '[', ']', '!', '\\', '|');
196,272✔
89
}
90

91
static int pattern_split(
16,370✔
92
                const char *pattern,
93
                PatternElement **ret) {
94

95
        _cleanup_(pattern_element_free_allp) PatternElement *first = NULL;
16,370✔
96
        bool at = false, last_literal = true, last_slash = false;
16,370✔
97
        PatternElement *last = NULL;
16,370✔
98
        uint64_t mask_found = 0;
16,370✔
99
        size_t l, k = 0;
16,370✔
100

101
        assert(pattern);
16,370✔
102

103
        l = strlen(pattern);
16,370✔
104

105
        for (const char *e = pattern; *e != 0; e++) {
248,868✔
106
                if (*e == '@') {
232,498✔
107
                        if (!at) {
17,506✔
108
                                at = true;
17,506✔
109
                                continue;
17,506✔
110
                        }
111

112
                        /* Two at signs in a sequence, write out one */
113
                        at = false;
114

115
                } else if (at) {
214,992✔
116
                        PatternElementType t;
17,506✔
117
                        uint64_t bit;
17,506✔
118

119
                        t = pattern_element_type_from_char(*e);
17,506✔
120
                        if (t < 0)
17,506✔
121
                                return log_debug_errno(t, "Unknown pattern field marker '@%c'.", *e);
×
122

123
                        bit = UINT64_C(1) << t;
17,506✔
124
                        if (mask_found & bit)
17,506✔
125
                                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Pattern field marker '@%c' appears twice in pattern.", *e);
×
126

127
                        /* We insist that two pattern field markers are separated by some literal string that
128
                         * we can use to separate the fields when parsing. */
129
                        if (!last_literal && !last_slash)
17,506✔
130
                                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Found two pattern field markers without separating literal.");
×
131

132
                        if (ret) {
17,506✔
133
                                PatternElement *z;
15,934✔
134

135
                                z = malloc(offsetof(PatternElement, literal));
15,934✔
136
                                if (!z)
15,934✔
137
                                        return -ENOMEM;
138

139
                                z->type = t;
15,934✔
140
                                LIST_INSERT_AFTER(elements, first, last, z);
15,934✔
141
                                last = z;
142
                        }
143

144
                        mask_found |= bit;
17,506✔
145
                        last_slash = last_literal = at = false;
17,506✔
146
                        continue;
17,506✔
147
                }
148

149
                if (*e == '/') {
197,486✔
150
                        if (ret) {
1,214✔
151
                                PatternElement *z;
1,030✔
152

153
                                z = malloc(offsetof(PatternElement, literal));
1,030✔
154
                                if (!z)
1,030✔
155
                                        return -ENOMEM;
156

157
                                z->type = PATTERN_SLASH;
1,030✔
158
                                LIST_INSERT_AFTER(elements, first, last, z);
1,030✔
159
                                last = z;
160
                        }
161

162
                        last_literal = false;
1,214✔
163
                        last_slash = true;
1,214✔
164
                        continue ;
1,214✔
165
                }
166

167
                if (!valid_char(*e))
196,272✔
168
                        return log_debug_errno(
×
169
                                        SYNTHETIC_ERRNO(EBADRQC),
170
                                        "Invalid character 0x%0x in pattern, refusing.",
171
                                        (unsigned) *e);
172

173
                last_literal = true;
196,272✔
174
                last_slash = false;
196,272✔
175

176
                if (!ret)
196,272✔
177
                        continue;
15,236✔
178

179
                if (!last || last->type != PATTERN_LITERAL) {
181,036✔
180
                        PatternElement *z;
31,126✔
181

182
                        z = malloc0(offsetof(PatternElement, literal) + l + 1); /* l is an upper bound to all literal elements */
31,126✔
183
                        if (!z)
31,126✔
184
                                return -ENOMEM;
185

186
                        z->type = PATTERN_LITERAL;
31,126✔
187
                        k = 0;
31,126✔
188

189
                        LIST_INSERT_AFTER(elements, first, last, z);
31,126✔
190
                        last = z;
31,126✔
191
                }
192

193
                assert(last);
31,126✔
194
                assert(last->type == PATTERN_LITERAL);
181,036✔
195

196
                last->literal[k++] = *e;
181,036✔
197
        }
198

199
        if (at)
16,370✔
200
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Trailing @ character found, refusing.");
×
201
        if (!(mask_found & (UINT64_C(1) << PATTERN_VERSION)))
16,370✔
202
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Version field marker '@v' not specified in pattern, refusing.");
×
203

204
        if (ret)
16,370✔
205
                *ret = TAKE_PTR(first);
15,074✔
206

207
        return 0;
208
}
209

210
int pattern_match(const char *pattern, const char *s, InstanceMetadata *ret) {
15,000✔
211
        _cleanup_(instance_metadata_destroy) InstanceMetadata found = INSTANCE_METADATA_NULL;
15,000✔
212
        _cleanup_(pattern_element_free_allp) PatternElement *elements = NULL;
15,000✔
213
        const char *p;
15,000✔
214
        int r;
15,000✔
215

216
        assert(pattern);
15,000✔
217
        assert(s);
15,000✔
218

219
        r = pattern_split(pattern, &elements);
15,000✔
220
        if (r < 0)
15,000✔
221
                return r;
222

223
        p = s;
15,000✔
224
        LIST_FOREACH(elements, e, elements) {
27,538✔
225
                _cleanup_free_ char *t = NULL;
4,066✔
226
                const char *n;
24,312✔
227

228
                if (e->type == PATTERN_SLASH) {
24,312✔
229
                        if (*p == '/') {
672✔
230
                                ++p;
344✔
231
                                continue;
344✔
232
                        } else if (*p == '\0')
328✔
233
                                goto retry;
328✔
234
                        else
235
                                goto nope;
×
236
                }
237

238
                if (e->type == PATTERN_LITERAL) {
23,640✔
239
                        const char *k;
18,540✔
240

241
                        /* Skip literal fields */
242
                        k = startswith(p, e->literal);
18,540✔
243
                        if (!k)
18,540✔
244
                                goto nope;
10,412✔
245

246
                        p = k;
8,128✔
247
                        continue;
8,128✔
248
                }
249

250
                if (e->elements_next) {
5,100✔
251
                        /* The next element must be literal, as we use it to determine where to split */
252
                        assert(e->elements_next->type == PATTERN_LITERAL);
4,230✔
253

254
                        n = strstr(p, e->elements_next->literal);
4,230✔
255
                        if (!n)
4,230✔
256
                                goto nope;
1,034✔
257

258
                } else
259
                        /* End of the string */
260
                        assert_se(n = strchr(p, 0));
870✔
261
                t = strndup(p, n - p);
4,066✔
262
                if (!t)
4,066✔
263
                        return -ENOMEM;
264

265
                switch (e->type) {
4,066✔
266

267
                case PATTERN_VERSION:
3,726✔
268
                        if (!version_is_valid(t)) {
3,726✔
269
                                log_debug("Version string is not valid, refusing: %s", t);
×
270
                                goto nope;
×
271
                        }
272

273
                        assert(!found.version);
3,726✔
274
                        found.version = TAKE_PTR(t);
3,726✔
275
                        break;
3,726✔
276

277
                case PATTERN_PARTITION_UUID: {
×
278
                        sd_id128_t id;
×
279

280
                        if (sd_id128_from_string(t, &id) < 0)
×
281
                                goto nope;
×
282

283
                        assert(!found.partition_uuid_set);
×
284
                        found.partition_uuid = id;
×
285
                        found.partition_uuid_set = true;
×
286
                        break;
×
287
                }
288

289
                case PATTERN_PARTITION_FLAGS: {
×
290
                        uint64_t f;
×
291

292
                        if (safe_atoux64(t, &f) < 0)
×
293
                                goto nope;
×
294

295
                        if (found.partition_flags_set && found.partition_flags != f)
×
296
                                goto nope;
×
297

298
                        assert(!found.partition_flags_set);
×
299
                        found.partition_flags = f;
×
300
                        found.partition_flags_set = true;
×
301
                        break;
×
302
                }
303

304
                case PATTERN_MTIME: {
×
305
                        uint64_t v;
×
306

307
                        if (safe_atou64(t, &v) < 0)
×
308
                                goto nope;
×
309
                        if (v == USEC_INFINITY) /* Don't permit our internal special infinity value */
×
310
                                goto nope;
×
311
                        if (v / 1000000U > TIME_T_MAX) /* Make sure this fits in a timespec structure */
×
312
                                goto nope;
313

314
                        assert(found.mtime == USEC_INFINITY);
×
315
                        found.mtime = v;
×
316
                        break;
×
317
                }
318

319
                case PATTERN_MODE: {
×
320
                        mode_t m;
×
321

322
                        r = parse_mode(t, &m);
×
323
                        if (r < 0)
×
324
                                goto nope;
×
325
                        if (m & ~0775) /* Don't allow world-writable files or suid files to be generated this way */
×
326
                                goto nope;
×
327

328
                        assert(found.mode == MODE_INVALID);
×
329
                        found.mode = m;
×
330
                        break;
×
331
                }
332

333
                case PATTERN_SIZE: {
×
334
                        uint64_t u;
×
335

336
                        r = safe_atou64(t, &u);
×
337
                        if (r < 0)
×
338
                                goto nope;
×
339
                        if (u == UINT64_MAX)
×
340
                                goto nope;
×
341

342
                        assert(found.size == UINT64_MAX);
×
343
                        found.size = u;
×
344
                        break;
×
345
                }
346

347
                case PATTERN_TRIES_DONE: {
170✔
348
                        uint64_t u;
170✔
349

350
                        r = safe_atou64(t, &u);
170✔
351
                        if (r < 0)
170✔
352
                                goto nope;
×
353
                        if (u == UINT64_MAX)
170✔
354
                                goto nope;
×
355

356
                        assert(found.tries_done == UINT64_MAX);
170✔
357
                        found.tries_done = u;
170✔
358
                        break;
170✔
359
                }
360

361
                case PATTERN_TRIES_LEFT: {
170✔
362
                        uint64_t u;
170✔
363

364
                        r = safe_atou64(t, &u);
170✔
365
                        if (r < 0)
170✔
366
                                goto nope;
×
367
                        if (u == UINT64_MAX)
170✔
368
                                goto nope;
×
369

370
                        assert(found.tries_left == UINT64_MAX);
170✔
371
                        found.tries_left = u;
170✔
372
                        break;
170✔
373
                }
374

375
                case PATTERN_NO_AUTO:
×
376
                        r = parse_boolean(t);
×
377
                        if (r < 0)
×
378
                                goto nope;
×
379

380
                        assert(found.no_auto < 0);
×
381
                        found.no_auto = r;
×
382
                        break;
×
383

384
                case PATTERN_READ_ONLY:
×
385
                        r = parse_boolean(t);
×
386
                        if (r < 0)
×
387
                                goto nope;
×
388

389
                        assert(found.read_only < 0);
×
390
                        found.read_only = r;
×
391
                        break;
×
392

393
                case PATTERN_GROWFS:
×
394
                        r = parse_boolean(t);
×
395
                        if (r < 0)
×
396
                                goto nope;
×
397

398
                        assert(found.growfs < 0);
×
399
                        found.growfs = r;
×
400
                        break;
×
401

402
                case PATTERN_SHA256SUM: {
×
403
                        _cleanup_free_ void *d = NULL;
×
404
                        size_t l;
×
405

406
                        if (strlen(t) != sizeof(found.sha256sum) * 2)
×
407
                                goto nope;
×
408

409
                        r = unhexmem_full(t, sizeof(found.sha256sum) * 2, /* secure = */ false, &d, &l);
×
410
                        if (r == -ENOMEM)
×
411
                                return r;
×
412
                        if (r < 0)
×
413
                                goto nope;
×
414

415
                        assert(!found.sha256sum_set);
×
416
                        assert(l == sizeof(found.sha256sum));
×
417
                        memcpy(found.sha256sum, d, l);
×
418
                        found.sha256sum_set = true;
×
419
                        break;
×
420
                }
421

422
                default:
423
                        assert_se("unexpected pattern element");
4,066✔
424
                }
425

426
                p = n;
4,066✔
427
        }
428

429
        if (ret) {
3,226✔
430
                *ret = found;
3,226✔
431
                found = (InstanceMetadata) INSTANCE_METADATA_NULL;
3,226✔
432
        }
433

434
        return PATTERN_MATCH_YES;
435

436
nope:
11,446✔
437
        if (ret)
11,446✔
438
                *ret = (InstanceMetadata) INSTANCE_METADATA_NULL;
11,446✔
439

440
        return PATTERN_MATCH_NO;
441

442
retry:
328✔
443
        if (ret)
328✔
444
                *ret = (InstanceMetadata) INSTANCE_METADATA_NULL;
328✔
445

446
        return PATTERN_MATCH_RETRY;
447
}
448

449
int pattern_match_many(char **patterns, const char *s, InstanceMetadata *ret) {
14,672✔
450
        _cleanup_(instance_metadata_destroy) InstanceMetadata found = INSTANCE_METADATA_NULL;
14,672✔
451
        int r;
14,672✔
452

453
        STRV_FOREACH(p, patterns) {
26,118✔
454
                r = pattern_match(*p, s, &found);
15,000✔
455
                if (r < 0)
15,000✔
456
                        return r;
457
                if (r > 0) {
15,000✔
458
                        if (ret) {
3,554✔
459
                                *ret = found;
3,554✔
460
                                found = (InstanceMetadata) INSTANCE_METADATA_NULL;
3,554✔
461
                        }
462

463
                        return r;
3,554✔
464
                }
465
        }
466

467
        if (ret)
11,118✔
468
                *ret = (InstanceMetadata) INSTANCE_METADATA_NULL;
11,118✔
469

470
        return PATTERN_MATCH_NO;
471
}
472

473
int pattern_valid(const char *pattern) {
1,296✔
474
        int r;
1,296✔
475

476
        r = pattern_split(pattern, NULL);
1,296✔
477
        if (r == -EINVAL)
1,296✔
478
                return false;
479
        if (r < 0)
1,296✔
480
                return r;
×
481

482
        return true;
483
}
484

485
int pattern_format(
74✔
486
                const char *pattern,
487
                const InstanceMetadata *fields,
488
                char **ret) {
489

490
        _cleanup_(pattern_element_free_allp) PatternElement *elements = NULL;
×
491
        _cleanup_free_ char *j = NULL;
74✔
492
        int r;
74✔
493

494
        assert(pattern);
74✔
495
        assert(fields);
74✔
496
        assert(ret);
74✔
497

498
        r = pattern_split(pattern, &elements);
74✔
499
        if (r < 0)
74✔
500
                return r;
501

502
        LIST_FOREACH(elements, e, elements) {
346✔
503

504
                switch (e->type) {
272✔
505

506
                case PATTERN_SLASH:
18✔
507
                        if (!strextend(&j, "/"))
18✔
508
                                return -ENOMEM;
509

510
                        break;
511

512
                case PATTERN_LITERAL:
152✔
513
                        if (!strextend(&j, e->literal))
152✔
514
                                return -ENOMEM;
515

516
                        break;
517

518
                case PATTERN_VERSION:
74✔
519
                        if (!fields->version)
74✔
520
                                return -ENXIO;
521

522
                        if (!strextend(&j, fields->version))
74✔
523
                                return -ENOMEM;
524
                        break;
525

526
                case PATTERN_PARTITION_UUID: {
×
527
                        char formatted[SD_ID128_STRING_MAX];
×
528

529
                        if (!fields->partition_uuid_set)
×
530
                                return -ENXIO;
×
531

532
                        if (!strextend(&j, sd_id128_to_string(fields->partition_uuid, formatted)))
×
533
                                return -ENOMEM;
534

535
                        break;
×
536
                }
537

538
                case PATTERN_PARTITION_FLAGS:
×
539
                        if (!fields->partition_flags_set)
×
540
                                return -ENXIO;
541

542
                        r = strextendf(&j, "%" PRIx64, fields->partition_flags);
×
543
                        if (r < 0)
×
544
                                return r;
545

546
                        break;
547

548
                case PATTERN_MTIME:
×
549
                        if (fields->mtime == USEC_INFINITY)
×
550
                                return -ENXIO;
551

552
                        r = strextendf(&j, "%" PRIu64, fields->mtime);
×
553
                        if (r < 0)
×
554
                                return r;
555

556
                        break;
557

558
                case PATTERN_MODE:
×
559
                        if (fields->mode == MODE_INVALID)
×
560
                                return -ENXIO;
561

562
                        r = strextendf(&j, "%03o", fields->mode);
×
563
                        if (r < 0)
×
564
                                return r;
565

566
                        break;
567

568
                case PATTERN_SIZE:
×
569
                        if (fields->size == UINT64_MAX)
×
570
                                return -ENXIO;
571

572
                        r = strextendf(&j, "%" PRIu64, fields->size);
×
573
                        if (r < 0)
×
574
                                return r;
575
                        break;
576

577
                case PATTERN_TRIES_DONE:
14✔
578
                        if (fields->tries_done == UINT64_MAX)
14✔
579
                                return -ENXIO;
580

581
                        r = strextendf(&j, "%" PRIu64, fields->tries_done);
14✔
582
                        if (r < 0)
14✔
583
                                return r;
584
                        break;
585

586
                case PATTERN_TRIES_LEFT:
14✔
587
                        if (fields->tries_left == UINT64_MAX)
14✔
588
                                return -ENXIO;
589

590
                        r = strextendf(&j, "%" PRIu64, fields->tries_left);
14✔
591
                        if (r < 0)
14✔
592
                                return r;
593
                        break;
594

595
                case PATTERN_NO_AUTO:
×
596
                        if (fields->no_auto < 0)
×
597
                                return -ENXIO;
598

599
                        if (!strextend(&j, one_zero(fields->no_auto)))
×
600
                                return -ENOMEM;
601

602
                        break;
603

604
                case PATTERN_READ_ONLY:
×
605
                        if (fields->read_only < 0)
×
606
                                return -ENXIO;
607

608
                        if (!strextend(&j, one_zero(fields->read_only)))
×
609
                                return -ENOMEM;
610

611
                        break;
612

613
                case PATTERN_GROWFS:
×
614
                        if (fields->growfs < 0)
×
615
                                return -ENXIO;
616

617
                        if (!strextend(&j, one_zero(fields->growfs)))
×
618
                                return -ENOMEM;
619

620
                        break;
621

622
                case PATTERN_SHA256SUM: {
×
623
                        _cleanup_free_ char *h = NULL;
×
624

625
                        if (!fields->sha256sum_set)
×
626
                                return -ENXIO;
627

628
                        h = hexmem(fields->sha256sum, sizeof(fields->sha256sum));
×
629
                        if (!h)
×
630
                                return -ENOMEM;
631

632
                        if (!strextend(&j, h))
×
633
                                return -ENOMEM;
634

635
                        break;
×
636
                }
637

638
                default:
×
639
                        assert_not_reached();
×
640
                }
641
        }
642

643
        *ret = TAKE_PTR(j);
74✔
644
        return 0;
74✔
645
}
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