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

systemd / systemd / 14505075066

16 Apr 2025 06:24PM UTC coverage: 72.117% (+0.005%) from 72.112%
14505075066

push

github

yuwata
resolve: query the parent zone for DS records

RFC 4035 Section 4.2 requires that missing DS records are queried for in
the parent zone rather than the child zone, the old behaviour could
cause subdomains under home.arpa (RFC 8375) to fail validation.

This commit assumes that QDCOUNT = 1 as per RFC 9619

Fixes https://github.com/systemd/systemd/issues/19496

8 of 8 new or added lines in 2 files covered. (100.0%)

680 existing lines in 33 files now uncovered.

297042 of 411889 relevant lines covered (72.12%)

688478.02 hits per line

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

85.39
/src/basic/conf-files.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <errno.h>
4
#include <stdarg.h>
5
#include <stdio.h>
6
#include <stdlib.h>
7

8
#include "chase.h"
9
#include "conf-files.h"
10
#include "constants.h"
11
#include "dirent-util.h"
12
#include "errno-util.h"
13
#include "fd-util.h"
14
#include "fileio.h"
15
#include "glyph-util.h"
16
#include "hashmap.h"
17
#include "log.h"
18
#include "macro.h"
19
#include "nulstr-util.h"
20
#include "path-util.h"
21
#include "set.h"
22
#include "stat-util.h"
23
#include "string-util.h"
24
#include "strv.h"
25
#include "terminal-util.h"
26

27
static int files_add(
38,633✔
28
                DIR *dir,
29
                const char *dirpath,
30
                Hashmap **files,
31
                Set **masked,
32
                const char *suffix,
33
                unsigned flags) {
34

35
        int r;
38,633✔
36

37
        assert(dir);
38,633✔
38
        assert(dirpath);
38,633✔
39
        assert(files);
38,633✔
40
        assert(masked);
38,633✔
41

42
        FOREACH_DIRENT(de, dir, return -errno) {
317,153✔
43
                _cleanup_free_ char *n = NULL, *p = NULL;
201,250✔
44
                struct stat st;
201,250✔
45

46
                /* Does this match the suffix? */
47
                if (suffix && !endswith(de->d_name, suffix))
201,250✔
48
                        continue;
100,354✔
49

50
                /* Has this file already been found in an earlier directory? */
51
                if (hashmap_contains(*files, de->d_name)) {
100,896✔
52
                        log_debug("Skipping overridden file '%s/%s'.", dirpath, de->d_name);
124✔
53
                        continue;
124✔
54
                }
55

56
                /* Has this been masked in an earlier directory? */
57
                if ((flags & CONF_FILES_FILTER_MASKED) && set_contains(*masked, de->d_name)) {
100,772✔
58
                        log_debug("File '%s/%s' is masked by previous entry.", dirpath, de->d_name);
15✔
59
                        continue;
15✔
60
                }
61

62
                /* Read file metadata if we shall validate the check for file masks, for node types or whether the node is marked executable. */
63
                if (flags & (CONF_FILES_FILTER_MASKED|CONF_FILES_REGULAR|CONF_FILES_DIRECTORY|CONF_FILES_EXECUTABLE))
100,757✔
64
                        if (fstatat(dirfd(dir), de->d_name, &st, 0) < 0) {
2,848✔
UNCOV
65
                                log_debug_errno(errno, "Failed to stat '%s/%s', ignoring: %m", dirpath, de->d_name);
×
66
                                continue;
×
67
                        }
68

69
                /* Is this a masking entry? */
70
                if ((flags & CONF_FILES_FILTER_MASKED))
2,848✔
71
                        if (null_or_empty(&st)) {
2,587✔
72
                                /* Mark this one as masked */
73
                                r = set_put_strdup(masked, de->d_name);
25✔
74
                                if (r < 0)
25✔
75
                                        return r;
76

77
                                log_debug("File '%s/%s' is a mask.", dirpath, de->d_name);
25✔
78
                                continue;
25✔
79
                        }
80

81
                /* Does this node have the right type? */
82
                if (flags & (CONF_FILES_REGULAR|CONF_FILES_DIRECTORY))
100,732✔
83
                        if (!((flags & CONF_FILES_DIRECTORY) && S_ISDIR(st.st_mode)) &&
2,746✔
84
                            !((flags & CONF_FILES_REGULAR) && S_ISREG(st.st_mode))) {
2,595✔
UNCOV
85
                                log_debug("Ignoring '%s/%s', as it does not have the right type.", dirpath, de->d_name);
×
86
                                continue;
×
87
                        }
88

89
                /* Does this node have the executable bit set? */
90
                if (flags & CONF_FILES_EXECUTABLE)
100,732✔
91
                        /* As requested: check if the file is marked executable. Note that we don't check access(X_OK)
92
                         * here, as we care about whether the file is marked executable at all, and not whether it is
93
                         * executable for us, because if so, such errors are stuff we should log about. */
94

95
                        if ((st.st_mode & 0111) == 0) { /* not executable */
890✔
UNCOV
96
                                log_debug("Ignoring '%s/%s', as it is not marked executable.", dirpath, de->d_name);
×
97
                                continue;
×
98
                        }
99

100
                n = strdup(de->d_name);
100,732✔
101
                if (!n)
100,732✔
102
                        return -ENOMEM;
103

104
                if ((flags & CONF_FILES_BASENAME))
100,732✔
105
                        r = hashmap_ensure_put(files, &string_hash_ops_free, n, n);
12✔
106
                else {
107
                        p = path_join(dirpath, de->d_name);
100,720✔
108
                        if (!p)
100,720✔
109
                                return -ENOMEM;
110

111
                        r = hashmap_ensure_put(files, &string_hash_ops_free_free, n, p);
100,720✔
112
                }
113
                if (r < 0)
100,732✔
114
                        return r;
115
                assert(r > 0);
100,732✔
116

117
                TAKE_PTR(n);
100,732✔
118
                TAKE_PTR(p);
100,732✔
119
        }
120

121
        return 0;
122
}
123

124
static int copy_and_sort_files_from_hashmap(Hashmap *fh, char ***ret) {
104,652✔
125
        _cleanup_free_ char **sv = NULL;
104,652✔
126
        char **files;
104,652✔
127
        int r;
104,652✔
128

129
        assert(ret);
104,652✔
130

131
        r = hashmap_dump_sorted(fh, (void***) &sv, /* ret_n = */ NULL);
104,652✔
132
        if (r < 0)
104,652✔
133
                return r;
134

135
        /* The entries in the array given by hashmap_dump_sorted() are still owned by the hashmap. */
136
        files = strv_copy(sv);
104,652✔
137
        if (!files)
104,652✔
138
                return -ENOMEM;
139

140
        *ret = files;
104,652✔
141
        return 0;
104,652✔
142
}
143

144
int conf_files_list_strv(
104,644✔
145
                char ***ret,
146
                const char *suffix,
147
                const char *root,
148
                unsigned flags,
149
                const char * const *dirs) {
150

151
        _cleanup_hashmap_free_ Hashmap *fh = NULL;
104,644✔
152
        _cleanup_set_free_ Set *masked = NULL;
104,644✔
153
        int r;
104,644✔
154

155
        assert(ret);
104,644✔
156

157
        STRV_FOREACH(p, dirs) {
718,316✔
158
                _cleanup_closedir_ DIR *dir = NULL;
613,672✔
159
                _cleanup_free_ char *path = NULL;
613,672✔
160

161
                r = chase_and_opendir(*p, root, CHASE_PREFIX_ROOT, &path, &dir);
613,672✔
162
                if (r < 0) {
613,672✔
163
                        if (r != -ENOENT)
575,051✔
UNCOV
164
                                log_debug_errno(r, "Failed to chase and open directory '%s', ignoring: %m", *p);
×
165
                        continue;
575,051✔
166
                }
167

168
                r = files_add(dir, path, &fh, &masked, suffix, flags);
38,621✔
169
                if (r == -ENOMEM)
38,621✔
UNCOV
170
                        return r;
×
171
                if (r < 0)
38,621✔
172
                        log_debug_errno(r, "Failed to search for files in '%s', ignoring: %m", path);
38,621✔
173
        }
174

175
        return copy_and_sort_files_from_hashmap(fh, ret);
104,644✔
176
}
177

178
int conf_files_list_strv_at(
8✔
179
                char ***ret,
180
                const char *suffix,
181
                int rfd,
182
                unsigned flags,
183
                const char * const *dirs) {
184

185
        _cleanup_hashmap_free_ Hashmap *fh = NULL;
8✔
186
        _cleanup_set_free_ Set *masked = NULL;
8✔
187
        int r;
8✔
188

189
        assert(rfd >= 0 || rfd == AT_FDCWD);
8✔
190
        assert(ret);
8✔
191

192
        STRV_FOREACH(p, dirs) {
20✔
193
                _cleanup_closedir_ DIR *dir = NULL;
12✔
194
                _cleanup_free_ char *path = NULL;
12✔
195

196
                r = chase_and_opendirat(rfd, *p, CHASE_AT_RESOLVE_IN_ROOT, &path, &dir);
12✔
197
                if (r < 0) {
12✔
UNCOV
198
                        if (r != -ENOENT)
×
UNCOV
199
                                log_debug_errno(r, "Failed to chase and open directory '%s', ignoring: %m", *p);
×
UNCOV
200
                        continue;
×
201
                }
202

203
                r = files_add(dir, path, &fh, &masked, suffix, flags);
12✔
204
                if (r == -ENOMEM)
12✔
UNCOV
205
                        return r;
×
206
                if (r < 0)
12✔
207
                        log_debug_errno(r, "Failed to search for files in '%s', ignoring: %m", path);
12✔
208
        }
209

210
        return copy_and_sort_files_from_hashmap(fh, ret);
8✔
211
}
212

213
int conf_files_insert(char ***strv, const char *root, char **dirs, const char *path) {
74✔
214
        /* Insert a path into strv, at the place honouring the usual sorting rules:
215
         * - we first compare by the basename
216
         * - and then we compare by dirname, allowing just one file with the given
217
         *   basename.
218
         * This means that we will
219
         * - add a new entry if basename(path) was not on the list,
220
         * - do nothing if an entry with higher priority was already present,
221
         * - do nothing if our new entry matches the existing entry,
222
         * - replace the existing entry if our new entry has higher priority.
223
         */
224
        size_t i, n;
74✔
225
        char *t;
74✔
226
        int r;
74✔
227

228
        n = strv_length(*strv);
74✔
229
        for (i = 0; i < n; i++) {
107✔
230
                int c;
101✔
231

232
                c = path_compare_filename((*strv)[i], path);
101✔
233
                if (c == 0)
101✔
234
                        /* Oh, there already is an entry with a matching name (the last component). */
235
                        STRV_FOREACH(dir, dirs) {
92✔
236
                                _cleanup_free_ char *rdir = NULL;
92✔
237
                                char *p1, *p2;
92✔
238

239
                                rdir = path_join(root, *dir);
92✔
240
                                if (!rdir)
92✔
241
                                        return -ENOMEM;
242

243
                                p1 = path_startswith((*strv)[i], rdir);
92✔
244
                                if (p1)
92✔
245
                                        /* Existing entry with higher priority
246
                                         * or same priority, no need to do anything. */
247
                                        return 0;
248

249
                                p2 = path_startswith(path, *dir);
33✔
250
                                if (p2) {
33✔
251
                                        /* Our new entry has higher priority */
252

253
                                        t = path_join(root, path);
3✔
254
                                        if (!t)
3✔
UNCOV
255
                                                return log_oom();
×
256

257
                                        return free_and_replace((*strv)[i], t);
3✔
258
                                }
259
                        }
260

261
                else if (c > 0)
39✔
262
                        /* Following files have lower priority, let's go insert our
263
                         * new entry. */
264
                        break;
265

266
                /* … we are not there yet, let's continue */
267
        }
268

269
        /* The new file has lower priority than all the existing entries */
270
        t = path_join(root, path);
12✔
271
        if (!t)
12✔
272
                return -ENOMEM;
273

274
        r = strv_insert(strv, i, t);
12✔
275
        if (r < 0)
12✔
UNCOV
276
                free(t);
×
277

278
        return r;
279
}
280

281
int conf_files_list(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dir) {
4✔
282
        return conf_files_list_strv(ret, suffix, root, flags, STRV_MAKE_CONST(dir));
4✔
283
}
284

285
int conf_files_list_at(char ***ret, const char *suffix, int rfd, unsigned flags, const char *dir) {
4✔
286
        return conf_files_list_strv_at(ret, suffix, rfd, flags, STRV_MAKE_CONST(dir));
4✔
287
}
288

289
int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dirs) {
14,852✔
290
        _cleanup_strv_free_ char **d = NULL;
14,852✔
291

292
        assert(ret);
14,852✔
293

294
        d = strv_split_nulstr(dirs);
14,852✔
295
        if (!d)
14,852✔
296
                return -ENOMEM;
297

298
        return conf_files_list_strv(ret, suffix, root, flags, (const char**) d);
14,852✔
299
}
300

UNCOV
301
int conf_files_list_nulstr_at(char ***ret, const char *suffix, int rfd, unsigned flags, const char *dirs) {
×
UNCOV
302
        _cleanup_strv_free_ char **d = NULL;
×
303

UNCOV
304
        assert(ret);
×
305

UNCOV
306
        d = strv_split_nulstr(dirs);
×
UNCOV
307
        if (!d)
×
308
                return -ENOMEM;
309

310
        return conf_files_list_strv_at(ret, suffix, rfd, flags, (const char**) d);
×
311
}
312

313
int conf_files_list_with_replacement(
631✔
314
                const char *root,
315
                char **config_dirs,
316
                const char *replacement,
317
                char ***ret_files,
318
                char **ret_replace_file) {
319

320
        _cleanup_strv_free_ char **f = NULL;
1,262✔
321
        _cleanup_free_ char *p = NULL;
631✔
322
        int r;
631✔
323

324
        assert(config_dirs);
631✔
325
        assert(ret_files);
631✔
326
        assert(ret_replace_file || !replacement);
631✔
327

328
        r = conf_files_list_strv(&f, ".conf", root, 0, (const char* const*) config_dirs);
631✔
329
        if (r < 0)
631✔
UNCOV
330
                return log_error_errno(r, "Failed to enumerate config files: %m");
×
331

332
        if (replacement) {
631✔
333
                r = conf_files_insert(&f, root, config_dirs, replacement);
35✔
334
                if (r < 0)
35✔
UNCOV
335
                        return log_error_errno(r, "Failed to extend config file list: %m");
×
336

337
                p = path_join(root, replacement);
35✔
338
                if (!p)
35✔
UNCOV
339
                        return log_oom();
×
340
        }
341

342
        *ret_files = TAKE_PTR(f);
631✔
343
        if (ret_replace_file)
631✔
344
                *ret_replace_file = TAKE_PTR(p);
631✔
345

346
        return 0;
347
}
348

349
int conf_files_list_dropins(
46,782✔
350
                char ***ret,
351
                const char *dropin_dirname,
352
                const char *root,
353
                const char * const *dirs) {
354

355
        _cleanup_strv_free_ char **dropin_dirs = NULL;
46,782✔
356
        const char *suffix;
46,782✔
357
        int r;
46,782✔
358

359
        assert(ret);
46,782✔
360
        assert(dropin_dirname);
46,782✔
361
        assert(dirs);
46,782✔
362

363
        suffix = strjoina("/", dropin_dirname);
233,910✔
364
        r = strv_extend_strv_concat(&dropin_dirs, dirs, suffix);
46,782✔
365
        if (r < 0)
46,782✔
366
                return r;
367

368
        return conf_files_list_strv(ret, ".conf", root, 0, (const char* const*) dropin_dirs);
46,782✔
369
}
370

371
/**
372
 * Open and read a config file.
373
 *
374
 * The <fn> argument may be:
375
 * - '-', meaning stdin.
376
 * - a file name without a path. In this case <config_dirs> are searched.
377
 * - a path, either relative or absolute. In this case <fn> is opened directly.
378
 *
379
 * This method is only suitable for configuration files which have a flat layout without dropins.
380
 */
381
int conf_file_read(
15,690✔
382
                const char *root,
383
                const char **config_dirs,
384
                const char *fn,
385
                parse_line_t parse_line,
386
                void *userdata,
387
                bool ignore_enoent,
388
                bool *invalid_config) {
389

390
        _cleanup_fclose_ FILE *_f = NULL;
15,690✔
391
        _cleanup_free_ char *_fn = NULL;
15,690✔
392
        unsigned v = 0;
15,690✔
393
        FILE *f;
15,690✔
394
        int r = 0;
15,690✔
395

396
        assert(fn);
15,690✔
397

398
        if (streq(fn, "-")) {
15,690✔
399
                f = stdin;
175✔
400
                fn = "<stdin>";
175✔
401

402
                log_debug("Reading config from stdin%s", glyph(GLYPH_ELLIPSIS));
207✔
403

404
        } else if (is_path(fn)) {
15,515✔
405
                r = path_make_absolute_cwd(fn, &_fn);
15,515✔
406
                if (r < 0)
15,515✔
UNCOV
407
                        return log_error_errno(r, "Failed to make path absolute: %m");
×
408
                fn = _fn;
15,515✔
409

410
                f = _f = fopen(fn, "re");
15,515✔
411
                if (!_f)
15,515✔
412
                        r = -errno;
358✔
413
                else
414
                        log_debug("Reading config file \"%s\"%s", fn, glyph(GLYPH_ELLIPSIS));
30,021✔
415

416
        } else {
UNCOV
417
                r = search_and_fopen(fn, "re", root, config_dirs, &_f, &_fn);
×
UNCOV
418
                if (r >= 0) {
×
UNCOV
419
                        f = _f;
×
UNCOV
420
                        fn = _fn;
×
UNCOV
421
                        log_debug("Reading config file \"%s\"%s", fn, glyph(GLYPH_ELLIPSIS));
×
422
                }
423
        }
424

425
        if (r == -ENOENT && ignore_enoent) {
15,690✔
426
                log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn);
358✔
427
                return 0; /* No error, but nothing happened. */
358✔
428
        }
429
        if (r < 0)
15,332✔
UNCOV
430
                return log_error_errno(r, "Failed to read '%s': %m", fn);
×
431

432
        r = 1;  /* We entered the part where we may modify state. */
433

434
        for (;;) {
200,079✔
435
                _cleanup_free_ char *line = NULL;
184,747✔
436
                bool invalid_line = false;
200,079✔
437
                int k;
200,079✔
438

439
                k = read_stripped_line(f, LONG_LINE_MAX, &line);
200,079✔
440
                if (k < 0)
200,079✔
UNCOV
441
                        return log_error_errno(k, "Failed to read '%s': %m", fn);
×
442
                if (k == 0)
200,079✔
443
                        break;
444

445
                v++;
184,747✔
446

447
                if (IN_SET(line[0], 0, '#'))
184,747✔
448
                        continue;
119,436✔
449

450
                k = parse_line(fn, v, line, invalid_config ? &invalid_line : NULL, userdata);
65,986✔
451
                if (k < 0 && invalid_line)
65,311✔
452
                        /* Allow reporting with a special code if the caller requested this. */
453
                        *invalid_config = true;
10✔
454
                else
455
                        /* The first error, if any, becomes our return value. */
456
                        RET_GATHER(r, k);
65,301✔
457
        }
458

459
        if (ferror(f))
15,332✔
UNCOV
460
                RET_GATHER(r, log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read from file %s.", fn));
×
461

462
        return r;
463
}
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